diff --git a/aospy/calc.py b/aospy/calc.py index e8f4e6c..aa23eb6 100644 --- a/aospy/calc.py +++ b/aospy/calc.py @@ -82,7 +82,9 @@ def _file_name(self, dtype_out_time, extension='nc'): dtype_vert=self.dtype_out_vert) in_lbl = utils.io.data_in_label(self.intvl_in, self.dtype_in_time, self.dtype_in_vert) - yr_lbl = utils.io.yr_label((self.start_date.year, self.end_date.year)) + start_year = utils.times.infer_year(self.start_date) + end_year = utils.times.infer_year(self.end_date) + yr_lbl = utils.io.yr_label((start_year, end_year)) return '.'.join( [self.name, out_lbl, in_lbl, self.model.name, self.run.name, yr_lbl, extension] diff --git a/aospy/data_loader.py b/aospy/data_loader.py index 3962eb9..719214d 100644 --- a/aospy/data_loader.py +++ b/aospy/data_loader.py @@ -1,7 +1,6 @@ """aospy DataLoader objects""" import logging import os -import warnings import numpy as np import xarray as xr @@ -16,7 +15,7 @@ def _preprocess_and_rename_grid_attrs(func, **kwargs): - """Call a custom preprocessing method first then rename grid attrs + """Call a custom preprocessing method first then rename grid attrs. This wrapper is needed to generate a single function to pass to the ``preprocesss`` of xr.open_mfdataset. It makes sure that the @@ -100,7 +99,7 @@ def set_grid_attrs_as_coords(ds): def _maybe_cast_to_float64(da): - """Cast DataArrays to np.float64 if they are of type np.float32 + """Cast DataArrays to np.float64 if they are of type np.float32. Parameters ---------- @@ -157,16 +156,14 @@ def _sel_var(ds, var, upcast_float32=True): def _prep_time_data(ds): - """Prepare time coord. information in Dataset for use in aospy. + """Prepare time coordinate information in Dataset for use in aospy. - 1. Edit units attribute of time variable if it contains a Timestamp invalid - date - 2. If the Dataset contains a time bounds coordinate, add attributes + 1. If the Dataset contains a time bounds coordinate, add attributes representing the true beginning and end dates of the time interval used to construct the Dataset - 3. If the Dataset contains a time bounds coordinate, overwrite the time + 2. If the Dataset contains a time bounds coordinate, overwrite the time coordinate values with the averages of the time bounds at each timestep - 4. Decode the times into np.datetime64 objects for time indexing + 3. Decode the times into np.datetime64 objects for time indexing Parameters ---------- @@ -176,11 +173,10 @@ def _prep_time_data(ds): Returns ------- - Dataset, int, int - The processed Dataset and minimum and maximum years in the loaded data + Dataset + The processed Dataset """ ds = times.ensure_time_as_index(ds) - ds, min_year, max_year = times.numpy_datetime_workaround_encode_cf(ds) if TIME_BOUNDS_STR in ds: ds = times.ensure_time_avg_has_cf_metadata(ds) ds[TIME_STR] = times.average_time_bounds(ds) @@ -189,10 +185,10 @@ def _prep_time_data(ds): "values in time, even though this may not be " "the case") ds = times.add_uniform_time_weights(ds) - with warnings.catch_warnings(record=True): + with xr.set_options(enable_cftimeindex=True): ds = xr.decode_cf(ds, decode_times=True, decode_coords=False, mask_and_scale=True) - return ds, min_year, max_year + return ds def _load_data_from_disk(file_set, preprocess_func=lambda ds: ds, @@ -239,7 +235,7 @@ def apply_preload_user_commands(file_set, cmd=io.dmget): def _setattr_default(obj, attr, value, default): - """Set an attribute of an object to a value or default value""" + """Set an attribute of an object to a value or default value.""" if value is None: setattr(obj, attr, default) else: @@ -247,7 +243,7 @@ def _setattr_default(obj, attr, value, default): class DataLoader(object): - """A fundamental DataLoader object""" + """A fundamental DataLoader object.""" def load_variable(self, var=None, start_date=None, end_date=None, time_offset=None, **DataAttrs): """Load a DataArray for requested variable and time range. @@ -280,21 +276,19 @@ def load_variable(self, var=None, start_date=None, end_date=None, coords=self.coords, start_date=start_date, end_date=end_date, time_offset=time_offset, **DataAttrs ) - - ds, min_year, max_year = _prep_time_data(ds) + ds = _prep_time_data(ds) + start_date = times.maybe_convert_to_index_date_type( + ds.indexes[TIME_STR], start_date) + end_date = times.maybe_convert_to_index_date_type( + ds.indexes[TIME_STR], end_date) ds = set_grid_attrs_as_coords(ds) da = _sel_var(ds, var, self.upcast_float32) da = self._maybe_apply_time_shift(da, time_offset, **DataAttrs) - - start_date_xarray = times.numpy_datetime_range_workaround( - start_date, min_year, max_year) - end_date_xarray = start_date_xarray + (end_date - start_date) - return times.sel_time(da, np.datetime64(start_date_xarray), - np.datetime64(end_date_xarray)).load() + return times.sel_time(da, start_date, end_date).load() def recursively_compute_variable(self, var, start_date=None, end_date=None, time_offset=None, **DataAttrs): - """Compute a variable recursively, loading data where needed + """Compute a variable recursively, loading data where needed. An obvious requirement here is that the variable must eventually be able to be expressed in terms of model-native quantities; otherwise the @@ -344,7 +338,7 @@ def _generate_file_set(self, var=None, start_date=None, end_date=None, class DictDataLoader(DataLoader): - """A DataLoader that uses a dict mapping lists of files to string tags + """A DataLoader that uses a dict mapping lists of files to string tags. This is the simplest DataLoader; it is useful for instance if one is dealing with raw model history files, which tend to group all variables @@ -393,7 +387,7 @@ class DictDataLoader(DataLoader): """ def __init__(self, file_map=None, upcast_float32=True, data_vars='minimal', coords='minimal', preprocess_func=lambda ds, **kwargs: ds): - """Create a new DictDataLoader""" + """Create a new DictDataLoader.""" self.file_map = file_map self.upcast_float32 = upcast_float32 self.data_vars = data_vars @@ -412,7 +406,7 @@ def _generate_file_set(self, var=None, start_date=None, end_date=None, class NestedDictDataLoader(DataLoader): - """DataLoader that uses a nested dictionary mapping to load files + """DataLoader that uses a nested dictionary mapping to load files. This is the most flexible existing type of DataLoader; it allows for the specification of different sets of files for different variables. The @@ -474,7 +468,7 @@ def _generate_file_set(self, var=None, start_date=None, end_date=None, class GFDLDataLoader(DataLoader): - """DataLoader for NOAA GFDL model output + """DataLoader for NOAA GFDL model output. This is an example of a domain-specific custom DataLoader, designed specifically for finding files output by the Geophysical Fluid Dynamics @@ -609,10 +603,13 @@ def _input_data_paths_gfdl(self, name, start_date, end_date, domain, else: subdir = os.path.join(intvl_in, dur_str) direc = os.path.join(self.data_direc, domain, dtype_lbl, subdir) + data_start_year = times.infer_year(self.data_start_date) + start_year = times.infer_year(start_date) + end_year = times.infer_year(end_date) files = [os.path.join(direc, io.data_name_gfdl( name, domain, dtype, intvl_in, year, intvl_out, - self.data_start_date.year, self.data_dur)) - for year in range(start_date.year, end_date.year + 1)] + data_start_year, self.data_dur)) + for year in range(start_year, end_year + 1)] files = list(set(files)) files.sort() return files diff --git a/aospy/examples/example_obj_lib.py b/aospy/examples/example_obj_lib.py index a71f934..91d3f6d 100644 --- a/aospy/examples/example_obj_lib.py +++ b/aospy/examples/example_obj_lib.py @@ -1,5 +1,5 @@ """Sample aospy object library using the included example data.""" -import datetime +from datetime import datetime import os import aospy @@ -17,8 +17,8 @@ description=( 'Control simulation of the idealized moist model' ), - default_start_date=datetime.datetime(4, 1, 1), - default_end_date=datetime.datetime(6, 12, 31), + default_start_date=datetime(4, 1, 1), + default_end_date=datetime(6, 12, 31), data_loader=DictDataLoader(_file_map) ) diff --git a/aospy/examples/tutorial.ipynb b/aospy/examples/tutorial.ipynb index 6d234ce..c703873 100644 --- a/aospy/examples/tutorial.ipynb +++ b/aospy/examples/tutorial.ipynb @@ -21,9 +21,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import os # Python built-in package for working with the operating system\n", @@ -56,10 +54,11 @@ " * nv (nv) float64 1.0 2.0\n", " * time (time) float64 1.111e+03 1.139e+03 1.17e+03 1.2e+03 ...\n", "Data variables:\n", - " condensation_rain (time, lat, lon) float64 5.768e-06 5.784e-06 ...\n", - " convection_rain (time, lat, lon) float64 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ...\n", - " time_bounds (time, nv) float64 1.095e+03 1.126e+03 1.126e+03 ...\n", - " average_DT (time) float64 31.0 28.0 31.0 30.0 31.0 30.0 31.0 ..." + " condensation_rain (time, lat, lon) float32 dask.array\n", + " convection_rain (time, lat, lon) float32 dask.array\n", + " time_bounds (time, nv) float64 dask.array\n", + " average_DT (time) float64 dask.array\n", + " zsurf (time, lat, lon) float32 dask.array" ] }, "execution_count": 2, @@ -107,9 +106,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from aospy.data_loader import DictDataLoader\n", @@ -129,9 +126,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from aospy import Run\n", @@ -159,9 +154,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from aospy import Model\n", @@ -188,9 +181,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from aospy import Proj\n", @@ -227,9 +218,7 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from aospy import Var\n", @@ -262,9 +251,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def total_precip(condensation_rain, convection_rain):\n", @@ -315,9 +302,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from aospy import Region\n", @@ -363,9 +348,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from aospy.examples import example_obj_lib as lib\n", @@ -401,9 +384,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "calc_exec_options = dict(prompt_verify=False, parallelize=False,\n", @@ -426,56 +407,46 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:root:Getting input data: Var instance \"precip_largescale\" (Thu Mar 30 08:49:11 2017)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:389: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " result = decode_cf_datetime(example_value, units, calendar)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:408: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " calendar=self.calendar)\n", - "INFO:root:Getting input data: Var instance \"precip_convective\" (Thu Mar 30 08:49:11 2017)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:389: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " result = decode_cf_datetime(example_value, units, calendar)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:408: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " calendar=self.calendar)\n", - "INFO:root:Computing timeseries for 0004-01-01 00:00:00 -- 0006-12-31 00:00:00.\n", - "INFO:root:Applying desired time-reduction methods. (Thu Mar 30 08:49:11 2017)\n", - "INFO:root:Writing desired gridded outputs to disk.\n", - "INFO:root:\texample-output/example_proj/example_model/example_run/precip_conv_frac/precip_conv_frac.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", - "INFO:root:\texample-output/example_proj/example_model/example_run/precip_conv_frac/precip_conv_frac.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", - "INFO:root:Getting input data: Var instance \"precip_largescale\" (Thu Mar 30 08:49:12 2017)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:389: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " result = decode_cf_datetime(example_value, units, calendar)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:408: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " calendar=self.calendar)\n", - "INFO:root:Getting input data: Var instance \"precip_convective\" (Thu Mar 30 08:49:12 2017)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:389: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " result = decode_cf_datetime(example_value, units, calendar)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:408: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " calendar=self.calendar)\n", + "INFO:root:Getting input data: Var instance \"precip_largescale\" (Sun May 20 09:40:28 2018)\n", + "WARNING:root:Datapoints were stored using the np.float32 datatype.For accurate reduction operations using bottleneck, datapoints are being cast to the np.float64 datatype. For more information see: https://github.com/pydata/xarray/issues/1346\n", + "INFO:root:Getting input data: Var instance \"precip_convective\" (Sun May 20 09:40:28 2018)\n", + "WARNING:root:Datapoints were stored using the np.float32 datatype.For accurate reduction operations using bottleneck, datapoints are being cast to the np.float64 datatype. For more information see: https://github.com/pydata/xarray/issues/1346\n", "INFO:root:Computing timeseries for 0004-01-01 00:00:00 -- 0006-12-31 00:00:00.\n", - "INFO:root:Applying desired time-reduction methods. (Thu Mar 30 08:49:13 2017)\n", + "INFO:root:Applying desired time-reduction methods. (Sun May 20 09:40:29 2018)\n", "INFO:root:Writing desired gridded outputs to disk.\n", "INFO:root:\texample-output/example_proj/example_model/example_run/precip_total/precip_total.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", + "//anaconda/envs/aospy_dev/lib/python3.6/_collections_abc.py:743: FutureWarning: iteration over an xarray.Dataset will change in xarray v0.11 to only include data variables, not coordinates. Iterate over the Dataset.variables property instead to preserve existing behavior in a forwards compatible manner.\n", + " for key in self._mapping:\n", "INFO:root:\texample-output/example_proj/example_model/example_run/precip_total/precip_total.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", - "INFO:root:Getting input data: Var instance \"precip_convective\" (Thu Mar 30 08:49:13 2017)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:389: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " result = decode_cf_datetime(example_value, units, calendar)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:408: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " calendar=self.calendar)\n", + "INFO:root:Getting input data: Var instance \"precip_largescale\" (Sun May 20 09:40:29 2018)\n", + "WARNING:root:Datapoints were stored using the np.float32 datatype.For accurate reduction operations using bottleneck, datapoints are being cast to the np.float64 datatype. For more information see: https://github.com/pydata/xarray/issues/1346\n", + "INFO:root:Getting input data: Var instance \"precip_convective\" (Sun May 20 09:40:29 2018)\n", + "WARNING:root:Datapoints were stored using the np.float32 datatype.For accurate reduction operations using bottleneck, datapoints are being cast to the np.float64 datatype. For more information see: https://github.com/pydata/xarray/issues/1346\n", "INFO:root:Computing timeseries for 0004-01-01 00:00:00 -- 0006-12-31 00:00:00.\n", - "INFO:root:Applying desired time-reduction methods. (Thu Mar 30 08:49:13 2017)\n", + "INFO:root:Applying desired time-reduction methods. (Sun May 20 09:40:29 2018)\n", "INFO:root:Writing desired gridded outputs to disk.\n", - "INFO:root:\texample-output/example_proj/example_model/example_run/precip_convective/precip_convective.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", - "INFO:root:\texample-output/example_proj/example_model/example_run/precip_convective/precip_convective.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", - "INFO:root:Getting input data: Var instance \"precip_largescale\" (Thu Mar 30 08:49:13 2017)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:389: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " result = decode_cf_datetime(example_value, units, calendar)\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/xarray/conventions.py:408: RuntimeWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using dummy netCDF4.datetime objects instead, reason: dates out of range\n", - " calendar=self.calendar)\n", + "INFO:root:\texample-output/example_proj/example_model/example_run/precip_conv_frac/precip_conv_frac.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", + "//anaconda/envs/aospy_dev/lib/python3.6/_collections_abc.py:743: FutureWarning: iteration over an xarray.Dataset will change in xarray v0.11 to only include data variables, not coordinates. Iterate over the Dataset.variables property instead to preserve existing behavior in a forwards compatible manner.\n", + " for key in self._mapping:\n", + "INFO:root:\texample-output/example_proj/example_model/example_run/precip_conv_frac/precip_conv_frac.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", + "INFO:root:Getting input data: Var instance \"precip_largescale\" (Sun May 20 09:40:29 2018)\n", + "WARNING:root:Datapoints were stored using the np.float32 datatype.For accurate reduction operations using bottleneck, datapoints are being cast to the np.float64 datatype. For more information see: https://github.com/pydata/xarray/issues/1346\n", "INFO:root:Computing timeseries for 0004-01-01 00:00:00 -- 0006-12-31 00:00:00.\n", - "INFO:root:Applying desired time-reduction methods. (Thu Mar 30 08:49:13 2017)\n", + "INFO:root:Applying desired time-reduction methods. (Sun May 20 09:40:29 2018)\n", "INFO:root:Writing desired gridded outputs to disk.\n", "INFO:root:\texample-output/example_proj/example_model/example_run/precip_largescale/precip_largescale.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", - "INFO:root:\texample-output/example_proj/example_model/example_run/precip_largescale/precip_largescale.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n" + "//anaconda/envs/aospy_dev/lib/python3.6/_collections_abc.py:743: FutureWarning: iteration over an xarray.Dataset will change in xarray v0.11 to only include data variables, not coordinates. Iterate over the Dataset.variables property instead to preserve existing behavior in a forwards compatible manner.\n", + " for key in self._mapping:\n", + "INFO:root:\texample-output/example_proj/example_model/example_run/precip_largescale/precip_largescale.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", + "INFO:root:Getting input data: Var instance \"precip_convective\" (Sun May 20 09:40:29 2018)\n", + "WARNING:root:Datapoints were stored using the np.float32 datatype.For accurate reduction operations using bottleneck, datapoints are being cast to the np.float64 datatype. For more information see: https://github.com/pydata/xarray/issues/1346\n", + "INFO:root:Computing timeseries for 0004-01-01 00:00:00 -- 0006-12-31 00:00:00.\n", + "INFO:root:Applying desired time-reduction methods. (Sun May 20 09:40:30 2018)\n", + "INFO:root:Writing desired gridded outputs to disk.\n", + "INFO:root:\texample-output/example_proj/example_model/example_run/precip_convective/precip_convective.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n", + "//anaconda/envs/aospy_dev/lib/python3.6/_collections_abc.py:743: FutureWarning: iteration over an xarray.Dataset will change in xarray v0.11 to only include data variables, not coordinates. Iterate over the Dataset.variables property instead to preserve existing behavior in a forwards compatible manner.\n", + " for key in self._mapping:\n", + "INFO:root:\texample-output/example_proj/example_model/example_run/precip_convective/precip_convective.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc\n" ] } ], @@ -511,10 +482,10 @@ { "data": { "text/plain": [ - "[Calc object: precip_conv_frac, example_proj, example_model, example_run,\n", - " Calc object: precip_total, example_proj, example_model, example_run,\n", - " Calc object: precip_convective, example_proj, example_model, example_run,\n", - " Calc object: precip_largescale, example_proj, example_model, example_run]" + "[,\n", + " ,\n", + " ,\n", + " ]" ] }, "execution_count": 13, @@ -541,8 +512,8 @@ { "data": { "text/plain": [ - "{'av': 'example-output/example_proj/example_model/example_run/precip_conv_frac/precip_conv_frac.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc',\n", - " 'reg.av': 'example-output/example_proj/example_model/example_run/precip_conv_frac/precip_conv_frac.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc'}" + "{'av': 'example-output/example_proj/example_model/example_run/precip_total/precip_total.ann.av.from_monthly_ts.example_model.example_run.0004-0006.nc',\n", + " 'reg.av': 'example-output/example_proj/example_model/example_run/precip_total/precip_total.ann.reg.av.from_monthly_ts.example_model.example_run.0004-0006.nc'}" ] }, "execution_count": 14, @@ -570,42 +541,42 @@ "data": { "text/plain": [ "{'av': \n", - " array([[ 5.825489e-11, 5.568656e-06, 3.746100e-05, ..., 0.000000e+00,\n", - " 1.773260e-11, 2.142449e-11],\n", - " [ 1.718738e-03, 1.864318e-03, 1.876380e-03, ..., 1.584265e-03,\n", - " 1.650416e-03, 1.634984e-03],\n", - " [ 4.336546e-03, 3.980294e-03, 3.332168e-03, ..., 5.023761e-03,\n", - " 4.934178e-03, 4.846593e-03],\n", + " array([[ 5.158033e-06, 5.146671e-06, 5.135556e-06, ..., 5.176134e-06,\n", + " 5.170886e-06, 5.165684e-06],\n", + " [ 4.978642e-06, 5.003572e-06, 5.029685e-06, ..., 4.947511e-06,\n", + " 4.960262e-06, 4.970894e-06],\n", + " [ 5.608857e-06, 5.626041e-06, 5.659296e-06, ..., 5.449484e-06,\n", + " 5.514129e-06, 5.578681e-06],\n", " ..., \n", - " [ 4.826577e-03, 5.116871e-03, 5.306013e-03, ..., 3.952671e-03,\n", - " 3.857220e-03, 4.159514e-03],\n", - " [ 3.559006e-04, 4.071099e-04, 4.688400e-04, ..., 3.086923e-04,\n", - " 3.293162e-04, 3.424217e-04],\n", - " [ 2.896832e-11, 4.326204e-11, 2.680294e-11, ..., 4.254056e-11,\n", - " 7.040103e-11, 9.109586e-11]])\n", + " [ 5.284707e-06, 5.260350e-06, 5.241916e-06, ..., 5.429388e-06,\n", + " 5.373469e-06, 5.316535e-06],\n", + " [ 4.975340e-06, 4.982203e-06, 4.983765e-06, ..., 4.975950e-06,\n", + " 4.970845e-06, 4.972452e-06],\n", + " [ 5.357783e-06, 5.319537e-06, 5.285620e-06, ..., 5.450081e-06,\n", + " 5.420615e-06, 5.394608e-06]])\n", " Coordinates:\n", " * lon (lon) float64 0.0 2.812 5.625 8.438 11.25 14.06 ...\n", " * lat (lat) float64 -87.86 -85.1 -82.31 -79.53 -76.74 ...\n", - " raw_data_start_date datetime64[ns] 1678-01-01\n", - " raw_data_end_date datetime64[ns] 1681-01-01\n", - " subset_start_date datetime64[ns] 1678-01-01\n", - " subset_end_date datetime64[ns] 1680-12-31\n", + " zsurf (lat, lon) float32 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ...\n", + " raw_data_start_date object 0004-01-01 00:00:00\n", + " raw_data_end_date object 0007-01-01 00:00:00\n", + " subset_start_date object 0004-01-01 00:00:00\n", + " subset_end_date object 0006-12-31 00:00:00\n", " sfc_area (lat, lon) float64 3.553e+09 3.553e+09 3.553e+09 ...\n", - " land_mask (lat, lon) float64 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ...,\n", - " 'reg.av': OrderedDict([('globe', \n", - " array(0.5979886366730033)\n", - " Coordinates:\n", - " raw_data_start_date datetime64[ns] 1678-01-01\n", - " raw_data_end_date datetime64[ns] 1681-01-01\n", - " subset_start_date datetime64[ns] 1678-01-01\n", - " subset_end_date datetime64[ns] 1680-12-31),\n", - " ('tropics', \n", - " array(0.8080250991579639)\n", - " Coordinates:\n", - " raw_data_start_date datetime64[ns] 1678-01-01\n", - " raw_data_end_date datetime64[ns] 1681-01-01\n", - " subset_start_date datetime64[ns] 1678-01-01\n", - " subset_end_date datetime64[ns] 1680-12-31)])}" + " land_mask (lat, lon) float64 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ...\n", + " Attributes:\n", + " units: \n", + " description: Sum of convective and large-scale precipitation.\\n\\n Par...,\n", + " 'reg.av': \n", + " Dimensions: ()\n", + " Coordinates:\n", + " raw_data_start_date object 0004-01-01 00:00:00\n", + " raw_data_end_date object 0007-01-01 00:00:00\n", + " subset_start_date object 0004-01-01 00:00:00\n", + " subset_end_date object 0006-12-31 00:00:00\n", + " Data variables:\n", + " tropics float64 4.062e-05\n", + " globe float64 3.501e-05}" ] }, "execution_count": 15, @@ -636,21 +607,11 @@ "execution_count": 16, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.\n", - " warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')\n", - "/home/skc/miniconda/envs/research/lib/python3.6/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.\n", - " warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')\n" - ] - }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsvXm8bElV5/tdEXtn5jn33LpDFVUgVSBUAfrAAezWwlYR\nh1Z4Cu+1Q6uvFbAdWlFop6d229JO7dAqNiJKq83DVgaV14gTIoKACigIIgiIBZZVDAU13emczNw7\nYvUfMezYeTLP2Xeoe+6tyt/ncz4nc+/YESsid8SKtWINoqqsscYaa6yxxqUGc9AErLHGGmusscYy\nrBnUGmusscYalyTWDGqNNdZYY41LEmsGtcYaa6yxxiWJNYNaY4011ljjksSaQa2xxhprrHFJYs2g\nLiBE5GtE5JUHTceFgog8XET+WkROiMi3HTQ9a6wxBJfDPBSRB4uIF5H1GrwHZO0HtcYqiMivACdU\n9bsOmpY11riYEJGnAN+gqp99D5V/MPB+oFZVf+6U3rux5t4LEBF70DRcQngw8K5VN9e7vzXuKVwC\n81CAs9m9n235NQbgPrPAiMgHROT7RORdInKHiPyqiIxE5HEicouI/L8i8mHgf8TyXyIibxORu0Tk\nz0Tkk4q6rhWRl4nIR0XkYyLynHj9KSLyhqKcF5FvF5GbYtmfGkjrN4rI34nISRF5p4h8arz+CSLy\n2kjT34rIlxbPvEBEnisivxefe6OIPCTe+0UR+a8LbbxcRP79HjT8CfB44BdifTfENp4nIr8vIqeA\nzxWRJxZqwJtF5FkL9XyWiPx5pPlmEfm6IWOwxr0Tl8M8FJFPAH4ReKyInBKRO+P1K0Tk12IdHxCR\n/7hP+T3nxhoDoKr3iT/gA8A7gI8DjgJ/Bvww8DigAf4LUANj4DHAbcA/I+yMvjY+XxOY+tuBnwYm\nwAj4zNjGU4DXF2164E+AI8C1wHuBr9+Hzq8AbgEeE78/FLgOqID3Ad8bPz8eOAk8LJZ7AXA78GmR\nxl8HXhTvfTZwc9HGUeAMcM0+tLy2pDe2cRdwY/w+Aj4HeGT8/ijgw8CT4vcHRRq/ErDAMeCTD/pd\nWP8d3N9lNA97dcRrvwb8L2CToF14L/C0PcrvNTceDDjAHPRvcin/HTgBF62j4cX+xuL7E+KC/zhg\nStAFp3vPA35o4fn3xIX+xjhpdr1YKybGFxbfvwX4433ofCXw7UuufxbwoYVrLwJ+MH5+AfDfF/r3\nd8X3fwQ+K37+BuDVA8ZsGYP6//Z55tnAz8TP3we87KB/+/XfpfN3Gc3DxTpMpO8RxbVvAl6zrPyK\nOsu5sWZQA/7uMyq+iFuLzzcTdnEAH1PVprj3YOC7ROTO+HcXYef1cQRp5mYdfrC5qs1VuA64acn1\njyNIViVuBh5YfP9I8Xkb2Cq+vxT46vj5a4Df2IeOVejRICKfLiKviWqPu4FvBq6Kt1f1ZY37Ni6H\nebiIqwiS2z8t1PPA5cX3nRtrDMB9jUFdV3x+MPCh+HnxcPMW4MdU9Xj8O6aqW6r60njvQWdhIFC2\n+aCizVW4Bbh+yfUPLdSV6vvgQDpeDHy5iDwI+AzgZQOfW8TiWL0IeDnwQFU9CjyfoI6B0JcbzrGd\nNe69uBzm4SIttxNUkA9eoP2DK8rD3nNjjQG4rzGop4vIA0XkOPD9wEvi9cWX5peBfycinw4gIofi\ngech4C8JuuSfEJFNERmLyGfu0eb3iMhREbkOeGbR5ir8CvDdIvKY2Pb18dk3A2fiIXIlIp8LfAmB\n8ewLVX07YZL9CvBKVT055LkB2ALuUtUmjtfXFPd+A/h8EflyEbEiclxEPuUCtbvG5YvLYR7eBlwr\nIjVAlNR+E/gxEdmSYCb+HcD/XFY+Yq+5say/ayzgvsagXgS8CviH+Pdj8Xpv96OqbwW+EXhutMj5\ne4KOOb2oXwo8jCDu30IwAliF3wHeCvw18LtE66RVUNXfjnS9SEROEg5lj0fVx5OAJxIYzXOBr1XV\n9y3rwwq8GPh8hqv3Futc1sa3Aj8iIieAHyCoElNfbon0fjdwJ/A24JMHtr3GvReX/DwEXkNwsfiI\niHw0XnsGQXX+fuD1wK+r6gv2KP90VsyNZf1dYzcO3FFXRJ5JOLQH+GVVfY6IHCP8mA8mHO5/paqe\nOM92PgD8W1V9zfnUc5ZteuAGVX3/xWpzjfsuLtZcOh+s5+EaZ4MDlaBE5JHAvyWYkX4q8CUicgPB\n+uvVqvoIws7k+w+OyjXWuPSxnktr3Btx0Cq+TwTepKozVXUEsfn/JqiyXhjLvBD4vy5AWwchKi5t\nU4Lj7CkJDrAni8/Pu5jEich1C3SUtFx7MWlZ47xxMefS+WA9D9cYjANV8UUP7JcDjwVmwKuBtwD/\nRlWPF+XuUNUrD4bKNda49LGeS2vcG1EdZOOq+h4R+UnCZDpF8Axvhz4vIutDxvsAVHWltdPHX1fr\nzbf2XpmbVfXjF8uJyBcDP0fQGvyqqv7kwv0HEQ7O7wfcQVjY9zNFvmSwnktrDMGqubRkHsGKuXQx\nceBGEiVE5McI1jjPBD5XVW8TkfsDr1XVT1xSXh/CJ3K9PPKC0XCTvmtd3yVU36v1t/dkUCKizYc7\nt7H6ATftKh99Zf6eYMH4IeCvgK9S1fcUZX4TeIWq/no04f96Vb1s4waey1z6ArOXEdzZ4Sb/Tq43\nj7pk6zubOsUMswa/yb2T6+0e9Z1lbOWb3N9yvf2k7sI5Bj1XH9b4V/vfXDmXFucRLJ9LFxsHfQaF\niNwv/n8QQWf+YuAVwFNjkacQTETXWGMpZtrkvxX4dOB9qnpzNNd/CfDkhTL/B8GIAFX90yX3L3ms\n59Ia54NyHq2aSxKC+94mIu8orh0TkVeJyHtF5I9E5MiKZ52E4LlvE5GXD6HpQFV8ES+LDnsN8K2q\neiKqKn5TRL6e4OPwFQdK4RqXNKbq9ivyQPohmm4lMK0Sbwe+DPh5EflXwJaIHFPVuy4Yofc8Lsxc\n2munPkQKUA/oOe/4V1R6bvVdgIwwSQLZt5zqPmWL93RZXxZoVe9R9n23LxgGzCMI8Th/nhA4NyFZ\niv6UiHwvwVL0+5Y8e0ZVH3M2NB04g1LVz1ly7U7gC4Y8f0yuuSAvYa6PJfWVL9NebaVyRZlc336T\na2AfltJ3Hrgg9RV9O8b9+vfOu+79i2zvP7GWqSkWa/4egkPoUwkWcB/kLM5wLgWc71zKON/fTMyB\nvfdD1XEAx7nmrMovQ8mQjsnVizeHV7Skb7vqy0WH0zyUucKgeYSq/lmMolHiyYRgvxAsRf+U5Qzq\nrAf7wBnU+eKqjetgrx/ByOr76vuTSAxXce3uMkOwgqld6R8Qv5/Fb1PU1XsZl9G3Cste4nIcIn1X\nLcTM3OuFXqRlV1teuYoH7R6z/RaWvSbc9t6PAvzUz5zer8ithPhrCdeyEItNVT9MkKCIoXS+TFVP\n7d/6vQerFr5l74QYWf2uqOc4Vw2SxIYutldyzco6zhXH7f3Dh1V07rVRzUW6cTguVw9bLwYy2qV9\nPsu6xNLRtA9p03M/brpaVW8DUNWPJFXzEoxF5C8JG7+fVNV91c2XPYMym5uo+uVMKDGnYhKIGHIA\nZK+gCt6DMalA93xpQOIXft1l5UvEemWJEYou1pVoW6zTmH79C/3Yc2EH1C3ZEaXxWLG4LKM3PFfQ\nrBroivSJtaHOklbn++Pce066PpQo+5OeGcCgvuk7OpX3c5+9lFn9FXBD3Pl9GPgqusjukRa5ErhT\ng9XQ97N/KJx7H1YsdmJ2awZ61xeg3qQPK+sXI0vqG7BArlqQy3c6tpsYx76MVxboPUvGl+pXr3s+\nu4uOxXaX3Vt4Tr3uPU5LpdaYnHgffcAb/qLhr94427vQ+eFBkYE9BHiNiLxDVT+w1wOXPYNaY42p\n7v0aq6oTkW8jxH9LZubvFpEfAv5KVX8P+Fzgx2NYnNcT4qitscZ9Bp904yE+6cZD+fsv/dxgBcJt\nInJNYSn60WWFVPUj8f8HRORPgUcT8oOtxOXPoA5tBMVm2r0nCUAErO0kHdV8b+n+wxeHsKr9Xf8i\nlkk1revfLySFnqSWpKpSSlp8bhly+Vgm7ZQSrapgTXcv9bPsU6KlrKdE6zopR6Q3Zr1xSPfLPgLU\ndVfWK5LoS1gl5aa2ct3FmNy+fDhKTP3+r7GqvhJ4xMK1ZxWfX8a5pyC5V0Cs7b4Uu/AsEe16YIXE\nldVKdsnNFaq9ZSrj/SDSaR0AlfS+2fD+LWm+3+RySUMXJLF9Kuk+Lu3ukr7sGrfl477rGfX932jZ\nHA43ivqGn0ENmUeJGvrLaLIU/UlWWIqKyFFgW1XnInIV8Jmx/J647BmUXhE5vmrQsRp6C6gu/ICi\nGq4Z+jpZQ2AWaVH2IJGh4DvVl4pAZVBjujri/17dadFdVo/zu9RdQMdoFhlg6keisTcA3cKuxoDt\n91syYyzogL760Pvd45cZWqxjkcFFhpjHoWRaibREM8VYGxCnu++X41W2NwBTrfctM8BR9zrCAe/R\nWOb7VfUPBxNxb4At3y3TbehKH95V6rO9Ft290DvXDAxnlQq8t4khMtQ0H7zvMatVzffrNrvfs8TY\nIoPVJee2mcnac+hjSf/iXF6kb9ccKNpbXNdiXbo4Dot17oGB8+hFBG3DlSLyT8CzgJ8AfmvRUlRE\nPg34ZlX9JkIorueLiCPMrx8v/RBX4cAZVLSZ/xXgUYRl7OsJTpWDIjC7w5PwQReYSPn7KZHnC1os\nwJqlkVhGFYn/AcQTJYH+i6KRsaiVzvZBJNS3jPF5EK+I82FxTvSkaiXeb/1CG0Xd1gTay7Hz9Cat\nr0K5Xd5tZfuerp7iJQ90lcyua0PcghRW1JthY//j76BG0MrEMZLIdNiNJPjG/pp5YJZyFgZQ+02s\n6Kj7XApHXRH5nYUJ8gPAS1X1+SLyicAfAA8ZTsXB43znktR1t1kpsbgOx99Tl5VN95csvulexqIm\nIS6kuxb+VVqM4pld9S+2H+e8WNs905MYtbue+garz2QX+lIyiMVr6bqkTWG5OSzHoKRvsW+r6FgY\nq2VMcygjHcKgVHUxp1XCLkvRmC7lm+LnN3IOqXYunL3yueO/AX8Qvds/BXgP6wjMa5wFplrnvxUY\n4qjrgSvi56MMz1R8KWE9l9Y4Z5TzaAizuhg4UAlKRA4Dn62qTwVQ1RY4ISJD7epxkwq1hcSkQRoo\n1W1JolIj+DpIB0FS6urxVV+6Mq1i4rFSaX0pCri+pBVUamSpKGlEdl+z+ZrafnuiIG1oV3ynbluU\nBjU+i+m3iYR7orGfNtQtPkgjQULTTupKNNuu8iC90Ker3MTF61m6SuNQjnu2jEp9FPzIZNqSFaGa\nblxTG+IUV5tunAdi6vedTEMcdX8IeJWIPAPY5Gx9hw4YF2IuyajOKtyeBeiCikrEoM516uPQYLpZ\nPLdghbpQV88S1Wvf6tNIz8ozW+quul7S0DXQlV9Us5VnsanPxfmPlHWWksw+kmFSeWZr4aQKjW0l\n2ru2Srri56RqtWaXFXLvGGBhLDoa08SS3WOyBwbMo4uOg1bxPRS4XUReQNjxvQX498A1A+3q2bm6\nRhxUU79LrScK3gbmk14gXyXVWFzMBdSGxdRX4OpQzs4V2wS1VC7v04Ic1VmBQABMq3nxVikW3UJ1\nqIWaMX2GwIS0AtOERdq0ijgNC3xkZD4y4cRsyuchMI+kvlMb+2UCBzNOERfUb5kBSlevVqF/1bRT\nfSYmLq4r27UVGF8aE4j9XdgklIwzMVKtBFeHfuXnFtQci+3th996zr4xXWXJtcWZ+9XAC1T12SJy\nI/DrwIULKnjP47znElW3HAj17vPKYrEWW5zf7OeHKMWCWda3AAlEssxYaNe55OIZaz47LhhPqmeZ\nKi+d5xb9krzzKs5mF/rWY2KJ8S32Jarx8tVyHON3KV/wpPYrUfapHI/FMSg3BqmOVefU+2B2iUhN\nJQ6aQVXAY4Cnq+pbROTZhN3dYLb/T3/3qni+Akfudz1b196AryQsrnHBdjXFghjKltKR2sgA6HTO\n4TlBfFrUyVKHr7r6wvkKaBOfiwxRfGISu5mVGkEtu6QTNYFJpbp9HerytqMxMRvR7pk0WuIX+pUM\nGG234Js2nROF794mySjRrZjoL6EKOooMdclBbzif0k5KdAVD9oHWJM2lDUDYLBTSk5K/+ApOfeQm\nTn7kpuUsZQX+5dNvyJ9/97m3LCuyr6MuIdnfF4V+65tEZCIiV6nqADvCSwLnPZfed+Yt4YMIxyfX\nceXGdWRndgifXSlNdNJHXkx71pil6kEWmJVZ7Y+3bNHP1qp+9/PJWrQ0PhKJVq0F7amOtIBXBeNy\nhQFRoncRqohzXR8TQ188T1o0fiotUxctW3tjVJRJ9aXxXhy/Zf0tx04Md+zcwp07N+/uxwoMkaCW\nZW1eUuY5wBOAM8BTVfXtg4lYwEEzqFuBW1Q1zgxeRphUg+zqAa79lC9CnGLn4fvFi1y1xj2Bww+4\ngUPXPiwzqI+89VX7PjNAX76voy5wM0Gt98JoJDG+jJgTXIC59LBjjw0fLmAorTUODlduXMeV4y5S\nzE0n3rxn+QHGRmXW5hZ4pYj8vqreVJR5AnC9qj5MRD4D+CXgxnPtw0Hng7pNRG4RkYerakqH8K74\n91T2sKtPcCMQJ7hRPEeK6iooJAxAqyAtiBeqKXhXSE9VkDzsrFPpSVRPpV1/lnaS9F8FqSKfrUQp\nIEkj2VpOgmRhmnCm5aOKK21WxAd1okR6fAXBIi+2bSWcRcU/Z6Sjqw7PmBakhXo79Cv1V9qO5vR8\nefaW1IWBbkFNkM6yGlQ66TOPpRTPu1C5cUHCtLOglvQ2SHh2rnGswu+jSb1Z0alCo+SVrfmy+nPA\nCxQx22fnN9BR97uBXxaR7yCcAD5lOAUHjwsxl5iM+9/9gkSkCjXdtVKV5KN0lSSu0mR9UZKC5VFD\nILguJKvP1LdsBZokoOhSAZ3bhvcwKs5wRPrPlf3JEpDtrlUL5UoVYekLWNK6yl8rqdtShIlE6xKV\nYc+NJPWnpMP7Ph1Jkkr32jjJ05iX0qPzYYEbiP3mEUXW5tBNeR0hav5PF2WeTAwkq6pvFpEjaYM0\nmJACBy1BATwD+A0RqYH3A08jGLYOisDcbAkmRoZPi3Y4ayEvsHlRFiIjA9sAPjKuCloBOxakJau4\noHtWXFD1qQE3Dot4CmAgLr4H2i2uicklFZ+0HQPzoz5zcY1gYrvi4tlQRWYG+Vwp/neTxCAjfVPy\nL2lcpF8hbYjUxvbo6oGkEpSgmvNgGsn1JIbsa7KKMT+rnSoxMPR4ZuW6CRsYz6JaMPa/jmeBUSWo\n49CGiXTkc7+BGKKaGOCo+27gs4a3eknivOaSO7yR7Xql9eC6xVQWVXhOl/oOAj3DG4zp+R1m46Wy\nztxI95zPjCz8U+j89ExwXxCnaOvDwl/UUfoiaklX2ZeEfCZV+PSVbUHngrGoAlxGuwT3it73ZJbf\nY3SRYUl3Hks6j11m7JGegx5zlnlYrILhlOl8IXv9YxAGzKN3Aj8qIscIWZufSNBOlFg0SPpgvHZ5\nMihV/Rvgny+5NciKqtmKC52jk5xsZwzh64KRJNUuUcJI0kbc1bc2MaJQSOgvlMmiLkk3QJ54eaMS\npbJ8/qRBkjGJPo30jTrmlRZkE9WU2YLNd/UlhpiNOmLdSsc03ATsFOyMzigj9a+kz9CTZHLbGg01\nUvvatY2AG0N53qQ2jJUK2cgjbxbqsBmAcM3OYn1VR4O4vmSnUeqUtqBhAGYDPOAHOOr+LPD4+Csd\nAu5Xpkq/HHC+c8lt1X2/tegDlyxBib5sPcZS7P4zzIKRS/IRLC957Rb+VFXpx2iKhRt6i3bwN5T4\nvhZSSXrfbXcmk9oIBkeBmeZz0raw9hUCYyHOmyVWf9kSNjOKkr7UsdC+VmZlkACJkphptTjLTmV2\n9zf5Y2atQmlJ24769Cy0eTbGRu97853c8paPrbw/MGvzMrHyLGZzHwfOoM4XbkyWjhKSRBBetAUJ\nJG3MosRi5vTUd34EfkxnESedJJQW+f4Bf/dsN9G6a5qk87iI+yq0gWhfZZa0Jq30rAB7ZRJzUMBo\nuNR2HXcbUcU2KujKu8Du2VL1mRhykuzcRtf3Xe0X8DX4Wnv3bDSEKMdAIlNq6uI3KsYnqBnDJV8F\nJouSzxSHYD8GNcRRV1W/syj/bcCnDqfg3oHmcNWzTg1aAIu0mr8nmNZnN4JsnVqqZk2x8K5YssJi\nT8E0yIyRaJiT1MXZurPqrGBNo4i3veeTO0lemPcwL0/9ygu+kSw1ajTmyeSmd3WB5p6an7ROSP9d\nF/IYpbHd5caRyiSyXaQ1ztnO7L1ox/RpLK1qob9JHYJrPu3juObTujOrv3j+u3eVUdUXEHJCUWRt\nLnErcF3xfZlB0mCsT0PXuOwx81X+W4EhjrolvpqQjXaNNe4zKOfRqrm0ImtziVcAXxfL3Ajcfa7n\nT3BvkaBEowos7B7MXML5h2iWXDBp6xGMAZyANBLKFiqtpHKDQmKyoW5pJau21Go2RAjPhHqRQqqJ\nuy5f7I7CDquTPHytReghxS+c2/RCDmloJxlUIECtPWMIP1J83Vc9JJN76NRquV+m67doOtsqHHAp\nylrytiyr5yrNu0s/7h4yjSCNdL5mdaELl0L1YGJ7acysgo9S4EAMUPENcdQNpIWJ9/HE9O/3JcyO\n2E7DENXfsnioH9W7yYWgczwvtA35XS/cKZLkn+vRLD33/OkKSchXnURe3kvXq5n2NAPJhSOrkJMW\nZNEtKJbJMSHT9+g2IS5pO6QngZTuIuIU0xTPk+ZIJzmWfpbeFn2gP69Ln8z+GLGwNvUd40ObnQSX\nXD520TxQihqiKmd51uZvBlRV/7uq/oGIPFFE/oFgZv60Ya0vx2XPoHytYDW+iOFXc5Pix/RBVx04\nUnhGvMRDyvBd46KoVfDf2WVVZtOLqPjS6ELDwqs+iOuh8rjgxpCIvnjpwmSSTuzP50KRsSUaV2ls\nvfTULJl2CcYdRAamRnvGO7te+sgE03ghhHM0OsaXzojyGAiZ4WOKiaBd+WAJFa75xDxd5xPWe9uM\nxmgXBQ0afhu1uxnkXnjL8/9mvyJnoxf/KuC3VfUsKLh3oNnsq6bSxqv3rhbzBijUV/FrucGL6uHE\nvNL9VG9Zz65YjULnRN6jJ3610G5IZpahrtURXXpxMwGSUU9PBZc61JXpOainuRzfU9NoVimm+/mZ\ngs6SefeOBCCfn4e1Kl6XYs5RlpeCGdNT14djjaI/pqhzIObDsgJ8zpJrz1/4/m3DW90blz+D2vRh\nwYw/DFGKyhu/eD0b3fgoXRUvuxQMJkhB2v3YCaVQUk5Aq+SD3viCJoZoIkPSVO8IVOKblcpFIwsM\nqPV5svRe5lRHOqSOi352cJ34sHC00VqopDUxDt+vL1sZ7lpcIoNLLzh0El9kUvhAD0YDU0/3PWhi\nsk7ASWbueWxMN6hqtPNNTPUpqJfe2dp+ePjXd24W7/zVty0rMsRRN+GrgG8d3Pi9CL7umEo+Fy13\n444ucGwsU57Nlhu3VEZt/FoyqGWsv3hpu40jeNPfXZRnw512QDIDy5LJIh2LhxnlPCvmQjIcStd6\n7ZXDoSCT3QxRTTds2Vk+Smfq4/NpSuhC3QsahpWajp7hVqyz7miAor+6YryXYOouvUgSB3oGJSJj\nEXmziLxNRP5WRJ4Vr3+8iLxJRN4rIi8Wkcueka5xz2HubP5bgeyoKyIjAhN6xWIhEXkEcFRV33TP\nUXvPYD2X1jhflPNoj7l0UXHQjrozEXm8qm6LiAX+XEReCXwn8DOq+lsi8osE7+XnL61ko7MvF6NI\nVD9B2ImrC6KAidKJbwzamp41pleQxkRJQcPnygfpwAQVnszj1iiqEyWGDEoSRE8hpCBW8G2U1KIK\nj0rDX6F70KxG0SzpiYlSSgxplKSWoF4J7Qb1mO92u5XiRx5pk8127GCSnHwKj6ShD7Wi+bAglFMR\nqF0eP286mnIffNwORklIinFXleCDoeT/6iXrOcVqZ1VowuGcxHpUw+eUe0h7+o29Mfd7T6aBjroQ\nGNdLBjd8CeGCzKUkMchu6bq0IoVOnZUl60ISyfVYsjSQ/eaKM5FeOLqi/VLFngXuKPHkn1qj31xU\nj+WQXfSltCxpuaIe00k3CkEqLFVvScnRdP0o/RLzcJWSj+8+5+PuRSlG+9JPOh/yVVEmDbXv2s6n\nB77rc9YM+e5zj65zED32m0ci8nBC6pbU4kOB/1SGOxKRxxGcwd8fL/3/qvqjZ09NwIHvplR1O34c\nE+hRgj9KCkXzQuA/s2JSjbdmYXETxYhiTPjvVWhdF7DRxOyZfmxoGhuYV1zsjVV0HBdSL2DCW5ae\nFavoyOfFVASIpuK2Ctd9Y2J9Seb2MI4Lc3zOWMVWDhNNxF1rUBVMXJhVBe9MeCG1W6Q1GU54wXvJ\nDEPTZ+sRq9ioTnONCeq11oCNL/7YB+aU1IpEhgG57bJNES3fecQGJm9M1x/1gXbvBVUprWfxLsx+\nU3lM0c7i75Hgffhd0lhIoQrcD0N2e/s56sbvPzS40UsQ5zuXgv9cZ7yQF/EUobtwsk2O6T3VndPs\nbJ2czX0tWIpFNdYT1INSMITu985nlkLn5CqSVV3BoX23wUCPKdExg1Ltl4NH09/H5eeVEI+yYAK+\nIkdByQzZJ0aomUlkv8fy1dbkA5mI7M7CQkxPwEuI97lkU5AMTcIZdTfGmUElZ/fSLaT4v/KkdQn2\nm0cxQsmjQzfEEFTn/2tJ0der6pOGt7waB86gYkffClwP/AJwE8E0Mf3MtwIft+Jxjh3awaswqRvm\nbcXcWSrjmbUWI2GxM0axcUE0KG4Sfj3nhdZZWmdwrcG3Bq8GM3aYyseoIZ10luqorEdVqCtHZTxG\nlMYbnDc0rY3O4JLbdd5QVw5rPFYUp4JBY9SSUL9XyYw11dc6g/cm90EV5k2FcwbX2qDTtkpVO0aV\nwxgf+jMKzFJVMJFWI8qobgMTjN/ryuEjY9HI0J0PTN1Hpl9ZjzUeEWVkQzupT42ztN5g44z0CDZu\nFNK4GVG069XVAAAgAElEQVRq4xBRnBq8CrUJ4wbQOMvMVXmT4byh9Qbnh0tQzT47v/ie7emoG8t8\nJSFDqAf+RlX/zWAiLgGc71w69NEUJbhb9EoDgM7atItmDx1jCAXDLiVb4IlGS9eOoSSn3xQVv/Qn\nys65hfNpYBw+Sg/J+El7/kbiQZr4Ho5MpqukW1OS0bTRjHVlhqL974HJpvOt0kIwBVXWBR+obqxE\nwcx9bqO0eEzwI5PT4iw65pZj2p1J9/29AMw8njnncGQhOHOvHwOn0pB5VOALgJtUdVl05uGTdx8c\nOIOKk+fRInIFgRt/4rJiq56/9X++AUWojOPQoz6eyaMeek+RusZFwMl33MyJv7llzwwOi2j32fkN\ncdQVkRuA7wUeq6onReSqcyD/QHG+c+nm93SBeY8ev54jV19/wWlc4+Lh7o/dxInbh2cG2G8eLeBf\ns9pX8EYReRthrn2Pqv7d2VRc4sAZVEJcFF5HiHx7VERMnHB7eiI/9GmPxaAcGU9pvWHqtjkzD0Ev\nR3XLyIYd/7ytQoR6NcU5h2E+r/DOYKKabDRqqGuXpYBx1eIRRtaxWc+Z2PAdgjSWJIHWG840I6Zt\nndWNWVIxjlZNVp9ZFWrrGNuWmQs/QeOCxHdkNKP1hpmraI3Fq2SJpfUmqsBcOK8B6spRV0FCOTya\n5XZS20kq0ViPNZ46HghUxuNVch8+dPIKrA+SVh3LBqkvSIlXjGZU4tlua1o1zJ3FqaE2js2qoVXD\nyDgmts367HIcwng55r7idDNi1la0atgazWicxRqPfsqDOPapnSP6R1/8hn3fncbvq3DPjroAIpIc\ndcuU798I/IKqngzvxmUVybyHc51LD7/qceFDVGfJHSGcR5akogQirYYQQ2WbyYzbCL4y+RmzUDaE\nC/L5cy++nJGcKieEJzKdejGFGnJKmfQyJclMdaYQQ13C0iQ1SZagchinGHKoUxUW4Y+M5NBH0vos\ntaS6skST4hUWMfWCX+BCWKQU5FUDvTm8UhGWKR9FN55SzVeGYyrVoSqCaVz+rJWJkTZCnVdX13HN\n1dfmMbzlPX+8x1sDd779Fk694+Y9y4SuSg08ieWJL98KPDiehT4BeDnw8H0rXYGDzqh7FdBEZ68N\ngtj4E8BrCUEtX8o+EZgP1XMq8VnVtlk1bFYNc2+pxOMJ6rR5ZfNimRblnbZmXAW1RuPDArlRN1Ti\nGVctIxMY1VY9Y2RaxjGK7F3zQ7RqqMQzitdONhMOj2Zs1A2tDwt1ZQJdp5sRh+Iin+g0aKAxMonD\n9Yyteo6JcvnpZszUVRjRzAicNxzb2A7qNW8Z2cCAK/HMvWWzanL/WjVZfZjUnkaU1hu26jmtD+q2\nzbrJTOPqrdN5DH1kpqmOkXEcrqf52ulmxBWTGRPb5DJH63AEMvMVO25EbVxWG1xRTTHimfmKU+2E\nk3ETcWy8Q2U8mxthMTwdaasWzqj2Quv2ZVBDHHUfDiAif0ZQA/6Qqv7RYCIOGBdiLpntEEhRvIfW\nd4FZU9TytJi2nhSPTysDxuBri8TzEpPsqb1iZw5pYg6lxBSamI23dd3iXFmoTLBiT4ttDN5aRgNP\njAinSNt28fwig5AyeK0x6KjqmJb3vYC14lzv+ZwkEEKSwxxV3FPmXMrBYCMNiKC17QLj2siIYsDd\nXjDbSAOlKrMILFueVeU6INQfdtjdb64KTWifyu4KUturawA2HvkQNh75kPz9Iy/6s1VFnwC8VVV3\nBe5T1dPF5z8UkeeJyHFVvXMQEQs4aAnqAYT8O4YwHV4aPZHfDbxERH4EeBvwq6sqmLYVI+u4c7rJ\ntK2YVC1HxztcNTkDwMn5JC/Ws7ZCRJm7iknVMLYt9cTlBXanrYOUI1CJ7y/43uLjGQqE+wCn28BI\nEpP0Lp73WMfIuCw9JMydZWQdUxdoNaKMokRjxOOjwnzqKnbamtZZGm84PJpxbOMMd802mDY1tXX5\n2e22xqtwuhllJpzgvMEj+ZxnZFvmzgaaTZCGjFR4lXCmpIaJbZnYllYNp5sQ0sHUysxV2Znv6Hgn\nM8a5q5i2FcdHZzjTjpn5ito4RqZlwzQ4hLubDaauZrsNfwDjqmWrnnHV6AyNGnZidNlWDSemk8Ev\n0Yd+fV8pa9kMXVR1VcANwOcQfKbeICKPTBLVZYDznkvmzLRb3FPKCWNC9tyU3mFxQa0rqC1GFTUG\nKRZeaT0ybwODSsn12rarP5ZDJKSbb0xIt2FMWHyTcZG2HV1JanEedqaBxqoKdCYrhXkDzsFolBmq\nKWmPdci86WhIKe6tjWXm5GSIZcp4ESSls2hjv+owJ9SGojTReGLehjKJUbRtKJ9oTmOYkhKuStVu\nDJLqKRNG+ti+hPqksl3E9ZIprUpTv4CzUPGtDAVWptYQkU8H5FyZExy8mfnfErKALl7/APAZQ+pI\njODarRNxcW04XE0Zm5bb51tUxnNFNc2SzulmwlY9xathw85p1XKqmWTmECSROcfrba6odrijOUSr\nlsNVkB7ubjYY2xanQi2eHVcH9Ve9E6UEpfUWh3CmHdOq4brRNh7hxHyDiW04PjrDzFd4NYxNS2Uc\nO67mVDthZBqmruaK0SxLOiPrODbaphLHVh3UbHV8phLP8XEwajjdjrO0lNRsc2cDI1yQ2DarJo/h\nVj3FinLbzmGOjnaojAsM2dVMbMv9Jqc5XE3ZcTWHZZolybubDbzWbFUzrpmcZOYrDlUzrpAwVo0P\n/TvZTtiwQVIb2Zatep7HYdPM2fYjnBfGJjCsrXrWY+r74cqv/Lz8+faXvm5ZkSGOurcCb4yqsH8U\nkfcCDyOoLC55XIi5xHQWF3vpFktfMBbnw8KcUqiXaSRUkcYh8+J3cw6ZxUUayBl5S0nFmC6PUYIJ\n7aZ0FElqkJRzaj7vFmZjOzrT/yjVYE2oB7r0IESVXXq+ZE4SGbEuMgPXMTdrQputCwxCSobchv54\nj8xdqNcVdfn4Z2MeqlEdaE2SZIy2niESyjZNP3tuiZgqhLYN5SQx7KKOUoLbA25/TQSFdP5NxbUc\n6gj4chH5FkIopB3CWdU546AlqDXWOG/4/S3+hmTUfXm89mtRXfYwOl+ONda412PAPEJVd4D7LVx7\nfvH5FwgWpBcElz2Duv/GKY6PznBVfToe2LfU4jjtJlxRTWk0GBrU4nAYrhxt03hDo5Yrqim1OB4w\nPpFVcRPTcMRu02hFoxaHoRaX1WZHNnaoxXHKTaL05DlcT7n/6CRHqu1M113tIbbtjFqS+k65ZnSS\n027M2AQjBIANM6dRy5Fqh+smd3Gi3QBgy84A2PYjLJ6taoqNWqlTbhLUaCOXpZmTbgM/Fa6ofT7v\naWIMGhuNE+5uNrCiNN5ixFOLz2deG7bh8OEpm2aOI6gyPYYtO8WgWPFsjuc4BIuyHaO5XjkKfXYq\nHKu2sbFdg+awOJt2nsdh243CfVEO2ylj07CloW9TX1Oblm035qpRUPG9csA7sN/Ob4ijrqr+kYj8\nSxF5FyHHzXer6l0Dmr/3oKpCltmUDTft+sudeZJ2UtZaG7PbmhgfKKWwcIo0bSdpmKj/MuEsCu9D\nfXXdq1ON6SScyvYTwnrtpLGSnqQydE1sw0Jd9c9kktDXRkkk9bF1nbotSVNJ4llUZ1a2kx69hnYq\ni47C+VMqK22SlpZIQ9ZGCbXI5pv7H68lycwWZcpySU0IsOBj1fsNU7LECyhBXWxc9gzqUw/fwtg0\nWIK1mYtnOJtmnpmOxTMxDU4Np/wkLvrK8eo0BsUj+bmJaZhIUH99rD2MqX1mDEY8x+1pGq24qrI0\natn2YzbNLDNAgEMmMJdj1Rkm0gQrOJRaWhqtmGqdmWYtjkNmhhHPth9zVdWPh2XFM/U1m2ZOLW1g\nMHHWOg3nS3e7TWrTYqI7+zgy6XI8GrUcq7bZ9iMmpgn0avBhOladwcZnt/04M6FGLWPTcMjMGEuD\njWOZcKwK53yn3YRaHFdVJ/GRoU99zVF7hkYrHAaL5w63xVXVKY5XpxlFhjWPfZn6mlN+A4tn08yZ\nmE4FuR90MXPvsjLDHHW/C/iuwQ3fy6CHN7ov3iOuWGTTOUxcqP2hmB4+GSikBRWCIQIuBNIrF9a0\nkFaEBX0yjmq6yOSicQGeyJxCnTqK2XMNiBFoTTaCyKgMSJWNLbS2/TbTZynUden8KzGTpH5LadRT\n2nYTF/wq9s8FRyWd1GhVhfZSIsLWQxUDBCSDjTzAhUFGUr3ZuqMvpWnH5nHp1JC+v3FI10YSfpvI\nlBJTVms7Q5aB9kZD5tHFxmXPoB41uQUrgYmkhRXARaYzEofBMxKHQzjlw878sJlSSziPSWUbLFOt\nqXGMxHHcng7MC0OjFZsy45CZxUW3Y2pJajhqdpirxWOgJtMzkYa5Whpsli5G4phIk40aanGc8hMM\nPjCwwpR9JI5aWibSMhHHGa2Z+sDoEhNqsDSTiqkPUl1isonBNVphxGPxOMpxCfWmMZtqHZgqylQr\n5loxkhaHieOZ6Gi4rr4DpybTuynz0PdYV43L36dasWnC+B01O9TiadQw1SrQby1wFzWOBpt/xyHw\nw3Tn+2XUfQrwXwlnUQDPVdX/MZiIewGaYxs5bYO0DqmqcK7i6Sz6qmAZ1xzu8qEks2wgmKC3vquj\nLVbHZAHoO5PwYNKeFlYCU6QwO69Ntgg0bZCUTLIKLKMzVCaYpVcmZwXGEK31Fizfoom4aRWZByvD\nXkp7HXX0xvTy+X/qjgE/stn8PhAd623qkInYdRlzc4p3V2Txpasrt5fM1ZP0U/YzIVlDLjC8JM1m\nU3M4q6wAQ+bRxcalR9Eaa5wtUoiqFTr0wlH3i4BHAl8tIp+wpOhLVPUx8e8+xZzWWKM3j1bPpSMi\n8lsi8m4ReZeI7DLAEZHniMj7ROTtInJemakvaQlqSHiaR45OAOBQXNQOpHx9TmFS7FQ8MNcdrIAt\nLI+DZahiEQxgRXCq5WaJptjRO4UmSkepLYv0aEjPLcIK1EjelDbxmZEIx80prEDMg5YlsDo+k+g6\nzhxbNTjdzvUAnIrP1Wh2s0j0llK+i30v6XcaaEv/U3/KcZqr7up3sTnNY5/aKyVaQ+f6MRGDQZhp\ni6NhIga3sEucLu4a98AA1cQQR11Ybo5+2WPIPAI488Axpon5lZxi5hpzLSkp/bofBamm3ZAcRiiH\nRZIQJ8+0GuLlebpEhDG8kXhiqnYN+Z7q4JybdvymDeXVkPNBlfWWQVJTgj5vYx0pOG0SaIrU72X6\nihQGqNpR7DxKUq1mWnNYoiJ80GIKm+DIS6anTIVh2tKBNz0U6W6T9aJ06tNIZ+pHV18Reqk4D+uF\nV5K+lOQtuHFy1t19fy8MVPH9N+APVPUrYmT8zfJmdM69XlUfFpnXLxEcxs8JgxiUiPykqn7vftcu\nJIaEpwHYkooGHxbSclEmvLyB6cQFOTMexSB4NJQjpFNJZa2ExTmVsyIYDD4u82kx9XnxLRqW/nVf\nMDaDUIvF4zMdk/iMQfCi1DFBUrrvMuOMz4rvFnOhR1utrsdQMoORrv0Gn/tVY7DxpW/U57bz87Gd\nNG5NEQUzMTAvXV8tQl0wm9T3Wkxv/BLqgoVb0/02AJOzYBWy/8QamlH3X4nIZwN/D3ynqt66pMx5\n4WLPpaHzCODkg01mLPgQ5NSk8HxpgZe4CE7IwVtzEFXSghgSaIagrnHhbjvGkCOQ1zGgrIWUH62L\ni5c6UFxXotquYwai4agrJQrMSRLL7ya2If367I5EhtzRmSNGmN1/GQt9LqOwL/5PdffqSEw0/k/R\n0v2InEE4Ry2PfVzUeC/G6sv12jCuZTDfobH49ptHInIY+GxVfSqAqrbAop/gk4Ffi/ffHCWu7Bt1\nthgqQX0hIU5ZiScsuXYhMWjXO5aKMRBOSUyfWQBWDK44JawKBgL0Fk1TLJhG+ozHigFsqCse8AfG\nUTyzZAO+jInVEk5nFu+V340oHo9RzUzIIBhsF2AW37s+kX4fFhlCoLkziyrHq5K+k56VoPt3Ghma\nQF08m2hLdKd6DCZsDuOmoWRMVe/5MKsXx3wZzfvh7t/ZO4QLyyWjxX3lK4AXqWoT/TpeSFjULzQu\n9lwaKj2y8dm35wDFALPW9iLVqw+R9m3lqesWI2CNp7IhmHBtPFV0IHcxUomq0HjDvKlonMlBgYH8\nnAUW06s4LzlEV5Y+coiykInApbQ5KYuB9SGYsY3nWPEXHleOSR2MlVQFT3S+j07wrQtBnr03gQcW\nwY7LoM8iijUhGkwKHN06E6O8CD4GOdbYb++Fdm7xje1UZqKkbAIIIUNA5anrELIsBYVWDf2PJXN/\nvDf4mNUAwNgQVs1anwNSm+LVtkWQ67/f703Zf6P3UOB2EXkB8CnAW4BnRtPzhMXN4AfjtQvPoKLD\n1bcCDxWRdxS3DgN/fi4NngUG7XrT4pgW28SQbCENpM95sdSSKdleObfC5KXH5LC5znTdRimho8vj\n1OeFu17MEyewzLxmkcnVYncxsK5sX+IZS52vhfZ3Mz67ICWt6mOq38huySfA4uL1ZYx2kTYfa1m8\nXj4XPttd/dwPx774i/LnE7+/lFnt66i7YFL+y8BSNdi54gDn0lDpkRc96gVM1WYVcLJKTS4WDqGJ\nOdiTEcwkiljJ0OewaalJknenznYojfbVvqE+oVGTrxvRbDzT6O7lKRghSaatxuV7Vjw1wXq1Fs8I\nRy2eTfEcMp1mAoJUvyl1b83wKI06ZjgaVc4oTIvoMYlmSOrx8P43UTQyokzEZ81dMDqynPGjbEg0\nj30KhlUVtbQcknmwXJW2M9qKrh5GlBFdtBuvwlQrzugIp4ajdodD0lCL9lT7Sc1uCMcH6UXYC9N3\n38T0fTftVaQiOIM/XVXfIiI/R4jH96yizJDN4GDsJ0G9CPhD4MfpBwY8dT7hKwZiUEd/9KfvQhAU\n5XGfucHn/YtD9zBZa9yTeN1f7PC6v9jZv2AB2V/o2tdRV0Tur6ofiV+fDJxzBOYVOKi5NHjB+Pmf\nPUWLoAifduOYRz92c1mxNS4TvOmNM/7yjfOe2n8vbF5/A5vX35C/n/jDXZu9W4FbVPUt8ftvs1vy\nvxW4rvi+Z4Di/bAng1LVE8AJ4mQWkauBCbAlIluq+k/n2vAADAlPw498z9VApxpKUsCiNBB29Z7F\nM6Fy978oea2Spsp7afflUoDMFUjquPQ57dhS2yWdZdl0rawnPLO3AWaSfhbbXlWPx++SrlapP8vy\npZS4SGtZR3du1rW7rI+Lm4z/8rMn9uxnqHzvCTgwo+4zRORJhBAtdwJP3b/h4TjAuTRoHgH862de\nAxDfFs/UQxM1DEkySa4RACM6J/TkFrEdgwO7XZqAvrSx6EZQF64ijVrO+OBnldwokvSR3BaSi0Ry\nl/AEKSy5ckDDxLRMxEfDo8X3Ms0z29MUBA1LMNLZ9lVwgyhcSjwm++4lX76uDy2NtD0JEeCQmTOh\npVGTpUKDD5H/paUWz0RCzxoJUqWlpU6xCAnS6EQMW1JTi8VpQ5ulxwqnmiU/RzBm+qQbN3nkZ3Rz\n6WefneO4Lsf+8+g2EblFRB4ekxd+Prs3cq8Ang68VERuJOQjOyf1Hgw3kvhS4GcJyc4+CjwYeDfB\nZPeewpDwNEC34AP5RysXv0bjmZGUqiTTW0xn2mJF8Asv8jL1Wrlwp/KJwS01nIDMwBp1vUXbSrLo\nK8+yTO9/18eCNtnNdJz6rHZLSEyn0TZ/T3SWjGuZ+i+fP0VDi2VMvxynVcwptCsr71HQcC5YWCeW\nYj9HXVX9D8B/OCcCzgIHMJcGz6OvfcM3ZNlKiszJ4TuICdmR0/lMOn9KZzQp6WVKfBnOiewuTbYY\nQEKizZDSRTEmJAhtWkvTWlwbrCFWBeQWE85tyvOhdHZWJik1hHMjKawKcpT/1mbGWibs9On8y3ft\nqzf5rEtS1ueCHqXzIyrP67rxDOdNHf3KZNTkc7gygWlK15OzhBM+j2xIZbNZhViWAK0PAZ632xEn\nZhNmrsL5kApn3tjcj4AfXPazdzQNmEfAM4DfiCk33g88rYzFFwMUP1FE/gE4AzxtUK0rMNRI4kcJ\npoKvVtVHi8jjWfGSXyis2vUulptps+uMxKkS9hfaYxhJl1yLoVFHUzIaggnrOO4Y04Lqc30+i8ql\nhR+RIdaR4aU662WLrpKt6GpMKKuhbO9gM7a9zOIwfW8Ugkuw9CzxStPwWgxeAyOaqss09FHo8FOK\njdhe6otBsBosABNTrQsDizTGpcVhb1yLcSgtKVP9Y2zsQzrzGDZTEgZY8Q02tRaRLwd+E/hnqvrX\nZ0XIMFzUuTR0HgEc/4txthpTidZgVWG2Ha3EoLN4U8AZaJIlngfjw0fxhTXawnunprM286ZwzWhh\n4guLuliPxtdDTbwcaVELbbQmXMwgW6ZD71nQRQs8M4/WhclSzoPVeHZWWBGWKd2T5WHqa7ovbdde\nsl4srfygey5Z8PkKphWdZaF293rLRrL4S1aT6bepFa0UnGDmgp2DaTpabAuVDmY8g+aRqv4N8M8X\nLj9/ocy3DWtxfwxlUI2q3iEiJiY/e62IXNBD5GVYtutdY41FmH0m4FBTaxHZAr4deNM9QylwAHNp\nPY/WGIL95tFBYCiDujtO3tcTxLuPEgJqHji2telJJDUmWgz57FwbfHY8ddKTxx36VJUzaqNFTvK/\naUI57ZxoIVjCuGIb2Gi3iQJC7LvoGzTVIFnVaLagMbG9BqFGadCccqemzeUAjHaSBhR+SAU9CTbu\nvhJNgZboiFxIIqWT7USkR0/hLxgsqegcbcM9ZRzHZ5bzWyXJMdSR6UG79iPKcbMqRf0ax6zNfajz\nudlww58BO8ShptY/QrDe+57BjZ89Ltm5dOSmec42q9FJ1Y8MPnplB8dciVJCzGTrAR9C+uSsulBI\nHtHZtNXO4ZRQvxubBf8psiRRXgsZaOkcb0U6p9rFTX8Rfy/Vk514NUpjyXA3SzuhL8bBrgkW+5Ky\n/XpLdirO/XSKbbRvrBOdiCH5bEVn57p71mcn5U76KpElwTQeJkpREsbPV4TsxZKkVe0cpaMD8so+\nLcFQSetiYiiDejIwBb4D+H+AI8AP31NEnQ3eOd8MQVTjgnbYzLEoZ7TKcfcgxII76Tdy8NagHlO2\ndZwPWQ0+HwRDOBi+22/m8ikid43rxcFLcehCfL2WOTYfiNbS5gNWgDI2XQqimgK3HjKzeFjsc5m6\neOubGPcuHSanttPzqS9WfKazazcxyu5lTeVH0jKN8cfS84dlmvu3He/V4jgk82xuXMcYfuU4Jxw2\n0xzj75SfZDPiudriN9B8kNxolX+H8iB+CAZY8e1rah1Dslwbdej3JIO6ZOfS5NYTHQMxJgRBrQxq\n7a6wKL3MtkVMuJRRNmfBzYc4oUxI2e7BGNzmKNxPuZpypATpqcekyEKrMXq5eO3aTtG6U7y8hQMr\ntSZGaJAubXwRmUFal+kq4/b1Yv3FrLlamX58PB/6mLMGpzGI4xWSK7puJ5syBVvBj6ocRSLQ0cUZ\nzPEJjYT4hDHES+5LTF/fMajIlD2YxmPmDjNtOroGYMA8isMhhuADdauqPmnh3lO4gDEtBzEoVT1T\nfH3huTZ2T+Cm+dWc8pMcQXwsDY1WpAjl237M2DSccBvc1Rxi085J6SNSksJTMRp3iqC9aUL68Sb6\nhASpwOVo5Cl6eRkFPTGvELk8BKftZcjVOgRFjdu3bT+mUZvrTZHND9sdJtLkVB8jcZzxY6Y+RD0+\n4YLpr6OLhh58Jww++o9suzG1OBoNFk1X1acyM/CRhqmvMyO7qjoFwG3NETbtjE0zZxbbO2K3MwPy\najhitzlsp3n8nQon3GZmnKnPKQp6LY6JzLnbBWuiO92hKG0GxrwZg+96JKTbiFHYkxUX3LzvO3D7\na/ZNyrGnqbWICPBsQkr0vZ45b1zKc4mTp5M3KFJVMB4hMT1ESr8OcdGdxxQcKSp3WiBTmoeYZTan\nekgRuFNqDMC6kFYipcAo28h1Q8c4VLso5a0PWWZzgsL4YIw6rnXheK6KteGaWgnBYZvFZIO+o61k\nqjG1hUSadBTmhSz0JScnpHhxxJCTNKbyKUOxtch4FFQgMWCuOIfszLt0GqM6MCkNQW1T/xKjLFOB\nZMYc+xKSR7YhCaUbxnnOQoJ6JsF674oV91+iqs8YXNse2M9R9xTLfSaEYLWxisA11rhouPqzvjh/\nvuP1r1pWZD9T68MEK7o/jczq/sDviMiTLpShxHourXGpYwiDEpFrgScCPwZ856piF4qm/fygDl+o\nhu4pvGfnAWzaObfr4Wylt1VNaXzFth+FVOgu7MbPtOF/SrOepJ4dN+KMG3G83uZovR2kl+hBXpug\nPqvFMYuBsowcKqSf7nrC2LSdesyPaNVSicvp4UOq9JBe/ki9w9F6h8N2mpP5jU3LYTvNkk6SKhq1\n3NVssuOCyu1QFaSdsWk56TZy5IgdV+PV5KSEO67LMeXVMLYtp9sRtXgcEpI72pDefuYrTsgmM19h\nJSQmbLzlZLsRclbVI26dGw7baZbqfKzj6vokUxfybc18lZNITkyDQzjRhnrvbjbYsnOMeDZsk8cR\nyIkQzwYDJtaeptaqehK4Otcn8lpCLL63nTUxK3A5zKWgivKoc1Gt5kNa8qpCxPeT/E2DxoLKktOu\nQ5BmNJhgJymqS3eu4F3e0ctOkBIA8Iq4FPgvqt/KPFJRChGvHR2zGTmPU5n7qa4QxkEd51Jqi9gn\naxHvO4kppamwwVRO5gvp3n1UzyV1YOqzSJeqPUlSZTLF8HAnOfWkTQ+tQ2JuLTUGqejKpgSRXpFx\nXbThwTmkaZFIQ8qZJQ09KapHhxsmGg2UoJ5NOKM9skeZCxbT8sCimYvIDxP08Z4Qp+mpyZNfRJ5D\niE92Jl5/+6p6TrYTTrYTzrTj4MznKka2xaDMfciB1HrL6XZEJZ7KhBxQXoXKBGe5Vg2nmxF3zza4\nYr6JVGgAACAASURBVHSIY6NtNm3TW8Q3bcO2qxmblk0zp1XLbbPDbLdjWjVcUe8w9xVH6h02bRP8\nERBONRM8Qustd88nwTnPtniEiW04044xopl5nWonGJQPc4QT8w1aNVTiMaIcqXeY+Yp5dIY8FZ+t\nCuWxV+FkM8n9PDraphafn/NqOFxPOWRnWf247WqcCmfaMXNfcbiasuNqZr5i24bkiltVUHveMd+i\nMo4dP2Lb1ZxqJhhRpq7m7nqDDdNw+/wQU1dzZ32IDTvPzNKr4Ywbsd2OOC0Tjox22HEjTsWNw+Fq\nhkdISSQHv0v7TKyBjrq9R7iHVHz3BC7UXMKYzEiUuIC2QR1FyuRaMo1eTqOwoGZ11nweGELKNJsy\n7qaT/6SasxatbExEmOzEtWNq6dnE4HZllHX5TAvvu8XZWsTbwNRStl4LMm879VxilE5CokNDoMWX\nbbpcX05A2DpyssREo40WDIlJpfEoM/ImJBrbNtSTVuGU9LGycRw9NG3Xrte8AUjZfcVasBpVfjZk\nDjYmRFoXQdyoo2kfbH/gHzhzyz+svC8i/ydwm6q+XUQ+l+Vz5ILGtDzIdBs/pao/CCAi306I5/Qt\nIvJEziJc+4lmI8SrMi1eDdtqmM4njKyjjY57rTfsNDW1dYzUsVk1kZlZpq5iZMI1gA+fuYK75xsc\nHe1Qied0XDxHxnHd5l0cq86w7ZLRgKeNktbpZhIWblez42pabxnbNrYfnOkATjejwFSiE2FlXDbw\nuHN+iJPNhM0qZANOjGjb1dw92+Cj9hCjGIxzFG1C595yaj5mbFtGNlontlVwPIyM96rJGSa2Yeom\nVOK5a75JU1lq4xiZljtmW0xsg1eh8ZYP7RzFa2CgJ6J0uONGOZV8GZts2404OR9nemamYupqttua\nubNUZoOJbXNq+bvnG7Te0HrD6XbExLbMneVMM+KjxrNZNVTG95jufjgL3bkWfz1H3TiZnk4wTzwF\nbA+u9eBxQeYSdRUWvbrI8lrXYYGM6dhJZyK6kdOy9zLNQpeR1rndO/q67hieRmmoMsF2tDLZ6CDX\nlaSlnG22+GxscLpKSPWWzMCawHDT9UpQHx2YFg05siFGUU9TI/G8Tesq56SRRDt0zNoYkDYwFonn\naekzxAjUpjPqSGa8ySACQibcyiJNu3wTUFVdnT5a02owoEiGJim7sVoQK5hE5z7YeuANbD2wC3X0\nsTfuUpf/C+BJ8b3aAA6LyK+p6telAhc6puWBMShVLeNuHKKzc3kSZxGuvfWWkW3ZiJ7VV1RTThcM\nZOYrWjWZYQVv7IaxbXEaJJttNwrGE6JsVTNOt2OmrqYSz8R2FmV3NxsYPGPTYjQ5sSqTqsnlWx+i\njRtRZq6K0kWVGeFWPWfTzjlcT9m0DZU42mg4caiaMfeWqasxKFfUUzzCdjvi6HiHE/MJ07Zmsw6G\nHlNXMW2DhHPGjdlOqS+KwJKtN5ycTzgt48wkR8Yx9xYT1X+JORlRauPYsHN23ChLnx4JZX3Nhmk4\nVM1o1TL3VZZGt9vAlLbqeZbqtuo5I9PSqsn1bFZNpsur8LGdQ8ya8BpW1mfJdqueD36XzD68bKAf\n1G+o6vNj+S8lqDKeMJiIA8SFmkt6eLNnOJCvp7TmMWstgBlV2ZIvZZwNh/ltMEBIZuCReYn3XVr3\ngolkizfTLfa55dK6L5mOl1Z2lUXqqpNiIDOknP48SR8p625iSvGZkHF3YYcTy2hlYKNGmjobVSTj\nC01tlplvocsYXFr6UfwihbVifj4ab6iJZvcbNWan6dO1yKyiO4CoZjVfznhsTZfJVy1+0qn498J+\n86iMtiIijwO+q2RO8foFjWl5oAkLReRHga8D7gYeHy9f0HDta9z7cSH8oBYW+S26JeWywHourXG+\nOFc/qHsypuU9yqBE5I+Ba8pLBPXKf1TV31XVHwB+QES+l+DB/59Zrtdcacj/xl/82yixeK589AO5\n+jEPzLv6JMmk7603VM7TVoZNndN6y9xbKuM52UxovcGIcroZoSohlld8HqAyng/KEa6oZ1kF13rD\nySaoAU83o6yaajXEwwKYtjWztsJ5YaNumVQNIhokDBvUbJX4rC47NZvQRGlvHM+rzsxHuEjf3Nmc\nK8YajxXNtDpvQv4dZxlZx7StabyldRYpyt0122Bs256/kVfJqs7Wm3weNKla5q5iuw3nZJtVgxHP\n3IdrzhvOzIMxxZ3bm1El79mom6BGtC6PP5BVnAmztqJpLcYoH3zHR9h51z9S2eH8YcDEGpRyQkS+\nlWCZVAOfN5iAi4CLMZfee/pN+e6xow/l2NGHdOF3ot9NclKVsc2+NwhZihA3wswd0vponECQmqIP\nlCZfpnjAH4wVQKtwXUPCtlCtj0YRGpxgg0Nrl/mWSdUzdkAEX9t+r6Vz7kU1+BHVpqtHFdEq9zH4\nXUXpzyYaq+CLRFdPV3c3JskDPdG9y6dKo8STxiU+lxyck+SjBsyhCjNzC3UUv2ghTSVpKYREijRZ\n4a47b+Luu96/6ufehbNhUKr6OuB18fOziusXNKblPcqgVPULBxZ9MfB7hEl1VuHar/iyL6B1lrpy\nuMrx0TPdwqaRMaX3qbJhMd+pak6awFSmbU1KljapGlo1zJoKp4YywKSNzA7g7ukGALV11EV8kO1m\nlNudthXOBTVWCqqpKpyajpnXgSnOXcwNY3xOptZ4m5OenZ6OOakTNkdzrCjEcyungVFtT0cY49kY\nh5jTiWkB1MbFc+AQANPFYJiJvhR80iPUBbNonKWOZ1lJ3Xa6GfUYzNRVvfIzVzF3ltk8+ojEoJrO\nmxxEFMhMKan30nim8qowesQNjB5xQ36GF71h1U+fMWBiDVqoVfV5wPNE5KuA/8QFjmh+PrgYc+n+\nNz6xY3sC28UIpQy2akIMOdPCYvy4FA9PXIxgEKNI5Jh6abGNKdRzVIXUjnRRH1L8vpR+vouXp73y\nJUMJDEoK9Zbm1OepfTVdWnU776vhyjTpqb4czcJpjmqRYwMmeqX/fBlBo4yMsegwG+IERuZkKCJG\nxE2Ar3IUirLPOeagYVfbAD72f/PoI9jkEfn6ze//E/bC5RxJ4oJDRG5Q1WQyUqpbzipc+/bJwCzm\nCngJcYLSJEs/eIzmKzZNEMVUYecmSUVtwkyTGFU50Ki7lrYyirGJkZh9zJ7pimjK3hlcGyIJS8z2\nqd7gnbDNOLatSIwObaxSRcawmEn0pAuWci5mJA3+fvG7q2iaCjEamLTrZwXtmHTIQuqdyf0V0V5f\nxSiqm0WU6j6zLxm+9yY+H+poGpvLhUjUMDUhgnOiV1VyhGzvhDK7qqqgTvBzizoZFLgy4SN/ta+j\n7uCUExEvJRgUXBa4UHNpftjkoKxQLNQk5lA2ShcBIS1sBrwlp3bHh7A7+ZHI5HIIohT81BaSS5TG\nQnkNFnULz2d6liAx0ByWyfcZRbgfmIJxKfxSfNbSYwRaRnnwZEZZjoG30lsjcir41M+SyUcpqhdE\ntvhcjk0Ye8nx8XIwGu3qTSGbNDFK013rjc9AZYQZGBLpYuIgz6B+QkQeThi+m4F/B3Chw7Wvce/H\ntZ/cZdT98NuWOuoOSVhYLvJfwoAM2ZcQ1nNpjfOGuSQiQvZxkFZ8X77HveHh2k9H2/82SE9ax11O\nK4gX1EYpQQjHdnH34ZO0ZUCr6A/RBjlbqyIXjhDKQZZIxCjVyDGfVagT1JmubNoWSRLLBW3B+6on\n4SefvRLzSiHmoBEB/jd7bx5nS1LWeX+fyMxzqurut7vtjV7oBUVkF2QQbFAcwBfB+YyIO4jjwrig\n7wiCziuDG8g4gzCAy4AtoiDSIOCobC+bIDs0IDRLN73a3beX232XWs7JzHjmj4jIjMw6p07Wrbpd\nVX3z9/nUvblExpInn3jieeJZUkuSWcRY8sJgC+OkxKhPaJ2HpswSJ5mUxiUfS12uGS1NvVpTqqWx\nWpykIri4/MYtz8RLoWG8aoPew90zabRvVfjVbpBKcRKrlu5d2sK9LxvUMZFlLuoch9UKmhukELDr\nk55gtmqiox/UL4rIE4AxcBfNsEfbGptFS/muWspwD7v/YulJvIW2TX25SMkQp7WopCUrjRQe4KNm\nR3UHFVeoqZZSIpVb+H6CdKbud1dDnYbDRP1IwicvVeqLqp6qeRf4VrRWKcZSjxpcupFKlSYNCSq8\ni6pCP45YMIoRpMFVkukESSpONVK1GWmGQtuVxJg6ibStPYLJfZmEWXQkIkNckOMBjndcoaovbpUZ\n4CxHHw7cATxjI8k4t9SKbzMgpd9jEsdsqh8zAa10Ce68TnTjPzTrNyTzxNVjPGcoFVJ1zM2oc1YU\nv7lpFC2F/PgACq+jLsQxhFglaHDPxx90OPBMITACRzgKi64fRsEOXDtlYRzjsFKrvkTriUCp+lmW\nLtSx+glePAMl0YihBUU1mFLqtsfGMSn8t20Ua7RJOEZQq7VjuuL6ZAVrFB26G6pNktDAVNWp9iq1\nXmB86hmlFaQQtCtFhVfdQTXRIWHhr6yv1Xsf1IANKqpo7yRo2aT0psjRpB7vP4UJMqj32pNpxVyg\n+V0FBhgxkZiZhL5VOZ+COixmTKb1TPzJJxHTtb6I+Otaj8uEHE5St9lApNYkLCJjZhb6qG7xZ/xe\nlUZawGp80bgmMROnUmwyzIb6MYmer9ePTWYXGGZHeppFR6o6EpHHq+qSiCTAR0Xkn1T1k1GxnwYO\ne9+7ZwAvw2ksTgg7nkGZkbhVTgoa1kyl+B82+mVyPJNxKxnxm7wApgg68FqPq0bRDM+0/P+BeEv/\nTIANX5xnDPGHEq2sGkzCM7yqnxHTUcElIYsm67DSA1AjJKVjjOoZoSaOqaJukjeFIHnoS/Dr0CbR\nVZNG3f96ReokT009kxL1k45jxlK4vwBNFR0Z78mu7vk4m6doNdNp4u97plTpyD2zMoX/fTqiS9lZ\nCQtF5FeB/4T7Um4Hnq2qN66q6F4Mk9Ncbrfmq8Bg4mR8MYNKiO5H339lZNDeY5FmXeDqKTOBdPUk\nXhlm+Hqk8HSvQBExutDf6LhKseHrC2krxNfrEhf6hZM3UjCBbsM4gnFC/I4ixjRJ0qusDyOpqO5g\n/VwYT1hDh/2xxh5Z1Jf4XEVI/f6Wszys9/fWgy50pKrBgX2I4x/tUT0N5ygOcAXO//CEsc4h9Oix\n/WBKrf4mIXLUfSIuKOyPiMi3tIp9Fni4qj4EeCsuZUCPHqcMYjpai5ZE5HPArcB7VfVTrSKVS4eq\nlrj8ZwdPtE87X4LKBVVnSWfU67tLp4qzIZ2yqV+2lG6VrgaSkVSrsLCKMwVQuJWIVa1WJOG5xirK\nr2ZM3lBsN81AvZqvlnZwKjfctZBATQ3owEJuVuudqduRXKI01s6i0O3Z+KRsflVqxl4KiVUvhVP3\naRKpYfASZdDnJ17KDGpCG1awUklaJnf1Ay71tJeGNBEncSH1orhaUQZfD4XSYLxqstoTE2/GW8q6\nzV1ltoqvi6Puh6LyH8flajqlkC0683BTUKd3V6f6CqbODZ+iKoGgL1fU/kpxRIpqj4emVFOZYhPo\nSbCZa98GFVYSl6eyagtlNKRst15F5fvYMG+vaKw26Q40MNE6L95YI5IIodaGBO10MO9uaDhq6SU2\nSZcymM1DsMKrTOL9OwhWh3Gb4VosuTlfp9B/W1v7CVUyw1iD0wVHDl3NkTuuWbOMqlrgoSKyF3i7\niHyrqsbRItqtRTLf+rHjGVS6DLGSN5jJ2kQIaYs0EWzmmULpJm5TuonWppGOPVJPJKN6kraZ+4Na\n3SDRx1+rCmtxPXwYlRlq+JjdU3Vd4kVrEcqBqeppqAhN3KdQL2CkNusN4nmk0642soMJvXWMxZT1\nhFGpOsKGr0pLHVITYBi3yWvCS0YRg0ugHLpJpnoPNInUVQqaasMPJjCwZEXWbU3UIVV1J0fdCD8N\n/NP6erHzsXC7dwwN00kiYLXKngs0kgnaRLyDrbgyuXWa3ER8qufmpLrKjym6HszNTR7TCRUjcMeO\nMQbfoNg83YSMto0kgzTqqQK4eroxuVbOstio3piGw+OVk3FT7e4GWRes+u4dbmMGIXldrmJm6eo9\nV0fP3v+r5TsVTPDDIkBKxeSWKtJ76ENwAo6SJ87CwQMXc/DAxdX5jV9939SyqnpURD4IPIlmOKMb\ncb53N/t9qr2t+HzrwlaHOvolnJ9GDvyDqr7AX38h8GxcKuznqupE22GAYM1TTZqJX9UPqT+MEpJS\nKgbk8xKChaTw90fRJOqZRuIzCgQrmbV0umGCrvapWvUDtHXIYWNXrF8RRquehu7bb5aGiV5T/83l\n9bfXsPxRCFkrKks9XBsmWDIKFSMxYcUmdX+kdP/baNwVI/TvKBlFzNS/pyBlOq9/fz9sgCf1iljz\neAGBW0AYbxxihKR7KD6k0JlFJlyb+JCI/DjOAumy7j3YemwGLQ0P+5ceYtcZP1EWto75FoKReokn\nTNghcoKG4KQTVu+Vf1OIquCjM7hFUB0BAcAUtorn5xx7WxOtjeqJs+SCX/BoVG/1khrja8S3C9l+\nI0kpRJrA4o2eTF0+1BOPUaQpEVWOvz5Wn60lxlX3q4y4Ieuw1tmG8e2b+r2GRJBqDBISOgaz4Ha0\ni7Qbh5pFRyJyOpCr6hERmQeeALy0VezvcRawnwCeDry/U+NTsJWOuo8Dvh/4NlUt/OARkfsDPwTc\nH+dQ+T4RuVR1ct7idKme4FVAxmBzMGM3kWMh8cwrSBmxqWo5cBNkMo6YRU4dksRPrDZpTrCxOWjY\ntLVpPUkHU9ogDcQMp6ojnjZDfXjmGDOSsOEZGFruGUiQpjJnfVXNCYGpRarLUL8pawbEkrvfMM+N\nrZRwEmq4brP6fVbjtvV4fdotzJhq8xkN0p67V0l8gQF66TREENDEPR8WB11w3demr188OjnqejPz\nFwLfpap5+/52xWbRUnJkqTmJB4TvElzEcfxk6QOcxuF73DcZhTmK++kDqFaTqaWStLw3dz2Ze6YI\nnrFFE38VRLZSUdtmf+Po6SHwa8zgvKVuFcA21BFP8DHa/iGh/SRpFvNBcxuMOFwXH9apsFUQWB2G\nXFi2es+NdPJx263fo1owhGC4UfDYxrMtprkWOqjKzwZe7/d0DfBm72v3Ymp3jdcBbxCRrwN3sgEL\nPthaCeo5wEtVtQBQ1Tv89afhUgYXwHV+oI/EceQePVbh4gvqdDNTwrl0cdR9KC56xBNV9c6T1tmT\ng56WemwYJuTjmgJV/SLwsAnXXxQdj3CLok3BVjKo+wHfJSK/DywDv6aqn8HtF3wsKhciME9EulJL\nEpXxg5eCwmZprLICp3v22TmcztkIZUYdEqX6nZxBgU1rNZnNnBRgimadolrtxQSJrqpfagOGxn4P\nXjpKaagepXRSVLXpnEA5kGo8xuuyw/5WYp3dRSNGWEatftBIC+HbScbqVXhSlQ1qymCuG2KPqThT\n/pCYNx57WLGJVyVWizVT1xHUhOG9hJhomoKOa+nU/TZMDWMzDbNWfh0ddV+GS1XxFp/2/XpV/YH1\n9WTLsCm0JIszxFYjMMJJAyEbrJeoJKiV2tlcw3EsPXm0JQ1CUr6WFCGxyique5J0EbcXlRVj6v0m\nqNSHlcQyrd9AlSyxjTRpPFcnRgzP+VsVLWhjjDIqWtKe1ONvjzEek0/1IYU03u3EdxHeXwd0kKDu\ncWxVNPP/6tver6qPEpFHAG8BLoLu+wUAt/3zuyo13J6zLmbPOZfU+zG+tcpzXdyk7/arIgKw6qT1\nKkeNK+ty9YnTDHiLIx3RCIQZrGbKzDGVNNdKp2w8k6ms9IKaKw3qEMUYzyCknvRNrqQjxYxrqyg7\n8M8ElWHqXkuwpFIDxTB81K6u2C/FOSTXTC+MweTAcpORQK26UwNGFRnBsAgql/pd2WARKIHpab0f\nB5XlVGzp5DaFA+E1GdTiDVdz5La1LYnakBkrP/fzzXTU7RqMdUtwT9DS1+/wgXlVOTh3H06bO291\noTCR+lAokscTZ5gkW79HCB8S7omBxDQnzpBht2w9G0+8VRRw07wXT+aTJuqQfyok7ps0YQfGEZ/H\nDGoSEyx8fYHx+USB0mYu1Ri1meTQqnsfIeNtvMcVv7O4jeodl5Prj/p+5/gmDq/ctMYv3hpSBzq6\np7Fl0cxF5OeBt/lynxKRUkROY52BPS+62DVRzLkoxbKs1YRp8npVEKyEgqVP2xLHFFpJOFSSl4su\noSYYGNhayvL12oFxzCmY4kLDCdBJDVoZDbj7kUhDPaGH4Jah36bwm6qlwrLvf9jIFSprJJuZeu+r\niExmA1Moa8aQ5LW1T1XOW0SVQ9NwYgxIRuqYj39Hod2QfiFOgWBKGputmkq0x9VaACRSSY9hX2pu\nz0Wctueiqv1bPj9zf6lOhbBWmdmOuo/19x+EC8/ytpmV3oO4J2jp0oWHNye9PG8ygbDnkluq7LGu\n0fqZOH7XJAYSM5mQDt5FH6YKUdJmXJGxxCojhXb5cN0qqhaJs9mKNNuPz8Me1KQ6XVqA+ty0xmCj\neiYx2EnMMh5fUXiGF+o3zbKJcdJYLF2FestytQTo995OS87mtF1nV7/JNUfX1ux2pKPX4WJVHlLV\nB024fxnwDuAb/tLbVPV3Z1Y8BVvpqPt2fK56H+hy4HX/7wSeISIDEbkvcAnwyenV9DjVIYWt/ibe\n7+aoez3O+uivT2ZfTxJ6WuqxYcR0tIY0dTmOjtbCh1X1Yf7vhJkTbO0e1OXAn4vIF3Ga7Z8EUNUv\ni8jf4mzrc+A/T7M6AkhW3KonWfYr9OB/EWsVBETFp2OmIYWo38cxY3Wx+7yEQBnC9EeqQC89mcKZ\ndaoAS84XxFYmuNR12LYDINUKqF0OqPq2yv/B+lWTUPmlALVfhpdkyqEz5RNL5ZOyKnFaZOoazFxt\narBAulQ2fUla45aWpVBQZVbmuMT11qtETaW6HqcwSMJK1HuuB9XhemPxdVBNdHHUvcHf236K+NnY\nFFrSsd8EjfdCYsQrfyNOOvFlta3WmwBpqK3KWqqJJJ6JqFRXrT0kM/tDqeqc4CsnSdKU7oIGJDwT\nS24xKqdj9Zu/E1SMdR6buDOtDkSSUCxdFhMcAaftI0VST0NijAbc5beBzqryj3hjozWr6tRgB2xl\nNPMc+Ikp914CvKRLPdlR57sRMnWGyTCgPTmbov4YgrNbUK21nfwCg5o0abtglC19t6+r9kcK6kWJ\nfEjCPZofpdYOd8H8NtRVMStvGlsPLqjMHGNOBkntkxKp78RvqlbPe9+I0C9jtYqGUWUZjd9bi8jE\nqqurLBsZTdUY7wfmjiv/ixGrP1kJkTX8Jntpnc9N9M46o5ww+zSxXkfdHYXNoqXqPfr9SW2F25dY\nJRd+b2iq6aBWUbWeX8UZzRQFTjvMf9W/1rmaumxMS21M4ck6KWtzWTbGLXEfW/WrMS7wc9xO+x2E\nPbtpmMYE43uh7kmqzJbxhMZtV/V03FuaTUdd8SgfDulm4HmtSBPrws6PJHHXkpugs6RiAkDt4AZN\nP41w3Hb6i/Xf0cQv7RVOW58+Qf+ridSSho2YThwCZsqzjbaqD6/uqkS+GmEc4s/NIg3HPsBLer5c\n5MwY/FiqPkcff8Oqqt3XePyB4QUJMDGr+tRAzGCj/sflJC+nTzRTcPUtH5pVZFKFO1FSOqnQcsLG\ne4AIam01YWtZQlk2JvBqYp9WTzzhTpu015I42vdnTajThcX6+Sn7V9XppL2n0BW/J9dm5A3LvsDU\njVnNKKaNb1a/p5RrvP8TwCYZSXwGuMBHPH8yTv18vxOtbMczqB49Lj3w6Or4mjs/OqnIejPq9uhx\nyuHOo9dyeHljAfxV9Xh0/E8i8hoROaiqh0+kvp3PoI4tuuVxMFudsvqo1j62TtneMA9dZWljEI3U\nB21RPLaoaa+sorYmWh5N8qkAt/KxdrrqY9KY1pI2vAQnUNdpvIlv69mGtVPcf7XNPieGYCZcWwyJ\n04Xnhas3ti6KTV9bJsMz+98V+cygDzMddVvYNB36jkLbXDzARGq9WKWGl6RmIXxHMaY91javXt3J\n6bemSWXT6G1WfdDsd6vPWpZ1f+Ny1RgUjbUQE+tvvfNQT/we1hrXCY15Mk7Lzua07Ozq/Jq7/mVa\n0bCDvvqGyJmqesgfPxKQE2VOcC9gULro05N0EYvXwlq640k/9lobyAFmCgOYUJ+qXW2i2lV3PAlt\ndVzoY1A5xNfrTjb73WLK1aZ4XDY+9kyomrSqWGZT/EhgdX/WwaArFGtPkl0cdUXk24G/A/YDTxGR\n/6aqD1xfR3Y4WsxGK2fW+LeeMCG378Hqb0hM4xmdMoGKmTTRNzo1ue9QM5D1PtcoNn0eCQk7Jzfa\nvCaVAdDUylb1q1l3/aCsZTW0adtGzKQjABF5I/A44DQRuQGX+2kAqKr+GfCDIvIcnFHOMvCMjXRp\nxzMoe3wRoPog1vrAZqH6qGau4iZg4gqKilAnxSZrPt+SrKYR1Ky+rUGIarUe47Q619jQnflmQ4yx\n9m/QcXI4YUyyemqhg6Pup3FRmE9ZTKOd+HqDgawuGApNXtRFTGryZN/6RuP6Wt+QrkkrrYl2wvON\ne5MwobyugxnMLhv2vNfo23rbPZF5K0Y3OvrRGfdfDbx6Yx2psZXBYh+Ei322C7gO+LGgv1xPBOY7\ni1s4yOlTQlN3//jATcCHuYOD8k3T+93Juqz+og7b2zhoptc3uXvTWcFhvW3N/sHafWx/7Ovt35oL\nALWd+ldhowQVmu1AWB0cdQfAX+Iimd+Bc9a9YVM6eJKxWbQ0Uyqi6wKw5LA9xEFz5uyiE9pfPSGX\nE7/T7ovRyTP8ur7VDjix+qZzn/XX16yr21xVowsd3dPYSkfd1wLPV9UH41QrzwcQkW+ljsD8ZOA1\nskYwqbv0tk3tVF/fBuvj9k2trxPyov6bgI6Ouj8NHFbVS3GM7GUnscebjU2hpc3Edv9OT0ad1cc5\nOwAAIABJREFU272+mYjpaAot3dPY0mCxqvoRf/w+4N3AbwFPZV0RmNde0a8fEzZ047vr1Pmq2m4b\nyd1r3DyVACejf+vAJqn+Oqz8Zjrq+vOg8rsCx9B2CjaFliZLJNFeyHpW5Fp/pxtRu9fVafd6On9X\nLVpaj0Q/sY3ZtNkJG+6Hq2O9772XoJr4VxH5fn/8QzjTX1jtVLlmBOYePTQvqr8pmOSo2/6mqjKq\nWgJ3i8jBze7rScI9Qktqtfuf1sf3OMR0+0Na5xtoYzP7uBn1nABiOlqDlu5RbFU089/E6cX/l4j8\nFi5m2Dgq08bUr/xaruJavWpzOhzq7OvbVvXNwKH35m+Kv7FDE8p0+aZWxbqYUGbLcE/Q0vvs325O\nZz2utSccQOAeqe9k1Nn52+/4ZZ2MMU/B9e/N33RB+9o91fg0bFk0c48nAojIpcD/46/dRNOaaqpT\npep6o7b1uLdBVc/qUKyLo+6NuO/uZhFJgL2qetfm9HLj6Gmpx8mEql641X2YhC1T8YnIGf5/g8tp\n8yf+1juBH+4jMPfYRFSOut5a74dx31mMv8dFMwd4OvD+e7B/G0JPSz3urdjKPagfEZGv4iIt/5uq\n/gWADywYIjD/IzMiMPfoMQt+Tyk46n4JZzhwlYi8WESe4ou9DjjdGxL8CvCCrentCaGnpR73Skj/\nvfbo0aNHj+2IrZSgNgQReZKIfEVEviYiv36CdewTkbeIyFUi8iUR+Q4ROSAi7xGRr4rIu0Vk3xrP\nv05EDonIF6JrL/P1XSkibxWRvdG9F4rI1/39f7+OOh8sIh8Tkc+JyCd9Wu9w75W+zitF5CGtuu4j\nIu8XkS+LyBdF5Jdb939NRGxsrbZWff7+UEQ+4fvyRRF5kb9+oYh83L+3N4lI6q8PRORvfJ0fE5Hz\nu9Tn7/2er+9LPlRRpz72WB/ujbS0mXTk728qLfV01BGquuP+cIz1auACIAOuBL7lBOr5C+Cn/HEK\n7AP+AOf0CPDrwEvXeP4xwEOAL0TXngAYf/xS4CX++FuBz/l2LvT9l451vhv49/74ycAH/PH3Af/g\nj78D+HirrrOAh/jj3cBXw3vCbZi/C7gWOBjVPbW+qN4F/38CfNyXfTPwdH/9j4Gf88fPAV7jj5+B\nU6/Nqu+RwLOAv4jKnL6ePvZ/pzYtbSYd+eubTks9Hc3+26kSVOV4qS5ZW3C87AwR2QM8VlUvB1DV\nQlWP+Hpe74u9HviBaXWoc468q3XtfVqnsPw4tU9K5TSpqtcBwWlyZp24jFBh9bkf588S6vxL/9wn\ngH0iUpkiq+qtqnqlPz4OXEXtB/Ny4Hmtdp62Vn1RvT5CL0PcJKHA44G3+uvxe4vf5xX41OQd6nsO\n8NtRmTvW08cenXGvpKXNpCN/fdNpqaej2dipDKqL4+UsXATcISKXi8hnReTPRGQBqMLFq+qtwBkb\n6OezcZvTk/q8HqfJXwX+UFz04JcBL1xvnSJyIW5F+QlxTp03quoXW8U61SciRlzGzFuB9wLXAHdH\nk0n8e8x0gG3Xp6qfAi7GWaB9SkT+QUQuXu+Ye3TCqURLG6Yj2Dxa6uloNnYqg9qMDKkp8DDg1ar6\nMGARZ7m1KVYjIvKbQK6qbwqXJhTr2tZzcIE+z8cR2Z+vp04R2Y1bdT0XF7vmN6nD+jSKdqlPVa2q\nPhS3on0kLtbbtOdmOsC26xORB+BWgUuq+ghcrLnL19PHHp1xKtHShujI92XTaKmno9nYqQxqMzKk\n3oRb+Xzan78VR2SHgqgrImcB647YKCLPxOm149D0nZ0mJ+CZqvp2AFW9AgibuzPr9JusVwBvUNV3\n4FZUFwKfF5Fr/TOfFZFvWm8fVfUo8CHgUcB+qZJeNZ6r6pQZDrBRfU/Cre7e5q//HRByM23kPfZY\njVOJlk6YjnxfTgot9XQ0HTuVQXVxvFwTXvVwo4jcz1/6HpyPzDtxG4vgHDffMaOqRnZJcWkdng88\nVVVHUbn1OE22M1b+m4hc5uv/HpzOPdT5k/76o3DqgXaonz8Hvqyqr/Dj/ldVPUtVL1LV++I+1Ieq\n6m1d6hOR08VbY4nIPG4j+8vAB3AOrtB8b+9kDQfYKfVdBbwdr2cXkccBX1vHmHt0x72ZljaTjmAT\naamno47YbKuLe+oPtzr4Ku4je8EJ1vFgHIFeiVtl7AMO4iJCfxWnF96/xvNvxK06RsANwE/5/lwP\nfNb/vSYq/0KcxdFVeGuijnU+Gvg0znLpYzgiCOVf5ev8PPCwVl3fiVNDXOmf/SzwpFaZb+Atj2bV\n5+8/0NdzJfAF4Df99fviomR/DWeJlPnrQ5yz6NdxG90XdqxvH/B//LWPAg/s2sf+r6elzaSjk0FL\nPR11++sddXv06NGjx7bETlXx9ejRo0ePezl6BtWjR48ePbYlegbVo0ePHj22JXoG1aNHjx49tiV6\nBtWjR48ePbYlegbVo0ePHj22JXoGtQ0hIse2ug89etwb0NPSzkbPoLYneue0Hj02Bz0t7WD0DGqb\nQ0T+u09A9nkR+SF/7TIR+YDUCeLesNX97NFju6OnpZ2HdKs70GM6ROQ/Ag9S1Qf6AJSfEpEP+dsP\nwSVuuxX4qIg8WlX/Zav62qPHdkZPSzsTvQS1vfGdwJsA1AWg/CB1BOZPquot6mJVXYmLqtyjR4/J\n6GlpB6JnUNsbk3LABMTRnUt6abhHj7XQ09IORM+gticC8XwYeIbPlHkG8Fimp+jo0aPHavS0tIPR\nrxS2JxRcgjGfq+XzgAWep6q3iUg782ZvqdSjx2T0tLSD0afb6NGjR48e2xK9iq9Hjx49emxL9Ayq\nR48ePXpsS/QMqkePHj16bEv0DKpHjx49emxL9AyqR48ePXpsS/QMqkePHj16bEv0DKpHjx49emxL\n9AyqR48ePXpsS/QMqkePHj16bEv0DKpHjx49emxL9AyqR48ePXpsS/QMqkePHj16bEv0DGqDEJEf\nFZF3bXU/tgoi8hgRuWqr+9Hj3oHNoCcReaaI/PNm9WkrISIXiIgVkVNyru6jmfdYF0TEApeo6je2\nui89ekyCiDwT+GlV/a6t7stGISIXAN8AMlW1W92fexqnJFduQ0SSre7DDkK/oumxJnYyPe3kvt8b\nca9mUCJyrYi8QES+JCJ3isjrRGQgIpeJyI0i8nwRuQX4c1/+KSLyORG5S0Q+IiIPjOq6j4i8VURu\nE5HbReSV/npDneDF8V8SkWt82Zd17OvPiMiXReSoiPyriDzEX/8WEfmA79MXReT7o2cuF5FXicj/\n8c99TETu6+/9sYj891YbbxeRX/HHZ4vIFb6P14jIL0XljIj8hohc7ev9lB//h3AZSr/grz89vEv/\n3K+LyFtabb5CRP7IH+8VkdeKyM3+/f+OiLRTcffYpthJ9NTq9x+JyA0icsR/y4+J7r1IRN4iIm8Q\nkbuBZ4rInIi8XkQO+7E+L3zj/pm1aOcRvo0jInKLiPxhdO8xIvJR/z6uF5Gf9Ne/T0Q+65+5XkRe\ntMZYTi0aUtV77R9wLfAF4BxgP/AR4LeBy4Ac+H0gA4bAw4BDwLfjJuGf8M9nOEZ+JfCHwBwwAB7t\n23gm8OGoTQv8/8A+4D7AV4Fnz+jn04EbgYf584uA83AZj78O/Lo/fjxwFLjUl7scuAN4uO/jXwFv\n9PceC1wftbEfWALO9OP7NPCbQAJcCFwNfK8v+zxc5tFL/PkDgQPR+O4b1XsZcIM/Ph84Duz25wa4\nGXiEP3878Br/Dk8HPg78zFZ/J/3fvY6e2nX8qO+vAX4VuAUY+HsvAkbA9/vzOeClwAeAvX6sn4++\n8Vm08y/Aj/njBeCREW0cBX7IP3cAeJC/913AA/zxt/n+PdWfXwCUgDkVaWjLO3BSB+cI4mei8yfj\nJvzLgBWcXjfcew3w4tbzX8FN9I/yxGYmtDGJoL43On8O8N4Z/XwX8EsTrj8GuLl17Y3Ab/njy4E/\na43vy9H5dcBj/PF/At7nj78DuK5V7wuA10XjfsqUvlrgoui8YlD+/MPAj/vj7wW+7o/P9O98GJX9\nYeD9W/2d9H/d/nYQPTXqmHD/MPBAf/wi4IOt+9cAT4jOf5qaQc2inQ/5Ok+bUOatHd/zy4H/4Y8r\nBnUq0lDKvR83RcfX41ZEALerah7duwD4yUhcF9xq7xwckVyv3Tcpp7U5DefhiKKNc3CSVYzrgXOj\n81uj4yVgd3T+ZuBHcCvdHwXe4K+fD5wrIof9ueAI4MNRf07UCOJNvs2/8v+/MWozA27xGgnxfzec\nYDs9tgY7gZ4aEJH/gmMyZ/tLe3DSR0Cbxs5ptRnfn0U7zwZ+B/iKiHwD+G1V/Qem0zgi8kic1PZt\nOGlyALxlQtFTjoZOBQZ1XnR8AU7lBKs3+28Efk9VX9KuQEQeBZwvIqYjUZ0HBNPr86M2p+FG4OIJ\n12+m2f9Q31c79AEcs3i3iPwBbuX3A1F731DVb57y3A2+P1/u2E6MtwB/KCLnAv8Bt1oOba7gVpa9\nocXOxU6gp7itxwLPBx6vql/21w7jJvaAdt9vxqkTvxK1GbAm7ajqNbjFICLyH4ErROSgf+6RU7r5\nRuCVwBNVNReRlwOnTSh3ytHQvdpIwuMXRORc/5G8EPgbf729sfi/gZ/3qxlEZJffvNwFfBKnF36p\niCyIyFBEHr1Gm88Tkf0ich7w3KjNaXgt8Gsi8jDf9sX+2U8Ai37zORWRxwFPwTGemVDVK3F7VK8F\n3qWqR/2tTwJHfb1zIpKIyANE5Nv9/dcBvyMil/j+PFBEDvh7t+L2yKa1eQdOzXE5jpC/6q/fCrwH\neLmI7BGHi0Rkx5sCn2LYCfQUYzduf+xOcQYdv4WToNbCW4AX+jbPBX4hurcm7YjIj4lIkM6O4Jhf\nCfw18D0i8oP+mYMi8uCoj3d55vRIPIOLIHBq0tCpwKDeiPtRr/Z/v+evN1YgqvoZ4GeAV/kV1tdw\numz8Ku/7gUtx0sWNuM3OaXgH8Bngs8Df462apkFVr/D9eqOIHAX+DjjoVSZPBb4Px2heBfyEqn59\n0him4E3A9+AIJLQXxvMQ3L7CbbgJZa8v8j+BvwXeIyJHcAxu3t97MfCX3sLpB6e0+cZ2mx4/iVNf\nfBm3D/AW4KwOY+ixfbDt6amFd+P2eL+G+9aXWK3Sa+O3gX/z5d+D+05Hrb5Po50nAV/ydPxy4Bmq\nOlbVG3F0/Gu4b/9zwIP8M7+AWxAeAf4rTjUfI363pxQNbbmjrog8F7eBD/C/VfWVfrX+ZpwK4Trg\nh1T1yAnUfS3OYe/9m9XfDm32jqzbDCLyOpzkeUhVH+SvdfrGRKTEWXEJbt/kB9pltgtOJi35+k9J\nehKRn8cxmsdvVR9OVWypBCUiD8BtXn47bkXyFK9WegHO4uybgffjVAk9epwoLgee2LrW9RtbVNWH\nqepDtzlz6mlpkyAiZ4nIo70K7ZuB/wK8bav7dSpiq1V89wc+rqojVS1xljD/AafWer0v83rqzf31\nYivEw4ltinOcPSbOwfVodPyae7qDpxpU9SPAXa3LT6PbN7ZTnCBPNi3BqUNPA+BPcX5L78Op3P94\nE+rtsU5sqYpPRL4F53j273A63vfhnOB+XFUPRuXuVNVJVi09enSCuJhmfx+p+A53+cZEZIxzKi2A\nP1DVd9xTfV4PelrqcW/ElpqZq+pXvAn0+4Bj1BNBJ4jIKWFqeapDVadKMWedkeihOxqWyodUdTM3\njc9X1VvFhZB6v4h8QVWv3cT6NwU9LfXogmm0dOF5mV5/06rP5XpVvfCkd2oNbLkflKpejtsjQER+\nD2dhc0hEzlTVQyJyFs5SZiLuy/25WB6waf25Rr/U17eN6nufXrHm/UN3WJZuvrA6XzjnujM7Vt3p\nG/OmvajqtSLyQeChOOutbYeN0tKT7v9CmKZRsROuTysrwtfv+AiXnv6Y5nVz4trSr9/+z1x6xmOn\nF5jUvxltVnVOCWWnk67HmyLhvv//6ls/xCVnXda8N6u+dp0Rrr75g1xy1uPq5uL3Pek4vjbBu+zd\nX/79yQ0B199UNOgIYOGc6y6Iz0XkPsBf4qwGS7whzqT6ROQRwMdwRjknvH+35QxKRM5Q1dtF5Hyc\nzvzfAfcFngX8Ac40dVuqVXpsD4y0k6AQvO4D3smMb0xE9gNLqjr2vi2P9uW3JTZMS0U5eeJrT6xr\nxSZVrf/Ksr4+jYEEtBlJu3xRwspo7b6FlElG6uuxwDCt35MYrUiTIQSU8Yk2ny0tMm59i9E4ZBIj\nmQTfTxkXmMWVtctuIjrQUQH8v6p6pYjsBj4jIu9R1a/EhcTlrnopzrx/Q9hyBgW81Tv95cB/VtUj\nXlXxtyLybJyfxNO3tIc9tjVWtFzzvoi8EXgccJqI3ICLlfZS4C3tb0xEHg78nKr+LM7w4E+9qbkB\nXtImxm2GjdFS4if4ScxkkiQST/hh0g3PSnRftfl8KBMYichq5hEnvVAFYyBNm1KLaUow0JJSDE1J\noi39JAbNoobWkAgnlrGVB60bkwKlXV2uXW88/rh+aTNTqX+T6L6K1GMxLfErLA58NyYy2SmYRUde\nmxA0CsfFJSo9lzriRsAvAVcAj+jc+BRsOYPSCUnFVPUw8IQuzx/gjE3tT1/f9qqvC1ZmEKGqtj3z\nA1Z9Y97B9Gf98ceonSm3PTZKS5uJg/Pnzy60rvraEb82oc5dF8wutJ76FjZ5zJtc3yzMoqMYInIh\nzp3hE63r5+AsRb+b6aGdOmPLGdRGcVC+qa/vXlxfF4ym21D0WAc0rMb9f6tUUiKrj22k0gtlrXJa\ndjbkeVMaCxJTkrj/s7QpDQRMUCkenL90tb25yOTnJ+y/rNq/EeHAvotQQKytrjXab/cjlKnqavbo\ntLn7gLX+ndjmM+BUkCaSiiapTiOp6uDe+7oWzJSy+N+sfb/UaszrsdL+8L+M+eTHxjPLefXeFcBz\nVfV46/YfAb+uqip1QNsTxo5nUD16rGifBHUzoAM3HYTJXCftmQR1XLzXFDEEUXX7RVbcRG1wk3WY\nnI1xqjVjIEtqBtNSb61S1YVr0iyr1T0Qq0ipiFUo1TGeUE5Mo05RBWuRsjWBT2LG8b1Sm4w71GlC\nUHOa6slqDDXj0aSl1my/x/A+jKnLxn1ojL+5qEAEUv/7hUvtMU7Bgx+1wIMftVCdv/qP2rwHRCTF\nMac3THG5+Hbgb8Rxp9OBJ4tIrqrv7NSJFnY+g5L28mkKOkf232J0Hc8kbMUYN9LfLuhAWyu68z/j\n7QC7kCHWr7ptLVlI6SbziZOqCKT1dVWFRNwzQcICMOIm08RPvCLuODyX+PvGT+ChDRHUtCZoABFs\nZioGhSpiweQWk1swimJWfT/i+61WqqV9JYVAPe7AvEq72iDDtKSXtpDg31ODeYRrniGrMav3h9p7\nR2H8benJRONPDIjbl5q432Q9w+6AjnT057icc6+YdFNVq0DSInI5zvfwhJgTbH0kiR49NowVzaq/\naRCR54rIF/3fL08p80oR+bqIXCkiDzlpHe7RYxsipqNJtCQi3wn8GPDdIvI5cWnqnyQiPyciPzuh\nyg371p06S8+TvdLfDtiBY5RZvjEdhMJZK79WnLoCeJeI/IPP3RPKPBm4WFUvFZHvAP6EOpfVKQGb\n1d+PKEhhnUQV7Wng1WYS/y4iaGqaqqfWan7iXkkkIWkiaGJQ4yUBpZreJOxzVXW5Z+zQYBPf1xxA\n0VQo02SyRGGbdYlNmuOwihTW6Q0LC1LW5uaxNOjHq8OssQdWSY2xtV3SVE06SdH/pV416ccqpSKl\nrSQesXjJsP0uIzWhKiC1EOdPK6krCQ3Mxiw6UtWPMlmBOa38s7uWnYYtZ1Aisg+XzuHbcNPRs3Gh\n8TtFYJ45wfXY3mgz1RNQU67oYFaRKk4dgIh8COcn9IdRmafhnBBR1U+IyL7g4LruDm0RNkpL5Xzq\nJkm/16KJn4uqzQyvihPBjEtMofUEGtRTBjRNsGm04R9PmiZSR1mqSTpWHYp199V/G6awSOnuucnZ\nYDOBUklyxxBCv+PJHyI3KHETvhSKKSwqQjkX1GNgcnXjwakJpbBIEe1jtT9Lz1DB8bOghhTPaOJr\n4boK2GGCHTjVpEa8Q6v+Gfc+I7Vl2FdrMGrP2MuhWxiIVawx7hnflq5zbuxAR/c4tpxBAa8A/lFV\nn+434HYBv4GLwPwyEfl1XATmF0x8+h6QGtZigho74p0gs9SOOuJZ7ay3nq3G5HG0FmgdfHBX7HTV\nnse/Ar/rU0+McHl5PtUqcy7NPEH/5q/tGAbFBmlptD+pJkRTKKYEm4BNpbEIV4F0WTCF+kncrfzB\nT4peYrCp8QzNP5iIY1weNvHSQApS4Nt0k7KKa9embvI1hWJyJyHZSPIwhSK5N8aw4idxHDPLXPvl\nQLBpzSRMkTiJRamMK5xU5J4th8aNN7yHwo8tkoaqyd8zBJsEaQk/dgHfP5OrYxiZ77vx787Ta/VO\nqgUANUO1WjGbMF5Krd5lmckqAcmm3niEWjrrgll01CWShI/+fjnwMOA3VPV/dmt9MraUQYnIHuCx\nqvosAFUtgCMi8jTgMl/s9cAHmcagepzyeNMrbl3zfsc4dZO45Y7h+D0t9dgo1trD9egSSeJOnKPu\npqSm2WoJ6iLgDm/t8WBc9OVfASrVig/UOdX7Uyo1xBTV0EmWsCRe8E/yll9vHRvtS3gP8bhPxruZ\n1M6k+9Owib/Lk3+xzkD/jlfdNLk7k+PUxbgJiL1B7wPcvGmdPPnYMC0VcxJZhNGQZNRQSVUAdiBO\n6imDaXctkUCkujJQZoLNpLK4s6mTnsKSIEglTk3nnrGZk0BCH1w71PeTIFlBMvb3rFf54aWvRCiH\nQjHn2hbrxhC8EsoM365g/FiCulEFkpGToESTSlIK/QlSkk0ETVyfTA6aUElsAYl3LQrXXFuASqUK\njCU8pyb047KuTPhtTIGTDr2lo8kVa2rJK7RfSVClG1cXzJKgukSSUNU7cN/hUzo1OgNbzaBSnCj4\nC6r6aRF5OW5113l2v7r8gjtQOJicycH07JPRz6mQdqiRgA5MR+2USbzl67BWu6vrmNCfWY6sM9qK\n26zbW4PBqDLxBXRo5y57iMPl2hJRGx1WftPi1MV4Jy719ptF5FHA3Ttp/4lNoKUbrnp3tSezcMEl\n7DnnEkzh1UUCJheSkWMExbyp9k2CKivJ3aSufsIsB63JPKieTFDv1c8DCOK0cynYzKu4PEOUIqi4\n3LlNXRum8AzGtx3UkuVQyBfqepIxJCO3brL+Myzm/b6RdcwlHbm+aOLqL4e+87jrSQ7psmNaNhXy\nXVLtJQGYLGLoadgHgvE+P9bSteOYjNuPC+8kMDnHjJ3KM8ml2vvSNIzZvSkzhmzJcbTQXxXBDur6\njt94NUvXXd1dxdeBjgKmRZLYbGw1g7oJuFFVP+3P34ojqs4RmC/d5cM9xRN1zDQmOdxNwjRmMQ3G\n1M8YU7czaRJutx+CQTbierXan8b4oueBOhZYu464f5P604Uptfsg0tSDrdX/+H7lPzKhT602Dsp5\nHNT7VOfXjD8/s5uj2XtQMDlO3c8Bqqp/pqr/KCLfJyJXA4vAT3WpdBthw7R04IlP8vsrbhK1XiKo\njRz8uXXMoph3E6MpHPNKl5xUUs4J5bCeoMOEGcL1VvWIm4jdXovrg82opAkpQQeubDJy5+XQlSnn\nPONZATN2zGG8x0/Wg6gezxTsAMoB3gDDjclm9X01Xiq0QULzEo+ppSbHMF0ZpGZ+4f0Ey0Obgh26\n9hDXV4B00fUXagkzjCUwH5N7pqyemfm+hfGGdmTOMdDh3U6acgy1HremMHfJJSxccEnV1q2fe8+a\nH1BHOpoVSWJTsdX5oA6JyI0icj9V/RrwPcCX/N+z6BCBWQbdub7zbJf6GGZHUQa0pa6SSeqpuJ5J\nIVLi6zHTbEzy0fFaBheTmEu7Tl9X6PvEPq+FaeOZFuE6vtdqu67GtIpbd81MeC/rQAcjiWlx6v60\ndf6L6258m2AzaGnl7BLmS7QUZDkhXfSOrkYxY7dqD2qodLmeXEt1DMRmVJZ6pWcsdgB2oNjMq8gK\nZ4ygBsyKYHIhhOKN1YPWfyqOuSnlfP1sOa/owGKWnDl5kkCeUjENO1A0VWyqmNxgRjXjUaMkYyeZ\nOMYaVIpQztfSInhGkzk1ohmJU9VpfT9Id4hnRnhGuGCxmdYMOXFjL3YZJwlaSEaCGfn3k9bjVsGb\n40OZQrEbyqGrS3JBE0U9Y5UimOg75hT+t758UJHK2jFgK1z1iSNc96k71yzTIZLEpmKrJSiAXwb+\nWkQy4Bu4lWtCH828R0eM7Hb4jLcFelrqccI45+Fncc7D61yfH/zjr00qtmYkiRZOzKw5wpZTtqp+\nnslh2btFYJ738vO04JXTQuUHtMP3t5wCIXrLa0kPcRiULuqzVYEnmZ7mYC1pJb6XJK4OW1Z1TQx/\n0grZUo05Mav7MCvNQnXNNNp1l1r1x8EwrfcxaUuVk/owA6N16M7vzdgoLSUHRszN5RhRrBdnVlYy\nylHq9kJGCTIWpBTG+93qPEgrUgoNf6mBdZJDppisJE0tSVJL1Pk4pVhKkZGpnnWm3QKJk7hI1J3j\npAXw+1vzJWZQYudL8iTDLhvUqNv3SRQdKAysmw6MN+POLMy5GIHlcoIUQrJkMBnYvaCZUi7Yitg1\nUWRYkg4LRCAfJ4xGCbKcYAqpyjgHW6kkK5spOl+S7spZmB8joqgKVoU8T1AVVGF0fIBZTLDz1kl3\niX+HpdR9SN1LMcsJZkXQzL0XO2+RRCEXlrLElTdOYiTBi6B+/ypT188OmEVHUSSJL4rI53Cj/g2c\nj52q6p+JyJk4A509gBWR5wLfeqKqwC1nUBvGcNjMmaLq4me19zpawSirsmvd76IOjJlh/PykSdwz\nm1Ve7iE4p4/PNTXzJtRe7e0+lxZNnWGChIjKbQYRj7kd9FME0mRV3aEvcX9D/9uRpKudQoZOAAAg\nAElEQVTYZXE74Ti017Z0bL+/sCe1DlXfUtkzqM3AWQePIqIYUfIyobSGvfMrFKVhaTxgNEpRr3sr\nj2Zoopi5ksFcwcLcmLm0ttxPk5JxkZIlJQvZmFGZkpcJw8SVOTYecjSZr5xlRcCWgubGxclLlWRQ\nMhjmbnIfpYhRdu9aYd/8CqU13HzbfnRvQT7vmRygmUUyi0ncJG72KCGbvbWCqsC864MthWKcQKJk\ncwVpYhGgKAwmUeaHY/bMjTD++eOjIYsrTpenKpSFIc3KavtpkJZkaUlqLImxGFFKayitMMxaXg0H\nYVykrOQpK+OMxFiytNbFFaVBBMrSoAcgX/FTtQpSGiSxpLtL0jNKjFGytEQV8iKhKBPylRTNDZJa\nsmE3Hd8sOuoSScIbFm1abpQdz6Ds7iFVqBVw4UzKcu0QOVGAyYnlJm3XtMuHOkLU5CpsS/PhKjK0\nD5LpTuqAnHEU4jiA5MQ0AvH4oAq6WbUf6omZYLgeIgTEjDsOYNl6N40Amh5qTKTbtquuVeONA4xa\nmu97wlgbTDJmmh0xnqHiE5H74aIphB2Ei4D/L3YyFJHLcPsz3/CX3qaqv9u5E/cCPODAreRqOJ7P\ncceKi2q9kObMpQUDU2DVcDQfcnQ0x9EFp7kwxnJgfpkz5hcZJAXjMmUhHWPEcubwGAfTRZbsgNvG\ne6rfabnMsCrcvWueY+Mhx0dDN0Ebi1WhsAYRZf/cMufvvgsAqwargkU4fXCc07JFPpRewqhMURWO\nrQzJi4RBVpAYJS8SsrQkEcv8wDG5UZ6SGGXXYMTubMxcUrBSpqTGVuM7PJrHqrB3MOK04SKplBhR\nCpuwXGYYUTJTctd4gcMr86SeERXWsDsbc3C4yHySszsZcft4N4VN2J8tszdddoxfEwpNSKX04zEc\nzee4c7yLcZkwSEo/Xid1GVH2ZisMTcFiOWCpGHB0PKT0C4Vd2Zjd2ZilIuOu0Tx7BiPmkoLCGgo1\njMuE1DhavXrG7z+LjrYC269HPXqsE6NyZgyxrwEPBfDpqG8C/m5C0Q+r6lM3vYM9euwAzKKjrcD2\n69E6YecHq2JhVZ54UTyrxr5GEBiUSlUVq9ZCTK9QR+2voazyKRCqkCWNfDXxHlOkm68QSRxxzCyN\nyjZywVQBLkErL7zgkJf4QJVU7ccpC6qAmNqM51VJe22jQx/Qsh3epkqF4EOwhDAslVNmElXkw8es\nCvVftV1LS+43i/au1mnxv86V3xOAa1S17agLm7Cpu5Nx34XbsWrINWFp14C78wUswp50hXOHdzEn\nOUfKBZbKOmbbvnSZfckS52R3cczOc3uxh1wTRjbj9PQY52R3cVpynEwsS3bADflBjpQLzJmcm8cH\nyEzBUjnkaDFHZixDySkxJFhOz45z0fA2FmTEkg65s9jNijrp66LhbTx44QZuHB/kjmIPt433cCyf\nqyQccBOuEWVoCvZmK5ReKsmM5ZuyowAs2QGll84OZIvsT5YYa0quCQeT48yZnCU7BGB/soTBkmvC\n4XI3h/J9DE1OgmLEkmA5J7uLPWYFi7Boh5yWLLLHrLDf5CxqwpLNGGvCkg64u9zFoh1yS7qfMwbH\nOF4Oq3dgRFlIRsxJwb5kiTmTk4mT8lY0Y9EOGdmMTAqMKEt2wF35LjJTsmDGDE1OJiW35XsZmhyA\ntY3Mu9GRiLwOeApwSFVXZZsWkb3AXwHn49SB/0NV/2JmxVOw4xlUOZ80VVRSm6tC7UXd2Pdp+ZpW\nCb3UT5yG1ftE8XOwaq8olLVe1SiNxGbNehvPhHhb08zKg1aw9ZxToWnFNEJ/JRpv/GzVVmsvaRIc\ng8J7zuuq94s3hVWiSM1K5b1el5Umg/TP1wE8W3uBMoGpdsDYrisUxzOAN0259yi/+Xsz8DxV/fJ6\nKt7pOFIssCdZYV+yzNC4SbLQhFKFW8f7KVUobEKuhv3ZMpmULJgRB1O3/z0nY85Ij7FiM5LU/YAW\nQ64pmYzJpGDO5OTqjh+6cB0Ax+y8e0Ysc5JzZ7mbXBN2mREGy0BKTksOc156mGPWqRYzKVi0w2ry\nPpAukngCzaTkmJ3jrnwXAAvJmH3JEgC5Zz4LZsSZ2RF2mRGlGm7OD3C43MW+ZInz08OMNWFFMw6X\nuzkrvZsFceEgbi/3kEnJedmdnJkeYU5y9poVjto5jtk59pgVTksWKTFclN0NwJJNuNMOWbEpNxcH\nWLRDcr+IDsxvpBmL5ZA8Txiagv3ZEgvJiFwT9ieLnJfdhVVhUQfcWuyv3kEmJUu+voVkzIrNuCPf\nzUJSM6kFMztLLnSmo8uB/4UPrDwBvwB8SVWfKiKnA18Vkb/yobfWja2OxTcEPgwMfF+uUNUXey/l\nvwEOAJ8FfmLaABvSR5AewkQZ2wYILjqyahVlp/K7MOKSnPmJ113E7+XUEzVQM5mqXj9BtyZUTXwZ\nH7G5rjOSOBBI3YZrWxILDCXs4bSZZWCEVb3GSzZlbbygJlhH1XtnVcDL8GjEJMH11TE6mmWqgfkx\nRUzM/Q40JEcXMUCgDPtSzXcONKUrxVliGXERBdbBc/71tZ+eXQjw5tdPZXIsus8AF6jqkk+98Xbg\nft17sbXYDFo6NNrD0WSOBGVkU3JPSFYNRiyZ2GofBOC0bJEj5QK5pqxoypFigQTLQjLmjnw3u5MR\n95930aI+sXQxt+V7AbhweDt7EsuhYh/7kiUyKbi53M+t4/0czeewCANTuH2cbA8LxjG+XWbEQeOY\n4a3FPj65eDF3jHeT24QzhseZN2OsCkeLeXI1FP4jGpiCpXRQSWYHskUOpotcmN3Jik1ZIeOs7Ahn\npEdZtEOuXDmfY3aOI8UCd+cL7E2XOXtwN+dkd/mI32PmJOeYneeQ3cdYU5bsgDnJOSM9xuFyN4t2\nyIpmjGzGiqYcL+a4M9/Fspc+xzZhbFPGZcLB4RKFGo7nQ1bKlLmk4Eg+z6FkLwcHiwxNzqIdsmSH\nHCkXuGW8j7vzeQqbcPfY7YNZFQZecrQIc0lOYevxd8G4nE10qvoREblgrSI4Cz78/3eeKHOCLU5Y\n6NMfPF5VH4oLm/Fkn4vnD3Ci4TcDd+Ny+fToMREXPvPR1d8MPBn4jKre3r6hqsdVdckf/xOQ+cgT\nOwI9LfXYKBzTrP9OEK8CvlVEbgY+Dzx3I33achVfmBSAIa4/Cjwe+BF//fXAfwP+dNXDUEsI+P2Z\nhDo0SXu/qOV7JDaSLqzfC/Fqs2pPR+u9FBWQItqPEUGMotTlxf1TN+n3Zap8NX5/aJI6LqjtpK16\ns9SJ3Dwk2gwL/aj64CUWKaIyJmojCHTx/lgIhjmOLOuqxqKcPdXeWPs3APGRI1RwEpjU9YexSus8\nluZMrtU41zK1byPvsPLz+BGmqPfi3E8i8khAVPVw505sA2yUlj5z63mM85SiMBQjF0xOjMVkljQr\nsaWP/6hw9dzpzGU584McgzIqU46vDBmN3ZSyMD/m3D1HuDtf4Ggxx91jZx23UqZcZc6sJAWLsJgP\nOLI8xzh3zw6ygvmsYC51eyejMmWYFOwZjBgkJUfHQ247vpvjx+cBSLISFIo8weaGZGBdv43zQUpT\nZ4ptrbBnfsRpC4sMTFlZyQ1MiUW4c3mBxfGApfGAlZUMtcJwLue0PYvsG6ywOxszMAUrZcbh0Twr\nRca4SFgaD0iTkoVBTmkNi6MBibGkiSUvEpZXsurdobjjUpDUkg5L5oY5i4tDUEgyS5qWiDdRT4zl\n4O4lhknBnUu7OHZ8DpsbNNS3krj5YWARAzIoyYYFaVpircFawdputHT7Z27m7s9P2ppdF54IfE5V\nv1tELgbeKyIP2rF+UN6q6jPAxcCrgWtwgTqD0uwm4Jxpzyejst5DmuCvFFRPjSjLgUHFEzp+ooeG\n2XTVz3jSTKWa/FcbaNRMMOyjSGnrZyfsv9S5dAxibW26HcaQ1LpHl3ETz1BXT/KxkYZYrZOqBUZZ\n2NqYonqmNuZwWVS92boxDUONKpOnlZqJas14KqYzQy6vDE5idayv3+TWM9F1MKgOqz0RmccZSPxs\ndK2KxQf8oIg8Bxerbxm3V7WjsFFaKv/lAFkOGS78Toj/VgUpDcZCBsYFjBWO+mddWCMFdeGI7to7\nx7Hjc3xFz6JYrB1yQ2BXk7to6CEWngSySWF5CMsC5cBFHhevVk5WBFNCsuyemfPRyEPcuvkCH83b\nhS6K90aL1F2/M1Hu5HS30EqpVkxSSBXOyRQwLF0d+a45bjF7ODR2IYpsBpooybJUEdzNGMYGVnyg\nVrEwTuv4gRmBPlx/XIRxH0ppAOMBZDG9l87BWPza+Q7d55MZwsLIP+uD7g6OujrLuYRyCOUwc+GT\nQmXJ2mE9Y+x60IXsetCF1fn1b/hYtweb+CngJQCqeo2IXAt8C855d93Ycgblieeh3vrj73DZT1cV\nm/b8tV97tw/BLxzccyEHFy5oTtxp5CsUGy64xmtJIUzGPqBptZeTSOM5TcRNX3imFSZwEef3Ezvp\nhvotmAlSgctc2nKiSyKjj8BcU4OURe1XBA0mArh73i+qkpQCEwh9jKS4CoaqDhkXtZ+UCIh1jCi2\n+kvqfaZ6IP64sA3Dk8pQw0YMPpyXZdS/pOrH4SPf4PDR65oMdAa6SFCqugyc0br2p9Hxq3GT+o7F\nRmnp6Dv+qQqkuvfMizlw7iUuKd6AalET9kcDUwnRv0d765Qa5ZyQLqZwc0p2DNKVmBEp6bKSrliy\n42VlhKMGNDM+VYQLflrMu4R8IdVGkitmrFWCQZuBGatPt6HYTFyUdc8kjNd2lHNCMXTlRaUOYpsA\n6lNtFE6CT8ZubOCeAxclXUpH2zYN6SyUdKT+XtPIqZgX8gWDKZR0RauI8Db1/cqVdNliB8anHZH6\nXuHrS5xWxuTNRbfT4tTJDk2pLjGk3/PNF4yPkC7cffvVLN5yzcwFY8A6NBHBVGoSrsctBD/qo0rc\nj9q3cN3YcgYVoKpHfSruRwH7RcR4glszL8/9DnxnbR5elLA0qiNLiECBm2SjnCiVlBI29sPEHiQP\n72QqrWfA/yqlra8npjbXDvVB7UQbnGNjiSowLv/RE5lcqwWJnI5RRcY0VG4V83EdowrxlJj6qymt\nU0Mag+RUjCGMIYQZcg7OIaeBVO1KHOjVj1eSiNn78jFDrE3QfSSLENkimPKHdksLRQlqIU2RNKna\nPj09l9MPnFO902/c+uEJv3oThe1IgacITpSWvvmMxzlVdGlhWeHqRexcgh0m1YJDwrcGYASbGco5\ng8lNHVXbZ3kdHLdki5Zk2VZZaZPlArM0doshqKXz1KDDDDtI0NRgB4ZyLiEIx2LBjG1llSuFkh5d\nQXL37WqWoFmCHaZIYTEjV3+xZ8joQEa6LCSjiAAAM7KVAZQUihmVNSPw+ZVMbhsp7TU1lPMp5dCQ\nLhUkSwUhzX3p07mbUpg77JhvuljU1rbGMxf/LsJiz6WXt7VxU5Ygeen+CuvGFqyDC4us5PXibi5D\nB6lj8pkhOyKVBmIv52IP1JkBbplhaN6FjkTkjcDjgNNE5AbgRTjDnKCJ+F3gL0TE50Hi+RtRlW+1\nFd/pQO5THwQVzEuBD+CCWr6ZGRGYe/Qoyp5B9bTUY6PoQkeq+qMz7t+C24faFGy1BHU28HqvOzfA\nm31enquAvxGR3wE+B7xuag0rYxeUtCwrqQGT1NJEvOIDQqZXyTJIE5/fxroEgLmFsAdT2khFp9Vz\nleQS6kxT106Wulh4xq20pCjq9uP4dEVRn6epl/a8NLcyQtIUxcXEqiQRqAPZjls+DZH0hBjXzyps\nkFkth4d6bAkmQYZZJa2ILb1kE72vol4BYpK6Pl9Pda4WSaPPSQTJQ3Y5F+OsaresJbBKrAy/nS+3\nDg1fz6AcNkxL6R2LldpaBymIYFYgWcqRkdd7WcDf1yzBjA1SJJixVmqmar+3ULLjOcmxMWZlXH+/\neVGrp0Ucrc7PoQNFvMQiuSVZLrFZc+/VZa61mMURsjRyAZIT4ySNcYFZzpHlMRQlumcek1uGd+cN\nFWUyKpFxrVo3oxzJSyelRCpzycuGtoTEOOnGDpAyJbtrGVSxcwMXkNY4acyMFFMqyXJJcsxLeYFO\ny9LRVJZBlrr6y9K9h0EGiWCWczi+5J5JEyRPnVQbB2P2c4es5JVEpZmjP8kLd5wIiTHY+W6xKrcj\nHW11Pqgv4rKAtq9fC3xHt0osFOqYUmqawUgDM0iM+5CBatcwMe6DDHssfi9IAkMJ+0mBOVWMSmu1\nXJpGKoqkNto3uICXgdGBm/jDBxb6E9SKRtx9gDhGXfgYE1MzjSwLL8mNT6RSpaFRewYoi+ojr1BS\nM4PERCpJYFw2GXJg0u7NuGuh3+qZDdR1hLYTt4+3an8thp+YUAu5J9AQhzIxzYlhBspuqol9wGuB\nb8NNs89W1U+0yrwSZ4q+CDxLVa/s3IktxmbQkhw97ia9wcCpoaxXT+d5teABnJXr0sgdDzKS1GDn\nBm6vtKh/NylLzNFlWFqGce4mYL+Q1NKp0yTQkBgMVPvAsjRyKqwsdUwz82nXl0awvFLvlWaZ/xZt\nbbhjDMw5B1izkiPjWp0veYlZHDl6M56uCt+XMW7MRVHTZFnWTCtLkVFCMiowaeK2FIxgTOHUcWWJ\nJk7VaFbGrq+jcfghoChQfy7DgZs/Ao1kmWNCReGu+T1lcoXlFdTPZTIc+P1kRzO6slK9WwlzQ5oi\nRThOOvsSdaGjexpbLUFtHGIcY8qioYRVSZo685g42nnYN0nEffRh36mwEwOpuskSZ1oD9aReEZZU\nTLHahxEBoyim3rMyAiatGVQgDlW3ohSB+bk66K038AA8k5CIMfj9tsGgvm8Do46MNAyuzcCgwvgS\nx6R1mNar2ErqEqrMadGE5P6PyqpfFBipxxL6HcYc2hKp30E79JF4i8XMrQgb1oYdYbut/F4B/KOq\nPt0nXVtodMM5516sqpd6/6E/we3hnDooCmceFhYmeQ5W0bBYEUEy/80kifuuigIxCUle1hJSeH5l\nhI48I0tTpHR163iMliWSJE6wGQ7dYicv3H7p8gq6soJkmdNsDDI34a6M0OOL6HiMJAkyN3Ttqbr6\n0rShrRARKCwmSGylZ7a5lwYHg3qcUC9m/fM6zh3NZCl1ss0Clpddv7LUMRarTosjgowKGI0cTY9z\nKgPKID2Bo71x7hh3krikq+Ox64eqY2IiSJLUtBcsfUdjV0+SOG3L8UVfv3VtGoOkZVUXWYaE8c1A\nFzrqEOroMjYx6PLOZ1A9TnnMIiwR2QM8VlWfBeA924+2ij0NH75FVT8hIvti36gePe7t6LjQu5y1\nQx3BJgZd3vEMSvfM13soqpC7/CxBjQfePDo1laVPkEKIYthh3P4TFEic8qT0onYQf42p8i7FVoBY\n60IXBcu4SnXo1Q9rSASaCCQJdpA2rO0qk+28rEIegb+f1hKYFJE6MkhQXvoL1orO8s94tYmzCqrM\nu/ESi2arTfHjfgZrSVUgqS2J/DtsBrcNeazDClBr9Wn47LyVIWlkCRnM2M16JKiZO1YXAXeIyOXA\ng3E+Gc/1pucB5wKxl+K/+WunDoOKNQhF8X/be/MoWZK7vvfzi8isqu6+29zRjAaQxIxmJDA8MMtB\nCGM2A/KAkXiHTTw/GyH7gS1Ws4PB5skHHos5hmeDbGOD2BdbYCGwETviAZIQQgNCCNBIGs1ImtEs\nd+7aXVWZGb/3xy8iMqpudXf1vT333h7ye06frsolMjIrI37x274/dDpD541pJ840ap3Zb+xOnug1\npW5uWkPXoW1rmlFnGpFGP6RUlZmpmjaaqzpU1TSRpKlMZ9kEltM2IGtj7ExNywNrYx7fma5DQyDH\nh3uPhBq2d/qI2eRLBdNqYh6hTmdZm8M5KxgYTf6afNNdQCVG02ZEbShZRJKpsG0Xta/SshA1vQRt\n7Fml/VJX9rziedq22TWhOzu9NQIQEcL2NmE+782jXTANt9QkmyY/s31//v3H0TpUR8CBXMh74sgL\nqOb0Jn2yXkDaLk62Kbgghr+OLFw2lJMoZIeun5lz1hJFQ7FfLLSzzK2KZih1fR2qZN6zbXHS7jSz\nfksTFuszYYJTq3iOj58FEnNFf6D9S+Harg24WWeO4y7WllKFMF7InTLhlExykJJ4QxWvV7CPi5pT\ndyEkNoUcl3lM0AvKhedo972cpNwfYH1YaEf6dvKzc6UQXA+Pv+K39jukwvwzX66qfywiP4Dx8X17\nccyqQbV+J54MiEJBm8bMU01rEyAAHnEh/o8mvhBgNu/9SeJMOKXgmLhItEm+swVSGkdBkRigpG3b\nT6LFpK4uJoTP5guTu7glwZT9rq5fnM2b3k/c9e81YH6dtrX20rXS9Yu8P0kCIeUYzufRPB7Nb13X\nBzi03YLwSWa4fD9lvqKqnR99vTqdRZ9Xm83v+fzkb4qf872nn8z7RVO8SG9WBCQ41n2NdQ0BtSYO\njXT5yAuo87ePEbVkOku2C7iO/EIGj+VqjLBEQiGzP5SlLvzMJlVJlEcK0pHzJFyrmULJkvzSRG/9\nkBaQlHXf+6RcSxYOiUEhuXhS3oi6nlHBmC+i4JS4L7qFUEvk840lKLo2CcD4MKRvK52Xy0e7xfaW\n4TrFp6qdS/t3YxYvn2Wo+2PtPntXVm6zHCfld8EYAGp7nitpqvbAqRc8L38+98qVwurdwAOqmrLZ\nXwF804pjykqge+YMPSnRRWERLBhG6NcieXIX0/azj8M7pAy2ScfGAAtxxb4QTKBQTP6lnyUeA5jP\nKk+8rg9cGNV9X+fzPv9vMi78RIt9QMSCJkSgabLvC7A28v13JjjryvxZSQgtIfe9XKBpf1+agolS\n30PIQk58b3mQUb2oYXlvGlAKvMoRtK6PkE3nJ59b9lU5W1jEZ5O3LwSI7Y3pW97J9C/fsf+Be+NQ\nSZfXMjqKyPess+2wISJ3i8hfishfi8jyhDJgAGArv/S3cr/5kR6IlXUBPhVYXtW9CvgiABF5LkYR\ndOjmvesxloZxNGAdjJ91Fyef/7z8dyU4bNLldTWoT+fyFednrNh2aIj5HD+ITSbvBd4gIr+kqn9Z\nHnf+jkhV0ohpPJ0jscurj4t0FzWbKv4vtAm7GKDp/Mix1YFrF7WBrEF5en6yNCem1B4tNIAgkYYp\nXWNJu3BkjQGHZc27uF369pMW4lL/WvDzyGW2pK2o79vtTWi9tnPZc44aT+JIS/cuobh2qQVRandk\nPrFE1ZI1qNBrQQslNpY0o7Qv/TYL11gTsp5p4quAn44lN94BvLjk4os5Q58pIvdiYeYvXr8HB8I1\nHUvrjiMANiY9hVYyRS1HZyZzVYpiS+HniQ0khD5cOqUaFCY55tEnUgkyGplGlLSsFHmbrlX4XEyj\nkl4baNuovfm+P+kaqY3U98pbOHqKvm1bo9oSQTYmvVmzs3w9GY8K7ale7EcZ7ToemYbTNPbatl3M\nv3QgoY9+TPeX/MNOLHKx/5Gsj0lLivmCMlu6ZvrchXyspChaQDY3+udWsNMsaLd7vSvrm/jKWWFx\nxyGTLu8poCJ55pcBzyyoK8DqfPzBlV50TTwHeJuqviv25eewSKuFgfXUj38vTefZaUz17zrHvHOo\nGt+3RiZf55S66qhEEVG8U7yEGEsQmFQNXZzBu+DogmPeedrYFqJmqRO1FKIVHQ5BLJdAIagg+Zw0\n5q1ypwbbByBOqXxgXLd4CQQEh22bVFY0LrevQqfWt6bztMHl/nUqeBfyW+OcneddwIva/yLwIN1j\np2LnB6HtPCEITePp5t7uO0Kc5j6niHdfddR1x8h3VD7kRD9dki5t5/K92/OztiySPlBXHXXRt7Kv\nb9v9/eixR7pVgqr+KfAxS5v/89IxX7HO5a4E13EsrTWOAPTklpmKImmxzNuejgvo0wtioFDVm++0\nXiwcmv2tGqnDum6RBgsIx/pJOgcR5aCaSAJd5NYtFBFtYzBUymeETMekMdgopY5oVaGTqvcnx75Q\n+mDb/n51XBXCNvqWq2qRTxJQ75Hcz560OhcPLQMnEpIQikJFRz4nPZuQ1v75pWcSA6eSUM1BV0Ht\nd6h9f91I42YJxUu2+fuWf/ElrDGO1qA6OlTS5f00qJ8BfhVjpy2LvF24BqUIlqOq3o0NtgX89Af/\nFFMVtgtG6/jq4lHm+Fx4zaN530TaeKxjRMdp35J4EmoROlUCMI//HfY+J+7GDqFRye0BNLFcdtmP\nTh1hD0tqh1DTMZIOJ8pEWkYExqJM4mD18b9DqHHU4lnmiGjp2NaWqQaman1L7QdN9xWoRfNzaPJ2\npUZpEILCVH1+bl0sMV0iINTSMZGGibRMpIvtuVwUzspg68JzuaRWXnsUq3zauYGJaH72c1W8kL9/\nwK5PrscBVn7XE9drLK01jgCmH3DCGLS9EbS6JvpVCu7HxOYgAbqx76NOnZ2nLpK7xirR2Yca+vNT\ncc7gBdcUx8YyLq6JQUAp6s5J5OiTzJnnIvODemcsDiLkIqN+iSXfC93EmU84ks1q7ShJ8I0t3Pa5\nzqJ8XWNCJ107FyfF+khQkMj6UgjyULl8L6hVXHCz1oRd7LMdGP3ktXEPagxocoloWQr/tDPhpbWQ\nSulkH683f1vmDi0WFOpkQRDvhXXG0RpUR4dKuryngFLVc8A5Yj0ZEbkVmADHROSYqt5/WB1ZgVVP\n6zLX+Xd+3yUUoVXHRz93zEd83GbWhEaxLGwtAU+gw+FQRpgwsInaBuHZ+Lb2QkwYxXOSgEvnQC8E\nR/H88zpiO1jirCMQcHHS9nRRQNV0eLHJu44C0sW25+oZ0dkAjXfeYBN3iJODQwmxxlItnnEcHIFA\nUM3CaaouCwsgC5qEkSwulZJgTPfuRNmioRMTOqOYpOzEnl16prUokyyEoJNAXfxEXmAijjHgJQBT\nmuhMdghePJ06GgKNBhqU3/3Dhje8dnagOFVZs97N9cR1HEtrjSOA+9/yaySqoplAfmEAACAASURB\nVJtO3cFNp++0g72Nh6Q9hBTFWjubGMv89hjMkyZ427gY2ZpTG6JgS9WgIZqipWfgT6StQI6KtUk9\nCo3K5erSaYJ2bSgm/eLcGKyktSsCpYqOlxN9JJCVEPDTomyNSybs3prQp3gQWchDrIeWhCZ0kyr2\nTRfMbiZcTeBoTS4tojWFoIlm+8r17PIixuTeWH/FyQLpCwrnHnk75x59+9oBRzfiOFrLByUizwf+\nHVZL5mHgA4G3Ah/6xHWNdwPPKL6vjKp6ydecpEOYahUn2AFHGc/5uDHP+bhxXgj80A/sX+dMjtCP\nfh3G0lrjCODOp/89m7xvvHlqwBXgptN3cvPxO7Im+s77f3vP42/EcbRukMR3YLQvv6mqHykin0Jf\npfOJwhuAu2JS2IPAF6665uf84UuAYlGSfDui5ocRss8jba+8VdusXZcXKTvzmi4IITi6zi3wpdp5\ndq645M9RXKyaCbAzq2k7jwYp/KiLjYgzs1flQ+6fE+1NcK7Du+QjCwv+p4TkO0rVQLsgBMz3NW88\nIbg+hDbESrvxvpPvDcjXTL4xMB/agt8p3m/v17L7LX1GpU+pkpC/p76PXMekapn4hpFr8aJ0KgS1\ne9juRpyfj5l1FV1wXJyN7DfIq7mXXvYMlrHOwIrBAn8MvHs5y11EXgT8W2wyB/hBVf3R/Vu9Ilzr\nsbTWOAKoHjrbU/0UCellaZbkK8nlIRLKJPLk/2mXcv+61E60reVcPXKuHU3X+6lSX0pOyMJ8FbYm\nUWvx2WeT+ppQJpdn/1FKpoecBC+h6G9sfyG4IPFzOoeOfO9vSjXk0rVTaHtqo+vIQSSV7+8lJtH7\n4jnk0jz099QXSe2Lh2o0C2aXcGuBKak8RzbHtqEn5l0Da46j/aiO/iEW8KPAReAlkSfyirCugGpU\n9TERcbG2zO880aGxqtqJyFcAv469Vj+iqm9dPu7Y6zbMnND1UWU5ao6oGpcuII18qUAjfbSbmHUN\nr1Cn6Lh4fI5Gc/1f8NB58wQCuBmMY0QdZC2bFO2W+yQxWd0XkXbxnGnsR1LpszO2jJlRcoRh7lvc\n5tWCN8zm3/c5jOxaQRbf1RSxV8WoPdcU10nn+8XnGQSmFWzXLORnLUT8pWcv/X3Yb6PoSHuTTRAk\nVld1LTE6Md7LLrlXq7CmD+qrsdDyE7vs/zlV/ar1r3rFuKZjad1xBKBnzy2+IClKLkWMeR8rB1iU\nXiJLpa575paS5Dfx5KVJMubmSBI8dW21wMpzEjkt5CRbnTf5+pm1IShuOstceYgzPr/Exp8Sxssk\nV3sgkUewwoimuxxtqG1rr/JynlOMnFMNxg84Gdv12rZ/HkkYFcdnpEi7HKVnfZaiYoKk68UglNRu\nFrg5uT36ABIP5qiOwR+dcftFJosF1opl/stdsOY4ejl7Ux29A/jEWPblbuC/cBWclusKqLMicgz4\nPSxU92FgPf6Mq4Cqvhr4oL2OOfmO1ibJzrQlrSQmfEYNIdqF0T5pNiW35tDsZAenF0bmrCQWK4ub\nvCX+quudpQkpBD210TuJ6ZNPYwj2ygqXhaACs02rWxQA/YOxxFppyaXWZfklFFlKBu7vN73s0sVq\npCmAKmjPZNH1lT2TnRzo26z7BOLy2svmoeUk5FBbxc/czSxwrWpqva2r72cP7LfyE5GnAZ8JfCfw\ntbsdtvYFrw7XfCytM44AdGdasDVIZHuIZKYlEwIsMC9IXdsxOVw1ahextISm0HWxyNpEyiqTcaZF\nSpQ/msrmJMGShFlifkiJr2ACsK6QxlgZEoVRFrIl1VAKT09JyLURN2tig1DN/VTIibH5eaRkWGJI\nuffGJA6IuMymkZHe34KtgqqyYxLTRpGkLLIo4DMbxLLwrqtegMY2rFRQ07PEJ6qmxKCxJtbRoPaj\nOlLV1xVfX8d6cU67Yq1EXSwsdQf4GuDVwNuB51/NhQcMOCws5F+txvcD3wCrgwMiPkdE7hGR/xYF\n2hOFYSwNuCFRjqM9xtJB8H9hkatXjLU0KFW9VHz98au54GFj44GLizx5lVsM5WQx8qa0Mefw2cxd\nJ5dNYZJKv6vG/AKzBSfaIrMJkzUW6LUaIoGqRrNEud00ubhaipx5WesTLFKpcqSQ3BxZpIW214UY\nbqo9GWthq9d0X1WvMfUPxezy2WYdj7f+R1t4ekn9IrdfyZuX2pDWNNhcYC5qqeWxKfw3hb72Cc32\nPFyr1Bfmi1yKa+Cx33r1rvtE5B9g9vJ7ROSTWa0pvQr4GVVtYvLuj2OJrYeOG3ksWd0zW4WrRDOT\nimk4+ZjeVJe0He1mPSddJv1N4XPxzK5DRqNMdWQ+HbWwpi6gZb5VPE+bBulcJm3VrjO/TKEZiSpK\ns3he0iJSXbPEiQfWTjQ9LviqFjj9ZMFElumJki+uaYy8trjPRJmUE4eL56VN1P6YmeYWnwfeZ21I\nvSfXYEtEtd6bpjafx9pvLhtashaVasKl55EIZ1OU4HKB0z2w8/Z72b7v3rWP3wvRt/pi4O9eTTv7\nJepeYPWqUwBV1d3s+dcM7sJ2VqPF+aKyLYu22yK5bSHLOle3LZjO04+dhElRqDAlxlnIbJdtxnmC\nL8Jasy24cCznDP02LCZBekcY14vO5lizKicYNl3PGr50jQWBu6IKaL5f1d4hDb3zt7B/58FYVvMt\nizy6otijiNXcmTX5XrPTOk1G5fERpcPXNoC0Abczs2qny0mGe+CWT7g7f37sNb++vPvjgReIyGcC\nG8BxEfkJVf2i3BfVx4vj/wtw6D6hozCWEIeMfEwIdTaZJv9Nrv9kpixtWvOblAuJWDdJNU2uthhx\no1E2v0k002kIebLW0iwHZv6rKhOQscZUEhZ54k6CKm6z7rslZvJUw81n341EjkHtur7AX2RSz4Il\nCloZ1dbndO/LDtzYV0RibaqmHzvx+plI11UL3IFUVc9TuGByNJLeVDMuP6MuLh7mjZ1XVVbPKhU3\njItzJBLKqsJsjiu5BGd7//xbT7+Lrafflb+vGEtrQUQ+HPhh4O6lsXVg7JcHdfxqGr8mmCeCxM6M\nqG1rL9Yy3Ufb5QJsmeW4LLQ3nSNdZVnkKaonOz/t5QCQxkHjkcr3me3RHi8kbYiFrHlpg03Iadus\nIVfphTxxO7CJPOWKdILM237CjxFOEs8pJ33ptLdtlyvBYCztCxFRJULoM92ld0CjgcySnMpUp4kq\nOrpziez0fCPNjVRFFFNK0ExMy6m/jsxagC9+p1Bcb03sZTtX1X8J/Et7ZPJJwNeVwiluv01VH4pf\nP5vLefquGkdiLJU+FCd9Mb1EHZRKOagiFy7G/CXtS0Z4B6NRz3gQGSnQGAnaddD5XJKDuobJGGnb\nXiMRyUKQWIpFKt9bB6p+ysptJ99LrqDtjfYnayvBBEzqZ2eVABjHgp9ta7RLidQ1Xb8MTICeaTxV\ntd3eye+t1DUa1IopjkamYSZhWrYRfXZIpFlaItMV74whPlUx3pggW6ZppTIlfV8EJpv23JtiXx1/\no8kYqSrCTqwqs4+AOkCYubDaEoGIPAP4BeAfq+rb125xFxx5NvNytQLYCzxvjHwjJ8sVK71oDoO2\nH3iZO6zLK7yFCB4tBJVXc2mL2MtUROIsRONESpLUhqT5VtWijZKgkKi0x0k+R/akaB2RGAobBWsR\nciut9BV+oXcgZ7p937MfN4UpYMmUknnHNMRnlgR3IWgSyhWZSH9MuW8eFp951jR9FlSmkamF64Ui\n3NjHiXBemG32wZXkb4jIS4E3qOqvAF8lIi/AgjLPAF988BaPPmRkE6/OZqbBVFYxluNbeTGUFksy\nnfWM37nicmLwlv69KrXwtFi5dImywqwe34StTWQ2t9+9rvs2g9r7kLSssfH3aeWQC9t2fDmWXPHe\n1X1wh46iuSxaPmRnjm6MbC6Y1P2+EJBprOXUhX6sltGGtVETSaxvlXgJcykMJzkIJJsJqypHEWrT\n9vc3GVt7abHbBWQ2Qy7tZIGvGyNjWJ/Neuqk0joE6MYIrSrw0VLTdBbuX9e4xABfGpdX/f6HQ3X0\nr4DTwMtERLCo1ZXMJevgugkoEfk32Go1YEXhvjitYkXk32MEmpfi9nuuVz8H3PhwawooVX0N8Jr4\n+duL7VnLOooYxtKAw8A642gNqqMvAb7kkLp0XTWo71XVfw0gIl+JSeKXRF/Bnar6LBH5WOA/sVcc\nfWJRHkV1XbVfnaSVelllNpGSRhtvRtJwluu/BDWNQugZkeM11IsxF5d+qnTd1Ldye9K0Sq1Oo/lN\nXK8NpmMjvYytvGLQgnf9qrT0N/nkI/KmxbVR6/KgddWbLJNJLq02l+81wfnF51hqXukZF6Y5Hfcr\nxHxMMhXmNgK0RTvR9q5V9IkFif4qM0+si0OKODrKOJSxJMePmbaf6hRtbaFbY1uZV87ohWoLJqqb\nDtmZQeVzwqw0ppkAOThBxzHZV9VIUb0gG2M7dz6Pv3eFjj2yUfe+1NLfKmJBQG1n5KrjyH9X1705\nzkjp+iCEjYlpSBBTUGKQzqiiG42RYxPcvDX/bDSppwRiPTmxPohpfW7WRH9pNFl2asFYoxPITrOQ\nHJyvH7XJhRFWWdi8NG1vIYj+2TAZ0W3VSBeozo+yf0wntVkXNkYwrhasKTqqsv8pbI5ot0Z0Y5s/\nLG3D0jX8zjG71j7VzW7EcXTdBJSqlhw2W/S2ohcQk8BU9fUicrKkcL8Mmxs2QVYpTyMOhrKkeTR9\nSSy/viBI0r7SH5Um7y7YJFz6T3wxaQMqujiBl22kF7eInpOUWFj6ggphoaXQdNggB2MsjpnuOk4R\nPOHySrfOodXIXuSYb4KPpdWTGQ2y/yfb9mPU3kImfYnCf0QOcIjnS8Gztir6LuzeJhDzrWKfvIBU\naxdZs/PXPvRJicMaS+HWU5HRu4h2jX6gMK7oxp5u4gi1gBzDb4+NN27kLXpToLpQI9vznn1iYmZ0\nhexvDJMaTm3gL81REcJmTbtV4+Zdzh+0CTZY5ecY+enm9r6HkScFGpnPpkZC15sVwcxmo4owrqya\nddeZcNqo6EYOPVEjTb2QG6gOUp5kIpgFcO0YNws5KhWIxUgDsjWy99eBeoebbeLmbe4fRL9ygAXW\nC1Xc9hytHN3xCc2JkT3bSvCnRlSXNvE7yTds/bLI3mj270KsIB7QytEeq5mdqmknkqViErJuHgX1\n6/d+j9Y08d0N/AB90vf3LO1/BvCjwC3AY8A/UtUrLvx5XX1QIvIdWJG4s8CnxM3L7MvvidtWD6rN\nUWpsMUwaFqlNpHC0xgg5O0hJzv+8EiqFTTFQc3u5Sib9ZF253Ha+Tiw/IRIWKEwW/FMieRVVnl9G\nBGZhUAe0nDxqTyiFZorCg154xOcgqrlGSKJnUS+LRW6brhdC6TmV+1O/0j3L4vnBF5GK6d5Kf1eB\nhXLzKSS/EIBaVkfdB279eIonLQ5jLD36kSfxTUzebm0VDvZbdSNLHk+Vk2cnPdXOCD+3AKB2YvXE\n/MkaP93Axck5VI5ubKzlKISxo6ttEnXNxJjDMQHU3BI1rEjsavXObH+o+4TvlGoxPjdCWsXP+oRU\nrY081hhUHM2mTequtUT9diI0W1a5Oef8pPpxzu7Zzy0Rvp0I3djeST+zRPLECuMim0vw9LXi4j6J\nCf71pUKoqUa2Fo0J64KELVx6fhuO5phjftxojPysotqxuU0CuWJ46meoJVfsDhV0Y6HZFNqJHRMi\noaz9jqvH4DL2G0dr1hb7PuDHVPWnYlrHdxMLgV4JnlABJSK/ATy13IStBb5VVX9ZVb8N+LZY5fMr\ngf8bVkaHrFiWG+597+/mzzeduIPTJ+646n4PuH44c+E+zlx814HOWYNJYowxN4ywd/4VqvrSpWNG\nmLbx0cCjwAufYLb+A+FajKX3vvHVuGALhpM335nZzAccTVx6l+U1rWu6W0ODWqe22IcA/wJAVX9X\nRH7pYL1exBMqoFT109c89GeBX8EG1buBpxf7dmVfBrj9g/7+Qu2Zln6lb5oMpDoxuXZNQvFdJjE0\nU8lJsbCkjcHlCbVxexi5nDSbaIJSYnCeEQrzX1l8zRJqLaF3geJHe7qhnHisZZ2eVJzMLVUHJt+L\nHdffTzomJdDmxF/BlmKlxrNktlyokCu9KSHTRy0nIS9ptWU9nfwc0qo4rkBPbT6LU7c+Ox/yjgdf\nw37Yb2Cp6kxEPkVVt0XEA38gIr+qqn9UHPZPgTPRX/NC4HsxYtUbAtdiLI2/9NMhgJs7Zg08VJRf\ncJEj0ipJK9WO4GeCtGbaazfIWomfeXyMbA41mY+x/J3Ux30CfmocjLnqtRc0zky5unPUUqQDN7fz\nL75fhW9A2uiTUjK1WEj/R9CNiZqZtZEqQLuUoTJW/Fxwc9M2QgUp6z7EEhhuLpkDM1+j6vfn+5LY\nXxH8zOG3+1c+82xWdlxqK10n1PYcu4lxVkojucK3m5uGmrQ36aJmO06f7a8bqz3DOqDP+kA253f0\nlpjX753XtIaAWqe22D3A5wL/QUQ+Bysnc9OV5kNdzyi+u1Q1pS2XUvhVwJcDPy8izwXO7up/ApvY\nQwxYSJNgmkiRXgCp5uMS84PEGi4AofBBucJntOCXoRd0uWYNZGFgJjNBfJzQS8GQ8iWi8Mh2+dSE\nd9GMIQVXoJnZUp8zd2DtClMiudZOMiWU9vRUPG75XqzOTi/U+oTZor/5pnWp/+Tz4oO2e43BDtIp\nilsQ4Lkptyiwln+PzIxxAA6xhd9rF6jqdvw4xt775ZM+GwsuAHgFZso4EjissXTnB73XqiNLYOLb\nzLTvRJkHz/n5OO+fdjZ17LTG4h9U2Kzn1L5j1lU0nefSbEQzN+HhRHOlZoItVPyow/lAG4TQOlDb\nVtdWr20+r+gaj6sCvgqg0EwrtHXgItO+Vzu3s0AJ7cT2VWlxaseIKBorOlejjq3JnLbzzOcVlQ+M\nKpudp7OaZqdCnFJvtIzrlllTMZ1WfXXuKuB8YHNjnqsZBBW6zpGqBjStVRaYzSu6eVpcit27V8QF\nXKVUoxZVoZ17QuNxdRct44qPVRecs8rfTevpWmfPME4cftwiooxGLaO6Y6Nu2KznbFYNJ0ZTjldT\ndjozFf7EPu/RxXe/jQsP7Zm6tI5G/g3AD4rIF2NWi/dwFVyT19MH9d0i8mxMhLwL+OcAqvq/ROQz\nReReLDT2xfs11BPDRrohlX7SjFpSnpAXHIjx0IIoNlUUzdqCB6TQbJJGVuRbXE6YGifd1I4mYZPO\nkQVBt3wfJmCU4CJNUVyVaaQE6rJgLCbnUvBFLUoroaui/T9iUUPrn1HvKC60nFjFU+j7FVKuUuxL\nek6hKp5h0iDzuLxci1ISBVJ/HQSorORHuSjYDwcot/FG4E7gh1T1DUuH5NVhZAA/KyKnr0Hl6MPA\noYylL3j/NzJxDcfdDpfCmEfbE9TSUkvHVGu2w4jtboyXwK31eW6pzuMJXApjmqjy3FKdx0ugU8f7\n2pO8e36ah2ZGktGqZ9ZVXGzHBBXmwfP4ziZboxlPmWxzvJ7iUBp1zENFGzxVjH2uop1qHjxt8MyD\n59xswkbdcKKeMfENG37O2Jlg9aJsuDm1dFzsxpxtNjnXbABwvJ7ylNFFjvspD8+PMwsVs1Dla15s\nRlxoxtw03uEZm49zy+gCs1DRqMeJUktHUKF2Vhl6082YuAaHsh1GdDgudhO2OytiGlSYRYbkWgK3\njC7QqOfd01O0wdo8PbpEGzw7oebcfIOL7Yg2OE6MZoxcy3ZrQmbaVZyfTVAVuiCM65abxjs8ZXKR\n9x+f49kbD3Hc7dBohZPARBpG0cm2n4A6eetdnLy1Z5J48J7LNK59a4up6oOYBoWIbAGfq6oX9rn0\nrrieUXyft8e+r7iWfRlwtPHeN/3avseo0UN/pIicAF4pIh+iqiVjxPLqMBlLb3gMY2nAYWCNPKh9\na4uJyM2YqVyBb8Ei+q4YR59JIqKMCstms2gWEwr/h4IrtSEni+UdQjFTJdNTzOUpNbAU8rrg60oR\ngZg2kEtR76UNSK+9uEYXop8sImhRU0F1UeMLmkNYzXRitDIqIHPtw2jLS8ZzsplNIvktiksmQNfP\nz/n8pO0tmd8k9EUNV03p5XXKY+yZC6r2/BdrS60XeQTw9A99Xv78njfvbWdX1fMi8rvA3SxSGj2A\n+WveG/1UJ66WR+yo4cMmDzCRlkY9j4UtttyMRitO+UtsypxHuuM80p7gTLfFdhhxodvg5uoit1Xn\nmEjD7fUlPsAfZ6YNb206GvV0kTy4CZ6Z1lyUEZXraINn2tWcOrnD8co4eE7V29w6Oo9HGbt+5T8N\nNY1WHPc7zLXiYjfhTLtFq54NN2fiGsbRoRTUMXYNt1QX2HIzJjLnfNhgGmouhA0ebY5Tu5anVue5\nrT7L2fEWF7oJDzaneNfOaRzK6XHL6fE2p+od3n/yOCf9Dk+vH+OEm3I2bDJXT1DHI+1xaum4rT7H\nROZsypxTfsrZbsKZcIzH2mMc9zuMpONst8mZ9hgdQi0ds1Bz1+YjbLp51nSO+ymNet668/48Oj/G\nufkGrTrOzjeYd54uOLwLjH006/mOzarh1Gibp03OcrIyK7YXZctd4ribMpGWyZp5GLKPqXy32mJL\nrCyfDHyXiATMxPflB3kHl3HkBZSbdTaZLiedJoEBC36fhIUfI7Gclzk9S0JFy8RW1cuCGTLTtyvq\nK7WXh8/kQoKRzdsV/cz9EgvX9jnXqmwgBTSohQEXPqVs3qMMWij8TulcKfw+aG8hVl3oX7524ata\neK4pkKRbfLaprdTf5UCIy/xb6TghU+KsKCa8K9YIj30KRrlyTkQ2gE/Dwl9L/DLwIixb5POB316/\nB08OXApjHgonuRTGBBzTUHPcT5mqma0SZsFIjR9tj5tpy9kEO1XhdbOWR7qTuOj8nbiG96vP8u75\naS42Ix6bHWPaVQQVToysnlIyvc1CRS0dzxg/xtNHj1FjE+tt1QWmWjENlU38coxNN+O+2S1MXMMx\nP6UWE4iPtps83JzgvulT8rZZqDjmZzTq8aLc7C7gJfBYe4yJa7J5zotyth1n31urjsp1+JHyiJzg\nvXoTAJtuxnYYM1Xzrz3WHuOW6ny8nmPLzZnqjLn3nOs26dRxptvi4fmJbEpsguep4/OcHG/zzPHD\nTEPNA83NPDg/lc2BrTrOz8c0weMlsFVbEvS21ox8x8S3tMGx3Y7564sW4LlZzXjq+AJ3Td5nAso1\nnHZlmtzuWCddY1VtsSVWll/AuPgOBUdeQPntxni5itIZwMKkvyB0sjChL+tQBFIsnCu9ryhPqUtB\nE+n4zBhebl7Ky8oCsHLmN1kqkZHvo2wjZdInlOcs96NgoVgoWZHuPwmHZQGRnkHl8nNa2fcVz2qh\nf8vJykmzS5+L/KlUaiQnMZd9K0t9rIH9Vn7A+wE/Hv1QDvj56J8pV34/AvykiLwNSzC8YSL4rhX+\neOcO3r59K4/PNzlWzThRTalcx0PTEzw63aLpPN4FKhdwokx8y2b1NJ65+Si1dLz14m1styOO1TNu\nHV9g083pcDw6P8Yj02Ocn485P5vQdo7KBx68eIIuCCE4vA+cmuww7WoudmMebY/jJPDU6jxnumPM\n1XOmO8ajzXEa9TywcxMXmzEn6uM5iOOh7eM0wbPT1MybikkdtSqEkbfAi9p1HB/NuOu4aS+PzI9z\nrtngfGN+saDCLAaABBUe3jnGvdUtVC5kv1nyh7Xqsv/LxW0nqiknqilBhcdb83tt+IZKOh6ZHeeR\nnS125jXeKWe3NpiFirdPb+VSO2YWzD/nUN63c4zzU0tq6jpH03oL8PDBgieKFdzF7THtPDJXeGU8\nbrhp605q11H7jhOjxBL7H/f8/dcYR9ccR15AufM7i5PiQqJpnOVSOeqSuBKWqH2S439RM5KyzeXP\nBaRZLDW9cK0yEEJyyEGR0FvQFZX3snABWTy2CHsnFPda9D1P+N4h6fiS3Ha3e1uuDLpCIKfjJX0v\nFwdlsnPZl1X3U7a33P6a2C+KT1XfDHzUiu3lym8GfMGBLvwkw68++KGcn07oOodzgXHVMWs9Fy5s\nWKQcmBk5mpJHx+ec2trhYjPmgfOnOPPoMUAYbc05ubnDqOoQUcYxIhCgdoGm9ezMakJwKBA6x2Tc\nMOsqzsw2uNiMuN+fxolyot7hRGWaVqOeeah4385xzs0nbNVzzsw2eWxni3PbcTIPjq7xhFbYrsb4\nGJ2XouHazlP5jovNiPOzSb737emIprFJvqpMGHXxuwYIswq84qoucylXo47Kd2gUbADj2sxvG3XL\n1mjGhdmE2lsfHr+0QQiOuurogjDvPI/PNqidBV1ULnBiNGO7rbk4G3P+3AbdTkURowRVsEhBb9GI\n8/Nj3PkKNwfpzFoxHSvvqY9ZYv4oIPV6Jr51omGvNdZP1x8w4AaFtJr/BgwYcGUox9FuY0lE7haR\nvxSRv45J4auO+QIReYuIvFlEfupq+nS9qY6+EnOiNcD/VNVvjtu/BfgnmHfkq1V1d8/3jq2uVmoE\n/YUu15p206JKLPPlJSSNwC1pH2us/HtT4ZL2sGSSzPWb0rFLZsKVZrrye2pn+diwdE/lvlLbS1rg\nKkLZ8tktX7fUopb7U2L5ea0yza6JG9E0ca1xGGPp/nfciqYcvvQzXvLU580kHWpQr5aaMVLmc8fD\n58c8XJ1AG4fseKgDbeV5vNskRKov5wN13TGqLedn3lQ084r2kmWsSh1oZ56dac3FyRjvAxcvjWmn\nNQTwGy1V3eFdwDulC0LbekKwlIRuWsEs5ko487vSOLpKaauApBypKiC1Mle47+KYMPeIU3CKtg6Z\nWhutVxCQJpq5GzFDRa0E52NwFczFKstYErGgXplNAtoJZ0WRWhEfGG00aBDm2zU69+xY5BY7445z\n1QZ11HC6ztHOvPVr7nBTx2jHEnTDCLrNYIFXnSAzsaFYgZ9Fmqlp8nELUz2IPAAAFRRJREFU0nlL\nuxp7mq16vXdon3G0DtWRiNwFfBPwcTEg6SlrXXwXXM9E3U8Gng/8b6raphsRkb+FmVr+FhZn/5si\n8qwYtng5potVuFQDknjclk1VCcuCpTRJrTJ/LU+ku/m6DoIViaiaiiIu89CtEqZ74LJ2khlwr+ex\nLKz6xvr/u/qadnm2qxYD6/wmy/v2wY1omriWOKyxtPFAZSwKQmZvcC1IYyHIwUO7ZWYkK7XmUFWY\nOeN7i7IhNDbJ4izxVlWYXhqx003QTpAdj58KdSuEkSJqprRupFyoJ9AK1SXHOA7tbjyimQRmPrIo\ntJKZGBCoG1lw8Ke+h9oYXoDItGBhvSrgGqGaC6E2brxqW6h2TMZZ7iM9714TWR/i++8a0AqarRj0\n49TMnk7pWrFhosb+oBXMNirUgd92VBecMVY0sP0BnhCgaex+RMF3ULV2P+k+UKi2ob5oCwXXxPsb\nGfOEcfxhvHsNRleVapemdtbAGuNoHaqjL8HyDM8DqOqj6119Na6nBvUS4LtVtYWFG/ls4Ofi9vui\n0/o57MLFq/P55duAhVIasOiP2gu7aUJlKYxVwqlsf69y5eX1yzaLNla+JrvdzyoUTBX7nl8WZ9xN\neKxqfxnL93KlOECp94T9THsi8iPAZwHvU9UPX7H/k4BfAt4RN/2iqn7HgTty/XAoY2nyGP3LlzSo\nSA+UJu5qaoJKK6HdEtpNzULNNSCXqnisxkm+NoEHmU6op+ACncUUBzHB41qMbilOtOoiIWuwSCWL\nKrXz8ySeSF81UTFZ2ymhXh10IyLFkPQkscH8NtL1VE6aktZJhKtQ7dD3W/tn5LdjX+L4CV4I40iV\nRBQkAiGW4JAO/Kxva+sBl5PM1UE36YVN+g0k0RqllJm4Pz2HKkRBrbEr6ZmUz2ifQoUJa5jI16E6\nejaAiPw+5kJ6qarun6i4C66ngHo28Iki8v8AO8DXq+obsYfw2uK4xMA8YMBKyHIZ+8vxcuA/sHcy\n/e+p6gsOrVPXFsNYGnDVOPvYvTx+7p17HbIqQmxZqlXAXcAnYqwT/5+IfGjSqA6K68Vm/m3x2qdU\n9bki8jHAfweeyXoPIeNtO2/Mn0/72zjtb7Mvy4UHE3bbvg66ri/rnDq2vOJfbn/ZXLi8f7f+LGsp\nB0hcXdnWbueX97RLflL5/bL7XWqrxPKz2vd84Ez3EGe6h/Y8Zhn7rfxU9fdj9vuezRzootcY12Is\nve8PXx2JSJXj738Xxz7groL8NGoyneBdJGAVcLNIwqq9Scq0E8lXV9dfNJkKw4hcukM6859UO1BN\nzU6YKcJC37a6SNDqrW0/s1a7kfE4+ib1ocjnc+QyIe1EevNd1ECSBuaWSGlRMvlttaOLlGJYm6LW\nXnpGoYLQRM7lSGyb+TOjSTJpeBo1Tj+Hatv6200kP9eehq34xeIzUZf61Wu36V6kNVNdVwsXHryX\ni++5d+2cwtPH7uD0sTvy93fe/zvLh+xLdRSPeW1kbrlPRP4KeBZGM3ZgXDc2cxH558AvxuPeICJd\npMlY5yFk3OWXLDb7raaX/SEH8HUA6P6r9d3OPMChu1zjADWSDnL9fE/LfqBlXINndZpbOe1vzd/f\n0fzZvuesSoi+AjxXRN6EvWvfsESDdN1xLcbS7c9+Hq7p2Uf0YsiTWzcWGFldJd8Ym0k7JpuhMk1O\nFi6aTU+JvTvzNnozgzVbksVsfUmpt+2AVPtJOqXasesRlG7s6CaCikaGcc3cmdkkGYh1kvpKACag\nhHpbjSndJ9Oh+YjUgZ+agLPkd7uvUEmuxeTn0fwe+T79zPIpTeD0ZM6uiSa8aEJ0rebPZmqUzNpO\nFH71jt23a8l+sHT9LgnyyKSe/G7JP1jtqNWKigLUzjE3xamb7+Smm+7Mv82Db9qHzXz/cbQv1RHw\nyrjtJ6Iv9Fn0pvMD43qa+F6JRYP8XiS6HKnqYyLyKuCnReTfYeaIu4A/2rWV3Sbz3dDt8nll09fO\n+S5O1rjeE1M6VpJgegKeh+wn9K6i7YS3P/BbV3xuxBuBD4zlOD4Dezefvc85NxIOZSzVF7pYLNJW\n4S6FG7t+Yiayzndjhx/17PtpIgYWmEMQ0BgNlzQCh2n01UyzEJBOTTgGxc/IpWCk00wkLEGztpFK\na8CiUEk0YKnCLdixfm7BEVVhmWi2XF9wMBb/y8It9gnM35b7X/jAqqmawKmtbb9NvIeoTVWJsSUK\n6M4EinXKjllgb0hakBKfgzKKJBChEIL52aoVV5SCuFmC3aufQy6Ds+YUKftYl9ahOlLVXxOR54nI\nW7DI0a+/Gsqw6ymgXg78qIi8GZgRqy6q6l+IyH/DeNIa4Mt2jeAbMAB41i2fkD+/46HfO/D5Zcl0\nVf1VEXnZEWIyh2EsDTgESLP/Ang/qqP4/euArzuMPl1PNvMG+Me77Psu4LvWamc/n9KyWSxpXGn7\nQTWwq8Uu19UnRjlaC0/ktXPbq+77kH6DNU18xdp3aYfIU1OdJBF5DiBHSDgd2lhyrUIqutnGwpip\n6KazUuQ4yb4QKxwouUx70mTK0uyJC3JVyZUwcrlsC2BUWF0iDQ6LvhM17aT8BZPvJ2s60dSXyZRD\nyNdPvJTJPKaV4OYu1pHD7jMYCbRruqKEe0y/kEj+nLUoMxtWUxZruDUhFwHVKpoXfexbYf5MZtT0\njHItN8gl45NGlNoufVOi5nstn30q3Bq8ZE04/y5r4JBM5YeKI091tJ9pSNwuD31ZQFwzc951lETX\nGKV5b/VC4pCeRbt3OyLyMxjL8s0icj9WmHAEqKr+MPB5IvISTMvYAV54OB07WvCzDmmDTXQFQbA6\nl0l+NRIMu3l85q6omhwiiXIKUlggQQ6L3IxiVaKTw7/ko8wTcFGVGtKk7zKRcebfTGis3QxdYd7S\nKKhmFuCRSYpjeolrQu5r5q6M13DO9cz8lRDqyH+nqZ+L1zX/lyOMrBipKJl7MlciSEITFvvRaX5e\ny/yYpZCi0dwH6RRpAj4Kv/yc1zSz7zeOrgeOvIDaD4cqePZa6V9VAMPSNXbT+vbDuhrJYfV1H+z5\n7Ffd55Vin4Glqv9wn/0/BPzQ4XTmCCPlz8XIMVG1qtRFrk+ejEM8NijCErt/QpxspQu9MCmSQSUJ\nrCJJXJsQV//F4qYUgLMlyu1E6Bz7GHN+7bxYMkaWIvCSwJNmRZ+TwIsaTebYBEQCiX9SW8FNl6JW\nkyAViUU9Bd9ZCZ1UCDSRTwO5sKf9hZ4ImuK4KGCyUC6HtupCFYNSQGa/3EGwhoASkbuBH6D3QX3P\n0v5/hjGadMAF4EtLpomD4ugLqGttotsNh9mPK21r3fOebM+sveKK0gMK+J02C6nEqCIQJ0nXCxOg\nZLYHYiBFKjlDLidjgRIe9VGIdRpNb8mcFW1c2DniHNqB+DRJF9fTeP2FiRzKlHRZ0PTitmU6sRWv\n3coKAUsaX26j6+umWR8kPwOIClEQpOjHKsGTEZ9JrkCgauTOEs2nu7C8qEhfTXqvNJR13Y77jKN1\nqI6An1bV/xyPfz7w/cBnrNeByzGQxQ44+ui6/m/AgAFXhnIcrR5Lmeoo+j0T1VFGGXAEHONAhXMu\nx9HXoAYMGDSow0F2kuvi0lWT1hMjDJawEAARQIiFO5Pi4jAy1ezTieazNpgJcRXhcNTeLlOyNWpP\nwcyGpq2E/lzotYm9uCWXjitNimWNtmyS3E0LKUvOrNqdYrKWNS7HIjVYumZYCirpVtxP0j6X+pE0\nrivG/uNoHaojROTLgK8FauDvXXmHri9Z7IcD/wnYAu4D/s8kfQ/CwHxGH+a03Lrb7gNjaO/Gam8d\naLO/gFrDdj7CqJA+GngUeKGq3n/4vT18HNZYWsiDCdJP3GkeLQVAGQBRmPtS3bEz59/J6eO3L563\nLGxcv62fwBWhNwGmifvMxXdx+tgHFv1bEiRLFa5zPxeut/j9se37uXnL2txzWt9NOC353R7bvp+b\nN4u86F0qJuTnJSuEX6pSrdq3t8yVuYc5r9yzbHrcD49t38+Z9sG9Dll14csejqq+DHiZiHwh8K+A\nL16vB5fjempQ/xX42khD88XANwL/WkQ+hAMwMD/OI5zm8CbEob0bq721cDi2838KnFHVZ4nIC4Hv\n5ehU1T2UsSTT5rLJUIrJcEFY7YYocB4/83Zurnrav4Uq1CvJhvf2k5w5fx831/vQCC63W9JqrWj/\nzIV3cvNoqc11KMV2uZczl+7j5nHR3kEDV5eufWYnCqhlbW5VP1Y1t+fey3FabuV03Y/dt0/ftHzI\ngZhJgJ/HFk5XjOvpg3q2qv5+/PybwOfGzy8gMjCr6n1AYmAeMGAltGnz3y7Y13Yev/94/PwKTJgd\nFQxjacBVoxxHu4ylTHUULQ5fCLyqPCDWg0r4LOCvr6ZP11OD+nMReb6q/jK2ynta3D4wMA84ELRt\n9jtkHdt5PiZSupw9QmwShzOWFgpb9iY3+6cHW5GHgOxlel2zwGePPY7dy+eU4Fdscw6qVTt2QVgy\nHS7XRUP2v6+yj6v6vVyrzl+FDnFQ7sx9xtE6VEfAV4jIpwFz4HHgRVfS9QR5IplP9mBg/lbgr7AS\nCKcxKfxVqnqLiPwg8Ieq+jOxjf+KVQj9Hyvav1bZtQOuI1R115lHRB5i8R17n6retnTM5wHPU9Uv\njd//EfAxqvrVxTF/Ho95b/x+bzzminnEDhPDWBpwGNhtLInIfcAHLm1+l6re/kT3aS9cNzbziL8P\nICLPAv5B3PZu4OnFMbvaOfeauAb8zcCyMNoF69jOH8Deu/eKiAdO3CjCCYaxNOCJxfUWRLvhuvmg\nROSW+N9hNW2SM+1VwBeKyEhE7mA/NvMBA/bHvrZz4JfpzRGfD/z2NezfVWEYSwOerLieQRL/Ryxm\n9RfAe1T1xwBiHZ7EwPy/GBiYB1wlVLUDku38LVjgwFtF5KUi8lnxsB8BnhLLov8L4JuvT2+vCMNY\nGvCkxBPqgxowYMCAAQOuFEeW6khE7haRvxSRvxaRb7rCNk6KyH8XkbeKyFtE5GNF5CYR+XUR+SsR\n+TURObnH+T8iIu8TkT8rtn1vbO8eEfkFETlR7PsWEXlb3P+8A7T5t0XktSLyJhH5I7Gy3mnfv49t\n3iMiH7HU1tNE5LdF5C9E5M0i8lVL+79eRIKInF6nvbh/LCKvj315s4h8e9x+u4i8Lj63nxWRKm4f\nicjPxTZfKyLPWKe9uO87Y3tvidFDa/VxwMHwZBxLhzmO4v5DHUvDOFoTqnrk/jDBei8WdVID9wAf\nfAXt/Bjw4vi5Ak4C3wN8Y9z2TcB373H+3wU+AvizYtunAS5+/m7gu+LnDwHeFK9ze+y/rNnmr2ER\nZmDEi78TP38mFpUF8LHA65baug34iPj5GBbt9cHx+9OAVwPvBE4Xbe/aXtHuZvzvgdfFY38e+Py4\n/T8C/yx+fgnwsvj5hZh5bb/2noNln/9YccxTDtLH4e9v9lg6zHEUtx/6WBrG0f5/R1WDWifxck+I\nyHHgE1T15QBqyYznWEzY/HHgf9+tDbXkyMeXtv2mamYQex19TspaSZOr2sTy89Pq8xSWz5La/Il4\n3uuBkyKSQ5FV9SFVvSd+vgi8lT4P5vuBb1i6zmfv1V7R7nb8OMYmCQU+BfiFuL18bvsmwO7S3kuA\nf1Mc8+hB+jhgbTwpx9JhjqO4/dDH0jCO9sdRFVCrEi8Pmsz7TOBREXm5iPyJiPywiGwCubqqqj4E\n3HIV/fwnmHN6VZ8PkoD8NcD3iRXb+17gWw7apojcjq0oXy9Gg/+Aqr556bC12hMRJyJvAh4CfgN4\nO3C2mEzK32MhARY4W5pBVrWnqm8A7sQi0N4gIv9TRO486D0PWAt/k8bSVY8jOLyxNIyj/XFUBdRa\npIX7oAI+CvghVf0o4BIWuXUoUSMi8q1Ao6o/mzatOGzda70EI/p8BjbIfvQgbYrIMWzV9dUYG9i3\nYlVlLzt0nfZUNajqR2Ir2udgXG+7nbfc5mWU2MvticiHYqvAbVX9GIxr7uUH6eOAtfE3aSxd1TiK\nfTm0sTSMo/1xVAXUQUkLd2vjAVX94/j9F7BB9r6k6orIbcDDB+2ciLwIs2uXlVzXTppcgRep6isB\nVPUVQHLu7ttmdLK+AvhJVf0lbEV1O/CnIvLOeM6fiMitB+2jqp4HXgM8FzglkkvkluflNmWfBNii\nvbux1d0vxu3/A/iwde95wIHwN2ksXfE4in15QsbSMI52x1EVUOskXu6JaHp4QESeHTd9KpYj8yp6\nevgXAb+0T1NCsRoRK+vwjcALVHVWHHeQpMmFNoH3iMgnxfY/FbO5pza/KG5/LmYeeN9SWz8K/IWq\n/r/xvv9cVW9T1Weq6h3Yi/qRqvrwOu2JyFMkRmOJyAbmyP4L4HewBFdYfG6vYo8E2F3aeyvwSqKd\nXUQ+mZ50cp17HrA+nsxj6TDHERziWBrG0Zo47KiLa/WHrQ7+CnvJvvkK2/jb2AC9B1tlnMT4zH4z\ntv0bwKk9zv8ZbNUxA+4HXhz78y7gT+Lfy4rjvwWLOHorMZpozTb/DvDHWOTSa7FBkI7/wdjmnwIf\ntdTWx2NmiHviuX8C3L10zDuIkUf7tRf3f1hs5x7gz4BvjdvvAF6PDYCfB+q4fYwli74Nc3TfvmZ7\nJ4Ffidv+APiwdfs4/A1j6TDH0RMxloZxtN7fkKg7YMCAAQNuSBxVE9+AAQMGDHiSYxBQAwYMGDDg\nhsQgoAYMGDBgwA2JQUANGDBgwIAbEoOAGjBgwIABNyQGATVgwIABA25IDALqBoSIXLjefRgw4MmA\nYSwdbQwC6sbEkJw2YMDhYBhLRxiDgLrBISL/NhYg+1MR+YK47ZNE5HekLxD3k9e7nwMG3OgYxtLR\nQ3W9OzBgd4jI5wIfrqofFgko3yAir4m7PwIr3PYQ8Aci8ndU9Q+vV18HDLiRMYylo4lBg7qx8fHA\nzwKoEVD+Lj0D8x+p6oNqXFX3YKzKAwYMWI1hLB1BDALqxsaqGjAJJbtzx6ANDxiwF4axdAQxCKgb\nE2nw/B7wwlgp8xbgE9i9RMeAAQMuxzCWjjCGlcKNCQUrMBZrtfwpEIBvUNWHRWS58uYQqTRgwGoM\nY+kIYyi3MWDAgAEDbkgMJr4BAwYMGHBDYhBQAwYMGDDghsQgoAYMGDBgwA2JQUANGDBgwIAbEoOA\nGjBgwIABNyQGATVgwIABA25IDAJqwIABAwbckPj/AQhkxmeHqirgAAAAAElFTkSuQmCC\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAacAAAEYCAYAAAD4czk4AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAIABJREFUeJzsvWu0bMtVmPfNqrW6e+/zuOdcXQFCukI8hIAQXsY8AsYCgy0wBifBxNgExEvBYKHwsIHgAANQImJnYI0BgYEFSISHAviBhpEREJAxGBSEUQgIMCAQunrr3nuee3f3WlUzP2ZVrVq9e+/d+9x9zj7n3J5j9OjutWpVzapVs2bNR80pqsoWtrCFLWxhC3cTuLNGYAtb2MIWtrCFVdgypy1sYQtb2MJdB1vmtIUtbGELW7jrYMuctrCFLWxhC3cdbJnTFrawhS1s4a6DLXPawha2sIUt3HWwZU53IYjI3xeRXzhrPI4CEXmWiKiINGeNyxbuX7gXaOEkICLPEZHfEZHrIvI1Z43P3QyyPed0f4KIPB/4clX95NtU/lnAnwGtqva3huUWtvDkAhH5IeCaqn7tWeNyt8NWcrpNsJUotrAFgy0tjOD9gN8/7KaI+DuIy10NW+Z0QhCRPxeRbxaRN4rI4yLyIyIyE5HnisgjIvKNIvIO4EdS+c8WkTeIyBUR+Y8i8hFVXQ+LyL8SkXeLyKMi8r3p+vNF5NeqcioiXyMibxKR94jIPxWRQ9+diHwo8APAJ4rIDRG5kq4/ICI/mtp7s4j8ExFxR5T/m0kFcU1E3iIi3376I7qFexXuBVqonvsKEfmDpE57o4h8TLr+oSLy2oTT74vI51TPvFxEvk9Efi499zoR+cB07wdE5J+ttPGzIvJ1R+Dwy8CnAt+b6OyDUxvfLyKvFpGbwKceR3ci8slp/K6k+88/rv/3JKjq9nOCD/DnwO8BDwMPAr8OfBfwXKAHvhuYAjvAxwDvAj4e8MAXp+en6f//C3wPcA6YAZ+c2ng+8GtVmwr8SmrvmcB/xlRwR+E5qiNd+1HgZ4ELwLNSPV92RPnnAv8lton5COCdwN9O956V8GrO+p1sP2fzuYdo4e8AbwX+MiDAB2ESTAv8CfA/ARPg04DrwHPScy8HHgM+DmiAHwdeme59CvAWBtPIZWAfeN9jcHltjW9q4yrwSYnOZsfQ3TMTjl+Q8H8K8FFnPRduy/w6awTutU8iqK+s/n8W8KdpQi2BWXXv+4HvXHn+j4C/Cnwi8O51i/shBPm86v9XAf/3MXiu1uGBBfBh1bX/AXjtuvKH1PnPge9Jv5/Fljk9qT/3EC28BnjRmut/BXgH4KprPwl8e/r9cuBlK/37w/RbgL8APiX9/wrglzcYs9dykDn96DHP1HT3zcC/Put3fyc+W7XercFbqt9vBt43/X63qs6re+8HfH0Sv68kddnDqfzDwJt1c2eCw9rcFB7CdodvXqnn6Yc9ICIfLyK/klQtV4GvTPVsYQsZ7gVaeBhjmqvwvsBbVDWu1FfTxDuq33vAeQA1TvFKTIIB+HuYZHUrUPfnOLo7rC/3HWyZ063Bw9XvZwJvS79XXR/fArxYVS9Vn11V/cl075knMBYf1uZhsIrLe4AOWyTqet56SHmAnwBeBTysqg9gdinZEN8tPDngXqCFtwAfuOb624CHV2xWNU0cBz8JfJ6IvB+mrvyXGz63CqtjdRTdHdaX+w62zOnW4KtF5Bki8iCmr/6/Din3L4CvTDshEZFzydh5Afh/gLcDL0nXZyLySUe0+Y9E5LKIPAy86Ig2M7wTeIaITABUNQA/BbxYRC4kgvo64MfWlU9wAXhMVeci8nHY7nALW6jhXqCFlwHfICJ/KbX9QWn+vw64CfxjEWlF5LnA38IkomNBVX8HU0e+DHiNql7Z5LkN4Ci6+3Hg00Xk80WkEZGniMhHnVK7dxVsmdOtwU8AvwC8KX2+a10hVX09pov+XuBxzPj6/HQvYITwQZju+hHgvzuizZ8Ffht4A/BzwA8dg+MvYy6r7xCR96RrL8SI8U3Ar6V+/PAR5b8K+A4RuQ58K8bctrCFGu56WlDVnwZenHC9Dvwb4EFVXQKfA3wmpln4P4AvUtU/PLrLI/hJ4NNT3acFh9Kdqv4FZvv6esxZ4w3AR55i23cNbA/hnhBE5M8xg+Yv3cE2FXi2qv7JnWpzC1s4Dra0sIXbCVvJaQtb2MIWtnDXwZkyJxF5kYj8Xjr89j+maw+KyC+KyB+n78tniePdDOkg4I01nx84a9y2cGfhyU5LdwstiMgzD8Hjhog8807icq/Dman1ROTDMcPjx2FnIn4e+AeYXvoxVX2JiHwTcFlVv/FMkNzCFu4B2NLSFu5HOEvJ6UOB31TVvXS+4d8D/zXwucArUplXAH/7jPDbwhbuFdjS0hbuOzjLgIy/h7k1PwUL+/FZwOuB91bVtwOo6ttF5L3WPSwiLwBeAODxf2mXi3cG6yczyBM84vQEpfTrPP4eVX3qYff/xqee00cfC+X/b//u4jWq+rwn1Oi9AVta2sKJ4F6gpTNjTqr6ByLy3cAvAjew2Fobp15Q1R8EfhDgojyoH+8+47bgeT+AuFM6N3t8fM2jYXQQ/ySPGVP7pfhTbz6q3HseC7zuNc8o/9un/emTIprFqdOS/LXbgucW7h74Jf2Zu56WzjSUvar+EOmMgoj8L9j5hneKyNPSTu9pWLDIE1Z8yCK46eJ6i4vobYEnyhAYFvcnDsNO6gmP8W2ASGQvLs+s/bOE20ZLW3hSwt1AS2ftrfde6fuZwH+DHWh7FRaxmPT9s2eD3RbuNVBgQSifJxNsaWkLpwl3Ay2ddRKwf5n05B3w1ar6uIi8BPgpEfky7LT43zlxrU90914/f5wUdcqSwqmp4E4Ah0pWJ5UgNxyLk/ZxU8kvoszvJqn3zsLp0dKdkH7r93RUe7ncujJ3mDbvKNzuvh1DUncDLZ21Wu+vrLn2KHBipfdhC966hS2XveVFuZoYT4iZnBbxbKpiO6ScOBnGYpMJuQHeG4/LRnUlnI5BTVWY65MzLu1p0ZI4wU0mRxdyAkdtGI5jPJsueofM32GunvBdp/pGc/OkNLg6r+txOKJfR22wCj41LvnaUfUfhftx9Ld39O27gZbOWnI6FRCRQ19UWdjW3C/3VkCjyz/qwtXPNZOJDRbk4ybTCoPIE/pYxisr+N4C0ysM6ohn1+Kx2vbq9ZXnNOpm4zSqL2WuPsbEH4G5bt53Eflh4LOBd6nqh6drD2KBRJ+F5Sv6fFV9fONK73VwDjm3czjzyfO0eocijpJ1IkSIsdSVCgzP1x6bcWXOrJZfZYKqECOyxutTV+uqQOp6nRvjs9KPYxd1QMOKmiuPx7oxS9fX4WzPJrxVR/iJ9/bsqodsiMNYl/UiPytDP9b0b/TMMczppLR0O+C+YE4ISNscWNzt5xEDfBhD87kOv7b8kYs0bDTB86QrhAOoxES83ia0P+TZ0uQqQdoDJ5KCrCL7WtPesX1dafvQdhPDEV+VK0x+3XjVqtVN1XrCXE80pV+OBSL90eraN2HJ6/LB1W8CnjwHV51Dzp+z3/m95PEXAe8HJqJa7h14g6tzUHW8mK7COoYRdVi8RUYLMFELsyoMqzA1d7DedXOsZoJ5g6txwDXj6d1wP/c1l6v7uo6ZqBrDzviJjMZtNLarfczXm2Y0hlK3Xbdfj19uZzQG1bi8hyPhFmjp1OEeVspuYQtjiAh7cVI+x4Gq/ioW2bmG7cHVLTzp4aS0dDvg/pCcSDsFn3ccbtjRyXqd7QGV2QFp4BixJcPKLkecO1zFUO3uJKsX8i4vxpEUdVjz47rdwV1olriS5KfrdNa1BLiJCnBVeloj9a3F8cAOuerUmp1srk9XxuIolc2oXT2V3d5GB1fvW/AOvZgkJ1XT7zhGO3tdeXeiatccg10wvz7nBkkhgiRJh0hRdakINA51bqgjfY/qztLAunpq6aSer6uqLyiSRal3dR6vSBzqnK0tVd8lhDEeua+5jWj4SFiDP0PfRtKXqo1/Hoc85hUuZbwyytV4S0jj6atnDmvvGDglWnpCcH8wJxGkbe13XBF5Vxf59MKEtOgdomJYu/Cu053XqoQ0SUeqq7psXU/FlNa2sdp+xtv74Zm6naziSL/zgn6ornulPzVjWL2Wr48Y6upYVCqg0v+6b4fhUY9FvrQyfgfG8xAwVcRol/eQiLy++v+D6cDpFg4BdY5wYZb+rDCQmicp9l8ErRZe9W4op9h8SN8AEllrg9HEVNTL4OcggroVpgcD44qKhGiLcsYnVyt2n6DjPriqbu9YNatIZETb6kCdlR3pmer2I0M9NU2Fg6q7XE4iSFiziRuZWlP/01iKquHSuDROkhjOuA95/HN/JWgaI7X+bQBraOlIEJHnAS/FVtyXqepLVu4/E9NEXEplvklVX31UnWfKnETkEpZF8sOxIf5S4I84qUFaBJlk5qRjg+XKrr8YbzOBrO7yR7rvw42nua5aBz4yVDoZGSbzPVmRVDIuIxyqfpV6DjNsVoZYrSRDqeusmcQh0s4gDQ1G1Ro3SW1l/HVkV5L1v707YDwvO8S6b+ugtkdtuttDmMe2vvQeVf3YjR4e4J48uHpqtOSE/lx7gBFJWuTzgpeZlTohtrboGhNKj+RFvWJcrldcIs3aEUyUxERYI7Wk9msHPamv+XJN/bi9jI8Ea1t6HeFeupeexcmofmTAUzT11Vv9rldjTr0ODE3S874ePHDLCMoYt3rf5gZpR/I45HEvkmIel9xPIU4cKmkcEp2pM5xz/RKsrbofm8AaWjoURMQD3wd8Bnb4+7dE5FWq+saq2D8BfkpVv19EPgx4NTYvD4WzlpxeCvy8qn5eSg++i6V6PplBujYaAkJ7UNSuFmnBj42SR7nF+lUPohWGVaMBY8Nnfa9ur95dZbyK6mPFuJqN0Bly+ayOqPomuoJrjAf6NmJgmTGsGnKTNFOurHpf1W2tPEP9TO5TPR41o1llPHUddV0ncojYjKCOgHxw9SXcWwdXT4WWYiMsLjW4oLhODzIpheitXH6nsckSRyrmh4U4NhBaK+eXiu9sp5/LS8yLcS0lpA1RKndgwa0kMq2kt/wbjAFpY2243hZpv4i2sCcmFr0kCcvwqJ8HRlKOMZDEyJzgAkiQVL8xqoxH9OO2XZOkSjcwcQlDeWtLrA4dGFFhYJUkWkuJNSPVRgit9a1mlkNnxu0dByekpY8D/kRV3wQgIq/EbLc1c1IoQRsfAN52XKVn5hAhIheBTyGFXFHVpapeYWuQ3sItQlTb7eXPcSAiPwn8BvAcEXkkHVZ9CfAZIvLH2E7wJUfVcTfAlpa2cNqwhpYeEpHXV58XVMWfDryl+v9IulbDtwNfKCKPYFLTC4/D4Swlpw8A3g38iIh8JPDbwIu4hUjKM38BJtViVJ+9qX+HOK5kdLZIx9LUqjGyli5W1HkH4FCX0rD++ewauupy6t0Y/yxZZamiqSSq+nxJxnkVVM2QW1xk/biPddur7qyreNQwkrpWyuUxXx3Dur+ljSydyrjNuFn4FPMwmm5U1lDQLzjk1r0W+fTUaGly7jKxhWLvAcLEJKVakgltpYqKigRwIavIklSS6882H2dSlMQsbVBUYbEZ6jNbCkkVmOprpKin1B2UoswuxAF1GdjzrgP1nthaXdEPeLqgSJCkuhtUiQX/yEgdmdVwuX7Xy0j1GIvkSJKeTCpy6ZyeKuhksGdVLyLZonRQXeYlI0uL0fDNKkb1Jp2aJMug5tNBqs11Z0lxE1hDS0epyNfVurowfgHwclX930XkE4H/U0Q+XPXw8y5nyZwa4GOAF6rq60TkpZjaYSOoIyk/MHsfZbayKMUVRqMKLWNV06oaKsSBoa2q8+rfR50S924wVGZc8//gk1548EoqnkYxwkRKHdkzaK1HT/7f+OFas1KuVgvWZypqXA87j5XHJkfSSOMka9SEpZ7ak6lWUcY4xiMzqXwvjzkM414z5xBtFdkA9C44m3FGcGq0dO6pD2uYCLElMY1BRQXV4g1oYwuxRKGZ2x7CFkp7ZX4JfjGo8SSpo/Jiuqq2io0t1qsLbF7oi+OB2KLtOrNhxaTSysKyRFMhSqAwtNhAbNNC7sVsT+kTnAx4pX67HqS3b78c9kfa2HVkeL62tWUVoeGd8PfG/Ir6UwbmXo8nZGaUGGYcVJLqjbmIWt9svIQwSSpMZ7gVVWQYxrW8NxlfO3JOnIyWHgEerv4/g4Nquy8Dngegqr8hIjPgIY6w6Z4lJT8CPKKqr0v/fwYjqJMbpJ0jziZo6807pY8QhkW0LJajRZrBblO5ugIjg6Z6f9CtdbXecmG4FwsjS/VUuKqXob4+2qJf1VG7nmqNV91uQTDboCoXVBj6B8VT54BktQ53MW+g0f/EJHVl/AreIoP7atZ3r6u77KDd0I8Qxi6wuR+1Ubl6l0dBVGGxoRH3PoNToyV10O8KErLEYgt2tp/khbUsyJIlK/CdPZ9362ECfiplkc8wOCqYBKUOwtQW77we5sVVK9tKZnBZcpJ+YF5xMmYsoRNrUyn2oTChSCTFhpS+wywzx4TfHGgGhusCpT5t09i0NgTFxkaWsqQwRtcnJtUM98GezdJbtmVl6cyYeeVkEus1x/oygsQoTSpMbU6tfhcwWi31HjcDEn4no6XfAp4tIu8PvBX4u8DfWynzF5hG4uUi8qHADJP2D4WzzOf0DhF5i4g8R1X/CEP8jenzxZzAIK1e6B+YFldUwF5IiKZyKAvmeoayem4je72U+6teeklaqeuFSn1RucVWHbYvV6nMFFxXSSP5sh9UXJmxmCHXGGkR8fvKg0owpkIi5jXSXfEuWucenIdE0o6ucYeeZ5EkFeWxHdQxuRyjMc7uw6WtFZfa2rVY16hENzXinsTD6H6CU6UlB8uLgzqpdnLIO//YVkwkDsW0sefygo9A7zMTskLCeIHMczBLXMDgOi6DpDQ4I6QFuk8Lb2J06o1BFZVfltZiknTIC/1QX2aGWRWZ69Y0Dpn5SgA/B7+gOGGUPjLgWUsvEsHljBNqasXaCy+3j0BISp+8IXB9aiONt0mJVia2xvTBrvlFqq8Z8Aha9SV76omNw+3w1lPVXkT+IfAazE38h1X190XkO4DXq+qrgK8H/oWIfG0a2eer6pHYnLUO5IXAjyfvojcBX4JNzScWlXwLT0qw3d5ZT+kzgy0tbeHU4KS0lM4svXrl2rdWv98IfNJJcDjrqORvANYZ2U5kkFYn9BfawcCYpBpRb2cbVs4ViGrR42YddzmjkDftxd6yrkGKS2ft1lqfSyDhkvXEqKKtG7nF2hkEP3o+nx0p7R5xvin3rUgiTooKTJux8bO02esY5zR+w1hmyW3o6+C2Wqnnqt+17SBLT6ZOGMZoOHdVtePGOJaxrexaxV14A1CevMzp9GgJ+h0OvJfaflPsNVktlt95D83eILmQ6ClOIE4Zu4VXqjVYlbpzo8Nvqa5pNmEm6SI21gaiI0eE2iSfnRZyH+uzTIWOndqlvlLPt8Ak2XwmVZ0uSZda9aWy95g6Lz0vEHaG/h/AoWortjrud5BkVxuPgyRpqWurd1XdEztaVaTDmOyGfsP8gXcDLd0XlKyNML/ky8TNultZNd4nPXdeLAfGNKgN6sWwLJ7KWByumVF1XgMYMZnYCPX7lThcc0nfXk/w7HWUxfNCzCsMMpcptpr8PzO8kCekjBb22sPJDiZam7kOTd4/MB4L82oanrXyFBVfGQMdnivjXf+OOvpvFTIygmdPpQOHNDeAqMJ+ePKp9U4VBMKOVmoqG3y3FLN1iBamgMuTQVCnBIHYOJsHyQmgphGomJG3uqUf7FvqtTgc2DNWr6mkpOAn0cwwA53as5lWY6uVWt4ajpWdZhSlQa0dCQyb01aLKrA4a0yU2A51ZJUhUFSZpV9JJVjTm9mz9MBczjanuh5ttDrXpaNUf64TpJOBNlsdbNJZDepSW9WY4RRiYrAbwN1AS/cFc4oeuvMy3gmlyZ0ZjxVkJEEBlVSQ/taElHXI1b1c72o9B8KICMNhPCiMEYYJLFGSC26WPsYn1OtT8aOwLpB2bkOfhz4MO0RWGNvgBWVtu16HMcqMesWVtjbYru5q6z5kpl/r9Ef4VrgNUuaAt7q8O6/65Ko6NwBFWJ5wt5d04F+eevb/AV+iqvMTVXIfgQrEqVbvxV54mFWbi/yOwiD5SpTyntRrqUsbLXOk2FRdVcYpsXawUFtwNUp1MDYttgFw1nyRIEJuiDGty8CsJA6bzAMQD9KyMTpz5CjPJEapK5s0u0dxUsjjhTCMBwfneRkHscqKba2S6IzRqCln09jEzDzD4Fo/WsWdEpsKh/wzim0YNtzo3QotnTZsaGrewhbuflAVlqEpn+NARJ4OfA3wsSmfk8c8jbawhSc1nJSWbgfcF5JT7d5aJIUsReUigXEQ2LSrqPXe9e4tl4keaqnq0J1HJSYUDyYP0Q1o1Dr3vLOSvLPT4f4Il4THAduXVNJMdrFN6hZ1dd1Vm/WQKEg+R5Lv5fHIw5PbTSoGrVxSqb5H9Vd9P0zVMfbWqvBqM15DvXWdx0EEltEfW24FGmBHRDos5M+xYVXua3AQd+Jo7pF24rF+78JwGiEmlV/MtqUsvQ/SEaLV9aruXGX9zr3ahShjySHZX1BJHnUKE1BJkyaXy95+ef41cWirloTAjkckyadoW4A4i8njN0v7OtIExElSp1X1SUWHrNBDkbxcNb+zKjKvRWB2r0aRSk2JgmYpNQgEKZInSdtRVKwM7yvXl8dXo4zsaUfBLdLSqcKZMad0COtXgWnC42dU9duSr/wrgQeB/wT896p6rBlvZANiPEFqdR9UC302SuZ7cVyPesAPNqyssrIGR/xodI6gVmGUOZMZXS6X1HxZHRarxbxmglnML669MjCDzEjwQ99qlaDrhn5kt9V6cajVd+UkeqxMCRWjXx3f0u80JtngWph3HNouZ6FjPnPC6J3UdrqCWya6E4CpIjYnKFV9q4j8M8yTbR/4BVX9hZO1evZwqrTkFKZ5Aivi7FNUclHQYCus87box86hvRtCOmY1V1IlIYp0Dm2iLbwuqe2WaQL7tGgnp4VhcbZy1jCIF2Kf1IdZbdeofSqC0aLSH/Au/jspEoQRnaR5au3aRjQOdNIocWqMWvrsmz3gQ8xRJdT60Co6ug9kVX0bCi4x29Gk6odSCE9gNO4axc79KeVboxT9pngdzjRWCUhVxeoAcuogrRetI+CktHQ74CwlpwXwaap6Q0Ra4NdE5N8BXwd8j6q+UkR+ADtZ/P1HVSTRziGA7XDKOYHsEUbe+Qzb9PocQ65Dgp04z9KGHWiTYTFN9QxMbtBly8q5nqJjzgdYxU6w5xPfqzprqzv9XZVCKmmqBN1kTCvlecVCpVQMIDaUU+SFGdcGXdVijM3SVo2b1ZWRrFMaDAcJSTas0c5RKY4W5sAxjPGqfbCWele/19oK1oAqLMOIoI5MmSEil7H4c+8PXAF+WkS+UFV/bLMW7xo4PVryyuRcVxYzJ4pzihMlqtCHISCwSwthnDq6zifGJTiviCQnhLyIOtv1SNWOTmK1eALJ48436boKYZnd+wAiTNOCnJ5zXvFNwCVPu9A7VAXnFJGBWcTgUkStpC3IDhJRiFEKo9D820fEa6rHiobOmdTSm9MH02iMKUtrJEYBpW1JTDC3K6L1Hsza8BHf2FhqNNxjeiZGwfk4xHcOxtBdE3FVW6vvBCBGey923cZD3GbEtIaW7jic5SFcBW6kvy1FqcOnMZwufgUWMPBoguph59GAS95rg5fboLaqD9VamYHJrHriWTTlFG4k6MjLbDU8fr6XHQrKwdvKC86YRrSQKgolvP2K+C9dIvaJK3jVuGvOd5MJOjG5QXUw/m8MVgrDGjwBzdicnx9ce4fxEgW3jEOZFXwBtEmh+1MagfrgratcjAZnirHLfOl3n1QmJZyL1VnUkptt9uxsxlg/flzKjE8H/kxV3w0gIv8K+K+Ae4o5nSYtNS5y6dw+k6bHibLsG5bB07jIovc4sUXOOcWnhdChhJlN0kXX0AeXdu1C7B1RHW4acE1MkanS/K3qaHxEVWibQOMiLpWZ9w1d71PQESnthuhom4B3ES9KUMGhKTqWPRtVRr9DYq4xutIHVVh2DSE4Qu9Nde2Vpg1MGpvEzkX64OknzhhwkkicKJO2NyaS2mqbQFQZ4bvsPCEaU4+JYTU+4l1ERJn4wM6kI0RHFzx9dPhqhxgRvGgZe7A+ti4gogR1RBVaZ2PXBc8iNIU5OtFSb4ibEdMaWrrjcKYOESLiReQNWFiVXwT+FLiiqtmRc1102/zsC3KE3G5xY12RLTzpQGwRSZ8N4C+ATxCRXRER7EzQH9xWFG8TnBotXd27Mwhv4S6Hk9GSiDxPRP5IRP4kpWdZV+bzReSNIvL7IvITx9V51odwA/BRKVHavwY+dF2xQ54twSovXni67rxrMUgrXkbSQw4ZpF5ScjAdslTm+rKrthNi48pzzf4gAlj4n8E/tA6FpClQao7ebCGH3KBWjDqEKqoO+BbJLNWbwwYNid2ytCRFcioJyHIYodyVOqRRypgJWN1JWsl1ZRyBIbdMfcZpXaijcsBZR//rcEtF1b8yTiVUUqUCVREkxuG8VorrlyWyA/H/jgFV6OLm+60UJPVnMHtMD/wOaU7da3BatHThOe+ju+2S1gd2m46+dcxDw82lxdiZtD0TbxLLsm8suLy6sqNfLhticPg2mJTklcmko21D2flPm56IMPGB3XbJzNt/MCmsSdLUPDTM+oZ53xYJoEgoLtCrK+oyr0LrA1Pflx1/F3yRnB6YLLjZTeidJ6oU6auPLqm8gtl5gLYJtI1JJTttRyOxtJXbN+nI6vEu0iZVQeMiUaX04WY34arO8NGkrDaVN4nPJMSLkwXL4OnVsQyeoI7W2fhHFRsrF5j5vtiB6rGwMQssY8ONbkKvjvOTBV3w+IRPiI5pUwU4PHY+bU5LmyQbFJFnA98MfJKqPn5YhPwa7gpvPVW9IiKvBT4BuCQiTdrxrYtuewAkKP76nBJQdNmXoK77lelSAAAgAElEQVT5GnkB7eNgH1K1Bdw5YuuRZB9x2S0tKs2NJdTMoAsDUwpxWJQbD40zh8C8yKZArBkHt0yTI6eOzkFPdWAOUgeidQ6dNAPDirHgoSLIyvOjFPW+8kSoI4PDENg196vv0Ulj7Uk2vmoJoDsKTJtwKOOpVfup7+W/Vky3TfisBuTNEdlFoPEHgs6O6j4GFFPbnARU9duAbzvRQ3cxPFFacqJcmCxwokW9ttt07DYdy+hpJBIxFdqy8WWRzAuyS/wvq5q8i2WBnzY9E2dM6ny7YOJ6piki7OPLc/TqaCQySdcmrmXuAjttRx8dk6S2alzkRjfhXFrcM54ONRzTgnyhXdC4yMz3OIlMXGAeGlNXJiYQouPyzp6p1KJn4o35NhJZRuvfzPelj7268nxut4+O8+2SPlqfd9uuMAuAqe/LOMbETDPTnLjAhXbO9W7GjW7CxdmCme9G6sgHJzcBWMSG/TChdYEuMamLzRwnkUVsuN7PuLaccnm6T+Miuzvm+9JHz3yyoI+uMM1j59HJaGmTZINfAXxfzsSsqscGIT5Lb72nAl0iph1M///dwK8An4d5GX0xm2QijYrsL5PHXFrwnIBzSE4Vnhfu1RQYbQOtx6miziGVzUj6iLu5GPIO9X1a0HW0ACMpTXznLGWGc7bwZqOt5ueSlBAidL3V58Sy+DqHZI+EZWe5nyaTwlBdjX8KvyTLruBackXlHE26pOSKqvNHiSA5HQVAHwqTVW9F6ZKTxLK3+5nJ9f0wtlXmYUSGvE3r0nA4h+R66pxaMYzzPTUN0nioop3n5zcBVTZV591XcJq0FNW8tPromPct875h1vRcmu7z0MwWyWvLWVmkF32DiLIMDbOm4+JsXhZWgP2+NelGoJE4SAMq9NETExMDuw9wo58yT9LPzPfEYBLCxAcmLoxsSWCG+4k3xjNLtrJJkWRCas8kwP2+pQ+eLjouTBZc3rnJ44sd5l1L60N5dq9vC159dIUJZ+jiYNeZ+J5l8MxDQ+Mie32Lkyb1MdmR1DHzPTPf06vjRmehGlyrRdK7NN0vTHEZGuZ9w3vtXMeJcrOfsogNrQtMXM+O6wgIV7od5qFlr7cPwPnZgocmN+nUsR8mLFMkgF4dV+ezjebUGlo6yrloXbLBj1+p8oMBROTXMf/ib1fVnz8Kh7OUnJ4GvCKJhA7LL/9vReSNwCtF5LswNcsPHV+VQtfZQtc0wwIZK6YSoi3IdcrzOhWEKtIFZFlpPkKARWYAcdjlJ0ZjOY/ckIMog7O2JUsE2VGj62G5XEnA5wdc83ffU/IZxaTwCDq4i/ZxkNpqxiSJEesqIwgDY/PO2uzTMzFUUk5v/YkRWQarN1R1xfTxKY9UHus+MbFcf+XZh/fDu1knBdUp4Pveykpm2Ctljp0FQjih5HSfwKnRksMW54vtAjfbS4tqx4VmztT1vGd5nsZFLjbzIuHc6Gacb+fEpI663s2YeZO0ojqW0bPbLHmw3eNis8+j3Tl69VxozMX2SrfD1PcEFVqJ7IfWJCKJvM/sapJOPAHhZj+lV8fDkz0iwtXlDjPf8eDkJovYENUxdT2NC+yHlpv9FAQmruPiZJBwJj5webJHI4Hz7YJGTDW3H1oaiTw4pajU9vpJkZSyam2dpLbbdGUcz7dzvCiPLs7Re8elyT6NC8aQQ8vM9zx1doMLzZz90PKQv1HGImrL+WbBe8+u4VEWseFcs+Ci2Hh10fp4rZ+x401Km/ie8+2Sme94xuxx9uKEEIWp64le0v3FiKkfBWto6YkmG2yAZwPPxaT4/5CSDV45DIez9Nb7XeCj11x/EyYmbmELJwPlScmctrS0hVOHk9HSJskGHwF+U1U74M9E5I8wZvVbh1V6V9icTg2axrLD5gyqMY534nWW1Zy91efMtOlYeY70HdQknRhM0hCXgicySA8i0LZDvVmllyWbxo+TuGZpJbfv/CBpRIXQpXY8tM3Y/pKjWPRJAqnVclnFlqWoLOms2oIaP0iOJbq5t+tuaEv6LCVVm58sBXk/4J4z2mZ8cpmsTiy/V8pk1SCkOG06lrbq93gCp4hyDmQLtwytC3zQhXdzudnDS0zG+Z5WAjfCjIvNnE7NqaCVQMDxlMkeXXR06nlKe5OnTa/iJdJlm43reMDv0WlDp56Ao5VQ1GQP7OzTSuB6mCWpKXKhnbPrOx6ePlpwe7w/x55f0KYT406U955c40aYMnXmbACw45Z06nmg2aedWdmr/Q4fuLsAYC9O8ETON3N82uBfDzNTm01CsYNdCzt00fEeznOxjcW+06nHJyeEK90OXjT1NdJKxCX15I7vuNTuE1TYdUsCLkljjvN+jkPxEtmdLgteAE+ZmMdkUOF86q9P7Tq0rAW7flnGYi9M7L4ol9ubnFfr2zy2zJIKcC9MeWhiar0j9WmcmJY2STb4b0ip2kXkIUzN96ajKr0/mJMIursDyTvNbDvV4pptLtnoPmnMQA+DM0JeWMGcBAgWe6htxwtkw6DKy/dy6nU/OBkYY7I6dSJma4ot9K44O4ygcSBNcazQ1o/bLfatSkWX7V2ZkWSm2SfHi5xqPTPBJvUvpFOvzqETjzY2HqIKfYQmHbTMzhkZaueH3L4DfDvgmDcG0gxjnpllZqiZcYYIOdKzuMKQMlNW74vjyIYTYRR9egsnhx2/5Nk778SnqKWZQQHsumWx93iiLXrquB5nacFXHmquF4eJLu3MZq5jJqbyend/AdfGwhScRB70N+i04aHG06lnL07ZdQse8HtcjzsAnHPGWC43N5lJZ55uKK30dNow17YwzFYC59zCnCAkcCXs8lAzRNj2EpnHll23pJXemEvCNagjIlwJu7Sup4tNYVbTxKTzmHTqudzssRcnzFxnzFrtjNLl5iYeYyitBPbilIDg07hMXcc5t2AqHT6NZ+4fwI0wS33pecDvERNDn8eWS/4mnTYEHJ7Io+E8DzXXebC5wUQCy9SXeWy5HneYSE9Qx65bMnOD6vFo2JyWNkw2+BrgrydVcwD+kao+enit9wlzUu8Il3eLG7db9EjTmA0lMnjuNeb9FmYNsR2YQ3a9BpBecX06fNqHwSU6e/pV9o+yiLq8oGJMkcq1vHXF888tJ7gujCWFVLc2zlzPG5fc2sU8/URGbu8lQoWqRWRYBrOV5TpVQScDzjktfJUeHtKYiOFXsvPmervWsginnFcAJS17qLLv1lAfcs79STa9A5JP9nxclZgqSba4k8PGkZRLDLIt3DLsSMeHTN9GUMdEQllQAQJSrjtiuX892m78grPdekDotMER6fDMtaUlMJHAg/4GESHg6LRhVxacc4u02EphhFlS+GD3TpbqiThoKfjMpGOpng5fJIqJBGbSFeeFLNldcns4InuJLnLZVnpm0jOTwE1tmUdjcoX54PFE5jphHk2iy0y2U299lIgnEqjHxerNEBDm2hpTRZlrw1IbYxo4gjouujkz6Xi4fZSgzphtYvIz6az/qa6WUP7PtWHX2Rhecvu0ErkeJ4a/98DjtCnphvVnQ2I6IS1tkGxQsYglX7dpnfcFcwpTx7X330lnicAvW9xSUyoKJSf9ixNbjMNURvHpcugcFQsv5HoLgWRBSod07BLBdVpyN0UvaGtnm/JC6nq7p46SMqPU2yk5iGqJUCFWLjZDmJ8SnqtKOjhKZqZWR7Ov+GViUv2AV4nmUEVcqKMslGs5qG3mDymkkeWZyhfzQ/Zb+qwOzExr+J/7XMIrxSpiRaViLBEjctUr/6MnvSNZb2o9CjY8AZ8hnQt6GfDhqZdfqqq/ccJW7xvYcYEPaa8DEFBCUgRkATcozKqNSQSWuo8X8OWsEnRpUnnEhGsRgupob9RVC2VQ6BLjyW15ZIRDfm4VvECLlH1ol56ZSHreLfACnd4szK1Nz2S8HmSJbzqC7tUxY+lQ5ol4WhRfjUPtphNS32v867ELShmjgJaxAlimcamfq8eXqo4Iow2DS/0HmInDIey5fWbiCGvU4fMTnBs8KS2dNmykVBSR797k2ha2cKaQdnv5syG8FPh5Vf0Q4CO5zREitrS0hXsCbo2WThU2lZw+A/jGlWufuebaqYGIPA9bODzwMlV9yWFlwxSuvb+Fg3c9SO9Splm7X6QOSYFTW4YketU9yLt4SzRm0g6WmTNJADmKeE5PEZsUzdjBKGp33qBIdT1JRUMGTvuODkrq64SL1v8dQ+qOqj6/L7iOEum7To9dP38gm+5Kn4EhQnocf+e6R3VU0l3+nSOfxwkl068EqCO9j97vyv9RfdU7QldwPwpULHr0hiAiF4FPAZ4PkCJ2b5jI+pbhjtLSSegIbBpPxQ07+2qOBEzyN2koSQpFIlIcQpf2+tnCk8v6JMXkcl4EhyOm8nmXH4tEMEg1yPh6rCaTQ2jFE4kFj1nCu5QVaJMkEdEiuZRnJQ5ShjDCrdPIrowlnYDiRUr7HbH0q8WVe53G0o8aclu5f11aNLJUFWXoa77eVpJQ7n8rbjSGGdokc3gnB56ZbcpnTkhLtwOOZE4i8g+ArwI+QER+t7p1Afj124XUJuEwamjOd1z65HdaCJAU2HHR+xLZVxU0OnMYayLeR5yLOAHvIo23AIytizTpIF5Ip72XwbPsGroczDJPHDcEYvTAaij6EC1kiKoMKrEUqVhTJGStoiE7Z9GJJ03A+2S3SvQybQKz1ozAms5eWIRoO0zYB0fXe2K08/l1gMg6WKaI4p2dqPfVSfE+WNiUmAJDqg4RkfulJ3Z+EPElOUEwELPzEddE2tbCvuSoyiGFP8lLiR0Pc8TU/zyOiOHsfSzBPF21AOUAnP95k8kz9p04Mio58AHAu4EfEZGPBH4beJGq3tykqZPAWdDSSekIbEE8LxMiSiRiy994bntxBB3mT1MxD79S1lXKGSeM6vLiAG91Ja8zYxrDM82a6CA1o8rQilliVu+NGJlgfVItDMghOIYwR3Wf7bor14b2x8zAV4ni6vFqxBemNeqzQNDE0ATa9HzGLeNdP+dwBf88RhmPpmo/17na59X3shFs6od0m+A4yekngH8H/K9AHczvuqo+dtuw2iwcRoEP3HmMn/ovXsFeFOZqh/VCmkzZXTUbaTNkw+IsiVfZgHrB9bSZ0CodcadjXW/WMXcqdOrKPSdKp465NqP2gGTYHXADirHS2ou0mDdQK5EJgVYiuxI5l7z78k6oFceutGln5spk7zSwINCpclNhXp3Ar/E23fhAZF068T+TWMxIZsj13IyTYqDNp82z0di8iHrOydK8gcRCvSzxxbNrQhiFY5lrw02dFOP6rltyTjpa0bV6/WzjWBu1tAalpMtOcFxU8gb4GOCFKc7eS7F5/j8f19QtwFnQ0onoCIaNhC2MbjS/jJkY5N9lkUzMypWFVkfProMRg8OXOuvrrQw0ZNJRLIt2fQ9IWoVxW1l6qiWuVvxa5pX7U0MrltQtXzcchrpyHw+FNX03RnNQ4gFPSNfWMWDrx7itmGpZvT5mbMM72RgO0tIdhyOZk6peBa5i/umkYH0z4LyInFfVv7hNeB0bDkNEXgC8AOB9nu55Wz8rLpQZuvRS8oJfe/LkRRNs154DT+6lmFWBeldmE6bLnkRrXnIrsfJU8tyMFihzWMSFefEWsqCPPnk85b1ZQIr3EXTMXM9MYjLmjtscdrZ+NNltoTDD515smK94QUXcaJwmMjDHVno66Q8w4HNuyYyeTl1huI4UvBJlJj2tRGZiPkSdCDMNtDl8E8boZ+I4Ly2teIJ29ASCZhVJQ1AtjDWgdAhzdVw/gWFWTmbEfQR4RFVfl/7/DGPGcWpwRrS0SViZES098+kNrTRpt22QmcUqk7HFMh656K0ytcMYVX2vZmgHF/C6/bjCNA5KTjWuh/3Pbawu8OugprV1jKKuJxLLWK4rt9pmJJa+H4//WB2a7Qnj+g6qP08CJ6SlU4dNHSL+loj8MfBnwL8H/hzbBd4uWDcqo7ejqj+oqh+rqh97+cHtwcstkHZ7w+fY4qrvAN4iIs9Jl/4aR0gVpwF3mJaOpSMY09JTn/Lki024hTVwQlq6HbCpQ8R3YVGOf0lVP1pEPpW0A7xNsEk4jAJ/euOp/N3/8IJi3ykpjhOIA3ExZegcMlRme1O2yWhKRtYHi1asyeZSgzhGNiILf684Z4nUut7T9Z7Qm296fYxnqMNw8S6O7EEx2WjqZG4OsxNJ5UGQVWTL3heJL+Mekk0n45/b1+gK3pKzYlb9UoYT4bWNrpiWnNmWhj4Yfm0Tis2uTvaW++TKdQvcOfM9u43FAANK4M9eHXv9hKuLGYvQEKJL9j5P3+d38K0cBaK3REgvBH5cRCbYifUvOXENJ4M7SUsnoqMMWQrJ0GcNQ7WX7TTZiKRWH42lgC5JxXFF6l+nUquliVzeIUdKQyFlD+g0jCQJL9mt/OCmdRXHkbu1HJSGitS4amcSR5dSZdVq9Vqicqw/ClHsTel+Pdar0s7qtYz3gMeqhDV2ul91INkUbpGWThU2ZU6dqj4qIk5EnKr+ym12f90kHEYBf8Nx6T/OUpZZuxZbyhkjGDzKYOxRp0Bw0GWPu4hllE7ldpcc2Guqqzz9vHnblSnRwyxWnnOpnuzdp1C86bJnWp/qKjmosu9B9gxc9ZRLz7pl8iTMHnERfDpP0VbeghkXdTYusRn6msu45dCepZJnIKxUV34uZ8TVdG3eMHgRJk/GmsZL+Txmkt5Pq+mslUIQ3FLwy8pDMoDvod08DQ3Sn0wVoapvAI6yS5023ElaOhEdgS2EC+0qdatBUMUUsTpiFtme2Yqj01C89XJdKEyTgjDXF0t9g6G+9uTLlvh8b/AArBb9DErxlmtxVlatrFsh3FXPwuy5VzwNFey47+Bht+rhl/vaa2CeGPTBtb+yI1f23txm7o9D8Cqj/q1jSLV34Whsq7Go+9YRmeJTH1wpH/Vk3OYktLSpV6iIfB7w08BfVtXXryuTYVPmdEVEzgO/iu0y34UlZ7stcFg4jMPK+4XywJuWaZG26AXqIE4cMZ1sswO3knYE+XBoOmAbLRJCSTgIZVEvSffyQVJAvRDb8SHRIRX54CZdEu9FS0MeG8HCDQ0T70AK8ipkkRSmIMWtWuLAZCmMxA7gusD4BF+pE3IixOgpB4br+808IvXmMGjpWx4HdVLGMz9n6ewlJU7kwOHauh+FqUsawyaNCVJc7F3Q4SB0OmDslmv6tA7ugt3eBnDHaOmkdAS2iO1pXxhPR6TFJaegODpcG4i02VabFr5Ho8OjyTnI6pxJZ+V0OCALwyHZDJ2O90yuKmMHYQMtyqS4cZtttUNoUTq05PJs6Uu5DC2Da3Vx467wyeDTRqvGC+wgrEcg9XWZ2ja7q4zwqc6mp/HS0rdQ7ivTRBxdxZTbZG8d4YQO7Seox86rjMYu0Jc+mGfieinsUDgBLW3qFSoiF4CvAV53sJaDsClz+lxgDnwt8PeBB4Dv2PDZW4J14TAOA7cMzN5yrYTEUecsPl3jUO8PWNZKgj4YEv+NYuzJkLiwCrNj8fEiOEdMSQAlBYk1USszwkqcV0qSQJ1Ywj2JOqTRyOk3YAgxtEJU6t2IsY0SDIKFWUq4jTL8VjpFFUFbP+CcYwBmaXHZj8cgR6MIarHt8qzPSRRLmCFXokMYLorrQslqixML6ZSOyJe+rGb4FQrTtkgcEbcMuHlneaU2hBGDvTvhjtLSSegIYF8df9xNiyclwAW3xKPc1KaEKgILnXMt7pRYdznUjyMy17Y4+2RnJDDnpCtxt5TPgUtbwih0UA7dY2F9epb44pDTplhxGepQPjneXI5zl8MUtQW/5oCX6lzb4syU287Pm4NSU/qRcbV2M5McaG5Pp+QQRnksYvJp9BK5IPPSxxxOqU1hl3LfgDLOjsFZy97FvIRGuh5nxUt4qb68h6FvTWkfKA5hm8IJaGlTr9DvBP434Bs2qXQj5rRy7uMVmzyzhS3ccbgHJKctLW3hnoCDtPSEkg2KyEcDD6c8Y0+cOYnIddZoVEkWBlW9uEkjtx1CRK7fJCcCFOdgOkFSeoecLh2SJNANUgIi1OkhJKVp0NaGxoKdxiI95XK+bUwSEilpLOp2St1QJJqSAiMHkM2RxPs+eVpQooeXqOkAqnhv19SLBXrtwlC/6pBKI6eoSNdzegpJOOnEzu6PArJGHRILUtlwxVGSLObyObtwSp+hjS8p29U5Sx2/n1LbT1rrs1qA2ty/LMGNvURkkBpTfyz5Yw839zeaBqIWmeJuhHuFlhax4bf2PwAYIoFP064+JsliL06Zuo6rYYfHu3Ps+iU5/QNY+glIEkGKgr3rLPBGl84hmpoqlKjiu85SQ+Q2MuQo4DnQrJNI1CwptSU6OMBenJYo4CYt5WeVC35/FKD1Zpwyj0YLV8MukM4+pqjmUeuzknZucS9MaSWkdBx7RdqKCYd5bMsYPNRYfMLrccZemLLrF+y6JYvUpkVcn5X6nzEZjroFFa6GXebacsHN2YvT0u8czdykrSVXwjkAHgvnrL/Ekp4kIsxjS8AVqSofcYE3HzkP1tDSLScbFBEHfA8pEsumcNw5pwsnqezMIKdVyJlawb4nraX+ljjOf7RYDuUaT8lGCyUVu4AxqJIBVhmlFe97JLTWBkBUJOR4SYnx1fmIQkrjUaeNWC4HBlWnx2gbhKmp4EKOAB6MoXmPxDgwoxzJ25vtRpYrWXBjUsllFWDuc86rlHGNOs43ZQ8PTGnEyKOV9Q6JrTFT55CGofyyszGZVilHYoQQkK63dOyZIYH1q2PEoAouG6fMuHslp3uFluY6sbxJYWJZVJPh8Hwzp4sNe3FCVOFGsEXuZm/fOftsK4H9MOF6P8WJ8mC7x6V2z5hS8sxsnanMWgksYjo3J+cqxmLX8/9cf1GHxQm9ehoJJWuuZZC1rLsPtPtcave54OfsBVOddep5qL1RmEidI+nxbpf9VO5cY0xk6nquhR0csTC//dAS1eEkcq0f1JtRHVPfc6Of0EokIJb7yu9zLeywF1oWseGq7LKIDV6UvTihi55r/Q5OItfDjAt+XhhmTHVcdUu7HiwtySI2PNTeIKgreZqu9lbvlW6H837JuWZRxjH3vVs5A7oJnICWjvMKvYAFVn6t2NryPsCrRORzjnKKuC+ikgO2gHkPMaIhICKWO8lXC3GVFJAQxjv3nDgvL9jLZWEGJRFhtvBnZtL3RXoQZxIRMDCHXC6lPse5caK9jHe+l1PAQ2JCyUaVcFYPsuwHqScxIAliEozDcIlVmzGU+kp+pj5QkgtmpuGTt0JmUHk88hjVY1XsddHc6pwbZlLJi9WZ1NX1Q7tRC/PPiQ8lj4/PkqU3071zaGP1yWruq8NADZ0t3DoEFd6xuMj1flbsHcvQMPE9lpbc0kT00XOjn9BILGnKc6ryXtMxgOi5stjh4uQclyd77PputIDv+o690DJ1PbtuSa+edy4usJdSse/6JRHhgXafXd/ZEQOE692MiNBHz5XljImzIwoRYeY7bibGmBkXwPV+xmPdOa4ud+jV0UjEifJAu88iNizT4fvMVJskAS2jZ+JMkrrWzUpfnzq9UZ6L6rjQzjnnF0Wq2wstIa0XN/spy9iUlOyL2LDnLf/U+cYkyhthyn6csBdarncznCjz0NKr4/Jkjx3X8Z7lOeah5bH2HDt+WRhlVMfNMGGvn3BDZlye7HE9bRouNAsaF+y9rdivjoST0dKRXqHpAPpD+b+IvBb4htPy1jtVEJHvxAxmEXgX8HxVfZsYW30p8FnAXrr+n46t0AlMJvadJAtELBlgSgyYnSRQNbVU05RstaMkfDAk66sZWK4vMzuwRdYn5wtS/cljsNSVJaWchK8k4xOT2KoYd6XumhF4Z96E+XojaIxAWtRJ7ZFG01uSvlJP1yLLblBVehnlZJI6qqpzNiM1mkrPMfzO45yZHhQVZ76uKdmjem8xwiqnjVKuaYY6Y3JvzGW9FMeSnPxRPfgL546dAtbwrUlOydvo9cBbVfWzT17D2cFp01JQx/UkFTQuENWxp475csbEB/p0Fq+Pjv2upfWBiQZ2m46IcK2bMnH2v0nz5O03L3JlucOlyT6NRG6khXPiAg/vPs7l5maRcFqJ9EnCutFPmfmO/dCyH1r66Jn6PrXv6dPcvdFNjKGkc4GNCyXKydT1vH3+ANe6GTPfFSa0F1quLHZ4lz/HJMXTnCQ91jJ6ri+nTH1vdSYGN+8bO0+ojpnvmPmOeZjRSOTx5S5d42ldYOJ6Hl2cr87yCV30vG3/ElGNgV5NEuN+mJQMuxn2woRrSxvHxkWudzMWrmEeWvb6lmXwNG6Hme/Lc1eWO/TR2SdtDm52E97lIhMfyiai2dTL4QS0tGGywRPDWYVW+Keq+hGq+lHAv2U4XfmZWF75Z2PhVL7/jPDbwj0IQjqjFsc8fwN4Ebc5VcZthC0tbeHU4aS0pKqvVtUPVtUPVNUXp2vfuo4xqepzj5Oa4IwkJ1W9Vv09x2A8+1zgR1PWxN8UkUsi8jRVffuRFTYevXzhoO0G0Kap3MqTpBGSsT2p4XKmWDPc9+ZskBP3NSaJlTTsVcbWYg9yFCkG1cESWLuYq47dvEOAxiNtM+ANRVIq6cqzSixnl83SUnrGMuWubHFSGW0c7LRI1xYHiuxooWlXq6uSWnbWqB0mYHz4ZMWRIdebs9iW81E1XqtSVHJlz1JfTmlfMhZ7V+xR2u6yEdyC5CQizwD+JvBiTpCl826BU6elpIra8R2TFBT5YjPnRiXZLGJDr65IUhb5o2Pqe3Z9U6Sg880CJ8r5ZsGNfso8tDQSmfnBpflKZ3adqetxJXisMms6ogpNUiG6FG1kEZqk8mqYh4aJC5xvl+z6JRfaObu+o5FAn2wsi9hwrlmwjJ4+ei62cyLCXj/h0nSfq8sZ875ltzWnjnlomPemcrsZpjQ+lNt0wg4AACAASURBVOj6dfDia8sZN2RapLeJCyyjx0lkP7TMfFfsdU6U1gV2/JL9MClq0YhY+djy0OQGvXqWsSkq0r2+Zdb0XFtOOd8ui7rxfLtk4np6daWe3TRefXS8e/8ci86W9sZH5r0ySVLg+XbDjDB3gefrmdmcROTFwBdhwTA/NV1e55L4dOAAQdXBKqezSywfOjekMAdjBpporTpTY/coB1etMkpKcQkT3NIYVz7zlBmLpnM6pEO+g6cellbcW3rxvJDbId90Riod4h0O4LZjL7u0yMfWj31fqrZQtXNCrRvwV0W0Kf20c1XDgq9ekL4p6eNHh3wjo7NG+cxTxvvAmSllnKJdKAwnH2DOTEV2G9wirNTB0LdaNVo5RqjkPlfOEtPNjbkncH/N8M+Bf4wZbe9JOE1amrzXRd58/TJtUnE1bmxTykwi/++jowmRvnHs6pK9fkLjTDU372b00aLd3+gmqIqF5ErP5/rfKg9wsV1YNPvo6aPjWjct7WVVVFZXAcz7lkXfEKKw0/bMmg4RtYXbm2qtkWgea6Hl2nLKY/u7THxgmuxTN5cTQsJvGXxhQt5FfArB1QePd5EQHV0KqTXxgavLGX3wJQyaE+XxxQ5T34/OEuV+7jam4uvjcIZr1vQsQ8Nen+1MkWW0/yE6bi4nPL5vG7PH9naTuTyy03amPvShvAOgqDYBFn1D1/uSvgbAOWV6gnAr9y1zEpFfwrwyVuFbVPVnVfVbgG8RkW8G/iHwbRzjkji6aIvMDwKcf/Bh3X/vdrz4VU/l9OPqLNxOiRulDAxMKCGEJKRoC5mpwLDIpgOjFu1Axm3JEM0h1++CJkaS6qh7I4yYiTGnalFWHUWhkJCiNDR2zS/H0k0uqxUDKFEqVqJi5Os2JrlP9nxhpDqMUcanhiHywzAm9TWJzTglfepzCdPkKlwr/AGir25saMNds9s7MmWGiHw28C5V/W0Ree6GrdxxuJO0tPNB76vvunoBEaWp8msNZY0p5anQeFvI95uWa27KvG+pc5vNkoPEomsI6kYxIn1idABX5jsAtD4UxghwYzkt7c77hhDMHpXjYqoK1+dTlq2ncZFlSCldnB16bVykC54uelSFG/Mp13TG7mSJF4VkpwopZczefIJzkZ1pzmlAYVoArQvmL9U3hBTPMuOX40dGhHZFF9YFT+vT4V0VGhe50U0Kc8m2sFx2ERqWwZc8bUCJuRmiK7FAgcKQ+pSDLudzc07Le+q6xNTnLRvB/Sw5qeqnb1j0J4Cfwwjq1gJVelhcdGXRZWXRq2PuDRcZohrkl+Csrpz1lmhhdGBgcKNQPDWTykwmSWH2jI7CzufMvEcd0s4MtIRbimMmUTMTF7JElp71jJiA1pEbIoVJro5D9ANTKBlyc19rJp8l09UNQPU/j5GNv5SzEsXvQod6a+/WHN4phzgajdOG9iOLC3jE4B6ETwI+R0Q+C0tfcVFEfkxVv/AkldxuuKO0FBzLmxNQWABEgZQQkmo+aJrX4vOmTXFNLIK5CGUiSAqKDLbArrLNOgixS4GUs8TRpaC/IkoMjtBbQGNJgZc1OmIQ9pja3HKKpADPzittE8oC3Qdf5u+1YB5xISURtSN86X9o6LoGcUrjQzrNME7kmROJahRisCDJJaBy1dccgFp1two0PWb2qkKIQowuPW91ZIaSy1kwaZg7C8KccVaV0k6scjCpWor1GBykdOub5mi6BVo6dTgThwgReXb193OAP0y/XwV8kRh8AnD1WB35FrZQgQvD5zhQ1W9W1Weo6rMw99dfvtsY03GwpaUt3C44CS3dDjgrm9NLUg6diB1V/sp0/dWY6+ufYO6vX7JRbQ560woU6SLv8GqpSZIHdo7yncuVjb9jLGlEkF4qSSC9qJE0MUhQuaZBOqnUbDLUmSUMMq5u3HYJkAoIUiKE53ooKKRAtitqyrK7dVgk9CJJykhyKr/zrjiPzdDMCLIUWAd0rQPejiSofJ43rmkn97/qU2wM1+jH5WA9LmvhLlBFnAGcLi1FgX1vMUjVpCZtk8TdCxIF9Uk6EKCjvKsYBRpFm2GSSW+6Xm2qNDaCSWOYdJF3/s0ksFw0aBA0OMPFV5NLsnpY0B5ibEaa5nxGvIalS21lDUYT8W1EXKTrHbFP7VQ4oZT0MZ1rcF6JGacg0ER7Js95pRCFZZxP9TU61JmlKBn6rDETgOInA+IxRQO31D8gSdLSmHAQJfY2ZjGbBgZ/LCsXBe3cEFk8bi415TE4a1o6K2+9//aQ6wp89Ynrq1R0WT1cInoXe01yiayYlgRG9qa8MGaVXr2IFlUfjFfLzBBWFtuakcAKM6wW7HLdrTxTzeucasI6lIpIuq5D31xOc1Gr6GqoxgkF/JhJ5o+oEZlLalKVCq3cP4bvejOQh8fUiCuqwVrl6Kvf9ZitMjpJ72UDEEzVeSugqq8FXntLD58hnDYtoUBe0ETBVfPegxbCsv9lhxJt46O94DpfFkJ1SccX1BiXT4t0ZlqSGGAQuhsT6JOdtBdjBLUa0GHP1xus/CMxhMwANKsVe4HO2UH11toJvSsMq6i6RIeNqVLw1CCEDkgqMgBZpGWzEGhmAOCCDG0v7XD5aEPplOiqPimIE8JiKKTBNgHRKdJGO+API1seGBPKzDEGKfY8DS4xF3sv5uRUZVzYAJ4ILZ0W3BcRIkQhhfAavfTVMnnBHDGNijl5qjJ5s1bZaupUGHkRP1Cfg9AKNAMKq0yplnS0MQZDz0jKA0ZEOLLPZMkq0a7rSXmd0uRMk9llZpR3V5khrk7Sys5Vylb4llxT6+ZqlrbyOtLnZ3UkKZZxkDX/ARWhSTYt8zIkpffAduebQGp/C08AFFxn0hEiaJbfg6SNRjV5OhKDscVP+vR8Pyy0Egc7ojpF28yw0neeA6F6DgZml+239aalkvJHDELywfLqf71BC4Lrh0U6ax3AaMYHY4qamKD6hJ8zBiy9PS8dgxahWWmjbGTzzk0qXK3PIvYcmYlFoDcpp86hpI1FTdG0kcSpPZ8SiZZOZFx8ut+5YR3RQVvietmcPu4CWro/mFMPkxuanASSRJScA1ygeIsV9++UX6k4DKQJ71LepuK6TVpkKxXVCLJHG3lhF2JrzCLv9mumkh0iXG/PxsbUE3kRd2HAs5Y4cg4nHXkKVszugKNDtTOjYrYwEEphCgMzGzl7VOrNopYM2ftwqE89g3dhqk+ipj7mOiq3/lzOpRxVqV4JcXCeEEq+p811epR3uIVbh7zRK2d0NO28g0k45Z3UmaaDlHnb3JSiSh4WRaBP71S1bD7yc6NNU6bFbjwnR5qGJD0NjISk/rNrA72AtokDde6g5iO1I51UST3NOcMkPxnRiwRwS1vgY5uu9WKht7IWopgQpGgOYsJPhSJRlnVGTUpyS6sbrG51ahuCfUdsTS0KMpBD2UTmIxwKwRkfTcwU0RGjPJGa7oS0dFyyQRH5OuDLsdxl7wa+VFXffFSdZ+IQsYUt3A4QVUtWmD5b2MIWbg1OQktVssHPBD7s/2fvzeNlS6o63++K2Dszz7lzDQwFBUUJojTNJBbQoNIOLdI+6O6HAt0qKHa/Rtt5An2NaEs/ePqw8eOANIPwkUEoQP20IoOCiK2FzFMxF1BFVVFz3XvPOZm59471/oiIvWPvk3lO5r3n1hnu/n0++cmde4hYEblXrFhDrACeLiIP6tz2YeCRqvoQ4Er8vk5b4kBoTqZUVm4J2z3EtURWwGnQhoK5K24CGMwCzkpYPCv+vsKFmY6E7TBpmQBI1uukmkA967KCKRpNxF9sawzprrLRwStOMUV7t924Riktp07GavyOtqbwx+Bpq8uWtuYV+2XTbr+xXTT11rRbaXxJsZ2Fbn4OfB92NBxThPVdnbVR0UyTLiyOmwumi5T9ouBorlhQfVKQXnM6K4iD/KQ0mgDUyzOcFew4nLOCy4O2UnltIgYLpX7e1ExuJ43m4PKgfUD9nrW0pHBsStrvsyRLHyTltfS9CiYpaczDEEz/kf9NSlMsl5q3UhN1bH+kyWU0pj7nNZ5osfF9Ey0IoV7xndg2zSdjkYKdNlYKO0k0rwrcQKiG3ipT9wVtq4Yv1JsCXS4trcqb/aW23CyE5Xhp280GVfXdyf3/CGwbFXsghJNUyvDWSW2Kg2BWKMO+QMGUUCcUhfByN4N1zIigVjDWtIQYNINqXOsTszP4ukLZSVYDU7pWYtR0d9k0bU8sq7U7r/rrTbmxoZEYrQf29Fxrka8kNDqCM9m0+iitL+2Ppg+DWSNkz8BpWyiFZ+p76p1so6DXZtt4Q5PaSEFiWFVMwRR38U3O1/2bLxgRAe2MFD3OCFL5ATKa3NQGU9OQZqJSga2kFj61zzeYj0TBTJPBMwgM67eIqqMzNwXtzEJihsaBLRNB0PFjxsAicY1gi+bjlk810BgHeM0CexYNm3b9rKLgBqGaIvJ5aHsUnEGAmFBHHWAkjaAWF9qfBGBEAWkntEziasCk/4UJ/V3SBGFZasGnhZ9A+P9Ma3+yGD+e2AWzF8EmXjqrzQY7eBbwtu3q3830RT+BX81eAn+hqr8Yzj8XT3wF/KSqvn3bspzDntyIBdOKL41jHfis4eGedDv2NCWPf0lcI8RiHSHXXD2A1hFMicAwphnIg1AEmjRI0OTbS2lLt8qIZcW2xDx5qXAL0U91zr9YRjqwp+jG20YabHvQjzkGW4I4no+pmkpX58xTa5s+rTOVN33Vqrvzn9QThiQnYTffXjwfs51vB9HdjzDaDewkL+Eg26AJhhGQKbgiCJvM32OLcE+ysFwcVDm11lVrPc4P4DWfhQHV2fbAmi6xiIFDLguaijaaWdQAUsGURn/WiK9euJZtkGhftKJkpaDWVKKQcaallNRCRKLfLdBpUg1x3V9LhW7qw43u4GyjOe/y9mQg9oGpmuvg+z8GQKFR0wt/m23oi5OJmK0mtt9Mm8nBdpjBS2e82WDrRpEfAB4JfNt2NOzWlhn/Eq/2PURVJyJyt3D+QfjFkP8MuAR4l4h8vapu7cpziqxt0+tG/JL3OPCK+ISxYeCTKNRSwRC/U8GUoDuI13sWdZKmSiowZ2ounf9xxv1iTGPCC22u655Fd3qc7PTbQroZogjNvlHxuVCMoS10Yk6/ODK1hKm060qv1Vt7uCDspKFxlkCLzShnvfszsJwpAhG5FHgNPjWQw88GX7JwAXsAO81LEoST/wEx0MEGAVNHqSZaAHgzrhdYWgcaxQEzDXoAHzzgMtomPhvMT1V6b7ueOsNK2EIm1Yjikg8IgidrNAyN73GZBD1ZqAZSt8cEk3U0GVrXLLGqTXtBaHXX30XN0U41aEVS31cv1Yh9EaN1YxsyL9DTtsfxJPaDK6Se29b5L7XRvmLf1OnNBo3gj/Sl0cYLYTleWigbiYh8J/ArwLep6rZicrcCIp4NvDASqKo3hfNPBt6gqhNVvQa/gPCKXaKxx36DKqZw9WcBlMDPqeo3Ao8GfnyGI3evo+elHjuP5Xip3mxQRAb4SVFrqwwReTjwh8CTknd0S+yWWe/rgW8J2ZTH+F0R/wlvu/zH5L6YSXkT0kzKI3uk2WF1lgbRPNRoH2E5uRTpjD7O3NOpW6qfO//bJtpWhHOehqrzR6baQGKma13bTnuI21rEjQtntS1qNOnvVHOapZ2l22WYWIdt2tV9Jmzl0brm1PdL3EE3amPSmfckgRv++Wpz+a3rykxNbBtIt/+3QEjnc0M4PiUiV+Pft09t+eDewo7y0uDQCbINbRZ+BzNY7X8J2lSdKUW8NmKC1gTNt1/U7v/3NBMIiJ8Vx50DJrG8ZslBXErgELJCa5+mCdpPHSoezVpZNMsrxkgd6g3BpOhgsOYw02a5iBuEZ6IBIANoNL+Y57JOQhzMZpZGizRFo43FNpgC2CBpS0NnuujdqCITGJba+G9Dv9WL/13UyLRZIgP1spJ0XaIPSlKYtDWn6GdadDF7/V4syEsLbjb4m8Bh4E1hfPmKqj5pq3J3JSt5qPcEfrb6zcAbReRylrBdppmUj+V3U6bTeCES0D6O/pUQFeZ3ne2YpYBW/pNOwEEzkJtGyMR6nPN7NMVzaXlxcLWdAXueIA2CRtUh6S60LWFi2r+jz2me+TB90VIBHQVm5J5ZwnWWoEwRBbOI10W69NmwKDCaPFMTauy77sQi0lXBppw0cyCq9R5dAYtsmRGqlMuAhwNXLVTZXYi7kpeOnLhUhyedN01lftCPC6qRMNAH83aMUI1RpmkkGRrWugWzG7U5UPxiXBODCVxj+gvluoGhykFyyOrtX2gWersgoEJwgL+uSYubgTwO+uIUOw7Ru4r3u25Q78tWC94Qredy402PLmwTUiZrCYVWJJ8tNBHM4R6nQcDG7XoSegLsRL3gKRWsBIHiA6taW/oQ/FqJmU2zJlAqRixGYVUNmzVW3o/WWY+5AGbw0pZQ1b/Ep8xKzz0vOV40eXGNXclKLiLPBt4SUqy8X0Qcfo/5M8qkjCo6LTad61Ta/m1KP/CHmbnq1n+EtDSBwBFRYCTCZCa6Glm6vfk2qMuc4SmQ7pbxkZG3qycpS8R44/osAZF+xzpaBHQ0vlSAx63uu/dubmBLM2oJ5EDodv9N8zDQZqgtt8xoSJPDwJuBn+5s3rcncJfyklPsRlVvF6FGmmUVqdIvICphc0rqAV7Ddi4SfTsx/ZARH8SjtN6jKJhM6cLmncB6WOIR9hmrNYTAq7MWw9dLL5L76joCfSZ9N1x4V4V6yQnQLLcIQiIuN6mzxQRh2jJ1JVGsMcLVZSYGySIT114m0mm7qCLjUL/EyYBuuj9G79a/M6nPpzsQ2Knva1O1NbJl0hfN4KW7HLtl1vtT4NuB94jI1wMD4Ba8nfJ1IvJivBP3AcD7ty1NtdFawm/tzLYlzuTjAFpRR7q1tB9otKKkjE1TTmO6ZxrMm+mnAsYYP3uL96YDexezzF+A2hn1VFWr7ZLSuUlAGxTn85LFerp9kJpC52GeEJxnZkzPxTqSYAlN6w/nFoNu3hV4G4hIjhdMr1XVtyz18N7AjvKSVEp+qiAuYag30OzcUyMO7FFAJJt6tgZYafYWQ3XmYI16U5c/Vq9tx/JiMTGCNC7bSNYD1oIvXV5RahPII01ZdRBTiHptGhcHci+U4+aZ9XKTOriiIo2aReJO1WHdpNM63Dzti1bfJfwglfNlVVVrA9J6jArrAjXsFA2EAK/uHyi43NRl1u2PXbzAhDjcuTQv7TR2Szi9EniliHwCmALPCDO/T4rIG/E2/xL48W0j9Xr0iFCWMkWIN36/ArhaVV98zug6t+h5qcfOY0leOhfYrazkU+asEFbVFwAvWLrMKOVnaRkiqHO1FlHfW1UtzaLWOKoZzvpUC9hq9rGIGSzWsR3maEytMub4q+qfs3xNCT11n3R9ben16P/pajDz6OuuNVvintZ/sCxUoVgqW+VjgR8EPi4iHwnnfjnYz/cFdpqXpHLYOza8Kc+YWjsBmgXV0F4bGI+7i8zTdXeJRtJa/pD6MLv+yORZtdIuN2pWMdNIvHeW7zKWnX63wtUbOmM7JD4fkSwmr5830ZwZtTnj+y3S3OFN6dKQ0huPoyYWfVhGtqYr0NZqQ/qfpdfnWWZmYXle2nEciAwR3tjdNjXUMI0pT1MTWnxyu0EwRqOl2OqRbpRal86tME/obRmttk2ZKe1dusWgZelpnhmhqOhW0Y91HZHhE99a2g9btWsrQb9ElF5NR9fXteXt+j42G0bOb1QOOXm6Nue21uh1UHeca3ayxcwQCECM4BRNeLBrDk4DZjrvRVoXHd/LpkjVFFGYObe1KT6tZ7tBPJZZm9x8BG/3WZnHA3FMSenOsiTSNbS/crXfR2K9ceKcRrWmQk8EWUYIbdXGJXjpXOBgCCdl00xb60WqyR9V78g1R12d9zJ1Bm/dYtAUM2uQrx+c+xzQCI9ZAm7BoICtaZPO9arz3ZyPe8jMFcTdEPtN9TcPylae2J00NClNSHuPM4Nz6Nr69prvAuXUmOW/nPWezrF6tGBmDP5zlhuoukbgzaLrTJBqOZG+aF2I17tIl2vAJqFcB2albe0KX9VmIl2nJZuzRKRLT9ePvFA72XVeOhDCSZk/KKfnW4Jj9s3xxs0veyKg6s2/ZjGE02ZgT8vbSrhtEjydl6Kr2XSvzcKM+5fxOGx/bxrytwV9S9a7tea5DfbAbG+/Q53DnV4Dtn7PF0XNC2f6v6Y8FFGHTi+i4SS0L8tDs+jYdDrh963K3MJCsGXvJpab7ceNHcQe4KUDIZx69PDQXZ/t9ehxMLD7vLRbufUeCrwUv2L4S8B/iOtLzihZpc7wC8Gm2cviM8Az/FPiDGfT47PL2xl69sJgvJM0tMuaOSudB8X70M4jnAte0tKvGZz5di6hqddlbKOZLPYfn/07djYaIMync7ZlYDl659J2JtrR2Vgf6nqX46UFNhsc4vNYfhNwK/BUVf3SVmXulub0cnyalb8VkR8BfgH4r2ec+JV5f27i91hmkNu23HOMM1XXl3kpz6VJAHaGFjHL9b86dLrEngAHAzvOS1viTN6bbZ7ZLwHu+4XOHeHtJXgp2Wzwu/CLv/9JRP5cVdM0YM8CblfV+4vI04AXAU/dqtzdSvz6QOC94fidwP8Zjs9Zskp1ekafXYGYM/ucTR07TeNOlLUkVEGLsv6cJ7jLeanHwceSvFRvNhiWNsTNBlM8GXh1OL4S+A7ZJqxwtzSnTwBPAv4M+D6aNCtnlKwSOP0u98Zb8SvjdwoX7fHyzkWZi5e3mNzeafoeuNXFU3rb299ZvP6i5NRO9/dexE7z0uRdeuUndpjGvc5Le728c1Hmsrw0OsvNBut7QqLYO4EL2aJNu5X49UeA3xGR5+HTrET9cZYknTkMpskqQ30fWCSP2qLY6+WdizL3Q3lbXVfVJ+xUXXsJdyUv9e/p3ivvXJS5w7y0yLu28PsYsSuJXwP+FUDIB/avw7kzS1bZo8cBRs9LPfY4FnnX4j3XiUgGHANu26rQXfE5Jbt1GuD/xkcbgZ/5PU1EhiJyPxZN/Nqjx3mKnpd67AFsu9lg+P2McPwU4G9Ut17pvVsBEU8Xkc8Cn8ZL2FcBqOongZis8q9YLlnlzH16zgJ7vbxzUeb5Vt5BwE7zUv+e7r3yzkWZO1aeqpZA3GzwauCNcbNBEYkbCr4CuFBEPg/8LPCc7cqVbYRXjx49evTocZdjtzSnHj169OjRYy564dSjR48ePfYc9r1wEpEniMhnROTzIrKtHXNOGcdF5EoR+bSIXC0ijxGRC0TknSLyufB9YpsyXikiN4VN3+K53wxlfkxE3ioix5Nrzw00f0ZEvnvB8h4mIv8oIh8RkQ+IyBXhvIjI74TyPiYij5hR3qUi8u7Qvk+KyE91rv+8iKiIXLRImSIyEpH3i8hHQ3m/Fs7fT0SuCv32J8FBSnDM/0ko7yoRuWwGjfPKFBF5gYh8NtD/k4u2u8fi6HnpYPDSgeEjVd23H3wepy8Al+O3p/4o8KAzKOfVwI+G4wFwHPh/geeEc88BXrRNGd8KPAL4RHLuXwFZOH5RLAN4UKB1CNwvtMEuUN47gO8Jx08E3pMcvw2/luDRwFUz6Lsn8IhwfAT4bOwrfIjn24EvAxctUmY4fzgc58BV4b43Ak8L518KPDsc/xjw0nD8NOBPZtA4r8wfxuflMuHa3RZtd//peel846WDwke7TsBZEQ+PAd6e/H4u8NwlyzgKXEMIDknOfwa4Z/IyfmaBsi5LGaBz7d8Cr51FZ3iZH7NdeeG+p4bjpwOvC8d/CDx9Fu1b0PpnwHeF4yuBh+ITh160bJnAKvAh/KrwW2gGkfr/SduIX193S7fPtyjz/cD9Z9yzdLv7T89LyX0Hnpf2Mx/td7PerLQZM1O0bIHLgZuBV4nIh0Xk5SJyCLi7qt4AEL7vdpa0/gh+dgJnTvdPA78pItcCv4VnzKXLC2aAhwNXiQ/1/KqqfrRz27ZliogVv735Tfi8bl8A7lAfWtp9ppW+BIjpS7q0tcpU1auArwOeGswvbxORB5xJu3tsiZ6XzqC8vcpLB4GP9rtwWjolxgxkeJX/D1T14cAaC8TgLwMR+RWgBF4bT824bRG6nw38jKpeCvwMfu3AUuWJyGHgzXjmLPEpcJ4369btylTVSlUfhl8RfgXwjVs8sxCN3TJF5MF4k81YffqW/wm8cpkyeyyEnpeWLG8v89JB4KP9Lpx2IkXLdcB1YWYBXi1/BPA1EbknQPi+6UwIFJFnAN+L32cn/uFnSvczgLeE4zfRZJleqDwRyfHM9FpVfQt+JnU/4KMi8qXw3IdE5B7L0KiqdwDvwdurj4tPT9J9pi5PFkhfkpT5hPDsm8OltwIPWabdPRZCz0tLlLdfeGk/89F+F06LpM3YEqp6I3CtiMQsvd+BX1Wfptt4Bt6uvBTEb8D1S8CTVHU9uXSmqWWuB74tHH878LmkvB8KUTePBu6MZpSEFsHPDq9W1RcDqOrHVfVuqnqZql6Gf0kfEfpkyzJF5GIJEVMisgJ8J351+Lvx6Umg3W/bpi+ZU+angT8N7SW0/7OLtrvHwuh5qSlvX/PSgeGj3XZ6ne0HH2nyWbyN9lfOsIyHAR8APob/A0/gbbh/jX9p/xq4YJsyXg/cABT4F/NZ+D10rgU+Ej4vTe7/lUDzZwhRQwuU9zjgg/jopKuAbwr3Cn6zry8AHwceOaO8x+FV9Y8l9Dyxc8+XaJy4W5aJn3V9OJT3CeB54fzl+MHh8/gZ6TCcH4Xfnw/XL59B47wyjwN/Eej4B+Chi7a7//S8dL7x0kHhoz59UY8ePXr02HPY72a9Hj169OhxANELpx49evTosefQC6cePXr06LHn0AunHj169Oix59ALpx49evTosefQC6c9DhE5vds09Oix39Hz0f5DL5x69OjRo8eeYOT5ggAAIABJREFUQy+c9gnC6u3fFJFPiMjHReSp4fzjReQ90uyh89qwgr1Hjx4d9Hy0f5Btf0uPPYJ/h199/1DgIuCfROS94drDgX+GT8ny98BjgfftBpE9euxx9Hy0T9BrTvsHjwNerz7b8NeAvwW+OVx7v6pep6oOn0rlsl2isUePvY6ej/YJeuG0f7CViWGSHFf0GnGPHvPQ89E+QS+c9g/ei98ozIrIxfitpxfJvtyjR48GPR/tE/Qzg/2Dt+K3av4oPiPyL6rqjSLyDbtLVo8e+wo9H+0T9FnJe/To0aPHnkNv1uvRo0ePHnsOvXDq0aNHjx57Dr1w6tGjR48eew69cOrRo0ePHnsOvXDq0aNHjx57Dr1w6tGjR48eew69cOrRo0ePHnsOvXDq0aNHjx57Dr1w6tGjR48eew69cOrRo0ePHnsOvXDq0aNHjx57Dr1w6tGjR48eew69cNphiMh/EJF3nGUZzxSRA7EDp4hcJiIqIn0G/B4LYSd4aD9DRL5FRD6z23TsNvqs5HsQIvJM4EdV9XG7TcvZQkQuA64BclUtd5eaHj32HkREgQeo6ud3m5a9hF5zmoH9PMvfz7T3ODjo38MeZ4vzSjiJyJdE5Lki8ikRuV1EXiUiIxF5vIhcJyK/JCI3Aq8K93+viHxERO4Qkf8tIg9JyrpURN4iIjeLyK0i8rvhfMskF0xaPykiXxSRW0TkN0VkqX4XkZeIyLUiclJEPigi35Jce76IXCkifywiJ4FnisiKiLw6tPFqEflFEbkueeYSEXlzoP0aEfnJ5NoVIvKBUNfXROTFybXHhX64I9DzzHD+X4vIh8Mz14rI87doyzEReYWI3CAiXxWR3xARu0x/9Ng97CceEpH/GN7/U4HeR4Tz3ygi7wk0fVJEnpQ880ci8nsi8hfhuatE5OvCtZeKyG916vgzEfnZcLwVX1kR+WUR+UIo94Oh/e8Nt3xURE6LyFNjX4bnniMiV3bqfImI/E44Prj8pKrnzQf4EvAJ4FLgAuDvgd8AHg+UwIuAIbACPAK4CXgUYIFnhOeH4fdHgd8GDgEj4HGhjmcC70vqVODdob77AJ/Fm+y2orNbxg8AF+J3Lv454EZgFK49HyiAf4OfbKwALwT+FjgB3Bv4GHBduN8AHwSeBwyAy4EvAt8drv8D8IPh+DDw6HB8H+AU8HQgD/Q8LFx7PPDPQ9kPAb4G/Jtw7bLQB1n4/afAH4Z+uxt+i+z/a7ffjf5z4Hjo+4CvAt8MCHB/4L7h3f088Mvh/f/28F4/MDz3R8BtwBWB314LvCFc+1bgWhp3yAlgA7hkAb76BeDjwAMDPQ8FLkzad/+E9sfT8Ot9gXXgaPhtgRsSvjyw/LTrBOwCY/3n5PcTgS+El2FKGPDDtT8A/lvn+c8A34bf5vlmwoDbuWcWYz0h+f1jwF9vQ2erjBnXbwceGo6fD7y3c71mivD7R5OX/VHAVzr3Pxd4VTh+L/BrwEUz7nnrgv38P4DfDseXhT7IgLsDE2AluffpwLt3+93oP4t99hEPvR34qRnnvwU/uTPJudcDzw/HfwS8vNO+T4djAb4CfGv4/R+BvwnH2/HVZ4Anz6F1rnAKv98H/FA4/i7gC+H4QPPT+WgXvjY5/jJ+1gNws6qOk2v3BZ4hIj+RnBuE+yvgy7q4g39enQtBRH4OL2Auwb/IR4GL5pRPuO/aOdfvC1wiInck5yzwd+H4WcCvA58WkWuAX1PV/4WfKX9hDn2PwmtrD8b30RB404xb48z1BhGJ58wM+nvsbewHHpr3vl4CXKuqrlPevZLfNybH63gLAqqqIvIGvAB4L/DvgT8O923HV3P5ZwG8LtT5mlDn65I6Dyw/nY/C6dLk+D7A9eG4G7Z4LfACVX1BtwAReQxwHxHJFmSuS4FPzqhzWwT/0i8B3wF8UlWdiNyOn8VFdGm/AW/O+1RSf8S1wDWq+oBZ9anq54CnB5v+vwOuFJELw3NXzCHzdcDvAt+jqmMR+R+0hWda9wSvlfWRe/sX+4GHrgW+bsb564FLRcQkAiqaChfB64F3iMgL8drSv03qm8tXCT2fWLCeFG8C/j8RuXeo7zFJmQeWn86rgIiAHxeRe4vIBXi785/Mue9/Av9ZRB4lHoeC4/8I3q57A/DCcH4kIo/dos5fEJETInIp8FNb1DkLR/C2/JuBTESeh9ectsIbgeeGOu8F/Jfk2vuBk8FxvRIctQ8WkW8GEJEfEJGLA+PGWWCFt71/p4h8v4hkInKhiDwsofG2IJiuwM/uNkFVbwDegWe0oyJiROTrROTbluiPHruP/cBDLwd+XkS+KdR9fxG5L3AVsAb8oojkIvJ44P8A3rBIw1X1w3hefDnwdlWNPLIlX4X7/5uIPCDQ85Aw6QPvo718izpvBt6DDzK5RlWvDucPND+dj8Lpdfg/9Ivh8xuzblLVD+Btyr+L9/F8Hm8LR1Ur/At9f7wN+jrgqVvU+Wd4Z+lHgL8AXrEEvW8H3oaf2X0ZGLO92v7rgaZrgHcBV+JnWCntDwvXb8EzzrHw7BOAT4rIaeAlwNNUdayqX8Hb338O7zD+CN6pC94H8OsicgrvEH7jFrT9EN608yl8v14J3HO7Tuixp7DneUhV3wS8INB6Ch84cIGqToEnAd+Df/d/H+/P+fTWTW7h9cB30pjXFuGrF+P54h3AyUD/Srj2fODV4qMHv39Ona/r1hlwYPnpvFqEKyJfwkf5vOsurHPXF9iJyLPxQuZAzKh67B7OVx7qcdfjfNScDjxE5J4i8tig5j8Qr+28dbfp6tGjR49FsavCSUR+SkQ+IX4h3E+HcxeIyDtF5HPh+8Ru0niuIH5B3+kZn5fuQPED/NqHU8Df4E0iv78D5fbYozgfeekc81CPXcaumfVE5MF4J+QV+PURfwU8G2+jvk1VXygizwFOqOov7QqRPQ40gnP9NcA9AAe8TFVf0rnn8Xjhfk049RZV/fW7ks7t0PNSj4OI3Qwl/0bgH1V1HUBE/hYfJvlk/CI0gFfjo1R6hupxLlACP6eqHwoRZB8UkXeq6qc69/2dqn7vLtC3KHpe6nHgsJvC6RPAC0I45QY+EuwDwN1DiCSqeoOI3G3WwyLyn4D/BGAl/6ZDwwtn3eYxVzmcd0EWOrWjWIKUHYPM/XGWZSVYWDHXbe8/Ob7xFlW9eN717/6Xh/TW26r69wc/Nnm7qj5hbo3+PYvv2ikRuRq/GLMrnPY6do6XsN+0uu1KhR77Hae4fUd56Vxg14STql4tIi8C3gmcxufZWnghmaq+DHgZwLGVe+q/uN8Pxwubb3ZzRrx5Jk2ZMdKacyyd5tG4SL2z6AV0ZjvmPNe9d8azM8ubVW6KZB2+pP3d7fv4Oz3v2re8/VP//cvzCYCbbyv5+79qEgesXvKlbxCRDyS3vCy8N5sgfmuPh+PXwXTxGBH5KH4B58+r6idn3LNr2EleOioX6KPkO84JnT32Dt6lVy7LS7MW1dcQkVcC3wvcpKoPDucuwK9Huwyf9ur7VfX2RWnc1QwRqvoKwnoFEfnv+LUOXxORe4aZ3j3xiSO3KwjKqjlOvxcYdDeV1S1jluAw0j7fFSLdZ9JsKekA3BIQpikrntctBEisc5aQFWkLg4gq/TFDIHSRtENmCZB52K6fzwEUZdJeKH+Lqj5yu+dE5DDwZuCnVfVk5/KHgPuq6mkReSJ+vcy8LAC7hh3jpR49mMlL2+GP8OvZXpOcew4+B2L0eT6HJczKuyqcRORuqnqTiNwHnyrnMcD98NmLXxi+/2yBgsAmU/euYJinfaQDaFcQpQNx+nxXIEnySZEmrVcFNc09Iu0yk2db2omhrT3M03q6behiVjsjXLDIVa59fSstZ5ZQljnfM9qpIk1bZvVdnCAEkmYK2RlwwFirbe9LISI5XjC9VlXf0r2eCitV/UsR+X0RuUhVb1mqonOMHeOlHj1YnpdU9b3B+pDirHyeu51b783BTl4AP66qt4vPWfVGEXkWfuX49+0qhT32DVSV8RLRpyIieG3jalV98Zx77gF8LST9vAIvVm/dCXp3GD0v9dgxzOClixY1kSdYyOc5D7tt1vuWGeduxSc5XRwCmplGyzAzTFAim019ql4L6JrynHozXDyGRkuyNmg+BqxpawE1PQuaEkVmP+9m3NrVZOKzgDhXn2vVN0sjSfthk0bk2u1PnwFvdjSJljqrXVErTOjDzLg3obHug/SeSut2L7rcwSFMdE5fz8ZjgR8EPi4iHwnnfhmfCBRVfSnwFODZIlLigw2epnswrcqO8VKPHszkpYVM5DuJ3dacdgYi6CBrmYF01gCcmpBUN5mP4rOUFbjgUxLXDMpRIBkD1v+eObCmAzPU1zX1JQVovCYgTpFKEadQqRc68T4xrTJFFZxDqjmBBakQSq9VOlPQefNbEDqz9tFMzHdqZ5gyu30Z+8fOuG9eX6WmvszTqLC5jXPggDVd/JVW1fexTZiiqv4u3pbeo8d5g2V5aQ7Oyud5IISTiuBWBqB+YNc4sKsf7HFuvmASgay5pqpgxT8XtQgj8wVSfM6Ge0wYvGM9USjF38TnwGWmFk6edjCFwxQOjKKYTaHVEuhWJ/Wo6usNP1zQpKLgqtxmjTEVNPWzHd9QvAattsa2qzGzfUFBQEnSv5ui/EJ5LjeNwBb/P84qU8oZquQMOITx2TNUj+13QPfQxf6XXYUYT+eibepiN9p4prQug23mezvES3/OWfg8DwYnC7hMiAOsqB/QoqDy54KQAqRik0alWRKwAC1TUj2AzwosCMJHraDWD7Z+YKZ+ASSaDsHfawTNBDcwOBvoLQAUzYQqs7MHatcuS5xFUt5x6gdyNVA639BYTmqiDO1VayFrhG0tkNPABRuEi1BrTbENXkgFrS+0VypFKt/34qgnDClif7UmC0gjH8PPWqjZxZhVEcaaL3Rvjx3AXTGI7gTOhs790sYEssjyk21k7rK8JCKvxwc/XCQi1wG/ihdKZ+zzPBjCqUcPwGkvnHr02Aksy0uq+vQ5l87Y57nboeTH8XuePBg/X/4R4DMsuXBLjVCuZl4DqRRTOq8VQKO+SjC9idSze1NqM7OP/hIDmlmviRlpz+JNx/TkQDOvOaRagDh/j4ZZlykdUvklS2oNLg+aR6XYwmsqEnxBqWYFyTInCe6vMrRPhGpkAm1gCvXtwZsGpXRImfitujMl2/aBuSzROitFg1ZU/1fq63FDixt4c2SkTaJ7r6bRBE3Ka0/Rj5ZqfbXGmQkuaFHiFGeMfy7Up4vMAuN7gDB256dw2ileggVn3j32LmZpe0uaJ/cCL+225vQS4K9U9SkiMgBW8dFSSy3cUitMj3lhJA7sxGEqcNYPupJYleIAaideOPkB3AsroOUDcZmpB22sNMcBzgYTVQZSgikVU/kBWYOp0WV+0DWlH3BdFGb4Qd2UihQa1jRJGMDxfqXcBxRUA8FljYAwpfUmNKUOpPCmOv9sNQzCofL1muCz0cRE1xr0g1D0JjwaM2mgzxTqBUXu26NGPN2Jua7umzgJMEFQOa2FTGwvldb9WeWNUGz1bRaCRcJ/ugi8nXyw2M0HDzvCS/7dP/emrK0EoKYLv89QUGp3reMO0bMfMLstnSinbdbX7gVe2jXhJCJHgW+l2RlzCkxFZOmFW2qgGkgILoDikG0JCDVRcIS6K6hG4gVKFSPkmsEemsGyHErQdPBO/MwLJX9TqD8M+F778fS43A/ukQ5TKaYM12xzvynBTtW7h1zQpAiCzQrVUChHzWBvSkVD/VVOKEcwoS1Ri1MBO4lC0dbaSKQnalxqfT0uB1OAWmphGGGnBJr8t6+LurOiltUWoKFdztNejnybTYkXhiFoJPa9M1F4NjRE4WSnC0brqWHdnX/CaSd5SQCprQ5zZgXnWHhJOo7OWwC/TBk7QU/si9j2c9E33Tq2umcedui/2Qu8tJua0+XAzcCrROSh+C2Yf4ozSFaZHz7B5Jj4QVv8oG1KamGAgCkEOwkz+YHUA6F32vtB3xZ+QNcwUFaDIEwyaWbyJmhMllr41IEPiFd6MnB50ByiQCwFW/hjl4Xn8UJFKsGEuqPGVw2FYrUpx07BTvy76QK/lithgHdesGQTT4taX0c1bIJEULAFZBteYLnMC77YLhUweSLQs2hWg+kx/y2Vr8cLmBj512g5apuy7ARsIbU5UbNOu6eQrytGpX7WZV7Qu0FTHur/u0Xg9oApYpewY7w0kkM+AEWVerZ9F6eiEjNngF1A4Khbznw1q97ZZXRo2m493RJ9JsaEOrcRLOl/smRdm/p0vPX9e4GXdjMUJQMeAfyBqj4cWMObHRaCqr5MVR+pqo/MRofOFY099hFihFH8nEfYMV4ayOhc0dhjH2Ev8NJuak7XAdepaswCfSWeoZZeuKUWiqPB3Ba1oWTm3ZiwAOe1FbVQrvjZuin97Dxb91pXNRKqYdQiqDWyVjnq65Ay+lY8LS6nNm9JBTrw99sJuIG/Xo0CnRXYMZip11qmR7z2F+9zWaOtuAFUA0Kwha/f5c11NeAG4s12JvrbAo0uCVao/D1Io+nUPp1YbgZu6OtDAr1AtubphWD2DP0c2xT7yhRQlkHLCrRVI+p6UZCR1+zyNR/MoVaCtte0XTMwk8b3tB2cCpMlZnsLbjYoeH/OE4F14Jmq+qGFK7lrsGO8hAiS5/WyixrpzHvWAu9ZWFaLMaZ5pltfF936Y7aUVlb7LdrQRbrAvVt+Wk5KY5eeRbWlLh3SrFmcW2+3rpTmLk2z2rnofxarXpKXzgV2c8uMG0XkWhF5oKp+Bh9y+KnwWWrhllqYHlPKIw5WKjidka2FBaxGMVNvRqr9IZUXCHFQrTQIj5w6Kq8KQqU8rLg8+GtKH3RQO/unginEm+460WsumqYNqFWqFf+7WlF04KASzMSvMbIWigwfiJCDGyiaKS5TTGEwk0boqFHs1JvLTJkIphyqlcaMCUHI5N6vZSbifUfaXE9NktUwPDOAatXhcm0EsvXtLw8ZpJQQUCLYcRDgg8YfVfvtMqgyKA9DNfRlSSGoVTQIVSmFYh2yNS+UqyH1twvPeLPegu/U8qaIRTYb/B58FvIHAI8C/iB87xnsJC8hggyW6MOwSH3TcfeeTTS3B16Z5SuZkxw5FNA+Py8t11Y7B7QJmH+um9os0D+T5u0wr03zhNuMtizVd902LIhleUlEfgb4UfyI8nHgh1V1G+Ph1tjtaL2fAF4boou+CPww3tS41MItHSjl3afYYcVoVMBxf348zqkmIa3RxCJTQSppfESuEQTpeQR04FCr2NWSLHNY27wQxTTDVUI1scjE1M/6CDkB6wUaVv1vgmAzoCsVZlBhMqUqDIXNcRsGNer9PFbRgcLAeV4IPi9yByOfVqnasN6HtW4wObijoLlSrbpa8KhVZFiRDUtEoJhaJhOLbFhMGQMZGgEgpQThqOhKRXaoYHVlioiiKjgVisKiKqjC5PQA2fCBJ27FeeFpQz9WISIw851iNixmLGju+8WtOMQqFEJ5yGCO+fsxXiBjCRLeC+Hq6OIZIiZu8Vd6wc0Gnwy8JuTT+0cROR61kYUrumuwI7yEEVgJqnIc8Lv5J2ctRo/o5nWMW8t07tukKWyRf7K1hcxW6OaOjJi1S8FWgqCVXss0bXBVXdamBfLd7CuxzZH+7XZKmCkcQ3aLdAE9NNF43TrSc7HMrWjYAsvwkojcC/hJ4EGquiEibwSeht9G44yx24lfPwLMSia41MItYx3HLljj8NAPppUzVM5wdGVMWRnWpwMmkwwN6kw1tTA2qFXMqGIwKlkdTRllTXxlZiumZcax0QaTKqOoLEPrr5+aDtmY5BSDrF5rJAKuErQwPrVQpthBxWBY+IF9knH0yAbHVsZUznBqPOTUyRX0aEmxEgQcoLlDcoexfgA3RxQJoYPOCaoCK54OVwnl1IJV8lFJZh0ClKXBWGVlOOXIaIIJz5+eDFkbD0LfC+oE54Qs91Ebg6wizyoy47DGYeq+FIZ5J/b0AjCijIuccZExnuZY48gzX1ZZGUSgqgx6AopxeNVUkMog1pEdrjDW12OMkmcVqlCUlrKyFOMMLQyDQ4upTk6FjerMIoy22GzwXsC1ye/rwrk9JZx2ipcQA6sr7fV8qj4NVtfM1MmRWN+71fV0EE1/R3QFYfr8rAE8STI8czPL5Nq8zTJlnuCtXJO6DEJaMN0scFtpyaShX8QHl6QCM82+QiPkWjR2rX4xDVlaVzyOdZoO3el9qZlv0STKy/NSBqyISIFfxnD9Mg/PK7BHjwMB3TzbWyjN/zabDc4a0ZazkfTosc+wDC+p6ldF5Lfw2vkG8A5VfcfZ0nAghNNKVvLYS66hdJZCDV86dQEAq1nBKCsZmBKnhpPFkJOTEdPSMi68PdUYx4mVDS5eWWNgS6ZVxmo2xYjj7sNTjEzBTdMjTMMftVHlOJW6vFPTIacnQ681GIdToXQGEeX4aIP7HPYL8p0aSjVcNDjNhfka10+O8bnDFzOpMlSFU+MhRWkZ5CXWKEVpybMKK46Vgde+JkWGNcqhwYTD+ZSRLRlXGZlxdRtvm6zgVDg6mHDhcI1MKowopbNsVDlGlNxU3D5d5XQxYFpZskD34XzKBcM1VmzBYTvh5ulhSmc5nm9wNNvAiFKopVRLFmLknQoOw8lixK3TQ0wry8BWoY8EI8rRfMzQlKxVA9bLASenQyrXzOYO5VMO51PWy5zbJyscGUwY2ZLS+T5zwZn3+W3eA1W6DLVtmv/tNhvEa0qXJr/vzQ7MCvcsrOBWB5uTCVcVW+Zji7knYwZ5Q/v+We6Z2my4uYxWRv6Og7/WNkIiZv8j7ESQRmSLbNacZtER2wd1Yud6G5pYXiin1tDSDP+pRpkmSJ7RP61+7bQvtlmNqc3trTaniawTutpZV0xDX6pZhT5aVHNahpdE5ATe/H0/4A7gTSLyA6r6xwtVNgcHQjgdzdZ58KGvcme1glPDxYPT3FGs4hCOZGPuNbydkRTcWa2yXg3ITUURVtIeyzY4Zte5JL+dU26Fm8sjFGqZuJyLslP889G15OJYdwO+UlzAndUqI1Nw/fQEALkpWa+GnCxH5MYxlIIKg8VxUX6ay4c3sSoT1nXIV4sTOBUuH97E8SPr3HziKNdOL+CW8gg3TY9wqhjVwgNgUmUYUYam5Gg+pgoDfm4cd8v9BH/dDajCAH4iX+O4XWeqGYVaLrCnGZmCdeejHY7bdQyOQi23VYf58uQiRqZgaApyqbA4Lslv54gZ4xDW3JAL7RpHzJjjpmBNLesuZ6qW9bB6/I7qEGtuyA3ZcS4enOJ0NSQ3jhUzZdVOGEnJMbvOyBTk4gXoWHPW3JCJy8nFmwuNKOtuwO3FIXJTsWqmNV13hmiS7aZiDmHqFlgME7DIZoP4zMr/RUTegA+EuHMP+pt2DCpCtZoTF3JD8KVqsuYppKVq+TDqIBttmaxax61s+DEgRzfroUKdfSRNOtzyKSX+4Yb4SG+7Li+4IGYv6ZogxYHW6Uji4m8bEiEn9Uiz40C9jUua/oxEkM5yaYWUYd2MNZgg9EKKrzqRcnK9RsgG002mnCaW9n1AS2jV/9mCWJKXvhO4RlVv9tXJW4B/AfTCqcJwU3GUscs5YsecyNe8tqCWSoUbp8epVGrNamhKVqwf+FbNhAuy0wCMZMrF2SnGLsdm/p8sNCOXKbmUjExBof74gaMbWDUTAE65Ff+MOEZScGt1mEIth8wEg2MgFRfa2zhu1gHIpcSirMqkHrhPZGvYMIXMpeKUG3F74ddvrdopx+x6TU+hllUz4e75nRwyEyo1XF+c4LbqEMfsOvfJbmOqlrHm3FYd5h7ZHayKT/Nwc3WEXCouzW/l4uwUBk+zb8eII2bMhXaNCsPl+R0ArDvLrW7I2GVcX55gzQ0p1NZCOAq/ieasVUOKwnL34UlW7YRCLcftGpfmt+NUWNMBN5bH634YmYJKDeuhzFU7ZexybikOs2q9gFo104XeA1WpNdwFschmg3+JDyP/PD6U/IeXqWDfwQjVqNOHQmvNqanafhKgpf209t9Smgz2Xb9Q+hxs8gvFe10WNIt0L7JOua1nYtqsedF5UdnqPOe1Eu9TqlN9pdfiM2lbHK02zfNrQRROhEwtM3xgxpvTaoEcBWq3yFnCJr2vq01JI6wWFVBL8tJXgEeLyCrerPcdwAe2fmR7HAjhNHY5N06OMnEZJ+2oFkLgzWlGHLm42qyUiePCoTev3VmtUmjGWDPuLFexOFbtlFuKwxy2E47bda5a/zpuKo4CcNnwZo5Yxx3VkFNuxDG7Ti4l11fHuXF6nJPFCIcwMKU3jeVHWDVe6F2a+d29byyP8cXp3bilOMIt08MUznLx8DQrZopT4WS5QqGGMsxWB6ZkPRvUwuBEvsYF2RqX5bcydhljcu6R38nF2UnW3JCPjO/DKTfiznKVO4pVjmYb3HNwB5fkt4d8WVNGUmBwnHIrfM0d445qlZEUXJyd4rbqMGtuyFhzJi5nrBmnyxG3FodqJ+nUWR8QUeVMK8sFw3VKNZwuhoyrjHGV8zV7lAsGawxNwZobsu6G3FmtcsP0GHcUK5TO1s9E0+IgaI0OYWQLSmcZmG0SgQUoMK0W15wW3GxQgR9fuNB9jpjaqv27SSVFGgcg+OTGqk1WH/HRZHHjTB+FGZ4VgmmsGaCBRsDU5frBWWlrCGrDPSHhclNmomkgkPnBtY5EbYVjU5vEuoIyCsG63LAdjKm8YGz2Mgt0xRgFK3MFZNT2NJMg5Gjfl9CVCi+g2esNmnIMXpOqoqkv+Z9iua7uDNSoz1wj+O8F2WMZXlLVq0TkSuBD+OUZHwa228J9W+xahggRGYnI+0XkoyLySRH5tXD+fiJylYh8TkT+JITG9uixLZxKEIz+c76g56UeO41leUlVf1VVv0FVH6yqP6iXP8iwAAAgAElEQVSqk7OlYTc5eAJ8u6qeDk7p94nI24CfBX5bVd8gIi8FnoVf+DgXp6Yj/vf19wNgWmSM1wY+ZNk4TO7I8gpXhbxZCjZzDLKKUV6wMigwKJMq4/R4yGTqu2R1Zcq9jtzJVzYu4I7pSv1nXW3uXv9ZI1t630wx4M6NEdPCnx/kJSt5ySjz5rJJlTG0JReurHNyOuSm04fZGA+oSj8zsXkFCmVhcYXBDpyn3fg1RllWYYzinHBkZcKFq2sMTFUHHAxMhUO4dWOVtemA9emA8ThHnTAcFVx4ZI1jgzGH8ykDUzKucm6brFA5Q+Es09IyrSyrg4LKGdYmA6xxZNZRlJaNcV73H4o/DmuZxDqyYcVoWLC2NgQFmzvyvKRyBmscFxxeZ2hLbl0/xKnTI1xh0KQ8JtbPHAcOMSCDinxYkmUVzhnKcrE5lCIUS2hOBwg7xkskJicJZiC1NFlGuv6hztoiUyWmLqchk4g3ldU+HG38JiogZcf3YtRrQOB3DUDb2lyaNDiYAtXKTPNbSksrTNzRbAwaIInzK9LR7OqcfJeJfyeaMWv/XOKnqn1aisTkxZ1Q+1ozzWST/0wUJFlwq4LXvKSpI7ZXkt+pFmcKrTXWrcLpu9gLvLSbGSIUOB1+5uGjwLcD/z6cfzXwfLZjqA3D9AMnsFPIC7BZky6nTj8UHbDGr+2cFjBViHHDPm2QgvosELcfHXHq9IjydF4vtI0JWk0RMp1PqdMHSeTRDDaGsCFQDXwG8Zjq58Y1wW74+wcxvVIwPZjCL1/yGbl9xod4TQ2UmT9/q1Vu5SL/wmXUb6WUUmfCMCUMK19GcWjEDeYIX5v6zA4uxy8u3pDa/uyzX8A4JFwVB9PMZ82Qyv8xMR0T4dtUNEluBzAdQJ4utQjM5YBb9FjY5wlWJ/5ZF5Lo2on/oFCNLNUQqmHus06EssyCC9VVqQNdzifsJC+JKnajagIWZqxHiuamdsBEfA+Vrg9GoPGhdOqqB8tMkkGfeuua1DSW+kykcs2zqa/Fd0izBY41QeA6fxzpso290e/cTBCmiXAjPBej52oa1O96HYVk6Wr/UNO4EIhhfbv8ztwhAtCYVmCGGh/9K91AjY6faLs0XjHAJNIVy0bBOBeE6ILCaQ/w0m5vNmjxGZTvD/we8AXgDlWNToa44HHWs3Um5cHqCS64OmwNHhylPj+bzyyebhgY7b5xi4yYxXtytNkaoxoJ2VoG12ccuTkVQEq2oWRjhx27ek8jDbZgzU3Y6sHniStX/H5FflsOyNdcs/dSEC5mqmHLDMXlQrliku0x/GypGknYugNEpc4W7nP8SZ2B3RS+rJjupxr5F9FOArNKyF83ENT4DOimVOwkme3h6S5WDaZUsnGy1UcW6CqUbMMvUPRbe1CXHbf1AD+LM0V7cPMz5WYvqOhcd5mp7enFqgmZzsPWHYvx056Y7e0WdoqXRoNj2NMTPzjHQa7qDNpZEm5dtYVRNzdeHe4dBse63LRMK96NThBYYeBWY3yI96xwaAdmhiYgqlCljp2g1VnbaFMiaGaQqmzCsqElPOq2pKHhUUtSRbOwuDbR3mqYpv0AMi2bfhEBcV4QJRF+JvUvkRyXrikTWr6vqNW1foe2q7VtOtLw8wWwF3hptzNEVMDDwi6ebwW+cdZtc559GcHpdvTIvXX1xrGfaVWKTINTfWRxQ1v/oampIZoRXG6oRgZTmCb5aO7DPQenHaNbynqzPrtRYtan/mUrK8hsM/vJDDrMcQOLZgY3MFQjW+/9JA6yDU+XlIodl8i0RIrwMuUWzS1umCGlw0z8mFIeGTI5kZNtCHYSX3D/ZSaudixLqZhJ1QiBsCWIKVxrp1/NDNVKRjX0s8FsrcSul+jAUoVdbk0ljG6rEKdka2UTwWSoZ4G+Q5KZaul3303XkkhR+XO57xPfdoeMi2Y2DOgoRweZF/K5Ib9TWrO87iaP86AKpVuQ+w4YdoqXjq3cUykqH+AwBSnDQB+1DhEo8YNrEpVXaxjJ2iONQiVZnyOdZyC8zjEDAiDW+AE7EVRAswYpri1KNal67VEQKDFFUTCLqQPJjB+gVZEpLRNbFDqBMFopg6xp5kchawSlaa3lktieIPgg7pXTNiWmZrrYZqm3KGGzICQIb0eToSKzdZujZuonCs6PS9Yg6diUrM3qrhmbh73AS3vCa6yqd4jIe4BHA8dFJAszvoO94LHHDkMoq/NTOEX0vNRjZ7D7vLSbO+FeDBSBmVbwC7leBLwbeArwBhbNpKyKXZsi4xJRxa34RYRmDHa9QCZxlz/8VuS5n1VobjFTg5QWM9XarFTb0ktlcPM6Zjz1M5bpFIqymfXHjzXIyggdKBI0FSkcdqPC5Y1dG7wmY9YmyKT05gdr/fOlQ6clZqNANqZQVuiRFUzhGN5RtEyTdlLV2iGAmRRIUfkZW6LGS1G1ZqRYg+YWcQOkyrxJ79QYVClWV4NfTjETv9283aiwp8a+nDiTrCq/H0aeQx5en8r58yIwyMEKcsdp/0xmkSLz7U8SZ5Jl9WxcxkWtTWkoU4rSH1tBDg0XeqdUfV6+8w07yktOkUlYV1ZVjaZgbKNBpFoF1NqJ5DlqMq/VxEwFhYPob4k53+LzUYuo10o174ZAyEuXQwxMKMum/jTXXFk2v+v3Kmg2heczyTKUvNE+oMmRN+2so4v0WQNioAhtrPnetC3NaWJYY5Fh3mR9iM+VVUtTo2wsEhjbKqcuWx1Y2/iiwFuHimCpdT6XZl13zevBSRv+O0nyAS5oId8TvLSbmtM9gVcHW7kB3qiq/0tEPgW8QUR+Ax8v/4rtChLnMCc3YDwB680B/g9zUBT1CwX4Pyt9eJBjM4MbDbwdumwGc6kqzE23+eiJQV4zq9apTgTJsrp8Q2Njl/VJEICZV79zi6yNYWPcTneS54EZXPMSGgMjPyCbcYFMTW0rl6LCrE38y25M8+Ib8aYKF5jViD9fVY3AyjNkYrGTEpPZYJ/3L3B25wSpKtR686IZT30b4kAVBgENv2U4aGiP/ZHnXgiVpe93Y6BQ2BijYQCR4SDY6qv6W4ui7mPJA2NlGVLGFFOLmiKklRbpPMKO8RLE98d6nslMO0NBFATW+IkVANb/19Y072Ti96nfs+jDiYKpFlLamOIiP4EvK/6dBp9QOQo58O93nPBEeuKEMX3/Q1mSCtXUlBbfuSjoRGrTmT+f1GmAqmzMnOAteFGIx0Sv8d4yEUyx3a3ErMH0mGXhno45sKqasct6313Lp9ZFNGUWgb+wTVutaU9Wt8CyvBTMyS8HHoyfSv+Iqv7DwgXMwG5G630MnwW6e/6LwBVLFVY5P+hXIQysXPODo1M0vgwiSJ6FF940L2BZIsZii6qZRcWXZzxBJxM/UFb+j9XptBZOYq1XaIZD/1IVJVLgB+PxGMlzvz3yIEfKHL3tDnQ6RaxFgvBhOvUaQ5jdpTNCEYHSYaK2VgVhWwRNcDAIU5yi6YfINKrotPAvc57R7PlSwsaGpysOKHnutUMRr9FNJn7GOS2o945x2mJ0nRZecAXtTwZ50CyLum6xthnYYvRU+kyWoZMJGmeuzvl6jUGyqi5LdDGGAnDVonPDg4Md5SXED9aJzwJotOMs82GZ6eAc/STRKhF9TNE3lNwXLQ1UgCb+LEkEUxAuPiAi8TsZRTGNj8oImKwRTlEw+mm/fya0pQ5gyBr+IG4nEBfNlpXnqXg9almqjQ8LvNCJAi22z3oBrcOsGV9UqcN4Raid3clE2X8nNJnAM2lb0oCQKqlPpOmHViqpEF2Yey2y5QcuzxkvvQT4K1V9SlhPt7rMw7OwJ3xOZw2XDNAAzqHjCTot/KBv/AuhE/8nm5WVoAkFIVRN/YBcVWhZeqFTeWGjRekH0bL0v0tvJnBFiRkN/SAfhcB4UmsWfoaYmB7GEz8rA1/GhjYzz6pCnaMOs7MWcTmsbzSO3mgGAC8wQtirjie+jYE5qKSeuapznr7KoRKcwDWqZqbqFIow0yzLtmBLHLVRiKb9Xtcd7pE8Q4vSa1llWbdRNzYahsI71N36eiPso+ZZOT+JSAX1xmJ7lvmJ51KzvVcC3wvcpKoPnnH98XhT2DXh1FtU9dcXrmA/whrc4ZVWNBhF5QVCnMzgI8zITB3IkwbciCoao/FKB5QIiSZSuRDBFwdpg0ZNJUatxaCKNMggDvgxuGKLgVat+Ai9zDTJVpNoNSmqdvSawwuuwG9SBi2PCq8Zts34PtpP8VFCUgf91FFy0JSRmc1RjSmtMWABWwujNNVTOx9g3NUzCittNNMwnGtm/X8ThV7SLsyimtPivCQiR4FvBZ7pn9UpsFjOsS1wXtpAehxUCFo1nwXwR8ATtrnn71T1YeFzsAVTjx41luKly4GbgVeJyIdF5OUicuhsKTgYmlPqwHQO3RijRYmrHZ0WMS58Bxu6tV4zmISZuxFEjNeaosPRGD+Lryo/Q4ozmVSFds5rCGXjpGzWL4g3zU2m9TlJ/SdRY6rt2aaZnU2LRrOpqsZ5Ct6PU5ae7lhXrD9ZPyFRUwkOap1Og/lB/LVoquv60gJtdR91136o+n6S4JBVr6l6O3+jLTWmh6o+brU/wNOZmDpESLeili1M7C0o9YaSC92u+t6wyWCPALWG8kTYCVfDUoSyCgtNYyBBWHYx8Es1XLqgNFqqKq3Xs5nC1QFB/h7xSwrStVNhWYIaaZYdBJOePxe0iUrr7N1SuPZGgXiNTrPwTFwoK2zObhENAiEc25QOM6n8gtwqbL9RB2rQWh/V2tJC/HmXSWthbixbCr8esrUcowrrDrvrlKBlSq1zDMb+TULnm5uUmL2itShZpNV3dcj7HO1tEzbz0lZ7o2XAI4CfCHn2XgI8B/ivi1U2GwdDOEUHaOVqU5KQqIVp+vw4cAZ/Btb4dQatRXQx+sY1553zggQ/mNYDf+pXCfcB3k9VD7jGD9qDJopGY/1BqMpomPiFZtAwGvrvoqh9XUDjr4Eg7BTJM++/snam87SmPZpQoBYGYq0/jv0UFlDWEVi2MT3IYBAEd2L6s9YLdBn4/8Jp8CGFVy0+H31s0cwXbfap/0yCEB0smiICtGzN8hbabHAbPEZEPooPw/55Vf3kks/vK1RD4dR9hpjSD6J+cbfDVNQDrLP49YED/MJ1Icnq4MuJz4qjXhwfs4vEtXl+ETvJovI4yIcySkAIWV6kVW4UDDErQnTnxLWKapJktUT6/DMxoWukyZSKLcBMtV5YXwuBaE0L5cVnkc3ldWEqvyC+XtvVuWdWhvC0L32WmEYo+bY2rqu6zFTeJEJTrV+zGftzZvqpedjMS1vtjXYdcJ2qxl2kr8QLp7PCQsJJRF6kqr+03bmdhIg8Ae9ks8DLVfWFW9wMQx9gIDGaRRMbb22vTTQAY7zDMEbyZY2WUUecpT6XqvLaTFlCFgbhOHBGjSc6jGN9iY+FLGsG6LKJ9ql9RZGutIxYd2a9AzY6jcuyXjkvKyMvOIvCR8OBj4irBVPepiN1sGY2aEchxDQu4Au5nupgkti+aHc3EoJAXEN3LC9po0w6dcbjytX3SccpLKsrTd+lTugFIdXCDLUIPgTcN+SseyLwp8ADzqK8u5yXluIjoBrCqftIyKISvitDTAyvNoyFJgiNLHzXA3X4Fp8eyz/fHEeBVeeOi8LJ0qQai3+h0hqMfRowCVlgqAfi1qBtAo1hdhoHeww425TfaHiBvhLs1H+n5UFDX52VRZoyZqUUioLEpzxLf4eyY/2JgEkFngspweKOFeKkEU6uTVddZyJ04vn432zKMr8gZMGACFW9UUSuFZEHqupn8FtmfGrxmmZjUc3pu4Au83zPjHM7ghAS+3uh3uuAfxKRP1fV2Q3OLHriSGuwlGnZrBaHxCFo6kggopMVvEMzGQjrnSfLIOwcrTUSOrC4QdN9tXM2CQGVShuTXRrdVFbN+xKcq0CdyUIHGZhQpnNolqGjrDGHxF1JUxNC6eo26zBLBG0wjWTZJhOCBu1Iqgo5NGwlzZQo3GOQRN3Xoc0xmkgEHVif4SGPZrzUGU7jXI/h49GZXZs6DOS2qTtkG/BrspLp5Zdm/vsNVHxAyA4h3bJdVf9SRH5fRC5S1VvOoti7jJeW5iMgP1xw/HFfY1rZOpS4qgzTyqDqU6Oq831sjJJnFZkoIoo1Smac3xXa+l2cqzB6V85QOcO0spShLET9JF/UW9pn0ONcCGlWcCpI/UwwPDi/0aY6fw1AjJJZxzAvsUE9cQgDWzHKCkwykjsVKvW0FZX1uy8H+qqQWFmkWR9kjH92YCtsaGtEbGOlEkKxpabPOaEoLNXU+rbH/8j48mPgoM0q8rxiYCsy6+pygNZzAGVl6vJ9H/qyMuvIs4q8E/yQ0vu5eS9AxPK89BPAa0Ok3hfZgX3PthROIvJs4MeAy0XkY8mlI8Dfn23lW+AK4PMhFJawC+mT2QFp3OMAQ9lR4SQi9wC+pqoqIlfgxeytZ1jWbvBSz0c9zgxL8pKqfgQ4GyvFJmynOb0OeBvw/9C2IZ5S1dt2kpAO7gVcm/y+Dr9Fdo00WeVw5Tgb9z4SUuj766YIPpQkZ5Y46vxwGKEa2laIqQsORBOcrmoEZ5sNz6JKHq/53HXNvdH5aorgXI1alvHOYwAzqTBF1awLscbnjovOz9qR2bRVrVCNfOiqmYb6ckOaNNhn/Q4O6LBtgSm89ubz/SU7bIb+8ZoaIHmdGBbwSVhDW1CwkwozKf39mWmyOycr7V3u8wn6LMjamC+i7d94jUpzIW470DJPWO9ja+zzjcar+RLh4Ysv40BEXg88Hu+bug74VcLy+rAL7lOAZ4tIiU9N+rSQAfxMsBu8tC0fQZuX7nUvw2se9BrG6mfkEVHXtyhTms08LVpfG0lJhWFAxRFT+uVQ4VouQqWKA6bh2+D/9pgbuEIoVFp1jdVSJDvkVXhNx20RaFwh5FQMQiSNEWUkJUekYhTeKxu+DUKOIRdLJ+8DJRWFOgocY3WM1dMH1O3PxZGL1v0Qr+ei5Ph2VghOYay27rtKDWNtfKkO+f/be/cg27K7vu/zW2vvc05339eMZkYjxGBJRoJAgh6JMRTEBscPgR1Rie1CihODcf6AmGBjJzZj2XLZsarAclGJixhsY9lQEUQKIKOUZSvgoBAbowcYgRSh8SgI6zWa53307T7n7L3Xz3/81lp7nXNPd5/Tt3u6+87+VvW93fvsvfZa++y1fuv3+v6opWMiDRNpmUgXr7PnkQqNejRrfo06GvXc1hGdOkbSse3mXHNzJqL52XcondqzTsdWMgAvYZO5dBo4VDip6g3gBvAmABF5CJgAl0Tkkqr+u1Pq1yqRvbAoLBK/vlRH1+fmxKtdJnWF5EjtyRSDl955WjtbEMs8wWjnTou7k8VIoT6HIX7byRGcUpoKAZMjZuJoEo1IqD0pCkcrt1CGGsC1oVjsyZ9JcnTWLjtNs4VCdHGBDylaKuCnmgXKcikESf8oNhYvMWKJQmCSS3e7VrM5L5kJQ6xFI62iNYVPIAmYaK+vXM8UL5IFbao0Kk4WEudR8wlUN4sctsOg69vJAVT1TUd8/kPAD63d4OFtncVcOnIexb7lufSVXzXSW6GiUU+H5EU3mefSgl9LwBPocDiUER1OlDpOhNvZOVUKMGEUr0mLu4sLO/QCcBTb6BBuBYscdAQCLgoATxcnYo2ZDz1KLWZ+drHteaokTZefRIMt2iG+aA4lxPpItXjGYgIjEOI5vWCaqvU79b8rdpCjpZDSJBBtvCZgnCg7NHQiNOoYaS88R3RZ0E2y8IEuzqG6+Nq8wEQcY4hmyylNDGgyAVvREKJg1V6oKvg7v/7V2HAunQbWDYj4z4EfBL4IeBL4HcDHga88pX59Bnik+PtQ0kqZt1SfexbE5UzxlNRXMgUnn0jyD91Bcw9Zi5I29JxgkP1XWjIC134hwS8nLCbfVNIwUrJvqQ2MK7SqokDw2UcjZYRg0ccFf1FKSoScTCgh9jet7uX9Uh9SSPnI9xplEqSlT61MHk70KZW3nzSWGOGoEtMrY4htYoxOY+rr9fQ1bDRqWrkOUPalhcxknrXdNlggypqQ9Sq6nxme57m00TwCeHz3Qf74L33nQgBmrhkmaj4XIfs30vHKW3HMkbcFtw2OpvXZ79J1jmWd0+JyFHEa/SGKi0UuwXw4e9MRGqSI41lsRJxpEpUPuX9ONGt9TtSEl1Mq3y34mxKSrygV7+yCEDBfV9e5PIZ0pQbBRV9R8rUB+Z7JFwZkH92CnymOufdjBbbGTfYRlf4jJ4qj+D32f+Q6JlXLxDeMXIsXpVOrC7Xbjrk5HzPrKvPzxWKiIbjsv4K/fthrYP0447m0bkDE38RYjn9eVV8rIt9I3AGeEj4EvFJEXg58FngjfdG0O9F26LPX+zBliCHiffSYeG+RYSkSD4yuxIlFwqXs95J7aj43poS0gHpvYedgpqvRyKjpy2sSnx/kHCWNi6uMEo+eIqM65hqFGDXojAIp8XClENIyRyjelzQuDZFfLGR2C41jzRF56ZrOou+kro06SRyJ50uWechyxnlEolNJwRC5z2LPMAWPpHuq5ueShW3OwRAy2WVu18dgjw729jNLRWakCHcuKKsg52C3twaez7m02TwC/G3HpV/ayvXOUvRYjo6DPiw7QSO9HFaksoyiE8Ar1CkKLp6fTbqu/wkeOm88q2CL47gha2BJwU9RbblP0XevRTReliTptevIEXywFC2n5EjC3Ld4jNj/WDotj6Gb2L2CsCB0U1RetRSll++V9n1+8ZlOa9irWQhxL6MKKfpbRiDa96PoSPsIxJngGqvzJm2KSLQfv6ap7jzMpXWN+Y2qPgM4EXGq+gvAa06rU5Hi/7uB92G7ynfd6/klA04A0QyYfs4pnre5NMyjAcfGOZhL62pO10XkEvCLWLjgk8CpKn2q+l7gvWueHLWTnktPg7EYSMpDgmyu0qXEUalrOy/Hp2o+TyMxK2JhtIlcVWpjKU88fIlfTlOZgaIcBmCMDolMMv6dSVkbyzVKnHl9ZnqRZ5WSYkPoE10jt13K69J4vkLOqSpJanEOpYnlCHzPfweLDBTpb1hMsq0qOycxaORE3Wh6lF771GlhDs1txvGWeVexHXHONM7EVJH4ATfQnKDf7Z5jPK9zaaN5BPiZcu23YvJ02r1XEpM5o9kq+ibRPiG2TFxdWcbdOmO7+NQ21k7wLCTwLg6gbyMHHhW5TEk7WVnCXBbbCFVkaCg0k/I+xmqRtIa+WvNimzaubty3ZVpYnANdrGxd+rGD5nOMHUJzwnHqQ6gkJzSnsS48hqVnUyYZq7Ok6JwXlTVBq8Dtmz4ZeOWYDsBZz6V1hdO3AFPge4E/AVwF/sZpdepYyLVhAqEx5zoqJjjyOZqFUV74uoB2M3IJhyTIFijxO2Q0ygwRlpjaRYGV2lj0FSGCNg3SuYKAtcsCJJu/VFGaxevS4pwo9pP5D5BEQ7TkU1qkQZLeJAY9q0PyvzWNEdGmvDDvMtNETgounpk2kSqJmQnF+EzsOfVsGep9NhVqZLLAe2u7bUl1cBZMj6p9mYLkZ0rksSl6cLnezoHvAGceYbQGzvVccvPA1qdv9RsyEfMXllGasBBYU/o5E9lpjkZ1wrIPXlJFXNWYz2b+yMT2YH7JdJ/kc6W/Jka5JmaI7D9Vso9Ycz4j+T0ygeBIEbc5cEiL+3QhRpLqIllrsZlVEcIk5QgWg1MsP7ANi88vjTvNwfSO+p4OyYKcJAY5pcrScX2IPlp7dpFVI1ETuT66V530uY9xA+Baxc07qwFXUlAdhXMwl9YSTqp6u/jzx06pL3cPccjI2xdYV72vxkdnfvIxAdpE3rvyy4qlJVRDXFjp6XxC9P84EzZUVV6otdR2wDSrqjIBGctwLAsKLQIftBQ8CwzjvQDIzAyRmkm7rq99FBnRF9ganDNtMGqQVH6xjxL6e3lncWPJv5aEVhLgThBXmZBJgR6Z4SEmMi9oc1HYJm5CMOGtwUppJP9VKk/QdT1NU0yMlpTkO5vj0nOYHfH1c/a7vaNw7udSCMiN3X6j4DzUlQUCpYU+LbhLydRI9F0Sz4vFLXMCPIUgKeo4aeUhJsFL2pi5PuAmR8hmIaSLwU2J77ENi4n33i0yhcfE9ZRwL20MwIkCb/ke5vMthFNhBXCTwtKi2gdEQR98VK4v6X1eLnaYNoejqmdeF7FSObMml8TQ2veBU2mT7Fx/TUQW3GACJo2xba1AaVhP4hxnLsWk7w8Dn1XVP7LZ1XfiqCTcW9yx77GPAFXVK3fbgROBGM9bWQQw1xhKVECpDENc8IS400iLN9iiOxr1TAZxgRaI5Kg+l9QALJBiMkba1oRD7EsWgpGFQlJhv1ycjf4eaiSQmirMdtFst721WGk2UQ3FvkgXYBwX97Y1nrvEgZfuXwYhQK45Qyz4J02T696ImClU53ML9PDeNL6iHEZ+1nU/MWV7q6c3AluwvEN3b9uxrQmyY9pVKjfS90WMdipF/zXF57V9VzIZ93x7Rwgnm4xHnHNGuDBzKWhvlgZbodrW5lJJKZXYQ2LdtDsKBbYdNCBdZYwlKRo0l6kJ2dohjYPGIykwJmnTxIdTRsTGxV3asBhlOmvIBQyhN7PFTaSmshGdIPO2X+hjdK3Ea8rFPjO8lBYTsM3arCg4uFzAL/RBV5nQuLSEpGNpzovlUkpiiymLiKZCol1AqrDYFsRyQL2GJg6LAi7JYqPwzPdbB8ebS38W822eyLt8VJ7T5ZO4yYABzwv0/GpOw1wacKGw4VwSkS8G/jDwVuDPn0QX7h1W8qg9IdIzXtc1XN7Ju6G0W0qmtczcnclHCwd/NFURYhBA2qncvt0HL8TdpV7ehp1tZDY301hd920GNS0gqJHEjuq+HPz+1M5PO68UZp12f3UfzKGj6L+Jpg/Zn6NbIyhsKaYAACAASURBVCu8Nqn7z0JAprFQYBcsPD1pXuk+deTBG9XI3r5pLONRX84ilpuWybg3R1ZVDmXXxsrAmxaPMabXVb/r7QIuVeUdjdCtkTGlz2Y9V18yXxRl5HVrlHd90gYrNe2wSsAApUHsAGwSWbRGsUHBSFO/GdgDvl1Vf3X9O1xwJO2pquw9HWHx4kXKABA1EgVae2fTexcUQpfN4QupDVpoUF4tJETEUjWSdrWcoiCykIOXd/aq/XtO7B+FeU6kL9kR57akytcpvSTn1UnO4cvn46LpMDuL7Pym8JUu+ZsXKgJoiM8takQUGtDysxbpPy+vBZgvmQqzCdRnDaoP/uqLLKoI4qP1aIOcwaW5dBTD//8M/EWMjutEcCbCSUT+J8wxHLBExG9X1c8dezGoPHI1apJqPh52dtCdsS12lSPUPjsd/aTCVR7Zn0HlCTsTS7xtbNG3dsy8puOYTDqqzGm7NUb2Z0hy0jtnxKxjj2zVvZ26tGWL2OR1oGPzE2mtuEJtjxw+fcDB1sSED9i1Eh2fo4puNEYuTXDz1oRMajsmCOvVifVBTKi6WWPXOxdNK5rLVetWjew32V6e7x8F9UKQUGX+KYkl3POkjLbvMBnR7RgVUp0c05PaJsnWCMbVwqKgo6pfbEQI2yPanRHdOLIHtPYsq51Y0v7Q9FEyy/UG+McYA8SPH/D5N2Es5K/EaH9+mBX0P2eJE59LQkxkl96MrNpvttICWFaGTQSjqVR6QojSY7lsS1D6UuKF7yY694Xknyl8uWnxr/zi8UIALfiAvIv3cL0PNQ5QJeUMxuAE73o/UOlbSmY7b0n9kkz2wZLos5myIEFeiHgtx5rg/OKzLIKjSAEN8W8dV4vCOvUvmQdzGwHaXkCKG6NV9IGlpFvn0MrM5OtgxVw6kOFfRNIG71di9egTwVlpTm9T1b8KICLfA7wF+E6OuRiEcUX7JQ/ipg0yb3uG8OjzCeOKbuzpJo5QC27bU23X+L0xBNNKQm2TpbpVI3vznlliy4hD1DnwQpjUcG0L6QJu2lrkznZNu1Pj5l0Od7WFNVgBshhJ42edceypRdDo9thevlmNhK7fcYFpIqOKMK6sqFrXmWDaquhGDr1SI029EMqqDlJYb+LjA3DtGDcLOcoHiHVxos1/Z2STwTvcbBs3b3sHM5jmGVhks0gRRfMWrRzd5QnNlZE948oomvx+srtbv3CSeQSlC8Z/GCOitHK0l2pm12raiWSpKJ3imhj4karFHIJNTBFrFBv8FuDHI5/eL4vINRF5iap+fv27nDpOdC7hHEzGvZ8HsrN/Ibgg1vmSWDJ9IeUhBUp01aJwSVqKlyzcykKDOUBIljSRso0UaFBEyUlKCi99P+l+JfM9WN9TPbTaZ1YVHcego/R39tMmweHQamSbqzgGTaVu0nNyBUvKUmTeHVF/RT/zM0gbyCLSTytsc7kqyi4c0CbEcPXYpxRUItWC3/sobDCXvg54QywrMwGuiMj/pqr/9dotrMCZCKeyFAGwQ+8oPtZi0I2Fp1+9jZtbTH+1Z0XDwBbjbpTyCFIHwLUV1f4IPzfHajsRXAP+ao2fbuWFu9nxuEZBIYwdXW0Lp3rw00iyigmf5sGoXUUePKsNY5+HusiLcIKfBqqZ8d/5WZ/Lo7Vx7amDMHI027agu9ZyStqJ0OxYAbGcgZ5q7TjTNvzccjbaidCNbZL5meU8pMx614GfJ3Jb+zt9JjEXpb5dCDS1EFbXaMyrkCyEXXqGW47mkmN+Wdh7aEy1b5qfBLu/K/oZaukLx2HW024sNNtCGwuxhsjB56frvlh3TKi7LTa4ijj1pcC5EU4nPZdwQkiaaulojwvgAoWW+GIhpl/IUxRdScdVaghFBFtuLxe1pF+oc1g0/X26GFItfaCClhWfY79zJJs9pN5SlhWoKAjqgJYh47UnlAKzjLaDLDyyCTsplz7e08ti7b8ovLOwK/uSzomCPwukpa8k+CIqMY2vFN4FlqMOF6rsekFLLe4wbOBzUtVHgUeta/INWFHOuxJMcIY+JxF5K/AnMTLMb4yH114MSibl+tJ9p9rXARcEShZ2EXdbbHDVCrBiC3u2OMm5NBldPdW+DrgguHMuPe84NeEkIj8PPLziozer6s+q6puBN4vIoxjFyl9jg8WgZFIef8kjev3LA1rbrqS67SD0OwTXWStWaVNxbdSSZoK0Zs5rt8jaiJ95/L5d240xf1Sxi0jZ6VrZ/34Kfk5fGdSbOg5JC7H7uwbcPF5feau+2YC0ptKVlTxD+n9kfTCNzNpKVTJdqmg+VvxccHNwrZWpTinlIbKEu7lk/rBc0VNNm0znEC+zcheCnzn8Xv+lSHyOadzL2fmprXYL2isBaSRXQXVze+ZJa5POxhTqXltL3GjdWO051gEdB/uO1oCw/m5vTWxMnHoaeD7n0pXLX6zdzqgwFfeaU9YW1FrPu/ID/HwSfUM5UTYFFZVaGGRfkx2jN+9VkrWmxKyQkn7zQAqTX+5fbFurPudHChNYLsBZ8GT2JXZSocu+/ItpKvHprRiPXUhOjM1JvcU1d5gpi8CGBZaJdJoWzy+kXDF6d4Msfi8L1W6T9lTMzeyDXhPHnUuq+n7g/ZtfeSdOTTip6u9f89SfAP4pNqGOtRhsb8941Vd9mjY4Rr7jUjXPzMROlHnw3JyPrbqkBKZdP+z9tqbtPEGF7XpO7TtmXUXTeW7PRrTzGieaq1gS7EUQF3CV4nygDUJoHSj4kVWydKLM5xVd43FVwFeB6X6Ftg6cIj6+bF7t2s6CIrQT+7xKi4CdK6JorHhZjTp2JnPazjOfV1Q+MKrsTZrOapr9CnFKvdUyrltmTcV0WvUVTKuA89ancd1S+WB1YzpHYlpuWmMxns0runmaxGLj94q4AALOK9WoRVVo557QeFzdGdN0ZKp2LiCCsVS3zp5jFJ7iA64yRunRqGVUd2zVDdv1nO2q4cpoShU9swdFLWQo2cx6QngP8N2xSN/vBm6chb/p+ZxLCLmEi/q+LEqmG0IW/Cgaivpj0tc+S0ItLcAL30sy06XxRSGXS83E63KJGMTmC0tCIefxkBd8Jb9a5u+tJBKlRj9wNKulPi+WzyEL31QmJzNMpPajX9dHYVaOxcrjJP8ZueZb7l/qcx54L6Ry8EEpnKK5MUShK52iuAUBnpsqNhHJLZC/j6KttanATn4ubYyzitZ7paqmSsFvAH4z/n6sxeD++jbf8dJ/xfVum4lrcASebq9QS0stHVOt2Qsj9roxXgLbbs4lP+XB6iaewO0wpomqzoPVzVxe+gvtVT5862UAtOqZdRW77TgLvWlX8dz+NjujGQ9M9rhcT3EojTrmoaINnirGY1YSmAdPGzzz4Nlra5rOs1U3XKlnTHzDlp8zdm2k+Ve23JxaOna7MdebbW40WwBcrqc8MNrlsp/y5Pwys1AxC1W+524z4lYz5r7xPl+y/RwPjm4xizV6nCi1dHkM6XncX+3iUPbCiA7HbjdhrxuxF0YEFWaRuKuWwIOjWzTqudlOuNlOaIO1e//oNm3w7Ieaz+5dpQ2OK6MZI9ey15r/adpV3JxNSOWnUymD+8b7PDDZ5YvGN3jV1hNcdvs0WuEkcMWZGnukcGKz3d4axQbfi0W7PY5FvP2p9Vt/fnDScwl6oQT9AkusB5RrmoViMS6CVzKNTuqf2k+qo2btA+IKbYa8mJfaRMkDlwVFakfpo9HUGiiFXH9dr0mog+BMYBE1f42Ld5eFYrEox74rUTBFTUgrK02Rx1gu+MUzUoEYF7jIHRgLb0p+cEIYx+AN7a0Yoeprz2W+v6Km3CrtSYlBR/Eemc1crGxHuSk4CmedM3hWPqfvF5Evw/Zgv41FF8ExF4MdN+fLR09wW2sm0vJEd4UdN6PRimv+Ntsy56nuMk+1V3i222EvjHASuNVt8aJql4erG0yk4WX1bV7qLzPTho83HY16Hh7fpAmemdbsyojKdVHAVFQSuHZ1n8uVURdcq/d4aHQTjzJ2DaMYqTANNY1WjF3Dbjfh2XaHm+2Ebd+w5eZMXMM42uiCOsau4cHqFjtuxkTm3AxbTEPNrbDF081latfy4uomD9fXuT7e4VY34fPNNX57/34cyv3jlvvHe1yr9/miyXNc9fs8Uj/DFTflethmrp6gjuvdNo16aul4pH6GbZlzzU+53k14NlzimfYSl/0+I+m43m3zbHvJqoxKxyzUeAk8NLrFtpvjJDCRhst+SqOe/5dXcWO+RauO6/Mt5p2nCw7vAmPfmqYUNdmR77g22uOLJ9e5Wu0B4EXZcbe57KZcc0dRQxhEN8tzWqPYoAJ/Zv0WzwQnOpcSEm9b/jut3VHbEApzkoLLgiYW5UzCA+tZbint6GMo9EJ15nkfKZfNhynAAl3Q6g5dZKUwk6niGotMTRpF3780OF3k8AvaR6cShaFY/SUVkPmd907XpP5bziFJuuEKYZ/U0L4chtyh0Ujo6z0J+ZLFc4p7UZwjAVSN5DWT3Obnsp5pb9O5dBo4q2i9P3rA8YuwGAw4r0i71hcQhrk04FRwDubSPcEQoSrM8dwOY54IV7kVNY3LfspUR7mkM8As1IxdQ6Oep9vLZgZ0tuufqvDLs5anuqu4aFh/SX2dz8zvZ7cZ8czsEtOuIqgw8h0Tb9pOMrfNQkUtHV8yfoZHRs9Qx9Tuh6tbTLXic+01npFLbLsZn+caM62ZuIZLfkotXezTNk82V/jU9IF8bBYqLvkZjXq8KC9yt/ASeKa9xMQ10ZRppsDr7Tj721p1VK7Dj5Sn5AqfU4tq3HYz9sKYG902Lhq751rF+zl23Jypzph7z41um04dz3Y7PDm/ks2HTfDUruP++jZXx3u8Yvwk01Dz6eZFfH5+DYBWHTfnY5rg8RLYqS1xeU/r+PxMs2yDY68d89jui61/1YwXj2/xpZMvcNlN6arn1nwRLNR9wF0g5uDdkUwKvSZD4UcpduJ5MSsDH4qcnWVtR8ukVV0q55A0jcqjrvfJ5BzGArlUhzfTmyt9PEXfpA34nEtVfhi1txBTUAofEkvnJzbz7FtL1xb+KUH7IijpeRV9zO0V/qn8zNL/ReL9IvN577vKwQ4p8KEMV0/nlIEWSZNaB+dgLt0Twunpbodf2vtS9sKIT+49RBM8V6oplet4YnqFp6c7NJ3Hu0DlArO2YqtumPiW7eqLecX209TS8fHdh9lrR1yqZzw0NnPVF+ZXeGp6iZvzMTdnE9rOWUlqlE77Es7eB65N9pl2NbvdmKfbyzgJvLi6ybPdJebqeWz6Ehr1fHr/Pp6dbTNyHVfqyzlo44m9yzTBs9/UzJuKSR1NfZgwdKLUruPyaMaXXn6KbTfnqfllbjRb3GzMFxZUmMWAj6DCk/uXeLx6kMpZ0MM8+BxgMI++oiv1jF+pfgcAV6opV6opQYXnWvNzbfmGSjqeml3mqf0d9uc1Ppapfmh7l1mo+OT0IW63Y2bB/HJP7e9wc2oJS11nJbtTOW/vQy43Pa5aZm3F7t6Ydh5ZKbwyHjfct/M7qV3Hi7b24jf9w4e+B8LZmyIuOiQofhpJVztdFEDlgl8KHNWctGsMIMVpy8mn0vuGsnxYCpBI56tzd+zel/OusvCrnPlIkuM/CzfXjyW1UQgdWLpmRZJsOf7lchjils5JfSrzmSqXn9XK/qveEViR20s+piK6L5kc8+8pL0ykZ4CB/ro0jrJcxxE4D3PpnhBO12dbvPuzryEg3JxOUIVx1TFrPbdubVk0HJjzNAi0AnVgdHnOtZ19dpsxn755jWefvgQIo505V7f3GVUdk6rJC2ntAk3r2Z9ZhJ8TC2kNnWMybph1Fc/OtthtRvw7f39c+Pe5UlkW6e1uzBf2L3NjPqGO3/yzs22e2d/hxl5cyIOjazyhFfaqMT5G4aWot7bzVL5jtxlxczbJz2BvOqJpbHGvqhh9F//WAGFWgVdc1WWKL18FJAYkdDH0flybP2irbtkZzbg1m1B768Nzt7cIwVFXHV2M/Ju1Fc/NtqidBVlULnBlNGN3NubmjS26/YrC7wsxMk+8RR3eaLfo9mrczQo3B+lsRzgdK5+tL4GH395Zkw9M9cxNERceXcA/FzcDyxFmifUASKUeFso/wKLvJDE0lAu+amYAzzjADyLLND/lvRaCHqT3z+Rk3TIhVxYX9/K+y+eW/V5VXiIJ60RnBItcgKvGU7absOK5pHMl/b3cXpnMXPZl1ZiWNhIrBe9h2GAuicgjWLzSw5j4+/uq+r+sf7PVuCeEU5h7PvWJh6FW1Gv/Xd721DdtRxVqUB8rUjrbUc3njidvjnmyuoI2Dtn3UAfayvNct03oHM4H6rpjVFu49LypaOYV3cyjrXlepQ60M8/+tGZ3Msb7wO7tMe20hgB+q6WqO0ZxUW9bT4iLu6rQTSuYxfAeFx3BjaOrlLYKSAozrwJSK3OFT+2OCXOPOAWnaOuQqbXRenMeSxN3aY3YfKuV4Ly17yA4BRXjk+wE9cpsEtBOuC6K1Ir4wGirQYMw36vRuWdfYvtVYN8rN6ot6toEWNc52plHd2vc1DHat/ymMIJu25zSdILMxOZnFRllZjH3bJpMIIJ0HhWY3b8m5YqCtINwuisEhb39Oxe3ZZQmuVJoLP++bB5cphhKSAttef4qgbICvQa2pKUtX1sV79EyP99B2k+JUlgscAgujalspxSwqY1Vz2VVv8r7ylI7q8aXPj8obH0TbDaXWuAvqOqvishl4FdE5OdU9f/b7KaLWJPL4uQhIv+9iHxCRD4mIn+rOP6oiDweP/tDZ9W/ARcTrtP880LBMJcGnAbWnUuq+vlEKqyqt7CaTi+92/ufVZ7TN2LcX1+lqjMReSge/wrgjcBXAl8E/LyIvEpVD7V+uha2nrBdUajI4ZOuBWnMdho8tDsSed0wG684VBVmDtcKRKUlNJ4wN4JLccr09oj9boJ2gux7/FSo57GtkSJq9+5Gyq16Aq1Q3XaMYwR0Nx7RTAINIK1k7c3NAYG6kQWqkMQoEWrj1wMie4LF8aqAa4RqLoTackuqPaHaJ+Z1WLuZR6+JzBBxJ+Ua01jaSWw3E1cqXSu24VJjddAKZlsV6sDvOapbzpgoGmh3YH4tEAI0TZ9w6DvwU8njQKHag3rXtFjXxPGNoN223423D6SN/S05A2vWgrwANaeTnkto6AtnpkMakMTJtmyaSig1hGWz0yoT1Sqz06rPNsGKBFPVXquRBXbwFWbII5DaEnGrNcPl57GsRS021v9fjj/7lZb6teo+5RgO+/ywdg/Airm0Fk9lJFJ+LWvRNB+OszLrfRfw/ao6A1DVJ+PxbwH+93j8t0TkceCrgX99WGPSweSZ+IfS+zgi3U9asKupCalENdLuCO22Gt2PxMXxdhXP13hebVUEIj1QinZJC6/OUj6DCR7XAqFfYNXZPSWkrO2ib3FRJhG4aqJYss9zlrmDbkSkDZKe8DWa46TrKZo0WgchEadCtU/f9/SD0SKlsSOWKBnGkf6IKEQEQiyjIR34Wd+Wn0F9y+VkPXXQTXpBg8RxRlLZNF7XxD6KCS1C5PHS2JWW/EwBqjXqOFkHeqb1FxBOdC4RqyEvQ2HRlAWL/qfDcJB5rixlcZBgSuccVl582cR2gMntjh4cNJ6DULBQHHp9WbtqVQmNg9pexrrP9yisWZp9uT9Lc+lInkoRuQT8NPDnlgiJj4WzEk6vAv7TSFg5xVhsP4Spgr9cnJfIKu9ASVY5unSf7bhbJebD2iIv/SIXvC3kPmoToY4unpksCgpNC7/02ke8Z9LAwoien464aE9NCFRTU79yFnvo2+5GJgBs0e4ZubuR0Yz4JvWhD51VR2ZUbyfSa0VxcU/CzXWL/UfJXIHVvi5kvdvzEeMDrGObUTCGJmqfoRegGu/n2l54pmx51xivYLVnfe4mYlyAUUgmupd87yhArV8gu3eOR1ozKSQG+Gp/zd2tsjLU+DCIyOuxukce+FFV/f6lz78deBvw2Xjoh1T1Rze6yeniROfSRHZygck7sOnxddB1fZHLCF1eTJfbX9bClj8/qD+rAiKOwqqgg8OOx/svjGmV72dJGN8x5qX2Siw/ryOvPw42nEsiUmOC6R2q+jMn0YUzIX6N970P+BrgdwHvEpFXAKu+7ZUrU0lWeen+R3Ry3R5kis1PO3WwUgyMjFvLN1ZBM4yEdkze3eewySxYTHikhThTinjTLLqJZC0Mhfq2Uu/ZSbk8RqdU+3ZPgjK/6lHps7Zdo5naJWtUIQmunqDShJNQ76mRyvqkkZnZTZ2V7/ANMU/D+hUqyeUq/Dxm5Ec6Gj9TurFY0dLK+izak9Mmzcy1ukDw2o0kE9zaYE141FGAuNaEThKq3SiaUSPprGv7Z+bihsJIeHsBatf1u22/fvHOvuLpOudaKd//FfgD2OL9IRF5zwpH7jtV9bvX78XJ4vmcS1fdi5QNniFwLBPZwv03vV9/5Yanr7jPWiUkDrrPwffXsvLuUdjwuR3/eW2GdedSLGz5D4GPq+oPntT9z4T4VUS+C/iZmMX+QREJwAOcExboARcTkvJQ1sdXA4+r6v8PEHnovgW4qyijk8YwlwY839hwLn0d8N8AvyEivxaP/WVVfe/d9OGszHr/BPh9wPtF5FXACHgaI6v8CRH5QcyJ+0rgg0c1JkGpdzvbyHjJmd7SqoWNuz5TWoIFFISRw48ixX06vzClWbssZHSrA5dIIG/Hsg5RO7GKrZY57mdk6nzpNBMwVnshm/mSGQsWtZ3E8WVmw8QjFgsDClSFaaLZcWau1N5MmLWu2CfAiC7TGMr7NtEUNzJVxu/1x0IcW/KTadQws4lNoKstsXCh7oskv5LiZ8po1w6HggU6PduknZpm1ptDJdh4/ZyoYR3brHeUE3dVzaNV1WL/qIj8HuAx4HtV9dMrzjkrnOhcAlZrGIdhEz7DY2pXx0HKkzr8nqeTaZpztNZo/jjPRNbQyO7qWW9g1lPVf8lqTf2ucFbC6e3A20Xko8Ac+La48/uYiLwL27m2wJ85MroIYgBC/CJaq83imkCu/eLEyrA7yULAagwR67pIXtSTTwTSQrmY1Z3oSoKXLAizdu8lZpsDGhapQlYssrkmUhIiXhYElIRAItBMZJXJJKaV4OYuMkhjYw1GnumaLpvhNEcNRdLMLKBivRtRYo6w9aUJuR6NVtGk6GPfCpNnFrD0/rVUMRd6k2USPCkwJQs6JUcDlc8/lblPz9dMkGsulqrL9vmjnLjrmL7+T+AnYyTcdwI/hgmD84ITnUuqerDPKWHZFJaEmbjNBdtJIPVn6d5rrBynhtO+d25/1dhP4nu4cy497zgr4tc5sLKMr6q+FXjrJu0J4Jpgi2Ab+uVFsMJhkcZDE++VYvxhAK4o3BW0KPBFXmilC4uUIYlvy7vs3C/pUkqK+yQgAcLIF6Wnizo0CU3fdnwYfZ2X4hgKzCyYY4E/S00wp/5mapUkMIoS01pJ7KvkMZYszUlrDJUjjFz2SSVqlFJIlRGSqS+u7Z/ZMnVLKaBs3KnNyFvWBHwh/HJRuKOgIPONJtSRpi9Vfab48x8AP7DJDU4bJz2X4Ogdt7gDFr5iQXw+NaTT0n7OK0qtafVG4gSex+Zz6cRxTzBEELR33sXF2KplxkCDks+qFAoiaFCEJdLJBBGk7XqOsaVkNEkCq8hn0CbEHX/xAqX8otmKuseJayxpM0USe6LXl6VIuyTsZBUxYxJ2UYuRpuuFrAQSPYq2wvJLvFCh1AsEwXcWUprq0eTqp+maToufkHnK8nliptOyOudCXwvCzAWBpb0muzYhGBvv9j4EvFJEXo5F470R+K8WnonIS4o6SG/AEgxf0DhRwXPYDn+tYIUN7lO2t65mse41J9nXI3Do818e5/Hv8sLUnAYMOBWoQrtiA3Dg6dqKyHcD78NCyd+uqh8Tkb8BfFhV3wN8j4i8ATONPQt8+8l3fMCAc4YN59Jp4N4QTgp+P9EJaP5fIO7M3SJDbzqv3Ag54nkSgxEi/T7e+PqCaU7mB+qvNa2m8J04h3YgPmkZRT6DSNYscjcEirS+bIq7g8yyHNuqiNiUhV+aCVeYIpMG6HRx91VqRAIQBCn6saBVLT9LyM8msTbnZ++tnVU5IdmkuNzHO05cPyCCdrPdXowoeu/SsbcUvz8KPLpRoxcdZ+E3WoWT7sdx2lv3mvPyzOBk+nKMuXTSuCeEk9VPKb6QUqvVJFBiJMEKLAQ8BBDEasjE4AgJvenJQt40sjoEMx0uswsn0yJL70k0NybqekmLeEqgW8VCfEdni3sV55VmxLJkQDZFHrbAy8Hlm3MdnQWBKneYM/M9loS+MV0sjSeZGVf0Iwm0Y+Ec7PYGDLgncA7m0j0hnAiKtG2/+AXpF+wkqMqFf2khllITOoiZeNVmxPXH+8VbEcKCBncnJcsqbWhJwKW+Ltxv6e9SIK3o3h3trcJh9usDyiDk53UQ6/Eys/IylcsRmfnlp8s+rkOhig7CacCAu8c5mEtnRfz6auBHgEvAp4A/kbiYRORR4E9j3vrvUdX3HdmgKpKCDQpTWilo1l7kDjKZFfdafd0apqejzFOreMUOa3+prg1w5MK/8j4H9es4gUDl/Zf50pbZjdcw122kQ6nCfAM6iXsAJz6XBgyAjefSUTRgx8FZlcz4UeD7VPU/At4N/I/AMpPy64G/GylmBgw4Gqpo0+afFwiGuTTg5LHBXCpowL4J+ArgTfH9uyuclVnvy4BfjL//HBYt9Vc5LpMySxpE6E1s9p+ebPrymkXQNmqv/H8VVi0r6/B2lQgrzIWFj2zh2EE4SFNbVWDtJLBRSQNF2xeW5sSJz6UBAzaeS6dCA3ZWwumjWM7IzwJ/nD4R8lhMysDuP3/sB57BaFtOCg+c8/ZOo83z3t6XHfbhLZ5738+173ygOHTSz/s84qTn9Zfd1AAAB05JREFU0uzn9ac+esJ9PO/v1Xlv7zTa3HQuTQ6hAluXBmwjnBUr+XcAf0dE3oJxgKUCMuvQydjBgkk53u/DR9Ub2QTnvb3TaPMitHfY56r6+pO613nC8zmXhvf0/LV3Gm2e8Fxa+13bBGfCSh7xBwEiWeUfjscGJuUBA5YwzKUB5xyn8q6dSUBEUUraAX8FizYC2/m9UUTGkVJmfSblAQNegBjm0oBzgEwDJiIjLBDnPXfb6FlF671JRB4DfhOTsP8IQFU/BiQm5X/Ouqzkhjvq2d8lznt7p9HmC629ewEnPZeG9/T8tXcabZ5Ye6raAokG7OPAu+L7d1cQPcmoswEDBgwYMOAEcFaa04ABAwYMGHAgBuE0YMCAAQPOHS68cBKR14vIJ0TkcRH5vmO2cU1EfkpEflNEPi4iXysi94vIz4nIv43/33dEG28XkSdjRdJ07G2xzV8XkXeLyLXis0djnz8hIn9ozfZeIyK/LCK/JiIfFpGvjsdFRP5ObO/XReR1K9p7RER+IY7vYyLyZ5c+/x9EREXkgXXaFJGJiHxQRD4S2/vr8fjLReQD8bm9MzpIiY75d8b2PiAiL1vRx4PaFBF5q4g8Fvv/PeuOe8D6GObSvTGX7pl5pKoX9gfjTfgk8ApgBHwE+IpjtPNjwH8bfx8B14C/hdHCAHwf8ANHtPF7gNcBHy2O/UGgir//QGoDo/j4CDAGXh7H4Ndo7/8Cvin+/s3A+4vf/xmWb/A1wAdW9O8lwOvi75eBx9KzwsJA3wf8NvDAOm3G45fi7zXwgXjeu4A3xuM/AnxX/P2/A34k/v5G4J0r+nhQm38K+HHAxc8eWnfcw88wl15oc+lemUdn3oG76jx8LfC+4u9HgUc3bOMK8FvE4JDi+CeAlxQv4yfWaOtl5QRY+uy/AN6xqp/xZf7ao9qL531r/P1NwE/E3/8e8KZVfT+krz8L/IH4+08Br8aIQx/YtE1gG/hVLCv8afpFJH8/5Rix/Lqnl5/5IW1+EPjSFedsPO7hZ5hLxXn3/Fy6yPPoopv1VtFmrKRoOQSvAJ4C/pGI/BsR+VER2QFerLE8d/z/obvs63dguxM4fr//HPA2Efk08Lfpi+Bt1F40A7wW+IBYldfPqupHlk47sk0R8SLya8CTGK/bJ4HraqGly9fk9uLnN4AXrejbQpuq+gHgdwLfGs0v/0xEXnmccQ84FMNcOkZ753Uu3Qvz6KILp5Ogzagwlf+HVfW1wG3M9HBiEJE3Y2W+35EOrThtnX5/F/C9qvoI8L3AP9y0PRG5BPw0NjlbjALnLatOPapNVe1U9TVYRvhXA//BIdes1cflNkXkP8RMNlM1+pZ/ALx9kzYHrIVhLm3Y3nmeS/fCPLrowukkaDM+A3wm7izA1PLXAV8QkZcAxP+fPE4HReTbgD+C1dlJX/hx+/1twM/E3/8P7CVeuz0RqbHJ9A5V/RlsJ/Vy4CMi8ql43a+KyMOb9FFVrwPvx+zV10SkWnFNbi9+fhV49qCBFm2+Pl770/GjdwNftcm4B6yFYS5t0N5FmUsXeR5ddOF017QZqvoE8GkRSSy9/xmWVf8e7AUm/v+zm3ZOrADXXwLeoKp7xUfHpZb5HPB74++/D/i3RXt/MkbdfA1wI5lRir4Itjv8uKr+IICq/oaqPqSqL1PVl2Ev6eviMzm0TRF5UGLElIhsAb8fyw7/BeCPxdPK51Y+zz8G/N/FAnNYm78J/JM4XuL4H1t33APWxjCX+vYu9Fy6Z+bRWTu97vYHizR5DLPRvvmYbbwG+DDw69gXeB9mw/0X2Ev7L4D7j2jjJ4HPAw32Yv5p4HHMlvtr8edHivPfHPv8CWLU0BrtfT3wK1h00geA/zieK1ixr08CvwH8Jyva+3pMVf/1oj/fvHTOp+iduIe2ie26/k1s76PAW+LxV2CLw+PYjnQcj0/i34/Hz1+xoo8HtXkN+KexH/8aePW64x5+hrn0QptL98o8GuiLBgwYMGDAucNFN+sNGDBgwIB7EINwGjBgwIAB5w6DcBowYMCAAecOg3AaMGDAgAHnDoNwGjBgwIAB5w6DcDrnEJHds+7DgAEXHcM8ungYhNOAAQMGDDh3GITTBUHM3n6biHxURH5DRL41Hv8GEXm/9DV03hEz2AcMGLCEYR5dHFRHnzLgnOC/xLLvXw08AHxIRH4xfvZa4CsxSpZ/BXwd8C/PopMDBpxzDPPogmDQnC4Ovh74STW24S8A/w/wu+JnH1TVz6hqwKhUXnZGfRww4LxjmEcXBINwujg4zMQwK37vGDTiAQMOwjCPLggG4XRx8ItYoTAvIg9ipafXYV8eMGBAj2EeXRAMO4OLg3djpZo/gjEi/0VVfUJEvvxsuzVgwIXCMI8uCAZW8gEDBgwYcO4wmPUGDBgwYMC5wyCcBgwYMGDAucMgnAYMGDBgwLnDIJwGDBgwYMC5wyCcBgwYMGDAucMgnAYMGDBgwLnDIJwGDBgwYMC5w78HHQrXt8yF6oUAAAAASUVORK5CYII=\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -699,51 +660,59 @@ "name": "stdout", "output_type": "stream", "text": [ - "precip_conv_frac \n", + "precip_total \n", "Dimensions: ()\n", "Coordinates:\n", - " raw_data_start_date datetime64[ns] 1678-01-01\n", - " raw_data_end_date datetime64[ns] 1681-01-01\n", - " subset_start_date datetime64[ns] 1678-01-01\n", - " subset_end_date datetime64[ns] 1680-12-31\n", + " raw_data_start_date object 0004-01-01 00:00:00\n", + " subset_start_date object 0004-01-01 00:00:00\n", + " raw_data_end_date object 0007-01-01 00:00:00\n", + " subset_end_date object 0006-12-31 00:00:00\n", "Data variables:\n", - " globe float64 0.598\n", - " tropics float64 0.808 \n", + " tropics float64 3.51\n", + " globe float64 3.025 \n", "\n", - "precip_total \n", + "precip_conv_frac \n", "Dimensions: ()\n", "Coordinates:\n", - " raw_data_end_date datetime64[ns] 1681-01-01\n", - " subset_start_date datetime64[ns] 1678-01-01\n", - " subset_end_date datetime64[ns] 1680-12-31\n", - " raw_data_start_date datetime64[ns] 1678-01-01\n", + " raw_data_start_date object 0004-01-01 00:00:00\n", + " raw_data_end_date object 0007-01-01 00:00:00\n", + " subset_start_date object 0004-01-01 00:00:00\n", + " subset_end_date object 0006-12-31 00:00:00\n", "Data variables:\n", - " globe float64 3.025\n", - " tropics float64 3.51 \n", + " tropics float64 0.808\n", + " globe float64 0.598 \n", "\n", - "precip_convective \n", + "precip_largescale \n", "Dimensions: ()\n", "Coordinates:\n", - " raw_data_end_date datetime64[ns] 1681-01-01\n", - " subset_start_date datetime64[ns] 1678-01-01\n", - " subset_end_date datetime64[ns] 1680-12-31\n", - " raw_data_start_date datetime64[ns] 1678-01-01\n", + " raw_data_start_date object 0004-01-01 00:00:00\n", + " subset_start_date object 0004-01-01 00:00:00\n", + " raw_data_end_date object 0007-01-01 00:00:00\n", + " subset_end_date object 0006-12-31 00:00:00\n", "Data variables:\n", - " globe float64 2.11\n", - " tropics float64 3.055 \n", + " tropics float64 0.4551\n", + " globe float64 0.9149 \n", "\n", - "precip_largescale \n", + "precip_convective \n", "Dimensions: ()\n", "Coordinates:\n", - " raw_data_end_date datetime64[ns] 1681-01-01\n", - " subset_start_date datetime64[ns] 1678-01-01\n", - " subset_end_date datetime64[ns] 1680-12-31\n", - " raw_data_start_date datetime64[ns] 1678-01-01\n", + " raw_data_start_date object 0004-01-01 00:00:00\n", + " subset_start_date object 0004-01-01 00:00:00\n", + " raw_data_end_date object 0007-01-01 00:00:00\n", + " subset_end_date object 0006-12-31 00:00:00\n", "Data variables:\n", - " globe float64 0.9149\n", - " tropics float64 0.4551 \n", + " tropics float64 3.055\n", + " globe float64 2.11 \n", "\n" ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "//anaconda/envs/aospy_dev/lib/python3.6/site-packages/xarray/core/dataset.py:374: FutureWarning: iteration over an xarray.Dataset will change in xarray v0.11 to only include data variables, not coordinates. Iterate over the Dataset.variables property instead to preserve existing behavior in a forwards compatible manner.\n", + " both_data_and_coords = [k for k in data_vars if k in coords]\n" + ] } ], "source": [ @@ -804,6 +773,13 @@ "import shutil\n", "shutil.rmtree(example_proj.direc_out)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -822,7 +798,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.3" + "version": "3.6.1" }, "latex_envs": { "LaTeX_envs_menu_present": true, diff --git a/aospy/test/data/objects/examples.py b/aospy/test/data/objects/examples.py index 7ddb311..6087f9a 100644 --- a/aospy/test/data/objects/examples.py +++ b/aospy/test/data/objects/examples.py @@ -1,6 +1,7 @@ -from datetime import datetime import os +from cftime import DatetimeNoLeap + from aospy import Proj, Model, Run, Var, Region from aospy.data_loader import NestedDictDataLoader @@ -25,8 +26,8 @@ def total_precipitation(convection_rain, condensation_rain): 'Control simulation of the idealized moist model' ), data_loader=NestedDictDataLoader(file_map), - default_start_date=datetime(4, 1, 1), - default_end_date=datetime(6, 12, 31) + default_start_date=DatetimeNoLeap(4, 1, 1), + default_end_date=DatetimeNoLeap(6, 12, 31) ) example_model = Model( diff --git a/aospy/test/test_calc_basic.py b/aospy/test/test_calc_basic.py index b36e0aa..b36a530 100755 --- a/aospy/test/test_calc_basic.py +++ b/aospy/test/test_calc_basic.py @@ -7,6 +7,8 @@ import pytest import itertools +import cftime +import numpy as np import xarray as xr from aospy import Var @@ -18,19 +20,20 @@ def _test_output_attrs(calc, dtype_out): - with xr.open_dataset(calc.path_out[dtype_out]) as data: - expected_units = calc.var.units - if calc.dtype_out_vert == 'vert_int': - if expected_units != '': - expected_units = ("(vertical integral of {0}):" - " {0} m)").format(expected_units) - else: - expected_units = ("(vertical integral of quantity" - " with unspecified units)") - expected_description = calc.var.description - for name, arr in data.data_vars.items(): - assert expected_units == arr.attrs['units'] - assert expected_description == arr.attrs['description'] + with xr.set_options(enable_cftimeindex=True): + with xr.open_dataset(calc.path_out[dtype_out]) as data: + expected_units = calc.var.units + if calc.dtype_out_vert == 'vert_int': + if expected_units != '': + expected_units = ("(vertical integral of {0}):" + " {0} m)").format(expected_units) + else: + expected_units = ("(vertical integral of quantity" + " with unspecified units)") + expected_description = calc.var.description + for name, arr in data.data_vars.items(): + assert expected_units == arr.attrs['units'] + assert expected_description == arr.attrs['description'] def _test_files_and_attrs(calc, dtype_out): @@ -39,109 +42,122 @@ def _test_files_and_attrs(calc, dtype_out): _test_output_attrs(calc, dtype_out) -_BASIC_TEST_PARAMS = { - 'proj': example_proj, - 'model': example_model, - 'run': example_run, - 'var': condensation_rain, - 'date_range': (datetime.datetime(4, 1, 1), - datetime.datetime(6, 12, 31)), - 'intvl_in': 'monthly', - 'dtype_in_time': 'ts' +_2D_DATE_RANGES = { + 'datetime': (datetime.datetime(4, 1, 1), datetime.datetime(6, 12, 31)), + 'datetime64': (np.datetime64('0004-01-01'), np.datetime64('0006-12-31')), + 'cftime': (cftime.DatetimeNoLeap(4, 1, 1), + cftime.DatetimeNoLeap(6, 12, 31)), + 'str': ('0004', '0006') } +_3D_DATE_RANGES = { + 'datetime': (datetime.datetime(6, 1, 1), datetime.datetime(6, 1, 31)), + 'datetime64': (np.datetime64('0006-01-01'), np.datetime64('0006-01-31')), + 'cftime': (cftime.DatetimeNoLeap(6, 1, 1), + cftime.DatetimeNoLeap(6, 1, 31)) + # 'str': ('0006', '0006') TODO: This should work once xarray version + # 0.10.5 is released +} +_2D_VARS = {'basic': condensation_rain, 'composite': precip} +_2D_DTYPE_OUT_VERT = {'None': None} +_2D_DTYPE_IN_VERT = {'None': None} +_3D_VARS = {'3D': sphum} +_3D_DTYPE_OUT_VERT = {'vert_int': 'vert_int'} +_3D_DTYPE_IN_VERT = {'sigma': 'sigma'} +_CASES = ( + list(itertools.product(_2D_DATE_RANGES.items(), _2D_VARS.items(), + _2D_DTYPE_IN_VERT.items(), + _2D_DTYPE_OUT_VERT.items())) + + list(itertools.product(_3D_DATE_RANGES.items(), _3D_VARS.items(), + _3D_DTYPE_IN_VERT.items(), + _3D_DTYPE_OUT_VERT.items())) +) +_CALC_TESTS = {} +for ((date_type, date_range), (test_type, var), + (vert_in_label, vert_in), (vert_out_label, vert_out)) in _CASES: + _CALC_TESTS['{}-{}-{}-{}'.format( + date_type, test_type, vert_in_label, vert_out_label)] = ( + date_range, var, vert_in, vert_out) + + +@pytest.fixture(params=_CALC_TESTS.values(), ids=list(_CALC_TESTS.keys())) +def test_params(request): + date_range, var, vert_in, vert_out = request.param + yield { + 'proj': example_proj, + 'model': example_model, + 'run': example_run, + 'var': var, + 'date_range': date_range, + 'intvl_in': 'monthly', + 'dtype_in_time': 'ts', + 'dtype_in_vert': vert_in, + 'dtype_out_vert': vert_out + } + for direc in [example_proj.direc_out, example_proj.tar_direc_out]: + try: + shutil.rmtree(direc) + except OSError: + pass + + +def test_annual_mean(test_params): + calc = Calc(intvl_out='ann', dtype_out_time='av', **test_params) + calc.compute() + _test_files_and_attrs(calc, 'av') + + +def test_annual_ts(test_params): + calc = Calc(intvl_out='ann', dtype_out_time='ts', **test_params) + calc.compute() + _test_files_and_attrs(calc, 'ts') + + +def test_seasonal_mean(test_params): + calc = Calc(intvl_out='djf', dtype_out_time='av', **test_params) + calc.compute() + _test_files_and_attrs(calc, 'av') + + +def test_seasonal_ts(test_params): + calc = Calc(intvl_out='djf', dtype_out_time='ts', **test_params) + calc.compute() + _test_files_and_attrs(calc, 'ts') + + +def test_monthly_mean(test_params): + calc = Calc(intvl_out=1, dtype_out_time='av', **test_params) + calc.compute() + _test_files_and_attrs(calc, 'av') + + +def test_monthly_ts(test_params): + calc = Calc(intvl_out=1, dtype_out_time='ts', **test_params) + calc.compute() + _test_files_and_attrs(calc, 'ts') + + +def test_simple_reg_av(test_params): + calc = Calc(intvl_out='ann', dtype_out_time='reg.av', region=[globe], + **test_params) + calc.compute() + _test_files_and_attrs(calc, 'reg.av') + + +def test_simple_reg_ts(test_params): + calc = Calc(intvl_out='ann', dtype_out_time='reg.ts', region=[globe], + **test_params) + calc.compute() + _test_files_and_attrs(calc, 'reg.ts') + + +def test_complex_reg_av(test_params): + calc = Calc(intvl_out='ann', dtype_out_time='reg.av', region=[sahel], + **test_params) + calc.compute() + _test_files_and_attrs(calc, 'reg.av') -class TestCalcBasic(unittest.TestCase): - def setUp(self): - self.test_params = _BASIC_TEST_PARAMS.copy() - - def tearDown(self): - for direc in [example_proj.direc_out, example_proj.tar_direc_out]: - try: - shutil.rmtree(direc) - except OSError: - pass - - def test_annual_mean(self): - calc = Calc(intvl_out='ann', dtype_out_time='av', **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'av') - - def test_annual_ts(self): - calc = Calc(intvl_out='ann', dtype_out_time='ts', **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'ts') - - def test_seasonal_mean(self): - calc = Calc(intvl_out='djf', dtype_out_time='av', **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'av') - - def test_seasonal_ts(self): - calc = Calc(intvl_out='djf', dtype_out_time='ts', **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'ts') - - def test_monthly_mean(self): - calc = Calc(intvl_out=1, dtype_out_time='av', **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'av') - - def test_monthly_ts(self): - calc = Calc(intvl_out=1, dtype_out_time='ts', **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'ts') - - def test_simple_reg_av(self): - calc = Calc(intvl_out='ann', dtype_out_time='reg.av', region=[globe], - **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'reg.av') - - def test_simple_reg_ts(self): - calc = Calc(intvl_out='ann', dtype_out_time='reg.ts', region=[globe], - **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'reg.ts') - - def test_complex_reg_av(self): - calc = Calc(intvl_out='ann', dtype_out_time='reg.av', region=[sahel], - **self.test_params) - calc.compute() - _test_files_and_attrs(calc, 'reg.av') - - -class TestCalcComposite(TestCalcBasic): - def setUp(self): - self.test_params = { - 'proj': example_proj, - 'model': example_model, - 'run': example_run, - 'var': precip, - 'date_range': (datetime.datetime(4, 1, 1), - datetime.datetime(6, 12, 31)), - 'intvl_in': 'monthly', - 'dtype_in_time': 'ts' - } - - -class TestCalc3D(TestCalcBasic): - def setUp(self): - self.test_params = { - 'proj': example_proj, - 'model': example_model, - 'run': example_run, - 'var': sphum, - 'date_range': (datetime.datetime(6, 1, 1), - datetime.datetime(6, 1, 31)), - 'intvl_in': 'monthly', - 'dtype_in_time': 'ts', - 'dtype_in_vert': 'sigma', - 'dtype_out_vert': 'vert_int' - } - - -test_params = { +test_params_not_time_defined = { 'proj': example_proj, 'model': example_model, 'run': example_run, @@ -155,8 +171,8 @@ def setUp(self): @pytest.mark.parametrize('dtype_out_time', [None, []]) def test_calc_object_no_time_options(dtype_out_time): - test_params['dtype_out_time'] = dtype_out_time - calc = Calc(**test_params) + test_params_not_time_defined['dtype_out_time'] = dtype_out_time + calc = Calc(**test_params_not_time_defined) if isinstance(dtype_out_time, list): assert calc.dtype_out_time == tuple(dtype_out_time) else: @@ -167,9 +183,9 @@ def test_calc_object_no_time_options(dtype_out_time): 'dtype_out_time', ['av', 'std', 'ts', 'reg.av', 'reg.std', 'reg.ts']) def test_calc_object_string_time_options(dtype_out_time): - test_params['dtype_out_time'] = dtype_out_time + test_params_not_time_defined['dtype_out_time'] = dtype_out_time with pytest.raises(ValueError): - Calc(**test_params) + Calc(**test_params_not_time_defined) def test_calc_object_time_options(): @@ -177,9 +193,9 @@ def test_calc_object_time_options(): for i in range(1, len(time_options) + 1): for time_option in list(itertools.permutations(time_options, i)): if time_option != ('None',): - test_params['dtype_out_time'] = time_option + test_params_not_time_defined['dtype_out_time'] = time_option with pytest.raises(ValueError): - Calc(**test_params) + Calc(**test_params_not_time_defined) @pytest.mark.parametrize( @@ -216,7 +232,16 @@ def test_attrs(units, description, dtype_out_vert, expected_units, @pytest.fixture() def recursive_test_params(): - basic_params = _BASIC_TEST_PARAMS.copy() + basic_params = { + 'proj': example_proj, + 'model': example_model, + 'run': example_run, + 'var': condensation_rain, + 'date_range': (datetime.datetime(4, 1, 1), + datetime.datetime(6, 12, 31)), + 'intvl_in': 'monthly', + 'dtype_in_time': 'ts' + } recursive_params = basic_params.copy() recursive_condensation_rain = Var( @@ -236,14 +261,16 @@ def test_recursive_calculation(recursive_test_params): calc = Calc(intvl_out='ann', dtype_out_time='av', **basic_params) calc = calc.compute() - expected = xr.open_dataset( - calc.path_out['av'], autoclose=True)['condensation_rain'] + with xr.set_options(enable_cftimeindex=True): + expected = xr.open_dataset( + calc.path_out['av'], autoclose=True)['condensation_rain'] _test_files_and_attrs(calc, 'av') calc = Calc(intvl_out='ann', dtype_out_time='av', **recursive_params) calc = calc.compute() - result = xr.open_dataset( - calc.path_out['av'], autoclose=True)['recursive_condensation_rain'] + with xr.set_options(enable_cftimeindex=True): + result = xr.open_dataset( + calc.path_out['av'], autoclose=True)['recursive_condensation_rain'] _test_files_and_attrs(calc, 'av') xr.testing.assert_equal(expected, result) diff --git a/aospy/test/test_data_loader.py b/aospy/test/test_data_loader.py index 852ea7c..38133e5 100644 --- a/aospy/test/test_data_loader.py +++ b/aospy/test/test_data_loader.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """Test suite for aospy.data_loader module.""" -from datetime import datetime +import datetime import os import unittest import warnings @@ -9,6 +9,8 @@ import pytest import xarray as xr +from cftime import DatetimeNoLeap + from aospy import Var from aospy.data_loader import (DataLoader, DictDataLoader, GFDLDataLoader, NestedDictDataLoader, grid_attrs_to_aospy_names, @@ -21,7 +23,7 @@ TIME_WEIGHTS_STR, GRID_ATTRS, ZSURF_STR) from aospy.utils import io from .data.objects.examples import (condensation_rain, convection_rain, precip, - example_run, ROOT_PATH) + file_map, ROOT_PATH) def _open_ds_catch_warnings(path): @@ -39,612 +41,751 @@ def test_maybe_cast_to_float64(input_dtype, expected_dtype): assert result == expected_dtype -class AospyDataLoaderTestCase(unittest.TestCase): - def setUp(self): - self.DataLoader = DataLoader() - self.generate_file_set_args = dict( - var=condensation_rain, start_date=datetime(2000, 1, 1), - end_date=datetime(2002, 12, 31), domain='atmos', - intvl_in='monthly', dtype_in_vert='sigma', dtype_in_time='ts', - intvl_out=None) - time_bounds = np.array([[0, 31], [31, 59], [59, 90]]) - bounds = np.array([0, 1]) - time = np.array([15, 46, 74]) - data = np.zeros((3, 1, 1)) - lat = [0] - lon = [0] - self.ALT_LAT_STR = 'LATITUDE' - self.var_name = 'a' - ds = xr.DataArray(data, - coords=[time, lat, lon], - dims=[TIME_STR, self.ALT_LAT_STR, LON_STR], - name=self.var_name).to_dataset() - ds[TIME_BOUNDS_STR] = xr.DataArray(time_bounds, - coords=[time, bounds], - dims=[TIME_STR, BOUNDS_STR], - name=TIME_BOUNDS_STR) - units_str = 'days since 2000-01-01 00:00:00' - ds[TIME_STR].attrs['units'] = units_str - ds[TIME_BOUNDS_STR].attrs['units'] = units_str - self.ds = ds - - inst_time = np.array([3, 6, 9]) - inst_units_str = 'hours since 2000-01-01 00:00:00' - inst_ds = ds.copy() - inst_ds.drop(TIME_BOUNDS_STR) - inst_ds[TIME_STR].values = inst_time - inst_ds[TIME_STR].attrs['units'] = inst_units_str - self.inst_ds = inst_ds - - def tearDown(self): - pass - - -class TestDataLoader(AospyDataLoaderTestCase): - def test_rename_grid_attrs_ds(self): - assert LAT_STR not in self.ds - assert self.ALT_LAT_STR in self.ds - ds = grid_attrs_to_aospy_names(self.ds) - assert LAT_STR in ds - - def test_rename_grid_attrs_dim_no_coord(self): - bounds_dim = 'nv' - assert bounds_dim not in self.ds - assert bounds_dim in GRID_ATTRS[BOUNDS_STR] - # Create DataArray with all dims lacking coords - values = self.ds[self.var_name].values - arr = xr.DataArray(values, name='dummy') - # Insert name to be replaced (its physical meaning doesn't matter here) - ds = arr.rename({'dim_0': bounds_dim}).to_dataset() - assert not ds[bounds_dim].coords - result = grid_attrs_to_aospy_names(ds) - assert not result[BOUNDS_STR].coords - - def test_rename_grid_attrs_skip_scalar_dim(self): - phalf_dim = 'phalf' - assert phalf_dim not in self.ds - assert phalf_dim in GRID_ATTRS[PHALF_STR] - ds = self.ds.copy() - ds[phalf_dim] = 4 - ds = ds.set_coords(phalf_dim) - result = grid_attrs_to_aospy_names(ds) - xr.testing.assert_identical(result[phalf_dim], ds[phalf_dim]) - - def test_rename_grid_attrs_copy_attrs(self): - orig_attrs = {'dummy_key': 'dummy_val'} - ds_orig = self.ds.copy() - ds_orig[self.ALT_LAT_STR].attrs = orig_attrs - ds = grid_attrs_to_aospy_names(ds_orig) - self.assertEqual(ds[LAT_STR].attrs, orig_attrs) - - def test_set_grid_attrs_as_coords(self): - ds = grid_attrs_to_aospy_names(self.ds) - sfc_area = ds[self.var_name].isel(**{TIME_STR: 0}).drop(TIME_STR) - ds[SFC_AREA_STR] = sfc_area - - assert SFC_AREA_STR not in ds.coords - - ds = set_grid_attrs_as_coords(ds) - assert SFC_AREA_STR in ds.coords - assert TIME_BOUNDS_STR in ds.coords - - def test_sel_var(self): - time = np.array([0, 31, 59]) + 15 - data = np.zeros((3)) - ds = xr.DataArray(data, - coords=[time], - dims=[TIME_STR], - name=convection_rain.name).to_dataset() - condensation_rain_alt_name, = condensation_rain.alt_names - ds[condensation_rain_alt_name] = xr.DataArray(data, coords=[ds.time]) - result = _sel_var(ds, convection_rain) - self.assertEqual(result.name, convection_rain.name) - - result = _sel_var(ds, condensation_rain) - self.assertEqual(result.name, condensation_rain.name) - - with self.assertRaises(LookupError): - _sel_var(ds, precip) - - def test_maybe_apply_time_shift(self): - ds = xr.decode_cf(self.ds) - da = ds[self.var_name] - - result = self.DataLoader._maybe_apply_time_shift( - da.copy(), **self.generate_file_set_args)[TIME_STR] - assert result.identical(da[TIME_STR]) - - offset = self.DataLoader._maybe_apply_time_shift( - da.copy(), {'days': 1}, **self.generate_file_set_args) - result = offset[TIME_STR] - - expected = da[TIME_STR] + np.timedelta64(1, 'D') - expected[TIME_STR] = expected - - assert result.identical(expected) - - def test_generate_file_set(self): - with self.assertRaises(NotImplementedError): - self.DataLoader._generate_file_set() - - def test_prep_time_data(self): - assert (TIME_WEIGHTS_STR not in self.inst_ds) - ds, min_year, max_year = _prep_time_data(self.inst_ds) - assert (TIME_WEIGHTS_STR in ds) - self.assertEqual(min_year, 2000) - self.assertEqual(max_year, 2000) - - def test_preprocess_and_rename_grid_attrs(self): - def preprocess_func(ds, **kwargs): - # Corrupt a grid attribute name so that we test - # that grid_attrs_to_aospy_names is still called - # after - ds = ds.rename({LON_STR: 'LONGITUDE'}) - ds.attrs['a'] = 'b' - return ds - - assert LAT_STR not in self.ds - assert self.ALT_LAT_STR in self.ds - assert LON_STR in self.ds - - expected = self.ds.rename({self.ALT_LAT_STR: LAT_STR}) - expected = expected.set_coords(TIME_BOUNDS_STR) - expected.attrs['a'] = 'b' - result = _preprocess_and_rename_grid_attrs(preprocess_func)(self.ds) - xr.testing.assert_identical(result, expected) - - -class TestDictDataLoader(TestDataLoader): - def setUp(self): - super(TestDictDataLoader, self).setUp() - file_map = {'monthly': ['a.nc']} - self.DataLoader = DictDataLoader(file_map) - - def test_generate_file_set(self): - result = self.DataLoader._generate_file_set( - **self.generate_file_set_args) - expected = ['a.nc'] - self.assertEqual(result, expected) +_DATE_RANGES = { + 'datetime': (datetime.datetime(2000, 1, 1), + datetime.datetime(2002, 12, 31)), + 'datetime64': (np.datetime64('2000-01-01'), + np.datetime64('2002-12-31')), + 'cftime': (DatetimeNoLeap(2000, 1, 1), + DatetimeNoLeap(2002, 12, 31)), + 'str': ('2000', '2002') +} + + +@pytest.fixture(params=_DATE_RANGES.values(), ids=list(_DATE_RANGES.keys())) +def generate_file_set_args(request): + start_date, end_date = request.param + return dict( + var=condensation_rain, start_date=start_date, end_date=end_date, + domain='atmos', intvl_in='monthly', dtype_in_vert='sigma', + dtype_in_time='ts', intvl_out=None) + + +@pytest.fixture() +def alt_lat_str(): + return 'LATITUDE' + + +@pytest.fixture() +def var_name(): + return 'a' + + +@pytest.fixture() +def ds(alt_lat_str, var_name): + time_bounds = np.array([[0, 31], [31, 59], [59, 90]]) + bounds = np.array([0, 1]) + time = np.array([15, 46, 74]) + data = np.zeros((3, 1, 1)) + lat = [0] + lon = [0] + ds = xr.DataArray(data, + coords=[time, lat, lon], + dims=[TIME_STR, alt_lat_str, LON_STR], + name=var_name).to_dataset() + ds[TIME_BOUNDS_STR] = xr.DataArray(time_bounds, + coords=[time, bounds], + dims=[TIME_STR, BOUNDS_STR], + name=TIME_BOUNDS_STR) + units_str = 'days since 2000-01-01 00:00:00' + ds[TIME_STR].attrs['units'] = units_str + ds[TIME_BOUNDS_STR].attrs['units'] = units_str + return ds + + +@pytest.fixture() +def inst_ds(ds): + inst_time = np.array([3, 6, 9]) + inst_units_str = 'hours since 2000-01-01 00:00:00' + inst_ds = ds.copy() + inst_ds.drop(TIME_BOUNDS_STR) + inst_ds[TIME_STR].values = inst_time + inst_ds[TIME_STR].attrs['units'] = inst_units_str + return inst_ds + + +def _gfdl_data_loader_kwargs(data_start_date, data_end_date): + return dict(data_direc=os.path.join('.', 'test'), + data_dur=6, + data_start_date=data_start_date, + data_end_date=data_end_date, + upcast_float32=False, + data_vars='minimal', + coords='minimal') + + +_DATA_LOADER_KWARGS = { + 'DataLoader': (DataLoader, {}), + 'DictDataLoader': (DictDataLoader, dict(file_map={'monthly': ['a.nc']})), + 'NestedDictDataLoader': ( + NestedDictDataLoader, + dict(file_map={'monthly': {'condensation_rain': ['a.nc']}})), + 'GFDLDataLoader-datetime': ( + GFDLDataLoader, _gfdl_data_loader_kwargs( + datetime.datetime(2000, 1, 1), datetime.datetime(2012, 12, 31))), + 'GFDLDataLoader-datetime64': ( + GFDLDataLoader, _gfdl_data_loader_kwargs( + np.datetime64('2000-01-01'), np.datetime64('2012-12-31'))), + 'GFDLDataLoader-cftime': ( + GFDLDataLoader, _gfdl_data_loader_kwargs( + DatetimeNoLeap(2000, 1, 1), DatetimeNoLeap(2012, 12, 31))), + 'GFDLDataLoader-str': ( + GFDLDataLoader, _gfdl_data_loader_kwargs('2000', '2012')) +} + + +@pytest.fixture(params=_DATA_LOADER_KWARGS.values(), + ids=list(_DATA_LOADER_KWARGS.keys())) +def data_loader(request): + data_loader_type, kwargs = request.param + return data_loader_type(**kwargs) + + +_GFDL_DATA_LOADER_KWARGS = {key: _DATA_LOADER_KWARGS[key] for key in + _DATA_LOADER_KWARGS if 'GFDL' in key} + + +@pytest.fixture(params=_GFDL_DATA_LOADER_KWARGS.values(), + ids=list(_GFDL_DATA_LOADER_KWARGS.keys())) +def gfdl_data_loader(request): + data_loader_type, kwargs = request.param + return data_loader_type(**kwargs) + + +def test_rename_grid_attrs_ds(ds, alt_lat_str): + assert LAT_STR not in ds + assert alt_lat_str in ds + ds = grid_attrs_to_aospy_names(ds) + assert LAT_STR in ds + + +def test_rename_grid_attrs_dim_no_coord(ds, var_name): + bounds_dim = 'nv' + assert bounds_dim not in ds + assert bounds_dim in GRID_ATTRS[BOUNDS_STR] + # Create DataArray with all dims lacking coords + values = ds[var_name].values + arr = xr.DataArray(values, name='dummy') + # Insert name to be replaced (its physical meaning doesn't matter here) + ds = arr.rename({'dim_0': bounds_dim}).to_dataset() + assert not ds[bounds_dim].coords + result = grid_attrs_to_aospy_names(ds) + assert not result[BOUNDS_STR].coords + + +def test_rename_grid_attrs_skip_scalar_dim(ds): + phalf_dim = 'phalf' + assert phalf_dim not in ds + assert phalf_dim in GRID_ATTRS[PHALF_STR] + ds_copy = ds.copy() + ds_copy[phalf_dim] = 4 + ds_copy = ds_copy.set_coords(phalf_dim) + result = grid_attrs_to_aospy_names(ds_copy) + xr.testing.assert_identical(result[phalf_dim], ds_copy[phalf_dim]) + + +def test_rename_grid_attrs_copy_attrs(ds, alt_lat_str): + orig_attrs = {'dummy_key': 'dummy_val'} + ds_orig = ds.copy() + ds_orig[alt_lat_str].attrs = orig_attrs + ds = grid_attrs_to_aospy_names(ds_orig) + assert ds[LAT_STR].attrs == orig_attrs + + +def test_set_grid_attrs_as_coords(ds, var_name): + ds = grid_attrs_to_aospy_names(ds) + sfc_area = ds[var_name].isel(**{TIME_STR: 0}).drop(TIME_STR) + ds[SFC_AREA_STR] = sfc_area + + assert SFC_AREA_STR not in ds.coords + + ds = set_grid_attrs_as_coords(ds) + assert SFC_AREA_STR in ds.coords + assert TIME_BOUNDS_STR in ds.coords + + +def test_sel_var(): + time = np.array([0, 31, 59]) + 15 + data = np.zeros((3)) + ds = xr.DataArray(data, + coords=[time], + dims=[TIME_STR], + name=convection_rain.name).to_dataset() + condensation_rain_alt_name, = condensation_rain.alt_names + ds[condensation_rain_alt_name] = xr.DataArray(data, coords=[ds.time]) + result = _sel_var(ds, convection_rain) + assert result.name == convection_rain.name + + result = _sel_var(ds, condensation_rain) + assert result.name == condensation_rain.name + + with pytest.raises(LookupError): + _sel_var(ds, precip) + + +def test_maybe_apply_time_shift(data_loader, ds, inst_ds, var_name, + generate_file_set_args): + ds = xr.decode_cf(ds) + da = ds[var_name] + + result = data_loader._maybe_apply_time_shift( + da.copy(), **generate_file_set_args)[TIME_STR] + assert result.identical(da[TIME_STR]) + + offset = data_loader._maybe_apply_time_shift( + da.copy(), {'days': 1}, **generate_file_set_args) + result = offset[TIME_STR] + + expected = da[TIME_STR] + np.timedelta64(1, 'D') + expected[TIME_STR] = expected + + assert result.identical(expected) + + +def test_maybe_apply_time_shift_ts(gfdl_data_loader, ds, var_name, + generate_file_set_args): + ds = xr.decode_cf(ds) + da = ds[var_name] + result = gfdl_data_loader._maybe_apply_time_shift( + da.copy(), **generate_file_set_args)[TIME_STR] + assert result.identical(da[TIME_STR]) + + +def test_maybe_apply_time_shift_inst(gfdl_data_loader, inst_ds, var_name, + generate_file_set_args): + inst_ds = xr.decode_cf(inst_ds) + generate_file_set_args['dtype_in_time'] = 'inst' + generate_file_set_args['intvl_in'] = '3hr' + da = inst_ds[var_name] + result = gfdl_data_loader._maybe_apply_time_shift( + da.copy(), **generate_file_set_args)[TIME_STR] + + expected = da[TIME_STR] + np.timedelta64(-3, 'h') + expected[TIME_STR] = expected + assert result.identical(expected) + + generate_file_set_args['intvl_in'] = 'daily' + da = inst_ds[var_name] + result = gfdl_data_loader._maybe_apply_time_shift( + da.copy(), **generate_file_set_args)[TIME_STR] + + expected = da[TIME_STR] + expected[TIME_STR] = expected + assert result.identical(expected) + + +def test_prep_time_data(inst_ds): + assert (TIME_WEIGHTS_STR not in inst_ds) + ds = _prep_time_data(inst_ds) + assert (TIME_WEIGHTS_STR in ds) - with self.assertRaises(KeyError): - self.generate_file_set_args['intvl_in'] = 'daily' - result = self.DataLoader._generate_file_set( - **self.generate_file_set_args) +def test_preprocess_and_rename_grid_attrs(ds, alt_lat_str): + def preprocess_func(ds, **kwargs): + # Corrupt a grid attribute name so that we test + # that grid_attrs_to_aospy_names is still called + # after + ds = ds.rename({LON_STR: 'LONGITUDE'}) + ds.attrs['a'] = 'b' + return ds + + assert LAT_STR not in ds + assert alt_lat_str in ds + assert LON_STR in ds + + expected = ds.rename({alt_lat_str: LAT_STR}) + expected = expected.set_coords(TIME_BOUNDS_STR) + expected.attrs['a'] = 'b' + result = _preprocess_and_rename_grid_attrs(preprocess_func)(ds) + xr.testing.assert_identical(result, expected) + + +def test_generate_file_set(data_loader, generate_file_set_args): + if type(data_loader) is DataLoader: + with pytest.raises(NotImplementedError): + data_loader._generate_file_set() + + elif isinstance(data_loader, DictDataLoader): + result = data_loader._generate_file_set( + **generate_file_set_args) + expected = ['a.nc'] + result == expected -class TestNestedDictDataLoader(TestDataLoader): - def setUp(self): - super(TestNestedDictDataLoader, self).setUp() - file_map = {'monthly': {'condensation_rain': ['a.nc']}} - self.DataLoader = NestedDictDataLoader(file_map) + with pytest.raises(KeyError): + generate_file_set_args['intvl_in'] = 'daily' + result = data_loader._generate_file_set( + **generate_file_set_args) - def test_generate_file_set(self): - result = self.DataLoader._generate_file_set( - **self.generate_file_set_args) + elif isinstance(data_loader, NestedDictDataLoader): + result = data_loader._generate_file_set( + **generate_file_set_args) expected = ['a.nc'] - self.assertEqual(result, expected) - - with self.assertRaises(KeyError): - self.generate_file_set_args['var'] = convection_rain - result = self.DataLoader._generate_file_set( - **self.generate_file_set_args) - - -class TestGFDLDataLoader(TestDataLoader): - def setUp(self): - super(TestGFDLDataLoader, self).setUp() - self.DataLoader = GFDLDataLoader( - data_direc=os.path.join('.', 'test'), - data_dur=6, - data_start_date=datetime(2000, 1, 1), - data_end_date=datetime(2012, 12, 31), - upcast_float32=False, - data_vars='minimal', - coords='minimal' - ) - - def test_overriding_constructor(self): - new = GFDLDataLoader(self.DataLoader, - data_direc=os.path.join('.', 'a')) - self.assertEqual(new.data_direc, os.path.join('.', 'a')) - self.assertEqual(new.data_dur, self.DataLoader.data_dur) - self.assertEqual(new.data_start_date, self.DataLoader.data_start_date) - self.assertEqual(new.data_end_date, self.DataLoader.data_end_date) - self.assertEqual(new.preprocess_func, - self.DataLoader.preprocess_func) - self.assertEqual(new.upcast_float32, self.DataLoader.upcast_float32) - - new = GFDLDataLoader(self.DataLoader, data_dur=8) - self.assertEqual(new.data_dur, 8) - - new = GFDLDataLoader(self.DataLoader, - data_start_date=datetime(2001, 1, 1)) - self.assertEqual(new.data_start_date, datetime(2001, 1, 1)) - - new = GFDLDataLoader(self.DataLoader, - data_end_date=datetime(2003, 12, 31)) - self.assertEqual(new.data_end_date, datetime(2003, 12, 31)) - - new = GFDLDataLoader(self.DataLoader, - preprocess_func=lambda ds: ds) - xr.testing.assert_identical(new.preprocess_func(self.ds), self.ds) - - new = GFDLDataLoader(self.DataLoader, upcast_float32=True) - self.assertEqual(new.upcast_float32, True) - - new = GFDLDataLoader(self.DataLoader, data_vars='all') - self.assertEqual(new.data_vars, 'all') - - new = GFDLDataLoader(self.DataLoader, coords='all') - self.assertEqual(new.coords, 'all') - - def test_maybe_apply_time_offset_inst(self): - inst_ds = xr.decode_cf(self.inst_ds) - self.generate_file_set_args['dtype_in_time'] = 'inst' - self.generate_file_set_args['intvl_in'] = '3hr' - da = inst_ds[self.var_name] - result = self.DataLoader._maybe_apply_time_shift( - da.copy(), **self.generate_file_set_args)[TIME_STR] - - expected = da[TIME_STR] + np.timedelta64(-3, 'h') - expected[TIME_STR] = expected - assert result.identical(expected) - - self.generate_file_set_args['intvl_in'] = 'daily' - da = inst_ds[self.var_name] - result = self.DataLoader._maybe_apply_time_shift( - da.copy(), **self.generate_file_set_args)[TIME_STR] - - expected = da[TIME_STR] - expected[TIME_STR] = expected - assert result.identical(expected) - - def test_maybe_apply_time_offset_ts(self): - ds = xr.decode_cf(self.ds) - da = ds[self.var_name] - - result = self.DataLoader._maybe_apply_time_shift( - da.copy(), **self.generate_file_set_args)[TIME_STR] - assert result.identical(da[TIME_STR]) - - def test_generate_file_set(self): - with self.assertRaises(IOError): - self.DataLoader._generate_file_set(**self.generate_file_set_args) - - def test_input_data_paths_gfdl(self): - expected = [os.path.join('.', 'test', 'atmos', 'ts', 'monthly', '6yr', - 'atmos.200601-201112.temp.nc')] - result = self.DataLoader._input_data_paths_gfdl( - 'temp', datetime(2010, 1, 1), datetime(2010, 12, 31), 'atmos', - 'monthly', 'pressure', 'ts', None) - self.assertEqual(result, expected) - - expected = [os.path.join('.', 'test', 'atmos_daily', 'ts', 'daily', - '6yr', - 'atmos_daily.20060101-20111231.temp.nc')] - result = self.DataLoader._input_data_paths_gfdl( - 'temp', datetime(2010, 1, 1), datetime(2010, 12, 31), 'atmos', - 'daily', 'pressure', 'ts', None) - self.assertEqual(result, expected) - - expected = [os.path.join('.', 'test', 'atmos_level', 'ts', 'monthly', - '6yr', 'atmos_level.200601-201112.temp.nc')] - result = self.DataLoader._input_data_paths_gfdl( - 'temp', datetime(2010, 1, 1), datetime(2010, 12, 31), 'atmos', - 'monthly', ETA_STR, 'ts', None) - self.assertEqual(result, expected) - - expected = [os.path.join('.', 'test', 'atmos', 'ts', 'monthly', - '6yr', 'atmos.200601-201112.ps.nc')] - result = self.DataLoader._input_data_paths_gfdl( - 'ps', datetime(2010, 1, 1), datetime(2010, 12, 31), 'atmos', - 'monthly', ETA_STR, 'ts', None) - self.assertEqual(result, expected) - - expected = [os.path.join('.', 'test', 'atmos_inst', 'ts', 'monthly', - '6yr', 'atmos_inst.200601-201112.temp.nc')] - result = self.DataLoader._input_data_paths_gfdl( - 'temp', datetime(2010, 1, 1), datetime(2010, 12, 31), 'atmos', - 'monthly', 'pressure', 'inst', None) - self.assertEqual(result, expected) - - expected = [os.path.join('.', 'test', 'atmos', 'av', 'monthly_6yr', - 'atmos.2006-2011.jja.nc')] - result = self.DataLoader._input_data_paths_gfdl( - 'temp', datetime(2010, 1, 1), datetime(2010, 12, 31), 'atmos', - 'monthly', 'pressure', 'av', 'jja') - self.assertEqual(result, expected) - - def test_data_name_gfdl_annual(self): - for data_type in ['ts', 'inst']: - expected = 'atmos.2010.temp.nc' - result = io.data_name_gfdl('temp', 'atmos', data_type, - 'annual', 2010, None, 2000, 1) - self.assertEqual(result, expected) - - expected = 'atmos.2006-2011.temp.nc' - result = io.data_name_gfdl('temp', 'atmos', data_type, - 'annual', 2010, None, 2000, 6) - self.assertEqual(result, expected) - - for intvl_type in ['annual', 'ann']: - expected = 'atmos.2010.ann.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av', - intvl_type, 2010, None, 2000, 1) - self.assertEqual(result, expected) - - expected = 'atmos.2006-2011.ann.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av', - intvl_type, 2010, None, 2000, 6) - self.assertEqual(result, expected) - - expected = 'atmos.2006-2011.01-12.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av_ts', + assert result == expected + + with pytest.raises(KeyError): + generate_file_set_args['var'] = convection_rain + result = data_loader._generate_file_set( + **generate_file_set_args) + + else: + with pytest.raises(IOError): + data_loader._generate_file_set(**generate_file_set_args) + + +def test_overriding_constructor(gfdl_data_loader, ds): + new = GFDLDataLoader(gfdl_data_loader, + data_direc=os.path.join('.', 'a')) + assert new.data_direc == os.path.join('.', 'a') + assert new.data_dur == gfdl_data_loader.data_dur + assert new.data_start_date == gfdl_data_loader.data_start_date + assert new.data_end_date == gfdl_data_loader.data_end_date + assert new.preprocess_func == gfdl_data_loader.preprocess_func + assert new.upcast_float32 == gfdl_data_loader.upcast_float32 + + new = GFDLDataLoader(gfdl_data_loader, data_dur=8) + assert new.data_dur == 8 + + new = GFDLDataLoader(gfdl_data_loader, + data_start_date=datetime.datetime(2001, 1, 1)) + assert new.data_start_date == datetime.datetime(2001, 1, 1) + + new = GFDLDataLoader(gfdl_data_loader, + data_end_date=datetime.datetime(2003, 12, 31)) + assert new.data_end_date == datetime.datetime(2003, 12, 31) + + new = GFDLDataLoader(gfdl_data_loader, preprocess_func=lambda ds: ds) + xr.testing.assert_identical(new.preprocess_func(ds), ds) + + new = GFDLDataLoader(gfdl_data_loader, upcast_float32=True) + assert new.upcast_float32 + + new = GFDLDataLoader(gfdl_data_loader, data_vars='all') + assert new.data_vars == 'all' + + new = GFDLDataLoader(gfdl_data_loader, coords='all') + assert new.coords == 'all' + + +_GFDL_DATE_RANGES = { + 'datetime': (datetime.datetime(2010, 1, 1), + datetime.datetime(2010, 12, 31)), + 'datetime64': (np.datetime64('2010-01-01'), + np.datetime64('2010-12-31')), + 'cftime': (DatetimeNoLeap(2010, 1, 1), + DatetimeNoLeap(2010, 12, 31)), + 'str': ('2010', '2010') +} + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _GFDL_DATE_RANGES.values(), + ids=list(_GFDL_DATE_RANGES.keys())) +def test_input_data_paths_gfdl(gfdl_data_loader, start_date, end_date): + expected = [os.path.join('.', 'test', 'atmos', 'ts', 'monthly', '6yr', + 'atmos.200601-201112.temp.nc')] + result = gfdl_data_loader._input_data_paths_gfdl( + 'temp', start_date, end_date, 'atmos', + 'monthly', 'pressure', 'ts', None) + assert result == expected + + expected = [os.path.join('.', 'test', 'atmos_daily', 'ts', 'daily', + '6yr', + 'atmos_daily.20060101-20111231.temp.nc')] + result = gfdl_data_loader._input_data_paths_gfdl( + 'temp', start_date, end_date, 'atmos', + 'daily', 'pressure', 'ts', None) + assert result == expected + + expected = [os.path.join('.', 'test', 'atmos_daily', 'ts', 'daily', + '6yr', + 'atmos_daily.20060101-20111231.temp.nc')] + result = gfdl_data_loader._input_data_paths_gfdl( + 'temp', start_date, end_date, 'atmos', + 'daily', 'pressure', 'ts', None) + assert result == expected + + expected = [os.path.join('.', 'test', 'atmos_level', 'ts', 'monthly', + '6yr', 'atmos_level.200601-201112.temp.nc')] + result = gfdl_data_loader._input_data_paths_gfdl( + 'temp', start_date, end_date, 'atmos', + 'monthly', ETA_STR, 'ts', None) + assert result == expected + + expected = [os.path.join('.', 'test', 'atmos', 'ts', 'monthly', + '6yr', 'atmos.200601-201112.ps.nc')] + result = gfdl_data_loader._input_data_paths_gfdl( + 'ps', start_date, end_date, 'atmos', + 'monthly', ETA_STR, 'ts', None) + assert result == expected + + expected = [os.path.join('.', 'test', 'atmos_inst', 'ts', 'monthly', + '6yr', 'atmos_inst.200601-201112.temp.nc')] + result = gfdl_data_loader._input_data_paths_gfdl( + 'temp', start_date, end_date, 'atmos', + 'monthly', 'pressure', 'inst', None) + assert result == expected + + expected = [os.path.join('.', 'test', 'atmos', 'av', 'monthly_6yr', + 'atmos.2006-2011.jja.nc')] + result = gfdl_data_loader._input_data_paths_gfdl( + 'temp', start_date, end_date, 'atmos', + 'monthly', 'pressure', 'av', 'jja') + assert result == expected + + +# TODO: Parametrize these tests +def test_data_name_gfdl_annual(): + for data_type in ['ts', 'inst']: + expected = 'atmos.2010.temp.nc' + result = io.data_name_gfdl('temp', 'atmos', data_type, + 'annual', 2010, None, 2000, 1) + assert result == expected + + expected = 'atmos.2006-2011.temp.nc' + result = io.data_name_gfdl('temp', 'atmos', data_type, 'annual', 2010, None, 2000, 6) - self.assertEqual(result, expected) - - def test_data_name_gfdl_monthly(self): - for data_type in ['ts', 'inst']: - expected = 'atmos.200601-201112.temp.nc' - result = io.data_name_gfdl('temp', 'atmos', data_type, - 'monthly', 2010, 'jja', 2000, 6) - self.assertEqual(result, expected) - - for intvl_type in ['monthly', 'mon']: - expected = 'atmos.2010.jja.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av', - intvl_type, 2010, 'jja', 2000, 1) - self.assertEqual(result, expected) - - expected = 'atmos.2006-2011.jja.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av', - intvl_type, 2010, 'jja', 2000, 6) - self.assertEqual(result, expected) - - expected = 'atmos.2006-2011.01-12.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av_ts', - 'monthly', 2010, 'jja', 2000, 6) - self.assertEqual(result, expected) + assert result == expected - def test_data_name_gfdl_daily(self): - for data_type in ['ts', 'inst']: - expected = 'atmos.20060101-20111231.temp.nc' - result = io.data_name_gfdl('temp', 'atmos', data_type, - 'daily', 2010, None, 2000, 6) - self.assertEqual(result, expected) + for intvl_type in ['annual', 'ann']: + expected = 'atmos.2010.ann.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av', + intvl_type, 2010, None, 2000, 1) + assert result == expected - with self.assertRaises(NameError): - io.data_name_gfdl('temp', 'atmos', 'av', - 'daily', 2010, None, 2000, 6) + expected = 'atmos.2006-2011.ann.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av', + intvl_type, 2010, None, 2000, 6) + assert result == expected - expected = 'atmos.2006-2011.01-12.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av_ts', - 'daily', 2010, None, 2000, 6) - self.assertEqual(result, expected) + expected = 'atmos.2006-2011.01-12.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av_ts', + 'annual', 2010, None, 2000, 6) + assert result == expected - def test_data_name_gfdl_hr(self): - for data_type in ['ts', 'inst']: - expected = 'atmos.2006010100-2011123123.temp.nc' - result = io.data_name_gfdl('temp', 'atmos', data_type, - '3hr', 2010, None, 2000, 6) - self.assertEqual(result, expected) - with self.assertRaises(NameError): - io.data_name_gfdl('temp', 'atmos', 'av', - '3hr', 2010, None, 2000, 6) +def test_data_name_gfdl_monthly(): + for data_type in ['ts', 'inst']: + expected = 'atmos.200601-201112.temp.nc' + result = io.data_name_gfdl('temp', 'atmos', data_type, + 'monthly', 2010, 'jja', 2000, 6) + assert result == expected - expected = 'atmos.2006-2011.01-12.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av_ts', - '3hr', 2010, None, 2000, 6) - self.assertEqual(result, expected) - - def test_data_name_gfdl_seasonal(self): - for data_type in ['ts', 'inst']: - with self.assertRaises(NameError): - io.data_name_gfdl('temp', 'atmos', data_type, - 'seasonal', 2010, None, 2000, 6) - - for intvl_type in ['seasonal', 'seas']: - expected = 'atmos.2010.JJA.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av', - intvl_type, 2010, 'jja', 2000, 1) - self.assertEqual(result, expected) - - expected = 'atmos.2006-2011.JJA.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av', - intvl_type, 2010, 'jja', 2000, 6) - self.assertEqual(result, expected) - - expected = 'atmos.2006-2011.01-12.nc' - result = io.data_name_gfdl('temp', 'atmos', 'av_ts', - 'seasonal', 2010, None, 2000, 6) - self.assertEqual(result, expected) - - -class LoadVariableTestCase(unittest.TestCase): - def setUp(self): - self.data_loader = example_run.data_loader - - def tearDown(self): - # Restore default values of data_vars and coords - self.data_loader.data_vars = 'minimal' - self.data_loader.coords = 'minimal' - self.data_loader.preprocess_func = lambda ds, **kwargs: ds - - def test_load_variable(self): - result = self.data_loader.load_variable( - condensation_rain, datetime(5, 1, 1), datetime(5, 12, 31), - intvl_in='monthly') - filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', - '00050101.precip_monthly.nc') - expected = _open_ds_catch_warnings(filepath)['condensation_rain'] - np.testing.assert_array_equal(result.values, expected.values) - - def test_load_variable_does_not_warn(self): - with warnings.catch_warnings(record=True) as warnlog: - self.data_loader.load_variable(condensation_rain, - datetime(5, 1, 1), - datetime(5, 12, 31), - intvl_in='monthly') - assert len(warnlog) == 0 - - def test_load_variable_float32_to_float64(self): - def preprocess(ds, **kwargs): - # This function converts testing data to the float32 datatype - return ds.astype(np.float32) - self.data_loader.preprocess_func = preprocess - result = self.data_loader.load_variable( - condensation_rain, datetime(4, 1, 1), datetime(4, 12, 31), - intvl_in='monthly').dtype - expected = np.float64 - self.assertEqual(result, expected) - - def test_load_variable_maintain_float32(self): - def preprocess(ds, **kwargs): - # This function converts testing data to the float32 datatype - return ds.astype(np.float32) - self.data_loader.preprocess_func = preprocess - self.data_loader.upcast_float32 = False - result = self.data_loader.load_variable( - condensation_rain, datetime(4, 1, 1), datetime(4, 12, 31), - intvl_in='monthly').dtype - expected = np.float32 - self.assertEqual(result, expected) - - def test_load_variable_data_vars_all(self): - def preprocess(ds, **kwargs): - # This function drops the time coordinate from condensation_rain - temp = ds[condensation_rain.name] - temp = temp.isel(time=0, drop=True) - ds = ds.drop(condensation_rain.name) - ds[condensation_rain.name] = temp - assert TIME_STR not in ds[condensation_rain.name].coords - return ds - - self.data_loader.data_vars = 'all' - self.data_loader.preprocess_func = preprocess - data = self.data_loader.load_variable( - condensation_rain, datetime(4, 1, 1), datetime(5, 12, 31), - intvl_in='monthly') - result = TIME_STR in data.coords - self.assertEqual(result, True) + for intvl_type in ['monthly', 'mon']: + expected = 'atmos.2010.jja.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av', + intvl_type, 2010, 'jja', 2000, 1) + assert result == expected - def test_load_variable_data_vars_default(self): - data = self.data_loader.load_variable( - condensation_rain, datetime(4, 1, 1), datetime(5, 12, 31), - intvl_in='monthly') - result = TIME_STR in data.coords - self.assertEqual(result, True) + expected = 'atmos.2006-2011.jja.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av', + intvl_type, 2010, 'jja', 2000, 6) + assert result == expected - def test_load_variable_coords_all(self): - self.data_loader.coords = 'all' - data = self.data_loader.load_variable( - condensation_rain, datetime(4, 1, 1), datetime(5, 12, 31), - intvl_in='monthly') - result = TIME_STR in data[ZSURF_STR].coords - self.assertEqual(result, True) - - def test_load_variable_non_0001_refdate(self): - def preprocess(ds, **kwargs): - # This function converts our testing data (encoded with a units - # attribute with a reference data of 0001-01-01) to one - # with a reference data of 0004-01-01 (to do so we also need - # to offset the raw time values by three years). - three_yrs = 1095. - ds['time'] = ds['time'] - three_yrs - ds['time'].attrs['units'] = 'days since 0004-01-01 00:00:00' - ds['time_bounds'] = ds['time_bounds'] - three_yrs - ds['time_bounds'].attrs['units'] = 'days since 0004-01-01 00:00:00' - return ds - - self.data_loader.preprocess_func = preprocess - - for year in [4, 5, 6]: - result = self.data_loader.load_variable( - condensation_rain, datetime(year, 1, 1), - datetime(year, 12, 31), - intvl_in='monthly') - filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', - '000{}0101.precip_monthly.nc'.format(year)) - expected = _open_ds_catch_warnings(filepath)['condensation_rain'] - np.testing.assert_allclose(result.values, expected.values) - - def test_load_variable_preprocess(self): - def preprocess(ds, **kwargs): - if kwargs['start_date'] == datetime(5, 1, 1): - ds['condensation_rain'] = 10. * ds['condensation_rain'] - return ds - - self.data_loader.preprocess_func = preprocess - - result = self.data_loader.load_variable( - condensation_rain, datetime(5, 1, 1), datetime(5, 12, 31), - intvl_in='monthly') - filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', - '00050101.precip_monthly.nc') - expected = 10. * _open_ds_catch_warnings(filepath)['condensation_rain'] - np.testing.assert_allclose(result.values, expected.values) + expected = 'atmos.2006-2011.01-12.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av_ts', + 'monthly', 2010, 'jja', 2000, 6) + assert result == expected - result = self.data_loader.load_variable( - condensation_rain, datetime(4, 1, 1), datetime(4, 12, 31), - intvl_in='monthly') - filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', - '00040101.precip_monthly.nc') - expected = _open_ds_catch_warnings(filepath)['condensation_rain'] - np.testing.assert_allclose(result.values, expected.values) - - def test_load_variable_mask_and_scale(self): - def convert_all_to_missing_val(ds, **kwargs): - ds['condensation_rain'] = 0. * ds['condensation_rain'] + 1.0e20 - ds['condensation_rain'].attrs['_FillValue'] = 1.0e20 - return ds - - self.data_loader.preprocess_func = convert_all_to_missing_val - - data = self.data_loader.load_variable( - condensation_rain, datetime(5, 1, 1), - datetime(5, 12, 31), - intvl_in='monthly') - num_non_missing = np.isfinite(data).sum().item() - expected_num_non_missing = 0 - self.assertEqual(num_non_missing, expected_num_non_missing) +def test_data_name_gfdl_daily(): + for data_type in ['ts', 'inst']: + expected = 'atmos.20060101-20111231.temp.nc' + result = io.data_name_gfdl('temp', 'atmos', data_type, + 'daily', 2010, None, 2000, 6) + assert result == expected + + with pytest.raises(NameError): + io.data_name_gfdl('temp', 'atmos', 'av', + 'daily', 2010, None, 2000, 6) - def test_recursively_compute_variable_native(self): - result = self.data_loader.recursively_compute_variable( - condensation_rain, datetime(5, 1, 1), datetime(5, 12, 31), - intvl_in='monthly') - filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', - '00050101.precip_monthly.nc') - expected = _open_ds_catch_warnings(filepath)['condensation_rain'] - np.testing.assert_array_equal(result.values, expected.values) - - def test_recursively_compute_variable_one_level(self): - one_level = Var( - name='one_level', variables=(condensation_rain, condensation_rain), - func=lambda x, y: x + y) - result = self.data_loader.recursively_compute_variable( - one_level, datetime(5, 1, 1), datetime(5, 12, 31), - intvl_in='monthly') - filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', - '00050101.precip_monthly.nc') - expected = 2. * _open_ds_catch_warnings(filepath)['condensation_rain'] - np.testing.assert_array_equal(result.values, expected.values) - - def test_recursively_compute_variable_multi_level(self): - one_level = Var( - name='one_level', variables=(condensation_rain, condensation_rain), - func=lambda x, y: x + y) - multi_level = Var( - name='multi_level', variables=(one_level, condensation_rain), - func=lambda x, y: x + y) - result = self.data_loader.recursively_compute_variable( - multi_level, datetime(5, 1, 1), datetime(5, 12, 31), + expected = 'atmos.2006-2011.01-12.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av_ts', + 'daily', 2010, None, 2000, 6) + assert result == expected + + +def test_data_name_gfdl_hr(): + for data_type in ['ts', 'inst']: + expected = 'atmos.2006010100-2011123123.temp.nc' + result = io.data_name_gfdl('temp', 'atmos', data_type, + '3hr', 2010, None, 2000, 6) + assert result == expected + + with pytest.raises(NameError): + io.data_name_gfdl('temp', 'atmos', 'av', + '3hr', 2010, None, 2000, 6) + + expected = 'atmos.2006-2011.01-12.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av_ts', + '3hr', 2010, None, 2000, 6) + assert result == expected + + +def test_data_name_gfdl_seasonal(): + for data_type in ['ts', 'inst']: + with pytest.raises(NameError): + io.data_name_gfdl('temp', 'atmos', data_type, + 'seasonal', 2010, None, 2000, 6) + + for intvl_type in ['seasonal', 'seas']: + expected = 'atmos.2010.JJA.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av', + intvl_type, 2010, 'jja', 2000, 1) + assert result == expected + + expected = 'atmos.2006-2011.JJA.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av', + intvl_type, 2010, 'jja', 2000, 6) + assert result == expected + + expected = 'atmos.2006-2011.01-12.nc' + result = io.data_name_gfdl('temp', 'atmos', 'av_ts', + 'seasonal', 2010, None, 2000, 6) + assert result == expected + + +@pytest.fixture() +def load_variable_data_loader(): + return NestedDictDataLoader(file_map, upcast_float32=False) + + +_LOAD_VAR_DATE_RANGES = { + 'datetime': (datetime.datetime(5, 1, 1), + datetime.datetime(5, 12, 31)), + 'datetime64': (np.datetime64('0005-01-01'), + np.datetime64('0005-12-31')), + 'cftime': (DatetimeNoLeap(5, 1, 1), DatetimeNoLeap(5, 12, 31)), + 'str': ('0005', '0005') +} + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _LOAD_VAR_DATE_RANGES.values(), + ids=list(_LOAD_VAR_DATE_RANGES.keys())) +def test_load_variable(load_variable_data_loader, start_date, end_date): + result = load_variable_data_loader.load_variable( + condensation_rain, start_date, end_date, intvl_in='monthly') + filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', + '00050101.precip_monthly.nc') + expected = _open_ds_catch_warnings(filepath)['condensation_rain'] + np.testing.assert_array_equal(result.values, expected.values) + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _LOAD_VAR_DATE_RANGES.values(), + ids=list(_LOAD_VAR_DATE_RANGES.keys())) +def test_load_variable_does_not_warn(load_variable_data_loader, + start_date, end_date): + with warnings.catch_warnings(record=True) as warnlog: + load_variable_data_loader.load_variable( + condensation_rain, + start_date, end_date, intvl_in='monthly') - filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', - '00050101.precip_monthly.nc') - expected = 3. * _open_ds_catch_warnings(filepath)['condensation_rain'] - np.testing.assert_array_equal(result.values, expected.values) + assert len(warnlog) == 0 + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _LOAD_VAR_DATE_RANGES.values(), + ids=list(_LOAD_VAR_DATE_RANGES.keys())) +def test_load_variable_float32_to_float64(load_variable_data_loader, + start_date, end_date): + def preprocess(ds, **kwargs): + # This function converts testing data to the float32 datatype + return ds.astype(np.float32) + load_variable_data_loader.upcast_float32 = True + load_variable_data_loader.preprocess_func = preprocess + result = load_variable_data_loader.load_variable( + condensation_rain, start_date, + end_date, + intvl_in='monthly').dtype + expected = np.float64 + assert result == expected + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _LOAD_VAR_DATE_RANGES.values(), + ids=list(_LOAD_VAR_DATE_RANGES.keys())) +def test_load_variable_maintain_float32(load_variable_data_loader, + start_date, end_date): + def preprocess(ds, **kwargs): + # This function converts testing data to the float32 datatype + return ds.astype(np.float32) + load_variable_data_loader.preprocess_func = preprocess + load_variable_data_loader.upcast_float32 = False + result = load_variable_data_loader.load_variable( + condensation_rain, start_date, + end_date, + intvl_in='monthly').dtype + expected = np.float32 + assert result == expected + + +_LOAD_VAR_MULTI_YEAR_RANGES = { + 'datetime': (datetime.datetime(4, 1, 1), + datetime.datetime(5, 12, 31)), + 'datetime64': (np.datetime64('0004-01-01'), + np.datetime64('0005-12-31')), + 'cftime': (DatetimeNoLeap(4, 1, 1), DatetimeNoLeap(5, 12, 31)), + 'str': ('0004', '0005') +} + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _LOAD_VAR_MULTI_YEAR_RANGES.values(), + ids=list(_LOAD_VAR_MULTI_YEAR_RANGES.keys())) +def test_load_variable_data_vars_all(load_variable_data_loader, + start_date, end_date): + def preprocess(ds, **kwargs): + # This function drops the time coordinate from condensation_rain + temp = ds[condensation_rain.name] + temp = temp.isel(time=0, drop=True) + ds = ds.drop(condensation_rain.name) + ds[condensation_rain.name] = temp + assert TIME_STR not in ds[condensation_rain.name].coords + return ds + + load_variable_data_loader.data_vars = 'all' + load_variable_data_loader.preprocess_func = preprocess + data = load_variable_data_loader.load_variable( + condensation_rain, start_date, end_date, + intvl_in='monthly') + assert TIME_STR in data.coords + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _LOAD_VAR_MULTI_YEAR_RANGES.values(), + ids=list(_LOAD_VAR_MULTI_YEAR_RANGES.keys())) +def test_load_variable_data_vars_default(load_variable_data_loader, start_date, + end_date): + data = load_variable_data_loader.load_variable( + condensation_rain, start_date, end_date, + intvl_in='monthly') + assert TIME_STR in data.coords + + +@pytest.mark.parametrize(['start_date', 'end_date'], + _LOAD_VAR_MULTI_YEAR_RANGES.values(), + ids=list(_LOAD_VAR_MULTI_YEAR_RANGES.keys())) +def test_load_variable_coords_all(load_variable_data_loader, start_date, + end_date): + load_variable_data_loader.coords = 'all' + data = load_variable_data_loader.load_variable( + condensation_rain, start_date, end_date, + intvl_in='monthly') + assert TIME_STR in data[ZSURF_STR].coords + + +@pytest.mark.parametrize('year', [4, 5, 6]) +def test_load_variable_non_0001_refdate(load_variable_data_loader, year): + def preprocess(ds, **kwargs): + # This function converts our testing data (encoded with a units + # attribute with a reference data of 0001-01-01) to one + # with a reference data of 0004-01-01 (to do so we also need + # to offset the raw time values by three years). + three_yrs = 1095. + ds['time'] = ds['time'] - three_yrs + ds['time'].attrs['units'] = 'days since 0004-01-01 00:00:00' + ds['time'].attrs['calendar'] = 'noleap' + ds['time_bounds'] = ds['time_bounds'] - three_yrs + ds['time_bounds'].attrs['units'] = 'days since 0004-01-01 00:00:00' + ds['time_bounds'].attrs['calendar'] = 'noleap' + return ds + + load_variable_data_loader.preprocess_func = preprocess + + result = load_variable_data_loader.load_variable( + condensation_rain, DatetimeNoLeap(year, 1, 1), + DatetimeNoLeap(year, 12, 31), + intvl_in='monthly') + filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', + '000{}0101.precip_monthly.nc'.format(year)) + expected = _open_ds_catch_warnings(filepath)['condensation_rain'] + np.testing.assert_allclose(result.values, expected.values) + + +def test_load_variable_preprocess(load_variable_data_loader): + def preprocess(ds, **kwargs): + if kwargs['start_date'] == DatetimeNoLeap(5, 1, 1): + ds['condensation_rain'] = 10. * ds['condensation_rain'] + return ds + + load_variable_data_loader.preprocess_func = preprocess + + result = load_variable_data_loader.load_variable( + condensation_rain, DatetimeNoLeap(5, 1, 1), + DatetimeNoLeap(5, 12, 31), + intvl_in='monthly') + filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', + '00050101.precip_monthly.nc') + expected = 10. * _open_ds_catch_warnings(filepath)['condensation_rain'] + np.testing.assert_allclose(result.values, expected.values) + + result = load_variable_data_loader.load_variable( + condensation_rain, DatetimeNoLeap(4, 1, 1), + DatetimeNoLeap(4, 12, 31), + intvl_in='monthly') + filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', + '00040101.precip_monthly.nc') + expected = _open_ds_catch_warnings(filepath)['condensation_rain'] + np.testing.assert_allclose(result.values, expected.values) + + +def test_load_variable_mask_and_scale(load_variable_data_loader): + def convert_all_to_missing_val(ds, **kwargs): + ds['condensation_rain'] = 0. * ds['condensation_rain'] + 1.0e20 + ds['condensation_rain'].attrs['_FillValue'] = 1.0e20 + return ds + + load_variable_data_loader.preprocess_func = convert_all_to_missing_val + + data = load_variable_data_loader.load_variable( + condensation_rain, DatetimeNoLeap(5, 1, 1), + DatetimeNoLeap(5, 12, 31), + intvl_in='monthly') + + num_non_missing = np.isfinite(data).sum().item() + expected_num_non_missing = 0 + assert num_non_missing == expected_num_non_missing + + +def test_recursively_compute_variable_native(load_variable_data_loader): + result = load_variable_data_loader.recursively_compute_variable( + condensation_rain, DatetimeNoLeap(5, 1, 1), + DatetimeNoLeap(5, 12, 31), + intvl_in='monthly') + filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', + '00050101.precip_monthly.nc') + expected = _open_ds_catch_warnings(filepath)['condensation_rain'] + np.testing.assert_array_equal(result.values, expected.values) + + +def test_recursively_compute_variable_one_level(load_variable_data_loader): + one_level = Var( + name='one_level', variables=(condensation_rain, condensation_rain), + func=lambda x, y: x + y) + result = load_variable_data_loader.recursively_compute_variable( + one_level, DatetimeNoLeap(5, 1, 1), DatetimeNoLeap(5, 12, 31), + intvl_in='monthly') + filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', + '00050101.precip_monthly.nc') + expected = 2. * _open_ds_catch_warnings(filepath)['condensation_rain'] + np.testing.assert_array_equal(result.values, expected.values) + + +def test_recursively_compute_variable_multi_level(load_variable_data_loader): + one_level = Var( + name='one_level', variables=(condensation_rain, condensation_rain), + func=lambda x, y: x + y) + multi_level = Var( + name='multi_level', variables=(one_level, condensation_rain), + func=lambda x, y: x + y) + result = load_variable_data_loader.recursively_compute_variable( + multi_level, DatetimeNoLeap(5, 1, 1), DatetimeNoLeap(5, 12, 31), + intvl_in='monthly') + filepath = os.path.join(os.path.split(ROOT_PATH)[0], 'netcdf', + '00050101.precip_monthly.nc') + expected = 3. * _open_ds_catch_warnings(filepath)['condensation_rain'] + np.testing.assert_array_equal(result.values, expected.values) if __name__ == '__main__': diff --git a/aospy/test/test_run.py b/aospy/test/test_run.py index 96fa9be..aec1d17 100644 --- a/aospy/test/test_run.py +++ b/aospy/test/test_run.py @@ -1,9 +1,11 @@ #!/usr/bin/env python """Test suite for aospy.run module.""" -import datetime import sys import unittest +import cftime +import numpy as np + from aospy.run import Run from aospy.data_loader import DictDataLoader, GFDLDataLoader @@ -19,22 +21,24 @@ def tearDown(self): class TestRun(RunTestCase): def test_init_dates_valid_input(self): for attr in ['default_start_date', 'default_end_date']: - for date in [None, datetime.datetime(1, 1, 1)]: + for date in [None, np.datetime64('2000-01-01')]: run_ = Run(**{attr: date}) self.assertEqual(date, getattr(run_, attr)) def test_init_dates_invalid_input(self): for attr in ['default_start_date', 'default_end_date']: - for date in [1985, False, '1750-12-10']: + for date in [1985, False]: with self.assertRaises(TypeError): Run(**{attr: date}) def test_init_default_dates(self): - gdl = GFDLDataLoader(data_start_date=datetime.datetime(1, 1, 1), - data_end_date=datetime.datetime(1, 12, 31)) + gdl = GFDLDataLoader(data_start_date=cftime.DatetimeNoLeap(1, 1, 1), + data_end_date=cftime.DatetimeNoLeap(1, 12, 31)) run_ = Run(data_loader=gdl) - self.assertEqual(run_.default_start_date, datetime.datetime(1, 1, 1)) - self.assertEqual(run_.default_end_date, datetime.datetime(1, 12, 31)) + self.assertEqual(run_.default_start_date, + cftime.DatetimeNoLeap(1, 1, 1)) + self.assertEqual(run_.default_end_date, + cftime.DatetimeNoLeap(1, 12, 31)) ddl = DictDataLoader({'monthly': '/a/'}) run_ = Run(data_loader=ddl) diff --git a/aospy/test/test_utils_times.py b/aospy/test/test_utils_times.py index 00b4216..f42d8b4 100755 --- a/aospy/test/test_utils_times.py +++ b/aospy/test/test_utils_times.py @@ -1,19 +1,22 @@ #!/usr/bin/env python """Test suite for aospy.timedate module.""" import datetime -import warnings +import cftime import numpy as np import pandas as pd import pytest import xarray as xr +from itertools import product + from aospy.data_loader import set_grid_attrs_as_coords from aospy.internal_names import ( TIME_STR, TIME_BOUNDS_STR, BOUNDS_STR, TIME_WEIGHTS_STR, RAW_START_DATE_STR, RAW_END_DATE_STR, SUBSET_START_DATE_STR, SUBSET_END_DATE_STR ) +from aospy.automate import _merge_dicts from aospy.utils.times import ( apply_time_offset, average_time_bounds, @@ -21,8 +24,6 @@ monthly_mean_at_each_ind, ensure_datetime, datetime_or_default, - numpy_datetime_range_workaround, - numpy_datetime_workaround_encode_cf, month_indices, _month_conditional, extract_months, @@ -32,11 +33,13 @@ assert_matching_time_coord, ensure_time_as_index, sel_time, - yearly_average + yearly_average, + infer_year, + maybe_convert_to_index_date_type ) -_INVALID_DATE_OBJECTS = [1985, True, None, '2016-04-07', np.datetime64(1, 'Y')] +_INVALID_DATE_OBJECTS = [1985, True, None] def test_apply_time_offset(): @@ -110,10 +113,12 @@ def test_monthly_mean_at_each_ind(): assert actual.identical(desired) -def test_ensure_datetime_valid_input(): - for date in [datetime.datetime(1981, 7, 15), - datetime.datetime(1, 1, 1)]: - assert ensure_datetime(date) == date +@pytest.mark.parametrize('date', [np.datetime64('2000-01-01'), + cftime.DatetimeNoLeap(1, 1, 1), + datetime.datetime(1, 1, 1), + '2000-01-01']) +def test_ensure_datetime_valid_input(date): + assert ensure_datetime(date) == date def test_ensure_datetime_invalid_input(): @@ -123,119 +128,11 @@ def test_ensure_datetime_invalid_input(): def test_datetime_or_default(): - date = datetime.datetime(1, 2, 3) + date = np.datetime64('2000-01-01') assert datetime_or_default(None, 'dummy') == 'dummy' assert datetime_or_default(date, 'dummy') == ensure_datetime(date) -def test_numpy_datetime_range_workaround(): - assert (numpy_datetime_range_workaround( - datetime.datetime(pd.Timestamp.min.year + 1, 1, 1), - pd.Timestamp.min.year + 1, pd.Timestamp.min.year + 2) == - datetime.datetime(pd.Timestamp.min.year + 1, 1, 1)) - - assert ( - numpy_datetime_range_workaround(datetime.datetime(3, 1, 1), 1, 6) == - datetime.datetime(pd.Timestamp.min.year + 3, 1, 1) - ) - - assert ( - numpy_datetime_range_workaround(datetime.datetime(5, 1, 1), 4, 6) == - datetime.datetime(pd.Timestamp.min.year + 2, 1, 1) - ) - - # Test min_yr outside valid range - assert ( - numpy_datetime_range_workaround( - datetime.datetime(pd.Timestamp.min.year + 3, 1, 1), - pd.Timestamp.min.year, pd.Timestamp.min.year + 2) == - datetime.datetime(pd.Timestamp.min.year + 4, 1, 1) - ) - - # Test max_yr outside valid range - assert ( - numpy_datetime_range_workaround( - datetime.datetime(pd.Timestamp.max.year + 2, 1, 1), - pd.Timestamp.max.year - 1, pd.Timestamp.max.year) == - datetime.datetime(pd.Timestamp.min.year + 4, 1, 1)) - - -def _create_datetime_workaround_test_data(days, ref_units, expected_units): - # 1095 days corresponds to three years in a noleap calendar - # This allows us to generate ranges which straddle the - # Timestamp-valid range - three_yrs = 1095. - time = xr.DataArray([days, days + three_yrs], dims=[TIME_STR]) - ds = xr.Dataset(coords={TIME_STR: time}) - ds[TIME_STR].attrs['units'] = ref_units - ds[TIME_STR].attrs['calendar'] = 'noleap' - actual, min_yr, max_yr = numpy_datetime_workaround_encode_cf(ds) - - time_desired = xr.DataArray([days, days + three_yrs], - dims=[TIME_STR]) - desired = xr.Dataset(coords={TIME_STR: time_desired}) - desired[TIME_STR].attrs['units'] = expected_units - desired[TIME_STR].attrs['calendar'] = 'noleap' - return actual, min_yr, max_yr, desired - - -def _numpy_datetime_workaround_encode_cf_tests(days, ref_units, expected_units, - expected_time0, expected_min_yr, - expected_max_yr): - with warnings.catch_warnings(record=True) as warnlog: - actual, minyr, maxyr, desired = _create_datetime_workaround_test_data( - days, ref_units, expected_units) - assert len(warnlog) == 0 - xr.testing.assert_identical(actual, desired) - assert xr.decode_cf(actual).time.values[0] == expected_time0 - assert minyr == expected_min_yr - assert maxyr == expected_max_yr - - -def test_numpy_datetime_workaround_encode_cf(): - # 255169 days from 0001-01-01 corresponds to date 700-02-04. - _numpy_datetime_workaround_encode_cf_tests( - 255169., 'days since 0001-01-01 00:00:00', - 'days since 979-01-01 00:00:00', np.datetime64('1678-02-04'), - 700, 703) - - # Test a case where times are in the Timestamp-valid range - _numpy_datetime_workaround_encode_cf_tests( - 10., 'days since 2000-01-01 00:00:00', - 'days since 2000-01-01 00:00:00', np.datetime64('2000-01-11'), - 2000, 2003) - - # Regression tests for GH188 - _numpy_datetime_workaround_encode_cf_tests( - 732., 'days since 0700-01-01 00:00:00', - 'days since 1676-01-01 00:00:00', np.datetime64('1678-01-03'), - 702, 705) - - # Non-January 1st reference date - _numpy_datetime_workaround_encode_cf_tests( - 732., 'days since 0700-05-03 00:00:00', - 'days since 1676-05-03 00:00:00', np.datetime64('1678-05-05'), - 702, 705) - - # Above Timestamp.max - _numpy_datetime_workaround_encode_cf_tests( - 732., 'days since 2300-01-01 00:00:00', - 'days since 1676-01-01 00:00:00', np.datetime64('1678-01-03'), - 2302, 2305) - - # Straddle lower bound - _numpy_datetime_workaround_encode_cf_tests( - 2., 'days since 1677-01-01 00:00:00', - 'days since 1678-01-01 00:00:00', np.datetime64('1678-01-03'), - 1677, 1680) - - # Straddle upper bound - _numpy_datetime_workaround_encode_cf_tests( - 2., 'days since 2262-01-01 00:00:00', - 'days since 1678-01-01 00:00:00', np.datetime64('1678-01-03'), - 2262, 2265) - - def test_month_indices(): np.testing.assert_array_equal(month_indices('ann'), range(1, 13)) np.testing.assert_array_equal(month_indices('jja'), @@ -421,6 +318,94 @@ def test_assert_has_data_for_time(): _assert_has_data_for_time(da, start_date_bad, end_date_bad) +_CFTIME_DATE_TYPES = { + 'noleap': cftime.DatetimeNoLeap, + '365_day': cftime.DatetimeNoLeap, + '360_day': cftime.Datetime360Day, + 'julian': cftime.DatetimeJulian, + 'all_leap': cftime.DatetimeAllLeap, + '366_day': cftime.DatetimeAllLeap, + 'gregorian': cftime.DatetimeGregorian, + 'proleptic_gregorian': cftime.DatetimeProlepticGregorian +} + + +@pytest.mark.parametrize(['calendar', 'date_type'], + list(_CFTIME_DATE_TYPES.items())) +def test_assert_has_data_for_time_cftime_datetimes(calendar, date_type): + time_bounds = np.array([[0, 2], [2, 4], [4, 6]]) + nv = np.array([0, 1]) + time = np.array([1, 3, 5]) + data = np.zeros((3)) + var_name = 'a' + ds = xr.DataArray(data, + coords=[time], + dims=[TIME_STR], + name=var_name).to_dataset() + ds[TIME_BOUNDS_STR] = xr.DataArray(time_bounds, + coords=[time, nv], + dims=[TIME_STR, BOUNDS_STR], + name=TIME_BOUNDS_STR) + units_str = 'days since 0002-01-02 00:00:00' + ds[TIME_STR].attrs['units'] = units_str + ds[TIME_STR].attrs['calendar'] = calendar + ds = ensure_time_avg_has_cf_metadata(ds) + ds = set_grid_attrs_as_coords(ds) + with xr.set_options(enable_cftimeindex=True): + ds = xr.decode_cf(ds) + da = ds[var_name] + + start_date = date_type(2, 1, 2) + end_date = date_type(2, 1, 8) + _assert_has_data_for_time(da, start_date, end_date) + + start_date_bad = date_type(2, 1, 1) + end_date_bad = date_type(2, 1, 9) + + with pytest.raises(AssertionError): + _assert_has_data_for_time(da, start_date_bad, end_date) + + with pytest.raises(AssertionError): + _assert_has_data_for_time(da, start_date, end_date_bad) + + with pytest.raises(AssertionError): + _assert_has_data_for_time(da, start_date_bad, end_date_bad) + + +def test_assert_has_data_for_time_str_input(): + time_bounds = np.array([[0, 31], [31, 59], [59, 90]]) + nv = np.array([0, 1]) + time = np.array([15, 46, 74]) + data = np.zeros((3)) + var_name = 'a' + ds = xr.DataArray(data, + coords=[time], + dims=[TIME_STR], + name=var_name).to_dataset() + ds[TIME_BOUNDS_STR] = xr.DataArray(time_bounds, + coords=[time, nv], + dims=[TIME_STR, BOUNDS_STR], + name=TIME_BOUNDS_STR) + units_str = 'days since 2000-01-01 00:00:00' + ds[TIME_STR].attrs['units'] = units_str + ds = ensure_time_avg_has_cf_metadata(ds) + ds = set_grid_attrs_as_coords(ds) + ds = xr.decode_cf(ds) + da = ds[var_name] + + start_date = '2000-01-01' + end_date = '2000-03-31' + _assert_has_data_for_time(da, start_date, end_date) + + start_date_bad = '1999-12-31' + end_date_bad = '2000-04-01' + + # With strings these checks are disabled + _assert_has_data_for_time(da, start_date_bad, end_date) + _assert_has_data_for_time(da, start_date, end_date_bad) + _assert_has_data_for_time(da, start_date_bad, end_date_bad) + + def test_assert_matching_time_coord(): rng = pd.date_range('2000-01-01', '2001-01-01', freq='M') arr1 = xr.DataArray(rng, coords=[rng], dims=[TIME_STR]) @@ -555,3 +540,83 @@ def test_average_time_bounds(ds_time_encoded_cf): coords={TIME_STR: desired_values}, name=TIME_STR) xr.testing.assert_identical(actual, desired) + + +_INFER_YEAR_TESTS = [ + (np.datetime64('2000-01-01'), 2000), + (datetime.datetime(2000, 1, 1), 2000), + ('2000', 2000), + ('2000-01', 2000), + ('2000-01-01', 2000) +] +_INFER_YEAR_TESTS = _INFER_YEAR_TESTS + [ + (date_type(2000, 1, 1), 2000) for date_type in _CFTIME_DATE_TYPES.values()] + + +@pytest.mark.parametrize( + ['date', 'expected'], + _INFER_YEAR_TESTS) +def test_infer_year(date, expected): + assert infer_year(date) == expected + + +@pytest.mark.parametrize('date', ['-0001', 'A001', '01']) +def test_infer_year_invalid(date): + with pytest.raises(ValueError): + infer_year(date) + + +_DATETIME_INDEX = pd.date_range('2000-01-01', freq='M', periods=1) +_DATETIME_CONVERT_TESTS = {} +for date_label, date_type in _CFTIME_DATE_TYPES.items(): + key = 'DatetimeIndex-{}'.format(date_label) + _DATETIME_CONVERT_TESTS[key] = (_DATETIME_INDEX, date_type(2000, 1, 1), + np.datetime64('2000-01')) +_NON_CFTIME_DATES = { + 'datetime.datetime': datetime.datetime(2000, 1, 1), + 'np.datetime64': np.datetime64('2000-01-01'), + 'str': '2000' +} +for date_label, date in _NON_CFTIME_DATES.items(): + key = 'DatetimeIndex-{}'.format(date_label) + if isinstance(date, str): + _DATETIME_CONVERT_TESTS[key] = (_DATETIME_INDEX, date, date) + else: + _DATETIME_CONVERT_TESTS[key] = (_DATETIME_INDEX, date, + np.datetime64('2000-01')) + +_CFTIME_INDEXES = { + 'CFTimeIndex[{}]'.format(key): xr.CFTimeIndex([value(1, 1, 1)]) for + key, value in _CFTIME_DATE_TYPES.items() +} +_CFTIME_CONVERT_TESTS = {} +for ((index_label, index), + (date_label, date_type)) in product(_CFTIME_INDEXES.items(), + _CFTIME_DATE_TYPES.items()): + key = '{}-{}'.format(index_label, date_label) + _CFTIME_CONVERT_TESTS[key] = (index, date_type(1, 1, 1), + index.date_type(1, 1, 1)) +_NON_CFTIME_DATES_0001 = { + 'datetime.datetime': datetime.datetime(1, 1, 1), + 'np.datetime64': np.datetime64('0001-01-01'), + 'str': '0001' +} +for ((idx_label, index), + (date_label, date)) in product(_CFTIME_INDEXES.items(), + _NON_CFTIME_DATES_0001.items()): + key = '{}-{}'.format(index_label, date_label) + if isinstance(date, str): + _CFTIME_CONVERT_TESTS[key] = (index, date, date) + else: + _CFTIME_CONVERT_TESTS[key] = (index, date, index.date_type(1, 1, 1)) + +_CONVERT_DATE_TYPE_TESTS = _merge_dicts(_DATETIME_CONVERT_TESTS, + _CFTIME_CONVERT_TESTS) + + +@pytest.mark.parametrize(['index', 'date', 'expected'], + list(_CONVERT_DATE_TYPE_TESTS.values()), + ids=list(_CONVERT_DATE_TYPE_TESTS.keys())) +def test_maybe_convert_to_index_date_type(index, date, expected): + result = maybe_convert_to_index_date_type(index, date) + assert result == expected diff --git a/aospy/utils/times.py b/aospy/utils/times.py index 08b0c89..9f8e7bd 100644 --- a/aospy/utils/times.py +++ b/aospy/utils/times.py @@ -1,11 +1,15 @@ """Utility functions for handling times, dates, etc.""" import datetime -import warnings +import logging +import re +import cftime import numpy as np import pandas as pd import xarray as xr +from pandas.errors import OutOfBoundsDatetime + from ..internal_names import ( BOUNDS_STR, GRID_ATTRS_NO_TIMES, RAW_END_DATE_STR, RAW_START_DATE_STR, SUBSET_END_DATE_STR, SUBSET_START_DATE_STR, TIME_BOUNDS_STR, TIME_STR, @@ -173,7 +177,7 @@ def yearly_average(arr, dt): def ensure_datetime(obj): - """Return the object if it is of type datetime.datetime; else raise. + """Return the object if it is a datetime-like object Parameters ---------- @@ -181,24 +185,24 @@ def ensure_datetime(obj): Returns ------- - The original object if it is a datetime.datetime object. + The original object if it is a datetime-like object Raises ------ - TypeError if `obj` is not of type `datetime.datetime`. + TypeError if `obj` is not datetime-like """ - if isinstance(obj, datetime.datetime): + if isinstance(obj, (str, datetime.datetime, cftime.datetime, np.datetime64)): return obj - raise TypeError("`datetime.datetime` object required. " + raise TypeError("datetime-like object required. " "Type given: {}".format(type(obj))) def datetime_or_default(date, default): - """Return a datetime.datetime object or a default. + """Return a datetime-like object or a default. Parameters ---------- - date : `None` or datetime-like object + date : `None` or datetime-like object or str default : The value to return if `date` is `None` Returns @@ -213,85 +217,6 @@ def datetime_or_default(date, default): return ensure_datetime(date) -def numpy_datetime_range_workaround(date, min_year, max_year): - """Reset a date to earliest allowable year if outside of valid range. - - Hack to address np.datetime64, and therefore pandas and xarray, not - supporting dates outside the range 1677-09-21 and 2262-04-11 due to - nanosecond precision. See e.g. - https://github.com/spencerahill/aospy/issues/96. - - - Parameters - ---------- - date : datetime.datetime object - min_year : int - Minimum year in the raw decoded dataset - max_year : int - Maximum year in the raw decoded dataset - - Returns - ------- - datetime.datetime object - Original datetime.datetime object if the original date is within the - permissible dates, otherwise a datetime.datetime object with the year - offset to the earliest allowable year. - """ - min_yr_in_range = pd.Timestamp.min.year < min_year < pd.Timestamp.max.year - max_yr_in_range = pd.Timestamp.min.year < max_year < pd.Timestamp.max.year - if not (min_yr_in_range and max_yr_in_range): - return datetime.datetime( - date.year - min_year + pd.Timestamp.min.year + 1, - date.month, date.day) - return date - - -def numpy_datetime_workaround_encode_cf(ds): - """Generate CF-compliant units for out-of-range dates. - - Hack to address np.datetime64, and therefore pandas and xarray, not - supporting dates outside the range 1677-09-21 and 2262-04-11 due to - nanosecond precision. See e.g. - https://github.com/spencerahill/aospy/issues/96. - - Specifically, we coerce the data such that, when decoded, the earliest - value starts in 1678 but with its month, day, and shorter timescales - (hours, minutes, seconds, etc.) intact and with the time-spacing between - values intact. - - Parameters - ---------- - ds : xarray.Dataset - - Returns - ------- - xarray.Dataset, int, int - Dataset with time units adjusted as needed, minimum year - in loaded data, and maximum year in loaded data. - """ - time = ds[TIME_STR] - units = time.attrs['units'] - units_yr = units.split(' since ')[1].split('-')[0] - with warnings.catch_warnings(record=True): - min_yr_decoded = xr.decode_cf(time.to_dataset(name='dummy')) - min_date = min_yr_decoded[TIME_STR].values[0] - max_date = min_yr_decoded[TIME_STR].values[-1] - if all(isinstance(date, np.datetime64) for date in [min_date, max_date]): - return ds, pd.Timestamp(min_date).year, pd.Timestamp(max_date).year - else: - min_yr = min_date.year - max_yr = max_date.year - offset = int(units_yr) - min_yr + 1 - new_units_yr = pd.Timestamp.min.year + offset - new_units = units.replace(units_yr, str(new_units_yr)) - - for VAR_STR in TIME_VAR_STRS: - if VAR_STR in ds: - var = ds[VAR_STR] - var.attrs['units'] = new_units - return ds, min_yr, max_yr - - def month_indices(months): """Convert string labels for months to integer indices. @@ -462,9 +387,9 @@ def _assert_has_data_for_time(da, start_date, end_date): ---------- da : DataArray DataArray with a time variable - start_date : netCDF4.netcdftime or np.datetime64 + start_date : datetime-like object or str start date - end_date : netCDF4.netcdftime or np.datetime64 + end_date : datetime-like object or str end date Raises @@ -472,6 +397,16 @@ def _assert_has_data_for_time(da, start_date, end_date): AssertionError if the time range is not within the time range of the DataArray """ + if isinstance(start_date, str) and isinstance(end_date, str): + logging.warning( + 'When using strings to specify start and end dates, the check ' + 'to determine if data exists for the full extent of the desired ' + 'interval is not implemented. Therefore it is possible that ' + 'you are doing a calculation for a lesser interval than you ' + 'specified. If you would like this check to occur, use explicit ' + 'datetime-like objects for bounds instead.') + return + if RAW_START_DATE_STR in da.coords: da_start = da[RAW_START_DATE_STR].values da_end = da[RAW_END_DATE_STR].values @@ -480,7 +415,11 @@ def _assert_has_data_for_time(da, start_date, end_date): da_start, da_end = times.values message = ('Data does not exist for requested time range: {0} to {1};' ' found data from time range: {2} to {3}.') - range_exists = start_date >= da_start and end_date <= da_end + # Add tolerance of one second, due to precision of cftime.datetimes + tol = datetime.timedelta(seconds=1) + if isinstance(da_start, np.datetime64): + tol = np.timedelta64(tol, 'ns') + range_exists = (da_start - tol) <= start_date and (da_end + tol) >= end_date assert (range_exists), message.format(start_date, end_date, da_start, da_end) @@ -571,3 +510,95 @@ def ensure_time_as_index(ds): da[TIME_STR] = ds[TIME_STR] ds[name] = da return ds + + +def infer_year(date): + """Given a datetime-like object or string infer the year. + + Parameters + ---------- + date : datetime-like object or str + Input date + + Returns + ------- + int + + Examples + -------- + >>> infer_year('2000') + 2000 + >>> infer_year('2000-01') + 2000 + >>> infer_year('2000-01-31') + 2000 + >>> infer_year(datetime.datetime(2000, 1, 1)) + 2000 + >>> infer_year(np.datetime64('2000-01-01')) + 2000 + >>> infer_year(DatetimeNoLeap(2000, 1, 1)) + 2000 + >>> + """ + if isinstance(date, str): + # Look for a string that begins with four numbers; the first four + # numbers found are the year. + pattern = '(?P\d{4})' + result = re.match(pattern, date) + if result: + return int(result.groupdict()['year']) + else: + raise ValueError('Invalid date string provided: {}'.format(date)) + elif isinstance(date, np.datetime64): + return date.item().year + else: + return date.year + + +def maybe_convert_to_index_date_type(index, date): + """Convert a datetime-like object to the index's date type. + + Datetime indexing in xarray can be done using either a pandas + DatetimeIndex or a CFTimeIndex. Both support partial-datetime string + indexing regardless of the calendar type of the underlying data; + therefore if a string is passed as a date, we return it unchanged. If a + datetime-like object is provided, it will be converted to the underlying + date type of the index. For a DatetimeIndex that is np.datetime64; for a + CFTimeIndex that is an object of type cftime.datetime specific to the + calendar used. + + Parameters + ---------- + index : pd.Index + Input time index + date : datetime-like object or str + Input datetime + + Returns + ------- + date of the type appropriate for the time index of the Dataset + """ + if isinstance(date, str): + return date + + if isinstance(index, pd.DatetimeIndex): + if isinstance(date, np.datetime64): + return date + else: + return np.datetime64(str(date)) + else: + date_type = index.date_type + if isinstance(date, date_type): + return date + else: + if isinstance(date, np.datetime64): + # Convert to datetime.date or datetime.datetime object + date = date.item() + + if isinstance(date, datetime.date): + # Convert to a datetime.datetime object + date = datetime.datetime.combine( + date, datetime.datetime.min.time()) + + return date_type(date.year, date.month, date.day, date.hour, + date.minute, date.second, date.microsecond) diff --git a/appveyor.yml b/appveyor.yml index 5ee68d9..b377849 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,9 +5,9 @@ environment: matrix: - - PYTHON: "C:\\Python27-conda32" + - PYTHON: "C:\\Python27-conda64" PYTHON_VERSION: "2.7" - PYTHON_ARCH: "32" + PYTHON_ARCH: "64" CONDA_ENV: "py27" - PYTHON: "C:\\Python35-conda64" @@ -41,4 +41,4 @@ install: build: false test_script: - - "py.test aospy --verbose" \ No newline at end of file + - "py.test aospy --verbose" diff --git a/docs/examples.rst b/docs/examples.rst index 6402eba..a988850 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -97,9 +97,28 @@ a name for the run and an optional description. example_run = Run( name='example_run', description='Control simulation of the idealized moist model', - data_loader=data_loader + data_loader=data_loader, + default_start_date='0004', + default_end_date='0006' ) +.. note:: + + Throughout aospy, date slice bounds can be specified with dates of any of + the following types: + + - ``str``, for partial-datetime string indexing + - ``np.datetime64`` + - ``datetime.datetime`` + - ``cftime.datetime`` + + If possible, aospy will automatically convert the latter three to the + appropriate date type used for indexing the data read in; otherwise it will + raise an error. Therefore the arguments ``default_start_date`` and + ``default_end_date`` in the ``Run`` constructor are calendar-agnostic (as + are the ``date_ranges`` specified :ref:`when submitting + calculations`). + .. note:: See the :ref:`API reference ` for other optional arguments @@ -189,18 +208,18 @@ that we saw are directly available as model output: from aospy import Var - precip_largescale = Var( - name='precip_largescale', # name used by aospy - alt_names=('condensation_rain',), # its possible name(s) in your data - def_time=True, # whether or not it is defined in time - description='Precipitation generated via grid-scale condensation', - ) - precip_convective = Var( - name='precip_convective', - alt_names=('convection_rain', 'prec_conv'), - def_time=True, - description='Precipitation generated by convective parameterization', - ) + precip_largescale = Var( + name='precip_largescale', # name used by aospy + alt_names=('condensation_rain',), # its possible name(s) in your data + def_time=True, # whether or not it is defined in time + description='Precipitation generated via grid-scale condensation', + ) + precip_convective = Var( + name='precip_convective', + alt_names=('convection_rain', 'prec_conv'), + def_time=True, + description='Precipitation generated by convective parameterization', + ) When it comes time to load data corresponding to either of these from one or more particular netCDF files, aospy will search for variables @@ -338,6 +357,8 @@ Submitting calculations Using :py:func:`aospy.submit_mult_calcs` ======================================== +.. _Submitting calculations: + Having put in the legwork above of describing our data and the physical quantities we wish to compute, we can submit our desired calculations for execution using :py:func:`aospy.submit_mult_calcs`. @@ -402,6 +423,17 @@ Although we do not show it here, this also prints logging information to the terminal at various steps during each calculation, including the filepaths to the netCDF files written to disk of the results. +.. warning:: + + For date ranges specified using tuples of datetime-like objects, + ``aospy`` will check to make sure that datapoints exist for the full extent + of the time ranges specified. For date ranges specified as tuples of + strings, however, this check is currently not implemented. This is mostly + harmless (i.e. it will not change the results of calculations); however, it + can result in files whose labels do not accurately represent the actual + time bounds of the calculation if you specify string date ranges that span + more than the interval of the input data. + Results ======= @@ -432,19 +464,6 @@ and the results of each output type quantity is, even if you don't have the original ``Var`` definition on hand. -.. note:: - - You may have noticed that ``subset_...`` and ``raw_...`` - coordinates have years 1678 and later, when our data was from model - years 4 through 6. This is because `technical details upstream - `_ limit the range of supported whole years to 1678-2262. - - As a workaround, aospy pretends that any timeseries that starts - before the beginning of this range actually starts at 1678. An - upstream fix is `currently under way - `_, at which point - all dates will be supported without this workaround. - Gridpoint-by-gridpoint ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/whats-new.rst b/docs/whats-new.rst index 66af034..3a42699 100644 --- a/docs/whats-new.rst +++ b/docs/whats-new.rst @@ -44,6 +44,11 @@ Documentation Enhancements ~~~~~~~~~~~~ +- Use an ``xarray.CFTimeIndex`` for dates from non-standard calendars and + outside the Timestamp-valid range. This eliminates the need for the prior + workaround, which shifted dates to within the range 1678 to 2262 prior to + indexing (closes :issue:`98` via :pull:`273`). By + `Spencer Clark `_. - Create ``utils.longitude`` module and ``Longitude`` class for representing and comparing longitudes. Used internally by ``aospy.Region`` to construct masks, but could also be useful for @@ -137,9 +142,9 @@ Dependencies - ``aospy`` now requires a minimum version of ``distributed`` of 1.17.1 (fixes :issue:`210` via :pull:`211`). -- ``aospy`` now requires a minimum version of ``xarray`` of 0.10.3. - See discussion in :issue:`199`, :pull:`240`, :issue:`268`, and - :pull:`269` for more details. +- ``aospy`` now requires a minimum version of ``xarray`` of 0.10.4. + See discussion in :issue:`199`, :pull:`240`, :issue:`268`, + :pull:`269`, and :pull:`273` for more details. .. _whats-new.0.2: diff --git a/setup.py b/setup.py index bb659f5..9c1537b 100644 --- a/setup.py +++ b/setup.py @@ -33,8 +33,9 @@ 'toolz >= 0.7.2', 'dask >= 0.14', 'distributed >= 1.17.1', - 'xarray >= 0.10.3', - 'cloudpickle >= 0.2.1'], + 'xarray >= 0.10.4', + 'cloudpickle >= 0.2.1', + 'cftime >= 1.0.0'], tests_require=['pytest >= 2.7.1', 'pytest-catchlog >= 1.0'], package_data={'aospy': ['test/data/netcdf/*.nc']},