diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fb1be91ea..bc914a979 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -135,7 +135,7 @@ jobs: python -m coverage report - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: coverage.xml deploy: diff --git a/.gitignore b/.gitignore index 54ea62f14..7fcdd7723 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ polytope_venv_latest new_updated_numpy_venv newest-polytope-venv serializedTree +new_polytope_venv diff --git a/docs/Developer_Guide/API.md b/docs/Algorithm/Developer_Guide/API.md similarity index 100% rename from docs/Developer_Guide/API.md rename to docs/Algorithm/Developer_Guide/API.md diff --git a/docs/Developer_Guide/Axis_types.md b/docs/Algorithm/Developer_Guide/Axis_types.md similarity index 100% rename from docs/Developer_Guide/Axis_types.md rename to docs/Algorithm/Developer_Guide/Axis_types.md diff --git a/docs/Developer_Guide/Datacube.md b/docs/Algorithm/Developer_Guide/Datacube.md similarity index 100% rename from docs/Developer_Guide/Datacube.md rename to docs/Algorithm/Developer_Guide/Datacube.md diff --git a/docs/Developer_Guide/Overview.md b/docs/Algorithm/Developer_Guide/Overview.md similarity index 100% rename from docs/Developer_Guide/Overview.md rename to docs/Algorithm/Developer_Guide/Overview.md diff --git a/docs/Developer_Guide/Slicer.md b/docs/Algorithm/Developer_Guide/Slicer.md similarity index 100% rename from docs/Developer_Guide/Slicer.md rename to docs/Algorithm/Developer_Guide/Slicer.md diff --git a/docs/Developer_Guide/images/Polytope_APIs_3.png b/docs/Algorithm/Developer_Guide/images/Polytope_APIs_3.png similarity index 100% rename from docs/Developer_Guide/images/Polytope_APIs_3.png rename to docs/Algorithm/Developer_Guide/images/Polytope_APIs_3.png diff --git a/docs/Developer_Guide/images/polytope_components_5.png b/docs/Algorithm/Developer_Guide/images/polytope_components_5.png similarity index 100% rename from docs/Developer_Guide/images/polytope_components_5.png rename to docs/Algorithm/Developer_Guide/images/polytope_components_5.png diff --git a/docs/Developer_Guide/images/slicing_process.png b/docs/Algorithm/Developer_Guide/images/slicing_process.png similarity index 100% rename from docs/Developer_Guide/images/slicing_process.png rename to docs/Algorithm/Developer_Guide/images/slicing_process.png diff --git a/docs/Developer_Guide/shapes.md b/docs/Algorithm/Developer_Guide/shapes.md similarity index 100% rename from docs/Developer_Guide/shapes.md rename to docs/Algorithm/Developer_Guide/shapes.md diff --git a/docs/Overview/Overview.md b/docs/Algorithm/Overview/Overview.md similarity index 56% rename from docs/Overview/Overview.md rename to docs/Algorithm/Overview/Overview.md index 8efc1a3d9..e4224f318 100644 --- a/docs/Overview/Overview.md +++ b/docs/Algorithm/Overview/Overview.md @@ -10,30 +10,29 @@ Developed by ECMWF - the European Centre for Medium-Range Weather Forecasts - it ### Traditional Extraction Techniques -Traditional data extraction techniques only allow users to access datacubes "orthogonally" by selecting specific values or ranges along datacube dimensions. -Such data access mechanisms can be seen as extracting so-called "bounding boxes" of data. -These mechanisms are quite limited however as many user requests cannot be formulated using bounding boxes. +Traditional data extraction techniques only allow users to access boxes of data from datacubes. +These techniques are quite restrictive however as many user requests cannot be formulated using such boxes. !!!note "Example" - Imagine for example someone interested in extracting temperature data over the shape of France. - France is not a box shape over latitude and longitude. - Using current extraction techniques, this exact request would therefore be impossible and users would instead need to request a bounding box around France. + Imagine for example someone interested in extracting wind data over the Mediterranean sea. + The Mediterranean is not a box shape over latitude and longitude. + Using current extraction techniques, this exact request would therefore be impossible and users would instead need to request a bounding box around the Mediterranean. The user would thus get back much more data than he truly needs. In higher dimensions, this becomes an even bigger challenge with only tiny fractions of the extracted data being useful to users. ### Polytope Extraction Technique -As an alternative, Polytope enables users to access datacubes "non-orthogonally". -Instead of extracting bounding boxes of data, Polytope has the capability of querying high-dimensional "polytopes" along several axes of a datacube. -This is much less restrictive than the popular bounding box approach described before. +Instead, Polytope enables users to access high-dimensional "polytopes" from datacubes, rather than only boxes of data. + + !!!note "Example" - Using Polytope, extracting the temperature over just the shape of France is now trivially possible by specifying the right polytope. + Using Polytope, extracting the temperature over just the shape of the Mediterranean is now trivially possible by specifying the right polytope. This returns much less data than by using a bounding box approach. These polytope-based requests do in fact allow Polytope to fulfill its two main aims. -Indeed, because polytope requests return only the exact data users need, they significantly reduce I/O usage as less data has to be transmitted. +Indeed, because polytope requests return only the data users need, they significantly reduce I/O usage as less data has to be transmitted. Moreover, because only the data inside the requested polytope is returned, this method completely removes the challenge of post-processing on the user side, as wanted. \ No newline at end of file diff --git a/docs/Overview/Polytope_at_ECMWF.md b/docs/Algorithm/Overview/Polytope_at_ECMWF.md similarity index 100% rename from docs/Overview/Polytope_at_ECMWF.md rename to docs/Algorithm/Overview/Polytope_at_ECMWF.md diff --git a/docs/Overview/images_overview/ecmwf_datacube.png b/docs/Algorithm/Overview/images_overview/ecmwf_datacube.png similarity index 100% rename from docs/Overview/images_overview/ecmwf_datacube.png rename to docs/Algorithm/Overview/images_overview/ecmwf_datacube.png diff --git a/docs/Overview/images_overview/ecmwf_polytope.png b/docs/Algorithm/Overview/images_overview/ecmwf_polytope.png similarity index 100% rename from docs/Overview/images_overview/ecmwf_polytope.png rename to docs/Algorithm/Overview/images_overview/ecmwf_polytope.png diff --git a/docs/Algorithm/User_Guide/Building_Features.md b/docs/Algorithm/User_Guide/Building_Features.md new file mode 100644 index 000000000..2ed8380fb --- /dev/null +++ b/docs/Algorithm/User_Guide/Building_Features.md @@ -0,0 +1,41 @@ +# Building Features + +The Polytope software implements a set of base shapes that might be of interest to users. These are detailed [here](../Developer_Guide/shapes.md). + +For many applications however, these shapes are not directly of interest and should rather be used as building blocks for more complex and domain-specific "features", such as timeseries or country areas. + +The main requirement when building such features in Polytope is that the feature should be defined on all dimensions of the provided datacube. +This implies that, when defining lower-dimensional shapes in higher-dimensional datacubes, the remaining axes still need to be specified within the Polytope request (most likely as *Select* shapes). + +For example, for a given datacube with dimensions "level", "step", "latitude" and "longitude", we could query the following shapes: + +- a timeseries of a point which would be defined as + + Request( + Point(["latitude", "longitude"], [[p1_lat, p1_lon]]), + Span("step", start_step, end_step), + Select("level", [level1]) + ) + + +- a specific country area which would be defined as + + Request( + Polygon(["latitude", "longitude"], country_points), + Select("step", [step1]), + Select("level", [level1]) + ) + +- a flight path which would be defined as + + Request( + Path( + ["latitude", "longitude", "level", "step"], + Box( + ["latitude", "longitude", "level", "step"], + [0, 0, 0, 0], + [lat_padding, lon_padding, level_padding, step_padding] + ), + flight_points + ) + ) diff --git a/docs/User_Guide/Example.md b/docs/Algorithm/User_Guide/Example.md similarity index 96% rename from docs/User_Guide/Example.md rename to docs/Algorithm/User_Guide/Example.md index 10ea2a510..8e5b903f3 100644 --- a/docs/User_Guide/Example.md +++ b/docs/Algorithm/User_Guide/Example.md @@ -1,7 +1,7 @@ # Example Here is a step-by-step example of how to use the Polytope software. -1. In this example, we first specify the data which will be in our Xarray datacube. Note that the data here comes from the GRIB file called "winds.grib", which is 3-dimensional with dimensions: step, latitude and longitude. +1. In this example, we first specify the data which will be in our XArray datacube. Note that the data here comes from the GRIB file called "winds.grib", which is 3-dimensional with dimensions: step, latitude and longitude. import xarray as xr diff --git a/docs/User_Guide/Getting_started.md b/docs/Algorithm/User_Guide/Getting_started.md similarity index 95% rename from docs/User_Guide/Getting_started.md rename to docs/Algorithm/User_Guide/Getting_started.md index fcaf042b6..57f76b248 100644 --- a/docs/User_Guide/Getting_started.md +++ b/docs/Algorithm/User_Guide/Getting_started.md @@ -26,13 +26,13 @@ or from PyPI with the command Polytope's tests and examples require some additional dependencies compared to the main Polytope software. -- **Git Large File Storage** + - **Additional Dependencies** diff --git a/docs/User_Guide/Overview.md b/docs/Algorithm/User_Guide/Overview.md similarity index 95% rename from docs/User_Guide/Overview.md rename to docs/Algorithm/User_Guide/Overview.md index 0e9b36d5a..4da049e19 100644 --- a/docs/User_Guide/Overview.md +++ b/docs/Algorithm/User_Guide/Overview.md @@ -15,6 +15,8 @@ For a quick guide of how to install and use Polytope, refer to the links below: - Example +- Building Features + !!!note An exhaustive list of all shapes that can currently be requested using Polytope can be found [here](../Developer_Guide/shapes.md). diff --git a/docs/User_Guide/images_users/shipping_route.png b/docs/Algorithm/User_Guide/images_users/shipping_route.png similarity index 100% rename from docs/User_Guide/images_users/shipping_route.png rename to docs/Algorithm/User_Guide/images_users/shipping_route.png diff --git a/docs/Service/Data_Portfolio.md b/docs/Service/Data_Portfolio.md new file mode 100644 index 000000000..52fd1d9d9 --- /dev/null +++ b/docs/Service/Data_Portfolio.md @@ -0,0 +1,51 @@ +# Data Portfolio + +Polytope feature extraction only has access to data that is stored on an FDB. The dataset currently available via Polyope feature extraction is the operational forecast. We plan to add Destination Earth Digital Twin data in the future. + +## Operational Forecast Data + +The following values available for each field specified are: + +* `class` : `od` +* `stream` : `enfo` `oper` +* `type` : `fc` `pf` `cf` +* `levtype` : `sfc` `pl` `ml` +* `expver` : `0001` +* `domain` : `g` +* `step` : `0/to/360` (All steps may not be available between `0` and `360`) + +If `type` is `enfo`: + +* `number` : `0/to/50` + +If `levtype` is `pl` or `ml` a `levelist` must be provided: + +* `levelist` : `1/to/1000` + +`pl` and `ml` also only contain a subset of parameters that are available in grid point. These are: + +* `pl` + * `o3` + * `clwc` + * `q` + * `pv` + * `ciwc` + * `cc` +* `ml` + * `q` + * `cat` + * `o3` + * `clwc` + * `ciwc` + * `cc` + * `cswc` + * `crwe` + * `ttpha` + +For `sfc` most `params` will be available but not all. + +Only data that is contained in the operational FDB can be requested via Polytope feature extraction, the FDB usually only contains the last two days of forecasts. + +We sometimes limit the size of requests for area features such as bounding box and polygon to maintain quality of service. + +Access to operational data is limited by our release schedule. diff --git a/docs/Service/Design_doc.md b/docs/Service/Design_doc.md new file mode 100644 index 000000000..545f4aa73 --- /dev/null +++ b/docs/Service/Design_doc.md @@ -0,0 +1,484 @@ +# Polytope-mars + +## Feature Documentation + +### Feature Keyword + +Feature extraction expands existing mars requests to include a `feature` keyword that includes a json dictionary that describes the given feature. This feature is then extracted using the Polytope feature extraction algorithm and only points within the given feature are returned. + +```python +"feature" : { + "type" : "timeseries", + "points" : [[-9.109280931080349, 38.78655345978706]], +} +``` + +#### Type + +An example of a minimal feature of `type` : `timeseries` can be seen above. A feature dictionary must always contain a `type`. The `type` in this case refers to what feature is being requested, the `type` of feature requested will then determine the format of the output returned, what other keys can go in the feature and suitable defaults if they are not available. In some cases it may also affect keys outside of the feature dictionary that come from the traditional mars request. For example if `type` : `verticalprofile` and `levtype` : `sfc`, this request will not be sent as a vertical profile expects either `levtype` : `pl/ml`. Other exceptions will be given for each separate feature `type`. + +The value available for `type` currently are as follows: + +* `timeseries` +* `verticalprofile` +* `polygon` +* `trajectory` +* `frame` +* `boundingbox` + +More feature types will be added in the future. + +#### Geometry + +A feature dictionary must also contain the requested geometry in some form. For a `timeseries` as seen above this comes in the form `points` which requests a timeseries at a given point, however this geometry is not always a point and depends upon `type`. The geometry is a mandatory field for all features. + +#### Axis + +A non-mandatory field that is available for each feature that is not present in the above example is `axis`. `axis` determines what field that the data should be enumerated along. In the case of a `timeseries` this will default to `step` meaning that the timeseries will be along the `step` axis, however there are other available `axes` such as `datetime`, this would be for climate data which contains no `step` `axis`. + +#### Range + +`range` is a json dictionary that is available for some features, it contains the extents of a given a `axes`. For example: + +```python +"range" : { + "start" : 0, + "end" : 10, + "interval" : 2, +} +``` + +If this range was included in the above feature dictionary for a `timeseries` it would ask for `step` (due to it being the default axis for timeseries) starting at `0` and ending at `10` with an interval of `2`, the returned steps would be `0,2,4,6,8,10`. This is equivalent to asking for the following in a mars request: + +```python +"step" : "0/to/10/by/2" +``` + +The above can also be put in the body of the request. However it must then be mutually exclusive with `range`. If both or neither are in the request an error is thrown. + +`range` can also appear in the following form: + +```python +"range" : [0,1,4,7,10] +``` + +This will only return the asked steps similar to in a MARS request where a user asks for the following: + +```python +"step" : "0/1/4/7/10" +``` + +Again either a `range` within the feature or an explicit `step` within the main body of the request can be used but not both or neither as there is no suitable default value unlike MARS. + + +### MARS Fields + +The non `feature` elements of the polytope-mars request act similar to the way one would expect when creating a MARS request with a few differences. + +* Most fields do not have a default value that will be tried if the field is not in the request. +* If a user makes a request and data is only available for some of the fields requested an error will be returned. Users will either receive all the data they requested or none. +* All key/value pairs must be in the form of a string n the main body of the request. Only values in the `feature` can be non-string types. +* Fields that can also be in the `feature` dictionary can either be provided in the main body of the request or in the `feature` but not both otherwise an error will be thrown. Both of the following examples are valid. + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20241006", + "time" : "0000", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "step": "0/to/360", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axis": "step", + }, +} +``` + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20241006", + "time" : "0000", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axis": "step", + "range" : { + "start" : 0, + "end" : 360, + } + }, +} +``` + +However the following is not: + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20241006", + "time" : "0000", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "step" : "0/to/360", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axis": "step", + "range" : { + "start" : 0, + "end" : 360, + } + }, +} +``` + +The above would throw an error that `step` has been over-subscribed. + +Ideally an valid mars request should be able to accept a valid `feature` and the polytope-mars request be valid but this may not always be true. + +Users can include the `format` key. However, initially the only value available will be `covjson` or `application/json+covjson`, these will be the default values if `format` is not included. Further formats may be added in the future. + +### Features + +The following features will be available for use in polytope-mars. + +#### Timeseries + +A timeseries request has a `feature` with `type` : `timeseries` and a geometry in the form of `points` containing a single point with latitude and longitude values. It also requires at least one time dimension with the default being `step`, although `datetime` is also accepted. The following is an example of a timeseries request: + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20241006", + "time" : "0000", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axis": "step", + "range" : { + "start" : 0, + "end" : 360, + } + }, +} +``` + +This is equivilent to: + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20241006", + "time" : "0000", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "step" : "0/to/360", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axis": "step", + }, +} +``` + +In this case the user is requesting `step` `0-360` on `20241006` for the point `[-9.10, 38.78]`. As the user does not specify `interval` all steps between `0-360` that are available. If the datacube is a climate dataset that does not contain step, an error would be thrown as `step` is not in the datacube. In this case the user would have to provide a request like the following: + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "step" : "0/to/360", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axis": "datetime", + "range": { + "start" : "20241006T000000", + "end" : "20241009T000000", + } + }, +} +``` + +Here a user will receive a `timeseries` for the dates `20241006T000000` to `20241009T000000` but not including the final date. The user can also provide an `interval` like `1d` meaning only intervals of 1 day are provided so in this case the following datetimes are returned. + +* `20241006T000000` +* `20241007T000000` +* `20241008T000000` + +If a dataset contains both a `step` and `datetime` the user can still request `axis` : `datetime`, and this request will return a timeseries across `datetime` rather than `step`. + +In the above case if a range is provided for a field such as `number` a time series as described above will be provided per `number` or any other range field. + +CoverageJSON output type: PointSeries + +#### Vertical Profile + +A vertical profile request has a `feature` with `type` : `verticalprofile` and a geometry in the form of `points` containing a single point with latitude and longitude values. It also requires a `levtype` that is not `sfc` and a `levelist` in the request or as part of the `feature`. The following is an example of a vertical profile request: + +```python +request = { + "class": "od", + "stream": "enfo", + "type": "pf", + "date": "20240925", + "time": "0000", + "levtype": "pl", + "expver": "0079", + "domain": "g", + "param": "203/133", + "number": "1", + "step": "0", + "levelist": "1/to/1000", + "feature": { + "type": "verticalprofile", + "points": [[38.9, -9.1]], + }, +} +``` + +The following is equivilent to the above: + +```python +request = { + "class": "od", + "stream": "enfo", + "type": "pf", + "date": "20240925", + "time": "0000", + "levtype": "pl", + "expver": "0079", + "domain": "g", + "param": "203/133", + "number": "1", + "step": "0", + "levelist": "1/to/1000", + "feature": { + "type": "verticalprofile", + "points": [[38.9, -9.1]], + "range" : { + "start" : 1, + "end" : 1000 + } + }, +} +``` + +`levtype` can either be `ml` or `pl` but at least one must be present. + +`levelist` can either be in the main body of the request or in `range` as described in the `range` section. If no `interval` is provided all values in from `start` to `end` will be requested. + +Currently the default for `axes` is `levelist` and is the only valid value for this key. This may change in the future. Users can include this in the request but it is not necessary. + +In the above case if a range is provided for a field such as `number`, a vertical profile as described above will be provided per `number` or any other range field. + +CoverageJSON output type: VerticalProfile + +#### Trajectory + +A trajectory request has a `feature` with `type` : `trajectory` and a geometry in the form of `points` containing at least two points with latitude and longitude, a level value, and a time value if no `axes` is provided. This is because the default `axes` are as follows: + +```python +"axes" : ["lat", "long", "level", "step"] +``` + +An example using default `axes` if found below. + +```python +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20240930", + "time" : "0000", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "levtype" : "pl", + "number" : "1", + "feature" : { + "type" : "trajectory", + "points" : [[-1, -1, 1000, 0], [0, 0, 1000, 12], [1, 1, 250, 24]], + }, +} +``` + +This request will return a trajectory with forecast date of `20240930T000000` for the three requested parameters for the points: + +* `lat: -1, long: -1, pressure level: 1000, step: 0` +* `lat: 0, long: 0, pressure level: 1000, step: 12` +* `lat: 1, long: 1, pressure level: 250, step: 24` + +The `trajectory` `feature` also contains another field called `padding` with a default of 1. This is the radius of the circle swept around the trajectory where points within this radius are returned to the user. + +`axes` must contain at minimum `lat` and `long` however a time and level axes are optional if provided in the main body of the request. The level and time axes can also take different values such as `step` or `datetime` for the time axes. + +The following is an example of a combination of `axes` that will cause an error: + +```python +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20240930", + "time" : "0000", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "levtype" : "pl", + "number" : "1", + "feature" : { + "type" : "trajectory", + "points" : [[-1, 0], [0, 12], [1, 24]], + "axis" : ['lat', 'step'] + }, +} +``` +This is due to the fact that `long` is required to be in `axis`. + +A valid example of leaving out a time `axis` is as follows: + +```python +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20240930", + "time" : "0000", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "levtype" : "pl", + "number" : "1", + "step" : "0/1" + "feature" : { + "type" : "trajectory", + "points" : [[-1, -1, 1000], [0, 0, 1000], [-1, -1, 250]], + "axis" : ['lat', 'long', 'level'] + }, +} +``` + +In this case the following data will be returned. + +* `lat: -1, long: -1, pressure level: 1000, step: 0` +* `lat: 0, long: 0, pressure level: 1000, step: 0` +* `lat: 1, long: 1, pressure level: 250, step: 0` + +* `lat: -1, long: -1, pressure level: 1000, step: 1` +* `lat: 0, long: 0, pressure level: 1000, step: 1` +* `lat: 1, long: 1, pressure level: 250, step: 1` + +These will be two of the same trajectories but on different steps. + +If `step` however was not specified outside of the `feature` the above would give an error that the time `axis` is underspecified. + +For any other ranged fields provided in the main request this will replicate the above returned data but per value. For example if ensemble `number` : `1/2` the same data as above would be provided for each `number`. + +CoverageJSON output type: Trajectory + +#### Polygon + +A polygon request has a `feature` with `type` : `poylgon` and a geometry in the form of `shape` containing at least one list containing three points with latitude and longitude with the first and final point being the same to complete the polygon. The user can provide multiple lists of points forming polygons in the same request. An example of the `polygon` feature is seen below: + +```python +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20240930", + "time" : "0000", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1", + "step": "0", + "feature" : { + "type" : "polygon", + "shape" : [[-1, 1], [-1, 0], [0, 1], [-1, 1]], + }, +} +``` + +If a user requests a a range for any of `step`, `number`, or `date` a polygon cutout for each field will be returned with a cutout of the data within the polygon. For example: + +```python +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : "20240930", + "time" : "0000", + "levtype" : "sfc", + "expver" : "0079", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/2", + "step": "0/1", + "feature" : { + "type" : "polygon", + "shape" : [[-1, 1], [-1, 0], [0, 1], [-1, 1]], + }, +} +``` + +This request will return a polygon for each number and each step within that given number. +Returned coverages as polygons: + +* `number: 1, step: 0, Points within shape` +* `number: 1, step: 1, Points within shape` +* `number: 2, step: 0, Points within shape` +* `number: 2, step: 1, Points within shape` + +Each of these will be an individual coverage with the 3 requested parameters. + +The `polygon` feature currently has limits on the size of a returned polygon and the maximum number of points allowed for a requested polygon. + +CoverageJSON output type: MultiPoint + +### Covjson + + +CoverageJSON has a number of different output features. Depending on the feature selected the output type will vary. + +A coverageCollection is always returned even if there is only a single coverage. +A new coverage is created for each ensemble number and depending on the feature type each new date (except in timeseries). The only grouped field is `param` which will be in the same coverage. \ No newline at end of file diff --git a/docs/Service/Examples/boundingbox_example.ipynb b/docs/Service/Examples/boundingbox_example.ipynb new file mode 100644 index 000000000..14a33a5b6 --- /dev/null +++ b/docs/Service/Examples/boundingbox_example.ipynb @@ -0,0 +1,560 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Bounding Box Example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import earthkit.data\n", + "\n", + "request = {\n", + " \"class\": \"od\",\n", + " \"stream\" : \"enfo\",\n", + " \"type\" : \"pf\",\n", + " \"date\" : -1, # Note: date must be within the last two days\n", + " \"time\" : \"0000\",\n", + " \"levtype\" : \"sfc\",\n", + " \"expver\" : \"0001\", \n", + " \"domain\" : \"g\",\n", + " \"param\" : \"164/166/167/169\",\n", + " \"number\" : \"1\",\n", + " \"step\": \"0\",\n", + " \"feature\" : {\n", + " \"type\" : \"boundingbox\",\n", + " \"points\" : [[53.55, 2.76], [50.66, 7.86]],\n", + "\t},\n", + "}\n", + "\n", + "\n", + "ds = earthkit.data.from_source(\"polytope\", \"ecmwf-mars\", request, stream=False, address='polytope.ecmwf.int')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert to xarray" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset> Size: 81kB\n",
+       "Dimensions:    (datetimes: 1, number: 1, steps: 1, points: 1258)\n",
+       "Coordinates:\n",
+       "  * datetimes  (datetimes) <U20 80B '2024-11-13T00:00:00Z'\n",
+       "  * number     (number) int64 8B 1\n",
+       "  * steps      (steps) int64 8B 0\n",
+       "  * points     (points) int64 10kB 0 1 2 3 4 5 ... 1252 1253 1254 1255 1256 1257\n",
+       "    x          (points) float64 10kB 50.72 50.72 50.72 ... 53.53 53.53 53.53\n",
+       "    y          (points) float64 10kB 2.877 3.037 3.197 3.357 ... 7.4 7.572 7.744\n",
+       "    z          (points) float64 10kB 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0\n",
+       "Data variables:\n",
+       "    tcc        (datetimes, number, steps, points) float64 10kB 0.9396 ... 1.0\n",
+       "    10v        (datetimes, number, steps, points) float64 10kB -1.797 ... -0....\n",
+       "    2t         (datetimes, number, steps, points) float64 10kB 278.3 ... 279.3\n",
+       "    ssrd       (datetimes, number, steps, points) float64 10kB 0.0 0.0 ... 0.0\n",
+       "Attributes:\n",
+       "    class:          od\n",
+       "    Forecast date:  2024-11-13T00:00:00Z\n",
+       "    domain:         g\n",
+       "    expver:         0001\n",
+       "    levtype:        sfc\n",
+       "    number:         1\n",
+       "    step:           0\n",
+       "    stream:         enfo\n",
+       "    type:           pf\n",
+       "    date:           2024-11-13T00:00:00Z
" + ], + "text/plain": [ + " Size: 81kB\n", + "Dimensions: (datetimes: 1, number: 1, steps: 1, points: 1258)\n", + "Coordinates:\n", + " * datetimes (datetimes) " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "da = ds.to_xarray()\n", + "import earthkit.plots\n", + "chart = earthkit.plots.Map(domain=\"Europe\")\n", + "chart.point_cloud(da['2t'], x=\"y\", y=\"x\")\n", + "\n", + "chart.coastlines()\n", + "chart.borders()\n", + "chart.gridlines()\n", + "\n", + "chart.title(\"{variable_name} (number={number})\")\n", + "\n", + "chart.legend()\n", + "\n", + "chart.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "polytope_venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/Service/Examples/country_example.ipynb b/docs/Service/Examples/country_example.ipynb new file mode 100644 index 000000000..f17e61349 --- /dev/null +++ b/docs/Service/Examples/country_example.ipynb @@ -0,0 +1,558 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Country Cutout Example" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This example shows the users how to use earthkit-geo (https://earthkit-geo.readthedocs.io) to retrieve a country cutout using Polytope." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import earthkit\n", + "import earthkit.plots\n", + "import earthkit.geo.cartography\n", + "\n", + "countries = [\"France\", \"Italy\", \"Spain\"] # List of countries\n", + "\n", + "shapes = earthkit.geo.cartography.country_polygons(countries, resolution=50e6)\n", + "\n", + "request = { \"class\": \"od\", \"stream\" : \"oper\", \"type\" : \"fc\", \"levtype\" : \"sfc\",\n", + " \"date\" : 0,\n", + " \"time\" : 0,\n", + " \"expver\" : 1,\n", + " \"param\" : [ 167 ],\n", + " \"step\": 0,\n", + " \"feature\": {\n", + " \"type\": \"polygon\",\n", + " \"shape\": shapes,\n", + " },\n", + "}\n", + "\n", + "ds = earthkit.data.from_source(\"polytope\", \"ecmwf-mars\", request, stream=False, address='polytope.ecmwf.int')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The collection being accessed is `ecmwf-mars`. The endpoint being accessed is `polytope.ecmwf.int`. Earthkit-geo is used to return the shape of the requested countries." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A user can also convert the data to xarray in the following way:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset> Size: 637kB\n",
+       "Dimensions:    (datetimes: 1, number: 1, steps: 1, points: 15914)\n",
+       "Coordinates:\n",
+       "  * datetimes  (datetimes) <U20 80B '2024-11-22T00:00:00Z'\n",
+       "  * number     (number) int64 8B 0\n",
+       "  * steps      (steps) int64 8B 0\n",
+       "  * points     (points) int64 127kB 0 1 2 3 4 ... 15909 15910 15911 15912 15913\n",
+       "    x          (points) float64 127kB 27.66 27.73 27.73 27.8 ... 51.0 51.0 51.0\n",
+       "    y          (points) float64 127kB 342.0 341.9 342.0 ... 2.093 2.254 2.415\n",
+       "    z          (points) float64 127kB 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0\n",
+       "Data variables:\n",
+       "    2t         (datetimes, number, steps, points) float64 127kB 292.9 ... 276.4\n",
+       "Attributes:\n",
+       "    class:          od\n",
+       "    Forecast date:  2024-11-22T00:00:00Z\n",
+       "    domain:         g\n",
+       "    expver:         0001\n",
+       "    levtype:        sfc\n",
+       "    step:           0\n",
+       "    stream:         oper\n",
+       "    type:           fc\n",
+       "    number:         0\n",
+       "    date:           2024-11-22T00:00:00Z
" + ], + "text/plain": [ + " Size: 637kB\n", + "Dimensions: (datetimes: 1, number: 1, steps: 1, points: 15914)\n", + "Coordinates:\n", + " * datetimes (datetimes) " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "chart = earthkit.plots.Map(domain=countries)\n", + "chart.point_cloud(xa['2t'], x=\"y\", y=\"x\")\n", + "chart.coastlines()\n", + "chart.borders()\n", + "chart.gridlines()\n", + "chart.legend()\n", + "chart.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "polytope_venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/Service/Examples/examples.md b/docs/Service/Examples/examples.md new file mode 100644 index 000000000..1e6a0be3d --- /dev/null +++ b/docs/Service/Examples/examples.md @@ -0,0 +1,8 @@ +# Examples + +* Timeseries +* Polygon +* Vertical Profile +* Bounding Box +* Trajectory +* Country Cut-Out \ No newline at end of file diff --git a/docs/Service/Examples/polygon_example.ipynb b/docs/Service/Examples/polygon_example.ipynb new file mode 100644 index 000000000..d7bf34099 --- /dev/null +++ b/docs/Service/Examples/polygon_example.ipynb @@ -0,0 +1,547 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Polygon Example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import earthkit.data\n", + "\n", + "request = {\n", + " \"class\": \"od\",\n", + " \"stream\" : \"enfo\",\n", + " \"type\" : \"pf\",\n", + " \"date\" : -1, # Note: date must be within the last two days\n", + " \"time\" : \"1200\",\n", + " \"levtype\" : \"sfc\",\n", + " \"expver\" : \"0001\",\n", + " \"domain\" : \"g\",\n", + " \"param\" : \"167/169\",\n", + " \"number\" : \"1\",\n", + " \"step\": \"0\",\n", + " \"feature\": {\n", + " \"type\": \"polygon\",\n", + " \"shape\": [[41.870881288,-8.8791360], [41.694339317422646, -8.824238614026456], [40.171924585721314, -8.902386975546364], [38.75694209400925, -9.493088042617785], [38.42424252381525, -9.171674240710018], [38.49907333213173, -8.676525850529856], [37.057269459205145, -8.971873318897366], [37.162874354643776, -7.406745406502978], [38.19776118392036, -6.931663452624974], [38.4280922170291, -7.321584397020473], [39.011852875635526, -6.9787177479519755], [39.66227871551288, -7.5393956904523804], [39.66568774825791, -7.03915852435145], [40.0019453234905, -6.883203763416162], [40.20373392742229, -7.035724907677206], [40.350463990828985, -6.8135246275213035], [41.030499770212515, -6.905947651233703], [41.593647729084154, -6.22847017956974], [41.67712153119277, -6.544984134823352], [41.949682257268876, -6.567927092516641], [41.96960294343674, -7.1747800681640115], [41.88337981339092, -7.196871678410446], [41.81334515396762,-8.156666519264604], [42.14242723772878, -8.205142297350534], [41.870881288,-8.8791360]],\n", + " },\n", + "}\n", + "\n", + "ds = earthkit.data.from_source(\"polytope\", \"ecmwf-mars\", request, stream=False, address='polytope.ecmwf.int')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert to xarray" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:    (datetimes: 1, number: 1, steps: 1, points: 1093)\n",
+       "Coordinates:\n",
+       "  * datetimes  (datetimes) <U20 '2024-09-17T12:00:00Z'\n",
+       "  * number     (number) int64 1\n",
+       "  * steps      (steps) int64 0\n",
+       "  * points     (points) int64 0 1 2 3 4 5 6 ... 1087 1088 1089 1090 1091 1092\n",
+       "    x          (points) float64 37.08 37.08 37.08 37.15 ... 42.0 42.0 42.0 42.07\n",
+       "    y          (points) float64 351.1 351.2 351.3 351.1 ... 351.6 351.7 351.7\n",
+       "    z          (points) float64 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0\n",
+       "Data variables:\n",
+       "    2t         (datetimes, number, steps, points) float64 298.7 298.0 ... 297.4\n",
+       "    ssrd       (datetimes, number, steps, points) float64 0.0 0.0 ... 0.0 0.0\n",
+       "Attributes:\n",
+       "    class:          od\n",
+       "    Forecast date:  2024-09-17T12:00:00Z\n",
+       "    domain:         g\n",
+       "    expver:         0079\n",
+       "    levtype:        sfc\n",
+       "    number:         1\n",
+       "    step:           0\n",
+       "    stream:         enfo\n",
+       "    type:           pf\n",
+       "    date:           2024-09-17T12:00:00Z
" + ], + "text/plain": [ + "\n", + "Dimensions: (datetimes: 1, number: 1, steps: 1, points: 1093)\n", + "Coordinates:\n", + " * datetimes (datetimes) " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "da = ds.to_xarray()\n", + "import earthkit.plots\n", + "chart = earthkit.plots.Map(domain=\"Portugal\")\n", + "chart.point_cloud(da['2t'], x=\"y\", y=\"x\")\n", + "\n", + "chart.coastlines()\n", + "chart.borders()\n", + "chart.gridlines()\n", + "\n", + "chart.title(\"{variable_name} (number={number})\")\n", + "\n", + "chart.legend()\n", + "\n", + "chart.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "polytope_venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/Service/Examples/timeseries_example.ipynb b/docs/Service/Examples/timeseries_example.ipynb new file mode 100644 index 000000000..66a5d4913 --- /dev/null +++ b/docs/Service/Examples/timeseries_example.ipynb @@ -0,0 +1,134 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Timeseries Example" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This example demonstrates how to extract a time series for a single point, and visualise it as an interactive meterogram." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import earthkit.data\n", + "\n", + "LOCATION = ((-9.11, 38.79))\n", + "\n", + "request = {\n", + " \"class\": \"od\",\n", + " \"stream\" : \"enfo\",\n", + " \"type\" : \"pf\",\n", + " \"date\" : -1,\n", + " \"time\" : \"0000\",\n", + " \"levtype\" : \"sfc\",\n", + " \"expver\" : \"0001\", \n", + " \"domain\" : \"g\",\n", + " \"param\" : \"164/167/169\",\n", + " \"number\" : \"1/to/50\",\n", + " \"step\": \"0/to/360\",\n", + " \"feature\" : {\n", + " \"type\" : \"timeseries\",\n", + " \"points\": [[LOCATION[0], LOCATION[1]]],\n", + " \"axes\": \"step\",\n", + " },\n", + "}\n", + "\n", + "ds = earthkit.data.from_source(\n", + " \"polytope\",\n", + " \"ecmwf-mars\",\n", + " request,\n", + " stream=False,\n", + " address='polytope.ecmwf.int',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualise \n", + "\n", + "The following visualisation uses [earthkit-plots](https://earthkit-plots.readthedocs.io/en/latest/).\n", + "\n", + "
\n", + " Note: This notebook is rendered in many different ways depending on where you are viewing it (e.g. GitHub, Jupyter, readthedocs etc.). To maximise compatibility with many possible rendering methods, all interactive plots are rendered with chart.show(renderer=\"png\"), which removes all interactivity and only shows a PNG image render.

\n", + " If you are running this notebook in an interactive session yourself and would like to interact with the plots, remove the renderer=\"png\" argument from each call to chart.show().\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def location_to_string(location):\n", + " \"\"\"\n", + " Converts latitude and longitude to a string representation with degrees\n", + " and N/S/E/W.\n", + " \"\"\"\n", + " (lat, lon) = location\n", + " lat_dir = \"N\" if lat >= 0 else \"S\"\n", + " lon_dir = \"E\" if lon >= 0 else \"W\"\n", + " return f\"{abs(lat):.2f}°{lat_dir}, {abs(lon):.2f}°{lon_dir}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+gAAALuCAYAAAA9jTxNAAAgAElEQVR4XuydB3gU1frG301vkIQaQu9IFcWuqKjYsCE27HLBflXs7a9ey1WvXbBgAUXsBRW7IGBBFESUKhB6QmippCf7f76znGV2Mrs7s5sNSXjP89znSnZmzpzfnDlz3vOV43K73W6wkAAJkAAJkAAJkAAJkAAJkAAJkAAJ7FUCLgr0vcqflZMACZAACZAACZAACZAACZAACZCAIkCBzo5AAiRAAiRAAiRAAiRAAiRAAiRAAg2AAAV6A3gIvAUSIAESIAESIAESIAESIAESIAESoEBnHyABEiABEiABEiABEiABEiABEiCBBkCAAr0BPATeAgmQAAmQAAmQAAmQAAmQAAmQAAlQoLMPkAAJkAAJkAAJkAAJkAAJkAAJkEADIECB3gAeAm+BBEiABEiABEiABEiABEiABEiABCjQ2QdIgARIgARIgARIgARIgARIgARIoAEQoEBvAA+Bt0ACJEACJEACJEACJEACJEACJEACFOjsAyRAAiRAAiRAAiRAAiRAAiRAAiTQAAhQoDeAh8BbIAESIAESIAESIAESIAESIAESIAEKdPYBEiABEiABEiABEiABEiABEiABEmgABCjQG8BD4C2QAAmQAAmQAAmQAAmQAAmQAAmQAAU6+wAJkAAJkAAJkAAJkAAJkAAJkAAJNAACFOgN4CHwFkiABEiABEiABEiABEiABEiABEiAAp19gARIgARIgARIgARIgARIgARIgAQaAAEK9AbwEHgLJEACJEACJEACJEACJEACJEACJECBzj5AAiRAAiRAAiRAAiRAAiRAAiRAAg2AAAV6A3gIvAUSIAESIAESIAESIAESIAESIAESoEBnHyABEiABEiABEiABEiABEiABEiCBBkCAAr0BPATeAgmQAAmQAAmQAAmQAAmQAAmQAAlQoLMPkAAJkAAJkAAJkAAJkAAJkAAJkEADIECB3gAeAm+BBEiABEiABEiABEiABEiABEiABCjQ2QdIgARIgARIgARIgARIgARIgARIoAEQoEBvAA+Bt0ACJEACJEACJEACJEACJEACJEACFOjsAyRAAiRAAiRAAiRAAiRAAiRAAiTQAAhQoDeAh8BbIAESIAESIAESIAESIAESIAESIAEKdPYBEiABEiABEiABEiABEiAB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kAAJkIBtAhTotlHxQBIgARIgARIgARIgARIgARIgARKIHAEK9Mix5ZVJgARIgARIgARIgARIgARIgARIwDYBCnTbqHggCZAACZAACZAACZAACZAACZAACUSOAAV65NjyyiRAAiRAAiRAAiRAAiRAAiRAAiRgmwAFum1UPJAESIAESIAESIAESIAESIAESIAEIkeAAj1ybHllEiABEiABEiABEiABEiABEiABErBNgALdNioeSAIkQAIkQAIkQAIkQAIkQAIkQAKRI0CBHjm2vDIJkAAJkAAJkAAJkAAJkAAJkAAJ2CZAgW4bFQ8kARIgARIgARIgARIgARIgARIggcgRoECPHFtemQRIgARIgARIgARIgARIgARIgARsE6BAt42KB5IACZAACZAACZAACZAACZAACZBA5AhQoEeOLa9MAiRAAiRAAiRAAiRAAiRAAiRAArYJUKDbRsUDSYAESIAESIAESIAESIAESIAESCByBCjQI8eWVyYBEiABEiABEiABEiABEiABEiAB2wQo0G2j4oEkQAIkQAIkQAIkQAIkQAIkQAIkEDkCFOiRY8srkwAJkAAJkAAJkAAJkAAJkAAJkIBtAhTotlHxQBIgARIgARIgARIgARIgARIgARKIHAEK9Mix5ZVJgARIgARIIGwCi5aswtoNOTjjxCMRHR0V9vV4ARIgARIgARIggYZLgAK94T4b3hkJkAAJkAAJ4OFnp+LtT2bij29fQXxcLImQAAmQAAmQAAk0YQIU6E344bJpJEACJEACjZ8ABXrjf4ZsAQmQAAmQAAnYJUCBbpcUjyMBEiABEiCBeiYglvMX35iOnflFOGj/PoiJiVZ38PxDNyAxIQ4FRbvw0puf4deFS7EpZzu6dWqHow/fHxedfQKapySpY+fMW4xpH3+H5avWIzY2Bvv364FzTz8Whx7Qt55bw+pIgARIgARIgASCEaBAD0aIv5MACZAACZDAXiLw5cz5mDD5Y6zflIuRpwz1CvTbr70A5eWVGHHJHUq8n3XyUWjbOh2Ll63BvAVL8c4L92Jg3+6Y8t7X+N+L76J750yccPQQdez3cxegX+8ueOmxm/dSq1gtCZAACZAACZCAPwIU6OwbJEACJEACJNCACfhzcX/kuWnKMv7oXeNw2vDDvS344+9/kJnRCm43cPy549G5Q1t88vpD3vj1XSVlWLB4JY4+bFADbjVvjQRIgARIgAT2TQIU6Pvmc2erSYAESIAEGgkBfwL9lItuV5b1+V+8iJTkxFqt+Wb27xh//0RcdcnpuP6KkY2ktbxNEiABEiABEti3CVCg79vPn60nARIgARJo4ASsBLrb7Ub/Yy/H4P498daEuy1b8Pq7X+LJl97Hq0/cisOG9GvgreTtkQAJkAAJkAAJCAEKdPYDEiABEiABEmjABLRAX/D1JJUYTpdh59yEouJS/PL5RMTuTh5nbMbn3/6COx6ZBIlXv+ScExtwC3lrJEACJEACJEACmgAFOvsCCZAACZAACTRgAk+9/D5ee+dLzPrgaZUITpd/3/scZv74ByY/fQcOHtzH+/f8gmK4olzI3rIdo8behwMG9MLU5+/yaeGGzbno1L5tA241b40ESIAESIAE9k0CFOj75nNnq0mABEiABBoJAW0JP2HoEJxy3KFKeF9w1nFYt3ELRo65Fy3SmuG6y89SieGWrFyL19/5Cq89eavK4j7+/hfwzezfVBK54UOHoLS8Ap9/+7NqObO4N5IOwNskARIgARLYpwhQoO9Tj5uNJQESIAESaGwEKiur8OTL7+OjL+aipLRM3b5ODPfbohV46Jk3sWZ9trdZRx0yAA/ccoWytkvG9gmTP8GbH3zj/V0L+vPOGNbYUPB+SYAESIAESKDJE6BAb/KPmA0kARIgARJoCgQqq6qxdXseWqQ194lFl7YVFO3CzrxCZLRpWes3+b2quho5uTsQHxeHVi1SERXlagpI2AYSIAESIAESaHIEKNCb3CNlg0iABEiABEiABEiABEiABEiABBojAQr0xvjUeM8kQAIkQAIkQAIkQAIkQAIkQAJNjgAFepN7pGwQCZAACZAACZAACZAACZAACZBAYyRAgd4YnxrvmQRIgARIgARIgARIgARIgARIoMkRoEBvco+UDSIBEiABEiABEiABEiABEiABEmiMBCjQw3hqLpcLbrc7jCvwVBIgARIgARIgARIgARIgARIgARLwEKBAD6MnUKCHAY+nkgAJkAAJkAAJkAAJkAAJkAAJ+BCgQA/SIWpq3MpKHh0dVetICnS+TSRAAiRAAiRAAiRAAiRAAiRAAnVFgAI9AEkR5vc/OUUd8cAtl1Og11Wv43VIgARIgARIgARIgARIgARIgARqa0w3g6gtu8U3s3/DQ89Mxc78IowacTQFOl8eEiABEiABEiABEiABEiABEiCBiBKgBd0P3pLSchQW78LTkz5AQnwcBXpEuyEvTgIkQAIkQAIkQAIkQAIkQAIkQIEepA/85+k3UV1d7Veg19TUsBc1UQKlpcDmLW60SHOhRbq9Rs5fWIO8fOCk42rnLLB3BR61NwisynJjzdraOzIcfECU32f/9cza7356GnDIgdbPPpQ6hMXOPOC3P2pg59py7M782u3o0dWlsPrrl3bqqI/nohltznGjtKx2jXbb0b2rCz27edocrDh9ZzWrQHXoawZqh9Xz1Nc237Od9uj+aHfs0aydXNt4X4H6oz5O1xHoPTK3Vdph59pO6tBsVlu844kJQPt2LgTi4KQdTscF3Q7dZ+yMOVbt0N8pq3boa5tZ++sr+tun/j/HDcUo0xXwW+ikDmEk77dc21ys2qHHteX/1KCoqPYbffCBUUiM99yjsTh5nzZnu1FaDtitQ187WDuMz9NpHbotTtqhn0Ogvm58v/S1/X075N2Q529sR33Uodtu933S72iwdhj7vNNxQV97U7Yb2RZ9t0+vKDRvBujvlLEvRrIdwb5xe+t3ydPFEjoBCvQg7IIJ9Ly8vNDp74Uz126IxpR3EtC5YzWuGG0xA94L9yRVbsmNQlmFC106Vu+lO6hd7fJ/YvDuJ/E45ogKHHtkpa37eurFRBQURuGB23fZOj6Ug+57LFmdZreO/IIo5Be6kNbcjbRUewtKP/wUCxlcpe2RKtKOhtIPpb2zf46r1dTLLihD107WfVI/B+NJVu2Rd279xmis3RCFdRuia9Wx/4AqpKe6/bK28876u39zZf76jJ06zNd6/e0E1a5AjJz2nVDbIePHV7PiUFbmwpatUaqfp6W6sX//KgweUBXwNpy+s4v+jsH0LwOPC5pNoIr99RUZn83FzhjkdFzQrJ1cO1hf17+H8j7pc6Ud8vxuuqo04HNzUofVu2qHsTzrP5fEIL/ABRlHM9rUICHBjZOHVSCjrfVYandcCIVVqO3w1x+DjQdOGOXIN7y89oRcj5+XX7BnvuG0HXbGhXDfJ6fvrB4zg41xxvHRaR362v7qsnp3ndZRH+0IpQ7jeGBmbPWs7fQR87wpEv3QXIeeS8n/m4t8o+T7JO3R70ko7QjWB/fW72lpaXur6iZRLwV6kMcYTKA3lhB+vSIuK7gfz6hWFoORp0U7sg5Hssc/N6lKWTAf/b9YJCZGsib71/7q+2rIqqesuJ58fG1hZXWlG+70CPln/1t7MLZfc+AjndbRVNpRV/ysrqMZpaW6lPUuZ4sbZeXAdWNj/Fpi9XPo2tmFsjIgJ9etrHD/HhfjU4W+drD799dnxDIw4ZUqy2vra5rv31zX2vUeS5W5jnDGBf3OBmJkvA87ludQ26EZmdtt592NxPuk2bRr60KCSW8H6iu6HQnxQLsMl/LGyS9w2xqDItEOzdNuXzf3R/PzsNNX7LbD33tlVYfx/s33FIixkzrqg1Wo7TD3R3/jgW6D7ocx0UBKioxvnvHQ6n0KZXxz2g5dh7wXCQm+iwBVVUDxLuux18n7pBmlJLsQ4zuEw6oOf2NOoD5f73XE+nooVFW6arEKux0O6tDjmpmR1fdWswr03TT2x1C+HXXZD+U+Zaw2f2d1HZFsR7C5BX9vnAQo0IM8t6Yi0MOZwNZH13Y62Q/lnpy6staHsI1kHdJecY3emedWbtLipt8i3YWRp0bXcgU087Q7SQ7lOZgnspFczAh2f0ZGO/KgXPlEUBUVQ03KOmR63PuuH7tnxlbfH1wnAl0WC7p1rm3FmjnXY+0zsw5nXHD6zuq6xFXywnOsF7z0+xBqO2Ry3bO7C1ty3WrBZG8L9MEDo9DCZETYmQ8s+qvGcsFFM5KFogMHuZC13g0RU3urHeZ39bihUQh0/2aBrp/jwsVuNXmNhEC3U4d+Z+X+zSUQY90fZaElo60Lq9a4lcCxs3AXCVZO26HHN3HJFVfsZilQ4lMWJaToMBDj+CZ/d9IPzYzMfKWv+xMtdp9HoHGhrt4nPZ7ZfWfN4t+J6HRaR7DvmLE/2hG2xoXkcNoR6L6s6tDjmvk8q/HBTjusBLqTb4fT9ymU71PIiwC75yJmVvm73929OW8K1h/5e90QoED3w7G6ugYSX/7Qs1NRVVWN+2++DNHR0YiK2jP5bUz7oDtZEa+bruXsKk4n+86u7jn6/scq1cTE7sAWSfFsnsjamYA7FbahWH+c1lEf7QjlWRvvSxYl/MWFh2P9CXRf4U4cjNeuD4Fux1Km70kvdM1fWK0WfiT+s2W6K2jehfpoR0MRtk4n+5qtE2Fk7n9OF9WcjG/Giey+INB1rOn8BdWY/4cbItAzM4CVqz3W2pEjotXinVWsqV1WWevcWLm6Bquz3JB44bRUID3N5fXe0TG/Jx0XDR3Kqa9ttQi3s8ANmbxbjT3BxlB/C3d23qdwREukBHo4VmGn4jkU0VkfdTj1BAilHfVRhxUrq0U140JRu4zaPf6Pxf6t23XRD6VGq4XwUBcBnL6zwY7n742PAAW6n2f2/mc/4IGn3vD59cHbrsDIU4Z6/9YYBbqdD+7e6Mb1IdAjOYFtiMI2FOtPQ2xHqP1RJtkPPF5pyzVcW8jMdUXa+uPvo67vQ94LcYk2Jmpqn+EJTzGWUCbJcn4ogtCfZSPYwhcFuueJ1ZXFjwLdE4IUCQt6KAt35rEzmAX9u9k1mPFN8JwrTz8ci6jdhv9QvXecjG/Sdumjvy2UeHsXMtsCeQVidXejRzdJvhjlk1AvlLEnVNFiZR21ep8o0N2oj0WAvVVHIIEebL5g/E6F+j7Vh5XeyTsbrM38vXESoEAP47lRoIcBz3QqBXrdx7mbJ06RcDPVj9GJNS7cRQA7brJG8WkVF26+7/r44Dqpw8zI+LrUZZx7OAJdx1cHi2fV906BToFu5921u5DqZHwLVRBaxTzLU7SKNQ1VoKc1dyHdYpeQdRvccLsBK4EuY4m5WMXShyOeA33dw/UQCvV5OBXooViF95boDLTQ0FTaIeEVKSm1e1bx7pAyK1d9pxZ0J+9sQxboocwX6m5Gzis1BAIU6GE8BQr0MOBRoCsCkRS2Tiaw9WmNC1eg2w0HiJQgrMvJpbDwFx8uv+m6ZJJSXOxWFu+GItD1xCnQ/Rv7VaSeh3ExJhQPIbuC0MliFF3c7Vu3NVdhJq7c4vItRay1Vt4i/hbXAi1A7o131q4FvXNHa1f5WT/WKIH+1EOxiN7tMFMf7Qg1ZtaJoKjLduwLwrapCPRgM8a6EOh7qx/6+5bXZV8PNl8Ixpe/Ny4CFOhhPK/GItCdrhKGgUSdamcibq7DqQXdTkbopiY6P/68Wu3LbpzAShvNCX5CmcA2NVZ2+2FDjVszL2IEm+yHYikLVdiaxScFeu0RkwLduUC3+k458X6hQK+dUDCUcaE+BEWosfRNxYIu3keSfNRYJImfeScQs4eTeaQJlFytIXoC6CSe5nZYJV8MNIYGcnGnQA93Bs/zGwoBCvQwngQFujW8+hDodjJCNzXR6TT2lxZ0+9uTBRsGrOLWIplYhgI9tGz0Dc2CHqhfWYnPUEIOZOvM0nLg+Ume/d6vHxeDxHgE3anBifeO3cRn4SwO2vUWCaWO+hCdwlPmBPL/UiSRmxZdOlGlPHOdOV3HoDdUC3okxzenRgOnWdwburC1Oy409HbsrUUACnT7yY6DzW34e8MlQIEexrNpbALd7gc3DCTq1PoU6IEsLA1ZoOv9p9U2OAtrVCZsyTTeIs2ltkOzKlqgywRP9qa1SmBmPI8C3b5ADyVuze77FIoVa18X6GlpQIvU2nG2MjGTEk7W6VDHBSfvrJ0tgupKoFvVZWdcjIRA155Nq7I82cklK3l6KpCdC7WXts74rzOUG59FJBcB6kOgOxWdFOhQCf7MxWksfV27uNeHdVvn7zC2XZKBNnULel256lOgU6CHq1Maw/kU6GE8JQp0a3gU6MEHz1D2n3bqWlwfAr0+91p3IijsLhSFIp7rY7JfHwJdZYgvB2SPZEneI3skyyRRrH4t0wGx+omQMm9R57Qf6rbYGRdCzZ4diuXZzNhuFnrzqGeVE6E+Xdx1XeI+KkW2AdtbAj2U7NlO+7o+3sn4Vh/vrNPYbQp0oC4WOetaoAeaEtbV3t57y/Jc16z2Vjso0IPPMcOQNjy1gRCgQA/jQewrAl3HPo88NTqo26RdYWTGbjcGXVuxxK3z4xnVaN/Os+VUIMuz04m4eQJoNymZnGc34VQo+9I7FUZOJrDGNhvdNE8+PhrpaXC8j3hdZmsO9Xk4EYQNLW7NqWgJZ6Eh0BBoR3wGi0HXVtUdeW7lLSIeIoccGO2zVZP5GYulRfqdufjLGB+KQLdyDZf6rPa2No5rdvaM3xsCXSbLUsSzZm8LdPFIaZdR2zqas8WtFoWsxoemYkG3Kzop0BumQG+I1m1ZfOvVo/Z4+M9qz4Kc3eRqFOi+wrY+F+7sjguhfMvDkDE8tQEToEAP4+HsKwLdrnjWKO0Io1AFeiiW54Ys0J3EzNaHQA81UZPes9Mq2Yu/V8zuYgYFumciG2j/bPNHvapyF0p2ZaN5Wk+Fz0pEG2N+zc9oS65buVvWhUB38s6GOjkJRaA7zengpI59XaA7cWV1uhhlHg8iuQ96JBfuKNAbpkDfW1bhxrKfe1VVFSa+Vor16yuxX+9qNE+pRE1NJeLiUxEb28zyO1XXsfS0oNOCHoZ0azSnUqCH8ago0K3h1YdAt2PFMt9dpAWh1Ge3jlAm+4G6aqCtt+xMYM2TZDnHKibOfA+hWOnra8HETj8MRRDWx6q7U9FibseGtTOQt2MpevW7AomJrQMKdLsr+/qenC4U6eegrapWMab+RJe5v/mz1jt5n8ztEOuUuPgH2ttaznFSR1MR6E5j6UOZiDvt6xTodSNsgy3c2R0X6nMMtcpN0ZCyn+ftWIK0Fn3gcsWgLjOs6/mOeTyUrTerqn09UpyOPeZ3tjB/FbZk/wK3uxI11ZUoK6+Eu6YKMTGVqK6uDDhjbtVmMBJSjsJfy1J8vHhCGReYxd1ZotQwpAxPbaAEKNDDeDCNTaAfMMjK5dCTmCSQG3dDtKA7sTzXlyBsSgI9mMU2lElyfS+YNGaB7jThlHGS3K71dqxY8rLCHRvXHL37jcHceYnq3+Fkow9XoNt5Z0OZ7DsVz/XRDqeTZH1PThYBrNohf6tLF3cKdPuTZKcLd7SgO19oCDRd25vx4QcMBNat/gj5eSsQHZOI1m2HIGf7QcgrSGhE7udVaN38W+zY9mcYs2I5NQrl1QPRpt1RuPHqFupaFOjO+7oT750wHxhPb6AE6kWg52zdiX/WbMQ/WRuxfNUGVFZWok+PTujRtQN6duuAbp3aNVA8gW+rsQn0QK2hQK9Nx2lSskgLdLuud+GI50jGgTYGgV5dVaomWLoEcg2vDwuT3XfWKGxryt5BUUGWaoe0Jym5HTbtuJwC3QQzVE8AOwsNTU2g2x17QpmI68cSybGnLvfdlvuti3GBAt25aLHKTbHXs583B1o2+wxiPfctMUqonjPqCAwe6BGqTseFUN6nUOp46bXNaB7/MeDOQ1R0HDp0Go7EpDaIiorF0pUxKCyKxbjLErBfr3i/7dhVvBk5m+agqDBrN4YoDBo0AMcccwy27UzFhFeq4CT0RbfDKqv+zgI38vPhY1gyL+6K272Eeenyx2LPDiDXjY32/m3CK54tEY3Gq8TENurb2Zjc6BulgOJNByUQUYFeWFyC51/7CG9/MtPzEgzohY6ZrREdHY2crTuweOkalJSWYdgRg3HXvy9Cu7Ytg95wQzqgsQl0GRzNRba+kaQ9jUGgh5p1WjKNi1ut3p9WEp9JkTYHKhTowa1xTc3FPXvTD9ieuwCp6b3RotUA/PZnJwAuS8uz7HNsLnYmDuZz6mKyr59Dx3ZrsSvvHURHx6PPgHFYs/IdlJVuR0V1HxRXjqQF3QCfAr0aX8+ssTX2NxWBHmi8N34DQ/Hk2BsW9NKSLUhMylDNakyCwswqP285mqf2RFRUjON27N3EZ5VIT/oSrprFiIlNRq++lwPuGmzJ/gk7t4tgr4HME/v06YMjjzwSH85ogzVr3Yjk++REoLvdbnz6+U9YvGi2uldZzO3a42zExe/J0OnUVT83NxfrsuYgLvof1S+l/Z279MPilUeiWbOWONDCkzNQHaG8s53al2H1irdRWpLjWDJERycgs+MwFJYOhiQlre9xIZSxx3EjeUKjIBAxgT5n3mLc89irSEpMwHWXn4VhRx6A5KQEHyiVlVX4ZcFSvDT1M/y1bA3uvuFiiOO/swAAIABJREFUjD7ruEYBTg88MsA19OJ04mBuT0NxcXfq9qvb4TQZlD6PAn3fE+hL/nwOlRWF3legxp2Miuq+uP7q/ZGZman+7rQf6n4kloB2nrm0T9Er++G4n3vqqEar5FdQU70d7TudgDYZh6CivAArlryC6uoylFYdg0cfPNpbt9NxQW+pJ9uySdynbMsmsduy+CWlZzfPgsX1Y2N82ufEbTvUyYmTOszjgp4sB8tG76QOJ5NkIywndVi1Q/4WCRf3SAoK3Y76sKDXxb7bcr91sagWrgW9sqIIy/56ES1bD0KHzic6FrZ11Y5Q3lnjs96a8ys2b/weMTHJaJt5GIrLD8Ta9dG2hdHeFOiTJ09HfMxfHnG+36WIT/BYyqUs/LMAlaW/ICluMaqrq9TfYuI6Iq/4UPTv3xstTLtU1Hc7Ljm3GB999BE2bdqk3NKro45E955HqgVpYwk1U3zXTnno1PYnLFmyBJ55sgvuqN7o2+8YJCS28qkjkEC3yqpvlcdE98MuHYtRVvgmKsrzERObgoREj+FPLO5Seuz+Tsl/r87yzN8lt4GUyvIClJd7DoyOyUBeySk4/thMaKNOuH3dp9E2FtW6dqrGP8unok3bg5Desr86Pdh3ylwH/914CURMoF9/97PKIn7TuHORmBAXkFB1dQ3e/XQmXnvnS8z64OlGQ7OxWdC1S+7O7X9h+9ZF6NX3EmStR61VQvMDaGgCXW8nZLxPO1mn9UDvb6smfT29lZsSJAtrcPCBnr2g63IrNycT8VAn+6FYt+1Okr2MFlRj/h9uCNvMDGDl7m1fRo6IRodMF9pnuJC4x2Pcp2vZTainT3K6YOI0Br1d661KzMoKukx683YsQ2VlkfeeW7RogUGDBmH6N31RXZMKp5P9YANbuAL9h9mLkBz7BeLiUtF34DVwRXk8RYqLNuKfZW8CcOOCC85H79691d+dCvT62KM8lAmQtMXJ+6Sfw75qQRdWYsVblVWjJqgyYe3ZLUrteS9b4BmL07EnFJdcXZ/dscc8HthJghlqX3cSB+q0jnAF+qrlU1FctF7hEIvfrorDGo3FT7M6eP8NWL3iLZ8+53IloaTyUBx11EE4dbjHqBNoXKhvYfvvcZ4FyGnvzMDqfxbCjST0G3iZjziX37Xo/NfF5cjN/hW///47Kioq1LmxcS3Rrv1hykvL5fKM0/XZjo4ZS1FV+jXKy8uRnJKG7B1notrtWYD2V0Ldyi0/Px8zvpiLNav/AuBxKRcPtcwOItRbq387tdL78xb5blYu0hLfhrtml1oE6NH7IsTGpag67Cyqud012LplPrZsnqsy08uiQpuMwbj80hOQkJAQsB/arcPIN5jXS5zre2zbMh9JyZnKO0M0BwV6wG7apH6MmEBfvGwNBvXt7ghWKOc4qqCOD26MAr2mphpL/3wWVVUl6NJ9JPKK92t0At1u7K9+3KFOxM3dxc5+6MFEp3lP6IQEFzLbAnkFYom0niw7nSTr+46kQPe3dZaZWaD90IOxMl8r0gI9IXo2crN/RotWA9G52+mq+rk/rUNs1BKkpaxAmQQ77i5VNZno0nUA0lv0VdYTXQJ9cCWLuTxvc7HKHu50sj/jmzL8Pu95RLlK0LXHKJVJ2Fh++vkPJMd+iZiYGIwZMwYZGRkhC3S9pZ65HWK5lWJcaJB/OxHPFOhun1hNq/5k5m4cH+S3YBZ0fwstVu+q07GHAt1+XHU4An1Qn8XYuO4LFS9cU+0RfUmpZ2BTbj/bludICAqrsB9/wijatRMtkiajurpcjbfJKR2Rs3mO2n1CFhNjYhIxdOhhOOSQQzBzbrQKx7BaMKlPYasT0X399deYP38+atxJQNzFOHCwR2gai1l0ihh+duIv2FW4QI3TUmJiktC+03HKpXxXaRKWrkhFt66x0IsAobxPgd7Z7TvKseafzxAXvVLVP3jwYAza/yR8+nUUZG6yM98N+U4lJrhQtDtDfPt2LiQmAGePiEb7TM/3K5Rx4YVXC5Ca9AuiahbB7fZ4FLRsvT86dB6ORX/Hql007C4CWI2L732UheV/vweXqwJJKe3Ro/doFeqlix2Bro+VhfkVy75BVfkKz7uVlIQTTjgBOdsH+O2Hdf0+dcrMRfHO1+ByRaHPgKuQsNs7gwK91qvWZP8QMYHeZIkZGtYYBbrcfm7OL8jeOEutXsY3H0eBbuqs+qMYia3cQhG2oXwMJSFLsGI1IbdrxTIzMtdltf2L+ZiGJtBL8p5X7u3de52P5mk91O0aP4ZLly5V7norVng+2ro0a94VbdsdpizWW7YmY/3mFvU+SX518g/YvGEuomPbY+BgT0I4Y5F2JMV+g4TohUhJScFVV12Fux7yeDbZXfCqD/FcH3VoLqEu3DX2JHGasU64lbPFk4eEAj2qXl1ZQxXo0a5CpCe+rCx8nbudBriisX7NdGXtK6oYheOO7V2v7Qjlnb3hzmKkxr2K6KgCtGpzIDp2Odk7XK3O2okduXMRF+0R6mK5bNXmECxZNQSdOyXAvAhQ3wK9V6fvlTiPi0vA1qJL0Lx5a0dx1VnrKtGtw58oLvjVJ5xKAIgneFR0Cjp1bAnx2HKjBX76LRVJyS0weGBLlbTNWJxYnsWTKmvVx6iuKoLLlYjzzjvD600l13SyoB/qnETGnEH9StVCuFiqpcje6RWuEcgr7BqyQJdv8vvvfwCxgMfE90S/AWerfAbmb6D824mn2saNa9G6+ZcoL/O4vSenZCJ7xyno1KltrX5oni/oup0utnueQxVaJb+qwtUyOxyLtplHeJtCgV5retNk/7BXBbrEpRTtKkXzlKRGCbixCfQ9kMuRHv+8WmksrBiNqpouthIFBbKGGh+gHddi8wO340bvdKCrj4m4uR3BRKd5T2jz+VaT5VA/hsFeqroQ6E6ysjplZT4+khb0Tu1zUbzjNURFxWHgATd73cOtPoY33VWMuOjlaJO+BMVFGywwuxAX3xyZ7dLVJGvbjjQsX52GjIx0dOvSwmdVvy4+6rt27cJTTz2DmpoqpLS4Aj171HZV9LSjBoN6TsPGjRtVPP2SrIvEhkOBPlDCV4LH9jnxBHD6zupO5KQOq/FN/mbXgm7HNdxpO0Kx+FlNZK1El7/xwE47nH47QhOdnj2i7S54hSbQK9AsbipiozYhpVln9NzvYlXn9q0LsXHdV3C7o9B34MU4d2QX9ff6aIfTOmTed+99UxAbvUFZzaUNYiXURVtHhx1ZBHflXPz1118qhrnGHY/ElIPQu8+hKgxJl0ACPSXZhV6etVafEmpcdWarH1FW/CPi4+Nx3AmX4a0Pgyc2DmQVlnBD+YaUl+WhrDQfVVUFAT/b4q2VktIRiclt1XHZsrhWBhxxaALat4tXixlfz4rF5pw49OmVhJYt4hHlisWW7B+Rm/OrWvCorO6MVu3Owo1Xp/rUVV8CXSeJKy3ZivVZn6K0JFfdR3n1AFw4+mT06+OJibO7D3qr1D/w5ZdfqnPKqg5Am8yT0L1L7QTATizoci3dD08c5kbzxN8wd+5ctQOV2+1CtetARCVIPpc9Fno5R8e5O1kEsEpEN2fOHCTG/IjEpLZqm1Tj+0GBHvAVaVI/7lWBvnV7Po4ddSN++XwiUpvtcRNtLIQbr0AHEmN+QGLMPFRUd0Nx5fkU6IZOF8okWZ9uV6A7EbYNcS/icCbidlmZx4FICvR2LWejfNcvPu7tUn+wj7pY3PN2LoNMNsrLdqKkJE/FvwUqsoVLQkJLdOl+FuLiU4PWYb6W2b3vs88+w6JFi1BR3Q+tM88MuLL/6P9VYdKkSZC4QJ3Z3a6gcDoR1/ft5H0y11FRUaC4ipeCv+chf3dSh76vfd2CbkfYUqA3rH3Qb7vnFyTHfqcWEvsOvBqxcc28w8PyZXNRVjxX/TZ27OUqjCWUdzbSixnaPbza3Qz7HzhOubIbi3l8k7Fq2ts/YPs2T1Z0aZ/sMy5eSzKWBhLoweaSdl2qpY7lS39CUuxsJc4vvfRSFJdmqHCUYMVJHYv+qkSXjgU49fgC5OXlIWvtDixZtgNxMflwoQButyeGO5QiFuW0Vsdh9YYDYdwzXl+rvgW6p163WjjYvHEOXKhCQkIyzjzzNGXZtyPQ+3WfjZzNv6grdewyDItXHGoZBmHnWx7sO1tUVITJb3yJvB0eDzpJIFtadTzKq/vVehzhCPSPPtuCv/+YBJcL6NN/HBKTfMMnKNBD6f2N8xwK9DCeW2MR6FYT2NVri1G49Tm4XDXK3bVtW8+KrFWxY902nkcLeu04XM0nFGG7rwt0HRtnTtoXKAGdsc9bTUbMk5KWSRPgrilEt17nI3W3e3uoH/V16ysw9LAC7NczHzt37sTiv7cjO3sn4uPyUVMtSec8mWMl7lASv8yd55mghvJRP3DgDrz44osq0dDO0muQmtYMLSy2U5RJr65jx44dePnll5U1oLTqKBx++J7M7ppLsOQ1dmNNrcaeQHHV5kniquVvqsWPPv3H+l3McFqHbmN9CvTCgtVISspQWYUDWYfDXWiQttGCHuPdUUA/60iLTqnHaR1OLegyljz33AtwuarRudsZKsGYsch7lbv5SyTELEJiYiLGjh2LXxc2r7eYWTsJ9cQa/sknn8DtjkFhxeU45qja8w6/CcB+yEOr5j+iskySjUmitVQlXqJiOmBjdgd06ty5Vuy2DlMzz2usQrACCcJ1a+cjb9t3gCsW48Zejnbt2nkv6eSdDXfBSxYsZWeO6qpSFbu/bkMZysrKMXhAOZISK1TSt5WrStXfEhLK4HJXqB085FvTtecolJS19Ds+hCLQrfYolx0+cnLdPosAQec9f+5ETdl0xER59i3v378/Nm0bjqz1CZZb0q1ZV41tOZ8hPnqpSpx25plnYvPWfvXS12f+sAbNEr4Cajxu7wlJndG63dmIik5SbEP9lkumePEUeeLJl1GyKxfxSYejb/9htabkFOhWKqVp/i1iAv3Xhcsw5ubHbVGjBd0WprAOsvqIbM3+EvHRizBgwACMHDnS7/Up0N1B4/Tt7rUe9EO12O0oWUpdxd9ZTWSdCAlz57GKjTMfE8zbQB/vJKmVsQ47C0Vy7e9+yEFqnCRjiUOnnjf7uJPV3RZoexIclZVuQ9Y/76O8PE9lms3JvwRud0JIAn3HlnewZs0aZHY4AktW1xbaZuZ6EWD16tV4661papW+S4+RKtmdebJfF3vAOhXPxkliavJf2JD1uWcSlNhaufr98JPHdTGcRHS6nfUp0Jf/9ZKyfvXsexmKdiXXmiSHkpvCqh3yNwr0pifQo6LcePXVV5Gdna283g45bHSt77VH2NagR4fPsHP7UjRv3hzde4/BzB8TI2ZVlJvQ72ywbSRvvjoHb7zxBmpqalBUcTYqa3rbDgcwjgsd2hUiZ9NcFOT/o4SqsXTs2BGdO3dGXEIXfDQjA6mp8Y7iw632KN+WuxCb1kv4QCyat7oQ46/r7FNnfQp0O99Zp4sA5u+sE8+aQJNS48K4nXlPQaEbw4cuxl9/fq8WGlxRSSgqPwn9+vX12ZJOQrmW/PUuqivWqYXpiy4ajW7dutWvt0inGiTHzVf73UsuCNler9d+l2HOPE/oRSiL7SLQ582bh2+//RbVNelIz7gS3bv4xtHLtSnQw5JCjerkiAn0gqJdmDh5OqZ9/B0uHjVcbblmLkVFJXjxzU/p4l4PXcbqI7J+Qx7S4l9QtY8fPx7Nmu1xl5O/hbp/uB1hZG6ynUUApxaK+piIm+swtyuYkAj1gysTk2DufU7c6D/+vBqbt8jWSx5La3oaUFUFFBVLRld4t0wbeZpnSxg7H1xzVlZzW50KdJ0F3U4COuM9BrOgz5kzC4kxv6C8eiB2VY6wfBtD/eAaJ7BGC5PsYbxy2WSVJKiqpp3KBfHsfz3bwUix09cPH7IRK5ZMVXGHp591E9ZtiFbbZq1e60ZaqjxDl1pYkiI7EEjR+7nKf99+zxzlsilJpjI7XYa4hD2btOds8VhBjDsXhOIua9VX7FjQO3csxa6dL6CmutzLJL1FP6zJPkP92/g8ZOyQJGeyP7vsyy77s0tcplhyWqYDLdJdGHnqngzE9TEuGCfJcdEbIJ4AUuITWqJN+8uxeGmcpYXJsvMZ/hgskZscSoHe9AT6r7/+jO+//x417gQUlF+JYUN9v9Xy3PfEzAL52z7AqlWrkJjYCtl5l6BL58QIJ7XyWA79lSgUIiPtVbUTxlFHHYVPvztKHRpueI3EL+fmrkPulizExWwE3J6M9lIkVtgV1Rat23RESvPOaNass/pmSrGbXE2Lc5crBvllF6JLlw5eK72uZ18U6GKQ2JnnVhZrKfJtk/FWtruVuYPaljbdpf7f6htg7ifG55HRuhjTp09HVlaWOiwxuSe69xqB2NhktSCzeuXbKNmVA7c7Ef33H41RZ3ZQx+nvU0KC7JZSuyfWVXy4cTcB+X7L/ZSVbldje27BxSqzfyjzhcOGFGHixImoqqpCQfml6NSpg+13Nth3g783TgIRE+iCo6KiEoOHj8UHk+5H316epCXGwhj0+us0/j4iPdp/gp07lqutTE466SSfG3Iq0PXe2Juz3fh4RjVkew4RdHb2D28qAj3YXuuhCFvfyf5GbFz7Bdxwo1ffy1BYnOjXGudEoNenG73uZE4Fup2VfWMHtrNQJBONb758Vrm3F1aci+jYHpZbzOhtb+T6dsSzHWFbXp6Pf5ZNQVVlMSpr2uP+ey9T26DZq6MG7dNfQWnpdpx66qkYMmSIzyRFswq02i7tSIn9GHHRK1Q8XUH5GLixZ5FALminHZq5v7qcTGD1JKtt6nRUli9Ds9Ruar/clUtfV9WUVA1HWdUQywlQoNE0mLCNZJK4wu0fqzwFusTFZ2BLwUXo3jU+rO2U9PVC3WbNzvvk1BoXyvhmHg9EtO3NJHGlpaX46rt8/DI/H21aFaF5SiEqygtRU1OBthmHIrlZx7DzRkib7bq433VjPiZNesmR5Xn4scCUKVOwadMmtQDYIuMSdO/imwFc7iFYjg3zOxXaNpIVSI2bjOio7ejRowdGjx6NG+/yxG5bhcrsLHCrZFt2xx5jXzn71FysXbsWS5dlISd7I1yuPTHiEruelOJxTS8u9ixAqy3EdofAywKfLOylpMj2Z56U6pLATTKnt2k/GstWtbeM3XYyvtXH++S0Dv2Mnbi4O31nQxkXnnh2MYrzvoXLVaIWVtq1H6oyv1eU58MV1Qx5pRdh+LCWtXYsCDarDkU864Vtq0XqqqpSrFr+hhLp1TVpKKy4FM/8N817G3bnC1s2vokNGzagbbuDsHztCY68XoK1mb83TgIRFeiC5IUp03HWKUPRrk2LWoSKikswccp03PCvUUhM8Gz505hKY45B1x/aoYdux5I/JylhIFZ0iV2zmvg5mcCan6Gd/cObikAfHCQjdCgfKmGzdt0utG89C7sKPfF3UhIS26B15iV+rXGhCPSe3VxISfHdq1ss1nLfTl3W6tqCbkdQGCcbMnH7bWENWqTLin60d2Xf2D+3bNmi4rFl8ra9ZDy6do5Wk8ZALvp2P7iBPur6HsrKdmLp4tcR5SpTbnoXXnghoqKigi4CbN70J1Jiv1BZ4q+99lp1jhTzBCKYQAeqkJ74BlzuXLhdbeGOvVTFWZaVebbespokp6UhaJy7kbGTCazc//c/rEbzuHchVqu+g65BXFxzbMtdgE3rv1ZWsaKKC/HkI91rTYDk3TOXVWvcKN7lu79uuOObk23WZEuhjWueVlX27ncFslZ9uNtroj1aZV6IG670jLehjAtW7ZC/0YIe3IK+q2gjysp2QGJ6d+woQElJIZolFaK6ukjlZghUUpp3Qfb2o1FV0z4kS5keF+wJdDf6dHodW7fmqrjcuQtOV7dmx/Ks9t1+7nWVwyEmriv6D7rAJ3xHrlOXAt0qBl1ian/79X3ERa9Cy5YtMW7cOMTFxXnHt0CcQxHovvuHlyOtWTYyW69DcdF67Cre7DjJmrhQ9+hzISqqO/l9r5yMb07FcyjjgtM6jN9Mo3XYSZhasEW1UNuRJfOeVt+hpEi23NPznlaITb4Q6zcl+3yfJFfNjjw3VK6aP2qgQi7auvDP7m/AWadGK09AmePoUlffcrNIv+3WMWo7Uyl26jig32KsW/OF8mLt1e9qfDc7hgI94Ci8b/wYcYHelDE2BYEuH8HtOW8rd6JjjjkGRx+9J4411BjNUPYPp0D3vClWH8X/PTMfuwp+gAsed98WrQZhV9F6iAU2LiETufmj0S1Ma5zTj3ooH1yjoJDYZ3HHltKjmwuS8E27z5vHjHBW9o3XsnJ1nzlzJn766Se0ajMI/2w41ftRrC+BLvc3+8ctauukKFc59ttvP5xzzjleC5PVRHzNugrk505AlKtEWaN69uzpbaZzgQ4cfVgJli95VVnyxU0vNi5FWZLkf7K4Ia6KUsSlUSZBxuJGHIorTofbsN1MsLCOQC7uM74pxe/zXkSUqwjtOx6PNu0O9Va3Pktia/9Sbr633HyNNyQn0AQo0HMMdXxzItC7tv8RBTt+RGp6L3Trea56Z1cumYLq6mLExHXE7bdeohZHw32fdOyswKJADyzQxS16xZJX/E5N5HnExKaiaFdz9f+JCc1UWkd3TTEqShd5z6us6YHrrj5OZUu3OxE3ik47Aj0h+ickxsxFUlISrrvuOtzxH4+HjR2BLsd99lURFvw6BdFRO5Ga3gdde5ytkmrpEmmBnrNpjtriS7ZIu/GGK5Genq6qlnFKxhIRUhK61C7Dhbx8yb8C9OjqUt+EHt2ivGIqUHiN0zwsEjP859/VyC+swpgLo9CpQ43yTpjyTgU2ba5G755uNE+pgbumGrKtmcQXO8nHEmh8q4/vrNM6dF8I5zsbKYGux7UYVxZk/E9IaIFuvc7D+k1xfvMCOWmH/nYE0il2F4pEpC9aMBUxUVuRlpaGMWM8Ij2YQF+3vhitkl9SSf8uuugi/LO2i+Nkd4Hun781XgIU6GE8u6Yi0Ht3W4+pUz2xrLfccguioz1xxpGcwJqxR0Kgy6RXBngdL6UzjuqM1mLJ0PFSxvtxsiKuz7PLyslEPCk+G7KF1tatW1U18Qkd0LXHqSpzrdE9uqomE60yLwrLGuf0o+6kHcYVa+NE1sg8WJx4qCv7YtURoWnOKqvrfvrpp1FYWIjefS/AvD+67hWBLpPkaFcOWqW8pax3gwYNwg/zT/U7EdfbKTVr3hnjb7rM51UKRaDL5Kpk1xb8s2yyYwuTVB4d2wnJ6Rdi3QbPxN8o0NVEfLcnQ0KCC5ltgbwCqMm4TMJ7dotSnhm6j7z0ylfIzf4NUTEZGDj4Ch+Ln0yYF/w2BTFROUoUyQRIxFTDFejVaJUk+9OXoUfvC9Es1bNVXO7Wndi0dopaYOnatavymshaH6W2bHLi9WI19sjfKNADC/RVK6aiuHA9klPaIyk5E8Uladi2szkOGZKGU4a3UGLYX2LKKFexEszx0eLJ5Im/lS2hhg0bhoef9ohPu+I5mECf81MOmsW+DpfL7V2ICzbZt0rs+M3MArRImgJ3TRFatj4Anbqe4h0zIinQJaxj3eqPxVMcRZWj8eTDexYS5QacfGfrUqBL3XZj0DUoCnRfrzrNxdgfnTAyz//sPA/JRxIV7dl33E4eEzsed3Up0OW+Zs4tRfM4X5F+98Oee/Y3LmzLfg9x0avVd1+y0Yea68XMlP9u/AQo0MN4hk1FoItQfeGFF7Bt2zaccsopOOiggxQVu6JTI3TywdXnSLx6aTmgkpTluDFyhCeZk6yim0sokxOdxMTfY7YShqG0wy4rO8K2sLAEB/T7AVmr/1S3HRXdHIVlx6Ffv34+2UzFPXrl0imoqS4J2xq3NwS6uCRbuc+bn5WTFXEnEwft3i4ul4OG3IJvZnmS3dh1cRdXb3PRlme7q+6ej7pnon/rtbkqbrS6ulrFWUu8tfmjXlm5C0sWPQ+3uwoDDrgSZ5++J7GbXCNUgW5sR011BdZtrMb6jdU49kg3hh4OZWGa83MVfpxXiQ6ZbrRrW4Pq6lKsW/2JWvlv2XoQVm30LCpYxfgFGmY1q5ycHLVHu7ixN2s1Fj27t6l12qy5RUiNfxVRrl3K5ffss892LNCd5lsIZXyTOjZt+BvJsZ+pLY76DbrO2xaZyC7+eyvSE96E212m4nKHHHI+XnithgJ990Q2UjHoQwasQdaq9+CKikH//W9Qe3AHiquWBRNZxDWXDRvzkRA9Gwkxy9TWSFIqqnujtOpoHHNU7X5rVUcgge5212Dh/JcRHbUDAwcOwllnnanqCPUb2LlDPkryJ6skWy1aDUR8gmcxIWud595PPn5PeMhX33vGo25d9nyDY2KSVajJ1p2p2LA5FScOi6sV+2t0cTcu+JVUHY+yqoPD2nmBAt0VdjZ6eaaB3qtIfWelXjvzHnNIXKA5SV0JdCfzBavvq3lc8HzLy7Ff57eQm5urLOlrt1wKN5ItBfqKlUtRWvCJ8tS4efx1ykhGgR5otrBv/UaBHsbzbkoCfdmyZfjggw+QmpqKG264QbnB2RWdoUxg9TlOEtGFOjmxmmQFsqruTYH+x6KFcFf8oGKSxTp45JFH4s8VhyBrXbTlfqC5uduxaZ1Y48qUNU5cpNasczm2xu0NgR7MLU73kUhNHGbNmoUff/wR+++/P+KSR/i4ldlxcbcjOu1/1D3CVkJNpk2bpgRxSeVQHHHEUJ9qNqz9Aju2LUJ59SAcNfQ0n6zsxrp0nLhx/3Pz/Yb6Phkn4mKJXLVymvj/oqTyaJRVH2Ep0OUcc8nLh9pSUAT6icNceOmll9QiYVnVYWjbfpjfDLbRri1IT5yiGJ188smY9slgdWkrC0Ugy0yg5xfuwp28T9s2v4qYqFx07HwSWrX1JPEzTpI7d9iKiqKpakuhDh17469VZyEtNdq/BBgpAAAgAElEQVT2RNw8huoY/MZmQXe6g0Qo44Knr9egXerLanvDdh2OQUbmkepSgQR6sL29/++WQkiYzPLly3fflgvpLfuiXftjvCLYXx2BBHr2ptnIlS2c3Cm4/bbrkJTkscLpdzaU5GqZbbZ5FnRr9mQ6D3X6Ex2dgNat09R8Ia+wGdZvTEOrVqno0D5VJVVbtWKaCplJa9EXWdmexYVwQl8o0CnQjX21YQt04PH7azB58mQl0nXiOPPOC9VVZfh70US43aXo0WcULjyvn2oiBXqoo1LTO2+fE+jbdxYgOSmxTpLSNSWBLlaAZ599FgUFBRg1apSy1tanQE9JdqnMqTJht/qYGycndl0InX7U9eu9NwS6JLDZuO4rlJZsUbfRqXNfnD3yRLWXbTDx/NffW5Ca8AbgrlTuloOHnOPYGhesDvOkP5QVcc3XrltcKBNxJ3Vo93ZxMV61rqttga63onv+FU+G4AMGRqnt6FZl1SAzw4WzT4tGy3SXit8O5YMr2yNNm/aOcm3dVXkSyqsPUNeJdm1D8ziJnY1Bfvm1OPG4Zn4FuvlTZZ4c1+X7lLdjKdat+URVWVQxEk88PMBbvd1FgJT4XyALJnHxqdhSMA5dO8cF3GLm8vOW4tNPP1ULiflll6Da3d6xQNcJHY2snLhpBpokPjVxA4q2T1aJ7gYecDOiovdk0DbWMWrEVjWRk611yqv3Q2ziWThw/9rJ7uzG0ktbGptAD9WjwcnCnfTD+OhFSI79CjGxKcqjISrKE88djkDX75V440x48XvERq3xdiexVHfsfKJyyw1kQW/bxqXGDV3Ky7KxZYNn1wLZfvF/D/bE7hyQYSdXK9mVjYK8Vd661m7Q2zB6wtqkfD2zWv1/106778kFyLaQksl+164ClUjPheAiPym5HXr3G+N332Yn31mn3/JQvk9Ov4Gal5N2OK2jPtvh5H1y8p2VY+u6HQ1doMu4IAuvb775JrKzs5VIH3TgZYiN3bNLyro1nyJvx9+oqO6BI4eeH9AjxfiN4j7o5tlN0/33PiPQN2zOxVW3P4X1m3LV0xx5ylD83/hLESsZzSzKYxPfwZsffOPzy+D+PfHWhLu9f2tKAl0atXDhQsyYMQNt2rTB1VdfXa8CPVj2c7k/u5N9O9mz62oirjuD3cUM/aGSBYlePWQnlwrsyP0OxQUed3bJpF1cfiKuHNPFG5dr56PeuX02SgveUhN9Efd/rjwDaalRtq1xduqoqyzuDUGgG93bb7/9dnwzy7Onqx0Xd6eTk1BWxG+9ZylSYj9UVe2qPBPl1X2REvsu4qKzUFo1FKVVR/pksDVOFldn1ai8C2KhPvgA2Y/Wd/9zq/s3D4FORUvO5rnYsnku3O5ojBlzKTp27KguaeedPfrwPCxbPEm59vfpfzF+WdDRVgbbr776Cr/99pvad1a2iBs2NLXWSG4nttF4Ul2NC/99/GNUlP6NZmkHoUevE33uy1zH+vXr8eabU1FTUw13VH8MPvAMn0ReamxeLAm0gmejl2Mbq0C3u4OEhulEUNxwZwnS4iequP/O3U5Xbt66OO3rcp6/2O1o12ZktpyFXcUbvdePjW0GRHdEflFHHHpwF4zcHZaiLei+nbYaqXHi2p6PsqrBKKk6GU89FIvdaWGUdU2Kcf9pO8nVLCc5hj863XrqhGMqcND+xWpBf95vBVixMg/NUgoRF1OIysoilWCtT/8xajGkLrdftPJocJokzt/75PQbaBxzjfkjmCTOFXSveHN/dDpONwaBLm2sqKjAgw978qZIqJNsjSsivbAgC2tWvv3/7J0HeFRF18f/m00vhARCDb2ovIiCioJiARuKIKigqCgKFpqAooifigX7KyBiQ2wo+AqIiA2VoggCCoiiIC2UQEiA9J5s9nvOLHfZ3NzdvXO3sOXc5+FR2HvvzPxm7sz8Z86cA5hikFd2X63FdiPzBXffN/8enATCRqDfM+kVJCbEYdrkkTiScxyD730KT0wYhuuu7KlZcy+8Ph8HD+fg4VG32H+PiYlCk7ST4eJCTaDTBPnVV19FaWkphg0bhmU/tBBO1vSIZ4Iks5KsQNUrbNWTfZoE7N6xAI2anIcGaWdj737U8eopu+quxHGneKgUx51E9GntgcNHbE7Gzu9mwvnnmoXHcSUanezuj8LIVv5q1Iv+GJERh2G1xqO0+hJUWM4GYIJj3Ga9E4cBVx0Szv6oHissnREV11/3bpzeNGqHsTHm1CoQBLpi3t61a1f079+/jlmZN0PMGBlwbTt+28QZZrqapl+GrMxViIyMR3zqWOw7YNYU6OrvSiv+t3KPHvEsc5Z+/a9LEWP+S5yjGzFihAirpCeNFg0/FCGQyElOTOJ1uj3YCs/LH3yAgwcPorqmMc7tfhdMEbUXXGUnft4Q6BRH+6WXXhZnk5u3GY3GjU6YUpwAr5XGmrV7sOKH+TCZakR/1rJNv1rjUtAJ9HolaN86CyVFh1BRnosmzS9CXHxjrzrnkhHokx9fgbjIXxAb1xCnd7631gKINwU6VRpZeBUX7sORw2tRVJhRZ35BoUxbtmyJiuqW+HN7c0RGNQM5UKTLVP0jTJYNsKIe8srvARCN6dOi7DvoWt+uHrHibnoqK9Bl+gUW6Cfp+2OclU1DyZ3M96TVDmX6znAR6FTO8Y+WICn6EyHSY2Lqo91pQ7Fr+zyxkBWf3B+Z2Z01w5nKfLPu7uXfg5OAXwU6mZc/+cr7eOi+IWjTsqnfiBUUlaDndaPF7jftgtM1beY8HMnJxaxpDzgV6PmFxXhhCg2S2leoCXQq5dq1a/Hjjz+iXbt2yC29OSAF+iU9K7Dznw9QUX5cVAyFhoqMuxwHs9ppdnSyq+7uGqaWeHb1jNbOMxluJMUsgKkmA1Ykwhp9N2BKFE7Tqi0wJNBJPNMZ5o8//liIA2vE2eh2bu2JPuXTG6LFiMma7KDuy4mDo3k7Oeny5eTEqECn8p91+lYc3Pe1vXm1bHMdCkq7OA0xo9zoi6gI7stRjaSoTxFl3ifOpt57771uw0IdziSz429AomXcuHFY9UuUboFOZSUx/PyLb8JsKhK7orQ76nh5o60r79O7APnzzz9j1apVqLS0RYczhtZy7Ejvcrbj9/bcHUiMXggTrGjU5AI0b3m5vSgBLdBbV+Lay48IU85/d2bi4MHDIG/ntS8TGjU5H9l5lyKvIMJw/+b4Tr3fbElJCV5+eTpMJgvan347KPqB4+Vtga4eC2gBNjLiECJNB5EQd0iYi9e+opBUL12cWT+Ws1n81LHTHdiwpbnwgO64g67Vh+oR6O7O0msJ9G5n1fUbkXVisZoF+ska1Nsv0BOy4tnIOCubhi/HWWd9p/obke2nZdp8ICy205GQ5g0+QWlJlr3oSfXaICJuqNONJXfzUK1ja+6e4d+Di4BfBfqBQznoe+vDmP/G4zirUzu/kdqz7xD63/kYVi+egbQGNpes8xZ9j6XL12LRnKc080E76N//9Bsu6NYJKclJ6H1RN5zTpWOte0NRoNO5GdpFJ9Oceg1HYl9mWsDtoDdN+RAVZYdgNtN5HisslhJRL1U16eja7WrcMKC5+LvsDroyGCpx3NUNw1PxTO+zpVGJlPiFMNXsETuiHTrdKeJ70iU7UGlN9leu3o6fVy8UZ5gbNjoXLVpfXaso3kjDyMRBa3LpbW/NetIgxy3kkIx2eidNmoSIiIiAFei0G3c0+zdk7l9u3/3LOFDXWkTdVk+NQCcPtpU4veVHIjRgeno6/tw9VJyZ1/IbsXtvEQqOviXivw8aNAhnnnmmIQc5E6ccRr1oCkdlQYvWfdGw0Tl2HN5o684mmVqTRNrVp/6TRGFR5WB07txRt0AXZrKJOxFRvVj0a02a9ULT9Evc9guuBlItZ3d6ha2WoKAY0jTJpD95uYdQXERiPF8jC7FIqtcM8QkUZcCKo9m/g56FqT6KKvrh3rvbSh3h8aQcFKpyy5YtqKpph+4XnLSIUzLta4HuCIeEbc/zioXVx9pfDyAz86CIm6yEbKN70xp3R3qrK7FyTc0pFeiu2hUL9JN0WKDbrEZ4B9350RdqLZdeWI3dO+aJvpN8k3Q6axQys5K8Es/db2KKE/IrgbAQ6Fu27cJtY6Zh3bLZSE5KEIA/W7Yab320FCsXTtcEvuz7ddiXeQQx0VHY9m8GVqzZjFenjsJVl3a3308CPTc3168VZjSxjANmfPhpXJ3HL72wEvTH8Vq5ch22bfsdpshOOFZ8PTq0M6Feok080jX8ljLx39YtbOfh6Fq9Nhr5BRH4Y1ukMNdzjHfcuqVF3NuqhQVtWp58hp57f0Ec9h886aFcMYeb+rB6BwaY+lICEqMWI9q8EzXWOBRWUviKJMSaNyDWvB4mk60cHTt2FN7Pf9+aKvLlaged8qSUR2HkLhbxHTeX2cuhzr8jR2XAckyD4h0v/nw5YszbQZ5w6UwSmV0qlyIoPEmDyrHgswwkRH8B2gNxthvnaRrUnmRYKWWc+pLNUYrjoO7ISN1IqQ4d61GLkfoZV2n8+uuv+O2334QjxD59+tjbrzfTcMyPOv/qvGq1ecf80/1ZmT8hKbk1EpNa2R1OaX27yruVdulYx64YqX9TRItjGnrL8fCYHCxYsECIVAo9VVw1CH0uruvr46+ti1BdsUOU6a7hAzTrQS+r6Ih/kBj9BWCKQMczhiEhMV086o3vScmDun/QYkQO/uhsvCkiCcdLx6BrF7NTge6s72mbTk73vhDJpre8EmlNurssh5qR49+1viuZ78mxf0tOLMfff84WobpqXyY0aJAqYtNHxaTjp/WtUS+pIRx3YCsrCrB/75coLtovHm3ZqjOu6XsRKMShbB+qpK2nHMePHxdREWgnurByJC7t1bgOLk/auuM4pf5mHRPSSmPN+mis+DlajItNGmahpDgT5aVH0aLNtcKBnSLQH3+w2H4GXasP1Xq3M0Z6vye6j/p29VVebkV5BYmNk/MGvf2CekzX8z3pKYfWOOuPsVymX1D3y64cVPp7TiLzPbkaQ7TqwRkj9Xtk+2mZNu/pfMExr0baumO/YLFUirPnKQ3+g7TG57kcy/X0b676/kD4LSWl9vGuQMhTMOUhLAS6soP+0+cz0TDV5kjI3Q66uhInP/cO8guK8NaLD9p/IoFOOybBcNEq7+x3a4tjyjethNMfx2vZd0XYvGGmWNXPrxgrRLDWNeM5mxdcusZPsXm0dnVppTVrjkXTjN7x3co7pzyxHLGRdD4vEtaoYUDEyWMSFRWlMFl+QVzkZlitFpjNZqQ1Phfb916E1q1i63iEdlztHTvSJh4URu5E5+gRZvvujzr/juXXSuOT+cuwZ/cWWBGFMzrfKc5lOl7KYOJJGvZyJG5DRLVyhvlkWCGvppHsOvyLYznUbUW96q7Ug7oNkUMkLQduWu/Wk8brr78uFtbIezsd5aDL22k4lkF5d9PGJjStHbZc3Lb5xMKX1vfkLmKB+ttV0lXapV5GauaOu4pKGmpG6meUhQYqx7Fjx/Duu+8KS5yy6p7o2bN3rdsL8nZi767PYLVGout5o9D/Gptlk0wa6ro+vc0q5GT9KqxSTj/zXkRFJdiFrSffk5KOun/Q2nl97733xO5oXNLlOHS0u2ZoRD1OrXKPbcX+vctE0knJ7VBYPhB5BdF+NQ2ntB3NZYtyv0TusT9tfhASmsEc1QIHspqhZYtmGHdvvL0PdXSYpW4jmzdvAeicNSoQHx+PAQMG4LvVbWuNAc76UMX/hfK7HksAOu6zZ88elFefhdLqazUtOby9g+7um1WcmLoKs0ZlNLKDru4PPPme3JXDSL/gWLeuvieZcpyqsVxPv6Auh+x8wR9zEqUcvhwDvV0OrfHJH+XwxvdUY6lEhDlaZNfb5VD3t6f676SR+DJOwK8Cnc6gPztjHh4YccMpP4P+zPSPkHMsz+kZdDXSGXMWYdOfOzFv1hT7T8Fk4u44mNAESstk0HHis+bnr0VYGqv5XFgja3sh1gqFpjiDchfvWJmcKGnpdRKneJi3Wk1od9oQJNdvX6uKlI6ud69ioOpnbN26VZzDrrHGIr7eRTj99PNgMp3cxdMzSVa3AU/NZb/55huxc0uixBp9G87patvlc7xcpUECLy629v1l5TYHds48rLdqulmEb6MrIamF8CCaV9gIxaWNcNuQpji7i22FU/bcGg24b7ybj+Sk42jR9DjKy46iqqoYKamdxOqwnjji7sziZCbiao7OHNENGXC8jnk7Patnsq83DWdCwl037YmjJvW7T52J+8l4x+Sd/P33PxKOz+jsfIO0s0Q2aRfhn62zUV1dgtKqPrj00h4ehZhR6rp3LxN2//uJcNBFl9kcA4s1DeWVaeh1YVP854xGYpf37Q/NhgShO1NWiuH+xhtvIDIyEokNxmPv/mjDAp3yX1S4Hxm7FsJiKYcVySisuAn3j2jmN9Nwx37h9Hb7kHNovnBgeUaX+8WRHKN9aEFBIU5v/RWyj9gcqEXFnoajBX1x1pmJuo4D6O0X9u3bhw8//FAs1h4rGQMrEkJeoKvHV/d+I2wbDJ70PUbSoDTdfU+OfZrR42ruFtuNOGNV9+8y5VD65Y7tTEg8GXFLFLW4GNi5x/lYfo6GTwBP5ySuGHvz7LaRI3Gu5iTBdgadOLtb8HL37eqpD3dzDP49uAj4VaCfSjQjHnoZ9RITMG3yiDpe3IuKSzF8wou4+5Zr0Lf3+SKb099ZiP5X9kTL9Cb4d88BDB//IkYMvRb33n6dvRihLNC/X5mL+jFviLMynbs+gMjIk+bxzkLMyHRCyoDrrk3QxGH79u347LPPbINYZT/0uog8nde+1B027eDNX/Aj8nL/FTdGRyejWYveQjzSZXRyqQ51pFfYnnXaWqxevRoREWbkl92CxHqtpEOguWLlKgRazpH1OHTgR83HY2JiRFi97ONpyC9sjHZtm4i/R0ScjNucnXMMO3cdRVrqcXRokyt2R3NyjqG6WjsWLrGuqDkP+cVnYfTIBLugUDLgSy/uSngxJRxR21YmKIsYqSkmNGv4Ew4f/AWK93b1ZD82FqA/NFmqrq7trE8NUG85KE/0JzeP2p0VsTFAXKwJRSccAjZvalt4cZz46fF+7ngOVJ03Xwp0d5YAjpP9SY9ttZmegxbWbkG95LZiwehYziaYI5vgaPFduh07Uhnd9T01lgoczlyNsrIclJXmaJhjkxV8IiqrG6FBg8ZITW2E2LhGdksWmXOU6j6H4rL/8ccfOOecc7D38NVSiwDOJrCVlYXI2LUIFL+aQthd2Ks/ruhjCxGmt+8xsvOstCdbGpVIS3wLluoiNG1+MZo0v9grfWhJ4VYsX74c5eW0ABGLtCZXoEVL2yKOcrmqD1eLau3bAG+++SZo0eSSSy7BkuUXilfqnSQbEZ2y36wvdtDdTfLV/YS778ndOEu/G2FFz8kI21AT6EbHcvVzwSbQXZWbftO7YMICnXem3bWlUPg9bAR6xoEsEQc9M+uoqLfrr74IUx+kmISRKCgsQc/+o/F/42/HLdfbzqQOufcpcfZcuej+xycMQ2yMzTSFrlAW6GTK0zh5CaoqttealFG5vTmou/uIJo3Oxvvvvy+OEpRV9xJ/ZCZZP646iJSEFbBUZYqk6Lx3ZFSCEF8kwkiMpTezdXYizFqWFZGRsK9um2BCvfod0LBRV2z5K6pOLGI9k+TmjTahrHC5aC+X9bkVi75q6fbstuNAZRN3Vnts6yYndtIz9tt8ApBQI/F5/jm2owpak306N1pamo3yshwczMyBpSoHsdHORTaFAyFRVVGR56aK4hCfkIKYmBRERSciL3c7qioLxTNWROP0M7qi71U9hFfvQ4etKKuwCQu6aEegsBjYtccKEqk3XGdGXAzQ/ER9KAnL7G67C3uXHP0mzBF5uO222+zm7ZSOswUjvWHKvO3sTnayr64kXwp0d9+sejcuLnIt4iJ/Eos+Ldv2w77dS0TbSmwwEgcONfSqQFfnbfPWIhQX5eCSnsdhqcoWzuuyxBhQ+0iOcNZ4xjCUVjR0GkfclaCgM7n//e9/RYjDUaNGYcGSFEMCnUI7dqxtHARYa7Bn1w8wW38XxevWrRuuueYazJ5rlUrD6PeUdfAHxEZuFN847Z6bTLZ+xhuLnMXFxZj5+heortgj3pmQ2AKt2w0QMYOdpaGnHCT+acGEzOjHjx+Ph56wPSUzdjgeq1G3K2+MgSzQyWHqyVCdMqLLsT680Q7pfXrGci3LQ5mFhs+XWZCZZRXzDFo4pu89KhLIK7CN5e3bmJDe1IRB1508dkeMHOckjmXXWkg2Wg6ZcVbJg95F6trhZZ2PICzQT7IxUh/uxmb+PbgIhI1AV6ol+2ieiIeeEK+yFdaoN9pZzysoQlqDFMTFnhTmyq2hLtBbNj+K4uNzhDMz2kVXdlW9MTnRs+oeYcpFk+QPxA4LxUheteFaw5OshvX34vDBlSgrzTb0hdKZIQu6oKC0O0aNaKjbzPTvbbSDuEyI88GDB8McfVqtSYk6M3rDKaXW114ooffJmJOlpRYI0fLFV1nIz89BYvxRVFUeF6JAucyRSSivrI+EhBRc1KMhUlNTUVxaH58uSUa9erEqSwAr8nN3IGPvBqDGtihCZT/jjDOQmXMB9mVqHMJ2gOCJt2bHSRbt8tLii+OVl5eNiMp3YY6MxZRHbd7blYt2t4/nWUETqMNHrBh4rVksFNBkSYl5r64rvZMT5TmZATcQBbpiCeBqckll1bIEaJ/+lTi/rFyNm/ZEec1lTkPMGAkLpSW+nO0w7dt3HO3bHIMZ2SguPijM4kmkN0q/A9t2pGgeAXI1Ea8Xt16Ep2zdujXuuOMO6cm+nglsVMR21I/7ChZLlbByqcBgZByop9uM3kg7fHV2JgqPzgUdJezYaTgSEm0RMujypjDKPPAnkmJ+QE1NubDaIu/1jZv2cOkZ2tn3dP9dwLIvZqC0tBT9+vUTFg2y35Oe8YkYeGIazgI9/AS68u3oPdqnp1+gd+oVtkYtUjq01d6x9eUYyCbuFk3fO642DQxNbvmhgCUQdgLdmzUR6gKdJsk15fNRVJCBFq2uRsPG5wp8/hDoK38uRr2Y92A2FaJNmzZix3PCYzYnd57uglit1TieZ8Fff1ejVcsa3DU0Qux8Zey34OPPKhETW4O0FBKoFOamGpXl/6Kq/E9YrTaT7ubpHdD7sgvQtm1blxPxgwf/xtHDS8Tk9oYbbkDnzp2lxLN6UJQd1D05f0c77bTLSbF58woi6+wq6lkEoBBMZ3bciIMHttudKVbVNIcpqgciIilk4clBX1g0lNQ+f0c77p9/bRHWA8fzyCzcZvFQdML8nCwf6N8GnRDTjgJdy0vuP3+vQkXJWqQ17opR99WOl62eOOkZBH05OZEVFOp+zRc76LKTS7r/5PlwCO+1RYX7xHGTM7rch30HI0+ZQN+TYbULW/JVsX/vUuQd34YIczxyS29Dm9ZptRYaqCzOBPpVvU34d9ssFBYWikU4WoyS3cXSG+Jx2JACrFuzwBY9xBSDoorr0blzB6+e3VbqmayWpj3/JmqqjyGpfne073hlrWbmSqC7G2e1BEWXTqUozP0ahfm7xOOxcWlIaTQQf//bUHPBxJlAv/T8dfhz62qxkDh69GixECf7PckcwVLKKpsGC3QW6Mo4pTWnonZFVn2045552IolX1vEjjudXye/M/Sne7cIYTnnuJAs2/co7VdmAVmrzXvbiowFOgt0d+NIqP/OAt2DGg4Hgd6w/j7s+fcTManudNYYsRvqa4FO8XI3bfwAkRHZwqnT8OHDRTge2QmQ0XNrWk2CvA7HRm5FtHmjWDSgKy0tDaWV5yEzuzPO7hJda5JckL8be3f+Txh6xyf3x6TxXcUzeoSt+py7rDAykobsoK6kQYK5WeO6q+uHs60oL7et7DdJK8bGjRuxdt0mWGtsIZqoPTVqegEaNDxLeDT19mRfS6Bv3TQbNZY8nNZpKG6+qYPml69H2PpjciLb1j0R6PVt1sS1Lqo7+qN1zl3vQpGjQCdfAEAVCo+/j4R6V8Mc1RK5BVbk59dOw5OdS61y6DUBdRTpNdZ41G90OyaMqm3t4Uygn3/2LuzasRD16tUT5tTURxr9nvQsqrVKr8LixYuxc+dOET6sfoML0bb9pbUWvLyxU/bzzz9j1apVqLEmolWH0WiYetIvBdWtr77Z/Nx/cHD/clRXlYg2WVbdHc1aXFFnwURLUBQUFKNhwmxYLNUYOnQoOnSwfeey3xMLdM8XwpUOxZn4lDENNzqW6/melIVw2W9WKZ9MOWTHciNpGC0HC3T38dw9GZ/0bix5smDigZzhRwOQAAt0DyolHAQ6Tax3bHsXZaVHNEnRpDQpKQkZBxNgtSahdaskREUlifPIcfGNxP/LhLGxWmuEsC0s2AOLtR4mP3yvOEfoySTLVRx0Ledq5MSraZO6ojPriC0GbP8rd2F/xnoRTokuqzUOyalno2Xr7qK8xUUHsGv7R+K30qqr0LzlufbJpRHxLDuoy4hno5MTY6Z35cg8sBX1E36zmdEDiDDHoGFaV0THn48//0nQ9EZPuwYd2tWtDzq7Trvuesz76GjDjm1zhFf/8y+ciGuuqC021IzDaQfdVRfoLYGupGFCpfBN4Hg5puHJBMhVOfS0ERLpO7cvRmnxDpgi4jFm9N1iF9bdJLlV409QVLAfffr0wUUXXSRul50kG+kXXp6+DiUFK4SX/MSklmjd/gYRWo4uTwU6xQ+fPXu2iIRRWHkL0tLaSkeQ8MTrNDn7yzzwPY4f3Xqin6iPYbdfj1atWtnrQ0tQVJV+ixjzZnHfnXfeab9XVqAbmSTLpsE76L7fQXc3vdPTL7j7nligQ4Sx1Yooo+YvswgQyDvotBicmlx3TkJzXbo8OfpipO9x18759+Ak4HOBXlhciv6xqiUAACAASURBVJ/W/YFDR44hN592Hk2on5yIM09vgwu6dRJO2oL1CheBXlGRj9LiTJBH4cqKfBzOKoA5oggJcYXifLirKyq6HqymZigqScc53dJx00DbBMvZRHz/Xlus3RprHAor78D0507GCZedAOnZBXHl/VxdLvV51sOHD+ODeWtRVf6P/Vbyzl5cuF/8PSXtcuw52N1pCDS9E1jl5Xp3Lo2JZ/LWfNLs17HsrnbK3C1mOJsARZn2IDtrPYoKTzhiNEWgovp0pDXpiQmjbOdcjYgWZ4N6VuZPOHJ4DSosZ6PXxf3sYb3UdRxOO+jE153pZIMUE1Jtkfjsl952SA/Ihl/0RKCT1YT6crWIoxW2sLSsBsV5ixBt3oWEhATcdddddpGuNRHffyAH9WPIT4cZDz74IOJOOCzwh0CnNPbtO4SU+IWosZQI55dtOwwW58Q9EegkyufOnYtDhw4hOq4LjuT1c9nHe9KH0otdsTp8+AAOH/gS5oh8kQeKvnDVVVeBok/UmexvOQ5T5Vswmay4//77xTl95ZIdO5TnZASFbBos0FmguzNxV9qhzCKAbN9jpK2rvyvHDsJdCF9H54syPncc05BxKKgnPJne42p65pSUTy2B3k0jTF7WEVuYXK2FcJm+x2UHzT8GLQGfCvTlqzdi4tQ37HDiTwRxLqWlNgD099nPjUf3rqcHJcBQF+h6wilVV1cjLy8PL87Mg9lUgDYtC4UXbxLyJcUUGqi2t2Sq6GbNmqHa2gx7D6SjadPmaN/WpgCOHP4FWZmrhZOg/PJhsFibeLQSqacz9cbkMmNfEVo13YTiws32sE5NmvVCTOIlhs5ue8vE3Yh4dvUheoOVehGgvOwYKAxc7rFt9rZCu189evRARFRHaYd6ziYnFHebPNIXVd6CPpe1Y4F+oqJlJn5K2zAi0PWa93ki0GWcxFE7dH5RBIvFqKqoLdK1WGUf+gaxkVtw9tlnY8CAAU4XMRzT8qZzNSpHl05lyD+6WFjuwBSB9JZXwBxznlNv9O4mfr/99hu++eYbsdgQU28U9u6PgZbTRVqbpcmlL/oFhRex+uOvKjRt8AsqS38VviwSExPRt29f7D98Wi0nSpt//x9MNbvQpm0XDLt9YK3qlRXPRkSLbBqBLtBtx1JqX946lkJvlel7ZE3c/SlsZcphpA+VZeVPgU71QheJbrrUEWXU7cdd3+N4fyDuoFNd795bg9303wwr6icDKfVNUKwryScALWg7hjtU+gVXcysW6EEpAX2eaZ8J9AOHctD31ocx6JqLcdfNfdG6RRNxNo+uaosFu/Zm4uU3PsVfOzKwduksREdrm5z6nIAHCYS6QHeHRo8ZD8Ui3n/gEPJys5CafAilJXW9qNPOT0xMKkqKbSbjbTsOwe9/thP/78lKpOyg7umObUpyNY4f/QvV1SVo0uwil2c0NcMpAdi52+YsTcvEWq8w8qQcrurclxPxY7ll2LnjdyREb0JNTbHIRlJSKo7knof4xLPR7ay6/YPeGLCKebvJFIvjZeNxdZ9IFugnKtrXk0tZ0eJPge5KdLZtbUWDhM+wZ88e+0768fyUWgtGezLKUZAzAyZTNe655x40bdrU/vnITpI9+WZtu3BWHD64CtlZ60Qe4hI64NDxAWjXJkbX2W1lUbBxwyK8/vrrqKqqwo033ogVa08zZFlj5Owv9YmRUbUXTqqrTHbnkbcMzMOSJUuQlZUlyhif2B6Zx65Bk8ZJSE3OxJEDH4pY8T0uHot2beqJkFXKJdsOledkBIVsGoEu0PUKCiPfrKzolB3LlbzL9G+y36yRNJRn9I7lRtIwWg6Ztq5uG0rbd5yvabUfmTQCUaB70i9Qn6i+ysttxyZZoLtTG+H5u88E+pJv1+C51z7B+q/egNlc1/SQcGdlH8flQx7EojlP4YwOJ8+WBUtVhLpApx3Y2Ni6nQpN5pyJZ3eD+hWXWsUEa8WqTOzcnYm46MOosdjMF+lq0foaNGzUzaUjOndpKKuXsoO655Pk2jnzlhMl9aCumOZqnXfSmvioeekVtspz3t7xcz0Rt+KS87dj/fr1OHJE8XsQh8ZNuyKtCZ3xT7QXR285FPP2qLiuyM7rqzkYqhmHwxl02UmymhF5E05MBDZvrdsfKPfKihYjk31XaehtI1ptffTdJixYsMAu0ntfPhwf/q8eFPG5ffsGlBf9gMSkdDw48e5an5nsJNmTvqfW92TZA1PVEtBZf0tNClIaDcaE0bWd3bmaJK9f+zH27t0rnKuRkzV/lsNVv+64OPj1txvx28YVoow11hiUW3ojOmKzcCpaVt0DZdWXOR2f9FpyeDIR15tGoAp0ah8UdnLj5hooVlh5+VbkF9jidLdvS38i7KFGjXyzsn2PkoaWVR85gty5p7Ylh1J/LNDdz0mMtHX1txoqAp3M0F21JyOsZMdAI2m46jv5t+Al4DOBrpi3//HjXERFmjUJKbvsn771pDiTHmxXqAt0I7GIjYjnVukVKC0+hOrqcqQ0+I94hZbnV5os0ukIisNMTSopkRyT2FYgU+vbzspSyBH6Q1cgCnQKHbb4K4vbctzQzxaH2/FSJstqxupVa08m+1rez70t0PVOxNf+ug/ffPsros077Y+kNT4P9VPPQFx8Y/yxLRrq4wBagkIxb09IGYqDWa1ZoDtUgMwEVi3Q3bVD+l12cqJ8sxQdQB3Hnt5HXt/p0mO9Q/d5ItApnjuFX/z444+xb98+xMYm4EjB7ahXrwG6dQG2bnoN1poitOs4ELfd0kXzWz0V31OEKR+JUQsRGXEUQCRuumkgOnXqZM+fM4F+3RU7sHbN54iKisK4ceOEKbk/BTo5rExMrN3nFRdbhSm02npn9pxcJEZ/i6iIPfZyWRELa/QY5BfanBDqbSPeOs8q29YDVaATO5l+wZ8CXe/YodwnUw7Ztm4kDXUfGuxn0JXyhIpAd2xf3jpLL9svKHmQsTZw9V3wb8FLwGcC/fCRY7ji5ofQp1c33H3LtWjXqhniaDkWQElZObbv2o9X3/oMRSWl+HzuM4iNqe3ZNxiQskA/aXYs2wn5c1CX9eIeGQmxM6i+XIVscmeeSZN9x0tm4qA8t2FTjYgJ7njei35zPO+kNblSl8NT0eJpGjIT8dfnVKN+vQI0TtkoPDrX1Nhi0ZOzSaspDRVVLXB577bocX5r4e1fPckqK8vBjr/egdkci4QGE7DvgIkFukODMNIOP19mEbF5abGJFseaNTGBfKOp2zglY7RfcNf/6xVf3mjr5Gdj/vz5yMjIQI01AYi5Ax1a52LPzk9BIdkuuGhinW9QdrLvyaKa1vdUVFSFrMxliDHbHFh2795dOFejmOBaE7+CghI0rvc2KivL0K9fP5xzzjniOX+Ww8hiRruWO5C5bzmqqoqR3uoq0AKeN8KAGpkky7Z1Fuj6ncTR2Ed/aHGeFulpvCX/wnknrPloZ59igQ+6rvZmkEz/JtvWlTYik4byDJu4m4Tn90BxEic2f8qAw0eswmqENke02pM/+gUjabgbL/n34CTgM4FOOH5cswmPPjcHilM4NaLGaSl456WH0L6NzWtzsF2hLtD1OIlT6kx2cmJkp0xLGOrZ/dBrFueJ93NXbVdrJdbIoK5m7ey8lyeTfSOTZL3e6GUnQOpyWCwVKMzfhcKCDBQX7kNlZUEt7BSXvqC4GfIKW6Jjx9ZolJaMw5mrkX34FzRI64rqiGuQsV/bY6p64sQm7hFOz+nLsJLtF8i09nieFYuXWcRkqUPbCCSRGf2fNidEY0faFrqU8ID0/74ycXdccCCRPufdj5GTvR8wJSI+IVlY/ZRVX4xLLrnYqUDX2y/46ptt0WQryou+E5YA5JxzyJAhWLsxoZZzNZokV5UuRYx5G1q2bInhw4fbs+3pN6suvzcWTNSsKCRb1uFf0LxFH5EcC/ST1E/1Qriz+pAdy9V9TiDtPBsZy1mgB5ZAd2yPrnbOlXYos7stOwYaSSPYtBPnVx8Bnwp0ygI5hPt967/CKVxRcakIR9MgpR5apTdGtzM7Oj2fri/7p/auUBfo7ujq3cVyFQddJg3ZQV3WizutoNLOoLvQU7SyeiKakn2HSe+usFJeI4N6qAh0rfBWWjFU3YmWzX/koqw0A6e3249jR/ehtLS0VnOiEH8UUUAIutNvQ05eKxboqg/Ok3aoJySd0cmJzATWqEBXztA7ItE6f3hy4a4KSdELEBWRKR6xWk3IrxgHK2yxx7VCCp5qgU6TzSEDjgsLgMLCQpgjYxGffCP2H2opvA+nJANHsvciFguEB/izzxmNXj1T7aH1gkGgqxmfaoHuqs4dnUHxDrr+HXS1QHfnh8XIOCvb1o2kEcgLDepz/q4c1qrbuC9N3PV+T54KW6WfZ4HublbOv/uLgM8Fur8KcirSCVWBrsecjHg77jDJTsQdB4Mmjes6ottyYsfM3flqVzvoesoRTGZxoSLQfSVasrOz8dH8PcjP24uYyIOwWm3m8GTefma3icg4YGKB7kSgK1EFXMVlVddboAn0+vXrtixXx1Jk26Ht/irUi/4EkRGHUWHphJKq6+2vOVWRF/RYvZSXl+ODDz9D9pEMcTyEdv7LLRfSEjqSo99AhKkYpdW9UV59geZCg540CIQyyTVyTEg2DWee4lmgn2zZobqDrv529VqTuZoveCrQlTZP4Qfpj5Zn7mAQ6Gq2MtZkvvDiblSg03iQmmzC4WyrqA935QhUga74Y9Eay06F7uE0/UfAZwJ981+70O3MDlIlMfKMVAJevjlUBbqRQcSoQJdxROc4AaROq1ljE7TisjoTEnq8TjumoUwAfTmo60lDXR53q9Xudp59YWaqzqNWGuoz9FT3tHN+JNuKlPo2B3+pKSa7kz9PyxEbeQiFhRmIiIhEoyYXwFU9qtu8u8Gc7nds81qO9NRMnFl0aKUl+z0ZEc+ObV39vKvJpQwro+UwsoPuqnt3ZGw/Q6/zPGvdoxaV2LX9I7RofTUSEtOlzlFqiUetPkfP9yQrKKgcc9//CXGRP4nXR0a3gTkyBRWlmxFhboji6hEor4jwikB3N9RqWRsEu0BXyqy3X/DnDrrMcTXZMdAfiwAKW2UMof/m5QNacacd256MhZAzJ6yO73N1XE2mD5Xp32TrQ7ZfcGSr8CW2NCbTeKzlsFZ2TqLcr7QVGfGs93tSp6HOo7sxPVAFumw53PW9/HvwEPCZQB/72EykNUzBQ/cNQXyczTmcs4vM4D9e9AM+WrQcKxdODxp64SLQFQdoWh6U1ZMSV5XnOOn3dFA3MhiqnwmEVfdwEuiyE1hv78axQK/7ddqjCqgc5DhGQ3D2TQfKDjq1E5ljKUp59E6StRaKyFlhRITNsamMoyN3At3d4OcVYZt0AFE1i2CxlJ9IzoTT/nM3tu9urCsqgpJHrUUp5ZgQTfQpTJcQho1NIgxWcYkVA681I/2EAyb1MSEW6LVrf+WaGlitwKvPRsGsCoTjKFr0+GFx1648sVTzdCw3Mgbq6XuMCltXrLQEutLmN55o87Ro0P2cCDRIsUWW0bqU/AeSqb6ST6U+9SzSqsd0vTvoaiauxLOsQKf+evdem1NBx0Wc888xO60Px7YiY+KuZ3fb6CK14o+FFpTJHwv1nYrzOqXvdPdd8+/BTcBnAn3Nhj8x5fk5iIqKxNi7BqH3Rd2QnGQ7q6dcFZVV+GXDX3jzo6XCq/uTE+/A4P62GKbBcIWLQFfXhVYnrHRCrurNGwKdOq0Nm07GaVV2Xh3jsqrzoOyY7cmwxWumDpguLa/Tjh21nl16o6vVavGpxyxO72Do6c5zqspU2Fth1tT5p3jBrnaePXHapzXZP9UCXSm/nsml8j1pmW3raSt60nD8TmR2D4yUQ29saPW7feEMSjYNI9+T0TTcjXteEejJJnTpVIy9OxeitOQwGjftiWYtenscks4x7zJOlGT7UHf1capM3GX7N3/soOs55qU1Fjrre7VEm6cC3VfHaxzHcpk09C7cqb9VGWHrbLfem5sGen29eFIOvXMS5T5FPNN/aT5GczHy3+NKPMsKdJnxyegYGIjWcO7GDv49+Aj4TKATiuKSMrzxwRf4cOFyQaZLp3Zo3qQhoqMicTj7OP7+d5/w8H7VpefhkdFDQV7dg+kKdYFOu2ul5YB6Bc/Rg7JWB6dnRdzooO7Y0VMILj2rnUYGEXq3+tKanCgDrV6HU+r860lDfY8vTdxdfX/qWMTESOasqboeSHxWV5nEzpor00GaXHVoV9dPwa4Tu3J6RYuvBLojMz3tUY94ll3wUtebnjSMTk5kJkBGdw9kJ8kypqzq/LtbBHAnCL2xg67uF/T0od4QthTloFn6pSJ5V0dfZPs3IwJdtu8xcgadQjupL1fHpGTKoe7f3C1AekOgUx8qs2Dnrq0bGZ+MjuUyiwBG+zcjacj2PUreZAS63pCpnvQLer8n9X0y5ZCdWxlhFYgCXWZ32+gYKDPOBpNu4rzqJ+BTga5kI+dYPnZlZGLnnoP4d+9BVFZWoWPbFujYNl2EWGvZvLH+HAfQnaEu0GU7CGUw1LPzrPYYqq7WzVttu93uVpT1CCLZQUTZpVev8mrt0hs9t2bELE5vOZR6UHYN1Gy1vLPKlsPI7rY6/4750hOKTl0O2bP0wSTQiS9ds04sFHXrEoGiYnLCVSPijt9wHUXDcG9C6e7cnXoCKPM96VkEMDo5kZ0kh5pA17Pj5w2B7vhNufqeZCf7MmdNjfY9RgS6q3Lo2R12tRhzKgS6Y3m86ZRMpl8wKtB9fbyGyqCkkZtnFebOyrlqV0d4ZPseI6JT71iurgc9C3eyvl7U3wQLdJNT60o1K5kxsNtZdRcH9Thl1ZNGAMkizooXCfhFoHsxvwH1qmAU6Ir4dHTG5Q6q3g5CZrVaTwi0UyXQZQZcWYdT/hgMvSGe3e3+GDlr6jjZoP9XrBRIRMbHQpyvcryM7Fy6Ei3eFuiUV1+ZhstO9o1MHLRY+0qgy05OZCfJRo6M6E3DSDtU2OpNQz0RV9enN6x3jJRD6d92nzgelJJsQlU1WcdZEUffbFMTtCJhyJiAyrZ1d+XQMnGn/ND13Yoa8V8tB5VaC7BKOfScNZUthyc76MSAxOeSry1iwW6QzgU7vY5S1e3RVb9gVKDLpOFp/yYjOmW/WZn5groc7qzh1Iz0CHTZduiPOYknaQTiDrq6f9dzht7VPFvP4prexXZ383n+PXgIsED3oK6CUaAbKa5egS5zPtzo2ThPBvUXZ1YLZxvuHJkYGXAVRnoc6jnWgczEQT3wOivHyV0DIDffitgYIC7WhKJiK6ottkk1Ta61vLPKDoZGTED1lsPdRNzVjp/W+TsZj/96B0MjAl3Z4XDntIY4ydaHzMTBsR0aKYeefsGoqb7RM5p6hK2akTuzX08WvGQn+zI7ft7eeXa1OyzbDn151tRdv+DMEZ+R78mXCw2eCHQqi8w3K/s9yYyzLNBttHwxlqvrQSaUm+w3a2Teo3csD2eBTt8eXa4WOfVYdOidkxiZ4/MzgUmABboH9cICXRuekYmDL1b21blTztQ7O0PvySBidAJkZFDX22SNiGdH52SuzoerB3MlXJ4eE1C9g7r6yIS63FrxTfWIFm+uVsu0db315nif0UmWHvHsD4FO+aBQeoeyrIg0A0mJFFrPivIKILW+zURfa3Ii+z0ZOTKipOGu7/GnQJeZJGtZ79Dzzna33Qlbbwp0mXKo+wN31jvuyuFNgS5z1lS2HP4U6Pa2ctj27dGuO3mCduYolQW6Fe4W7jyZL+gdA9X1oE7T1VhmdOwwMifRawngy35BzSZQxkDZfsHTchiZZ/AzgUmABboH9cIC3XsCXf0md7vcvhZGlB+ZgUoR/45m2/QOd4sBMmnINlVPBLpjWjLmjb4Q6O7KreUkjnbQySzV8aIziPkFVrBAr0vUyPekdwJk5Hy4kkO9aRiZ+OldBHAnCL3pJM6TcpCgoGvLnzUunS46O7vNAt15TyPTDrWsRrT6UH8KdKPfk55+Qe1vQE2RjhbR5U1/MjL1ITuWy1q9GPlm1aLN3XzHyAKkvwW6zPEomXmP0XLIthE9bd0T8eyvcribL/HvwUOABboHdcUC3XOB7suVfQ+qVjwqM4jIDrieDOp6y2VEoCvOydydD1fnX8Q7bgJoOaBzll93q+7KkYndFNc0w4r6yeTkx4SsI7ZdIIo3a9uBPRnfNBjOoOutP7rPX4O6LycngSrQ9YZfDDWBLuM8Ut2vuYu8oG7bMn2o3rau16LBVThQd7v03piIO74jHAS6u36NBXpdQu7GQH98T56k4a7Onf3ui37Bk2+WnvXlGOjPsdxonfBzgUeABboHdcIC3XOBrrzBH6udslUtM4gEskCnSXVqsglaJuFGxbNaoKvfo+e8lN7JicxCQ6gKdFlhJPs9yX4bdL/eNAJVoOv9ZkNNoLura61vV++usCeTfb3HawJdoOud7IfKDrrib4B8a5CVUpPGNh8nGSd2zsliia6+l5s1m56vhRElKjOWB+IOunq8dWUFpu7XKLQgHTPKytYOZ+rJN+uuL2GBfpKA3gVITxcajNYJPxd4BFige1AnLNBDW6DThGP+IotLk2g1Ab2iU3lOOePoKmSW0SZqxMGRXtGi3Kc4+1OHsdFyQGeUlRGB7opZMJq4O5ZHjymhXvFstG2xQD9JTo+Juy+dRzoKCsqVOxN38gWQmFg35E/xCSeSWgJd1rLGE0Hhrq27WzDx5hl0JS+y35Me0RkqAl3NSPbsth5WnooWIwLdnW+KYBG27r6nYCmHL61eiIGRdqgcbaRIFuTTwdXFAt2T2UZ4PssC3YN6DxeBLuN12mhH54sJkAdVKx5VTKy1wu84e7esQPc0j66eN+JJWVagG5mIy6YRyAKdzgZOfrpK88yvN+qW6lAdTkkrJJ2nE1gjedXbL4TKDjr5NGjWuK6wdWWZovecuyeTZFmBbuQMuuw3a6Rf0LsIcCoE+icLLdi4uQZ6LIP0joEs0G2txIgwkp0vGBHo6m/S3TlxmTSM9Lf0jEwa6tCCtDCtJ7yuTBr+KIdR6x3ZNkLj7Euzqn02lrNAN9pawvc5Fuge1H24CHRZRP4YcI2kIVsOI/cHkkA3Mkn2x0RcNg0Zga5+t69X3ZUJph7RbKQ9+WsCazRvep5TvlXl7HPWEZu5pR4zTdlJlpHJpbtvVq9JtZZ4M+o8Ug9X5R5Hb/T0bzv3aJuyuhO2ehw8umPlyUKD3n7BXTl8sYOuLHbqCY2o95tVBDqFwYyNrbvwQw4t6Zo+LQoRNivxWpeRMVD2e5JJw6hpuEwa6jb/whNRbncuZYWtXt8U3mjrMt+5bDn0fk+BXg5/CXTiQG2YdsMHXad9HEO2vhzvZ4HuCb3wfJYFupt6P5ZbgIT4OMTFRte5kwW6NjxPBlzZHYpLekb4pDM12h3ITmCNpiPznC9EizcGdWKVUh+Y+kiUy+IYMdUPpcHQH9+TTHuSvdeZwA0Ugf7I1CrhdNDZDpliSUP/pV1UElRNm5iQl29FfgHQvo0J7duaajkrVDPyZb+gtUuvdQzCnbANNoHurh1600mcu7SMiGdFoLt7d7gIdD39gcLKlyFTQ0XYBkM5ht5oFmE29VyyfajsYpSePHhyj9E5iYwZvSf542cDjwALdCd1cuBQNu575FXsz8wWdwy65mI8MfEORNEBvhMXC3RteMqEVo85lfIGpTN9eGwkmjeru5ugTkkJO+KLs9uefKayg4gnael9NlAFOuVLTxvxp0CXNWXVWwee3BfsAp0mGIu/soC+2cNHrELg0jeuFf9czUl2kmXEb4SyY6Y3JrRiIk7ho8gRlh5h4ct+QWvHT2sX6FQKdG9OxPVaNLgS6LJOF2W/Xz3fLDlUO5ZrBbVZWvgRkTAam4QFBMWyH3itGenNTGIByKQxJOpJw9PvSSYNozvosmyN3B+oY6BsWUKlHLIWKcRJtg+VHTtk60L2fqMCXTYdvj90CLBAd1KX90x6BYkJcZg2eSSO5BzH4HufwhMThuG6K3uyQPdB+9c7SfZB0l59pewg4tXEnbwsVAZ1mXLo9QitRmZk4uDrOpSZJKsXvPRapPi6DPR+f5TDiN8IvWX3xBLAH/2CuzT0CltXbcZdGt74nvSm4cnRF8d86nG6qLeNKPfJtHWlHOTjgP4UFwPV1XB73l0mDSVfev1GGCmHIohokYG8uLuLfy7L1JP7ZcYOJR297VC530gasmUykkYglkO23KEk0H3d9xhhy88EJgEW6Br1UlBUgp7XjcbHrz+Grp07iDumzZyHIzm5mDXtARbogdmWAyJXsoNhQGTaC5kwMnGQTVYmDaPn1mTz5I/7jUzElQUvPd70/VEGR4F+5hkmjBgWqStZWcsaXS81eBMLdPldLCOo9fahRixrqA6NOF2ULYfMN2ukHI7fky8WGDwR6GpW7pyrybI1cr/M2MEC3YLvVtTosgwyUhdGn9HbLyjvD9QddBboRltA+D3HAl2jzvfsO4T+dz6G1YtnIK1BfXHHvEXfY+nytVg05ykW6OH3negu8Yszq4UZbyBMSnRnOkhulJlk0QSZQr9RmLxmTWxOX3zpyM2XCGUm+77Mh6fvVkzd9Zi2K2kFomWNTDs0Otk3wlrvBNaoIKQ80aSXLnfHAYzkX5aV0XL443uSSUMJtUltncYOMm2nIyDuQjfJpGG0PmTSUHbnlXjo3buR13Dn8c+N5snIc0aOvkx9sUrEddc7lhvpF4yURfYZvf2C8t5QKUegCXTiK/M9ydYz3x96BFiga9Tplm27cNuYaVi3bDaSkxLEHZ8tW423PlqKlQun1xLoeXl5odcquESGCRzJjkBZhQltWloMv4Mf1Caw6pcorF4bjZsHVuCMjjah4O56b34smjaqQd/LK93dGrC/U5v6ZkV00JcjYAFLZmzLX5H44psY3NhuawAAIABJREFUXHphJS67qErX09QO6bpraLmu+43c9OSLtrHqqUdKXD6ufEfqm+68pTwg+i0qR+O0Goy6q0wXBqU8119Tga5nuu8X8gsiMP2tOLRqYfFZfRj5ZqmN7D9oht56yDhgxgcLYn1aDl0VoLpJKceE+8pQP7nGyCu8/gzV+Za/zGjdskZ3G6fvPL/ApPsbV9qhTL/g9YJqvFBvv6A8SuWmP/Qt6fme/FEGSoPKkVyvBhPvl+sX9H5P/ihHoH6zvip7fXL2wZdhAizQNdApO+g/fT4TDVOTxR3OdtAtFhZihlsfP8gEJAgoO03udpYkXsm3MgFpAoo3dzIt7tDWvUNL6QQMPkC7M3TpzRPdvyfDFpJN7zMGsyb1GOVLxtpFKYeyY6snMTLhJQeV3bsFTv1t3Gyz+tFbDqUdBmo5yHliOF2B+j1RW6cr2OsjEL9Z2fYdqN+sbDn03h+hFR9S78N8H1igazQCrTPoz0z/CDnH8vgMOn80TIAJMAEmwASYABNgAkyACTABJuATAizQnWAd8dDLqJeYgGmTR7AXd580PX4pE2ACTIAJMAEmwASYABNgAkyACTgSYIHupD1kHMgScdAzs46KO66/+iJMffBOREWd9D7McdD5Y2ICTIAJMAEmwASYABNgAkyACTABbxFgge6GZPbRPBEPPYEOxakuFujeaob8HibABJgAE2ACTIAJMAEmwASYABNgge5BG2CB7gE8fpQJMAEmwASYABNgAkyACTABJsAEahFgge5Bg2CB7gE8fpQJMAEmwASYABNgAkyACTABJsAEWKB7qw0UFBQgOdkWho0vJsAEmAATYAJMgAkwASbABJgAE2ACnhDgHXRP6PGzTIAJMAEmwASYABNgAkyACTABJsAEvESABbqXQPJrmAATYAJMgAkwASbABJgAE2ACTIAJeEKABbon9EL82ZoaK6xWK8zmiFolpX/LKyhGcUkZGqelICY6KsRJBEbxqi0WRJgiEBFhCowMhXkuqD4izeYwpxAYxac+yVJTo1kf1I/lHM9DclIi4mKjAyPDIZyLsvJK5OUXokmjBk77Ku7L/NcAXNUHj+X+qwclpYKiElRUVKFRw/r+T5xTrEWAxobc/EIRPjk5KYHpMIGAIsACPaCqQzszj7/0Hj7/5mfcN6w/xt41SNxUWFyKHv1GYfmCl5HeNM3rpaCBe+p/PxDvfeqh4fb3//nPHoyeMgO5+UXi3+LjYjFl3K0Y2LeX1/MQqC/sfdMEUPi9d1+ZhB7n/kdk86dft+KpVz/AyoXTfZJtmmQNuXcq7rntOvS7ooc9jRVrNmPc46/VSXPz93PCZuHkVNTHgUM56Hvrw/jh01fQrElDO/9e14+1fxuOlbL0/Wlo36a5T9pGIL00M+sorrplksjSL0tnISU5Sfz/q29/Jhb1nnn4Lp9kd9n36zB9zsI639/a37Zh8rS37XUydGAfPDr2trBZ5CIuk597B107d8DHrz9mZ3/jyCdxx01X4bore3q1PsY+NhMr124R70ytn4Trr+6FB+8bXCsNZ32ZVzMSoC/z91juqj54LAf8WR/HcgswbNxz2J+ZLVpnu1bNMPLWfvZvkMdywJ9j+a+//41xj89CaVm5qI/zzj4dD90/BJ1PayP+Hu5jeYB2oWGVLRboQVDdNIisXrdFTDJ/+nwmGqYmg1Zhe1432icCffnqjXh2xjyR3o39Lqkl0Lf+swe79mai90XdkJQYj7c+Woq3PvoS4SYIq6qq0axxQ3z61hMgb/6+FOivvPU/vP/pt6KlvvjYvbUE+o9rNuHR5+Zg0ZynarXkls0biXyFw0WDuj/r45ZRz4Amt3SpBfrBwzmgVXnl+mfnPjz09JtCOJK1SahfikAncUaLdhPvtYmz/771GfILvS/QDxzKxsiHXgGlS3wdF8hoQnzJoAfEJPie2/ohM+sYBt71f3j2kbvDZkGRBPrT0z8Sk9A3np+AS3qcJerDVwL99feW4MpLzwP1P+s3/SMWcz998wmceUZbka6rvizUvw0qn7/Hclf1wWO5f+sj51g+vvhuDfpfdSES4mIxb9H3eP9/3+HnJa8Jyx4ey20C3V9j+frN/+DosXxc3OMslJdX4unpH4qx+80XJoiuKNzH8nDojwO9jCzQA72GTgzqyfUS8NuWHejWpSMeGX1LHYFOgv2l2Qvw/U+/IykxDjf2u1RMSktKynHPpFfw+MRh9pXBo8fzMWbKTLz8xH1o2bxxHQKlZRUoLC7B9HcWIjYmupZAV9/82bLVmDV3MVYumoGoyPAw96VBZNQd1+PJV97HrGfHicUKtUDfs/8wps2Yhw1btouV8jF3DcKVl5yLgsIS6frILyhGeWUlho56BhPvGVxHoD/13w+w5otZQdCSfZNFT+qD6mfW3M8x55VJdvPnNRv+FAsi7/73Yc2dVppoHck5DhLqaoGuLuF9j/wXaQ1SfLZz7Buixt+qCHRaSHpk2ttYtWiGMOVUC/RV67Zg+tsLQd9JtzM74vEJw9CxbTo++N932HfwCKY+dKc9E2/PW4aS0jK72HfMHZlKkxBf+csWvDv/q1oCfeUvmzH2/17DumWz7eaLL85eABL1s58bb7yQQfQkCfQPFy7Hhed1xs/rt2Lxu8+INu0o0C2WGrz36TdY8MUKFBWXoU+vbnh0zK2gMWfi1DfQ49xOuKnfpfZS064sWfFcdWl3tyTo27x5QG9h+UOXq77M7ctC4AYS6P4cy9XI1PXh+Hs4juV66sObY7kjb6WvnDdriugDSaDzWG58bmVkLHesD8XaaOuKuZpHpcJtLA+B7jboi8ACPQiqkAaR+vUSxSTr7gdfwo//+y/i42Nr7aA//Mxb2LH7gJjE0pma52fNx/iRN+LWQZcLQZjerBGemDBMlPajhcux6Kuf8OWHz7ksPe28WCwWTYG+6c+d+PL7tSAx8+B9Q3BtnwuCgKR3skiTnCcn3oltO/Zi+erfsOS9Z/HLxr/sJu4VlVXC/Pk/HVvjjsFXY+OW7Zj9wRdil/uMDq0M1weZDtMRB0cTdxrUH3h8FgZcdSFiYqJx7lmn4apLzwurs9Ge1EfrFk1x8cBxeHrScPTtfb5oILTr16JZI0weM9Rpg6EjDpSuK4H+2x87cOf4F9yKeO+0ysB4izLpJFE8+tEZOKNDSzz2wO21BPrujEMYMPwxsbN98QVd8PHiH0Csli94BbsyMsVC1IqFr6JJWiroW7powFg8P2UkLu91jtNCfrtyA15+89NaAn3Nhr9Ak6r1X70hrH3omr9kBT79YoXbvi8waHqeC0Wg03GcCweMwatTR4v+wVGgL/xqNV6a/SkmjboZTRulYua7i9GsSQO89sw4zF3wDT77chW+m/+SsMj5+999GHzvVKxePANpDVyfoSVT3mtue6TWzr1SIq2+zPPSBv4bTtVYTmSc1Uc4j+Xu6oPauC/GcqqPJd+uwf+9OFcsrpPFEY/lth10o3Mro2O50mvQUSAam9TWiPR7OI7lgd+bhn4OWaAHQR0rgwid5aMJf+v0Jphw7012gU6d+3l978PLj9+Pa/rYRMYLr8/Hhs3/CPH4w8+/Y/wTr+P3794Ru4T975iCWwb2wS3X9zEs0L/64Vd8vWK9EKl0Nv7WQVcEAUnvZFEZRM7u3B6XD35Q7PYlxsfZBTqde6VFkR8/e1VMeOki5r3O7yImwUbrQ2tS+9eODNCRBHJwcjj7uJhM0zlbEkXhcnlaH8+99gn2HczCOy8/BLIuufSG8fh87jM4rV0LwwKdfDjcfN/TdouXcKkLR4FOR2HueOB5Ie4++3K13cT9tbmL8fWP68XxHLqO5xWKRZLXn3sAl/XsKkTdoGsuxoih14pvZcrz72Ldl68LRz7OLi2BTlZF/W6fjA5t0jFkwGUoKCrFgiU/gnaM3S1Ohkp9KQKdJp10FIkWVans1DaVM+hkCXJ6+5Z4cuIdotiKUFj35WyxQHLZjePF+XU6x07fyrHcfCH0XV0lpeW4bcyzSEyIxwczJtdxNBruAt3fY7mr+gjnsdzd3IoWNXwxltsWIp8V3+CYuwaKT4nH8pMC3ejcyshYTuyV3XNHv0JK/xauY3mojIHBXA4W6EFQe46DyJZtu3DbmGlY8MbjwsSWJrl0ZqffsEfxzccvolW6zWSdBt2nXv0Qv337Fiorq3DhgLFCSNLvQ+59qpbZpzMErnbQlWdo9Z0cn9AknHYdw+FSBCGd53x3/tdYuGy1EN7Pvfax2MEjh350PMDR7JzM4YuKS8XE1mh96JnUUtrUXpyZaYVi/XhaH9t37Rc7irQb/sOaTfhu1Ubxfbm63O2gKyJH8RkRity1yuQo0GnRiCa3tAuVWr+eXaDTTgVdL0y5x/4KqkPaUadFw08+/1FY+VCfMuaxmWKhZNzdN7hEqCXQ6QGaYM/55Cvx3+ZNG+Kff/ehRfNGYWfiTgKdom5cduME/N/428X5V0WgkzMksrxSHH1mZR/H5UMetC9SkRPK1OR6eHTcrcKaYcbTY4Q1l7OLnMA98PhrOJKTi49em4L6yYl1btXTl4XiN3MqxnI99UGsw3Esd1cfZP3m7bH80JFjuH3sNOGU7LnJI+ssXintnsdy+bmVkbFc2VChBcrB/S+r0+2E61geiv1vsJWJBXoQ1JjjIELZJRNcEuXUsZBAT0qIR8/+o8Wk89KeZ4sSkXOYb1auF6KdLvKivG1HhvAkTavp0yaPcFtyPQJdccSk7LC4fWkI3OAoCMn50iWDxqNr5/bYve+QEOh0vpbO+NMOFJ03pIsWVRRzX6P1oWdSq5j1blr+jvAfEA6XN+qDBPoVF5+Lpct/EULRXVQCVwKdzkWTxQSZzCtRF8KhHqiMaoFOu0I33/eUiHbQtFEDcRb/5Tc+xbrftwnrHrqoP+p+zX14deooca6ZzimTOTZZBE165k18+8mLmr4yHJk6E+iO99C3SpZGdHTh9huvDIsqcdxBpwLTefQPP/tO9A33DxsgPEiT47wLu5+Jh+4bIpiQd+MRD71s9x9Ax5jue+RV4Vxv1nuf44dP/+tUVFB0kXH/9xrKyirw9ksPaopzSkNPXxaKFeTvsVxvfRDrcBzL3dUH7XR7cywnE+rhE14QfmvI74arMJ08lhubW8mM5WR9SH42nDkODeexPBT732ArEwv0IKgx9SCirBJS1pUwayQAExNixfmdvIIiTHhyNq685Dx7iJuMA1lil52uebMeQ7czOzgtOZmA1tTU4NmZ81BdbcHUB++E2WwWzoXo3BTtjJ1z1mmIMJkwfc4iYR60cuGr9nOeQYDUoyw6CkJ6EZ1rnTZznt2LNPG/8uZJuOX63hhxaz/8/scO4azK0YuyTH3QIGGtsYr6o+ME/S7vYTf3pbRph7FTx9YoKCrGpKffEs763pv+iEdlDKaHvVEfi7/+GU+8/J4oNlmdUPhAZ1dVtUU4ibt66MNiAYzCrDk6SKSdD/IB8eNn/w272KpqgU4MaQeWQgiR2ToJdEUAkiDveW5nsVv+xodLa51rJgdzZAVEwp7MDp1dZH5IfRRZPVCYteXzX4YpwmSf+JL5fL2kBBzPKxDOAMlRGvWZruo3mNq+u7yqBTrtpl5584MiQgdZMJBAp8Xcz7/9GTOeGoPGaal4dsZHyMrJxcJ3popz59T/XHbDePEM+TWhBSyti5yL0mIM3T/9qTFITIgTt0VERNiP+rjqy9yVJRR+9+dY7q4+eCy3eXEn/z5KKED13CohPtZrY/m/ew5i0N2PC389Y+8eJL4LuuLjYkQ4Sh7LT5q4K9EmjMyt9I7lS5evxZTn54gFW1owUa6U5ET7+BDOY3ko9LfBXgYW6EFQgzSIUKehhCyiLNOqH63+KQKdBB85CyOPo3TRTjpNwBTnSPRvt499TkxUv573gssQXHSOmczjHS+aWNMEmzy9kqdR5aLQRmSmdcE5nYKApHeyqBaEdE7z8sEThWhWwjyRV3cKr6XE2HSMYa/kQm99KHXtmPuvPnoebVo2FZYR5MhJubp0aoeXH78P6U3TvFPYIHiLN+qDzH/Pv/Z+Xef3aRdWqVfCQz4glOMMSlsYdtNVToVMECA1nEVFoP/61Ruod8IxG+1CXT/8/+wCnV7+5kdLhTC0TVBjRV9F3sOVa+OWHWKnSdlVd5YhxeGc4+8kOhXz+RlzFgkTd7p6nX8mnnrorrAId6fwUAt0+nelf1cEOrXlKc+/K87700XHoCg6RbvWze1YaQFl9vtLXIYLVKxK1HXl+H246ssMN7ogetCfY7m7+uCx3CbQ3c2tvDWWk5UPzQnUl9Jf8VheV6AbmVvpHcvJQvR/S1fWqQ9lNz3cx/Ig6lZDNqss0EOsaikEVExMlE937mgX5HhuIaywolGDFM1QVCGG1VBxyBLhyNFccf6WnPP56iqvqBTOzeiog9Z5T1+lG2zv9Vd9BBuXU5VfardkVtukUarPog7QLiItSjZp1CBswkAarU9yqkfxgGnRla9TT4DH8lNfB0oO/DV28Fiur879VR/6csN3MQHfEGCB7huu/FYmwASYABNgAkyACTABJsAEmAATYAJSBFigS+Him5kAE2ACTIAJMAEmwASYABNgAkyACfiGAAt033DltzIBJsAEmAATYAJMgAkwASbABJgAE5AiwAJdChffzASYABNgAkyACTABJsAEmAATYAJMwDcEWKD7hiu/lQkwASbABJgAE2ACTIAJMAEmwASYgBQBFuhSuALv5poaK3LzC0WIL4pPrnWRp+SE+DhNT+LkubeiogqNGtYPvMJxjpgAE2ACTIAJMAEmwASYABNgAmFEgAV6EFf2r7//jXGPz7LHZD7v7NPx0P1D0Pm0NqJUBw5l475HXsX+zGzxd4pj/sTEO0S4IRLtw8Y9Z/+tXatmIm4zxeRUX9PfWYh3538Nx9jGQYyNs84EmAATYAJMgAkwASbABJgAEwhIAizQA7Ja9GVq/eZ/cPRYPi7ucZaIX/v09A9BO+pvvjBBvOCeSa8gMSEO0yaPxJGc4xh871N4YsIwIcIpxuoX361B/6suREJcLOYt+h7v/+87/LzktVo77Uu+XYP/e3GueB8LdH31wncxASbABJgAE2ACTIAJMAEmwASMEGCBboRagD6z7Pt1mPzcO9i6Yi5KSsvR87rR+Pj1x9C1cweR42kz5+FITi5mTXugTgkys47iqlsmYd6sKeh2Zkfx+29/7MCoR2fg6UnD8dDTb7JAD9B652wxASbABJgAE2ACTIAJMAEmEBoEWKCHRj2KUpA4351xCIvmPIU9+w6h/52PYfXiGUhrYDtfTrvkS5evFb+rL2WnfM0Xs5BaP0mYvt848knMeHoMGjdMwYDhj7FAD6G2wkVhAkyACTABJsAEmAATYAJMIPAIsEAPvDoxlCNl9/zdVyahx7n/wZZtu3DbmGlYt2y23XncZ8tW462PlmLlwum10tiVkYmho57FHTddhTF3DURBYQkG3zsVdwy+GkMH9hGinwW6oWrhh5gAE2ACTIAJMAEmwASYABNgAroJsEDXjSpwb1z72zZx3vzJiXdgcP/LREaVHfSfPp+JhqnJ4t+0dtAPHTmG28dOAzmYe27ySJjNEVi+eiMmTn0Dw266CiYAuQVFoAWAIQN646Z+l+CMDq0CFwbnjAkwASbABJgAE2ACTIAJMAEmEKQEWKAHacUp2VbE9LOP3I2BfXvZS0Ph09Rn0J+Z/hFyjuXZz6DTzvjwCS+g90Xd8PiEYYg0m+3ifsUvm+3vIo/vn3z+I+69/Tpc2+cCtGvdPMipcfaZABNgAkyACTABJsAEmAATYAKBR4AFeuDVie4c0XnyKc/PweQxQ4XIVq6U5ETEx8VixEMvo15iAqZNHlHHi/u/ew5i0N2PC8E99u5BiIiIEI/Hx8UgJTmpVh7YxF13lfCNTIAJMAEmwASYABNgAkyACTABwwRYoBtGd+offHr6R/jf0pV1MqLspmccyBJx0MlDO13XX30Rpj54J6KiIvHtyg3CM7v6ohBsL0y5hwX6qa9ezgETYAJMgAkwASbABJgAE2ACYUaABXoYVHj20TwRDz0hPjYMSstFZAJMgAkwASbABJgAE2ACTIAJBCcBFujBWW+caybABJgAE2ACTIAJMAEmwASYABMIMQIs0EOsQrk4TIAJMAEmwASYABNgAkyACTABJhCcBFigB2e9ca6ZABNgAkyACTABJsAEmAATYAJMIMQIsEAPsQrl4jABJsAEmAATYAJMgAkwASbABJhAcBJggR6c9ca5ZgJMgAkwASbABJgAE2ACTIAJMIEQI8ACPcQqlIvDBJgAE2ACTIAJMAEmwASYABNgAsFJgAV6cNYb55oJMAEmwASYABNgAkyACTABJsAEQowAC/QQq1AuDhNgAkyACTABJsAEmAATYAJMgAkEJwEW6MFZb5xrJsAEmAATYAJMgAkwASbABJgAEwgxAizQQ6xCuThMgAkwASbABJgAE2ACTIAJMAEmEJwEWKAHZ71xrpkAE2ACTIAJMAEmwASYABNgAkwgxAiwQA+xCuXiMAEmwASYABNgAkyACTABJsAEmEBwEmCBHpz1xrlmAkyACTABJsAEmAATYAJMgAkwgRAjwAI9xCqUi8MEmAATYAJMgAkwASbABJgAE2ACwUmABXpw1hvnmgkwASbABJgAE2ACTIAJMAEmwARCjAAL9BCrUC4OE2ACTIAJMAEmwASYABNgAkyACQQnARbowVlvnGsmwASYABNgAkyACTABJsAEmAATCDECLNBDrEK5OEyACTABJsAEmAATYAJMgAkwASYQnARYoAdnvXGumQATYAJMgAkwASbABJgAE2ACTCDECLBAD7EK5eIwASbABJgAE2ACTIAJMAEmwASYQHASYIEenPXGuWYCTIAJMAEmwASYABNgAkyACTCBECPAAj3EKpSLwwSYABNgAkyACTABJsAEmAATYALBSYAFenDWG+eaCTABJsAEmEAtAtUWCyLNZqbCBJgAE2ACTIAJBDEBFuhBXHmcdSbABJgAEwhtAus3/YOnp3+It158EC2bN6pT2LLySrz85qdYvW4Lso/moWPbdNw66Arc2O+S0AbDpWMCTIAJMAEmEKIEWKCHaMVysZgAE2ACTCB4CWz9Zw/27DuE199fIoT3lx9MQ7vWzesU6J5Jr2Dtb9vQt/f56NAmHT/8/Du279qPJyfegcH9LwteAJxzJsAEmAATYAJhSoAFephWPBebCTABJsAEApdAr+vHIje/yJ5BLYG+KyMT1w//P1x3ZU+8MOUecW9BUQn63T4Z8XGx+G7+SzCZTIFbSM4ZE2ACTIAJMAEmUIcAC3RuFEyACTABJsAEAozAwcM5qK62YOGy1fhw4XLNHfR3Pl6Gme8uxnvTH8H5Xc+wl+CF1+dj3qLv8fncZ8S/kQn81Zd2r2P2vuTbNfh6xXpMGXcb2rZsGmAEODtMgAkwASbABMKTAAv08Kx3LjUTYAJMgAkEAYG5C77Bq29/pinQn3r1Q3z25Sqs+WIWUusn2UuzdPlaTHl+Dt56cSJ6nPsfXHbDeERFReL7T1+xO5GrqbGi760Pi2e+/eQlRETwTnsQNAfOIhNgAkyACYQBARboYVDJXEQmwASYABMITgKuBPrYx2Zi5dot2Pz9HMRER9kL+OOaTXjg8Vl49pG7MbBvL7w9bxlem7tYOJrrdf6Z4r6NW3Zg+IQX8PDoW3DHTVcFJxzONRNgAkyACTCBECTAAj0EK5WLxASYABNgAqFBwJVAVxzEbV0xt1Z4tVXrtmDMlJl46qHhwqw951g+LrtxPPr06obXnhknwDwy7W189cOv+GXpLKQkn9x9Dw1qXAomwASYABNgAsFLgAV68NYd55wJMAEmwARCnIArgU5m7GTOvvGbt5AQH2sn8c2KDZj0zJtCjJMop2vi1NlYvvo3rFo0A9FRkbhwwBhcf/VFmDZ5RIgT5OIxASbABJgAEwguAizQg6u+OLdMgAkwASYQRgRcCXQ6m06/L31/Gtq3ORmCbc4nX2HGnEX4+PXH0LVzB0GL4qnf/eBLmHDPTYiLjcFzr32M+W88jrM6tQsjmlxUJsAEmAATYAKBT4AFeuDXEeeQCTABJsAEwpSAK4H+6+9/Y8RDLwvRPWLotYKQxVKDoaOewd4DWfh5yWuIi40W/644hauqrkZsTLQ4s77kvWfDlCoXmwkwASbABJhA4BJggR64dcM5YwJMgAkwgTAlQM7fKioq8cPPm7B89UY8eN9gNG3UAG1bNcNp7VrYxfg1tz2CzKyjmHT/zWjTsim+XbUBy75fh7tvuQYT7x1cix6FXqMQbHQ9Peku3HDtxWFKl4vNBJgAE2ACTCBwCbBAD9y64ZwxASbABJhAmBLodf1Y5OYX1Sn9yFv7YfzIG+3/fuRornAIt33Xfvu/3TygNx4dd2stx3H0Y35BsTh7Ttdv376F+LiT59bDFDMXmwkwASbABJhAwBFggR5wVcIZYgJMgAkwASYgR4DEd/axPLRKbyxM2PliAkyACTABJsAEgpMAC/TgrDfONRNgAkyACTABJsAEmAATYAJMgAmEGAEW6CFWoVwcJsAEmAATYAJMgAkwASbABJgAEwhOAizQg7PeONdMgAkwASbABJgAE2ACTIAJMAEmEGIEWKCHWIVycZgAE2ACTIAJMAEmwASYABNgAkwgOAmwQA/OeuNcMwEmwASYABNgAkyACTABJsAEmECIEWCB7kGFDhw4EEuWLPHgDfwoE2ACTIAJMAEmwASYABNgAkyACTABGwEW6B60BJPJBKvV6sEb+FEmwASYABNgAkyACTABJsAEmAATYAIs0D1uAyzQPUbIL2ACTIAJMAEmwASYABNgAkyACTCBEwR4B92DpsAC3QN4/CgTYAJMgAkwASbABJgAE2ACTIAJ1CLAAt2DBsEC3QN4/CiZ1H0aAAAgAElEQVQTYAJMgAkwASbABJgAE2ACTIAJsED3Vhtgge4tkvweJsAEmAATYAJMgAkwASbABJgAE+AddA/aAAt0D+Dxo0yACTABJsAEmAATYAK6COTmAbn5VqTWNyE1RdcjfBMTYAJBSoAFugcVxwLdA3j8KBNgAkyACTABJsAEmIAuAt/+aMF3K2pwdZ8I9L3crOsZvokJMIHgJMAC3YN6Y4HuATx+lAkwASbABJgAE2ACTEAXARboujDxTUwgJAiwQPegGlmgewCPH2UCTIAJMAEmwASYABPQRYAFui5MfBMTCAkCLNA9qEYW6B7A40eZABNgAkyACTABJsAEdBFgga4LE9/EBEKCAAt0D6qRBboH8PhRJsAEmAATYAJMgAkwAV0EWKDrwsQ3MYGQIMAC3YNqZIHuATx+lAkwASbABJgAE2ACTEAXARboujDxTUwgJAiwQPegGlmgewCPH2UCTIAJMAEmwASYABPQRYAFui5MfBMTCAkCLNA9qEYW6B7A40eZABNgAkyACTABJsAEdBFgga4LE9/EBEKCAAt0D6qRBboH8PhRJsAEmAATYAJMgAkwAV0EWKDrwsQ3MYGQIBAQAr2isgrrN/2DC7t3RqTZHDRgWaAHTVVxRpkAE2ACTIAJMAEmELQEWKAHbdVxxpmANIGAEOg5x/Jx2Y3jsW7ZbCQnJUgX4lQ9wAL9VJHndJkAE2ACTIAJMAEmED4EWKCHT11zSZmAzwV6UXEp9h7Ickn6eF4hxj42kwU6t0cmwASYABNgAkyACTABJqAiwAKdmwQTCB8CPhfov/7+N0Y89LIuoryDrgsT38QEmAATYAJMgAkwASYQRgRYoIdRZXNRw56AzwW61WrFtys3YtIzb+K5R0eibatmdaDn5hVi1KPTeQc97JsjA2ACzgns2mvFngwruneLQGoKk2ICTIAJMAEmED4EWKCHT11zSZmAzwU6ISaR3v2a+/HhzMno1LF1Hep8Bp0bIhNgAu4IKJOToTeacf45Ee5u59+ZABMIQgK7M6yIiwGaNzMFYe5DK8tlZcChI1wfgVKrLNADpSY4H0zA9wT8ItCpGGs2/IWz/tMO9RLj65SqvKISv2z8C5f0OBtRkezF3ffVzikwgeAjwJMT/XW2YVMN8vLB1gb6kfGdAULggUer0LypCQ+PiwyQHIVvNshq6fU51WjXxoRx93B9nOqWwGPgqa4BTp8J+I+A3wS6/4rkv5TYi7v/WIdqSjzg6q9ZZqWf1WvvVIvjAGNGRqJDW96J1E+O7zzVBEig0zXz+ahTnZWwT58FemA1AR4DA6s+ODdMwJcEWKB7QJcFugfw+FFBgAdc/Q0hUFmRoAi0HSYW6PrbFd8ZWARYoAdOfbBAD5y64PlCYNUF54YJ+JrAKRHox3IL8OQr7+Oh+4agTcumvi6jz97PAt1naMPmxYEqOgOxAvzBKjcPyM23onkTE+Li3FMI1AksC3T3dcd3BCaBQBToSt+j1yLl0GErPv/aIvqRQdcFz7E9dYsI1P4tMFuu73PljzHQ96XgFJgAE9BD4JQI9AOHctD31ocx/43HcVandnryGZD3sEAPyGoJqkzxgKu/uvzB6vNlFvy0rka3abiRCaw/zoeHikBXWF3dh50C6v9SgvtOfwh0WasX2e/JSL8QiLUWKuUIRLZG8uSPMdBIvvgZJsAEvE+ABboHTINRoIfKyr4H1RYQjyoTH3VmSIj0vdz1jsusOdXisbEjA8dpDwmpjZtrhFMyX3lY98fkxB8Tcdk0jHhSlk0jID4KjUwo5Xjy4SgOrReoleTlfPlaoBsRnbLfk5E0vIzRK6/zRzmUOYkvxw6vwAiAl/hjDAyAYnIWmAATAMAC3YNmEEwCnUx3SUAdz7Ni46YaMdk9/xyzODvLTqQ8aAQGH/VEoPt6AmukSP6YOPgjDX9MxAMxDSN17o9njLLSs9Dlj/yHWhr+WBz0df9mRHQabYeB5ptCtj0aYWU0jTPPMGHEsMBZdJYtR6gsUsuWm+/XT0CZh6fUp/k3W4XpJxeed54SgU5n0J+dMQ8PjLiBz6D7qd15Igj9lMWwSMZZPagLr3XWkXYayiqAWe+c2EE/EfamfZtT56WbykPewnftrcHuvVa0b0sLPhE+Ce/FAt19qCPlDD2Z6h/KsmJQP7OIJ30q24gnH7asMPJHG/GkPMH+rK/FM/HxdRpGRKdsOzSSRiC2DX+Uwx9p+IOtP/oef6ThD1bhmkaotPVwrT9/l/uUCHR/F9JX6QXLDroy4XHFgXecfNVKar9XLdCjIg4hxrwe1dbmqK5pgeqa5uIBLYGuTBLVOT2V4YiUCYOeBQb1PbQbJ+NEyZeTE2X3QyyClEPEYSYncYOutQlcZ5eRAdeXk31n9XEq24iRL0s5e75hkwW06ND9nAg0SDHB3Vl0X7YRI+VQnvHHzrMn+dP7rK/FMwt0vTUB8V2QVZwvd+OM9G/6S2C70x9pyObJyP3+6Hv8kYaRsvvjGdn5gpE87c6wIi4GLsd8I+9VngmVtu4JA35WPwG/CPTMrKOIMJnQrElD5BzLR0lpWVDvnCt4g1Wgm1CNuMjVKLdcgBproigOC3T9H40ndyoddISpDHHmlYiJ3FrrdVZEC5He/dx26Na1FdLT0yF2Q49YkXnYivJyIMIMRJiAattGuti1lhG6nuRfPdC4e5crr8c02Y+LBV54Ul+8Y19OTowsNHy3ou6REVeTZV8uAig75yKNTTWIigZiooHiYlsNjQ0Aawt3bcXxd6OLUUbaiGKZ4ksrA38IWxm+Ru/1Rzl8nYaRSbIvF9WM1oWRcsimFSppKH48UuubfObLwkjfI1sf/khDNk/+ul92viCbL2ojk5/2bchUI9+TMv9zt1kgW16+P/AJ+EWgz/nkK/z97z7MeHoMHnvhXTRMTcaEe24KfDpuchisAj02ciPiI38UpSuv7oZyy0W4snc9p87JZCcnQV+xPiwAddBz3tuA+KifYEK5SKnCchZqrDGg3fTIiEO1Uo+KioLVlI6i0taoEjvs6Zq58/dZRy1TfXPEcVhqGtTKnzuBTjfr3d315eREeXfTxiY0bWLCv7tqUFKqbcmgFFDLMsVVPRhZBKBdAzG5zLKKxQzazddajHH2bnVj0cvaF5+AsuOnx++F0udQfcTGWJFxwJYjd/k30kZeeq1a8HX3boWJTDkC8ViKJ3Xra/FMefN1GkYmybJjoJE0ZOslUNNQBIVeJ6b+KMeff1sx9+Nqn25EGOl7ZOvcH2nI5snI/bLhTAO1X5Atu5G2Ltv3yOaJ7w9cAn4R6GXllbj29kcw8tZ+eOODL/DtJy8hMUFHkOHA5SZyFqwCPcJUhFjzGsRG/mEn3KhJN9w29FIkJSXVoR6IHYQRz6/+2Clz1WSzsrKwaNEy5OZmidtIbJdUXQ2LtZH9MbJuiDQfQo9zDqKk6CAyMzNRVVXl8NooWCOaA+bTYTV3Q3UVUFwC4exv3IldUm98NrQ7TJczk2JloDGZqhBj/gsx5t9gNh1HdU0TVFjOQ4XlTPF8oAt0T4StIiTatATKK0zIyra6rAdfLgKoy2EyVcOEYtRY69dqDs5EqBFP8bLtTGkz5K351pu0IxV4Uh+UHyMTWFlBSFYK8xdZdE32jVoCyLL11/2yrJT60Bs/3MhEXLFM0bvD5I9JspE0ZOswUNNQ2vzDYyN1mQr7oxxG+gXZ+giVNGTLbeR+hdXdt0Wiy3/0+dDxdd/jj3ZoJI1AnH8bqXN+Rp6AzwX6su/XYX9mNtZv/gdbtu1C59PaoNf5XdCkUSpu7HeJfI79/AQ5tEuIj0NcbHSdlINFoFNnSN7bf9tsE13KRUK9Uf1fUFl2Uqifd97/s3ce4FEVax//b0sPqYTemyBFsIIiWLFggWvHrqifYsEuVlRUbKio135V7NgQGyiIoqBIERWlEyCBECA92U22fc87y2x2z7ZzTpJld/PO8/B4b/aUmd/MOWf+M285FCNHjvQT6lpfEHpeQmq7VQoJIdC/dArzbkpLpsZ0TetOmZ5FAMnKVwjZbDZ8//33WLFihWhmeno6Bh90Ar5aMCCg2cFcDZ5+YSt2lWyDxbgNZmORd+fd4eqKWvvpcLrbNLtAj/QxXPVnGT6avRTJ5r9gQH1AO1zuNNQ7D8KE847AoAP9F32kPy4FlaOi1kS/JSZATRGEktFxRxtRXgmsXO1SJdBJ0PfsbsSK1S5UVKpbxBg2xIiaamD95uD3+Hp+PRb+uB1mY+G+cbJdsLU6joTV0fieDSXQ9TyzWk3v1NxDb3/IZ7Ws3C18cylLRW6OQVXav0hjXTm41YxDuRj4yVwndux0IyUFMJv9XQ5a0tdR7ftU63EtwUpZB733GHeqCaOPihwZWc04VNYplr6Bsm562qG1v/XcQy0r+cwqLYRaIt2ammdWKxs974V4uIfc3VYzp5Lt0eq7rac/1L4XQgVKJcsziisTqugZ6zJWCo1Z+uZEKnruofZ5inRv/j3+CLS4QP9hySrsKNkL+u/S5WswdGAfnHzs4cLMfczoQ2OW2LbiXbjmjqfF4gKV8accjftuvgQWc+POT7wIdDFRtwJFO92gj+JnXznRsb0B408zicBLZlMVfvrpJ6+ApONJqG/YNgKbt6YH9FEkU1A9LyG1AyGYaTWdq8aHXu0LXtZl0c8uwUrNteU5ynusWrVKiPO6ujpxyBFHHIHRo0djW3ESnt+Xz9y37cHupdyFsxg3I93yBYyGOmEab3WciM5dBzfLDnok8bxx40YsW7YMGzZs8Fbb7uyOetehaHD2QZJpDZJNy4W5viwHHnggDj/8cHTp0kX8SatpuNK/mgKGUYqSSB9culekwDJyotC+nQHt2tahtvIv2KxFSEnritS07vhrbb6os++YVzLKzvbEAyB/bxJinTsGF4byXmoEeqR7dOrgRJ/uO5FiKURhYSG2bdtnAx7kQXK42qHG/h+xm96cAl3txEHLRFwyMht3wWLcBLNxIyheQ619rDeAorI/6P9H872gZnIZaufct3vCWb1Qeyg7ghp3AHnNlgxEF+m9EOr9rYaV8lyt72kt99D67pF1UzvW5fF6voFaFryCub60hLDV0w61rJryzKqdL8jjtIwReY4UX5ECUzblHtFoRzTuQc8VBVa9/QZ1afL09Ifa94IeVzLfb4gWa0S1Y13Pe0HvQoPW/ubjY5dAiwt0anpNrRUnT7gd99x0Ee6Z/gbmf/AEcrICTaljCdNVtz0pzPCn3TkRJaV7cc7VU3Hf5Itx2okjvNWMJ4Gu5gVRVRUo1D0+6iPhcqsX6no+6mr6Xm80er1+oFo+IvJjTudQGTV8L7ZsnIua6h3i/3fr1g2nnnoq2rZt69dUOo/+keAMlRcz2GTfYLAhzTwPyaY14nqW5N6YfON4pAZZIlazIi4nh3JX27eSqakNyG/zFxy231FfXyZ+SkpKRo+eQ2BzHIpVf2eL1eO8XAOKij1R0E88Zi/Kdv+KbYV/w7Evml2brPbIb3cYlv/pMX+nRaKGBmBPmVucT+0PFmRNzweXVvRpN/Xd2U4xcaDFqGA7lnTtHxatgcW4GkmmzQHD0OVOBVkqnDG2J3r06CH6T+841CLQlfcQrg/GHT475IGC3OHqAIerO+zuHsJ9wog6pFtmg8SuGxZYHcdj+kOHBX3U9DyzaiYn1A+0MPjZXM9z4VvkQgvt1KSn1WPTpk344cd1KN21CUZDbcDxbuMwVFiPhdudFLDQIOufngb062PEzhK3cDlQs7imduInrXdkID7f+it3TyQbqo9FEQORPFYovoGaeAVq6i9BqW2Hmnet8hi9wlbLO1RvO7TcQ2s79E6SW+p5UjLy7SctY0XtGGjJdjTlmVVbf3mcljEiz5n6uF1Y40TakGjKPaLRjmjcQ+u7R09/qL2HvHZyMkTcluoawOkMb6lGjFpyrMs+0HIPPfMerX3Nx8c2gagI9JlvfIqdu/bikbsm4pHn3hVEptwwIWbJVFbXYsRp1+Gd5+8WO/5Upj07CyWlZZg57UZvveNRoMud9LR9AaeCdcKcr8vx69Kf/HzUrY6j0eAaCKer0ae1OXfj1KxWaxVGcnIlzUyVbaXI1uHMTNV8RJTimcy9U80/IMW8UtyOouRn5pyAW28crHu80wJDnQ3CpH+Hx33dWyzGDcjLnIuGeqswnT/zzDPRu3dvv2P0tIMuYDKWI9m4TPiYGwwN4poFBQU47LDDMHjwYFAAu0gfETLvX7lyJZYvX47y8nJxDbc7DTbnQah3HuLNIiArHEy06PngRpqIk2//n3/+iRUrVsPl8rRN1M3YGzC0J5sTwLUNBvduP5bEuKyqK5zu7ujTpwcsSbl+v4cThloFutFQD7NxA5KMa2ExbYEBvrEIgNS0fAwa2BM9e/aEE92wdbtF5KOnndfsNkBOtgFbtrmRav4RqeYlop40NmiMUDuoyIBn5AJD0d89CyUmVTu3agR6uGfWbNwtdskLcjeiusp/wcFgbIO8/D5ok90bTocVxdu+h8NBFiMZqHOciicePsCPu5z4ZGcBBw8xYnOhSwSWUyNa1E78tOz4STbkmpCT5f/ManGHUFN/eXW17ZDHq1m4Uy5u9u1thLUO2L7DhY4dDPhPiIUvPVYvtJhDZeYrnvQUkd7Psh1q3m9KRlrdUvx7cP9P9mVf0/jSshilbEek/69FUMhrqXkv0LFNeWYj1Vv5u5YxovV50vJe0Frv5miHWssaPc+sVlZ6nlmt99DynfXl25JjXd5Hyz30zHuaOr74/NgiEBWBvre8Cm63W5i1V1bVor7BjoJ8/+BFsYRlU2ExTr/0biz65Bm0zfPUc9bH8zFn3i/4+NWp3qqSQHfS0lyCFQoQNm+hG55gcj8jxbzK20LK1V3vHIwGZ388PS24Q8/GLcALrznRsztw/cTgwaCUyGa+6sTmQuDeW00hfXkm3+0UZt0W43pYTBthMW6Fy5UClzsHLuTC5c7FoIE5OH50HnJycvD9j0bRjnAlWB1l/ZXnjTk2MA8z1XvLVivI7NxjkrtTBEujYnMMh9VxFHp0t6jmEK6uJM5JqBNbKtdd6WHbsV0dPv/8c7EDSWXo0KEYM2YMVv1lFnlyyys8QozEF+1QUzCljh387yT5Z6bXwWxcC7h2wOD803tQg3MActsOw+TreviduGylW1gA0D3oH12f/v1nbOA9nn5+Hcr3LPfbqbY5DobT3QF2V1+43ClBx4wcj0rTcGp/b//qgOpD9fh2gaffyeTc5YLYqc/NrkHndn+isvxPVFd5+oiK050vIuk3OAeBfOd9C4lkk2EbxowuxtatW1Fc7B9lH4YMuA3dAPNQsUtta+ggUuEFGytq2lFbW4t169bh8y/+EaLct7jcWbDTDrmrOxyuHjjhmPSAIH7Keyz4yRN34pb/owCFH6O6ulpYWYwbNw69evWClrEu60Lj0FoPkf5vRwkw7lSjCAbVq3vg6KVn1rfQghL9SzLRLnm132+UUhCm3lizvrdf4EQ6yGCw7rMY+UecM2DAAJx00knehQbZDqVAD9YPylrKOs6YFv5dJe9hMgGZGRCWIvX1ZC0SGEzx+ddc4n0mBbrTWS8WgiyWTG+8Anr3TLoyuM809eP8H4JfO5Cy5y833+Pp66cfjuyHTcepuYdsR6h70t+DtUNeW3netVcYA55ZeYysv+854RjJ49S0g1h62ux5L/TsbhDP6Y4Sj/XOYcMMoh3K94m8tnI3Llw76Po0Vl583RWUTSiWknW4a28q9Jz9wmuevvYIdBd27gIOHQocfohJLAip8YcN16e0kL9jF/a5xLnQsT2EFZKaa8tvSbD3s7yn8r0QrC5qnt1wbfD9Tb4XtVxT63tBWRct94qFdkhGyrqE60dlf0Z6h/o+szQ308JIbX+o+c4G492UeasaRvK9oHZurLcdasdSNI4zGtV9i6JRl3i8R1QEeryBoWB2F06ahiVzX0BWpmen6aO5i/DS23OwcPYMb3NIoMtdwXhrY7j6/vCzBYt+aQyKZzJUI8m0SuykGg2V4lQSIwcO6APyL+7YsaP4W8kuI75ZmASbzYCSUqNIj9S+nQsHDXRg6KB9SbsVNyYRWVllxKq/TKioNOKgQQ7kZLkx+sjGXc3S0lJs3rwZvyzdCrOxRDVqiyUD1vpcOEnAu3N8/psrzGSpdOvixOUXeNKd0f3/+Nss/kv1UpYeXZ3o3tUp2lNXWyQE24qV2+B0+NfJ7uqDOsdxcLo8u6u+91Bd+TAH3j/dMyan3uFvBrx27VosWrQI9fX1yMrKQkHH0/Dbqq4BV7r0fBuoLbLs3r0bn8zZhtpqzwKDLOTWUO8cinrnwcLFIVw75JihfjvmKP+dXnm9N95LwdbtJhgNFUgxLUey+U9vwDs6hhZ/0jN74dz/dBULLLLIaysFurIddLy8hzyXopnTDnSScTUspq3eayYnJ6Nv376wOYYEZaSEJlnTgtyjM8phNmyF2UBB+3aA7uFbKJI9WRoMHdJW/HfN+k5+z5Py2kZDFZJM69C76xrs2OFxiZCF/MftrgPEAobD5e8eEYy18tmV16H6NzQ04LvvvgPFEaBiSRkMg+UElOxOhsHoyZtO3gj0LzvLhewsd9BnV8nY9x7KtsmxajEWIc3yDUyGRosEt9sCu6uHWCg4+cQuYuEgVP3ldUncp1u+Egt1KSkpIqAlifUt20x48/0UKAV6uPEorxnqeVK2Jdw9jh3pPwZeewco2VmM1KRCGEGWGORmkAQkjYfd2VNkXqDn6YoJgQEW6b4LF5vFmKH6K68d8EDv+8N9j3kWlx680xPvIlShdtBzuHmrAYXbTOKd1rObp6+p333L6+8mi2PDlWDtkPVXCtvLLqj3e/f4XlfWn/pQugGEYyTPVcNKXjtcO4KxltdWvnvCteON95IDvoHt27pwygnB34uyTpK1GkaR2hHqHRy2I31+lGNdeXy454m+/7YGA775Pkl8/08+rkF8/7t3CdzEkM+c3nbId5DyG6i8npiL7PZ8z//4yyzmFzQXyW7jDhjr8lwt7zc6pynvHrX9IY9T851VXlPt+01eW/nMBvvO6r2HnnYEGyvB5iKRvh3y3qHGjOxHLfM1OVa0MPJlp2ZOpWbeo3UcRev4bArQw0U3gagI9KKdu2E0GNCxfT5K91Sgts6KHl0VW3i6m9D8J8od9B8/fVbs+lMJtYNOlgGJVkKZLVM7KZJ4kmm1ED2UYotKbm4uunY7CKmZg/HVfP8dSPo9mK9mOPNzuu4ZY7aiaPs6lOzc4A2wJjnbnT2EYHG69wkWgxNGlMFkKEP7thWwmCvEwon0fQ7WP2Rm7XDnID0tG8MPzxdtqLXl4KPP2wTsotL55BPr2f3bhPSUQtjtjRNrEq92Zy/Y3X1AdRMTcZ+iJeiImrEk/dwper2y0A7shx9+iu3bPf7UFMXbnDIamRkG4etNO8nk620EWSBswLat60GxB2ShQGIU7M3u6ge7y1/cN9VnNpgvPfG0GNchybReiC5ZSKB36tQXW3f2RnVdN2EBQLvh9I+CsZGIJN9ycrmX6ZVooeHt90tB/zUZ9sBoqIEBVcJU33fsDDhwGC44d6Afukim+r4H+5qX0t/rrUWot+0QweXqaooAdyNPeR75hzvd7YW1AAl4t9uIJGEFsjZg0al79+74Z2NfNDjJqqBNyCERzPw5VDt83VFWr16Nr7/+Wgh26u8a+zhQ/YKVcEELpX81RaKnEszl5eYpJUg1fy92zKk4XVlwuHvvG2M9xd/UBEaUdaN7kNvEvHnz8McfnuwTxOugYWfgjffSAwR6OBNxZdwFyiZARZm/OZQJqy8vsrQ4fUwR6q2FYuGupCT0QqLVMQJWx+j95oOuZaz7PrMmYwXMhu1wIw0Nzl7e5odzS1ETEFFpRi8yFtQC6zeGN6OXgfTIrYNiZ1D/9elpFJH7lTvI8pml+igLuRzQGA73PKlph7xuJPca5f1lmjjBwRb4Xgv27qFFDGWRFh3hxrxW83OltYiaayvrFey94JsBQ3m8GvcUre1Q3qO52kHXbYqpvla3FC2m+moDOypja3RoB3Rob8T6TS7xrR13mgmdOxiCBmVVew8lfy3tUPs8hZu3+t6/OVwz5bvntxVOMTehOS4FXA4XzT1a7Qg5YeAf4o5AVAT6q+9+iTXrCvHMg5Nw92OvCdE7+aqzYxZWMB/0h2a8jdI95XHvg64FergcvpSbe82aNaDJPkWRlsXu6rnPXLh/wK18P4rBXqYpZor+vR4WY+P16CJpaWno168fNmztLUx7i0sCd7dzsz1plXyDrZEv/eIlHuFuRDmMhnIh1uh/S5/qYDxod13uuruQ7tktNe7xO5SsBmgHtk+fPsKCIJbyHdPH49U3fkeaZQEMaBA7r3WOMaLtFgO5BxT6+TNT/TPa9ENyal/8uNTfp5oaTb7MxFXyDcZMzQc3UmRr2mVtm7MeacnrUVbmCUZHhSLV07hyuPrC7uomhLfRsEeIcPqXl7PXz2RdWT+nOxcNziEiNzv5MAebnBGzjZs9AfvIRF5+aMkfO9Rkn1KlKUtZhQtVlVYcdlAxCvJ2CJP4rVuL/fzclee43WY43D1w1rgBYpzTTjLVh8rzrwbuPpF7wvixngwMoUxZI01gKysr8eTTH3otJnLyRyErb6S3auH8WpX+1dKM3nfiY7VasXDhQhF7wNOHKSLYJAUVVBatAl2eT1Y1c+bMEQtMwr3AMQpWR/AgeMHycKt9ZoMJdE98ALKiKIRZpD8s9WuWwWBGg7MTsrJ7IjunB5JSOmLvrnmorvhdHEeLX207nIUbr/EPlipjAihFZ7ho7sGCYKqJrUEij0yWyZSZTPWVjMia490PC1FRvh3J5iK43TXeNprMBTAmjcCusgF+Cw2y/iSYiZvH5cUgTMnJpNzzHml8vuiCkd4LdIzvIpIj/oYAACAASURBVIDyHsrxJO/hG33bd1HN7bbDXr8XZksWjKZUUbeSEAEF9fiz+t4rXGpEWW8tCyaNCw1WOBo2ocG2GZbkHkhKHQJ691RUhI+7EOm9IOukRXTqEUayHcE22Gic0D81IjrYc+07HmQ79Cw0SPeUYO83uoeahTs6LlwdW1KgqxWETWmH2nson1E18wV5ju9iTrj4HcpnVXnPUP2oHPNqNlS0PLN62xHwoVT8QW3wwkjX4d9jl0BUBLrV1oBTL7oDEyeMxYtvfo5v3n1cREiP5XLlrU+gTUY6pt15ZUJFcdfC3BucTPiaukF5ZsnXtM++nSZ5rYqKCjz2FJnA/ynMl5uj0C6j3dUb119zADp16uS9pJaALOEmDrRbazKUo31BBQ4ZUiEE4Y4dZdizh8R7oIkoiURaHKA6XTzhAAzsHxjVnio5/VmHYLU/X57eSYmhGmmWz0DCV1kanD1x5Ij+GDWyv9ePl46Ru/Pk/0SFJknhhLnvx40Ebu+exoDxIY8h8UuR1b9dECg6lYsA5Nbwy9K1WLnyX9VuDWTWX2fLQ3UdxSPIF77lTldeQAaCpk78tAYrJKbf/1AKk7EYJhTDYtoBI2rQ4OrpsVRw9hQCM9iYoQm18ActcSMlGeL5o90M8gcNV9RMxKkdaSKg4VJxKbehHWA+FG5jL9jq0wMmyY27BtSPnij8ZNGwudCzmHDy8Ua4XA5kpvyGn3/+WezQU8nKPRSFO4+E2+3/zveITmPYMROpHbRQeP9D3yLZ5AnK6HS3Q6399AB3AN9JslyE8YxHID/XgKQkD2PZDt9sAr7vHIqIn2JagiTTvwH48/M7o3//HiLa/9zvuohgfcpCVisZljligdBgzMBVEyegfXsKSugpWt5v8pxgAjfYJDPcRJwW8szGYpiN29G9cxHK9haD2PoWszkNKWltYavbLQL2USELj4zsEZg86WCYzWZdokVrxHs1goLe77dcWy3e62RNNX/BHhhogdZAC7WN73dKEdngGiDcSE48Ng3SKkmP6JTvzG++97w7lX7utHDgu9Ainye5mEFxDZSWTrSYQe8rsgajhfDX31oPs3FLwIKxw9URtfZTROwGvQtevn0dDYFOVmnkMkcuPvTP7jwATh+Loaa+p32fJy3BI9UsQIZ7VpXPfHMIdC0ZJOT91S4UKRcxlPUPFwVd7T3kNfUEootlgR5pkZPaHSllqjIta1MXGgI+OvyHuCXQ4gJ97vwlIpf4ryv/Afl2D+zXAyMPH4z2Bbk4a+yomAW3ZdtOkQedzPOpnHnSUXjglkthsTTu3sZjFHc9wCNNkuma8iVKu0nkq55k/EeYwNPuIAwmYdKbnGxCaooRJpMJVqsBtVbP3wET3DCK6N52Vy8hWmSwrqaYI9HHgKJTB4t+TnWeNNEE32j28kNlMdUjPb0MBlR4zJUNHeA2dgXthjgipOuQixrKRQw93PWeo5zApph/R4ppqUjB1SB2oXuB/H+bY+Kgp45qFwFkO5LM1UhPXQ84KXjdThgM2XAb89Bgz4PDmY+zxhVg6OB8EVVeuQhAolYEibM3WgKEW0RQM9Yb6+9ZaOjR1SCCptEunFxo8L2H/ODKj7mSGUUbp9KUsa68ppp2ND6zRciwfCosE2RxuvNgd3bDoEG9MP70nsLfO5JoSTb9jRTzD6CYFVTIwoSCFe6tyMHzrwbGoFAToVy24/brzWJxIlihdpDrTU76l3DYPQuEZBGQmt4HhUUFwlzad6xr3bGlcfjKG+vFM0S75rKQZYrD3V24tVAqvusmpnkXp8Ldw2SsRIb5A5iMnkCFY8eOxcEHHyz+txzz5AfasR1EQLlQ5teyHr4Cl/4WKoWb8r1gMW6HxUgLYNtFKj5lITeTWltnlFdTTIhuSEuX1jUO1Netgq1mKdwujzsHZQU44ogjkNv2ELz8pgmy/sprBtutVxfxnmKFOIR7w8YtNnz8ub/vPsWCMBmKQWb4kSykQr2zyNph9KhBOOCAA0QsBCm4w73jfJ9ZPQt34e5B8VbImqx/781+VmpUH4MhBaakbjCZctFg+wNul1VU0+Y8FEceORpjx/i7mqm1FpFt1SPQpQuS5xo28e00uKthrauEAZU4bGiNsHYh6x1a1FcWoykN7btMwN7ygogpEiO936SglSkeMzKAvr0aA+pJt7tO7T1uUlT0vBfovSbFv7I9K1a7xLPbHN9ZPQt3aoWtsq+1tEPtPeQ1tew8N6auVbfg1VRhKzlo2UFX4/qi970gr63sj0iWAOHeV/xbfBFocYH+w5JV2FGyF/TfpcvXiLRlJx97uDBzHzM60Nwx1vDt2l0udvvTSckpCgv0RiBaX0Iy/3fxTo/fnclIkw6PAKbSu4dnMn7DVYHm7PR3LS9TtZMTNTszdO9IZnX7eww3Rzu0mt7paXOkezSlHXpN7yhvOkW+V9PHaicncuLgP4FtJCbnqvtLoJMpp8tVj8q9v8BatxkNtkD/6Q4dOgCmHli/uRsyMrsgO6vxuXTai2CtngenwyPyKFf8qaeeim7duvkNCy3PrDyR/MRpkh3qPUDHyX6gYH2ppoUgVxlZyIqCovOff+4QDBnocdyV74MO7QwikKVvsdUbhDiQkzRy4flh0RJUVjSasFMGC5vziAC//XBm9Epz2aceMuCLL77AX3/9JW7fv/8gDDt0nDdnvBpBIU3bZRpJSoFGJZTvNvF/8bUKYelEcURoN9m3UEwPu7MLjjumB444rDsyMjIiihZajG2TuhROh8cFyGJJRrV1KJLTj8CwIRkBr4VgooX6o3DrHnRpvxsGwy402ErhctbA6bTB5WyAy+UfDFPNu8aNFHTqmCuCTVJ8kW8XZgm3pSMPz4PZ4qkXXb+yfD12FP+LBtsWv2CPbbJ7Yufu/ujY8QD07BFo7RdskqzVzz3Ywp29fg3s9RvgaCiE2+VvyUVZDjYUdoPD1Qtduvo407vrUVe1AA1WjxWJ2ZKOsaeegCFDhogFS3qfFe3wuBjQuDKbIUzhqUSKu6Bm51m2gxbkaZEu2fS7cD0KV8iaxoUMZGV5FhIc9jrYrLthNFqQ2mY8tpf0apKJu55vRzQFuto5iWSoxVRfGVuD3AgoZgv5k9M3iHZsaWFCWmHtD4GuJke5mv4I5japV9hq+T5pcX3xtTJQPhPBXMnktdu3o7hdgYvSK1d7Fiz2p5WmmncwH9N0Ai0u0KmKNbVWnDzhdtxz00W4Z/obmP/BE8jJ8ve9a3pTon8FFuiNzOmlQrvVv6/0jwJMR4QzZdX6oVJ+sAb1N+DKi4OLeOWIiCQIZY744p3AZ18GmmFTKh5aeScTY7nqHv1RF/mOZDlAwUtkTmyakJlNnlRjLjdw6DBPQJNg/tXy6pFYRa5F5CPoHrTDPf0BS9CD9UyyfMcH9efr7zjER44mI77WEqFqR/ckU/1wbJSMIuVUjrTzLK/XnAJdCltKdxdu55nuHeBL725AeXkhbHWFyM/ejtqaxsj+sq4pqV2RktYV9badsNZ6AsBRsESbcxSmPxR84VXLBCjy6Gk8wuvPui9wlgHVAKUIdP4Jg7tRhNKCweDBg/Hz7wdgU6HFmwLN9160Y71qtQ0dC1bB7F4m0tJRobgUlIqPhLnLHfzbFU6gZ6STcGoURrKvV61aJQL2kQkz7cjX2M8NGxhQryUALQQsXfoHdu70BI/0lCS4Tf0BUx/A2BXV1SkBFkJKi5Rg1iLkApOVvl64NfimIcwvGIp2HY9CUlJjRLOVq+tQU7MLo4fvEQs6u3btQknJbrjdwbN8+PYNxSIhSw43klG6Jwludwrc7mS4DcnC+srpzvJm66D/H2x3m55VZaGgZIXb7DjsoC1IsazD+vXrRSYMWcgdyO4eAKerQFiEUZC8YBZevgt2we5BljIkKI472imCCP7wUwnWrS+ByUDuL7tFvBDfIoKPunrignP6ikwHFJsi3EI4xUlJM38pMktQ6dKlC7LzTw0aV0RZP19WtKvv+Ya4hKCj92d5hSd9JVmGkaj3NdX/bG4plv2+DCnmvwG3h1tySkeYk/JhNmdgZ2mmiP1xw9VZyMzMBEV2DsZqe+HX2FPqWWSotZ+KY0YP9bocyPrKRSkhQne6RSwOer/JBX15nB6zbaUlh91ejXqbJxYKmXlv2OQW6UnpnlTkPIGe7b69G4VURqZnYTLcDrrWeY8WiwY1wtZ3l3h/CHQ1O8/hFlKDWRbRd3b+wup9wWEpTk0NDIZ62BRxSYJ9ZzducYuUgmRtScFnab6gJo6HmnZEei9s3W7DqBH1OOQgmwiI/OvvNVizthYG1MIIq3C5tDqOFS57voUFesCrPOH+EBWBPvONT7Fz1148ctdEPPLcuwLilBsmxD3M1iLQ1XaUHjMspZ/7BWdRUK5AP3dlHei8T750+gWFi1RPtaJTTzsi3Xt//K7FnExZP7WsmtIu+gCH86duikCX9SIGanzo9bRD7Q66dLVYJnazKPWgZ9K7fqNbmF+Po0lmB8/kN1hpaWEbru1SUJD/6w8/bkZREQVE8xfsbphF8Deb8yjhOtGcCw1q+sUr0INkdLHWkN//X2iT/i8a6j1mwFQanAegS9fB6Nipr/dvdnsNtm/9DeV7V4KCwFFp06YNDj/8cGGCvq04SZjqq9lVlBdVMxGneAtvv/0+amsrhOBE0n/gNvonlQ/mBxrJd5syC+Rn/wmj6x9hGi4LBaijoInEgGIfKEuwhQa1Y/3nJVvw7beLYTFt8V42K7svXC672CElxsEK+R4bjAXiH4xt4Ua2MOO2O5JRW5eMnj2SvFYUat8LTTE///DjDfhj9b8i/aGvz7pv3WkRoG3bNBHElP6tXpMKlzsVfXpmwGRJFQsTFks6rNbd2FG8A9XVpchI242G+n1pDxQgSPTTIoDDTbEpKChmgTjCtx3B3GtIPFdUNS6EWwz/YPUf81FDW6ZkbO44FFm5o9GhQ3IA+mC7cWqt4bp3Wo9ly5Z5ze8NBgty8w9EQfvDkZLamBIynLWBcsGktORXFG/7XtSzQ6cRuOrKE/zqrOZ5ohP0iE557cz0PTBjKQxOj3WL1pKc2hXtOp+LjZstAe41yvdCpEB0TTHV79vTiAzFWqJv0MIrL2wQC1HrN1kx+3MrMtJs6N6FLFYa4HLakVcwFCZTStiFBrXvBdluLTvPygUTm3UPbLbdsDfUYPfuKlRV16IgvxYpSTVirFMGm2DF7uokFj5pQU/5PMnjtVrcaWmHvDYthLucFbBWzYPLWQ6Xywq3Susgt+UC7zchXNYUrWOVj49tAlER6HvLq0DpyMisvbKqFvUNdhTkx39+PBbowQe3HkGhxpS1qY/SHQ/YYatXbxqkJdJoU+vWEuf7C0OXiEyu1gIgGgI9UpulRYMUtsrjpbDdXxYNvsLQYSdLIf9o08r6ek1aKeNAthE7SlzC7DSSOb2e5ykSW/rd1+olyULpEg1ip6i21mPirQzg5jXVT25AatJ2wEXZFmyAeSTcaCP8LUNNgHwnzWp8z9XUP9zkSnk+MXY2rBNZJ9auXev92WRORXZOPzEhLS9b0/h3SwecefoIDBzYmI5P9gNZo9DEV020abkAKX3wqR5UlIsx/66vx7vvfiJSH1KhneeOnUd76xPKNJwC0ZEpZFqqZ3GHJnwN1r9gqyW/5EYzY9q17Nt3CBpcg7G1OFMEsCPrlZQUA2pq3cIENpxljdqJuGSUlroLmUlLYK/3D6ZHpuckQvsfUIB+fdujoKAAn36dh81hLBpIRIZL5UYmvJGeJ7WiUxkkjnbI2uXvQEXZv6itKYbDUQuH3SrM4/UWMrknN5DK6hxs2pYPlysPDneeV0AorxsuTZnvsb7PFQX4W7RoEZYsWSIOMRjT0a3nicjJPdDv8ppN9StqRfyBNmmrYG/wWJekpOahrOpgGCyDkEyDSlGCufCEW1Rz1K9FkvtTcZUBAwbg7LMbM/4oF6VCvXP0CPSnn9+Ast1LYTE2Li550k82pk2VJuJUNyGed/q7yBgNlFK4TrhT1NjPh9OVHfT93pKB6HyvnZZcjpLin2CzkcWKDQ5HAxzOehgQ2WKFLIVq7GfC4eoi+qIpC3did/orSknmVpUyle4n29Eubzvc9l/gaPC1/gn+9JGlhiWpDVJTPQuP9dYykCVEckoudlWeD5c7K+gCsq8VFsXGDBXHQ95Vj0BPMa9AqnmhXwYd8c6GBcnJacjLTROxPMorU1G8MwXZ2ekoaJsBszkV6ZldQEE6qbAPut43b/ydFxWBHg5LnbVeBF6z0KwnzgoL9OAd1lKCoqnDQ+siQLwLdMlLmm2HC46mZBsLAl35MVTWMZKwbep4iXS+3lV3re1oyedJi7VIU4PwROKp93dqg9Lc1JM6i9J5BbqlPPtyDXYUrUGb1D/hsHvMgWVJSumLPVWHCf95pd+7FlZanyd5bQromGb+Tpxud3UXwed8i0whRn+TUejl72TSKaPZ09/ExC+1H847e5iILN+U58nXj5I2ZilHsppI8eTnbjFt2JdNoQDOfe4BwUz1fRcaZF3rrJ4UaOEEupbnSe0zq2asUxTyu25qEGap9O/N98i8lkxSG01TPab3lFGCsknk4/hjCrxm2/K9LF2Rstt4AllSDARaSJYLJnLRwLedwdoRbOHr8y/3YPmyL2ExeYIb0niqdZwCp8s/nWYkd4Ca6m3YXfI7KsobF1z69++PQw89FGs3dW32gHoWYzGyUz+A01kvnsXDR5yPrdtlENDgGSSCZV5QE8DNWvOXWMggdwtZ0jMHICvvSCQltxN/CpYuL5gZvQE1MDR8ALhLxYJLtf0cXHNF94AFOa0CXaaJU471UJY1hYW7kWJejOQg2SbkNcjqIzk5WViqkMuIbzEa9nqDhlodR8HqOLpJAl2t1Yvve+GpmWtRsednkV1CFnI3IZHtRibcyMDQwW0w6qg2wnVi8a+pYhz6+qCTxc7Gte+Adt/JQqXafj6entY54FOjdgFS+Q5VY+I+eUoF0i1zvOmDk1IPRnLqEBiMaaio9ixsN6cvfUDj+A9xSyCqAp0iur81ex5ee/I2ZGdl4MmXPsT/PvhGwHvx0ckYNXxIXIFkgR5X3aW5soki0DU33CfwViz5OcVafwhhuMONz75yqvJzlxNyNbnWffusJQW6dBVRpnIjIUj/fEssB6/RIp59zWRJQFLANAOssDmHw+X2WHYFE4T+Fh2NFinh8tFLfpEWvHzrTz7EGZaP/aLqa3mGzZauMFiGoLSsH3r2SA660EBxFuietJMu/YnVxKTwrUc4ga4ninu4Noa6l9Z2UN+rSVuoJ25EJF9T6YOuFNzK54qC/CkzDwRjo+a9IK+dZNqAVPM8mAyeqPt6C+1Q1jsPwojhB+P0UzzPippsKXScr9WImkU1k6EMX8yZJaK+p6bmY2cF7YCGjl2kza/aivraP5CbuQxWq8cSAAYLbPbBaJM7AmlpjXET6KdgC0WSv8cHvfFd6XbZsat4Nmx1nh3fI48aj+OPG+SHXKtAV7PQQHx3796N195YiAZbo5UQZW2xOSh2RjrIgoViOPTsHugy4nsPWhgpKvwWZXs9Zv4OVyecdfbZ3kCbegPRpacB/foExoFYt8Eldq1JoNfXrRHxLLwLJgYLklIGIyV9OAzGNqI+oXzQlQKdjiWT/U3rPgAtMtGi5YUTzkHv3r39+kOtQNf6Xvj7778x++OvYDTYYLFkoHuvM5HRptF9ieJfKN8LahYHqfKxNDfT+z7h88ITiKpAv+aOp5DVJgPT774a6zcXYdzl92D8KUejsroGu0rL8eHL98dVf7FAj6vu0lzZWBOEmhvQhBOkr2Ow3ZsmXLZJp8Zif6iZJCsbHSlFkPJ4KQzVBLnTC1hNO7ROTvTWRc95egQ6TRYtChdsLeaNWkz11Qp0OUmmPON7RcAsAwxGE4p2GGC1mTD6KDM6tDPBaDRi/g/Art1G9OxuQpsME9kxC1NO8n2mCWwo03DJV8vzRGOWisztTmIoWPwIryWACC4WOBEP5tahNhCdcsFI2Y5xpxKfwHsqx1NLjXVfX1PlPcsqXCJyerAxozdWiJZ2yJ2+Hdt/EEIFcMHlcqKm1gW4nWib74bT6YTL5UJllRMGg+fvFJmdCqVcrHcdLOIWUIlWrnWyTJg1a5YIqkcC05A8AdnZ/sGylJkXqH6hTNztDVUoLfkNu0pWeYPykVnxYYcdhj/+PRibCv13kpX9GGwRINT7Ks38NVLMf4ifR4wYgRNOaPSnb26BftHZZdi0YZGf+47R3Afp2aNhMnusAKiEW2gItghQUb4WWzZ8KVyZKEPD+PFnijSELRGIzlqzCu1yfkVNjSewJ7ll1DUcgn79D0Vejn8WJT3C9relH4IWqqicfvrpGDp0KCg4HJWZ+95vFBOAFsd8M2HkZhuQm+Php/YbOP1+p8jS8e+/HouTBmc/1DpODenGwjvooZ6i1v33qAr0MeffhivOPwXnnH4M3vzoWzzx4gf4/ZuXRZT3Y866CT999hzycjwrZPFQWKDHQy/pr+Oin11id1TLRFz/3fjMSAS0CIpI12qu39VMkpX30irQm6uu4a6jph1NTY0YjXaoGSOSP+VFprRTviWc+bY8Ts09gvU5/S1S2ki1O2V0La2TZGWd9LRD7UJDpL5uij+r8tpyXIbLWOB7jpqxruxrNaasWv3c5T28zxWlQqv3mG9TlpBI7VHTDj27cXotAWR71L7fZP0jxXQgf/oXXvwQlRWbAEMSevc7G5ltGl02gi1GNS4UOdAuvxxulw0Ntn/QULfCOxTIzH/4iBE4eczB4m9yochj4eQGxeUwGiH6hMpJx3kC2MqFIl/ro+ISt8iUkpkBkTaWzsnNBtKTlqOuar44v0+fPuje+z+oqm4+U/2Vf+yE3fojkkwbve2ypPTB3qrRIitEqKLJ2uCParhsn8FspIUdYNiwYdi84wRsLjQhUiA6+b4LtWBCwSMpcn/x9qWA2xPUMC8vD8OHD8cvK4aIBcFg72mPC5Nbs2l4mvlbpJg9mQJGjhyJOd+NjPSq0nwPi3EzOuR+KQLXUdaJvdUnod45IOx9ggl0TrMWsWsS/oCoCvRzr56KE0YdgisvOBVX3fYkrLYGzJo5BZXVtRhx2nViB31gv8YXb6zTZ4Ee6z3UtPrpiRTftDvy2eEIyMnm/vY91zvZ1zqBjeZokKn51ES8VxtJOZr1l/dSIzr3h7CNxCLUBFaeFy5IXLhrBzMNl8dLYRjMnSHUNSMJdGWKx1B+1cHM6dWamUZiGel3NcJWOZ7UCHRpdfTtAk+KzmAp6cLFAVErbGXd1LTDG9gxxZMDW1nCBXALSL9I5uxhLAGU77fH7rOETUeqxeqF2rH4p7lINq0Wt3EnnQG30RPwjgJ01tXVo2unvRg9ogx79uxB4dZSFBXtgdGwL+G7T8Mp6JnVcQTsrj5hA7gpWUXKThEqu8OmTZvw4YcfghYaTOa22FtLpvqKlUGfm6kRz3W1O7GjaBGqKz0pLqnQzvYxxxyDLdvzRTC2cOPQ9z2vXChRtpv87x1O4PijlmPlcs9ig9GUiwrrWRg8qAA5/t4AQa13gr3fSnYsRmnJMjgdnuwaDldnHHvskThmlMdKQ817OpiwjfT8nzN2Gb7/3pMpoN55IGrtZ4jMHMpCCy2UbVHt7jYtNvz263ykmFeJS1Hcj/HjxyNj3yqw2veb2l16NnGP1NPx/3tUBfpzr3+Cl2fNxanHHYGvFvyKB269FGePHY1FS/7AdVOe4R30+B9P3AIm0KoIqJkkK4FonYjHGlBlakQyLaZcxKHSxEWz/moEugwWSRGYaRJGPqQWs8evkQrlVA6X/u/PNW68/o6jWS1r9ARRklx9zWXpb5FM25vSH5EEury2lijH8hytWRH0tkPLM9uUdlAKMTWuBrIdclxG2jmXx6tph57Jvl5LAFkv+X6I9D6QrjvKOB7BYjp4FxrMvyLNvFDcisyGKXaEyUgBzYKn2KLjjKZsISiNpnwkpR4Ik7kDduzyiK9gi71qMy8o+8GTM94YdBGD/KnfffddVFdXi2BlKW0uQGqqJ42eLJFM9QcPqBV52UtLfkdleaOPOXEYN+5YHDLU/3pqBaGWd09Om71isYEWQajktTsJXbsd4teOYGN+6bLt+HzudpgMRaAAgEafWAgNzj6odw6H3dXZrz/k8yDN0GkhwO7wZEtJTYHIV+67uKhlrP/zzz+YPXu2qDelnTzksPNgNPq7N2gxo6+tKcLWTZ+jvr5C+LmfcvLxwm3Ct6jtD7lwKiLeV0Bk26D2UkBAWijptM/CJpQ1lt73Ip8XewSiKtBr62yY+tSbWLpiDUYNP0gIdLPJhLMm3g+T0cg+6LE3PrhGTIAJhCGgZpKcqABjcaFBi1uKWj9QZf9JYaEmOJzavtcySVYKn1gW6NJMU03gM7UR1tUyDXWclmc2mgJda7vUtEMZwK1PL4Mww1652uN7O2miJ3uO75iiNu8td+P3lS4hDjq0MwiTb99c61oygkRql5p2yH6gumRlrEVp8Sd+l6XgZ2ZLLgYOaCtMpO3OfHz1XTYyMvNx8JDAuATBLFKU9Xxguh3lFZGDcam1BKA83TOeeRtORykMBjN69BmPrOy+3tsqhW1RURFWrd6OX3/bBoupGAbsC2a37wxLSn9U20aizpYfNsI6pSEMlwZUmc6U/K3zcg0oKvYsYAZLZzr9yW9hq/1N1CQ1vTfadjwTRqPHRKOqshLbthchP3sH8nOKQe1QFoero0jfVu8cJlLSyRLO9YUsOsJldZDXUPvMbt68GW/P+kCkPUtJzUfvfheIFG2yhBPotBgjLVIMjh9gcCwVp1FavlrHmXh6mv9iCf2m9f2mtH6RlgyxZEEY6dnm35tGIKoCvWlVjb2z2cQ99vqEa8QEoklAzeQymvWJ5r1iUaBrcUuJRVN9cYo6IAAAIABJREFUOZ5kZOhgvpbKPo6mQFc7vvQEPqO2U/HNGd8SgRFpjDw+0xE0Ur+yfbIdehYa1AoKtUyVx2l592hdjFKaJAcTK3rrracdStFVW7Md1ro9Igd7vT0Pf/yd6tefelxGlPWSPumRAqU2unX4Z0UItojx7MtW7C6eDYvJk2u9Y5dj0K7DkbA3VKK0tBjbtm9HekoxnIr0j7JuDlc7ONwkbA/xS5UXTtj6tiucy4uWZ5bG07ZtW5Fu/lxkmyCrAIe7M8yGHQHZJ8gPOzevMzZt7YKUtE44aHBnGI1mP9zhFkz0Cls1z+zNdxch0/K+yF1PUdZ7H3ChEOtUfMf8sSMd2Lt3Lxb+tAd/rymD0UD/qoS4p6wbVKyOUbA6jhT/O5j5ud52KMclC/TmevPE/nVYoDehj1igNwEen8oEEoCAlsl+AjTXrwmxKNC1MI5lga5sR7hAlbIdNCGlEipvuBY2TT3W10yTdiEpRzX5vaox31ZrRt+UOhKzcG4M8tpaRIs8R+tEXG879Ah0GXCL3CBCCQn6e6wLdF9m4YLEaQm6qLcflGNFzbOalboAJrdnB5pMoknoKQvlKM/N7YTN2zvDYOqMgoKOMBj8zbDDmeprXfDS8szKd056Wj2S8CUMrnXe6ruRgwZnZ+TmdcUlF3RD27ZtQ0bVlyeFE+iNsR08Y5b4houVouWZpWeVUg/mZbwDh71CmLln5x8Nl7MeVVVlaKgvR0pSORz7/OSVfWQ0JiMppQNyC05AUnI74V6k5rkKt1Ai7+G1fpnrBC3USlcyem9REEkuiU+ABXoT+pgFehPg8alMIEEIqJ3sJ0hzvc2Id4GuFFSxEHRHWgCQ/6GvsA0XyC3YQoOaCWA0xqOamADKetCkmUyrpz+gyIEXjQor7qEnwjq1mSbXy1a6xMIE9Z2a4Itam6dHoCvvoTfwmda6hjteTTvCRaOPR4FOPCgFG6Vik8XpyhK70Lm5XXHR+d3Qrl27Jglbve83Nc+s0iKjopzSiRmQkdkV1bVpAXEwmsOiQe3CnZZnVl6TdtAzk96HybAr6FDNz88XrhNVNbnYWJiLdgV56NEjH2Zzmt/xC35qPoEuL5wo39nmfGe0lmuxQG9CT7NAbwI8PpUJMIG4JpAoEwe1E79odpaaSbKsT2MecXU7TLHaDt8JqZrd7Wi2Q+tYVyM6m1p/LZkXvIERFanc1Kb9ixUT93DMgkU/l4HblOftKHHBZgseJE5vv6h5Zr0pHnsakZEJ2OoK4XJZkZzSBVZrBtZvdjW7qb7W95uWdlDecC1R3COxDWe+Te2gFIR33OhvHh/qmmqeWW9gShHF3Q6DYx4AE2DIQ4MjD7b6XJxwbB6km4OetIW+9dPzXlDTjkhc+ff4JMACvQn9xgK9CfD4VCbABOKagNao07HaWK0T2Gi0Q80kWVmPRGlHNPhqvYfWSbKeibjWOuk5Xm079oeJezjTcDURuoMJ9EiMmtOfV80zq9x59q1fS1kCaH0vtGQ7mtIfNCa1xKRQM9Z9I6sr66Ylirs8l3fQI/Uw/66FAAt0LbQUx7JAbwI8PpUJMAEmEAMEtE5go1FlNZNkFujR6AnPPdRM9n1rkygCPVzqsOjRb7yT2mB30rogUjT6ww82gaKWN0eRptVq3FG07jw3xZde+nBHCnYnGah592hdaFBeu0dXgIIoqommr7dv1DyzLND10uXzokEg6gLd7XZjU+EO7CzdG9C+4YccKNKuxUthgR4vPcX1ZAJMgAkEJxCLAl3NZJ8FevRGtJrJfiIKdCXhcLvc0egNtQJd1iWalgBq2q9V2CqzOijvoSZ9oZp6+R7DAt1DoyV20LW4pcg+0fru0drffHzsEoiqQF/513rceO9MlFX453OUeJbMfQFZmemxS0tRMxbocdNVXFEmwASYQFACsSjQ9XRVLLZDzWRfT1ujfY7WSXK876BLIaEMdtec+c/19KHsB0pDaLYAFRWeq8RCsDs17dEr0CNdO9ZN9WX91eYoj9ReNb+reWajvYOupt7KY9S0Q891+ZzYJxBVgX7hpGmoqq7F1NsuQ7v8HBhNRj9C9DcSvfFSWKDHS09xPZkAE2ACwQnEorDV01ex2A4W6AaECsSmp4+beo7WyX6sLTRoTY0YqzvolBIxLdV/rltndQekSJRZHaxWoLjEDbMJyMwArDbAVg/kakhfqHbsyPReeTmGkOb/WhcalAJdTY5ytfUNdZyasS7fmWRuryxlFS6xAORrNSLfZ+T6Qf+UJdKCkZ42qWmHnuvyObFPIKoCfcz5t+GMk47CtZecEftkVNSQBboKSHwIE2ACTCCGCcSisNWDKxbbwQKdBbqesRzqHBKsdTaAAlT65obu0zP4xk6sCvRwTIKlSIzVdqhdaFAKdGX7m9MCQHmvSJHhI43PYAI90jnNmbKTBXok2on7e1QF+j3TX8eesgq8NP2WhCDKAj0hupEbwQSYQCsmEIvCVk930ESOSizt2EqBfsFZJpEPPF6L1klyrO08S+6trR2xJmxlSkT6b3mFGyRwU5OBLdvcootOOo6C1hkCnhXZjlgJ2hfKksH3+Q620CBja5SVu1FeAeS0gAWAlneMDKD37QKnOK1HVwOs9RCWDDnZnn7wdeuQ1gXLVriwbCX1H0Tqt/Ub3aipBcaNNaFTByDUgpGWuslj5ZhpzoCGeurB50SfQFQF+s7SMhx/zs2YfNXZwsRdWU465jBYLOpyHEYfVeAdWaDHQi9wHZgAE2AC+gk8MN0uJovNueuhvzaJdSYJi42bXYj3yWVrE7ZyFMb7QoOsP5mGi7zjZBpu8zdb3h9PnN5gdwFz5uOM3hzd0WyHFI3hhG2whQZZx1izrPH1RQ+W7k7JNpq+9NHsV75XbBGIqkD/celqXHvXjJAEOEhcbA0Org0TYAJMINEJyMmm2jREic6D2xdIgAV6fJrqS4EeK8JW1kOrL70M2kft2bTFDdqdpl3a/R20T6uwTTSBHg1fen4ft14CURXoFCTObnfg0buvQtvcLJgUQeLSUoNEXYjhvuEd9BjuHK4aE2ACTIAJMIFmIKBVoFNQr6KdbqSlAJ06xk7gW63tiPcddNkPHpNkFw4bZsRhBxsRLgBaMwyXiJfQKtBjXdhmZwMOO4SZdzDT9lA70Ps7bZ+sl9aFBrmDrmxXS/jSRxxMfEDCEoiqQD/lwjsw9oQRHCQuYYcTN4wJMAEmwASYQGIR0CpsY7X1WtsR7wI9VoWtUhiqda+JVdNw3/HeGgS69KWnoIUUTZ/80FNTgf+QD3oMLcjF6nuI66WOQFQF+vQX3se/G7bizWfuVFe7GD+Kd9BjvIO4ekyACTABJsAEmkhAq7Bt4u1a7HSt7Yh1gX7/7ZaQqcB8If65xo3X33H4pcxqMcgaLqw1QGWsCXQaH1Sef9UToJJ2kNVYjcRaO7TuoMsu1vo8aRgafCgTQFQF+mffLAZFcr/8vFPQviA3AP/ZY0chKckSN93CAj1uuoorygSYABNgAkxAF4F3ZzuFiXS8m7BSejIyvVe70xerAl1r3Ahp6r6/TduVgy/eBbpsj952jDvVhNFH7f/sDrL+w4YYUVMDrN/kUmWqzwJd1+uUT1JJIKoC/ab7nsd3Py0PWTUOEqey1/gwJsAEmAATYAJMICoEEiUavVZYsSrQtbYjVo/XK2xjxXdbr0CXJuJqF4pauv9kP/jeR42pPgv0lu6Z1n39qAr0REPNO+iJ1qPcHibABJgAE2ACTIAIsEBv2XGgVaDLPNzxbgnQslS1X52ENllZ7ChxIyXZE9ixcwcDxp9mCnsxFujaWfMZ6gmwQA/Bivzl3549z+/XoQP74J3n7/b+jQW6+oHGRzIBJsAEmAATYALxQ4AFesv2lVaB3rK10X/1RGiHnrHOAl3/mOEzIxNggR6C0WPPv4ftO0px+7Xne49ITragfdtG33kW6JEHGB/BBJgAE2ACTIAJxB8BPaIl/lq5/2qcCMKW6CVCO/SMdRbo++/ZaQ13ZoEeRqBXVNXgsSlXhRwHLNBbwyPCbWQCTIAJMAEm0PoI6BEtrY+S/hbLKOh9ehr0XyQGzmSBbka892EMDCOugoIAC/QwAn3+j7/jiGEDkJOViWOPGoaDB/f1O5oFOj9PTIAJMAEmwASYQCISYIGeiL3a/G1igc4CvflHFV+x1Qn0FX+ux8q/1gfteRLiZ40dJX6bO38JCotKkJxkwd/rtmDB4pV4+oFrMWb0Yd5zSaBXVVXxKGICTIAJMAEmwASYQEIR2LLNhNffsaB7VxeuvLAhodrGjWk+Avc8kiIu9vAUW/NdNMpX0jPWX3snCYXbjLjiQjt6dHVGucaxf7vMzMzYr2QM17DVCfTFv/2JJcvXBO2S3OxMTJwwNuhvdz7yCioqq/HS9Fv8BLrdbo/h7uWqMQEmwASYABNgAkxAO4GNW4D/vuFGz+7AdVfEtxm29tbzGWoJ3HKvGx3aAbdOit8xwmNdbW+rP85sNqs/mI8MINDqBLreMfDMqx+Ddt9nzZziJ9DdbrfeS/J5TIAJMAEmwASYABOISQJs4h6T3RJzlaJxkpbiSU8Wr4XHerz2XOLWmwV6iL6d8cpsnH7iCHTt3B7rNm3DZTdNx5UXnIqrLzqNBXriPg/cMibABJgAE2ACTAAA5d3+bYUTuTkGHH6wkZkwgYQlwAI9Ybs2bhvGAj1E15179VThey7LmScdhXsnX4yU5CQW6HE73LniTIAJMAEmwASYABNgAkzAnwAFu+vVw4AbrmLTbB4b+58AC/QwfVBdU4fyymq0zctBakqjMJencBT3/T+AuQZMgAkwASbABJgAE2ACTIAJMIFEIcACvQk9yQK9CfD4VCbABJgAE2ACTIAJMAEmwASYABPwI8ACvQkDggV6E+DxqUyACTABJsAEmAATYAJMgAkwASbAAr25xgAL9OYiyddhAkyACTABJsAEmAATYAJMgAkwAd5Bb8IYYIHeBHh8KhNgAkyACTABJsAEmAATYAJMgAnwDnpzjQEW6M1Fkq/DBJgAE2ACTIAJMAEmwASYABNgAryD3oQx8NZbb+GSSy5pwhX4VCbABJgAE2ACTIAJMAEmwASYABNgAh4CLNB5JDABJsAEmAATYAJMgAkwASbABJgAE4gBAizQY6ATuApMgAkwASbABJgAE2ACTIAJMAEmwARYoPMYCEnA5XLD7XbDZDL6HUN/K6+sQU2tFe3a5iA5ycIUo0DA4XTCaDDCaDRE4W58i0gEqD/MJlOkw/j3KBCgd5LT5QraH/QeK91bjqzMDKSmJEWhNq37FlZbA8orqtC+IC/ku4rfZdEbI+H6g7/l0esHeafK6lrU19tRkJ8d/ZvzHf0I0LehrKIKFosZWZnpTIcJxBQBFugx1R3BK3Pv42/g069/wjUXn47rLx8vDqqqqcPwsddi3vtPoHOHts3eCvpwP/DUm+K6U2+9zHv9P//ZhOumPIOyimrxt7TUFEy5YQLGnTyy2esQqxc89uzJ2LW7HK89eRuGH3KgqOaPS1dj6tNvYuHsGS1SbZpknXv1A7jqwtMw9oTh3nssWLwSN9z7XMA9V85/tdUsnOyP/thWXIqTJ9yO7z54Eh3b53v5jzzzeu+z4dspc/43Db17dGqRsRFLFy3auRtjzr9NVOnnOTORk5Up/vfTL38kFvUeuv3yFqnu3PlLMOPV2QHP3y+//407p73s7ZMLxh2Hu66/sNUschGXOx95BUMH9sE7z9/tZX/WxPtxydljcNqJI5q1P66/+1ks/GWVuGZudibOPGkkbrnmHL97hHqXNWtFYvRi0f6Wh+sP/pYD0eyPPWWVuPiGR7C1aJcYnb26dcTECWO9zyB/y4FofsuXLl+DG+6diTqrTfTHoQcdgFv/71wM7NdD/P/W/i2P0Vdoq6oWC/Q46G76iCxaskpMMn/89Fnk52aBVmFHnHZdiwj0eYuW4eFnZon7nTV2lJ9AX/3PJmzYXIRjjxqGzIw0vPT2HLz09hdobYLQbnegY7t8fPDSfaBo/i0p0J986UP874NvxEidfvfVfgL9+8UrcNcjr+LjV6f6jeSunQpEvVpDoY96NPvj/GsfAk1uqSgF+vYdpaBVeVn+WV+IWx/8rxCOZG2S6EUKdBJntGh389UecfbUSx+hoqr5Bfq24l2YeOuToPsSX98FMpoQjxp/o5gEX3XhWBTt3INxl9+Dh++4otUsKJJAf3DG22IS+uKjkzFq+BDRHy0l0J9/4zOcOPpQ0Pvn1xX/iMXcD/57Hwb17ynuG+5dlujPBrUv2t/ycP3B3/Lo9kfpngp8/u1inD7mSKSnpmDWx/Pxvw+/xU+fPScse/hb7hHo0fqW/7ryH+zeU4Gjhw+BzdaAB2e8Jb7d/31ssngVtfZveWt4H8d6G1mgx3oP7fuoZ7VJx++r1mLY4L6447rzAwQ6CfbHX3gf839cjsyMVJw1drSYlNbW2nDVbU/i3psv9q4M7t5bgUlTnsUT912Drp3aBRCos9ajqqYWM16ZjZTkJD+Brjz4o7mLMPP1T7Dw42dgMbcOc1/6iFx7yZm4/8n/YebDN4jFCqVA37R1B6Y9Mwu/rfpXrJRPunw8Thx1CCqrajX3R0VlDWwNDbjg2odw81XnBAj0qU+9icWfz4yDkdwyVWxKf1D/zHz9U7z65G1e8+fFv/0pFkRee+r2oDutNNEqKd0LEupKga5s4TV3PIW2eTkttnPcMkT1X1UKdFpIumPay/jh42eEKadSoP+wZBVmvDwb9JwMG9QX906+GH17dsabH36Lwu0leODWS72VeHnWXNTWWb1i37d2ZCpNQnzhz6vw2ntf+gn0hT+vxPX3PIclc1/wmi9Of+F9kKh/4ZGb9Dcyjs4kgf7W7Hk48tCB+OnX1fjktYfEmPYV6E6nC2988DXe/3wBqmusOG7kMNw1aQLom3PzAy9i+CEDcPbY0d5W064sWfGMGX1YRBL0bJ53xrHC8odKuHdZxIslwAEk0KP5LVciU/aH7++t8Vuupj+a81vuy1u+K2fNnCLegSTQ+Vuuf26l51vu2x/S2mj1gteDukq1tm95Arxu474JLNDjoAvpI5LdJkNMsq645XF8/+FTSEtL8dtBv/2hl7B24zYxiSWfmkdnvoebJp6FCeOPF4Kwc8cC3Df5YtHat2fPw8df/ogv3nokbOtp58XpdAYV6Cv+XI8v5v8CEjO3XHMuTj3uiDgg2TxVpEnO/Tdfir/Xbsa8Rb/jszcexs/L/vKauNc32IX584F9u+OSc07CslX/4oU3Pxe73P37dNPdH2Q6TC4Ovibu9FG/8d6ZOGPMkUhOTsIhQ/phzOhDW5VvdFP6o3uXDjh63A148LbLcPKxh4sBQrt+XToW4M5JF4QcMOTiQPcNJ9B//2MtLr3psYgivnlGZWxcRU46SRRfd9cz6N+nK+6+8SI/gb5xSzHOuOxusbN99BGD8c4n34FYzXv/SWzYUiQWohbMfhrt2+aCnqWjzrgej06ZiONHHhyykd8s/A1P/PcDP4G++Le/QJOqX798UVj7UHnvswX44PMFEd99sUGz6bWQAp3ccY48YxKefuA68X7wFeizv1yEx1/4ALddex46FOTi2dc+Qcf2eXjuoRvw+vtf46MvfsC37z0uLHLWrCvEOVc/gEWfPIO2eeF9aMmU95QL7/DbuZctCvYua3prY/8K++tbTmRC9Udr/pZH6g8a4y3xLaf++Oybxbhn+uticZ0sjvhb7tlB1zu30vstl28NcgWib5PSGpF+b43f8th/myZ+DVmgx0Efy48I+fLRhL975/aYfPXZXoFOL/dDT74GT9z7fzjlOI/IeOz59/Dbyn+EePzup+W46b7nsfzbV8Qu4emXTMH5447D+Wcep1ugf/ndUny14FchUsk3fsL4E+KAZPNUUX5EDhrYG8efc4vY7ctIS/UKdPJ7pUWR7z96Wkx4qRDzkYcPFpNgvf0RbFL719otIJcECnCyY9deMZkmP1sSRa2lNLU/HnnuXRRu34lXnrgVZF0y+j834dPXH0K/Xl10C3SK4XDeNQ96LV5aS1/4CnRyhbnkxkeFuPvoi0VeE/fnXv8EX33/q3DPobK3vEoskjz/yI04ZsRQIerGn3I0rrzgVPGsTHn0NSz54nkRyCdUCSbQyapo7EV3ok+Pzjj3jGNQWV2H9z/7HrRjHGlxMlH6Swp0mnSSKxItqlLbaWxKH3SyBDmgd1fcf/MlotlSKCz54gWxQHLMWTcJ/3XyY6dnZU9ZhRD64UptnQ0XTnoYGelpePOZOwMCjbZ2gR7tb3m4/mjN3/JIcyta1GiJb7lnIfJh8QxOunyceJT4W94o0PXOrfR8y4m93D33jSsk32+t9VueKN/AeG4HC/Q46D3fj8iqvzfgwknT8P6L9woTW5rkks/O2IvvwtfvTEe3zh6TdfroTn36Lfz+zUtoaLDjyDOuF0KSfj/36ql+Zp+hEITbQZfn0Oo7BT6hSTjtOraGIgUh+XO+9t5XmD13kRDejzz3jtjBo4B+5B7ga3ZO5vDVNXViYqu3P9RMauneNF5CmWklYv80tT/+3bBV7CjSbvh3i1fg2x+WiecrXIm0gy5FjowZkYjcg7XJV6DTohFNbmkXKje7jVeg004FlcemXOW9BPUh7ajTouG7n34vrHzonTLp7mfFQskNV/wnLMJgAp1OoAn2q+9+Kf7bqUM+/llXiC6dClqdiTsJdMq6ccxZk3HPTRcJ/1cp0CkYElleyUCfO3ftxfHn3uJdpKIglLlZbXDXDROENcMzD04S1lyhCgWBu/He51BSWoa3n5uC7KyMgEPVvMsS8ZnZH99yNf1BrFvjtzxSf5D1W3N/y4tL9uCi66eJoGSP3DkxYPFKjnv+lmufW+n5lssNFVqgPOf0YwJeO631W56I7994axML9DjoMd+PCFWXTHBJlNOLhQR6ZnoaRpx+nZh0jh5xkGgRBYf5euGvQrRToSjKf6/dIiJJ02r6tDuvjNhyNQJdBmKSOywRL5oAB/gKQgq+NGr8TRg6sDc2FhYLgU7+teTjTztQ5G9IhRZVpLmv3v5QM6mVZr0r5r0i4ge0htIc/UEC/YSjD8GceT8LoRgpK0E4gU5+0WQxQSbzMutCa+gHaqNSoNOu0HnXTBXZDjoU5Alf/Cde/ABLlv8trHuo0PvosFOuwdMPXCv8mslPmcyxySLotof+i2/enR40VoYv01AC3fcYelbJ0ohcFy4668RW0SW+O+jUYPJHf+ujb8W74f8uPkNEkKbAeUceNgi3XnOuYELRja+89Qlv/AByY7rmjqdFcL2Zb3yK7z54KqSooOwiN9zzHKzWerz8+C1BxTndQ827LBE7KNrfcrX9Qaxb47c8Un/QTndzfsvJhPqyyY+JuDUUdyNcmk7+luubW2n5lpP1IcXZCBU4tDV/yxPx/RtvbWKBHgc9pvyIyFVCqrpMs0YCMCM9RfjvlFdWY/L9L+DEUYd6U9xs2bZT7LJTmTXzbgwb1Cdky8kE1OVy4eFnZ8HhcOKBWy6FyWQSwYXIb4p2xg4e0g9GgwEzXv1YmActnP20188zDpA2qYq+gpAuRH6t056d5Y0iTfxPPO82nH/msbhywlgs/2OtCFblG0VZS3/QR8Ltcov+I3eCsccP95r70r1ph3FA3+6orK7BbQ++JIL1vTHjjia1MZ5Obo7++OSrn3DfE2+IZpPVCaUPDFXsDqcIEnfSBbeLBTBKs+YbIJF2PigGxPcfPdXqcqsqBToxpB1YSiFEZusk0KUAJEE+4pCBYrf8xbfm+Pk1U4A5sgIiYU9mh6EKmR/SO4qsHijN2rz3noDBaPBOfMl8vk1mOvaWV4pggBQojd6Z4fo3nsZ+pLoqBTrtpp543i0iQwdZMJBAp8XcT7/5Cc9MnYR2bXPx8DNvY2dpGWa/8oDwO6f3zzH/uUmcQ3FNaAErWKHgorQYQ8fPmDoJGemp4jCj0eh19Qn3LovUlkT4PZrf8kj9wd9yTxR3iu8jUwEq51bpaSnN9i1ft2k7xl9xr4jXc/0V48VzQSUtNVmko+RveaOJu8w2oWdupfZbPmfeL5jy6KtiwZYWTGTJycrwfh9a87c8Ed638d4GFuhx0IP0EaGXhkxZRFWmVT9a/ZMCnQQfBQujiKNUaCedJmAyOBL97aLrHxET1a9mPRY2BRf5MZN5vG+hiTVNsCnSK0UalYVSG5GZ1hEHD4gDks1TRaUgJD/N48+5WYhmmeaJorpTei2ZY9M3h72shdr+kH3tW/sv334UPbp2EJYRFMhJlsEDeuGJe69B5w5tm6excXCV5ugPMv89/NT/U+W/T7uwsl8JD8WAkO4McixcfPaYkEImDpDqrqIU6Eu/fBFt9gVmo12oMy+7xyvQ6eL/fXuOEIaeCWqKeFdR9HBZlq1aK3aa5K56qArJgHO+v5PolObzz7z6sTBxpzLy8EGYeuvlrSLdneShFOj0d/l+lwKdxvKUR18T/v5UyA2KslP06t7Ji5UWUF7432dh0wVKqxJlX/k+H+HeZboHXRydGM1veaT+4G+5R6BHmls117ecrHxoTqAs8n3F3/JAga5nbqX2W04Woh/OWRjQH3I3vbV/y+PotZqwVWWBnmBdSymgkpMtLbpzR7sge8uq4IYbBXk5QVNRJRhWXc0hS4SS3WXC/5aC87VUsdU3iOBm5OoQzN+zpe4bb9eNVn/EG5f9VV8at2RW274gt8WyDtAuIi1Kti/IazVpIPX2JwXVo3zAtOjKZf8T4G/5/u8DWYNofTv4W66uz6PVH+pqw0cxgZYhwAK9ZbjyVZkAE2ACTIAJMAEmwASYABNgAkyACWgiwAJdEy4+mAkwASbABJgAE2ACTIAJMAEmwASYQMsQYIHeMlz5qkyACTABJsAEmAATYAJMgAkwASbABDQRYIGuCRcfzAR8LpjIAAAgAElEQVSYABNgAkyACTABJsAEmAATYAJMoGUIsEBvGa58VSbABJgAE2ACTIAJMAEmwASYABNgApoIsEDXhCv2Dna53CirqBIpvig/ebBCkZLT01KDRhKnyL319XYU5GfHXuO4RkyACTABJsAEmAATYAJMgAkwgVZEgAV6HHf20uVrcMO9M705mQ896ADc+n/nYmC/HqJV24p34Zo7nsbWol3i/1Me8/tuvkSkGyLRfvENj3h/69Wto8jbTDk5lWXGK7Px2ntfwTe3cRxj46ozASbABJgAE2ACTIAJMAEmwARikgAL9JjsFnWV+nXlP9i9pwJHDx8i8tc+OOMt0I76fx+bLC5w1W1PIiM9FdPunIiS0r045+qpuG/yxUKEU47Vz79djNPHHIn01BTM+ng+/vfht/jps+f8dto/+2Yx7pn+urgeC3R1/cJHMQEmwASYABNgAkyACTABJsAE9BBgga6HWoyeM3f+Etz5yCtYveB11NbZMOK06/DO83dj6MA+osbTnp2FktIyzJx2Y0ALinbuxpjzb8OsmVMwbFBf8fvvf6zFtXc9gwdvuwy3PvhfFugx2u9cLSbABJgAE2ACTIAJMAEmwAQSgwAL9MToR9EKEucbtxTj41enYlNhMU6/9G4s+uQZtM3z+JfTLvmceb+I35VF7pQv/nwmcrMzhen7WRPvxzMPTkK7/ByccdndLNATaKxwU5gAE2ACTIAJMAEmwASYABOIPQIs0GOvT3TVSO6ev/bkbRh+yIFY9fcGXDhpGpbMfcEbPO6juYvw0ttzsHD2DL97bNhShAuufRiXnD0Gky4fh8qqWpxz9QO45JyTcMG444ToZ4Guq1v4JCbABJgAE2ACTIAJMAEmwASYgGoCLNBVo4rdA3/5/W/hb37/zZfgnNOPERWVO+g/fvos8nOzxN+C7aAXl+zBRddPAwWYe+TOiTCZjJi3aBlufuBFXHz2GBgAlFVWgxYAzj3jWJw9dhT69+kWuzC4ZkyACTABJsAEmAATYAJMgAkwgTglwAI9TjtOVluK6YfvuALjTh7pbQ2lT1P6oD80422U7in3+qDTzvhlkx/DsUcNw72TL4bZZPKK+wU/r/ReiyK+v/vp97j6otNw6nFHoFf3TnFOjavPBJgAE2ACTIAJMAEmwASYABOIPQIs0GOvT1TXiPzJpzz6Ku6cdIEQ2bLkZGUgLTUFV976BNpkpGPanVcGRHFft2k7xl9xrxDc118xHkajUZyelpqMnKxMvzqwibvqLuEDmQATYAJMgAkwASbABJgAE2ACugmwQNeNbv+f+OCMt/HhnIUBFZG76Vu27RR50ClCO5UzTzoKD9xyKSwWM75Z+JuIzK4slILtsSlXsUDf/93LNWACTIAJMAEmwASYABNgAkyglRFggd4KOnzX7nKRDz09LaUVtJabyASYABNgAkyACTABJsAEmAATiE8CLNDjs9+41kyACTABJsAEmAATYAJMgAkwASaQYARYoCdYh3JzmAATYAJMgAkwASbABJgAE2ACTCA+CbBAj89+41ozASbABJgAE2ACTIAJMAEmwASYQIIRYIGeYB3KzWECTIAJMAEmwASYABNgAkyACTCB+CTAAj0++41rzQSYABNgAkyACTABJsAEmAATYAIJRoAFeoJ1KDeHCTABJsAEmAATYAJMgAkwASbABOKTAAv0+Ow3rjUTYAJMgAkwASbABJgAE2ACTIAJJBgBFugJ1qHcHCbABJgAE2ACTIAJMAEmwASYABOITwIs0OOz37jWTIAJMAEmwASYABNgAkyACTABJpBgBFigJ1iHcnOYABNgAkyACTABJsAEmAATYAJMID4JsECPz37jWjMBJsAEmAATYAJMgAkwASbABJhAghFggZ5gHcrNYQJMgAkwASbABJgAE2ACTIAJMIH4JMACPT77jWvNBJgAE2ACTIAJMAEmwASYABNgAglGgAV6gnUoN4cJMAEmwASYABNgAkyACTABJsAE4pMAC/T47DeuNRNgAkyACTABJsAEmAATYAJMgAkkGAEW6AnWodwcJsAEmAATYAJMgAkwASbABJgAE4hPAizQ47PfuNZMgAkwASbABJgAE2ACTIAJMAEmkGAEWKAnWIdyc5gAE2ACTIAJMAEmwASYABNgAkwgPgmwQI/PfuNaMwEmwASYABNgAkyACTABJsAEmECCEWCBnmAdys1hAkyACTABJsAEmAATYAJMgAkwgfgkwAI9PvuNa80EmAATYAJMgAkwASbABJgAE2ACCUaABXqCdSg3hwkwASbABJgAE2ACTIAJMAEmwATikwAL9PjsN641E2ACTIAJMAEmwASYABNgAkyACSQYARboCdah3BwmwASYABNoHQTsdgdMJhOMRkPraDC3kgkwASbABJhAKyDAAr0VdDI3kQkwASbABBKDwOp/NuF/H3yDFX+uQ1lFtWjU4AG9cMPl4zH8kAO9jdy+oxS//7EWRx02GAX52YnReG4FE2ACTIAJMIFWQIAFeivoZG4iE2ACTIAJJAaB59/4DP99ew6OPXIo+vTsjH/WF2Lxb3+Jxn348v0Y2K+H+N/zFi3DzQ+8iNefvh1HDBuQGI3nVjABJsAEmAATaAUEWKC3gk7mJjIBJsAEmEBiEFi6fA3aFeSiZ9cO3ga999kCTHt2Fi4+ewzuuO58FuiJ0dXcCibABJgAE2ilBFigt9KO52YzASbABJhAYhDYWrQLp1x4B44bOQzPPXQDVvy5Hvc+/jro7317dkZebpZoKJnBkzk8FybABJgAE2ACTCB2CbBAj92+4ZoxASbABJgAE4hIYO78JbjzkVdw+Xmn4JZrzhFm74+/+IHwQR95+CB0aJcvrnH+mccJwc6FCTABJsAEmAATiF0CLNBjt2+4ZkyACTABJsAEwhKw1TfgwknT8O+GrZjzv2no3aOTOJ590HngMAEmwASYABOITwIs0OOz37jWTIAJMAEm0MoJ2B1O3PHwS5i36Hfces25uOy8k71EWKC38sHBzWcCTIAJMIG4JcACPW67jivOBJgAE2ACrZVAfYMddz3yihDnEyeMxU0Tz/JDwQK9tY4MbjcTYAJMgAnEOwEW6PHeg1x/JsAEmAATaFUEqmrqcNO9M/Hbqn9x27Xn4dJzTgpovxTorz15m19+9FYFihvLBJgAE2ACTCAOCbBAj8NO4yozASbABJhA6ySwrbgU19zxlIjQ/uR9/4eTjz08KIgfl67GtXfNCHtM6yTIrWYCTIAJMAEmENsEWKDHdv9w7ZgAE2ACTIAJeAmce/VU/L1ui9gVP2RwvwAy4085GgX52di+oxQnXXA7enXriGsuPgO1ViuGDeor/j8XJsAEmAATYAJMIHYJsECP3b7hmjEBJsAEmAAT8CMw7vJ7sH5zUUgqs195AAP6dhe/fzhnId6aPU/stlN55YlbceShA5koE2ACTIAJMAEmEMMEWKDHcOdw1ZgAE2ACTIAJNJXAztIypKUmIyszvamX4vOZABNgAkyACTCBFibAAr2FAfPlmQATYAJMgAkwASbABJgAE2ACTIAJqCHAAl0NJT6GCTABJsAEmAATYAJMgAkwASbABJhACxNggd7CgPnyTIAJMAEmwASYABNgAkyACTABJsAE1BBgga6GEh/DBJgAE2ACTIAJMAEmwASYABNgAkyghQkkvECvqbWivLIaudltkJ6WEoDT7XbD6XLBbDIFRb2nrBLpaalITUkK+P2tt97CJZdc0sJdxJdnAkyACTABJsAEmAATYAJMgAkwgdZAIGEFep3VhgnXPeyXjuaCccfhzkkTYDIZvX07d/4SzHh1NhbOnuHX39uKd+GaO572pqeh3LL33XwJLOZGIW8wGEACnwsTYAJMgAkwASbABJgAE2ACTIAJMIGmEkhYgU47529++C3OOOlIdGyXjyXL/xaCe9bMKRg2qC9IgE+89UkU7dyNdm1zAgT6Vbc9iYz0VEy7cyJKSvfinKun4r7JF+O0E0d4mbNAb+rw4/OZABNgAkyACTABJsAEmAATYAJMQBJIWIGu7OJNhcU4/dK7Med/09C7Ryc4nE6Q+frCn1fhtfe+9BPoldW1GHHadXjn+bsxdGAfcalpz85CSWkZZk67kQU6Pz9MgAkwASbABJgAE2ACTIAJMAEm0OwEEl6g0w75R1/8gO8Xr8Apxx6BSZeP84P4zcLf8MR/P/AT6FLML/rkGbTNyxbHz/p4PubM+wUfvzqVBXqzD0O+IBNgAkyACTABJsAEmAATYAJMgAkkvED/d8NWvDxrLlb8uQ6jhh+E+8mP3GL29nwwgb7q7w24cNI0LJn7ArIy08WxH81dhJfenuMn5MnEvby8nEcRE2ACTIAJMAEmwASYABNgAkyACQDIzvZscHLRRyDhBbrEQmbrx59zC+6dfBFOP/HIsAJd7qD/+OmzyM/NEseG2kF3uVz6yPNZTIAJMAEmwASYABNgAkyACTCBBCNAm5hc9BNoNQKdEJ1y4R0Yd/JITJwwNqxAD+aD/tCMt1G6p5x90PWPNT6TCTABJsAEmEDCE9iw2Y3nX/1/9q4DPKpqW//T0xuBkNC7AtJBpCMgIIiCKIpYUEAFQVGv5fLu0/uuXuV6FRWQJoKgWFCQIiKCgAhSBKRLCyFAQgKkt6nnfWsPOzk5zCTTzmRmPPv78lFyyl5r77P3/lf5lwXNmqgwbVJFxF7IC64IqGjABQ3k5AI5eQLq1VUhPNyFG5RLFA38BTUQsgCdwtRPnE7HwN6dERcTie+37Mb/zFyMZR/+HZ3btWTl0SwWKzZu3cvKrP244h2o1KryeugTXnwHMVGRePOVCQqL+1/ww1BEVjSgaEDRgKIBRQOeaEAB6J5oTbnnr6KBHzZbsXGLDU+M06JdG8XL+lcZd0VO9zQQsgD9yIlUTH51FnLyCss18vKUB/HIfYPZv8+cu4S7x8+opC0qofb23yex/zuXnsnKshHJHLV7hvTC6y88Vil/XSmz5t5kU65WNKBoQNGAogFFA6GuAQWgh/oIK/J5owEO0IcMUGPoQI03j1LuVTQQshoIWYBOI0Ze8ryCIlBN9Lp1akGndX8hyLqSy+qhR0aE3TAJFIAest+FIpiiAUUDigYUDSga8EgDCkD3SG2y3VRaCly6LCAhToWEeNleI/uDaV6dPSew1IkWTYPX86wAdNmnivKCENBASAN0ucdHAehya1h5vqIBRQOKBhQN/JU08Oyr5qDP3Q4VgM6BbbgBqJcSvIDw8DEBiz+zINg9tqECbENFjr/SuqzI6n8NKADdC50rAN0L5Sm3KhpQNKBoQNGAogGJBgigU/vgLV3Q6iZUAHqoyBEqgFCRI2iXBKXjigbc1oAC0N1WWcUNCkD3QnnKrYoGFA0oGlA04DMNnDknsGc1bxK8nk7qvwLQfTYlvH6QAtC9VqFPH6AAdJ+q0+uHfbjQwlIOnpmoDeqUA68VoTxAFg0oAN0LtSoA3QvlKbcqGlA0oGhA0YDPNBDswJYbGGYvtDCdTJ2kRbCGVocKsA12OXj/pR9ZsIa6KwDdZ8ulTx6kAHSfqFF5iBMNKADdi6mhAHQvlKfcqmhA0YCiAUUDPtNAsAN03n+xQoK1jrgCbH02rb16UKgAdM4FsGe/DXv329Ctsxq3dlYHHekdJ7k7nWrDmVQBzZsS2Z0a3Tqpg5K8TwHoXn2eys3VaEAB6F5MEQWge6E85VZFA4oGFA0oGvCZBkIFoMfFAmYzUFyCoCSLCwVDQ6gAWy4HzanO7dVITbPhXDqCjiwuVMaDRwBIF71gDRFXALrPti/lQQ40oAB0L6aFAtC9UJ5yq6IBRQOKBgJAA6vWWVkJpqkTtQHQG8+7ECoAfUAfNXLzgQOHbApA93w6eHVnsANb7nG+mClg9ToroqKAls3UyLxsQ2YWyj3Q9eqqEB7ular8cjMfD40GiI4CSssAozH4DA0coJPBJD4WyMiyyxFsAD0nF8jJE8D2jkwBo4ZrWJWDYJlPfpm0yku81oAC0L1QoQLQvVCecquiAUUDigYCQAOv/NPMDrzByhrODQwUMkqNwkbpoDjqLk0AaNf1LnADQ6gA9GCVw1EEgHQUAz2H25nHOVg9t8FuMOF65wC9SUOgaWM19h+yIS8/+AB6qEUCuL5KK1f6UwMKQPdC238VgH4pQ0CpEYp10Iu5otyqaEDRQGBqIFg9zxyYX8wQUFZWWbdhYUD96x6dQAfqPEy0qtkRDLnosxfZye24oSQuDrBYgKIigI8H5dpS7nAgt1AC6NzjLNV3YRFgtQYPMFQAemB9MRygGwxAeBgQbPMpsLSp9MaZBhSA7sXc+KsA9FDJswmVUFYvpqxyq6IBRQMSDQQrQA8VYBsqcoQCsKVPQxzJIF0sgiWHWwpopXIEm+dWAeiBtW2FSiRAYGlV6Y1UAwpA92JOKADdC+XVwK3BHspaAypTXqloIGQ1QIzIuXkAHbaoDR1oDwmn8N1gaBzY1k1SISK8cu3zklIBl7OEoMjh5nJ0aq9meaniFky56BzYUuiutOXk2ZCXFxw5wwpAD5yvn3Kd9x6wgf6k9YoiMVLqqpCbJ7C1q0VTFUtpCRYW9GAHtjz3nLPpJycByXXVOHXWxiJlRt6lQf1kFZo3qbweB86MUnoSTBpQALoXo6UAdC+UVwO3BqunrAZUpbxS0UDIa8CZ5zZYctFDxfMcKnIoHvTAWTJCxYMearn0wQ7QneWeS2d+sOwhgfPFKj1xpAEFoHsxLxSA7oXyauBWBaDXgNKVV96gAe65DRavh7MhDHZuCg4Mk5NUCDMIrPwStWA5XPH+R0YAOl3lUQqmMmWhBtCJoVragol1W/GgB86mxQE65TqnJN3Yr2BjQQ8VgM5zz6UjQoR3wbSHBM5MV3rylwToRcWlyM0vREJcDCIjwm7QQWFRCSxWK+Jjo92eIaEO0PkBXFpKIljDdxSA7vYUl/WGjVtsiI9DwJMm+VoJocLpEKxy8HXt23VWZGQKaNlcjahIe1kvalMnaRFuACubE8gtVELDpXKUlmTBEJYAtVoXVOXWQgHY0nwPhUiAUPOg8zru0vUoWHLpac1d9b0VObkCC9enUH36obBwIlKsl2wvdzdqmL1cWaA2qYFB2s8tv9j3kGAx8gaqnpV+2TUQsh70ktIyPDTlDZxKvVg+1mNHDsArzzwEjUYN+v3LbyzAzzsPst+3a90Ms9+YhsQEu/l75twvsGzlj5XmSce2LfDZnBnl/xfqAD3YQ0ClH7kC0ANr2aPxSIgHXntJ4v4LrG76vDfBCmylighWOULFYysFtley9kJviENsXMugArZiOQTzYZxPXcumWoeuryCvQBs09dDFAL2kOANZmb8hKroBaid1Q7CQqykA3efLvVcPDDVDQ3XKCPR66ApAr24Eld/7UgMhC9DJc770q424e0hPpCQlYtfvR/HUy+9h+ey/o9MtLfHxiu+xct02LJ89A+Fhejz9yiw0aZiMf730ONPv23NW4EJGNl6a/GC5vg0GHerWTvjLAXQeQhns4TsKQPfl0uH9s/6q4xGswDbUAHqohIYTuZrVuB8X0n5gQ3RT24koMycFDbAVA/RL5z6E2VTA5GjSfBQETeugkUMM0E+d+BTFhReYHLd0mo70i+EshSLQ64eLATrVcc++vAdZGbsQl9ASDRoPCxpDQ6gB22D3oPPxoDW3VYsbSRRPnrahuCTwy94pAN37c5/yBNc1ELIAXaqCs2mXMOKxGViz5E00b1IPoye+hsH9umLiQ8PZpT9u24vnX/8IR7cuAXnGCaDnFRTh7b9PcqrNUPWgS4kwVDBCgOEGPQRLGA8vr8br0xLrKbWpE7WufynKlT7XgALQtYyFN1hbsBoaQi00vGM7Ky6mfgirpZRNpaSUngiP7h80wJaPR/OGvyMne1P55xAXfzPi6twbNHLw9axbhzSc+XNFuRwNGg9FQUnHoAPo/XoYcezQHFitxqAzNCgAPbB2lVAZDwWgB9a8CvXehDxAv5h5BV+v3YrNO/bjztu745nHR7Ix7Tr0Kbzx8hMMpFM7fioN9016HbvWzUVsdCQD6Ju270P3Tq1ZfvrtvTqhc7uWlebDXwWgh2u3QY0iFFvsxgzeAh2g06Zw9pzAypNQ3pO0DR2oDsoc6NmLLEFtYKDcc2ri8lZ/pVz0YAW20u8nWOUINYDerP4u5F7dVj48BkM8khtPCRpgS+ORmmZCrYg5sFlLUb/RYFw8/yNUKi0atngRBw+rg6JcHAfoKfGLUFZ6pXw8IqMaQBvxcNAB9FaNN+PK5b1BaWgIFUCoyBFYEIwD9Lg4ICHuxkgASmWhFuhn48DSqtIbZxoIeYB+4vR5LFi+DvsPn0Tf2zrgtecfhVarQdv+4/HRW9PR97b2TDfcw775q3eRnFQL6zbtQtrFyzDodTh68hy27DiA916fjMH9upXrkgB6cXFxyM2uzdvV2PKLvSYwtVj9AmjU12C2NUSxeTRsgp1s761/mANadqkcjjrbuKENTz5qr4Mc6G3/ITXy8lUguagN7GvfDAb0CY7+r9uoRma2CufO37ixhYUJSE4SkFxHwF1D7HKFalvwqQZp6WpMfMSKpo0CQ9bMyyqs26R2S/+BKIcrc4b3u6prg2FdIDnOXzAi3jAHgBlRCeNQkvcdbLYi6KInIetKIoJFjqyMXxGu/RUWWzIKTOMRo/8YWnU2isz3wmRtFRRyvPovHQyaE4jUrYZaE4kGzaYg/fT7EAQT9DHP4XJ2BFur+brtylytiWtIDrUqD3GGj9jrdWGtYS47Dq2+AUzqh1k990CXI/W8GouWaVBdaHggrcGOxlqRoya+AOfvdOVMGQxnY39pNSIiwl+vCsn3hDxA56OWX1iMgfe/gH9Mfxgj7ujJPOhvvjIBd/Ttwi6RetClo/3KvxciL78Q82e+UP4rAuilpfawwlBqP22rAIEadQ5i9fPLxbMJsSg0jYVViMfM1wIbGIrlIAG06suw2OpWGqomjQQ89VhggKTq5tD8pWqcO39jSHSgjwOXS9p/nTqdGX3ELZjGg/c79byKsX4n1xWqG0L2e66HSY/a0Kyxa/e49GAvLjqbpsLCT9VwRf9rf1AxQ0tGJlBmVDHDCjHwPvlo8H1HKlgQqVsHGyJQaukL4brx0RU9eKFun9xK8ygrYzPCNPtgtjVCoekhROh+Yv8utfRCqaWPS+Ppk8548ZB5nxiRnz0XKpUZBaaHYbE1QLh2J8K122G0tkax+Z6gkOPlf2oQq58LjTofJZY7UWbpwOaWQXMEJZZBKLN0ZeB8UL/A+OadDRnJEaVbBb3mTxitt6DEPBjxYf9ll+cbp8IqRAe8HHw94zIaNMdg0PwBs60B+y54C6Q12NF4cDmqMzQocnixALlxKz9T0njEOyinyEt1BsuZzA3RPbo0jKj6leaxBv4yAJ00dOe4lzFyaG+Wd0456EP6d8OEscOY8qQ56FKNvr/oG+w/fIqRzPH2VwhxD9fuRrj2Z1hsjQCUQavOgoAwFJlG479vNnM48SicfO+Bmi+hJc2lj9avgIqF6o+G1WYn+2vWRIVpkwI7F53n0F/MEFBWBuj1gFoN9ndqlFNfr64Ko+6qiHrweEWQ8UYpe3a84T+wCnEotdwJs61+teNBqQo0rwKpFAvZ5175P7Nb8ygQQ8N5KKUr30OwV3cQh7gX5W7A1ewDbO41aHwnNIZOQRMa/svOfGzd/D7re4FpAhJr14FgvQhz0TJAlYhuPZ5CQrwq4MsYbty4EXv27IHZ1gSFpgdB5GRXrubiYupcMqvitdcqKqfIuDx5/ej/vr8PxfkbYLXFIs80hR3greaz0Nm+gg31UCv5MTYW9BPIbeXqdBw/vAQCtCgTnkbdpGjkXfkGKuEUImIGoO0tt6F5U3VA82fw9Yz0rFYVsChAMgCRTHnG6RAEe+WQQGcNV0LcA+tLkeagnziyAILNggaNhyA6thmUMmuBNV7B3puQBegHj57GidPpGNi7M+JiIvH9lt34n5mLsezDv7Nc8kWfr8c367czFveIcANjeBezuM9auBIj7uiBhvXr4uTZdIx/biYD808+fFfIA3TaFM6k2ljutrVkCbTqTBSb74LJehPzCJBlnRk87rwTXbvac/jFzZ3DvpwfUHm+UCwQF1OCgiv2wywd+rThdyA7t4NbwErOvlb17FArC0Wy6tRnEa3/qlxsmlulloFo3DjWqcGEj+fIYRr06xUYh1xP5nqoAPSWTdWIiq6oHx4seXdc/61bpOHyhQoyr4jIFNSp/3jQAPTVq1fj8OHD0Ie3xuXce0Bs7tRS/5wFtaoYU6ZMQWJiYk0tWy69t6CgALNmzWLXFpgmwmKrzQB6bj5w5sR8aNVX8cADD6BVq1YuPa+mLrJYLHj//fdZyhsPy+dynPvzP1CpTJg+fTpiYmJqqosuv3fhwoXIzMxEqaUHDJH90Lm9GqdOHkdx/ipERCbhby8+5fKzaupCMbCN1i1DcVFFud2GTYYhLaM9qCqNAtD9M0J8PLQasD1D2ooKAYs18MdDDNDDtNuRlbGTiRId0wTNb3pIAej+mU5/mbeELEA/ciIVk1+dhZy8wvLBfHnKg3jkvsHs38UlZXjx/+bhl92H2L/btmqC2W8+izqJcezfY578J8s95+2eIb3wj+mPIMygL/+/UPWgcwHPnC3E55+9x/6ZW/YC7hkWgXopKmRn7sLmzZvZ/3fo0AF33313pQ/GE9Aixxcn9qCHaQ8gQrux0mtMtpZIShmBaU9GyvF6nz1TCtDDtXtZqL44PNwVz6fPOuThg8RyRGg3IEz7B2xCHMt35C08ug+mT+0Fne7G2uh8PAOlVBGR3V3LFbB3v43Vc7+1s6ZK0sEK0kIrIy3s1lmNWvEqdOukZvfXROPRLlI5aD5JGealpIspdVWgCLbUNHvILpEuUqPxCeRG85JiO7AAACAASURBVPBcWilqRS6E1VKExDqdkXP1MGw2M+o1eRpHTsQHvOEuOzsb8+bNY2qOrfMMzqXHlAP0P4/Rt3UA/fv3R58+FeG8gTgm3333HQ4dOoQ2bdpgx377PsKB7Ylj21heert27TBypJ3cNVDbzp072Z6YnJyMY+fGV5Lj5HEKcz+EQYMGoUePHoEqAuvX8ePHsXLlSugN4cjKfwaxsToG0FPPmZB35T1QSsi0adMQH19DC5aL2uNnkPiovVBZNkNviEPdlN5IP7cOEZHJKLKMD1qATuuUWm3fH/cfsgWVHOLhU6ksEITK0YuBbjDhZ5BGKdkozPm40mxs22Eqftlttz4Ei7Haxc9JuayGNBCyAJ30KQgCK5VGNdHr1qkFHZnvJI1y081mCxITbkwoKSwqQW5+IWrXime10qUt1AH6vn37sGHDBmj1jZFdMLaStfn06dP45ptvYDKZ0KhRIzz44IOw2Qy4dFnApQwBq9ZbUS/ZHnadEKeqEQAiBugx+uXQqi+wSAAKdQvXbmaHDZU6Go+Pvx/169tDrMWNb/I1DQg5sLXXbbZAbSSjiQVQ14MFPVBQ0iLgAQXp1c6mL2DjFiviDOTlK4NZ8whKSsMQY9gIlZDO1B8dHc0Os7fccgv7Nw9tp3sJUBKYpdDdmg5156zN4jlTlaFEmnLB76vJQ4k4FFQsh6M576z/0u8m0A8n9D1lXfwaes1pWIU6yDdOQKT2exi0h1Bq6Y5Sy+0B/z2tWLECtAZTBNOZi3ewahXcg37kaBqidZ8hKSkJTz0VuN7Oa9euYc4cIrgDnn32Wbw2024o5QD90OEsxBoWQa/X49VXX62hI1L1rzUajSwKgP58/PHH8d+P7DwnXI4jR88iWvcFUlJSMHHixOofWINXfPDBB8jLy0PXW4fhx+3ty0nWiJ36SuZa6DVH0a9fP/Tt27cGe1n9q2ldm/cxzZ8F7OKb2k6AISwRRw7Ogs1qhE0/AXkFdYLGg06G0JS6agiCEUXXliIyYSzU6mhkXLaxVLea3EOqHw1AWgedqjVkpn+KlMYTWLWGYKuDnhi5ADbrNSTUugVQqZiBl8pbnki1fxeBvge6MmbKNTWvgZAG6HKrN9QB+vLly5GamoqI2CG4mNXphk3g6tWr+Pzzz9mGnpCQgJ59xuLTL28M4atpgPvB/FzkZX3IpkNu2YsQoGehk02SvwUdEqk5OnQEiseWA/S6SSroVMdRnLe60tS2ComIie+BF5/t6HDKc4BLntpAyH98YUYqyGBiE8JZPiA1ArZ33p6GTZs24coVe4mi2rXrwaS6E/mFtR2WySMDEJGT1RRQ5wC9SUM7WVpmllAlsBOnXFB+akYWYDTW3OGKIgDI4EHzQ6sFoqOA0jI7twF5z4nbQOxJl/ZfOtk4QU6gH07++8FBFOetZd3PNz4Jq1CLGe/sczICecbnAhqgX7hwAZ988gm0Wi2ee+45LP7cUAmgHzhkQ62IWRBspZg6dSpbmwOxffnllzh58iQ6d+6M4cOHg39PHNiSHImR82Cz5mLs2LFo0aJFIIqBLVu24Ndff0Xz5s3x0EMPOZSjVvh7EISygA5z3717N3788Uc2X4YMewZzFlkqAfRLl84iSvcV856TFz2QGwHC5cvmQ6PKRt2UXkiu349190LaRlzN/h2CujNySwYHDbDluo7RfwatOh1lli4osdxRPgTBAtA52d3pE8tQVJjOSirWTuoaNJEAtAdu3/4zwrW7oNVFok27ySgtvYJTx5dCq41AdtFzCkAP5IUhyPqmAHQvBiyUAXpZWRlmzpzJtBNb51mcS490uJnRdV9//TXOnTsHtdqAfON9sAqNGJGZxWL/cXTY90Ltbt/6zqydKCnYzMr1UH4gb+/+S8UAIUUKUKNIgNGjRyO/IBKrvqcw5Moe25oCuOLQ8Cgdef7OoMQyEAIMCFP/BmLap0b5jRRCSQdeOrwHqud5waKNuJyxB0ZrJxSbh7CQaDGZ1e+//46ff/65vEKC0dIepdb+DDw5ajV1OHEEKBx50Hlo+JlUgXkSqOZ7fJwKGZftpH9kNKGoAH+HhjuKAJDqV2xckxLkSK8NBoKc/Px8zJ07F2azGRExg3AxuyvzPJPB5Pfdc6BR5zGQRWArUNuiRYuQkZHBwtcpjF1Mekd9JmBbN+EHmEoOYsCAAejVq1fAiXLx4kUsXryYrVME9ihqhs9HqjFsMQNFxUBK4naUFe10mEoVCEIVFRWBvM6Ug/7000+jTp06DgF6csIPMAbweNA+Tjn0FAVA0XAqbYsbADoZ4OpEzYLFUoonn3wSdetWrogSCOPB+/Dt6q04evgXQJWEjl0rohaoPj0j9kIY8sqmYcrEsKAguzMYgLjwNTCXHbsuohYxiVOReSW8Ro28ro65mBOgbvzPyL68m91KqQdt2j8TNAD92zWXcPQPe2h705ZjEBtnNxoePzwPxrJrKDLfB5O1heJBd3ViKNdVqQEFoHsxQUIZoBP5EJEQ1atXDwlJ43ExU8C9wzUsB91Re/Uf61jeI7Viy3AYLe1uuKymPOmc9KbIPIoR3Uk9fBQqumrVKtAhhcpCdO8xCqs2NA6Y/vPQ8L37S4AyOydAnvHZcsDavWMqrmTtQm7OBfY7qj156623oqCkM37afmNqRk0BWq5QOggSUCLGZmJuduRxpdSJ79Zux4lju9htFPUAbS8I2u7l41LTxDKuAvRADQ0XRwBIJzuRdBGJUqgBdPI8kwe6QYMGKLI8Uu55JoC+a9cOVtqL8qHJUBeI7c8//8RXX32F8PBw5o0lrgZHJJINU86hKOcLlhM9adKkgBNlyZIlSE9PR8+ePTFw4EDWP0cGo8b1s1Fw9eOADXP//vvvQQZFSscZNWpUJTnEkQANU9JQlLMiYNMOyFD922+/sVSvJ554ojwkmXs8KcSdAPpNjX9A9uWDlcYt0CYXEdzRnk/Npn8SnTvUrtTFk8cWo6Q4E0XmuzHp8fZBAdDjI7dBZbXvhbwl1+uLS1d7BlUOenz0SajM31aSo0nze3H2QqugkGPmf+agrPQadIY2aNu+ghfjyuW9uJi+CWZbMxSaxigAPdAWhSDtjwLQvRi4UAbo5BU/ceIEOzzRIaq6Rocrg/YwIrXr2aVl1s6sfqq41QRAz83NxYcffsg8NVeKnmdlVhwBQvKELFu+Eley7XnQZZZOKLEMcSh2TQFcXsaHlyPinePykFdtx44doEM8NUHQw2jtiDJrd9iEG4nwakKOrKwszJ8/HwaDAZn5L7B+OguJtucR5iJcu5XlPrIDlxCHQvODsNoqSIr8LQeFhlP7YbP9z6aNVcwTTh5xO1mcOihCwzkgatr4RlK3nDwb8vIqADoZrzZsysOuPflISixEbEwRTMZ8WC1laNLCDmYD3YO+a9cu/PTTT2zukbfz068iKwH0rTsKEGewp8K8/PLLzFgXaI1ytiktZ8iQIcwIR80RQKdIjuJr7zCOEAqDj411ULS3hoQ7c+YMS42icSAjA/1Jjb53ahRaTY2+64gwYOXXHzCD3rhx49CsmePSnjUhCqV2kfecGuXQx5HrX2RoEAN0Go/S3HeZETjQ0g7EcpAxh4w60vJeHKD37HIBJ44uZ9FaNHaB1iiS4aOPPgLt+yWW2wFtd5a7LW6m0oMoyf8eZlsjPProowEP0Bd9cpBVz6HWrOUDUGt0OH1iOTSaMFi005Cbrw2KUP35izMRo18KwAxi0lepNDifuhZUPaPI8ljAA3SK6qPzFUXyxdd5Gk2bhJdPK+I1OLT/HfZvcp7M+rd9LVCaogFvNKAAdC+0F8oA/c0332Rhe64e7vhhX6u+iCjdSqhVpTBaO6NYBNJrAqD/8ssv2Lp1K/NwbN9nL5FXFSBcvIS8aFvZdRZbYqWa6Xyq+BsQ8vcuXboU58+fZ0R3RustTuU4cOgavvlmByO94q3M0gFl1n6VwsRrQo7t27dj27ZtjJl5297h1Y4HHdbp/J5UKxOlBRthtWRCF9YOETHDayyHO1RCw6Vy6DVnoVblQ4UCqFWFrH5wdEQhrNZCFhLurBWbR8BobVv+60DMQeeGIeokMYLT/HMEbKN1n0OnOY9hw4ahS5cuXuwOvr/1wIEDWLduHQsHf/755294AR9Prv81a9bgjz/+CDj2cGKfJxZ6Z+H3Ujl4jnenTp1w110VZU59r2H3nvjtt9/i6NGjzFBCBhPeeP8pdaKoCDh11sYMds3q/ci87YHGrk9kr8eOHavElu8MoNMefuyPWaycHBHiUSRKIDXKoadc+viEZJzNsLPpSxuRxBJJKZHE3j1yGjq0C1xG+h07U/Hz5uVMhAaNhyGxjp1nhkcBCLohyC28kR8okMaE+nL8ZBm++mIe21Pia7VF42b3sC4SaZ/FXAyb7hHkFdavcUPDmXMCwg24IVJUvH8UmcfAZL3RUBilWwO95hhKLX3w9r/6B9oQKP0JQg34DKBnX82DXqdFXGxUlWrYsuMAbuvShtUeD/YWqgCdiHuIwIfy6cjT5EoTe+MEWz7ys2ez22LrPIfcgohK3jhXnuera8h7TtZ0yqubu6QJe2xVAJ0AoVadgSjdagZWyONebB4Nk7VpeZdqAtiK6wVzorvq5FCrShCm2QOD9iBUKLvBYFITcixYsACXL1/G/fffj/nL7Hm+1clRXjtVKIHaaK9lbzM8h6KiiBqpnRoqoeFiOSymcyjO+6Laz84mxMAqREMFKo9jhFZ9GRZbHRSYJpTfW5MAnZeNk5aJo7xzIrWketpUV5uaI4Bu0BxDpG4NS+2ZMKFCpmoVI/MF4jrb3MAgfaUU2FLqDrG9B5IsBGgJ2EZGRjLjL0U2VScHD1mmsP6XXnpJZk07fjxPU+FrJi9zRykG5EmmvvHmrLrDiDsu4tNPP2UkbORFD4TGdSvmAqB+VQXQ1dbNDAR369YNQ4cOrREx+LcrXmvE5IkPPTQZJ87EsDKY+w7YEGYAkpNUyM2jyj5A3fjvYSo9jC5demPYsNtrRIbqXkpzbOHCj2G1miFouqNTZ3sqCLX83D+RevobQBWFnNJpNQ5sZy+yoF5de9UeR23BwmW4nHkOUNVFx64V62pW5i5kXPgZgroVckvurVE5SkuBV/7P7JAglO8ftRLb4vTFEQ5l1KnTEa3/jEUr/vP1F6sbXuX3igaq1YDPAPqBI6fx+n+XYPF7L6F2LcfhHZ+v2ox/f/gZdq2bi9jowK49Xa3mQNUVVKyUW6g17nnhJESuyCc9lFRYE+1li6j524POD1G8TI/0ACuVS1xySgUTInXfQ685wcKp800VhoqaALa8zm7r1q3x6wG79bk6YMvlI6AeZ3gfgqBDnnEaI5ej5m85KEyV8s+pzZgxAy/8w/7tuCoHXUuhfgbNEZRaeqLUYi9p4m85xLnn0jnEQ0GDIXdbLMe5098iL/cEoqIbIiy8NopKonEtLwqd2kWjf59Y5rXdvssACu8n1noeFs89IC1uGoc9fzSscjxdWUe8vYb4GlZ8Y6201lBYO4W3EzcDASNp6Lp4XSAg/J///IdFDEyZMgWJiYnedsmj+6WVF/j3X6tWLTzzzDMOn+lofXvrrbdqNMydy8ErLfAyXlVFKDiSg/NWPPLII2jSxG5o9WfjgJCvNRSiT6H6jqp/0LV02KeUFwKGxNtS/3rJUSJepTD3yZMno3btyrnR/pCHSqCWGsHAFNkUiKiPCPuISJAiGnjjeyEv7yVOeWl382V8/PHH7DuiVJCaaNLxoO+VQBTtMeK55czQ0KtbBo4fXsrWhL/97W81IQJ7J0+XkhKEFhYWgozZFKlgsraENmI0q0cvbscOzYHJmIdi8z2Y+Hi7Gg3Vp282PAx4+zV7fXZx4xEwghAOwTARnTtUVPqxWstw5ACdTSzIN03B00/UqjE5+FyREr3y0HYyKtKeQMY4KWEqr0dfv9ZclBTnB3TViRqb7MqL3daAzwD6lWt5GPPUP6HTarH0/VeQnFSrvDM2m4BZC1fiky83YEDvTnjv9SnQahxb2tyWoAZvCFWATgfU0lL3mFr5oYTqoJPHMzoyC2rTYpYHjfCpSIgPY7m5/izzxRdWHhrpDkDn0yrWsBAa1VVwgjn6f38DQnon9zyT9++jpXZvvjNgay+dZcXpVBvLsY2LAQyq72A2nmDs77e0uxW14lW4tbPGr/XpeRmfm266CWPGjCknhXImBx1yiZxw734b9h4QUDcJSIzPRkbaIghCBPoNehH1ksEOvyIHluwrgrsAff3GYmzdkQuDPg96bT4g5AJCAaAfwYwllOtd1XjKJVC5B72REflZ77LXxCY9D5UqAtIcdPqdIxb3rMydyLiwFTFxzZF2+f4akUOsH2lpRCIiI0Iyas7Y2aXrAoWRUzi5FLTINQ6OniuWo38vcznDdlXlxhytb0T0SYSfgwcPRvfuFQSL/pKFyzFymAZRYQdApGrVlelyJAc3slDaAQEwfzcxINRr0kHpRnRgp9xz8qJLm7PD/oYNG1jVEKojTuDe300sh8X4J6vAQiCVmPQ5FwD1SWysFveRGx65oeXhhx9G06YV0WX+kkcK0NeuXYuDBw+yvlCfeKsqEuDsn/MYnwNF17Vs2dJfXa/0HkdznZj0yXBC5UZr12mIk+njysvdiW++mn0QF9K+h8VWG488+nSNANsKPhYr69rQgRpWpYSf8zipJf2uwDSOGYClhoaL5zfiStbvKLN0xuPjh9WIHBQBQOeNS5kCMzSQUY2MWLd1oSgGO+Egnb0oAsvRXsgB+oCe+3Bw/0/gZ5wamVTKS0NGAz4D6KSRrCu5GD/9bZQZTQykN6yXBKPJjBlvf4wfft6DsSMH4OVnxoYEOCd5QxGgp6WlsTA8IhWiEER3mnQzPHLwM1jMaWjYZCDGP1I90Zw773Ll2lmzZoFCw/khojqATt6Fb9db7Qv1dUNDTMRRqMxrYRVqQx0+kZUDq4rN3pV+uXsNbdREfONqJAB/vpg9vKLOczTGPTK9RjZBnkPPQ3RpPByVJZPqR8qCHq3/HDr1eXTrPhxDB3d2V51eXy8F6DabmXky6CcjMw+5uTlIrlMAgz6fpVfQgctRo1y1UktFCSx/h4ZzOcK0+xCh/emGMoTUZ0eRAORV4/xplDqhul5ZIN/0FKy2hBplsBUD29t7W0D5zuRVqwrYSdcFHipbk941sRw6bAV50Cnfl/J+nTVH6xs/IFd3r9cfheQBZCTMyRPspR7329C1kw2XUmejrKwY9957L9q2reAskL7bkRyXLl1iXtuaCnMXA8JtWz5mZe4ovJvCvB01ZwCdOERoHfR3mPuqdVa2pzEPehlQL1kFY8Ec2Kz5DuXgRl6eMsJBV/OmarZ3cON3x44dMWKE45BfX88pep6jtBSd5hyidV8wAwNFl0RFVaRZ8nGIigRaNlezqIbLWQJb1+Ii97Iyq+K0Fzn67OiZjuSg62gP4PskzZEBgyZgwadatt5Kye7o+tzL70GFEtw+cBx69/Q/gaKzdI5pk7QspYjALUU3dO4yGD/92tmhocFkysexP2ZDELS4/4Hn0fqminQRf42HMzk05gVMDnGVBuoTX5+JFzIhjuaVjRHFTnzEhG+//i/r9gsvvFBpLvpLFuU9oaMBnwJ0UktefhGefOldZGRdZZ7yuUu/w74//sT0Sfdhwlj/W77lHKpQBOg//PAD9u7dy7wt5HVxp/HNkOcMm41pMAgroNVGYMYM/4aR8QOdOAyvOoDOZZUaGg79/j5stiLc1GYsxoy21730Z3M3EoD3TQpsY/ULoFFfQ9/+D6BfH7sl2F+NIjIoMoPaK6+8UslbU10fpHLo1GcQrf8akZFxePHFZ6u73ee/F2/malUx4gx2JmdHjbxrGm0MCotiEBYeh+joKNhsJhTk7oFarUeDZs/i4BG7B87fAJ30Sjmap47Ng0aVg8i4B1BY0oTlaNpzuNXgB3Lqn7NycRHajazEotHaAcXmO/0uB/XNkcePp0OQx5ZCih3lO9O9jtYF7iH0d010qRxqVRFiDR8xQivOsO1srjlb3zjhpz8PjNK5Eq79jZFvxsbVwXPPVs1r4kyOd999F1Rt47HHHkOjRo18/l07eiCNB0UhUUQSgdU2Lc8gM/1rxMTEYfp052uPM4BO7/jvf//LQpf9WUtcCgjDtL8jQruJMc9TFICz5kwOqdFY7sGoGAcbGwfe1KoyxOiJfKwE7TuOxD0jKpd3rSoSoG8PI955x868TWHuZJDzV3MG0Ht3XsMI+6gv9L1nX4spr2rgqG/h2l0I125DnaRGePqpx/zVfbuhxwjMXmivuJCcBJjMKlzLEZCSrMKIIRb8sH4hCgtzcfPNN6Njl/uYHLxsn7SjB35fCZXtJDp0GoC776owWsstEJHCUeNykBGntAS4kGFDnbjtsJTtRHh4JKZOtYe28+ZsL6Toyj/2f4Pjx4/j9ttvR+/eveUWQXl+CGvA5wCddFVcUoapMz7AnoMnmOpmzngSwwfdFnJqDEWAzg9B48ePR8OG9pxSV5ujzTBGvwha9RXcfffd6NChg6uP8vo6zubatWtX3Hnnnex57gJ03gmDZj8idT8iOqYhnp/umBnW6w5X8QAeCUAlYRo3buyyHNLNpMstR5F6ei3L4aRcTn82Cj+kMERpCKI7fRB7FU8fn8u801WF/LrzbHeuFQN0Ah0EPiy2ZFhs9WBDPIhIrXvXeAwdFM8OWo5Cw8+e/AIF+WdRN6U3jqfaN3F/A3R65+59F/HjhsUQEIlO3abDUQ491w0djAnQr1pvRUYmWCigRguUluQjVj+HXTbqvhdwS+uqiULd0bUr11KYpd3jZy97R02vOY0o3dfs77d0nIh+vVOcpnM4WheonA4ZxvxVE517KqVyUNlKqsagD2uFjl3uR7dOarfkIPk5S3dVHl9X9OzKNY72ALXKiBj9HNCfReYHGOFmVWlCztZpvqb7k5zsRiPnfGjUOejRaxQGDbjFqUqqAugbN27Enj17/JJCIQW2iQkq6HVGlObNgUpVipY334/4Wq2YR9lRq0oOiuoioC4O/XVljnhyjTNARBVj6Fs3WVsgqf79IM+tuPGouJxcAbl5YOHXFAXH0+14OVlnFQU86WtV9/BIhosZAvO4krOfeBIpzSlcux3h2p1QqbSYOPFxVu6ORzI4I7tr2tiI/OwPIdgsmDhxIlJSUnzdZYfPc2Zg4BdH61dAp05jRMNEtpl2QVslQN9/MB1q8zKEhUXi5Zf9R7DmyHNOMmjV2YjRf8zEadX6ATxwX2WHBn0XZ1JtbM+hecXXZUobzMtNxfLlyz2KQvXL4CkvCRoNyALQSXoKbf/bv+bh4JHT+Gr+a0ipWzNkO3KORKgBdG/ZcnnOMG2Kq7+3IqWuCp3ansCuX1ehKnIjOcaIeynEpWA8BejUPyJZIyt9dV4sX8tCBD6Uj0b5ji++aN+46LBCjfK9XGliYHvkwLuMX8DfBFhffPEFTp065VX5KrEcibEHQPmcNWFs4POofy8rjhyYxTziN7d7GmFhtRwCXEcAvaQ4AyePfQK1Ro+ckqmwCYYaAeiffb4WZ88chKDphU6d+1UJ0Plcc1ieTL8SOvXpGsmtlR6y6DuN0c8Hedc4maC7gJBImt577z0msj9qojsCthTVEGuYz/qQb3wKViHBI2BL3pyVK1cyrzN5n+VsjuSI0P6MMO1uWGwNUGCy5we7Ox50D089oBBmigaQs3FyOwJ2BJIIRIXpjkAvrGM5v9G1JjHei6kTb2Shp35VBWy5HP6oJe4I2EZotyBMuwdmWz0Umh5laqyOqNNRKhI3YlG6AqUtyNnKQ4pjgfhY+5vMZcdQUrAGUEUgr+wpNGkcfgNA532SclPw/yeiPyL8qy6SwFeyOQO2RHzKa51HJYzBC1NvqvTKqnLpNbYt+O2335inmqqj+KNxOSIjACkFg824HVrsBFQGFi1D6ZLS/kv7SDncttKlrILOPffcg/bt2/tDjHJnB3n2xU1lWgCVcA1Ga2v07jPK6VlLyoXAn8FJLWuKo8EvylNeIrsGfAbQj5xIxeRXZ1XqcJnRjBJKeALlaUSX/+77z2YiJsp/4URyaTHUADoPpe7cuTOGD7fXqPakSQ8ndNClA6+/yFh4np8Y1JIcrgJ0R5t6TPhubN68mZHJkBz+ajzl4LbbbsMdd9zh0WvFh5MwzS+g2vD+rClMzNgUZkvNmzBbMeFUz1utLFTUZDLhqaeeQlJSkke68eQmPo/a3/QHLqRtQHRMYzS/aRx7lDss7tyLTnnolI/ubw865QbOnPkOK+Nj009B5w7xLgF0MsCVlAHkDaKcTiIA02nTsfnHT2uE2VkK0CkflfJSrUId5BvtJX08AYTkBUlNTfXKqOTq/HIEbKP030CvPlWeOuCpHL76/lyR5cYQ/RLEGuawEP0C03gWaeKpHHQfj/CSuwa3I2AbZ5gNtaoQheYHYba6VrLTGccGNyDL7fWUykGlQ+MMc9kYFJgeh8VWl/3dE4DOS39S6ggZsZylkLgyb6q7RprzK9iKUHBlHgTBCF3kg8i61qRKPhNnAF08p/yROsEBHeXEa69zC6psaVCZVjAVFJuHon7DTjcYGqoC6L1uLSk3JlK6Ahkb5G5cjk7t1eUGEzan8s/g7Mkv2eujEsbihan2lEApF4C0f6fO2GAqO4ko3bd+debwvYPk4C336lbkX6NIhkjklE3C4Nsj3Qbo3HhFVXfuu+8+uYdDeX6IasBnAD39UjZWrN7skpqenTAa4WF6l64N5ItCDaDzkDVv8y6lAJ2zd1P4FR1I5G7EFPz777+jR48eGDRoUPnr3AXo2361sUgACv8b0MfKDocECKk2PIVu+aPxsjzeeO7Fh5M+t5UxYEuNagqL86rkkod78OrXr48nnnjC49eQV4t+OEkfL99C1nayuvur8XmUGPkRbNY82UXlqgAAIABJREFURMaPgc5gP4hUxX4u7R95C2L0SyFAj3zjVMz6t39Dw//44w9QSUWztTE0EWMZu25VIe7S/ku9B/Pnz0dWVhYz7pGRz19NDND1mqOI0q2FAC0KjBOY15maJwD9yJEjWLVqlV/qiEuBLdWXj9F/wuTIN06GTbDPDU/koPu++uorEGFcVeXNfDFeUjk4P4HZ1gKFpoqDqqdyeMOR4o58UmAbpvkdEbpNMNsaotBkN8ZR8wTY0n08XF+6R7nTR1eulcoRqfsOBs1x5hmk8lzeykHEfcT3Uh3xnyt9reoaqRxRuq+g15yF0dIOxRa7M6EqwtGqAPq2bduwfft2tGvXDkRgKmeTetAp/S9atxQqlRmlFntJWkdyVAXQKZLuu+++w6FDh/xmeHdkaIBwDWojVcwwodTSDykNepYbGpxxAUh1XS9+NkpLC8uJfeUcC3q21LgrDm0vMo+BydqsyvLAzjzoxJVB50Vq/uY3kFtnyvP9pwGfAXT/ddm9NxUVlyI3vxAJcTGIjAi74ebCohJYrFbEx1Z4+MUXXc3JR2REuEODQigBdF6jmphQicTLmyYF6OTBITIWArdyez6o385ArbsAneev8Xw1HmHgj5A+kuPs2bP47LPPqi1LVN1Y8dxhKq2WEA98++23OHr0KAYOHIiePeVn1+fvI2MJHUh91bgHh55H4f8UMeGPRvOIDod0SLQJccgzTr7htY7Yzx31LVr/JXTqVMbm/va/KuoQ+0OOTz75hIUNF5tHwmi9udIrxf131hfp4YQDWn+Fi/J+iQ9Z5B0kL2GxeTCM1gojgSeA0J810aUH2Gj9MujUF1Fm6Y4Sy+3lQ+CJHHQzHxu5U0K4HMQ6nVynBPnZ77O+xyROglpbp5zt2FM5eHSU3GHuYkBI3v9YNq+KUWB6lHFN8FZdiUg6clDJJmnjqUtyh7mL5dCosxCrX8y6kmecAptQEdfrqaGBcukpp15uJnRxiHt0RDqKcz+DSmVAdOIzMJrCkJkleAzQOZiiCADaR8Tl5ny9DvM1MzlJBYPehKJr82GzFUJnuBnqsFFO5agOoFO5uDlz7Dwg/gCEUkODCiaWs61R5zFQS+BWbGgQcwHk5AFhBjuHSWERYLGClTSjlJGbmxzArl0/oFmzZhg3rsIQ5utxkO4dPMSdh7YL6jYosd4NKsBS1V7oDKDT87lR1NfnHrl0oTw38DQQsgCdQusfmvIGTqVeLNc6lXl75ZmHoNGoWej9y28swM87D7Lft2vdDLPfmIbEBPumlX4pC0+9/B7OX8xi/x51Zx/87/OPQkcU5ddbKAF0KuNDIdzSchKeTFmeiy4+nHBwK/dGXhWonfmBhYXkehpKXFJSAiJso4M7laCj3Co5G7eK9+/fH3369PHZq/x1OOQdfvvtt1mpMTl0Rh5OAh/Elkqsqf5odFj88+hnKMhPY0AwpV5nxmhL5Xvi4+wERGL2c94naS465d0VFdq96FRC7/nnn5f1cCjWDT/Q6XQGZBXemM/rCUCn5/N0Fn+S95WXi7vu5aRSb1TyTdw8BYS8JjoZssigJVcTA3Ru/BFgQF7ZM6A/eatKDjosUpMSZdH/kXH0rbfeYr+XM3JGLIcz7zn1wdPxoHupGgTxaBD5VL16FWDZl2MjBrbh2r0I126G2dYMhaYxlV7j6V5CD+HknxRVRNFFcjSxHNwYSPWmSyyVK7R4CtCJjZ5HZL366qtsHZOjieXgxLPkbSavM2+eetDpfjKE09mBCGWJWFauJg4NLyvcjssZO2AIS0DrdpORmw8cOGTzyINO/V2xYgVOnz7NjO5yrlX0Li5Hy6ZqREUD2Ze+RknRKej0CYipNQGnz2kdyuFoD8zLr1gPxM4cKplH3EVyNnHJ1KzM35BxYQs02nC0aTcZ5y8acC7dc4DO+Q08KVksp8zKs4NHAyEL0MlzvvSrjbh7SE+kJCVi1+9HGeBePvvv6HRLS3y84nusXLcNy2fPYN7xp1+ZhSYNk/Gvl+x1Zif97b+IigzHm69MxOXsa7j/yX/if6c/grvuqPAAhhJAJyIyAm6UL0N5M75uBG55SRM5F14K2aXQXQK0BGzFjefOUh1XTxtn4PU2T7+699NGRZEA9Oe0adOYF92XbcGCBbh8+TIjlSFyGbkaN5hQjjjlivu6UUg1hVZTOT3Kb5czD5L3neqizp07F4KgR57xWdzex1Dl4Yrf5+xw0qbpV8jMOOtXgrWffvoJu3btwq233oohQ4ZUSWrlbMwceQ/omfRsqjZAVQf80UivV3NMOHt8NmOmjoy9H4WlzSqViyN2XYoecdRen2lmTLyOQIq/a6ITwF2+bA4re5fSYACKjbdWe0h0VcecqPGuu+5iobByNM46fepMIfKy7OUHdVFPIDe/DkxmoGsnNSiSp6rxqC7SyVkKky/l4SzNp86akJP5ISMcLBMeR5kpCTahQg5XiTod9U36Dfqy/9JnvTfnHAqvLYMg6JBnegaCYC8ZVZ2BoSqyO/6OZcuW4dy5c6weOtVFl6PxtVOvOYEo3WrYhAjkG8l4VUHS5w1A52lYcu1TXCd8zWzftgQXztijS5q1HIOYuBZeA3QeXUJGEtoL5TKWUJ/FhgZYjiDt7BpGeHpz20koLotzamioDqDTs3nqGlX9oeo/cja+1vS9rQTHDs2FzWZGo6YjkJDYzqV0r6o86NTvmigNKae+lGf7VwMhC9ClajybdgkjHpuBNUveRPMm9TB64msY3K8rJj5kz1/6cdtePP/6Rzi6dQkKikrQ464p+GzODHRsa88tffOD5bicnYPZb1bUDA0VgC7Ol5kxY4ZsIId7o2gTp81cjkZeIvIWyQFqqb9idmfyeEZHO06N8FY2qoVK5ZHIQ0SeIl+3w4cPY/Xq1bIzO69fvx779++XFXwuWbIE6enpsufX8jHgRiAefjygj9orgP7Q6KusZqw/vehkLCOjGTeWuXIQl85BR4cT+vbo2WRY8id53+rvtuPwoW0QVMlo0vIJFilDEQ2uRAIQrwGxdTsDW/6sib5p8yH8tvM7CIhAfNI05OSrWQkmV+Sobo2gHFWKyvFH+Oi8BeuRfXk/K39VZHYt95z3vzqATmCQQKE/PFOfLN2BC+d/hjs59NWNA/99RkYGFi1aVKlCh6v3unvdG/9eCKs5s7yqAb/fFwD9wIEDoL3dmxKarsrzrzfeh82aj2LLMBgtFUzf1clRVQ46fzeP9PKG76U6OcrZz3WrYdCcgNnWGIWmsZVu8yQHnT+A5hPNK7nT17gcHW4x4mLqHNisRjRvNRbRsU2r3AtdAejiqAy5U9f4WpMUuxpm4wlodfURVcte6cIRn4wre6D4GuI2II4DX0SmVje3lN+HngZCHqBfzLyCr9duxeYd+3Hn7d3xzON2EpCuQ5/CGy8/wUA6teOn0nDfpNexa91cXL2Wx8D8tm/fR+1adkbM5d9swpofd+KbRf8snwUE0OkgGuyNNlgqWUUM5XKW6cjJyQER0VEjcEs1on3ZqIwX1TWtXbs2nnzySV8+utKz6DBCh13yPopJ6Hz5QpKD5Bk8eLBsIXdk3aVQUSLuk4sFnb+DDj1yEeudPHmSlZGiA/vUqVN9OQw3PIsODxSaSi3PSOXRoiEG6E0bA89McFxTmOp1b9oKNGkING2sBoW4U3jf5CfU2Lf7CxZi2atXL/Tr109WGYgsjIw/RNpInBDUzpwDPlpsQ1X9l3Zqzsc2pKbZ+9/cTmrNGhFg7du3jx1K5PaA0PtoDr///oeMjZ7KeFE5L97u6G8Ht940Sv/ZunUriywaNWqUN4+q9t533/2AkSRJAYgv5CDjCYWHUyMPm1wEkWTwpTJD1AQDec+TWP1p+hk1TI0UO5G70/b8/9jr2b/3hvNx4+sKGS/r1rUzkfu6UZWDWbPeh8lkhD7qCVy+moR77rT3v15dsJxZb9uHH34I4tKgaJMGDSrmrbfPFd/P90Uab0PMM0g9r7vhm3X2PlfWhbKyCuLR6dOny8YFQoZeIglUaxNQZH4KZUYgOck+Ds7WXC4XX3ur+o4oooFy6im6hELdMzLBUpeaNfbdaNCamZ5uT2uilm+aDKutMuu6ozWYjwPlSovJPKXy8L2QzlZ0xpKr8bU/LnwN1MIxCJrWEHR20kGLGSgqpj3uxnGpag8U7yFr164FOREoXF8aCelLmWit0WnOI1r3+fXxeAqUIiVuVc0ZZ3sgv1+8FhI3gM1mQEYWEG5AteugL+WsqWdpNK6VAq6p/gX6e0MeoJ84fR4Llq/D/sMn0fe2Dnjt+Ueh1WrQtv94fPTWdPS9zW6F5R72zV+9i8tXcjDumTcZWI+NthNPfb1uG+YvW4OfV1aUkiOAnkfujSBv5Eml8CgCg3KGO5OaaOGl0kVdunRhYMSXjYwMdBihRV3OPDIa86VLl4IWHwKeviaVoXztefPmMdXQ831tyOA6p9qpdCAh4OFpCbeqxo9C6L/88ksWZeANe7src4TYhGkzJEBIRFhyNa4z4lL47ZC99i+B7ZJSu8e2UQMrnnjI6PD1P+/QYttOPYhAi36KCu0EOePHGhEVfpnV49XpdMxgImd4InlR09LSmJeFCA+pnUvXYMkKQ5X9lwq1+HMDzl/QsP43aWgt/zUBDiKgo0ayyE3eR16KgwcPQqtvBiPuQ36BGrExNsTFCujUzoqOt3hnRCWjDHmmqFEFB19/71xxJAPJYgiLhz56EtMtzSfSbZNGQiUdezq/+VpP37scqUzUL+IbocM1eVSj4kexOd+vpwm393ZtHP737QgY9AJmPF/qVEziSyGiSzn2Ef7SvXv3sjQQijjILhjtcK57Og78vl9//ZVVHCEGcbk4ND799FPk5uayKKYDx7u7JYer6wJFFVFkA4EpOWpYkyOE1hSK+qEUjW27W7slB197q5qHpCPSFaVJ0Xe+4NNIXM5W4/9eKfF2mCvdT+v8lStXUGa5FTe1GcTKlG35xW6UcvYuPg5SgO5IHtITrcHkPGjTpg3SLthBUuMGFWu0twLR2p+RcQHRuhWwCQYUmKjSRGWLlaO9kI+D9P3SPYTSyIgXgNZaOgPJBfSoP38eXQSTMZ8Rcial3F7OJxMbI7C9o6q119keKJaP77dkeI9N6Oz2PuvtWNXk/XLzNNWkbP54d8gDdK7E/MJiDLz/Bfxj+sMYcUdP5kF/85UJuKNvF3aJIw/69lUflJPGOfOgC4Lgj3GS7R0EBim0i5qc5EFcAJ7T6es8KXHONlnxiR1XzkYe7hMnTjBvJx18fNn8FTIoDiWTY+x5jqXcpYRI95xNWO7cZ05QReHb/36/sqWd+uFKHqR0rnDCLDq4EbGMI/4EX80vMWMxjTkZBKj5KsSd95N/H3ITFomZ/OlQvf9wLZCXxhch4WKdi2uiN23WBXsP2MmcvOGzED/f7q2dxaIBxowZg3MXW8giB19bWrRoga7dH8TZc3b2a1/JIU6Xoiimg0druy0Hhc/WT1Zh1F3OvS+c20KuMHfaF2k86M8pU6Zgxao4pquqyO08+UYzMzOxcOFCxqEx7uGXGDjgjNaePE96D09r4Gzx1eXMSu93dV3gVQIoCoBH5fii//wZv/zyC4tiSU5OZoDNXTlcCXGnd/HKFlRubfEXdm6W6sLn3ZGTjHDkpFCpI5BTOpnxmIhbdWR9UoDuaJ3j3zivplFdyog7/efXpp034uuv5l6P9rkLbdq0Z4zsp88KzDM8argGjioYSMvl8ec5+q74msuJ+2hdpygcImH1VSMDGeW8W4VoFJiexu299S6lq/H3uzIPKaqBHBUJCQkYMuwZzFlkqfKc4CvZlOcEvwb+MgCdhurOcS9j5NDeLO+cctCH9O+GCWOHsVGsLgf9X7OWIftqbsjloPur/I74U+F5Ur4sP0HeFCrnJVfOtvRTFx+sfE1ORlZ88nCSN5iIUuRsnAWdvDfEhO7LRmGuVL7PH6X1COAQmzCF8cqV+8wPV/wQSocNIsUisMYPDgmMAMvxAYKXu1u1zspypEcO07ASTARGKEyTIg6IvE/OXHR+0JUS8HByr6r6L50bVR1OeKUAkoVC++Qi7+N8ALyGMeWTr/jG6nOALq6JfkvHx33+DgIfNDYcgLgKKNz9XskAwMPcu9z2EjZt1fpUV7xO+U033cQMDXLJQXJzvpGefZ7EiTOJGHX9e3JXJ46u57WxKcqAiFNdOYh7+l7O5h5d6yGcv9TIp0YA/my+l7grh6sAXWwgv+mWaci+Fo2pEysI3DzVDd1Hc5bkoDWe7yXuyuHqPCSCWVpTGjVqhIMnH/IpQKe9ifgsKAogMm4ELly2Ry+JW3UAXXq9M0Mk5xjp238MVv/QjN02dZKWhVY7Kvvn7vhwslyzrSEKTeMqpXlVZaTm73FlPLgRTq2JRVzSMziTKrDSbNR/MmJVZcBzRR4y7lJpOppXRebRMFlbui2Hq/OQk8UZoh9F5pV6PpXDFVmVa4JTAyEL0A8ePY0Tp9MxsHdnxMVE4vstu/E/Mxdj2Yd/R+d2LbHo8/X4Zv12xuIeEW5gDO9iFvcJL76DmKhI5mUPZRZ37umSu7yI+POgHFiqEUmh23R490UjCyVZKilMv3v3itIrvni2s2fw0iwULnrbbbf55FX+KlvDO3vp0iVQeDiFIRMhi68ahfAR3wDlPZKn1h9t06ZNoBB0uQhZiLmdQu/ElQ5cPcCK5a9qU+elcuTyonPCM18YTao7nNC8ovlFLPHE1+DrJq79++yzz4I8RtzQ4KjMnTfvF9dEb9fpaWzbFe8zYEvfPBmz6B18XFw5wHoqz+LFy3Dx4jkk1b8bJ860QbfOamZU8tZzK/WeU264nHJw40xK/V44eqYPM3j16+W9d4285nSgpoM7ec8TExNlBei8BKkhoiMyc4b6DKDzqCIqVUVkkNSq+2alc8qd9Y14LYjcNDz6dly60h0vTdX6FAwSR86DDz7okRzbfrVh9ffVG+5ozGfOfIfxWeQbn4JVSCgnj/SWy4LSMojPQuxEcNW7LS5fKB4jZwCde4YttmQUmMaX3+IKeK5uXeFnBrou3zQFVlus28DW1XWBADSt8xxA8775Qg5+9m3evDn2Hr2fPTouriKH3hfv4P3l37jR2g7FZjsxNTVfvqO6cVN+H3waCFmAfuREKia/Ogs5eYXlo/LylAfxyH32+p/FJWV48f/m4Zfdh9i/27ZqwrzjdRLthB3n0jMZaCeSOWr3DOmF1194DDpdhVU4FFjc33zzTXYwJC9wVFSU32YwBwq+8BKLa/zKzfopVhAva+JLQwMBTAKa3HPjjwHhJdfuvffe8pxkb9/LPbWUIzpsmD1KRe4mDnf2NcM+8SZQyB0PFeWyuHOA5fdUdUiW04vO2a/FB3ZvxoSXLeQRANJn8dJFxEEgB2ERGfnI2NetWzcMHTrUG1FcupdXJEiudxuOne3vM4DOy4ZR2DnVj6fm6gHWpY5LLvr0s31IO7vB58zkUu+53HJs33Ea235eAbUmHleLn0bzphSqr0a3TmqnJfVc0Rcv80T8DLQmUnMX2LryHn7N5m2XsXP7AggIQ27Z86h3PaLGm4gAacoERTR4Ioc769u7Hx5HUe5KWIUk5BufKFeBNyHixPdCZwVq4vKs7o4HrVXfrrcyQ1R1IdJv/WcNTKV/sBrrVGudN2/kIILc2bNns0eJI7xcBei8D66sCxRFdO2aCXt3vQuVygKj6mGYrQ1hMoF9FyS/N6HiJAfJM2DAAKz8/kbDqyug0xU5aIwvXjiESO16RvxJBKDS5mkqE98L6XmUEvmPt25kfHRFDle/c4ok5MSZjVr8DcXFBpxKdVzz3tVnKteFvgZCFqDT0FF+eF5BEagmet06taDT3pjTRrnpZrOlPNdcOuRZV3JZPfRISqiRtGAH6ESoRrVx/RUWLlYfz5PyBVgQh6U99pi9RIa/GuUPUri7r2oL8+dReCg/VMktCw/frV+/vs/I3LgcDz/8MCOL8lfjXhxf5z7z/HBptIQ7B1iuA17Wy1ktaO5Fp5QDXxJHUQoIpYL4MuKjunF97733WGlCqg7hSwJKXqKKcujpgCUXI7lYvt/2XsCmHz5hNZjzjM+V/8rTQyI9QAxAKIeeVzpw5QBbne6d/X7thnwc3MdZ1l9EQZEeViu88tw68p7T++WUg56959f/QKUyId84CVYhkYnsTZ44hVPTnCWjtbhUp7uA0JWxoXWA0mOoxJ+1ZB40qlwUmh6E2WYnueRA3ZNQcV7eiSo1EFEjb+7K4cr6RtdQfj7JI5S+AxVM1z2rdmfH0IH2qAZPPNA8BUuakuOuHK6MB3+mVm1nWReECOSKvnN6hqegjUf4SUvMygHQuRwR2i0I0+6BydoUReYHKqnAUzm44Z0q5UyePBm8/+KHu/JsV9YFLkec4X2oVSUoMD0Giy2lkhyerr1UPYFIAXlqH81hMuJQlEXK9fB5Rzn0rswj6TVcR9H6L6BTn0OxeTCM1s4+kcOT/ij3BI8GQhqgyz0MwQ7QeYig3DUznY0Dz5OisDUKX/O08VBz8tSSx9afjRs5fEFWRBsGbRyUt/vqq6/6UwyWv02htkTs5G3ZIn5YJwbWV155xa9yyJH7TGHtFN5OYJAiNMQM664cYN1VgBxedDEZJKWVyFUZQCrr7t27Wdk1MgI+9NAEXLosICFO5ZWHk96xZMkSpKeny0qox2UpLQXr98VMAT//+CE06nwUmh6A2WY3PHkTIs4NSjyHnr/TlQOsu/OKy0FA6sSRT6FTX0DtlJG4lH0zioqAkXdpGB+CJ6Hujrzn1D855KCD9Krvif9BgLFoLQyaozCjD4pMvSDYvAO2nNhSmiYjByAUE2aFa7cjXLsTRmt7FJsrRxy567kV52yPHz8eDRs2LJ8q7srhyvomloO8nQbtYZRaeqHU0qfSFHVXjuzs7PJqJtKIKHflcOVb4c+ka2MN86FR5fgktJpH2tG+Qak44rVXToCuVhUhzvAhEz3fOBFWoXa5GlwB0VKdiaMApLXi5ZSjWYPfkHtlK+ISWqNJc3uZy9Q0G86l240+Qwe6V8prx44drNJEfHw8M8Lx5spcd2UeSa/hutFrTiJK9y0sttooMFUYzeh6T+TwpC/KPcGlAQWgezFewQ7QOSP1c889x2pI+7vxhVJq5XenH1SDdebMmeyWl19+mbHh+rvxkC9vQ8Q5UZTUyu4PebjHRQoUPHk3z330xbM8eT/Pfaaw5z2HOnlNKMNDmymPmvKpxU2uTV3sRe/Q8fZKRHSe6IRKRhGIIi82ebP91cgTSYY4SkO5Y+jj+HJ1XfTtofaK4IeY7imigbzm5D3nTPRyySTO/wzX/oZw7VYYrTej2Dyy0iur8twSAzE/iPGbONEk/VsKQOQAtmI5wrS/I0K7CSZrKxSZ7WHcvLnrgRZ7z3nONn+W3HLo1KcRrV/p8NDrLiAkOSicWuo9J1nkAIRiYKtRXUGsYRFj9c41VuYCcVcOMoiRYYxyax96yE50xpu7criyvonl0GnSWOktqu1NNb7FzV05uOGd+F2kZUDdlcOVtYE/s26SChrhd5QW/AitoTmi4h8oL6PpCbAlLhbiZHHEjyMnsCWZI3XrYdAchtHaBsXmu8vV4IkcfF91tA/KKUeHW0y4cOZ9CIIFbTs+C50u2iWATnwkOXlCJYOjOA3u0UcfBVV94c2Vue7KPJJew3UzoI8aRw5QdE4JWrV5AhGRyS7J4ck7lXtCQwMKQPdiHIMZoBNLOLGF81AlL9Tg8a0EromMhw5E3XuOh6Cq73YYHNWQpRxOqlU7btw4j/vizY08RJzCUzt1exK5eZ6F83HG3UceeUTWWt6OZBWT03mbx89Z6P0Zpi+WiYiKyDMZFR2H9CuTPQ5LpGeKWa/FIa9yb+piL/rdo6ZjwVKNV3LMnz8fWVlZ7MBOB3d/tg0bfsK+fbtQu87NOJk+koXuEgOvp570efPmgbxr/iKE5Ac3jQaIjiyC2mT3StkMzwMIY+WFqgsRd3SA5SWdiNSSZBE3OYGtXY5iqE0fQIAWMDzP/nRFDkfzhnvPHRl/5JSD9yXeQGHuFuSbnobVFl/eRXcBIWempjre99xzTyVR5QCE0pJTsQZ7mLvZ1gDF5ntZKgU1d+SQlh3kKRNcGH/IwUOSSyxDUGbp5NF48BxhisIir7M0hUUOOcQedLWqDHGG91jfSyyDUGbpyv7uLrDdt28fNmzYcIO3litFTmCbnKSCXnsNhdfms9fpwztApR+EzCxdlXLw1AvicuD5+lwO4mAhI5w4ioyeLaccUZGADhuhsh6AoGkHQTccZWVgP1V5nqlayvZdtkopL5y3xBGRrNwAvWljNUoLt8BY/Bu0+iaIjL8Xufl65F0/L7obCeDPPVx5V81oQAHoXug9mAE6P4zIxRbtqlp5P3SGFsjKvw+vvaRzKfyVh2v+vHkpMjPS/VKSrCqZeEkxlWEMruU3c+tQRc+Vi03d1XGg61avXo3Dhw97VdudA1oqq0URDXKV16pOrnfemYWSkgIWoli7TisGCD0pMcOjPHjZKOl75drU6T3ci96mbS/s+L2P24dD3lfuqSUSSCKD9Hc7fKwAq7+ZxV7LWX/p756E9fFyilKyPjll4mNMwTkpddUoyl0BizEVhojOCI8ZiozLNnZYrMrzLD3ActJBZzn0cgJbOvC2bK5G5vklMJZdQlyt3ohL7ItTZ2woKnYvh7sq7zmNiZxy8DGP0q2BXnMMZmtjlFiGwyrEsF+5A2zFcjgyxMkBCLluaF7Rj8p2DirTKgBGCIhEoWk0LLZ6bsnB09bEBHfib0NOOahGd3wsYDaeQEn+avZa2tevFIyATTC4JQc3whERWa9evW74vOWQgz+TgG2YQYCp7DBKCzcCggVqbWPkFI9Ak8bRmDbJtfJx5ICgiAz60xkXi5zAtlN7NRuPK1n7kHEVrp/3AAAgAElEQVRhC2w2C7TaGOSWDEfDRk2cyiH9ZolDhNjUKQqKnCDkDJE2OeWgd1FaUax+LnstcU0Um++ExWZ36DgDttI5wo0+jlIN6Lly7eXiPH0ywMUYFkEFCzPAFVvuYetWdYYGSrHyhjRSzv1RebZ8GlAAuhe6DUaAzoHtqpXvobS0yCc5x56okIcffbMmH8Z8O0srL2tCf6eandSaN1E5fDwtph99XACy1lOjnG2pVdeTfnl6z8dLfsel9O9hsdVFgelxxijM5KimFizXw9FDP+LQoT2sRJzUm+Zpn9y9j3ttPSm5xomCVLbD2L1rDSO4Iw+6vxtZ/ymCYds2CkXezLxRhaZHWDc4gy15QVpcH5/q+sdz84l8kGrjSptcmzq9h4+HAD3yjVPZIZeau14czhJeE8a42YssoDUn7+pqGDTHYbR1QYnlDpYrTONBddfFnhqpfinfuNRYsQ74svpDdWPPfy8tcaTXnEKU7hv2a4stEcWWu2G1JbkF0DkA6d+/P8ujlzZ/ANsw7QFEaDeyV5ttLVBkGgEBBrdI1qryntNz5ZQjMgJo1UINq7UYWRe/hKksE2p1GPLL7oXZ1qhKQEjzUlxLmbyc5CV0ll4kByCUetBJXxp1HqK0X0KjzmHjUmK+AzPfcK2Ep6Oyg9J55S85tOoriNR+zYCVTYhBkfk+vPtmfZc+OV79gfYhSr9zZOSVQw7+TDJgaXX2rqqEXMC0Ciohi62/0fGD8eKzHR3KIZ3rzngZxDfLCWzFckDIhcq0Biohg71eH94O06cNdZgSKJWDqpeQQbFNmzYYPXq0Q9nllKNlUzWiooGy0nRczVwPi9n+bRitbXFbjztw15Boh32SzhFODOespLBceznXTZPrVBA2ay6K87+FzZLN+l1m6YjevQfjzjvs+7s/vlmXPkTlohrXgALQvRiCYALoHJjTgXfthkuI0X+CsPAoTJz4gkseay/UVOVmRr/kHhCjpT2KLVUT5FTOo9yLCO1mmGwt0av3/W6ThfhCJgqjYgRSGQLCBM42Og4Wm301JqAuPghK38k3w6ToD2A2F0NKvuKLPrrzjMWLF4OI1qLi74Y+vG21Bgb+bC5H05RvkJdzCiNHjgTloPu78U2ZmJ3jDB9ABapnOwFWoU55V1z13FI0AUUVJCUlsdI4jhoZWP75H7PboNlVvcz438+g15xFqeU2lFr6s9vcAeji+t28Vrir7/bFdfxwolFlI9bwMQRBizzTcyzXljdXvCBv/68Ox47Z01l8UfnBHdkc1SDWqdMRqfsORMRErdTSA0+MH+jU8CM+wPIoAAIgNCaOcujlBLYsxP16RU2VdT9Ulh+ZDDYhFkXm+/HUE8kuGbCq857TM+WUQzqGkbo1MGiOXR+PgXj7Xz0dDjPtgf+ZbSn/jsRh4cRpQNEZ0iYHIKR5dSbVhtOpNsaCHhmpYuOSm2uCTlgHIpWiRsCIQu6ri0bi4btdu3YFgRBHTQ45HBka6N20BkdqV0GvSWVdcZXElRvhqqqMIocc4hB3qe44iR/9P6UI0f4mJdoUz/Uu7XNAuefUquL4kRPYOhr/MO1ehGu2sZQQWn9oXklTnsRyNEz+k6WLUarB1KlT2T2OmpxytGymhrgCcEHubuRkbyezIjQaAwYN6g/Ki6/qm828uAPE8UMpH1Qtw1GTG6BTiLu4lRXtQFkRyQEYDHF4+OHRjEzVH2uPQwUo/xlwGlAAuhdDEkwAvTLRkZ0xlvKq+vQdXCPA1hFBDg2FxWb3UtqEMNgQhWF3xLH67FRLed2mCJhMUbh02U4EF6NfAq06k5E1RcbcXK03zouhdnqreFOnkiZU2oRqjpZZ+8Ei1GFApCpARXrYsi2VEetIWUXl6K+zZ3JP5cpVR2EqWlVey7ZeigqJCSr06aGuMkSc5PjxZxMoH5QasbfTpu7vJh6PCN0mhGl+Z170EvPQ8jJMrgJ0nrddnbGBZCdPcHX1dd3RRTmwVWchVr8YUi86PcsVObiRgUrdUZilvxuXIy4OUJs+A2zpENQ3A7q+KDUmVJtHyMfz6ceB9Ws+YJUG/M1t4Aigkx4JgERof4JBc4ipNTa2NsaMGYnk5OQb1Cw+wHIA4syTQzdTJAj9uFK32dUxdSaHVp2FKN1XzNhAuei39bgTgwc59hKeOSew11FkU3Xec7rOnwCd3sfXYPq7M0JE6UF83bp1oLKfnTt3xvDhwx2qUw5AyF/kDOAO6LEXBw9sZpcRVwxVO6E9gjexHKOHZ2PRokUMxBMgdAak5JTDGcAdc9c+EDs+tdatWzNQKDZKcTloPasdd9AlI5wcctAeWFIGrFpvRUYmUD9FhfBw4PRZ+5wfOyoLO3d8i8LCXJYTf/fdd6NVq1YsQoiM9Cx3e7+NVXUouLoc2VnnUV2pTDmALZdjziIr63en9irGL0FypCQDg/rmY/fO1bhy5RL7PRnSiVB16686cDJL+n+1yogY/UdQq0oxYsQIFl3irMkhR1UGE1qrIrQ/lhux6Psgg06DBg3AoxKZ8yRTwLBBxfhthz1Kk0oOEimxoyY3QHf0Tq06mxmxeMSMmBBRKseo4RrQecyTKhuu7hHKdYGlAQWgezEewQLQpbUqY/QLoVVfRaHpYQZeqLly2PdCVTfcKj2UEEAlBlhXG3l71Kp8dqDMMz7PPHM1IYd4E6H6r3Fhc6BCWbkYRFoUHpmMfr3rs4M7bQ4Uil+5VJCdaTUipg/69OnrU6Dnqj7FcnCSn0LTozDbKiy61Rkaft52gpURqUnCvspEP3mIM9i9GNRM1hYos/bBwP7J1RqleGkcT8L9XdV5VdeJv9lo/dfQqc+wMOQyy60wX4/OcMXzfFPDz5GdfZ6FJpInzt9NLIdecxpRupXlXTDb6sNk7YT+/dpVm0d4e489+OPAFvb9iOs6+0MeOoBTibVLmcDq9fZDr7i1bZWGnKzvUFZWzP67Z8+eoNKV1KSHTINmPyJ1PyIuLo55z/3ZnAF06oNKVYoo3Wro1PY1mEiU6FAu9dry8XxzhpERfFKTMreLZeLr/MhhGvTrVdmD5K3szoDt6OEX8OsvX7GcWYp+GTt2bLlXnKdc0ME9PAxIrluEvMt20j9n3nPxOLrLcO+KjNwYQ3tCmZF4DuzA8N7hGpiMaVi5ciUjqySD56hRo8pLkooBRbRuOSs7WB0glAPYchlJDip9x9OMKHWF0lgoR/jChQv4+uuvQVEXCQkJzNgQGZnIgC3bC9dbUS/ZAqF0LvuOqjPCySmHM2BInAZmsxnEm0MGHf6dtGo9FAs/rchLp7Wardm6KPztb9OqrDIhB7Dl4yE2jlrMYPwS4j2cmP6p3BjJpNVFIjx6GM5ntCifspHadaxkXlh4PbTv/DhLRaLxdNQ8lWPsaI3T8w4BbFp3uVFQqwEEAbDawL5dIhtt3igdp/9chzxiWgNABI+asAHYvL2img/tObT3tGjVBWMfqByhKZZFLoBO6xS1jVvsf1KVAJMJ7FuJj7Mb9vNzfsLJE3vY7+PiaqFn79E4mVqbGXykjZfDdJb+6cqao1wTHBpQALoX4xSMAF2lKkGcYT4EQYU84/Ry6f0N0MsPJZkCSq/jWY2qCGpVAdSqQtRJLITVUoAWTYpAIYj0wxdh8ZCZrK1RZK5g3PW3HNLNnDz6OvVZaFQXmXefrM/SZhUSWK66TUiBxVYH0foV7BIqS0PlaajJcRCsaqqL5QjX7kK4dhtM1uYwWrvDJsQx4iVHAF18SI7SrYVecxTF5iGY+Hg3l8Jkvfj8HN5643hcgUHzKwyaE+XXx8Y1xch7+jjMKefy3NL8m/9n7zygo6jaN/7MlnRK6B1CsaL+RbBgA/UD+QQVVBSxgaKoFJGqiAJS7KDYARuCBRUVUEBBFEVABQsfFjrSSwoJpGyZ/3nvZpbNZrMldctzz8lJsjvl3t+d3Znnvg17dv+Dyy67TD30VnYrklim0Ipu9EFiOgscp+Pcc89Gzx4natt69lE4bN8hCxQvqQf7yq5Hb/TFe3HQatqNOPN6xJn/UolypJnNCWjf/iyIa664r0srutAilpwXlUXHu65zZc5LSQJXvnM6X2RT2ZqlooM0GYd4Xny8uL5yX5amaTbUjHtRieGqWjCRfhjfvfKAKPkaUmu6cgGIINyy+TusXLlS9bdOnTpKSImg8p5HwzslUNk+41xybLH+VETzZaU/fPiwKsUn9wyxdt50002qHrj3OIwyVO3bt1cu2CW1ihSExjlLOofc+95//31Iskdp8n30v62XuCy3+3SkJG5HnP4eLJZ4jBgxzK/nUlWO4/jx42ocItY1kwX1G/XApi2nuZEnWr6HuJKbrQ3Q/rwB6n5TUq6QihyHIQyNz630Q5pncrh//vkHCxbIolwuzOZkZOZeAwfSEBcHJOhidc5ASmpPnH56W7/CNtTPQygeKd7XupzL+x4un4+58z7B4UP/qq7I89Qxe1dI2b/qce+q14zcQKEkwQw0LiO847xzzAFDLIMZh5SJXbXqezgcdmhaPI7ZOsGO9oi3bEE8PoCuJ6H1qfejTp2EEisFVZRAL+le6D0f8rmQcAL5vEvLtV+EXHvx/CTG8UJJghloPvh+eBKgQC/DvESiQDeGK9ZnsUIbrbKFrXFe42bobT3wlSlVvqhN2nEl4jVkq98OvbESu1U1Dnf/CxcazCZ5EAfshYa2ls0yUT1pDxrU3atiu42HLO/Lzu5siKMF/dwvV6VAFwGRGu/Kum00XbfCYq2N00+rpx7czZa6WPdrKrKyaymXMmmpCc9CQz4y84eiYYMUZQWq7Myj3qvuRv8le2pK/I+w4Ff3mOSBXR52JQbPSHK39heHeqg3LO8D7h6FRg0Ty/AtUbpdvR9KzFo64sy/qRhbue6N1qBBA2U1EIunWPs9F0ySLN8gwfIj8uztMfXxksVH6XoY3F6+Hq5kTxHbcebfEW9aD7PpiPtg1ao3xb4jZ6PA0db9WpJlBRIsa2BztkR2wU3q9ap8OPH3kPz3339DMmmLxVOaDechJ+9SJKdYYNV+gGb7VtXsrtfkbr+5KYKjW7atShqHeI9IPLOMQVyRxSX5tTknLGsm7ZjK7yDNn/W8bL0Lfu+SxiHZs0UQynikndOhK75adY76W7LYH8/JxPHMF9X/vW54EI0bVitRLFSkIDRGGugcRpUNdV05m+OYrReceiKqx82EJGWr0+AKdO3S0W/5wkDnCJ56yVsGOsfXX3+NH374QR0g33E2jtm6qUUr1+KVTSX1FO+aYDyERg22VNjCTyCr8B+bcvDBB+J14oqxz7P/H3RUR6LlO9idjXC04A71enney0MR6HJPk/biTNdCqPQjSSzPXgtlst2sN9Yg0bJCxaZrWhKgmaE7s5GQ0glHsjsiPz+0KhXlcR0Zxwh2HJ8uTsfP675QBhJpkndGQ5a61+TYrnbfU0q6d1S0QJfPhdwW9u7XkRDvmocmhSVHjbGKN8Prsxbh8MHf1Uu6Vg+w9oKu1XIjzcxy/VmV98DynF8eq2QCFOhluDoiWaB7D7uqBHqwDyeynXyBluRmem47TcV+1U7VAq7IlmHKS9zVn1uc504yhtdm74DFtFdZ2c2mPTBr2UVqrZb3TT2Y8Ra3PP/r6iMOw2Q6DIvpSBHXfc9jOpy14UQCrKY96sHKyJpeFeMw+lXSqnv/m/OwevVq/Pzzz8q1T5pYbJymi3Ak6yT1v8S2JVh+UQ9cqXWvUgsNgbLxB8M4lG283eI8902t/i/qpf6BzPS/YLPlu9+yOdOQ7zgDNufJkAWV1PjpEI8ZzyR5lX1Tl+v98BHgux8d6iHvSDoQZxXrLHDqSSacepKG/OO78M8/v7itz+rBRE9EvrMtbI5TkRI3T1nbjxYMUOK2qh9OAj0ki6Vw+oxPYcvbrPrq0FORa++KZMvHhQLkZticLUJK9hfKtVMe20ppJRHpUv5RmtSzlrrWl19iwtbNS3E04yfEJZyCh0ZXfqUG7/EFmg8jVl72y3ecqnKWSDOSyuU5zsFxW9egBGF5ii3vcQQStrL9qEfWIdn6pdrVqVdDvuM0JFrWqpJNWfmDVMhXMMK2Msbh77tm5CN/q9hbyeNgMteF2VITtvzNsMS1QoF2I0SEBCo9Je7PwZY8K81nIpBANwRdvHmDSlYriwtGO1pwl/KOK+97YKBr3dc4gx2HGG3kMyFeTq7vrVrKem608rSgV8R8GGykykaSZZl7IVvKsR0trOTi795R0QJdzh3MOWQcK1ZKmMTn0PXjClWztB6oXfcs9ffy71xu75V9Ly/NnHGfshGgQC8Dv0gR6DJEsVB/vMhRbAWvPJMQlQGl26U10Iq4Ox50r44Fix0qXk9qXFeVMDfG7E4ws9ChVkjlZibN20XPl5useAUAZncZrfK+qQczL8HEezWoewwd/i8dhw4dwpZth/Dnn4dg0uTHdRORlmu/Arn2c4O6qQfTr7Js452t2fNYYl1bu3Ytvv9+Lex2l7VTSmblO9ojyfq1EoRZ+ffAobtcrqvqZujPvU8ytEtJIskKvnmzSwxKk3wM8lAiOR08LTlVOQ45d6CHE5mT9z5cj+1bf3EnzTHGFJdwOpJq9sS2HVX/cBLMQ7KIrd27NiLJurTIwpZuag6b1rdYPGhZrvOK3PfFl7/AkUM/FX4+6sOc2A1a/lvq/zztHjRqWCdsPQE8uYwa9wuSLYtcnw+xSlmugGZzhRYd14ciLz+5yoWt8R3sLxxAvg8spv2FSf1cOQ+kSfUTqYIiLRhhW5EhB8FcjzIOKcFWPf5DaPqhE99d8QOQm183YPLIYM5R1m1Gj7epnACBLK5yHpOWqXI4SFibsZhlnL88F0NKkzwyWIFu9FfKL4o1PbugD+weOWgiRaDLOOT+nWhZhXjLzzhacCcczhMWaH/3cmEVSpWUUK+xQPdAOZ5xfxGvkmTLZ6oKQo6tFwocpxQ5XVU9k4Q6Zm5fegIU6KVnh0gS6MYwg/mCKAOSUu9qPJwEuyLumflVktCESzOEekmxc4ESThmeAOL6JJbbym7BxHtJnzwXGkxanhLqZu0wbM7WyrJTEQ8npWEhQsnbjczzOIuX5eG7Vb8gwbzGXTJL3rc50pBt6+PetCpvhsF8Zhd+mY3vf9iIOPMf6iHRaOI+Km6kRguHcfgTEMbDiZQxi1ex6ptU17MK7ofDeSIkpyrH8fv/dMx+1x6UoJPFKynHZiRf8/QCqMgHwdJ8VnztI5+ff3f9hWTL50UshAWOU5FTaImu6nEY10wgAWEx7UZq0nw4ChfkZLx1G5yLAucV2L7Lv7CdO9+Bdeud5equXJo5Mr6fXdfVx7Ca/lWfC/l8GK2qveGCGZfnfcZdZtVxBo7ZeoTNOAI9k/habJc4elnklSo04XIPDFWgS781LQ+6xxjktUCfL9mmIr+XA43DV9JIEbnijeXZKrKPga594/nPV6iBsa/3OMRl3+ZsVezQVTmOQOPk++VDIOoFelb2MeTn21Cvjiv5lnc7knEUZpMJNWsUFoYNgWskCnTDku5PtISAgJuWkoDEbUu8sxH7bCTEad3SVCXJ1YxhBBvv5S8jtCeS8rQelBK13908b4bx5l+RYP4RZlOGEuci0o1WlTfDYD6zRcsWZqh4dUnEJtYDcXcPh3EEM3/eDyfygGXRdqsM9uH2kOXPa8dwV25YX0NCvI6C3F9ht+1GUvXuyMvXsO+AXqGWmmBYB7ONMQ75TKRYPnTnC8gquAcS2iKtqgV6MOMwHu4lnCgl7n2VBEvcwbPyB6s4bmn+hG0oSa2C6U9ptymWrM/yFex60yLWtUgT6MJCrLYFjpPg1E88h4X7ODy9EiUbvWQZlzr2kvRWLO+1PJIvVlSCxGCuo0DC1hiHJI1MzwRMJvEIABwOQKLYjbJegTw7pC8VeZ8MNA7P5Jf+xhGsESgYthWxTUnVKbzPVZGsK2JcPGboBKJWoB9Oz8JtQ6Zg5+4DroeI5o0woG939OjSUf2/Z/9hPPjYS9j493b1f4f/OwXPPnYfaqdWV/8/+dJ7eGf+0iJEz27bBu++ONb9WiQK9NAvEe5RkQRK47JWkf0xjm3EQAfyTijpZhLuwtxznJ61X+X1OPPfKHCcXARzuN8Mo+WmHi3j8FfD17iwIkHYFikjqdmRbFks0anK5TKSxuEpbDXYkBL3CezO2iokx2jhLgilnzIf0jxLTzl1wOk8IQjDJWzN332qpOSRnvtEwnxIf43F6po1gHPOMqkwnEAeGZVxDzfOEUjY+rsXynvB3MuDPUdZxi3nkIoT40efWHD2dbxofCbxNc5wfyYpy1xzXxeBqBXoBw9n4tMlq3B11wuRnJiAOR8tw5sfLMF3C15AYkIcxj/zFvYfOoIJI/ojPs6Ke0Y9i1YtGmHKQwMUmCdenId/9x7EqPtOuLnGx1vRoO6JWBYKdH6MSMBFIJi43HBk5b3qLlYQiwUosLkeeg3rQbivuhsWvs3bnO6yXkUfdl1hIIEWXMJljoJNuhgu/fXuRzQKdGOM3u6vkbDQYAiIli1O1GLX9XxVkik90wkpoxwpglDmwZfAjaT+eyZ8lconiUlQtaHlp6oTvob6nWII9JRkV3UAyUGz/4AeNtdTsOJZvPqOZOiq9raEc0jdc0m8G0y4XbDnCJWt5/byjCHlIGUByl8zxiEhCjIXPa9ylXgMZhxl6V957xvpCw3lzSMWjxe1At17MnfvO4SufUZizoyH0TqtCS7ofh9enjoMl17gSqyy4vv1GPzIC9j4zZsqtlwEeubRHDzx8N0lXhcU6LH4keGYfRGIVIFujCVaboYlPZyUlBMhXK/mUMovhusYpF+RvtBg5NR4caarbmRKCuCwu9x4GzUEenU3+yzdFG5zEk0WW2GrBG6YJUoNdc5LCpOKpIUGYy6MUmaeDMJlHMF6w3nfC0Ppf7DW7VCvkbJsH0xVhLIcv6L3jZZnkormFM3HjxmBvuDLVXjkydlY9ekMxFktOO+qe/Hqk8Nx8XlnqPn9a8suXHfXo8rCLm7uItCXffsTzm93GlJrVMNlF7XDOWe6yjAZjQI9mj8aHFsoBCJdoBsWaLGoZ2SKFcekyvWdd465Ssr2hcLe17aR/nBijCnSxyHXk8R2GmEU8tArLVI8GYx5CDZ5ZFmv24ra32/Zwpouq1xV5/8IdezBJI8M9ZiVuX24VmQJlYFnDLfcO8QN27D0BrL2hnquyti+NPfyYK3bldH/aLl3RNs4KnPuo+VcMSHQN2/fjZvvm4Tbb+iKQf1dNVDvHTMNf2/dhcH9e8FqsWDZdz9h+ar1boG+cNlq7Ni9X7m/S5y6vPfc+PvQtdOJElIi0I8ePRot1wLHQQKlJpCZZUJGlobUGjpq1nCVworEtniZBT/+bMGdt9iQ1sxlNYzENuvdOOzYZeI4wmTyHpniyuo86eG8MOlRaN3YvssVIjH7XVf8p3w+EuN1NKgfWZ91+VyIMDxwyIT4OB0NG+hoWM+Jq7q4YrsjqcmcyHy0aObEXbcURFLXi/Q1msaxbQfQsgUi+t4RsReSV8c/XmjFhj/MvAdW4YRWq3aiok8VdiNiTx31Al2Swd06eLJKAjdlzACYJeAJQHbOccyatxi/bdqKasmJsNntWLX2D7eLu/eMjpnyOjKzspXV3Wgi0G02W8ROPjtOAiRQlEBGpqbi8Bo3QJWUuSuv+Xhptg55WLy3v4bWJ5LRl9fhK+04xjgeGW5Cak3JKRyZbfg4V9+ffVyLzAEU9lrmQ9r9d0buOLZsB155Q1dCiuOo+ssxWuaj6kmyB54E5Lraul1X3nCRfO+I5Hu5RRL6sJWaQFQL9C3b96DfsCeUe/q4YbfBYi65Xnb/YU8iOSkBMyYP9Qlz+syP8Mvv/6gYdk+BruuR+9BY6quGO5IACYQ1ASOG219pnLAeQGHnDBfxSHMJ92ZbGUmUImE+w6GPke4abjDkOMLhamIfSKBiCUR6mFfF0onuo0etQP9767/odec4XHX5+Rh8Zy+YpLgjgKTEeBVTnnMsVyWDszscWPTVakx5YS7ef/UxnHGKy9w07fX5uLpLRzRr0kC5wvd74EncdfNVuOfWHhTo0f2Z4OhIgARIoFwJiJiSFmnJ+soVQpgcjMI2TCaisBvRMh/hRZW9iRYCRqLOSMtCHy38q3IcUSvQv1yxFiMmvlKMrdRBl8zsP/y0EXePfEa9LzXSJ4zsB6lzbrQb75ngrpEur1175UXKCp8QH0eBXpVXLM9NAiRAAiRAAmUgIB4NkVAezt8QjSRrSQlQZaQitRkC/dKOJvTqUbKXY6SOj/0mARIggdIQiFqBHgiGWM73HTiiMrYnJboS+Hg3iVPPyMpG3dqpqna6d2MW90CU+T4JkAAJkAAJkAAJ+CZgLDTUTtUismIG55UESIAEKoJAzAr08oBJgV4eFHkMEiABEiABEiABEiABEiABEiABIUCBXobrgAK9DPC4KwmQAAmQAAmQAAmQAAmQAAmQQBECFOhluCAo0MsAj7uSAAmQAAmQAAmQAAmQAAmQAAlQoJfXNUCBXl4keRwSIAESIAESIAESIAESIAESIAFa0MtwDVCglwEedyUBEiABEiABEiABEiABEiABEqAFvbyugZ49e2LBggXldTgehwRIgARIgARIgARIgARIgARIIIYJ0IIew5PPoZMACZAACZAACZAACZAACZAACYQPAQr08JkL9oQESIAESIAESIAESIAESIAESCCGCVCgx/Dkc+gkQAIkQAIkQAIkQAIkQAIkQALhQ4ACPXzmIux64nTq0HUdZrOpSN/ktYysHOQcy0X9uqmIj7OGXd+jsUN2hwMmzQSTSYvG4UXcmGQ+LGZzxPU7Gjss30kOp9PnfMj32MEjGahRLQWJCXHROPywGlNuXgEyMo+iQb3aJX5X8bus8qbM33zwXl5582CcKSv7GPLzbahXp2bln3QlcW4AACAASURBVJxnLEJA7g3pmUdhtVpQo1oy6ZBAWBGgQA+r6fDdmXFPvYFPvvgOA2+7GoP791IbHc05jgu634el7z2NJg3rlvso5MY9/tm31HEnjOjnPv7vm7bi/oenIz0zW72WlJiAh4f0Rc9uF5d7H8L1gJfdMAwHDmVg1jMjcUH701U3v/3xN0x47i2smD+tQrotD1k33jMed9/SA93/c4H7HMtXrceQcS8UO+f6ZTNjZuGkKuZj156D6NZ3FL56/xk0alDHzf/iawe7Pxuek/LZm5PROq1xhVwb4XTQ3fsOoWufkapL3382A6k1qqm/n3vtQ7Wo9/io/hXS3YXLVmPazPnFPn8//LQRYya/5p6Tm3tejocG3xIzi1zCZcyU13F22zZ498WxbvbXD3gMt9/QFT26dCzX+Rg89nms+GGDOmatmtVw7ZUXY/jA3kXOUdJ3Wbl2JEwPVtn3cn/zwXs5UJnzcTg9C7cNmYKduw+oq7NV80YY0Le7+zPIezlQmffyH3/+H4aMm4HjuXlqPjr83ykYce+NaHtymvo/1u/lYfoVGlPdokCPgOmWm8jK1RvUQ+a3nzyPOrVqQFZhO/a4v0IE+tKV6zBp+hx1vuu7X1pEoP+2aSs2b9uNyy5qh2opSXj1nc/w6jufI9YEoc1mR6P6dfD+q49Cyu1VpEB/5tUP8Ob7X6or9cmx9xQR6F+v+gUPTZmJj2ZOKHIlN2tcT/UrFprc1CtzPvrc9zjk4Vaat0D/d+9ByKq80Tb9swMjJr6ihKN4m0R7MwS6iDNZtHvwHpc4e/bVD5F5tPwF+q49BzBgxDOQ8wpfzwUyeSC+tNdQ9RB89y3dsXvfYfTs/wgmjb4zZhYURaBPnPaOegh9eeowXHrBWWo+Kkqgv/jGAnTp1AHy/bPml01qMff9Vx7FGae2VOf1910W7Z8NGV9l38v9zQfv5ZU7HwcPZ+LTJatwddcLkZyYgDkfLcObHyzBdwteUJ49vJe7BHpl3cvXrN+EQ4czcckFZyEvrwATp72t7t2vPDFMfRXF+r08Fr6Pw32MFOjhPkOFN/Ua1ZPx04a/0O7MkzD6/j7FBLoI9qdeeg/Lvv0Z1VIScX33Tuqh9NixPNw98hmMe/A298rgoSOZGPTw83j60YFo1rh+MQLHc/NxNOcYpr0+HwnxcUUEuvfGHy5ciRmzP8aKj6bDaokNd1+5idx3+7V47Jk3MWPSELVY4S3Qt+7ci8nT52Dthj/VSvmg/r3Q5dL2yDp6LOT5yMzKQV5BAW6+73E8eHfvYgJ9wrNvYdWnMyLgSq6YLpZlPmR+Zsz+BDOfGel2f1619ne1IDLr2VE+La3yoLX/4BGIUPcW6N4jHDj6WdStnVphluOKIVr6oxoCXRaSRk9+Dd98NF25cnoL9G9Wb8C01+ZDPiftzjgJ44bdhpNaNsFbHyzBjn/3Y/yIO9ydeG3OQhw7nusW+569E1dpEeIrvt+AWfMWFRHoK75fj8GPvIDVC19yuy8++dJ7EFH/0pQHSj/ICNpTBPrb85fiwg5t8d2a3/DxrMfVNe0p0B0OJ954/wu89+lyZOfk4vKL2+GhQX0h95wHx7+MC9qfhhu6d3KPWqyy4sXTtdO5AUnIZ/Omay5Tnj/S/H2XBTxYFGwgAr0y7+XeyLznw/P9WLyXBzMf5Xkv9+RtfFfOmfGw+g4Ugc57eemfrUpzL/ecD8Pb6Lfls32GSsXavTwKvm4jfggU6BEwhXITqVk9RT1k3Tn8KXz9wbNISkooYkEf9fir+GvLLvUQKzE1U2fMwwMDrkffXlcoQdikUT08Ouw2Ndp35i/FR4u+xedvT/E7erG8OBwOnwL9l9//wefLfoCImeEDb8RVl58fASTLp4vykPPYg3dg41/bsHTlT1jwxiR8v+4Pt4t7foFNuT+fflIL3N77Sqzb8CdeeutTZeU+tU3zUs+HuA5LiIOni7vc1IeOm4Frul6I+Pg4tD/rZHTt1CGmYqPLMh8tmjbEJT2HYOLIfuh22XnqAhGrX9NG9TBm0M0lXjAS4iDn9SfQf/r1L9zxwBMBRXz5XJXhcRTjoVNE8f0PTcepbZph7NBbiwj0Ldv34Jp+Y5Vl+5Lzz8S7H38FYbX0vWeweftutRC1fP5zaFC3FuSzdNE1gzH14QG44uJzShzklyvW4ulX3i8i0Fet/QPyULVm0cvK20favAXL8f6nywN+94UHzbL3whDoEo5z4TWD8Nz4+9X3g6dAn79oJZ566X2MvO8mNKxXC8/P+hiNGtTGC48Pwez3vsCHn3+DJfOeUh45//t7B3rfMx4rP56OurX9x9CKK+9/bxldxHJvjMjXd1nZRxv+R6iqe7mQKWk+YvleHmg+5BqviHu5zMeCL1fhkSdnq8V18TjivdxlQS/ts1Vp7+XGt4aEAsm9ydsbUd6PxXt5+H+bRn8PKdAjYI6Nm4jE8skDf4smDTDsnhvcAl2+3Dt0G4inx92L/17uEhlPvDgPa9dvUuLxq+9+xgOPvoifl7yurIRX3/4w+vS8HH2uvbzUAn3RVz9i8fI1SqRKbHzfXv+JAJLl00XjJvJ/bVvjit7DlbUvJSnRLdAl7lUWRb7+8Dn1wCtNmF983pnqIbi08+HrofaPv7ZDQhIkwcneA0fUw7TE2YooipVW1vmY8sJc7Ph3H15/egTEu6TTdQ/gk9mP4+RWTUst0CWHw00DJ7o9XmJlLjwFuoTC3D50qhJ3H36+0u3i/sLsj7H46zUqPEfakYyjapHkxSlD0bnj2UrU9frvJbjr5qvUZ+XhqbOw+vMXVSKfkpovgS5eRd1vHYM2aU1w4zWdkZV9HO8t+BpiMQ60OBkt82UIdHnolFAkWVSVscu1acSgiyfIKa2b4bEHb1fDNoTC6s9fUgskna9/QMWvSxy7fFYOp2cqoe+vHTueh1sGTUJKchLemj6mWKLRWBfolX0v9zcfsXwvD/RsJYsaFXEvdy1ETlKfwUH9e6qPEu/lJwR6aZ+tSnMvF/aG9dwzr5Dx/Rar9/JouQdG8jgo0CNg9jxvIhs2bsYtgybjvZfHKRdbeciVmJ3utz2EL959Es2buFzW5aY74bm38dOXr6KgwIYLrxmshKS8f+M9E4q4fZaEwJ8F3dhHVt8l8Yk8hIvVMRaaIQglnnPWvMWYv3ClEt5TXnhXWfAkoZ+EB3i6nYs7fHbOcfVgW9r5COahVs4t10tJblrROD9lnY8/N+9UFkWxhn+16hcs+Wad+nz5a4Es6IbIMXJGRCN3X2PyFOiyaCQPt2KFqlWzulugi6VC2hMP3+0+hMyhWNRl0XDuJ18rLx/5Thk09nm1UDLkzuv8IvQl0GUHecCeOXeR+t24YR1s+nsHmjauF3Mu7iLQpepG5+uH4ZEHblXxr4ZAl2RI4nllJPrcd+AIrrhxuHuRSpJQ1qpRHQ8N6au8GaZPHKS8uUpqkgRu6LgXsP9gOt554WHUrJFSbNNgvsui8TNTFffyYOZDWMfivTzQfIj3W3nfy/fsP4xbB09WScmmjBlQbPHKuO55Lw/92ao093LDoCILlL2v7lzsaydW7+XR+P0baWOiQI+AGfO8iUh3xQVXRLl8sYhAr5achI5X368eOjt1/D81IkkO88WKNUq0S5Msyhv/2q4ySctq+uQxdwUceTAC3UjEZFhYAh40CjbwFISSfOnSXg/g7LatsWXHHiXQJb5WYvzFAiXxhtJkUcVw9y3tfATzUGu49f6y9HWVPyAWWnnMhwj0/1zSHp8t/V4JxUBVCfwJdImLFo8JcZk3qi7EwjzIGL0FuliFbho4QVU7aFivtorFf/rl97H6543Ku0eafB+d+9+BeG78fSquWeKUxR1bPIJGPv4Kvpz7pM9cGZ5MSxLontvIZ1U8jSR04dbru8TElHha0GXAEo/+9odL1HfDvbddozJIS+K8C889AyMG3qiYSHbju0Y87c4fIGFMA0c/p5LrzXjjE3z1/rMligqpLjLkkReQm5uP154a7lOcyzmC+S6Lxgmq7Ht5sPMhrGPxXh5oPsTSXZ73cnGh7jfsCZW3RvJu+CvTyXt56Z6tQrmXi/eh5NkoKXFoLN/Lo/H7N9LGRIEeATPmfRMxVgml60aZNRGAKckJKn4nIysbwx57CV0u7eAucbN91z5lZZc2Z8ZYtDujTYkjFxdQp9OJSc/Pgd3uwPjhd8BsNqvkQhI3JZaxc846GSZNw7SZHyn3oBXzn3PHeUYA0jJ10VMQyoEkrnXy83PcWaSFf5ebRqLPtZfhrr7d8fOvf6lkVZ5ZlEOZD7lJ6E5dzZ+EE3S/4gK3u6+cWyyMp53UAlnZORg58VWVrO+NaaPLNMZI2rk85uPjxd/h0affUMMWrxMpH1hSs9kdKknclTePUgtgUmbNM0GiWD4kB8TXHz4bc7VVvQW6MBQLrJQQErd1EeiGABRB3rF9W2Utf/ntz4rENUuCOfECEmEvboclNXE/lO8o8XqQMmtL5z0NzaS5H3zFfb56tWQcychSyQAlUZp8Z/qb30i69gP11VugizW1y03DVYUO8WAQgS6LuZ98+R2mTxiE+nVrYdL0d7DvYDrmvz5exZ3L90/n6x5Q+0heE1nA8tUkuagsxsj20yYMQkpyotrMZDK5Q338fZcFGks0vF+Z9/JA88F7uSuLu+T3MUoBej9bJScllNu9/O+t/6LXneNUvp7Bd/ZSnwtpSYnxqhwl7+UnXNyNahOlebYK9l7+2dIf8PDUmWrBVhZMjJZaI8V9f4jle3k0fN9G+hgo0CNgBuUmIl8aRski6bKs+snqnyHQRfBJsjDJOCpNLOnyAGYkR5LXbh08RT2oLp7zhN8SXBLHLO7xnk0erOUBWzK9SqZRo0lpI3HTOv+c0yKAZPl00VsQSpzmFb0fVKLZKPMkWd2lvJZRY9Ozhr3Ri2Dnw5hrz94vemcq0po1VJ4RksjJaGee1gpPjxuIJg3rls9gI+Ao5TEf4v573lX3BhW/L1ZYY14Fj+SAMMIZjGvhthu6lihkIgBpqbtoCPQfF72M6oWJ2cQKdW2/R9wCXQ7+yjufKWHoekBNUN9Vkj3caOs2/KUsTYZVvaQOGQnnPN8X0Wm4z0+f+ZFycZd28XlnYMKI/jFR7s7g4S3Q5XXj+90Q6HItPzx1lor3lyZhUFKdolWLxm6ssoDy0psL/JYLNLxKvOfK8/Ph77us1BddBO1YmffyQPPBe7lLoAd6tiqve7l4+cgzgXczvq94Ly8u0EvzbBXsvVw8RD/4bEWx+TCs6bF+L4+gr9Wo7SoFepRNrZSAio+3VqjlTqwgR9KPQoeOerVTfZaiijKspRqOeCLsP5Su4m8lOV9Ftbz8ApXcTEIdfMV7VtR5I+24lTUfkcalqvor16241TaoV6vCqg6IFVEWJRvUqx0zZSBLO5+SVE/qAcuiK1vVE+C9vOrnwOhBZd07eC8Pbs4raz6C6w23IoGKIUCBXjFceVQSIAESIAESIAESIAESIAESIAESCIkABXpIuLgxCZAACZAACZAACZAACZAACZAACVQMAQr0iuHKo5IACZAACZAACZAACZAACZAACZBASAQo0EPCxY1JgARIgARIgARIgARIgARIgARIoGIIUKBXDNdKO6rTqSM986jKIC7lz3w1ScSUnJToM1GZJAbKz7ehXp2aldZnnogESIAESIAESIAESIAESIAESKA4AQr0CL4qpJ7wkHEz3CWfOvzfKRhx741oe3KaGtWuPQcwcPRz2Ln7gPpfyqQ9+uDtKpuxiPbbhkxxv9eqeSNVFkpKfni3aa/Px6x5i+FZOimCsbHrJEACJEACJEACJEACJEACJBCWBCjQw3JaguvUmvWbcOhwJi654CxVHmfitLchFvVXnhimDnD3yGeQkpyIyWMGYP/BI+h9zwQ8Ouw2JcKlhMunS1bh6q4XIjkxAXM+WoY3P1iC7xa8UMTSvuDLVXjkydnqeBTowc0LtyIBEiABEiABEiABEiABEiCB0hCgQC8NtTDdZ+Gy1Rgz5XX8tnw2jh3PQ8ce9+PdF8fi7LZtVI8nPz8H+w+mY8bkocVGsHvfIXTtMxJzZjyMdmecpN7/6de/cN9D0zFxZD+MmPgKBXqYzju7RQIkQAIkQAIkQAIkQAIkEB0EKNCjYx7VKEScb9m+Bx/NnICtO/bg6jvGYuXH01G3tiu+XKzkny39Qb3v3QxL+apPZ6BWzWrK9f36AY9h+sRBqF8nFdf0G0uBHkXXCodCAiRAAiRAAiRAAiRAAiQQfgQo0MNvTkrVI8N6PuuZkbig/enYsHEzbhk0GasXvuROHvfhwpV49Z3PsGL+tCLn2Lx9N26+bxJuv6ErBvXviayjx9D7nvG4vfeVuLnn5Ur0U6CXalq4EwmQAAmQAAmQAAmQAAmQAAkETYACPWhU4bvhDz9tVPHmjz14O3pf3Vl11LCgf/vJ86hTq4Z6zZcFfc/+w7h18GRIgrkpYwbAbDZh6cp1eHD8y7jthq7QAKRnZUMWAG685jLc0P1SnNqmefjCYM9IgARIgARIgARIgARIgARIIEIJUKBH6MQZ3TbE9KTRd6Jnt4vdo5Hyad4x6I9PewcHD2e4Y9DFMt5v2BO47KJ2GDfsNljMZre4X/79evexJOP73E++xj239sBVl5+PVi0aRzg1dp8ESIAESIAESIAESIAESIAEwo8ABXr4zUnQPZJ48oenzsSYQTcrkW201BopSEpMwF0jnkb1lGRMHnNXsSzuf2/9F73uHKcE9+A7e8FkMqndkxLjkVqjWpE+0MU96CnhhiRAAiRAAiRAAiRAAiRAAiRQagIU6KVGV/U7Tpz2Dj74bEWxjhjW9O279qk66JKhXdq1V16E8cPvgNVqwZcr1qrM7N5NSrA98fDdFOhVP73sAQmQAAmQAAmQAAmQAAmQQIwRoECPgQk/cChD1UNPTkqIgdFyiCRAAiRAAiRAAiRAAiRAAiQQmQQo0CNz3thrEiABEiABEiABEiABEiABEiCBKCNAgR5lE8rhkAAJkAAJkAAJkAAJkAAJkAAJRCYBCvTInDf2mgRIgARIgARIgARIgARIgARIIMoIUKBH2YRyOCRAAiRAAiRAAiRAAiRAAiRAApFJgAI9MueNvSYBEiABEiABEiABEiABEiABEogyAhToUTahHA4JkAAJkAAJkAAJkAAJkAAJkEBkEqBAj8x5Y69JgARIgARIgARIgARIgARIgASijAAFepRNKIdDAiRAAiRAAiRAAiRAAiRAAiQQmQQo0CNz3thrEiABEiABEiABEiABEiABEiCBKCNAgR5lE8rhkAAJkAAJkAAJkAAJkAAJkAAJRCYBCvTInDf2mgRIgARIgARIgARIgARIgARIIMoIUKBH2YRyOCRAAiRAAiRAAiRAAiRAAiRAApFJgAI9MueNvSYBEiABEiABEiABEiABEiABEogyAhToUTahHA4JkAAJkAAJkAAJkAAJkAAJkEBkEqBAj8x5Y69JgARIgARIgARIgARIgARIgASijAAFepRNKIdDAiRAAiRAAiRAAiRAAiRAAiQQmQQo0CNz3thrEiABEiABEiABEiABEiABEiCBKCNAgR5lE8rhkAAJkAAJkAAJkAAJkAAJkAAJRCYBCvTInDf2mgRIgARIgARIgARIgARIgARIIMoIUKBH2YRyOCRAAiRAAiRAAiRAAiRAAiRAApFJgAI9MueNvSYBEiABEiABEiABEiABEiABEogyAhToUTahHA4JkAAJkAAJkAAJkAAJkAAJkEBkEqBAj8x5Y69JgARIgARIgARIgARIgARIgASijAAFepRNKIdDAiRAAiRAAqEQ2LpzL3b+ux8XnnsG4uOsoezKbUmABEiABEiABMqZAAV6OQPl4UiABEiABEggkgi8/NaneOmtT/HNR9NRr07NSOo6+0oCJEACJEACUUeAAj3qppQDIgESIAESIIHgCVCgB8+KW5IACZAACZBARROgQK9owjw+CZAACZAACYQhAV3XoWkaKNDDcHLYJRIgARIggZglQIEes1PPgZMACZAACYQbgSMZRzFr3mJ8++Ov2Ln7AJo3qY9zzjwZ/W/qhrRmDWGzOzBvwddYuGy1er9aSiLanpKGPtdcjgvan66GM+rxV9GgXi3c3OsKfPj5N/hz8y6ceVpL3HvbNSgosOG1dxdi0Vc/Yve+QzipZROYzWb8uXknXdzD7WJgf0iABEiABGKSAAV6TE47B00CJEACJBBuBMSifdPAidj493bc0L0TmjSqiy079igxPmFEP1zf/VJMe32+EvCXnH8W2p3RBgcPZ2DJN+twwTmn46lxA9WQLrthGLJzcnE8N0/936RhXZzapjmmTxyEgaOfxaq1f+C8s0/F2We0wYFDGVi68ie1LWPQw+2KYH9IgARIgARikQAFeizOOsdMAiRAAiQQdgT27D+MLjeNwFWXn+8W29JJsaofO56LZo3rK/Fts9mx8uPnYTab1BjyC2zYumMPTjuphVugi/B+bPgduLpLRyTEx6nXv/3xN9z30DT07HYxJo2+0z1+uriH3aXADpEACZAACcQwAQr0GJ58Dp0ESIAESCB8CIj7esce96sODerfEx3bn67c2i1ms7uTg8c+jxU/bMAdva9El04d0CatCZIS44sMQkR8y+aNMOuZkUVef+61DzH7vS/w+tMjcGGHthTo4TP17AkJkAAJkAAJuAlQoPNiIAESIAESIIEwIfDN6g149Kk3kJ6ZrXqUlJiAfjd1wx29u6q/pWb55OlzsHbDn+4eX3vlRbjvjmvRuEEd9VpJAt0Q9z99+VoRUU8LephMPrtBAiRAAiRAAgAo0HkZkAAJkAAJkEAYERCX9b+27MLGv7ZjwZerVAK3h4f0Rd9e/3H3csv2PfjfPzuw/PtfsHzVelx24dmYMXmoX4E+9olZ+HTJ91j0zlRlmTcaBXoYTT67QgIkQAIkEPMEKNBj/hIgABIgARIggXAgkJV9DPn5NtSrU9PdHXlN3N4lKdwrTwzD5u27lVu7Z7t+wGNKxP9v5Vt+BfrMuYswfeZHKv5c4tCNNuG5t1W2dyaJC4ergH0gARIgARKIdQIU6LF+BXD8JEACJEACYUHgj7+246aBE3DbDV1xzpknIT7OqjKsixV9ykMDcE3XC3F6pzvQtVMHdO54NurWrqkyvktmd3FznzzmLr8C/eDhTHS+/gG1jTrWyS2U9d1wl6dAD4vLgJ0gARIgARKIcQIU6DF+AXD4JEACJEAC4UFAMq+Pe2o2fvhpo7tDbU9OU8ngpA66pml45MnZSrAbTUqoXXZROwy87WrUqJbsFuitWzRWyeC8m2RyHzHxFXcJtjNPa4U6qdVV4rmVH09Xop+NBEiABEiABEig6ghQoFcde56ZBEiABEiABIoRKCiwYf+hdKQkJ6FWzWrF3nc4nDiUngm73aFqnIfaJFv8jn/3IbVGNdSpVSPU3bk9CZAACZAACZBABRKgQK9AuDw0CZAACZAACZAACZAACZAACZAACQRLgAI9WFLcjgRIgARIgARIgARIgARIgARIgAQqkAAFegXC5aFJgARIgARIgARIgARIgARIgARIIFgCFOjBkuJ2JEACJEACJEACJEACJEACJEACJFCBBCjQywA3KysLNWowwU4ZEHJXEiABEiABEiABEiABEiABEiCBQgIU6GW4FKTkja7rZTgCdyUBEiABEiABEiABEiABEiABEiABFwEK9DJcCRToZYDHXUmABEiABEiABEiABEiABEiABIoQoEAvwwVBgV4GeNyVBEiABEiABEiABEiABEiABEiAAr28rgEK9PIiyeOQAAmQAAmQAAmQAAmQAAmQAAnQgl6Ga4ACvQzwuCsJkAAJkAAJkAAJkAAJkAAJkAAt6OV1DVCglxdJHocESIAESIAESIAESIAESIAESIAW9DJcAxToZYDHXUmABEiABEiABEiABEggBgikZwDr1juRWhM47xxTUCNe+4sTGZnAue1MqJUa1C7cKEoIUKCXYSIp0MsAj7uSAAmQAAmQAAmQAAmQQAQS+GShA3v26+h1lRmNG2kBR7B5m44XZ9rRKk3DkLstAbeXDV543Y6t23UMGmBBm5aBzxHUQblRRBCgQC/DNFGglwEedyUBEiABEiAB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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from earthkit.plots.interactive import Chart\n", + "\n", + "TIME_FREQUENCY = \"6h\"\n", + "QUANTILES = [0, 0.1, 0.25, 0.5, 0.75, 0.9, 1]\n", + "\n", + "chart = Chart()\n", + "chart.title(f\"ECMWF ensemble meteogram at {location_to_string(LOCATION)}\")\n", + "chart.box(ds, time_frequency=TIME_FREQUENCY, quantiles=QUANTILES)\n", + "chart.line(ds,aggregation='mean', line_color='grey', time_frequency=TIME_FREQUENCY)\n", + "chart.show(renderer=\"png\") # Replace with chart.show() in an interactive session!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/Service/Examples/trajectory_example.ipynb b/docs/Service/Examples/trajectory_example.ipynb new file mode 100644 index 000000000..35c90955c --- /dev/null +++ b/docs/Service/Examples/trajectory_example.ipynb @@ -0,0 +1,562 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Trajectory Example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "n = 1000\n", + "lons = np.linspace(0, 5*360, n)\n", + "lats = np.linspace(-85, 85, n)\n", + "linspace = np.stack([lats, lons], axis=1)\n", + "pts = linspace.tolist()\n", + "\n", + "import earthkit.data\n", + "\n", + "request = {\n", + " \"class\": \"od\",\n", + " \"stream\" : \"enfo\",\n", + " \"type\" : \"pf\",\n", + " \"date\" : -1,\n", + " \"time\" : 0,\n", + " \"levtype\" : \"sfc\",\n", + " \"expver\" : 1,\n", + " \"domain\" : \"g\",\n", + " \"param\" : [ 167 ],\n", + " \"number\" : [ 1 ],\n", + " \"step\": [ 0 ],\n", + " \"feature\" :{ \n", + " \"type\" : \"trajectory\", \n", + " \"points\" : pts, \n", + " \"inflation\" : 0.1, \n", + " \"inflate\" : \"round\",\n", + " \"axes\" :[\"latitude\", \"longitude\"], \n", + " },\n", + "}\n", + "\n", + "ds = earthkit.data.from_source(\"polytope\", \"ecmwf-mars\", request, stream=False, address='polytope.ecmwf.int')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert to xarray" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset> Size: 847kB\n",
+       "Dimensions:    (datetimes: 1, number: 1, steps: 1, points: 21170)\n",
+       "Coordinates:\n",
+       "  * datetimes  (datetimes) <U20 80B '2024-11-17T00:00:00Z'\n",
+       "  * number     (number) int64 8B 1\n",
+       "  * steps      (steps) int64 8B 0\n",
+       "  * points     (points) int64 169kB 0 1 2 3 4 ... 21165 21166 21167 21168 21169\n",
+       "    x          (points) float64 169kB -84.96 -84.89 -84.82 ... 84.96 85.03 85.1\n",
+       "    y          (points) float64 169kB 0.0 1.169 1.154 2.278 ... 358.8 0.0 0.0\n",
+       "    t          (points) int64 169kB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0\n",
+       "Data variables:\n",
+       "    2t         (datetimes, number, steps, points) float64 169kB 237.9 ... 255.2\n",
+       "Attributes:\n",
+       "    class:          od\n",
+       "    Forecast date:  2024-11-17T00:00:00Z\n",
+       "    domain:         g\n",
+       "    expver:         0001\n",
+       "    levtype:        sfc\n",
+       "    number:         1\n",
+       "    step:           0\n",
+       "    stream:         enfo\n",
+       "    type:           pf
" + ], + "text/plain": [ + " Size: 847kB\n", + "Dimensions: (datetimes: 1, number: 1, steps: 1, points: 21170)\n", + "Coordinates:\n", + " * datetimes (datetimes) " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import earthkit.plots\n", + "\n", + "chart = earthkit.plots.Map()\n", + "chart.point_cloud(da['2t'], x=\"y\", y=\"x\")\n", + "chart.coastlines()\n", + "chart.borders()\n", + "chart.title(\"{variable_name}\")\n", + "chart.legend()\n", + "chart.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "polytope_venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/Service/Examples/vertical_profile_example.ipynb b/docs/Service/Examples/vertical_profile_example.ipynb new file mode 100644 index 000000000..c9c2f028d --- /dev/null +++ b/docs/Service/Examples/vertical_profile_example.ipynb @@ -0,0 +1,99 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vertical Profile Example" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import earthkit.data\n", + "\n", + "request = {\n", + " \"class\": \"od\",\n", + " \"stream\": \"enfo\",\n", + " \"type\": \"pf\",\n", + " \"date\": -1, # Note: date must be within the last two days \n", + " \"time\": \"0000\",\n", + " \"levtype\": \"pl\",\n", + " \"expver\": \"0001\",\n", + " \"domain\": \"g\",\n", + " \"param\": \"203/133\",\n", + " \"number\": \"1\",\n", + " \"step\": \"0\",\n", + " \"levelist\": \"1/to/1000\",\n", + " \"feature\": {\n", + " \"type\": \"verticalprofile\",\n", + " \"points\": [[38.9, -9.1]],\n", + " },\n", + "}\n", + "\n", + "ds = earthkit.data.from_source(\"polytope\", \"ecmwf-mars\", request, stream=False, address='polytope.ecmwf.int')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualise \n", + "\n", + "The following visualisation uses [earthkit-plots](https://earthkit-plots.readthedocs.io/en/latest/).\n", + "\n", + "
\n", + " Note: This notebook is rendered in many different ways depending on where you are viewing it (e.g. GitHub, Jupyter, readthedocs etc.). To maximise compatibility with many possible rendering methods, all interactive plots are rendered with chart.show(renderer=\"png\"), which removes all interactivity and only shows a PNG image render.

\n", + " If you are running this notebook in an interactive session yourself and would like to interact with the plots, remove the renderer=\"png\" argument from each call to chart.show().\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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31iXvrZBbf325xZ5PGmaNrbfSW8nUPXmZ/vNbHaLTPvw+uru3+Nu62UWHAgggIDTBQi8Tu9h2ocAAqclkJqaphadntB1DepocO8Op1VHToUCVW++3SAVIYAAAg4UIPA6sFNpEgIInLrADz//oVdmLVCdi89TVIniWvn1Ousht/mvx+q86medeoXHSwSq3tO+IQoigAACLhQg8Lqw02kyAghkFzCv33126jvavHWHtbPBedXPVpe7m+rSi2rY4gpUvbZuisIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAggggAACCLhNgMDrth6nvQgggAACCCCAgMsECLwu63CaiwACCCCAAAIIuE2AwOu2Hqe9CCCAAAIIIICAywQIvC7rcJqLAAIIIIAAAgi4TYDA67Yep70IIIAAAggggIDLBAi8LutwmosAAoVP4PCRowoPD1N4WFjhuznuCAEEEHCAAIHXAZ1IExBAoOgJxK3+Ue/M/1zf//Snkg8dthpQt/b56tWlla6+4uKi1yDuGAEEECjEAgTeQtw53BoCCDhXoO+IF7U47ls1u/kq1ah2pvYkJOmd+cusBr/36lO68Lyqzm08LUMAAQSCLEDgDTI4l0MAAQSMwJIV3+nyS85TpQpl0kHe+3ilho37r/r3uFtd7mkGFAIIIIBAPgkQePMJkmoQQAABn8C3P/5PL77xocz/litTStdccYn69bhblSuWzRVp3sIVGj7+dT0z+AG1aNwQUAQQQACBfBIg8OYTJNUggAACRmDFV+v08BMTVe3syrqz6XXaf+CQ/vvOJ4qKLKHFb4+zAnDmIy3No90JSVr7yx8a++LbSkk5piXvPKfIEsUARQABBBDIJwECbz5BUg0CCCDg8XjUtP3j1kNon8waq1IloyyUNWv/py59nlHHNk00sGe7LFBLv/hejw6dYn3PhORpY/up6lmVwEQAAQQQyEcBAm8+YlIVAgi4WyB+9141uusxdW7bVAMevicLRpN2A6wAPG/6yCzf37Zjt1at+Vnmf9/64HOVKB6ht6YO1TlnEnrdPZpoPQII5KcAgTc/NakLAQRcLfDT+g1q9/Ao9X2wrbq2a57F4t5esfp9wz/6dtG0kxr9/L+NuqfHSLW5/SaN6N/Z1ZY0HgEEEMhPAQJvfmpSFwIIuFrgn3/jrSUNt95wpSY91Svd4ujRFNVt3N1asmCWOpzs8J1X79ILNHPKYFdb0ngEEEAgPwUIvPmpSV0IIOBqgWOpqbrm9p7WGt4fl76miHDvm9O+/+kPdew9Rrffeo3GDnlQ5kG1zVt3qHrVKlm8vvruV3XrP05tWzbS8L6dXG1J4xFAAIH8FCDw5qcmdSGAgOsFZr+/VGOen6WbGl6u9q3+owMHkzXh5bnaun2XPnpzjGpWO1NHjqaoXuPuuuHqy9TwyktUqUJZbdj8r15/Z5EVlt9/bZRq1TzH9ZYAIIAAAvklQODNL0nqQQABBCSlpqbpzbmf6rlp76Z7mK3Ixg17SFfX874y2MwEPz7qZS2OW5PFzCx5GNGvixrUvRBLBBBAAIF8FCDw5iMmVSGAAAI+gZRjqdqydYciSxRXlcrlFRISkg3HBN9de5KUuHefNctboVwMgAgggAACARAg8AYAlSoRQAABBBBAAAEECo8Agbfw9AV3ggACCCCAAAIIIBAAAQJvAFCpEgEEEEAAAQQQQKDwCBB4C09fcCcIIIAAAggggAACARAg8AYAlSoRQAABBBBAAAEECo8AgddGXzRq1EjLly+3UQNFEUAAAQQQQAABBAItQOC1IWy2GfJ4PDZqoCgCCCCAAAIIIIBAoAUIvDaECbw28CiKAAIIIIAAAggESYDAawOawGsDj6IIIIAAAggggECQBAi8NqAJvDbwKIoAAggggAACCARJwBGBNy3NY62lDQsLzcZmPovfk2i9sjM8LCzb5/sPJFvvtS8bUypH8t0JSYqOilRkiWLZPifwBmmUchkEEEAAAQQQQMCGQJEPvCbojnjuDYtgZP8uWShWfLVO/Z96ScmHDlvfH96vs9q2uMn6s/newNEva9mqtdbXdS6uqSmje6e/y37Ltp3qMXCCNm/daX3euvkNGta3kyLCM0IzgdfGyKMoAggggAACCCAQJIEiHXgXx63R6EkzlbB3v+66/cYsgffQ4aO6oVVv9bq/lTq0/o/iVv+oR4dO0eK3x+nsKhX16lsfa+6COM2cMsSavX1o0ERVr1pFox6/36J/YMB4lYyOVOyg7toRv0dtHxypYX06qkXjhuldQ+AN0ijlMggggAACCCCAgA2BIh14kw8d0b4DBzXxlbkqUbxYlsBrZncffmKi1i6ZrmLFIiyi5vcOtMJvh9a36q7uw9Xkpvrq3uF26zMTnvuOmKpflr+ufQeS1bBFT816YYjq1j7f+jx28kztiE/QlNhHCbw2BhxFEUAAAQQQQACBYAsU6cDrw3pq4gylpqZmCbzvLojTG3MW6ZNZY9NNHxkyWeeeU0X9erRV/WY9NHpgVyv0mmP9H5vU5oERWr3gRe3es1ctOw9R3HuTVLF8GevzmfOWaP7iVZo3fSSBN9ijlOshgAACCCCAAAI2BBwbeM2ShU+Xr8kSUM163pJRkRrer5NqN+qiqU/30Y3XXGbxbdi0zQq5S+c8px27EnRvr1gr/MaUirY+NwF62oz5WjZ3YpbAe/DgQRv8FEUAAQQQQAABBPIWiIqKyvskzjipgGMDrz8zvLGDuqnxjVdaODnN8K54f3L6Q2wnm+FNbHOtImd+zhBDAAEEEEAAAQQCJlC8ePGA1e2Gih0beH1reH/87FVFRIRbfdmk3QB1bNM4fQ1v00YN1K39bdZnea3hHTVxhuJ3J2Zbw2sCb5l3v3TDWKGNCCCAAAIIIIBAkRQo0oE3NTVNaWlpGj15po4dS9WIfp0VFham0NAQmQfa6jd7UAN7tlP7HHZpmD57oeYtXGHt0hAVWdzagizzLg3d+o9T6ZLRMrPAue3SQOAtkuOem0YAAQQQQAABFwkU6cD77kfLNXLCm1m6y2wrZvbMNYfZY9c8qOY7nnzsPrW78xbry4PJh609eld+vc76unat6tbsbaUK3ofUNm7ZboXgrdt3WV/f2fQ6K1D7ZovN98y2ZAReF/1toakIIIAAAgggUCQFinTg9UfczAKbh9AqlS+TJaz6yibtP6iUlGPpa3VPrHPnrkRrP97oqBLZLpceeGctk4plfxObP/fHOQgggAACCCCAAAKBFXB84A0kX3rgnblUKp49EAfy2tSNAAIIIIAAAggg4J8Agdc/pxzPSg+8M5ZIJdguxAYlRRFAAAEEEEAAgYAJEHht0PoCb8wbnyokqqSNmiiKAAIIIIAAAgggECgBAq8N2fTA+99FCilZykZNFEUAAQQQQAABBBAIlACB14ZseuB97WOFlIqxURNFEUAAAQQQQAABBAIlQOC1IZseeF9dqJDS3u3MOBBAAAEEEEAAAQQKlwCB10Z/pAfeV+YrpEx5GzVRFAEEEEAAAQQQQCBQAgReG7LpgXfahwopV8FGTRRFAAEEEEAAAQQQCJQAgdeGbHrgfel9hZSvZKMmiiKAAAIIIIAAAggESoDAa0PWF3hLvzhPoRXPsFETRRFAAAEEEEAAAQQCJUDgtSGbHnhfeFehlc60URNFEUAAAQQQQAABBAIlQOC1IZseeJ+fo9AzzrJRE0URQAABBBBAAAEEAiVA4LUhmx54J7+t0Crn2KiJoggggAACCCCAAAKBEiDw2pBND7wTZyv0rGo2aqIoAggggAACCCCAQKAECLw2ZNMD74SZCj27uo2aKIoAAggggAACCCAQKAECrw3Z9MA7foZCq9awURNFEUAAAQQQQAABBAIlQOC1IesLvKWefUNh555noyaKIoAAAggggAACCARKgMBrQzY98I79r8KqX2CjJooigAACCCCAAAIIBEqAwGtDNj3wPvOqwmpcaKMmiiKAAAIIIIAAAggESoDAa0M2PfCOma6w8y6yURNFEUAAAQQQQAABBAIlQOC1IZseeEe/rLALLrFRE0URQAABBBBAAAEEAiVA4LUhmx54R72ksFqX2qiJoggggAACCCCAAAKBEiDw2pD1Bd6ST01V+IV1bNREUQQQQAABBBBAAIFACRB4bcimB94RUxR+cV0bNVEUAQQQQAABBBBAIFACBF4bsumBd9jzCq9dz0ZNFEUAAQQQQAABBBAIlACB14ZseuAdOknhl15poyaKIoAAAggggAACCARKgMBrQzY98D45UeF16tuoiaII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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from earthkit.plots.interactive import Chart\n", + "\n", + "chart = Chart()\n", + "chart.line(ds, y=\"z\")\n", + "chart.fig.update_layout(yaxis1={\"title\": \"hPa\"})\n", + "chart.fig.update_layout(yaxis2={\"title\": \"hPa\"})\n", + "chart.show(renderer=\"png\") # Replace with chart.show() in an interactive session!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/Service/Features/boundingbox.md b/docs/Service/Features/boundingbox.md new file mode 100644 index 000000000..dfb70a464 --- /dev/null +++ b/docs/Service/Features/boundingbox.md @@ -0,0 +1,86 @@ +# Bounding Box + +## Basic Example + +An example bounding box requested via earthkit-data: + +```python +import earthkit.data + +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, # Note: date must be within the last two days + "time" : "0000", + "expver" : "0001", + "domain" : "g", + "param" : "164/167/169", + "levtype" : "sfc", + "number" : "1", + "step" : "0", + "feature" : { + "type" : "boundingbox", + "points" : [[-1, -1], [1, 1]], + }, + "format" : "covjson", +} + +ds = earthkit.data.from_source("polytope", "ecmwf-mars", request, stream=False, address='polytope.ecmwf.int') +``` + +This request will return a bounding box from yesterday's 00Z forecast for the three requested parameters for the points within a bounding box with top left coordinate at latitude -1 and longitude -1, and bottom right point at latitude 1 and longitude 1. + +`"polytope"` refers to the underlying service being used to return the data. `"ecmwf-mars"` is the dataset we are looking to retrieve from. Setting `stream=False` returns all the requested data to us once it is available. `address` points to the endpoint for the polytope server. + + +## Required Fields + +For a bounding box, two fields are required within the `feature` dictionary + +* `type` +* `points` + +For a bounding box, `type` must be `boundingbox`. + +`points` must contain two points, the first corresponding to the top left of the requested box, and the second corresponding to the bottom right coordinate. By default they should only contain a latitude and longitude. However as seen below this can be changed with the `axes` key. + + +## Optional Fields + +`axes` refers to the axes on which to generate the bounding box. As stated above the minimum default `axes` contains `latitude` and `longitude` meaning if `axes` is not included these values must be provided per point. By default the level is taken from the main body of the request. + +However `axes` can also be provided by the user and with a value for level. Such as here: + +```python +"axes" : ["latitude", "longitude", "levelist"] +``` + +In this case the user must provide a `latitude`, `longitude` and `levelist`. `levelist` should not be included in the main body of the request in this case. An example can be seen here: + + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, + "time" : "0000", + "levtype" : "pl", + "expver" : "0001", + "domain" : "g", + "param" : "203/133", + "number" : "1", + "step" : "0", + "feature" : { + "type" : "boundingbox", + "points" : [[-0.1, -0.1, 500], [0.1, 0.1, 1000]], + "axes" : ["latitude", "longitude", "levelist"] + }, + "format" : "covjson" +} +``` + +For this request, a bounding box with top left corner at lat -1, lon -1 and pressure level 1000, and bottom right corner at lat 1, lon 1, and pressure level 500. + +Without level in the `axes` this will be taken from the main body of the request. In the case of `levtype` = `sfc`, no levelist is required. \ No newline at end of file diff --git a/docs/Service/Features/feature.md b/docs/Service/Features/feature.md new file mode 100644 index 000000000..bc866f540 --- /dev/null +++ b/docs/Service/Features/feature.md @@ -0,0 +1,22 @@ +# User Guide for Polytope Feature Extraction on ECMWF Data + +## Introduction + +Polytope is a service which enables users to download "features" from entire datacubes of earth system data. The best way to access this data is via [earthkit-data](https://earthkit-data.readthedocs.io/en/latest/guide/sources.html#polytope). + +Follow the links below to see how to request different types of features. + +## Feature Documentation + +- [Time Series](timeseries.md) +- [Vertical Profile](vertical_profile.md) +- [Polygon](polygon.md) +- [Bounding Box](boundingbox.md) +- [Trajectory](trajectory.md) + +## Notes + +Some important notes that hold for all features are that: + +* The data has to exist in the fdb on the polytope server. +* Further details on the `from_source` method can be found here: [https://earthkit-data.readthedocs.io/en/latest/guide/sources.html](https://earthkit-data.readthedocs.io/en/latest/guide/sources.html) \ No newline at end of file diff --git a/docs/Service/Features/polygon.md b/docs/Service/Features/polygon.md new file mode 100644 index 000000000..96423e0bc --- /dev/null +++ b/docs/Service/Features/polygon.md @@ -0,0 +1,85 @@ +# Polygon + +## Basic Example + +An example polygon requested via earthkit-data: + +```python +import earthkit.data + +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, # Note: date must be within the last two days + "time" : "0000", + "levtype" : "sfc", + "expver" : "0001", + "domain" : "g", + "param" : "164/167/169", + "number" : "1", + "step": "0", + "feature" : { + "type" : "polygon", + "shape" : [[-1, 1], [-1, 0], [0, 1]], + }, + "format" : "covjson", +} + +ds = earthkit.data.from_source("polytope", "ecmwf-mars", request, stream=False, address='polytope.ecmwf.int') +``` + +This request will return all points contained in `shape` from yesterday's 00Z forecast for `step` `0`, ensemble `number` `1` and the three provided parameters. + +`"polytope"` refers to the underlying service being used to return the data. `"ecmwf-mars"` is the dataset we are looking to retrieve from. Setting `stream=False` returns all the requested data to us once it is available. `address` points to the endpoint for the polytope server. + + +## Required Fields + +For a polygon two fields are required within the `feature` dictionary + +* `type` +* `shape` + +For a polygon `type` must be `polygon`. + +The values in `points` must correspond to a latitude and a longitude. The first point is assumed to be the last point, however users can also provide the last point in the request to complete the polygon. + +The polygon feature also has a max number of points that can be requested in the perimeter of the polygon, and the max area of the polygon is also constrained based on the config provided. + +`shape` can also take multiple polygons in a single request in the following form: + +```python +"shape" : [[[-1, 1], [-1, 0], [0, 1], [-1, 1]], [[-2, 2], [-2, 1], [1, 2], [-2, 2]]], +``` + +User can also request ranges for other keys such as `number`. In this case the polygon cutout will be returned for each of the values requested. + +```python +request = { + "class" : "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, + "time" : "0000", + "levtype" : "sfc", + "expver" : "0001", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/2", + "step": "0/1", + "feature" : { + "type" : "polygon", + "shape" : [[-1, 1], [-1, 0], [0, 1], [-1, 1]], + }, + "format" : "covjson", +} +``` + +The returned values will be: + +* `number: 1, step: 0, Points within shape` +* `number: 1, step: 1, Points within shape` +* `number: 2, step: 0, Points within shape` +* `number: 2, step: 1, Points within shape` + diff --git a/docs/Service/Features/timeseries.md b/docs/Service/Features/timeseries.md new file mode 100644 index 000000000..609242b89 --- /dev/null +++ b/docs/Service/Features/timeseries.md @@ -0,0 +1,106 @@ +# TimeSeries + +## Basic Example + +An example of a time-series requested via earthkit-data: + +```python +import earthkit.data + +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, # Note: date must be within the last two days + "time" : "0000", + "levtype" : "sfc", + "expver" : "0001", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axes": "step", + "range" : { + "start" : 0, + "end" : 360, + } + }, + "format": "covjson", +} + +ds = earthkit.data.from_source("polytope", "ecmwf-mars", request, stream=False, address='polytope.ecmwf.int') +``` +The following will return a timeseries starting yesterday at 00Z with steps from `0` to `360` including all steps available in between, for the parameters `164/167/169` at the point given. This data will be returned for each ensemble number requested. + +`"polytope"` refers to the underlying service being used to return the data. `"ecmwf-mars"` is the dataset we are looking to retrieve from. Setting `stream=False` returns all the requested data to us once it is available. `address` points to the endpoint for the polytope server. + +## Required Fields + +For a timeseries within the `feature` dictionary three fields are required + +* `type` +* `points` +* `axes` + +For a timeseries `type` must be `timeseries`. + +`points` must be a nested list with a points containing a latitude and a longitude. + +`axes` refers to the axis on which to generate the timeseries. In this case the timeseries is generated across `step` based on the inputted `range`. However if the data requested was a climate dataset the `axess` may be `datetime` denoting that the timeseries is generated across that axis. + + +## Optional Fields + +`range` is an optional field within `feature`. It refers to the extent of the `axes` on which the timeseries will be generated. In the above case where: + +```python + "axes": "step", + "range" : { + "start" : 0, + "end" : 360, + } +``` + +A timeseries across `step` will start at step `0` and end at step `360` with all steps found in between being included. `range` can also contain `interval`. + +```python + "axes": "step", + "range" : { + "start" : 0, + "end" : 360, + "interval" : 2, + } +``` +In this case every second step will be returned if it exists. + +As `range` is an optional field it can be left out, however there is not a default value. Instead the user has to include the timeseries `axes` in the main body of the request like below: + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, + "time" : "0000", + "levtype" : "sfc", + "expver" : "0001", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "step" : "0/to/360", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axes": "step", + }, + "format": "covjson", +} +``` + +This is equivalent to the first request presented. + +At least one of `range` or `step` must be included in the request, but not both. In this case an error will be provided telling the user that `step` is overspecified. + +Conversely at least one of `range` or `step` must be included. diff --git a/docs/Service/Features/trajectory.md b/docs/Service/Features/trajectory.md new file mode 100644 index 000000000..7662f4192 --- /dev/null +++ b/docs/Service/Features/trajectory.md @@ -0,0 +1,139 @@ +# Trajectory + +## Basic Example + +An example trajectory requested via earthkit-data: + +```python +import earthkit.data + +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, + "time" : "0000", + "levtype" : "sfc", + "expver" : "0001", + "domain" : "g", + "param" : "164/166/167", + "number" : "1", + "step": "0", + "feature" : { + "type" : "trajectory", + "points" : [[-0.1, -0.1], [0, 0], [0.1, 0.1]], + "inflation" : 0.1, + "axes" :["latitude", "longitude"], + }, +} + +ds = earthkit.data.from_source("polytope", "ecmwf-mars", request, stream=False, address='polytope.ecmwf.int') +``` + + +The `trajectory` `feature` contains another field called `inflation`. This is the inflation of the shape swept around the trajectory where points within this inflation are returned to the user. In this example above, as an `inflate` value is not given, the default is `round`, meaning that the `inflation` acts as a radius around a circle. This request will thus return a trajectory from yesterday's 00Z forecast for the three requested parameters for the points contained in a circle of radius `0.1` along the given path. + +`"polytope"` refers to the underlying service being used to return the data. `"ecmwf-mars"` is the dataset we are looking to retrieve from. Setting `stream=False` returns all the requested data to us once it is available. `address` points to the endpoint for the polytope server. + +## Required Fields + +For a trajectory three fields are required within the `feature` dictionary + +* `type` +* `points` +* `inflation` + +For a trajectory, `type` must be `trajectory`. + +The values in `points` can change depending on the `axes`. `axes` can contain the following values: + +```python +"axes" : ["latitude", "longitude", "levelist", "step"] +``` + +In this default case, a nested list of at least two points with values for `latitude` and `longitude` must be provided. + +Another required field that is within the `feature` dictionary is `inflation`. This refers to the inflation of the shape swept around the trajectory along which points will be included. + +`inflation` can be either a single value or a list of values. If it is a single value, this will be the inflation along each of the `axes`. If it is a list of a values, each value will correspond to the inflation of the corresponding `axes` axis. + +By default the shape swept around the trajectory is `round` but this can be overridden using the `inflate` keyword below. + + +## Optional Fields + +`axes` refers to the axes on which to generate the trajectory. As stated above, the minimum default `axes` contains `latitude`, `longitude` meaning if `axes` is not included these values must be provided per point. + +However `axes` can also be provided by the user and with more values: + +```python +"axes" : ["latitude", "longitude", "levelist", "step"] +``` + +In this case a point must contain a value for each axis. + +`inflate` determines the shape that will be swept along the trajectory, by default the value is `round` which corresponds to a circle in 2D and a sphere in 3D. The other value available is `box`. This sweeps an n-dimensional box along the trajectory depending on the specified `axes`. + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, + "time" : "0000", + "levtype" : "sfc", + "expver" : "0001", + "domain" : "g", + "param" : "164/166/167", + "number" : "1", + "step": "0", + "feature" : { + "type" : "trajectory", + "points" : [[-0.1, -0.1], [0, 0], [0.1, 0.1]], + "inflation" : [0.1, 0.2], + "inflate" : 'box', + "axes" :["latitude", "longitude"], + }, +} +``` + +This request returns the same as the first request, but sweeping a box of size `0.1` in the `latitude` direction and `0.2` in the `longitude` direction. + + diff --git a/docs/Service/Features/vertical_profile.md b/docs/Service/Features/vertical_profile.md new file mode 100644 index 000000000..a96b96eda --- /dev/null +++ b/docs/Service/Features/vertical_profile.md @@ -0,0 +1,107 @@ +# Vertical Profile + +## Basic Example + +An example vertical profile requested via earthkit-data: + +```python +import earthkit.data + +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, # Note: date must be within the last two days + "time" : "0000", + "levtype" : "pl", + "expver" : "0001", + "domain" : "g", + "param" : "203/133", + "number" : "1", + "step" : "0", + "feature" : { + "type" : "verticalprofile", + "points": [[-9.10, 38.78]], + "axes": "levelist", + "range" : { + "start" : 0, + "end" : 1000, + } + }, + "format": "covjson", +} + +ds = earthkit.data.from_source("polytope", "ecmwf-mars", request, stream=False, address='polytope.ecmwf.int') +``` +The following will return a vertical profile from yesterday's 00Z forecast with levels from `0` to `1000` including all levels available in between, for the parameters `164/167/169` at the point given. This data will be returned for each ensemble number requested. + +`"polytope"` refers to the underlying service being used to return the data. `"ecmwf-mars"` is the dataset we are looking to retrieve from. Setting `stream=False` returns all the requested data to us once it is available. `address` points to the endpoint for the polytope server. + +## Required Fields + +For a vertical profile two fields are required within the `feature` dictionary + +* `type` +* `points` + +For a vertical profile `type` must be `verticalprofile`. + +`points` must be a nested list with two points corresponding to a latitude and a longitude. + + +## Optional Fields + +`axes` refers to the axis on which to generate the vertical profile. In this case the vertical profile is generated across `levelist` based on the inputted `range`. In the vertical profile this field is optional as the default is assumed to be `levelist` if not given. + +`range` is an optional field within `feature`. It refers to the extent of the `axes` on which the vertical profile will be generated. In the above case where: + +```python + "axes": "levelist", + "range" : { + "start" : 0, + "end" : 1000, + } +``` + +A vertical profile across `levelist` will start at level `0` and end at level `1000` with all levels found in between being included. `range` can also contain `interval`. + +```python + "axes": "levelist", + "range" : { + "start" : 0, + "end" : 1000, + "interval" : 2, + } +``` +In this case every second level will be returned if it exists. + +As `range` is an optional field it can be left out, however there is not a default value. Instead the user has to include the vertical profile `axes` in the main body of the request like below: + +```python +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, + "time" : "0000", + "levtype" : "pl", + "expver" : "0001", + "domain" : "g", + "param" : "203/133", + "number" : "1", + "step" : "0", + "levelist" : "0/to/1000", + "feature" : { + "type" : "verticalprofile", + "points": [[38.9, -9.1]], + "axes" : "levelist", + }, +} +``` + +This is equivalent to the first request presented. + +At least one of `range` or `levelist` must be included in the request, but not both. In this case an error will be provided telling the user that `levelist` is overspecified. + +Conversely at least one of `range` or `levelist` must be included. + diff --git a/docs/Service/Installation.md b/docs/Service/Installation.md new file mode 100644 index 000000000..807fa748b --- /dev/null +++ b/docs/Service/Installation.md @@ -0,0 +1,55 @@ +# Installation + +Install **earthkit-data** with python3 (>= 3.10) and ``pip`` as follows: + + + python3 -m pip install earthkit-data[polytope] + +To use covjson functionality also run: + + python3 -m pip install earthkit-data[covjsonkit] + +To use any of the visualisations, also install earthkit-plots: + + python3 -m pip install earthkit-plots + +Installing like this gives you a **minimal** package which can talk to Polytope. If you want to use more data types or remote services you need to install other optional features of earthkit-data, or just install all of them: + + python3 -m pip install earthkit-data[all] + +For further details on earthkit-data installation you can visit this page: https://earthkit-data.readthedocs.io/en/latest/install.html. + +We recommend to create a conda environment for your earthkit installation. This can be done as follows: + +``` +envname=earthkit +conda create -n $envname -c conda-forge -y python=3.10 +conda activate $envname + +python3 -m pip install earthkit-data[polytope] + +# To allow easy use with a jupyter notebook run the following +python3 -m pip install ipykernel +python3 -m ipykernel install --user --name=$envname +``` + +# Authentication + +To access ECMWF data you need an ECMWF account. This can be created https://www.ecmwf.int/. Once created, you can find your key at https://api.ecmwf.int/v1/key/. + +**DISCLAIMER** +> *Polytope is currently available for users at the national meteorological services of ECMWF’s Member and Co-operating States.* + +Copy your API key into your home directory, in a file called `~/.polytopeapirc`. Ths file should have the following format: + + +``` +{ + "user_email" : "", + "user_key" : "" +} +``` + +You should now be automatically authenticated when using Polytope feature extraction via earthkit-data. + +After following these steps, go to the Quick Start guide to begin making requests. \ No newline at end of file diff --git a/docs/Service/Overview.md b/docs/Service/Overview.md new file mode 100644 index 000000000..6509be2bd --- /dev/null +++ b/docs/Service/Overview.md @@ -0,0 +1,42 @@ +# Polytope Feature Extraction + +Polytope is a web service designed to provide efficient access to datacubes. Polytope's key feature is the ability to directly extract **features** from a datacube, as well as whole fields, without any intermediate copies. + +Features currently includes time-series, vertical profiles, a custom polygon region, bounding box or spatio-temporal trajectory. + +Extracting features directly offers two main advantages: + +- Reduced I/O usage when requesting data from large datacubes, which means less data downloaded. + +- Reduced post-processing needs for users after extraction, making the data more analysis-ready. + +
+

+ Example Features +

+
+ +## Polytope Feature Extraction vs Web MARS + +Feature extraction differs from Web-MARS by allowing users to request specific features rather than only global fields. However, Polytope does also allow users to request global fields by simply omitting the `feature` keyword from the request. + +Both Polytope and Web-MARS are integrated into the earthkit ecosystem allowing users to request and retrieve data using either service. Earthkit tools for mapping, plotting, regridding, and transforming are available for working with both whole fields and specific features. + +## Feature Extraction Client + +The recommended client for Polytope Feature Extraction is earthkit-data. A guide on how to install earthkit-data can be found here, a quick start user guide is also provided here. This allows users to quickly install earthkit-data and to begin making requests. + +For more in-depth information about the various features see the following pages: + +### Features + - Timeseries + - Vertical Profile + - Polygon + - Bounding Box + - Trajectory + + + +A set of example notebooks can also be found in the Examples page along with some examples of integration with other earthkit libraries. + +A Data Portfolio containing information on what data we provide is also available. diff --git a/docs/Service/Quick_Start.md b/docs/Service/Quick_Start.md new file mode 100644 index 000000000..b79bff87a --- /dev/null +++ b/docs/Service/Quick_Start.md @@ -0,0 +1,72 @@ +# Quick Start + +Once a user has installed earthkit-data and has their credentials in place, you can make a simple request. + +An example of a time-series requested via earthkit-data: + +```python +import earthkit.data + +request = { + "class": "od", + "stream" : "enfo", + "type" : "pf", + "date" : -1, # Note: date must be within the last two days + "time" : "0000", + "levtype" : "sfc", + "expver" : "0001", + "domain" : "g", + "param" : "164/167/169", + "number" : "1/to/50", + "feature" : { + "type" : "timeseries", + "points": [[-9.10, 38.78]], + "axes": "step", + "range" : { + "start" : 0, + "end" : 360, + } + }, + "format": "covjson", +} + +ds = earthkit.data.from_source("polytope", "ecmwf-mars", request, stream=False, address='polytope.ecmwf.int') +``` +The following will return a timeseries starting yesterday at midnight with steps from `0` to `360` including all steps available in between, for the parameters `164/167/169` at the point given. This data will be returned for each ensemble number requested. + +`"polytope"` refers to the underlying service being used to return the data. `"ecmwf-mars"` is the dataset we are looking to retrieve from. Setting `stream=False` returns all the requested data to us once it is available. `address` points to the endpoint for the polytope server. + +To view the returned covjson run: + +```python +ds._json() +``` + +To convert your covjson into an xarray the following can be done: + +```python +ds.to_xarray() +``` + +The following visualisation can be created using the latest version of earthkit-plots. + +```python +from earthkit.plots.interactive import Chart + +TIME_FREQUENCY = "6h" +QUANTILES = [0, 0.1, 0.25, 0.5, 0.75, 0.9, 1] + +chart = Chart() +chart.title(f"ECMWF ensemble meteogram") +chart.box(ds, time_frequency=TIME_FREQUENCY, quantiles=QUANTILES) +chart.line(ds,aggregation='mean', line_color='grey', time_frequency=TIME_FREQUENCY) +chart.show(renderer="png") # Replace with chart.show() in an interactive session! +``` + +
+

+ Example Meteogram +

+
+ +For more information about each feature see the Features page. \ No newline at end of file diff --git a/docs/images/polytope_feature.png b/docs/images/polytope_feature.png new file mode 100644 index 000000000..4f6cfab6a Binary files /dev/null and b/docs/images/polytope_feature.png differ diff --git a/docs/images/timeseries_example.png b/docs/images/timeseries_example.png new file mode 100644 index 000000000..a57ea904c Binary files /dev/null and b/docs/images/timeseries_example.png differ diff --git a/docs/images/timeseries_qs.png b/docs/images/timeseries_qs.png new file mode 100644 index 000000000..1c95e9b57 Binary files /dev/null and b/docs/images/timeseries_qs.png differ diff --git a/docs/index.md b/docs/index.md index 285475da0..00677abc2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -37,20 +37,33 @@ Note that Polytope reads only the user-requested data, instead of whole fields. !!! important Note that Polytope reads only the user-requested data, instead of whole fields. Importantly, this implies a significant decrease of the I/O usage when reading data from a datacube. +Polytope feature extraction consists of the algorithm itself, and the service that uses the algorithm to extract features from ECMWF datacubes. Details on the service can be found in the Polytope service overview, while details on the feature extraction algorithm can be found on the Polytope algorithm overview. -See Overview for more information. - -To learn more about how to use Polytope, refer to the User Guide. In particular, see the Quickstart page for a step-by-step example of how to use the Polytope software. -For a more in-depth explanation of how Polytope achieves its feature exctraction, refer to the Developer Guide. +To learn more about how to use Polytope, refer to the Quickstart page. In particular, see the Quickstart page for a step-by-step example of how to use the Polytope software. +For a more in-depth explanation of how Polytope achieves its feature extraction, refer to the Developer Guide. !!! Warning This project is BETA and will be experimental for the foreseeable future. Interfaces and functionality are likely to change. DO NOT use this software in any project/software that is operational. +# Index + +### Service + * Overview + * Installation + * Quick Start + * Features + * Examples + +### Algorithm + * Overview + * User Guide + * Developer Guide + # License *Polytope* is available under the open source [Apache License](http://www.apache.org/licenses/LICENSE-2.0). - In applying this licence, ECMWF does not waive the privileges and immunities granted to it by virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction. + In applying this license, ECMWF does not waive the privileges and immunities granted to it by virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction. diff --git a/docs/requirements.txt b/docs/requirements.txt index 710d77926..c3e040380 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ jinja2>=3.1.3 -Markdown \ No newline at end of file +Markdown +mkdocs-jupyter \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 52d84d14d..fd323459c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,19 +11,34 @@ markdown_extensions: # - pymdownx.superfences extra_css: - extra.css -nav: +nav: - Home: index.md - - Overview: - - Overview: Overview/Overview.md - - Polytope at ECMWF: Overview/Polytope_at_ECMWF.md - - User Guide: - - Overview: User_Guide/Overview.md - - Getting Started: User_Guide/Getting_started.md - - Example: User_Guide/Example.md + - Service: + - Overview: Service/Overview.md + - Installation: Service/Installation.md + - Quick Start: Service/Quick_Start.md + - Features: + - Overview: Service/Features/feature.md + - Time Series: Service/Features/timeseries.md + - Vertical Profile: Service/Features/vertical_profile.md + - Polygon: Service/Features/polygon.md + - Bounding Box: Service/Features/boundingbox.md + - Trajectory: Service/Features/trajectory.md + - Examples: Service/Examples/examples.md + - Data Portfolio: Service/Data_Portfolio.md + - Algorithm: + - Overview: Algorithm/Overview/Overview.md + - Polytope at ECMWF: Algorithm/Overview/Polytope_at_ECMWF.md + - User Guide: + - Overview: Algorithm/User_Guide/Overview.md + - Getting Started: Algorithm/User_Guide/Getting_started.md + - Example: Algorithm/User_Guide/Example.md - Developer Guide: - - Overview: Developer_Guide/Overview.md - - Datacube: Developer_Guide/Datacube.md - - Slicer: Developer_Guide/Slicer.md - - APIs: Developer_Guide/API.md - # - API levels: Developer_Guide/API.md - # - Mid-level Shapes: Developer_Guide/shapes.md + - Overview: Algorithm/Developer_Guide/Overview.md + - Datacube: Algorithm/Developer_Guide/Datacube.md + - Slicer: Algorithm/Developer_Guide/Slicer.md + - APIs: + Algorithm/Developer_Guide/API.md +plugins: + - search + - mkdocs-jupyter diff --git a/polytope/utility/exceptions.py b/polytope/utility/exceptions.py deleted file mode 100644 index fb30f627d..000000000 --- a/polytope/utility/exceptions.py +++ /dev/null @@ -1,25 +0,0 @@ -class AxisOverdefinedError(KeyError): - def __init__(self, axis): - self.axis = axis - self.message = ( - f"Axis {axis} is overdefined. You have used it in two or more input polytopes which" - f"cannot form a union (because they span different axes)." - ) - - -class AxisUnderdefinedError(KeyError): - def __init__(self, axis): - self.axis = axis - self.message = f"Axis {axis} is underdefined. It does not appear in any input polytope." - - -class AxisNotFoundError(KeyError): - def __init__(self, axis): - self.axis = axis - self.message = f"Axis {axis} does not exist in the datacube." - - -class UnsliceableShapeError(KeyError): - def __init__(self, axis): - self.axis = axis - self.message = f"Higher-dimensional shape does not support unsliceable axis {axis.name}." diff --git a/polytope/version.py b/polytope/version.py deleted file mode 100644 index 382021f30..000000000 --- a/polytope/version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "1.0.6" diff --git a/polytope/__init__.py b/polytope_feature/__init__.py similarity index 100% rename from polytope/__init__.py rename to polytope_feature/__init__.py diff --git a/polytope/datacube/__init__.py b/polytope_feature/datacube/__init__.py similarity index 100% rename from polytope/datacube/__init__.py rename to polytope_feature/datacube/__init__.py diff --git a/polytope/datacube/backends/__init__.py b/polytope_feature/datacube/backends/__init__.py similarity index 100% rename from polytope/datacube/backends/__init__.py rename to polytope_feature/datacube/backends/__init__.py diff --git a/polytope/datacube/backends/datacube.py b/polytope_feature/datacube/backends/datacube.py similarity index 93% rename from polytope/datacube/backends/datacube.py rename to polytope_feature/datacube/backends/datacube.py index 524bb5298..97a8c7c77 100644 --- a/polytope/datacube/backends/datacube.py +++ b/polytope_feature/datacube/backends/datacube.py @@ -1,6 +1,6 @@ import logging from abc import ABC, abstractmethod -from typing import Any +from typing import Any, Dict from ...utility.combinatorics import validate_axes from ..datacube_axis import DatacubeAxis @@ -31,9 +31,10 @@ def __init__(self, axis_options=None, compressed_axes_options=[]): self.merged_axes = [] self.unwanted_path = {} self.compressed_axes = compressed_axes_options + self.grid_md5_hash = None @abstractmethod - def get(self, requests: TensorIndexTree) -> Any: + def get(self, requests: TensorIndexTree, context: Dict) -> Any: """Return data given a set of request trees""" @property @@ -69,6 +70,7 @@ def _create_axes(self, name, values, transformation_type_key, transformation_opt # TODO: do we use this?? This shouldn't work for a disk in lat/lon on a octahedral or other grid?? for compressed_grid_axis in transformation.compressed_grid_axes: self.compressed_grid_axes.append(compressed_grid_axis) + self.grid_md5_hash = transformation.md5_hash if len(final_axis_names) > 1: self.coupled_axes.append(final_axis_names) for axis in final_axis_names: @@ -130,9 +132,10 @@ def get_indices(self, path: DatacubePath, axis, lower, upper, method=None): """ path = self.fit_path(path) indexes = axis.find_indexes(path, self) + idx_between = axis.find_indices_between(indexes, lower, upper, self, method) - logging.info(f"For axis {axis.name} between {lower} and {upper}, found indices {idx_between}") + logging.debug(f"For axis {axis.name} between {lower} and {upper}, found indices {idx_between}") return idx_between @@ -149,17 +152,19 @@ def remap_path(self, path: DatacubePath): return path @staticmethod - def create(datacube, config={}, axis_options={}, compressed_axes_options=[], alternative_axes=[]): + def create(datacube, config={}, axis_options={}, compressed_axes_options=[], alternative_axes=[], context=None): # TODO: get the configs as None for pre-determined value and change them to empty dictionary inside the function if type(datacube).__name__ == "DataArray": from .xarray import XArrayDatacube - xadatacube = XArrayDatacube(datacube, axis_options, compressed_axes_options) + xadatacube = XArrayDatacube(datacube, axis_options, compressed_axes_options, context) return xadatacube if type(datacube).__name__ == "GribJump": from .fdb import FDBDatacube - fdbdatacube = FDBDatacube(datacube, config, axis_options, compressed_axes_options, alternative_axes) + fdbdatacube = FDBDatacube( + datacube, config, axis_options, compressed_axes_options, alternative_axes, context + ) return fdbdatacube def check_branching_axes(self, request): diff --git a/polytope/datacube/backends/fdb.py b/polytope_feature/datacube/backends/fdb.py similarity index 88% rename from polytope/datacube/backends/fdb.py rename to polytope_feature/datacube/backends/fdb.py index fd10a130b..bd60665af 100644 --- a/polytope/datacube/backends/fdb.py +++ b/polytope_feature/datacube/backends/fdb.py @@ -3,14 +3,19 @@ from copy import deepcopy from itertools import product +from ...utility.exceptions import BadGridError, BadRequestError from ...utility.geometry import nearest_pt from .datacube import Datacube, TensorIndexTree class FDBDatacube(Datacube): - def __init__(self, gj, config=None, axis_options=None, compressed_axes_options=[], alternative_axes=[]): + def __init__( + self, gj, config=None, axis_options=None, compressed_axes_options=[], alternative_axes=[], context=None + ): if config is None: config = {} + if context is None: + context = {} super().__init__(axis_options, compressed_axes_options) @@ -24,12 +29,22 @@ def __init__(self, gj, config=None, axis_options=None, compressed_axes_options=[ self.gj = gj if len(alternative_axes) == 0: - self.fdb_coordinates = self.gj.axes(partial_request) + logging.info("Find GribJump axes for %s", context) + self.fdb_coordinates = self.gj.axes(partial_request, ctx=context) + logging.info("Retrieved available GribJump axes for %s", context) + if len(self.fdb_coordinates) == 0: + raise BadRequestError(partial_request) else: self.fdb_coordinates = {} for axis_config in alternative_axes: self.fdb_coordinates[axis_config.axis_name] = axis_config.values + fdb_coordinates_copy = deepcopy(self.fdb_coordinates) + for axis, vals in fdb_coordinates_copy.items(): + if len(vals) == 1: + if vals[0] == "": + self.fdb_coordinates.pop(axis) + logging.info("Axes returned from GribJump are: " + str(self.fdb_coordinates)) self.fdb_coordinates["values"] = [] @@ -77,8 +92,9 @@ def check_branching_axes(self, request): for axis_name in axes_to_remove: self._axes.pop(axis_name, None) - def get(self, requests: TensorIndexTree): - requests.pprint() + def get(self, requests: TensorIndexTree, context=None): + if context is None: + context = {} if len(requests.children) == 0: return requests fdb_requests = [] @@ -104,12 +120,27 @@ def get(self, requests: TensorIndexTree): uncompressed_request = {} for i, key in enumerate(compressed_request[0].keys()): uncompressed_request[key] = combi[i] - complete_uncompressed_request = (uncompressed_request, compressed_request[1]) + complete_uncompressed_request = (uncompressed_request, compressed_request[1], self.grid_md5_hash) complete_list_complete_uncompressed_requests.append(complete_uncompressed_request) complete_fdb_decoding_info.append(fdb_requests_decoding_info[j]) - logging.debug("The requests we give GribJump are: %s", complete_list_complete_uncompressed_requests) - output_values = self.gj.extract(complete_list_complete_uncompressed_requests) - logging.debug("GribJump outputs: %s", output_values) + + if logging.root.level <= logging.DEBUG: + printed_list_to_gj = complete_list_complete_uncompressed_requests[::1000] + logging.debug("The requests we give GribJump are: %s", printed_list_to_gj) + logging.info("Requests given to GribJump extract for %s", context) + try: + output_values = self.gj.extract(complete_list_complete_uncompressed_requests, context) + except Exception as e: + if "BadValue: Grid hash mismatch" in str(e): + logging.info("Error is: %s", e) + raise BadGridError() + else: + raise e + + logging.info("Requests extracted from GribJump for %s", context) + if logging.root.level <= logging.DEBUG: + printed_output_values = output_values[::1000] + logging.debug("GribJump outputs: %s", printed_output_values) self.assign_fdb_output_to_nodes(output_values, complete_fdb_decoding_info) def get_fdb_requests( @@ -124,7 +155,7 @@ def get_fdb_requests( # First when request node is root, go to its children if requests.axis.name == "root": - logging.info("Looking for data for the tree: %s", [leaf.flatten() for leaf in requests.leaves]) + logging.debug("Looking for data for the tree") for c in requests.children: self.get_fdb_requests(c, fdb_requests, fdb_requests_decoding_info) @@ -161,8 +192,8 @@ def remove_duplicates_in_request_ranges(self, fdb_node_ranges, current_start_idx new_current_start_idx = [] for j, idx in enumerate(sub_lat_idxs): if idx not in seen_indices: - # TODO: need to remove it from the values in the corresponding tree node - # TODO: need to read just the range we give to gj ... DONE? + # NOTE: need to remove it from the values in the corresponding tree node + # NOTE: need to read just the range we give to gj original_fdb_node_range_vals.append(actual_fdb_node[0].values[j]) seen_indices.add(idx) new_current_start_idx.append(idx) @@ -187,8 +218,6 @@ def nearest_lat_lon_search(self, requests): second_ax = requests.children[0].children[0].axis - # TODO: actually, here we should not remap the nearest_pts, we should instead unmap the - # found_latlon_pts and then remap them later once we have compared found_latlon_pts and nearest_pts nearest_pts = [ [lat_val, second_ax._remap_val_to_axis_range(lon_val)] for (lat_val, lon_val) in zip( @@ -325,8 +354,6 @@ def sort_fdb_request_ranges(self, current_start_idx, lat_length, fdb_node_ranges request_ranges_with_idx = list(enumerate(interm_request_ranges)) sorted_list = sorted(request_ranges_with_idx, key=lambda x: x[1][0]) original_indices, sorted_request_ranges = zip(*sorted_list) - logging.debug("We sorted the request ranges into: %s", sorted_request_ranges) - logging.debug("The sorted and unique leaf node ranges are: %s", new_fdb_node_ranges) return (original_indices, sorted_request_ranges, new_fdb_node_ranges) def datacube_natural_indexes(self, axis, subarray): diff --git a/polytope/datacube/backends/mock.py b/polytope_feature/datacube/backends/mock.py similarity index 94% rename from polytope/datacube/backends/mock.py rename to polytope_feature/datacube/backends/mock.py index 1a48360eb..c23b80802 100644 --- a/polytope/datacube/backends/mock.py +++ b/polytope_feature/datacube/backends/mock.py @@ -24,10 +24,12 @@ def __init__(self, dimensions, compressed_axes_options=[]): self.stride[k] = stride_cumulative stride_cumulative *= self.dimensions[k] - def get(self, requests: TensorIndexTree): + def get(self, requests: TensorIndexTree, context=None): # Takes in a datacube and verifies the leaves of the tree are complete # (ie it found values for all datacube axis) + if context is None: + context = {} for r in requests.leaves: path = r.flatten() if len(path.items()) == len(self.dimensions.items()): diff --git a/polytope/datacube/backends/xarray.py b/polytope_feature/datacube/backends/xarray.py similarity index 95% rename from polytope/datacube/backends/xarray.py rename to polytope_feature/datacube/backends/xarray.py index c01ea52bc..735c87e25 100644 --- a/polytope/datacube/backends/xarray.py +++ b/polytope_feature/datacube/backends/xarray.py @@ -9,7 +9,7 @@ class XArrayDatacube(Datacube): """Xarray arrays are labelled, axes can be defined as strings or integers (e.g. "time" or 0).""" - def __init__(self, dataarray: xr.DataArray, axis_options=None, compressed_axes_options=[]): + def __init__(self, dataarray: xr.DataArray, axis_options=None, compressed_axes_options=[], context=None): super().__init__(axis_options, compressed_axes_options) if axis_options is None: axis_options = {} @@ -50,12 +50,14 @@ def __init__(self, dataarray: xr.DataArray, axis_options=None, compressed_axes_o val = self._axes[name].type self._check_and_add_axes(options, name, val) - def get(self, requests, leaf_path=None, axis_counter=0): + def get(self, requests, context=None, leaf_path=None, axis_counter=0): + if context is None: + context = {} if leaf_path is None: leaf_path = {} if requests.axis.name == "root": for c in requests.children: - self.get(c, leaf_path, axis_counter + 1) + self.get(c, context, leaf_path, axis_counter + 1) else: key_value_path = {requests.axis.name: requests.values} ax = requests.axis @@ -66,7 +68,7 @@ def get(self, requests, leaf_path=None, axis_counter=0): if len(requests.children) != 0: # We are not a leaf and we loop over for c in requests.children: - self.get(c, leaf_path, axis_counter + 1) + self.get(c, context, leaf_path, axis_counter + 1) else: if self.axis_counter != axis_counter: requests.remove_branch() diff --git a/polytope/datacube/datacube_axis.py b/polytope_feature/datacube/datacube_axis.py similarity index 100% rename from polytope/datacube/datacube_axis.py rename to polytope_feature/datacube/datacube_axis.py diff --git a/polytope/datacube/index_tree.proto b/polytope_feature/datacube/index_tree.proto similarity index 100% rename from polytope/datacube/index_tree.proto rename to polytope_feature/datacube/index_tree.proto diff --git a/polytope/datacube/index_tree_pb2.py b/polytope_feature/datacube/index_tree_pb2.py similarity index 100% rename from polytope/datacube/index_tree_pb2.py rename to polytope_feature/datacube/index_tree_pb2.py diff --git a/polytope/datacube/tensor_index_tree.py b/polytope_feature/datacube/tensor_index_tree.py similarity index 99% rename from polytope/datacube/tensor_index_tree.py rename to polytope_feature/datacube/tensor_index_tree.py index 5cb813b99..604aa7441 100644 --- a/polytope/datacube/tensor_index_tree.py +++ b/polytope_feature/datacube/tensor_index_tree.py @@ -105,6 +105,7 @@ def add_child(self, node): def add_value(self, value): new_values = list(self.values) new_values.append(value) + new_values.sort() self.values = tuple(new_values) def create_child(self, axis, value, next_nodes): diff --git a/polytope/datacube/transformations/__init__.py b/polytope_feature/datacube/transformations/__init__.py similarity index 100% rename from polytope/datacube/transformations/__init__.py rename to polytope_feature/datacube/transformations/__init__.py diff --git a/polytope/datacube/transformations/datacube_cyclic/__init__.py b/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py similarity index 100% rename from polytope/datacube/transformations/datacube_cyclic/__init__.py rename to polytope_feature/datacube/transformations/datacube_cyclic/__init__.py diff --git a/polytope/datacube/transformations/datacube_cyclic/datacube_cyclic.py b/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py similarity index 99% rename from polytope/datacube/transformations/datacube_cyclic/datacube_cyclic.py rename to polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py index b7e402eae..3373dd082 100644 --- a/polytope/datacube/transformations/datacube_cyclic/datacube_cyclic.py +++ b/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py @@ -1,7 +1,7 @@ import math from copy import deepcopy -from ....utility.combinatorics import unique +from ....utility.list_tools import unique from ..datacube_transformations import DatacubeAxisTransformation diff --git a/polytope/datacube/transformations/datacube_mappers/__init__.py b/polytope_feature/datacube/transformations/datacube_mappers/__init__.py similarity index 100% rename from polytope/datacube/transformations/datacube_mappers/__init__.py rename to polytope_feature/datacube/transformations/datacube_mappers/__init__.py diff --git a/polytope/datacube/transformations/datacube_mappers/datacube_mappers.py b/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py similarity index 87% rename from polytope/datacube/transformations/datacube_mappers/datacube_mappers.py rename to polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py index f79d43de3..e28566e69 100644 --- a/polytope/datacube/transformations/datacube_mappers/datacube_mappers.py +++ b/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py @@ -13,19 +13,32 @@ def __init__(self, name, mapper_options): self.grid_resolution = mapper_options.resolution self.grid_axes = mapper_options.axes self.local_area = [] + self.md5_hash = None + if mapper_options.md5_hash is not None: + self.md5_hash = mapper_options.md5_hash if mapper_options.local is not None: self.local_area = mapper_options.local + self._axis_reversed = None + if mapper_options.axis_reversed is not None: + self._axis_reversed = mapper_options.axis_reversed self.old_axis = name self._final_transformation = self.generate_final_transformation() self._final_mapped_axes = self._final_transformation._mapped_axes self._axis_reversed = self._final_transformation._axis_reversed self.compressed_grid_axes = self._final_transformation.compressed_grid_axes + self.md5_hash = self._final_transformation.md5_hash def generate_final_transformation(self): map_type = _type_to_datacube_mapper_lookup[self.grid_type] - module = import_module("polytope.datacube.transformations.datacube_mappers.mapper_types." + self.grid_type) + module = import_module( + "polytope_feature.datacube.transformations.datacube_mappers.mapper_types." + self.grid_type + ) constructor = getattr(module, map_type) - transformation = deepcopy(constructor(self.old_axis, self.grid_axes, self.grid_resolution, self.local_area)) + transformation = deepcopy( + constructor( + self.old_axis, self.grid_axes, self.grid_resolution, self.md5_hash, self.local_area, self._axis_reversed + ) + ) return transformation def blocked_axes(self): diff --git a/polytope/datacube/transformations/datacube_mappers/mapper_types/__init__.py b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py similarity index 100% rename from polytope/datacube/transformations/datacube_mappers/mapper_types/__init__.py rename to polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py diff --git a/polytope/datacube/transformations/datacube_mappers/mapper_types/healpix.py b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py similarity index 88% rename from polytope/datacube/transformations/datacube_mappers/mapper_types/healpix.py rename to polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py index b198a157a..6fdf9d54a 100644 --- a/polytope/datacube/transformations/datacube_mappers/mapper_types/healpix.py +++ b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py @@ -5,7 +5,7 @@ class HealpixGridMapper(DatacubeMapper): - def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): + def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None): # TODO: if local area is not empty list, raise NotImplemented self._mapped_axes = mapped_axes self._base_axis = base_axis @@ -13,6 +13,14 @@ def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False} self._first_axis_vals = self.first_axis_vals() self.compressed_grid_axes = [self._mapped_axes[1]] + if md5_hash is not None: + self.md5_hash = md5_hash + else: + self.md5_hash = _md5_hash.get(resolution, None) + if self._axis_reversed[mapped_axes[1]]: + raise NotImplementedError("Healpix grid with second axis in decreasing order is not supported") + if not self._axis_reversed[mapped_axes[0]]: + raise NotImplementedError("Healpix grid with first axis in increasing order is not supported") def first_axis_vals(self): rad2deg = 180 / math.pi @@ -133,3 +141,7 @@ def unmap(self, first_val, second_val): second_idx = self.second_axis_vals(first_val).index(second_val) healpix_index = self.axes_idx_to_healpix_idx(first_idx, second_idx) return healpix_index + + +# md5 grid hash in form {resolution : hash} +_md5_hash = {} diff --git a/polytope/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py similarity index 92% rename from polytope/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py rename to polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py index abbd01392..c71f59f5b 100644 --- a/polytope/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +++ b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py @@ -5,7 +5,7 @@ class NestedHealpixGridMapper(DatacubeMapper): - def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): + def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None): # TODO: if local area is not empty list, raise NotImplemented self._mapped_axes = mapped_axes self._base_axis = base_axis @@ -17,6 +17,14 @@ def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): self.k = int(math.log2(self.Nside)) self.Npix = 12 * self.Nside * self.Nside self.Ncap = (self.Nside * (self.Nside - 1)) << 1 + if md5_hash is not None: + self.md5_hash = md5_hash + else: + self.md5_hash = _md5_hash.get(resolution, None) + if self._axis_reversed[mapped_axes[1]]: + raise NotImplementedError("Healpix grid with second axis in decreasing order is not supported") + if not self._axis_reversed[mapped_axes[0]]: + raise NotImplementedError("Healpix grid with first axis in increasing order is not supported") def first_axis_vals(self): rad2deg = 180 / math.pi @@ -211,3 +219,7 @@ def ring_to_nested(self, idx): def int_sqrt(self, i): return int(math.sqrt(i + 0.5)) + + +# md5 grid hash in form {resolution : hash} +_md5_hash = {} diff --git a/polytope/datacube/transformations/datacube_mappers/mapper_types/local_regular.py b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py similarity index 69% rename from polytope/datacube/transformations/datacube_mappers/mapper_types/local_regular.py rename to polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py index 40f86bfbd..df611250e 100644 --- a/polytope/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +++ b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py @@ -4,7 +4,7 @@ class LocalRegularGridMapper(DatacubeMapper): - def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): + def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None): # TODO: if local area is not empty list, raise NotImplemented self._mapped_axes = mapped_axes self._base_axis = base_axis @@ -15,17 +15,38 @@ def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): if not isinstance(resolution, list): self.first_resolution = resolution self.second_resolution = resolution + if md5_hash is not None: + self.md5_hash = md5_hash + else: + self.md5_hash = _md5_hash.get(resolution, None) else: self.first_resolution = resolution[0] self.second_resolution = resolution[1] + if md5_hash is not None: + self.md5_hash = md5_hash + else: + self.md5_hash = _md5_hash.get(tuple(resolution), None) self._first_deg_increment = (local_area[1] - local_area[0]) / self.first_resolution self._second_deg_increment = (local_area[3] - local_area[2]) / self.second_resolution - self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False} + if axis_reversed is None: + self._axis_reversed = {mapped_axes[0]: False, mapped_axes[1]: False} + else: + assert set(axis_reversed.keys()) == set(mapped_axes) + self._axis_reversed = axis_reversed self._first_axis_vals = self.first_axis_vals() self.compressed_grid_axes = [self._mapped_axes[1]] + if self._axis_reversed[mapped_axes[1]]: + raise NotImplementedError("Local regular grid with second axis in decreasing order is not supported") def first_axis_vals(self): - first_ax_vals = [self._first_axis_max - i * self._first_deg_increment for i in range(self.first_resolution + 1)] + if self._axis_reversed[self._mapped_axes[0]]: + first_ax_vals = [ + self._first_axis_max - i * self._first_deg_increment for i in range(self.first_resolution + 1) + ] + else: + first_ax_vals = [ + self._first_axis_min + i * self._first_deg_increment for i in range(self.first_resolution + 1) + ] return first_ax_vals def map_first_axis(self, lower, upper): @@ -68,3 +89,7 @@ def unmap(self, first_val, second_val): second_idx = self.second_axis_vals(first_val).index(second_val) final_index = self.axes_idx_to_regular_idx(first_idx, second_idx) return final_index + + +# md5 grid hash in form {resolution : hash} +_md5_hash = {} diff --git a/polytope/datacube/transformations/datacube_mappers/mapper_types/octahedral.py b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py similarity index 99% rename from polytope/datacube/transformations/datacube_mappers/mapper_types/octahedral.py rename to polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py index f48fca712..51c378786 100644 --- a/polytope/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +++ b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py @@ -5,7 +5,7 @@ class OctahedralGridMapper(DatacubeMapper): - def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): + def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None): # TODO: if local area is not empty list, raise NotImplemented self._mapped_axes = mapped_axes self._base_axis = base_axis @@ -14,7 +14,15 @@ def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): self._first_idx_map = self.create_first_idx_map() self._second_axis_spacing = {} self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False} + if self._axis_reversed[mapped_axes[1]]: + raise NotImplementedError("Octahedral grid with second axis in decreasing order is not supported") + if not self._axis_reversed[mapped_axes[0]]: + raise NotImplementedError("Octahedral grid with first axis in increasing order is not supported") self.compressed_grid_axes = [self._mapped_axes[1]] + if md5_hash is not None: + self.md5_hash = md5_hash + else: + self.md5_hash = _md5_hash.get(resolution, None) def gauss_first_guess(self): i = 0 @@ -2750,3 +2758,9 @@ def unmap(self, first_val, second_val): (first_idx, second_idx) = self.find_second_axis_idx(first_val, second_val) octahedral_index = self.axes_idx_to_octahedral_idx(first_idx, second_idx) return octahedral_index + + +# md5 grid hash in form {resolution : hash} +_md5_hash = { + 1280: "158db321ae8e773681eeb40e0a3d350f", +} diff --git a/polytope/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py similarity index 98% rename from polytope/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py rename to polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py index 5a76f5d10..391e1bbe0 100644 --- a/polytope/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +++ b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py @@ -4,7 +4,7 @@ class ReducedLatLonMapper(DatacubeMapper): - def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): + def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None): # TODO: if local area is not empty list, raise NotImplemented self._mapped_axes = mapped_axes self._base_axis = base_axis @@ -12,6 +12,14 @@ def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): self._axis_reversed = {mapped_axes[0]: False, mapped_axes[1]: False} self._first_axis_vals = self.first_axis_vals() self.compressed_grid_axes = [self._mapped_axes[1]] + if md5_hash is not None: + self.md5_hash = md5_hash + else: + self.md5_hash = _md5_hash.get(resolution, None) + if self._axis_reversed[mapped_axes[1]]: + raise NotImplementedError("Reduced lat-lon grid with second axis in decreasing order is not supported") + if self._axis_reversed[mapped_axes[0]]: + raise NotImplementedError("Reduced lat-lon grid with first axis in decreasing order is not supported") def first_axis_vals(self): resolution = 180 / (self._resolution - 1) @@ -1504,3 +1512,7 @@ def unmap(self, first_val, second_val): second_idx = self.second_axis_vals(first_val).index(second_val) reduced_ll_index = self.axes_idx_to_reduced_ll_idx(first_idx, second_idx) return reduced_ll_index + + +# md5 grid hash in form {resolution : hash} +_md5_hash = {} diff --git a/polytope/datacube/transformations/datacube_mappers/mapper_types/regular.py b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py similarity index 70% rename from polytope/datacube/transformations/datacube_mappers/mapper_types/regular.py rename to polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py index 3b40f77e4..f4dfb6486 100644 --- a/polytope/datacube/transformations/datacube_mappers/mapper_types/regular.py +++ b/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py @@ -4,18 +4,31 @@ class RegularGridMapper(DatacubeMapper): - def __init__(self, base_axis, mapped_axes, resolution, local_area=[]): + def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None): # TODO: if local area is not empty list, raise NotImplemented self._mapped_axes = mapped_axes self._base_axis = base_axis self._resolution = resolution self.deg_increment = 90 / self._resolution - self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False} + if axis_reversed is None: + self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False} + else: + assert set(axis_reversed.keys()) == set(mapped_axes) + self._axis_reversed = axis_reversed self._first_axis_vals = self.first_axis_vals() self.compressed_grid_axes = [self._mapped_axes[1]] + if md5_hash is not None: + self.md5_hash = md5_hash + else: + self.md5_hash = _md5_hash.get(resolution, None) + if self._axis_reversed[mapped_axes[1]]: + raise NotImplementedError("Regular grid with second axis in decreasing order is not supported") def first_axis_vals(self): - first_ax_vals = [90 - i * self.deg_increment for i in range(2 * self._resolution)] + if self._axis_reversed[self._mapped_axes[0]]: + first_ax_vals = [90 - i * self.deg_increment for i in range(2 * self._resolution)] + else: + first_ax_vals = [-90 + i * self.deg_increment for i in range(2 * self._resolution)] return first_ax_vals def map_first_axis(self, lower, upper): @@ -56,3 +69,7 @@ def unmap(self, first_val, second_val): second_idx = self.second_axis_vals(first_val).index(second_val) final_index = self.axes_idx_to_regular_idx(first_idx, second_idx) return final_index + + +# md5 grid hash in form {resolution : hash} +_md5_hash = {} diff --git a/polytope/datacube/transformations/datacube_merger/__init__.py b/polytope_feature/datacube/transformations/datacube_merger/__init__.py similarity index 100% rename from polytope/datacube/transformations/datacube_merger/__init__.py rename to polytope_feature/datacube/transformations/datacube_merger/__init__.py diff --git a/polytope/datacube/transformations/datacube_merger/datacube_merger.py b/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py similarity index 97% rename from polytope/datacube/transformations/datacube_merger/datacube_merger.py rename to polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py index 717237a33..5b0516638 100644 --- a/polytope/datacube/transformations/datacube_merger/datacube_merger.py +++ b/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py @@ -93,6 +93,5 @@ def unmap_tree_node(self, node, unwanted_path): if node.axis.name == self._first_axis: (new_first_vals, new_second_vals) = self.unmerge(node.values) node.values = new_first_vals - # TODO: actually need to give the second axis of the transformation to get the interm axis interm_node = node.add_node_layer_after(self._second_axis, new_second_vals) return (interm_node, unwanted_path) diff --git a/polytope/datacube/transformations/datacube_reverse/__init__.py b/polytope_feature/datacube/transformations/datacube_reverse/__init__.py similarity index 100% rename from polytope/datacube/transformations/datacube_reverse/__init__.py rename to polytope_feature/datacube/transformations/datacube_reverse/__init__.py diff --git a/polytope/datacube/transformations/datacube_reverse/datacube_reverse.py b/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py similarity index 100% rename from polytope/datacube/transformations/datacube_reverse/datacube_reverse.py rename to polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py diff --git a/polytope/datacube/transformations/datacube_transformations.py b/polytope_feature/datacube/transformations/datacube_transformations.py similarity index 94% rename from polytope/datacube/transformations/datacube_transformations.py rename to polytope_feature/datacube/transformations/datacube_transformations.py index 34af22550..f174bf1aa 100644 --- a/polytope/datacube/transformations/datacube_transformations.py +++ b/polytope_feature/datacube/transformations/datacube_transformations.py @@ -12,9 +12,8 @@ def create_transform(name, transformation_type_key, transformation_options): transformation_type = _type_to_datacube_transformation_lookup[transformation_type_key] transformation_file_name = _type_to_transformation_file_lookup[transformation_type_key] file_name = ".datacube_" + transformation_file_name - module = import_module("polytope.datacube.transformations" + file_name + file_name) + module = import_module("polytope_feature.datacube.transformations" + file_name + file_name) constructor = getattr(module, transformation_type) - # transformation_type_option = transformation_options[transformation_type_key] transformation_type_option = transformation_options new_transformation = deepcopy(constructor(name, transformation_type_option)) diff --git a/polytope/datacube/transformations/datacube_type_change/__init__.py b/polytope_feature/datacube/transformations/datacube_type_change/__init__.py similarity index 100% rename from polytope/datacube/transformations/datacube_type_change/__init__.py rename to polytope_feature/datacube/transformations/datacube_type_change/__init__.py diff --git a/polytope/datacube/transformations/datacube_type_change/datacube_type_change.py b/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py similarity index 95% rename from polytope/datacube/transformations/datacube_type_change/datacube_type_change.py rename to polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py index 5fb88ece1..e9fa287db 100644 --- a/polytope/datacube/transformations/datacube_type_change/datacube_type_change.py +++ b/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py @@ -15,7 +15,7 @@ def __init__(self, name, type_options): def generate_final_transformation(self): map_type = _type_to_datacube_type_change_lookup[self.new_type] - module = import_module("polytope.datacube.transformations.datacube_type_change.datacube_type_change") + module = import_module("polytope_feature.datacube.transformations.datacube_type_change.datacube_type_change") constructor = getattr(module, map_type) transformation = deepcopy(constructor(self.name, self.new_type)) return transformation diff --git a/polytope/datacube/tree_encoding.py b/polytope_feature/datacube/tree_encoding.py similarity index 100% rename from polytope/datacube/tree_encoding.py rename to polytope_feature/datacube/tree_encoding.py diff --git a/polytope/engine/__init__.py b/polytope_feature/engine/__init__.py similarity index 100% rename from polytope/engine/__init__.py rename to polytope_feature/engine/__init__.py diff --git a/polytope/engine/engine.py b/polytope_feature/engine/engine.py similarity index 100% rename from polytope/engine/engine.py rename to polytope_feature/engine/engine.py diff --git a/polytope/engine/hullslicer.py b/polytope_feature/engine/hullslicer.py similarity index 84% rename from polytope/engine/hullslicer.py rename to polytope_feature/engine/hullslicer.py index 767f89161..9a99682a6 100644 --- a/polytope/engine/hullslicer.py +++ b/polytope_feature/engine/hullslicer.py @@ -9,9 +9,10 @@ from ..datacube.datacube_axis import UnsliceableDatacubeAxis from ..datacube.tensor_index_tree import TensorIndexTree from ..shapes import ConvexPolytope -from ..utility.combinatorics import argmax, argmin, group, tensor_product, unique +from ..utility.combinatorics import group, tensor_product from ..utility.exceptions import UnsliceableShapeError from ..utility.geometry import lerp +from ..utility.list_tools import argmax, argmin, unique from .engine import Engine @@ -110,7 +111,7 @@ def remap_values(self, ax, value): def _build_sliceable_child(self, polytope, ax, node, datacube, values, next_nodes, slice_axis_idx): for i, value in enumerate(values): - if i == 0: + if i == 0 or ax.name not in self.compressed_axes: fvalue = ax.to_float(value) new_polytope = slice(polytope, ax.name, fvalue, slice_axis_idx) remapped_val = self.remap_values(ax, value) @@ -121,19 +122,8 @@ def _build_sliceable_child(self, polytope, ax, node, datacube, values, next_node child["unsliced_polytopes"].add(new_polytope) next_nodes.append(child) else: - if ax.name not in self.compressed_axes: - fvalue = ax.to_float(value) - new_polytope = slice(polytope, ax.name, fvalue, slice_axis_idx) - remapped_val = self.remap_values(ax, value) - (child, next_nodes) = node.create_child(ax, remapped_val, next_nodes) - child["unsliced_polytopes"] = copy(node["unsliced_polytopes"]) - child["unsliced_polytopes"].remove(polytope) - if new_polytope is not None: - child["unsliced_polytopes"].add(new_polytope) - next_nodes.append(child) - else: - remapped_val = self.remap_values(ax, value) - child.add_value(remapped_val) + remapped_val = self.remap_values(ax, value) + child.add_value(remapped_val) def _build_branch(self, ax, node, datacube, next_nodes): if ax.name not in self.compressed_axes: @@ -142,26 +132,23 @@ def _build_branch(self, ax, node, datacube, next_nodes): for polytope in node["unsliced_polytopes"]: if ax.name in polytope._axes: right_unsliced_polytopes.append(polytope) - # for polytope in node["unsliced_polytopes"]: for i, polytope in enumerate(right_unsliced_polytopes): node._parent = parent_node - # if ax.name in polytope._axes: - if True: - lower, upper, slice_axis_idx = polytope.extents(ax.name) - # here, first check if the axis is an unsliceable axis and directly build node if it is - # NOTE: we should have already created the ax_is_unsliceable cache before - if self.ax_is_unsliceable[ax.name]: - self._build_unsliceable_child(polytope, ax, node, datacube, [lower], next_nodes, slice_axis_idx) - else: - values = self.find_values_between(polytope, ax, node, datacube, lower, upper) - # NOTE: need to only remove the branches if the values are empty, - # but only if there are no other possible children left in the tree that - # we can append and if somehow this happens before and we need to remove, then what do we do?? - if i == len(right_unsliced_polytopes) - 1: - # we have iterated all polytopes and we can now remove the node if we need to - if len(values) == 0 and len(node.children) == 0: - node.remove_branch() - self._build_sliceable_child(polytope, ax, node, datacube, values, next_nodes, slice_axis_idx) + lower, upper, slice_axis_idx = polytope.extents(ax.name) + # here, first check if the axis is an unsliceable axis and directly build node if it is + # NOTE: we should have already created the ax_is_unsliceable cache before + if self.ax_is_unsliceable[ax.name]: + self._build_unsliceable_child(polytope, ax, node, datacube, [lower], next_nodes, slice_axis_idx) + else: + values = self.find_values_between(polytope, ax, node, datacube, lower, upper) + # NOTE: need to only remove the branches if the values are empty, + # but only if there are no other possible children left in the tree that + # we can append and if somehow this happens before and we need to remove, then what do we do?? + if i == len(right_unsliced_polytopes) - 1: + # we have iterated all polytopes and we can now remove the node if we need to + if len(values) == 0 and len(node.children) == 0: + node.remove_branch() + self._build_sliceable_child(polytope, ax, node, datacube, values, next_nodes, slice_axis_idx) else: all_values = [] all_lowers = [] @@ -215,7 +202,6 @@ def remove_compressed_axis_in_union(self, polytopes): for p in polytopes: if p.is_in_union: for axis in p.axes(): - # if axis in self.compressed_axes: if axis == self.compressed_axes[-1]: self.compressed_axes.remove(axis) diff --git a/polytope/options.py b/polytope_feature/options.py similarity index 87% rename from polytope/options.py rename to polytope_feature/options.py index 6baf8bc73..2e88c9679 100644 --- a/polytope/options.py +++ b/polytope_feature/options.py @@ -1,8 +1,7 @@ -import argparse from abc import ABC from typing import Dict, List, Literal, Optional, Union -from conflator import ConfigModel, Conflator +from conflator import ConfigModel from pydantic import ConfigDict @@ -21,7 +20,9 @@ class MapperConfig(TransformationConfig): type: str = "" resolution: Union[int, List[int]] = 0 axes: List[str] = [""] + md5_hash: Optional[str] = None local: Optional[List[float]] = None + axis_reversed: Optional[Dict[str, bool]] = None class ReverseConfig(TransformationConfig): @@ -66,9 +67,7 @@ class Config(ConfigModel): class PolytopeOptions(ABC): @staticmethod def get_polytope_options(options): - parser = argparse.ArgumentParser(allow_abbrev=False) - conflator = Conflator(app_name="polytope", model=Config, cli=False, argparser=parser, **options) - config_options = conflator.load() + config_options = Config.model_validate(options) axis_config = config_options.axis_config compressed_axes_config = config_options.compressed_axes_config diff --git a/polytope/polytope.py b/polytope_feature/polytope.py similarity index 79% rename from polytope/polytope.py rename to polytope_feature/polytope.py index 271725471..f18f10c4d 100644 --- a/polytope/polytope.py +++ b/polytope_feature/polytope.py @@ -1,3 +1,4 @@ +import logging from typing import List from .options import PolytopeOptions @@ -38,7 +39,7 @@ def __repr__(self): class Polytope: - def __init__(self, datacube, engine=None, options=None): + def __init__(self, datacube, engine=None, options=None, context=None): from .datacube import Datacube from .engine import Engine @@ -47,7 +48,11 @@ def __init__(self, datacube, engine=None, options=None): axis_options, compressed_axes_options, config, alternative_axes = PolytopeOptions.get_polytope_options(options) - self.datacube = Datacube.create(datacube, config, axis_options, compressed_axes_options, alternative_axes) + self.context = context + + self.datacube = Datacube.create( + datacube, config, axis_options, compressed_axes_options, alternative_axes, self.context + ) self.engine = engine if engine is not None else Engine.default() self.time = 0 @@ -57,7 +62,10 @@ def slice(self, polytopes: List[ConvexPolytope]): def retrieve(self, request: Request, method="standard"): """Higher-level API which takes a request and uses it to slice the datacube""" + logging.info("Starting request for %s ", self.context) self.datacube.check_branching_axes(request) request_tree = self.engine.extract(self.datacube, request.polytopes()) - self.datacube.get(request_tree) + logging.info("Created request tree for %s ", self.context) + self.datacube.get(request_tree, self.context) + logging.info("Retrieved data for %s ", self.context) return request_tree diff --git a/polytope/shapes.py b/polytope_feature/shapes.py similarity index 97% rename from polytope/shapes.py rename to polytope_feature/shapes.py index c3a45a6a7..8d6744d9b 100644 --- a/polytope/shapes.py +++ b/polytope_feature/shapes.py @@ -1,10 +1,13 @@ import copy import math +import warnings from abc import ABC, abstractmethod from typing import List import tripy +from .utility.list_tools import unique + """ Shapes used for the constructive geometry API of Polytope """ @@ -26,7 +29,7 @@ class ConvexPolytope(Shape): def __init__(self, axes, points, method=None, is_orthogonal=False): self._axes = list(axes) self.is_flat = False - if len(self._axes) == 1: + if len(self._axes) == 1 and len(points) == 1: self.is_flat = True self.points = points self.method = method @@ -60,11 +63,13 @@ def polytope(self): # This is the only shape which can slice on axes without a discretizer or interpolator class Select(Shape): - """Matches several discrete value""" + """Matches several discrete values""" def __init__(self, axis, values, method=None): self.axis = axis - self.values = values + self.values = unique(values) + if len(self.values) != len(values): + warnings.warn("Duplicate request values were removed") self.method = method def axes(self): diff --git a/polytope/utility/__init__.py b/polytope_feature/utility/__init__.py similarity index 100% rename from polytope/utility/__init__.py rename to polytope_feature/utility/__init__.py diff --git a/polytope/utility/combinatorics.py b/polytope_feature/utility/combinatorics.py similarity index 83% rename from polytope/utility/combinatorics.py rename to polytope_feature/utility/combinatorics.py index 629ebbec8..4040c40db 100644 --- a/polytope/utility/combinatorics.py +++ b/polytope_feature/utility/combinatorics.py @@ -46,19 +46,3 @@ def validate_axes(actual_axes, test_axes): raise AxisNotFoundError(ax) return True - - -def unique(points): - points.sort() - points = [k for k, _ in itertools.groupby(points)] - return points - - -def argmin(points): - amin = min(range(len(points)), key=points.__getitem__) - return amin - - -def argmax(points): - amax = max(range(len(points)), key=points.__getitem__) - return amax diff --git a/polytope_feature/utility/exceptions.py b/polytope_feature/utility/exceptions.py new file mode 100644 index 000000000..205b4688a --- /dev/null +++ b/polytope_feature/utility/exceptions.py @@ -0,0 +1,40 @@ +class PolytopeError(Exception): + pass + + +class BadRequestError(PolytopeError): + def __init__(self, pre_path): + self.pre_path = pre_path + self.message = f"No data for {pre_path} is available on the FDB." + + +class AxisOverdefinedError(PolytopeError, KeyError): + def __init__(self, axis): + self.axis = axis + self.message = ( + f"Axis {axis} is overdefined. You have used it in two or more input polytopes which" + f" cannot form a union (because they span different axes)." + ) + + +class AxisUnderdefinedError(PolytopeError, KeyError): + def __init__(self, axis): + self.axis = axis + self.message = f"Axis {axis} is underdefined. It does not appear in any input polytope." + + +class AxisNotFoundError(PolytopeError, KeyError): + def __init__(self, axis): + self.axis = axis + self.message = f"Axis {axis} does not exist in the datacube." + + +class UnsliceableShapeError(PolytopeError, KeyError): + def __init__(self, axis): + self.axis = axis + self.message = f"Higher-dimensional shape does not support unsliceable axis {axis.name}." + + +class BadGridError(PolytopeError, ValueError): + def __init__(self): + self.message = "Data on this grid is not supported by Polytope." diff --git a/polytope/utility/geometry.py b/polytope_feature/utility/geometry.py similarity index 100% rename from polytope/utility/geometry.py rename to polytope_feature/utility/geometry.py diff --git a/polytope/utility/list_tools.py b/polytope_feature/utility/list_tools.py similarity index 57% rename from polytope/utility/list_tools.py rename to polytope_feature/utility/list_tools.py index 2d18917c1..51b8c7fec 100644 --- a/polytope/utility/list_tools.py +++ b/polytope_feature/utility/list_tools.py @@ -1,3 +1,6 @@ +import itertools + + def bisect_left_cmp(arr, val, cmp): left = -1 r = len(arr) @@ -20,3 +23,19 @@ def bisect_right_cmp(arr, val, cmp): else: r = e return r + + +def unique(points): + points.sort() + points = [k for k, _ in itertools.groupby(points)] + return points + + +def argmin(points): + amin = min(range(len(points)), key=points.__getitem__) + return amin + + +def argmax(points): + amax = max(range(len(points)), key=points.__getitem__) + return amax diff --git a/polytope/utility/profiling.py b/polytope_feature/utility/profiling.py similarity index 100% rename from polytope/utility/profiling.py rename to polytope_feature/utility/profiling.py diff --git a/polytope_feature/version.py b/polytope_feature/version.py new file mode 100644 index 000000000..f871089b4 --- /dev/null +++ b/polytope_feature/version.py @@ -0,0 +1 @@ +__version__ = "1.0.15" diff --git a/readme.md b/readme.md index a9142841b..4a6eed368 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,7 @@ Polytope supports datacubes which have branching, non-uniform indexing, and even Polytope is designed to enable extraction of arbitrary extraction of data from a datacube. Instead of the typical range-based bounding-box approach, Polytope can extract any shape of data from a datacube using a "polytope" (*n*-dimensional polygon) stencil.

- Polytope Concept + Polytope Concept

The Polytope algorithm can for example be used to extract: diff --git a/serializedTree b/serializedTree deleted file mode 100644 index 4bb27cb8d..000000000 Binary files a/serializedTree and /dev/null differ diff --git a/setup.py b/setup.py index 005e76dfd..06c058056 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ __version__ = re.search( r'__version__\s*=\s*[\'"]([^\'"]*)[\'"]', - io.open("polytope/version.py", encoding="utf_8_sig").read(), + io.open("polytope_feature/version.py", encoding="utf_8_sig").read(), ).group(1) with open("requirements.txt") as f: diff --git a/tests/requirements_test.txt b/tests/requirements_test.txt index 0df9637b0..e48f1da1a 100644 --- a/tests/requirements_test.txt +++ b/tests/requirements_test.txt @@ -4,5 +4,4 @@ cffi eccodes h5netcdf h5py -earthkit earthkit-data \ No newline at end of file diff --git a/tests/test_axis_mappers.py b/tests/test_axis_mappers.py index c1dc0cd24..b500b9a03 100644 --- a/tests/test_axis_mappers.py +++ b/tests/test_axis_mappers.py @@ -1,13 +1,13 @@ import pandas as pd -from polytope.datacube.datacube_axis import ( +from polytope_feature.datacube.datacube_axis import ( DatacubeAxisCyclic, FloatDatacubeAxis, IntDatacubeAxis, PandasTimedeltaDatacubeAxis, PandasTimestampDatacubeAxis, ) -from polytope.options import PolytopeOptions +from polytope_feature.options import PolytopeOptions class TestAxisMappers: diff --git a/tests/test_bad_request_error.py b/tests/test_bad_request_error.py new file mode 100644 index 000000000..b1154cf9c --- /dev/null +++ b/tests/test_bad_request_error.py @@ -0,0 +1,65 @@ +import pytest + +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope +from polytope_feature.utility.exceptions import BadRequestError + +# import geopandas as gpd +# import matplotlib.pyplot as plt + + +class TestSlicingFDBDatacube: + def setup_method(self, method): + # Create a dataarray with 3 labelled axes using different index types + self.options = { + "axis_config": [ + {"axis_name": "step", "transformations": [{"name": "type_change", "type": "int"}]}, + {"axis_name": "number", "transformations": [{"name": "type_change", "type": "int"}]}, + { + "axis_name": "date", + "transformations": [{"name": "merge", "other_axis": "time", "linkers": ["T", "00"]}], + }, + { + "axis_name": "values", + "transformations": [ + {"name": "mapper", "type": "octahedral", "resolution": 1280, "axes": ["latitude", "longitude"]} + ], + }, + {"axis_name": "latitude", "transformations": [{"name": "reverse", "is_reverse": True}]}, + {"axis_name": "longitude", "transformations": [{"name": "cyclic", "range": [0, 360]}]}, + ], + "compressed_axes_config": [ + "longitude", + "latitude", + "levtype", + "step", + "date", + "domain", + "expver", + "param", + "class", + "stream", + "type", + ], + "pre_path": { + "class": "od", + "expver": "0001", + "levtype": "sfc", + "stream": "oper", + "date": "20230621T120000", + }, + } + + # Testing different shapes + @pytest.mark.fdb + def test_fdb_datacube(self): + import pygribjump as gj + + with pytest.raises(BadRequestError): + self.fdbdatacube = gj.GribJump() + self.slicer = HullSlicer() + self.API = Polytope( + datacube=self.fdbdatacube, + engine=self.slicer, + options=self.options, + ) diff --git a/tests/test_combinatorics.py b/tests/test_combinatorics.py index 980ad8e2c..0e4798720 100644 --- a/tests/test_combinatorics.py +++ b/tests/test_combinatorics.py @@ -1,9 +1,9 @@ import numpy as np import pytest -from polytope import ConvexPolytope -from polytope.utility.combinatorics import group, tensor_product, validate_axes -from polytope.utility.exceptions import ( +from polytope_feature import ConvexPolytope +from polytope_feature.utility.combinatorics import group, tensor_product, validate_axes +from polytope_feature.utility.exceptions import ( AxisNotFoundError, AxisOverdefinedError, AxisUnderdefinedError, diff --git a/tests/test_cyclic_axis_over_negative_vals.py b/tests/test_cyclic_axis_over_negative_vals.py index 24d30b636..37bddd75f 100644 --- a/tests/test_cyclic_axis_over_negative_vals.py +++ b/tests/test_cyclic_axis_over_negative_vals.py @@ -2,9 +2,9 @@ import pandas as pd import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingCyclicAxisNegVals: @@ -40,7 +40,6 @@ def test_cyclic_float_axis_across_seam(self): result.pprint() assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ - (-0.2,), (-1.1,), (-1.0,), (-0.9,), @@ -50,6 +49,7 @@ def test_cyclic_float_axis_across_seam(self): (-0.5,), (-0.4,), (-0.3,), + (-0.2,), ] def test_cyclic_float_axis_inside_cyclic_range(self): @@ -93,31 +93,31 @@ def test_cyclic_float_axis_two_range_loops(self): result.pprint() assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ - (-0.7,), - (-0.6,), - (-0.5,), - (-0.4,), - (-0.3,), - (-0.2,), (-1.1,), - (-1.0,), - (-0.9,), - (-0.8,), - (-0.7,), - (-0.6,), - (-0.5,), - (-0.4,), - (-0.3,), - (-0.2,), (-1.1,), (-1.0,), + (-1.0,), (-0.9,), + (-0.9,), + (-0.8,), (-0.8,), (-0.7,), + (-0.7,), + (-0.7,), + (-0.6,), (-0.6,), + (-0.6,), + (-0.5,), (-0.5,), + (-0.5,), + (-0.4,), + (-0.4,), (-0.4,), (-0.3,), + (-0.3,), + (-0.3,), + (-0.2,), + (-0.2,), ] def test_cyclic_float_axis_below_axis_range(self): @@ -142,6 +142,10 @@ def test_cyclic_float_axis_below_axis_range_crossing_seam(self): # result.pprint() assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ + (-1.0,), + (-0.9,), + (-0.8,), + (-0.7,), (-0.7,), (-0.6,), (-0.5,), @@ -149,8 +153,4 @@ def test_cyclic_float_axis_below_axis_range_crossing_seam(self): (-0.3,), (-0.2,), (-0.1,), - (-1.0,), - (-0.9,), - (-0.8,), - (-0.7,), ] diff --git a/tests/test_cyclic_axis_slicer_not_0.py b/tests/test_cyclic_axis_slicer_not_0.py index 5a117c8e3..84d36d9ce 100644 --- a/tests/test_cyclic_axis_slicer_not_0.py +++ b/tests/test_cyclic_axis_slicer_not_0.py @@ -2,9 +2,9 @@ import pandas as pd import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingCyclicAxisNotOverZero: @@ -44,7 +44,6 @@ def test_cyclic_float_axis_across_seam(self): result.pprint() assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ - (-0.2,), (-1.1,), (-1.0,), (-0.9,), @@ -54,6 +53,7 @@ def test_cyclic_float_axis_across_seam(self): (-0.5,), (-0.4,), (-0.3,), + (-0.2,), ] def test_cyclic_float_axis_inside_cyclic_range(self): @@ -94,31 +94,31 @@ def test_cyclic_float_axis_two_range_loops(self): result = self.API.retrieve(request) assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ - (-0.7,), - (-0.6,), - (-0.5,), - (-0.4,), - (-0.3,), - (-0.2,), (-1.1,), - (-1.0,), - (-0.9,), - (-0.8,), - (-0.7,), - (-0.6,), - (-0.5,), - (-0.4,), - (-0.3,), - (-0.2,), (-1.1,), (-1.0,), + (-1.0,), (-0.9,), + (-0.9,), + (-0.8,), (-0.8,), (-0.7,), + (-0.7,), + (-0.7,), + (-0.6,), (-0.6,), + (-0.6,), + (-0.5,), (-0.5,), + (-0.5,), + (-0.4,), + (-0.4,), (-0.4,), (-0.3,), + (-0.3,), + (-0.3,), + (-0.2,), + (-0.2,), ] def test_cyclic_float_axis_below_axis_range(self): @@ -142,6 +142,10 @@ def test_cyclic_float_axis_below_axis_range_crossing_seam(self): result = self.API.retrieve(request) assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ + (-1.0,), + (-0.9,), + (-0.8,), + (-0.7,), (-0.7,), (-0.6,), (-0.5,), @@ -149,8 +153,4 @@ def test_cyclic_float_axis_below_axis_range_crossing_seam(self): (-0.3,), (-0.2,), (-0.1,), - (-1.0,), - (-0.9,), - (-0.8,), - (-0.7,), ] diff --git a/tests/test_cyclic_axis_slicing.py b/tests/test_cyclic_axis_slicing.py index 2ad154315..3f8d53e8d 100644 --- a/tests/test_cyclic_axis_slicing.py +++ b/tests/test_cyclic_axis_slicing.py @@ -2,9 +2,9 @@ import pandas as pd import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingCyclic: @@ -44,9 +44,6 @@ def test_cyclic_float_axis_across_seam(self): assert len(result.leaves) == 1 result.pprint() assert [(val,) for val in result.leaves[0].values] == [ - (0.8,), - (0.9,), - (1.0,), (0.1,), (0.2,), (0.3,), @@ -54,6 +51,9 @@ def test_cyclic_float_axis_across_seam(self): (0.5,), (0.6,), (0.7,), + (0.8,), + (0.9,), + (1.0,), ] def test_cyclic_float_axis_across_seam_repeated(self): @@ -85,27 +85,27 @@ def test_cyclic_float_axis_across_seam_repeated_twice(self): result.pprint() assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ + (0.0,), (0.0,), (0.1,), - (0.2,), - (0.3,), - (0.4,), - (0.5,), - (0.6,), - (0.7,), - (0.8,), - (0.9,), - (1.0,), (0.1,), (0.2,), + (0.2,), (0.3,), + (0.3,), + (0.4,), (0.4,), (0.5,), + (0.5,), + (0.6,), (0.6,), (0.7,), + (0.7,), + (0.8,), (0.8,), (0.9,), - (0.0,), + (0.9,), + (1.0,), ] def test_cyclic_float_axis_inside_cyclic_range(self): @@ -151,31 +151,31 @@ def test_cyclic_float_axis_two_range_loops(self): # result.pprint() assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ - (0.3,), - (0.4,), - (0.5,), - (0.6,), - (0.7,), - (0.8,), - (0.9,), - (1.0,), + (0.0,), + (0.1,), (0.1,), (0.2,), + (0.2,), + (0.3,), (0.3,), + (0.3,), + (0.4,), (0.4,), + (0.4,), + (0.5,), (0.5,), + (0.5,), + (0.6,), (0.6,), + (0.6,), + (0.7,), (0.7,), + (0.7,), + (0.8,), (0.8,), (0.9,), - (0.0,), - (0.1,), - (0.2,), - (0.3,), - (0.4,), - (0.5,), - (0.6,), - (0.7,), + (0.9,), + (1.0,), ] def test_cyclic_float_axis_below_axis_range(self): @@ -201,6 +201,10 @@ def test_cyclic_float_axis_below_axis_range_crossing_seam(self): # result.pprint() assert len(result.leaves) == 1 assert [(val,) for val in result.leaves[0].values] == [ + (0.0,), + (0.1,), + (0.2,), + (0.3,), (0.3,), (0.4,), (0.5,), @@ -208,10 +212,6 @@ def test_cyclic_float_axis_below_axis_range_crossing_seam(self): (0.7,), (0.8,), (0.9,), - (0.0,), - (0.1,), - (0.2,), - (0.3,), ] def test_cyclic_float_axis_reversed(self): diff --git a/tests/test_cyclic_nearest.py b/tests/test_cyclic_nearest.py index fbf7f2996..86e37c69c 100644 --- a/tests/test_cyclic_nearest.py +++ b/tests/test_cyclic_nearest.py @@ -3,9 +3,9 @@ from eccodes import codes_grib_find_nearest, codes_grib_new_from_file from helper_functions import download_test_data -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Point, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Point, Select class TestRegularGrid: diff --git a/tests/test_cyclic_simple.py b/tests/test_cyclic_simple.py index d12e45413..d9974b7e7 100644 --- a/tests/test_cyclic_simple.py +++ b/tests/test_cyclic_simple.py @@ -2,9 +2,9 @@ import pandas as pd import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicing3DXarrayDatacube: @@ -43,7 +43,7 @@ def test_cyclic_float_axis_across_seam(self): result = self.API.retrieve(request) result.pprint() assert len(result.leaves) == 1 - assert [leaf.values for leaf in result.leaves] == [(0.9, 1.0, 0.1, 0.2)] + assert [leaf.values for leaf in result.leaves] == [(0.1, 0.2, 0.9, 1.0)] def test_cyclic_float_surrounding(self): request = Request( diff --git a/tests/test_cyclic_snapping.py b/tests/test_cyclic_snapping.py index f81c65405..599dc9573 100644 --- a/tests/test_cyclic_snapping.py +++ b/tests/test_cyclic_snapping.py @@ -1,8 +1,8 @@ import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select class TestSlicing3DXarrayDatacube: @@ -30,7 +30,7 @@ def test_cyclic_float_axis_across_seam(self): result = self.API.retrieve(request) result.pprint() assert len(result.leaves) == 1 - assert result.leaves[0].flatten()["long"] == (0.5, 0.0) + assert result.leaves[0].flatten()["long"] == (0.0, 0.5) assert result.leaves[0].result[0] is None - assert result.leaves[0].result[1][0] == 1 - assert result.leaves[0].result[1][1] == 0 + assert result.leaves[0].result[1][0] == 0 + assert result.leaves[0].result[1][1] == 1 diff --git a/tests/test_datacube_axes_init.py b/tests/test_datacube_axes_init.py index c02c44f84..d92a08cf3 100644 --- a/tests/test_datacube_axes_init.py +++ b/tests/test_datacube_axes_init.py @@ -1,11 +1,11 @@ +import earthkit.data as data import pytest -from earthkit import data from helper_functions import download_test_data -from polytope.datacube.datacube_axis import FloatDatacubeAxis -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.datacube.datacube_axis import FloatDatacubeAxis +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestInitDatacubeAxes: @@ -14,7 +14,7 @@ def setup_method(self, method): download_test_data(nexus_url, "foo.grib") ds = data.from_source("file", "./tests/data/foo.grib") - latlon_array = ds.to_xarray().isel(step=0).isel(number=0).isel(surface=0).isel(time=0) + latlon_array = ds.to_xarray(engine="cfgrib").isel(step=0).isel(number=0).isel(surface=0).isel(time=0) latlon_array = latlon_array.t2m self.options = { "axis_config": [ diff --git a/tests/test_datacube_mock.py b/tests/test_datacube_mock.py index 0b147b95c..b941af1d3 100644 --- a/tests/test_datacube_mock.py +++ b/tests/test_datacube_mock.py @@ -1,7 +1,7 @@ import pytest -from polytope.datacube.backends.mock import MockDatacube -from polytope.utility.exceptions import AxisNotFoundError, AxisOverdefinedError +from polytope_feature.datacube.backends.mock import MockDatacube +from polytope_feature.utility.exceptions import AxisNotFoundError, AxisOverdefinedError class TestMockDatacube: diff --git a/tests/test_datacube_xarray.py b/tests/test_datacube_xarray.py index 362937cdd..f15a4f8bc 100644 --- a/tests/test_datacube_xarray.py +++ b/tests/test_datacube_xarray.py @@ -5,14 +5,14 @@ import pytest import xarray as xr -from polytope.datacube import Datacube, DatacubePath -from polytope.datacube.backends.xarray import XArrayDatacube -from polytope.datacube.datacube_axis import ( +from polytope_feature.datacube import Datacube, DatacubePath +from polytope_feature.datacube.backends.xarray import XArrayDatacube +from polytope_feature.datacube.datacube_axis import ( FloatDatacubeAxis, IntDatacubeAxis, PandasTimestampDatacubeAxis, ) -from polytope.utility.exceptions import AxisNotFoundError, AxisOverdefinedError +from polytope_feature.utility.exceptions import AxisNotFoundError, AxisOverdefinedError class TestXarrayDatacube: diff --git a/tests/test_ecmwf_oper_data_fdb.py b/tests/test_ecmwf_oper_data_fdb.py index c08674638..e848716c8 100644 --- a/tests/test_ecmwf_oper_data_fdb.py +++ b/tests/test_ecmwf_oper_data_fdb.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Point, Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Point, Select, Span class TestSlicingFDBDatacube: diff --git a/tests/test_engine_slicer.py b/tests/test_engine_slicer.py index 5e79479e3..3392ccd3a 100644 --- a/tests/test_engine_slicer.py +++ b/tests/test_engine_slicer.py @@ -1,6 +1,6 @@ -from polytope.datacube.backends.mock import MockDatacube -from polytope.engine.hullslicer import HullSlicer -from polytope.shapes import Box, Polygon +from polytope_feature.datacube.backends.mock import MockDatacube +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.shapes import Box, Polygon class TestEngineSlicer: diff --git a/tests/test_fdb_datacube.py b/tests/test_fdb_datacube.py index 777e60f42..301ceec30 100644 --- a/tests/test_fdb_datacube.py +++ b/tests/test_fdb_datacube.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select, Span # import geopandas as gpd # import matplotlib.pyplot as plt diff --git a/tests/test_fdb_unmap_tree.py b/tests/test_fdb_unmap_tree.py index bfc71f1ee..a9dcf9cbe 100644 --- a/tests/test_fdb_unmap_tree.py +++ b/tests/test_fdb_unmap_tree.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingFDBDatacube: diff --git a/tests/test_float_type.py b/tests/test_float_type.py index 222ce458a..51df70df8 100644 --- a/tests/test_float_type.py +++ b/tests/test_float_type.py @@ -2,9 +2,9 @@ import pytest import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select, Span class TestFloatType: diff --git a/tests/test_healpix_mapper.py b/tests/test_healpix_mapper.py index d3d1ac50e..8badbffc6 100644 --- a/tests/test_healpix_mapper.py +++ b/tests/test_healpix_mapper.py @@ -2,21 +2,21 @@ from earthkit import data from helper_functions import download_test_data, find_nearest_latlon -from polytope.datacube.transformations.datacube_mappers.mapper_types.healpix import ( +from polytope_feature.datacube.transformations.datacube_mappers.mapper_types.healpix import ( HealpixGridMapper, ) -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select -class TestOctahedralGrid: +class TestHealpixGrid: def setup_method(self, method): nexus_url = "https://get.ecmwf.int/test-data/polytope/test-data/healpix.grib" download_test_data(nexus_url, "healpix.grib") ds = data.from_source("file", "./tests/data/healpix.grib") - self.latlon_array = ds.to_xarray().isel(step=0).isel(time=0).isel(isobaricInhPa=0).z + self.latlon_array = ds.to_xarray(engine="cfgrib").isel(step=0).isel(time=0).isel(isobaricInhPa=0).z self.options = { "axis_config": [ { @@ -69,7 +69,7 @@ def test_healpix_grid(self): eccodes_lon = nearest_points[0][0]["lon"] eccodes_result = nearest_points[0][0]["value"] - mapper = HealpixGridMapper("base", ["base", "base"], 32) + mapper = HealpixGridMapper("base", ["base1", "base2"], 32) assert nearest_points[0][0]["index"] == mapper.unmap((lat,), (lon,)) assert eccodes_lat - tol <= lat assert lat <= eccodes_lat + tol diff --git a/tests/test_healpix_nested_grid.py b/tests/test_healpix_nested_grid.py index 8d2108521..225b16206 100644 --- a/tests/test_healpix_nested_grid.py +++ b/tests/test_healpix_nested_grid.py @@ -3,12 +3,12 @@ import pandas as pd import pytest -from polytope.datacube.transformations.datacube_mappers.mapper_types.healpix_nested import ( +from polytope_feature.datacube.transformations.datacube_mappers.mapper_types.healpix_nested import ( NestedHealpixGridMapper, ) -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestHealpixNestedGrid: diff --git a/tests/test_hull_slicer.py b/tests/test_hull_slicer.py index e733612f7..c05ca5814 100644 --- a/tests/test_hull_slicer.py +++ b/tests/test_hull_slicer.py @@ -2,15 +2,15 @@ import pytest -import polytope.engine.hullslicer -from polytope import ConvexPolytope -from polytope.datacube.backends.mock import MockDatacube -from polytope.utility.profiling import benchmark +from polytope_feature import ConvexPolytope +from polytope_feature.datacube.backends.mock import MockDatacube +from polytope_feature.engine.hullslicer import HullSlicer, slice +from polytope_feature.utility.profiling import benchmark class TestHullSlicer: def setup_method(self, method): - self.slicer = polytope.engine.hullslicer.HullSlicer() + self.slicer = HullSlicer() def construct_nd_cube(self, dimension, lower=-1, upper=1): axes = [str(chr(97 + ax)) for ax in range(dimension)] @@ -20,9 +20,9 @@ def construct_nd_cube(self, dimension, lower=-1, upper=1): def test_3D(self): p3 = self.construct_nd_cube(3) print(p3) - p2 = polytope.engine.hullslicer.slice(p3, "c", 0.5, 2) + p2 = slice(p3, "c", 0.5, 2) print(p2) - p1 = polytope.engine.hullslicer.slice(p2, "b", 0.5, 1) + p1 = slice(p2, "b", 0.5, 1) print(p1) @pytest.mark.skip(reason="This is too slow.") @@ -30,7 +30,7 @@ def test_4D(self): p = self.construct_nd_cube(4) print(p) while len(p.axes()) > 1: - p = polytope.engine.hullslicer.slice(p, p._axes[-1], 0.5, -1) + p = slice(p, p._axes[-1], 0.5, -1) print(p) @pytest.mark.skip(reason="This is too slow.") @@ -38,22 +38,22 @@ def test_ND(self): with benchmark("4D"): p = self.construct_nd_cube(4) while len(p.axes()) > 1: - p = polytope.engine.hullslicer.slice(p, p._axes[-1], 0.5, -1) + p = slice(p, p._axes[-1], 0.5, -1) with benchmark("5D"): p = self.construct_nd_cube(5) while len(p.axes()) > 1: - p = polytope.engine.hullslicer.slice(p, p._axes[-1], 0.5, -1) + p = slice(p, p._axes[-1], 0.5, -1) with benchmark("6D"): p = self.construct_nd_cube(6) while len(p.axes()) > 1: - p = polytope.engine.hullslicer.slice(p, p._axes[-1], 0.5, -1) + p = slice(p, p._axes[-1], 0.5, -1) with benchmark("7D"): p = self.construct_nd_cube(7) while len(p.axes()) > 1: - p = polytope.engine.hullslicer.slice(p, p._axes[-1], 0.5, -1) + p = slice(p, p._axes[-1], 0.5, -1) # QHull is not performant above 7D as per its documentation # with benchmark("8D"): diff --git a/tests/test_hullslicer_engine.py b/tests/test_hullslicer_engine.py index 148025323..198b96b25 100644 --- a/tests/test_hullslicer_engine.py +++ b/tests/test_hullslicer_engine.py @@ -1,11 +1,11 @@ import numpy as np import xarray as xr -from polytope.datacube.backends.xarray import XArrayDatacube -from polytope.datacube.tensor_index_tree import TensorIndexTree -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope -from polytope.shapes import Box +from polytope_feature.datacube.backends.xarray import XArrayDatacube +from polytope_feature.datacube.tensor_index_tree import TensorIndexTree +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope +from polytope_feature.shapes import Box class TestSlicerComponents: diff --git a/tests/test_incomplete_tree_fdb.py b/tests/test_incomplete_tree_fdb.py index b863dac2a..2fbfcaf28 100644 --- a/tests/test_incomplete_tree_fdb.py +++ b/tests/test_incomplete_tree_fdb.py @@ -2,9 +2,9 @@ import pytest from helper_functions import download_test_data -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select class TestRegularGrid: diff --git a/tests/test_local_grid_cyclic.py b/tests/test_local_grid_cyclic.py index b30f0c2cf..3f4bfd36c 100644 --- a/tests/test_local_grid_cyclic.py +++ b/tests/test_local_grid_cyclic.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Point, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Point, Select class TestSlicingFDBDatacube: @@ -26,6 +26,8 @@ def setup_method(self, method): "resolution": [80, 80], "axes": ["latitude", "longitude"], "local": [-40, 40, -20, 60], + "axis_reversed": {"latitude": True, "longitude": False}, + "md5_hash": "47ee1554a9aebbc4f8197f19449bdc0c", } ], }, diff --git a/tests/test_local_regular_grid.py b/tests/test_local_regular_grid.py index eee0525db..3f8f2d402 100644 --- a/tests/test_local_regular_grid.py +++ b/tests/test_local_regular_grid.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Point, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Point, Select class TestSlicingFDBDatacube: @@ -26,6 +26,8 @@ def setup_method(self, method): "resolution": [80, 80], "axes": ["latitude", "longitude"], "local": [-40, 40, -20, 60], + "axis_reversed": {"latitude": True, "longitude": False}, + "md5_hash": "47ee1554a9aebbc4f8197f19449bdc0c", } ], }, diff --git a/tests/test_local_swiss_grid.py b/tests/test_local_swiss_grid.py index 29001f7ad..34019e54f 100644 --- a/tests/test_local_swiss_grid.py +++ b/tests/test_local_swiss_grid.py @@ -4,9 +4,9 @@ import pytest from eccodes import codes_grib_find_nearest, codes_grib_new_from_file -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingFDBDatacube: diff --git a/tests/test_mappers.py b/tests/test_mappers.py index fb4783a33..3a2cb4fb4 100644 --- a/tests/test_mappers.py +++ b/tests/test_mappers.py @@ -1,4 +1,4 @@ -from polytope.datacube.transformations.datacube_mappers.mapper_types.octahedral import ( +from polytope_feature.datacube.transformations.datacube_mappers.mapper_types.octahedral import ( OctahedralGridMapper, ) diff --git a/tests/test_merge_cyclic_octahedral.py b/tests/test_merge_cyclic_octahedral.py index c44483a06..0997c806c 100644 --- a/tests/test_merge_cyclic_octahedral.py +++ b/tests/test_merge_cyclic_octahedral.py @@ -2,9 +2,9 @@ import pytest import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select, Span class TestMultipleTransformations: diff --git a/tests/test_merge_octahedral_one_axis.py b/tests/test_merge_octahedral_one_axis.py index 86e466769..4ddf90582 100644 --- a/tests/test_merge_octahedral_one_axis.py +++ b/tests/test_merge_octahedral_one_axis.py @@ -2,9 +2,9 @@ from earthkit import data from helper_functions import download_test_data -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingMultipleTransformationsOneAxis: @@ -13,7 +13,7 @@ def setup_method(self, method): download_test_data(nexus_url, "foo.grib") ds = data.from_source("file", "./tests/data/foo.grib") - self.latlon_array = ds.to_xarray().isel(step=0).isel(number=0).isel(surface=0).isel(time=0) + self.latlon_array = ds.to_xarray(engine="cfgrib").isel(step=0).isel(number=0).isel(surface=0).isel(time=0) self.latlon_array = self.latlon_array.t2m self.options = { "axis_config": [ diff --git a/tests/test_merge_transformation.py b/tests/test_merge_transformation.py index 61c3284ae..5d2311c35 100644 --- a/tests/test_merge_transformation.py +++ b/tests/test_merge_transformation.py @@ -2,9 +2,9 @@ import pandas as pd import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select class TestMergeTransformation: diff --git a/tests/test_multiple_param_fdb.py b/tests/test_multiple_param_fdb.py index b12f11443..11285af84 100644 --- a/tests/test_multiple_param_fdb.py +++ b/tests/test_multiple_param_fdb.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingFDBDatacube: diff --git a/tests/test_octahedral_grid.py b/tests/test_octahedral_grid.py index 959f7ca64..f44ccf337 100644 --- a/tests/test_octahedral_grid.py +++ b/tests/test_octahedral_grid.py @@ -2,9 +2,9 @@ from earthkit import data from helper_functions import download_test_data, find_nearest_latlon -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestOctahedralGrid: @@ -13,7 +13,7 @@ def setup_method(self, method): download_test_data(nexus_url, "foo.grib") ds = data.from_source("file", "./tests/data/foo.grib") - self.latlon_array = ds.to_xarray().isel(step=0).isel(number=0).isel(surface=0).isel(time=0) + self.latlon_array = ds.to_xarray(engine="cfgrib").isel(step=0).isel(number=0).isel(surface=0).isel(time=0) self.latlon_array = self.latlon_array.t2m self.options = { "axis_config": [ diff --git a/tests/test_override_md5_hash_options.py b/tests/test_override_md5_hash_options.py new file mode 100644 index 000000000..0233290af --- /dev/null +++ b/tests/test_override_md5_hash_options.py @@ -0,0 +1,80 @@ +import pandas as pd +import pytest + +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select, Span + + +class TestSlicingFDBDatacube: + def setup_method(self, method): + # Create a dataarray with 3 labelled axes using different index types + self.options = { + "axis_config": [ + {"axis_name": "number", "transformations": [{"name": "type_change", "type": "int"}]}, + {"axis_name": "step", "transformations": [{"name": "type_change", "type": "int"}]}, + { + "axis_name": "date", + "transformations": [{"name": "merge", "other_axis": "time", "linkers": ["T", "00"]}], + }, + { + "axis_name": "values", + "transformations": [ + { + "name": "mapper", + "type": "octahedral", + "resolution": 1280, + "axes": ["latitude", "longitude"], + "md5_hash": "158db321ae8e773681eeb40e0a3d350f", + } + ], + }, + {"axis_name": "latitude", "transformations": [{"name": "reverse", "is_reverse": True}]}, + {"axis_name": "longitude", "transformations": [{"name": "cyclic", "range": [0, 360]}]}, + ], + "pre_path": {"class": "od", "expver": "0001", "levtype": "sfc", "stream": "oper"}, + "compressed_axes_config": [ + "longitude", + "latitude", + "levtype", + "step", + "date", + "domain", + "expver", + "param", + "class", + "stream", + "type", + ], + } + + # Testing different shapes + @pytest.mark.fdb + def test_fdb_datacube(self): + import pygribjump as gj + + request = Request( + Select("step", [0]), + Select("levtype", ["sfc"]), + Span("date", pd.Timestamp("20230625T120000"), pd.Timestamp("20230626T120000")), + Select("domain", ["g"]), + Select("expver", ["0001"]), + Select("param", ["167"]), + Select("class", ["od"]), + Select("stream", ["oper"]), + Select("type", ["an"]), + Box(["latitude", "longitude"], [0, 0], [0.2, 0.2]), + ) + + self.fdbdatacube = gj.GribJump() + self.slicer = HullSlicer() + self.API = Polytope( + datacube=self.fdbdatacube, + engine=self.slicer, + options=self.options, + ) + result = self.API.retrieve(request) + result.pprint() + assert len(result.leaves) == 3 + for i in range(len(result.leaves)): + assert len(result.leaves[i].result) == 3 diff --git a/tests/test_point_nearest.py b/tests/test_point_nearest.py index 75edf041b..b9fdc4bed 100644 --- a/tests/test_point_nearest.py +++ b/tests/test_point_nearest.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Point, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Point, Select class TestSlicingFDBDatacube: diff --git a/tests/test_point_shape.py b/tests/test_point_shape.py index 7b6d4c643..d4d6eb98f 100644 --- a/tests/test_point_shape.py +++ b/tests/test_point_shape.py @@ -2,9 +2,9 @@ import pandas as pd import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Point, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Point, Select class TestSlicing3DXarrayDatacube: diff --git a/tests/test_profiling_requesttree.py b/tests/test_profiling_requesttree.py index 6bd6afac2..670da7881 100644 --- a/tests/test_profiling_requesttree.py +++ b/tests/test_profiling_requesttree.py @@ -3,10 +3,10 @@ import pytest import xarray as xr -from polytope.datacube.backends.xarray import XArrayDatacube -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.datacube.backends.xarray import XArrayDatacube +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestProfiling: diff --git a/tests/test_reduced_ll_grid.py b/tests/test_reduced_ll_grid.py index d554e528e..b0a3535c4 100644 --- a/tests/test_reduced_ll_grid.py +++ b/tests/test_reduced_ll_grid.py @@ -5,9 +5,9 @@ from eccodes import codes_grib_find_nearest, codes_grib_new_from_file from helper_functions import download_test_data -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestReducedLatLonGrid: diff --git a/tests/test_regular_grid.py b/tests/test_regular_grid.py index 45e2803df..7eaaeccb5 100644 --- a/tests/test_regular_grid.py +++ b/tests/test_regular_grid.py @@ -2,9 +2,9 @@ import pytest from helper_functions import download_test_data, find_nearest_latlon -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Disk, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Disk, Select # import geopandas as gpd # import matplotlib.pyplot as plt @@ -25,7 +25,14 @@ def setup_method(self, method): { "axis_name": "values", "transformations": [ - {"name": "mapper", "type": "regular", "resolution": 30, "axes": ["latitude", "longitude"]} + { + "name": "mapper", + "type": "regular", + "resolution": 30, + "axes": ["latitude", "longitude"], + "axis_reversed": {"latitude": True, "longitude": False}, + "md5_hash": "15372eaafa9d744000df708d63f69284", + } ], }, {"axis_name": "latitude", "transformations": [{"name": "reverse", "is_reverse": True}]}, @@ -85,7 +92,7 @@ def test_regular_grid(self): assert len(result.leaves[1].values) == 3 assert len(result.leaves[2].values) == 1 - from polytope.datacube.transformations.datacube_mappers.mapper_types.regular import ( + from polytope_feature.datacube.transformations.datacube_mappers.mapper_types.regular import ( RegularGridMapper, ) @@ -108,7 +115,7 @@ def test_regular_grid(self): eccodes_value = nearest_points[121][0]["value"] eccodes_lats.append(eccodes_lat) - mapper = RegularGridMapper("base", ["base", "base"], 30) + mapper = RegularGridMapper("base", ["base1", "base2"], 30) assert nearest_points[121][0]["index"] == mapper.unmap((lat,), (lon,)) assert eccodes_lat - tol <= lat diff --git a/tests/test_request_tree.py b/tests/test_request_tree.py index ddd0463e4..038599724 100644 --- a/tests/test_request_tree.py +++ b/tests/test_request_tree.py @@ -1,7 +1,7 @@ from sortedcontainers import SortedList -from polytope.datacube.datacube_axis import IntDatacubeAxis -from polytope.datacube.tensor_index_tree import TensorIndexTree +from polytope_feature.datacube.datacube_axis import IntDatacubeAxis +from polytope_feature.datacube.tensor_index_tree import TensorIndexTree class TestIndexTree: diff --git a/tests/test_request_trees_after_slicing.py b/tests/test_request_trees_after_slicing.py index 3e7bf6dfb..2aa28b4a7 100644 --- a/tests/test_request_trees_after_slicing.py +++ b/tests/test_request_trees_after_slicing.py @@ -1,11 +1,11 @@ import numpy as np import xarray as xr -from polytope.datacube.backends.xarray import XArrayDatacube -from polytope.datacube.datacube_axis import IntDatacubeAxis -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope -from polytope.shapes import Box +from polytope_feature.datacube.backends.xarray import XArrayDatacube +from polytope_feature.datacube.datacube_axis import IntDatacubeAxis +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope +from polytope_feature.shapes import Box class TestIndexTreesAfterSlicing: diff --git a/tests/test_reverse_transformation.py b/tests/test_reverse_transformation.py index aa4b27fe1..f1d9b24ed 100644 --- a/tests/test_reverse_transformation.py +++ b/tests/test_reverse_transformation.py @@ -1,9 +1,9 @@ import numpy as np import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select class TestSlicingReverseTransformation: diff --git a/tests/test_shapes.py b/tests/test_shapes.py index ee17e6b22..ac730905a 100644 --- a/tests/test_shapes.py +++ b/tests/test_shapes.py @@ -3,9 +3,9 @@ import pytest import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import All, Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import All, Select, Span class TestSlicing3DXarrayDatacube: @@ -61,7 +61,13 @@ def test_all_mapper_cyclic(self): { "axis_name": "values", "transformations": [ - {"name": "mapper", "type": "octahedral", "resolution": 1280, "axes": ["latitude", "longitude"]} + { + "name": "mapper", + "type": "octahedral", + "resolution": 1280, + "axes": ["latitude", "longitude"], + "md5_hash": "5ea6378bf5e2904f565ef7221da63a09", + } ], }, {"axis_name": "latitude", "transformations": [{"name": "reverse", "is_reverse": True}]}, diff --git a/tests/test_slice_date_range_fdb.py b/tests/test_slice_date_range_fdb.py index 80d5c96e1..dae81cb22 100644 --- a/tests/test_slice_date_range_fdb.py +++ b/tests/test_slice_date_range_fdb.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Disk, Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Disk, Select, Span class TestSlicingFDBDatacube: diff --git a/tests/test_slice_date_range_fdb_v2.py b/tests/test_slice_date_range_fdb_v2.py index c75a2ca7d..fcacf6b7c 100644 --- a/tests/test_slice_date_range_fdb_v2.py +++ b/tests/test_slice_date_range_fdb_v2.py @@ -2,9 +2,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select, Span class TestSlicingFDBDatacube: diff --git a/tests/test_slice_fdb_box.py b/tests/test_slice_fdb_box.py index aa65edf7e..a21910dfd 100644 --- a/tests/test_slice_fdb_box.py +++ b/tests/test_slice_fdb_box.py @@ -3,10 +3,10 @@ import pandas as pd import pytest -from polytope.datacube.tree_encoding import decode_tree, encode_tree -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import All, Box, Select +from polytope_feature.datacube.tree_encoding import decode_tree, encode_tree +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import All, Box, Select class TestSlicingFDBDatacube: @@ -47,7 +47,7 @@ def setup_method(self, method): } # Testing different shapes - # @pytest.mark.skip(reason="optimisation test") + @pytest.mark.skip(reason="optimisation test") @pytest.mark.fdb def test_fdb_datacube(self): import pygribjump as gj diff --git a/tests/test_slicer_engine.py b/tests/test_slicer_engine.py index 29bdf5f09..289eeab6b 100644 --- a/tests/test_slicer_engine.py +++ b/tests/test_slicer_engine.py @@ -1,11 +1,11 @@ import numpy as np import xarray as xr -from polytope.datacube.backends.xarray import XArrayDatacube -from polytope.datacube.tensor_index_tree import TensorIndexTree -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope -from polytope.shapes import Box +from polytope_feature.datacube.backends.xarray import XArrayDatacube +from polytope_feature.datacube.tensor_index_tree import TensorIndexTree +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope +from polytope_feature.shapes import Box class TestSlicerComponents: diff --git a/tests/test_slicer_era5.py b/tests/test_slicer_era5.py index 39a335c1a..9f11bcef8 100644 --- a/tests/test_slicer_era5.py +++ b/tests/test_slicer_era5.py @@ -3,9 +3,9 @@ from earthkit import data from helper_functions import download_test_data -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingEra5Data: @@ -14,7 +14,7 @@ def setup_method(self, method): download_test_data(nexus_url, "era5-levels-members.grib") ds = data.from_source("file", "./tests/data/era5-levels-members.grib") - array = ds.to_xarray().isel(step=0).t + array = ds.to_xarray(engine="cfgrib").isel(step=0).t self.slicer = HullSlicer() options = { "axis_config": [{"axis_name": "latitude", "transformations": [{"name": "reverse", "is_reverse": True}]}], diff --git a/tests/test_slicer_xarray.py b/tests/test_slicer_xarray.py index 428345e39..b36cd051c 100644 --- a/tests/test_slicer_xarray.py +++ b/tests/test_slicer_xarray.py @@ -2,9 +2,9 @@ import pandas as pd import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select, Span +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select, Span class TestXarraySlicing: diff --git a/tests/test_slicing_unsliceable_axis.py b/tests/test_slicing_unsliceable_axis.py index d021291d2..286252065 100644 --- a/tests/test_slicing_unsliceable_axis.py +++ b/tests/test_slicing_unsliceable_axis.py @@ -3,10 +3,10 @@ import pytest import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select -from polytope.utility.exceptions import UnsliceableShapeError +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select +from polytope_feature.utility.exceptions import UnsliceableShapeError class TestSlicingUnsliceableAxis: diff --git a/tests/test_slicing_xarray_3D.py b/tests/test_slicing_xarray_3D.py index cbffeccae..72f68d0ff 100644 --- a/tests/test_slicing_xarray_3D.py +++ b/tests/test_slicing_xarray_3D.py @@ -5,11 +5,11 @@ import pandas as pd import xarray as xr -from polytope.datacube.backends.xarray import XArrayDatacube -from polytope.datacube.tensor_index_tree import TensorIndexTree -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import ( +from polytope_feature.datacube.backends.xarray import XArrayDatacube +from polytope_feature.datacube.tensor_index_tree import TensorIndexTree +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import ( Box, ConvexPolytope, Disk, @@ -249,3 +249,11 @@ def test_intersection_point_disk_polygon(self): result = self.API.retrieve(request) paths = [r.flatten().values() for r in result.leaves] assert ((pd.Timestamp("2000-01-01 00:00:00"),), (3,), (1,)) in paths + + def test_duplicate_values_select(self): + request = Request(Select("step", [3, 3]), Select("level", [1]), Select("date", ["2000-01-01"])) + result = self.API.retrieve(request) + result.pprint() + assert len(result.leaves) == 1 + path = result.leaves[0].flatten()["step"] + assert len(path) == 1 diff --git a/tests/test_slicing_xarray_4D.py b/tests/test_slicing_xarray_4D.py index c8b3abd5e..9345e8db5 100644 --- a/tests/test_slicing_xarray_4D.py +++ b/tests/test_slicing_xarray_4D.py @@ -3,10 +3,10 @@ import pytest import xarray as xr -from polytope.datacube.tensor_index_tree import TensorIndexTree -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import ( +from polytope_feature.datacube.tensor_index_tree import TensorIndexTree +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import ( Box, Disk, Ellipsoid, @@ -17,7 +17,10 @@ Span, Union, ) -from polytope.utility.exceptions import AxisOverdefinedError, AxisUnderdefinedError +from polytope_feature.utility.exceptions import ( + AxisOverdefinedError, + AxisUnderdefinedError, +) class TestSlicing4DXarrayDatacube: diff --git a/tests/test_snapping.py b/tests/test_snapping.py index bdb365ce5..529f1d69a 100644 --- a/tests/test_snapping.py +++ b/tests/test_snapping.py @@ -1,9 +1,9 @@ import numpy as np import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select class TestSlicing3DXarrayDatacube: diff --git a/tests/test_snapping_real_data.py b/tests/test_snapping_real_data.py index c2bcc9f02..9b5950742 100644 --- a/tests/test_snapping_real_data.py +++ b/tests/test_snapping_real_data.py @@ -6,9 +6,9 @@ from earthkit import data from helper_functions import download_test_data -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select class TestSlicingEra5Data: @@ -17,7 +17,7 @@ def setup_method(self, method): download_test_data(nexus_url, "era5-levels-members.grib") ds = data.from_source("file", "./tests/data/era5-levels-members.grib") - array = ds.to_xarray().isel(step=0).t + array = ds.to_xarray(engine="cfgrib").isel(step=0).t self.slicer = HullSlicer() options = { "axis_config": [ diff --git a/tests/test_tree_protobuf.py b/tests/test_tree_protobuf.py index a88633ea5..7f2e56f23 100644 --- a/tests/test_tree_protobuf.py +++ b/tests/test_tree_protobuf.py @@ -1,4 +1,4 @@ -import polytope.datacube.index_tree_pb2 as pb2 +import polytope_feature.datacube.index_tree_pb2 as pb2 class TestTreeProtobuf: diff --git a/tests/test_tree_protobuf_encoding.py b/tests/test_tree_protobuf_encoding.py index 7497453d4..b177cbe2b 100644 --- a/tests/test_tree_protobuf_encoding.py +++ b/tests/test_tree_protobuf_encoding.py @@ -1,15 +1,15 @@ import pytest -from polytope.datacube.backends.mock import MockDatacube -from polytope.datacube.datacube_axis import ( +from polytope_feature.datacube.backends.mock import MockDatacube +from polytope_feature.datacube.datacube_axis import ( FloatDatacubeAxis, IntDatacubeAxis, PandasTimedeltaDatacubeAxis, PandasTimestampDatacubeAxis, UnsliceableDatacubeAxis, ) -from polytope.datacube.tensor_index_tree import TensorIndexTree -from polytope.datacube.tree_encoding import decode_tree, encode_tree +from polytope_feature.datacube.tensor_index_tree import TensorIndexTree +from polytope_feature.datacube.tree_encoding import decode_tree, encode_tree class TestEncoder: @@ -49,8 +49,8 @@ def setup_method(self): def test_encoding(self): import pygribjump as gj - from polytope.engine.hullslicer import HullSlicer - from polytope.polytope import Polytope + from polytope_feature.engine.hullslicer import HullSlicer + from polytope_feature.polytope import Polytope self.options = { "pre_path": {"class": "od", "expver": "0001", "levtype": "sfc", "stream": "oper"}, diff --git a/tests/test_tree_protobuf_encoding_fdb.py b/tests/test_tree_protobuf_encoding_fdb.py index 7cef1070e..2e288f0d6 100644 --- a/tests/test_tree_protobuf_encoding_fdb.py +++ b/tests/test_tree_protobuf_encoding_fdb.py @@ -1,7 +1,7 @@ import pandas as pd import pytest -from polytope.datacube.tree_encoding import decode_tree, encode_tree +from polytope_feature.datacube.tree_encoding import decode_tree, encode_tree class TestEncoder: @@ -12,9 +12,9 @@ def setup_method(self): def test_encoding(self): import pygribjump as gj - from polytope.engine.hullslicer import HullSlicer - from polytope.polytope import Polytope, Request - from polytope.shapes import Box, Select + from polytope_feature.engine.hullslicer import HullSlicer + from polytope_feature.polytope import Polytope, Request + from polytope_feature.shapes import Box, Select request = Request( Select("step", [0]), diff --git a/tests/test_type_change_transformation.py b/tests/test_type_change_transformation.py index f74dd4744..09f800610 100644 --- a/tests/test_type_change_transformation.py +++ b/tests/test_type_change_transformation.py @@ -1,9 +1,9 @@ import numpy as np import xarray as xr -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Select +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Select class TestTypeChangeTransformation: diff --git a/tests/test_union_gj.py b/tests/test_union_gj.py index 30f22fc54..9182fc360 100644 --- a/tests/test_union_gj.py +++ b/tests/test_union_gj.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Select, Span, Union +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Select, Span, Union class TestSlicingFDBDatacube: diff --git a/tests/test_union_point_box.py b/tests/test_union_point_box.py index 7b7d1152d..ba280bc02 100644 --- a/tests/test_union_point_box.py +++ b/tests/test_union_point_box.py @@ -1,9 +1,9 @@ import pandas as pd import pytest -from polytope.engine.hullslicer import HullSlicer -from polytope.polytope import Polytope, Request -from polytope.shapes import Box, Point, Select, Union +from polytope_feature.engine.hullslicer import HullSlicer +from polytope_feature.polytope import Polytope, Request +from polytope_feature.shapes import Box, Point, Select, Union class TestSlicingFDBDatacube: diff --git a/tox.ini b/tox.ini index 36b7ba462..495b19988 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ exclude = .* extend-ignore = E203,W503 per-file-ignores = */__init__.py: F401,F403 - polytope/datacube/index_tree_pb2.py: E501 + polytope_feature/datacube/index_tree_pb2.py: E501 [isort] profile=black skip_glob=.* \ No newline at end of file