From 492a67db593d042fb2fdcfad9b5ebc026628619d Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Thu, 5 Jul 2018 15:09:58 -0700 Subject: [PATCH 01/22] Starting --- lib/matplotlib/axes/_axes.py | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index ccccae9db634..71bc40f7ea1e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -639,6 +639,44 @@ def indicate_inset_zoom(self, inset_ax, **kwargs): return rectpatch, connects + def secondary_xaxis(self, loc, conversion, *, **kwargs): + """ + Add a second x-axis to this axes. + + For example if we want to have a second scale for the data plotted on + the xaxis. + + Parameters + ---------- + loc : string or scalar + The position to put the secondary axis. Strings can be 'top' or + 'bottom', scalar can be a float indicating the relative position + on the axes to put the new axes (0 being the bottom, and 1.0 being + the top.) + + conversion : tuple of floats or function + conversion between the parent xaxis values and the secondary xaxis + values. If a tuple of floats, the floats are polynomial + co-efficients, with the first entry the highest exponent's + co-efficient (i.e. [2, 3, 1] is the same as + ``xnew = 2 x**2 + 3 * x + 1``). If a function is specified it + should accept a float as input and return a float as the + result. + + Other Parameters + ---------------- + **kwargs : `~matplotlib.axes.Axes` properties. + Other miscellaneous axes parameters. + + Returns + ------- + ax : `~matplotlib.axes.Axes` (??) + + """ + secondary_ax = Secondary_Xaxis(self, loc, conversion, **kwargs) + self.add_child_axes(secondary_ax) + + def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): """ Add text to the axes. From 85623de1471267b04f37da24f521b160f1e8bab8 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 6 Jul 2018 07:52:21 -0700 Subject: [PATCH 02/22] ENH: add secondary x axis --- lib/matplotlib/axes/_axes.py | 4 +++- lib/matplotlib/axes/_base.py | 26 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 71bc40f7ea1e..770e6798573a 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -38,6 +38,7 @@ safe_first_element) from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer from matplotlib.axes._base import _AxesBase, _process_plot_format +from matplotlib.axes._secondary_axes import Secondary_Xaxis _log = logging.getLogger(__name__) @@ -639,7 +640,7 @@ def indicate_inset_zoom(self, inset_ax, **kwargs): return rectpatch, connects - def secondary_xaxis(self, loc, conversion, *, **kwargs): + def secondary_xaxis(self, loc, conversion, **kwargs): """ Add a second x-axis to this axes. @@ -675,6 +676,7 @@ def secondary_xaxis(self, loc, conversion, *, **kwargs): """ secondary_ax = Secondary_Xaxis(self, loc, conversion, **kwargs) self.add_child_axes(secondary_ax) + return secondary_ax def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index f1e1116a5a58..ef5cfff47b2f 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2510,7 +2510,16 @@ def _update_title_position(self, renderer): y = 1.0 # need to check all our twins too... axs = self._twinned_axes.get_siblings(self) - + # and all the children + for ax in self.child_axes: + if ax is not None: + locator = ax.get_axes_locator() + if locator: + pos = locator(self, renderer) + ax.apply_aspect(pos) + else: + ax.apply_aspect() + axs = axs + [ax] for ax in axs: try: if (ax.xaxis.get_label_position() == 'top' @@ -2542,12 +2551,15 @@ def draw(self, renderer=None, inframe=False): # prevent triggering call backs during the draw process self._stale = True - locator = self.get_axes_locator() - if locator: - pos = locator(self, renderer) - self.apply_aspect(pos) - else: - self.apply_aspect() + + # loop over self and child axes... + for ax in [self]: + locator = ax.get_axes_locator() + if locator: + pos = locator(self, renderer) + ax.apply_aspect(pos) + else: + ax.apply_aspect() artists = self.get_children() artists.remove(self.patch) From 033564adbdfdc7d4dbfa2e698c8eaa441e47dde8 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 6 Jul 2018 09:56:54 -0700 Subject: [PATCH 03/22] POC --- lib/matplotlib/axes/_secondary_axes.py | 233 +++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 lib/matplotlib/axes/_secondary_axes.py diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py new file mode 100644 index 000000000000..b254c296b692 --- /dev/null +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -0,0 +1,233 @@ +import collections +import numpy as np +import numbers + +import matplotlib.ticker as mticker +import matplotlib.transforms as mtransforms + +from matplotlib.axes._base import _AxesBase, _process_plot_format + +# DUPE FROM AXES. FIXME... + +def _make_inset_locator(rect, trans, parent): + """ + Helper function to locate inset axes, used in + `.Axes.inset_axes_from_bounds`. + + A locator gets used in `Axes.set_aspect` to override the default + locations... It is a function that takes an axes object and + a renderer and tells `set_aspect` where it is to be placed. + + Here *rect* is a rectangle [l, b, w, h] that specifies the + location for the axes in the transform given by *trans* on the + *parent*. + """ + _rect = mtransforms.Bbox.from_bounds(*rect) + _trans = trans + _parent = parent + + def inset_locator(ax, renderer): + bbox = _rect + bb = mtransforms.TransformedBbox(bbox, _trans) + tr = _parent.figure.transFigure.inverted() + bb = mtransforms.TransformedBbox(bb, tr) + return bb + + return inset_locator + + +class Secondary_Xaxis(_AxesBase): + """ + A class to hold a Secondary_Xaxis. + + See `~.axes._axes.Axes.secondary_xaxis` for usage. + + """ + def __init__(self, parent, location, conversion, **kwargs): + self._conversion = conversion + self._parent = parent + + + super().__init__(self._parent.figure, [0, 1., 1, 0.0001], **kwargs) + + self.set_location(location) + + # styling: + self.yaxis.set_major_locator(mticker.NullLocator()) + self.yaxis.set_ticks_position('none') + self.spines['right'].set_visible(False) + self.spines['left'].set_visible(False) + if self._y > 0.5: + self.set_axis_orientation('top') + else: + self.set_axis_orientation('bottom') + self.set_conversion(conversion) + + def set_axis_orientation(self, orient): + """ + Set if axes spine and labels are drawn at top or bottom of the + axes. + + Parameters + ---------- + orient :: string + either 'top' or 'bottom' + + """ + + self.spines[orient].set_visible(True) + if orient == 'top': + self.spines['bottom'].set_visible(False) + else: + self.spines['top'].set_visible(False) + + self.xaxis.set_ticks_position(orient) + self.xaxis.set_label_position(orient) + + def set_location(self, location): + """ + Set the vertical location of the axes in parent-normalized + co-ordinates. + + Parameters + ---------- + location : string or scalar + The position to put the secondary axis. Strings can be 'top' or + 'bottom', scalar can be a float indicating the relative position + on the parent axes to put the new axes, 0 being the bottom, and + 1.0 being the top. + """ + self._loc = location + # This puts the rectangle into figure-relative coordinates. + if isinstance(self._loc, str): + if self._loc == 'top': + y = 1. + elif self._loc == 'bottom': + y = 0. + else: + y = self._loc + bounds = [0, y, 1., 1e-10] + transform = self._parent.transAxes + secondary_locator = _make_inset_locator(bounds, + transform, self._parent) + bb = secondary_locator(None, None) + + # this locator lets the axes move if in data coordinates. + # it gets called in `ax.apply_aspect() (of all places) + self.set_axes_locator(secondary_locator) + self._y = y + + def set_conversion(self, conversion): + """ + Set how the secondary axis converts limits from the parent axes. + + Parameters + ---------- + conversion : tuple of floats or function + conversion between the parent xaxis values and the secondary xaxis + values. If a tuple of floats, the floats are polynomial + co-efficients, with the first entry the highest exponent's + co-efficient (i.e. [2, 3, 1] is the same as + ``xnew = 2 x**2 + 3 * x + 1``, passed to `numpy.polyval`). + If a function is specified it should accept a float as input and + return a float as the result. + """ + + # make the _convert function... + if callable(conversion): + self._convert = conversion + else: + if isinstance(conversion, numbers.Number): + conversion = np.asanyarray([conversion]) + shp = len(conversion) + if shp < 2: + conversion = np.array([conversion, 0.]) + self._convert = lambda x : np.polyval(conversion, x) + + def draw(self, renderer=None, inframe=False): + """ + Draw the secondary axes. + + Consults the parent axes for its xlimits and converts them + using the converter specified by + `~.axes._secondary_axes.set_conversion` (or *conversion* + parameter when axes initialized.) + + """ + lims = self._parent.get_xlim() + self.set_xlim(self._convert(lims)) + super().draw(renderer=renderer, inframe=inframe) + + def set_xlabel(self, xlabel, fontdict=None, labelpad=None, **kwargs): + """ + Set the label for the secondary x-axis. + + Parameters + ---------- + xlabel : str + The label text. + + labelpad : scalar, optional, default: None + Spacing in points between the label and the x-axis. + + Other Parameters + ---------------- + **kwargs : `.Text` properties + `.Text` properties control the appearance of the label. + + See also + -------- + text : for information on how override and the optional args work + """ + if labelpad is not None: + self.xaxis.labelpad = labelpad + return self.xaxis.set_label_text(xlabel, fontdict, **kwargs) + + def set_color(self, color): + """ + Change the color of the secondary axes and all decorators + + Parameters + ---------- + color : Matplotlib color + """ + + self.tick_params(axis='x', colors=color) + self.spines['bottom'].set_color(color) + self.spines['top'].set_color(color) + self.xaxis.label.set_color(color) + + def get_tightbbox(self, renderer, call_axes_locator=True): + """ + Return the tight bounding box of the axes. + The dimension of the Bbox in canvas coordinate. + + If *call_axes_locator* is *False*, it does not call the + _axes_locator attribute, which is necessary to get the correct + bounding box. ``call_axes_locator==False`` can be used if the + caller is only intereted in the relative size of the tightbbox + compared to the axes bbox. + """ + + bb = [] + + if not self.get_visible(): + return None + + locator = self.get_axes_locator() + if locator and call_axes_locator: + pos = locator(self, renderer) + self.apply_aspect(pos) + else: + self.apply_aspect() + + bb_xaxis = self.xaxis.get_tightbbox(renderer) + if bb_xaxis: + bb.append(bb_xaxis) + + bb.append(self.get_window_extent(renderer)) + + _bbox = mtransforms.Bbox.union( + [b for b in bb if b.width != 0 or b.height != 0]) + + return _bbox From b8bcc4cc91f4c745e27d5c59c506fdd2d71ca8b0 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 6 Jul 2018 10:27:51 -0700 Subject: [PATCH 04/22] POC --- lib/matplotlib/axes/_axes.py | 1 - lib/matplotlib/axes/_secondary_axes.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 770e6798573a..93650dcb58a9 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -678,7 +678,6 @@ def secondary_xaxis(self, loc, conversion, **kwargs): self.add_child_axes(secondary_ax) return secondary_ax - def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): """ Add text to the axes. diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index b254c296b692..c02dd4ac1799 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -9,6 +9,7 @@ # DUPE FROM AXES. FIXME... + def _make_inset_locator(rect, trans, parent): """ Helper function to locate inset axes, used in @@ -142,7 +143,7 @@ def set_conversion(self, conversion): shp = len(conversion) if shp < 2: conversion = np.array([conversion, 0.]) - self._convert = lambda x : np.polyval(conversion, x) + self._convert = lambda x: np.polyval(conversion, x) def draw(self, renderer=None, inframe=False): """ From 976bdfe70503ac948128c41c273870848c2d67a6 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 6 Jul 2018 17:07:25 -0700 Subject: [PATCH 05/22] POC --- lib/matplotlib/axes/_axes.py | 42 ++++- lib/matplotlib/axes/_secondary_axes.py | 215 ++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 93650dcb58a9..c20134fcbbc0 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -38,7 +38,7 @@ safe_first_element) from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer from matplotlib.axes._base import _AxesBase, _process_plot_format -from matplotlib.axes._secondary_axes import Secondary_Xaxis +from matplotlib.axes._secondary_axes import Secondary_Xaxis, Secondary_Yaxis _log = logging.getLogger(__name__) @@ -678,6 +678,46 @@ def secondary_xaxis(self, loc, conversion, **kwargs): self.add_child_axes(secondary_ax) return secondary_ax + + def secondary_yaxis(self, loc, conversion, **kwargs): + """ + Add a second y-axis to this axes. + + For example if we want to have a second scale for the data plotted on + the xaxis. + + Parameters + ---------- + loc : string or scalar + FIX + The position to put the secondary axis. Strings can be 'top' or + 'bottom', scalar can be a float indicating the relative position + on the axes to put the new axes (0 being the bottom, and 1.0 being + the top.) + + conversion : tuple of floats or function + conversion between the parent xaxis values and the secondary xaxis + values. If a tuple of floats, the floats are polynomial + co-efficients, with the first entry the highest exponent's + co-efficient (i.e. [2, 3, 1] is the same as + ``xnew = 2 x**2 + 3 * x + 1``). If a function is specified it + should accept a float as input and return a float as the + result. + + Other Parameters + ---------------- + **kwargs : `~matplotlib.axes.Axes` properties. + Other miscellaneous axes parameters. + + Returns + ------- + ax : `~matplotlib.axes.Axes` (??) + + """ + secondary_ax = Secondary_Yaxis(self, loc, conversion, **kwargs) + self.add_child_axes(secondary_ax) + return secondary_ax + def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): """ Add text to the axes. diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index c02dd4ac1799..b6d8cde9dcb3 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -5,9 +5,7 @@ import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms -from matplotlib.axes._base import _AxesBase, _process_plot_format - -# DUPE FROM AXES. FIXME... +from matplotlib.axes._base import _AxesBase def _make_inset_locator(rect, trans, parent): @@ -39,16 +37,13 @@ def inset_locator(ax, renderer): class Secondary_Xaxis(_AxesBase): """ - A class to hold a Secondary_Xaxis. - - See `~.axes._axes.Axes.secondary_xaxis` for usage. - + General class to hold a Secondary_X/Yaxis. """ + def __init__(self, parent, location, conversion, **kwargs): self._conversion = conversion self._parent = parent - super().__init__(self._parent.figure, [0, 1., 1, 0.0001], **kwargs) self.set_location(location) @@ -98,6 +93,7 @@ def set_location(self, location): on the parent axes to put the new axes, 0 being the bottom, and 1.0 being the top. """ + self._loc = location # This puts the rectangle into figure-relative coordinates. if isinstance(self._loc, str): @@ -105,6 +101,10 @@ def set_location(self, location): y = 1. elif self._loc == 'bottom': y = 0. + else: + raise ValueError("location must be 'bottom', 'top', or a " + "float, not '{}'.".format(self._loc)) + else: y = self._loc bounds = [0, y, 1., 1e-10] @@ -232,3 +232,202 @@ def get_tightbbox(self, renderer, call_axes_locator=True): [b for b in bb if b.width != 0 or b.height != 0]) return _bbox + + +class Secondary_Yaxis(_AxesBase): + """ + Class to hold a Secondary_Yaxis. + """ + + def __init__(self, parent, location, conversion, **kwargs): + self._conversion = conversion + self._parent = parent + self._x = None # set in set_location + + super().__init__(self._parent.figure, [1., 0., 0.00001, 1.], **kwargs) + + self.set_location(location) + + # styling: + self.xaxis.set_major_locator(mticker.NullLocator()) + self.xaxis.set_ticks_position('none') + self.spines['top'].set_visible(False) + self.spines['bottom'].set_visible(False) + if self._x > 0.5: + self.set_axis_orientation('right') + else: + self.set_axis_orientation('left') + self.set_conversion(conversion) + + def set_axis_orientation(self, orient): + """ + Set if axes spine and labels are drawn at left or right of the + axis. + + Parameters + ---------- + orient :: string + either 'left' or 'right' + + """ + + self.spines[orient].set_visible(True) + if orient == 'left': + self.spines['right'].set_visible(False) + else: + self.spines['left'].set_visible(False) + + self.yaxis.set_ticks_position(orient) + self.yaxis.set_label_position(orient) + + def set_location(self, location): + """ + Set the horizontal location of the axes in parent-normalized + co-ordinates. + + Parameters + ---------- + location : string or scalar + The position to put the secondary axis. Strings can be 'left' or + 'right', or scalar can be a float indicating the relative position + on the parent axes to put the new axes, 0 being the left, and + 1.0 being the right. + """ + + self._loc = location + # This puts the rectangle into figure-relative coordinates. + if isinstance(self._loc, str): + if self._loc == 'left': + x = 0. + elif self._loc == 'right': + x = 1. + else: + raise ValueError("location must be 'left', 'right', or a " + "float, not '{}'.".format(self._loc)) + else: + x = self._loc + bounds = [x, 0, 1e-10, 1.] + transform = self._parent.transAxes + secondary_locator = _make_inset_locator(bounds, + transform, self._parent) + bb = secondary_locator(None, None) + + # this locator lets the axes move if in data coordinates. + # it gets called in `ax.apply_aspect() (of all places) + self.set_axes_locator(secondary_locator) + self._x = x + + def set_conversion(self, conversion): + """ + Set how the secondary axis converts limits from the parent axes. + + Parameters + ---------- + conversion : tuple of floats or function + conversion between the parent xaxis values and the secondary xaxis + values. If a tuple of floats, the floats are polynomial + co-efficients, with the first entry the highest exponent's + co-efficient (i.e. [2, 3, 1] is the same as + ``xnew = 2 x**2 + 3 * x + 1``, passed to `numpy.polyval`). + If a function is specified it should accept a float as input and + return a float as the result. + """ + + # make the _convert function... + if callable(conversion): + self._convert = conversion + else: + if isinstance(conversion, numbers.Number): + conversion = np.asanyarray([conversion]) + shp = len(conversion) + if shp < 2: + conversion = np.array([conversion, 0.]) + self._convert = lambda x: np.polyval(conversion, x) + + def draw(self, renderer=None, inframe=False): + """ + Draw the secondary axes. + + Consults the parent axes for its xlimits and converts them + using the converter specified by + `~.axes._secondary_axes.set_conversion` (or *conversion* + parameter when axes initialized.) + + """ + lims = self._parent.get_xlim() + self.set_ylim(self._convert(lims)) + super().draw(renderer=renderer, inframe=inframe) + + def set_ylabel(self, ylabel, fontdict=None, labelpad=None, **kwargs): + """ + Set the label for the secondary y-axis. + + Parameters + ---------- + ylabel : str + The label text. + + labelpad : scalar, optional, default: None + Spacing in points between the label and the x-axis. + + Other Parameters + ---------------- + **kwargs : `.Text` properties + `.Text` properties control the appearance of the label. + + See also + -------- + text : for information on how override and the optional args work + """ + if labelpad is not None: + self.yaxis.labelpad = labelpad + return self.yaxis.set_label_text(ylabel, fontdict, **kwargs) + + def set_color(self, color): + """ + Change the color of the secondary axes and all decorators + + Parameters + ---------- + color : Matplotlib color + """ + + self.tick_params(axis='y', colors=color) + self.spines['left'].set_color(color) + self.spines['right'].set_color(color) + self.yaxis.label.set_color(color) + + def get_tightbbox(self, renderer, call_axes_locator=True): + """ + Return the tight bounding box of the axes. + The dimension of the Bbox in canvas coordinate. + + If *call_axes_locator* is *False*, it does not call the + _axes_locator attribute, which is necessary to get the correct + bounding box. ``call_axes_locator==False`` can be used if the + caller is only intereted in the relative size of the tightbbox + compared to the axes bbox. + """ + + bb = [] + + if not self.get_visible(): + return None + + locator = self.get_axes_locator() + if locator and call_axes_locator: + pos = locator(self, renderer) + self.apply_aspect(pos) + else: + self.apply_aspect() + + bb_yaxis = self.yaxis.get_tightbbox(renderer) + if bb_yaxis: + bb.append(bb_yaxis) + + bb.append(self.get_window_extent(renderer)) + + _bbox = mtransforms.Bbox.union( + [b for b in bb if b.width != 0 or b.height != 0]) + + return _bbox From cde7eced4f96f2df9ccc6865330a6eee12fd87ba Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Tue, 10 Jul 2018 14:54:13 -0700 Subject: [PATCH 06/22] Add blank set_aspect --- lib/matplotlib/axes/_secondary_axes.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index b6d8cde9dcb3..df39f141734c 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -2,6 +2,8 @@ import numpy as np import numbers +import warnings + import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms @@ -233,6 +235,11 @@ def get_tightbbox(self, renderer, call_axes_locator=True): return _bbox + def set_aspect(self, *args, **kwargs): + """ + """ + warnings.warn("Secondary axes can't set the aspect ratio") + class Secondary_Yaxis(_AxesBase): """ @@ -431,3 +438,8 @@ def get_tightbbox(self, renderer, call_axes_locator=True): [b for b in bb if b.width != 0 or b.height != 0]) return _bbox + + def set_aspect(self, *args, **kwargs): + """ + """ + warnings.warn("Secondary axes can't set the aspect ratio") From da78fc443a1deb9256e1989e0f1e0a333c58fb74 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Wed, 11 Jul 2018 13:19:01 -0700 Subject: [PATCH 07/22] DOC: test linking of returned object --- lib/matplotlib/axes/_axes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index c20134fcbbc0..cb249041ad03 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -671,7 +671,7 @@ def secondary_xaxis(self, loc, conversion, **kwargs): Returns ------- - ax : `~matplotlib.axes.Axes` (??) + ax : `~matplotlib.axes._secondary_axes.Secondary_Xaxis` """ secondary_ax = Secondary_Xaxis(self, loc, conversion, **kwargs) @@ -711,7 +711,7 @@ def secondary_yaxis(self, loc, conversion, **kwargs): Returns ------- - ax : `~matplotlib.axes.Axes` (??) + ax : `~matplotlib.axes._secondary_axes.Secondary_Yaxis` """ secondary_ax = Secondary_Yaxis(self, loc, conversion, **kwargs) From faef515058cf324b154a21a3af4321adbee169c5 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Wed, 18 Jul 2018 13:16:02 -0700 Subject: [PATCH 08/22] FIX: move towards preoper non-linear axis --- lib/matplotlib/axes/_secondary_axes.py | 72 ++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index df39f141734c..3c24eb130156 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -6,9 +6,47 @@ import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms +import matplotlib.scale as mscale from matplotlib.axes._base import _AxesBase +from matplotlib.ticker import ( + AutoLocator, + FixedLocator, + NullLocator, + NullFormatter, + FuncFormatter, + ScalarFormatter, + AutoMinorLocator, +) + +class ArbitraryScale(mscale.ScaleBase): + + name = 'arbitrary' + + def __init__(self, axis, transform=mtransforms.IdentityTransform()): + """ + TODO + """ + self._transform = transform + + def get_transform(self): + """ + The transform for linear scaling is just the + :class:`~matplotlib.transforms.IdentityTransform`. + """ + return self._transform + + def set_default_locators_and_formatters(self, axis): + """ + Set the locators and formatters to reasonable defaults for + linear scaling. + """ + axis.set_major_locator(AutoLocator()) + axis.set_major_formatter(ScalarFormatter()) + axis.set_minor_formatter(NullFormatter()) + +mscale.register_scale(ArbitraryScale) def _make_inset_locator(rect, trans, parent): """ @@ -120,13 +158,34 @@ def set_location(self, location): self.set_axes_locator(secondary_locator) self._y = y + def set_xticks(self, ticks, minor=False): + """ + Set the x ticks with list of *ticks* + + Parameters + ---------- + ticks : list + List of x-axis tick locations. + + minor : bool, optional + If ``False`` sets major ticks, if ``True`` sets minor ticks. + Default is ``False``. + """ + ret = self.xaxis.set_ticks(ticks, minor=minor) + self.stale = True + + lims = self._parent.get_xlim() + self.set_xlim(self._convert.transform(lims)) + return ret + + def set_conversion(self, conversion): """ Set how the secondary axis converts limits from the parent axes. Parameters ---------- - conversion : tuple of floats or function + conversion : tuple of floats, transform, or string conversion between the parent xaxis values and the secondary xaxis values. If a tuple of floats, the floats are polynomial co-efficients, with the first entry the highest exponent's @@ -137,8 +196,9 @@ def set_conversion(self, conversion): """ # make the _convert function... - if callable(conversion): + if isinstance(conversion, mtransforms.Transform): self._convert = conversion + self.set_xscale('arbitrary', transform=conversion) else: if isinstance(conversion, numbers.Number): conversion = np.asanyarray([conversion]) @@ -158,7 +218,13 @@ def draw(self, renderer=None, inframe=False): """ lims = self._parent.get_xlim() - self.set_xlim(self._convert(lims)) + order = lims[0] < lims[1] + lims = self._convert.transform(lims) + neworder = lims[0] < lims[1] + if neworder != order: + # flip because the transform will take care of the flipping.. + lims = lims[::-1] + self.set_xlim(lims) super().draw(renderer=renderer, inframe=inframe) def set_xlabel(self, xlabel, fontdict=None, labelpad=None, **kwargs): From c93a326e1a4a198f1482e9cd01c78422af2a7c81 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Wed, 18 Jul 2018 15:54:16 -0700 Subject: [PATCH 09/22] FIX: move towards preoper non-linear axis --- lib/matplotlib/axes/_secondary_axes.py | 276 +++++++++++++++++++++---- 1 file changed, 233 insertions(+), 43 deletions(-) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 3c24eb130156..70d48a2c2dc7 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -20,34 +20,6 @@ AutoMinorLocator, ) -class ArbitraryScale(mscale.ScaleBase): - - name = 'arbitrary' - - def __init__(self, axis, transform=mtransforms.IdentityTransform()): - """ - TODO - """ - self._transform = transform - - def get_transform(self): - """ - The transform for linear scaling is just the - :class:`~matplotlib.transforms.IdentityTransform`. - """ - return self._transform - - def set_default_locators_and_formatters(self, axis): - """ - Set the locators and formatters to reasonable defaults for - linear scaling. - """ - axis.set_major_locator(AutoLocator()) - axis.set_major_formatter(ScalarFormatter()) - axis.set_minor_formatter(NullFormatter()) - -mscale.register_scale(ArbitraryScale) - def _make_inset_locator(rect, trans, parent): """ Helper function to locate inset axes, used in @@ -75,14 +47,31 @@ def inset_locator(ax, renderer): return inset_locator +def _parse_conversion(name, otherargs): + print(otherargs) + if name == 'inverted': + if otherargs is None: + otherargs = [1.] + otherargs = np.atleast_1d(otherargs) + return _InvertTransform(otherargs[0]) + elif name == 'power': + otherargs = np.atleast_1d(otherargs) + return _PowerTransform(a=otherargs[0], b=otherargs[1]) + elif name == 'linear': + otherargs = np.asarray(otherargs) + return _LinearTransform(slope=otherargs[0], offset=otherargs[1]) + else: + raise ValueError(f'"{name}" not a possible conversion string') + class Secondary_Xaxis(_AxesBase): """ General class to hold a Secondary_X/Yaxis. """ - def __init__(self, parent, location, conversion, **kwargs): + def __init__(self, parent, location, conversion, otherargs=None, **kwargs): self._conversion = conversion self._parent = parent + self._otherargs = otherargs super().__init__(self._parent.figure, [0, 1., 1, 0.0001], **kwargs) @@ -97,7 +86,7 @@ def __init__(self, parent, location, conversion, **kwargs): self.set_axis_orientation('top') else: self.set_axis_orientation('bottom') - self.set_conversion(conversion) + self.set_conversion(conversion, self._otherargs) def set_axis_orientation(self, orient): """ @@ -179,33 +168,55 @@ def set_xticks(self, ticks, minor=False): return ret - def set_conversion(self, conversion): + def set_conversion(self, conversion, otherargs=None): """ Set how the secondary axis converts limits from the parent axes. Parameters ---------- - conversion : tuple of floats, transform, or string - conversion between the parent xaxis values and the secondary xaxis - values. If a tuple of floats, the floats are polynomial - co-efficients, with the first entry the highest exponent's - co-efficient (i.e. [2, 3, 1] is the same as - ``xnew = 2 x**2 + 3 * x + 1``, passed to `numpy.polyval`). - If a function is specified it should accept a float as input and - return a float as the result. + conversion : float, two-tuple of floats, transform, or string + transform between the parent xaxis values and the secondary xaxis + values. If a single floats, a linear transform with the + float as the slope is used. If a 2-tuple of floats, the first + is the slope, and the second the offset. + + If a transform is supplied, then the transform must have an + inverse. + + For convenience a few common transforms are provided by using + a string: + - 'linear': as above. ``otherargs = (slope, offset)`` must + be supplied. + - 'inverted': a/x where ``otherargs = a`` can be supplied + (defaults to 1) + - 'power': b x^a where ``otherargs = (a, b)`` must be + supplied + """ # make the _convert function... if isinstance(conversion, mtransforms.Transform): self._convert = conversion - self.set_xscale('arbitrary', transform=conversion) + self.set_xscale('arbitrary', transform=conversion.inverted()) + elif isinstance(conversion, str): + self._convert = _parse_conversion(conversion, otherargs) + self.set_xscale('arbitrary', transform=self._convert.inverted()) else: + # linear conversion with offset if isinstance(conversion, numbers.Number): conversion = np.asanyarray([conversion]) - shp = len(conversion) - if shp < 2: + if len(conversion) > 2: + raise ValueError('secondary_axes conversion can be a ' + 'float, two-tuple of float, a transform ' + 'with an inverse, or a string.') + elif len(conversion) < 2: conversion = np.array([conversion, 0.]) - self._convert = lambda x: np.polyval(conversion, x) + conversion = _LinearTransform(slope=conversion[0], + offset=conversion[1]) + self._convert = conversion + # this will track log/non log so long as the user sets... + self.set_xscale(self._parent.get_xscale()) + def draw(self, renderer=None, inframe=False): """ @@ -509,3 +520,182 @@ def set_aspect(self, *args, **kwargs): """ """ warnings.warn("Secondary axes can't set the aspect ratio") + + +class _LinearTransform(mtransforms.AffineBase): + """ + Linear transform 1d + """ + input_dims = 1 + output_dims = 1 + is_separable = True + has_inverse = True + + def __init__(self, slope, offset): + mtransforms.AffineBase.__init__(self) + self._slope = slope + self._offset = offset + + def transform_affine(self, values): + return np.asarray(values) * self._slope + self._offset + + def inverted(self): + return _InvertedLinearTransform(self._slope, self._offset) + + +class _InverseLinearTransform(mtransforms.AffineBase): + """ + Inverse linear transform 1d + """ + input_dims = 1 + output_dims = 1 + is_separable = True + has_inverse = True + + def __init__(self, slope, offset): + mtransforms.AffineBase.__init__(self) + self._slope = slope + self._offset = offset + + def transform_affine(self, values): + return (np.asarray(values) - self._offset) / self._slope + + def inverted(self): + return _LinearTransform(self._slope, self._offset) + +def _mask_out_of_bounds(a): + """ + Return a Numpy array where all values outside ]0, 1[ are + replaced with NaNs. If all values are inside ]0, 1[, the original + array is returned. + """ + a = numpy.array(a, float) + mask = (a <= 0.0) | (a >= 1.0) + if mask.any(): + return numpy.where(mask, numpy.nan, a) + return a + +class _InvertTransform(mtransforms.Transform): + """ + Return a/x + """ + + input_dims = 1 + output_dims = 1 + is_separable = True + has_inverse = True + + def __init__(self, fac, out_of_bounds='mask'): + mtransforms.Transform.__init__(self) + self._fac = fac + self.out_of_bounds = out_of_bounds + if self.out_of_bounds == 'mask': + self._handle_out_of_bounds = _mask_out_of_bounds + elif self.out_of_bounds == 'clip': + self._handle_out_of_bounds = _clip_out_of_bounds + else: + raise ValueError("`out_of_bounds` muse be either 'mask' or 'clip'") + + def transform_non_affine(self, values): + with np.errstate(divide="ignore", invalid="ignore"): + q = self._fac / values + return q + + def inverted(self): + """ we are just our own inverse """ + return _InvertTransform(1 / self._fac) + + +class _PowerTransform(mtransforms.Transform): + """ + Return b * x^a + """ + + input_dims = 1 + output_dims = 1 + is_separable = True + has_inverse = True + + def __init__(self, a, b, out_of_bounds='mask'): + mtransforms.Transform.__init__(self) + self._a = a + self._b = b + self.out_of_bounds = out_of_bounds + if self.out_of_bounds == 'mask': + self._handle_out_of_bounds = _mask_out_of_bounds + elif self.out_of_bounds == 'clip': + self._handle_out_of_bounds = _clip_out_of_bounds + else: + raise ValueError("`out_of_bounds` muse be either 'mask' or 'clip'") + + def transform_non_affine(self, values): + with np.errstate(divide="ignore", invalid="ignore"): + q = self._b * (values ** self._a) + print('forward', values, q) + return q + + def inverted(self): + """ we are just our own inverse """ + return _InversePowerTransform(self._a, self._b) + + +class _InversePowerTransform(mtransforms.Transform): + """ + Return b * x^a + """ + + input_dims = 1 + output_dims = 1 + is_separable = True + has_inverse = True + + def __init__(self, a, b, out_of_bounds='mask'): + mtransforms.Transform.__init__(self) + self._a = a + self._b = b + self.out_of_bounds = out_of_bounds + if self.out_of_bounds == 'mask': + self._handle_out_of_bounds = _mask_out_of_bounds + elif self.out_of_bounds == 'clip': + self._handle_out_of_bounds = _clip_out_of_bounds + else: + raise ValueError("`out_of_bounds` must be either 'mask' or 'clip'") + + def transform_non_affine(self, values): + with np.errstate(divide="ignore", invalid="ignore"): + q = (values / self._b) ** (1 / self._a) + print(values, q) + return q + + def inverted(self): + """ we are just our own inverse """ + return _PowerTransform(self._a, self._b) + + +class ArbitraryScale(mscale.ScaleBase): + + name = 'arbitrary' + + def __init__(self, axis, transform=mtransforms.IdentityTransform()): + """ + TODO + """ + self._transform = transform + + def get_transform(self): + """ + The transform for linear scaling is just the + :class:`~matplotlib.transforms.IdentityTransform`. + """ + return self._transform + + def set_default_locators_and_formatters(self, axis): + """ + Set the locators and formatters to reasonable defaults for + linear scaling. + """ + axis.set_major_locator(AutoLocator()) + axis.set_major_formatter(ScalarFormatter()) + axis.set_minor_formatter(NullFormatter()) + +mscale.register_scale(ArbitraryScale) From 481a8c06766d50a3f6ee218b52cd4cb6540f1afe Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sat, 21 Jul 2018 19:47:59 -0700 Subject: [PATCH 10/22] ENH: nonlinear axes --- lib/matplotlib/axes/_secondary_axes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 70d48a2c2dc7..72ae930ea1b6 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -230,7 +230,9 @@ def draw(self, renderer=None, inframe=False): """ lims = self._parent.get_xlim() order = lims[0] < lims[1] + print('before', lims) lims = self._convert.transform(lims) + print(lims) neworder = lims[0] < lims[1] if neworder != order: # flip because the transform will take care of the flipping.. From 47e56b2c257b57fb25bd47f07c50db9282bae881 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 22 Jul 2018 18:46:59 -0700 Subject: [PATCH 11/22] FIX --- lib/matplotlib/axes/_axes.py | 10 +- lib/matplotlib/axes/_secondary_axes.py | 369 ++++++++----------------- 2 files changed, 127 insertions(+), 252 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index cb249041ad03..bac2485b1c9e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -38,7 +38,7 @@ safe_first_element) from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer from matplotlib.axes._base import _AxesBase, _process_plot_format -from matplotlib.axes._secondary_axes import Secondary_Xaxis, Secondary_Yaxis +from matplotlib.axes._secondary_axes import Secondary_Axis _log = logging.getLogger(__name__) @@ -671,10 +671,10 @@ def secondary_xaxis(self, loc, conversion, **kwargs): Returns ------- - ax : `~matplotlib.axes._secondary_axes.Secondary_Xaxis` + ax : `~matplotlib.axes._secondary_axes.Secondary_Axis` """ - secondary_ax = Secondary_Xaxis(self, loc, conversion, **kwargs) + secondary_ax = Secondary_Axis(self, 'x', loc, conversion, **kwargs) self.add_child_axes(secondary_ax) return secondary_ax @@ -711,10 +711,10 @@ def secondary_yaxis(self, loc, conversion, **kwargs): Returns ------- - ax : `~matplotlib.axes._secondary_axes.Secondary_Yaxis` + ax : `~matplotlib.axes._secondary_axes.Secondary_Axis` """ - secondary_ax = Secondary_Yaxis(self, loc, conversion, **kwargs) + secondary_ax = Secondary_Axis(self, 'y', loc, conversion, **kwargs) self.add_child_axes(secondary_ax) return secondary_ax diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 72ae930ea1b6..74802bd52dc6 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -63,30 +63,51 @@ def _parse_conversion(name, otherargs): else: raise ValueError(f'"{name}" not a possible conversion string') -class Secondary_Xaxis(_AxesBase): +class Secondary_Axis(_AxesBase): """ General class to hold a Secondary_X/Yaxis. """ - def __init__(self, parent, location, conversion, otherargs=None, **kwargs): + def __init__(self, parent, orientation, + location, conversion, otherargs=None, **kwargs): + self._conversion = conversion self._parent = parent self._otherargs = otherargs - - super().__init__(self._parent.figure, [0, 1., 1, 0.0001], **kwargs) + self._orientation = orientation + + if self._orientation == 'x': + super().__init__(self._parent.figure, [0, 1., 1, 0.0001], **kwargs) + self._axis = self.xaxis + self._locstrings = ['top', 'bottom'] + self._otherstrings = ['left', 'right'] + elif self._orientation == 'y': + super().__init__(self._parent.figure, [0, 1., 0.0001, 1], **kwargs) + self._axis = self.yaxis + self._locstrings = ['right', 'left'] + self._otherstrings = ['top', 'bottom'] self.set_location(location) + self.set_conversion(conversion, self._otherargs) # styling: - self.yaxis.set_major_locator(mticker.NullLocator()) - self.yaxis.set_ticks_position('none') - self.spines['right'].set_visible(False) - self.spines['left'].set_visible(False) - if self._y > 0.5: - self.set_axis_orientation('top') + if self._orientation == 'x': + otheraxis = self.yaxis else: - self.set_axis_orientation('bottom') - self.set_conversion(conversion, self._otherargs) + otheraxis = self.xaxis + + otheraxis.set_major_locator(mticker.NullLocator()) + otheraxis.set_ticks_position('none') + + for st in self._otherstrings: + self.spines[st].set_visible(False) + for st in self._locstrings: + self.spines[st].set_visible(True) + + if self._pos < 0.5: + # flip the location strings... + self._locstrings = self._locstrings[::-1] + self.set_axis_orientation(self._locstrings[0]) def set_axis_orientation(self, orient): """ @@ -99,15 +120,20 @@ def set_axis_orientation(self, orient): either 'top' or 'bottom' """ - - self.spines[orient].set_visible(True) - if orient == 'top': - self.spines['bottom'].set_visible(False) - else: - self.spines['top'].set_visible(False) - - self.xaxis.set_ticks_position(orient) - self.xaxis.set_label_position(orient) + if orient in self._locstrings: + if orient == self._locstrings[1]: + # need to change the orientation. + self._locstrings = self._locstrings[::-1] + self.spines[self._locstrings[0]].set_visible(True) + self.spines[self._locstrings[1]].set_visible(False) + print('orient', orient, self._axis) + self._axis.set_ticks_position(orient) + self._axis.set_label_position(orient) + elif orient != self._locstrings[0]: + warnings.warn('"{}" is not a valid axis orientation, ' + 'not changing the orientation;' + 'choose "{}" or "{}""'.format(orient, + self._locstrings[0], self._locstrings[1])) def set_location(self, location): """ @@ -123,20 +149,28 @@ def set_location(self, location): 1.0 being the top. """ - self._loc = location # This puts the rectangle into figure-relative coordinates. - if isinstance(self._loc, str): - if self._loc == 'top': - y = 1. - elif self._loc == 'bottom': - y = 0. + print('location', location) + if isinstance(location, str): + if location in ['top', 'right']: + self._pos = 1. + elif location in ['bottom', 'left']: + self._pos = 0. else: - raise ValueError("location must be 'bottom', 'top', or a " - "float, not '{}'.".format(self._loc)) + warnings.warn("location must be '{}', '{}', or a " + "float, not '{}'.".format(location, + self._locstrings[0], self._locstrings[1])) + return + else: + self._pos = location + self._loc = location + print('pos', self._pos) + if self._orientation == 'x': + bounds = [0, self._pos, 1., 1e-10] else: - y = self._loc - bounds = [0, y, 1., 1e-10] + bounds = [self._pos, 0, 1e-10, 1] + transform = self._parent.transAxes secondary_locator = _make_inset_locator(bounds, transform, self._parent) @@ -145,9 +179,8 @@ def set_location(self, location): # this locator lets the axes move if in data coordinates. # it gets called in `ax.apply_aspect() (of all places) self.set_axes_locator(secondary_locator) - self._y = y - def set_xticks(self, ticks, minor=False): + def set_ticks(self, ticks, minor=False): """ Set the x ticks with list of *ticks* @@ -160,13 +193,17 @@ def set_xticks(self, ticks, minor=False): If ``False`` sets major ticks, if ``True`` sets minor ticks. Default is ``False``. """ - ret = self.xaxis.set_ticks(ticks, minor=minor) + ret = self._axis.set_ticks(ticks, minor=minor) self.stale = True - lims = self._parent.get_xlim() - self.set_xlim(self._convert.transform(lims)) - return ret + if self._orientation == 'x': + lims = self._parent.get_xlim() + self.set_xlim(self._convert.transform(lims)) + else: + lims = self._parent.get_ylim() + self.set_ylim(self._convert.transform(lims)) + return ret def set_conversion(self, conversion, otherargs=None): """ @@ -194,13 +231,18 @@ def set_conversion(self, conversion, otherargs=None): """ + if self._orientation == 'x': + set_scale = self.set_xscale + else: + set_scale = self.set_yscale + # make the _convert function... if isinstance(conversion, mtransforms.Transform): self._convert = conversion - self.set_xscale('arbitrary', transform=conversion.inverted()) + set_scale('arbitrary', transform=conversion.inverted()) elif isinstance(conversion, str): self._convert = _parse_conversion(conversion, otherargs) - self.set_xscale('arbitrary', transform=self._convert.inverted()) + set_scale('arbitrary', transform=self._convert.inverted()) else: # linear conversion with offset if isinstance(conversion, numbers.Number): @@ -215,8 +257,7 @@ def set_conversion(self, conversion, otherargs=None): offset=conversion[1]) self._convert = conversion # this will track log/non log so long as the user sets... - self.set_xscale(self._parent.get_xscale()) - + set_scale(self._parent.get_xscale()) def draw(self, renderer=None, inframe=False): """ @@ -228,7 +269,12 @@ def draw(self, renderer=None, inframe=False): parameter when axes initialized.) """ - lims = self._parent.get_xlim() + if self._orientation == 'x': + lims = self._parent.get_xlim() + set_lim = self.set_xlim + if self._orientation == 'y': + lims = self._parent.get_ylim() + set_lim = self.set_ylim order = lims[0] < lims[1] print('before', lims) lims = self._convert.transform(lims) @@ -237,47 +283,9 @@ def draw(self, renderer=None, inframe=False): if neworder != order: # flip because the transform will take care of the flipping.. lims = lims[::-1] - self.set_xlim(lims) - super().draw(renderer=renderer, inframe=inframe) - - def set_xlabel(self, xlabel, fontdict=None, labelpad=None, **kwargs): - """ - Set the label for the secondary x-axis. - - Parameters - ---------- - xlabel : str - The label text. - - labelpad : scalar, optional, default: None - Spacing in points between the label and the x-axis. - - Other Parameters - ---------------- - **kwargs : `.Text` properties - `.Text` properties control the appearance of the label. - See also - -------- - text : for information on how override and the optional args work - """ - if labelpad is not None: - self.xaxis.labelpad = labelpad - return self.xaxis.set_label_text(xlabel, fontdict, **kwargs) - - def set_color(self, color): - """ - Change the color of the secondary axes and all decorators - - Parameters - ---------- - color : Matplotlib color - """ - - self.tick_params(axis='x', colors=color) - self.spines['bottom'].set_color(color) - self.spines['top'].set_color(color) - self.xaxis.label.set_color(color) + set_lim(lims) + super().draw(renderer=renderer, inframe=inframe) def get_tightbbox(self, renderer, call_axes_locator=True): """ @@ -302,10 +310,12 @@ def get_tightbbox(self, renderer, call_axes_locator=True): self.apply_aspect(pos) else: self.apply_aspect() - - bb_xaxis = self.xaxis.get_tightbbox(renderer) - if bb_xaxis: - bb.append(bb_xaxis) + if self._orientation == 'x': + bb_axis = self.xaxis.get_tightbbox(renderer) + else: + bb_axis = self.yaxis.get_tightbbox(renderer) + if bb_axis: + bb.append(bb_axis) bb.append(self.get_window_extent(renderer)) @@ -319,134 +329,34 @@ def set_aspect(self, *args, **kwargs): """ warnings.warn("Secondary axes can't set the aspect ratio") - -class Secondary_Yaxis(_AxesBase): - """ - Class to hold a Secondary_Yaxis. - """ - - def __init__(self, parent, location, conversion, **kwargs): - self._conversion = conversion - self._parent = parent - self._x = None # set in set_location - - super().__init__(self._parent.figure, [1., 0., 0.00001, 1.], **kwargs) - - self.set_location(location) - - # styling: - self.xaxis.set_major_locator(mticker.NullLocator()) - self.xaxis.set_ticks_position('none') - self.spines['top'].set_visible(False) - self.spines['bottom'].set_visible(False) - if self._x > 0.5: - self.set_axis_orientation('right') - else: - self.set_axis_orientation('left') - self.set_conversion(conversion) - - def set_axis_orientation(self, orient): - """ - Set if axes spine and labels are drawn at left or right of the - axis. - - Parameters - ---------- - orient :: string - either 'left' or 'right' - - """ - - self.spines[orient].set_visible(True) - if orient == 'left': - self.spines['right'].set_visible(False) - else: - self.spines['left'].set_visible(False) - - self.yaxis.set_ticks_position(orient) - self.yaxis.set_label_position(orient) - - def set_location(self, location): - """ - Set the horizontal location of the axes in parent-normalized - co-ordinates. - - Parameters - ---------- - location : string or scalar - The position to put the secondary axis. Strings can be 'left' or - 'right', or scalar can be a float indicating the relative position - on the parent axes to put the new axes, 0 being the left, and - 1.0 being the right. - """ - - self._loc = location - # This puts the rectangle into figure-relative coordinates. - if isinstance(self._loc, str): - if self._loc == 'left': - x = 0. - elif self._loc == 'right': - x = 1. - else: - raise ValueError("location must be 'left', 'right', or a " - "float, not '{}'.".format(self._loc)) - else: - x = self._loc - bounds = [x, 0, 1e-10, 1.] - transform = self._parent.transAxes - secondary_locator = _make_inset_locator(bounds, - transform, self._parent) - bb = secondary_locator(None, None) - - # this locator lets the axes move if in data coordinates. - # it gets called in `ax.apply_aspect() (of all places) - self.set_axes_locator(secondary_locator) - self._x = x - - def set_conversion(self, conversion): + def set_xlabel(self, xlabel, fontdict=None, labelpad=None, **kwargs): """ - Set how the secondary axis converts limits from the parent axes. + Set the label for the x-axis. Parameters ---------- - conversion : tuple of floats or function - conversion between the parent xaxis values and the secondary xaxis - values. If a tuple of floats, the floats are polynomial - co-efficients, with the first entry the highest exponent's - co-efficient (i.e. [2, 3, 1] is the same as - ``xnew = 2 x**2 + 3 * x + 1``, passed to `numpy.polyval`). - If a function is specified it should accept a float as input and - return a float as the result. - """ - - # make the _convert function... - if callable(conversion): - self._convert = conversion - else: - if isinstance(conversion, numbers.Number): - conversion = np.asanyarray([conversion]) - shp = len(conversion) - if shp < 2: - conversion = np.array([conversion, 0.]) - self._convert = lambda x: np.polyval(conversion, x) + xlabel : str + The label text. - def draw(self, renderer=None, inframe=False): - """ - Draw the secondary axes. + labelpad : scalar, optional, default: None + Spacing in points between the label and the x-axis. - Consults the parent axes for its xlimits and converts them - using the converter specified by - `~.axes._secondary_axes.set_conversion` (or *conversion* - parameter when axes initialized.) + Other Parameters + ---------------- + **kwargs : `.Text` properties + `.Text` properties control the appearance of the label. + See also + -------- + text : for information on how override and the optional args work """ - lims = self._parent.get_xlim() - self.set_ylim(self._convert(lims)) - super().draw(renderer=renderer, inframe=inframe) + if labelpad is not None: + self.xaxis.labelpad = labelpad + return self.xaxis.set_label_text(xlabel, fontdict, **kwargs) def set_ylabel(self, ylabel, fontdict=None, labelpad=None, **kwargs): """ - Set the label for the secondary y-axis. + Set the label for the x-axis. Parameters ---------- @@ -472,56 +382,21 @@ def set_ylabel(self, ylabel, fontdict=None, labelpad=None, **kwargs): def set_color(self, color): """ Change the color of the secondary axes and all decorators - Parameters ---------- color : Matplotlib color """ - self.tick_params(axis='y', colors=color) - self.spines['left'].set_color(color) - self.spines['right'].set_color(color) - self.yaxis.label.set_color(color) - - def get_tightbbox(self, renderer, call_axes_locator=True): - """ - Return the tight bounding box of the axes. - The dimension of the Bbox in canvas coordinate. - - If *call_axes_locator* is *False*, it does not call the - _axes_locator attribute, which is necessary to get the correct - bounding box. ``call_axes_locator==False`` can be used if the - caller is only intereted in the relative size of the tightbbox - compared to the axes bbox. - """ - - bb = [] - - if not self.get_visible(): - return None - - locator = self.get_axes_locator() - if locator and call_axes_locator: - pos = locator(self, renderer) - self.apply_aspect(pos) + if self._orientation == 'x': + self.tick_params(axis='x', colors=color) + self.spines['bottom'].set_color(color) + self.spines['top'].set_color(color) + self.xaxis.label.set_color(color) else: - self.apply_aspect() - - bb_yaxis = self.yaxis.get_tightbbox(renderer) - if bb_yaxis: - bb.append(bb_yaxis) - - bb.append(self.get_window_extent(renderer)) - - _bbox = mtransforms.Bbox.union( - [b for b in bb if b.width != 0 or b.height != 0]) - - return _bbox - - def set_aspect(self, *args, **kwargs): - """ - """ - warnings.warn("Secondary axes can't set the aspect ratio") + self.tick_params(axis='y', colors=color) + self.spines['left'].set_color(color) + self.spines['right'].set_color(color) + self.yaxis.label.set_color(color) class _LinearTransform(mtransforms.AffineBase): From bdc7537c45dd92c2e96081543b43775cdde71b25 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Tue, 24 Jul 2018 08:04:08 -0700 Subject: [PATCH 12/22] FIX: refactor --- lib/matplotlib/axis.py | 3 +++ lib/matplotlib/dates.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index fdebfb2156ca..f0c62549cdc1 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1469,8 +1469,10 @@ def update_units(self, data): return False neednew = self.converter != converter + print('neednew', neednew, self.units) self.converter = converter default = self.converter.default_units(data, self) + print('default', default, data) if default is not None and self.units is None: self.set_units(default) @@ -1758,6 +1760,7 @@ def axis_date(self, tz=None): if isinstance(tz, str): import dateutil.tz tz = dateutil.tz.gettz(tz) + print('tzdate', tz) self.update_units(datetime.datetime(2009, 1, 1, 0, 0, 0, 0, tz)) def get_tick_space(self): diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index 80d790ded87c..585a90eeec47 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -311,7 +311,7 @@ def _from_ordinalf(x, tz=None): # add hours, minutes, seconds, microseconds dt += datetime.timedelta(microseconds=remainder_musec) - + print('tz', tz) return dt.astimezone(tz) From 2294a30c3a306868a505ec06c3407fa1d0a5c999 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 3 Aug 2018 10:30:27 -0700 Subject: [PATCH 13/22] FIX: flake8 --- lib/matplotlib/axes/_axes.py | 40 ++++++++++++---------- lib/matplotlib/axes/_secondary_axes.py | 47 ++++++-------------------- 2 files changed, 34 insertions(+), 53 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index bac2485b1c9e..2512a912b174 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -640,7 +640,7 @@ def indicate_inset_zoom(self, inset_ax, **kwargs): return rectpatch, connects - def secondary_xaxis(self, loc, conversion, **kwargs): + def secondary_xaxis(self, location, *, conversion=None, **kwargs): """ Add a second x-axis to this axes. @@ -649,7 +649,7 @@ def secondary_xaxis(self, loc, conversion, **kwargs): Parameters ---------- - loc : string or scalar + location : string or scalar The position to put the secondary axis. Strings can be 'top' or 'bottom', scalar can be a float indicating the relative position on the axes to put the new axes (0 being the bottom, and 1.0 being @@ -671,27 +671,29 @@ def secondary_xaxis(self, loc, conversion, **kwargs): Returns ------- - ax : `~matplotlib.axes._secondary_axes.Secondary_Axis` + ax : `~matplotlib.axes.Axes` (??) """ - secondary_ax = Secondary_Axis(self, 'x', loc, conversion, **kwargs) - self.add_child_axes(secondary_ax) - return secondary_ax - + if (location in ['top', 'bottom'] or isinstance(location, Number)): + secondary_ax = Secondary_Axis(self, 'x', location, + conversion, **kwargs) + self.add_child_axes(secondary_ax) + else: + raise ValueError('secondary_xaxis location must be either ' + '"top" or "bottom"') - def secondary_yaxis(self, loc, conversion, **kwargs): + def secondary_yaxis(self, location, *, conversion= None, **kwargs): """ Add a second y-axis to this axes. For example if we want to have a second scale for the data plotted on - the xaxis. + the yaxis. Parameters ---------- - loc : string or scalar - FIX - The position to put the secondary axis. Strings can be 'top' or - 'bottom', scalar can be a float indicating the relative position + location : string or scalar + The position to put the secondary axis. Strings can be 'left' or + 'right', scalar can be a float indicating the relative position on the axes to put the new axes (0 being the bottom, and 1.0 being the top.) @@ -711,12 +713,16 @@ def secondary_yaxis(self, loc, conversion, **kwargs): Returns ------- - ax : `~matplotlib.axes._secondary_axes.Secondary_Axis` + ax : `~matplotlib.axes.Axes` (??) """ - secondary_ax = Secondary_Axis(self, 'y', loc, conversion, **kwargs) - self.add_child_axes(secondary_ax) - return secondary_ax + if location in ['left', 'right'] or isinstance(location, Number): + secondary_ax = Secondary_Axis(self, 'y', location, + conversion, **kwargs) + self.add_child_axes(secondary_ax) + else: + raise ValueError('secondary_yaxis location must be either ' + '"left" or "right"') def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): """ diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 74802bd52dc6..79a3bcd1f1df 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -20,6 +20,7 @@ AutoMinorLocator, ) + def _make_inset_locator(rect, trans, parent): """ Helper function to locate inset axes, used in @@ -48,7 +49,6 @@ def inset_locator(ax, renderer): def _parse_conversion(name, otherargs): - print(otherargs) if name == 'inverted': if otherargs is None: otherargs = [1.] @@ -63,6 +63,7 @@ def _parse_conversion(name, otherargs): else: raise ValueError(f'"{name}" not a possible conversion string') + class Secondary_Axis(_AxesBase): """ General class to hold a Secondary_X/Yaxis. @@ -126,7 +127,6 @@ def set_axis_orientation(self, orient): self._locstrings = self._locstrings[::-1] self.spines[self._locstrings[0]].set_visible(True) self.spines[self._locstrings[1]].set_visible(False) - print('orient', orient, self._axis) self._axis.set_ticks_position(orient) self._axis.set_label_position(orient) elif orient != self._locstrings[0]: @@ -150,7 +150,6 @@ def set_location(self, location): """ # This puts the rectangle into figure-relative coordinates. - print('location', location) if isinstance(location, str): if location in ['top', 'right']: self._pos = 1. @@ -164,7 +163,6 @@ def set_location(self, location): else: self._pos = location self._loc = location - print('pos', self._pos) if self._orientation == 'x': bounds = [0, self._pos, 1., 1e-10] @@ -276,9 +274,7 @@ def draw(self, renderer=None, inframe=False): lims = self._parent.get_ylim() set_lim = self.set_ylim order = lims[0] < lims[1] - print('before', lims) lims = self._convert.transform(lims) - print(lims) neworder = lims[0] < lims[1] if neworder != order: # flip because the transform will take care of the flipping.. @@ -417,7 +413,7 @@ def transform_affine(self, values): return np.asarray(values) * self._slope + self._offset def inverted(self): - return _InvertedLinearTransform(self._slope, self._offset) + return _InverseLinearTransform(self._slope, self._offset) class _InverseLinearTransform(mtransforms.AffineBase): @@ -440,18 +436,20 @@ def transform_affine(self, values): def inverted(self): return _LinearTransform(self._slope, self._offset) + def _mask_out_of_bounds(a): """ Return a Numpy array where all values outside ]0, 1[ are replaced with NaNs. If all values are inside ]0, 1[, the original array is returned. """ - a = numpy.array(a, float) + a = np.array(a, float) mask = (a <= 0.0) | (a >= 1.0) if mask.any(): - return numpy.where(mask, numpy.nan, a) + return np.where(mask, np.nan, a) return a + class _InvertTransform(mtransforms.Transform): """ Return a/x @@ -462,16 +460,9 @@ class _InvertTransform(mtransforms.Transform): is_separable = True has_inverse = True - def __init__(self, fac, out_of_bounds='mask'): + def __init__(self, fac): mtransforms.Transform.__init__(self) self._fac = fac - self.out_of_bounds = out_of_bounds - if self.out_of_bounds == 'mask': - self._handle_out_of_bounds = _mask_out_of_bounds - elif self.out_of_bounds == 'clip': - self._handle_out_of_bounds = _clip_out_of_bounds - else: - raise ValueError("`out_of_bounds` muse be either 'mask' or 'clip'") def transform_non_affine(self, values): with np.errstate(divide="ignore", invalid="ignore"): @@ -493,22 +484,14 @@ class _PowerTransform(mtransforms.Transform): is_separable = True has_inverse = True - def __init__(self, a, b, out_of_bounds='mask'): + def __init__(self, a, b): mtransforms.Transform.__init__(self) self._a = a self._b = b - self.out_of_bounds = out_of_bounds - if self.out_of_bounds == 'mask': - self._handle_out_of_bounds = _mask_out_of_bounds - elif self.out_of_bounds == 'clip': - self._handle_out_of_bounds = _clip_out_of_bounds - else: - raise ValueError("`out_of_bounds` muse be either 'mask' or 'clip'") def transform_non_affine(self, values): with np.errstate(divide="ignore", invalid="ignore"): q = self._b * (values ** self._a) - print('forward', values, q) return q def inverted(self): @@ -526,22 +509,14 @@ class _InversePowerTransform(mtransforms.Transform): is_separable = True has_inverse = True - def __init__(self, a, b, out_of_bounds='mask'): + def __init__(self, a, b): mtransforms.Transform.__init__(self) self._a = a self._b = b - self.out_of_bounds = out_of_bounds - if self.out_of_bounds == 'mask': - self._handle_out_of_bounds = _mask_out_of_bounds - elif self.out_of_bounds == 'clip': - self._handle_out_of_bounds = _clip_out_of_bounds - else: - raise ValueError("`out_of_bounds` must be either 'mask' or 'clip'") def transform_non_affine(self, values): with np.errstate(divide="ignore", invalid="ignore"): - q = (values / self._b) ** (1 / self._a) - print(values, q) + q = (values / self._b) ** (1 / self._a) return q def inverted(self): From 96913b958a276434874f2c4146d171f423c8176d Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 3 Aug 2018 10:47:19 -0700 Subject: [PATCH 14/22] FIX: flake8 --- lib/matplotlib/axes/_secondary_axes.py | 14 +++++++++----- lib/matplotlib/axis.py | 3 --- lib/matplotlib/dates.py | 1 - 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 79a3bcd1f1df..2424a35c2486 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -21,15 +21,17 @@ ) -def _make_inset_locator(rect, trans, parent): +def _make_secondary_locator(rect, trans, parent): """ - Helper function to locate inset axes, used in - `.Axes.inset_axes_from_bounds`. + Helper function to locate the secondary axes. A locator gets used in `Axes.set_aspect` to override the default locations... It is a function that takes an axes object and a renderer and tells `set_aspect` where it is to be placed. + This locator make the transform be in axes-relative co-coordinates + because that is how we specify the "location" of the secondary axes. + Here *rect* is a rectangle [l, b, w, h] that specifies the location for the axes in the transform given by *trans* on the *parent*. @@ -170,11 +172,13 @@ def set_location(self, location): bounds = [self._pos, 0, 1e-10, 1] transform = self._parent.transAxes - secondary_locator = _make_inset_locator(bounds, + secondary_locator = _make_secondary_locator(bounds, transform, self._parent) bb = secondary_locator(None, None) - # this locator lets the axes move if in data coordinates. + # this locator lets the axes move in the parent axes coordinates. + # so it never needs to know where the parent is explicitly in + # figure co-ordinates. # it gets called in `ax.apply_aspect() (of all places) self.set_axes_locator(secondary_locator) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index f0c62549cdc1..fdebfb2156ca 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1469,10 +1469,8 @@ def update_units(self, data): return False neednew = self.converter != converter - print('neednew', neednew, self.units) self.converter = converter default = self.converter.default_units(data, self) - print('default', default, data) if default is not None and self.units is None: self.set_units(default) @@ -1760,7 +1758,6 @@ def axis_date(self, tz=None): if isinstance(tz, str): import dateutil.tz tz = dateutil.tz.gettz(tz) - print('tzdate', tz) self.update_units(datetime.datetime(2009, 1, 1, 0, 0, 0, 0, tz)) def get_tick_space(self): diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index 585a90eeec47..aa483535d367 100644 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -311,7 +311,6 @@ def _from_ordinalf(x, tz=None): # add hours, minutes, seconds, microseconds dt += datetime.timedelta(microseconds=remainder_musec) - print('tz', tz) return dt.astimezone(tz) From 8d058b17cb10e950c0f07ef486c4d719ae38a477 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 3 Aug 2018 11:01:02 -0700 Subject: [PATCH 15/22] FIX: flake8 --- lib/matplotlib/axes/_secondary_axes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 2424a35c2486..fd068d493129 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -63,7 +63,7 @@ def _parse_conversion(name, otherargs): otherargs = np.asarray(otherargs) return _LinearTransform(slope=otherargs[0], offset=otherargs[1]) else: - raise ValueError(f'"{name}" not a possible conversion string') + raise ValueError('"{}" not a possible conversion string'.format(name)) class Secondary_Axis(_AxesBase): @@ -178,7 +178,7 @@ def set_location(self, location): # this locator lets the axes move in the parent axes coordinates. # so it never needs to know where the parent is explicitly in - # figure co-ordinates. + # figure co-ordinates. # it gets called in `ax.apply_aspect() (of all places) self.set_axes_locator(secondary_locator) From d9cca069ad6bbfdc1968d8385a6751b588a6b890 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 3 Aug 2018 12:08:40 -0700 Subject: [PATCH 16/22] FIX: flake8 --- lib/matplotlib/axes/_axes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 2512a912b174..213fac0194c6 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -682,7 +682,7 @@ def secondary_xaxis(self, location, *, conversion=None, **kwargs): raise ValueError('secondary_xaxis location must be either ' '"top" or "bottom"') - def secondary_yaxis(self, location, *, conversion= None, **kwargs): + def secondary_yaxis(self, location, *, conversion=None, **kwargs): """ Add a second y-axis to this axes. From b1f36d6d5b2edfeb64ac45665e801e66f1968340 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 5 Aug 2018 09:53:17 -0700 Subject: [PATCH 17/22] not working --- lib/matplotlib/axes/_axes.py | 2 ++ lib/matplotlib/axes/_secondary_axes.py | 27 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 213fac0194c6..96e3cba84076 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -678,6 +678,7 @@ def secondary_xaxis(self, location, *, conversion=None, **kwargs): secondary_ax = Secondary_Axis(self, 'x', location, conversion, **kwargs) self.add_child_axes(secondary_ax) + return secondary_ax else: raise ValueError('secondary_xaxis location must be either ' '"top" or "bottom"') @@ -720,6 +721,7 @@ def secondary_yaxis(self, location, *, conversion=None, **kwargs): secondary_ax = Secondary_Axis(self, 'y', location, conversion, **kwargs) self.add_child_axes(secondary_ax) + return secondary_ax else: raise ValueError('secondary_yaxis location must be either ' '"left" or "right"') diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index fd068d493129..40bd5261b3ea 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -107,10 +107,10 @@ def __init__(self, parent, orientation, for st in self._locstrings: self.spines[st].set_visible(True) - if self._pos < 0.5: + if self._pos < 0.5: # flip the location strings... self._locstrings = self._locstrings[::-1] - self.set_axis_orientation(self._locstrings[0]) + self.set_axis_orientation(self._locstrings[0]) def set_axis_orientation(self, orient): """ @@ -127,15 +127,15 @@ def set_axis_orientation(self, orient): if orient == self._locstrings[1]: # need to change the orientation. self._locstrings = self._locstrings[::-1] - self.spines[self._locstrings[0]].set_visible(True) - self.spines[self._locstrings[1]].set_visible(False) - self._axis.set_ticks_position(orient) - self._axis.set_label_position(orient) elif orient != self._locstrings[0]: warnings.warn('"{}" is not a valid axis orientation, ' 'not changing the orientation;' 'choose "{}" or "{}""'.format(orient, self._locstrings[0], self._locstrings[1])) + self.spines[self._locstrings[0]].set_visible(True) + self.spines[self._locstrings[1]].set_visible(False) + self._axis.set_ticks_position(orient) + self._axis.set_label_position(orient) def set_location(self, location): """ @@ -271,18 +271,29 @@ def draw(self, renderer=None, inframe=False): parameter when axes initialized.) """ + # check parent scale... Make these match.... + if self._orientation == 'x': + scale = self._parent.get_xscale() + self.set_xscale(scale) + if self._orientation == 'y': + scale = self._parent.get_yscale() + self.set_yscale(scale) + if self._orientation == 'x': lims = self._parent.get_xlim() set_lim = self.set_xlim if self._orientation == 'y': lims = self._parent.get_ylim() set_lim = self.set_ylim + print('parent', lims) order = lims[0] < lims[1] lims = self._convert.transform(lims) neworder = lims[0] < lims[1] if neworder != order: # flip because the transform will take care of the flipping.. - lims = lims[::-1] + # lims = lims[::-1] + pass + print('childs', lims) set_lim(lims) super().draw(renderer=renderer, inframe=inframe) @@ -471,6 +482,7 @@ def __init__(self, fac): def transform_non_affine(self, values): with np.errstate(divide="ignore", invalid="ignore"): q = self._fac / values + print('q', values, q) return q def inverted(self): @@ -543,6 +555,7 @@ def get_transform(self): The transform for linear scaling is just the :class:`~matplotlib.transforms.IdentityTransform`. """ + print('tranform', self._transform) return self._transform def set_default_locators_and_formatters(self, axis): From 1bed724cf5991b6aeface6be8d5bc836e1a26cd3 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 5 Aug 2018 10:34:01 -0700 Subject: [PATCH 18/22] Fixing --- lib/matplotlib/_constrained_layout.py | 14 ++++++++++++-- lib/matplotlib/axes/_secondary_axes.py | 25 ++++++++----------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/matplotlib/_constrained_layout.py b/lib/matplotlib/_constrained_layout.py index 7f740b9980a5..57ca7d607cdc 100644 --- a/lib/matplotlib/_constrained_layout.py +++ b/lib/matplotlib/_constrained_layout.py @@ -181,7 +181,8 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad, sup = fig._suptitle bbox = invTransFig(sup.get_window_extent(renderer=renderer)) height = bbox.y1 - bbox.y0 - sup._layoutbox.edit_height(height+h_pad) + if np.isfinite(height): + sup._layoutbox.edit_height(height+h_pad) # OK, the above lines up ax._poslayoutbox with ax._layoutbox # now we need to @@ -267,10 +268,17 @@ def _make_layout_margins(ax, renderer, h_pad, w_pad): """ fig = ax.figure invTransFig = fig.transFigure.inverted().transform_bbox - + print('ax', ax, invTransFig) pos = ax.get_position(original=True) tightbbox = ax.get_tightbbox(renderer=renderer) + print(pos, tightbbox) bbox = invTransFig(tightbbox) + print('bbox', bbox) + # this can go wrong: + if not np.isfinite(bbox.y0 + bbox.x0 + bbox.y1 + bbox.x1): + # just abort, this is likely a bad set of co-ordinates that + # is transitory... + return # use stored h_pad if it exists h_padt = ax._poslayoutbox.h_pad if h_padt is None: @@ -288,6 +296,8 @@ def _make_layout_margins(ax, renderer, h_pad, w_pad): _log.debug('left %f', (-bbox.x0 + pos.x0 + w_pad)) _log.debug('right %f', (bbox.x1 - pos.x1 + w_pad)) _log.debug('bottom %f', (-bbox.y0 + pos.y0 + h_padt)) + _log.debug('bbox.y0 %f', bbox.y0) + _log.debug('pos.y0 %f', pos.y0) # Sometimes its possible for the solver to collapse # rather than expand axes, so they all have zero height # or width. This stops that... It *should* have been diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 40bd5261b3ea..20080141a183 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -89,6 +89,9 @@ def __init__(self, parent, orientation, self._axis = self.yaxis self._locstrings = ['right', 'left'] self._otherstrings = ['top', 'bottom'] + # this gets positioned w/o constrained_layout so exclude: + self._layoutbox = None + self._poslayoutbox = None self.set_location(location) self.set_conversion(conversion, self._otherargs) @@ -241,10 +244,11 @@ def set_conversion(self, conversion, otherargs=None): # make the _convert function... if isinstance(conversion, mtransforms.Transform): self._convert = conversion - set_scale('arbitrary', transform=conversion.inverted()) + + self.set_xscale('arbitrary', transform=conversion.inverted()) elif isinstance(conversion, str): self._convert = _parse_conversion(conversion, otherargs) - set_scale('arbitrary', transform=self._convert.inverted()) + self.set_xscale('arbitrary', transform=self._convert.inverted()) else: # linear conversion with offset if isinstance(conversion, numbers.Number): @@ -271,13 +275,6 @@ def draw(self, renderer=None, inframe=False): parameter when axes initialized.) """ - # check parent scale... Make these match.... - if self._orientation == 'x': - scale = self._parent.get_xscale() - self.set_xscale(scale) - if self._orientation == 'y': - scale = self._parent.get_yscale() - self.set_yscale(scale) if self._orientation == 'x': lims = self._parent.get_xlim() @@ -285,15 +282,12 @@ def draw(self, renderer=None, inframe=False): if self._orientation == 'y': lims = self._parent.get_ylim() set_lim = self.set_ylim - print('parent', lims) order = lims[0] < lims[1] lims = self._convert.transform(lims) neworder = lims[0] < lims[1] if neworder != order: # flip because the transform will take care of the flipping.. - # lims = lims[::-1] - pass - print('childs', lims) + lims = lims[::-1] set_lim(lims) super().draw(renderer=renderer, inframe=inframe) @@ -480,9 +474,7 @@ def __init__(self, fac): self._fac = fac def transform_non_affine(self, values): - with np.errstate(divide="ignore", invalid="ignore"): - q = self._fac / values - print('q', values, q) + q = self._fac / values return q def inverted(self): @@ -555,7 +547,6 @@ def get_transform(self): The transform for linear scaling is just the :class:`~matplotlib.transforms.IdentityTransform`. """ - print('tranform', self._transform) return self._transform def set_default_locators_and_formatters(self, axis): From dc81e88a79204302e4465e36fb8b17f35121c7b6 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 5 Aug 2018 12:56:56 -0700 Subject: [PATCH 19/22] works? --- lib/matplotlib/_constrained_layout.py | 3 --- lib/matplotlib/axes/_base.py | 9 ++++---- lib/matplotlib/axes/_secondary_axes.py | 32 ++++++++++++++++---------- lib/matplotlib/axis.py | 5 ++-- lib/matplotlib/figure.py | 17 ++++++++++++++ 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/lib/matplotlib/_constrained_layout.py b/lib/matplotlib/_constrained_layout.py index 57ca7d607cdc..189b9c986579 100644 --- a/lib/matplotlib/_constrained_layout.py +++ b/lib/matplotlib/_constrained_layout.py @@ -268,12 +268,9 @@ def _make_layout_margins(ax, renderer, h_pad, w_pad): """ fig = ax.figure invTransFig = fig.transFigure.inverted().transform_bbox - print('ax', ax, invTransFig) pos = ax.get_position(original=True) tightbbox = ax.get_tightbbox(renderer=renderer) - print(pos, tightbbox) bbox = invTransFig(tightbbox) - print('bbox', bbox) # this can go wrong: if not np.isfinite(bbox.y0 + bbox.x0 + bbox.y1 + bbox.x1): # just abort, this is likely a bad set of co-ordinates that diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index ef5cfff47b2f..25a6d2db3966 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4210,7 +4210,6 @@ def get_tightbbox(self, renderer, call_axes_locator=True, bb_xaxis = self.xaxis.get_tightbbox(renderer) if bb_xaxis: bb.append(bb_xaxis) - self._update_title_position(renderer) bb.append(self.get_window_extent(renderer)) @@ -4230,9 +4229,11 @@ def get_tightbbox(self, renderer, call_axes_locator=True, bbox_artists = self.get_default_bbox_extra_artists() for a in bbox_artists: - bbox = a.get_tightbbox(renderer) - if bbox is not None and (bbox.width != 0 or bbox.height != 0): - bb.append(bbox) + bbox = a.get_tightbbox(renderer, ) + if (bbox is not None and + (bbox.width != 0 or bbox.height != 0) and + np.isfinite(bbox.x0 + bbox.x1 + bbox.y0 + bbox.y1)): + bb.append(bbox) _bbox = mtransforms.Bbox.union( [b for b in bb if b.width != 0 or b.height != 0]) diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 20080141a183..44d8414c9f41 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -21,7 +21,7 @@ ) -def _make_secondary_locator(rect, trans, parent): +def _make_secondary_locator(rect, parent): """ Helper function to locate the secondary axes. @@ -37,17 +37,18 @@ def _make_secondary_locator(rect, trans, parent): *parent*. """ _rect = mtransforms.Bbox.from_bounds(*rect) - _trans = trans _parent = parent + _trans = _parent.transAxes - def inset_locator(ax, renderer): + + def secondary_locator(ax, renderer): bbox = _rect bb = mtransforms.TransformedBbox(bbox, _trans) tr = _parent.figure.transFigure.inverted() bb = mtransforms.TransformedBbox(bb, tr) return bb - return inset_locator + return secondary_locator def _parse_conversion(name, otherargs): @@ -174,10 +175,7 @@ def set_location(self, location): else: bounds = [self._pos, 0, 1e-10, 1] - transform = self._parent.transAxes - secondary_locator = _make_secondary_locator(bounds, - transform, self._parent) - bb = secondary_locator(None, None) + secondary_locator = _make_secondary_locator(bounds, self._parent) # this locator lets the axes move in the parent axes coordinates. # so it never needs to know where the parent is explicitly in @@ -185,6 +183,11 @@ def set_location(self, location): # it gets called in `ax.apply_aspect() (of all places) self.set_axes_locator(secondary_locator) + def apply_aspect(self, position=None): + self._set_lims() + super().apply_aspect(position) + + def set_ticks(self, ticks, minor=False): """ Set the x ticks with list of *ticks* @@ -264,7 +267,7 @@ def set_conversion(self, conversion, otherargs=None): self._convert = conversion # this will track log/non log so long as the user sets... set_scale(self._parent.get_xscale()) - + def draw(self, renderer=None, inframe=False): """ Draw the secondary axes. @@ -276,6 +279,11 @@ def draw(self, renderer=None, inframe=False): """ + self._set_lims() + super().draw(renderer=renderer, inframe=inframe) + + def _set_lims(self): + if self._orientation == 'x': lims = self._parent.get_xlim() set_lim = self.set_xlim @@ -288,9 +296,7 @@ def draw(self, renderer=None, inframe=False): if neworder != order: # flip because the transform will take care of the flipping.. lims = lims[::-1] - set_lim(lims) - super().draw(renderer=renderer, inframe=inframe) def get_tightbbox(self, renderer, call_axes_locator=True): """ @@ -309,12 +315,14 @@ def get_tightbbox(self, renderer, call_axes_locator=True): if not self.get_visible(): return None + self._set_lims() locator = self.get_axes_locator() if locator and call_axes_locator: pos = locator(self, renderer) self.apply_aspect(pos) else: self.apply_aspect() + if self._orientation == 'x': bb_axis = self.xaxis.get_tightbbox(renderer) else: @@ -323,10 +331,10 @@ def get_tightbbox(self, renderer, call_axes_locator=True): bb.append(bb_axis) bb.append(self.get_window_extent(renderer)) - _bbox = mtransforms.Bbox.union( [b for b in bb if b.width != 0 or b.height != 0]) + return _bbox def set_aspect(self, *args, **kwargs): diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index fdebfb2156ca..73e3d56c00c9 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1155,11 +1155,10 @@ def get_tightbbox(self, renderer): if (np.isfinite(bbox.width) and np.isfinite(bbox.height) and a.get_visible()): bb.append(bbox) - bb.extend(ticklabelBoxes) bb.extend(ticklabelBoxes2) - - bb = [b for b in bb if b.width != 0 or b.height != 0] + bb = [b for b in bb if ((b.width != 0 or b.height != 0) and + np.isfinite(b.x0 + b.y0 + b.x1 + b.y1))] if bb: _bbox = mtransforms.Bbox.union(bb) return _bbox diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 1f41fa4b755a..784697a5ccb9 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1633,6 +1633,23 @@ def draw(self, renderer): if not artist.get_animated()), key=lambda artist: artist.get_zorder()) + for ax in self.axes: + locator = ax.get_axes_locator() + if locator: + pos = locator(ax, renderer) + ax.apply_aspect(pos) + else: + ax.apply_aspect() + + for child in ax.get_children(): + if hasattr(child, 'apply_aspect'): + locator = child.get_axes_locator() + if locator: + pos = locator(child, renderer) + child.apply_aspect(pos) + else: + child.apply_aspect() + try: renderer.open_group('figure') if self.get_constrained_layout() and self.axes: From be9293d9fd6f264634971b7c8363b03478118958 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sun, 5 Aug 2018 16:16:04 -0700 Subject: [PATCH 20/22] example and test --- .../secondary_axis.py | 87 ++++++++++++++++++ lib/matplotlib/axes/_secondary_axes.py | 15 ++- .../test_axes/secondary_xy.png | Bin 0 -> 53025 bytes lib/matplotlib/tests/test_axes.py | 43 +++++++++ 4 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 examples/subplots_axes_and_figures/secondary_axis.py create mode 100644 lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png diff --git a/examples/subplots_axes_and_figures/secondary_axis.py b/examples/subplots_axes_and_figures/secondary_axis.py new file mode 100644 index 000000000000..040be159f0c7 --- /dev/null +++ b/examples/subplots_axes_and_figures/secondary_axis.py @@ -0,0 +1,87 @@ +""" +============== +Secondary Axis +============== + +Sometimes we want as secondary axis on a plot, for instance to convert +radians to degrees on the same plot. We can do this by making a child +axes with only one axis visible via `.Axes.axes.secondary_xaxis` and +`.Axes.axes.secondary_yaxis`. + +""" + +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.transforms import Transform + +fig, ax = plt.subplots(constrained_layout=True) +x = np.arange(0, 360, 1) +y = np.sin(2 * x * np.pi / 180) +ax.plot(x, y) +ax.set_xlabel('angle [degrees]') +ax.set_ylabel('signal') +ax.set_title('Sine wave') + +secax = ax.secondary_xaxis('top', conversion=[np.pi / 180]) +secax.set_xlabel('angle [rad]') +plt.show() + +########################################################################### +# The conversion can be a linear slope and an offset as a 2-tuple. It can +# also be more complicated. The strings "inverted", "power", and "linear" +# are accepted as valid arguments for the ``conversion`` kwarg, and scaling +# is set by the ``otherargs`` kwarg. +# +# .. note :: +# +# In this case, the xscale of the parent is logarithmic, so the child is +# made logarithmic as well. + +fig, ax = plt.subplots(constrained_layout=True) +x = np.arange(0.02, 1, 0.02) +np.random.seed(19680801) +y = np.random.randn(len(x)) ** 2 +ax.loglog(x, y) +ax.set_xlabel('f [Hz]') +ax.set_ylabel('PSD') +ax.set_title('Random spetrum') + +secax = ax.secondary_xaxis('top', conversion='inverted', otherargs=1) +secax.set_xlabel('period [s]') +secax.set_xscale('log') +plt.show() + +########################################################################### +# Considerably more complicated, the user can define their own transform +# to pass to ``conversion``: + +fig, ax = plt.subplots(constrained_layout=True) +ax.plot(np.arange(2, 11), np.arange(2, 11)) + + +class LocalInverted(Transform): + """ + Return a/x + """ + + input_dims = 1 + output_dims = 1 + is_separable = True + has_inverse = True + + def __init__(self, fac): + Transform.__init__(self) + self._fac = fac + + def transform_non_affine(self, values): + with np.errstate(divide="ignore", invalid="ignore"): + q = self._fac / values + return q + + def inverted(self): + """ we are just our own inverse """ + return LocalInverted(1 / self._fac) + +secax = ax.secondary_xaxis('top', conversion="inverted", otherargs=1) + +plt.show() diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 44d8414c9f41..92ac0fdcaef7 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -40,7 +40,6 @@ def _make_secondary_locator(rect, parent): _parent = parent _trans = _parent.transAxes - def secondary_locator(ax, renderer): bbox = _rect bb = mtransforms.TransformedBbox(bbox, _trans) @@ -111,7 +110,7 @@ def __init__(self, parent, orientation, for st in self._locstrings: self.spines[st].set_visible(True) - if self._pos < 0.5: + if self._pos < 0.5: # flip the location strings... self._locstrings = self._locstrings[::-1] self.set_axis_orientation(self._locstrings[0]) @@ -187,7 +186,6 @@ def apply_aspect(self, position=None): self._set_lims() super().apply_aspect(position) - def set_ticks(self, ticks, minor=False): """ Set the x ticks with list of *ticks* @@ -247,11 +245,10 @@ def set_conversion(self, conversion, otherargs=None): # make the _convert function... if isinstance(conversion, mtransforms.Transform): self._convert = conversion - - self.set_xscale('arbitrary', transform=conversion.inverted()) + set_scale('arbitrary', transform=conversion.inverted()) elif isinstance(conversion, str): self._convert = _parse_conversion(conversion, otherargs) - self.set_xscale('arbitrary', transform=self._convert.inverted()) + set_scale('arbitrary', transform=self._convert.inverted()) else: # linear conversion with offset if isinstance(conversion, numbers.Number): @@ -267,7 +264,7 @@ def set_conversion(self, conversion, otherargs=None): self._convert = conversion # this will track log/non log so long as the user sets... set_scale(self._parent.get_xscale()) - + def draw(self, renderer=None, inframe=False): """ Draw the secondary axes. @@ -334,7 +331,6 @@ def get_tightbbox(self, renderer, call_axes_locator=True): _bbox = mtransforms.Bbox.union( [b for b in bb if b.width != 0 or b.height != 0]) - return _bbox def set_aspect(self, *args, **kwargs): @@ -482,7 +478,8 @@ def __init__(self, fac): self._fac = fac def transform_non_affine(self, values): - q = self._fac / values + with np.errstate(divide='ignore', invalid='ignore'): + q = self._fac / values return q def inverted(self): diff --git a/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png b/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png new file mode 100644 index 0000000000000000000000000000000000000000..4f4610d17f74247d24e210c28eddd0babf145d00 GIT binary patch literal 53025 zcmb5W1z1*Vw>A6#DhNt<3Mwic(gKPoC`e0ph=6oANK45^BqRi+Tj_2@x{(f*E=kFM zEHER+h-K&8tRGo10|>U)k$YfrQK7}&5^>5BZfvVs`YdRbfaPO zf#Sh$7WpsVWX7__oeZ*=7fE?ku`-r@5Tax=QcF)S8~mJ$!*=b>>y&HgDXTe6Upw7A zJg?bO`RRQi(e7z8pv4og?T+1Fu>I9(tewccAnA*bF8=q+P>Hma%`5Ql7kO{c-*2SR zF$4+DzalOdx*e^RP4oW1Nfh;@&5%iWT~fj`F)R@OYV zmA4%~zjk#!Qd5g`Ks3a%7+pja|K(JdL`y}iA;MAvnK;o)KZmNz8o(e0g`#6FmKr0~VS!67&L{d-WN zuq&@CP2JgP70PPl`t|D%Gwg?j+ztxgO5R|&eLGWayG&WiY@)(81AfprFrawh(j{yu zDXFb`47|YpeubK06E^j+q>Z|>+QKhih~SPXwhP^jjg4aB5)wFR3MS<#L76S;l?6Jr zZfY>ISgx+FX!olR=7Rm1-@}{=-tf7&Gdij|J2%I`rc=w}i$Ay6mx^3}e7I6@wVOS< z<57}`do%p-(UT`Z_+o*wq9;oH0s=g)G`nau+1XFO8glJEOjB6O?RI(o{CSG&{bBH7?Ij_S2-IeUp-soOXv)cGWW~UYdAXj21pXZn1pdW3iWB&)l4K zI8Vd4FfJ(bCydtE*f=;QhJ0&4Ry2tIp+Cn*A_9VlsVBZ1o#w+ZIDAUVH$9?fn!;^_qGMbVPGC%4B4p z6Uu9p80o+|9dW+wz1G~?x?J&;fq`N9>lK>_{-~#Z+{WG4?%q{0aC!Oi_CJx4RGPnf zlOK``+TH%=pMN;q5A8Y=gvi?4+ga5mYMi%!(Q|PTa&mIszH_I~BmuSAM&~(GwNcxY z{XD05I2k7|Z>yl=?9W0HzuN&vYdBp&0aw>!jU%GWs^JGOUNE}sEGCB&21#Pt6&1j| z5G;LofDUs{^20{pp0F@ax~q#zN?~DP1Dt=`h=>SpGHoZclfzvfI3L8=7sIFp`d95o zM@Myc7W)5Y9>Wmjqq`OHNfxD=$a|>pszwB4Se%^J* z#AocjFLz9rnWC`yEGFvxaL9U7CGeD#l&*)_%ukw;h>G_W{`>du=X~%) zdessfT{dd=cRW6nydOU3;+qbH4Hu|UW)UmDTW zno}w$C~&+Qub&}T!x?_{ z?(o?&sUzFAFdDfmC8v|!5uPcj>k?tj4L+DxwM7YdWTp1){lvUQ+z&I)WS%~4EHxiT zB23M(FOuKB>h3=%P*G5LU0r?8Zf%T2Mn>jYcY)k(tF5_5PEJlA6*FX5wI$%7=WEws z3keB9D&RfxfH*rquC9N+z?PD6v z7GGBkn&6H`i_>82+atfu8(Gn%{^-#~O)V{XZEajKGP2aXCSvV}WSwa;w)M3)mdVSJ z?H*AS+ez>r!$}DXK|{_aG6fe@DYjc+C$VW)W8J!SOX>M@G}Q0kzvblR`?7bRD=U{e z)1+@wR_e0Z$6@TWy{ba6#Mx3 z^FQvnw^nsd^v{;*j#TTEm`HwaZ$C55^4itI+ zeE5KqrDVFw1w-5U_O8`5mG;5@_BMvx2t9myD~n%XpkxLUGjktYy{l>*w%qnBKAu{e zUse_{#kHuX-`iXoPlwp|3Qmi{cbVDQ@!XI0{30VGi=^baP4AZ#W*%LxuB#KBwh|W~ z%pXi*)DazyO+6gAqot|IFS9}c;~cF!Y&jhg-7E4K3?EA0*+$zw8NUB_JqE+h(;|X~ zVo{g=wX{U_UNZmhE9>>|1^B;xXl`6wa%Cl@pP%36)|R)Ak58+u7#4)b~YD z8}P&ur6~kGiDaa|dzUzvjQz2d6~~mv(c0sMp3ctZn*Avp*tQogUex>Z`zOGpuUA58 zg(*EeJe>Bv8*^Gr@JpsCyaM=gaxBGO9%NG(| zIb8Q%VbSk=Cyq?t==uB@qcSIl8Ofax5$+xwe>Uonlaur6?3DhLp3VTdu&=ML+-28v zTm3Q2o_S+YTxUC(j+-}~=P^N6R+i+GCk@FD12(5zH)a8hUG26sGV-&YZ`Ff%OU`8& z=$&(uOLTU2R#H}0;%^!#w3-z|PzMDC#j^8@uDFY_(&-)uyjc<4U;XX_pbt_R9l!hG zU4SU~badfs<7F#V0*q4ANk*ZUb|h$>FR<4hFf=wbZMev!DbNGD+Fs}hsHze|U1MUx z*4EahaWR0+M?u(T%5_Vl^i{*VJBqz|8hj|gruV!sX`N6X_$)1~P9Yw#sC~W=F1$yM zPF1k{AX)6#CjFAU7t#{$_^Z|A)owH-q)nJ8W#1P);r`rqcU6JA*D`@tSM_nrtd(S`eDz zq9I!LXJHs`Z~g8zy!{-#Mo_@Ouui6xEvrkNTiG_A(LF0W`?V&U<>ra1s%qw!ky+Q6 zu^gf4j?&W7!jckmJC6#@Fa)Q;-Vm3R#6s0iPt#dgSU~M}|MBBXj56_ocNEiK3R7xp zYEH~}XecP4M?JlWgVQugysA^D*%)wEfx@}mJJNHpp|)F4!_mkn?J*tSaGn1FiDZ+2Tcs=E5b8?z;16*!KTZ6gPi& zNkghmsFZlRX;Aw~nnLKlgS51C>NXQGwsZ^!T2xe2V0gHS?Ac;!_+|^4&J3jRW}1rinsT^QJ`0GzrK#E_>J>k@arCN)9V{ z-Z_2!8a;IB;>C*)QcUbBpjt?&uGT#H+TL!`)dAUJsabX*-{5rb#%(2ZS-icrfF4}m zG(WvMyG1FpR@1P7PwWtm3(LwZw;bEv&mmI_VK{Vu4=@~6ylj?|{hM3R`%c7u`@I>qNsLYu;nmly|Ox9hL_}dTH)e`M5ZTC zE@$WD5Rj2&)QUWDaNuRQetmXvF|aRHB4>otab45xa4BQCE;TjP8j3<{ek&Ae^Jt^a z0AgH>uxshm_?Ovz>FP6sNxyY%_q*imqA5vMQwlC;EJUIFRx@5uLC;62XcLHG$tx;e zPjuf?L+VQ41bj0xGDeXMpJOKH>|BAm_N}8sqnMh2K;Dc-ri$(L^nv^Z)~Q?C6An)Y znclfL(>x5ZKBUbNkXg}@Seb{C1sg)r?9$Sm)wQ)20P*=A7RLu$`lE$<%XRQKRBoR8 zD%%zf+CQO;0%NL#Yv zj~&R_{_1P%H20(4Id_JaTOwP1vSd4Zf&u~p=w8UF z=(9OIW{JeIZeyR&xGgG5*V590g^N4vasV^`~ogDjjziTZY4jW1z^xw`=#9yLH#pI!XuS4 zyU8|ZXJ;r>y`g>|p3dM?l7Mylq;LnObxbpW*V@x?cCBUs4aF`zfq_G}l2sPm|MS-` zF{UVnTemRL(9l@bm35Mg@+!N=bpHJL)6ml+OICspN4l#o*e_<=NQ<9tsE$Zv$bg@Z zhdQVI9RS_>rlzJ*BYb(<{e8TYn2Lpis7St6?J&dtBeG z@@0=5#Kgtd#)?81?D^^XM##(<{W#(w*2KOw5Bl!3a-@W+kH@Uk+nT50j>iz>Zvlk* zRq4b!3rk)@=6=51Y@bMNU;;Hd6|=uB1A{^Pw{HqMHI)E!C`Ie;u&{{R+n2N+Ly@_; zyUX)3)KFC@OsD1Z#&VHeWy9+<-{B7ynMEqvs1HZ+*x{@8?&>PL!?q=^nLX$wf;zUqjX2O$Bme!Ci``Kd% zmRlQH^o1p#?>eX%`WGj>!9o2G5Xk#DR{TY(<#a8jXE==zDS*9lyQP0z;{N9ND_WEk zqkI`QMF->D_~=fjwQQ=VhC2S+<47@*ImvIGc7c0N=Du=HGlp=@R?8s$m4!hj07#?7 zCPaev%NGE?rKB(*G?r?O3y)W&D|$FUw1w^jAAr+rT9v*4_w>e!O&;gRf87Kue)qH# z;&WbJ-mnp{B)}HQDJuHNr#{ZgxW&k*Ki3=tOkX2Zmm$W9&X3u7BDYp$zO}4Of4Yx# zjf(+`xs@aSjHGA(Izx)X?2jnIdv#0v7E=oPPK9K{k8r&cc$x%CWE3;z*fdJ7Koa}f z*(q*n%A{{#KmfrL&@W|$4MS7p(=;o4`{$zi`uda!`<=1ez}NZI)CfZe^@Z{}*|;YW zwY=U(LrWWWdUBNd`7t#3;J6p23RbC}bg^&T+~abA5ds<%I-))gtuqGRrm~D?;SC zBKm&(_)*`|^5~?jGQ3JPVshK*lc3xVqZ6f_Zw+mV z7qErXn_PC-&H(_a$xLU&Z@Ycg+sW~jr~mdPqld4pIv-CmcN42=n43!>=q)KKDS5OpRfC2C zaLMod`vx+RWMqyb)dsC0P;g)XTqW=An!vj=QaF<{Aa_|;A%>W_Z8yO0Vw3Yjr#C`y zhVupljAClH__KdtPmFgFQKet?tGU`Fu=Lmz7o4{wU8lPGCn#~KB`p?thjU{A-9s;2;q-`!NH1Wn5zFRVNzAt znC8*-{F;wSu$SmQ6S;xn?BR&#^Fu1R&hFR=F6qgbz;Bcn8!KIO2eu#1>Ne?^M_3Fc zy`dr?@CK&9V5Z(1$PmAR0(R8Lk00+FARz4E;NWgW_u+uIhK)LhyeqPS5H?|Sv>pn! zj`yrhm~?JluuUE&%jqulr@^T~Socc1C4{q}zUL%Y(-Yw=V8u(vz9#9faFYW@MHf;R zFk4M=VD1m&dTOdIkZzHQv+%I0+qxu@2ZnvfP~{079X!!q!+-eK_IIkLBez$H#6E%aT8Rx&W0K41~+3Wl$Ycu^^}E z54X?BmS@ithLr#Ii&=za;G)WM{u%R`o`8f0+xJq1!FC7-{}}AcbrT^H71)bV&cHE! z4D;)YuUcYCV#r0o_$cw>VB=s8D~@DD(pT#+mUaa_67{!j7rD}e}t9C>RIXY-x3ZA4{xh>-qtlS zx$Gx~CMzqun^le~)NjEyG&H=%&rco}7FJkX+}xcYkG_Q`&9&dRh6uQjm`fu~y=bAPsDi$9vl?aqT{OT~<>sRv*AU?nGl}bC|d@yvepm z)-fh)mhfvD@`kJk8J)W0wnD&m+u@;p*C%{`TN*ufLbyk{rmEkQ6(TvMn-V&rWUoGu z;PPQo8ALyV%Dn8qVE~bXkstV@s+`Kp*slKs21<*)&?8~Yle({$M(s}C4fspCJ1P2% zteb9W&BybZ9{&i>9wU^@TFUY~jCr#6AEZ&&2niUB=USBuah;=&H@=7#!Z+6q}Gmirz1=)=UG2e_KP$BCMz;x9!hsRLk zj$gq+I6#V)p}7ypZT|e^S)85sUF##*gdGg`r?^${ZsyOkHtTI=Eq)zQ)_1d-J?Bwy z#^$Moe*fVGP3D3Jj+PRy!f^x2;DEz|W${O8y{y7N29g=KpGx=Em!5tp=jv{yVKMWvIVu7uqMH;VIB zdHhs{f`N}QMIrybstVpKm&DnLUgRE<8^1eN!s2yUD}t`cVy?!}c+2EpS%BF}pOdzr zNVkv{Hws19)4Esq=TEX#VG);u8VxQoV@pH3zbTPBtT7u#A4}S zwT<+o989x=b=xnBhqEs`tSqh|NbM6JACoEUOZ0r=Y2U#nTA>#bAHPujoixcV=C*fN zgEBMabtT^l@%CT=KxRNDu0Qzo8(2`aM?+XvWo^i`ai4FTknIsBj=w<%>$2U$iI8Ln zI($s$msGs|2A9M>8rpQs!C63C$8BW`hM`$VbrjUUyL$O{m0Iuv1-@6Y6qk&J%nYK3 zW;u~OE<>XHjnmnkfsIT#^-B(oLwA_hpwHh^^GOG}@b5u~3q#>fB<3QX53-czK&$-P z)6@3xfzQm!N=|U)x&lB7AZkKQl$Di%&*-hx^zaY?Qoc7!@zxc}yOQiBCE`$~f=sXx zx4yWDWofxlmEQ5w=EuUqG>amjTdAu3?MtgNjYfLYYFEI4^L0aP&vBPP<3k~e^m?024YG|i;} zGBYzje>i4jYN}T4D1);Uv)4%@U$dfr<`_sAD_dK$O#y>fk)U>51L_E#1F8`eBqW|h zzUY(GEQh-#p2W}FVXn~GNR%3ShH$AerZ9{iq2((bThcyUqZ=F34?B|7AUDlrWMG&# z?dCr<`+f14`-%5A4fOpB<@zg~9OZWjyF>p7mo*WZzm1Bzc?pk}Wnyv?!9jkZp`nnd z&G&voO^5Dvo1NX#W{QMa84E=j{_*3rybcf}k@~c+&eIc-acNywIiVJh(@GMt+n74Z zcR=BTzHVZ5I_-H@)9{Czi|dN|e%Z7K^WokaGYbpL1=QB0y=H7{}ip*OG_zp zb90{!Pt;tbtcZzSg8vj_>f=yC!XR;=+_3TR@Mc$6S%wRA3OuGX_wdZuUUr|#E2&+l zJvvY!D0WK1@;rN6uApS}4pyTSOE1R#(@leTkrJa^-YDK;<5dHM_9ugCR=vNii=r*B z2K~`Y$2d}4i&2s=*G{_LUl9!D38Q)nx)SP|jg3u+)*JN?GS=Z?Vg3L(;RkQuzdy&t zKcjV8W4VpTK94=VN+&|i6)SyG4m@k>c6wUcEb^D2Vq5R6s%ci*U0zyRaymQN-&N4C z1x-Oa^=eR15XuYK9aF7*5Y7!MfN`Ws(c}S2uJZ#QotxB2a`X3M8(F&ND6?tmQGji7%u&VMCV@=i0}br z_H8DnMuTu+|I0*-_ZcHiv@AjNzFXRpAo0sCFKZ1A_2orZf2Ij+F7*+UkTtZImcc;j zJ*EQOQ))4Z_zR9tHatfvwI5nUZf;tA%E(9scK^|%N2=vk^uM|jfCpniSvd}$`3 zcqCyRMGa_VZZ4GRpmJ1yVHN9wvmiRh@oAru#&T?jNWZc_Z4TgM{y54uIuBE>!!8}n zpcC%i)!IVwUw7k1Umpb3%~EO zYE>#-z{E6@)~K*aLzsa*!7Iq5lTKDSTJB&;pAhVTeu$wm%n>W?Fd`46HNsLfgRng` zpu`llI8pH>Feu2xP76E?#pK*CLRyOfXubiaTYTSdIyu?l?u|Qlo}lpW+_@9l{l(+N zrLV@-Q7d|sCA7q#(f4v_VpJcwfdw9YT-~$BA>{Or!)f#EX$>rh=zYtbx6@JAY7dv0 zzf6cOpOnyC&6*bVkd-F9E-JwEnskHcp~@)>7f#OLHo2?DVAU5^|4|9)*{=#3ZKjGZ zAG)@Q41-AIQRKN>!0LWHQp7(0IF0=GR1ZS^a4ru_i<^{8{HR-?6sr<^%@*1IQ6Xz) zaWRu~5;R>fzL;830DJuA4R)x66i{z^%Y#|KmhtjhOknwm^(|IInT2)r>QD?RxF!Im z6O)pbF52(@gq=ECYEJc{JN|Nzc#8ehf3qcU%K3Fg{$J<<6x=U#e|5&HetB`#)YSBf;zv^0>@ob-48MAk!eM#%1nLA{ zg##-~Eog@&FE9VB+pN?4pZD)Cp+L+CP}`vMG?y^w(GaTC`$_jxvQGQ5rLhK*ee<>B z=3n1grMp^^CcJ*#(R4|5a@aZ!Xs!9~o);CGQ$M(N4U%eP$)vTDyi%A14i74H5ilh% z8ne4Uv6nl9HaR&-^WedQ(K^pMmoadLAS(S!R9H}ucTP^uA$`(*3#vA2zda^&AnwxuNAVb#Dj+TmwDg^k31VQ`Pz&F4| z2WkTi;WEo&a2r$tuS-W78ajNXp--Pag{897d5J?pOsu;#*AgS@DWa*VNtw_CNwS5)&DJv}{r0|HV#&rUOQbDM#Ar zljBbeL=;CwDNUtZCneSg`->`B=kEe`30GnoI&gzI=@axk8Qt$4z3j9Rj!mret*hD7T&)Qqds+iZ2E14*K<8Xji*+fOiFMbtwE$ z5nJ8%TkH2P#(pIwC8O2Owmv<0G=fS9WkWdtWC9y1u(-IGv@TI3gq&0C-c~aat9p86 zr8?M9q@-iH9?3p88R<3o3cKj)-b%5_KyhN&3X%eH)lxalSBCS%7$UIJ6tcz}H+lz` z291o2;PJ%~!7n8}J^j&2cUKn)r0zKo^9CMesq^d?ZwkBaVWH;g&Q2^-x5;f?vHI*Z zkf6|?7K~_w>eUtx%_AXvAOc@shAf@Ds_JcDj-Hd$WQXJDK;e=TJ0vX%c&1{hSIb^; z-nbQEotmPy!u#WtUVOkjDz<=z_cVJgmC0lYI{S)hiZbM%Wb-Yau+jbcQL_Q#{MP#Av+ z4hSnNt6ufUEQ@d#$V?4zbT|M;kg}Tvnx`l+aTcoLmzL*(?EEeZwr5jm7Mtg?OQKMuD~Nd+}PzY+Wysr@OywPzRwvI zXjKjWsHwPmhl2xOL_}nnxgX;{7zcb((&0rL5LqZYQ1;`J<6MWz&!77NONfTr+nDwo z5$^xcF<2^ZVsgijD|R?e+S>Jas5O+z8k{UCKy>D*YbTA1gO&;2u3Iwk^>8G6fw0)M z{Z$N!5>u|sGsNxpop%%56lL+pGfOXhpsK>!GuP%XFb+gu%S4v&>h&>lGXx_xWzijl zCH1^+`?B$|u#&VvL}rwh295pJxWkcf*`!Iw>^>OX-hdAW3%n9s+}tgJ1lPr_tO_9y zz4-FpvfYFeAQu@B_hP`5f|-xgxSJZRA@s<>^UbUhJJOML?*cO-9OOALRgs8cA#%~| z+8V2Jp8C+6_sj2}rV#=wNfJ!TTDejoG>$;Tpus^Uc?}I_czRGid?5Z4)46SW(@T+p z@sEL(_1ULSpRiCMWh;M5OAA7*LG;V{aT^;O1s%boGuUt#bMx|Az!4eJj|*4;wjvuk2F4>XvDcDPQi`J4*{6=rmticB^55j< zvLq`pJpwhyX(g`=Ff0UUsx%rHi9RfMN@k`cd?ajI#JTv)#`W&ddzZAdw8rjk8IbOi z8|=wlKLEv!4dx~5nR>L){b$zJMfiOx( zP&DTp6wznO@F@M?pe2~?8i(qt$cx>Q&ZERxn`d*<6XT)z@(Q;eo*RnyT56u5odmPj z!cHS?1Tr3l5B5YdH8r&^?b@4`^?|FKx)ie1$>>>^+78tdVwZ1_Z1WYC|vvMxSSIgID z%k|zE==ECRD%#GEu;llvTEO3@tg4}b!^g*mNLzbr;~7O^9w&!TbKVX$$x+TD1b~lN zPJ#Xl{Mivrl}7``)KwA8`(Pp{>vOpG=SL*v#xqMx%OZ-QoV-V%_-AhHebJcGSZ^YjXo632bil=9_JnS{{OIyo#Rp`C zQnqtFad0H~c1Mk6rfsjb6Ju102iC=7Oku;4Bb;hL04BH`HHu&Tr!i?Nce;WE9AzMd zAS7l|lFk?Hhm4W4zkhasgGKp_bZ`Q^N3#GOfu?_`tBYuNAzBKl|H-V)yf{lwOG!Zo zF%>i#C=my2{SM{T)e(eMURlX-?b^ro@m~~TP}u?um|0lJ*exq_G{qao;OH{f&p(og z()2c=B8sppxnhAkJj%;VeIi)J>*}mHLF#q7AMrlS`fzZpXzblsul9d=Yzw-D_fyTy z_Lsl1MkRlUzK+!7;lT+qrW`}onq=Vj1V9rJ8R_HYg))f7K)tlRZ;7)$2<%1P)BO7F z4`K6$4tF%yRvMi>&wd4Lhvf=~Sg$2tO%HR=MP?O>BpusB{sL+Lpq0~LgfjsRyH*f; zVJ+d#CA)!|RTiLzo|kBPQ+!xytO!mZIj1UuDwG+UKEaUgL?j1iU^s|#$A1t)XopHE zEq+mNB9-MPXV8#<3A)_Q9<7IPME{*#`4xt6)^{hW)9O3RiTFLzE_6F6A8;{IW1#;B z#q)n~E8jGdN-)nU6vnEwX1GeSlgaywIf#k3d8UIe5)u%U{15hF`xhT z-fQG$C{WKcS#Q04tA>6{gZWhucGI=LM5VlyP`ok8^QP%8T7XP5Jyuis#W`MV(H&cR zXhw{GfC$#;$Cy_mbj&DG+MIxwo+iYPP9?YDhk(WopU16R#|o8@(x0`@-db21M*V z7**%#Z(@(0+pm2>Z_cTo&!c1=3$-Sv*^!ziTjga@6(PXa4OUYylyi0OkEv-?%=XVa z#|}k>Z1FuY3lzZRL&f$ z8X#LKsi>HBLR`Vq(b0Jr{X~3C2};bq@t($3AuJIY-ce2=bggRg?s1{iOFKxk^aA|4 z;OAF0V0Ab3La8!oV8T7(S>`Z`s$7RGh!_f4 zO2Wet&X@7<+A@+n-axt&!}3OCIxz0HfIBu2XnRyWjH2*G7v(M&m>OG|q65bw`j<6g zD(MM;u%G?;GW6YUM=#H>JHSa_(jW9?bS}r=;F=|xe zx@$la8x5>c<{4{*WTDfRe)V>@kfe-^GNv7r`_PF%kt5Dv8t~0xs&Ozhh(AdO(1ymV zm<_zf>~?zTRW3`Rp3Chf0eKwA_h=}@mY(=NCMFOV zj>e7-96Zru4uq00h>{RryZ@(49TB8#N0?@Q8v4)x4_&fgFD*!U{;>%;@Ug$c438Hy z&sk;SCZC#6Ez*eZ(6-?;>qo|6^FtC0UMLhqaf1ItAdM@@M~Yue0{Mzuw)l)+&+7;J zE{&&N$6xJ8lC&4U_gY4LFl>nFxs#DG7w#wRfY7vy!;f(re1w4tj*|i<3u=3$&;r?Y zXh8IE@p|YVFx`hE<~lHr0Z;nU&K=D9P74W+JId??u}L;+tm)>`&EKnSocPCd0jj!i z#XG8=-$&-oJErQxA(LaLJ5vl7eSwH0uSHJ&!K}JvDOd@3;^li zoHWr|0HYGPW%E5ikzK5O#lUr)gr_8?bctWVrek(pH`M$~V_y|fLDx{k7M37b20qDU z7tX8@uZl+EnMqwf73R5sxBG(smMI&cy{}&-r~ z$Hp3mhn1t#2qR*%bF*4@p0!2%qj-rA(+ngIZxN3x3p6x{DWQZLI}IDBPNre%otgct z+c*4s{cR(oMg*}@NS!}j>wa*&-6IbzF<|yV$RQBn{;HG@t*tpx*MN2fCOcm&eO8t9?t9)#PMqpz+b~Ya2+s8=Kp6d4H6Cb~|xvES`cx+~aMZ4BvB{Qf1 zLbZgE(ap!9lzxa`n(hOy`D;)HVWwt({zOW3yQTh%fN=SpHm|w4SJV+iIDiC@IBiG$ z#Pjk3BvWM965K;}ZGv3(1?}mm*Bi1RR*$yRllK)w7IiRiNn!hmt!x8b_O+wqAsAqe zkB^H_Mc@MM^Bfm`o-J2%@##pyPEW5a?1&=AYt2jH=H^+gfX`&Rb7ApY-L6j!PSv7K z0udsAj*gn9r>DWX5)lg$=Pj$br+4DlayN~X_%B`Hh>v7UoRbkZ{H4U?eRDdoq3lTo zTt4$9$cNL^D+z`Jp7n8X73hLj4z@V2Qu)EkMFjz{1`!} znO~l~9tdzUp)-g$%AKZgxq53-&CQ+*laz2K^0Lq758*M(iy(FxvVi9T)({hXTAa?m z4Z-+Y{rem9^8aqIRlcq~;qY1Gskpg-_t^{5ruJcM$cK7XAFJT$h+4syZ0HXUZ#12u zzYuihw_SvRXfa>ySb=jvfLcdK)9%Vh5g2;iz-SAMjVMa@`)4C))3^^^FA9o^1XNVP zi;FLzrD*7DRGG}PJibbSKKw2Oj4!$i=k_)TK9v{kAsM_;7zW%gnZfQw|#u^~C#r{_#giDr&4w8XSAOj~_3n2-(mknGm z_mHq+$momjWW3?*7W?*U(1nA|@80C1zK9YNBE=NdI5hP9f-Z{s?rvPL9jO+- zByd<8^MmY?0z?*SW@#ylvo0g!7B&vfu6iBY&>$1%LWknPnm+w%9&6FkpJc3+Z&os0 zZP#Iozqk(xX+J??B1z?11S+R>+Q;~Or7l2tdKq7tzqaUHpj~5ixMT7zDoPK!9h}xH z7B*K3>avr_#9nr?`+E8LIa4dXV}9pEe@YBGu5UugMP6Z1VuHzgAGF^tvUko;LQM9) zOuso*d-v7pJT_{^$N17rgG|LRnwy*34m{6SlBb=)vW@Iy76IE!53Ma7{cTMJ5D-el=v#kuG*Gw{Ug_G} zdXF1Lf(h<22>}XhT0Br<&O#RmIDvO)~wvv)lpS7-KHJ6 zD+c@-yfc;%B&FZtqtj7Kw^dGap`kW}hv*a=eL^%rkC$I*X=^i>s)j&R>?@Y+1?wwv zFoPkTBmxH3|IJu1lu`E~PCX49>=}(c3oDI(4o#BDX;;l5$aEr_Uo1KOh0_gDvckQW zTKt}lnxf*55mQ`*vyj6oIrw2BqN6c^uLNr<1>ln91rTkD%tqKis!N4k*3i)KY%0G? z)ijE!y81fBnSdh(?|V{JGJy+tdxrcOrROJ;UJT1^XW-HC(MSi%tE_FOrP#HKyzDkC z2#vL)BiPHUhMgf0L&aqL@r`=Ea#ka=P^9&>UBOo@tcAQbSf|4sJM4xMzr5?f|HMU~ zME&jlDtj~v)seIsybxf|V}pqFO0vmHvqJ?k^KD;7^vv%xkVVitW%7XmNajQM#~dXl za4}tiZnXQ*ItbKKUT`IJLdMJDX*C-MaHCL`B7K$X~{ppOR1=tt03%cfcG1%0rHPq#8V9ISd8w)_}L)RkB$Rl#h&6gpj)v{_`*c%Meq zr)b?7wXI}}Zw^$Ta35(^$)Var)*$L%-PT(RQCLC(i+#h5M7{%geH& z(ULb*tDQLCJ-rF-oXLO9bbq!Na@hOH{-%dKJy&oLrxe!-pko z56uVCr$4eo*9A!1SYTNP7ZdREMNm;7tZ>7hpEd$NY$^^npk1)E`W`{VcXihtuG0-& zfZumhZfUz^21GEY>}B6H;At~qND*X;s_&mP-ZWQWV`05<@M=ysgjToO%41=n2=P?9 zHiKMWz1fIEE?{#58aJ(u53ErTj51o?KtWNLwPt5yv#^T&=O04Qid9uN({(F{(`irk z>&`;U%lUJ)sz^nTH!+m+HG@WUJZYe#)7EFZt+^R&%+F7NTrqcc2BTx~X_<60wIn8r z#i{t#FH!1OKi*^to3*QUfgF^{7GH-T7`*yv->8Cs_U()-qA?l2v%v1!axqFfd$^OV} z`ZMaC0JRK=XV7Gxa#~fCx&Yj&V!Cugh3$gX&Z4}NvojjX&3z@GUk8$!bxzm05AUuA zoggsAr;hWvV!=zC8QQ|?%qy9h< zOYVdYS+|ZyIi|J|c+Sk0&L>EaE9&V?mvGjobv0YO=kdg~5UL+wPeIM(_x$oluT;nq zH#EE<6VLArlKe;8g|m}3G2px)w<4{miRsXTxIB2r*UyjVQ29n>>c6}Im4CVN`#{hR z8)cfS)AZ)$=32qr>2<~NlzW^AdR5{Oz*(KQ^gQ~IzkHp+lx*(wYh%?>HwNmL=pY6T z?MnMVljH`!G7$k!I6iuZr)0}sbIw~yFHf-3B~rekoUN{S(HY|nZF93=rj+>G*?v6qQGr4WnfqX*tgXr(0mJ6 zB)lY=Kw~lV5H00k^XXa|SXzL1{V}J&ZtC}?JW?&vzsSYKg-=a=MM6RX91!|Yl!2m) zHZxje0B;yruA}of&}(jPo`FVX|Hw!p=q4{(kIud|v(7@pZoE{L1 zysi~c?7}zDArqg`M<8FdVnbnvX(>IK4n6QjMK93B-yZh>qrHx%oNEq&q2!!&X&=#We-*FAa$=HbW!>gR5OXc86UmiOz4~ z=E_8NwgFWeEd_yZS=*k$`Awi1<#;;-=U{Hp2vsj+a76!mimjFPP3C!9t@)0Y4I8O@ zBs#rU76EvussDPv;7R6_*ynWWV1JPq9O@ssYlUbK%U?e)n00%W?p5#E!=@xA4RmbV z1DFD+lRk;|TYcPhz(JP6-^sZB&v8&F0=P`m=oIqx8dNfp3G15?=dzNgs_NfPhc^h~ zS#F|0dGRGJ`eYtabhj5HD6pgv>V7ACV=g6hC$_t&Gba}m46N_Wc%yqhg=t^Z%hUgn z1O9h$-ury579QlqF*o7 z_;mz7Grz!nS+MXXkTx8c+srUr?eYM=j-L4Nm6ag8ww=FAc}?fkh9>FM?JBC!P+XD_ZFjc~+u8Uy zT;%uF1>>#ga2($GZF9Qc+uSNo-!|;$+$qNA(=w`q&9}s;&EYQzoR;`a0zAS9hY-HC{Zpxv z8E+xgrKDk5ybG zJOhyZu;|aehN|-n7iwm=fo6*lhGc!5_C#FCk4R-;bBxbeK1IR6Fnm~*sPXSSm7Y3N zG4DnDZ9Dr2>dt;ippV#O;xipPFpCUaq_5MKYRmm?B{EUQBZ9YtVSE1AK3iQ$Wks*1 zZ$bx+vEaxzJC20nvju~ zae#A*MZFjcZ}FgL}6B3rKaTKqaG)jz6Tk z_HXO1X$d9RZV&?sC~;^g$lS4(RFTaBGk-}A4((cM`5EJI#ZfwY^inz9~t@N)&GC~{Gcd*FP)Cn0%ml88SSF~Z2mb5^Vl zMt^v_$KA;2=;&cHMpImmK-mV3=_tSLCl98aJm1kly1$6~MHv(970|mF0NvXlPcwJT z%P|j?$wJQ~Ha0dBtC^9}MLxc=RyKKderee*Q&T#vRUzZZ&m8sxCdBVDd+Smo9|UZ} zt1V_>s!(1Qla&pVlQsQr2Gfr4y9T1`Zyf*>0|{^$c&J8bxj`gpRPU+;IINI9&8=SZFRP2?oUL*hSL?qEQjmVgXl5%i=k1eC%XD@WFLzy%3E0EgNxDD zc2}*{z#Y&G@=~5g;5ShC@J-%bQ9eGEosDUo;#c1l4h|0iUf>iNb&v`_A3vZS5djMY z8H(vylmFEL#gb4nc$W`iwShJ0%YM$RXKZ{6UYzqW)^hczdwxt}{HXi;Czjc&)xu3R zxBI?sCmFYuZkH5M2qJwida%Kv2kSc0fC;De{CybPq3+Ypnn&b~))`ql);uP-<8nGr z(-feo_(8GRh|G;!w^ZUXlym&RoON(8AI`H8_5M9J9O1IG@`qO$10|iu>xV(JCi?KRS&+E#WqSWabFT%~}?zwsdUKM$HwQAmXi zNFFFt_!n$>51`KkUN-=}umWJ*x#n9hLFeuZbujYi(`iIJYMAU3O&_M;CkS}ntS6$Y z9`rHpZeQg1IMN7jgyFE9x(6>L5atShm^U~fLL&X_nVyMBR%~Zi7gdqi$t;I3PfnIQ z(@TSC0rrlDccGkL6>k%a=9BO~Tftl*^>+`D-Cv*7v$QO57B(HcU2Hmp4R1eLsP%~( z&v;!#>nJHyYB?PS$~Abg(7{4IAPsCgx}3wY%UsYc#HWp1k>tKd;jli@K7KoJd8xO> z)yduXo|37zwY%`vYzzpHhVQN%k(mevS1}0T-I#D;f zW;qIUF;z9SS$GRfE4<_Z@$#$-{meF_^J=3py$3yYo7>xf@v<|L*e0P9XiF~pV(ZRU zecQZCS{X{Iyc!v5- ze;oIH9QRT8=ks}w*Xw#+*Lj`id5x!}*3EwwS77Mlr*Z7xkm`^SI3c%rZ(;OSCNhtl zEnZ&Ray&du?@*7BK0G_cMnE^nz)>_%8hd<6;&?B1aJz?N@;%D7i^(h&^Hnrr6)=Z!a&lT0eWoEVbNW3Y zzTWnHTJAb zN)2d%OziB*^>1i(p}7JEVG6^7X-#J^cFiEw2iv{&`%r!Jb2YAa@>6os`Rdv>FQyF( z1?ry$TYuSlKQUkTl-&a+D!Int?~dXiW1>oS>`zfWx}sGZeOnG`!&JdOmaB<_XH&Ph zmnF$5dOc%%dz#}^Fo|MLKZV`+D-bS{@7=pwu`|)SbV_EyotZY9MoIz2>Mn$p=xGhqi@-B{l{y8BUE^RdFn=s+rKFkj?S(z4ZK+Lbsq@5!GpJ+|N)XGPc@>nf7Mx>E3Q+j7BhHeKLt`*Z_4lit)*D{HPI+okf?lgoj!jPJEIisB;=X6pwP z(+%9FKHUI>0)jh~pYulaz7+Dc3JB!rJ4KW@SXhv;u|-j^v}qL<7gzW88ueSU??mi? z7=5&u*00tp$upwIEi>t>**zg^Wql8mfuRdaJr-QP=^DX1=s38t-1siF11 zhf1@kSR(cS62FZ|UaFzUhYMiLB6xyEUw?=gyix&nm=+tAkPs0T7ItzP4kplET4xw9 zpDHC=233n7H6H8p0W)<5EL-X1J$#AX1@ZSI{+X9(Xb?N>wm8hvbnE2}FKMQ4J<}ah zj77Uxsh@Ew7Z;jMs`x<1)(ukU+V=>bLxT5?WawCc>hBJL>4v)sXJ3KWr|muU;=N+^ zLx-~SUrXZT-1vlr(_5~AIeS*{Ubjd*Y2RmFrzkNK7>Y$+Om&ZOJbpa5J%pbxwJCh= z`sq%_@UZKfiVAxydfx7M`SSAP>D*vi>J=NlmL#Ri^6X7UIhX%c(vOyi9sd{N2|_M_ zoI*(wKzO=l0wLMSFvzcJZQUwXzIr8m9H@!cv-jEuY^bFTOW!}rx7PR>G(#9;Jyxcq z-6oS=d6bE%?aM_^#og% ziK~`4GhyU~(?s`Re@jbl%Q#qVv|Rfqa`OF8?ELqb;;AByY2UR+z>(~FUeST(cvFQh z#q}VBnE^*0dx%6e=6#=6xu9~{YO85gp!L(Qdq31>2*>6_1?QRGJ=Zpobn7^m`QZ*xm$~etQh3oRKva_vcs?97VIc{?+gy3l_xORcANOWbk+#-jy)L}h6 zcCzBz)?X8Hyq5+j(N!}AXJ1-09~~j=7wg?AKH`X5nYXS<*xh z#=$lR!h9sW5d?riWKu_QloFc?{7Ax?33@FDN4mK^8RT=}TnEgTW(Fck_wmWgZ!0V; zj5z`Q$k6z_>f^JYL8eY~esyE5Y9WQAC&^PgPwmP@-raV=bzUA4vW4B|J#S5AxGu?z zl%11$rQT-qQTISTagDkYlftKau5$+L5~Lmd5|))T7x3(0b>aSo3%MujM`mVbC^FQk zZs0nHu+jqxJ>D^N(&4CvL@AUo%fmudMss$mzdSLp8GP_c^%#(?Z^0cB~fiRw9CHQ&g_^Y)#OO;*zd7T z`4W$E3R$v`iT&}O4~9A6E}@fY1`Mwp%wKKELh5((hhy15o3CKC2Xy?fEdWvjp#I}Q5kbJNoxV#PYB|0n>^t-Hw#n42D-(lK5g(+73)C9ao zq;B!ax-yi$F!>`$rH$H5A1ZuB{STqrlpz|vgD*9$xUe=l`>}kiT#u=hp19%U&qG@h zi%d<_REk=wN~MM;P9G{ea!a6Fj(4!lJKIKM-f|MWR3P#V4Gn}X5n)|`qnuwtLgU3- zI2s7ijpTuwUe|s1BB5adXxNcyL`DKF7*U8I2){Ue<_!J9RBz~q4<7_OB(5z?oQ%pk zcJ$}~>d>=kZD9N8y|UwIidP6FSgQG47Ed|nwk{%i3?ZA2matGQRWxyO@jB;q((Y)l zTAsI&#Y$_~T!Xksx7TgK z>?TO!($Zf)R&ss*^7;-tFvw_<_iYBJ=vaaT%Aft#?m88=fat zFOfyOeM<{W7y!LqxwitL7^)VLFs3V`Eaer<{3*ca`A*s@I(a>aL+HYmAiUxlb7uD}?7kQO6Wms^>?*vz3WYXEkSy2jQo3Cwb7a4P%LT`m0 z_;@W|!~oRk|NO%UegunAk@JlgFQgzN41;)N)S4!SLb)sG#fuk_|NgrXEk>y7+GSq4 zo-+?#d}M36ZEiXD#S5~t3qt{vYJ3}gSFUV2l5gZIT+D8K`+ld)@1rl*+}eIuox0Ef z_YAZh_IYh*(zxpM{fy}DV>LU}A0NpP-jH@N;Lv$PDi5cw)$Xw7jI(>h!SY7LrA5aP ze#Hz7C^xsD9d!KkX~N+O`g(tC`Ek!XqY^+Qpmj)|mJGXTOy*fzUeejqBLy6CYJ}CI z{q_sXfs%VHWpVT5-7mk89Z$YwD{9373g-^qU2Mow4;iPc@Tgy#Sea9B?kaoQBH0)v zR)1}}RCiJqy*q-)_1BtUb`2Z?A&~=c^CZMOFtFsj>F1YMv|T0eWEKG}9!V`>XFi}Z zIe|<(b-VNQ23u*vrRxFyPtfWEbhtii0V>vqu=4Wrk{`lNtbTHQ5;BL@WxxbHMJ*Qc= ze)@)GbWn%2$v`5O`P(L-#@|j~);KStemt`J_47@y-qETGpeId0#*uf=yAH3Gm9WPd z+W$#HSaH1#*60;Pp83LhZ?G*~q9FR;P@VlE*~n3h^w*m>93fxc9yzVY6Mn6*c68Jx zbkeNS5hTSHA)xFnvK~%*pI?>@pLM%3U~PZfUdr!7US4V0It%VfZu$`-$;9b2`q|uX z((JHf4hrp-1H=bYOMW3mBv=Pl~fO<%g=ZzT62GPLdp*maUf;3g*R zI?u-pJp8a@Zst!iUQj%9Z_0&V8w6O7s| zCU@0a>IsQkq_L_n=}8`5Vq!h9%w_kAYV+ESbF4b?Gw&BUonxu0G&gVd=aF3U_&Ok8 zDjYi<%R-x_gCA1cfEC!Le^f@vMS-kchHu?yLWV4HMQTH^dsbz^jS7L-D{Mc1}(n9r} z?Qwr!Z=D_$IW#RZUep}Nkk?gBFjIu>o6VfhNgIXt2PKIxi8=RT_noH!pBxLD{f6gbv z%Vv9bJA_p}E50Jr^|Io2wyCpo-bbv#re{MUp{D%whR3lY$Xny#kCn3s5$126v+sP0Wr`>;`UQ>RR>SCT#UwsS+LQM<9j4@ils7LnSGAI( z9S~|&RIt9bO2K#{ewlvn9t%Ka&SDkGEk6b|ONt~Y04OHj zQ=_q?p*a%Oj(7QP^ZQ54h-SYRw$U4xP+mQ^c}=d1$B($@NT(ju#xQ*Z9vT6TR&Ue1s>|A>KC~KVD5L+7sB@j|?X&PqjyGb}g~e(W>s^n8wo-#92@iNi6rTb4`T6^GPN~Q~ z)ubsY9hYB-6QUWh6<8iR*xyc^I?n!Hd6z*zAPl)sj}S6sP}84G|C`M58e!8OVRP3= zMPCbT&qa#L_wfJVR%d(P1fk(>fTDp&+=*tBe%6CG`EU{>xFQT)`7QkI z5CGurRXrY&C*c_Z@DQPfW_a3$Fi^ z+_-V0I2f%+A5T2|%FO9b@eBMv6#`YY>ynIqaL>{UO&5BxBNraE+T7T6P1JO;OHN!m z^7HIJee7L$56K5DOnTjo-3K;7#m6ryn%QkhuN|0LD*0j^r>^ziVobmmn+QtXP?WuS?pB#ri!%GKd1 zAV;^&a~1sf$k!WOC0cl1n0Wu(MHtflUMc^r4C>D-8-6|!&9qHuga%JHeYydHvfXtD z`TiH-#BsizxJVQi$X>~XgwkxHa9af=!~Ohzv$npdnBJGXEK_tB0# zwPbnUM6@XvCc0)jzYYxzF)=cp{Z^wED-16cU2nBt-Z05?Jeok~lvHqW+$M1s^B z^(^G2%HR@%N7j`y@At$w>3#D0o`ri7h(VQc|XCH@S zh`SVn0~RliJWlTL9Gjg8g9!~puG*f2ydMlil(h4yeyJUgq(+ZWbpm<;mEL{!-2sB) z4(A&x0s_49Q#(xu#M#4A=uk+18NZdxir4M7xXZNu+lAvhE5Hsgb9P`LLVWmBQ7`G0 zZ*R`T%iqHNKGw}LCi#Mr_ypa>rO$p0|F2xzV};aq-YJFJ%;5d!#c`j2*)m0l8Q5Ti z3-|@U-e#+S21T8&iC2JIzZVnw(a}*(rNtSt0e?nNK<86$*yv0L)jWuf zhHc;`b%lAYXOuL=jr?9)EKgfK?d~o^7{U_hS$Ns3K+7&(^83G{eJ3Tp4y=APc29{& zB60&&&5p8|U(S}GMQ45ATZ_h&@_&KCC=^Ci({IOwhB$hUc~YbsMDtxy-LbLz74n`7 zrdZa`49sB>@-nxNG9gZ*w_2q4(Tw?!4Vti|&hrOu2QycP4dbYQmr<%SErN ztOCr9)j#JB&4Gw5I6C%#CC|fE6_Z2Y;3!zi25U$BLPm!&T}h1>7ORZ&C?gnj8%V_7 z-tW*C1_bxgll^?D0^l-Dl!s;b>^{u5L6N(Md1#r)#`qW7R02;t)-rQ#J--UzT4(rq z=e`V{jXe^v=c=uTx;ThYCNa{s`?9L5stBGS;hb@(Yf^~_B~XWPIyHtJL!bj)a+aX1 zNx<+N{IVmWQ5FCb-4}b^@<3v1iv$&mry#6B=`tBfk@F+77$y7ku2hw}+g{R<0~$z# zm|23r{4l$8`6fUR#!qEqLT7J{5&P(W2RbMa{cfNGO`w=CV+T$=+LqSA|Gw$J5Dx_U z8~~U^)Pfe*S7F9J@P0&K8;WEOkjI`TT<5km-g1TJL`%pgD2C@rivdBq_2N^ zDO+5sa`@7(nG5yLf39v+w(B2OWwSTzx3Yrd0@A8V=+HzA_D2l-Ci*kL%0Rex_vEZ= zdrl|oXIu%b1k3}w^+4nSWsPTbI?Hjb@)XjCIX1kVW!*nnn3_z#Ox`yL3W~;7CTJm~ z0u-oB${iJPjoe#k-{;ZBb(6@ zKrWVwSqI6tm?re2Rp5AZ|!^oXw^Wz8d>wW$S4&x@1)nJ~j%~44|sL$FIb$cS@0a^F>-=9Is zl82Nfp&Z%l=Cat)h{wIis*_!jgvI0KJk zGi_N1X1)&~WcXAUnNCS{w|9D`$--=7KD%Z9*r%z#ju9FlZ}2dY?HF=f1b4tjd1TZrvA7> zGUm-QT}yrLWmfB`h8jDWY~+n@_c7Krj~Pg4=6U002rEduAmzt8G$pgj8(r((>>IeQ zMbOb~*inj*ZT-0-k}mJaLCQ9DJSP07&}0Wi(ZXb-YwVdooZWNx`q+DWl#xqnNTH^< zU`i0crYFT{MrJ8oDiW>al=&h@*B9N!vv^;V#s2mZuL;U-`6n8&1){v0#G;kl(ZA}! z)wp1^k@VK%Ia!}~Ce3u6drQOdL!m&R4UJ2CATgqgm!HmMN@X98{B0``BB9GuZh%W$! z!lVG+P(d(bj~Cq4!VrI0K$6sB@$dI3hRr13^XmEa{|*@aDS1KX{O{Pf?IT$hx%;>L zX{y${2f5Zx2~p- zul0%+`ADY2=O!5yZ~o`fXR`csvwtr3{0V8-l^lMtwyB`md>D{8KR+3+me6Wld>tB(otf>-Pw28Fz`5AXKV}qly36RiZpc`)>q3wh*7lD6NZml-biHV8gK5N&}z}!ga z1>mg~Mht&=VV9^14DG2vaZ^LK`}<8)8wfhseTDwG^_w|F#XB=#wzvJaD>G^4J=-sF ziQoalheUgXG}@WNPo!Wlcp@h!w;l$BPH?y|yvbzO7HL_mJcXsTiS4VuRRVH?L|qQs-Bl&Lo}4Gk9*M z-3>^q=06Aec)r%J$M+roYfPQ^`8TGvk>h{?dTncLpr@x*oPP3I75M0m-&%GNv(CVW zp&%6$7H%M6QW0Ub{_Y1@UT}qO)A*_q81#GF(bnS&ai=O1r<)Fo_-?+@r+2WMVfXGo z(1iKBW|iwstFolez2z>|VM*QJ3KO4K!mzMK%jl49idM9wCAGPQg%h+FfQ(h8-lGo7 z%*=!^u(>1&COI&r!@!`i>s-OK0Q)rfJAKwfS-lNdo0OM5228~MJgN9-P)wXm%A zDn;myKKQ6KUdYB9(@k!}@Xr*?Ak^Q;ode*!0u|%VEt|1=zGF}sF*1+Pbu}L&oaZn~ zZO%wmovN`qmbHO&|MtWi$4q{xeS`3@ zU}Ga#`Ws@o&jjlRW*m{wlM`egkTK9>>BJno;U8{%*oSAHDhG-7RgcRJJ=UZN`#-qA zX+9w8o>FzLO;3^=EleCf^?xT3wYe+FXnGz0|HJv+Iex~l zUO>rc6KU1Uo$W1mgX3i?*sp*jnmH{QJa=;u`@TXwY6_z%-1oF?6r2A}gtSV~e{iUn zUjH>tcl~;SaBTT6L#wx5&!2#kOyI>WMG0a_=4%Tw_{fuM`IY4+fJN8YyxzsPw1b|K ze2>0&ty`^(o5MMy!GX;qBKu$bUsJ8D{d(`#m*82xGkrDXYi^rJGs7RwxO-7yOK}h! zW|4!UYPCSB?xvE*Pk+0uZt`uD)VZ}=II}oH)RHa_Bv4|vjVsFzyn7r+oMjr8rx{fA zXByk~y zB07s}P3kej+dt7b(0-aJ#!>N_DP_qoLpGdPl6t4NsLVSHN|2#)d8Cq3ceS^7rk4B< zqD7V~G{qeo_>PQJvfzA8n42&S4phsEDxl#dk**XPCRj4IouA9F6Yg~305RgdvrT}u zn6rwLnPSMi5jzt{SV&n$Vitd5UEYwhqz-o(J;`0Vfeb5#c=LX`)4uVrQ32iNjgXX- zUs94)MhBsNB!T^#>cF8A-c(ggX;?23bgOU|HM2$b*E`yslIK#8W;7gvOD9p!iS=iM zm&F|K!R)#)Xuq7t+v#98B`sLogkQa0tiR3ZC;4ZY<4Mr^($UFmDT^ zix6LxTzxk5njBF+`T494@t_n(&D>e+CPqxoBOQW^5n6PFHPy?@y_~pXqOgEq2R5ZT zld|CS+`{aYlhY4kVveNjYhueo`YmY4(yQ|oE#l1;FCKrG7^M=1J7IJdC7sf`6YNSZ$@h$ zvLJXkRWE%Xasq9K^ZnC&hkkCbgIh}W50|NQ)aOztG{3=bL4h0d8Z1cOG=51v*7&mM zMf;5*C$-r-yEgrfBa(kLvK1T2twcSKLx{L~QU%Pu!r?l?iOEcvSAOE%fMRp@YT%`Y ze|SebvUqTSq#3TQyl|UYoJNdDQ(rRuNz)rHtMZQ%r{o1HVtN(4;*Sk?b#@YL5IFvA#&XlYwk*d$uNj5>IBmQ}1VV7%b# z9zmiwvAVACOicJ4k=0)U^C0k0hRzTqsW1vop7Tbr4LZ4P9zD3BxZ*@mDQ!OG0!BXyy40K@OmZc##^=?%}s4t;#hn?mzt@@VREvW4VWS0}l2Z z-@Fwd9dFl9&1Fo7gUsf6%#hQIOfMI;U;8wav7=#&52O5)mK8`*K^f0#lO?c6UUF#I zG`{`v8qv%iExyXQk)7xBjeJTEBpeYd;?hD4JP%o@kNR~yfX6Hd`xa}fKc)M+Ep2T> zVbR43QU`{tt*ay9gRw(Nsk~B}J%loXLpm=@G{Tbe$SKUq+XEtSE#6L=(M8NG+gQPW z4s60#Z+5iAyf|^b%B{cp&p!zXJP>^|g03|w2{agLJDgZ#uaW7&cX8j( z(wJWNWm}s!XAm}e;}snv@Zhx0rG9z_hOEURXatC{aqv4MJWgE7uDpdk1mtST5{uH( zpu=TquZ#`qEr@4qaz+enZEfXDShK}Kq zKLw?9dAgTpPEYC~1_GBMCw&Im5)l?@Y=BqhHL>(f?N(fzQAW3JPknA`tK6ewDk`6W z;V~ZBff56Lc-8Qb1dp_UQ_1b;56f+e*V#{;IFYxOiOE(&GXvGfk01ACOl%)Vq`Sku!0M=S*BuBp}IvJ3tTC780Sm_4xvYciBRB^M8U_Gm@{8m%x*60{2hS2oN6-n$*XTt{zZTUD&Fg;5g z|BLeMmqFOX4uPK?eemjT(r1(3%WRm*XaDX85x<}d$-eUSB(^szSVj0pLtx9ya+5pi z_Mv6Z!6!KiX699tRZJT!%QfKoi_QcE2~2|<$WtM%2?-6AfP)ZHbKqvGQtE>D5;5@N z#cfb#ETB}4=|I|#``(Yr2>ceT(m-HugP0_BX>L-Ayj%9Z_CsD9p8#)5aQB!<{_tid z5!%?A#eN@?$sdp}z?+Cl2xA~$Sg8|CT{I*#hrEOiIP`5LKCyfE?y8<11AucOc%HiY z$J7>0AO+8TbB!7Sb6jg=XzF`1o|6Pr_n0lFo^d}b3wjSccj6B)E)q&hR%E|}kchHE znkk#{A|=^cU2b}+%BoqE*x6KuxyoPIqI3#Z%h%?`pbXdxyE+wc`2FFZinD}S@Lp=9 zkU(Nl@_5cHEGzkBWc09SHj~WJ25?zcR4zDxFg9OJYsM8LXJI=8tIf@bj)aLfzNm(v zjm)AMAOx;36a;|Xk9k+0ckw*3wCa_x9u7Wnh}{)*3GcVEo9yZAEGyFO*sln^)V-f0 z;RpH{aS_^0WIJG@nRPxWD=Uj|BQEg<@l9xc7no7z811Xyuj7?mKxu@q@a-y{!u<`5 z5TK-8>80~LZm6#x1CQ;S1rV=TM4_XVfk|&{aBpBdLiHV{Bh7)kd zK1!4OpiYAP*-L9Q>_cYRhZuDweO>MR|ZSJS+!_kXD`2`lah^macU zt>3BD6OYsyLxh-P4iQZfEP*~ddM%ZA+C~v^*k|S#wKjf42uts4iht1E?oG+-|qo!H!Zt3;lU#=1si~`-RVpqn9j<_l4ccx1(lKM%{Tb8&Tg$NT z2nz-M{L6G`M%aBSU;>XXWnseAiy|I8Kjer~>yjhzP}?p?ylPFVqXwhSx;b$Zc4h?p z1k9ngl1*=&FiaInj=O4c$A{22}EcJHnK-Cp)PJLeM%02mI4+BM8lvh zQ=M&0(;@&PuXX^2A*?*s+Qx58qHJ%VDNs?^Z%5BUWB3w^k z2U`7i_V>1fSI7~L^>Qz4Ms;ALt!vbpS4W9)MNEkLm6-ICjW?rOxU-+p2>ON^y5wlMNgn529NuH24nY4|2}l26OnTCc+QqCvCjF)Jgi82yhwV4_$%ilYXrxF~ zWl6XDgY6#aWPO+42p6s!wq5rgnosDPz~A>$y9QG`Pl?KRC%-DC37q;weRwXe%_}G+ zWiKdr6o}-Y{1TY}+F+=!P&UHxIN8N0PrHLN&BDUs+@%s_i%320CMg;?m6}C3fKJf= z#2~KcDk3vsubYk01GL=odL5Ryz)Rz}z@o*TJZFD<1-~uNIIFMs&LnL1G9^mzx+7`t z{j)0ZYW?9+i{Y{_|4a|p=Dauwv0M`IZTlLNokz$q~{Jmn=l zGcz;%XoUP3Bhrb@$;Zok`0oAtAyCR8$nrw?R*UKBdP%aj%eR9v;3_;)o!*vLhuk3) zMMyCC{4ik^^#1vkt+~j>mM|@gYrkZ@<=m`F|2-v|gj&j)hW9?qU7M(RReF0c3qvy+ z+YuR}3c%!}Qf*f56nr-F^9RdxSG`tNRzfi(_!jEfU~KjFu@zcN>)1eR`kqI`j0Hmb ziag?Tq=!wOJ`xmFq*Z#56SaBt+6}!qV|^<=CqzXu$Gf;UJTg2S0-MB8ykBZ;UBYJg6;z~2({T0m{*Y2rUgcr^+U@>GVMeoKs#XZisqDKf`;Xz#F`1@WYi67(XDxIM!}vYilsH_;qB2 zAJf@U+#>DCoOd5}`AP4Uaw;62N{~ed-nzB9q_h-2+@jQND41$@<4g`SX@_k$9Z znK7B25#va|FZ}n2N~`0lK~gtSYzNYE+{C29W5X$JHiXehZ3qh-%g9NXcx;YZtFz2Y z4l%N&^XvZeK79?X{tLE1QZ6z&q1v^G9bWv8cdh zoG_zdWMq8gq0Sdc3>yCa{kvy+`lVZiKXqkz*3pAcD5`_Q^q(Xg7OPt{Er2+Bvz!xZeY<;9B|ljUo~Vu)e=j5CD;2$=U&{2Js-Sz{E3rTKKphf`y2&f_9g=3eoDrm^yV1V2If0s zKE@gPdDMj5;&o%=sg7NoPo?r-h=Yr?^*$aL`l5t^#f=SdF&q$i5DJvuPv*{!Ih709 zI|@3%XN4Igv@7d4yI^So#!TqycGxJf!8-KQC&PC;@4R-_PvVtDO%F=elSe86NwNg~ zt$@y5Ot@TE0HL{AXNBfeg7Q|-eSV#fW)p~&2M6wu=xFwL*RXmqvhk>?scBxqi@%(f z*U>e*to{l%W-d??{oceNaoq7g|DJmaW^im{b)f>$_-Hdhb%Y_R|D#a({{z$w3(Z2* zo9bG_lOnUu@)dXq*~NA<=!g(xWA2)u8jU*yE0uTrcTL;`-%FR=I^x2KC!s+W2iqxd zvOko>gH}WOjEua3KnbCB*tvUmA{2u-uW#V2fVelMa5Zl23h1@E2jW%ZkKC$2vVDmY zSFEAU{vqW=ag>4Uu;G1eavlNOiD;Ivc6#Ey{eIsUSASG{`Zt*mU1&95So#}LLDAq- z!4>aB3@&%ucVvYW2o?05Y|J{<`?8kDqqz5F`aW^DoV4@3!o^70iBvDI(Kf4avi4Zl5T)i)v)G&>+|^`z;cq~*yw4=aAf!;}F8tw`wS z5c09D4~vd?sjrQiU*;H|y6r$a@@spJRi$3%++g`56_s}beUYB&H-E^!T^6VBdL}*N zYA1hX>UlOR!7i1Q_%q{)xtR9E$=5>lvC-~Y?I{wk%E=alx6v3J{QV1f$>yxXN!%%q zO6bj+of-sFZbUtiHs@C!=nEpC{^Nq0Ki)8LGe?BTO4lvPn7*$9MU5}9*z`~m_p$RD zL0$OFp3J}`KYZrMKqUd*@8pPbxNp%k<$kwg|BesE3=5*$LcxAgQE@{9P9Y>Wm;OcK zkmHjAPHr#G>cS+yaJa8BF*Vz#91vo}59P*Cge{Eud4=9Iv$EVvSWYTzB$J(IKYCBl z--|tn>u8&!uHL}$3wuhot%K`N2Tyu$sTA&*2w=IbvId?kZs|xSs}`U9Zti>=PVts=H-6!lz?D&Hg~jkrk6LX(|N0tUqw@jY z3*pC_O5@fk?@tHy&30WpR{!{A5jVg5R+;QQ2bkKp)^L;?Gb~io=ebKGBdfblF6QE$r$r|d;7Ms{^4DQ6JLVEX#)tm3Z}>l zxVwPtQJJl}DdKP6aR=S^e-xo6MtH7{HRETlys_boh)rVAsw&Limc#UzeYC~k@9Z|N zquNv};`Df~w3Ajr%BPNeHYNOHTF>J#>~*X1u<_m$du_2QvVoEI%6iY}-;l6D8~>yA zhtH=}{Zke`@F|kGnN#AA?p(OrvCnjB>T1FD7aPyKDrMwi3OStVY7TM-h6-A;>he!i z%WTz27B6sc*cL){8^k$xXR(CU^ zY2bRhNT=72;n(`(-?b*U6(qTr&wN;PR z5vAXUfnGg~@))Y4BkHgMNb+^>pY-{#Yl8xsvV`gfRZNk&(}fE*um8C+`2AY#3Vz0?|1{<+*Jcjnp0(wv&E&y)C%6<-W zl`j5X?kI-|tF8lRnq|k^7UbP*`{79jBa01a|LlnOS@R&s5vcy%jHYnX5Y9R2Cgt2v z)2pC)Lw;Di)h7Do8Opi9KnenYCtUWH=LVQGT67JvDhItbFHMJU>0FSkFFRUEMNm}6 zbSBbLq~E-eeH(JSIvgWG{|2_#;#;XV5xg`^Wy+U%hqCOffk7Hq8D^1@Gl=0)_>S8<6u6R?_I@*d@1#Uy7q2#d8Hl zLRdx=Cvr>jxzKT{O#I%Y^n2MrhG(dnhyuNMZl^*7*F(gCkko<9>?3xBOI$k{gFGRw z#Z027wU>a+o5A0JOISptIHxK;BX6P2CXe_n%v#w*8C3WCHJg|*Etrow)eGRTiet16 z&<9o$+>9b1{xZWn!@%>KnXQ^1JUFMRIWtu}J%|p8eT?xl&BM<$zZvUlX*mxMov51f zpXJG7t9~AJa6yz>^{me^Vn0b9ocxy7rT*~#pdbrk^FT+UO~KxDMlKF&2c#!9}IrJf2L`{mvnmqg+-OEWXPZLVI5d1^rsk8JmG5++AkB|oU4uj<~X4&U3Y)B%^#JslgZzvJH~ zZJj+=;SsAQ7MS86lcu;6oO+nzqi$$<{j4O}S zNRifaosn-c6tbZmp{eJ);QO)HYA0W!Xh$D-tS>L^Ar7|P#KHF2M&Ov;g+0r1QnBai zzhby{U!U<}k<_vG{oo6gb`ajG%h4GJpTo#;12a z#%%HC4u2wlz4d(k`3>pn8%4}SyFX>o8ml4q~n!rf@Vr97bqj31zqnjRiEq_@KuC`3p4_w;h6rlB5wWR z(e9zwODV0K=`zv5A1|G35P3V(yqZ(s_UOfW4BWTSHg_%Go2|)c;)~2SrjQ$=rL~Z> zY6GA>Vs9@(O!*OgRzJJ25C~HxboeBk231JD`Wba#Gv|Oe%Q`|sO$|rxl0MwD3=BuO zUK*dPO^{FiIM$}0E`QhG0b`MS;q1_Ncs;6XYUq4qZ_4_jTd{tC^Rj<|6IyQ;Muwsx zgowyVB2Xp#H(~(-YaPlz7pq?=NX=WX)$Sp}ebL7kDJ~g#)3~baOCNU=RueG0 zLB%P4;6Odd>vP?U@bvK4&uA|#!$>hE7M1{F!Wb%3Rha9;%E=dhU9zI{*5CQkA%TxA zkD3Rwg6_fgWSLRO<3F+L4{{&^YhAmX#`p z!8nAOF+4OuaH{NjKmu|LXr;n`_xfIM;)h ziy)iSXq%);JKD-i$0-tjORcISisnP(C)(oS+m%l%|GsA99$$~*A*a1r+ivGq|3sEO zwt)yRtL#=|?3iPOpU$)8216GG!GW?vpGtB&39DvINs>yy%Tlt zl(t5PuLn<@@|DX6x7i6hS)ZT^1kqc7?_aRhY}oQtLq`VpyK}fGB0;-t^PziONGOHOdnT&(I%YLcI{pmGcmb)M#9~c-J$a^ z@_iO^!o=qpq+k4U*os-_lBQoT2vkz z`6v1p&YY=CMj~ghp`>sw5(?L;=zj8gY2t{5OuvL2qYrnV`rnk*<{l@_Vr;e8`Fv9c z%Nu^7%8Z|hlB4w_ekyIB{s!BKT|@4g+;87qa6d4?eR}gcBkSLgI>&fl|F;I&FLEm? zck!|hye2-wEb-`E=u7<#Ys`+KFSfe= zsN)~uSNM6|oG&px1a% z|C7F`#DT!&iK`UzzJHT{^T8s=wWU3OGTmg`4?E0%i!J@^_?u8zNYoJ}TLU{1^4mM8 zmU>3^1}j9-NpZ})=XmnH#+u=84*I{Gr6SaI>^$pdX`*PA?->$j_0Prxle))Cb`2LQ zl-8`a#+Ur90>?P_1vhB?v1APm_SkEd-*2$;X|#j_tmU+vN!$iKgZ9=O7Kpw+UIRVbhyvd zqzDCIip6CZUou7JSR6VaadXG#@T`wB0}Ka9q%0*4-!9%i3%!{LJUO?P%3F`j<%%oM zmFx&xBzB`DbcVZC7|BHPVuUZk}mYg~F>C0c+xmn;G^?%~aPGYs( zG3RyN(5le4R&MJ)U0mn5_T$N#o_Le)7ve*s^m#fJS9we-bfdg!M(zX^gc#jF{Pg*A zKFm5nVD^hpAGi8(@9y2ta7M%l69|PvApQ1kHSH}-c}-_mB)JbC*wAyvs9G*5i;QHv z+$b*0Nh}WF0FqL6tBcy#JtTPU4@+Jb7ht>-#AzbaVZ9H1TV>=`K$$6(l$2mAURe80 zX>G{@N8)ENB#Bg-kdR$!*@-C(E+)+?Dn6#8UL0gakv-mm+k4JDBeSL*IklUXah*$F zr2K^C^Jt?&riKUFXz;#2<_ zL%+$H6;rd_)wjEDxHt66R39Ww^yD*u;DP=3{dXiL1(~C73igBgi;velJux=u)S~C- ziW|}5W*gCp=@XgX!X#b)VMS6S>o6Pn2GUs#4c?0!s(Y7{>`JcfrXkII`mz=+-5oG_ zTBS97YU1kjoQq0%QZu#QwS61{ns*uZ*_0f#C+A)dj!m}w_U@W(AO@H|U zyT3%8k@>kf6`+6nIO*0F^2^OCZ*CGflct$MOp49&aJ(7ZotT@e>EOc~)rfx{p6=L= zyC*9mNPIg41Smn@Gr!N5ih2t*mdgpbiCc}eJxLf^khc5hjw=?lzR{w|xg+1+xJs+q z@qDx)voO)!o7o>1GleH}$U?^CzeBP?0{hM19z#|+jbr%uKkMKKLU0! zs~GV^c$Q#Z?Mz4cT3*+sCy9yf;j&D556jET`^MEC?$7faI-oq&s6sc2qJU)T;2`$E zC%LAz%QbjyAW$?m%flK2penM`PL4Mhc;(%mw9vOVofsFaoiF~_!E`Ba<9=_?&A*qQ zNyIuNNOLl;EV34-UxvdZC5HQ8Vp8dMK&+XW8T~ol=vI?_30r^s3Gy*}iAN5Q{@J#t zHiR@LKcy`P$|KDxGGS#lyaGWg&%GJ~?BjU@M}!+(&E&CNEON|w5I zZ9CC$;{mPvwP9;{*%qUT+@f$8hodRV$itI$KoJW91WtS7+Zh=m7;0{C9nAl6@8mm! z46Z6W)zB;STnWFw(bH`LTHx@CV@el4&x;{GOED}B6jD-#u8Tu@agw=zo84LyzakQ= zr1-|U>L+qWCOF$2J#u6tiE#gxahuvkG&xC_vQ9(DID6+XFkmyf13>sGlG|*qL)GQJ zGMWD!{pk=*B_<}*C9em`Nd@QlG47do{7a|QY!}w<-q2Nlx{>ozx7Au{4PKL z&ud?w(>wu}Eac~(OVsWc6%ipsW57+Ci`~XMK6kXY6VVj{5ARCu3!r7GUX-AltujhD=oeiAr!NMUV zuVCVZbRGe0?Q>yNlqRCqJxK2_Z$AIf7kqJT<5%4mg#%-2Qc!+ z;mvXQ`dcLbcb}v0g%Qer)B<;T=E1L0peeO-SPrq z>%8tMA)PA4pI?|O!P^rD)r&+0K59ze3M%g^04V-oA5uZH$%eTR*$(}Rk*Sm3Zng=~ zIq&bHCrs?|2cD>jST0Czja@GM4;SD8JbZUTXhMyBbHv&C<&>92x8l^ROod;?#<>@6 znF8a2e+vy*XDq-;0zijVT`X%YJ7qCC6lS))=#EYri?Nc^`3_y4+wY6U3^=P|PV`p>UXq_FYg zr}4+ID4itfnyt9r&vgFKB30e|lV^FxoY^!7Yv@=-FHX={RG~`LZM&celWW2mlPjk9 zNq^34p`P0+ANdA7M+cvh)nujWpVRU)6=ZsaS9W3MModXkn~l@V=fmlL8fPS5P)Vx+ z~Q zt^}OQb!{&x6p;p$P?}||44D;4qL3k3hR~plnIaLHLefB^NF~Ztq0B?5?8p=m3Yl3X zLx%kKyX7X?|q-=e(s^9wghP~0D+*cDFsnA9Q0=6{;O=A zbdPEkKcvNa+3BuuECHu8f`$CwBc~!c>X+!sn=1N z&H(f&5i-P~%FKHv{oMor8xq@~Rk~^YdULEXtx{y0^v%pFH^&1~qPZrnZN3MJF8K$L z))!P)FC}_vU;{4sMqanChjfKY1&r*BZE;8K%GNb-2&5{?KWc*7HnHHxrQn&u^#G)2 zWfT?1KHT>AXXPdpCaj@_&GJ^0C`FLF!aSxX+Oy`B4pYr?+&SSlM>r}Z{T-@+qeEW{ z_smdTnWk)tm!kHao~vTL1EiHY!_Rv6irML?T2 zm2oh4yRP}6dx+DS-1Yi=rMfGPoy2n4GZ$k*=!V+BH+A5dSrR>KXeo1_uPw|_fWusd zHeY4Q?HsA??IK7ROUi7i_EfNK+(E+J3C{=w1hvG(K8&ybxVv*1=pb4}!D+SvB1umr z&eg-zq7WbQpn*X^LxU0G#u;0^XLGSTOo_^cc%@s|%H6o^c0fAgdwKYzWuc5&1omsa z`lp6sx%J2#FM>m+#L4Q123^<6zk3`XzUrRUkxzsPpa=G7OFq235SJ3{@w~lx!F2*= zJ_h7T8=73kAimvFh883j9u8Y@k9LG-!(60fv&Ijkyhvc9UIQz;e%-84tWb#AiF5-C zg#e;Hj&^wCr_Ok^xk24d0)NOD9#U)`Nt@-a{S6k^TdfUYb&#HMFDqc6RjS3u@%#PB zw(|#2-*1N}!&WD2Rn@TGzCOq9k86RAb|*7W666}3EkPk6l2hD#Xsu+t9z*UUy~Rf! z=q8}jjg|grk1FxHLG<2!$)&~&H&=G}iyXL2F8iFHZm%|tVO6*&#=86{SOg6=QvU^D z)5If93`w9P1QBNkgi9{8l}+UNmMY`PJB4IA1g-KmyM4aVSObc(-5s}lt+EWgux~j( zo!Nemze^h$9rXw54-YjlCl^(#(BH4@4H6Ey-RmNjjE4`eL|$HN+m2k1(IVhyzKD)4 z#5#r{Ln+)h;nMuszfRB7StzD;XWdZOeeco-Y9Zp5rEJqb%Ed&q1rVa-zblp(ZgR3C z!wRPR_p`wRf1%0}vcI0J%w-=L)?D+}YT~{`bdbmGZ#kqAivU3$#6gA=IiA*_^}^l_ zey&_%rbN8B4qXbmZ1BEu0;nOt?d=V>@K25LzyQG;t)u4OL2MGPHu5jI!sUMi-L!VQ zh4n{RoAkgJTk^w!Ud1&j8XqyVoJc0>5|9O0e0_R*ZTcJ2SxJ+?@AXqBZb~#;*K^Wl ztj2A;#jSq$)2!s+0Y70U@y5i5TH%@-1bf*4XywkFIdrI&+VwftQ<_+-ad3ol#V=%M zFO4!g9}KI76)+EE0-KXKs}lws^{`^Px}oBsnHEpl3i^eea&d9lg?w|nS0m;Vpr$7a z$CbSTM|D1h*cq5_pD-;vKG5Y`nYFCwcDNYJsxX88A0DCCHXT?*xy=R#mS<~N!kU@^ ztSx|%bu4T?eL*F5XnV6T;_0AbQu+bZFOaDyHM_DXV-a5;0`c(@)ip{&TH4-qrWKS| zl_jC4rcT&W!@gznOWWPWIpI7iw{z!0$YEMtXqp4r!eU}kRs!|LL_^?0^NJFkcqX@& z{K}bacCi55O~j9~>H8DxHkJAgoH9CXR-9c^3Tc4HR99A3mO)o}MFkhmxQm!FWln7e z8E7jE1E0lh7~kwR`5Ic%G|j4>YB`24JL^(6!2JJn*SR~1AV48H>|4!6-iAp_i5J($1g-(km%>3l6oUjKqorh9yFI^dky<-V$B%rVntpr>^d zJiI5_>t64Q^?|Mh)rbp?x5n+{-lS2uh8)Ql`6Fdwsyyl(dRL$A;(Z$xMJgAW!Bk|W8rzzN%4I{TLzQv+3dBAdRY6FrWC0hw;+1; zz-8&#f|BP#{?K_%=Ph z|9kdn*0DNjd$GXz65dTU6pGmNS#bkxyK_=Q&HPb~t4+HOs0M6gnMX`E&yI8!Vnx$( zVK@BMpWE~!(wbjXIzRw`=AyQ)&H~mOGEN;VP^VNiTi4j~{SF?-Hcujp@Zn<(o`U~< zQM$!FuI|o~D|xe;B5&Ro2Om7OKwhGe&;A1sU4%#t@N(%Yxtn`oc3dCb<;X|#XIZTW z#!XL!Yf@9qRbG^bfO)(%CFf|WM{p2HZ=1`%c=FB@#At{~@t%;BB#u*q5?0s(l_v>a zMrdM_?dVm-TOS)=r?^!$CIr}G76)VA3wR7%?>)C*%gfabvSzM~Q(5;lO=r;Evvjl3 z%{zlH@EW0EhbCWD{H)VG%$(N$TY@p=-rfZi`SBQmXwTH|o9e#6cRUDU-*)gWPmX!r z*!Re7{QF!ped{zY@9hNp;(*dI$(+Fx?TLv(aQiQY&nv=?HoDx>2B!|Y6+)gFL5WT) zXZRSru3Nc1feM-uRX^n2gs4_9hB8$#oI_C&b_CY467PR1cdYUNu&LwxCyo$M2P53m zqm5eRHtE)6yV-*i8$xAjEjhPm)F>8F0E;p$4B zU1FfrUElBQea@s8eeZ>pd7x6cs5$#z>7h;*V@%9jI$r! z0I_7EJbPuoCVF z)`$J-xO>QX40o7fAowj>3P$N^|TLfX;p zA3!oy#W$>KI8Yyw=599c5MFw9=cVyu%nza#+(CHzUZKl(%S%fSh%rzEH%=U#sg1Q= zgsZJe4b!OdIsbb!)8Ti)LBkwwdb_rM!|cAdg_G@J2mo|9JAKZIhajlI0Vk`J+w88J z54V^dtdA+_SS#x8t-Yp$Yrs=m9)0@CiYhaw)WHcgsM{Q8hO5m;Pr28Gt($3ycieo| z@%1qoDt;P%)S<$=67qu{jNSkh&Owk*}AF1YhNs99Y+bc z@O(vsq#N5a;XgS!H4ja49E4Z;QvO&xTG&|@NNf`$){Dou#hPu?O#2#l=1BsWOjRSk z^p19Y$BT#Z^0O(W4vL#aP#L*#uv!2T+H+^F$vATiS_A@85oeEt|M*DF>qm z_s;wJmwsW+;gZYL&PORbsV&&J|E3SKz_}%rO0)7<|EXa8MB|JPXl&ejb}nHfDcQQvMUo^I|s z_(*r1v(Gx$N&(hB`j9|XcldkBIe9naomsWQuBpHMt zuAOKupzxwBvT|H((A@nNaz4!i7A{JpA)ry_oV$5CJl zY+ZuY{|WNd8i__rhz$t#!Z{Wp57`r*Jo(6bNJu|05?p}4+K4xIN+PUyJj_acS{SyR z{eE*9=AmU{ZUxc)mKIy-5n)#^&6HjzU>yn4%rMhQN zTWlbI@8f+5xw!o?2RA$K27^C7audt0YxT#Dg^H|S9MBTwsA(U6J;w>Ou($`>VG@e$ z4KKv${<(X1<-IoXxe>TD_R}JFr8I=7+`|`kX1D8L zN~lazY{+&H_MkhFYLq6_?s_Kwg{R^^&%ou(W1+`nq9|A=@Wkk)9$}+^*N|j-WE$Go zZ}?RL#J%a~PDrNS^?b5&$K&APXCg}b==3N1b7I21Sti{$49M6z8JSm4-*2!1V*6Yx z>ZEg7UMNFuSaOFUrn>Cbl8bo;>X@gv78F;!*Zp%iS98+e$Z<30;#CacVNPX7Pa@3Lt;V>?_fmLCeg-u8s}Xq%BAxCuVcRdo~2iTFd!KcJwKaOpkN+y@&mJQrSL zhl!g$glD^ei$Kt6e(Dq}P(vssRNW@!BGzJJ0%_^)@kzVm7kHeoTF-MEvI~Y`hV6MZmoH$C7hn5_M{4(Jl>0EjN)|Dc_QwwrQ_l@8l9TA^|cGzmjBRNlD3%HtCy1MF)p0bDnhJc-)Qd zw0Nh+KA_0%^K%#UWEP**a8Z~i-|y()$g*5&?gbsCa#=X1yj60ILjnWGOR`_AtayY8 z@H_xd#h9Io!^(QKoGTl!iDD2lSm{VIv0))vdSC;4_U{jEnMS6REa>gGLqVj{#z&Nh zk#5g=zR03#cyn@MU_N|?!H}$-cFyI|9GjVU11~i_)z+M<9B?KWst10k%}4?X@g6{a zIY6?H&5L(}Sq6Md&`?9QR(3b7+-lb;?h8CdI4pp#0R?JuuJCr7NPn{E1t@Ylj}{ew z>34M3U|D_IGnm+=j*9E{w>N4g_6?1U9u;nk^2uVFJK~GWVxrV4*}8OXEq4q39S*kl zJ0H0v!`=pf_YDE`nMusq!r>1yZ% z?*^lnnG$yo@VEJb0D$qQC?VRaQUe(Hl#e+|68fKo|02JsvC`O}B{HL(eCvXPf*!V+ z?AgLKsHJS{$8?P;`>WqhOY&CUcHcawTq$%?-pTZ!4zp5y3t>xBD;g?AJc1$#_49{?ZeA(u(}q{yV)=QUD-%f0BO&-(T` zp0@ggQgX3!nBl48kG2NHWE5`9zzwxOd2BzhqJ*giba!r3Lhle#qp#$e;9V z&M&Q;p7aa*=BBE$*yfbogwP6NcBW-}#F;<2n3%4Uv+#u+{|XnI&;go{0&7{30lh4I zT++?xVWGxX^q~F9VO>Ts^;2*51Qg|R{P{Y0hcAOb1#Y%^XgFx#Y;-4-YgV-k)U$GT zk1sbYjtl=gmm92X=Wp_Vr*T_7kga2cl|UQn5=hGm;0b|~Gb=G!Th9QMAg>y@6ksvR z^PQ$&_a{d*UZ><;HM=l$Q|3B$=a<->sms~qwrYZpz^JLErG_LNxLz;`2;Aq2*Go7= zB0ILV)YL5F;+p0R?8dL6T)K+96U%po3CY!Fx&$PCaKAQ1R}F zQ2z>U85)OnBt(#4VeAgFS+7L(=}d5U!UT8oUqPtq-`|pn$78kk;P(0M!`a?$pv4I8 zYT@q^vQ10dtO{d-6bCY;A}Dwe*&YbH&`cRGjJPnyxx)qtQ?tQ#odeHd%dpW~3~j!> zOZ0e)CkLERh*pB$} z3}J9+u>y9EqgRib-8(OLH|kKm<;B2x%v}D;79a2?Vbc-_svptdQp7=p z+kaPv6WJIiT@>Mu2wizIpqH#TlL!3oU@%Ifsfk8q$_&7LgxJRd#$~0TZz3KnEu;3; zwJSZ$K3Q`Q5pOgKVd`m(e)5IU=V#flZw(c#TW$?B*9l2;hSrsS?B1ZWz)3~(-M zp0wFLRzqtfE6A(IrFX(5<4?RX@z{QHLh8XA6b97{<_k^D6@C`WRR??&E$udnaVQXC>= zRswC8gQH_l)`}~yFnP|EvP+!OaoIRz`W}cJgDsg!(!|hWa-q=_R1pIU}~R z)r*9_5$2`p=5zGj#ksNCkoWn0CLgU?Z578Gi+-aSfBZNxaS?(Qzzp?hN75Byh-&-g z|1)%HoXgbwu}M6_2<7h2i~$TYHr61P$GrNX1}29rpz)@8jN0svmS1U;p7q%}^7{21 zQ_>4#O7b-=ztd>m^*+a9i1v`xr$M} zzy}X97`ATYD56BQP@nGyIp6Z*r@Y21Gdn1!@0{f@RfxD`C?IfIQ7Sj<^y}I)EWRH* z(r+CeDGPxu&S*wGu*8)Zg1GlQigEz{f2>R5r_}!SkvJD|T=$tuyE%{lJBV*YM`dgW z?UyQtct@2ANlLB&=#*8TL|sMUaW(st;FDZ+VhMy&Fpq1;jYD;0=Q0_oy`p=OL1gDd z*M3DVAJAdO0U48POyQQ>*|Iph%)ES44<=`R>FJV)KWTA)q`MH3D=Cp;KstccWZ;Zi z)wUy`B_R0CQN$2(e{2#2s)ZU2aUiFqV#a~se$LmR*#6Gm!G(Iv)~0o^Au(AC0SZ9b3`fHQ2`9;Z2PDr& zT@Nh4W$jqq`VIfZ@E-Yym>963HOR`6?QhBDRlmHt5Rz@xg!_AYvBx@$bZP-kCc{IM z*GsYV;c_kO^Vr)G1|7T4pI0QKLJBcFhbg%~q7Zd@X8t1xMaLn+e zHUd8AuGlb9*gU#SqnUhUXdaD>jNrRdTvo;md#Ms|6Bu^P@Qwfkz$UWu$~-<()Fn*I$c)KIT?rpg-)WBO*~UHlPhu_>?^_G!7(@WDYwzRdc-d)2Ft2GjIQp z5Y|cTkDsr4UI!Xaycj`(!3j5TyB_D6s3*HDt^{XQGue86O@0Eix45;nwO|G3N+bbC zxhmyD18H#)MW8Npi$=7kfYBTp>)VP~cM_Mry}Ztq~R`gn6S(J3cf*sQ_aQy51o- z35s}(^2lxpl7D37iH(9f*s@F4Ar=}ao&$Vx-Vi5dq!W_!fZV)|2ckoX;THBEt;ev> zC^P&0gB!uEZB6$1{+K&rsuY1q_^we*n6O^jIq4s38*y@3ix4ju*dW&zM+(ICZ{U-D z``&M2u7bH#8f4wr*eDcWfd07Eu17wAL}o=uWb~`L^18v(5{!{KmoI-Pt+;tJ3l6sr z6=z@FT$Giy3!GjTZ1HAm+0cpS#FI0^$x>6|Yd>X1MhibsA zg8eQ13Ssd0zxQKkHes7o2H!t&Vd0of{0u;k(G0o2?5}xIma{_8TbW-|Q+)$+AW+=C zmn;Xu4-@_8X8EsS(K5YrOVT@M@6$SBfsc`VNsNjgg@Va*v8Gt=MT}6`L6Fw!{G4jx zhx9NZAt9dX;O^vqP>3+!J94k(3gPV07lq&d-q((G^}s9(%wy--kMjyJ=#AA)aKO($ z))i6y9lc~dFPM|16&2d{`Bl2gw9BCFU`{;!qoB_Z;AX>@o~GXuTRjgHoo^f(e3`W6 zufKWbad7a5hkn!Ub|83%>D#s9Ej0ZupuZDEb0uY;f_8k8Cz_cq(TdOBsZ~%kE1M zhP=eOIXyj{P^ah|DiZJuOOoLa(yiT(l`!pu@m6BCL=CPZ=dxvSo;g*`pLX|;*``E~ zNmuJmjlCQ_5!TRGt>2W|&{w6O<)MJ`2B?69`_NaZ%I!$oMG~>)msgIsnd(|vi!tGi z&3@sDvIWk3&NbOtS#8%2DAGM1T>1#uDp-GyB(Gb;aVhCQbP%0{oS zCkOuIDbI3HcvxceY4`ikgs?Dc>KmhQ3}`xF7zi^EFcb3nexhc;9Lx{qd9dNH@82=4 zNd5t%c(ltm4`+ql{pFOFlDU;0f$u?bR+D_LzyAUhV2h2=JxyVcE6sidl$U&I`250Y zC>=soHQWY0fY*34Giyk+mlNgh*PP4pFvphdkENs{@e+Gk>vwyET2o)~EBNV9{ZRdr zuUfs~B~=SC;BdkA&L7@QTZ z4{*p2rfO${v>N;D*?#-G_?Os8)e@hK91>aqBaMSN!k|YXmP!3r8!=<#SiE?#R%ugX zV^K+o+Qmz<>T^6!j8fp1L}<1`c*zG`Te%QrD64p0gdwUn* z?@ zkSkNsf9wNNxdK*7^D!AN#5pG8*mkkOI{h=Ay*uXwC>K$@U!odkTOU8pinui2U9n5{ zTwc8e-jA#t@KUL0H6qjy*HwL$^S3nmLxkqa~SfnH2icPbgYZ}H}1x?4bYS0Xs21$N=dQePo$0NDOr|-!#?l)t}GS<=xqbQTqafp*@ z@MQU;YYnFE39PNJXN}tvwiIceB#%ko82>;S8p++k2)I1N%vMRfHC^k7ugVR$xd z&pOQO)nV6iY zVwCG4iIV9{yX<*j{D3e8Wo1{vmlHuQ;@h@u6Ni-Tu7W4X60#kC@mR!Mx9~j&ax6bu ztB2||0NIJSufv^p?AwOR1h}(bN}X|V@CgqOZ%N3-4J2H79i51v8!p-!8Ve~qUh70& zyQUXzDz@dX&-U{lpRG-FP`RCNF9`Km{_pucMH;}5arjoW09)W%DJd>EycB?R-5Qsy zfEf_E-X!81*~39p?RfdA{udr8@Y%bJ>$h6oeF-a<@RIl^?BICez#xEzxt-l*Qqv$a zu#lkjQ0Qs*|7f(ubRVA3`@)+`6(_zj_BUs9BlXocKVOODYa;yQC{BL&?%2RLqA4FB z9}57JQ&34s0Oh)jYd;T$9q?Eg$>y44cx_K?eW_NMXS}MxP;=Ro%}IPruhK@t@o7nA zOZ8=cwu@MGXcxJ%AF`tiB0lM0fTej&xrVNiTm;~FN%k|>oo9cnCk*Fj&xDC27#hqn zM3r8-a>WOyU0iaqcK=d2;eU39%d@Y$!gbIOO#nzp9GE0DPoNSWf2-Gpdc^^SD8_q6 z{kpOs`;ssiY#nc)0(PL~jvs|t^-U?;RY>9xc0D`rqvP5ELDWBqvCW_wiQs>~r6gj-?(E*3JZMO`(h zrC9EDb#+Ihszl){{1veotEy-#{HHeNnH5mYG) zjgt&up0^aWmC#KDPF6r6PCW7hEvGl*qi1fmHRh7lL5Sl+~gSN`dQ!V5GI zkFdK6Zk07E+Fj=6F|hm;WAR;s-f9sc|3VAB!O42y46M?L)*aK5A}oyG-kxsZi84HU z9H)Z~({u!@N;y)U7+ifD8d*gP_+Bkk`S{7UVWE^=QR#9n!OeTrRF|iFKIR$px5yi6 zS=k~zIjb_cY~Ie*Zv#K|xrp4@=aFlwr9Kr`{{2B7tC0a#L-UvKr;H8bzJc52t*oixubw^G--miErk7tMkjiLr?Wx zR;$NVJ-HP0xaQ=*sK;Z=nPnCtGfuCc3I1@@x>WLjJ#cqTnqpGZfQ>xAVZBS8<7(S< zV~_0S{be5L8+ACVKA%1&`#$QAt}{u8gjNNs){e;IC-Q~`L~C!Vsp9{ksNF&&zyN*n>;zU9`%6U2h-W{IqVLd?o8ZpwIc^FFwEh=5R$fVg0cC z7w6SRzR4LJ)Ues$ Date: Mon, 6 Aug 2018 07:19:53 -0700 Subject: [PATCH 21/22] Fix test image --- .../test_axes/secondary_xy.png | Bin 53025 -> 53060 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png b/lib/matplotlib/tests/baseline_images/test_axes/secondary_xy.png index 4f4610d17f74247d24e210c28eddd0babf145d00..710f35115dcca806ded70ab735e4b6db460736c1 100644 GIT binary patch literal 53060 zcmb5WbwF0z);;`)N;eYHpaPPDA`ht|p@5Xq-O?c4DBU6oC?z7@Af1vbQqmwTA&qo@ zb9?T6?;F2=KF;wRglF%)_S$RBIp!E+?hr+JX#!koToejL@bH0zG75Em5rsOpjg1LE zDPY6mg8yA`5P$dt8@@cTjf3IuICc*-98f4C1LV)StUHw ztPLFPtZW^v%nccxO`bWJ+uAVlaU=iO**ZA#qWJ|m%nVHg`2=qo3z~8=K6P}o6XN0d z&q3U_&&+s~b0nEjC`QyniF;366IaIFw4cnKVr_Ke-#A~w6Iv3C{ecaaB{W?}wo^+} zbBTZG`$gH4Hxt#r%e>#1=qzczja4mpYiTj0m(I1OdHE@BC5pT9#)}siT}6c7zi%C# ze>=uD)|j{9)}Xu9{youQU#$D>&2CS0f&@0xU5S6bRIimepZnMMExz*qd}mBX)e{o@ z_shyR?b=y9F-F7jAH0&15@K@lfcW@$_XVZg0kr4-jX!_>5Yy6zS5~5Fg`M;6b+ECs zH&r<-thtE_2~o?-%d_N{YRbsqCMG8KjgrantMTQSaX~;yrl? zBjt)MA}-V93dFz6?CIsz+W7o@Kv-Cq>t-wEaw4mspoYJ{KQ<8&QE#c4!u0Iy74iG` zX==mkJoa!=O#=f1Q_)FD)<1-Dt;fnAp@@@&U2J-`BTTF!^gK5M>>M30=~O$ChgwuZW?_-~S?ku9=QN6EbhH&fsY^^6B5~p3#gMh}8aym4tj+B{1-u9;qt7`ZU)0-d zmNOMnDCmTpFQG1=Hve?+XccKPGc#L+a8mS8^5}9NA0M~uEDr?`i__lm)<&bz!7!*n zbe&8(>AidR4FCN4DxR;3$K>yNcCr@`6{X-8SM0K`J3Td}KPZ$tUhO1%0TVN{ox$hl z%DM4jYTKWcOi$R~oVv-&N5TRo=Be9$rLp{pvTb)E<57ZyscFXPL1DuwJ3NDyZ*Mrb zlzB+_Ba6ol-*BiQf0umrtRy%z)bK7V46(FySa2~xz_!gFhpeaES_^g8%8C;cpUO9a zMqm(q5=cO!sI48YAa-zp?=3#6$Z1(s?%l2I&mBb)Ndxm;NnsCTSeS%_MvcqjbEJHH zd@5^dmQyM$hX{*{i@$vP#^;g0+c(}~wYvX9gMPe4Yc>ONrRq@A%rJ71gBz!k-_NL2HI=p@$!J;2(odmn# z%TG?yp0LnRMJ1(kwzjr@Lv?j^(J3ijd$a->@^54U7E*lZQ+!SZwY9Y^65C!=KQ`i5 zP*A9>s+taAO>TT~5pUyHjKaIpCpA5I|Xe~#!tg#J6 zRcOr<%^e>UlmF*saqJTh{wlAd+pl6`NT{fSKYU>J-1v+&Ty9M>SfGO^E#2-O*Ne>k z@{q!r=jri5a7f7KG24cSfn2qOk6mtC^9ECm)bRm@+#k>0j@4V8GuhrYG775(fN zHauoL7Z;cB$)aOcWutx>+pMVC%{H2S*u?gWJxqRm*uh`QZ#iip%ck~Ha6VHCnuJPnXv1y2?!+h_1}A6epW?mQAVv+sB7KBw>Frg!Xa|Fm{vyL z_0%fq;NU>L>e;nVnq0{8g0Nw@K2i6jqoes}l|yuNbnpA>@ZR|PJPKlB-{4?uxfC%v zp89=evBSkHF+*my?|!7<;NYlNJ6`ub{LSjPJ~5tXLaIc@Z}QLRE`?oi43T&-Jgk0| zg@w8{IX*sE%uZ_i(A3hB#Vett#Kap$ zZTOYd)gNkWY3JtVdY;}Q7Jpw`tF2daoj-%ek~!w9qNb+B(w`N12pDNmVof#c^R zE@>|76Z70L!SB92nM^{Wt*J?NN2~2{(LJ=VaF|hhBo0%Kjmot*GLkYfp~T|B0u!#y zQ{{TFz#wL+T)lb~J4v3^X1w|u>>##}%H|LotjBKKFZEwfRm}~62+|SDMFznsPrbOw z*lA+5Y)JR!k@aABzWvsGC-b#ylCR_9!b(c`Hk*m=V8bHjh1)0{H0KFl?d}#kl6#P@ zTD0KXKDkNhJ^*Y&18LkO1(~v^KiYFaI)8~M(xpB4SeMO z+35+d>&6r&&umZXeJWvRPKkk&iRrbqNS~8sJ=fENg-5EH{8U=A3_i!tzs9gG@2zzh z^C1s8jFP8ZmCP!%_@oMA(4u3tN7p0+zr`ikT?W0QAz9Pm&{qxZg6sF@ppJb-dzj;n4 zU)5;3DL^rIKvgG@>zLqiQJm1~xuaKECR2a+{GjR03XR9vPd2u<{gt!*gMxwvVh!+o znlG)S%X>7|U$wYgnGa`xp#zesbzn`wm zZ{a2>DfvkEe_r&)PC2*#U3+_87~<5_RL`$D4L|wJ`||RHKgY+Jq95ex$d_LAzYBr$ zXjpEzVZQa0|ITi)+iyo9?FR4hj5dqhWWQuc`2{*Pn#pc}5oTZU1 zCRgu{oFnM5yQ`~R;_aI-&SIa=uoFw~6{D>z`dsVZ+vko>2L1(+`osU9kErp#z|#5u zfF;zwAAB@{{@;Z9|MR*3x0BK?C)`a{WE2pfoSvTE`16O)weZ6SjHA7EX#)eMnVA`4 z3JTM5aX@KVrnI?Vb+MT?I(QlwIXLhDH9xs9o=Q`SkP@8yC0a?i-0+4lXVN zb8~aMXV1h7b?flU-^#{ur@nvRsPE3h!vjlbsOD#ybkxj`bXi!McOeSO=~mS7F1x(V zYHf?4kxaO4eF1gu?#j=qzNwR+Keb6NU%ozCVar4Bxp9vv=4*>tikIrxjK#+%>=#j~ zPuL^gMZx17)G!Rcxu@P~KYFaG%#b@^!E5}5czSu61Cx-J4YFQK7*DBW zp7()C+L@{BlAfjh*}D{8ub;VUJOF+eg6JH$u>19wsz*;*6e{ghZ*&K99wt#A<{u zQN)eZtSj;J>B*7(uP-E=diAsr26FA)VW}Q(rJPATd&c+S!v{`nuIQ7uLQd>}kzYa# zf|xZ^w>Qyze6STA8=H}0+7^Bpw%y>Ja@$GW1-_wttssD#Q@_5(3|H7PMA8Wd0%D#? zu&w8Haj=T5YUr>aSUfq}Yp=AQwGg>fP*OrF?7Y&rGEzD{GlPMON82;k*405WC=V=; zfXDaF*NA^4rZYjnsL3Da=~y`zit+Ysiu0KGjqoTf6UDAxy|B5t2`lBfpP%0~*=r@T zrKX)PqN1Ws55B*>$>iVoyDyWb?mKdV{wyWP)fZ6`Qc{M)MFwRy6&( zi&;v`t#!XIbKkMuhqSfu3CySBQT?^DPE$ibpa^Gq88*ZkkO(qG(hG5*h-{cF{yy`zsJrPD?= zYd*4AeupLa?t6Mx7B)FKxzw5SEs8tM{o3Ov8nGR7FFWSSkV!-Ck#r?-fJo_iGK-e8 zl2Y_NhM~cR*nMuxetL*K7UeZ(v&{ZI4Qes|{uf-`-Io~Pv=IkekO){Dj%Cgz>$u>j zlWU3$ejTE0UtCmVe{&clp8E8A3ZOZ@ z5HdBqjg`?HyNNqLiE@)e0!klmp@+);87rlLfWRk?(@$WbS#0@q^bc^)w zMf929q7J`&6BAPA{=q`Z^lyzL-FbuNz|SKiRbBT>Pfkuc>&FMp_Oh4bkDPEFG4{0z z8Lx(~5N1Cy{LuhgW$^uJS&NHC8isoiA>Gu%LQqSK#EB(5_pe{SI!#b2aK|;n*VH}8 z5d^FS6o!9BXW6oQW}e@eG$rqP-WD-HoB37yDk+Ykkr9Dd6zXMs{L>#E>{~bGItRv6 zmfsOJ3|f>?K~2(?EE)zIEX`@TItshSX_?|%N5}NilIHR)D9>&mIcvo+DCo6^X~m=t z;DoP{`|nBb+;Br<`1l~ioGa6efTd|{Y&^9z zFz>)C&HVVCh#TMT%7`C1hw9zzCrcG6O-3rh zuSNfIiuYmmshg*#2;|=YKP(~)l=J#T1bkx%@ygV;TfwYgHh@cI+Vhv!b5p#9HohYl z4Qqi1XPfN|I|7f-w$cvUmP|}c41axzZuSq?fQxAfGtaNuVbj6@!+PBl%ZK>aZNQ} zGu_9wC78sp(f8cp0)PU3@A}!98S3LbP0ce+>Z76K@ZUyQSV7bkZ?DP51|J`L0%^d5 z!VZyOMg;@#RJeNW+GWlwoSbq<4Kr0%G2t%z#h4E{|F36N`-^E&a-(hUjveQ#v9*e{ z{J&3LgKa}Wo!`ajBb};x8*x{yeIh7G1mai0_6x>TuWbhPB7ts7l?Q}oJedGt#&~Wi+>}Zd6S&LX# zcegZvDc~p|X_*c-43HiTy#gF%wj>4wgw(xz=P)oZrk0n-rVap369kAqfBBNQv~XF} z%vOIeBc^BZf>g_esWe_*yBmGUTd=VK7E$2_U;v>Y)oEFlT1W z`4S!vozir9xNNjln{`rlSrfEdlw7N?ua~-bo|!KR&=Qag)S@YIag?>Swciz^Q@zZq z;_Ir|6N#b-8-skpZfl`!*&V#%GWGWXoNr&g1{aWLRA6UT$*ff{7s6Bi;F+M`R9+gt81m?^3@I@M{c5SYxHy6e8T%R z6|L7So?N3sKINVd+L5 zP1zTN9DIW+$w-uLzV_A$z{w@EulYpU`&oD2LX*MuMj{!B#=r;3Fhxa0-~Rq5iGmK8 zK|~B~v0Pdks}++=nQwWGFhj$_c8CJE=9j-5Bm|!=5j}b8xNj-oKwUgXVjL`3dwY9qJiJn=YzgNpSFg5Axj{8H zy}BBam&eJWA~E=9IY8^s?(~5goOn$va7*~_?2MpOwEQ=7NtG0ppRYYn9=^d(n9Pm8 zU|f4h*xCtY`F$c$|5vX_fqB#a_WC+fc_;gvx>?4_8{4zL`8HLh%^jr8UFhT{|G z1QLd*BQSxz8ES9=@&KPs=#sa$Hd$k`*t4qFrd91PR+k&0Gc$6R z6@PyFCbKeJd>)EdUck916d9|03rAt?3xH_T3}^d6{4y*h)B)n>fTBFvs@_d@Sso;S z9UBl3F#O4!Oe&H#1fni8H#Z?5$J_C{2p_aD-OTHRTXP(_;=nFnv;&uxFt`~_ck>NTe?WImpSh;A%8n`uz6l>AI-Ih|# zs@CHnR23s4y@#1J>>yEFNp){Szi7olX* zmCqE}E}7=0d>KTLy;_0xb0Aifvs+3`I_#zz&*3wO(Xzk6wH(a5r>jfvC`8AtUFjP_ z#@YnD-2FiOV&~ue584YaO^rCUD{7{;`XwC~JtJ*)^Fp@Imo@Erl^6E8q|s_0rOeK9 zL>FLK7G-py%j@ezs4tzJ{PAJ}0%~Wv*1w(|hz6Y}E4!psZgmgB4U|_?b8`WJ8>WG{ zY5Djt=6&zejZw?O&y$l3k&%%|y#{QC_vwMz{{FstjiZJC|3YgfXj_!-Rruh_w>MhX zs4DnKs@VC3%Iupn_R;&*1K#ylqO+ecgpQ@&$J#$QVB+N^IXT=pujpjs2IUNt3}$oe zAyz{`B!>co0BFo-jMxGnzD=_}vJ2GS}$!B_Acel8vWq|PHfyLp!H^hx)6fb-glemFAqgRB+NymI3KhHGCu^6&*dj-Q-%cM;{`vng!pcR8+Xsd9<9z zz#t|j)=v!$4z}vcwyfZ_e?%JkjeoK4GuARy7fr+hvI#oB)$3#sEu?xE4DIe-Gj4nrB0X^xEW2 zWnmpWX!Ms0<`;$yUe^FRGAt`PFJ^)NsNv)w7;9ByEK<689cnbid#>lWT6j;b?#s z;QxD2PVEQb>0?tI43VT_Cn>!PM@S+k841O6X>#*^c<}>*D!E-aL6fG9PBF zWoTg31PI4oy0-#hyf!M#HsrY^U+rvo5pvR4>7dmD(3t;LRqNAP?)JgUdue_Qe+QRe z`R|AtO|3IpZPd4ocCFz{)WY~XI60UVWJ22_m_OOa8NpJ(Hu(p|#o4~m|CuB4i#)q` zq=7vVCPoYY?;#S?DNSycE`qn)_axOV1rUHGA>pE31@V!CEIL>DO4B4k!p!W{GE0TC z1{W@J%-iwRA31i(-Z~fW2nd$9v@_Kb{A+CQ^vV{F19NDTMk@^*JoWZ@6SZjn9--od zR;Vd>k&>_?k9L6*{at3N-mqciTAWM-n=W_dhayY)yOf4z$V1D>X{$)w`T{b|8*po66WGP|XUu_}f?Mz1DzsWvUhqoefyx#=NX zSzc!o<`Ubu)U*_i@|HbG8W%ImxPcFU*X7sjZycN7-(5|t&hU5PU`5WjQ2x)gn2(&_ ziX{deRv99@Y4)?7jrHH>qO3A~Or^Z;zxAl^=!2{l(Z6Qv*H7-|;WrJlng*AJ_RE`M z_v<&z&=ok5k96*8Xk7L_J8}70X@4HXH%JdRBz5+_eEkY>DDtx<@LZrp6gt%3H0xr# zcI_erZK@mkD3IK~E9>1)0@Mkb#$N5(@^V;DPmhN#&;OEjO|y%+0jgA0Re``76c-ow z*#lUW;Rc^O5aUQFDT9iNiZHph7JJhw0er*u>B&@JaCq$%H9DHw_uKBzuZP;Tt|Y(* z2Zx3Efx?;80k!shRn^$fmPj%S3E`Dp^-cVr)P=QfsFO0(05?o}hraQkMMR zUVwrE4Bz;hX7Ukz>*FwMwY+3u` z4GX2~Gc!gzaHxXIEsWvUfNar`IyynE;BMOvH0oyk8MN!0XspTqghhrQAY+7ZV?34r-hMk(|LuN?Xod(?B)NO;_^SuW!H_$@;{{t z8ZjhL9u3tZ6oVYYkU*r*_JQVR+}>W;%Yzz;v%TH0knXytF%1q}Un$PJr*+@s0ZFen zO&WO6!aM!$pbrO!hqr(sfVxIKW~A2bru)uPKoEmB6>4gB)-U^Urr~T`glfU}p3ySP z-h3^A?X~f-(e(aopR-dc0UPazpgFjGLqN9iq8_*ZT^{*YR0xz?kHNG6X%?=OCiRujq zSD|!ZgxIMpy*RHd;#NR@)Bq$!A*TqhIa0xT9^BS}7kn4oYV;vE$KUjN^$6m@Z)&<8 z4t93Hkc=^3@pa?A8l&C*7)NYn4Ojrr<`=}vty>#B-5fOgJ2R#2)iv4os=1a)yZzaZ z_Y!GH$jJPFK)*yuNwee9nVy%2uT^2w3cp)Ul+WxlfY?pNYj^>P9KYt~G_slkzkdHN z@$g~DJs;Vtq!QB7SNQqM1`jN>q@6mA%%PBl4ZR5TZ&II$nb~Z1W^xn6-%VKVX`epz zi<1wo{a>P8uue2D3IOj^#iVzxOTxLl8hK@9Fi1iDRJ^=8+r65#>gC4aQoC1!3pGvC z_y`G2Mr*3aO9OO8z-n4X`K@;AUp8%#1CQu3p}G-SJGCj`z*5PjKN|ISY^qcVsdUV} z@LZRBm~@iz-bDV&p^J^f;G*x^@82OC8+M>0Jw6)+P2X;JMdR1P!Vp?PTpUA0RCFf_ zia*S=Iw<&z7JE`%!Mgyv)E|nUR9HS)d3kyEhv1>{*ez1I({R*uckoZj*)djndV2gV zbLITL%F0T8h^cV%ScZm%MMmv}5F#fWiZLy7bSL?DT&z=|wg5sX{9XHxOSl)2`K|r) zjpMK`kbiS`w~b6er$6zzSQ{K=Wpi~p(NrZA8#z60GY4WW1qxrk^#!0%gfa{1j{+lU!(6-{Pygs7 zz2GWjv<%Q{*W6^N!6Bh8gO&j(d~P5pI57#RuVyHuAm$3mg{~yYD0)$g#Cq@}01tVR zb{K!wmRRvBIhh+`vU-J$PQ?1&V(CkqK8C|?^XYXDAd}WPzm-+FdgyT~G zL%D?8opLGz`B_N@hFd6w(bKOxd~jT@Ld3Rbu{G4O|cw zArA$golKtbyN|?bvVx{_yi@2yjySL?Y}JzdtfR)HAFb|}f;%acTS-)+f>JdU2~`yW z-lVLBgWivP@09-9Ws*3t&Nhv-Zc**(yk(kqdk+1Gz=$IyWB z%OV3tdXCyYn;Q+eU4rXJgM1PA*F14>l;OQK0-$ds)^)T1WgM zx62|S&|;dIEeKhb4D2Yaf@!D?3}n0}-!7#sl(h);KD|YC(-a^4c^g~ZcbfZ3mu}d4 zjEPxTN|!;gj(@tW_~4X}tTi8Fip8Y`%Br4HujT?A{jaNO_2tJJ4#QFwM&H$~zuCVF z4whS3TQ{xJ)}3`6Fq#~oIoP1zq2Qj2OU;*ts%W=9|J~gB&-sjp=jc#P&40NlPVGu! zFm0h-HyG(XR(K!xtFq>+hS3PvG!6`uwe5NAtwn%d<2OV~Mh5AF2rWG1nQ}+=5LkVn z%}8Z-`Dp6v#2Q`oulnEf8fPab`*lLyaGTNFbSq*Fs+HlL1H zmz|1wj6$A-J?ka%XHoI>-rI2HJ^jjWv_;+)w%fSo`xlQm%e?xBVr%$=tUq!OQs+WY zdzRB)f0MG{{kZ?0onQn0Dg866q+Uv<`hw0~7n{<)*W>G8Un2!u-z8YXiNY@O_|&%^ zIIjJy>XO|#d&?Wt>IpuUnWceT@!WyX<)Okb^S%rOB}FGDa{3d`LWvVpB(U!=D<+Bp zc9itAa>i@j9s^`8_C7hJjx$(w;xlNvF#Y@Ybx^E~Hmb5ukHaCJH6& z_GT2HUnfn{8{=}O6*7+*llHPM?!Rrw5#)W_B(MC7iz^sir6O}i4WgSo(O`DUEg(Jl^(Syc2S zd2h|89RVGO!{g&R;Mh{Hr9;G6lwEh)p~x9{uXvyRdVm}aOIiXv6)h8W9t5#~n4P=5 zf+pRUxq$5RFDxts`g#VOV)GDE#iv)>c46L&LpC zP=lmAkdfh259G>=IviDA1!n`r+S(fC`7bFN9!X#`?H>+Y+A3W!IRdbho!K3F+s8u| z(YIcl)$4rms~%a|g@?jf7BT#dXec9mp!`SRe7Kht0>WWR!HBW)YyR5kqXP-#7!0#~ z%1E*2hsE8=2oC4MbPOTh`Kgvc543Wn^C~su=bi!`;k0>`e_NESdfaRP*0q3@H2hEt zvh^20Gq9YOMO%7R0kQwbX(e!|O(OdZYE^K$2sthFPc@tVNAs!?o#ef9hheBtF9^^g zh+@Ft<>`<@Kmd&F>+6f!KRmqcT0dVTh8ojm-L3X|t!A#+sN2%^D`$_7FUB&0`%CWr(EoaNhq}B z6KTvKZSx2%xu=fFFi_%2$CtZS=@6FWa#qjc4UesdE3&{9rL0C^0WAW@SOt{i!7(h; zj^)Y>T~~nh`^&@q$n9U+xRan@XsgNnD^eNJs!yo?em*d#mb5$XVNt3{%n>0ISM z2qRR31hpO)9c?>(q5Zf&^H$fj(jft}+m>VXDSPC@J&XVO)N+o{tUL^*6`8ZfgVwIq zXkeRYt1r50)`--Yg;i)cd_v6h#-h~ugbod-kI^Z{+6W`Wy!Gmf_KoG`(g*b!FEV8Z z3|8?MI@L;rT7Y??mQNd)ULR8~Uha273qIHwX*t@kqx{QLYAI=^N+$H;MSDKFNMt5$ zl}Hhkbr*!c?>Qblxlsjb*-uzBba$S~m5EsXXLj;N2=nEq%GSH5gGvRa+1^YwM@KKx zE&j;TtQ1?)O(yTY3c|av>W4x7!6CCL+Mfh#&#4Vnp!I+X%&bxz!55rDSxSWUco`mr^&7{WVnl7WEw<2hnYw997!kBgS?V~3AvJkPs zMsrvkxgrZ5b*Nf^A0mv9IuFXY%Y++NVeKDWls<@n`)f7pP?~LTA;xS(l`WHD`C$4X zPdqGJk6!x697g3=I=kuIA(EXzd!W@Q3CJKmdUYh1e)&tiT#^tun0CRXPlVF{`s$kN!CV|b;~PNwkfRWbu;=0L%t21@ z`@Su~<;uvv6WJnr(Jn@KrnGo`Bd(%btA?|L5s_uGF82JsaC*>r-P7R@H5iIK`68zi zv$#R4`Vr)SivO?bl{^;~cwVh?*l#_$_TKNF|DYEJ39M(Vrn1drd0@zO5K3x%ahBM& zO9n2Dk>O-2MDX}R1qC9?7YMaQJz;RW1Kz^DbLS2|y$Dqd%cGpVV4N~df-=o~ADeYb zwX4j1peZt+=HA&DYXvn=muccS^67I#zR9}W_xpnGxEry z+W!FQ-G z!Ps=7g)4>_`8pKQI{!h`cKaLCLbh=N@KDjh!br*8<;xw{0bE|E5;w`nWeCs<0?Ccj zTLRNFzkJKOccoGk1Gi!$fmP0WOCmlMVcTu^SefD~=~OdsF9dI?AOCS1fsgAwSd*RP zeU=Bgz?j_(Z7Jg9q|u^}EkDIqZC)izs=+a1y*?;5M&l)Xs0nN3uZOrZm>2`EK{$*c zM2#sL#Sk9Z_E^*pwoml<+ke@8z^;5h`-yn=6ATnogkYZZg`dpA1o@_NDQ`0f~1uf!D`QVrI0;QVJ+gQ8Dw-^9#UzEemQWK4(^$&pLwXD^+bfI z)7*Iz$@tr#HV1@mW6eg(D#8B5`v(I0X!9A_#&|Bl#+0)_?w)r z8Tm-j|0`<{`5%F{Qo{7pPhO%QA1NKr6_lq})m~Wel4@$N!R~y_a9naO(E=o(-`vP{b(Y6|?&6$jG;XVgLaj%vMDiBb zvfqlXx`>I%eBXTesO6P1hNz(p~^-_8ih)Q}P&A+MW@n>WT8s{RKS*r}j#ar6FA; z0H+lQ4M-m#k^okIprFtWG)}QWGbV7zK>bXo2GNT@e2611E`9?_K2DwLOWk+QYRhD8 zKf$#e%2{UHZwfFvyqpPpSa-vBI&rBntOe93CZ-D$XTNFd(a&&D8(lFT9_2Qh+=KTlm1+aSNn#E)&~u(tfJq5*!S9s4 z^+|?_i3vE9xQWR%qzPuE)T|kd^{5;6GZNtEGy)_z$X?ZZOOx)jo{PLjK_@82??TF> zS|TDB19ax$Cb0`#2zdV>k1nIu)Ev9h!3~>uM<1+Z3b+v0JDGGo*MGL9Ws+`M?|blh zf<*ru)+Bf)qTjv^0jjX2rDZWQ=E2qA$Ve#*i<}V)0W8S=apmbVNBxO8c4DcNn39J` zL?%O)q_K4DN{z1qk?sXl?SX`vocDV_Cp>264-`DW_W<2-C=Jgezc1tC$3~DG#Kgp0 zk5(%-cZT$?fN}JHc^XE}Lr!KGd%OvbQQCA;P6i(aOcxV$X5cL1*Ad>_tlv^EKf^#x z?X=ez$}}i)sDbiyf$b8mtE=l`%23%}Eii*%OJL*VY=Omc0}2RQ5m#bm^8xSs(DAd4 zbo`Xd=aRXac(9-Hq_Zdb2%U=I{~jD1HHl6tj#3aNQNpWmoV@5+3;X+Qz3745@?UYc z{4>H~D5>3b0ZX`|A~7iHk$V#!L?9Wd7aLwYKFQtXTbQ4xmP<)U$o1)tHFv@&TzSyy z^Qv^3@y%4D7!o!@b{NGb=#UcmRlzjY)sfp zMNfN~VGn`(v~4tfNG6kPf5AuIpgqLW?xjqTm6ZbiYL}gSM7slI8>{}%`;^7|3l1pX zgDB_E4(FTlv|;#4v(_Ue7kO$oFu)lDZV#wyHl~9Z41auli1Gz=I@1(@hiG8Axg20r z0V}t6cfhPwfIREQQdi&bZae>jYmMy+?fU9Ofeseb$Znz8KA4s}KFWk8{7p~TX#W1+ z7%=I5vZHp1f`VCCmr+*BO{nJ%4wXPgr1+B z?BbWx9@(N9NXVY9mSnx15`-Xn+jxg3ZljPPr|Cd8N@B_f)4AjsqRu{fg#BEgUf8em zp~lMxzC`U3HxpwX^1BgLYv#9s6ZJ)Htq8D|Q^213LOZ9VHSEi8@AP0z+w@nR&sXee|p zfQzwdzA(pj{oSP=9x&lRKj8h`fe%0uA&U$8wm<*^Zxzwwfd>-(EBQyOd=#rP=TWuj zEV6mR$f9SG{n~bUEX)={5GN1C2=q?8$w_gFTpe#o?Udx((bLd`ft3)tBJQX4LMO&l zlFP(ruy0|YkoL3xr_b#`GcFtY8-N|9Mcbhe5mH&1R4Rfx0CM~$L!If5kN1D-`J6aG zg^EZ-h+GQBUAu!nCY)O3-=FLNcJo+{(*3M8sTQzBi5WP%?pL*tC>LbJ58VgfKjR1Fxn^9I&A1Tg zMp�$t_@Tv0EADwHmJ`pY+^{NPjmlbL;whJ?O{*gWzE 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
    j65y@12%?r88eNCJr3AWomoJ>e=+X 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 literal 53025 zcmb5W1z1*Vw>A6#DhNt<3Mwic(gKPoC`e0ph=6oANK45^BqRi+Tj_2@x{(f*E=kFM zEHER+h-K&8tRGo10|>U)k$YfrQK7}&5^>5BZfvVs`YdRbfaPO zf#Sh$7WpsVWX7__oeZ*=7fE?ku`-r@5Tax=QcF)S8~mJ$!*=b>>y&HgDXTe6Upw7A zJg?bO`RRQi(e7z8pv4og?T+1Fu>I9(tewccAnA*bF8=q+P>Hma%`5Ql7kO{c-*2SR zF$4+DzalOdx*e^RP4oW1Nfh;@&5%iWT~fj`F)R@OYV zmA4%~zjk#!Qd5g`Ks3a%7+pja|K(JdL`y}iA;MAvnK;o)KZmNz8o(e0g`#6FmKr0~VS!67&L{d-WN zuq&@CP2JgP70PPl`t|D%Gwg?j+ztxgO5R|&eLGWayG&WiY@)(81AfprFrawh(j{yu zDXFb`47|YpeubK06E^j+q>Z|>+QKhih~SPXwhP^jjg4aB5)wFR3MS<#L76S;l?6Jr zZfY>ISgx+FX!olR=7Rm1-@}{=-tf7&Gdij|J2%I`rc=w}i$Ay6mx^3}e7I6@wVOS< z<57}`do%p-(UT`Z_+o*wq9;oH0s=g)G`nau+1XFO8glJEOjB6O?RI(o{CSG&{bBH7?Ij_S2-IeUp-soOXv)cGWW~UYdAXj21pXZn1pdW3iWB&)l4K zI8Vd4FfJ(bCydtE*f=;QhJ0&4Ry2tIp+Cn*A_9VlsVBZ1o#w+ZIDAUVH$9?fn!;^_qGMbVPGC%4B4p z6Uu9p80o+|9dW+wz1G~?x?J&;fq`N9>lK>_{-~#Z+{WG4?%q{0aC!Oi_CJx4RGPnf zlOK``+TH%=pMN;q5A8Y=gvi?4+ga5mYMi%!(Q|PTa&mIszH_I~BmuSAM&~(GwNcxY z{XD05I2k7|Z>yl=?9W0HzuN&vYdBp&0aw>!jU%GWs^JGOUNE}sEGCB&21#Pt6&1j| z5G;LofDUs{^20{pp0F@ax~q#zN?~DP1Dt=`h=>SpGHoZclfzvfI3L8=7sIFp`d95o zM@Myc7W)5Y9>Wmjqq`OHNfxD=$a|>pszwB4Se%^J* z#AocjFLz9rnWC`yEGFvxaL9U7CGeD#l&*)_%ukw;h>G_W{`>du=X~%) zdessfT{dd=cRW6nydOU3;+qbH4Hu|UW)UmDTW zno}w$C~&+Qub&}T!x?_{ z?(o?&sUzFAFdDfmC8v|!5uPcj>k?tj4L+DxwM7YdWTp1){lvUQ+z&I)WS%~4EHxiT zB23M(FOuKB>h3=%P*G5LU0r?8Zf%T2Mn>jYcY)k(tF5_5PEJlA6*FX5wI$%7=WEws z3keB9D&RfxfH*rquC9N+z?PD6v z7GGBkn&6H`i_>82+atfu8(Gn%{^-#~O)V{XZEajKGP2aXCSvV}WSwa;w)M3)mdVSJ z?H*AS+ez>r!$}DXK|{_aG6fe@DYjc+C$VW)W8J!SOX>M@G}Q0kzvblR`?7bRD=U{e z)1+@wR_e0Z$6@TWy{ba6#Mx3 z^FQvnw^nsd^v{;*j#TTEm`HwaZ$C55^4itI+ zeE5KqrDVFw1w-5U_O8`5mG;5@_BMvx2t9myD~n%XpkxLUGjktYy{l>*w%qnBKAu{e zUse_{#kHuX-`iXoPlwp|3Qmi{cbVDQ@!XI0{30VGi=^baP4AZ#W*%LxuB#KBwh|W~ z%pXi*)DazyO+6gAqot|IFS9}c;~cF!Y&jhg-7E4K3?EA0*+$zw8NUB_JqE+h(;|X~ zVo{g=wX{U_UNZmhE9>>|1^B;xXl`6wa%Cl@pP%36)|R)Ak58+u7#4)b~YD z8}P&ur6~kGiDaa|dzUzvjQz2d6~~mv(c0sMp3ctZn*Avp*tQogUex>Z`zOGpuUA58 zg(*EeJe>Bv8*^Gr@JpsCyaM=gaxBGO9%NG(| zIb8Q%VbSk=Cyq?t==uB@qcSIl8Ofax5$+xwe>Uonlaur6?3DhLp3VTdu&=ML+-28v zTm3Q2o_S+YTxUC(j+-}~=P^N6R+i+GCk@FD12(5zH)a8hUG26sGV-&YZ`Ff%OU`8& z=$&(uOLTU2R#H}0;%^!#w3-z|PzMDC#j^8@uDFY_(&-)uyjc<4U;XX_pbt_R9l!hG zU4SU~badfs<7F#V0*q4ANk*ZUb|h$>FR<4hFf=wbZMev!DbNGD+Fs}hsHze|U1MUx z*4EahaWR0+M?u(T%5_Vl^i{*VJBqz|8hj|gruV!sX`N6X_$)1~P9Yw#sC~W=F1$yM zPF1k{AX)6#CjFAU7t#{$_^Z|A)owH-q)nJ8W#1P);r`rqcU6JA*D`@tSM_nrtd(S`eDz zq9I!LXJHs`Z~g8zy!{-#Mo_@Ouui6xEvrkNTiG_A(LF0W`?V&U<>ra1s%qw!ky+Q6 zu^gf4j?&W7!jckmJC6#@Fa)Q;-Vm3R#6s0iPt#dgSU~M}|MBBXj56_ocNEiK3R7xp zYEH~}XecP4M?JlWgVQugysA^D*%)wEfx@}mJJNHpp|)F4!_mkn?J*tSaGn1FiDZ+2Tcs=E5b8?z;16*!KTZ6gPi& zNkghmsFZlRX;Aw~nnLKlgS51C>NXQGwsZ^!T2xe2V0gHS?Ac;!_+|^4&J3jRW}1rinsT^QJ`0GzrK#E_>J>k@arCN)9V{ z-Z_2!8a;IB;>C*)QcUbBpjt?&uGT#H+TL!`)dAUJsabX*-{5rb#%(2ZS-icrfF4}m zG(WvMyG1FpR@1P7PwWtm3(LwZw;bEv&mmI_VK{Vu4=@~6ylj?|{hM3R`%c7u`@I>qNsLYu;nmly|Ox9hL_}dTH)e`M5ZTC zE@$WD5Rj2&)QUWDaNuRQetmXvF|aRHB4>otab45xa4BQCE;TjP8j3<{ek&Ae^Jt^a z0AgH>uxshm_?Ovz>FP6sNxyY%_q*imqA5vMQwlC;EJUIFRx@5uLC;62XcLHG$tx;e zPjuf?L+VQ41bj0xGDeXMpJOKH>|BAm_N}8sqnMh2K;Dc-ri$(L^nv^Z)~Q?C6An)Y znclfL(>x5ZKBUbNkXg}@Seb{C1sg)r?9$Sm)wQ)20P*=A7RLu$`lE$<%XRQKRBoR8 zD%%zf+CQO;0%NL#Yv zj~&R_{_1P%H20(4Id_JaTOwP1vSd4Zf&u~p=w8UF z=(9OIW{JeIZeyR&xGgG5*V590g^N4vasV^`~ogDjjziTZY4jW1z^xw`=#9yLH#pI!XuS4 zyU8|ZXJ;r>y`g>|p3dM?l7Mylq;LnObxbpW*V@x?cCBUs4aF`zfq_G}l2sPm|MS-` zF{UVnTemRL(9l@bm35Mg@+!N=bpHJL)6ml+OICspN4l#o*e_<=NQ<9tsE$Zv$bg@Z zhdQVI9RS_>rlzJ*BYb(<{e8TYn2Lpis7St6?J&dtBeG z@@0=5#Kgtd#)?81?D^^XM##(<{W#(w*2KOw5Bl!3a-@W+kH@Uk+nT50j>iz>Zvlk* zRq4b!3rk)@=6=51Y@bMNU;;Hd6|=uB1A{^Pw{HqMHI)E!C`Ie;u&{{R+n2N+Ly@_; zyUX)3)KFC@OsD1Z#&VHeWy9+<-{B7ynMEqvs1HZ+*x{@8?&>PL!?q=^nLX$wf;zUqjX2O$Bme!Ci``Kd% zmRlQH^o1p#?>eX%`WGj>!9o2G5Xk#DR{TY(<#a8jXE==zDS*9lyQP0z;{N9ND_WEk zqkI`QMF->D_~=fjwQQ=VhC2S+<47@*ImvIGc7c0N=Du=HGlp=@R?8s$m4!hj07#?7 zCPaev%NGE?rKB(*G?r?O3y)W&D|$FUw1w^jAAr+rT9v*4_w>e!O&;gRf87Kue)qH# z;&WbJ-mnp{B)}HQDJuHNr#{ZgxW&k*Ki3=tOkX2Zmm$W9&X3u7BDYp$zO}4Of4Yx# zjf(+`xs@aSjHGA(Izx)X?2jnIdv#0v7E=oPPK9K{k8r&cc$x%CWE3;z*fdJ7Koa}f z*(q*n%A{{#KmfrL&@W|$4MS7p(=;o4`{$zi`uda!`<=1ez}NZI)CfZe^@Z{}*|;YW zwY=U(LrWWWdUBNd`7t#3;J6p23RbC}bg^&T+~abA5ds<%I-))gtuqGRrm~D?;SC zBKm&(_)*`|^5~?jGQ3JPVshK*lc3xVqZ6f_Zw+mV z7qErXn_PC-&H(_a$xLU&Z@Ycg+sW~jr~mdPqld4pIv-CmcN42=n43!>=q)KKDS5OpRfC2C zaLMod`vx+RWMqyb)dsC0P;g)XTqW=An!vj=QaF<{Aa_|;A%>W_Z8yO0Vw3Yjr#C`y zhVupljAClH__KdtPmFgFQKet?tGU`Fu=Lmz7o4{wU8lPGCn#~KB`p?thjU{A-9s;2;q-`!NH1Wn5zFRVNzAt znC8*-{F;wSu$SmQ6S;xn?BR&#^Fu1R&hFR=F6qgbz;Bcn8!KIO2eu#1>Ne?^M_3Fc zy`dr?@CK&9V5Z(1$PmAR0(R8Lk00+FARz4E;NWgW_u+uIhK)LhyeqPS5H?|Sv>pn! zj`yrhm~?JluuUE&%jqulr@^T~Socc1C4{q}zUL%Y(-Yw=V8u(vz9#9faFYW@MHf;R zFk4M=VD1m&dTOdIkZzHQv+%I0+qxu@2ZnvfP~{079X!!q!+-eK_IIkLBez$H#6E%aT8Rx&W0K41~+3Wl$Ycu^^}E z54X?BmS@ithLr#Ii&=za;G)WM{u%R`o`8f0+xJq1!FC7-{}}AcbrT^H71)bV&cHE! z4D;)YuUcYCV#r0o_$cw>VB=s8D~@DD(pT#+mUaa_67{!j7rD}e}t9C>RIXY-x3ZA4{xh>-qtlS zx$Gx~CMzqun^le~)NjEyG&H=%&rco}7FJkX+}xcYkG_Q`&9&dRh6uQjm`fu~y=bAPsDi$9vl?aqT{OT~<>sRv*AU?nGl}bC|d@yvepm z)-fh)mhfvD@`kJk8J)W0wnD&m+u@;p*C%{`TN*ufLbyk{rmEkQ6(TvMn-V&rWUoGu z;PPQo8ALyV%Dn8qVE~bXkstV@s+`Kp*slKs21<*)&?8~Yle({$M(s}C4fspCJ1P2% zteb9W&BybZ9{&i>9wU^@TFUY~jCr#6AEZ&&2niUB=USBuah;=&H@=7#!Z+6q}Gmirz1=)=UG2e_KP$BCMz;x9!hsRLk zj$gq+I6#V)p}7ypZT|e^S)85sUF##*gdGg`r?^${ZsyOkHtTI=Eq)zQ)_1d-J?Bwy z#^$Moe*fVGP3D3Jj+PRy!f^x2;DEz|W${O8y{y7N29g=KpGx=Em!5tp=jv{yVKMWvIVu7uqMH;VIB zdHhs{f`N}QMIrybstVpKm&DnLUgRE<8^1eN!s2yUD}t`cVy?!}c+2EpS%BF}pOdzr zNVkv{Hws19)4Esq=TEX#VG);u8VxQoV@pH3zbTPBtT7u#A4}S zwT<+o989x=b=xnBhqEs`tSqh|NbM6JACoEUOZ0r=Y2U#nTA>#bAHPujoixcV=C*fN zgEBMabtT^l@%CT=KxRNDu0Qzo8(2`aM?+XvWo^i`ai4FTknIsBj=w<%>$2U$iI8Ln zI($s$msGs|2A9M>8rpQs!C63C$8BW`hM`$VbrjUUyL$O{m0Iuv1-@6Y6qk&J%nYK3 zW;u~OE<>XHjnmnkfsIT#^-B(oLwA_hpwHh^^GOG}@b5u~3q#>fB<3QX53-czK&$-P z)6@3xfzQm!N=|U)x&lB7AZkKQl$Di%&*-hx^zaY?Qoc7!@zxc}yOQiBCE`$~f=sXx zx4yWDWofxlmEQ5w=EuUqG>amjTdAu3?MtgNjYfLYYFEI4^L0aP&vBPP<3k~e^m?024YG|i;} zGBYzje>i4jYN}T4D1);Uv)4%@U$dfr<`_sAD_dK$O#y>fk)U>51L_E#1F8`eBqW|h zzUY(GEQh-#p2W}FVXn~GNR%3ShH$AerZ9{iq2((bThcyUqZ=F34?B|7AUDlrWMG&# z?dCr<`+f14`-%5A4fOpB<@zg~9OZWjyF>p7mo*WZzm1Bzc?pk}Wnyv?!9jkZp`nnd z&G&voO^5Dvo1NX#W{QMa84E=j{_*3rybcf}k@~c+&eIc-acNywIiVJh(@GMt+n74Z zcR=BTzHVZ5I_-H@)9{Czi|dN|e%Z7K^WokaGYbpL1=QB0y=H7{}ip*OG_zp zb90{!Pt;tbtcZzSg8vj_>f=yC!XR;=+_3TR@Mc$6S%wRA3OuGX_wdZuUUr|#E2&+l zJvvY!D0WK1@;rN6uApS}4pyTSOE1R#(@leTkrJa^-YDK;<5dHM_9ugCR=vNii=r*B z2K~`Y$2d}4i&2s=*G{_LUl9!D38Q)nx)SP|jg3u+)*JN?GS=Z?Vg3L(;RkQuzdy&t zKcjV8W4VpTK94=VN+&|i6)SyG4m@k>c6wUcEb^D2Vq5R6s%ci*U0zyRaymQN-&N4C z1x-Oa^=eR15XuYK9aF7*5Y7!MfN`Ws(c}S2uJZ#QotxB2a`X3M8(F&ND6?tmQGji7%u&VMCV@=i0}br z_H8DnMuTu+|I0*-_ZcHiv@AjNzFXRpAo0sCFKZ1A_2orZf2Ij+F7*+UkTtZImcc;j zJ*EQOQ))4Z_zR9tHatfvwI5nUZf;tA%E(9scK^|%N2=vk^uM|jfCpniSvd}$`3 zcqCyRMGa_VZZ4GRpmJ1yVHN9wvmiRh@oAru#&T?jNWZc_Z4TgM{y54uIuBE>!!8}n zpcC%i)!IVwUw7k1Umpb3%~EO zYE>#-z{E6@)~K*aLzsa*!7Iq5lTKDSTJB&;pAhVTeu$wm%n>W?Fd`46HNsLfgRng` zpu`llI8pH>Feu2xP76E?#pK*CLRyOfXubiaTYTSdIyu?l?u|Qlo}lpW+_@9l{l(+N zrLV@-Q7d|sCA7q#(f4v_VpJcwfdw9YT-~$BA>{Or!)f#EX$>rh=zYtbx6@JAY7dv0 zzf6cOpOnyC&6*bVkd-F9E-JwEnskHcp~@)>7f#OLHo2?DVAU5^|4|9)*{=#3ZKjGZ zAG)@Q41-AIQRKN>!0LWHQp7(0IF0=GR1ZS^a4ru_i<^{8{HR-?6sr<^%@*1IQ6Xz) zaWRu~5;R>fzL;830DJuA4R)x66i{z^%Y#|KmhtjhOknwm^(|IInT2)r>QD?RxF!Im z6O)pbF52(@gq=ECYEJc{JN|Nzc#8ehf3qcU%K3Fg{$J<<6x=U#e|5&HetB`#)YSBf;zv^0>@ob-48MAk!eM#%1nLA{ zg##-~Eog@&FE9VB+pN?4pZD)Cp+L+CP}`vMG?y^w(GaTC`$_jxvQGQ5rLhK*ee<>B z=3n1grMp^^CcJ*#(R4|5a@aZ!Xs!9~o);CGQ$M(N4U%eP$)vTDyi%A14i74H5ilh% z8ne4Uv6nl9HaR&-^WedQ(K^pMmoadLAS(S!R9H}ucTP^uA$`(*3#vA2zda^&AnwxuNAVb#Dj+TmwDg^k31VQ`Pz&F4| z2WkTi;WEo&a2r$tuS-W78ajNXp--Pag{897d5J?pOsu;#*AgS@DWa*VNtw_CNwS5)&DJv}{r0|HV#&rUOQbDM#Ar zljBbeL=;CwDNUtZCneSg`->`B=kEe`30GnoI&gzI=@axk8Qt$4z3j9Rj!mret*hD7T&)Qqds+iZ2E14*K<8Xji*+fOiFMbtwE$ z5nJ8%TkH2P#(pIwC8O2Owmv<0G=fS9WkWdtWC9y1u(-IGv@TI3gq&0C-c~aat9p86 zr8?M9q@-iH9?3p88R<3o3cKj)-b%5_KyhN&3X%eH)lxalSBCS%7$UIJ6tcz}H+lz` z291o2;PJ%~!7n8}J^j&2cUKn)r0zKo^9CMesq^d?ZwkBaVWH;g&Q2^-x5;f?vHI*Z zkf6|?7K~_w>eUtx%_AXvAOc@shAf@Ds_JcDj-Hd$WQXJDK;e=TJ0vX%c&1{hSIb^; z-nbQEotmPy!u#WtUVOkjDz<=z_cVJgmC0lYI{S)hiZbM%Wb-Yau+jbcQL_Q#{MP#Av+ z4hSnNt6ufUEQ@d#$V?4zbT|M;kg}Tvnx`l+aTcoLmzL*(?EEeZwr5jm7Mtg?OQKMuD~Nd+}PzY+Wysr@OywPzRwvI zXjKjWsHwPmhl2xOL_}nnxgX;{7zcb((&0rL5LqZYQ1;`J<6MWz&!77NONfTr+nDwo z5$^xcF<2^ZVsgijD|R?e+S>Jas5O+z8k{UCKy>D*YbTA1gO&;2u3Iwk^>8G6fw0)M z{Z$N!5>u|sGsNxpop%%56lL+pGfOXhpsK>!GuP%XFb+gu%S4v&>h&>lGXx_xWzijl zCH1^+`?B$|u#&VvL}rwh295pJxWkcf*`!Iw>^>OX-hdAW3%n9s+}tgJ1lPr_tO_9y zz4-FpvfYFeAQu@B_hP`5f|-xgxSJZRA@s<>^UbUhJJOML?*cO-9OOALRgs8cA#%~| z+8V2Jp8C+6_sj2}rV#=wNfJ!TTDejoG>$;Tpus^Uc?}I_czRGid?5Z4)46SW(@T+p z@sEL(_1ULSpRiCMWh;M5OAA7*LG;V{aT^;O1s%boGuUt#bMx|Az!4eJj|*4;wjvuk2F4>XvDcDPQi`J4*{6=rmticB^55j< zvLq`pJpwhyX(g`=Ff0UUsx%rHi9RfMN@k`cd?ajI#JTv)#`W&ddzZAdw8rjk8IbOi z8|=wlKLEv!4dx~5nR>L){b$zJMfiOx( zP&DTp6wznO@F@M?pe2~?8i(qt$cx>Q&ZERxn`d*<6XT)z@(Q;eo*RnyT56u5odmPj z!cHS?1Tr3l5B5YdH8r&^?b@4`^?|FKx)ie1$>>>^+78tdVwZ1_Z1WYC|vvMxSSIgID z%k|zE==ECRD%#GEu;llvTEO3@tg4}b!^g*mNLzbr;~7O^9w&!TbKVX$$x+TD1b~lN zPJ#Xl{Mivrl}7``)KwA8`(Pp{>vOpG=SL*v#xqMx%OZ-QoV-V%_-AhHebJcGSZ^YjXo632bil=9_JnS{{OIyo#Rp`C zQnqtFad0H~c1Mk6rfsjb6Ju102iC=7Oku;4Bb;hL04BH`HHu&Tr!i?Nce;WE9AzMd zAS7l|lFk?Hhm4W4zkhasgGKp_bZ`Q^N3#GOfu?_`tBYuNAzBKl|H-V)yf{lwOG!Zo zF%>i#C=my2{SM{T)e(eMURlX-?b^ro@m~~TP}u?um|0lJ*exq_G{qao;OH{f&p(og z()2c=B8sppxnhAkJj%;VeIi)J>*}mHLF#q7AMrlS`fzZpXzblsul9d=Yzw-D_fyTy z_Lsl1MkRlUzK+!7;lT+qrW`}onq=Vj1V9rJ8R_HYg))f7K)tlRZ;7)$2<%1P)BO7F z4`K6$4tF%yRvMi>&wd4Lhvf=~Sg$2tO%HR=MP?O>BpusB{sL+Lpq0~LgfjsRyH*f; zVJ+d#CA)!|RTiLzo|kBPQ+!xytO!mZIj1UuDwG+UKEaUgL?j1iU^s|#$A1t)XopHE zEq+mNB9-MPXV8#<3A)_Q9<7IPME{*#`4xt6)^{hW)9O3RiTFLzE_6F6A8;{IW1#;B z#q)n~E8jGdN-)nU6vnEwX1GeSlgaywIf#k3d8UIe5)u%U{15hF`xhT z-fQG$C{WKcS#Q04tA>6{gZWhucGI=LM5VlyP`ok8^QP%8T7XP5Jyuis#W`MV(H&cR zXhw{GfC$#;$Cy_mbj&DG+MIxwo+iYPP9?YDhk(WopU16R#|o8@(x0`@-db21M*V z7**%#Z(@(0+pm2>Z_cTo&!c1=3$-Sv*^!ziTjga@6(PXa4OUYylyi0OkEv-?%=XVa z#|}k>Z1FuY3lzZRL&f$ z8X#LKsi>HBLR`Vq(b0Jr{X~3C2};bq@t($3AuJIY-ce2=bggRg?s1{iOFKxk^aA|4 z;OAF0V0Ab3La8!oV8T7(S>`Z`s$7RGh!_f4 zO2Wet&X@7<+A@+n-axt&!}3OCIxz0HfIBu2XnRyWjH2*G7v(M&m>OG|q65bw`j<6g zD(MM;u%G?;GW6YUM=#H>JHSa_(jW9?bS}r=;F=|xe zx@$la8x5>c<{4{*WTDfRe)V>@kfe-^GNv7r`_PF%kt5Dv8t~0xs&Ozhh(AdO(1ymV zm<_zf>~?zTRW3`Rp3Chf0eKwA_h=}@mY(=NCMFOV zj>e7-96Zru4uq00h>{RryZ@(49TB8#N0?@Q8v4)x4_&fgFD*!U{;>%;@Ug$c438Hy z&sk;SCZC#6Ez*eZ(6-?;>qo|6^FtC0UMLhqaf1ItAdM@@M~Yue0{Mzuw)l)+&+7;J zE{&&N$6xJ8lC&4U_gY4LFl>nFxs#DG7w#wRfY7vy!;f(re1w4tj*|i<3u=3$&;r?Y zXh8IE@p|YVFx`hE<~lHr0Z;nU&K=D9P74W+JId??u}L;+tm)>`&EKnSocPCd0jj!i z#XG8=-$&-oJErQxA(LaLJ5vl7eSwH0uSHJ&!K}JvDOd@3;^li zoHWr|0HYGPW%E5ikzK5O#lUr)gr_8?bctWVrek(pH`M$~V_y|fLDx{k7M37b20qDU z7tX8@uZl+EnMqwf73R5sxBG(smMI&cy{}&-r~ z$Hp3mhn1t#2qR*%bF*4@p0!2%qj-rA(+ngIZxN3x3p6x{DWQZLI}IDBPNre%otgct z+c*4s{cR(oMg*}@NS!}j>wa*&-6IbzF<|yV$RQBn{;HG@t*tpx*MN2fCOcm&eO8t9?t9)#PMqpz+b~Ya2+s8=Kp6d4H6Cb~|xvES`cx+~aMZ4BvB{Qf1 zLbZgE(ap!9lzxa`n(hOy`D;)HVWwt({zOW3yQTh%fN=SpHm|w4SJV+iIDiC@IBiG$ z#Pjk3BvWM965K;}ZGv3(1?}mm*Bi1RR*$yRllK)w7IiRiNn!hmt!x8b_O+wqAsAqe zkB^H_Mc@MM^Bfm`o-J2%@##pyPEW5a?1&=AYt2jH=H^+gfX`&Rb7ApY-L6j!PSv7K z0udsAj*gn9r>DWX5)lg$=Pj$br+4DlayN~X_%B`Hh>v7UoRbkZ{H4U?eRDdoq3lTo zTt4$9$cNL^D+z`Jp7n8X73hLj4z@V2Qu)EkMFjz{1`!} znO~l~9tdzUp)-g$%AKZgxq53-&CQ+*laz2K^0Lq758*M(iy(FxvVi9T)({hXTAa?m z4Z-+Y{rem9^8aqIRlcq~;qY1Gskpg-_t^{5ruJcM$cK7XAFJT$h+4syZ0HXUZ#12u zzYuihw_SvRXfa>ySb=jvfLcdK)9%Vh5g2;iz-SAMjVMa@`)4C))3^^^FA9o^1XNVP zi;FLzrD*7DRGG}PJibbSKKw2Oj4!$i=k_)TK9v{kAsM_;7zW%gnZfQw|#u^~C#r{_#giDr&4w8XSAOj~_3n2-(mknGm z_mHq+$momjWW3?*7W?*U(1nA|@80C1zK9YNBE=NdI5hP9f-Z{s?rvPL9jO+- zByd<8^MmY?0z?*SW@#ylvo0g!7B&vfu6iBY&>$1%LWknPnm+w%9&6FkpJc3+Z&os0 zZP#Iozqk(xX+J??B1z?11S+R>+Q;~Or7l2tdKq7tzqaUHpj~5ixMT7zDoPK!9h}xH z7B*K3>avr_#9nr?`+E8LIa4dXV}9pEe@YBGu5UugMP6Z1VuHzgAGF^tvUko;LQM9) zOuso*d-v7pJT_{^$N17rgG|LRnwy*34m{6SlBb=)vW@Iy76IE!53Ma7{cTMJ5D-el=v#kuG*Gw{Ug_G} zdXF1Lf(h<22>}XhT0Br<&O#RmIDvO)~wvv)lpS7-KHJ6 zD+c@-yfc;%B&FZtqtj7Kw^dGap`kW}hv*a=eL^%rkC$I*X=^i>s)j&R>?@Y+1?wwv zFoPkTBmxH3|IJu1lu`E~PCX49>=}(c3oDI(4o#BDX;;l5$aEr_Uo1KOh0_gDvckQW zTKt}lnxf*55mQ`*vyj6oIrw2BqN6c^uLNr<1>ln91rTkD%tqKis!N4k*3i)KY%0G? z)ijE!y81fBnSdh(?|V{JGJy+tdxrcOrROJ;UJT1^XW-HC(MSi%tE_FOrP#HKyzDkC z2#vL)BiPHUhMgf0L&aqL@r`=Ea#ka=P^9&>UBOo@tcAQbSf|4sJM4xMzr5?f|HMU~ zME&jlDtj~v)seIsybxf|V}pqFO0vmHvqJ?k^KD;7^vv%xkVVitW%7XmNajQM#~dXl za4}tiZnXQ*ItbKKUT`IJLdMJDX*C-MaHCL`B7K$X~{ppOR1=tt03%cfcG1%0rHPq#8V9ISd8w)_}L)RkB$Rl#h&6gpj)v{_`*c%Meq zr)b?7wXI}}Zw^$Ta35(^$)Var)*$L%-PT(RQCLC(i+#h5M7{%geH& z(ULb*tDQLCJ-rF-oXLO9bbq!Na@hOH{-%dKJy&oLrxe!-pko z56uVCr$4eo*9A!1SYTNP7ZdREMNm;7tZ>7hpEd$NY$^^npk1)E`W`{VcXihtuG0-& zfZumhZfUz^21GEY>}B6H;At~qND*X;s_&mP-ZWQWV`05<@M=ysgjToO%41=n2=P?9 zHiKMWz1fIEE?{#58aJ(u53ErTj51o?KtWNLwPt5yv#^T&=O04Qid9uN({(F{(`irk z>&`;U%lUJ)sz^nTH!+m+HG@WUJZYe#)7EFZt+^R&%+F7NTrqcc2BTx~X_<60wIn8r z#i{t#FH!1OKi*^to3*QUfgF^{7GH-T7`*yv->8Cs_U()-qA?l2v%v1!axqFfd$^OV} z`ZMaC0JRK=XV7Gxa#~fCx&Yj&V!Cugh3$gX&Z4}NvojjX&3z@GUk8$!bxzm05AUuA zoggsAr;hWvV!=zC8QQ|?%qy9h< zOYVdYS+|ZyIi|J|c+Sk0&L>EaE9&V?mvGjobv0YO=kdg~5UL+wPeIM(_x$oluT;nq zH#EE<6VLArlKe;8g|m}3G2px)w<4{miRsXTxIB2r*UyjVQ29n>>c6}Im4CVN`#{hR z8)cfS)AZ)$=32qr>2<~NlzW^AdR5{Oz*(KQ^gQ~IzkHp+lx*(wYh%?>HwNmL=pY6T z?MnMVljH`!G7$k!I6iuZr)0}sbIw~yFHf-3B~rekoUN{S(HY|nZF93=rj+>G*?v6qQGr4WnfqX*tgXr(0mJ6 zB)lY=Kw~lV5H00k^XXa|SXzL1{V}J&ZtC}?JW?&vzsSYKg-=a=MM6RX91!|Yl!2m) zHZxje0B;yruA}of&}(jPo`FVX|Hw!p=q4{(kIud|v(7@pZoE{L1 zysi~c?7}zDArqg`M<8FdVnbnvX(>IK4n6QjMK93B-yZh>qrHx%oNEq&q2!!&X&=#We-*FAa$=HbW!>gR5OXc86UmiOz4~ z=E_8NwgFWeEd_yZS=*k$`Awi1<#;;-=U{Hp2vsj+a76!mimjFPP3C!9t@)0Y4I8O@ zBs#rU76EvussDPv;7R6_*ynWWV1JPq9O@ssYlUbK%U?e)n00%W?p5#E!=@xA4RmbV z1DFD+lRk;|TYcPhz(JP6-^sZB&v8&F0=P`m=oIqx8dNfp3G15?=dzNgs_NfPhc^h~ zS#F|0dGRGJ`eYtabhj5HD6pgv>V7ACV=g6hC$_t&Gba}m46N_Wc%yqhg=t^Z%hUgn z1O9h$-ury579QlqF*o7 z_;mz7Grz!nS+MXXkTx8c+srUr?eYM=j-L4Nm6ag8ww=FAc}?fkh9>FM?JBC!P+XD_ZFjc~+u8Uy zT;%uF1>>#ga2($GZF9Qc+uSNo-!|;$+$qNA(=w`q&9}s;&EYQzoR;`a0zAS9hY-HC{Zpxv z8E+xgrKDk5ybG zJOhyZu;|aehN|-n7iwm=fo6*lhGc!5_C#FCk4R-;bBxbeK1IR6Fnm~*sPXSSm7Y3N zG4DnDZ9Dr2>dt;ippV#O;xipPFpCUaq_5MKYRmm?B{EUQBZ9YtVSE1AK3iQ$Wks*1 zZ$bx+vEaxzJC20nvju~ zae#A*MZFjcZ}FgL}6B3rKaTKqaG)jz6Tk z_HXO1X$d9RZV&?sC~;^g$lS4(RFTaBGk-}A4((cM`5EJI#ZfwY^inz9~t@N)&GC~{Gcd*FP)Cn0%ml88SSF~Z2mb5^Vl zMt^v_$KA;2=;&cHMpImmK-mV3=_tSLCl98aJm1kly1$6~MHv(970|mF0NvXlPcwJT z%P|j?$wJQ~Ha0dBtC^9}MLxc=RyKKderee*Q&T#vRUzZZ&m8sxCdBVDd+Smo9|UZ} zt1V_>s!(1Qla&pVlQsQr2Gfr4y9T1`Zyf*>0|{^$c&J8bxj`gpRPU+;IINI9&8=SZFRP2?oUL*hSL?qEQjmVgXl5%i=k1eC%XD@WFLzy%3E0EgNxDD zc2}*{z#Y&G@=~5g;5ShC@J-%bQ9eGEosDUo;#c1l4h|0iUf>iNb&v`_A3vZS5djMY z8H(vylmFEL#gb4nc$W`iwShJ0%YM$RXKZ{6UYzqW)^hczdwxt}{HXi;Czjc&)xu3R zxBI?sCmFYuZkH5M2qJwida%Kv2kSc0fC;De{CybPq3+Ypnn&b~))`ql);uP-<8nGr z(-feo_(8GRh|G;!w^ZUXlym&RoON(8AI`H8_5M9J9O1IG@`qO$10|iu>xV(JCi?KRS&+E#WqSWabFT%~}?zwsdUKM$HwQAmXi zNFFFt_!n$>51`KkUN-=}umWJ*x#n9hLFeuZbujYi(`iIJYMAU3O&_M;CkS}ntS6$Y z9`rHpZeQg1IMN7jgyFE9x(6>L5atShm^U~fLL&X_nVyMBR%~Zi7gdqi$t;I3PfnIQ z(@TSC0rrlDccGkL6>k%a=9BO~Tftl*^>+`D-Cv*7v$QO57B(HcU2Hmp4R1eLsP%~( z&v;!#>nJHyYB?PS$~Abg(7{4IAPsCgx}3wY%UsYc#HWp1k>tKd;jli@K7KoJd8xO> z)yduXo|37zwY%`vYzzpHhVQN%k(mevS1}0T-I#D;f zW;qIUF;z9SS$GRfE4<_Z@$#$-{meF_^J=3py$3yYo7>xf@v<|L*e0P9XiF~pV(ZRU zecQZCS{X{Iyc!v5- ze;oIH9QRT8=ks}w*Xw#+*Lj`id5x!}*3EwwS77Mlr*Z7xkm`^SI3c%rZ(;OSCNhtl zEnZ&Ray&du?@*7BK0G_cMnE^nz)>_%8hd<6;&?B1aJz?N@;%D7i^(h&^Hnrr6)=Z!a&lT0eWoEVbNW3Y zzTWnHTJAb zN)2d%OziB*^>1i(p}7JEVG6^7X-#J^cFiEw2iv{&`%r!Jb2YAa@>6os`Rdv>FQyF( z1?ry$TYuSlKQUkTl-&a+D!Int?~dXiW1>oS>`zfWx}sGZeOnG`!&JdOmaB<_XH&Ph zmnF$5dOc%%dz#}^Fo|MLKZV`+D-bS{@7=pwu`|)SbV_EyotZY9MoIz2>Mn$p=xGhqi@-B{l{y8BUE^RdFn=s+rKFkj?S(z4ZK+Lbsq@5!GpJ+|N)XGPc@>nf7Mx>E3Q+j7BhHeKLt`*Z_4lit)*D{HPI+okf?lgoj!jPJEIisB;=X6pwP z(+%9FKHUI>0)jh~pYulaz7+Dc3JB!rJ4KW@SXhv;u|-j^v}qL<7gzW88ueSU??mi? z7=5&u*00tp$upwIEi>t>**zg^Wql8mfuRdaJr-QP=^DX1=s38t-1siF11 zhf1@kSR(cS62FZ|UaFzUhYMiLB6xyEUw?=gyix&nm=+tAkPs0T7ItzP4kplET4xw9 zpDHC=233n7H6H8p0W)<5EL-X1J$#AX1@ZSI{+X9(Xb?N>wm8hvbnE2}FKMQ4J<}ah zj77Uxsh@Ew7Z;jMs`x<1)(ukU+V=>bLxT5?WawCc>hBJL>4v)sXJ3KWr|muU;=N+^ zLx-~SUrXZT-1vlr(_5~AIeS*{Ubjd*Y2RmFrzkNK7>Y$+Om&ZOJbpa5J%pbxwJCh= z`sq%_@UZKfiVAxydfx7M`SSAP>D*vi>J=NlmL#Ri^6X7UIhX%c(vOyi9sd{N2|_M_ zoI*(wKzO=l0wLMSFvzcJZQUwXzIr8m9H@!cv-jEuY^bFTOW!}rx7PR>G(#9;Jyxcq z-6oS=d6bE%?aM_^#og% ziK~`4GhyU~(?s`Re@jbl%Q#qVv|Rfqa`OF8?ELqb;;AByY2UR+z>(~FUeST(cvFQh z#q}VBnE^*0dx%6e=6#=6xu9~{YO85gp!L(Qdq31>2*>6_1?QRGJ=Zpobn7^m`QZ*xm$~etQh3oRKva_vcs?97VIc{?+gy3l_xORcANOWbk+#-jy)L}h6 zcCzBz)?X8Hyq5+j(N!}AXJ1-09~~j=7wg?AKH`X5nYXS<*xh z#=$lR!h9sW5d?riWKu_QloFc?{7Ax?33@FDN4mK^8RT=}TnEgTW(Fck_wmWgZ!0V; zj5z`Q$k6z_>f^JYL8eY~esyE5Y9WQAC&^PgPwmP@-raV=bzUA4vW4B|J#S5AxGu?z zl%11$rQT-qQTISTagDkYlftKau5$+L5~Lmd5|))T7x3(0b>aSo3%MujM`mVbC^FQk zZs0nHu+jqxJ>D^N(&4CvL@AUo%fmudMss$mzdSLp8GP_c^%#(?Z^0cB~fiRw9CHQ&g_^Y)#OO;*zd7T z`4W$E3R$v`iT&}O4~9A6E}@fY1`Mwp%wKKELh5((hhy15o3CKC2Xy?fEdWvjp#I}Q5kbJNoxV#PYB|0n>^t-Hw#n42D-(lK5g(+73)C9ao zq;B!ax-yi$F!>`$rH$H5A1ZuB{STqrlpz|vgD*9$xUe=l`>}kiT#u=hp19%U&qG@h zi%d<_REk=wN~MM;P9G{ea!a6Fj(4!lJKIKM-f|MWR3P#V4Gn}X5n)|`qnuwtLgU3- zI2s7ijpTuwUe|s1BB5adXxNcyL`DKF7*U8I2){Ue<_!J9RBz~q4<7_OB(5z?oQ%pk zcJ$}~>d>=kZD9N8y|UwIidP6FSgQG47Ed|nwk{%i3?ZA2matGQRWxyO@jB;q((Y)l zTAsI&#Y$_~T!Xksx7TgK z>?TO!($Zf)R&ss*^7;-tFvw_<_iYBJ=vaaT%Aft#?m88=fat zFOfyOeM<{W7y!LqxwitL7^)VLFs3V`Eaer<{3*ca`A*s@I(a>aL+HYmAiUxlb7uD}?7kQO6Wms^>?*vz3WYXEkSy2jQo3Cwb7a4P%LT`m0 z_;@W|!~oRk|NO%UegunAk@JlgFQgzN41;)N)S4!SLb)sG#fuk_|NgrXEk>y7+GSq4 zo-+?#d}M36ZEiXD#S5~t3qt{vYJ3}gSFUV2l5gZIT+D8K`+ld)@1rl*+}eIuox0Ef z_YAZh_IYh*(zxpM{fy}DV>LU}A0NpP-jH@N;Lv$PDi5cw)$Xw7jI(>h!SY7LrA5aP ze#Hz7C^xsD9d!KkX~N+O`g(tC`Ek!XqY^+Qpmj)|mJGXTOy*fzUeejqBLy6CYJ}CI z{q_sXfs%VHWpVT5-7mk89Z$YwD{9373g-^qU2Mow4;iPc@Tgy#Sea9B?kaoQBH0)v zR)1}}RCiJqy*q-)_1BtUb`2Z?A&~=c^CZMOFtFsj>F1YMv|T0eWEKG}9!V`>XFi}Z zIe|<(b-VNQ23u*vrRxFyPtfWEbhtii0V>vqu=4Wrk{`lNtbTHQ5;BL@WxxbHMJ*Qc= ze)@)GbWn%2$v`5O`P(L-#@|j~);KStemt`J_47@y-qETGpeId0#*uf=yAH3Gm9WPd z+W$#HSaH1#*60;Pp83LhZ?G*~q9FR;P@VlE*~n3h^w*m>93fxc9yzVY6Mn6*c68Jx zbkeNS5hTSHA)xFnvK~%*pI?>@pLM%3U~PZfUdr!7US4V0It%VfZu$`-$;9b2`q|uX z((JHf4hrp-1H=bYOMW3mBv=Pl~fO<%g=ZzT62GPLdp*maUf;3g*R zI?u-pJp8a@Zst!iUQj%9Z_0&V8w6O7s| zCU@0a>IsQkq_L_n=}8`5Vq!h9%w_kAYV+ESbF4b?Gw&BUonxu0G&gVd=aF3U_&Ok8 zDjYi<%R-x_gCA1cfEC!Le^f@vMS-kchHu?yLWV4HMQTH^dsbz^jS7L-D{Mc1}(n9r} z?Qwr!Z=D_$IW#RZUep}Nkk?gBFjIu>o6VfhNgIXt2PKIxi8=RT_noH!pBxLD{f6gbv z%Vv9bJA_p}E50Jr^|Io2wyCpo-bbv#re{MUp{D%whR3lY$Xny#kCn3s5$126v+sP0Wr`>;`UQ>RR>SCT#UwsS+LQM<9j4@ils7LnSGAI( z9S~|&RIt9bO2K#{ewlvn9t%Ka&SDkGEk6b|ONt~Y04OHj zQ=_q?p*a%Oj(7QP^ZQ54h-SYRw$U4xP+mQ^c}=d1$B($@NT(ju#xQ*Z9vT6TR&Ue1s>|A>KC~KVD5L+7sB@j|?X&PqjyGb}g~e(W>s^n8wo-#92@iNi6rTb4`T6^GPN~Q~ z)ubsY9hYB-6QUWh6<8iR*xyc^I?n!Hd6z*zAPl)sj}S6sP}84G|C`M58e!8OVRP3= zMPCbT&qa#L_wfJVR%d(P1fk(>fTDp&+=*tBe%6CG`EU{>xFQT)`7QkI z5CGurRXrY&C*c_Z@DQPfW_a3$Fi^ z+_-V0I2f%+A5T2|%FO9b@eBMv6#`YY>ynIqaL>{UO&5BxBNraE+T7T6P1JO;OHN!m z^7HIJee7L$56K5DOnTjo-3K;7#m6ryn%QkhuN|0LD*0j^r>^ziVobmmn+QtXP?WuS?pB#ri!%GKd1 zAV;^&a~1sf$k!WOC0cl1n0Wu(MHtflUMc^r4C>D-8-6|!&9qHuga%JHeYydHvfXtD z`TiH-#BsizxJVQi$X>~XgwkxHa9af=!~Ohzv$npdnBJGXEK_tB0# zwPbnUM6@XvCc0)jzYYxzF)=cp{Z^wED-16cU2nBt-Z05?Jeok~lvHqW+$M1s^B z^(^G2%HR@%N7j`y@At$w>3#D0o`ri7h(VQc|XCH@S zh`SVn0~RliJWlTL9Gjg8g9!~puG*f2ydMlil(h4yeyJUgq(+ZWbpm<;mEL{!-2sB) z4(A&x0s_49Q#(xu#M#4A=uk+18NZdxir4M7xXZNu+lAvhE5Hsgb9P`LLVWmBQ7`G0 zZ*R`T%iqHNKGw}LCi#Mr_ypa>rO$p0|F2xzV};aq-YJFJ%;5d!#c`j2*)m0l8Q5Ti z3-|@U-e#+S21T8&iC2JIzZVnw(a}*(rNtSt0e?nNK<86$*yv0L)jWuf zhHc;`b%lAYXOuL=jr?9)EKgfK?d~o^7{U_hS$Ns3K+7&(^83G{eJ3Tp4y=APc29{& zB60&&&5p8|U(S}GMQ45ATZ_h&@_&KCC=^Ci({IOwhB$hUc~YbsMDtxy-LbLz74n`7 zrdZa`49sB>@-nxNG9gZ*w_2q4(Tw?!4Vti|&hrOu2QycP4dbYQmr<%SErN ztOCr9)j#JB&4Gw5I6C%#CC|fE6_Z2Y;3!zi25U$BLPm!&T}h1>7ORZ&C?gnj8%V_7 z-tW*C1_bxgll^?D0^l-Dl!s;b>^{u5L6N(Md1#r)#`qW7R02;t)-rQ#J--UzT4(rq z=e`V{jXe^v=c=uTx;ThYCNa{s`?9L5stBGS;hb@(Yf^~_B~XWPIyHtJL!bj)a+aX1 zNx<+N{IVmWQ5FCb-4}b^@<3v1iv$&mry#6B=`tBfk@F+77$y7ku2hw}+g{R<0~$z# zm|23r{4l$8`6fUR#!qEqLT7J{5&P(W2RbMa{cfNGO`w=CV+T$=+LqSA|Gw$J5Dx_U z8~~U^)Pfe*S7F9J@P0&K8;WEOkjI`TT<5km-g1TJL`%pgD2C@rivdBq_2N^ zDO+5sa`@7(nG5yLf39v+w(B2OWwSTzx3Yrd0@A8V=+HzA_D2l-Ci*kL%0Rex_vEZ= zdrl|oXIu%b1k3}w^+4nSWsPTbI?Hjb@)XjCIX1kVW!*nnn3_z#Ox`yL3W~;7CTJm~ z0u-oB${iJPjoe#k-{;ZBb(6@ zKrWVwSqI6tm?re2Rp5AZ|!^oXw^Wz8d>wW$S4&x@1)nJ~j%~44|sL$FIb$cS@0a^F>-=9Is zl82Nfp&Z%l=Cat)h{wIis*_!jgvI0KJk zGi_N1X1)&~WcXAUnNCS{w|9D`$--=7KD%Z9*r%z#ju9FlZ}2dY?HF=f1b4tjd1TZrvA7> zGUm-QT}yrLWmfB`h8jDWY~+n@_c7Krj~Pg4=6U002rEduAmzt8G$pgj8(r((>>IeQ zMbOb~*inj*ZT-0-k}mJaLCQ9DJSP07&}0Wi(ZXb-YwVdooZWNx`q+DWl#xqnNTH^< zU`i0crYFT{MrJ8oDiW>al=&h@*B9N!vv^;V#s2mZuL;U-`6n8&1){v0#G;kl(ZA}! z)wp1^k@VK%Ia!}~Ce3u6drQOdL!m&R4UJ2CATgqgm!HmMN@X98{B0``BB9GuZh%W$! z!lVG+P(d(bj~Cq4!VrI0K$6sB@$dI3hRr13^XmEa{|*@aDS1KX{O{Pf?IT$hx%;>L zX{y${2f5Zx2~p- zul0%+`ADY2=O!5yZ~o`fXR`csvwtr3{0V8-l^lMtwyB`md>D{8KR+3+me6Wld>tB(otf>-Pw28Fz`5AXKV}qly36RiZpc`)>q3wh*7lD6NZml-biHV8gK5N&}z}!ga z1>mg~Mht&=VV9^14DG2vaZ^LK`}<8)8wfhseTDwG^_w|F#XB=#wzvJaD>G^4J=-sF ziQoalheUgXG}@WNPo!Wlcp@h!w;l$BPH?y|yvbzO7HL_mJcXsTiS4VuRRVH?L|qQs-Bl&Lo}4Gk9*M z-3>^q=06Aec)r%J$M+roYfPQ^`8TGvk>h{?dTncLpr@x*oPP3I75M0m-&%GNv(CVW zp&%6$7H%M6QW0Ub{_Y1@UT}qO)A*_q81#GF(bnS&ai=O1r<)Fo_-?+@r+2WMVfXGo z(1iKBW|iwstFolez2z>|VM*QJ3KO4K!mzMK%jl49idM9wCAGPQg%h+FfQ(h8-lGo7 z%*=!^u(>1&COI&r!@!`i>s-OK0Q)rfJAKwfS-lNdo0OM5228~MJgN9-P)wXm%A zDn;myKKQ6KUdYB9(@k!}@Xr*?Ak^Q;ode*!0u|%VEt|1=zGF}sF*1+Pbu}L&oaZn~ zZO%wmovN`qmbHO&|MtWi$4q{xeS`3@ zU}Ga#`Ws@o&jjlRW*m{wlM`egkTK9>>BJno;U8{%*oSAHDhG-7RgcRJJ=UZN`#-qA zX+9w8o>FzLO;3^=EleCf^?xT3wYe+FXnGz0|HJv+Iex~l zUO>rc6KU1Uo$W1mgX3i?*sp*jnmH{QJa=;u`@TXwY6_z%-1oF?6r2A}gtSV~e{iUn zUjH>tcl~;SaBTT6L#wx5&!2#kOyI>WMG0a_=4%Tw_{fuM`IY4+fJN8YyxzsPw1b|K ze2>0&ty`^(o5MMy!GX;qBKu$bUsJ8D{d(`#m*82xGkrDXYi^rJGs7RwxO-7yOK}h! zW|4!UYPCSB?xvE*Pk+0uZt`uD)VZ}=II}oH)RHa_Bv4|vjVsFzyn7r+oMjr8rx{fA zXByk~y zB07s}P3kej+dt7b(0-aJ#!>N_DP_qoLpGdPl6t4NsLVSHN|2#)d8Cq3ceS^7rk4B< zqD7V~G{qeo_>PQJvfzA8n42&S4phsEDxl#dk**XPCRj4IouA9F6Yg~305RgdvrT}u zn6rwLnPSMi5jzt{SV&n$Vitd5UEYwhqz-o(J;`0Vfeb5#c=LX`)4uVrQ32iNjgXX- zUs94)MhBsNB!T^#>cF8A-c(ggX;?23bgOU|HM2$b*E`yslIK#8W;7gvOD9p!iS=iM zm&F|K!R)#)Xuq7t+v#98B`sLogkQa0tiR3ZC;4ZY<4Mr^($UFmDT^ zix6LxTzxk5njBF+`T494@t_n(&D>e+CPqxoBOQW^5n6PFHPy?@y_~pXqOgEq2R5ZT zld|CS+`{aYlhY4kVveNjYhueo`YmY4(yQ|oE#l1;FCKrG7^M=1J7IJdC7sf`6YNSZ$@h$ zvLJXkRWE%Xasq9K^ZnC&hkkCbgIh}W50|NQ)aOztG{3=bL4h0d8Z1cOG=51v*7&mM zMf;5*C$-r-yEgrfBa(kLvK1T2twcSKLx{L~QU%Pu!r?l?iOEcvSAOE%fMRp@YT%`Y ze|SebvUqTSq#3TQyl|UYoJNdDQ(rRuNz)rHtMZQ%r{o1HVtN(4;*Sk?b#@YL5IFvA#&XlYwk*d$uNj5>IBmQ}1VV7%b# z9zmiwvAVACOicJ4k=0)U^C0k0hRzTqsW1vop7Tbr4LZ4P9zD3BxZ*@mDQ!OG0!BXyy40K@OmZc##^=?%}s4t;#hn?mzt@@VREvW4VWS0}l2Z z-@Fwd9dFl9&1Fo7gUsf6%#hQIOfMI;U;8wav7=#&52O5)mK8`*K^f0#lO?c6UUF#I zG`{`v8qv%iExyXQk)7xBjeJTEBpeYd;?hD4JP%o@kNR~yfX6Hd`xa}fKc)M+Ep2T> zVbR43QU`{tt*ay9gRw(Nsk~B}J%loXLpm=@G{Tbe$SKUq+XEtSE#6L=(M8NG+gQPW z4s60#Z+5iAyf|^b%B{cp&p!zXJP>^|g03|w2{agLJDgZ#uaW7&cX8j( z(wJWNWm}s!XAm}e;}snv@Zhx0rG9z_hOEURXatC{aqv4MJWgE7uDpdk1mtST5{uH( zpu=TquZ#`qEr@4qaz+enZEfXDShK}Kq zKLw?9dAgTpPEYC~1_GBMCw&Im5)l?@Y=BqhHL>(f?N(fzQAW3JPknA`tK6ewDk`6W z;V~ZBff56Lc-8Qb1dp_UQ_1b;56f+e*V#{;IFYxOiOE(&GXvGfk01ACOl%)Vq`Sku!0M=S*BuBp}IvJ3tTC780Sm_4xvYciBRB^M8U_Gm@{8m%x*60{2hS2oN6-n$*XTt{zZTUD&Fg;5g z|BLeMmqFOX4uPK?eemjT(r1(3%WRm*XaDX85x<}d$-eUSB(^szSVj0pLtx9ya+5pi z_Mv6Z!6!KiX699tRZJT!%QfKoi_QcE2~2|<$WtM%2?-6AfP)ZHbKqvGQtE>D5;5@N z#cfb#ETB}4=|I|#``(Yr2>ceT(m-HugP0_BX>L-Ayj%9Z_CsD9p8#)5aQB!<{_tid z5!%?A#eN@?$sdp}z?+Cl2xA~$Sg8|CT{I*#hrEOiIP`5LKCyfE?y8<11AucOc%HiY z$J7>0AO+8TbB!7Sb6jg=XzF`1o|6Pr_n0lFo^d}b3wjSccj6B)E)q&hR%E|}kchHE znkk#{A|=^cU2b}+%BoqE*x6KuxyoPIqI3#Z%h%?`pbXdxyE+wc`2FFZinD}S@Lp=9 zkU(Nl@_5cHEGzkBWc09SHj~WJ25?zcR4zDxFg9OJYsM8LXJI=8tIf@bj)aLfzNm(v zjm)AMAOx;36a;|Xk9k+0ckw*3wCa_x9u7Wnh}{)*3GcVEo9yZAEGyFO*sln^)V-f0 z;RpH{aS_^0WIJG@nRPxWD=Uj|BQEg<@l9xc7no7z811Xyuj7?mKxu@q@a-y{!u<`5 z5TK-8>80~LZm6#x1CQ;S1rV=TM4_XVfk|&{aBpBdLiHV{Bh7)kd zK1!4OpiYAP*-L9Q>_cYRhZuDweO>MR|ZSJS+!_kXD`2`lah^macU zt>3BD6OYsyLxh-P4iQZfEP*~ddM%ZA+C~v^*k|S#wKjf42uts4iht1E?oG+-|qo!H!Zt3;lU#=1si~`-RVpqn9j<_l4ccx1(lKM%{Tb8&Tg$NT z2nz-M{L6G`M%aBSU;>XXWnseAiy|I8Kjer~>yjhzP}?p?ylPFVqXwhSx;b$Zc4h?p z1k9ngl1*=&FiaInj=O4c$A{22}EcJHnK-Cp)PJLeM%02mI4+BM8lvh zQ=M&0(;@&PuXX^2A*?*s+Qx58qHJ%VDNs?^Z%5BUWB3w^k z2U`7i_V>1fSI7~L^>Qz4Ms;ALt!vbpS4W9)MNEkLm6-ICjW?rOxU-+p2>ON^y5wlMNgn529NuH24nY4|2}l26OnTCc+QqCvCjF)Jgi82yhwV4_$%ilYXrxF~ zWl6XDgY6#aWPO+42p6s!wq5rgnosDPz~A>$y9QG`Pl?KRC%-DC37q;weRwXe%_}G+ zWiKdr6o}-Y{1TY}+F+=!P&UHxIN8N0PrHLN&BDUs+@%s_i%320CMg;?m6}C3fKJf= z#2~KcDk3vsubYk01GL=odL5Ryz)Rz}z@o*TJZFD<1-~uNIIFMs&LnL1G9^mzx+7`t z{j)0ZYW?9+i{Y{_|4a|p=Dauwv0M`IZTlLNokz$q~{Jmn=l zGcz;%XoUP3Bhrb@$;Zok`0oAtAyCR8$nrw?R*UKBdP%aj%eR9v;3_;)o!*vLhuk3) zMMyCC{4ik^^#1vkt+~j>mM|@gYrkZ@<=m`F|2-v|gj&j)hW9?qU7M(RReF0c3qvy+ z+YuR}3c%!}Qf*f56nr-F^9RdxSG`tNRzfi(_!jEfU~KjFu@zcN>)1eR`kqI`j0Hmb ziag?Tq=!wOJ`xmFq*Z#56SaBt+6}!qV|^<=CqzXu$Gf;UJTg2S0-MB8ykBZ;UBYJg6;z~2({T0m{*Y2rUgcr^+U@>GVMeoKs#XZisqDKf`;Xz#F`1@WYi67(XDxIM!}vYilsH_;qB2 zAJf@U+#>DCoOd5}`AP4Uaw;62N{~ed-nzB9q_h-2+@jQND41$@<4g`SX@_k$9Z znK7B25#va|FZ}n2N~`0lK~gtSYzNYE+{C29W5X$JHiXehZ3qh-%g9NXcx;YZtFz2Y z4l%N&^XvZeK79?X{tLE1QZ6z&q1v^G9bWv8cdh zoG_zdWMq8gq0Sdc3>yCa{kvy+`lVZiKXqkz*3pAcD5`_Q^q(Xg7OPt{Er2+Bvz!xZeY<;9B|ljUo~Vu)e=j5CD;2$=U&{2Js-Sz{E3rTKKphf`y2&f_9g=3eoDrm^yV1V2If0s zKE@gPdDMj5;&o%=sg7NoPo?r-h=Yr?^*$aL`l5t^#f=SdF&q$i5DJvuPv*{!Ih709 zI|@3%XN4Igv@7d4yI^So#!TqycGxJf!8-KQC&PC;@4R-_PvVtDO%F=elSe86NwNg~ zt$@y5Ot@TE0HL{AXNBfeg7Q|-eSV#fW)p~&2M6wu=xFwL*RXmqvhk>?scBxqi@%(f z*U>e*to{l%W-d??{oceNaoq7g|DJmaW^im{b)f>$_-Hdhb%Y_R|D#a({{z$w3(Z2* zo9bG_lOnUu@)dXq*~NA<=!g(xWA2)u8jU*yE0uTrcTL;`-%FR=I^x2KC!s+W2iqxd zvOko>gH}WOjEua3KnbCB*tvUmA{2u-uW#V2fVelMa5Zl23h1@E2jW%ZkKC$2vVDmY zSFEAU{vqW=ag>4Uu;G1eavlNOiD;Ivc6#Ey{eIsUSASG{`Zt*mU1&95So#}LLDAq- z!4>aB3@&%ucVvYW2o?05Y|J{<`?8kDqqz5F`aW^DoV4@3!o^70iBvDI(Kf4avi4Zl5T)i)v)G&>+|^`z;cq~*yw4=aAf!;}F8tw`wS z5c09D4~vd?sjrQiU*;H|y6r$a@@spJRi$3%++g`56_s}beUYB&H-E^!T^6VBdL}*N zYA1hX>UlOR!7i1Q_%q{)xtR9E$=5>lvC-~Y?I{wk%E=alx6v3J{QV1f$>yxXN!%%q zO6bj+of-sFZbUtiHs@C!=nEpC{^Nq0Ki)8LGe?BTO4lvPn7*$9MU5}9*z`~m_p$RD zL0$OFp3J}`KYZrMKqUd*@8pPbxNp%k<$kwg|BesE3=5*$LcxAgQE@{9P9Y>Wm;OcK zkmHjAPHr#G>cS+yaJa8BF*Vz#91vo}59P*Cge{Eud4=9Iv$EVvSWYTzB$J(IKYCBl z--|tn>u8&!uHL}$3wuhot%K`N2Tyu$sTA&*2w=IbvId?kZs|xSs}`U9Zti>=PVts=H-6!lz?D&Hg~jkrk6LX(|N0tUqw@jY z3*pC_O5@fk?@tHy&30WpR{!{A5jVg5R+;QQ2bkKp)^L;?Gb~io=ebKGBdfblF6QE$r$r|d;7Ms{^4DQ6JLVEX#)tm3Z}>l zxVwPtQJJl}DdKP6aR=S^e-xo6MtH7{HRETlys_boh)rVAsw&Limc#UzeYC~k@9Z|N zquNv};`Df~w3Ajr%BPNeHYNOHTF>J#>~*X1u<_m$du_2QvVoEI%6iY}-;l6D8~>yA zhtH=}{Zke`@F|kGnN#AA?p(OrvCnjB>T1FD7aPyKDrMwi3OStVY7TM-h6-A;>he!i z%WTz27B6sc*cL){8^k$xXR(CU^ zY2bRhNT=72;n(`(-?b*U6(qTr&wN;PR z5vAXUfnGg~@))Y4BkHgMNb+^>pY-{#Yl8xsvV`gfRZNk&(}fE*um8C+`2AY#3Vz0?|1{<+*Jcjnp0(wv&E&y)C%6<-W zl`j5X?kI-|tF8lRnq|k^7UbP*`{79jBa01a|LlnOS@R&s5vcy%jHYnX5Y9R2Cgt2v z)2pC)Lw;Di)h7Do8Opi9KnenYCtUWH=LVQGT67JvDhItbFHMJU>0FSkFFRUEMNm}6 zbSBbLq~E-eeH(JSIvgWG{|2_#;#;XV5xg`^Wy+U%hqCOffk7Hq8D^1@Gl=0)_>S8<6u6R?_I@*d@1#Uy7q2#d8Hl zLRdx=Cvr>jxzKT{O#I%Y^n2MrhG(dnhyuNMZl^*7*F(gCkko<9>?3xBOI$k{gFGRw z#Z027wU>a+o5A0JOISptIHxK;BX6P2CXe_n%v#w*8C3WCHJg|*Etrow)eGRTiet16 z&<9o$+>9b1{xZWn!@%>KnXQ^1JUFMRIWtu}J%|p8eT?xl&BM<$zZvUlX*mxMov51f zpXJG7t9~AJa6yz>^{me^Vn0b9ocxy7rT*~#pdbrk^FT+UO~KxDMlKF&2c#!9}IrJf2L`{mvnmqg+-OEWXPZLVI5d1^rsk8JmG5++AkB|oU4uj<~X4&U3Y)B%^#JslgZzvJH~ zZJj+=;SsAQ7MS86lcu;6oO+nzqi$$<{j4O}S zNRifaosn-c6tbZmp{eJ);QO)HYA0W!Xh$D-tS>L^Ar7|P#KHF2M&Ov;g+0r1QnBai zzhby{U!U<}k<_vG{oo6gb`ajG%h4GJpTo#;12a z#%%HC4u2wlz4d(k`3>pn8%4}SyFX>o8ml4q~n!rf@Vr97bqj31zqnjRiEq_@KuC`3p4_w;h6rlB5wWR z(e9zwODV0K=`zv5A1|G35P3V(yqZ(s_UOfW4BWTSHg_%Go2|)c;)~2SrjQ$=rL~Z> zY6GA>Vs9@(O!*OgRzJJ25C~HxboeBk231JD`Wba#Gv|Oe%Q`|sO$|rxl0MwD3=BuO zUK*dPO^{FiIM$}0E`QhG0b`MS;q1_Ncs;6XYUq4qZ_4_jTd{tC^Rj<|6IyQ;Muwsx zgowyVB2Xp#H(~(-YaPlz7pq?=NX=WX)$Sp}ebL7kDJ~g#)3~baOCNU=RueG0 zLB%P4;6Odd>vP?U@bvK4&uA|#!$>hE7M1{F!Wb%3Rha9;%E=dhU9zI{*5CQkA%TxA zkD3Rwg6_fgWSLRO<3F+L4{{&^YhAmX#`p z!8nAOF+4OuaH{NjKmu|LXr;n`_xfIM;)h ziy)iSXq%);JKD-i$0-tjORcISisnP(C)(oS+m%l%|GsA99$$~*A*a1r+ivGq|3sEO zwt)yRtL#=|?3iPOpU$)8216GG!GW?vpGtB&39DvINs>yy%Tlt zl(t5PuLn<@@|DX6x7i6hS)ZT^1kqc7?_aRhY}oQtLq`VpyK}fGB0;-t^PziONGOHOdnT&(I%YLcI{pmGcmb)M#9~c-J$a^ z@_iO^!o=qpq+k4U*os-_lBQoT2vkz z`6v1p&YY=CMj~ghp`>sw5(?L;=zj8gY2t{5OuvL2qYrnV`rnk*<{l@_Vr;e8`Fv9c z%Nu^7%8Z|hlB4w_ekyIB{s!BKT|@4g+;87qa6d4?eR}gcBkSLgI>&fl|F;I&FLEm? zck!|hye2-wEb-`E=u7<#Ys`+KFSfe= zsN)~uSNM6|oG&px1a% z|C7F`#DT!&iK`UzzJHT{^T8s=wWU3OGTmg`4?E0%i!J@^_?u8zNYoJ}TLU{1^4mM8 zmU>3^1}j9-NpZ})=XmnH#+u=84*I{Gr6SaI>^$pdX`*PA?->$j_0Prxle))Cb`2LQ zl-8`a#+Ur90>?P_1vhB?v1APm_SkEd-*2$;X|#j_tmU+vN!$iKgZ9=O7Kpw+UIRVbhyvd zqzDCIip6CZUou7JSR6VaadXG#@T`wB0}Ka9q%0*4-!9%i3%!{LJUO?P%3F`j<%%oM zmFx&xBzB`DbcVZC7|BHPVuUZk}mYg~F>C0c+xmn;G^?%~aPGYs( zG3RyN(5le4R&MJ)U0mn5_T$N#o_Le)7ve*s^m#fJS9we-bfdg!M(zX^gc#jF{Pg*A zKFm5nVD^hpAGi8(@9y2ta7M%l69|PvApQ1kHSH}-c}-_mB)JbC*wAyvs9G*5i;QHv z+$b*0Nh}WF0FqL6tBcy#JtTPU4@+Jb7ht>-#AzbaVZ9H1TV>=`K$$6(l$2mAURe80 zX>G{@N8)ENB#Bg-kdR$!*@-C(E+)+?Dn6#8UL0gakv-mm+k4JDBeSL*IklUXah*$F zr2K^C^Jt?&riKUFXz;#2<_ zL%+$H6;rd_)wjEDxHt66R39Ww^yD*u;DP=3{dXiL1(~C73igBgi;velJux=u)S~C- ziW|}5W*gCp=@XgX!X#b)VMS6S>o6Pn2GUs#4c?0!s(Y7{>`JcfrXkII`mz=+-5oG_ zTBS97YU1kjoQq0%QZu#QwS61{ns*uZ*_0f#C+A)dj!m}w_U@W(AO@H|U zyT3%8k@>kf6`+6nIO*0F^2^OCZ*CGflct$MOp49&aJ(7ZotT@e>EOc~)rfx{p6=L= zyC*9mNPIg41Smn@Gr!N5ih2t*mdgpbiCc}eJxLf^khc5hjw=?lzR{w|xg+1+xJs+q z@qDx)voO)!o7o>1GleH}$U?^CzeBP?0{hM19z#|+jbr%uKkMKKLU0! zs~GV^c$Q#Z?Mz4cT3*+sCy9yf;j&D556jET`^MEC?$7faI-oq&s6sc2qJU)T;2`$E zC%LAz%QbjyAW$?m%flK2penM`PL4Mhc;(%mw9vOVofsFaoiF~_!E`Ba<9=_?&A*qQ zNyIuNNOLl;EV34-UxvdZC5HQ8Vp8dMK&+XW8T~ol=vI?_30r^s3Gy*}iAN5Q{@J#t zHiR@LKcy`P$|KDxGGS#lyaGWg&%GJ~?BjU@M}!+(&E&CNEON|w5I zZ9CC$;{mPvwP9;{*%qUT+@f$8hodRV$itI$KoJW91WtS7+Zh=m7;0{C9nAl6@8mm! z46Z6W)zB;STnWFw(bH`LTHx@CV@el4&x;{GOED}B6jD-#u8Tu@agw=zo84LyzakQ= zr1-|U>L+qWCOF$2J#u6tiE#gxahuvkG&xC_vQ9(DID6+XFkmyf13>sGlG|*qL)GQJ zGMWD!{pk=*B_<}*C9em`Nd@QlG47do{7a|QY!}w<-q2Nlx{>ozx7Au{4PKL z&ud?w(>wu}Eac~(OVsWc6%ipsW57+Ci`~XMK6kXY6VVj{5ARCu3!r7GUX-AltujhD=oeiAr!NMUV zuVCVZbRGe0?Q>yNlqRCqJxK2_Z$AIf7kqJT<5%4mg#%-2Qc!+ z;mvXQ`dcLbcb}v0g%Qer)B<;T=E1L0peeO-SPrq z>%8tMA)PA4pI?|O!P^rD)r&+0K59ze3M%g^04V-oA5uZH$%eTR*$(}Rk*Sm3Zng=~ zIq&bHCrs?|2cD>jST0Czja@GM4;SD8JbZUTXhMyBbHv&C<&>92x8l^ROod;?#<>@6 znF8a2e+vy*XDq-;0zijVT`X%YJ7qCC6lS))=#EYri?Nc^`3_y4+wY6U3^=P|PV`p>UXq_FYg zr}4+ID4itfnyt9r&vgFKB30e|lV^FxoY^!7Yv@=-FHX={RG~`LZM&celWW2mlPjk9 zNq^34p`P0+ANdA7M+cvh)nujWpVRU)6=ZsaS9W3MModXkn~l@V=fmlL8fPS5P)Vx+ z~Q zt^}OQb!{&x6p;p$P?}||44D;4qL3k3hR~plnIaLHLefB^NF~Ztq0B?5?8p=m3Yl3X zLx%kKyX7X?|q-=e(s^9wghP~0D+*cDFsnA9Q0=6{;O=A zbdPEkKcvNa+3BuuECHu8f`$CwBc~!c>X+!sn=1N z&H(f&5i-P~%FKHv{oMor8xq@~Rk~^YdULEXtx{y0^v%pFH^&1~qPZrnZN3MJF8K$L z))!P)FC}_vU;{4sMqanChjfKY1&r*BZE;8K%GNb-2&5{?KWc*7HnHHxrQn&u^#G)2 zWfT?1KHT>AXXPdpCaj@_&GJ^0C`FLF!aSxX+Oy`B4pYr?+&SSlM>r}Z{T-@+qeEW{ z_smdTnWk)tm!kHao~vTL1EiHY!_Rv6irML?T2 zm2oh4yRP}6dx+DS-1Yi=rMfGPoy2n4GZ$k*=!V+BH+A5dSrR>KXeo1_uPw|_fWusd zHeY4Q?HsA??IK7ROUi7i_EfNK+(E+J3C{=w1hvG(K8&ybxVv*1=pb4}!D+SvB1umr z&eg-zq7WbQpn*X^LxU0G#u;0^XLGSTOo_^cc%@s|%H6o^c0fAgdwKYzWuc5&1omsa z`lp6sx%J2#FM>m+#L4Q123^<6zk3`XzUrRUkxzsPpa=G7OFq235SJ3{@w~lx!F2*= zJ_h7T8=73kAimvFh883j9u8Y@k9LG-!(60fv&Ijkyhvc9UIQz;e%-84tWb#AiF5-C zg#e;Hj&^wCr_Ok^xk24d0)NOD9#U)`Nt@-a{S6k^TdfUYb&#HMFDqc6RjS3u@%#PB zw(|#2-*1N}!&WD2Rn@TGzCOq9k86RAb|*7W666}3EkPk6l2hD#Xsu+t9z*UUy~Rf! z=q8}jjg|grk1FxHLG<2!$)&~&H&=G}iyXL2F8iFHZm%|tVO6*&#=86{SOg6=QvU^D z)5If93`w9P1QBNkgi9{8l}+UNmMY`PJB4IA1g-KmyM4aVSObc(-5s}lt+EWgux~j( zo!Nemze^h$9rXw54-YjlCl^(#(BH4@4H6Ey-RmNjjE4`eL|$HN+m2k1(IVhyzKD)4 z#5#r{Ln+)h;nMuszfRB7StzD;XWdZOeeco-Y9Zp5rEJqb%Ed&q1rVa-zblp(ZgR3C z!wRPR_p`wRf1%0}vcI0J%w-=L)?D+}YT~{`bdbmGZ#kqAivU3$#6gA=IiA*_^}^l_ zey&_%rbN8B4qXbmZ1BEu0;nOt?d=V>@K25LzyQG;t)u4OL2MGPHu5jI!sUMi-L!VQ zh4n{RoAkgJTk^w!Ud1&j8XqyVoJc0>5|9O0e0_R*ZTcJ2SxJ+?@AXqBZb~#;*K^Wl ztj2A;#jSq$)2!s+0Y70U@y5i5TH%@-1bf*4XywkFIdrI&+VwftQ<_+-ad3ol#V=%M zFO4!g9}KI76)+EE0-KXKs}lws^{`^Px}oBsnHEpl3i^eea&d9lg?w|nS0m;Vpr$7a z$CbSTM|D1h*cq5_pD-;vKG5Y`nYFCwcDNYJsxX88A0DCCHXT?*xy=R#mS<~N!kU@^ ztSx|%bu4T?eL*F5XnV6T;_0AbQu+bZFOaDyHM_DXV-a5;0`c(@)ip{&TH4-qrWKS| zl_jC4rcT&W!@gznOWWPWIpI7iw{z!0$YEMtXqp4r!eU}kRs!|LL_^?0^NJFkcqX@& z{K}bacCi55O~j9~>H8DxHkJAgoH9CXR-9c^3Tc4HR99A3mO)o}MFkhmxQm!FWln7e z8E7jE1E0lh7~kwR`5Ic%G|j4>YB`24JL^(6!2JJn*SR~1AV48H>|4!6-iAp_i5J($1g-(km%>3l6oUjKqorh9yFI^dky<-V$B%rVntpr>^d zJiI5_>t64Q^?|Mh)rbp?x5n+{-lS2uh8)Ql`6Fdwsyyl(dRL$A;(Z$xMJgAW!Bk|W8rzzN%4I{TLzQv+3dBAdRY6FrWC0hw;+1; zz-8&#f|BP#{?K_%=Ph z|9kdn*0DNjd$GXz65dTU6pGmNS#bkxyK_=Q&HPb~t4+HOs0M6gnMX`E&yI8!Vnx$( zVK@BMpWE~!(wbjXIzRw`=AyQ)&H~mOGEN;VP^VNiTi4j~{SF?-Hcujp@Zn<(o`U~< zQM$!FuI|o~D|xe;B5&Ro2Om7OKwhGe&;A1sU4%#t@N(%Yxtn`oc3dCb<;X|#XIZTW z#!XL!Yf@9qRbG^bfO)(%CFf|WM{p2HZ=1`%c=FB@#At{~@t%;BB#u*q5?0s(l_v>a zMrdM_?dVm-TOS)=r?^!$CIr}G76)VA3wR7%?>)C*%gfabvSzM~Q(5;lO=r;Evvjl3 z%{zlH@EW0EhbCWD{H)VG%$(N$TY@p=-rfZi`SBQmXwTH|o9e#6cRUDU-*)gWPmX!r z*!Re7{QF!ped{zY@9hNp;(*dI$(+Fx?TLv(aQiQY&nv=?HoDx>2B!|Y6+)gFL5WT) zXZRSru3Nc1feM-uRX^n2gs4_9hB8$#oI_C&b_CY467PR1cdYUNu&LwxCyo$M2P53m zqm5eRHtE)6yV-*i8$xAjEjhPm)F>8F0E;p$4B zU1FfrUElBQea@s8eeZ>pd7x6cs5$#z>7h;*V@%9jI$r! z0I_7EJbPuoCVF z)`$J-xO>QX40o7fAowj>3P$N^|TLfX;p zA3!oy#W$>KI8Yyw=599c5MFw9=cVyu%nza#+(CHzUZKl(%S%fSh%rzEH%=U#sg1Q= zgsZJe4b!OdIsbb!)8Ti)LBkwwdb_rM!|cAdg_G@J2mo|9JAKZIhajlI0Vk`J+w88J z54V^dtdA+_SS#x8t-Yp$Yrs=m9)0@CiYhaw)WHcgsM{Q8hO5m;Pr28Gt($3ycieo| z@%1qoDt;P%)S<$=67qu{jNSkh&Owk*}AF1YhNs99Y+bc z@O(vsq#N5a;XgS!H4ja49E4Z;QvO&xTG&|@NNf`$){Dou#hPu?O#2#l=1BsWOjRSk z^p19Y$BT#Z^0O(W4vL#aP#L*#uv!2T+H+^F$vATiS_A@85oeEt|M*DF>qm z_s;wJmwsW+;gZYL&PORbsV&&J|E3SKz_}%rO0)7<|EXa8MB|JPXl&ejb}nHfDcQQvMUo^I|s z_(*r1v(Gx$N&(hB`j9|XcldkBIe9naomsWQuBpHMt zuAOKupzxwBvT|H((A@nNaz4!i7A{JpA)ry_oV$5CJl zY+ZuY{|WNd8i__rhz$t#!Z{Wp57`r*Jo(6bNJu|05?p}4+K4xIN+PUyJj_acS{SyR z{eE*9=AmU{ZUxc)mKIy-5n)#^&6HjzU>yn4%rMhQN zTWlbI@8f+5xw!o?2RA$K27^C7audt0YxT#Dg^H|S9MBTwsA(U6J;w>Ou($`>VG@e$ z4KKv${<(X1<-IoXxe>TD_R}JFr8I=7+`|`kX1D8L zN~lazY{+&H_MkhFYLq6_?s_Kwg{R^^&%ou(W1+`nq9|A=@Wkk)9$}+^*N|j-WE$Go zZ}?RL#J%a~PDrNS^?b5&$K&APXCg}b==3N1b7I21Sti{$49M6z8JSm4-*2!1V*6Yx z>ZEg7UMNFuSaOFUrn>Cbl8bo;>X@gv78F;!*Zp%iS98+e$Z<30;#CacVNPX7Pa@3Lt;V>?_fmLCeg-u8s}Xq%BAxCuVcRdo~2iTFd!KcJwKaOpkN+y@&mJQrSL zhl!g$glD^ei$Kt6e(Dq}P(vssRNW@!BGzJJ0%_^)@kzVm7kHeoTF-MEvI~Y`hV6MZmoH$C7hn5_M{4(Jl>0EjN)|Dc_QwwrQ_l@8l9TA^|cGzmjBRNlD3%HtCy1MF)p0bDnhJc-)Qd zw0Nh+KA_0%^K%#UWEP**a8Z~i-|y()$g*5&?gbsCa#=X1yj60ILjnWGOR`_AtayY8 z@H_xd#h9Io!^(QKoGTl!iDD2lSm{VIv0))vdSC;4_U{jEnMS6REa>gGLqVj{#z&Nh zk#5g=zR03#cyn@MU_N|?!H}$-cFyI|9GjVU11~i_)z+M<9B?KWst10k%}4?X@g6{a zIY6?H&5L(}Sq6Md&`?9QR(3b7+-lb;?h8CdI4pp#0R?JuuJCr7NPn{E1t@Ylj}{ew z>34M3U|D_IGnm+=j*9E{w>N4g_6?1U9u;nk^2uVFJK~GWVxrV4*}8OXEq4q39S*kl zJ0H0v!`=pf_YDE`nMusq!r>1yZ% z?*^lnnG$yo@VEJb0D$qQC?VRaQUe(Hl#e+|68fKo|02JsvC`O}B{HL(eCvXPf*!V+ z?AgLKsHJS{$8?P;`>WqhOY&CUcHcawTq$%?-pTZ!4zp5y3t>xBD;g?AJc1$#_49{?ZeA(u(}q{yV)=QUD-%f0BO&-(T` zp0@ggQgX3!nBl48kG2NHWE5`9zzwxOd2BzhqJ*giba!r3Lhle#qp#$e;9V z&M&Q;p7aa*=BBE$*yfbogwP6NcBW-}#F;<2n3%4Uv+#u+{|XnI&;go{0&7{30lh4I zT++?xVWGxX^q~F9VO>Ts^;2*51Qg|R{P{Y0hcAOb1#Y%^XgFx#Y;-4-YgV-k)U$GT zk1sbYjtl=gmm92X=Wp_Vr*T_7kga2cl|UQn5=hGm;0b|~Gb=G!Th9QMAg>y@6ksvR z^PQ$&_a{d*UZ><;HM=l$Q|3B$=a<->sms~qwrYZpz^JLErG_LNxLz;`2;Aq2*Go7= zB0ILV)YL5F;+p0R?8dL6T)K+96U%po3CY!Fx&$PCaKAQ1R}F zQ2z>U85)OnBt(#4VeAgFS+7L(=}d5U!UT8oUqPtq-`|pn$78kk;P(0M!`a?$pv4I8 zYT@q^vQ10dtO{d-6bCY;A}Dwe*&YbH&`cRGjJPnyxx)qtQ?tQ#odeHd%dpW~3~j!> zOZ0e)CkLERh*pB$} z3}J9+u>y9EqgRib-8(OLH|kKm<;B2x%v}D;79a2?Vbc-_svptdQp7=p z+kaPv6WJIiT@>Mu2wizIpqH#TlL!3oU@%Ifsfk8q$_&7LgxJRd#$~0TZz3KnEu;3; zwJSZ$K3Q`Q5pOgKVd`m(e)5IU=V#flZw(c#TW$?B*9l2;hSrsS?B1ZWz)3~(-M zp0wFLRzqtfE6A(IrFX(5<4?RX@z{QHLh8XA6b97{<_k^D6@C`WRR??&E$udnaVQXC>= zRswC8gQH_l)`}~yFnP|EvP+!OaoIRz`W}cJgDsg!(!|hWa-q=_R1pIU}~R z)r*9_5$2`p=5zGj#ksNCkoWn0CLgU?Z578Gi+-aSfBZNxaS?(Qzzp?hN75Byh-&-g z|1)%HoXgbwu}M6_2<7h2i~$TYHr61P$GrNX1}29rpz)@8jN0svmS1U;p7q%}^7{21 zQ_>4#O7b-=ztd>m^*+a9i1v`xr$M} zzy}X97`ATYD56BQP@nGyIp6Z*r@Y21Gdn1!@0{f@RfxD`C?IfIQ7Sj<^y}I)EWRH* z(r+CeDGPxu&S*wGu*8)Zg1GlQigEz{f2>R5r_}!SkvJD|T=$tuyE%{lJBV*YM`dgW z?UyQtct@2ANlLB&=#*8TL|sMUaW(st;FDZ+VhMy&Fpq1;jYD;0=Q0_oy`p=OL1gDd z*M3DVAJAdO0U48POyQQ>*|Iph%)ES44<=`R>FJV)KWTA)q`MH3D=Cp;KstccWZ;Zi z)wUy`B_R0CQN$2(e{2#2s)ZU2aUiFqV#a~se$LmR*#6Gm!G(Iv)~0o^Au(AC0SZ9b3`fHQ2`9;Z2PDr& zT@Nh4W$jqq`VIfZ@E-Yym>963HOR`6?QhBDRlmHt5Rz@xg!_AYvBx@$bZP-kCc{IM z*GsYV;c_kO^Vr)G1|7T4pI0QKLJBcFhbg%~q7Zd@X8t1xMaLn+e zHUd8AuGlb9*gU#SqnUhUXdaD>jNrRdTvo;md#Ms|6Bu^P@Qwfkz$UWu$~-<()Fn*I$c)KIT?rpg-)WBO*~UHlPhu_>?^_G!7(@WDYwzRdc-d)2Ft2GjIQp z5Y|cTkDsr4UI!Xaycj`(!3j5TyB_D6s3*HDt^{XQGue86O@0Eix45;nwO|G3N+bbC zxhmyD18H#)MW8Npi$=7kfYBTp>)VP~cM_Mry}Ztq~R`gn6S(J3cf*sQ_aQy51o- z35s}(^2lxpl7D37iH(9f*s@F4Ar=}ao&$Vx-Vi5dq!W_!fZV)|2ckoX;THBEt;ev> zC^P&0gB!uEZB6$1{+K&rsuY1q_^we*n6O^jIq4s38*y@3ix4ju*dW&zM+(ICZ{U-D z``&M2u7bH#8f4wr*eDcWfd07Eu17wAL}o=uWb~`L^18v(5{!{KmoI-Pt+;tJ3l6sr z6=z@FT$Giy3!GjTZ1HAm+0cpS#FI0^$x>6|Yd>X1MhibsA zg8eQ13Ssd0zxQKkHes7o2H!t&Vd0of{0u;k(G0o2?5}xIma{_8TbW-|Q+)$+AW+=C zmn;Xu4-@_8X8EsS(K5YrOVT@M@6$SBfsc`VNsNjgg@Va*v8Gt=MT}6`L6Fw!{G4jx zhx9NZAt9dX;O^vqP>3+!J94k(3gPV07lq&d-q((G^}s9(%wy--kMjyJ=#AA)aKO($ z))i6y9lc~dFPM|16&2d{`Bl2gw9BCFU`{;!qoB_Z;AX>@o~GXuTRjgHoo^f(e3`W6 zufKWbad7a5hkn!Ub|83%>D#s9Ej0ZupuZDEb0uY;f_8k8Cz_cq(TdOBsZ~%kE1M zhP=eOIXyj{P^ah|DiZJuOOoLa(yiT(l`!pu@m6BCL=CPZ=dxvSo;g*`pLX|;*``E~ zNmuJmjlCQ_5!TRGt>2W|&{w6O<)MJ`2B?69`_NaZ%I!$oMG~>)msgIsnd(|vi!tGi z&3@sDvIWk3&NbOtS#8%2DAGM1T>1#uDp-GyB(Gb;aVhCQbP%0{oS zCkOuIDbI3HcvxceY4`ikgs?Dc>KmhQ3}`xF7zi^EFcb3nexhc;9Lx{qd9dNH@82=4 zNd5t%c(ltm4`+ql{pFOFlDU;0f$u?bR+D_LzyAUhV2h2=JxyVcE6sidl$U&I`250Y zC>=soHQWY0fY*34Giyk+mlNgh*PP4pFvphdkENs{@e+Gk>vwyET2o)~EBNV9{ZRdr zuUfs~B~=SC;BdkA&L7@QTZ z4{*p2rfO${v>N;D*?#-G_?Os8)e@hK91>aqBaMSN!k|YXmP!3r8!=<#SiE?#R%ugX zV^K+o+Qmz<>T^6!j8fp1L}<1`c*zG`Te%QrD64p0gdwUn* z?@ zkSkNsf9wNNxdK*7^D!AN#5pG8*mkkOI{h=Ay*uXwC>K$@U!odkTOU8pinui2U9n5{ zTwc8e-jA#t@KUL0H6qjy*HwL$^S3nmLxkqa~SfnH2icPbgYZ}H}1x?4bYS0Xs21$N=dQePo$0NDOr|-!#?l)t}GS<=xqbQTqafp*@ z@MQU;YYnFE39PNJXN}tvwiIceB#%ko82>;S8p++k2)I1N%vMRfHC^k7ugVR$xd z&pOQO)nV6iY zVwCG4iIV9{yX<*j{D3e8Wo1{vmlHuQ;@h@u6Ni-Tu7W4X60#kC@mR!Mx9~j&ax6bu ztB2||0NIJSufv^p?AwOR1h}(bN}X|V@CgqOZ%N3-4J2H79i51v8!p-!8Ve~qUh70& zyQUXzDz@dX&-U{lpRG-FP`RCNF9`Km{_pucMH;}5arjoW09)W%DJd>EycB?R-5Qsy zfEf_E-X!81*~39p?RfdA{udr8@Y%bJ>$h6oeF-a<@RIl^?BICez#xEzxt-l*Qqv$a zu#lkjQ0Qs*|7f(ubRVA3`@)+`6(_zj_BUs9BlXocKVOODYa;yQC{BL&?%2RLqA4FB z9}57JQ&34s0Oh)jYd;T$9q?Eg$>y44cx_K?eW_NMXS}MxP;=Ro%}IPruhK@t@o7nA zOZ8=cwu@MGXcxJ%AF`tiB0lM0fTej&xrVNiTm;~FN%k|>oo9cnCk*Fj&xDC27#hqn zM3r8-a>WOyU0iaqcK=d2;eU39%d@Y$!gbIOO#nzp9GE0DPoNSWf2-Gpdc^^SD8_q6 z{kpOs`;ssiY#nc)0(PL~jvs|t^-U?;RY>9xc0D`rqvP5ELDWBqvCW_wiQs>~r6gj-?(E*3JZMO`(h zrC9EDb#+Ihszl){{1veotEy-#{HHeNnH5mYG) zjgt&up0^aWmC#KDPF6r6PCW7hEvGl*qi1fmHRh7lL5Sl+~gSN`dQ!V5GI zkFdK6Zk07E+Fj=6F|hm;WAR;s-f9sc|3VAB!O42y46M?L)*aK5A}oyG-kxsZi84HU z9H)Z~({u!@N;y)U7+ifD8d*gP_+Bkk`S{7UVWE^=QR#9n!OeTrRF|iFKIR$px5yi6 zS=k~zIjb_cY~Ie*Zv#K|xrp4@=aFlwr9Kr`{{2B7tC0a#L-UvKr;H8bzJc52t*oixubw^G--miErk7tMkjiLr?Wx zR;$NVJ-HP0xaQ=*sK;Z=nPnCtGfuCc3I1@@x>WLjJ#cqTnqpGZfQ>xAVZBS8<7(S< zV~_0S{be5L8+ACVKA%1&`#$QAt}{u8gjNNs){e;IC-Q~`L~C!Vsp9{ksNF&&zyN*n>;zU9`%6U2h-W{IqVLd?o8ZpwIc^FFwEh=5R$fVg0cC z7w6SRzR4LJ)Ues$ Date: Mon, 6 Aug 2018 11:29:40 -0700 Subject: [PATCH 22/22] invertlog OK --- lib/matplotlib/axes/_axes.py | 101 +++++++++++++++++++------ lib/matplotlib/axes/_secondary_axes.py | 61 ++++++++++++++- 2 files changed, 134 insertions(+), 28 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 96e3cba84076..ca4c0f794406 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -640,13 +640,19 @@ def indicate_inset_zoom(self, inset_ax, **kwargs): return rectpatch, connects - def secondary_xaxis(self, location, *, conversion=None, **kwargs): + def secondary_xaxis(self, location, *, conversion=None, + otherargs=None, **kwargs): """ Add a second x-axis to this axes. For example if we want to have a second scale for the data plotted on the xaxis. + Warnings + -------- + + This method is experimental as of 3.1, and the API may change. + Parameters ---------- location : string or scalar @@ -655,14 +661,26 @@ def secondary_xaxis(self, location, *, conversion=None, **kwargs): on the axes to put the new axes (0 being the bottom, and 1.0 being the top.) - conversion : tuple of floats or function - conversion between the parent xaxis values and the secondary xaxis - values. If a tuple of floats, the floats are polynomial - co-efficients, with the first entry the highest exponent's - co-efficient (i.e. [2, 3, 1] is the same as - ``xnew = 2 x**2 + 3 * x + 1``). If a function is specified it - should accept a float as input and return a float as the - result. + conversion : scalar, two-tuple of scalars, string, or Transform + If a scalar or a two-tuple of scalar, the secondary axis is converted + via a linear conversion with slope given by the first element + and offset given by the second. i.e. ``conversion = [2, 1]`` + for a paretn axis between 0 and 1 gives a secondary axis between + 1 and 3. + + If a string, if can be one of "linear", "power", and "inverted". + If "linear", the value of ``otherargs`` should be a float or + two-tuple as above. If "inverted" the values in the secondary axis + are inverted and multiplied by the value supplied by ``oterargs``. + If "power", then the original values are transformed by + ``newx = otherargs[1] * oldx ** otherargs[0]``. + + Finally, the user can supply a subclass of `.transforms.Transform` + to arbitrarily transform between the parent axes and the + secondary axes. + See :doc:`/gallery/subplots_axes_and_figures/secondary_axis.py` + for an example of making such a transform. + Other Parameters ---------------- @@ -671,41 +689,73 @@ def secondary_xaxis(self, location, *, conversion=None, **kwargs): Returns ------- - ax : `~matplotlib.axes.Axes` (??) + ax : axes._secondary_axes.Secondary_Axis + + Examples + -------- + + Add a secondary axes that converts degrees to radians. + + .. plot:: + + fig, ax = plt.suplots() + ax.loglog(range(1, 360, 5), range(1, 360, 5)) + secax = ax.secondary_xaxis('top', conversion='inverted', + otherargs=1.) + secax.set_xscale('log') """ if (location in ['top', 'bottom'] or isinstance(location, Number)): secondary_ax = Secondary_Axis(self, 'x', location, - conversion, **kwargs) + conversion, otherargs=otherargs, + **kwargs) self.add_child_axes(secondary_ax) return secondary_ax else: raise ValueError('secondary_xaxis location must be either ' '"top" or "bottom"') - def secondary_yaxis(self, location, *, conversion=None, **kwargs): + def secondary_yaxis(self, location, *, conversion=None, + otherargs=None, **kwargs): """ Add a second y-axis to this axes. For example if we want to have a second scale for the data plotted on - the yaxis. + the xaxis. + + Warnings + -------- + + This method is experimental as of 3.1, and the API may change. Parameters ---------- location : string or scalar The position to put the secondary axis. Strings can be 'left' or 'right', scalar can be a float indicating the relative position - on the axes to put the new axes (0 being the bottom, and 1.0 being - the top.) + on the axes to put the new axes (0 being the left, and 1.0 being + the right.) + + conversion : scalar, two-tuple of scalars, string, or Transform + If a scalar or a two-tuple of scalar, the secondary axis is converted + via a linear conversion with slope given by the first element + and offset given by the second. i.e. ``conversion = [2, 1]`` + for a paretn axis between 0 and 1 gives a secondary axis between + 1 and 3. + + If a string, if can be one of "linear", "power", and "inverted". + If "linear", the value of ``otherargs`` should be a float or + two-tuple as above. If "inverted" the values in the secondary axis + are inverted and multiplied by the value supplied by ``oterargs``. + If "power", then the original values are transformed by + ``newy = otherargs[1] * oldy ** otherargs[0]``. + + Finally, the user can supply a subclass of `.transforms.Transform` + to arbitrarily transform between the parent axes and the + secondary axes. + See :doc:`/gallery/subplots_axes_and_figures/secondary_axis.py` + for an example of making such a transform. - conversion : tuple of floats or function - conversion between the parent xaxis values and the secondary xaxis - values. If a tuple of floats, the floats are polynomial - co-efficients, with the first entry the highest exponent's - co-efficient (i.e. [2, 3, 1] is the same as - ``xnew = 2 x**2 + 3 * x + 1``). If a function is specified it - should accept a float as input and return a float as the - result. Other Parameters ---------------- @@ -714,12 +764,13 @@ def secondary_yaxis(self, location, *, conversion=None, **kwargs): Returns ------- - ax : `~matplotlib.axes.Axes` (??) + ax : axes._secondary_axes.Secondary_Axis """ if location in ['left', 'right'] or isinstance(location, Number): secondary_ax = Secondary_Axis(self, 'y', location, - conversion, **kwargs) + conversion, otherargs=otherargs, + **kwargs) self.add_child_axes(secondary_ax) return secondary_ax else: diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 92ac0fdcaef7..070dce4a238a 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -18,8 +18,12 @@ FuncFormatter, ScalarFormatter, AutoMinorLocator, + LogLocator, + LogFormatterSciNotation ) +from matplotlib.scale import Log10Transform + def _make_secondary_locator(rect, parent): """ @@ -239,16 +243,24 @@ def set_conversion(self, conversion, otherargs=None): if self._orientation == 'x': set_scale = self.set_xscale + parent_scale = self._parent.get_xscale() else: set_scale = self.set_yscale + parent_scale = self._parent.get_yscale() + if parent_scale == 'log': + defscale = 'arbitrarylog' + else: + defscale = 'arbitrary' + # make the _convert function... if isinstance(conversion, mtransforms.Transform): self._convert = conversion - set_scale('arbitrary', transform=conversion.inverted()) + set_scale(defscale, transform=conversion.inverted()) elif isinstance(conversion, str): self._convert = _parse_conversion(conversion, otherargs) - set_scale('arbitrary', transform=self._convert.inverted()) + print(self._convert) + set_scale(defscale, transform=self._convert.inverted()) else: # linear conversion with offset if isinstance(conversion, numbers.Number): @@ -263,7 +275,11 @@ def set_conversion(self, conversion, otherargs=None): offset=conversion[1]) self._convert = conversion # this will track log/non log so long as the user sets... - set_scale(self._parent.get_xscale()) + if self._orientation == 'x': + set_scale(self._parent.get_xscale()) + else: + set_scale(self._parent.get_yscale()) + def draw(self, renderer=None, inframe=False): """ @@ -287,14 +303,18 @@ def _set_lims(self): if self._orientation == 'y': lims = self._parent.get_ylim() set_lim = self.set_ylim + print('parent lims', lims) order = lims[0] < lims[1] lims = self._convert.transform(lims) neworder = lims[0] < lims[1] + print(order, neworder) if neworder != order: # flip because the transform will take care of the flipping.. lims = lims[::-1] set_lim(lims) + print('new lims', lims) + def get_tightbbox(self, renderer, call_axes_locator=True): """ Return the tight bounding box of the axes. @@ -478,8 +498,10 @@ def __init__(self, fac): self._fac = fac def transform_non_affine(self, values): + print('values', values) with np.errstate(divide='ignore', invalid='ignore'): q = self._fac / values + print('q', q) return q def inverted(self): @@ -564,3 +586,36 @@ def set_default_locators_and_formatters(self, axis): axis.set_minor_formatter(NullFormatter()) mscale.register_scale(ArbitraryScale) + +class ArbitraryLogScale(mscale.ScaleBase): + + name = 'arbitrarylog' + + def __init__(self, axis, transform=mtransforms.IdentityTransform()): + """ + TODO + """ + self._transform = transform + + def get_transform(self): + """ + The transform for linear scaling is just the + :class:`~matplotlib.transforms.IdentityTransform`. + """ + return self._transform + Log10Transform() + + def set_default_locators_and_formatters(self, axis): + """ + Set the locators and formatters to reasonable defaults for + linear scaling. + """ + self.base = 10 + self.subs = None + axis.set_major_locator(LogLocator(self.base)) + axis.set_major_formatter(LogFormatterSciNotation(self.base)) + axis.set_minor_locator(LogLocator(self.base, self.subs)) + axis.set_minor_formatter( + LogFormatterSciNotation(self.base, + labelOnlyBase=(self.subs is not None))) + +mscale.register_scale(ArbitraryLogScale)