From d3c302e1ee2b1ed3c05aab8533341fa5d240b792 Mon Sep 17 00:00:00 2001 From: Padraig Gleeson Date: Thu, 12 Jan 2023 10:15:20 +0000 Subject: [PATCH 01/32] Install hdf5 from source --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b770161..e3683cc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: - name: Install package run: | + if [[ ${{ matrix.python-version }} == 3.11 ]]; then pip3 install --no-binary=h5py h5py ; fi python -m pip install --upgrade pip pip install .[dev] @@ -56,7 +57,7 @@ jobs: - name: Run pytest run: | - pytest tests + pytest tests -v - name: Install & test NeuroMLlite run: | From f49bb8862ee9c242f9d0b0ebbad14e1c4464f286 Mon Sep 17 00:00:00 2001 From: Padraig Gleeson Date: Thu, 12 Jan 2023 10:20:34 +0000 Subject: [PATCH 02/32] install libhdf5-dev --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3683cc7..1c0e3a0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: - name: Install package run: | - if [[ ${{ matrix.python-version }} == 3.11 ]]; then pip3 install --no-binary=h5py h5py ; fi + if [[ ${{ matrix.python-version }} == 3.11 ]]; then apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi python -m pip install --upgrade pip pip install .[dev] From ad4df1922bf80bd1cdba33361079dc7915819dee Mon Sep 17 00:00:00 2001 From: Padraig Gleeson Date: Thu, 12 Jan 2023 10:28:55 +0000 Subject: [PATCH 03/32] sudo --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c0e3a0f..8d7fea41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: - name: Install package run: | - if [[ ${{ matrix.python-version }} == 3.11 ]]; then apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi + if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi python -m pip install --upgrade pip pip install .[dev] From f25194c61c89fb46646d5d80340a19a5bb226afb Mon Sep 17 00:00:00 2001 From: Padraig Gleeson Date: Tue, 31 Jan 2023 16:42:07 +0000 Subject: [PATCH 04/32] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d7fea41..050a41a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: - name: Install package run: | - if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi + if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt-get install libhdf5-serial-dev liblzo2-dev -y; pip3 install --no-binary=h5py h5py ; fi python -m pip install --upgrade pip pip install .[dev] From 9770a5a82848be618ddfc1386d40cdf3199d2930 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Wed, 26 Jul 2023 12:01:38 +0100 Subject: [PATCH 05/32] ci(py311): add to matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb406efa..92b331bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.7", "3.8", "3.9", "3.10"] + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11"] runs-on: [ubuntu-latest, macos-latest, windows-latest] steps: From 2b9235110b83211792dfbd8f77d94d9c27faecc4 Mon Sep 17 00:00:00 2001 From: Robert Vickerstaff Date: Sat, 29 Jul 2023 11:59:38 +0000 Subject: [PATCH 06/32] added simple sbml test with example xml showing desired output we should be aiming to produce --- examples/sbml/example_sbml_minimal.xml | 12 + examples/sbml/sbml32spec.py | 379 ++++++++++++++++++++++++ examples/sbml/sbml_validators.py | 88 ++++++ examples/sbml/test_minimal_example.json | 18 ++ examples/sbml/test_sbml3.py | 36 +++ 5 files changed, 533 insertions(+) create mode 100644 examples/sbml/example_sbml_minimal.xml create mode 100644 examples/sbml/sbml32spec.py create mode 100644 examples/sbml/sbml_validators.py create mode 100644 examples/sbml/test_minimal_example.json create mode 100755 examples/sbml/test_sbml3.py diff --git a/examples/sbml/example_sbml_minimal.xml b/examples/sbml/example_sbml_minimal.xml new file mode 100644 index 00000000..fe9b1524 --- /dev/null +++ b/examples/sbml/example_sbml_minimal.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py new file mode 100644 index 00000000..400cff3f --- /dev/null +++ b/examples/sbml/sbml32spec.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 + +''' +initial attempt at creating an SBML API using modelspec +https://github.com/combine-org/compbiolibs/issues/28 + +based on sbml.level-3.version-2.core.release-2.pdf +''' + +import modelspec +from modelspec import field, instance_of, optional +from modelspec.base_types import Base +from typing import List + +from sbml_validators import * + +@modelspec.define +class Notes(Base): + ''' + XHTML field of SBase + + Args: + xmlns: str fixed "http://www.w3.org/1999/xhtml" + content: str valid XHTML + ''' + xmlns: str = field(default="http://www.w3.org/1999/xhtml",validator=[instance_of(str),xmlns_notes]) + content: str = field(default=None,validator=optional([instance_of(str),valid_xhtml])) + +@modelspec.define +class Math(Base): + ''' + Subset of MathML 2.0 used to define all formulae in SBML + ''' + xmlns: str = field(default="http://www.w3.org/1998/Math/MathML",validator=[instance_of(str),xmlns_math]) + content: str = field(default=None,validator=optional([instance_of(str),valid_mathml])) + +@modelspec.define +class SBase(Base): + """ + Abstract base class for all SBML objects + + Args: + sid: SId optional + name: string optional + metaid: XML ID optional + sboTerm: SBOTerm optional + + notes: XHTML 1.0 optional + annotation: XML content optional + """ + + sid: str = field(default=None,validator=optional([instance_of(str),valid_sid])) + name: str = field(default=None,validator=optional(instance_of(str))) + metaid: str = field(default=None,validator=optional([instance_of(str),valid_xml_id])) + sboTerm: str = field(default=None,validator=optional([instance_of(str),valid_sbo])) + + notes: Notes = field(default=None,validator=optional(instance_of(Notes))) + annotation: str = field(default=None,validator=optional([instance_of(str),valid_xml_content])) + +@modelspec.define +class Trigger(SBase): + initialValue: bool = field(default=None,validator=instance_of(bool)) + persistent: bool = field(default=None,validator=instance_of(bool)) + math: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class Priority(SBase): + math: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class Delay(SBase): + math: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class EventAssignment(SBase): + ''' + Args: + variable: SIdRef + ''' + math: str = field(default=None,validator=optional(instance_of(str))) + variable: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class Event(SBase): + useValuesFromTriggerTime: bool = field(default=None,validator=instance_of(bool)) + trigger: Trigger = field(default=None, validator=optional(instance_of(Trigger))) + priority: Priority = field(default=None, validator=optional(instance_of(Priority))) + delay: Delay = field(default=None, validator=optional(instance_of(Delay))) + listOfEventAssignments: List[EventAssignment] = field(factory=list) + +@modelspec.define +class SimpleSpeciesReference(SBase): + """ + Base class used by SpeciesReference and ModifierSpeciesReference + + Args: + species: SIdRef + """ + + species: str = field(default=None,validator=instance_of(str)) + +@modelspec.define +class ModifierSpeciesReference(SimpleSpeciesReference): + '' + +@modelspec.define +class SpeciesReference(SimpleSpeciesReference): + """ + Args: + stoichiometry: double optional + constant: boolean + """ + + stoichiometry: float = field(default=None,validator=optional(instance_of(float))) + constant: bool = field(default=None,validator=instance_of(bool)) + +@modelspec.define +class LocalParameter(SBase): + """ + Args: + units: UnitSIdRef optional + """ + + value: float = field(default=None,validator=optional(instance_of(float))) + units: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class KineticLaw(SBase): + """ + """ + + math: str = field(default=None,validator=optional(instance_of(str))) + + listOfLocalParameters: List[LocalParameter] = field(factory=list) + +@modelspec.define +class Reaction(SBase): + """ + A model reaction + + Args: + reversible: boolean + compartment: SIdRef optional + """ + + reversible: bool = field(default=None,validator=instance_of(bool)) + compartment: str = field(default=None,validator=optional(instance_of(str))) + + listOfReactants: List[SpeciesReference] = field(factory=list) + listOfProducts: List[SpeciesReference] = field(factory=list) + listOfModifiers: List[ModifierSpeciesReference] = field(factory=list) + + kineticLaw: KineticLaw = field(default=None, validator=optional(instance_of(KineticLaw))) + +@modelspec.define +class Constraint(SBase): + """ + A model constraint + + Args: + math: MathML optional + message: XHTML 1.0 optional + """ + + math: str = field(default=None,validator=optional(instance_of(str))) + message: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class Rule(SBase): + """ + A rule, either algebraic, assignment or rate + + Args: + math: MathML optional + """ + + math: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class AlgebraicRule(Rule): + """ + An algebraic rule + """ + +@modelspec.define +class AssignmentRule(Rule): + """ + An assignment rule + + Args: + variable: SIdRef required + """ + + variable: str = field(default=None,validator=instance_of(str)) + +@modelspec.define +class RateRule(Rule): + """ + A rate rule + + Args: + variable: SIdRef required + """ + + variable: str = field(default=None,validator=instance_of(str)) + +@modelspec.define +class InitialAssignment(SBase): + """ + An initial assignment + + Args: + symbol: SIdRef required + math: MathML optional + """ + + symbol: str = field(default=None,validator=instance_of(str)) + math: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class Parameter(SBase): + """ + A parameter + + Args: + value: double optional + units: UnitSIdRef optional + constant: boolean + """ + + constant: bool = field(default=None,validator=instance_of(bool)) + + value: float = field(default=None,validator=optional(instance_of(float))) + units: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class Species(SBase): + """ + A species: entities of the same kind participating in reactions within a specific compartment + + Args: + compartment: SIdRef + initialAmount: double optional + initialConcentration: double optional + substanceUnits: UnitSIdRef optional + hasOnlySubstanceUnits: boolean + boundaryCondition: boolean + constant: boolean + conversionFactor: SIdRef optional + """ + + compartment: str = field(default=None,validator=instance_of(str)) + hasOnlySubstanceUnits: bool = field(default=None,validator=instance_of(bool)) + boundaryCondition: bool = field(default=None,validator=instance_of(bool)) + constant: bool = field(default=None,validator=instance_of(bool)) + + initialAmount: float = field(default=None, validator=optional(instance_of(float))) + initialConcentration: float = field(default=None, validator=optional(instance_of(float))) + substanceUnits: str = field(default=None, validator=optional(instance_of(str))) + conversionFactor: str = field(default=None, validator=optional(instance_of(str))) + +@modelspec.define +class Compartment(SBase): + """ + A compartment + + Args: + spatialDimensions: eg 3 for three dimensional space etc + size: initial size of compartment + units: units being used to define the compartment's size + constant: whether size is fixed + """ + + constant: bool = field(default=None,validator=instance_of(bool)) + + spatialDimensions: float = field(default=None,validator=optional(instance_of(float))) + size: float = field(default=None,validator=optional(instance_of(float))) + units: str = field(default=None,validator=optional(instance_of(str))) + +@modelspec.define +class Unit(SBase): + """ + A unit used to compose a unit definition. + unit = (multiplier x 10^scale x kind)^exponent + + Args: + kind: base unit (base or derived SI units only, see Table 2 of the SBML spec) + exponent: double + scale: integer + multiplier: double + """ + + kind: str = field(default=None,validator=[instance_of(str),valid_kind]) + exponent: str = field(default=1.0, validator=instance_of(float)) + scale: str = field(default=0, validator=instance_of(int)) + multiplier: str = field(default=1.0, validator=instance_of(float)) + +@modelspec.define +class UnitDefinition(SBase): + """ + A unit definition + + Args: + sid: UnitSid required (overrides SBase sid) + listOfUnits: List of units used to compose the definition + """ + + sid: str = field(default=None,validator=[instance_of(str),valid_unitsid]) + listOfUnits: List[Unit] = field(factory=list) + +@modelspec.define +class FunctionDefinition(SBase): + """ + A function definition using MathML + + Args: + sid: SId required + + math: MathML function definition optional + """ + + sid: str = field(default=None,validator=[instance_of(str),valid_sid]) + + math: Math = field(default=None, validator=optional(instance_of(Math))) + +@modelspec.define +class Model(SBase): + """ + The model + + Args: + substanceUnits: UnitSIdRef optional + timeUnits: UnitSIdRef optional + volumeUnits: UnitSIdRef optional + areaUnits: UnitSIdRef optional + lengthUnits: UnitSIdRef optional + extentUnits: UnitSIdRef optional + conversionFactor: SIdRef optional + """ + + substanceUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + timeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + volumeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + areaUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + lengthUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + extentUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + conversionFactor: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + + listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list) + listOfUnitDefinitions: List[UnitDefinition] = field(factory=list) + listOfCompartments: List[Compartment] = field(factory=list) + listOfSpecies: List[Species] = field(factory=list) + listOfParameters: List[Parameter] = field(factory=list) + listOfInitialAssignments: List[InitialAssignment] = field(factory=list) + listOfRules: List[Rule] = field(factory=list) + listOfConstraints: List[Constraint] = field(factory=list) + listOfReactions: List[Reaction] = field(factory=list) + listOfEvents: List[Event] = field(factory=list) + +@modelspec.define +class SBML(SBase): + """ + The top-level SBML container implementing SBML 3.2. + See sbml.level-3.version-2.core.release-2.pdf section 4. + http://www.sbml.org/sbml/level3/version2/core + + Args: + xmlns: string, fixed to "http://www.sbml.org/sbml/level3/version2/core" + level: SBML level, fixed to 3 + version: SBML version, fixed to 2 + + model: Optional model + """ + + xmlns: str = field(default="http://www.sbml.org/sbml/level3/version2/core",validator=[instance_of(str),xmlns_sbml]) + level: str = field(default="3",validator=[instance_of(str),fixed_level]) + version: str = field(default="2",validator=[instance_of(str),fixed_version]) + + model: Model = field(default=None, validator=optional(instance_of(Model))) diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py new file mode 100644 index 00000000..008c49c3 --- /dev/null +++ b/examples/sbml/sbml_validators.py @@ -0,0 +1,88 @@ +''' +functions used to validate the user assigned values of items +these functions will generally be called by passing to the validator option of attrs.field +''' + +import re +from lxml import etree +from io import StringIO + +#sbml.level-3.version-2.core.release-2.pdf Table 2 +sbml_si_units=\ +''' +ampere coulomb gray joule litre mole radian steradian weber avogadro dimensionless henry katal lumen newton +second tesla becquerel farad hertz kelvin lux ohm siemens volt candela gram item kilogram metre pascal sievert watt +'''.split() + +def valid_kind(instance, attribute, value): + if not value in sbml_si_units: + raise ValueError(f"kind {value} must be one of the standard SI units: {sbml_si_units}") + +def valid_mathml(instance, attribute, value): + 'http://www.w3.org/1998/Math/MathML' + +def xmlns_sbml(instance, attribute, value): + if value != "http://www.sbml.org/sbml/level3/version2/core": + raise ValueError("xmlns must be 'http://www.sbml.org/sbml/level3/version2/core'") + +def xmlns_notes(instance, attribute, value): + if value != "http://www.w3.org/1999/xhtml": + raise ValueError("xmlns must be 'http://www.w3.org/1999/xhtml'") + +def xmlns_math(instance, attribute, value): + if value != "http://www.w3.org/1998/Math/MathML": + raise ValueError("xmlns must be 'http://www.w3.org/1998/Math/MathML'") + +def fixed_level(instance, attribute, value): + if value != "3": + raise ValueError("this implementation only supports level 3") + +def fixed_version(instance, attribute, value): + if value != "2": + raise ValueError("this implementation only supports level 2") + +def valid_sid(instance, attribute, value): + if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value): + raise ValueError("an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*") + +def valid_unitsid(instance, attribute, value): + 'same as sid except has a separate namespace' + if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value): + raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*") + +def valid_unitsid(instance, attribute, value): + if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value): + raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*") + +def valid_sbo(instance, attribute, value): + if not re.fullmatch('SBO:[0-9]{7}',value): + raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}") + +def valid_xml_id(instance,attribute,value): + 'a valid XML 1.0 ID' + #not implemented yet: CombiningChar , Extender + #NameChar ::= letter | digit | '.' | '-' | '_' | ':' | CombiningChar | Extender + #ID ::= ( letter | '_' | ':' ) NameChar* + + if not re.fullmatch('[A-Za-z_:][A-Za-z0-9._:-]*',value): + raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}") + +def valid_xhtml(instance,attribute,value): + 'use etree to validate XHTML, throw exception on error' + etree.parse(StringIO(value), etree.HTMLParser(recover=False)) + +def valid_xml_content(instance,attribute,value): + 'stub' + + if not re.search('<.*>',value): + raise ValueError(f"{value} doesn't look like XML (this validator is only a stub)") + +def valid_mathml(instance,attribute,value): + ''' + stubvalidator for MathML + note: see pdf section 4.3.2 for special rules for FunctionDefinition MathML + versus all other MathML uses in SBML + ''' + + if not re.search('',value): + raise ValueError(f"{value} doesn't look like MathML (this validator is only a stub)") diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json new file mode 100644 index 00000000..e8419a1f --- /dev/null +++ b/examples/sbml/test_minimal_example.json @@ -0,0 +1,18 @@ +{ + "model": { + "substanceUnits": "mole", + "timeUnits": "second", + "extentUnits": "mole", + "listOfUnitDefinitions": [ + { + "sid": "per_second", + "listOfUnits": [ + { + "kind": "second", + "exponent": -1.0 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py new file mode 100755 index 00000000..d7c59338 --- /dev/null +++ b/examples/sbml/test_sbml3.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +''' +derived from https://github.com/combine-org/draft-modelspec-sbml-api/blob/main/test_sbml32.py +a minimalish sbml to xml example +''' + +import json +import yaml +import os + +from sbml32spec import * + +def test_example_sbml_minimal(): + 'aiming to match the xml file example_sbml_minimal.xml' + + path = "test_minimal_example" + + sbml_doc = SBML() + #open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4)) + + model = Model(substanceUnits="mole",timeUnits="second",extentUnits="mole") + sbml_doc.model = model + + unit_def = UnitDefinition(sid="per_second") + model.listOfUnitDefinitions.append(unit_def) + + unit = Unit(kind="second",exponent=-1.0) + unit_def.listOfUnits.append(unit) + + sbml_doc.to_json_file(f"{path}.json") + sbml_doc.to_xml_file(f"{path}.xml") + + +if __name__ == "__main__": + test_example_sbml_minimal() From f0f8e6cd92d1163b046fb2a3476d49b16ef1bb9e Mon Sep 17 00:00:00 2001 From: pgleeson Date: Mon, 31 Jul 2023 15:47:06 +0100 Subject: [PATCH 07/32] Formatted new sbml files @robertvi It's good to run the test script at: https://github.com/ModECI/modelspec/blob/development/test_all.sh before committing as that will run the precommit hooks, including formatting with black. Tests will fail if the formatting is not done. See https://mdf.readthedocs.io/en/latest/api/Contributing.html#step-7 --- examples/sbml/sbml32spec.py | 245 +++++++++++++++--------- examples/sbml/sbml_validators.py | 98 ++++++---- examples/sbml/test_minimal_example.json | 2 +- examples/sbml/test_sbml3.py | 15 +- 4 files changed, 229 insertions(+), 131 deletions(-) diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py index 400cff3f..997607e0 100644 --- a/examples/sbml/sbml32spec.py +++ b/examples/sbml/sbml32spec.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -''' +""" initial attempt at creating an SBML API using modelspec https://github.com/combine-org/compbiolibs/issues/28 based on sbml.level-3.version-2.core.release-2.pdf -''' +""" import modelspec from modelspec import field, instance_of, optional @@ -14,25 +14,40 @@ from sbml_validators import * + @modelspec.define class Notes(Base): - ''' + """ XHTML field of SBase Args: xmlns: str fixed "http://www.w3.org/1999/xhtml" content: str valid XHTML - ''' - xmlns: str = field(default="http://www.w3.org/1999/xhtml",validator=[instance_of(str),xmlns_notes]) - content: str = field(default=None,validator=optional([instance_of(str),valid_xhtml])) + """ + + xmlns: str = field( + default="http://www.w3.org/1999/xhtml", + validator=[instance_of(str), xmlns_notes], + ) + content: str = field( + default=None, validator=optional([instance_of(str), valid_xhtml]) + ) + @modelspec.define class Math(Base): - ''' + """ Subset of MathML 2.0 used to define all formulae in SBML - ''' - xmlns: str = field(default="http://www.w3.org/1998/Math/MathML",validator=[instance_of(str),xmlns_math]) - content: str = field(default=None,validator=optional([instance_of(str),valid_mathml])) + """ + + xmlns: str = field( + default="http://www.w3.org/1998/Math/MathML", + validator=[instance_of(str), xmlns_math], + ) + content: str = field( + default=None, validator=optional([instance_of(str), valid_mathml]) + ) + @modelspec.define class SBase(Base): @@ -49,44 +64,57 @@ class SBase(Base): annotation: XML content optional """ - sid: str = field(default=None,validator=optional([instance_of(str),valid_sid])) - name: str = field(default=None,validator=optional(instance_of(str))) - metaid: str = field(default=None,validator=optional([instance_of(str),valid_xml_id])) - sboTerm: str = field(default=None,validator=optional([instance_of(str),valid_sbo])) + sid: str = field(default=None, validator=optional([instance_of(str), valid_sid])) + name: str = field(default=None, validator=optional(instance_of(str))) + metaid: str = field( + default=None, validator=optional([instance_of(str), valid_xml_id]) + ) + sboTerm: str = field( + default=None, validator=optional([instance_of(str), valid_sbo]) + ) + + notes: Notes = field(default=None, validator=optional(instance_of(Notes))) + annotation: str = field( + default=None, validator=optional([instance_of(str), valid_xml_content]) + ) - notes: Notes = field(default=None,validator=optional(instance_of(Notes))) - annotation: str = field(default=None,validator=optional([instance_of(str),valid_xml_content])) @modelspec.define class Trigger(SBase): - initialValue: bool = field(default=None,validator=instance_of(bool)) - persistent: bool = field(default=None,validator=instance_of(bool)) - math: str = field(default=None,validator=optional(instance_of(str))) + initialValue: bool = field(default=None, validator=instance_of(bool)) + persistent: bool = field(default=None, validator=instance_of(bool)) + math: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class Priority(SBase): - math: str = field(default=None,validator=optional(instance_of(str))) + math: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class Delay(SBase): - math: str = field(default=None,validator=optional(instance_of(str))) + math: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class EventAssignment(SBase): - ''' + """ Args: variable: SIdRef - ''' - math: str = field(default=None,validator=optional(instance_of(str))) - variable: str = field(default=None,validator=optional(instance_of(str))) + """ + + math: str = field(default=None, validator=optional(instance_of(str))) + variable: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class Event(SBase): - useValuesFromTriggerTime: bool = field(default=None,validator=instance_of(bool)) - trigger: Trigger = field(default=None, validator=optional(instance_of(Trigger))) + useValuesFromTriggerTime: bool = field(default=None, validator=instance_of(bool)) + trigger: Trigger = field(default=None, validator=optional(instance_of(Trigger))) priority: Priority = field(default=None, validator=optional(instance_of(Priority))) - delay: Delay = field(default=None, validator=optional(instance_of(Delay))) - listOfEventAssignments: List[EventAssignment] = field(factory=list) + delay: Delay = field(default=None, validator=optional(instance_of(Delay))) + listOfEventAssignments: List[EventAssignment] = field(factory=list) + @modelspec.define class SimpleSpeciesReference(SBase): @@ -97,11 +125,13 @@ class SimpleSpeciesReference(SBase): species: SIdRef """ - species: str = field(default=None,validator=instance_of(str)) + species: str = field(default=None, validator=instance_of(str)) + @modelspec.define class ModifierSpeciesReference(SimpleSpeciesReference): - '' + """""" + @modelspec.define class SpeciesReference(SimpleSpeciesReference): @@ -111,8 +141,9 @@ class SpeciesReference(SimpleSpeciesReference): constant: boolean """ - stoichiometry: float = field(default=None,validator=optional(instance_of(float))) - constant: bool = field(default=None,validator=instance_of(bool)) + stoichiometry: float = field(default=None, validator=optional(instance_of(float))) + constant: bool = field(default=None, validator=instance_of(bool)) + @modelspec.define class LocalParameter(SBase): @@ -121,17 +152,18 @@ class LocalParameter(SBase): units: UnitSIdRef optional """ - value: float = field(default=None,validator=optional(instance_of(float))) - units: str = field(default=None,validator=optional(instance_of(str))) + value: float = field(default=None, validator=optional(instance_of(float))) + units: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class KineticLaw(SBase): - """ - """ + """ """ + + math: str = field(default=None, validator=optional(instance_of(str))) - math: str = field(default=None,validator=optional(instance_of(str))) + listOfLocalParameters: List[LocalParameter] = field(factory=list) - listOfLocalParameters: List[LocalParameter] = field(factory=list) @modelspec.define class Reaction(SBase): @@ -143,14 +175,17 @@ class Reaction(SBase): compartment: SIdRef optional """ - reversible: bool = field(default=None,validator=instance_of(bool)) - compartment: str = field(default=None,validator=optional(instance_of(str))) + reversible: bool = field(default=None, validator=instance_of(bool)) + compartment: str = field(default=None, validator=optional(instance_of(str))) - listOfReactants: List[SpeciesReference] = field(factory=list) - listOfProducts: List[SpeciesReference] = field(factory=list) - listOfModifiers: List[ModifierSpeciesReference] = field(factory=list) + listOfReactants: List[SpeciesReference] = field(factory=list) + listOfProducts: List[SpeciesReference] = field(factory=list) + listOfModifiers: List[ModifierSpeciesReference] = field(factory=list) + + kineticLaw: KineticLaw = field( + default=None, validator=optional(instance_of(KineticLaw)) + ) - kineticLaw: KineticLaw = field(default=None, validator=optional(instance_of(KineticLaw))) @modelspec.define class Constraint(SBase): @@ -162,8 +197,9 @@ class Constraint(SBase): message: XHTML 1.0 optional """ - math: str = field(default=None,validator=optional(instance_of(str))) - message: str = field(default=None,validator=optional(instance_of(str))) + math: str = field(default=None, validator=optional(instance_of(str))) + message: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class Rule(SBase): @@ -174,7 +210,8 @@ class Rule(SBase): math: MathML optional """ - math: str = field(default=None,validator=optional(instance_of(str))) + math: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class AlgebraicRule(Rule): @@ -182,6 +219,7 @@ class AlgebraicRule(Rule): An algebraic rule """ + @modelspec.define class AssignmentRule(Rule): """ @@ -191,7 +229,8 @@ class AssignmentRule(Rule): variable: SIdRef required """ - variable: str = field(default=None,validator=instance_of(str)) + variable: str = field(default=None, validator=instance_of(str)) + @modelspec.define class RateRule(Rule): @@ -202,7 +241,8 @@ class RateRule(Rule): variable: SIdRef required """ - variable: str = field(default=None,validator=instance_of(str)) + variable: str = field(default=None, validator=instance_of(str)) + @modelspec.define class InitialAssignment(SBase): @@ -214,8 +254,9 @@ class InitialAssignment(SBase): math: MathML optional """ - symbol: str = field(default=None,validator=instance_of(str)) - math: str = field(default=None,validator=optional(instance_of(str))) + symbol: str = field(default=None, validator=instance_of(str)) + math: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class Parameter(SBase): @@ -228,10 +269,11 @@ class Parameter(SBase): constant: boolean """ - constant: bool = field(default=None,validator=instance_of(bool)) + constant: bool = field(default=None, validator=instance_of(bool)) + + value: float = field(default=None, validator=optional(instance_of(float))) + units: str = field(default=None, validator=optional(instance_of(str))) - value: float = field(default=None,validator=optional(instance_of(float))) - units: str = field(default=None,validator=optional(instance_of(str))) @modelspec.define class Species(SBase): @@ -249,16 +291,19 @@ class Species(SBase): conversionFactor: SIdRef optional """ - compartment: str = field(default=None,validator=instance_of(str)) - hasOnlySubstanceUnits: bool = field(default=None,validator=instance_of(bool)) - boundaryCondition: bool = field(default=None,validator=instance_of(bool)) - constant: bool = field(default=None,validator=instance_of(bool)) + compartment: str = field(default=None, validator=instance_of(str)) + hasOnlySubstanceUnits: bool = field(default=None, validator=instance_of(bool)) + boundaryCondition: bool = field(default=None, validator=instance_of(bool)) + constant: bool = field(default=None, validator=instance_of(bool)) initialAmount: float = field(default=None, validator=optional(instance_of(float))) - initialConcentration: float = field(default=None, validator=optional(instance_of(float))) + initialConcentration: float = field( + default=None, validator=optional(instance_of(float)) + ) substanceUnits: str = field(default=None, validator=optional(instance_of(str))) conversionFactor: str = field(default=None, validator=optional(instance_of(str))) + @modelspec.define class Compartment(SBase): """ @@ -271,11 +316,14 @@ class Compartment(SBase): constant: whether size is fixed """ - constant: bool = field(default=None,validator=instance_of(bool)) + constant: bool = field(default=None, validator=instance_of(bool)) + + spatialDimensions: float = field( + default=None, validator=optional(instance_of(float)) + ) + size: float = field(default=None, validator=optional(instance_of(float))) + units: str = field(default=None, validator=optional(instance_of(str))) - spatialDimensions: float = field(default=None,validator=optional(instance_of(float))) - size: float = field(default=None,validator=optional(instance_of(float))) - units: str = field(default=None,validator=optional(instance_of(str))) @modelspec.define class Unit(SBase): @@ -290,11 +338,12 @@ class Unit(SBase): multiplier: double """ - kind: str = field(default=None,validator=[instance_of(str),valid_kind]) - exponent: str = field(default=1.0, validator=instance_of(float)) - scale: str = field(default=0, validator=instance_of(int)) + kind: str = field(default=None, validator=[instance_of(str), valid_kind]) + exponent: str = field(default=1.0, validator=instance_of(float)) + scale: str = field(default=0, validator=instance_of(int)) multiplier: str = field(default=1.0, validator=instance_of(float)) + @modelspec.define class UnitDefinition(SBase): """ @@ -305,9 +354,10 @@ class UnitDefinition(SBase): listOfUnits: List of units used to compose the definition """ - sid: str = field(default=None,validator=[instance_of(str),valid_unitsid]) + sid: str = field(default=None, validator=[instance_of(str), valid_unitsid]) listOfUnits: List[Unit] = field(factory=list) + @modelspec.define class FunctionDefinition(SBase): """ @@ -319,10 +369,11 @@ class FunctionDefinition(SBase): math: MathML function definition optional """ - sid: str = field(default=None,validator=[instance_of(str),valid_sid]) + sid: str = field(default=None, validator=[instance_of(str), valid_sid]) math: Math = field(default=None, validator=optional(instance_of(Math))) + @modelspec.define class Model(SBase): """ @@ -338,24 +389,39 @@ class Model(SBase): conversionFactor: SIdRef optional """ - substanceUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) - timeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) - volumeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) - areaUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) - lengthUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) - extentUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) - conversionFactor: str = field(default=None, validator=optional([instance_of(str),valid_unitsid])) + substanceUnits: str = field( + default=None, validator=optional([instance_of(str), valid_unitsid]) + ) + timeUnits: str = field( + default=None, validator=optional([instance_of(str), valid_unitsid]) + ) + volumeUnits: str = field( + default=None, validator=optional([instance_of(str), valid_unitsid]) + ) + areaUnits: str = field( + default=None, validator=optional([instance_of(str), valid_unitsid]) + ) + lengthUnits: str = field( + default=None, validator=optional([instance_of(str), valid_unitsid]) + ) + extentUnits: str = field( + default=None, validator=optional([instance_of(str), valid_unitsid]) + ) + conversionFactor: str = field( + default=None, validator=optional([instance_of(str), valid_unitsid]) + ) listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list) - listOfUnitDefinitions: List[UnitDefinition] = field(factory=list) - listOfCompartments: List[Compartment] = field(factory=list) - listOfSpecies: List[Species] = field(factory=list) - listOfParameters: List[Parameter] = field(factory=list) - listOfInitialAssignments: List[InitialAssignment] = field(factory=list) - listOfRules: List[Rule] = field(factory=list) - listOfConstraints: List[Constraint] = field(factory=list) - listOfReactions: List[Reaction] = field(factory=list) - listOfEvents: List[Event] = field(factory=list) + listOfUnitDefinitions: List[UnitDefinition] = field(factory=list) + listOfCompartments: List[Compartment] = field(factory=list) + listOfSpecies: List[Species] = field(factory=list) + listOfParameters: List[Parameter] = field(factory=list) + listOfInitialAssignments: List[InitialAssignment] = field(factory=list) + listOfRules: List[Rule] = field(factory=list) + listOfConstraints: List[Constraint] = field(factory=list) + listOfReactions: List[Reaction] = field(factory=list) + listOfEvents: List[Event] = field(factory=list) + @modelspec.define class SBML(SBase): @@ -372,8 +438,11 @@ class SBML(SBase): model: Optional model """ - xmlns: str = field(default="http://www.sbml.org/sbml/level3/version2/core",validator=[instance_of(str),xmlns_sbml]) - level: str = field(default="3",validator=[instance_of(str),fixed_level]) - version: str = field(default="2",validator=[instance_of(str),fixed_version]) + xmlns: str = field( + default="http://www.sbml.org/sbml/level3/version2/core", + validator=[instance_of(str), xmlns_sbml], + ) + level: str = field(default="3", validator=[instance_of(str), fixed_level]) + version: str = field(default="2", validator=[instance_of(str), fixed_version]) model: Model = field(default=None, validator=optional(instance_of(Model))) diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py index 008c49c3..73894926 100644 --- a/examples/sbml/sbml_validators.py +++ b/examples/sbml/sbml_validators.py @@ -1,88 +1,116 @@ -''' +""" functions used to validate the user assigned values of items these functions will generally be called by passing to the validator option of attrs.field -''' +""" import re from lxml import etree from io import StringIO -#sbml.level-3.version-2.core.release-2.pdf Table 2 -sbml_si_units=\ -''' +# sbml.level-3.version-2.core.release-2.pdf Table 2 +sbml_si_units = """ ampere coulomb gray joule litre mole radian steradian weber avogadro dimensionless henry katal lumen newton second tesla becquerel farad hertz kelvin lux ohm siemens volt candela gram item kilogram metre pascal sievert watt -'''.split() +""".split() + def valid_kind(instance, attribute, value): if not value in sbml_si_units: - raise ValueError(f"kind {value} must be one of the standard SI units: {sbml_si_units}") + raise ValueError( + f"kind {value} must be one of the standard SI units: {sbml_si_units}" + ) + def valid_mathml(instance, attribute, value): - 'http://www.w3.org/1998/Math/MathML' + "http://www.w3.org/1998/Math/MathML" + def xmlns_sbml(instance, attribute, value): if value != "http://www.sbml.org/sbml/level3/version2/core": - raise ValueError("xmlns must be 'http://www.sbml.org/sbml/level3/version2/core'") + raise ValueError( + "xmlns must be 'http://www.sbml.org/sbml/level3/version2/core'" + ) + def xmlns_notes(instance, attribute, value): if value != "http://www.w3.org/1999/xhtml": raise ValueError("xmlns must be 'http://www.w3.org/1999/xhtml'") + def xmlns_math(instance, attribute, value): if value != "http://www.w3.org/1998/Math/MathML": raise ValueError("xmlns must be 'http://www.w3.org/1998/Math/MathML'") + def fixed_level(instance, attribute, value): if value != "3": raise ValueError("this implementation only supports level 3") + def fixed_version(instance, attribute, value): if value != "2": raise ValueError("this implementation only supports level 2") + def valid_sid(instance, attribute, value): - if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value): - raise ValueError("an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*") + if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value): + raise ValueError( + "an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*" + ) + def valid_unitsid(instance, attribute, value): - 'same as sid except has a separate namespace' - if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value): - raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*") + "same as sid except has a separate namespace" + if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value): + raise ValueError( + "a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*" + ) + def valid_unitsid(instance, attribute, value): - if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value): - raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*") + if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value): + raise ValueError( + "a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*" + ) + def valid_sbo(instance, attribute, value): - if not re.fullmatch('SBO:[0-9]{7}',value): + if not re.fullmatch("SBO:[0-9]{7}", value): raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}") -def valid_xml_id(instance,attribute,value): - 'a valid XML 1.0 ID' - #not implemented yet: CombiningChar , Extender - #NameChar ::= letter | digit | '.' | '-' | '_' | ':' | CombiningChar | Extender - #ID ::= ( letter | '_' | ':' ) NameChar* - if not re.fullmatch('[A-Za-z_:][A-Za-z0-9._:-]*',value): +def valid_xml_id(instance, attribute, value): + "a valid XML 1.0 ID" + # not implemented yet: CombiningChar , Extender + # NameChar ::= letter | digit | '.' | '-' | '_' | ':' | CombiningChar | Extender + # ID ::= ( letter | '_' | ':' ) NameChar* + + if not re.fullmatch("[A-Za-z_:][A-Za-z0-9._:-]*", value): raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}") - -def valid_xhtml(instance,attribute,value): - 'use etree to validate XHTML, throw exception on error' + + +def valid_xhtml(instance, attribute, value): + "use etree to validate XHTML, throw exception on error" etree.parse(StringIO(value), etree.HTMLParser(recover=False)) -def valid_xml_content(instance,attribute,value): - 'stub' - if not re.search('<.*>',value): - raise ValueError(f"{value} doesn't look like XML (this validator is only a stub)") +def valid_xml_content(instance, attribute, value): + "stub" + + if not re.search("<.*>", value): + raise ValueError( + f"{value} doesn't look like XML (this validator is only a stub)" + ) -def valid_mathml(instance,attribute,value): - ''' + +def valid_mathml(instance, attribute, value): + """ stubvalidator for MathML note: see pdf section 4.3.2 for special rules for FunctionDefinition MathML versus all other MathML uses in SBML - ''' + """ - if not re.search('',value): - raise ValueError(f"{value} doesn't look like MathML (this validator is only a stub)") + if not re.search("", value): + raise ValueError( + f"{value} doesn't look like MathML (this validator is only a stub)" + ) diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json index e8419a1f..27e2a1f9 100644 --- a/examples/sbml/test_minimal_example.json +++ b/examples/sbml/test_minimal_example.json @@ -15,4 +15,4 @@ } ] } -} \ No newline at end of file +} diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py index d7c59338..1ee6dc18 100755 --- a/examples/sbml/test_sbml3.py +++ b/examples/sbml/test_sbml3.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -''' +""" derived from https://github.com/combine-org/draft-modelspec-sbml-api/blob/main/test_sbml32.py a minimalish sbml to xml example -''' +""" import json import yaml @@ -11,21 +11,22 @@ from sbml32spec import * + def test_example_sbml_minimal(): - 'aiming to match the xml file example_sbml_minimal.xml' + "aiming to match the xml file example_sbml_minimal.xml" path = "test_minimal_example" sbml_doc = SBML() - #open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4)) - - model = Model(substanceUnits="mole",timeUnits="second",extentUnits="mole") + # open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4)) + + model = Model(substanceUnits="mole", timeUnits="second", extentUnits="mole") sbml_doc.model = model unit_def = UnitDefinition(sid="per_second") model.listOfUnitDefinitions.append(unit_def) - unit = Unit(kind="second",exponent=-1.0) + unit = Unit(kind="second", exponent=-1.0) unit_def.listOfUnits.append(unit) sbml_doc.to_json_file(f"{path}.json") From 49d9fc6e1f70826b503c8be7e1515ca5203dc157 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Tue, 8 Aug 2023 13:15:17 +0100 Subject: [PATCH 08/32] Test 3.11 --- .github/workflows/ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69208b06..5b8706b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.7", "3.8", "3.9", "3.10"] + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ] runs-on: [ubuntu-latest, macos-latest, windows-latest] steps: @@ -34,9 +34,16 @@ jobs: with: python-version: ${{ matrix.python-version }} + + - name: Install h5py + if: ${{ matrix.python-version == '3.11' }} + run: | + #if [[ ${{ matrix.runs-on }} == *"macos"* ]]; then brew install graphviz ; fi + #if [[ ${{ matrix.runs-on }} == *"ubuntu"* ]]; then sudo apt-get install libhdf5-serial-dev liblzo2-dev -y; pip3 install --no-binary=h5py h5py ; fi + pip list + - name: Install package run: | - if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt-get install libhdf5-serial-dev liblzo2-dev -y; pip3 install --no-binary=h5py h5py ; fi python -m pip install --upgrade pip pip install .[dev] From c6e70f8e598a3e98b85e1bdd8d39c31589b4c11e Mon Sep 17 00:00:00 2001 From: Padraig Gleeson Date: Wed, 6 Sep 2023 14:07:21 +0100 Subject: [PATCH 09/32] Add comment --- docs/sphinx/source/api/Contributors.md | 2 +- test_all.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md index 66101321..3dfc0108 100644 --- a/docs/sphinx/source/api/Contributors.md +++ b/docs/sphinx/source/api/Contributors.md @@ -3,7 +3,7 @@ # Modelspec contributors This page list names and Github profiles of contributors to Modelspec, listed in no particular order. -This page is generated periodically, most recently on 2023-08-23. +This page is generated periodically, most recently on 2023-09-06. - Padraig Gleeson ([@pgleeson](https://github.com/pgleeson)) - Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin)) diff --git a/test_all.sh b/test_all.sh index 0a91b46b..0834fcd2 100755 --- a/test_all.sh +++ b/test_all.sh @@ -33,6 +33,7 @@ pytest tests -v ## Run OMV tests +# See https://github.com/OpenSourceBrain/osb-model-validation omv all -V From 9d3ffa911b1b1ac643526c467b9245c307785148 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Sep 2023 10:31:18 +0100 Subject: [PATCH 10/32] Update gha version --- .github/workflows/static.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index e146eb91..5720078f 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 From d03ffb2b55f1e6902432475f8d2b5a25aacda9c6 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Sep 2023 10:32:46 +0100 Subject: [PATCH 11/32] Bump version to v0.3.3 --- src/modelspec/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modelspec/__init__.py b/src/modelspec/__init__.py index 8f33cf5a..b8bd118b 100644 --- a/src/modelspec/__init__.py +++ b/src/modelspec/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.3.2" +__version__ = "0.3.3" from .base_types import Base, define, has, field, fields, optional, instance_of, in_ From 2964ff40ad21256f91bda830f6a6e3836e51d485 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Sep 2023 10:49:49 +0100 Subject: [PATCH 12/32] Some doc updates --- README.md | 4 +--- docs/sphinx/source/api/Introduction.md | 4 +--- docs/sphinx/source/api/Quickstart.md | 11 ++++++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 12721e5a..91de3fd5 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ # Modelspec - [![Continuous builds](https://github.com/ModECI/modelspec/actions/workflows/ci.yml/badge.svg)](https://github.com/ModECI/modelspec/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/modelspec)](https://pypi.org/project/modelspec/) - -Functionality for specifying the structure of models & enabling automatic serialization to them (e.g. in JSON and YAML format). +Functionality for specifying the allowed structure of models, facilitating the creation of APIs in Python for handling the models & enabling automatic serialization of instances of them (e.g. in JSON and YAML format). This package is being used by [NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) & [MDF](https://github.com/ModECI/MDF). diff --git a/docs/sphinx/source/api/Introduction.md b/docs/sphinx/source/api/Introduction.md index 12721e5a..91de3fd5 100644 --- a/docs/sphinx/source/api/Introduction.md +++ b/docs/sphinx/source/api/Introduction.md @@ -1,10 +1,8 @@ # Modelspec - [![Continuous builds](https://github.com/ModECI/modelspec/actions/workflows/ci.yml/badge.svg)](https://github.com/ModECI/modelspec/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/modelspec)](https://pypi.org/project/modelspec/) - -Functionality for specifying the structure of models & enabling automatic serialization to them (e.g. in JSON and YAML format). +Functionality for specifying the allowed structure of models, facilitating the creation of APIs in Python for handling the models & enabling automatic serialization of instances of them (e.g. in JSON and YAML format). This package is being used by [NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) & [MDF](https://github.com/ModECI/MDF). diff --git a/docs/sphinx/source/api/Quickstart.md b/docs/sphinx/source/api/Quickstart.md index 832b149a..b6adbf02 100644 --- a/docs/sphinx/source/api/Quickstart.md +++ b/docs/sphinx/source/api/Quickstart.md @@ -15,18 +15,19 @@ More details, and importantly, how to set up a [virtual environment](https://vir ### Simple example -A basic example which illustrates how to create the specification for a document(model) and create serialized instances can be found [here](examples/README). +A basic example which illustrates how to create the specification for a document (i.e. the model definition for a document) and create serialized instances can be found [here](examples/README). ### Serialization formats -Python scripts can be used to generate the specification of a type of model(e.g. [this](https://github.com/ModECI/modelspec/blob/main/examples/document.py)), but the models are saved in standardized format in either text based [JSON](https://github.com/ModECI/modelspec/blob/main/examples/document.json) or [YAML](https://github.com/ModECI/modelspec/blob/main/examples/document.yaml) formats or in binary [BSON](https://github.com/ModECI/modelspec/blob/main/examples/document.bson) format. Support for XML serialization is under development. +Python scripts can be used to generate the specification of a type of model (e.g. [this](https://github.com/ModECI/modelspec/blob/main/examples/document.py)), but the models are saved in standardized format in either text based ([JSON](https://github.com/ModECI/modelspec/blob/main/examples/document.json) or [YAML](https://github.com/ModECI/modelspec/blob/main/examples/document.yaml)) formats or in binary ([BSON](https://github.com/ModECI/modelspec/blob/main/examples/document.bson)) format. Support for [XML](https://github.com/ModECI/modelspec/blob/main/examples/document.xml) serialization has recently been added. ### Current formats using modelspec -#### MDF +#### MDF (Model Description Format) -MDF uses modelspec to create the structure of its models and convert the models into serialized formats such as JSON, YAML, and BSON. +[ModECI's MDF](https://modeci.org/quickstart) uses modelspec to create the structure of its models and convert the models into serialized formats such as JSON, YAML, and BSON. #### NeuroML -NeuroMLlite uses modelspec to create the structure of its models and convert the models into serialize formats such as JSON, YAML, and BSON. +[NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) uses modelspec to create the structure of its models and convert the models into serialize formats such as JSON, YAML, and BSON. +The XML serialisation of modelspec will be useful for integrating NeuroML 2 files into the framework, see [here](https://github.com/ModECI/modelspec/tree/main/examples/neuroml2). From 18ea90bcf48cf67689c90b682bc288dd88d1654f Mon Sep 17 00:00:00 2001 From: pgleeson Date: Mon, 18 Sep 2023 13:02:44 +0100 Subject: [PATCH 13/32] Update testing on save/load json/yaml --- examples/test/test.md | 35 ++++++++++++++++++++++++ examples/test/test.py | 38 +++++++++++++++++++++++++-- examples/test/test.specification.yaml | 15 +++++++++++ examples/test/test_instance.json | 7 +++++ examples/test/test_instance.xml | 2 ++ examples/test/test_instance.yaml | 5 ++-- src/modelspec/base_types.py | 15 +++++++++++ tests/test_base.py | 20 ++++++++++++-- 8 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 examples/test/test_instance.json create mode 100644 examples/test/test_instance.xml diff --git a/examples/test/test.md b/examples/test/test.md index 366f1b45..f81c64cd 100644 --- a/examples/test/test.md +++ b/examples/test/test.md @@ -24,4 +24,39 @@ A model.... + + float_like_optional2 + int + name also says it all... + + + + + mid + MidClassNoId + + + + + + +## MidClassNoId +A model.... + +### Allowed parameters + + + + + + + + + + + + + + +
int_valintname says it all...
str_valintname says it all...
diff --git a/examples/test/test.py b/examples/test/test.py index f14d8165..b52e23c1 100644 --- a/examples/test/test.py +++ b/examples/test/test.py @@ -1,6 +1,7 @@ import modelspec from modelspec import field, instance_of, optional from modelspec.base_types import Base +from modelspec.utils import load_json from typing import List # Example testing multiple options... @@ -17,6 +18,20 @@ def convert2float(x: Any) -> float: return None +@modelspec.define +class MidClassNoId(Base): + """ + A model.... + + Args: + int_val: name says it all... + str_val: name says it all... + """ + + int_val: int = field(default=None, validator=instance_of(int)) + str_val: int = field(default=None, validator=optional(instance_of(str))) + + @modelspec.define class TopClass(Base): """ @@ -26,6 +41,7 @@ class TopClass(Base): id: The unique id of the thing float_like_req: name says it all... float_like_optional: name also says it all... + float_like_optional2: name also says it all... """ id: str = field(validator=instance_of(str)) @@ -35,19 +51,37 @@ class TopClass(Base): float_like_optional: int = field( default=None, validator=optional(instance_of(float)), converter=convert2float ) + float_like_optional2: int = field( + default=None, validator=optional(instance_of(float)), converter=convert2float + ) + + mid: MidClassNoId = field( + default=None, validator=optional(instance_of(MidClassNoId)) + ) tc = TopClass( - id="MyTest", float_like_req="03" + id="MyTest", float_like_req="04" ) # a string which can be converted to a float... # tc.float_like_req = 2.01 -tc.float_like_optional = 43 +tc.float_like_optional = 44 +tc.float_like_optional2 = 66 +# tc.mid = MidClassNoId(int_val=4, str_val="three") print(tc) tc.to_yaml_file("test_instance.yaml") +tc.to_json_file("test_instance.json") +tc.to_xml_file("test_instance.xml") + +str_b4 = str(tc) + +str_json_after = TopClass.from_json_file("test_instance.json") +print(str_json_after) +str_yaml_after = TopClass.from_yaml_file("test_instance.yaml") +print(str_yaml_after) doc_md = tc.generate_documentation(format="markdown") diff --git a/examples/test/test.specification.yaml b/examples/test/test.specification.yaml index ca4fe7b2..07ee7946 100644 --- a/examples/test/test.specification.yaml +++ b/examples/test/test.specification.yaml @@ -10,3 +10,18 @@ TopClass: float_like_optional: type: int description: name also says it all... + float_like_optional2: + type: int + description: name also says it all... + mid: + type: MidClassNoId + description: '' +MidClassNoId: + definition: A model.... + allowed_parameters: + int_val: + type: int + description: name says it all... + str_val: + type: int + description: name says it all... diff --git a/examples/test/test_instance.json b/examples/test/test_instance.json new file mode 100644 index 00000000..05941ece --- /dev/null +++ b/examples/test/test_instance.json @@ -0,0 +1,7 @@ +{ + "MyTest": { + "float_like_req": 4.0, + "float_like_optional": 44.0, + "float_like_optional2": 66.0 + } +} diff --git a/examples/test/test_instance.xml b/examples/test/test_instance.xml new file mode 100644 index 00000000..facb4731 --- /dev/null +++ b/examples/test/test_instance.xml @@ -0,0 +1,2 @@ + + diff --git a/examples/test/test_instance.yaml b/examples/test/test_instance.yaml index 51f8b261..c5497868 100644 --- a/examples/test/test_instance.yaml +++ b/examples/test/test_instance.yaml @@ -1,3 +1,4 @@ MyTest: - float_like_req: 3.0 - float_like_optional: 43.0 + float_like_req: 4.0 + float_like_optional: 44.0 + float_like_optional2: 66.0 diff --git a/src/modelspec/base_types.py b/src/modelspec/base_types.py index f4a81a9d..8f9d64b8 100644 --- a/src/modelspec/base_types.py +++ b/src/modelspec/base_types.py @@ -156,11 +156,26 @@ def from_dict(cls, d: Dict[str, Any]) -> "Base": else: return converter.structure(d, cls) + @classmethod + def from_yaml(cls, yaml_str: str) -> "Base": + """Instantiate an modelspec object from a YAML string""" + return cls.from_dict(yaml.load(yaml_str, Loader=yaml.SafeLoader)) + + @classmethod + def from_yaml_file(cls, yaml_file: str) -> "Base": + """Instantiate an modelspec object from a file containing YAML""" + return cls.from_dict(yaml.load(yaml_str, Loader=yaml.SafeLoader)) + @classmethod def from_json(cls, json_str: str) -> "Base": """Instantiate an modelspec object from a JSON string""" return cls.from_dict(json.loads(json_str)) + @classmethod + def from_json_file(cls, json_file: str) -> "Base": + """Instantiate an modelspec object from a file containing JSON""" + return cls.from_dict(json.load(json_file)) + @classmethod def from_bson(cls, bson_str: str) -> "Base": """Instantiate an modelspec object from a BSON string""" diff --git a/tests/test_base.py b/tests/test_base.py index 54a5bbb4..1237f254 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -162,8 +162,8 @@ def test_save_load_json(tmp_path): # net.id = net.id+'_yaml' net.to_yaml_file(filenamey) - # filenamex = str(Path(tmp_path) / f"{net.id}.xml") - # net.to_xml_file(filenamex) + filenamex = str(Path(tmp_path) / f"{net.id}.xml") + net.to_xml_file(filenamex) from modelspec.utils import load_json, load_yaml, load_xml @@ -173,12 +173,24 @@ def test_save_load_json(tmp_path): str_netj = str(netj) + netj2 = NewNetwork.from_json(net.to_json()) + str_netj2 = str(netj2) + + netj3 = NewNetwork.from_json_file(filenamej) + str_netj3 = str(netj3) + datay = load_yaml(filenamey) print_v("Loaded network specification from %s" % filenamey) nety = NewNetwork.from_dict(datay) str_nety = str(nety) + nety2 = NewNetwork.from_yaml(net.to_yaml() + " ") + str_nety2 = str(nety2) + + nety3 = NewNetwork.from_yaml_file(filenamey) + str_nety3 = str(nety3) + # datax = load_xml(filenamex) # print_v("Loaded network specification from %s" % filenamex) @@ -201,6 +213,8 @@ def test_save_load_json(tmp_path): ) # Order not preserved in py2, just test len else: assert str_orig == str_netj + assert str_orig == str_netj2 + assert str_orig == str_netj3 print("Test YAML..") if sys.version_info[0] == 2: @@ -209,6 +223,8 @@ def test_save_load_json(tmp_path): ) # Order not preserved in py2, just test len else: assert str_orig == str_nety + assert str_orig == str_nety2 + assert str_orig == str_nety3 # print("Test XML..") # if sys.version_info[0] == 2: From d65bc18b32df78875c9d234ddd2b9a6119ccdc23 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Mon, 18 Sep 2023 15:48:17 +0100 Subject: [PATCH 14/32] Minimal working sbml example Update a number of print statements --- docs/sphinx/source/api/examples/document.xml | 4 +- examples/document.xml | 4 +- examples/neuroml2/TestNeuroML.xml | 2 +- examples/neuroml2/neuroml2_spec.py | 2 +- examples/sbml/regenerateAndTest.sh | 7 + examples/sbml/test_minimal_example.json | 23 ++-- examples/sbml/test_minimal_example.xml | 4 + examples/sbml/test_minimal_example.yaml | 7 + examples/sbml/test_sbml3.py | 19 ++- examples/test/test.md | 8 +- examples/test/test.py | 27 ++-- examples/test/test.specification.yaml | 8 +- examples/test/test_instance.json | 5 +- examples/test/test_instance.xml | 6 +- examples/test/test_instance.yaml | 4 +- src/modelspec/base_types.py | 20 ++- src/modelspec/utils.py | 128 ++++++++++--------- 17 files changed, 158 insertions(+), 120 deletions(-) create mode 100755 examples/sbml/regenerateAndTest.sh create mode 100644 examples/sbml/test_minimal_example.xml create mode 100644 examples/sbml/test_minimal_example.yaml diff --git a/docs/sphinx/source/api/examples/document.xml b/docs/sphinx/source/api/examples/document.xml index 4641d46e..164039f1 100644 --- a/docs/sphinx/source/api/examples/document.xml +++ b/docs/sphinx/source/api/examples/document.xml @@ -1,5 +1,5 @@ - - + +
diff --git a/examples/document.xml b/examples/document.xml index 4641d46e..164039f1 100644 --- a/examples/document.xml +++ b/examples/document.xml @@ -1,5 +1,5 @@ - - + +
diff --git a/examples/neuroml2/TestNeuroML.xml b/examples/neuroml2/TestNeuroML.xml index 70586cf6..6319bb51 100644 --- a/examples/neuroml2/TestNeuroML.xml +++ b/examples/neuroml2/TestNeuroML.xml @@ -1,4 +1,4 @@ - + diff --git a/examples/neuroml2/neuroml2_spec.py b/examples/neuroml2/neuroml2_spec.py index 6b07fda5..36422ab5 100644 --- a/examples/neuroml2/neuroml2_spec.py +++ b/examples/neuroml2/neuroml2_spec.py @@ -197,7 +197,7 @@ class neuroml(Base): with open("NeuroML2.specification.yaml", "w") as d: yy = yaml.dump(doc_dict, indent=4, sort_keys=False) - print(yy) + # print(yy) d.write(yy) from modelspec.utils import load_xml diff --git a/examples/sbml/regenerateAndTest.sh b/examples/sbml/regenerateAndTest.sh new file mode 100755 index 00000000..198da2b6 --- /dev/null +++ b/examples/sbml/regenerateAndTest.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -ex + +python test_sbml3.py + +python sbml_validators.py example_sbml_minimal.xml +python sbml_validators.py test_minimal_example.xml diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json index 27e2a1f9..75b6be4c 100644 --- a/examples/sbml/test_minimal_example.json +++ b/examples/sbml/test_minimal_example.json @@ -1,18 +1,11 @@ { - "model": { - "substanceUnits": "mole", - "timeUnits": "second", - "extentUnits": "mole", - "listOfUnitDefinitions": [ - { - "sid": "per_second", - "listOfUnits": [ - { - "kind": "second", - "exponent": -1.0 - } - ] - } - ] + "test_minimal_example": { + "level": "3", + "version": "2", + "model": { + "substanceUnits": "mole", + "timeUnits": "second", + "extentUnits": "mole" + } } } diff --git a/examples/sbml/test_minimal_example.xml b/examples/sbml/test_minimal_example.xml new file mode 100644 index 00000000..4ee2729c --- /dev/null +++ b/examples/sbml/test_minimal_example.xml @@ -0,0 +1,4 @@ + + + + diff --git a/examples/sbml/test_minimal_example.yaml b/examples/sbml/test_minimal_example.yaml new file mode 100644 index 00000000..c07f26c5 --- /dev/null +++ b/examples/sbml/test_minimal_example.yaml @@ -0,0 +1,7 @@ +test_minimal_example: + level: '3' + version: '2' + model: + substanceUnits: mole + timeUnits: second + extentUnits: mole diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py index 1ee6dc18..0f93ff62 100755 --- a/examples/sbml/test_sbml3.py +++ b/examples/sbml/test_sbml3.py @@ -15,22 +15,27 @@ def test_example_sbml_minimal(): "aiming to match the xml file example_sbml_minimal.xml" - path = "test_minimal_example" + name = "test_minimal_example" - sbml_doc = SBML() + sbml_doc = sbml(id=name, version="2", level="3") # open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4)) model = Model(substanceUnits="mole", timeUnits="second", extentUnits="mole") sbml_doc.model = model - unit_def = UnitDefinition(sid="per_second") + unit_def = unitDefinition(sid="per_second") + """ model.listOfUnitDefinitions.append(unit_def) - unit = Unit(kind="second", exponent=-1.0) - unit_def.listOfUnits.append(unit) + unit_def.listOfUnits.append(unit)""" + + print("------------------------------------------------") + print(sbml_doc) + print("------------------------------------------------") - sbml_doc.to_json_file(f"{path}.json") - sbml_doc.to_xml_file(f"{path}.xml") + sbml_doc.to_json_file(f"{name}.json") + sbml_doc.to_yaml_file(f"{name}.yaml") + sbml_doc.to_xml_file(f"{name}.xml") if __name__ == "__main__": diff --git a/examples/test/test.md b/examples/test/test.md index f81c64cd..ccfd2fe6 100644 --- a/examples/test/test.md +++ b/examples/test/test.md @@ -12,20 +12,20 @@ A model.... float_like_req - int + float name says it all... float_like_optional - int + float name also says it all... - float_like_optional2 + int_like_optional int name also says it all... @@ -54,7 +54,7 @@ A model.... str_val - int + str name says it all... diff --git a/examples/test/test.py b/examples/test/test.py index b52e23c1..a995ec2b 100644 --- a/examples/test/test.py +++ b/examples/test/test.py @@ -10,7 +10,7 @@ def convert2float(x: Any) -> float: - print("Converting {} ({})".format(x, type(x))) + print("convert2float {} ({})".format(x, type(x))) """Convert to float if not None""" if x is not None: return float(x) @@ -18,6 +18,15 @@ def convert2float(x: Any) -> float: return None +def convert2int(x: Any) -> int: + print("convert2int {} ({})".format(x, type(x))) + """Convert to int if not None""" + if x is not None: + return int(x) + else: + return None + + @modelspec.define class MidClassNoId(Base): """ @@ -29,7 +38,7 @@ class MidClassNoId(Base): """ int_val: int = field(default=None, validator=instance_of(int)) - str_val: int = field(default=None, validator=optional(instance_of(str))) + str_val: str = field(default=None, validator=optional(instance_of(str))) @modelspec.define @@ -41,18 +50,18 @@ class TopClass(Base): id: The unique id of the thing float_like_req: name says it all... float_like_optional: name also says it all... - float_like_optional2: name also says it all... + int_like_optional: name also says it all... """ id: str = field(validator=instance_of(str)) - float_like_req: int = field( + float_like_req: float = field( default=None, validator=instance_of(float), converter=convert2float ) - float_like_optional: int = field( + float_like_optional: float = field( default=None, validator=optional(instance_of(float)), converter=convert2float ) - float_like_optional2: int = field( - default=None, validator=optional(instance_of(float)), converter=convert2float + int_like_optional: int = field( + default=None, validator=optional(instance_of(int)), converter=convert2int ) mid: MidClassNoId = field( @@ -66,8 +75,8 @@ class TopClass(Base): # tc.float_like_req = 2.01 tc.float_like_optional = 44 -tc.float_like_optional2 = 66 -# tc.mid = MidClassNoId(int_val=4, str_val="three") +# tc.float_like_optional2 = 66 +tc.mid = MidClassNoId(int_val=4, str_val="three") print(tc) diff --git a/examples/test/test.specification.yaml b/examples/test/test.specification.yaml index 07ee7946..4ffaa7aa 100644 --- a/examples/test/test.specification.yaml +++ b/examples/test/test.specification.yaml @@ -5,12 +5,12 @@ TopClass: type: str description: The unique id of the thing float_like_req: - type: int + type: float description: name says it all... float_like_optional: - type: int + type: float description: name also says it all... - float_like_optional2: + int_like_optional: type: int description: name also says it all... mid: @@ -23,5 +23,5 @@ MidClassNoId: type: int description: name says it all... str_val: - type: int + type: str description: name says it all... diff --git a/examples/test/test_instance.json b/examples/test/test_instance.json index 05941ece..7b8b0878 100644 --- a/examples/test/test_instance.json +++ b/examples/test/test_instance.json @@ -2,6 +2,9 @@ "MyTest": { "float_like_req": 4.0, "float_like_optional": 44.0, - "float_like_optional2": 66.0 + "mid": { + "int_val": 4, + "str_val": "three" + } } } diff --git a/examples/test/test_instance.xml b/examples/test/test_instance.xml index facb4731..4895ba5e 100644 --- a/examples/test/test_instance.xml +++ b/examples/test/test_instance.xml @@ -1,2 +1,4 @@ - - + + + + diff --git a/examples/test/test_instance.yaml b/examples/test/test_instance.yaml index c5497868..1a3bcd49 100644 --- a/examples/test/test_instance.yaml +++ b/examples/test/test_instance.yaml @@ -1,4 +1,6 @@ MyTest: float_like_req: 4.0 float_like_optional: 44.0 - float_like_optional2: 66.0 + mid: + int_val: 4 + str_val: three diff --git a/src/modelspec/base_types.py b/src/modelspec/base_types.py index 8f9d64b8..5dbe8f48 100644 --- a/src/modelspec/base_types.py +++ b/src/modelspec/base_types.py @@ -57,11 +57,13 @@ def print_(text: str, print_it: bool = False): """ Print a message preceded by modelspec, only if print_it=True """ + if not print_it: + return + prefix = "modelspec >>> " if not isinstance(text, str): text = ("%s" % text).decode("ascii") - if print_it: - print("{}{}".format(prefix, text.replace("\n", "\n" + prefix))) + print("{}{}".format(prefix, text.replace("\n", "\n" + prefix))) def print_v(text: str): @@ -128,11 +130,14 @@ def to_xml(self) -> str: root = build_xml_element(self) xml_string = ET.tostring( - root, encoding="utf-8", xml_declaration=False, method="xml" + root, encoding="UTF-8", xml_declaration=True, method="xml" ).decode("utf-8") parsed_xml = xml.dom.minidom.parseString(xml_string) pretty_xml = parsed_xml.toprettyxml(indent=" " * 4) + pretty_xml = pretty_xml.replace( + '?xml version="1.0" ?', '?xml version="1.0" encoding="UTF-8"?' + ) return pretty_xml @classmethod @@ -318,14 +323,7 @@ def to_xml_file( if filename is None: filename = f"{self.id}.xml" - root = build_xml_element(self) - - # Generate the XML string - xml_str = ET.tostring(root, encoding="utf-8", method="xml").decode("utf-8") - - # Create a pretty-formatted XML string using minidom - parsed_xml = xml.dom.minidom.parseString(xml_str) - pretty_xml_str = parsed_xml.toprettyxml(indent=" " * 4) + pretty_xml_str = self.to_xml() # Write the XML data to the file with open(filename, "w", encoding="utf-8") as file: diff --git a/src/modelspec/utils.py b/src/modelspec/utils.py index 16844cb1..29f9275a 100644 --- a/src/modelspec/utils.py +++ b/src/modelspec/utils.py @@ -14,7 +14,7 @@ from modelspec.base_types import EvaluableExpression from random import Random -from typing import Union +from typing import Union, Dict verbose = False @@ -247,8 +247,12 @@ def build_xml_element(data, parent=None): if parent is None: parent = ET.Element(data.__class__.__name__) + print_(" == Converting to XML: %s" % data, verbose) + attrs = attr.fields(data.__class__) for aattr in attrs: + + print_(" == Looking at: {} ({})".format(aattr, type(aattr)), verbose) if isinstance(aattr.default, attr.Factory): children = data.__getattribute__(aattr.name) if not isinstance(children, (list, tuple)): @@ -258,11 +262,32 @@ def build_xml_element(data, parent=None): child_element = build_xml_element(child) parent.append(child_element) - # Filters name space and schemaLoacation attributes, only allows non name space attributes to be added as attributes + # Filters name space and schemaLocation attributes, only allows non name space attributes to be added as attributes elif not isinstance(aattr.default, str): attribute_name = aattr.name attribute_value = data.__getattribute__(aattr.name) - parent.set(attribute_name, str(attribute_value)) + print_( + f" -- {attribute_name} = {attribute_value} (is type: {type(attribute_value)}, should be type:{aattr.type}))", + verbose, + ) + if attribute_value is not None: + if ( + type(attribute_value) == int + or type(attribute_value) == float + or type(attribute_value) == str + or type(attribute_value) == bool + or type(attribute_value) == list + ): + parent.set(attribute_name, str(attribute_value)) + elif type(attribute_value) == dict: + + """for k, v in attribute_value.items(): + child_element = build_xml_element(v)""" + else: + child_element = build_xml_element(attribute_value) + child_element.tag = attribute_name + # + parent.append(child_element) # This defines the various namespaces and schemaLocation of the generated xml if hasattr(data, "xmlns"): @@ -285,13 +310,12 @@ def ascii_encode_dict(data): def _parse_element(dict_format, to_build): - if verbose: - print("Parse for element: [%s]" % dict_format) + print_("Parse for element: [%s]" % dict_format, verbose) for k in dict_format.keys(): - if verbose: - print( - " Setting id: {} in {} ({})".format(k, type.__name__, type(to_build)) - ) + print_( + " Setting id: {} in {} ({})".format(k, type.__name__, type(to_build)), + verbose, + ) to_build.id = k to_build = _parse_attributes(dict_format[k], to_build) @@ -303,10 +327,10 @@ def _parse_attributes(dict_format, to_build): for key in dict_format: value = dict_format[key] new_format = True - if verbose: - print( - " Setting {}={} ({}) in {}".format(key, value, type(value), to_build) - ) + print_( + " Setting {}={} ({}) in {}".format(key, value, type(value), to_build), + verbose, + ) if new_format: if type(to_build) == dict: @@ -316,8 +340,7 @@ def _parse_attributes(dict_format, to_build): type_to_use = to_build.allowed_children[key][1] for v in value: ff = type_to_use() - if verbose: - print(f" Type for {key}: {type_to_use} ({ff})") + print_(f" Type for {key}: {type_to_use} ({ff})", verbose) ff = _parse_element({v: value[v]}, ff) exec("to_build.%s.append(ff)" % key) else: @@ -332,13 +355,11 @@ def _parse_attributes(dict_format, to_build): to_build.__setattr__(key, value) else: type_to_use = to_build.allowed_fields[key][1] - if verbose: - print( - "type_to_use: {} ({})".format( - type_to_use, type(type_to_use) - ) - ) - print(f"- {key} = {value}") + print_( + "type_to_use: {} ({})".format(type_to_use, type(type_to_use)), + verbose, + ) + print_(f"- {key} = {value}", verbose) if type_to_use == EvaluableExpression: vv = {} @@ -449,19 +470,17 @@ def evaluate( if array_format == FORMAT_TENSORFLOW: import tensorflow as tf - if verbose: - print_( - " > Evaluating: [%s] which is a: %s, vs parameters: %s (using %s arrays)..." - % (expr, type(expr).__name__, _params_info(parameters), array_format), - verbose, - ) + print_( + " > Evaluating: [%s] which is a: %s, vs parameters: %s (using %s arrays)..." + % (expr, type(expr).__name__, _params_info(parameters), array_format), + verbose, + ) try: if type(expr) == str and expr in parameters: expr = parameters[ expr ] # replace with the value in parameters & check whether it's float/int... - if verbose: - print_(" Using for that param: %s" % _val_info(expr), verbose) + print_(" Using for that param: %s" % _val_info(expr), verbose) if type(expr) == str: try: @@ -480,41 +499,34 @@ def evaluate( pass if type(expr) == list: - if verbose: - print_(" Returning a list in format: %s" % array_format, verbose) + print_(" Returning a list in format: %s" % array_format, verbose) if array_format == FORMAT_TENSORFLOW: return tf.constant(expr, dtype=tf.float64) else: return np.array(expr) if type(expr) == np.ndarray: - if verbose: - print_( - " Returning a numpy array in format: %s" % array_format, verbose - ) + print_(" Returning a numpy array in format: %s" % array_format, verbose) if array_format == FORMAT_TENSORFLOW: return tf.convert_to_tensor(expr, dtype=tf.float64) else: return np.array(expr) if "Tensor" in type(expr).__name__: - if verbose: - print_( - " Returning a tensorflow Tensor in format: %s" % array_format, - verbose, - ) + print_( + " Returning a tensorflow Tensor in format: %s" % array_format, + verbose, + ) if array_format == FORMAT_NUMPY: return expr.numpy() else: return expr if int(expr) == expr and cast_to_int: - if verbose: - print_(" Returning int: %s" % int(expr), verbose) + print_(" Returning int: %s" % int(expr), verbose) return int(expr) else: # will have failed if not number - if verbose: - print_(" Returning {}: {}".format(type(expr), expr), verbose) + print_(" Returning {}: {}".format(type(expr), expr), verbose) return expr except: try: @@ -531,25 +543,22 @@ def evaluate( if type(expr) == str and "numpy." in expr: parameters["numpy"] = np - if verbose: - print_( - " Trying to eval [%s] with Python using %s..." - % (expr, parameters.keys()), - verbose, - ) + print_( + " Trying to eval [%s] with Python using %s..." + % (expr, parameters.keys()), + verbose, + ) v = eval(expr, parameters) - if verbose: - print_( - " Evaluated with Python: {} = {}".format(expr, _val_info(v)), - verbose, - ) + print_( + " Evaluated with Python: {} = {}".format(expr, _val_info(v)), + verbose, + ) if (type(v) == float or type(v) == str) and int(v) == v: - if verbose: - print_(" Returning int: %s" % int(v), verbose) + print_(" Returning int: %s" % int(v), verbose) if array_format == FORMAT_TENSORFLOW: return tf.constant(int(v)) @@ -557,8 +566,7 @@ def evaluate( return int(v) return v except Exception as e: - if verbose: - print_(f" Returning without altering: {expr} (error: {e})", verbose) + print_(f" Returning without altering: {expr} (error: {e})", verbose) return expr From af43a938165fe2a1b71025174386122812024c46 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Mon, 18 Sep 2023 15:50:01 +0100 Subject: [PATCH 15/32] Updates to core sbml model definition --- docs/sphinx/source/api/Contributors.md | 2 +- examples/sbml/example_sbml_minimal.xml | 2 +- examples/sbml/sbml32spec.py | 20 +++++--- examples/sbml/sbml_validators.py | 69 ++++++++++++++++++++++++-- test_all.sh | 5 ++ 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md index 969b553c..4885bfc2 100644 --- a/docs/sphinx/source/api/Contributors.md +++ b/docs/sphinx/source/api/Contributors.md @@ -3,7 +3,7 @@ # Modelspec contributors This page list names and Github profiles of contributors to Modelspec, listed in no particular order. -This page is generated periodically, most recently on 2023-09-13. +This page is generated periodically, most recently on 2023-09-18. - Padraig Gleeson ([@pgleeson](https://github.com/pgleeson)) - Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin)) diff --git a/examples/sbml/example_sbml_minimal.xml b/examples/sbml/example_sbml_minimal.xml index fe9b1524..f1a8fdfe 100644 --- a/examples/sbml/example_sbml_minimal.xml +++ b/examples/sbml/example_sbml_minimal.xml @@ -1,5 +1,5 @@ - + diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py index 997607e0..1716b285 100644 --- a/examples/sbml/sbml32spec.py +++ b/examples/sbml/sbml32spec.py @@ -66,6 +66,7 @@ class SBase(Base): sid: str = field(default=None, validator=optional([instance_of(str), valid_sid])) name: str = field(default=None, validator=optional(instance_of(str))) + metaid: str = field( default=None, validator=optional([instance_of(str), valid_xml_id]) ) @@ -79,6 +80,12 @@ class SBase(Base): ) +@modelspec.define +class SBaseWithId(SBase): + + id: str = field(default=None, validator=optional([instance_of(str), valid_sid])) + + @modelspec.define class Trigger(SBase): initialValue: bool = field(default=None, validator=instance_of(bool)) @@ -345,7 +352,7 @@ class Unit(SBase): @modelspec.define -class UnitDefinition(SBase): +class unitDefinition(SBase): """ A unit definition @@ -412,7 +419,7 @@ class Model(SBase): ) listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list) - listOfUnitDefinitions: List[UnitDefinition] = field(factory=list) + listOfUnitDefinitions: List[unitDefinition] = field(factory=list) listOfCompartments: List[Compartment] = field(factory=list) listOfSpecies: List[Species] = field(factory=list) listOfParameters: List[Parameter] = field(factory=list) @@ -424,7 +431,7 @@ class Model(SBase): @modelspec.define -class SBML(SBase): +class sbml(SBaseWithId): """ The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4. @@ -439,10 +446,11 @@ class SBML(SBase): """ xmlns: str = field( - default="http://www.sbml.org/sbml/level3/version2/core", + default="http://www.sbml.org/sbml/level3/version%s/core" % SBML_VERSION, validator=[instance_of(str), xmlns_sbml], ) - level: str = field(default="3", validator=[instance_of(str), fixed_level]) - version: str = field(default="2", validator=[instance_of(str), fixed_version]) + level: str = field(default=None, validator=[instance_of(str), fixed_level]) + version: str = field(default=None, validator=[instance_of(str), fixed_version]) model: Model = field(default=None, validator=optional(instance_of(Model))) + # models: List[Model] = field(factory=list) diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py index 73894926..941704f2 100644 --- a/examples/sbml/sbml_validators.py +++ b/examples/sbml/sbml_validators.py @@ -6,6 +6,7 @@ import re from lxml import etree from io import StringIO +import sys # sbml.level-3.version-2.core.release-2.pdf Table 2 sbml_si_units = """ @@ -13,6 +14,9 @@ second tesla becquerel farad hertz kelvin lux ohm siemens volt candela gram item kilogram metre pascal sievert watt """.split() +SBML_VERSION = "2" +SBML_LEVEL = "3" + def valid_kind(instance, attribute, value): if not value in sbml_si_units: @@ -43,13 +47,13 @@ def xmlns_math(instance, attribute, value): def fixed_level(instance, attribute, value): - if value != "3": - raise ValueError("this implementation only supports level 3") + if value != SBML_LEVEL: + raise ValueError("this implementation only supports level %s" % SBML_LEVEL) def fixed_version(instance, attribute, value): - if value != "2": - raise ValueError("this implementation only supports level 2") + if value != SBML_VERSION: + raise ValueError("this implementation only supports version %s" % SBML_VERSION) def valid_sid(instance, attribute, value): @@ -114,3 +118,60 @@ def valid_mathml(instance, attribute, value): raise ValueError( f"{value} doesn't look like MathML (this validator is only a stub)" ) + + +# from: https://github.com/combine-org/combine-notebooks + + +def validate_sbml(doc, units_consistency: bool = False) -> None: + """Validate sbml.""" + import libsbml + + # set the unit checking, similar for the other settings + doc.setConsistencyChecks(libsbml.LIBSBML_CAT_UNITS_CONSISTENCY, units_consistency) + doc.checkConsistency() + # get errors/warnings + n_errors: int = doc.getNumErrors() + errors: List[libsbml.SBMLError] = list() + warnings: List[libsbml.SBMLError] = list() + for k in range(n_errors): + error: libsbml.SBMLError = doc.getError(k) + severity = error.getSeverity() + if (severity == libsbml.LIBSBML_SEV_ERROR) or ( + severity == libsbml.LIBSBML_SEV_FATAL + ): + errors.append(error) + else: + warnings.append(error) + # print results + print("-" * 80) + print(f"{'validation error(s)':<25}: {len(errors)}") + print(f"{'validation warning(s)':<25}: {len(warnings)}") + if len(errors) > 0: + print("--- errors ---") + for kerr in enumerate(errors): + print(f"E{kerr}: {error.getCategoryAsString()} L{error.getLine()}") + print( + f"[{error.getSeverityAsString()}] {error.getShortMessage()} | {error.getMessage()}" + ) + if len(warnings) > 0: + print("--- warnings ---") + for kwarn in enumerate(warnings): + print(f"E{kwarn}: {error.getCategoryAsString()} L{error.getLine()}") + print( + f"[{error.getSeverityAsString()}] {error.getShortMessage()} | {error.getMessage()}" + ) + print("-" * 80) + + +if __name__ == "__main__": + + import libsbml + + sbml_file = sys.argv[1] + + print("Going to validate SBML file: %s" % sbml_file) + reader = libsbml.SBMLReader() + document = reader.readSBML(sbml_file) + + validate_sbml(document, units_consistency=False) diff --git a/test_all.sh b/test_all.sh index 0834fcd2..f646a3e4 100755 --- a/test_all.sh +++ b/test_all.sh @@ -24,6 +24,11 @@ python neuroml2_spec.py pynml -validate hello_world_neuroml.net.nml pynml -validate TestNeuroML.xml +## Test SBML example + +cd ../sbml +./regenerateAndTest.sh + cd ../.. From 5c1eba6edefdcbd5e1cf0d99bf8cc1dc22a55421 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Mon, 18 Sep 2023 16:36:10 +0100 Subject: [PATCH 16/32] Don't test xml examples on py3.7 --- .github/workflows/ci.yml | 13 ++++++++++--- examples/test/test.py | 9 ++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3d8cf85..49374e01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: run: | pip list - - name: Run some examples + - name: Run simple examples run: | cd examples python document.py @@ -55,13 +55,20 @@ jobs: cd test python test.py - ## Test NeuroML example + - name: Test NeuroML examples + if: ${{ matrix.python-version != '3.7'}} - cd ../neuroml2 + cd examples/neuroml2 python neuroml2_spec.py # Note: NeuroML files will be validated with OMV below + - name: Test SBML examples + if: ${{ matrix.python-version != '3.7'}} + + cd examples/sbml + ./regenerateAndTest.sh + - name: Run pytest run: | pytest tests diff --git a/examples/test/test.py b/examples/test/test.py index a995ec2b..ce820a7f 100644 --- a/examples/test/test.py +++ b/examples/test/test.py @@ -3,10 +3,11 @@ from modelspec.base_types import Base from modelspec.utils import load_json from typing import List +from typing import Any +import sys -# Example testing multiple options... -from typing import Any +# Example testing multiple options... def convert2float(x: Any) -> float: @@ -83,7 +84,9 @@ class TopClass(Base): tc.to_yaml_file("test_instance.yaml") tc.to_json_file("test_instance.json") -tc.to_xml_file("test_instance.xml") + +if sys.version_info >= (3, 8): + tc.to_xml_file("test_instance.xml") str_b4 = str(tc) From 53c86e9cf01c52ba7c76030a1a06cf328c637706 Mon Sep 17 00:00:00 2001 From: Padraig Gleeson Date: Mon, 18 Sep 2023 16:38:32 +0100 Subject: [PATCH 17/32] Update ci.yml --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49374e01..49c77d85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,7 @@ jobs: - name: Test NeuroML examples if: ${{ matrix.python-version != '3.7'}} + run: | cd examples/neuroml2 python neuroml2_spec.py @@ -65,6 +66,7 @@ jobs: - name: Test SBML examples if: ${{ matrix.python-version != '3.7'}} + run: | cd examples/sbml ./regenerateAndTest.sh From f5c7445bb50472ee11f1870d8b1b71b706d404ae Mon Sep 17 00:00:00 2001 From: pgleeson Date: Mon, 18 Sep 2023 17:16:18 +0100 Subject: [PATCH 18/32] Add sbml requirements --- setup.cfg | 1 + tests/test_base.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 8d8f84cf..cc44eee2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -84,6 +84,7 @@ dev = flake8 pyneuroml>=0.7.2 NeuroMLlite>=0.5.3 + python-libsbml modelspec[test] all = diff --git a/tests/test_base.py b/tests/test_base.py index 1237f254..3cd6fddb 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -162,8 +162,9 @@ def test_save_load_json(tmp_path): # net.id = net.id+'_yaml' net.to_yaml_file(filenamey) - filenamex = str(Path(tmp_path) / f"{net.id}.xml") - net.to_xml_file(filenamex) + if sys.version_info >= (3, 8): + filenamex = str(Path(tmp_path) / f"{net.id}.xml") + net.to_xml_file(filenamex) from modelspec.utils import load_json, load_yaml, load_xml From 1064af305feef66376c0b836eeb91066780f2e66 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Mon, 18 Sep 2023 18:33:11 +0100 Subject: [PATCH 19/32] Generates same example sbml; restructured handling of listOf... classes Can certainly be made more succinct, but generating valid SBML --- examples/sbml/example_sbml_minimal.xml | 3 ++ examples/sbml/sbml32spec.py | 42 ++++++++++++++++++++----- examples/sbml/sbml_validators.py | 15 ++++++--- examples/sbml/test_minimal_example.json | 16 +++++++++- examples/sbml/test_minimal_example.xml | 10 +++++- examples/sbml/test_minimal_example.yaml | 7 +++++ examples/sbml/test_sbml3.py | 14 ++++++--- 7 files changed, 88 insertions(+), 19 deletions(-) diff --git a/examples/sbml/example_sbml_minimal.xml b/examples/sbml/example_sbml_minimal.xml index f1a8fdfe..c3315f41 100644 --- a/examples/sbml/example_sbml_minimal.xml +++ b/examples/sbml/example_sbml_minimal.xml @@ -1,5 +1,8 @@ + + + diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py index 1716b285..c6372a91 100644 --- a/examples/sbml/sbml32spec.py +++ b/examples/sbml/sbml32spec.py @@ -333,7 +333,7 @@ class Compartment(SBase): @modelspec.define -class Unit(SBase): +class unit(SBase): """ A unit used to compose a unit definition. unit = (multiplier x 10^scale x kind)^exponent @@ -352,17 +352,41 @@ class Unit(SBase): @modelspec.define -class unitDefinition(SBase): +class ListOfUnits(SBase): + """ + A listOfUnits + + Args: + listOfUnits: the actual list Of Units + """ + + listOfUnits: List[unit] = field(factory=list) + + +@modelspec.define +class unitDefinition(SBaseWithId): """ A unit definition Args: - sid: UnitSid required (overrides SBase sid) listOfUnits: List of units used to compose the definition """ - sid: str = field(default=None, validator=[instance_of(str), valid_unitsid]) - listOfUnits: List[Unit] = field(factory=list) + listOfUnits: ListOfUnits = field( + default=None, validator=optional(instance_of(ListOfUnits)) + ) + + +@modelspec.define +class ListOfUnitDefinitions(SBase): + """ + A listOfUnitDefinitions + + Args: + listOfUnitDefinitions: the actual list Of Unit Definitions + """ + + listOfUnitDefinitions: List[unitDefinition] = field(factory=list) @modelspec.define @@ -419,7 +443,11 @@ class Model(SBase): ) listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list) - listOfUnitDefinitions: List[unitDefinition] = field(factory=list) + + listOfUnitDefinitions: ListOfUnitDefinitions = field( + default=None, validator=optional(instance_of(ListOfUnitDefinitions)) + ) + listOfCompartments: List[Compartment] = field(factory=list) listOfSpecies: List[Species] = field(factory=list) listOfParameters: List[Parameter] = field(factory=list) @@ -435,7 +463,7 @@ class sbml(SBaseWithId): """ The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4. - http://www.sbml.org/sbml/level3/version2/core + http://www.sbml.org/sbml/level3/version2/ Args: xmlns: string, fixed to "http://www.sbml.org/sbml/level3/version2/core" diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py index 941704f2..5ab66a5b 100644 --- a/examples/sbml/sbml_validators.py +++ b/examples/sbml/sbml_validators.py @@ -57,10 +57,11 @@ def fixed_version(instance, attribute, value): def valid_sid(instance, attribute, value): - if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value): - raise ValueError( - "an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*" - ) + if value is not None: + if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value): + raise ValueError( + "an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*" + ) def valid_unitsid(instance, attribute, value): @@ -163,6 +164,8 @@ def validate_sbml(doc, units_consistency: bool = False) -> None: ) print("-" * 80) + return len(warnings) + len(errors) + if __name__ == "__main__": @@ -174,4 +177,6 @@ def validate_sbml(doc, units_consistency: bool = False) -> None: reader = libsbml.SBMLReader() document = reader.readSBML(sbml_file) - validate_sbml(document, units_consistency=False) + issues = validate_sbml(document, units_consistency=False) + + exit(issues) diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json index 75b6be4c..e83375b6 100644 --- a/examples/sbml/test_minimal_example.json +++ b/examples/sbml/test_minimal_example.json @@ -5,7 +5,21 @@ "model": { "substanceUnits": "mole", "timeUnits": "second", - "extentUnits": "mole" + "extentUnits": "mole", + "listOfUnitDefinitions": { + "listOfUnitDefinitions": { + "per_second": { + "listOfUnits": { + "listOfUnits": [ + { + "kind": "second", + "exponent": -1.0 + } + ] + } + } + } + } } } } diff --git a/examples/sbml/test_minimal_example.xml b/examples/sbml/test_minimal_example.xml index 4ee2729c..29bf4ff4 100644 --- a/examples/sbml/test_minimal_example.xml +++ b/examples/sbml/test_minimal_example.xml @@ -1,4 +1,12 @@ - + + + + + + + + + diff --git a/examples/sbml/test_minimal_example.yaml b/examples/sbml/test_minimal_example.yaml index c07f26c5..e0916973 100644 --- a/examples/sbml/test_minimal_example.yaml +++ b/examples/sbml/test_minimal_example.yaml @@ -5,3 +5,10 @@ test_minimal_example: substanceUnits: mole timeUnits: second extentUnits: mole + listOfUnitDefinitions: + listOfUnitDefinitions: + per_second: + listOfUnits: + listOfUnits: + - kind: second + exponent: -1.0 diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py index 0f93ff62..50f595a3 100755 --- a/examples/sbml/test_sbml3.py +++ b/examples/sbml/test_sbml3.py @@ -23,11 +23,15 @@ def test_example_sbml_minimal(): model = Model(substanceUnits="mole", timeUnits="second", extentUnits="mole") sbml_doc.model = model - unit_def = unitDefinition(sid="per_second") - """ - model.listOfUnitDefinitions.append(unit_def) - unit = Unit(kind="second", exponent=-1.0) - unit_def.listOfUnits.append(unit)""" + unit_def = unitDefinition(id="per_second") + + model.listOfUnitDefinitions = ListOfUnitDefinitions() + + model.listOfUnitDefinitions.listOfUnitDefinitions.append(unit_def) + + unit_s = unit(kind="second", exponent=-1.0) + unit_def.listOfUnits = ListOfUnits() + unit_def.listOfUnits.listOfUnits.append(unit_s) print("------------------------------------------------") print(sbml_doc) From 757a45031a3e05688412cac5037d243040d134c6 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Tue, 19 Sep 2023 10:33:54 +0100 Subject: [PATCH 20/32] Generate spec docs for sbml --- .gitignore | 1 + docs/sphinx/source/api/Contributors.md | 2 +- examples/COMBINE.md | 20 + examples/sbml/SBML.md | 2266 ++++++++++++++++++++++++ examples/sbml/SBML.rst | 885 +++++++++ examples/sbml/SBML.specification.json | 1006 +++++++++++ examples/sbml/SBML.specification.yaml | 735 ++++++++ examples/sbml/test_sbml3.py | 25 + 8 files changed, 4939 insertions(+), 1 deletion(-) create mode 100644 examples/COMBINE.md create mode 100644 examples/sbml/SBML.md create mode 100644 examples/sbml/SBML.rst create mode 100644 examples/sbml/SBML.specification.json create mode 100644 examples/sbml/SBML.specification.yaml diff --git a/.gitignore b/.gitignore index 3ce81332..e7c235dc 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,4 @@ cython_debug/ .idea/ /examples/document.specification.bson /examples/neuroml2/hello_world.v.dat +/examples/sbml/example_sbml_test.xml diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md index 4885bfc2..51d4661e 100644 --- a/docs/sphinx/source/api/Contributors.md +++ b/docs/sphinx/source/api/Contributors.md @@ -3,7 +3,7 @@ # Modelspec contributors This page list names and Github profiles of contributors to Modelspec, listed in no particular order. -This page is generated periodically, most recently on 2023-09-18. +This page is generated periodically, most recently on 2023-09-19. - Padraig Gleeson ([@pgleeson](https://github.com/pgleeson)) - Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin)) diff --git a/examples/COMBINE.md b/examples/COMBINE.md new file mode 100644 index 00000000..72e6ad4e --- /dev/null +++ b/examples/COMBINE.md @@ -0,0 +1,20 @@ +# COMBINE standards in modelspec + +To illustrate the utility of modelspec, and to create a uniform, compatible set of APIs across a diverse range of model specification formats, we are investigating the [COMBINE set of standards in computational biology](https://co.mbine.org/standards/), and attempting to express these formats in modelspec. + +## NeuroML + +A preliminary version of an API for [NeuroML2](https://docs.neuroml.org/Userdocs/NeuroMLv2.html) can be found [here](NeuroML). + +## SBML + +work towards an API for [SBML](https://www.sbml.org/) can be found [here](SBML). + +## CellML + +TODO... + +## SED-ML + +TODO... + diff --git a/examples/sbml/SBML.md b/examples/sbml/SBML.md new file mode 100644 index 00000000..6d2eb680 --- /dev/null +++ b/examples/sbml/SBML.md @@ -0,0 +1,2266 @@ +## sbml +The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4. +http://www.sbml.org/sbml/level3/version2/ + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
idstr
xmlnsstr string, fixed to "http://www.sbml.org/sbml/level3/version2/core"
levelstr SBML level, fixed to 3
versionstrSBML version, fixed to 2
modelModel Optional model
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Model +The model + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
substanceUnitsstr UnitSIdRef optional
timeUnitsstr UnitSIdRef optional
volumeUnitsstr UnitSIdRef optional
areaUnitsstr UnitSIdRef optional
lengthUnitsstr UnitSIdRef optional
extentUnitsstr UnitSIdRef optional
conversionFactorstrSIdRef optional
listOfUnitDefinitionsListOfUnitDefinitions
+ +### Allowed children + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
listOfFunctionDefinitionsFunctionDefinition
listOfCompartmentsCompartment
listOfSpeciesSpecies
listOfParametersParameter
listOfInitialAssignmentsInitialAssignment
listOfRulesRule
listOfConstraintsConstraint
listOfReactionsReaction
listOfEventsEvent
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## ListOfUnitDefinitions +A listOfUnitDefinitions + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
+ +### Allowed children + + + + + + + + +
listOfUnitDefinitionsunitDefinitionthe actual list Of Unit Definitions
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## unitDefinition +A unit definition + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
idstr
listOfUnitsListOfUnitsList of units used to compose the definition
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## ListOfUnits +A listOfUnits + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
+ +### Allowed children + + + + + + + + +
listOfUnitsunitthe actual list Of Units
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## unit +A unit used to compose a unit definition. unit = (multiplier x 10^scale x kind)^exponent + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
kindstrbase unit (base or derived SI units only, see Table 2 of the SBML spec)
exponentstrdouble
scalestrinteger
multiplierstrdouble
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## FunctionDefinition +A function definition using MathML + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
sidstr SId optional
mathMathMathML function definition optional
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Math +Subset of MathML 2.0 used to define all formulae in SBML + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstr
contentstr
+ +## Compartment +A compartment + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
constantboolwhether size is fixed
spatialDimensionsfloateg 3 for three dimensional space etc
sizefloatinitial size of compartment
unitsstrunits being used to define the compartment's size
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Species +A species: entities of the same kind participating in reactions within a specific compartment + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
compartmentstrSIdRef
hasOnlySubstanceUnitsboolboolean
boundaryConditionboolboolean
constantboolboolean
initialAmountfloatdouble optional
initialConcentrationfloatdouble optional
substanceUnitsstrUnitSIdRef optional
conversionFactorstrSIdRef optional
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Parameter +A parameter + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
constantboolboolean
valuefloatdouble optional
unitsstrUnitSIdRef optional
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## InitialAssignment +An initial assignment + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
symbolstrSIdRef required
mathstrMathML optional
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Rule +A rule, either algebraic, assignment or rate + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
mathstrMathML optional
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Constraint +A model constraint + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
mathstr MathML optional
messagestrXHTML 1.0 optional
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Reaction +A model reaction + +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
reversibleboolboolean
compartmentstrSIdRef optional
kineticLawKineticLaw
+ +### Allowed children + + + + + + + + + + + + + + + + + + + + + + +
listOfReactantsSpeciesReference
listOfProductsSpeciesReference
listOfModifiersModifierSpeciesReference
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## KineticLaw +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
mathstr
+ +### Allowed children + + + + + + + + +
listOfLocalParametersLocalParameter
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## LocalParameter +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
valuefloat
unitsstrUnitSIdRef optional
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## SpeciesReference +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
speciesstrSIdRef
stoichiometryfloatdouble optional
constantboolboolean
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## SpeciesReference +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
speciesstrSIdRef
stoichiometryfloatdouble optional
constantboolboolean
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## ModifierSpeciesReference +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
speciesstrSIdRef
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Event +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
useValuesFromTriggerTimebool
triggerTrigger
priorityPriority
delayDelay
+ +### Allowed children + + + + + + + + +
listOfEventAssignmentsEventAssignment
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Trigger +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
initialValuebool
persistentbool
mathstr
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Priority +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
mathstr
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## Delay +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
mathstr
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ +## EventAssignment +### Allowed parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
sidstr SId optional
namestr string optional
metaidstr XML ID optional
sboTermstrSBOTerm optional
notesNotes XHTML 1.0 optional
annotationstrXML content optional
mathstr
variablestrSIdRef
+ +## Notes +XHTML field of SBase + +### Allowed parameters + + + + + + + + + + + + + + + +
xmlnsstrstr fixed "http://www.w3.org/1999/xhtml"
contentstrstr valid XHTML
+ diff --git a/examples/sbml/SBML.rst b/examples/sbml/SBML.rst new file mode 100644 index 00000000..5d446b71 --- /dev/null +++ b/examples/sbml/SBML.rst @@ -0,0 +1,885 @@ +==== +sbml +==== +The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4. +http://www.sbml.org/sbml/level3/version2/ + +**Allowed parameters** + +=============== ======================================= ================================================================ +Allowed field Data Type Description +=============== ======================================= ================================================================ +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**id** str +**xmlns** str string, fixed to "http://www.sbml.org/sbml/level3/version2/core" +**level** str SBML level, fixed to 3 +**version** str SBML version, fixed to 2 +**model** ` <#model>`__ Optional model +=============== ======================================= ================================================================ + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +===== +Model +===== +The model + +**Allowed parameters** + +========================= ======================================================================= ==================== +Allowed field Data Type Description +========================= ======================================================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**substanceUnits** str UnitSIdRef optional +**timeUnits** str UnitSIdRef optional +**volumeUnits** str UnitSIdRef optional +**areaUnits** str UnitSIdRef optional +**lengthUnits** str UnitSIdRef optional +**extentUnits** str UnitSIdRef optional +**conversionFactor** str SIdRef optional +**listOfUnitDefinitions** ` <#listofunitdefinitions>`__ +========================= ======================================================================= ==================== + +**Allowed children** + +============================= ============================================ ============= +Allowed child Data Type Description +============================= ============================================ ============= +**listOfFunctionDefinitions** `FunctionDefinition <#functiondefinition>`__ +**listOfCompartments** `Compartment <#compartment>`__ +**listOfSpecies** `Species <#species>`__ +**listOfParameters** `Parameter <#parameter>`__ +**listOfInitialAssignments** `InitialAssignment <#initialassignment>`__ +**listOfRules** `Rule <#rule>`__ +**listOfConstraints** `Constraint <#constraint>`__ +**listOfReactions** `Reaction <#reaction>`__ +**listOfEvents** `Event <#event>`__ +============================= ============================================ ============= + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +===================== +ListOfUnitDefinitions +===================== +A listOfUnitDefinitions + +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +=============== ======================================= ==================== + +**Allowed children** + +========================= ==================================== =================================== +Allowed child Data Type Description +========================= ==================================== =================================== +**listOfUnitDefinitions** `unitDefinition <#unitdefinition>`__ the actual list Of Unit Definitions +========================= ==================================== =================================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +============== +unitDefinition +============== +A unit definition + +**Allowed parameters** + +=============== =================================================== ============================================ +Allowed field Data Type Description +=============== =================================================== ============================================ +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**id** str +**listOfUnits** ` <#listofunits>`__ List of units used to compose the definition +=============== =================================================== ============================================ + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +=========== +ListOfUnits +=========== +A listOfUnits + +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +=============== ======================================= ==================== + +**Allowed children** + +=============== ================ ======================== +Allowed child Data Type Description +=============== ================ ======================== +**listOfUnits** `unit <#unit>`__ the actual list Of Units +=============== ================ ======================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +==== +unit +==== +A unit used to compose a unit definition. unit = (multiplier x 10^scale x kind)^exponent + +**Allowed parameters** + +=============== ======================================= ======================================================================= +Allowed field Data Type Description +=============== ======================================= ======================================================================= +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**kind** str base unit (base or derived SI units only, see Table 2 of the SBML spec) +**exponent** str double +**scale** str integer +**multiplier** str double +=============== ======================================= ======================================================================= + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +================== +FunctionDefinition +================== +A function definition using MathML + +**Allowed parameters** + +=============== ======================================= =================================== +Allowed field Data Type Description +=============== ======================================= =================================== +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**sid** str SId optional +**math** ` <#math>`__ MathML function definition optional +=============== ======================================= =================================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +==== +Math +==== +Subset of MathML 2.0 used to define all formulae in SBML + +**Allowed parameters** + +=============== =========== ============= +Allowed field Data Type Description +=============== =========== ============= +**xmlns** str +**content** str +=============== =========== ============= + +=========== +Compartment +=========== +A compartment + +**Allowed parameters** + +===================== ======================================= ================================================= +Allowed field Data Type Description +===================== ======================================= ================================================= +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**constant** bool whether size is fixed +**spatialDimensions** float eg 3 for three dimensional space etc +**size** float initial size of compartment +**units** str units being used to define the compartment's size +===================== ======================================= ================================================= + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +======= +Species +======= +A species: entities of the same kind participating in reactions within a specific compartment + +**Allowed parameters** + +========================= ======================================= ==================== +Allowed field Data Type Description +========================= ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**compartment** str SIdRef +**hasOnlySubstanceUnits** bool boolean +**boundaryCondition** bool boolean +**constant** bool boolean +**initialAmount** float double optional +**initialConcentration** float double optional +**substanceUnits** str UnitSIdRef optional +**conversionFactor** str SIdRef optional +========================= ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +========= +Parameter +========= +A parameter + +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**constant** bool boolean +**value** float double optional +**units** str UnitSIdRef optional +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +================= +InitialAssignment +================= +An initial assignment + +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**symbol** str SIdRef required +**math** str MathML optional +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +==== +Rule +==== +A rule, either algebraic, assignment or rate + +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**math** str MathML optional +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +========== +Constraint +========== +A model constraint + +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**math** str MathML optional +**message** str XHTML 1.0 optional +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +======== +Reaction +======== +A model reaction + +**Allowed parameters** + +=============== ================================================= ==================== +Allowed field Data Type Description +=============== ================================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**reversible** bool boolean +**compartment** str SIdRef optional +**kineticLaw** ` <#kineticlaw>`__ +=============== ================================================= ==================== + +**Allowed children** + +=================== ======================================================== ============= +Allowed child Data Type Description +=================== ======================================================== ============= +**listOfReactants** `SpeciesReference <#speciesreference>`__ +**listOfProducts** `SpeciesReference <#speciesreference>`__ +**listOfModifiers** `ModifierSpeciesReference <#modifierspeciesreference>`__ +=================== ======================================================== ============= + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +========== +KineticLaw +========== +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**math** str +=============== ======================================= ==================== + +**Allowed children** + +========================= ==================================== ============= +Allowed child Data Type Description +========================= ==================================== ============= +**listOfLocalParameters** `LocalParameter <#localparameter>`__ +========================= ==================================== ============= + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +============== +LocalParameter +============== +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**value** float +**units** str UnitSIdRef optional +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +================ +SpeciesReference +================ +**Allowed parameters** + +================= ======================================= ==================== +Allowed field Data Type Description +================= ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**species** str SIdRef +**stoichiometry** float double optional +**constant** bool boolean +================= ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +================ +SpeciesReference +================ +**Allowed parameters** + +================= ======================================= ==================== +Allowed field Data Type Description +================= ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**species** str SIdRef +**stoichiometry** float double optional +**constant** bool boolean +================= ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +======================== +ModifierSpeciesReference +======================== +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**species** str SIdRef +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +===== +Event +===== +**Allowed parameters** + +============================ ============================================= ==================== +Allowed field Data Type Description +============================ ============================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**useValuesFromTriggerTime** bool +**trigger** ` <#trigger>`__ +**priority** ` <#priority>`__ +**delay** ` <#delay>`__ +============================ ============================================= ==================== + +**Allowed children** + +========================== ====================================== ============= +Allowed child Data Type Description +========================== ====================================== ============= +**listOfEventAssignments** `EventAssignment <#eventassignment>`__ +========================== ====================================== ============= + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +======= +Trigger +======= +**Allowed parameters** + +================ ======================================= ==================== +Allowed field Data Type Description +================ ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**initialValue** bool +**persistent** bool +**math** str +================ ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +======== +Priority +======== +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**math** str +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +===== +Delay +===== +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**math** str +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + +=============== +EventAssignment +=============== +**Allowed parameters** + +=============== ======================================= ==================== +Allowed field Data Type Description +=============== ======================================= ==================== +**sid** str SId optional +**name** str string optional +**metaid** str XML ID optional +**sboTerm** str SBOTerm optional +**notes** ` <#notes>`__ XHTML 1.0 optional +**annotation** str XML content optional +**math** str +**variable** str SIdRef +=============== ======================================= ==================== + +===== +Notes +===== +XHTML field of SBase + +**Allowed parameters** + +=============== =========== ======================================== +Allowed field Data Type Description +=============== =========== ======================================== +**xmlns** str str fixed "http://www.w3.org/1999/xhtml" +**content** str str valid XHTML +=============== =========== ======================================== + diff --git a/examples/sbml/SBML.specification.json b/examples/sbml/SBML.specification.json new file mode 100644 index 00000000..459a2c71 --- /dev/null +++ b/examples/sbml/SBML.specification.json @@ -0,0 +1,1006 @@ +{ + "sbml": { + "definition": "The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4.\nhttp://www.sbml.org/sbml/level3/version2/", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "id": { + "type": "str", + "description": "" + }, + "xmlns": { + "type": "str", + "description": " string, fixed to \"http://www.sbml.org/sbml/level3/version2/core\"" + }, + "level": { + "type": "str", + "description": " SBML level, fixed to 3" + }, + "version": { + "type": "str", + "description": "SBML version, fixed to 2" + }, + "model": { + "type": "Model", + "description": " Optional model" + } + } + }, + "Notes": { + "definition": "XHTML field of SBase", + "allowed_parameters": { + "xmlns": { + "type": "str", + "description": "str fixed \"http://www.w3.org/1999/xhtml\"" + }, + "content": { + "type": "str", + "description": "str valid XHTML" + } + } + }, + "Model": { + "definition": "The model", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "substanceUnits": { + "type": "str", + "description": " UnitSIdRef optional" + }, + "timeUnits": { + "type": "str", + "description": " UnitSIdRef optional" + }, + "volumeUnits": { + "type": "str", + "description": " UnitSIdRef optional" + }, + "areaUnits": { + "type": "str", + "description": " UnitSIdRef optional" + }, + "lengthUnits": { + "type": "str", + "description": " UnitSIdRef optional" + }, + "extentUnits": { + "type": "str", + "description": " UnitSIdRef optional" + }, + "conversionFactor": { + "type": "str", + "description": "SIdRef optional" + }, + "listOfUnitDefinitions": { + "type": "ListOfUnitDefinitions", + "description": "" + } + }, + "allowed_children": { + "listOfFunctionDefinitions": { + "type": "FunctionDefinition", + "description": "" + }, + "listOfCompartments": { + "type": "Compartment", + "description": "" + }, + "listOfSpecies": { + "type": "Species", + "description": "" + }, + "listOfParameters": { + "type": "Parameter", + "description": "" + }, + "listOfInitialAssignments": { + "type": "InitialAssignment", + "description": "" + }, + "listOfRules": { + "type": "Rule", + "description": "" + }, + "listOfConstraints": { + "type": "Constraint", + "description": "" + }, + "listOfReactions": { + "type": "Reaction", + "description": "" + }, + "listOfEvents": { + "type": "Event", + "description": "" + } + } + }, + "ListOfUnitDefinitions": { + "definition": "A listOfUnitDefinitions", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + } + }, + "allowed_children": { + "listOfUnitDefinitions": { + "type": "unitDefinition", + "description": "the actual list Of Unit Definitions" + } + } + }, + "unitDefinition": { + "definition": "A unit definition", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "id": { + "type": "str", + "description": "" + }, + "listOfUnits": { + "type": "ListOfUnits", + "description": "List of units used to compose the definition" + } + } + }, + "ListOfUnits": { + "definition": "A listOfUnits", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + } + }, + "allowed_children": { + "listOfUnits": { + "type": "unit", + "description": "the actual list Of Units" + } + } + }, + "unit": { + "definition": "A unit used to compose a unit definition. unit = (multiplier x 10^scale x kind)^exponent", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "kind": { + "type": "str", + "description": "base unit (base or derived SI units only, see Table 2 of the SBML spec)" + }, + "exponent": { + "type": "str", + "description": "double" + }, + "scale": { + "type": "str", + "description": "integer" + }, + "multiplier": { + "type": "str", + "description": "double" + } + } + }, + "FunctionDefinition": { + "definition": "A function definition using MathML", + "allowed_parameters": { + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "sid": { + "type": "str", + "description": " SId optional" + }, + "math": { + "type": "Math", + "description": "MathML function definition optional" + } + } + }, + "Math": { + "definition": "Subset of MathML 2.0 used to define all formulae in SBML", + "allowed_parameters": { + "xmlns": { + "type": "str", + "description": "" + }, + "content": { + "type": "str", + "description": "" + } + } + }, + "Compartment": { + "definition": "A compartment", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "constant": { + "type": "bool", + "description": "whether size is fixed" + }, + "spatialDimensions": { + "type": "float", + "description": "eg 3 for three dimensional space etc" + }, + "size": { + "type": "float", + "description": "initial size of compartment" + }, + "units": { + "type": "str", + "description": "units being used to define the compartment's size" + } + } + }, + "Species": { + "definition": "A species: entities of the same kind participating in reactions within a specific compartment", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "compartment": { + "type": "str", + "description": "SIdRef" + }, + "hasOnlySubstanceUnits": { + "type": "bool", + "description": "boolean" + }, + "boundaryCondition": { + "type": "bool", + "description": "boolean" + }, + "constant": { + "type": "bool", + "description": "boolean" + }, + "initialAmount": { + "type": "float", + "description": "double optional" + }, + "initialConcentration": { + "type": "float", + "description": "double optional" + }, + "substanceUnits": { + "type": "str", + "description": "UnitSIdRef optional" + }, + "conversionFactor": { + "type": "str", + "description": "SIdRef optional" + } + } + }, + "Parameter": { + "definition": "A parameter", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "constant": { + "type": "bool", + "description": "boolean" + }, + "value": { + "type": "float", + "description": "double optional" + }, + "units": { + "type": "str", + "description": "UnitSIdRef optional" + } + } + }, + "InitialAssignment": { + "definition": "An initial assignment", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "symbol": { + "type": "str", + "description": "SIdRef required" + }, + "math": { + "type": "str", + "description": "MathML optional" + } + } + }, + "Rule": { + "definition": "A rule, either algebraic, assignment or rate", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "math": { + "type": "str", + "description": "MathML optional" + } + } + }, + "Constraint": { + "definition": "A model constraint", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "math": { + "type": "str", + "description": " MathML optional" + }, + "message": { + "type": "str", + "description": "XHTML 1.0 optional" + } + } + }, + "Reaction": { + "definition": "A model reaction", + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "reversible": { + "type": "bool", + "description": "boolean" + }, + "compartment": { + "type": "str", + "description": "SIdRef optional" + }, + "kineticLaw": { + "type": "KineticLaw", + "description": "" + } + }, + "allowed_children": { + "listOfReactants": { + "type": "SpeciesReference", + "description": "" + }, + "listOfProducts": { + "type": "SpeciesReference", + "description": "" + }, + "listOfModifiers": { + "type": "ModifierSpeciesReference", + "description": "" + } + } + }, + "KineticLaw": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "math": { + "type": "str", + "description": "" + } + }, + "allowed_children": { + "listOfLocalParameters": { + "type": "LocalParameter", + "description": "" + } + } + }, + "LocalParameter": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "value": { + "type": "float", + "description": "" + }, + "units": { + "type": "str", + "description": "UnitSIdRef optional" + } + } + }, + "SpeciesReference": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "species": { + "type": "str", + "description": "SIdRef" + }, + "stoichiometry": { + "type": "float", + "description": "double optional" + }, + "constant": { + "type": "bool", + "description": "boolean" + } + } + }, + "ModifierSpeciesReference": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "species": { + "type": "str", + "description": "SIdRef" + } + } + }, + "Event": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "useValuesFromTriggerTime": { + "type": "bool", + "description": "" + }, + "trigger": { + "type": "Trigger", + "description": "" + }, + "priority": { + "type": "Priority", + "description": "" + }, + "delay": { + "type": "Delay", + "description": "" + } + }, + "allowed_children": { + "listOfEventAssignments": { + "type": "EventAssignment", + "description": "" + } + } + }, + "Trigger": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "initialValue": { + "type": "bool", + "description": "" + }, + "persistent": { + "type": "bool", + "description": "" + }, + "math": { + "type": "str", + "description": "" + } + } + }, + "Priority": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "math": { + "type": "str", + "description": "" + } + } + }, + "Delay": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "math": { + "type": "str", + "description": "" + } + } + }, + "EventAssignment": { + "allowed_parameters": { + "sid": { + "type": "str", + "description": " SId optional" + }, + "name": { + "type": "str", + "description": " string optional" + }, + "metaid": { + "type": "str", + "description": " XML ID optional" + }, + "sboTerm": { + "type": "str", + "description": "SBOTerm optional" + }, + "notes": { + "type": "Notes", + "description": " XHTML 1.0 optional" + }, + "annotation": { + "type": "str", + "description": "XML content optional" + }, + "math": { + "type": "str", + "description": "" + }, + "variable": { + "type": "str", + "description": "SIdRef" + } + } + } +} \ No newline at end of file diff --git a/examples/sbml/SBML.specification.yaml b/examples/sbml/SBML.specification.yaml new file mode 100644 index 00000000..0a420b31 --- /dev/null +++ b/examples/sbml/SBML.specification.yaml @@ -0,0 +1,735 @@ +sbml: + definition: 'The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf + section 4. + + http://www.sbml.org/sbml/level3/version2/' + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + id: + type: str + description: '' + xmlns: + type: str + description: ' string, fixed to "http://www.sbml.org/sbml/level3/version2/core"' + level: + type: str + description: ' SBML level, fixed to 3' + version: + type: str + description: SBML version, fixed to 2 + model: + type: Model + description: ' Optional model' +Notes: + definition: XHTML field of SBase + allowed_parameters: + xmlns: + type: str + description: str fixed "http://www.w3.org/1999/xhtml" + content: + type: str + description: str valid XHTML +Model: + definition: The model + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + substanceUnits: + type: str + description: ' UnitSIdRef optional' + timeUnits: + type: str + description: ' UnitSIdRef optional' + volumeUnits: + type: str + description: ' UnitSIdRef optional' + areaUnits: + type: str + description: ' UnitSIdRef optional' + lengthUnits: + type: str + description: ' UnitSIdRef optional' + extentUnits: + type: str + description: ' UnitSIdRef optional' + conversionFactor: + type: str + description: SIdRef optional + listOfUnitDefinitions: + type: ListOfUnitDefinitions + description: '' + allowed_children: + listOfFunctionDefinitions: + type: FunctionDefinition + description: '' + listOfCompartments: + type: Compartment + description: '' + listOfSpecies: + type: Species + description: '' + listOfParameters: + type: Parameter + description: '' + listOfInitialAssignments: + type: InitialAssignment + description: '' + listOfRules: + type: Rule + description: '' + listOfConstraints: + type: Constraint + description: '' + listOfReactions: + type: Reaction + description: '' + listOfEvents: + type: Event + description: '' +ListOfUnitDefinitions: + definition: A listOfUnitDefinitions + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + allowed_children: + listOfUnitDefinitions: + type: unitDefinition + description: the actual list Of Unit Definitions +unitDefinition: + definition: A unit definition + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + id: + type: str + description: '' + listOfUnits: + type: ListOfUnits + description: List of units used to compose the definition +ListOfUnits: + definition: A listOfUnits + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + allowed_children: + listOfUnits: + type: unit + description: the actual list Of Units +unit: + definition: A unit used to compose a unit definition. unit = (multiplier x 10^scale + x kind)^exponent + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + kind: + type: str + description: base unit (base or derived SI units only, see Table 2 of + the SBML spec) + exponent: + type: str + description: double + scale: + type: str + description: integer + multiplier: + type: str + description: double +FunctionDefinition: + definition: A function definition using MathML + allowed_parameters: + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + sid: + type: str + description: ' SId optional' + math: + type: Math + description: MathML function definition optional +Math: + definition: Subset of MathML 2.0 used to define all formulae in SBML + allowed_parameters: + xmlns: + type: str + description: '' + content: + type: str + description: '' +Compartment: + definition: A compartment + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + constant: + type: bool + description: whether size is fixed + spatialDimensions: + type: float + description: eg 3 for three dimensional space etc + size: + type: float + description: initial size of compartment + units: + type: str + description: units being used to define the compartment's size +Species: + definition: 'A species: entities of the same kind participating in reactions within + a specific compartment' + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + compartment: + type: str + description: SIdRef + hasOnlySubstanceUnits: + type: bool + description: boolean + boundaryCondition: + type: bool + description: boolean + constant: + type: bool + description: boolean + initialAmount: + type: float + description: double optional + initialConcentration: + type: float + description: double optional + substanceUnits: + type: str + description: UnitSIdRef optional + conversionFactor: + type: str + description: SIdRef optional +Parameter: + definition: A parameter + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + constant: + type: bool + description: boolean + value: + type: float + description: double optional + units: + type: str + description: UnitSIdRef optional +InitialAssignment: + definition: An initial assignment + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + symbol: + type: str + description: SIdRef required + math: + type: str + description: MathML optional +Rule: + definition: A rule, either algebraic, assignment or rate + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + math: + type: str + description: MathML optional +Constraint: + definition: A model constraint + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + math: + type: str + description: ' MathML optional' + message: + type: str + description: XHTML 1.0 optional +Reaction: + definition: A model reaction + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + reversible: + type: bool + description: boolean + compartment: + type: str + description: SIdRef optional + kineticLaw: + type: KineticLaw + description: '' + allowed_children: + listOfReactants: + type: SpeciesReference + description: '' + listOfProducts: + type: SpeciesReference + description: '' + listOfModifiers: + type: ModifierSpeciesReference + description: '' +KineticLaw: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + math: + type: str + description: '' + allowed_children: + listOfLocalParameters: + type: LocalParameter + description: '' +LocalParameter: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + value: + type: float + description: '' + units: + type: str + description: UnitSIdRef optional +SpeciesReference: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + species: + type: str + description: SIdRef + stoichiometry: + type: float + description: double optional + constant: + type: bool + description: boolean +ModifierSpeciesReference: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + species: + type: str + description: SIdRef +Event: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + useValuesFromTriggerTime: + type: bool + description: '' + trigger: + type: Trigger + description: '' + priority: + type: Priority + description: '' + delay: + type: Delay + description: '' + allowed_children: + listOfEventAssignments: + type: EventAssignment + description: '' +Trigger: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + initialValue: + type: bool + description: '' + persistent: + type: bool + description: '' + math: + type: str + description: '' +Priority: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + math: + type: str + description: '' +Delay: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + math: + type: str + description: '' +EventAssignment: + allowed_parameters: + sid: + type: str + description: ' SId optional' + name: + type: str + description: ' string optional' + metaid: + type: str + description: ' XML ID optional' + sboTerm: + type: str + description: SBOTerm optional + notes: + type: Notes + description: ' XHTML 1.0 optional' + annotation: + type: str + description: XML content optional + math: + type: str + description: '' + variable: + type: str + description: SIdRef diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py index 50f595a3..6b1c717b 100755 --- a/examples/sbml/test_sbml3.py +++ b/examples/sbml/test_sbml3.py @@ -41,6 +41,31 @@ def test_example_sbml_minimal(): sbml_doc.to_yaml_file(f"{name}.yaml") sbml_doc.to_xml_file(f"{name}.xml") + print("Generating documentation...") + + doc_md = sbml_doc.generate_documentation(format="markdown") + + with open("SBML.md", "w") as d: + d.write(doc_md) + + doc_rst = sbml_doc.generate_documentation(format="rst") + + with open("SBML.rst", "w") as d: + d.write(doc_rst) + + print("\n >> Generating specification in dict form...") + doc_dict = sbml_doc.generate_documentation(format="dict") + + with open("SBML.specification.json", "w") as d: + d.write(json.dumps(doc_dict, indent=4)) + + print(" >> Generating specification in YAML...\n") + + with open("SBML.specification.yaml", "w") as d: + yy = yaml.dump(doc_dict, indent=4, sort_keys=False) + # print(yy) + d.write(yy) + if __name__ == "__main__": test_example_sbml_minimal() From 37a9da1d6def92dab378210ee1245ebceccbd830 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Tue, 19 Sep 2023 10:53:27 +0100 Subject: [PATCH 21/32] Update COMBINE specs docs --- examples/COMBINE.md | 15 ++++++++++++--- examples/images/combine.png | Bin 0 -> 168517 bytes examples/images/neuroml.png | Bin 0 -> 3889 bytes examples/images/sbml.png | Bin 0 -> 18373 bytes examples/sbml/SBML.md | 1 - examples/sbml/SBML.rst | 1 - examples/sbml/SBML.specification.json | 2 +- 7 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 examples/images/combine.png create mode 100644 examples/images/neuroml.png create mode 100644 examples/images/sbml.png diff --git a/examples/COMBINE.md b/examples/COMBINE.md index 72e6ad4e..3aee77c5 100644 --- a/examples/COMBINE.md +++ b/examples/COMBINE.md @@ -1,14 +1,24 @@ # COMBINE standards in modelspec +![combine](images/combine.png) + To illustrate the utility of modelspec, and to create a uniform, compatible set of APIs across a diverse range of model specification formats, we are investigating the [COMBINE set of standards in computational biology](https://co.mbine.org/standards/), and attempting to express these formats in modelspec. ## NeuroML -A preliminary version of an API for [NeuroML2](https://docs.neuroml.org/Userdocs/NeuroMLv2.html) can be found [here](NeuroML). +![neuroml](images/neuroml.png) + +A preliminary version of an API for [NeuroML2](https://docs.neuroml.org/Userdocs/NeuroMLv2.html) can be found [here](neuroml2). This allows generation of NeuroML2 models using the modelspec based API, saving as valid XML, as well as JSON, YAML and BSON equivalents. See [neuroml2_spec.py](neuroml2/neuroml2_spec.py). + +[NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) already uses modelspec as its primary specification format. See [here](https://github.com/NeuroML/NeuroMLlite/blob/master/neuromllite/__init__.py). ## SBML -work towards an API for [SBML](https://www.sbml.org/) can be found [here](SBML). +![combine](images/sbml.png) + +Work towards an API for [SBML](https://www.sbml.org/) can be found [here](sbml). + +An example of an SBML file generated from this is [here](sbml/test_minimal_example.xml). ## CellML @@ -17,4 +27,3 @@ TODO... ## SED-ML TODO... - diff --git a/examples/images/combine.png b/examples/images/combine.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba0e32d9bccf9ee34c7f4fec6afb38121552941 GIT binary patch literal 168517 zcmb4r1z40_w>BxQAOa#Hhzin3O1E@(qjYz72#TbXbW1ZxH&`I8bf3)aB!lpqg7B;6HYFA{%zCW_#H zQ;+o3Piep#w>x4icy5Uwe|ip&NcPKeTqr=0KM*J1?PCz#kX!t#lf zElToDYzs>#e@opUxV(H;7H75ZfWq{ zZ6OFi`nLDs6Q+%I;->~?dN1RKp?Mm3PH~?bxAY(Rx9#|qTeFuOPHPtDyWeNB5e-b? zWqn~x$K|OT?!G=`*TC>ZJ8ef!5gVI=fSmEfWDQKSEyU}o02o4_=$)41Qg;?_1FyHrq(eD@=Ke<18BYq*p-SqME zCA3r?5e81?I4mf)~YQGRhDz5DZ%+iUYE z>miMs&Ip7bdF$+S=RP^xJ&)eMaPxe1*2QoiH%<4U;JqbN!I&?kFG!z6-Igr-ie;@c zMil0);-#8ob8VfSIc#I!Kil8&linvO0hH5NZv?p z4OYIW-I<=}&kyf!EF`du?UZ^A+!$W?$a-odVUFX7%^8-|O!uOt_!?h}^otNxYDdkW zK$St<;pIEq_!Ms_P?k~8r>z!S#hKzZ6j%CYcZDZZn&{s)WWbx17{{xI$0_j{eWFK5 z>B6@q7U@Y!O4Nc2_tg0bfmD7d*|?V8dz_STfda=vfr5e(=DF}m8liX+(emQKBsRl5S-R%k3$y}Le77*JTPdAgd2oe{mTkv{P;i^5Urk_Gc%6xls_ zhi>Ryor)CYGpCD{1DDXGT#UO4kHUxe^qRDv-4tULx~1=l^`5<7Z0e0V6hu_taBM08 z!Cf5OFE>AkB;s6~d4qHxNA&Jn-^}}QudpLXnZl1m2}^F~@>P(seDnN?YI~>pRoMfl z2z(@G2_eq! zvBtdD7z`v)n7-a+xYqnB{evP6BCcL(v3$YZ{O09i^20=k9pA-eq(h`f4tlwGVuV-0) zawCh#5iBEjZK{7K&_OOuL3iJhbp25>Sp{zGtFwnU#ZvTTw(ko^KFvgsM>F9zVOx5* zbSF1qS58c=Rj5^sQk7CkEQeVpSJNc62QAA?ygTaJw`F~a5(4)4jt4Ri=pR@;V2gl+ z*R~?w!U|&f8u*s4^?gTHK}Mfkm;6212`Xyp1WGrm7AlOWg~t$uiEMZNYK5c7p!Y#Z z=n@8XK|%((ZP%j}sGO80D3j(4Ha*EJ|qjN-cBlQcL|9762lYTU=a);QD*+Vd`keBT+IU5Q>^ z>#xl{P)jKy$xO*;6lmnXNQ1Z3HNrMy=-18osOC3;qyC^y#9AaO^eMwk@_kcdquWML z$F1|9=Ra?Z_#pQx;+t{(55f#>Jz_ntS@YTcW{O}pNn`Q`a)ii!atbN!@8wI9u5=#6 z^)DxkOpWiDQau@eF=EJ}YiJQ;%-U@Z85Xn)V*kwb*}dGQ)FtKRYvIhVm~YhIxW(v8 z1Q^83skiUMkQ5XYzAeO8XHn&@94Ix^AkY|8=PgbfIUJV!kXupxT%yciXnx4DxZX5y zqZr)gXRe{QLJRJ>jPif=S(06pUHQJ0GkbgbY4?YZTm6sU z$mM1z2XpW)CqyPF6!o%ZpJcFz1coHD)tKxO8F3mF*=6lu5#&h}x0}a4iybW5FLECY zDRLS!wv4dE7>^%ssBx=$GrltB#zn)`!(kzM;s)$bHHQ zFvji-)y6wxXyz~!@A#qm!d%kaV70gEhYizAS4-~<#n+7>uN3U?Vw!2sbgA@*AK1lc zP2AiLUvSajFV6fWd}}$ zBx|xJbG4Q}Jr$0F)%FuOGiQO0{I$WwUiNBt3C@wjep*L0cNGwp1E#l+dlsQ0pfBEiuWC z(#~%FoTuAlqRnMvo}$}IUAIiP%eP2Jz%!Rtjut0&Onb53x|hs7LRW6|MeNrV0x#;h zXl*JxDr)+Z693wtJ&+79t%H@Rp5QEt_OVzCO=(R!O;59zlR-(;EXw$hRdu}TJWm!6 zhoxmFGxEY})phj-6Iz2peT$ZwR}LX7v;*`y7g=QaGlm9ya}!jb^S&yPux_$yjxIKS zu#EiizCM5SG9?>d&&$p#ulti2^L%@%>hx_} zw*UFabdPJMr@DuZpt7Kdp19sgMDc@8sR^mTFphiUHXXaFb2e+U?b5Q+G*6#6mmKO} zQ2xlB=#9&+ji+Fl$@Aqdslt#ST&b5{Pd!9n|8 zp=Dv9m($6ab%LA58D1E5hzd7^wLHv(*QRr0WNF1`uzOs0vdW!l+3=+Bi-wbBSmU@& zpwq(9_*gAu18;(;igy8intw@fFK{jllSJv_`AN&f^@GsaB%h?huvwDHjTy&!_qP{6 zB@TSE`?BY{C6oKPoIE`XD5@5g-`LRC=mcqB-;FuV-7@N@PEZLv-@eE%w=&l`Fxy)B z$R;td+&I3~wWnq0C3%r?;Ie_j6Xn@=QMH3R+k4dAnv#Mgc_wgNvYx*-_}*%I@*d_q z*7cyLFG5#U;Qa79zrjs)UWdz}g!?{q6wD~?c^-);0`{H25*~I$Yn!i-RHU zLnk{TZ;#QbEsW*-5ERk?bUt4SJ}u3FmG5d&jzv-UWLzlbp=a^QvDeR~B2w7BUY#u9 ziz;b->|Sex^FMOeCWtH402xJ)fvT9HloT8lc#R5&1b+t(8N7lA4<2};e_lU>e+-9s z`91<1T%ZXYNJ1{ZBMqLRKjGj39rNoM@ofMc3i#_LcsQpb{P{Mzd@ACf*9iXLGdO+) zK`}A#te|IWU|?xyY-Ru4clH##fo3hLW(Nm{{|Nek7n6Uu39dhAqNr-GDkaIGXJtY6 zT;J-20iCmjHS{`gT+STerGvikU?1+!E=@EZQ!T54bRFdQ5ooR|>5 zqBH#JIEwqN{)y%-3T}V@?`|8UWUNht`QFHQb-u)SyHq(t80p`Hg=k10zd)Q*FMcy4 ztoX$j!8hIa<#pfPc>nl`$vEL#^!}@sjuZ->87IS?>y#He+_+g>ajzeWX}Z*P~32ktgpXf*vV@5UGY)(#FH37hX<{NkeK z3-2k_=>CNBZ$@GBd6Pa`_?J>dbyoH!%`&P?e)-^EN`zm$>t8Mld$RzJU%cCr8$;w@ z2m>lP->5{ye<}AVH83i9+!a6cUrGdtk{#t=?G`p)Qz-%xC3o!}&E0<~5mb5G>;FdQ zfegHjkWuAb9aq$z{yTF)#m@YB_rIx=4HQbYCo$FR*<5kC*E*h4#cjuL;6O_()%L@ix<|~aDDC8i* zTs#m1JJbH~nmo^c*~2t!1_)!d!Ntw93bP{C)iXI{VVAVqtOa^~IYS);Ip*4XieE)k z&{s4uuW0!?)K^W^8yNf-n~9==?tlR&Z+}o9eN=Q~W#7J|IW{3qR&%tBav%pm-ZKFO z&KOZC*vshZ0lQ+IxGOGrWiPI10Cc6Knh3*3CuRBz6wkc3?dAw8w;kJmkXHLi=h#J< zQdHQqzg0znUA@1WH>trpAHsVuV*1|{h!ahJeys<>$=j3Hrxse};Z*GC@OI>mPum-% zM)?!nz*G|0t2zq-?Sey&?SZN5qF^*#!ZIoN);` zoF{3w-#&pI)g%o3h*nt~4f6kA2tIt&R4Z^{Ki*y>|sm*T4l!m9kufW)fpxMR4F> zPwI^eYKm#c@@&G0$YUB#W;4=gr2Eo-0-aySz(S;DxWG+`p^=V1-}ekMEY{#8kV{gZNJW@1u!{ra`1Qg5x3l~e+2C!~TIAQad= z`488&8OrBs8Dv=KCQcb=R$scvSinr4=PPYx?-#FfCPMfujT-hgPqi*@6Atdh>dW{) z<_vqMKy>r5gGfV&*FaUY(|bV`SxVF71&gg8o`!VFBXSB^@6<8QOgxH$QO zM0v6PQRUKlO%3=0V@y%b^|9$1%iKLw6QRqUXqbO$+(Me?|&5jirHaOgQjgo`0i(OcO5_T&7 zz!nTdZ@2TH^0$=`F(N=e<8ww13Yk_oSnUrW<}BX|4?Nln)v68QO=ZzQ@)WwQl}AC5 zFSn-NATrB=lH3VdQO?i%_U&5}O4b$I1=kTp8dy(;oK^5HhKG7l>;9>C9_}M zM}?_gmN3(u2Az+^d{br55c?{c+3`ER+ya1*+~)G06Gdm|%gDD;-;9*OHX+x}RdETiE5?5lQ` zlAdm(v3Z>~fhes_dAYBmT%9#%!^=qhKYS4q4PQ8;Zb>R9b|z+gXedBrsj9;9_2=w` z!z|sU*QPa{zYdvlG|pKog+hqBOl}jw>b*JHR2%7Bw2F3aUtgd6Ya&+joLC*t>OMzh zW>Qg;myC9oW`9>q*UK=bLGv)ukmitIVxWl0Ghm!uq_@VQZzoEc1G3Yar&Jzfhn_8e z#iW|<1AkOF`q(#WK#d6(!4l`taQ0!Lw=;w zx9*;Pi|CHP!D(J=)W}=7aOfeVxCcR+ZHvAO8-#!}Hvn!;%~8-x1cW-stu+9`ZmE`8 z1bL3I;$dPS?|L|eiMqEV8PTQ$JUv4}^LjOt(^!7FYf}zd8=NDfoh(m+V#-Nco(%fOI z|G8`MH1B-mbzy($9AZ#tu!G|Gtt*y$4g&u}(WQ%F0g1~wwV-FNk7u*-rexii?^1BI zOx7F35uN@K5y6q&D^^p{Qf(?<=}4+&a^)hZq?>^NNUumE|K1pBeAP-LAE>kY`t_@{ zcEAHOE6t;a4pywHI5-iZ1u5yU5N2u-%pFbs-Wle!!sQ%9Fw7-Jvj&YkbC-4+?M~Nf z{l-yuDkjIJ7gJZdcvT9CSWz8sAK$+cK$C9)?HaGZ%`EGRkHfakYbq+himx!uL4UKI zRD;wq=%Kl$y{y>?nH~Iy0BeR*)|bM+!AtFo0#)OtErb(BroB@X5|iV)4MkbS#lt=S z7w|aEq}fi!5PlE3>6pnhI0MYfiT?hz=?`6eMI33C!$5~Wh(y~MrF$RSPRi(wYH{10 zq&jhezE{%*$!0%oG{?P7Du8r%tL-wr37EAt9Z*+G5O2 z0^E!h*W`@+?v}TKK066U>T)`*UgB(48(vpk}k|Im6W%nD0{@Ecy}j@{x+<_g5=L153ms8c&FSd8&UXPS z9cS4{Y=}uy4n{f*h@0nX7m~>NZ{4^O1fObL?yphGN5wD@qMc<)A5d#NplHm6cpiWC z%fE5)Hq2r~HEeuH$01(@<3hjVib0_cLF1kSkx+OKhnb;36B+_L^2y$M31^f>__V(* zw-zWxA@-!F$euK5NRUiETp-PMOgUj z>2z-YsOML;aps)vM?tR`GX5h-cvnJ0b>-x5OI@Yg{INVelHAasN zitG*R_cyk0!e|gu;zK~&kY+`8shA1__rXQEIkPLnyp$osUF}#)b37JGVIp*4b_Sz%AZ7)M=x^B(lv!+p>*EGq>qe;K#xvDr=xJ#g=Y_r1Ro*^g% zZSjkWi;EMNkWg@Rtk}>jluh%!iHoaLZmJ}s?Lz(Ff#CbsN8ego<=oxtuE#%o^l13( zB<>j4mH~pWu<)I;`CA?gPRZ@c^Br^cJnuuACv5$0&delM!{uGgRD7{XMG2AA!?i%3 zjwZr}q)EQH6faWdGWjC5u#Kqwb?__s6ulHBd2=i)ns-XDHq8iZ`X0Lc`}gmc^C1mA zr$;+`?evYtd&~VJx_}B>y=8t3Oi=H_3VC^>SZsWcPd9xwc0R(;h%! zNsWDcKM7!YY~gwA_0sc)@wa@(xZ7J5kz9YG{79wX_VGtT9N7KCE>45S2-cUsV}us6 zJd@X;m&)}IW-fhmfqbL!;|}VdSI&&ZaAD1%IKlgYU)0I|T5lAkf_T|oM*Ua&tHTO2 z3n;uf3fZC0f^XYwwukdfT7SlZ4>8h)|50}{lUHP8qMpTKgxM?J^y2)?bs@=VcYoCC z1R-j^ztfjnz05extf=}K+qiw?Mo{#}PzCL?3d`}lg9$eoAdDh>TJ_8l)w`O4l9ItS zj%#^u=xRh&G;LnKjf}kE`30IvV3+aP*nGHm*sb4r`uzEKz3)%l5n5O+(qA8prJ>t= zm`?DIxazMH0M%1w`QVTKDNXH6wwQiSCRtOz=Wui%8;>x9I?O{G?TON6*bJvWXy^>B1*cCr0=>>f6(UM=>uFA5*fo$Z};9RN-yG) zz^LubMHg`j?!F6CVIiR?1!T#qz|3C@@a%Qk6%w zG3;6+V4GcXxfx zr*twI&Zjn8hT%P!idRAb9D?N~>MXkX3lkJ4G*#@*9Xs!#)KS z@Es3Uj|%XCw&1%~%x?SUNv_MebIQ%YpXDW3HK>8A z)130>2y#}dqvH;y?!^I(BdnbWcjp`MNSU3S3ikcqezcVhdo>ObT_BN#Ty*hz9at<6 zRg`-X8^62~?4F83gI$8~?_g(%V89pPutU{$iuuq*zBk%DeSs&Brx4ZkVyz*Ubv-oOzuCi>!<(G&$ z@VB>2LITikw|NG>jk=>kL9Op5f)7*t*n9kb=^GzKF`-8BPlQ$mz6LYW_Niag+DLhC zgU3mkOX-;a+n}ah2)Ru1UGn}1H%_MfZX6f2x%`wla~rHP)h|>p?<;;Ts8pn>HcwmK z$=%V_rL3+VUtvC+-q)viag91H+67~mkjXH|?O^>;*^~u9HR(-@Y?J*brHxxNJzACX zks6gf{`{4cjEp`10kTP_w1Dge*0_IXW}Il|V<(Y38k{52kK`m?A;l{$<*avJ3p8d9 zxRMuG18|KQX${u#oQnM+5bPJL#xv#YL|bvuW@?M@t=X29qS|$;!eJa)=*Ms#! z%ki3}%t+~Nw07#hYL9(}w4LW&1} zjh{~7Hv9K{#`ryQa`J!@|HcplZIi8n(k}UI=_LJYs6@=hQyABu2@4+;)l|71SXzu# z$w|c0^#fPRVm3fCdE!O%rJLS$e>GQz2kpY@xRY=8Xdwmi>5~92b!12C!lPKt84R76 zN2_YGvZ+hG*=Ao};iy-b$4%}eV0CjJXm%#DGo|t{13+?sW-cbyuk=8K@&RXy$D5ta zp!DF2ZZ6(M$E30M(SdlX959N8S|tXxrdqMz)+CLfpn^F7vd&A1JhrwOZBgP*GeH&6 zze+^JLPJ{C^LO|8ca%G_h%H9SegxrBi<=p?E=;4!E9Waqspxo=AMdZ_<4QlhpX#tW zUaJyGF4Y5KS-DYnviEWPb73Y;<7=#+9SSrmyDG_d@E6YS5p&po$Dps9!iYQ`%`S5^ zOxn4~{iX*D<(t5f8%Gmp&N6@Fik*|aam`}lBl=oIlK zQ#Y4FsZsZST1b2kPj>9&Et_EFxAYY~1DSrvTC-O}A`qfqQB)cwl_?MJKET)-Q zM6CYD?t39@&p#oJ+qTnshFEA5+l^J()a-O|o|)>zJhPkckOPo2IwfUekYOH=7t(m{ z`iq?&dr<7=RYP}tfSP4wP-`St@RPwuboPQ5f>o;m_(P6Wrim+z-(@<`Q!`E`YLdjl zbYGXi=O{tDXFIC<*D|1<{9>6w4b;M(-%h#@>&Z!@02I-}&I}F<8=U&+XP}MDcwe#- zhXo?((($AP$v$C2DPK7kJkq#y%7L-Psf_IyY5g!Th`h1+u{ly%6in>%~dvUbf_ z>io20m)Xjv3wYp(o43z<%;G;xp;*z5WaM)e(Gb|KMPx z^IF?i01H=2u=nAR`$&Yj$SYAZWA-j$UI8*`Hvy}*Noo*Fr=_nwTx`%Dlg2?Ies=+A zIp*W2r#3S}Sa+Wmf7$XqYQlWObACAM=}{dDI#UdXE;d#d&xtu$Tz+42$Q_oR04ySL3lT)17Y$9dI-Ng;DktsE1yuQ) z5D1*ur34`jWK*8@S9O1t6uJvqfQ-4+eH0&yrS`q8WgN%HFYkP!`Cg(<;9zEJ_Ri?J zGt3@fKh6g$M!7}_LU^G`@B3(n4Zr1!?RI*zCU1DpJt@YbhH|e9&&H9Mft>GEZ#8<~ zOO=DK^~C4tGA@vjWQE50~D1H`&k)5$CL$nJ40e{@Pn`3c&umMrgcA3ku=(y3%zHk=` zRI(~c)A@mo?{>0HMsf6quubF;XgjC%IWhwsFhakDkGGVb_Mq$$OJ^KL>T)>hmzL(` z$R{`Q#Zf)0NFO3UL4{FP0OGU3p7W*&d=DlyK*c6RfJZ@d-#WonZ(Kk@QIgCxz+v!< zGEBYdpjJVr%Ue=|CL^QS=8$rm*=LQ%w5r8?Nt{mm`4(a!L2uqDLA1FoCL8KA$-Npb znu)A(tR@@0JhhVP*BeeOx%c`MvSM_P-${e=z?8I)48dtCiuO!)$ar&y@kbr zH*UxSuLrJb1p6hIiufl*=GSeux?$FS{s^knG}w!y=g3OIg@zv`C9i;XHw8n6l4Lc){>-rk7FLBXVlb2qpO97dg<7Q2dHFXwyVUdKat^6G6cHRpdY zqSBX)sPu0}6evbu{&`^%VdF6HBA}#I;wY0V?(TtM_~J^r0~Pf5JmPQj?oN`RuUAya zmfp)xaY?Y)aU3;Z@5twfS8<|QGY0a-r`Oh0XKLq3VKIw=I*rD82^krc7cbIvEKL02 z5V~;Qo$Wws7LLTaDBp>HrnBC8Nsx1Kv=HENkNm2`fcjrX#kW~ug5f3h57eR6wS(Bn z!tvpR@v?^lwzqtMsmXT+b%yo$hFvkYx?kpYc=zs8>r`#`E#byv4}hzdhfA|{EF?_( zAUeGDAWMJbak_9asO6NT?RhlE+eo)T0N`mf;ri}^YPo5&db#PGjKT;2Q+`P6w|V(@ zV(IK;zee4$^l|Oa2QDJ;77h;1dhTtKD-Igq zXqMi?x3&M11mCO2Nr9omy!onss|1=efZHY=@1A(>VF-tpXw^EZH@Lgx?r`r9>D64E zpGa63Dft1yD_5=5&{K-dX6qG2(prlT|*%Myt;Ev&W zJjQ5E4qNEI~I7SXtbgnR&pp)^9@(fL-GSB~%-eBc%G_?4Ao-b=>K z4B9gpb@f#**ygJ=76Z8mB2Aocn{fjUCs0u$P8nU(`8fBEW2cg`bgSl76Z;O3kKcQY zss}*#S{J>&Gmy29kHqot=Qv%lZf!I+#YtJI?Br!1P5Q*Z4%Dp%P6YU|6#FpV^Qr($ z!Wosul53QR)p;2@$irnn%1nCuKo3BIvWOa>#)6xaZiMvYNhSC#iIc=)JXFd_rBA+7 zIP~szEF@|Uwq)f^bjbz3`2$5s8IEGTC@UFyuV=~nAtQ_@OlFWrbE+}mD*O|Ep`oJ~ ztO7{deVyy*XJpMnvfJsh$^hvc+oXF2omR~uOKc=jJpi1`z1d-rv3zT&q}H#RHZ&@i zN~Xi^ZeORzIjn73^g*=WZ>2arlWKt7Nq8jl$I!?s^#|Xe_*BMVDn6T_;s+8_-xDt%T^nPAkWG6_e$rGrb#4e&UMw0#Yo&{1 zIaNimF8O;{llRg6ZMb6}7)6hulu0P(I%?IkYzF{e%7`S7@CwAN`1~ zJ2SD@k81EVETB?;b-;?055wKT!H-_L<~M&`^T=h~UB8$QZd%PVU*f?HQtZ%zwc{o3EvTd~!r8&EQ>{Q@2! z%NZoTsCgnG^v>wTRj~Kwx6SebG7#{pd2$)gj+FEUi?sUFg)tgOMhfKsGv1%8KmjG{ zfZ6&tu{+(68)-P>-_3k8h(z{)Ya*i$-N(Ye^prwsuz z^sz=Q7_HEX<=M1;8ek|HiymBkbE>m0GaeUs?bkT~ST;#?K3GJBf4 zp2hKD-C#O7U<*bRe5t{|ofx_rVJ7qEh{`eBH##gl$BP+^R>;s~5;ZEesraHUFK&{a z2C@`4=z{=i{@*ojOl&l@Q4+!i$~rQriX@T$P6lrHyr@65iHtg++WP zFX~FGhOX6b^%N|`l$TCml_JoxkI-QO{BWTwfS_ll5)T0UCB2Jq_AGQDC?w(26K1^{ z#Z&>!f_d(XC^9lK%^HE2E5xca==?Alzy5~>e$&*4=T}Z`=BL$`7RrF8Ip!FgN5PEt zzooR7R@w@zG+p?OJ%AP`N_t`UxS92uF@LwMp$0ZBRgPtK-1YS3x?5p+Z>RS|Se|k} zm8CYB*_GrJ<avjp@K7Yl++`yoRqJ*1-rUv1R zj~)q^oAz(#H$MxxV**NFzpwx)wy95F{fYfLspXHmrYiKoY&PH5pzJb1OxqpUO>OeI z)CJ~0x`3lvxg#|^R4O%;`Xi#OrgWvorREQ_g6t3V zT!eJLDaEezFx5Egm($?F>PjLxR9A=}{FA&+CJi_|Q)wB`WF*~+b^S=cEsC;cwWR&JOJFPr(cUZw z3JV>*IcyUj3>gh=qWMyoYE3tzPaE5n653wCn2&V?a0x4{k-wAhB&7E=wSsPT6~nm? zP0`Z$HU`3SlwagyhklcV#Ut2s@G>Zr(6*`&BDR2b)>Iob`IrtC=S^L10QDcSJ^dL8 zsu)XqgF3uFiM64k9zgw5;u?@_rYXAlbnYFi>-zerW;r?jm1_9~h@Q5aU~{+)>DFbm-$O++Zdl#hpR?Mz^cT?QN`Eub(mezkn5m2A6h)MtaD zY~9amf$s9T@{d6Fq=klt7FgE~?KvVnMR4Lh@tr@fd3cKZ#BT-Z&OlzSNmz*Wc#SGH zjN?VE5`;#h6VZPN*Oh&m@3DZ(02 z&JN_)-oAZX=5=u{KdSZvRJdB(YMnM>2wiC(KYm>O<9>={JhK?!8qe-;Y(nersEth_ zdd+5_gYFYA4pWshX-P5PYofs_OD#(cS#$G(JQc{gTVIacY_ZW!JxHynsDh|5t3brK z@n_;w4$$N|3H}d)c^IzpE{hFc2jZK~#ThM3dmsC0;g3C2jSM%`XD?7;^X_7SCg7v> zLfM~DD;GTOrza!(M9}pnIGCo~qKXJRldEQv&?qr&xYUTG{s&14s8;k;ULX;k&ZGlU z%U?8g^4kyf4>k&~p6T6|LA!+~CV}s2+G?p)aAYRVB&pWu=>e@WEsvDvgW|yQz+4I4 z-+PB$#+Hmfu|-OEpeB3wWPM}xSDlE8QT=S>Ld53BYlGV_i(qwAHw)TjN6hKi_vz(1 z^39%}9>qd+7V6Hhrv&_M4@ypt<`1`K9YCsdcI03}UqxkLBH#`>Hq!8`j2BTY#^P=+ zKPT+QK=|5B7JK>_FM)MGL(_rxSK)P1L2)PnRu3M>_W`lSPY`1VN5>N_giHZg4YaN$CEm;l# zg;P_au4hP;VMPfbRR-J?6&u^ZBG$!WU)|n&7bW=})&yuM@y0_~nDA(nKUgb$K{aok z(bOX>|2&OIr8)jOPL6z5kTmBSb6i>*a8uMgJm=vuF0cQRXx`kHh?f43K9I{QYUZ%T zj^(VnpS%CCbi+00USMui387B8YwmQV$BmC2wTe-cUAGCY{&c(D(Q5 z_}h9MAl37C>d%i?ve-^DzVcoi$w;N}XwB1(t+|>H74riQNC6b|wInjZK5r^a?Ivu< zzfXB?#T%Fz7mE~gK{mJGV)8XvC!a8#`E%4f#A>9{YQyCQ++Qn)5+gk|^?$&ah|dPA z%s@}lxz@ukp?eCwItZY3cG+p{q3|=Ii#>A4-+`bTWFP7tpz-|SSb+5Om__L6X>9%K>ao@cK8MOw9YsdUAGMMm>|u#uP# zMYL3ksA93pk>qCU%6lX)U9k|;>3aj!er2HZz|0r42MGGY2z~7`*Lw6v=#Rr=%C$)f zss=-|{`#@1XHeg# z@u%2Sgg^rE6dmme;iZ_dF5m;LXUAo3>d`Y*-T``S5iqB;xTvAsxPYnbD^O!bDbn|miaPY2GIz!n=sZm&SC~-b0 zHd1q*K=MOzGBFWZ1<7%s81vagGdc1`P_rOQIIOub@D(~v*Z(9Kz(vRh&d^DnAee~S1S<$o%ehB9#8M!WbW!XY)*Z2oq0`hy0IzJ z4r&xCkh<+4(6@tYJy~MU;P>vun^kJpHv> z7KlE0jp8oVtdIR~u9E3$(|Q1MxaM%?4X8Bf#g@SIRb6&;6pq^e0Xz7I*8Q?;9jbZ@ zH**V-d{BiK-|8hR_M>Bo8-E;ySwE?+5&YmaN=W^_I%uF6wmP{^%wY^Vdh#@@OC~)8 zhC!40MSOKLtv#pHdV!7?ivR`uz4`OdyX-wqqa8y-LvU^=1MVMvk>j7*qysj@wvXBv za3~$o1E@nvQyrY^{S(2MwJ{=Ejs&XvkwVym>$Qq>d;Jg{9l-$1AC%dN@^m{)ur2t& zAUIL}U`93F*@l$e;rJhpbP*%s*v9f%88vJ~nxC@kLjIZ(9D*Z(wMDq?Z^D`He4aLA z(q(~ovM>X2`A}pwnp(va2tA(1qXu*m-TICVrMP=QXteJUqiD6qm}h{fy0#EIEykM_ z$pO6SOE~k&14;|u;5p>8YB+k|vGXwn*cPvN89(UgAQ(IFu6Vyz;+3XoEaUSCBP*TE zZQz;G933_x;nInN#AJiV)qeQRWGJZGPy7w4>XCz39KP0%M3;Y@FpwXAjzCaOTzChq z^SWCK+Z7i@h){$jkAGXmf$_7gp(t(m0}c*#(BG1~!5TELbYrDI-|5Rb=w%)Oy+`y6 z>_%OebhUb|<64SEXE!1{=vzy7NL>pg@ywB+s}_3s@}-Z{c}Ad%QX90U30km{rFqZn ziS0U`Fm}UxUAGGxgy=v(8pyu`jjhcFw~bPc_S4XPm%v}N>R`f$I9WKu&w2tFF)cDcpU-} zizRFrOHe)L;n4dJ5@(CcWg6>uNQuMfJGNL7+Oh=!3#peqWlS zl?+iaEPF9VI<-uiRn6ao>el620;D$H6ZVta}$jlCOBMKeuS^ZxeKJ7sQA zlM#=1!A6BNy^7TtY?<^RnGm}@9*wXT&pcrEdEH@dq^>w8J)LZAxU|LT>oVX{P1eV% z)$5!;bR2wkd8g9hxtJDcKcHsx((X<(OiLyo)G?s_b_`F2r()N@S($;#B>5tWl>+&+ z209Z?iCEQ$7>Vpxs5%y^1Zup?U;vnWdZv#PhicWLIkg!(!WsI+q67xr0eB zNMd4Q@naz&p~d4B$RHNm80iiRY!C1>w1M<`|AR2G@a=DOb#J_5k%NLTcF(unpeNm8 z3C-*Gy(;dc)rhqW)jSKjk~v=>ae<7EhGrLeb;50f`$AFRdl2ayybwUL^TCF)(>^Ho zKqormn2Imeiph&>_iF5y(!+R;Ma9I#42VO|FUx;A7rXh_WZo%uDKXUJB3m|WrYw7% zyGYX{#h+m_cv)x;naiahY8?zrY?h2;8QbwT=c9~3I?=84>k zB%O=UawdR>V3>ES=7y<+$=cXBZ+K2SEa(nQ)*KTq_Z+_0t?9Wuu<++Ff%)U9*Q8lX zmI0I-H7j)7C#$6~=7{`lh#kr(ls7EbyLm4PKx@|WPB$;_VT?txS$|#@=#I(*sSxxq zjKy*~HaHcP0Og8TN~UkbT41}n$xm%1Tw8>8;{P0}uUG_;cLj9T3>K)B1~;BQzP_~G zsk0E@*f8}+-@#Hy`v40dNUOf>3=ShzOyeTe(QWSR=c-pZ1Pv0Jm!AWwbTOSZ;}$WY zQGU(iYq06LFTti{_%a@$?wnq0svWpr<9I1f1BUe&SY*4P-fpp#&6p}j|OeLJ;(%_snkVP0^_w#D)#of zA8)zGO}3BVjW@?cC`eXpp)*dlC;l8L(s(W(+7fY3lLlz4+Io+i$#(zjXO>D18|GDP9hn|;jo+|5_XT#4`P($5HmA7>qi*C1FDXsKH7o-W)i0H zpu4hf8~;`CtM8|;XYmMd6(+J6yx37YQg|nw%6CC~bb!9&PN1!}+ZqcC;O-yJUa6nH zhenew#NQA{`qsCu$?RO}bvfm8ZHkzUy!cwy2I0ayCx`xcRm?xxF7yO}Xfme!y|Kfw zF-?$xj2{5_L?0D{?|k_66l0~rVl)rnFAmVB)>mz31ntUZgrML0Ca0^}y4tUzvzPzo zD0UWDNrjHZ=O4yPJz2-+CmUyXjuY*=#uKq4TL)*)G?lO7mr%|YNntpaShbILTxD$-Y(i{iw}fz zivBKN&3DizWVp8cJ4gyBSDv5Ov|}p7t9*tf%1CcPl{#&4@-~3XcIkKs+W12;4U#FR z`^BIhT-PC6WM=id)l<_<*A~X2u~Eakv;CqjBx;_2zD5iB?wr1U0@M)|H@DXC@Gun( z4LJCn2-@JFfWN;`W02rFl&Ao`O(Lr6UiXRj%Uxc!LHjFL1|fr>Ztfli>d@dnkV$}+ z&<49E%$oShS!q$x!yPV$g9gBHn}Rd1^ju~QAs9?@e&LBCAo} zz)IwIgd{+UdGDUL!FuHM{5rj}YIU>vYgz}-_Gw0|waX6Wf8zE!_{SDY4crF8mk@aF zVKF$6IL@&?Y#X0slJuAX9FnNnUS&#Dk6%d*d8!$!sb+QCJ_DosiZGMP_<|VvL`0?vRg--^$ z{=$6jsD)pYm1gTZM0hWx36I4|Q@O@uUMZ7V9QYlop?QmjNw14DMu_uJ@$;I^PZ;f7 z*Af3&7OFF7js{HBA2@>br;&EN_|_=o{*#J$*K$uGa8%PmhZ^|`*|`Z6$*(el;+MSy zqG8_VmToLia~nFT^J%(3R}_=%i=8D=5fb`x;rV3{)fgb@JDiRM9l8d^A|QPS-4b}^ z7Ne{~_ZMNw45;_H`N#YCf#&>(hC`16)>W;a0dqr+Sb(SjDJMyci?BFsw86Hn1QzbJ zL7#sa4vST%zAmL0_lTsZb5ZxQ%T29DN`GHWxc2mey=jH0F6bwLcFsq~#S!iXgTg^N zxm5fudoK={{01N?2R-3Q--)C`LeE_3w?6 zUC0;l*sLZrH|lmm$sRoLyXP1BH|LuG#W<7ki9ZJu6F_ia_L`MY2Ms@?G0Jo+VxbTV z|Hpy3@^`%)Rp^MCGXD-mjfw>1maVUm`lU-bKp#6>#fU#yxp_br9v!Wg`n>Z_JnM2A zW7uc737p$J?p1~z?{Ic~NkB1O%lacS|3UVHe;l*7(|>6=X;*_hlf8n4p?ih zxn|Be=bTyAOZN^2*;I#R+2hmjs>qrlX=VyLrs)40!D`|FFS$R0P5$Hl&63dBst;@Z zsrq&JDQL(60UbZ*u5d2lhsLA#)dJev1y_Js6x~)=YZZnH&GM8Y#f+ln=H@SVQwaY* z*LSx8r(L<^^*6IPV6#@s|6VE5*3i*k-!Tt0w}+05zg2{&z0nA%i5~m%KL#QF)4aJ} zB`N#kvyNY&vhtrT_rk|xBW^}lNrVmo@TP(L`7&jOF$cf0BhL(|<5N?XY8~Ha z0JtuN`|SANPyn!jJb-0Cx~#qBf*M;i4VKvF(f-l>7}zYDO=V=}#$Kwy$Nj~O7Izf~ZuW>t6L zelvY1B=P`oYseTSz%*ww;&xV9*8E+~7{Fuv_hrECP`RPr{iw~KE5V3<`(YGvAaW-c zWnqd>gYdfTPTpXCn?#BJv#vcg5ZPGvpk$$n=4b7%?SOC~=>J&e{U6J${k4pToYC8n zH$Gk4AF!~U$|27y?iX6CD}WIHcCX<1F}(V}cM#zGpffK*&HIdh+I0ZOKQM^d5%zj6 zW%}!>^5E-OwD72iiQP?U2D;opQ=1}{djvBJ3xG~H1V|_4?ty{9K^vgfE(3Hc-9G#O z9ZXzTgwpB90su6D&N){j9Q|A^0oDyn#zt!z>fNEu5dcbbrK>8nLwrkT1_%Y>8TJ3a z0)R*r7}wYz^|74!<5Jl0Z|S-#*sq!w65Zdd=D=Fq*Voo#vIpfyOia zCVf(}pmq2E8F>ck(!eCDrBj4|AI=w?AQMdp71a!pmU-;df@#j{#|Gia)*uzZ2<8gXxvm;;M z(lc{I$~R;3b;V7yW*z-)s2a})1K(2pk7eNho%v_y|JSqEySf1-<`kgFJnMSFWWX?9 ztH_|(zKc9p6&LcB<#Yh&-5!9;P&_um!{L1ZR9ptAi2m<63H>=IzQ5-LF8EsU0f;sl z_RDRu1A`(CLi7VOYS4u)9~vAY!^2$<+27*)@8blELWx4CW)J#)0f}I@lhu3x)-rU} z)D%#+L(l?n;MPh%={8*s+q^Bp0_|v0RCV6jRO-}{ukQmQojgEW#`%AwqyLu}`Um6( zb)ABDjnCR$khezqxpjNhD1=XC@*#Kpo!22YhF2tDTB6R>r2i4X`fEZ>oM=J($L3>* zf`Y%9e0U*xV^ujJ3PW7BW!j;uFJM7rI6BRaxi66kaZj6pfsM#)ualPldsJS3j>_fl zQNa^3*RZ&^p_OD7HMH%P*U&znzC`vcmL9O^{KtHgo(RkMq3?eJ1b~NbpI%5Qk zO;Ax&znqR6{BLYs1+bd+O7`D8CHloIV7d48W2aZZ>`{6-5nj21gH^sVx=C4yXM{BqR&GKJeLm)Y{0~}({*e{a9Ah{9K?S^8M)L*QTo+*>v6VT!K{!?`H36&Mr@p>V zLLTBDQ^;8X)pp0_q(b3W{)bHfy_Nz6Pnch~z<-|!o)Bp7pQA>zb^%>#3!_k(e**kr zueVQFLCI^k#P=@lFPB9!iSj5mrwc~D*%$}Y9MUxGUa$P>x3K%4#}b5U1Skkb+5g0+ z5pAQ3pO^;R4`{%0hotvpKO9=RUEEj7)E65XybY-@W4oc+-lq3!{y5%0`F8hehlpP_ zlMwwE5C4g;t&e{t2mYe#pnK;=Yv?Mc3!xMS0YAU>prl+GGs&+|74OI7#Zm_~ff-;# zyCgh~Jz7@#e;y1~bR7bC!vEjG0w1in9)cn=;V1mWX}8Hy^9cSac;*oUNI>qfq@}9R zX@MsAShi4ui=~lvDC_&fN1$S&2@*|s|QDUC8gMN=ZmDA)MCMlgp5P4 zpJU&(RaJ8%5u>7ayF|!eXAhUqN=Zu-dq{pBgX@fqjnLB9Z#UUA^%(;c=D?O0Gy+@w zmBFKL0scPV9~UbR#KWUM`A#2(ZX}FzJx_4TPU9djYjcA)0iHi)@OGbiWXvw#Rim`y z_^fkmKH_d>D$B~MgajjYLU8UY(&a}Kpn&lFFX{kP*rK&=mhZX6lfTF8DlTT!&v@8z z^d{er@YhuPxtdiZZXT96dmC)3j=?K7s%7uX2*_ zdW+RF$vexkX5KXq%n}{AZ1Ya;RgSpua9$uk`$Rl!E~v+ti3quBzRT2n&nBx zn62dNIo;5aQXur*&)vprVNkzO0cMo`)AtALg=#kVg+~wXkh31t_ z1BvHR&Pkt}6!1K)2eUluG*mLnfnM|G*_;bd7@&nmUxkq8KY4I>#RA*Y#NS|14Qi=- zzo{dS_B(QuB+%~Wl*j^loGQd1X)wEA@mXI|hW8lkZ@LkddAf_1FQiffgMOA3g5=T9 z7M}w~($dm9cdafKJo<4VpT`msuqqkDTK--YQc1wKCBWZU|Gm$*6s&&V*jLUj!G7tJ zgTFxT`X6(vgm3J^ssqon$WI?{)(%tj91xn90ZgscwEEGu`*9B%-hy?zUn>?t)~X-9 zZiqaNmUdsoeugal4(F)SdD|k7r3oBPa=E5z;g;E2^7D~CN5g`4_iK2_LWhEv+wrX7 zWCvl$k_yZrA|2i1QJ33H*f1a#Mi6&31^ zb%B*K2P39AP4+&Rp+HUZgF$KsFW_7I^3|Yekn`wndyKRW`x2W%bR@Uxv?}D~-L!%HZGD?J z+%sxRKKWCO=vz(A^0x+>Wty9jKHJSCZaMsO%|!0H@?|W%EtWS9=6KpUu6L?zwHpCO zwPSxBEz{Z^`5w8@Gz)p#Vrb; zV=GKHGai}r@vxGQQnqXVmY4bzfNOH;eG~uV37OusQRSo1z5)DsMIb00qrP; zQLlKVDp}2t`x|mfeK`UF9B)@AfN=a@9str2YNbqE6%u^tfOZe!mf0??`4I9kd!T8X z`!TyimnQ0wO6Rp@kXnL7yTT0m+`}>=L85+WRPhMJX?aF+sY@{8j|821L>)sayA&gvI%oPy9N9Qe~+jGsKxVu zg9i#_fW3&qX&lON9cQtYbC8{o8@~GTm+w43XQU1<+R>i^JoX{Lkt3J10+lqh(1*Fo zWrH4YxTu>}qnpE8Rn}6YZI087TvOO!tcB_7%NCm}n=*syDjNdr>R56OPfliSAB)wp zr3YbIuHH{?KfkTg)gz5!tDe{GgU6gJeK&@~$lXQ5n+dSWK%n?LE4EJk2bfX+0cPKP zKm*b$lkFP?c-cQVi9=wmPxQ?2v@o#@2H-Waok=zD))sLiRLt>s_amW5!yo8aLq*j2 zf9pzfjOOQ%)T@EsoDpvL4tuKcB2|eF{!8Kk)aaU-PLIO0&m5LE0D}U<#6w3X2h6`A zl%T`_SvX)4aPo48_bcsE0*wwA{a82h7rc|ZJIQpmbLIITkqGdc#HySDoVV*3a8$u) z&(VqUBP_M=6P3Lwz=A*!lRq5Hae)SBu+Vzmp{JOchur`bO4F+~MQdc;Uyv72%A*1(iRvrP6LLKAB%Z4mK zWZ?P9bu*I2i-Y|u3xztc6DT?PQ~A;Ii#WzjJ{FO8upv_PjO@fAA@ub0+zw1wg}j?6 zXQMI|PKOOa)hNP4={a~1Z8>?3n2~St^0GB6i*&#ix=fmQ>*dut$gt|7+45>)-)~R^ z*2dNB>Ahx`1=21{C>F1p(XZYRd|pSR?gqd6&2pB*7dPK9=q{4YA?U|1{ea#;&@pROr%fo$4Q^BQ%{i9Ni-m#a zTi#9j$`3PNvQew}3{+MVd@VbBAAJ_i zZ8AUD=fQN*05N{(WGKr!^QxlUY)L#@OD+&G%1wb-eF|mPKclXasYWb8dhgRaA7CxtfjDF2+=B@R~Z_ zFV=-5(o?k<%T>JvG}ecWlP6*$M-LtHV+xwU&cW;5!rSaA7xiT0_EdwKcb*}i2j6XA z>Al1zFn`I&P-Ut@%y!BOP^7Y?gdJHXzy& z|I*w%4ljM8CgvM?9wQ?baK`$MV9es5m51kAytJ=JtWR&K+1V#<{nYB5(CY9bV@s`< zeX5Hf3*JQb?Xm2Esf6$zz+Rz39Jd8yTh8Ya`!J{N(6uIqIsQ#~WVq}m>kFk#l>IuU zha4%q6?%Ma4J);w+gJZwu)stQJ=ePvJ31$868O2@{kUFLsLNQnRHP|L6!#x5K!m47 z^eu5!6%!LpD)S#HK3zbQN|!}L6~}@{APc!%t?#&5pj-0_O9@tVv$(##*Y~~1`0TZI zBeeT4Hd^Dd+p_zbVK_=~*HRwmv}A{b+)#-IMjg%LJf_KUEErL z#;sOcV|KN}Ck$2Pq{#7TIBkR9q<%q#v`ihDKo$9&iHW|gVeGRW zTxvRns8ygq&F8s+ABn0_6rQ2gVy)~iE`R0x1&7RB+m9o?%{{-=5+&WsEozAP>(wkd z>VY~+=<5_agzNAx?0tHm{e*ocsnBZ4aOMliN-V>iWEi&8aReAdx*3IFtl*@l&aC;a zn~zstbeGk9CzJUt+gKIYYbH7%_wHL1h1Nj)!!M%MIQ6ERg!P}r0EI}*&Surl6b z516(l2@39`#h92p*kx(e0kf%-4lBsXY(EZH^xbk|H#^F<6l@7INvzG; zrp{YK=7D6J?qVeLakc;w;Rl%q%ioAZUb1tsl18p?5uP0Tt{nLm)Q)7Mo8Jk6>Mcga zm7{sGmpym0biP_8d{mlDJb_n{E%lX;W-Lgeha4}r5^>#M*ZVZ}7RS22FwAbItM)dd zS|Vcj!Jo1%(L;(He(|nMbYWo??Yl;7Ops{f-Yppd{gmV!io+t>mv}XyYh8FcfybDV zd7Aqu1K061p3P$WWwJpjN3Knb1$#yABZJYR4=}BS#ge09i zMq6MZ(&gv&v(=)q*2PakaJqIIJ4wq_I;k8O596rpVv{5MhXl0X>AS$yaXjC?2Pb`x z??y!k4pwnS`RK)yTm9Zkl5>GYn4!=38Fg;v#dC%^kHe zB#B&CcvzvIr zgeT>=ZS9enxp_TMr^vD5+aF7d$IuoW9qjctDrQI#-0C`=#TDL63)f-zATBs?_> zn-7;ti>w#+$(O{8aj(rS+_WS$GP8SLo#|300KQ(rz*O)$ zOd@Y&>m=M!4PANG%|SY~ORnRe4|d;VKdgT;GLuOqRq1)3c4 z{nd!JWWJJTT}u^Gl@JQyBWneTkke2h1GfS^6ELSCF1s+R9n@i8K46Mv!_Si^GHhG_ zVoJoddKz0FT^+kvNmOn`(u@k(a$rOP?JO%6ddP1nQv>~+imeF>#9H+vWeX}}IweS*cvlM~H7w7AaBtWevrN=9@n4T2mQQr7Mpggd#6XBO=0L*7eXyVd_z>Y;1?-AP;=kT%74tl* z1;a^aY_yx`w>>Dn{!|Jn{g~Z=iY^e?e!x`3M`*_k!c;5EQKo>Wd-U-=ns4P&3s6Q+ zbecj5DA=OZh#?AMRK?54a0ZL#qFx^_QalmKg5)D(lPHMpM~KgwZuibMDXtw>-nW@NM2P(5WrY~+e5qeC(t7TcCT?lo;e{e57Q;|@#^6H8&_CAo(NUOc zy3{ixFBc~#0-Cg{2QnD6O`xj!CbB@97BZo#!3^cn;dr<_jEyT22`$RBMjC3n9CajQBDBV9pVfN z8kK^ksf$uevcl<3kGX52yt`F5@NI|E<0p!jQLqD2xO@(;mwbl08Pjvswr3u_u1)TD znD&ly{n_y?bYs4;Frq%c9PHh;I5a490fO0Nu*f?RLo)MsHLu!qQ%t?StC8jDhZ&VT zUr7CycLS-JTb1f=pI+|MD(3`H;s|ICBY%E1C<%B{nj?}mYe5l#oN@QZmh%T=lGRw>5bOIwSSH98t(~V(V8T%A*F1mou=<@# zKJrQ_%dV3G{zwcCV#%i#B{QEcF(W9^CNn>NdNEJlig@XI0YO>71%I4Do~0H7dZnS05ANS91)*Ku`4 z3q<1k@!2~;UA-r&%TMjXMo0DRFyH7d5)wSo?5@pB!^9zSqQ;;AQ6EX(g@v;!RAmS4 zdHhrnbeK~1LLMhINsZ5Y8KBY;W*&5v4B)2NVDL0|7|No5{lKid=+{o1-w zL;}i}k8&FMVd}KOrXHV?R6sJchky_-st;~FS_xx-_@Tx;knu44IxC-tlUPI0$wwCQ z@L74##~FZUzDmdQxd&qZ-S3Hv_`1pltcaGj(rYx&jTBUqC<^TBJ|8q$+rwpTDX2 zmGhCPN3>p3%_xGG3e8;WdRsh=a|Qj3T^2n&MU-_u^k-}n#SG2;$0kXEm`x3GQq8i< z-_#Uro+%#Kc_ZL9c|a<200fDzcX+5$4{BN8PodzZUt#zEh&Y z0yfL{?)JZ+~0i@1E(tpknb7eP{oRihx7fvKuynAD}1vrjbPvjz*1OYBiF z`)l3YP93H>B`;YTE_W>KKx!eg5mQCvTtcJs0ej?y25S+v$bYD~JgwV*{5wj{9 z_7QnPZ5e#TZCP-2Gh6{nD@)8YD;Ms?q(m9e1Edx{T(aM>>auhs3RRbEFdgu8R4}Th zeGo~kMxr+{5eNlEIb>z(hNR?QKl93mN`r#X;G-M_ZO11%!3G8-;Z-XYMTDfu#wkZ{G zKP&oq4WWqZZZ}|7Twacq8eyo>VL8~>uj}p~D^)g-8dXJPLNcx)pEw(Vg^tc0PmSd+ zGg;npw5SM6S_WBh&Y%$vPhxDAPE31mptAef+n2!YANhxEgbEp3?WMR+p8buQosCZ5 z;?Ggls&3yLIHqJXSHez8sgb;?POf0^7rig z3`loJ&9!(Tf}Ll>-#W0}ydtrB&7&z#@nA|P9b|?MH*{}pH*`@261`sz`m+eG)}o0O zYM5)=!2XN@Z$WLgEU8$t+Dg~U`2UiTnzd3_k4mknwcd+?Z4zsx!0^~T z7z38=<^{KY^m2L(LNWa<{p68nF&nzQz0q@6C2438nr>ko1>S%LwB!?H`n5~e~Qz*X+n98#O*A1+(Zqg|F_kD|s2m?M;}OxgC36 zHZt5tNoGy_5=fVYqUdVag&L;zD_ylx8abD6Z8IE73XvE!5n8V1*6M7`H<%Nkc6lc+ zP9q{{l25e-{L<7OOJNa#PQvQF7Z%f~pA7B1HP&+U1qR4X2HW|f3$Cdx@QrNuCHSM) zY;NThtgE{p?d1m9)R1lAKlayVlr&=!@{1?3?WJK7~I2mxhnAQD|Cap zIv?m~5y_}{Trg-yM|-rfnY!sXiu#T>P;7U8a_4hk1U!lU<1ToPo9*RZ`nh+~o>5&! z>sQr#pBL70a;;Yhu?gM3ptVva{F;mekix8JzX?dcA~P3#AgWc=__AE@!lQ@#Jws`w zYDeMHxObXPR>?dWflls8_?f2i=%y~Y!(mLc$b`GL8MASSI=5q*PpoPjUJ5=NO%-g{p|5IsovP{{lQ~vVx<-rP#r{ zu=&9ydYT2ajij<>bBlNQr3Y@oWXp=6P%2bpxQUtH*{CYQLu34X7Hltv29Rtc5c=diU~s|EQ=4bv$#wnB`bTkjw5^ z$TTRE03qI=4PG{9$zA9UU?uM5)lJ{!^Yjz2HAio zVU#z<*X33jYUDn^cL;SHz$rwfqsK22#^tc1@p*ct-{lrMHP+1~c3wA5u}`u&En+q= zI~-@R)xcq@;q!XB8r*=@mOXKEyx7a?c6y9AEt5|$iqzqtft>AcAfLum3+?lYvdGuf)Fe&l=tB#Q5JT@=!@#j)1=sM1X=qo^(p0` zF&;U_pU9Qx7>$FG01l%Vdlz1p-$oF}ODq z`>%l01SVZ|eVyAKDA)%==MMte%~9--q_f0LLGS~L@*f2 zAqXJ-^^#Jmf}{Pn1f}A5d({nyx`TKxV48{%Jz;qwdeJ*^Eu3f(_QHT1$K3yEEU)7j zYoen~Pb78%V-%j(KuT6YL|y+?I+M2%+&`n}#wvZliEuidwXQ5o1q~Vcyzs-oq7~@3 zb)a-)g~?FaCK0h`@4PBhS7BWAbD@AiALoH7EjtzR^8$@^USxL?afQ1NMdL>-b^Yct zE2?=c6jPJtfrI9?e^i=nRe%V994UJZV*^iTdqWZ)PJ4fMXLghG3O~+&T+Jl-R?l)i z9C4G6By0${%s}uMe>CqNx3RG>G;oqKd7?YLqM#jM*USBPC&*e3W)8$) z2U#O`PaheM8Kbhj%k3PYMvnf41ErA>U=iQ~;I}FP`RgG{6}!S#W3q zU=TbAzoo^yx0a}RlI!`R$c77`0AYW-hcn4Xk*u)JyRx}8QGDd23T0ycR1f@uNlWs3 z+9v7xxdy2SdC?Yjm7^QR`hM4$49D}igH(lrEn7d{&JOTN#qPsu{p7soMJs<{7 zl17@2%)A3tx$4~o+JlepRZyN(A1W;^bMJh_OuVjdhV`qLLGz8)9B%K|A{^8FP%`M6 zoCgz=GBHcv?7OyX>Cax;s>)j5VRDEPVVv?$v9duFaI@;+uffH!LWQ=v=ul$m?r)q) zLRFIo_eu=Ai3VV0T%_3$_{H=ZPY)=!h7|srhpF(_)Vg{WvR}W%EBTE(?Yf;s+yBvuH zA8N(if9Fa7cPk*S@whuH?&*cz^@)H7&pV*Z<`E)V6N1<=rm>&ZP_7(N8E!lLzz;;d+nogbKf zf=gTa>9znvGb~Gri})*IZnarMwTJh^B8wr5=cD;i`2tegIPB$3=)@Wh`WV@5;oQdW&6}LA zlS4$~)guf?pjOt4qzV#Bf=29fmCdY7@sX6GqHzC;;x;{`IqN=2@CbcqzReFS2^mau zzZ-|Oc32=LSz^gcSgQH#a@5zeTHkRRE`_5Dg(Rs4Zm^=O1c(}s==`94!e?(Uq}Rzu z$VUZStK*HTY$>sAxlW>jEFSh8l3qFqhUQ{+(5mUpze7>N0Xbz(;^obLD=qVBIr zN><*0&Q5zPn_G&zk31;CX%w=g*dPS%f&xbIkAt2M6;UA>u!Cx{)_IgXUmJe~&}0w# zrBoWJYh!F{#^pvcGxVUawEmOd&jBqy9?_wc3Sswa!DSHz-+G;zxB+;0i`Ka9s8MT^4_IID+|jjEFpha~K3obk_O zp@hW%-_(Sw>VpN6vk09tv_e>DjLE3Ay+=kIo>tDaX4HzV5ciycml&K{mdL2h0Ww80RNZ}7y z#QqBwfj{&3)KOC1f%5nyq}4OXt4Y4@oJyt)*v##zBNyE{oiALJb)1}DMVOvmwUg>v zjno$vzccBx|7bR4f6^{F-?pSv%rNuCf&R~Mz`r?7W|1%W)I5)_noh?##_G@KjqPr= z_(0de;7krb=FSg;rLyNAlR5g66=lBgjFa_?-A}A))6kcFkfAUAjZ$8Q{ralU`lj3Z zg81^`%(l8&^i6=WU9ps{CC$^*0YL(NGLeu_ts?iWRjSpTd!*IUO~XKx3T@d>!0S8E zto9ee9*GWB)*7hJ7v>lBFn;S#2jw7aJYUIGRfN~DujmB^Zm|t3<0GYJJ+IA4_-Hfu)Onm%mF z4Kpx~`%HIiKo%MpwDVhPI1{Y%}%ds!n{@DZ{=>}M{FZ>4wkbDHj7ULI=u1wTDK zkqwM|qjcnjXl=FBS3vUiB`UOx zghp7jiWk5i-1gULc7X?Pps8J7J4j2cJi=e{yzXHyP878R*Euue*uRg9vqzWtB$@>7 z{U!XBQz*2t(JzX5^+*_WsJHM?X(Vjh42mr<(jhj|?i`QxWSsjZE(&He6MGv)9G@ zg{26WQEnJerWM!%m2mLM_?6Gu4d0YX0vkc{1_#fSw#9*91|2UD9LFpw#7ANqfyPp0 zd)pgBRo%xo;~PETc43yHu`Hz2lB3FECdx(k_qtugUhXHepY$#V%)p6>pU8K+D9g64 zrGO~wn}Bc=?{A1u;E6~vLsxhLvs?j`y+D%_z=|I$DoyCP$;@#pnVuyTE;hxj3VhLC zm2U+*5eN7;TT^tM(wusf#j^Uh$&m_!gjQ_F=TB=;!{!E>{;^E(4+MKhs7tj$zOOr) z(OZQm9L4}%{PFv+>cjrQ@xTk^2aK_B`lJdmPfQiT0=|H z)1tc-*Yc5Ct}Q8?R5$h+p7Qcd7ffcM@ZpZY#)ALgRlWJ+VG-%6-3XWyOu(PwQm zJu5xZR64F7L0pthXvyAg*VjY%x1j;ZSas=2^!{$xa`We``5wx3*`YsjrTLwFNVpHP zG(a9+{{F7m1!M*d5uRFjx|`$GAVu3bGTMGKAlg}j@ZNq-V>fMW{w+&_wL*1PLrI>} z-~Z<)QiTJ!9J#vI9HII-;_?zjF-m%A{+G{Z6AMwS9ed=wb`GyT)hO)&!QlTh9}|ec z44l&R+#|Y>0YhbAsM7(|^m&#oWU}A zAi!syJ3!v#&s_?%A>lrUy^&j2Pv%3RsK)^^R~oglGprg-8L?lDHHVhnGP>t%E26Wm8acJL zrpW>A8SdMVI&Z{=?GdgyJp6!^><$3(ayN26;}|>@;MdZ;YYs0f0;GRnE1Nt_W}pD ze#~VJc=h>7n$FhAyeruwvgRYf;9^a2i%dk+z2lYOmt@GhyXhdumI4#N2dCnbFYM=! zzeO&r_uc`uXrb`Aqa@4S3mC!M%lg|ThU!|xZ8W+w|7?UG#3I-tom=E)1*d+rAAe3w zc-x*V1Zi?TV;;Aq_C_CDuf$SDe}5ZY+Yylr8g!WG`JKk%1RiZGXWmoEBf#R{pA74`U6cdn5^U}~ zw&BNFe79CAP&C#!~MdlGXrW0PL{A9x8v$2QSW+_(77K3Q77iyRBv z$UIW+|HOCNy5J@s`{0NKgbkB(`%#FI<}n8Vv}n3uUR7G*i0s4+r$~z+o%l z3yKO&7LPvHEZFI`d=Y2^2mxj)*Kn{2wMrU0_Qgn*DEWJj;JZ+OPdia?FMFcvk{uC z?ni+bR)ClUfp~$vmS*?nFX;yOQ3cvc$X7_eusnJmW)ig8wWyR5K~x`3G&~%6Nz@9!v6cSX2#K=bHTXbP+lA-Br>!C|zzIKIG;lO3FY#UPFQYlK z7e&e+QUwH7F*i=vqR5kk%9e`6&{ZP+zZ-;RF5CWc#iBD0JhTDYjq)+wB;V|mbKb$z zlJvcTFbPbep3uNZV9>G*4`rqLz1R+c4UO%K{&^Gj@eC-OY8p?Zf_ud{LUGmK2wU>U zGCk4L2X}`-=}#^Mu(+?Coqix!!&vV3HQ*zW^io;z@&0vRt<%!{sOxGGT{Q^mxeGPJ?)F5s> zP^^#xGmwT&(k*9uFxAqz>Z8Axfd_Z9B25vyKze~IW$XIzy%PpV#~5Ti^TXU+_hMqs2*8zaBq+D}2}IWT_27`OhwZPh~M1 zb^DLY6Lpx7DDWm3<>JbFU=@amPy<{}ASiL|^s(&l!F%03@P__9`eoRg`o*_-@fl ziUCr2&irFWRx}K(Hl-vZJsZ-?k%8QiuTTpdD!Y~mVvOJW;re*X5L z6Qz+P=tEY<*_u$7g!+vGMeBR~oWc=WAm$Bh1=jznX;H*THGGi^XiI*0(-%~fm-e(K z+s=>ZJYtTUyap$#Cx)asb5p7oX6h-p)T63aVd6}CZR@AlCiqu?sfjqS7Cc+(s;v75 z{#3Pl90;$KNGFmbq_)?jls3Y9;9P>M0BX9_@Z;aRCMPc61O1u?9J`U6!9l&?hfXQ`J=dHfZm-H@<}Pn&z@Ivi;o2 zPAKxgU-0o9i4fL+1_m8BT=e}HqVLy_NI*mq1-L*(5^$pa+ced4uGb2tT(ndi;$(%1 zu5gBJ%@vr;O|JwR?O?lJBL4{Yx{@;LFQI>3ouq753-t5s44a5^QJ-_ltKUpz^da0P zqO-ZI>Mw-!hb>Lq0~N$oHrbsF=nSJ~i7-vo8$sHVCDIS==X54C4;DiG2U?~ntzKE# z`iLzXHGZQf<50ptmsJ$d)G)bEN1!m3JwpY4hCQkn7kSW{>rv!_jDVXCST16@9ny20 z0O2po4ZxlDM5N<-qyIjKgy;Eb%)@8f2C3tVHTrnAsFX3xN!z(IriG<7BpOx_KJ>xE z9g_~6=_l^>+m z2Kh|2CJN-R+&l{GC0ax}(@m-&tBe)uli{5KNj@ECqz~}($8J9|{RrEIA@Q$6CCKdb z+mOB7kMSn?Gv9!0&`a<+5Y4f+ZPKT+n1Jw{iq08rd?m(@VXAYSr^3OQ?6Pthj z^%pA2=6<9U-}2z^yrc4@J)rT!Nx zh#=}VeF9pSx|yC|TJK zgVWBH&}v7;RXL`W5rPAWN-A5TWQh=-wb-?mt}k0>GD|UuwSMn$l<-_FyS`12rwi5vM#T z9d-CLWxW7RL^6><9pj!8p9COmHaD@y7=izC2l)V)phXt{nTa2$- zI^_WWVHN`<5Gbj}u;$ddVuHa#xp22ibI|IEPY7BplRR9z?;YjF%b8ztT9Sfrn05Va ze+sETpiuRQ8Xhjq#2sW0-FrRLgXH&JV)aBa^w0LTp{zNqncUm zb5pKu*i|5pYQI`ri*?9uK0kyL4eQwu*T#mffRJA7Ck@OYs_Yy2{%B7ZV+@`iskWK9 zX_MoT8F(;gLg^PT`Zy8H{$2kiUwwRHN}0ggv}3rqJQwZMOK3YRkAa9rYnis{L#ralk9wR|1*`^;XqijMZFlL3V7H3{PpUfZA=oy%X2xKF|&cXuD9+CXP} z`}ap5!~>BW;$#Gu6D-GQsOo3be` zZ@*Up5}a+dpZ;F`ubMGKh}m9F2TPFL3T!`u&e?wMZs5- zS-kog1m0s}MnYev0*Lv(jf`jhQ~++!Af-`#abCd@*~Eu zPh@dSMo9X$>Des(sUsuB<*^cfFIi>}LEd@D!76Zyfp#cZ?KQA`od&i~Bg!0>^Jq}v zuYS!O-xw<|jqK4lZN($V9Wg}TRt#23sw+dLW9u4Ps4OPTzM{#J zkNn7F+}heE6JXJPfEj)DdZI4MLSOG4Q%d+)-{48s#Oqz$AoO@$A7v&%<*#?b2Ap>m z4*X?Ug_{5 zx$bswfvo|MQwH{qq;`-d!3jVY#1I|uhd3@Ks0=q%6j1%eC$`Qv!;D|#qg&Z~O6y;E zUY3}8!~=iH2)KN8sOqbXktazgg8p6~#DF9XJ4LEl+Ij;~UO$_Ai=-s>Lw^wj9(>I( zP+i>uDwT`C4tLul3+;tz|9Yi#WczHj7v&Vv4F7}v_sfAKr-UhAH9cDfe47Im8gNq9 zF;STS<)dgqNOKFb$c5FfE_@&s2X1;y@ePU+8T9Gw+@e*vuO|ZkLOUmp) z^6?}?$PtllG~0Mk1{8#mX6TyG>~#FK2hKT|8X7TC1ONn5TSNsUnoaZ?fjSlayRJNX zDgk7Co~RdItCJ6Dw1Vf;-Eb|bU|CXn0Mx%L7;PHjI8{7QW$G1+gp!nG?P>(&de67; zPaUo;;@HiM2s%;Sr-6bGkPq5qG(w}g0YkXG0R+wBjsw1~P45r4(45EYGCHDL$&Pp1J!NvgsiP%<`90r}0JD?&HLds{} zSsV1_1`Z8Azw~U(>b=jeFFxLKAWBpgfDj{S`OVXPNJIib&nzYdvKN*=Lj>*n-+dGw zsv5@YK&4Ac5)k;~^~26xxoYMornNk3z5Nv-zy8*{)Hm44Y<;y$guJC2VYF}XEPwpN zxbdN_M6|`TPnwg5bD!HpE=^BGKRhx)^VFlG^-7&3Hl$ZfHRRmb&cR!Pg?R!63EqQH zj@PL_W>~Rey1Vu44bYHl&zvwtAAbSmuNPkJ_8gymIT1ygtWzfj1L2kA9m) z3YFUwP80rJA}w=YUI3sYQi=e8_u5nP=-5-@l^WD+Ng@P%Dk*3~%r;n30HO!5 zFKY^^L|;=`07$t-9?#OBXm*79`=A2O?`qpdxpZ@1oq}qjJ?xfcwp~e*S|BtJ;4yv-<{)c6=5m2NXq`MoW zr5iySY3c4x=>|b*kZwe}JER*yxgD7thGL|*1h7y`)ajn zUg5cj55t^2u}hs#VXgW6Bq>Ztea_L<>HfQTUg?mf-9buFb|<2Eh(aXD-m#Tp>Sm&7 zcSq4mT&E65dF__?AV!uGg{=KhzUQT>G6jx1q=dmZzYfI6K>LzfNOx{>FG}|WTHyG*M*^qXHXHL7NEuK}@>qRIo29>jNe;z^RP}a$z(Pc( z&!L%uu$OtaTjBPw_&0_p7#>ts!3tR%+&FD*tGpk-Pal}FaC5c>*SQFs&v~|vp}R7<#Y3*wnFSbzLP!wmrMlXq2#Uu^fQAIrW+?^9YAposutTjb$7)LTPLt z&vM)@)AQKM`i=i7F0+)Y8@#V>XUW7>geq^;mTO*(+BD*Qvuod@aU$@|ufQ~oY=(77 zm8}!h>Zp98ZrA585YAKAm&1Wc>FVd_*xnZ{JA1ah5!ycCTNy2CJc)Qlkx=yRphlx` zW)Wa&V;!j;0fDdVv!#U06YKPJ9mD$Mz*|lnHnrdN(SvlbzA?s$J{H?vo&6GcGLT*g zWbG;fuzF34xS0tOMFuM)c;=)CVciaS5kijf4{tRoQnvCX$7fC>#j}l*D6$y+q2mUO z6)Prg{5jRmfivV!r+-)M>e@(gt&-BVzZ(B5DGYjOaJ$?1+NEzr+Y7-uzLeE`!bu?3 zOcJ!lM7Mgp?={WOk}ziOPMdgSeJ>RpCf+#r)`Gji@;7qV6=7Kp*Z85Q+=Uw^CXP5? zkJGxA=il{Z5wW>lTMiv7Bq?3Uw0nVp$9RyT(h`g}50Dg5s5}2YJuUbEAwTjDgIqeR z_pv_}?pxA4#Q1(xDPd9wyKHN8U_gVuccvKqX7lMG6Dhq2;ozFWxUzHP1eNNR;h)>Mqd; z>k-Nj8MvCWr}~F!lTw|L8F+Y>I&3Nb?0IA(Fnn@wKm7E|m^4Ob5rjz<&yKG{7(O^eDs6Ub|Ep`cg zj7s1wSd6HWSd3CpY>ivsDy>-5(a~u51MB4MgwGYMK{z@x+;K(wdBu*$Fxli+rL{Mw zo6Ds9&aN%D6_V2j0*G=AW|75T05s`Y=}wuTv?f*R`Q@rQz&7n|-$86O`*jtGmoW9Iuh>f+Jznc^-P zV$xSYCIkm`@~zlyrEp87OG=P#qN1PQgp}BK+4_cLnc)ophp(_UgqnyQu1?Pd(F(h$ zcV?{S^{7oNV#hmS?c-`9c7I?nZRI#c{$)K=nvKu?orju(lf!U09A*6ID8>=aNI_Oc zp|jg4zw>L7+0e7>^Twt&xJO{#j}~-P$B#mX59xWU55iAUg0Bf7`|z6}5gEs=L?uwI zfn!OdNWw_|x^F0>sF^g15yU2Yh*<^{Nm#gX#Mxq#v0uKJ&983D@yS^10My_A00w?& zbF0M$*4;0Dm}HLlj6=@CvTHn453CpcGeEE(lGZ=9);BY_W*p#Z51>A zK0eU9d-3p0T+W_sg9AM_y1Sa=D1Y=vFY~F_QqJ2w ztf}vVMZiJ+HWpzGSjozhHM6Hf5sHdBT_QX1fa2oG>%WuWx7?u%Z9vmyGL+5ow@jFW~7|Q#PJ2V5Ajf&`LwVac3`xbtU8sH+k%N;$E;rFPIWNB|mV5G$wPI8=KiK-?PQip?!*kgiJNc-fp zS*BuqZSQF~x>9OJ&R0?ETYvIrHRbNcjSmX-J%ed>=hGKi#lAOAcM0 zWi0ak)<%qF%q%_>_c1GcwkkJsjT~e<>4l zck5lW;RfQiampUgsvSbb;T4=qErSrhN-MR0JtB`46GM43;Kw_XQH34IK)u3zxJau5@I>7|UZol)^?U=t z-vXmwPP&(z`|+7O9-c+2+QJh3Z(k-cu|do_QP@3H4W2u#1k<5+eMvvdoLrq(@9UP| zwzVy9#Pwq9%2>R}AI_lFGWlhcTG%yid*p*lK=5T}jJ@%r6U4@oOo70i0Qk!!P$+F4 zX>%h9UQ@^avy-vUaxa&vaG3mNO_wg0FqKcmoEOm9U?$$ZP*5i|*a;R!sNr<|R)s2GH+A&=b| zX#d*B|EyrKS*X6~EjKEHeln_(i;GjS!H6A~!^=}vgT0Sk*WTZ|=#lyTQBe5ZkC|Gj zU^Ef0(sp@_z>RY-{@GZrgbk`Bn&j-ve{-t`KL^QP65rU~rSB8FI)m4xU6lK`vMXLWdq({xgFE zU>1estZY@w6d5=dr#4+}8b0F) zUf%rPVBc}I+`ccx>cyZ1{qBDM{Lg%?=*!9AAGEtaOA6$IRnjMRVL8T>D}RrA{JptJ2=@}5b(p|r zW%0wrxN@Anu)zxIE>%Yrb8pPl1`3@cHf*cK!Jlq*n zVr8Z@HtFSPqMjx@p1{6Yy-&4EDLIjAq~sVt>3g1!rK`Vz%5ievzT^$;v^7+p z3XtTvucVY0Vvf&E{R&0C*^p-6!Q9-#Bdsv%00IkWC^GZ3z;637OLT!610dlT2pi^$ z)qz{$?puN@*asz+6I8^V4B_m)sJ2D2`TmWM`%AO_gbPb|Oke)E>4Tp#MTamoF)Jrd zSSxZ!iw%T~AN^fFpsXMt8?_j(98-{&?Ky9#GD^;#`?Y!d7_G`VYG#rdTqX;M#$L^w zqqp(&gKVgPKa?xRqxm=120%|EUauTex>KF!af|6X!mrsh^?-HKN3A{UH8)Ew43?+OVg0}!Iu0}-z%X5ZXl`2>}^6$6lbJ}jL~MqQamtH4KcM~=2R(; zm3RhUMu2K);vZ%5ANn*63c55CkZ(UDDD2Vp%gaOl@l{POPjN_VgD-u7CiT5R>75hK z2PQ`s2_*#WsX=8acc==n)k380Y5EY3z5GpcBUWw2=C&%=p&2!XOD9( zG2guMt4`2j>9TZK9=Pr1`A-=2ziA3AF1l^kaFeP=Z(;Mn=w+j@Zxh3(4rAYb#4>TZ z;9f7fSQfR-(t)!CETbe93ozFazIR%UgHPH)07bkEl_W+uDZ-g6C!U=HBKjB{OhE}Qh)X1-9eFQ(hc=)=39uil%Bo&&FP^@is;hlv zm(T>#P^3)zmsr1LU0=@p$WjhDgvWU&j6Gg%f?hpdUJeL*ICDq+LXW6+H>67F#tzHk z@}N#QcOZzzVgVO$7WrxZJnG01%qOF^4X-#~6RdskpOg|7gdnb!?_fwSgvP5l*6#2p zVgAQ1^HksegDL8zG2XrVfcXgFO_wIkQ@{|dlWO;aQlRBgr2cutFu}jG29!>c(x-+qi!HQstu*{5dF(EI*Idp~ zqU~FZ@~Nen;J?c^6=v5~ohvFxKQ~O*{Mt`d0riS(TKK{p!E@q%v>&Kug}0o%d`k(&j7%aR3JXCR;DiE4xix*G z@qUp5NG!WFY5RcVdUsLTb$QANO0i^wp;V2iOuZAKMigmu4C!n#n;9~@k)+HBY{|Tt z7ren{>dk)zQ}FSPk~+RglD*UAgkX`EL_29*+#h&5yE)uDBLv?4V*}yA_m9&&W3Bt6 zElFT_Wdt&fU=R{0`%;Hk?7)vlzQ*LPosW{@9{!#{lbPi7IMvRgoW*|YqM=>2L|?1akhM;3~# zmk>cy{1KwFmu~X9_j-)KK*SZt2kY>kyaJ}-_PBN(l(V?T;!ENnaD;zZnkB{jGw}-} z1=Jz$QvA5>(N9l(bF1+NOBG2ssf&<+n2e=|avn7UZZoe66eh*$z!6FyVQH*}`Zny| zexEz1*6_5PwXG~Vai;9tN_kH5z5bL6vp|KQ{r$yda23L z4lsV=sZvb$skQpb-Sk1;2E?hhVEYE@!In~{nVeW<9P=!Ue^Ut+DIiFZa(ZJpr8$U0 zKtWVA@l#()SRJ~B(u7XVw!JezP0kB0t%oM~vH$I!?lN=(Yr*P}gfp_d<$#id^G;V3 z#ViG!d~2W+aEFHz`*kJf)S6GufuG8CQq&O`Y1=g2IXL3h3BW`BC#!z&eR#v;yV{(C zqI_+}vJfMw<_im2X$HQSD=Ceii>jDIHV!Lpn=ZR;zuwRzk6oj4ExzPetLu%O%E^+vBO5BT`J{eqvm8 z3K%#9m5)Q+0jsk#-+$D~DoYhH`fB}KsI%bs2L?hJ_%|u!OIioz0UOs4<{$uY#V5qb z{gV+qZ^d_(Q$F5#D$K58NB(rhTDF0IrCjA-vKDpcS76mq%rJ{Tw99>Vgz}PdDMv9 zWVj#3(5w;C*tY~fx<%Bmma~@#EDE^YPR}}RCAu%zzkGXbPt7nzkp*t(^IbzBZgduO ze}I1@I}_|B9NgdO0Zknf4gM(vKGoWCP_SK}HWCt&wszdX2oH}DWN&g9M^qm3ZWN_> z-f_YXGgLT$uHFLr;x z+GOl2Td>zwgNEUnSElznTuv94#lz{B;J!IYtoWC-2F*?1po;T1?V3!v*i7J&Hs)~o zCsXS6{Yv{h#Z@Bf+2s1C8+6q=@1|;|r3jj0E31|dHMLv|FwAu@pPRf1DaDjbsYBh4 zZbbnO<;g%`V>ef()h4m2BUln6_6iGITm2;49K-*CiH8vR*PmLcJkH(aw>*UCG2hK_ zFtM?MMG>Au5Y8h)+zyY9LfR@&yw~_#^=yefJUqr0=YsTEXi_V{Y2P5zubPWZq8Pc( zrEqJ}b@S4ddc;m+qE1uzS{QE-<8}Glida1gCCu?*@=TAnAn#1j;UU6t`RK2&%aZK2 zuc9lrho=YmquR#l*ZrMT;fsWSx=rdnv${!=8Y6w@NdR&m6XfTil@vD?pLIt4mbN!K zSnhO~e_5Dwagq+I@>lgQx;gy`PZnw3^P9f#mq@7>j74Z-L__4NxN6-9<$u;>CrH!! z7<%+D!3hiPe$OnDJ$&33V`HzACC{(b<64Mqf1jnvZ_%@_<_#xri%A_D=ZpyKa7qi& zsz|`)@~dgvBCxxS*#;ch8LHSMeu6g*Tl?ktYHEy(xp(7__h`(_+TXA(=fO1x1H$fx zkoeu3>d$^2OiT^>oEV>umb6m&1a8F)rcjB%_YO`JtOlMQ?Y3ha=v=KV=9$@8O)Q~d zc(OGAfRIZ>sW6I=^0j~7uMm|T6`Z!3Kt=4A*6%nj0c{W3Bb1yvpq7?Dv9wuObs`a9 z$1oRpml^*hJYi}o)J$2r&m$=7M@bdN!P-_Y*NK^}HI_&f5v9C?H9AkA-;~FjH=N>W ze|O_jrIjZq0{xWluBb2RYl1{b0r&E)N|g7nHu-kjxveldp(8u7KbRIlQ&G$zum4|w z<|MEG;m~l1rd4cKaBunzz$}BnTm7WkmLX7trv7}UM)OTG@x?9iI}NX&-BdC=eevc3qFU1<3_jR zLqC~D^c8~)y+2{dD3uXnXgfKjkE2sR8QMczb>L_=+m4no-6|BY7rVsSspLl`xcNjB&vKuAk+7sC+_}MdhPLqlB9tR5(JZ6Z2!Pe z{fT`7lo(S?{-wBVSn+tRUicaeWOyL&mDFE5l)06nCqCANzqxbveP{O^Z{<=2@W=|E z+)8Y@Jw3UzO_t?vT;i8Ns4SFeUme0?Z1ttJA)I}{FD&*S)E@%v&wgeNP7jPiUC}98 zc&Gr0JdLli1Ge>;hbxIl8m1|RT;p4!9zefxRw;JfklAhRG{qedlrtBoTyFh`3m8x< zGq<_ybdSR>w-9?g4<^p4^bEnzH~R#{$nS+~p15DQ;QFpQrq=gus+i^KKP(}b#IOnoxDGW(UwwNe9b zLALE9seL;he3~?ZNKrsx)Z5&^`gA^tF_7pcw%XxncyK=?qiTmRH z_lRyHNLe>}hkX`HET%oaDHgB!iMbj1v)O@UjE46^GjM@&k1^A!JFs)X6j6H39k+#( z4B1yTy38RMJuV$d7S~&B!!iQ)WhYM9V=E||d=$^aNq$yPQ(5Y|f3RZhtD<1ee)rfL zuzuDEkpzNBzBj8E;k8bDS|3NPTj2oOislOJV-V9<+s(uM>>Ut9_Fz~@>|HOjAYS(v z0beia0?Fx(dK-3yq~h^=vi1e2dPU9IKV(8dEK1jiW|v&dtAEXj1muS2#@1GD%|w`A z60)-DrS7EGCVRt3wGz8OHq{;p4?oy+flVUeY5+`G;PzLH;E1RSCuQtta zI%`b6Pl5KS8{F`O{CCZEMoYsbS z6b>c2?vn<%>wL5msj{{@+a>&l4iSrxp(rRa0CzHC_L}BmJbxS-kcF&%{f$p@`@O{& zM^_g7sxv!!RF{OVHR5an>8Zx+Co=-h6;vtv?Ue;qly(4i8qKOCNR6v=g9W33M#8@F z!UA0?R3%MLA&wVK9%B}M=uBW&KOB(hX66qes4C?=pxvDyP}rs;M$CCi`uT~PuT8Ls zz^}UP7f{$cYfay|Z;}*QC=h8d<|_T{wKtCuDl=HM=KXQsm7m;8Tv8r8PdXw#6kUMG zYdzt!UB}7Uij$cuPKMC0D50{OVtmrc;I{~v1!_hop{Yn~Wo$OB_j{H*yN609_T?T3 z{>_~b6-A}XfFA$o3pby$vXEb!1n4Ym{&yQnzz{(#flRsvJUYRO1{BYVj4LV97)o%X zr>Z#|p`5~)2W8e8ZCm70qo5hgLOU}#{dG}sdU~pn1qq0jzmCVXoLpU!c@@~I4*gwXx0%r$;9DVaMjI+4_rukC9{xTMQi^+*~*g`eJjIY z(*(sPRqTggP#al*VO#LqQTJG03bk1g&+&nZ{x04Wq~%k)4Y~nTyN1C)k^`0TPR}p2 zTCRPfw1k&AdAqFRr?(BjyB+-;G(pCQx*R6rYjCyx^dZGxs@ujNpKD#d>R^F@SSF5G zFF>yJ?wnoRI?3E#7Lm5X_t>Dh(jqGPv`+)1;x!5u1F<*dLXU)z=t8{v#=qy!xpbGW zEnDhEg`9v1gW)17EGAq{V#}(571ywBIfh%;!}z-aKNALwo*|hDa~k!eh2DpJU};8; zcsvGiFa(OEkVTV+PpyvMbB1O;8+E;x-i>IM;(2{N0b1RdwG7kb56;s?Lg2jraFQ*3#1Jf^x5H z_IV5*)1lG0RmSNhv{(FD-z9Z+T$*0LRD2`Nnj`D75OXkmL|ZF}Lj}W#2w)D*T9uX% zw`uo4KxSPf`lL(q;DLzdwH+99`l)MmYzFVO#?kI!Z+PC|uR{&^hIbQiO%z!Sj8O0FcIp)IteHUf(`b zUcCNMYPuy39DB?_@_Q0OpOeQv(_7;JQecl-x}@;*vhTARcpo|~@7dP|P}hBbFnb_$ z!XRd+155N84C?gmtSf5YizvBo3aoISdFGh?-E z5Lr4V3l8BNqMKM^Mn#4bKa=2zn^eU`eM{rL;2$&1V#(Q^&%w}W=0~RB0>#-e*aJE4 z8SWKP(#Vy3B7e9DMZd1ygvBhOUqO;HAAt2ETS%>HTpkkp2dh7 zI>i4$v$fI)sB;x!xNQU+#kolPwp?G(Ix#xVU8FlTF#y7EK0m+gqJsx;ENO?6{PwaG ze%ztL2t;)or;Q+e5aziNh9%64op>gl9YLY544H6cD1sGkEa=Zg8^8dAp#GoX#WHBI zT5biV8O(I=)M$)56ZW5fvIfi+`_28WJYFVV03DO3l#|X+E>W7|?y=b>EZ{-N3TMOz z2#o~?=!3*SAH;lH`L1i6%@UDx^GTAAsXgMyhbIEloLETx$BilLAMPCAG1C3THMrgG zCC>U5k?0r|OV8=epT8p_*UzUT|GBZU=E+CCSg*@xdJ<{C)AohVoP^5LP+qRN-Y*sbzz|hN<;p#daqwJf|L;9uNDw>;vyF ztbg1=_)Yt%)f%*@;M1BHHC`y$Mo&*q8D7BC{~DLI4MTev;xmsc#LOKn$E{q+j%CBv zB9sNbGH!n`r=ZgA%GrV0vKS1>RAEm4_l62muQ;HHO3bLQUPRx5^V}mwf@+4|Sw+zE z%1oYhcwOHa0TXi(v93V*wGq{KWzQArH3>s(Qr5S0co0N0(0#W3TS`GQE6h3vm$N1I z)LSX)l&@66JmDfg%hB(R@<=;nt~ZDs}) z1#Kr&XW^2~U5)epnQNdA#v|lafvi)>3%{*wRwbIh-8=p@PanL8zx~~xF|tPi}cOUfI|TlTePYK&mu#Km_7&N(JLEB^YJ+O0;AZf==uEwHnX{$Wn_G#znY z-o9@Wl{@r4d?S$Ak4HqERTeJr@rtFJlFIEv1RmT+pgEN;-l0}nw50N`O9cjp6j7D* zd6?z?VP#WzQiS6CaGLFILM72Kx*4tbx@5?WO+TIsDU#FeGw0#W{7LzaUUb|kHda#Sio-0m)KNb0P$ zu#H;nEey|lIv62eAtYQj+@f9c{2P)q6)7ePM;^i} zHR|f#5%Jl+yF_1MJS9n}Y+yRcP5v^4%A0H-wP3uuNVQgG90a8tOZYS2Ll^m(2??2U zi8<#p_e5x^n0{|PYyDQ~L7Dkz6rz) z6RXpPT{ktQ%%J(E*zoZ1)oxRsnUz_!LvntFyta3Zq88oruxa&shVK`ZFJ93yGxiK* z`k>L57G?$Gb3k%X@IrD0Y4bzglIQJe_d}%G(V-)RCp_7olo^#Gmc+-Tinzx|*1>Ok zL!dhzB5b$wu{+ed+1q>l07;H95*U-akva+YG`1bZ;Pw=#*CqeL;r?CF~ z5T4=7s92(*9S^g&!(3kR_MWq!+!CP-VkDGa6biUrUw7%U<0c##&rVL>-ekc@5%WA- zT4P1IN7#s9Y~{9#u)X)&Bcx^HebFTuv$j{l>p0zM-X>((CVBSmlb=ca5qDM_zM;pr z0Eq$J_H`9d5iBAgg{4RDnyc?&kIML)}oM^^m{G-uV6%>pq+n?Soo{U8E>v0Aox| zozHbj2yKtJq3VnFc{8S%^4DU;-C>*x|XI%~C>`OW80sQz*2 zH>tzl>ek<=)YR4AM{F93t5JW=Q^}4<(eyb!HjpvfjY7bu^%RY91V;CBvX%9NrlX@W z{Wh8w5iKg@sDx83Y4SY&ncANln{~D?kNOKId$^W-YH5E@2C1oe`TvMl=V$zM{U}Mo z^N|ca-i9=W34w!qo1EK|v>r|zC)N$fhFOx(JJ{zWc}L^|DMnvAzHllpovc}2IrP}N z?2_+O!2Cu%#>)T6Zb}#iM@<}K_p)X8k6rCb&$OfUq$Ktpum5?WbQ0%^#4NRj*+*;n zS6`SlExv_MG4+g&qDa2x*5-~=D1fb~XoE&EayIsSBtd6^_5bP3)SlG+d5Gk~PXeD7 zB`fxlq)73{uS4hNYA+Xg6=jvc>9%8M7}cAK`d$JI)g$W@T3i+%lyn@25^);%vp`6mO)s`_yBd|AyVWmM6_q2 znIza0n#khQz8s{tI~{h-YN(lh=Z0iM1VJ-QowzEL>J|$W)&9xCL4)wtkP=BqPp>lN z8_pg+zLKhX$lg5fNej{Rnth^Msdt5~)y6=6c=gS9v@ZMmHwEMZZ=})0ZwB1Vha1eC zIra$>#^n_C?S|r#;(QW!_w5S&zI?%ExL%`o&3A|`Y*HEsYXoRt5-@hTfhv4uYF)G0(V4dUXCL%(wf>)Zy9|JO7vkohV31dMJ zU(AV9CbDe$D6{7%1c4oH3uwT*RefJY*aN4Sq|~^{aeLJZrn0&ZPUoq=Eh|&)|Jg{W zAo*FB6ffjS#l1vBKu#u6gKl{?9N5BxwamO(X>(hrw;`WvopW(3$D;}<$TKu+Q$doF z7V%IfN>%E*&g%dT*(fqTq;3kZls9fOINsfSB zMolJW!`*wa$IPtgQp$!(^=o_R)f^B1g|agoeyP<@oQScBP812;NO!%fqpr_Pjr z{TiFhODRllTwsRO@cpzvuUpMIFSy&}-7I3Dq{Pa1@`47zx8rdf%5?L%13=?LPfGDX zljtQjo!sMNN|hLhZoswwA|XRwIL}8Fm5}kLO*Copfk7>g;W8ItYX%{7R%sHxSrR)P zwUFla*{1R1;Rc=JKNc}t1h;RBd+(3W6nEuO2Zw9Y;(oMNB0840VNG9Le6hrS$(!U? z_Y2=@s2zsKWX(+uUl7wg3~{XXCHsw;wX?pfk6`T!qs=XW|g8Qjm71i>c?#C&5u)7Ph*11iJPF5YeaJ3 zA=J3Js57%!gI{%mnU5ghP`-p@5WIhRc3Nq(X~As*UV;jRz{1*Z{7Lo*K9^#^rxgfD zmj*G9moy7+ZY$aYU-$U%W8l5QB)HYv|GCv1${w|oiCCFoC|NCAnrK6brx}tI2E`;}A-%RDjQ0u_32*!<)_k6&TFR}6Q^zWY$ z#Gef!q!{HZ5H_86t%<*Wi0+4gikr4VPnA~^4Uav5_Dh!RdHs8Pc#1{Z6s(NS1aSKCA9* zk_oZ|d0zA3xD;(njSkzm)b5;ur8bqRY541ov<0les`O_!m!8U%Le`OB0wB!@=%2EZw6RO%k=fUHh3j5W0}! z?jYuoqTzmuI+VtP95y~KkvmSAe`(T_a=_=yty-=dak_`Evo=rJf7JnN-?hNbN>Njx zBO~~3n;Ne!C(GpbKV<L|tO$a!F+ik}8&ke{%zGNohJqqj3 zNcYLk32dTBJNtmG8WCnBWNN2|N*i>=qVkI2Qop}UrtX48Utz4Z{4=~7h`|}vQ)o7X zkw3tXIeXZ-v?pI^O}8(vF~Z5}Ukwtfz={NhdRB!KKJCTkoGJ%CZBuraOxM4yd zH;*#XR{r=#3apaaL3Qy>4~>e`3+zT~OpnibI zbcc#W=@?ScVZGC)8B~!!F7GK$vR|W+H91&s$GY_ZwJldKtb=vZTC+*b$=(2Mru1sK z_F|B3LADgifB(y9<>;tFc3|3_SYLJNwz1m~6`h3~hS*kUi1-%YtL5UF+n56upEioh z%gzfF4km`@$;QhJ*vtrAxaq+sO(Y;N>HOV`ly|Nx=F28E`{0qbK`rQ6q;UT$8IMNgKd-Xuu2gg9Vf=zQt>dzCCULW#c>sd zwRg{($45zjF%MjQr0N$2w}a~cZpXF4>=c8feFsJcj-eymPjBKyQ|ziY!9oek3u!S6 zr6mt`(Tb(=?S0(n{%>mKsif$C(W-4(TPy_K+(fNeV&LzyuQ+i@4~~ju zP_8f!HPWtav&1YTY79tSuMixq2@U#uv}F_~A)|nv#iZ7Zis7wnoBD_Q5zpFo%4T}g zs>uykiwSsOp;5^3f&UQoGCCcx?Sh&>@8kc9RXOzlbY{DFhK!C1|Ql3!HKN{Bj`28907wA+RsCW?9~S(H8k z1i%^b4}^>llaLv8onI{!pUH@*Z>bB>j$7D%3xy(|e1B0h@innEY~VCz8eN>T5o4@& z3mH)W$-rPTD9oWgbh&Ao>H@vc`lYUV6J%O-@S%>b_KBDaE!EBYfbwG=^RWKdz=NW|ZnR;KR9JyU=S}w1`R| zOU^&@_IYDt&#_wLd^Wyk_C*W!bDWS?W6T)wW*&dj@p9S?1nbEhwLkkT!9C08~35W4jGtEgWKDR@kVRKN_eX!b1 zBw;TR*z-(^r;tpr-CuTVaVH>8oBi$`eSJ|Kn%kQ?%$u;`0)WM$#O!()$i=vL)J~#) z5(75Rq#M)P{@NH|&ePBmOgf2?&~6eLkP*y{Q)*{8@@mJXyT2hU!>TP^J{6BZDS(5E zdTVy&6c{LnUUE~?Ql%;@i)c~oi>f!+?XuGF#WqIgB~nCE)USFt7sf{vXYF38>)lg5 zyU`Nchi^QBtSB&gl|AD{l5#zH)FW@L!~)#||BCDuM;=^lOJ*A0moK)28jci^PfprB zg>wtx#y2lnq?~Q0pLR8-^f+^zbU6P7u?v^F>V*dZ@~LlNI}AC^ z1fpA12~MsrJueV?a4GaHAc)^S*$<4t%PGME^z>VdJkM*ZtgP(naIoTA*;ro}JJc-q z9O-Y`5Q7>0kJDp9fAZ)z_;0Bmzq5cu+vKa#Ut0DUZn!NJ;T7-O}MlW|uFF zA);35eO+46p=u1jxDE4Mj`%}jqe-c2Ng}TqPmnGyGrkd_(a{UO)%;1|5on0SjYvVY z%=BP`zrAI(FJDro>7Na;sj_Ss@wh(Jc4s5oQ>ky3YB54zgn2qp=$ad9&%z$;2NkPpH#TRw9BJ%GMmzM0r!Ns>R zf|#(a;?Cu7ZJ97&X`}NJe$f#7Eu~`9gZv7mYYI=Yk+UvG`ahMkt5evvRJ$6~tNGsU z=T%+zJk8R;tULfC*dS77Hhej$BlbmSK2P+ddQ%kuZt?=OW|~vuVNrRM!gR(PiY%m6 z%7TV(VS&$~oYLrR6LEy&9{P*xnu8(`$%QXi2BU-^;=9^iOJmEqXXPi62$_k(JRInA z3B{iA`-)w0sHI$9oU$%4xFCJ=Ovz@sDmjx;QG9V<9jovSt%}J6celLh&*rM{J`FJS zHxXYrf6(6`VWARO-8_i-er$w_=FaBjZ6v4FLu|}FWh>!+9b#7a`?&9=mx%?W5>Xwo z>ImumY*vVyEG}&XJ)PactGY17gbDp`ee$IA5^aHQ3vDZ8NQ(Q^^m#wr9U^4yGBuQ( zr)ysiSp13l{!C9VjoXuoiPwfJ0CrdZC^8R2q4~!aq?!kxkD&96i=2hRg*U%I$GobT zkI8jQx^kJ{;2rto72EUVeCcZ(3_;XCTuM6U{M{^w*c>=;RWdw6EQhl_jwx@{AE zipAx|3zeL(;N_0zatFeACZJl?iB`HNi%KYV zmD2O}?vylg$b5;4k(O_(FP=Tk_0_<6DWso9bBM7GYggL#<5f6!9y8%B8vsL|7`^9? zU~y+f{-I=Ksqkr4cdKEOo0tP9l_~(|zZ3UfbIZ_{c6<4(&e zaMr{A3jI)=Wq7yCiqX3s9Wf#)mDQrRV)k3c+Iaxw>w}-K*D>oJewk4&-%V^~{Y~j) zEIPsO+7E(iZy@(*U=g8DcnPE})gRhj^^Dhw~_8Yn^`RDi`pMg)# z>jl#f*SGUS4?OEGm@{^X$6QiZW4+L5f^+rrsCT}y_~3b$bN^jq1}28i6s$IPaDpeK zb~`+Un~rL3Geb-fcJ6O$sf$5fhB39djdT*zYv8?YBs^ts`q`7j2mp14Q6IFPnkIqR zblM9FmiMJnZ-h(b%(p*1q3VKvQFV5la=L;eobc!+8Iu92NLRk6S|Hh630)i7%Lx98 zQ2YVAufoDB@49=L{o2}IV`DjH=X>DE$S(Qi)J*7nOe=uEFcatIw@^g3Z9NFv*(zyu zO1UoyppcUD71S~C=s{qFh>1TqUheWpT3L4I7(v`A^XiXk>cBwE`xQajdY{AOB$g@s z-;YiUjQ68HZQFR2J!5S5u;gwYKTSJFsioT5sX{j} zOYQ*I_MHCgA*%O_76wgzcQit-q^MD#vCn)$T!6xtjF!^pb@u@|!ZP3}osp|0qUEXg<^vn=5} z&21>IdPr4Dt({zXb*6DQh*h$Ze1p7{{xydEs9tf)yX%7ux)eGy{^Nn-SHq8w9Ngr; zOj9mLPxD5xSFaTXaZ&|R_d{R2>cJ|=2z?)u^upHG)-g<7_@H(oPor^L6E5LhKmnp# z#OBg=l=rI#E>Dac`a?@!?Xc{-&hp@6Efho~B0>{lp9TwN*fl|q+#VhG>fFAP2XL+w07~) zX5V_i7lzbe5|zNU=vXQ+MY&Bkn}EPIyKsfJnqzwxE8d<09RUeHEH}ZE!bF8RaW^V~ z+53_*?S!F0QhWX&W_2(O`VLlHA+)RH_MIzD34MUa|2KRtWD?Y-i)4x2xCe;Fwgsjh>X_auNA%!;bnuOd>zXuX*OD(c$k;`z&s zj8<_p;%>1CN3dfozxidL@z8mbKJOtMuad^b2dT2zWa7V&yGWrxTYE6BWn0-9Iwu!a z8=xmPMaxxkv*J)fhZAPqqQ`p$8KseL9~{*4@;gPZe3946*G{YB#Jxrs?U4}5xMx}7 z`^SWWnO;(u;W8oRGTZHuNBNEU4`}j|l>0*qih-qCG=uKYq$ottP+Y{?x%1l5q+@VU zR~F|ls%k-(y0KN?t5Lz2y~Z1bKDV3BDJ62d#W$uTjI14#$_#Oji&5&|isMQSHM5gj zXr5~Iqyz^WW-<-Oy)9>g*wjSp;WY|%PKO2D+}z7)&i&f>qwgGNZ*+{6%5!#7BeOZvbsvg8JbSK~J&MpS}@Gq2@nk6pD|G35Z21&~Y_Y1as5nJDcUd7_r)4UMM#^k&}MZJNnzujwgIw_I79qoZUCW$U7T>Fh9)P!VRS@J z8TbFk*IP$ry)|LOloHb2DIg%-jgkUVlG0t$DIp-zt#pfYhjh1;G)Q-MH@y4S^PJbxz;&n-N)bT+1JcmbIt7jF^S%fYp(gQ_UPx&0eKQYuOa-qR5jEzU|;M$&6Bx zj3-6SvRd<$eb_vIIpi_ecAqu|ZBFKo;kJ?Uqwj5=6HERsk`W@zVv$?^dX(w6yb9x>ymNAb`lVLF3w3(wARek5JH6=DVLGe#V>bKA`Zi7 z)CgNxLBTtH7=VE{tGks5uKEI~#@5&u(#o_vc=oE-gb#iLEbv|oXZXCDB2RdQrzwP- zAD(}{Igg4Si09x?B5YT29*aJV0!LS@l9y3-(<*d3voplA$802r+2OT9 zpcUWjs`Ej@mh*pzy8XQ>tGud`4ebh!GDA+>vH$8Aiy>N3v$iC2Qx}OYBf1ztOd`!E zce7R-WvP-2j<{$X^bDcGP&~Y zz&OO!bY1Y85|8)Xgsc3dA@bVO5UUu+q&LQ(-bS8SXc>N;PEvNRTH9SYUeL>|cTs0j z%cq*_Il7G236>jg-V*Bdgk?5hZk&%yNcwPZCN9<9OgNPu48;&e>HIyCB zXIm-^msW3N@c6<#mtEYvS7T_l0*U9bjmRHUDr(NTZB2$;2(P&Wfodxnq25 z9EPlaS0r&wept%<3O6aE&Bx2Z1b(LUq?2&Z)9_I&;N@Y4i!J9bSl4LV~Z(V5f-nmq!a%Z?p%OukCfFVESgzf1Jy*YOcMoV_-)<9R-UWEs6g z-CJXLf5r1Yk@Yc(rNXv04(Hif&8baRELASI6&GRU_$Wc~&N1800Rk15Q{YEInj zU7nr!bFj%!8-aMjg;atzIo+`qe5ef?_|Sxd!CX&ufRG`Jipb2 zA6W;V1Wngtpa1qpo4@?g(eq9Md$&=OQISj0(1j{%bSk(|5a>|HW-?c37)Sf?ju5@d zifUyeip?+FUJbM7=H0)DunpBG_Zd1Q2n4I(TadT?xr(Cf0eN1tRf{)HZTZGHaoBpl zpMa}KM`ucW4JZDgh3XmhLYR<}fuZeyM$S^MXcs|B@X-wVFV$m`SQDYib!4(>Kkurd zi$~vd#k`r-JooZ960eqW4q}7lH^!YfLno&(q-5pV;)4Rd^A{|P;;E&<23%4Fg56Rr z4_NV$_FSm1_*Nd|pIrh;!qw}qQA$E5RRmt>44RWCE`e22nF63)fwV67W!1#k@T3kU zMQ7vD3c`?-IkBe1ern5Q<|r%cTJk=;y;wCI8^ee8>iYYi&F|}}*QTINk-e~4Tg_8( zmV9=>>G*V_?REe9HS)8>h*MSg(wI@hQGS;R2TqW2*2Qp_n>jULdM@?N=j|J zW=^}byb^Ji`ld_Jr}wECcGjAr@*tho0-D{9`e_saB5r!E^JLT)ouZ3uR!pPzb# zfiWJHYtJ*leGrPr+SOCBuzf(y4vu;Dep6CUatrP9!yvnD@WYY9mzGQ+K>97`Sp>5y z;Dkj#ZnMHml<0L9x%@r}esGubWeO}$sp+Iv%T$Jgmj6z;_gq-Cah6R+`R&R+%TZkp zg6n%KiX$4h0NK-Mm?!H_88r%;3HEu9EGy(*(Rq^jdWqCIA=v{ZB_W-B)0*5WZd{Ez zO#s1bU@DW9{84!>K9rSes!}#XBHa5s0*)Os%46K{3cLpyXaO-)Vl99BqeZS5#uhK7{< zCMUHG)IA>4Kv!Vmle!>hpJ}Fl2i)~qb-h}yEC`a{y;72>oAS-GWN6(IM5J#KCcU&t{x^{ zr-!ymI%xaY9F*caQN!oA@)RpDm=`ZfF-PX-_LXBchtfXI4WU7V+=K7SpT*wArfVn{0|;-Qqmbj0^WXp^_$`rn{zlVk^$p#GR@$IF>vi#m#zkS2)cA5_%T_FY?G-1C4c!IuDOPx}y? zEiD{Kr@E3~R3BIR$z5*RT~E$dmliK#3BF|Y{|V?a4;GtUhQkZQ3EuMQB}haj`wszk z9AC@}=9bK)r?P}FlvhNRXJyI0qw`AWg#7+Y^(wkoqRuDr2)56-{Fmnn;jkEEj zsNc-cW8+}IEn?H&Cg6C7aE(TqDN|VdF?zyuseNEJwuwQa!9*US%VKd~9FWF?2n5bS z?vnO?CX~Ok_rAVr&&)kIf*SOdlwekwlsLG!47Su3oLN7{g;Ii6hoTWdH5H5<#hu;q zV*Szv*4VId>S&lZ)`F=}$R2f{W5^GYYYZOM+q-f3TN0C;-=qdW(to`U4=CXN@x;e@Me^Zx7ETHoHVevn5DM9a@Q z^LpbN2;;^#I=1rKJhNe~Htukbp6OtFi+@lPe?PVPh%l7Qpn5HjeX?eXZoPogy8khOt48K0?{t`RUJJl=fff4M|zzO%?OK zA*73F+*id`Q>|j~lQ+iV@tPQ}Io};M&M|J1Lof=C`Sr8$<^=~Aw+q|ygS@4s+)wPT z#DYOSlvk|74_-bI(NsfYZ$m&)P4K$Nsn`4)xhLXj1RruYa*fyG zhF=Eg4{Bxue;~*Tcp!Orc<_;?pG+n9R$kh7`|?Q|^I)r&f zU1tA4lWJi{oYfI(Qh|U?nr-H(CvsC0>pG^jeI@6(Oy_o?d{P6_PESN|0z*}`aJ!qI zlVa3XXf$BYc#cepoyWxGv$O~!lRs7OH$1vG5EmugMt9xW>2zJbi>Slq;HX4{i0`^& zeyvx^P&90^`7E3_`Bq*%IK3BuPyEtCU@#sSzsTVbtS058VNR#|aLho=<>kx6@(ewI z)Q$&eA+nrIU9OlfohKKv)(j~jOJ;tw7$plH(`BszuAo^EWE-aQT!vAX=+o1>YE}RF zA2(g>hdV|>BEout&OSFRaJOwTLMJhN6cM`(r5CP9w3m5(5qIft5JOyq&24f04-g?8 zRxOTwluOsEbECw(Zt(jhp5XT((kW2fO>Wgqk3Z|*_y}FjZ>UC<)vg#)IClbPo%3$B zf07QG`RUUKoYq!*XZIat|JTgec!D8``3LXbHYOUwKLXnG<2?}BrDA~Xbc4A0t=*SY zd~I0@8Q^X^dR0s2oU#1RcXoPh!=t1`X7YDN0v^K8_+7}NIr*L=dkO75O%||1`ffG# z9&BF|l^JhRq0=PxXzvry-bonw5Vc~r+oI_XmL~C2D`@D1S2h=w?=@16-SIi&&q3As47lt z+IsRx74=UJ2X0U(4>QY}>`kfc4qZ*DevcnHv3`kDra5%@Y57b2(ytdYuLuuLj(|pI z7Nu-3q`pyP?Rd=p`muhtR#1MhblirzzMhgan?xuPl@|_ECSV9GbFJCy091I zznC%cLf9faZG^+>g0T^`mxsLEz;J3pN)~jV>gFp{ zFRueppejR}O!4GX!dgb@?Isw~cm3FaN`%Z$5p9UR2j5i{gOx|8r-Q+Z zYTxuq&K7F__`@sM zB$X9cp$aptj4I z{)7)srS*fxCqQBSd|taEdc$v32#tb#$l;(O!7my)#hBVAy?hyqATPfC5e%zc9F>4U z>=UXY)(&NfUjJ+w;`eJ#W*9SgCz0|Z?D9=s2YXU7($Q(j1ny!jOOAkfaM7Pl_Yq9& zZneNtkGUXK-8g#l%EBejbQ?4z?WRiuFtCy*DuRgTs9?ZoYfEvc6}m7XzD2h zT=VS@o$uRh zyLp{AB+f=iXN)DxlU8NmuJ~c870mCv&~#&PATuLl8WS@5 z0d$`qZz*j7nFVy8pJ70>@xe6*C-P78uwqoheZ6(~?YC^6ko@wTs1cw3xsCQMv+rhV z11hTVEJ2+}75HeJ2t33nKL+lfyzQiK6%ddo=Xl4rjEU=}6ZrH|Hk*?5bR+x;sjD(3 zVYa#a;xAF!MW07_)sQqo1m-iscb>$<*7Y5Q@gLkxPbs(s268{Guw)&G`)}yOq_Z?b zB~28H%Y;$$b_FHms)kkpb zGNL~X!L_m7e;!h0ake~&=j5EQfdWb5Ug)`_JAb?M^A;gT<%GKw^k@~eZEHLp#xCB}d9*PZM0HpBADB&<4>XL< zb~L9LId_LkNbix3?sJl+(k|{{g*V-$`d7Q;ZgeWNLc>&YHmBzRYFn%x2O*P(JXtkCS8U0EBn`EnSi!NeomQ%u| zZC{wQ*;fQig&>jUAELG(OelYLjKl?^p_c}ih=911oVEIBBXsw&P}OR9Ex*2Ko*njg z=f6q=ylNGg++X-#GE$`<3_bjYrQ)HIt5ECiGdK2!a9w*HHQihkY*OJ%XMHYGLtkl- zqO4UW{0mx~{3+EJ&KwgqDsPg-Ri=ieG&D~tSc0ay<1>$oh={zh)Js>N#ZXVHYQC8F z%q`J0ow<&o)7t$MHSs{=`u^$}RgV+}u#(X2guCm*jj_bB-mV-1F{=r7>SZ4eguDcp zoB-D}e}%xtpxO(H(#PmLbr0kC_?|V;Q3U1uFhFzi3p9qEI4kE+RF2)0WjXvq@%;mH zpJkn@aN;h`g`I0mXwzL{f8&7OGi^L)*IfhY`_VjqnZXs?7XNH_j&+D@qt{Pz#_{sb@FH#5#SeiexyaRKbbMA(!Y7q@ z;^Jkf9FHP2b(ak2k-B!>un$SqrolDuh=VxWD78(L!}5SJHh!OPqad;K*_$LRdd>JC zBV)InsrE1f4u~Dvh;g4`2I0uMK9g#J&hcfqFBpoC9;016NX!3--Od@`EA@VBfwX&? zq#ik-4Gtd5^{j+j@6`JqUU!iIm}m>mA~Cr0N%>?(=kPN@Zmez5|BWyRwc>X&!pH?+ z@oH%NYP5nUBpNufq2mxh_6N?$q_0KlTF3)+?pnQ)1`K# z?!Ou|&2DRQA)~C7xEc2Qr1b=lrn)sb@UvIx$6emV1!w^?^{!b3yc0EyXp1txZK>~V zoPS#mCv0>tV;od9#Kp5N)i2fDi(3gv7Y6>iVOyUwHWaig)nD7;su)XB8SCFKeA175 zvY~z&YohT~j~B)caT0(|;@b9hF8L@vT_t06Hgf|h=NSsgc&o!>Odg{jh%a zqLH)J5tquzb!mZ?K&!OIxk1+xnIYBk1x|uCHL(3!Yx5Q2+D zg>8@zqTBLl9{%hqP?%BPCV=j*ix7Ed%O{6d(mJ&Seq1LG*!y322Gq~D@>?p-P7UQ4 z;4i!yYUx?EKQc&+H2}D(Qt9QSjb^hdt;a+!cO)H@PB)qayQS7zQ=npL1|Ea?E`=a( zEZsd1d<~oe6-;qPCiEd8#1c=+)PT2e-&N=;$LUV&9XiR1E#95v5j=Y{IvP1GOd=#C zWC6V+HV{%U=-}+@040SXFTqd_DBjPYX=d{~7J`^Cf!@+Ukcm}1F>vH-i?MR;vzCkaZR}k5HXtif zrP9l?2+6E#5jkH|KQ_@f6GQ|fIX+wFUK8w34Yy=lmTc3tJY2t(rbBf;@-vHUz^tnB zbn!5tT>gTIjzN&;;M5i1ZZ+q@Q8h-G7Dyn~A_j-L<4yr+>)h`K02=?HyG*q}c34&? z_FH2_4lont(~iOBY~efb&RHv9ptA8Gr5 zRL**Ahc%u_Ymnj#PuuCz=2kV(Ep8Wpqi3L3h}I3H)Gy>P7^k-?*#EKlOg|ZWy{7{X zF5puOp|3y1Jl+a0AtYmovv{iaKv6{x6SVro|(g zEOfBr2-y0Alx%Vq9X$O69o(7&jP|0Rlw?(Xdu#dL8-L# zPPmA+RncX875-VJ6Bw8Ct|D>uJ=gZAta7w6IX^iO712w-M7mtK5c%ZqN6AG&3SPyY z%iI?4;0=%&VVszeu8O5xxkHI1Q(-n5E`}zSQpU(Q7`6We_H|#EeF^ras zIiuguLJd(`UtVc096*dUE`NvB$NjE+`!3r^+f+8qUz0h$Ad@Q?ECn-@X$dNE5&GC4 ztYRg9T=o(!Tv|8q1*_z<%s{16nxT=AdvQx>cW@FZ1AwQtp(t@+ffgT9v+dgJr=TrsRI2Umj5vSpDVZMu&L%_on~~4f zc}|zKF#?#Y2*JN?k!Qz!zCN4#KU>rslI7;z;BqcqGOmfQlA9OS{R(~t|@Go>olhf=g zeUj`ivv?1`&E^+1vSh2*n^r90U^zKsLYfS5Q;YJ(XnBO0QPx`X5!b&b+KYUJ%9uBzB{na=E23BtpW``p2OZu4Kxq`27g z%HMT?pr-%tCV}Z}sTN*`tj_cJKx<30~pZS$bt9 z=9GJ3b4Ho;(4I+V|4%LNiCCqWw<3(-6rT<;`UL*?f=G_sP({s%DxXo^76N`d7j@3F2-e`0ar5VNpYXJLf{0f)C|8p3yD8G0 zc8b)^!vwK`xB~G6olmJZPcr|uhFwYtvm?^0qbGpV^3gUHW+)y>JihgSu{)~aF4E9S z$_FJ9Z2MvWC=8K*`7fDgDpzdDnXSSv>FWwZ~J(@mni%aQ!7KeJI zq#B0v*GO{vuqAp2+{Pn>B)X*x)^ntNZjxmrs{lmr2$sJ5NRk74n<}wT!^zIsgGyd*|#wDbg@gnKg9Fzy5<&W^Wl-=kg;7C7Kwx@%i=P@Btg7*h7Wx5 z?cfO6(PGj;o%U1O^H32W?DaTZoD)py7zWPYi!r2{eQSRM#GW^*a>?S4vVih%cxw_+ zM}rwS01`J-TU^{zyTV7v>cHmUr+Tn3AiYJ3^6BYSlQslHEoyV`B-P zruzx-R>0aPW-EEru6&4|+(>N3?_|Ko<;EISV)l)Ts|I4L2)Iz{Qkn;0_1cStE*gDD zOcrQp8)h;swtFkH1r=ck+eHDu3`~sJ-NY9Cs_Hr3LGI!qY)=dK?-KG2F zV~1#KxI(?mTgRD{A0_koL|BjQQ$#=p2qx%jcCdJqG+=~{1S5(%Lt@5FjrG|_O4=6V zsfx5H!T(?qvT&fR3L;J(-*dD+f62kUmiLdV7v|q$W_2=a7Wx85mug`G?;`8^M96>2 z>f`i_I`)}d=#mt^HQJKMRi3SwWb9bQXjlF$27$ar@}XZ!zT*63FCl?TY-y|*0Sxt# zV7>XdWr0u<qalP!^Ps-D z)*169AkL@v0exrv9_wO()u8)-v>=N?VMYdw}%do2Bgd#{?h}o*R8=D|aGNHZ;``lu31ii~9OCwkX0c z%9{ic2-M5e8`P6j%6h}sh*T@8dVfVXov>2gtGC;;upq{ViM+8C)Y0W8L(OS_H5JZ|V=B5wtu4rK*O?nJz2SzxVCV*B< z+3AiAe|`VS?>HU8u+K+s}JX*SQd0WAw3SNUPjSzY9x~r+^lSQKR ztB%(&08Ut`?&#sJ{c8!pHu;umxDPWos1xfClhfkme3+Xby{qH6*CC2!p%>brN*kCU zez^mctjGEuyTy!~<%~PnxBw!1svv7@_&3q z1+eFf!0}yq<)+^>QoF1x9eDz|z-q0?>JbOc(No3rI56|A0v65r%;uw98r*pDi5VbZ zve5c`8hQbmJHFW%kQTZsL|qBc9BgvyIA`Qgs`|^j*RxGLsy8kF1v3wq6_s3z=VM`r zu(cB80^}GDl7f5Fdf{x3d=AcUVfm%KIQ&!@f@D#Nd;ptkmGv1l-w#sC;5&m!nf9S+ zE--hrUDZ^&s5mxWsGyeK89mh_Rm;8cn1ge_OdVbW>^%btF|p3%h#$Fc7`2PRVM{)& zNhps^ysg8?$99>KaU-5hV5?0g4=pm1>Xv=_k~( zm%N2OaLCkNdv;KyzyqcKq7~sVhpv)DsZUTl^{A+77iJ6!3efVcxu9Zu@b$ zqD9#EVx3$;xgP6bu2)6E*rgA(iyK&ePQ|$z!?GYL{$Q-%uqA|k5 zr7RG+p?|sFbkHnZV)<^@NOdx&v11La204|HoCkvL@G_qk0hy5v}q ztB8AX_jJe*28@T>jYPV;2@HP00v;uw_NUzI2kj#Qpegyg+;a|`(`mX(2Gob$7FR#S zRuj6xfDoIDXNk0g%g)gaqF^1>ChAwrCI|9cyaTJ{42<$5oMp1G6G>ra{-QAtK$5kC zlJ`F-rTYLq9-~|EPny&}>&cXidNHqCp+55)RuBW=%>wG?5nVU{;_WP9%Vi6@c3MN#7UD>@$bO*bnWbb}qeZ zwcKHP>0s%*Uq%oKrwrb3Sp$0#9Q}JS(MBX7Z9rsU5Ot9DQhDK|G{5SSXDkT5? zlI>nbT!ac7e5A1_d5sI&5}%G4L!y_yn!q;09nJ)$hIC8u%Un{whBTNArf=6IVp3B| ziV=Tw?XN%oORez#kx-iV^yTI+F36Yj8PQU$rD~f(|5W`8f(V`AmNAPbAP^MItrV-Q zs({X6|!YPOTbj9Rgi)sLD!6)kajiO(3Xp}KIpKZ#n)Q~O26>gxX9a#CTxiKq>$g_{4N!pE8qF*}q{bB5EW^kbmHe!XsqP0+NBJ z$H9T6OyojG*E$l4?sdl!y=~?8#4amV7H_@>)`@rt-=5CC!OinGh9s@8AoNL6Fs{DC zky`9qz#Y~!tNR=qokpYrIrC1C?3MaVg~L%NbMtZm4uhhSad?dQca-LCn&vw0+TI{F%R%;p0)xbBG{#(_h&-LCWXy$pGegA4 zA6R}%Y549_e3{iZkwNm3_Da5#N32_(Y-B);qQo@GZDJ=^t9`U zSGHCBgWupDYe|1W2 z*^W_IUOd9xe<}m^Hv;i-`l6kbY}4F`F}+P@KOKD+UG=>277eJ`&<5{GD13bAUuEC| z91=(52;x?Bj=B4|XWXZtkn%ORcft6|4W3-k;xmf^sgIEUx1}}Xl5twdQj4|0=C%>e zouO?<`Z)%dj?Pa3`unVsON9iIlurX6P#gljMmeaiHU*tMx?L@f3MJ4)ftlB7s?qCz zf^drRMpOY!88{8BIv)zCMrb1&5k#qsnHOt$f!VlQw|EBVs?P8IHQjfl#wry+p<|(m zVX27;*xAM8D0vD>77c496ZT{%HCIkzy8uL7S=Ygl`eE>c`ZFlJz-uVftw?~7!)ZoW z^*S5erow-2Q@I2_-G)Y?`|n~_4i3gwl8SC>npB98ZqHG&USVf4Lip3K9|(97_6U%& z?^f7ndYx@m#R{@yWJsr`!20?9LkOvBCIPjf?`RYz7iP~ARTMTKa$>@Wb+>;?{RP1@ zujv}Zi3)kr*#X^Sc61Cv`$MSIBLtuF>vtZdS8frjCQ6u*ugDqgDNWEC zUYSVG*@H7Bl8%D~dt=z>+aIq77ByDeTH3T@v1CIv5qJUI87Xr}a3y)--EYiszv#{w1#T*|lQcyv_TqHEic zmhd*In>sF_Y~bK-+O`~t<2R_PUY-3}JamGqyKEO}bJMA><+IvrOvpc2WfHZq=W zypFfHR6XBrLokEN^8LhDL|9nM@wTkS_9yG%#aSggQp9A_*F%Tf&nI==P70tcDNf$P zt^981j`I71b;1!OoK5}-XA*>TGgz$mykXH@?E6e$nZq0g+j<=+GD}M8@lhDZyPyol zc`oK8_ToShkPR98(W zic5A{GB7T9%umG&7^s7}c{v!L>vg1t$|d_UEZEb2^SB+N(&J=fXiV1g*&yN4Ss zNUCiz=DNQ{mAgwwL;ybAtPQ?9C+b*_rhE|a4oQ~>SCHb%@Gc81d^$xIAu<)Ld*H;! z7aZ?`D^zoHGQ^mla?LtJdee|W-V8RDtW$TYoXY{#Y~#x0mnpEY;uCIn-Aho(t37*f z0wS4x?M#nULhTN5S*~n_R!cwF$1VcH2UfeKgnsHEath8i!3TRx6M2ocaVB$L4Svi| zZCaO>CE#rP7*qBX7%u_N%`g)g1to@V{1lKl+7aFJcTiVZ#@QvEn=s)CEToeu4v&|= zQHIHNUXq{#e))xwTJhc@l6;i+6=E{mDKscixvVI_aoK>bKcsOgMP=+x*(D5m8Q|?) z04cm9~q3I^NYlWNUAeU0QtAl)Vl zIgt>aY{l702Da1z73=@nrqz&vvc0gP(CGTXfKf$eJeCSjK1O=W!M$wDrVwfcU<*FA z2o;`2+c`##(Kvn|LBOLkE1oWtMgRnIsdR40+zN`|Q^Ad5aE-$Nl|5)wrI9X)r#QL^$Efbb!pI^@#3vu%2%>N@5H34H4>y* z)z7&3(dj6aXf8ovM?$-(8B5m#8W;MdNwV#qov9O+a7Q2@&OfJ`yjt3?Q( zps)2#055WbSBogoafPtTj?^}>Uk|W={Gan(0O3U=3keYZkSliMUsO#_34De;rm9^qlzaaG1(&kop9jfY4xxew<;iS<^-wY(+Txq=LW%oU6;w}-?-1Zf>Lj&`EGvKGumS7Z=?GVa z%iM*gF_G6D6dBN@p_5fj5K9EIqp{ z^RXuns;TbOaz{*CPlRnUexTTK+c;x$O*o ztG2NW)WWnf&nYL8|8i%_Z-XMrr&=i)c?4a6G#kBl<1R-E8GCBRsv;Ri79v@qrB{~! zJD$D+wDq=pLumuC*WDz8dbLB`eXAAm@Z=1UTCyAF`T(!Lrl(9TTJojd2@1Gov7f_U zNwt{4u;1v00O?$)oS`e2c|wu&Pyl*Mw3n23aADCSQ4n&Vf5tR3s$39*cd!UB{}g_ zQgvI5wvWDO$t$pi2)oa35W#SmxvZ@OHFOXh5LVeu{Gjc48$FL%LHH$DLfWv#87D@f zw|MhMEeQU9l^P%sCfEmwVN>(fThLi?dz?N{VId(spwQD!2xlgJE=vaADci2N<#i-$ z|4IYk6WrTO(6arP_Xe%&YHo0EaaV;uT~DjnL$m)0^J3&&_woo&ge7v!Ec{vhqU@Kj z1{XJ8Yl4G)p%Oysj((&Vg8FW2a{Z9W%V+El9FmnLwo(T%a?G2&A{Q2TetUMRb1pNvT5G04Sz1bH7O zjVL^rSbH{)>I=@~uf9s3$0y4+T0i?o%ExiUi6IG4U`52t4}&-n&zLFg<(Q{V%{u zbESIul6D6ISRiM%D6sWBW6-`TxBlAQF)`2HfxGYU2 zx26O4IBbY6F;O2tmB(e##o_%$-|s^{+30U;W0xz_Gky*>{EFa$YvC?Az2&$}M)o0; zyXm^h#fN8x5k~Rr*UN5BH3L^S;p%k;u0ZuA{-C9zAfD2L$#=%`nzV2d$ zEk3YEW=`~Nxo(p$quhAy5Lg-CYrcF?y@Y#xQ}hi?qfKGp82$tA6VT*yBME`#qbbe0B)W}1GXNW;W8tuA z(3i=4YYjN1!9a|k%DF69cp|ux)olheY3aY}@d4O`r}0++ZaW~c$pKJJaXAIUkdEcM z8Eu2g3dZ8temg!)2ypos>iWlBlA$?Z{gcUXx?s?#2s^Y?;-hnRW^QYL0Zg1_@+m$b zA4stYzA=Imni)~pf%wynK4@1vjaLH|Tikz%megx66d@sfKJho~p@8I1yE}cd|G_V9 zBHHPRAKYVuE%u0zBBU#rl;`XHqCf@-{K%cei6^<{>?*F*6-QUyB_h44;?~3C@B)Z) z=%@mA0Ggd+*z%DKRUtgjn-K+1OwhxCkuKD`Z>a1s>-!y`LLs5JD*?(I;n7qH0Ma=f zZo)ujHP60l1qb{|W~lKBfY%4Gi)~fa$`%dE^enN3pq)%y*7j}53XAs!f>}LVLzT5q zq9&05ZCh7@Jia}iQk%rvL;$SE(|-~c~x9L z2vEA!MvH?H526uOZ2lo|^UW8iw`A@cq@$`Nok#5j#;|p4a1(8hdo>l=@M*egJJlRp z7oRA{ZY|3i1@*Cbf0Sm^{g<8(fz4# zSTcpaINd%JNn&`IkbWAA!Vj_bB{FM~1Fm$$cy77l4oM;hd%H(a(1PevRjZ1MCMl%S zD#}!1(&9DoaH5$;A6au>lPf*;V@~fxr-NNjm&gL<;q4_8^WmlehE%_mGjvZzS zv&Q&JqK@uJs4B^TyBLo6o1kOv4RtYZ)Gm^bq=PQ4W#(P)Bj^ZnrDGy`G3gJ5VV@Bx zvz%#BqCOwI3w^4jLoEEt3w7-HQRq8zC>ZhoA^!?nk)@F@m?ZJq$Ws4ExN?wA{h>N(S9PoP4ZO3CZF6X?p2O#y5g- z7+A!-b5h#vDH88U1b0HcFI4BVCYvQ>C_8UT!N=U&NCZaTCJgo?MA_3|oDEVapUEN-SwAB~v z!nzr?_2FX1wU17I&LU87t{C1@d7+Mdc#>w4?DsFU2{@aEtFmOa=I{#WC?K@qKPR=l4yiv&A7E6o0-D?iD%xwW_o_O;df&lc*8?zDiAc^D$o1RrNA0 zCZEGnmhE3E@^-i(v($mG(CARmWYB@pLfD_-Q@WdSrn_kDmnOPC7vl1fGxkWl=2S5X zdfPCRg%H<#BTMT!KJ!dV*G!5S2G;>5X1+A3?a>`B7C6C$0(817_Ot{H$~9Ta?KII$ z<+L*8$AQsGpT^#kP-gLIg;nh&M%(uWHTRiWJsCdWH6`;rm5D)b+$;;~*nI9rexo87 zTMV8Hiw?GZgo7N#_RK?F03Q6k97KkHPzOeP!0U5F*{7F$q@jI$`BH+5%x5#66C1Pc zhf6ptItvr@1$Excn)ByAPKjdkoQ+wB+lMRJrI}~Vsyh;yS`YTHcM}l{vaa_~HjM8h zxr*zPOr=+0pwZDkL5WGf_ub{2lT0FX9_|>6Zg>><))t0)!)RDDMw5<%!>H8~BM)kj zY&5U3J{;UD%1S>SILcC{Z?US+>Ckh*d#2<{*C&g1kE1@ruU$EaWG1IY5SF~mRQAm; z7oHOXwgqm%<3?n|? z_Xss}SR9;Jc(3Jn1qM;s5=NhmIJnyklht9+>6*>Tr;*&roxV5AZ;tK|yROks%C>mP z3NHFJIr#s%$$gWfwQ@D4Jwrp2fv0-;2CnyjIU^?|ghS;ELZ_>ZgL7$t|EzT!!d^PO z4?^bKCtInf8;`cloEgyROxYE0ki?`b5oHe0qLdObb`S!i3xntv&?vL)O`fZYWE#D| zaCb$Fp1Y*c=nwM#N(D3DyukHPlh8%;FrheZ5R+)*9*Fd%x5L6B+?rhJC$xB$47eFE zmWx&tN)|4cz@>h6nNhAyEqOYASbftJ88skD-e#1uieq#6(+(gN6pR|yP?%Kh#*n@R z-)+x$nIyo{VY5#dYW9RsV;vw+@R!-P(s`LDT-HKRt&%=1+y~^)WqG$&!^lKi?_S61&0I650EJ#Qa1n zC?7)vzduizYa*+j6rip}cKJ4PXR>HP&4=D zBT~gEHI9#=%M6@ZGr7N0MM60-#M^JHwt40U1D>8#aNZgDBA4RTehq(RCdE4?Ud0F) z1VR|l@RWQEg60BF6p?6Z;xQ#B1(&;wDKtbT+S)^S83bK)9^~^_>|Qn_k5s=5 zrBXCO5)#3Y%0dEgn68hM8kgj@7&)|Xa}q-~CTe(YUVoS1;fS&Z6vMZ~JFvbu|GD#@ zA?|12`E-50GzHK;+;v(^T#^4)VNgoFsnziNly~kurE6=EQ&90>4syx~f9QAGzTz$= zdV|H*{w#O{2i}MqqgpUMnXo9VZz1%E;wggbUF^+CUUqZ zV!z*Fg3SD=c6r_2;`8Pqqh?}F-#+E~w%GIxI!7=NP$?itgQ6E%@B+JhCIq#4EG3uW z(C_t__XdBTu1h`#WT+SG{$N9F~vXPPZ*y@`z6%7B& z)#086(UvY`3&g8EV>lV`277rvJC+_~TfRPe>|0!KBf*|l=dSjf`fDd^vRjrabvygF zu3e|lt-={751Vt;odA~>;>#5>Qzl%n`E|)qPz~`XVNyfE#qkBVRCw8#gi#HJhIUl| zCdZ{;LiR)mFblhG*#Uk{YK0fxd!O?z%S(T;Y4leq7+kKUzUdTFtmt|T7G=%zZx=CUH>^ps~t6nT`Vmx{%6RP@vX40&vC1X3rpxx)_m`rF)z;+YcS(q^q*h_K@^ zlz;1lSo8BZ*aB&4@hGQxC4=S*Ua!WRKyS*ua3<8^h8HN(WUn%qeq@b8UBBCYf6@1T zPd?Tw$`1q5QWeqj!H&l)M~(SD49rN%KnTT*rGsPqrMisNbMS}xwNZj&KBESCC`GAZ zXcftbWcKR6>K7j0Adb=Z)#$_$zMf@uZ?>b`tWs0$Y%+KwI@QTm@dBLT^uL`U0jw|h zAYQfh@OeWk*~FyA*P%wu7<2>#P4)+=OlS%s>&OGb z-l0qrgqtw$@^y-B?P@UB6JwQ+%b#Bcsx)JNuM$@%**&&|BMfS`X(J^9RvK*c+mT5v zU`Vbk3LiA8k$8{=$3#_ynA5}hEPp%N=^>-I5Qj*j4_R5GrpwZo7hLf)-yC9otl~D1 zN4DV=%EP`Wa_!L6myb|%i-6`f$EhKz4$O&+YK1da!HBXQENXPQplcO#?AIIfT1~W9 zjN0Mvx^7BD*Y1LqY(4}wxo+S8A<Y>WERga&EP6=uAGBo#DN=AulaSNp zd|GdISzFB|%6wv+U;m=zTx_Ljlm1vKm*#`R3t{)e`0qcz(`aIHdKd=IzE^E2mGarR z6J1t)yrP<8lN$Dk0iOvLg(<=g>-(m&{^TPx*N~PbLNbcm$#rBP2Tp=1GGrU#*SXrY zem&9NRwZau!LV?`yO6kRo?Vzpd(vGFdBNcFdHwo_b^y2$9wqO!omgks= zc&lpQ?F18O_fqZN46a)43)uJ3eVeRDGZUheaou@buYc|o&6B)>My!<6{3}mr@9XI% z>wLX7lEb9U+2$zh_gI5zv7E6X*r2-)GvMcX(ab=p)OSVq$<~rnd_p^m0=b968#zuP z@skW=4=NS-rAx7t@!9nkfqo3lwEkbv&fJ&;N-u3*4GXg#P|+vIf-)l>@F^A)2~kip zDxCcZe5P(5tT=zD8K;Cv8ZhL}SXEydl}O%)`A3FwWatmis`r?@Dzj)5uIcpsq+c(?W>;5K%;RcN5qoe;S+9#b6qf|G%xp<&_%}K%mg1PhZYB?~M zv1kU@i&G|292NPhP5bcn&EN(iVzk`_5a7SA-VKjyX?Bo$+^jS9ZO}&T$nW2 zrcd!qa;Lt=5+e7i0?go8m1D80> zu^a<9Ivm`?6lgASX=r1z-xx)Nqj#B%xk>&k-?ey1Lz9KE0a_m(P=9xw6NY;o{MR zwp&3_^$!-PCNhe7`uEzNeT~IWaEbX3*5OC=$Tt-uJwCbe%XPT5klxD+v@c&<%D10-6$&Ah<)iD>4fyvO{4lpk z7Qso>v7l!2j#=&DpC;=Mq1@!ONCZzOXWA-G?={}v=0f8ib7(J!@~GoZZsH+vVIYK=lkf- zp#0BZ;jfk9vo7?~r=wqtRWiEn>9vVYrW$gi1x3JepV}q}HXU5Sr z??h&)(NQNwHXX?3y~F*!Yy{+hE@wWlUH^(!wQ}CoUd}n4xh*csvq83}ho3t2;k+a; zJ9t@3R3>eE%s^c>+U9Agw1d&baD?W8b^oI{BQge)+T4}z@KR9S9GaOo8BIca4qim( zibXhH%KOE)INEiNJVQ-Bn545iVu2UfXB1*rj01wEWFK+Qtd7J!?}dTuKm<_Dn#j++ zE*>>)o}eV;k9fFy(~2yMw|(*a5IyF$u4J*YAFj;y`0?+e6k!>r(>KIZ*3@LY@dZB! zwoMq-q~$rU&heQ#E%Ht=0A$C*qyTuqK{`tYpn1jm6#ymlRy&$ZP(y#aqP@1jCc;Zm zJ%y_1HsK7Hwc%3Oj~SkN;CMnLg%rEno1;yQK6If2clGw)Y%3PlXJ9s*(Y$5V(4NS~ z2z+4Ry;RN4m5qn5Sno?IgIPBOF>%89PpKZMjfKNBPiQB}nQ#Wy@Np;O{itDmr)^iH z&O7xM^WXQB6i``{MM*$%%u3&oi2td$pu1~jBHPl!Duf^1$;n~RBicbF-H=UmO%eCu z)AkMMO2f2R^d{)`@{i-flv>!Zisrod(uw)3f=iyQ)G%~2V8G&(XEYPTUj31hqUE@qb{rZs^qU(EXb%-O*i=39~i-ltLeuigjj zP-COLioSc8{=xtuE_k|IOw4qQ@m=tWK* z)E!~zk1iiBDi;utQKGHpTP^zqECW-;o4?W>k1bdJ(C z^p)fHnTiXH1<1GtRtc}#@5WtDA@G?!57;+tJvuy?oQn{+V0QM!tZEltij&pPnZ4#- zH+)NEqXkW-vm=?t0$bBqv0m%S8Ko!k`1VbP&Wvv9&7_KP@49@x{0P4c4*^w4;-MOT zB+1~Io3z3DMMAHeWo1J9biw{%S>;Ysc|s}<7WLFDR+X=BIobl6(tMeI#kzjA4m2Cf zS9y(@NPJ&fuDuVsLO$H;Cu8WzqxLGr0t)d7y3T!63cK85#Ve7U&3*PHZs9wnmSx)j zvsxWolMu{FMyFrMz*;u_IKwCl0`V?B$V&$9@~Gut&_ZtjG;#0w%&n4xAq^h@p53IL z`{fMS0uth?`5JhL{rrizC}s#?bNu3cv;)YWT(wAPFVJG`#CpFvltso>XO0R6o5ZW~ zR}gC!HLCo&iT>mFA71oCf>rMosHh#Orq?(E23OI68v@T`tK8z%5*TE+N!d74i7Y>R zVV+uW%qr?mP4*Omgp9!g2H*5Osz2Q2Kw%Axub}-_CvPEK?ttYc95en-OY{P})Z;go zdyN58r-y)zs{*M989SLX`c+fV0Sp2~)z-*4x;=F7Ywn(GEz(EU?0HND?ps)o2SdbT zI77alcL>qrj0R7(Kpyv*4wNAC?>Wkp8bI4Yv$;;S@F=b8=2)Al9-e*V335y5-F}_2EA!I-snwVO1e+^oyvF7)hirT$i{B4qFcTjwd90lvaK5H^_#!zuL4f{EV`Wq{)A-J3QWv z;k}ND+G|@20g@nNji`f{iVi+HGi-4jxRcTCz)~}O>)1W#mOMWrH;eMk&6X32#d|aI z<8>CnsfME7MTrn)NWK&!=vS`<)Y8F>#NX|dp#QIWo0Ol{T-(Yty>?{?dBQ8Fr#|%hq#W(R4*@F%I z@3mImVjB_M-o0h>LcgJzJgY?U-^wZFZ@nbTVR%*E^TtJ&4kni>9)J@7H6t!Z#>|B? zq1rrp&ZJh0Br{4A3?y*(b28y1cizQwQ9s;O_$(_z1m(z8RycU{#7fbW$?$w#5HL+9 z<8<1EJO9M;Y&EN5gz`}b*tkn=YJh^7dOtBR6#9^8 z{_gf+eEVYgoYHR`q)Tr#+dnfeuoE^rdVeZ6xEDxPnS~L-@axk ze6&J_lksDtL_@Ld=Q3CI*yHUs)3c{(4noRN-1;UX5deORbXwA=Tc!IV zRu0k3_Zg}RBEyH_PLNW9Z#d`y>1*$FdJxE~v@Yp_LrP;2yGFn8Y9;$Ey}F=*2TpJK zXjiZSM@I3HqwQBwy13Mjf@C@Z>WrPtBbdj*oYnBD$?$GU}@FoT)3 z#qg%Lc8peP_0MmWd%d(>rYi2P!6cO^tW>+MRz6e6I z&Qp*bKNuF+seve*vn~0UE3d50ul{*jB1gYz@9r{r@|ljZu^abSq`qpO-QD! ztmGeGp;0W1~ zSb}4!>rRrQKl?`j{26cGs}KSZLO?1j`M-1+CQ03hK240x+Ou`{gh` z{X}eO+sJNl-{!SMm}>FOwvx>aEU~jEyE)qy3DAIO0b=QY=+Cr2y#428U_?XkBh=uS zEVMc@@W1-?-T6)sK$CdHmR>NIocPiN|5HYD>qGl%C!W3yUKSNhdDQrJ;%)6+;Sh?= zZ3|cw_s`vgFTLtHp(*4RwF-WS&v;f3BCs71quNbRa<6Cqb?X?9o|lAjb$^qD&_+{g z+H=fDMU5LGfJxDwd0sP3Z2e8i1};zeexMkwdVIaZ@A-ZQKrD)6Xg5#Zf|<|-p+khT^*9cmshx3#XP+xSLNB&%Bl`-eF=PB zRQEv9OkEnOq(Uqef0p_^G;wV4A>ONjsa#W2*B~)v5{`)kB|FBa02mD!uVxAiay08?A|<7iHfFWK0$|HGsDHzReE9HhLqzRM!$i?*k59gBRKP`SHh}3k_U*KkE^md%qCsVI}d5dc!hH{PF$-S3*^Ms%)~gb0U1dGMl9 ztVDz<*|j`j%qAqaTx;Q;FPuzE8PRCFx)(b}7)+PYdclM_DUw05vAVde zt__B?p1^sp{H-^D$a3g@$HRF6-?@`(6OGR_@DjQbdn)>lPxM*==YmyI1A@)g2)Tb$ zJ&LVZ=xVat_;U{B%6`WAtnrM_-D{_X@<5IRq9dE?_-yULH&HV&vzOfJ)L^gNqW^p8 zi9`d#|7!e(zw+?>i^3Koy+2fSb2j$u#pd#vO zJ;d3=naqYolDHh1CLk@;@ZBslODC5dS%F%uQG_Bir3$sFu*k)~gX#eFXy=(sEN5QnzB}_1e%SAY2Q0>3aKAbj$UB@GgLzOvIa_jq3zhzl!%{0WY1= z?1lv(=i!HL0+1Jtga7h>{ICFE$5$N839nJlZkw8vhfv8TeK1Cs4v!ubi|3#yKWUx4 zA#I)7M<@OH1pQeTZWi6bErp2ZLh48W*OeDAeqH###mznu56=p#&hzR0IRekQU!HGq z%Y{ZG)3|hnc06U+ ziP2}a8D52N9}IOYVCAzI<^P5N_eS3=1S-X|tY=D;5PYY~HWPtAk`x5wqM4O{yz9f4EX!^65$WqsgBffS1sKZIcQfQu1Z|}hOb0b3 zE-J0Kzs2DUgP&jbC?>qx#zg5Hb~>f~#?Ev1a99A3220RkcqY%Qt)v=*?|OcIZ|Am^ z`|2W`To^TkJ~eFMnd$9=`Mod|8zfC!rn>m;j!zyT1e!W4`F%7G;L7_E0qCUp8cF+m|lg;VR4K7 zK_5P{&2(vu)j9)IQRrl5o%;vc9TyiPYsiZ8G|t=)k*Ps*<(HzRv-6VecEP){>?TH# zJRlj67c%|obk$hmKCf{Qo(T-3N@z&foIhN^{h>aVGx_3~^$4%<{;JqMNZYeUwt1f! zO*8YdJR#6h5}m=twb3Ixv*ANx35rSgX`>*Te|5RjeF$dsNMz^*3JW(aZD^())CPk| zw|#pW`(ov#=ka!!%yE{5Nua2@2)LhbPYzYKwW@l~SBgoJ0P^)A`u|z)f2?hpO1Ark zI_+*f{~haHUD@k1(Nwf2Kh?Hxx_*CB^-$eLoT~^QV!(}J`eOLKno~jAJ4Ccl{R;~O zw$5k{_Fn#*N}b*O{bo~A$bOJ_RFWXZS0>3C1Lo*h{rKCnourM3q}%%BS{5Bs*5Kff z-AwXRk-Pi*BBoJk0{8GZW8vrHdb`Ghw<0XWB%S@x$mq0y3K`0v{myWy(ptUTC?iBQ zh{AW-_AKJygv5jT=}WQoa_Z^QEDLIILdhJ~c;z4A=XGv#y@d>EwXU5F7m=uEVq|-} zo_23Lhm;xLBP(P+J>KbCHC9LoySg)Ek71 z72LoD8vln#Yc%&=V>k1~CHbZ30{IwD`IJ6fj$)Mc89hCg-)t1=*pr<&2C;yqphr_R z(-_-F0przp$!?#XX}q(oaTT?U_A8L;E1cF1ZC;Wa=Q~IU=WVhp4^&d*Cn+UA^d z{h@`7A^@!8sGV3Ki?n&ssGI>W5E{)u0-_HXt=Aox+~Nm41@Wj#7;Cc?Hv}NKY2OKK zasQLv+b06-^1soq#gYj$yvwf73E#)gn`q4vZg*~)-<&n7qy%83{p#!Ly50?#H7~_0 zf!d+~w)g9Mu|Mhq{%l*|3};sH`t!lTjphSgjeU9){V>ZD$u#zfmSRgK;q%-=p6C*o}@vt$eyUekC4DZ-O8 zRta?E&sng^6bQu!mCt6oe>e6@v$s`ik3k_z9W#Q~~=in+wByq*}ic4EC2D%~DKe$_8yvNOQU-z2Ztp zYuYAgdRSGlEhO5Ijy5+4Pcc&$?&Jd@eNb z7=&Nvb1Mq_5)Bb^VZ^6$Nq;qvQoO5M@m^atr`dhb_UzRxcCd@z$|pJFK{_InuBZ1u z=~8S4zY)*QCv4lfsMjfEE!7=1U3nabHuMZ|GLV)h{vVr^rS}Kvo&&}mE%L4!C`a7E zeJcc3w6FI`y6&9A(gM8#uL<(+_`R<*|PhZqmDTWaMfWx{x%bR*3HAaW(9oEn(dLo`Y^8u#9?egXF&YmJ$L-L|LQ zx|<~-ZFnD?1s9zJ7s`cH^2vJ@pQ>He1sKfG9i=Qsd^YL{lWy*(6Q$9hMtpL_`^wHJwSYC05m(jKMDceR5;`D z59YU$ZBEo?R``Kp*n3;dB_6PF7}{jD=>$&|J(Y+zzVVD_6(3B#)=LLmk81<(r9s~% z5Gkv?-lF*-KcoFtB1sWv)l_-B`$MDqKsy=>xtLZhJjs);c9(g=pHx!YzkOh=*LXvg zLHl`uzUc$xpL5bSWj9Fglb)^L(#gmUa_n7%){VJjv5?L-%{!(vy4$L?NZB(HN6OY)L@BL~ICfff&k@=gs@*`XTs7_QgdVMFPPRUJEr^b{?g4#mKZBy?#=i1@| ziuAeOu^_(Yg6isD9h zPnm`SugPYjxxhy|x+uutLLYu*l^v{BSL%EO|LD~1ll|;>) zqq?J^HN&pXvexIbJ5DRK#&;ksf`pHEm8SBYW$r%f$CdmOKPLNEGn@11pFJkCaD?zk zr-wdJ7MfbZufM8A9dBY$H8dK(%3nGyyV>juFv)DvqUTqSii#||W)gpWsC9ekWn1@E zc+&Q&T~bekrxQ_IZUV{ge^nTPA8afFv<}Lh?XJ^~?q1uHK(;9ge>V$^0i(q1t~$_3 z9s`>=0TmO6xu(9fryf&dpYJ;%gMkGH$Xnr$74uSZWMgTnb#415tZN{OY-&5jpSlVT z<1Wf350ypG#-vkG15eU8mJy)YW8r!d zbkNAxciwA0?Ss38)8WWZpQw)YCHX3Y#EBE^8k!zcQf@vmj&Cl2GsYLb9xzp za75)rs0c#a86BXz3xdP;eIS=^7D5rx5;t%Uw}ahkZm~e^E?2@(11p!=70r+ zq1HPv=?cN%7}fR%&djhrPJc&t72wX_LW8I7fWID5&%p?D(^)rCt=N434aFHB{97*6 zNiz5_KS5%mMSGrYaEI0wT7qE?D^pp!)r0t~%mSv9Y8@oBNO2X2qY?u#gvLxo>`aB?ZojZ~g_KeATzR%^y=X0eO!}j?TAxwjBWeKIS#1n`E!$j(%YpXFm^jYhXnM@Y!-B^DiT?@u@pS8g~$Mp zLmc4OnV7w;BtS0vHIO*Zyo&JUYj9fi$+x>Y!uvmj%(tm0V30go2l0rVdh?4HuZFBG z`i>L5Rl!m@TF$Y&u4VS8v>g4)^Q4MMZAi9e}7=6SdOdHY9rp&hG~y81}E(W6Hp z3&%0mxMuGt-;Id2nAPRC6U1Z3vlz|b&fnH#oKLUYlm*3M@w6|K!o~T(3Asp;IDw)! zbv7<9D06UDm!RaqrG_1IoB9*Mt}53J2?}<4LvaQIsss_Yum>^j6-T!awpXBE@;Dnb{qJeWDyFI zY1R1~nl@YB{LGhpk`-|enXbbcx9x|QD<UoSEygh8WX-qn zri50F-Saos)$)8gag9c?9=CsQJiF99dn%npn1NtK{(+n0!K3l-J`d_@H*lG&nmv_NawJYmG&gO*UBy|RjME?2dzN9)2HfrcJ5Jza@&jL3#<>#u*A=RC^&`C;P~pRJ6> z_sdTO=g5kf$u-2SAC|hDK6WPFz5Q`sP|*1>-FTK<%5>`PYMeYcQ2n_ZoBzSzt};a8 z#)B!PyFlH|eQT1f50r3>NC8}_h=M%-V_L9lk{$TbPr*l-fPyoHeOcYksR2__NAefM zr1SvV$Ui0D=tYNwOSgf-$GzOpT*`W18M)h`7zCssj&J{l1nMB;&LiwRo%JE*;w!*( zjefXhH|x&Ce&oPKm8KG)Mxrck5t!tZ6g{P+Q&qoKtrDM7sT+#4wk)yCXQ3>x>WuvO z`fI3O)y1D1=B%R=!DOe0_8S9}W2FSOhRLBUq!KYAX ze-s7(RsR=IC$1T&-r)ARisIf#4I!qW(;_v|+)JFoi;8n?)@&P$wC0o>iNj@jG;5dA z>P~|07Dt*ZRt!3wGVVbNf`Sq}he*K4;OA_@D{KU)AedaOPGBayo_$GBUWL*PqRljk zZ_CTfC5-^XwBfap@XZ^@cfdr~Jh^>8Jb{1lLNdJCN^kA-`@6nHxRL;wtLH~*t9C4P|t}$ z?)B@)HY$wV%Rl0{VT3r&;YG|W2w^+snbSfb_y|cvaeK{)rayYGXP#Y9!FcsG1&kUr z)`r|fA6@l&_jbGSY>__W_^AfXZ6mS2K6PeoUVHlevf~1LjRY0vR{M2@`+>k0CFKXt z9|wM+1#YRbvX+p>d?+?3z5zpSAqR}C?7laKp?kek>D?;*OrtgIz<>kD{~Ip--_K2L zp~2K2Q;q>PB!trIcPb6OIfz;@>^65xiIhcv& z?|}Luht_nl0ngzo^7Ep?vi4^L=)X{<@I8mbXlS72_3cSBIE*ByJA5w4#%RJQtHaVD zuy6O(f7h#SH|g?Qr+$zypZwCB6?7t5_WG>-CP}J*i!+03r{>7Vkp1_8m6PL7Z9)HZ zYSkdaFBa$>@6^Obg8P8fIfH`-wuMV@5%evf%QS025FrV5GppVbcDVlhx#E&B*eG@I z&Q?`qDvzR&to59IcR8+ffo4h^VotR{#6*Ax zfL?qMGw6)#+eU`a@rjLSjq!m=uDAgu zEUk=hq>35kW0b85U)qd1U%@SMYC~Wi_&2#+5iZ4e5)l+fSQG9 zOSI}V9#TJxYr{Qo$3g^5N}x)~P0ipd^%x_%^=ySQ80$n;W;LQL6B@mB+6Fogu!~TK zzHgFTr&~OcNii=<-;-EA0h)$%&s`(!^NDQ< zd&f6gpn-yOK?LM=KkuT!PP1VGX9wxqlPgU2Bt6kjkt{0%XSk zTyt_D<-0l=0^K?-z2jj8yvarfkgZ$Y*kTKmx?=w#M z=pFeJ`-8LJ1IgI4R~t@5TdsD(gt9zrY(PjXn$osm?sb015mmm7=m+iw6vPE+R-!de ze}W~8h8C=4U3vhn=S36W%bi#tXZ@)k_Uq*<8cydcVg+)SH+K6+SicS%YG|5`#={SM zl!SVkp6>1uMp#J7O*;Uk+E)kuQP!b1{aUdI@QzTR{vCMdBmzE0W&x-@ldV;8wSeG< z!Uk;3zi6NVyz6YLx4gIUvh5oKaW;Ux#Bt(wbF4Wz6t0op5 z#NA}}!72k1?n5amF`-9V>3nGScPI2zPy#S%jbTwv>vtoTVk2(xKD?Qr{id)`L3cRP zrl&<`^?3aD-A8h_jDiZ4uNMPqF|$!`04Y^XsUMJNCaWJ9^ygA4{4baC;Rm<8h2on! z=t!(x8cfy@4OHvf5w%3ySOtVEjg!p3>(|?n z)=p^W_Y$?waYRhlgYIjG9GttQml_)ntihi=6wh$Whk*Wod%$DhKTM@QMJ(&!nxm~J zMQCRV@9uoGIFJ8{PNKWnu}j|w@Ra3}c#NAbMYT$Kd5C@mvxqmHXi+I4idDEO@Bf?= zaGC!+Cq5S6PyaUy;6L_|A`&jn`@k=Rb6u8*H~lhP)m91B1j-`{pb-7HW9$8=9AS)u zism2L7E37&x?qX_;3~JmATyy{)WK<~zQ?Yl`|{ZEvn?EJV2$6RK>uL-y=)mPnXNR` zz}ZsM7Xj1JexU!fa9WVeb4u9VYfeOUPXujYJTq`HX+1g?s%q3mArx#M&}&fm%s`W= z2ecQv?!3y$Be#g9b50HZ6A};z^1t>tAeN`kT9%P_4hpltAs&nwXnEAyQ)ZVsq0$wb!F=fq?n=asSy|MvD$rFHY( z7D(LoJS5V4q9jmh^C{k^{0uR!JDi6}+~KkO0(^Kts9c@@pN!EFWQ;i3A?~16>chx7 zZxj?fe$~T~{M2+mlGcsZXjYa(?_%kPf zW`G4BkFT51oAv6d>ur$-LNzdaGlSLtD0JjYUt*Sxw={DobcJH}^{KDX^POqW)B4Uj zL>Q-Y7|oO{H|cp?#m=$1Sy>1_KN6eo3{w`xbG~5}Cx-;>JGyViJ&Gizq&&<@ia40_ zc?2x_g9jU{F&~naeyi*FsXoPV%OkY;Za>7e{PuTcW01*)Dz=wy$8k3fJN?Q%;yRJQ zFGvIBBI$oGtE>6WxzrfFl<_M22Dv=K9{K=&u^f&=qhN;VT5=6d%W_O%|EF#M-hTho zI2C)NC3gOJft>?Png(pjkgrzGz!V<=}4I9fSk}7S1~}#I)O|C>*p!;ZloVFh~0GJSa6U7+<{}T zgB1n>8R!2oa$g#N8a!|@hsCXQ{rvEevW89S+4$+Lj=XGP-eR&~q%FfC)_Q#*6* z@Jj3QLeS;=1$d9omqtD%Hul;H`Vcn*F1)~#O%^^Y@!FoA!9m>IUIW6tckyyKG76Q-B7u|O)T zOY(UJENdz<)fs7YRQVZVSw4T3^>@`x1jtH2ULp%3p0|GjG)SQhcVI=}49mFw!>W2` z+NPvO;?Dnc@83a72od+{@EL4UowmVpW@GIs%7jnJ-KFr}R{VLh^`Jy}#TEo;8eHN^ zBm19E+z7XlK6S)vLo^BhWPtph)Xdh_-4f=nR?rkkJcQYWOl<}>iUG0$t-i?VbzJ68 zI2~Ugb5{-_uvL`eDl6^>U{rmm88CtOP_vjjE(ze?-T&}B0Kj;9PNYr%}cFNJa}>;iFm&$xqujAkxgZkZv0%jpwR2Sko-=7xoQBS98MisxGk3$?b)h{6(Oje z%V1vm7Enk#{{w|Y93ehjOyg*Cs?)>r^9}(h_)Q>y=3m#fx-Ge1yQ!*m*zj8lJjP5* z_1+J2`;dr+%PKO)MpIrQsxmA*Uh~;fiWy6V`eh%{pTzF$G`)F!G=$UU+f9$byU)yZ zjqr1zv`MLXb+0)oW;p%{8rf(a_RbI2_OR}v!VQD1pFS3>YCQQv;EAwsNu{O`m+(FZ z+HwxC(U;sf-ht3u%Z|^>fTbOR@8bLs*82cq{UXnpDGE$ZgpnHseCr6>3p&@1gjSZt zN(N$YCb=IVU>WLx$n_Vn!ITC(dYHua2Ya_M*Zr>v03wPr`X3EAe(6hNd>k127JSX< zHHYuTJ))9U0!0et$S5xmH8Pm}(R#6Zo1KTA)d;KqFmnOq=(vaVZl}=g(JNt$wSqvKQn{AjI!tdZc;IwO%=~+g3*br0u!I~$$mi;Ya8)^ z_Y1_j*I!*PJfh@Q-*z7)^N=ZvIS)!c;r&;rGy>qockw3>$&<}6p|yc`V~Z*!kJTqY zYle0m=x$;&y0;^1wPAluNL1$=Vds9<=fNh5=TQZ8Yr!FL`}d8bB2Dpygxmy2M`gMh zg~oLK5uwTe*!fhE)q*4;F?a&Mwe2HD82AGe2>xz)A#AY-z6anuC4++`yevy zW3jnEJxKUg><3dl`cuz-&+IDxv|=xzgVYUPJP7~kD=r`LZWgFc-q^mEUpRjDp#O7w z1+B4U(@9lSijx#;h_M~sAJo(UHvR8VQxBDcO5i@R8Yg(-oU~pJ9C8v!Z2sDs(9kR| zPa$sBZS53|7!NQ03e2ad-gO|rjmG-Gj%9Hl(euHWHIMT6@sq{oSnG11!?=eQ4#eOu zS_RWfNoAE_qvdIW!H5A8RG*xIRsHklGL@)BxsmU$N zax-?XUVeqnjQ$+iMrr$gPWLo6{IY*|Ff@+=QSJ=+AF#zZ!#AdD@sx4vs1r9@VJD?m zpho)Nw2xpw5H#(~%g)w0yl5mN^;HkTD{!HLaSKmh_gI?ZTkkOg>A*<4ZLV2D<&t!a zre*+Fh$L`_FYwB(d2a@JI674CW5lE;o%?{_=yY1w5&FpP^P1RAHDanpQadJ5dc-o8 zD14fxJW?K#0Ly;vbZOTq zJy5?-IL^oW9yn>l!f+7vgS>FTr<#JRR=`sFW>s2m%>o={&J5Qqen1OnEhsg$(+G$q~uIY+A9D2x|u!q5`%{1I+EeHIy=gO>acT zO(`HAbE6wwo+;AH`N|BetSs|C_>ca}2%E65!*e2Shts<;CQ##ECR(bbcG*D8&%qD!f|NSXo`U4?=c8{m5yIg_ zH^(D$d>&@Z5G=(sucN|ypgp0g+C?Mg_AXGy#>PbijZ;U)crhLb4rt|5X_~krfJPl4 zf&Y%p0rfSkrU&^)t)xI_`bj54wg|LtmsR|yRe28E0I-|oJ|ow@Hg(859}N~I-cB%o z@BH3`RdNw6vr3qY90>f+%P2js*?ykXtd@%#ETIG> z|0v)64=kffgdZj^+03%j(HF8pLr3~DAU0`6`FDk)8vAa$jYh1!=~{TxUHii%P@)O| z6ND9hx#{1_=V1uY(B^ppY6llSv)Rf^yOdu4A8Y>|kM;ilf#bPSE+t#Y-g{lLA})Iq z$;i%1RyNtPviDvI*@VbevPl%#viB|{>-&5;$9ca$-_QA+)9v^D{gZPpuIKCdeC%<5 zJYPhTs7fGN{Tzb)w&&_%FimQlo4Y~~ZJR|2luO^G&uh!3$mPci=$55vbmP}KGc~Iv zqEDhzl`p$C3^I-j5n4OHm>#i3QNb<~cuSF4BJS?q$LRZ%8U0ua>Q`+-&9;9l75{{j zj&AFa4xirsd94B|33$Z=BIW;iJqyUVT&rWlrgqs>rtjhg-GG`@WY^RmH(g#MPl2(f z$5ET$PhBD})exLH@~l5 zr6hYE9fAS$Wyi-%{D)iObj9xj*T<}pV8oDa-sn~wI=`+f<5K0ru>ldSd4yKV?^oU4 z!hiJMC6{(ida15*vjBLkdei@Amz)VS=?K^rUvUVA8H@AsXQUpn?N00BMoKiBO6wf~ zEEG946V(?~V5x{@XnR(rs!5<(G39S3*b54nAX)zW92sAAIme5ki+waa);%A95KOuo z4h(m1D%9>>J&<&7>=8A1x~cch`Mu@(7PCMpxSHx-)h>=eXJRWhYP2fm7thROv!I7s z*|q0J1B}M~tI@cF`plRt+^9|$GiC3(NaG{$%p9SOzf-Tex0b>g+G9ON1R#HXYz5y0 z`UR(tOmhR-gY>meAGWN(O=TlD)37rJhxoo{@l?;$-AEAwm@L4xXV@}|3LhJGIixc_ zrTu`n6Wsws7`fAfrteASeD^G32yN?yAv~LoZu&(bA?QD9%k?TMj#bh$n6)fgL`O%+ zhKf^GpIByRlbWZ3H+tJa$Fl(irhh%&E!0|Lj5hSU3m>fQ3sfQ{A&hb9_TL__*iu-Z zCOAYrvvH%YDqx!!Sky}}Fjg6rZd>5pfkBK3%>DG|Cq|Z%jyFCvP17GgR2y$&6ozUy zAxJSD(xFdDFD|?N{LDf-yCh?9^6O_JzwM)8oGF*t8*_7Wes}w1@aji#rnqft@G{)1 zKgpO>3|)eqk}-bMc}5k0b%*l%i+9~KSr%`VQ9PNzb&MqOphy2AJk91?_<<5)?UtsC zB`D6v_CeUlf4IyIs(@4Op30H!WUP;wvX7#oKVGTmyCfQ0b&57@%S@PqK{HuYChoMTairs)86(ouCV z(X7*5aRM;cO3gIe2BMljr3u>3ykH0*+um-cke+JvXh}D#L^p|hz`vLr$C#hT`Ea>t z-x+2`!3VSN6BfS}0f6amUb%!-x+@q9!OV$YQwo|TZ(lQOl{<%_-2jaGw>az#-Dlw( zEUBM*u!Or*RsNiA+)%*Xi1RneK!P*lNd4q3OBS8rwt%pg7>h(8#b$&Xn<^ru;_Igb zrBw>(a>0~HO>Zw^entBYD08RLaFc?gW_Y()w>xs@P9IP`N0%^!>6H=&k~t9xkt=IH zn6&F=-&teEgE(`4?!W}Qk4XQk=i5Ybzd|>U6h3TpWZysv$lxAC<9DD<}E!?ft}tS zumCmG1{nnO?j>eaFn2KfRak@O)BF24YRgcyH4U)&e+MxyO?O?GlTEsE+lpT*P4j~z zbE1QR^zfdwik8&YX=)%<_{i$)@h0y1;$ev-)l0eol?aqGrC65RFLvHN$0|o$Z7K4Y z>xg1xLbnN=Yz-ka8h>XSpk-9)FT6ES`0mI*;nj_nLtON1dU_Cz627F=n8f)3yuP^P zVF<29r>721 z6bk;8SdxqY475E&pNw*K4YvAzg>ZMCUO*r(hPnPPsNozQNF_Z~ zOId30a@>E2LidLR%T%L4h0ac76G%<+H>zQOl+l?IoZpQIrOSb-w{PKxlh_+-;lP7D z_D4Q!V*)djLtXbL!|8w~z?L_^8h3?!8xPR0Cb$k>w?J#)3F|~he>Yzq!Ud)ib~%P5 zSW>yoFekmvPRi8hroslASaRa@ZYV0go68;BZdGp5G#?4jF7)zh%K1WX_#-M%zSu*1 zS{2L%Gw?9P5T}4Si2x z3+R>?2=|cP4Qjfz2F8(hA=iF6;E@eTTdd&V-1Rh_z*;><%wL17Rj0!z&=?qg4vw6k zEFs_+A?RL+3y$ntr=p_!T&udIm5+q?+NY0OwBS8?%bl|CjP`Ptriu#%2p`WpDAuXL z?Yq0Lqsbz)lK1NAYH@V{Pdmo*!Y3BCrv)k7_J&c!@kU)d;Oat*+;j)uuWtvxd4yQ` zYsmL@8ZS^rCOPsV%9Z%qAHBQA1ReN&y#|0myG;jmUIAWd_`mY3i@`lmU9vrQM%xpm z-Ypxr;MS&B0E3EPb~jW6(^WUwF)^W30h_Y*F?7`LV2OD$bKcTw^08{&QmaclR@qei z5vZ(6%>hNJvH-2tJ45{2r!^pl37DH}y0t3&Skp8} zm(+Spw?E}2vQe{`UZy@?ZKE%AdN6C)%hmlI*NSjhc;O&VB|-xrV@#OkRFoq`JHh3l)i64>#c>L2G#WV!&7#2P4Gf7?g!R& z@&NJD^UQ*8IG9OQ?ulm1*z+7LAz!|;qnhkc1#ws#{bwqc>(0Fm7-_qs#BNkKOHL0L zGund*SUylkCMCV7p7Ind5-`10pYI}=tM7&9^gtuUTq{7Y8%k&1E-L%?E{Ke4Pj-9V z7V9691SN==WhjO17w+dBby%J8{RsJ^01Isu&`p(;{scg!cKn-3{?zm7w;%0-``Ikt zfwA!$&ZZ#r4oXKhjpIEvUR)lXdNKS8s|_v&nb=_G((k~&e|UeL42bntl&nl_e5NZt zR&=bOTG-6^Y;4gU#Q1OXN`4{xuW8*x&y#F!+LqNjs*zE+m91`Alq(68L;EB3LM|{x zD-0R$xi{-M=uuupd&|Oq3^`@iI}YhbCd8RX6KL5JQ+OUwnf4|V?9F7j@5ugG{=%^4 zYMN9AF2LxipH~7f3eemO&~4vFqwpi(&V9ce*|aM5#_6`0Zm(H@N&5#Q<>u)giP!AT z7EN0-{T6RF1AY-e&1rBq$iD{vwy4Jx^mi-2>yUmbC(zv46Tk z2An?BFF6&U)#jg`(p(2u9>@?a7h(ZWt(5D?RH)i*3p$i#2vb|xryqK7kUn6~ZDJjY;aVrkjd!Vrc=pz0GE6u z&7x$~a(gpMwi5}S5TgfjAgX0*BKwU<2@W%iw?SAo1~ifgdg__5)+Z{6eU8L=nV)#{=0LA9t zg4V?U&Nf227Pi%zy;dH2OgLw$aTnd- z>q-aT_$J#%7f(^DzZnggv{~wc`)>>fTNhtxwEHFwZfQTF;I*m0cT}lo9=dAq%pRxg z<^6=WPiuF2-m;cRX+SF1N~{XH!c>O1cR*q0UOn4YFxo?XGoKH5pVkvI1h{)L>pKaA zC4sKj62M&HBnN;aKgA;oeduafRlUu>%Rj1zEy1O$$1I?AiJKD8FN@@YIZ59Fvt`de ze)I``&}R1mst4;lg0(mO*iQ0hP!!z_$4wv{^pQS1=i`WMg2ERlV~bBRo!dyaaD~ffn>gtF`uMV#n!6T zOM$kv`gdm+r@bf=r{uPPfo56RC*PWZKQMe+U-2M>6z2*++P;z^assS3r1|0jf=&?S z;e#O%nMD2|&`AIZ!bS{DZ65^DiRR``g9DmL-m?+_H#EoBY+27i_WG0350JKGp>L99 zfIF~-dY(SGe_|86_U^i{O#^X!3r{361$Ab|jk2^wh(E2yDtuPl2zc3i%?!`s`YXFb zlMx@%1PKa5=Oev^QE@A0?MwNOFhWV3vV~EIC0$|r6V4PH4f~U2t2(s~OiF_5m1T=b z9%7P;H%eT$xZJk}`Aqd!&MKVApE{|ToOxB5S@4cncIDzm>gCADFWr^Fk-*SJ#?G)% zH^O~O;Ws6M>u2ox zcAx2|5^95|4;em0MNxZ4BHqm)Wr#^vE9d+o5w&z-k%-}PQ5j;^%}hHBn+&GjT~DSo zvR&VfLAK&r*bT7n-ii1wN<3 z=7Ev$g+*qh{k$7gXLrXCASl#@SCI4xw-T;`;%@tJ@85RD&FUu#?6Dp7`v9c zSTfRt@*bti?Wa=*I4!*mOAaH(Y7QHcNeU@F2S094_Ec~PKbn)xH2)f>%4JX?6tPg2 zjR$t`j!u(}k?z_@ile$mJ9Fj*HpN}ljY5?}M&-2!h!Mz=FLnubhyyNv%u*n7%Z$PF_&MQI)Fxfunj=n-|XXe4fnMdSarmwnw)4dc)~1@)P^ zBYhF|tFf;>Mm)wjd@__R8)G~vv~NS=vFSDyQ|fiH+BkZ1@#E`0n|S?k&xHCrKcDm0 z;Uuo#xjt_mgBc%#DU9su+1XaP#>SEn=imEHx|7yYmdIz+2TjZ7BV7;M_;%1GF}Vn2 zt;|)boa>sxl9DpZc*CC2k}@M$soqXh*%)w#2upCnOCR*%NHD=SPxn9yX|BgiCPWo` z-1$`>?EPduCFPD-m(H!@JzuaG2jj1pt739q@Al%D#kv|hXw8ng2qL;?B&(CrB}b!Y z@zb;8ky?jD#CO&NRrR>o-(p0s@r!i@C2TO*FLt&zojq4b;bEAGe?*S@jNu-)f%oy& zTxqvx-P%_-)T&(H<_8+Obj}lw2^RXRtE=-qXylQ}$+y0>=PrA<>v<@a9IoCie>$>r zyJo%X+lw(PqmSeIak*4CO2V%*?;O{NMDY|(VKNKu$Wr;tMBgIj-Y~7Gyi{?VTXtZj zD#bGiaZa&~s0!Q+6^^ghwK}a&~L1*2&U#QrtEYnuJbcjUO3s(2G&Z8x-0a&3D

1_qqDps;PiB#h>dKn6^w<99bQO5iTg{4jHt)W(aY;!J)w2A$SEffbT?xxv)yv=k>d#c)-Ex zkJln1BFmo!_~&{@mf!Q;h>(_9PEYzKO_}{8Ij`aew(6Z(3063=*$vhzNmp+4k&lew;K(J6*s53C@309_WrteJ0^O^j1%e}+gaEZ>|qvwP8V zH%Jz7n0b<&ov2iMJN@!qkBzmp*yoscWa8PhhwKy9&rnR&zZ`8g>E9y3nH&9pz!6Tp z+m5Rye6-LMb|BnvBl1`Blfz#qM_qH5_hV4z=aoMD-!%JKz%(GVx%hHkxow$>^XccP z1-2TzFSDHKcj&z%2e_E~sHwAmV3r<60g{&U>2-b2i5a22#p89s6FM@YzKvsD>~XEzjOL@zI6^M$wzjQZ0Opy+;H)y&A2^F>>A!Urd2>S(4N~!}jk@aWUOkDilbwcHs^r6O{?OBb z1I7H35s#KMOIcR15iYh-a1eqgaXN>3Cl5a~V4w-K8?p8XAvm zLmv~dGEmrMlD=8AR<$;54ZEMnz3OgmlY+w#^MXIwIh6_jcqakOoR%~DNvZ@K9@)7dUf2H@U#WO;hx76 z_qP6HxX|%di`{Zg!h+-0Qva*@sxj-B+S=OsaOES>T|$niqja}^IJ16SO$Z?aO!@_m z)nUApsQ%l#yN~^1JqIQ@t^8t15VB+O3c2F(Yz^)1 z8{FciY3BA6UNjP!m6m=<{yQY9T5rYuiax|rq66a-U8U@*nDLFZseZO}r|av!N0*N= z)n`GUfZB&O-w#T=eCpPQfiDpQuRGh=uuc+kEmuh4Ep8__^Ku?BA*`7WAdxW3qn(qc zjOpx1`63)0*ozUTT)!7kS!CYJ@e(^?)Yv$Nv}!D2?z^sYc!RgwgKqh9gVRB(0ov6{ zoIGIEZ-^@?ri>j}hy$|RprmjD0R&1xH!Y58E$V`(gI_E;l9dzPFZK$oXZNXJtgG)V zC(s!jpD0J|ZofBXUCqHb;WKzlN#~ZMT~cG0fL+yyJMfD zF7rTg)YfHzZ;ont4Zl3A^9cJ~7H&j(ckJSi`i+bgirnbMu zq70&iT~v)`LpalQ@5TO?z|+E(sfqn!*$ocyfe0uYSb@mH{BqC)wl`7_^$E)@lW!^v zT?e9d^NvDjbRkfn+J*P`8k3Iu24*fO3-@m3NcS*{}=bHL{0iH^j}2MEfu;q?*wHj594FOTBJ`knOUk| z1isNEh6C>T80c}}`NghI!{@PpB)s{rkR7)NgzWF1(i6gI(~wd4orJgOyVzefvJaX( zkJ(^9t)T@@oURO(MU%~Xi9B#m4iLOllllz~Ft)8AgVCI{?Jlmc_hHp?NGW>aDqx*! zWFvu&PoN>|agxxBnAGR(cF~adD%W!jlEJbLwAf8FZnI8DuR3Kt*Ub}eJKB_NoX=X* zp9B`*0*@2Ydc-9bvuTmdBrrlz`0 z%-5Tm>(#1($gOi_NPTzWNu$V7@zlChkk$hgst1>Ua3c#u&W5HOAH+#93aGUaZ_nku z6wmH~8+{=f9n0-b)tZujwR%8`@Cj(+&bI#ko=RJEPX$WL4hBF(Dzuv}K%KoN`|<1y zMCxXi375s>1jSC*KtzY7p$Zw~+k@6j`iO29tl~*pMeEtZ>p+@xo0&O5@D4+?6mA3ev z3LV%(0p08yU5=)s^y9rrwD|^N?z>7!bJ22{FH6*w_#b--ST{A~WEHXIThC|&p_=xk zv$d1jhdMYo%vnhq8NE7Q(P)Y;uAaQJd7B>_W{9IXxRb@2c(z(r&M<|Kmscp#^RR%j z75=mV4M@jbZ`3V9Dw9i*R^>=wLplN{;7lIgD+pO&UVL2f$*6LqDP^z1m}ohROz)4> zEL$MT!wIgaY95#9NXaZ;(-4iqQN8IG@!X9H1igfeI3pmN9rgtzWZitXSoLghwcMdo zcVKc)c3k)UX(k*$PR?rL6!CbAOH~!Yn5WapkP9f)R{4F0=g8lyiG}a(L0SFLa;SUE z_ji7HL{Cf)1(zc)&*i;v>+s&$@pB7tGsOE$OjlaBQR~~2$5}ysx+E}N=&9A|k(xR!;?*mq+ zMPCyp2Z2Wt(5852gk5(>!GqGSfmUZ(X~CEmbgvXVwzBdxpFE+zg)(oe`jGc)AchR$ z1z3mvjT+g(j{(6mF5fQcxv32I6-r)}0c2*rVOgsClIYI-UFVpcSoVsP0K4$4hy=Z7 z`A`&cCB>2=45Sq58cYwMNQk}zzzZ?o@$g~?#o;`5GRAxiLCFDuq?a<|!MV+S$T zkZI$9benrKiAJm|UE=sGB67g1%}Ia~1e2LK?g5-O3MoFQNt1b85L!4z_hMr@ zCIam77ra_}0M6ugY1-yJbo9{A)zGH_Rp0zTq95!Z77iy8&Cx=!UXDKF_U#e~52sYU z2FE9mb^KW(g*@b8VxfqG1={w@gKacq6Sq{J{>;eVS!_>_)aY@h57;xZn?`NJu!_Bp zB`AYAu`aLrP*v5%H~SJw0_y14*nAInU##<_Yx{f@iBtx^ZPxLR*CIRwuU2k2mtTS5 z&~Ta!`J0aTM9`QLEM)t7ndXBWztO^Tg7ufW|>-V1ecMSobv74BN^QjLsfO& z54^%Sl%Gj}ay6*PPFF=N^u!6C9cBytDrFj&97z&`E!(?}h4Dqt?u}_f&XdXTYSWLj zOE|kO{aKQB1F{UV_V)JgW>D_EJo9 z2Uk=4&dwW;4KwLlD1U6M2T4+2y;(S59`5#;h?v|N@z^{t;^C1ZM>xPS^5o~o-%OA< zb9PgmXelK7Pq57TDn zC_v<08SYkB4PluamZS9yOqB)^2g%pg9hkvmV|isJn#Fq-krmJA@Yrot@uh&jFg(U} z{xL(qGH0WFm%&+NHLr z5g?>j^&0`J%qF>|foXeja0ZHSXQAtMz_dwsWSpEAo4%?!Q+SlWeezkb7fW9LC@Z@* z=}A+Ue1UOD+lk5Q%Ro6rSMdU^@Ab}Lxo7PI>~wyyPJY9if28=nArX-X{-I=$c^J69 zrmOxGZmQLIyjgD7dS6Oe>)^ot;k(w(zMbGO6N8g!(|QZ%ifbf=rEU3ZS(YqfU5p7z zK~;{4T1*+9vsN{mHH5}pU$Hdz1V5H*iElQ%NFJkZqxWMTnff z({bpRjP5iR>NMzxq#7!pmL_-2jxBR&U{H|#NCI28`eO2_Xwx-@C?83Q1-lVumdLk@ z`Ds=LQ3nBv^IsB>Ys${)2L_Xcoo_iQT56B<=USfd#<2ygam=D^(wmsp2*6P5p~yf!`Q){KWVx5{ znxirZ+1*S<$$TOai~||-Ad%CYH1961b9-A^Pi2E_$pTSr;;e!*EaK zAx#ReLTqo~8pCszc8T$aJt5o>spEi4_^$byj=?XspUoUnm>cJEBEUzWTB5n_hodfR zW16p%W{Ur2x1s(~{nw#&E7Ocr1NwUfA!#Q>q!d8cX78)N)E*rfq~ka~(sf+}NkdKb zRMgPk-e;>1-apatN#4w1>JLj`9uFv$pM?v`cv$uR!Z|?mA;SJV!pKp-CFZ)4|5~Wp zDaEQP%D(TehSRvMbl(?xQTRKmvS&X@288yn@daWN!qRc`DBMFaZn@XLHjmbH2Zj!# zaSbTYznG6b`s|)x$y^p!d$qR1>GD$FtT`^PS$A*D2#TQw2M6cmJF(EDk4y6wOg)eb z?xr>-%cX_tf8XUmlKVF)d#7R#^Chc+ft{?%BA15`zbWv@)N8TfR88VCGLg}Onoh5o z4HL)W{B4`%=!g11Bljc&)=zLXCr0V}>JO`M9wb0^KOxB|Y%o|NG z>awoepKkV?IU;MKZlLng-qY|(yp17X)k0<|O9!!+Q1IV1>C`W_Mz}D@uC4sr+-wd< zchS7fEfqSB`o$geU)3e3x9d%>kGOo^KzM#GQPtw*e2mw@F%Q`-XBc3>|E_`Z)f6jd z7TUkSdC*8(*z>yBF?r-go}Gu-`%@NX=7fS|-Rz+t2A0Aq?j%Aqi%T^*z<76k&4Bq| z)*Kq3R&F|t7djehnjg%kkkOWwo_-PS%jTmf#oGyTd0Mpf@>ij^Mjw-8z_Ei!pq##D zZ`5KwTySG+@JDU!og_{}U%H}ktLlmDf`YDZ$Kc)eat8Ck3?}`)Q?Sdhm!NVqrg1AU zo$TSMF)166+~*uvfJpxoaVS11P=d6|*`YXUIX-WPE~#OLZFyy*XQJ081CB2ng;6-7 z#!Fr_Bl{&ojkn`6| z9GXi}POh#sZ`bw{xs0;kyip0jATFP*4rX_ZM}eVUqbqYCnKJ3j(!Q=3FiRE5=d+SL zVY0NfIqxBaMt`1+cY|d7AG<~|$xnnqYvAU(ZecIDTr@tZdn#IsWis}`y1bq9ZXFWp zCm;%}wTbUOMcWU98Id7DdWTQma0UUnnLnUC=H-o;U zh&t2L+<)CyW}2V6Gl{k{Bu&AsofGm!bn3~ZWe?-XiT(6kRZ3{WKr}8P6E*>?lWJ>k z%%=Map$?^dyr1BuOfh)^jiI3->O6%{%LKs;ZkvNlN1NTOR4C2Nl}mW88YDPGDuVdag_aBmwA zDuEdO_PoSXkpdtJsh~N-BXyjU_NxEZ{TUk}j@cGNJ4l^5jyrNWC zY;k);%sTIgrqjfe>%=-5Df*8{ulS(iCW?49LNAEld5=ax`&m#&j>HF#0V|rOQHayZ zde+7du7)wJlPsl+LlweFrvNF|YY=p}EZnu1kQs>yIy(cAT;DJ|8WfitkAS$IqC{44 z&;k@g7<6Iorv+HXr(OFwBkmlJ5AWTn9kY9teOet%`+#4d-qe0~q{kh!9JD-EYA#X! zf((xaNQVBV$gXJZj|W7>(l73Qj#ckbd~iR7u0`Q@K!0pYiK|SLdL=I2$TtZ9~pvxoi$WnSehogCos9X9rRq{Uw>)2@oDGc zE`YC~|3W7zMECtQ=+#bdC?v4z-PK*FuD3yFeV{oq(3oddDRD+|?;%UrAW12K5@zwK zZ|qGC+EKGqdHQqT4&t}J@$E4w`>3_OMyYIa*;c0*He)pII(v}^=4ogyy@;p15>Q>~Np#7C{$%h}8e=qTw zz=o#j+aRM6y4HoRg7uz($prdVvSY?bU@u`P3$~zFD3s~y4u|rhJ2IG5IhSRGrA1|y zxxWj8*1`xH5%0bnr3{sc&DF{zpGLYuvyMjp(p$*F#2{R8o^h>CzK+_L4m&>1cms86=#*V(n07BJw4>{9t_5a*`20A&174oxDhS!;x^0zYCDqgaE?>9UOYNGEA4(G%3^47K|DNh0oHK!<*$T7bGjAqn|Ockr_9l5x<;+ zbJxu+y`AZtnd#cP6wQqaqXgBYOC8439FTw4D!0qQ(JqOU>`u_m>YfMC0cP&HoKH{9 zJ=9m73oD(Sg4mfF_r8p|AUuBk5S}f&3{#;gJM(;NH3?Hug8Ynd3Cm{Ec8GImox>_osZDT?Ld3 zwrIj#WHW0_l7$YJ-|ErJKJ>uOW4DN;;ccpqXy_aspA?o)Uet}kFnjd970Od3Tj{yV zZhL_D5AJyvX`Vj*MT*rqJ6iiZ+w-#u)x1&_b`YjdNeRKgV2x(2f?Gk|m;z(&*|$^P zv7#x9o`SHnA?fVd-KmjHrGDXeplh+qbaX7ZSZ!|aQB^t~L~FtJa?Z@R^BL2j4uAfb zWN_ByEAo`soWORWTxJl2BQL?Qx6+XbI5Hwz&6kIwr>^%}zD0#gq&1VfsdYQ|S%lUm z4JR1D94}+V1+>}F4vXtNzl2J83FZ8o-v8%+eg|ECxy7O&^z%Q&VfZ{c%IrLD8#wMX z2IBewTw4n+j$Tx!<3~9 z!8P9OSfu#uvMS$JWbCtuvEMk$CDb7X??^ij^5b*z`uO2$zIo&iMiZ`sfOtMZKYp-K zgW>D_@!-ooZEdPG+W{)JoCG9za+$UVUb2e4etypawSVg|htN-B7kHg4}TQghrMpe(+6Ppqn=9Ams6dN2ICfn1Rxu@n+%vzA)ntaG-(pG79bg<) zIH=Yx3?1bgRInP`^%NVKA}{`%cVTZGkGdmYP6ekMYW|*VN&vYL_fsCaI8b&9^0n5G zTfEjJ2-Gs+w=vI3zJ6UJR&$!W{9Ceq6Ucg7Rw5_vpDO5+jho0bYq`9Zm;tqhAECBc zI)uc=ewFTF?(z9I5rmW&yw?yQ3w9WsFU-2Cl0m;Oi3v#TRh3ZopHtpb7k(%eR>K=q z)N%RGhyLSEkwuVgeqd|JdLh`dG@l1{d|6Yy6bHsXWWaIrNL+$$-%ckN?dC-Ptrg#a zLkDzcj9(ZZ83EuHO%S_ZY_d~f8)%{iOF`FjYJbU#8cUL25c*HGxX_0GX|Enh0%a)* zzHao_iqt+e6=&L`4@DHREDrE-V3Mu#2-9(j?04@F)He;UUL570R`*!~y#nI2@@Cw> zNGWH~x+86}{UPQ4%3W(T(ac&WWq{YK`~7`NgMF?AV?6(BpSQrt;}G`0mZ_6IW!D6V z;81ZEE#u}Fj8ah#8T{ThA43qk;+^JO|Ji1y>m-Nq&jJl-n@6&l*_D&5s~$0(S;Kvn zG9HG(-JZBa0>7Rxl@cwQ-ORqQ0skoSltYmA)E;nN z1Q|ZGIGUAP`{j0rMzVJ3`eO9=)Vb89!cHAK)Fba+oa7&Q{unA&ukXCS^A|BCGK+uG zBncj&G3^j(oX~T+bmSw-Jfo*ZB87izMNMeM)tMHTixs1#3?@@4(-@{!ITw#J`@DB@4v; zcK4OS3mK5%@)<1DOIUE#$W6RdLMUhOJ@b{x)0R20nCZXBbMd$TQ^WuKG#|=$2wFp9 zE}s7}1Sz%9Q;P&W|MqUnqA*mbze#u_Ji1Gh-b5_ULYZ-$6}n#uQ@ZI zRQ@Eh6h9iZ9U!y+eNh=Qpq2S7cBBCpTB(EzfR_w~>cWH&vX2&KGOviR`@sW{>9?Y< z7+?R3x&Oc?%5+FCE5^%iU7S7^duin%eT8W%OVY+5(>4*`E*)q6G9tfdly|B{0P{CC z>WzE?iKtco>|Z9yma6R0Ax*i7~x3^GqoxWIn}VSg(|fAZu2Qq42O z$IBP0iL0Ec4W#Mb=$No#{cHZEbfk)HPAIv31Bfr3yf~%*kvV@6#G5jEj`ly#<)ZvN zbVqUF)9K4MxA87paB<1No(HB~ z&Aolvx*!{NbWl=MNS4xLS5=$Yk;xMO$JH*9CFFUn9lh~Fn7ol6(Tx#l2;wDgT+h|z`ofmF+^9hSts3R~XL zi<8*j>b&A(NW&ftI)yr)I4r=AXF0=i~EJ zKOTg|dx9hbfq%ELi~U^iG4dLq>wT3T{y&miklPHv0bN4?U1dOQLxG0MeASYf2GCVf zG6(Y!-j%-|eX+s?E&s>U) z$@Y}$gSSRrZ-&u+M_(_&*nw_2pocE~^>j)()X|>M*xdykdVohzL0s2%r{pwVs?Mph z7NJn3m%Z4)Kgz)}0Er_e<|FnmmZkM>P98jqKinum2XukmZ133hJ&iH!irqL=Q~M zD_~lBndl>hz7<;R$G&Vi?dDgH638p<>!u3xSAi7Au`uDadI7DALwN}u`$nIGi&ocOkoGdHb9j#Xj zcFl}hl*ScTjdpj9f4%?H|Ju>h zk2umD0*k3e6j4OyTa0vpwNrT2vp%fs80zCPYU^OD9)Cm<0mp_%H#5CXrM6$_dDB}C z`i2|zyGmfS|F{BRU?NfP2nx1zWM?OZN8mp2cz`Nc+(BkZ$2Eh7H#3->t)bb~bD6#}Via+gw3QB^ zVK1R@ps&^Me%b-Y9ii9K&|v6J;HVCh_WOXISLRsJ%WEGS8O-Rln=-{8EKhr`crv^` zDzBsjaI>sx=HquV&W;&IIJelHPz*D}&Hi>Q^-^RVM6dLk0?XIzbNI6B!zTMoT;V)hbl)< z@8d-*a|xD??E-F6m;=`fIHwMQo+Ssk2_|v&ph2?7c+|N6LrRWG>-~mxQ|;z+LoXhf z!I6^_hO(&OX(V&$@`;SGT!~o}P$=t9>hepWcSN>9pObwXG>2}1t*Xg3yvk3q_w{f2(u>))F zS9SlaijV6f5}j9!Ubc4hU`2Bg&6K%1M8W?2mQrL^7A^-aZCDVLCBA{ZCk1tU4e$uJ zR@{}gGXGczBO!FORuN8|w`I38*9m}DI52EY>F+H1hr#3Ex2wFv$F&=^t8+k}Dqgg1 zRN{g&d39#EE+pA~QRMqc!DLptH~2Uu92)~L<{WJN9|iUTjQJ60kqBmBFk*yVFXEEO zhcLryOa#xQk1Wm8z1|+CCygTDE!#8E4?*uBT|OGhnShz(Dz;v_8Nit!wQ|U zYk&UA#}5y#EtBuUo#dot8UIVKW~S5cOL%}AXg?mgGi_4!bHZ8Nq5dOZB`pRD0GI$a z1#;e%yY?qqmJ3QluKMHC-~qg(z9sZ^CeU2AY}-IH%{QL(ZV*MXCxWrTO703#nMwt@ARdyuhRO(RH49 zpyxVIpnY9Ursl~Y^O%dML{N03>dD{;QvzFqa(s$1{TCcmU$r?ET|v=Xx0K?e2SoF? z2pL2Gt)sb7VAw&I!A|>G03`2R@4X>J&@V~ev}FL+ZUWu?y*`Gu2B078C_8@yczmb; zoG~LKQ#0W-)(M(`^YrF3z&mk45oqp}ZxJKl#6B(Tf0#I^&)dnO0s4G#=F;s#7EWC} zh30dFVsDGwvq1riDlTpeKN_RGEf(0~#G9n1;*=)f`BJ6LKzd3Q%HS|hphU}rBM!MC zKk4cG~lK!RU{^mycms7&_B5@f7BLv!D6GAf~T#)1_(zB;uB23Tv<-x z7tf??oAwbl!IG^6sv=876`}hHP)N`iZr~~kz5=1IsV-f#5ktVlV6C^L8vG+PeVIsT z@UaK%!^;OuM`S1uyMfs%UDzDfxp}q*%I6>+@(x=D0)lt4m_h&C7y2XPfS@E+ms+3l z0fgW+wixpmg6@pS`EGLLJ@e=9@?5;X4w4z5#!8okR^KSRy(T4-6rkEB4gZ({9=a4q z^=CVv83K=>_7t`2G(iR>gmoG4SPvMY zWjaNh0Wq278`7#i7ha&XT?$Be0)bf9>7ic(z{U*IAy3cE=y&LWe3e1heH;LvlIrci zvFZ`6H0RP$2At2p)q5Du0C|-A8Ii=UHMrx8qc+JxwqEhP^LADZK1u&v^DkK`q-#(f z=s5Z4fTP))Z-IJR5c9dzf{C5qmfAm1O7QIGfSz-40P`>x$3%W!)b@r@J-ptJ-U)__ zb=7S)YJR zq9Wd8?U3W^2KzwJK@R_aHIj5k3~t1 zRIWl&x%Knj(io*XvY5gL1}U0rN-SVcCke;Bd{eB-cYms#McM6<+xKwU{pgoow48pO z0ja5MLggRE!~zhefnM$zc9018+BQc^IysO|S--Q3=m}DakGA-#^f~yowejE>8GIRL zISp3NRL39oMMMw0SG_OT@4gqkv%|HzK8uC=3_?`!Ff zJP6>2ScgtGBLN{1^Wrg_&IyV9V+P&d3R{K`?4y$f;N|~L{<|LIKXg07FxT1+t*jIR zBd7AO4As)rK{+mRWrag)rn9TSN`bl6HTR>TITX`|<%IWP2SJ0M{Vd#nvK*v;|3`o# z5e4?d7=MjdloAq{mibL-peHApa>?h^z%~aT(&!4g5(h>iI4p(9zp@nER|O#qKWkGBv1-vRr1aj_>NSD%yNRlS_-FgsOnMFWb0)&HoHoa8+?!Si~=O zFEj|{+yIaO4XL5RtNIuS_HYnO5Pu%GdjpKfCm4_k25^MCFFpbodDlS#75TZ>_J%P1 zy08#v9&ODXCVsHnK2dbDQKfWKG}0m| z-Q6WAAYIZdDGdt?Qi8MyB3(@(vK}Q zI!rW`4iuXqR#*AvD)igs&FPyZ{#94=(8&gWO`?~WyxP4J#gn^3{WR5kM9uqk)Sv+~ zI;vGwSJz4l`TfZD>*URIDGgK zuq&c*V0pzb>;9@UnO5kVy*T)MR~6pgQr{AG=?emZKEmN9V?pet0b|%Wc=2$x^Pe;N zRt22^%N-DK(ERs1nL^8cpvA?)nNDFSI2^Q7VPk30Fo)JPQ(Cb^gim8C*HjkN<k{0z!1NslZb1CvhW zNf}Y|Te+f?cKwtK%SN52iTk7wefjqI;7d_V zhJ`bDYF^&RvwQzW1(VTy@yN3o)Yl&FdWJDZ*tcdkZxsbFAE`{IzW0EcAH+#}QN)GE zJSXICCG$WZE}K9^gLL@Xy?KLlbTRgG2 zS7q0W2VP3Ve~U`(66;|`HsC$>5WrTbR`0v>^Nn6ms7h575}QNE@I^g2-EWvpG~xRVtqk8B#__nlV4A88z7ayKSAN*M|2GZ{rO+zu(+ z+GREr%q>r5-pzx-CX|iFBEq^?}-4a2R?Q0zMD4VkaiiH z7ifzVe5T+cz#_`7377~)Fn+rwc&yn4bQ*`Zz^v#nU|!K@B3VM^2Agu{EC>vfAzU79 zBW3bx8sTVKFtHrDpR+yaEdTR~>mksHX1ynpDgf_uhXCe+TY$4%8kzjR|HyA88&zdu zTT~(dOCnoK@98sMKBWWGDcQ4e#LlSB zF7*#Ar)265P&BV~S_h~thvhMcY#IzcF~&%g(VMaQ;N6Jy|S@?p;P#$ZMQ46+7)bcg8CVl4|~a5-Q_e4g8>DlpZHhPwb}7@>(DPo@9; zidLcdzzX%Z0ACKw(MYt|suq$hhd1 zP4Ix%uT;Kr{>n--M3OZ0oF>Sg4zrm=?9qGouwGraK9V*mf{Y?;!NaU^!!d*uSGB22 zWdq@jF(J#~X8FF?8MOkA(u6#|3qH93)Z*uPr#+9D@}V|#D^&Md{{r7?;YU8-4Jd)o zFN-IyU9|MEaVy#4g#!M@a5MxIIyK^_cw_o89j)Ykmz6-$GMO?g&+VdqdNYk_PHA=- zatNrM8G$GbJq<#DI1hRj#pB!_{b4$nFPh@3d$g|+K+OXUeA(Y(ue|Q)1*~va#8?4wSGGvqZwVZvur0b+K%k5^ zBto3r{)a<0ync(8sCfb<0}DinzY1Vck$GA=t<)e7s(|0!tly0=2K}@n*_GK-4E&~y z@77|3ilNZ^sv0GI%p52Q2-X-0H(E}>TbZVaoSNvc2;lA5y1VJS<3xOTt@+9Ztap7J zr_jRmCKj3*S{wNO?r%`vrxowMRr0&-zhgV?1>e^%CxbRu)^7v3aCG*k)M%_XhtGi% z;-~c8;WPK4IiUM@wZ=i57`RRo-L&_j_y98qUoP)d>#&mn6PKKPW&~F^3T}B+a@l6P z-izrc3;)Fhfw>d}|M`&eXItIy?^zB-bG zhwcEzSl$!Co0_+VaYrLQr>_@!wS!sEslRIOo4`1_mP17g<5MbY{g8t4nK#E>AgYl;FzWz-X zu?ulYt=lP#=q+_=Oqe)-Iw#$gph`q2z0tdd-(s_~KsCQzgv?I z$|JT%XH}xtcSBBrhqHe5SHLR+&5aL1yX7i36e~+PLgujPJS+Apc8azk7pt6r_mru) zTj9ENn%`Q;4ysdOM5g6v11ezh5TtN5lParLeCc+p7e!>2haQpx@J17e4hD`YFw~q5 zEFnc(zwLhr9~+Bzy(-^pO*35iX3=oF!F;z{IaT64Ccjd8)Y?2LtvxAwrsH15-2E^7 z@NQ6|*Dg7BsjTkLs_pe3n!uT`^@b@`Pnq40WYQd&PE$!Ctpf?3rMX+0{GUpg`0u1X z6o&h-qrctAwo!EqTYdu0k>r7#p4t$iTg|N_Kx8-1VbR^@_PSc;Dr(wZVUB{ zhi{?Sy}N&v>&vfjmZ5C(-xW<431Hm%8rCRU>Yip5O}${0m=?#}T&yFPA&NJP;es%I zx4ZXnhnqho6q#I@-l(fBsDic&3p`7!hx95v1`R<|d3F58tNA;D9{GNo_3{P%f# zLK5nBX+81V6>rbwb}o*k*lt$eby?5t)j^8Y^zBa;6#rGzF)-idY^^QL8f&Vwa%3?z zeV{oM@}2amDb3A15BEr7`ZhW&+IL>)MLwl^gCAq*n9Z1UZ+UZwTVYf0QVEKL5d&X` zz={goYCaG*iQx;Oh-9e^05@Q$aJodL~>FH*4}ID z-th5pd^(Z2mxH43MbtAM2h)8~%1(N6?5N|kWU7!^+R5rt-|;6Q3G^2CuZ!3F@^1&j zrXpBbp-Xsutv&Uucuf)K#~h^>Iu(a!$u82c+N)A?wP&Ic ztIi%KV#8lDiNahQoP{U9kT$nM9wX2J-^>s4?C@bJ^_|-}Nly|ao0dP=-V6vameaAh zO?)A}RkW7K?@YOo*~#;hZP&#lTy3Ox>th&tNVm7thc4-7{QH)3m4M&Ga%%A`aBt7* zn;4^~6B)DV3ygsoNqx!U5^?76UYF!DI@%F^wwXkyXmijM(m!BO;QZMCNC|Q?<<2%|A|7I}+lAe&EpXv$Dnz_|eM&gd|o24ur$Tjx8QDvt) zd-gFv*JU~MvJPb@BKg<-&cOCoiVui?nNnYgr6m=78@UR1fDC)AvT z_s7yU`-_sBMsH5!VQ)HtW4G^YV7~8*nC?#wt=A}7!B4%oe{yNEcg^CTNc?~aw>Wr=rT@)-=`hLULZvVLFmcPPW z%dYhtuNGd?NkHnI4Qs>aPJJsv`e&XjvY_xS+TR+QSeO#>e924{c7CX{{+GM+*6lJk zyEWRkZZYh<_oKT}L?061Yi#Cr-AmhyCPv@V9Vz%puPus1oIOys9cgH{Mn=k|9v$?_ zr+z2cPh4@ygoh?qN)Y?0Oc|}0yo5~SmlS;U($DMF3vXX(HE^VykM!@&kx;5lNXix( zI~h2H7xwyY28j*uOy|THG3#O;JB5(i&@dFu(4Ivq8iJ|b_xp>;<%6%5rKC8&0D}^ zCBUP);`ZMq_wrJ>nUk@z6u zY(lMZffS6*Se7Sd{BPfUQ0ZEtS)oDV)j^BLS#n(3EIn&k@1~P;JJDvI0d{fVwT95H zVrKRT$79mv0|QKH@6|$sMU++rBo%NIwcSo_N;AX=plq_`@UO4Mn;)B(TyfcW+pW9? zh?dxM8k^u^?+2p=JFx|Gv3k#ceGZwA&9K-JYCdrsKQxZC((iOC!GU_=m#>~81;QC( zGMv?KbZAiqa?CBdRXZB5qs>j78%=Ie%Dkh;q89TP$2xvL%psfhFBGGU+UV zLD``9DhjWT>>Ktl_W{xE5{{~|-Qp`4cgms!{%7OcZFlCJnRA4^vri5Ap!12EF47`K zt?zw}Mqvb~_JW>j!6``Y=op0`fbh^scGi*Oa_nheEV+H7oWDB))JfyB{A72 zhV?Q4sfQpNJeM_vT{YNK| zILFTnc+svGv!`I_Ay4p?>>^!cRB$H_V8o%UCFgRT_pqnWk8?ef^14F;6j5O=%s1~o zq$@|uS_WB>OHkHgbd>?@W>lo6&ed{Q{*HuO6^56Y1I{?SjEqZ(3Y`}Qb@(vA(<_{H znZ>%oGfvPMI^T%z>ArbNxY3@X`;hPLLR3N%@Z;FoLfT?+HsU<5iM4-y?H@S5#LEvb zI2g7|ZIG_>qkIN^ZJR%7QNCEuJayDN+P@cQ-9NDh*{Uo}@y+q@*Z@LrKD@YjN?vBe z-`S_)Xa0{lbR!&9O7354>+&JMzI)y0_3ripQIQXg;r`|C7O%>!(^+kS%Wy5w>grzN z_LY6h;zlA&s*)mJQcfU1jb9vI;Sm5S!*H6Dr76Oph+hR zxaucJaIN8r8(2WcPqQw~5Wg)?eEO=p>R&4oP%fOF_q_ITeymL@xm8Ry#=>UCiU* zz0UstLnIuk&aIsxWLOqDJ zhBU)s}Mgti(0Sr=hOaZKA!ZXVy{&*jX9w=*t&H^9T`pIO~y5+dsg3 zk$E-N%ExBNCtm9h0{>Ll z`h0tAxCXRm#jW2le=5gXFJTA(TfUCTKrk2QZ(a2uYp$b~>T%}n@bmdtMsMW+o=WSF@<>1L@E_GRuj%B7 za?ktDOCtb$la8Ril8AdDqN0royecUbK3{2&4H&iJ8geY_3*&r;l;2v&nM5w1D zd?MB-M?ij3#n4h5{_@ZI`%=AXKB&+<2Aw2(NXq2GnR7r*1m?uheN;Mr}ujMB0~?k zVfB)6E3J*-urAwQWi8m}pG0>7VP^mUa@JMp0BCVyGo(XbF6GyB(!BBRgE4FxP=(Sq zAEUNS|F!|hwPIG%Ti`mEp(V#0$-Og+i@aq#r}Ln8zs~n4%;}iG#SPxBiDs2I(Jj!v z``0yHBo>Q*^}h^Iz)>Cz@X!7@8!kB5;3t<`Z(^Zvx%nv|*6=?80O%s*HYTX_zHu58 z;#ud>n%%K^)eppWEEV8@YvI_D59lLJ~o zh_*x6E!>^p?882dlB3OZE6AopB z0v{kJ-CW2V9{jOF{r1;XwF(jr1Z_KyrW%s(I*Eu@%n?HdIJeclbTgE z2CSNwbW$4`o!F}#wh0%lC676PD# z&E9`z3Irbk*&;?hjtN}p&5)nMxwlEAH>Q0=^6V$zc3u1Ms-U}Xei{7wH3!N*> z7l)J&Nf7W~*NW72@0zYBhWo-M2DR^8w@UlWli<|81qzQ1>v4d`8!pHZkLeA3Uo8lm z1~LvU>(+g<*=5i8BP3%&MW!Z-+z9yX>Rr}^W?`UR&yTeMr=+BoAea5hciQ2J$X<+> zMmQBYA#0hO9H0yYmq|a1w4c2fON30F$zp6>+=!%iiMb6ZH(auJ zFQ`ndo`_PvP=JhRXK9YLvh(*vlN;{WMpYDm-8Sh!GDd2R;kYpwe`7KLTEq1@F1mBE zDM9~Ru%E4qcWTKmwk%!SKH&ImUoB+;R=oEFyeez(C3fgWtL-QG;O>k`cma59wl;s~ zb3>GyT#&!|i@q$rl$LpW#Y) zu;?KL>Qgtf*Q%>|4CqUA zGO#N&#c@P%8LPFd_gAGEW|5&r+pXTm-cHI_wmzuHw}~y^89M2jFD}gF1MWxR`iy4+ z;LKLr=NMVHq+5Wx1mB@Yy5eZ| zFCssZiv9!?ZwLH=h2c_EhHj<3y}k13Q(J&?$muh`^*%*$z{ucB0FLtT^sEz${w7V< za|dLFf-0aAo}&xA>f*d?w01e3InW;Pk)DfT4x!8`UlXte2nEcyW3EAM0odj;%%)J| zieV-&bgM@UZPU_I5`CAtF0n{qYG9iAKB80idAWGn zNQ5iXcoS~SVAd5dPZdqjdLS7f40|R)WsEWjxAQi#izYgmV=6JT4Gi^iHQ(3a50B*{ z?Nb8I8u!$sj2ZbNY&V zY5PC$EiUTRqpb)KfNhMWXTvUchQxss4jkYmHK&PDTqnSMaG*zXO0v4zb+#Px`YMnI zI)AXVclJH?tgTP9wWr5y5JBSL>2l%nX;mH;v?q50$n><-hk61Q(%c(h;(j-%MHl}N zkhR_M4+{P9!>)t+hYf?lOLlQv)*3(wWH@GQzLNPe@D4~?8{4nnQkh~nnlC#pZvpIT zn}=EAV}Q7SMO!7y#27H8Wl62i9$J!80;SL^X-2~LwOj7aAAVlOB??n-ZyW{CTjoE|BK`_pu4}P zipRN}sy$9B;qL07Au+^NCc)D6xs#)G0-w4?ibXgWl1rpl{L*rWzCWEI?6|)GnwcA) zonWO?CK_ba(1!v~hnkovAq||d<_yrzAO#`qhmr}o^dsYeFPs%Qf{dWy?&f=b-#n)s z0BxQ`d)$Z5oqMmT=A^jQ=Q+v^pKt4m97s9i<=2Hq?tN|hYp(s7x3R#s*?A%z$g!Gh z06H6mwEG`P0I&#eJ%i9#kb>;V--qj(;V%RrHeH0yMgM_Q5R9c4&tdu)s+Pa@&f`>b zSR*eU%?b6itrab{-7dP=PYHUj$qFU=)~p@>iY80QuB&jN)VR}i>CJrqi;*s;v9uPEf@6Eo1-c$iaCJ#F z4nI}lfpKjLdAM^v4ht*!KXeTl<_K8&NRz6?V!b2A+ zTd796TMrOG#Wl4GV=V+`4onN#5Pv5*xpw*h*1{KHt2X^Q4jjgRy|V(?k^0mzIYnfm zMb=zs{hET`fe+ZmJydPVlMf(&b@QdgkULTYpOQ@%;#FG|}&`@~S%kLnoBbh09fasKmwZ;L7-gW!f&UUUhwFpJK zb&D!;5uJgyykb1>edEm2_&47|Vg?hvCesSrRudF}j_8M*nEt_;+Q9@E33sn)_o0Wm?4+#d6w1pL9@c`3&|>Z@s|{cG z%5yCo+yeL9Z9kd!&hT5TVOksdIo(peyk1UyMJOlb$;Gfby{F@XOzUm^&R6uEtBe`a z32{++qa*$P*8=VPCx)Yd{_dAr1rO~jm;*B(m#i=HO7J84s8t2+HH0WkIw!S;+KiD!g*c>= z|5d9imer#bjKxPLj zVfZEz4+CdvZ=9(Fr4DMF83qFAUyl9P^K&09Sks^}6mmmgY8{Zd+wsAAx{alYSea|%gMWzVNo>|dYN zKm7$DE2#JN_RV2WhwjOLvHwbwFAGSHN#v{|60d4#b^!Qst_KgWmoH29J)@quA`Cs( zml=Xl8}hJGiDoau9sm6JP(s3X^lG1P_uc2Kik)crfBCO{!P=aKo>#ACNU4v47qBOL zY28&8#;>>l1YV(bFq8C0C&|?>I9?BPzW{5Q{8G!=%NHj+qci;@YdlPd5m|>p;xUCY z@%n|CkKPqof!Djg%I6~GEAF8qwlb%(%pYq}2^o0_66blh@b=yXOW}u}bEH;v$6X|P zfE1;3(cOB=MUL}4yC~nt3wdC^;{QgaLE+4#?fN7&q6Kfa8Iu?89v5li(fTn<{}uhX z&KQIVyzG=ERV{gds;BZ(h+1>Ofbq^VZ2@68PtUYVHr)Itp4^ask;>FoLH>8W+1WF5 z8PW6QUGZJ1vMFB1AHb7<{U8INwU|=nt3weD`fl(9nixD;^Pe%&LHcpS)OHvio$+Fn_ccxXIcGYJ69;hD) zJ}k@}oSS>8^c%L;{_0!MEb94DvF{c|g>3j*Ka;pv5qE1i=<|8teh!uX*wnZQD+0R?O-1g{qHX)L>uSHoI)K+5VuK&Nnvs~F4=6J! z46EN*BE_WR>ro$l)OnXIF`5r;ECk@84Mb}BJ(WwAoo*0~0pV~wh@R^Wk1aiF`HjhV zS_1%Lmh3d=7KOMxl(yiUqft4f0@g+86r>qWRUD$qOQ5~`(uj(3h=WDpqDx5c%?IgU zrF-Wlg}#0^LsIqkxHQ1_7p5dS+U6V{tyN7KQc+iFzQn-J@;#Ieq7jp(LZypJQ`(FY zjgP|bn^^3lhOwOiMNb*mRShHtmZ=mG4k~PezYO@PUg+%=ox-;(QrCX#+YUleD*$H6 zkZje$)yw#&0lX8u0q)7L4j36I-@35)e*DPT7MZLcu`sjm+}heg3V zTY8HHSU~G(xkX{;Kn|>H12IKhpY#kZ08mIyb~A58-c|7p81a{kt#a6Us5mdlZm+ZE zNCV^YA}x&u%3{w)frCp&0zm(ksw%?l-Dq{kqe#R}@lJzg}?~G(7^R ziobALQuBk^+Um^GXdOirSn6@g&xW@2^6qO)pQMHrG$10X11iFXC^2Vh1Ci3(_B8&p z!VtYss`H0mMJ6S`2lyh8Ws9E*LJ{r1#dp7S$oKhwOAc#cw$Q)4JIu%b$~e$?v1n^Y zOifv=SswF{GWN-v%tduliA9n60flO|6@lOguDK(fDkWWv;7eIw zytCnvFB-6$?VxKDM0b$N#ozJ21)&u+P`RL>!14;0K;?^wF7vo97Lv5t;O3*HnEfA1 z{uF8HQn6B+8$U@Nvy+}l)^2gLVmCiNnsdx3EY9XysJEUd5fa_~Go2US#KA0L9_Rdy zB6daS{p#!%`QWH%^M2u_X6<{B;F^-MNLo$sI~SZ_K5JwN&BXK4ZBFeJyo_Z+Drf30 zwSjVZiGl~p&6h2M`oPMSllhMo2ENeKSN?OvO~q2|hCHu78fkC;+QY*h*OL|Iij>_O4|Asc{0Eu(>VaoT?OO&sF7h0T$o$4U zQ4q2sOTIjw*4D*ftLhSb$ zJVsZB7Ovh7Z}wt9LZgw>41Yg58}l%_Gj6`{OytVPCd=T_KTRh)vgT#ibwyTAoYoK0 zo9@s%6wIaz@xI3cHmJb2GYwG70JzL}s++-xpEOfRQLQ!>cQOs7`oIGddRo0T818oc z?}W9fT?y|?1Ccr9NZMP2!FP7ivGcM?B~271vOM~JPAMXaGxK3%KSe8MR4C+FetI-d zD5Dde+AL{zmjDWTeo=hu*Vm5=R=C3JnjL%e{FPZjjt+|TCYQ&>u&rVk`;jBDPO(s0 z%;2%9bZxFsc-$-`FrBmXM;k&RJ-fL1L=wtUYdPVkxd5f zt@gX8bW2bVtSP)yn=__F>N(BRBG0Dogj;cZN$t2!k%DqHr}G8XU34=-J+$zq_d`78 zXOfd)#3=1wmBFW;%TS3y1lI-ZI(J#9lVeYNO-PgidU45S zyuZ7}Lwi+a$CH#UaFB32zk?wme1+hMWbSZhqL|-jncuTOO<5F+U#Jj(S3m}VfN`a0 zOOQcdLP0a0`jFj!0cD$xr2TP&p_Lu565q}O-%;+e(TrYPJ>|Kb-y;khB+2-8peKL) z2k9eRNhvEgH|-f$-;2eny6!@kD26cFaBnz0e>}?3Bs(2rd@LM-`e<)9eX{Zl$3%dt zso+L97?<8r@msM}8bgkXRCQo6bN2ULakJgbIF6Qf=oDpH@!vN?U#aNvIb(hQW8!D^ zAXjtJn3_-$d-=IqbZL0HUBgMUzE);0t6-M>-Qs(BZQkh#LIvZvVk0}rpsGX1uMWQ^ zaL}3gwBv+?0T6RlTKyO}1{*cc@0zASO8!Z1=8R&4afUi4kM-|=I%@{{e&reT{9 zhguOkzlEEjg`9O9?ocs<272JK&b>DXYfNvQyox%+*Iv!vRJ!&EGg(Rh@}qwzhxQyy z{FTzsVC>iA--!YQi1vohj& zp+k|N4c2T^b6{$#;6{ejs=%Kqsxsc5}o?pck=ftjT+_cjfIvLRg%FaJcF4 zmvLxQ?M%9;uxuwRq_4KG{1lXr#ouKxOg}vTku!?dc)R(Z_q=+}i#R?{qq!n{te~fK zY1E8uCuoo*C958jf?sEf=dlc1yF89`w<+3*@P$x$|3byXL%Lam|D0SR`c9yG5@DTrbf0LNmCI2>Qb^)2H7vvw};6f*5a zTOC)@1bOUKPqoR9y(?^0NlZIAZg=3iO&ScnyzijWm5^(|FGjmg`pj8oACpG~K#_-JM@n(j=kLQIYqO^IhdY4Ru?X2d&Cjm~BLUv5XP4>NQ z0bG1_3c~G2KrPKvu*BIV$?`5s=g96MVj;#)zb5BX=-mnP9V=j0&9Vh-B~#lvtFwCt zFc~uW!>rldZ?v$c)z*X0f0pBil0j?npsGR9Lx#j`{F(=K2?5o*4XFnCv03z4?kkql(zBbpZbBp?;RT^Vxq4Y|vmpL@kluGkMDYoe^Us4mTD}UMKN8N(H4tI|Q zIFSI{#A4f+a=7qR4;poYuO<&9)TXeSmJ9sK)@hj@c^(NI zms`DF{O9NY<`ai*(<+19>35EPCLSBF8E=}8Qyw_nGZ3tTo^^%{yD7`qVB0RV=O`Py zjcse3&9UNvQpSK>>C^*mp%}scs36BDJG@X&vcUn}$M4^1rE_A9VR4^8pET&tcvF+M zalGpRH*xF38{#F1Q`)L{)5sr2PEe?gfPI8(_e=8MGStI9yXrB{^ztWtbc)RA*mcI< z1&hS5g!5GpwNrVnkl0gq`Sjy^p?O!m{^=q+WqNDk^jCKwWj%Eo{B5H1oiq_>etw<;yCIlk|fyLM+wOAybDc=T9nNU>V8s8 zK3Zb8H?9#lo67(Wn9nqgLtFLx(Lj~wv-EfG@rg6A+^s0I>OI?*)dyBs;~4Do`S^x2 zaANtO_o{_^UD=-_z_qgl;e_Q3`Y&qO8z7ZULed=JxM}3Q;%mVUKA5lJEg+oqVKtnL z&)}8xj1@l6D8doDM$qf^!yU>ILP40LPQq*lmPBOMG{XZ)KrWR~?Szsl*b@E4dT7>G zc^_P)waca9hW@L7rDn_WPC356&Fk{bPS1w1SyzfSQncW$`!g1yi`y-Mh>^^?x;O?i zxN6rI!1-GRo+>^UXZ{(?Gl** z7-=?C&lSB5@UgGyAiT|wQp!ly!&RSoz`bk+q87zMnmNv^#k^yOTAhESw!Vz_3>}?+ zz@1%rd}V^)VG>Ht%HIc2Fj8W(>X6N=IhQcoA&`qYCl#c+!L4P2XTb_IU0vJ-ocgr}n};+Vr-twrxcZ&p}Q>s=x3<;dLvJie)1u3Qyg_(I6$D`JT@Kb4|}fZ+PW!`c_A@V zb@c)inen2o#W4ONy^CNa&iP1F#(|*qrEVk>K_>r0uz@$4wJ_}T!$kGYwi>N|vPL-C zv2EvP`MZqy=&Q}-7bh+;IzJ|jY3%yuXop_9Q*3qu0AzwwbRMu^p`lB4u9RW#!fB3; zo%m-s=7{I=-YQq_U*IKvC{dLSaU6Y{k`Vua^9v;Kulg@JfPu6uC21lCm7J;+(sya& z`DyzxR@{f&q8IfY%DilcH*J+wVrq1^{gOpzqX!BzB8O_?dr2abk2C2r3j^4rvUM$d z2B_4+lIcVSwHhVCqt%v(oA0DD<651wI-*?y{}^QgYen4+x~dhmJRFmhmTq#PU6C1B znXTwVrD!~<#9y<}L+i{{&d3bJLp=3JjwEVOva~0nB`vv>TCNB^vqKQ#1}EZ5tTf4@@F`zfp! zFaTJSj|e@*LUlqbVG&Q$qx#i|2ijb65*jb6c|6pMar~O zfEA|+4aLPhM676-DASO5NG?M_A#h8{J|2QD31>}&iVfdX668rX-_)GVg zWq<|sq!yS2GN4n*J_if?n^Hh>W@RFzU1iqI+qPt1;`0TW{A%MS6RN1?FM&n4e;oM5 z<5ADbON{K%%Y2V|+cF0UQeaP^^MoD@`|+agk{y@jRVo3ecojFx7HZK}pkAcux1ic; zO#Mp)Kh&YnGaAc6I{=n37GxBslHSrK_7Lirs{9%og!$Fq#6Qbm@m)zd;yFng`B$k8 zwbd42%qSj169@JrFY?&4`6863f}{I*{Ws9!@}*M*Mp(|z=alf?8Olj*^iF(dP!pHw zDb;>9`VRS3Z0PN=#eU=opdaRaams`Q4bqoD;Q_9E_USnKh91~{O!yKWf7x4TEB^I# ze(>=gNtj z^hk|a^E~gdgYM&m)RLDXe?HcD%9kl>;T3f0Q4OppQAmd#|LSiA<0bx+{S(jj<7Pbe z)DQ<6vsfOF%$lx0xy`%CovUr5W}F>JAv-!!30=9BC&1(_PL;ZN3|e8?LC+pBIjpD} zb{2PheVH_4kV|Q8p#C-6E$TffBhU7y&KZuEkr@G@&*K*j9MY7OUR`j=PP(u5-Q)j0 z>wv?LUv2YFWX8a=1uX8|V}2-#Yxu%U!sp#rSK?HKh@JeP+C~CFu7aR7Z@q4}i}p6! zRZvC#k1*ea#lbKMzzW$Xdw62In5XV%&I3a&{qBv(4Pdx#-ek-TToq3ECO%n{05}!# zfqdpU`X-L%w8ux_)&rQ~X+v&YiwY;LjQ)a(e)2ce~1+VucOkn*p)n9*|@@Qdc=r_g^D zfOE7F;6bEGNEW=Mm11cg!B!!y40=GG4$h9PXp^K%Xa_O&mHk%^%$SO9!?gY05#^52 z*oN8Z`ajiAjY;uJ=kMGkKM8UjqYpB2PiXSpe(MKc%IJvzth_V$%7;RO-||1$bijO+ zp8Gnmcr*CHq-hcq;c(Vi%!a#}^*I7Aw_-OQDylWfjKDb)zvRV%s@etv>y9ig(hJy1 zzG(ztb6>WJ;!EwiGpU*{g;fz=F{F_Tru+Za88Qb7$y(x=s@b|djz_B{BV6(<>p%x3 z<^%7hO;$>m{{0zF4Lp6?PdP<%dNR7Q*?)n>K)PRP*Zk*n?U_8gu`0f;K7huUPJGKi zo~?xJbjhvW<0BHO@$Pci0Cz^$p!V!Zgrx6=GxBxYO;}5a5L`f8| zdCV!?vf>+xm(dhYKK=gey@w^#e0|!hO~nhe|2r~67!>d*&9^jJJl%L$iATBEIay@{ zGd%ace@;m(&xIREwXd39K6`4~w0e3g4~Q*l8&`05Bjti^Zqldjg%9qi1!w3b+|0HQ z*uQ{yCOjtrTps}+V>GBj`nxdaL)7tivYM-zdx?f4b#)*1WV8ND^i*vhwx-0(S-D04 zXq=~$dnfA1f7(oEA*sFn^wlz;IcRFyJG%h#!^$LyXcT+83Opz)un-o-X8}Pis5#x= zY+C|vKA8g?0NXhJWWC)IulyjUM%63!M&5E`zQbpOXazTQ>F!M7j)MH?MV4@~&Zj9tP7Whn55c^FAxb1MJ}hsMyiy^;5d#DtFfT1A~1VadU% z%kzzACwb?@CC{r#7swY`yZ38$yk2B{Y?Iv{l{(BetnMgI1xCP2cE+e~_wiWR4|w*j z@Gh4g7#Kfx_iXq>#Vh}J^JWbgP^dlIy3b~wGHK~gHA#fe8}4P~9kZl{zlwPpB8CdB zfraQMk}Mgvvg<$>w1yqL{2U~Ex6$83^gF~zgrCBfd41C zDQyugdi4adQ{Vl!dfv3_a?1L+)vDsYT`-WATE2Nx zxF_%=`Ax^)*je$HZg5vE*cz}Z@7;aGMd7Uy0Mf78Q9^MM&;Q4Y&@atJx+E#@o)h(j z`k9cs`&qS00mJRzam-$2<4xJYEM+>r_d!GPey-t|7b`^^a7yFLHTy(9)gz++PJr>xy4MWv8cHCOz>Y%MQesdrvignP zpzMbWZ8)wtDdA%|K5xjAi1$GoxU)= zq=;G{9a1*F$wFYy2q=UUC-#W-a06wY1cE-BJ3on#6jyP|%FG`WR9y979a|xV&gXq) zl6?Ed{KPa|AM6&3&EWk?ylUlL25n6uN7Js<&D`LKNmbNj92#8=^9;}3MC~Ztng3ir zVe-s{zM)Z`Yxw}Ji1|O7zB>@>_I;a2kqX&`>=8ot$jaWbHzj1t%3e{~dt_$ZWXqPF zBr|(QA$#vpc+XG2_xo?pQ*QTlUFSHC<2+8g*Z4G=uUtgUysf#4HAhr}XgQ|bWuGyR zBUPMd^-wi@9YLev<6_&#FgUL(&p2~b!4q^okR@57(}%$vCSAyRWm3JD$a$3ewPshR z8JWdhC`^Ud&a=hynLrUah{r99uV3KoToq{Ox>y%7Gkqi~pnkLQ&R z9qJ8dHO1h)Sw#vYuY*C^lHh9l0Sru4oRwyrH{XGqQTf5=Z)kVt?>eUX{Cx>B%)|@J zXOv}AA=uK93!%S{V~+S&gA=vhprPNq{Xkq3*L3I>lZ1K^CQaGxH4L5>jT<^Et0mLPM%pSHDAJic5S`kYGC59Zdy~oQ zIf2L;)MJtio#dZV{U=Ns!_#=YGxsG&N5LrfjAV(JT->=2omkksCIG&I_iZ&{W0W?AZ)f)^0AN=EOEwiI^oEV5yYvUN>1?TdoB+_2S zR^eOO-eJgXjmZ;MuXr5IqO+9lHrtV+PBs=^Q+ZeA1Kc69MZ&#`x!g8rYu>*(hKCr( zII5*%#6UT|d(%7>b-m~U7uAkNlkTT>Z34}G+R{c?Q}MLsYU2w0wutj%Vjtp?nIl+F zix+=ao!b2GmEw|ac>Cq_yw@9{cUI?c30KuI@o{y`y^Cr+p}9*HC2};;O<^e^?3gm; zI>BZ2nrE)*TjX8Vz8W2&h*|-n9%YRgaRrDQnUcm{R#d4W&oT zK+}&p|4)@j3uemC;_8Ik_<50>X@ic$#E?MMp=}&VPd}9Xf9e%F29{Z`+wD9DrAhUy zhUy>TZ9GMtlZdMtSb|%LX|tV=gDVCzlWNBO(r0nFY_UEPgtHB{QUnpRx0vn;cv_`Q zp`k`rzTP4I=scN+$m)u*Qeh|Q7l=kfd^d~|Olr}XdC$H=uiNBAZkfg}0s{Snp2~#J z$YLtWage8Lf10QtCrY@(cL&5vM)&E^{I!B5bv}=P^@FbgjsIoEl@qb3 zvXx0tBJ0)!Q*IN#=8x)Lu(oNM4h$ghvuyKvCRPa`UoxBYl6Ar6!!=dK`{h!d=oG4> z{|=uZ$ZI5oM9(Z{ZOG;kt~+tanx+fGhJk?`;j1ke8n0-3IUNwM87nQW_HZvvBS1L@ zv)8nTRabKykP@h8qY4Sr5THkMbVvuj#E|b8%$18$EmyDfy&-sgFN1Nn)DtG`-=Znh zfJ=qSgl8KzMyTv*$?jhJ?WKTt?nRSYS%nC6k^y;xl}@@SqQ0mD4v9BYnfB=F3X)0G zV?lNB5K^cK#lB^ zJGD7?Q#QGOe6P;|epkzh{F)F$Xn;~1l@;#8jgQ^MR^1B?wl}Ogx&a1DFeT@Z7^X_2ThQKPm!cIhKc{ zHq{!cB3gzQVD*uS@AzJ6noYbLl;S2oO8IP8!Qvt+Ul{lM1r*LDtK|OcsK`)TDU|{* z<)?}RL;2UqZ9hI*rQOZY`TgU`7aabLgchDgw}hiOPlKwgg~eaD_^z)u`ajF#yM?9` zfn`gM#UdQd)wH<#H`3l{>aE)jD43I5_OIvGPnGhGUeDWYh#9_UHt9vIixd8P;!q( z$4R5n%ZA^^=lX6cC%a3}`&;f?K$Q!noM7eqF&tF;jIVwwYDN-$oTMIPykEV+Yh+D( zVvB@2m*>YlJKF`b3gS6NuFqcMAoFPhMK}+=5@%NMH9ARh%YZ2>pHC~m;V_b+^28d@ zza8(jL@1#7pTR(lpfwb}k;&m!h-f2Le0j&Q5k0{@sCv zjtD|O2a1}SM{pI(ah4+JcAu}POiM^T3f@mV_5!Uo$JZmo1eiTgvfnO~4~;qI3+Fcq zy2QKnrTwCRdQ(nM-zbVNfS1QuyMf<49KX!>NaO57Un? zO$tAtLJoJ(`FBKMYE3jF*Qz9*h-R`KJPx)t7y@CJ!n$>c-$iUxiKTXwXrA!U=ek^1 zJKL%p>hp_n(%=^&LqcNU;!;vj!6=3HOM-j^N4(oSQBP(hL-{{u!Pm^2&XjU@-ZX60 z7r|u-pN7y(&UOYe7s5w`Oe#0gxnFpiM+z?8I+kCpEmdA_`$*&JF#A)|%SPf>y(QBm z>D0pnOwHDgI6^3#InW%IGx<} zXug0KF;v%mtS&!WK7P$(LrFE&FgY-bGz)bmnpf2;e9tEPeHq!eArY4h;gmG4L)*;h z2}P^Qhcu_qy31NCD%PU(bWM05BOA|>^sSj$B?`$8GAGw-n1$C6kBp1iZc1?lK1;}H z%>JQ=RFoA{67Hf&rShb@PXg3!ZETCR#+!0)D|r;Ki7QClP+WGM8f28yaYkzj{qTus z*KLz+RbYT8THV=w3^O|R+TB>k4wC3u3#{14t9*^j<4t)V89uz$l(>`7|didcsbo{7V0DacXP@7x-fv>D*wnIAP8 z+8k^j(6p9;pRsR$_+OKRf0<=y7}Mn*U(k$7<}gE`FVAC8f_;ONm&po!xK+VKHBY<+ z-~CARD1EfLr|D~7cuY_8)sE%^ilF|_ReI%fSMC&QqBlXNpL58`<18sHdq!VB{Q|{? z50!Lb6&11}twPC0PCEaVvdP6tP85stDDpLo%THG^Q!}KBHG4^@D-bIW2~EnuznvhK zq<#lj=sg&mBlR-@Wb3{AJT_+(F!$v&c?9Nq0STAyvU{Z-a-}zN9fRBy${s^ zs6VPlLws`eOz)p(cu3%C)~vp-vMche-4XBJ+LiOzDE}7Patc`a&HW+K;EWw`!u}AU zTBA-$)<_E_Vg30G9mCs?sGt5fd?fh~`XO;zr``X^{_)XJ(+YeVlTQK?;R;`1 zG7~#+l0@Rez4KYL(`kWDmYHY29Q!eGpolb-B{wdx-BF_a>HDXo5DA<*YuUVBc?B9D z3isW1Ul9pc&w8$Zy4Nh5A>|Y6E9M?qv3uT%<{Fo%2zn+bhiwoVOv=hl!!=O9{@`oO zqtv7I9op)<6W0Qams8r7jtk4fGN3$J%f^b)+{##=~gzkKW$$dTEKH1dN9I$6{6un_~kDXsn6eUv#*iHvS$1d z39hil>-BBvM`PBzuOLKZWs&ZxZNubKA-#TqO+$rptyv76uqY95WLUfeqop!KU^|$x z8(3rH=?>vtJs5Gn{^dkJ0YSMQMYPpyiYvrZ)%_k1FMtSUPDc6$sS3{_GK#S!s2G zR0K;-(0b&aD?GE9K=|mfA|QlHifhgdpTO`Q3k_HD=-B+LDTn?Mk6nkV zQ*ff<$)ZwgB$B2#0#ZKyh8%6Xn7w{Q9R{$&=9{s^%j{5sjkzKeM*s{2n-8OWSSA_m z{!{-?s)IY;#e3R&zb|f7?XnNu!pg%gBs{Ye6-ZitRW8KNaH?4=Al{uYK5?{fRVMtl z+9%o3PkH&hwzPqO(d8=9 z3?CDwp52d<<3Bf}5%>4`C{Hk;oS8BfIl-jA<#S(JrxnYN4s)EgIcFJCc5$yS9PqgA zGn^Qulpp!<7D`!FfMgrVJ8+Gc(dvR=PO!l$uF$wuQZ=M7FG>c)bx)ar@2fi47rl#49rA`t>#jb97bDn?3CGG~v z_5B0!?hM+t9@~Q%Aaty2B3?p+!g&b}>RRPxhi89bB)M?UEul_@dJ!cb*S^QGW@6{- z3(i)W*6esF_{&weD9-z6WDXcebx(|1U%H^2`h#?g?zoQdhN~?q3+i#?LaKN&zE@`) z4(aEp3}E0tL#-2UpxfoZM5|h_m`!6L+#!M&Sygsu`qdG^QuK@O&x_yi4B}$qkfFM! ztfu(TnG04R)Un#riA!r`@A|-V%Np70de<{2r+S$v4haz|4Hy24$4{`r4LtqJq3@r7$E%C6==;R=QDJ?fP28}$o-B%bO4)?WL7LE= zzK2oL3*ZL*p1#K8gJIRMKY#IMDLD7PV)~7i`0v1rdFBT2^6ODhDoMt(4Ni-_II zgTvzcx)!tnJMy`^+Ee{%`bc>(B7f^^2IJrQDzG3WkbTJjoe z)GAOJ_yS8)*^G~E!A4T6$l~0`A9u)=By$isZ}o*Vvp(MOVtwBAZSH=>X&JB1lK?Er zTgwsXEo}6673t9^$z5rGf5Ns3vD6U4t>!fH z1Kuw`aqAIUo%|}Yhk_ys8RLO7o@?4QWM(MfUn`99p8qRncj!$=_SmxVrcMiQruEXV z!jbm@@hfj3ac#FbFo`Cb2JTaL7dELJ`MyV2H-=7qUZrNrx~K~^yr+q1`Rl&(8MaT8 zV9`)5Q2EX}^qHl9{eY!zAur?h(+f4yrAXLjpY@t#$UGxfzg{2}0;u|I>2@OK6HjPW z4W1XL2E@bXH>v}zNZ7aj#`5Y7?l#x&Ff2+V&V6$y?M))tJo7LH^YfQhR&p7zRl{#d zk-=}q@!Q>b-{FqxA%sR;^6P!a$&kyl&x?mjV-CwUDgN>t5~1Ut_`6nowQ*= zX%t2_CDm=P;arnK{|7*d`V)Q5c(62STqqzuN_a2$i(MA%r%JiQN93DhDkj>X;h(RK;v7ji-;_<3reD3zT~5LGJ=gl^5<7q z$)A&aLlj(;FdY}_Q$YIQOeQsLvN)Fj4UZIN^nwlBmEX@3 z3omog`L)p(+mbPr*wX2y-j%jsFk9IaXb{jg$XUUQ=ubD9JeK=np$K>npsh5Sir7Xk zC(S=KN}tC^kJVfwmyUNBJiPd`U8_}`;63ZTugtj(K4fl-Tz|Nu_rN)ls7Gi`mSUg< z@;htK%=5?h14J=UjeqxgzTUMt>I*!nNq(C_^4>$B&BKW_Pbm%+R>v{qC` z$@M(^HYcs{X4P;T4Bw+LyxX6VF~x`i$h8B~hYf}ma60WbZ5EDp$~%E#2R?V-JfHP~ z{;EF)?yY?>r{A5)66~jlVB*!YoEvpQq8Srs%AGjP_-*w$;*V&8Y0!Qx{qBnU0zxZ3 zGvP}Bt&_Vq`hAbM@l62gVi8^D7l|H<42D#@HzVJ&&39*vR-T(*?ojG?4pb#|(KCPWY%YkHrV56nv z_@h%Yz57Gk78?`gEJvK(iu)aygCy2;(dw0|vP_+d*GM`5aK)$4|*s zV-r>_$rk7`zt#+@Jlv$Gq(sY!nlW%~b1l)iF z)B7QmHWW|hx3C%aw~tw9i9_XAxuY+1R&7u*6~7C3MFjs3b> za%nOA4n*z)m82DRyEsE`+xe>!YC6lrf zl_MLaU5-c1aE2RN3gWD67Kfye>1!xdvrTdN8{G`^woaS2#)cQxzbcg1JEuV#ujm2u zhx4KaJTg_MpRm;5%)vY{tNl8(RGDti<$uIbG&cWwb%Vgc{E``G8^`#O1n^Gm#oH`J z#k5@tX>WFVhPKiAEA438WE4HH{Ph#g=>ACxNqYo}*j`Ay5}k^Bi2XOHSQ*1bBsuE- z0~#vn!v|qQt!ZQUxpvO93!wrJW<8|tBW*zdr+DblSb}B4X3Y)6NK(zwLXz^Ga`rtt zZCXMJwi9^c6_ovT4&m~j0hHuj*cS2|;QyeMtgef?^8{ldx%5GL*cA{eI*5D8b-JT& zl9+fq%B!oTK*Nez1;P)3`QTq5$Bd_UP3ds&xgIV{sdfhg3-=}rpBt%76-#w6cs*&G zF%!vfe7`*hU+}wKw;8cK=UPjZPgtpTHF_UZ$?%+V_Z7T$;Zx&ACj^D>F-}0hVUYi7)homFfIPD8 zUBRG~;d`whB;ux8nHOV_@{@SMrY(MtKv$xL%%G}uRcCjW2Xnm)dRR!m@FYGqyQ-A1 zq;ao1VE4>KH(W6OcKqG#oZbl8?kAV$$59u(O}ONIN{tp>efa@zy`yC0P3nIm+RKyC zGG7MI_naf{jRd&~jM?eRmW*n9h)B427~kBIpnP_bJZ1pE_glAH8|Vw-czXAHj|sT4 z>f0Bd>-$z!hTM)m${!bLrdnuf7(cIfcqfO6?sXeG`O8mqXL~>TXYSW;W|n_k!<4&o zJMj5aA>~(y=P}OBXpMtVbahN9YLf1igh_SmkiC<{%`P@tA(y(!pLXpoX?-cJzUOK# z#)5pqJw3L5WlM*0N+Khs^+Xmk331H#U}^r8q<4`(MA=xTV^#JefGdj($-6hnLw05O z7q0yTaUT+K&|zj1GoQ=9KVWb8d@=*3%-8@okd z?YW`2;8;8J@&@8nS|!`Gl@4v~XSg&|>%L<+E`%!>3{@KyJ(!>&YossO=K+_47dy~k zlOzUiVc*p^GQ+LHD(*oA3C9!#<#U|LVeqjiHLqlcVXo|;oPD3Z@u5c-x*<q{W9OfxE-xA;xpp);DsD#~+4lGIK@@VNdb4*yMeF;uW#mI*8&azYhy5~}) z!+XCAk0-dpzjWC-ihcp-gQpbK{DUXuZSkiF4DvzNi8<7M*}X5JA+5|GRX`l=+>7rG z%}gvR*{YW&sns2)&zsWEe-`ch5&0?zf3Jl;A4PW@POUC4 zAay@Z3#*ewO9g-9voZ<&z#Y0Ya8FFKQMrzOJEkJdW&J>|V3&TZqdHcNB&-D;qnUl2 zy1W)O%Nah;b)1yKo{Ir5 zRMqemGSy%$=r$zu&q~Ot-=$yI?_ESlt9=Ke{KD9XrwVDcYT8A6FO%z3qT)gMO4`$T z8)TjhqaK&ze&%5;Sl^c1lh^Xg<5jmI)?bfoLiBU~GY+{RZYm)6o zNex{dQ8EuN60}T5P%?+~(%qd7KG8^6gI`4N_?AqAUDezh^3drwD@@RiM8y*_DuE`H z@J&_WP1H8|t^9X>5yu?e{Qhi?vzTd2=93=vHJ&IQ|Io+XM2LMs&Z^RNfY^tF%km;o?hGlC@i{F-N@ukMR=}Pu&k}39VexFf|R64=+|?xYpeGO5Md6C>oglKtr>Ta#hIfL8l zITuDQS2MMsLgg}ewM1W7$2M*mUBmhpUi({R2aJcqEuleK@ptUZRvc#d8!xmm8puAmdf& zWtHh-F%xzO?WxGp$#1hcD8*8jiS2Chq&9E%&i(*ohCJ<`sR*R*c}U*zS?Cd7$9k6g z4?g3j3i6sPCPvd&@B9eQQCvyL&-updI5SXL8*z=Fa=h~QIp#nS%|eNGT5SkRHsA&N z%)0nqW;14uNkwMXv}?o_*TgHYRS_NDAjHW_4D5ZaRQn(R=^*2gto6erbvEp9sd@IH zYbmqg=mwtONnm~x`jw?Y#Qo-->K%g!o<{!qW?>-^Io~*arfl8lykwr^+tDmc78X_t z$c^Um_TJTnWTW1OT@vGDVxV>f7rNJFJ94VuZ?hh^&)xxaH+Kmol`1TLU{7#*cVO3p zO8KuIIXZraD~vR#P-;T+Ud-|td}RaTg#@ih&AFn{Qd(k%kR$42?~^kPcpA5q~~5<4FBk$Aap;shL# zt0jct%igYAz%36y0*@E%&JWm(Fb;^pJbaekf$<4DDkV{R^B931oe)(3U{qm?BX*#? zml*UdUpb3=|Cj!XfF=Umf@h(;Axy2>(JjShzYNJpeG#89Sq8;s2<7Xy?`sCZt&YW> zJgwhSuHi#Dj?mx^nd-C!H3Q!@<#^_$2HGJC`>p;zg|vcjTO6>hT1VX3xB4{!j!#I- zH4dIE^9I&C*_OihWbV`c(B&V$OPF7)<^I|4^$`;P;!>Z!8F1k@5mu$wmOg{u2yOH2 z%{%IyUR8-)G`-xX4)Xyrm#w<`2|NPV38xyY9ly_Bf~hHk{eJ#toK7=;c6W00%vz(i zo_WGfszbA*cFV;(M*s<|8QuAQ{sb$g@!I2q)#0yHYH+ASIJkkGh$GLZCcx$@u06a1 zz^6)NP_Cj%ke*-EZBB_K4+U%H=-BNnc~3;u>hzSxJ{81R^6!F*MdMz+Co~1$%VfJ4 z;w9i>UehWlQ(r~(iCz9J*tW5^Cd$4)Ve~d|d`Cqe_ciM9w8~4X>Eba^x3sTkQP`V` z(`ec43zCG;kSfa=8Rr4DYMzi(0nIq3Em5Y`g8h_;#Fg5RF?}00&Tai1I_Es1n}NkU zPbM*u%3pA>8T#|uN5T<1zXIan4vp>dU}f)k8t7=g;xW$g`u9jLS<)XO;_1KP3c}`F z3aKr_TXq1HGAMjJ29SWHrvl_I6c@_tHlPWd3ZDMf@n{QR2$%?p zth2|!ylH7L90VeZpm%=aTx6!_nds+D(2k{Sq~|6~-7!Klr%xD?n)oazfp3MIQi-)6 z&kpjF4K`920jvENQzG&vn?@2pKWQKrVFZmzVfl~njObh3p))xb2f&u@8(jtc*nNXr zuI+(N?TTZ>zf$aC{l)2gL8ofhaH4REt{v{qT0b?xX4EFxOsH6=8ThI9e+=)d9>7ns6Rj6_@?MtNierfLxxo)-4SA0I&B zx*WkBlECi+6qu#RBUYzoczQR59v+B1)fdd`pzjs&c#mVK@x}xGsM?B43*InM_HFIB zbH?@U+F4K5F8drmMZP9ABiQ$t(A@}EzW!R2(Aqsms%()eKLvtjr`kixDtS0r!mPFnL9=$aEBZPw!TKm~&*yyLH%SG+(8mRw4^BA0*n z=Nk+Cleq4zNa7)K3C`TsyAm|qjE6b^($1-ONXdsEJn$HYPbHcV0i6#?vltjOj<_-K z=gu|Z3H6>)J|5zt8{&@{D3I`I7FeY z_f*L`bPN!9H7bi=wxRH~FnJU>#=?a1H8199Cr}LzYPCD9f#m7GH+G>sPT6?!ZGLry ziU2>SQiu(knE(^Am-|cH;F-F9de0BKzZk;u7mn_~kszy~6y=7hAmg!o9Cf^QAxUWc zxnotKaaLg}fTuS=*;8syyu02jr&JiZK&-6Qn#`U2dc0Nq!o>*K&zY(0Wxx5T83Zn3 ziocVOlML(y%WX?rF3x(r*Sx7`iYE3f`e%)~-lE+*0hhFOB59j$pj_}!y=&osaMiB- znIpHs`1lzFNBR}Za7j0qql#PL92B{@x82C2_g>v#zZGQ#xW#^NsP>5#|4M-5MyxBI zm95l5A$1C6nJDycrp?O``zW^)&jFxe5LO6qtLj5iWR3pVPp^dt{&u^-t(I_nA3)0R zlR#knT(|65^3B}M$#_|XwV-3sRrd$B6Cag0H()q--g~Nws=xSgAaWUuPEwk?XRBLc zU6cbd^84tGo;^peMAu!4uoD2G_N?9j3^{QA_juIyM~bnR!txVR8Z%YlU8&vLBhVuqle`qGTL_4M zN%qN}`ri`kt?3+@hzbq)8=owHOIhY{Ap~5xt%}Iu2$!_3-v@i@+$!NC7K9KuJbNkm z=HL6)JJ-y)taNl|48vQ6(~g(#hRV<>mdqnr>;eedZm!K|2rJ_Orc|Nh%0Tv159zB9 zd;x;pAy>GsemO7IzCt}OLW$?Bsd`MmM<(X!jJN`tE8qowNk%V|6NIKZe?u=vw5!zA z{bptcTBWb%w$RD(6>14afhgSCsth>L~WctZD9`C&=dTnOZZAm0Wj$kyLVM zgiYZJ!kCDG^lI`!V8alv^kjJ*J3fNOWdq0OlybcBIS^~P`4a3CYn-$I%m?MNg3G&! z9>W0utW#K1Psq#wZ+H4Eu`0}4|Mnh{>uOtX*!dl~t^mC=1xh2|(?y!^&*pt(_Wpq4 z;d{@R7@=IIDmpVm{z3jT^Q`5oTsB$+IQg64xMz>EjqHs&39MZlf?4-Xo|e4+CaCLQ zUO#U!MAmsZwq!-|D#N&G^g9y+%04to*#*PFL-1d=Qj&t|%vPNl_;Rd}={V&_aVWK_ z`Ukdhe|(2$b>r+b`J>e_wyX)H(&C4K$ocxSjl{q<#=B|cC#Bq^fh}xDC#=N?{prwr zn&-yII}6n|*=wWhd%&g8RK@G?pgkg|WbPMQzrQQ+z^pTxC^GevD|FStr|ez?XI{{(GR1lb0^?J;wo3La&SC>UZL+%kC*iHlqg^&44kMpgR(^*-Gm(t0E2OAf; zl#t7LfaPV#G-m2E5I!=xe7oy*|LYO(6uZ8tc6Tz8P2K|{4qH;;&RxyQWRQ5oi2 zQ4?8Igt6w~#T@L_=_(PHCd|Vh38l4(DwrO2Cg#eVCsJwanqV>uPzWJh+>$huJ4t1y(|L*rwy-J9_ zW!xGzMZ5W_1jCAe@{ehX=v7oB(8piXgCDiZOnl3G_0-27eD|wtaNxdp_ zRa!;kx1`syVlyPZx+;~_x}mMf|L!}sF|l1%d4XA;LUZd~(M?9`T(-yBcN{M+j$24; znf4#Wjj)L;G{5Hi#8XBNZS%DzfFAfJb3Nmmx)@V~TVBKM`1Gy8{1Qm|*;tG#>3r`| zGjx9)hLO^&R^0a#&V_?&br|MF5kaCm#XFEV8*nefI`C4{W>^+H;?GNJV9RMm_)wYY zD(A$1>elf&cE>+{6+Zn}f>)j;(%;vsVx&v6jT^k49|%lTwORikxK$dIdVWxkB$`>k z>=ZS_8*?iZ#!$>Gru@HSl{yXX0Ad~RJ^P$c?LA!>aQ*6CSyTClAQOYDy(a^wElgi6 zRjNNp&*Bb5+sFH z`F0GjuC9-2C9e4P zt@lOUT-k%fHPR|pu2+NfN4^j4!$i-6E8)d75pnhT6wLvA9y;SAwK!IPK(hjOl+^99 zP&AHPr80kZ2luV+Pki??#m*0gcRq-n7u1!YEH|=i;J9e9yE43@;<*H46zV^j3zLcY zHVQAK#jlsOG@nc>WKef^jh*G>1;kT%?*!M=90kDmlWb5Kjf`!*3=ms4l&jCg;;V74Kt zs}R6LX>ZA;%RrMUPv%h$A{=v9t;pprB$o?u4zN@Gd$^FFx4;G0GGkj zPh1VE_ghJlzf0!Ewn~Vxv4?ZIidYgABsdXkBw|xZp{VpJLBrFuS zb0RAj6Ak#7y;#jPo@1Wnt4Qk@e7I8dpiFT0xii7OcsEN4-@eg5CbiLoESmc|3y~I| z+(LCpam)nfdys*tsgOh@4enRW-L>nK$LIiRNY;)>PbdnGzDi@n0%GI64|I<}o{Tb7 z87A$z=cytPm4{H!r2Ap&lO&JFfTyT!WJGNPcRTqoFl8Im)_wo!kU(3f5MWLZVFR1o zy*FejBzqmCsd1k@d}?_=6zC=S0f(CV?%735wj0B`1y=3QHHm})%H&{1(O(4z7hr`sNw@Mx%rIs8< z2@0R~m|P5zHNXXZN`belCdOJH30qk8je0DC&>MsMS5854Moe(M`o0wfFZH)NtdAAu z+8$+a&LfUZhhv1bbd+**K#iBQZy$d9Vi}Xh1A_M6#KaC*Eg+ZBr~L4}+?S_>1-Y9C zz_x~UBq76_9qyN)+#zaqhG`3fqI!^23%B*cdP{@O1!QIC?z_Z65DOS}O7gf3>lF6R zSS==f`WBEK{2|+BNYN^J&mG>!Vr)*7 zf~oEcizZD4@rUx5hw6MeyoM&UXW&nP(Rnk^=L7%EPOHI5R2hvKF`Xzcp1Rd$`H%i> zfQ_y8EwsCbpt#fRY+woTV+pLE45Etp69rBJFYV?-%eTjqL@82}65pWfYLtNE| z^WZnjzT_6hR+F!J|5uV_KkcVk=%5n1i_V-NlJHV`>>78?`VZ7iR4~|m&XQH-!iHO3 zllC2mKQ(X?{S7TbY`Pwm4{bGh=I{z;tdPqiENo&ygBNpsdZ4rNXT`de1x04jM=D@R zbJMFzFOYIgGb1?lXh-4Q{z^llp`CO*l;cKtf1$RKc)<|M7y98T-VuQ7>(|$p3;s|< z@4-OFV^@mSD>EwmKVAeDe&-{;r9X)v35D{3^^*W)R?*2L@{|QYxpMbpzF3(UxImf; z`xeOfr4;0UW}JPDjLpHFXLkER)ivo*?yHf%eq_oA^~N;$w5O z7$m^Q5m=2w;b-+hBn7Hu;WF+co`P$CAs4p+x1}K*g=`f}AX|b}E(A$e-7#}?<6xX? z4;I7{^#osFpk;~0V5tKhVC*;UZoT|x5(XmnE1b`ixOHB@&Vz5axQ0pKZjF2|Ks<*_)(ZDO-wAhV&a~@@ zqAFvjyJ&-EFsEWJtHr)MjAZc2QihlPpxYb%&OB^Ewu(+#T3;*0jq#MgdJv8q4Qp+r z>_M_=C=lq)$Chk?-F4s5%FLIiYBPvlS1!A0C|HD~8oODF zbjvD6F#r3QRQ|HnAN{qfCr}|J`^ml0+jPo_R>v=-#^|kfs3zKJ2duR+;PG-i!u`Mo zOCIN&i4XaYB$eme4|wg4B4FC{FSHt(S*4sGn?bqu=S$O3aZ zg=R8p=B%0@wN(&Mq}}HNu+UKp2VS3H@oq#zc)y78%M{b=S(v=T`JNrd&XTATXN-Tl zCv1#&a38We+h6dUj-_Qk2TJ{jB;6wNDcv|s!dN2O!FC-%4f0-1$ln% z$zfYeN%V_gGHR^ViqH*sJ85YgYlv>lCl8Nv^#<_R+Fc9D< zq^TkimM`P5)esL<4+1egX!kx#o9_)qVB+o*gGdrXs{$u+NV;B>dEy+TOqiQepD_5l{P!eZ^2k{w57bX))xa_kW)zauXp|=f!|J09vLg2n-Ak) zx3A29U+&;tDXm7{GuoU{i&z?zBNNVrD%>tB106RXc3H@T@LKe~+ZmLlQWot#_kx#A zI-ZREJ+WcNFpmI_bI(fUd-z z9Po!+KY&4{Mh<-_bd5pc3Kx?^(dy)DT^1|}_8Bh+GXg2p%Bs{-A`EBpw0@hK? zcK%H;3CmEWa{m<&ou3SK91qLhyk(A|j>oSJ*=$)RmzRU}Y2|ksB5%Y79W%E5LM#ma zY*Tt|%)R<*>@hF+7~`>vqkk!>1~Wg8PYPenDhgCh8e~%L>rj?x3EmFxfWfDrjN^EY z08wnz72c9b!vU+?nyHL8F>X8jLTuT!%l9EL6!77KfW@>^{-{1U?lCgsG}lh-)9No` z$BF|lR>cPJpBf7EYYGWY2ZwqV$zRt@F}~}@Tg6|4i0l0FHHGjeKpsXZd2Csiv#p;N zhjgT6+i1+biWJ_H^cTS?*=2Xq@=$D#I-z(G^?T zXKo&lnq;?N-hb4XY}CIcsTDE_esdLzdV6tWbfz4_J1ga_tIT8XpX z3V=dP3o|>Izx4lKw#v>YOZ&7LQ%8-UVsE{XQf>q5(QDXE3%--|uk&H6YLXeT z4O;EgYm(yQD=pYjW`2dQ*EFFC=3j5WXK0PF41X$GqH%l%DDoSSU*=hVuO)s(F`acV zphZu0M9MBw_L=adVV@}De1r|svj|J!gzsw~^0zAC1%s*=2~HdbMTiKm@I5EZsejZHCUg+XfAJ#S?R@72 z%tM`NrB3t8InTmh*g7)HikHM6U1U*W-JlAGUp@BKBcmfme1x=IIj3hgo(;KAsQRf# zA+Ie6=j^ldp0-9hELDpu>6`~6ws+)gn(NN{+61XsIO3c z+ljc;96Zd4_dhQ^1r^z9LWMgi-t8$)mXuy2bY^JnG|SU=?gyEc+yCcI*7e8<0po4V zcY$$JC;wdV_QsbEh|ig^#B(N7;&Ai@58q(Rw}cKsbKb}ogMUH8qT^)rJl(Z;A9SEi z(xLP6mCRrE9n$B#IhQOWZXR$Tv1!cp2Ly$O>h@r7b37$NIk)q;WBd`~&SFjZ*&Qy^ zP!;yGTiptzHaR?k3r$x!cw;~DW_0jH6U+kQW5`PtzN_r{P^J3-tR=25yzv8sP(kjJ zpVD*QhhzH~)G(PGt@GsW=UodN7aNFzQ`hOX6E#;x%WwV+L0c-}wkN7`WgrkY=u=I5 z{!f>ljn@v95IxZwJ4|%~Ar(T2J4U(69Q-i{^EF8)x8wiXk;}=SB5NkJ7Jg$hrQIsu z{3~SIevR1S;bv8wYsUO~ftgqPrLkFzba7Hix`v%>Qg_h+IH6}-lTX*<;1+Z&a-8-x ze?H36$JId3G-IQiqhVD}^^cdL@13vg^&$OfZv1oOMn9s?Cx&)AbL#`z_h};Kc^Kuu z6O6y(qXP&VR5Cr`!a0qVrD#XHKYqSMR_(OAb{Dc9!=UzhaUlgyG8o>m?uSr*myyD^ zlGYoy#wFrRn%c|IZ>RI}Tot9YcC*u*p{0bMA}8^+U$81KWQ(+#A;t-@ikbiK%qkT2 zZT=3b>Cj@oi#a|V$D~?X&5HI!VZTljQ%=xsiig-VO~?d{`+ z_>{fbCyLL(fF)OYd_fw=aywcQcXii(R7^mQ=SeG?qsj6^0oZd;L}G9vibY?&Z;*WM zMoCv(w%`2cH@Em&P2)Gz?p6N;!p)#gl|;7F>8mh^LadKRx>FAa!fPKyZTpl+nVww? zZsEF5J+Pk4aisNET(0k4Fr7x=M}1M#LR0W=*vLf}o4T&}4`J0C*@qA+C$R~xjK=cE zV}k-qZR89Dwghsguq!CP!*+uXL@pfmC!x&rSsXbp=emXG+dlrJkB&XFY*eSk!o93# zYz;mLtos}0i*dU`UVvB^HlAn+O9`9q7q{WP{h{lOxa}5VG^w3hjl(#E)dym2-K==q z;kAz23-V0=hVqMg16;-X-Q{dz5iIouG%uUHkyHPv&kHba8uZrJLgL_O##Ng{xPn4J z%*06?5;U+h$5) z&0QfE%ixS}8V=Y8U=g4tvWkk+q!LTN8mM^pmjq@DRKpL3TQ$a_1K&2B_-h}KXSTrx zwDE@Ly7hqpntukluP*gN_0X)|>lxzVyEMcMN7KYI|ZIzQ4GB21e`Hk|n${UI% z>0E;Wj3kTHmR>rGYUwvMO`V6w2IP8sH1}m|-b5DX>O4qoYu`gb*6WunPnm=k|#;SI_@?%Kx54M7fQAXbVomc zAk=D_O5FplcA`zZS>e=bQU%*+1n`?IT41n;f=Ggz*-t^F>sH}-b-+VYyeCn7JP+#m zbtqq@Tv4Ybw7X|w%d*j8G>8n@ine<~J0|$_gFH0@ahpmKpP2c~Gs?T%7U6_?(adKG z5z8zmD8EnC8}WKh(Lbb0vHm5&xAwQI6e{Z&5SWq zuj*n=7_}J)CVItr&j4wCy#V@Y%;!eT^dV1%of#xooYXl$Mu! zNi9L@xzDvPL$;u{@HA*ZH1{v*fAB=d?vmpL48q*Bv5?f@4VPcV41?McU+HHBfX(0cJ2=Wk$eI7)51h@jz4Ug!=l^8afq zbY1+|pd*#=?P3e~Ud0ae`zcp^&)%*Fhx_L%2S6OZObY8W$7sp)+eUCe|P4p)|Xl$k)L zPJYOpM|3WTFzurPs>ZA(?xJtDE%BV8$Vq^hhAZzkT7fb(!`mAuC+fRCT_o3(tqpo( z^~TPtI>}Rn>V?g2?b^~c^1pqbn#omTywnMW{4h&HI4dG-DZCuQyLPz+Hn2mbny0wD zKked4IDTb#DrBl+Fc+)=a6j5HoB!nr{VZHs7IN(!&o||6!^-@`Evqn;LT9|}r=r;G5> zxeO{OT4N5T&B)?^_mcL0Fu&G%dtYGT1=lujoNN)4RA8&=UFelyzT%t}>2Nd`EEUUCz*P+~Lf) z2i#i7>iNs?VPb+@#Jf+9Bis<-OmpS`Yws)ns@%GE7u_x0DM&9;LQpydL?r|k-5@C~ zSfog&AV^6_E<&UPqyz~8X%GZKPy_*$&@ALf4b!X4t|Z*Zd|f7un%7v09j=c15?4gsYZqMl~{X z$SC+ce+-p_aS~Q1Mltr2lPuTD(zzQj>j&gd#6)gvcy@+A=TX+SC%4x5Q6*?G8=it@ z^nY+>zgJw3Q9k|E&oSXZ*l#UG8A{UAcFEeIsW_-9LrMQ$1@fKi!SjA^?_%z01F?@` zrCJ`C~#Ncs6q%KnA8QOEXxL`iC}wonlI5OBUhA+ z#oejje6}aIs5|%WOAjVXDqO2^`0MBPug@$~{I@-qmQ)y-T`2I8KcPNG`og#=6pFy5 zy{FqK$h&}}6>*NDu&_+}T4BMI5@fYIA@YeHLr8Vh7F(s)zHyBNXV3(wPdZozWe5Rl zqQ>sg?xI#~X>K&mf<)0B8UD`WlpS^IoygO~=};c^FPt4x9(Ypa_40_6Ga-_KeSt_6 zE06p-fi|bI8oww)`AyZ1Q?yrQnYmB6HipQWbC16d4`+NB2s&dch@LN(ubA={wwfx` zC7fsb^1vIvN=U)hO{i`?rcrIwGaXJ6gJw5AQnkmWYOsg;mMkhj2{1=I`PhJ^F0sp1 zzz1KMitbr|@(>`m^oEXUQq9+5E+oBK<40pX#ag#p*@u*fAKjv8%a;qM(H$U2B5xVt zw)aip99*{&FHh?!hTjG7WNV#62GU-;ub5&u_+bOVAh9NO9^uI^%uzMKMeB^obI@X+ zBdkk)>kv4+Zgt0h;toLX2kRzTM3=6lf*=CkR_v)b)8Q7Lhz-lZ4b^MTqo^V z#BQ-qwm>hyfbSsR@7rTI`RI6!&4=7F9uoSq225olRaFj{ihx5b{EMr*zvrPN*jv-Hf1((X=oJfs)sSfdz?N(dpe)f_smj$F)W)V(-2{w zHJ&e5T`jV(z!pg4j=WU()r0BgsG?m*#_OMW2BqD}3a*OeOJk1`cq8SLTwC~mJuW9O z(r$}6mc-noAE8UuMXqi4#~y~w_}p*Q{hKY^#@Y_mFE8i9Ukx=@*!(YN?w+M;rCJ!< zjp`D6Q*v>8-B9+9`Kbs&_ll^`UL8G`(a}%H-4RBeI;s1d4fMVf>t731&03`x~( z9t15%pVp2QOsynS)@Bgh%s>QU`(qTDiQjjarQWvBIx8{lUtzV$!NorPB{$=J`^UwA z#*~i-z$@wwGg!MG%p*e)z`u9|iRIJs2Sb-Bl*c@4wmXUh|^v;52d5Lm(G{AVYqDJIl4?hwzcb?G%aasg4T7$0@;^2{3T${gVX&AH=Z6GQ%fAo8>zj%`%^m0`H=%A3ID z4X1#Ekd#AE495_~*VqxzMLc-xvG>`YCoFEyR?~{UMzG^7lg81a7?6B=*~&+QBD=<5c9mG(mbCVR~#laC+NbVkCB^CqRdI z(1ue~&~bPYN`oDU0OGGQv4tH0v4XlExL!a`ikV>~pWhV+O^5+G8JcNv*)9<~J@Vi! z&?SP*Ro~2%3`cu7@_7zv0#Z!f#&JS+EyMN}rcspXPg6!_4trGi1=9z?b21CF*mch+ z;=GG`PdrY#5CS+jme)JmrUUTTjMI}_Vv$nH104v5*rfe{*DS82rxZUIomeaA;Ngd4 z9P|w+)>V^BgB$E%un}B!Bo)o^%JRE?mUiA$hT$#>b&4$ltJwdr8tVjC9qW~K?wEZh znI}pi!EX{(Bq0zmdPjy3xJiA8YuE*I{Mu_l0%NOcfOm!%pH0E_Wsy;TOyAQ4MS!mx*F<;-_-u$5BO_ z$t~7l>Eo?)n~*T9xH~?yzWIQ)o8cDDifM^AY^PuTt*4O$PB@dvYTYwtk4ppwPH&#w zq+G)-)fW?d1er3Id-M0CZRSZw&kdF&&i4d}z8<*XdvkpxKVoN+A#c`8Z7Of6F-q5I zyMv`oNc+m+*_E0dO*V$pBt>7B)O?ir#g_VB=ic+<-yGGHp_?9Gw*?syBi?z2#kjNG z?3hDr>i5*PV<=q$`J(!K$pfuLuS^fXF_6d6m7n2U%PZ&N-oc zM-CT*h^jAYC|Rgh`t7Q%Vy-(_{yapP{O4>e3VGV?n0$}cC>`w;l#y0qbX&|*b<7{c zSV?c4tr)HOaEb3%*%a0CDkFz|$CP!fWRQxvebNKLIK&aoVF;sQv=^ER<@)rt$xY;e zc!>)U+q-%#?B_Tl_(B&=A5(})SF_eP9!1s{R2OsZ!9K~6;L522 zq4K-i7zo`9$dDUm#N&qetW(Tr#+h%q@^sI6vIJ`-rRjIws0GgM6Zf_)tf3dp9SCuq z^m#{1T+L~#Cl$<>-b@uyKEJ$B{xLC0`X#qn>KnJq-`}>xzLjqYea0sd9W|3!ovfEQ zUJz6MhA%1T8|{OXtaC2h!q(m&`7X=~1B3r@m}f!!n1XC;Os}PLP&%7{Gff>8$=M$w ztG!p`?rUg&@7A%&tj=usKJZ#Yh&EJwMYW0?w0N!WrxqR6^*!Ldg3tk zWvXdsy9e}m3TDlGrH&OqyrG_^$$dVwsf$BO7wovpCSY+>Z{vrn0>k9$sC>Ub?ytfX zn*6}5yoLbKWV>^!YqrAJL9a-HSpD+e-Lg+H8mUNzGi0jgZq&I7J@YX5*On|ax-sF-%b4L*2kSmFshO@QqXJfq};_h z`jQKY5E>0}Am`MmLVt|8u@i7o*nU)};mJ$}1go@Uu2G)qe4Qib*|F(8&1hVriMcxp z*KBYHW+n$CqP%m;-kaMihO7+LlBrs`+1f>amvKe-GNSOJj~O6MriJB?%kzBY)($nUtMY{tpps@SbPe^ zV1f#V7VA0Z7&fTjT#48=P;IrJ8)LrMuk`Z_C?x*X3~VyhNdh+VxzV}#umhdzWGNRFEsm= zWT!f;+n>abZDIh~wW`0EsNtBtdlGIi%QutJJgj}x@XEI#B1LmFfPuV7&dJ=*hp138 zcWJ_%guKY(49!BeKeovvoeitdJjKjM`LU3x6--vRJ_>Nd^D&IQ!DDAd7nI@}mnh@3 zZWf0T6c*O(IN5Y6EOHcBA`m>pTB_%b%TM15_~3Oi3ti3*v5i5M$j73Q|E2=oL_MPh zIh$tmsWQkT4uBHR#&fvMmy@*Gkn_!%udJ}vc)J1cF#0 zSqY!&cNy=NrW;lL@$1BnU*r~8FB0Qa$4gZcdl!i?d(8dvT@#h zi-mXuoxbh|cc%~VX@7tYIpdW8X7!R+YcYP-jBAv2edJ^F!?44R-Pg7PybTU*7w@xR z;FUT>v#-`Lbr>M9cwAPL^b<)=^E)JynL|}2;b8z~5#d4#>g;V(HnF8gjQ;IVx;I|VoRC6y>vd7vLo-dJ9TW9ZT#6{+67sxMbx!>C30FW?|U=GPiZN7 zHeKVPooI!Wn_h4(@1D@TPYoj(; z(8kX@H5IT<-gozy5Eq^)t9u`4g=NGge<~R?1U%FNuzJ2yajOp^Y`Uy4``%kXhINMM zB45R$n*c{=)75E*;?fpp!wg(xwdEgsa3>(k7)z##7W3_LpC&m#K9&sRHg2j{Ncpz{ zl*CnOovPm*usx*7OGypb0tNU%7_9|3Pn3=NdGfTk2o4+-#LEq6qsi`%cH(`HyWJG% zcV7v|&N>_XTCVDJ)zjvWWy=R~jZfcyOVSaU7H6P-EplHHI5|=WSCg;};8TzrYfA)D ziA{G`@TK{2a1KZW;dQuQ*LQk!W%vT~&)@fgU&BRaNFK1yGsI&R+Sc3s6w1b*pGwqV zn8?MA=P;xlj$Oid-j9^GDXfMH-CP(8FN&LelV*~a1I5j2%n&)G$-%Mp3XvrYqq!;(EjD@$GuLs&Z0-w4c86pZWj}G<`Qbc9dA=zIZrckcT06X3(5C zsn24%;pL|Q;wJhgDmixZC2f#rc2VT)5nz(0NTeSKAcI?6-1n8eQ+W?7q#OJ&oz*OR zAF#78?~l_dC53>P+GPA#{H19vPz#}E6X%$eBH!cXupe)`sC42>$Y3_6bghsRO=iuM z1qF$5tsE8Y03PpAirrnFQ=yYqpuT z2m!x1e@a7h!bl`n!@+Beym|{CMZT%+g#@bn3=pfK7Yx>zypru!tL1o2SdK-nZ|1v9 z^0y-lDK(ETR3iG+Lm-EMAKJsy{KOp2D*I3#vg|SKqb=K4kK$^WN5W{vuW;D |UK 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 BI@|yN literal 0 HcmV?d00001 diff --git a/examples/images/neuroml.png b/examples/images/neuroml.png new file mode 100644 index 0000000000000000000000000000000000000000..297ee4e750d10232f9b9f1d8307be927d99bec08 GIT binary patch literal 3889 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 literal 0 HcmV?d00001 diff --git a/examples/images/sbml.png b/examples/images/sbml.png new file mode 100644 index 0000000000000000000000000000000000000000..1fed3ed0e3bfeecbead30d1dcbb066fdefdf059c GIT binary patch literal 18373 zcmV)8K*qm`P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L02(C#02(C$hv>K900007bV*G`2iXJ* z1td9hLT$qU03ZNKL_t(|+RVLaux!~?=l5G{?|sh6%zN(}YaXO3NvJe|Bs7(}p}_(< zgcuqHwB0z`gDf1#Zkr}-0)Od3QT|*6b2fQ1PBQMrKzekl_-^@ zD%Cu``Q4kD=bXLwTK;hI-g-5?C+SJxjM(wseJ^k3Il2FP4gd9DD}@(${QmF%zTb7% zU36?S-;Ru|32ma9AVo?}uI zM@WhmWgQif#A0o%(uzg}^MXmD5}`mF4e=4PZH!9QLRG^sf&>BquZGYOv?6&;kVKU^ zB}K;J6!T`J*ftk^)E?6rO-j`MJZ;;u%mE1zYLCZCB2hD#D4~lC{T_#6hYp2h6fWmc zq@`8Ea}Qoz2aUeDdLjQ0-;tkI9@A7 zl`$w)1wn~Gtc6f3k|Gpm9l?u}T#%$_El7Y;8>X|kMngdgkOfa2V+uri+=W`eCD6nO zp0PR-)fjY#lGg}ElR6?o9eOl1RF4Q32T2&#)S01@pw2LZL6fR^MMb1faP+@FaF{$%5y;Y z-uJ%Of8iH?fmgorm4px&hJmi@FfwM7*~J7aKD{}Wkicq019&aqj)s7XQVaq@4Fl05 zYLR`9JngPeZbFubC0treL305sSX4q4Tphw>3B{bKiE2=MdZEr7kJEv$AOs1RRir?Z z669UbZIFNuYjPBFlZZeX_wcZFaS+zP5RB{)Oi2l{3{ZP!1dzb!PBEv|hEN4GFj~eU zL{l!$9}mQC!Ez|HZG;L|Cj^QrMV2(W!1DAy8lgy~6eEU&*cx#q5urL*#v_oRSjOE+ zamLUWLI{L3x!2$c^+1tw(aVc5QcA(yad%>jh{%CUHZxR}oHH>-hGF2tAO0|B&Ya=r zfBxs?0EEB#tH0`RdefVD!yDdk;N$lZECpyHB?y*{R-NDuHE-NqLve5yqADvlzItYl z&z%~1c%{_cM2$`fBO*OJVdSQ*$g8h7%#DY)>AW!409MFuG()^yS?Y+ygs3vEmV}TXbW9&XWt{>|0tW&hxkm|jNMxY3a#AH@-@A>^^w#;ZE1or4PPA5zv(dXbT zcbzc)^x=W1BR~Dp#2c?T#1)M)vx=#6z8akeuu6^2BQoPJ9~}A1drxw5t;|w~l@*-~ ztUCw1#lGy>WoVa|Fmny9!fu^N#yqZSwkXal5Bz(%^95P`Cnk<=1ngUGbuEd!%p zDlvH)WFkVXmDy~@IF6ftpFC&RwG2b16hk%gOTY9#^UJ^d%hN%B|NB4SKk_3#!m(q= zn9pa_YSda$Ra6zP1rx8I^1t5tGJg2RZEAw0I}1^2y||@nJ%XPH zRnFLqrVGS;7ONw4$0obcFIlt=d0n8{rZ_YXp$-foTowqM2z^x$MQWxC#%k!1#UajT zIBJIanh+op8KtG~M~-bNN?gYgz-Gvt-x3);SfV{+77&) z$L->W$ZAGa2_aBxrD+;gtJRb^#fV72-8V^FI7nUqe&aWOgSWo*Ey%z5H~-du;qYo?ZpI z;L3UrpZVQi;_A9f0~8h%6)HyK7pItFaDMOievdouxP$AiyN;K?{N+6Q=%c**-S6i8 z?|(mUe)Fx|a?34KPO%^6ZUFh{qmS}efAuj`RS^+NnSAyE@8gcSBXI@GMAnA$UB+=f z`xyMU2$@2^E!!GeU5*6EryzofzTBc!n8Q@$p>P z5hIN-${{}V)X4iE8i~wkx(*KoNgMhn29m=p4}>@;$&6G2GmmV?mNqCJ3~QnB2AvmX zvZCV{A9`@k&;Hi!eCAAKw#pPQG*%(6aIFlv(AYUB=MEAvEr)qghI2j#MohO^%M8*f zG_LrqPu<5Nz)%*5C3ehd$}WRO@OoKGrQ1*~D>~bqvQAeHmBMd)el{g&F(9KtUZDz4 z_!V5EMy|(cDU`Z`cmx?b7A0fWvXWzn42YlPq+ZLlWk6X_yrD>@DMm88gui(3Dax=wRf(zu1Tmor9b{N^hd6h- z=f1Bz%w2bWg-`wMoqYOl?&R*f?&ZlR&XRjUWroD_bS8KT(CsqwHBaOP|HE(nHMcDf z(|F>HD4~U1gf2dfolGMqcCvpJOtuZ!UR?2g&Ti4?oOL|MXAu zhky8oy!XBD<>7}Prt2mzHjd-u{Yp9Twp&|U#Ql`|;G&;(VR?kE323T(*x7T8b%$#Y zEx2W;M(e&EV$`N69buip^{T@%;NAD!;m z_QOaAt@ea49SR}xwR;}oQ-6CW&?2$JJYY?rDpaj_QgjxH^9E9+NF^ecEHxLJZ0`o?7yE z51*!#LK7o-s6;9F=5zga7nX?_o=3cnh$>&W_X!@~HL&qHd>#8C)dj}ol3=>vIn#RD zmG9>UFSPdE@bTlvad&RM`DS)^ce&w)8weqA^UXJtb3P!gMdUdlj*7_BH@%4|%Lh%`=Owz#~td;It%i2()d(QYu?lx{0+UgoF4CcjCsCp@HA|_}#>AK`rCN1k?*R_S;)kgrqWoJ2)eO z-~Ytjgl2}wW+OM@lyB;82Y`H*3%>AmpKSs6f%o*!en~61U$v=bgcvuGiXS8%x}j}i zoVenBeC3=;DIHW-bpJ$Oc!3wP-(NTdpM4_Z3>?uGx(@1+zTIM>&ajxVwf6)+`=V{$ ze$x>wpJHf7WUHYcgf8^7j5H;qIU}WjP8F0vSB!47-4^XS^5u19yKV8UL|zXNI?M{Q z*^DoK@yq1VALgZYkSw!Pzod zx2_bd>(F9oR6g*TM|sM(Si4hch#m+VS9}n^Y)*2m6{^uR4PGj_IH6Jg;IoggH%{Ip zHVswMWhW+sO;&^0(l*MO9{%L6rwDF%t>8}6woij?v!BmSI=-no^*0H;XaCNAnKz|G z&iTON^?iSEIQAtPtnJYg;kbo`0YrLNlk7 zJsv!t_{Jko@S5w7)8_$|z-21A@TR4fOg4e_nung;>?|VN z^T75zu(c4j=7Gf`urteaTh8{*j9J$)KQ<@MN2>LZwrQi&lgZ5ye(#=B7z^+Kb)bq8 zd4`R zsNkD)I-pI5Yv$TedGjl8=J*U4N3=R-id&d!soq?gf)K^4QO&S%Ah@GJ2%;#C#)cE8 zP9iZNT}zCSm?E=T%WT##>l$YBhS{uTYu+)Rcgzh@QoEKQeV)m$);5GnyV8?!Zg6p^P$gvnQ?GDH4LJc&l5@| z<>`>`tyg^bw)@b$B(#Z98s7M_t2s1p2r*HrV_v3s6e!s)+ZA7KOna%diEB>r@HhQi|cmLgcG zpn_Cqu2U(f36A1aabnjZVG_}5Qs(oHZq_hgG|XlVi&`Q`3XXfg#a@u)-LIGai+}P`G*_xd8n4{` z$O#@fx!%;Z+i!aFUuaoW;^2%va&k3t`+a*TYdl1Z$dAAACJr?Y5-7-IY<%2MZTfA; zd4FGziAt)5#K7PKmS7>1xZvL1!daVBbxPqY6-h#4fzgE+GPho}O{iqgu zHyrV&DW4ZwVvd;LL7Ay?C`D#TXvJs%H{tM+9YRuQh1dkzS;yAArEP?H7iqgl*GAd~ znikqd>DoZoDqXwzzb??t0<$i$m`xTl;Di_nC?<{*x$V)t4IMLG({nP)XC-hy@rE0b zGC~NHqU4bH*cTqd$33$1O<97A##}B00lf0bub!jojF1AgC}`zJzW16*&2CHtdI(@6 zwKzfIrQh?yO!?aY?04d!>PGayKqV`*eWl>sb?zKNwh?iZuu%?A7E?r6G=ZBJ5ksjV z(4(w!#l;B`lmebFy$PF}E&}rq*$zS*E89U>1fh)qt0Tvb@36hSNkW>)Y?kO+p=%?v zZUeG5(l&uv8#gO4?;2)pqH6+OqqIqAn?RESn;OGZ7Tj=G_ncfa7A5M_8o1}HI_$6O zjwWuo@z~^3;(|tnKf3*4=573rxHHRBA3;cSe&?_51U6d+vAL9ajablUqarJ{BYir^bO;BauqbM9>byk>JciU^@o14veLeT^Q@s zN1}mAUSIMzeX3lAbzWzGc zJC6s&Rs)~B_bDDd=Sa?%Z6$&zeeF51jC}g5ClK#3PvDhzzV;esY82aCmm5|M4O5&d zR=*{yd#MBW-2n!<%LD=spY18OBuRV8I7wpQoo~3BrcoLl z2(n;s;UjmQCduV*qDrN@@QE)!&bsudw2&3;3jgdaH&g4x8o*RC#RZiX7eUmXLsmcM zex66f@T~y%JK~P3f}_l9pp?qc1?n&|FNW!q9877wPK9x;cphmbP&J@+oCxTW3GM`+ zy1inUG1NuDU_7*S=@N-LCX2;~JW+y9>)T4^xr zaTAgVlr`2gtV5(|q{)HML9i7{gXC>W7rCEPB!lxQ5Ky*DB$`oUVPr+Mo*+VX1q5|S zHfhw7%RzTn@{yqnob0y|R)nswFz8|;Cg@^A19Vck<>o86;;7IyBSAoQh9n5lh_Mh` z2wjAfa1jD9BavL#Y7$}@6GdhnkL{gdm^0ZZ%Zi>#R|<$QdP2mhjHphDqM042WG;*o_o*kf zN$61Pq@FQ%Dl&2N_3t4r6i_iIX=K>c4pkeh6pkc88R=(<(KEdaxEPHFLI&GYPLw&_ zsGMC0_iy){q0kHq=H(dT2`@_m8N5&!*`w#Y^z7-tkP0~(RTIl0;|{Gx0+Xc@nLLH6 zpyK2_&~*(jzTpOr9z6_FXi_++K?a=?0Wqh=*-6ULR=H-Ua>J3poHfViE!!ayRvACn z6PJ}#TC^?^?T9|${Xpxm?iF;nbnR4}Iyyi2+LsVS0VBjY>I0wn(qp*SNeWj5b!93$ zPo=FSE!Aw|9Ai9?VELw2UCkJ)ap%1!IJxSn60tlW-t&&v-TX|w-wU|aZ-)O;?Tzo7;aM!>Wk(rYD)%TvRqLRpH5QJL@5m)Ke=R%QTXf&YJQ3MqMDYz9<3Is9SGCSKduDS9EhqvaW zu;23z;eZ+Hnu7n6#$u12C({^Sc!O@h?iJ6GBwl;E~C*foY zoX*Pm%?J%e&<$ncUMDMaK{vnGOwB#>E^+MW4zn&2bpr+2?+z3eF>-g}d*+pA~{HI(9u{Hc~@_g-Sm*{MaixQf+WDZtpo= zO(FP;yH2sDMJY64M$m-GfF{LD#;P7@|Lg*p*o9)$AsZk5{6mmhFvDWQn_s-ccFfZh ziRS~?w;6}NkTv|S$Ase3SP4WhD>#anuqBn(U3UnziGr~V5kZ2I>qrn~Mc@yfH14Z& z2wPKwb?g|!oUuk)>sWV6A1b3&WMd79k@HeGQ3p;9&L9zFqZVX+SLOdYG-6n-K8gU7XTZcJ~a{t*CfAht=S(?(Dk=>>V56wssMN-4j zlzH7Xk>~?O3R-6`xOoS$6^hjnsfD+_?rNkMUU%`(aAH^Z*ykT)$dz#{jN^#e)R&<$ zS-gsqH}^--NsMdtV147^bKHMsO|VJ0YJJa7zv(6HmW+sg%jLFbSAOJqR8-%JJFZ;b z0*K}X3xbS-*NP@%%!XOvAKh?-Pe1ex&UY;{Rbr`V45Z=&HMTVJwRM08&(dEt=hchD zl=XQmI)~?pcxIQf-JlNQ0@6_>u~(Ioqi}A>EX9c%)pDUygiX*sZBUg+^-8s=ilH01 z(B$5QBsy6LGb*sE6^c-Yk<(P3>IY7q-DS5gc(cWNooQpsYPqJVm0m%`=+7?snO9!R zRoh#X;T)kkitm9p{d4Ho!6_Lshyy|*7`z!;MS`#ws_>)gR$h*G(#rUXX zD>NEd_C}MGp=5$SU48KjdPDXlWPkC+CkV{~+gqaZhSrz-Z8ppVH^Z4gn9W&@aHft(F6VIs3VQe&?q$Pv;5PS-@spe?g7*$Vs~(% zN?=bNRiQCqF?3{axUFXHSdCm6mBUR-^}>-NSf~_rsu~)g4Goh&6^o1fow*$ zDMp-V17*x3rhyO}3KMk@bgJ-(Ob14=Nny-1tjWEhI>;F4WKO>t5Z8lQIeTMI0a{v? z#reKQt3S(eRQzZwZeAlL->I`x>i34K_wH4dz0PVm$G<;F!n9CO#9GE^1PW%3=?*%aLSo<}Uk%gLt<*A_5zCsl zzu_ot9a)z(qi#c0Di!sBQ*bfVg=`so8i`kJ5<-~E!QWjb&cc`mQxybb;xelVz8N@G z{o?C8LeX5Zb~$8Ta~OX76<4C;$WFh5SI1aVG&A>()PoE?&d8XYl{%xhNZlcuPr}t= ztc%f?N}mgZJ2eE>V`W{8bt&}b4DO5ur8=cLV=k=6O!cXgZY+g<947@%P;zB6lrvC0 z0uj*$Bu)c8MR2cF%hTkrXZ75gM*hi7SJD&ldX}UO$)`~n66KOrh9m{oN(_RA#MUhG zrk5XQFqycoI`Ek9#0RR$nbM|`Bv5=!Btciw}@l$d!{Uj3p& zTzTj)8UrD9SQ##v?S8BBtbY&{&84y+i3?JHp0^!x{@ZJ|>Dv`~h-}A040XlWCq_?{ z6ehi48R>>hG|(6*D7_177uK$l?lrV%j&GFv#v(ba)W3#f!{HHmJW;Rs=42ml^cas!LwWo|D(F55OlYHq- zE+e`0h&fqc(I`Lh{a4}}CIPM7{pcQFe|#6Kj#>sP<_Wcmcp+9NGgXnK_IT(xy;r#H zp2w#_BC!FF{HJfchD2q^12sn)t(R`)d;xXLcTBusT-IW$J#o;b^^A+&WNc-O*kZxF zp6C0o9l7D~aeni$Gdz0c9Eax}V>^uiPQ^F$i9k$LF>>xvi8w~@PBEbsA<1OQ`%*8M z{=h`$I%Icpb&5eY5N$A=TryTCc3n49!)hI-7$*3X1c?wLQ}3}EY|2t01Vkn}%H1hG z4V+Y;=%X|)rvX93KoeRNrTReN;=<5?lg$Y}C11%4Kk)J+paCzA;8?rGNA7rnTi(&p zg&EaGL?i0coJUNEAz(4!!;&yt@R|G0v0D;^$#qJ<5uKM%Gao!kk_@N}UE= z&Z`lbI@Ss>RAV((*4bDUW4BgT6_&->EzVwXmep8S7+lDvRG)ykU&o9wq7tZOQjC~q zG&ljL=7BQy1n~{gJz%3zvQcx{R3tW*(7-ChT=-gP33<(sXT${Snu(qc1kPf=7c64@N39`syPMw+4wVu)AphCjITF^J6+ zdwS)iSI&6lHQT664fkSBuH^+m_(E--&!^?&BG37IDcfDlWV<`{cHQSibXJ=K6RffIMS!Ua~ zU#Bn5SZ#8_JUvaoH>JO+bYvT{}JmT3OJ_f;tLiZD@(8bVO-@Xoql7wH=h$?dTspUX?KIRpk?^(hw%1)lV+9GyuwD~`QE8t^oL@wi_07zq zc|IWieaQRcGl|KKF#m#6Qs+H?e0SkPr$(;XQC`0=Zauo7I}&MQ%iCn&&0WK(u;7ub zd}Fug(bX<1i--s2qO5CWbVXy@j2)id%N4BJff~3T4A21G%t0xoAOax<#Ed)^RHoO- zL*~>|OCGrYVIF<-an77R4?~6^wA%}=yz)3#UUv;mmuO}UL0eQusAZZUV3UC2bKf|@ zM;^P1A3YLi#)ctgijJh(GLoOOAtG{MM2adZhL=h-rI*Z##5-SiHNW}S4^PxojbzLG z*&R>tk{`Np>O`~k6w8X!5UFZ}TJR9~^w&>t`ur3->gFK)z^jfi^iH!7qBS(JvS%G) z`J3MCOYwxrKaA8XK5rax!RdJ5@k4y@(T7;guVp+j@c8-2-<-?5YjA$>(28@j1>%(} zY|S-NdCQTAU$xB{;Hh=br-mb(TgAeif&wY`T506vq6&hfHhDeCQaDL$7^Cx-u!G}3|?bY0H<2B3=%@3^Mej`@) zgoYXa$HzX;5C79w5bX#lIBPr0q8x@x^siScL6t%#dB9>N%bYjga-3PGoL?=GbTwip z_{eAO1*Bs0=7K%oS0=8xW+|&J2jSt2D(XbD=N77bPb!YToBvg z^ofDfC%?{%UUEH0uQ*K9$WYd}8R9*w((zc%yzk42|K-I*_3EiP=vKknmska zw2~b1W_oIsi6Y80v|~Z_9DB`m{Om0Ye*H@c{5)>T-H#Q{t(7!i(xo}J?!jAJ&QolT z`GQ3p`0$+%A)P|Mg?GYFz3MQ>ImfC*%#V`d3HHQkvg2w8W8A`KVwi+Lu}To0Q}vjP zj1xq0@+NUhQ_Wka8vbM?0#z$&muefmTo5W+d729op9tV$MwP&m%go~^&(aq|W6Nr+ zlqzV5)b*0{=gwU+1s@jBkH#Ah9pzu#c!amk3x~%{7!+-F`c929b=(CMtC_(@vckyz zm$f)*k$!1>@OS@^uYBd}5CSv}bO~t_)<(REcnWw5(^pfZFmZh`AWg#~k3Yq7HPDZS z5GLV@i!%HD6fe}I? z4!c)xL$ip;s>gO&jU(c-Y4GSb znUq{uH{Sm~nSfm+!-+-hIadrRf}2uoa>rDgR5PlKE{r8_ zCL*-_<)7Wgxzj5o&PZEZSPIZ3yp2ekpiAhiLFWmbC&IizXG&U3m5Z88&agBE0&Nhs$-M4m+l&^7x}XayJhrH{7~@5=<7ZEL zunUwc2V*|u1+<5MpFJk#lMLR`gP6>ihF^Z=#0X(h!PGKNb66^jdCm2U!1c#=E}4Qa z#)!7cUT2&RMiY8&-O0S=$o|ko#WgUvQ_4Prx%tq_rs83NJ8%Cx9(&*^+Sw62w&b9& zHA7NF+lDl6=oTHbtr_#JIg9NDTRU6K4|Oz)hIVUCcjOSu^~m0;-%vh+Xr!*!R0*tf z#wQ*;J@J1IV=b5NG4V>R1<{I$(~lXUL2_Xy_59##ub_BC3W=(fzrE*C9zPG3drXXM zj%z~eh~j+gOHXWw^M{+<}K%z zXE;_yUbB6SNAilhmO^g2$@QAk%2c;BD+CWz70#SE%a^|RHQJpk7=}t%%&0P=O+&Y7 zVrts`W+r*YB(36^oQpG#>?-g3Z(aA^@9mtq*dBZV zyI_pD8gol=ND9P2L(�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 - diff --git a/examples/sbml/SBML.rst b/examples/sbml/SBML.rst index 5d446b71..083cfc0f 100644 --- a/examples/sbml/SBML.rst +++ b/examples/sbml/SBML.rst @@ -882,4 +882,3 @@ Allowed field Data Type Description **xmlns** str str fixed "http://www.w3.org/1999/xhtml" **content** str str valid XHTML =============== =========== ======================================== - diff --git a/examples/sbml/SBML.specification.json b/examples/sbml/SBML.specification.json index 459a2c71..4380131c 100644 --- a/examples/sbml/SBML.specification.json +++ b/examples/sbml/SBML.specification.json @@ -1003,4 +1003,4 @@ } } } -} \ No newline at end of file +} From c3000df1f4bbd1aae43090412afc386a1311342c Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 8 Nov 2023 16:17:59 +0000 Subject: [PATCH 22/32] Regenerated --- docs/sphinx/source/api/Contributors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md index 51d4661e..348797e3 100644 --- a/docs/sphinx/source/api/Contributors.md +++ b/docs/sphinx/source/api/Contributors.md @@ -3,7 +3,7 @@ # Modelspec contributors This page list names and Github profiles of contributors to Modelspec, listed in no particular order. -This page is generated periodically, most recently on 2023-09-19. +This page is generated periodically, most recently on 2023-11-08. - Padraig Gleeson ([@pgleeson](https://github.com/pgleeson)) - Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin)) From 5f6b50dbe927d03eebc5012aca7b9d1b5e7e20c8 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 8 Nov 2023 16:46:48 +0000 Subject: [PATCH 23/32] Add isbn to doc --- docs/sphinx/source/api/examples/document.bson | Bin 222 -> 232 bytes docs/sphinx/source/api/examples/document.json | 1 + docs/sphinx/source/api/examples/document.py | 2 +- docs/sphinx/source/api/examples/document.xml | 2 +- docs/sphinx/source/api/examples/document.yaml | 1 + examples/document.bson | Bin 222 -> 232 bytes examples/document.json | 1 + examples/document.py | 2 +- examples/document.xml | 2 +- examples/document.yaml | 1 + 10 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/sphinx/source/api/examples/document.bson b/docs/sphinx/source/api/examples/document.bson index 61eb48f7410792fb18b01ad108a1764219f2bd56..577bfeed44bab2a72fb0578c06f5ced25c7f882c 100644 GIT binary patch delta 34 qcmcb|_=1u51p@;Evu~wSettH??TNe!oC2P~PJRs43=9)>HUR*%hY8vM delta 24 gcmaFCc#o0y9s>gdvu~wSettH?#fiKM6U{dQ0Aa)k5C8xG diff --git a/docs/sphinx/source/api/examples/document.json b/docs/sphinx/source/api/examples/document.json index 5e61879e..f94e651c 100644 --- a/docs/sphinx/source/api/examples/document.json +++ b/docs/sphinx/source/api/examples/document.json @@ -1,6 +1,7 @@ { "MyBook": { "title": "My life in Python", + "ISBN": 123, "sections": { "Abstract": { "paragraphs": [ diff --git a/docs/sphinx/source/api/examples/document.py b/docs/sphinx/source/api/examples/document.py index c57be063..578099c7 100644 --- a/docs/sphinx/source/api/examples/document.py +++ b/docs/sphinx/source/api/examples/document.py @@ -52,7 +52,7 @@ class Document(Base): sections: List[Section] = field(factory=list) -doc = Document(id="MyBook") +doc = Document(id="MyBook", ISBN=123) doc.title = "My life in Python" a = Section(id="Abstract") diff --git a/docs/sphinx/source/api/examples/document.xml b/docs/sphinx/source/api/examples/document.xml index 164039f1..a7f9fedc 100644 --- a/docs/sphinx/source/api/examples/document.xml +++ b/docs/sphinx/source/api/examples/document.xml @@ -1,5 +1,5 @@ - +

diff --git a/docs/sphinx/source/api/examples/document.yaml b/docs/sphinx/source/api/examples/document.yaml index 54e03ea7..a5a8df3f 100644 --- a/docs/sphinx/source/api/examples/document.yaml +++ b/docs/sphinx/source/api/examples/document.yaml @@ -1,5 +1,6 @@ MyBook: title: My life in Python + ISBN: 123 sections: Abstract: paragraphs: diff --git a/examples/document.bson b/examples/document.bson index 61eb48f7410792fb18b01ad108a1764219f2bd56..577bfeed44bab2a72fb0578c06f5ced25c7f882c 100644 GIT binary patch delta 34 qcmcb|_=1u51p@;Evu~wSettH??TNe!oC2P~PJRs43=9)>HUR*%hY8vM delta 24 gcmaFCc#o0y9s>gdvu~wSettH?#fiKM6U{dQ0Aa)k5C8xG diff --git a/examples/document.json b/examples/document.json index 5e61879e..f94e651c 100644 --- a/examples/document.json +++ b/examples/document.json @@ -1,6 +1,7 @@ { "MyBook": { "title": "My life in Python", + "ISBN": 123, "sections": { "Abstract": { "paragraphs": [ diff --git a/examples/document.py b/examples/document.py index c57be063..578099c7 100644 --- a/examples/document.py +++ b/examples/document.py @@ -52,7 +52,7 @@ class Document(Base): sections: List[Section] = field(factory=list) -doc = Document(id="MyBook") +doc = Document(id="MyBook", ISBN=123) doc.title = "My life in Python" a = Section(id="Abstract") diff --git a/examples/document.xml b/examples/document.xml index 164039f1..a7f9fedc 100644 --- a/examples/document.xml +++ b/examples/document.xml @@ -1,5 +1,5 @@ - +
diff --git a/examples/document.yaml b/examples/document.yaml index 54e03ea7..a5a8df3f 100644 --- a/examples/document.yaml +++ b/examples/document.yaml @@ -1,5 +1,6 @@ MyBook: title: My life in Python + ISBN: 123 sections: Abstract: paragraphs: From 309eecb2df696f1aa731ca33993f93e74b211805 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 8 Nov 2023 17:03:16 +0000 Subject: [PATCH 24/32] Add pandas requirement for docs --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index cc44eee2..34665b58 100644 --- a/setup.cfg +++ b/setup.cfg @@ -68,6 +68,7 @@ test = typing_extensions; python_version<'3.8' docs = + pandas requests Sphinx recommonmark>=0.5.0 From 5b96714320c788b8e06b63e59501a304e0bc5f3a Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 22 Nov 2023 16:11:18 +0000 Subject: [PATCH 25/32] Tweak --- docs/sphinx/source/api/Contributors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md index 348797e3..f6b2757b 100644 --- a/docs/sphinx/source/api/Contributors.md +++ b/docs/sphinx/source/api/Contributors.md @@ -3,7 +3,7 @@ # Modelspec contributors This page list names and Github profiles of contributors to Modelspec, listed in no particular order. -This page is generated periodically, most recently on 2023-11-08. +This page is generated periodically, most recently on 2023-11-22. - Padraig Gleeson ([@pgleeson](https://github.com/pgleeson)) - Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin)) From dbb32226b3b7452dadeaf79ae1a31deee7a6f186 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 22 Nov 2023 17:06:45 +0000 Subject: [PATCH 26/32] Pin cattrs to test if that's the issue... --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 34665b58..cc57b630 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ install_requires = numpy tabulate attrs - cattrs + cattrs==23.1.2 # dependency needs to be removed... docstring-parser typing_extensions;python_version<'3.8' typing_compat;python_version<'3.8' From b8b1f662ce8c6c4b241fe40a18f227656c4b8e9a Mon Sep 17 00:00:00 2001 From: Padraig Gleeson Date: Thu, 23 Nov 2023 11:01:23 +0000 Subject: [PATCH 27/32] Update setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index cc57b630..a54b863c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,7 +38,7 @@ install_requires = numpy tabulate attrs - cattrs==23.1.2 # dependency needs to be removed... + cattrs<23.2.1 # Issue with JSON serialisation from this version, see https://github.com/ModECI/modelspec/issues/66 docstring-parser typing_extensions;python_version<'3.8' typing_compat;python_version<'3.8' From 50c54c5c43ba67615af7cbe6620852d823b1adc5 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Dec 2023 17:17:03 +0000 Subject: [PATCH 28/32] Add 3.11 as supported env --- docs/sphinx/source/api/Contributors.md | 2 +- setup.cfg | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md index f6b2757b..a43d6881 100644 --- a/docs/sphinx/source/api/Contributors.md +++ b/docs/sphinx/source/api/Contributors.md @@ -3,7 +3,7 @@ # Modelspec contributors This page list names and Github profiles of contributors to Modelspec, listed in no particular order. -This page is generated periodically, most recently on 2023-11-22. +This page is generated periodically, most recently on 2023-12-13. - Padraig Gleeson ([@pgleeson](https://github.com/pgleeson)) - Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin)) diff --git a/setup.cfg b/setup.cfg index a54b863c..c43a6e39 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Topic :: Scientific/Engineering Topic :: Software Development Typing :: Typed From 6216251aa1a831359e5d61f17a82d861c2e9007f Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Dec 2023 17:34:10 +0000 Subject: [PATCH 29/32] Test unpin cattrs --- .github/workflows/ci.yml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93f171f2..e9e5f119 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11"] runs-on: [ubuntu-latest, macos-latest, windows-latest] steps: diff --git a/setup.cfg b/setup.cfg index c43a6e39..fadb3e7a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,7 +39,7 @@ install_requires = numpy tabulate attrs - cattrs<23.2.1 # Issue with JSON serialisation from this version, see https://github.com/ModECI/modelspec/issues/66 + cattrs docstring-parser typing_extensions;python_version<'3.8' typing_compat;python_version<'3.8' From 8e9339475e3b04e4fbd0a9d3cb63331d8a87c963 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Dec 2023 17:46:39 +0000 Subject: [PATCH 30/32] More specific version... --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index fadb3e7a..c732f09c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,7 +39,7 @@ install_requires = numpy tabulate attrs - cattrs + cattrs>=23.2.3 # Issue with JSON serialisation in some versions, see https://github.com/ModECI/modelspec/issues/66 docstring-parser typing_extensions;python_version<'3.8' typing_compat;python_version<'3.8' From 1f6db923e35b1b82db6d6360444efcf61b2188b6 Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Dec 2023 17:57:01 +0000 Subject: [PATCH 31/32] Remove 3.7 support... --- .github/workflows/ci.yml | 2 +- setup.cfg | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9e5f119..a8f80fa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: [ "3.8", "3.9", "3.10", "3.11"] runs-on: [ubuntu-latest, macos-latest, windows-latest] steps: diff --git a/setup.cfg b/setup.cfg index c732f09c..cd33b37f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,7 +22,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 From 386fa236fe8bf5ae72b4b236b2d7408d46757fed Mon Sep 17 00:00:00 2001 From: pgleeson Date: Wed, 13 Dec 2023 17:58:40 +0000 Subject: [PATCH 32/32] h5py install not required --- .github/workflows/ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8f80fa6..44058c1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,13 +35,6 @@ jobs: python-version: ${{ matrix.python-version }} - - name: Install h5py - if: ${{ matrix.python-version == '3.11' }} - run: | - #if [[ ${{ matrix.runs-on }} == *"macos"* ]]; then brew install graphviz ; fi - #if [[ ${{ matrix.runs-on }} == *"ubuntu"* ]]; then sudo apt-get install libhdf5-serial-dev liblzo2-dev -y; pip3 install --no-binary=h5py h5py ; fi - pip list - - name: Install package run: | python -m pip install --upgrade pip