diff options
Diffstat (limited to 'chapter25')
370 files changed, 46926 insertions, 0 deletions
diff --git a/chapter25/.gitattributes b/chapter25/.gitattributes new file mode 100644 index 00000000..6313b56c --- /dev/null +++ b/chapter25/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/chapter25/Makefile b/chapter25/Makefile new file mode 100644 index 00000000..7497f98f --- /dev/null +++ b/chapter25/Makefile @@ -0,0 +1,232 @@ +# +# Makefile expects to run at the project root, and not nested inside anywhere +# +# cd $DESK/Simple/chapterXX; make +# +# +# first time only - this is the only thing that hits the Interwebs +# +# make lib +# +# any time you want to nuke everything from space: +# +# make clean tags +# +# run the tests +# +# make tests + +####################################################### +# +# Boilerplate because make syntax isn't the best +# + +# Use bash for recipes +SHELL := /bin/bash + +# Remove target on error (no broken leftover files) +.DELETE_ON_ERROR: + +# Keep partial builds but not partial recipes; keeping them speeds up the next `make` +.NOTINTERMEDIATE: + +# for printing variable values +# usage: make print-VARIABLE +# > VARIABLE = value_of_variable +print-% : ; @echo $* = $($*) + +# literal space +space := $() $() + +# Decide OS-specific questions +# jar-file seperator +ifeq ($(OS),Windows_NT) + SEP = ; +else + SEP = : +endif +# Find a reasonable ctags. +CTAGS = $(shell which ctags) +# Hack for MacOS: /usr/bin/ctags is unfriendly, so look for ctags from brew +ifeq ($(UNAME),Darwin) + CTAGS = $(shell brew list ctags 2> /dev/null | grep bin/ctags) +endif + +# Fun Args to javac. Mostly limit to java8 source definitions, and fairly +# aggressive lint warnings. +JAVAC_ARGS = -g + +# Source code +SIMPLE := com/seaofnodes/simple +SRC := src/main/java +TST := src/test/java +CLZDIR:= build/classes +main_javas := $(wildcard $(SRC)/$(SIMPLE)/*java $(SRC)/$(SIMPLE)/*/*java $(SRC)/$(SIMPLE)/node/cpus/*/*java) +test_javas := $(wildcard $(TST)/$(SIMPLE)/*java $(TST)/$(SIMPLE)/*/*java) +main_classes := $(patsubst $(SRC)/%java,$(CLZDIR)/main/%class,$(main_javas)) +test_classes := $(patsubst $(TST)/%java,$(CLZDIR)/test/%class,$(test_javas)) +# These tests do not rely on the stdlib +test_alone := com.seaofnodes.simple.BrainFuckTest com.seaofnodes.simple.MergeSortTest com.seaofnodes.simple.TypeTest +test_raw_nums0 := 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 +test_raw_nums1 := 22 23 24 +# These tests DO rely on the stdlib +test_raw_sys := 25 + +# Convert chapter numbers to test files +test_raw0 := $(patsubst %,com.seaofnodes.simple.Chapter%Test,$(test_raw_nums0)) +test_raw1 := $(patsubst %,com.seaofnodes.simple.Chapter%Test,$(test_raw_nums1)) +test_sys := $(patsubst %,com.seaofnodes.simple.Chapter%Test,$(test_raw_sys) ) +# All the 'src/test/java' files ending in 'Test' +test_cp0 := $(patsubst $(TST)/$(SIMPLE)/%.java,com.seaofnodes.simple.%,$(wildcard $(TST)/$(SIMPLE)/*Test.java)) +# Minus raw0, raw1, alone tests +test_cp1 := $(filter-out $(test_raw0) ,$(test_cp0)) +test_cp2 := $(filter-out $(test_raw1) ,$(test_cp1)) +test_cp3 := $(filter-out $(test_alone),$(test_cp2)) +test_cp4 := $(filter-out com.seaofnodes.simple.Chapter25Test,$(test_cp3)) +test_cp := $(filter-out $(test_sys) ,$(test_cp4)) +# All the classes to compile +classes = $(main_classes) $(test_classes) +# All the libraries +libs = $(wildcard lib/*jar) +jars = $(subst $(space),$(SEP),$(libs)) + + +default_targets := TAGS release tests +# Optionally add ctags to the default target if a reasonable one was found. +ifneq ($(CTAGS),) +default_targets += tags +endif + +default: $(default_targets) + +####################################################### +# Compile Java as a whole source set. javac tracks Java dependencies +# internally, so compiling only the directly out-of-date .java files can leave +# stale dependent .class files behind. +$(CLZDIR)/main/.mtag: $(main_javas) + @[ -d $(CLZDIR)/main ] || mkdir -p $(CLZDIR)/main + @# This crap is really just "javac", but with a beautiful error message. + @# The very very long list of java files is suppressed and counted. + @# The required output "Note: blah blah deprecated blah" is suppressed. + $(file > .argsM.txt, $(main_javas)) + @echo "compiling main " `wc -w < .argsM.txt` " files" + @if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main @.argsM.txt >& .out.txt ; then \ + cat .out.txt ; \ + exit 1; \ + fi + @rm -rf .out.txt + @touch $(CLZDIR)/main/.mtag + @rm -f .argsM.txt + + +####################################################### + +$(CLZDIR)/test/.ttag: $(test_javas) $(CLZDIR)/main/.mtag + @[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test + $(file > .argsT.txt, $(test_javas)) + @echo "compiling test " `wc -w < .argsT.txt` " files" + @if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test @.argsT.txt >& .out.txt ; then \ + cat .out.txt ; \ + exit 1; \ + fi + @rm -rf .out.txt + @touch $(CLZDIR)/test/.ttag + @rm -f .argsT.txt + +####################################################### + +# Default Simple .o target is build/objs which is where all the test .o's get +# dumped. The default sys search needs to NOT include all the dumped test +# files; move it down into lib/ +OBJS:= build/objs +SYS_SMPS:= $(wildcard src/main/smp/*.smp src/main/smp/*/*.smp) +CPU_PORT:= x86_64_v2 +CALL_CONV:= win64 +CPU_ABI:= $(CPU_PORT)_$(CALL_CONV) +RELEASE:= build/release +RELEASE_SYS:= $(RELEASE)/$(CPU_ABI)/sys.o + +$(OBJS)/lib_$(CPU_ABI)/sys.o: $(CLZDIR)/main/.mtag $(SYS_SMPS) + @echo "simping " $@ + @$(JVM) com.seaofnodes.simple.Simple --cpu $(CPU_PORT) --abi $(CALL_CONV) --norun -o $@ src/main/smp/sys.smp + +$(RELEASE_SYS): $(OBJS)/lib_$(CPU_ABI)/sys.o + @echo "copying " $@ + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @cp $< $@ + +####################################################### +# Base launch line for JVM tests +JVM=nice java -ea -cp "$(CLZDIR)/main${SEP}${jars}${SEP}$(CLZDIR)/test" + +# Tests that do NOT need sys.smp +tests_raw0: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag + @echo "testing " $(test_raw0) + @[ -d $(OBJS) ] || mkdir -p $(OBJS) + @$(JVM) org.junit.runner.JUnitCore $(test_raw0) + +tests_raw1: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag + @echo "testing " $(test_raw1) + @[ -d $(OBJS) ] || mkdir -p $(OBJS) + @$(JVM) org.junit.runner.JUnitCore $(test_raw1) + +# Larger standalone tests, still JUnits, still no sys.smp +tests_alone: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag + @echo "testing " $(test_alone) + @[ -d $(OBJS) ] || mkdir -p $(OBJS) + @$(JVM) org.junit.runner.JUnitCore $(test_alone) + +# Tests that DO need sys.smp +tests_sys: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag $(RELEASE_SYS) + @echo "testing " $(test_sys) + @[ -d $(OBJS) ] || mkdir -p $(OBJS) + @$(JVM) org.junit.runner.JUnitCore $(test_sys) + +fuzzer: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag + @echo "fuzzing " + @$(JVM) org.junit.runner.JUnitCore com.seaofnodes.simple.FuzzerWrap + +tests: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag $(RELEASE_SYS) tests_raw0 tests_raw1 tests_alone tests_sys + @echo "All tests" + @[ -d $(OBJS) ] || mkdir -p $(OBJS) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) + + +# Build a Simple jar and release stdlib +release: build/release/Simple.jar $(RELEASE_SYS) + +# Build a Simple jar +build/release/Simple.jar: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag + @echo "jarring " $@ " because " $? + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @printf "Main-Class: com.seaofnodes.simple.Simple\nClass-Path: ../../lib/junit-4.12.jar ../../lib/hamcrest-core-1.3.jar\n\n" > build/release/MANIFEST.MF + @jar cfm $@ build/release/MANIFEST.MF -C $(CLZDIR)/main . -C $(CLZDIR)/test . -C $(SRC)/$(SIMPLE) . -C $(TST)/$(SIMPLE) . + +# Launch viewer +view: $(main_classes) + @echo "viewing " + @$(JVM) com.seaofnodes.simple.JSViewer + +####################################################### +.PHONY: clean +clean: + rm -rf build + rm -f TAGS + (find . -name "*~" -exec rm {} \; 2>/dev/null; exit 0) + +# Download libs from maven +lib: lib/junit-4.12.jar lib/hamcrest-core-1.3.jar + +# Jars +lib/junit-4.12.jar: + @[ -d lib ] || mkdir -p lib + @(cd lib; wget https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar) + +lib/hamcrest-core-1.3.jar: + @[ -d lib ] || mkdir -p lib + @(cd lib; wget https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar) + +# Build emacs tags (part of a tasty emacs ide experience) +TAGS: $(main_javas) $(test_javas) + @rm -f TAGS + @$(CTAGS) -e --recurse=yes --fields=+fksaiS $(SRC) $(TST) diff --git a/chapter25/README.md b/chapter25/README.md new file mode 100644 index 00000000..10cfe7e1 --- /dev/null +++ b/chapter25/README.md @@ -0,0 +1,86 @@ +# Chapter 25: Modules + + +You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter22...linear-chapter24) it to the previous chapter. + + +## Modules + +Goal: A Module plan (not yet a "system") +Goal: No syntax for the starter 1-file-1-line helloWorld.smp "module" +Goal: External symbols are in ".o" files, like Java ".class" - not an source file #include like C/C++ + +Needs a search strategy for ".o" files, so they mirror the source code directory structure. +Means there is a "build" root for .o files. +Means the CLI has a --flag for the build root, or some sensible default ($cwd/build) + +Projects with subdirectories pick up all symbols qualified names from the path-to-project-root + +Goal: under no circumstances can you load from an uninitialized variable (although you can see default inits for fields that have defaults). +Goal: nested tree-structured name spaces, to control complexity. +Goal: field decl order within a file is fairly flexible. + + +## more + +Has a defined project root (default: CWD). + +Has a path to root: %PATH, e.g. $ROOT/a/b/c/ + +Has a file name: $ROOT/ a/b/c/ foo.smp + +Everything in a dir is implicitly in name-space above it. + +Suppose `foo.smp` contains `PI=3.14;` + +Then the fully qualified name is: a.b.c.foo.PI + +Code is implicitly in the `struct a.b.c.foo {}` namespace. + +Final constant values can be accessed by direct names. + +Non-finals or non-constant need a constructed object. + + + +Top-level code in `foo.smp` is implicitly in the foo class init, +which runs the first time foo is touched, +which requires a "has been init" touch on every possible-first-access, +which is every non-local ("sideways") reference. + +Always the init for parents are done before children, +so `a` completes before `a.b`, and so on. + +Means `a` init can NOT reference e.g. `b,c,foo`, although +you can define top-level fcns that refer to `b`. + +Legal in `a.smp`: `val make_b = { -> new b{} };` // fcn is final constant + +Legal in `a.smp`: `val b = new b{} ;` // not constant, so requires instance & <clinit> is done + +Means making an `a` instance in a's <clinit>, will also run a's <init> +which can refer to instance fcns, which can refer to not-init finals? +Limit: all fcns can only refer to instance vars *before* them. + + + +Would like to make golden instances, static singletons: +val special = new a{/*anything already in-scope and <clinit>ed*/}; + + + +The default 1-file main then: +$ROOT==$CWD +Code is implicitly in the *empty* name space, is in the <clinit> for the empty +name space and gets run on project start. + +------ + +Implications for e.g. sys: + + .../simple/sys.smp - top level, has sys.parseAryI64 + ../simple/sys/sys.io.smp - has `struct io { ... }` + + +For now just gather the exported TYPE symbols, and a count of them, and export/input ELF. +NO: for now split into `sys` class from instance,
\ No newline at end of file diff --git a/chapter25/ROADMAP.md b/chapter25/ROADMAP.md new file mode 100644 index 00000000..981f65e3 --- /dev/null +++ b/chapter25/ROADMAP.md @@ -0,0 +1,462 @@ +## Separate Compilation + + +How to handle unknown forward ref? +No H-M, so no unification. +Demand no forward refs beyond functions... which will provide immediate signatures when def'd. + +Plan A: Remove FRefNode; fix all my bugs. +Fail A: I want mutual co-recurusion to work +Example: `val even = { int x -> x ? odd(x-1) : true; }; val odd = { int x -> x ? even(x-1) : false; };` + +Plan B: Require types ordered, but not any other fcn. `new T` can complain, +but not `fref.field` or `fref()` or `fref op fref` or `fref[]` + +Requires "lifting" Type.BOTTOM to the parser requirements everywhere. +So loading a field from an FRef requires: lift the FREF to a +TMP-to-TSTRUCT-with-FIELD... actually, just locally assume FREF will become +correct. Can't Lift it, no access, since type comes from random code. +TMP might be nullable (should error later). +TSTRUCT can/will be unnamed. Implies ALL TSTRUCT is unnamed. + +Issue is some unknown/remote/external/forward-ref. + +Allowing an FRef *at all* means everybody has to deal with seeing an FRef Type. +Allowing it for functions in same scope allow the above odd/even case . +Allowing it for structs in the same scope allows for co-recursive structs. +Promoting out-of-scope attempts the external lookup, then fails. + +Random junk usage: `sys.fld`. Looking up `sys` fails, now has to +guess: fref is ok, will be defined shortly... OR extern lookup now. +Then some usage delays trouble: +```java +// Extern or Future `sys` being returned. +val foo { -> return sys; } + +// foo() returns FRef, so Bottom, so the `.bar` has no type +// to do field lookups +val main { arg -> foo().bar } + +// HERE: Parser finds `sys` OR NOT?!?!?! +val sys??? = user_stuff....; + +``` + +Which gets to: parser never complains, builds SSA like a blind AST, +just wires up a graph with bad types all around. Requires: No Types +to guide parser! Just Syntax! + + (Obvious minor fail: widen floats- or-not) Fix: insert e.g. int-oper, return + type is MEET of Int,Float until both inputs are isa-Int and cannot lift to be + isa-Float + + Possible method call fail: + UNKNOWN_STRUCT_BOT.UNKNOWN_FIELD ( ARGS ); // Insert SELF OR NOT? + Possible fix: always assume a SELF arg, which is null for provable statics. + Optimization: constant parms get dropped from sig? *Constant* parms + must be constant from *Parser*, or else no unknown callers (so local opts). + Possible fix: SELF arg is *last* not *first*, easier to drop if dead? + + Load drops ConFldOff, keeps name. + Load keeps index unscaled, has index input, which is null for structs. + After Ptr Base is *closed*, base layout can happen, can fold index math. + - Load(ctrl, base, null ); // field name internal + - Load(ctrl, base, index_math ); // field name=='[]' + - Store(ctrl, base, null , val); + + FAIL: No Alias Info! + + +Plan C: + FRefs allowed for direct calls `odd()`, and for Types. + All other cases must *lift*. + + +## Unifying Structs and Arrays + +The idea is that structs have fields indexed by names, and arrays have fields +indexed by numbers, and these ideas are compatible with each other. This idea +has been used by the C language for a long time - an array can follow a normal +struct C and can index it with the usual array syntax. + +The alternative, using a field to hold a pointer to an array, exchanges some +flexibility for an extra indirection. The obvious flexibility allows swapping +the array out (generally for a larger one) and this is the basis of very common +`Vector` or `ArrayList` generic containers. + + +```java +struct str { + int hash; // A cache of the strings hash code + u32 #; // The array length + u8 []; // Array of bytes +}; + +str X = "abc"; +print(X.hash); // Read the hash field +X[0]=='a'; // Read array element 0 +X#; // Length of the array + +``` + +Here, the field named `#` holds the array length and the field named `[]` is +the actual array, which follows the other fields directly in memory. +Here is another example for variable length array: + +```java +struct vecInt { + u32 size; // Actual number of elements + u32 #; // The array length or capacity + int []; // Array of integers. +}; +``` + +The obvious limit here is that the array cannot be directly resized; it is +literally in the same memory block as the leading fields. Such a `vecInt` can +be *re-allocated* to a new larger `vecInt`, but the original `vecInt` will be +reclaimed - you cannot use the same `vecInt` reference forever. + +Any number of named fields are allowed: + +```java +struct Class { + str className; + str professor; + int time; + int credits; + Class prerequisite; + u16 #; // Limit of 65535 students per class + Student []; +}; +``` + +Or self-reference: + +```java +struct NTree { + Some val; + u8 #; // Length is 0-255 + NTree []; // More NTrees +}; +``` + +The syntax rules are: +- the last field is named `[]` and can be of any type. +- the second-to-last field is named `#` and must be some unsigned integer type + representing the maximum array length. +- may require a leading space in front of ` []` + + + +### Constructors and Arrays + +The allocation includes the array length, and can include a constructor: +`new NTree{val=17;}[2];` + +The constructor is optional, the array length is not. + +Optional function for initializing arrays, allows for write-once final arrays: +`new u8[buffer#,{ u32 idx -> buffer[idx].toUpperCase() }]` + + + +### Changing array max length + +An issue for large numbers of range-checked small arrays is that reserving a +full 4 bytes for array length can substantially increase the array overhead. +Simple allows for arrays to have smaller max lengths, although these are +treated as a different type than arrays with different max lengths. + +```java +struct x0 { u8 #; u8[]; }; // Array limited to 0-255 length +struct x1 { u16 #; u8[]; }; // Array limited to 0-65535 length +struct x2 { u32 #; u8[]; }; // +struct x3 { u64 #; u8[]; }; // + +x0 x = "abc"; // 4 bytes total: [3, 'a', 'b', 'c'] +x1 y = "def"; // 6 bytes total: [3, 0 , 'd', 'e', 'f', 0,]; 1 pad byte to acheieve 2-byte alignment +x = y; // ERROR, "x1 is not x0", no auto-widening +x += y; // OK, 7 bytes total: [6, 'a', 'b', 'c', 'd', 'e', 'f'] +y += x; // OK, 8 bytes total: [6, 0 , 'd', 'e', 'f', 'a', 'b', 'c'] + +x += "VeryLongString.... ....255_chars"; // Runtime RangeCheck + +// Explicit conversion to widen string +y = "" + x; +``` + + +### Two flavors of extendable arrays/strings: an extra indirection or not + +With an extra indirection you get e.g. Java `ArrayList` or C++ `Vector`. + +Without, then any call which extends the extendable array return a potentially +new object, only growing as needed. + +```java +val vec1 = new vecInt[0].add(2); // len=1, capacity=1 +val vec2 = vec1.add(3); // NEW vecInt returned, only is deleted; len=2; capacity=2; +vec1[0]; // ERROR, vec1 has been freed by call to `vec1.add(3)` +vec2[0]; // OK +vec2[1]; // OK +vec2[2]; // Runtime error, AIOOBE +``` + + +## A String Implementation + +Default strings have names like "str", "cstr", "ustr" or such. + +Always unicode8 with explicit u8 manipulations. Unicode "characters" will come +as function calls over the base implementation, probably in another string class. + +## Default string + +Efficiently growable via concantenation. Supports a variety of IO/printf functions. +The underlying array can be shared mutable if you ask for this. +The defaults will always be private mutable and read-only shared. + +```java +struct str { + + // Add a string to the string, possibly growing + val add = { str str -> + val self = this; + for( int i=0; i<str#; i++ ) + self = self.add(str[i]); + return self; + }; + + // Add an char to the string, possibly growing + val add = { u8 e -> + if( len >= # ) return copy(# ? #*2 : 1).add(e); + [size++] = e; + return this; + }; + + // Copy 'this' to a new larger size + val copy = { int sz2 -> + val v2 = new str[sz2]; + for( int i=0; i<len; i++ ) + v2[i] = [i]; + v2.len = len; + sys.libc.free(this); // Needs a better Mem Management solution + return v2; + }; + + val print { -> sys.libc.write(1/*stdout*/,[]/*array base as argument to C*/,len); return this; } + + u32 len; // In-use size + u32 #; // Max length is 4Gig, although variants can request smaller lengths + u8 []; // Character data +}; +``` + + +### A Final String + +Intended to be a **immutable** string, possibly used as a base for a **secure** +string. This needs to be created all at once, and probably requires a +defensive copy. + +```java +struct sstr { + // Possible static call syntax; no references to "this" + val make { str str -> new sstr[str#,str.at] }; + + // Array contents are immutable. + // Still figuring out good syntax for this. + u8 #; + u8 [~]; // Middlin '~'? Makes primitive array contents *immutable* and requires constructor syntax +}; + +// Construction requires a function that produces the array contents. +// Here we are defining a defensive copy over `buf`. +sstr safe = new sstr[buf.len,{ int idx -> buf[idx]; }]; +sstr safe = sstr.make("abc"); // Defensive copy is made +``` + + +### A Extendable String (aka StringBuilder or StringBuffer) + +This API is intended to be a drop-in for a `StringBuilder` style, and takes an +extra indirection with each usage. + +```java +struct xstr { + str !_str; // The extra indirection is here + val add = { str str -> _str = str.add2(str); }; + val write = { int fd -> _str.write(fd); }; +}; +``` + +### A Unicode String + +This API is intended to support unicode character manipulation over a plain `str` + +```java +struct ustr { + // Assuming idx is at the start of a character, return the character + // as a 32b integer. Returns junk integer if not at a character start. + // For all ASCII strings this will amount to a simple byte load. + // For bytes with high order bits set, this may read 1-4 characters. + val at = { int idx -> ... }; +}; +// Unicode character iterator +struct ucharator { +// Return successive unicode character codes +}; +``` + +### A "C" string + +A zero-terminated string, but the length has to be available directly + +```java +struct cstr { + u8 #; // length including the trailing zero + u8[]; // always ends in a zero byte; suitable for direct passing to C string functions +}; +``` + +### A "german string": a very short string that can exist in a register + +Length in memory is 1 byte (3 bits?) of length, then 0-7 chars; max 8 chars. +Fits in a 64 bit register. + +```java +struct gstr { // aka germanString, or blend string and register "streg" + u3 #; // Size is 0-7 bytes??? + u8 []; // Up to 7 bytes +} +// Using inlined object (see below). All these strings are short and are +// represented as packed integers in a 64b register. +*gstr prize1 = "gold"; +*gstr prize2 = "silver"; +*gstr prize3 = "bronze"; + +``` + +### Main becomes `val main = { str[] args -> ...}` + + +## Structs can be inlined + +Structs are normally by-reference, but can be converted to by-value by using +the `*` operator, pronounced "contents of" (this name might be changed to +e.g. `inline`). + +This is a name-space change only, you cannot take the address of the internal +struct without entirely consuming its lifetime. No "escaping". + +```java +// 2 64-bit words, no other overheads +struct Complex { f64 x,y; val len = { ->Math.sqrt(x*x+y*y); }; }; + +// Current Simple rules: always by-reference +struct ByRef { + Complex c; // 4-byte pointer to a Complex +}; +print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c + +// New behavior: by value (inlining a struct) +// The '*' syntax indicator can be something else, e.g. a keyword "inline" +// I am pronouncing '*' as "contents of" +struct ByValue { + *Complex c; // c is inlined, full 16 bytes into ByValue +}; +print(new ByValue.c.y); // x,y inlined into ByValue, no extra memory load + +// Mixing Refs and Struct Values. Basically, a ref can be converted +// to a value by taking the "contents of" the ref, and values cannot +// be *converted* to a ref, although they can be copied over a ref. +ref. c = val.c; // Error, mixing refs and values +ref.*c = val.c; // Allowed, whole structure copy. Does not allocate, requires ref.c not-null +val. c = ref.c; // Error, mixing refs and values +val. c = *ref.c; // Allowed, whole structure copy +ref. c = ref.c; // Allowed, pointer copy +val. c = val.c; // Allowed, whole structure copy. +val *c = val.c; // Error, cannot take "contents of" a value + +// Alternative syntax, to avoid the "contents of" being on LHS and field applying to being on RHS: +ref.c = val.*c; // Assign into ref.c the "contents of" val.c + +// Arrays of inlined structures +var ary = new *Complex[99]; // Array of 99 Complex objects, inlined + +// Calling methods has the same syntax +ref.c.len(); +val.c.len(); + +// Function arguments use the same typing, so can pass-by-value +val math.sin = { *Complex c -> ... }; // Passes a complex by value +math.sin(ref.*c); // Requires de-reference to pass by value +math.sin(val. c); // Pass by value + +``` + + + +## RoadMap for other chapters + +`&&` and `||` +Drop `int` to `i32`. +Drop `TypeMemPtr` to 4 bytes; using `mmap` to make heap in low 4G. +Default parser for-loop construction, or partial peel. + `for( init; test; next ) body` +becomes: +```java +{ init; // Normal scope entry to bound lifetime of index variable +if( !test ) { // Zero-trip count test + do { + body; + next; + } while( test ); // Exit test at loop bottom by default +} }; +``` + +## `&&` and `||` + +The logical operators && (AND) and || (OR) are short-circuiting, meaning they may skip evaluating the second condition if the first one already determines the result: + +- `&&` stops if the first operand is false. +- `||` stops if the first operand is true. + +They are syntactic sugar—used for cleaner control flow—but they don't create new nodes in the execution model. + +The `&&` and `||` constructs are simple syntactic sugars that only modify the control flow and do not +introduce new nodes. + +Consider this example: +```java +int a = 1; +int b = 0; + +if(a++ || b++ ) { + if(b == 0 && a == 2) { + sys.io.p("Or"); + } +} else{ + sys.io.p("And"); +} +return 0; +``` +- `a++` evaluates to 1 (true), so `b++` is not evaluated. +- As a result, `b` stays 0, `a` becomes 2, and "Or" is printed. + +```java +int a = 1; +int b = 1; + +int x=1; +int y=1; +int z=0; + +int g = x++ && y++ && z++; +... +``` +Here: +- `x++` is true (1), so it evaluates `y++`, which is also true. +- Then it evaluates `z++`, which is false (0). +- Since `&&` requires all conditions to be true, the final result `g` is 0. +- All operands up to the false one are evaluated, so `x`, `y`, and `z` are all incremented. diff --git a/chapter25/WIP-HANDOFF.md b/chapter25/WIP-HANDOFF.md new file mode 100644 index 00000000..9c4503d8 --- /dev/null +++ b/chapter25/WIP-HANDOFF.md @@ -0,0 +1,387 @@ +# Chapter 25 direct-to-SSA parser rewrite handoff + +Updated 2026-08-04 for transfer to another AI session/machine. + +## Read this first + +- Repository: `Simple/chapter25` +- Branch: `ch25-parser-simplification` +- Preservation tag: `ch25-parser-before-simplification` at `4d6a59ed` +- Last pushed commit: `4f8759b3 Rotate random seed and fix more bugs` +- Remote branch was at the same commit when this handoff was written. +- The working tree is dirty **after** that pushed checkpoint. Preserve it. +- The full original design/rollback plan is in + `parser-simplification-plan.md`. This file supersedes the old WIP handoff. + +Do not reset, clean, or mechanically restore the working tree. The dirty files +contain Cliff's next experiment. Untracked `.argsM.txt` and `.out.txt` are +transient local files, not source changes; `.argsM.txt` was locked when the +handoff test run was attempted. + +## Why this branch exists + +The constructor syntax rewrite exposed a deeper architectural flaw. This repo +is an experiment in parsing a high-level language directly into SSA in one +pass, without first constructing an AST. Forward declarations therefore remain +unknown during parsing. The parser had nevertheless been consulting provisional +types to choose permanent graph structure. + +The central rule of the rewrite is: + +> Syntax and resolved lexical binding choose SSA topology. Types may sharpen, +> optimize, and reject that graph, but provisional types must not decide its +> shape. + +The branch is deliberately unsquashed. Git, especially the preservation tag, +is the “this was a giant mistake; walk it back” path. Removed mechanisms should +be restored by named revert/cherry-pick/new commits, not by copying mysterious +pieces out of history. + +## Original goals + +The original plan had these major parts: + +1. Remove parser-supplied `_con` lower-bound types from Phi, Load, Store, and + similar nodes. Make `compute()` total on weak inputs. +2. Stop using inferred types for integer-versus-floating arithmetic topology. +3. Separate unresolved lexical field binding from type inference, eventually + using scope-owned unresolved loads/stores resolved at scope close. +4. Parse unresolved stores against bulk alias `#1`, then recover precise alias + structure monotonically after the alias is known. +5. Keep call shape syntactic: `ptr.fld(args)` always carries `ptr` in the hidden + self slot, even if the loaded function is ultimately a top-level function. +6. Separate authoritative declared function arguments from inferred return and + call-site information; reconsider/remove `FunNode._sig` as cached state. +7. Replace boolean field-final knowledge with four states: unknown, mutable, + final, and conflicting/BAD_FINAL, including TypeTest coverage. +8. Preserve `Type.closeOver()` and canonical cyclic named-type closure without + making parser topology depend on whether closure has happened yet. +9. Advance chapter-by-chapter, keeping earlier chapters and TypeTest green. + +Those remain the completion goals unless explicitly revised below. + +## Goals and invariants learned during implementation + +The following refinements became equally important: + +- Optimization/worklist order must not affect semantics or trigger structural + assertions. Random seed changes are a diagnostic tool, not a way to select a + preferred result. Minor graph-print and register-spill goldens may vary, but + should eventually be hardened where practical. +- `compute()` must be monotonic and simpler than `idealize()`. When both inspect + the same facts, `idealize()` should inspect them in the same order. +- An idealization must preserve already-known type information immediately; + the expensive `IterPeeps.progressOnList` assertion is intentionally catching + one-step Church-Rosser/monotonicity failures. +- Maintain Node keep/unkeep lifetime discipline when moving, cloning, or + replacing graph pieces. +- The parser carries only one bulk memory state. Precise alias slicing and + `MemMergeNode` are graph semantics, not nested parser state. +- At every program slice, alias memories are disjoint and collectively cover + every alias exactly once. Bulk memory means “all aliases not split out here.” +- Calls, call ends, returns, and memory parameters deliberately use opaque bulk + memory to control graph size. Precision across those boundaries comes from + careful inlining. +- Public concrete bulk alias-`#1` memory is never final. A single final bit + cannot truthfully describe an aggregate of unrelated unsplit aliases. + Private singleton memory and precise slices may retain finality. +- Precise aliases can peek through a `MemMerge`. Bulk memory can peek through + only when its excluded-alias coverage agrees with the merge. +- A bulk Phi carries an exclusion BitSet (`All - E`); parallel `MemPhiNode`s + carry the precise aliases in `E`. +- Mode selection for unified numeric nodes is a one-shot structural event. + `_mode` participates in GVN identity; flipping it unlocks and reinitializes + the node before IterPeeps observes it. + +## What has been completed on this branch + +### Preservation and early constraint removal + +- Created `parser-simplification-plan.md`, the dedicated branch, and permanent + pre-experiment tag. +- Fixed the `_Scan` constructor migration that initially obscured the work. +- Removed the parser constraint from ordinary Phis. Weak loop Phi inputs are + tolerated. The dead ordinary-Phi type field and serialization were removed. +- Split memory Phis out of ordinary Phi logic: + - `MemPhiNode` represents one precise alias. + - `BulkMemPhiNode` represents `All - exclusions`. + - Memory-specific compute/ideal behavior moved out of base Phi where useful. + +### Casts, guards, and conversions + +- Split the old overloaded Cast concept into: + - `GuardNode`: delayed zero/nonzero branch refinement with direction. + - `CheckCastNode`: proof/runtime type check which must fold or diagnose. + - `ConvertNode`: the one numeric/value conversion abstraction. +- Removed the old `liftExpr`-style broad parser helper in favor of explicit, + smaller conversion sites. +- Removed the obsolete `Evaluator`; `Eval2` is authoritative. + +### Parser memory simplification and alias recovery + +- Removed the parser's eager nested precise-memory slicing. Parser `ScopeNode` + state now tracks bulk memory rather than a parser-owned `MemMerge` bundle. +- Alias `#1` is the unresolved Store state. A Store sharpens once to `#N`; all + later alias evidence must agree. +- Store sharpening inserts/reconstructs precise memory plus a `MemMerge`, + preserving the bulk remainder locally. +- Added lazy creation/widening of precise memory Phis from bulk Phis and + MemMerges, including recovery when a parallel precise Phi already collapsed. +- Added checks around the exactly-once memory-slice invariant and fixed several + load/store push-through oscillations and missing slice cases. +- `EscapeNode` is recognized as a precise alias consumer of a bulk memory Phi. +- Parser constructor handling now recognizes named constructors structurally + and threads private constructor memory correctly; class-initializer return + memory handling was also repaired. + +### Unified integer/floating operations + +- Replaced split integer/FP Add/Sub/Mul/Div opcodes with unified source-level + operations carrying a one-shot mode: unknown, integer, or float. +- Bool and Minus share the small `ModeNode` contract where useful; Bool output + remains integer even for floating inputs. +- ULT is permanently integer-only. +- Added mode serialization and removed obsolete commented float-node/tag code. +- Recursive numeric SCCs are resolved after SCCP using graph evidence; float + wins when both families are present because integer operands can convert. +- Mode resolution now reinitializes cached type immediately before IterPeeps. +- Error checking for Arith/Bool/Minus was unified without allocating argument + arrays. + +### Load and memory type cleanup + +- `LoadNode` no longer stores a parser-provided `_con`. Its declared field type + is obtained via an accessor once the pointer/field exists; late type checking + reports missing/invalid fields. +- Deeply read-only pointer information is honored by loads instead of being + widened back to the generic declared struct field type. +- `MemPhiNode` deliberately starts weak enough that later precise field + knowledge remains monotonic. +- Public concrete bulk memory is forced non-final in `TypeMem`; cyclic dual + construction handles its temporary null payload. `TypeTest` passed after + this Type change. + +### Calls, cloning, and optimizer order + +- Instance-call hidden-self shape is syntactic rather than inferred from the + eventual function value. +- Function-body cloning/inlining was repaired for loop backedges; an earlier + bug left cloned inlined nodes pointing into the original function loop. +- IterPeeps replacement scheduling now revisits both old and replacement inputs + and replacement outputs. +- Monotonicity diagnostics now include node IDs and detailed input types/classes. +- Worklist seeds 124, 125, and 126 exposed and drove fixes for collapsed + precise Phis, bulk finality, numeric-mode cached types, and Escape alias use. + +## Known-good pushed checkpoint + +At pushed commit `4f8759b3`: + +- Default worklist seed is `126` (also used by `TestC`). +- `TypeTest` passed: 6 tests. +- Chapters 1 through 20 passed: 347 tests, zero failures. +- The seed-126 Ch5 graph shape and Ch20 x86 spill count were accepted as + goldens after structural failures were eliminated. +- `git diff --check` was clean before that commit was pushed. + +Recent milestone commits, newest first: + +```text +4f8759b3 Rotate random seed and fix more bugs +14a1a028 Fix general memory split issues +eea3d783 Fix cloning functions with loops +f036a040 minor fixes, thru ch18 +eaee86b8 Handle ambiguous self-recursive int/flt conversions +b9434562 Fixed up to ch17, plus some ch18 +18f07318 Uniform int/fp errors +f729484c Hey ch1-13 working again +58e012e6 Remove LoadNode dependence on _con +0bc690ae Final cleanup around unified int/fp ops +9720093f Finish off unified int/flt opcodes +54e5b83b Refactor Phi into MemPhi, BulkMemPhi +fe7ce3c4 WIP parse ordinary memory as bulk partitions +21352840 WIP rethread stores after alias resolution +1f7cb4ac Replace Lift with one destination ConvertNode +bbe7ebdd Rename proof casts to CheckCast +b3da12fd Delay branch zero refinements with GuardNode +fa4f33e7 Remove parser type constraint from ordinary phis +``` + +## Current dirty WIP after `4f8759b3` + +At handoff, tracked modifications are: + +```text +src/main/java/com/seaofnodes/simple/codegen/Opto.java +src/main/java/com/seaofnodes/simple/node/LoadNode.java +src/main/java/com/seaofnodes/simple/node/StoreNode.java +src/test/java/com/seaofnodes/simple/Chapter10Test.java +src/test/java/com/seaofnodes/simple/Chapter21Test.java +``` + +These appear to be an active experiment around weak Load/Store computation and +preserving precise Store users while an unresolved bulk Store sharpens: + +- `LoadNode.compute()` was reordered to inspect pointer and memory weak states + explicitly. A non-pointer low input currently falls back to `mem._t`. +- `StoreNode.compute()` currently comments out the `err() -> BOTTOM` fallback, + and `escapesFrom` was changed from the met value `t` to the stored `val`. +- When an alias-1 Store expands into a precise Store plus MemMerge, users which + already require the same alias are repointed directly to the new precise + Store before the bulk Store is replaced. +- Several Chapter 10 goldens currently retain a `Stop[...]` with returned + memory instead of expecting it to disappear. +- Chapter 21 `testAntiDeps1` x86 spill expectation is changed from 7 to 9. +- `Opto.linkStart` has a commented-out high-escape-set early return. There is + also an accidental comment typo (`asn inst`); clean that before committing. + +Treat all of those as WIP, not settled design. In particular, reconsider +whether changing goldens to retain memory hides incomplete dead-memory cleanup. +Do not discard the changes before understanding the failing case that motivated +them. + +The attempted handoff test command could not run because Make reported: + +```text +Makefile:111: *** open: .argsM.txt: Permission denied. Stop. +``` + +This is probably another active-process/Windows file-lock issue. Previously an +IntelliJ Java debugger was conclusively found to lock JAR/class outputs; killing +the debug process allowed compilation immediately. Close relevant Java/IDE or +Make processes, preserve or remove only the transient `.argsM.txt` as +appropriate, then rerun tests. Do not delete source files to work around it. + +## Work still required to finish the rewrite + +### Immediate frontier + +1. Recover the intent and test state of the five dirty WIP files. +2. Clear the `.argsM.txt` lock and run, in order: + - the motivating focused test; + - `TypeTest`; + - Chapters 1–20; + - Chapter 21, especially `testAntiDeps1` and `testStringExport`; + - then all of `tests_raw0`. +3. Decide whether same-alias Store users being repointed during sharpening is + the canonical local rewrite. Verify it preserves every user's old type in + one step and the exactly-once alias coverage invariant. +4. Restore the `Opto.linkStart` high-set error handling unless a reduced test + proves it wrong; never iterate `XInt.next` over a high/infinite set blindly. +5. Separate legitimate golden changes from leaked/dead returned memory. + +### Store `_con` and weak diagnostics + +`LoadNode._con` is gone, but `StoreNode._con` remains. It still sharpens from a +Field and trims stored values. Continue the original small-step experiment: + +- Determine whether `_con` is authoritative declared storage width or merely a + cache of `ptr.field(name)._t`. +- If it is derivable, replace uses with an accessor as was done for Load. +- Keep error reporting in `err()`/type checking rather than manufacturing + parser-time graph topology or accidental BOTTOM propagation. +- Use a diagnostic whose offending node has no BOTTOM inputs when validating + late error selection. + +### True unresolved lexical fields (original Phases 2/3) + +The memory alias machinery now tolerates an alias-1 Store and sharpens it +later, but the full lexical design is not complete: + +- Unqualified instance/class field references before declarations must resolve + by closed lexical scope, not by whichever pointer type happens to sharpen. +- A scope-owned unresolved load/store representation may still be needed. +- Resolution must select self versus class initializer base, precise alias, and + constructor `init` status together. +- Locals remain define-before-use and need no unresolved mechanism. +- Verify loops can lazily widen precise alias runs without global O(n²) + reconstruction or Phi/MemMerge oscillation. + +### Field finality (original Phase 6) + +The planned two-bit four-state Field finality has not been implemented: + +```text +00 unknown +01 declared mutable +10 declared final +11 conflicting / BAD_FINAL +``` + +This still needs accessors, meet/join specification, equality, hashing, dual, +printing/serialization as needed, representative `gather()` entries, and full +`TypeTest`. Parser-declared final stores require the structural `init` marker; +non-init writes to a resolved final field are errors. Cyclic named structs must +merge finality during `Type.closeOver()`. + +Do not confuse Field finality with bulk-memory finality: public bulk memory is +now always conservatively non-final. + +### Function contracts (original Phase 5) + +`FunNode._sig` still deserves an audit: + +- Declared argument syntax is required and authoritative. +- Return type is graph-derived. +- Private functions may sharpen from known call sites. +- Escaping functions need a worst-case/default unknown caller tied to the + declared type binding. +- Forward-declared argument types need a changing type source, not a true + ConstantNode. +- Remove cached signature state that can be derived from Fun/Parm/Return edges; + retain explicit declared contracts. + +### Final acceptance + +- Sweep several deterministic worklist seeds, not just 126. Semantic graph + results must converge independent of order. +- Harden graph-print and spill-count tests where harmless scheduling variation + remains. Do not weaken monotonicity or memory-coverage assertions. +- Run `tests_raw0`, then `tests_raw1`, then `tests_alone`. +- Run `TypeTest` after every Type/Field lattice change. +- Confirm serialization/deserialization after every new semantic node field or + mode bit. +- Audit the parser for remaining type-dependent structural choices, especially + conversions, field base selection, function self shape, and alias slicing. + +## Debugging guidance + +- Node IDs are central to debugging; refer to nodes as `Store#107`, `Phi#105`, + etc. +- Stop at the earliest `IterPeeps` count where the graph becomes invalid. +- `Opto.worklistCheck` catches missed/non-monotonic SCCP updates. +- `IterPeeps.progressOnList` catches one-step type and idealization failures; + its current detailed input dump was added specifically for order bugs. +- For memory bugs, check both: + 1. each program slice covers every alias exactly once; and + 2. a precise consumer never obtains an alias excluded from its bulk input + without a parallel precise slice. +- A precise `MemPhi#N` fed by a `MemMerge` whose default bulk excludes `N` and + which has no explicit `N` input is malformed. +- Avoid “fixes” which merely queue a favored node earlier. Change the seed and + demand order-independent semantics. +- Golden changes are acceptable only after semantic evaluation, type + monotonicity, and graph invariants are known good. + +## Workflow and safety + +- Cliff wants to review every push. Ask permission immediately before pushing. + In this handoff Cliff said he would push the current completed checkpoint; + do not assume that grants future push permission. +- Push remote checkpoints for backup once approved. +- Commit at useful test frontiers with messages describing the architectural + change, not only the symptom. +- Preserve unrelated dirty edits. Emacs temporary files are common and need + not be reported repeatedly. +- Windows IntelliJ debug sessions may lock JARs/class files. Report the lock, + have Cliff stop the debugger, and retry before changing build machinery. +- Prefer narrow changes; unwind experiments that cause cascading regressions. + +## Rollback paths + +- Whole experiment: branch from tag `ch25-parser-before-simplification`. +- Individual mechanism: revert/cherry-pick the narrow branch commits. +- Inspect old implementation without mutating this branch: + `git diff ch25-parser-before-simplification..HEAD` or `git show <old>:<path>`. +- Do not squash until the rewrite is accepted; the commit sequence is part of + the recovery design. diff --git a/chapter25/ainotes.txt b/chapter25/ainotes.txt new file mode 100644 index 00000000..625acddc --- /dev/null +++ b/chapter25/ainotes.txt @@ -0,0 +1,30 @@ + +Meta-info: I intend to keep extending and re-editing this "ainotes.txt" file so +I can reprompt the you (the AI) between AI restarts, As such this file will +contain a mix of high-level design decisions, goals, intents and wisdom, and +then taper down to more detailed problems and solutions - which probably keep +changing. + +AI is allowed access to all files recursively in "C:/Users/cliffc/Desktop/Simple" +directory. + +Meta-info: all the tests run with `make tests` currently run, and should again +run before we accept any changes. + + +The overall project is rooted in the `Simple/` directory, +and is a compiler tutorial broken up into chapters. +The chapters build on each other. + +Read the code in this chapter25; the goal in this chapter is to allow separate +compilation. I want to compile a "standard library", the start of which is in +`src/main/smp/sys.smp` and subdirectories and end up with either an archive or +a "fat ELF .o" file. + +Then I want to compile a 1-liner "hello world" program: + `sys.io.p("Hello World!");` +and have the symbols `sys`, `io` and `p` be found in the standard library. + +I am using a test case to represent this seperate compilation strategy in +`Chapter25Test.testModules`. There are comments there to help drive my +thinking. diff --git a/chapter25/docs/18-grammar.md b/chapter25/docs/18-grammar.md new file mode 100644 index 00000000..7b611fbf --- /dev/null +++ b/chapter25/docs/18-grammar.md @@ -0,0 +1,132 @@ +# Grammar for Chapter 16 + +```antlrv4 +grammar SimpleLanguage; + +program : block EOF ; + +block : statement+ ; + + +statement + : '{' block '}' + | returnStatement + | ifStatement + | whileStatement + | breakStatement + | continueStatement + | metaStatement + | structDeclaration + | expressionStatement + ; + + +returnStatement : 'return' expression ';' ; + +ifStatement : 'if' '(' expression ')' statement ('else' statement)? ; + +whileStatement : 'while' '(' expression ')' statement ; + +breakStatement : 'break' ';' ; + +continueStatement : 'continue' ';' ; + +metaStatement : '#showGraph' ';' ; + +structDeclaration : 'struct' IDENTIFIER '{' block '}' ; + +expressionStatement + : type IDENTIFIER ';' + | type IDENTIFIER '=' expression ';' + | IDENTIFIER '=' expression ';' + | expression + ; + +PRIMTYPE + : 'int' + | 'i8' + | 'i16' + | 'i32' + | 'i64' + | 'u8' + | 'u16' + | 'u32' + | 'flt' + | 'f32' + | 'f64' + | 'bool' + ; + +type + : PRIMTYPE + | typeName '?'? + | typeName nestedArray+ + ; + +nestedArray : '?'? '[]'* ; + +typeName : IDENTIFIER ; + + +expression : bitWiseExpression ; + +bitWiseExpression + : comparisonExpression ( '&' | '|' | '^' ) comparisonExpression)* + ; + +comparisonExpression + : shiftExpression (('==' | '!='| '>'| '<'| '>='| '<=') shiftExpression)* + ; + +shiftExpression + : additiveExpression ( '<<' | '>>>' | '>>' ) additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (('+' | '-') multiplicativeExpression)* + ; + +multiplicativeExpression + : unaryExpression (('*' | '/') unaryExpression)* + ; + +unaryExpression + : ('-') unaryExpression + | '!' unaryExpression + | primaryExpression postAssign + ; + +primaryExpression + : INTEGER_LITERAL + | FLOAT_LITERAL + | '(' expression ')' + | 'true' + | 'false' + | 'null' + | newExpression + | IDENTIFIER + ; + +newExpression + : 'new' IDENTIFIER [ '{' block '}' ] + | 'new' IDENTIFIER '[' expression ']' + ; + +postAssign : postFix [ '=' expression ] | [ '#' ]; + +postFix + : '.' IDENTIFIER postFix + | '[' expression ']' postFix + ; + + +IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ; + +INTEGER_LITERAL + : [1-9]DIGIT* + | [0] + ; + +NON_DIGIT: [a-zA-Z_]; +DIGIT: [0-9]; +``` diff --git a/chapter25/docs/Makefile b/chapter25/docs/Makefile new file mode 100644 index 00000000..12659b8f --- /dev/null +++ b/chapter25/docs/Makefile @@ -0,0 +1,12 @@ +GV := $(wildcard *.gv) +SVG := $(patsubst %.gv,%.svg,$(GV)) + +.PHONY: all +all: $(SVG) + +%.svg: %.gv + dot -Tsvg $< > $@ + +.PHONY: clean +clean: + @rm -f $(SVG) diff --git a/chapter25/docs/d3-graphviz.js b/chapter25/docs/d3-graphviz.js new file mode 100644 index 00000000..7439ba65 --- /dev/null +++ b/chapter25/docs/d3-graphviz.js @@ -0,0 +1,2014 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-selection'), require('d3-dispatch'), require('d3-transition'), require('d3-timer'), require('d3-interpolate'), require('d3-zoom'), require('d3-format'), require('d3-path')) : + typeof define === 'function' && define.amd ? define(['exports', 'd3-selection', 'd3-dispatch', 'd3-transition', 'd3-timer', 'd3-interpolate', 'd3-zoom', 'd3-format', 'd3-path'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["d3-graphviz"] = {}, global.d3, global.d3, global.d3, global.d3, global.d3, global.d3, global.d3, global.d3)); +})(this, (function (exports, d3, d3Dispatch, d3Transition, d3Timer, d3Interpolate, d3Zoom, d3Format, d3Path) { 'use strict'; + + function _interopNamespaceDefault(e) { + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n.default = e; + return Object.freeze(n); + } + + var d3__namespace = /*#__PURE__*/_interopNamespaceDefault(d3); + + function _arrayLikeToArray(r, a) { + (null == a || a > r.length) && (a = r.length); + for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; + return n; + } + function _arrayWithoutHoles(r) { + if (Array.isArray(r)) return _arrayLikeToArray(r); + } + function _defineProperty(e, r, t) { + return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { + value: t, + enumerable: !0, + configurable: !0, + writable: !0 + }) : e[r] = t, e; + } + function _iterableToArray(r) { + if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); + } + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + function _toConsumableArray(r) { + return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); + } + function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); + } + function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; + } + function _unsupportedIterableToArray(r, a) { + if (r) { + if ("string" == typeof r) return _arrayLikeToArray(r, a); + var t = {}.toString.call(r).slice(8, -1); + return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; + } + } + + function extractElementData(element) { + var datum = {}; + var tag = element.node().nodeName; + datum.tag = tag; + if (tag == '#text') { + datum.text = element.text(); + } else if (tag == '#comment') { + datum.comment = element.text(); + } + datum.attributes = {}; + var attributes = element.node().attributes; + if (attributes) { + for (var i = 0; i < attributes.length; i++) { + var attribute = attributes[i]; + var name = attribute.name; + var value = attribute.value; + datum.attributes[name] = value; + } + } + var transform = element.node().transform; + if (transform && transform.baseVal.numberOfItems != 0) { + var matrix = transform.baseVal.consolidate().matrix; + datum.translation = { + x: matrix.e, + y: matrix.f + }; + datum.scale = matrix.a; + } + if (tag == 'ellipse') { + datum.center = { + x: datum.attributes.cx, + y: datum.attributes.cy + }; + } + if (tag == 'polygon') { + var points = element.attr('points').split(' '); + var x = points.map(function (p) { + return p.split(',')[0]; + }); + var y = points.map(function (p) { + return p.split(',')[1]; + }); + var xmin = Math.min.apply(null, x); + var xmax = Math.max.apply(null, x); + var ymin = Math.min.apply(null, y); + var ymax = Math.max.apply(null, y); + var bbox = { + x: xmin, + y: ymin, + width: xmax - xmin, + height: ymax - ymin + }; + datum.bbox = bbox; + datum.center = { + x: (xmin + xmax) / 2, + y: (ymin + ymax) / 2 + }; + } + if (tag == 'path') { + var d = element.attr('d'); + var points = d.split(/[A-Z ]/); + points.shift(); + var x = points.map(function (p) { + return +p.split(',')[0]; + }); + var y = points.map(function (p) { + return +p.split(',')[1]; + }); + var xmin = Math.min.apply(null, x); + var xmax = Math.max.apply(null, x); + var ymin = Math.min.apply(null, y); + var ymax = Math.max.apply(null, y); + var bbox = { + x: xmin, + y: ymin, + width: xmax - xmin, + height: ymax - ymin + }; + datum.bbox = bbox; + datum.center = { + x: (xmin + xmax) / 2, + y: (ymin + ymax) / 2 + }; + datum.totalLength = element.node().getTotalLength(); + } + if (tag == 'text') { + datum.center = { + x: element.attr('x'), + y: element.attr('y') + }; + } + if (tag == '#text') { + datum.text = element.text(); + } else if (tag == '#comment') { + datum.comment = element.text(); + } + return datum; + } + function extractAllElementsData(element) { + var datum = extractElementData(element); + datum.children = []; + var children = d3__namespace.selectAll(element.node().childNodes); + children.each(function () { + var childData = extractAllElementsData(d3__namespace.select(this)); + childData.parent = datum; + datum.children.push(childData); + }); + return datum; + } + function createElement(data) { + if (data.tag == '#text') { + return document.createTextNode(""); + } else if (data.tag == '#comment') { + return document.createComment(data.comment); + } else { + return document.createElementNS('http://www.w3.org/2000/svg', data.tag); + } + } + function createElementWithAttributes(data) { + var elementNode = createElement(data); + var element = d3__namespace.select(elementNode); + var attributes = data.attributes; + for (var _i = 0, _Object$keys = Object.keys(attributes); _i < _Object$keys.length; _i++) { + var attributeName = _Object$keys[_i]; + var attributeValue = attributes[attributeName]; + element.attr(attributeName, attributeValue); + } + return elementNode; + } + function replaceElement(element, data) { + var parent = d3__namespace.select(element.node().parentNode); + var newElementNode = createElementWithAttributes(data); + var newElement = parent.insert(function () { + return newElementNode; + }, function () { + return element.node(); + }); + element.remove(); + return newElement; + } + function insertElementData(element, datum) { + element.datum(datum); + element.data([datum], function (d) { + return d.key; + }); + } + function insertAllElementsData(element, datum) { + insertElementData(element, datum); + var children = d3__namespace.selectAll(element.node().childNodes); + children.each(function (d, i) { + insertAllElementsData(d3__namespace.select(this), datum.children[i]); + }); + } + function insertChildren(element, index) { + var children = element.selectAll(function () { + return element.node().childNodes; + }); + children = children.data(function (d) { + return d.children; + }, function (d) { + return d.tag + '-' + index; + }); + var childrenEnter = children.enter().append(function (d) { + return createElement(d); + }); + var childrenExit = children.exit(); + childrenExit = childrenExit.remove(); + children = childrenEnter.merge(children); + var childTagIndexes = {}; + children.each(function (childData) { + var childTag = childData.tag; + if (childTagIndexes[childTag] == null) { + childTagIndexes[childTag] = 0; + } + var childIndex = childTagIndexes[childTag]++; + attributeElement.call(this, childData, childIndex); + }); + } + function attributeElement(data) { + var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var element = d3__namespace.select(this); + data.tag; + var attributes = data.attributes; + var currentAttributes = element.node().attributes; + if (currentAttributes) { + for (var i = 0; i < currentAttributes.length; i++) { + var currentAttribute = currentAttributes[i]; + var name = currentAttribute.name; + if (name.split(':')[0] != 'xmlns' && currentAttribute.namespaceURI) { + var namespaceURIParts = currentAttribute.namespaceURI.split('/'); + var namespace = namespaceURIParts[namespaceURIParts.length - 1]; + name = namespace + ':' + name; + } + if (!(name in attributes)) { + attributes[name] = null; + } + } + } + for (var _i2 = 0, _Object$keys2 = Object.keys(attributes); _i2 < _Object$keys2.length; _i2++) { + var attributeName = _Object$keys2[_i2]; + element.attr(attributeName, attributes[attributeName]); + } + if (data.text) { + element.text(data.text); + } + insertChildren(element, index); + } + + function zoom (enable) { + this._options.zoom = enable; + if (this._options.zoom && !this._zoomBehavior) { + createZoomBehavior.call(this); + } else if (!this._options.zoom && this._zoomBehavior) { + this._zoomSelection.on(".zoom", null); + this._zoomBehavior = null; + } + return this; + } + function createZoomBehavior() { + var graphvizInstance = this; + function zoomed(event) { + var g = d3__namespace.select(svg.node().querySelector("g")); + g.attr('transform', event.transform); + graphvizInstance._dispatch.call('zoom', graphvizInstance); + } + var root = this._selection; + var svg = d3__namespace.select(root.node().querySelector("svg")); + if (svg.size() == 0) { + return this; + } + this._zoomSelection = svg; + var zoomBehavior = d3Zoom.zoom().scaleExtent(this._options.zoomScaleExtent).translateExtent(this._options.zoomTranslateExtent).interpolate(d3Interpolate.interpolate).on("zoom", zoomed); + this._zoomBehavior = zoomBehavior; + var g = d3__namespace.select(svg.node().querySelector("g")); + svg.call(zoomBehavior); + if (!this._active) { + translateZoomBehaviorTransform.call(this, g); + } + this._originalTransform = d3Zoom.zoomTransform(svg.node()); + return this; + } + function getTranslatedZoomTransform(selection) { + // Get the current zoom transform for the top level svg and + // translate it uniformly with the given selection, using the + // difference between the translation specified in the selection's + // data and it's saved previous translation. The selection is + // normally the top level g element of the graph. + var oldTranslation = this._translation; + var oldScale = this._scale; + var newTranslation = selection.datum().translation; + var newScale = selection.datum().scale; + var t = d3Zoom.zoomTransform(this._zoomSelection.node()); + if (oldTranslation) { + t = t.scale(1 / oldScale); + t = t.translate(-oldTranslation.x, -oldTranslation.y); + } + t = t.translate(newTranslation.x, newTranslation.y); + t = t.scale(newScale); + return t; + } + function translateZoomBehaviorTransform(selection) { + // Translate the current zoom transform for the top level svg + // uniformly with the given selection, using the difference + // between the translation specified in the selection's data and + // it's saved previous translation. The selection is normally the + // top level g element of the graph. + this._zoomBehavior.transform(this._zoomSelection, getTranslatedZoomTransform.call(this, selection)); + + // Save the selections's new translation and scale. + this._translation = selection.datum().translation; + this._scale = selection.datum().scale; + + // Set the original zoom transform to the translation and scale specified in + // the selection's data. + this._originalTransform = d3Zoom.zoomIdentity.translate(selection.datum().translation.x, selection.datum().translation.y).scale(selection.datum().scale); + } + function resetZoom(transition) { + // Reset the zoom transform to the original zoom transform. + var selection = this._zoomSelection; + if (transition) { + selection = selection.transition(transition); + } + selection.call(this._zoomBehavior.transform, this._originalTransform); + return this; + } + function zoomScaleExtent(extent) { + this._options.zoomScaleExtent = extent; + return this; + } + function zoomTranslateExtent(extent) { + this._options.zoomTranslateExtent = extent; + return this; + } + function zoomBehavior() { + return this._zoomBehavior || null; + } + function zoomSelection() { + return this._zoomSelection || null; + } + + function pathTween(points, d1) { + return function () { + var pointInterpolators = points.map(function (p) { + return d3Interpolate.interpolate([p[0][0], p[0][1]], [p[1][0], p[1][1]]); + }); + return function (t) { + return t < 1 ? "M" + pointInterpolators.map(function (p) { + return p(t); + }).join("L") : d1; + }; + }; + } + function pathTweenPoints(node, d1, precision, precisionIsRelative) { + var path0 = node; + var path1 = path0.cloneNode(); + var n0 = path0.getTotalLength(); + var n1 = (path1.setAttribute("d", d1), path1).getTotalLength(); + + // Uniform sampling of distance based on specified precision. + var distances = [0]; + var i = 0; + var dt = precisionIsRelative ? precision : precision / Math.max(n0, n1); + while ((i += dt) < 1) { + distances.push(i); + } + distances.push(1); + + // Compute point-interpolators at each distance. + var points = distances.map(function (t) { + var p0 = path0.getPointAtLength(t * n0); + var p1 = path1.getPointAtLength(t * n1); + return [[p0.x, p0.y], [p1.x, p1.y]]; + }); + return points; + } + + function data () { + return this._data || null; + } + function isEdgeElementParent(datum) { + return datum.attributes["class"] == 'edge' || datum.tag == 'a' && datum.parent.tag == 'g' && datum.parent.parent.attributes["class"] == 'edge'; + } + function isEdgeElement(datum) { + return datum.parent && isEdgeElementParent(datum.parent); + } + function getEdgeGroup(datum) { + if (datum.parent.attributes["class"] == 'edge') { + return datum.parent; + } else { + // datum.parent.tag == 'g' && datum.parent.parent.tag == 'g' && datum.parent.parent.parent.attributes.class == 'edge' + return datum.parent.parent.parent; + } + } + function getEdgeTitle(datum) { + return getEdgeGroup(datum).children.find(function (e) { + return e.tag == 'title'; + }); + } + + function render (callback) { + if (this._busy) { + this._queue.push(this.render.bind(this, callback)); + return this; + } + this._dispatch.call('renderStart', this); + if (this._transitionFactory) { + d3Timer.timeout(function () { + // Decouple from time spent. See https://github.com/d3/d3-timer/issues/27 + this._transition = d3Transition.transition(this._transitionFactory()); + _render.call(this, callback); + }.bind(this), 0); + } else { + _render.call(this, callback); + } + return this; + } + function _render(callback) { + var transitionInstance = this._transition; + var fade = this._options.fade && transitionInstance != null; + var tweenPaths = this._options.tweenPaths; + var tweenShapes = this._options.tweenShapes; + var convertEqualSidedPolygons = this._options.convertEqualSidedPolygons; + var growEnteringEdges = this._options.growEnteringEdges && transitionInstance != null; + var attributer = this._attributer; + var graphvizInstance = this; + function insertChildren(element) { + var children = element.selectAll(function () { + return element.node().childNodes; + }); + children = children.data(function (d) { + return d.children; + }, function (d) { + return d.key; + }); + var childrenEnter = children.enter().append(function (d) { + var element = createElement(d); + if (d.tag == '#text' && fade) { + element.nodeValue = d.text; + } + return element; + }); + if (fade || growEnteringEdges && isEdgeElementParent(element.datum())) { + var childElementsEnter = childrenEnter.filter(function (d) { + return d.tag[0] == '#' ? null : this; + }).each(function (d) { + var childEnter = d3__namespace.select(this); + for (var _i = 0, _Object$keys = Object.keys(d.attributes); _i < _Object$keys.length; _i++) { + var attributeName = _Object$keys[_i]; + var attributeValue = d.attributes[attributeName]; + childEnter.attr(attributeName, attributeValue); + } + }); + childElementsEnter.filter(function (d) { + return d.tag == 'svg' || d.tag == 'g' ? null : this; + }).style("opacity", 0.0); + } + var childrenExit = children.exit(); + if (attributer) { + childrenExit.each(attributer); + } + if (transitionInstance) { + childrenExit = childrenExit.transition(transitionInstance); + if (fade) { + childrenExit.filter(function (d) { + return d.tag[0] == '#' ? null : this; + }).style("opacity", 0.0); + } + } + childrenExit = childrenExit.remove(); + children = childrenEnter.merge(children).order(); + children.each(attributeElement); + } + function attributeElement(data) { + var element = d3__namespace.select(this); + if (data.tag == "svg") { + var options = graphvizInstance._options; + if (options.width != null || options.height != null) { + var width = options.width; + var height = options.height; + if (width == null) { + width = data.attributes.width.replace('pt', '') * 4 / 3; + } else { + element.attr("width", width); + data.attributes.width = width; + } + if (height == null) { + height = data.attributes.height.replace('pt', '') * 4 / 3; + } else { + element.attr("height", height); + data.attributes.height = height; + } + if (!options.fit) { + element.attr("viewBox", "0 0 ".concat(width * 3 / 4 / options.scale, " ").concat(height * 3 / 4 / options.scale)); + data.attributes.viewBox = "0 0 ".concat(width * 3 / 4 / options.scale, " ").concat(height * 3 / 4 / options.scale); + } + } + if (options.scale != 1 && (options.fit || options.width == null && options.height == null)) { + width = data.attributes.viewBox.split(' ')[2]; + height = data.attributes.viewBox.split(' ')[3]; + element.attr("viewBox", "0 0 ".concat(width / options.scale, " ").concat(height / options.scale)); + data.attributes.viewBox = "0 0 ".concat(width / options.scale, " ").concat(height / options.scale); + } + } + if (attributer) { + element.each(attributer); + } + var tag = data.tag; + var attributes = data.attributes; + var currentAttributes = element.node().attributes; + if (currentAttributes) { + for (var i = 0; i < currentAttributes.length; i++) { + var currentAttribute = currentAttributes[i]; + var name = currentAttribute.name; + if (name.split(':')[0] != 'xmlns' && currentAttribute.namespaceURI) { + var namespaceURIParts = currentAttribute.namespaceURI.split('/'); + var namespace = namespaceURIParts[namespaceURIParts.length - 1]; + name = namespace + ':' + name; + } + if (!(name in attributes)) { + attributes[name] = null; + } + } + } + var convertShape = false; + var convertPrevShape = false; + if (tweenShapes && transitionInstance) { + if ((this.nodeName == 'polygon' || this.nodeName == 'ellipse') && data.alternativeOld) { + convertPrevShape = true; + } + if ((tag == 'polygon' || tag == 'ellipse') && data.alternativeNew) { + convertShape = true; + } + if (this.nodeName == 'polygon' && tag == 'polygon' && data.alternativeOld) { + var prevData = extractElementData(element); + var prevPoints = prevData.attributes.points; + if (!convertEqualSidedPolygons) { + var nPrevPoints = prevPoints.split(' ').length; + var points = data.attributes.points; + var nPoints = points.split(' ').length; + if (nPoints == nPrevPoints) { + convertShape = false; + convertPrevShape = false; + } + } + } + if (convertPrevShape) { + var prevPathData = data.alternativeOld; + var pathElement = replaceElement(element, prevPathData); + pathElement.data([data], function () { + return data.key; + }); + element = pathElement; + } + if (convertShape) { + var newPathData = data.alternativeNew; + tag = 'path'; + attributes = newPathData.attributes; + } + } + var elementTransition = element; + if (transitionInstance) { + elementTransition = elementTransition.transition(transitionInstance); + if (fade) { + elementTransition.filter(function (d) { + return d.tag[0] == '#' ? null : this; + }).style("opacity", 1.0); + } + elementTransition.filter(function (d) { + return d.tag[0] == '#' ? null : this; + }).on("end", function (d) { + d3__namespace.select(this).attr('style', d && d.attributes && d.attributes.style || null); + }); + } + var growThisPath = growEnteringEdges && tag == 'path' && data.offset; + if (growThisPath) { + var totalLength = data.totalLength; + element.attr("stroke-dasharray", totalLength + " " + totalLength).attr("stroke-dashoffset", totalLength).attr('transform', 'translate(' + data.offset.x + ',' + data.offset.y + ')'); + attributes["stroke-dashoffset"] = 0; + attributes['transform'] = 'translate(0,0)'; + elementTransition.attr("stroke-dashoffset", attributes["stroke-dashoffset"]).attr('transform', attributes['transform']).on("start", function () { + d3__namespace.select(this).style('opacity', null); + }).on("end", function () { + d3__namespace.select(this).attr('stroke-dashoffset', null).attr('stroke-dasharray', null).attr('transform', null); + }); + } + var moveThisPolygon = growEnteringEdges && tag == 'polygon' && isEdgeElement(data) && data.offset && data.parent.children[3].tag == 'path'; + if (moveThisPolygon) { + var edgePath = d3__namespace.select(element.node().parentNode.querySelector("path")); + var p0 = edgePath.node().getPointAtLength(0); + var p1 = edgePath.node().getPointAtLength(data.totalLength); + var p2 = edgePath.node().getPointAtLength(data.totalLength - 1); + var angle1 = Math.atan2(p1.y - p2.y, p1.x - p2.x) * 180 / Math.PI; + var x = p0.x - p1.x + data.offset.x; + var y = p0.y - p1.y + data.offset.y; + element.attr('transform', 'translate(' + x + ',' + y + ')'); + elementTransition.attrTween("transform", function () { + return function (t) { + var p = edgePath.node().getPointAtLength(data.totalLength * t); + var p2 = edgePath.node().getPointAtLength(data.totalLength * t + 1); + var angle = Math.atan2(p2.y - p.y, p2.x - p.x) * 180 / Math.PI - angle1; + x = p.x - p1.x + data.offset.x * (1 - t); + y = p.y - p1.y + data.offset.y * (1 - t); + return 'translate(' + x + ',' + y + ') rotate(' + angle + ' ' + p1.x + ' ' + p1.y + ')'; + }; + }).on("start", function () { + d3__namespace.select(this).style('opacity', null); + }).on("end", function () { + d3__namespace.select(this).attr('transform', null); + }); + } + var tweenThisPath = tweenPaths && transitionInstance && tag == 'path' && element.attr('d') != null; + for (var _i2 = 0, _Object$keys2 = Object.keys(attributes); _i2 < _Object$keys2.length; _i2++) { + var attributeName = _Object$keys2[_i2]; + var attributeValue = attributes[attributeName]; + if (tweenThisPath && attributeName == 'd') { + var points = (data.alternativeOld || data).points; + if (points) { + elementTransition.attrTween("d", pathTween(points, attributeValue)); + } + } else { + if (attributeName == 'transform' && data.translation) { + if (transitionInstance) { + var onEnd = elementTransition.on("end"); + elementTransition.on("start", function () { + if (graphvizInstance._zoomBehavior) { + // Update the transform to transition to, just before the transition starts + // in order to catch changes between the transition scheduling to its start. + elementTransition.tween("attr.transform", function () { + var node = this; + return function (t) { + node.setAttribute("transform", d3Interpolate.interpolateTransformSvg(d3Zoom.zoomTransform(graphvizInstance._zoomSelection.node()).toString(), getTranslatedZoomTransform.call(graphvizInstance, element).toString())(t)); + }; + }); + } + }).on("end", function () { + onEnd.call(this); + // Update the zoom transform to the new translated transform + if (graphvizInstance._zoomBehavior) { + translateZoomBehaviorTransform.call(graphvizInstance, element); + } + }); + } else { + if (graphvizInstance._zoomBehavior) { + // Update the transform attribute to set with the current pan translation + translateZoomBehaviorTransform.call(graphvizInstance, element); + attributeValue = getTranslatedZoomTransform.call(graphvizInstance, element).toString(); + } + } + } + elementTransition.attr(attributeName, attributeValue); + } + } + if (convertShape) { + elementTransition.on("end", function (d, i, nodes) { + pathElement = d3__namespace.select(this); + var newElement = replaceElement(pathElement, d); + newElement.data([d], function () { + return d.key; + }); + }); + } + if (data.text) { + elementTransition.text(data.text); + } + insertChildren(element); + } + var root = this._selection; + if (transitionInstance != null) { + // Ensure original SVG shape elements are restored after transition before rendering new graph + var jobs = this._jobs; + if (graphvizInstance._active) { + jobs.push(null); + return this; + } else { + root.transition(transitionInstance).transition().duration(0).on("end", function () { + graphvizInstance._active = false; + if (jobs.length != 0) { + jobs.shift(); + graphvizInstance.render(); + } + }); + this._active = true; + } + } + if (transitionInstance != null) { + root.transition(transitionInstance).on("start", function () { + graphvizInstance._dispatch.call('transitionStart', graphvizInstance); + }).on("end", function () { + graphvizInstance._dispatch.call('transitionEnd', graphvizInstance); + }).transition().duration(0).on("start", function () { + graphvizInstance._dispatch.call('restoreEnd', graphvizInstance); + graphvizInstance._dispatch.call('end', graphvizInstance); + if (callback) { + callback.call(graphvizInstance); + } + }); + } + var data = this._data; + var svg = root.selectAll("svg").data([data], function (d) { + return d.key; + }); + svg = svg.enter().append("svg").merge(svg); + attributeElement.call(svg.node(), data); + if (this._options.zoom && !this._zoomBehavior) { + createZoomBehavior.call(this); + } + graphvizInstance._dispatch.call('renderEnd', graphvizInstance); + if (transitionInstance == null) { + this._dispatch.call('end', this); + if (callback) { + callback.call(this); + } + } + return this; + } + + function graphvizVersion () { + return this._graphvizVersion; + } + + var io=ArrayBuffer,H=Uint8Array,Qe=Uint16Array,so=Int16Array,qe=Int32Array,pr=function(p,c,d){if(H.prototype.slice)return H.prototype.slice.call(p,c,d);(c==null||c<0)&&(c=0),(d==null||d>p.length)&&(d=p.length);var s=new H(d-c);return s.set(p.subarray(c,d)),s},Ye=function(p,c,d,s){if(H.prototype.fill)return H.prototype.fill.call(p,c,d,s);for((d==null||d<0)&&(d=0),(s==null||s>p.length)&&(s=p.length);d<s;++d)p[d]=c;return p},uo=function(p,c,d,s){if(H.prototype.copyWithin)return H.prototype.copyWithin.call(p,c,d,s);for((d==null||d<0)&&(d=0),(s==null||s>p.length)&&(s=p.length);d<s;)p[c++]=p[d++];},po=["invalid zstd data","window size too large (>2046MB)","invalid block type","FSE accuracy too high","match distance too far back","unexpected EOF"],T=function(p,c,d){var s=new Error(c||po[p]);if(s.code=p,Error.captureStackTrace&&Error.captureStackTrace(s,T),!d)throw s;return s},jr=function(p,c,d){for(var s=0,f=0;s<d;++s)f|=p[c++]<<(s<<3);return f},lo=function(p,c){return (p[c]|p[c+1]<<8|p[c+2]<<16|p[c+3]<<24)>>>0},co=function(p,c){var d=p[0]|p[1]<<8|p[2]<<16;if(d==3126568&&p[3]==253){var s=p[4],f=s>>5&1,m=s>>2&1,_=s&3,h=s>>6;s&8&&T(0);var M=6-f,I=_==3?4:_,Q=jr(p,M,I);M+=I;var Z=h?1<<h:f,Y=jr(p,M,Z)+(h==1&&256),z=Y;if(!f){var q=1<<10+(p[5]>>3);z=q+(q>>3)*(p[5]&7);}z>2145386496&&T(1);var X=new H((c==1?Y||z:c?0:z)+12);return X[0]=1,X[4]=4,X[8]=8,{b:M+Z,y:0,l:0,d:Q,w:c&&c!=1?c:X.subarray(12),e:z,o:new qe(X.buffer,0,3),u:Y,c:m,m:Math.min(131072,z)}}else if((d>>4|p[3]<<20)==25481893)return lo(p,4)+8;T(0);},Xe=function(p){for(var c=0;1<<c<=p;++c);return c-1},je=function(p,c,d){var s=(c<<3)+4,f=(p[c]&15)+5;f>d&&T(3);for(var m=1<<f,_=m,h=-1,M=-1,I=-1,Q=m,Z=new io(512+(m<<2)),Y=new so(Z,0,256),z=new Qe(Z,0,256),q=new Qe(Z,512,m),X=512+(m<<1),V=new H(Z,X,m),re=new H(Z,X+m);h<255&&_>0;){var S=Xe(_+1),P=s>>3,ae=(1<<S+1)-1,J=(p[P]|p[P+1]<<8|p[P+2]<<16)>>(s&7)&ae,w=(1<<S)-1,j=ae-_-1,W=J&w;if(W<j?(s+=S,J=W):(s+=S+1,J>w&&(J-=j)),Y[++h]=--J,J==-1?(_+=J,V[--Q]=h):_-=J,!J)do{var ie=s>>3;M=(p[ie]|p[ie+1]<<8)>>(s&7)&3,s+=2,h+=M;}while(M==3)}(h>255||_)&&T(0);for(var b=0,k=(m>>1)+(m>>3)+3,se=m-1,oe=0;oe<=h;++oe){var K=Y[oe];if(K<1){z[oe]=-K;continue}for(I=0;I<K;++I){V[b]=oe;do b=b+k&se;while(b>=Q)}}for(b&&T(0),I=0;I<m;++I){var L=z[V[I]]++,A=re[I]=f-Xe(L);q[I]=(L<<A)-m;}return [s+7>>3,{b:f,s:V,n:re,t:q}]},fo=function(p,c){var d=0,s=-1,f=new H(292),m=p[c],_=f.subarray(0,256),h=f.subarray(256,268),M=new Qe(f.buffer,268);if(m<128){var I=je(p,c+1,6),Q=I[0],Z=I[1];c+=m;var Y=Q<<3,z=p[c];z||T(0);for(var q=0,X=0,V=Z.b,re=V,S=(++c<<3)-8+Xe(z);S-=V,!(S<Y);){var P=S>>3;if(q+=(p[P]|p[P+1]<<8)>>(S&7)&(1<<V)-1,_[++s]=Z.s[q],S-=re,S<Y)break;P=S>>3,X+=(p[P]|p[P+1]<<8)>>(S&7)&(1<<re)-1,_[++s]=Z.s[X],V=Z.n[q],q=Z.t[q],re=Z.n[X],X=Z.t[X];}++s>255&&T(0);}else {for(s=m-127;d<s;d+=2){var ae=p[++c];_[d]=ae>>4,_[d+1]=ae&15;}++c;}var J=0;for(d=0;d<s;++d){var w=_[d];w>11&&T(0),J+=w&&1<<w-1;}var j=Xe(J)+1,W=1<<j,ie=W-J;for(ie&ie-1&&T(0),_[s++]=Xe(ie)+1,d=0;d<s;++d){var w=_[d];++h[_[d]=w&&j+1-w];}var b=new H(W<<1),k=b.subarray(0,W),se=b.subarray(W);for(M[j]=0,d=j;d>0;--d){var oe=M[d];Ye(se,d,oe,M[d-1]=oe+h[d]*(1<<j-d));}for(M[0]!=W&&T(0),d=0;d<s;++d){var K=_[d];if(K){var L=M[K];Ye(k,d,L,M[K]=L+(1<<j-K));}}return [c,{n:se,b:j,s:k}]},vo=je(new H([81,16,99,140,49,198,24,99,12,33,196,24,99,102,102,134,70,146,4]),0,6)[1],mo=je(new H([33,20,196,24,99,140,33,132,16,66,8,33,132,16,66,8,33,68,68,68,68,68,68,68,68,36,9]),0,6)[1],_o=je(new H([32,132,16,66,102,70,68,68,68,68,36,73,2]),0,5)[1],Pr=function(p,c){for(var d=p.length,s=new qe(d),f=0;f<d;++f)s[f]=c,c+=1<<p[f];return s},cr=new H(new qe([0,0,0,0,16843009,50528770,134678020,202050057,269422093]).buffer,0,36),go=Pr(cr,0),dr=new H(new qe([0,0,0,0,0,0,0,0,16843009,50528770,117769220,185207048,252579084,16]).buffer,0,53),ho=Pr(dr,3),Ze=function(p,c,d){var s=p.length,f=c.length,m=p[s-1],_=(1<<d.b)-1,h=-d.b;m||T(0);for(var M=0,I=d.b,Q=(s<<3)-8+Xe(m)-I,Z=-1;Q>h&&Z<f;){var Y=Q>>3,z=(p[Y]|p[Y+1]<<8|p[Y+2]<<16)>>(Q&7);M=(M<<I|z)&_,c[++Z]=d.s[M],Q-=I=d.n[M];}(Q!=h||Z+1!=f)&&T(0);},wo=function(p,c,d){var s=6,f=c.length,m=f+3>>2,_=m<<1,h=m+_;Ze(p.subarray(s,s+=p[0]|p[1]<<8),c.subarray(0,m),d),Ze(p.subarray(s,s+=p[2]|p[3]<<8),c.subarray(m,_),d),Ze(p.subarray(s,s+=p[4]|p[5]<<8),c.subarray(_,h),d),Ze(p.subarray(s),c.subarray(h),d);},yo=function(p,c,d){var s,f=c.b,m=p[f],_=m>>1&3;c.l=m&1;var h=m>>3|p[f+1]<<5|p[f+2]<<13,M=(f+=3)+h;if(_==1)return f>=p.length?void 0:(c.b=f+1,d?(Ye(d,p[f],c.y,c.y+=h),d):Ye(new H(h),p[f]));if(!(M>p.length)){if(_==0)return c.b=M,d?(d.set(p.subarray(f,M),c.y),c.y+=h,d):pr(p,f,M);if(_==2){var I=p[f],Q=I&3,Z=I>>2&3,Y=I>>4,z=0,q=0;Q<2?Z&1?Y|=p[++f]<<4|(Z&2&&p[++f]<<12):Y=I>>3:(q=Z,Z<2?(Y|=(p[++f]&63)<<4,z=p[f]>>6|p[++f]<<2):Z==2?(Y|=p[++f]<<4|(p[++f]&3)<<12,z=p[f]>>2|p[++f]<<6):(Y|=p[++f]<<4|(p[++f]&63)<<12,z=p[f]>>6|p[++f]<<2|p[++f]<<10)),++f;var X=d?d.subarray(c.y,c.y+c.m):new H(c.m),V=X.length-Y;if(Q==0)X.set(p.subarray(f,f+=Y),V);else if(Q==1)Ye(X,p[f++],V);else {var re=c.h;if(Q==2){var S=fo(p,f);z+=f-(f=S[0]),c.h=re=S[1];}else re||T(0);(q?wo:Ze)(p.subarray(f,f+=z),X.subarray(V),re);}var P=p[f++];if(P){P==255?P=(p[f++]|p[f++]<<8)+32512:P>127&&(P=P-128<<8|p[f++]);var ae=p[f++];ae&3&&T(0);for(var J=[mo,_o,vo],w=2;w>-1;--w){var j=ae>>(w<<1)+2&3;if(j==1){var W=new H([0,0,p[f++]]);J[w]={s:W.subarray(2,3),n:W.subarray(0,1),t:new Qe(W.buffer,0,1),b:0};}else j==2?(s=je(p,f,9-(w&1)),f=s[0],J[w]=s[1]):j==3&&(c.t||T(0),J[w]=c.t[w]);}var ie=c.t=J,b=ie[0],k=ie[1],se=ie[2],oe=p[M-1];oe||T(0);var K=(M<<3)-8+Xe(oe)-se.b,L=K>>3,A=0,Le=(p[L]|p[L+1]<<8)>>(K&7)&(1<<se.b)-1;L=(K-=k.b)>>3;var Be=(p[L]|p[L+1]<<8)>>(K&7)&(1<<k.b)-1;L=(K-=b.b)>>3;var Re=(p[L]|p[L+1]<<8)>>(K&7)&(1<<b.b)-1;for(++P;--P;){var Ne=se.s[Le],Pe=se.n[Le],Ve=b.s[Re],ye=b.n[Re],ke=k.s[Be],be=k.n[Be];L=(K-=ke)>>3;var $e=1<<ke,ce=$e+((p[L]|p[L+1]<<8|p[L+2]<<16|p[L+3]<<24)>>>(K&7)&$e-1);L=(K-=dr[Ve])>>3;var me=ho[Ve]+((p[L]|p[L+1]<<8|p[L+2]<<16)>>(K&7)&(1<<dr[Ve])-1);L=(K-=cr[Ne])>>3;var _e=go[Ne]+((p[L]|p[L+1]<<8|p[L+2]<<16)>>(K&7)&(1<<cr[Ne])-1);if(L=(K-=Pe)>>3,Le=se.t[Le]+((p[L]|p[L+1]<<8)>>(K&7)&(1<<Pe)-1),L=(K-=ye)>>3,Re=b.t[Re]+((p[L]|p[L+1]<<8)>>(K&7)&(1<<ye)-1),L=(K-=be)>>3,Be=k.t[Be]+((p[L]|p[L+1]<<8)>>(K&7)&(1<<be)-1),ce>3)c.o[2]=c.o[1],c.o[1]=c.o[0],c.o[0]=ce-=3;else {var Ge=ce-(_e!=0);Ge?(ce=Ge==3?c.o[0]-1:c.o[Ge],Ge>1&&(c.o[2]=c.o[1]),c.o[1]=c.o[0],c.o[0]=ce):ce=c.o[0];}for(var w=0;w<_e;++w)X[A+w]=X[V+w];A+=_e,V+=_e;var Ee=A-ce;if(Ee<0){var De=-Ee,We=c.e+Ee;De>me&&(De=me);for(var w=0;w<De;++w)X[A+w]=c.w[We+w];A+=De,me-=De,Ee=0;}for(var w=0;w<me;++w)X[A+w]=X[Ee+w];A+=me;}if(A!=V)for(;V<X.length;)X[A++]=X[V++];else A=X.length;d?c.y+=A:X=pr(X,0,A);}else if(d){if(c.y+=Y,V)for(var w=0;w<Y;++w)X[w]=X[V+w];}else V&&(X=pr(X,V));return c.b=M,X}T(2);}},bo=function(p,c){if(p.length==1)return p[0];for(var d=new H(c),s=0,f=0;s<p.length;++s){var m=p[s];d.set(m,f),f+=m.length;}return d};function Eo(p,c){for(var d=[],s=+!c,f=0,m=0;p.length;){var _=co(p,s||c);if(typeof _=="object"){for(s?(c=null,_.w.length==_.u&&(d.push(c=_.w),m+=_.u)):(d.push(c),_.e=0);!_.l;){var h=yo(p,_,c);h||T(5),c?_.e=_.y:(d.push(h),m+=h.length,uo(_.w,0,h.length),_.w.set(h,_.w.length-h.length));}f=_.b+_.c*4;}else f=_;p=p.subarray(f);}return bo(d,m)}var Mo=(()=>{typeof document<"u"?document.currentScript?.src:void 0;return function(c={}){var d,s=c,f,m,_=new Promise((e,r)=>{f=e,m=r;}),M=Object.assign({},s),I="./this.program",Q=(e,r)=>{throw r},Z="";function Y(e){return Z+e}var z,q,X=console.log.bind(console),V=console.error.bind(console);Object.assign(s,M),M=null;var re;s.wasmBinary&&(re=s.wasmBinary);var S,P=!1;function J(e,r){e||_e(r);}var w,j,W,b,k,oe;function K(){var e=S.buffer;s.HEAP8=w=new Int8Array(e),s.HEAP16=W=new Int16Array(e),s.HEAPU8=j=new Uint8Array(e),s.HEAPU16=new Uint16Array(e),s.HEAP32=b=new Int32Array(e),s.HEAPU32=k=new Uint32Array(e),s.HEAPF32=new Float32Array(e),s.HEAPF64=oe=new Float64Array(e);}var L=[],A=[],Le=[];function Re(){Ae(L);}function Ne(){!s.noFSInit&&!o.init.initialized&&o.init(),o.ignorePermissions=!1,Ae(A);}function Pe(){Ae(Le);}function Ve(e){A.unshift(e);}var ye=0,be=null;function ce(e){ye++;}function me(e){if(ye--,ye==0&&(be)){var r=be;be=null,r();}}function _e(e){e="Aborted("+e+")",V(e),P=!0,e+=". Build with -sASSERTIONS for more info.";var r=new WebAssembly.RuntimeError(e);throw m(r),r}var Ge="data:application/octet-stream;base64,",Ee=e=>e.startsWith(Ge),De=e=>e.startsWith("file://");function We(){var e="graphvizlib.wasm";return Ee(e)?e:Y(e)}var Je;function fr(e){if(e==Je&&re)return new Uint8Array(re);throw "both async and sync fetching of the wasm failed"}function Jr(e){return re?Promise.resolve().then(()=>fr(e)):z(e).then(r=>new Uint8Array(r),()=>fr(e))}function vr(e,r,t){return Jr(e).then(n=>WebAssembly.instantiate(n,r)).then(t,n=>{V(`failed to asynchronously prepare wasm: ${n}`),_e(n);})}function Sr(e,r,t,n){return !e&&typeof WebAssembly.instantiateStreaming=="function"&&!Ee(r)&&!De(r)&&typeof fetch=="function"?fetch(r,{credentials:"same-origin"}).then(a=>{var i=WebAssembly.instantiateStreaming(a,t);return i.then(n,function(u){return V(`wasm streaming compile failed: ${u}`),V("falling back to ArrayBuffer instantiation"),vr(r,t,n)})}):vr(r,t,n)}function Hr(){return {a:Qt}}function Tr(){var e=Hr();function r(n,a){return N=n.exports,S=N.B,K(),Ve(N.C),me(),N}ce();function t(n){r(n.instance);}return Je||(Je=We()),Sr(re,Je,e,t).catch(m),{}}var R,$,Qr={172808:(e,r)=>{var t=ue(e),n=ue(r);o.createPath("/",C.dirname(t)),o.writeFile(C.join("/",t),n);}};function qr(e){this.name="ExitStatus",this.message=`Program terminated with exit(${e})`,this.status=e;}var Ae=e=>{for(;e.length>0;)e.shift()(s);};class $r{constructor(r){this.excPtr=r,this.ptr=r-24;}set_type(r){k[this.ptr+4>>2]=r;}get_type(){return k[this.ptr+4>>2]}set_destructor(r){k[this.ptr+8>>2]=r;}get_destructor(){return k[this.ptr+8>>2]}set_caught(r){r=r?1:0,w[this.ptr+12]=r;}get_caught(){return w[this.ptr+12]!=0}set_rethrown(r){r=r?1:0,w[this.ptr+13]=r;}get_rethrown(){return w[this.ptr+13]!=0}init(r,t){this.set_adjusted_ptr(0),this.set_type(r),this.set_destructor(t);}set_adjusted_ptr(r){k[this.ptr+16>>2]=r;}get_adjusted_ptr(){return k[this.ptr+16>>2]}get_exception_ptr(){var r=Yr(this.get_type());if(r)return k[this.excPtr>>2];var t=this.get_adjusted_ptr();return t!==0?t:this.excPtr}}var mr=0,Ar=(e,r,t)=>{var n=new $r(e);throw n.init(r,t),mr=e,mr},C={isAbs:e=>e.charAt(0)==="/",splitPath:e=>{var r=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return r.exec(e).slice(1)},normalizeArray:(e,r)=>{for(var t=0,n=e.length-1;n>=0;n--){var a=e[n];a==="."?e.splice(n,1):a===".."?(e.splice(n,1),t++):t&&(e.splice(n,1),t--);}if(r)for(;t;t--)e.unshift("..");return e},normalize:e=>{var r=C.isAbs(e),t=e.substr(-1)==="/";return e=C.normalizeArray(e.split("/").filter(n=>!!n),!r).join("/"),!e&&!r&&(e="."),e&&t&&(e+="/"),(r?"/":"")+e},dirname:e=>{var r=C.splitPath(e),t=r[0],n=r[1];return !t&&!n?".":(n&&(n=n.substr(0,n.length-1)),t+n)},basename:e=>{if(e==="/")return "/";e=C.normalize(e),e=e.replace(/\/$/,"");var r=e.lastIndexOf("/");return r===-1?e:e.substr(r+1)},join:(...e)=>C.normalize(e.join("/")),join2:(e,r)=>C.normalize(e+"/"+r)},et=()=>{if(typeof crypto=="object"&&typeof crypto.getRandomValues=="function")return e=>crypto.getRandomValues(e);_e("initRandomDevice");},_r=e=>(_r=et())(e),he={resolve:(...e)=>{for(var r="",t=!1,n=e.length-1;n>=-1&&!t;n--){var a=n>=0?e[n]:o.cwd();if(typeof a!="string")throw new TypeError("Arguments to path.resolve must be strings");if(!a)return "";r=a+"/"+r,t=C.isAbs(a);}return r=C.normalizeArray(r.split("/").filter(i=>!!i),!t).join("/"),(t?"/":"")+r||"."},relative:(e,r)=>{e=he.resolve(e).substr(1),r=he.resolve(r).substr(1);function t(E){for(var G=0;G<E.length&&E[G]==="";G++);for(var x=E.length-1;x>=0&&E[x]==="";x--);return G>x?[]:E.slice(G,x-G+1)}for(var n=t(e.split("/")),a=t(r.split("/")),i=Math.min(n.length,a.length),u=i,l=0;l<i;l++)if(n[l]!==a[l]){u=l;break}for(var v=[],l=u;l<n.length;l++)v.push("..");return v=v.concat(a.slice(u)),v.join("/")}},gr=typeof TextDecoder<"u"?new TextDecoder:void 0,Fe=(e,r,t)=>{for(var n=r+t,a=r;e[a]&&!(a>=n);)++a;if(a-r>16&&e.buffer&&gr)return gr.decode(e.subarray(r,a));for(var i="";r<a;){var u=e[r++];if(!(u&128)){i+=String.fromCharCode(u);continue}var l=e[r++]&63;if((u&224)==192){i+=String.fromCharCode((u&31)<<6|l);continue}var v=e[r++]&63;if((u&240)==224?u=(u&15)<<12|l<<6|v:u=(u&7)<<18|l<<12|v<<6|e[r++]&63,u<65536)i+=String.fromCharCode(u);else {var E=u-65536;i+=String.fromCharCode(55296|E>>10,56320|E&1023);}}return i},er=[],rr=e=>{for(var r=0,t=0;t<e.length;++t){var n=e.charCodeAt(t);n<=127?r++:n<=2047?r+=2:n>=55296&&n<=57343?(r+=4,++t):r+=3;}return r},tr=(e,r,t,n)=>{if(!(n>0))return 0;for(var a=t,i=t+n-1,u=0;u<e.length;++u){var l=e.charCodeAt(u);if(l>=55296&&l<=57343){var v=e.charCodeAt(++u);l=65536+((l&1023)<<10)|v&1023;}if(l<=127){if(t>=i)break;r[t++]=l;}else if(l<=2047){if(t+1>=i)break;r[t++]=192|l>>6,r[t++]=128|l&63;}else if(l<=65535){if(t+2>=i)break;r[t++]=224|l>>12,r[t++]=128|l>>6&63,r[t++]=128|l&63;}else {if(t+3>=i)break;r[t++]=240|l>>18,r[t++]=128|l>>12&63,r[t++]=128|l>>6&63,r[t++]=128|l&63;}}return r[t]=0,t-a};function or(e,r,t){var n=rr(e)+1,a=new Array(n),i=tr(e,a,0,a.length);return r&&(a.length=i),a}var rt=()=>{if(!er.length){var e=null;if(!e)return null;er=or(e,!0);}return er.shift()},Me={ttys:[],init(){},shutdown(){},register(e,r){Me.ttys[e]={input:[],output:[],ops:r},o.registerDevice(e,Me.stream_ops);},stream_ops:{open(e){var r=Me.ttys[e.node.rdev];if(!r)throw new o.ErrnoError(43);e.tty=r,e.seekable=!1;},close(e){e.tty.ops.fsync(e.tty);},fsync(e){e.tty.ops.fsync(e.tty);},read(e,r,t,n,a){if(!e.tty||!e.tty.ops.get_char)throw new o.ErrnoError(60);for(var i=0,u=0;u<n;u++){var l;try{l=e.tty.ops.get_char(e.tty);}catch{throw new o.ErrnoError(29)}if(l===void 0&&i===0)throw new o.ErrnoError(6);if(l==null)break;i++,r[t+u]=l;}return i&&(e.node.timestamp=Date.now()),i},write(e,r,t,n,a){if(!e.tty||!e.tty.ops.put_char)throw new o.ErrnoError(60);try{for(var i=0;i<n;i++)e.tty.ops.put_char(e.tty,r[t+i]);}catch{throw new o.ErrnoError(29)}return n&&(e.node.timestamp=Date.now()),i}},default_tty_ops:{get_char(e){return rt()},put_char(e,r){r===null||r===10?(X(Fe(e.output,0)),e.output=[]):r!=0&&e.output.push(r);},fsync(e){e.output&&e.output.length>0&&(X(Fe(e.output,0)),e.output=[]);},ioctl_tcgets(e){return {c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(e,r,t){return 0},ioctl_tiocgwinsz(e){return [24,80]}},default_tty1_ops:{put_char(e,r){r===null||r===10?(V(Fe(e.output,0)),e.output=[]):r!=0&&e.output.push(r);},fsync(e){e.output&&e.output.length>0&&(V(Fe(e.output,0)),e.output=[]);}}},tt=(e,r)=>(j.fill(0,e,e+r),e),hr=(e,r)=>Math.ceil(e/r)*r,wr=e=>{e=hr(e,65536);var r=Zr(65536,e);return r?tt(r,e):0},D={ops_table:null,mount(e){return D.createNode(null,"/",16895,0)},createNode(e,r,t,n){if(o.isBlkdev(t)||o.isFIFO(t))throw new o.ErrnoError(63);D.ops_table||={dir:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr,lookup:D.node_ops.lookup,mknod:D.node_ops.mknod,rename:D.node_ops.rename,unlink:D.node_ops.unlink,rmdir:D.node_ops.rmdir,readdir:D.node_ops.readdir,symlink:D.node_ops.symlink},stream:{llseek:D.stream_ops.llseek}},file:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr},stream:{llseek:D.stream_ops.llseek,read:D.stream_ops.read,write:D.stream_ops.write,allocate:D.stream_ops.allocate,mmap:D.stream_ops.mmap,msync:D.stream_ops.msync}},link:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr,readlink:D.node_ops.readlink},stream:{}},chrdev:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr},stream:o.chrdev_stream_ops}};var a=o.createNode(e,r,t,n);return o.isDir(a.mode)?(a.node_ops=D.ops_table.dir.node,a.stream_ops=D.ops_table.dir.stream,a.contents={}):o.isFile(a.mode)?(a.node_ops=D.ops_table.file.node,a.stream_ops=D.ops_table.file.stream,a.usedBytes=0,a.contents=null):o.isLink(a.mode)?(a.node_ops=D.ops_table.link.node,a.stream_ops=D.ops_table.link.stream):o.isChrdev(a.mode)&&(a.node_ops=D.ops_table.chrdev.node,a.stream_ops=D.ops_table.chrdev.stream),a.timestamp=Date.now(),e&&(e.contents[r]=a,e.timestamp=a.timestamp),a},getFileDataAsTypedArray(e){return e.contents?e.contents.subarray?e.contents.subarray(0,e.usedBytes):new Uint8Array(e.contents):new Uint8Array(0)},expandFileStorage(e,r){var t=e.contents?e.contents.length:0;if(!(t>=r)){var n=1048576;r=Math.max(r,t*(t<n?2:1.125)>>>0),t!=0&&(r=Math.max(r,256));var a=e.contents;e.contents=new Uint8Array(r),e.usedBytes>0&&e.contents.set(a.subarray(0,e.usedBytes),0);}},resizeFileStorage(e,r){if(e.usedBytes!=r)if(r==0)e.contents=null,e.usedBytes=0;else {var t=e.contents;e.contents=new Uint8Array(r),t&&e.contents.set(t.subarray(0,Math.min(r,e.usedBytes))),e.usedBytes=r;}},node_ops:{getattr(e){var r={};return r.dev=o.isChrdev(e.mode)?e.id:1,r.ino=e.id,r.mode=e.mode,r.nlink=1,r.uid=0,r.gid=0,r.rdev=e.rdev,o.isDir(e.mode)?r.size=4096:o.isFile(e.mode)?r.size=e.usedBytes:o.isLink(e.mode)?r.size=e.link.length:r.size=0,r.atime=new Date(e.timestamp),r.mtime=new Date(e.timestamp),r.ctime=new Date(e.timestamp),r.blksize=4096,r.blocks=Math.ceil(r.size/r.blksize),r},setattr(e,r){r.mode!==void 0&&(e.mode=r.mode),r.timestamp!==void 0&&(e.timestamp=r.timestamp),r.size!==void 0&&D.resizeFileStorage(e,r.size);},lookup(e,r){throw o.genericErrors[44]},mknod(e,r,t,n){return D.createNode(e,r,t,n)},rename(e,r,t){if(o.isDir(e.mode)){var n;try{n=o.lookupNode(r,t);}catch{}if(n)for(var a in n.contents)throw new o.ErrnoError(55)}delete e.parent.contents[e.name],e.parent.timestamp=Date.now(),e.name=t,r.contents[t]=e,r.timestamp=e.parent.timestamp;},unlink(e,r){delete e.contents[r],e.timestamp=Date.now();},rmdir(e,r){var t=o.lookupNode(e,r);for(var n in t.contents)throw new o.ErrnoError(55);delete e.contents[r],e.timestamp=Date.now();},readdir(e){var r=[".",".."];for(var t of Object.keys(e.contents))r.push(t);return r},symlink(e,r,t){var n=D.createNode(e,r,41471,0);return n.link=t,n},readlink(e){if(!o.isLink(e.mode))throw new o.ErrnoError(28);return e.link}},stream_ops:{read(e,r,t,n,a){var i=e.node.contents;if(a>=e.node.usedBytes)return 0;var u=Math.min(e.node.usedBytes-a,n);if(u>8&&i.subarray)r.set(i.subarray(a,a+u),t);else for(var l=0;l<u;l++)r[t+l]=i[a+l];return u},write(e,r,t,n,a,i){if(r.buffer===w.buffer&&(i=!1),!n)return 0;var u=e.node;if(u.timestamp=Date.now(),r.subarray&&(!u.contents||u.contents.subarray)){if(i)return u.contents=r.subarray(t,t+n),u.usedBytes=n,n;if(u.usedBytes===0&&a===0)return u.contents=r.slice(t,t+n),u.usedBytes=n,n;if(a+n<=u.usedBytes)return u.contents.set(r.subarray(t,t+n),a),n}if(D.expandFileStorage(u,a+n),u.contents.subarray&&r.subarray)u.contents.set(r.subarray(t,t+n),a);else for(var l=0;l<n;l++)u.contents[a+l]=r[t+l];return u.usedBytes=Math.max(u.usedBytes,a+n),n},llseek(e,r,t){var n=r;if(t===1?n+=e.position:t===2&&o.isFile(e.node.mode)&&(n+=e.node.usedBytes),n<0)throw new o.ErrnoError(28);return n},allocate(e,r,t){D.expandFileStorage(e.node,r+t),e.node.usedBytes=Math.max(e.node.usedBytes,r+t);},mmap(e,r,t,n,a){if(!o.isFile(e.node.mode))throw new o.ErrnoError(43);var i,u,l=e.node.contents;if(!(a&2)&&l.buffer===w.buffer)u=!1,i=l.byteOffset;else {if((t>0||t+r<l.length)&&(l.subarray?l=l.subarray(t,t+r):l=Array.prototype.slice.call(l,t,t+r)),u=!0,i=wr(r),!i)throw new o.ErrnoError(48);w.set(l,i);}return {ptr:i,allocated:u}},msync(e,r,t,n,a){return D.stream_ops.write(e,r,0,n,t,!1),0}}},ot=(e,r,t,n)=>{var a=`al ${e}`;z(e).then(i=>{r(new Uint8Array(i)),a&&me();},i=>{if(t)t();else throw `Loading data file "${e}" failed.`}),a&&ce();},nt=(e,r,t,n,a,i)=>{o.createDataFile(e,r,t,n,a,i);},at=[],it=(e,r,t,n)=>{typeof Browser<"u"&&Browser.init();var a=!1;return at.forEach(i=>{a||i.canHandle(r)&&(i.handle(e,r,t,n),a=!0);}),a},st=(e,r,t,n,a,i,u,l,v,E)=>{var G=r?he.resolve(C.join2(e,r)):e;function B(y){function O(te){E?.(),l||nt(e,r,te,n,a,v),i?.(),me();}it(y,G,O,()=>{u?.(),me();})||O(y);}ce(),typeof t=="string"?ot(t,B,u):B(t);},ut=e=>{var r={r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090},t=r[e];if(typeof t>"u")throw new Error(`Unknown file open mode: ${e}`);return t},nr=(e,r)=>{var t=0;return e&&(t|=365),r&&(t|=146),t},o={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:!1,ignorePermissions:!0,ErrnoError:class{constructor(e){this.name="ErrnoError",this.errno=e;}},genericErrors:{},filesystems:null,syncFSRequests:0,FSStream:class{constructor(){this.shared={};}get object(){return this.node}set object(e){this.node=e;}get isRead(){return (this.flags&2097155)!==1}get isWrite(){return (this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(e){this.shared.flags=e;}get position(){return this.shared.position}set position(e){this.shared.position=e;}},FSNode:class{constructor(e,r,t,n){e||(e=this),this.parent=e,this.mount=e.mount,this.mounted=null,this.id=o.nextInode++,this.name=r,this.mode=t,this.node_ops={},this.stream_ops={},this.rdev=n,this.readMode=365,this.writeMode=146;}get read(){return (this.mode&this.readMode)===this.readMode}set read(e){e?this.mode|=this.readMode:this.mode&=~this.readMode;}get write(){return (this.mode&this.writeMode)===this.writeMode}set write(e){e?this.mode|=this.writeMode:this.mode&=~this.writeMode;}get isFolder(){return o.isDir(this.mode)}get isDevice(){return o.isChrdev(this.mode)}},lookupPath(e,r={}){if(e=he.resolve(e),!e)return {path:"",node:null};var t={follow_mount:!0,recurse_count:0};if(r=Object.assign(t,r),r.recurse_count>8)throw new o.ErrnoError(32);for(var n=e.split("/").filter(x=>!!x),a=o.root,i="/",u=0;u<n.length;u++){var l=u===n.length-1;if(l&&r.parent)break;if(a=o.lookupNode(a,n[u]),i=C.join2(i,n[u]),o.isMountpoint(a)&&(!l||l&&r.follow_mount)&&(a=a.mounted.root),!l||r.follow)for(var v=0;o.isLink(a.mode);){var E=o.readlink(i);i=he.resolve(C.dirname(i),E);var G=o.lookupPath(i,{recurse_count:r.recurse_count+1});if(a=G.node,v++>40)throw new o.ErrnoError(32)}}return {path:i,node:a}},getPath(e){for(var r;;){if(o.isRoot(e)){var t=e.mount.mountpoint;return r?t[t.length-1]!=="/"?`${t}/${r}`:t+r:t}r=r?`${e.name}/${r}`:e.name,e=e.parent;}},hashName(e,r){for(var t=0,n=0;n<r.length;n++)t=(t<<5)-t+r.charCodeAt(n)|0;return (e+t>>>0)%o.nameTable.length},hashAddNode(e){var r=o.hashName(e.parent.id,e.name);e.name_next=o.nameTable[r],o.nameTable[r]=e;},hashRemoveNode(e){var r=o.hashName(e.parent.id,e.name);if(o.nameTable[r]===e)o.nameTable[r]=e.name_next;else for(var t=o.nameTable[r];t;){if(t.name_next===e){t.name_next=e.name_next;break}t=t.name_next;}},lookupNode(e,r){var t=o.mayLookup(e);if(t)throw new o.ErrnoError(t);for(var n=o.hashName(e.id,r),a=o.nameTable[n];a;a=a.name_next){var i=a.name;if(a.parent.id===e.id&&i===r)return a}return o.lookup(e,r)},createNode(e,r,t,n){var a=new o.FSNode(e,r,t,n);return o.hashAddNode(a),a},destroyNode(e){o.hashRemoveNode(e);},isRoot(e){return e===e.parent},isMountpoint(e){return !!e.mounted},isFile(e){return (e&61440)===32768},isDir(e){return (e&61440)===16384},isLink(e){return (e&61440)===40960},isChrdev(e){return (e&61440)===8192},isBlkdev(e){return (e&61440)===24576},isFIFO(e){return (e&61440)===4096},isSocket(e){return (e&49152)===49152},flagsToPermissionString(e){var r=["r","w","rw"][e&3];return e&512&&(r+="w"),r},nodePermissions(e,r){return o.ignorePermissions?0:r.includes("r")&&!(e.mode&292)||r.includes("w")&&!(e.mode&146)||r.includes("x")&&!(e.mode&73)?2:0},mayLookup(e){if(!o.isDir(e.mode))return 54;var r=o.nodePermissions(e,"x");return r||(e.node_ops.lookup?0:2)},mayCreate(e,r){try{var t=o.lookupNode(e,r);return 20}catch{}return o.nodePermissions(e,"wx")},mayDelete(e,r,t){var n;try{n=o.lookupNode(e,r);}catch(i){return i.errno}var a=o.nodePermissions(e,"wx");if(a)return a;if(t){if(!o.isDir(n.mode))return 54;if(o.isRoot(n)||o.getPath(n)===o.cwd())return 10}else if(o.isDir(n.mode))return 31;return 0},mayOpen(e,r){return e?o.isLink(e.mode)?32:o.isDir(e.mode)&&(o.flagsToPermissionString(r)!=="r"||r&512)?31:o.nodePermissions(e,o.flagsToPermissionString(r)):44},MAX_OPEN_FDS:4096,nextfd(){for(var e=0;e<=o.MAX_OPEN_FDS;e++)if(!o.streams[e])return e;throw new o.ErrnoError(33)},getStreamChecked(e){var r=o.getStream(e);if(!r)throw new o.ErrnoError(8);return r},getStream:e=>o.streams[e],createStream(e,r=-1){return e=Object.assign(new o.FSStream,e),r==-1&&(r=o.nextfd()),e.fd=r,o.streams[r]=e,e},closeStream(e){o.streams[e]=null;},dupStream(e,r=-1){var t=o.createStream(e,r);return t.stream_ops?.dup?.(t),t},chrdev_stream_ops:{open(e){var r=o.getDevice(e.node.rdev);e.stream_ops=r.stream_ops,e.stream_ops.open?.(e);},llseek(){throw new o.ErrnoError(70)}},major:e=>e>>8,minor:e=>e&255,makedev:(e,r)=>e<<8|r,registerDevice(e,r){o.devices[e]={stream_ops:r};},getDevice:e=>o.devices[e],getMounts(e){for(var r=[],t=[e];t.length;){var n=t.pop();r.push(n),t.push(...n.mounts);}return r},syncfs(e,r){typeof e=="function"&&(r=e,e=!1),o.syncFSRequests++,o.syncFSRequests>1&&V(`warning: ${o.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`);var t=o.getMounts(o.root.mount),n=0;function a(u){return o.syncFSRequests--,r(u)}function i(u){if(u)return i.errored?void 0:(i.errored=!0,a(u));++n>=t.length&&a(null);}t.forEach(u=>{if(!u.type.syncfs)return i(null);u.type.syncfs(u,e,i);});},mount(e,r,t){var n=t==="/",a=!t,i;if(n&&o.root)throw new o.ErrnoError(10);if(!n&&!a){var u=o.lookupPath(t,{follow_mount:!1});if(t=u.path,i=u.node,o.isMountpoint(i))throw new o.ErrnoError(10);if(!o.isDir(i.mode))throw new o.ErrnoError(54)}var l={type:e,opts:r,mountpoint:t,mounts:[]},v=e.mount(l);return v.mount=l,l.root=v,n?o.root=v:i&&(i.mounted=l,i.mount&&i.mount.mounts.push(l)),v},unmount(e){var r=o.lookupPath(e,{follow_mount:!1});if(!o.isMountpoint(r.node))throw new o.ErrnoError(28);var t=r.node,n=t.mounted,a=o.getMounts(n);Object.keys(o.nameTable).forEach(u=>{for(var l=o.nameTable[u];l;){var v=l.name_next;a.includes(l.mount)&&o.destroyNode(l),l=v;}}),t.mounted=null;var i=t.mount.mounts.indexOf(n);t.mount.mounts.splice(i,1);},lookup(e,r){return e.node_ops.lookup(e,r)},mknod(e,r,t){var n=o.lookupPath(e,{parent:!0}),a=n.node,i=C.basename(e);if(!i||i==="."||i==="..")throw new o.ErrnoError(28);var u=o.mayCreate(a,i);if(u)throw new o.ErrnoError(u);if(!a.node_ops.mknod)throw new o.ErrnoError(63);return a.node_ops.mknod(a,i,r,t)},create(e,r){return r=r!==void 0?r:438,r&=4095,r|=32768,o.mknod(e,r,0)},mkdir(e,r){return r=r!==void 0?r:511,r&=1023,r|=16384,o.mknod(e,r,0)},mkdirTree(e,r){for(var t=e.split("/"),n="",a=0;a<t.length;++a)if(t[a]){n+="/"+t[a];try{o.mkdir(n,r);}catch(i){if(i.errno!=20)throw i}}},mkdev(e,r,t){return typeof t>"u"&&(t=r,r=438),r|=8192,o.mknod(e,r,t)},symlink(e,r){if(!he.resolve(e))throw new o.ErrnoError(44);var t=o.lookupPath(r,{parent:!0}),n=t.node;if(!n)throw new o.ErrnoError(44);var a=C.basename(r),i=o.mayCreate(n,a);if(i)throw new o.ErrnoError(i);if(!n.node_ops.symlink)throw new o.ErrnoError(63);return n.node_ops.symlink(n,a,e)},rename(e,r){var t=C.dirname(e),n=C.dirname(r),a=C.basename(e),i=C.basename(r),u,l,v;if(u=o.lookupPath(e,{parent:!0}),l=u.node,u=o.lookupPath(r,{parent:!0}),v=u.node,!l||!v)throw new o.ErrnoError(44);if(l.mount!==v.mount)throw new o.ErrnoError(75);var E=o.lookupNode(l,a),G=he.relative(e,n);if(G.charAt(0)!==".")throw new o.ErrnoError(28);if(G=he.relative(r,t),G.charAt(0)!==".")throw new o.ErrnoError(55);var x;try{x=o.lookupNode(v,i);}catch{}if(E!==x){var B=o.isDir(E.mode),y=o.mayDelete(l,a,B);if(y)throw new o.ErrnoError(y);if(y=x?o.mayDelete(v,i,B):o.mayCreate(v,i),y)throw new o.ErrnoError(y);if(!l.node_ops.rename)throw new o.ErrnoError(63);if(o.isMountpoint(E)||x&&o.isMountpoint(x))throw new o.ErrnoError(10);if(v!==l&&(y=o.nodePermissions(l,"w"),y))throw new o.ErrnoError(y);o.hashRemoveNode(E);try{l.node_ops.rename(E,v,i),E.parent=v;}catch(O){throw O}finally{o.hashAddNode(E);}}},rmdir(e){var r=o.lookupPath(e,{parent:!0}),t=r.node,n=C.basename(e),a=o.lookupNode(t,n),i=o.mayDelete(t,n,!0);if(i)throw new o.ErrnoError(i);if(!t.node_ops.rmdir)throw new o.ErrnoError(63);if(o.isMountpoint(a))throw new o.ErrnoError(10);t.node_ops.rmdir(t,n),o.destroyNode(a);},readdir(e){var r=o.lookupPath(e,{follow:!0}),t=r.node;if(!t.node_ops.readdir)throw new o.ErrnoError(54);return t.node_ops.readdir(t)},unlink(e){var r=o.lookupPath(e,{parent:!0}),t=r.node;if(!t)throw new o.ErrnoError(44);var n=C.basename(e),a=o.lookupNode(t,n),i=o.mayDelete(t,n,!1);if(i)throw new o.ErrnoError(i);if(!t.node_ops.unlink)throw new o.ErrnoError(63);if(o.isMountpoint(a))throw new o.ErrnoError(10);t.node_ops.unlink(t,n),o.destroyNode(a);},readlink(e){var r=o.lookupPath(e),t=r.node;if(!t)throw new o.ErrnoError(44);if(!t.node_ops.readlink)throw new o.ErrnoError(28);return he.resolve(o.getPath(t.parent),t.node_ops.readlink(t))},stat(e,r){var t=o.lookupPath(e,{follow:!r}),n=t.node;if(!n)throw new o.ErrnoError(44);if(!n.node_ops.getattr)throw new o.ErrnoError(63);return n.node_ops.getattr(n)},lstat(e){return o.stat(e,!0)},chmod(e,r,t){var n;if(typeof e=="string"){var a=o.lookupPath(e,{follow:!t});n=a.node;}else n=e;if(!n.node_ops.setattr)throw new o.ErrnoError(63);n.node_ops.setattr(n,{mode:r&4095|n.mode&-4096,timestamp:Date.now()});},lchmod(e,r){o.chmod(e,r,!0);},fchmod(e,r){var t=o.getStreamChecked(e);o.chmod(t.node,r);},chown(e,r,t,n){var a;if(typeof e=="string"){var i=o.lookupPath(e,{follow:!n});a=i.node;}else a=e;if(!a.node_ops.setattr)throw new o.ErrnoError(63);a.node_ops.setattr(a,{timestamp:Date.now()});},lchown(e,r,t){o.chown(e,r,t,!0);},fchown(e,r,t){var n=o.getStreamChecked(e);o.chown(n.node,r,t);},truncate(e,r){if(r<0)throw new o.ErrnoError(28);var t;if(typeof e=="string"){var n=o.lookupPath(e,{follow:!0});t=n.node;}else t=e;if(!t.node_ops.setattr)throw new o.ErrnoError(63);if(o.isDir(t.mode))throw new o.ErrnoError(31);if(!o.isFile(t.mode))throw new o.ErrnoError(28);var a=o.nodePermissions(t,"w");if(a)throw new o.ErrnoError(a);t.node_ops.setattr(t,{size:r,timestamp:Date.now()});},ftruncate(e,r){var t=o.getStreamChecked(e);if(!(t.flags&2097155))throw new o.ErrnoError(28);o.truncate(t.node,r);},utime(e,r,t){var n=o.lookupPath(e,{follow:!0}),a=n.node;a.node_ops.setattr(a,{timestamp:Math.max(r,t)});},open(e,r,t){if(e==="")throw new o.ErrnoError(44);r=typeof r=="string"?ut(r):r,r&64?(t=typeof t>"u"?438:t,t=t&4095|32768):t=0;var n;if(typeof e=="object")n=e;else {e=C.normalize(e);try{var a=o.lookupPath(e,{follow:!(r&131072)});n=a.node;}catch{}}var i=!1;if(r&64)if(n){if(r&128)throw new o.ErrnoError(20)}else n=o.mknod(e,t,0),i=!0;if(!n)throw new o.ErrnoError(44);if(o.isChrdev(n.mode)&&(r&=-513),r&65536&&!o.isDir(n.mode))throw new o.ErrnoError(54);if(!i){var u=o.mayOpen(n,r);if(u)throw new o.ErrnoError(u)}r&512&&!i&&o.truncate(n,0),r&=-131713;var l=o.createStream({node:n,path:o.getPath(n),flags:r,seekable:!0,position:0,stream_ops:n.stream_ops,ungotten:[],error:!1});return l.stream_ops.open&&l.stream_ops.open(l),s.logReadFiles&&!(r&1)&&(o.readFiles||(o.readFiles={}),e in o.readFiles||(o.readFiles[e]=1)),l},close(e){if(o.isClosed(e))throw new o.ErrnoError(8);e.getdents&&(e.getdents=null);try{e.stream_ops.close&&e.stream_ops.close(e);}catch(r){throw r}finally{o.closeStream(e.fd);}e.fd=null;},isClosed(e){return e.fd===null},llseek(e,r,t){if(o.isClosed(e))throw new o.ErrnoError(8);if(!e.seekable||!e.stream_ops.llseek)throw new o.ErrnoError(70);if(t!=0&&t!=1&&t!=2)throw new o.ErrnoError(28);return e.position=e.stream_ops.llseek(e,r,t),e.ungotten=[],e.position},read(e,r,t,n,a){if(n<0||a<0)throw new o.ErrnoError(28);if(o.isClosed(e))throw new o.ErrnoError(8);if((e.flags&2097155)===1)throw new o.ErrnoError(8);if(o.isDir(e.node.mode))throw new o.ErrnoError(31);if(!e.stream_ops.read)throw new o.ErrnoError(28);var i=typeof a<"u";if(!i)a=e.position;else if(!e.seekable)throw new o.ErrnoError(70);var u=e.stream_ops.read(e,r,t,n,a);return i||(e.position+=u),u},write(e,r,t,n,a,i){if(n<0||a<0)throw new o.ErrnoError(28);if(o.isClosed(e))throw new o.ErrnoError(8);if(!(e.flags&2097155))throw new o.ErrnoError(8);if(o.isDir(e.node.mode))throw new o.ErrnoError(31);if(!e.stream_ops.write)throw new o.ErrnoError(28);e.seekable&&e.flags&1024&&o.llseek(e,0,2);var u=typeof a<"u";if(!u)a=e.position;else if(!e.seekable)throw new o.ErrnoError(70);var l=e.stream_ops.write(e,r,t,n,a,i);return u||(e.position+=l),l},allocate(e,r,t){if(o.isClosed(e))throw new o.ErrnoError(8);if(r<0||t<=0)throw new o.ErrnoError(28);if(!(e.flags&2097155))throw new o.ErrnoError(8);if(!o.isFile(e.node.mode)&&!o.isDir(e.node.mode))throw new o.ErrnoError(43);if(!e.stream_ops.allocate)throw new o.ErrnoError(138);e.stream_ops.allocate(e,r,t);},mmap(e,r,t,n,a){if(n&2&&!(a&2)&&(e.flags&2097155)!==2)throw new o.ErrnoError(2);if((e.flags&2097155)===1)throw new o.ErrnoError(2);if(!e.stream_ops.mmap)throw new o.ErrnoError(43);return e.stream_ops.mmap(e,r,t,n,a)},msync(e,r,t,n,a){return e.stream_ops.msync?e.stream_ops.msync(e,r,t,n,a):0},ioctl(e,r,t){if(!e.stream_ops.ioctl)throw new o.ErrnoError(59);return e.stream_ops.ioctl(e,r,t)},readFile(e,r={}){if(r.flags=r.flags||0,r.encoding=r.encoding||"binary",r.encoding!=="utf8"&&r.encoding!=="binary")throw new Error(`Invalid encoding type "${r.encoding}"`);var t,n=o.open(e,r.flags),a=o.stat(e),i=a.size,u=new Uint8Array(i);return o.read(n,u,0,i,0),r.encoding==="utf8"?t=Fe(u,0):r.encoding==="binary"&&(t=u),o.close(n),t},writeFile(e,r,t={}){t.flags=t.flags||577;var n=o.open(e,t.flags,t.mode);if(typeof r=="string"){var a=new Uint8Array(rr(r)+1),i=tr(r,a,0,a.length);o.write(n,a,0,i,void 0,t.canOwn);}else if(ArrayBuffer.isView(r))o.write(n,r,0,r.byteLength,void 0,t.canOwn);else throw new Error("Unsupported data type");o.close(n);},cwd:()=>o.currentPath,chdir(e){var r=o.lookupPath(e,{follow:!0});if(r.node===null)throw new o.ErrnoError(44);if(!o.isDir(r.node.mode))throw new o.ErrnoError(54);var t=o.nodePermissions(r.node,"x");if(t)throw new o.ErrnoError(t);o.currentPath=r.path;},createDefaultDirectories(){o.mkdir("/tmp"),o.mkdir("/home"),o.mkdir("/home/web_user");},createDefaultDevices(){o.mkdir("/dev"),o.registerDevice(o.makedev(1,3),{read:()=>0,write:(n,a,i,u,l)=>u}),o.mkdev("/dev/null",o.makedev(1,3)),Me.register(o.makedev(5,0),Me.default_tty_ops),Me.register(o.makedev(6,0),Me.default_tty1_ops),o.mkdev("/dev/tty",o.makedev(5,0)),o.mkdev("/dev/tty1",o.makedev(6,0));var e=new Uint8Array(1024),r=0,t=()=>(r===0&&(r=_r(e).byteLength),e[--r]);o.createDevice("/dev","random",t),o.createDevice("/dev","urandom",t),o.mkdir("/dev/shm"),o.mkdir("/dev/shm/tmp");},createSpecialDirectories(){o.mkdir("/proc");var e=o.mkdir("/proc/self");o.mkdir("/proc/self/fd"),o.mount({mount(){var r=o.createNode(e,"fd",16895,73);return r.node_ops={lookup(t,n){var a=+n,i=o.getStreamChecked(a),u={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>i.path}};return u.parent=u,u}},r}},{},"/proc/self/fd");},createStandardStreams(){s.stdin?o.createDevice("/dev","stdin",s.stdin):o.symlink("/dev/tty","/dev/stdin"),s.stdout?o.createDevice("/dev","stdout",null,s.stdout):o.symlink("/dev/tty","/dev/stdout"),s.stderr?o.createDevice("/dev","stderr",null,s.stderr):o.symlink("/dev/tty1","/dev/stderr");o.open("/dev/stdin",0);o.open("/dev/stdout",1);o.open("/dev/stderr",1);},staticInit(){[44].forEach(e=>{o.genericErrors[e]=new o.ErrnoError(e),o.genericErrors[e].stack="<generic error, no stack>";}),o.nameTable=new Array(4096),o.mount(D,{},"/"),o.createDefaultDirectories(),o.createDefaultDevices(),o.createSpecialDirectories(),o.filesystems={MEMFS:D};},init(e,r,t){o.init.initialized=!0,s.stdin=e||s.stdin,s.stdout=r||s.stdout,s.stderr=t||s.stderr,o.createStandardStreams();},quit(){o.init.initialized=!1;for(var e=0;e<o.streams.length;e++){var r=o.streams[e];r&&o.close(r);}},findObject(e,r){var t=o.analyzePath(e,r);return t.exists?t.object:null},analyzePath(e,r){try{var t=o.lookupPath(e,{follow:!r});e=t.path;}catch{}var n={isRoot:!1,exists:!1,error:0,name:null,path:null,object:null,parentExists:!1,parentPath:null,parentObject:null};try{var t=o.lookupPath(e,{parent:!0});n.parentExists=!0,n.parentPath=t.path,n.parentObject=t.node,n.name=C.basename(e),t=o.lookupPath(e,{follow:!r}),n.exists=!0,n.path=t.path,n.object=t.node,n.name=t.node.name,n.isRoot=t.path==="/";}catch(a){n.error=a.errno;}return n},createPath(e,r,t,n){e=typeof e=="string"?e:o.getPath(e);for(var a=r.split("/").reverse();a.length;){var i=a.pop();if(i){var u=C.join2(e,i);try{o.mkdir(u);}catch{}e=u;}}return u},createFile(e,r,t,n,a){var i=C.join2(typeof e=="string"?e:o.getPath(e),r),u=nr(n,a);return o.create(i,u)},createDataFile(e,r,t,n,a,i){var u=r;e&&(e=typeof e=="string"?e:o.getPath(e),u=r?C.join2(e,r):e);var l=nr(n,a),v=o.create(u,l);if(t){if(typeof t=="string"){for(var E=new Array(t.length),G=0,x=t.length;G<x;++G)E[G]=t.charCodeAt(G);t=E;}o.chmod(v,l|146);var B=o.open(v,577);o.write(B,t,0,t.length,0,i),o.close(B),o.chmod(v,l);}},createDevice(e,r,t,n){var a=C.join2(typeof e=="string"?e:o.getPath(e),r),i=nr(!!t,!!n);o.createDevice.major||(o.createDevice.major=64);var u=o.makedev(o.createDevice.major++,0);return o.registerDevice(u,{open(l){l.seekable=!1;},close(l){n?.buffer?.length&&n(10);},read(l,v,E,G,x){for(var B=0,y=0;y<G;y++){var O;try{O=t();}catch{throw new o.ErrnoError(29)}if(O===void 0&&B===0)throw new o.ErrnoError(6);if(O==null)break;B++,v[E+y]=O;}return B&&(l.node.timestamp=Date.now()),B},write(l,v,E,G,x){for(var B=0;B<G;B++)try{n(v[E+B]);}catch{throw new o.ErrnoError(29)}return G&&(l.node.timestamp=Date.now()),B}}),o.mkdev(a,i,u)},forceLoadFile(e){if(e.isDevice||e.isFolder||e.link||e.contents)return !0;if(typeof XMLHttpRequest<"u")throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");try{e.contents=q(e.url),e.usedBytes=e.contents.length;}catch{throw new o.ErrnoError(29)}},createLazyFile(e,r,t,n,a){class i{constructor(){this.lengthKnown=!1,this.chunks=[];}get(y){if(!(y>this.length-1||y<0)){var O=y%this.chunkSize,te=y/this.chunkSize|0;return this.getter(te)[O]}}setDataGetter(y){this.getter=y;}cacheLength(){var y=new XMLHttpRequest;if(y.open("HEAD",t,!1),y.send(null),!(y.status>=200&&y.status<300||y.status===304))throw new Error("Couldn't load "+t+". Status: "+y.status);var O=Number(y.getResponseHeader("Content-length")),te,de=(te=y.getResponseHeader("Accept-Ranges"))&&te==="bytes",ne=(te=y.getResponseHeader("Content-Encoding"))&&te==="gzip",fe=1024*1024;de||(fe=O);var ve=(ge,xe)=>{if(ge>xe)throw new Error("invalid range ("+ge+", "+xe+") or no bytes requested!");if(xe>O-1)throw new Error("only "+O+" bytes available! programmer error!");var ee=new XMLHttpRequest;if(ee.open("GET",t,!1),O!==fe&&ee.setRequestHeader("Range","bytes="+ge+"-"+xe),ee.responseType="arraybuffer",ee.overrideMimeType&&ee.overrideMimeType("text/plain; charset=x-user-defined"),ee.send(null),!(ee.status>=200&&ee.status<300||ee.status===304))throw new Error("Couldn't load "+t+". Status: "+ee.status);return ee.response!==void 0?new Uint8Array(ee.response||[]):or(ee.responseText||"",!0)},Ie=this;Ie.setDataGetter(ge=>{var xe=ge*fe,ee=(ge+1)*fe-1;if(ee=Math.min(ee,O-1),typeof Ie.chunks[ge]>"u"&&(Ie.chunks[ge]=ve(xe,ee)),typeof Ie.chunks[ge]>"u")throw new Error("doXHR failed!");return Ie.chunks[ge]}),(ne||!O)&&(fe=O=1,O=this.getter(0).length,fe=O,X("LazyFiles on gzip forces download of the whole file when length is accessed")),this._length=O,this._chunkSize=fe,this.lengthKnown=!0;}get length(){return this.lengthKnown||this.cacheLength(),this._length}get chunkSize(){return this.lengthKnown||this.cacheLength(),this._chunkSize}}if(typeof XMLHttpRequest<"u"){throw "Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var u=new i,l={isDevice:!1,contents:u};}else var l={isDevice:!1,url:t};var v=o.createFile(e,r,l,n,a);l.contents?v.contents=l.contents:l.url&&(v.contents=null,v.url=l.url),Object.defineProperties(v,{usedBytes:{get:function(){return this.contents.length}}});var E={},G=Object.keys(v.stream_ops);G.forEach(B=>{var y=v.stream_ops[B];E[B]=(...O)=>(o.forceLoadFile(v),y(...O));});function x(B,y,O,te,de){var ne=B.node.contents;if(de>=ne.length)return 0;var fe=Math.min(ne.length-de,te);if(ne.slice)for(var ve=0;ve<fe;ve++)y[O+ve]=ne[de+ve];else for(var ve=0;ve<fe;ve++)y[O+ve]=ne.get(de+ve);return fe}return E.read=(B,y,O,te,de)=>(o.forceLoadFile(v),x(B,y,O,te,de)),E.mmap=(B,y,O,te,de)=>{o.forceLoadFile(v);var ne=wr(y);if(!ne)throw new o.ErrnoError(48);return x(B,w,ne,y,O),{ptr:ne,allocated:!0}},v.stream_ops=E,v}},ue=(e,r)=>e?Fe(j,e,r):"",U={DEFAULT_POLLMASK:5,calculateAt(e,r,t){if(C.isAbs(r))return r;var n;if(e===-100)n=o.cwd();else {var a=U.getStreamFromFD(e);n=a.path;}if(r.length==0){if(!t)throw new o.ErrnoError(44);return n}return C.join2(n,r)},doStat(e,r,t){var n=e(r);b[t>>2]=n.dev,b[t+4>>2]=n.mode,k[t+8>>2]=n.nlink,b[t+12>>2]=n.uid,b[t+16>>2]=n.gid,b[t+20>>2]=n.rdev,$=[n.size>>>0,(R=n.size,+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+24>>2]=$[0],b[t+28>>2]=$[1],b[t+32>>2]=4096,b[t+36>>2]=n.blocks;var a=n.atime.getTime(),i=n.mtime.getTime(),u=n.ctime.getTime();return $=[Math.floor(a/1e3)>>>0,(R=Math.floor(a/1e3),+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+40>>2]=$[0],b[t+44>>2]=$[1],k[t+48>>2]=a%1e3*1e3,$=[Math.floor(i/1e3)>>>0,(R=Math.floor(i/1e3),+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+56>>2]=$[0],b[t+60>>2]=$[1],k[t+64>>2]=i%1e3*1e3,$=[Math.floor(u/1e3)>>>0,(R=Math.floor(u/1e3),+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+72>>2]=$[0],b[t+76>>2]=$[1],k[t+80>>2]=u%1e3*1e3,$=[n.ino>>>0,(R=n.ino,+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+88>>2]=$[0],b[t+92>>2]=$[1],0},doMsync(e,r,t,n,a){if(!o.isFile(r.node.mode))throw new o.ErrnoError(43);if(n&2)return 0;var i=j.slice(e,e+t);o.msync(r,i,a,t,n);},getStreamFromFD(e){var r=o.getStreamChecked(e);return r},varargs:void 0,getStr(e){var r=ue(e);return r}};function pt(e,r,t,n){try{if(r=U.getStr(r),r=U.calculateAt(e,r),t&-8)return -28;var a=o.lookupPath(r,{follow:!0}),i=a.node;if(!i)return -44;var u="";return t&4&&(u+="r"),t&2&&(u+="w"),t&1&&(u+="x"),u&&o.nodePermissions(i,u)?-2:0}catch(l){if(typeof o>"u"||l.name!=="ErrnoError")throw l;return -l.errno}}function Se(){var e=b[+U.varargs>>2];return U.varargs+=4,e}var Oe=Se;function lt(e,r,t){U.varargs=t;try{var n=U.getStreamFromFD(e);switch(r){case 0:{var a=Se();if(a<0)return -28;for(;o.streams[a];)a++;var i;return i=o.dupStream(n,a),i.fd}case 1:case 2:return 0;case 3:return n.flags;case 4:{var a=Se();return n.flags|=a,0}case 12:{var a=Oe(),u=0;return W[a+u>>1]=2,0}case 13:case 14:return 0}return -28}catch(l){if(typeof o>"u"||l.name!=="ErrnoError")throw l;return -l.errno}}function ct(e,r){try{var t=U.getStreamFromFD(e);return U.doStat(o.stat,t.path,r)}catch(n){if(typeof o>"u"||n.name!=="ErrnoError")throw n;return -n.errno}}function dt(e,r,t){U.varargs=t;try{var n=U.getStreamFromFD(e);switch(r){case 21509:return n.tty?0:-59;case 21505:{if(!n.tty)return -59;if(n.tty.ops.ioctl_tcgets){var a=n.tty.ops.ioctl_tcgets(n),i=Oe();b[i>>2]=a.c_iflag||0,b[i+4>>2]=a.c_oflag||0,b[i+8>>2]=a.c_cflag||0,b[i+12>>2]=a.c_lflag||0;for(var u=0;u<32;u++)w[i+u+17]=a.c_cc[u]||0;return 0}return 0}case 21510:case 21511:case 21512:return n.tty?0:-59;case 21506:case 21507:case 21508:{if(!n.tty)return -59;if(n.tty.ops.ioctl_tcsets){for(var i=Oe(),l=b[i>>2],v=b[i+4>>2],E=b[i+8>>2],G=b[i+12>>2],x=[],u=0;u<32;u++)x.push(w[i+u+17]);return n.tty.ops.ioctl_tcsets(n.tty,r,{c_iflag:l,c_oflag:v,c_cflag:E,c_lflag:G,c_cc:x})}return 0}case 21519:{if(!n.tty)return -59;var i=Oe();return b[i>>2]=0,0}case 21520:return n.tty?-28:-59;case 21531:{var i=Oe();return o.ioctl(n,r,i)}case 21523:{if(!n.tty)return -59;if(n.tty.ops.ioctl_tiocgwinsz){var B=n.tty.ops.ioctl_tiocgwinsz(n.tty),i=Oe();W[i>>1]=B[0],W[i+2>>1]=B[1];}return 0}case 21524:return n.tty?0:-59;case 21515:return n.tty?0:-59;default:return -28}}catch(y){if(typeof o>"u"||y.name!=="ErrnoError")throw y;return -y.errno}}function ft(e,r,t,n){try{r=U.getStr(r);var a=n&256,i=n&4096;return n=n&-6401,r=U.calculateAt(e,r,i),U.doStat(a?o.lstat:o.stat,r,t)}catch(u){if(typeof o>"u"||u.name!=="ErrnoError")throw u;return -u.errno}}function vt(e,r,t,n){U.varargs=n;try{r=U.getStr(r),r=U.calculateAt(e,r);var a=n?Se():0;return o.open(r,t,a).fd}catch(i){if(typeof o>"u"||i.name!=="ErrnoError")throw i;return -i.errno}}var Ke=(e,r,t)=>tr(e,j,r,t);function mt(e,r,t,n){try{if(r=U.getStr(r),r=U.calculateAt(e,r),n<=0)return -28;var a=o.readlink(r),i=Math.min(n,rr(a)),u=w[t+i];return Ke(a,t,n+1),w[t+i]=u,i}catch(l){if(typeof o>"u"||l.name!=="ErrnoError")throw l;return -l.errno}}function _t(e){try{return e=U.getStr(e),o.rmdir(e),0}catch(r){if(typeof o>"u"||r.name!=="ErrnoError")throw r;return -r.errno}}function gt(e,r){try{return e=U.getStr(e),U.doStat(o.stat,e,r)}catch(t){if(typeof o>"u"||t.name!=="ErrnoError")throw t;return -t.errno}}function ht(e,r,t){try{return r=U.getStr(r),r=U.calculateAt(e,r),t===0?o.unlink(r):t===512?o.rmdir(r):_e("Invalid flags passed to unlinkat"),0}catch(n){if(typeof o>"u"||n.name!=="ErrnoError")throw n;return -n.errno}}var wt=()=>{_e("");},yt=1,bt=()=>yt,Et=(e,r,t)=>j.copyWithin(e,r,r+t),ar=(e,r)=>r+2097152>>>0<4194305-!!e?(e>>>0)+r*4294967296:NaN;function Mt(e,r,t,n,a,i,u,l){var v=ar(a,i);try{if(isNaN(v))return 61;var E=U.getStreamFromFD(n),G=o.mmap(E,e,v,r,t),x=G.ptr;return b[u>>2]=G.allocated,k[l>>2]=x,0}catch(B){if(typeof o>"u"||B.name!=="ErrnoError")throw B;return -B.errno}}function Dt(e,r,t,n,a,i,u){var l=ar(i,u);try{var v=U.getStreamFromFD(a);t&2&&U.doMsync(e,v,r,n,l);}catch(E){if(typeof o>"u"||E.name!=="ErrnoError")throw E;return -E.errno}}var Xt=(e,r,t,n)=>{var a=new Date().getFullYear(),i=new Date(a,0,1),u=new Date(a,6,1),l=i.getTimezoneOffset(),v=u.getTimezoneOffset(),E=Math.max(l,v);k[e>>2]=E*60,b[r>>2]=+(l!=v);var G=y=>{var O=y>=0?"-":"+",te=Math.abs(y),de=String(Math.floor(te/60)).padStart(2,"0"),ne=String(te%60).padStart(2,"0");return `UTC${O}${de}${ne}`},x=G(l),B=G(v);v<l?(Ke(x,t,17),Ke(B,n,17)):(Ke(x,n,17),Ke(B,t,17));},ir=[],Lt=(e,r)=>{ir.length=0;for(var t;t=j[e++];){var n=t!=105;n&=t!=112,r+=n&&r%8?4:0,ir.push(t==112?k[r>>2]:t==105?b[r>>2]:oe[r>>3]),r+=n?8:4;}return ir},Bt=(e,r,t)=>{var n=Lt(r,t);return Qr[e](...n)},Rt=(e,r,t)=>Bt(e,r,t),kt=()=>Date.now();var Ft=()=>2147483648,Ot=e=>{var r=S.buffer,t=(e-r.byteLength+65535)/65536;try{return S.grow(t),K(),1}catch{}},xt=e=>{var r=j.length;e>>>=0;var t=Ft();if(e>t)return !1;for(var n=(v,E)=>v+(E-v%E)%E,a=1;a<=4;a*=2){var i=r*(1+.2/a);i=Math.min(i,e+100663296);var u=Math.min(t,n(Math.max(e,i),65536)),l=Ot(u);if(l)return !0}return !1},sr={},Ut=()=>I,Ce=()=>{if(!Ce.strings){var e=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",r={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:e,_:Ut()};for(var t in sr)sr[t]===void 0?delete r[t]:r[t]=sr[t];var n=[];for(var t in r)n.push(`${t}=${r[t]}`);Ce.strings=n;}return Ce.strings},Nt=(e,r)=>{for(var t=0;t<e.length;++t)w[r++]=e.charCodeAt(t);w[r]=0;},Vt=(e,r)=>{var t=0;return Ce().forEach((n,a)=>{var i=r+t;k[e+a*4>>2]=i,Nt(n,i),t+=n.length+1;}),0},Kt=(e,r)=>{var t=Ce();k[e>>2]=t.length;var n=0;return t.forEach(a=>n+=a.length+1),k[r>>2]=n,0},Zt=e=>{(P=!0),Q(e,new qr(e));},Yt=(e,r)=>{Zt(e);},zt=Yt;function jt(e){try{var r=U.getStreamFromFD(e);return o.close(r),0}catch(t){if(typeof o>"u"||t.name!=="ErrnoError")throw t;return t.errno}}var Pt=(e,r,t,n)=>{for(var a=0,i=0;i<t;i++){var u=k[r>>2],l=k[r+4>>2];r+=8;var v=o.read(e,w,u,l,n);if(v<0)return -1;if(a+=v,v<l)break;}return a};function Jt(e,r,t,n){try{var a=U.getStreamFromFD(e),i=Pt(a,r,t);return k[n>>2]=i,0}catch(u){if(typeof o>"u"||u.name!=="ErrnoError")throw u;return u.errno}}function St(e,r,t,n,a){var i=ar(r,t);try{if(isNaN(i))return 61;var u=U.getStreamFromFD(e);return o.llseek(u,i,n),$=[u.position>>>0,(R=u.position,+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[a>>2]=$[0],b[a+4>>2]=$[1],u.getdents&&i===0&&n===0&&(u.getdents=null),0}catch(l){if(typeof o>"u"||l.name!=="ErrnoError")throw l;return l.errno}}var Ht=(e,r,t,n)=>{for(var a=0,i=0;i<t;i++){var u=k[r>>2],l=k[r+4>>2];r+=8;var v=o.write(e,w,u,l,n);if(v<0)return -1;a+=v;}return a};function Tt(e,r,t,n){try{var a=U.getStreamFromFD(e),i=Ht(a,r,t);return k[n>>2]=i,0}catch(u){if(typeof o>"u"||u.name!=="ErrnoError")throw u;return u.errno}}o.createPreloadedFile=st,o.staticInit();var Qt={a:Ar,k:pt,h:lt,y:ct,z:dt,w:ft,d:vt,r:mt,s:_t,x:gt,t:ht,l:wt,i:bt,j:Et,m:Mt,n:Dt,p:Xt,A:Rt,b:kt,q:xt,u:Vt,v:Kt,e:zt,c:jt,f:Jt,o:St,g:Tt},N=Tr();s._webidl_free=e=>(s._webidl_free=N.D)(e);s._free=e=>(s._free=N.E)(e);s._webidl_malloc=e=>(s._webidl_malloc=N.F)(e);s._malloc=e=>(s._malloc=N.G)(e);var yr=s._emscripten_bind_VoidPtr___destroy___0=e=>(yr=s._emscripten_bind_VoidPtr___destroy___0=N.H)(e),br=s._emscripten_bind_Graphviz_Graphviz_2=(e,r)=>(br=s._emscripten_bind_Graphviz_Graphviz_2=N.I)(e,r),Er=s._emscripten_bind_Graphviz_version_0=()=>(Er=s._emscripten_bind_Graphviz_version_0=N.J)(),Mr=s._emscripten_bind_Graphviz_lastError_0=()=>(Mr=s._emscripten_bind_Graphviz_lastError_0=N.K)(),Dr=s._emscripten_bind_Graphviz_createFile_2=(e,r,t)=>(Dr=s._emscripten_bind_Graphviz_createFile_2=N.L)(e,r,t),Xr=s._emscripten_bind_Graphviz_layout_3=(e,r,t,n)=>(Xr=s._emscripten_bind_Graphviz_layout_3=N.M)(e,r,t,n),Lr=s._emscripten_bind_Graphviz_acyclic_3=(e,r,t,n)=>(Lr=s._emscripten_bind_Graphviz_acyclic_3=N.N)(e,r,t,n),Br=s._emscripten_bind_Graphviz_tred_3=(e,r,t,n)=>(Br=s._emscripten_bind_Graphviz_tred_3=N.O)(e,r,t,n),Rr=s._emscripten_bind_Graphviz_unflatten_4=(e,r,t,n,a)=>(Rr=s._emscripten_bind_Graphviz_unflatten_4=N.P)(e,r,t,n,a),kr=s._emscripten_bind_Graphviz_get_layout_result_0=e=>(kr=s._emscripten_bind_Graphviz_get_layout_result_0=N.Q)(e),Gr=s._emscripten_bind_Graphviz_set_layout_result_1=(e,r)=>(Gr=s._emscripten_bind_Graphviz_set_layout_result_1=N.R)(e,r),Fr=s._emscripten_bind_Graphviz_get_acyclic_outFile_0=e=>(Fr=s._emscripten_bind_Graphviz_get_acyclic_outFile_0=N.S)(e),Or=s._emscripten_bind_Graphviz_set_acyclic_outFile_1=(e,r)=>(Or=s._emscripten_bind_Graphviz_set_acyclic_outFile_1=N.T)(e,r),xr=s._emscripten_bind_Graphviz_get_acyclic_num_rev_0=e=>(xr=s._emscripten_bind_Graphviz_get_acyclic_num_rev_0=N.U)(e),Ur=s._emscripten_bind_Graphviz_set_acyclic_num_rev_1=(e,r)=>(Ur=s._emscripten_bind_Graphviz_set_acyclic_num_rev_1=N.V)(e,r),Nr=s._emscripten_bind_Graphviz_get_tred_out_0=e=>(Nr=s._emscripten_bind_Graphviz_get_tred_out_0=N.W)(e),Vr=s._emscripten_bind_Graphviz_set_tred_out_1=(e,r)=>(Vr=s._emscripten_bind_Graphviz_set_tred_out_1=N.X)(e,r),Kr=s._emscripten_bind_Graphviz_get_tred_err_0=e=>(Kr=s._emscripten_bind_Graphviz_get_tred_err_0=N.Y)(e),Cr=s._emscripten_bind_Graphviz_set_tred_err_1=(e,r)=>(Cr=s._emscripten_bind_Graphviz_set_tred_err_1=N.Z)(e,r),Ir=s._emscripten_bind_Graphviz___destroy___0=e=>(Ir=s._emscripten_bind_Graphviz___destroy___0=N._)(e),Zr=(e,r)=>(Zr=N.aa)(e,r),Yr=e=>(Yr=N.ba)(e);s.UTF8ToString=ue;var He;be=function e(){He||zr(),He||(be=e);};function zr(){if(ye>0||(Re(),ye>0))return;function e(){He||(He=!0,s.calledRun=!0,!P&&(Ne(),f(s),Pe()));}e();}zr();function pe(){}pe.prototype=Object.create(pe.prototype),pe.prototype.constructor=pe,pe.prototype.__class__=pe,pe.__cache__={},s.WrapperObject=pe;function Te(e){return (e||pe).__cache__}s.getCache=Te;function ur(e,r){var t=Te(r),n=t[e];return n||(n=Object.create((r||pe).prototype),n.ptr=e,t[e]=n)}s.wrapPointer=ur;function ro(e,r){return ur(e.ptr,r)}s.castObject=ro,s.NULL=ur(0);function to(e){if(!e.__destroy__)throw "Error: Cannot destroy object. (Did you create it yourself?)";e.__destroy__(),delete Te(e.__class__)[e.ptr];}s.destroy=to;function oo(e,r){return e.ptr===r.ptr}s.compare=oo;function no(e){return e.ptr}s.getPointer=no;function ao(e){return e.__class__}s.getClass=ao;var F={buffer:0,size:0,pos:0,temps:[],needed:0,prepare(){if(F.needed){for(var e=0;e<F.temps.length;e++)s._webidl_free(F.temps[e]);F.temps.length=0,s._webidl_free(F.buffer),F.buffer=0,F.size+=F.needed,F.needed=0;}F.buffer||(F.size+=128,F.buffer=s._webidl_malloc(F.size),J(F.buffer)),F.pos=0;},alloc(e,r){J(F.buffer);var t=r.BYTES_PER_ELEMENT,n=e.length*t;n=hr(n,8);var a;return F.pos+n>=F.size?(J(n>0),F.needed+=n,a=s._webidl_malloc(n),F.temps.push(a)):(a=F.buffer+F.pos,F.pos+=n),a},copy(e,r,t){t/=r.BYTES_PER_ELEMENT;for(var n=0;n<e.length;n++)r[t+n]=e[n];}};function le(e){if(typeof e=="string"){var r=or(e),t=F.alloc(r,w);return F.copy(r,w,t),t}return e}function we(){throw "cannot construct a VoidPtr, no constructor in IDL"}we.prototype=Object.create(pe.prototype),we.prototype.constructor=we,we.prototype.__class__=we,we.__cache__={},s.VoidPtr=we,we.prototype.__destroy__=we.prototype.__destroy__=function(){var e=this.ptr;yr(e);};function g(e,r){e&&typeof e=="object"&&(e=e.ptr),r&&typeof r=="object"&&(r=r.ptr),this.ptr=br(e,r),Te(g)[this.ptr]=this;}return g.prototype=Object.create(pe.prototype),g.prototype.constructor=g,g.prototype.__class__=g,g.__cache__={},s.Graphviz=g,g.prototype.version=g.prototype.version=function(){return ue(Er())},g.prototype.lastError=g.prototype.lastError=function(){return ue(Mr())},g.prototype.createFile=g.prototype.createFile=function(e,r){var t=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"?r=r.ptr:r=le(r),Dr(t,e,r);},g.prototype.layout=g.prototype.layout=function(e,r,t){var n=this.ptr;return F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"?r=r.ptr:r=le(r),t&&typeof t=="object"?t=t.ptr:t=le(t),ue(Xr(n,e,r,t))},g.prototype.acyclic=g.prototype.acyclic=function(e,r,t){var n=this.ptr;return F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"&&(r=r.ptr),t&&typeof t=="object"&&(t=t.ptr),!!Lr(n,e,r,t)},g.prototype.tred=g.prototype.tred=function(e,r,t){var n=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"&&(r=r.ptr),t&&typeof t=="object"&&(t=t.ptr),Br(n,e,r,t);},g.prototype.unflatten=g.prototype.unflatten=function(e,r,t,n){var a=this.ptr;return F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"&&(r=r.ptr),t&&typeof t=="object"&&(t=t.ptr),n&&typeof n=="object"&&(n=n.ptr),ue(Rr(a,e,r,t,n))},g.prototype.get_layout_result=g.prototype.get_layout_result=function(){var e=this.ptr;return ue(kr(e))},g.prototype.set_layout_result=g.prototype.set_layout_result=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),Gr(r,e);},Object.defineProperty(g.prototype,"layout_result",{get:g.prototype.get_layout_result,set:g.prototype.set_layout_result}),g.prototype.get_acyclic_outFile=g.prototype.get_acyclic_outFile=function(){var e=this.ptr;return ue(Fr(e))},g.prototype.set_acyclic_outFile=g.prototype.set_acyclic_outFile=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),Or(r,e);},Object.defineProperty(g.prototype,"acyclic_outFile",{get:g.prototype.get_acyclic_outFile,set:g.prototype.set_acyclic_outFile}),g.prototype.get_acyclic_num_rev=g.prototype.get_acyclic_num_rev=function(){var e=this.ptr;return xr(e)},g.prototype.set_acyclic_num_rev=g.prototype.set_acyclic_num_rev=function(e){var r=this.ptr;e&&typeof e=="object"&&(e=e.ptr),Ur(r,e);},Object.defineProperty(g.prototype,"acyclic_num_rev",{get:g.prototype.get_acyclic_num_rev,set:g.prototype.set_acyclic_num_rev}),g.prototype.get_tred_out=g.prototype.get_tred_out=function(){var e=this.ptr;return ue(Nr(e))},g.prototype.set_tred_out=g.prototype.set_tred_out=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),Vr(r,e);},Object.defineProperty(g.prototype,"tred_out",{get:g.prototype.get_tred_out,set:g.prototype.set_tred_out}),g.prototype.get_tred_err=g.prototype.get_tred_err=function(){var e=this.ptr;return ue(Kr(e))},g.prototype.set_tred_err=g.prototype.set_tred_err=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),Cr(r,e);},Object.defineProperty(g.prototype,"tred_err",{get:g.prototype.get_tred_err,set:g.prototype.set_tred_err}),g.prototype.__destroy__=g.prototype.__destroy__=function(){var e=this.ptr;Ir(e);},d=_,d}})(),Do=Mo,Xo='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"';function Lo(p){let c=p.length,d=[],s=0,f=0,m=-1;for(let _=0;_<c;_++){let h=Xo.indexOf(p[_]);if(h!==-1)if(m<0)m=h;else {m+=h*91,s|=m<<f,f+=(m&8191)>88?13:14;do d.push(s&255),s>>=8,f-=8;while(f>7);m=-1;}}return m>-1&&d.push((s|m<<f)&255),new Uint8Array(d)}var Bo='v7#aSXal?Dk_E:R9nY4y_2i"i;~[]K^kgo:$AFCU{J9IkELi.B"R@=S|<$rj6@;VSL8/Umet8FXV"yuga|s:|JQV!;h,P5abpC[?a*=<u$ZXlD4Lbrxa[Ib/?jYqI)1M;VFbX[JB</82&UrU<zgYYsy3h:`N&O)!;Lg^4#0uD(d1xmoCzK{p?Iyq8+QXYsA1iULN(p:x=#}lUp7wIjp=ug$1OZg_BkIqb_Ax~?]rx^Rk?6{zbKC_4mJ@xg15uTA%Ize/jhEV(!..*!"Jw>c1nj7>l<jb^+#ufsTpbVFn!E?>/~153Sz]>dVL?<tO&mW2lNDzMrphmz:q/S6`o]2>!}DIeTKcnk6:ic!fxa$*(?dlM`efH89dj.[DNfg@Jkl:rw>L6:bw6!JWh{k>e3k8:yB.5je`h89|9H/I(98ri{8]*80r!av]f,2h`5&hPSP}C4|s=%K|R3%>kwl?jD&UK5Aq~d@fD8c6G=2MD84&D9Q*PmG`X:$L9=r$<:(F|V+Rf8:vEe9gB8)ep$[]$9E24&~7*7w>]^1]6rtla.7/%mH}6sxrwkU`qs1MU1|gjoj*C[dy0&O_UTB%.zmd=2[eX>p2?1@KN{5zNpcjV+2&Jg>#P=|{)6kbAPBexKuEG7eTamP8Ae&~hS&vJnQrY,`!X*}{Te@xO|}7Fwu;0R"dSbJ]L2ZxN^EQ+LCrxY9/k37~>ZQ{!wwu`c:n^Xf0"7WBfA!_#9lfb)$ndH3Sr@y]]Ot&/f%!NZ|bWd$`CUXpyHA|_M4fFy9.s@.$H4Jh53nNY9ss~/mm_K;Ukd}n/9>8]TR{8dva1;b:5HFK7XfsuwP0;Ry1*1?8~/Oh<0{@.,%g*gV#XOt_ohd;}i@2O^s&aS2anY1/ss|@B<Ly;fHR#:x~/n?;cltzX|DVh.`8Z+wK98;+R.|oQ=]EbVZu[2B&nk#,Z=ibY%$7!hsTw&M3I+z:lsT@L@I[cY5%Aa:&0SvKd3]Mmm2@9B}Vh|k1mo(vTgRlbU,q4>n7w,=Um_vV7Z7u87=+oTRh<0I]B$5j&q>p2auqxoOMgKAaQ}Olg>kKP:<`^9EQ:ZI}D[i20<j}b2QSHV9QVFvR%f{5r"7AH<s?d[4w8O`i#Yp640f@hk;Um?$bCRw!$fWZ`75Yb)G+jerdNDVKF:d!1S<vls|>j],e;x0_`(?q:nC]f1/hT+8$0f0_)Twl3t$/d}*KOqJGikUZ#6$7?N"bf3o/?>6%]`4&nT2@{S9IH[!7/{82Me%26v/{Rb/$6fF(R2np{f,d_~IbXyvl|<=X*S<dTT!?@TMD2V0^Oy.dmhYuAk@UCrI5v<FQ)m|=p[JMebn3Z)?6<6dq6O1M}=Kq!nVp)_T.bR`jD{.rz.0fcRK#Q:h{^2@4asOlPpSpu`D.IDC8%]"Gd>SFZ[]`EV`EA=?|{qX1a6$l`j!36?,/%zx9]H>rC]GjYu?aUV)i+)|`&U$`]`1<hHV{%D"5T9Qb%wGm*o]!bbC9W^a!05T+?pjTm0=hpLIJLY,,2XBL`qy,&5W}Czl?q7VVG_tlZ	>tKnoq.0py*.],Ra9F$q^v*/cp3hHxp^KURMWc>G>6W*BUfd.BB<N_Y:/+UbjRj^*3!db.,mlYd`w@fGZXq>u9JFY_`kR^:f.1[p[]:Xk$qd@M;Xe(16Hr%KCoH)Oa3#.^<9]M).$ly<QL2qkzYIMD0+0;G}J$M(16D>t*}[&=pZi5=Gc[.nJ]ED/60?m27rK>KG7c4HE|m8K>{Zz3:ne)w7fI)a;Fc)7L&F}c51$z@i+aRz61SGGRBYvNwX_Mzq$zy)?8dq40&1|,%z1VA]OZ_Vln;dZ(64@qjom:mYr3r2j2FNN><}g.+2g6g1pd[=)Gu(o:U4:{+BV_#ZLy8s.Vd2jdT27cugf8&]KeOo,I@+yoSWy$UZ]P|jur"UcEpGOp02gIL:Ud&9Ae!ig8!@oTruE5x|xlT$_iC;iJH85|82e1,PNHN4wqFv_5%7Q*S=e|B8KLq9y@8G^<{_6:xm5t1{re:>Yb#8/6l#[eEF}=M@XOWw`Mr$PlyT]%5KC`h0}ofw_fSt$`8#:dY&Fke;3BN9.z;OIS]Pt%L4@%`6t_;srz2nqD)oSsh_|8m+)25tvYHSR}IZ"033JF8/5t#QeN7]w&!}VPM&x2B%s{O<D1~OiI9NIG7&2b/k9i#QBOiEJg{5zS3NOyI]ZwJr,InxL(okA~<n*4"yQNU:.<bGcet%zY_`,c1(H[5c1(oeVD0kXgi?Bn$xr0i#I46;bgyik953a?!p{;%>Nw7e{q(M(=]S{rqMzKa~krYTcydVeTPq7.L;`2;p0vGGg/#QvBiW?!h2(4+7!JL@M85WhpDG*:XJ3M%JuMNDt_A}]Ct%[!f{1Q}t]mDVyT|W2h1U_j,FJ;5&gIl>$5p7K:*SP"%~8aa8kvu;|dP.flC~l=|I:yM:xG5&0C&=%23%$MyuW2xJAhe"ne^[Hy#/_azlDKq!Gv0?)/}7yl<)4cFQP{UZt;Btx,Fw?fVC0#<T:B%6+sZ&xe<z5ryA.fmYd37NoGQ[`6gvYmwq3,7L3]v&l&NZxseNBH2o$SdnST*<yc3}Jop!1T"Xh(b!4aOTVl^lz`/$tE[li0bzSZ3^F.zHa{Zv!1I2P[4S<dYmA.;Y^RqEk$5$gq?`<9Yl.q4%Y?5@^anqu:j)PKG(}]/}0e=Wo;d`f;nmkmmb6Q*c2#rmhffav:+Kz<b$5tzr|+hJb47GdeH8R4Xp6%~ddOfb];(g$]_0!`w&#&#]<zaGZxpf|N:1OZ_mh8mb54O[GFzKMrYE8$YF0xu%QqqNJ}<9?ahlf}VgqG=yP[X}Ah[ZR~ND%5jpl4n%okyT"Xg6q3ed8+`NWyle6%t,]lkyRbYpHSw%>&MeB8GSY9t0cmHN|otEc,}:@6S~<uay)6KgdVxrYm#NcY=cznjlk1*/*5oKQ!?8Rr}ozKc%yk/8j+;)|<@<c]tmhzzph1_M0!_1dlV?F_;V>84rjj1nGQ252Tx)dZ%)F{uMn8o!muNl7x*.dqsVkNnyFoH)`INu,8vnj@X>VK/e=WNKr4eVPp8:[Z&2C4DS(]M;>1p[jTx~R3o9@iYkpmQ7n.5K,&;;$5PZN+td~qx<+&6eR(#:k@"`V%Xvx~CI/EeVkSV8i{[8f@u6F{08npL?@UY92?5bpy&LhleHRJ{#yjbP0L_Mfy(|Wyzp36W,bYE?*fP]Ybj7w#)Ck<IR.yVYbG`4|GKnQqN],&5z]n/"O(k$G?MO{U3<oDQu0{{8S&|GKn}+vU496~U@N.Nz[0$qSFn2,SM/E&?Zf3u.=qk5zHLOnDeUg:zTD@~T^lWr"p,w(.{=l2cY]SXk~mT*ch]Zr[B2mvJNU:"^NMA;ljkB2,d@AX[ZBJ@5U;lj"^76(2JcQ`0mX>clX>&>0.F7G/nRr6!t}ov})aTPX0dZfy:SOp`=HUGn~4`nYZ&PiSbqI{.&>|x$JcF{yuIqpD$LbzO|pyhfD.sI^<7{7ENu3jKWL*I74Tylg1i0P7cnMlvu.qqw){01h~(@*=*k<^3Bo?t@OG!_L9=,Ky8K{:tYPZ`<o,b/.LiPJY<xy|EQPWqAFMf@^7G5f:6M7ZMJ"kOb{]#Gp1C_x|&cYmKW%zR[/yBqd>x1zT/<Q]_r+a!?wsNSbz{Y0M?q2K)#)^uVEJXq0@"`:nT&ioVr4*.xP<TnEN3[rx{XB%KL_dj.Y:k{j.z^^e,Eg9&Yv$Dee}F3yoTNfd:R)`%)"7d?TGJ}k$LeCW[jT`ZU"8]Im&[I83NM/L29`UG7bxVPFn;U>N`]}z<$PO]ipS#vkX<3}a_XEz9Xg@$9x6v?/nEg[)>%iGn0nG*s)]s.G@]iuMp#?ZU_L%4h$n9:_mJ]42=0xs]^j.&%(mtlgd25c:g<2@q{O|7^J$`si7]y~3{=F`<UeVXp<{B5"[J.WTV7XTIVi{#2z]I9<q9apyDy"_~3f(w3:n$r4eu|/r#f@pLil|oyk./B|ntjz}am&Db^uC>us;:vF(d1;7|}D7q[2VDj;H@5F&[iz=1$_Mb_Vgt*i^iTgh`M5qd>KU=|1d^#f=4`K>uNUoea/6l#BLo%VX]C^!Tv6X]ChauT@)uN*oiYWb)e5SKUhBxirnpRk?16EQ#2A]?kUo[x;R.^4t70I]Zkj2TtMxF#8,>1Qs$zb3E%YmqG{*8zE5X1UmkybT(1@kt&t|M:j.1S7]B^WW?{9DN8zJkyjbazl>2X<srzRmP_c+0@CaoF!jA{o@|bUl2?eU4&ty.t|od0{/c{uO]rV$jQaGin[+N.r}L=9aRnlSJ$u302h0bz^4ZQA|Z>z%;j#19VMJM*+b&rL=dq]{x1]1H3St:$@KY2nSKU@Y>#3q{Hf@qs5QI^GrV>{_,^v&.n_J7GQ*b!IS!94lYX1MUoWaibB%Xsx<vvyr~0y^#a<${5XUS5od1Mm&c>4?xlTe.xfN*7]Yu"{Y[Tl6rP6D?Zr67!tL+H?!%*RjqAFfGXOIbr2RvLfElj??e<+yKxRhc}:I2+6YjJO2:ln+!Y^Jma<1/y;C1sOmIfTX1T=#_3pZq6"e$Avx>af;Tj0zsDx4kEdjkXsOmi98m+uu)Y1pUjrX%PBB3R7"iM#0xhSCVj0X6UiwuJBm+ylEY^eeVj5XQSEg4R=LtKf>kEej(X`T(z5!bzGF>[88s+75KZqD<48<i/aY+H@!_0ig2X"Dq!O2j/BB9QPTSJ+yTa/3LT1p3kztGQRz98bjNXASl398l+oullQ8ZLg9vLYJ%|NJk/LB=gx`!<k/LvTJ|DR2l4nCsq$co;3EU/XBcjTPU9$k7t^I,p2egSe?VvQSf,bvgS<WkFW6J.U/jB7Wy6h#>W<FF92xtg8AlDlv1xV/vBrmwo#<p4WZhGVNP2q4[Eva/*4k>t,NfrcS{W@JX5HbX/;Bje$c8ft4<DQ!;PY/~B@nJ=cSBXFKP[n;5k,X!HyhaC6k1)|K+Ge#EX;Iw<=8Z/WC~N<U/>r41E|b_06kPB!Kx;0EbQ>WwJ`=g^a/7vfV(jbC7kYuaUl5,0PtNvNLap0kf4TANHG!GOMt]AOOdZ4WrA?PZlvWuAcIaScLYAmE8<ZLuAXCNp7yZ"IC=h=x/.NtRB>m5FeVzA#CZR~PYLcADF|Lb4dA"ID8.o0WNB2Ky:Et;*AP}}XLgAfJ*BYLhAnJW!NcRAMDz|/`Ft*B:yMcSAYDF%7yRAcDVRZ4mAtHD35Fe",By6wwOt[B:OxdGt~BQ$5FXA@D|5Zd}L>yk,ZUbpK16nLy%eKs3e7nPy1i]Smg4)/Uy0K18nxEk/ya=$hvGfk!3e!n=E{^m{mgI@8KC?bP>$vvzowYQV{nOFzsj3oILt<DhVoYiNu1Lt]BTfN/VA_DdhXLy&E0OtPCSma]0(LBptBEptFE|(LB?E*B<OWmVxHt}eF!c"REptdEWip`~;ZtqI9(WBAF[t+*;Bb,u2ZI;hsXkD7?ZE(?,7Ktr1dtGF]"KFy]GON~(^ww{GB5$gqysFlC5J{iD,H<m<m<=n/thGYY#Hh/pdsigc^?YDv;We/h8A8F(|Xr~eNYKGrY7D^d@$`XY"|CtxrP]h%ApGlR};<FtYAE&gj|{)Zwy""I*Bwx$IH,(nDYMGNuoQR(tBPc[/VRF*xLkCwTY@+5O<JOb*`JBG]vMX<I@<,7}vNXVFK.RVPcB5v+|)1L7x"KvzJO@L.C#W5dYRJtuc^vnM+E|(iM}CJYzPa4u2^K+CVtfTv7+xATpnAu(FXDNc=xq1C?QCsRRlxwg4tBptSF7jtpScSBQJS6q1bt%EO!:i_94"=E@AB<[h@AOHrfYjsiUt<DLrUE^h^AmHGeq1Wt@DG:i6:hAjIBMg5*:hXBNK9%ww4(yC+QFBK73tZEy]hN;hIB7R{O^95"aFnZLv|9g",C::hN/hRBBGV%A^3(1BOaA$/h=A[GN%{x&T_>5tuvXTD%*[D5dcWxLUm0j|x?;FAH5u)Mk.+[})#4]I|od`}Lj7iueOGa)nG<CiJuTP&d08Hh[F^u7UY!X7~)WGCvYQ{i)0t?MwxXWK,er`;,BDA*?LDF5o*5ti|y>NqD:4c<z+DN:DZbqNSjHXeK7i`J"L]FKZ_IK.iq~X;L!E:lB*w+")=L>Ejq??qN8!zLUF:46lk+quKmxJ3RWzMD4zOmn+&udd$YUjpX[P_wf<@yFEO[f;UjxXrR3X4R<L4J%jR$2kytjJ+GR2p4*wRPx`d#]4/En7XLWAeE}4vW1ApRJ`vW(A/R|LwWSBzT2!wWXBYUvtZ4Ao5E=!}wxW|AbF%P8M:hP)`B%PS6p1*("AddE)p1NtVEVVgoVx[h6AUF~1),2(kC3K7zs1&W*E&e~1};146uuOD?+e8;xnjC$j?frieVaAsBpN2]SVsitL`,n4BBa&vD[vUBLO&mqT9KN!+4gX/>jP22+G<ge<MUBJ[*BXqt*}:o~;}Q[1[*DXmt?adS~;BV=F[*_[O<Mg"KY.dBaBE2/k_9>[*RNkUuawUTK0|e;mxN{BzC+Mu4BXI*qoz:zPQSsYi!_4uii5%!WhLnSMd.xD{F{v7[^9b9SFI([dSO.$Y>q;b*3<~Y|miZ`EtnO6ueO^ywqQ@#Qy!UF+"0v5_mjO&4ic8#2:ec^PTHKhkyxr7DmUyC_PF$nf#1:d0Du<8fB?*|LdAkT10*M$A.RxLio,|BC%|RWX>Hso{dMjP7n?8Aj8[e$LA9(F20q^.&m*R>OdB8Kg[<ruzHH4{<zec64aw7$,ROtQE`Aqj&S>9@i8Xs({B8Ae&pk7O_doM9vlS5xV}N~7Kj:F&6Cp9UU6uKC9kyd0H6yO+waYIri7NAXDwPOvN8aizSH"pAi!ZjXdG8iYvO4]VPdY8?_ISZ/cos3.*c2.yRT+Z&aO<aCd)jW5;;gP&wV_+V<a7ijc%pRFliTcaOAS0@J1ouR>ju:]zXnB{E`Wt*0$%KP^ggL<%n.lGh]0~P8BkAi8)uk<WBT|T^*H3[~%PX?KB2Q3Cj753L*qQ%o$:W$d+:_v+jwldx.wG;]>Kx8H+Lv<lU:Uxq*;DggkdU{j5X8k[IJeyRX8ahkQ$4S<!rW+~7_!4p^%$TpXn`/v/KSe~NT3;opq&BPPp,cPB#NXGdj"NBj2(#y,}7:j!@Z5)im+WIB8j*|./YcEs8z_]=_+*F^IlxisE{"gjLaqL8K|u4js7$S_MXa|Um8f}.8^zhq[K83$+KOrQgXa%Dv0MINp{eeE`]wjmFSxtq!*4/FkQ3GAa|5VXA:{xrQoS2$+Y7iUn*Jyj&|If]o,3jg7UZILD(%C[K/%`{x|FEg$Ky08MjZ#77kdj4E"OKqu:D`|tp{RufOR6$mLD8yQ|fpGPvwH*7mX,(@Aaf8k,CDvIM!8N|T3"UP0Ue$1Z<&%TbDH]f<!Tg1p_&i8K<NcW1:o;$2iQ3lC(K>rkMNNdZDn7_pMS6:gK3:rb8A1"HAvz3>,JrC8K|JPW1Z4rEw@D8,%Lg8^C(|AQK|f8cO1mey_);XBa1J82(z9"!Djy8J?!(M8[CA1xW5FjHa[yqkhV^PdsTDgd/B8+S~lZ8OR0RKNO*Kjgg>TN(/UrGI3k`hGxn0|.#V$`4#"S_C`yrv?17oBF,IL@],R/ikpVR)V@V5;oif9<hRsIKvX1P`)F!DI6l`w5jc4`>O;+_JpV@(&^b73P^;Vs[Oyg1>TK?uuJW?SK&^biPz$t`);4^n8Dn@4DF"g>ry!58^!1$SnzPk_Qr*]dUK|3l@},?B;S9Rr0%]HxP5{L;0*`_472^[wyh1wSgZ!KioQp,x,ta!#>=uL|7c/dUK|uq73+>`P]!sd6|fH*=6%M(xC&o?^$7c{Hp[HMLyI<.:`O}H_Od>L%AT]yr{bDe"@/K%=Ae<&U!78K`Z5s$XaZ?%UK|E]hujH}z3.C`45)W7b~Pksb>c=YW7LA_7}h$>^F4]^1y5DJEFP<`0W=tsl8B]W9II7XT[gEDtj/Tx.mwX68iU*E:`hU*bd`hU*Y:_hU*#5^"V:mwKxfB8NC8f"EtD=.0&JXTC9Ac62GW^G8N/XhJXT|u7NdbkYgRE8W3)7MyV7*K`z/G3clw1v.GPy{oFr0Uk2FZ#}ZBTY(<*NjfMU#2_8ow6.gr1HcR&W]l_:0H:nOf|uIOf36bzc]"IO8NcKQ3&SEH1/}o}6Ap^zkD/[d[;76dU/lwQfViu1r@KO2<r4JpXxK5B]~5l:Vcw@;;+6IHOFH3:SF0sDxbVbP#^8ugG36UeUL{SY+Bi4D,2N1f][!23#7NcKQ3q2Vp|N][d[lwkeI={qMy#dr@9Vulk:LpJ;{q=3L5SY+b{(z/%{ymrVW*K5]P`+grU]7;wc<<Ye6%>(.kPJ^DkJmyY57^BxkXkgdc=+EDC/7HP{ovV9P75<$y#7E>N_X+w7A;Mw1vOF&3t)>*pcbnOH+ZZl5V;$6gCna`aYoGdJrk3ex+kgdcY=`5lvt]]g5UgL)SojMZ$nVOG;Y=4nTYPFi?Fhs!Ub<wI}G9mn,"[N_X?za9J+=zgaKr<e+?y+#h8O}u;Gn28S|;4Vx(}]f)v!zJWw<])eBhJ.O/SYmpcxG5?DqldT|>(z|(}eUs}Voq<tq=hn9k8S&6c^+"$pQSf"?*gD+Qj:7p]uz[@V2n@+>w,6[&:u.g>c;i:!vLk(86l+W1Bnr5Oo;WGEu^_uDRj$o7.Joip&wTGWx[1K[EOwj<`Nt90KVI[,wH"kP@Z~VwQl_]S?<2j)ps5_S&0Nd(EvMVLl]8!7sI{Xy=vyk.]265X;{Mc{5?u6=eTG65c`7t>G7.q9Io}d:5[pT=.Hd8#$BRPOx=:#a^F1zK^6/pAcfS<i,Ki*8Sn5"7VXR?E3Fb>$#f~QOpf%!tkASg$E<z/D8QzK>Z$y),QT"p.JlOi]Ir})SiM<q7vHQfhM95v>Gb3[#9w<.U,>u?W>.z:wxpUZwW)6%pFG*4~c&LIHafYPb{]$#p:/#pE#9Vh,P*@+%(%jO2m6XSp1uWRn#6q|315}Gqnn*t3}TtAJT,%VIkL102.P{kz!^ddo:DC8zVm*x"t5eX,@"mB.}7Q^2`DZPQIR:lT$9N~^0!hc~qPC;[4$z>cCfX23k>;zk4F{O2*(GA>ic$$<u0![v&Ek{>=MXLy?JY"n/Kl>mHnm&qH+JqHSv6xyGP0M6JXLz)yjbzmCJV~#6Jh/TZjgR$0m2+?8NgxL*fOgd"TJb$1)#[L=Txx4nmYJfDON?p!k$#00I&oOj<}?rvUbk9x{b7L(!+f_8@i+PqE{1B]?WhXE?),G>q}+GQFI2^ICj]hfA|j+72a{BWWEJ;S~5B_>0[MC.^hSW2c}UQT]0mL=2$Cr0iObQFbM<g4lh>u]g_a>g<w=UTt@A=))X$I5AgLZ@Sf`8u/N*=dA6:c1!+R]ipvh_)@H><o;F!4}x1)*Bz)+s>**@zBFf^rzYPJ%0kQlQmpHQC#4;b;tZibqII}h#r$jzWOgl^xv742X*:tmE(Kw9pO:`krFs/r7gqk/o7zfTfQD&H(g?#9b:rTHt@Ar8o5j>6`n^N9U#4bp"[iOE#(*83q]K{h9QK"[4@LuA@e_<sYmS!ibR!"@4mVT_*ljT6O#L:6vqlz<F],PE{Ah7Y`gyTD42.gay3BOG$NgZUGBz^t]_+rvFQEQ[(BffFL?M8Q2khq},|r$MVGi0v<w(KXnRt,P|y[YC[7bkB,T+MW+vv}nY:`7wii$oa~+,8!ch]p^@C=%*S(}D/X]Q"5`:7{p42V8I#*jjoaUtnWuSHA+5p.]7%&LYII|7~V|jUF+I8j*R<*c91+Ud[$.`#H}%N"G(=>?W8u0o,IeB8`37erip9~c:!l/_f+jwV_YVYHSmZ"bO5!wQ{<naY=<dY>=E/O5Wk1wlX,T!BSHH2?%9s2Kh.`C8o=^2.~g(.dUuL0M.NNV5rAw">Ol8Hor?CU3}RN_X6YN`*HZG&vZd=r^(ons7U+o<m&bA;R[51wihZyk0M63"L+52^P3rwlH8~Y>Yz*DdcA#}+O4{S3+z9Y]c(qKITQ#,qcPP~)i*Ll>q;_)jovi!%smwOb3F*jUBr#_yZ&lqi(2==*Idb_Zr]gM_e;e2.{U[T*g[$=&^Ze@XB(&ZvaX^99>v*I%71vxF$$uksw=5@&xu!X9$Ex!|9mark^8|.2}&ojm>d"G(<0yLPmYR<8H(<sKY`$ERznBeJ&e#I~O~njm{V^]iO.znOxWD!UPN*=$xf"*iTV#(,PvM8UwK/;<j1.sS6O#nJ}}dE(iSj693W%.a/0<JiJgn?aFW7]6deS}&_i*V}&_MEi0rS>)1K(/G`(]?)~O?KEl9$f})77K0<1KD]l7=aETI6~q(>mykVT9TH%X5qzBX?Wp47>YCppZ/EvzR%Q:%GhO,?ScbVZBvt8;%Jag+PLz=X_36U2={H|R2Ngz8na&^[xBwb!M^cQM7>TM9V]c4tYE;M6`D1XEB{3Bz&JGr7_k_]VVaY3m]<G1&b>/RZZErqUiee<RF<A;1R4i2ta{Y>hj[a*HNegh0MtO]&`=V!+lk+1mXC5@#{FSW2+S/)MsMUzVHRbV=f//}[}1@b.Q(y!kOZ1Xf~!C41CJQ8J{dHlLQ,0]Pl|<BV|%8tyG[oTu6Oa(qjA,LxjHmq[pfJLoM?)GN49%d0@#^<GW|%hZ,ZtB`*J@xgoL~6/nt9teDV$mEq]{{Mt[6Z8GLb`<#6f307]W#9jzJgmfy]B&jEg(J~9||WyG,{},QVeNE|lcPo@y)+Dr;%5R"Jd(ad|>1r82k(@nx:*eva@iq/0;_2[U75/{lj0kY,o@/ni876Hpyn+36yl9y5#kU;Fkp]t;)D@T|bo5)US}w*YTkTX3;m?lk+CJl3HJLY^DaR+Mxy9L}DyJtn)ijqF%qptpBGS4n^?Bqla$I{&.a1x<@gU*]Td).|yN8mE8e%b:X[E#~Y",f:&H7&iU3V*A]a%T*!mr^!NwGMA]vS@]_^~9xU]it_od9e*lt[E=)coYNFEil32<!_w^p"KkwNP+uYv{eO(i$SOu;9vzw.R%jtKn$eo_LQ&YmY]KvYN0!HKV9nb#ycJ*EDwk<wEoz+T`k[tm9Zb2iWn]Z+1S?MiW2ltvj1]4A@E{ZtK/P$++CgPsF1F$:_YsS<HzDNg?|wV.d6*Z3i[..a_uuk0CFY=JdF[kbDgVOSp]Gaoqd@lUVfwWb//YR/M13QDq(~c^D[ib++@6dSk"R>v5SNO@6/F0h[1CFYDn0Cl#05SNn`W^}1[tA~FcTYH&:Nq6)![lJj0CQ>tQTS1P7qErzL3ChDh<Q0q3?:.o[X3UNM29![)?IXla[V~%m]D,uQ,5S""$V+?H^:brt6m4W+8~%GkSi[4xy/Q+bKT?1>A2Khg/NFT^#Hg^MjN,/GTUx@Pa]lr[kP`|bKqY&SK%jtP$ADijB+%7[!sGXj&R3{~p!O*A/n,"g)ED(7<TkQ_lgZ&u<:<INX:b&S_z|L/zjSz>}5q)B5tTUbN*s5)sO@abRuf:|}eajyG5r3G5@wD>.1!BDINI3"eDK/+r;1DL^<Stl3r4M#uVn"kKs4M#]|uhzgw,%.J[14~crJ<zNm+Hs9vz[]w#)CXD&BV<:FPYyrF$Vi|vD$,u}h6y)SnDZX(5Y84bbml4^?@IZB{$c](H0C$cQp+toDXlzb|bG,Dxv?%VeU.8P<Ys83r}T&,WFNF3Ir8]VKB2Eh;)ih.t~B2,2`8_g#TR&P|[9f;F{LpEDQOwo(zBz&?*#Czac.xt&{+k[&{o/fxWG1KzR]M{(ZqVx4@6U$]TN#54h.a^l8Z/2_O?B&Xsd9"4Cws,q;ezJo1Y_%=%eD)+`=>8l^8(*NjNQDA]r;#Ood{<0hD)UwL*i_LQ_M!Og>?#_|V},Q8p|"81Qt;Y)n_B@/oI<YxyQoDKNEGDPwVQ+0.rqFqU&Y=6<HqU&Y;0:*zv:f]Z;xLyAok+UJ<wc>ZxS0uJY,d5+nhpc`]6n2[0Q`C~vvC{Nq"z?cc6121P&:a8wxjkTHe~bCU5gVkXQZP=|bUoWnpjZU(5aG{Q5xI]"duIw4~|dl]ldxNMIz+Kt_e.!g{_$&Rx"^FL:Lw*OVKw(<*MMi*fPV#57e{qIzKWmZ[k3@nlU`d$|i*7LooZ1YW@h,ZpB>M^Br;qTT:{jy+5"CfZ0MHSy=Q*rfQ@EY#c]k*GH0oGVXPDL*~F^j@i_QIMzumzb]FZ#=(W(9y#bjg1ZuAh|zGxLVt9SzFzPEp.SYcWPvay#v7V)?"WMEEuf!(O*#IoL8#ym8hxGYvj&Mg`FHGGDGq7)Lu^B&fw_kh;/2"MxG>iS{JkEUzuEIq7Y=s2n7Kn.kF%mD(=_dM*BUK(&Uzu^c{_nt0tMlfSbm^>2XUp4nE5NSL#_p<cqK$7~|~=zxwA]%:NjZ6B&62._EcJsHmY@s;Ed&vO<wth1X_3uq^u}$A*,{>%7]$p1wRxr51+zPmLQ,e00>pu.FRXi}})%erL^s,@GYh~L[y2=O==w%)M@]*lXbc9g4$ml;WFyfqG"%cNbYq/prx@P3}B_3AJVU96z99OpS28Xj:XyGt8Qqrpk8]YO5b3"B42KgH~o3Y,e%ju!wA7{o18io|feT9L]Z|jM5kV_p/q:/eN6,i%G~A;0!tcF6;VEX:g1_e^wgeAPGaEkZO@k6lH2q|5t5#i%d)PDyI/rsw:GPI:Z+0k@NuT[v>ENtZ.5wd`cT{*h>_y[O3prtU$hqQ#cRH4@$vg1YRzVObVKJg^VVQG2sX#Cj{je`hp!)(#crBcLgm7d8Wp3q`=o]~>a.*54MrG9P6YFQf((znlUV)KX~R~>H7dlYt54m3p5Jm/_iu,ZM/|)eGFn;%%]pwcn*=Ug%39t~a+Im4PfNf@UgBxHd<+B/}?U`8N2|dv~AZRmtP.g`*kg|E;#XjeS1{m$pEIXM)m3O%kh;z?W[91EZ_o,tOC|9o;>B,[x36sanWsA`Z_J]5<g.f.K}w<<^cT[U(Mz^B#/<YVj`,3J1Kvn^AD~]$Dk`0KO%3uO">iQFNX`;S9Zu2xU$$)8hB#"bs2yuh|61$@zBQcZdHg}%*np:F]9QiLxyoZs#()[KlrnuC5l?4LO&=L[B[Kiua>yr^_3yi1P<aWAzPD,IHYo8$*D8+Iu^Tf!*Cy?(1v+j|"1|Iv(<ebOl1uxepEXI8:(j7m&8x]q$NbDWsb3]a^Q=;ya>Z{2cT_3>i}k=MYe{OVu88+_%r|/KkJ5EuQqGEjy~+6`wOkI#s$x,V#s$)Onl&;@S[_f;b.VV<SfKcfO54cC8*MwZw)NimzO0J|TPk=.E#&JC+Y_8;CYr4Uj;_3Q#)LfCq*5],s`UP/hKy1R~n#b5#/U={UP/Bp,"JCdG5u_;n2D!S1gZ[]*Fem2gxg?f:8rp#oTwu=p^GAKe@?4sb2jluFk),gTLxM1#VG7V+I&N/EM9MzLdwxGSpL0qoB5+&r)soMIJ#:yXL>8:mI@n:c+Ksl$7eDI9i#ID}`Zl1)JFfB`44ZPTCxz*.o?0fE$4X_zQNs;zjR/K86OTWM0JK=.(i"."tXEiFRV|)Y9x15v_5C#ma)](erGxn{|%4g3~JX7HhG3,h]HM;:t,mmgRX3/Z:6fNb$829=)@ws%mU1S%PXjdPk(F{1UXRD4v.y61yuj!k^,3krW$U/xqYk$<SG[tPTzuY6m5V;ya[p!Y15ta<Ifv+?7)(g=3e=Q=w+(ec*bsQv5pt$o]ZTlyCbV)C2@SoB|oO*a)(gwlZXz4XG|o=gS@Z1oSHon(*/<@i:y+c*e(:,;r~;{+gq72(BR#V8+^Lv^FZ:{Pr~z|`9y95TB7zMk@VWYl!O6}O45J,VhT7Gr|x2w=yJZVHT__#sjCmg/|euRBb1mWdT$QgYz*G>Wd,i&61cPDQZHOc#PTVx(/8F.LPjuQ9;Mj&Y=8;*t^b!/znDSe(GDU9w<[V#89krbz5jqGr22?Q,gq^Zzpfb7%5Ku7/l3v"bjdr!{p.U^7Gw,V9z%{R*>5i)hh?t<L<ncPWEb?{"_oN^lbc%QnqZ&0W{{3c^`=hYz0p7z1rekgbAgZ)E~^v&P?YSK0EU`=zbN4|}D7Uve&)m59<Vepg&cX8@2IV$Q}`yT=A8Nw[(Sh},s.QS3Jh|<m{Mo{1oY~CxZ;0+CtPTH{EO6U!;I8~J87]IE>j~bYW`*ztvi|DK*&MIPWEr13)6ACV{~@Es:Q*?rS3J>s9,g;6v=2w&1rd`c`@.`Nnv,oKSyr//B>G}`<dV=HweA/J(0A4L})w7^K:?e@AV#/,^|j[?zBZQXy1*EDSe45!!lP^.0S(O;O;@{|Tm+@q&8a]_8^a^E|Fnb22mgnjw1yaYT+5&eF5<77%7V+OlQ{nh,Mg4sBZb7eylS=Bjd#vkWygwy=GRcP|%6Lc]vBJ6Dp.tI_]nDMBq0QI%2|t=mxJ*7+sgaVHS49j>"GaG/,yGo6KCiPau5q`^^B7_`5U0AY2_7w0ST]hu2gC!;o!9n*_q/XA%9oUlT=rh%S4?Tpf.&mK|UY,/!Cw_6Kok0pFyvV.c$1jF~<Ybh.Hx(SEr"b"<~<XBN.ps@U!<]!x*hF.{R#q[M,zm?q!?f5OT?e+iRByvKab!"LFgR!/_e%C?gj">y=ruXKq:QvyGn0XM1MV$+6egS@I&hwCthhFQI%16VI.r>CgG!9m1jZlW64/M15|(8~`=TYZ>oc?8iLDr6X?[j<cOI^;k|yEsA_2!ok_d;fi&/,=8Hp&Y[b#Huc_[,BxI[HJrWlqXaY`+2~%*KFR*$V:yx;@Y:#"T`{[T]<+)(7py0Tz~X17m&I^<NfU.>.e0R=Qh*$kbJc:]LN#9ui5T0rTBo&O+Hx_3_/Qvcpt<f8{WNn8aKL]R[21ttSd5WM]KSC$etP#2&NLmFoB..]0,OaSHC:sPn9^@QjDH9k{yr]??_SGN6:m2%XG;!uw+h_+e%E9[%S365BeJc:M^y+dQgTE_M>W[T<n.D)Z`.)d!s`VRjF|qb#9rR)4.6Kh<`x:]<.nV,A5{PO90?O(`XD3f?jV(Vh_Jd]D:g^h3^MJ~f^uo`|.znO~4PVovF7J09.5^IwmQnx$+QZT5uZ}p*)g/M:z}9MezE18>[>qUMSMi"o8txN<f>q8+7F]IvUcyu"T,8U1_f;zyRL0T(uEAXlwIV$DzM&Qq8hSHhcib!LYFT|(jN:v6HQ.~,^j#d;5e,Ib7eX)mM>~o$zL($@GYlsp*/rmhXy%lL?yMJ{CDM*,)R8[jUr,d2Qu_W/:ROb)au`17#3p0l8=Z=HCW},bYC`)S+P}yFe1f+vi/d7]6T+U6o#K8NAz&;x*7Cr&c1|Czu:8O3ut{.$7qL721=yUCH`%FHhMrXzHPb]#Q.L"O<@1q|yu%PN0ai{L`0#ml5C@5U7;+[m%:[xG}W2ASqL_MAnzji$H`Jg+LV}Eb^2=8Ym"O!PY]kqFQwra{Q:,ED2iGiS)kY,Vd?/GOi{GMhuX,~IL;.zoYHxp_r]l5nuEd8X=Z;T&./Y(5X+XiUH*!3kW(7PaJ%lF/1gJ:;by!Wwm/l;]+&XTb16SlKYzAf6JK$4;NV#@VBHKLL@MfuIAzt7vS!PiNE#n;k{@oqQ]#F`#Cu6+l#{t[k8TH#yh#)j%vBrK67etPa$ft_Oz!$vBr?R4j&[!VfP)HFIk*7pR#NyZmU/Q:[GoPcKnh22@WCNK:F:nsA+v6SB`4l4/mq&0$I7sa[[3Ld{^"l*:*z8}EUJ7?7HV2h9x*RFUJ<zY@/5|hi9$n_n*2+E9]w?AI!?iw19/S1(?6u90)n/QF1mBqJZ0gUH)+/x?8u1W:caA=1v7~3jE_(HI<g]7S0Yb7n)~#?pGa>KJqN=X?|>#&6;[Df@|la}bZ4B0Fjbzs%UDTlW2PW1`4SkITK#Z]_.uoIqNZcWc?z/ixf?7s,,cM[/tQPt+xw(D#<[q|]&k,cv>0X+,JmL1~x5o}Fca?ay5jHZYR4$@kn3J]NB=|]n|n<|/4WVxck3*S"Te/^bdE*50qi0$sRRS.BA"=r,!VlLr04.yzdVzvqlOt`y%yoTkU=}iy<`y68_ids,m%JfeHb4Z+o03+3+g&XQc/j2$3|7;`O<z2hq[pPTBzAMk`}qvb9c_CmYB8[L[*{Eq,yQH6ww<Js5[7NZWuRo"}d&!.]J=$32%c/iNkz:6oV9u..<r_$RaS,qB+$pExB?%I?iOhHw+K3`=8D1(vn=k2|>^xNl;YAhPFK~6E@4=7)E)+Qs)=v1W:B7B_QJKs`5l&W[J2mqK_L;ngr<Y*pm8vRxA~qQ26o8x|uYZ>m?.&SeE(g>:{fz1,2~z#n]Qg5~_g_MT$M?IE$d#CrGQl,ph8)+ibvT2ptIaFRkbc:dwV8Lo{;f>]:QC&NZxsR(K5k"_?Hz!{x}ibe(!7]IyoqhWy%l:g+5,R69?eY!/nj5=],,z+W"1M$52$m#+>z[A.h[P3b><VrhN|v|]<,%ox^._?K|DZbzG5"[Gp~EK]}mb2W!U/bgAe/FXN8KS+UmtL>I060LQHW,X16pJVxr6nX7r]7!WOR7NW("[Z8sW32X^BUAGu9HPv*fxi5lDCzn$Dz5zy=R2ov4gS1!`f)]7i&;6Wi:Lb0NY^XQS{N`?t&C(f9cr70Z^Zc7hQD>6qw88:x`;J2_)<9/2Ti]HuWqaOq:ZZLDbyC6$v(Si+m1;tbvTE[$<=/[5d]|G/yn#v`HpLI6A=^3W@O[W!.hP5Yj_.5:N{UY~;zERRG3>J`rYf+BV,](nX!5N,iAS?R6ttVh3mN`tQ6(?8qG?*iCGX3aKfyC;ggu,hrPPXx!5Y_U"?YCX@wT=>t8Crw?iAMl~,gSi{QX/[~|klPg9Mg]FW=|v1Rp`bZn~1UIN^qu=zDur#bwo@q:qyXI#!q8{8kMcpZ%e$Qnv]FGWwFn/}3wfhub9EMJP?CwdhAhi&H&A&B>ihS_"ZKNrX=gX6wq2N$H/09r&(cBxOlh>`X{Eb0;|={nr%@IE`>cdvT<LKs&M0zZPxHG^&2/>8~/aK0*;[qK]4S(li.!1KZX..dB%[c_JR$;B#nfNRs=aiMT)lFYPDZLdGUQq?pLcV,4aIyqG!bC`3N^;VkL5y0p|O1R>XVQ5yDlEpMPZ<x82a}VeY:tLlJW0iSR6?gV~//)N+#zy&3wK@*2Plf?<=i{WJNm?|c8bPUW[bvoAe9.`V>V>V>V>V>V>Vf]KQ}de:f=bS=?wv"=YRc<S@1!/@li;.}C7`e+X@n?s<dBG|dx9PK3VW*LgF&,4KTR45NVW]kHG%EFI_kH",y|a&4dO@L`/1SI8FW;z)kL}5=a]~XTy2`h{H(,db;ej&xpJ{{aK(fv>iGxKV#Efe@/9w=a]>ylt%qWND*#7:Gna73:Fz(7.%<G=oz!l+yc],p"fuPX317K+(RR)dO@(|KT<fn`h&_JwCZbTDuC0K*L~y^@liZO,||XYFYF;k_6E5lS4jxXI6QCyrU@3R4jli[PhEo6s>7bXFtq,Nm,h|31:!(!vq]*"b9O&|sND{K]/Baqo@>nyt:,Wj5vl+4KJKXd_ic_7yh+o0y|31wSPVR./zR:JGmCq~[*)?G@HBV>qmJ(]u`kCgXWYCL:[g>6QfqQ+?mL`$F`AFBYXp/?NmAMT@AMR@I`L?uN3`cLSzSI8lI{D^Q+.({oFQTce_LL#Qep[3}iW?|>Y`F/[hV2r+&f9)8`QI<!qXN_GmXkl&~Sg;aZ1#J:]6g:kafso*7+)pb?SfWy_<_I7>rraX49,&hoo6yyUn4mhf(e]tJysjWbh^RUp)z`nmfhMTs$ePfd*p@wL.I*&N"x0Wgq]0dKpsV`a+T@[g)E~a9|K1l+m`oQu:HZc1?Om?%qUII:HZaLcVlir[ub1Q%yh+.%s0+}vmRC"dxO?8fwOjxGI?&r%Q3*Z".LGU(.A?vp,B_6{6=Z>m=jwx<.h|,}]pYT%+s1t+loc<AYPZl~tC8C[YjzIN#I/vWCEv#XKI<VM$~$8_OjUF^YjRK%Vmp"6mhQ!,_G1B,{M1?6fiB*reB0vx9230*Mn0<,R%efLZ,MFCu4<PKYLVR(oZ~w0Qc[j3y|3V?F#a38;@I`*2fl7xc2.ugat]>coWs!c7w#G@BRV2aIHy}xwik@_)JIOPoSnP]IYgi77/XP1IGvC{O^{e}Ik;Vi|$S>~lV*LS80FJGyv=)YP?ih`RS}WUtxK$Qx19,MNUSP{}nf9N;O99ZdKOLA"7}DpZg0xr2Tu50,b_=Jpszr}()gNW/jMIQ~NmEo~gak+izZ/R}~z,HNpMOb:y2SUXrB(Ez&m5[3}QAj)a@{Kc[fp5dpXz:?W#&"wmm)}+pFo;3&|@i9oSMxD&4,YEvuNphw0N0`/$gR~CVD}5asjyx<ko#oX$Az@me3D`M65If5uCR`H^mE)FNvccv88(XpomkS/FOu?(<>3L{C7S?DbS%F6Y"ES@979/8uF%9w6k89]KulPyuthdh8b<HnO.]i;vNZNmNVDV;x<q6b3bsvy;/L=CS":CZVl~DgRa[nw;yz]7BR~L>?.1C2*r405Yz?u9JFb*.<K@(ge.C2$i>bNivF<!`V}<(5RRGiUp8gYRqNI+lBJt:CS"e+6?UR;l}6AMnM/thZWO(yY*$q8,QhBH>^iK:)^bCFYw1pz,K<R%i&?@QX;x+iXFF=,i<{h[5~x2L$0*gso0SV(E{pYhD`d8v6g37OhA{u0L#k!XYkTb!7I;isZx/R72*<fmPD0VD8zlfLB%0GW.~L3i4SBhC!n_=ZJuAr@E6y.+_d16(eRXnOD8vmp?NPT2Zf*)<^d7X37AzNbJg|6HVFbJLT@kZ5io$u|h,G$q%%Uo#nytw=cQ3sOK`2@A`I}4w|cfIIu?(}nj>nW%rSNs!_PRKau3Mj9}v1&GByPVUAK}/,<8}FS{#xrnE`<ojF(M^tPLSH;"aGq6bQ3BZL$ddQW,I%TX]CofAP?0?C<P)F<&P4vLUVxaZ[]tu2>q0p$esO]&>v]&D8o]HeT/E,1t=DA84{OfgxcVn9i1,S*gmS?ko[kXfweT)3O{du7V$p2VKMj`~peMT~OCDO7pr2>q(SrmAP#vBHF{/Xe.e~ulP0J!4]#2G=Mom_Oz&j?A*u;e|0J3!4j1s2qCPe,EY7#3TuE;#X7{T,^FBw2goJ:InO`O!XAM@)[&~XD5635<?)m+?U*kMX`GY!<FEOoy"X7r4<zm&8Y*q&m2<!"/mNBF~:mNkssCd0[njC]{Yb25xqNmoqLLy,U+A~.@rx"|a1ft,fpt2ywhnf//3w[rA(`Gd+lU]aT$R@X:z^3g3&>!?G.c#IK3RL7O,il`EJ^<rgn#F{)uz*=%PlNH9SZV5{L17Q]R%KhoN`6LYfV1>dJ]kya*Or8A~bB!|;<=U(4x<e9+z6`#n]Iu[cCoaTA9JF[6jC.mSI9]fmn9oScoiGG6kHyhvc%tPlVy(fjRb7p2kmT}:x<L,+cvf4@`;N|xPVG8i#/gcQQ$=4X$&x:G9kP89`L5SyH58C^COpcM+Km`4UN`cbPU%z"mu&L2.L8qlxRq;U4?nj~Q696=q(h0?FWwGk:LUM43@kQ_w<g.7de6{X?{w3[*cT1yw)4E.ryxBq*$RN^ioO(Es4gT:*>Q4gy&@Kc{W)}k[6k.&=>Wg]UCnR##r~[h6)Z!n6<gwMEsp])l;:TGI*fUfMaG[Ct7N|c)wc`+(Fd{z=n8c:malZ[]x&cdYU*92;DDSs4?Zdz9dH%$WBNu]]$l_CP?#{|n0~YXFD9pg7Aqj+W+*fsU"lz]J4>Z)]){NM(Cnwuu/XmMtTQ,Y#1KC_+pO*aMk"0<|2>9tiwl"j0My>p!|2O9%mYm`r6s"Y,n7L6~Q]uYO>h:fa_c*21jV{TYLVPp<{v/T>1<%/sO1uS^V,rbyPi]ErJ8C$KfdlnKW2Gu%+w`<F5bXp1`DYN4|}mankv+JcOgC5vbjhIz(?n3!={QrqS+=tIEf@!6mO1Mx]oZO9W2aa<.D*`pZN&.f>TlFW;7XNdydVm8j6|^i{BL4bWc=a@G*+(SK&.gxuqm%ziN8y^l9IXK.fL~}JEr.YW}DK7W/K^j/E.l6N}GMzjR>|KIPst+f@}!.RfvE1x_i]8ex_t%!W3n1O(bHtfXoCQDuL0Ucc?bb#IHNXq&Tc3_BFta:^M`0![O<9?gb/P:;lhCWs042/.[Xu.ybQY1NUNoL`r9m`fmrV^K)[zR:?Td`4s,=X=+g,F0eY=Q=,6>dy{VQ7d8sD&Kq@ryJM0/;ly,cb=n_#/mGUIL?}D},&mwUQH=6;@KWTi*{jT0<wFMF2y^zcj&R:+voajRlC3j6Up<{q=P)[8#*Vmw&uZ&+^&zl#Z+L@$2?ba+&~&zXQ4?}!*GjR+vdrSogg"2vb<oJFq@QT45+a+iJ|t<yz.hy*g.FI//?`|P{fE{1BJ)L.sot/tc48>JULGr<xhQ:`/,9S`&I82Mo2QHR{O|l+yxf5k>e%d"x"Sj59BDTET_e~SRhO2_VS_M$a2b*9[NHYjXSZ(#dM<C)vXxA(3Ej_YRN_snNy|}a+>7(AbK>cznAy}Tdxf2.znv5&*;QWbk,&mpIyi3oq{aijF&<.@m3`LSpEG&or:dJnRrfwMlc&,2X]yh+F2V{<%j*;w::=iO$eF.HW/sQMf4$%hP@5K/.&SgmY:Kl2oU]QDop%Z&l?#&6ef:wy3bV@l:h(3.Xf=2aTNh9a$9Ja*;l^Um{+sm=EI=qFq5j=&8kfe=M*k[M*t{youM(5#s{qG+Ab^4*6|5%|[l]zi[)@{"y^r0I(1{DsX+mhHSHS6Jm?Me:dxyZ8G)V7]*4jY.3J&nyrDy[Kjo>G`#^qKTUiC[jbamhS=gd#O=!+IFO$.#JoT`&b<~[ZW{B7{^J%l9&67g0M^xNZOo?mdbgMOw:#GCO/g<rh3(P,Y4zm0aKLXHLZ*SA6PNvZ;GW969`s9!m?{vPI6L5L4MnJN?g.+2?73P##b_kek%.L&A{pe[]Kiu&{^v25>;LERu@F>v1*bA`qx<ZF947u2Uzo41^Cz,ocZLX7O=T._vr,]=tbt[qD0n(NK4VK&o1^tV7Jegtl$<Vi{MEqkxX`7JXgjo[=xU"Flv{zhO][?^IU,W=&=h"J5qdG^<YP}e%mkm<%xeFoab7h[!joF|y/U{g.4:;+,X,}{J"%;gv]bp;E:CC#APi^W$]%E;"P5."l$p6hb$T.&kwC2#8G%mdz:r72l();nmdh<]vmImdSD][!7q+a>hVKbUQ(}Qdj]}W83(Q_8Qhan/&#Y8YwcX*?~o.5)H9xz|&)}Wnk&MPpP0@_y4E3=nsj"1y4>JQqGT]iP{cP"YfSXo3r%EF?{JF7,bp4?ol400A73TiZlq:R/^>1Kln8&OU[M3aJapBz~qOwzKQ|G:t	ch"97vQVq5*m#y,JckT0<w|heBt?kH)7S+]B7V%LGuai;U`WS;PW]|=UEX8LyN<8Q(`d"T{x;Il1i0G2U/pAJB@QKew]AAgSXL{ND=;_zh)k[/tQZc*UuXhCR|/h3kFj&:gpp#KVLZCW,cjil#1LHqwE>=TBI:|eUILOM&#zq2yvx4Vg(5{>X>pdF]=eC~x;4apZUZP_vXSwy%6/u38k[TI;rwhZNMGjd)p/3`uB6PxI3Ed!Oi&k<EpGQC<L=fv&^O}7LF#kzP7DH}X/>8A/mzMv`lkyP=!)[fdj@%GBR~Z3vL?eUf.cZ_Y:SL<A,rQ5l"ur@T4`OZ`*(S;qRdsTi:awY=*#PY"COwG*J|{Id.*^$xrnT_YA&nYfWO4G$HMMK}*7*Q2%iR3xbpt%Co0{cRT^nNa)e]8#Ib3sF3}XRnx+rGfmyM#5jIP_<#gr4H]{5XoyI|2Xv5Kwo&<7Q^Gr%!fgsG@{GRCtpxa{W@{Y$+R=#5_^sP2PNfCirU/zBZD0rbv5Z]*o"Uk7yAsc9O%d2cyXr]*?IeSwd:!A:OJY>5nEnH6E|	LDR^Gi<oV]P3Aex)os;sT_kbM=;yC>TsswhZBX&]UXWGM*J6w?sY(b4^Gy%2;4^tR1{$*Vg:M*:*M2vLxTm[d&O@.r_4J{Y[896vd+Q~ZT1KKhaT1K+%mN#CCKiW(Z@!0M7W`52gLQ(ZMWDxilgbY`[;{|&zW.5Kz:cFLW]+?o^ktEwb^$X.Ly+i{oej"b<EMqySvKmL3~Ll,J45GwvidhsC,{x2#!09vi5I[P]wO!zP2)@WO30_1id.14_b5%wxd`h|qckpT!_**K/z|J~5@{t7zf)d8g15=[X*5MtuDIi*=O$yt%IhjL>5.CM7!C@:L@Qq$n:xwV=z[7h2Fd.f!t)d:g,iN~~c(SlQ:_SI=]V#7Y<Lg^cdPZj:aO=5E/FLG$cH#y,|O[i/rN.jdHy*CpMIJ:&#zv7lbu@!z6/=<DI?Y2wGI^^%j866rS156L)a9Bw+3lTo0r)Mv:j.s=MfAVGUv~#/[LA{KgrV!PPe3{yE"k,YCiRTw_%F3P^9:imBYMAg/H3(DkDZ]owfkaIbx^L2[F.jt&KJ)+7>]zu?r#]M]uPyuKQjh7C4hP_XL+zn_<!8IK)p@Gxp{[6ML#wXEq!_h^KGv3"U[6!Nv=3u8pk&[2r,MD|w,Q/({%B7E/Fn2E$`h&_Deix7WF:%qG1)2XZ(vX^*q=$`e}:WdHz`@lW@(%vXnF7I>xl|`5zML#{PpjE$PY=YsEt~i{bXO*"7H!gx,38x&:9m:t.U"Fum7IFs6dPpJC:=&u}UvLQ,!_GwQQY4Q`:kSZT{N5!4|Z8r&_7vhLW7T?dF#w&uu0j,Sti+nV7Z,:%oEap*.f3U;tuVAl}`=q"eCNDdJjdPr^C{:G.l6b@y!xb/!{%eTMycMuBp@]4K&_LnF^P#M3G!q2&)6xnFhOG,yKkOd2]3#/~Z8r`+23b5458%kc5)BpQkLf&:|BR.LmIWBzjw}_XDZHQ^,elY%N7$+yk^}Y:aC1gzwV)`$wfEM_`U)(CUL),@l5w`"6sdjm+=]TQ6Kj&fL@dr<_7vcq9vBqRSGr%`drdr#NG?aF`$7K[]S[yUqEo&<3odu~~9%3gU_qT[dtXKG;$jW9rHx!$KKxhwUKG6Y[&(IyF7Uvy+J[xrkQ4:{y&<ShpeYwctC^WekUlVT9*Z_Jrmq)lk|Q*y^*S@ADE7jwk"hjpI<9@[vzE06#55cb/{tV5Ty;D%+NM40uRM;emL4V7ZF)1G`1b#9;,9<W&UJi1hxry:+k1P~}*01Wzo<#0Wlu[cAkC9f{8L{9)P@S#.nJBNt)f0j,vS19+#iOnO[ttbWWw|l"K4J3u!yuUO8=FX"8;m)s1ob}2mVCz&BDLH^Tpc31D?P3RebwdOdx&qPucg9G_^F5_wLkhqNQzMZz/;>6{o0@}nb`/(PO}p6Z]lt9JOj[G,H0=w"y7dk7iwzt,3<8Y=V^aOcRWWx4S~k>~GcD=9S5>;GL4B[yDc&N|E<fwDgWSa89mmSx4m:F7ZFQ@^5Q.L}N)a$GIbF3#Ku?,a:.uLiNQD50;p[.;%l]i%|$74R|x1zWkad#=%]W_`ZL0^oabz@:huluZXX]_Tu}>/WWB@qF5_.E.+dswAk!,754eD487tV3|yyXL^VP6}o]}+vGuTGVjRvVYiC}pga,:`<"r.tDCp<z,0[F)q]F?mpE@G!c>98as$oD^yDN87,=(3C.gTA?p.rZDOg7AK_Z(5{S36X<SvT#*G2?Ftgku.ii2c(:3S<;cUv3nLy%eyn3]`8.=`Y?+y~p7OA^YGVgk*X{C;eT36`9Ih4b/ELFG~"%7UIp*@+g+r`&*/f#^Y|CtV([uv4=1Xpy*4Poi<7czMsSQX`KTzIlGZfc/[/$sQhyBhr/"0XQz+,@E3/@fmbzL5!r$UR%k#f+?o*MjRBqY^VULIG|}aKx%o}^^?dSI`CLrKfGTM+!fSu/0wV*Z&a[jyd+L6k~cF<zvgM{Usbz>#_4!dK@H`*33_0,F{T*Z&8;1)V+z/T/cF+Vr/myq4kn_E6"mnzDu?<>zyC1ALNSjdKjmH(B~g{Kxrp5pL6,Cri)eF%UjL]rp~0MMB,x4d%~dy{IOmyGHnVs|?/}Z&H|gsMQJ]kDgV3,K5!t*V1LTYqD8rlV8C1,RIO`1},1f5maKnyKgVH|Nh"cs,P@2!xYs,E2y%!Z22ypvJ7mjh8]|b=N=|bKOwc6kN@bxk"o0M<6?=$KIr7b@b"fXFJC.zHF];vJwIK*PCH1fEwk+iKi1lhXeZ*a6KrD?/^H{CZfjRc>g/R2R5jRL[]+D{GYkD@pdK><7nTr;gDe&8jo47U.5q3n"<C/Oi,W.XP=zTRMpJOgI:6Zr_?p@c7HOpw>~c*/5Z+i}6XF*/0."+{?v3#!o]CWuE;FiNJ5q={8RZvqfyi2e9sXR"*r0PY{<fP6cFweT>A8.]xWG#KyTYw$,Vdb(TG*oa6_E<m(MV{)+Q4f^1O;:urNg6+T@BSHIN$d_;uIjZO=[O,<$]UT,<MlB_L;8[$U<?qcs?3U9o!Q8v9p~%R3Tg8z?}GYV$vPgZx8#<=wC~O&R^$&99:WjxGz)`qd!y`]n@.#1(Aev!5D2jRS2Ktc@M%!k8WhmDTHmJoP1Ah#<GmO)xtD3b;WbKJ:wzKh}RhpQtjl$I#%0W#S0Efa=$dv]grtMb8^MFKwh=NF~Tz9uJD[tlG)SH=eaHl2b_cO9?!)]H3W589QmY.g9HOp~E^;B~8X1sZXnc{fo[n*=U~+x|^Zw;uK~?5caYK$!})4!Se(Cg=]#xa9;#W3c_:*]$%3O)}]Q=~9T*g;;sEZ!m/Deiz{m[je$UR#nN]tw?V+}eu=b4,qfwvm>NDZCHgN{MNMs{%kfKNr7c7pifnJUJ=7`Yv:;gnU0>sF9$$p[a71c>`*YWY=$@:<"pj|;Bb:2n#YNn%5A{sjEHi{C.(fKMt7w#BR4t^8iGNu~q.a:YmIqXW;_{.8b$cY)u<d7bYh~S;UZerq//(Iapub!x=EVlyfk:[[77562c_ndiUB41}dF85=z3#FcM6ksxsRE9kN6y!.kpPn,bbM1`#l1w}i(6ym7&;i+qCodi&Xz_q_6>z3*7``_F&<)M&>Xcg#sSh8C!?4Zq8*ckTYC`tsF|+lXwzs]TI%9o;gqEfr<xG;nd_rS6G%:wPvLl0BZLBaCU$#"Uu4!;`L_Ms#Dwx#}<Y=8d0WsmkH060#A&wLBr.aCAAW_OLb.lduR@~V4BdZ4gxub7^cIM+b:/9#Uptn!m^0xNVX*L9{./O+WF{C1Ew4;y85Q3#MCp[d1*cvY2x*1%^TWK3Xf:AkWykwD3Zz"CzMoXidbd@6JP@kQkT0<w3nSGu<EN&?1Xkgj;>h0L/<&B$7[&D#,2GMgVDeID1R*h8fz)Wb0M$e|j+b(yw`S@!E*LEJT0DOw6N`+*{(}ksB{M86e${(i^m}]`(BN!w)lqLVqouHO/7"U28N%0a!g|?JoL(r:;u01$x6oPt$)M%B8M~>V0DcrDl7G5>H2k71}631v(sIJ7SO2v1+k&^C/R+K|pJV^dN`=An7"8/8:dYV+M3q+KoF^1Dl5S11YQGiKY1>gdVKkz+7^*%Pk7PX8G)f3_LyQVgd;B~7=ZNNmy<Rie3)7j{"p<66A^?g;DXKsYb}]E+fQkSg2ye9tm_*Z$N9b2(J=)0Rt,]<;JEJ?yNRI<QtPJx{Jk6xaF4/U/ej90~#CZ:R>RC~cY<q"6,."7CM:nn%O]]N%3_UUgw=5chRQ)}q~Qh}aa8zOZMrgV8btN~/1fw=<CkZ2@35r*GoG=>a=V#m%40"CE5*npdQzKR@K.|d`*>!#40?F=5ip,(JeD7ZKTPYsO/R^m<vW&s!$Rb{h!5jy?=[^P2HQmiF&nr6qG3_r,TJ1KoMFf~y`4QD8v:{Vx7~P[KI?go9%d9;2?RS19gLYTNhbBEY3RNV#v92#tIATvbyx7[?t+}gG"BRb@@Ieq;x,*fflG%&:Lzy2WF^)eyCH(*?:{J`;9kqU2^O<{qUzZ|=L?.I=M>X4R`j/MgJE7d)CBD#3e,8K,IwMch5+>HA|LoiISeMoFQ)dx$Y5X|Xt1Hi0Q}L;IjAyFq|iy0+!W@!H^IewzlEA0mx097B{E9>?G0KK:1>k{;huhd%t^x1`<NjZO%}/9Q$Vbi?G<LPa~AR_dG*)$q_6>e5Uy#zAD@t#_G}rasZgY78iE<5bCyRgaIFAunZt~[tw4utF;2E7t,r.?kSdR1BPK&<WW&NhAw!VJQ+aymWG`OKw!jFimc_!5/XHwjEL<LSFqC]fS%<54?oA+#qGa}y;*PZuSfVsY=&79>I?5,/eE_WVV1k3r"J8?;g^TUB6vuKIejc1YQ81cEj<)^h0M[a,SBe0z9[Ib7is.E`G+Yv`6.o8{zcHsGrdXn|;fb7RzIDF%?woLpRmi,$Igw3QJ{E0GwBW/d%YG1KOu<(NZ]k2NyF;GPVJZ;M<xaF./v&a!1KMqAIcv&k,0v=vHPVPZY*fm8#z+2WR>(vK8f|0nFQ:FA3b_ko:BAk5_<0UGnmIKHZrUsTtf#nt]Vh+UMT$?Y:/Xf@<T|835^=7tsMp%0SfS$~9^o3&YO.^1=g<^ISYP6/Rb$m!q%Bjb@o87^1bU;d9m=KLzubVhNe%y5j$@?G2aUpexY9402W?El#O3eu:k|p@Qj64;KlR]Yr4JThJ{.e",pRQQW(AEPj.|rF|#a9m`xE8M6?Fh&_L%RHv^4OHBFdc0j<5,Gdc03lZX;uv4m`*3%(Z$ZQR&Sej@c62Sp);*,l!,NlP6]=G=Ya_1KOhE<5A(55JM_M[SN*J%<4OL)ajdnU{oM@!pOorg2u_/vd,UO9^*l,DiY>>ldr?!OG?o%}UCu#u1zTn_gkE~wqt9/<^1RlOZgXHiIz_<n,.G0C1CSgPcS1bn^cf*NEyPwBVF7UxfZ5.ErG]1&^pFO!D@LtGngE~3Xe,U~/2}GD$zKMoMiD)acyDI4MnJt#[u/[O2fD9L9fWE&G##/<9//2fV+Vm,+HVDmA]&T}Pk}m:V$+0Xz1rL+V!v#!_]_ug!jNZErGVz9rlvDIJd*^UM<c!oT2EgT_tv~!8PmS=Z,cXkgEcGfyT<,%<VD99s0j"B|h}>!V;7THJ5pgB*|jph[ZR~[ZG]swPtc|j#H=r%,H)a@+%bf0a%VQn)i^D(ljV](2jN^6.M[6U~Sihjt]R_q`:Yj}9*NZS!=tWuC{2n#E$/TX7%TI)u`^fH]IZMB=h`Sy0+bV,B1KYBYJ%::*7v[k4T&I&_nuT_+VL|SPd8=_1JT>=;m=%CXg=Ep5>;6JyRrdeCHY*IfthmI;^?AT{<sVK@bk?iyf.c2$"+o]3!ADJ)._`YH6MP}^0k+kh?KN<]0kQmq,l;p5VaN19Gn.pAmk<3P::*5<QjJmBIu*o;,l&j8a+ju,Z3a?98&fY?@041Q:0!)vej.{Z.|@dqfvNk+X!S|`k{Lt006KU,T@i/<vayE]oNe[5D@kV+/Y//DZx:OzFVjV|(HsNzLZ~%)`f*NZQf7mEzmF?poZnOo%VquczD6Nbz=z9PNHZ+L.gHWY+*?8kQ7Q+aLlvYs[?3F&x.$Dy.Cxah4KMrh:1ozN^OkKC8JnkWA?k=s@n3IyNfG?hG@jhfB2zmx:&1(z$r#8~fQV~g33+]OF^m{=`ZQbM7;Bf.scY^XlDM4UTDfK<:(k=YU;~,&%#5cX.2m~y6,=1;N4}wQ%,wx<2!+d,oGKq[y&/6[YXyH,=U./|22&aGBqj"<.9o2.k3#K)%PSk%W=n$Sn.mtL!?v<UpBJ1bDaD@_yV{O!:OB8dHLV)SWqzD?9Ldb7$LAg(vpt"CupDGJ!y%"XS@*bBnFa7%>gLkyMOiulRD<5z~aKJ$&Y&VfNNT$x2$cmQ._D3|P*Y$^Lvm0B{p1;QDBq;Gn;.g|>9lMTXM_oEI.tE~eYd)%b.V+puZP9Kp<kDG6KR1=5I6URbD?8(?SJdqPRc`/$j:L%mTlus}4b611YF{8uP.g/y_hF2E_W^4bL}aiSNZT_7=<WjFR+bc$FCd=>NB~X2r]Tbtq1y3.T/(gw4x+mb|rV$>]?kM[r9pqixINw~x{QB&.Jt/0+bI"Vk:[wK)Bx/cdPpi?f>$q[*kTZbqc_f53UD;{_p=g+B%n9k1MXd%3P7!ox"yKJ_=J#wL"!CLnu[Z,u*/9?y1^j6.n1:%?%^:lXLuJ8qxv=(;7pE6s14O+?t_kkEY9M3P:57S<ihxab0M>64yM@yTdz)}2?%M2HQ%r1C`4!1YJug,,:IbYQ:Q@}Fvm+M1E9XNaQ?jsld9p+oaro~gfNF+FV>xGE|&g|/wl[5cN%oIR:~2;RxW#9Y][TSw5dkOkCsubz)i(if>x@3RWmcM=jgmKoW$D^:oUm=ym#[@{P8FHJLTx/l|t]qo1r66jRgms<oUZt1M~J:iDu:4,.l)@8cJzJZtJI+c`oSe?EFCBU/uGEc^[(fDUJ5.=GUJIt{JEr{!@7RvJ6/[xg^vIgsI2|vfdChJDCtq}b<=uZC)qUMS~QOo_nkiy5aFcQ+kTZa9@o%n0|SZC(>s8,CyPVn0_!$/#2zSz`<F~c;yi1+/z@[k$o#I/v^rr`*xJ69_Bp#Irkjoe.G)Om[L@RU!Wpox0Z[?f?dS7EJ)3|=xO^CCoiUaDLMyCM$L:,Q3ZF<[mi2Y>?7/nH&_p+6+/Y`N>)X@ni;5_n~y7KTRZwAqd<|(V`m+0Xw/;bT0f,xue:0Wij!n`;4n3e4]vrh`S6k~NDD_^B+(BuWJ7>q74c!:p?@J,t"&O$<&%TDR#e*pm!L#K+B(tBVGU,oU&W9[:KIt]+Pac~PC*X)%eH3Quqf,ya}xCRnm=Zry+$X[|>Gr5`mi>f&QjR}^d5e+x>N;$U{66#[(It]+A78~~t24U%UiYO1cj&iNX+D_@)S{iKoS%pn*bCoa#n1t(?<i"x>IPHc<6S,E6XMh6u77xig7!7(upPK@CI4VCw)+i8de(.HUUJ"FU;U4XN96cYu`1t[&PrNEv(M`F{q]Zo~G}5!{377`j9`2ngZU!p}p(N4ilotDc!Wqo@#>|EX@mi5uzdVj9v(]2dBt4*PC:4k0FUhqhqu5iFq;HsHTC;`DN=csQT}7SI+):Gj2@t_Jxv;qa6L.5q*=1(2|=(|ETUVd,is@#&_@C=?TE3^tE53l"AaG~UzC0M%;j5ay/=pj5Lw(b@W%FFrbh=$$22YQGDJxioh@]/NzKxH~!rIEfTrF^3NE>GJId]s<Mo@r~LF[qL{0"Niu^rw2/|&m{V(K31]j7dxUzK`Lu(LWUY7/9=35=Vo[+[UK|;356eU@9tjiB*GO3rgAe?jCOCz,HD6P#Im^WRE"Vvy+wo(furu5q*(AxFLwFxtEHYGF&l(V:e(QoSaG.OUKFbQYAp<cj#/g]>Uj+,b%D{V[1Mjs*^LSs#|l(w3|(|c2aPA+_rz902%;P8s%B2^_CJkT>QFj?0NIkOS+*RHD~o2%.@jsG`wvAe#y?US"2CB1T!oj6hAS8+eFCQ>s;DEx{/}CvvIK~+>4F$Pld+v:p<5R=PS%9NAGb`jbAM?;mwEYwghu}<j2ADLleSiq9F#;xfaGfL7KbS9wSQg|Sfj1uN#EuC*|ohMTpi&B><1jW1ek3gC*@P82wg(Q.c!eCx`$Ufibvg*#%<}m0;_{Z!^Am8A=f*bu.6w._$4]I`H?.o:~R*,{Z&z2`aILsp?^;b~=gWYTCSI=o){t"#S*/C4&Okq}2("JB&MQ2#x)`dSw"kyKNDji7/01Jxr_=Zn_7v!rO>{X:,5i2`J]pwykw4)Kj522gq}@18SRf0Fmr<quzitk*{P.&<`FM|/jdKFrnia5rwyEqbeymGaV2h_M(85K#[&1{K)[_ao{h9>Rqt+XZR<n+X]W)?jNQr!efkx#phTfD1!lx<^#hQIvxv3DgDUyHVrO{M|vNoJWhlgd8fvmLf+iXF,E{w>,GvSu:)`@mVp5G@qonnY9O>#W<nO`zB!wKzzMVh7z{k1,us)?zBTF<U@k});$74zjr=dcaz?p_QR>v=#W)4:uUy3Ei|li`T/(g_Lf@k{q{WO:ym{tm+~gB8FSAYo&bfWWcm(1}Om1C<3QCKH/#YLblee_fd!Y6f$zQ:@Ii58~[3zJFJ8riC#_{xJZ+3~*VJBS?6aG~v`=yrYe?B+w`$g7p;=]SqjTi@hQ]CM?10y]:M`J>wKz$`|)uIngb&{W(%#N|T0y9!z7tX1,{TyCM?`SP"a{QT;YM^~~B5G5M^`0azgRV2|$}$roch[D;y&vp+%<MoR(*~SJ+)?XW@UU`>n>4qhngF0xL.BSKw[kW@l1@ke.8hn1f=T]oYTYL5ngH|Fx*LTY4iYQ{KGE;q5N}G[qmWr:fs)3*p?rr@^y^U:m+/#v?O:v}cjyyg+p!$<354tkRa9(vyRLx^;d|aMIm5[K`gU^mihG$<ZThX!G[)@V"X$:Fo46{q|vj7KjpjfpJ!)%HYM=4H(fPKpEvHui#LXVID^d4M^gAp,bNZ:2OYxePH)/by<Ip,I78V/#)/4ZI+LK|a0m0jmBUhtttlQ^k?WkKe?MD.YT}VbyJ%VJml?hFrM#z3"CF_nvZG3vBCkx{;%?c:Zae/PueLxy3wbtqGhV.(B8X)@Vhl89SaLgYX!<lQAG%LJZ=!iTNf_wmwhb(twetVR;>dMmfm[/.DV]lcahsD,)k{gG5Hun_q#AnHun^9%N`=w]4(2UDN1?CKXBp#;P}.{aeJ""I,KKD@^C@/#yC)aOW"03WOw]Ge9![BrAU7YOzG7eRf}w.H*?@>/oZF,Xf&{R"&{R!ehX`y0d59DF&4j2QHdqKyNfuR,cy4h{(Rbb=,2.EL1)xt:{LQof<Ofl<tf{l?_{}#H{p;TYpP~L]u[wq#cr%H.a19Y5nfj!o]+VheEiRTx_,^yz.t*2!!f:!B4B`MkS&98GB{_qPSu51k%{62PX+?#<ztPloyYTX>}3Fo.[xUg^d8sD"pL`q9N3HT,6Jx]dH[nlyrg4@_v&,_$Km%I=i$PT,n/4b/1zfEXq:|9L_IwBz#NQ]Dl=Xi_H7G$%{<t9*;a[F1j3!oeQo#7o#qL<sO(b%f[P7d:y9RdP&w}Q#Gz?6)IMI7]>O?A]e@mYcK=k}:PW[2XhlL8~Bn>E9ZUr>_6y;sX5h4#~R|fb_Zqx(@ZOIB0_;>$IlB8c@pL:GNGj7[dO~7UMdZwH+4%i.jfM*^V?bYsH8m92I|a_(ex}i8:w=#"@(]3Oz`f{==y~?!7i6XL|+ipzol0;M@X>`Kn{M=LI^K8N,iomjlW#4r%}P#+!`W|w^v"c/hY[)]rM2|GM!3$4(!QeuY*mBPY5&`xE3%7Wg}a^CjEHe5@fLB>ClJ&cQuFV&|$:&vQDP?nmrT,^zu]28.PD.V]pekKXS}(%RX<>u8[G`dG*oDpr&$XrRjAgiZiJ`<#+mMRh]Lwl9l)5#xJBkM!#Xin7zI,^e@/)MgF%L_e@Eqz*P;8u)y9,D^:!?F$jt$pN$5R84#<d}mMukrE;r5eX@NnEW_GAC2+yMR_kh;J3.sVa3u5~a$6X#9%5];wGI;D53("Jfy(vp7(*L{>K0;"d_^Qnt&mv|)$eXRcDt7hb+=?jmB{pb!Z]"Frm~kI5eSP>gT{FUN^[{xk$idCwrcCD^[ZMo&2~{C^[#m0iR{rH]C#?2Orm{T$utbozE5?37PfPnmwW~DOuIE)d,]`[4],u.&_7n"gq8W]oyW}}`#Hwq{Y>OlOZrq{6b+p.d*y3x1"O%>zud)Qq~0%2my;Ke^P+K84mj36yERjH~TT{vknBBDiNC9J>^JTHEDv_;wL,F{=z@8]I0fg?RfN`np$3!<@cj=AoL@+:vV#G>pRJDYt,N^&.v*BzNR1`PvqlqzM.[S^wz??zpNB4]u+0k8Q{TW.$WTk9Krl=!.xra+Vy}o.KyG*=b"wkiqOA45?lsjXr%pBe8bq|]x$IrEuRlZj8%2HtO&pblMf7~)]8X=#aSeWR_@z|EegHtv1EBquF"uiHhd5[g^B|2uEsz=OGH%LEroHzgIUJ?dZa0&H@K[rg/5L.gPvSGx`U?]R.EM{0d.@G&82Zc;{hQnq5jN+iu}gY.&d@+Z(%F#sHLm{.T,#7KbCJjMI~)M;e=:`@*5?bT;c@Q^wzn<X?8?e3l0v{3JhqXQC<*gBR$1*3Pl`EO<M(66_T73!_1U1V:x@V_aK{9`|^|vc>dKmg3m4xuYE>0+"GGvaq}U42`pTY$:yc:x1pS!I`R1k(mZ#I8hLwNZRy6JG*SvgnObZ<QE|ow28&7=?<&!rz<zA&FsyeNg8NWkTYG4zCts92"<4OZjw|5K4fll?kBG^[k&EQt3}SP:IljPMfA8~1P!Z2Dk5SPssmygFiF3f9~&$?EW~/Bm_Ws1lNqkkwZG(+gmCcPg{59eL166F3*FbY&QW7GGz5ad>:H302B;(61cr3|5D=X#8F^syOQ89#!:MBe;`86g0MDG7ZvuVz/F7ZXIkOtTj<1>Qw/UC%/{,y[<*R}m@lY@$*j|o@YIYvLo#Qr9*yn8<[*3wno>EN>,=*^e}4?2%;5/s&3tkw1[bdVNIU1R0@g?e+K&KkoRPWB|VW^+VXfvv$GZ&j%S:s}5T;1h&oTX+75o/e=`gJYD8Sa&=wtn>E6q@E"Llc64mYpjx<[ws$`tm7Wp99J~YGA?lwPoL>T*.Oyy6gCvn]m2+cGh9]4NicQje98lQbT+bKdd3QOft9b|O[Df=Bzl;=<:%@wuDYde,yu63(D.,z1%YS1>|MXy?DfK`i9(~I<Ext[y4:4URq.t>`+}Ag^HJBi413fT26A>:SC+m/T9LH)u?4bxh#BQ0E"t:(f]gI!)zEB{5|_c1ot)2(Ahetc]i+keS&d5dZNn[vVKn0baqKCftK1DX(Vr,O)QuHY|uq:R@)tt9Z{lNohSKE98U>2ky6_uFu&NcIC!%gFIn9WI(_16DZLZ!&ukWybrxMu5HG_qSJ<wtb/g`4iRL@[?@W!{>W(<Mlagp.#V<nC5!?1v7OX$]>BEWA[ac}bImY]uuYMGFt1L.UMI3S5%lBlU<ci^%?gD@.Q]aS<p<w9%/|d*Fwh8ZG[k8p"`vVClqT8^<w[9#7&<JJ5Nw69H>ZX3e%ep7m9~G6<px;g8MV<SyV=^t@n=o}{[&;I!TtYPic(bGKhJkaOT=<yaQs=`9yVS!I%jA7<;#4xq0AN?JC:Nzj*=Nl5sKgGu{]VmxY;h>7>GJBvtk7pbkEQ_$<T.z/aOe{hnr.liGQ%dFXK}%D):A_i]sJ=7GB069dY*l1C&AD*t6OG#PjKa@$>gqX/Kkzl9[f!cYH>cTKUgrDV..Oida3k]`t_Mp6>ym/BRSAv([NC"{E*MEim!<)2EwsUA,L@*FdAH]ZlLmXRv[FYAAAAAAAAAAAq}eF(rLo^}b$Y{REhVEO|K2x)UtbPJe@5ZT|Wp;6bRiQ11s_y{NV/D9q[OZHYZPy[v2hFXn/t~N[Y.0QM8{{/7#ql]iICIO{{=b#1PWrb>(/?YP{8.>s|RN0a6GF)5y6BE@.}lS8wq=Fi[$MPE~fF<wBg94OoHA9g&()rv|q6#[}Vn[fVl3V?/1|"I(RZuj$nRuE4<Nu4a[c!hxo7tI+f4s;EqnkI#7TA2CVt(*rsI2.eQBVy@0Plvq(I[7ms8QlEI(M*0E(7SDV_SQgs;L9h~C#Xn%FQrCr7`CKRfb$,F=bDx!,2(2UeE]{0oDo*`U4E7(m5(*v>/NY16m=L;*n3:Y058yl1_Ob"5Ja|WQ1p%xH!4eL908_e/wJ!Xoc5"igcGMvZ94m&:Ed7O.YCxt!:^J|O.ICt+02z5taJP5d<$uRIM4f`vMw=w5y>!S#+["K9=*|X*W8##G^zh5$;yR2xK:pd%4~NF0!+mhYEJ$SI;?3z:<|2Nd,VH:Ju|9La=_H(KB1Rf0P@9.]<6D|~#9I"5P!8dOEMC`e)D>H}R7a]u|g{MRU5>YaY}]M3r3/A~<loy#ht*^btvZQw8n4tupwZlx%dXs(!^a<jQ].i4KAxO>l~KU%Z:o]I5g@qU1T*fYi],bE[!zw..xS*BtGkS2<qpp2S,oN&rVz{9.K$XeVEY&eW[sgon9Z=;,=B~5^ckDqL{T}%N>a1FMiMUor!vF[q^i<Kp4XReg`RO&FI4IPppNHwKNV#Q|1m@"u%HHrcgwc$q?MuvLB9xrA1w*vR]l@3>IF%>:?kAUzdMR.BBRfs@C#zPWnoO%">.HmOI/c`d#FtO/*Y`O.$%!07LaFExniXHh~?/QLb/jm?bgeC^+uDWq/LdDMHa%l?p@5;wB0|%``NP@%y~S[#@3ZgjObv;[^X3j%@qr}+U9+lif)R?KW5rN8*T,B#cEf4t/$JScp`m[>Y_?(xL>^p]Z=QZ6ljFZ0Dz4T=E,9ifiX%]oN7gDu96X0(qP0<l|}ZiUHxbf0Hr;c(7.W^KJFy&*Q:13D:O=G2=LXukoO!L+ium`|%O"]a=3YK:Txj*xUdC@WxNgf?;^x?ktJ2i6^wD3@ZZiiIEq?+eA@v>6xa+KjH^SDB}FN8,^Z%Om9}c6o]c<k`B,,!y4a{5+LV6q}va>c$/eu#$ju%F[(Ad_NZ*bR!;;B!_Dw1u`cGUC^h22]xVpnW,XM_Nyf.hrxUEE_R*yAE=9zB4><=j5pqL"j%o+wxjq1`h]{WL|Udn!pIk,N5Z8[W=vUo[<8c$[lON,3@tsXa*Q5[2xIXqcI&16Ue6Ezqw30[kx|Cp:<mnD5C:3Ig|tJtC%+6LsVu"$WcrX(sR2>g|gb8I`=Tj],[AmJz5._4fQ*Egru56@*pQh~2Xr([iz+pUkm1yOs%5vz?.`kXVyreX]+rNxav[qR1F!3z(vQcvK}+xnM=V(GAlba0RS_1}H89mzhUjTJxK7/h/=%r8)yiha_Yyo.Y5Oj`.e%)!A{vDStNS<%hL%;Qf~d2I{DNKZs0V..8Wb:=p71Nt^NkF@}kT7|_KccsP3eieO^zR,4x9||*ZIi?3"?w3dhcRxBzQ7&.!F<P^7`,|:FyUUFpb)Upn_5%y>_/QKPrE6d!4bo)_crtI|:a+Fs9~^62KwMWrlWzl@^Pp#qvhOx%,&#g&q$tdm,O;Pvtq{S4H`+XY/]HAtFgL~8*>pq.OvL`l8P4LQqZ`Z*l&9xi;nY.m.}3:L_~hjM{lAM59J4wizs0pM,+.F?#Mc7K,Sb"5rji}&(ZZqP[.$XD6_;p{wF=gG^+{HbKAkFGPEtk0av@.h:zCUdsISIw<]U[r&e22`%kD0PxnA;UtYQnPkIWwYuC=.=xk]YUW0].kj4Q.PMXA0`3b*|/&mYdd{|{1>PMxMghI/^c@3BU51GYaq;tK(5/=+*Y(3mVR^gvO>e`Lo3AYNpbtEN?QvQI`XGF`1$.o)lneu|hL$fd&D0u$3r&RFkvJ1hNw)R|]{IGSh4uTSwei$Uv}q@^o}?7cX&K>HQ0+Oz.1&uFU?~WiawevSMvQ*(atO6J_Rtck)|uhKx]u%JY{DxQI&h/gL}mP2a]]M%oV.o2&8u:tEW|E,u5EZG0bha<o6l@9w8=S3=)bhv[$mSPx"sJcHD@O!+2h]]k$D!,lO(xIfygYha?Lv;GasBGA!XL^Uq"S>?rhCP<J4}R{(@^g1WGjlop{k)PVM.@=9`Puks*_V?QLRoIrfmCt{OW^aNN,tX)TSl(Mq*=tYre_.7k3vP}O3prI`u+bLX?SF$4UK0j?/tilr_:FEldysYcIgy!YuG)(T+TwuJ0~;/TP:]hSMbLpAjW+m4aa~Ap8m*kJO)nv}|j3pCD;GUVEL:q$MsD#)}Wh%VX7:P+Q.dOu"*L=G/HN3i0z}4c67/co6KQd,lj+Kwi0a/qfn*#&5i3#NlSj@_Jad"PcPP!Ah>*xwo9kx|G.n.zBKkKCE|7Ek7UlWjm>2/`W#*;4{/Bc|R;K&grD^ReBk~Y|S2!:Eh6q2rzzAgmmDGhw;2F"v<4wfi~[Qo"dpE&44H1jCIpIb4Ga)nIvKG#;zK`d.6KAD$_1"a*pb0uH!gy0j1e>;8(IzQ[WQ;zV*v?oTlG!p@~&~c|vc`i?L:?lA5%:Eh+g}/dVg0v#IR0JVXTq2wB!x0Qu,GD{GDT(RZ:Z5Mgh@N/sMX^BcJ#@b//(71;ekSi]G~Zu]r#w+WDQ2z^oge&~=CF{*8y.m@R!(uI,t#6/3sIN:O[SV}:]BmeJEr_Es,OJ[e(.I,[aiwp)!.EhQ(gvt5zP5@*WZ3v}.qaZux~UvNnk%qmL1)f|;1v%Zm/lG9=iW#ggGnax6Z$5huu["t/eO$9u^G4?ba}3TFqBNmEq)r.MWVux)DD2J+B@[5:(pMebefAz/)d*Pt[?58sP#NBEcNWh+gLmZTgMORuI[]y7uT>J!3)x4cKi`{X?@fz"pnDzNkV0+i4iCI%l[`xOy1By"<ixFb$Ks%z9lu|AMwF|[h)MddsN_:QCvZ$<$?)DAF?[[ZAfu/e8VhtUE%!;epj@_6!lbw||qv]Y$K*l5J[@.BF?~sja@uL*SfFY$p?VX!F~Wf!3Q1GO_7wpjGAg+O+>CP#]fg3k.wU^SgOas+M*$arn)X&06+V,d3X56b|C[!E=)}0qo2[pZ.*`*[Hcn>rs+>4HFD?&DPEg]nm9?j_du[0eAl|`#VuCV_:`cVmv6.j=&WF5c|zaHTU^n,CVW#KgQv:5n?VI$lVR7%{7+F(~a_LlHa?^^ze>L!sq5)?{Ip@3&~H^q*3e`]9M!Bsz6OFAanOu0:`a;710[D#eATj96|k!H]uq2#0_Vku)641oCmr|^N/4G&tFHpI%yDOHO~N)sS)e+B8k>LTNK^)(6jy0A&S606qoGb]lJ!N1Tkmy;F_aEy[DA!=G=^kyw;$Hk!X<:cTo6U1<*U$#Gn|;(wY+?OgGE#~k@QODAqGAc8o6w}75m7kFLv4gE8$z0x@!#1"7VXF}z1FM*3BH~[kBOVpw}3*xDA[JK|1P29__iBBTWK1[CMQKvza}i|SYZJn8Nu`qUVJ2TwM]/;Kz&i3?{&F/h.A.n*%_/R^QM;PTl1|^I(rC.y!~Fl$ci6pk:aUgG,3ZiQ@ZK3"("KG?0l5(SN({l6<Ii}|]rSTqlZyi+`MR2hKgDqJZ6[Vqkxbr$X[N;v*hbE(.b7e{m,3E5O*t8PBstu*VxqYFy~q>Ko8|+52Zn7xy866{LR4*v#b[3sgo/*EU&46M".K5SBc|^?4W#(5k=VbfZu6hJWy,KGjZ;|@fVtqZ)Fw;7E&n0z]@ymYtc3&L?_6hSxS~;ds[8be.rY(*9R0oE+x!+!c*y;}9Y<~8|D0Fu3Tf!Ee;930#[S?rO<2P6WL6XF_W`&kp3K<$?G09DKKfnbP:XaLf*TE7L)89>9J~H~Cad7hYhMnxg&bKX{hhgTq%#wStb!7U,0VItl^#~n/v0uZDOX:koCq=YY^A#qSw|A}[7eAX%)3a`y]zy(:4[aAlLJPR{!Lf@om/_`=JO&U7`B_&|g20zOE,jfyj:jPO%g=G@ouOf8cfdgVo5LCP_TI5vbVYx)>_"8Z<>;q}BC<XoVR5Z}}n59zVp[p$(g3RdmBW%*wz$lpCk@z:#Pa7~_xC{5YtraO4fF#8Nhfc~DBlEr|a><;h`O.OHLsv?U4ogGlj>{}u?QpD@A<#+!?8NqqBR8hRFhrOtD*,EPL}eE>zpx!Fq;ofCqcsf[wl.P:V,Ws,SH&ak2nO`:okC(1:BV5|Y)BhFjsgh$`M=[5"s?1swmm%]=?{qEhNK/{4OC$(+$bCuGw9p63]Kpw0J,NwFXaHTKL@o]p,Y`45<x/`%S}E!y`8?euL}V#fD}1"X&306"hF,>NTtX3HJ{%IDAB$U?!Wp@74esZD!FZ?,ER>eAR#icV!A*]5(?Y,=yKIXGx%>7y{(_R;k3zQnTqDNb"2fDtZZL+&eYj)yZ%<VDv7~/.g~pwrCH~w?&a7!!m(na2j`ls`V[&R@(4_Tzy7<d2aRI[C]}_7R5&n)#?]WxIG0O.{7G&6(m[6N3Gw9[tf?aje9JZ}e>pgQ(mGI:cNP}zFydD2vxbXAmIVuN0=/5H{=uKha.m{6@N@m:DPP<q=}V~Cl`("^cM)BsO/gu&0[v?uf.3gEpwXXzplheZL1iDv8/H|yTdN)O7c=w&eYOu|nEg)_ge/(93.tJ:7{ZsOjT0|y&hAKxnw/bG.S8xb7jHN:~r=9]MuSMoen9ajIQlKZ7Y<_VPUR1I.&iM,}8If^#:DO51N<]tR<!"C(=L`A9u!(E81#nr,b>K9&+)PEy}$?t^/V}@&$?A)Xt/UO>@H#%|{JLoR(v$<;2NE1f/Ii4Fvi!`Nv,!ZU+Xu~L%]|S6xkZX0zYOmve<5daMTL3|vj0A$HHf0MbYm4Ops)sbD|Zfc$(&plST%.*ujLZ..}/XP%%5Ir,;9q<pail8%qu~wBkD|sE:fkW>_~H[yq^@HZQ]r|LIc.tXfc=nO>7*q.E)3.s0iXmMmjS4EsCk4%dDCK$M5,ygm7+&XRE7?(RpeT?(kO:27i%8&ogOH}yF!15+LGN>6sA8R5?GSH>0no2fqE8&L~&j#4(d6FNw}C>|vxI9O:!V/)`4!aH5q][7&~+[C~c}6,z2pikNh9FKB~0Aw}Q0|.edzV5O*Kb6c8rS<[Lz/=n+OYrpdg/:`0vRJy6Ne!.odmaqDDoDE^=`AoxZbgVC0&e|*0/lr`T.)d3=OBXGm/z|ax23]2`:UT40|y*Eis4j5Y*wvpN2heJPkTVR8^:WS>#&t*d4I(rFh*iuJk,BA>>ek@k`="P#7CvxMTnE=vv�qm%JXbj:]5u2,<HeEhl2#T@UR}*dg[JKH]u*!DhW~mvWl|5RUQk;~QnI909}=Ip"uc{uvy{JlWy9BI7i:M0;lRdSXqoXUd2rI|yVrRb<Do8Gf]4]1f[[V=|dt6<BJ`~IB:TfzV0#_wGVez3&rp#k2o@0<>*OgX7(6C*UIa)=94CHuc{3cjT.voffg!@E8FKQNe~bI:v>sFp]>{2r%/l~1*}u#lvAqt:F0T49L@&ck`(!<xNSfD*[<lJ=^4oOqo0hSY/$WQK4O6{G:Be&FWK:TT(#LAFI{Ttx52[#ZAM!?a&3J8x}&$+4!wxKF=_yxSXI:f_fWpMpO/WF,j!R/j%$vo[67qst#rI#`OZ4{~NgWbDw8c,jnT|*fx]!_<Sd`|l*Fv4k`N<h=gpMGPa,c$,66ApWV|c[:|EQJxO/JsRUwQO/RezXuE.$$/T[/r]e;">85>:#~JihO,KM=!~!eEN0MX~Br&@R|UgJ!Mvm<rQgS]nb26DW{PgA>"{BY7"z$w5H1=~a~<{+|f0&L]V*4~@;wH)cg]3]>wnI2A%CT9D)A[T>?)c?,mpGa(Hq@1nO4xgE!p|v98r7c@:;].QZD75op+#l|2[z*K]$@W61!{[w<uCvg]3|80fe`L;R[^p!57yD7Wq6T}qp$`mwP,b{e>663]K2^*ix4X:(xa#xs)|uP*#z}zB{.95r&^.I5tdF?=vvh?>+[XX*e$q|vM:cY1?L.`Z@I&Yr8v{Fw&J1C<a^aqFI%|8GwXzE[b^6`.@Y46{VuFyQx;yZL0L9U//=mp$9Cs*d"7+^(6!$hWlCsE<"U/<|>~(bk&_@oVz0n0NK>/lxIXQ*J)YYV/9Lig@~4h)<HG?jdJ42pKw^QJ(C/^C?+_aP~olbx>R1a?&Njezo`dxid3[8QmkNYp~Usp15E@qxMhRSFwc;OnJH|@*7}b{<]F(+VM7R?6MEt8b1p?]`2Q/a#2ld3P~W~5d1vK7P6;JDOr22C$zcjf%QB2T6)d1bVY+vT1O(uw6GqXV&X(Y4P!O!Y27XnhFKxj85.ugvBI&i?q[%m^,>tw{kcGpr;qj]d@Y)|32m%X|:)_Ie(HKm%):E31_xlaIJV&L[3F){Y2X9O)i~>q3tcb}gVjo4ysom1E[G}?/,7vG[ivnQ>qgYArN[UV4|UTM.Oat2q}^"4kHnTJak9l*di+k*GiKzDpcIG7x_q0j{!~eLDpk`Zf[dPAH8p[vFTb6vb!U8zI!DL!6rDvsUrW3JzfP>|6Rg7jBZa4dmusw7$}gA?u.B5i"m{m3~lamb);_XVROwX8Py%l#=**@sTD)$=+eqH^u?Ov4=Llw>3[~a>U&cidQ)yN0%`w>a+FTzz4:;c(p3rW<ePfTSga!a+@;Y+PCc=4*@n{zlVL<>?$$[C~_Vp"`Yi5]gGYqcur;|AgD7gA4W$5>RXZ0Oi]YFO<1#7ae0=%)X6Dav8^#^:H$ytb5,^Zdz`&%"@PDZ*`Qy$h+0P(5fo@Pma9D)lhx[@1X3I;MeBd8q1g_mTdBMx`k:Q(wiLm;%u*N:z%V,B[(%6cfKQHa`$_c]jqxYJCV)ddtZ$dSvh+JP]{8?rX"C"Svd`LJ];ix/QElFe~kLf}8Jjk%?@>/h[M_l$m#zu9[9""v)=.<kqC^GO|SrZ;r%l|p<v5j%Xx@[)5xm![D,A&h;g5GB#=<BcCj89HFeQD[})c#&!!k?C9pA{{v:3.v?`ru}DKb(VU8g[OarEu:4SQ_Z0o~alj&|hIq3xzrdCBw=u,N)]uuOo#8fj)%=IWv*AM7f3b56_*]ntj|GK9ZI`cyWw%!Pc7I121DztJKP:c9Y`sOn96vQdgSy|L"s?;<4m68]K%{gXVJ_R5wLZ>A"Gw$;#|eLdw^@oAb@*gs@;Rz`G^~2i+<wgOK/(s~u_Q|gnXs6xBK%TgbUKl1sB?@w}`EcoI,Zl#.DtRg2W2.VkQ=sU_2U!/BIQU?LsdaC&`<b,*(j^dTW&#/Qd,qU%Zpv5)X:H`H__aoMmclN+e:SF=<tqpf:w9#NFuAV`Y._K5ioY4g.y2I@k}C2e"6w[%_TIhg~JHX*1>#(8ZS6]Z7j+h,g^ggvxTN$S!;iw6Tx<i~yWXH0wOw21|y+*oiO(jC,spJE7Qge4]<WbU`O_s#qYeYQh#gki*x,kuOD#wn*`9pE!$L)67];)PR%h[7vb[|LYg__YU^N1(E<Fhl!+.}e@AOK6Z1{[;xobIt:YB~gz](dG.k(O?+frB11_nXJ6PMKB((0YC&Rbq4WExZ/M[vkerkMS3sH7C*"PD&Z21jxpG?lH!e&lU#=QxBOj+hLda=$MPIddJ#P_`h}kqk[W/;X2XBF1RC9bCjN+@gtS%H7xBfeS/C@[@I%az]Kr_gZ(<hIh|kvDaqZV#JCdfqAaOWQhXj+]I_o;~f(=2d(L*K<+FBXdbbe4|QCfT]?fee2VybX3xyskr]E[BT[,ev*ctKa8$U$!*Vj^9]WpicHWjp!v?klhev:o~}:H!AqcoQ8vyb;<+NB2Oo!T`33>oyY5s"M,s{w;wGWo6<ees{yh`Qh)2SKAJr*$]=3bQgDr<PIce0C7(H:KsX+>gtl3b2)pxr8c<Tfv2!A[(JueCvh91vKo1a4X!kzM2^"lp)VR{q%]RXrP/dL^i*|v!WEUtGcJ2W;v.NRKU"bY"sGodX.MZYD?9RAe89=lzVFFU]cP#(3n^yOJJlcHI/T1wcoN|]fx2n;EU+UN}8|!%s.;%:aW"p&x{uo>G2kvbkQ+1JChRmwk$EJL{tm&<2A^@(cuD<<5m_5G[eaX[r1$:fQ(RI+{n|ZFV3BPh/~$>PiTMjrpKLBVjB_rn`oD@[m/y*)Z@Vj;:y{45%W[+u@zQUfa}goqg)lP6xA)~P!9L3}SW$yo&zAJMKT/i<!01t/<7i>;O5C;Aa.w}:_x*2t(pBwJL4nC10[<0E|ww6W4`oj&tI*HlPYI=?w?$*QU@gSO3vtIuHKIV~[9@Vx1ciDQDM/>p<Jp)xx@Rh(/);&}f]JB,vziTe7eqyvZe2q&,:O?#jFy4mzOUf,`#(a~,=]`x}jDTn!>kI.$%yQCU_z~PV_hZ?~^`51c)(u@?+I0zg1~V[B`R#6WNEZNh)@^,]s#.8p2SCc.cGV/LoS[D,0Xx$_Lij?tFqMAUFsNu;a|X#A71}p1l~MBb+"09pEsZ$hT$eKw`a9YU+~XdOdM*{KsCV0KRz#;SiY<!>`IuPdsMoi;xn*e/uxdd+6SmP`ruQXg,Uh1$o0qkL7o&Qu,Wb0NV*a`P6:VN&LIfeFcL"u8nF?%@AK:o:zllz/Hs5Zl^<i>4M%wQ@[OM[L.V@YSC;Nzc@sCg3HcV;H0=uxpPaJ}g^Cv}qkegfu9;2|Z[)>8B]2gY=5d9Gj.!QU2|&MXnsz=b^[4%0lwG}^o{yLJJ`%A4B0mSTg,]p":y`WqMED~s&5XpmKY|0$`U2QYok9^pR7:&HPTL6ppS,DPah>r)^~pLCk.g,38HU)M*r]%,Ksg]G3YK(5jkHvy&z[:n5r`Ct*_IEg.M~>?vC>?bqDBKN6yqs[8_<4S,^J:o2$#e!P;OwPV~p)22`;n!W:N|$BplnXDL#m:|b+{&vzk8"t(*dXvzmRp@g&9plgB|q9$C9(6#4*:Kc}X*MaiaDd,#.{+}G^!2OdtToC^m#<M]3/X28dSdvjXfk<pw.bp~M:[yVVk51(gUz~%ciZ8>z>wdMX;=7q(6Q!)s:rvK+SyJJNa_yv;;JLhH`6KncO2@p?p4Z,"Bm?uL8@VMp:]AX</Y/c#X?6wdWXPaEK=A;P!2Y;D/0]aYejgj~fl"v;0g*moe9,&U}N3>?$Y^QRZ,7sZ~gsfHbC2)/$sU;<mPx)FL/TQR$)g(1.ER7O{,wTDR.f:1%1Yn5s&lD$A%Xx8}e=8y*3"32i,5ntT9.m$x*=8O=dL(e^5fj(.Qji"lu;"_M$Kr@5hf4*`/Ry]U?[$<R?Qar]YfS:cgfFi5X0>|?LYzqoiqN@kC)JmdhhhK&6d4]~aXGJ|1~xs)k}74T*J{mJhRn.7f8)F9);S<>fP~ITd~P*.3@,F@PF5qj3$kR]wAByhrq}C^Z?*eZ<ls}K4+"}+m)~~J_3kd,f7](M1p..Zy_8Nn#g|ubjyy9*RFw55M14EJ?NLUs#uVw}dOccklcO1":Q/hOuTz:n.1]UetlXbc6.nw<D^F)4f0XJ9`rvG_<#Q<j4tMj:7LFj]+=I<sV<Zn`:!%M:>1}tNx_oL2RCR{69FZ8LJ2&+3pS6g_/E3F7ZPbf!>bMg@bwJv.X|{c:J,oXbLlTUR~ePk~ONPp1^QJ%R=N8h,E)~`AR.]R96)mz{lR&+17rvG~%xwu(YP(.%O.xO)Q6&g:y"o>6*X}:~*F#lj~!Rx}O?EXPC46d"R6S#gycM8lmY3Sqh:YZS9$z%}P_^`h~MBBJa~HJPB5E6o$rk~k&J|w)V"w*=;q&,w,O7Lf)Gc*{z=+fJLrJ5VGDbHm/U{r+L+(JdJfdmo?ig[GU|p^X[KW]|l$e*5/oVUJZZ/f:`#[!41t>Z8E=egtT_xb/u8JI[anv{o2IKkLZ;!*O~;g""wkURo#kplDCS6HTr?l$Mv*fre%>S_nF!SIvZeP!fx%R0k0p^So}LU2JYg!5MoQ|[O:Af%VTX@4gA{&AVDigvo~=P3V;crspheo*0iqlR&Alfhu!j)t&UY5t`_^Fl*8_`^z`=@6?&q+$/jw=AP)+Zv{qV3k6FuaQsfZHtgnZF.HYZS0?gqBhF9UQWkz!^xVfqXeE=[Pbl4<jzoZAMsT664FFd]AN&1i#Q0jHbcoxokBa*hq_4nk)Px!f)EHjf7>^wp0I/Qe=9;(!u:9f/vG33xfC80Z!KYSo_5Y/%a#m$9t"!0@(Nf_?7~ZjxR^7o~s,S/[O[GI6N6cfKk=Ws2;|)8k,}&`c?gEzxa#sm2F(UF|*@T*1e[R(Ox$&u{^YcBb+`eFg|+6$wFltXS8>xTL,*B|+,Vm{WMXrieFP|30(^+I{_L@8>spl__Tevo&4p]?0|nv=]@CHc*bJ/U][A&;7{ha[%,TW00F#C^/Ib]ZR5VdYR/J@RNs[wC3a}3EhzgIAHDr=,oi$P*D9QB}.z<cX9dguuQ.3E)]mTX2C82{YU>NyYaaU^^nRFW^T3E03ER0E.Dx4.J`Lw&veUpPBvQ:#pD>kOIQ2!~ztMz_BkrkV[3hI%}+Rp{p~CZTFZ>S]/H&$,GN%GLt6jPLvo7;}H*Yvz#(BT@BEUJj/nEauol/e$)p+vPUw,|>3FjoI?e+ivO"diaQ63GAx[_]cn[T6F)0F*rGo9g88]7sJ](P+fc7gt@q2ew.VZRwU81|rO_ut4D2{ion?$dnU&Sw8o,0=(L+]aRC",jX"8lwiU989xTDBeqGziX3WoE`uPc=*|99}>D|<bC/wB!I2rwpd8`rHiBrvJ1kC$<VfhZ!?4yVa5^?h|S}Kr[2.^wJ&FN}:KiQIYq4dCi1R@Lr=N$SiQNr{*K]29Iu<lo[R(9ZnNAI=q<gg.Yu0IE[&Q1]%tY{m2Jm4,L~1zS~{14p._{J||fwhle*3r@o<rJyl+;G=#!/l"aIS^5FF0P(.Dxl<Y68LO)CI57w[?VWbvT~qc_SV)*"GOoxqUM(:F#Mv4>&M:*m0l58,Eao>]~:R}Y*`5r!b1M]W<)r,TL}`erGClFEK/UYBZ[#*`x`yLisI$IY~$d6UF?/4]~Cq=&%?JZSl%}y!D{ZVM)mg_"2a+gcae(9M_X7e[R%Js8]ohCH"eCgrQX&GWm(r"7(*q%eDMt%EnAe0B=,1,nV7[C>i+I8{&VzPn^V^a*T){~MJFDl?RdDD"|jG<]b99BcvE964^}B1KvLC,Jqeg3;tZc74Wa8"r_T"Gx?c,KEcPU7Uyp=mW!P_p/BQks*)Sv@*fE]d}M4R:e~*a@k2%JAx/<GEs)6.6FPRHW<[l}.,[)AzK^]zzXCI?=@h^Dut%7@%|wefkGGrEEM/2hbMt,Z@mFw2fmtFmamoDKIAtt$/B}S^7Fc=37T*#LCS&#/$d%`7RXn4WH^6Vd;F^*@mV*|j4Zl}_B23E0wh%d=@;,?#amcPw$W:3plyWyx&|uI[xz)kKbZ/3uhIhBdFk2ZT:N,6F#a)tBKuwFEig|Aw1dpcX[}vSUL(}|9F`f(/c66WB)(qAzWFHzyK}}MU9~Su1x4v}}s91U!(Jrms7N[jNN0Cc>r{Uzw,roB6a6PoF3Fx"NjD94P7K0dJFYi8]#:v~:)ThZVnj"Ugg90pEUov+W*0[7]T#sD1d%=k%5F}(1^V+C=<t}T_mK`l>dbYQY+a]wOxKq>ZW1>|FwUM.khs.|@@|POTPLtPc.ph=K=vfhI?91NC~J<)M(Pq]9]VyKgz3hINg8@K/5S!@NW.mt,6es1t,UXBps%/%{ZlMG2Z:rAD9lY][N,:f4pNi`+.(D:+qoiNx{@,N:Qi#mPd@b(V3>)>yCUuK8Y@BC5ifeJA|#sV>SY>]DICZpJygtK"8.]FFn4gM@k^(V@,NK)uu;~p#5`_A#/ZH]IR"?mEmHUpyR|LLspwc3xhumlvV+FqJ`:2j4%MJH"=K<=ENsOfOQ1Sg20z}PilX/ADW>aO&I&ruJ1(dUJz4Fr5/X`@7PrV74%#cNR.l{KTRH)z4%%N>KlE=u8o(2{y+?X<IV1pf;*Sok&^*#}hdh|:x|o$W?{D)C@ez]i4W"lfxnWea7R|zC8h*jf(%d2O==}jNb[<82dvM!UI09M/GU*x:q`$bd(rFNI}<b.7WR>L#r9=h3BS~GEIqK;PJ[mFa,1<EnHaKuIC}_([;1d{$$v05T9QOLK_"Az;/l?>Ssi84W{ZO~RwxrA0S&%kRbR!uHe>7ipWH,S4"L)4tInUE?eq]O:<P2]w~4;T^FmqlVIx;q)3|Q1L]8)nIkt;1@&aT:@7deC3%hUO}?u8ei9|.7L*?LWYB"8e<nUEJ5WKl@BQ|C^3D:%E1R$e9Ob,f@ksk%`+6>n5]R|Xg9O~Ip)|)E"IB5:*6@@JQL~^teht]aQTl,f+YsJJ$4S=fOL.@EN0/K,]oW<%w;*aVRhaS@bR0c`*Fow8`U4Kc"KSRbkER>Xq+2*jM[]7(NwH0HU>Ocr#sSUZzw.%(8nC!NU`r7yODaRa4ElAG<K3g:PcD}Wpq.V6OJF>gtinBtD0]tK4H}Sd4}>nag#tXFuS}cmxEv2v+75pL0mA.P5D=ds_li$<iBe;#4TXGb9AR[(6j2r%MJngU,iw.B9b2@MK`ozrhCCroA~`FOr_^i6f3W;2*hBcJ[XO2AsERUO^:mr0HZJLHyfkod@[aI8~;LR0*2o%vwO]hc[07YjRW"CVc/,V"`j;|[?&YC*lE.0^4Yk+sL_`;%OI@Ji.A?xp+BN^SKra*jfOP_2/Uyo;!+>G9#ZxV.({o_SrR:4S6H!c%1]6+y%e.[U)^qq!p~8Yx}i&s+]1J+_$,uFqq1G~V@[1UUMX=J?wVwm[o5|K=lVrK`NgZX6KbuJp7x`:rmO%#Lh~m(W0l:=B!eDe4|k2r@4q>qr4+wn$[(H&fxM@m@&XU=a8nEWsnpux_zS>U[M/k5&/b]h.?F]i2*Vx=wYhW:BNHG/o_B~J]m*{s+p>vGAHrzRA.i[hwz1qQwJR>Op$v#w@vD`eJ(o"F/`e^Q1Bt*S}5?|l[8G`j!.G{CmyX@i;NM1eeQ=Nu+sU$^a?beuLf1ZV54I0(&xo:nX9B6u^rR:/O}H,V#=)eD(n4nc[fqna=3LO1$(,%bH9GtE2xP{G3PL9Sh"@ySp@53H&(+S=,HT91bH!R`8P@Z>0&3O6J22a=DL"fF&7bB/~8Ci:Q.5%wsRAv[$6/sM!hg#f,H4;d1U;h9VoOsCcJ]=l`B3A$2*YsCx[l6u+,SD3m[K:MG8]":2D}0TMSBR&@PD5KRH%*<(l"c$4gV!wF&MR`e7&}!(.OTgJs9QQe|/y0mLU<3WQ_NqNjQVz+[",)kEpksp(DLD#YfiT:v5j3Dr~.X(}}(}#>f8f!T53aCI5P4@:yHO]84&3x"g`WV?W_%`3<>P`wo&in2*o19cPa$n48)#y+ff,/hP8fpel#H1Mr,@$Lg&$$+q4J9nuwr}{fn9`7hIZ=KjN_D#p]gB8,8w#8bz(G9f6NLDp}[q{K]K{G+$LIE|OY%:==8:bzK9JXTwD?y{,ae?orZ.s/6zDBy=c.4.z!=1/Qhg.u}%u]w4|u16v`p^s=,K9@FnQr.duOUyO}iAaC+<XP`ih(,ZB`_wZ>F^4%h^X&y&C#M]G$8(kS0tZp&^SCY#1M3:~ff1JLiE$lV7(>}OmSNqx&2#4B@Jvo&zTObL=M=_/JJ0/*KZaw[J6l^@`(.#`6iiCJJ>y#[imZ`]|p+nDB4[T/?P&]g7HBXr:)Xo[t0(jNh%#TGc|!,0ep<0&AC1k,y]uJS3ZI(=2vu=j?Bt<"/3crwM];zs!ls0S7Ryvnb@dz`(BF.+>FJ7=dGnmWDLye>sb&goSlQvv3z!DGe!ENFW%##xT=fyV^NkPO.NK$LH#3csn)O"}06xmW*#_3:x|9$fP<}[,_<s_snHCngP.#U#%spp9&3F*wK"e$UM,~Z]@UfC`WSV(vNkJ+UZZN:N!f@vYB<gzB<a&rJuLFRkVTO%}*whXHlej8EH,W!n`I@cagf{:sj`CC.mqcpWk*WFFM=;ux(pu_{s+|ov>pT(C<UaZ7@Cai/V9wehsKp%f@v,e&q#`<eoblL.2.<O8cM8ySR@5M#A1Fg^j#wK+gGBw=d}qIp@O#qm[#QHUzd1ZqY6;j.QNnGW5s)~D,un!YwwFs&DmPQ5cUF5Pd#!!KoR/ms,s)$yLtp"Wf`awj8$5,1wjqkvtceyPC_gBvu#cw]WqpF0r3<0B5mE?kJP]}q%Y@j=6axJt$VsvzLkXZ2nBu2YRgz22HWHwJ<4ek3):C{PNpXEPymAlQ=m/zQV%p`yd)*0c{`/>_g9!BwL$p8/0{O<[P0Uet@X][l)B`t1f,}DWc#p4fe8J|a.T4&(#MdO{,T;@OyTMPV.V|5T8;~IF^QS]{>_B.OZokX7jd=R{,h&zuGT?>Manzp!DWh)s~H>@E(+!LK{amk$JZwJp|}c`rplY[)KLA6|ydJM|eq2[^2#n?.rwZN9w?K5B<s:|26["qZzz|Vb`th|xzBBlHY.1um$cll&zAn<G3$3tCyhcUAvIL,F*K!o9b_iz0LVU,;$1=$D/KNZB=dLdJFzIT;xk"?uBD^A9gl,y<64/J4t]kY=BX=@wf(Z/71/VU@b.qzdUHk&5Wy<94C]D57x<2bHd9px;p$:{m6Mlp"8<I*"`::fYYc^9?W#/i5|yR){#O*xY)~yGN5,9w7NE]WjkjK~MFv~{$[<Xci:{=sJ?!JVp*)FfcdX=CH%d0f]hYVp*r<d$.F1|{^zv!lJoPYY2q4(K~dXPAIFk!0/M99gepEQy&]Ef)J;|MeW"N*Y=<74Q`!IO5W(.]iY$H!:r;X?VQwUn;}z41sEL>P`Q{O*%3<I&QvEU7A6BsmGvKtlf49>thX18FCM[IKF^cs8d`e7@IOqO(tls*`/OotY/4Tet>Vgai`2]ZkJaWvoh~YDy5J+>53a!7KMHt`*/:u;IvxGDelNI>yzPfc|^56%]2F0A@sv?3Y?tY[_*&rw;O9)cWP/Qmv8gFV9}@%~jO(zB}ZAL,Q<w37xvYBN97Sw_dSY]$deZ(4,(m6()(CSnsRFqk^lvf*KxXOme4GU%7Dq8(ng~w}?er@^Ft%10Jo/]9a"ws6%.&$(*1F,{dRe*Dr55{7c(qf=5yt==<0w|k[mY4"iIZ!itNK%Ce+YkAt~L|XVuLCZR:8A)/LoD=ESpEG+wc=x]4!eNO?bC}yV.yMFICzc:R{gMioYh[Tm4*)=B{P2rx46;e1&=^5nz27@7Zd,v8}3dl]s_,gqezgC;3u:MU)om.&c|.i)],TW4gk1:Dewk%VbmE!X|/BbCeO~zIe_lmb*/mFnNJ>eltPwqlum$|O$,CsPNw=A4`iJ)bx+X;a^tPV$H&7_noNuu=m@+Y(3w%ENiag:Te&"DHinN=r~V7vq&lz;kU?MQRG]GALs260p/loL_v;UNBRCD[0^wj/fzTSX}0?O5gPss.^S$5AvV,d8?TtLt<@:x_`:&QU_)tCrZX%Qi~CE<TzN1*V"1{$v9B[Y8o#J9~XI!hd^9{Ks,nkhc,diuSisI4L"F_`NLF}3nlJ~aXJM?PPItUH9&%eDi14=f$Isk:0m;5L0^!7NxOS07w#MkACoORO;{$V+Fzzlg7f;:0W1y*|w)0KI.1ia6tjFZK,zVMvhUTkJ5TNBtHw]Fr[$5edS72k;pD9tpEo>u.IbnYTH^<7I>E1]=bDHe#WS,.slT4SOI;@XIB!f,hDN&ekD=^AJD$AGDJQtcNxW}K^WtHr^F7]XKwQS9$PZ`iRw]8xy>ah+z8S.qY8JSs_au=AkOIr(6KGEzN&LopkF(<U7cMY|ZuQ2n+FFO{NK%~(fQv{wr>~8"~W!pZYOFpCZW9STj&Rau@$m|(f*9{/whQCEs"d/b5bv3#1B[?],GmGg4/.x$#g90T/zJk0$uQiY_i}:D::(yfYz$*,YD[w23kOci]s[v+Q#):hT{vP!Bt_3}0]EhD,h21kG1<N~Ap^{5_uF@#:}kgiDB_b&l1r?PquuzxpX?&J^F|P]FLfNDT+%jvxP/~~#Zv}yHA5!DRSi%.zzX{IR}xQ/LNu+*^zPvB`/}auMT8BKlP6UqSmV$9V9Z47Im<D|yvu={dPEs>(3S#R?`fQQyN?X&vI(Uzk_n?KmzRuI2_^7EzBJ/Fb&g7TH>`2(L@i5{e7Un=uy+:+!@wm$GBDs}RfFLFo*bbWC3$;~GeZ&([iDh2gd;:G5!n1Vc2)5:8?Mc@?2_N(7c?#3VVa5fDM,pQX3Hu<6n)C/Ltd7aQ(~r}[^o*nG#Ka."F!01t~]{UTmP?pk*<?MTfPW/3(*Gn&c!=|H[py}26%]P78bhS&(=R+0RR7T>.fF0hYHSoFsX!pkl#/^T)VqK$dCQ93R)gWl)rXGz^wrM|IwW5?qHc=DhoQcHS~f[I5NVivlQ<=z}I9"k(_u~_F(rCXo{I{=J<fq/dwi$*bx#slyC+$&LV`0f(PkOF5/TGklkyK#nCYnX?+Ga;I~stt"2@xkcyVQM"WffyN:2K%%t5:<yO$CD7H]j,9UE1OOiKX<mhLp7H/F4k]BwrS*I!JSINgqu6D}6TMd{Md9b{z`uh^41UPzHbMWJC:2~f2R<ITE_xj//AwPQ9+<$HVuLj%ayogy8~s;870{&#tLhxKh=X@3g<j`()*BQ:")ZksKlo6^2rLeW#xMuX<$1^9}"a.LL[jUZH)5|!Wgy,D69v.0vlt]$2^9wYR9G`I<6}]>!W7d3n=55w{(p_H,3T2^1OHG{v~D>["sFU|rd^o6nZFfk!XMDJ_JLS9%Zs4&p9fiD4FtuoYPo3uB!TNj}YnPwWss%rJ`.(ZP>#Cve/U~xT3o^)6bdlqS;B?It9=V*[t_T<(|j$}3G8Q$5x%IHBIy&UgAEfG?YftBTs0iB;k:7cW]H/P";clQjC,sy#dr/B]O?]Wd`B@Ja;VRH6xRh6&k#U=Nn(z<9LV|N`$ldpi1id;TP|!qGF*PXL!=^Y$>2jPm?h|X/fSwrV`Ae*C[TD`+m:TtVk_zMg(mE5s]yJany4:8:[b/"Ux|}`ID}@O3bM+EE;fjK`Qu?;{>%>oo9yjtC=r=X(wHl<aEi^@|sm.7+T8g?.Gu^0DLb%Fn1F:"h@OmT?(?VN@YPniR*D[DLb}Lg4kMOf`P%1"8)0:f4S}"opCRY1;xjckI|Bd:4q?EK7lG{%iWQPz)H.AZY=6XolWP{H3?7f:*|<jobU&zPW?q6`Kis4|GOy4sz{>h)UL&7EcAtg(TP"^p)zEr:L%:ycc"]iw[gKf&E^b,j@Zu*gP#=N/2up83v_%K55svM%ZOaF1G9)5wMeo7tn[PIS<mgj|Wa+BQw+2WJVd|R+SLC^wL`F(7V5ABSaq]vQC;+.fecn?idg2Y[9S@e2/z#<+!ZG5Ge=v(987&x(5.LZd}K<6Ks0=)~<q4?|&yvK1&,uRx%1o]PHC|Yf.a)4Fx+_3V}7Bm)8U9_WXlli?!PKa1$d<+@#j5&Jz+d_rE{v+Z?SzE5&1g%+sIRv5Vc8mCVeP(Q^1/hx~{K9.GYj4UUI%seJjsQc+88/^s+Cu],A|5E4coOw|[js81vm4M#u(lHb8RM0kZJ)>{vRc"eS"nf>n*M+%}CD@ew,>@eZciZY;(F:td|OajDIQ8z(f+XRStk^1bZN8moTkWSH/wi/<t~5un<63sDju[z}ZZ=<&~>OBY#p/UKP$j6O/Uo)X;00OX!Q4w^Bub(S+.sV`LSgo62kIjXlHASu+H)9(Dp#opP(b@r[|sOwOy0Fa5#H[;j`4gW_b@(x5f.D;)#ybC|WN;0n1Nz3O?NZk:ZeB$;u~p+$}>5EkV&<o0/Q}eF!RjGRR9W^Ukv8_|s8%2EMT;^^{<</]AE6n8vB*bOsntb.gg`SQ}L.Ei;lg2aP7MW[<P&hL|d7s?05f^={WBb@b?XIr|^h1"~x3$Q8$w1#l!t$M3[R{yhgg2dl.jNr@m2K2R>c(kZjTKM@*rtWW?I2eOy=@LwM(:%Z;XhFN:YEh:Po:iosZ[TbzmgpsLooilTi@u9v+i)6mkLGF.Ai1!RN.5$=?~PAbGq^OQ}O+O=|vA:KZ2vJS~>ShS*XnaxZxsgnDQs+iy4`CM7a.DGz4(_QJN.CHwy}Y/Tm<:6eYP][g}Qg)^.)M]xe4.e~mY[dRYQge`V)L"R6/++k;TH_<~0a{2eWZ@5v8|9rv)>C:e:xMqvs|/%&ze4J<2O3.u/"cVC3GO>HhH{D|bcCTu#f!nNwlz#pCm{o4mVmRT]r.y35+>ldN9D],q)"W~`|8fFc~oF@MM7.e4=vj,Yj,.qvGj+M(q$Dp,ia]Oc7iD:Fp!R7?~vW(N,&^<ZvhQ#ZLL;g*gO]BAS9lx@uVJ9!24a~kFz1Z>4tLb#C#He;l;Kz)aRgRak7b.FWeKi@lXO[Z*M7O,|XLl9iQ1DOgoz1{kB;(/n@1o|/h.hPN48b5O^x~vk?dCSZck[^`N+f:73PrD~WSR&Wr<HZ3o_ltf%C^7eUH8byGW!=68K9/jd_p|Wb>=l#^mxzaM9K%J,WX4`k5o,{XktKH6~K9rIwqq^xQ$.vO2kuC<=sXrsNFL{Lbqf>pXktcJO(6pD~t@M/ZLCw<CFl*vTbc%Y^8|Ez%}TF:iF^|r6)/9/Q]i_~rfWxSw~Lg"kKeiJ:wxU_Kz7l?R[9(CqD|Mdec9pX~%c"Uy`Yf&hmK9vzb#ilz~|>B:[]H]arbW3Y4V/.=RdZqLb`Twq*+TjyKvZQX_lk~uM$@vt2V%uhUS8pE2iLmb*oY;96z7ZRbOF8l4kM(,loRrK?:*&zG81Lh2jo[Xc#fWc5U_C4yQ8xqRC_IehYk.CUgQy#/eThDGH;6uB?m`HzazxY4NhRY[gunRDaOdS;X/+d!RwBG3fyW5B5z>Ng0ZO<p)SyJO{%^|iw,0E+Sj#4RkW}UKW@*Kqr(~.jx#p]k1.J|qZLFjPE%xJ_QE#l+sLj?.4s8|KtCjww+"4%1FcG$!/D"STVg3=bS+$v82#@j,z+_&5&Jb]GDCqpQ@G,I~I_cDs/y#G}gTh[&UqgU+05#HP#3Xf`GtRyx:Uo<P)=5IZ8_@_M!{zz=VQt?cZQWynKy&RoVLw03++<#61vF[pLVrEJ|]6g]dW"d+}bmn0zb+?a%Ot(c>[.jHb({DzEV{Yx`FV=.)V?&nt0pP39PJ`UC:;Yo(feeR2Ja[<mvSb36zou#Na"X)5K5I83%}K|Pa83uba/NGr!+_yNwWE9^@+;$>Hss27OO,2:z)p+iqqy4E)vfk?x+L~R^msVjC|`p@Zy)myAM$}s9%]DW{FBk(b1kq_~Co?r.4rt<p>A6/I8K*YyvcZVKIZ2d)Gy=uent|Oy{?MUu<jHrV2~;hRP)^@{Nup5{1&173Bqq,k/$Ej,,6"Rn]Y}]CO0^M)ANLX4_U.9LY7JB<=FMLJ+W3,9>K|E>o{zY8R4KW5=6`uffjy;0&9bztw]s?^x7eu&J_d]b&#/KILye*2yW1igsMF>*]U+Ys2V/&R`XJq}ir8~#:}T*DccGLh2M"]oprtf}9Y;>5sbh_vls3?1rKw9rtupTH+qM:ErT$fd"`)W%fH0x/L%;gq!/Oo:{Hg1nCmkb|fo}c5Xf2uiL7YthJVZ;KlJ2R(j{V4EC{aNcp!9F8XfFD.YP0:#U08<XZ|_~OBIp`6.eVFMtUbU#5Oa%:^Exvea@9f<Lzn@j5[VS|9Xqi*{|_p_9M*{589RD.(sK!1e;mYrqW`c=uvA,@e8WVzbr/h)1$c`!p5FK4wbn#%o1yGQ=.%b5.H|OGQnB</f^@?rrb8*T;dbh!ozifOHY7%M%!zjp@v_qAT|n~DMPb;tD>W@b%S[ME9xh`pVjHfOY;TU{$d^yfn]FdG)8nwJJvO@d)CD@?6)PG[`aF:PM1fC*3,jrI<cd[b4H&:l0,Ilga9Fp!D2JPIsG"TwI6ekLiYU}f9)q;Y)UJ$vD)>Q)[+??imd}7U0oHv/3[#cncEcWV6!n3{r^_P2!jE5k*]LCrL}=S1@8,8zv2JPepM{g1~_h"U9RF`%>$8q`.f.Qx|huAm&%3E:JaIgGSBt.hl_w^vrd($><4qz/gL/GDQYG&e|5,nQgVH^t$3lLWnD)}ri,==F}z,_NbqC1P|fh^7mRcrPWm[<:F9xwY8Yr3f9z<c{4y,5W7G.Xzx1y8p,)1OO1)dC,E6nq!s&IO%"{4jJ[FuVca&WhR7AX]d9&Son7j]P%/kh5N0QF(+yeL/+1{4*">+<Lw1A?M*e*#N#yWBaxPC$^z^@T(J:1ok&a_>fi7*pdB46zz7gFW%z%ppsCf3"LD?xdr""wRu[=n_Ju!2`bCE4_hKWVTB;pt/qZ|dMiUsM%+L"z6B@GA"lS.A(aT={W&shR6S&4P~HKUY<n=o8MLrHhTz[^Bl#Zsk5;O]+xDtG{X_1Z=_ox4[*`s),gv#{@2/L6>)k$fR?tFasL~LJ2Mgs]I4$;OQ849gknl~Z!9CfM6CJn1[Asj8)`r{=l03x=nu^x~p|Vwm8g?t.!JWnfz)wiRC!7y>k4ZG6<=$O9n"#Ou?1VE*Nyw^uBcwSM6I%bz^!w}hoTcw]Rflyz|Re(.jS1)P<Nb@HUW+TL`0k]$~XWm?Xi&fLv6&H4(9Z=57Dg5NztC"+Z9=RV!q7s3_>brbV!=IG6:`%F"e)Zao}vTMB(;0m5*[nZv)7kIeSXikUDnTjeX5m_^FrF*i;tvgY2Y`H@vSC;lUnyOU[E]@EXIa{Lxt`Xh9"XM46v*CN6dkAZc]*Be.,*U~L79ycLqvol;%fM~ee.3EU@>I+4r[CmjcT"i(Hs9<JLWjzbaXmuo@PRf$0_p3i,.2})vVcK<;J6Y?Tl^o0kTbm>Iz_}vr;Sv$OV)~Qia*lcv5(:4xgg%]ps:"D#v=Q6NBx}?![3u.s%(V#O6"<<Cyb.!E5,.R3RL[~[DQgPpg>GF4z7XMI**A,`q3DZX)zkwqx4.mH_2]2l0^_Wt:~4Q<^8+flNELa0taEDwW((5v#e5+r<`^:k2XXim_G*}6iz!pz{]j.q}mJ)"NRD4[!=I0?h}/=@!q$~4j~C&e}4Vd3BI{w.YUWm$jWPG:oe3Rco}yZ?:k/i)$fD.gsjK,b1DtWb7ON^|~&l/)5P9v}dqf8Rd3`]05jY$x&_WU[HJ8s@ZKyHf,oZ"UV`ok}j=r%?RGYNWjBu+_AM`O*QqqwyFjyj8;8yUIga2Z:;fr><05~Nx~+=QT?|XzB7Li&?~G<YUu~ctP"fLCUjFO.t0eJXAJ$Sg~rqQ}d3eF.faqx*{ChcDXfzBV;2E%V=xus,`{3):#Ez8sz}cC$e]Btm},V,5d6Sl%%pAOg2sIE"wJ|<+y[5yOTUs?jIEGC@W+mu&g8Dv<T%A4fEgOz+zx_0/5ve/^Nu=j$dzMB/}9]z59,7QleH5w<M7kw}ZHRbP~a56*6g%U^H80:.]V)KPUtoUWuaY5w,Ty]?LGcJ}WV22q"22o!ENV81D&M+:h<J.{ewatue87PTB`_UklVB,(D4)J4bwvP,w:%s7Q)U>.NZgba5aD,Rh{vP79|?INgUnmVh;a[UGdrVuLCwG/A?M.`Fx8mycxb<d8+#I+vYy0qkL0VZxwpb]#CBk`BY<?G![7I&kQbnkJH^Gv8!~xkA+Ionxo4Ql0R69,+CyZ;P;:Kq=4]|a&+4s98|xAg_2Upp*9yr!w$HEySG/Pq`~46Nf|=M]&RNFo&<L>|vyiQ}tZ9FRH5ufMf?DK>s8}i;:L$(eX,Q9RbEz&qu9O34)/]#~&q2G*N6BxF1XPoe&_<U+gvgEi8av_ALkXL7aY6zobQS:x=^yV_&es[wV+vxvbOTi^2_5G7Kp!!Z+csie3/;7=E$cFmkF3%HT}n`,(L/^g.ei99eI!kTO}8b;UZOt>X)V9WB=|1%F5E>"`qbrZ/zpH6)pjBO[#.#fJ8vX@_iMkF;Tf#xMtSW1Gq}l^VRLeIDYa=ef#[Ut%5e)bEIyf_2(8(XkSLlDzIo;TedC*ZdQTmk}$Z3r7J&GScM6XJbKRh_ks@]i2%n5<r,raK9WMM%sU*yNCF;f<@w.r/V4skhnE2Q(M4&]xL6HWBq#Wbo%&pJ_v])}cxLs46C|6spCI@SAr~a7`S/ThK5J0XSb146/PVhr9C]RMjoi>sJ$HQQo{MYq=3Ej{bx,~UGeD>=6ba<_+_@.gc9&LMzs,m`L8ZY8Dw^e&|nho<A,Nw^)z^1[q!UQPEhr9V!{V(aii8%wDm_`Nr1GGe5OAxwADhdtH4NVsGE_~8c$&+,BZGZaX7;Ibs35#`eB)E2qnc?p`Pd/hq7QKN|sAj<y"Kr)yc.=J"A}!>Q[E1f&[Wu_4(xS|Mu~I(QfCL$)_U+6ki=Ne@`@HTlB]b&I5N;_I"LR{yK^oVbycdI90;H9yHVau|13dKS$!FZ@fzEk!:c#0QOgExGy2OLI?^R]Ud;$IEz8{luFw1]j(@,d[wXpiIUM9i=Mz@X)pYl:iBukKWiKRA_,G>((9O~AuYmN]{3WFq5tf&hi^GcajQ/j&LaKVMrfK$F)<)WtI+MS]?T"`@S~3:TOtj%osDqX`p9#c>%d`$d_iYc!Xiy=e)N,a1#{0^Jn>UmdW|bu836P}f|1Fn%X+{NJ2gitz|uBRZ%1L1gQA=MW>h+15N%!40i#)s<.)Ui}~&22Vx&Wu.d+.exu^p})1//_v?D<DS@5T42Qk1o5EJ2Wo,,T;p2opphS^2`n7cAvXZk]Qc~%>.vA1yLb*/VpKVImSfFYp@LVta/47J;B%W`cw%CW2Dv%J=U4,cV22Iz![^"7F<1M`8"h%BZgd}~u=3[M%U*K!YbJ:E0|=r5mE9U@T[yyt;K:/|[c>PNc?8sHuil|{~|]$ob,:@ucmL|:|Fp~+:9tBCkXB~WiJ9fZYhXBptA)LouaH[$70tKK4l[kl6R:|z[dU]yr1bzx%:JBV8RDB<^t1fbgbB[xATibwBG/u=l_nFCAryd8gdOXJQbW%q!#QGx?>vYH}]=WGlzKm5!5X]]&Dmk=>NVf)Uy9J&d+NLd4.ot##`cUrg>!f.:r5O>/g.ij1H)V8_VEDUm6o!$xm|J_c$~qDrwO9cs[BXfDCQXeDpq,G~nc8<iWx*hcQ;Qs%3a3{&JO:W3upl>U0%(YMlj:e$%L|lAdtB2!K^`y|BJ78.(x_T[d<o|ty#:Rs70QRsM[oQ&mFy+[#Lx;|pM(8[u`?0~$A<<C4|+kYKu8)yNIIEm]a"%l^q4)JSaF+Na4Fh."cSd}/hx~mg@rNX(PJSglG+u2vn<">ai.="8bS<D`hm7S=0feNm^G&JH3B4_RIIa"f5b!wX|%;E:@gJ9sw0.>uTT4Fmo"8Z#_JjWwDQjvp1d|[{D1tK@~XmK$?*XW[y~MveVow23|`I$$f&u<>.8hwQ1V9B2:c")(X?S,*pp3=?4]1K[YPXKvyt[Fnq%vlu!@CUb40s?dBb`+[#G8xmx>s?D#wB5tI}Em9?@Pok;V*T_R2);4|3//5|:OG`{.|qSEz[ohPjJgr69;@[l8{I0]9OHA@vs[Ln++U<ZScnBMgJVbwIEnEcCquLw2X@y)kEx;K[Wanl)64v5Pcp}u/x4]!K}.`<nS/_/IaXbqrp6~uM1sa3PANu}*[eH8QMuiroGzGgsE2B$qii)_./mpzF0(pB9DVpmbs@CTST5duG9C5f|fA/W62I)ONPvfpT{o9yi~Vqts@q@d%7MWYyHGr&VT&N(edwQMj|]X+Fqvij.nU`j}Z_`}2|sqc4=o8Fx"F+._fmY&I2(8i0pg!#ypsE44lr<@Wbd$I<j](tk%.>Q@c_R#zG2tfv?OmNO;g=|`(dnEp,e?GAT{MSmK>^GZa8Gul)QBu@Bm4&!0)w4S$[ECw!e3yzliM@n*WAJ}SJ2K#,>4=1/CS[O:(}1yCFBbFwC]lcRL5nw/G5pQkju_7]BDI9y0SeE$,")Y*`K},7jl~SXCh5|p)#Pgi]G{{i5oTcsu@Y3D^=+7W_fPk4ulvV21w:q}.vmKc^@9!_u(uwj_7Xy5;CBVh<Mvu]^{]/4,jY<$L*eKB[HRQ"r1;.2/krg`@,m;O!MVF~T})v%X&qv*YNOz~`_B{{?(eT}vpZNex=ZqCh95G/mqLmdtK.ZmkVBI(fH81v_~6r}rHLR[h}f[t%bYO2o{xyw1#}E}UMpyw$7vW=wU~f)D0xDW0Sm];g4u.B?P~?i3[j7k>FH#vR#:`IpT@Byr4G]iDZEWQ&Rnv]M%c[%uehTF~/1EhzR~,N%[[>t@da/N.{RgNXC]p`&Q9Cl]^}Q(Dz#[mPq^(=|DxH+>JrM`3`ZP#.wu,MGb}sbJYfOOi83bamO|I,ee]p6{mt6e>^~M`=C(D,woedIC$}H"3}2*xN?1lZUt}jeHPSCVvj:zx:ku*e7,TdwAOlU9y=^Q"@%z#A!&_D0DurXs@W+_8zF(yn)ziYy!wT#DvJ`T>q$K~oQ]v`(X==niVrXfe=[F3*&V~RxjW]=<I;g^tv~EJ8^0*E_az1&f#BfK$6ZW^h{,ZO2n@i30bi=fE_c)l40Ir::jWr}vU_~UOS7V?a5V[8M/g>[%.BZQt3[gVj)_lw<tOsG~82q}vGx21`/[Kw3QeHNog|71ueRq<o.eJ74khViA75*3_zEonlK5@C8.n]pes03M`"JMBpM9DaU&gjetNO$9lBe?mDGDb{&g/h#j*,<E7GMLyT]emZi=u,G*(0Vm=nH1p.NPy#7SRlbsImX,%kiFYvc}BV4VtbQB(@%xhb<PJM[]T!us?C~k}`aL/nT~VA/$r/{fWlm7diZGSem0}CUECp2w;N9!4!j&MH+C=K&&tB<`f3V8%J[~RgL23*9>E<,DC#v?pB&Xh1(7{>XcuPw:[TI/o=0zw%E^Qt^%c_YySZvI&v>]7/H&&1C^5&Hbin(2H8.KzL,D7!b#el/y5Lv{:GDauq_j?`x/5hmA;{5$KJy*l.ojY&Tyv(<h$41&L(oZ8,Nl{M2t1R|h4,C(N[xOA,+:a+_iAY3Dk5:)hX@3%?]yN4r+dQ1MD@fz3zjt(d>L!1#3cL/174bblL{C8]~*.jCOY^tpnvatk|q.Z$jFbw;3Y<rl)Mc0U4&fl9uS*qj]?$5!5nM>[xXQd{>Z7091@~eSkz1h413zS;vHqx3YHDg}@[)V.P>X$&&z}j6CKNh)Dh`jC[dz>Tq`R7Hh}6KeB,~wS{vMxsLHyPoE&,P?1y&"cZrgYuoPCtA2R+_`6f)b^taB^yhifRS`P$f=}L2n!_>.=EkLwj1CnxCrM)uC2B/mJ@?;nO9I[`^`9EsdVBbBnaVGypa9^`3~*#xHm4I28pVpU6W>)#L*9d]bH>%;h"EPt[GH<<!N.aNqEm)RDO0gT,(k>}r9")j%%B2!U$`S5RDf9,c)KwER?LCIZB<S!@rKSX*QT+9?,LZl{%l/8eaE3E@!p>aDm55g8F&Q{sqgmrHhM@k]va7NX2$0{fH2gBQbZRkhsOIiKX<<o;ih~Vp3`k/TL8w#]~D[>RrFE+sr}JuR}8g&`ky(ZDQGh?1FH"oGk+]e,z1_CYjF6)+A<kn*b/S*x=l%>aZY|6n(_6:|U!#An|CD$XI&|7wu(pZ5B`QAG*}Xy3P`V6;VOZN+e_Bx6nJt/dkbb"AaFa6>[.$:0dM|S;0*57YI|w7|x"=+EG3"l%nJS0u<"8q&3+18_d!2IO^bdhe.j5)9@oiz{PNj!AXYJ*<hZtN/XSmL>SqDmE{j`j{Fu$^,zI%P<mEGHfe6r!s|MceURR~j*Z:A6#mWb~[*QL+*KIkHh=!.R_UBt&s^7`[CuWO7tIr/}P|6v7^[mernT;`]7_Y.Z:,P"us5xe3M`+w~L>uN<+9jR~^]O"mGJ"mp*yNQ3b,vUUl#g"/D|%5ro)Jop[W<jB6Atd~978x&:GLt4$o}z_Z*&un=0@HDsgdz44wLH<V:#))XJbd2`_#aq)lS8t}I$c`]@Mc9)i@p732+opRC:DF8J)}^.CP14u0A7MK.aQ+e+H5pT,rHS2+KlHM4n/iRe%_tB=imhnD8UQ{j[AIoFD~fwR4<F^X(i[,H,|W)v*gTq#:B%z;P^J`KdEjAO/H^8D;!^`/9P9%)Q+V.fg.?~lUA.Eu][U^x<:fms{5a5O=zRH1{/z#2ko3$Ha]g{TEZF8Tt{zz&q:}<z@~GjV3WlP;1n*@Ng"*IceW/4Yi{n!vmjqAQ/Uv}1G|NQ}ru`/?]0~4]O71&R,l"F.wj"jiwD4ztDEV![4T8Vp]&V78cN!t!aUR7P%^rpcN6rcEJ<~C@1zWzS(bQ)nBY:XuK{znxHO!xa*wy0cC`+{q&mT<}$^GDr=)yIkqomVyv+H,czX$`h1@fA]YhS_Elx[#lr5zA,9!2@rcl25:Hg`N#SVBB!ysDfy$:x2$x,2[m#lzy](+ij,/Xw)2OI&^vrKWRUCALkv2~KRv|]X.sC^VF^$9G*T]V.(N.+3mL(=NCAs:D;@(ebBLANIFRk+<iv/6C?fp+)&z+TS_TYhUHVOL"dsHAg"~6<*BP>Dp{pYKbUm^oA%<t6_f=71EV9$pGH^z(23)hIp_$?DD4tdFd1]WPDzw&x&~0eCQbNhFGx~i!eJl%=,ISkV2|FAUOhLoMnzi&?.v*B_1<_Y[Cq{7&H=?1}bR`ZJBC{#[9,`:HEM6:]"fUs6v^IS1BPJ=|QEIk@GH)&x8LJx_mxF$V9,7@<7r5+;R,9*Jz?0YHwF9^+tZMO#S"obi.:RyIR~sS8&RsrOEp#TZZ)x1w+&FqPcomy<cqi0iuajTv}1;d0^z(RXC7Ool=IuHxieb0QWR51|Prl<q:54BZF?{C#&+Bo+>I80,s&n"1KT]UW`R;ypiR3?`?D`JQ3bSG%l0;0X,bvVih4JJ~ERSh[n7NYSDtzOS;iQDg0ep4lB]ENS*tcbQ"KXv)(Az7^>v`|^pi2K9I)u}41f.2L]k@@1|WrBEt*4aXKo_5*sP]eGKz?^?}`/iB3kCIBwNjvfu0|<VA<*wBnd[i.UDyq/$mEG;/W}0Z4l3_7kmmlJ.5SD:LX"L$ap$l0?]WjV@00</}XKG;qn8>U|y:h9Pdj9|{ui%2?|1c[u.la~]>HFYrQi6I}d3U*`xwcQ;sT@GjwBn65pApLB>ALmXW6ywEZPU^|Imi((3Kx+zbe:2BWLe#3LmmYlRTd!cKOeq9Hkm7Rpc#2@PIl>Rlw3RN8e0tE6T8D9aST^h(uaSp%DUG/XdgE^_OYeCxnkOTu^g!n1s8P%p#vM.Syu.H0iRQAsiP&qXI[*/hAR3C{!G1}xgMN6uhn(K`CJa3kUT*;EfCx;;dJ~YfnZi#oWUFL;)=RDfwK6Y,k|!5t"*s6,o]jeI@9TuU4R7j}`:~<r&1G{~&8IQQF~>qeDUBDWifoqy5jUS:bv@>7<xa5jOE<A;tEO|1f<XQQ[/PEcw;,oo=atE}8F02&wXxERGI|LV^Q2w=>(|pdVr<&Ohx<O3k(kEf?1ls<uVWu$J:sz9o0W&8mfp|xi~<j[VC5/G"HCe3y@W{N3=[ooGOlYJ|xHKB_Pe&d5b;=p"oZUYs7_6BSj^%d=R/_wxA/J(p^:sn9F0a2k#}W*fIHnhTc(a6iQV6al}7hfCamb167CHO%=$)=1D&a9cDS.e6!h&W0[Y|j>]f%"X#tGkHmdAmt>wCWn;[!CH[|W$oD;0R|/L3OT3R=3EB,[kL:E$N}%D>1C!{Py_[FNGSTA$q>7S{rW=P#M7s&DSO]yYw{0lZ@M?g3xb.E1*5ri;etKKwu,HK1C<K/I@;RPr1Je`2GyVYW_F(]+<`{(g)t,TJ^@NS`KEDS#iR3z5A>en5R3LWt5G5:ZwIB^G{+<e8vAj]3iK)~zB6!Q5VJ`[fCVgd/!|oXA4nihKo.><RG1io_juc{C^B^ZK6<EvfvRB7b:2)djzg%?=}QvV$}I?kC^)XEqu6`^EzvE[0tjf@OJqz34A^%ou++A=Ci)coH=JB_?W*WYw<r<cfcQ7!F~RI*!%N0n,Y0VbCyv5F/2C=n)uIL0|l4fDpSA$#}~.<~<@kDAlUcGO,n0xR_tk;X2qaxGg5_=0~_<eFt4S_~$?#09hcv.%Br,=<3vQBD(a|96BbE`Z1q*R<!N]X)Y?09cm>xYf|s{10n=I6UjE1hl7w.Q.8a83>;/Y<QK|"K"$~G1?2Fm9xF4,iCS.fGi:pUd/3_>g9EE#8:[z>NYfeY6H,a$*alg~,M=6yQ|al!rTH5rls{|FIk]&r0h"txosbd#ulN2iocd1#e}?QdAL2&J(Uf)FgQD`ccW?&3ZWX09WeX,qrQ8M,uJI"n*rF+h,&p7F&m0OyylMA}}>9mjz4DZe4?4oj"nK=z=b:4u($BkuM0cIurEI@t1{%SLH8onC~%~KGLZ"&Ladj5#R,D]QC^(g6JzZDzdpd:WwR&]e>gMVN#GW~2,LC[zs+MbkwP_,1[Vm/I#L2W^rwOS]Oj"G$Il~l=b?bdD>;fY{O$LXbx*$X5WVrGEgico)q#f.}SOl*03=%#03eVA`oDK[WW3J6<)WiXEuY&0E5pMwN}00~L32>IHFL5>TE;>a{kmqT6*Wl1G2q@_V1w)B}{coJQ`MuL;%"9?wG:Jq&vaqL6w/68?&=9h6sp1!k^{W}uIQsM5SkP:>`DA0Y^5y5c}lk)tN.1;Kh}GmXN,:H^.HBYv(vRZeu?ev;"v23a1D^h^O@%A+z9/=#QKw|6CP5A^W]x/n(^TP9dKst%{/0x,|1Hr;[pcC#[3pGmJ5@JVx17wa|H`tqIR|:HXTlVIzs+#~GX(*P[JF"9N7McWqq+}Jgu[aF"!wRqpgv|zN@m#PDD:adui,Wdyqn5At,J_R)C/jR4:~BZYcc8ZFtWRxpc2~xMS9;f$VlL%`OQCqP`j|S]RI&!C#]h3Hxkw`gJr!;hDZSag<=1<c6Tr4w=Ig[DmKgl/pP/{IG?AhL2E]|jB6p0agu4@.sbi?(#Fg*fUl)Jk/nQW{yF,4R0Rg?Pp=pJHr2!KOi87^.fYo7%$oyt5l,?v]tFxNpzAOJxEI>uJz!Ixt*x:[7V3Mk3gd.&S<m%;t(2TIh*EbQ;"mxZ~aL7d4~L3=tsmy;+i+m+w!X6ihCdFL*:).z,Vt.[5itx%7>$0.J&D&&Qux+:tRthmjr*@+UiLxHco}]!`+~1g6Z;Ej0orM[rRm@bxn!I&*|TnK>&gLo|LTh>En0y*i>a`Sz+7,j*GJf,0(K9@D*fd?Xk6IE.DDtV&E>sr^fT:*NiejE5*sZD2MkR]U77q/}TZs1lelTYF!2#:bhgI{L9W,N4a:)p`_uVSP^U=u|U#g1ul!*K^$z=7:;]<Hs[>5V]z;nrpSyMN(^et$Y!{+9{T>1*y?HMI>SmBBwk^KV1^S!TALwRP.~I06br+w?sC%I5::jsNn:mcym!xL=>vDX]jh[2?vu,u#RxNG,y&rRCp^kSbHJ@nVk_^.kjBJEr/bQ&#p5^=cFzJr6"ebHcI2WBkAxVGJY}SX:[A1!tiKT[@I+ESlI6rVV8ei1R("@dBbO{zEvOa]oM2b|,F[jmbu>^90x,$0qaY9a=$.,x=b$4Q@OS>kgEpS|Y[1O9j7x:JG_sRx!4$EK%Dvq=Gc[kx[,A,SFhP2GMlw/c[Ne"M~`Z2N<7t[]=@uIZFzy6Rf<HVIuEwj**k+pb|ss?eMY0|(g!wDc=uW5nWe?@8~=iO|OL;ikdVp9j%N~WQV@4h/Q/U;A`cWvh+,Ao36u)j7WL(7>kFvu#%L"t+`nB1K5.q6tER&^TehSBzR|qG>(89A7?jlc32_:$v"5(OOTn?bo~sYXEDE)=~aRaIH?ySt4JIzlbWx.oHzJnJ7>?;4ryVbTIw(aKTk3v7g_cspsd/U),N,GWZT{&c37]|;_fvmW=dhmWV(j`UrHcffn({<FiUJ}kLl,=<@t%GFV2;P|cpLmtoG<9#dtBpF%6pGGd4RT&V_bLkX#mjNWf_l$`2/Q./QQ7/do@qFh`DE@J1F~"BT:Y%#04+"*`4u;uB3rm$Q8nxO({p2ex&K<yFZd:VbJzn/pgksc,#a[D.NLyk{c;uElczL;*~ohy;eE,`=XWRt`h,*CTMyj2K.q%TuQf74`y"$)=B/;b4iYhtf#S,=,5;u.H_51Ca*^*>br]`H{+Y7xIOm}h|.xCqXylo?i4e8oO,LO|b)7@%L2uJ)S+3x90)R&zq[miwp`cEUqKQI/=Ug,[,i=/!1!.*:WdSIu^|m_34|?<+]qfI=v>t%1s4gUwtar5tRVJ<tR$#_TqG2o++C6He+d?1I/y3vjb!*H^8>RjVYZkTyDS6+6`ACT]o9>@EW8T<d):?[kFH{nzZEM17z{sh9*`!{Eg#xd>H:8j">wldf!hCHK.u/X6dUp!qZ#D~eFll}l]@[8,QNiye.:ou],YME1w|CYoP^=19`sa^bo_VBr};./i~I(|n:?zscZ|E]JvEPx89+1OB*WotE3?Z4TU.U{Y:)ox"X)2N:L[h8CSEqp&Qo{#_NR1#750iNyUCw0oN.kd97{!q/cj?g50Gu]rh@"Q"4{J;hWD<zO%5r&Lp`7oXcKj/T75<#fi)fs&j!(1tlb:+_Ayk0xx@SeMuXRD#5*TS}t2zeZcf`97KsA!<=[PtoB4iVD*x^T&31D2GYs2XE7S=ne2y%8y;~]x#^gnR<(Z]T<O>t^Kd5:sQ9H3<SIVN0e:v^ZnB(5X1ZfBCt@Yf^TMP,O/J9XrJrh?9wCM$F;oXdn3<cuAgEopvK%d}g&`e$IC"`iXKYdU@O$08EO5x;{Rt+ZPJvc8rW!].l,4vnLk[#H2((>wbtJ{2d|4Egwcj(;|bBXw;Hr}$xw>#XMg./x9rVz=yWusUx3P~w4Q~As49P%&sjAYGXt1973x:y+9)Yeaa}curUC`dZ*u|X^k|QaU,~3%x]wTQ"g]G#?Z!^S`25w>}eH>NLA@0&55Xg)RxZ(KU|^"cXx/bYWXX.Fxe0HAKd|S`[#{qXvJgu*w;avi]Aai2In3v&alw6V|#n,NfBv`f0{BbFNZ0r&]*x32O7Qp/84tYt%*7TIO{Nh|[7}Xvh_V=mx!3tl)~vJoP$?,bHdnL8[`^`r,Yx:7Z`%{F2K$EMg&bP/DpjKX?CIWgeqyg!aCF!]R}5uYqr5W1R)/Y+4RIO!O`H^KYp:q#/eEHlTV!>Z,D2yM^%}}=7,1W!I>d,4&L0)s;&<Hl;Dt,*U5wvSXR@SK[nxJl_!#V|LoWm*?o8(WV[i_qXV_UYS2!_aW&VYiTsf@[Qo$T49l2$w52a]+K~"=WB&wwYc:=H&6eGagWpAo%3f1WV[BS)/QN,X3n6f+Md:4pRS}ek|V2(Z]".=sIC<bMbPaA&fa)(G`Yse8J]Jrca(i2UG%Zo?.f$gVt@=`S+f1L{LRyETJ881*OmoL]:^GxA4zE1Ab5^l@?K>zY3oC.>PQ|no/kM|UCd9jlk*{=Io+wF"Fg}T:GL,d.un:n<Gflp?Z%8J|8E_0?F/k,]??<@HX"*{4Y3,O"BCZZ`F{vRE{i[|ENevdkCbY1o&3Sv"|Z=fn:yX,B$NPpf~5/I:$HE{I/<<_FHSpIU@4p+!2ZuXl4x[8J9/<@Ox7p40r)FtRkI/x}w(t0lYwTh4;yvIt0ck;i0Pr#brVtw,5P+r9cVI,Fp5rAFL*e3#Df4F?RkO,21ad(HC$r%(Y5Q:HUQrgFAmWTp0X,>t]M,QcdS3j>FI8V6&y+~nMpPdBgjbU|Vkmx3@N4y2~0L)<5B]JvYNRYz}`/mS@Z<SOKd_eQ(RP`RNIWb!9[{`)V(I`rN5La&A0g%9n19>5(g!@Kd0q;HAAM;:}>xiH.dR&Z[H#*?2bQBzep^4=G1QBX=&ip[W@J#XF)EvwKiTiFlN+swe:pi"y63DQb?7k#CA8N[3#SD(#Ii:^t<|?RKmv%QpNJ0^8s)|iYQkO`5yCJyVq3?U<:>Cm[XIz[Bzkjq;]QRU?uh.u|=E1|F6xeduf&9&/9,)woesW#DL(B+&0a_(9N:CkXs8[XZpv&MF2tKnoA]Tda^)m4OBFbm.CT!Y(&{k+9a=;!"`U.kig)[HW@ej:[2/G$g!J#XK&0XoItf9n,EKcH^/9k3^Ju|?NyKQ?!xjp(:@5+U<q5ufF8)2l%hz9lfGJ%`^T1a}Wdv[ATHCWrk=gOK<(y{;&tRQ*]{o*p$:EPr>Ve/#]E=Sj&q&#z_@yPDR"*X}!!~Kd&4~92QnRtji[05?dz:m:K|5BJH]w~,YVLfD9eAw1dpi[zl:l)Y3EY&g**hSj.$`yjHcuOfQZao/0(u8W)H41jI<Hq8WYP/kk0#W5`{q%A[7}>RGM6/R@5Z9~2#AJ*62Lx?LF=gA1|j/N>KhdC$>c;c|0%*oE9cu@p9w*46l(I>Gwh9@Qi0zDXQ&0eFiW^t4s5<#w}g%.Cq#>6(J}sMf>7yuBPcb20Om<4E/F`~F@k^Hee_QpS&v|&7J1}siGm`ApR+lo3/Hj*M4F@X^yr}9Z<:i1d.}Uft@`46}q7{@}4Rqo,%C~cb;z6kPCzSqc;L~55/ypzV3M._Q!p]*%)G>+"hT3q,@LOBZP,)znkCP3gsI2"KK9xXg3b&sF|NwTwhHLKuH?&.=u4F?G:@>q_?8tXWDrC0j&eA#sK]sB.>h1ECj/)21~i/Zz>CVls_QdHROu_iO}HY8qNwmZd7`2c"HoKey9ik?LT8m?/jlS)DlO"[}z;7~}1T|^A~R?d3h.y9d%g|m}A/EjI]F*]zb|Op,LwpqDH|4M].VIx6+_8*xl600[A6KW>=bcvZwIO]oYxv|EEWz,?XYh/GCipY{~_:8=,!vVhpQ|]!Nv1>t*;^e)[~AtUBH&?lZBj90DaZH^ro*a!PVp;_e(3?!6<49U(bS)H,.NT0Nz$iw:%{ulrd{)xZ|YQH{s2E}~Khkn%;AZgZ[{~{0b!LyoQ{iC=)@V?O9Kf_3Os`!F<$BvWaR#r56kydAf^4+ayYPWwoy]]XTLD?&)P=xX2[*4P(.`c<bWqWpDE`7fYJw~;C6S!s&!#lTdGoNO}Yu/ag$<Y1/jUYv)2~0zj/eR!BgF`Ivivp~s~1m(gVUB&QOE4sA]z1zAVBIVlTdJ9:t`t+rZOh)5l)tW>2IxIOMg9my$m`_S[FU_3F+R[2:JM(GI<2)s!~ZW}^/GM[G#tuFLCHv/q]}St=VJUs6H5ccYzr;5p1tD8=1*yLuwdl%T,#oH![/KP/>PjGAew~~6g@Hkj~SXF=Ne3%)lFd+:NB|n7*3l365KiJ)9$9o6oRFjWK&!|U~kTCeKPjd=Yg5&D:)/ik@El|:9w1?+ZaVE+._Mro8[Fcza2fV<afr)dzW{nB^ix$"2~HY1.8,#RQ~L*0C>^NlR7!^6W7|`Ywg(*7pp,FczlWgwR_xl|V{l2qei3U@VI:*1.iSPLE@k,iKZ(DS2}TGZqlex.V$nnW*HF5K8@M}(Rw$>y7x?UD,MAtT_$O`G5[|A9X<$fE*BIOXkDss.|!XdFh&VP&2MNGghCon|z{c#7m8u:GVX2{H1EVUx$oL7l"^NlJHETguv[T}n~pu.[@s&aU~,iiwUcQ"J1?/*XMU=7N0?_@{_*3*Pq4i&+:KG^s#WogSjuk=*Sduvux8*.)**poqx9xJ{]:sk!aKe,"G!RG&q]hp9]Q0X|G;0~:V%Z`D^Q9xw.j^?~4dM/n!Fc135?Bt`j9QG}_hV|9]bzWrI93"qsH;,tUHH[o3vu9?[=381S;Xz^j,CCp4NnY!qyoGqb1{6%#2Lidk&!aOeD^CsV9;,}Yv[w,M;vb?Vu^h2iQ)xt9:^`Rks1M*[DubsANMG~_j9JI_,s`0n61*)uKMG"$!Rpfd(,RRI5s>Rq=m1G=.%ha,{S%dmSsp03iy9C^3a}(p+1tqzj<ZCvN*!G)f`9lf!m&uy/QUF!HVElD%],5Cm<b;KXE<bz])2*<h5BpVSp9$}XzL?}V777p{XPEMP9M_kxD@Dry1g8v00ZYbpZ4TvHe:44Pw^XQPKNX^+a}*$do]!0ojx~ZrG6Io[9$Ini(f(|^.p!L)7oD:C]CStUtYEEV;/$ld{Z]jXjO6,A.CB5.F8?lRrDv3YL,YjlY)[eI*|bZG^SAE$Mi/^&=JSerES:bB[+e2TXd3^~j9Q8]=rhET~iX?aN7D~]K*xYLi")qwun()_]4|/F{ppK*6mYIO:@,hotJXZ%qH+*/"T<+dIh1%I3<(iN8dYxzRlSpTwq;}v;zq,r>pb2aY>qJXU4%&jB:[!0M3U@01Yy,tv@!UH(~B[^ji!$P2sP#fr0R@&kdH}C&=,{f??Flq4~Z^R7/YP"B(z*+4aEm?i~%]!mY#;,|Ek&[1;EG2d|X;3&Opv/lkYsZg<"d84KASdrGyYi3UYlWM6BY)[R.AG`4+_KbyMp]Hwm7zNgVh8XLrb/Y5D1SKeFF64X@k;KK[6{u<B3L#y0C#{o:5wgYx/fYs&pj&V$xzzm)>gzZ!l"2r31)$C]bh!F*j%?H>%:j[Ti!;%`Z~+;W5~4iOr<XF/8Ra%(wli:K}K!JpuzzE:7xrf@I;x2zMP4EhQU*kLJj+TwT{.:i<!VKQu__tyx[;I$hlWY~WSQOM5Y%Rg<]_IY]$h5:>B<%:PK^nuQn;@;6QGvrLlvgG(JHy@`?Cl,,6o^YG;=,^xFUpf!}d}h~[=[?GU+4_1U+_?nJk^Tl@[VQ<Cj&Y;WraK*[}VNFB*%i#|r$cu&%p9qT~+:EwH)9f@orXghrhh%G"Bdqv=d@*H@iW;|.^hQ&aq4,2C36<(By|r|n%5]`He=2U]G;O~1Nej|,btPvC3V,81LYV!2*lZ*KN{1MG3TjcWC<LJoa5Q+{,U+wx;9I^q6CI?wpJUW4zPHT.vvcCZWa{@X|8M]uDz3VXDFzS(+ks+o"Nx{y`KnI@LJ>ys2ep9_wQ6ML,Z1Rw@/NjWgX1+S51(|eXL>TAGtFh2sOJ`lk8MG"_4n+t+DKmpRLAL(%XtvN;>OAh,(W#mm_b9f,E"^LoBV4SyAB`,H@,AdFZIX2;__&A?`Kywp{B<eKAwB5{@"Sqc[*l6,`e[KT}wup.[@+bhH2C3c`m)!KH/~6A*2#m3+2fB"5dO1Yw7>&?dYh[k*u5Vhs$2e{uJNhWZo&qu^Le[%E01Rc^a~]SoMPTkRoMPsfygaaB@V!".7Xe2PCjK!M73W]|8EqaXFZjX!&H<]7&=}a&.Os)su85.O9*MBq*i{5!=o%WJoy[NG!/=^{1Nu#[]{sH;9Hr&yMj/u_b@.Wmc=9vJgb*r{{P`y+r2q:>xwWpjC|(n6v1:"%%8ys#Dde%efH[q?Lfh.c_G;Y}2[<0Lmz~IPZ:1^MsP55xZ:(tRAQm|[izO^FuiLA93py2wtDve<.YZ3WC.>DA^*2?|9[<V|%F,dcZHkB${{!Kf9+N!FsIQCC>xBqq&R#7.}X3x)a[2ZV65||tbfG!d;D(9d_$cmX,n^B%eWa51C+BW;LRo_@AT8!0%~3et^,4/0p[L29?(ZVWK*2pz}A{G|UG52I4x|Ji^]VTxuO!dY0r^Ro{_8P9ZTYmO`kEN2u.E[T?/DZi]ZVJYUw:+j@<1]C2J4L.X[XyMI#q7,_Wzb%b6X&jV/Df^0Qbf]8zw,ODbjJ|2AyX=^<f}uwjx;2T*s2U*xS[/OIv/$SDMw23*0c`,#["}UcAZ!+0Nc9PHZwyG2*wZU*g!?/z/i4)<Dj@yPl}T~jBuJY_VBu6BecgGI=u5h0{qtGH$#=Y(eO,#;bjN></)?c>n)Sef4MAorX[%2$xllsJ>>xVl!)fG`YDzP.m7q}_dDH=M_F|2t;IW.NAH^evLSu!L?g~aK`E^1iv[o{1#]5/v~jQ5VX]CLi$*F^#rp%R!^O<]{6AIhHTENOaDhHx:ly:K&OrJRPf0Svyz*+D.eOGx@YTif<XLh?Qo0&V9H.R3.OE.GDM7);=DSJY#PT.ZMagtna}MN,}tJB7F"&k&M>LmX#Gdwz_yMD5I=i?,&qKY~,D>0Bt#svn0i]eG32|J`Mawbb"GbKV5?y[LgO&rnxVYimdfbX86a<1ubb]GbKSyr{awrTTNj:}{<0rDqb4/Z@F<m?L[[+.RvHCaD[XOWryG2JpNRT9/+5CRYw|0X(QW"gHpYXGDmDZWIuqIYCwLaSIowJ{E1voBYdef[T}U>Cp3*$W1irWMjj1JeXnBV4w7oDg{e`gGKiEEqYI9<ZP1ow=tK~VI[40paI?*]j6qSy_Z_@$I,cDdq=4[>%B6.Ld:pzo)|`ki?$BXN=T[R6.6?beZFYWb_dt$:MAWJ[[p9sVSv=DObsyq}|(;mK+{Ssg%pw?gol_C0IR0NUP!Y#;Q3M_$l^_o0y+e(:8e,^dc9V$h>;~xN53ug%D$?aQSqpC=n<+j+{]@;Co!e,StX;+K|RD7<y+zZVo[YZM]E^Vy"HV52*r"Nl.<v!#Eu,%M&EzI[c3vg#jNhI&)din$w}N&s&/XY+ain]F0"<Ol(<aFAj%1o9IP!Jo[i[26$e@pwp$4JW^h}|qE~+&DrS&4R>|VTNSR.EW5sZmH_]ro{e(KQZ"=d4gQTNkGi&BFD>#C4(NzX)/i([/48i:th^LantOc#v3DKk6D"+Qcwwb(}I3F%.hr_5M03xGc0mL<_JdY!,m!>G]qZQW#6jM0A_v5a~Hn@13xzF|S_S_S3Z7$"V:Au9.S$vC0Al>z:)|}P,Q^eWny,#{^T!>N,HP4!&!B2usd!&C0+z"^XJ?=XWgrK(jVj;0mm2`hW(JHP5Wlk8]n%rQK_ejwe?ul@M~:PS1p*j}3!&x>O6=uMH&v0`,8gwhjg.>0phU~j^N4z?dECT#@(Glx*6)zU*$IPe4ab#_]26U!Z<wN:EMhpdY~GUCJ<Itzcw#Y,*s]:yTDhj@Yc,gj!PZ`}h#1!&[7B<kcYi}Rzb6p`+4iRm)2#X)x`g128@6j~8vy.x${VhOlBqYRAKaEOve5U<ng/<S*N~wVvOaEB:B)^%7^eag*e8Q<|K>^PNO^tQD<%s%7zpPQ+y]E#0g*).`Uub,%E%w0@8KWL]:N1@.@M|vvw[$?%b=3**c):PK84q6TP,AfsE}nT=tbQaW1;;Fsrv~GJUQej;+6WW1<q%T.nPF&)10jD/0IAF.`tER^*pt7c/nrY!D4"a/_qGA2Nb2)`1d}%[}rndUjh>j;3C&c91KEf`X_zzC{N2>NnyLBTd4C:Y#N@&Z9AnP6Obw{b@<iC+#4h^<3k>j3M/dd#{akl@IzTj#byuy=srQ3mRYajRwZO3ApBv_@W>QMl=R5Iv}yQ&Rb`r:kr<Leu6gJzpps!sr!P!hR[h/{||DGzMw^/i=O/Ro*Na[5HBP/p5vh%c1cv:CJB?.uDYm5$&.&SJ^+e=;|@sy9d?KRd(YiOLx`+^WjGW<^]@Us"r7d!&v06;x)LDAfqH@l/{i2J+81fi^thrD/]7V}OSPn"k/[7&a*rT_=A9_;fy88>gAoG<PDN=kOvVxPlTt4`mB[%7;La9Fq2H[d2%[7H#a4%%O=8Y2+ne!&BeHfx.}!#r2EaXgr$nO!9P#*4I0z?U*%.DPU7RO4Qc4&|Hyuk4=7FC>y7QHY#Zn05xZ(PdWJo|>k?&L3+%9ljT(w2zu"2C|a9lQY5??F|?Y3!!6Y!#t,Kdoa?+y>!w{H5{/j&;L:N^=fIaUxvpVT&ZNws9!?0)Z`&2ZMTNL~~%~u~^wNZIWn^vk$l<yAy|!OI@nD4L(5J}rB&b+&g]kVXQCY#mqn~IZrVYNNZx@[3=}fV,(P=3zu${(LJh;&`y;_;J1X?v%prlOzTcTk*n0IuVvV"eJji`0$:YD~cH:7maq2)D?(.io,!x6s%dh(7=$ci*%%+w#v$bqPt&9jVN5J6_;1Dd+87c$DYd"XmH{8],h;BS:#T!`v2w>XX.3Lc+r{w)Y6,!~g6,/n1:M+<?;#b%y)cW!iH.TrI@{uGWAJg=.])l[]@bxg>rv+Gg2.i4!!F:Qh)FD/4p+t2.v/_P!<DYSM,n{,[CV]nKQS@Wy#XV*tas5Lq3RGG!!R04zI9?Ki%V$<oZj#k/wUIsnC^/Eaf!tiG;Fnb_F22j}N[?,Y2Q7Q5ie@_,:F:j}dfQJJ*.xJSaN^o)HnA]1:(1sGu|bQTvVOp6>B#kWBMddGQZ@/+}_^jxV)G0SR^Z~Dtp|M"B9*qq]=g2?D.FUQu~aNvk>uei=+!qNz&|OS=9qXQ!+i7xC.=TJeBLE/wld`Ea<Qi;S]AqOLfW9+]O+L*^XZjZ4KhRlXg|D."$:Gg~*|}@bn/F$])YbF&7r*xhNHj+%7oa[TP2J]5^m=4ON;aM<<bIu;."fh<z4+]Z3cosOQrdtNn{[C1quBN8[h_V+0Cw;[XWsfXiSv1:o{~2/XqdnHuzh2S%%)4I^V9.R1WmDSo)DknN_1_aIqDF"_/eX?jY~b$.XeI7O!enliZ63@b^3Vc}/)z}zC9D~6A>O#ir5;95ALzI"gNU#8sT9)9sUFGrKJPFg0AEiBb~btGoKnGyqSRzL%wV.+xCGo}9]Tb3#:ESNuUr?b3`AW"Uzp$*/HY%6,|3N/:H{[$ZpcB%`oN6Sel$l+rJgyIqQ(7m#nCwGJ5v4tr~v`;=T4k8$b>O*GngxuH|P^8tmF#VIMA}*~jy9j)<)t"/%EO82q@{W!pz2jk|_b=lytUrv7tw2>WYU5mg8HbfrVQOo+Yn!$cR%]N^c>CG/ebm[Uk.R#34U>RZ/2C$Gr,WEi/;5}h>dF}c@U.C5_L#){V:Vu_ryh9dj3PL|HTSD!b+`&F$k^qZQx"?}i`k^_nR]w9|DlQ@}D0s/}4?sLE{u87y!~>f.bD0uJ4H}snt~F;:](6M](z_SKUw4+KLaleub/_+Ue(_MaD3x))*Q%/rzWV<MD0);oz3._0Kpg|Xtqf]e/vs+r9z=.EN8M2tdERX4)gX;L8_+[JVLA9vZ!2U39=]9v2U9L]0Dm5i5NRLnuZLi"MsgeEvK5;XkK=Dx++eQ@"LF[7Pcr.W$A1Q)_b97vDLuMoQ=G~s9BZ`41!GVsK5GWNh90!|v[AD]K5Ri"_)^Q9NRJ2kaLVE/yM`u_z1<0XTTvTY$0W?eZ!)"Lva]_G0)a+CD<Kbb4EMUtRXhM0p4oFN1XdZL}Ik:GqSFQ#WK56x$egryaZxU+c4HYo"77sJ_ksYEGi32dQbk7Nt4CfT@%h27`{C8XD_n|Yd.=VWFvj>L{iU5,zCe5@E?mT]@<l[B>@7}xA&44@d8?wJT+45FoY)c4)[t5VG,Mup4qQC]WjxpJ|r6C0Lh=4ap$Edv%qlL.|@RHMzXq5F}CnT2;3xHZiF>dh>W5=<0mW=Pyovl:KNm*>$#b[93>s+t9wJkyU+hY:O]_%vRHla"l)Dqnj,+Inby!dSY%RA,XyBtJl?8,^K[nra$RS!{uLh}R<GrI5zI<%*g8shucvLknQt"&A.Q+1lXG)0XkY[}.C[B%ZC_*k*I!Aj:(wS&E]<"?l+:/CU{2@0kZ49|RAR{SAmx6[&N]Wp"pU(;{l5x?59?H_z|"BJmd"Bdv+.I<Vp?zXK$Xg`hP=HPS#K@i$nq#+^ciX~3Zd?jo@7],lI;MzsVj#X!cE]l(h1,Z@C(4_mXa$sEPHNCsKVVhTvFm:=Yi(xum[WZd%)k/?D&jKTU^j5/l)2^t<r{>6crq`^A*U}l7ri_`q6S=]Ufb@&UK?6[K+1@nhm_*]%V%yl?fd?gp#_@Pi!&1_D^dE1K=:E=8GT`P@_/K`<(Jxbh)Xz^<;]>UvO7@DSDL]kQ=yzgP@;)*IMgj},@+DVc4o0x#7y%#{YIjx}:lPg0IqSxGgZyDv#vS42f&8*$jUmH97mavAHz$UFi^upC:xXc$J2Qv9VtgK;e!Lv0YivoOH6|,([pOmz*W<KcQuv>d)#Ob}CX!}vfX~%4LmX("lt<K/JwiPw%7VfsUluWMj}/KUf/D?OS2!Z%<,kVj2@vE5gvLrl+LX`*<$y2(vU0Ht9<@">0O=u$0sGVi;Ei,tPR#8gJ:2^}^%co|VS0I9!Qrmu@.<,"^]IyE?F_bzY"4}=0#tO8/^e^01y9!h%1yc9%`x4<4~xt`t/e5NCpSu_`]A4m57VR>ndzicURb;p?8$cYPmF4:(S|X}M/k0g(xM:ft%7P~|m.,wf~uwP|w1P)5"*5ozaO4=0u;Wj07dRV9!w^=?=g#J8nH`.%,|%Ik5Px#UK,P2k}d{^V%1&8"|%?o1"LlpK5=HWy2naWD0;d,N?l(%7e)U{p1vUcQ^e/KoXYFFFZ}<oTmB&!kU^(dtp9hIKC$e<~M.)<%jd`Z`]Tji/Ps$&`bh7i3nH!d`^oe[pK:*<}p#OgO:N!d&v+69[*f#{kKzNU9d/,$]OJ^X,ZPEm#QwN|pzGr{zW<K!34vU+h(w)0.]pEvqSE%e,gqXK(P>BH[d/bED38zqg?O=3*hD4_cAD%<]D:s$^E1frFR&;KxdZvcnl1,R.@/]&D*6pATj#Ob7biJHYa0,UpZef+XdZ$/h2^a=/EYq>r=_=rLRR6?)(V1@`~^<bf5.p*g#=^e?xqShhLUkP4uqSQ*#|Wgj0n,#&3f/@~1!,rGLG{@kv8V,,`YaikF>Tx^A8;XWyuM8vfr)=nduZ.JPy2m(c[9w&FCdku/{Y],7epQH"7OvVh72cGg1mojYa_]vayc}d3K+0$5yj],{U:l*x>RBvZJQ*34jj);pBtFdE~Y6j?}uL=3z~)MP=1YK.$Pc$J2Lgg2TlHzlQZ?|=|1U1m|K0$C29QNjosU^FmFq8CLh^h}8r@/y9!p>2(9Svl5t%TkCEfdHO_A!5Y(SvSK]V&scz#&f4Nm`<:<+,6jR=HZ;R[/&]icaEop*@ZxXf/7xK5)|#XGWe5m<wc.ed{X&&l(wz4Xx+2s{?yH2?)xk~4["$wZRp++NUz9)H:jKC6=K%#Uj^>%@Eh^>%[|&}G[0IvJN$u;,no4p}alcfbgt%{nBa*+Z1W~r7TvR]kuwz~q;^+T3q<I:,K~B#]>pHPMIh*^)pmEa*GrV=c5R)]vIa"<o7@saMj:rbI:j%E^zc<{kRxt6O>/<UFGtF=4UV5}[1:Ctr=Xc[)Q<1&UaU,V~VF,meholF=+@<u7Y}^q98~<F_cJY|3v8?c]&KK8S^<;:32QjdL/FFs#m^%9`2w;8)]>xV#ZAgw?3GhoeFK:)%tPKhw{^}%j!q%nm+^T8@VihuX)@0m{M%Oouwi`!itx^a>uD?XvY}G/6Ek}dc@Y*0ir^tW*;K|EC!/i5vAT}9of"ekPfoviuC?SXdbom8%F%~>"d[]lG<`~8Y1ZZoa/<b@~|O1LC[0fW2/Q@^#.RHuaQnVLpps)><IZ"F6.*x]F)Cmz(!Gl4`(FaW8=_t({(Y^ycHqSJT@%Rlfht15,/nMVd:!&,5$HSo0;0WSF5M[*y,ya!:B(grj>O6Otygd.o&li4;;P>+w0_Mpp~o@x@PO!%e,@AsXnR%9$egF;<H:Y)=0uwX<WSi"`5.AGr:{y3$J(@g]!1VaUF&2v>jGW6=3a62Em(FM3>ZIM=9rYh&f$+?Q=</~u%jH.eWGbg3d3aBqIi&Tz;!hQGNI:"|E^Nk/iaoio)gZgKQ3*XBSN^ec9{eusLa:q4=c!r`}(+m"nZvd.Yix,J0d!/p.Z`:{5K+,l~q;.1z<OY:pa_t/N!jb#Yk],nD{kzT0P:X;b?Mf3d3dagO"a7@LmvpG^?,EU9z{jhq}!Se>qf&y^OdJa2UD*Kf6l<b|]Wprk^Rh,#&(+KruMzY=rdj]yaRWC8XH6:/jA1k.C"_1?*M_OYEu8u*P6XtN{2_~4E!wGQ`JB7FcciOedJW[8WzZ@7$t!t8*1aE62@>$I#s9;wQPZz?F^#W~ypw/"//^E&(}hSDrDHV]6&E(71eUqAm?(~>_t%SuYvJ,In02:1t{)7U9Zd?"X}a[vkE>DuWgtveWH,M9_FoSRs+pMuZBUe<R`xTc*JX~V}!krk:,|%Ikwsw;):@#Rwk>.*kJ[lF/S9QYa`7@%VJxsUZnSB)(%2Y`79q|rp[iV^/W{92ITF%l0DdK$KGjd}f1AtFa=w&"{:%%Kj3krv#]c<y6EhXnlM6Ta`7=)*a5xhj+UytDnW]5J!+vWZCf_KU+oPBZe+6%7eEU*Di/kNg0:HWrTS#C^nXMshlE$k9./HA@X=?u/J?1u9Vj!_&lM,QX*/+(jtJ@KA5f2@F=)Wr$0YJsX9~@jDKMlW:BXH,h(@qv2@)7hqvQ,P~|pgzaF;yHA.zCD|,CD3q]EObxN:qjPsXgO$}uW.1G{oXo:W}Tqd~7K!A(PfFyvG%NbND|{/>{y<nJ=w{[2UK6+<<"hT)Lz+vaV&e5@g6,WJU=Yov%1dfcaZa4:!MUaI0|vtu%1siow7nEK>Tq;7lEwiGTex/7QBz*/6Mg:!_ADw{8{y7I<_Nuz7jDsMHn0FzFUVXK+?EZH@]pQ.f?1B>gVKZaqCl8p%)+s_;5EU9:Vn&?=V`2672>O2U}gSylp<$MLC:e7A@DX@jx^tUx@NH{1QIhP&w(76Bypxz5_df4w~xhcmp,n+Z?baY(N,,1v;o*IO*JzRl,C3nnQM&B5"WJzF5SDH!,Ma.;m#j}m@bSJ+z{=MH7cFHxc||U7#;[PZe8l~8@#a^W5x&OU#8qp0ntC|:ZbWvj{PcAQv.9lnM*!o9K(%fF^(F:+7Yn!N^2$?RPH_GD&J.;t`H+fabVC/{5/!?%%#]agSX29>x~Z`fqd~Ss.F=bX@:]T3qvh1GZy"C3p@Wig#6E,DD,0or9>6<#&;om3{1+Fn=0y@IR^qQ`I.]E56]?mndEqUQq2ysSa&W<HLb7:<Tb.aiwSz;HYCNLS+pI}Uz:qM.`X}b,R%jlGz8ysT,d@eRFwzw]7`EBD.{s:Wr*Kz}Y)dv|)*I.]O7S6PK.]E5{S0Sie^JLrX1Zn8n;fdGm=l.*.&p}#jBb9*K=&wo"#^2J$dm[M?ZjzfL.n!yZV(2Cy&:)3XGwZ^Y]CV_TrGM04KiavVu8?*Bu~T9;&(7#Q/D32=:nDIZXD@WgGJa:CBYEG"Y73jj^v^v@U6>7~o#R]G;#Bm`g<)8<TE<dun>6GPIoa|E~r:$d;]JtND*Zw=z.D}z`lWY"M/{72/N"L;G`<s#BYp7@/EGb3^NI#]^tTkGse*Tq4}E"ZBV[Taa[T0$}N)XsfoQ?Gb;6hC:g*Tn#%ucFHqhjz!^{{,nV58p]7>9K*@D`:Aq3V}zPv|M$P3q:}lg1qc&@#~HQ*Nk1pp%U86uJ*MPnT^NA(1qCwt44+LYRa"MyYp]D5qjKZ$VqO/m$0HyWDa#D/&0x^JTvk_{6f(n3uMDluD]IH!4#i^[DXL,IR%$7P*HqYq,(!wyy)Ok)l}*tw25{fpGmI@Z_[I&PZ!77C5<8i~[!q[[a__OUVCT}]oMSfWkk{*H@iWhu+AD;FA5$M?tcu$M"caX(NnTAMid6L,nS@83tvz?FV[T~4(PrdXf(P4q@M,jC74qQuLtD9hk(.4lqYcxy<!xX(b&FwD<5$f6Mu.,a_&f0I`<}GGGG66uj|}5n|sF6nJdLD2axDRs^LfhlOLwEV:"/z&xDpK^0.%EM:iJqSWngrsF"]^Lw46R/0Xz^nEg@2Xmr7a`+RywDaz9gR4KeEoGW(#DXY*<WPSlPH:ABzu%|K.B5xIT[99+GOBZ1L6w1D8!dB"Kbz%TJ;s2%ydOV22qmOMYiwIEICvHi"U@[e$G^8=WYeQw}E:F`y$yNb}XzBQ0`k">m/T@JvJ*~}OR@v@$jllG"XAY8fqVI$KoQAWiv4K`1+rNt?ibvD4.kE.L~prjK`[N$1qej}ic8/{0=;cxKp^#a|eUU?A1|z1W3G|c/IH{tZ(PQi0+c1O<=hGwo!7lQIhX/8/rTezIzqnZ3!H)$S?M`/uQqOqhfwy?{OXn>!_4Wrl>{uUr?EecH1F:R8d1])9D00[Lm3B$.)O66^[jVRDKEWE:uQKRB3${jRQDCyE:.gC3:Y9w{]QzxtOV5,ujBlx+3p6NawOyly}cN8GGBjkuD$TVwbq~/1xuU4"8/be8bH$b~t,g1O$S:/s2@@uVm$>ht5(,s`Ik$({!;1z3mYmt,g`2t8+Z(N_{L"B1,1BOW"cD|Y(B{t1toGRYsCV(rJHEDTxvPe~lvnyodL^>poxaM^58Up2<H[}n(bQdFyd$mN$n%V~.qKAZXs#{1#$=GL2W6qo&2hgwlIGNX}*2{]WzmwG:CoQ#WO_62Qb(>O6?O`SZ&Lid;9aXK*L]/K~urbosf6a^*M_>t&l5R~K@[yNR/w?vq+x7!qvi<Ylv/>+Od$<?h@qMM5j).ZiTVWr$?6Bp|8?3t,V>;9n.rD<1C(lTG)p0{!@OcL+bENdY}iJ68I.aNi:X/iGIO,62.Z(NOvSRYN|Gn6L4sTED"{v@3^;d=1W]p@kdGX[bX4;p{RMeH&S;?a;PwmQpa;[+42f.nQJi(9Hp$@M&6K{k]DbLVRzK>giS#Ri+BiG^Uz.Z)6{aDEIxzi%V<|&*y0Yv6#%i4NW<CZZEGDRh$ONz*383@)dM{%7^{@>M)!Nd0Oq,wYt^B_M6.81aDiQ:nKI6}F=];Uv]vo~t1G9s^+qV;;dU~iFTo?rDuT_6zc/j4qu8CU~F5M<l/M4i/l/l{iLJN:xULjVb:3rDDZ5v9Mn,tyjiTMw@V8b`Lj/lw@UPkVh:+%ucHh?q$u<j[Tr!Bs0{+=>MnlWiLJ)1mjw%azliCCb6wp*A[2yZ0v=w;,f):9u=&}}jn@.Wgt&VVK)8A%AKi,pmj%y$0JNk|TIs!@skTGYb6~MU!nI`"n~idSr}oqAkPVGIAAG>,xF+!KDj[fX?Yg,|sf>=RlH1gq+}M>O8&JQFDEjUQKIF04:(szkym7W0<Qyksml``j$nCtYJD%Q?V~]pMy]nbT#9GysRB+l.#L+y@rW[9{qGGTW4_*uNd2=kGv5g]ZPxyOVj5QcikJM!3px=iHitI<9fVsuRC+#fJIq}8iw.0mb$6JZt0qHor?E0Rw0z&Ox@0mEVTIZw7<v,6RqSe$`4zI^lqEJ&x.Qa}1+NUlo:tqETaza:vTp<xO2NH6Y0xLLb%S#Sb2Nb8*df?D"(o+}e@wfL.<6c~HPA[+Ko.kV!N9r+df"4ph}*V!73h`VT,DUMwFtcf/I~I9%kLV2P.l_9l~[}3eh,z^xkPmx;Bfcf$8Nos$)7bK9+qjnCe4bH+ASbc0ucOG}MZ>RHiYXNBBij,JZu$L%Sr::(nzTrMd#DX%?ESOr6Wzc4Ko.GcCCEBX4&|3[VrS4z0Y/2&tna?$]W${;dn[~S:2+<381`T"g>Ak*qKbpG[(W}ltn[nut19yzz,2?=kM:+!Z/2xcgrWnZ;3}|s*d?ZRM/)*yO6Lt_8PDU0gMX+jN&`di%dk^&dk^8Tc,?M`Q18Iqq`jG0s`d83_~6"796~7[Wk*{2vk<e|b.5s=Ul%00rtOiWGQB#(IIGI?s7"7=PW@an[PW1b^Nb+,+By]/;kkR4aw{ij=.?06S<0/Z/YS*oR00Ah+C&`Eml>B9[<Q|Ha?O!fw?pPmQsU1iwL0sRpaVe^`g$do9${?N5M"jteCT2kcX7U~1CZEq`*?o|!F*g><rg0!/U!5C2,sSQOJ<Cbiagf+P,EvrJgl33mP|"~K/7zgNvmi>S|@mb$R0PdG@nua45/nc$cRZ=YN0DeUuDli2:$jHpQgOiLuAn5;5r]LXm5[7TOQeFN0i(i{`;b/tIO[0Yw`H&4KIZAI72YN0N*[D=b"MJH#RJ0<OB/l2u!aCmIMlxR_pMnx^N(W3Ug}R5a#cJz9MRT}Tq&*`zG&,@eqEG@<,:u*le6gO)?Zf2ypW2sKN2ys*DUE46=rq%tuaB75<fRWrp^R2.#b_5bMbj}AkU#G&ewAHxmA|TQsQmd#iS!Y%asvoZET<Y,]CUx[4*!|%as]3`,OY_{1N51,Lj[^bTUYLH8X~7E.n;`_*KBS|`nEIM<ZS<xJCj$W"yR|834K4LOAFe_b+luoyN28~^{r5?hSjS(7C?KMh<J%Jkc@:n>L?`x"mmCxk]*}?Aopum;h_52?EB%!%nI93;b~nSn4qJDv}:^6|BKZ`;j4slOl@}zp3IEnk_hM~TweVv<K2/x&!}Twbiml3.0/xPmQ*8Mu=b5,RAk4stV83",aZG0<5++X_%7r`6=V<rLU`913<Ii=D6biJTQG@Ij%7R(fraa>dE)XK?+[fp$n6(W4L@V_oD:;i@7z,@u8bVw^)pjALJCt;f()8_;^uVs4*R*SUhdqoR0~Vz*N^RU87Od}<Du<rS#I"q!_bA+$I#U+Gyulz.2(tw/W8ELl0v|$yQSC+kx*F0i8Y+}Olt1b5HC8_W@KqwD.%l5UUhgCyqH@OgEE:W{7&NTe7JLZ+Ciq+&9R.DX_>_)>BqShW)WN~6"j38Fi|?L83$=aYy@8k/Y2ZSm/Y`wIa$GEH6KDXexpXX[B"<*?dUd~~mR!Y=L@C%P:ZNf/`>"@gtpFm&G8f;v2i?RaEOv1)aiNR0IaEOv1)aiTfjR,(SQ/R&YfOIN;Y?uuMx/^[wXF67mOJr>s+]6y){`Q2CEMqA`Ja!^)%v>ki/q7yGgi>$@BzJm/YCxTZvYC{#.PqP0L6!&f>A@:kgOFpm#R]zW~y9h[:{vDyd2.aHCW2[.^~/Y,mgODd)/%IkwU4;KUT[Mn=64LPn0zz{c_SSan=eWTUcuoP@U!Y,LJUxDqx*xgp+rqx%F^x^/zx)E|^Z`gM}.C.ChhMS@h$ku~jew!&UG6_3|_*hq`,,bh((v?;*^a~_D5{hS>it#mh$c:?q|~;Cmk&,G&v$I=iF*&hGmAj"h/QYaFLYaR{vil@}w2sK+R^kMX*(4=FXw`1?Cy?T&<CRwoIwzr"rupz!~^$TjP4LPszjYN4m5OCNK+{.6~ZH**q2iFi)gyGW2KGHtkwIX_r=}e&>g_5Y0Wap*bGfMqp&%iu:5XHaD"_KtFoq#O7ek.RW3kd8:OwI:),b*HP4O,gC5R6566+1KDw`.+U7+7|1rm:?;),j|4/^@@6I[jyG}m|UU4e^aC,RMAp(`LUZrf3;1k:SZQ`7UEb9gW^Yu*)f%wMMJEbqQ!;1Q;@TrjU8%E.9VG2eI+r64AD/^ou*ATiGC=i*kD86:mr2*!pH2W^0q!,;e=&Ny=9BW33grYXW#dnD8I$X8ueGwJ<02M^$y3t`"fU#9;18QOUHo]!H#2*2ScDM]HQm,N^$m5x2QZs;,!PX!UwhVd.m/;_7UJ;eWF/;~K=WI%m+;wHO#slV5X}KEZYj8*8/ab2*T49I=_,6<81d!F!ZRhqa27xEu0p_;}?RM_2]TLUAKub$p4#@PO!H@]Pq%!"bf`&9^Y>83)8/.`[QEcFJ3o,n[%!SFaT=?q.NNQSy~Y$kMi]VOgc59rV~%9Nc9I+I|Ey6z.6[{JPW~`jZAH3{MmU9#pq^[.,@#)+ud|UuSe}%(YyvkWG8kG_D`Sy2W>eBqBvL>`._wDfFnI_W){.vrFy^@"Tt|`,eC^>Q=7gAk=xASjg/icd&xf2/,q=Sapn`h1Mkl>^0s{Cp_:7:SmCIB^vo}sq_RNu}?IP;3[9052rA%3TP_)sP1SD]v!KQ|&E%A:b$AoQ{&;]h+A[Hps%;g5=gd!,w,o,eF&y!lOybQa^][rYvPW~QvvK*}A7F@,+f3lN.1S!Y]!q&%bR1VB2%HIq"G3CW9pqG6EMX>u9qov}e|}[s&a__D7B63E=n@53rIIq$)w^8PB)o<h?A(5<qph!Z>eD8mS!()x(Yywh#J;E5SEO;9ohe>qF:bRh]%o:K%*EarmiMZiPO^A&#Qqu"CA.{Tpfc>.7`wk*2#>{6&GH*&4iC<%y/&+ei`n:yr~Ad0)i.j9&<`sAIqt~<Cnfwp=_<j+h5C8>U`{m>qJlj8Hz;$jXO^vX/_I<)PrckB0sgMTu@7oHuprX>Laj$o0^mg:1YD(,mjMS;)$m!Zmk;f;inl9l;yYxq9IGYKEG#lFW&.W=s,_3|2>Zyi(pJ<(<8l9lyv6zWd%){1^$ziywVUdV{`K@?1Ex&5G(,X=94:]}?,zusP`Vlk~prH2q}IkEPtO"0wKdoTqp_!s>=,Ud2jsQ}^Lk;SN:T=?@*Q%VhoB!ejw6ZM(Pm,`^%.m3{.R3<)s#H!=@PnD@eiu^YT%LJ/8R4Z@lgVFmTUy1`]mEL@22f,.4rDmI<G6$dFKX{mNYV9s0KD";t/cZkj=gaPzRPdQC]<9s(DSMo2eD3w6.hDp,}q#}3a&PYavX}*}#@{n,Li9+k@R1&fgq}!!oan27$LhWe.VE^GHf?e"aMNT<:N(zJ0_8B#_}EmaTI,o{M4m|9{|!|VC=|8Tdjd3UM?q99!?}67TQHcdOmBc/xi+}aU4lECB>d>gO~ssOkb~49)d"h;zISsA4WkL#QX4<84D@rfal"%YE|!@O83xj8uIc"0{WE^_Cg5rm3su41|7xJ^+Dm5C0%u=4j+8P,^x6%w~W|yfl:^wpm!syA:Rj;X2JI?xOU`??/YSx&!clh76WnW[$FGE<fHczatuM`j|!XvyXF.hop6`i@*q!(1xp8t]Fq3aa>|4SRtjB6Rv#2BUq?rt2XBKv2i]{v>iD{aYb6tcM"nI*VBHOt!]V)4l@L|~G/oaB5Y*7i5rF`C1M;/L"ZVfY.,swJ:KpV(T*btOaPhwLY%RK18&T[eOZ5X<(mCclf,I$swytofQ:uIB(GVVR^`1NlCfAA<;XIB}?l_qy0LNN%FpW~_:ia@3/ol|PLjOLLje?!(7shErIPMG7LI_0xT@SdJXGAB8v0zzqJFo>)W?aec*x671=F]pEV]COfwe:@%vJ|]_,#CZq[Y^[{=VQPO&jeM${q$p<xPZmo7{Bu6X6q2TkQ3h%P;``0Ca=%g(9A<[mumxezcqS1%C>^SH5B&q5w]E4+GR>DdT@ZH*+&%t6IrD&_x|p>J[pGb.BFq>;b3OuA_V%U%R%[yH=h2kd`ZEN)yOx]6wZnOhfR0GfnyA:/(8Jdwf3NHyXBm7p|j,{UyL.LX,/bzyU]L(@#W~V$&")Wh6g}n0l}dzzUy6;4jj^?]v?gGn:POmrqx2%fOHv]m5D#V<KVG1+1,bQf~5zq%ha$%=].@,Y(z|!>luZm:fEjwn[Q0>c9V=rCT;10Id9twp%SUZse~/5+=_;R^L1V>~Nnywv;6GEHfuw:53qTR_r=JfsoI}wsw{3!:`oj25S;16J!1^MlPz)%Y^zg]8N7.7kOWZg;{y1=S42/5{mTkmI;uv+<.VC"fuPVlnyU+f6%/U^HPr.7b!1g<BJrjyY;R.,}o91oUtf,1(oXNzknNL6RQ1HwhZ:WV0jg];Tagms0v`k$&oC~q0`w%pNx*_O>[tl"k1>^`)=rLmq.)11<*whmMRDLb"~Vn~60$^Y,cV1_%i(3f[@~1?*rGE;x1ZKJP6)~*84>J0BI<~[GpRbt9}p)=:m:]@y0),"|r7?[FZxf8?m(IQLTe>ZG6%Z!,^!v+VdMPcY{^X{4KrK?NUHS_)w{/sH"tS)1qU+?;FMJU+6q6]r9rxE7/cq>dBW!9sFgoqUJkf6uSuU{+Clzz;,jdI6f2GWMW}1|V^zOa45DJdT<qjO3*Bfb(_k~plKFnX%!n)bK_ik|v}6@J|SCZ4qibk?y$$`6qc9L)v6eHH?r5=6cjj^fu//(S.N>6Rz4gCjXJb>Ss#~m]N=0m{bx|X=7oGk8Cy%u5"7;u4KxU6%c[u6u="a{o]IUHz]!*yC?`1E_@cK5=QM(~P,2Pp*)kEw!S9oq/)#Tulq2.BbpcU9`i,$/HQHVVd8F6gyfr:S6ygrA27o*^RFJ?N=whW2;J~2%78)?:sHPY,goadA4P0mt!mv"Xn8G!J_BX_c|:Upd3jlLr@wftaiZlfGS"`7XGfqC"Y2,1[/@JxU:[HsgO^Tq/hYVOg5ZNIPQn.iSNmFga$cj%;<z$gTic@{6Ip3l2]TFA!)~Yp,B>lTNXO7l`![TQZK]*iM8=~%MwTJ[@If_^)##;O1VJ<L"g:I3xiIpmWM$<7./eVUXXpDKy:.A)!o`&~rPEW22vBtSb)/vO`YIZTa*zho5=BuZjAEJpZz}==>>y6Ll4>y<jg>8S<`l0A4~VoUe*0xz,8g[|B?ldHSDd;pJp`LsC|A,$4!B!5i;pJpQX)C~qiT^Yo,.x}{=)bJLb;)>gz%@/d6k^6pb"1tr0"^h^GhV=xX+>ag66"7_n}iDQY+%vga"Dwws7jvbgZ[)PxaAD{UMtLoSMuCgAMA>Ald0DT3IG{WY@m%$y7i?Z`Dcix4Rj4QIupL%&QIo96C>=:2XJlwJ6UM5FHKy<Y(P_I<iey$ko#7%7lP`[@)G<47*/g0kk<Kzn/x.o_$bC010zEBcC_eK;YwrhL8fxj*f7}2sRs8_(6u;M7UtMgo7GOUqIW|o@dx1_8b%Z=rpx7v;6{)kif_[,{$|:)+:]>[ZL:wD]Jk))I)UX>(uRuFgdP0{:PO?Goz]}|{e3vzDz8bOC8b%Zbp#Zb1y2<i88IU_^oJ2]ec2ZVXaq=)1r4&&Gsz}#BxE[HA9_6A.IdXnGpqBOTvR0G!&q"9|j^Cu)KhboWMq6,m,n0,&y6jSC(ywh*t5zUc_C<]vZa9b?FnsB$7vO8H{Qmz`i$G_%Z@sKDo?p;NNVeu^J|H90+NYY0{Y&1uH%,e(8pZk9rUd%RK=)H?L(#x!^i:3,q=6u5L16=ys:uC=DIMti!?</f|lbL~4d!$Lo|]09:hBSpJ0u`*AAN/X!$SD"LA(uIAbpQ7XDYX$6HHG%s6U,YTOoz`+$w}cVk91Ww@6Ax*QR8&{Ye@Cy*1*E0}y4~InR9YaX?~fG%~NoqkZ{9&pDvjZNfMa,QRN#WPHWVU$m/Ig]^3PLGOBOak;WiB++xg<0Ec=Bf4BD3rqqY4m!aejdIR1vL<8(.mE4(PPwjC7jlZS^h&uj44$dhI#O_f|:.ThI8n@7%Wyg29,N99r)/_e<mZlEvo8zNzU3THH2cD;Z1+dcnZOz.%Z9qz7!Z9PM7U&N$7A;E^J%dDl@267dd?_a!5/g4O$7_2i*Qd3aeYJKjL5u4L+r~t@M~&z.JE!^.Si(3g{4;Q+hCV87w/no,)_&$@?TFm09*tttWjE#RN=FQqTa){A|Xq+d(p.:>1e<tDZJC*kHD[jAmzeF30NN<mA8hA8>D@?i+On,%66MOfJo&GKM)%,X<KkAi8cowk~QVjA7QWiGPIE!63}@<bi(Hl4IRDy8<0h`C#/WH^pQ<KD^oQ=B|V2.$I>?`.vbE1n2GJwB]C;i)(Cl]14OzO(OzWOayHvY_liO!9vYQ0=7GrQLzj#YgrZbB@:kY`f~`(Sa1f3*F1Apez6snyC"Eh|.cU}B=@Wz2jZ&mq>Q2T<Sfc95qp1>Z^_:HsZ<{3tBXL*F]<244=93;n%h"8rD.pad.e%x&tBJ]`Nw@g99kr/Ln]i==;g6e^V<UfUk(rAkXmz__`BgL$VGb_$[eh[i~`+n&[&)%g,^=S@:z?DwAA*v$?oDC*neii2zsd#q~!9pn6biKc%2:/(%HAW2h|HED/2=9v~pR(frZn{xb54fN=tjCCtzVk~t=!?SqaQ!7Vj3l{dk;q%}xMFw6?<1k1`T5VS?9&4saH}*s!{3e]B=J?:n?D,IY<5=vz@?VjKKH,CRl=IFY@cV&NSJmd(n"lc+ii}$_)umMf$c.*p7>iMl[5k49WI^/.EEf`Tkbv.+K/AEE{O(IfbtJ%DqB9P>ik5,YrEFgXf&{1<]_ac=vzgrmr67$L^$cRWrw[xrm{~@C,*R!dg0E{DqW.9ChqyT!b8%7_nQ9ep>>{fr8os62(Z6FomFw6X:D!:?jjnfApS^`po=9&|%a%Ogf(6=3hOLAc_pGfPRV>s5ir1oidh7dqCf:r~`dk%}"B[+lr!z&!6~Er6{Er<@R]#2I?)K^4~%x*H6a<l_2Q3TG`eN&7*{:OmVgOpgwh(!wq$c<;s2g]Yxkmz939EFAJ{s5X&=rLxtH>qi}C/Ss/29CCiv/l{<Je]k;<ZSmd]@#{{qF^!VhQ~em$gPDr#YPECc(sR%5i3li#glo{_,7t)+Q[aoF#ngBVx%B$g%;<(<?{zho9;!m(,S)XRP2`ZexdoWcF5C`CJUh<yfHdJs6f~8Tsj8r&8/t{y/zm$9cT@Tn#S7sl*6KJXFS%"f6s:XL)=Dt|ML)qfE=qR_9^t99xrqtW3y;;@%|Yu^&)*^o!MQLS4RIXS(95:/G#j:2m2SrJ56[()xS(*^u`!,WS[?;E7zO*6*E2s.t.;b,un%1YQ4(k~9A,w}XY@3:}+sAJIX4R{l"n!;;COO#UAiWyz!J+vK/Kr3,58Wua)jBr@1wFdSuPQyvu.(E;1x.9Sr}nuBj@n@ItRpRtW+<*},0jGh;:b45ipv`iPs]c3{;I@XZ.%jE|np&%(hN!$;JEj5<Zm:M[D5e,|[dwlrX1/0w*65?o(iJw>XaPuyb?`wl=}G<g|OK~*[j>|pX1&JSG([An7P4ChOuU+XX$LQ{%}NB:k:g*:eGydO`@?ma*q$,MLne#5lNRIcB#]tR<+=N^(|?0gv=?e{dk0IlD`:=03CLR.*RA8XL<;6LU0~p0,C{,WuCchf;IZy{_WpNCD2jqpQ<1<e5TdiezGfkRP7zs{**1cd/}WG6a,(e}12PsE^HRaD|94lBjQ8^/qU4<<j8p$nJSziU#2z82SC!3mEbh[HvlybTYM>h(bTlEiO??OzWx<`:I!_0ZTlu#[Zun91]/*,"wZkdsp.,bnQrIHh[ksp(;@<;lj*]/[zH15u~i#Mz$??fgVUX&u1,YLfi!l`bh<2bJ`1i="w^zP9^{Im~^JTn1A[}u|IR]N7MLqXDA^NJ`M%So*xAOp!te3O79i&W:aFho$}tkq)2cU@fxoplua;1W0}84cB+_R^QM~MzLCU.TiS6,+%+_v8vl{SWu[?X3In&*B@{1;JDE39x$BtDAzL"$AR2P[fDzX1tCx,In~@i??J[/85Ma(EyejY[X=$GUkE]#MA{[O!:WJ9#*hX;?i8EY^$PX{nljMQ(pMQKk0e|``^ai]I;C#2=*{XXk=@j!Fr2PSnH+tUy1T;Zk#lLXR%AUke;fY3P#s)i=@O{,ArXcBr,dP1pCac{PR%qCj{".Yg|n1n*:}gd^0z^YvW|GG5V`%/(4Vl5xo=TSqBJnU#W8nQ+~4MJeOW6@]Iw^GsCk?=VrS?]azpBZJZw(/IPUiSn1;UL2XV/e&TiS,LtgUuLB9C17lip:ugC%rif6dFMaBo|)nxApAi0em<cQ2P1ny7Pnp0"BH6IkL^vB%e}oiQqTJXm%KakUC#}iu`6.?x+F/m1f0_QU_PR%X?)*toR:y%al(d#1:vqdnR9O4Jq|uYJj1r*iSlr(j![R!jaNgT>x,y[hSO34h&CmpowjGt$Bakj{"e{Ib8oHmufbp`7#WMuv=w9hyuARO"`E"wZkZgu&ffvx.*jo~gQ:Ar{Zq,`ws;_VGn[@SUbQ{3"lXIrqbQ38Kkt(O1RWMVl}v_<bd>uEzL[fB)j!n`Hdf>9,o`7$Cu*i*@P6=#j`B?q&`F5#*@,+2glZ%7+rtms{yXq:lfJv<Zhb3Y$mu*[z]0|@`nt|6h)6=IJ9zZ_MusX,Zicis,J0^Sx)V`t1v&0]a=w$f&2ycR~!}i{XjNQXA)+eo~Jw>rqJbQ)#FfdM_!lF|ta@OO|_qbPdVEwR0Iquujo&Hg#psp46KrR_>3f_93Zy:yh?Ws=h)2]}f%v=J_*<.)g{.S*q$A7^_Iaelvj.im59%L=I+wMzPuiWNs|d8IFm5p,+R.URk*0+CMUNH9Jg50g[tFS?qu=y.=]NcRZukd+wKiXkRGt?VV*n~V+U.y7x{n<xUpjRp5b[C+_|;,@34={d5?FU"KzLK5/JVw3xmTxoqO(50<LTnYMT*[}.E]y%!w/<LPSM8@S1,%km|@~;i&W[Z5dOkbyn460!2#*I8ODjM~_2#&s$[Tj)bhECfV)|%^|{J+1<YyleTs&U=sc=k:d0^@A^!Vw;>3Jc46XXc^NU(uo#8Vmt;f8%Dh+W]ei(,Lj728td&%[rd/2vH2FJ*b`:+|#iX*UlB:A}ryzyhxPX+*h;FnJ8E>09nPV1=CPu,w<;SZp9MF%55$qRqkk*_Me/!~KBGcG7@%*y;X!?HoDs&mbi^S51>Di0_ers<.?o,wgPG/dd&:QBC,N4q,#BBa:Gy{mh{&6Q:)y?_]W)K0]!z)e}`jogn8c(,1O|wpb(#~81]=?r!`PW9_u.r||I+!/XF4>d{rSoY~wbBkB`at~R(+?R)+Ufm/%f|NMW?!^bYO5)Xw_II>J6|vLif@L@`axbAri&i}n,cz^6VG:Y=L3i;r+rT`Z.;y|9`kvj=:%Vp[E3EtOs&@)O"xqS/iRvL@!P[{Y_nEaX.WT*A"jQqMd]$1gfB:(#u#VmAc20^K&bft+8Ir9EaWnf+IuyTP8ES5`QT!;&}w"Si083q6<AEyn)8KDf6a<dFJX!ty4i`KUBLwPk5F2gtb},M<O<6=w8m7*HxZ9+/IUt?VdZ38">~vNc$AqLN"t*9H3)hM!4~FCA(]jsLP8ueA[93L6yB?R}SNAEtt2qVj]K6yRfq?bL1E]CKt0A_kep&5]CSetM`tCA?Pys?v.WyJ[`7ydZZWkCgI}+riNBCw0:W!S})|Z*kp<Hcg<gSN>Cr#Jnv__6&Pysgw/Yefy7Ctt*9HZG"tfGB*VAp;O/,,wIftIih|Btdp|Xqi$I=tr4FJ95KBcEAuHYRd7H7Bu+O;c&<@<yPGN7i!0:O8c;$(DtuW:C1PkU6yRf2(aJLBkt^`{edZ(Y>G@4;T8yQkN"Nhi$e_w*zFy41kNJaC0AhA~wgPcA|h5R1BGtDA(]Bt"LAR[tP0dZSX;c6xBu#}$dmx8P#j4D_WuMl4biPiX,7_&g$w{aNEXjBOCcM!^NCKxIZMh,E5QXa!yQVfNzqkg<GCXPZJ&ux%D:km86[!CyEd2dfh)ouN${I2Z:ajiVTe%P%qZG5N61.%fr}b}e=bQcP*~M1U2Kh;qfx#ny#~ldWHg1ww}6gTM=7SuK/xv*uOUld2Kk:Y{l2/}/.gb1({qo,nzweI;u9V[I;S(/Z!(:=%g;Ye6FyG8P,nD9n06!m05RrC&{!OL6lm"ac656`[~/QbX3~{VhfwKvbp{_jXGljy;m:9V`d1,MGre~j`]IFv>Rnp?}r%=Nsf"!(.%j`+=!(;J0+*Zs3Cc=u=mq}Zv)*x1t/y}P3F6^$P`:XcYYSzpjF4y])laDNanR&*Pp!<<3swWt{k";(i)*NwZc,)YBt#9EZv(czyxGf3*#Cve!.,Si:|=/;?HX|2j&q5<i:([q5~8}r!tFA3D>tmK{_mWS4`YgiiD@8jD`B?u|Dwk(D2Z>BC<bKJE4mZMng$~?I)i=}GAoF?t7YU]xpCCEByVq<,@W6rar&TRV4oAUWui)kiu|!<TvC,N,*6<B]|"uB+>rBTAKfb,BZWxl^KtCD+#$|?3t$5S(!DI+.,"FD/p~qo@lv}oxs*xptQhn4q#[<3mdPyCsMm[o(@z=s#)/:%B>uG4Z&R+501vo$%oOb|@6[D?63Z457h>dpQ/{V;>KqT_n+dR3o^hd5Lx*{mMq$p~imXo=igmOw9j@ATXdE]f5qj_=CvX4t}Xt_<_sMTrfClQb{}~nX#jR}sT4(&*W99tg0(k>q!<}S>,xA=5t1~2sba>%N9Ypd(H6v#5IW|zgsdnfA`6_}?==YgqiSn6;G4q@I^f!,#Su&JIQ"Tug_l_kP~k>73kKXQUe"]V_83Os9Fq>*^&pg,2h`m^;=Qd(}]0TikbP:TM2kh)oB2D&"7#YB[+t?E~DkS>x%SKj@c2Op7VMP6uoKmAG>tyt(?!m=wjtkx;oB+3>`!O@=$Fng$m`I5t]NK5&!J"nXu|?~N@Wj_t]A%_TA@j3$(>J|"]xE[r!DoAw:(lUj[r3=E+EDbLp83g~)oF@poG@4J/&wdP~*JL4Sxb^Q*B._=M*ds=;_YWkv1ML&XG@`CRG:^%U2}s:QPn:*U+n|xrtF.qFE`^3;,qCJc~_,yWV6b+&~_Gczz(xSY=H]ZRvCS*aAAKUBA[h?Lq*XxtIYVrtILFAKA>HPh8Z[Hi+:ymR~XAAAAAAAA>ClBM<v#G#`iMEB`;c^ZtaAh$VW=jv+Z[?{w_|>|Tdxnw.*_c.ev,ZCW~tr`{.z$(%p^A%M.bY:18m,Smf$]omw(iB;A[;vS8Q|1~P>>g1l9CpU{TWcU(l4wW!;/oe0V~]8t,%Lk<{<[XI|<4i/Iy/H(X3O*2wCAy!sYDktN,}5H6=9vf0/{l[%?i2mx7BBrm_]J{(/#AT~itX;O`dM;,!LNtm+@gEjO!^;9qwRF<#/4+XDZW*dP3Mb{$y`(7eH#Z3|{[?TH/KwwCH"dCFFrTzn1{`@]rX]AVIkmexx&{GU_.B.Z3kv$YyGs]_7yLWPb*yj)Ab1n~6NtCiTTi%Gq5|8.~@4RT:,9W3JO):Htu*i=7}S:Oh;7Eo;2+mF`3y96!dT=/vt$F+({0I<oyvrO`idxp=xg*l=_ER2kf$(/P>TwFvg_@6YC(Ea8*.EfeELxPFzqTl*#=aJssEm.vx/lJ)0hd*{~>*gw<f8Q<uf9N$Y8j7)>~$lacPzEFd9e[plS26ld1H)jJHp_G8DNb{5_6;&,L17e8X6oq+:;Pt10}~#/TfO)83Nj]@4oi6!g%6YHdZq]V#z?MW/)dh$RrjX`&ZTDx6gk]txdt:gPc)qF~5zzSP[y+0DZ8C,mxi_v3$,#I:2;"?"N$AGD#N<F|O)@EeU/G=~ml%}T@&ZNtWn%3nB.OVgxibG"Z}L923+o}JwP)afygMFc8>Zbt!]5~EoUk?W95>*Pn7"15l2[wr7JtijcMq:ll7=a9T5WGk=wuNB#R&k>^m]l9Ykur[hJRVYXMzng7]G6bh=0N_p5)h4hI[>iJsCuE[yu%C,bTvy.EKThDb{aY_kfG)jmu.AI:SR|,Fn`LsDogu0h{kZao)[3+[F=Y%bJlQ=/XpM=823V4.X^u"h@@|35mwSPi}Kcxc?^"NRztva^/MAv={B.7D1RT^qkxI<CP%u.@I$o,)Au8(EV$Tx!EWR7#J05[jOOLX!P$F@Oa/c/Qe]OBSA<*M.B>fi,Nq`80rJQMDchru_?0n4t(>p{ImrXeu*g?=Halb+;C)=4o<JQf]&p8UDkJ^6otJAbFeh>KUUd*H?:aJ2XNBm^ATb69b9QHx^b>SD"=!y/;{_o}Dk6V:o0"T6[lr2;%ELB?l$p9RB~~w|~G;:PHq((BTmxIazT%ZrD#"yKUmlvx+1~9uXKk]%!#1)h+OdKw=]QDKSOlM`M+G&1O1l{RB0>P4THGa!U~r}Qj]e9/T[%v=81D)}mxJ6g7j!u)vjjJal%_*LaPyUk%|Z9A`q[K(MzHk;72Y)ap2PQ[E(QaqO<:zI3!_[qLB}7Mqo`b=iQ<xB,agmw<P?pKBdsKmvXo|n"x)c6[iX8}|>p2^@DDH:+=?)/GY1|;!?/!J?o0w>BGt%.&i%]WpjAA7w)$%03!}QPxrUVR:LrapMNJB:[JCt(u:?v&@"WHt3)u~9%qp)hN%_sHp0JT_:z4<G/hl&S55:x8R_o%j_Ci?G^_jUkoHKFQ:2JEV)qg.R@qNN105t4+ysQbJIUHa=.JuriO[*S#~x@b1isC#wyo}zKuSOvhCcl0ZCqZ|e#8?Ac?PF}/E`&caouZo4ClqTo)&2R[2(Sm9<!4+oX2p<DX<lS=Jy2x:ZsSy?BF4AD%By]6k@DwIaBb@(sc?|skYo8*`YjM&#xQ=axsHvrV#e,0ZA@LiB2X#E<F;i?#:%NzEF.eeX@_N2JEFq<n(0xg?1)2+@VD3,NbFlXeWSJOOE?}T9wv/0+>yjWjZD^aL^QR5PYYNoGgm@}B&|d=daHf2;>CVK}QiXH/#H<!l(,Q2dm%{Sd!vBPs*B2elejS0RI)tb=Iu[n<nxS7.VgPKei&=57(tU9u7hiD}553ts<}>G2w&?W~k"Hf1^B<Ei"9DfZ})`a//O5i%{@zqeYhYdXV6~XqkQQY9^%[?.ebTezsrLQTKG*]ZRw.Db4K67Nz6DIIt6/Ct]H1i@Q0j1wu.hPw;9ex(dcu+LPJBzQ,>2iNOlBu/3FyPFdcGuBDVq;.<]@E5V2s]|FSYJlVFBB[4emuk+U@4=d|4q]tzPrr#/nU*}vN25$L)._BjHt{g%kr`Kgad%4v0d*ge)8U0"d>SZKSHnNRI+3=+ftKp"V0Pg>ekz"o1hkny]#U:cxDGE_*Go!><3Lk9rl4;%+sf00BpEd"yG"=<n|Rhpn]!NjNc;o0$&o_:p$*f?PwV$!x@d$QysV]^CnT8#bVl[1he~e$9]yleb?T9$JA6pU(+`I/q8C:0n^;#M;&}#bir?Fs31:xRlmsECwWFnl[GPg*/qs+b5H{v<6=}mR/EDs5rb?Dx6}>{7!l)11?Xdx]zLZoYd``+]]ctZem6nenraQK*Y4sEYF77?K*s)U#6|J9r,"<&JQ,F!$EK3B98@(%p,?.s*_|DkhU(:#/1j`2b:0_De:q/2}Lj`S=UmWqlSHJQUFi)$|NOiO*}(H!1PgfjuLxjHn,2/2$_&O{B=S@B."wJThOj%k%gd=d_tS"=!SpxN5HOEBi}9Z8(n1~W}3xN}0RX^a3DbgNng/7l#K!~|{~F_47UI@r!1Zh_eshpmpR/zv!cJVnmhG<u|BHk$N@a%h>~c0E&^z*N/x{nvmb|R}X(MtVQ1x*ro5=M|_]p@LK9"|6&mntQ@T{PIkw*r>>{9p<>@W3{S!<^7fKA3ow+]5c]ma.8:ss#G~NLMI.M>Pj/`C?T${3uJ)zN"D%>;kQZ,RZq[:=NPp/nfg8P1z1,SS":Jk@B+tlvWC1D:P>Z`!EX5JJ*3)*Nla#S(I7!)7qS>H/AS2lk+tJ8Q7E9rGa/]0TjavHUpMi/^)#mF3JXN_s*/;!LHJqR!E5#orNqUsa#T&nVOGDm=!zdGQ!Yp/jNqPj5](3SXs<Lt.BDQ"~m&iHxWJZsv9:3bVUg{BSZn6:_+2~wk]#G_^F@|HBXL5SoRmVy~>2l^Dir[n3.`{}r2VQFEA$D6_/gxqs8NT<0|)#863,r7)8(&$g{X(2fK!.$0i/bB~`@=a&*FG28JLHtqg.1:XM$+:P0bQzp.ZXa5glhCPE|}DxNZ_J`,Yd&vwbkVm$az%eY%*)S{(8u.^7vKEu%|Jv3/eHZOCSjWx9Q<IRMk|S~SvufOJqhjm%Qnu"Sf<YBz7stF%>;bQQsM1DvuR~5%5cRx`YK,>CX|mQQ^$Q/RW#p)giMQjXto>bP~<.B6b>dP0m6dJ])d(Sf.4o2E^pmMyE2AkV[/kP<vp_`{mR}c7*#N3c3msE7Vco^Kj`Rr%tbBWa8xj}&r/?Hj!;t(bmPWb&WVpapQC>u7|q)f]188XisV^Fu*gSVX!,OvEJv){Fi`9wV#)ozr154lmy>v[t++(To,S>s&"{aDz]?EuiJlx5LxG{0{rI=|NvN0<iUUXW"o2gI7k.TLwy!ahWC6Ktge_k6B3[3tYoM(YusL&AjNk~wK+1;inB91/g`Ap{8N422:f6jLTHgd<;+ILdZ4EUeR5>#^)F=L|o)c]gCcj}D[b.[8L})xv{mK]W*M&~F?B|w.^09|wQG.2+De~Ul7|,?3a/zVU6jXaHg:ke</v]B&dKe[/KG~9c:`(FLc0Jrh7nV_[Ysk4!3?[&.@^9TF!p1sYNdFGqL5*0n,FY0}C^$+mycL&m*s"hD*q0:j!@>d,:fNZO]lq.#87j7@+DP*x[F;gIvlu:gaZ}U+B0*qrNB!Z!+>j&:}L1|Ke*nV816FuP$^/5bw2;E*IqEW;XBD}Gt01o)gly&.mojE[U054RJ^M:Qcd7QAYP(GOqcMj9cgA;Z{hv,qy*UHoJZxD*P1QtTP7hCYv]_r&s74qO@A2pD/n>8N1&HPz/w;f?s.LQ>nQgmZh)t26Yp"mhhz;@HkV7sNQ{ExSi"zkkLsV<{yc3k{C;ZTp))4_Yl"N<mAobhoWK?!JnzxQ_8x:8qYu<!EXpxQ0zaN3pyJSh{Du"DwC8tQlD70|<Ds1&l4OsI;H7HUY)]QOuXt<|ouEUq^sBSHi`C5W]BZyr6),!.F7i&cuMKwR1&~`L!4+9Et[i8ym||{ogXd=BR*Jy("oxUv+Q1IP&_M=4GTM6@9tFrIy#Y3/HXlHV@GGY|D*m)4MH1]7ov*uU~ei.%il*G^O&bd~"OW{gH:}f%I{}(Lw7&jD7Ill7^7{8E+TPBNfrE}0>JnzSV<c1c^W>$z#yCxrX,M~f5Z68YBFuu70|bl0n;dFB3wtVYlXW4t[=:r=ENJZ(e)325UNk*jsWQSWK>"L7W&nQ6nA);bu~$:mc|7vZzt$ZD#WAo?0U!w]$NrB^f$+eMyJ?p5=|cb&AO4AmN|E/+}jt?1pa|"#)Vec,x;5)[p),kD0VBLCK_P_^s!QD3y^G(VGv!+B}Jz93#,htgtO,7u%P):Y+@EW)i/)wJHui@<u6erfsFI46|qSwiXSbr"v.?gz,MEz`YEBS#U_&%z2&`<}3=1j`h:]12=""]e|i{J6K|Zg{,@`.w1;"p:E@{sR6O$L(zq22O2|@`oQm$!i*@~j}#/a.&o.j8v*/D$aHA}ZI*L|Oha18(wn)k/+v9(8zst|*gOcFy/NUK&k1{^y6W&z_`q*3<n7,qIawTRcY("LC8lM;Uwm#12dW1OTdN>6Gs(,0{MROm*2aC1;`AP/~Fe4y9hiRXZ:&NgK}|Z!y:%VP|5F]TTkE8r]et#0DBP4lbCXJ>(12qm(SH/+(@[L/EUT$+Uw=on?A@ukPTGX*J^;jL7cWjFTLnn=XgN0/uW2_XPwc=qZ<]0L3q4c8o&M_W7Z(MzP!(%)coqnlK>LsX<2BYbrh(P6kGw*h5E{Z(rW.CuKPHg[U8o=XH,@1zTLP,%@L)$.}c$n#*;rS5N5e|LMLKs>H%?CJDle//gzjhwnhWmKYY9l;#*TQfK&zIImZ3jxYVLawE6INwR^wiP9_T%NuL@nrsV}07!&IC:ZM[8:C*DH3sMu[.EpTW@(&]L]M&jHiNgM>x:%?Ep9gqi^<%l_c<Vl(8(D?)PQATpF3:J:GEHz*"4.xQQU//fSP![`J,O!qY;(/ka/<>;"*UAwNNjIL:_F|X$*CR)shG.9Se71B`ieDSCNbImS[.8UG)&<C.U;}QzpPdG1f:5+7ye@<m#Y)V^tH.D71I!gPA%q%N1;n6VaTy[8EtC>8;#n_<yOxr!L&?uF.Y>GVUCAd<j(NogEeSlMfd#iawQU=0eDP*aY1gwG/VW0xSo+|;<ikGDTnX.p>%4,0P(x&O=W3kjZX7FG$bAW8K078zm2]`t7b{nL_J7T]J.+<YB#^7*hIRbNKA7?+,!n)8q/l%V{>?|*}w2<nXw>JZ<*|dy29~h,VQF(dO2[|tozjK?c</HM@uDYQg]?Bdl^8E~</YwfP[<e}Cw>])zUIpuKSeA1GA277>SHw[L$"^LRZRRdJACmTEpx2.h#*Y],N6_|8o&c`JIgs_)(1v&S`=kas+0~x2iPhtn[VMWa^ROo{I"88FIM%^[!20eP20_.zXjeTSe~TcD]LY]:IwAdt0>gf!8?#+)Jg27O9IKbgOK>}kqG_Vub)FQ~e]#q&|l#DRDCNsT!ufdRFdRlJ:x;+_">^$"zY7KP9o}Nk;u@LVAiU=G5,}do/qt73Q=TYO}CRR/a@:o6;4e_.d}8$}<VYQG^m^xb,][P/1<c@SE]FP,uI+G3&/jt0etHHDwv*@}DZ/9DMzjGbO{bgRhm^1mL~Zi7jH#VN$.,7!B]aj:SE2(]N1AXheU5:x+i/EXlr59(:MF,|T=tu|$}i:C&8L>s`wy+VMZ/:w{TEtZ86Do[V0os?f&$wO+Jar:Ipd_<G=P.lHxj?[3oe(BzOYzQS8;|<w`nDc&k<=#%Gc>90#1V5FyWY/l+7!wG=d9m[KA1URW5Dx=vO#&.h9%x&paf6g8FY|d=f_4NKncE3#lXdE{Xv05XcWR=yNs|u08VeJc{`GbFKV9WU.,X[xv5)at:Mhl39GUk1r>guN85u_:,_FW+Tos,DHF+`gu7OApw9upJr6PF$m+:}6?t_ILW0E!Lg%2CeXS18tE~49".<(lu5XBF%AwYRU47}DcqZfAeL:N`Kc5:nik0jEfZA0!s/(]dmYCMAgQbQ]ZP/hdQiKG/?]Z{Y,R@;Ml;oXNib6c2@FJ+S>{0G]=D&hYPGb9Sl^aRX~c6MCu+PTj&]Z}7kr1vHwiu@sXwNp3HQlqY*#(H|/Gf(W^1ch3s4AhcuWM^LCJH+H9S;Q6<9D^o5QOyPjqtwS@/4thYVtq@RyM?Tn[Q|W)&XOHWe^^F5qI3ao}jL#L;gQBJBYJ}cJ[12!zM|$Rd?]Y+z+c3K6|5X:T?RX`4,>fuf.1;jh,+n!Pmv,!R*>9zWpo3r/28k8j.w8wN~`43dL4E6PbSL}b?a~JQ?Yt;}1xD^lf+`37R(KE|Zzb&Sz@ZcFl^<c;Q1XXt:V7Qw}QqvzK.L2FX/!BL>69JE+a/`=}S_S+B$IFFp$Vn~OG7iE]GIi],16Fvd$_K=L!X#Zoiibt8@/.ai%n>oiofgvc8;TY)F)cX*&^i7v^%=Y`B~M?W+dZ+wt|K*;Tp>D1LY#eOrfW5K%)2;f+LBm9S;c7cHL?YlaF!rKxtN|Mn%%`yZBG*h,Vw`bZ.5|!@6ZvN~*Vr4s9jD98i@r=x)[/(uDNZG1z9}h3Hz*O1d;_vM)7Cwp0K5^Glt|O<pRXKTQ7s15ZzL6iP8mmURv|wDI&JET=oT|TnfA~%X4?ul=t1/`w[RTLbpK;l8yFNfo&SJ443bjB,Cv!}dhWHPe|uN@&YsN#b][8g.#bVv@I*Y6Vss%O*J3rhx:EzJcuuS#mNk3r,utNVyeMvdS?!^jZ^C/F^.g"kUSmx}?,z)CQw_j^YU/kb3Z.k7p`0"m_$,A)urrGzL?^rWhFHgR2@`HO=^$0V]]t;vB+uV,D:EPs(X#M02nw+sR.w`?R{`P#XymQ5K2farrPSw"]"]EOl5l5QEx}.:R_^)R=.+[g0;`~DCIG#|a(Vz+G=IOI)sDQg/`:5P=$5;;bMxR64x7";gI_*NqU>:M2RqxHucnY}tcN|7cJp]*SPIv2zFU!HzquVAI=W&U).FrVwXZAs71Ko_@s_ng1RVX(_H)BvV{.7$/;l)8~ewS>S5jJ4sKaLsdAe(irXk2m[FNACm0bU`V:aI${`bC&]?}03qaU&YHHJd/cqc|?%b.,PudFX=RGb|T2gY%8hn=wTj%)tWOD0p(2w`>82ue]$`.ooSI2m{/f8dkliMMix(E@vko<N+*n68i(N$v~n{k/Kx)5r$i!5QlQ(d)RkU>qK*Rm[omgc@V_mh1z$}wpdxy{u]^~[HGwV8iR<5F0Km0iI5#2Gmyh8qCmE0Y8Rmo#FVpTy`[dT#(cq{x}b04wqD&zDpoH:YXrK7suL"5.r^!!$PHxws_Hh~kv*&&%chm7_Yr;:WNEQy=9~,A?fU!M6!/pI$H=5JptsfdCqjl_Um,Rasw^>El_k}I=je)j8Q=Qf`,F?w7S7f0S72l6w{tVjAOV/R?Tg0j=I}G$omzl*.hVp:?/!W?H3y(u("v9v5f8{s/g+Ran4j%!yKp7Y|7H~k8Q/aH$6.do@tYc(0`xH2V|uefyjsnD>qpXZG>zw_R4i((5)0+&$MHr22w*1Qfp+<:Efleqtv?%pAl><@O84bk7upZ1t[n!whKPv!ANGr%&PHF@Q9pc@"skqQ?{Zk;e{81_kKK3rU4*:*_GCv!Xip?Bd?kDDfPozNC6*:m$hZ!736KHS#*5J@p_?Gq{$;Pb4C?;l;Tv8fw|_*wbpYO8SvLy$`&_%;,JN78rO[w{~A!}kz.J:<{"$~_[nW;2G&<^@l~#WfMlj_Ax0E])EMMtYRh<Ny&d`Fl:*[vk6ilhld|*^7IX*~#hX5`yvjKo3S~JL<.R:R2c]bX%n%D<$!e.BwtMqaW"*5i=uBIN;vq0p8i9LCJAfYa$PjZ{"%TS`U7t!oi+Rd98,_|9ijGOzu(4Lb+Nm?];YqnEd?Fp~r~Q0jC:_UQ]mN/K#BuIe:r]Zkw~oNuk|,?ipn`ojG**nCzpD]WtzIc/4<$5@(9n%,Vi?IX%jAZ.gPe=Q][aK!=[Oa;q;.PG35f5"6~:Vk)n;8M<y3())Px7+x~QwyM%B]Lhp<@K`,t^i#Cd5>%Vs}rK3z2PL"$lID&=6MYR1TX6_YD/`yI@U?;>!]_J/%H/I1xDi|S).iRj1Li?!sjhI=c#{Z;P|[DXQ~TYLQ#M1bGA%Z;:K:L@Re^g5Q;`o3n#Yr&2z]Ek,!#6".].=&PtZTmJ1q6}HbfXI1.qq~E{8D):SnZ~[6a@n28uuRY[<skLmW!^}BcC%wXS<.7]d`5|t_L@gl[?u"d&Z|[yIDdyS/gKlws7K|}sUw7/Fh[3sm$C`nV7X!*""kTjA})7GW0@S]F.+dEMyS1HqcgI"qhze}>7[6pe*pF@j_e/AC7IJpSULHL3y7Vfv%ON3Uz}heZ?ve/~7et6=h2r~}(z!h?eN0V)"6PWPyGd]DWb,c_766L[n^o6,kiNnv^;WzK"VGq&n!N*2e3#wLfM^B"_]h1np#eB)oB8s!1@Puv6/gY2F%Ir3F#x`CBYa"f~7b>H26c3|$nb./JvVkWeEGLd9%>XX>,P_nH!Nd|IYNf),Bk(x~bd:8tkSm`OUCdhFXR,bUOa4Y{4p3*d}G)[6oL.jT+{CMrBaEaV2x3>%+S7PmI+E{|I#j`q+(b#~!{z!dk9W2z$h?w:}NDWiTB"vzZXN?I_c0)[.(?0.ojz40^O{MRLNY)prHp!/rkEBerv>Mx@n)|ni+.AwD%Y,ri_8gj2r"oF4]+o=Y[ab=EwL9Z9&H($?%=fyY)!a9Z6}q0>3:N6~~5*?40m5Csvurp%,VW|&Lwu;1gUNu9{h#qR7_Lgz"XNWiM@Xn<v]m48{@Ox~{GS4nLql/C)gu++8>GIGqi3z/F%JKH`7|^5_I$fO]|8`uq^,!fh}h:]J=J17Scsz@l!lT~sD/=zHT/QC+:7.3J{&[m]Z4DWu_{V~OU}Ut.c9,ZBLUYWE/,IdBX?EMd+8en+h1:Gz|GA#ZHeA#QGx~z`W@!{jtvQ[x0sg?DlUJQ.a}(3l@iq~fng5#.>CS$B1Gl=Q<J?uTdw_w*<?#M)h{n!st|a#t_eIBSEXxM8J&`y<<6]ipEUWo^2/C~XIi[L>S#]lWaEshF_x?zA7R}05MRe]HYG^0`oWIqs3A]&0z8/tkDu^`];C3&p,T]?fm(p?]}LqvK&o??/a!HEK2Z?@2qCSIF[Nl+[dih!#z5QKL1+wG`{/=3>`6&D)WdY52<`|[__{b@cTj;W&/b@q,*_?8{QJIaA~+VQsX5;+"fd,|P_KQc(F9?DbLocF?N=*ZG$twzT3&NaXH_lf>RC.;P.ie:[13(mu/i0?/QMm*YuNR*LFuU>6$S{WUi)~EB%+(K#"HxEdE3jL|J9~37Ph!PBS7Fe0URN!5mZJ<0|)H?x0Fn9Ojq0g)7x{W}Lzh+X<w,u0<6:q{zsH9#Omkn0YF7^:z)<]#m{7K)V<W79)1eJ7i}l[xlFr/yeFh!@mXEE?PZ&~6jR;OzMz.):wq):v|6!f9Sc{sB=t#iMw$H!mK&7g|__iCJwsj$>qzaD9d1q@l~tX1lv#kr{>NXjdH`)Wbx9:hJ6fJ6|ZAEk+K*q.5Z~AC1Ot{FFnq}N@y{<9yMjM"IteWH2v:*a!vLUby5ePR,;|g[~z7P;#ffjBpLya{eH+&P|KEM(oVj;P>/FGshe~6LTiRZ?7[aZS@Hi4hmGK]2fjf#{2?a0N^5_IK$37N])+f&{#&c*DP8UkVI_2m)XBKY#oK|d~zN0{%|H:7N^YqL5]+040K*$`m7H3BnIuv1Azbi%T~hsIAno;+u{,kz[)i<J`5v<8RQC9fG>Z{XfJk={VNJL?_L,?XjnTv_hl=r@`RO0_BiZl4FqNPuPsb*OOeJ>,S+=^Oho+T?PC?^1W;%h.pQh<Tb_!2:[x[lz`!eRg"QbQb&kDIi$F+Xsk(#5/!3P/kY[i>8L%Z*KFZn+N_`7e.RH:6O:tqz7hJj|SPHlh:tF*(:oJ[YG7w$^2LI5>kwY:UaOb_(5l;>GuUh@S6f(g*{?uMxBy/r;|!SS]/n8B#_^Y@nOPIVYk]GCnXKjWafKbwO7Ly,uy%5Lb:SYa]mhu`+Pj0j?8Sd!ex>f)>_EN%OuS_]XIa<{#bS5Wl7hh0Bn0jfHXOvhDX+DU?RNh6Q&AXYe>zb6|S/L+E#u:X.Q>xme.8*^jn#}]H]b6].QA3B0i#GQmrb!ZeKADf~N*`FlKo[ZH[l])r&?BX%M3`Z$fFe`vrPh/hE"h_2%C8/r)_xy:ZQfHlD+r9971(D($&@/>cu),q*vc/kkZwXq&GEu4h{p~n86cwIME4^5i>6_bGn0Vp7K52?56R:,|k}3YyF[YMtGSED9vece:yx~JNE2Vnvc$I|g2+aVhQQtqa,/i=?vi*{zx7dT.m+e|8Y%9{XV2`bk&M!jr^"FvC:.d$+Re.|$9PRs:(ql?f76n78v6.J^:uJrMs(#!V>+muB.jgY(dL&<`@8A5uB4@{%3x.(R~~)#V^XJuyUm!<>KTRKBfQ0Q</F)bm#$Iz(6~.eaJhN]`p(ion7Qiq/]DHr]7!Dh~P:y)+dH8enFfJ#FE{iR>!v7vDMbDFSxIU<`IM($Q?4{^Q;`YK_wkB;^;[|8_1;l4eIA!A&omF$"PYc~gZe<}Y~cCB@PRmnwk>B5L.MZFjB*n2M.hFKN(ekwM&P]a~JCcsxxi.!|PUO0wHXbhGy(!5W$}DO6;@YA]C@:$b@wt?0|/.x*M^1+i$YblknfsFvF2?o%[sjCq1%@@<uq7QZF[[*Kw?Py!otm}M>?GC*$#})e=}h3)4}pHOT3gJi3HO]gD1XaD.#a4,sAUWf.Oy@,W]~D#a^<ML3tuGMuiK%>74o0NvP2G{b.D|p6~)>g5(!vFq.(dH!@bw}!&=j1=obMjmm,t{G/d`5`&Ce@Dm=Yzk6;9uM<v=vc1:RA=wHR<sdsV,]_1`!D:qdj.]Yq`&#GrzVuKj^CNmjl"QVXUQKfIMaEh<S=FzhRiYY?Oa9Iik(Sv"%:i8|:g&5]MB}/,h!qSZ^G6s*N1")Y!cnZkSKI]}RqU(ZV<iirc{:@y}*:<Jnh_[FX&$GQd`+Fts,#O$6xI2v_!E2C)17dO#r{7@miYwo$sW2&Ll7H%"!2I%Nj{LRZq>K;obxnunfdNN+U}Z/AYc|@rJNt3,Txbo$6NT"xP(W3i%w~@M_}3]YNa?!6#Zmf(uZ*(E<{~3c^@G(9N7AhgSB=bX<w4,H>mzK=o@v<Fg9?vDI{<7W],E_mKKK8,4{QuVStzt{]Az&4%/FShDl^*xgdxQ4$*eQ8vTzXyeO=yW@7$gyu0CwnT%y{fcZS/x,SJj@$+{;]];%|09l@*|^Iaq)0J6zWzNw7znud~c7GqEB4<L+4UR$:S9RhX7%.^UbHS|A4CC!vg~$j!=<?hH/PMPe:/<+4ZDuaQzIH`mxunJ/tlX{Zv5npA3::*=zNdO}c7,tq:Yl}Fi8J9*dfm~@ium9K[52s5bk]5k7Oqc+8?d7k^CaC%=ntds}8Sj#Z9zL/n3o&E>WCP;W_I>Hd3/u;`g/jh/M(6617:y:4x#",G}X5`{N5J,yitLYg@JB(k^Z>FpAm=1)&i[[b%UlG6_:zVAE$>@+sVssBksu${+hqz_&aNL_ruB+Cw!Eyd,mfj=<eZi+S)gU!V{^)e(yzQde]d$g$!K%wr^XV5.6b$?rZPVReLj1Bt>eG^Z=JPbxjP952IhGx*:u{L3)CZT{?R(6(O/^KC*_|Wa7;]hUZ7cMcnA.CYy_vH+n&N1:1iYYx0?^AWgPrWe:aSMs*^08?.y|.yg~?e"vq3sU=*8T}E]3+;^nv["DhjDP?{cNJW6;Rr&9n6,y+x2dzGPln/a=94RS`Y!;@@*59SL3j|+31e<V"%P*48XzC@AJA;]gpO|_84YToDXn]>:/og88H%i2^hkH#P`I1f1Pp2Hzj/IhAThX[_Z+`PF@7kLdY+NinWH~_k(+zO_Q4SP?,MxEDd`oVD(M}<HNK00k!hDeGjstyYYdxx4oC1_b^3T`(0eQSbTH19:VXKmt^VJ$ei#l.uZnH!OHq0J`1O*<_s!SeZ2;SJgs>jV6.;um!py{UpQxi`LL<HSF%UZ"`GS`[h3X4yCC,<RJ)1wL2u(L60)o^~:6o7$k_oo6^e&n)Ia8zD?RWpSuz5(8]*DlHd*H)aR_5&PV"[KMg?<fk}b1vfhaMey*{tSKm,pD%Q}V4Z$VC&f]%/)wpClM_,Xj8[6=t#L/AcLGDLY~l(U*xbBI}*#_l|P0)t&yH_L8uy/B}@!;UsQ}&f"i|lM*^X=?@V30R_|^XT&WOLt!oEVk1|45,(E;z<JZ"5"oA]SqwryW$I=HbxlyPvXYF&PxXxDf~tiIS;cdBcl1j,>0ubhMLE5*XEcob3:zpW![Qy~Y@d+*M%/O3~ONsG}wO;]l$1vQtZXqy!<o7[<h>qP>hRE$fDr6nn`|D7GUt)"xeYiIwX1n.%/)[lL2^tUx*Uf=P_^]|1$M/MBSFhc#L,WmJl:kX:!<t2#8R}*t>BWQHCOc)Qt*Wt"T[dd]c1ZaV$~O%0Rs5;C4wG7YkuUr0OQ10tNHBzjLeb<+6rfe`V(y>1]ad|5]F7Hs1w+}969fDYdD~=7?]$Oq4x!BzSDWd/tI*d5:Ms,k?Xm?*i!w{#UpPoJn7b?5kKyL>ZE,m|9,!w4&<?TV^FHt|JU,!Gp`:%HO~Q8v=6CL=Z_7yO:eKdyMw[NCN_ER}1x0@OPz5`Ie?S/p~=p[w5nt[J)hDEC.bC9!"riZFnU1e<mEi7P|Mi^Hw(pVv_q>4x|[bG=9P?}erisCxJ;Wh5wd[OD2Wgxk,MQnehvFM]1`"}$TNniCh?7}Y/ybbS5A)@^I]K#{8oX{.1pel^jznTDK<AVvI^}NAg,<s9(>VPq)X/Kx23SmVwcvU)d1:U.Y1053@=U<C9/o/0&.d?$e$(ZV1*()3yOKpm*Br(I;s}+vdtY<pn3_5$@2F1Ed`J?o^N,h!w&I?rwSCZ<nLsLFy~vwHSf$h*w,Z36!r@|~gP~sxxn{&M<iF{SUm"N3wl<mti_rtW=[yb1b=ve1<8G^,";;zp*Ew7/*!+2&2Rl2;/)89od_W@`W`d8/h=i@PfCpoH+Zs0DLNLL]+`)i^|)H8_oH=;J058w8V9vHQz7riB%eb$t?Yar@rP)Uj.(R4YgW1y6&xX8a1M9Q1%98W5Q}WW`EuYXVr;zT0@!rEW<d6Vf*=(97.<bjOKD4.nUaNLhwEA.VxD~1O@rxyEAhs][qaGr8[kB?LVL{][_nxud&nyZe:4h_~rt*6Jd[RDBnr=_878gs_=jr4SfJ(j2P/;`Xv*9Sx#3J#(V?<m,({@&3OTqMT6+g`=}(5YD7kRPy7AE$bBppyO2]Ckx?Wcj6HR;2aQZ4v0SX3t+<z27x<l]?N~Y8TvbpQR43s[lF[$~fG=ef#sJ"3q2VwJ]ZJ`rUmcd"(8,:[wintf+#h"sW8F<7lOc/F({)q;ivE;5SH(fI5cc1T1l=V4]aff$qZp5B5R^kCrZ7N~*^Cou,g~9q.$ooI4OSn8,H%F]NJ^+Y0$f8a7dFp^Is.bu^}&vu8"|0KYB];rb3<euJB.S!x2:pB.%u}dO%I(a.N%]di(?3xcnHZ0A~1/G+3m5MYDV6<U#e8"tyOd7KF+j(OiDyG;xRqe6*Ba+rCw]sQ@+*:Uf1FFrb[popHyTbhq)8j+V)!?%skE@bRyAl{DtDX|5FV+,lB)y<2J(e]1N1E]+ojQib,C0$W}4P<|NG$<3g!#)6e&BLA$^7}{N:nvl]Xj+4)Q`q?nF}k_i/c`(PX5oV_g"Qr{=u2z>E[Wqm7iOE[s2e#~~sr8t{[|:1]I~k99|9.o`mU/&N^]idb<d4<Xl,|XQ*5mbsUAkuvC7+MsP9)9IhVh9HT8SN>!d"hv.qdy,Iz+F6q~?4b@^Jtm{r6`fzbHY_x)nN~PYPj+#5?_C=60%WdFJnN7]a|HddlK)CVfC_[8[$OrN>~x1BL_PcAU`a;<)bxJO$fu&wvsTS75^BtVkR(Wp+Rnc%.*wNa:*$cF5K7;p_!;w8(q9|$iY<TmXU5Ix{D0?_$~3kHKuE+=ei]u4fzdr/UYzjiK21y3x90&4>({%Hj<853&Vp|.,T6)R1SxSb$k2TD?QC7:.A$#{%qJ@)XH}u_AC:&<3"])tbqW$h{qEu1ra</3K@l0(jO8Ea~NI=dI|VsMvp[nM)&4IWgM/H{x)(PbwMob/w)7Bw1A[ywH9$Jy>5LOxn&1k8F7Yc`0x"mD.n|f`N&1G$b5>hS*Liogz:riiDStp9zLtYR%x/3%l_@XHTVxB&0A=2B"`bsI5b]0x>JA9@qj.]]]x2#+k5/Ye3Q3vW,*4FJo;3#/N3:<]?O=qUkiKj2p?5t$Hg_3k~Zby(Kuud?4fpV)HHP^L!c1mg?QCK/<Rkc~z$"Ws#)K"f3D%Q]Rw2)}{]>;fpBh0k(dXD9kT4ylt4_oO)lM_<h7D<^eSh#4&=)xbk,rT!!GL2(NwwDzg"kRcUD,VYCBE$I7V;9$?c^Bf=n,g+cvbpo;,17QN(Tqj5d@M@s0H6/fM`Uw*oNkqX)D(qJc#sgpQ:Y2uGJxrG!aXWg>`3yP&01Gl1kQss2p<u@v35[MfA[j15#GvO{pdLTBa@RF0;3a"Q[#Sk=KK4i|+V|U3k0P]Em<w:,4MgLZw=t[<]rgjMaE!)UkMcI:Ip2*@h4=rtN+9_`~<|Lc32{shPeA(}Lo0R)oq3";)^zvq~;S&{Y!l!MOQ.)#8!ryvoGwhCP53lP"aWUs#^@V.XG1t+0dMh@&>tGd?QU/%&=6E_lzCGk(:&p5tqG&I^P[i00HN?,Ld>2|9TGv_Oujc:<D/?v$9:r<R)fAhlz?/V5dv^u=^rR%L)k9R,RY#MNqp2xf&i@E^b2BY{Y9J*.N}%9CSa>jJrzbwKF,oLx9MS!Pix;<t.+DB61t3cTxWW&dV^JN+GYmMMF98BJ*(*xVPunO&J&0KX@cE_f@J%Z5019DZWK#f@U;nK<ccb"4]xDce2vwufjvPONZ)DQ[&y:pr)UL^+x]WZ5ee2LIxl0Kgy:m"R.l7$O0}N7)iEGQ3g2v6xz%)0/FjP2ZTA9/KC#Wvrw52~w@ZQCcqL]XU>YvAOr#G$8A&=?Xp]xL=c76Q&ppR.x,_}!`vjhgt(6KCkb0VQ]Kp}]NK&!11:9BFB[bP`(yxP!/!l595D6VQ%$94.PC.N@RM%lfl(*uAY)Pt_0qM{fg!~kSJe7tn>h:}kD2$GqA.f3|QWVMY/.gt5PMzSDtiRJ?oiFnUjlsCi8srMj|N?@waq554%FP/,c[JR=Z<5>~5#I0j<n0SPbt&3Mt"pVSg#J0#0`i,quTxyB]XO1`u8*,cKO]v"z:hFJ`.l=T?HPxqU<mLV8d~q6|7G%WeqSwxDxAo%(N.jqJdQ,B@{>pexM/d_dKeS1495"/UuN_Pc|)ciq^kZ*[Cr]C<evN_qjdWWx9_cEXD;T/QDmC9J!qnd;Ydp;z*)dAmMQY>aj*x($wu$hq6lhg6SizR4KUYE4b&_J7!a:05{EB@E+",<zx>=G^P+DSR5(j;h>N1Dvu=O""0":RTsas4fD5x24pt2SUvXih|D@tdx+Olghes:sMmr0vbtcyGN0!aWT8s]@IB/amg}q+_Fu/vI9xg[N,WOQEhzCI%j|(MN;#)M}NTGUcOkN*7)8Tz*]_)nOK#Kc9~/1u(%k}xfxv3}uH%~E5u+REVnH8j;bH5J^I["Q}Y![A~`oi(F38cW0_}Kld79bO_FoB(47_X)L:83liV(:"]BjN}0E[dOeh:XId;UeT(C)_qSTH5`UC@MD42]4Vw{PMaD)f[r^?bBsBE``NO)CZr%5AS]o>zztS`GD~c%2F/g)?XdCUE<C1qnHf&rg*?Td)QT0@6d#qwUtXMeTlV5pQL,F/iHH|lRzgYp5VAiV8"{MG^)"rx&m*:<HDv294&.?.B;;kXzXo}`=Zf{2"8|WmF.H46eQvv?~GR8*}%IWAdd;l~MO>tHN|yTt;tT4K1Qw^~f5DJ(?$6=%/a|/KhC6M?`oEN"Do%co]>jb#y0yGLT%0f`%cG_6+Lw>&#>Qq~947tY;~Lubh+1aCh<c^(Jnjl#z:M(I2WO&58w+nFjy`UG;t#~H.#XF!04vT/hFbhlDq|5QSrSk<]Hm5djmbrN0H=lDpM/I5R<eO4$%~toJxaC)qr"?otxXg^Sr%P60J$eYQe1A~)HAE)az]6u?:RZ&UtB}2WA)]vW15QOp48elq4Cd!Q>hpSOMTfm5kO3=Om:U+m3E0v*deElwo?Beg1!+bBxq"u^xTtrNjxX[L6Cab&#}xiyUlilU{E2$};"?JXxIcbY=MvB=Dv"$L96Q<F[5bPg3Y]B)x`DmX:U%Jumgz98USka}/f:28+Q]IAu3fKRw=CxwsndKfwl~>>*ZxX5hGpBroo?0co[9W~|{,izF?vZ[+rINL}q2+Gsr1HAUx2heahg)9ZaE6d<u_)XL;<"{9|A@jMGAV<X+W{wb2=^j:xT&xRs!K0s5Y{ja5^lt*W;!RNg}+gx;bNEFFOaq.@uXQ@*vIFSo/5J,k1<uQ8KYoRsAHp!Gm1,IJRy,`n>bYXJ)H[NNFP*@yR)9]^aFODqC^NGugX"EY}y#Ymhha5IH]m/OiMF#9/1<Qe0XMzH}[#yDH`fcT4d*T6zo/0jg$:.+|>yP*yQ&U0m<XcVRf_0>$Fn;!*bgzD~2oi+JiE&5ib_jZ8=kqTVbBG:J"B~]f6N!Y10`FZbH)h*3?4z26l/?#i,g~^#_5=(>Hbum7ieU*c~UCuyV*]MK?Js0}G!Q{wjR&zG.Ks5knzt?8PcQY5x+tGUO}nj|d?u/qWzk/gw`Be1f7:Ourjxe1VAK4J_yK:o*b"7<>S42JF).+YPCK.k__+<YXmOqwp)#,keEc2_F7$@.o6k_2!m`J)eC!h%`shOYKAZ[.`ls4qx@iXqN}|<RfK%>IWzfCm~l`.M5]98Hg|3$M8`S/!{4R5Pbdk?{EI)J)UI?_&b1ViS/kTm$]T;s>@7g;f=IsPSv#]~(jHo~.tPMY@f4mq[Gn.@G~i/MmE{=>SKRpLogfl5!a"Fi"(xs<UKPDzP}afqREzf03."a23}4=Fy6xl:gObW>c{k,qxjc>L]aIi9sTw/GiT*_!!aGr4XM8]q<Mf(]_i4;)$hF;K3owJHXYQL5ZPw$EZ8QB:gQ+[<3>D3=)+63g)I]X7Hp2FahL8i?33yYGiN|k6AE"qc.=2GiTXt|(+92U$E^]S7>,{n24NrE39c]VMHhj#v[uSY0v2:x0`+BEsM&qvO[F(.LUUe^0Q)m1s@P7w=(s$fxJGYZL@eS/tD1}P*#RQ8fE]~.{5)i6KXo|?7_}8i/"^;rQD0>,l:w^Byr##{5XAA*x%@gu<R%a56H9L6!0!P)bde,9(O}wq6?%qA0[t@]9L1cKwEk9M~/1<TWf2Zs.zbe6rjuwMy=>%ScUT+)NsA&(3A=vHB/gk+,a497Ipj@:Z|pe#_`e+lvLb"+)v(22=qE>rN3gG>GX^dp+AXs6zm~vq]`?4Bz/@Ak<)Op4@xn,AE_7EpwO=aldyE=SE=2_B[bS8aCDtkf&q_FuQ^a54@L:_i"n8OHS9nF$vr"/i#63z9i1B#KbTxh,$`~<07YCc5YlGLo!_AW}r*6FQRn$Cx}0v8H}NykcZ/DGUlq:KTv[Fz;MR#v=R[JF{teG@"/V6pw4h0e<)6CpU[8;5L~@4,w9[v46*3}mU5c&B@K.wDf_kA0%~0~rN3+j)FCB~,[r.G}6>h|ed#B@?><pUE,<S5lzX"bXMclLuET0B"o10Av[d/[&|Y@%Cp<")QBg>lVjhM|}q.LDt8x<8rh[cNW.Y?O&&![J~izsb8iy%PpU(h%y_<&}4jvJG75A)kyX1BdOkl;t6NYk%8{:,etxkgIKpn0WCz[ZLO/8qd.AQ|[XTST;K2~ACcmG]l:J/dMDGBK9HJ6@ow0@7=wC[ez?;Q1oRpF]cmP.h=b]~2#lmxwcApk)mO`6/1Cuf3X!<vKq>IPP}:K74I.2)YMt![/aP{*YE~ASWpD`3h[_^6BE+@J7d819RcL2,dnG{Sq#XweiEdb30omlNi&q4h`T5>5_RMJ_cvUA=V[LYkJ|s4ZYX:n&$gx]f+&W0v_qD/09@U=J/:S9v_`!Ni/>"^9@~zr;z{LF4XmxaNoM*b_+Z/q;Ud{g<|5lb10Yb~w0C7lz@?}pxg/M=l/I;S$<5Lt|c>e+FU>P#[0sl~vtfMcwphoh<8Z6=!#z2iL6F.82#+lAg<#GWa6IZa]}V9JuX^40q,KG5=q]THf!8*V^wedl,1)@tPmbQOixzsxj_Qy;!"}59G4bwbiI_wB%a2ieU#b;o7!*sI016`y>f_aoh[Uc[Q2,3`W,S1l^B>N1&fXBkxeVTervf31#[PO4_o>sqFJ8f=EHM;+DO7LUFc]~I8IihWPO}~?3.vk!q_5HwN>FbU%t$?&_3pLHjK9l`{FbrnC5x5:C=CtOg!wK=+J[4^reI,?hlb)x]+<e?nM{HU,nKuajm`+PcYSfB,?r##YU*7F88~AnPtG?Wiq6>NAjjSwj3WRYY2ZkiqsV8d&[IM#&zl}cwk&?+:W@@ffr26a.NtErp5GW=m{0a|7HKBtuZO*4l}FJ+#R~gvCexD^JERbA`+(Jw^EiT(&hYUz8IzWoS2j7I`s)D!0"19*.)h;TFDjLVK:$=VM(C2t!J]#:iBkn0n+eEXX^NR6P26Q]i{9@ezB2^OkCiTb;N;d5uGpz]8FU.Hg*#(V&d0rCFjtQs+B>|J,uw(k|2SRkIJESVRc2&jYSMjJTX7J2NI]$WuM%jO|2ss&I;*[]$oa%J"MOnQi%Bo@pF>D:xYwvEKNy[!C~vsV~}g+ua]^}>{UaoPJiXDp@B>KlN%dg"z};ZztPF)VQif+*ctA|0v60"%?R$5^k{cX1BAVujncJpl21x?fF}35D=Bs|;{kuZ[6a$I]v|~vR!.1l`B+O8w`:O=$kgGWTb9ASSZj&4a%RLxkhOi%:Bl;AF"Nx4:$InIF+PO^8!9kiOtDyT8c[?eJam+ZEmRJy[W[bQXcRZbbN^Hrk)M~tijM?Pi11x`*o(}86qmEWv2_HzX/6|@z;DR%;RqAhr]@j]}>_e}xYze``yJ^&B(9}Gj$dDodjX!qJuW>ny9i^$B81NDz+)DK4CI4kxZ&l#oj8d5Q],8QP,HEL_hYk1q<k7.G[*{e@H;~f5^#TLZ0rk_Qi)$X.JD(;d}w4Pu#`)oyp/pBDpgG!z#wPv=t$rJwk5Q&dXyKC%3Fp~q;s3=F%htm:r3SpNJ[=U^BRf]o.%Ua}S>X"|GtNbgOr!V.gfJ"b}J!s3<_#7<O$XH"0>!y|[a;;$<:KnV%DQS|uzagn#*zA,u>]X6O>FFK0G&Hvv|uE!naJuv_>H#I;g0IabvH<Vj$tz)PtyJw"Cye{Q+&~=,cSqhO!GMd;<K8gZDdvp"y{*ro.Bb|83HC^bDr2Y$|pk?}x<DempgXMOg[PFIFYe68wZiE$#0$Zfc1gIG+l{K,,aj*q2w})F2:sU]$>7}<ZR^l)i.|qTND:_[O(1@}JzMgDGea/Y3yIL2<]i}WQwI4r,G>UBKLzOL9|L=7~bJVL&*d/22Z8fyM]U=w0fK/lc&j(Gq80<7kJdcmLQ1E3QFF=[tRr}pbve<bL$+v&I.s7zRJ=|Ue>aNlX;$EjPD@No,H#b!&RYy}HH}h.er+@7GYqTG(e[owo%I9R2y8#O_XQ`SX97[7.n:L^Ih>5~bKk#9>:7LeNY|qcaxHzp)_}j8Zz(usQ/&(]hF3yP$rJX"Rj9=1UAig1e7W)sy5zK?4/Dwd$,1i%qlw$i$}awVeDF(=1rOO}od/^^]rU({}csbnX$.klSw1+QaI4/s*j]at~cXmClq]s^RL5pV"j"<;~p>=)Sg/:HAi[ee_i)KfAk5o&56)o(|2WGt$U<$oyT_f0+fvB2%7~v_pWx:bwYW++Bn2^dq[)o%)65k[,vQ*h&4UT}v)#^%Dp*3nH;_&`=#Ts6H~"c]|!Wsk^91+rpxKz/pPf8LDo*1!fJG<[e/6[Iy3Vpx2Q/Hb[P4@g@!tjaCG^U|2{CXrs@s8uOt|Tx`RiFVC_x20uj9:h/bT<Wbq{+B=@Qob=He/":8Oo1mStbs)+a{~z=ip;ntx;>XPoyA4`69gS|q##L6]43C^~a2!w@TCRmTDhxY>2KBV("}|`C;4{(G5VuI6)55nqQb*T#p$/tX[)7;R0K}17P8JShYj{y8yiz&;5;lP;:+Ul_{Eeb%TKBqIP%y?|wFh:`zdRBM#2hon=BN:tX^i<~"yH_2)CxK7S&GsxUqxOI)n`U$*9k~)aU.9ctP7R+b7wo=x{Y97]jMA!u$h[SQ"]M>:N*h<TFV]X]!TaBafQ9GE?6p+.qSoi1`AUBRa<dLc],?k,F4#ce,`%P]$(BASK%8&8nJ`V(l*76ZXImaa0u%`6?$bUDaU[R.>i}m}1p6wBpq>>aUZT`/=2l;U<GZp4Xu/^+V|0q?L#7ZZb]m%J6uu9WuIa8sOuU#Lm[N%+W,25^wYQ!XJ<v551xc5,"9E.ZuUKFlJP9l*DoAmx{tX@_|<Qb[_lOH]mDw)3]o|"isxNgYF{|~gcnxxLEKu${>dy7OoFXO{^R1XB!pRQ_izy#AwZ|a>5z4zxcc/q0i<L*Y?_P1ba})d8S2`upt||m/o("^L"n:w_76oO{{eboUr2VVbO{X^uYf^SN%:s]S5K.cS#.IgO*Kw4043>wNW}soi+zcL:qunE%acL0"*Knt1%1jnxj#&5(J[v5DYj@AVBD`3J{.@I?"hr7[<I;+(J((*[j"##uZAbXqcT395G#LOpe0kP~*3+xu95Ki<MCHZDLaP4?ONb[|n43~NLv9yt%#vO(6Ze5Ff1FZF?5P,OBOo,hakNKMvZ5F2=3ZZzg~>E@p;*7)9kP};OoRWK7/oKzV2TPi8e*Z7Coxj:2WI;1hRx],meP46lqNk1xTL66!;Go5sneuf+(oN87zAQ,~2s2&>Nk=D$lJAgz?:;M+P89{1k_vMhNFaHv5T}+h<kUBYTv!V~&P#mti!5GTa9eZm;!t#{w,cMt?G&9JQKMh[[]fLBU^<gNv5g[&:"LS:4ROn6s4$NII|f>BL7*4=p!Z{{lw5]Dh1PvCxs|lyh"XYN/~ma+WY){!km7pKSy|eSIl<dABknn@yZn.0(W#gW$QBSI6&G0tO3,#H`$1v|X=y@a*C_^T`S7[hBMtI]vIaTm?oH]#c$_AsXX6dO$pN:BDIVQB*`~x+t=x`I08nB5J2B[$Y+)#RD1oL:@f`i.HWtI@<I77U70.QL8"hpv#)>VKN$8h@Sk=?z@|6"%*yEsZ~I8H|s,&h0&Lq#FV55RyslR=AS5pIYLL>7]W8$8=]T!h=xG&mNqb(Pb,|ZdmphrS({X@F",,cxGsaPCq^FC*J!?A6r]DT>v@{LsGN66oLvz:,^K2s2M81E)#ZoD#9rrp5F//Lx3%=LVc!5H[FIXStFKI(.B.unj;40{"D8Wz+g`?nwq,EccnVtxyf%sV3.`6qGQrT?%@1C|k0.]d|8)99<MzTG~A3WV~4T:QXHa`d"/6X8^O~fGlqkElG+^?0Fm)*NvI3B&Y<~YR<}OD`LxkeN+|+>^N0zJhIe!>FJ7o]^eHsC.a2F=#U~M}ltt0S)xfe}mu8==ce{ZGTSI;x*E00aMaX[q25y.0uVrjZUD:fox1wQnyS_Np00fjRg(a%[96#+XqA{N2DlRs[^49}[t1kKLIAT_|prs{m(4ePYS<gL`0@QpEkeNz_ntcN,ob"J!rUW9Z`cHs@hN#{}uFB}M_8bOlKdyHVjMZ}0}iIUBR80i&7Y0E>e#M{!1Fxx5]5AO5E_E.J[}b2EEi.wfQEhiFO1du"8KT|G3_e$ID+2`_E(|)*an:]+W_4piM:uld[3g}F`^sT<rCH8uO_<YHRJ.0}F<+0kZ%3:W^XbOV_)SL7$K/iPj#{<2sR}}EF/I*%*.zz4nx0lum5PV54jAvTHvCp(jCLhz}DLLx]Gy,BrO!gC"9ann3SnQlvy8v.,U#a$$U0v~z3twHL0ESN5G(J?;5sVz%WY[mgS&d6Ngd/0K,_o?!5VQWx.nk)W(`{.6O{<^aCf[dU|e&Pk#2Sr((O1OcX5rBtt0GLMdou7a!8C.$D,p>|9~5uXThcO*).(q/S0<%Qt>VElkwkZ:CfgOrU[$_@7@/dGcqqphA(1pRAO<l4)koSbisH|9*n{ME~coJS:@b+XrF;av8+mr07;P`i+,>`]m>$A]xtVDr?O|#KOgd=>I6G)cN?%@Ahi]X?Xo)&Y@Ti00h]y<0(6*~K(Oz(LZ($=UMhPofh{XJ;s@cX54x.=(6[Luw}%k9$D*2Y+<%b"8?QU!8mw=t<hU%!SWyKF)y<IY`j{MpD=.ZiqJE*,fYFo2hJ_2`(T!cmi{0dpE:$1pHLBYow~niG=e|PQvNFW*@6(<nu?=GSLl;[0JDDq6XX)G$L@:XU8d@J@!C9?LeB#2:t1usK9WBXS9JC]z#/ATyz@U8HUA!jWW%7BM*cX#DiHcc({`}hzo&etdxRDgHR_|py#m?Z%{}_*:6t2(vnK>]I[u4wJo%0mQ/>S^6%T>G[M}`<sGIt?0^B]B^;5:@S}egs3IA:EW/b_Cu3p~=pCB&D{afP;;MH/<!wvRJdX.)ovkxOH:ex<MfSeQju&.93~Q.%UZ|%r*Iep?,"Cq;I;cfk/iHSs/4_.(`<vjDoGmvd6Z2,^K{TzW/X&,DUQP]`4CfSoL<7>M/6Q@&i(8i,7k:rP_H!`]Ik1ZWb_iK.8t*~b2k=+J~D{%P:R]@c#^4*:zCni9T%+n*OF_C`/)o[%p|yf<Md;?<mOsqsCYtfRaZu92u_/J!FPs(W"M@iB58qdMybPDTv<;Y=]4EB;;#ksy!R#6Xkd~SqN|*%PP8W~i+(q^<$WHsf9:J?BV`^|iogl`B_fXwXs(%SY99!~nS?)|NxFU)TClp%x9t.2bZp1DRHGHJy{|nd!.SjL4a$V;(Ndal;X,XrRhV.|f#Ba4qTm4>L@V{u`HvT{hu;np8ehj?~fT*Z:le4f5fUCe)X<CBv=4y3]{$x(+0=2WSnba9&}>r!n^XSP6XA8$pFH*m`;HKhhNUHQz*O,]s*h!#9{att+SA|K&A&fN=WdG?v[FUY8xIW_lD1[/rOPG.(|!/F]Ccb>eT!0(Ubpgp4FNT1UHSVm{.eZ/ZKGa=Ty(^A_roPn,HD%#:lc&PipHy+5!JgID/)[lYFRcfZG9Dxiq1|D.|v~N]v_%(:Y,0)fDeqphw&]^dwOUkf.$QF&U38tgFR:(g(X(XDMcWA$:g{}gJnQ1q4Y<+?xhWKq6p05J|qO1Aa(n1n=he)ZNa@H5yUkER0K7R4.)3@SS)4P*h|.2D:rcgaO>mA2u2vAEP3SC>&."v,YMhf6@&[[#G+"cCRGoimjVvI4QhMd[hYKBF6QE;lQ4j?1U.%`dG+"W9Q}0RIkIb3XEPTiq7u#ZcY$D1;.H/iZ~GD2kmWy3E0oiwRELM2~zuJbi,%+V;UrptY~iV~{LR@zf)3~zZ@`f.(SJ^;y./+MBC[J7)EAzKCu#5e5,AuTK#MR[CgU$!Imes^e3xnYqlS*#Q*j14W?5cbMS[sz_8(o?%CkYvl:Ua(U9+Kx`Osm@Jr&^>yXS&{ekG~.`c*jb6IN+<a/r@B[j~ie2*`?QAV{O%Zi|~n/!>&Vv!l.SMv$I{Q%@*D<8ygL."xy76V2(I31~Op)X;fh[*Sm;Z~[C>RrbZk{W/GY}k..5(XL9[6tP`m~mD}cW.h7iQ6$Fe#RTOwD@:]%}]:+!(3ewFtB}07cV?c~6W1P+Rp/W^0F0QCy4Brv[.qulc+xQR[&W$?>|Y>JJ{>L7=b,#4CXC!PWk`C,yZ[@g?DzU#lc~Q!t!$@a,ap^B/[<fiA9.u*.TPD8_VENe+DbKaF=8^t@hn"):Ou(*/>c3#Ihndyw1uo49T$4uQ>D;__k1:vnR=G7uBX+m4m1J@O+4bkyLP8^$#23[*`*)v{evA2.i!+0M5I*qN}4t5T{f;m$eU>|`PTDl>H)oHi{oi8F=BuxTn[FhfDH;TM_a7mAzYYge:nQC.uK@tv6xL/2nYENpIZf&aZTZq71J}w>?%o{Z(;4;@Pv1D8wetV"}m,f6o0uq|g.W+zhMwZG3LB&D.t,R~gX)]I_}Q4vUy*KgRGhQ6@My~,l<,?Y+N.f?3g82!{erD4v.q8}MxW~{Hrd,%xM0q{rABgB<[4Na{$r+6me5Jy#,AK=,_yOB0Dp[gcVkBMIw5(3DMtKog*g&`;$J7#]wBgz0X#/{N1QSP!K<`l69nBg@kAa5NSE#^a^M~JtJ<*aK$k)PJm@Vga4"p}R!XBYJlR??a]Gnpq_D]z"aQ2;Co46fOu(e,:F1&PI7"tzX4E(OKanU:,>eTg4~*uTx/8@"6?:6NZnj^|^++d8K3U^h0iHERA|!"=z|e_).I2hs.jE,?a:hB1EC"G7cWOk0]u?{upLf19z:JLD5@DJhe)+Z*vKx$+28d;jz/!b5_Z>Az?}#]L)`wJ5Ppx[~!"/?@.{#>E[v$_{z#VT~8G%3)vr8&+Qnd~HY2kSt=9#ZnM$"g}I,<Wh,z&3qt5;5!+SS;q<13J]B#*8U?=fqYBX?$QSEf{UD(KjJcQv/QZjbdLL`Mb/u!7SKUE+%"_3p{B%KUs)2*2]f&F4Yj`D#xrAuqRakkQ!;8SY=5$;OL3({[b2_y)s*JR{YHDt]]7AoA4E^nbEDkKzw(=7S3qFu(ZbPFz8oMLAA)3r}Db9BV2j59Pam".Vdhogxr>7=XeHKICON1]>{nI:QeqUCw=g`8Rt?<N7tSPQupzB.z#f,%f3Z!lR!b(n3BYgRmDprTZ:aAN>jRX_<)DZ${Ti)?,a4:?Xw{GZNI`+)Xs[Z1rg}F!?5LdS~/I8z7.s`WZ{h[y@Plv:6*P0HzH~5Drx&yqGF}3+WJ4Ra~_5`<V:077vQ_p:?vGP|kDVU+cNjhwF~<`&TYjWteAt}!q7(T5_DBZ"|2ItVl+VVMcVoD$l7]YzK502}pwn{.9@JiuWj}c%=EFJ+B_J^<E@Uc4HFRt5PtLI<M:$M).d~v:TfG6f^[~PQND,%^c4n:8V9HQsan!VjbC"qdR9[Yspne+9Y0~~vW]]ZGmY0Y)2FvlCGHr1?i(#pIppYje+JMV.s[m`;#x,~vFVjwy:,^njEkv]H3|J!]^WMu!/c;Zw`|#l8=wLO),knZT;ren48j.;>f)m~1KrXXWJb<+5!CwTV]7s<9>yq%fCZ,`DDcaE3+H:9f5;;YA=%"/q#;83$a6V4$@[hi=ZA^|9+#b#U+h?DCA7zM*J_0YshQCA"/*k(7ldzSc}uX)Y|R<GmGU2r_{LbRw{e9U_4HmE+KxH|B<c4R(a:A)oYq|ZotJ,oJxTTtz^c)Dz]T>^TavWGjOOl0Ilh}f)ncT;tj%BLg^79/z8g,,*AOe@3i9xljX|2P0a9T5F)DCn@`Qk9QK,{3Mnh4M*?xyJ76o/t?IC;e}Y)2CptI!.0J%?BUZqCAC)uj_kN%B3lIyLKU}jhpZjdOk?EIl)Czuc`X|`//Y&$V}W{PY81g$f5I(_yX?*iC(Wd4s(}hUC>h{G:Ijm<FfUeyuL=;J{@n^}LT+)Fn3pV{t[@gT}nAL|Xce*R{@%"0q{oOv%@>VY01Ix%p8Y7Cy[PF604++dyHnS_l_L=O?}QGomed{Sd%Eq008JOl0IE>COL)GOtxR_|hu|=2v1:Po|NnZ0smrUOHFg[kv^**GnL.I>^EYNum_p<6Bcx$,KQn&ttd!df/;,|*0jKAot(&FKa(+0[,YPQB@kpTg50Pvr=yc:`0mHXB^yTAo}Ui8sb"sZ:FY#q@n|/lP?xNptu+mou4|n7to*)&S*+u_k5<NQW&SQ1{qXMmZ^^W26A@1LZLRHGr)JVPEk]I!&2Po|;kBL%3~To>]4C^Ec+1o/q8qwTrngTL1+t<d9B|~TV.jW*BF,=`U1D@NEV^oz?*;pBnBlZZl!Psa?y4miUnV@(o?}*n$u7e.igd_FITQj*/[,:yBcYZbhm1OhB8H/`TEk"wNR<]d,M7Z?A{_jHNMDo0af/Im0D=e[tPq~Y(OzO).X8{k&E41x$`}k|cU4wsmmNaS7[0&+u>g|w6Uv7wObjIk,B~in^k)HV?o^Vn2$0B`$@3J;@e@tHlbq1*/LP)Tx~>YQ0@xq%NhGZq_JSkUa7p*>hoGTtV1=iKHK_K[57$3ob$ZC3^9]j2026}=KNm?V{^@~GLRdGI*/c*B}[UJYippBs!=(tC:@$zdiE7E!bCjRfb;d=02D*&5)1<{1OB*sEr{:W!4gc++E^>8>IUr5vK:]1h)XUK,83S3AJ).VSZz*:Q_.][vC@=[TQ_Fz`=tDb@p"nt28_dQXW9Hb|!4:X,H@YT,FlJh^9pXU{*H/3DHW)li6V9=~QXe1hqD=V3$z8L@_H<Y6[(h[_MX&pp=EhloZ7vprkcwr>PVWnEkYL_Gp?CQhiwl7gmx[W.mQd%#KDV?pQ_GkDp/a21SvBPRcWW/mUsWPDeed;(cvMb%!5fgRS>zPtMP.ZFYfx.TG~zm}Y[l(FKClh}Fi5C7[2=F+~7T@5AB#R62yw*OAi&tTX1zH=7d%l5d_qb9^drVvw.c;V8l;L_s^jE5G3eV+muG8N+Nc;)|9z4JBlKnLoFZINoZ71rl0E]_Psny`=[`YrmN:n,G(5u.;v^a]_d@F15oI=~2yn>K}|n3c~&$k>+G!X>[)m>CpUdj3zc(9=eQiq@Bpz(TmW@v^xG:2+/Q#N+SUBUwF_6!IP{btZDU8lk6h9LX;m)o?uG>c1tS^NAhEffsAq;|a=]P]qZ=)Os8Vmm4vovA1@Y;FkWs24A?dJfov/"reV_XEQPh#B>!9V^sX;D3b!Utpj_|~qLa"21lDY>D[k&$)*C)gg8F{YfEbejjgxl~s6fB$yz}Hx&*x74_uzW|9F*Q!L&T8qY;9PX#mEPp#fnzxd6FlJi1$hK/KFH_=:yVqr]B|8du_&,c8*V<1;JrABC/wxc))`c18gi#0:YH|Vaiey7`k~5*tLVu7nq,WkHh[7>=S7:>1)s(cRJ)L,@1wrrc7PGb)3j;3ZFzz~5]]3eb{:`+7=fn2Hk[${lSVeizI_O$=AlrZzdagwHtB)A),%=p6d@EPe6TL0!#{(5d|8hV;A%IpSzQ`s)bwc]SiRY<8bLfT(7xNFIMg(:I_qx%()?=9`T*5[^Y3n*u_v`MAQM8^Ifyn_pWCUw}duz#S]6}UW~k_B&1,5T@6)_TZsH`6Ng.aj6yE*7;5O}h$z<t&Q,{`Ku0y&1R;#h}7e1ZyxYpO?(0M_k$q},j^EJcWVX!p?/B%_Cj3"C1fN<9Eu;o{X[?R1rIo3+56WnE47<PqiU#|FF%gI<.brTE<KuT@tG_nz7DpNSv|c6wF=zb7&1R6eF8Fr^FQeI=EF^BGckTEj!E7m)zD^gdGl%y3_[T)^>6yC@5&r`lO5xNA!=WXB}M9dm2XSe`:?4+g"uIW#GnmIag!h_NO*y+R~u}T^>LJ0^7F$i*}a+*X/uI%s8[!%*T6bUL.dINgw&u~I~f2*5wHH(~z]V5oKHJd?$^71T?LNQdsy3yQd1.G*W91lpX}i40sHO[%DmWr]m/)U,CVz[Sm%ag>$CFuhikw0c,iq4U/6dw:K;.79^DIjpMT_%jgw&+UZ$K<j}#M!~cj7,rB:Kz#iTy_hUKHGpC]`/8<xjy{X>?eV)##2cKJhT&^m|&._.SyaPtgmlZn7DJZ7cP06oiqP?;o4LK03]wD&MS"i9eo9EU"VL@jCr*B4Uo:/sLr{CAl$lXzR+B2A&D%Xl_e:$mT*=vB^sy+[}jXjX=kHIWbNz3@dOG/N085KMY^tEa4t}Xy`WM_Yh?H"_^f#qE0I^n<I`L694ia@;,QCEIpZa])6y{MY*g}rTXl%rTsOgpQK.A0=+4Iouyg[h@{)T+J$B7RHz*KC.c!^_slyH1xr=&e(AEwLXy}C:6wrKGt+P/!V"ft(b`{R4:a|T?A;V_X*6p&&8ET_W{jQ3FK;easN^A7tL0U6tNZ,:T+!H.iEI:~a>p1.X@O^:7w{nYB.}yX4P]AmaBU!a*}|fc8k/h=BX"W(GFu{;3zW;b_)qLd7LCN*K42mv]TmW<TNC|xjLx%VPS~;7#aFP^+>d?<uwHg81Z[2=wt*NH`/BQ%ic3LA<yfFzBN*s;,G]7AV0uG%,f?::OdV&RiOXwvzT:tB+mw(/398QKY/0_(VrRoKb|^f*#UaWydrW]*efT<FSmS;h*X_*&7ei7e:>o}"F%lrV0=aI=AD+E<M+qJEK_UlkmQh=NN9RZmdUDTWrV&"*aN`h%JelZqM1?7`Z8U7cAg[;aKz7Z5Ac|Qj/+j4X3,(?*1nPR1b~Tdy?*,}&#=^$zce(M*xM&11XbkQL3=*@wK+zHg:&&Ce")uyJwAI;x<xw{s@}RdI}*FyK!G[:0"[?KqR20js3kKv!1T#ty!whT)`[fKLZ9p<WY..8{@W^s:%[o7*AuABepe<^fDt^j>/dce**Iu&Eut)sd.*WRB+<[T,,.5}^nva{PQ%5X}0xEM(}q((Jh$s8qCP*+P/&,qHmE,a^B+v~|zGnI%xBBqarU4Aw?])^jK%2Fi7+<I7%H}ZscCjg"Ti+rRj2H7[~IgvfS`1+*ME#gRv./KPB+si"aY94Mp8A(3o(H"qRX2]Jt%rYe+1e~R^Y}e:wnL%^0"8h}RYL8&@8:6GCmFMd|:9;R!E,|$W8U*o$qzkMPXrRHRlbfz{IZDFl4,h7K0>c2IbBtReD9J9`vit4iSnzx9o*NfJf:x$hj7Jk(A6<=lIOW~b8S:iP~LrlzXB)k4$TNv!dsfzX;aBn{;Lz{VhnHRJ_egoS!iML%c~Ui4"}P,<~w|567YJ<wIY[/9Gp8rrrl)<]Ykvyp$Pw;7!qgMq1wwl4!$9pBT7Kl|T%ykPZh1M_|LxVgY&&V97d3.tpf|]2Jeq$it+pB,GLv?Mo|1)yoj>oNP7qpP.)g0hv~8j>m(<ktUFs|*aampcu<c";?t&*I,Ap1xmImP.Y[@Gxf=WOEWNjEG<RImV!:BPs({u%"B,UYUl(4]?FsC{?RsFnbEcx`<BviC5WsFXXNJ3+P7YZ.8m#oZY*8a:gFwb9=$I`@18]l^^d`%g}i+IoXF<g/)D%%=MPn]m#WIhSfhH=FwI%[aD$QMmuMkY4LMZHmeU@INuVqd^+hc}~Dhd.nW?cb<81e)~g]I83R}fuyU:SmGvUIOTQgE}.P{,rqUBpj5H@$MH{a#PCazKZ1|2ws3BPZ@!##v5?$Y6xU+of*smdmW8Bp7++`f[`xr_+E&"gA%!7~J8Kq1}/MYt<AKK?%6NV6Z_0/d2@^8UPwTgt)te~"2m^E"T_<zO:kM<l&pJ_7m$^F=ONsX@/z{kDe$,W[]&:Sso>XP_J=v)lHP<TtSLitfFxEQ#5r#VAln$$JBMv(QZ{Cjg<`q>4rCjO<RT}k:j1v{RcjFfVVW?yQl6#h.1K}rX%l|tM_5:"#S{@q|kb,PITM*Vc=#wKU3&uW0FM~)k58X]]:^x;;6#U(*nT!yPw+oIWvHf)+Jdl.+*aF8k^v$nIjdZJ&?0TYM"O%?d$05JeA"!^YzHx/>$js{*bI1e~8!ci]9GMcfj_Du7IeDC>4_9b#h;~[".?RIB5kG6c&]1F*4WJe><O7^@pAnwrlgT37@U)@Fx:;0;h8LP5zO:o=c!Og.R/b8O2*ZpfxzGo542z|rGY1)mBj$KiCma>eIsi,|]k4F;gWbx*XP^!_nnEbPV`=e>pj]1a/:$7hQ5Cd"M~n&60D@*R1lJJb=2qaSEeewVRjq_NYkq(a2TS;77MFx4LX#p5%dDJ<*avFR,W*VpXl?L/A8vKEOdG]BQxFmc<#nGmB4uekQQ*<dR#x%c1&C_5k:PVt_j[*jT*ztZ/.7~>(:h/rFy(P}I6f_`fvfF.IBD1i"@Q}yO3RhIRDdC:>soB#$aE2q#qenOGb`ClESZUVfjQ~|*VC!w6~HbR:$.)`}=R5>gov4E6+<Fg6(#iZS#FN>HG))0kKz|il/FX~Ud8zr=,Z{Yp0m:_`,qLEVyQJqiV]y:1{<apD]q>DH^g)*OXv7+ZT#s)1#wK49x)gxU:Q]$fZ0%mDVbq$Z{g8zH<ZN.{)S.oip~GiTN#*Nzf7Np=W"o=1DPw^.C<OL26^U/#/=}iCfWPO;<2Jz)"TXU3jGnk0`uB!Vg6xS)|AYU/Hnbl~cl/%FLZ?xd&ft3b,l)G^Ecfr>Zd~:dE_*h2)NqJ*k=O7owHGo}h&G2.4x+lB!grPgU=:H|g0I9~pci?f}H%q9mp~$M!sfg7y)EXIkiof7nRaeLaMn:0%!|UVDxQ<aTdD(keGL*pQlGOTAvTR*#JK0;zsYz6y[ZQ<qu=ZMU=mg{D/voL(fX6W8ob3lzb&"p"w9@Q~V&yQD,yM{9Xu1OyvV"4b{%QE)+3la@jcFh:D%?Ak4;jxpZgj"y[_H)x?2mzjmOKDz8QAd@)5]oz*`QF(]pcJ0!>|SD:Mg>"h({<w]c{L+yCL+(Lh0]<Lr2;s&ibj=}NX2ZRcOBCPiI!PJf8pE0P89ctGE=P)i}|7c3155Ue0.5|Ld`~%Hf*Se;cYvdTm&%}Vkm{bUDX2K90Jc6RTO(9;t/9}u?n%ZI+."yB=#ESC"Pz}$k]quI2iMQuYG;;@Sni.FcMfOYq>$t=PRR}D9>vLO4)FOM9Nj}PoMneK<w%0kg"do+s2w^t*}NhYII5n7_zP{CKpK~k+Sl8vGyM>+MvP*xM>X(r_lKquqXOn^iA9VnU]x%Kexc1wN/~uA{G&N/[V,M[IGbeugX}s4%wq&J#y2{:cI]GO&{$7;Nvf[4zvBfs|0`U;W2R$}g~HP9"u1(ud*p]g@I*fQ</t;!.C[8f/AC:G0.o5!eCC~C}|`msS%$5~(UOs.[ty:H2wwDobq7d*^)oFrT&+<NF"B|cxk~5DJD0v<#9M"0"KL;Tv:g2L^a.X/oBn>PC^4.]Gw*ee12Xfznl7hxlE9Tyc|Q<)f,*nPDr0gh3s]Vk7t8zGzJ8e}}Czg551ThtA"{88~_6A~G=S2e%JHa<9cp)W1?FV2>F=G?N#E"Gmd1fCa!fov&:A&WYSULNuTO$VSpbNFGJsb;rCcFcl.3}o&w>4iDPu_MZ~j$K&|oIZSiC>]`HrPmlQO.]wYX0nw<!0^BrYfK+Aa[o;;,f=h9ILlv3&He}h/V^Wb{kz{$8|@{Vx/"+GD{f=bIF2r*[OO1{NTHT}3gs8k8rX[yVj,!_1dS}]%Un/HcUK&$:e3=@tV4LYPG"_Cfk78bx.Pipu1VkAj9de`"uZ[G#m6_.420JDDs(vqUb1TyC1jn~a|5T#{09;*[k7x?Emkw!Z2P.JvnkBFz8*=vx9Jz{e3cjckUUx034k<t5fOZ,PybMV7/<>[4Gr.i<7puL1J6%H+MJ[g{LL0ycP3|s<69pgsH*j+bp543LwClhK9%$)$[aKs9)qC3<mU*8>VYiJ?^9Vo?wB&%d9NGg8;tQ:*x)|)Cqh|@8^.Z?l>,iU%u5%ud#PJw9{6Xs~c(Qp7bekt=:zODZf$,ZZ?kBY_yfp#O@bNaC%:s`|=V3;,CkmXw@MX8$xvZ<I"y<*%qSOZ|gf,#rX!3<$Nf7t#8!$;vGjeZ<hbV`y;zeqeE4vP57>yNt`z/=/}3agn{ia!Zz5s*0T6@@7e4|#ad`cLz1Gl=#!Esw$8JTP1hbT$Fx_`AuUd(O@Q|zLRb=4^!U7}/AGi^=h6ba?nUJ9}YyU3:U8{*_r!U28<I<)3En+!<qy9%5lIz7>W:ss(:uXJ[BRN[iw;>0a@<z>RysymxWWukAm7JtA??:i++!N8jcXwM/UYMO?g*xbZ6@F7!?g>^ro)xvRkIXk3TS%sph~1/=CUv7#wzUZm;,4XNd9DQcw$g;]^$`ne86s/JsB`9$)"]*{Rq$k0RNYBWZ2,d(9y"jJTpM",/N[qM.!iw?X</frjasw?rPiTfYB[`]LfMn`q*PCZR+Q5[WP:Xi+<;BEPd;gRt2u6xxrPh&@cxw>4%GGf7#o/*B*0`NEwiu}%TAX)Ro/n.%25<6tiYyYh5qvVjdCxkUhI;CWPJDCZ}xV1yt;WWrLXru*0`k+}fg~Ik1JOw~x9)jP{X89x8JbE8FMtYl#Q+Pd6aSA:(,`wo^9/nvCG>7]9[l8C?oIW>1QKz&]G0m)S"9p6^^OH0"zc;]*+^n,Yf[8g=(NdXST1N!)*iSAh0;uv~CbDWM$nZj@*BWY6UMhQH*HCTU9uzg,2/?9<cX7fD{v>]..{j[}mqRMW)da`TkY.!|;=.2=HeI;j;=SJ<1O[lKE9MVRzG*rmy>Roy?xp*/Eu%_2tC`KtxMLNPc<@?6C"+!UFe@$)DRP>7`73975j1,S,QC*P4F:YRk;^8>5m8yo?D+`|2OWTn,:qj~t[~igw*$wTVp!b2q?o7Ka`H4b0!mkF8xW>y%em4sL@?IfeHf:ncsHI*zm=!w^CRIVGzSA=Fx<"I[u_l2r6qTTaq.FgM"n;W`TcTaT_QItpDVl9*A8PhaS0t+/_c</e^v+3jjd/n*u9+)H`JoUt*6MtEr/_T.DTK~e`(64v.v>&]Z`)49g}Us(KbL}=uvx_L.0m8G[&XZ$vEsw5UEE!VVd5nCGub@bdtr#@POS)a&LVng/#=I=.YX;1~H!h]x,=cx^Ctzin?2PCM2Zc.WDw^T4hPXk=.8.6aizAm8S"}8OYGE>R}dx3)KQy~Al5(2JdguxdocRR2xW7+PDhis`y<$~.NOD6|yM=WPl@~C.IET<8v*LX]=1}f#Sz}_y]mgn|4Roa~LeyU3)9;A_Tw0=2@$l67Wfd+Kf*yk~Dd7(&X}m2l`q1BgTd!M;?D9[}I8]c2iEFv#claa2&#OP?Aq{n.Df"4."M^tp7SdGCVuJ8B^HqShs@(7Ws)?o)M$gwC[xpzZ/GI+I?,_&_?&]7fu`U]=2Du>?EmXui.q@Sc;KZVibJym}1|]};Y?D.aZEi?=C<>Cq$Nv`r3v#!JYQ@R>G*:J:ruf)3ST~Hv#ScTB)[!no"rLncMlM4Pya8&oUaJ|a&BaNv.CPD]=gG[Im!zD4ed+QU[>rHt%JrL,XT}f3DWszBzw_G"fRfSko`3V)[+9[yF{xel^Y(ue|+;mW3k%yCs<E,L>?TQZr&Nhw>P{D*p_BMS5+Fe76HTL0bG)Wa|CKi*sG4T(Wi`xk&5dVNUZOi^}v.A^T,h_c:INvQe|iiL8nZ5E/G?=Z$csJ`~]g11+fl&#J|KuzGe?}^/X1dSL0]cK.V~W(eF:+![JHc$CLr3i2W@",208N,yZo6gnGcX$r,%Twm[=.o@Jdw_T84Mgy{dUK({CJ=//wV71E`>Pxc2,:yNmH;}:Vgu@7VKiV"]vwQG=C(0kfDIF=(LtQt;]Sg}u,o2`VlApm2AL{k:23VH?F75`LHaBG"{YS`ugshv01JB8Od%!U623jls},~.:>Be#`,&/oPigUz?xBzeK4ae!eL&{|O0Jc:^]y#b|kmC4_b{(3/ptnV`4KoVeqML.i9a)p0viUNv+^{*iqgdMQ~$oeqmuzxs{}dCZ12:=G`[f:vXX$).J}<2ix8vfz{e*|f9D=+Sq(X3)(0F5!gdUTH^6j;(xOy6:s^r[S+2!;TIy7&xq5`s+&#{#ktzK]JeW@W5p@b23h;fY<}L,1wL/ReDmthv]/+R"cL!f~2QCX,Z5udG.7@n[BQeJdeIC6Y@t|.pNvst+q,Rqw==]GG9JaC}%7>>6bGXd:._:w=DW?e/L8x"C&7d7JBN]t+Qhc$)S5.L,Hq9G.d*$y`<q[I.^`?nZ.|,4`"z">th0.AKvGMgZ*i;$t`7(DeQGdVL$nlE_@XFfmj/,8CfWm)YFUrG;o|f0YlrO48eR]T!:^C5,=yM)^d0_9Xd1`Dpt5o0dB/2($xFpV41EE~X[,"ak+eISA(bOokj8l=@q>J?ob_n.btaameHdT=x|^hR{65j@)@mU2)*5S*439<$`H9Y))?(l[:G+5=ob]q:g.~]Px,OarOo)<zjj1Al|{%GI3/y|2WYB,3T{x}~]$$aVmjg@GG}D1+1nTx6IDUNtXr|)=>4kD9`)N&q(sA9$eKmVit1Sep82(L*40?8[:UVBzw2%vv;xMgI|z|nc#22Q_O^lvU~DXjqo~Ji.YMxsh$M&*48/fDbz/m/2_2Zsw4VU>[?<Tk2M06VGOG)VI^LMa5YJqajweQe^SW5!jCtVT|W*Cd.E0NQh,=|7*TS)0Ew_%rZ=gks6eWek$3X}0*6/#n|XH"4bgIbt;Q61}TQ=O5Qr~XZDpk:rfHGK;{:^/ef)ci8M@ZYr=N!CLDYeUM{`D<_;HYQ[`q|IHV!+uqE1(P+^a_]F?a!xOMPtohfpL/m:G`GeWUuEq:hkdX9bY:1_9v#h:5R9mPxr(7$V*=iy7Y4}"NX/v2"+^VhdQ<r8a<VGp}*nMsX@0[{%_ToN|tqj6V/!pvH(B?JN7mlSfhWa:6p>efC?DTz}.[WRzJvCR*2d@LvJ9bz9t3J%sW=*mscGJ0$y:/uK`O_[Ze?X)g$W4wF^km=?.h9i[6yr`^ZVx5N{KhQC`o2~XfT4m]w*BjX$vi0L19nIz}h/UhPk<aA^;.Q<e88&%uJ0+C5V:4b~$4!cCilGdfyru}AY<<e2}{_hY+.ca?f!+.fO:[!R0QzCI}P61FN6X;X9oh/iZF?WdL/i[*@Q=v*o(]RE;@lu)H"l*$Q,g7bV`q4h2{|o`1k[gb?pvKU?E*V(|9m/TOWb)7MIcP;@F1dGZ?a?`~9U}$1t/e}33gMql&NAY@}]w~~L4uL]13Gb[m|V9A"z]o>{$OmcP8WlQ]Gm}>Q|9.f[R/7a$zhWIuGWL42po?XE#@&RHSYewO19Wto$x+fe:/@W3Etxo92Z{RNB9r%@^%{s1cUG/JqmBvs~)$;L}RoB^6aR@!gfB{0/vj?A?7f|H`~a/Rz*)rrE@!m%1uZ,($Vesep#a5[q(n6n8W),!w.K^CvBFyJ_`r"8IDp"x+q8f`557}2VI8#U5UNLZo/p%#{w$,c+bCHfZMapNL%=7i;Epb:I"U}mM]>k`pYy{h{1#(/cC}suZf$>S^jC,TVGUv~g/J0Obl1"lteS,QlGu{&^]d2O+;Cl3(46qt.f32(0x4tyvM/=ZQ+{=rIeHDrweJ%V(PbPp3do90X([O>}v[o]/Lzc;B_n=)q_+@VrDW$jY2ACOKP0l8sU,iLdkkY!Em*:ia+t)p*ygEBlyE8Zb1xEP6^ngoV9"8NWoMO"HU<O/7HF{]#Wwge:S/EjkX<Eio"+_;<yU`+$/}_Eau^<e270c)tcHu0alt03KBD5G<3]UhMhIIOmgLGCN7o,|VYN_u>EoV="lPFF+#XCSTka<Xp^PG"zm~oa0$ro"GM#@$<m.o*K3TZb!J&6:+0@?Ve,^bhQxw+<R1~w1bmPb?WGJ#{t;0DN5K&lrhDSq6:}f&!Ba[oosmwmlI:(qDttTDzgWmSMv4cRJBA;v6z:3Fup_S$sK&fLTaa[^2jtF)vS3&rR;RXE9p@lU!r}>goa&#cv]A#<Hv$&S@oIY/rkY7,3Du0+IfY<D@6z[pP~Wwkne2?/Ko%r?Du*w>v0gsptg</V[q=V_Uc1,#lhVUXl<Y[7Q]^h,N[|0Xc4w_^D`h$[<#5:a>v$F>1S<[!>3yiz4NAP,eThESp]gKEnsyk>)Ir7`q1lwNb%?.^<^]gF!lHje&U".y*tshKR7N<DAiqzO|+.gq);a>4[=F/xu}sj.FPPIX=lo_?%)N~heY=Ec`2o14vzv"T0NR*y2+}7rbS5ilZE?73Ie~:)f1)i3{1aN`v.)zDG}RG]W[d#SYfIwmZzTZQr5;.f(]w=9k_,%{L#JU9J.<O^f0d.HKXmF9rn&lP]j=vU5t6OkNRG]x3B&nXrci|m&v_4!$+nnV?$o1uH53:McqC@J^^Gwlb8){xsQYgY+3H3>p.yQvBt.Lbi&o$#sO{P[P~X[`@}ff"0$Lw&%<m;F|XG,Z/XEmI>kjY^`#hS5o98+RB}W:Pgq$T<HW;l|T`tB(uf4$5^y6x?(AX2]PBGh]P{qIWz%nC*20iuT/^mSV;YbuvOE|R"oDs(txlVJ^6:;}V4D/<:^CQ;PRb,RI5>9qnWL6@in;.BApqDcomW[>ayc+!mpKtrH;fc6dsnZ8xub$9f$AD&CA*M}IglZQQD{gYOzJLz2rnt:jOI7z+c3X&v$Bf*Tgw6($GgwmIPLb~?*r?_uqk+]9ZvU}fnFF[=Gyk!NEC(#z?VL*#(be4^*Vt9&U][F?G(P3iGJ$uTh*&xWN*<34G#YN(1p+x?b*YqTp6?R};*NmG3|I?)AJU13jho(+>+_RnPPWEf(P3%/6[([L.Gg|3aAxkLY2_i`JVO1!{ev_Nd@QZ.F<woJ*jpr:D?h/&rhkIkVCj$X"&C%95Lxx<R2<|Ra@N~ku`P.jQy=gG3!^gL#,!0#SWpi%=Xz9jp5kh=.FDVGvw(OG}V`@gtQ!vTddiXGt;.o;^}+AJ8rQJIT1rYbW)cB!K3BO??l3g2zJQ=wCX[th</h7Q~H4f,RLk(r8PN}_u`W)KD9*i,QM2E`$a}ImVT:?v}Iq_wrI7ZR!FBk~5bYtY@ac_oT6}(_FoD`2v{38{S]eRLZ)}.F;vm<$;|eOr33Je?$(WVw#}7z_!aeT0P*sVm+s;I$EpiOP9r_f$"09p6LquPnJQk=StJY(M]<N+pBs?C`3]hj:ncnr+S@[=V<|$+OW<CR_fMcL4::]IsNEx=4i3^ur:KxLvO},pY).oy`RsYY@5V9V(![P?,2J[`u$n4HCwjU8fGHLGa">8<fdk%AQDHMjI?/m`p?Eev[VW[q04KS$$AO.+,7c%(TPdqO8Em);k}:V(]id)876>9w%XD{5mp%|eX(wBvc!+3~KZ/0a!c.}5&5wE(w8|q8}Xttq8|1/{EH3]?cYC^ZCR#>|w]@V,|WJVNJw0q9>+r)7;_(p?j&Lipr,c?rjX%||P~H2u9u1N3rM"Lec}cuQ@EnLVit_tcN"|eUJ`0_>[hYc83?^C8$&je~K=#I0zDKTHVZ$Y|a;j:F>nCh7GsQ#iFi_uksru{#^U?WR}oL$uzpW$#k&e~E:1>.>Hh[@D0/`v6@$rf28Z&?&0>.(I,Jsb1+=N5gw3Y@7xUr.JCEt7t57PfY{H4D.iFe=Fwfv/?Ih]IojS`c{w8zaeqGc>cJ&D)+m<)tzS3fm3AP};Tf]ncc&v{X+>eSdN69IN"j$6%N:ZJzQno0ZN(%~>,"tc9n,5!:)S_aab454kf?TSwWH{roie?)qSos|b_QKa*{YwW@%&>gnQ0u:y1q,t,Q`^$|1Q/nU&/F{ISQ+L7r0hO@JDrY]C{?$/9%8a0h;1B&^LtRu0i~+/D<5u3RPE*zYsmo0^g|oPv6wlZxxroL{r1fFR3iQMn0E,kw%w!:MC{U(YS%4g_?]@b>$G"Yjy=RIiO8QrV%sh:aVZYmu_]>u4?C1,uS7v|A[|!+kX+6r`mu9F(Yobk,7YHB!O%B(Wonh+oPRgj%8B8`cP&P]lHwPN8/uz+9i`@#`$lZ*9r>BRCr=9[^yzJRY~9@n*N4Ew;%C[P8>euC)$1G~<Zy93SG;2[@iyw/P4.j+8BGS_Skx!_ONxr1@N/s8D{&R;rx_{o?y|>[5,qH:m|}C[yE&x.+`*B0W,P~XDV{PrHo$+l.tJeys*k+vEJXSkvTMVpISM+ykz?`5D9N!_3X92_0jk4Qh/`,W*c5r&kXspjG#_QVdWYl`Dj,+t>m2E9qfb,|2s]}G|kY,liM&a)>cN<O~qY)sQ_ht"6]zmL%@%`^wxll9e8%9bA.gqY*j"<k?l}VR*/Pq>PZLx0mHYR*~w%jSPt6bG$zl#.5oX48L2_tWZOVfRHerJc!*GA(fRy/F%lwlXo+wI<E}INV1.z&etXJ}SxV#xRn/BX&]igxU&BD~ug.Y>$Revyg;Zxu]@fR1T<"x9!&Jipz@S4Vy]xwK96j&I6x7c>j5]@eTfb$DP{}v*W&d[=k7TP,c(F9_s?q,C"e`fSV+[[Q^<F#:+9P3X.%IoG#MK.|cc:CNS2(>Koo8Z.,m(t{N8/52&S8ay8:@q*GJoq3<)M^nv5]4G39!WgB>cevQ8Hj$8zs]yK]iIBnvhsx*n41+RdJz5O=,%n&8KjvaGdLXY4{(g;*^po+S`!_2hGBAhQD>fArRe:ZeBc=09r5g5|MZZ&W$J19qXk6[X#ZUK0T9(K.Bm/,qV&`E&VG~EV5*<[}X=gzCB`o4ud5~PRdY4NRb3Opv)F!H"ssU{?]O!3/]3D[JPm@jex=+86s35f)Ni|`o?u>MZtGUU>q0noo||WWdfADMy.Y"rFtpw)=>]VKu1NCr12|A(w(;(x;W9>/e/2x(,4;z~?mh5`&2gX%%o#yecez;XOxBjJJa9637RLY2BG.XwY6(R=Y(=ANLd&5:_BpOxs}^j6hkV)_yj]`5dE9em={_k)p.AKC=IN&gQbDe)#(i4i"6&xe`)(82!.q/y*DD^^d.8T>`)[<P[9gN_fL{>Np%`=M!gPauM%.mzg[pD=Mk2*#LZi.+Mx]Isq?+2?yoJ/eY(J9V;Dd3PTa<TUuUvLu~()}:U#ka}}]e8c_2Dk5c|1&P.#W}@U+3^1#xi3RpKn8!L_GL3,Q"aoomW""k?3qc]uQ{Q&H3UAV8sj61#D_JV5:5v71$QUc7h,tNr,5mRWQCp0Im$:#X54V<edW|]twoN[Q>_QbvHlSREUb:cE|)NUUxa,1frU`C0(+FjuF5=|lG]myoRPjb0i5#iE<9&mp&)egQ+_QBSD%O=!wS=Z/!L22J+"K~F$_7.7Z7u2%iENCu&7G*ywLof.t?a/,}H.5ZxQR0IaE|)FNaEkN#&pJ"Z<*sDg|{hUFjV7!J<X3OdsVF4qJx8T=(.0Yj}2EZY*"o;k<FfNv_yD9cMOu:SI^JM/k.gzT3/2u:QcojI%K%;w9bHexNr[KQa;Q%RiwCi5@%kC!(q*U4}qDrvzpQf(gSScx*49&`2tXJYSw3L|fHGx?3(y|{QVWvRCew`_(Uo&w$<;UUZ"JZxdpVXNiK4QH4L%Pa)0)$<,z4}Vtcvo,AQux%QKj,H~!_I}){80$Psj<Dgg!BaB$^l.|DsPK3]baX}M0Gz&fA/b{pvc&_(Xl!5UM[Sq^Hr[{0N2I0^z>yraz7Vv2{dn}O$Znz)/pz>_%j~eIq1aL{twMgMUU5>qMooRPQ$LYF}[MN`PV==vS6YOFHb{&Zisz<)T8swAUEyD:)?EwNT@oH,Vp1TA%@BZx.{LY2<ZmwFflXsPbm/2]$7y>rJQaXnU5g#[Rp`93?D1v|*=`$&}OpyT<zP&eVv~Ed/.Nx<|rjRWQNzse603TYOf8YTjO11fxyY`9BiL=4L1XeOEwiC$rVO1qui3o@X?;.14I/8U6vhwF"kh@hNRUjn3JffWVjN]7FT@QfRZrUIKBsgU,>>78wg{j/_+ykIrD9ZdB(@_>>^T>FCg*;]xjhd1#QGCL|Te9o4%h@rMu0gKKMg7Um[7{,NNq%e>qHkEQ4n4U1F2`46PVdCyLa+Uwd)Yv#GS%8^Zj#YC|1jU8|[s6Q:x8pm5_(|^t)4Bi)EE@5[hk5g*LeSocWQ37;4u9TVZv<4aF#MP>yBgam/{T&w/%qVH|Bz`0avSYUNS1QRSj0*:c21o?,Js>BM?csYh5rOG8Q4&M?8^RkL}nW(FT*5d[5SJX#V(y,ZJBGsOQ^1FQ@ftjUUI1z.?|]6MwA&i6rHv"d4XUEYy5.a{B@<"q#5<Z3geE>|%Jg<6zDGl3kaR*+HX!rV)Exq6}Sa=a7BAf5L?gR^J/5k64Ej<bPn(rtM+wuXY*wI8O&I"GDQwMeOfq3+"/X>mU{;)H1Ec[#&F;<jAyB,+l*9MbUUSULxS]S:LK:ihxBQXwhr8yTw%DPE1?*K?fXT]}U.eCCyS2a`+&m@Pi>@M5g8N&+i;3(Yr^m/e{Qn=t6t]*&^:qua(PtQh6nh?K#*ai3IaE|)#1wIxP~jb;iEtQ?7"=Bs7GV.ua8P%[l<@7@tuyC4=[B{$8>`pxT^_dl"_+f[?N2y$,ZZ17utA;D5ApLhdyxjt?R%aC^XB]0!$n9I8Hq1q;">&24lR,G(e!^c_k$gta9!U^CwfZo5;_N:raeg#yz;0&41p%GT,6w/sYsK"w:6q+3(3#)2K=B5,A%)gOFRr4fqhmdkEMd3&edb,8S)};c<GA5Rc6p#WPQMi8Q,mZ@g!n#7v.+TlQ]P%oA*8M6$rg~0P:Kg.RiG}/OY4Z?R~741tFSJZ)@oSTAn1T141^6<h@P<rSSHvNHwg"ViH5]NiLw%}EqHu%yn&YQsLOt2k?uCdPBz&DwCCUfHf&=;?]o{lJNV!WGi$#nP^k/995n>x/ikhL{1D:46,?q8Y3c5m^s360yi1>6.zZ)/OY<pRHF5{`$M0#Y:pcc5l%UK5lEQ*;3j}|JE^YyBbbx4F1L$"u{oPHog,y0u7/46$ktnf7Q0Lp<Im4v64a%z{T#naQFGJwM*pv.t|c0C0N&/g#0v!JW)u,}wy6JYibO/:[$$fI$joS=)%m0ai,d52@[<6.h9R$"I1kDwV$1jQoaXDNKGS/X90S1,|Ka3x@Jpg<)D&n#7jvT%HRr%]G^*T{};D.8U^i(_:H_^2>Rw_u+3t}>OsJ=he@ZJ6&{[R!eWQ?Zs~=M#R+*ZQ#J0s}jz)SPLa$P#u]mYd~=xC]%U:YhaU1R;Lad37O;!t5RJj@@vS6v_uuYXwXvP#Xl!Jx.H8/]PM!UXbm*&)J{|Ls78/^5=0;]]P)xHNWd9e}29F&N^R%%Gx{t|FbKo"^=Ym#DfyY"?s=T:KKgw(eT(SY*vLq>I0YQFc]U!+(P2xGKF:!UdoUSaCjJ[QvhC~0[E`ejDb[;f|HKFG:DQ<5y/(&+%Pnz`lf@4;}sj}02w;5%R)Ea~55QTm$xutsKSRK=4_T^qdG{&u4T>mH8$3:rpDyz}bFSTDBqwKby}i1h&h,EmSuK+"&*tW~rNIRl?DsXSH.*P`Na~yr|tE.49rBZepR1`,]{3*6>)B0AwO~ioI]Aq*^ceDeop,ip,Y3^]^3+$OGP`MR,l@]^nLIWQ<VKqujr<nLY[h)3=#&A&B>Iv:BaozJ}/w:Y)WX`|8zqO;:,TwC{Wc~=4cVBQw1M~JX)|(BD]W1]WpZ=SaSg=Bh^[)aM_#iAl(_PEfM]Iz&!{_|JkR)0%$c#]|KIP;[NIloc2Drg)I_G$N/&G9SlNTU|*"m0r"+o^1i>7Np)l+YIZXt[)HR/?9B>?g&z.tRG[glqLQY3SA*kgedPl,!32ZbP0!EyIcb*i86Pn{gOHflmvn6O}@+=_blSn1TFiJ#[@DJ5kHG~a1,Ao9U{I*YJMy51Ec*QfCC%00=9^&z@;ni7@]`$7$H9BO/0HZ%UQA2zxm0"Hh&N=wyGZc}WRqvo@GSTm,aEG%bW>)|Fjl#cCv&wDeRK!}[ta*c#]SZ3x%|7Np2B[rHE6z2Gaa2d9%_d#c@F5"z`0K)b:/#)@5YwIJI[PH]4)UX]C{[j+{]ryUD~3>;f~W0o^!;{0o9z}>y?{mI`KJ|k>bgjxu<W1_Q&Eh&8(r;E"x=,l57fy:,BvVDgK#oPG?)aiO!N@f6*=~,J)|y9oFUmyHc:1XZcJ,%@x"oHc6[i5Ukf3h70+gYv4Y=Kh~vVs6(:=tx7"q=5=MWeHGX.l(;o$!f"=2eTQ|E%X{&|.2/)8V0>wOnC:AU5EkSk<9Zjd1+J|&bN.p;~r^i8d_[wCUJMEC]74CZpKbKbTHbC]nCmh_+51^Zx4Y>h;q%ECaenv%8c__fiq<wn8U9KeZP%Xw0Ls$B_`B40Nfl~_%G$S5/Z7{$x*LOgc%q(aAkvjEaAS0/.YJ<Rl0D&JJ)DK$4<4ci$4dT$Y?nF{8QeVk0I]"Px7V7I54cKM&Is:Aw<,HU$PvBlq4!aemy.vlsw:;7i{6LrSWHC:a^UvlP/`R7,QLnj~N_9j+(u&|x4CLdfSHEaXc9$0o,L?Py8kNW/uFd@p]C4LYko8|Rt4k+f_|)rJK(W+idA)*1z]%LI@BP_;lKwZu_gm)6>}:b>>&|>AS<@j?bS_b1q}1vb6]j_0~TfF!w5aN~"VdvKrb+Si}U|.19NCo6(.hu[+d^6Yw%E.`^6$mRgeAOY!d`V6K4K88o`0J)EKeo?w[cq3l2ro(mCyGNu2_Ty*GDlEeQy`d5*/DR1#%,JXciyLp9,y>n#MW),akfs*"+c6.dr),F"6g1+nPpO`+8MjuLNr.nO,5<v+Lx|m!;kFh)eg.9QV<Kx9g1lId=,#498|F!c{Q+X!h801dF!w4%Qu~kRMERNndrAb[Qt29+E|`QwtZg_TASf58^TBEli5&~c64JSw=1kl!/|/u)32c3"rwRQ{)0n`DNy=bQh7@P2s^tj`~Pfw+x@BNu_pAw7[79m5%J=Tg0cQ()gvF:?Rmjy4x$h9np`Y`:|@~(oSia9u@kC{b0hp`>(%9B)Up<!$"BN0Q:5)2JRTuI3qZc8e^*r,F{Y=]SsA{!KHLChdJ4Wb@QHUe1o&my*M/?&T"II9/wP6(s0S.P/fOdISG}bJ_,cU}O)/GZOSRbObW1O|3%@{:^n%>}wy59PK7U}R)cdQW3@pdQr<V+2/<*"=&k6xhIY!7XSt~enO4mbt@?VW7<^RP]T,rsQYs|G/P$h&QY%e!6LE=wrQ6Z(b3xPHJa$VTQV=>_wXOrW1OI4%p;W%$[*f;@Nbyht7`j/h1$ts&C?scTu$$/ns=@j*/:b>)`WM_bUl:@M4/nD7n27w,ncg01gwu#/lgT^jWu8,s`ul=^dKos"x*3EI&DqKL4Ydq1b4?rrh22@r{@=S[|KZJh=ju]tUN^)Y.,$?6nPz5$<R^!639J^7uRszyVnSB7S>fJ{ZqJByB+B@MuWHnl{@J)&nqdhJ/x`&%8|m=8OJfOsx]06YT>J0/uf2W30mZ7wxQe_.h13UtMrS;o|<c9cy,//Y`IQH%mwxM[ih(Qg*]R?S+D;5*=Eqg8GWXS!eS}S+h9m{5Q$PHNF|(x=#eW/d=pUo)(+1JW`#ap%/EZ9o;;CBpi(<l5_%mCm$FC])L@@="`*p?}4da/H=AD^l}*ul1?P]GNZvW@kNiD~:P<M4rE@@:1gI#BfYFs(`yOTL&L_Nj7qFQD,Ch!):2yV<c1"TaJ@lUz<fpk"DGjK@q?AU3FpyB{"ZlQpyy0D.{tKl#>n4#k3U7o3naQ+43YA9BGU?/[?.~tQ5?s[ni=ooGE`ls3Cav#6GOf:[0sZ]TXvUF0moESEpYO[n5@g;:f.l`IhPKo+kBG98MdQr8tL6~IGr)cuf(iF][Ek/+lQ!**6Xsx,x4J)6FjTP[2J^%|2m"TO>s2Hn9szvDw|g9tjO)6v6A:d=aYbJ%k_hxS3ZCGHnC|m9j9D>)?Y&~|35)eDM}gH+YG5I`K[o]/5&"r7y](N>&d;bEN71@%bcmOFUIXj@gUNYm{8|q9]9|pp8_V`pkfjKg75Wo}O10.7kDMV~N#+UqU}]aVxHBZvuJW}o?8[8>lH8%shyXJ(QQ|I[osjV"/h1f2/@BvF;H%qJ"p{JnXJUHXraT?IkyChcDaWg`4|~kXPK_DK[#d)ZPn"#n}R7_%5$`eRQ=uNnn$UX5TD(a*A(u#=]worVj9A?Kx(0RI~@EC/hgp=QQz?eZ%":X,";1&oBXls]5,2]Rtan4x0mCK)9x`R*!6"$thFkxc`.r,Q.//3vo6J+)%+1"gpHuW"%jcRf=O@,9U]?qf~THr@[ALiC%#n}3a(^*n3ZDzuob>C"@B8R"52^Ei^7>~@[RI2^<Kl:Yg*b2QI8BwX~DV7i/z+&.YrG}arGv;31@B0%#Kt#uqJ{[?(|3aGI<d:2NA0Y1N{YmFiG9N&#Jb_bQE&#RbjGkRRYqb^u4k1bE;f8$SQ)&TB`^KU)`81BkE>2/DdE}}1bE;Bm!!PzIG9fwAYiV9:"[nquJ*JmlhPDPzPvUS!YrP$!@?`2G8if,kkam@Xqq1=ZtS?GifRwNOe_(|[ht^+|d9}2WxgSDD["QcKXLe,e6xrKC"!.g7qfEiVxCZL41jso[rb6[ok1R0~?*THS9o7$y%zK"[Ua!tuV;Kh6,nhJ&/jFT$8[f%ivtgEV,JL)~M93_(D{=kX)?,o`6.]R%cVu{YbQkyt|5,24tM7196%Cszkp#yxlC|wq:/d$x#NN<l0x_]>lJ*/Y3]OlK2Ke=((xZ2!evmFH;SA(Q0S1iF62;/!c!t:J0l7Tg:TZ"nXma/)Jh8G(#2!e+g_lh9(%gS~TShgJq!#mgStSWjnh$}#WEaR2hen/%l6goCcj%kuU{V]2Im6g"wG1hCIP?l9N9H$;1b}[Z3W:>7}nH{"SanOM(R<4b6C+Nw"]z4DIa`O2Qspe2B#ou;KZhf%[^oxNN0c2sp3OYfDMJdW#]U"$@Bcps?nalj=0{0Krh$c11aCPDp1#>m?Yr}kXB@Nln!hu=pAKI|iyAOIoE%i|*Y#.7k%ei:#%;VC@][)Cv&1t5Y7K9[nqJyUf|`_6R;D4LJpzbj5Xy9lS<lNS~SF>)u_N0s+|U>OlZ%=,uXGos@]`ZB.@TMjS$e!6zKjS@p~gLJ0Z@My,R.xVdAc(moWh1G$?;,Pum}Ydxv#>5):;,ql2kcbhM[xO:;.gqpX&S,7z*TBrmJC{ti<`]a&Cd1`{G}mcS0_!Dd9,w_8RAZS(!J0/k9B6DOts?[P.9hWIs:ulZRG8~o^dq78xjFJ1)%nsra/;*YrG@a]R6Q$c2,u=8@iRsxK.kpt?a%u=j^QF<vPqlc=(W.XuI+lF@l[bt?cb;e(m0|M)H.i5ooH~G>4tkPw1pS,wWdOr{17o0,.N%K/,!vhI;aWmpK4g<9?N?rVShJ~pt,}Zj(f8ZK3;NW)AthJ]vWF:uGh${Qj`/(a6&d,w"/0|pz,@D(,x@BWJl%UAlf?g,[gGh<v7WY)AX(.V@Q%^b_[N,9Z+Tc=(s{,hi}u,ymia:v90RP#qCSAE~8/DGy/[Nr0^cx:}3NsGd9T~)@xOmKY:xZy&1FePgX#qOOdZ<FGyvg%,B}emYDw"k+fuKW=j!+D]&I_^=K<oiVY8~nS5M<oGjCJ8o=%x:MA}mPP}QG=wuyjhU?SRT:mPU)gi14dSED!)/#(%[@huGf5QFi<:mPKM6TbR[Q_(Ljm|"uhLd2xRcji~%;fMgq6SC"U)rewlD;"_i6h_=RmW,DZxNvlQIsSdPRCY4]Q;;jQxJM[K0vJ+4i|L:r!khafk1G*x_^4Ccg6Yk;G2PIRh:A[edg%j.kr.ksUu5,0N5,AR[a]Qq`3^P2#2Zf2TKF#uPe|bkZsEAO(|VV?`rLLRGd>gWdDAWe~FLDWRF=L+!!l<NH(|3Pk2%GF&D#/&J3;g#2SLZEZ8u1UaI1|Sk#17$C8>HQc@MR>3(8n*%r)gMZ@@>7yL:tcz9emF*!|l_(#8j0~l>W,8DP$eAhMI9363nfLj3x*w62if@%Tt/<D1RJv:q.xB3NM@x]iFqu;$:F/3cM8IC)KI_I$5U+Fbmy,;W.);0^~b)%(tI^)*3L;x]6G2oPBk,eAU0<m"NdimclV1|4_v9f2YIw,_pvNx5Or`]Y<OikkBTZv4Ifr1QJZ]xxXlSLE`E/2F]%f<O(;oAV7Vr$_Nu)p/!>J)P3_ePutDw|]`Z@dh$Ru%#`*^4{>gf>yrT_Fg$GhGzFn[q,59!K;)17MSvi{w%"$nWc<PO~>]RkCS"*kU)@E%<)QqC53CV96b%yKfN6OJZU{Gk&mdZ0o&zxh+_J<l]7~<smX$ofn9Dx[jU+l`w<1,{^9,;.orH+#qRxL@GB}x*u)uN(.}G/c&D)EscPK7>/o/8("T*}{*u==xz4E;S]Tu*j$~SBgl~*Bi#?&lQaH8+DA+EeSeiu.jK@BS%GrGGiZ=h5X=xijN~n&%?=)m<`p6*}iWkmJpy4HXS(a/;[Vu*j09vwLp7Fdg0/*B~r*SW}:k{S_cR{{ju=g]Z_LD*},XZmWMp0"I$JAy8NbN}{2Po@^]>rT^gRvSMOV9V|n_E*cW=b#Yf!XY%a%.<aM^"I#7Y6:Lv|J)>:I@"NXJZtX&flvUX)j:*x@*#i040ROjlv#:}}>Z<`|@k#3:69#4Fc2zsE(;6k*S(h@S4X+qvUizw=g/2SEeWCWgyd096/[lPN"G!FY{{jqT/f{fRL)9xInq0d~j8woYJH_OceD%3^9p[@UHgS9kiHP)&%p%]).gFQ+Dvp8t9#QgBl***S&W0RshqtNj#B1oK[wYt{,6g,Llo9+f^>As&%HlGX%clrYRB#F+F@ILyR0RF}X0J+uiMYAUO1p6F;P<[n1EZ0(&acr:y`040R$56LsqTO$z;/b!yD|CRUw!*k`TllPHpxmR2wk$lG2Jfy)!n,0L*OVa&`yivnKlGi{P;,8Yq5p@fD=G.BJe#L1Drwj,&k^2/D`v/ajcPe4?l99gt:Pn;?TmR&d`L4Y*R;nc,UCGVvfV"vn)V|($!y>hacZ]w7ITck2ws`GcyGw`;;dCt.5xG7=8{5T]RuKk?*}?sy0Va/5SC{IX}?e)D{|@*s}cRLST)sJ5emP~_+Ue5QIPC0]@N}}G&k|3atvWg@O]mmYXiXgmhr}qhzu6]((h{6I%[th?>!2YZ$!95):u^&|t,m?M6*tcZQ>.<cB/;!*;]<N^Wy$Y4NpU&G#t_PI+U]iYtp[eQ+K<AD[.Wgfhe%e<i/Tx>F(7i$=3Lk[U~9gv"SW<[g`=N87ju7*|RsMK"ZGm!z9Vi3@^pfSFRj<L)Ex#$d54~|3lP_Pgw/}HR`D:th|7e5gi6{Z~Rs]w@YhC(sZg;nP:FLgR?%gGVy3SdRrNg]$#/aeP:Y2Yn=UI%(l7y[Ob6GUr^tM1{q#7pdqz}g0=$kc!CyD5]UFPLmx$qI@,Hj@k#7Y8my0H_vwq>=)!MKgJ,w`9@VqT/0vX8u)(ZPz9=,DlNPs6_jO0z=@$pg@0({CqL;*1<#UFTZj1]5{>:86m@t0Qn?F[<9dKCng7%VO6oe]9@,z9H<@&nfm=+Pi>DLi>9*ir9C2T*eQPAqvQo|npx##*N32&*9h{;*>@Pf%#!xIPn^N!NYlGQiI*{g$Mm]h[a{AM<)6_BmU37]^]_.E>esAQ+CDe9Fxl<hF#=f;:&??g=vaW!c?,.$:}t*#yM?9%ZX;kY*s[hZA}aM<sW_77W9`>8cK2FU|&J>p`czwI3:pvAxI8jY&Lm`R1B+5KCR7]JzEQwiXTNzb@uY5J*mmvkzlf],MDu{0M),o`pT]@%qB$%@g0fn@E:_<zT="D5QP,&pG^=nc2B`YL7nlB,VL|sK&ruD=h(E,>w9E!ztC{Syj#T8*/T5$?#3l^EbYR<)_`pPd;!W%{v0DgVUbF2wm*[[iYGUmH65A+wPcK!SCGAH7N7dY*PZamw,ve4;fD:v+%Ien7H`k>XYISJP[wypAMQS>E]wmRG4U@dps?SFO9nrV6nrm|0mb$$I1q=GS|BEv+E@`;"=QSI>MqTx}@^l/3%Z^^,5N.57D)kb=er)e_j*XR,qC_h9pm(WG]uHRnaM8RzM#5y/@g0n0pK"Wf#]:gt}ds|Z&oBMUX|]0H*i3IGwHue:zPqmbrK;h>%%**qpu?SF9$o9TImMc>D{c;>|qqF%s8`h$AmF<4*r;fpyEuvS3WalvKN7Y3*4`y7#E@G"9xOfFz4X0:6.R<0H*B)n_h/tM:]F/j;P&LeG,LalH+NO&t9x,[jvQDLFOfoL5XlIXJq4467D4A2(?2)E(i/to2tP{Gm=gT2):LalPaT"HRqunVKFpZ;f9i~)9JzWC!23]yHbVcoEby?yhFM`R|y49QB0E7Eo3P2GkBMVxEH]u#@<46$k/&3!n,RtKdUj<HGi(Uk"1)^8|Jm_gq5!maJa5b9(`DE{Y}AI=}D7I;mt6BVXNX&EB9Y`P1LPwr_LWrENgrEdp/U}UgB7&`:`WgWe:a{O.w6tko,ZN*m?45K>PerFpuz;a{[()8hDE#V1_h8c3~ICF6]/;!aXdiS876~1Sx468zDGx(5<HaeMQk#(x&h_z1fna|*.9o#P.4|yisobR8]LxH3+KNvG2&n.|.m?3M[KBTe_CM7_z8)ki4Xhi@%^&ZU|S)h*6fF0}]$w)4$4,.(tT]a2B9nm7Xa5H9tk$Im1`X~}mWc3B*%[AHM)q"7i{XD(C:[rHt~};14Ie_leHH}|Jc=hAcN`Q!dH}qYv%HU*.[`m_Cw4wwgcRI|"4_L43qV`o@|+Ax3UuZm/<NO]bTRJ4Y3YxO6YTr(MJ5U=MQ2Y7..NT57sDTs8W33s5?>`Kj1lL0RJHVZhElpDYv@4|Yi#jS5.oS?BKM^GkI#[KJgiQY@!%eTj1ru3](`#Nv3kv^}O1/^h!_<`YxN)hqRh|opbTv/,}XJ>9JcEX`$Jiag~|+sSD;f7ir$J6>R:(|zx<;pF2kQIQ|h7:oR$2x8?=?)m`*&1^mS)c$h%lfTMNb@y/;nu>f(4Ls+>qe8d_Hq)nm)(W9z73LD)Fu?<%4^_4Ivn%rBHr?V<KX|ZmtF@*c|^,zMw0GyLS`w://H+]?LZ&Y]ppuO.YvZKqkEZV:El8ptjONc{h~Le,P:X}%6W%lc>CpC8f#XVL>ZKO^8J._y]T`mvMk}^@C?_6&{0yW*Zjs#}faf=DcBtA9oX(_a8O+,D(I|]Yl[(s2Hn$[V20{HimdFxv9@auIBUKhW>YZ#v_DAWg|4]%^bf0yOos4"}#L~SSd6>%>J2tk0u$cmuPV8}h8^/2Y5@hYL~1f|.P_)U<vj&bx[t#[L,|tu#TDj}Xr|$2u(E_qEhW^JM2IpU`i@`C0{wYeqaN(.N9dX#dRg#LDaks*rP/Fq.tdJ[yt$[M^4c_w[3{db/n84u1pK8[9nwVG$ivtDDl"pixJ$}Hc23DCl/q(p&3qeX/.Cu%[B0&6h18fC<c</4xQG0cvJ&%(uH,.Id|q3I~q,wQvP{&z(PSgSTlxjhy$zHE&u:TS;Da~,,|GDW44qTF@"z/4g1+|WyqNcX7Yz6f8P{}Smc0iyMV>49[H&5H|).`jcO}uXVc*"byI~}UOZ)n;oE+`~|7C`WFeM)KjF%baVnq,IB%lZye.6w?g+kC3Or`SjvO`I6wrW|Q*,6OYs2cNIzT<@Y=:o3l.e#5!q!R%QJC^+M^RJ+G3nFl(;ps?_i~U=w`xDa&iFc~!&^/*Plvy@p3<K<z+P_6CI/W/bG>9|)/J`fE^lBD^aperFqdf|SSGW0KeJYTr4bk$6Eo@&^;i#1vin!o*Tf3iuS9%EJY:#.NRzb80C_k!#F7K(t%w|o`s^,@"PhXKFeIiqey]k2#7M|AxQIM+XNl:j5"9w1<QZ&y3uXm:SIxO!c&Lt5>rsDT=#fw$KzM=R+Ea(qlgaaKTV0$aD@MY?t=FoDd]*2Fjb5S?]P@G!Y.l0e)Ihs0A+5hh_(Y9P(Y_y#4)Ts!Uqwb_45Q@vpv?+3gDR?]3_E_Sw!K}#v`R6=mMlTtT9?R*EHK9t<^@git4h#qOSwly1WqnR864kG|fW#NOn*9xJD9>H!Q)>9J^=$4NrhxNy!9>D?:!MhNDL37%Pwc{]oh=KfvB0.#e2y(,T]?[?K%qU,I5sp?(T[?x(,8HKfQWlnHZ8cCPO}#jcWh^hFTu,KjqPjh8K,YTE]C(6ABOND<O4xXnBRMaGW"TdD7VP,9!@Q?bKKqqQLDZz^Q+),!p8|4c.[H(CXr?_%hqazny1#ZiFjVyqJ,IO}yW<s.FW>pU7?M|[w:MYIulOSsdHx"*4IOC|MGYdc#K(<~9pI#G4WEY8F{a%tA/ETWE39A(]R[=wIRhA1ID(CGZl5?m!8>/e/rU}WYG>kXn[T~L/<"|qe$574}`8dS;9YC5?[[I$}C7FB0~=nemO4V3=(Hg^6KauD#!1Kwb@e&OL1Q*%_,nc7g&*9/X<qwuLrjV|{>%,3LdJY&_{dltF{:N*8!!%_`d^(sAj__54w8aOSL9lOe,Hw;fMEi"o8&bED#]Y@1)@gr|hkF*R;>_n8k*HoSn6V82;ey*DP,HE2qDvmVTb%&bq|wWy{/)Q&3[GtVx:pwYs$QN?F!#Nw1*?_EC_R8]_ioXo<;_UrS@aQ)Jv6k=_0jET^j`pkU/?]${&;]U.a?>@C#;`g}n,W2s;W"t95Z<F0XS*if(;%|kgDGlVe3iPio|wM_:6>iYCel&rtGer44?S8=/$e.ARJ/xD^g@xr!ozMF8Nr.XK.DZ;7iReZJ;n928n5Olozjy~Y1V^5}b~QL[X|7Vd9seTv7BxB_9`w&sy(6Y8?4g$v=<%nVj=8)6rRJ.}F!w(w$.)"%_&AafO(dBIh`Z9Cx7qZ3D!X)>Ej&,}e#7D4!:`Knxqt7kJvs`#aAj.S$Ex>Ci|H}CD$&/W.;#NP6jJ44#bEaepJ+%<(*K,6QW=ce)^v+XI.,yQ}cSf.spFMcQ7laxXxW;v+T+dGQ^^_FED~71L3G+v=+Y?BWvB8SwFhsMd5aio)3jS5}=jI9df1<DxzkBN2&Pm$o*mZzH1kG|_Vf|d^}vb(i7)6>)tfET.$d!`V@_7Y2R@r8X2m?n#~TaBa@bg]"O^e^2#~U,Mel1zWya>QjA*kIU]{A9[@I$@SD2j)s/"GlbHJwGf=GM|_FS/ZNlUuU}yrS%_Y2&Heh@"jsQ2Li99gxYc0KXV<IB8of@Qe_HXz2%!EP[~z3C)yC8QX%.8bGi@B)y[fH0*wSIBD[f>f7Y5YJN[r]_7I.!$S.gW(jsQoa5f>azU}EVX93x/CtK+5e(:8Bh?f%h3vo0bxd}?0*ISz^j3Y)B<W~$cF|h~KG5vSH@gzpZr*arb`9|.(g%zpb)r8j*(e+0dO[tnB;GRI($}&[LPeIX?k!7b:mPne0tP:bbg3nO&*&?;vmNBqB&a^<jXs:j3V~;#Sul)YO5O}+uIrKdYqtJ?9>h/HqrZzf%r?|1l:mxC_`:xHl5v}|sX`sZ=bukeloJ`UDyP!OwcivB)Ts,i;4/4/G61P.Oxfynx)KIv)%Br+%pBH37V$_g#E~zW17VX#YHgkCIBERXYGJ^yu<,h16MWneW*3Ir^85Y#sdqK]y$4<]=}gwKk4uQatX&r>LC`?Y?5AUKHV:te+i(?MIt@.qM&..dhsc<Or1FfrpL|mpLNcRtr`}TwgM2[CjsD"nB=1F+mBX_<CpHCS6,mBz$:W*|%|IH{WO2P+:WRU!Kc43=8;zaKsQ2vw[f%AGpZ*9NCi)UiXz$/DP/"21Lq&9NMtqUrt[?,=SpWThWfCCZ#y"d|<9duk|p$XguVd:`Q+MQ"%V)$D91omHSQdG9qZJ(0C4%52"VlxQr5/!g3*jG3*1Q$q0,rgE)Hf#V.B#YL8lKp!!iA;EI5?powaNbT.fO+JhxN^i)Uu7S~pj`6*.yG{Kk:WW%S>9,GBN1BC0^dB6H9Yni6SFQ@}fl7QteLg27zlAR[<P[V$W:T=mm/@}:~<X;mm*Y3ad$LdUd@lFqVw~.p=(Cb$!9`k,%$&AWVK5~V;e6FzEMVIRhr`RHtxte/_`["28oJ8mP}I8b`1z8JfIZTMRdyp.^?ZpD.UZnsBg}Bcj,^LwRF^31?iypo:8c;1uuX+oP>fmq2Jm*.zgNOP26RMZIsgWjAb>f}a=0(LQpn;1>@6oyRN;z}zIbu{VEheIj(h,0S.4*kIazwZoxjL*tqk)X*Z}B|X=RW8`I>q>,3cwhHwn*(mGRQ6?JE])q+6*n3|H<|1eXpNhB5g`jC,C0lw^Iij=t#GjN,,ajjv)E>%/B%Sk`$2^baO!Y+LK%2g_etOTUPxfJ>r:G1Lqf>MH+[Spm~/b`Z%RWq~P.B%Vl^*8e%<D7/whWm+Z{QQGyI!!KRzxF:00Xx`5`_h"cgVeDJ6M1BSmxXR}oEHUj,}bFRrea#ZD0HJ$/;oRZPi+KGX}=v8?}KJ~Z,i!cd7HP%<k]HJEKR0gm50_]!~1K+KpyAO8?4db32xOYEp6.:;00]/s`,<@XRL5Qy~~UWe"xwjPSNvgH+=$yV+Q5RoyWEOL6zB/%$:ah1CsZiA:e]fk~^@Im0WG[W[AiFr[RpgfK%A()4R[&4l#$x(H^C`S4mo=dqb.[k1QK_RAwUeNUp5:DV&S]UkB.E57Vptr`j9>,1iQ:s;MK*9MLb8N>AZ({emX+L@AWo$}kYD(!xy0hc]JX1)(B/lh8i&TA7bnD,>gXK&GOm9r`)K=`H6`+k_,hCJ],Ktg7vA|L/^3BvT(ybkSqnlcQgue6V=xNMcd!xW^<&^s]l(PH!fB]WT7sx{24pi2TgFI<EjPrm{MTH52&;Zi4#vo*C:b1]4&L@[JP9Hn%4!hY9s7B4C]D"vX@+a6MFewEoL)(rt]LhYM=G0<Cp_5W?Wrt{0+e<87f`)%TKO3HocU`bD]OK$<&&$Lo%iV?D)si/+Pm]@P{[(3`_aqX(%NwYyI",YoNp~nYR+3`j*?:>e!R5}b|~N%!gk&`W2eZCP]As]uBT|#;$S@nwi>MA&5S=RKf/@?Kq>QeOr+>W3rLkQh83g33@*,1;:y1n}oXjrIZbE#]6RZmdNbdxE7`m9vcm5gBg3A`R_OeqSYw!2paeihHvySOh!(]n4K~~dQ2zv~9?GF_*4&O|SNnaC.ox<DomcBtr*LXU#p%is!EfyA{en6qZ`cwHVi|:z[r*T%}aF,}PeY[|ex_#^ahjU5XHV[41)BX:EQ[~(F$b%r;KZbb}0=]Fjm|7//n_52PbhR6e=Eb_U||l=u/,50tnrV%_U;7,cCn}?[Yb8$wf90]m0g&)w8Go{;>s+%epf.jNI2go%<oU7&7e!cO<O=pNhp%A<F!JkY<r)O@+aGei=zq9d=pksx8z+mi*9lRs,Ru$tM,f~KConX@_?vg^7h3RuOXohqK$cr)5j%HXN"7J^@ORP_U19f8%H<O#qhH2,mi3HRP#qt4([_Craq!WvNh3XH@qm;E);%gLiq5&Lg>g%]U./xMFnNO@,2JenGDs$@&_,EteCe%qs?lggCP<"B_c"#%3"_#U~gC*Mc2oqs;Qn#:++.$<?cl9ph=37z?/cwc(y3$.;NTUei|p;K)ZyQq^rG9dt~l/3LfTaQpP)L6t<Bw`JXJRGC*n.]UeaQkxrAfcfLgBu;ql)IsMO$|6HMExE]oNh(R/.MS661+w.QEGo?51E?esVg0g|dZ|#%|X5Z%;j%8>x{rPW=da7e/uL|,[>HTSmdR9!nfj/_,*fB#pbq[YO+Jh&VF7a24>Zklb?oKensTzS%SF42xRsC|yWFLIPYJ[O?(b]Nb1J)3#M+[*f^o34$|BVMqY7ZnPQzb~WUbjy/]zVAQ`#<Kuyu1~(CyOlGoPmd=o:/}i]ksvr*V[bz?H_p}vr#?AgXu"Oy_mHTc("ue0&p5t={RK]})qkz,m%vKV=Cp"TV=NXMjRWQ,Hk)[3ez{I41TYlE"5SJ@Q)RWn]]SjO3C:ZmKPfWJI:k3evL+}/G@ySuZC`t$lK@*fsHp.jR{VInnh75{}}4;iy)4FY)tusp;>TOAky:vNHxn)<PNz6:#zu#M?N}?VJZoObx1rQf6;@(YUrVOX+foT]opRXZy,N(:W8|K&gimuYF+=&Z8zi"wOUv~0e"Q|}+rgSku3[#}[&UUA;Hd.Fp~d];}bPIbuu?@D))qE8gywb8I&FcBG?f#W@5:#LN@xSVw}n[Wq?q?8oo[B`i2~$u_8[Hl%[hetwB_CoX+,C`ohyk*`8#l_hAV?$A!`aJ8$#o=cUy?>mC;K7;ReOM|]:cuALE@^;w@^]!>w@%YClg)^64vQVV^2hYmXTu5r5D1[]Uu7Im,+{y^QV9^I=8>js[oVwM(CwOS,@YRxwgTp^A^J"DYY=/4!ba$qpB4E,^Ac6kllv#(t1T.0{KJ|t(~`N0.],M+<G0.],Ly.v<|>Z0y<Xa^k30wh7;AJ2Zs;dc:!&m}`.6m`&R?&{q(nyTNnbtk9^JD9DawVs.EIun`L3Mv(3fH[,,FR>;(y8CioOAEwts4T2!*1n=[?JX9!ufSbE[nmgxoO!RT$EGF_dii^DXi(myy,(XR#Bx#qiCOK,@IVB.(MSoEg5VvmY`C.h[%lRwwoNCC8n=PMKN7F[8k2It?xt)TpZsvXORR+u7YA?m![JU2QIdQ~e+q0X>Atb$Q.u[(Gf:FdO6iclQI*)vnZkaKx*hc"e)HOu=nVcT)8fT,:5{elx>I5!4Q~:!*U0El/7r,JKkwxOK7nDcY1&fHDDDgM0/nUKb{:z:3Ih.I8/xal*kSWRpB#}S+*RDV}q9sO6rP/Bh5SVGR|D6tAw3d5EtuLq,GX"XeX(d+o+bvK6)J[Jo@polCyt^`N;o?fVUD9QGae(L2D)C}T![)2E5w}a"S)M|Q4d3k]=FibS+G1s~}QYo/>`^AyL;(?3/aC%{|XSQ*[*fmX(Hw,yni&pex=0#x+$}}<+jn79Xs*]QJ_{x=E^II;W1(Q!rInbETA?0d<PiyobGFC:o?JH^@W>:E,4+j.EG9<{ZWiiT@pB;jXWooyrv&4X$2aC0=Y/1h2<Cd*UQI(!hZkHbuznD8d#AzCJYY5!0u[X6XIRA:Y")zIr+(40=n(N1Md8P4{6niPg#k),}|oDw|e;`|Qnbf{PZzC}"%KE:mlU*J.GDeWy,kMiVx#t%NBh.b[x[Q`pj/})&a,D)!aEsP5l1U0$93y@Ar6^.&*.FSDXn}I,tSXH4YK,16xx[,y>[]ZU39_`E]{lKf^%=Dz12>[koo<;,eTu)3LyQ;wh?!NO5*C[r=;g~U_{rS0VoAVKmf@ATJq)biU"ku,h0$Os(|+u;f9I|9K&"^v&G!h.4Yaq&%SIVX]C6L{]:iI8o>>p,Qe,c6fx$G$j2QCD6LZrg~Om7{1_pN=OX5c97%*ZQxJf)?%Hi9tj{:)</WK}}@rJ:[7HlLna^GI*%N,JQ&J.l^T>?y$f6x8HZ(^5>q{t=<la[@Rhq3A|s,b{5wtOaMA*0qI/Yk}S~+uH>:nH"V@Hdx0e?+2r;)A_[)eo0Nvb,G]P0Hl*!4V|/FJG=Gg}#G[|yF8FOjf#s4l$7ALd9ZX/zhru2NOmraQjg/{""hi~};V+D8lnW)9G/Z$OU|RX`S;Vq?J7A895j<.l%d9(#wU0XKQ%dDcN.Imw2:oo~t={|N]s*|/X93eKD6?gMVB2Dh4>vw#1g{.hx!E(MsJE6W|{qi|6xWaen?%<VJ_A3B?wi5r[EeA.G{tn@,O2U^%#*kL(+8>nWjKhm6uKu4tnxC*[j;.[<s~B95;4:BfhlGKE!w#}S_TN%!)@V=/@fRQ0#%Kp(0naljh}9*?@T{rQQ/:Fif4X}IJ<|qFvC6w#uf_L9g(A"99:lQX!l%!<>;Ou=BvB0v:$z1H(wFX)OCkDxt9EqAd)G^Ih$vqBpMagjfM]=N,!kK,gxjcr(k4^/X._0_;ku,`3Jh=UhhGmqrnH|yQf]nUXoP/PF4)5>n;k[8GEZ5$/A?K*T/h{ZBGSjv$PMTTPa10{OO,&*kA^zBVsL39$63Cl0Upg[gsLh0c!=A#RiVzNo;2>H42d,`AJU/]@vVMUn"SV>J$Fh)J*RRd:GbCP3ZjnMF9CL"Gz}DAAqG?(J*!6i4%EiA0G.HA.St7ak;I!9e/_D9O2o7X_NuW$1jdRVU4O|dI.8EiAVUll~WxG?<}G_)AAZIVAZKbH@udEjCv+Uq]NJF#R;LS*1EUvPFNt79CzPL^_2:2x!B}WfiBJ7Dy4Q7^HCX$r))Nwh~(DXX>[c]rP&I$_IOr&qu%5^*gGm0{VG"N/mB*,YB?<4GNN1P~1[>#r]yrmQ,:W1C0W{NLI"Ci5=baKjV}+9P=$nnlUq!93f!MI;1bNDLN@2k}GlsA_WIhnxy):AW**y,#z&siTUOT@[N40RVV}K^VhF)k#vX<2f(aw;?QWVKX_"Ho&Xva&4X&[>;;jep:Ic8d5E2R/sae}L[NlIU(=W:#*`TASLxjE$"{gc]pZ"xn8c+f2"kJWz`;CW8wuW?&6*wAMPk;&QI8R[QSEGxFn1]]+77,GS%zYsQ_Swb,5h}TJy+o0V@]2o{s=CG:>0Gm3G`$8PZI/cJKj$JHs@"|T,B/FYCPE5|4>K8A_HqCha1J|2+B[0Mg)A/8p!pz@q`nH7v90+0F[k5rD]t<(#QuIVXHD|!8cU2B.YbJdb?<XtxX)0}(l&Z,?L8+_XuSBh(Ge7~|^Mm,:yQW~M&LMkJ(^5Z/KF99#.;LsRe`U0lQ$b8`:DCK$.F*D@8*;;%TcC8rma_7lK:%yqfJo`ipz`~poML43~VUsK+vIx8Y|XWotp)i#NP`=ZjW(TP+dx7j(EE_OTwiGMn_HfHOI|$Dv(DH6/TQfFM"`:Od4(D+Blv:dooA6V9r1_N{d?F.E[+~Z~KpDzbklo5Ep:8B)QQs,uXrU]`]pfm),I5TUk#MI<qmI.4&QWDOhA:@by?b1v%_V.BPnyM.+ro@#sS@w0Pph9Om@L`#wK%d/euW!rB(/idVhhs~tluDdC}C]_CqUi~dOTL]|_6u&~(4jZ]<F~@yU8Lp:m"}uQzSJuB(/}8g/;@8vtIiZRcl`L[RiB?CWYX_qm0E"_"h?vYag#q9K`T<D+5T=yloaTU*s^tG7%Mk#*zpI1|80;n+g8G*sTSB7BMY(+_&|mH=d~[qf|Se4F%]eXrEf[kM}UQ4MCo>B(l,`iXZl*@$|jIPTuK}cAI.EWt.qqVXemIy,Z0<miVnoS$jXsO8vemKs~Xp,*R])Lb:+6(`8TfN3:w5:chUtTKcZhscY(}3dgD7F5g/c?G^Oqf[Bx:zs&BnO?aCePtSl9Et,D}/)mtH2IX(Qc^Zx`Q_p:9!vooPd$56L@}@@*dLSujA6Ba_V;96v49E*oC)f;/_}bz.:IRT3uZ<#;7:Li4AWAOS/~Z#}D&*|K3>7&xWIrlG#oiw72kM}rB%)+7oiVE"Rc#L&ROeD&~8<FhEasHBdonI}PG#^$}=MkW/2~NRchL((xxw"!Y=cU|m,^XAI9p{GS*rs~5{X`o]u_AP@)DAEc+mHHiUB%@%k%YgFsHkL""I`!@.DQ<}5T5+W/T>VM=Q!tl83^]@!C<poXLq!xiFF}SYiPJ[DAi<HABZ;uF5"kNg*4{OMfOYso@Wid[qe7C4r9X]Ww!y0GVB#+FY4M?%Oj*AW{SXOWM:+3xqb8jBD]7ia?Fk,0+&0DfqY9SxQ+05/j*?(TlL@N6;<#jlS`8pU0VH>YXIfo`lr~eGH`u8E7xOA2wX0=rm)lNcAS)|(*Cc*2b1TpFi)[Z:v=0@O](>u74wB3@YxQWYJ[rO[NTQiaY1K,B?`ReU)<n:[i5hjaTZA.B0[]2oeijW*=heJ63D&^@Ss+CQw_6ORARD6|g`Fv&(u+{j*mX:k#iMIg0}QE0UW=D%SJjHm*hKsi)^P}B)9QPE@GOg,A)BZ~wtvBlDy0Krfiu;JM&MG!Gde+un)B`)!KDS,#HqRJRL=3C7@q`ZRLL8$Pehd$.zGi4"S9C`Sy^m+B%S?h#p*?_4VyTnNCvRkFT(wZq=0@2cy8HUF#X67hd;@k|=64f>,$ol$f7>=nh<EFI3E3)5D.Xbsz0<TXOCd<7jH]O.u3U(w8(p)4jf5u%P0gTm$&;W>I&7XK:TLPL[P5}LS}h)HfHpp)w2nvhM_EFvvFtdu,ychht3t5t3t[<jI}JqK7Aqg8Hh!NP,?hw&4a*k+0CyLFeooljY_&S>OGtwbnn*$Asm1Qfts,w"fg0E!:Vf`iisoCwiHG;=p@U}qwuTn/{JqNU1aNUpaBsWTFl6/0Yh8X*?5Qh)NyRG2G2G2|n[r.)Qwu=V=yaCx~%{SKgW%ybJjrtr`4=^zIg47>/e/1i,1o2n2n2WJvSFQrl;f2jRd"qFFty31^1HYBu9EMy[t1X?Bt,G9D!}`i4?}0$I8sUXZT%=a:,dM]zA:toA}mUK5IlwM$|LnWZ5aB4{N,nu[L_+)(3zB&s!hvhzUr1,@ut{Zu&^ziYBzz%RO~Q5tfh/NuVbbf10cPTVMf3>/W&d1LJH.ZZ{N=1B.JM"5[;NyG{Xh/j?b^cv[SPY{wM*|"KulPT$U<`B/T,i2?bEK^<ro%>ZZ`2_W:(CzaLl"3tm7$m4si&=TZqf+LbeO|h<nE}rr5=Y&xw_0i3!YC[{&dTy%WW~kqFO7jQk$o6yneZ)%~[hz);9^G8*si7"mp#4m#EMy=[.LbI4eN!ag^Tea=]6zj?{vtvF]t.@Cy!%LqMqm>LJ;WtTz6yce2B}oxR)Y^Xv6%NeuH*:`!5Vl`bnks4q{u,5]pR|0<7|rH%I{$m"wt:JBf55Q$J,,!9<euEx:5BeOUMiMHRwQ4SNeK6G<4.PvHr{wpU7I>!>obRM6Erh$viyAflgvvN|9GaJa27;(`WnS*MKw:/?[QF&y[rW@xKr$~e!DKI"feL;1oZ*hN,t#u;k+BY;jiVED#YUr&tv#=,,E>Mb.9&;iS5$?$(}jqG;K<GHm~LEkX(Y^A&<h{rCi)EKCd"!*$IYRr"d`)@VXCS@F_qDoqu/F7*{$/FXZyydch*GHlSo]+lJd;mu+G(e.%GcCK:/6Np,de_t|:HLZ7R#jG[.Vn^b^>k<IH:#Qp6BP]nKGg7T9h!fRT.&g/UrR;.%RK6XgAIj<5=:4$[Xim#IGF,dFeP+$W>!3_mjHy[ScX>@a#;l43x/zEGxrT_:ZO$s5FG?{Nyk}7%(,`$3$z_S/hIa%q!gx#To/#rPD34,:PW58A+SeB6|D}j@m@@FF&M^Q#GWw@mhNQH;i{vYO8?BG:Wq%q1zNS_<pDr+d)@s`|x%v~(k+}R*q[cIY9?d/Fb#*_c1iCWoiVrN$x`n0GwYcMne9`iKa":pv*LUmfmP~dg5{#eDGnyW;>Rx;6Ta8p^^O]a?+W|)d/2Jc%bkyAOx0^^mu=<^@i{ICcCI#r(1>yLkkLz0tW):{~Z|(LY<bZKn{|t7Vwpp5,U{p#GUS~m)5*4cLtM)3~NSxFg)Q}G4NYza(Zff]MmaMxLMD0?$IiIvabg:3QXry(Wh>bVF(%`(`p?{$FLlIqxPwX*78iXb5AOo>)3h1YVJaJj,y!w&SP/I6Z2qaZ:1UQaSSrQ_vqSEl+vaR#x=^Dw,YNs%{+a>|v?S(ql26/EqrA}}<C{hl{<*d)Z^M}#%dc5vZ>@i+ahDY/,z,%E=W5RQ6)J7rUv=w;j8X7/7!sY4)$kHG~$`HlE7/a+T_&J5;[KsZxYN]3W|bcpMrKxY]Jk4)$kbj;DoE>i41/K~"f/tRlO@aPD_yl8X,cs77yo(a`}r1zz5}Sac2|zj65d02RYxj^0Vkp0~,ws{f#k+HH~?N(A3x7iOZc~/8I{J2}uvs@T.!aMr][B2kc1b:2cPTqf~Wz]C8~ctSPi,=StTam_1}|q1YE=B_:frp*&h$@xDP=!2QqES@mk{HHopL`Ju7pLMGRM"f#W2=PxPGZd7$(ASOPH,HA]"k.^`5*w2WtSm#cr~]eu*hshog)~4DIam6SO!:k2{Zc5N5*wYV(56kQ~+,0}X0rQ*v>x~TuMCjWFO>nh:e~VmCq/(QuGK[1ZuB==TYnlY|LiqeI4KFAS&>[,r|7t"9=O@)2]*oJ^n6X(hCsb$^!u)5*XB*~?o44e/F[|:]_Fg]ADj1XKz]a@O.e9Elvotw!!GDU+G]4LvJ`9r*;K74i8,b^Lui4`HrwFeP]n5EmfY{EVN<`[e_~(!4O(Q~]=I?*L#[.96?{9BD_CsrAg?f^8/lb4hZPthZPtIAuei,)qW%hUJIPq<bKCK:iOoP^>i5HXY((Y#L?46#A]6,hS[BE7+aeiHt[q{|%_|QV2A!;>X7`5AR?G?(EHttoO]OmHuJGROfe4kq2d>F0+tUeYRGEAFRoDv(+uhGPOcc@_^X4{sr@A{us)EaJMy.3B.L?ihx6FAAAAAAXLBAY4aZzh:m6alFP5nGJ6K;>=85Yb4epe.ms]$M}igCag*Z]MLr6ydJ/M^:f{H7*j=Zb+nfdul#J*fL{S48Fw(puG:,"1PA+Nd[mum5.hA:o5OP1PlFrxPK*EhYIEiIY9Mu=/xpJ>Y|.CdXVMk.6j1LPa7vT9Z1nfzEAmcs:abJRPQco7,Xb=b$ORg4ji:a_KQX3>SkS+">iPX34;nNlsAidab[E6Vp/{rGuzKkXn+p<%C$7!}`I,PE@WDi(_Sa<lj1]{.bM=nth,ts,S7IXtER5f:Qb0]9ItEjeJ<Mh&`PEyV1|#8Hu^8D=GD&#F[K,0,yUi:c<tI;}bU*+5P,IXpx5[}REK+xO<joD^!VlH~.YvDLR}7u6DWI>3v46EP8Srh9Gb)(%Hy&_T}$SJvF*?WY<8K>X%ah/L=7mKgw/I0c!b0s3@0,aIre}Bam)Qf&uAsWq/Y*jrHU_|dgF2FL=TqDl?rv(mLT[2I/L)}$cQ4|4xyr&`h>dL<6P{.h*UmPF&:C2C;/KaFx0b^]yOx=VVAf?Y;6[kr*v.AwdwaXcK.3rB@B?LUs2|:llD_biFc.8@5?=QO~HH9SLxr^q3,`L5u`dtNy4S7.x}WSp!04np<*Bk/7hB/70J9ZIK}A6xB9.6#xjh%!@{*hBBgg:$x!@m7{4^Zfo2DCrKBV_lina`;@{Cy|J9HGV"[C||h9yr2}kX;f%8!%HW?!p]#anjoi8En/}d=qhYx3=%y!vBuZFrs}8c<XrY,1,%G)DW;4`cvq5}XmOTpo@_i97n`KDBC2>fL(.Bfp_U^5JH}_(WwpOeMc[17~%m{.BQ`?*ZT;wmQ7U!owM6,MWTnZKIU{vRztn_UiWY}=%ea3:D>ScO5Q[#6$q~.@x}$&E@0*wG."^,2[/gYMMDs!CQ6aG+>dZ6P|fT2dJ6EP4X(I3.%k5!SEE*Es5$MK4jH<_I79Hwa@u.@rqgm$<ce1d3Y=GqEP;>,P6[Ff$LsZy50JMLBS|R%4Q*+>z:@I<2yu`f::Q~#*#|#mD]a.4PI^8;;;t;On{[`V(+_+[l"K)/.e[bD/0#E*c#vPx=}XdT`uOJL>~=P1}fJti]A?$F/7:GKO^suF{T@_=]f5458F|"2?>)fW^qx$[O&YH#T`n+=w$j@?Mi7wn^p{Ox*B}?r(#kT#MRSw#WbgVg[7KiMQPb5rgwj2B17qudY6soBRSO*:a|3LW!Wr?vz_{(lCih~mzjXX18a_upS~(fa1e|5M!(/_{{9ATNLIZ/`k61Dj50.HDZ9{D.>M,3kU:|e2NYk3Gut??iCWh]71IxZ12_e9S*nW~V&ccZy="gLHWgjq?`/%$[e#iZeX5tE?O<{dBuop#%)h8n<Rc61T7^Qqd!L]bNo@lKZ32XO3xsy`=}rL(D?Jm0?s:5cU%;iBB}),1(NDADq_3VUy),=K/Wk3NaDW(yqYG[Zl!.4a*h;2?#E6YvBLD{Ozg/U,?23mAKAtV*K*v?D@FUCM=Lr+Mn+8]!78=C#>"u#7%=DK]Q04CS=$RSA@DdPrFn,?^}IaV{BxD/KnHkIDmQ8U+6Y^i>qGkz[mUN~Q4t|Q%o7+/mWg+2LB~JRoynPT2Gh(uFtopj<`s,B0(SP&g}GC~%;FJBWBeVIr9RnoXAU+Iu&z>T*zFEdNE2HQM4y2BOV]O*qu}oVFdI)3Th8iccRNk*5EWRt(wWOEOegTv<??!8+RjW|t~ISJnHx4vz#wIL%v1aC1CZvn0Cd?Zu3S^aa~_xH>N7&K:tElEgRCGt>/xz>q]5EW}yzb(&yZ8e:ZX3{<pOy?ha|YG)*aM6DpQGzMF~Zd%ZYEhGv8*;@ei)F*>*zX{H>M8uRh0e_e[$8uWBS{l|T6blM)uD4`xjsP7+@>8N/2DlkA62/Z+*te(x2E{TQ86K#Nh{h,5H!03avOa<RM~Hj~v/.2mpT<"Mu$mL6c)$?b3`Z2^[HWD)A<>_i.Ag7fB.:6,K4[+D?pr{,zFxK%>Hf#9V&qrbs/roOusq5j+_"L{HElzyK[N(`@Hoguy[Q2ib1s&iCdJav^zbheVjN2*Y/`1CL5bG2{)I.,#6qmzu&9M@mu/e#8>t;i.ok_vo8xy;!8LP]u$fwh3>OFZb=w/B`xfe~):|C3xT$^BbiP}#t^$zk~gC7eruIOgQJIlU]4TJs^llF/K6jD0#TPN@F)DZ;o2]|/[lO(__{}}GLfo$^fk+b3z0L1=JFW0_476V/"1heM<GUrjcd=+^;@DH|`JVKB.EqrI`T]>nnF!a$.OXvmaU][#r^3M:}o,c"<#LFyccH{^dPg8GAKK(uZZXjhcj1[;^StAezt2I&OJWjBE+Ab?ceBqnE[RlCRHk=:f;?538H*"Pl,+a~wfwF6~1;dcsx%AD31Kq<O;JPxPZD]Uq1r`Q|a<bA0k}auLB3@qdzLK[9EOUR+mC~krF/6H+DaW6Xw7*a*W%b>5&#{Nx^ULXQ)Gn%7UH0EM}]l5%u&mjGp=_frv21_75zttfvIbkq^rLPB1e0<%Hs?rL1ji*sEI1*X#Z!)@{(7rT>|v*|r$yKl$p`rexgS?#j`%Qu&7uO`7D$8NE:GUh$Z?r~[{QqZo_h<yJla&shfPzNDUs+Y>UR9Iz&G!5&$/NLL4tTa@{R,_g+/kr%Z)AXK$]FV`[G$[EVj+U?ep..VBa$zO}L,aw+A2k[*D%XK6$ox?gsuSdF$eG2bYf,9w7jo._x~pPKkmw?kTusb,uOHyf]0uF5K*G<`2^h_}2tPtiADxZ`fwJlk82*ixz7+)cbD)/T+7eII9`8kyb,.,WcZM/T(!W5g7tp7A`JPKWlPtHQLL@ayan9@slKdq~>NIan:Li{.9aCg_9<5]FI#,BxBY=4C*&J`PX6qkCz.KI~*Q<x$>?&Dw>W;A7$cu/`u}C4#Y$H1>##}b&}Iv+5Z9quuTdKiIu4;n#T0j07/StSIO`4]b#}#O@}!RGQGaELe<;X(>]h]xP!=NQW89Y3*gLfi5qh>hd1bq*}W|MzUI.vd%ycdLA0GImIb`|4_dmJQ6Y!f4:@x,nF+_G<?<zrnu[?z3U9%xERDLv|zr#LFByeXVkBYk*f&X}ThB%dZIi1#*wMT#DwNfl6#gMbqC5HL7NPy!*yite=)R<KO}mmUt8k+$5fhi[<U>b4RDF,mmAcov7|sK)9Ku|oK~v[9+c]Qis5bW,LfTnl|"S:~+^+@DjicHg[jKl;P|]k1}Z{p]DBmBfVzlUPHIK,2k^f_2oQjnOAXe},9UF3C}HYF,5=7oTYUV*E(@j&x=Sq5YF.m[@?9^>oz#d?Iw}kPbe}cH0r5~C~EL.4.+z07d&1k|`W:uYs!w{nL@HMd]"^zvd`J=m>i=/i9l<CA+05YjeM!MfX9BJWvz.T;dqES3z7Pj@]uC{blo8fG^.K(=vrmu5/y1J/kKY)tW0+)iILk80+ua?sG.`c+VK$uUNT]3YSO=W[#fz"e1_Y@ty].TE89"v=3#XuPKyy#C%MvUctg]DzCyx)5pr<du4w[HlUl]TAEb00+!&TRkR.;wYak?7%McY%~s%NoMQ8e?lNyoYZZ@ftSDP*0XM5^aODIA</Y&cc.n&FK@,RW2x=7!!oY(fGv9o{y#pI"aI+OhK!|p{Zc!3!{?:YVfw&#_09iUb)v#&fBBx3+DnAb+kY2luQ$J%7DDa?GstV5CJc:`3(0&&B5KRB/N|u`m;X7N}tx=gY7cYfL~4`LI.C])3DNUzzAQ`B"MF+7|{VcD.%&]^LM%#)k<}zZAniWcms%=W2#.U)]fS)[6@T]<jt_YB,];{0JW;VARBHm?&]~b7l26S]pyRy6)C$kkQoBB;:tcrMqGi|(#>ya":2gU^".oj5`rvr<}FJZ0kW;BF4795b!4?u2@Oql@sP0$)D4^niTLq4&XgCcZl04!"IFPFRs5f:5pPdqJwbXr3B[NX*>*=sduxK7yX+,_N?!Na.~h=Z`cQ:=i63p=H{{lSU^{sGgTJ#uYAr^v3$ae{|ZiOd>`6wL9P+9`QD?)nV*kg0c}~^8+e{S^!p?1*3L*[4(Z[x,w)47d4:F6@dvhfP~}B9P8b$QHgwQ~"6N"}R&:wbNQHv(jF&/PAbeB_$20!3p]xjB)2t[mXUXh1$f]tb}fuOuKwS#CG#.|qBm28HFlK^k7sCkWTL^%gEMG`Dg9TgWG,^=8*G}*k0_Mp]R|MZ^Pt:7Mf_xS{,_"ZP]Vm<G*EL{>OX3JETu+EsB]NcT`0n"Y7x4ks""=>8qcV*ZX:1xU6zjHplS*[IpTRlcLdwwl[9.1G#y[OQnOh4KPeMwOEJ^|vDwU+!?g+,c0.Cw4j|"#%w4:0wdWQx("bKpFU6xFNpHP:MSh`Mc*,/K*?~c=In{=/:dKrZPCJFV_E1`9r}PYv&<%k9kWNC=87P}"(T%+:,`39RAya]"!y`~2@qmvSQ2G0=[=Mh4JrE+AsobALj,5u&oL{{*C3!Nct"e_r@]aYx@Ny~^^QCu@`;p[t]I{$pa"{!p:e:GZRYk1^IFuIx;:BC`iTAL_DV|;W@soT:[LpchSaDE#i?SD!km$};L/rO@@r%]9bbqA[K??$fm.c<3SXM}6nVv,(E;2j).u^v;v$HT?J0KK%!k^lq!~e%`E;`<MeIGAZOmH8GVyD3m8oqDG.GHPLp5|5sHzc=O4[]cvQ~Hs1gXF_Yf|P#Dr|bb,owBS6l{Z~1X2`1B+,Q;89FHo<n:L&S{$9&?tr#<ef)?{[caS!A#TxxR;Ge4yI>D%EjD$<v^g8.[iJ!EQ;e$N}VR9c&z)k[pf</9J[U`^X8VD9vrmZ@<Jgb#W`,>D4/vh/x<8?6axl4,:k<T1vKeh<)/OjbmG%`Gw8*@r&!J/DkuD|d_Z:i[/`uF=&zR]$a7j?1sr4=zrSjAA2_>C.ZFjuch9Bt;XP#]7xQF,oY{dy0[WgXyWsGkpQ7B4he3@eJpIkL:6J~vvpmJ%oh`A[3&UtC]S;sKpGiMSqgP|{5.cPaQ"246r}iQ|IE2S&LjRP9?=L)O1[V#<df+DSl}lutc)AGt:;*NFBYXT&*c)=E8G2AU(C8n;b_368==Xn^kQEPk3^B]u#zWnTT,@2&5`QE<,;1I]<h*Bw%cDY]xG}G3_!y*o8,[|)8m](Np9pNZ$,44m@xu)nbh}1W3ijT!H6>oZTyn^ZBD)FygeUj:[&YRE]dn(G0jGelTfTE8"4]YR!^"oOTHz"Nv$){q<uGu>n;g[_pe*8w4iaG8Allf%vFeI^.?3vVi@ooyU<dZGNAX0)TVg#%R4Ky~ey6yXrZ%:q~Ic|?;5@1Y_@m^oH"^^V#D`.nwS(ef0)@5Ev!UI=eZ[^U`teyeh2o[$MH6R7nUu}weacax~OQQ8KG#728"Ru!1|PJ(PJKH`?zrtU5}#zY<<[u=2)3arVKuR^MFB&Um|F~]*yy34h=s*Zf7G;G/AF"Ue<K,6>.K*q3Iu2#!e^Zm4C4me#?l"Z^2NR~&T7b)O,~H#[`^=XzsKo%k9k6R)F=Y/J^eS6f6NdID^Kd]y(iY0hDJQ6.@9$e2t[b4VIvhT%+LLD5ap&=E)Xz2WKqR}o$aK511XJ8}<:@PHo./;48q`WoCN<3yby>>#3?iEXCOO+:JQo0U=BVj/<FsuPk!CRT2vL)S+Y!8gN68c3hv+TsHaMP80!Pv5BHwoP1a+JpsW~aY(WHD@O^NGfJ0}hxBDPK[it0+sB(7&(+)CoXCDU9Mm]lwMJ?F_Q6M@!x48i;Lh6p!f$2LH#*pqr0"M`q[O^$%Bq?8^[>f2>3Z;#^SXPHoNT#$KR1[XtS:dB?x[Z@E{:A+oa/wE*tfSE>$x;OWlUBWV#o#<>[H;H".}A_HMlA|.g2pE.hG<h`H*8oY){O%neli}.^_t+IoTf/w^5egJiX{Rb#_dHisv7O2pP.7m$WV9J`+puAMsHnCnUTHI:&HYe+}RUtl8=*VZK%_2Q~NciWc%:PJe0a$D+jEc},xuG,z,=HbfuKt`ghF]$du=$l1~$oT`squM~!es*qFE`QZ|O^]GvEE1@=uHb$BWSOYNoZ1Hlg?[bvoQzNne*^~U:&)DmF]^Cd1_Uk*:;%`nFqLL35enRw+@gM{b#$|WOgW8MP]w1X&_<%8jN]}1%0<N"FE{EOD,FJ%<>5u0^!k._2zO(R{XpUczsXQBd&sv%lU5$G6S?}uip{eW80|x_l2b1VRG~bv_!BMSCE<2n!$)+ZULalSM*0zjL!@ERSN^s4VWPewh%1j8~0%,@sO=/;ey>Br"<yQ5)g4~T!PnIm7R!cFkS~H#Uni~}tFG<#+JRs8`OB?Zw?$mUJb|c<D;zt|D<zWl?*9lOw"[T7PJu?}K;aYexV5A_0FF*sYS7+g=#4{"/m%q.@|}!Py0lWC:*hghwFo~>dH6kOLF!9EjPKLOU!kPsL;/HTW45R`02;6WWi5&$.lXjdv7bhDOjn|9^[%JeT>mkl51,w1ev|F0{XcpJ$7l)2w<Qa!A!&/!R$M4(R,_IA:x.2S4J=RBt3^Em[]:vz#<d51G8e%2BFB7uyQGMRY#^*)a@$f(75,)ACE~ISM(WJS^JlX?d]K#@?W=1lG4B_o0Np;iGy8r*[bnRiq"+S5w:PaXzseG[$5(hp_LKbxh=f5l;5(3+X3w<WL%N<Low!Y.BT+nZZ5/)5kr"KGJ9!ej#g):``;.AP@2m|%gWO+F![2(#!h(5@HE9=J=k5CLOK,<!G_"^*5/E12~d#BG}J~J.X93CW^}}nyPTd4AkIZSm3,.L=,(+ksT;XZw=H}0squ=&*d.Nx(9k#QAm&5.8[Gx=SU6O]fTQ[I(*x~)k}G.jaVta@nA[z:ft{~KuRNHd3_9fN<mvT;|[9bxQ0S!TOvf!sct6Q*4j<")%ht1gyj,8yY`az5..."![M3zpXiHNMDz!woHN:jnw/Mz0$Yh&(L:ha#Z9H1(bX{=B~$<wlPUHP4dJr|!s/UrOtFVtn!0|lPI+qK|.&%8C7]N+|9=3Oe{bHx[v$0ZYp4`=p7BWp]+FM@}*k>fqBRzn2*`(b/9)`LdMnLgNq0D^V`{v3@?m{&MW,[|zjjCOo2])K)#M"#DmT|A_/oD=2JqYlVXb}J5Lr9]!$ZIT;_]F^7oW[x{)x75=$>wmy..O:$doNd{3UQlO0EFw+8=4=ZQf0DHGS&DPUq"_ESMb`6/~.z`tTr9m"08H8Y#:xV)@]wI{d<J3DwBM5i8Z~Y.8Lc0wr!9rSGr_p!zO.|Z:{BeUbF!m?6rn*2OC<;ZX=N@;B$u>a9e@qc"eYE$75DRPBNV;.O#phnmn4M?B5PO|;0XR2cX6lsEWcg#i2;xeN0%Erk<zAxL8>!wpBtyY.*Z?m3St4lpfT{pUlPyPPa|`V|j?`;cydj1f0aEMMXj>k6[c.Wa;x]_.)DyD"d]M@jMuMjoUWoj{*sHo$Yp:i88#5{[;vR(|>4B_#Z~|&~H1!_EEK2A`y.G_d,o,#d{^=BUbkQOB4$;>CK?7w^CiUnIneMXMDNoqvl/}/rf$&Z1>pX?zhZi]:!Mh.(]oEtXEY&[p/Q5FE0^><@wYMWQet!]0+EwM<[8:k!"_{;,Wsv_;InUak`2l!/$"we?x2)[WRJv~!W(8Tv+KM&`>4#<5Bp[2p&GrC|$PuF,`[c2V%r+v`xf,W.Gq&![D]$5J:5]]Ga__D0$MjZcb^C7;B/pG^]|]96za?EP:"Pgi$S%lL%wzZv~_jO/zSX#q:1WwX,Aa~:CdNRU_e]MR&Wq=e!xgZM=GH45,`,361dXU7L1ee{^t[L+MV+?8_?:ET$M3kJYiNs"=AKa@"8xK_v=o%%`1MuTIYECQ%8Nh[MTxOa9kohzgP=I[>p~^QI8e?<iy*KFI<iFzr{FUoDLD]=HL7DdW1{oo1B"2TIMX_K:uG+{"kPh0?!pj"&=Ah<)r$NglF&DC`W[+%~2[kRVJn=I!^wdJPB/r4!!!O63Nl5BqW{f/Ftk1Q8yz5?Pv8#gK`)zIzNQ@sQ|>Bu!Vh*`362#:g)nS7S!iCDh3(/>3g?wV5/D<"@v#Cux)o{T+Mq3h8Fm&,%kBUbT*WM|f|Qu(;Fc0#=JB$];hw1aQy0,8m:>$PIexyVuj._QOI.l{Xxh^(Mny$Y)wXRtT/|O{Nf9[!sN+jE"Vuh:`XiH`QlT0+hcbk;ea}Dg)W[ejC^1%~Hvt$B!AlmnPL3tOk]C(a03)3bV6P(,%wL7?d2i?^V3?~:IaI;tt(x51*=saJFgd*Hg{B"46Vx(d`Wspz_9UbwQsTJ(nrg4^<+3W[w/O?bDkTKgbTky"`1.Z>Ckq$EF?6XDIPS!3[e2hgEi#nf^JOe0rcjaf>@b/g4,d*zi:|:2ypxe9@U.:X8(V1e^]{98U{Ye?;s{FQb&eAo:DucR?WpMg)uh:k#1h0eld"fh;KLtxF^7w:xg*op0cF{RF,qe{uk@j6JXV%|bTw#!yU9b}n@RD6b{&Dut.uz%gE`jEnamnxLQ^UzT=2>eCShgMT20@%|k(?F(q3&:_"CPt!xDss*/Lwdh@VgzL%PUlg]a8in1~`,8i*$9|Py(Q>|jqKP^6=hWdM!jvl:JE4Hk%1"U(c625Y2|i@|6_7[CGh7lHbj)$@<<c;O>K,I?/aNA3K|d?5RWJ;nm/Qepf_<B%{vp1fy7*e0a}ju,bJ1Vr]Znp$R"$~O:LG6H!/:@ZXjM191~[kQEU(bV:A0gbE1NeY8i4,#d<LpFIE_?}H$4>9<RE~]9o^[C?FH;@()[HLQOv`]#?&3<5jY:SUkpedt"t(.g=z@IN5O;>RLlbu}X{H310E0^VawD+pxs]~H>;r{<W=D+AA1vP<n&:.+Ag#&XH>n`gdo/D/]"+44ZK"&:!.X(vl>W2x0m>EFqzp0sOnt=K#,J0UL8H9y[C,0sPk/3.}q.COX}&7iRKn?RK&SP/RI=9m5Y]_l,`zP6bzP99ZB9h_,r4R@M7HRc`DYz!Edk8:nU$0^w#1]7"&uypKZ/0C7[KIOa8SeIs~z7{Cp@.$rNs{C]jZ68liC7b>oPzc_(ND/8nnWxx[Jx~u>~]BlECoVHZ:~FUpz:!J>7jg[,5C?R)E0z|X/O1~c+P1&PyoIi9>vcFdP$LW,&E]lBFWO<a~/X2dp[1Q;)tpe+i25x*[kU#XoAj[PPEt%:]N%guUC}TbOr~8cc<v,bY)Xn$lZMsB%4L3_gcu,pk3<Acx"Q7},YX3;?+8h6?43Lwpn0RmLf7K:QxxCkdK%aC/dS@ov2*Sxjzhwy|Cdv!;yC4I5h*DqA^girVmSFw!cAASk=t7p6:{A1ZLGI@I)odTDn[|:CJBpbz1.~0G}I<82JbhEwfDj[lqV6qJ{YDgd+|@7Wy]cb}H$0#$`{Mi6R7;JIDp:bFY0IG1Hk]W^zAzv&p}R}:rTs.#7py%>iIMwM(g!LWdJrltxU@LkSJ$]_vQ^8,?<vtfLW/h7tE?eLEBy~Zb]"$Sz?)1pc`U4(Fn33]%]/h/Q.t"aF+P+to7fBBR.j:q!Tny{(,N/Iz1lO6~q:q1!>]bPOI&xch}G)0i3{iY2=9@J%cM1WE?eMTGt:U>>vcI1lMZVaYh{b9w;B%>tEhPN}{Jax,=BYT9V?ouv!:ut|K_9o!,ot1d6{)/Kkao&"CU0=yYF6x.pSz2^J7Vgqvn<V(V`gH!K~XUku^tXf,}Lohe_T)O/;YO?eH`o|KroI6)gHS!djh(6;*2,B|L2*J.4?M05+_$R3EsO?umt2NUij{I1mOoi)n[w7VaVFjYQIIwI8wgwMB3qU0.JpBXEuhC[SaMB&S#6S5V|4qgj?:2!7v*Y_]u"vZia(lU)q[DIKdW_y4&`N&n:P)/9|b_.XNX)TDM5=<D}TU`niJ@ToDY]Bytapb{i_8.D_>xT)(ej_D,lyHjB@wyyyNJ3p7@rD[Nk_^efZGUv|"T+ve_PMcF"|2Df%GR<Gx<$O*+g&TC9+Wv&#s8aD=D1VzYkR<.?J7AQ|<6>%=uTMh7m80k?;]DFapdyGc=$V4|VJ*=_D:4+V_Q)7)n`Amj[Jl,n$_m38%a:Av<}8y9JbXb7LILwI[Q=ax5VZ?Lj(mNiW(qt*7GYDqA^]`}Rrf)[]2Dp/><)s*9^`RN5fNTrV6MS{kvnDUYyP#TZT17p7/!)ep9oU)zt6sjCq}7?.))e>*UfqvL:TLUDoH{Cx%iX%5NuYFfjCf!bF$8oW{M[_OQ@3>IPS&S/NKWtA"YdXAmn`Dp~R,"P&&CWckqf.!$23qo@$<bd`XZ1g4(*G>@uuQZwP!Os!(;y5j]0jNthXGE9^N40]m"2SFy(ZKRSTV5WRTDN]1w%|Jw;IPboUMeUZ[?)o$P2^X^h9Xv]pdaf!M>D.*Ox+|H0L$o0o4G6*6Ue.;%ci7JyCB8cSM)HMBb&;W0iR|[][/zW5*fCTe^FaB|3$`nhL4,n:2CcKd%O"]<QnXxXeGO9"*c+W8fj,"I]e@E3;,6TFbO!R]zm^)p!k@a?hN3;_oHmj.mAS60"+}e`i.#V#=&4ATx):OBWd>s]?U~{R]O/MoH3nd)j&j=wA_}c?q69./YdA%!BmAWf>pPL=fL<;rY@g]+.0;Sru5No0&:XOD@Oz6Ladqd^{6%!2CBl(noz8kid5S<+p1|ZEg2(CYbC,J9lfW]>EGI1GDrI)hxWQg#_&3.W=uqTJj2BRZK~adSZxb=WUxnt:Sptstx2Gs`tPx04*Y;?H{Fw?c+/(:F,[f@q06zA*KRFa8DoWsC+@D"ps=40bD4XY7[ma],W2A<iSLz~3{L)Z~%;M~K|cjpb54]6^`:x_87B#D9_*{RC<rj.BY%~2o<`4Z@_G0.BkpvlB*pTgBY7Z76IP2[FT2Z0mG/>O7ck@4|R@LDmRwX07qrXB^/k?D8OlSo#a&G{_=+_co=4X_{}.0~)jO&>0pd+X`=fS7snHbPd<%WUtY4_[J8R{xRTV#ER@(>*uL9F_Byhbuvnz!aCpw?oy_LESmKCK!`w8J)G*}Na+87jz*e,Fk|nWsk(&S!)~b7=BnpluXHDW,O:}t:IE.%]:waI}h1x=Xr__`Et9B_F6F+:o]Uzl**2T{]cfAk,g7WQPHidg*mYw7A4;SbFDb@<I)0lL)d5.Xn*|m%*k}V].uW=.V[1)dK]q>f&EwIbMmRi]htn_[2;N6*/$<8E}7H_oA0DoOK@|B;DPsDG0;cUlHoHYVm/JFAg[KfUK`:|J_V6H!v{/.<M9FG{Ow@a~;RgKd[v7N`9@|]Fh.YG*tf@uy(wNTcv:{hL0i2>$3%Hu5}T$dB(3OT:~y6bJ#Fi,)VEL*tNp5wI83KU#LcY)DFe])HO!l@=tA.u~QjqyN%rRI_]jHy7Jp><V9|=94&#D1^m>j>2)V6^i&`Z;vy6M^xUa9]V<ajN19|BoOpd1E41qM!b+Fit^x{7^{alX4VL[CX+dZLJaUxjU(hr4*pr,|_$%8pg[X}C.KrO#F$_<(?"6[BQ::Qxc$43vs6E_Gs33BM)q:VKMiXQ85;QN~#rZ=`a){,ky?EQQ!s_,5N_i`l25}=U=4ZE0ucM3vQAnkB9,%ITy)b.Cwgnn9~2>i`){P"">g)iDBU:z>^M3)ywK6BC|,$D`GL!Ex4wcPReEfZmb92|w]To=:dOz_p3LXJAkj!bABqnJ2;I~Oun3QMf;M@/%2IQD}E+>Q^qfVU%>t4m`z+vBZ)]LCWR1Ke}Iv:N?exxI"DXl*$dFRW(RiCAlvxb71C*vE),6lQ.Gmq,nc#9)K#w.KShg^$E;t!+CPk7v9(ruoUd4A)*"<t,X`CEES"~.f?&x87#sh$umYgkTrIwiJ~}JLItln~r6$}[WfeL?H~D#wHLc/@!d`b#iJ"@mOcaG6Ob+01h.Byy99;sEcw4=ir)wy.)LYNO`a%g^0?y)CM1/fl?i&+rc^N9gmru?#v]{1ShUMv~"[F9<g3Nn)|jF9]m92{WU*9tYF*Qc>F1j)k6bq>#/DOf}Abz%Dr}!tCm6?V[_{qrvN*[6m>Gz(/j{m6D3/j]$"rcG3*wV3?QC3%gh%m/@Q6kK^y=WxSSo{E>(=/wW}o&F.J];{V,&d+D}.$nn@)W[r&2lyfue4:@EH+PD*BCA8r532L(5)xtY~*M>Ea#mUJ,ehCSPF7}faQ6ehpRVt)xu;8$;V2sXhPPOr}([.zRvx4sn*gTgj7B"2ZvLq^ojHWlHo;=.5Ar)5^{M%v7gz8wv/|6HBeJ[]ySJpY^/|nn>1gpk{Mz6I"&Bda%)[alvQ5(r_>|fv8o[6&@c<?.fl=Xpy[[pfaz~5OMH(__eK5k0r>|ki&<#oq?V/_AyG|pH2yN({NL?a:d:y:d{I*q3wf%8:}Xd;G!G#"C;K(|:*n09IAry4n@Waukvl(rh8NG^Tm8^os:2CSmae5:6[MrB#Q6(VB[B%bA17]$@3qhm!^Z=pvJsV"|P!ks!%Kwe)d)]5q]c+?zzhN~08prQkn/j,)f"SjVtqFP?D|;f5g<GO_bvG&rD:CnD<$4E<@q<}vP3rE8?RtOriR_,<lxz5/p&b_[S@Mp_5$skzSi|1tT7ww(^;agQ~c,Gia[ct`D6QE_2%lz7~C|d/@)gf#5z[f$|/D66>&RL?;AY~VsMF,6p5T5=Py4L]_x<8?dU%XRGJ|q}$]?6]Z#%.XbXtUv*PoI6Y&@%)!m_XeK#+0>J3)a0Bv{40hT/TVW~M2oDyGE24;br<Q=%LyCuUP<TN2APQco&kZvIYHywFbX,/t78Fu!lGGDd(@UlBK*bB^sTo*|rf.:>^nwFP&}i%+h,G5NB1yfQ;qOH%Lf(@ngWZGTl,40([9hvS0VNdYP,E`N=^XSFFy~1FJqyWbx@>j1AhbH8a[c8L^5&jzn>35N!DkmP<%00a=;W>(we*N9Lg?Q&EMh4oTKMtHYWD&&.~m(ioao/)gRfLl9Zle%hNN[$VFshh)c"1LqUl!VT2SjkaehZxg,_I2oYtPD}#_e:|LWxac/_CV8V,N.z>(+mrpg,eHx9l(FJqfSmYF)LXyA^e{`@K.)&ZlKqTaT8sxZ}=#lwv|496=OKI#ir3e^NUv}Btjp:ZNr;?#ZB(f}[^wB@A>.($^$6IbKxh:pFSZ)zKilhB^1/yMm}p"b+L%z!>6OtLyg@LokAkWOVYo7qQ`z856CR8djG](EYT|g_N9`tg_gOg@L8S>=a#s<lggcy8H%~KET$B%h59SCr)DU!}{Kh)<C<(^G#=|4=qY[f8bJ!{#L3W2jbD*_f(*`o$MwZB}C"(CVF~vuJfJyZ&`Yzh|{mmEF5DrF~^8:Q$<.fCDg{<c/SyYiC>heF2m@!qcU/hhkG.JTvRBha$(k|$cT+7:iTR6}u6rEA8f|d;0N%q6L%I*nMm8D[>U_kQYcI8k;{4Ej9TPYL^H_XRu)21"V>_0lpS$5hap0m/8Wz&}MuA]>M$SRfpi/gNH1)BEE`Nh%;?F?I{c{/T(;EN"|DeBV$N^]aJMPUpM^3j:{(n(9V^)G44kH[a*|q%hV~DJbj:ZKu)61Z|g*:qt%|w^/u~$E|7Ozd,lXg)_e8VVtE)q.VLqtr?I=N]E2&_4ibz/{_0Tnw$M:S[]0QsCP~}gNbqtw/L1H0ndf%#2fVKoB|OEIWl+0q}}1KG2}WqfCRCD:_QD+J:nHPqYCHj1|S*Ko>^iEmK7!`Zr61eTA"EElFA]ugK+.?kUR;(zO^|/JHwpDAuW&|X:PG&th_?Gg%T8,s/Oh}[B`pp_C|B.r_AUE`ZSX$mcUQxrpO]1K}Lx$jKJ=`s!$ZQUEh7"K@^wV$9ieD:0(A7R:)DaOz4`%i)[@yLK_amDK0a;raQF)YUbff?/;a?Gk%~ld1gx)@^1G4lI{_~dT:kYJW{izP]}u<=~>/Y8vjTyorgW2*^2EVsWaZu~kuZs<UdwDFz(K5BFwNRXo/8rAvmuD^TwFV"~{<)R.$Js08~!fjo(p6mm3hF|hlTg6U2{x@U=RHk+Ibq%H?Xxj23tiEx/+WMvGc4JY}GYw{H/Yg3Z9iP2wsxx9j+ujPy`b[6W92JUk)jhSseNCH#W$WOJ!9qrT+D*:C$diJ_p7U>tUu,|rC53Zq]y*3cjWTK@,^86?ELPn^[.TVz*:D^4pgvG0uPxXzlq~&n,|kbz],Xd;1Kx+fp,zyxD+J8!9.JX("$GW]h#QaGNqaG3;zkKStlbvj=_Ep<4*1*G)lFAI}=*"5Dvui|`y=LJsE79t5>Y;TmoLV@m&wI9O`+>u?$2S7nTTO~>x51d?P5P&_1Eqk~|3E.@.7Vs/q!EvRV4K*|Lva%DNXPje/yr.XXamUO.3zjcrRN.,e%~2BsOLu3jmngwUNF"$>ojgTQBR#u)B}BT(v+oX!^b+$#%;YIK)zLfgHpGV=RE{/ROV^vjp30VW5MGpAv>9X$vykt?!`6kInV;fQ$c$*!}|hIi>fxSHzpc(&=dih5<P$wzsQKBygG{08~?Hsox^ej_;"FJA$>D&fn{{H+v{I_{q`{r_qB0r,K>zD,;fhOFRFNN;O.b4K3<C$m#g:h*x"n^Kq9KZs[16.Fgg)q|M=6/SHCzWB$sXb)gQZ{;R$prMzO"{0#o8hb,l]70_+k"_9|OlctNZ,~{FYnhv+4V[8fdOv,;Ybv2%Y)QhY29aE`BFRIU;c6SxxT|,Cj?rD2^HTd~BB%}NS4y:SRw1[3VV|~o_i*>@cJjnw1?)<EDGF~P}bKGUU8y&/EP7baVb+s+S)WET;VEh!{hMF6(@*)enl~_|9,UU})94D|/~`:"`TTETg;(U$vxm/BEo[#pT"YF5?zcoPZpB6<kP%Rdx00k5`r!/Hw>D5rOx`"t[D8!!BajG!>"s"Fk*^K.CLKBV7L5jVO`LTUEMvwF^oDiLtC9`6G,x"bQ4x@$z1}vG6U`,0o),*z<faCg2raZd;t3O.Ua&?!bvu}M=/p<HeEULx>%P1d12i#Tusla6fU53BuN43xrsKdb/k:iN2R?Ksd%nR__EM?u$B46aH9o70E22Mie|^S2._nDS?tK/LAddJQ=fV^$M+C%2p>>lW"vTR[(#Z3C$wK!!z|uRL~Nq|XKr"ztf]ES5?U)2_W@BQ!i_%J>!A1m5/0e1BjaD;)}mM6qYr*SIOigbK!9idn<Tpkb*3t102fy.z6?BB0EBuimd*RZ~>pn^Gp>hZv&Lr8a$_KJ<2W|z2pPv7KoDnSn.PtZ)r^6@"VA[;yEGT1_UEQ2l7iAWCNv6ST_T)LiWx<lYteOv#.Fp/o%G7f>1UYsi8o|@3a"`q^"/DzZQOqGU$y.qlQc0R?OS|8i4WfxDa2~9@fPgE:s",O2ptB{CeFb$3gKsZ~}>G&#W$?*IZ}Z_0j(V~L87Dk@vcG&i7B/C;I!#h1[bEXj<1K]mrZC3Iy7@+5I4+7V2HmtWC^[,uCPJn]Bp!u)nh|]3$?Dft!N,(aH[I9w!Yk3kF^xK(X?Po(v>$<~pB]P{q;9[XG9BW1_A|62t7`"i+D.]+hAww.0jz]Gw"e0mpdhdl8Yotbh;wC})MNM0"Iisp!J%CPq=/[m3(U~iX`+K,huB.bGtj]D!JZOy+6$SvNcgwectv5Vl}M$.Y0U0`053xPtas!GSY<ZeL2sX&}:jxbh`NpsmkO?.}9m)PRh_i%v5AQ]C&>v7R3IIg_yzp>zN3or7<&&j`}!^Ze~%`<fk"kMV|G2qoIaf].iO#BuF/A!dVM8gX(7F1h!?pM6u7Y4(7FY!FV(CyJJ|@QNJylQ^yV0pSS,o+ae^]x=wy}A9n1BJ7(HE}Ip&94%Y5|8O5#O=&N+qH&$CQ[>{oJH#Azjv*:oJn=A9saezr!G}YPCix``!B&K:C6m;a8aV|caW,i^,~kAnmMu]"Qw3S0j(ghk{B?q2v$=Q_a`UNVcsJB#Ufl4Zzy%&IPC60m31_)T8elz}N7WU>V(l_Zg]n}U_bB$7S%"20mW8K6JJ(Cu<|>K#N!caMqT9h${:7{mQjT0*Z}H@tYX*n4@[vX^b:rp~4Lz^l|/SW4A2w}4yl*I6kM`n`g03`]MV$+lEsnQkvb)MbJ$*q]G&)XpI6.8oI0OddY8de>IY>@NQgNMG(/,<O{4,pGX,uNF[<M:d`&^41Wb##sg1"*K6+a[;VH49,Sgb$_5Gahr^O1uWbaw=dVf?l(sd&Crkfr_K8REQ^2_!A)aFUPF8`4O}|8J&!x&r;TL&=PiXzVZC?Da!MBT3c%_[Qtq?o0*sk]OYJpq$6SrXM>L`;gUjOJ&]2J5E1`6NrYU3%P4vdV?~G4KDl3[0m;P"S5@m#O>g;p{QdYSL9.DD.BkY&7EGz]6SVN#JYpM[TU3RXx)OHiC@r!?"C}f9^Q[N:p*nf[58)9`MNQ#_t}Z=6hUfc&Ma$RH{xt|=st~_@JWnpzpnub2u!qQvG7BtAHqf5@HNBdHoWWaXSn@:"/fcEs#lNYRJJl(9LdOvP!QX*{:v`{r+bazo=ym{D,^K#dQYXAa_xr:U1J$(@7.X#"x9Umw[A@MbjJ^~0*ego!tRDVbbwgZwpPTX[^#2P&4y,9+#%S?u1mqE*"u9:)|~SGw*W^n6,6*,mhs:y6x1.VTC5>J<Now7|Y;@f8ua}IH=K[I)+bdRai$S/6S8bav$E:%d7TJ!2b?iK;Hc/p2De(qLvcJq!1!7FdI&dD;Xqm{0?5k;#.b^AXH5dFo042}B?qP8TX6VS8]w%<8jPTBSl9[$Z2M=WYWc73o;V+/CN&UKq}FTv4*ewK>q3m~w>L_<bDZlHK%::ALY~MUR#,QEHYw>tR@90Nv05)%a!GYa?=svF](iX9A|OYHam,9L%_w{MsYJqI1pS7K1@y$qEO_BVP)~X%dv@MgX7#6*}*9lxH=>}`WfYcR2c:L2pO.H|f_o+EC)FwEo9Z)zLN"lEg,g28I!4(cClI9J[Oi6Ei&}3wV3gy0Gg^bKCzzMQ.DN`x4ef_T!T]27yxqutOC^SJeBy>9Z"9:trd>0v)ozWlS5(?,344yS:7Aee@EM_E{ux=!Zi}UE|di6V90I)v~=*yK:GYCGiP#aD^byNs[xWDa0}(rW>)BWv}nOoE2CEZxG|cUaT:;P1_uQ$w5h<>PEetkuB"%$n>}9%=T@s5J@q&N/QS/m,7|t;^&Zi>L?m^vZoRtni`R/gZ}bBqWT&IH>9eR5TkA)gI7mnof}>12gf+rP.[]mv%E7H;_W|VIUQSbG5)ja:j8c5:yVzBUWc,I)=mQyNSSMHY]$Dn?ZWbQAOkp[k`W0uu|vZvH@9?0`!X70`^u{!J}&!FqqNnIlOgPxIE<P[OYkF+ClgYq*hB=Jt.Zb0==cJRuPT;_k+QftM)nOa6tHqSlKNI/80ZNM/)~jk%U/.G^V!m:aiU0y<Ro<VD>uifL2&zC+[AR!//;H6{vt1;_]]G(RRH{u:4|W)OKC:9jYzUjT%&Z>G`.zv+p??6_jO#yV6uTKsb)!_tddMTJOIu@F]bguJ@vZO2t/vbvQyrFcZa+[6!HDc`}5(DLw,2>qOT`;_c0~__whcENDe`>a%b!<Xg8aN1`0.aiuK3).^7HBAl7r9t1oEE&Aq_mr!P/Yh@S/*DzWbq0[x)~l/M(]A=%~mf1&IqH&eY^w?.J$e}b$YUuSsmNJlj}s0leQEf?XfZlB(#l61~Q1!&u!LQrIBFpm9hXXxM=i`?(H.tm!_J9x7BjfaB&gu1A4w!/faIBBFEw*GtLTBDaqc.X0aYAJ8s+G{&X3Lv^vkHNUFhDHSN"g}#Ww&foVZp3FUa0HQw0RIgq8f,_+vX+Oy;.?V.)J?5~sD:6`W:v(eRI`lvXm#;DdBAt9:574hDX/mvgqw+d%o~}l+C;<s1rR7T(Hp%2)%Y|kCf,rd8tvHrQJp@pBO%avv>xaGW^yL71p[j*U[rqC;n<l"f{mJoNX2xS(OZCNVGBu:{WD;^$UI*8VsnLL=n/lGc!IJvp~b*E1K$qDoH$YaM|bl@0qcycp(NOe!*F5~ABFglT4OTa58nG^R)eQoMWcFbuHaK@H^HwH$@0B)HBM"iEpe9XDt5mT;(R(33;WTdC?=I4SgADaJ5o=[#:G@oPUbJfNx>ooA^Y2#1MRYAX~Ud!?6~`Wg2##2c0^I)!#1`19r$Fv*>Co=Jzf6aef~`:[FXL0bg(O{OBb#y,&U.?r~Bl}/S:ZdI5Y`(O2{iPrXB=,!xpX[S&}gdCDFYs=d"KzB^Z&3Qy!hzS)zuyg7qd3>i;fnxHk0^^/`t@IeW#c3]dkg)Mw[T?Mm*QhFtha!9BFGUu)ohdvS*0>&v`{:/tKB]MON)J!xGrJ^{qwXnqlyP,@VF=R_"E[JkH<8U4i&F!{},P=WFEqg8X3`9F>zHP,$KuPRIz3PWf6sk*>4K5?>#~h8HoA$g0=JBQ){^0]r>]nEOeArs<{}kf8Y{yx=+iEJ,1DX%{H"|yR`LpuCUl[KtUrZlz)%*}t(keB7W3M{":iC~Z]+k>LMJGO59k?rWE+3uvF}wcaes[6J_dw|701~XylPuf[Be,u9?=g7Slb*gG8XqN4>@P+J_08W_C5m(Y]@Ly|qL4bZJB(TdL_F,=U3<L/bhw59Hwi#UPf8+C1Y5Jj3qg,g,K{V7z23Fs?TZtffTQz@P|8WSQwlqge?Jc$2,w[mz!WnKy&b?/M=kH0[u,+{xtY{xA7sMb#Y<zuTun9C>6Pep)ZRo.Ln7B]"hEvuY/]bSmTx,*Iv03&7qpAT5TO)e<k/+@7*(T<Nfve@O]wpUulI2)M>+rSxj,n.HL=R2X*s&hK]wq$+UQ2{0DgAZS;hWm8tvGHvKy3$QBz<{KkgNbMfhU1nFb4!Dc,_npt74k$mRAh6?%`j<3HgVVpLCWUt?[l_OAZ.KKZN[!K4tH!*S,@V7v>}_fojXPx2vB!3@,R5tpf1;z|x8h})4zztKJZz]&Ni8O{liX6g<6:uZl(%jLMGLSiQgEW$ZK^InQWW_%=9:>WzR5{D?"3l7!??I*&yO(`~I*|1&T]p+4Y(]|hNru#aC$)Ntf!>bzz}Mpb:_lA$Q1RgKN?/4Ry?Hui&4~$J]~/,L8SBL^WZl6<QV%e<4XNxu]t{E_Is3C|2vAHlAq,8b[~FCUjv)Bp$4OMtD"ibImK!hq4?L([j[T}&/vAg]f0.E42Jt3Q;[vt&$;,%T_JD7)tWk5`:4M+MRZN2GS=Z}T=B_7{})VWrn&R$"cVP<PL|`1Z&u3#mNr:BQeWEaTKW&l~GCr!4VpP<9>6mt593[o1rP5Y47AxT_<>Mw_pA:,+0ovBxXP82:MwoW&@Y8EF}N8yV[+{HDLJe+t]s75c"{vz7Rd7`MC(Z(rE?5jtft4#(_pk$wG$786=BWB#^*&4yoXEKdzpOoslq7*Q+[M*pGJzT/#c9Y|]4J>XrS:UGL7z$n*;i)]V(H,f9=O>4857E#aSbW6sMlI*DLrCO7yJO4lvdHe1MB@DtlEk.=0_|b`eKwNP?YZU:5.h0+Oa*IW{NCF4ou[FX)t<{)acjIGE`=*KvEk8{>|xDG%)XGbgcN1g"{9~xvmd%w||S1i6d*0k@],@@_A9BChNH+92w~:Dv6(dWyTq)DJHd9{U,_`7U4t7qw$p`r.et]JkZ*ToF#)oQQ[,jjsVVR&5vAj)~"xVv1(FW!>^d@nu*MwQ;Dr]&29b/T.!)HL7uH#7Yn%|g5h!]DZgsEDX<5A)^e+yYcO!`{xkw{AbBmj(/SXKFDc.x~ffX5:*UJ;?h0{}^*:y"jIW%`r8s}!I3(rQ53p}q4S*49SzVj{NR%J)wPNRb!V#ts/wc*}Ocp|i3VhvTPG"vOKM3%YIH>Xz^:oPQ?HQ+9vT%&B.77KP.XWmt"EP)P~hv%Rjntye[sPfFU6Qm:yrPU!3:glo;3xt6}L80q~V;C3!<XvCuko77w8YcYBQaOgaxv2cSSJiA$Bhy*M,)eRCT.IlM.jcBBpvgHB2phAE9_0{[48OoAm~5#V!Sy).Tx"7#Ir7Y2d4&AzF{|<S$<lG%/qnW%#shG@qLoC8+U4GtxiyvBC8zVd7~l8nCLkG7(,9@r"Z=6DJ>4JU].r8/D#5o)Ao0?>BO;:B+a=h!B0l6&1S>UBG;GT$Lo$HlO7O0)ivzqowW6Pl!dc9/T`Bh%!c:^`_pFY=og]XSGVG:8OAO3|"Bv>=fLOuCLP|[~>mfRX#HzRc*NEj%=rAbG%rx|w&)*gxY(5GvmlMMf+]=$[u0gYW~s`<^aJIt{ni/o;#tln(/F6&"+qToMb{L16SP3i%D2.hU$7^^;DvY$)0FTfcatPQ~0f&Wg8>hd.+3f;n9=jWpFW!iQ$(bB&kZO^n,H4:8LNx`TyJ5VK"g)"$!hn_}6k{H/kl};knRq%t:}i*pagXXS3e;39$oB&M,2@qeGf_w_6jf}SJaL.T,4xjb/Um%Bn{ZEL)YHs%sIp[[ktS;u[4vMCq:qr$:OX/"Ig7Md+c@.}cAGuID&@Y$I;pMTdVMCb.,fa+L^Gf^or%`f55GYw8fE#}D/><PT[*TGz]CYpaAP~hX5KRzZ7mVQXX{;gbxrmT*`6Ni:HbEOq%N%]DQzz;7Yi`F/7]k.:;XwR|.O#1e%~F3x#%7UM}iVOLqeD<_e60nx|*DkPz/3r80yGZW([~<8puQ%S9FJp!&=k,*jPS/&%A8P{B&r)HGyt2$mlnhZexO@HDmK5PPT>XK$]pNiHN||m]33@`y:DFl[^$i`"rp*&YqN|(KYHKh~{@rwB3K{S:&|wX;05M>+$f2f<R?r,4:<nAps:o{li/Z8*b:Sm}BkyK$Y<)aCdh23o6FXH=O+VVOO/C4!+3Z*A<Cp%#JPxeBG1SMIQrnU>rSo}hcrJfD/P%^R&12Hs6JZ&>gy}RlseB?4*DDn![Xi?I0wg9]j%^X~!]YW&sA8X".eMzC{5^3D/:E`M8>QUA{G+o(zrim13YXds};btvE?&I!+[4qf!(A>djqFwXH6fSE1.[v#77HdZVT&W9t1i>dikaQ;LhRMVBn>0<"D_=h+S@1Q~?~R7`hBRuc)54:k+kmSYPfw.XN3r#xDK1380._D}uRgP9ZJ>wKY5kzto7>jb7W~p3aE3^$ESJ?d}+YwJM/,xbA<c?DlQ^,6J+bDMl"=/pawA1eU]nBPROpe0jFlUZF)5iEb4;j6i&bh|l}IYluCiGcB=!"V)}?bVW3t34,/pHPq24^T~B4[,SLQ&i$?P~8RSB*Sbhd4{Inu=G.I[k=<hJ44AI:2d$wZ09*91kFiQ?[w%QCo2GIsyvy~y974;EkHwk_IkTP=iVUi{$F>{?V!bNH%)mlbM`2"w;0gLY,7gfRf}|_RJqwcPNo3bSoviU]HiJ1,$%Y=mX@#O4Rwb4|dwPKqu8peFt:3j8juM};ydR<zh$vg!~rz1HAUp|,{{a%?D_;ly,2Pfka)X&hCEj/6<6!z=bnZ`@1E0FJRH%.$evPem,AwA$vkW=faSh**,W5pd~&M@9f%6?|en*}n/sp_X}Tv"A6%/i5/4V5Fe%e[X9vrqP;m8Z[nyV$xTK$#j=Za]7JCd#R+Ph5eE0e3^<~Fz/67sX$7;MVvmld6Vcw4Wo<OK.?]$C4?NZT0+uujj<(qnqX3dm`0%6L["WgAZD5~EaLZ1_*7(rj|YfK=liyWL|y]9iaNg;860%QZ$@TqV{^kV<~z_NRN7X>{lj;?F/;?OFP,_T7(zF$5.mRH}5nO(n,v9>gP~]:w.G4G56T/3M6h#T:2AgCT>TI<t]~>>Oc*78E$MLEkVx>{5:xnDx"x+$$273();|H3n.?vx=3[:ch4&VqE6F7oPwOhooi~PypR=H}DBMfxg@|Hz}l<J8MQaa`OIJ5b@Y<D<B,gUP|"W2UZmxH`j|Gb{Ny?S!E%r=.@eMAIKD1<As+],4x4LTaGr/*UsGYb2;DKZ*,3"c|._M,x],ByOcib(q_JH7]3CeLx7HENi^]X?JA4YY1LeND3gy@u&Qhe:Xwowqb]6[_CBi|f+fokYW3jy]#Qi^2eDTnk2QGx6L{,9jec55=RJqg/(=g~3!k.R^5zU2;J}D/%dL#AEfpuiH8Nw`lK?coa+a`aQcY+_fL7kC(?mYNyo*?3H#`T!j46fy=919?46wV|D{VIfbeq(tIk.{^OJ?YB+8v&^p<sePhxfvi~&V+>`KA<$P0%bjM3a@Zhq,*^wZ!J)+R3b`S?L@b+3EOljab0s/y>7&X/<HHFqUar_+IO[<J8Jx#4}hGkL+Ou.L"W4E#BxBpOGW~+,l/kH~4fdB^2&{TmOnoTfX+L{uK.n*NA@&&%?I+I"YXj@l<Z5*M0.PqV_z^">e$YgK[aFNe0#hGvfCj5KC2BK+&5M067&TI^dd6&5=Rx5W:..9ncUQhfv"xfkGo@Z&{qA|_$jiSk$v5uQ*8]6.VWlpN52O4s]b[Mg3K`NDE!:vVj^zc!+},^SKIZ4d}G{mYC"2#C;|K;!~)5nvgUo2dtI".|#smM"3L%Zk8]3GwpO|.7=PRk/$aUe}r6PX9jt{|,;o$6a?nP5eYp,lQ"edP|kKYdd.58D_l0]CzJX<Qzd|>qD[qe7y/Y4AGl"|E1|L0?k%Ljp)Cq*<5$Ob%t6uWrk#k.Z{ify+mG^7Dom!VQdt=Jc9ko`F`.Ns<$Z.qD&a<JkMSo0R0vF+8UR[&`;qOs`$cbPcLP"Y&=)6ca=}0e;>h%R(x,rEbdh`OjaG&of;vdn!<yEQb21k;"`Z)*N)v4&46{NL2prHLvWB.E[7d4a{FH./&`V).JeXkXIKh>R8#:,MOqQPz^~7||Sx{s1uzz;u"dd;OLqh@@I_XP2$u^9=4CP1z$JN/i=s=54s+J!LmHJ2r5w8=XmTzz@Fl>W8(*h6RYwO(HA>]9<N}#V|L5i5+{(&RN[ide9mMqc=)).vZ+Eh+P(]rDYL6UUC;e@FTD/u>%#<M6hB]kHx1Y6sCq(%+<#`o5r+Asx^S=<gq(}NdF]bIVaLGRD]"&RETCN.SjqlicQp:/s{w!evmOr*&o[8UA8bJR$yubt<*/V%R9^d~~&W)4.eHcJL>lL8Nso?#fb4C_tWde|6{Oi:WnjDcXprh|/#79[UP#}T5{O%YzdbREZ}9paLnp1oWJ3g0l)!d+hcSg}6Wx8S7ja5",pv~+l%C8A`10f>Ul6=d<d0V{0k<m&=9Q<<xBt<1Ss7iYzEpCj$m>,k7nPI]mPy#]:?nVyvi1<22k#Z<KqWV@O,~[5mFLZ:mp~0klP3*u<dM.kc)HJ0u#XHQZ{r:H;il;*K03uMI3#JD"/*=LI]{){+jjC6yGU%YD^QF6JNGr`!)D6h23DEtTOq2*owdy=Uh)2C!dgV=Irg>+lJ[>?E0c+pSX`Vl7Z&a+IU;EG56g5C]k7fWtLPK,].GF;!)v@%*:tR~Trv7k9~c6Mf3p%_+vw3,ggRr=Q?jy"?jEKbqHs&$dIt4~.%=c&?m[Y/3oP"6z&vL1OMrHd40zD?;zPNhFukc_TYlnW%/T9/|cuUA/u,$_FEk(h]cg^,qd.kv,:56Dq+LT(#hmiY(all(1%U1bTyuiFUT)[t}wVs@Y$6~@Fh~8sRZ@[O92lYL|q<3c:/+_TsalcO(dj!cScE6OkB*RqZ:)y%uA8ed0wKFD@P!f(BXXwyQJ;%w#gO"($ax~,$W[fIH_i]8:=fi%IVK_77]w8d=b++/P`CA;HInKEo5;zlG/R?_X+P45mE:gJp=n[WUUEdwqloYt^SSM1MS54F(KPKDNt=U6^mtpqy)/SMu@"4h$|L?!BWJqQv`t<8QR|eM~a(CKYGy{bR`qj091X!gd~I{$_tK!0cB$"h4@HGX;Z,+t])ShxHbN=7#VzOws"85^LhmcpNZ1Y}L;3aBci"JWWJ&+,OxzZkHW<Nu1[Wc@k>{d?L20H$4II+"rkN3`Ek3EOoMgc*ij0viFbp/IO*E|76Wv&mhk"3n+]R@S*(oXD50>=w[K.8t$4tmZUYn7(jvu_bds4H&|w}IP9d(k8mD5!"kdp0DT+{7}p!Wk%3g;j&"/8Y(^6~houJu>^J37L23qn{knP}r/["|)v5_]?W|4cNixRFm)=m+qIi(l@z5E$Y(ShBNBusJFNfM3dVhl%`CahG6TIcB6mk|[#|heH>B&Yg2Q*H*Bd0FAN={upFoz[2G{@oMewG,:oh$1NO=UZ~RwW!?KHt{8bEQ.%7d!]NVHsVsFWVG%wI~e.w@d4RNHowvDZ<B*Y<QmRdkUWk`0]y7f^E/Ly`/yh`,*R`@`^pM`Mp1?G.AbviU>bX|irutX/`:RBf+.n{?7Lx7L7Eq6<55O"COOqJt;Qv!"e)kBm~bx#E=N$WqW<8?se&!m0dj|HpviNz]G{oZ!jFu/r%cn(X!nO%jvVIB[}K+@73Y8eC:u>K#TQ7AH~lI.uOkGPU)QKRClE&}LyjaxQ~$UmiYm7;b%[?>Uuir_)=Ba"k!tQU~7H@&`D&,ji:3%PC*[yiyfdDvy:VfT^u?][t!u#a"oE6P{k|5Be9OT*.3T{$XV;z_*p~Z<x]Ga(Gk5$7f&z)u8ow6"Qg{oVu?el#uCx]3wUNo{F(Y}L8A]YSV]]pmJA,mk0L@7qx^gWMOiCxQe0Cdw+xD;PSS7PzF?@K&t{U{9km}IS,7X4$t%g_zrA=8:C#K&IZlfV|klIv"*m]e*ArGA?OtD*c414Hsjh[;[`j?|:#YqZ]V]|<oLCTE;N0+!~iA@~Q2YErL%wkaev5,gNnkUT4m=8?d<QpkBl!Ea/%@|a^|^vLM~${l}|[Cv3lDP_;H_a)APn|f4jPP@1|>&I9;0lrD+yro,J~8d>^x";%3Mx$Ui?zlJ!Qa;a;ElkQ`^E|gT.:T:R1fxrNVkC;/,["?4vH|]z$)#ajHAm8X}W>kLJw,Ot*>={ovNd$$.&`j~qBmp8#XT)VVcpk#u*m:ONAMvVsHuPZ^#TIIf4<9*%TZe|r]ZJ8Oxr#S:^1sMWa^N[hk99Cm0TG.z~7X3A`<6JxwSQ2{_)IKOS^%03yE?ywtwIzo,iDaLTPc1Of?zzK`reGBb1r~<LthmH/9La!LCOD~WC3+R;YkksZ3Y"rm^|9IzM^zRM&`;L_hC>VpbMmu!t+{C~U1=svi91~E0CiN7|`1FaStapHX6?,K0jMXH{t|C{p!jIKcAu,q;5uZe.dKePBz+[|OSgyYar[?ATkYV4T}V68w$}hg.Zx>9?|lg6{DeNv&TV+41|rDG{=+LZtLl2jnBua?k:i59ixvQb+oO5XTg1mBCWHMzYugrMn[>$Dch:KzD/UYRf=n|jtKhHgWj:gsLXMgjiq^D;j`+r+<zOo:k)S|t?U,r/vAn|L2GMEEg_N*JsX~S]U`uv(^NbrDJt5?]yo%EsqKPUWX!sl7(bZ[8yBtR[c<Jj2jMi4>98XK]FMB43_bzG4d3Xy;tS^nvzN={m<Wu[Q|G[?{Kg,DbgJ|d{/7,(LP^NA;<!5=i&s`=P+,;t%JmQ2EcJ7[<qO#P&p/5UcSL_x|a#z1"%y]e{o!d,m1[iQD:V{J0[}T=Zd/!^/yeMYFa!.26Xuc9x"qZqa"u,48x)kKyVy!Kr,,BB0f~(,]gIL:gcoY&W/KteG@F61"R*f]/y*z/U?oIm#bhy}nHl2vXH#V@vXCPVGsHiQ|dOpUb,^:H0,Xhy?`~nHp("Q0I$n?W8&B%Hle]n|9=+BB4xu!.O[g#x$>,@g$dwRxD:=e8~#_v$qm#u2xGz[c4k*x+Fcfs#$PVE:()J[,BCbxZK;an=6^v:rCrkUgtT3,KED%5LY}%+5(P_eFy]E7Q%KV)_K~6^wm!g3z&6uYg/gnLh3LngiPMrtFNP&2]z`?37#@itS?kb1G!k[7~YncGy.fD8p":6xe*?]eJlRUMJMzNE]F(]U6#DeA#l728`[+b,U7Cg6.e/ePuMYgN&DCjaBNLL`$4pM9{x&/#t4M)M*iPgt"b;sqMSH+/zJ!Jzj.znxN7^=6lR^u4zs~LQ#qRMp~i%ryv6N#3>W!1bKntvSqpdNdjS.Da5mm#J9VjM<195QH">8ihHF|@1$8[z~RT5Ik(TpQ;=cql3?Eu&4F)3k8M$g&;S~~t1[_/zW{kXPM+k1%l_cv+zfIlJ!8lbDkUZiff].p(;FqTero}|oU`!*;m8%Uze.&0,>8Rrb;%CaSMa_27H&W.!WdZ,G$g30+z,<rU~rh7/XaD*BYFv`VWkYJxN{L^Zf"F&N=JIOOit@t4iOO<<$1@FP2&JE+b/$"ACV~0C0!x^!I{xLS#mciXl$tbzHaO:NNao7{mJIkq$Lvv?rN*FBu!CX]j^xM51zvu]l01TJj|7PF?MRdy?HnMw3B/df1wRHDI9VFTuQ=Nkrq+D=X>uQ1ax7&p`Ola$?Vici]3w)esShPeDYQFJZWly7*?TmDb(M]9Q"F}I|0qv>Kf;2T?EzyW.Q%9*~GQc$*_.`1k}^6K^n.&FMIE]pTB%bubWtF$2aNrRx=LQ_D[=x%I]~n1x&;jH|Fy1Q"xE4?EB[U#i7r)/2%?VzV:7xO$K>X|iHB];X#zn/`o|C,mC)f0#y&]%]F:X+;g>d4nRXH`|PxOehhXC!FX0jX7(S4a%i#2>*J9VThJ*c|(gH*LA"LSFb|1;e/80MsgQQ9ht4,jQ2]Cg@%l5gW}^jQBN{]<MNnI<NI4=p%Jl#&V5^uO3Alip>`$Lj4SVM+SHl~sgYPyq]29)4z7d+W<>B*ks1:o$6Y:yV>G2k!HQ^MP/q_n|qVHq1rxGSpH25c{07]=0X$2ulY<|Zrtyz=<1[]6rXLbflbk$qd#HP.yei*qk6@~.~6LFkxlv0],#7f#L%f1><ndAT`zm&?!=v<QmQ#Hu8|Uw~!d60`XTT$3SjJlbOr+RuD;7T|#ydGXj=bNc>JsiD~RxF>st*+S!T<i@YjWI_>(8Ye2,@yxT]X1<Cn0w(i$Z1YNx,E[+sCMGx7PDV|:AY%b$Yl!=qu9?n@Gn9vn!ZbCXIThoi#B90|^}cd`R~.yzgP`.iYSzCuB+A?vL[hCi|uvL@YyIz2R|GDl5moTU809q%,IZ$@ozMsZ.%AaDB+)CJc>Xc)ic`j5JHZY}9]<Q6nmF_3tf5Yz*4v%r)<s&^odP3i?gVu)M!jv+.zTbZ5*P*C;2wI7C%sRSz|6Z}8D<aXq#*l_*6yq#s1$4_!,/XRq=Lb!_Ntd:N=VgXv]ge!{A"*S^rX?f3c`N,dZqxJUE+D=!V9cX_3K#i~85aNuo}O&*u|y;Gmqx$WP0Xs&7l)Lrh[06]H*!nt~~ybC.Id2n5SKHstMm{j6S1UGL!v)U(Vc|,=cdA!x=e:_t?$czg*wo0G!D?BU(R[>4aRGz8P7x:9~~y|vA;&Nmt?)~Pu:,k{;({y6u"/uJvTqX8E2heiCDGLd}m#smw%DxUV%BUyJ`]a3iIv<>tVh7.%0"zAjL1r#ilQP5s7fmn]/**X]H[+Exn.)#k31czFA7}9]KVd#8^vw^~}e{Hu@;gHRXTZQ03m/e5~/O%uZ^@0+z10ETm[d>y>(1/`15LoNnn5X](;~F"t#(UR`]EZ~Ocmc<uEOvic7T0y/S?!_Y3"6l+r)vJ+A3hwI7m6mZA<$x}1TrbvQ6J/zJ;bL4l<]+T}R*^3{$IE/OGa;~]Ly`zWNk+DE(f9cBKQH@jmMST5)<wx449MyTUHT~J!{+^Ey4h"rixZ6U3fg4ivvyY9:0g>x4}1*&gQ.^upq;te%][k^ydQ&/j)3j,vWdvTqgWnATtC4dbdNAmkx$Hf^FhpsXQ)Hs/|D~CsYht`dIl%UnMkrD<gFmT"m@7BFt.3_{~N"q4.c|9N8kEnN5pK2rjbiJ(+ypY2vd)hD=bJ{vE0UND1,pm:Tb>ke&e72"<<%{OveEzkTy7*a(5[l.!W/,Dl=S(f:oRyz:1S;e#~[~0_Vh"^%Hn+z$%luccwi}v9U$iX%&pv=l[_e_V?,ElNI$(tVJ22*mw*r%HKhswAWX6gum67#}qxHkeY,<O0wo|u)Wy4&WqYzc%rTFGcAV[C%BO`]&RQIpJWNJP8Kkb=7XXa)H10:"gwcz3!8Ky/3lOIx<Ij@|!ZPTu8ow;MH`>&Ky`[WuyjY71=U@GkoftxUX3L>E8dexhit3M/<zGV~~k7vN+Kbw&OO1KtCLO[41F9+V:6V/&W5U?zrr*sibXlK6HrPaj@@mAJ,;FN?9g9Kf[@8X#f>aXy].2aIb}vKbxNlDG#l.JHGnfOAZy<tg]68tr{7.z|3teEtb,u86Sms1qmDA]QxKydyR#ecvk4n*=#QQJe!$,G<31*=6~o0.uKPSF4<vN(_C?6)G5H%KV(Lao7LD``NQ8#V1z7@}&0%^wB?UO.N3gw|S`r`SM,DiN8UPi8]D7CAM#3I&:IGOK;W1Qme9m/$>y>?4>VCbXp+E1v]y8?,w_tf:QANstOrC.a@h3`@IQ(YM"daE>"InHci((=?q@<D^/,(ik$vH4V","?3:4msY$pk&afavx00(/F0cov;bp!^KOlXE@AFstC>G]RphJ&@>[.cMG*A>=ho%5^o3lxav5JU{+ubMo3E_>oXNgrl?Q.8D~5GOeA|%UwCkQ%D@:Jj<RC=M(#q8yFG%xC$2rJRXa?Z?OQq/"&]:B_a[W1{Jh5&]{b}*!cyxD5$<B1d&#.}oXX4q8q]fsL!n=#Dpa89`$|rqxJN8rz04q`):_?X^mGEshb_)W"rl28kxagHGB~kG4kvBmo/339irl.,)/+GwJNym_[y0{KR1}I0Tn:+FUQ^qB|mKinRds}S?3IaE"+YtD>T1&N0Q{7W7UzU~Wl2+{~Pm%B|R<afPDLRH"ntDKF&#pYVef3Gp+$b*];#;!}bf;$BU;I_)zl!H?VZ#t@jUB^9Q7|nkdr%Zy+pLMdSVu[qA4a6IMY.J]46FmZ<#}>I4k](#DYR}JVVqVbdHv_*!1Q@<R/|aBqYgA{ZqW#4o+;q{h|HAqUSWwMmCNyVRV?=+x16LO8dSzJ3^EC5(9^GF6uIDN<pg?Mj6CqaggNL,<jgUvp9Hp[bDGS1.$]5HIsO&Oc{oS*o+zoejc$`YN"_+AZRlo``kWJFw#ORe3UMB;V$$xp+Ip;z4?=1r=!~_P!!q&iBAy#OLDO{q&tC|Uq]|!P4uvf_K_uPF1*a0]=5c@J{RGM.zd.^3)?sxqq_uj/$;*SXhM~()]BR`Z@etfw<Joax.k>"*R!Peka4,=ePP#{X{#G9rp&DXuQOKWD<?(QdbRb/z@5siayXuaZ61XkyCYs[G4mnY~pe4ww[#zy^QlkzWWKDRKt;p+B]nel[Ro3uerUszmwkdN7g@3rz<8u("hwk&SK;Y,*H19,ZdDyE]Q>y)*xC@!{Stha{sAG&[8I;+`>~.~l!Arb{TMcJpMW$A|@wKJ}=l57c#H?h9FOw<3{_;4!JlU|z*=jEzIkG3cgr@pABAo`7;c~;F/VhrrgSCUA*LMtb)nr=RW+nQ,u/f3R8_i}iIO!!8aBN*{`R5m"sl2*$Yf@Oi%;fQrz66/*6|>X`]?N^~{L!b=`0EI/Fiv*B(0}j4i2FB*_am"P=oQhLVB()qX7M?><}lc|?$~p]NEX"p22.g$)dx:RIUo((r0Lx~qo/S~4UfK2XYh:|@Eb*R*nH%A"ri[oIxGPmOQG5lhyg(6m]Mvpjt{>@],T]?M<0eZ7q=J(Y<TS9%TWDr$47)(988des$!V]!auZGQ6"VRG:6I*3pZAzdniw<Ek9$<V22&[{^FI)g3Thc0`B!sa%7R*mTej4ELkbR9Ayn6p"kK:[18m3$:"S2a5hD}U~dX./<joCo|_i&;lwaT`YbJemuGWD_X"nM7X>UXxA~W8o[NVbjRrrbr1[8:x)DD/xvA9L!WY(q}^k[arUMnf+}iz]Am*TFvcTUcwz?lc`Wt.2Ef*Xuc6/dr{XV9]+q0egQ){D=JdjAzVKN*/0{]a0x:ws/jK*z1w)?AK,Wq}X$sb$Lu%k?(]GipU^36UK<;.o,iBn12,T[&B#&_1==/O<Mg|)[9Z7#E2S}FYU/qdJ39&;Rbt=tXQ,)W,>vi<w*0/(X*fzw[^H.fiW,0}GI}t6jF^O{p$R,h}4/w!|Faw78,lsNc8WOdG#|mfKVhPOj:RQC+hxAl^kX*;f*hotwEX#z03OmFL5dSEo3^;mF1:+[p>:7s^T]yk="W`oIB$CR;;7WpZjU=!N/Kk,K:VX+R(7TU5/.;N}#~pHdL~lj|cDQVAXyx]"m$6R^Rs,F;Jb3Dui7tOFQ6$%?eph]RQ5=WlpeI!jOq{Ss9bk:qw+}[kfM#O5qmHbQ.Ao0Vbgp{c&Gk.fdR?6@;U*8"EYD.W<WNvynk3$@8(J#"33H1&GbKLZa`9~E:GlWwQ7+}%.#o#,[aXG>I:gL%b3!:.q+L_Zw8QG=~Sr>x2WaRF|=xTz^;GBv.1Sb5C*EjEL.B^=Wc<vC*X%p!c!su*^>o^!gQegE?2e>TOlcm.:2Wy8bhl%2$_A`_a9Y_$SD8?W&OfQcRBv7b``k]^9odz&|;"KEo))pelGLW+<d1HHdq1riW(?6p}}3b|Ul8]ujY}EUn2kr,Rb{Rm!,&/sfrGu$?7`)dtR,Fn7*!tZi~Ao91[u[sxes3K30jwNxcr?G=o$O=)Pi.S`MQRJH~~mG|d$uIIjLTBzPh^d/`/c4w6WlFF[#SAL$]=,X^0jgm^`(^os7iYNmij>]RTjLs2,4tUiFiG7{do>tq?v~*!GhSJpx3`$tBI5TE[tf|RYm?k",V=vR$ci?,,b_r/7f_CYI:vi7F,HL5phnJ^=6Y]T)i^8x<@IknA(`alQ0Gez(!5LXaSsWrhyN:_WVypRjvu9;#I7YU4yAsoN4|>"y:A|BqaOL={Gm3nGKmB%L=$7t`4AmwM^%fJtk=GB]UesgIo>R8#)R3v;Uu|aKvk$&5hXG{r]ddJfOCUwD<KTj_?C]ArO%:AkwS+_PyO/MG1ZRi3F=NOgDS0[d*^r,g.ht]Vr~DGm1K><{S1.*%+wGpW~G|x/UtM4"YRjv~4x.C6T6/a8]#iG$gk|y!I?_A"xf?,ZNJjCE3.B7$VN~1A>7PAuSm;e<Np>@xnfPUglz}J_tTau|I,^m&r(/OnFT`cwA)s50YuTb7zXRNudLE<C=cB.m{X$:z%W5?j;nzG17YL*]{b8m9N&&>`h{j2Y5haL@j|$@gn_{!*YR~!%eb=}M!6o+Li1{K$e+dayX;Xn83ftgzOmX_e4)0|b@*gj_uV27&%FaPm{^8%Rf%F=ZMc(;Sm{H|F<+#ZduRNra{+sM3~7]>vu)p]~_J{zGv.g*212~F]*?8n$Cv!1;$ir?82H&l|";p}:,!y8YrF|r^JE>`{re`8dbFFi$^<K@=1::3)JH[:Be[,<e2V>pO0;}SEvjh_]b9#F"d$5KEhi,N=#pr@#W8It,O0~P^^Q6o^J/&)T.<mDXJvguXC_*5xS|Zc4gZJ!$V/8GfiDcGj3ggreW;yH$o&^}XfHG8c/y301^[@.+r[^Kf5oh*+Xl$w*m%v5K~A.nQ@x.NA|7KzW#@.SfhZ7A9CS=EFRG$VSH&CtXJrg8]zG}A1e]OR"=D+|y]yg4grK1^HaaT~}^g}>$rmW$#hbs+9ta;rTPm+SI||:V0YwZM#FI{zIWq8T0Svc/GJcy&(RJI>Y1II/@^U2L=IC5y^yY+nTokf0IrNYWY0Gk5zY1BzY[V,nQa]#@@{)ze.J,X4oOlic.F"Q`[T,t+dEtf7(jdkWwMHebxlulH)<U>wIZMyPD){gkhxs8kC:;7S>K9_pDSc(N7{w4x#DlV`/A15X9e?8H!$uZcKv/LS&UC&f9K.HX}{zd~nsh$cJKB)zduyBxiOMz`suDc#P`X[|?uabB",py,?T_#7~dMAKNC{DY@!a<wvu#ccD,w_B~|U=%JX1C&{AyTW!e]8"Z?fPKm2C+8!YM.%=nM3^/bD!tQ,ixuf+TQKwGmH!AtR(%y&}t/3I7~U<z+Y2VoiS@Tk&[9^=b36G}DBo._q<i5c$Cu45j0BRp%BiJ+B75x+O!^BhrZF,3kv]9RQ@Tu3*:B1`DNG>X[Qra`"3;uHMkm!$lqc!Lw1P{0ZB||j"zFNL.J@P3wps%4y5/`Yd!7knHuJpw#3CyI>~BMNqTyiS3z%8{BmB+fahuEZY8IKw}+4PHY)N]`71shXp,=gpPSAmRAK./;Zp)|>(Zjx?MC}]ISSO/>]63P=M~/PI%7fHgP4,mt@6VHoJ|,H>bXsdYQC#u="#h(88AmU)PTuS#3B(dBzy^Fe4bruZrQD6lv8S9SpPr_A|$dC!u4mS$e^:)d{%{1;=8FANA&I[;8.)dXFsgCCz_?%KB8RXbWmLhj4!wGvhO)I(?i$:kWV2tL,nqyUA|zkGOcg|m8NzN0MxyQfIHg_^*;aug+&@[qE1loD9~Wcfw#opZz^muF=+Oeo<RL:<=c(6dN7&3?oB;M/dWw9AcZ&`jT1DvrpH)|hk;=})#EQvzzVWma+jO^m_1eA~[}%@~%NQbE`Gx#9~:,U0zPcLo)9Gt|9e@e/1cQz@:vxvK3uSc6hj1DHaOni8A]:Rd/KRye);FM}:7RCK"A+yd(MYx7UZ~GC)RroP^mB<V6}oP]*@@CD3I5Oj@*;qXc@K"s/B/>5rO*~]?!cZD[lt!EJeJ>Blyl&_;IJ"<H+Y#~bQVL2bBg|_}gFio`O}l3iT|mOt6F07&z92wqpJuRNtRb^=+H9im}cy&fDwhyPiJn#j<Y9bVhSeg_w3P;y/:dC3L&{.?0xjetd5Ii1vx]f;z1;|ce8.^JG!H166:]oao[DFu3HiAD$/7LV7?K](^a"RC>t?NERS=v/_ik(Fh):Fh(5^E4%a!SKO{j6PaT`Fw4k}.)f^`2AZJnsQPQ2w7{J`GXH>z.))=Tk*^#,vdy0d+;QH2w"[<"ZO<rwP{|ic24@Xq(q]u<]q2Z`G8nrg~>*c+;j"nShg<V!t9yEZMO2=6:bh"M2#cEku`YiOPCW]n|M|KgdGC|S8%rfzXY>KX@=Fv#w8lA#=ZqdLK4Rqp_5N]oF%y[Ak0u?l+!O9fVeos)p2iO0s0gwGu,d3Rf@4M[71^|zdiQlg`PT|l<&<jRD)YG$_=^!f5EIhqfPiRYzvq{0b13U$gn/FSZgGJsoBQT,q>hc*9VLi9k!{Qhl=i?EnWlI$j*bourVf[%GGwf.9!>%Q8+x^*1PDgogKFJk{cva<VnfL|=Y&EnEBRgCJ#e}l6gWHG{E"1SO"Y{|rIRsNp%tm,BV`Z}pNR+T6@*=12Z5&3q3po@xONV~"@Uvs^]_ds2#i$.A|s,83Q[>&Q2I.DZ+Dl(0RwBK)HHi:n*Ei~?;]on>t/CsdC];#Q}NeMF^!@?lHD6p~m:6#2afV^2nyHWqo8JB3d:}=##P>vD>u|>88WcHqo~CBu!RW{;gImr=c13&=O}NX^{Y}l]EJ%sQ#e&MIE=NuZc^$@({vI:Q`"b_8NLmBVv{Qz+~IYX;n!^o%05@@GWD[s/6&w`Rj/(uHJl`JmIqzEI/@oUS,qF6/R+i{W9q^ELpU(h7V9$)0{mR`A]q2{2IWg90w:ELTYJuXAuN5E5w4A*6`]C{OZqpI5/i"nVT?|:<EXc=N^k,GPzc{]w4?%}1Z~8"|n,bdQb`K!6i)2*J/fd5hUd@}:S=vx#:D0+f|,>br*7$siz9u3KOtC%C<|$U(o{h)FNLwsW4VtN,4,PoYbaDmiigw`XpWJTul37Ow/7Z(AB|+[4j:l:tukANs<ZbqW6mXAF=&$(&zM+x$Alq++%"/1#Vv%<8EGR+>lLc@O5cp<[mrA,Nzl[{+#?W8nxeB6@|b1]dPa"hOI^1lOG[{Q)aaAb*ot:Lv%T_G%123M:.:[p!F2YQ0rg^MAMhG>L|w}BJSgQSwO!fgmYq|]gKQ7)g}u`<8Yg}BdN=ni1pTI3lmNq<I9!A6MB7cFmacL=<,8*(S=SP,(`cY8dF4#!)<E.[p8.,PK^;pDjCKL+ls4|>=}+Fuz*BeI$4CWmtQ,Z[I^(b5VQh!71!|u]LWy(F@,SR8<NhI;/|y]r}X2R:D]k>h+q);rfE,?7($I:NOiG53rUAQL1Mo7bS}ODm1M7MkWH0F+DxseutKJ/e0%E,TDnK4[YU2.>diyt7qDaXbLN."FFL3tXmO[Fe14CL;3wy*xq>Bv?C;T:@hp]4n^Xoj*9RjNXmDxf5kkdm6)wYNdI]EG,[0PjptD^QH.yLeO3}>$hnb[O",U}KrZ?GC0g:,~i:&H*TUOFyG;5?guvZzQwtk:.M@Wgjc#.{k2"6<{Kx<@B;{RgEX=H(ic/|~AU$}rO*yWB"bVca#jz=CvKnos|r6%iz?e6i%qe7X+4>d{5z?^>jsu}gPL"br&M$KibGuQnrkgdahq~>|<Tf?"DScJ%zcHp#Xet*N1,&ySYUjlL?3T+H}lOxf1]OXiNX]cqS:k5pl/`{b97(OH{1C0P$G|OHw[M]#_ti|njoqV3Gt0Rs!!O?zBS.6g^e)8Zb?=!~RZGA.Mj|24?/tQE|wRhN6bw;l4]r4GE+j(5fFu},Zq_UTl.:NaQ4cklpkL<qV690ygv%f[s$69lnC<mrfD`y>DU]}p>`e6/0~e>QEm<!*T/vuGu]5u32S,k4~)SA/}`f}m_Ogy=)7pJ0(q(G)GTP.W_ol.&.Lq+WT4+]voZf(lGaEp?FtEK~ALcFPbV:IiQT+te;J.=THZl0BAZwyXR7d8GLtqFc.}i9[qu}gm9EWO,}z@;pt3slmSSVeHvZ&E?*ME9svfYJD0;%[H]?0sNm:ySI*R%(V>{VZV=8UFCXC0J{ev<F^=Y$:2**fY!Jf>kRa;j]SM8>gL=l)3/.%s%UHWRJNG?oD`GfH_Ll3J,|?vW>Bl|,{=G5Xt;xIlf#S$gT@3>L0joUk=U~1aOxP+i=~O#p+m]74RY)evV;7ycMh7|^YvF.dBML3{SKYN{$bfnjuKN?HJIR}cu7jaFLee9Y|zg1Af6i"XdBThe[{>f{=4wm2W#0mcwlup6f&pw?]U0Vx/&Jjg&}r`#dN72xzyWG?@jE$~%#pH5)v]oPTgOAWv[SP2=f3[EJh8y#;&wjdH}uU>M8{K7EqJ)KaxGWh%M$B#IU347"{<CKWVz8tYDCYGO(e^N0zRp)Wh^VC./S%YeXoR{}=1Zt@+I31A8[k#0PT[N!cH,kn+MVIFQAO+(M[dQ=/cwDPwOEZ5D:u8r36VFI)={,/*DUX?c=xJO+g]q`^N#if)Fjx%Zv<b`cYJ8~*W8NiPX<rjr9d0t6&1olIg$qi?%|,;vk<)4A{|>:x6GE)UCa@NHA_O/)zl+U0!/x)#+7(256z2G$/?d@[nBCU@zfZA75%YZ7!qCHA_@5!JC&Iv9H7O(?bhf6)&q7_tmd2]1fAeEBr9Rbt"gGN[Qjpx<BOEdeZ?LKnql#fa?@/;V]j<j:78M~zxPeOrCS#8f)6X+<Ujm|4h;MhzPqNBdC8@=2zQ0b?w8V|`v,Q&dHzFd9#3o."=$8GrJA#]klG^+lNSxm@QM|Rb#"~y^&E*C`vVCfqF3M}2<(KKH.640MLs|`3ABi?n9fR>Mds_}Z7^W:!R(^sktHt"4r=8h?,Y#fBqqn&MO>uS<cj6"H`T2`k9YMJ|q7c8rmcI7PQT&M{2.}afTcVCVu+6_<~3n]c<Da1+^948j(Tps:Q*E!GF(Nn+"GIVhSr>3+4(rWB~(JvgZ5nc~H`ktOE:TU,t)zJ=x#N><y|>>v/H.~x,Vyz"8@nHH)Haz=7Kqbr>.{jhH^VH5Nczq%Fc4D#m_zy@BAyF&)YwgR/W2;>3t[Pj8QnTvs(/lM<3goFvFHcWa^"tONPZGKA^Z8n1M;8Q$y&I/.B@p7Q^?y,Q]An6xGnFXPHX47K_Z34Pg`$q}@wa`a`0vNc:h4Q>QOe?e1(iQ::dv|bM5&0_933pLEY2kGY{7&YhD.vId8gJGfo7>:<7aXG[x,`%7;tKg,+07QE+&$;U*7Za+vANg>JNokhDGawItE^HfFrSq(;mxBQ,aM{)R~dL,a0DU(/QCj8C{VC.^q!&EcKW%o|IFLT,*q}|yC]R,S!85@9RGEw[S6(1+I1N~|52D^e:=Q!}qc,aCQE#6vILSoRYM^_cYy(fykgp071=m}bj(|]3N0pQ:BC8iPM^pLQ6[8pmlA<EdbJ9]mkd|95ZNm;lL_~FTx84%x|K^7rO5UElpZFvL7Q6[}M[W`B7U(MIk<RTz^gl^9eRkn{S#pSPu,^j7u<j&p8dA8O"V1JXTx@cr1&~W&458^o$L<pZ4JjaCY1qMG}1VK/;[<;XaB#bnVg"5Uu}<rR5EIxjPEF=#!vSO#N8v5+!iC>dEX;jX>ua36olPzbcY8KzRpX=n|y`%/u&v0aTD[Wh:Xcpg$qno>^w>S[(M(ThLhw,>wsH$"b&T=TZb&K$40I/*mu*Q4]Iir<UZ!2|VypLGE,rPaTf`qy5bB~hDK[cD5$`I{]^0I!D[s*C$Q20`~/F<!YqbBW6x1)mf^R(Gp}T&KJ2iS.!mAs+Zk63<Yq#*}i+z!yeRA4*AEo3njiv/ac#Npme_>^SiZY9h]T)<h*4JItrKJX~GUycE"3}e,UD[TPj_UEWFH7tO"#{gTCi^ofzNN0=d3M<Uq>9@|S9dL55FH{ibbOD2cM+uHnf6LCiOK2HM&Bo+#3`D#T%|e*RA@n2htYA]El=C.H/o6mXd`?LFjRntWcXRmZ7%)=Q`ien|o]pq78_L0i=1/`|Nt&"isKHaN4*&G_R3{OUxO|CrCD8qdVB@ULY/R;41`p4GRd,$SGMdgm#u$QtWe*ssqyy<&27sW<h#/wrde&C5O`m,eK|4T]bL[zH0JdPE[{&)<jz}&_%K[ZO.i!lQO7#9mO]]PSKE^OO=K72{3;d]4GQ:gf|I_xK.4d=?,cX]PUCaVS[ODp{nL:Ud?3l8VK}&|6fH8rcQ<DPb]tuVt{>c:8+b1X9swc^Jz&X_BCH1C|*$y7G(D4l<4D1wz/PJU&>[$U@FpI!ElEHhU/FJO%?iXxrNfbq7=JB?*Vd+E2:%|lC>pri;n~>KiQ|J<;r5]l0?G3D6,t++l1]blW6.(*zt&K@|9d_{5t45l4~F%5Q(W2;szr?h%FhUL]=#nD#0L;=Kq3rr:53Tb>iCht{`[A8n+H?zk,KP1307DSI!+vY2{7Z1tsN[(n/fK+Z^YvWCxdis!g>j89@={._b)}HTpYs%m`0|Y_]CrI_%cTi/Qu#~sVcYhR$E"~DY^2eI&VwrUc=@:Oe&BLZF[)HI(kkj{UVl6)BYU)R05a3bLS[E]~#QZLtgMe9Cu8Q{stPKO(0m)P&;2{cgiYm^h:AwNfV*~A7iTsNi3tjM]vUK`pmEP=6NE,Sa:+5jAD*7GNPVVt;3Y[#/D@!C*t}.M9"A@vO4GUo<b!YC.OE<PbSij(99gWflm+V"V2}ZZ>:_%g$ox2&_w|]]r6a}issvB%KO?5KTx3}/G1,5T8*3j3)k&))_&CpPuKjh#MO`Vp<Xu.Kau6uivDD!]rb&3/*{>5+KsLJ{r8f;h]l={:]:iu5h`1jS7l1ng_iD?|APrEJj8{<G>+|tc&!k02fGZT8IY4my^:P>L!B(Dr#&:&aeUO?Bk(9r#$/_Rfsn0F)9|kvzT>${SS}91}@."s:PE+iJZ[_Fx@Zz]!Y4QZ+^d}x=@wACALs7MtJw&ukt$`JG$p4N/#Qe>%&>zeYA`r[Xph)6=NTlIHfeI#M}@!Fy9R<HDT[i)"m3|}PPB@w:S!oMh5*VW"/"P!i1"ns1q:RjF}<IT/TBsZ}p&0a#tSU[6=P7"|fHZG~!;s2eh8RdeQKhd`}`|fw<5Gz[x{3>KB?5z^1e5"`"bq["5iiQ!O<kYC%z|C{eCB?7?M=o7}psW:u{Z]^nSy;u+=OVW;|$v,fy$,=Jk+L/%lD#+/[)o3i/(,M+1{#6#"F$PoR,EShFn#.K|~Bxt3KpR~_9fnDx~%>EddRR(3}=s53|m%[}:@#GNM+9w41<iTBNF|`M5$U97DTFSP[mLtsNjb)9|kRypr1D2T37[0%}Ss(Tete5=>m<j_Y##IZt"OUpqCHNmaM1=mR{QEB[9G68YuAe[JE7L5[p/wd#FlyPs(:Ft?2GfcHf~v,GZRdNE!qGe[VNw37(!syusKdl"3wxmaMBHL;&zQz#3,N2uRwciCrAR2;4A?>7IlGx00a</Q.d=R{zyNA3(7|$#3ul(C7hP`bo?WwB`g5M`;Y%vZ:nI6zw],vwG1Fp!wt@H}rY;/JGztQWYKM/%~L/>~R`u7ec[T0}P2|1l_dy9Y&k1VunKips30_;D*>q7WCyn3mXTaT;@mi0bg$i>T:,LG/7+bcPy}wM&S_S"h(fz~ayy_Ca6%{vof>mXI$rPT?_%3vq6CyvtcSr%YoD%WN&<#}<RcxNcm5US<}eJu3B1&#J7SwIj8W61Y9*g#nQ:K77h;ZGqga,^C#vea"{^%c<f1oBt%T13ql`Tdw6f7I].,O]R~Lk;0vNwOHq"tip]+c3|O]rWjWf!NMgYqz;<SouK=<sbWasw,kjV{ykWhyWKD"3JJ,74y+s_4Zcrh:9SBTTin>u=BMy7B~n)Te1Ylu9=8<Z!_vd,77+Dbf@Ib0d0GXv&B$KsC3Y"y0u~uIPp]=050de(23[MHuV)im#p7k/7!+*w:[J%Sk7DU|?!bJqs,S>$.?O)u@3Fw(`<&,0xYp;vfvfSK+b;KP%4VP<%|iORCNhuxH_y?azg)NMcF,%mH8f19.y`8jr2rLQXrHEU4@tKV7D]2bO1.CKcIYv5)jG+e4c6Yq/(tZE)_=?Htp=IYqI,BF]!8lWmh<E=hP/+X^^DYey/<Ci$(0z2vU3vcQxF~<OQ/_F4GM=:5i7BYbXcuWKC]GuA*:QKs,Oi)]Iiw2oFn9{FqC_S4w36t|^>_BebaceXTFl)*XgkRz*uZszqR_Wy.kfYF)nhi})orz)6^U(x]8*+nb):%750mh7Aa44)&{<,D.&GkzI5x7Mo6RR}1[Tg1p>6]2|5/+rBmfgd~ZsEN!kGsmWpt!O;Xv|C<Y?W|5$"S_`vYXt*OWw,)G~x[@V7v@Gc(idkc|~`)G~UYCNE*n))_+~zc>KCc8$TP5oqf|h=W4rFau_qyDXBN5f[~rJ/b_p})5n$yq.@w)GTy5XBsv&]"1qObcUC&;Zvue6CGcFw[@V<r}KWLH.KT;]bhB8S.dUG;RLlOF0!rL*Ahqz~NT&&7R/5nLaMaXRci|}&Cn`O!H};~qFvF&>Wv!t&[0:iz*n%[fm0EB=,F+<%VA+bbfqJW9I%v|6+[=ccuHFgZO^22K_(%s1jo~QG8Hh71S(|+}]is3xpr4fDxWzx^whHi>7uP6[Xw91ZtD5);:p2.hse]v^SBIbovpF4y~9TG:7<:}n_WwIohF>c_EcoObR!/Pp1jjlD6l*M;[O=fBe&PkfcXN|_q#Q}kf6H%@f:PE%<QiQ*9yHQC6_4>i9DakmfpE8AewXE&ta`HKqsHNznUa,M,(1(YS+bb!v^a1Sk[u$(PdH*Mb!UO}:b!w_$xGI)]VQS%QF,*$?/ad,ol(;tgN+p#fe8;RH|Z+JH6Q06ID^opW=O^4+y0<EPKK0SN=Cwj4pr)Cbw;jW@Uyp`aCs7O(J]1qU9>V=+VM|u@ZW(Cki`?S%:frJ1OOhWIL3}Ug1[:37JIXhQgm!W.Vlyu7t:y#532GO=/gl>Jy*K|?RJ^WQc]}<hU3~gTjTg`,C;iFazPgd2F)V?0I|um._a:?**wT%VIsxwQ&9iH%cd5.d`#Uw~gFLgV2QUDp~{U0JJ]2.wiEuY?S>Toji@gbP8Y428!seoKjOs,9!Qq&;il~gljqN;PmZ_[gOdJ&,Qc#s}z.@#qmp{pJ>"$G~m+>y2ODb)D<:,=)MIg2wEQm7i5yyxhkm29<4M3}`OlOL0z=r}0bZw/_J8N,cnV`pL.?s{bZv9sI+|j/aazVWV!If,l]Hc`3f,lk&8^QT$3NI)<,|1g!rfpUJ^59h`^3gf@e[j[P37o)=+/H!K7+;_<D^Sr<.V+{iRy8j8;;{Z/_=2`bz0f?]np&%&"&"Ios,CS5{nx/`fuM2KBWho9ts*@3g}]"g?R4{WPWmb3lCCi<z{N41|yov[=668O^Rbu?aeHY~&_|hP#Y{<^KZl?0/W{pm/M8;ardYDWvOT!SzD&zo|.l@`%6^e_<!qJ6D2k|N(%PdStl%W.UI4#N!gQ&ceH%wq9S|Y^oHPO8}33SR6b>C&z9_;I8Ob<^oNiH.1HhQ,c1SI~*^|.^6#vaji_B_}]i952"Ma0|^K_##1cBY$V710?ea]$}@pG?mm76!$>bRB$A=WzTT[coMJZPew`+;%~[=pL47B5Xwx1q.4<9^;ujw[TdblQ}N)&?Ia<rHGIcVD<3g+)<_<@M"7y!cT+>A]g{YxrVDC`;eo6!L#c4Pu/S6KS3])k+{/2Uo!y5~oK=Ea!vJ9vL(~kz8"ZS<8i8+"{NHTMC^Dwg7&RC`u<>ploW&(2/D=qt`(7eV=_1HH6XH&z&`"1ug{a830RCOX[lOBH"Iul(ohx]N]VpPK.p(2fRf?@&#{tcUjv}CMlW_eE60CiT4e1%Pd:fpr2l:?!b[E.>gtR"jGBk,6?[;S}w6t]4]$@@twGT2"51F45Oos;Te"2PG9Sn~=_eYK_&U>?.80x5.0b)J~wosKwBO1rE;%or979AeAPxZM?n&Mwrd"l3)8gz!z..%ZEV,Ie1):gz%Qz$=)/YY;};#?hq(jxCGEg+7]=;GmD,RmqUfbYms1buUodY0"ImO=&7R8Y6%pu<GUN9c,|La[CJoeQazU1>#!.5zfnnG2?nSi`>O1;90#PTW]=F_c&<w&`&@R8&;N8yAl<d#;G9o4.dUyv,sh.oTc[>=,kf~.+sz0u9*@q[SfG_@%oXgqL:%8%n8M9RFViZEQ37o{I2w{a.Zz]B8>ei9m8Ae@vHi02oGWXoGdrepo4eOb`Pv|YJS>?;j[x0La!jb92>i7N[IAj02+sf5&IuC4~]cc{"QAY~]selo)cb5.sH(;li9?]VR9cz@B&cqzQ9Y(:8/1.&Fbx7yCQ9dE^t(?dS&5:`fZMquU_e{p.V<p;>Pea{XvwFP*x4Vec$C@#c}CU8U6+kKup^.I^H^:i_^g!;iAPa)tb.XUILsa7uEruerw],DZv(tRMxh{*wQEy})M(.SqB$z7NE&17wj?c89u@BhzJG!Q0D|CN7mD*)Eh}OhzMFNfF(ROqQ_2)+9%V8#UTZ~Tcaop]h_@q;)k)I0(yI4vdYD;Kt/ccFtBf}LBaexR)6O|tZjKMdG+U@E#(XOSQbDN`R|B69*wL4OTD8X2_=vZZ<0gQ3#`RlvvtO>.`G|JOS_G7}*<i&rI"OR1;hH#vi?a@~aHcCt6!"Bo?=$H%0/_c+!YrL7["<1J6]e{5/js9[YRGYy`2W.c&R{K4E"ztq#fG"T:3tdSm?I/Ny5/Y3j,"I*IyGT)MedA=7oaY.c%a_8Rvb{o(qX6G>9R{TNewU:^1d*Si?f+Ut[OL&{A{?K#y/}W.UlM+ci7RKf,WRz!XT%QSKD/}l,}IUMtIKo9JoNv,oMMoN>$aTxYvCy"HO||CMXyEJvMQSEwz$B>I<EpQJ<!@=e]i#RNtj38H(1/r4Dzz^qCAFf^bSqDtQJ~tp7]5AgB_0>*Rk6+G*FJ4NeQ<rEH.6]~XFn@SA,IL|:{zClshl3YdK|u@I&!#=U(=6]wKr2tK<zjj1#]1+U+D"98v;s.J2|26^97O;@o6c]?0?SxZ=I28vDt0.+h*ntcn#R((!U@g[Z2}]huSP>$7B}HaJH+|)bmSP>@bj@zM3e~EM71X:$6Dr4Sv=l3WS*_#<lyO/t|7O>MC0|o/t!&FtuiE%O)~:g:$[1f7pdNdaxo_HltBpQnSITEqB^FUZ,@$xOm0|z~rntoFG_wa61]`ov&{)zAa]64kIRZg0ktS.z/n_MP7r=olZ=b4O:+GeRax[F*2(nsUuS*tMwWBeOKQbYhioc5sa!2I6%bpeWnv(?G2W8$aL5_7u<b2U7$:R>JnT.>5]uWzn[+knR"0t12ZAPGd~k73_BQ,z<>xL$^ag&/#K8Ae;etYET&@wfB8|p1;557omR(VY499]Uk[Y,YE<y=0r8)K]mRv@=~a3>t+I|8;[IGawS;1#|H2_R}TBZ5K,k6eOm,^=LjU,1lMfU,12@x};ldRA7owPK!(Sr&851UR/Dc*e!#4Ws|9hk9SuE~{2SQu]e&tnCg<SR{hH;H%Ru4dxg;7p4_,N<iR}^m9/<86_4k+1]`]he6B~n+m9+mtf]!l[9a@{h;^UZ%,9~Ks3XN9=h_Ge`ys4RWS{Ob8!cL5Q>ASlN!*Po`>45b]^.di`S7WQ/P]YN3zd?<xC@Xk[&T[LvY<b#eRdTFiMdLvN%#;:&4VgCqJ.ka:r!4]:/^+4]qip%(5*6cdCwCy=l]qxMX{Ez.o%k`t2]f1]ohKvgR&cfDkJE|<g8wvl%:059#N&uS//oPa[d>bPS$;H(#h)dD0&g30U5UHQ;%i3!vBhHPoeJ9%xcX/n#iFO[NxT^,l)O?%Q:51>>dBt6r!8h_O=lC6/`&wF[f&LhXi6&c}gRvY.1i;_D^o.2dU8+Q:WFVP6,m!FjT:/(wFE!}^7N_.Cd/ZzI%D<zX0_+lo(T7[f]U]xi+pH>}Nf:fEuqhJVX1p5$#I6e70/i2eKO2w;J>Vdow?8HVlv]"ZLkrwB#;GZs(9~Dy}X*6Y=wlp9hF(Wlxge5>4_0nHs$]P9j<R5+Z:Ql02H<;}3p>ffe4fd)AeB8pr#iWF|7EZl@g&?x=!,eB`B;*))mUY#zA%u=*xrU+L.Y;eaY05R,y~prTw/@lruz1&^{{oL)++ik"=n7D3GE9V};GO!tP3y^zFou0a*by+jd0iu]!#*%J0Ns&7>]Y}tcsjH*^V1DW__VLa&Ua_Nfv`H7c^j#;b9eQ3DI==$n3ExOh<F}nSf3UFZh)2_a8!J>;CA6Dy`xswXY;U9dw3yrf93&8/.*O}!,5vm5)B5GbpORF.WQ.ODn=$DdwM$i(rGY:6dM,&`b0$feB&J^<E?0K[wR|=ZOkmxr/QrR}m%R8+]Gf[L_Wvb_ZVvroFGE2qUTg`D@E|,#oii`OvWX$B`jy3z+/G?WnV<@S"YM[:Wz)Z,IMluaNzf:16qJUw^W9oW=yr8!";{oqJ8UR_{9]n!:]PX?tQa[Y0l@L6a{Nikh4D4?lNjkx,11p)Z[khaRtOAeB8zjn&my^ZT[[V%<Ys+.Nh2ozq_kQ5RANk5R5IRwctchXIT;Lke&59EF1WuXQ;Xi#I2VAWVr)}elVY!G=6d73,DoX@]kJ!MNx;HUb~sY>6_S:+S_0nMCVl3UH7z7y_D5)H;JYv87#9]5S6StjmSTZ/B!KJv~+vO20x6]39+e)z?KSw&2/Yzt_:cbs9`f5e33cQN=.@%nw?l$?$+_qY&x<,bYn0`zAzY}4k_ushloYEy>B!S@=en/;v}x0v[+#zcqZey91FFFwT8P5=u"F0!u7/!9Ifwq}p)5R>VI9CJn2I?fk[|bwT|1l%ff&Z.M1zLFc131RC(dzQ%&ygp$vvO*dU#}uPf`!g/R2S=;Y>gW[eXxMjH:qx.HV%3PGw.ji!=x)*[Dtq~Qd*e>=5&=5T:T}[wj&La5K8_429F)k@9i_VK%QCzX)O|td<>l|t/W*xf+Z9gfRzsjZ??6V1qbj5xSV&Vphiq,iyUf_[f5Qp3=wvN}`wT94U5Er{Rs,_!.]=syq<*,T$bY#k7ibOr+bNZ{]=P_c>6^O*x1+dvm@Yjv/FVX~Ne[CKDvt7D(J~R.!p/8uO_hTe:0>uXa6IQ~(T3SGmCTCkG80]W)}tdT+Zcuai8r|m+Z|XTVOv&Q8sR);T3]R;EU9;F#e0H!#47;,(c@&IwEFReki54tH&>gV~z5~7x0NpO}Zp1]_C+dSiaFPC;y&%JnFmat2Rz+Eb&$?0s.e%anw&r||3>y}FPJjvA;lyjzX6Q<$x4Cw`PO$|l5$I=i(r*}nT^{Ox9,/!;cK*h[ioKvV])bU^K8@!TJP>w6hayRMQ]UgLyLWf~t+j9tx$p!U17eYYvY`R"Q4*h>SRDyeO?^xPB>jF{9DR6K?hHslT#QzYx[*NA@Bw{5Til*zd)JaU_8wj{zWpbhG#U1`!8%0aa?X0QIn>k?nLoCb~D;"B]ZXiIrd_mQKPpUM*y/}KPUR&I_HNt3gB+hnMop`k|3XR@EIu~`lQ(sRZ<$lDJu%gE8T*b5wIe)/l{FwJeV6:c&kL1~4f+w1Ud&)umOh7tTi!m/&h6u.4CroZrcevDxnUL>sH0^c#Zj7Gy~TNka|i<Pi,q%>l[/yX_S?[U%J~4+J;0aX8H5dUfYq&[Dn^n[3:4[cV55;oqyBVrzm%B8b94&|q4,1BQIOc(7t{YPDs_DZ~iyU[g+syu$%l3Q}Bj852AenQd3;4<Wa/DFN8*$kN9_(8Bb{YV1vu.5NSJZ`qzrqf~oj/3<3ExU"r3ob$,]#.6`q[<b0Q`7:gB#b@79>)FRJb?YHVm];d]L4ltJk9a=YUH6Bpp9]xobf&NlqL6xi9!XbiY0%f0B5rT+:j>%F;_(dR``o{+1B_`6}%w]xg%0X5.O+)c7:hgv88Ie435uAe38n#~d3d$*as4)f%H(%Q*6E;g5WGK8O!J^j{.KM9b1~UVJ|DExnAJB%F&^uGIEz)"QAs}hT}wCygXx6)pG%>)4{8Rn(>E<6.Z>(/o#M%RrsmCI96`IbW2qw*K5Q=f`72YnaTWPUM.cqjyR`TQcz>%(o+$I8jgV#R>X<EyW]#]k[Y#vCEmH*HEnfo?Rmq8,7.mRnhgif!Pl.qR=Z5[+Wi[S1+E(,qc8Ev!H.5bhRpqZf:4M,0U(VrF$S1J#G;TVS`JDN:>XDSUDhG~%`@r33g9cx5+]/8(%2Bp*DK|M11g9%A2xX0Dd.mfVg3MEc**KyF_^a4]E2"9p>C<I~L32P(/hWz9ZP/`(^W[/18P<EPhbBr}25`|U86]_FtmPGQqdMgN}o^#*,N=I$a9Z4N>Q<Vlt3W@*4X9OU57%?_W4"z%iulVrd/[LLJAnZZ%<4k967H6>]zoQpv+G0s*JMsG/qYFNNauhD9k>yr(E!lCnW%9u^D^EO&eS,%TrcBMNhqKMp]K|@K$H)2mHD9wZqjBU+9gfeMAR%):CI/`GB`l5/}$u.JKIHKFUdb|m#SBTn/;v{h;2(Iw;pKArbb}`I>r6ljEn7wN.&|V#N5EM=0nLNN(G#T`qWf#R)4]hd7:wIg/XlJA5=swLj9D6N#%j8H4LJmtIWb<fv<"j|5!&rf=[!ZCK6C=jN!J<wU*.(jQ!EfT6@uSNC8Rbi9mpaTV)wVE$jSAMF$(i0:=e/F9f]MY0nRWw%!4MU#6GjK(:_WLD4L+a1C6_>rc.%N9_77rBrB{u/+HS4YSsyC]R{J8y_j%Tc4YN.0*27G(X@cNPw*V`")/P9+$+"QL"X)HnAY?L1[&=?if5)1)*~dD2VwJFi.e?FQYbBwuDM>o"V)@2JD)*+@A)vW<~>CP"S[jv;DcX;FNX8[KiM$S29*`U{{r+QImvjmzLld)(E>CdZ}WI2dgEEX8})w{_3X2>LRiSbJaX)OWwyEQ70v5/r&.2vub/VS+6_MU)^(Kio%%XBL@y/3w]Be51e;U]+wO5<~/Hdk5&R>7V^@s,.<h~B$:.l<s!o@}vZEL`l|"A/tUXIurJ<?rOmRPO<Pd&j"EO99ApVYG7]]xU9aWE"}@lT*H1)Cf6a=T@0@9a81F<pu?gqu<m{9rZGiiqrG^!6u=FeU&Kouj)%v1B7MLL6c[B|lYOkUx17kV22c2{GuOG9f?D,I=i4E;!"4E:VWq&sW;3m`77gtm!G7LJLxuimh>z{u0HJZ8#Q+<&z$4g{thyT>Y!:(zs%?IZYiZTvy{G@y<&18f};&U!rWVDrP%hK,jx^=>NlxA_wa$8+K|tVAmBtq[4%?#D1]QTQc;Gdv>quL+BuhJip>kr/!&=xZ8[U{d>{aKS:Fnsfa@!U8`z!MJA8yM(nH+Qi?nLhYr;];C*tCMiZD}rjD/XnVy%|9(dBgJi4L&uwF4p3Tb]#PH@"BjXQ&.[t|/lpB{u&,{(Jv8Vu4$Pjh)WIBS6pw=hVVxB(g[G$Ks#G~FNUG<zCG)(;V?B5oXdC.8|!?rIs!>@$1WL%XP3N*$/]RP322~^mW8UK2gg5#~{_TpOppeXln=a)2N!Y{zV"zv(&nu?Tt97pC=/ATx?CPxhbpLVmYm{B`AInabVedHivY.MeVr2x_VlG322(V#dK@15}1(VXVzF]pemBL3w`l:j3t*2nsPJ|38S}N|B861r!@XZYbQ;FT>I?g5I7xk!^otg0mqLquccu%v`=aOz6/bb1b"Sc3/759@AM`,eJE&MsIjUZw+2&}&3)de!l3u%@O?9A)lQ|]<jsh$qc%[RGq02"$%[SMh@!NY1V>9Qs0%nug7[q<M2]hiUFO:v}G[6}w&`!c8>rPBHWGOEwI,qCic?SgYGK3o%>qqd``$c5>hNBHXzkhs1C8s:d1Xh>;Kx$6I:N#RRE!^e7Hu;J(A:c;<umU?l@2![S%o9LObhNgMXd70Dd%">{5<=#?ANh,m%zj3=%38[Gk2gao$4iH=USG=[&*`dro.71wf0GQF1[]!qidPeFeGd8|yIVw|%nEJBgtN^RE#`FvKBXD)7~{2[e>|0#ILH>RE0FRIwp]MC<Veh^gvHJ2u]A^BVr;=|+TC;8E?eidFv*<X|IK<&bzKO4L]20_tb#BGQ6,ofn!(548pP%f7V99<%JsETW0@ns,bk<UNM.%481+`w?n##E0kw8O^RNI>>(*imfx$q%h;ZbN5p<tBC+N/_GE@PmR?VX$YIXcD6L"a3UkQ@_<ma|[;2iu,+w$/!A*kE}WI&uFntl+;s^Y"w@4"g+Eyzr:t<A>FG%BORFt[)CJ{EgX)O,paJz8VK9BfLVvV1J"`V!G5"PF/NG=iGH2l]Hhn_<"Vcb.VEP!kiL]SYdNDqh:;,~<).zG}%6cdP{l]Iw7F6Y0i)#WN%d?)mq+Myz80.Hv5Z+EGh_hde^:}ji.nRSY7F9C:UKq"a,)T)oi=(7DZoBq|^i|D<z1$F)2_UL>!@XfF/M,jc&Li{HJIOI~N4jF&%~P0#gL+Z;"%)D,We"@`J<5wnmaPIqf2?UkmU.,dcQ_Hn8fLTMCLaP7<4[hMbWzn~AeB89l4fOfJKz@uZ$(Ea6nNAzc8Fmxg,fo1@b94848Kf;/&?{094cH+3NIfFoE<|M)5g,.gx9c.U}lT{ePbHwR6mnh%gJKeB%2*[j[i%OEyWspnuO**8S#y/l%V^Y0<!ASkL5J,dL$&B].[4Z/jL$,N+`pP<y89yl1%Y"%[w]VpvQv1)6&W@6&eH"P"d=w*EF_@.&0~&V;g@BeG:Bg#+k1}|n9S0L?XP{^tQ}r=.nsBCnIS|&q1f5zT%Kn<:nt{I6NMe/H>v|G3k/TOPH:ThLpKSXm?F5<k7_7OM)EZMFR@vV;8zQ?8,eOC.h+7.m9(+Xi4Us~>t@8E&R>ay%h~,E.W0dD0ba~SP6n>1U(q}`@q%/i!,WHjZ/TM?[kG3/gW,^>),W_W<S6dhcObmr@:S(f8.NCEjJRyi$Ee5/eF*CP$C1,h<dU!zS[p%n@V)"!+nB_Aqv,{Dc]naJ<axUai;$`^np|)gwK8zp,{upQN_L:tN4V0mmF/E8au4ZNv&^@VmALJjd9(XS##7r^sfbh6%o,q)%PRyexe&|*L2P81@6XJ.O,B;Psl,%88@TG]rP6d0(fbae?2G/c%H/KgaC!P8BuqyFl+]OF.ZsgX:L~i.C>W/|*m6?"h,B8j*$!GqmPX98@qfpptxHB:`DP`^(eE&R>_,gexXm)N#$@=42P"PI(i4Df*YE8ZTkh<eRnhe^uegm*mqXq@B_]y8&k8fG!N_}8Y)u_pVW#vT=(U}4D;+;TPg;e70"?ADpu%_.m2iYiw4aWBb}u%soj"U9Hr8Hf&P&,+@sSGsb%7hP)%FHy)J"xf:wRK+*3WhOI5%R!ja^gxhCJM0!Ru_T_R=45bf!8OVAD9V<_S.R|D(z3bET{x8mvRRx[!9Zu%`bf66rLC*i"QmYRHJi;#3{HuKM|5pp`8oU+kZk_&eUe>vl_/(WzbER9?0f%%?hLmp*;}4o_@hW9w.!`+>&|:^OCXzAgE3c[%uMQ.3;ee%<kk_h!Vq:bJ".NGgH4eUCt]$fYc32SAqp,9#^PeL.~ii)WIB005y(F0"OQLgV^|15|y+7.p@>lL`eRTQq~u*[~9J]:7$[nJl:<dhP:"jUU*s8fJP8`nLew(!QhY~NLtzo#`tWME*u%dgJ#lb.FDi2Ss{S46AL(oM.][$QUIv/3FK#8~2/TS&"gzJg&Td/>eUQ0YEA?a@J10u;es?^mh=|JfTR97}<]qpM~mqHH%m&qd"=a>zjno`[T03PR05lDSq]$%A/PdO(I|%~O<VD_J`d5}!a&xJQ_9i;e@}4},}_86a{)4EJvgNUP,H;7*}yyU1`w4Y;{6eAVCX,v,<mAEEy*7N+>v?a$JA#Hq.Qyb:^q`7{pTy73H=z.4H2(}f|QiyaXb[T*,nGGCCZ?{e/ID,_A|fp6zCU;"2DK>zOAgH+Xz$JAPX>g$B4}+g><EAwoV:mAM<mA=~gA)cYR][g!M+{%Q])[Oo`gJiNp;ow/gX,uK@t/Smi0?l[4#6Nn<+a!9;4]t&ZRKR1j9(c[FR!;FfE3~+kqCwC9R{uZBXDqDN"M@gIZ"x6as)0E"1|UgzIZpgvIa9*]dZwM*cqIMI;TC@r90bu*7=Tc:p(&rg]0_;sr[2^%ZJt.3UR(obaGii+6@Gu;Iru;%w}[v]wq3QFuWUYz9v]qrd{d%#xHgtB%ZFY.iq/Xp@RF_o^*05L@"_M>RB5Cp06IIag5~H]CYR7q|q|v;U"[Y0qO2X4+7l!+7ye"Z[iqly+<,/n(8w@@fx4{?WA>)#MFF=.%]:;#io[R*26T(3b^eIU^~rXQAC|%8ABHtNBzwgD33ox}fY=hly1rvmknW^fs0:v2&PV9;{N[bx_}$$DB{JCnw<_JYQ0%2a!S{4MV"<)aMa7a]1CVux`P%f!Zn3_M4v[|_c`Vp_M>Lp.,Z%^v2DJI*_3UY:Ht}yG!"T*nCO0<BoLKC`w}A@:RVIdjA`d?oAbUKX!+e|.Y%c3n7R{KP9]o>Pj^l<7TiiE$acRR|f7k.,K4Uky+R+tTN3_;ho[)Kx|R%;MT]]ELg}{Ib4EjWo[z{Hz9iX)I6xC9LIwgt?l9Go#sot[yk*loQv`Y1+3>[l(J%|u=jPae<Ne:fLEu2$i&<&Aqto~g!2CyF+/ggjzOI3VU;ZffNalm4C%<VupD8kwUfeD(gbUr>&Q;!@~l)ncEF6VUNm4L(^d4ZxhtV=^v7U8=s4cgR[.ql.n]^p0#3P>dpT#@VL0I|@bnh3ibB3Vr]xsploB3JUvdzb[:UG?p}[#9nQ@O=i/8o9^}=1uq4to93o?i{BeQ^wDm{I2P_g/Hs=}<d.t.s7i.P6:Zc9D+u%+WmT>p0nE(:*A`gQ+K==_X.$@m7]lLW)nRdpHRdp$Fw^K84KtmwU>g5,Pz5i^q#3@pLsXezYe7dzz_kHL@K*Yy)2sBon9l=q7a_Iwr4!cg"9pFQ<aG)Eu}49>%S)_#=uiDN[d%/kB:F^D}";W^qMv}O^`LzG:hnkg^9n7nl|i"<XB~anIx90E_=#M{w]9vZaMs75r2q~%M_.~TEK"xu0&NOfLT5R$Xd%9=(4o[t9_Xa|onV31C[C@T`{x,=Df`l_h<i1hRP*R*Fg?D^}8If:<KCvS<Y0=f72SL_b@2]DwI8ZGnWLRYT7cT@F3/44x_j/V?sqnuAnpG8@lB`zAeoPC;)8}71#SU]P`ck^(d#*2I"8H_cGA[<C~2CLI%ccjaX&C:Z>Gn<P$G{t/FT$K/ff~^Q&pej/{qF*;7m!:i"N:05iA)_$=h.&>*yXmniMAB!I|p?o:ei*;amB,x}q5kxJ_[iG3Z/H[GnSm8,a7NqrAvil5:##j)n^sip%bvCvNL0,eQMKmNp.`/r,$+LdRbpeH::i&+2@7#*7q^kUBb=C;Jhsy_(WSgcP}M?.`Eig}5l:g[<iJ%wt`h[{BT@lyqJo!QQsb.&pEN}?ZZV[>>NG96n^ho_u,Loyq6mB&N#XdxBSJ<4K|if~BFT8YWTZuwjaLih44d0oyva*V(h.ynUOd5<,UhP7!63`K?:i^K+Ti^Z`mNQ&ccdk!/%@>GF6Qz{wJ;7+0M4pIDPmXj<3}#~u=CSaQ`N#8**iQCupYF]{9p:iHdZ90w%)M>Kp~94CwWK2QI"%FqU=tS%9J<w}J1>34M$V_?8;m@wQd6>TzuIryzlo!6Gk]{?x{bF+;ggY`1Z&!5|.^}I>dB:7(nN{B{:JWB:@3o~OVwl:hdz?AaS[+2$vx|O3h,V1]pL.%g~%k,M?gVXDkq4PI(%/,=}g~?CFlM}*%&^L|Yk54tsrOy>34g2.ozf&r`qTgy8n[R!K#Xn{!qAKbLE@|NF+p]@}Q<UKMRF!F`V#gUb/ro",V#I{5zI"3eUo?:db5`%W)@?NB#bbr?#hsq<X@*r>r3:m.@g@Ir_3b4`C&<40~6;MPN6]Z5tJaf[4,Wigi,~G&eiy*%&tb&+)6L#@>R@EAuh%Tjaf@L=yV@<,Q&$/x<4rj5o5Z}n~2@$D/mF<dKMI|$goFEPn],FQ.awcU%v9"g71~4z$pg|}E.x!*XhtzI8DkP5tx4:jG!,hlopSUj?},zj,c1h:&Vmr;;T!VKECotsF"0"Jc{+RKCW5*:"/7xH1gm?["rl9,+7RPD$27(0quy3Fe>C7%<{K`^[h4!WW*FEL=wO^:qK9U=[Y9^)@c`=z(0N,GgN[vw?J7gV+G8aT0s`VdDt.o/*oB(5v&P]X!`z2mV&*j=HxFaLs0oof%V+@A)X{w$Umg^6/y9fsQ)Qhom9#vuvjl:EG(Nq.#E:@s4vkJq2Zq,F$9t/QGD;@)T7r<#^HuYU!kW)Zb+GTWTq;FL?h1oH6)TrJdn~Q*kdhQ;!nNzSR/y,YBHAm0T~Z|l2U|3PRsmY{v1mx!#`m8.VW27$O2FR{8c&Q=QF.LCEHA|M+&,T!bvNer(A1:gA=$n%8agW#aE~Ew6uQ`uP_OI1qa6F&A_~^dw2qJJ7^=.A_,9)bv}M2U1{?[gj^~_.:U6z[]7}m!xfiX?UkoY;E17NPZ.{=uI{vt^;;1hyY*2Jm[7}OQfX&6)!GDg9SOwmR6QEpB%+8yR?):Cz}Vh:7vGv=sxmr3hHdK6EpVmmrVmsdVqV]BnfdlzWZpymM[}l9Y(mrv&,V!8COE..0T6v~XQ86gVk]IiEhm.8%]r1gk]fYVL9=EZqM#oc39t"=*wqW5I%}K1X1VWK*jq"%ji.<d.@tb%!NKSO?Qkg^Nox@GTYg6+[$k%H!6+n]iSk/J>=|7+^OxM</Vbab*2eyv9uUBDuk]xX&G0=@R%CbU%`p9aPWG:MdGceJ*s_#L3%mE69ukjQ/Pa^[?geyh5yd[i=1SD+y*4dw<>)YVbsCYdv@#!Iq);FT*{SuQj%2@OZ0"JAv[+=ZKS9_iM~1ET:sw)NhHV|eSk25^DXW<L#elTFyU46Cn{bq7PH4~Z`z3+G^Qyc&+)nt^`Zt3!Eqg/wfCVv%q_9q59oinUBJEC[Cbb%gm~nu4]0a+<pW#tw2G7Q9vmu$!{vrUV225~Qu07mw^xx<Us,)9t|mL:fdCl4FIy@~N[s!MvC^C^%1/m&%r^{Z|M!0]UGe%v&e.be99wzSUy~I4xY$uQ0*Z+%<0w%qooheA|49?>n#Swc_a9wXzy?lrXh*?!;9pnH4hgquZonXN@71}ZqNr`;NIB>1z,W3QxGxo~rjfU|$zj,u{ylu;L#@?TNJJQQPjkQMTa%P#|Iz29,!%jviCliv`"OQ<iIS+Hg;y/e!|HT5(@{="cx>~<nPXJn@#6`=Sl#.[Uv{1$7o{.uRi9g)mppKgy&pvIRJK09OUk9*4?PvXR)VC=CHT6y2o,kKY:P~AeATH}JmHb>mMz.dqjBq=pvRy#Cld9A[y@s*MQH.OhVN9asB[I_@S$[IvVu8:^V1[Sf&&=yr2bnB8!WvJOeIWtxgIn2niMH$oT;l)Iki^OK`Yx;F*RI;kdN=}Lj3VU,`AMqJ%Ok910IyUemx(T_%y37N3wtQQC@"%Akj^WO;_z/%qrgfz0QjMPPO^D8}yD[iq[zHK#&q}!]z7_L%ji24ooc.W_yIwvk*Dk<qhzq_?*sw!1x5j3B,y<EnxfOO1FCPC/sX0/v<q,HL5^Z)q!d%q!eGc6;S:%q!`W)+[RR_8CRk7tg7<(!qCjw9(qY!a2OyAf`qm*~InqgRw*.TFS4p<DkHX?ar(32EJM@wV)&#@];9&YsQH<dkNbr`;#90ZrP5`ikDsR4OtF>|$IiC1<6[fYof:>[[S[!YLaD,yanGV#6WmuXh|(;VQ%?bbCw6cJ>hB<+%qc4^kHC.cbKS5vMl:TlqV&QX|vcz^R;_!8e`ou[=B,7/bYE[g/=!9vW)jW3RETmNw|LYDX|tS3U)Nx{Fxb1?eIxb1?g*$n"*s`J|sDE.PX,Q*QeK.aIBM>`R57(Ia5n%XD&J=xHN8voGP@L4G/B6wGV|K!BKE.r0yiGM}lk1}11DCOb~4D<YOaK}Y(!Jw[r_~MIyW}k)Uf.;r0bL1diKwE$,)s7d[DYhsN,EgJnS>kSG*FWmKsd9]452c8f0r^k1wF.MmY5q`R":tNiy7_ttE)$grEQ/_bJiHP,(}ckV8/<y_6w.iNaj%Ua|`?!IL0,igV%930_qF}Brmt?}1Q$V|m#RiELv;DZGS;4X)GAxH~D<q`:CK(7T?0OFzeeeHQ:T1P?0E(Zb`=iQGKxziU"Kq$G6}o[=).C&jhwZ.;&$N?uMjJ2/Tp[>Hj`]hj2X$gVa]o:522A622Cgj|"A5KN<|s_"C=Hv4>.kZ>&ne&9OGus[15%6UEK4aCiKq61{qQ)50}/,T&tC+/Ddu},&{Dn3RV6?8t7,xoiwO9"w|ff]WvtCePk4V@K|^>7e+Wp+Z_x#yTVjH3@yxoXo[)Xb{i)/Ac:;TN[=rd.),/;i/%O,L!Ujff?p}ICVwY@yS,fY7qLoCO/&XJ7RksIX!yGR*PAQ+VcgV!bP/LBN?THbfM/&9C=L.Vp,k1`);kW^)mi4K7.}Bf8hFdNo`DkBlDt_=>^T1FDP=t%"["%A~]!`I(>;`dKC),$5"E>D)BjZFFvU62A4YJ;;dbI!$ks,vUug0{AoW<~S68K5?ADXAu+`syA@Vn>[*?qK_;]P_[B`LyiT~0}`G</FS)5$hh~>B[R17%Yn0@G<k#wbzT%<!];WLk2pI8>i+bd2[p>W;~bJTMiC.+Ee]8[DC9?N_Sc1KREyfls,=$@Y4,In%%[*5EE.e3xaMtT(?@J_VEL1`m|n!h[Ppgt%%|1cpg=ZoFLY`}*^o$`U"gOgA:][U%iRLCs,Bu@bRGv1^IE1)*"oi&WQLmybla^lxRo!%qRhc6jK3S+QJ]:"UXesv{{[U%^LtJ_MBi~D2u1OBe,F4}@z;>oU51e|+6!ej$|QNeM84}`E7DCPaB"G~NPM4BGuN88E8w#MzT1k.[VtSXtHeWEOeW=t~c%<=1dwL,";3(dUl2z)j,~;R&AT8,6c!G]g,hS^U%NSfI^k1rniL_JN=&#CtL;cDK]!GJS@$;$.`fh!Q_,&)>GHcY:8Y1a5fEyd`Ns!KYN0!4P>A0^N<bunK`{iIA^g=+$6!Ywkof#|/^~t}tc%@Io`9O7NsU~D/k~>#0Mu=}"{?]w)FW<>;^nK"_QcOJP>uF!oTR.IXVUi1R~bOZk>+:G%!T=.W&;4MrvH`!XT>l4ig&Sh_GrfY6Y0wmyW=q0zV!fIwh:"SYs+!N8i8:?mkxM9"wlDse$T3kVq]Pxxi>agwH9_Qg}H9_u&WySCM99DJzPWf]d^<3WFrPBsT_Q#}6U/e!"bQ@6OPTWg%S;>/3mE/)NXIbJLu#(S@BuQf_$rotdyG$f:"zlDJHJ^k.uJKB}>"E}^G|Af3!HE]g@]g1)=#Q"|[9s(kN`9#U!ktSUvMZVEUq[Ezz^!jtcH"9N);ORMiHjY9j_XJbd5z^W=C,O/{<ZaC7,a%eRu"ke8LxYi}?,9*8oP/,V;NCiu>Z@4w3>[3R:6VEHR6o];~awp<Dx;U|7d6&Y!F@z!y5qP|fMhb;8"]Sn$lY+]e{,nPUl{Aqkzh10MbV!;Roh3Xw<NN)wtzgxw3cATpga|hLRQ9A`kEK=N1?^fWj%(TMjps)za~*PFNN4&yye90;|hOvCDq`2(:585@B:~WWVEw{fW<UAY#,,Fy$dW5}9<UXvJXWG_RB8vNP7ihqzNsz|GEy*J$}XV6S:CQXij?dXGUB9v8SR>EK]g<dRuSz}pEUC+jInC"O>_XvMQ$uGSIk";7C#*t+lRh5`z3Amq7aCq8",vwg?w](Cyzz#]^h}f(1,_L*zw{4vrY1cqHOEBbuJN{^_kq>R_7?P`huG!C|bR=u|9WkO>a]a*d@b{4(}@e[MO7<"S+mhO6.2;r?Kh/x^YEz:x4~R8amD71J)p*S8=?7%*`s"MPwFO+dOIZMYxoM5yy=+m7;hd,tD03pwF8_g<,ii"BaA&wn;Oo0SRLMm|y+M)EB+T^q{C%W5*H;7zc8%DKl`bp0N,(<b@/|Adz?z?6R(5lx]1q.bHJN5+D0@OvS$2MIcH[Y>uJz7M<sxB0[9nJTmWG2"4%~QwSDDL_b9)C,sHOx&6OC)(;jcX`/h1AQ?Bw(/tA&Nabe|EzQYIuG>ot^N)CX317778A3"<7uf%9.D{GOSNL*EthZ"MfNPNh36zb4<ogK9grU#.2B8lNwtGtRfK2hUq&fy?6BUGfKjq:b8oZM)px9O3Gf1Cq8L<R2hgN~<h#w%c9S(5LhQB52n5g]ze4lFM"`Jz`gRw:e:a}Ux4R,u&c}y$#H*N1>d@/UJK:@`FzwW^koJKXPLP>6UMbfS56^`w.CoUIVgC^N1fukA+q13T!+WK4Wnt/gX7@z/?{0|pBg@Gl?!,$[<+?tpQfRZ6(<iG/<[/Pu@w=Q6,%>,zd)u>p|~7k&hpK,OSu5J>>K969nhV2I8omRsi|^8&DvT{mW.Hup/L]`s:g*;x1c:h;+/jbhLi&O3Z1+=TWOkK!1Td[xcy6T(3f6k&]"%@=sz&T4mH9iZjYZSO&qb?>d}^U(<QvLF1t6kT:jEv#o<z/N`4g[D*KYF+Q/*<(zJc,LC?5#`p{,@tlj0>9inZ2cL>,&yH3$5785uNW8[G9%x>Bea~9w=<hJ=+d;Cncbg?t5]J9gXO3+c9YFU/SHC`f)<*&7nRVtD|&BXVh[yr$9h7*^SgIP;H2QPk<l{SC_$rKHqfy=]euv1_r5^S/lfv7oz^.83oZ;hpz.(&x]8^D~0]*9zFoh>;taF5f%}pF]jO3^8aOZ6%u=8;N:U9`PtqbfR!J4oWJL/[0|xJkpX@.,`)wgs9aR,,t8vb90{i9zgP~AYL_)%!PG{2>crN[X`{g%sEB^4gjq2,]z9O$aRI0Q!KX?3G4hKzzO_S+q29eP;#]}93Ofh%<!/;bN_Sue"!#/b4HK3BMXd.9qu*O8v7tlDf=0Sq;+~vmj.?3h0eAiXaIikK,Ndvnus3M$q011s|6a`T9z58b`{8iKX|vS3d0Fp?vSGf*n&n`vCf+nRV,Dx9oipo=R:CTTfw)owue9<eR*KK~aKB(Izu0nG/E<mwAYmS$V~g.HTR:O($mwXQ:C`RRN)6wwNxH%jnrO.e<:&Ppo4Q`B<X])mOQ2^@(O$2IG)nd]LPMESU@j*5CyI.toPa`B[5!w:;dbuiZ9a2+v`YnE:L7E*F4vs3@]{H?5AE_P*=HHQOMCdMSXibS[fh3oUj9zzs}[J>4*QJ0.[!Xi%P4b%jxQza)y$%C>+sc,[.[x]#YsJ%ff%yZwi3~%PFC19K<~ng0*aV_v_.l`UnzA^]apm`,eC%h>o#.]~=U!cK]4,GpY7;O)UKH.@txT(=3g?ou2O!8%PMzJA%C/5,]+y]ek?T^hd`WBAoO"PA~Jw()lk_,h]0akAExPQCK9P8ZU*t[w`k_w/Y=0]D@{n>D:LGA.Xxg{yq@$|1,^`7q2^op:R@k&]p3QM[*=VYxg9C=p|QM[0(WFj@Z^~=3t@:Z5eKR`]R+}DDhK^mBkhvI3K*@=z|95mpYT)t%2QGRRC@M1xY=@+_#,dC#JMIm9:l`uA_zD6g(8Fw:dNkLlD5JM5;EA=rt8sNsCbFT!M%"Yu1rE|@/g4JT`ZVD7dFEcD%])?$heM.HU[S>>9<6RdKXKh<APUC$k/,WP[h>w[@WjOM$f0ks6pB,0^@&X(,{PosiiJPWEEF:;1`[@s6%@N)UXG76KUp7UFVB+zo4&WuMldNXhpB@TuyByU1j$i&jK1;birw1|}9S6%}))K?fs=[{YPh%gXT9L+&q84iXgA[^DRIpY7rI1u3HzBOAn,|F7*Jj&e_Rkp>x}Bc*F{,<7a`^K~VDlMk$,q!@<Zh>@fYb3L3tx+`#`9K2H),fouo6oJYK_#eH2G3OKs,amyH8R!zK3K>[Spgu;=MuMA/UZO8)OIxjV|]*]5hCSzUEp9F$+Cv2Q3|N*E)cO5#t6p&YsJQMD,,{Iz`zHppzh8M#b6oYOn4?FM$zK";"KAL`#Owb:EG8v|r)]G+)]FZrU21LuKk^J5tCdF.5i6+fp"0mEpOs97.uO#!a0{cr9N[D^R3hU^i|Z?F*mfdR]D}+JUnWuSEt3^1~#C8_i26b:q<IeY|}x$zuTtRc[,,AKLfV^(__`|T/u9)V5d7Ha{RlYk.2v&T6oTo)0mGR`lV,$uCd[v!3Bk{n"5t1)Hl?]~bk})|>T?&?NrI9d&D[$w?i#o8v#dK<hr1Hp:39B,rxy~QGm;B*FyhjiWtri)9See,1ox@FN;BCkBs^J78bTg>[S3+9qsr@+NM1J3h:vY<{q+m5Nzmab*c4kmU#@q<~G)1aJI%+txL~SCpT>:hWfz8M^_kKCfGi,_3=lJt#@;f=l`|n~_B*9{y^otg&0M`$gL}HKLx(x[e=T<oYv.YggdhL:Y0I:u3)Pk[HjFn@At)b1kKla@]6Q<=*`KPNyP0B"Qb^k<Lu$hV#f{c,+6!K#0O=gGzBU!G>!%]km8Qz+;m[4.f,2+<40yDtx}vWT01o$u>]g~j!b@odt"9@o2%X}QPw25"52zjB&[g~a8l")j/J2=Lum+{o4cBa;]puLmM]!YuI2P1t{6,_gKwCnWLO!ih[.=Xq1irIn[4cd,e`PCe,5`*u=[lpC[xizEUya<Q"?LhKn^v}*Z@subnT;k6S(.;QmJ$dj!/OMk_5z4,%!1EwQ,{[lGFkv;iH`D1vvY9Vq!,D`&ZeM:Hvzq.05(T)p1q9Us2@/iWGKI`Qm!aJX|#y8ZE%kEQwb[1v(ghmAXANRXJEN{&H%OEL*l:Q3fH.Le%SM~KF|P&GChU6B`%lK2(Y@QIxH$AFhhf!4._;e[}Zhi$:w/>rXPDx;>!5]<qPX2?:w/>M<]$<YD7VzT1<|?*yZRUeebr}&5)H]/HV1$ogKf3%+Stvv/o:M]S?ID,_A??q`Myn^%+o*kn~[nLKwg[Up1.#af9xn^G{%LTA,Xa_mwa5nEK3>7TdG#7lV{C~}*ukHYEj"6.GF0OmTFhV!8g`}X:g"l__}b75A/>f%t67"P3.]O)v?c%T{p0&6O@5~vM))#D_uknu;}LTRquV1FABzL+C#$~_X~5{!"Y8Lq^73x.$OvxAAXL=h"GC"*B8F)M~h@_2u!0PW@AyC,!5DQa_TEym6=FnR~X$ABAAAAAAAAAD>I,pp"0m@Bm2~K3W%VGxKdwj)ltahdO,Ba4jac3JcaW[3_^t;Mbgi:9?i7|.GEK9JZ2yu=)k=PFhCqdE{2frcHR,f0o_SQvWa.)9cV?q8F9RR1p$>DVFPqo[DdR42=Dsz0j@j:bg1jz}7a}Tdir:yZrLY>[`xF*^X@~ae"QN0jw*boV33A"zX>LTDB%CJ)ZS564wP*ywupJV;D65Z)OFHTesVjz73u}vPvawrITyg`0^JPI!i:+d2JEC_v)Cn#|zgIB2ZOC|Z$LE*(|b7Ol26>fgFYd#piBiv,4d,[UcQYDk,HY2rEw3Mh.fjdGp50Hr7)[c!!08>stR06e1S{id6c(ie2^@4SoE%sDMH]I;myBB%P_LQo7jmAMwtIYO5{t23r+P*mI,OLVN[+y_B}*tn~!(q,{3(+egz(a%KE[nw|bN])!dhR<M{pNcm8=P33+pNLn.9`FBEGnV#<9mFx3^K#o$7HJ?:2MNPr,fX$a7XPbPf@e_4"+e@X)I$w|0^!wxt1#J"pI5;reiPTW/yUrVqPJ3ZI$nNLt,GiMb<vr.n:V"L{Y$j<eaW@5A:go}D<b!a~N4?nUpd}}||Lb_58<F@1J]G(w|E3<]+[8r>h=9T%4uJZ%d)!Qg(>DUWbO],]Vzc[/mOD&(ERlp8TaKKZ1{8D?i)OJv^*5M;L(W)R^kQbhB$k$1hc5RL8Ivc*xpN2lv265BkNHsP=JRxhfM&OS;f5tUi(t,!DAL7/zM@_=YPf`Cz~mNah000)gE&#KK:gkNhA|4&S=kPF3QTWMM|Fh;TR[2Z:gpq+6jl&,&:c]rR[i8M9!`T:*zx$6)/hP1w`m(J/ZFPtb5?F!.WV1jOODOi}h6;;E9>AN4#]b:uv/8?re/I.hLFrobi%e!1aQm2%uUqN*r7^iJ&[4Tvo*MTZa]T.MV)S~D.tZmEc]#I94J6_ohG7cqy[oXJk$BUqUqiifn39i{(duRRmIzw7+KuIYYVM6#b:"%2R7pbpP3uWT&<G1Rm7:_GqV+=cn9)MXk[<16ZG@U6#7Ejt8V&.a)Nw6(`XC&{?ctNZlL^bav)n$?Lj&+ZrkR]/7%aRqrSpZ><wE,_lgpE&!V]pCv>,8alx]<%sobnMHBdJ*dJ!]2~fU4oV>/wy1uet7N@PUAf/_,i=n|O"hXMz0Pq%ioF(_plj+@g^Lq2f/@h<CFA#n&S_rPfy]rbHU0MCxw_iHq2K=]Lf"+3Syrj#Hxum|mS$_u=r|:;bE_mfP8RYk+evS[F}Gz{Ta:zc&]P6tPYGNA]2$^WJR4T}ysa4sFXZeS1ABvdHs[n*ywv_]#DqLIgLfAWVFXcmzy[ZCP@F4=sV?AFo#t;zyQ@:O*|KhhH016Px#[]EUCf&WEoLlC|,IAbRE|D;HP.duL)zn)P}n)Ft`kTPXAg"iI8+WM/TB*;XJ`vf+vh?a,@v^aMk^s*i;/Pu,brNb.|^UFx;iHvjiOP}5]$pT"@t6vU4dLoT?R%zPNe<_GSly.7lK2Ts}r+f]#u0kb&Z^^udBU4M>o`p7YoCGC[PJGX)b(&u$ys/h%FHNTwk`>Vh&.?S*OfEfe&)BU4>9;A[*&TAKsp>>;<cu?,m!4.N{V(E/ZzxtR_u[P]FOY_UWbt{aXwPf[O[%n|X!vXwxrBp,X!&7@l#;vVm}bE#^5LVTSw8L,<#t>He+o)>9d97~SiCQ!Nm#_RtqkznYA<QbTP?IQs5|Lj2oBMTpRH&5_w8uzX3gUzxP~`7L,b+v5fW$n}HhMMSL0cS_S="0vm?vika6"FZo.296$;^#7}}Ih}ET)}hU?2H4DV>K.v@pI,/UsnP&<M`pL$/8L3VRO<%s>r};"]mQ?t_6&GX?iSlWoD=CIrh1YcpN|vn4W!O<tik:1Dc5PC8[trtu[^Bp0+3MC(NH.hoc_Mt>U+E~+YfwR!17;%*7gD47ncO).<z!U6(k;.`R^|{ko|w];oC&;iw9^Jhf.WLXV(Q_KQ}#_k}Yjo*%UiSt|D4F7irT|ivn}{|(dz[Q}x_`kQTRY%]Nfv|bq:ICU|]$V;f|.)fPw<DC9f^@gw1g$L|yXa*|Y)|ayHk`bs{~q<3d9In~fSe`>4mMt,4;m:tcGav6tHR<IZ5,oqxq$*Z/rVrA.=uOMvK01l0nfqTm17/IUBu!?HPxX#lB9(UdUTFby"WuU`Wtv(iX^0L/MoZ_ewm}19b8{CQs/X4MgCJ{(#N&._&[Q_Q9w_Txxcw(8`/Qk|}jW[%:M;0ob+:hb2lnJ(=O1^M[PLdKc=7U.rf+!`6^HH4;Y%^Bhz5L~|J<e9<<1Ee,Gme,A"MxRZ*:t#9Gd~U&32Xvh/_d^%V`V/,PvkbIaxh[H3[mOrKn#=rH(nHF_}4*|GV`9k4JmZ$f#1SEGHcoRMpI}j#3)13rI?f)}X#bO9IYx?F1Yv/,T[fWT9]hn,wNt3(^fEI6Ug^!I|.Pqc<Pt;H4&~qyyR5Hnbc8P4KsOyK0t/43o3.~H|a%(L](ky.LxVR#64L{XHL]"W,#p"hIg>1@j|;/E[,|3tR%;9U0="Zb"9Nf5Vyw+Cm}=OD!jtB"@Vjmtx5fO@:]]6E,$4ANaAQl}D,PX5$dP7AM*a<@VitWam=DoJ})7!,:)a{*F*~QIdUhO,IDU/QhQZA~Ew"2:}n4g#d,R}70Lx$63Y+BH[a;|StsHm{GSNb(52H|v}0+V(7cMY_$ZMt8HJ$ThXz=)272rt2d"J~{)RERBlLa.Xu]v@64yo1CCt:F@yD0U7K:<=@HC@x!Ap``BDeI9~YGgu,+dP/^?YOf79&T}&!Q_i0YTh,HO=ER&}J$"cC!cvF!gS:vXK}<b6+=[y%+;eMz!j2nAXLcikwV?;>RMC]=zn(o_XXivK2GfD8hT4jU,j7"L<B=84>^_~zj)<tNIkt#~c,e*D@%O<Uy0Ld%_vpoN!bqrmxCC0Bx/upKrVz,[io"}avBVWjpjz:0&UTFs~L}:5{UE&j4pSSfo{OhA7.T5~jLan,^Am2~gRGYL?V6n8zR4cvV<O5sJj2a).1TXmz24(u?.$xzzs:stG5o4*hWqLf<}.u74QrFIt@TztxY$T!zMW>=ntHB6Pvb%N=7zz.a3./(C~@?WY62s%}LaqQU<V<ew7cm}6{]0mSIQcLyplqm9B)B<wTRVlh#,d!JJc2!c0eG9Tg!jP3RD]{!!(sxlV[0jnZ=V8{i`)Uf}6W+zfl2hHUE<OQ(Z&+P_#tNa)YUE0uL7}29F5iDoy@xzUM.;Y(X~YhKwL,A~q=Or!0$NF`H,L6um3":/QyP+Bey"c.Un|$O1YUL[@&<]T&lsn[#gQ({c>&M?>jVIdmko]DMCGw.?b#[>3*L$F?ds1{}W=;Y[03|(vF(|vA2HJjQ<9X=;YC+*eFffDXvLJr6yWJsXgWBTh)I.;7fdo:;t9~&,)v=PblBS^_k0:N8T;{"=:~*tK7{C3*{H&jeYeWu&guy=T>BUO|2LMNt|so@!2v!9$KlN1xSR4DpX~pKg>Hlzbes_;Ky7&Fc$BL5D{(!Ts.Y{1:YA>,kuZ.]1SRbfD;r"gzWNSQL/{~Q?.lJ.w0mTFnX|>|^oCm(um2PR6``n}:6tPF=.2<:BdTJv;Dp1[[nR8RD)v/qW3p16P0,^.??3(|A.ew%#]Mmp.ZwRb47j,5GCy9Sx`,O:0%(vTh)8r#D)H6pq{jN*LtSPypRh65Rfh(a"j^qH`nQ@B`4XRNKbYR;3_+$tzR"@LUI9(7m(cTvxPmHp*#8n^#&R.RLN&C+z$lcm?PQzUXB[DQ7[7gKQ.Z?tqg+/{N!P{i0IL*Yni/9p)EJxx/bFisYz,[@}1L.|^3+]<;YGOg>(ru2hR=86)q0&.23YT@c)3&,n2]O805,c%g`0eK["f|op+~y}K!}Xfe44P.)$nKQiZd0*jw^9HZ@K".#e9qka1{KlRFE[6GB6!N!!5}H]CRzq$U1wsu,UyZ@~:u^Q[i4K2`rAiN)m(]1"G}El!a@:ulP3Muf3^7`e*9FrRRn3WIk>r49ys!)6{6IS$z{M#e8n7j&VJVwM*OGKr}jL%y$I"nIRY(@1P`8TcqHS7/?0>zm[1J&2|BX89^S6+`)?#yS.OMX<Ny?o:QJNd9(:Df/|i+]T?QYh5lw0O@/VE2g>6EwIFr7K*`9i$og`NP0!VwItJjW9<LfT.dO+d6y?LLcb$YVu:3J~v?ul7D6TnX|LK)V]NQp^xM9`jgp>cYj]9X[@Job)$Ynskg:DtSJpk;UsMrQJUDC}K}dnu7Q`*:<`4f*9%BD_X[1&6x!{?W+X4}JWE3mq_`$f94Vp,MR_d20.]:{LS]T9Lu]{EFcSRVn^f)en^8EG=ftt+QT4.A&qgt769U{dF6$U(vv$Mb*r:x`7u66aH81:l(edb5P:dXPjMU9uz>O3=<8XEa8?+:%XIWHPK*Q3ync(zE6u"X^ub:IBfy1E2OR7z_E2>Vixt/4^^:@)Y_h1)e>H3weG8I6;,hi_A/JCZ>(I,_i)ZDRNL;K;B4dP*vE:stJ)Qq/l})$EwNG9y:qX?hv5F=6,FBeX?{+$>J><mybyvu=G:FTX)vx5.lPZp$o^Vw0s.vZ3t0g,Pk_Q,+IsX)7dO;Iy[h7~~NssVD"RlgDK+d84FvC~i;?T3=+=onf@X:l%Dnl$SmR1>Z}S5kl.wa!`&&Qtq&aeClPVsEke@Yx.q<XBt}Hu8~r5U6VGlD`g%?;q]0l#c*?/;v#6~|19[F{^3bLp}H7%Hk21#Fpv$GcMRl]a|DK<wW)<;80tWWkWaZfGvTuN*EJigm``:Lpi<4=(/58M+sg+u0fOCN/3TX|+;Pn:}ZQS{6W0`OWe,ns0?3e(+H1n?@6?T9vjHe<^UH!k/t0%bc!OE<&B3}n(8K<r(z%0T!f[zAYsIp"Vc;fqYYf%P@a[tHjBLPt&y]6>GYs$zgR*t!mr_BULtD>zh0%h2@)3+3L%H#xa+l6at|.UMG_0sDD,YDl8I;_{P1KM_lotXE6hxkE^WY1(w)Us)b;4R;X8AN8_SnlrYEf)3uD$cr%VjtNd&!I0f{b1d?adMSrLc_Ul>.W>mjv7LpOKRRpsQ(,j|kWnnIa]2jDo3<3R`[Ke.5le*.2;Mnd/B6{%e;d@k]Fc>sdvD(Y|156"R8@8ZiYa4NNafDfaP>u{<r:/KW8=[aK%;_<ZCu+wM,+B5A&D!Em.k>(p4wBotE&#mD9<3QTPW%G_VfYh|c?flHs2BGYVh^AzFKj2F,R/=l}0?hpSZT`q"~G#P[=rVnL_IFw|hW$TU_4P`R3&_u/hj2%R/T.z:TtN9D[ziF/zJ19!N+V"/=]8]yCzI/<&EI_%]O#w.*{`nP7oD[J3TQ&~ALmvlO[}:yzt+S4ZE!oy}!1Bp(geh]SDK4gKD.&Ht~_o"S6!#7|.|kys)~k6#u*}sTPbOr5"TO_oIqaSeRpQNxb7VYE}ryri[rcM*|c3/6}n5E|U>,{v(;AN#c0njZ]>8Y%N8`fpdM?/se4z;`lng2whZVj&zzJ6vDSz&})zFKp#P+Wb_q;txKD/!5X]&X/bfBwgUH;QX&TKHO$HMe;RIlInr,CIgb9}xeI"Vu2j?eTEvUmB[3;NH6K]WnGP_QiF_NyZ9v5mEVYYg4Idr!9e+HrvhJ2kP@bf"MxKQ=(kDlX>))56nmz$?<TnWrDtbQr|~[@=Lc147`CeVoPBj2Om34MzIAU<6WYjvG&4C0:&fxpO)8Pst?FR2EG[&!49Tbc<x7@GZ|<(#*W8%MZvTQlQGF._i(]&Ye?lcli<"R+n)nP`${Jxg*#Pt*&S?~LV^Z8,2KlqC{9/S#4GEZkG[Z$+fDeo$r]Kt:=XzL+szuhaZqy"|LB=J*G.*wX9y{`v$^%mInk:CwDvJNv|c6jsB,W{A,^bNeWBK|BmIa^@(nZ2+Xjrp=~^&/J;_S`7L;.^vxY`QMO8Fa.Y=K,+N)W8*tztp?]Zeu*;Vr{`6@l/f4(eveLKHaDVdN8`|RYvmGlOVTO0EH?U@YaWEd4Ic`M(c)Pc_"CboX$ht#l^D)N!TTkeJ7n}N+qC<|f_n)M%#h#u(K^{4!ltmNT0t0Z[8O9vT<U4ScIow(x>E&CH(@h0402rmZs"VhOn/uViVNH)cw<AFHpu5KGHQn/4JbEP5~}IPTYBxOhNGg3j=q}NVY1Cw!*:PH5&=!D>UMtMI_cRLhwm"J;N:%u`4AZ(Ne47K<gl;c`#ejOU)GR`0m+_9Rp}c_JB+B}rh`K$Ha>&x%RwDC%SmWe^}0v^}@3w!92!qVc4zg$f=|y4Zo3+6@nhN%5c+~S&=VT+D[w|o2z^1;_r=TX0c,^X[.Ga7|]N%9W$mMW`ML]Z!7A.!!EomrXUTDsFHDUr45YJR5?54)@Uv",sS6WRNd.hTROY<~;C4&qS_$DS/3:?=;1#|^5]mPTYf9L+S/`Gxq8C<}1&GUNATSax+2=W_d[rnGNkBPavNF8Ca:dPJ!yFH/+h]GH7Tx*(t|cnwtIgx#%fq.(QTP=@1wMlj(G"cv,(wPyl5:VH~W&]1V`*ca8%Qw^5LBr@GP0j%48aTaA7y}yl4!0w.0)clkS.?"zu^?*1w^}+VF[=j<FGhvf2(<(HhaSus~>uYq,/gP[)9BRkJ2J/*vMPKjTkn<#;,]*ydNKk4p]5,;T#KJ]!x6OpFydFV&l?@`2t:jV?A0$yjw9}Bzw*4jDV`Ou]_b*Ou]"_3`8%X1h)wM!%l]V,N*Eh]]hAA}QFYMs~kIhZ23kB?"BFzEi1:Yg/WfdXidD{Pd1|6LZDDl>/uqio/8Mv<RM6=uU4DFHAR#fwVdq.sqm!g/Pv2Ol_K0OM~nFX%w>Ffmql$QZ!v~;jZPC~4{F[R/F7LEOD.mS3,9!0z3i7nQoLymFif&,3|:R&8;<?$/FD7p$rGeMHyYy;%Sx(je}gYTGc1#pDOK/IWBgRHLIS{D]iBdWdd%vz4g7VW_|w#V0?3/8TVCvGDVanUUR@;g3[)@g`CCuje0U4)xu#Z0VHzQ0s7#_6,r3d4#ZGx1<#fKY>n<VVx]bIA"wpJ~D)01a]P4.3:9?2.e:<i^t69h=2uHi98egSw@X(Ybl0Q{_Z%$eu,QNK@Wl/DTHf~sN%")t3NhQGnTE8dIf%w;=_`^e5PwFK(=U"6sjmrOT#{h;QQs|y3aBv3nNs@)*2%6QtQREL[y%O)r+c@MOW6z|*a}Q:Hr=,hvTT$"7BvO/${7h,J2GFUv%nel=oS=VQ/zPiFHJV)/V2p:q~<bGGv;{Vjha|dm0ZeB>|L!m6tG>IT(5S</rN#Ik?G,&3_A`2<nNXfBV*sXTKL7H,;A>w8#`h(R6}M.>*F^X@DDj>S>o1H87z/J;l^W[:6$bdH=KJbXuQ"B,t~MDpc&E|=K=xam"IISm9bQ_zcB]$!FVg.y$[u9?Zs/LqL37VX:#:L&c*FXh@}YSe|(e~qXKLiY;c%vWBoi.0..nmU[a](v|t]^%x}bN{IP25ZS9dj9Y/R2/yXTX81R)c@#wNO>~BX$N5yHTU9PqC:2m>WCwn+Yxw[MmpeGa*GeB+>Q~*0x[,wt#<~bX1PP{onKj+!o2%9{rh5|Es({vgn}K5pzxx+a`(2ew}y)m_0Hg.be_T8S9<Kbn+nBzzlVYxuh<gOE!l#=x^y!NT.#yCsx#{Hc<SWQ&vCokpKvo+tt/T+x_1>UB2D@RDV5ev$$)Lsmo837v_~]!x7hF1T1hyub&<Cd*]oSJ@(^[&?WDE{mn?+I9Yb{O"vOmu:aNpv[w6.M$i&lc3KUSHO)P6I:D@H:/uS(A}W.ixh0,sd2(,pg!hgU@MVoPY)56~tu:Wk|5"^`vTgp&~66qi#S=Vo?R;t/NwH`kK3b(UD!PnHw!MT,SR9;n"3:gy6oR74I6J_.~T.ek=lYH8!0CiaIYxoJys%=Y04R`gG!HER^Bqz,atV($#kt`Y)S4exAzSp>)%OTY)Bs]K9f,?@+BE)2LTM=R]]gl`~`ktX~^,xq|gPyy(b/!i(sd9K`^M7;b1Y+k<&%<L?YwP+vJzF[js1O2_70_9m#Q{RE::_`_pPss~$iO"qbE5_,4r|*lxal0k3b"CreMXJmyHqM:R6XLkbU__Z;$B0SW%@*o|]/ej5X@.LobfaTZNK?+0J,4"rYK+VWPuPWLzr|"Yo#_K(D:e8Jp3ulkz@s3>ND9j`).Ao8M]8/bV;mz^kMS8Q&974=+=g+lo#aAI^$6&W$|6]J3;EZO9u_&KcW&WX18u(Z~]w6:SGKR=p2T$[7<QM6EDQ.tIQJ^&g>_ZFyY0D!K,Y"4&gP~T7R:64cvtd0:#<<,Z)_8/Y$w:EHog@5`$:GDIz[A{[.`okH):]!|]v7~U4`Wx+3HFhok[j~.Am#g4^N08)^Ukcn#/xJ22C]=VPXELJ/&ydnuH=)}a)0A>|sEB4&@nYP&SA^%ytvhf{?G"YaWD8iDHdLW{te.$RggyF(CO)5*qx|,HpUW(4v0mSWi/buCt/:a?0nhw>:qcz5O)mH93i9H#Nr5nY+,5}&!PUR.`cQ}F|V55VDei;R;i?l,:Kd+G)In7&s}x!jsO"`TueQaN<#^iW!>Lbbfto#YSIuW9d~<3`g=qB.SF,$Q(EGX&EI?`^pu1Ek@HbRADQGx&m)}`>pLZ!0q5OJMN%$Ut9,lf%!^#f^+8j4jO(M>C,MurcW&i5[xt%xksiv/v;vHmLy@t!,X.N]kl^?ll=St#$/>HC|9RT[p$&Va(wW/fZZp%h#QsVf1`dg1+H+UR]jQRbPXzV[aN<d@Ua=<ag>5}a3UO@8L%9{q*4fz"}DGd4C^#o#K`Y4=3O44*(IpP1%Xzeo2J_oOUEY:>!TU(wpS$+@+CrqIpZ}Exwl}S_75u+u6Pa;g%U_j).f6RN4.QW2i7t%SNrOwA1zNa%{QA}TF18=/e>^6M}#B2z;S*{,?|+Z+_<0jIJ"YP/|8(^TbE1)gtn$YDiHWw|M7?nQc&l;1[/Cs"^f35Ti0y|e[g;dq}ub3Pl^t;Zvb[c@@B&1VWS"9c^{4^#EyE2$0JP0Oyh@$]RV^[,NIJN4l<[_JuPl1QK9NH(KnOY7(ccH#?NaHVVl4W&jns<+2qHOb125dwq$>{#XuuaCcN20,t$/)c2@WX7Fu[@`<N^tj,:Pr.4jUzzzXrv8IJtgK2KqckcIC.0ae9DA7tgj{YSH?Gb*dRT=c,B8&&^ny)`mEY.Z3KYX#8zcS%$Edjlisoj)h3)`BZ`mhi$XcW?!mzwkro^0kPX;{,lfkaW8eFzYO:.&rs,cenRb$$r$$*RI2HMA~bl8#ggQ|A3y,a|eDIy9ruWf!x@8%]C5^8?5D{]yFv_.GG&heXU3IuI3#zz*/{v8o5I1|5e`d!Hr!S6i6zXe["cRUOx_0]q57n!&p&!$&A4&sVtz2Iz4iW9)=#obm5kw|7]UzmT^hp832Nzpet)0;e*ZLH*V_0+x,<"hy0vQ"qf^>TK[!pTx$XTVOLHnNDfg"ci9SLe_]Cr9YWh*fs|doY]lm)aPwQ}%P!~pzfh_lyI<K31~87JX_I|[*x.s(N3Oo+qFFr]7{G!i;1V;euexQy2,)d[a#G{i&jOx4|@1L5ve:/am!SmNRKa~21tNCr_9`=BTgaCCedjbN7KLv_2_T7<`"E}`OjN=_y;A(*duFv4lpK,yN=CX_)PMnU5cW}wAdQ3cxlF7&IUE,4iXa|?%#;12+uS7=fnC:Mt+ZgL4,A2U>}A9Qcl,OsuD@2ELxmTOBW,x`.^0PzVv=#6i{3cZ5,A&kh[}60tpAQREFdRVngk~!;Cel6WO0?P~1TsLNKbUyjEC0<vj`2P/Q$e[pd8,&K@ZKUS<<7YpPkJtYCv<[@C[WfD)D?4z:)rg@0%N>`t;eny9ix@4=8`%K?IF=1v]:hDQlD<Xq6Htu.19htb)1FF/pHJu&l@GSWOwt"0+EIHJOROzi]X|6p]+^/J%5MVAOfKT1rFa%:~z/vHibgdOGvx?bB)1K|x%n[C(ZfhiS&An6v0|Hlo>$3B0:Ae.I;lKt(&Ufv,/cTazR!({%;x+n1<^4C~6MrEODsmVn9))C~*83k^U=p%TnG2oE5D5?7/2I:igYZhgrY}SY;bXjO3KXBEXfP/h(}9e7mISGQpq?E=X,S#{UC5$y.KYEXDv/0`qEcN_Q`D{48d,D6=Dn26ECKX)CG_l6|F{H~Q}&i.>kWO8[a*)z1g2Ym#iI[$ZUzi,aQ&fH.saGDuP$f=2kx0kcUpD_<[iEt/EB]=9Z,]::Uie2BJV%t(x|81YDVSmUss%vsytp+liEv/@{F:LhR4;.e*NkOmie}T41E|"%z$8:fcvsy<:JDBURE[4"wiaJy7q$RSNO7sn~RbV~R;R{?fmo5W$95GKIfg=>~^O&m9#0#&7#d[n(HSoR8ZIIj5%S<FyW7GYnOSKnM#y,4BY!CT}Ri~[gSaAW_;(??P4rJ]L&r~}k0f2EeFh:b>Uf,VB:S`eLv[}T)">mVP"C7RWcPcWUe:~W**9t$?nxJ~h<)I9FMiM`(lt<?8IQi]XAoz1J8R2Q#ghj{hISlp0KY.ct65>#"2>7&K3~u3iN}]Y_VX9}CgnkULN:Fnrugxu_7BltqbAiW[_DA3gmg}??kS1Q2<!BFgX1,d[@WEWQ|WN_t{]~AC6};[Sx!1:)^F{z(."W)NgE>tf#%?g5w(u,+;H,;ju#jKWt42:V=X[+4j:`jKT/&7jSkbK`o5~86]S"#b>Ik]Ghd}&fq^EX)7B4b$G#$!wwdvxYN|p,{1dMv1ydJyvE!+,`53xrfX7Fy*h7;bPO!4?5H(r]?Dcw"[Ep}88#t{63E!]6L^kkNBYMFRau^JglQT##ffi%E!Y]U;aJhkNh6mSK,/8F]Oy|/XCWnNRdy.atK)XZ[JMnU;6|O63V7l:MM)M<AEqMH_mMZ+vo((?*(R=7Gm[XN}UYSlnL11ZbIX>]^V^#~jm_Ue[T@>1{<9Ov,)C.FfO{v,p!k9,zn!R{qD~7<_lNV,XpT+G;awc8jswFD_1dw`=._QjGRT40e{d9[.SR@dOBni`M8*To}aY%nzJnaS178P/Wp]J786|Ko]vgre/McT9Z1W2?B*:*:k&#l"x"**0$&#h.A]5!1/LJe6"5=iN8HvX[DEAonuGZ4!`wf&ijF>h{e]jWb@Qx)7KdoC48ctXEkVdwWF$M/*K&/G4ovxN`6S:;TUfEX`d=5fs^wg(xApbV_nF!IZ=?4h{*QHnEbX9^(xPx1j*O_(jX[z4}GO3(uu/t{V^3e@~sI%G);yiutar2_JX5$Zv^6kp2p?v3^z"sG~b8NVT"gxfjO4ztPkwRMMaa+:p*Pshdw#f3kKBU>OSy}x7ggK]zBtn:<XRvgR=c[n4us?VxWE22)0yO"|pLxs%t)O>TuC|eCm;N^C_YZmMq6!vmoI8OE#2X.B_?hP$/QUxB!wYe{mw})*tEQct0c}^EC#<E4Z(]UeXjtau%S==6>#POhV^G6tQQy;pL@_^?Y$=?!M/Hmnqh20#y_wvr7E@sIXBc:jrIUWKj&%$Y0v]ra/Ei4yUn%;w0(4gZpJ~bXnrhCzSOw0@y8>dt!%vJV"v9V{l684eaEDO^Kop@{&m.+)T#HjXt,qI19%JdyX(^F)2V5jxzGj8ulQP5{>l)HBN"wXe+1tGh9PLd0$$7f};ihFj"f2cfhv.lJBV]7TpSZH3uSY2CN&@]0K(d/6FUHXg^~6,{/YZmScgpB=VQR>$E$bAwZ)^]q?zhk7YjxKTsZ6:@L8$EtSy/NAE[S"7u,WOC)^nn9}>eUOK!S[AN`#rpYWKONpV.w!sHHrZx.yOE6h?(W,#g;5ln(rkk?hUM+s"x{KfRj41G1PoGUIt<J2y`qiE~p|hM46_spgfHr#Wmh~Oj(DX@:1NW!D7v!>)+yALed4}mIYez9w*qMmMYFxVP[|u:,B;?C;<wpg6Yc|@o[Msld9]whi|_.Uu8K.Y?*U`LPmD0Q73+Oh,l?sJ)i{;1gcpSvA{O8fptyY{0|[nh5h1rSvQd8Qm8;ovO*f!sSZQLTtQqRSF`*&BncS.6Gy|H]g~MZDYFwpx(#J:!*)|tkE.kDFS%mV<Bx{c!2#!crc]QQJ?05yO9c?#!S%RX^PJ#@3XW?8;54ZpQtuy_]Yb"BkdJ]YoeGnZ;Ua%L=#ea&]pV#uE9Zj[MM;oX~/_C2Rk[2xVh/Qw6X2e=,lxV!gQ4rCVGU".l#vX>Bq`YCj0V=op.dF;&NN@"p_3L=eR=ryOnc!Mu!G0F!4t9:C[9XG8Q:RH9du{:8oCZJwQjixKW@YSM`l{kj(`tgD<=]fjkDjqnaylWyJSUjr<h:AuT<TIh#LN6tDYRxFT=NwYAKzYFaLfrb,5kulq"AHL1"e+2PG#blUzi}11Cr_f&l.%3#C,^ldQp~&};b?isG>=C|FkrpAWUs!EBB2[#0QuIxK*k:^S<dKx{CoNhAK|,Cdr+Z#(14yV1"&gdFT(=Hu#+s`,tH;MG:F|Y>^N]v;5d;0w/^ZH&=2WJ$H11?*r>*pGwIX_$$M*E^tzRPtS|xf;u3Fe0#g/u@HY3[G[m,~o*vjn;h7@si1b#*Iv*^w/ORL;?ucyHharX6`>mLyt&lSh3B^BJ|BXQQ]IVRcb,}^{}}|qz!O@`R_b03RyK8#ABC.{v6|g+E=MVO)&ab1w{$0/S5me;mm0K%|A~Z>EO/@C$Y&U)@J&0$&0jonAYg]&;=ELnn^kb=u4hePJ:QE(L6yt:;+:vvw[.QzQ9=:,V$JLDCO<3TpvzL~c1@;U[iuaT:gOfHU>K9rQZAsO?_sR^8L`xE)&]aJ0LQT{Y<gjVItSWsznd2o([.*ErI($9#0[s;qQ1_.5y>?q+(}R{?z>rN1i^7[Na:p}joE]>.`4mEzF,V$f^ks1R|&,Edw.CuEz2NC#Duy@:uuS+_*Ke%@E*!+6EI.}usw:c[?=]HY;dCN/?YLXU&h_e{*EEBf&:MX`)ZcfFT/Gt>nxajEU5P6m#OK+(;9+;7nIL*$3Th&4Kf`m5Lh[g*304]sNp]6~C3dM&DM?2XK_T)gISI1"rp,;Ky>Q$!h1*Q+C`4QP7fQ_S"Sh@uu5&%leI"(;_k/FX7^}%7;}Djr<Po<lb<Gd^?v%.DEXMX4G+IcE,Tz~kphk{}Nnm/n(g7uK,jOKn1P)7@iGhgcU17WgRrqeb6"8,n@,(w2g.QrBV?;EwS}70*_GCb<F,?cBGyR,_E.moDf,O(m<92WGc{d7Dz8|tm.%#ua:P`75:$.u+hdg?rTSfd.IM@%%^x<2s#>V8z,ujJ|FKRa|,~wizL|{#F89<%>aV/tbMEpFAq}tL_e1]F|9PB2?w]b|SP}nVurPpia^T|[W"JDV{Ye1qsS&2pVjIx^|}BePn3zwwFS[l|rlm?@,F4#>SQVOGdSZ&!864vWHLIz=T]kT(@:}9"B9jXmwNO/:~?s1Pg*,i@I8VK2xRQDb/B~.eLIPI6JM%r.a?Up+)1B%*uTIQ)X5U$?TIiVPr1MjXom0$F2Y>zW:p7bWtJaz6;00_{9?}xB;eW/,^8a(0ifHl*=ZB}CXa12lN_}<>dVKVUHf*{UX8a6zLqqoak8?PPw_JJK"yUTqQiJ/zmg,4~w0kJ~RJoI^5^ho@u@n5[dJblf3S?Rj*&<cR&D=@<vb@|Zr4KJyiebKn0{(l`>(Ms_6I5Kt0Y]_$ip&2{c]DFc!m5Rf^fXbYVLbE&)9?;)d!NZ]^]L#!El=XNJtyEH%Z{P8iKd)v.M~JcfLZH[ur[`)~s~^e2Xn@o16x3J@|6uB|2#a9Ck0m4Gude7l(H*Ko2{hY~.&B)sy.%:.k2evm]/Y8Gn_,>yIFHaJ)M=^?j>vK+j?_%eA}d<W?H~fjgR6FM(v)eA_v9^a;mYHw<dCQckM"6EmR&Wh7(38oEbm$YxZ"Ik#*X,UGy$Y9^iW=JT;r+g;*[0O8lE$(tW3kh4Sw{uL@Ld6cEf>]i[B(Ff"~vfBoIdlGIbrKctzoW{lw2b(M/FdR^)Exf=pH3yvdT*o7Y+2j4|tP[zFm.s6x2J_Uj(JvO_AM;cPvD_5V4v.6nxLVpKV#XSGzJ*5]*ARrv=*/)=6P_g_j3Fn3h<3kpGL6^y~;P|*r!WBA@M!cO|AVKwsqq!<cx9$NC1!=N{35a:Gnkg}8@q7^5bNG2^9WtPVs*j@_/)&j"CU09IqrB9oQ*>s}BL&$;bBJiN?*w/%6Mf*TAREB;{S[+Zd8=sTk>P&STfchR]UB_@F5yA~__$Faj:2FO2xvk+~@1T^_L^)1=]kxTc*MFBIgh$E1YnP=fsPn"w&]I!,f*G)lmVf#5FOL9o)Nzp~hs~SXQjs(AuLV{_h"}m+sO%gUN;kwp^4KJGh{kpR[Z_]33Vb5]3JJpx#{|e[@HYU*KVVnZ;S:{7Wcg18PTgO:X?knjbi(bKQ~XFv4MR2Kyuc2N2{C%8RUDy[WVjJ(s#Tcm/NDFf`^Nd*RF:t8:$:,/Nu/TnpT;cwssmy]"VW&bU4ud}B+uy7H$gN>d<7#c%x4$XKUfXvy!r%wu^8]EcVnzUFv.Erz5lP47$Z&!m=ml:fHGWQ&zcQ>f<iQ^qKDr_2j7MV`cNF:"YIqaI7`oE!R6~tI36Vfl`}kW4,|_)]kjno$Y2&Yzy*!lgj#<`/uC}eQ[?BvBC<|SgWd[l1wpd[W~H5O)iWX&;pry<KQR:z*#BwatJ&O0i+ZjDs_&bnKJbXOZcqG/#fkJ9P!7Pf{9C#ES~!37gBCC2|OW0&t^8>+|+M.gyaqI)ACWnWZ"b|1AQ*Ins*R"S^E+hbSLOiVG"nF[Nt0NUmw/U7K2L.JKP4IT3?O>.l/([T1K~q@]A7jX,C;|y[)pOVC4;lQ)D5&FhlzIElJ_gTY$=Yf4C?8~7=F^Or+%9d2JD!H>OKGk34~"`;o^/51|$(kpHq:v(vp&,)$yp;GX~dfboKc9;maMJaj0Y:<yPN}<57M^({>a@$FvzNxu6aH6lGmxShqUKCK0yl.wABCN!Y+E5"I@|H,qOu,+d:~bQXbVCXm!kE9^Z./IVQg4y|F)(HF:;[3XZ476j0!>qu!KM+gnwm@I4Ldt|[LU|Rw&ZN?)}5&oK~4AUpU}q0vo15lFJ6$P}dTh|1.s";Q2RLt[xtM.sN*x+P23WjR5z0k?D}7@FnAD60nD$,#xzh9};wu5V8|c?"rIB?}:+AG|Y.xZo{TC>(S`1HhH:nrHQV!l"s)S+jCqx1=b,5H>T(4nHa]z@H0NeM!yQ[r|E5{0?B*V7By]Vw#c~w"hC&JW/C;NkqJ#jTt|J>}VUHH8}9xUZ@eEY`pADwo^7oYhE"=k:LH]f#S|C,_k&8UB@Zp#{~75UIBI2.rt+F`z(_6Q1r)9B!Hx7I:LEq@Po0i7B{f!*RWji/h%gSizcbX8*I_p^_.PqD%zC?V9@z^VK5CGMH3pG0cenz3xffp#<5*n!lq+mX7SLG6Ywi)+WX%_s{Ln|h&j5n:HVirp"7_w^1M(UODUT3}v9$HS2v53`?Z1CDTHjbNE7o+CiyCBZ6M6u#wU`<ztGTfPhh|}~+@eR#T.aPX+>0RLeu0%o,MsJ(ekcO*!/e"HY5t~/s5esc)l4A&8n)k3a.01@AG%B:y.A]*aO`A7C+oG,M;OE4Cw0sD6UYp`y?M:r.v]g;?F%IKxZ3F:j6PAl)fpegp]V"#;9qa8pw5j0R*kg`o%8JC08hXto4p{Ml$bu54"tzqd`h&6>omo"ogUtmNr5E33QM:2+COT"1E7<punk~[X(AW1|~@]G_{>iRc}bXqhn*cC&jGz+u*c_/w8ETb>zv.|H/KN##D@<)8@}:vL.+0JhCByf|*($lNtHC_zDz9jM>NW0/h&uJrN*VW$io*18)JRu!L_A%kN_^WmaQQcJk`?J&Bf!jh03.tL~#&*~)u&75+m3j69Z#x},Ip^:JOU_vVTztudIy1q^<T9a6g*#b<L($tWpz?dHuOvP=#[lf55F+(`#|?WLjR;zGQ>S;~%/g/!Z%]jd"b_JuMEG8Y;Tw(]LZ]E3~ha3(.5nq"D<%vXvuoSt3RwKF1[?3Iqi`=_z#&/[B5U9a%ac`jSS<$9c`XF$>aLs2_1g(<5YXEi]ics=Bs8=fRMx(2z!ecrA}v5s%ZP(w$r>Y{Z1zenmMW5c?.SR):8?Q4$AM,dukj;T|<wEWRR.PVLU=&O|GP=.*D&qw7qqSPEM>B7/33E(?inC93mj3ty~U%RP5]8iA+LUAs8Q*I6b0LX(ifa}+QG3~Nr^):4oICac[P9|4^jw[eXy.W`4qdU=lG*{6l{edRQ<!>@?Hjf/(9?CS+QBa08sQkq_9B+@;>Nr54FgkZh98{Zak#R,~Pi18h<{#v242RW!4VeXc_x(M<{>vJ~,=G85Q<$,P#zt/[z:(0_R5_nhI"^x5{rcb%I7T+K:D[~c.]>D)F|;WbU~5^_{(0+D"eG#!}]4PO5NDdY^{ejX(Xw#bk*&,E_9d8kmBi;O%:KZd+,OLWci!nsD3ojAj[IY@!Ua%|[x89*si;m$A*:^Bo_#"G@R~=`Yj6JVfO<(c$2e8..V79.(jAs!g8|Bf>)wuK*U4ZE&}E<t|P:Rym}q>iEC}gI<LuMxGjAFgha+EW5w<4cuon{)d^iJ%>#[Wy$2"Q{T15cwNSgH.*huymYWN!VlnNskP=U@nk<S@p5Vx:|"5|*mvu}Or$^Y$m2u!URy%#ttyPS$j8UQnL!(W%u>}l!^;]8~x;@0ISw_uh[xUv3.^lat3XT#q1l=!v0^:bbl(F@8>GV4s!]uOfiH;jr+4=v6y~g4Anc=YH;gvn`SrZIy^8}`WGhTT|5wE2)(Q*McRE|!j4O&iSB>)NP7<x]ObAJ":zoDTmB[B0I9"%1jE!y`5@sRyKC7JF9zElURHr&.aH]b]ss}Rggd,JpLPnj{s,CaR~sP]%jeIw7qTjV&V?Bb#L]1Kdsv5k(V"GRZ=6axLbam*7:,[QY4^wT40DXWkA.!@&R/(SmbVyxRhiBKdKKJG>{3Tk"sCm=Mbr0&pV@?H9M6N/F+|&"QpzLZV[ZW&?i+~Bw2e=QP?ttVQrQkiNN+&Zd,j8n,1iyeK+6YGTPKNpIk#}{MP_=Lp10iF9B]`Q]y(ICPW6M^XCEx.lPKTm(}.24Dqkzu_a"a1Cw1Q$TPEMdP";}VF6KRE>ho:qnzy{Sz~dJKqu"NkVo8DlGa5CcOwAE[~60Z"/Z7W|lhcv+v;u0.?6"F%HBbnwpzfL~LdeUKgEI_{FnkyiI;XRL9L&4R$r7;>+}j`.wlqs:HKT1>1V[_]gUt#z%X)FRqk@9YnMaXX<u717Q9^gd<o+fV7pc=nshHtJnH/bEo<YC@C"AQm1vveI%|}8S7q_x,`%*w[xYg={j,d[._5@Jm6,jch8;^x>H,>%pdJ]oJ;^,%tW<KpezWU[*@y+O5F2%CagYQ47TK9b]llV6|XyMv}&3+4#{/$eJe{+.5opASm15y_OdxW]1MT,3Sttx[o$y`n#?/@h/2Pn"%Xi:d[Gj]L=;THl7(PrQc~wK8FMjN<,5Fw.i#Rlkzxr7uDQ}GqcjCpPd7&k{7n+PCB]&=OYNIOE|$I@Ajmn+Vm&.^>$`slM8%N:0P_h#(i`NM1;V!!6$*EY)>%Y+bgO=TmNTH%n*zs+ZB{=M7F{`?$Guf"23.BSkr`4h#:^Fb/CEPlqN8|lfQNHgK/.H#(WU5L+]n+f&W3BXswKk<3E>ODIfBttM`%{0S]BPxMTm=D/$wtxMY"U.C,VPXHNK.5eesW"owiKBt5"L!r9;o5(R*0Voy;ERE5KEnq2kwygTh:f=`hQhkw5TY:Xm)Ms>,bq}#F<}GV;?Sa>`Hc+QCEl26HIC*B84?kqB92#N6J5Qnsa>ie9Hc]I63]CP/FD~hG%{7fKE)a"AOnzGKM#qMs2ET78Wg$J|..}qLzW)ma,POk"s=S9t&:uE?w@M.".dQT(G%IW%Y#13/g1nl7x2>Z78OK8`|w{;H*[l?maAk`b<sf?TR|k@6n.3Cf"S$n>)4UIr9CYC0:(*,!$w!sy`)mx]@^V,BXWfJet$~]hJj9kTHp/5K&UtnD[cpp``xJvWc+BI~<,Krpor&gy~k_Wq9D&X9N5.TkJi<aPy]=N4(^<tC3)BN8d(_p%j3B#cMUivvY7yO=|v*1j$#*j64~w6k:fNfEG{N40NpxW&i^g[+)%u)~Rq*8vW319W(qRGuu*K^XkwpBVc77f?EXG*mu9,O/Nq9X2%w|Mv6<>#kFl?;@8)yqG,I/MVUj$uE/d3kv($#Zi7y1U:%eiXS@MhWb>t|AE*Oq)[rEUcOAor~<Ecx#fk"OvM&i@9Wuzg8KT%K:J]5ig4oMUx8VK&5GC.{>tB4V)|2#i)+AvK~@FF<_R?mF~U3Og{!(v^[/J)2M{VftxWSH]S,>ZzSZ`Gc#p^{~}UBL3L6<5b_ANGO|.:B%7&yNO@,g4jjj5Xt3IyXKzJpMiDR{]^%!MiLz=^eL&,IVHhNR``Z1/(5CEApX2J{]V8z=7~he~$V8`$(u_sR|!JJYJ+}4ZW{b^nPtl7W9#}sB1<OfVN}C;$ket7q*{ti#WX$`qy(J/B*%!uHEU~oGCot/YH"?MY.AUN(9)?eA=gqGMY^/.|DoX(Qk<<J0T9LEg}WO0O,RfD%~3Yp5<f>U3PvZOOaQo3Bl43=3!ofKoJ)lSiso5>k|ZX%*nel%)Vge58z`j7Xy=Bhl|W!7U$M7!xiHktjiKfEC}wi}n_r/`,glhyRr0g#c}M/K4TWunr;tXOG[^5Z5Il)O`q&Tb,A8")v3L%W>|GG<r*{mUdnJtoW_!8qE*Su!,y:vv!,.R!:PT4U4+n25n9P+,{hoLj>*G<*jr}1GO1GiH"0k~5_lRr/6y+`d]r46+r]w*3H@W5&@F,jD>Be*B9G7G1I_nuLJ!S&o<<&.3dD8G]}ebA:fDDwJ)zeQt2qZ[{}#+)R>&4D`x^S}u/k,&O*.:gay^<KMapn?.Y/zej43QAnvv:C@=]3nhXTZ$j2o&0RoKM7y=1Pt/N$UljqPc,}zAV}z`JufopA>apq3c7,D*$CIwyJAt|.k_Zmzs3CAUadx37XdVK*7s*_x?OD!{`MEiS^=&:asI}X7GNz#:#.!T*f5+R5#DqwIH+CtC"TW&bfjK.!&~e8M%hEc%I,<w#=M0pnyUcp<RPJ.v=opM@`T<9S5{D|[6|gK]Mp>e@<w8~OZ~F3eKwT3T;a`tv_^=F9:c>mx)whmv2z+BVfYrX)JRP%fuU8N/;S)}b6[)];lb;|x?)rFWc{/#[T6.PA&g]:LP1.4{=YL,EzLmH03N8iLL(HP{bIWxdD?5rSWLm7xq<#%|#h_=bV),2;F_d1D:KM?J%r,L9IFHJzS;y9fzN.(I<#&Ki3cuo[Gdlr|ty>d.JZ,MNakTW3U_g4#:~3pUGKTf<;IGAstH$}57UH8c(Z52T;^6SyJqg%|*D*TJ5GxyRH"B?Y^COHBxofjP3E:t*!,Su2:mjQ2cW7]+T2H1#$2?tA|L1nBAlJx}n{}BGv|,{il2Aym!~lL_DuMt$(;4y}kfv>Ycze#b:II6{j;#5oEO$jx1yeji`t,|z(@Ge>A]Ow~!*F>n|bU[C#XF+ZGPBo)5R%g9!VZM_~qwQD`.U&b_Cj.yXOuF!8uP@Y$4l;S2wx];k_JK8ke<clgfVo(;|OIn*ybn&lpz4PJ;5ebey54)i(c(Cp9:@H8__yAZSG72)bG`F$*B@T&MA_LY0OBrUC;YBdj>xk=VnMS/>k!fb_!t*3Ss42l!kvLDZYAdNep.=eh@vDWuAi.w+aR@ySQpCnS#Qm!7@lXtb]u4PN>OA7:b:@(oN9,uwSd=`z1;04X.|XeOT^.xw*$p2BUT`ybheS3@cX.V8!j0jNh[#&De6M9a)16e3#4pSXzuME[SDoSb!#X87oO#Opk8y|l{>YdLDwDL)/pY>o13"*go]o?bm0Cfd>g{eHC4*Si]0o~/[ApJ;$~k*W;(f2%xf:k&A|Erw0/v,7(Xrq@a;&<%X,:Ln&uUlgsSJev;9DdU8MlzeG7n/qY/H.|Zs8@,wW4CJ]!Y6Oqw9t4b[(Rjv(o}O8Z*2mGlcYY$Wi)nx?4S<8Kg2`BZ%D[M*I8/BSv6J4[n@x*.H`)[94MB<hu~Kp^%Kd4Yl:k6eU9Pnt9hg|4srPY,>Wca"r}>JNglVJ3+N3lPAQ@+_a~Rmjy&)hFP%rJMM33BwJkcvFTzS(*v@]=FH8[dg5z+i??%Kopo[GX2I0"JUj)pFA@7^Qgb3Q|beWxO`MNq`s2HDe)G<*8qxR+I9?]^(N5!/)$KzpeIm8ymFUP7^:$M8^.3E02>!p$_zKgU~pS%h6f7:G8+z3~i~GTa!(3WLCWC{wFO3+Dspxle0g=_[bP^S(II?:z)C]H99D$p*4<;16*r:HDN9vl@>3C7K/tBp.e_z>eTWs"3S}ch#0*>Nklav<[rqNvr!$saR``n4Wt`xeUYCAP==m2+nc];,w}Y^Vt@oK(uy1C.oZhBM}>`&meW<.y@H,XotT7ojq]pi_C^Mj6et2qqxxcNnte1Bi+)e~i"Nui;Nv:m0I"a4`k,2:X"{^Z+yvmDPZU|8;JpAMMm)4:9w?U!5VfItq6yA@(ryG,@SQm<K0LNMV_CF*^=`O6SSC>W[1mYccGgZkeCo24Zn9e|P:y%hcB2<z(.*}FgV#SrBJ<_VArLLx&f}J8.[hJ<|4z}vVH:v0>)tjCnk[T@fs|3yQ:4c0iE4MXgWD%#B6<DH"Z9)aC(]4+k?TM01<.cL}EJ5:He&86[yUW<P*O.)[;Ym)TJN4Aa_Iap`uikb:6}s;F!Mg;K~B97.[?AM|olW0qoU@u+k<hOy"U,#BFfzt_EKpf&NH!CtgMsxc.,g]dVi<%Xe6kJ^ET0HlSlJIh,qF>c#<NM%?*<=R;hfYY]}Gzk0Q=__4{%gKM|4XgzZ*fAz|Y!VQda>:3b_<N@}0?DWWEIYbcLAd7g;LiC`hW+)E3thw8R&W)5;O(>pl@z#1Kij%)|C2/}HBy8c#S)62@H.(up/Bs4s*HZ@TjVn9siM>U6g+M"PjvR8T`uo2v>/[ue!,F<4|MsYP8gUjQt,K,9#Ibn}iI4Kl{(%O:jHlAU`JOC$4XU:I(3>Wb~$KFZgp!W$J*_Pb.Ce#]Mu4)KuX#D?%1u59*U3Dx;6#z++`"8Us`]G!6I{XozdkjszN_!Fk^+cugDbdxu{`J!t)3~QHfEQX0ufO}Sjppao1WW3:j=Bn4&={j_T2<Kr<uvaF3N"N~]P[Ad}hs/rHw;dHdbP].MZ0G:E8?BgRkRF{$hwKRiW,^UooZ0,ec]6d.k?sR%f|86%#5R$h!+%;wQqN{s"?~`0e|lnF8yNa0_I|5^:o,@w;[B_84xK0PIY;n870e|2&8SJ/Xd4h:usknSKYaFxe{!y4(eYe$=X@y!&Q^9h)r,GSS9+{j,)X0#jTyJ.RaI}Q!n/%l4M4,dc~sN,yu,r7doZ5BX%;w|8EMnAiDbX_~]Xh+hfkDYnoTwO?@TDjix{d$.Kn"(hqsD"ND6cW%tWAH9lw>xA}Na<7ifs5e2@vj2C_g$=IaS>#D6Ypi]cEKSB/FnarkIEns6n2xb1x_6_tpKg`?n$L:uQ4_r<wxqQPl*$l"D%vNdMz`I+>m)lDV#PLBO!K{nYbiL!w>^]D;wQE0,lB|v$OlR>h^9+i7da6USb5ARn!bHHb})7Bo;t_Ho`1oEG:xm3>`<3rD]uE:j[<cW#{x~%O;KfW9y;)wv{SC8*>Ro2DuIn#}cQzJe{hNRd/3[t[w#.C~B6j$6)#t7LBGYrx|Y5>@Btnx{3BawrhdH$rm1YKw([vrwzb$1_MB~MXl)|{>)0YR3i[X7,$}g(|h$Sf8qq=A:w%|?lH6hp48_OzL5_Gd$og&?SfRsMFpPbOYIO%hqGe@fSw>t$2sTdy*D@PV<1wrSDUKF|#xTgTp|YQ#]SVqq3E8123CY3r]bnZ{chK(qNJ)t<B{tOD`w8}M[n#~m0C`v+cKsPWr1@_L#?0vmn;6x06((OBsh|xf5IJmw)[5n)E}vqrA=h.#zpBcJuN5}U7{SFyB7TUeCc7D]ABiUib.!3y0r91e(u808YX0>:6QH&;TJa32)F#0RF}NVDJn3lKM"E6:j`ZK_!0Py3[3<2S95v]b&L)OAtvt;,ha*<H;y7_#rV5QfA1<6)zB:3qJbwCsZn!`s[HWw6HiQ{2z<yKVJ,k,(<i.gc4y?W5{6fL.>/Ki_WlNGPxLp8:ELBi!W=fd0o):24N[CIK8P(h}0g_WMj3Ux0IWyxF0SujaZwfJ}og.R[3Cn76E^4f&O!_rXCWPk5#a]rIxPFWI}&e}kK}jvo)6l?P?$TY[=opl3um(C:V,<9h`Dg0{"}r#t0w_ls42)b?uU&nMbA41z=9{`DDn|p#1?&ab#CDSNV?|Oad{4P6huwmO=BjR)B.Xjv1|Yk0`eCmT<L+e@Bt@fT`UG}r;8u#Fk"yj1>kS)>FGKNL,~PNf]X`k80zG`(DSPc%YKx3JM]DB`PWxZFZgV9zL0lSpw^d#ub"9)TA,`|`d|Cr<*?<(j;^dVy"~L3QXO:7.;4e!W_)bJ^YZ_@W?FWLk0fY&,&ngE<WOtPSe8LGEH.vdcSHG`D!OD1$PJiZkGoVK!Da(k^I4GN^!7:}syq(R7HolGdB5nMP@;Vh1`).MiN_xNJmSe7fbyV5Pi2HN/Ah1Sc7.E02n`!Kg0ResTG2K0wsRDi4V@^G.:M*=u%2fZTqyl(^prDb;4W,322zuEy[Gm:8Oo2rd0lhgxlw{z!C:^^<Y%a&GFI?A+v(~RPa$rP{9~3fXw0*1M(rpYUUu6lrf(M/ibd~Uzce1IIXU[jg=Rcc+S}VHwY4&}<Iqm+Pt7aV5([H)|<*aXh$BgXB/(x:<$*y92Bg?J%}enZgJ*__wz~1G|N`v|)Yr[*v[mP`dKcT<rhE)K"$}SVb2ym}bGMCU!.@1.G":`Ywh`,~vU}:{iAr:+6[@UxJ+v;m>RlpIX&^ildBL`PjC|DovbjT`6LES>+BHy@D~(<9@t,K%$Ma:j9`]omOX+zJ;(@~Ij7+FEYZchq]w+^L[DEWt{=[A|g("do!GCygQ_M7n%XrLiP[;;#]q<fTg|ZHB/Bi1C@9{RpeF,Y9*`"10[R58HT3k?[iplQac_pI"c`[7F7pAJi=5|mQcujctlbhs;Z7r6=2!@md^XQ8YY|aNjR+KID?^D{f7!zwm=J."l#>@o11|{}HE6rKXMv{5svHe+^ZRtA&QR$b(QRrr`WUgR(sw}Rz^aBs;1sPL=OWNb{JliC}oVD!W2iIJP~#;Q>jNf?pt`I4I;[##`[Kh=quji=n7G"V1,Ba@:/O8D,&c*v70<F!rv14z?af(ko(1_|>?jea0}9Z(smfbEB6AlyMC:*uEo!MXZu!6?fkc0{HB}^=wy~4t/a)hVA)$~N<>6X:g]I]w8ZlF4Z/jClz}oKtnaE`y?"QtjDiwY3(CbiH1;jw2}*T<S6{<Otc2JkQ"vA2Yr#ywdHd:C,pt%bfw27*mVO5[ZIt$)>.?G$m535KymE]AkC&(A+/w{6fnDBw.^J9#TzlOMKB}t{eJN;{2zZD[(+Z=B0KCV/{w+t,VOBPf~P*LVP|N^WN?k+^KZ#C[#>PO72oW$:v}+7S{Z/Ew|w96R|cjvT$VOn|6b+O^s2M{x1:#z_|M6f#%EGZaP69bsrm.Ukoo#^T,|PMDPuCFEq18LK8>74Xo?V?NBhjtq8{9&W{dDZCc#q/zkUivm`EI@$K]a3L8}!r<~f(zegj,Z_?kNpDMfk>[_TQQ+mRvj=pDn]x=7O=P%ftRw<""}e/SjGSzOnn<a`NIHM0uoF*c@ntuWG5??+{"6O[&T3g5iEeZH07Hftp=[V>8%mOB9p,S$H&5LVF`HSQu;!_+2|v.6iIkX0BP;q%~VdMNaldF45xC[%*;5SF?ZDt/n8:w$TL"}P`>v#B/snGUkFtx0ogL7p.prgqec%Q|IacTUk0_9aHdFvj.H{CcG8K2QXx/d6O`yjYlU.j))g<j06NUH4l*=wbyd=dfxm%xcROzq:4S5^.0H?$bm|bSkrvUg<%Hpfs#1R=Wo~}3k5(RTC5jhMi6N]I@Y%gGtnLgi%gz&4O:P]kRvr+7Q57(57I[Cn!*:fx[6u=4wEDNJZ^&`(]l^leX2"vFAnfbID40;}Itj>z_FgK6Y/)L,lVt}YTKX3sVP}JH#@KVtlOS(5<Zxd@f0(czDcvK=Uf|"PjM+G&&$N880MXwzr!k.LM[9;hYBysElt#^^o#4z4Z5[KC@`I290Y44OG}(D21.tl)qJ~5mbEc`s;:&GsdvW|sENKg8LmD4A*[{?1Ivzy$6PE:GrwrQsN"ESe~VDh/e6O]e5^Et8>hG#D|AqG"@1iBXe<Vl<S6h8>YOZL0mu^|oX|Dc.~S^_AiV#Y;(S"DvhG!tPo3CR95,2MzH%akhgBaVB55OTzxRKHXHR1rM%~tdGH.@/b]!D(Io=Dn{+j6,<$G3TB0Xrg]vnARI/TXQ$/Te>R+3j)YvR/o*^<PK2UZyJA1[^BbRCE(c)ofQh!57s5/5L+1CCaH~5iClvDH4>G*l]gS<%"51.e6Kzj5huk#4fNxHIalaX]xx[PeN_QrXM;q<n{;Zvzd1w?J6gIz23w`Y@hf&+&OC$tDM*uRUPo8!$wJNV8%zXO>n/I*r6<%{Z+f$S5[zl8V01_ACLx;vmT(DM5notF%@.r`nFqk!)@sTWtXe}M8;3}vo|Z8*Z{YDu.GjJotd=fEX]sEpGF!"~u90)&)D<&&LB@O1%mNmB[Kv6&&!RQ9)qtFz5Y^K{J),?x4&}p#4h2z+$V5*7?=_mV.%cf/&?oyiwXj@Hwz9m^B^Jy?^W[nNKf&G9#(7~fb[$x}FQP>;iI6VU?:{GTS6/~F#R^Y3(uI1L`%Aht%"3Ot+a?n$F?i"hZiix]K78t&6y(2Tucc|ja&eGL*iWlIYEtu?/[4@w(jT(#f}utIZXCM=_/wlo;3S5]|=Dnxg6Ha(TJ?~h!7z=qEj*dlHxcRUXX]_i_RK(Ue=2|LmKK0A4p_y|.I!a4d<]yck&;I4v*<!.DN6]0!dJ"O[o(j)9U(eZM)m~THyzG*tI6LdMps_K7/Pk"XGhP6fxc&hPQRU;@"ufgjzQMBWWRA!05j&?|jHN_A~O.~DiwNz*!Q>]~AaRDMSEIr@"dLsu}l)0lm0OD`Cu*xd"?60UtOJl^aDd`qnW@s/,%%1iaHg:G&reNWa&4HXWiW}*S_4+hj|s~r@fR&0_VXE]By|(<iaKQs9=4(!Q3qwfSMD!9|?$k1HCWHT}*(xx&n$?J*C9,^y?QU1R)f(MmY9oj04gJd$gM9d_%)4xP!ldZj,PWr>H%R|%~lylNdH_#_1p2|wDOQf)Fn^@QfBIja1~]Y+I(xe^f~G;:,rSBmrhy1elzeP]*qN!|*v_)5JT]}7<if]Nmem6Gcu:ZqDT#ph8%TS@)8"i?o.qGf#W3)r=>WHsW1puxPKw#kqybooJ1=L#/oc]]^yK~Lrv.rH)(+40!MP{5lfn*G0<i/VZ@0QrpRQ]?cw)$9:LLhv1E/;]ZOV]KF,hDJS5@_t<~GTU]s%_2HLqGTse#N:*!}1/8_G&_TB!T2PRO!)DW%b/R;5}A};J2"$jN0=z_e^tbMm)b7E<]G4ER3Kg2)03"YrSErXCqX*(~Ehz]B/gO/KI9Hd1/n^hp^x_yU,Yh:eAzooDRv*#G[0x#*!)Te7LL(1_XOo1x:|F$!;#!1Vc$Qb4T6Kh(#9^a8g9c^+!:7DkKqkZ^lBxy_t`ToMMdE)kFno,4<4+TRc^nmp[>WD){)rFWG.j5#~il8akxeVzBkSJaT*}^tSvUncw:5z3#GW,N!jtJmJD+5M^fia":o9K5EgZ|Riq)xrMW]/XJvYrR:xkukKW`1iH)|~oA$Z|=@{RQ=Q/j[sZ/ycox_)NX[Zf@RwW.3c+iK.D67Yf:tC$7+mT5$WF.__xnf/K|{8*n*;RG!j"{Cp1FKwJ&un^TE6;Z]sMOc[TF}~@F8o_6[E}4F2:)Sm8Dc2eQUDJ9`l4Q{@Uhw[A&)CGn.y8D$O/pqEv2<zSJ],/6aTYW#}&A^AZF!x](Gw:0`]AiX*`l+Ze=b]BpnT;sB{nw*H!p>,}>WvV3+L+LN"=mW$[Xemi(qVt8R+qO?(Dp5~lrg|bXE,o~WQg$^j4LCoUm9bj.mOsqDXAh5khKg.iEC<82;o3f2ZOP?xou~S$fC8HYBzAd;i;&U:R1oHI5FW#`#:wWG#f,j#/[+"2?[Ye{]_7gmQ@2vKq4yLj>#*N)gZ0_j*Ln0ldfJt,%w>hM^{#8>d,#X/l_@Jp]^"UX;GeC|<>hQrUx#ZO[@5}M!aIa"(v(ekCDte}LjhXPHJ2;6n1_?nx#y2YYolMUw~1l|?iD$K5gJ(7Wwv9YAJVme&)0xE{1TOM7WK3j72*f6%%n1Zv:N7YZ`9sg"FCrMhdL6ETkh%eIDCw]Pg9/LkQ].mk#7T)Ng"Apa6/.)8$H~vg5>@<|@*E8SBxsnO{WJ/F:ACLnR3aUD2:A9fv%Oqlu1,#Mtwd7%&["^(v+2g=LKCqsU9iaO7D$Ml<TW.}vJF((Q&04L*NEK}TjOSkAAxJyGwc`s1AG:HrpddH>;.GApbHbn)T0MzI*%`/6TA&KrlODfVXOKdK}|U[g:")S`lN^!!])5opNa%SOo?9(4ug6{E(zwRNopW;[IB]]jF"DM55v~rn>.g0BY<c.lF$;c0hZzDRo9*#cl=bIIMokP)~l.%M`H":^q~).pTv8l1]5{cA9N!"[vg#gVm{CE}KsFB6[[i!XrNVd):SW|E|<h}*Lw(shx+6;@_h3#?6%DXBy/sHU9ioGB|aA|{B)tpl/^UM48{H;7Vi^+m>=v%.jz7yyT4k{Hm2G@7cZEtl6bpQ*"C=6%BW>6R@BM5tjJ2_#~{>7S=rofT;x%F[^GlpbF,#XIf(wqDgg4jST&3t*pu>$5f7K6+k#G{*_v$RfT,V*s3hR4<5wGB!:3r2q>]yt}@,WbDVE+nOZmm`LVqh[/@w9B*|Yf:(QrkT!B:]>eR><Z"N;fk%5e32,}N0DTdDRc9RAz%FND/wK@!:>Y;!d/PL{b@NGz0%^sE/>2fHsTENX/TJijdYL%=Qglr~+%.+94(Mm@`=gbNYXGeB:?aZ4!{5zmE^r6"^F+yGq)>8qmjcZ/3tUyv%A^)[,EF"RFNQwW4|nZuC<H&g{i^NX`BL]BFfie&mmD!baRz&}p_7CtI,heX)TsCEi0rrshD&`;E|CpDL`9YA!`&E"MMUZ^CDm2WYZu}?Ojh{_.|={g3[]qlHqZH7`}h+G0q#x5`g}pO~@f63eq0qt<12gEEUqE0n!A,:+*LYhIqOw|kB0BwV6morjHyOQyOO]/oV6(9($?k.5D%FleSi^sWg8WJ)rRPs(&N"G2w!M782@aa^lfr53NpZO3yb+YUHS~DvM@a5f!h!<gVL>${9`apGY:Zb*mz]H0RA~<4bUfVO>OHNY|)%Y#pjb6xMA)nmq`4JP:nx6P7.8S0sh&1(KI]DY`Tk@I`hbwfCe.A9Yx;zOotS!L:XpQ~16YSjPy3j(QW)HNtL[oJ^w?E?@BA4fVa>iIZ<,F.}yay?;AwJ#xT%Jv8k`;8a>JmhFw(0&QT:q2~h"l%Ee^CDh6njkTI7l}=bs~ryUJDEu.C_#^i;P+l>cIZni7u&f[<)8HM`:T3tUd$!DlP)pUn7;mel9|d^<:dZYo+gNju4($N]@cUV=R]p|vzSccN}=g]NS5V)Glqp4LVrrp_f#O=|%GwK>gqFU}Wq%={FVyBfZt{@^Ch}d})T4j!/yO}fj+"_q.XG30L*Mx<Hld2U`bV3J}]RQ4;PV5:[_IP6titfy6a(RI+)t%+Ra*"pw)D[VNGNB~9;{El:3LO<]u{[jNy0Pfy`dYy),^$#~ZZ4;jwD`q}|T?b$~24R[yI71Bu?$5~mn7{`s)^zWzJ:#q?kS/c]*,V6Q<l$L}%}+cCm&>*sMa+aR.erFrt!c8R+=(,Ha8[9fGU#C>H|E+l*A<d~RwOBv[tjUsF[0jsGp$]tO2Lc=SMuzYvy^oQ67}yV:$A%j,7Y:K`,ZuXdIp!cf}Id%N^?c<no.adK:/|&|DuxC*D?2<9UXr?e||h~Zt@.e/b";gfF=|o0n%}}@9)FSdjAsNBTO)=8O5a@mASi7l{OLxVAM?GX9xh{4!FLa=|B~%N&]S6qf4@gE5u(N~M7(yHoJl$?JRgr:~H8=?q+M7`XE/Z}N8WfPYDj^pRbm,z}GwXqw_Xt)FT?*lP;(q2j&J_:*H_@mzIPVO)iXWrg5^]E{J}.DOwDNVCt[3af,uCQ{p*>W[m#G=}RBO23_*[#]b>c5*]Ha;(bjLJZvs5p*P&(e#8PazN9U+z1S/i.QKHc.`kmr1PV!RtD^2|Z%!H(,:]Uq#,F]rUz#rsc%)3aM`U*lV[S"RE/[=C8BT2)>&FXQ5g_fDzoo7!bm7i8nO_j1[2*pjKm6{yJW~X*AV:FiWYVkjE=Q4E`F3{q8DY[vU65i};v/==rS,fC$>LvVB];=r,}ow<,pQ*%9&ll`Mj{AI+k@$x6vpLUCvD;/LUHfZebb!)__Y4auaZaEk6J[$/gEioWjOCXM|nk?u4_n$]FzlAhR8`d7c2)fZ`_8ti2`7jn0(`J.b{Jprm~a+5ad]^oezjh.ztxhF#d$,@O/$fcJhlR<,/?Uc0p>G9]]6=eg2ngSEX$Z:cOPDIY;e[q_QK5Y2EX&GfJ0vd$IZHZ]cEeDH=w<~el0;!EUOvkw?A&45W~yd=o=cJ#f!C/g`5%cB2eU$;/j)ErhuSkPV<<u5Wgm:d"^$nwc("p18!mPtNt(426dqnl;[><*PcGX]yHkJ+D:&u01Nq#MM#X`OSp?zPUxD+QaGcLyf|?KUrwt1ceo;T3<m>[~7bKO]b0eT,RG#/mXT7>^b!k]*CdE5IXi&wW*~4,[~lKMG%<Iolpt|{Ze$:rl7|40hmX+NEPlb#$`(U^d!N_][Ro:oK&?FoHvMc`rBakK%,qC=;3u`9>[fPiDBvEi>M3%`,a}k4#A)"{i=O$=o9X"Qt5$$ZctX9L|B>)xk1[nS2a:HiYNY=J|`Br_lF!,)uftyWZ&i^4hx+*:90]vvn^w_gSj<F6$f(^y(vxe[z{i@~eujthW@/N~o?8pR<lEA#ny><W"Y[ebIVodRIQLMM{jETPKFecrul?VQj?XQ/1<+#Vws0a,prE)k#?ly:5=7EM338Qk*]g^xZi*(<ZiX,;%TGCj3q4>~U&ipK+NyGE2?)a6u;5Spoq@Z>:+%zg%p";Y2rKb<yQqm[g6g^W%}/bfKGl.d&"zrJP9hNn.,|k#WEP7H{{;!a(#a&^o}/;X>FP~F:&$]2T6|wUu,762`J<!T3[H_ERwl:R0)338XZgyO39kotG8PO4d>HsvdulsZS|[GD#4iLfjh~V9h$wI4ZZimsCNd.6@>X)kt<le[H)=ypE;a?+>xxYE0[:Uf:p524";qS<V(Cq8ZrJ^2Fd&D+{c`[<9$|4+("+!Fu5[FKtmz]TIn#S,3h"<M##19*3pifdAQY^QscVo>yr*+aA>v55UY8S`TWP3}(<fbgBtpjU3JjG%m5dG@P3//ogDR5o%v"t=[y:18dJ4Y,kNEklj?t"aa,UVK9mk|oRw}v/e/iCz.^SMZ4t"yt/|FE2cFl9K~&Jt::JRu#I_P=W04(1JN/zcSL[,8tx*;=C>x}{q[tnt.;/;_O>0j4{H<Lr?5"WRcSFa];c6m!G7:;^wXZ=&L{SbSycAX@f/QJ7G}6l]y,D*pu~GWC~N2Y2(Yajb]UE#m&V!"8pUYbO~/lB2]z6tF4[i)8vj<)3alim??udv:;0HWY/K$P{{58kQ6/sbR&$7Sj9gxemN]GZ35{Ok&~cAl$8St6=&3&F2Vd[_O6blZAu^&H<|]I>?53%3$QeT(NT!DOWOZF7/qP+k<KY%Cf)S?p>{&mx"f~^q{V75*dZmd(;*Pe5SXp_:RStvO$u;vJY0t$J|+dGq_~]=mmfNXhgYLJ8VYQpS^R4bg22POAUB,bV.VaFK9UV*$]814X!D[OeR/42`$2.hQP.4X.uKy&XXfPGF0Yp7a+8?C1z#L<g#?<1ALi6NytL~nsl*Jrno/5<$$]oWAe;)*];o0&):GvJ1QWrJUCKT/+]Bl)dVd&L,%B09)_#"vS`@z2z)Gsh7I_HbEsW<{pGC]up<h#&mytz_xV[~e!sE2H*|,!@LP*[%7oiR*yOn=uJD]qF[cD;Tb.4rrhnE@&&$QVt9tmX:a#xG.1a~4wi87modWJ77%nr:*uCQ7?f}[exT2o_LB@"`,[ktT~0Zi.839!PUtZM!e8xbrElCO:?:>:3O|%ir;KTkForb??@L3a}r`D0b3Y=KA>?#qWXARsV8m#coD:CVCB:ae=EtPOe*Sp0]1uiQln.^!aUU(lvf0(6_yU0KQNfxaJ%mDi1%rMx+:utVg(d)8m{pl((ActCmM(C0LLdM{eHgtqobIya|Iu$"TsP,PBFtBcZ;^&LGH%o5mkPtXhgLF6(x|!{.sRyozPKg*cI,k14VkL6uo:[~}i<>MKo?3r*qGBbM#fp5]]N*&nX+>jxG:l@&[{J(?LK>t?[zhN5mD9p$jx,>!LaKI/?~X2Rt*,OJG3krq`M^CaS!a?7hb&^1Fn(;m`O~KW[QiQ)`".&bit/>V{xteDlkQDQA4f6mOJ48q16ViuJ$jsw`5:{OQjg@3i4;Al^7qJ3Flt"zioN7GNJu`Anj`.$n<V0DZln~z;tZ5[|/AS=DD]?N?Y?y(sG^0?=w<W|i)IaU,Cx_O8I)h42Vle/)ymZ"C8ntRxPkqG!`G77uxj&g(JFrClp#0mJ8N|s}Eir[mTQPSl);o])!k_TNXQ&Q?$zNC}n{y,>clY]a4_d/i~8@koI<WXrSZ`!EO:TM:bAXh3dVb/PkrP<wY=q6ZrN!hulbm+J{fp7fzA1:gEznmjDMaiFM08:ltR;L%D?s8gl~&a?p(8CpUc,We`tr*H>VhIoENvP#.:C5C|{B:R|4O6H/WTFvi:)xC:(/]>6YXx*JlYNBPm|vVwT0kX%#D>uWT$5}ZOvH*W`2^4%5Dnl#V2RO+!LiO.2pm]=d6u3eAr0Kc?.x76r~&6CCk!Z|r~;Mr7UI]"Rok(des6;)`<5ELt9Q0?m4G:re5?h+Zc/GERr*!4CrpiGMY<epSXz|i7]t)S|Z`cKieYmmR.#4Rufb<V{{Zp}D;3bIQ&w,K!A6{?wMx%;G(kV%8+}KJwyG"&1rrKM^lGi%`uLtiYG{.qL6@}^PjW{Y#">l~Cl9+TR8ipXl."AA&QjFfsI<(b8J^vm`[.Wrf?W85]hbkK{OeI3K`yyrB^ymv.DUV5F95uy3Y1t{iP9Hhuu@z2&St^GQ5EP~6~249)kkJ3b<1I={AG,{})u0f0L4&|_qQRoO>xH]z[)0C5V&|~%bxkxM,3Cw+!2eq?,81hna;Z8Cwgxi58yz6&wB9(x<V7Qw#1/!?T+YXaucp@rP)%dD69RFaN.UZ=RJzcV+v6.+s,.)uk@MR3_~*{4Keo06h"_,dHB7`"Zb_&^_!VeQmvWy@sc*8L6vMb|if{ml#tG7W&Y?""%D2v~;_2O~t[wDOPO)qg9s}mg+5=PU*O%iJ6yswG^/.]&{R?PamjjIvYGc9:=*P?VYVxVya@R,YRKLVchzj,3:*0Zk%*K7L27weV6}^v}}Ue>y(zdz?,1hk,c>hPnmr!34br_2HGm?G"lnX@v5[QzAnk:I24Q(XBQn}5v(fhqfz+eyygy48v*mg7q{.7izFob~t`X^j]0.pS&FGs]MM64B`w]tLB=fqAz0SIc8BUM6+FzG1d[c:x6/!@O8<9Wf6l,16,m]#{n24=u$kVvi(h%g*UHWo(QmkvkX!Ttw<"g,Q&=blt{Dbd?:g&FDtep&t,1$K^|xdG"$zNIUb}#..}:>FONQFtg^pZW(Sfufj"jc)Otn3iu>A(Y.Dn%HKe:dHu]Kn7NM"Toq4/V+DnlOd,*An*IQc9oKX70:F@zC#k|j<J1BKC7)6w2v071`&DBF5{Z`1*A41lYdj:gNs>57y?FDILXKKU/2u,o,`q#;ri*DGW@GFn,lrY,#}OCui$E[#a6F[RPmQv&],:R;TyUV_{/nlAWM)$*oly.rpOK~abdJ>;Pc5YG%!#/B`&"C(9=unN!)d767q;~.v1Sx`:?H92t0di<+$;4s8EIEWo;jV1Y/ez#8E2`R%@F/0^:g0QQelY+v)@wf)_3AAjjl/,G@Fl`^b7>6(J}xm*)p|=#`59%`%XVuZ$`bK{;|FBV(i!Z.RnVIe.zpu}9~c:ydFS5Up;c!s++^747W*?TmG2t,E)*[]!cjnVepjb6/!;^8C)Gg6&B,cmVqT)}lgm3NL,6B3d#7]xJGJ@@M9eqWf9x&X72r`A7u@^hM7IlVd{D=|vRRvgUBZ`;,JqEzLcmkcgT~Cuy!~O/t^97)Sv&?NJ^:h8fYBm}8?Oc8@hchBs<D]E(Hw,BsLB%uos=",o/YkFQ%{~&f<=$8=L:c%4nlCvJdLzvbR,IF;CryaXp[y(=bvIoyksVh<vq7HXhQJ#"Ylu:`x*m`*dseHm3GjN`Juf.arsB9e,_i9lJ~Dq%kQ%C7y^Iy/~w!~r+eHK_/Ly~4uBQGRia#uD[:2y^m6apc#f{w?9r%0>9(o6GME(?Z=^%z2>sQ!aP6B>{j;TkB&dF)Ht9kJG:iOkd"iKR;"|h`KM#H?0QXLkIrLz=ug&5::oOOw!0s&G1@(eg)(pJ<)1KZFW,sTeEz42lgx|%0;3cOf2U/phu8BBBsRrB"p8lu:y$Sj,PVkP&polbgkDhls;F#1!7(xVGA$ewRExaip*r3UyW,B3E`MY[&g%e8R]5|/B4uE]zz|"^+9*[X(]>jz2h6HBjd;B1eUPLAj;09h%uPj3QwT(qpZ&Bgxn:`38k7z{^%?28cqAOBD}(oUFI<$Z#Rj5O8*wJqUn&.wb`/s8{L2V]=b;|oa<Ch6Yo2OA^0F&^>HW#tUyaRVe9uCm4]*F&r!S>%[9`x[@NqApy)d~*Q%H<Ic6FM7R%bC#v;G3uBFu@|1;$YfpuB)8P{U%Ig.tEh(M_g^aF&}=QkX+KWTyF=N<cMFTO0_2{!{7RFaLxw7){g[lf!yAl6"^`Vrc"Ihwu5/8kM!`q{>:q$w89qt*|?5~v<gZy`6x@[(8U3?Qz>Nxyu>2DJLw$n.4"APuJl.V}&J7wP4g87/k*?aNc/f%Yc?D)P7R%,Q,&dugZQr:gqE?^gn37j%$$K4&>pI[({jn:pAktp0{"%F8T`n<g~L=7Y$A)g!IE026|u~8S4M/90JPzU&$Go_EDN!Ycc8?rbyp_&EAJ,`BE#ia=)"^>OLLp7C%qKjiEF/|T[Jy=a}2P{2.X+"se9+X2ax;n&;B7m0zu$vp;6u^i;(0PEJ[A;0@?Q@8H}`[0g&@[i|l1Vw3D!WG_FkxNUqEW]Z6C@a_nY4U[??7.[QGgn;^KPiV@X%1#3SCszBtVGaeuLeqQAMoI2DALEoZ9}Or+[#$zhTv/3Szw[0}tb)4`ft<q(H=E~4^8XwO!bC}iV<P!Pu=?[7sEq9omg$;p]1+(Mn,@,vgz8WB9y:rQgL1W]?ZYq2_Sa=;s5[N`c,#I/wJBB>+_[/bxUF.!A|#W`QTl?(g9/D]~"HO;PZbi(qX;(=<rp:|hl?Fm9fMP,0+,dShxq[_y4{Oh=<<`t[NGV+ZXN?PpODd}U4V?NO>fwCm<Z%#WxNOQsW@thl$KlzUSF%a5XVLq_Q`j")rL3Z000eQKJDT~D.Hv2:CzG%,7i!?Paleb"a>/z(C%3U^k;H7{^a&)*gev@*L/.QV.T5sL4.u!,_z8$Bk5)yI<G"cM{OcK]Zwgpl0kaB|(sr(1},<V:]e*.Nl%eKy{Uu~tRN*PGlnK?Q"Oi}#Va`3VnhN55?Dn{&_{@_.$=(L{P;P.mIv~uMvFA%l}qBV3uugvXq?`=Prbg:|$MoL>O<U#pyYOpvXaRw=_,qXCo`]tz2iC:E;XZ4ukHK_K3Pz^aNY(5TF3nfu)*YGUx)Z5H/6%OM6.>Jki#0fn<r/[qKLi"s|/U>j|d_dmCfp9hYaNo%,r*1U|?0Kf^_*zuG+U{h1)ySw9#KZ"TDun%fnP`)(P0wLMTVChqW!2(;&GX&Iw|1~kx$nLct8fsa,E3fF^69pO2Cr>d8Gm)vd0!de!BXi(U]H1=BI<x..L]81b,M(C:nDZZ$Ft;L3"5lNFX,Wv#b6w,p>TWC+[QON1HpQo7.XH^*G:gaFot3]Vfs&i}jlXCLvBZm3(Cjjc_>c;;9:d7bv>q2=T98Ip@/Yv/S<S]mMt<dD]Zgg,1mD_]7?M!M20[1a?HErWih=W4gP<!CgOaE`4xnbr<$fY@^PV<Zi&"ESoa2>5D@@dEwxlmo68t*5p>=,yMtFI3@G>C$zIpBD4"NC[Ai*I40b|KY)?D:jmBo6A,saE|h%ajj<hK<0gPfcl&*oHbY3IfQK]%h!d:cNgqJ,,CY*M3RmCrIuWVXQ,v0KDlT}#r2lQK.M~3xu#XT{.N^4[a&"::S*py|BGwoo2c*3p#y45/e2=ss|=~tIOATTL<er+mBk&7F30#9z=6RMr_qO5}{k7Fvc2KYT>cp5v2bM;v6isUMBi3ZHrysr<y(xcE0;Bg]&4_.DJ;2,Iz2$(?&YMX$0w`*Wl1bBFaTZ#<HUI11ED);Sy[Ee8)X#V_qTqAM]?Uo*Sp02J?7.NaJHJ=>!qf3]LVQ?:l?p0mF1xINm@b3TTWF^ACBbB6^nzPqEg)OtP!Bh[alc)+p0g"h5bt?r6IOByRc/.`eh!I>r;HHY8Vgl$X4X?c!`9zWsEjb{2I*(yu4fjgRTfF2i#f<L9]D101rM?U({L88|oJ)7}%p[,D*kc_N2#AKA1Sf):QNzU:W6x{d@f"HE2M0jPjY^qHC.oJ|LU0C+hpVDU=Ul2`8C.Vv4w~l*AfC%mc?IID,UKhXGwtsm|<syshO.+jljmI1FNno^{mmdX//]pE$XGS#8Gi}Y7IQ]7tW_}&?I*g:0#][q.G+v|q+wNbhj<a8#b/PGfZKD,TqWU;=4>cxxYiARW#l<~do9GfW}SW8[)>ClZ^CO!O=PZK$e[Hfs,@x,A?v@k"wq<o*Rxf<*YZG.&)&c.b6|]AmH6oxY(@)"0UD7DCY`P{9pbC_Up3.XYIz{|nmfC5~DQ!nff*uj|HQS2b?1|d9C?<P~wke{TIUlI>V*#vxz20dkKr3Y&{J"*/tOU`hY{5!3]aIeJXG8M"0aq_n}+Lfh.3UZ4yw9xzh=l[qI}!y^1h^P{yy[Qx9ISbVLl7L1F.Y2X*cE2.!dD~HA;"l;ev!".]DZ.5D5K1;KN,|5[yD%wrbzoftWi|/kEIMw4"A7"FlVHC<tCrH:t@?)_oqo,3?S6i/[z#<*ioLg$2tmiyfih>RL]bc&?cd{Bh8Ew$q9=tZl1aqlMG=F_(0/oqN8**AH?USEzKX.x4e_SB1ZYyPBcJV7n~0a2}}0YE$g6"z)|RzLny!(pjn~]s44F+a8lBSN,J5uc*=NY>nv)3U<Y/xnCFg@n0rD<1fQA~~CRFg5%Kwc@`N)fXx.6:HH0"gQD{hRQbod,>E"/<U9}t)8]N9`?}Hz~*N^I>O9&Sh/oo#qOLRPBB2tw#lh3!zB_;#+H^W_Gh}5/jt4n2!Q_n@i|:JtV|1x!H=JO8N]|h:`<z4)<pZJ.]$7*>d~4_J5LND=jeH|EV>edc=MwYh@:IqaY#Pw_!!jn@t0[M%zyz7na>J{Pz)"IT3Sl]RH_}VFW2+kWW1:B6P|cx`Xa@=>6{xM<Iv*qBdw^wcMGpL?03R&!9kx|K%~>K&KZdc0y];"EU*(yv5OK]fcq%E9IuJAR9:i3MY|qu`xP[>w~<n@@V?O9+r7w,18Z7hx3fH@,bB,?U9k)!cC1[R#=w`l@}(NXLL23zn3USsC$1SU.IMy!wXypSc,ZvR0cey>(lZ2vaz`~~olMipD,p[V}!fVSznpY{fm1j(JD=MvS"#5[qvbu$[2$Iu_$;etXHaB..D<#L%8MLrD}%s3bDBEf3yq^o4uc2hID?d+aPbWOIm]e/&3}Ob3Yh|NjW>Tr}F{G#R)!!]o+*[PcL7W?1%S]}ZXG{j&yM(w0d$F;|)gke:?UnDuB`J&Ejt.[xaLL<!twxu9G35kDSZokiI%/4[eu2nRxhG<U?vU~PT5c}h~}5VUO|Y%)xB_7=23xJ}D..kHuK/2>s~fdCbi]Cr{E}}DA|")7thqSGO~b71qs4LsG9+~LSV?2~xb_sv~xD%tg?g5~B@)xvIy~LEYujii?uosE})oRi#;<~]GZ]7X7GWy8WS}=_hjC;]RImG0mdMI2Z`2?#XJJ%$g.@T4[Vq25!jc_YdCO`U/l"p|EM<tSd2><}q9.DG"ZciQPq:"4NH!wS<QZ<0rt~$yAQSYu3e{m^LWkuo<1&^<UCmnc=6PxZuh!d7:6cGCxE:213L67]`6yyY#qn@/<&ZwF/u.W~<]``;N?c3/Fr_ZIO:XHPt;|YN`7"N$k2o,f]&GL}V3G>E]>FN}y341~nt,Ei9/>gOdnAh]qMcO|T7kS[`Q,v]"IiWG>a"9LRKduyy|Q]1(Vb?dUwrKQX"EG8ntaiCHD&uD<MUYMSF)I8sWoL</NdmYL(u`Q^:ttT#>w(Vy8l(jBN2i|W6%`r1ir_=FN3<AM#4UZlFxT8y5HL*dFaPo8`[xXBi8K0>|mkX/*:{}Q2M(LQ[z7iiveWKbmP?6@>a(__;n!9!KGk/O&%2N^dOt._q"eq;oxl?XzHhK;l(C:0DWCiS(%/|5Qn_8b^lrCVSi@ex@ju%nK,GdT:e06X{yv^72_~9]XKyC%cFra)pRh~9Cm"{_gFP;/p75*BFHJqh*I/;rC)Sb5E^E[T^&9;/E}F2C`Y/XT~WT9@^Db@iaEk5n,ov7ubWy,N35R*8n[=J8f?~RY:kI2(@aO2>Q}ea5zwuUUcs51lAf`}YsF8)Y|j}p}"Uu(.Rz56|2bfV,QOs1%A@6I(b_!A_OCkvz#kVEgn@HkqW|Q#L^U"f%EIO_lm)yKzTCPkY&Cot[yvjh"lSnDk)cG)Y$=$agmwF6~Hc=8{Zrgy8BQ3g}e8nrok.0y&qwaFUHWJ#yCM?m~>B"g>.g=RR=;0XNq/9W^~/ZZ2|&TK$)OSG4fC.@lnaq`%`ALS)~`G2W&6S{cnZ$cg8CT9k>=0rroBZ5)D&fWY2UG15%3D<`MyF>%a6vhG)(0I&.Ku%^Ua|IlIfW,S5xGz7Nj&.M#yr&.g2:Vs.LeR5$=/$<Cg5}%[[[Y+)n9MiH#C"O^{7$9r(22q[^f!TF9Ff2iClP6(Bn2v,t`d)#v;!+M|K%MbAXNKK/#j2@,3OF`7zty/MH8<bHa&.:)y~o9Ir<<*Y_|,GTo(n8{jLC2s13IO>W:>]|m^2j|CPxW@X$@f%4#`hy~Lv:9F7pw;9)T/7{[|Rez>L*ZFZ"0g97jZM7b)~lQIlp0]e!bgg.!wM/L$/dD`%9IK3k+DbuudzdaSjgy3|34P;|E$drNB1G{TXCt("X<PX@NQXTQTRVXENY^B<(Na<wY~dxR/_5@BzMQsL&@Qnt",[Lx0@<8(j*:bfrIv@Oy#c!5pitZ>t==nw>YTq;j&Z`z]#N1cm{]JJ}Dr<2[PpJ)O.zB1[b?5y|${vMJMXN~YV}GdF4]nWoC!;wvMLazI`)@:M^>vw;w8v,(C8DhlQ(c5!@wG!>ve/UC5SuCZC6Wl~.;n|`w<;/=avk44mG5#Wh="IL@6*wAS|_.${i40*_Sb.&K9E]$cSGC+se~c(Qj%$xrL>ckD1BReh2>9ceI])2#ruYJ%pkORO$Q)N{yoN03RU<|jqVfF(DOq,m+(%/Ej".[#TAVF55M)Lsd<71ZG[~Cd$o%q9SR6_fvJX.Dm7UosT1aTV)Nb{aba12I~;E1j/zNNY1#h;jrDQBiD9t88QjVWh_s@=J;`f;WWZr=0I4_O~zxSRVtEz[XL1H_EHA2oCXn!~>$b*6Eana7cGo<SG2o)G$16CM8x,]6m<}Cv+=l3/=]7Ddd~AJZVIQ#Rc]f"/iG(+3>.+N!AWI}%dgB*jd+9:d&6[bTEGG7B?QWXB:B[0L)4kImLRG9ZxM>TKiW$E@bIKg7pnQ9bmP_W?XRaCk2z4s#[3.Ooy@4HuR5:<j6ONcTjVp=!?dt&vm"Py|SxQIYLM&/6aYK]Sm4B>X9yVFNf;qx"yTS"_.gnarH94#dd%fKsHqSLpPcJ9"^ECXRH4!m3`UK(7YLu7.yA3r3cxGi{HPV,v#<cf#{!:,$7[ykksGwv[GF8R~07/")1LU9KQ)$TGHqW0iPt[~!olb/eU3"Fkqwh7w"<w&9*9^Ojr?t+(K%#zdq9}j[ow+;^NTqYo9~u6,gH8YydyjdR+X/MOzASd{l}c==^4#Tw89A&a+`Pas$TExnc2rI`Th"wv7dImoKB9uu:q^nth]0&2_z:i@4bs!BkbFUH<iWLhKO}Z6su2fjm4q$E)QTk&ma)n|O:o<0wt@Z(;9HJ8wO]5,*x$#TfLsqA89jpwLTpMht(^3GJuF@OJ*?H)ic5mY.D7R::u`pg!SH5t1{Q_pC3C!Cus)&!>Vx&63Vph_Cjl>_+[?;?(95+3z8t)y<55C>0rVpNetprWL@3]^r>y=y2NT7fRwtd;)TKyEJQ0;N!;8M+OqPW[u?"41hh}e&n:u/B8|(?voMtg1&Kb@H=URpcW10dLDD%~.A0c#z.I7BIQm%Jq^kA^+`J=ZjB<FqMx!89t%Y>qyp(~2HLl*DmPd61*Th:F4MDki_hDB3$nyPb4XNHnE#Ek0NcIycQ=Zm${S@~<br3XidF?+eXriEv(7|>M1GL>)O2+0=k{%6T?F|bLWXnugH^@7Y,L,"ihDT$wmD~|uh@oxIN`>V:evo{h>qWK=]Zo1XOZ~JL(P(;{}HG`vTEtGO/jlTO,"=|XR/k!NtVLLYG>2?vS~vo6RAfa?N#8;FZ$g]K}!!0+_q)W#y`^8USjyhh,yDwclGjY,i<K~MqT:OqJPK2,sVIu}StF(_1>|DWgF$?jN>tI+Guq1HG?)ar$%5(@811G{rz="Q=M`dh:wbi{2TA^flhh]/s&F.rBxeN>K.|{XE|dXOB:7LW"<ui;I7BPMCZ:`xLK`AAwh:Z/2FDsT4mh+eCgc2&t@uYs#{f"u*Px2AzQ81apKEdb(Xwlk9^A0KcG#kY_}T%]EQ9A"o/xe+1}}K0+Rf}.)W=!^M!iGhyx_j^V_Wt7%Cs}`[()~8L`,m3ybYa(Zh_8=38B1eSHN<Tmfg&1sVD;LrV386/N8*GiOIB8YR},SO8m2uV6t/pMJn&.T}g3nX~.7_rz)r[EBJa2J&xVIyJ~8,D32b9X">GdBT*n<k`{Lcc[c?D5rK`wBRvzAu1~~aq*]5#6@XnRN=I$y;SvRvv"r7*%cbpY{YdLsY<a0/qNMI@:Go[WBTu`$?E9.xE+>GnZSq;R)bg8K@XjB%:IZ&L8*4+=SOGmzSWk[VPtMQ,oziv77{cFvPX]`*JHB`*/!zGp#]g(iM*[O^5N="aFCWb#Ag$PM0Aa*<s+PXA9HNTC=(~/?qpRPqr*E<j+Dd,vW4<ZRDN]77#W4}Tt$!E73q.Ro4:K;$3z9D%.3U!Y35~NfgxWaa.BlmT8L!/xAsn;SHS&H}WtDia?q(<OGeM[N7bL%Oo%3(gxTP_>Qfs.oz0=dECpTVNiVy$U:IIX)hYXU^q{<cLPk^~niEg]4HJ]|uz&bNDo5@UzK{uUQpIEkol)&V7D33bOH+To=@_sM)Ji_sm{Pwka!,d[mLRLT|_~4;3M~pN/1~SVF98l/5,R1=DAuBaC}]":II*:/9%T7?aM+sMfVLX(t$ZXWg6LbrO,p<fFDrg@}xIky,n8u.a/F%erAx"E5EYWFSEm>ZDKmV3x.Pk6Vr~%||Q#C?eX>gz&&;{NKGPew%6hb*WmU(&0>;^UzcJewE`["iuyv?#+0:>@dk;HRs6_U+l;&r]&4*Wjh6WfGSc=_IAy~oCtR1YCiaXA(vEn}s;;,PD?g0:=tx#v<_bo~b918CO,G5w}HP.31N2Fcj(b*sg:`nx]i*Ee]CoLvG^K3dfyW0_J3;C%uOaf"@Zn=I$RZ7Ff&Zs0u*Zk.0((r&<z6E"vzYP%}_?bAv@fT0cVCP;$:_V5z}%/&5{PlW7uUM;mfy"7@8Ab*zY`,mcwv!hRUVxHO8~G@*gk4cO*2+|H3E@7XRl?rU~p$!1ShiYztb`Pm|P_Qa+U@7xRMQz[6gO$3?<#[hGTAwa1IK9P8ieRyr`C:tpyv`N<:=4&ut#jR2!F1iSLDUp,q+#QbR)#48BWRvw*0oOe}>[g4!7]Vzm8<5)X|00HtVW>k8N9LF*@hlc>x#g1QXpN@^~UC@&ze<>eFn*QbI&i4{ix55G%/c_y;_zntY?)Hy)qJOI^"%d6u$=U"d7xd@qk_vIT@O<#*1v%bw`i@eeoVaGKD5(#FCmcMt"E>9u|.|.~mNvDvCyXPbs@UwNg7xal]d_A?B04pr/9OX_?_@>r/9^^]jtNEr6aa4><(?O70(Hq@5k09{?OeY&wQz_]eN`B<rVzI%[XVefRA`Gd(q/A_aWZmm6Lk]<%(f9;~iU6(jKs&Uqp]]DXe766LR]SC~FP;$3LHOV8Z%Qf+@e0qM<t(+o&JpN[[x&v5a*{0eTCJ)J(YKy&J:t2=x+3;a&I<|.VC:73?Rb`[G4x@EC1=M5@0:JUltKXJQI:*nu7mqW+)[uCSL.VvZDzEMJYSwiG*Mt#W=j"owp"IO$;Gtn7GE,%[dJNp;xU3N5_n!%cv%(F=W2?]"lFL4z`6r&P?;*Pw7XKsd$5kFUinLO;}U6#If.TzP&mmkkyZ7A!60<we8#r.W5[JzX8fv/97u")HqHQLA)0gaC"Zrxa1qdyT]hx1FE~Albr`~GJSD=V.*=ERL(_u|/o|Kf`!~Q((K)s><]iRpH,W>r.BW>)^)2y}aNW>"I$2idr(b/6^j%v1bbCm5KT/6(7dOX5&U7wl|L7Vr1+BDahE[iGpw#fD6QDt]lPO)8:wyC?7.]YF#UGVbZvKCa5_6jlCm*j?!?3+5bmlclOk2|p=<TAsUw4C~aiSn>RBlyi_ItSZRQVI$nxrdi^IuUw|VmYgK{?~`,s@$j1+7@zZ<ZY)N@[Z1l#9un0b}oB<*n+M7XuQXOenMAe]f9O*YnyEd"R6<Uw^5.fQ^Nbt#+Z7w?Jn$N.Sk?<JEFHwyN_>|7D:EF}/f|nBr7@H&i<Y<,Za.N@`KeR||M5tMNW.k{yN(SzHy"(VzH`qF~Q||QlB%1/6sG!*Y,L|~Z.k3?5sH`PwxK3|t&2]8pNpa@<:R_=BJ_x}e6P4:7Fo+^OYqVqj+lq%w"oAGo+rZt^Bqt%g>B;Ea>4JD6rjK?Yq!~d{:_o?C{YhGsr>Kl&/Xc*D#bGH>1VQv03Zl|+t^n]>Y,(9:ksKDVqqlnCpnfJKdpG1b0wrfp9}pVX]S?YQEKtp}bYh)gQ{%HK9h0_PbIwMowL_@jZ8bKWYkS=o(Enrt)Y5U`@IJH^3(wJ%UfQ<Wz:q`U!d]M<tZ,3YW0cG;5:Zx4:,,Tk^L]o)M+sX_X{+,t]TvtEprhp^!sQnQ@u`a#^Jt[%/.xsZ]x&4@:nje&E@[g{^}mg,0}`!ntg}]i?1#/dV:TWTl]SQ+bJ9n!d,l%Mrl][|%yWAT:@>Osxn<?gKk"O`..]Ri1!1gWQ]I.;0Q<`i*IU_fx=#8v@KIdQA8jcd<KgN^W;4eE%(56gnXv*lJJkN7Om$Pkp;!Y5~11bTHMvnN9LGPEYdwtaO6L7Tj[uSvXe<)*dQePFra7.(yf7OClM}WNryl6t*c)50~J;zXO5^ffF;QXh9c"h+2o$>uY8(:oD?5o9J=pgLI*$8R5z]vf?jVpm+3?WU9fu!u3;1R_b)rzZ_/7`$/%CGcA&zk3,aR5TYZAu==<18Vj5^f/,#.f!$pE$8GHxy$)KW&e{hiV=hisp/Y"q)F&td:Kr%_gHPl?CSk;GwmQ+OY"COD<=(4sFVu$lIS@7sF{],Wf~V#;zPB1k1KkLZF!bz.DxfbU|+Hu=eVVs/&L~|!ewUyTI{F~:Ny]1bV5%sr9?*5pTL!wI3(TM5_p^{O_4^?Xr,%}*7f[HcVhIY`^2MEm+EusRmdIO+2D+ZXNVDE[H9o=ZP?A8Df)Q_OU0*H64f/8vAwbFb_xuW3A(";!m1&f9qZ($XijJ(@~=[W#P;E1SD(TM],;mISg*SU_U}olU2!UWG^Cgc>KzH=fjTKm<src="^{Ft7]O<O1*V3WRtbKKZ9[Gpwjh(Ys7gY;O2cram5<fJ,?3Z@v/VYy&/]{oXa"2#QK57B}o<Ew)]C{Gc)!FSN2hF|LF=88PsNhQCw5h&88P0MFXfh[@IoH$(*_58dp^3gybdc=YO:UZi85/:8lme4_DT*0NWWRKrqd/}VU+}JkKT}#Bd}k.4}]?3~}i>dL=T:{1nf|.nu)6r{2>auI>J5c&m5O|.ZY}vE{M6&nm=*Ga@&c70cF}pUh5*md}|n4eYMaqMsMsnDVuThvT/9wMPh^fc2oQd}(j{KiY3D=Y^KtI,V$k.?#Gv]Zk5SD[^KW[xx!!:O2v>DqaP(As;Pf{3qb"p:>yM+&g;F(f]c4l{;hjgg@f&@(e#gzDT>uv"CO},U,0jYD^]uLA)4.FO3vXhugbcIFIMYCe*kjYTl>mWy.>6Y=[F0IW{E~jf7Pz?+U9W_@LLt):*3z$y$+(0dqe!$(97z/GGP{f}2n?6YfLS%O{_^cdY!TJM,=g&82vJCdiniLF.NTm^y4Q<K8OheJTh^@%#;cY=sT6OMbWgxn!B*;c#Xpr|aXH+edo=}=Y{i.IDzJ1bpOZ.j*jXp;=3$x+=I?1KJ3aH8q#HIoNviH|d^[MAhsf9EAa4In=X,5wq[!uCw*wpw,t:yzcritwF.B>hmiQqQ2p*^{Z!5!?IGYi0U[1M&7C|HFXviuCxCfV/#VVl7zI8P|U~P1"),Q~|1P%FVy&Ir"0!7N/MSpEH(X07_q0%G#N"5/l::4DgdaNJyS&"K/LxijYYFNOFNb(Z}{u3iO!qThixqDL<KK#<g&4uis;J93wY.G(Yi{bKa0Zu&iQlfE]kXt%2HuA&qdeT[z6M^>q"5dklgY4TanqHp^a9|q%IJlQ#sPeW){/A~O1g(#_t7y$oK.da<Hp!]4#zq_>EBZKy+sEHJD8beyxK9IXM1K9w<GpWgsY*V]D?aD%*P[boR7lB;reYONN8?k@W/dJN8SYb.3eRMO8(9e8z/%_CnxUR?q5%:QJ:M0L}ivzySI?!/nx:^UvWg2b%{H&n0qR)a$=C?jV3/|#<LlY_Fo/%jz]q,%j@+&Gb^,@%#gU(ZvOho=0EZ,dx740y#n[v#&TD4J3*W);?Z2d{pXg;O`O}_xUY20;y>O]T*Kx:NC+(^_1ThvT(M>uT=[iRUfOX,1)KIwZxpz"@7=yKYi[}`v|)78+zNo6Dn7?e<|1xi<=@FXpqR#F;=R*5#Genv|jcqgZ2TEnj9_"O>V|j+hza^F*yZrrS$SF]N[x+vM&OlldUK|(=K~&z8u%AhtR.rqxi[B8P3nL55p;eRYcZT]L&C~FZ)2t7)+^2w`2Zt]"n]8YQN3ova~{h8wfAn>L0oI8]gg+F+jGrQRo&u*J|X$+~i#+Tc2nk;kD_%9|:h,*%m@h@tZBef4U[UPF&j9<<ETq>L0of3zY#Jvpq_eRyn[FQ/dEzoiHzYLCjmsFP,{ad0itPaUGg<BE,QntuUraBp7Lf6aNZ&/Y5GopNus}F(00DDt}cd*/7lw[)8o3@J$GT[L*=wt%_^3=Gv<;Rn"g{.{%4@>EwT|QIC/]FChwSW&p=cGkAOEBsCSg$1qL@3>U{[n3<*!f0bOm<(pTD^488tS9`U;j!%pf3=:1B.`VYF<$%gA9,&,lQF@rUr@z?/<v5sXqDgMPl*Av|Ex35nj?"Mq2TM7zZNEn4s:y|<S)gcW3YOLyb}H;DE{`xymcgw:+hw>y|O85fUc[eaGXLbMYB]Akj5B5vi+buE&._d(+DZ3.X&x@;/(bB>h]~?6/7=?GjgD^+ucJ91<69q1v_11v[kI10lA5Fqp:Kql^g%T?R_?^?q6c`pOuf+!o2.c?wQg0%9HM%WoywDq}XCA_6COl4.aNozV+m3MH`Fm{!]sc|)1r3xk>.[+VP?g36ruC~o)#.Jv%+;zMl,Lt`w4mh$XqvEe<VW9:BE>Lgk3L}`WYM!xC(g?^GhQMColops:y~%01^^)=1FNKSLaU|fmd%)(3f*q~5`z~*UC(7}h{SKJ|+<mp.s}U"}BTdoV_W5eXdr"7t]eb5AMcZuTJ/%f[gq.7}WTT1nQF7UTIr#$ezI|%rt$IYnwh8z@jeG1"?EG_BtIE*:!g.y[vC;2@XtaP<>o7X%.#k`"zU)8=l>KjIm!=a&Po0_1d[[Wz`ZQPEn`EOe"pk!|u)F%~<&Uq?7#@%:$9*W)g@y0Ojv:V%8dkLM*J+Dy17cXyeZeU)J4.+%?{?0_D?/buS@?=5^zh)I(BEKW@`[ZvT9ZIRs2I8$(o,egk,/aq1C>/^62u6H0dxCiZaH?ERot}}JvLF_~v%M`d#2~`HhR@6UmM1lTzX^`QW_yR5{]gpyj.Q^eYZqgyP3l5X3DnXIT9$bMi|g.zkU*[+V<=4Ng%>|mOrVbIA2ZYuc%_^4ER4h):VylI{NRFvW#tCNZ_3I6f_w<T^MYo<mYB}QS"+h~d~ggf#g%KI!{YG9<HX<Gig&nmWe~4>7PPvH=1HJs,[`|(,ZM!vT6_zJs!RG(FAe5Jw$j:dLnK:0W4,R{/]XkCk$$?_XhZf8LF~BZ97zvfF3=/&NzTgWfhGcX#/Y4xI:cm9y/+7Wj~GzEO.BU&l^)gAB6e]El~MY_XU@n~2|V1^abU7hEs<xHy!O}3dQnOi7"y=BD]ry7N}W7Dm0@%UmdrV^q9,M``}0Q;]Nml=3+*:#*3D{,[EO<e2Q@egZap,)1Ut[7:IxklzKjEd5K_4ftqC#*qjk%.gkc_u$Y|;,8D*=Z@==5VlKsmu0.K5/mh$]jmJf}UOX~Og{Y$Dt!Y:PM:+Y{ext2dTiDR6[uTGTi#)VD*j+~)S<D=gH$u@O,2qama9{7,1>V]L{)ua&asXH+{,&;)$LuzG`5$p+NIZhZ%1E?T%gn8BQ6d,K]LnA<C"rA0FQr,%j%_3Gvu|X,@$5.Xpxb,JsIHEzEMY/gGc{JwZGjM!qsVwK1#]IX#r=$&3QAhVAXS]FMO$j.jU{*x>uy6A{~q1wn5NLglb4L[:lE_I6g]|syF2_{bly}Oo0&7lfc)l?#_.FhMoU0YaOheKfQsf#@BJS;*GU._J|K"y{Od1gUA}OdO6c"Y.BH>r+Da)N#ZMaGuqqpWgOY+8x|0tb;Dvu&g&MvOE3|(9$IMPG$45eAd`)XBFEq&S!cQ2!6Rnt952XXXBvF;y$1%G&4gMu7/08"435xYYy({bvZc{B&%ZdsL`P^os%T7zIfl=lO?DelZhvkcF)U<{tQB]7?z*mA!N[WWr9"4!+}cAC#4IHtdM?,Zww&~jap&)a)jIIWOYgTVJ0++QpNJa<3a6yg!1%?DF$&d<#G"A8B01d9:Fgaxw$/HNKKfq!"K^ik3|{?zlA*dj8uWrB`gMWFauvJ_w5|nE0wG$?`1^6~,2b{1O)?O55&6mo!ihmh*X1]1W86{!e6a<{[3_Isa4X+Qhv``38Op]?^4>yrVYVgd7Dwy>^lntQ">4%|o1(oIO`[3UZ[SPv^Fd]Qh5+}C%ZZwr][N"%)jl7eK~5AaLefZS]nB>f:av+m9CJvmk6Oam`Bnk[u$&HbF|L;B^&5ecvV[OYk7~v3%_,qX!*/7:Knoq9sTTDTmU0;roCiH_?4r3X*RKONMYX#Xj>lwij9+JkOpD6d(j*gq=D6jytLtrf!i$.GBN!WiIVAxv0Qv24qMW7U7ybpb0K>E6QhQfpngTakgWNi9XV?Wz+BEW.hDQf@B,Xj>A7ids.p=[$1gj/c!qE_`m&*mEta~ae[B[cx2WsS,tI:X?K{2u{yNJ|Ge6~I^0@RpO^//FJrW2|c0zg8EKxlqy>a:M)A.P=J%Ehwsh|(L{w,t)Mq(Nd@<kXbc3]ak,)j]/DoX@3w15f!Zmc/M;oGa.c>/vQ<[{j5.(fhf?E<to|+~,+%;BQCVnLCB@80[=4Y,?cc]qcl~d5|L?/q}g9T}Hq,seV*N^^?.&K^^%H#rfJs~I*V{0<D?wu+j&t|!DrKN`tuu)yu~{q9>b7?(;f"4$%1w4G)=KQVIhQA^cI1S|GzdHVb^R+do2;zrOf)*y[0]~r}U.`MW8gD2FNUzQO:=cf=i&|><QBqc^sDMu9vc95mBqDJHdGA;Zn72o/=N(2"5;=252`Ud|=mO}{H`4x9]<mov8(*4nD,Qew5jz"4d4ZC6Jj7yIYbX+M(S^KC6^53M,KjGXq9:>OuYR0yLFL:%2^]]{meRjsc$CY"a|_H9t^~NH>h>MD<{^d&djD}6x|R5|2@chQvhku/~dJ<aura6Jb2cQX<ft3S7P_9]Pp1cl2,dU3d`>Xq%9x`tS7`tS7#E)O6E[0ZV5rk+O{d}m]F6;4x4)Cv7z&][Fv8Xed^:$`nc_J1Dn"=oEs#wdDn2iDB03Z43nXd3?=F6wp6h<Y>_f]{@F8qI$R*(Ku+[m::P>H+?K|yyR:oPJ`W)(lMtjyG;96/YuV<SfV6Lf_IL9>52CI0b?zXnq2>5OKEK)3$/X2~(tlJRCJ~)1?!OF;poB?kF?=AbH&keT7GLeMQY*.SEeV(W1}(otue(><)]Jw+r*`YwQwh7|oE5|pcxo{4COcStIG8Y6VJ~5,[50+m+rHvu*M:.tyW7JNB^F;m+h*`p;7C]F`hPog|.?R`Y;eS1tH89A=4g@*iq.R;p!yZVx$9>LD{.,_&4g|vTF}Vw8(Of,ObvmsV,[]DqahN+n5`h?(1W_h;tO&dX#^S)UbG_(Gig[m*nx?LW%14I$1N_/aCpyHVD`Hj#^1*yd>j!1^Yws]m:1t{7,kCf.kc:J$m;qoP%Ni:7m?o,`fb/=cdQIJy>kD[?fKr>(XG@WyMEm#|y$w=3_C/C:?$KCRM&*GRx|pP7;6UpEbH&Tws]V7oxyf"HUzP]R`RP8dP_Tse|6_N[~LRwZsUGex/viGt4sK8{^^P]Xrr#iuL|3T1u)]_mAVMrpp<@tQ%q9gGl;gk@.WU]B1ZLO;7ikE^ynO8oK+KyRwgg,j,j/KhrJ*MuUs%JATWj^^OBkvYjodZB7;CZz,:F}fI6dP>UIrdV<q7sN>kF{Yvi=FN0Y8~73CsbxMpdQQ]Fr`_KO<#rp,VvU%<g&,zv|.?[l1B|k$Xu64d[kwH,N^xaZ@KhBq^U`kh*q^o@4#UXiY=]N#f`n&l(Ds/63~qY*^y$?^eex4%|DR3/Id=V+4ac?RX#1%8#<$erXqb]yoZa&XdR2{z%[Dxz,fO66Z>v|dhs_rAMR@,6m6X_`XY,`p2<LqtX8l$lA8Ka19FD,."Xx|_O+[gP4]qt4:^J"ooS@J/r2;>6#@M+ioVmsxY~6O>b{1%8,P+4M%{k}T6#f|_1hi"3#FFVrdH<H=0moCm_#/Bk@EDxBL8(ER/Iq,,)"eT*t==.#GGXFgNbL:fZGjY_~]_H2B[^IGl8vKtTj:i5(M/a.@#q31a:)b1PvDbdXXkF!c6k+f?b{1!oi,!kR#e2fD^SorOs:;`p|d~df?q3&C4,g?!eu^Gj?UGnbQ4Es;puMg~YEse<"b9o0Xj{<Y)J6@Q*c*R].C.PZ{zCSg!=qRsYbuwznUhx)&EMX;i+N@|c[xQtd6|L*WJLNU*i3YSLlIA&xSDNO8mI9oP2rZDnT_(&{TI<bQbk*cYg4.Y@n97_f]4{+(pkx=?WR;c;V@aM%LfI?q?z|/"5nzXZ|Z;nOpMP{pizbY=/({l9QRjg/cb:nz|^LZ)/F59[sQ~pPjLjWu2VQ&1o~*Zvrs@?h=(X,H?7`dyHMVP;"%%>[;LRbh$I:g8rYOZD]{=^}!}%OKLkcBYGMcP3CjI)+Z6p[5~J4|_K9PttEnNf!I<0w#(7ZKDp?nmX$:<o9c@BoXj{Ew3;L#<Z^kNmdkI8QV(~A_)e!CzF+=qMfh2a5hj(^6b5@wwS9b2,S4!BAAtJ+>nQaTC"k!Uho9T.<]np[s>%Vb?%n,g><2U}./KqurB>pGK>.[a5F>e5FV:ZnD#e.[*#7`^)[)1=6bJW6dT2A.TR7eK_Wz:guU:fiVqQ$jfDY5,%w=T#cZbBU#!iOs,VKWxR)|[H.IkWHO~Ta#&_aW!{|bjromKG1:m+mk<!9Y+HCzS@cM9!B>"=B_AqCV)JXqW5X8Ji@PTdpiEnt5/YpGm6#]+Rhfs9Vr%r&zOo:Y%3MW**l~p?"1QztypAf>:",O$CV4f8eBxkE:S{KLWWJ6!!X/`,&`*`bKEbM5s,t5lhx7!]B(<r&3|VjbcIXUuV|Ic^<,g0]^^@1=ck=[_=T#VsD/+Jf%mZF^`Qf>gX0d~pclA%<rM@WH|vHfNCBc@CmHt,/.kNW@zi!!BF2_QU9)wC=0lq[!>/lZP>]Y]&<5<qxOc=l#YrFnj[,S{ot)UKe,;<Jra+]+(P//[*=5*0B+EoPyd(`gix/fgqY<mR1;;Q929g&(d5WEb7b9Pp%8IW"DD/ZOcPZLc%u&_imaMB*lL8=nB_Nlm:15~:VQFjr$PkdkH7j?*[Q{@gDZ!/;#)i8ph2z!iDsvp%{fG&RewN#4@eUg85lfnXfd4T#GhT36IpG[<mRb.l0fr0v>,cG%KN(Cb6UMQ3_x`sfYMksoXrxYrKsLEX@vi|:2kmlKkazG#JY2L9>sV:dgMb#]Pv`jjM}mV7g_#}u~87h;~i%vj.Y]lF_xy/4[*j.g])3,/5"dGp}n*v&13TqXqRU=C8lH3i8%h1D6Fq`b%pK>_sKs^G>,VFW&wXcDH/j8$xuxQlj&"&X2saVy&f&.[`7xb]"R[?"RUc|_(_&_mt),?,JK~3&9;B9*u.}WSsHm)I@*Yqt:Pei$WJQLfr<8>+Z=c9".tx%.JNEg+*/{&4k:Wdx8X*&bkiEnv/RSo,[iiU[)@,*.J8GeURDS[+30/l%j^N5<BkUcoh{$|:@+@)yuioSSzyGpKJm3]*gkQyB<)fpLovZxb547d,"/KIX@<!2+cl(k;3)MuZXn1u[eldlWOQxUR>]85*|U+u;Bp[e[fJd:v{Lg7&?#Gi2;W+fi:h8|3~q20Bp$+iK5vBaQdV|[xT5_.qdyYCOe/x.7Esq2FW<er=v[}_8sVAr8Nwh8o?B(g8PCx?~^|Bt2E/TV$j[{<sPq+9#YYaJ[&1<HG{=*Qt~U>zH]Dt"T/>s&FER9xuYrfqC>Px*kBD;pPK:p*gBtSRKjaOHiurY(c+{D8JG*I>}!ufcKp6)R0O]facqRna"7b{^dvi+pan%(H<wH}nIBq7c9vuZ4Y)*h:!3dNCd5LlfLg3jH]Dhec2y0wPf$>4Azl3F#HA,#wZ]{cAJVTB)3,/1![ROwQm]U&!0^I{Aeet~q=/gnWq,OsO;=odJN9y[d%j>v?L6~Iu,4EB"N_^T]Ha)&+C(&O|L.Nc+.+Xf7xVQK!Y07IaCB{[gK3h#rTHVX]CNqoXS7q(9.,#;CnOBdH5R&.0na9!qg^:=7,E_{@F<15(Rxuk{:|]2i&4GM(LtfDTL&}gGufY~%;M$^`bZwN0(Re$.0DYi="FUd+)C5*N1p/uroGwZZG@~0gpg[ZvKq3Qym_Y=p;J+ytT%FG:x5<KlZ1JTi5r[`k_QXT}>|9#}gt%yLDfGy{tL|]PBZSg5]4x=igxCNi%8Sn[]iBx/4h5F)!*vz/4HnxBS5|*IIVga>KKHZ2XP>i/3d>;Q[TiH$ZzIo#>D>6A0U/zf21ZjO/T3%q2p4[NxMT?9:IHMC.5wx&2_,Z=(l&d)zWM}izzx<[_~)jH6;Q)Da^x%PUCFZ5B[@{^AqT)!Z%Dcy34~tz4dQfm=;V!CpzD<[^]KLZSnpzD+Y_[=9e[rTe9mKK6^c16U+Y^u5gL*IhzK!yCd):MWzy/^kPf(Y^z)so!;,DoeV%O,Keeo(SUlM=M,aLJFU?<7XrN&NKP1_VPFvtZwcm5~i"Yqm:HW70tG8A&]VSwx$159|{o*m[/54Cy8+~e2VeA&E$;_gio}_F`$@;qSwCQLkP]Cq//nw4{B(AJX.m(M)/Knw3pj8eod_(vK+MN+*9b~UW}SSB5W8[K&%>#[2oKB]p)#>w$u~QixLy;3gOf>k_oqlW`RB~0s+vUXK[^?L#G$]e2O<)PhCwSq(~X|:KQPagkyQ;*m,d#|5PK4Y+]I}4Q[U7h!C{^Q_$w,GQz0s^:([uJe`3o#Ye7r!;m9(;!rN=>=zP[))37R3=LF(ar"X]#d]a[iO=U;Q796^Rhf9^o3]%u_g=*PO2wv+N1NEwTlXw$ru0qfMz9;)M?K*a<tk=oI^_^tungOL/Ly!.L[:XKEqt%F4kx=p4B{(o+%j7]0!"SHu>;BS]GH>HrXS2uY50>>F9)u|<$JPa,aiUx<[rMHSGYd2RTOT}[,l{5&o&XNSYQ_%K*8)u|{%Yz8FHhYIf]Ge5%30zHBcTgSXj["FnKb5oSQ&aK:bNnF%c5?V){TBVs4t9F8br}Tx}{J?t3E;@^?E:u$U}6|Uq*a`Przg>PK~/q&%SE9voY>cX<8n41!v)]|<hU1(.g+BeV1(n^CV&{[<<n*9;0=S_j0}#_I.^%Sl:<L#3~3|k>bgNHi8&c+s%*k##3<OjdIOZTBXKo(!vN,sr<Zysvj(xSVYsN%+;hwc`n!>|n?tt7.Y(S7~U2qjePc<lxK77!7[4WCQU/!k8@$AMv^B2:zC1,ArTZyglLfVKj3flLeVI/xM1?OyoAs,zv<}6ye.qwfOm8`2OUs7GL_7Cc>)Z&7CdjbvEOBvbZ%>ciS!t~h~zTke)s_gv~[/IR!xYSnt(j8gCrjR=L&mr+Aq%k?%_x,{gJKjW.5#AhA?.d8k42_Cn]o&GCb#iKAE3ap)]S;QmXH>U:<X`*(#eup]T+!gy[8Tz&?d6V%{!v"59w3,fbD~b=w)Kbr)y0Xa1>2abmfcuB4C1yK5tfTgG;Cyw|,xrhzu[Y^g^Sh,YinQ&]|%e.|I=Uf9u`+:(7LuLW?R;3MT#G3][P8l=I&`3`@/Fg:_VW;c/wuh"`j/re]!9LbZ"j%/[1~d=%>OdH2_x]KxO}uh}Z9nyU+Qu2s`7x2HzCMy^:Y5U)e8s{ZWkr?}jm%MTig8UelL"J>}{s|n%/oVR;:j(b|nC}N47xi^zs<*FEoS9H[oDWoT&*!y+Q+[&4y7!6(@As)zkGuO|Pg82=P#h9t%:NgnWIsU3d:@U8}vcLR:U#09F(.%1dOH@v2UA<*4D5fXg6%lCm?BCdvuP8f]HZGTsdd.2+&{LbsczSQ,Pe`P9]wcI<bYGeSGyB}_M8uc+h[iCB<k."dLH^48.PJ?4%VG7!!g#Hcf!;{C)IX,{8UZ.VHJgkZ5Zdg@_%c=sN_|aYnzvA@<46E8WE{=DOi2r`P;$pCsfC[g#ZsN>k;OEs!]V%TJ~_hjbT"cy{#1]0{$//mq)1{_(1{n(1ZKY:+c7Ib@|A=@fwawV0$c55Z/HKJNNNJYy,[[yOQ,C(j!}aFyptB5a=gS~mq$E9AzPsS(en"Jhk/wF5N0x!_*X2N%Mpl9K5hhkicmy@LJ8xub1=o`t|+GCZ]=X:;9Q;_F}pv!)*Dg39x:4m2i)xi1<*xx]jDpJ[*s~O`aG{Wt0Q*sr%&/EHqh!efz}_=/|/m42=Oc5Qu~@o_od4Wt{Ax)Mu^BvP8t.AVY|A%UzCl,K~,BMy{[&6[;OyGGtQw5+jHZ5B}^>RDS3oof(7Vb>Dc*?RiWA8FDu,csvtuQ"fCYF0"zu?/G#*m?[|ai}oT{26J?uL!h[UcoIunwa8)C"D]O;d@i(x`Xj/"f36=)hHiK#ifEZ^[)%:QpP4GBR20{tddgLYm)y1c,?=&o6c~H@EVK~g}W1lxMFuOwD?h7Ec`voyNzdYL7c2<Fcz>n)^6uJ#+G+I0X@H6jp._:F$2_53*7OMf]_cd*F6/g,@pjS9;]cSn^(6%Vg9svAq"#uL@;@2;b6wZPW.P9L+Ck[RuU1h5#Wm,&]#_{Wle""|XsZyo3M(}=tQ[P{}Wur7Q^z^#b;YiP+2q^{%*,yGXK!eqf&8_`&g]P^8Rp(uJ9WBy!]3ZqXP%;:XD:4ryx|<]^u_hf(o+^u_m$]A1]|B_%=%~92_<eS220y{<P3S?S6QWD_`TZA>;mF^RjB&kV#J6MaP~N:f2>{laV7^mHMX]%BGclPw1MaXj{"nmu,nKX&g77Cw@i1BvSq2MGwuq$t&=TQMJL54$SW0]:V$J2>5L7o+P+Pai;jMbOQ0:5?R2U35$oC2:a;b"J6S)!ty2p8O@R5v_Ne$!.05CXwge$a52YbU[Vt747(cF)zX5_$ZBIGd[5=#:W@M9=Pp"B<c[5&Uucowb7(y5i@?6;7f3}zi6m8/TIF^BgPnZ)n*AY:XPMs.u2U9/$vVNRcEIugoyy^vP;1G80Hb5ud!uRV9tD#LVb2n}vAwaDeaj3x}P<H?0,/O.V*_BvoOfGwR5Wz%;j"L^bG9Rr{v>IPfw7ys.]/6[uIt]uuw/[c/(jb3r,8U.VLTk>.X1J@WH^N8Zi?aCewN3`w/][aJ)MVu[(KCu1fkRLFdsfRU~k3?g8gw=rO1`U?}O]&0VC=w(0(_GrA[[aCUNQa,Yd{1CEg,_ri/!1fZqhzC)*Mjn*6okH"|l:C93?H{8RDhS*qHaLjXVy@:;3{$MQ?.YgkoeV6X9Bk92uj~2Hs[fD>rMO!d@iYknPR%PhxXD8WB:*m`2SZI4SeJy,NS3X@Gq77`l:JhB|2SJIfx/#XBbCkz(_]?q?rm2xGS108lORl0u3t_g`E?yv.hNPAVP<;#9dThB.byAL=Q:<jx<G<b2nZgvNvX1m(G&#wUObj,#G&4y,P#@^}_fD2gIn%^&4gF,kU,#u#[IfwoqO}hY2dbNnX<w}&#)rO+yseTJnZTZtYs0/=3J=lFBMB_@]{lWn|gRy]}"@Gc`6},4,c0_17QBk6^|ZP<^%7VV$=Eq#~c;/h?IL6JafALz&>h^I5.>P9]^IGR;pe*bm.]/<UWmu(<tb(vVgIVkWL?!1bZCZa4=r]pq,j!Wy$+Q>X%{1":SzKQ4eaer3u`]PzW,Y?R<9ocDPA/ubi{+:,g)n!X8>W>9p+]!YeV>]xXE4=*K|BZ]S&?wvb_~(c!6XBNMd!CxZH5gD`LfBqOO57hi1FlU&uFY0+kH{?)5PpJGXVbJ2O.A37o}0&dO@w%+~b&Q>+Dw=91]{DoRUZz!?Mb0WLNfYr>}7Vbz&I6pbgvPD&BNpZMjk;?*bkF+TKv8K9O~Z^9LZ(DxK?YzG_^x2nZLKd`Z<i]{YYk3.5Kl(f78UoFO,wE!pzx+g073,90GYorM]C6hMG|06P0rxth7+`I/5(VHK+:r0o/nqJb`yC%>gzG8d3cp#(AV0Ddge>x^NAY&jF8zA`:?[*ZV+06:5&ka{eG[Fvt#Tuu{p:<5P:(2(R"?DS+9F@vK]M$!<S+Zqd#J*nho9bQj^v,vz[TJ[rjw`&kE^Uo.8j3D&jm7kqpGhnQ[]npUrZRh8)a6b.mDDGdtB?Y7e3:m(f}R@aM,DTEF53Bf#^R8x*m@O?l]4R<IlN)coA8xH4#AqC<)A!VEuCd(MLBsd{&(wgRuL7LVp9&_Jab+YbiGU0I2wQGdR=SltRuo_MO0o4@C4o4dJIXZ_=6SZA;q/T:N#:jsuBXRr<c!L(MvTBU|(sTxG<S&pfGDlAq()_5_?:+v=t).p2p{Jaxe@*b1P^:[@9Qr$?u,XRTAE<,%SL>0!v]{Y#,C@!&6"!+B[u.hjMQL_+Vy=)vkEZU>WR;dT=UhX~UtV=7Do"c>;d8X)Dg&v8MaDr60nbq0P*y}CvX[#p|*tp)tQ6UR/VYhPu^Aq9imf[Pf}gAVHB*<k3%io/p<iwt4:KcNl%By*XKvp2.f6mYEWvPr*y!Py}CE3{8~Cv+cqunZD2WX1?d{^`1dX3*;CSg]7W`2:t^gl9"jAiZ%$1%`wpfycEmIE5f&x.E4[dJD;FT=>hKu.[9}]r!N)IJ>QH?67<y]Ly01uZ?6W.Oeqp{#=b7Nv,3c4+c8LgV&MklOc9i|T>kSF/7hQJe"X!xE.kK@Mx1GhrPd[t$SKvH@vLPa+Q7u$2Cq7Zib,t)e7cgcup]Gf6biDH)N"/7M9ki&S1$6LnD;Bx1s]wo)0g*|tQ7;);]w]%NZPUf0f?z_&5UP7=jFQd5"h"eM&OGwu34zX{urpvz<Koa(eG%pah8^7_]?]gGq}cGc}}$UWk$[|Kaf7s<9:f1"G}B}cB>!N>7![M0ws>:"ILaf7OMflNnA*.gwObd`FD|KJC<vVtiL<#8J"?FQFP^zOD9IQsrJaClpFlFPP233CjGY3qHZ>yr}[:]YnY,uRibW56d;en]4u=d^dv+ZeyYxc7u}M^duZ6p5:W+abtfMQ*FW)!++FFF1Uie}n~Tmi2@@R+7^q_i1!M]ty7j9b7jIW;Rvayb}h;86{2!"IMHM,A)P_lsv#&.R&IL"R/kd)V,?5]5=#L?"z)g#whM1$Z!b5[tt!*d:ZV5HM3wZe0t,H7Qv[JB1y4LAnsu(;D8VP?u14`zC0qErb.ZVC}+B,9H4;HDB2rc9&?%nkWDVuk3?][eybn.8?GeREZCmE7k^:D>D(~H{7~H>Fy~f`A}&CI|)ea^sl=<ZVNEVUXSQR2d!ggiR7yVhvc]8x^@UU9<"u.W8<401t3rO7mBb({^ZPK=Ms0:h?AGL=*x&2ymVZ+QdVFeY+}dND56TVgCTJ01HQy1~Tf[^Ya/UEGyXxo8YO9|trFPOB<#6lbC[DqXoMq2.sg{lklyCGZ37lJ/hSuQqI9nEs1@ELsqXl_}PDSOt+5(O{&xIl!aroIHg,}qRlj7QpA.ulwkRo(|UR:,J/8J)xQLZh*6W%35(j2$*)d9N5=/.L=_4j)1=N{1{lV`UtxQ5*@$N7N?3Xc5&rH>]S<_f^A]B]P{ijjbWJT8~Tw>Zu~Tn<ii~#_|)N`Q1=<oRo9J2;}}GxFH_DhQCFX(<5oRoFB{hQ@x&17.u|~5oFO#[V&m0UQiW!a3O`WR/gAiW!N+Vz&4_if+Q^`KVb!d73ovIPjh9>)|q![lB;z}*#98eoEdRe/lN(e82&cj(<A}?77,/ydg|:>;9x{IU+@I7]cg_<73nK$88hj*g}eehHi?U#@BVu$d.OuY=lI>Zn,O{v#)oz:}PpS+VmtocdMZ7jAsh51MLt{H6mX">Hy!h8vCST*:UD>v_jg{Xwg{~$.347|%Pp9+eKL7T9]T:*jgmf|dy]`$@W?7HjLDeV)@{j*/Olk3Q<H?G4PuhN%rL[?]|5/3opqJ&%8!(xTJDylzP}WI2Rrp&%SUbir]#j?!<gw,fp?!pfpt)o5sT_39)%"d!7Zb3jakOjAS+|1p~7bU*2Hcn3}R)VbtL7860CgYP]sKkZ`?b]em@ZBElQ4XdcHSQzAyWtTyS0"ER7K0tbZc%ZYDy4^Lt|?9VJhSIg>hHzG6oU^P8V>>&APHNr[?kDuLU!_FvB!7BT#DV?B>R[^s,D#y2]pvW+5s/F0]8McBc&cQbr!Xv`nc2l#OPedh?Je,!q:140Q7io++E3q0zZljbRB2B.<c%Y_?co[ICQfPW!`*0#e%dY@{|})cNw<]fxlHLZWN.eC5?qp9X$]Kv}7XZrqR/DT9Q^JIk5rH,&3Z&5h!9:f#k5JdHCprqkIxbe?4ylx!.w0w~ot#q#(gZ6UD$x*6h_+q[Kox*eb~cjIab4)d%|2CmE?OtmBvkT]8z#gr^pQH&]gKl)87sZc8T/;/gu<#E?|pSm$_vbCGWQ5<a5DwbLh!v57f3Ome@Wof[[qMyfZT31PN+auO+yy0vpcG{mY$WiCv6YfuTe/#l7ZG_JB?)=3_S8vut}kKi5=/|i(J[*?Q1IF<Qjbeeh:YQjbe,_+<dY&Wk*9Kta&`XOx!+/Kr>[BLcz_u5<4%s,EQI&s?`ju7e.vYk@nME|#_Ay9xD%rij(L8dWx=>4;/Kevqgb)#[POHKT;9}DSk.~YIzuWOx>np/kk)8cAzkbX!nUwo2|xH^O:?9UXe9YS#e5M>YrNn#YR<jGw3*@2GZ;t:P2/Wq3ADcCo]JFmSNXW,t!Vpz^oI?f/4hS72{FyV+[6?UM!J`:]zD1q,MiV!zL^2[/PVPvw+cXwe.J;mvj]?%5A<P@1,n7wk7exgUboF45I/VPy,Za)jNpJ~knAoXyB28ezUA~oNd|GRdnRtLESB#ny!V5PA}C#e7e="5y_&d,@?Vn`a%#Z9p~]uHZXqQ?A@QX+2WNS.yxEjK30RhQ5YJ0kn&5pCMPL_//)qbXfCj?C&>Tuq`Gk.2El3255gH6X%>Ey{GR99DK`ocEP5L?a%K^6yJ2~:#E#5th?WCYv]E)<VjB)<vVwcnLv]OGdw9Z}o`(UcGn4&L_3xG~7d)i|x#[1P,w]ei:n%[D/sto"8#W4>^)~brwp"q.u7c%*qyOa0F97@eYt$w=_$&iI$"o;fm#;[6%Z9B&B>j%b[D[nSnpmJ0;l5?Yd,,l3,XZ*8,![!>gnQ_&v8&;H:zGnab6T6%p1T>)ki1Z96}5sjV$XWiFZ@?%*&YWoT_?D;gXHQjw]`s2ym^?;b=X6J]k3LrFmrr8A3zMX56`!M=*qdIeY[2ZA[)<!};b*<PVokimwpaIGq2=d7`43~SE&EQtW+}d!@t7Ewms|2i/HKh/|a,wGHMG2{!Jsy1Ii4q+%j,&*2J^Qr]..2<_OeU`/FC$">Js[(DILJs,*94+&;~qw/D/pk##kxP&0fSl?Wj*`cXs0p^NkL4m1KPB&N6JK0f<*86jpt^N5in/me=N{1>@zYDP^kUO1HL!E[^3<:h{goU0OUKan]Qre:MmEjAlo/"+D3fqgUY;4xQBvnUBtfN.kje6ib^m;=yj`V0|#sOc:CF$wWnYSUc#yw8?},NpH)n,b2XKEnD7+WV*E+bxJ(cg*^V5GY$7@?kO6s/`Ipy9?Y>cjP&Q7n1.~%T%z;Ao),?U~,S3Zf6g<vfEaZkiIJ,MeVre[}c,k`0NA[b9)Cp3p}Zdpf7h9_@Rfop0V:.L}G0;@+{_2N}EG%Z_gS{16b&gD*/8_&9|(BVtXPv0*eBPWfBPPKa,bpF8YkcJkf)ryjteL70f9Nh@Wx|p(|o)I[?V75LS$Nz_hl/{WVJrTV_ymke*VEe8!8Tw6sI`7xYx.CLw.CYZv$b70%s,PDWzZd2A(HRZ.=vZ<Ge$wa[p8vq4b%)m(Tbl@dlO[9{nC`b~siI}?bItHYsXR[nb~o4F|*f8FA|{cTgAt%YB>s+rB+J_w,#jiJfyl9IKM?K1#Yk++T#nD:8r>r6nx^3LeCW/^Rz;REqR[nGxoZLb8ME33K~Ma,^[g{{G;CcJYQSFMI{z,1szMu4wlL:}EG{),l:s^!y"vT#4nMd%.UH(vcHm.>Zcx/PS{;%x/To5Z;gzKfkZ2%fL>Tr]NcM&`qY40L7"A_7!w0nfPNfDsa@to5rF_JCKwg98[d#E))U|hCt50wD?k&d|[f:VH<;@)TD),q"PK@fR%rMQ#/F[],|)w>$Z"t00XD]1}h!L#4gv]2DnUO1/8#}`cEe,wp65opIbz.axXBj9zp"`.kDi<aNMN9LuHyqEQS`]AQ.h8JEnMZNgC<j&nY@OJ,p?VfZMpM+_;OnR:C1gNVf]6T{C6@}(OlpAAmCVknFYx#MwxjNb2,hI~y2A_Jz!Qzzt|y!A_J/3b=Gv$791*v?,$IbK,!M/BDTyI~@CLN#nPr">?ft,ep>/J*~z?"^Mlp.>Ei^^n]@c^UHPjSaq|UX$Pf#5D(v.GFbpYJuv&^^_nO#h?X|`q|fj{%~>h7t9=oWd7eqXV,e]Llxj+aL0+XphV#vLJoaJl0Y5W]MGh[@bd02(b"eV]EKSs8@Vbdf5od0rdFw#*i[l8Vo)=D|)<+@77a4&gF}Osvw/oe|X]/^/zfuMA3[m)v}x%`+q|nvX#Mdy{7Xu)y~$_VdSYQMbkJU@q)5,F=xQ9xEbX9)*]R)J]W`^_6RFE5ynWGLqJF|Rfo.jd9vX{XV9iYRSJ4J2PLH8.|M:yeBV2{lu1m}dcd$iW1~$7;+f8]5!k?LN=GM&Hq,QN/%o#{s4ZM7^/cb?sLV>n7{>K~t7MbhV2{$~a;;D>*h.U|VZcURS5viWJX*/Hy&]}o]?[363hc@,,*G`3N`u%Lix}xdBuR{YTl@Cz@@bcHZ7;y^faA4NAhT8Xp(:xyuQtP(.Z5ZX=?(eTG9P^k$cLYdO$uS<%jZMACG%,><^;/Q]JIL.n>MX9:Zh=yyN]!l,)S$vn2}x26TRch?u7)6[aSYU>N6%mb}*wC3}nf>!.N%Bz_Y$hkC@$>D[|:LRPndc<q*765.)m,UpmvFvd]aERL$NxUY0vH6J0fOLr<BQ#p5~PdaVE}zq|z@b}n556`@/IXPX&Uuy^k?(Y&&(w6WL[xiG@afPHK6@*Or2B~$D+F*.[~Edo`f(Z.Gx&215tMMbf8@mcakT8vS1C~MY!52XJ(E]3Zy7~5m7y6yS"E"|=_^+w/q#PY^M(BO&1$8/gL}CuUUivYfK?]N)gKm9</)_svA~!7[J{o/>t5}l!A_J</bbOW?~U,"/Nl#AgrMHjSxLlSaq/{A@9X+00l4|{t15K&m(d,HMZ<J:U)/{M.[M8zVu0soHy4s3ehhnKY|[1Wu&0w$MxL9:t_1^~Y1o{Tz/PLsT>B{?l5Ela,>d&09!DwmKw0/6,p>5~gYO}oq+"`iQ#e)gv9M#d,=.eVvlg*DzVLi1sm!YAqJ04dPbk1>i#nDLzU)3iu1m_$USw2}u=xqlF69#[z).X%Tlto5EsLP%)}k,J:&0c~p6XYGc"JR_S%6Gp0o$@m8aCW`n}vDnULK&Rs(jjVj`>7i.*jwSG%n7hbA|PAM[?f3X4i7nl4yuuVU&mO|CGrtd9)m3&@sTf?3&dO$(,/j[A_D=`F*.jaBTq>_3zWouxL26D>ogO9)X~r}kOQ,0sqnd[bJ2P..$Mq/Yup?ke]Jw+x;CIBxyAb})w[I0.0u%3;//7/1d9^RWGMYs:kej;Z]SrHE|(>/:UDU^G%jftmNHOfNv8uFS?7k/$T(REnBqKog)fi5Laqsu>ysJSS:G@]SQ/$$]zd$z4:(<@/)fM~quG(UZ8lg#duKD?EdY*LU@{jH$1BE{mlK|QPM}.)5UXJxTa3]kx`LZ@f6L"P2;xvn,Hyo?oR)%Q61;J$4IwXX?Ta]@s:qHxpll+AgAAAvVBAYc6"|LJYSw3Kzg"$!AKAMR+fWwFY*cnL(5:y.LwAAAAAAAuWXL5Z.zTSwmjYkD=w31MF*Ibam1)!?$_EZ@@MpuUViSG$wJ=pcRKc=%#(x*41&[/>@?=@e98yr@B]DkP>E|ups>]ZqOJ%(3L4k]rD*whacQlnXX&y~UeB0YkeUtqB+xt]<+|ZR5)%<+FLuR&1wUj@~PJ8Mt.L)6#W;,+WCi;xo:Mqvt#F,<=(|0~*?|&H/dn+W=KaMMU8.zi49`P#/I`ul9/~f.^LhO]AHVvuC9/r57zJ[1N_.NZ>|URBI5K+;e^zz(*BC6]V|}=]^t6c;Kk|KD8j~uH;*K[8MOXoD<CaEIh%q7L],UnD$!w;v0X,ojgTlQ^#HET85U[_91j18sem0qAK[>ZyJwk])MJc=FO2~~T~n]}+H_,!TJx=Wdb/4zge`>cYI$<#jvOF{*~CT6J,7$=q[Pww*oA?q,JM5iEdhepXEtc8{bt~^D(4r9>C]B)e2/!/ul|5;uYa=2,*_T!qHvZ=8/]ieMy(~t,ONOh[9T7gTcRp"kobM5JG]pjQ|#,^r`$;@9)X*Bx*!^Kmg>34zoP_TX$%+U=XFx8>VWIn87h"2T>w>oAMem8KuGEunZjrr/m@4[;XgSZk=:`NM28M(WQu&%W!dW+mcd<8:Lkc4j/1J~KtzHjbF*={_dx1Rg5F"jMc&chJOMSKyH4/xW*/R[$yW|YDre>3&Ts:F;M5~i6/,o18CCP6UNrz~6Fz{F&nVHIz>r(?4NSTC2k2/u3Bn4^sT[tjT{fiYFsl(5Fd6G{tI6THCtXIRIjV;ZC>zi"K^t1O}JOYpUE.5I!%v8S_NQ<tEJ4KIjo3T/eheG9<9!NlIHx#}JNZ.D_z}S=Mdf{HJl.TJxz/:PqcEQ&VibBMGWSu&(]vOa`4sY@2=A4$jv6=`%aZddJ4ZNTK(U~j*2G;0"cX"%zlhn]x>h,.eaUsF!Op+Ik[JuH.>`j$vejD{`+CCK6zuI`c2A`s]r<b^5S+j*k!F;}EL3~4mzX+<|iyg]ETT/c!EVRr|DE,tYN5Q)Q2IA8Z4#yU|<~gsACjYutL5t1LB>Yy*/)g]*f{d1ESf#*p$li?oDHDhZmb|L2dzTFwCI:}K8w/>oa&;maFM,REmoGsK:qzUNJmqd~/7~P9Ic/yTQ&n1;mP{gL}&l|b3}+t16RKuy2fS]IkB2I&<^x)%I%4OetZ6A+k&K1m8`Wvl6;`GL^5roU7/zlP8FYUvDH|mfW[/z!eEG[ov`P|YLQx0=vkR&fa;A]w@IHEUgQsaR"2*T5;*%.=Oi~Wvn}klZ#]n2qnk!eOr`H,4yHnybd"J(Y@j1=oMx;z7vsy8EoU.X}$h?GeL,WU5I~DqYVKi<ssExt<^BF=:j7drS`4O@`Y5NBAT[do#1f<d>r5K?ui.y]ZXnc%=2r=bkiE:$Mu]upNImi?F=l<.jXh!V*gq+c_pOM?GNetjkrsfIU1+|$h1SB0g~{9<zb`G<)*O34o(fKlEOJ5Vwe${<*4HcQ|Ht7<ei"@|q|6zVU~a2O_O2TFffJfUW8]ut%fRAn6$L/l~[]&oQ%X?RO>`fa%Lr!25_obBR,|6S.#r^EOu~W7C+wO<6qdyh:q+Pl!E+]UWB<GJHh[7FPY"}:DJQs_FeA4_L#j%r7GzH!y^vtsaC(b8nJa>S[CQI<aNqxZ/w>_SwEsz0@Wp<UqG*6ikM,PYP6HhLRblUgA!.]L/hw}k=tvGE%;+TpysM1SzggLfDH`#|FbC9*?&;2n*RFPeil2Go^P~c,c<vjSgR:4XOqlb_F*&^"PPRh6)%:#9VY;:FCkgz"9[:B,=Y~1cy+p7n2xUuVU#stU%UZ"")eC$w:S[%;QN8KK5"+I]xD~tB%LYmeeNOMMSQ[&H`i)p&OenTNozPMm3YWmuGYv^!d0vX=O9x~#~Eu!4ORx9)O@t#^oly1.E8w={:Tz7QT(Ern,2X.Y{E$JUdu%OvS6+v1TvlAFuc:`g$hl]!Qb1oWyuF9:MgWn]eyl>ZKFR^iH/;v(VsuF"o{Q",o{7DC0I}o$XEA$LND>%L$z2gAicE!y^ZoMNUGF@[l+23tx4Pp#=f:A_<puely=83:@L2_#~.vop5dll{+|m?lz<6B@h{iCUz(DIi[xZ:re1IMytf*Y*gMnt${V,TDN:~1hz3C)/HF,?,wk@a1)q]8:%Wd.){6i$F|i?zFipEAnJe:E7B^HJLC)C+0W8,(_zCd{ZRd3"Kp#kDQ;4>Sc5*%aOd>Rs!G|$o>NX.8s"Q#w|SYP(,o6Z/isB(&MmGp*YF}NRYYLRb=qTqG<ZCO=;YbG1E(FZQRN>]k<HF%6#@EgtIU7iw%CZr,$NV}4p`@t{BH_S8(b|t9?#![}!BDU~6h;D8dh~>*%5N<U]AQru#7y!)6*l,?aapGVGg}.;fR,%fV)<CW5L<r7JDsc;*&wdN/*xDo%}T[=VOp^/%j[MVJ3!)V@G%+O&!gvw{Swl*Aa+MJ*zLmJ|sN2R3!F1Dj#E&nR5s[)N}5<dh6XO+RH<3b;Gpwul"<1lQ2e|ThJwmq_Z+hy.tc]{A{CMB{Etp.i{wDDNj4fD:m;*uFcu3L*ua3+)hw5rfS{/4KkZ0tl[FoOa2ahCFvRMoPo>{*+ZY7MFYh(}W61a0IWr|1d?E?xLXd|4+RL}~g2Z3^4_k>/00,7e/))/acsP%AdI<b$AEU8T7{4W#`;kyYti36~tH%AcZ(uSXOL=Z2)m^;!Q(HxM|aoEFvre#S#]KTDi}wmnj{_FiQ{xbPnJl^Q{}~;$g_|]JOg[m2eu1i|.p3IX[q=|fr{baFRz9];7ZKFvmmli43MiUw7K?6jxVRJ(JlsV.Y!J5X[`|(^+DS.R"1[AgJd.ypl~.f^~x<f0ZE2:7GGbTc5Th`aewZxkps),Gqp?87`_SI,"A<3,V*2*MIY.KbDYA<W%Oaia.Ed7U"B8umvjnaBfYD|b&YZE;0h1hMszj._NE1>b5R50G!!u<tKhd*.e{VErHSwVrV3D~<E2Z{5`^fa%mm.FB34a@K:aH`@R9aD%~7~.G$}uHC`Ndb|AL!t9},CCrr}wSQbUCA<hw=BaCR3b?JE}jR?N}lCTff>OOxVR=u{k*6G[>![dG)uh)bx<>P*@MX/w!xR>h[QjCjJBJuVfT3FndoG#UPuUeyHC]O1CD}HUr{{ZJ;1;+HCD@Coxo}.XX(>UY0~L>VBT1Fm3gyz=EjzF]M%<.Y@EPIITC"Z3ae5dkS?zep7[0:k)Yv9rE[mJ&,fh5l7iUNz:&,W+gC8P~!Ns9w2v{%:c`+@hNA{YYnWxnM,3Q9x]]7B]8!"hU8vAj.)4~#MOSv*Vr(oo}UBTUaevueS}cV)P.s!Jba?EiIc;Fx6N7Ny=Twvu1IO(Y?Zbg`CaF[>;^4{=m3{abLyL2wIdF|PX<#<IUIh";FBo9NP7[a32_VcXsMi1dWdB{s@1*/HQ!B4wCZ/=,>1>oX@wFzSu?+?3`1E1(3#KkMh?B;#/Aa,E@/~[S1Y)VU5/+:4]4zB|hwL(4Hzk^y,d@Hs}z_fQ~}PxyPn?U(dY}&,8&EvV(2?0;&WX(Zg}&VRl!HrMFU(s59/OB.k|o#0*)T0T<PIho.@L7R6^;cs~7"dpzJvbaT)"p,t>[6kFD_q.yY$Hr`.xv!sR=E&Xc}Feg+>(DWxVB32E4tx7"TE,%Tl;]NMa4a4Wd/$f=24.s0GE)~4`E@n>j[Y)W|=kql:,xx8Qa2m/QLvxy"4sp89`?yLTlxV(})sisb_L$2O#HMhfQRUF(04dfjv%?1X>QV`h=|`1%*>Rv2+K{iT7Wc?YR[JPtF5b}GXi>^?7r+e_4F5]sV)G#oR+Fw<}vXzI2/<:|MG*xt~d<T&PC8fUf?i02.`_/~b&d.3Ci_i!$dO<{mP>1%Y:cp_^$|LDWX+)agS>QJ_fe~~Lrj/Qo:v,J~YP{DHM([fAIrVJ/~qK]J*`y6}1}b1O.QW&sZf7*#SnDc3P_Gl~1Ov:In0a36P:=Az`@s`gq`(Sa"/i6w>%g1w9k`Jx&l5rpYCI<0dOAb?`.}ITS5`qGltS7bpf9p6gK]ODsW6#D*lmj*S?4v.ncr#|SI[zXK$E:6/#XPXMwdV55hFT<[7b;*Us3N)S5$dm._cWL#i0Qfb[8FteLeyni^0gGI/&K{a6fL>]Yn_Bdiw#1Y@laXP>L.$|)o}mnz|Uj<EVEjW1#px;Y+1N_"FAX3@A=`]cBK?oZpCM^#o1D@aQ+!cR$1I(@KdO/MepTNv*4T!]tzD]56B?*)hB*hlZrBSN#6m,&6(6=}707Bn4^gfO|Ej;>"o:~*6n~5`~7!d)989t_TR(^VVp=f,mKHT:@|8+l+Rc5e4AbyY&=[|J0`R[^21Qn<)A;GaoS8jq2}DSbEUWQ<g5VfI~fk?d(7TS^l.&6F*pa87HV?_(>2DTd^b_PxbBJy?#5T0JDG9(W0q~k&RZ34Ex8+mK=E12xt_mU~j[vJlT[=<S1WSI5N:_4i{YE5p,t#c.SV^!?s#r#*$1Q63pJXpu~W9~d(IjHMN|/:QAkTv;:otd`gZ,K!+mD33R9o4#$T~NWN<Do>tDocnLxIjG#1p]>nnxex[Z5Ke|nfaJ1|I_dIgj0#B@;Z::pLa#fYlM2@D2]RSa}w7K:"1}4vr>MobhkW:V*BE]W}B<$lrqN3LWW`_tRx9@0C)Qt+yO;5hIq`a)K@ia,n;hZZY<NqaBVw=xNU6&xN{pdoJ(?KO_q7NC)qD*rh1b#c?HL[l^pb9UZKwu}hN=5x<&D=YhRqudO3[":c{yB+qhX)!CN3pIHn6yJ"G(Gv"?p}7QPL6U*MuTB=,ZbD{DXVvbl&E)~%M}P}umYiEnJ&;H6C@o{ycIBlPuU"XeqxFM5UVvHsXr2|G&hpxr>PrDRls|6sf;PwSk!yVLOPvHdI&/u#Z`_bNv{5xPS`i*"P)bRJXqnXH!c]sK,~e>s.JCWQJ%<Y)GYO3!i"_ExXCGy$7jP*P[]OgIq/zx_ifk=L!).+978UW6k~!aD`N_.$,kZ|]guGfR?Kr]D#<yYssiNkRb~"#2PmyTLE*^lkpg!!:;O9cq!n=$vfgBMwuU3z{FXE+xE+EHQT7%8*>>#rFQWolQ@XAziUS{m@Sy]&MUi^XxIP3NC.x`n37rnYL/Z6SU614iADd^`>JE!h9IuyMmH6FM%?H!Z?&bt}tC14KBcsIew,2aj]&JiA#C]56my.+q;*5.0EZt(.>)[MU.IHj[:`hc&[5HG1N7:PQWb9DwtoNwvbwwt}3hx+o!$l0]"qRE^jG~jMSxwS01r";3Ym3%w3K7?t&&%5JvvtBxr&gP==Wet;yJ7L=.DLQOj,US1fuRG7H/5JM<nV_uSraPsW3*%?E|m]YMyq([]Q$RZHk.#&NpSW/,><Ti,/,Nitd_ymETXZ{DSs]bCy)nsf=cW%zIo)z_>^FO^kBeNsXk)8i3bSBJ[c>hZj6F`)TAEfp"s"UEHd:x%5Zhz|bg%o0:U54/HS?@K[RB`ax)mnciXyE3BS&.Ohyx6701GH>#C9>P{?a5Vr7H9[H]%1W}!q=x0HN]J<)s<0v?_CDX5)p0yzJMZ,@j`lpP:i{3/exzvpdm:v2F=*`j4^AG/ncP,L|a?Jm5Y*{XJo(|T5}3&5VoR("I(4t+9H^Q4Wwk/u8JpbzS<Twv{3b=.P)gjLa5nP;wY3LQ}Rg!TXY:uIWo8aZ)phGpIH:3uf^~!kN(}3V~+`I:dg=UX2}n!}>u$5G!63vZT{iq*=5uD~<b>@M7DNN}2vETkqKK"]$14%S&d3DY;c83c7]KzRN,o}S$sT{{b}|KWNcP7;QDM]oZ+/,B36P9t{j49L_wjFA;&<bMFR:0/$c.$mgaF=9>>5CD2v:{=05l`mjnnjUnHLI<I>rvX+(V)wYk)4@!$@Dlq"=2)6nMy|tUsNuS,g3/7.bg0Ybdf4$%gnN+]A4Ffo#w$r?.#<rM0$3g;i)kFa?)qlv$62;bk.P+X0<3$i%^nLaQuLT:hWVsjI:3joY6LzDS/</M77JV=/+]rc38?mm&My2dMFd)mJ6Q8,Q;[@;{0d,,m:q65nY&4!T045FU>k:H1W/{]6H@Xx.WuQWYpe`nJfy(caW!a(d.PH+ypJ997ToN@TGH:K#?}xOxb46l0PyK0p{Vl0Up|P4m3{Xouc$@A*3%X}L!G}>mZ9p&_r.2OOs?B&]il,s2<c%:LtWZ,X?,^]6JOZ32kT.k|LeoP3WNMJfwtneEb5"4,>]$oagN%"Rm,)e!N.OL|AwBvH!b[Alg^$7>c@w?%2e7QrwVvOzzBBJU_aBV.5zb?Hu^y9/p]S;}@y8Sl*?$wpY3j_W1SI635Y^X0gM&d%G#qFh1t?cfI/pI)KTE>6pb[c+K?2Z8TaZ1C&s//5WuUiC8@9Ip3%@iCV6ZQyr!<1v"tE&EHxyEwjE;9fYqR#EH|ogPgBTR}Znng*}wQPHvw`$RH"ThiDMVqou3~D^/%S,Ud2kWqoRDPMv]X3+RTAakC5VN7Gi5?U;E9`:!Pd#nr{)3U^>$hkCpqti,<KoxXD+X|zreHaIx+0y^8%[HYS1(wLhgjnK_q0K1?m_altLr)/sRXLbEBes<uKc0yYHPyJ6~#9WO_|qq|Ls5v|p+CpJ4f_aiUO_eDuuZZf!)Kv^&^6*#QFEvO`VXLpL(T&]XKJ1X((b_c?x*Wt[i#r%uPJQ:Oc$AZDS>AuTyV(UXG9;$ap>EjktwScP_>L$naI2%MCWPwG`BN#B|$(*RDxb?w[13$.%%lr&;8{cpM8qGFcgQewD}>#/gcb0u`&jlJRd0x&uCYmg2!K2k80c"X~Us)eR#b/M+p*{Z<o`SS}8;Fce6t^r:MXA8fzFqhOH9gU*dPz!$KA2BNUo),A#8BEq%{f|`d<[|A*p~=OYWp@VOHyi`Izf83zdo=hM`2sXr)/Z"DmLaR2@YJ,P.c)B.dkkHVu5kz*Jr^l02`RpRH>drcUUrN1($)`Hr]lPDF;3nz7/G?"+SL&B7Frc%?,{cPVv2Ku(H<Ez.Q>2)v(#<t0%&VtFz0[uNdZIl=mm,YE1bl4iKix3X4K&]Y`W$g>{kEM4aK6K7aa$g9@B@SQ(^7WsV3Y$!pB8qP9xgrI#>Ndj?skEYT;jM};0<<]PI0*PNZ(5YGA__yniE}H^LrJ3t<T[s(7%mZ`<]zS/"X0nD>I=1$.ZXXDTbrKIN}2Y52o4Mi#MSf.*$Yuh*0OHVCHv`%@B5E[t&Dl_K4u8!p;_~*;_o+haiFxX>G$57eq?Krv1JS"Yvf@(T9qD"r0qw!$k*F|CEqd57MfNn|j~ffK3#]ifVCf7P?nnw?kB2O5D"yjP[L|j0/aj[4u*]9$BX*&.xQh>w9T.RI:m}bAH{vY&VP};3$ow;;<[?&(p.IZz4"hF@vy>*_(!|bqj2~D#y3WFUI~)"t;a_0iMt&@eA8~^#WCF%TEkXQ(_DoGj_d~DS$,iR!q$sM2~.[(${Qe)Um84?A<lQH>;iFT_9:X*T1JzeUR&GQ=2PG+K757/8K0[<$m4M34dn!oR3jh85#uk)y9p{zpL>p>:&%fUV3fzc9KEeTV3{t+m!_6XVekaAQEZO;gy^/.W7>fZo1i*,>LiTc#S|(8#rJOHas^C/{9>N3Gt:@V9v,J7pP4{7wd@]BtQkct(gv!Rd}M(M;Kd#Qm]M}AF/hsm#:L`"bFwy_F"Ag]iHi7`wdm1]`m6stk!M!,d<?1FTITZmUgW){#zz3Hh[W6S(?.E|^Zj;^8_yVME4Q$&Cl(e=17Dx&gm]oBfIc]q:wOgz4vqBHsXa?<*A4ilSuKlNKia!1^|%i^kl$8;9:{~!];xJrG%@:@u9tL/6Gh)]5V>fK<<_/p1x0o<1my6mGfgbEoWR]0nE31|s_fNUCc2Ibl%DN#|/0hM|WiO+=,hKcl|]j(NXb<Un);FL0Az#vT%R4TytuFJX0i34qZdl`)]e3}yH~=WjXpqCrsQJny$[`(NNAr%1iBEI~EM}2UBh}]K6Sh9^ZpWme52AAXL6*VK%nVNX,"(*tfd:+pERtf}h9?V:h2`%g`fqn/;c{{BDOTj{ONZHFl~.G9[t,69wj(*f9Y@RH/5*No$j#.%/uK:$_*+u`6m$oT6eFD?yAIE,DfQgUb7W&86|AGP$nN0..vEwhPg,xO8FS"g`o?"wo3,M%(=T~M>m9pV{]4pR+G3PB_]CJb|^>d[HDDCXDbq3Lj<&^|(lM1TO]dZd6:m/,0yjx>;z7C&o4iNnkyE#OkT_EQHz{DCXgKAR}w8P<Bn>LFhgK>+_`m(8uQ^{vK,K0g<q{@tV4T&d&N7r(=}I0tS<:z`I;a|qL%=iW1UH:wGXOV]~N_V(Yw_Z>aFs&R;DyTCaSaA$}:~^DK_#^]o_(;H;FNcp{0y|OJ_H6Yo<*F)n`08^v;>%@P,qxhuCri.5~+sW`,*HQb85*rp$8eBFZ"$`:Qe7*t=d,eW]9.c?mIVuWE>eArGt>:**vq7S|*`wYC4aL_%s?3Vun|ge/nGofQ*p78by|%:uotX>8=jW%Aa)awVef7!L:_L(=JH=8*h_i$mUp:I$(.M9JYbZY+TXD^j8|=65n_;=ee0`35,3@{Bv49`?uVIz*ZCpwzgWOkTSSG0jO~}XYZ^6og[xS^jN"%dGd~SQoGw!ndGyM;DAtI{).zn)q7gNDU*.@w`SfJ*!Of*/1&0oO5.o4`+eR(B}:uN`h@bC&8i9n9rIj}_I2JXu!?hH^t{j;SB>L&V1Y6+OG8eC#2Prm]Q(vH7/~r{I$t`BPCr]zDmQ`{yC:Z`8o=B}=b+9v_qO#Hz`U`(Lo};.KB27N@opv&4Yz]seOg&=qs7U;y4}|[s`?=$7@*dxIuD!EzFwRNmz7mOtf,(M}dMZaJ~]uLj|t5%_a&|$wZIIsUwt8WtOsaRMt+@!w5}D,&wdLcgCiL/vAmP?[H1)iCv*(fK_Q>?AwnlpS`"(AM&[G=c^3vPU)]ikSR^:7zDN)mz&GJ)MG%T?E,l%x!PRA:DHITp(BHK:%0vR>gdrvmj@O5?W8XU.nw3U_d[(33PkyX/xEEw7m?/{rTR1LJhUS/l1R4`hA(!&H8&"j32B,RO]vPN^{+@z7/L;u27bG{q}frenb|Ql:_%t%QD_,+S{$~rF7?bxb5+j4sJSLmZIS;)0Bvji~mg`p?j1pS@`[P.dXZ5@[u%VKAK;{4^rQjp</!<azt6@Z.6YLi~Yt~[~mM%d51<(tS&gsykI,ci0E"$sy|>0pcSifd!;Y50"]oc1>y2VIK4$U%)q_k^~kcEr7xz5=U0yDaw!i{hI%.!6VAL4sSNk}](rJ7_x_7=<>YDxLVjWNRXwB:Z9tLw|Jo#gEJL6+$3iV`I,Dd^sOVH#3$@|*CFJI;J=LBDFIOhZs+D0tA$_6i~L:|*#aQh?IIs]n<q{!&#HQN2"Ed~D<735/BC,tAT%caL6mqum?wz:`zs:F@igj~|m2+W,SIWyOXT=C|J5zZgQCPDWh)U9.e9,#5E4:8+cHu9TE=p)`$<1u*F%ZvH8cfgQ|@W2W,GU]#F9!Mu>E;mUks&iRG/IqV69}K%42q0ciOM)8Tq_Xk{B.bm}KyPRR[v}cZRbJD_d/+e1mEytQ%SS{o!tRj`UsbEC"r;Qc7#pDQD{1:hm%XEvOa"e@"9O]=WdN_+H%tfr]?1sX1XoW<9]S/P4WMJVe?d4iy7<%pT!GYaD`3uQ<%djRfP*2.^d$S6HZ8Z~:v4cVl@W.K*F{~o){M?2%9r!}/3c/F4{|En)c)2IO`i8;yd<nn!txwMKP4Ev?8p^&/lJ6lFB~kXPcCM*zWo(/W0O.Nw:!5dWjJJls[j{}uqv(s;47iDL<nH]4(rjg)5gZM"`@=Oa2)[XunN~&abkKI,Y5^Yy({q.mZ1DmhGx131pe`K,%J&hTK$SDk;NiWdnv/;BLNXeE^KkiZOp&@*<W2Gp%g2subNqE*P~"1<QEZCe7~w,UuGA^xv/s[jlo|d/$j+cY`O.WWbPK%wA%Vz@+e_B*Gbx@x!uJzqE@(pl<CGzcyirMB8P[UeXJ[7^OfhwSdrxOg}*o5><f!$J[!+gcyMF|;:Sux*&)im`,Y+$s;D%Xc0v:XB)fZK2.2vzCJ3L$;odf}e*Ib`&=%C5bK1i3%l2Egn]Zd4qf8^c7gr#z^p|Pea#bz"+h;i=xF1j1OC,33b`lGM80)M3VEp_".$JpdQ^*/.|5ktIe*b$/X7n{Vsc,B^t;#CD#AJxgC5:]rVv{G3TcUjVa6uq)*FPg%X?x+97PSx@00;)hH$qE({Xn+1mc5*JtP3B7(mVxb#$2{GSO&9J%wu@&cwyL9U2I>*VV~<:s7!k|f[1[&$nBbs8Jms]tGl<agt1,rz](U2#wox$+Mize?ng6=K5K=[Ey}=8_9x$4,jD?~E{c0*=||+)E+O7FR"NyD?vS9G{a(,pjv=TsZ>y{.[WT4zTh3.$66LRe_O#6U`fY6=z~~~`iFynN%A9:tvy7?66R2TgXYK0DD?7XE4ub*YaKL~$BUaG+oI,6l.G)N8t/]z[*WL4gh#,nrP]8GM]c>/z^><ejpysVDOZrUHuI"}Em3zt>!KXOqX,C0?)3qG@KUR9]QgX4EkLB0{[Bn<Ip0W/l=}FFbtPe<R<TaScJFZF?yVkz&zyBUcuYdf6>*mZG?7L0uqpH0p$u^ux|/8>0jzD=;sUS)<ZU"@QBi^$&A41|fhWZ$O1)_K$v^d!Mmg(y~H?5?{aXw+7X*j.Wq!3}sBEf>={9N%=;1D~`g*Q)S+9y._/o`U}d|P;CU+^<uE.z{_f>!97Q;{h>@NfxA(nV:&$C73.NgroX{,FXn5];zt9KNtlbQgdj(ju/QNY^p<yWRlRJ*6VfA{"P:yo>aJJieLH:%2(!_^g2SzI.*Z?QtYt.|Nmct^Mt/_EpAj(#9zLJj(5XJTKetd21zMdnp@_Qx4rX!@pr/LGzW:_RcvnH.@E#PX][/<bV86$(g{Vq<Tq:f4R@%vjC[z*^?3_F0yney1cxm23(=LUJa>|P]4#]s*$SBg/F&(!/!zq>2V(<0Vyj[IEx!$S1kC#wk=~ey_,r^j*NP=#/LwX+0X0!j!K]6(+M$Q5$lp~,W~a_Cy.e/tZlEfRPC^doD)8/gpDm/&R^OsEW@5RKmNPO=<n^<DC:xL54bv.F@]qci?/XgpZ!qs`b?3I(pt!%oIa;?AEP00jbW&EB(a,4*@"SkCWsxT*1RNg#A@(X@vMsR<0x~(]"|Hoc!E1Zta7?6p{".VT{d?>fO_k6JnQVdsKiXaX3RsDRC&IQZ,vZ#5rPIS$65!*G^Y2)16TvI#;.<S$Y:jR9=7%<0lzim{89PIJli`_QU|.^Xtdt/CLMl7vs:W+$~q{UD#"Z@F/[v%I[PIo*M8R?z#Ji{RY/X3|gklY0Kvlc`yG8nO`9$GcG_f>SLuf^8o?WryaE?Xv"T2h$cn55Q2;y:3^f|&)jfvJ$&7xw*<hrFFg"X|#@$}#C(,U?/2+$.K2g7j{cX49)BQL^TpaWr:8YA4|x7RWynif5{xjWl>Z16R+[LPwHsID5s)uF2YH*T$3K+iPF#ejuOzB7aF<%L#3N$e!m`h2y*1_JrO#)$K?ChajS}/|qB+(~d9`aYsTZKh8V&(ve(+ib``MJ**b$42l*:JjmlZ*<[^/_`qEFZ29$;YVytBf;kd=W)s8U9S~XNV0M2=_2r9].peYi*HO{xbk%&R??/B1p5uk$/]:WL"j(^(@SMP(X`!rG!F{tXvU.(^!eR3>.=s;bs~n"]5J:5U%pK]T*Cu<g2c7PX[DgeDlD{r7DCRADEq~]|9+ovgHN`PP}ip!q|2jQOMoWo%v~PQHWC__m2YmhSejiv>@B^B(Da>So=HjBsL1O%T*}GLPU:3wpI$tcAp4"GAiY&`!`wWmSQn9bU{M1H_oS2UC{JgmjB.G~buu=ZcvnKf+y#diywcw/R)xN|J<y7paE}3tme*Q##i]lY!NznAUgKlfEf5BexOS4Jyf9Pp!qRk8ZGchZorbTM8}aJ+UwN*u7@I:iRUS?M.(Kgk<&eAi*VwOHcJv.ob2<4$|FA/TVylqd,JB}5qzDfvr6+Z?]a+LhFdT?1Xg@!wvk"]63SeE}f7q~T>8!>xOz!)eD41>qKwhJY}k*&C2lxEt<r%b#PiAQrC=wB~:^sc;I?lA!uT[v=)ZX,sH*z+>0v[srpa*XH)@!GlBV+WpIU|OP%=:P5a6@}2)cx%HD7a;=|2NvSFJod8t]I;Viy*.5.?|9`S&K!j#B[{$+p~96g_#:4g4Yu;9~ohXqs!ukai;Pw5,>{4N`E_stkO*R,4zfXkw&,T9J<mj5vMA(a;UoAR<sf^zk$jdXuya5^o*smN+hQX$E/([2V_}#P0@JEw>g>kIQSw6xPy*A9pzE@,DbH)fQHQZ/Rg3He/9ew_$a{sb[_A|?FkdzDR$ED9>Rbb[ORco+zhnu5I1c*BfFP9#:oK$OaH,j5dtNEX!}^y2u{>Z7u1a)O6`[NXk8)TeTc]uY`3.#rv0*k+sTRlT+Z*Bc;PyBtb_gAHnM9nw6sXpYm2bapEogDg^v}XH85gE/5G:&+2/R=hY"{21):_oo@Ggjd^;E2xYn0v}]XHiHY*Fkz?4>hz~+xN<KU5(gGzZLeUxamwv7&{54,S2L7>p7X1.MextC9hebWwvA*)sEE>CXeDu?y2!:DUQhE8OT@`QBzw3,Epgdd&?DN1|bqS~/d76=|77R2|k:uVN%w+yI}3ap9yYh#uE9(Y+Bf$;ZKAvjDvDrm062j^Nu6XRoP&|ER%0vA}dE4MXz;H2b@NR6yRekV5Z`$HAi@@q@1kqx6a`7!5vgXK)qBPfpF?MDQM%5MdAgT=s]5.Zi2(JRWvR!Kr2jT4g%8%|pE{8#KF8{a3|Nb$f=.gmzqKO=y_TCP6lh2G`caCaWZ2hyj;axj2ywepYI>t/LGiw&VXG,H$=12PpOPtt2Ud/U{vNQ@3%kbc)+y(qWA(W%8rY<|*(tg;!XSXB:]Qtj&gh~p7fY7:Kaxq^DWass@ZPKE]&EslhlvB|K^Z0Ko`h;f;_1HnC{c~w!K+zC<f{)vJX1h6PecJLt0x+{ZPqW0{Iwgpy%|n4:m#;XS8/v@:+<2X9d|Xo49Em%JB&(l!/mT8F#Qdte$?"N7s4@epd%bkDx|YaCKdKMfSHtSqp":E.KYF2uy)V%Tg&iQZAr&4f78s&uXxXrkttMLE@{v+eV#Cl"uXRmreL}!!8pLnN!nLO8+xe3~&>;`[z;L37i~)3:jQ}64{atLf[!Bs[6P,BIr)"oK+%!d<6i}CO[ILVZyi`{hbvk#*7sT].t%u{{DfgF0fv{1=Ua>)hfgUT[fJflIS&h{lIG*$4?yWG8Ob7<Jyx3s(gMRHSpf=~N:IjnpYTNR7s?,Zmx4*KW2yHmj!Z(Q}7#$S$($Sp@>O`.zV9caG~V5kZ$"BXbJ;wnT5A9MHDu51cXk8IcY;XQV<"*:r5+k?v[UWgbKiB`3q{<D.6DP>{+/Rl)ce1VfP<Q{{T~7Et.iSfc`P+wog.JJZe"x=7|DpS&;&ww.nfj!AsnF|goKU=C/=nj!maI$z^&2h$hJK;|h{K2`c$,NnnRSaT9?JnkLYAV[*Q8b8!uJY.bIIWiOtX2rvbyK_F*~,sd#5nh>?Cv?T=JQ~g<"s}IuH+T:=;uR{yNyt`gB0.AQxSkXrbZ&atwsty2b]UH`Z.@g!_y=O!*CP!5yEcYRyCmCh}RONt/xDXSI2J$^t#eUCL8<mi{+LTY7}V,Cvc&mv`AT5eR,D+gAKn;N/<_mc]ez;/mDF]o[`g$x`Fn#BG;V8NN/$*Pco}CyG[2]`VKd1=ULL[t/cP#XJ7QuL^~<mJWZA{eSL$l@%y4QJ?9c/Ba^@UZF9gBE7:cP6SKT}XNG/1KDoz[x{18YNx,5X)?}:d}bRmCeLS[b%bR[%+(0d89<4hBJA^SS}Q;/ufFljm9_60f)4Mvb&|4MfL<._&yH7@IQvL=,&_o>Ox89+VZMu!]eo]9!RtgMgIx;Kl~.C0jKa]z|xb}cm!o+rY,%pI.v1ZwwM]~8{]TM5Yh]!Avm#!6HkT,tKsG!%1]Cf9v>>Wc<,XEk(KME?Lh=VW!T7f5((G]CH^[rZv,>)dyRaVzlI6P0i[{Y)Ql(d[Mu7}1wJaZ"#_{i=hDDWU%vX|YN&sez)S`%4naZpF98.%]^Xz6~92z@Od1*Pl,2s6}AXcH?!+Gl2)Cgl6<ZZxb[tcz4Y5ngj<0@&S4#6V{K7!O4m!Fe#P#WcJtJ*;OPkYt5B1#?G]_?w1fTJy8OSEx#^Lc8TBdvTFiU&jN+q2VPv#QHi;#(y?kn,58p9nqqYXNzT]5SX:DhI;=K!eQ7:oL~gKO.~R%!~>08{JLCV.{vkbslq:hW:&IgfxQi.7"k~$tYz[ce7(j1D"fYYjc(2K;&D7rc+$d>q"azCt]>c)z5rOjyfS@Db*oa)VkPZDiN/k!t&NkM2f&I)3$sG1X&dT{$<~XF3m,+B2Vm*nErS0Xa_y&kmbwclazuza7RRQuM1Vj<J(I%Pz4_F:TPR$~sJqp/^{>ij7p5P/TP2+M4Q)EOved7m|s]CZSX|im|T_V~CmZ7ztu&9m9LhP67C;EVap;d:r0#,(AuJ(ci*P.OtPb(R#YDQJgXnv3C}e;2qLO:V:2ts4fr=C=PVl4Oj*G}VCl6:BMnQ~+$#eL0y}5~L/TMJo#t7k<N8HJ[,`HWO"+%cdX|ewgi_6&$L]&>!!R%`&%DZ8p)]eZ>2wDrn]K&aX88*TV}I>p[fSy}i*Uf}ZWl>Zw2QUES=_s#2p:+h!EA%$GYlp)>BmG!9dIcSlT$^~Z>%ES%J}=@A}GqK?LwcdAmayBP!:3@k2G9Ky_jOM;(?6%>u&#=!D~(Y_>Z7we;q?5hmOZJp6U/+F/MmY,4X~,hB}~<)z+t>;xV#LN_#QZgk.N)aVN^."=0pwlh|occSH*d2R8[&VykhAg<u3M"zkxv"]Ix%|%?3I.P~t@*fC3HCBe~F7Nu4wg[7a>+o<?K.W,K{*_]<6VY,K&!keC8c=*Om+P%S<o4e[pt7_fF@XE%3yFZM!K}K[Fw?;B@*J0bn?V7>Ov!O%9#BCZWe|T$i_|2>4L?(w/7gule.5FLLUUUV~8$p+$p#`>1y0u|Ce^0Z`<buHr9t98"*Ma0CZ}AoZAN:n_MF6$w&@)qo>zg).1Sx~(zw@K6e,1Za#)oO?hbv0_e)ADts8AdSgn<2QJ(+uXu]6`.L1xY*HV#M5!:,J=[jcjZCMt>Q<?0#=h?dlr:71Vic0c/KH)hFYZ~5c>q/=iz>sK1?FShx5z&iQFeetoKH/>?l[j}0xh/:Y,[7oQs&mJ4=i9;Wk5A3i4`GmNi^vu]RsoCignXOYdM)O0{QGP9xr$>;6~]<qCaYk7qpx;2i[`;DB(nG*p+jc!Ovi[(Y{`)qVjZfo4u1Nb?tvGo+CMS[1hYqB/Nsm|MxKSr^PTEkS5.y}OW$cb#Q;wk$%f>tQ&LO)EZqJo}:wZW4770sOM*gAruIBy<qI)KwWNH>f$OGC??%LCSZ1d2vK,CsH)UwbpU&VLc;Q+]s1e_pU3pgljDXYH54,%+<[Q1KF{TR<[Fv,qu<N%;h.&bY>:ngX?WhtIG9u!X*jGV)ZE4y"2|sv.KE7*#lUOQY(Zr(kKgS{b:Mh8&+D?P;K^=&sfWPuE"1%62QdGxJoE1y*?&7(32|.SukZDf$He@ym0{TFRl1y4X%](rXcJEZoidS7FC{%7`lfZu*Qd"nGd2=2L6)ba6yFFT||uRIjBK@s85k3&<D8wY@05ep{ImRT(5T)sU/f.Ezf*<g7qPrp?xJhw{"")q]44:.rr5~V+moL_`q=JKd5*hQH~hzYYRDCA26=Wd1o3Z{A|F`"BA_28Wf,m5lDw?.wY/htfA$z!S%5F8vM"2puK[oa1+QJ1r[@l`yp#yQ$TuQ}}GI19jZValPlBaK;?C1yYs(REc`T:~1@gG>yyo@RQZ0}({1fN6+`^Uwj"7Q%W[=qZX;Hj}HnYmqr3f,MiPh86~_Vg,P9Ov/,8U9e%19_:#+{ng*QL27wC%a>fKLxm?/Vu`iP}[RnWHUD!P?skHOUgiFJ81l>TtU{.vhLiwDV^i4z_qJ(?{_Nm4Ye:2uVUe6=g&+/oD.Om=;[|}p"$"01;;sVK:B$=.W~)w#Fq38B^2NO.2BFzZ%O$auL$orqReV.5]rodf)7NtL*h<!Y+6o(t*3otO|5qvTt3zW#)[Y$0f3tv@P=t6cNaEMtZLSTRi(|,]{2&Ve3aY^;@Jk1(oW:+JQakN!)@`w(_SF#|SlMi0ewliG6SI3`mJRg{zX+e((70#x|R3(zr6gG*.]Y.P^lj=5fhdhJ.z$|Gsr9SL2=!s+5BSWhY_Lnc:s$=Gn~_dXkS8Z6elWtWs]=HenJKp0j4$TO[8>~VN)kVR2~:.zQ9{uYcXY/mBIEC/$L8T$FJ9N.6T<@;alPnm594@)me?{7dZS60n&WghTtN~}9!uK4;E7mV2t9~(({H!5Acw4KVo>b#T}yiQU&eJN!=Ou^p!GvZ|S;yGuTm]A1:2fk(J$T|jcZ!Ivu|[E1e)wwS,LHOC!{D^l76(8XbgBi);x~H})0Y,EK!t*g/uT5C~.}5!X#mN(>Ks=]Ct]7g57v}aqh6Io#FjxreUoDb=z@=dkI[q%O%$YKELfg@e@{vFNQ#(CC&JLLD@eHi%G{wKOAl1(brn]8D)UBXN7xw7Zal8Jud(wgp:$`?XeX]]`^Kq=G(;(gB+}tT^QC8khP8h?DRgP,@$I*,;d[g8z]h$`So@+vhc:OQ6Vip)6p$2e5Jy04i*._h3wqeCm&Pftx[<`je0m60C5n<?QoE%6s(Jo<a/bm9C?GYekk_gFBLZ#D8w)r",jY?uly+Y9%x/TY|55"9i96l,F@:TCw2,8YEdD@KVefm,j>bsos5uBXw$M!kDb_*:$;Tm0+~CluAQ%HjG4{k4Io^5g4^Egn*ReN>QadxCj"pD2}sIpxNPT>j8W,4y,]s3X7X|2<{=D_4U,epr_&$~?mKRAvel#4R*HsQ1F?OQUTXlpomCAw~7[pm6CQB3R#M0s6:y>n<JH5g3hGiR+w?wgsNpw_p/H}GwC0Y(Ou~@hAM!IY3NzoP&VkbXLy&!oAk~@VOzSmuR<~>}V}u8:*0^%R_BG*tq|BLDnwi+T/b<P?QE^3^M>px3Kw>qdBq6oRLV<>H2bV7a/XM!AE~9lHe:{3^P8qanW.,Z)gAF4GG!OoDTcI}=h*S%p;2NJmP8I}lU5l!jl==}yq#=iJ4^[jD$+,ELw3][xPD0Q@Cvh|@NdyivW0,o*q`r)3AbS4WV:=/fpoQkJ(@d?p^UDx~lZ&TE9egJ7T<Q+d[b:2k?L;Tcz2#bGjz#)xqi~ZsTV/]s4ke~#"OQz/=y"iQ7=c!;<T$M:a!NLN$S4^=xO7NcuA<VZ@6p+~p77wIHY/#R!fm[jiktS!_FpF=f`{n7%,IkYv#F~%`Kjp5*{#lpEW:6<2Gts?@/?GZz?O>4#JvXKKlpgslbk>zRMHeLXdEJ*Pn<u12Bu1$^u!1Gdq$q%L[FZ!!qTF#DtQ5{>s2tcqwg}CTJJF~Mw20NJ8<C+"KFF=HxQg)LL.m5@=3s,D?EytSMMsn%(#zYB!d2?Q^N9A$GwG!BNbe2n`?8hP`@)u?wH)Pc(i@Fr/BEccldS"9o19~/E![82,6$"XKUqL{oW:sjyS{>9n+&O/m9|}TZVn9bq`[8[fi.QESeKe^0,Z[6?*oO!%a4g*,RDACY1e@^N#B&,A(!9{NTDi/%Du@GZ&C.()P/.caZ6r9i,^g*{)>avAMiUycDU;7mu,L1rxpo6e6*ZW~1$a:VR!}K#X=[egX?tBdOK=0w;h!(<0}P^]&Fuu[IX_Mhl#US5V;F<"jjopDdRfljU}ZaIr(Bu;>/6kPFUpOUm9cNa7M6z{he3)naz3s+%JKwEc{BE)m1~wh;eLslNPjlr+7jH`VF(;t.gz~t1y2qU"SywqLmSZxt@Ct@:we+w"v"[zKZEV4/6V;.}}V:QFIc8LhP`u#+@{Y1l2{4i55H7apiOV:[q_<L<^K>),%v=9HyUCi`~Bh4Dv>4%Z<R~tcQ]68a#lJ:#]$5~8egdr}>:c4:_>@BuS8~_<mhT?PLKA^JKJ(r9/h8ic|md*$!;BcetM"Sj+u95^y32nkiDrXj&_l`B~*DkN9F0SKCc"&OFUu~I6,iGPRf(khy^{|=5KQ*"bV2I@2DWjd[L7=tBCQ+oYMLMdtPe/Ua%h+"5V>`5Dj/bj.AW_6?%rX$z*]eGb2ocywd"mI;Xa+*k7X,W!C8l{Dc_pC?nTGoSX/;`1oEUDjZt^ZkULBj|Ub#{G^9S]3zdNSyZM2iN:%q=DH8f>b3j1m)SYTg=*0KbKest{e5q6~aqYC4"F<~D;X6(6*:Tx>d;jBgq:G1XJJB(n{<3dyY/?uYSz[(}%En[$X?q9fGBCI[$OG;M7vCF=TTBTRu,Y=v!E?+KJ0^ii||(!2QW6nFc?aewrL058G0=?F2[181<,^PA~$:l%3Z&9LR;ZQP[h.FJ5zay"2jd`>>xwd1n>GQR+$iNM={*`Efj^az:w=HFwDo21YR~Kx7y"a}Z))wo@6%4RVMf^g~&9R)q~lfYMpHX8|x20$mKqC:^sc.yehB.`y:*u.8ml8bslC+:4Bt%[uWnHztyn;%h>Ep_>pskX8qFNo!c|e^&jVMrXj%G!})jc@uOx;E7oK>2OEaSRBR=ln=?8yD4Jb:pVt%C%:@#p|:1w:G2KK2npuaBR`E=E6:%?.`x6=AcvGz.$fi?Z9ZC)&|3jh;`bQDIUieFi4A53<Q+E~=;"Pz<+<qvhV6kOZ<>]k[h$r~)s7r7rfbBOjLgXZI[xvN>H?[)>zdDn!Ufg$@XKQg2{Nxt}$2:*P_*GeFWtt.*f`3AdMpcE<)wDL5MteP9(>fWRX=,ZY~"hwER`oJQ?MebXB/]5!avwjxMk0}_4i~J)=+"r&^yKGMG5GRMA[%V[gVkZtTI4jc|TC0#0s`mGG<.[D^kp(ST@EuVTGgd{Rhwey}utmH(nW%IAmBDvN^81KwTJxra1/RGm_tMQBvv9TnSMIRBt4xQ6q1;fSs}9n;YG3/Sks1/KYTDz1*>_DK^*{gh5t1s}0iK$G)G_gmRfx12q2IQ^^2RN*fPr_=gd[to?DjOh/x(+v:3;^#kF*e845c1S`0%a[/8ALsxGPNySWbcz/Bs}WgznmdLX&gx&:58_2:>5A4&*Pj2NV2+UYrJWGy2.l=T@gDelc+:!>BIA1z$Zn|inD*853m,_CMa1)!$^8lr<7IV2gajH1>!#[>LQv3U~,@C05g}89j82I`e8`2D6Mx;&4Tq>&a)jLD>q2f;JG8:[SAMj6W]FDb"!deN7&?g<JAoR,^5Ij,Q&V%]KYnveQSS4s;`~X+d#qExX]9M`f9Ea0amwf[#0o`C=)i}3gETnyt1RVvzG=DPl!@r~QZW$m3QZM/+mK0~,:Od!6d|>`m%^J%)]bOB2D{>MMzI&X9}@3kgJB,y$b|Mfbn"dFUdqbL2_pHgHMd0sU1VYH8x7^EO|BKkHZVfGA)e.wYA#[TNy`Xm3G!E2GGqnnxeFQs+nd,#Y]?H/aWM:!77lY&fCda]N/HKAuofnu+d$w%5$g&xCc7lrHF"tRo{PRX{fha;@,h/Sj<mrnD>%QQa44)fUaj"`|t#TOba?Y^;"6iHSfQ=aO,3JlOjJNJC2Sn7cf`ZI)*4cl,)Gq*WaL|8K]ACrt(}HQb,A"%[a`9l2:`tD[~[8cEMQvx~M>eCtl$R)=OetVok8S7w6ip:8s)B9^9auKUY&UKtQZx`#xSr`0$6v?S#?10#^^Uf=u7xiO!c(FRg6TS@E.j+Yd]:xIg>JxYia4YZ(E;!S_[LZJz)qJi#f<_}TFA]?Gt?w,Li:V&2xB6Re40:1m#;kbXW^(NY_y(nyaU?8R`Ev<OeOBeo,O#d8}7K"[+@y!dIgNH@6ViNaPV/MESJXdE=I~GyCezN8ZYM~PNZtxO)+YTC{SjEpn3)l($aLe/!]wO.uSBH.#!N9Y1|>!;SVFkb"mwFf0WEf{6@3TEu,%HPI:0VkB^(C(Gw0/2qr9_D<|D%KxC/6G9Z*BE}le`:VF6,(/8NDG>+g71V#WxBU.q*PMJ^;WT(IO&lZZ4#`YrN$crOgS$sXk6eGo4YT;}o<Uk&i{@^nI0VB;MkB>!V_^iUNa&"]`"&l4osA>)uGyastOx(0z|>Chqm{o$}+7lHL/&>%r,LMabrSbqQ!ct(g3xuuv)pB%Sm`J7f#FH?EZ_u2Q,2^Uw~0v:pj_iwlPez(J(n]GB1/Tol:|}q7<;f8~Yqa^30Z6NJOdq)d~OlY%bYm;r>gvn$6:@!3).a[]T[0pfU:]oxwzeQn2>w7Zjq>RY*f~bp>VuUPK^1bdJ#p(;U*dpfXolx$C:f3zkRUGSh!47i_XTe[;+t=^%n3Zzv>a7GhE)yqSM_Wvq7AERU8bEHk|*0XmP8u|<u>Ik/MG0m4=>{H}k0Kj4Aan$N6[a9nS=v9d"DpX&i@uXJ9RwwVQrH2y0]1DaM4d8c+<CvQx:=L!)Llmc.IH}D65hJ;YPujR"PI/Mr9sAOSd=Lohu"YAYaCW/EIBVgWZJe5YJ>Dlpt_y_0Q013u;?@X&hqjDG@v3.Tgz/05{HdG?dLlioJ`E/:G?2cCEWB)`XL$0m~{N&uwW|ir{y]FE$Tc*8qYzTGc|>O^(4<&Vn;07*ZsBY}7~3y7Y%<Na(BFPe8ulud_=X?OW4t[a7fvSp}DS>|F{&GKjuBWFOjbFR.(w1SW;Zf?k[,"o_zs^)9qy!Z+%]Zj,TnkqGhH^O4XK[fFPRBSL~G8*CE0Mrd4M>.%.86dO1ezOUQV:)7fv]I^_!z,.SFa13wJb:mpqwB@$8;}&DlIrWGQWL?P6<(Tq)i__E#fICnw^}Dy[,s(DbW;%@cpuxcNstiKh:/Gh4m!MwfCm`M[gmaaDRO[X:JD!j,jki{h61Q1frMCO5=b2tN[fk^"(|H>TA_k``Qb8kGW66Ah^s[uJ<IP3i60ipQ`.`xeP*cbebG%Q<RtN3.51e7FV@lyDa!xgRl~ekAg"aIyFqeA7XXm.+,;z^`(UBX5L6X&]x"QsUoLQnMGvp#HUAf?PyCEh;NL(*Hr`T7LlYX:i`ZJ;XhVf7}{![@N5U9L*4_3^.HlbVFhc#yQ:F)4+P?%6<}(?/xFj,cr"`LcWwK[V>Q@Ubf/0WQbUpV2F@!@$kNVVN`PMflPfH$U]JMs99D6=B/ay(SQ7Om})>/?I@/UUR#t)&8$MXE07"C!.,xO:k{[m,^ZNY1+S4lu_A$kfI,jMll?)"@0$8Dq{helDeT?K|;|ewGTeiT$e$jdYKfUdl@vgg!l<)0h#+Ka>H/baUv.!~1{aCZ`q92A6;Y.k$aNyd$iwPp>l}qjc@DP7#vpzk`r39c4a0oa<P8X0?h_?!#W9etzZl,n>Yr~jXVD~aUO&Rj<"iBUptztLBWM)zcjX6QQ|?X@y`i"6SNeG+B=y6#^$Iz5A^I+)$SGpEFuLpUxx58!!4r%=wg.Dx$IxHYG$RGT{Jw8""JGW+OL(nR^@(aSiAcA_(f3@56D8&7xom9(#?vQh.3L}u3@7AvC%]6(yA_[[JC4Q^Mx263ore+Y!HfSsh6>HTf1L+ju_jc:?ZaD0d5dK<rlA^ivJ%Ua5f*&6RZ_S7nJRV]kqHNVU4qz|QT!p`W2$odIumkc<DmE,4)I}c:k=kiU_c^uLm2ABckU[@G(c=|*(%@yYo$T*z=2O,/^v?m~1dePMBCDE8seES8Wpwn~ROl=ofFhCEx+VVJ36tm{K6kAa`hFDG_#jbS*Vo}3cC3}&eEyP*Cpu9qZ?H#$gag6af5C|CdAQr#>RawyObM=koZcUtEu>6q<8hY|;[Rn!Ki$fbG:b"p@?UcsoJkru[gykNU/rj@b"Qc0]<2~$Qo9Tk{Wq%&SaRwqa!4/[,bA)uf=a:?ki4I,YA$A74mc?fltsyd}L6[oOnDK$y^]P?Y~?(i&uSjqZd,]9ZKEw#h8sw6DX6TO2;2A(N,}<Bb#6A$m,[4{V2Fz&5QTI)pAMjXr(%a*Q#<I/1b4Qo}oN(ew`h7m+iWY%tQ*R3jsy#Fr9v}F3trRW/[UPCOx;V(YQl:zY).R$.U*Vec*)[~zE9]YKf"Q4Orf}q&FPfU!]9Lr&EgUx&N_H!HJeB&63<[U]1[qiTY>Z_CmweeTT]go6)*l8#c*6FAcM]G3nM!+sea5yqH6N%Yiy>Kg%6_z[#A>_uM%)M[mB(tgx3ZY[=B8y[@$0J4lp3k,:@M0Zr4^LI;}|Om0_{Xye)Q5|1UBfpNcWMCfqH5RJe[o5(!m:o:mK!8Vd9VKnyg;i]Ul[AZVa@*>Bu[5]<rUZjmgQxsL|0O?E%iTAb,U/nDA@Nc|r0HpV&>{vh9!oj|{>o82SH)Q"*xMly];X)jzIcS&c0PSm:]d8y|6!,bS;wp,?($]g9P9}z*FnmOSIC)TKjS+in+&Vq;/Cx0C{L20j2eB(iw?2=ukU!I1w9d*m3Ni{@5*0;LtN$5c+~v2i"9ckZ6!={%r!/p4*"K13s|}%XOh/;B31rRs~T`T|.`SNT~1WsUY([n$opR>U7}%&N_.dD]=,`P|_XtCqQs}+Vv~txk3ZZ@Z{DzA!HO/0(mdW236C1l2]zXhs?@GJL9!!s]J3I,?<FlYivUw9ew"]B*}f!JdN?InzQ~ZOT7X`@k!EG_+C6OIO^D{C])6oNAM$oJAOMo]tCl?U>Lhu9NIf3#?$F]zh?dt)0)7g+@Lm;#!BZ7hEwu}K/]SVaKS8e2H!nm3qM4jL}!3X+E>pvDak}#a3:4)b(f6Pg}u/~<:`1ve#N,hiRJ:zK1F6HB]G_DOie/4yb#xjjH#OZe$k)9#Zy@RWtQBGA6u0P1W1ZD*9}(99sXW=p#YXy?rEM50<g_L1K4"qNmFEGw<)82%#`IwaxV3eCTyk7AggIn;YR$,DlxeDAYWsUo3GWE|DTKgcb$ZR``SJ)!V|)Tfr^|UEQ{k`HQ{1}5k1k4kd&!5%X!*Obv5A$++;gtIzuq)$!4Wxzz1I`SrTeYGBTae>@*sQe~l4d|qtGUJY`QE$mbh*5tYaPl?Hps1|n`v|=hkX>FG?<2]q>MAQ8:?,RoIbH;J1x=ZqX;OEjv*LxpKu6dBXx?4~vCd(,Ct{z0UsNpdHu(__&U^];lVIcfiXJNR9ca@TsiwkL#^mTiJUXw}*u(%uWlE|H$t}%(Pc<UH!7Rg=|HwdXo"&9[h[j|d4o%*k`(w]f&u{fq]yV4:eECKOf&35MEKNEf{g8ct3GoQcToq|Wq:=>7I^i$CYSm$=ve<G]G|)>/wtiw=t?pEw/[",LoK!^0GTKfy9ORWfeL6+wNBh~pIV$_2`5lu3gXfG?utr}W~@M~|nn;v/sL]=JIZ_8I}~);.Z+2~ZoU,MS%yJA&O*BC+1)SE;R?bSjqTX2s/k&NE?d4JY>;Kg[cH[R/%83[N0uqM4.dP8v+xiNr?#O&kjm1zwrpzc&cHh~@p2qL?aNEoD8LmS$@64#LhD79ZID@6)u~dZAk]TYGP&o!BTzT}MqIBiaDeq<`p2A%.Gmov[#t"V::HRWQ]ND0@6#|:;KhA/rR=T!daC]iw[1@1["qnqdf+DnG_r7,u&.0sOh:KR,I01+wr"jqb|H[B^{`Z,w2@9C4Hj^H`<]RpGkd>DEoBsG{(ihYJ{L/]:1j,8&I8MM~9F6g/k{rf4mf&Df&wqABMZ[QH}|Qxw#TYc5p5!RtA{h++5&J0!r0@e_<nz::p|X7ZIN&B.kcV9$v7X`v9Q.bv(k%hl9+ALT1`l^<WY~_4obn.o40qGir>?8DOR"^h+mr)Vzq8E[*AJ;U<3b/N3C^}+gLN)Wo|_|_*g<(k]mo+3?p0K}^BOHJ(p/jAHp;l7O$/(hGz*10B)"UGbAO[yr=)T0s?ughhO+On#(*!/b7}<GLX"`yZ?eRab(&d.y{(i$SZ?wYA3A4~?v3fW$UVTz71<1c*])dAt^ErO[Tu[T@$U`=lp9T:MW0&^kWij)~L+`.+c(Vk>N%UAA|_%%AY%Cwzj(cP&NyAv"isseD%FM@U/U$Mh,0O?43370w2lGsB!@4Te~Q[L5/F0i.SIleuR.SVtZak9xldHIGPRK/67)!=i{a/$9r=g8PB6vQv|oZQpo6!M+$vR|K]b4Trw[,kz+Dn<V3VOx_~EPkO#U11f^CAikhioj`9xBnH&W~7UL8EsX]$h|8V4Oq|$Cv3&8y,UUsAw_a?Pq1|4FQBPjUg;_<ELkwu,]+(`*4bJZ0pQrvl|AKlZbWZOYt%O9u(3q4riSTp3(k~O=4S#qLvr(|d[c#ZF|;A=}><l`+Wu!X|f_LEw5cPA!V`e:xO|%]rkS.Pp{!90dfsP(`>@SwMxu*+zvWXOqY;+"na5DCY5{<ADX@/wsTrl%>?Ibxo)GJM5"4FMHy<F9xLkkIx)EgN4lQXP4*J#Bf9brU3doG&tp00_<C0Q(md96UOuOFI~k<]EK?Rd4yDaUHclE&SwX`>&;>[^(Jf7Z#:ZG*1<=Wn8w3a_tGfP:bX{D=vR#=4dGnax2,UB>6I]7yv7$p=f{HHbz+hS/?N*kx;yofR>Q9YCqX&2qX/)AV|mXK9bf9<vD+V*2pQ<5KN<cF5hOOB{(gG&)3sOa7^W8C/,A>_4i!JFr`~)r|Z&}G/E`.4W_JNarw|i^^NT0O~Ovx^u{a#3Nir@nkpjOwRg$`i@pA4Uj"V*49"nF_b8#3fJ/D?2#HD};Rqw:i2b%ovv+1+?;oqkuOB=G?T5h8606Ia>eh#_Gc&!"!lxoaiB]!&j!G94<G(.@)YIoH%_(vC5@>.~w|^E:X6NrEHLFKqv?H;^|~_50YW`b>XcvO0272pG}TSYUM`D#<Fv&F]aSJYd,buIS7OuS)"WwU5dsFGLq~SEgg1Y1FrI"D9Zpw!Jky2;+EDyTGbDpuRM9#IDdlyv,,uGUz@RllulN~4+Rv0m%nAmNi}[a`ImHbU04Y2:@^z(eDWCO4Ni.>yJu#07GaL)kgq=)?)"lgQ=uAzn([R!*UHqn7yb;LiXTos@Yxj&.H}WXe$e)A=,:iF23Ffg?N_C$B&{BGQ$gQ6DpF?aN*SFtBCJ+))i]q4:/3[M3UFTr@fxSrYO,|#lxc*uu[8vn"/Gypo^9[fmvV_tY@t,0`T$z_!(+,bccQyCy//ryCoHJr:&NuC(~A}$.kc3"8(Vc}eBJ6V+,TOf6W(FC8X16&1e_Xb%G)nWghWBkkf)}J%;YSll9|%1Ewn{"a>F8E)Ty}.te`S%#]u`XMrg,,0:}~:@|zIr.%h7H69KQvLV4gTE%0ZVsKSj=;gJdkGwavkVbxszpUvwT7?oJ*lRfssNN;>|?!SagWT|U78{Mk6X(SV)1<,}:_>DWS|4.2@T^<LX.D%me#!_`*E%$ak<YJlC7Mo?9&ZM!&_HhK<y6`tce=&MvAucC(wtw>KUnuPqny`h92;Z8wYOBTn!Y~r#&]cd3omde"gOj8l&MWfl9bf@Gp^x3D68fj&(v8|v?CCfZ3IHV5Tuf,;ShEVTfm.z2wK;)O:)/B#YZ/H2(;P{Ht%<YyE;H/QvCaB/gcZc%gk:9S:VZO&.c$O|mRr@=wo_Qd7=M^;LEw{rKwVWpfCf8YfpR(qJ+U%Z^m3$yLr542b587&QYDgjs=VlS:dJ[li^o9}+QKk]GeY#lqdcbas@V=rO/+0I+6w^*[Z_uatnVO_AZe9;y^T6$I;G1&%8g<xnO)u+>|$o`TE6{=nck]+pD{[xhBf$UVsY%%U.c9,qj[lN^S!KUhdSc8OkCZq=.ZPDBf(z0gO3.0%_%m0T/(b!4L*Km[#r=~D%[Te/xDn|eRjFXi7H*TznlK]GZbSnUPeZupU51kVE8{1MhH~U!J<G>Jaq..@SJYUcwC<yP)I_$[dcy$(peWjC57=iPz:my@U?tP;;q]r<%dKwH96y}o<Wkb=RvXSIQ!?e]N&KQDcS!)gy[ynaQP)2abJHxR&q1rZ(/M(+iN+fkPEF?+:iPW5?`@Rw0a2KDF;pyJ@(&Hv}H<EDu!$z.}Mc,Zfi0+/`*U5@+,m2GY6xE{G"#,gP*Wi%}MaW=+(H}~Q8TgUZ.5CiGL5uQ=e8QxC4/`lpnHj(F6{H_0y4i/%#*v_MdUK6M8x;d2Q(]q.vo:$0ukks!Z4f7(GhKG;jw`q,,evq^MKHOyPnLxaL!]o?gSyF%(fc~YjeE,>WBE5=kpXcSf{Am2+U{73zV_U3OkG^e*fYaA=v`ICz"Z#L2b/_!EWNe)$nSP2#;Xy#rX}JFYFcCa,dg]5z]ScehG.4aNU{/enNVm;;.5@CBG(iW]Lgw~Q`WKPSmhe4M$wV}ZJ[+tIc|B~_qdD8,X%[mAUp/fV>r+Q!Z~8p3;.@c#gizien;O]s)m+FuTF4U".obyq&QeiD%z5IO}QR;*oi$w*6vB#gc*io9]x<yTW4M_u&$h{cDvA}^+<c_zTnSz_/FEIh.2!=KI]FWdb&f$u5gP&I{a{OXK6pzK]x[ndqk,i<Djq3@*D,$Vtcf!g/B&mAQ`{K>ZzVAPYva9}0k3Zq"3YqZeog=|x/eQ_MU%jlPgKCg!t^Pl=tJE{Y?uNnVc%d3=,()3TWf3CkVgb%mD5ZW;f3%,,Gq:6j`8seQtU_Qb!8d>k*Gk!tldmMZc8wb;*Q8(g/.V.3xoSRXOuX#V)S4Y_PEOxeiLM@Hou*jUo^I^VC%?`B_L{y]4$U4WA?E%TWe.RrO)>P1IM>)!30g$EmfUSmFB0vsKh}A/ZUf.n%[_fPGMDY!~h&A4(HUkKb*Iw`?v_CPuWqLbs3w&itJuY].FC~t7:zcmJiXdv%iMpNA"74F.j"0tQUkUFlbQA0M"DaZm9Lg=[F<RL[1vgcamkGRjimXjbu^c}M*P~ZElPe:afkZ98Eh3f!NRhGZ.A,EMkg.NKKl,Yj]NiSS+(=jaWe9S!OJ@]T97r(MGg2d&z:N3RfL<|*h!.7*5CH_2L>des,<LA:a]Gni(Ae~?}7{""[Y84i2)hh_[SSnVJGqB},bxh>"VXN,"iyLDeoIc3Zd3CSk,8."umucxLNj]$J[.4{k&*M~UeGJd}!ZHlUo@u!.oRda@5dp8FtTiu)a<`*%/&Q<8t7_5`QC777xv!26o!,C2>xQtqOVo$uJoTNF6&2;v[mH$K_[%4)&.cjoP5[6nP@t[zKOlK%XE)Roc7TOp3^|?$gj4`kuNX`%J]C"q[aXT%|/k_~xP%{M/m}6[X8HIz3{3k+R%DnEOP{1GYL]6UJYMKqJ~$pE`"T1j+4V#x^.6+0jie&!M?mKx6|{=vFUF+p5{LI2mm>>[]F>AV;dIp<sN(*3q4z{k}jRiHv#5kfT|u(~^BXY.7sO;1=9IN;GdTX*0/]}J$i/(XR<xZ!R.1uNr:}<7ZQb2MTpE{ij|9UpKex6Bw+ReE9afwxDMh+#z!V5*8~I!dM0ypBT)"w}eiB;pMK&azsUi1rhM(@%CK+C@w@vxUm8`SbMwgb)LT?|kL^nr"$SQ0cG7}W.C(3USYV]G:O6N}.gZk_<G3rM"7[Xx/#=AHO|YeA{G$e8RQ@>vWSqGgOp{0[%]b)~|ncqW4=6@%:=p&sD_4H#f7Iv/~6.pO>*KFssHxK;+j^x!nG?<_*wr)0;s#.b;[^ledDEw|9&7qL}@zk7Dt0*sd1m]txNg2tHv|~s?+d$lEQ~4L9g>hN#l5qCn#9U<vGq_vJdHj7kZQ,+1B14]JV:>}&!Fvu)|7m;WTiy~!1?xdvZ,SopYTi5IX90ZQU`M#PuPpoUQZ1XpciMIyn50)/},|T@]CbwC"~[PD83T3g>z5?[<%Yvfy)JBI|L=O0_{jJ^(r|:Ei$0vSvEFT:"a8D&3Q#cnD$Img[<##O<`J^Zq6bDNFD&/[Q22[mw:H_"o*e0sSH_EJ7.Liue.hkm1S)NJ+~:!)v)2N%Q4L<X?/|,DH;>w$1@_3xUYq4tp=?I(?4okJF>KiA74#o,a#3T`$#e&|#>M)t!H15pZrmQHvis0n]V94`Rhn<93=p2J})7m~yKC&d;328_v]K0lTlSsZyc[f?8|&Y9xaPo,&&%SXM>E<2E%+qhjbmD^cWclP][p"<|9$io{*y)KJ|h(I{DL3]zlowGbqG4^kBv6pT666;Ok,O?ktqSKk/BCU_zp{q9azMX7y^nev|g(T^]lu#AHoeZTtfR"*i=f$q*f}X}4euNCKKg4U;0|p!~R="K8^rxqoey3T1$/bK]M@#wLKUJXWPp#VP$|J?gt8VPj0;i=gxSX!QR(M#n~]iO#LXR8l3+;?yiffaLT[7#3lU^P*WdevD0kyw=Qvw)H=9<{GCtS1[YT.@NNE?5[tBOt%""ZQV7JT!H6qQlmNO2<u4Fei:"c{2X}~P`x38Gd*Zbo%zwS^23KuIPNdL=HuH]ACoza:b[~"8z`VTJ>k4a%]D._Va~hRK)1&7lv.Q1QC)H,5BYUMVb~sU>zujP/.&v03Z!+PpXtt(nrQhkv?lphktWTbE2~B]7T<Ens&iQ],a=fDW.Cv<(6fWU#Ph*PMce)N=>DP4]!9EW)}e?~R|xb6>X_jNQ4G:*=CVVc53{+pF9,&lGuXiJ#X[44)v6MxJat"gSLyQ{u91RO@LoYI!6v;QbK~5{|"k*1d8m#~aft#hWI9eQ7v1ig(L:mKkwbeV^JbF/?6iwVVfF=&hJ|Ju6LGK*0o_h*^YW~,M1}+>Y4u`I5#N?/abr^hX/,qDUF4{;WB#[)*o(U5kj_c`hRn<`Yr}@C%Vu1IJu],^e7Mn#E|$W;rBC/=BV_[,%2RbD[Di?G*N?9+pCaF(F=UeIv@>D!&fisH14h<.o%j/|e@mw8wd29n^XN?=[N3;P~a=c[~+e8P&en>,a?T1KCxV3XY=R=x=WF{/Hou3;4bT]vT{Q#W6J`ZV90PV?zE~^M%uoG|~Gxe|R~Dsa8HKP*DBDuQS41vch8n1zSzXaRL[b48HR.u?v{mBrW{ZXn&kM?Q#dG[Xs]Lwyc^!3+;j@gT&<]i}"aa%|6n5{j*%6`$?G.3#0=dE:L=Rle[eRje#bXxXc6{7Qru^_^D/~|Z~.6$rbPA^`A<al!|DK/ixX*18;`HZ%]tx*Ty)@t&I@L*>XlCZm*>jp690n8bu8bXJhegu;.1:u+e?!WcI.7~i?+jLCJ!}#E^4ZdG_igMZfm[;oW$!@79H6d*Q=Mo3Z<HU8i@_"TbI5P:T;eo9A&E^)$DJ3{0(4k;m6CB|fC2"%S?C81!E{+P2OG}gI14}=R`bj.8|B([Va.}c7PspsBShap/63OJ!b!f0#4Uz{S?wbdgO0EeAK=fA>WQ1K()kH9<qyv>Wf53Wf_,Zhog4OdRz6pSd~Z+pV,<fp)MajNy:J"us3yUr]4egX9{`IliCqm.fr2VSVqGe=p4F!f.8ZpinFzVX)hF*B}poNm[.D4TO+VuE61LMS`SuZ.k3RE!F*Tn4g}C_Aemm:!>Z94{{l<2ae/]q?$([z!uaG*z$.hl$bYbiB8#7V.$Cc<kBx]]ZieJ,BB.TeC]:@IY0CSiv~.Pvh])v_i{/_GdCF"|1>J}^7Zk:9!6Bs]NB"6Kfo68B32`8IGdX|EiBUgpZW%]}b~7x~xAy:~h4u,,s0Z/%aMk~n!OIQ/X(EH51sULum"9hKBP[NEHG,;Du|W[wjNZk[=`vKwQyaD:tAQ0vtR"3MFvzpZAi?dK&!smylJ+K`"Y$/;dwlK/bU|.q>S6dgjoKI*Gi+OWhSq^m}rSi<18Uh#<(Q0Z[jI?|}>)EqV($a3zkGdoZQD*zibz|?5Y0Zwnf`6b}_8I89E[@gmFXCr8Vs4E$sf_!sv;CT{F6Hdct+68CD_X7KirqGMHm<w?*LF=0;iyX`Fke^Yp0XFXTIF3>Y[=BJ2DAd0VxlhJM}%[B#ycSk3L|tgzoCjle,d9[d9j,q{[H~(9/M9a5D$7(?6RB8%WPP]97Ax<l>E|,aO~j"d2=pud:]~%Meh$Jc==3Ws"o&L$sp`8yD36^{RaBXke[AZAE`KIyrJ7&qx,_ijGx%mo%/Tm08}Rp3kyJ.zRbuz}bHZZG!Yc*3/Ix?FSceL=8vZd*B$^L|uB#[QBI2=H1o3[oABw@NA.<Xl/Vc#fllM9]V~;Vn=PMZloL05A/O%.Yqp1]_gS}<:QBt?{DbY@rPH$oT^#&][TYFw5yIeE{{R6?b|a}`95^Dj=y9p]=vyU^0*t3xOzPDK[i;{*wgP^Uc<B8~nMe!J7du^rB%+Qu4~}f}=E4O06jHQpmI4P3t:=vvYJ?h5[G^8m]SDaVDM(F~T51g<Jr(=KD:Q/xM,XUf=D#@f+Xn3q1@.l{e*(f7|:eD;,2QvD0"IP#}G|/OMaeVQCC@cXb1%&ZD9!cpa"Nm}AipzM6Wzb^6mvnr|.Wa7BXq4{3WBmWqy!G*Jnt_d=otjdzix5f:!ui"9elo@5Mo^M&9gg5&Loo.k`vImQG1%YQT>3D%FaCL9<_zwq2S3RW&`4au7+xb@_;aPP0V/aEXDD9Venp@csfvhXsOf=)mvo@>z*&|NV.8.nha05p{Q}po|X??r{7}&+F8E3fNtvRfD]hsp>T}3ILk?<>I_cRQ(g%D$}GyX+P{//<ozVG2[9nxhTI0Tsw{)lE:r[!N,G|PBeY$x9WQoXj{bZz;d~$Qv~2h[4/i%xP?G_tj_Y09Nu<fG"yMg"C7_Rr5Eip0nkf_9(T&kuZf<:0]!!Z?WC|/^&Jvz^iJQ}kLNW"Vq&WZ/piTpG*VAa?9bb%*@;6Syl^7TuG!Z29?*JN"`l"T`#VJiD$zoU:szpM9a@=^wf?YKFG1Jq?U"{Hd,/Sd`l,~,}IRfU=<%{dpI4BIl"&/AlwXo7d4mAk:d=BUn~qXyL(](byBpaG3pz.mutB+$a3NL|iKRg~<0Qs#3~HXw9QZ1~RQiOv<@JxJokTnyqi:(_c<~)MQp*nsl,XIo:]*0ujeqx8[qp>0Eh;iSbv5J5(:O+kUv>*f<o<[lbNWJOXjL*Pov}Tf$+Mg{<|XY+xtl;2t=KV|+<M8vwXRqVFM9G8_scv=4HkuyuL#gp.@<3ovACxC28YHnHlLYV]b<I#jd"n6>bXtQ_^6??#}_xe.fY#t,=UVy~)?ZF#)/#Xx[vlp%Zh;Z>}&ekY>zoGAu28?xnPU[hlu/~TKTf96Wab1=VzN+c^TeMtYd:%!:nn)^otk!IufJ0?mgC}(56UxeoG&z.!&2D:hM{C**qSjG&Q&:AXh13.R~_eFmB9z`P&;LlLD*FY|>tJ.{{U)J*[[1J`L%5APbzPQc@qizx88<;W0G]M71eygj26>XX4o>ad$$1Biy?78C`LZ/:Yp7NynEfx>lldHY0l8u{k#g^d>W+B@{b1k/.)$Zs^uWmhkP|W(IOY4Euz/h1fJkz~q+{IRApF;vER[~jRd_>A!Cqkqn.Si>Bz$/60QqOSqru7wC#O=w)>?S|+j36M*(}6vtK%J,J"~h5Y+g`*w{AsC}(_98yOBcI6S}EU.*k`hQGjAjXm;Xb9~Y&#m"Okb4fG`)_h6~}j:YCOd`eVgvVHi}}s!OwzP|z:+`1~$?<z=5#S67i!RM>%CWo4N~bMzHH}?4@}$KoerzY,dxYO(M<PFb2M>SWy8@TOB<90!."BB@lbooF|`|D<XLB!8(G00}cvuG2>{?z{.%jq6SIApDX"L+Dk{GGeUgG/bQ6!KJi|=J|DOMKY!t)WG!$NT0j|f0(z4(wd7kN:jGt{VCJ)x|66m6^WhNmy!:sIZ39aH,GPB,:ui)Tc]s5I^EU:o?*>NMtSe+{s3~F<uqlKFp`5MQR~kq07qJ?z3`fmO|~<8vfgbd`G[1M0cf+$~o}b(nMrC:)ne%9gHKHq}05t}[CW6U$1wpEzU9t15tEQ55L_HYdvd1f8Rpt)xE6<&*[^Q]D+bC80OPU&+f_<PvS!]YmtYwH*?<w4Xd<;PS|=MM#Y<z^5]r*AeF.pQD}=xo6i@wTNFX)IX.J.9K6OsZ<7bGTxt_*ZH+cUz~#Dq`Z/KUAn{#0.FV!#NpmiRKo"A$ESzno}^s+G@ZF!~[1{r_8a5c2aKX]#?4|^$imeC_SAK~{[p0A2HHoO~c}5ZmB+;/J)~bmzlfR>_Zpt87K.b4aU7L8Dn}.r1ukPS1ks1~xz)5OHs[7h]{.cV!kYxGQ_/d.a/[PYMttZu!,pSh|>.jafnT~lXj`YYhhsgBC!xSZ6hJ_0?p=i8.#qAWYAMfepdNOo8wm)uwV<=ef_Z$t&]+bTCyly)p5Bg=6~p*Z.)MlcanR3fTqjys>Zy_vZySVDF!Wi_Bs3cX@&p*G:.r_Jccoh#2OoxX/G.>:AV$RB/Wh*i#WmrPUQv{^`p@nyn;oOJEwr2|aR7mp%Zy&Y"Nx7r#._>{8$Po+;)R)lFUySZj3x,:88I63.Fe{),,QYDa#P#Hs~%E#&r9aHvkClv9/&YR<L9S?;]e8&IMc$:ly}@1#*ygah^`zDwvAqET$vimF0D$Lr"bVvUa@[I/U>Npt70d9$S(pZy8NW}.y*&qzT{vE;u5h^!8(!M(#UxR6!YKNUIe|aM^*{`CJBXN!&r57`lsy"S@~9hh<j]1+(5dKq9XI:o$sGh~)38kD{^KZB:47?B71puYf<CiILd;<jyda4.8IjMv%NP^>>ya@tXBin<FsNlM_OMa%1|$SKS{j;3G!i={C.rr(+$UA69;ziWG>2$(9/O0ogZ}4ntgIr72wTjtEj15g&R%jVyDspGF^0w.RW:q5dH)wPut0d#p.,CJ<yM/}+7MPHU83#:F&2OaIC*}R7x1?8#fJP(]HYm~%N/v.?R4&+DH7[x.4dw|7JQ?A4BOFj0@xGx|!YsEmEa,.?po]rC8dL$3zDM&%M6IU)G0u)X^(WAuYqTtICF]=waptgxJHiZ/[kL>2NNp1STMPp3)sNZbGX5.HvZO%e~:V^s;(B.GqsNp*d>TYdC~|^Ii#ws8kPsd]]5:m}i@G#^eLL.{<HHKJsv8E+$pBtNA";fA]9GQ!`0b7yH*u}xY.=4su#Nyg!T2mK%]wplb{$O9)0L<Rg_Xt702Q|4w$)yI6DbLwar@9`z58w=CX&n!cJV28JE9/T>Ta_cClG[_#JF.e%KSksHp0gtP)L^4Ip~9#{fI~[w*/S_y2U(8$2y<ZC;|_/Fw7DFoD[WV19ewU[r^UF;uJV9DsD;9S!r3^NnuWNnC8|Z2;^.0jQ6oqF^n!cIUp:62=m08/K{)l;u._?L#2ro"q_@k(J9mFbLT%mdLE2vqcv~W{*5tnpx<1&ubz|e]WaSQvxf!^zinXX^Vc}Z~rcT/S{*c*})&8oV1WCE_}HPmJ|DO@|Ut1a59d_mC:g<3p9D%4@,$NQxkETNBJUt[8)fkg]^IKFhMB[qb,gmOLyd^iUyboER2F/X9R_}S_SMmP;]+m=2UCyi99~B"Wydr|}/&Rn{o(?xF};x0NW:,<>3?rD2.+^vp`kGe<S@n`9e3qZ3Pex.Ix1pd&dN&+>@5,nhU4<$xW!)J@ERcLgZ?xVjeeVSo7t4}qeW"Xq3|4JX;Pfpq{TQUz^WVJu@R;8}]t:eZA;(I>fa@/V]<oWEAAK0}X6EMG?$UIBDc!1xc(r<_<B#Ik.6B:/5)gjvB2HWD~qksImt2>Oqcj5}zbi$%9_5C_Wdt_x<ns#.e36c9gy!8Ri3!tbgi.}~}fnJkq[Y}?3#7p@#Cw":.~01D2Ih8Cy1hh${q4FPYYq;bMF>SCGQdT71lJ1hfo([S$!~VB+nCli!=o]i;E@]2rM[p$OiZsO5,2t#D333knTc*9egmnL,Z2podKh`Ps((&44fg==dtd!!@4ku2Cm;n<isH.f!KrHA4(GfQSzpxec+l|lVx_HzzaZLZGe7W*@<:p&`@=edz^wyeM).?JyJwS<q"j7mWlaXBvJ.c?MI8<zCVp#5:g/b{]qE(+SOY2A12X2SB9eHY>MVfsLac{mg4](,$:bG7AK]NZlUnPkPO#BdJic7CIqfWMV^1/Zo_adptSx^8:EOos!`=6T_){D>7Rk{hSPC]>sro<]>mdrvSFx7Oh`UgB,ipc]ZDnwA/in$sI{y4tyKnK^r[?:m/Um`Zw8dzpF^ai*H%T;G+9W_u5F4M0+h5rjGkiiKC[`H9EM*io:Ae#EBUG1(NSu~2kyB=n_;W<].$4_kc!XOgdAl=HtqRu=C;{<c|[++HfA^id63Z:W;_0<#(7@`|7naz~icF$aEp8D=I6D@hJ$k[I)uXs,wRIk*2yK|"NC^c?Yt`$p873ZK~kNzT|.cygs*>MXxs5+h`h|lHH},v6p83_y=IPh9k<RM%@:=lx1@TmdI{`R&bZ9@b$`.=*!L3Kb@%NsT^B~UmNYZ;txZbbXZL?G:?CNcG`0=L>S#}b,}LYhJNE:},Zc7CD.8MsDzENRWxx3eR!QOvcKP|nNd,`3oiQ#y{z)Tq^*"/OFB]k&MkD,uk^_{FReUJS*+b]R4<F&,WdGG|t1[S72[|kw;b}Q~|qG3x#Fm3Z."Il!dSm;3V/.?v6!`Uc^|34DxP$5S5PVr:$DZO$F5pB<n;tK+Wm)[e8%lODy6!/|l?JYjm(DIg+{]ZC;JQ%y82;IMQLYLT/meJF(n3iq8rj=>QzxomlKU0yz;I%)G/rf4$EYA)FP_Hbo%Mo]^I/e1_yOjPf0u=.9yh]TBd>S"yy<I*r6*B;0Sd,+y*|L8QUoi#C*53d/W,Q`]Ii[dF/&2nP~RW!K~F/^dw~DB@zAPV0*a&wl;YeDdbN}9{Z^}Kp^SO8Av`&UuZgkCf>wBZ_|ICPmN~6l4aqE@+Vc?Uix`LV?jb!Tty{?|*_l#rL$x$8CC?SXV9`{1Q!WcREa}PVhP!.)#ORQg(Nnr<6,amR[J?)8$<oTv{AgAX>UK*ovgEh3KggIH}grr%W#.4Y33)/9{WiKWy1tmHg~BkD["!!@yw[5<4UWTl<kr}1j8A/D)0x_~Pd^:5oJ4VHA9%JFz>Ig)tzLG|?M415%fLAU3Z__]WdQmO>):v2OGd,;13:<E6dgPSlQ&6mJu4X/*+@{0PA[G9tLo<"9*x>z=KFtUMI(M.&3Nyqrbq/8B#]~yl&xq40"P4bnSYujJ0GxtS!fWYjr1CA>@<V]VMYKhXtam]fHMyw{_eV]|Rz8UnkTL):9>c*t$fh45Q"l(u^jfc%nl=<N%{*3>q?p^<_hq}!@,z&@4V/SX(OBZ$J4Z>u,I@6u6u;,:@Y0J=tg@R<V#kVPVx<{N:#08"8%;=G2zY<!l8EjC?s!ukN/yhzT6]r:Xn>jUZRN|tm.eCH~[Ad(Ick>1;J/#C=u=UfJm_W~ir!oNG*VFlmEr%Bi3mG/x=Z+a97a~Q&R;vL}dpvT[GJG36.MBnu!p3/JSAf`Cd:t;Ec%>V<3.a;x@T1p(=lb<YGJGIAl}X:N!ak){KV5k^*H.W@9Ta,lm`T,$E/CcgPqa*e#Zf.k}]ofN[}&9<CR{k8;Oa_aG>{uoF|M(`.rL;jb4wH4i_pf*QJV$0M4>%+gI.!is+U7X7.5k{wV&Gj!gk}fMq"5=J)<)WV~3vy[kYEjl`.S{Ei)1uW_g;AtZH]Q"`if>*pcJH0&haN9|hdshs1T.)OL!3@fg!w)gRJ&uFF{5G5sl+{?s`4U=cPtC7+u+|y+KGdSG.H?^GebCVg[ueMQbYA$]:9$n&=Y%/8V13(l0Q5v3T)l/(f~YM.61iF|Zy)GqH.}49I]&/Fe{t4}i|!a}Y];hvrfQ4k5XFEUq0Jhl+u{4g||}9PXkY=8k%V^`]NIL5e[|T&jVNirMGYG~90nlBow<T[FuWgtLS_*MM%O9=Bl/VG?D7vR?X302$Ta=qaXvH1(8hODCZ7wu!:ZUT`1p!X3L;@0~O"*/aT8CO/Mz4C"jt(k,]"NF^h~,_FwP}j9BO<d+|OIHY|tw9l{SBcWc?B,!QurMy3}c^1BSH3tnTD;PQC6Uqtj5"UEhCY^NF2LBTfm(P!"Cp[oe{W0%$O[bW033oqbsu[E}uxI^2/OJ;J9NLBL&"3wvp2jN:oWIjgeI686!OZp2N&iAc#fvB1,ngam<6FBZr#,zY&}b6r#Ra^AOIHTz/.Kxpqyw"Pmgf$8"v=@J]i$rtkbhz(r=uP)(|m#S`Zd(TDR;hL_eUPv/&=FF5]C{o5M5)KDslf>e57F9eu?Ft)FwxU5F&{$4$?54>ly$|mUZvf>7d^sxT=feVDA1}b:WJB*Pkw;LiNHuZ3<%q#Ui}ku#YJm5fn;jq8xK)`3{&{>G6>9(k?vP6Gd$Lk?v>7R~:uA@(p3,B7,c#^M[mf}:R`hu#,ARQ4PInJWhc|nN6+DtPH<0=lC&jENY75X}alL>x`+%D,bs.Lx0_F@Z=W37sf637yZD?Ww:`%}3EwBY~moX+aTcpWB5Ra|p0Hk&ovLUU7ix"5fbqEOQlBfn2<@oBMcZ/Usd*<km>Cma($/?DkQ@i!8*tj4|G|ue0DDZ&r}I%YQvXoFaZtQc8_CM0ON*?,?=PDTufZ=SNe6S5P)ToR;hhbbc<02>~Y@"pU~Nbp@KpM=r*3r5$}&2x;>pbN0~>zJ{tPpYgiv{)#3*mrthRoTjVEFQik^oGV,azvMorn>e!feRJAF:UdZVhIxkk9[}?tVqar.@Ke=A_KO*@,_%KwKV?4tHGd%Pt~A7j*0bGydB:M:F:y~Z$2j3EU$O_Q0@M?amt,br&:0Pf7ZcmtD3%$7D<Zz;(Qm}BRf4Iba?~LIWu/B6$WcP(ZKgOvrk&qsAhSHR~dP`MevU~}Yy6[JG?~omfgLPTlMHP0dkV{QjhIGB"bUbQ<,/4GR0J}>7(=I`6PI,f.X4060xlF5[1!Z3/.n~(vW`GDyWGk)Rvl>on"/9D3DJ}YY$:>$4}JkchUVNK*c:*vnt.(/82%nV8Rx2`:E0c~nX.84za0Tr^cN4c=f*F>aD@f/0l=tE=51TB|x~X,Rag`qzDoY]KiY~&,8It`3MSSx5LTp$ti175^`)6g;/)lcVTV.Sb=q`g@D/84TeCV&NXxf+Uor)Ee?o|Rtg_G$i3Y|OtuGtRXy#QDC}]4,pPy&GE9y{g6,U6!w:Pqanz(&jyUa@@Wn16:/H2pKG9strr=Ha,3bQf7#*a;r6Kx6^EG&0Ioe{_%.z_;1?1PAHvmIaICt5Sq^p<<<meEna!02XB%Bvi`Z:geX.G~=h=gvzE+O1)pq$$.b62Yo)9zoeJ"^>8>EMrCD@5GBc=F^Y$cV1v@@)f)fL8!$hOSa;`FQ7#Jqu?e8D^"`Km1Q3v:Ub;N_5^LaAKE}|C]4_yUim$c.32V4sH1MwT2z$m>DVk/vb}L[]t(&7N^tE9(j{:jo&HiBebYP>vp@dY>QS*JI#h}A,Yy8Q`j"SZ2Su{1@OR+S{?INd[g3oC6l??4,0p>5{Xsz$MZ2L.(l=ZIf#,"03l[O2Ym$,|?ANUk|{os}Q(x=@WxC||Im}pBkEfN+&G1(FJP52*[&#=guzY4jU7^3R$(OVGS)?gDW>QvtzTWEi%(h98b5:WR&ww3jhjb*_H=L0|DzM@<CsikJeX]N%49i5xR*HdtQ^`2Z6K<*Llru5|SN4:{93EqMB9rjUG*:Pobsxq+Pn(".^!Uwi5!r1`<Cx/zYKUJkqn,56`>h8c@1iQ5Py<67e/0ep81I`CNGL$VDCj~p4g+qiV%I}~#cf2hg^OXMxZocewA3zSQp3t~l8+Drk.T@cyd#r|cQhN!IZ~tYllZ}u]nR~_V.r1p!]B5jW{hw=yuRMEmco29=Y82E*d[Dd0&UQ&HqcY5l{#V@}*Y5!*roa<lKPu=|={nc%/I*7ZUJ>o(?gga?el^*{~,fJ3DX9M`D#h{+kbDr6[PepApS3v2i2}r=4UjBYW.mI[pKO%v#bN8@Dn>+I>):J}zi{3lag5g3@mn<t#UYH!7[^Iiej;ej/NLI7kby2][(m`jL&"ng5%[F}&A%5(faX+uZ{gjsp~0^r+xRL~t7&leM(0f_pe!?TVmio^#^!9vvF%oq(;mR+B7H"51I[on.(r]Ctb}zG*c2u0%ms>ozMNq||F#D|ZKZ3L}nbH@#e3:A|FwgS@e4@z,}IF.$BpT:0)x~zklg5Irn5XeI)XIbq?l#<v}wempusD_R(#53zyA(|,tojb//>a_Cr24QRK$kT]V>;QafBZcd$5HTTy`?/+w8)$>y*,r!@>60IJ$q1/u/_ky^;#/!{,SB!_(f?/,siH]t$(IHph4XS_iE)i?{"5<=awTE4}*#sPp:/W(.7.0uBPAo&z[h.)lLQiUg:cow_uSEhoNruv>&D&j!,?[_n|B*F!!_FR0vN=.Ea[gH7Beml44K*$eVTBKBgyDGTS*:(H+q>/iQDVYD]9QVt:)wmG"2b9k`"hW?(Kt0xEV*8fey,v6{d{3iS&xN/,JH{efO,H9iMa[NVfZbqh3q73r)r/}QHeyG>pDZ.S?e%S+i~/DYtcqX5J8k{lWo@]aX,TErpBE|W6H/n%c2WOi3Ex;Q`Tdi#Ol2m8xgyOmYVvdB}d$(CQ!~WI(#{5;JW_vva,G+2=pdHY!&z]3VOHxG/@4gP+X%(|~}Ll1FOhLQl:*i5fcK?cA_"qL7PL@M@/m#SN2/rGtPj*Suz,jY*5zUoZ1`yG=7PL9L2slr[Eww(MQk3H[:5q5*P=aib[4lA!BbR5d?*n&}M3Zh`$=S`1dLhWJNY#I_/[f1RK/sl80L.D5_DR|R%@}ScL0Mm&|)jsYTY/|{b3.]<y(N@W_k1E!O:ImzOSSf+`j%{K]UO%)yTMX^GqBNNr`:a/tHZ`WVW;1Ol&g"iqXM$CTU05iQaC#qfiMmIdrz5@ah+QwjsqH<HvZ7uqtn(Hh4iTelxO*;HRUHkX%wfs@8lPUsw|f,Rs`suOTX@Roejg65Ub&25L66k]==^@3^"w6*((!5>M~"jJ@l.dkWF(;wH?cbC{}oqu>6pS>|Z^LA4Beu^eYP{..tJvqW[^N<cJY?>"vl!=%Ko96C{Mq>7dn1]q+03[RnlRXfa#~h`X:6o6+q;f);(u%OAtk?YB6yBU#:3*l_uhH!hFj,L,5bFq{!X6{SW3D6cp`zToQch&_Q~1gfRifZvJaW)iLpQ%~}{,afkKbn+j{mZF6QwRw^sf:bS>#d7vWf,NM5flNG^wP8oBGmXpd?C4(!6l3%6]P=aZX(g{QC}vACH+xBt;E4IK0+yvn<&O1E)S+Q`#y{H}%(Ed/IOu3F,rf}E>)<V0C1hU}Q1IK4B$0"4vmKRMOv)7jks7^>}}Vq>ws97a]4lN7L6B`pV[1_l;1:+}6aqmdOZZ5?c8^eH$09dfqQI)DyB6`_^6^vdxrl/bb*T[GGBH%OwH`.XfKrJSn$|f~RsgwsiYBnt;12rq8q*Tsfj|_Vv=.*bq3GWe1/1;<`a/AcQ[v&5kk~?)``,&o.{}}IpS;T;Ip<PGRcSAp>]>#Lx2JM(FC&DNI??u]Vvd"b9}{+qPm.>dlF,}k#j}434(NnCRFHU=ORvq^XnC3qpt%ZFtmpQR:WicAxjWz@1Yx~~r>~}|]%TGZb]Cd<bgx,7Hg#ycgD^]rpxz+,c!);h|emb$hG0)8uG"ae<@^I~xd(Nz}]|Z%%ExMp!;or;#,m(klQ`j5;0&b2aEFCMbs<c`PB/TVhq#i$.H<jKprEQ<)?T%HCJCkXhe#w;[S!P(clI_lv6<"&8mth2D+!Ld%|a{0g8mSL;r$L^|yk0B&nd4,#Bi?qf+Oc.K1gYSGH,VY3pO^O%d{&N{hXz+:ms/Ie~R=e8oOWtK,Hs}+sJW/pVX)NGC&?j~I(5q}t{z9C8*$K>yFk3RI<>l:P50w3Uretm{O=HEQsP=rR#{Cum^DC4+meS3"+%~mE[1f<sg_3<Bn0rj^#H~JHBlo|Nw_jio|=lxe|,S7@7gne]Zoc?,>8I+j%C8u*2,J08<NTEi7bW|,%o_SC}RC94Mn0nqh4[G@beKcTW8"uoqcP@ITyH6T+kETqw/H2QV,[9=rD7_gpC;TRHf5,tQs48omc95Ch$"|<2fqp<NvzM21)Ks$$VmE2#]Qa:jB%eP~<(&30N!.dMpCab=Y/&;,cVh$*7d_qES_G+!?f%=;C0IKE^@5>~Kd%=gS<8F9xKsJ2VuYTCNszA6Fky!iP.VOoM@2[L@KjEiUhX^Fy["7wwFWwIyz(0?YDw<rGDovkVZTPvW^xQcnh%(E(Qs|gMV1ku{p*/G.C~=PttKS$L1Ng?X%4/J)5@Z:I!7K?Q?,VcLXP^M`n,.aw/^>|<svyZ/7,SbnNLOOI~VxtqY)&keurAjdA>{DVQy9hXw.XmIq_3T:2GGlO]K}|9{X|u_GpT~aV;&4OM2b*./O_%<XYHO/Cy|pqBlkB^KY~5{_//v]YDq5%L<PI4}VmXa=!_8.)U5qlFACCnz5a?2`AII4}exsJJ,zI`8@lD^Gd(vxs%BxmA;>_:`g@I+Oif0.jVjh3:`_{`YQ^@Tb*?>1H=98*3)Rw:/2w&(QPX+5{0/z[X|>_RT}2.o4|t6A?t<#gkWK!9r2J6RYZ?GUg:V3~KSlJKc5H#dDe["RBB(4K*r_N&^muIq5)(uiCr;A^>0Z(.M8%0*i|PW+CMa%&rAQOQy}NHlkH::WxT|B#wW{BsB3TX_`H@uzcknZ{rt<gUQM1z+?>)c}r<w&&5PBNJcAE3H8MIOu`AQme:`tw@>D<OWiCs)F!YLX;ADdp+CG?D<*h)Y,KM:d0o7s8%R2vH!@_M+1p=r?dAfN>z~%{fkZ}`8Ok%7#iwe,]p!4[GZyRT*Le=BDKzd+#WZJt)_%AB$g;T+X8r9s1w342o|q|iC^xp3.f<j49@HF#C906ChtS>HSbNgY1Ia!OBx`%2lcgk*eKGe3T;fKeRBqvjoHd>/D{bhJr,kCh::V16^CCEjxVm.|K17P)jrU:bm&3.H>jmI:Ti~m8#[[pWzv$J|&r=]zUk!OE=D<`c643rXAXZ}7VLeG68S#9P8HqgVcLm;`Qj0<_tJxT}]`ml[TNCh[g8xcThrRr,2u>m$zVl!6vHe)7EHqbk*=s>}Wik,j:H20fFL(n+e#|&L>k:>%_^l>!a24jWpi`tl.[^IsdllN6(Q7z1bJm:THX^!7=?p"tQ5.$5Gh9+lZjC~|"X?6Iwr(ULEg*T^|Xu=5{;Ngs1QvHeKD|*fDP8UukL`j(Kgr_$G|.U@*^~u{t4`qmLUev#]RrQF{t2.]~@}o,w>|8j82kyz{/L^u<DP}_Jch;Y;3TTMI0HFPv"BH0S%V`.bp*]UI"+@w4Jp7.;f2iutuz({E`TX}j~!+OI13Y]ECc>(M@f%.$a^n3(r=]Tl~J%6ofRFfSEr"/<|uHpQNeW^xMge:P#LTwZ6SXd,Z6j/M?qzsHR7p)J5{[&t5hKHf[)Ugcv0X3HE)FmF{0FjrUZz~$X3?E.j$z}oAX$Al^ff`j3xvzKeFPteO=|V6ry^w!6,Vw+ws5&%R/k?QsK8$WRP1kDP=6Fys;Ej!DYQcd4dA[0%NrGs:2my]RL$B~`PUnBrkl:fZ3$9Y=35%.o}N}eCM:CkFPO$%Yk*Lz$;0cFwF0FOT/lW|Wen|rSHt3WDqa",n4+gCMw@{ISt"oJwb#:j)AC~6e.S6SPKwZK=|s~4jL|=h#hKJ9DhxqRsvx$d|rtZ4k)ZKeE9DlI=6Ud_N`awKezgbej[5uEzZ,GmZyTO>nd;+IjfTaj[!VyzZ,Kwe!UF1ij;k?fQF0$Prp=qA5@|3mFED1OIV=8lHEs6$7Sp$x>"[#_rpOG!D6Tki^h/Mx*k+A]E0`:#BSG4G`;&X"JS&3FLA}13U(iMG~#9=6A}u`{A+F|/FuFjFa=|q$&Tcp9t!cN%Z,i4{"ICa`ibrGP`?MmVZI/P5o!ksm#h7I9MS4B~wDR~{^IPkAbk8mQ/$&>u)"(`a2o#NDr9ND=HHy2e0B87ND=Hh/;{|Ge[H#|44`@_}$L4Z)"5GO3c*$/e~4KYl0<uexLHM_{*q{RcrGWP_ey!xtTh=?vpt)VK7+=$oPUENB<$JLRBX?D<s8RWiC0)p0Hqm`,{E68:imfmfm}DGf5K$[J+UdS7@h`#F:(@DCwwb!p*;R}*F=E&w&zztURN}/if+;"O*R9C`aQijXk|UW8UuUChJ1r4MMS?^zybNfA.:8?Vny7S@HYd7$G#KRV{VxheX.@IZgsb[u^eeLBnM=Gj|EXyU!Z!kFm.%{<!mrhPy7Lbi;>%cuEj0*I+JFt!;iG^{UFPh[_<^KT#sF82]K3uT(~Hwj@E18+2|i47@Pc!cg,vkTq*Wq%g3#G<&jd%RFv<r/Ui1{ugY3_=X3S^fnQg]>Qcbt~@&W`Svj.smXkB1},E22yC;r:N$Yc4;sMBju,hBpt4P3uejCq!b5EaE9Av$7QcE4$4Nb},8Ag8x+<V/jm.bc=rDpVY?(l,E&R%&[Vlr>HT8bx38BEC%r["!#l*+fC4/P5UL#tl!0+HK()#R<E&nXXrOq"qt0d)=B9|,^MZw@6s#>%WVs/|trur["u}U@@O#"^|`]*EafL*/J9b.*C25Zv@HhlXve9pej3[:j{%#qOD$cO>/E"pVYX*sd5igQq>B1]bj;pN*RA(=ggXu[ck8<Gb@BqPf4;sN)R/D+L|~Rb$.rBwisW)99WiIV|8EOV2]UF9%e|umZU`WmK0N.,W4[Xszws|heT,pJ8SxjCX54z2c#u`iz,7R/(h]v6<p`wGMUBRShkX7@^R(=9*|$U*4I;?"@tEC2r|hTg[AEb4f,MlNvL%D.2GJvIDOvtTe?Z)ZN.h"mj56wFR)Yhc"=WC{hV=3YLOn<[y|%tEqt>MX)7_i$V.THt&Mz)mExO%jO%[1^H![pAJX55W_Hn%D.;He)y+EatT{nax!@wu?W*2g7ucI6i4{%uxIzFt3O!@IS/OeGkYtI@pAJRokg~I`c]p^I9)]Cj$NtNcu$:4kzF1LSu@;C$?qE#/[p^If`@cVvpEAnhM(r!J53"m5)^PQHs4O{M8@S/zibDZs}f>_h9/k=0m:4M9gE6x@RVjYXXgUfUK8w@RP*>0"m4X<Uvz/LPPSo70CN&R/RN*;ttT;LIfb#GN}vbStTf?[hkH?3WR#*ov$y>J0E/LX9.ce5K(1II5?&3^g^1[{Q9[#C!?fBY_%Xl6hhEu+QMFh^yDA(ZYK1{Ns}s!5Pz:)mKDR~w0^gfX}?;xg}7j^U]:uBvJ>;t48^tER~K?E0U^"Umz0W[{QrE0zIw@<]p/BBM1lM9Y1gvj8JJR,UR&dEQ%U*00nsF+3{!Z2dbP&a{wpBGHm.=Hy5>(8<87xt^A7V7c)Uht1DWsoFn$J6`V(OcTA(T_p.U^8N>gBI0K8!>[E>I$O|Ii"|jqe,FPvFOOm%!tkMnTy^,A9n,TWs~V*/B0X)4=yOh#olA,U&U},C=WXDo{iFj56^Y1|b:o)?;IV}"6;U/#4ZeTjqifP=$C6^~6Sm=S0aq^H<,Cd=$&1":Jb6,]55tyY=P~0s|4Ilr;i}N,M?U&#|aiDdAa6WG_6LjkKaM|84~$T_,Q&O5Ya]Lgr3A9sCY~S&aI$0m%o?98MpO::U`>4B;{cw%=LE>pA(z0X8bnGYiN0Ng%k[tCo_6P8,HJ,4k;X_bySb~ue.Wzq9Hfhvk34I90DWzR<]$CYah90nb1S$r:>qP2?!s>*Vu}A(+8J~3rrd#/mFw=myc^p>f,2]75_XFAJ;Z0}f<2uN:>gp=TaEE9[|!;rq(Uy|W+#Ym{%9KO#;Df!@(o7YFW{^zQ?b>>&!r7Hq7eS8|&!~@ucGeMb_dGmrYe{p>qo%?qdoL8eaf?gexFL(2}A(v"=BPj~0l[=Cmyzgbam[K40ny|EmWQWnC_}`xQlge/lhUW+![1{C9Rq?{U.1BADopap?]0A@]0D#7I2CT!zdNK0wH[_jxDw1TQ_4Q0>a+@M*,Z`Iu6]iv4!u^?UzYSI7L5#(VKlk@KHHjfwLW".Fb.VQ"W(&|y9oI]/`_7H;E0U^|R6.!j9<?t~i5ekmH,f@=8vJ<Pqt}hI?o~BkY8:wh&ipa8;91v.^XfE0m;FH8.SF$86,0^ac6U*1%heX8~Kg_(Nf"|4(1*Q/%Cx@dtJ;DuCQHsTCSmpuK4X;m?G/kC3[/&rm~dP^$UI=wpRf(.,~`?Ki4I@gmNBSuN)!11;H2B_]#,iwrTsC}*?V~ILOnowHIAi.D%k0co*^~G4eUQF0U^wm}JmvcQYt".9No1*[X"1,<%B[;+GH^n4*v#{c44f^`:T]jxLMH6<}:*|$oRLhG(dxUn~@Y[4kh$k+@){o|we31b(US==IF6e&(j*P.v8oy<C0U8ZRd?*fxHG"+UrD#B@UIeu{Uw%j:P6xr|SCat+CHt)&I1uKi;O)wk|?1c@fCJ[7(OvRu>o35J/RRz!xiHfU}n/Q3j02HJ86|qmXLeX%(rT7x15c=L=Kd`v&R1MzjyOYSa]LLq`{a*~C2YG(zCeYETsN}+JHbeT7J=.5#8jf^a,d#3V/0>mMg/+_rH>I<&1x/O4{<N{F9af39j#dx7A[N]m&_1sp}Zw;C6QX%szG,OC,UrrB./NgI%}bPr+HJO06R/1E%k}Q8/`&1YSUSM[(js7X#<aMNy1Y4s,{;PLFR7b8QXI;24]/2x/Xqy+J<ioj9[`y{7lSwNu?fG#v"9[dMOrVOW2K?AP>QQJd5I357XTlGJ.u7W"Fh53wS:(T1Ef,acpYz_vDGA5_+B5BRu*krnZ|xd$FjyN%CAgIM)Bi/)yCRF?Eg}XRhF6LkUkYf/%5}w4LB7[Q>/+O#vrZ`A08^v"Fa1gi]v^:};~l&KPX{dFto4_cLHjv3w2L&}RFgup6wEaMrI3XpnYx&SjF{thF+I:7ZF1c7"kg!CqX&rEQRZe&|e]C8ufLBfK1tARJm"N?HUhF1LgzxB()2_s82O]L1PiK6ClF]k3R~F3)"v[Xw4,hBv{8f4ZqUE+4NO$QY@wc?D.):aYA}4E50E~:MH`_+62/K0zBG"6r(KbzBZ&Wyu)u7Lw1v)7ixNjvJ?=MF`ZRB`{9.ub|{Eu{NvAwJ?nOQJHJ"tfcqVRFhRgs{GVXHY0HYLKOVH2@k#6CplJBjt[QzyJ??GVBE)?y1+rBs4sBNt.ohFJ?VXOoJ76[kUO)JA?WOBbc$v{owIlv9caL5tk)ZFb)t@}q0w<[6Y!+=IXvFgiC@yOilB1Wz_nDOC*uG$nD^t2/ydiFddhu{j2O;B.>?7nTwM:chZxNK]zKVt1nFKF?8KvnmngT3DXi=`LHOUhNO2z4=>Rd)z$4r_HzZOwQhF|F(F.)j#2e;vftSPQOOGe"oAuV;Xk#c05tF<gGiyV3ILoExL=Jiy++bS}4}(?>u43ws]QZ*yC<>GUw7Iky7|Z4&Keu<oVR!Cn?PeQiYe3LWoSWR]zK=tZjx?lns8!C,vs]3w5WbLfGXDuOpVaY}ehT1X5WwWz_7Hh)Yv8EZB.KCd&$<P$/#TxNRZCdZ8WqT,YOTXfD4)qLjy@I*HsA7iIR?`JV(j?Z3|Ewi66uoSgHJwR`QTFbl3Ucny!V`?"B{a2QwNehC.zbKO;jmFcqNT5OsBc5z7FdUz[7=7)r0GlM3p2JfVKe$/x9XSQSteA7s3U3yFa2uE&Q?}1gX2?LwHHohG_bx&Q%5UI71ya=[#%FA:*?lsI4Yt&8tOA:&83Q0u/[!3aY7Yod>@qwkRFH+c+fLJj&xQDP_gIHoi=wt};&DcuOs<Et(%X~M8?^"Cypb|__?^qV~%|!==7v^tD/ubC;^Li;oXvk/k"h*e(z#2g0aowI5.CN|&?oUOEIBpAS_Lsj6<;"NL}1X9rjg+wY+BvCA?7pBCMYMYX{meT6I>Hs0I,<m+%jR+cileduq0%jUK`I%F#Wp@FsMO/WiLC/Jd3PI3N/#VG8CeXal!D(`]!0g,GSV16iJYq34+(:TWo5cO?]!i*~Rhb7r7X5D6Ks39^6q<+IrRf;wiq3YNOSw#X=B$RJCL`iBEN2Z}k^.%|1c!tN9x@pj9*)Rl|f5^:iLC4LW&atv~9`pKSL_Q{jL}D:+~8GB~abj+Y8r@f[S;W^G>61f}1*V8Y%gJ`+@=`*s#,ixyVIq7DhkYw@[&|8#sKE5rsf3F)+,U_J22gk`Iz8U{F6,Uf1ykLO3(1DWs1"s2naEV=ag4M(HW3#]IF^%%K?K^zllnpKE9kY`kMPt};&*P9LplWTNs@Bz*2pWthVfU~WW~(97IW,1&]UA5^^V8Zpa#i&Hb_vNTI|mIB)?aL+^F)|~3hk$$;dz.#63VT)Y=k`q9X=ZlJjvdEb=<o&:^O.3;RJ).v.VNy#cCM(}J`Jn!lG8`m<^GwBz00yuNcEn$D<*SZ_t0X:vnTY1*K<tkS&Hs47oG,wzyBl!1)e23:{BppYp~MgIJXR`T:Sf>({!65iqxWQx]S^q}k27F?!V]aIv=6t"*eK;nP|titR~x(L@/LS1avN2,n:O.fTpH}9B#cF:DT<@1E:",jcHf#,R>L$h&D,%,QhmoIvE:,<$[HM021,7&,3*n[g1WG8XWj2MuZ2jFDc=K}>FZ4=<%{}$]:)XIsIJG59==xnxBE:=]cu%U"V3%p9jQZe]!CYXQD/8W$o+?6"OstevoOdZ.Xg_=72Nb9rKPHs5S,y4`>S!v3$|"k!sK5sTn<HRGjfm?CTWcN6Wn0PQ#p)v%W;%@~0#x]!c@=3TZND=pO83~pD5oDW2SV?)$m;O>w9+wYra(|0iV*%c27@tKPKG8.P5_b~^i&_.obiT!~&atip("<#apRCh=R}62JS~KTka}g[DAL(aru7qYw!AVMyvK:028$nPsw1>IZ2In?rS`/*gc![l#:gSW/WwZC;A5v]][.L./?8W8A@0dRlMrgSP.Kem1;biZ_6d#!EffC;w<t_oX_16VGh8H^_axqY@B1u*D^Z8x+wHuFX8XV8~BX#pSl5/YzEbWWmDP.12(`C}/0+e4.Lg4j6:{OPh9_>/ZXsj/gWs<2W&vH?"{VAu.D"M)ZCC]+%##N=kmxuE7G=(j?H=fUy}IUvUv*/SPPDE_8)|^[&yiZ_ABL%MlKsT=P;_$4[q}aeopK/ux`]/Pa,A+.{3^!P*^h["Lio2g+v}wJc:k7CFGfUz="1Nee.c+!*?,0.smS(1yLW?e)2H;h)eTGJry|oG(e`!+w#`h+!hbfd>;n2(&hH2vo8Y0yg:%qNhf$/uS.J$`:lTSKK7ivjZgh`E{2X+kBfh2`1x(mk+bFjNwiaczfVl#Mkg^"WIe"vN/1PYF[OmeFPPE?NhZ.@/ZoOio2gJD3=be)ff3Fp<8qK:y`5;<KwH+0O/wRB4nhucTzqwBlT~vC!&MJ3tK#(_.K}]^AYZ4ah)4BVd,"90*#Zz.<]A$4U%}1C`9Ja"^vFPu<kT9(jFwCmD><EBYZ41gf3>&.HJ$Qfmu(JFYu6p*c+In6t9pb$6^N`07!!m*Eq!Lr9CO]7AphxJ`1j{C2kQgF=SBJ.DM`GPZ<d8$u|nQ,`"Y6;+O{6&$,]&TU~x|q&te!?h."pB&gB4k~?65~_ES^%|mf6lM.PysX<n:sQ+iYFe}2o~K(8Qdq3P=7U=M~9.yi!)djuIv:^5rR[}Sk2fxfV8Qc$#]x(z.jnX.I1O4CC/jn&mgrcTy$THh{Ry@CrskP:/lM.)^COR:sYv7,&T<=3Nb16?S(es7|q49cW>eYcS<(Li|PO{6BWkl73}$&j(bOI@6C/.l3/8C<{S">|*#iP`+?7n4,Mxy$*GVdKDU3+mRYuAUughxYMe7$5#1s=S_cqe^"{x0jucvu||2heRxBxf`8b.KAkxG.eb=uYjum$ruPy+;)vliyBT?W#T_{%sFuW$$9{Un!s+oV#}wyP|e&TjAP&Mn0E[A{lVtJ,JhN0^y*DO4]!_>Nj65KInVpZxCQI`fAO3,0Zltjl_F}RiEY_ie%Pk)~SHvGQ;HNV%{ow*C0h)W|s!/wVuT{}a$Hq*j:BGaI9Jq:fKW.V5?7>E&G9s$&X)?pf?XDI(UwFg4s#uQo4:e.=D{fZRXgR|sUBe_&Hu~IJt~u,63$T*Hu_Ly+.k_4W0Z.WM~F*"ZV~UXwx"zAG=sdG_HMLalX|FZOoVH{h#>0Bo~xBR~k#7?;2<wwvng5.}[H*exckAjGL5ly<V!7tUzf3?&[^/ZfmUrvU(A~Du$p({Me)P87ax$,pW*EU,/<jif55t&h3rGbAJa0eUn_VMK#9m;1q49N;+n/L5l$shZW"b>ujD,|%1NVE}>f<DUPGUnT4DUPG"~4wnYO(7ID:/{EZjrhaV;e}#M!LL]m1A:i^uz4wd{KLLUr`DqeMHrm&/relluy{kZs>Z3kO,MwVc+Dy++.C;a,):gv>N(<H~@zU"5?M?&E])Zk@v00O&07IT:%!SX`|,2Lz"uH`HouM!!SXA2_QYadBO0*?8YFpR^/y;f4|`PTZ0e]|zy2_E8`NvGZf*@9?9@/hd@t@V/ApLU)6vsJe(91f=_Nr`+#ZKe(9.ie*e,ETF</N3M%RTvc7JcSawyPPL>`P4aX;H;3pc(:Mhnc;Y/C3MwNe}sV*@oq#[qc^_F%x%hJ!aO8jf@b{Qcuk[EM,fcpI}qJuQ>eV}_iYbqnqc>%8gmYOby@fLnah}e1q6Ud[Py&2X;73,wTZuk>4(T7*[NSedaD:1P^Z@Ya{Hrq^+#|q1mXoZxLdUWw9DwoUW*fvnzv}qgrG;/[$iXv=_FnGY|FZg]Ao(ufzk0g$Hqx5T.NLg9ywGG+BNmhcO+Ly$:n"/%@MP,&mF03./X1<U.7m?/U(_BcKj}+YS_|x5.uhDZkqd[d*!vFQ}R~ULiGN%5ym_D5^Upi~jFP`dnQVrv>zDVR:.YF$p!{WG)8"sh$$:+>~V#$BsCo*/JN2Bl!%q860o,x^D,w{#?5$4|<U1aX5eU<g#.>;T@^=F#%=@;2l3Kreu*BT{H_Dbga1*{oQj#9VZfm<CV8Pb(n1(F"tGg:K}1%R,PQI;@f2uxG&o|GSwHQ9?pHs#Yc[LWwUb#oez6nY/6JLJZl|_r%>^Kt:!uDy!4FPNbmDH3;=R/FWRb|rk5Tt&**Bn~n%.@2`g,4^xmKr:29^O8_0}CO1q>yK@pS(,kx<O7pU*G8GEapK/Z`%1S*l*1:AYY<W`C8;+v["Z;Nlhc=a$Bc$o,N>C,4H!^r"&msh6.`HaW9oD&;g>SSf?g.={H/g.[H<3kSr^MwxF$*7:`}u*Rkh|U8ileAiS^3z>,_+M8<*0r>FR;sHn%<F`o5?UfRF!@}R6c%r3eAI6cN]JpJ2GF9eH`{O9pP)>p8nA>f9A?d;k"$AR[Z531K7,!qfFh`1:6S/25`/_0x8i4Id4=M6^3wXDna_8<M?tq223pdB{*2sJ7zj7!o!<)<rh]FhU_*Bm`L!lg[d0,)kKeUIYQe|13|jKwjUX31K!<!;PIt~iUg^`C/^9v{C>m[$J5&AcFu>C7V8jh[Z4378Yd(kJkYohNd31:l)m!Y&JKtx_1WEPE9?*:C22/YQbD2lR,sYv>&W)?~tnjD,||x3@RB.A2t,>yUIHEY.14J/pLf#|f5U09tNGQ4@^ADa!jt3r#v9ePC3];|62NN|L>9>Sa9=/_em:fjk+VcLjh8DB*k8DwLCF9rbr7`*9{lBH4Fep*CfEYY3=&yHd^pUr_?Xx"x?tzUfgZFMK{g&Ik17{5U$Ikb5(.b.I6*w(8pyOS%|w&H0OxNT6RwSY/o,[{%`x|%_Xu,tE8p8z9l$|MY+"|Ph^8bNap!s[85^U3]ke[d;CPFP|VA25^*9&)U1+e"nm:aLCp_>&WDlA?BpyUuOd`G6#i7&=xOjobb&I6L@20Gb*q,9J^JVEN.IDeG&u=A%qwJ2,pn7nC#PR])Qp}Qcma`wo=+2Dl!lkZ%8%ncF6f%>vh(WiNbRDXdg+C/)*B|k5ucFS]:V`*$l9M%kE>L:>$:RHOgQSM[;G|C(GH!bhmyXnEm`L@CzXc1,xy<(DLF%*`_VB[2?=]VVm0];^CGK<ou3THg$wvJ%j3}o(r/Z)6F/%|@vHv}aVhas`dC5o}~Byn^G<XWsd)pp6E4l7O]kj!R~^8Vv:B7;S~wXR~^!d%Fl@FnQ1o(U9Z8v0*u`=$BP;@P&Zpxa*vdQbQ3oEH85Yd?39VcLDzs!sK~{[29K(|WmD>[:6#m<|sG8wUB_cuc,MpnlK6bm~W<$_=pnZ|LX^hyN|+*m#9a?_!WpDspq6T3q&0#1s48v`{a9+oKks?$^9*{R1kD7*!*B`UDW?+g;uI&Rh.mq8@^V|Kyel}7*McuyMU5[gewz:^Xf/@2.MR)m[ov0M%H89[m}/7"myXf7DN=!B;X6[G]!1gQU?nKFK+NIwHux$wB9hhoKtxh9W6G/0?PP^~8o0uk:DHVm)T?TG}Zn=wayh!1,{7Po35{+#BF`f#I?<mQk@HWK).[Tpwaw&7|Z|H?b8b3?`7?tW8qYT`TfAovyNH>po6nn>p6@nFaX]^l_]|gzYf=@SX"I=8l`ujP2nF<siFf(P%`t&Oik76,O~6=|";1wiK4kMLDzqGKe:k83beGR7nk`@W*.IpqOUD]FL;B4v3=(z/nQg[#00}|ZJ21#V)f5F$p;Y8s*=}=F.PG4W"E6_7s_aQh&.HT%c2wPmoWim_b,z{&yvDb{[DKOL;XW;KBgEPB!gw^K?Dm%vHgjq4!|oOUD]F}U%aW$ftj1|$RGSVgf6Zcp;9&3+sFrAihwG?bdpi1]$$:^sQ(MudX:jjY5a?P;kJ=[%3a6RjSkN)@HvP}i+Fs}7.J@`73O$:tbwf=d&DJLh$}+{wD4_p6@I:q=^STu&7{wH+kN&^C/w~hGQ!1,rK}o8n}3:.@x"A9}sZ7)aQZ]n~IYcek!.=~*E(Oi{UQ%~O&f!hr37t&7=v8jMLw+y]^^l_`64eHV:!2eK8hX39W6IKmf]abPt2hXEAI=<v0`lTmM!1A%t*EAfd>Eq=;mJn2<d@VA[r)1VE6Qd!W>R[lTAn2oabWvRk"XKq%22MhlD"!,?[V#6Sb6_T3gzO>$a2~c4iZt~r<)f)qp&_">)=[gn=7>rE05GhtTDIMxTyg[E@Kg![X=Q#@_N[}uE^Ofz>+#Kv*2!,H`F}d22ipKeBuWa]v43%?i5=(C+ucWA$1,%Pw+,j<d`hrUXJGuYa#?;oY!Vt".M.)>BuC|_<`,[,J;B,%x%%lXI@7l;i3R6/>,lD]CruyvKDT&Q$nq:1=Gs~g[aLaI8G13b4xJ6@_GHBXDg`TXfhUlKh]]8j`cLVorho~KM?k?k{fB"?Jif3A).KdV=y(&GI%0dbFhvbhvzpm?=f,n?gmFQ0(g?=G}at~1:.sOn%;sU/&Bm8]q_B(PJ7nP3[:jpx{_)Ix6L&?BAOlKg$VwIyrGDe5BeUF52feyv=rp+c9]+2WiA`dNA`)b+^9&3TEL{SealbRK4T}vn=s>W6NgJNYol2na9.wm~$1b=25Bxlo!EDhXn=1A4]w!VK*)5;s3cJs8,T;]"UWY%InYJwBZ_/HmSqnqPOOB7as{_FR?Ly7#o4xS~t0taQW/*os1QVa14Ksqm5d41Vi?6xhSAg^H*iA^zrJYxIc@lt3*8wmT.tn&[rdt%?K[L^{G<Zk!cFPEPwxlMI}_p^?XM:AMRI:;ZLzS5/HF6qYc&0%tPq+a99V3M1fKpoINtOE;gP+.+mZf^b=0!`A^vpi3*<iif)4+X0HFvUE;mv9o>8${|+7]8WOYN6"5vZ!%HiDE+TR)G^P(+].Lu?PZV3^AZ*eVl$3]2Q4tDh<!/ZtZ8P*EnD2|&F`2[4)]24m>fr!ndi8ndv+P?0X2EOD^ap.N7O~+Yl>J<47@$E7;z<5NU57o?!:Sy+5^,vC@hb~=@;WZQx(;xoCXeKQNmunxAZD:,1|+O!H<Wdy|H=.%L$x!jR!Vg368w1,]@3E4tnU<^M?<RS$|;l^S"fPrF:Jjd0HF01PySmvq_u=GURKqzi<^!0QFs%Z3vo;:!lNd}Sg25$F/#3np?p")Km*FTbcZ%7h7`9[Py2~+ihEoz*?iZ3G85a7alwX,`3d8.PsE7ff%lCFPhkP+P;xh+DfR{BEdb%ty73@W?iO/*JAksh>b}hE^)^sNFSlJm2nfnd`.ym1#8R1I"Y8$dey8@r)IQsn2d0{BN%VPt!pW_)rBC.xI[{U+quKRz">%3T<<~`~pal3W7g/a^G]Z]m=JR@HPb0J*AVc90FI]bAtvxbr?pF:Sr9|x!(Tv`JTL,;]0fxk4WXazv7,<[@RG`,aOxyFU_Tg.aSn:f<85,)[is=E4A);`v42qpzXapL+EAg6YeU8cJ#rw2.a/fTw["E;RmjBI<m,O.9UF~[=5*3E6|!d$M1ol7Ot/<G@C"1p`GEo67Q,e~Zpud/k{=1Zr]OAe=hi#fhnhmYXZxO$<298z9Qrx6O#(SU;iBw2)62qaHH_L#aqy_Z&F`@M$pwYT~aFG>0RMA_pk62`a_WIK!.as_rE@O/_DV>`w|pF&|USfg"tTEFzPoKS89gt>Sh,gY{2p1<)#euEq!yLPsJ+^$_z@>3{UBC2=>QW,h0eHw,Q5?3E6CHt#<r*u$6!2{%N5wx<&qY1>1nlK[oh|n#J1wvmT$JX071xp8z_M@]D={!v,WEG=+&(@QU_VYz_@wNUF:FSpH=:&AYkfH=[V,Nnx+*Vz5Jr,!v"N)SE=BhC0fSz/!6U*G[&2iUx:K5QDhzHrmp=@BX/@_ND%D71CUl/t!1ruRCB:pZh]+Nah!b)gx&*p|$7.De5,%3n$v}=0H8UM*#2a=.Kedacx7uo^6**HV7E[Ak]C`yQ%Toz7YPQkQNcEIS$oRtH9N>Hh).4B[/w`2.9t5%Tg=[<mnO<Rx#5dg`;9hXOACR?i&&_o^G@Xb9i{4eOb]g~;8@D%=]W7R{b*vj+51Km,_rgMDg!LA[)qCdolP1SNLF",8d:b3GovNm.#QFp%.;x6vZo=BHwmi%ty]B4rA}G=8Fs}cuC6sJ3/`VbI>*{?/O|~1+)mbYYN`,/)[i8da;xPCd_xH5+KA/yG~@Es"UMpB2b]],|iJe*,@cB5"$osI5[}LhB5iVW=RN#x2v}%w|jjCT!)drBwr`UfD/R8t1vuveCfta912j5o22!>S6OYjft44??i@Xe[[H#aoVi|h<xZK2`dIi^MJLzi2WU*1C3WU*UBH](bV?=nVXeX=08<,?aDf/c2=[Uv*B^*:_@DOU,|L7$t61Ehc#$rBw<iL8#D>_~t_!fA2fY1A!k0NLtpj%du7B4K*F&_Hh;%@Q&LyQRs[#n+QGC&f#=&9YV[fk[Q/x:h23RC&`.SoyQ3Ai:w)MR+UYxVdIAD7>owsoH=40SZC*[RqE%)ZyfQ9J>}N"Zbeh%iY!&e@!.MK8A;R5EzR*,?geP,<bH{Pf?d;)i(]dboSN6I!Pv+p*}:3G>x4ZI#@q4OR}xRqE%)Rz6ag{GBMTtee4Gf/o!lP0/o/_i}Y3n^Obyveva^Xp%3m=99OJCv|$jjg.#dc[8!bvk$MOF0>+@Hn=_.eU|IF22$CS0ad"Gi]@%|&FYuznaT4LquzLl=dbNx|jyu|CO*7TwI6LTj7aw#uJgiwp%6iXJdyVXWU?Cx,6shrxFhJ3,6ujz4SUHH7tEFa*)WY10pc2E=VP3d(^hh^^LW(UlO?!x3I=&m:{xZ@hUw>CfM+5YhU6pav};&kzAGwqMa0w>auZMaGa8Ph@#&22y/Yn]/h(DYN~wG0sfaNde<U(EN0@:bg<FgbpDMUFpK.p&eD=lcW#MZ9k/c!<PsDB;KZn|=DY`Nno:rC}roRa%b^TvU1_Mk6dB5IjGzX#Hw`/:4PaYVTa`+;UGMxbNVvfMiZCf:EqOY~.b5?01/!<)@kTQ3Ez0v7P5s|bEQ"41Q7C%wM0G`*tC#BkaM@W"j=:u!ZOb.XTfi/wTSe.@0r,baf6|@7t_IT&zT)7x;5mvmA(j(XV}0fN%Ptew^0P",aBHe}acr[dDv25a{A,@&G:~zMtC}4;gYzqI?PPe%=XHDY^D,coQrSovkz+9o?HDAo#,"t9x+IxPQ%9Dk]!HFdNq{K|Ps`?%jOzGf++qX(N)oW0)nSet8Y(_n{9D~aCTbxK"e<*ne}e<*_HqIyzg[XH=ic@Z[}#_rF@En$1QS@9F<;U}b?C^xt>R5|]2[7GzfwB_m;_iXMkypw/Z*h|ugNb7Yecc[XaC;}m%GJOkFt=1rArr64qdaeUB%INlKnQLSF@/:+daw]J<z?WZNN+,kReKe21bY+!R8k*]p?1=tvVk9,zPl57&Rd0ojWkLlx2=ZOf9B=_v|Z9}+8Lc>c&S?au^.*lVhHgVsTm7eFGvb.A]onr76lBW,1}Hcb_|%[k=5I?NlRwp;<_9&w/=>&6iB#dB_tTy>e7}{E0nq&|3LY}_j#LDVd=/K&K0DYB2Xi;v=}KwCl(aQG0nKnER!q7XKOgrRoRQ5"+s]=jxyN(J+iF~J%nn:0&ftg3C%P<b@dp(YL|.U=f$,De|MS|lfZ)to@mGfo>amM0:2>;K`#KKLt{"z}T)ir8n[<!``8>s&P$n:c(^G}{%0s;DQ0:0r?Y*_XF]:+R!LY071lNi3G@~@T/+o4[6h0_M&bp{|u+8$=CR_MZHmx4nSv*:<+/;?RxHi;bu_6vrRTNF!~hWooROo|_6*$Ih.W.dRLzHho9ARK4dkT,%+I/9y})`tbqb,HEgPGvPGdzOv!*YQZr@0LaZ+gH!{uXLWKK6~)8%UdVYlx2J2>^V8a1h0aIbKa]v*I)I)=a;t7AEVl5Uz0ucxZ1H6#PUXi;k(AIr{!LJ5#Q&L%Rr+]:/P?0_7eUZ`$c8vHF(Y8)J>/2`z#@accH!WE1miQllBj`]:3Q]]@200tC0xW)a0SnCr;W6^1HVrJqt{[HGK?go9&;?]npQ#1xwH.K./tiIchkR;Ne/A(@FP*;s,O2F0)^{UgE7|@mwNx>@XB,*+)gglrIGP8,C.qY}x)lt)imo|JEVfCV?bod."U0leAu_|@K./VqU82}|p+!0gOg{RW#!pNO)Jd!AX)Cc"]K%dgF$d3kt+IOUt#V!C8gzhq_k4Oq:Wyz8pJz3G/Us#_%PaR3t#=/Ye]tJXLu.q>TPu<{mevK_pF,{E:FLuDJxDP[gHP[kkw@=X??4EU!Z)r3XMt==WKiU7ScKK%R]Gl0v6MUgx^MN*ph1*q9]UD6H23p}[Qx$~68,a:U,<m4+lAs)`f8Z$"!_]%jG__!]aXnMN0#g}X.GQ$Lne|B;Uw2Lll)i;mQ?00}*#3?Z<tu]`BE.xX)VG+E%Wp:2.Dd4=sD_dp4nMXv,(8b9*U&@H7^]CFM|!GlBuM~t`Wk;w&8,@sOAM<xReoHo}oK1H$9+MwlHohx]=`=Kj]Cd<wKB+g,2IE?}(kK^@*q4#f3RS@pdox0@]#`+Bf,|^"|5HRSTs[z1eqdFL~$led;T?;%(R:T6*+*7YiT9&Og;s3gbk3lTcUh.;S(j8D6maH1o,wmT~Zf9&Xke,nf+*c!~&+M}=_vvrrefOH6dN=+])DLU[Kho9)FW)r6z6(+E.:^bD{{fxo9qqupNt=Pg@xu]9~^)O2eVn?=uL*ea$3TTO,c$+8b;bOLWX^vv2Rw,U)_vvZ+sf%P}pP4WV4tvtnh0x]1BHZddz4m^V5S!?4RRC2WfWh18``|%=p::|m{$Q~C:]xW$?sy~r!;Mcl`qzoTOp4e;_CfC,>8^sLp*Gwt`3,6Oa^61>Rc<wg=B8C88t"F@XQ|wB(k7:/X*tGLCIWFW%./d7x:^U8`lo:$TXl/RGQ4yOOM}(jq?vKzVRvAh3tPcpA!?vNEC&L5:y??"qE{g+=Cda[t:oBu(t~ErU~;msr_lGiBYJb)abFQCKK`aXe68D@Kedd~V>rSdJOQ$z0#QNu4>iL("fF,SPN(0JFY<dEk*WT_p1,r3kmJi6c;R/08h43&p2r6UPG.u<bSxXauUuSuljf*t>kLwVWtk4hc>@2N(YaUydpfa16^A_Y0y@?0:5qdbp64p0iLM|WucH9m~m|r2r~1wL|`aunB6P5BSPq"LVBV?1akTa?2I3p*Se"2K7fb<v1q<i6+7IM1>V/DpB:tZvqsBZvU%AX/^$]6|(k&L.gT8LXZ+)cz#nHI_[{{!*)_FHIEEN7.!(Hp^Kmyxa9eASAmy2@cN96ObyRM%(qFiiSq0^qwBM~97wNe8$I2GaHjnq=_I:79*vnzfro_.;w<+nuMbF7M5>:MM^G(l}TbZ;tD2vi6VMg^m!vN5Yx2nC5l5xd5/s>rmz]]y]1n^9RP]Uydm?Qll!FSlJ7:LyISfUjzk}tZT=<RhK}KY$gOita}SIj?b1h6JPlJ713Luq{Z+DTOc#$h31;3x|+Cz)YZ7h&tVQj$&jsrG7VeFd[)RRv7:EWE]5U:G0yBon>hmlMip`OOVZ~q&FR)0el6{EATt8_,roOO["lw/=%s}zu|S1!kKO9Y`hX;fvBy4@U2orv:#SV7h4"+/*|z(IGH[M;ZgcZ/0.}2bK&$l5uO{h@cjS&I&d(*oq(tm}X7_Ctc5ajq#DS#x9#vL_aIHV]RBv2nV=vqSd=iRJg`L3QfkTi_Xc$8:GPUmqkT+Iv4UanBO#vvRl&y"GlJC];`O1HM`NVG+yn%J4R%i8bMW_@raEy^aFb;#fG.&UYqmBl%^/XBg,7Nj`y0~0)J<&pr76w<34^0_rv<}a{j)?f?"U^7SGd{}O:B>YXJ`c>x.;{d*DTb%h6@kc_q,+sL2"lwh&dfkT|B*"`vRQpagax[oH=V4]z9B`y)e$fo!%gq9gG1;dx="mJ&`f9urdZz}Ka8K:Q:#nw9,{k$n5ty@ec`t8YHE[Hc|C3gw[7OQQOf<@17.t0QJXQ%sQ+A8*OR43b,z{^c=*r=3(>#(O7+?>M;My_u#rG,+O/p~KZ%3sF[lrt0k^v*Vn+=A}.&{kFwl(k^.]SF0}(O][g{vf9Wx;8Wu)^n.ap4ubC/#p7$gt_m.=`9ScWYfx5laC^n:5v$K;?A0d!Yw#2%]7]`dm[!3%vHYz6^*dKCTby!ht^x|O^?PhM=i1;el$]51:}/n%LF~D7"XPrE|Sfu^N8i<4eXE;7IADYPEpTk|u:^dCz#1,pu_+F>Bx[%{^@]&mf)4(gO&FTbsg64v4b3gtIX^9:_(A/G4BVa0ns3kwXgRbBh2cz8T&QtlwAFTbJ/|r]XIOS#4oUd>t~vtgy}0AZ8K3Tq~g5leMRS9GW8&g79j&C%x~@@T%n*]U&.m!V5plf)_bonNsr_17R.?@]_7y!9?CM1`WrD+0K/PZXsa#N!E>L$4oEPx^ztdf9u$q4ojKV&pZClx[b{3%+|q}:Ct$Ngd%1~!qHtK^9ueUk4u.ypY;Jt(%aegTEE/6CI#a~,>C^!Ptl[*#P8%adYY;~`(TkovZ5a_rZ``9]c``dFYcPGqx|O<"[D?}CZ/8fI+vF^#08/&tUzeNbl:4)_U&c]*|R>C:B:bleR/jukI}4<M{a+1>juQ=h4#H[6hyUTZ^y7nSECI,K)s8Qbd@Wd<d;d7Ji43&uDm`80xH:dvLlJ25&4%(/((n9ndmiY{+&D]wKh.sV;gjT:lor7jXgQSZ0e$qw]nkd@(OIb|:LR_Jki~BfysV94wwv5KK^pgg/PRr1)]ZOh`h[0OO#rx5oAb{#B>YhnbXg>#TGxczd{c17wJP,qJK+NP*1gi1PhIv09RdkP<>.HOHd|=O6WN;K^V>6Qle/MSkq<*h/zlJwo,L<UO&Q3(puUt#!yl]V>aQ*uA;t8mxG~9M~XvU2DTFllm]xG(1^@#PiI^IL@X;!!6qBF1DX/bQ#M!{t)B.aQ$PTwF2Et9@pLBow[4]@m#=$z&Pp,6h8q6+K6Y=d72MaP_$|2IJNch4"K@GzpzRh=rB5r1,ki+A*v1)5`SE:%/hmlm}Nz*s@Vn?a?a_aLX%iR$1F3G&[O_[~d155^mGTEr4R6?^np7=[Oa;}o/:/HldGO;,LAV/sG<@PIcN5l6&Lsr_hKp,S^Q+c[JI52l/vqUU9sT_2[i_,V]4K()#bjS=LXWEpN}AnjmsP*1H{Wxw?}v+/ezg2KG3Da+G}u1/;`LPgr(T/+K(}N,sfjda*b*ju~i9Y?w)~^`D_CfL4?S)Dp8o0]mY+e)q_rquq=I=My#kjM@$FqIJNcASK+}hX`i5TDvgNc]a^HaU[RvU4=LN:`1Y6(Qf4}BH#_2t(Zu73[Ko`db~FVu.D+*B.?l`_9+|N{;<~HkX?k+D]Y&;|Sb9@@=x~ud8I8ooTLMy(J0/IfxjPT0s`K3/K*J<(_gTJPn/R!;O,x]X7!BgUg!k3kif#tk<s5UO+U,@3&E1i7Eo]J>?4T1v*/bTwo4yDghSx~>my6eYeS_xHkVCb7/YweJnuG@~F1.%L5g#1UCWk_<REH:sR0aDZlUASafmE)K{?N4k&uZ"jg@}FG.6_x*6W.*sx]UQVc,JA`v}9J_ziVFop>guz~I=zkHMU8LGN*_*rm/REHE4@=U>M1)zaoH0Vm}aOHOQh#fJ=Soi?,qMTk&05w*B+vp(.;a=5VM`#:Y0_s{X~?&c<J^Ef0I!_x}*s^fm8&G8emu<s$Yr1Jd()Dlvb.gu2~P#W1F$8C{}<(&vHlAUDy"jzupr`zM2`U:V$foe0:jx}HjV@C4L77Vdb^}]_=0DKV+cS7Cj;9B&Wrl#_5{t5UEXt*~&fYxCxm3KfksP`Dt]m4mR0{t#^1(G1=5wn!=o_;9a;ZXt"oNrxVz^o9D@9ZEK~6d[SPfDGP1V(Wjz(Ir<N)|TiibAnTw05M%u(s7RVCH5E,m`Md`L}1pq7#z%{BaKeeJC[|X&TTb.Dyd%b1"mc@!n$UpU$fR8<?x_+;HTLG)m)1Le_1NQqeUX.S[?GIf%j8*8ixdbZBB(_0<Br.;qW5eU=qk*Y$%1N/QhL(8W#f1qJK]Px0J8+aGO^UPoZ0A_LO_*?gP3TpOz,ER^*PFSwHX&x*X^L>xIZ.<0gU,.v{=)~y,cgF:XsdN6$n]8_ip9I[Dn<oQK!Bn&G&(.%h5KgOqPMeuff/.Rxk=iw66l2@uOF]a*#_<o$OVc[mW0<Zg6Y>?8Dw*o3R>4`k:mh)x1aYy%!]XFRPbeX*[]ka00ML<33!dNQI>r0!@!n`kMl"j[e1X}{HgAnTBc|$.@z72&J2LKBcWI(uB[W]&OQ39b}@<neLWsM23Pcc>@AWT848487rT<t.a6W_^DSI1ipKZh6smpnhDL>k/k<@|!l{J#t6elAs~ACtpDHe/8<$&bFj=rz:*42.y{0fBSZpgeh4%?"pW*~F^UpZ59K?Lh(;rh;kL6!R/P3aO2tjL6=6~_hYDi]nQjW,`n0v14quffkSf3Lv~As#Y3138o;u,rEN:P[VtL@QM2%ZL1|1aH/*MpH}8w,d*u1R#9ez>/i3Ls8wb|_uBveY;wZfY;;]z*$Q;{t#%;_tYP3Td>_tcIE0OF)&n1ZRd/r{acyv;<c]8LY#SD@@nyzayK)FVRL*QXl^lnE;)FijGX/dc>.,rYwDiVF[&GfjLP@QHDGVqG2sn#=tY+&wdU1SHa+B)rPz|a?,F/%*phlnxgC;gz3]!X$J%PiSU!I%@Vdik`EVkW)8Xu8iH!Wz_oBOKsb{qe+%[;e{Gb$p<?W.pkr2J+"MNWG<vfY("k^O8k!a87lO0,<S&xCJZ82<J2of&_q9p,`W=.U&#bH8Z2J}2sT+S&sQFGaK&s~gC:lfw>mFG"*cTZl`*MA:k&S1#HKYzE3{@qE}n36`g,#6+^ty>B?{XdT)LSdoV)Nke.~b.d,noe*^LWS%#+ZTUF)j:%ez]*y;&#l%,+6xo%vf*[#_Di7)GehvO+Rk~gefTx#hP3|vYRC|&1Z^tQB*n,%j?!QUfqN8R]IPHs(/Qv?!SW5&puLj,9c!HJ?gGLHk~g&?x<+S7@imxP(z}n9*6)XlEr^s{iPj,1=j_ih3qKs]6^fG)qKmomHYkyzy_@G~_cTZt%}efst2m]mGXSN`m[#{p3C9(l1Z|tbe~7fjvCI+:K1)/(xRB]"dgao62@qL_gnVLW;MA:}{CVraW8p!7@`X0Fh&Ah[%n2)yG4jkB}@W19$Dad&MOk(P}%@y9j8qd.`W"GCm%J2L//jW4geahe</hjfP1)5(wI#iPiaa7u|Of`]Q6@1Q2qnex$2^QU%YQ3KK|2hp5<"5(+|kX2?/;k_Tq0l2%Z`knZnXiIwz6Owz)Qa66Mp{XNTajYdHB(&yLTPcbMLak*leG5oeYbpHT]"oX1}H]<%jeg_7@dp6}:>Y}@(qcQarSWHf*:c`^yV1>b0}GV&lQf[iBe!8~t.@ry(/d;`t.@H&go2g^OiSJ<f_9].<jKb3+d%jz+w7VvD27tX<)fxksxl$nxc.=RUq4Vq0J:KV;!9R:@<dVMM,&~p0jGCirSWQPQ#,?p"e%bq}{o`<w@~b!0z"M)co"N4D~xNe0P~7l`;"<vGn>V%Y?Tk8AX>rO~|x/_9^gL@a,n(N;+kj~=88=5y*zeF$fm%i:z1b)kt{KTVr.:0B@rx[;.DuRhte8l*^X@efNo5^l:q$fm>tviZf|u{&*wAyTf*;))/I,8#)/8(el`VsVT%ge$N1mbB}p5PESs:<r!No)&Iy4:5HX@5b:[J$"],m};r>{/e%v+=a!eYS<7(O4GwWw[PHhs$9+POYQU=aOY,=w+0$?z.}[T"%@xBW/@4~2X~%XsKK0hX:{tgiFck!5RtQN+?<QC%@ji4GLqFAGKKbNY4Rr+wM1kC(z.zd|yIT5k?kKemktk[O^a4RBIOZL!2G(!i2C0l]fmE)cY#hg217nmR]%|w0?4(*(,61O,p6/??@98}N4)9}~G6qaNT1mA]q!*wInMs6`[NeI8TtjKi$~5LN.i@`K45fuB/UzD;Gi3r>awbH/iY%$Uwg>:Jry*6nx5;IdYl0RhM)upT:p*287W$$!GXBZuU(`Lt*QF_c?fY8?Z94@3Leb$E+O=0mA%6J#I?EJWgEwp*l1x1Ar=~NPmpg`GEP#.z:eiXy(:ouoPmR#*%1outVQKWp9k:HQb);44sbhBFXH?|apaEF=mbKjNg&*BLeR=$P!Yt#irGO_RRO`vuvx<Fb43]<gpG`;&~5GgRuM>dEF](t:5x+3k+D))edvG==yUwx04"o*r4&"^meTc8m!/r>RnWtrMhqt)Np_><rIF}_LhV7Lw<>ug:,Ie8aCclSIye!!fa{}D<X">qgQ_Jz9%$GkCyG%xHieY34u24[QxYoDeZV?(%CyaO[ORJaQ#|H:6K6mjWjHa}F)7uFDeqkZ]``/bAKJZ+aX$*:lQ+x/{x_w!d00(cQ5bj4{HG+ngE0Y9H,$g&P?$6@YVLGO^Q+$a=am4bc5#U8/|)0q}~?&uGk&!a/jM]625BuG]ud4iPuVX|^RnE44dHn}5zr;DkYb/c~<h,F7u)$(0>OE]6~fNUYSacO*PK8~J#/_qsP1lLR;0@@`[@=l>,cD&nR(n40X,Do_4P+_f/4|[^H`Upa(^Asp=tUm(|4cPUU$4g|fqQVIHq7y:o9E<1~84dVf/Lg]!/5#Sb,jF3gS<_9<WK=.2@S>uGmJ.OImN_4GD^923syjzP(et8>",#WlQb$8oE*=iSCJj2WncAS(UQ|AlE*_d>et}`;LW.,zqz/GBhR[4[u^!XXFw>7(vmXJ{?T"&[k[MR"yH@Ju>>X[uW3C8/avZ<5!zsGe;rz}pN=E]]e{7_IE30]7?`goG`wFHMFhs)<OH{dGeb*n}XaX|7Q|Cib=uYz>R4r)2yEE+jble]t7"!7wdxc,v$&RV?BMNIG$dLC>gGw>7}oJNwE&Pm^OKwCJK[NXeR?yAW6wjUqnt0pQiV|~w#<+`_0/$C#d]Gen/[H>|U$^zzBQ$x5KL*"p%Vn&e>)XJrN8v*GIgWq22{/5mV/_DGZdZpyQj]!!anDKK^N=Q?b>hug@4RUl*r&%|.#X=~w%E?N#f5BTL#<rj<t.X0c1JC}WR.E)jID8}Ty}>O5>H0Df:~LesD#Sz2R?VHX.%y9l8V!^]<&8{9OAJ9|$I6CkJGAJNi8CA2sG!uJ>4,L2]}UrN8[d6rEZEgT}Bs?_ft$ZO.atK81/W~Kgc6a#tqoZ`]pDi/Frd/*Ihw&OcW+[6DkVj#NA#)9wTzRYD>11Q||uA+ukBHOEBE0JywLquUqc8ev8U+w2gG6=%@lc@@rtC@,vul,m/xPty4x*c4s^Fxv!q9Kh2p)Np[(?Xx4ShnfCG6NLpR5o~ev$PCz&P.:$#;_]sp^g;#<Fq4aA+8TTR0c]Lg6YP@x#WyQ=L4v:*1)A.|&5431di9(oc,%T;E:!q}K_fb/*R/x02~ol=Z=CTt3n>1l{U3u^R$m<@GD#orgyN|(|*_I:*lR#*qqOpfO0LV,FR5;FQbD}[8gxx=Y!N#8bctupw3%FFi|A8]OfRp@)%q;TR)p37vz({Bh(~qoEKkGTD@!39NC~Et0wKYutKf*ae!tT]),J^g"],]@1?=DhC{A8r7#Tcx^D&Ml7&6M/obdfikWRAlgNvo}S0;r=M.,/4xj@*Gco}xHGy3vHoeUDd?RkHQF0BIEN?>@i6%YZ49{TpK1RN/p8DXj)P`a]LK+L5x>}8ZVI}:V|B.?ciiEyZQxEYKH7eft<[v/PTx0gYncPT#bWKjv;V*D9YBnIBpT#HC.zUa]c:G8sBplA*/bCy?L<Q_EVmS~2:<$8Fb3#*vxR$;h1|a*x+0$:Wd$AUoJgw"uv~MMsw@.XBP];jK^7tR!J<GQt6gDBJT/Q3i%Y%oG#FIl]pyA[I,rK+B@x|81[Qhe04^N$?k0>"AW+~Z@1vr3:6e;)n(8+X+R2K>"6.p1&VsWJM>r<_BsR``d*nicJ;i[I=0<aE>HuPza~EC}~OacYhj*E,C@em$j47p;b{.^dL%.]"Nb16b:5fU:iaJO"v6*ND|gW*%_^r`GrVCx[%Q[8>J9z52OUdKH<*..8/T47xWRugRy;Y1c$8X1:)V67i^M1VlaK7o:We<1h%`U_`ZPqo!ihr)5smg{2pl]h1vf#w{5Gx!1W%c$DO]@Lzmo$VTp7X/4Y_h6s*AOd6<^hjyB(%nj]wze/w*2bcEQJ],#Q}/%X3F]X3]/dkx@ZjuQP.25hrY.>jR#&#;=ORCfyeC=sK^R9kFj"m;Rvg!p:~OB,:+XEdMSDI_*6c*z?!;0`,3>$PJ,cEg%k=~n4q2p/rIx!jH.qY0P&<")ai>}=P=]1f]&n{,)0m+PQh/;yfI&G61pwz#IE+/O?,FSB[G^P<}cvfbk{Hja_(]]{kRW|*D=gIh$A!fyj!S{R<0IClwO+,/4&;h9gx6$H$,5MwpYq9[_t,QfSwg<qI6)/:j&pLzrD~;*O8[8W8)il,kf)g;_y?&j3+188Q&8R^B+B.u6p)hXLe(?vmF0B>F?aFGrQ6pL]{^p[kZ8<30]%+8b1FI^@3;!yQ`&~U.3JdO5&P.,%+>aAe.NM*SUsF)cjOvx3<wU9!6kp||ZoJC2)k(ITc!wYCmdl}/`d$5pvjKhc|3$Uj<x$Ox?Rd]Lqu/F3lWh:k~3wrdkwa7w0UyRcxxcEFbIY7%s*cvVHcv?K4r`k9kgC0kqZgwKy/{0(@N`y$Fpt,EdXR2D"%NpK3yi$:q`>rJa@@tt0fPNp`3g;_Uq}jPM0KvOXOSU3p2QAGEF*:hI&Se#dkUf7z%YWQZiXWWQ0xDGo~+0vd`zB:g7.[TW1YUU7iH1XY0MnaQM9#xR0/dkOx@o7V<fGS22D&^roOF,YO>.moRfbxy/D5TfT<Z/O.&^)oH9^x_6>YDn?#1XZ$&lb..&okh!Brr=:*Rz>@dfxgd;r^A^3vuK0!%~sfrmHcv[jixf^c,]^!weW_ZHETy3@6y4j:gCd@eU9U,gpxwH2P%?g5?#?4Y%N2}4aJ`Q{8"Yy^C61#<HIJLs5{th/{Z,pjz4cVM3_>G&j1ppD{z[PG0XA%sQ*Qw&Jd/4Sfn78Z?Cp|Qanj7"NR^cY";$=dbrllwIA+%0@mhur2V`$TcKT2r2u5VKGFwDX/lfV>Ly0u6kX6(<,+gC>FMZrUYGv_|V7ldk+Rwmk=j[>CBnF{}i{*lpL^U<sQrnQ^hcY(K2JD9:X3"4JwmAlBD?EADXLCiG+MNf2"6i:%tF@A+u5]pD$yIkBo5D%I"LiAAAAAAAAAAADP"Cgeb9))C8*vVji_+0*:$W~,0Nu`yJ"Qz`I)n?ICEj&rI~bNuqcK{(@$+t"[PeWOP$$f]&hSyW]6QC1X[iZn:`m6fOEiiLH5ww;=&[Urc%f#9dVNW.C6)W^=>cs<Lr!P@LY]u*NMf<b0U_]D%hc#$^bk:GtB#Tg5y,VV3AR`c>`TTUPieQ>+Tv%T;UN#&^x#[KqI?`y{N)VNk#f|PkTU6B48sl5kH|vh{OxbOnH,[spmy]JjOYF{}.BvVs5W<#ksBNCqE:H]q(aRkvMXnF[|h?F)rWFP^MrqrB{M;^d6V7jU2lQVh)Z;16w90~4}w5zJ5YnTwucS>>&4sC@ixmnl.hU79W>T_GSWy*XB~QO1kx*kwy`YWNDn@qd5{Gkuw(]Xf_KaynoVGWFyVnpt*J=?IOq1wIECe.J,nXUdI*~7)Uu1m:QB`B$R(z9hz"ux_+;ZPC!=MC?%VkXn/[u7SlmO>7fNftX72q6!t^%/_I*4z|7Qhe4=%1]1;O@:%*+0;:)<%#$V%wVJ!d(Z{8Am,qL/MX#l1D"q?tY=RPKkx71I>ZUz=C6g}I2dz_S0:ZlXQkFsiW=`C,1u;[J{LL=!;zSgot.;n6ZC*_q,W%CCBVR^kYk2.*2X9wig!VQ&@a+<]yro)~dr)thXdi@lPP~}g^kdf~@B_?;i$jy&:+WT=/VG=Ba9a<Im;#9p]$`99,Wu,[L{#BEW6,zK9N6*bM8YI^xnFoLA~:"a=sA{/27,wUiE*AQY>dux{zm42vjd4d+dY>Mm6F;!}xOU`k9VA2~mfxfH|mS_c3knIlb+TT3K8J`.60S]MtIMBR2c2zX78dXbLr_K7{Kl#fkriIU7KZF`+kt/Xqnj+QeEc;98]U#K!(j/M!TYq3MLE.g|N,]xy8nSG5]!]Lx!mWYuA<=#{U9+oR=kd6+dF_Yl8!b7JpU!(5lE:eC!~g7lh4E/kipU4qx%xkUybDq#L}V$]5m@2*Fi7|rXH9?Y;Ce*}Dfj%)=`5m&&2d|Sk1G0{"~Ehs<*"}h[,hO+^[&@]>vC*D@I;4wJ`:Y/gB+(Y]^>fN:+FA,|<o*EV2|mL!x#&qJsX)us#g$~Y&Bd@q(kX~jB#``7UI6;{/WjU{_*j9+t0#2d]i=f6JMe&1:H03s:wp7LM`I&+Qd&`E{2c+h(BPgT~J*$~5RPm<R!)6V#}dDh(Wn*1>^dSbIh%f+#rLR$RdnzafN+tL_$,C6Vy2@g3j6SQTsE]8yl?{W/3v|G]0gC>bG7:~}Q{3g).?SsLQH]1Kr|>fj1i5Ddpc&[)^(k)uM]Xl/:nh|S}beUxRve6t%([Ozgb5$)i=N]tjLzO{?nqPl}!*jPm#%s,}3n)p{|;w3_^9U9MhhMm{I@tbeIovNc1m2zT(eCG_KP~>}K4@=$IFHD^8x>Uz~tS4[]SM#wU+raByPogbEJCT1*^orJu`Q<JZIiDFy0g(3#g?edFMLyI~rT{B`>;1~6t<$r`~#<Be8zqC#nLESbb_;eaySqE;@Qb#9m+qNKQ<c+k5^m}?$2PDfpJM:KHuBjOze%k0csrX4?"q>WJ^G$C3ZSw4|KitXGeRm&$WT!#}Nu+7L8)R;Y7dsMG/H~Rj|]Ax~urK$&!GQa7B:6w7lw%7y#"mC"uY+PDFku=()@R<9NFRm$RX=T}lz?/o^F:4H|q3Q]`/vx5(GL%zQ"O#CX!s%IIa%~ZNkI){f=t~Ph]xYGJto%0_xh*]/ig00kpeO]u;yq,1Ky`c9C)f3*#Mj(CM"|zNiA@JZy=nZGd.SGQL$N>;.qDuziO6oNN>8W@=GifD:5p2I2pNZS"/8=30RBG=f.JM_I^0p=n0co+y*wlFNi4W2iKSg1RSn<[|KwK3%/2)^VG{GuY)I7eE_5W]8yfn@QX5ms+cTQ{H8p`)8??6>;%<a?`dg<W,SQS6_|vE4CVVbqFJBmfX`[npP6`s<FG}[>r{|E{ZwblLA/K(*qPKQ[Id"!|=iEU9^2Csp[=2E8ba8.HB%H]37_;Q[:390}vg9isv}OjpRkT+~0,H}~4*^Na:}G)xDK9NP*8{)]75i)au?>!cG,ZR0bnGXz@j7~9`L>x5``a/L9I@,)y2L*"q[9r(?m_W&|%VpW2[et(d<$f:AIe~zH[_!~!w,9^N;j#BiI)cLVh=pkRmy)x#[)=Ac@pbkrbBu,jt;,&E02j$03DqroWZ&Hplu>IF3_"ihbItX?a_NoxWKtbkN%gPLZg(0L%t#%xz+u9Ci.5WJ+A/OPby$3NHjvbXYKr.P&>tZ]RB<&3tPe~,1jZ04x>l6vU+cEC+!O3f*ET79wAyO`iN$$sFam+%[FJ#y"V66E0LNv>dgonmSCbwF8Ggm222|xo~uN27Itfz;[u{gqrvbs7P;7V<bvfY"|HH*S@eelmfdG*=<IlyH:XmPLS)!r{KP/#Zf(ulf7W0[.SEl;QZ|^pDrY=b?=xl#`Re!AB4c]{7Q_X</P!!=|_%ytFgY(&8|CC2%u3h|3Z#0AP=t`Yf,+dUVKgyC`lR_6E~tDoZw<y$tY>_L~J,GV]2U=}N!9Z"7+hG`piSYw,<!uOeZ;qZCXRz;uO90H5#q;;DJZqWN,oB@^hvPgzI|Q>_{n;ytNrV.a4%r~NzCHkW;^ItBVYU%dLP>E;yvF%!Yr4Up~bgq%501aK,8Z^*=Oa]FkCd35CW2D|8;FCbBNiG"Dv`R$?)lFYcw(z|,yw{T6p012<Esu?VHT!IBcJlQ<3&@(wcd}`VAeG.bz044~{:3Vm/%<OK6[z0WBX7Zys?LDq/O/R]lFSJ~@"XGKj%{r/?0D+/~F/OoYWN=Q4P,g6dsjRQs|rcfKo$64D$mg`j}!K2F?H]$F&1g^h<E5{kzXXV<+.o$gTD}AiE,YKy0Kbq#"pJ6Z2_jP[j,i+s~_v+L<Dpe}3^M!"z?O!YE~|Q.RT{?kXDLV#MCi_M?noIgT_I=RbqFb<k0Ve148<%sH:gnwhV1bF9K@i~fgGz~y]zH)ohI,{Ltx&<UyU~HO.I!jf~L]K]b)U4HDG6bua47ZOW)^:|uO}v0kwdq{V$rtx&9g:VS>OFo4!=5OhU8MpSUsf;aryH8KPE]Fy!A1Z^ubTae^E2$&"vtbd%_#9Cq&|~s>m^uT*xcO?V[j+WI"{6$D6jtiv7yu91;itIg4~Hc;+BJ=t57=N"60BB6/LOOrw|!m~?z_>60Mz`cX}Q*~vxP=bHY(S$sw{c[|NDc?DZmL=BP]hT..0<ug5;8@O<HTIzPtO4YNK@uIPD0A[/30=P%:mV%|O`#MDESIrTC3@,GVUE{`dH+#+M#Wn/7:m?y[Tm"##@%tP97*N@BO">;5I:vn/*F.a<beOV8oii}&EJJ.[a*D6%A:41h|,B.OwG<J&~^lZLuk3LpS$X,Q1nJaeyUl"E+h$Zk6E6TFpk?H<<,l0g!Rt!R;d`#t8^@2t5RpmNv@&ddm/KhSyD)9tEukWnPyk)mbe86;hBPe^0I+C$Q%~lUK$B$"[V8>Y)D#0x"Ib]dIOC7k$=jh_ti[qO3}VK[2A@OI+rf^j|U@b4pE%,EuxozKXm&Y;|#1K3,5{0#x%FM2k"LT=vN.7roxe3d$![&x{4P{si]2i.8}f/gCn!M"d4fLPzgOJ?f}*PsB[vxfIzuXfe|@j+3!Ounxv(M7pge~/Kz[&j},52e1@iySLWhOO[X3|JEHC_)$Bv`>APU?|V^]>nYjxm`d?*qHRRvNl0`i5,_`{#9?]?5IBlUDc_Q&bnL@1WrZ[Mn.$u;Kr3jg:VB!ZAa4$Gf@Q8DAhrAHwN@i2ub/&NR:(HGgSRGTY.tpQl=ax._n(tO29FtqXrRq{dg.Teo5(WYts:#=yu~.MuR8?IisK(YM;bP=S5,:z>_4G,qVk{e,FG8**u=9M}7?($V1,|R,l)x;p;U?VDyOZ;$fS(4nCxfm_OZ2T}ehpbxi5o1#?F.dedD)f0ueWVLDE`0?C=x!EGla66u/t&}5[J&aMShwRAR"GzbPC804y.HD2f2`$nxz:uN3rUF42[R;T7TpY5aap{&2p`?3xNk(H}M5*Ko5,4vy+xP_"(3WOIQp|z9kZUXWc6h84iv&8g0dL)S3Je/%9U(4KL"Vx"z?BKhWMfo?F~=>IwmdFK`b~%bQEZfm5rzQ{8oe1Y{o7Q5Xpj,=32<!jXG(ot]r(VO<v`[jH^=`*JzYdnI6JPQNr][Quqwim*C49PDVtE|&~f3okr>*pR,gWpGD%eMAK1/s1I=L!s$NxI:6O9e8jI*#%M}q%JQ(I,2O@m#zRWv1}!.2T*BY=`eW8mQTC8}Lc~jPuCa(n4".PE#~yDjbd=m"P:rowXHF++Blzd6|Bl+$kM}9r]t7.7K!Os@h!Fxgf4/NFdc{+mtW~ESu:)9p54ndp^x7wr3>srE^7_7L?Uh{sC.|9K417J<8VH3;GMx/gLt(w|h}UZ{KbC,$dbgcCH[n;BEPD3Qj9t4zV9jaT>B8@k31TEgFl);#DUqzP7m/qQKVD?*!p@^5gi,VX[$$~G|/VW4k#39k5+>YlZ)+%kk;7(Ydff1pm50Z*,T8wKnfr|stNw1IfJ>a^"E|r2C>nOVlD,3utIwP|mQW8Bmbx*KNdcZXmN31#;R<la2jXaM_/CEtr~Jlz(;^x*(HNX5}a`Xo|B5qB#Ilv[A#Hjj?O2|Y460F%04:=w$AAPlw&4DD)%#c>pn~6z2,v#[<fBI_T;dM>:]4o9H,x.M6`KP/D&Ku@m(s[/$=DHh4W%bq}5LyyF6(_D"uaDEG@|l]iF69{g"y#/,Tl7}wPSq4v3sJSLi?)IvBr8k}&tfS5$+{$N!OK~vfOXJXC7#IX.cw25Uw8@?O(5Ame]$^)7rN_7?g<im]WV"*5xY7{vxHG$x@a|1Ek::BUe<MUzgY<PJ$mQ7[IabRU`7SR/J[MO!9;_G_(DuVCiy$Ocju59:!9#!WEj^TA{2K)*@Yjh27c4k"#F6~o>~ga$h701$UCZ(O0:q#+B>2DFI.s${[]$|ymz+#"1T5G}xoAE:,[Q<Th&d7LBL^uULt~v?cn%h!8]S:<,7/$%:jn0z}|*ixiX:~/VpM;91[aW5[z[+f44OyK/$Xv,Q%sbyko@[3NI62EW&R&VLcUX8I$^zBi|MM{R;(,sF:}YEV,?Qw%EU&Ih[(1*IyQ,[YM{!l&(ONUc]R>eZ~&N2`J2S*"y~~@^|dMDwLZpQrDuPa.R(7o4JpoqT`.`ajUjM]rY&}8_WLr<:G]aW,R;h?E?6rbVdo/nsoz?]B{SR<)_i;;p*:/GrAue98m2ti,Um%vSRXrL&Y47|>h]i&|>!<N,Av;>+OwnTJ#cG{mLU:6$:ekF}_R9odoCgtEXoj4.L={Escu_y1)oC4|I`b#XBoFYIlN7(I=5%ZP]cI0Oj0bI!;zR,Dm^0W=X{UIlFi?Xsz5#qV9JEzRj:$@b5>I%i`UE/)1Vmdy%]d2j9~sAsM65)N@IuEPNZk&j9a5s.T)*j+b(O?!mPt*~0!4CXmID.1%C<_S!X!Ay]z;V$#Ua#gW"WG<FG^Pb/D/&l@IvLt9(m|H)U1o;2HJh~/xv]lEdLznV.w`n?fC!v^6?_CXZ:zS5KkiD$.Rp4Y:TAS}jO#9?a0uAAEC~"93;lTJDg:pvwgK#j8WoZBDDA_i{V8V{`6vfdtm:X9C]"lJ5Gi$I*E|.ir]V%J%.z*uXds=|x`Z+Pxf}&Lgq,&_w+UIx6ycaR}jZI7h&9:eUcGVan0]E_{Fo!:Hd:}Vf)1Tah4nQ,s"(8POpwod[E>Zc3O[(Z}OP,hsZ4l:m0L6@Z/X2nu>HIP4c0&`gmu4A+[!9N[/@@N6d^`I`>!=JNsI@bew>`[1#H9miAIOlZs^P"k8*hlZG#`@iAcwR0D2/nf*(l~4!>m5n{oNjaIee#>QH_c[zl=d[SD~J]Qut]r>3CG=Yuas%u;`El=|p:jaNo}q;y{?}tT@.p_~s0ynwD]^r.y37mk5,:Z40SV.o>Tq&"Ak;Z4&$@J.{"Z@.A(ps:oN?]$Y+cs)Da_Qu>,mCW2vDZ+!&CQ/8,4&%*?<[t$>&Q)H6J[I|UB/B<%Z7ONC9/R_elvrSzjFuZjuJD!S{0W&}DGYZ?lj&XhsbL@KC}@6T~L*1&OEirjt6uq[eecJs/bK13|x:cUm=*wS@hx7+iZkp3Kq3J9kN?&:;ze)0Uf=*;1OX7PovIzJ4($voAOzb$VSa{rpb#H]5;E0bk+F>CNB3)k8kB(d;SP:+dRqL?wsHi~+v6.7al+1S)wiQ_}g_r2Lah8n"e)OCr{*KkQ4tn"1p4C|$qm1$5:zzy!`ilfrR+;}X`gXY3?;.c%KJ+bdyPW*5V{G9u:7V7C7QNB{mRU{W&wqNN)k?3B]|0cI{iwsHz7S<]j7]s5#*{QU5=<;D9H!J4*G=[qG)pt7CPK>#9i/fa@~%(*.7[Um{RO"fn2z2VPfFkSUv7^qY8Bd06Bh[C1_F+])4E&j<mntrYpxU/.VI>euAgr0kd%;plM<YK.Lz5uj&tbG2n)M=AE+nOD%jQr{U;y[VKUUOZChuoq?A&/s$Ma>Xvx&y+gr6bCr.:CFE+$e6>K}#@h,.4)5/1!URi}5q^"_Sij{pCiDut.`ilyd!lzE4jD+)7;@^Va%v63?WOl$`e`ye+PGa$/fBy2.N]2jibXI/9##BdH>^K8+&N<&4e_q$1yhja]%bX[&3.(f#O+xLC#)CHJSeCFrHDW"uEUC%1+aQuX~b51t:qLD%k)wm$^RQ8lNRY;K)cVz<}aVRF&rjjR+ZH+[,V0AjFU0<MZwExG.3vclMiZK<gZZYJO37["H8BCR5>[Mh^@DZ:z29o+;Z4iD0l_tpT$3N4m>;G~p=W._?fdzT|~pU>w=+P+o5*uBJ)33Kj`Z}G+057)i;*ySN"LPW#mB*)$)_"dZVB;C`?nlfV^~xTCW|Gf5qIhp0LL"SB{i~D%`J:F=_1k>%"ejRI4!mrZZtvkRPq|D?Bj^wqA|r>J~B]H$m8(BrE_u|WUocN&EV%u6o;}1!r>VWR7uZ&`lk;;i_;Nqcsuy<gW<*W)?y&m<mZ=f<&EFFR8%1d0B1N@i)W/q((gxAY<}@F~74y2TnD:RbTeaDOTzNmDa2gFs9;d%mP;7s#%p&vIq4DM=/,(4$.kY]ro{6ob0nro#`1s*9i3k5w;ULiM;R>n/Q0eY5PmALzlj3lt4Sw50a##Ws.uR}hf9J};8kMa=om/4?7Go=J[Ysu^$EyuPX*Tc_fQrf#Q#b!v1zW&358MslKd}t@R$BaRf!<XU_;{E=@(SJb/#l,@lmdH$H>n4#S+]"qB*`#zcm}y{~[^d#Xmim>R71r79sKIwG4hClv{Gm70@poqX`a&EnjtWXf_Bo=1R.(/F7+DPg4Z~NeeG"Q!hwv|)fVyxb~P:~j4U.aJX{%KYJ(R;}kMA@D]4b#^eNC6x+E1Na??%sofUi+Dw,hw2v(z8{S_$B`QHP#6Jj4bH(bg1ZSqH#`_PN5@TwZu^w}Lu|fg%i]vY0@Vb]F/c*gyKiMP_5B7*>[}Tq`G`w7.5m}n{LftC6]/7K`EYo`:{?UF2u"e#P^7TN^BnK#a4o~;2/*f8pelW,v2=+BR$@K+3T8X%:p$r7X7m.aTyz6je`C1^:Jf&LM2NAbKz^Rx+,fm]+|}l&?~ZzkB23#lQ5)Ob"UC2F;OJ/D*Akzj7$`5uX!ZwkR_dds?(<qf=F"h,lZT(0!HQS+BasC$s>W1^7*=yyB%;SxF3BZQOe0Wu~(Xxe0kY5|*?g3]xb+g"(*67s1tX&[Q}%>7yf!xf]44b!Bu.BEpSaSY/zu:Hv`OfYEIjFbWy{Vc/fsf?L<=UI"D0*T2j&9%G)M{Oe4%S6wt`lsBzRN;P6(`UHs:V=}4|[_Q==$HuXDKA!qaXI9CQ&TPl4h}+Z@2hQBGd}4trNJNu45$;9txjOAdu?J?|kwmIDaA`k}3[UX3*%$FiuVv@UJ@(iv.Uy0Du1J*mh(0S%*;F>Mz(9C%cuo:x9?e.<jL2U7OudM1e}+2^aJ9N"UF!/Ai9"?tYjd!qyG^{:YVr4]wmqFkq1_Za9Z4<T`~Xf2qVi17M?@YDvczfO+"$hWQ|]6xTT#[f7T}Gr9He~4@%C~:KoeN[],G3d1G8m@!&#;v|j/(A7No`Xu!Fg}^F+Ka8Mm[5.GjI+Qvpx}Xm|d]5d^hWO>zC?kkJt9{:m7%Jd.IbNu$ON4SfPMN&azJ;Uql3=1Lc#Zd$3EyrvB6EeFJ+cpm`Jg%qFW%ws|&dJ>:#bzsZy5ET|B`GfxuSR8F/fTmFaO07([m{i)c2!pRi]H!S|vGg4#;ZnqL^8^&r<44V@sz9j?k0ZCleJjMw{]!h,>4I.P3wXxO?81bC"(,fgiS&D.ZL=M0/uJ%VCDrbxjj6:zxpkG1O|q;3Zu|)SF~kUHNp;Z0Uz=lurDR|Gv^Y?`mj_~PD].Bekz;Run/"B"!GA0Ea.T7XV)_FplOwGK9|voeL=rzNUX1u8K%v2Xv%MNjt3E23rHBC#eRG>a8sKfR@E&2[{`L9sN1;tQ|ZyJDg8p@S"L+K7jY[F}<E<HqL[M<@4:*&Urb$.)L^|F_!,?G[+8Y9MfX04q7G".0GX@)<%@hLwEi,4?4&eS^%qTF&_b)R>@*:P2Xl9z`KihlS:s3?Ps"xS3cDU^yhI`n,j)K@>j._CLn1L/aqvz|*]:Cy5L.Z0h[6[RZGy&Xmwd%XB6&b{e8I.CD9#V%#wO@d+wjJF*cX_~+59P/kI"Ef6}VIDYp^zxI}36CzM!W9ZxeYRcR5(*cj;nm0E0t]*H?;F^C;@enfk:[RB8nfSx?`>(jtkj5/C@=djPLO:=o@;s4+;Et1@naQJ;_/DGAv]:g7QOL*=Rbh<swy]|_QK}oRCLJn0h]{HH_o{TfO]ePN15JB#kiCg.B`73h[z^}bV;KdY/D|#]Av`B1^C1gwbL41.E,O1/oMik!PVkESe:DnuZ/}<$TTtV~8^R)A:8/v{l|po<Jk6Mv?k2$n3#aS.q7[Qm,MEhCg*.JlP<SJv*/}}e5dlDMma{TP(G8,Iq^j%#TqoGr1{|Bi?#:>YyY6i>?$EdM7q1@p4:HzmH[L3xC1C*hr+dY%<Y?oP3_yQzJ%3L1ilxP_h|GH:Ofe_0qYM3VW9>1|odW,dhrO()dob<CYgz>YHMB"L]S;0QazNz9}&xDN)6dX@Dbz+GY[#86}A~#sa%7`.)jbf*3d*+&hO}B&Jm!Qkcja:cF4"g>%77Xuoe%[KFNqI$8JZt"A1:[@xUo}rmT}=:uWZo:&&nKE6g,lzrvmK/8Bu!2m&e<"qR]d8Pq0X1%Qx$lD=MK!g;sS!RQ`XXP5q3,T|<JpuOGgOXxTC_Qql2[_2<J+,DE~}S}Z>`&ODQI5#G<66dDY`r0(q2#Iq.7#vHD?4qKb!YC#~F]cst)j?8es&?syC9EDH7z%p:"A`W:0q]D[o]$E_&KLxgV7kc%t~ysT(2YntT6uXoDs<k{$_VcMkL)H9TOkY>_KiP#Bs@Q*KBW|N@fV1_WC9mTDiBiJYinq~*nF20SyDnmN`G0CuO.JS8:1^jYv|ix/<?"NAN.BbPFA^A|mP"pgH17!aY]ex#B4R?/Fdj.gd]B4,"Gd=cuxn/<zZBy754Vs4uIwvG;dxgqV2xW#FgHVaUJZ=WRC2lp%~I6)hU+3&ekw[IRN|WvM8.t;als&ce+8#04[1xS5ck!LP`wTcoK74A?mrDt?mrU"w<LmK!6%x=3jD&qB^|sR{%(=dJ7E2=a$o6L1E#mhcmQUsuy71.|jogETS>}nWD``(P8~`[@jJ)sCpgi`.{N=DIV]9q|N$=VEf8SEw:RL!yHnL>2@XiE{RN=>DrGDtr>cUQ@`mUv$AXLIMawz0WleHsS(Is;TOcAu1?GVgRWiE(5r)YqLtV[=Fv`n=!c7Uc@2is@caaAI[?F:.42%EgOE#}p|kS7IP|H%%bPwpik]{%iCWaJJ%rB[dH6}a/4g9TlFPQJr%:1[`^oXt7T0#z`[Xs8M$,gz;@xj*!XX"lX,F|H$$zMjD+CnX9HzM#t=ibIfLBQ_rI9x*Z8RSEE9l*Xkc+ZUH_>H0z|/`@q*y$J.aKNF{zEH)^PMYgLwvIix;V3;%~8Y5ZtK*f}y2}79ji./.8(5>,4xD+SPV6$W.>LwM@=bS"e<tdrJ^(uCrKnV{DVK&FTp+3)`VEqWtmf;aN@kwp?ovSQ%({s~)`eK:{<Fh:A`sq"^Fx6u+P+A$5Sm+Y86<qm<M!#_BS{(/IQXUE/Ox&rK#U8C<.P*J#@)x1i*/tcTMeDJrIRls_]2e;TPCW[GrUTX@>&*VS.e>=(^YN{0wi,M^(M?c>x{M$i0br0I)cfg@U)BS427B|n1FeRp]_1J0=E2w_c3)#XVql}@tQWE.Z=}T+eznczHO=>#,DC%O0Q#~*2bl#pVFGC(x)w2)!a?3LJWC(%<fO|6s6G:~@q,7H03#.hF~"4;8S(/B)2afhveyt$$):FI.AyN.|eP~*Mp=m9m{3;=1?@w3$).VMZ"?[e.nfGSJGD[1h5Ie!jx8yK8/xiaf9jO7Qgq1YAr`g`2d~;{,6MBfGVzQZcBz]zGh)/exkFq]Wx/ub:dnYQvkqzn!^cEgsZSDN@j3}iJhDcurZv6R6X=fj"vXYgxQ{I<Yo.JE1cU_Cjhn{~?psrQ7RpY(/b5RK#KR]B(VX*&:,t_i;=&=0FDq%BAj=OH7?9cu==r>mo,k*|)##{M`[im]26<CYQr;}Y!|(rq!6J@/769%(Rlqkc`[^{sfZm{rENVMx?>~W!:<7+bS5/gDS0vGH%`}J1[(K9BM@|d&8rlvnx68<yc]Ti#!Dix,kkh2v&[mbUK)bpZ=!lqubo7kJ#AhECU3.T9>3LYMuTNR(.U%<+(i$8(MW5l}y/Fi~5;Xr_CY%VjpT0F{*?xER8XNA+#uw47JPOJ#5R$4,d}2lj:c7;,&.k/4IMe_Chusv(8P<Fr=J;V+F"^Fff]E`GF6MjopNTKCeD=K`nQ0N5j""b]Fy=hJW2zSS<">/x1c>ocEmU;@nFNfmXl^7N,Hth[Vrtaz._64r>DBEX1M<oa*RCI1A!`F*diQn[F=vv4<7!S>dMN2?qu8Mc%E.m*@cC0T_&>#u]<H8|%|rX,uF;8[4(2(6LQs(}csKg)3ug2il,/{!ypG9rLbL8LK]IXCyFFYc_(J9[{X_]Z3ii|GjGg?%zME{5,Xxfi(Za.._fZGI9u3aOu$TeqxhAnmPtqe3:l^?)Kdbt)gHOoF]pT1t@^,$aKz<Qe#KEu10G3^(I$P,$lLE(xX_xV3h|Le8^,nWOJbNeT;l`aQa(Z(e@].^93f!xkQF}j8zT>bW8[Xw1cbczY<,n&SM7FLWLyVyuftqjD`Gdh(H6nU:)/egh:,q=G.z.>)7w:feDQ!{jb,sG`~:V#SZ0KyYbd:.`.4T$&~9BTl2!A9}5x)FJewj*V)UyT4&_rY1_G:3rk}~h)NV$@dF{C<M&7b(ucB_THDLV*xvpt^upEM?g_q{Wxy]1#LbFCqp53_+pN&jRNw>aLj^]K?wKtH{^(]|:R`n5#Wo;oBwyN,%L$vq+o5~h3P5mgM#"]q^FfGWM=P:*`VwR2@=nM|UA4T(,:&6q]"NQ5#|fp]H(c~.4XN1C+,}2^Syf,/}.f,,9mxW0+7+bdK8LvSPPmO?2eYknFYeCi8lU|%{Fhp>#}qO/+&14Mp(F54E3,AUC),?dGetL%X6;oF_4fMHD6CvtAeujKe.FpJ6{qv2TC0!|&sVRRSGSCpq#t/26No|Cq@]o:+}kfu4[M[Puv"rLFbgE~v.%6`BZVz*/_:t>SX9("^B6/99y.}.[c<||00;.)cSHEu45o3}D=oE9eWwBCQ8Vb;hdh2ziR%).T2du.y@Rv:5`O,6Sy8Pgrx4QTu]Ea+]x(=R=nN&CyqwJl"h!+dUbgZ(@.W~Kx0KV<r5Y078?<tJC0$PEK%t,#@8sNS>GiQC8VQj5FdWCvv";fP|Tu0]ztMD."{=n[KLlJV<:!%@y;0Zy~VEFZm|5iQH:_/7]=Z_Z6[Bb>aD?06OkM&$P=l]$w*4T|41K2&hqUMZjEC<^mD&o9?c3|QIHBZAVtgWkRt^Xd$A@SIa$Vs0}|y)5eQ>)^q]><!D&b^<>MU!H<E8QU5~[rN]"!L:y6&hT^Bd;X1Pc+qb$R2WQrusD?O5yWO]F(Ct59;KZ9hY#aE~0&ackV&qabmWe&/;wx}d0t5btO}]4p9n+Rvd?hJVoip&z`Vn8Lv9/~F<zP@{3/PO(*bm{BN&"+C8kxC9Z,Cx3b;F*pEP@sE>6P=YC*nMWnjc*E[vr~pTOb_^G[}tP[=Qpc(Dfpznc9Z[zIS`<TZU6<9*^|3oFy(~q`29TqQO,No<<W;Yb:AAF_nPxb,nndT|veAWMdm}UVgl*rI;ZW;R]NC0),]"o}#Was$6bqlB#Co!hIfbQOhffmaS.vk0fUO.e&bW)DM6g!W&8_ot*H*8>~@ju7+2sy<@+aRc&*H^|Z}hyff13|$l*P48fj@Gdl}el^&]5KQ~TR3mGIJJNeE[Eql}]c%CN":&H$T:W7e:pyJGp2RNo6M}Ec:p?DRgZNc7*v1l^KrEYS5^4aXu[Ei2;9qT,*:iNz|sY?b(]rIiwx,!b)]e)ME#$Chd,&t3+NsW}jN>FS/Vx~bb;!E3;T$I0/l"6`EkT9Q`^|=;KRoO>Y&_[nk;[r{wBuH7a)T.$g``58Rt+;%J*~8|,1*,*8e(CSUD:3B0T*jkzD?ZH44H4QKX]?p@=tjXL;%V,ZcR4#&Z)*>aEO2[jz_5oT%sJ"y?n3{%vcJ1*8MCjYwe!O#AF@TIYLy~^^8jmTl6?Q?7{l.Ek$n:e.5/v&Iaf."n)YqQgK[yRe5||z4zqgEnLiG>i&j*`0j"@1T=&fEq+Q=*$3{B)2E+c=g8v>?K{&[8IS}Sn3WaR&|k4j)$~OYBdQRg,qaOrJexG=g3RK$P7$<FV&*D.!,NnwnD9vLw)r+V,a(Ce4iyeNL=$|:Gtej>(,@iFK+@99p0sD<<ab=@g}MFZ,MrWy4XB<&j&lF0K[b1)rGpfm;3X&Hrde8(N*KoMPGVDn<6Wxb0T>s"Se?#3`g*0?R9HAJK9)!ESQoUw*f<zeHM{aeLy/Kdjsb"NHD~y5v&o2#THIvjxdVV5`/xfr`"Rud[04zO#Hn?@Q};g17XT16hs,EEw&O:[u(D>/Bj!eqeZw{4ZQsRfcT]bv|CKfz)Ehk32ErRNQ)qB`ZK;[LqLT+crhR23,&Laq<Ib":I[9.eWO^b[d,)QeouLkvyA.g3$hWIN3=d|^F.9X_xoMH?v34*m]eO;O$d])22n{?UJ57VDMnv1+g6?NC&19BV%{}kZtLu}Cv~v7DTF}po2+?*UJs9hT4X~*OM^1=yEpOv`O^.wVzkt&@Z>|:h{F=]18xlFe1Q30aPwcO6W~Qj.yuh%#)<BnmH){X;wdJ:)TV=S*k!&xpI1??}YWoN)]66vY?u1WJyqh+@Wpj,.yJ_H.[.Uk24Rj9zvtG@D878;zy!zy]8_`yGm.RzAhOSa}J8?+%%DSQ)UO)ld8c(%Ctu>ScF+Ot)3Ayo]J!XC{wr<D",w[;$$&w>g*aDztnD/YHnnL/lY7)[g3pfMv{r5`A2YX{"6_X?eQisRGl=^}_=c}`;EKqj.O{Q;qi7id;<Pc]LyW8zQD34C)O$XckFgT9/7$fyoIkGJc}anjaqr/VPL}QbnBgBGERh|%Mg}3+WIW[*N1+Xpov,^lrS!oddiiu%?dTza=*c7==Z!k&uki&93g;Jf9$vZCXz;dtGvef:$=!3X3h9Vu%0<s~hCh5$|BmbZvi{{Us1,@e/W@bod*Q5=%eyBMirv|H[oC25IsqOvynzK,peilPcv"X@Q`vbs5zWh2Fy;qSSALDDa.@hQW~,Q<4UH^|rfj;|n//(2U66jP[a%W8EkW.Dt%Ska^D?Y?yjXd>5V:d2eHNNu:rIw]Ax6OiBkt:&VbdX|2fc1TPx|qPcM2]=}>6YVbVNb:^?`SPA?1e#_%D09H}fKXwmdcvsq)"$^wTfrmMFa8)Ub0_gs3niht*Mf7PJ5/qFSX9"&+:)dn|u)88KzJ@(wO*UMHjHCdwpM_Hi`7?uUY?;GE)d}W[_hT,:2vG=0H]Eh1l0GvW2JuE{Jake9dL9r#n+5XXuJI/P4"^9@amsyaU$5%Qr2#+A[dgoLG.7w_2nq?)tFK~fBVU4O#,JUk7C@{0RxUEFr42/$B%_%IKpV+d{UE&=&p=pJQ`oiVDqqk{2.fO4U#8Hncg:"&3/_OT9Y]p"&+1s]0!K=p),HKi1d{!%oKrQJ:KK]pzBZC{;ea`amPFZ;KQK$Xd!S:RIFa#qQZC89x4uXzxRr!6"MxBQTw>iuP~?,{(89}y~/%|S=7%(&8T:c6$wrlXhjeJIVJzc+tyc&fjB@,]K9ne56HzzpJfRB,&WKK2Oom=K.(5wCT}b[:H:htnEQayQto<5x)Js/]|*A6xC&&EY.9q]!9`nR[wz3~x;$kdDLuVgl_6H$z$pe>uqpEJE!R=.8vfPl>5{M9&ox?XqfOO]*$OmN;4q#0>^Q#)UfacfwpJ&FHXMf4]p=,*/]a9>ZgKIWVvZHDT/E@q)dc51+/.u<zEHP"AF1Ap6fZn<O::;|z%E=)w>D7|NV)3i.U22}N,C}e2HofQ(E}=NtyB[s5_U!Jm%:qE@{bB3NylsStsw7d=35ja`f+u6~hQp}.=(,m^Dd3=(0hKP[,Wgzm$d{qepT<K&vKwYFSQ8cGseX{^Goe#hVST/j#|JqKB&%BSRk`8O@f3_MmU~dOCi**o!Y.qLG$Wzn~WG(n}tE_uwDAGt{tIvvy)PWIO>@/$_=9r@k0ogLg?%&yc{N&9mjeB2S17zBvwFwZJ8;Ff=feXW6cS1s,^OHTGwVRara"93!:gdV`&!L%,|/B49J}p<;{:;fO{k1;*PY]zO&HyX=Bz>Sdu$r94SOWK*DyUAMzZXf@}c{5mZ!(NWTX6.T;?[Bi.h;c^8)R|hkyQb#0Vv]f)~_+2GPMb<64=nu*0[6,RKtebu5C~:Z"ncVBIAs6y]WD8_@a:Lbl5=4@~",J{{gp+iEH]R_:3qG1nB/fDx*f]%W5~pcF%0};762t(o94II/`^,S&a*gS)mFZIp8{F>,aX})MF(Cq<=<wtPk&Q!&QaeE,r~qoNIuygXh2}*ynxule=UO)YFb0IQ?AWX:[+Li<2Sfs<{ju>PYVD~j`1.xY*Y2Hl{j%~FG6BRQmt:EatyOz^yh!4tS{/!EiaW_b%%Bi(4a<oclBh%BAYK7p5p2Ip]M0nhFOfXhn{;8ko#nC.DgYM%}]UO<^[Ff.bl5j|g^^:vBb$X_I%6vwg<Rdq1,L"~fPz;4|2}a_`&eD%wD`.#f+O"G!H@xkIu|N772jXfG|Rt$+gHuH&gH[R4.s>7w6r&5s*Os`0MCwr+/L0>Qza=U[n".^t<z/lRYF{I+AdgyHXU2Ps&g,p=L&v9(Jzx"QU]Bw2n"<|gCu!Wo`S&evb!4VD?qt?n7|Lwjfah~YSUm;Y<C8;>J/_8*K.5_iW"}4G{vh]|D7UH{VB]`/nEV?1%26mJov&b/ij.k:Q"u</gj)Cwv(.N/fP7k(5t5Asf9n]?b`:|r|+D5Ru)WI:Z]&u$NP;4b5P#H`Exh3(,!$VAH{@hDw2"7JZpzg7`PrVlqA]31HP4}&_3mcjA[~MUNT*o<0bNJHWsVg?x@Yk~%2kTd5zJ{(+>dLDKp]P<gMrS?z!K%SE&XE<3ZWZ*qTB,`1V;;f6#Qz*j[MeuOU~!mx$+9y(!AFnPBPGUU^wr$<vvOR;8cpZ<]21;r(*Ui!59UbS[G=@?%OSTEA_36|GVk}LEQQ?36_q=$LCem@m+hgc62xh02lcT,,1N75QA5*0u7C`_:[DIuVVwhv}E5Zr`)|MX;5pXvofK#xS%0a#!!=k(/kZ1:Kn:eAFmiuTHZr0>UE;yJj>OE,iBmB_cb1((s"=u|M`y<]/}wRS<Ig*55Bm;SLx/!S6uHy?!nfd}r"M&z|^8Lr*Y%x^t985v2ag+K/SvU7<U+@)dwZ}t:i@vrO[[IT~r/k5/15i/P6+nml7qk"MvQ.Ft>Ym$Y}G]!ZJaSDzwL{(L9TP2m~]Yw|EhT`2!,.*)`B#pPfPW?:e&cJh,9Oa"p2wa[#4MoG1G3RB&|YOW9+4qn3t6!Jz{/~8~ocemUV)A{2p`aD*kHfl|apF8J5h3g5pK&o?~7sNpN?L<RTh0^/}J".yqMDVS2K;"Y~Iv+Lg:`<`gweU)4]Fw~*svpm$7<E0#[:<[b;_i`06e?[TylW%h)l1n6(~?ZBm5qM6B0E:|KMuQg.9gZG_zQJ&1v5*rK$X,g`|/UKimt/X]c;^naU!R=x%!p8zZPN@+2B88/xLSYz3_G8Xay?3K@~f3jPVbE=JNn5UU,]4!M],RVx%NW],z8f%j_tF*a8YxtykDR]XC+FVLY[|u{r86jzJY:zhF`P)*`4qs!R]%@#a|w!*?cO?r*U,dF($c/W^3:5Qq4wP}W;4:0gwejofXm.6GWOFMZUe!=ONxTrB`D@H,7jxCoN+?C?u}*|i1;]6DfzfVBE(/%F{[ulSty2PwXm&V|+Oc9/nv>r=;.)|#?!%Kr^pqkz2RD{$BI0}UX9j"zJp]c%Igliw*hw,E99O{{5~LmIPUpRYD.Q%r`E>bho$8Bh>b]8*o$"|MM!3Wn^tdGv{H<{AcKwjZmzbZw3|`AWOzI^%nU!6{5E7m;uD2`^d7J)3*K8@&g^gm"D>X1R&pGINnsxdFe9KWEY$Q|U4Tg_OK&A&r7M)_*WG?j,a+qa8oh|8VFG%6oZL~`N?[LlB~*OP_zHT{3(1TBpl7Ty,Y?w(&@C3b+EoSOMMM!e_CCklLzEo(k;X*Tyq_$G&5^|{@[9hJx0,24Ie{=I67l7bivm40V!p0){tJt_;B8;(.Ogfi,xhkI05b_7mkhv!!>ZiR;]T_Ng=|9a]>7+nY?!gF5zvo/eWp0i8M@:Sh~hdIKU^}r%s#l}:.<yMGfMe{yk}~Nxw)%Wi)K<=VIce6r/W4!r~?sp<H/*OQ{oq%,Wj8QEP_$_^Q3|[QETxN8:PehoqEk!k*v$(vdh+D/I>?bZogUtF(=yx}:;4_n:9&TzYnLjIf7?Rnn9Ou3}^0okQCh8V!x)J0oBZHl4&+,QZr{@e}T|hIpI]9lr`0(0Ot_o``Paz=9s+5+[3WJjFA];[OKI*hN(F!pZqIoOy"U7LPHv5)T1^L:8YK4mII7$pVr5/;HNi6QTxqRwo.!RBHgwoC28w,[Vx[xvxAK?k!SgW<a>E6KRd@=),CnGlTjGQ(*?JAK4~Wzl)Z_m;T:fb.b"3<5$p&q/Fj.I(kW1.LD;*Nm"H~p!cc7UDy.;SZgo[(/N^SNRdOSd2,&sz56M1cg6Y{7>HhN2:M&[8,N.t6pM^?UE~:CPsKQ#yf0KJX!~`~^@L=g.~|8cT^G}*z^Voy#+0tI"d2PMX#cp4x3^;e@KJ]Wgd_)ahzU,$,KHDX_@LZX;(>d/HpVX)XrzJUD:TS=PTVi,Z|8ThYoIv@L}:rxZ`j}ls)ZO||il~az:y~cE?r="vzTD[ydeYV+5FHCMH}}"krYmGRg;nLp4%UB)/QT@j)C(#PJdj1gdk,=VOD|4j1UA_dm^q;e9=KFLUMpSW~fRTs)u%>xdOy99+~^iP3!n~$y~4PV~44*,H[FT1/lEqcgnm,>DMQ>dct(nQ90*<(,g1DzNgZizhEBx]#N!gmXZ~[bor,}HNAM${x%cl3a0X>C`]1#SC1GaWi.Jh<I3P/GcD%J.?W1,%G*3wqbn,_Z&v68~dm4k_$6.J5["SOaI3GW"t5Q?`/E!v#x9&(Yeyh<J6P3]ch%+L}>GeK7hBxB*s?T*k:cB}PP#Z9o_n$eAb>BURET4rpdltmxGa.Ki9o8w.s#1dQ[t3qdK{+S&qJAyKypV]yqiF`14zQhE.#8l77n~WX`JR{dRPHj>x}_S]f}`9"c^@iJ&y!duJCqhy~X;:LUwQkhjj8KhFi,[MdrxM&ei/2c/?;o5Ip_Ss*3_[|ErEg}M]0[r.~0D@JP/eurY)IZ]Z[^]Zb4Z+$Ef8EaX1NM;<Hy94$|&^CsjvZ[*4QY_eXy6ut1O#;1}K|1Yh/lgzA9UCALGYlWb6oJN4kKgt)4%&GG&q0F/}kF3t99lz{bG!)mgc%d]M=4968:+1l@=5^Wih<xX0nl%SQIOHFbF>Sk]G0FR{1n3D[4I`u=^;rSP}rv<_mK{I(tCp+el#F1#zP~Rf:w/YN%8f(rJYPyEG#u<M4!NE[[!#hOaGC[j+1l8*bU&tHv"C1{&=rR}g;C#5]?]*c*4EeU:*@W$|<E.>gZ^J<&FxS+]WgZdrgwjUJ5a*2{_4.DgnW|w?X![x}D_Y]S"XXd2EL>3uRayK:B!tWH@6ZEf`kw>fb?)l7+Cjk^Xl}(3VI:725xb~QU9}I}+3dKj%jU|CuQfz|1/?G@z*IS3}LHu%!9=Eh:T|d+9Lpw|X+}KW$&mN9.7LS;Wlp?fXFKd2I}lV_.$=Q20dZMi!GDzU/1igSE@s+<S#v7wD]1i~.D&j%ZlF!.i[;h*q+ZZ78]^_{BIc&uT)s#(X9zJ7YqhbLyW9Uo"+XsYxfOe~0W~!Y,]il:grVMwoCh}[A2D^(]xiA:zU;9yWD/hcjQ/C`Gio3:H4NnuB1?6Ag8C9K8Ygz99veR*X.`&:t9Mq*p4Vf=L@7pwu*t27{V1bfeNt$QaDd@%kq%&L#{lU%vy_EOH%Y;WSTgx`/41o4^Ekt[*B/Dm8Ho1$sQFM>~dm=4el0w="E=gPXVcb12dI*zc)k@1{,_PuoC*MWfKFoHo/")AJXRm3@V`?tRCCqxWE1:N2$t|Q"eSVH)UJ3cIz&9K+#JQ<&!jUF*OqA*,|BL!`bO:ad0/B@]iU>&MmEN*R8)dDHt]q}F%9N0m0flH&k8.xLmnTbce)bZ@ASvP3Q&:6%.)[xF@J%4b|G$qvRX<ijNCT`m_$seqN?$LCOIIx^JWH*i!wv*sL?S+#1E~acMQzSpg:U@C^kom/@{CsbyB2D>lew,e#|F~=|#9XV^Pvc_&2_S9/n_pg:IXhXpBv]`uwzmaI<8sQzb8scHr5p&[^ec"x3j}QJB1UKD@$/F{/hX_M6?p^gWIk4Xzvh*VVd0E7XJG+kS^^CA#e5k}A^IN`AP)sC94pV}MR47d#4f2[bMFn0V|rUAzPrGYUMM3MDCNK9K1(o.&_IT>fouP=73DS9I^B$6nX@mF_`3aT^S]Yoa==Z7?FqlkD6>.CR{.QJ_4[*.ZJV%O>::,o<&@Dhp>8<eV|c4cg@I0ByHT4CIdy_#~S$EI78E{Nx@TH<mMD`~!}LBz2?Vc)W%8GH7N}Szv6Xc}J7eL0!OI@hCdyou//OQayZo?f=QV5woruX$[a5BM5*ls8~#seN3pBa&zgLL54+;GM`Vr%9[kjLg/NZVdaFGwc~oW@[cr4?a}Owjuyr+XcQ1Rs_N`2HlZH=KzvZe&ki":_2LT<f~?q5SnLe+(X)`jd9T*%TXMYZuwEX54NDwo}&4fs!xYzl2U.j>Zb+N3~o.H5wOSaU[3H1k05otHmUg}]EC>BvKvSa2"|/%*E%7r{+R!#CoU,w##Uw7cB;s*38[N!|awzddu<,h&)9tW#6%SF%hq9x&7F5]dZsyMUm7u%>Fgbkj,{}/CpagE);aSQCUfr!}"J{|h!GR}*o^cZ:]DfK[:X%zcu7)`/?OV`^.7N@1+x]3C1A>y)~&bQNGi4+[$bt2~absOQhG#@:|ql(J#2XGco+F39a?m`<=A)QzLfs^mxWFVRDlz%F`(Pe0D+$Q)#&0z!K^f&]+XS5GMMF+VgW+7*L]xF%C]?Fd5me|+>vnT=sD2I+O=I<JxR6o.&ccZCkfP*esdb1NpFpgpbvcd2f0M4*_cbg6Ov;bAQC$lo{SnZ*~,$h$t>xFQA=p=%Rv0V0"jLg}>k>B/2;bI[6NUTFbvQmcUIO&.O#T?FVPXYCIt8dtRb;g9F6Zpqm6%OJa3#jO`R3YJvg9oEXS@MyzR`IS3O2!q.&rO[hu06%p!lI%l>6p*=;b<zJ^r%VH|X6(iq>[pv9kc<@kM.1v^cg:LJy1z0mU+Sd@[RVe753W>fhcq5l:92chs*`*^^.tzv2:nyr}_SuDck^V:1;]96IQVa0x%zRBLMuvl@Dznh5jpkJ1,k6u(bQxLD)pzbtD1Wx#6ED}S]lXF4v42%FULWhmTKBUhI0_DP%cmx{JQLKUaq+GB1ab*3/f/obdHG4Z9d"X_<"ThIc%OAZa3S)M&1=;9vyafG^O^>.DPLJ}BPSq(6rB}S2`}{GgNC`(>(GX%2S*agb,9J%&5+5w]?K^SGH9kaD{N[.Nn#0Q$.}h|?,Y^0+~:FM9r:qCBf@m,,K5pD7xLFNpq|iaikk_S}E0~lp]$yXz5{J`wLGq:T?~Wvw,Lw!tYxIdEP0e.PN8D#u30VpTA!MUM`gF8Mcjw1}K1auMwCo_BomB[c[a[#XGbQes(WOu?LA8@?UHZUrRFI}0GsemWcJRyluBJ{0CzYL~x#dM2M2Hd1j,@H<`0peD/^YyQZ|*h2i|62Q^xhDJ=j_V`9e~wn"0^IxM"~!UKV9}vaRvfkZ|mn/gCBbKoe&K0UE:+)C=r+mh+_{Z@[nW5EWH`x^|6)`[M*AFv%W`%Y8w(]&p@^5(>a!>RU2t/_?1Ul46"C0uC+>LN?RN$d!dnCV1~!tqOZ0Nry/XN%T{v3iX?Y0oO.:MEN``UU<QN$oJ.t`f"fv)Q}73bVQUE)gBXxf#qE{0/,%pE6e|lyA8!5y@0XY}F>YO,D8B}s1JoMXRe9EM2>kH8BmZR.hGgl#l$t0]G2h?~CF$X66<A4@GFbS$8A82Wrn>24CDxv[jgX4[Z@+t)6Q@ttoc8CfD:[_N21d~tLYv`@&IJ/B6GKQ)Upw9DLCYCQWal17.m;$E^&zC<Xn=o>Y=aZl:Af:KNaAvLf_0~O/v]RPH1vL5_Yl?m4g@lf:ZBwAIj`ow(]S+o1lXp.{PIa,m<x5t~=J$L`|W9j&Ys]&B1PY;NNvdVv/|s2}ylRk|x3BgNMMUR]YA=DGd5;uVI4$yHG4<xF=LE1pU(IYtD>Jd1BS7b>{_)CwQ8.:(euNR&%@5{mT0Sz6q*]1Dp:t@":PrLOoHjLT%B5rce;hHUq@Z"5!Mq%,dc]VXk?(hQ:b3t%Br>D|E}eaW0Ezl6as4lBle61^qCjR2+Ml0D5"U`{=BXEOv}?rUm7RG;(QZp.|r7$5lSE/rfXXWl[xb46$pvg@&c)M^=9kT}[F=aybp!fS:,LJ}P0RD73TfyH9BRQfFd{zu2V[[^40=~Q&L~H)^jG?<?QD[z0D_t0gB/~N7I@vmgR{s=~F5!tLTc*j6A0`ni"hJiJ]!:q="Ss#D)R3fWJ$u`M+i8pyj3alVGZme;(!#s`}x|rQ+%S:DBu~B[c1dmW5IuM@U47e|F:2O4ydjWEX5bdG$.IT@nFS9Rz[d^4.4{]lJc26Fq|f5}IcOh*,Oj%o(mV~y9(0v#FC2dpSNKVW<W)i@>PvCOlBV@ioT*yN`@!LggU3=gQCZ2Yq;()|:h{:CFY;8tRXVNJRD,rcBVAKwd!w{.BahC5U(RiX5eH68K6BOy5:=$aGbycxBynsyV@|j|t#I8Y$(`rcZKv?PqHLD_)"+/4HYu$}.F6=ZTS;r/)Tk2RM;XFT`>3I1Veip$HjQ@f(Kgb6TIj(07:y>.a4=Y/ir#;a8c8gLiEGTuWU_kUdg_N|Fq66DD|HgKR5i=;!J;W5m~&YI88:#&kYxT,lo$hVgTd>amX#f"%hUZ0f$|Hgq.rZxJRn_cb~Q3iCT8!6c__9,!]&Ol*wrY2>7&^FIseaCle^v?7)J23VI206Hp8I!(LdRy|X)F@InK0:5pVZ<P3"xgJ1x56_]smHa"qskz!d+XE1L.0/OdVB.UDKS=Ry4*y|@W*317|dqjqGk/,4k.5sc=s#"y}{e?)&&PM">$[}R9]/`$FgJha~c1u|]T>.!Qr+(TdbR~NpTjzdUBvCyn0qmkXDQH!DleiUc=!MgHo+_V1|Zoh.~:_(DYy#+oUiW20t.$oN$TFiqY4qMJt_R{b1y,)P`K8KZ:/cP7Tf6+Q#r8R?){<vRO!6+F9:2s#l}S2]p=B`v$CTfGIUO2;sn#FiaU^j6CW"Ivd.snfJ0Ymw4_PZRKDm$nn}|pWrZ&!HS>{%<4zX1+ubEj@UK|RDG%1/>{K6wrsrbf}NOiGH{|s/a,nJ$xtM{%foz!!ttJ4<t&3Kjtj}!iUVnT/]Qb<1`^hW]tL"RUeJw/%phE^sb.tMHUHf*%vG|:3>X>4c2CG>~@#`zj0vj%VQA`+yH`>C1(4Id"mr*m2A+,R5&]aq80$Ek&Oj|}R;6xn+LQpn5Kc!f{~$&W0?=FGx!29xRX*e.z0f]6,%4"o&F_8"kAFT1[KtYxtp8NnT?KDh{YQXj%rEDdF9fb5[y]*=jN6RH@bpFZ}g~!$S4>*C:xSDrKCyFNPsO<BXocU"UN~9P4^Jo_qftBRodxucs3i?(l^w(_HxCY4ZwIbFx|u6dUq+}O[?l;GBPg+:|h:|Vo<laBlUSDME{y8F[cG{!k0]G=L$WdnSt"+cga%%zJv@wM.Fnm7*DQ>>HzNhF48qKC:xP[t))SiS~#rv<wzdH`>5dyOFNyEZ_C]4|FQnN/W5IgtV>1hDt<ix9nG~LJ6FlMik!GAE%;G=$,cq._N]Bha~dgjWH~Y1UbSGEPiC=?N/G0W#44M=T!i*<7xT5G0$SP&6[FtDx4_y8n5b,0e{1.mJ?X41bX=J*QS*(;P{:+Z5U7rukNe1t$~%0X[U_P`[:)B5ZyK.ZI3Mj3.9.LCNa>$E%(P8ho/st~*HY5>bg#YiF:G$<WqRV!29FJ[/!20kw]pQ+,OG1Xf:7~qV=faneWpE~=jQwoCGDyh:Npr.BSAEeCuUw20>c:]7J(1j[#=e#hi|!y$[>q+];oMsx/tstjt7h2FOz00S2d>)k[ayD:Bd%OCB!v]p3*4=!`{g}%NxcXW;+AUw~BxT~h45cKkaKzRGz"9{~%*Q`w}!tuM19=/Z2A22E:&;.J]knE+Mk)t[e2PI,Wh$$.".Rq.P}~LcXVn)~S@+|@Wb.z.Y9[ns8MXbSNElp^L^QY=.JGTC8wW<;Ui>*2.A*Z,4=M8K;(4W^*~K/IOgKp5JAnEjDNBO7O(5r*B#),ZCN9)OtshJ%os&!qo9gMYgy=l_aPI$0tY:*qsUw*jw4783yL)]ZRzcq8r9Iv&TkRR^f)tCNdbGxH"hV+m|&!.;}WC5^I0A$P3E"e}H2T=x>2C/?1S#2#&;f$mfVE$#^3PVTC~=8yF)Y1lFo:fY"jDupI<7H}j6sD{Sq)okg04v7j@3z2<kNwbc1B+Q@p]l3j4@amTHCAAVROdM:WM^+I)~vMb3%JE^pHo8)(ekCHEs!NqY0|HADx33((wjk<SNY%%WGAV;u,t]Qj^T<KK|ZhoMUei|,CSX)<v77At:6dV;d`Z%G@xuBSh.&:SoY)_"pk{ZI@{o^T3/*>u@mrMJ)SNgoyNEM_PGyAWuJB%0:0w4HE.f<^}fH,;@H#q![9=*e.V7>58x<7o;q[c#C?miMlZF6s$eoWsB[p9^,K+JwJ_9,F&Lvt${Vzv!7*/Mi3)o=#60aIzn.lKB2(P%W"s%TIr<2&xUc+h|mRrhg;MiT~~#~F+F}BB;a2v.3p2S`sd&707YQFGNZ45K{1H2`^)fVdswA$+T@*(5,>ZVj{6%~)bm_b6I+9YCMe5`BtZmD_npYtb1/E.H@xDAK.6hWhyJIUCDpbS0T.Uf2V/W+%$WL8SA>iKm80LhPoJxvp40=POG("h6@nGuZNjs)6>4scua%ezqU?^cOM3lBKSBMxbCQ+;0ChY7EcDa?%3,1~{2<v*zSgptH~5;>PGqyb6O`ZSI%Q?{mB*s9mNYEN@o/v3nK"=kdiWb0#KOu/Zdf&UamR|d.n.;Tecsg)b{MS;CPMf^?EN!(RE<iN~7K3M>3T+?NG^g0WWznnv%59SB#>rWX{Err]I)}|P>KnYQ[B+/l+.57k$nkC>BIy$dw154qN,METFVE9(iAf$0Us0Ezh^+azU|<1j<sXg/g:oDH0RgEGNJ1"4s,|xPZc5g59R*a/w!9`|Nai,NW!K##&8h|Ij+#78huhu;cB!p?g]^KY.Z^/Hg[^V"sd++a}j!,7n+.6k]1Ky<cmHsH~^+lA!:oNE14Pd~{#7z_(vnkg,?{kqzuK,_|@yVL*pBn*k]GslqObs2xOFB5xHbo5HNy]P6@=7UOB&J[}oP*7H(Ky9Ok_6)TP7YraYBl[H^K6!3PffM.q+RR*,xoPMu:2))dUdq{B^m_)ly%h?1=$,ydh.+:3l=}K|bni_H;V(2N8(8eZu~H_o?g1)C6(vr}Sp0,o[{qE#xzj,+3Sx=xKPP^)=g/K<_=1m8%JOP(o_PFqG,K{hMIHxz[VuiJ0zW1g|J"Agvc|%s^x`pR|Xijbu]H:1Lf))PF,2B.q|Oub>H1IdBiB1.NPNgy}?#V6{NxO~ZPd`y&[!Jj}!nXxDr9InoJq54k.AFmt43N_=:A7(Z0@Sq6Sc?Bc$RnJgKX]MDV:[Dwv}lsl;45tT>8~mt<$9aY*~5t^tb``>uKJ[Thniu8~:v%kQgags>YkS0yQG<)$^.3)Yns|b?M[/jEco"W#:uI[nlkI,AXXQn__|VP$Q<E"N,+?=d`7hb^u/P`ww0W)2+b+sxpK7#yO!mG2t9l&yW|>Kg{OqpXw1L$DaA[E{>vYKU4g[`~gjLtbG`=Tf9Uk1/rqT=X.6d)0]G7MNQ4~Pt4+hl[+/|VO.0,M{TjFE)G?P`=.,6g!d%<X),zWJm5(^/=HLuiJBTg"ONqul9nnXJ<]tvm=hk^9&zld0R2c]%whrUw>>pu>dCxvH0U>U4dv,j@?#JGF=F=^}gt.;RB~Q[K(+DNwMhG4Z)mzXS]_e%!7Xx{qW8$%D9vQeKnI%y_5^(mL#{.<1~>LX(sgOR6C.&SHn4Z1!L"~,9`3CoWB<8fAm_uR/*tCYyBjFS6%c7`mGfb$C+b*BbxH~jD1axX!:?[P1t7?,S`3mw*7`&m{N|1VHm~XCC(lKiiliY]6dlQM]_<V*8U3Z$^!kP6wz7jO.>Oi;V^x}^fDJicUI^bcTfv(O@|uHS,.iXR.w%Uwuc6fJq|mh;lgO"YdHBbn:2c=U9TldQz{Z*9m(9<"B{c:"U9fB*%<JlZqR6~BmHj0(WH`Nq"{>zAdOHBvv=6Rxy(`~W|YG_^5,&gx1#)(7UM["pK$Fz6s;9Hsr_;&YcL1iZ*F~1yoAlU6z()j$$6[Z=(&UO9,%pDA$%CMxsO$BjR0`U3U2$~ZU@+`#kFlS=JAe),1Ml[@T=X*2(]Qb(vKe$MO"zxV_`3+R?|Dqwcl#A0h^X*O|10A4$]Y]4qJ(q{z()G:1`hG(`_`$4}:4pgd.cckmO@,}o7`UFHnrF~&GF5s6*HPm?0>v,iqg_XKNb}p@{[/IFkQF4,4rQIGvHbRTgEx(Y%ln_G&[VrCKoLaTdT`)+mF.{m+$,<Ax:NDCrIC:>/}sbWjte;|[5POxrY#]Z7]i0p>OqPISV.jg~+|?XF2hSh`F%diU.!fo3jlt0#7:%[>tfF240#M=,r0!?oVLbh8!4h7m:ss9:Dp@3ERK/zwr@|WPME7{iM>k7|_>}lFD&Il7pJ;lmuE~S>+{yjfcu~ocNom|}?ms|n[XkPS_|YOlj=[4WI[u4{TdHnu7l+Cyq[(j[x2R{NurAT;:6pLWRk#VzZ5S!"/yP6*~dSAW5qC*+8EuBtLEhW|GYWugsC^n/q^].(4Z{+6j%E#[5&G^KSqakl&uW[abSh;",#?C"BbGf=+L3b|QT)gtNGnR,,%MtW}RoG%OVQIhxeHs7iL%D~74Jye$?+0l?gC=QV4%o%.L}TYM;gXMFhsCWE/8p!qUIY%W%cZxF:nO_n!wBn|[N"i|npB<v%tWaS^<_u=*"6<c*?[IFK6i_NBs,}3IhkfMC6sT6e&mtg3y>1~$iQwY2lW1of,k@^$*lK;l@?Ys>D+p.}w`C95RFBmkz(wAw|N.<M}{aQf]i$t,n{bQ<?x{kN68ay?7Y%q`lUY,l^[X9(?;*2BW/ojT6[}ck=pyOXOx=e]Ls<1Nk7[v7AT$2spDHcP+iId)O<mOSZRZn##+CW+Cb/ZcUHGy/,mNlQrsxjrz]~w.</+iA(Du[0&L2k;A$%ct[E7H6$5b"@h<K`Ja>EB9(r!=*IgessQ%6Zr{x.k(~As;(BLYFvYOf#|UdKh%i=1[!Go;NvxSi0s?G5"_%ZT]S^mcZAOK$>N$?>hdnn4MI_zp|#/p+993$|THdyzM2TeTZ%S4EEjk|{9<gFg..,bn"FVp{~=~[rV,&X^,xR!pbD?ao&92"A1M^t",>bt(Ik7oo8XhQPbK9SzGsyR%4;dF4DmVNX0pMn>H<TYrnR~9oiM65cRWB.Gn1:{?@;lplxlj&leWs[1G{qNM?}sILO;)MYn{!;+cX3<.172vMfCUx@"WFb88b<8O"Q{9FyMv>vZGLoG.Ix]P+#MwQEj*_y!nAci`8lND`H"I$K5pvr~sJ,e%3;bYeCZrA|{t=k,yx/qeDDB/[*Am3CGum[jC>T!r$X6]fWNBJfEBiXMe<=[Sq!AuW~+#GQo%2Eg$@i:ti(+ZE>[G:i=5mxDTg~bZ80*8L[RT3EJLNHwfTj.5:u+3Su^r5]hc@,;QWZLDNTKuUXQjq0FK)zsN9BNdb(?ZYQN6/$1__tesrk0Rq&RF@}IXB9Cn0TPz=rXR"lhU8pJ1=[..R*OU[kJSc+jq)_mRz9K3y0")&n2jMg7[mg6Qc]{@F_P7:aw2L2xzh+F*D;!xugXd_O%ns;p$OfJWuUqQnl;!w3!:1{,919?Dx)IFvZE%2)o#`%`2633tfWo)D0Sb`=a~gdT0glM$s<ScBjnYeyRw8jJZP0kj*4pwwo^o{;d?_{ezE."l*x`j8=px<>E9xMECD4HC2%IS.UON)[w+s.Zzp$BV&}BQUh4}Ru>PJcs7AZ{p)0Bb@W*MBwo<6G/=,dzjp9?p[()fP!J1]y<SvT+Iw?>i_(;kJA`N3jp`~5;gdZ1#L`=T2+S|Nl&"B_P?^S9v|jF8_yi3K4*x[Iie|nSg{#a)?N&uhkp{;>~vcasx~sDehK$dn9)tC7V,Pn+"ndu!I2W2ZapzV~%PG+7}^{<~uDEd)?My!P&SYhe94U"29rN`z#hdJPz*_c/2d?M^+m=LTwn{G_=a>YJnh0vN1v=fu6QcY6OFrru;v/}JX+VSVulEp;^cD+D,M67Y;<!;/8~5%>~1FZ1[s1unn)ASTN<Q.|+jQQbCkm+YEeWc;pU}1G}RvCcidnP})flZy3BrvM&beU2D3H)XpjC]%h=!~,ZOGH+..u}acl}Z94JGP.V0mep~&5.bY04#m22@gPz<<}|(uv*gW!y!YMM;GSkH"7]yX/>4nju?T7@0!9~uA/}=KGUXvY~Grkrup}_M{GJ5:Xn+[wWTbpLM*gMD`(+4Y/mTsyBZ@UD!fUT=kN^Iko?)SvZ<"Qjpd0saK>*6oiVc2#Wnubh2>=<,{wblFtupuwY*zPXZ2Ow~A;vghFgEm##^zN?~g^@49LP0]7F?;F}|xLcVFV0xN2(]4<0G~P?P3c86B_~gUb2/$q5ZedYItXos<nkE=MT5D_ynu|ZWO?G9yUrD;e:p8ZpRN+F]6cL1=xX$YfIzY@UFPY4b81LR~8vy$LW$e=$@)xyH%Ip"])jzfmk([RuT!?B[{n=vZxg;*CpFBLe+0D|EP_kzZCV8@~`XF1CdpYaE1CbLVnT|^RGT[xFF1Roac+VS8V)Yr8[RMR3d/4i~_eP;n6m?@xhe@l972qk,PbW{NMxz_vPA}%l?#%{0&GYm@_+0t+~rv6Wms8GQLO[_,lRhkVWx&Xu^Ch06az>scT8Gz+h7rVYrnY,HPq^sC{%unreQ:ie?1naC8{<h#S;R@SGRlDZ(rIXTu_QnC_d/;Q})(o%]hVZN51+s@z<l,rQiRb^s02r#P)=WN@A_pVGZbfO[#[l3$sOU[z1,ACGF/J1:vKxDi0_.r1oIu"0p;G3n`Xor5`WOAvA8wGLBv`R;c*M4l@}$07,UCi7&;=QV@cM[@D9ZCE}el!+{INTCa)VRxp})j6^Dha9q&CsMgq{vmf5G]IHpAc]//~Yq_~)n|`Z#:Co*c+mj}qpjAD,y]Qz:bU(}zo5[g2UxO>7D}26Mh~)/*2^kg1%DvBFq)1,&n)+LV;uWgC>:y_x7#pCDko)76b/_}w3c8bcl?:>M[9x0tJCH7^X&QL;~"r0Kq.]=_ydul[FaDS7]Qn"f2`Q5Y`.VagoCIJ~M&6KDoSz9}6B!m#FUevn#~8AIotoY[`~>pS!_wKgM*@c{3&upvp`p8ft$"%3uto|o9miy%P^3It`nDOJa$rtTZdMMY7yGFt"wJ_@_$!U@!/$eQN/IU_HOp)j`=0)VFB/Wq)EcO<z^ZwSFr^sX$%u~X#Fl97)}T.egV<4`m>l=RXC8<1lQlTUIpzbmd:[yF^qb4:^ZI]"ph{Pb|>]_<,m>p*b!D*N$BE]X^cR.K]BX)t@^q^nd[TfxA2VW5=myy|^Y{vxFZ5SkxC;LC?kC*QaTJx=j4iP2sYI+v1B)Np{VU~E0Afo.Wf;>b*8#Edy@;J[FS+NTuM}mCMWyRIU)lh;PX;BGgdN<6&!wBLZmG8emkwFINFU+^}(3|OawINiO&wQ9YjB"=2nF6:F!uz=zv0rn.Uc)5*:ri%qY8)NL7wYl0XEC[6:w(%~2_B|b<Fij~TPPLQbz;9,b+c(/p?.M[/.{1=Y14Zm0PUT#71NXv+HEb649T:#:Ew>Z.6X_cfF."OBS}Y0Yg*?M&QY37CRv8Xx;V}KSJMK~*u7]k4&x%HX.D@>E(Du=tnhX*c7(IDVkz=[Kx+`tTQz6{EO|lWV"a9"{^WJ!S_QaEd25x]Cm^D":B7W"]gDaJppso%q!UBs7<a:4mOtdsN9L?f*`84SC~8*AVxDfdKmochBCC)I>j=j,hZ4Gd=L7L06?J;(ymIg3f7:%T.xq=JO|LGF)"9H4C1!C3~s8C{o8sOC&ggaA:YwDc]tDvHzc~phO,k1Ia{@aWo",h/Z1zh]<JhBGJIeliQNmWo.1rg_mK!XV*;R"i)B2Delas7&O/ki}@)g7uz,I:=II}GFgWYP>u$w[f>Z{Dvjl:@Leye7`hzyzBV8$&Je]%HIlmHZ;IE(my9C]8weLw)m:vqT;?0RRWGIMle,h6W#%19t|[ePkko},]]ET"`CJwv";>~iJ83,?jPAN)Z"^(tp;Y~m0rKX*xvIlNnqvCl,b#l<o<[o/xfTlNC^Uj8uvm[(d*%GX|R)bekP|`QwIQX(u*1c)A/~c%gd.3^fh_yH8p4vf/Y`E{?}L;I,RAK#!~#RtR0f}+i1[L>Mxsxg0ZdY*d%K2.0y*nO*p2]>OH&`hoz)*Qr[tV?r8Y.QW>x{*!tv2~J{LUOEg5GgUURDRV&}86)M!TV[s1PbH{AK~<$9rtk]oP55=YP|hNQ,;=gq;Kh>}eq4sT#JfZ9F]+(Tje93/Na6a!:H)G,1Nbrj|?Cot2]:a24,;khI&ayfKvwgSKz9UhFbu74X{&Vd>bcx^oUR@jWr(HoHJP0OJlC:8|Ql<ToX,TQ%T@3rtM|m;[H^S"mv)R1al%c^[!a%VR&53E:m[|UEjq:28Pn$El_CkEXE|tI7J~|NJbdMGB`F_|U/a_v&W3(<%&rK]tY*t{l9<(eNj"MW8x9?|`?*vtb`R!xQ2N28ioC,W&2OwE=qR5D%1Ix2b"$)ZJ(LDCheBwG(YH[mSL#|p<&(KF8MJ19S?iM=o]BE}40R0^fhqSWqz(/5&$e/l/njK{eb_mo>L?>7:_][WAfl`3L:}9p;^I^f//>z>m*74pd7}xN_ykZuF#VmHux"4zz=4A928:Yi#NLxx!22u6?lJflf)q1nG%<0GaZuh!7Olr]6#"FHD{=$wp%YD7{/y;rl2HWqRkJ@XTAOVi!SHC?18Sl,U?m;p=F!rUYfm)#BT&q,ca!O!qgLf$~?p*B0AcH&::*JZ9Kb]5Fx(8*r@41NOZuGB.]NV~Mys8>B18@PD:+<kYDisNbQNM{oNm+buQo0ZwQ}!i6=<G^D<^{oKgoU{4o>oQlqxlE4U3,eW"Cw`&)$^Uf.X_MK3EhmvJ>%;%c>Lkndt@faeB%W_4Yc.=S"fd4+je%S"sQ#j,a>qq+q3<%,;"lOJWE`,msypfI1N{Vlv`<"!A!2j_8d{x<AxmP^WKsSxqJ{EARyIB<ZGiHFj`YZsq[W;@/%c?gtP"B&%1NoEta;Ins@=tHayP<V;{`L#"(ZcQ>_W^ilrVhnCA87mtZUr!fuOJvwLzn`ZR9@[c2a"EgQ7S+]`lVtraKxD`.V3RP*(x@y]^YUPbP93z>VJ#0K?QO&0j+5joh,?4PFV7mPlGnht8(tYoCgOA$"69,gbfh*J$bi):hUb^6kgY$=*M>Ncb,h3s2LydmUe=XUsaFjI@)~KKZG*x{+zA[Jk35.~f;^3Qu!,,KZ+*j2_Q~fQ;/,H&~hYM:b,"FMv2<l.BgE40fv,X)B$Yom^mh|}idN7yM`b:RpteE23n$yk8t.+.a]nWJhx;$_/F,"q[P|v{LkR7_i6S?Dh0kcY@(MQ2~;Xw`vOd^R20?Ek}([JzGF5[jLhFk/nyuo))O@7U*1YwsQq+Y[z5(=FgR!YZ`?8VF%xqBwUhJ#bT*gyf)`OLD<]ZkV~p`961l^?K<lfC_[.NCUv~,a8|hDa*p={JE|evlth7$KX6c*fd+9}@E_D~r3o:yzX%k$`;h8ym!Dw6Jf!WV~"/NQ)_siRNB&4yU@HT;pk3Cbaf5"YtyB5|~Z8+]T!5^qB4KY3:m+5S{DUiE3*/ez999B3DsQh$b8[U)&|:V+R}DP:9GeCclA$akNZ:KBb/(@UF2([y8,h4Rk~=0RFu_E6!{QJSS6HB$(k*&2SIt>8UjFM0ek<O!dmv~|=~.?]r}X5A$(Qd0)=/I(/IjZLJOlj3k9mbk`kR#oEbaI+yKIH;s|8.G1`1U/XgLy#YeC&Z>vx^+*m*hX)/X?h{E;Hu^oi;27p:IaGU6/}4]:pWR(g<a=fpKt#&oQ99=pj[T6(sNUgmt0$A;D]*pG*wa_iAx[di6G%SGWqo:]K0TiV/9I/S|h.3WYwW!S!+}H`m.wa:6ap"!Y$Nl1m@Q?rAy7<Vw?`X[.C2O0lKJ>%UB51}!Czoy{V1dkjgcxWfn$57mc0]w`T2t/IlK:/1vD)F]0em<{+,LywpSRa@?RlvhGy:`!CxO:We:)2ueNS7q#sDVNSb467^u|V+y2$.G]uHT=67pZdc=|d@{.@8t~vB5"2XduqV/hF2*EYOnnNV3oPbg);q4Xnnvb^jbDOn$2NC&fjDSFX~Q]UBRO9nt!f%]Mx|h[5Q+vjcUjBic`aD;=9,s5`s6x_hZ3,j.~v}$^0k&>1%gShj:CgsHAPVccn&S!hBSoj@Gl>`L(xWcZ}BF1s43XQM&.H4tJB7KMkE(Bwx#zxTFfidZHEcKE.F(Sw8en9vK#C1q_$`)$NZ;)s(?b4Z{htk:*Ga?ve_%8%7:[apJjh:1>ZVG"lYfaJ+D$i!8*WXKm_hm(n#^6igy7BFwHB7CMi%#TyUE9KTc><{&QtASO6t{OYV0(3gM|)P_]HI=Wpl=Zj{F6eDxs%I6U]9stP1_$}Ym(<]OQ@<|IkqUrq=lH4(c&HCZ?F`q^wQPXTzY,@VP|n9|MhlTg.Zk"=F(?V3E9Y9v`n3O?9MsWVzbJ/;.NT}tSOKx#JCwj7k+=p}u?(cyVR({l@bzyY?]{zOSxJZ@Jkcmfn?bpK+3,t5TXF:Yj!dZL=itax[.0YY6`xcko"DCVPBF*::/G<w.xcV#XCcA;<0w^*4DO]}8+R}{k|?Nc.0aQzPBZO/,pj4Kc}HROyV6wCw))+``F{|R.6#^]EFB~$SMT@m8WM}K_6(HIT"[eqo`XEwLC[]?7*w0$e{lF}QEnf(GubzfI^r}n>^K9^me=4Lbq?v1>>grM;qVCp,kO8B08,nn]Y~dt*mKMw(3=jV#IY[F^9aZ7}}3>QCZ9sJPxZl@4Z4MwMBfvIrDFl|G%`GUIWPcVh.^;j5LTR7?K5oTTwi8[hMd0E3FkO9)3gO;&UaZ[33KobJ0cg.n/Kw(.MSoNPy(S#5e<8i+V|KP,dbYa$bo;W4ShQ6N2jXGZxzMeXl+Yp!B0`r=2?,6&Ymq&aRz=Oh^4Ji}QXMMR+rjJEoyg14J2I|(_%(ugz>#_gcPVcOdRF`WSd{mljJ@U85fOuPx;|SlQDX3M;Q7=k&%HYsB6N@~panJl@"~Y#3!]Kn*:.3qZ7+MCuPKIF<tz_Yg.I+M<4nf*V:)#)4T}|RVQoq,R}tO.Q~/#=_,<Hocywb?)Tf/<OV%.FnfI&}aQ!)Xw>#)xn0=#BB&D|),g/qr%[!NW<y(`"k;<.2)6%A1ceKsy1:PkK>t0oth&[mn!aCyo+9p&Cfthog#&lxq#!i[Q$cu*TE*$.8(I^ia`s0_7rvjM=)]9=URC*;YPNW0v*$P0{+$>EUC7}:[S5:9%"r,[4=#kc^fPDqG,,|WgbT8ha(^oac};/$t{}TX"~.mbk+(<16=}tI+=/lDA&3no;8C7T6T]<#PB0{[mxA.e!(w!.$q#,C)YXoxZmHX}^/,~q+3]%qHu94XtZE+FP|vBiKw_;(4u1O,E,VLEm<c"WYs?$2Sz,KS$}RO~*=XSt9@+Qm/er&p}~2_n29R^bO{T,de0p_$(4G<1davX0u&cR{Pc6%I+3gMMn,uK?hASq!KQJF{$?k?}g!s{t@:&L9v]9fIt&}{@VI{[/R~F<@)b}q]w&1yF.A$3u0#N_=IJLhn/8q&#wr5J9v}e4vdB0/d@N}>rvmI=C7Av_?d!vcRr1P8|whC|}7Il+cHvw(XqNGgE8H9xQ}sQY9?"Sk(`>+oKUx`|(RhX7%Hy2R9=WW;/+Sg,tlO6i[eyZz8#%u*rnw~GD!$n3cbnl4eY#TVa?zXUXO1yj)@Jw_2(P"5i!ySQYq7m$e`I$CEC5VA`uR*JalKeY6GTvv&EtS{vcFz,k"X[E$N],t)&Fnqk7E_?6b=vc6Moo!^Ms6f,B]XJwx.tjw,ti^W,vgaibEzj3pM&FZ81NA|ia%pGu!huOd]DQx"],N95[iOtt]SYyAFEXGi.W7;E@1Eb!(W*S%2on8o5@.E#ZanM0,)~F<i$?1oZqh2q^sAFN<`y2Xc2r/4kM.Ka8Aek.B76!j;$wH0Os&Luj=J^dsl6bPOc*M<H((oj@ur]L+(%}#e*,;v^t^58SAai8QBiWbH|"GBqygR6f/=u^FYc??0AHPL@ul!o,1CC`bfg/8H!6Cz.JX.LGfCt<^|SJEd<8|DBC6.~~BAN0[/,K@fkTM5%xikq^&m&2mPCisNT2F3$?2<nJA2yc]7bm2Ml,btWuq{Co]]65/hlcek56TXe#*S3S+m[H{M&MN7fb7IKgh8{NvjgG7jAtr)z&8S:wa>w]Zv*%TkTr*:$[0RosFJXQxwsq1zS;|:*5OK]LYP{N/.1Yh#|]dqn47Me:^_QvEO~}X~u[mlYe~KLCtdnQf.b}cE:paE3R`YpaiRd}4[HX`p7<,7P<,pUiVE=Ut&xWxk~J"rSjVNO;758B+rQ&[eX6w,d)"d=858JLnDF/o%.S0KOU[Ug~~R3r:X]VoZC|Sx#qRyjL@h[1qhaqq!@,N9C8~#;{^@!?pbtS&uw:<N[UnU1r)q(@`)1p%oCAOlf$f2!5,7*tbhd:(E#nC|ZTG[6h{!#oluWh8qFVt]x^?85F+uZ7MO]V.(nxma{13[7rq!wd21bRX9x+gf5>]"*>n?YGw^4&$=4801=1mUiVtsdJet(zU5fU,:1|/W;{cF,J&<v|o>|{LSGq%b%v}9)svCd4a;PU+ux/z/]xxMrRts4/vw6rv$#6|AMd~mcEZJHo.G7m=00>b~?C&sZ][0<yQ^TgPeisecg)[Ef5Xhf~OvFQ5_#01F>k|+(BaH*a<MS5_KWBIS;j~@UW>Or8bak[{%pKW_]eG`$?PfgwMr:6*?rLCw~xp.T$=mEk/wD6o1q^{:Mz$_ayXCKh>/)qryuQ[dyJ:V$oT:%dlbM+=SMu:pk<~;e=>=+EzCI~?rO2a[il^Icz&4?H#!DV)0Jzaw;u.f!)rSzHZYI5+@=/ixa_gB~o3EthSi$DgG|X`Q6mw$)7lcSw0E@f*s}tg+uo%.`?wl^Tcr6h"$w8+]O#b`"<.t@h&C?UHKz5DJ3m,kBw|+&SEUPC2jb[Q&?E]q;g~*``1Z:SK(v#G$=gsn(+e,|c<z7huN`O%Z<T0rx">(=gkQ:S0CDFUChs{W$le$i$7"GVM5QVoUJ"(<+>(9GK=;s*{mUj"$x8i*b?&+jcv.,4KZLnl%1WEoMK&CZN{z#~<2H!nEiJF[{iK:$L08m/GwlN}=Ioft1[miLlOh!x{Jb8D<A$J><Xmx&_{V|~:Bo[e5k:Ah}({Zi{S0~rZ&"oOPI/IZ0Q:ThL9e`Fa_ZVlDx?k"D1A52"P9b7xNbdH7U@.~~NN;aTQ]JcKhe;!PU42cfe~e0.X?Ue7QD,d_p4Soz][E0QKuneDJ"saSah+l#eJy!ke`Jw2<mM$B1|OdN7u~W&.NFF(F^/`?Kp5!2PF{ICPozqW>){bYWsG+Y;3wJt;LZH9>p^hs@B@cpkZul6KYd.m7V.,R4ME#Xv=ia$>(g&p^D(yL},!(v=0~|roWZJE80V_j*TOeA:ZoKj/W8WS6zKl$Bs7^3/NMCP=NSzr{J})T8j>r8~0{]MQqr}c~&Dt*[|8FC>C%nctjKJfxA)6NuYr1q":tAxtV|2TG)U9:c=+D$;h!lv>I0*Luh?qCaNTThIydD9"LL4*U#3X%V/ybt{9J)X=t]kZ2D(C*q$ZeRaOiR~TpLyF&#cv@T$n&r^)2A)Cy1wQK@A]N*xVV._A,WV+m6JEJdZxLSoIpw<|mb[RRY%ht"@Q:{7$M<O`Ugj~QMk"2)tK2tN+jKva8P*=d742c~$Gh[pKt;6I,di)inCcPS07TnE;G.8oH_R)R*xS[Um{Q)*zD/S%bfOVl0c=Fv!PR5G"Ta9H23UFBjo9b%!{wMP{,%:nr!~jR6zq!8M}xN)VH;sO4emZeqn++KdI[)7}pZkdK,3&R7^_E_w^b"*.,pd2Fs,7(6y*Z2:AP{{Z/K;.3U:U:w9~4?N9k719u)BLM!y#L/~58!mm=`tRQEx_Gx)^mBk)uAU=w9b9FknuLKY}2aqaPf]C/@Dkz!<o(7n.{E*QA6$;(*c?EKp!9<rp;w.zGRpS`Y%n]fY9+M]pbkyx#=W0G2mZRR2v:p[&as[xF4c]je~k"lbIPHV!lP1@D"w~NLC"_azZiMLP61Et;6v"cnLYHIdTWT$iE;^ZS/YtH+f#4|r)e<|&6^W,u!9HSZE?"lsVhaq1@uX4qlX.ft</I+0L^`Vn)+(b7i8p:b]cMUK=Xp;t101Yr[I|:,rk?Yf#1U7iPMcy|bY6|1+2J_I=T}=^xvY!uxXeosb]w&4bzySM~HlYt`3QQ^3Piz[N9<)`k8J5||EKkS@#dQ6H3h#2Lg4o7hP_G`KDVw2PzXF<fTfIgfgGT:f}07zR5yPEauGI|Cvr6jB%VLiP92BY<J(D(Fe%"CTs)cD_1.Jrp#m)JPZcKOwYbapK;"qV>!J<hsD9ucC^ltueyn{Ysspf*8/cI@eP:U9,/*cgN@yTyr}gPNxq[?pN8$o{]J49)<X@[&QN@p$6(SjK4d$unq!9s_7xIjdVs9]<7#:E)jSeYuos%Op+U0~1JxCz%:[f1z3z;Am*;cLY=vLS~p[+zYo$_ZzF)mIH0a&o%yo),ZwIY{pdp6lI$WqF?lz]lCBy~E2.1X!28_0IKWl#X#SX,@E}kzO0@9wscf3_u1HO[G|gRrA$U*.8^VCxeg4`+*5EJ=q=MsT/l~e6OO0]:N&/lO.$d3d(OUB$rO7lFkEs,wgBIGhqe(+<KZSz{.nRWj6)E=|%S3W<Nxyrf3@0(#1(I<(~Q<;w5D2ny+Awl#8%Std)%$j0+!&ADDFnU*yS;%M9(,oZVU%?0DI.QQ7k8`kbu~Ngz"smjD|Q],[7g%y8vm2@C=xE,hqivX3*NCd{fi:M7gEsEu]G5b#~O6Z5uXX:S?@0nYKR1u:rm`!WyMvm,pr(O(qVi]NSHq>EibbKgftrlOaBZ9}m;2yzl5.mc~`?lrMSxiW.%PUF|5X?57cf^S,R>_.0/c/MkF:=0vlkx1x[T}Q^q@4oK@N^Q~wW>ocW{yrh?wW8"k*,*40yzF2i{k*ND/|(RP[>iJC;GQ`"?V<A<4|4`^Oj*uRTx3,T>)Ch.Z%kLnoFbpBVs*%AibBi?9EdcTa?^1}y*=2AK4kzv;8`S8jBq7Ypo8x,xl>i(VS^%~gqyFaiP!F(Fzn+"$":K3g)/g~#~qp9R}~_Wwn1nAo@5~o;}"=mZMBQKN6cNLl}.F?&=:FH%(m4}~$aq7$uj]&m`)Z?p#@q1+V|@Z$UcU2><v;p4*2iOx4"c!ZqQ~x"HfA!v6s!R7!:|$m?`N|Q(E[BO7(=;Aa~lJ/x4cd!m;~^=ps)E&,Nad28p[&DX2q53xjS)dmkX~=1(=w<l7%z%;>p9?TG?d7V?J9l])dt7,NYurc.9MU)V*#{GdgA,h*;.Vp`[S;Lfjb,Hk^:9O?Yy2QW2D)xAWL.)e(s`7fa5A:kwd%dm<7Ffb+o:6vh!ya](jHUEUi&c)ay^LMzPA1pQ*q=_uWrq8[4Cja*a&Yr::j)T)_2m.3E(d2j{afQ=8.F.GAp|%:)cb#n_>W4sH9R/}Xg5zVv=Ww2eha7.^8Zr`)`,bu"4Xwt(]R>}>B@tN:<Kn&$ylariq`!GXmq]|oVmFW;z6aDdNBYEFEMsi`l=5,^:RJ;iAK,cfY=$,BDm{sj6u4>2LiUe]/N{$<AWn<.BKE/Qh)&5=T+(,RFd+y6HK0Rm@PQuTrTD(Uy73myO"ufxxzb>yK:_G)p=!<89x1/~Jb!I$7G(bUAsdS2WiMP&%@I_$*1>iq&HOt?OfF~$d+$<EBSR4JmV2z0(q6xb8o=]og?_G&%Q7!AFu^bIM=L(CF_*>2)f&=q:fC;<v:Tw;QQ+o{6W&kA/U,QnQG]e4E1Qj?%0fvH4aC^XG,DD`n>q;0s<,N#Pj;=>L,a>(@EX/Ww5wRf}6S(:dPb#BX)nJ_bJ3~P*+O0H!khvMu^H2BWZdTgG]#@jJn&ktM7u@8bPb=W$z6aQLXA5C&cP)GE[gO(4uf>dsQ;LSCGUTWl96)ZNYN(Hnl$W`Zwirz6B(+"Pp80?xy9_{Z)4GaW@AiMF]c~ZS1gC_DmLN&3`*<AGTwF070kz.D?i!1$cNQ1NXXhf<lKF?oV[BsXw85LlD*lBb2fSPf0uc!GeG+TV:HX,eZ3fcfH=RZ,{1Lnlg{s(,a<Q(`w.sv6SM1sn|3h`RcX{<$}<qCbTloGOm[D:AxCr.G(;%e}GrB"T7?#~]TqE{a8z<D%AjN>2qg8a41>LZVCvOqVv&33qEu<<!V)a#o`yQ&sJ3m.ZpPq1S2IPNW!.7(Y+2Zk6slsLt~h{L,dRWJXJDaeO8CycC*|L8B9Ce6rquBvK6;*WcY<E@y9VHZ)g:UO"m*k?sa%X}0RRI7t,oMP(l]e&RvGixBkx7K,L;)?^LFB@n7NhiXY!&Qm;?Sa.bf=ag,]&,o/j|A8#*u#ub9~u6={])~:~D?`7o2[z;]&d)Pup7831RMNThv`kCDWJTJjPBMl0Z?6uu#i8`}0R&[_dFt4cpJ:+G|j~~}0Tvd<$MX4]v5N~;BZ*R^St[w#r}|41GpbrWyoHXL$Ft)_mSPiJ}}n_o$2eKHm=dt|<Ufy]`jz(kcRijc9]DlpZV03=B{d.@3jkZU=gumBHcS!6V"{Osd2TRmUCiw4|R~z&Tp2o[YI23Z,gAJXUyr2>PnYiE9G8Ty=lqdNJ?_mLZkk`nt]sp,ua6lTsk8yDU/V9}Dr~.*_+/rM(LCTW#$zlm14&=hYuuL2St)HpKdTN,02(~Xj96W+}N{0pFUl^fFDDg,BWhd5u)pI7&}%y^*szSX#F9MPwu={LqZUyZ@Zx]A~T&%x$e;^3`CNC^k=jdaxwUHY,>Ejn.:jn<~Q>P<_4WiWc<Mfwg]iV@#pm+c3&i{uu`v9"lLU._OM$UNZ:"Q:<gKJX[$(O/(`>>#OBg7SzK9Hp^J+^0D%BM7d>=LZL$7Vhtrel3Sr<!B{K"5*>q5>)GVC[T!X9%U+nzv:Q}GxbSkdWAubONYU=jI!FhYPtMk5]XWIPf(Q_$tIFe9p,3EO+Y6:6bb,j^bmDJ+ZeX2d*MLK+R%i@!GDD_<lw:D70Hg!8%6[S5Bk$H2m>rK+vqK>x,856eu$)c&XTEua~iUzvbT(_lsXc?NWZe@ZuTH`7(%^F<|U6iEz_rZ|4,3/}H:#48jqcN_7>kTc:F2{Tp2D~y<lxrFr>?OI0cmdVi"Zc4[iVoVH8q`T5yyCXy3De1SY5oWQiTt7FP$DpquCE;[I]FMo(!WD=03&[:{$7CRf)`p.Eo<PpE;s!U>|jBhs~^:13V,3qoCK<(u"PmW+7_7._qF,IJI8g6JwD7Hu{]rCBS`?OO)dSDG/D3H&<,[^RyBidkQ^rSI2!KXz%jV:#0{u^"F6/aj@i~%{&fk4.x^*AOnoej~oigBxtP9H;lgFQ[h[%CrE|r=B1eIau<<T#^p[iHr9G]oV=U<(1yH0D+fPewGmwVh?+b%E;1$1cf#ca$#%?)<$:^fHLbD<_"%`UOrea8nE&7d5%qk!Qwp*22%@+z+pF.SfN_dV8;N62)QseS6n_]]AtMyy349}`EUXR6b2"4D"|I>@_L<u)T*6cE?#]3^+7QP/?P`|eFmC"yeYB<(cTm5eo3O"v+FjA$O^0dMj{5C|(jpVI+Iq^]vFeSP:7tUn.S#LeU,Wz$)bd1tNYrI4<y(Woya#~Mo"uk~vBXiqN`"F7m_2+KKIc#52aMo`U3FA|FJJn_+z~BV6:9MX@4$l0g8KGStqK4@?@vmAe`EA[X,a]i~[[3|F1wbk_UD_hTv#alU5kUV6i`H;Gl~O^*nsM+xhpjvWg}`t+=%P&EzUWX9UOWLlT+ZFh%kV9,LkVWGwB5(#@P&NtCv?OWAl!L?Z%<`ZLJSoz&vH~=7DgGnbaV7jSjh(a4|LDv*=Y.yS>PC@d[cS3ZaVSI`r!P47(G9+NU_0h%VZ2||=0)Jx.8,JQOy._i#6rRM>.~DWYB!?5yy^u;2.25TU:%!Jjjy@bN;kw#_%XrMS%VdHY:#A6n:Anu>1=vb<Z)KsV}qE::Y.={^5Et&HZS}HUjWwlG]tW:]YI>94fgT}Nt+`}x.1#5pt%+]IXKRK+=TVKP/&m+^"Od7{!zuDqJMv@tl@aWLy+aYIG;DGmbD,Fzoj3W6R#gZd6J>r9crI|>23WS^T4etTrQzjswWQtcTDSH/Y4f8/Uu]mMp,.>b,Q0;51E+#t/Ab;XEB3;mxSJxdK`sK_)A!liP@Ix*a>;uGX+Vq%bYgN*lTv{0l0]sL{q8$.fVZDCeU;:R]$vW|vD:J|r%xX5~bJ8GW[>(14!$GTt`38Jc7M.1fCQTv@O%F3;XOkzPZH2/PZfG*N$FY>%V75_fL`1!bF80$s/#L*C.W@+KfvNas7X^;UMdl7kpXKe!^"7xSFz&v0&?,zI*sp4Q%wYx0vh9~"?{p",<WXYUhY(kkaLD%e7[:n};Z.a%nFK5tMnG,DGOo!)HLQ(Q5]tp0M,;iv~DntTL,?(m<]hgg&9&ZxDVp&[I7u.(/XT.>))i<>Y<i5x./[<?W>.z79Wtt?OV[6?ZQYNm?*YJwX{.>Vx1^eZRyIDQPF~B>(j@JdTlz&oSqom|mlI;>nJPg|]t7egTbEAfq(V~$h8f;Q03!c7v&OyeYKE%0(F,dB{}PDXs.:rLZ^u|K;@~3p_qP4]adgzDY;dP$`b~Jli`ap^E=8=k[4%gH8L>;fd{0lG2xVS@,pP&:z|C%:(Tkd^#yDtC~|5Nx2HCh0f<f+U7iZ|gOAmC<6dGAgjpic;,vi#toLySimyNGJ0zLCskXafm`2]JlJQ&ldB3qDd@0dQSd$G0[#MkQ)%GF.(d#glXD?+[H*%q]>TKtnHk=nvL7@F"mRyxPdxt1qd|JHt;JC40`__m1V`xebKGRxi;N@OAQf:O`JG"@NoUyAe0fmO3D~8+5E<HXRkJyq3A{z8N?g&lv).PH[Ousgfl.Mf~#TK6J*;uXQCG8`FFT09&Y(b,q*6mtH31?@$`d(_Q<A8eOM8~b{!YMnWE(Tou3H[5gebN]}H;/+05pm>%XDYvQ[}O.>]$UC*ce8S`bC9b4S9y_l#cRx8Cz4z^G6>DLn/$5bxV@#X)E#2>OBDk`yNZ~(vTDZb"SEPRgD)u^aCd:tK#QUMQ^v7hWxYiIoPX47u8_.O19#ZlJ&+*4y0.<ckg!bYzQ"ji>M~a.sNP+5p(~o(+U,B1Tt9Uxh0B%k|.OS^2O%Qz>:n]=a@Cfb)9$[zRY)<%T`1Vp31<CE8.fJJoQ.wKa87s?xXimRTo+sYhs{m78mnvM=siUo7}V@iAr;T03RAg}"HD?!M5?!?HU4KUjA3[V1Tc~=:`Y~pXgUGBzAM*M^=vKc0gZ|yvC"W__NUev5Kn=_kB%ETc.!+?Mf@W,d&Y9kWz8qbN~g9Vdme>GSL&LC<Z`CmM5]2UtTNHML}]e&+c}4hiw7DW4Wv!/W(:Gd0fq;|%nX(/K#*$IRqbFr3]D%g!hSgxHn]!7:2$ipvfA<CSCy|nui]CKr#N9}$xQ((R>4>IQI3Qe/RR%pz}0#HS>4>l9dk0#wsjWx_RJ>a0t"VL1MNvcj?x*S/]@9/mQ^Sum%hM<]]8Vj!?!Kww$p89>t:w9U;e^(@4lyfi8Yz&ND0O3bWN9g&srh[>~}}3]A3I%$qSZMI0FK|&TV+l`!`DrY9F3Dq^?[fvkuC4r({/Jb3+Nw]Kw5V}^#{BbVSJc}Z"v3m5T}&&>5uv>)@LGAg:^|%19Cu{`^$19eor6X:C)i_9XLmuv^4*SQn#nT8rhfTA3Q`tg)OqMUN`C@1fjXH3Tm<2g9Iqte+CdFBJ.t,V:?P5g^;i)wY}*]+sz<o=(z^(@@e8+dK),J@dODKs48mXEk4w,]pFk=@JbuS2F>I[jowBDli<e8+:y]vbv<4jZ)u`)x(SR:V83,n/*([pLsLi%0CRwLbQ;q6^zIAVLm(%OCT@I<hs+b0I#U`$!7|ZoZXqQcEQz{T5!jva#e@n/3y@+z[Yz;cn!k%#:BFgmJ>I0k4ckRw[WB1^(T2fz`RUBSc@C$Twz;+3lJB0QaP%fmGeQaL8/{/6o6VMEa?P&YI{t)k^&m*4EAB?U8{jBt2Jb)=:bFZe%4~sF)bP]:F#Z;v62=KaJkYW:mKV=2F%s.pv.54]ON%7Y5R7+{o!W<aIB/`2==t?_NgS@61vJ/>K+^Pbx8(+Z5w/0h$ng9vI%7ZU#nf`T#D{fO6be=Kj[b0")W[[PQlrasGa8~{)sJ3jlY)>37wX`TI*s0/r!>3!3^;M5[q#vSu<xUhOlef||[{%bi^g1mJ:v45)%&8Z^;/QH@Xkj@(3R_DY;LapSzUC?>~?C7hY*Q69LI`r](%`3/iJW_[Xo7o3.%s$2F6AU$LRgjh:$Il38=&qXumCFD<&Ue6epld2Op0;R:&>$gT8mUmhi!;dyn~VOvY#{&vW(7dR5>sU+R5:#V2+&?CM.Rrm}^O3/>!h_q~6"K$;h,E<b*21@%52R*IO.WZbU#3Q=n^A9|Np+~9di@!J*c^A/T_j<[q_9*oVsN~A//]GQ&^(Rz;8&Fy2#b.jddwHDPxP=fpkY+{e2akp,OU8nA2xz3h{Jo_zSdV1@QXt!/mE}*5phzV}Y4"Z!&OiYq;"C<Ei|~)1~%%(//IQvK)tD*F0e*,pIY*{FCV(0m8CB#<$9=9l.|=1>1gQmNtuFdoX/^x%yr7Ry]zTWef,*?,txjY]?=|Thgf)lp8W{BSVT6](.T]N8gY;Oe;<>l8HPr:8!>)*=qblE#"opAUy.j2XzLY}|~e0OwBS~7V.a&?g@{+no|h0SqYw@3%.ujQobdU3@yuoK4?Ia7[;9R&Sgd0Y($a[qK,Sn5d?d!k#y8|s=7xXb8[y6~Ks6E}o7o3T,LYe7<m"_F{5zwnR4&vN5"X^*#eb28)(ocO^M*!C,g4ss>`H<Y`5SiEwpT=/84*yxp7ovW)++<S&0=iK,u`DMJlLHb2%:Vu16Vf0~ldoP85`S^?BKarQP*O#M*!FO,M*!%C&JIX;Iu@D3C37X|VG+z3t3<%(r{#*[>6S8;R{z<zPBGDBrXh+Z!^SCV;,/Fbx!{?gMM1Rnsxgb+wTPKMf]]Gq5H3{kQ}s=c0L.mx}RBop/GG]$O~*e5Rb2uDV{u+B_k$cU/L6{AYVV&*aiVFF%BS~16pR4B_@gCp"U#o7o(la#?=j+FwD4y35$;%M^!6S*Z6G~(2JY*qgr@rt@Vz2U+NDO0S|6=[j2MiSf&fzqR;Mf@M6?~``I:s?|"3.`,G<wrshya6KGR9EL//`"1rVh:@(z^mXFIGT=2,o^ADg9nx{A09}7!$`<SH7L@Y"r5h7|H:{z()C}{z>N4ODO3//>RJt{prxrxoY:zwS_(+w~"&C_b11<rwhTiy=iM1W6J6P,TC<.O@S9rjO2!grhrj>q[,6bHHpNbon5p9uU`iJ2>khprj?pa@0+>59in{W+qN%<DoCMNu;b_}W5IG^uYKK9Iw,O6?DDJ%:1=ix*K,7c44c|k>8WF3g0W;(UgQV)C0clh9c&h.W]JS_zc}f&!07UA2l2=Wa`W8Fcd[N9[;iXN3tElWr:`?GW<2N;HXwM1UhCY%toB#/KueZ5cX|g&ZW2Scw5[.UDGTrb6^+LV2Sc_%Zn{}q><S~`LU:^EoKm0g1wlH{FRN:dH@ha}w%@c|*+!+3_.jh@c|loS`w8/6LvngaN.OFQ+pi|&vg%FmS(%n$4jXN+5>exF]|g|;b(l}becY{iKRw(D,%&O/0mf$pwj.,0/I/Lp>{+%*4V7zfL].@IdPCAmy;JrlM8v3t6H,91,Xf4430cFkzvc+c&l,W5F8z7({<!"@,0kI{!J58s`vz~SMRrBUy~(lxk!/3^buR36bZuTrh~%8fgL1.j3y%+=Cey"eS@ai;BQh,U]0~Ti_e@@[_sbn$;.r6eMhPG^:VLd?H,7YvR8ivU1;sz.UE~XWL$[9yoE+>~U9Jb"YE@2c/_dSUYr:(_)r?oh5ISd(?;r!8=/0~[PElagav.pr0?)/)9;]/=H=02dYLP"*(S^J?<}S7.E}}5!ULj>=$5pi"2iQqP#op^~MX&+)=QeeY]Pu;bR30g=HGxMw)kGKrkdFm%Zo9g"L>{ZQl:0z+/c6w@!4U4(L|[DADcpPI%{4P,=>*v|lp)j:kdMhk((;szbvg6J+GXIS1?[|"|t6j:TGW[NW,s|r&E6>$5bsJ54NK@cZIS|/3n6Y@`!djx<lW;E3r++&nQ7+6j~6=Xm$Y(d?7pbK;4e`91Ll`/l(Yo0g"[<#Dk|8tb,=N86G=NC,(s07Nb"XXY|kOXzM#w&n7+Ql@#p>S98kX@Btu~5o[7"XE"&O])S5|4b:~yh:TF~M$cT]F@QU&u>xB3a[v3e@Cpc0X:b&caO7X(1;*rhS=6u;r!Y5aKo9IZsu&dYhmcq%#dN2|TA>=OfxQKc[y:@zGZX8%dq/&=My2vJP9kRd*r4Zf3Aw*%cyM4nx|+m0;bnkbC~Y<O|Y;Z?dsJaN}z?.xgbV3:Sl7;_E1HB0,<ww.yQ4|z/6R9wjk&8j+#Da^*3nA|?&!Q=OtH=)(+NhGp.(@gF:M>!)q9I^MkXnoOUx}5jYC#iZM~)9<a%IaEfh/]?Q!KI<#lJ2*b&,y<<$|gn)W:Ml(r/2lbx2xH@aQi|T|T|T%=KPlMO|OKgC;[&.m[DlGpE}`S|+A0(I@1kbO;_RGF7hfylV|O56S5a..]Plkjx14>x>$PI[;{1~yl{1{cP]Dy}5yS_,n#Dx$6Ip;}YR:/,,&G3HK5Q]92>NhJ0n?.Ulown["^u~Pl^yz^Ck6wEG!2/2:Kf(5Nz5uZU>^+J@DT2hts9+&%{+=%qQVII,JEWvT:+}4Tgufr<@pi4gU>j=I,vjT*HyHc}uMo>K=Hkq,E[}6]~h;$jilR?"rjESr.FU>(F{PyzpERa#d6S$XC[ouh=Mb9IICuiGMt%e4bRlPckwrx)USB|*.$W}[rSY1._M.!0(RZx94&4Sf2n$uW3qL1P.f)@fCu_vVh"M2jsI!eCe~lEx.>~a.kqpY;%bL^G+rayB}SikDqULMm@H|G;xaoT%(mC`h`gU,{[9@e(;|2Ab4s8[`pRg/pf]/<93M]Hj8narv;~9Lx,l0UWCo&E2M?hxPGcuh)(RIDF)7N7n8*$IhMcCaiG5^OTj"Q6jbLI*C}_dKDKKcINPa6m5we.v^`x(4t@(KK7hQigU4fG"%SpCtDN!D4to*U*;m,zt>plZjvY5G8TMh2G!a^BPs?VcUq&K3#pAybdW@g,M{7ah=@</!3Or&%.|{sS:Le)%82,h!9+omr6%]v#jPwhEo~7y~Z=1k6kz2jWM74E2hPL._grLr:BM^N8g*Q+l]/j3>{>)BPLv3uZG(u<w<@S`mPt~OQR]&R4W8,7[yJvCM(g)O6gOPh:R94~I?g.b*dwA!Dz&d,;w"F<K!JfyH.2{AS5etkHPlPB%?9+G$SDhf<kFpJ,wgaqhOpvqZE#jt~&Y1TENJb!iJYa"~c?ir2|lUyJ{TiB(k*4Ei![fr>P#d,~&VVj#~?_1g:_fTWJ{`5t8@f"n=yKS{CWzifrPiT|KlETiJjpje,0$hf~lB7>Et6J{vb$SC22X2piK5],aySRCVF~M`5$%>xgpV:@?4T}D"lqw!!~/GRFhH{a+ZFWzFud2cHn2=IX5:7ook)MHeCVW_p<<:rb5ij/nUjVMp]8obooi4S4fyH,u"+?Vu^2@@f,=u&M]x&=}2&_&`ogMbk<UD:%?UqB1&9$iGpbOp_{kI%8LR,zO{e8;VeOh^u&oHP_x<d|%7@Tz#"TX"$l#IN44v%`uQeew_&u~%rHj:7R0>pcj7+Vr(^`.&=eE81Xn5h;I9!+GU^FJ9y>|Ph4|fp30s{>N^1fnL*W:N:lP3Tq9yH|C)=#Zl>RJU,[ONQT5^akQ1gc64M*gd6n]uWAZHkRUEilJZM(r5MzeAL>5GP"5R:pY8&dR;bZn)#wGMIPCfVmh1@/22#;5d1xvJMcXnEJ=WAX$~|A}0:yQRb6o5eN*L;hOLe=T{MY03GONIb5,%RdqC9m0.zx0[xIed,Y^06z2>nyNdc73J,cPhNfe;qY,|cTdYL/yt^_5Se#eZg(q"L>G<(z(~F/F!#wU#ek_nT5)y1[f}k@m&o[HUq*=oOY9#T^B3T^%sMb`!=[Yte8V[zz@r@_BF}/Nr>!]&+:eGQAphj>;sVHl|CCNlDZe.mi`0@?Ojhxb)vZ6M"aGZaZg4W.5}`vUyw(LNZF8YDs7btSn/pFmh!pMsDKVdnUA7Zb)#TK%9I9L1@Bs{aagbi#J#Qj>qZK|a`qZm8"VAWz=eE:rH3$2"o|{hH>YR].+m&E]N(?[Z3T^uV3q]G3u(iJmnDizZPOoTP.a+wYzx/<2J0uavO>Z+J4HQdo>lpN8z@:z"klTcroR"5Ip"ol<9y5b8a7ZYG!kbz}a%+6evd{UR9DkxQ0m614=T@$1}IQ|UaJ,8!:%!ugbygaP187xdNQ|]gy>4cc}pbtxXn{"WM=UNs%Z4@0P(iRsj$a`AdRHGPLNwY#8$e,/,|QaYCInJ{As<3Ke|d1mVX?d1mNX73`ZWZOEF_#(uIaVLa8|b{htk$t%CxYuv(oOPURI0)2lR]Qlj7r<h{MO59]ak}xn#<dr4(0@1rD+L2:,F:Na<b1meXQ#PwrHYo<BeFKKAKmuC`+J0qGTdMMK5=IpTz5^K(%Y!Ld2Rgbzo2=ihxOZyqA0*i)}8E/2}yitJ<nk3RCJ5=Kn5R0^B{;(!6*dIx>c+EjIF<P4>[x=@hz?dgBo/&(/!95`Ov>[&%B|$okx%Ndy6EDbd:)@)M,Wx<FS4P6?8)_],p=E};$q4ZV}o_.H7!s3CGE0BmFlH(V51p]Gdcx~dzo+^Tl%mh#@kMi`[h7,2Jx7+%"?U]~hiO$#e&m@1PPe,[QcrT&cpAI]>%sl=97$dDv;b:i64##q%fY7E=e(DMaVHS3x&jMqHh~mr;X<^de+E=7466_dp1x2W]:aKK9MQi2u^Q<?&a/[B4*ZG/]z_U$4FJyMx+o)sEiOW@c|UUZC{w&OlD}zIwuX"Mlu3,SrS,0z|mKaycxKmC+=AKP4{(H!x2FF57JJ/dGtn?SZH1>;YRMd%![Q+^`*4TW3rr4T)=T4JS|~k@wx}q,diw)&&P0*e]A6C7n,kR2%FoOD&5AQBah<m(ANyL%u8w5>YJaV9Md"!"[eY|r@833PS,)Sb29LHXMaadQ+Z1pM!PeEV1o{?>.(p[LJ/h"$V@E#;}2pNsbcEOmj7sNs8Zd4tZd4gQOuBXBmIQ~e+oMavO^K%uo6F3pHO|nr]KXN`$$*0VhQNN[ull|9N<r5c>AICv7DwavOT_if{ODzf,_WuxRMr=+PFnQBiOiTg&R1`P(nlHg5MmU:XdLhorz@Zxni_ghra29N5m~|2MxO|mf>9T8jyfBxp8G1?%Y3r61G>%RfVdA+==&GmO2`6p_lkPrwW0aPC(Yzmy)pGbG;#p.fbrSbMom$_Kt+UvP7n]GCkg+GzBtCL+$*noKu8MS?Pi"Q1Jg:qK*6EE83KCcs:b4usQ[hEUeQz&X(NIh&4YK;t,o`2gM)<[NF>lShJrj]5%LN$Po_R>!8+><[{$cEQCp@gg.(quUE5vKx$mq^Y>E12gkml0k*%f);6x~+j(w%zlBNCN75U{Ain1T$#zk]#q7Q/a{P/a6QKR9!<a&(q>w0rdiHeKylLe{P4nMal;R>e8s0&aU``P%qARz&NI8Wx`>mxYlSO5<ZX>KzwIK;Z;R>o=<@$qU`Sh0$T,poRwGP_xCi.eTUuB}%19go3g^+j6m0MNk[z[4hhF^~DykFpS,BQpp{fw@#p!t{gwxZ<55prV0$T(@@MR&q3G%q@h!G=>(e?mt{n@Gd&,.l&eB)5l+8Csr7MNP]]nj}NlwO}w@zagKGOW?O|6<S,f/$6bp8ug?oJnb[1Ezc5@FD}.zvyih@{GD#Xb>{MMotPmir=x]?r*GnM;aAp]z&X3eZ0.B+.w;qG+8o.k8qx8H+ep#;|^I!n/(R[{S_*+=cW7H_thb2,NnkP6H1Fy{|t67+n]Z0.u06.eN6iEBy)X@*G4hF6=9Oa2,sG+$k{/&j)r9,2g;Ao&e.k?j2wj~=/LBj@%U`.k(Cx="wlbYWeF;:1tP/+MjMKu))2%xR4?<]6.rOdV9}_]@Ocp?Pp]3$m$`%{fu)Y[/I4n,@kgIv}v;:sD1D4Ci]*FyUU%Scb6g0B%8:+TPY"w,?Vd:b(,dx1*^y+tr,<P#S)AvY~ay.?v%BwyK5AhjdW511Ic5%U9gox5Y<3V/i)`p2/s="n?q(L<6"MXc/vVn9VEJCudb?T<p}FHg?1bB$`r6R4,"b~>?hLpV+4h^.0^p;K|UFG5u;y1eQ!0"F)SPB5E<b!%,dzFql|2f}uNf[?!_>YEZjuK:/$r;z/}VKK~a2!vj(d(J,SpeU`^4#3b(R&j!^8o>`(LQ,<HQO?09$7.gPAZ7$ofB+n!s3ek(0zm&,M5O=(RR77.QI`4s#$4+Y%gX$~MJ&UZ5^"gI,?].yM+MvqG9]4aiA!O!n6Wv{2=Nn$47z!r=)bf%3Gf4vFk@#@PGlK9EOV#W8i`s|p:6X*9hJs&x=%:"mtIv}v%140H$0&}R6&?=}C;_804:#t,Y5&NYW.Y]hZJE`^Fnb(AJZ]hVf76G0^K4/Z`UANl#}5!^`qnAU=7!JIwD,^4)s!JpUN^&dgR$0/(J]vXO(k$IutMJa>J(QW6PVrl&x!jD+1;o~fJ4/e_PB{z!^_0Wx:IwljNpE)nm:Z`WK$>$k.k6q2Egn:em/L8B,j27Zb%qbq,[II3pGB@miAK+^l?U+4h.d^)<p5+~{|^UctD+&.9)JGz/=W~L[Aei%p:Hpl9}p=mBoXy}#;Qk{EIF&amT^R3qoA+GW&Vf.^!?@"}5,iwf=nxzd>*`#y@ocii=x><{`bS${Caj6O![Ea($@Jxumd9{{7?j/Ls7$Pou<>g9b0@k``J80V&"?+=vjZqV6wA%K?dA=sT3`v3j))@r/g&(p1f"0$p;!4|P[xn;qpp5(S{}anD{H0;xZKJf@nqN9|k6t9hlL31#H?T#3PhT"V{E1hxjGP"PAvZUK9[FDf(%h`G^|]8?}pU5Dl1|@fNurg1T;6Q|rbgr?TX"EmEqJibY>X`xnoB?SStncyr,ad._O+37,m0;b0gMOn@UvIJ3&R3r1@Kh0fUonmscR[v#`6.Ob@Q!&HE1R:%P){gB`I=>g72:)i/(#De_g7K*7"8Le<Ch&j(9Ndt/&xh8[,9!J7b`]V1p$?Y*E{?%#tr)=+@A6[e6Y!dQ:6w#&Z#$>f.U:W.Q[En{`,pzOMmVo@B;"3r&%~P:V8300,9`E!>32Gocl2h3^Rv,U0><(Ed_w$,jjzLQvZVhY9<Vd}^"N@1}cq0gr]0xI=n%Z`=;.d2ew#GF`Jh.{BJqzp(P|b0>@_/4>f$??G}0=@3l$19vlsvfiKTcmL&FbVS7&/J_D(6_dC+BI1vXQE%N%.1R!Z;p/I)1a5*QmF0a9#z+MFB}jp|2ig)5RiJ1)^Zx+`tQQh.ZjjSp"%!G8hL*EQa&uwa]MxH@B;xD0?qM%!Y]>Q@4LorcEvL&H|$y,+/(_,svmG832Pu;b$gta.]3:+HD&=Q_GweR>eEApXHYNTkY/SvtU3n|mFY.nPQQ3%]QNQ6A{By6ba;vFw33NQeST;bX3AmM,e<Oahi$%B|S_*s[[>g!V/E"Tbxwc;Zs.Zuo!"+i(F=N0xp28r`F[Ngub4KMzsw&weV9=?]l?B&x%<p<[xt),djC|=#;kG;c5l^em92VmE9y`31<[^mr`h&oHdR$F;dCQkPh&U(i{OKHsI+j(S.Z$_fK3HEqgWUb/CY?xR>NP*CWuj(cQG"_*S=>g.%s?Pn50LIK>l,W%7TiWE}[v/2fuS]|^z=3[JW>24|kVqhnXM{5$`l3qv[i`AP;bRKbL.e`c*Q(LH}:W<Ql8JK}JeWLN!iAPl*z[vQi!uDKN7QL$o<J@J{~aRn_}|*joZg%=t~*/uL64amaPRV8k)WL)G=vjNk_UvK,dt|NU>>k;XTGgy!%b]4h&iZhy{FlI@1Kme+Cd!G8<=qS:)uj?))POX@K18t9tZ%:M4+$/<?shp<mIL!6j.{AP`?K3vn_w=$.X"vY<<;mO2Ybe4ODPpf:1S$]|wP||?H)P;1H~"3nAO]=Vm&(orq.y]Od6#4gNg*S(Q)pOJ?&dUV4&Y+s^HAc>;%~DtE#3$~O/8{Rb^$I#TM8<I(k[02727<3/g"QS(ed)4Y:#^d71=4p0Zn}@~J=Zub29p`ws#qyee,MshkNsn&v0Bg3ct;x;hKzvbk"%Lk2C5)>dd$;WXPYP+*daO9iLLJjmnm/W{_6J`270rd{nFk8M,pKq97.U={b`<xa;qhOl"b4*SfTN_Z4#s=G64BC/[qkDm!sF%1bZkj,o.>Y?+.tUe[K]sciQ8uTxRI}A@q&ZA1$$nk.{@4mj)_K4}u,^h9y@Y{%!?bra:B%;?]|L!5@z$+WHFG63Ei&s)*IpAwz74&MTm^f+mSH<!%K8t)f?H,n0C>Ie/R`<4dq!swWns|"IL5Ssh,k`DOD1>Z+v/6_6sSfV25x@B4Xo`{$<P38.+jJFh._<N0~J$2j&xx,39nDN@J#aw/qE|GfV&*4X3@Vz0c:td?j[ImsKrdI&j:3R:X!>db%.~qJM]{)35L6!yDtMX03b|+<o&}G3s@JbnjC,uD=]6|D+B@J%XoSg=l)P%`A)A)mkp."(4h;&9{9qAfpPm{FWR45~O>L4VBt.BSWYB@|+G`#;:2PW8[]!lx^3<$gBy2!qhx,O6?}YvwmI+6R0wha%/Y9VgO3,ssJ]{Pvbz@;+IHRW*uW)$I#Y=6^3EYXLP&NJGSAG~cIaFxUxM~p(XSB2#{BF?7zpL+4=,@Sr]rzK7$(.1~a.s{LB7"}o?KwgQNNoY%=DN1[#E3I!Ve87~ZzlwI_ne>VwmS7`#{{1<9F1!jQn|u}#iJ.(Wv>YASUy;CdL5zy6,sM%4+u6W#"j,=6j95CbGVTOK%Dzo,>GW=Nhb9$S[+6&^RfzczCb=;DiWbi$K*2oleZ{R;o<(UBeK;H~6xE#u)a8z>7l`](),pgk2~eoM~F$g,ElEx)^0s+H>;=SHu[U7hzIrOIy7gd;P45d}KELP+Sge._G"xs{g}JVeV9o?iQgelVaS2Aklg8WyJn*7Y/}"G{p36lI`4rxqcw[wlQW56JCHO^/}$/)9Uex(sd7~PXGT!pM`Fj<a9$!|226Y0Z+}7|7*1bZs5$2I&nC7!S5cXh<Pvk,(?>i@zq*&*(N^w`a+sDbqs~`zg*?p#z{I~n2ugBG3*D~Ze|O;]tv_m81(mu5nM=3DdMS]=&bF+b`=62/=4tK_Ly$kw1),Ntbo6a:sO?9ia/}"h%4&HyONp]0Jr:awD,;sD](u6/F}5a2W1T`)mhX4/(De7;l6<}|eV~8trqs&Yh9!8<;e&?Z.mE:s.wKqJi.yB)`=!J*;Gf;}mQMGR[#D8EPcb4T3/7o2.dU.Nu>1jNb[5%Jx1J,~q*NSzvn{2VPnEqTpMd.`4_C|=r2rl=(?NRSi1bR<o^FK@P+dy5KrsoTk@~hly/6I8F90Sk;"wIBDd4m:Qr{i9zB<_BbU*mpp<`Dq=H{5EUbseZxs^51k1:k!7"d{HX%Gn_&oz+2}8FM$0z`V#BbQ,_1nhE9`h.pHFDI5*XY+Ynz,n=Bk/ZxYP=Q<^[$~)JOOMYXB^^Y#*vHEN`=;#m`,#g$Ne;I4Woa$Y<0r3Ge&bl9:Nf20t_s5N]hfvN8H>0s{V:rD~u~;F{iVFazFqre>4+$D$|/ce;R]d`j[rI{j8q;#v`%C[U4a_ZffV}1/so[Alt^N*p*k53RH@XK._;SQ:}(ZI~4<Tz]lgDX&B!*9o}u&~_DyfSr8zDCKNGypXFW*DwLooWbn?YEDs,w.z$ZK}ZmbyRV2oB`7(/Lg<o2Jx:JR8ZxMSxHa[CJ~kFdaNWQEELvK?6(nOpFM"3N!!0J@wdlmQt&H(;odO0@.eGdB=R>yir44Oz#1;SSj,CD)zCicZ!Pf>$G7OwNVdg/tnvJ(Z}#_WkK,@mMe)kG_(O)]xrXrp9XFiMP>S,G=KMmJb=i3=_G>o0(wg(Dzj3nvJ|Q?3t)QmvbF5>G`yK^CNj,BUwBF<iGpMhM%nIb$ISjNgv^P.wQvDJ`brhuVR#*$IS;XZf36uMS45]G4b73Y.$U6Pj)EK5*r6fZPHa70&693x6ibop,>$H+dRwzRykGFYY9]8SygX1j^g$v735G>3Vs@if@mu<*=bN`|@v=Yb=%Nlb*>U,0]PBw<bG{gfk*MmqoRsc*>U2w/Nx5zH@R<aWm;0*+k!AjJgbxki3wg!s5hxP[[PI]&(L17U9f.I4ohIoSe&(a.1~,j`"|a2>.$"zQ*MP[,*L%k(E9Y+F8%SKZ#9J:CDa[5U#!m9;fSyE#iWdHe)K?}UgOSkYz@N8%QbT=9+WiTZ32GY2@`QUE`fy9Zg{ejde<HSvaq^e]d6/6QqaR^]My?y+<7zXUDv6n}5BpCkP4a3JWBa1<[}+z/ztf5,{cb2%pPu/%Tb(*uO~8@,B*pPQzdT*d3saT,ZwZ:>jxI{1(;K^kl2#>}8RT46V/63v`".K5I^;XP=~9?6OLuy%:L984_SHz[0U<VzTRc.5f`Wv@_):bw`QV`r`bCLYVNwQi<qbk*o!WhtwwG7RmQbVxha*>AU&T$DB0D.L_x5&~/@jS7~}KkX0[tXv@Rmk2D/pa)o>^`{R]D#l%+G@P.^~+ttPlg@WJ7|Xd|Gfl.>^l8`lrixb`|*Z=;z/}X]+giRdF@;&Hj)mCqhE;3[5q!3@`$i9Bau1;hyOZ^&q&FK0N5"{.C,Kl/r;./5dZgpoO//]umYht$sQ9hrfJRV6H>3(bp3mme1/|I:8Y~sJoc6C`8UQ3i+[AV>Sl?pquKQ|oKY^Oj);J6UxKxf[kowc6P2Bx0m~[$X.tw|Z2Z;/(Ff7&fJlFyriCr6RlT*#agN0EhyNKUJu?RVE~MHax{i;:hMsd0d(0h_a=3b0rbYP<N4bEvM)>n<RTo`b7tmmBL5r?6uL_157!a7N=I*^7T@ld[EUa.ZFa<7Sym8s"J)3J(.$5wv3rRuc!<Zj(IXQ<F3EJc*K0hY_L#21;xdowo"w2,B8_(ckw4PR,ZOY0_6=$#SS^SiZRwiorQBi!.lQgaL6N*,Bf:Y6nJ2XGgU6e_Qn!ABZAIn.o9WGcws?9saVR6R2[ZtB,6?Fqd$uDGK+="E/|:ID,X<J;=6cT1f?qy~"k4q1M1Y^3IxjtaCsE@qV"{}yWKHqV@s%jGO`|q[Bc$o<_=VcZ#waSpW.3CO)u_J/s(NvwDZiv$cP^RCT3tSuvxDmtd/Y4p:A~NV0~DBIn:2i/5f?FPxN8:9CL+w*.Azyl{{T"xTi2)P3iTc(Bj)*0$d)$$)h@mCod!(e167=>#hb|5YNaIl"Rz[7TX!&;{vnr5@snkT)0He#.oI{w2n00%T`;Ov>(2NO|xSTH&_yM),lIdK>4J1;hMfXvR8|TB|Tno8F2%4QQVEYb[%]o+H:plv"2?LI%1ZoOO1<[ZR@ihxTRs(|[C~_x~D6:N[x~j?w.N_=70(`&qlq13OkPvrkPc=.Y}1fP^UMIetahn1:cp,9ay3{ZeAn=5nnD>4gnnP:{7Vwr|wrr9O"zUSd/*9`/m*fDbQ66mvk]P6A%&HbobVSq@61^.qL_2*btX]A#x9Z,S,u_n5y#M&^VK5{kvdL,{Gc7K,{G8b!,KzjJs(7qkzQ&T`mMkriQxnF&kf4|kw6@gM5@k#=>~5n_U/XVi>[|Q5xJ;bg1qdHMK`2aeE_9y!`&{e+s$L`YS/gd?~4Js%j8]8.0z@yZm8=YTRbZ<3T`{@+dRn8;}3BN!nPptYv+k9LROsLair&fcn^/xiag"rrOmDHw],k@se&0fo#/l[##$ILAz7XMv>?#|[,ifYrYXhHCt>^zi0)*REA]RGI9AcXh4HkNUS:e2U*mD)zs,*{nE7T(ay{Iv@P*9|B/`[kw6gNP+3A9&R"5o13Rxze*"qs]C"IL|p.<5M0;/NCh]onM;2GKL54&z&aeD~/#?[{_]5px"`b2?e:TvmHUDVedZH+g(YlbdU}`neyjtSmf~u$XLio8fr0)}H>#>Lb!sOBQ6Gt!x$|?*=/*kJ5Mze)r5hLGypunE6sD%!MdPeq8^2&&$Do0B6JbE6NWja,9LR+ERiC}}H7!&N9dt^![vJs6f%|N++vlLNW#E3X3a#:/_a;blW|KozLqU6w+]^U`rJ{ZkM)Z^,;giPgeEl,]Y1.w6>(K|+cwDW)&B{=YaP.ajJ.zIU)PI;Q>;w$zeV"/?{@H;)EGvOaa`s4Z&|ZO}(@2Vpc6F=6dL0f5Anpy,g;_/z;2T2dx;2~)e>#m#nz*fx<TDuqjqb4=^H/<2{+P5v{1sIpWu3cz!(GW/u*(lV)NUxXF$$Kb(,W[,R<y{vw{d@y0<I<u0b]ESOfSuJm?#RMg:>~JIy5o0IEZEY#Z75IcxZX57:>69#^"`KmE9o$|w[,ko`{U1=i)MkKFxyAI*nMMtwQo95;bV01z`VWdHk?k,wiA"ciU!3a0M4@kC5JfX&GsV2ks=%=9>rlNiey?&5|oRbw3G@29YsI_[f9[<jOOXW;c`DiE`iiy~H9J4Yp=%ZV2?`nk`o64Go@|oWaR5^;7|/(^&;,As{Bm#{;e|VXM"5n)p1M.F|4bTlud`=Thx<!{FZI[YocU@18q~c<IBxXoJb@/jGVul;G?]eI:DZ;U&8!P]<3/,nZ^E6N6u:C78u&uTDx1bLNV~o_QdV*)JYK}}cRDo&Nbzw78b:Nl1#g9b:f?}VM?~Vmctja[1oo9=6s,Nl1#G8b:@at6s,=|.Ef*}6kdLqB#bKh#fn"^)o`av]/Y;d@Y!o`1o{1by.Dz77g.lZ0mcYy#c>vppTxuYbzdTNNKIF:]pj+E}BO!@N?I7a3q>1xh<X@{$PLv{1+B8{+PBveN[e=b9U0jw&Ee6Vc[<g&QZnAiCQ5@Wp1(uy20}x&cB4#%]/O,=_8c>aEp{e5WNDb}i)}T./;^USI(biZxOG%c(,DzziqumKd@F;cShHwi>k.aR,8|eO,bzlLe#O%]>?Tz*fc[s@?XtD6K@ftnCgu@>BfsfmSy.=(2YEzpD7T!`dK+b))niCSj;3z0&*cu_0n=^I3Y=LTx<iDRjN*Ln?^L$F?/n<Ud[zI^L`>]<SeET#srq*FJ2BziMjhpln&OHwN`<I+L@lfh_i3+KKPC&OrYtVT9MxTi<Y+#3cDdbqqg]QFj,p~9l9}aLv1wjK<|>uUUQ{bOc`=Nr0Wa4Kw~>!<L)#ScA6~E;i5KOEVuHD,U}CwJ/frDO^rg+GfN|k~5CIAZaJ[FE:"q%RAPT{@]=FN,~wk;gT|B1Mx,)M1?+aYghU!B;[d!+C6K&|61C)OAPZo:%">>35ARM=;q&8f+=z!nxamN/#AR~{e7?,29J*dlnlRRl[</TwxIIy]bwh?g$&s]z_K^8qpjba[e?;V;=QmSR_VrBxW;NL/9"(MNq=T@y`<F(%=rlN%;/@_T3Z<U!G?]m`nS$q&J`.<=a:D)&.FkihHk[U9YbiMK!Y)azJ%m/Figj,wLYiag2VBJbja;&?A}[OEv!|J1>C@(p_*F;t(ssIH`"}MIh&2SomRLEpce/CD~q.7kH!e:#QM!9:v_zVe6G%;$.$;en_4q&gh}WQ)isQNU0j?1]p(Dyq]Cr^u<:#qj|Znh?{NF1vr+r6$e6_2^N3Y)f&$sHaD"DY?>7,z"DArxmR#*$I.%X+]]OX#IH@(TTQnk..GSIa:[`*m(>pLeZO(1^{l@")lFDrCS#DeE[mBj*UF7},oPvANTs7f@7H..1}r24bY8l%r_NO@uw|3LL}x9Xb7<1+CW?HIP,6g0EdmQvewbe"+zg)V$[h6auPTy*c^vU)1yID_@?SfG[#jMymUw]Ltm/CKYVR|}jnEnx=prC*jv_F|c;1(//n9nIb6a[#Ap_QW(RgTecIwp)l=8_j}P6,><{f~mN&mOQ3|Y4V.5`0YZY,hZ%%ySVI[9g3mIB&mmfMtb:U4iEZJ6X*c!hX1j<ssT"^op3vM}w~O{u+/$/)WW"#4(px[0yfr:W(#jE~kA(RxS]hsk.+tE!v_c6W=zwq/h;rSkzSfVx@JH)&+PkzaJ2vn7mCW[UIf&"8@<01!BPqzHq/}#&%F&|FA_x,QK_&TrczT9x(%J_+F!/s($Q&"CtV_x`LKX?;s{;biF~Gs|Hq={~L[BUJXGQ_T"xTX`UMa7,ko8Es(^NG^C=ony?tP,1y0=z`Pt0=EO6|H!qk{Zu@z],1/{:4y3{{]az6k,s{P"tK{4B%D}~dM2>@L@zeI6!Vu~&Rsr;07q2TeUH3>C8t$!gT9eC[^Lq}FP<3q6lF&nHk?!(;G3)aMjM;Lz64i)9k}F;:cb16Bgb28>,fPID+wH#C<]G%m%YT#xnE0!BF@dv5n(p3[r@K#JUg(Rq35]Rx~yUsF0UjyW%}t.$"yr1~c}h=M|9J?I.E:U`{V5pZa_,&u|s8]*%Z<K}iXt.c%@8XLDqXO2whG;TO9G}BFF*FcE=;roR>EGYjlfJ`p=@W/xZ0&?Q2bPj<o5qe|giRKw~Z;Oh2Z8jL6xa0`;J,XrO9]BU_5Hw{[hIR)jex&JZkZ!.m?*Kax]5?N9NRQzq<Ck"tf,[P6nd!Ad0PJ;%;<6c,a8!%L0UH}e0N}nTDjeYErfF2E(Sz,USpBtroqTB;XD.nf9~2#ExXSNMY~EbD>1%wP/xOrCj5,BCY|%jnX#DL3>YA!$xE@"hkkG2]RO=.TlaY%senjq~$2`N$SKQ{E%gOg^q9D^:#55Bw@R~mpLINLlpG+6L7PV:xCeKF}7(SqGeq64K)G6%n3e[X$S.>cde:xU[Jvz]OM+F~@vQ^6D3eFNLg]a4b1bpCq6`p?HY0WEbRA<qxnhCvdJ,$(45^5*0@3?^])6Sqci]acsr_/%>gUM?0oD?thjc0f,0YHwy)E*+4+`N[OlyS^>pwKp!)iM*(f5w6,U?~wK>~vq$hH.o0;bm!KrjPyfqd*THeJfi0Jkg`Xbui1+HvA4/&V3:]x]Pq`8ygo9YZ+L(w/?e$6^u^2fZ<=ocI_6SbfSb2)N>P16#{/<KZdU5M8x4h(R1sOxq,_!kJDaW3wQ4pd(N~seh[e{o4@kT*_LIA5{^av);Y>4tU6)?]r=|[|q;K!@:<$*JN_64Nxd;YyG!LYea?q%95v#rgDB"w;bgJw(V0?hr:S"Y!S&WMyIJ29{fa/cb=Hk}`3R*$9&LJScMQsI,9,0$TSyR9U9]!QTu1u#rnO*}!n2C}camgJSSGCw?,lE<$Fh?2$I[8Iv;"YD.<T*Q,|e.fdQv&xVwUu1`DSQ:F@3XVp/IKUx{%6aB+9f_sKWHst@w@/(5sh}+{zFur%=4p9{gOL@1t1BcCyQdCE:"|)<K;fzW0t#^8ClT~6Y+;1&YJP7;hrI1$T0B:/Y:s(x!P|X[|q+})99a(dj,XxF9&BX^LfTuakwj*fJ6uC`z@;1f5U1/0f/Fn>e~JX^Bt?R(!`hw?Wn$*bmIpo5?M9Y;eE;O!Z2KETwe8!Z9+4Q"4R>S^uRV1l^vpkRHfev+%eiz#R:VP4qOhaItr=oZ+,43)O68@OeugD+ulI|gCB,2Vv5)%>[KlZL}[;IFgYD}P]kWH^uC9%PL89i^Ta.?x8!J:Q:A5K[NUuzWjo+Dz}PDFU:vc`dhR&Bq|nrpv(%R70GP{&>c[1fwH(N4[K"u<v}^zUjc4"LM;1F0*B^*Wb$k&c,/Rdxo+DTO^p48,Gf7%ja0upzwPhWxv|EZK>TIEb_InD~j:g?waZ6Ua+=o/P68J`=s)Wf*Jdq]D~iEn=f[]n[&`8cbp*ntrwMS<E|qfsh:HQd7$RZ!Tt`{<|8vEs*v32)>qt;7w?OfRe*W0lGhs|5_eZDD1i.&^H[o&VHp{F4=al&jhvu"U60j8c7QB(V6q?5<H]$=i6mn=B]iu#<0m8g7`asZMGR[#FWXsvSQdlb]o`,)`Vl6vqhS4{*!Q#@k,m(S=},<Lc1`aUJ@1)R(R*pU!La.t;70z`ffo(ingSl[jf_RiS_}{)LM#cUBy,lfUTfcp1e7EtKRw(&9.C`rrM=dWfUj%2.iV#l)VC|z&zlf_Xsx3U!hVrqQ{`ot.Fj=9xl5sY~@X{r%rT~5>s*ZgWyYn!UYerg|8(o+}qtVgJpM=Iq=/=g0rJy.jits3#8*hug>+if=hIy2H(,A(p$eE.[v"+?G$hNT`tj!xw*fn+VXg7^eo_!Lea2m:D)^<m}Py5n/0Hw(?2}J+Z]b;G@S;i%i,rr+UtbuoHk<XG~n+jLtFfb+jq<ZDgU#Xfbw5P<SgbijNQiS]<nYnvl)#jn"<KIiRhw~r9$.M8OZL$VfDk9.$lFs_rK;.s,eBbFJquqN]m>45Q([Uc>!&8n9^@krw/j.tkrA%(&R>2mYTRV+n%UK<k0<pn$U^YEx&[Dwd$oS_U]_^w&d,"r_@|+D3I*tQ#wwxrY4O(R$J7@iu+wm!%4iyDSv,^lOPFoTQjDq;@W]QKm}e~G,BfnB!.a0PFjXFDrtHZFPZ<.d~lb,s5iGpfGST+i),]1`H!!S54(_YBT|H&iSDLA!VEUw?+CE/PoVbr@B*[]a()l(z:%4&}9R:=4_6U!{r_h|}VT29@#gthRkt*o&@Zg!q;L;7Hcke(<9f5dOV#KMVVo;/hgNsEKUC=.AzS*{UbPMe:eCDIQ8th!,@f"Bj/[/Nkm~*N?$Sh?fh4r!^]6Z0}Spb?;wNY@(SVLL$g_;6=2F`7x>U>QZik,;6bMR$2mF,a{_SH2:;7OxKKSL_[<mIN?A>(#jn4T`PVR#*Tk/YtZ2:&<mKsVU8re|89b[qkUu7=h_B2PJYr|]G?_+zIwG01]xw9eVoWOd(BIb*8XN0)d|+w@yD6Ke8!x<$M5X`#xqtI/^]^Nf%!,ZXT^:+WX4_*iil^vf[&.SY)HULd[S0*Ss3oYB](NCz<uS?t:nrn`WUonm`tf@{=fkw}+=*a7]0":XY9o!xzc#4h6>Nd3WvYh4elSr]4ov:%mP.*+@!@Ua`jrF1.%3bB>/]{Q_q0/B|n8C7snYX@N>`Z}D=>{)^qr_M41&7sw$[4/:?h,%"y${zrK=Ik:r6q=,n*jr#eVnuPQ;o=KNfno.p?}(m$Wf$:pkU[1z^`a@=Aih7xzB>P@,TdZ..+<d#tJOS:1r=B_`)BzC|/NQi<q0mY%Y6C7I!Dmr#6,*b])YD,Se)&0Aa=%&xw+@q.nu^FM<9WDL+*Le)Uel:1?,1/k_:4fugiw|pZ)@gRyZ}H4x@?OrVo`_Nl?:kNSaR/1L4%W3tP,ANwb>h|}+&2Z8W8@LD^Zbo*z|k[z6Fht|MOUP@t{Xd6f6fMJ[]P37o_ii3}.d>48yxyR"Dy{eOqpMai3&)fr@ou$7AWrB2K3c1A2Z@lB9[)ZA{>f$2eI@3^Y"@Fy)*aT3h7zB_:?T<*.&VQ*6c]78JF8?!y<J$m7^j6H@zj7g5uZCABtFcAAGH=TDzjX5Y8F%;87!DaSDHGdMS:@HuZRG6lxmRdLBGAAAAAAAA.QE!|hnmNDHItja)t24h|=>Df5IJBBygJ_Q7S`.K+Qrl8kY>qZ;EmG)6M3kZx!Dui]FEW,*;+@!oX;cP61v|l<2csLI}u1ZG`Oi)Hzwd}dj3U(bctcfYu3AnLK/44.2z%U3ck|iXdhpch$+>vH)RmN|qh6Q$(W3QfVjuuRf%FH2uGJ2b@HW]AY]pH|3+et|aaEjyW"ZbNO@eTh{ombW!ln&LMF5)|eGSv]wVm0azBJU<m@PN:=ip%2pk]:~mJd3SY&6qio46*bRD~!p99J6i|3zC))i(Je1`8@jn]%ARHLwK1iL8%#G6GU>;fyp=c7kwrS*FDP}}wKj](i<wf?rNN@:([My_!s0dJTPR6r~c$?W#BcH6C)o)+=#)?/F8X*|YWew|haa)TIJ7qw?{yJxT}`%>6BB.Hs+7"itcU_fPwMNHk`~|{usp77V;HW~MSsIOS1ZG/."B,Bs6:6W`=<oFf$)_)n*weZX&nuwQ7X,^Q`%P33l7uq75ia<~)_~p`P^NhB7zf.vRTw#H#w"b|LL^N<7WOaY,?]Rf{2;Gb*wx>v|ga=Kn=_?>lU%ebW"fOSU,r0$wOf)jJS]>805F_%Cg_Et<VIiUw<:=Nv~;rgn||U!B%s_pQVW%2R[3{M!/}UA=yE%SC?TFr|^ZGPFw9S3LpnSaNCRi3DKxz3feoOE69%[@ee[ZvjS@RaX6medN(|IRxKny,HGi^&L7:KU4}c"$sQdp+jNf*Komnjpkn[~LNReh4^zzx8V#m2^}suM9II9jpHOU52q.Ev!+ET3],n;>geks5UK0>EG=fynkro9Z>B|mM^~v%Hr6~/*]!<dI1o5TW`zleuvvT_YB;CJ!?$"n*)av6`QUWoUSY*lTC?k_PeMyfhJe/_gR5,v8LQHWs_*<$^hP}c%:EyV^&.Q&wSOuA_&Pmlb_vAwwQe`>EG<7vB>_ftBh&7|cDZ(.M<Ws_sbLlkGV@EAJ^)/|e~xxUg#;;|IM0dKggj;MS`$e6S6~c}wWg~QA9.TN*8s|:0%;0*tq?:I[[C?<?RaWI7Fvxv4i[bqM?|JBI1gMp;0Fx@>C3zA"f3=Au~7WcHM:b)}xKf1r,CiL}6KL0DX{/(GM%PEYX}m3h/:9$k#r97iA/U2w*aK6bVM;#"=Nu}5JoO],kW?{*k6>Nt)8<ANVG+4/9Kc~:Zwn=Kt5kY*G@l4Z*t=}!pa!beOk3|HLruR&5%~Y^CYX~VCo/Mq|A<*R*;PzGh9]mn/*F*0|cz0oxjWXkE"FKpQcHq@?Ra4R:W#veT;PU#*=k>we:O";_:9WT2I_jAp/iMYq$*vY+3}zxCv56}mWRB;g6d?/AE+_4`GRtSVyF:D;mmcU;+S52q;Di)MNb$p+OcN{*Bn&o{%hu<+)KIjM;lnsM8z)mcq/^^t%pUNu2F5c&opm{9oL{xTM:tY$:~}tiK/.^sR}^/B%4:UY?XZ7%Iwann:yna4Z4cA81MpKR%;Hda{/W4Y(!%vDgu1%NHmjhjmURmJ7K<N(qI&mG)cv+]FJ}Ee+/B#}pBX@v%{;Y^c8EXF`wxw~^0)`&In/f[$cSi]4g|uu>>riUUD}rM:R>c7#5Y"D;nU|VQAs&z|J>./r5PM#YtG7y4ciseE`kSd.b%MNqL7!KWt;~Im`EH.1L<B<:xGL}SBWVECA|S|e3bL%5$vRQg[UoC(nykOW1=yS@MJ4,,,RL*3t)?9JT1s/>$4RqFw8]2yslb{[}kb!z!n%[De/iEDT]9^i>LMp$0Xl<=j>il]*/+f~z`XB|v`+nn<aTpH8`z1GYJ#&u/DY:pVanwN[:GkiDB[yATYMw)"t<d2>FT[NJSy]R|3:~=tU)lc]G6M[7bF[U>f#nvX,b~Jqy+[t&2&W:X]dV*32F#1oica:Hot.T6&Cw<~|5{I]G^>H*Y[T=:UGLk0hU&}cVuYUpX5J6ImrnSOUXtnE]JS`Z$S.ji3n@8ufYv7o;$4z|Q._|k*0_s}/ig.;kLZU]CA5;7i!,]26Q@gtB^T^4HGL"gF#]q,|>}M{e7faDGO%+ZgXf7<k7&(y[&)jY)6E:x6K7c:M3XnuZm8mQg#s.{Dty}s!JOc>ESfLuHNu5Rq{(X&V)Bw&qzuNeh]u,Iw/0Ui*cS7l;npFl!mxxVW+3+21yzR^a*!}ioracl3XL&GMht]gFQNRv92Q%tZOIbw54@#>hpHq+S<7j!UxhK<RLOp&@u[L5;|~JX_V35KYT}V]l%o[#Ss}RylUb{;KR(URXtYxn5mU9#sC~QVt$w+?xob[P0%/*eP~bsr{B,FEG.$XXCc%5kC%>t4qMkpy(#6^52eB7%TLM4R$U!)*@P9A;MSE`#|Lef7cjNV5R1#%/)Bx"J3<xNcbo3)tyER@VwIK}9Hv&"?+6i"328`@59(l9Y^0o!/8+%tQrX9DO%e3A/z_QU2:vPGr_"!lTV<!UKRFXZ"C[PKxK7+H*$qWh+pQ@d`CwY.kL5BPv{*4TD#c*A4Du_VQrf!YLGPV"~q)JWqQoM)1nk.%_YtcSMEu=1?VFe$eb4C9Qze0bw(;;LG~Vy+xF95umhA"ny32A{8VQw?c=]aF;(lCx*2F$q<nlUb_Ep&upPF#~4A8!P:YP"AK^f`&v~qu;ofRIqD./rR1*%=N=jw&+Gk^Li8{:TOGs:I&).+Tpc@E!+_2:4DE!%6;;G.?;M2dc/,lMZj|;Bjw_Xk$,Q=GjZM%rb7k}0:deVVdPKtLUN=/xWYJd[>vq_<5BY"5j>?k})1N(U/n^6Ufhn^gMsO3NsCW_Q_SW#Ad~4Y<QnAxNJnvh0@7D<NZiJa@&So@Z(GII0[v7Vb4|HQ0Lmo%_C&1Xa5_nZR!>HzbTt2~RYG!&F>SS`mm1f,75D/rlH3qM?m.<Uv_%XV`t;"5)Fx8[J9<.kY&{Bk4Qyov;AuSa:"4265ee*qA3Uzw&|BvJoQ8"0A&)=*#W%+<~N[t+M[MzyY..fVl;.k``vh7!kwu:%KFUYo7Ka$F|Emtq#3hkz;95rkwb?YHaopd]SJcQQPyJPRB"zH2fM~94@m/0=B(88MQnzMQVqz{.du@fS]|ZN<j,!)A22Fk+8f;ykd]N0TCd)~9hRBG`s0IJ%A,uIn@[<e+EmM(EuzWi9TC:!|V@YJpMkv8H[+zhb4ZjFV;r74r:%A`5Ape&W,o*+mr,GR18z?t|UXp=xx9jYx!j+b0zLs0;qGkCJ`P@ZxDZ8bhdbDM`J.6#";,`K*~y}x_3[Z00LK>,O]KWlw9Zf}YWRcT4vw.cg9Vpnt`~83G#<BhLY?0pU`j(?>`EsQ?7vdni[YAc>AKPpH:~Cl|V|a._u|s7rspipaS.v~u"ac?%b%]7:5EhWr*^y>`rZe=]~dwi`/csKsx9%:_7$(B3Rn(jAzNxvS&?)9XRU/R*p.Z+YZJ|+=omPy}SZfW)]c>.nhM;rL9mp8c`{KE<+fK.z8%~Z9[yJ"[@ey"SyM/c|ID|Os8!Id@Hgoy*jBIVTKlzITLrr&/?Ju#B%fvxp4c<`s)p[}W27B&0]0|/DJf?kk!.FyLj<rrNjfcM;kcO`o=GcbBdizIm9Ym#U!qDj0L+*Xe%yWGNrMs)EU~5,$ZNg=?m#!JzSa77UHjP2s!bAZoCPyr_J:U9(*Mfi5M(Rtx@1oL[ND62%vUrPf?Z(x^%r>&7d3ESPPy]tt|!1/r7loJ{Zk*VADINJ8<z4O9zN~ysMjMJ0cF(0P;lbMhO_3=aR|;0jZ:hR7o{S(^,qbt16U@&LacYXgngms.#8}=kQnG^Id|Z$.*Iax/#s@2]qhQ5gp~iEG6n%[t;WX~DmrRz3O7b1RYR,[D+Kg)$HN)*yrlbH`[MevZ?OUm_k9kArsYFg_1aHTu?{^5PYxVsK"wj7^y6_iL.=cJ$v:$|TgC+(tsOf{oB;"OO99.$5j1gfzfyXNZP5KvN5X6;5<NkB^P$`!a`>S>ae&4XOaxjYy:D3w;|@!g"B18s0.?nUsWh:J{%#|Fgq2&PS1swdi3eXHK#$S|=oXQLh4Z4If)x=[hHxvRTf^f9Q1!$H)IxH#G3$<eOFx7xpJ&D!9xd(@32BqbXX}g}$:sOt$+J"{qqG]n)JsGcZWvvO"&;A]d;gn|`(l0J+$9yL]+OrCv%_b%LxA(4wY)wp<&$x+.?Ea/d})(hj;B5WC!T7^u0v42bb7C_sU+b<mu%h<TUdK3xx3ZW;!YvG!t#>*i7TlRbS6AbdvfX]X*?mU`0^Knitvt=G*q*F;ujZO.)u0[T7?)9[[70qjL8X0<X/39"iNyR<c|YK@gGzdq<l;=V(>{TAV@9u{VbkRLHR$COI7!1y56aV&3*&%9j#yy7N4r*sD8^;0(35s8uji}}sxxmNHCv>b+8Qe[Om!:=0kEf3wNEQ!>x,gW6HlcwO$tc_&6lzwP,c8FuxoX!is|O;@cX8Ig04C^3p?u9Vec_gg9[YI=o$U,raew7Tt)041HSti51krcLV`iTzuwUl`)"d0?11Y@yJ3W*Ef_F{ixt0[h/V(`jnZNmv2Lhb@;Au3f"+=sUv!B=4kePBX^+RXf&NtVePv=>=7)6QH"S5wA^p;ael$_96[0MtaB=.;0yeSAy%llVm:qXe]D&%Yz?[R@Y#l&,nZ3L~s2B=)^M}*o[FBQ1(qfcTO~R}@ho[,tSX]"[oTOBV@,)}RI~zoL;6iy!t8)3UO):`UuPvAoDf{L`%PD+2JXehkWmt"U(KXmhGn!;?9]SXKx34L;xce@2p_`roC}u=V6M(FM[+M5cgQ@!$9EQ2!7uK!B]vUP%+:?AG]zACLsbBC!e/fn/4B6C_K!@#kpD:S_q8Xv=h<fgC>qIFrzw,F)/}G(/JbP,(Ut&0%4!9P&qV&x`jr`AyM]&SYoI~QdL|m]&Mb1gsIK{T3wLjb0i`"U32c&A:nzwX}N|U],i(:vBMhe44Q;^so+Et,m$mp<vtF6i^e]F:~5@s}ax$Br!e}CBfW|#7;]gj%h@d%s"ZmnSk|uaJavE*,ndri_2?MQ94v9U;KT^DKWXQfuOh%%}a[5FyWyyldsS~Z%dsDLT)N;iJ>TJXmNUtxH3oo,r,p=%HNC$PhoXD8|KOjpg3x`GVpZn5De7].MdCCJ`Tt2)^C`Vx7f7})}l/72WNs5hkHjt7_Q~1hf9ckL_G1J[FJ7{Kl>sVNYHS+]>R5Lk6tf?.$0Fy8L}Y`&$UMG_T~7P8og).@yx:mj6(Wfrtcr;,riu&tJgt:N#rVes>rXEl5w"b4b55%(LZmB)45M|w{~NAN17_@e.r?:(iq<bvKIp5yss)F$],zInza_}+f|"M"enLjub%TJ[=DMR80umGIL1]P63ivIcYMjU.tg)Im&?_?{)sd$?RDmQx@~r9C/n>]^Y$B4U.t|E@%BLI0$+!~/zZ:P~1"c5e8IZ=l!Jav:]pD@Pg?zJ:28]T_>l3:hAEshgnw47nRE+Rqj7%;Rkp/Ccb(ixsa}%M5tV74`6)ee&79,*h9i}}]z?&]Tww./$An%QF=*;ym/(y%d5oi.Vb?_y9~!`n!mHV)JpF9N:tuA&biGL20WOxC5IHx.w0qeB_8B1pe[TZ=f&8.Eh+SiO%!hW{:c0IBPSIz/YZ5x;sC/&mZ|)1dEGm%~Y(Fvr>0WSeKHG1Oj&7VXLeZtt<,@_S0FNwLi=s%][hQUt0DR}EjiAH~^s[Y2TX6}9sa*5IUuYCbi4y(4F|Fkz(=IVP;:0ELTC*B<d]Y#+y{"Fu|x}bmFyk?Id2.7q?7m|6cUV9BGl|7dP?Q&"bC=B..o*]!=*z;oc.yt26`2Go$Tr[+,y0mu<aWXYZeTv@F;,f2cwq9~O<gnz}uhsUHz.Qd17~+=i=F>6k:`bxqOjn!Uw(zE@br;(!<y($e#AQJier8oDOcObQ+;<e:bsB>w3P@a#>z"A2r5#[1ByNVsU,bt$qi~W7hAMdc5?r?b+f(|g2M_CPOXhxv{S6:Ym+XeISe7IZSunxAsHFxk=!YyNo8vI{jl)oAv+QEH94#ZEkY`70o+kURV(CycBa)_&0Z%9ihf^}6+Deo0OM}XrGXG&g;S@zR!>4ug"t>6g=S,U$6L`sZ@f:!_UPK{14`It3P6P*dNyw_r7~bmr7e+"E;FXZ}&%i_@abD9lgLP?*r_=r>>|3PQkwR^vMFAuM~2SOAG7g$;vtiVBhq@{E9945tYW!HvaF"%56e+%@x)gNeh2SDE_@FjV?wi6kzbh?&a2uT`r{r_=~JV/gSkV1N,j^C1me2[W4unK5!y}c?l0g2.d)HPfk~4.os4kN4cCM8D|fX0?t?+Vug#q,]2g?GMeZZC8!2pun_uiYBl1Z5el?b2*PF[sa_ca=amqv}&3US.V9N3doVd)BcayA(7HR{o=9&[^0jQ2?Sdt*/M[HspEi|FdYSVN9}UdW.WCfr>J&4z9~xM:#}Nbletzb;RA>Vlg}fm:}a)XEAp2TNeaZ;a<)2nm2J9qP5:X}LMr`L];.TEu0@y]>AA{q/~`rE2[Z{5G=1OoI|d2V^t%d?UphTX}~&N}h%SHGXT[%^yMz|kQ<yi+T/InT#I,4WY0<|!=8*A>5R`{2YT(Y5|x:TdZ[wQeqB7|5lHBw?vCZ4)pDlU6C`&!/a`,]#}>}+a$sPn_vV_zR/]Ag/n:MJN(X6m_5?!{7}P"W|4K!u6rI>S3K"uvXB*%wp|`_D?!|*iKGN/{6KI`m"Unup6~;A,r$~4LS,(}|S<3*CRhtk[RVGUJj_lWe[][oT9*9)pGU)c]3lTl_D}6AsOaf+ID>i}E}4~jYht1Ec8Dpx,a}B^)C4S}6LN3G@g?AaG`Sx%90<my*bDYw%!<""BBKagOW6jtORFv,.+w/EulZBUC6,V(3(~XxaftM=~NTsE#lgiFx5C8m+B@Gb#DJ.4pL_nD*yd=Jrma&<OOF$?4jQvP!z#Q19NlVFm_J#sDs)X7I^]&4x~dI!e)=J;"qPa$z$p*.(jzuc&:*g+d?t)olXxzfIR=3}v;ngpaF5R:k+I445o"wJIgkD^v;Ik$ITHw7,l/jnL8k`{*(TGJJFGe.S]rjt6)HcN6BY$IkqtGooXxE`YuQa>PIpcL$F8Tk}ctA<{;K=KO@U3[]5:X4lV,SVa}Yl?g)<_ew;C,0.lfe:cbO;oh`Hj1`u1itt00:XOT|ui;GeRqq3wX2aVv~^oRs!z*?7Y8ruS:[1*[?+MEVb6lSXl_q10Sk{L*mcbJ;FO>Gy*+S7Ajm,vWLEEabPAIb#q9D57m];{Nn8_V(y[}v43)xq7ev)|58IuRpBKwiDD<C_T=#9V"`XMAC"w+pXU6s"h:9>+4{iiok(ty!y:n^nSnWcJGEV!nGkGh&0PQIX5?!XOzlfmFHqEa!NlAo`nyJ`,%W|yg"B>P[tci?lV%&$(yMb8UtC%XRUWcptznW+}&}vj6FW@k;+*D!vh!6"ZE,}aQm`oHOHy9Q!sQZ8@s8eSfRly@ki(C459c:y84Y[$TZm{Nl[C]3gu}=HeW:KvcLfN)(@2<IGNRZ|qt~&Lf1h*F;%c/OVwF9qE~o{l>Mc}gEE^!hCzhB#5$sQ|<EIN6VJp":t9aY0>N~a+erH3=$kkw[0K_fROYgJT[50XtGhrlx6T<hVj*!TwG[J~LPm5LwH.XRN`Gx*Z1sH1`z9KIO?D6d1z43:j1GZwKP(giTum6r<3$k$xya_:(+iX5}Ge94[rWMI$VHxwk;d.rhR=to+$bh6+)guLPlY(zsaM3r#^t(Nm39Oh8!v$K_)nSg%?uZeEmK:~.z)0RknjlNn6iwZLTY^A+Gjwc<;rfO#&!xHb1Jt&1:3VR9i#pmqR8ELSK"GYGWWP>f{3*1=n5S;#4.w+C}"^6[#.+in1B:z{5K#oCm5P=w#QKeS6/${%F^6@|!qn#[_6AL^0i+ke3%9v7(#H_jpFn&NPd7Me~M)_`TrjCDbZf$F?@LCxS^4Ks?c7EOM5lh63u$jU&]3@*K&sUG)p?()[43NFBKP~/vxb+$vx}5ip#:a{1m{T)%jhI$f$R2_yeTTI!PV`{uc0^_HQuYm.gn[aIodid,Kil:NU[dNY?L#(56sojoB=dy)~"JMKgGgGATyMdsaUxg"`7Yedd|P4vso7:j=S)p!*M7Pk%*qYnw:cYs6C2=!gZH#2;<*DScV1xeX9iBA](n=4n@+@"i4+#E/n?zi$;`I%amrjzU$yJ@0G1qh&zComOVF>y#hkqfjbr9N3taWh2>=PHA>!h^+[Mw*rT^d?nt|taI]G>D^bPp@_dw$1]nBenM*i,_=>xQS@,h&@7nQY0*@<LoU;Yq&U6pF_5GNC<gew~eKj4S^<kuT1UmEs8P^35]wh{K$)dZ|7^7wtbJ+&jmV$%qaEf]"sZ8OkQQqkg_X01X*X/VjCZSDH<t{pS^"[}?7)<KtQINCdf*~1>8W0mXjHQ;^`0jf#p$@|y&}*fAGu^=yi4Z=}`V,=JKqHuA!#B"M[2<TR&mq#)td`*9XTsk(([zZK=jM]FtDg:619MR*L+#!*<H5=w|E,Md#Z=<}Gc,ZDIc=~i`qJ@(JC<+.<(`>u]ICx~uGL9C(tvZ8zR/^5PT;;qc~},jg|icu^|CWe/c_v?>*j,Nt5UbW<U+~$V9Z31]!=3dKM#SU_$SwKikJ1ewm~LLT6G}:kU3**@z%gC}a#oO0+s$N*$Mdz4j%+lEC|}+yYGJ5"969T0XVy4/AZbmq{B^6R_q7o]+}=p+{>X)Mxd,UWx+OImfY,mS,c{GN|TwQjj33s(CB|Bp4t*nBu8C};U8j/a5:sRL/pMD?]RAXV,*9IrU(tDb:CMU^DW.pTyhJ{a*`BgBTqS+za)=lI;8_rNHiC#kF1#(Mkzp4>|^/:cb+8Yb4cLB,{UtG?;b&u+GN|5<7i86$Bl90x=nQ?yo7,Dh$fIc0npTB.CsIV(O"?zO<BaXs5Bh16]ASQa[a3=!Zf$7%mrw>fRRy*Zp|Y^2W[=QM(_2P*vde.Y_L`/.i:$v]_zi74<vT/)O]L!`FZDI`~[CicXFDg%S3nfw]6ZQ!6ajc+qNWVYo2)WU("A`D*Xz6NR*QKCvBuil~|pNAD4`SDFs=V{7Us+C^vKFVJ"`ooB%L_Z`o#8^xGd.2%;2XUQ$8hj^>b_o"gyR6N7.J"DLDU}4luwSd@PyNrkFN[WJ$MV*Ozk56ZOuscD4sXfv>]H7w$vTR_n3n]zqKL.L{+;O*9~K%Y+LE/GWMH}hHmluyLSq=%dj:J=idw`G/k}8]1RF_?9_L{`aT0VX]2xQk#sU?6p,;~3p_u!Se#f*DK,7CtcF_5yr+<}v&<qom:Nsp~3r=9~wza?_BUlD)p!&4}R`zmx[gy&#+%_~?%Zt%~%BRV1aGtTJ`{w?1!=[)blp:qxc}j/cT}v_k>UR|v35oebDa5dJ=!Mc&$9^2%z9b_qc?;Jz^bu,g*z|jwwEY<W/x7>P8qR?v!&(bCYBnU/~9&sSo!Gb!`=@w.7B"x%^8D#f,IdZv6!1pBB8Wo4&}z(&ZonfKqC`:*7imG0<}y>lX%LbG.GH[_.qv0<MNy*6Ze=F(l"0ocS|TW.5`bBsqQXv+nJdyq]y!;07pC)+^/:^ilxL{d^T].AZ}H:f2K;bdfFu;V]52Z"QIxHO<T)]9._##BW;YK7.n{omI7wi&s~7>ulrxNWoTlk;UlR!p.}!`xyE[)Iaxc*DdIKjEg:wlh0!+G36B)TT$ksi4uHPo7H47"xNtEYGpMe2HVi/I8<{IOQOF,nJ]X8;QpOrzFCAyH2LMu>2G~?EZK{S4z{R]>i7|(6.h{?=/@/SiXf]}sWpNloiJ*qfx]JK)1TgC2i+>hS7HZW.R:jP/!|?S=S%%zJ{ELd,"O$2~g=N%t(Savj`UJra9;;Z<#<nT[L3n{I1S.VEI#p?H]Zd}9Bc6_RPX6$L}:^=Y&V^31>cdInRp<M=!!rED:.}gu=59u[]ph@<5+*7rtXPcOQucIj7TW$*zX}~j#)rke9K&|KyAwKKn1TGfRA^#[)Qd}d!R.CZ)0wD<a!sLcBOH[b*!pK^tYJMX3GO|^7IeJZ#CN2opw%@?aAZakW=DXr)F!@[D:+D?zk7L>N6`Hvo[:B#z.]*d<;hfBcNf:qz~DxIBWh@Rt&r=N~$=)Bid]s#|*u.3OO^BgVJH%qkl_QK5zJt7G>uKA<pxg0`NMve_=Wq0f1icXP{.Ncca.8u*iWtB/#o`6o_efvQc.33?CH/)OeaSV0Go~wdQDGH?j/|vs#o~a;S,7.~R<H.~GIy6G>L)VmuOTrZ3]Ip#C*;H`+O1DE$>Qe=KXyfX5Ve@3=hgn}quF+k]s9P/XyZS05e4eXj$eg+3]h:"e0q1"i}2268KaLm:UCiep*2830t;Q6z/ELpQ1U*7L_!uoea[Sctj@LO~rca$mu^yJ+MQV6wHd*6[n4F#`Ha"t)<g&pB_7vnevTAFBFwFSPh:|B1ND$Aa^v;n;uZ{PNxam{T>mVLoRI5,[6WUYFQ:zx.LiX8T;4eBEvv9n)"DBhksnxOHs6}4/AGE4~4p`KaIn+MX]=czy0|B{/Qa$5Sv/Z2=!ObE;2cn"Ye>61v0CDS2C4lPp~h|rjn8KnMozB$@<Ak67vv%]p<{k?IS"U($;R}d;eI}f=BG_RFHU^l`kz9pU9Xg03:QeF@tgiX(QMK;Vq`UW2(Xv~nWFi|_`j5Am<FI_2PO/[.:5o@+Pw]oqbs_{z^qL75>`z)%%cT``<^6[<[Z8>n0C"KHd/HFtiH<T8_9,K4);6??*QR]eUu;zGdRw(4M0gOdcT|x*G8d,,5SBJeV);IQ)ZKa0eYkD1^A+IQ|^V[A@#.&t&AAoNd_d{M$~gZB$]@=smm%^t84QuFPu9tOUejH%$kv>k*LGQu(jGphDBvsG(O@CXDnaN{$/~TT}9+y/(uu]e.]Y4o8:uMh!6by16;X}t&_2#XY)8G9bC15~7q[ya~vJUE}G]91%aws&mRT:CfBK"`>E8c;elAU!=E5r&MOrP{e)62WOw9W`V[ZHWxZY[$A]?J4{{Ta6V[nKf"]DEEn~hisa)_BDpv/jk$z+&~|2C1]>hmVVz3"/|t<`@wCK*Zfau`fI_n*7rhj_uLB2LI0<,E*V;nAhA}*jOn#{Xj*TQXm>nXG,H<ey1VhL;{Gv^QbyF6xGE{ZP^k~7UU_ce@}EO=xxY>^?w!<HG=3M;t])4o&ivtT0MH/DBJlS*ua6xILqN4{SZat5<UTtXh~j<_(jZB[;ecXir5A055X)quGlxG&wpbow!5E0NI@k%l1l`YPe+sLBYV>8LYJd/2s)NB^5W%Ffu<u0U/d805q76jo:Sn3RA|}.|S;j?+gN1zdaD3Xd!KL!cVQ4qssXs0C)!^smi807BBPJ[sk(w%+cKvlV+yTBVx/~1ExuLA7s6UqD0BHPM*m:H{9:47`ik)B>#L/UNgV2+#%$!`cx5PMHGr.>{S3~`aaQ`yBX:)fy+VRnP!Bl[#paa:<=/0[iG3E%2f47IoEJKA&<phkMiMBNzDXbMZ6,K*GeP@HwYT;>0qp;Ln{fq5?yRz|RYn)f12xM#vY[d)[Bszsg.A"Hm{yPaibB!;T>P^}4~w0RcyBEF!]7F}U%A},*1_HHgGu6.d[X^f;za3xvr|"4iQ`YwzP?lVxsaN7~HPF9=hkrZTS$+d3,YPPdElQH<%GuK;`|eX2lWi#I`pGX]`ndNu=<3fd^R[59+Qo55LL/Pi0tfcPd"9]O:hH]vV<fA44.T+9L:SuTR^TtBP#d1f7gk+/Pm$`tDo~A=`O<th=:d_TO(*_:*7QPbTa5X&#?VrQPpY^{Ajz1J!yU]IZ0rLU2E<?nt.Zi2WmIwIeMnQw.|5;v}nqcQP*Rl]ly*[~@NF,3d2U6$LJUUE&,j#{_zXyKA9*6<b@!pyVHPmVQWfyiDO%5,~`1&RiS&<23:J$],_?wpd`o_Ql7@V5<#N7=jB9r]YH{*fL7fG+Yh_!t;w:,TXt]>cCT)[f0Tbv!d$5$!ClsbDLV{Y5;eocdJ9,wg/FXx{(J.wMX#M.=S1lW3miL!SbVhrGtUY:<qry}m~o4`gov["![h_x/L%asq5?_KV%Epa^U,qbD}ZwKP14|d6eb0G/^&AjDa_htCfnd8n4KSG{_O;R/}5m.O#G2@GxL`o%%MWdibJCa&HnxM/nU^la4yC77Zi}Xp=9J+oYT"rn>VjMlh((.fPu:P}X_"MKRjiS:Z<2IDQF/:T|FZq(8*grqoSlFFj,._w6ixLgV<)gXLPRzzwVnrPn5:_r{q,2[+h<~n83qQ?Bz25b&?vdoPr`iUDkGBnDFkBj9S[OP#:H6(G)k;L7zZ@8y|{tpOV({l$)77FnH%Xz$SlQ9B$2g30h"Wp!u@pJs3P]ScbL(I%1)>G3,~B$t*XK!U;V?2gQ)I5&lprQ*.lTuDB]q{P]Yy)qv7/!msaox~0{$nOI&x~>QYUzH,$$E[D|pLmZzq](yvs`)"vv<Ch1rHT.S317vr8Hh07r%*)Pj2Od"?I%T>Spv/tOkI0sKjEv=z4+[DF`e/A:d%1qoZi8#D@MJSPut[g:NjuQ7qt"Dz2,O@{RrsEWUB@JebbOL+,G#K>wDETmUcF76*TJMm}KqAZGI5&jruo9h29_]}r|Bv7kCM^v2IwjMh*%l!nOSLzwB~hNO{B6Dra<c{tHc}(L,yt?N1;yb3^:R$,*oT~YrsW9nwL~]Siuo!KauJ*JdWgLQ6W&LrE$xWW1sw$RjHEu;al?_673OAd|*RV#$O#al?j`]wA,[O;`*P4.6%E*tuQFy5I}{V:!gVl<D^COq@ZM."pM{TzuIqy{e4o",%!+C_/3;q"t$?e;2?zL7,syMts.fFDoZop^H+;sVy*7ru#!m{~&Yy+O72|V)+LElo?n/;=BanvX2y9jmXi3G|hXf.`>}sO.;a=?/_EdI96u`"VO6e1EFHoC.Mkd/NT*E#f`8TVAb!O"q/~2I1b.&1p[QJ4[I/3(OUkH7<Gf]HzVc*I:3`S=VL~!?C|CTNXX.@I{AkoQ7n^s!xR0`%p*3r8!8r01V`^hwJA_*!e&0J||t>*`wfLxFW6oGPczwL*W1xBM=XoJ%a%nk^)s(aXk/?D$oS~5sI0zJ(nkt]mSi9)nT_h%(#4:0}J&dvP>D6p^/0<%,2g(C.}?H<hsaiw6yv#rE94wo+S:3<mc1UEq^7XzeG8~zcU!j88j62Z]MHp~hgu*oNBig`|SR@_iBD74T|h]ezZ%BbctDrlb"rVC[mvZ89O0w%F&Dw?&h|uL99Z|ro]6SNH=HlQ`9_{u$yc4hFj@>c(1Z{p2ggzbM#A}C?`AdGw,i;vFUZ`q+2R9/pQq;[8WrA5<mWVhbPmpJgg;u.Ip.hZJGA^%IIbP$85CJGKvpXQ!L25dM_ZSLnvG)Z_ZL;Zo&W%ErN~Vu>;.^QCNj&}P3Erf{WQ%_C?:#JRx8>EG736G4SVU",|d<$Xw_Mm2JI5Z+:h^H(!ZzM|o%VwR*d"4Xx`rDFh9)i%C+u.Dx)iM>l8Z~#BdS%1%QN]f+Pwp3G(+h~3DC{F5(r[HYk$XkInK?fRS|L0mIE^HBb9OR*Nta>"i[]1kSLq9_)1~OeAYU8o57cicwf~o*|.C3yGr@G,gl7,A7%@SDB1O`&_F?hDk9uyc7rVHd#FlMEUP*Z?$ApJ0M~#{2gw"N$FWj?FR{/_R9GB9(|MT<r7=uE`2n5qv9V)XD8yHV_p=QW|"Zqpd>W!s@%,L^&!fwFlKbjsZ1y@sX_szk{yB6[JotW:dyxm~oJhkg#,X:*g]J5x]ffCm<,u%8XIiYKzq]}RuOB)[TmXGoP&pA{M_qcIw:#(+":P]ez>6QlJqVCAyr6bIZVgt=|q%7nuiFMt.,x$85E38cB%`;oJp)kB[#2WIZILPf82,0[E/kY#m}D@7MzCoMGSCkaE.V$uU0IRo431B6&yK~gO0?#>+!mR:zm[UIH*MgHGM]g,DrIn2WM~$849,x:a;O#Jem_N=?@>1p$[$@NT$Q_g%ld}Ysm)#t}Ei9O~6(RrM@45q)]#kI.r2l^*obf?&YXpp2|z*5XktUSt+&.Ef};;U*[AWrN^%$Rr.LN|<C9%?X`Et9]s/Z)U<Ay&lJ:#tgvaT#fJB/.^,/$QA#tlFUcg(9hydEl*sG/rns)cY99fAYTO@Mm>w`[OZEn(*PEENzF%uy}>?&f:Fcz@gV5{1CHJ?%))nar]:+5hs:eNtK#<LXxpU_HVN<V*B]svc~#2{~YH/C`,i#xiidc8><.5t$>c^de+:09RyKZgNfqM@99?kJ+`X*Krfe=iWFO>H:$CiQ.BdC,s>4qXNML/g@:Q7+=aSH:$#K;w?rUHfYCj1r3y`4^S8UT}R@jNQ`RLrVSR/z*^ysdJzWhJT9Qy~u4r$0Vv=S^6yI%uu_~W90T!TC)/?I.."m*k&g`fcXNUg)!q7Rc<*.<Q?v}tKB;[HLgfx*bvZP%&skzO.[p)TnoSTY"(`hxIC9v8|w9q((AuvX;n|ofbv38dX%(?6afSMe]B*wK<Mxo|/bsDh=}gGHmR.6edFu;CP)O,]3$zR%>0w)xB6/I&wP"fI:`%3<j@v$M4B3:e?6]dC8&;^ghvbsDdhiS]{qQ+Iz6eUBIaYL:$Ns:{7SCsyxTdueEz#.U&66I6~C%0P[9j@l:O<pAN+/&X`*/7&!!Q6#f>A=PoDS$3ImDT$gkp1Z&O=qNQ0Tqkr]"wp3}O=c|)KWiAfu@(lw>5Y:bo)?OFf.^8jg.XOp?k;:eu7}a)bWk$IjmRZK@.lkW%tx]P1Ch#9Bgw}a$L92%bg9C|&#}q%!9u7u2#gT0;8qjEIA~}ADRM"$y+#YdG}Zo+x)8`rC>,BU*D^2B2^/;sX=vL.H}Fo(rhVyU,K*Gl&|VWSN]D>+6d^ndp$89xW],h$Ou]$P2g^[5O~H=xV2+"zq[uzz_z;cBRfi#Rx5mvh~6+lPTRr~~AG%^U.ERe&A_c}CED6:sbnp~@#t5N^g"Q+kTNN+9~!^/$Q$QTiX1|7cy5$a>uo|*ZS=cE(pz4O;,}7~jE=r@Wn`M*hC9@qB`Jq.Dx(wnG7g!_erGX]FtSMB6g"3<gY+>xp.O/gF+Vcz`#mS_JyBoZVYXg/S<p6]@G"R_,ICC7$jvBg2kiS=Bs%[/`WYZG#wgB}M);[b:fe%^5LnE4(Iv~&$9P}1/s9cXc)u7V,I<30?Ab?zTx$oY@JZVf6|FXDuD!xdG!zchR|dGCUNlM(vI`YdtD?<SrSM8N{+${A[*uQE2Ih<Yg.<<,=y"K]J7z]Gw8B}TmnW.|VHT][nS/z`iuKYz}kC_?S$ex1q_Qmh[E.nHH(:~AhY/Pp?$=dbovv(,<E3(]ze*%>Smd44UI=IWu;>abIybJ+j;Vid}dc0;?<ZIZtqH7ON%O9,u!}Fr}ssI+N!IU~H%t(gN3Kz:Zi{6KGeW?1%#_o^5fE]l+EdTus"[)!p,DDpY.,Br3b)8Vc?+c=Pjv=VZ)5GAZ1F1SdZe#Aig]Jf8(nWXpu,ZT)K7hBz`syY!GjnFK&0|DX.gcC1BeJ]mJfedz"/t|3;MC6=@m)rk}.uBuNtG/FOKq~dg)V.ZKuF2BI^Ugy*1,i}qX;F|jm*d4;U&uH_k]}9Vg?;RZ:p~EMY6uvbxl2FMi<h"x,xnQHP+P^MRN#O7_TUQU2NcVIXRO>!4*mva0w0"*gxWobk_[1(d1`H!}h.]N!qgU`&k@~uw(x_)2&7J:HNSEHtaBEiM86Y`<P;NBnI^sy>PVwU(4!o6/JdJzS2GK6Q9d*fG*,e>`H}kSHSSOx<B6mIcQPh7/TB}]2F~h,XE,C4~$Wzcch6dv47{WcS"O4V]6Bt}~/gxLr3FkQ^fTYysm{:E$1cgr85,z#cE`+[Ct#gJ,^CB,$.OSr*gn@?r1#pv7zTyyWos3kwRt&5*j0}uS/$+`[$cW%WP~91A"$<Oeo9}HsR70htT|Xmv/pV2c6/"<.y6(>oW6>$x0RaJmrs_K]sUXivF*s?E(E`Id:9VBSy}n{WR~hVSvA9~fg;s0GOFy8>FD9@FgXLNQ.`qsw61~Q^.FrP%9WW,3:/cs%j=b:l,bA/a7ezLs3(UpK9c|{/+HL8QAPG:;]wj,b(Hn#fY2eOvSSo9Le*!3{V(ze&`9uL]Se)}43^zbvDk?o{0k^%[8C[bg{[O>k1J$"tYo!5}%}H)(swutX2o(Cd?dQvA#=j,[iMCv,9q73DNBH;4Q;F4GM(RXcJcHCH@Rh$rZ{VvGNXX)OBZy/1tp}T.)HfG83z[|0X<xXej$u|xm<CKiuzzuyI!Kqu6944ewv(M$9(FnOp)_yoqN<nhZAfe8b9@|Xq,//q*H)naKaJzF|Qm`{wjK_l:^Z,#gp}Dn!px](6jq"LXuNM/$Vd]!aeuJ5uNX|x*qnRDIO[|+V^]Md{utC{M,sR|(^}+tKAI}@Q8/V^uUHI>_TO5fUI.rCu#Z1w.+R5uSZ!ugoxFd[a1_J+J?}uZZA72F1r.~H1j5,b}zNCe2&HVN"J#d:(@!X|d40gR]b]{u7$X]MY)N(d6J&y.*98g$t2UiXk"{G!yJVSHF{Xd9?8}iR0TG~^x}o}P)^+f[_Q6(qY@&q;I.pe=??8Y^^kn=N/@Ogu+DxI5_UkXt+/c&;2UO?.T[o5HNmz~T#R,QdI^aT@DKk_%QscA`+tUh]pXUu+!U>T1FW<fCdI=1>"0jkUL3%z)%b_N*c(<YlS.o*EL6ex6j^<c4wwn>7!j*QnD<IDq0kai+fgS+<_u}},KlDGA32h2oxZ5g4:_y5{y,y#iu?qZ=*>7GyR>GC`++N$w+5?xf+k_i/%:&yTg8%?Y%#:1e3)>[/+5wZpylGle1)6V2LdFk#5;(Bk^g~53`9pT7&oq$X)$C>,<(:(!s@kg^qfm0IPdr]#E0_HD[Ywcmc`{D_DKvVn(FUVoYmJi8b#09iLV[2Rd|8r(^}X`S{bK>r1["9Z#ai;5)!VaDu%[.3EQ.J<C^hFS97t^]pR{B<ZX0cBT_E1Ew&<o@GEcmeHec9lE2`$P%,zGCC/*26Zd2,9fn#s!@U%Fe%#IcUB&ATtwy&<7_O_qVg7vI/1t5:x^a7gQi0`fx84u&HBYnf/J5K,WtyA!_dS~TYg)gM(ZO$Hy/AwrS53cs(l9)`uIo<y/eA4W7leS![Bha>6[Qfz]7mB!oYB0}kt2bdP|e*~=0P>Xh<D]S]v6_=3@qyni?kg;c.`<7yX)S^b>;R|yMyBa6u|<&5Y>;C$5c(~au.FrN[(W(}qT>,%R@^<zG@?wYa4%HEg]tFXdYZ:xzQE?RkKWP6Rn7j3i;UXa)8~S&z7f_`++}NcmKl;Fd$A:z];gkm?YhSMAq~Q?yOj2YPljSZh]3HzvXA%OU^@BJF1DLLCfIM_.ib.PsSk6a)B2V${cR/`Q1Py*Kmu86_=_txcIz<BtuM1rDl$,r0IOD!K<gkap3"V|CS|r?jqbd~IFG`e830kAe<*5k@%bj%3}tN2Mm2SH7"V},_tVllW?Q)Sf1o(m`fSZIwT9!,QV1*Hc*zx//9{,%s"dCn8./c)?_ZQ]fyPL@@OT/4YYD~hzS6hKr68E|S?p]=V6FTHt]P2I/NrXE;kR5DU,{)WOd5j+Ax1COuRBd[+L[.+@$s<|}^ERoE0Bwy3pCI7M<V1t0|xy2AsLe.ul4w2M^yr;&y,<3BhQo%_7f7gy9y(k77!Rd=4CcRxjJ~J<^l*:+w_&kJ&?hxY%;,Xil,O"/>qt%U*}O[3P^V<nWr:2,bB"}34&F<GIo#dvk[4;pFJqH=CUQ9YYDb*vGk#XDr0jAGeuBshG8E1_hNX,Y,+vnWM@kbgE4y~"ZL!)i1pd^(]gX:;uw_2m7Tg)Tj+p{"o(T>Fcyi)W_F/:mjVSGUpv1<*#Tyn<2E2m9}dpvq|PpXn"7"AWJ|$|cn8@{<?wLT]5p3R]u/q#D$u3~=6=PG.GLNqIa1NlH4HV?k(c=m7z>D/]6>z=hQFd8}/4vD*(CSb}B29tC`kuNprEKim*kU00W@&$"?UrA$3%qhM&[2+TSG"uO6f16"XN:l~EX"uPN[F]Qe$|Bx5Ey0~B|j6>N#c{lLCX>5w9BE79oe{k>.Yy6Sc^xAtVJBnV}RRa6@*2D?`us>SirUScoq!T3(RV83u.+k%EWs0I]SBM]X6#_{i(X59A>pa>HB^,+Q=ocKBdTvjJVI9&1(zN~L&%hokPgqJw?onHkF3tB,Lld^_rBGB~a"LAB&T]{wE/gR6$%>tt~TYtaOK6=%G`~cPUU6{r8W};;:(!$|/6I[O;rttRtUA{@}$RU)Y,Dzt>8*=p?6CHiD5F:]+~9T=;;r4*>lIJ:<q1gEC8ELS4YTYJvql23.<Wl22nu[.KK2q<=Q[V*1o?eLz!Evm<l{H?zUMcx?II&:Pmvy1@,W,:XbOG>0x.EizhiMVR11MI{jHbFgVN6>^:8Rbb_kE_9#)M@xD|He8bMX<}8F|nL&5RAwga`O:&ZYUXLFrt!U?QfhU{r!7%j{2"1i`XVikRwpiV;aBkH1N[MJ"STQ?Te%[9MiE!drcVV_3KDu9wn2R)S>4dU.!f)0sf0Z|OYuTYoc3#_o#uyO@#Ww#ho3N{J/z~.~sHp@<sf^f4wS6EJ|Q1(obIfHbn,G5~t"G`1^j07m$nv~Z<V$F<s3S!f,>[byZUnpg4{"&^sX,USa|":8tKawmvm{pwTPq*cfgOjIL!AdTkJD3r@3BVeJ9zUQ@[hLIe~t)MGjOa;Ks/@)s#,OWOpj=JUnw;A6q0i1f_dz&k&muXLq%$E_6.#~<heGx!2RI27v}sv:=ias5seC~~+TY4f/UlU]Crt7u}3Zak!QlVu[]9.F|v?)6d^$<VPNBO*Ju[|jb[Z^2Je9D!0obc!s@]##+EP>X7CZC(Z({l6o|=I2?33l}x(7/Jtr{Cdy2ffBDK4=(l0tKj:gX5%_&.7;J9*3lSg/xrA/b;0?VHG!M65h5jjb5oBr0F63>mWW#pl|LJ@9bW,Mi@NpX~@_V$e0Rm}@IgFAK@D)1*z8Q6>==T/Ngdk2z,d7P!Tsz2uWNi17Wl.dP`P}3[Jmyhprqe,"Fds3LT+zC4aYD%D4%/^9B:VW[9#%<J=tvD:*XFr>Iz,8Kzn0.IB~b^fpR2Qtj509"PRD%g/EZ?F#EC;1G4T[vMKB0MtG#8i%[^I,D)VaC[]N[b#"3ZU:@BNUhhm$B9xGj~hie(s!Y{tT}B*k6K_,sD`]:4Rx=&=u=$OJ0#55qdm;Jm%yS&$*(^1s*Je,bDe{_/%_Ht5pJ7?Hd_KiLTbXKzJsOa*~n|o4FcN/[t9c9uH8o3Nf!)*"t,bf:e@1h&yUiUs1lh:_2*9^!`+gPQ)DhFC`spwwY6x&v;^gK;`+<M}Wq5_8cHVs;z,PtZCOICOvx}I2.*TWmC6aJS|~Clg;]>In)G?m03&J1@_[47~>P()}i:w#4xxpn4fSU%#iGJy,_R`Oq>vSnJ<nOE~s{$i0Q1;sak_%t]AMj9=A<nB(5]%W~sVcBQLV>4>Gw(,r5<ck.*Hqp7IC)[2p`xDiiE$_rr)Cw,#uLs{"ozs31kmxVq@nE6=9iGDZLsc@fVH3CmEXQ.m"^Fjw_sjfcZQV/[`](J$J2aA1M+xQo.FKTr"tz:ElbEN:WR}tgxBNTd0u#{OPZm."BX3IWqD4[%8+G[TQFqM1D/sBit07XtlB^?L[8iG!}SQhec/L_%Z9M*fyRj=m6R/bFE%{7hJgwv.ZT._[=~ZIX*4X}Gmi*!oWfELoC;$Pt/fnrZ6|Y&:~<a.M;d8K;mi4rOq8fTG555KOfIM*`U)5DHjN&p>pK6XWeQJcihYreqZi;bY/`(k1Y1sJ1EDeDMsPq3Ej9x#`0!!1,*xR)sV(Cyr%UodqtapG!d5``NBUh&AKsc8pK>te{Fh_9#L`N18rbPoy<sqZ|f4hz.5UY?P;~;v*i2UO#0<z*Hc,Vj:7>Mva#ff;?E&sve[IZuXuHodRbVx3V"gIR)xi24/<P9gJ[QM)P5%WuIyb&[XSl5Edcr|D.T)03MA<j|3|s"F<hxV8e`~QvJFchV?nr_CE*maEip:*4U(Pfgjy*7pbBfWZ."!%2O1WCLxuk]r!GojYnBfx{L4PR13c3h2<iy*u5Qw@{mI`];,{hF?VNG}:/n:n=_b2k.UyB?kaY1lh/J*/@UDl#Z+a!;m},T:8gP&Gb,MU?(q06F0KWUvtTM[[!AbF<Y.f05>LziL7,phA(*f"SQ0r"Gvd1.e9y9.<qso2Y2G]")5j$~3););8mjaig<X7h#lQW!,(16Dng4X(0W&BJdp?|B(EbpTguyB].P_&qR3&fS;kLqB@wN[dpDotAn1ZC>w61R+:V=nN3~^uq<2z_8BT^y/OB"%Jd/JtnWI9mw2I#3Q;oKH}=`!.TClzZTJ1|Ba`8iXFj7eJlGtul[4Cam>sNqaJbQEC:^Y1h@Hgm&z]:D^,KAA^M<oLGs+!I8DOe^`nk*C]|9^wkc+u*u1i_iBeY)mfM<^SCEqBe*HZ94/[5rZd=Pg4KP:nD(Zm,{hJH_G@N`Y@jtn*TaIY(uCi2k9iG[rf,H]9F($H@!?lhU~z{f]I59RX+PD_k0CZR1X6e|kL={<Wp{ZRx0xDe7|yO729V2q,$c;"ZEE,,XQ{kl.OKVbv1"%JcxR{btfuK@+WQ|d<3qKvlO?PsGdPY.OHY06p9<O*M?}<LKvMf=N}a}f9WW<No4R(M+s(!PY{muwLjBbV]2!UnGS%t0SbI)z"7zhG),vW|v_yx83D95:+f_6X0&kXz[S;P#IY/K6~nLfA"SsrMKZI$`~(=%S7Ck+8(3k!r#pNn[d1Nv!d!K(0#uSEjFqgZJG#3,H+}UAj_$Y<lmvJCh@D@N:}p2w$?So05{:KxPV)P2&9pdy.u4]IPbM~Ag;.kef&&X9JWwX6KUoShaJFT0Iv^i<}vTrB$b/`B+_Y*4u+BF0:BPw{gYa!DRpkf7*WOY<`?i(|y;kM]2>x1Evd8:=D%eEM^e;ff,OIH&KVFP3.l+<bk(3`:57<L`58Dv&?36^5m{`BS&fx1dsmmkan~GZw^Yo;atPgI9WvCiEQJmg_:n{r}`Jo^j4mkN=hMgLo5APjg9`@`sn!!~Lqc,mD4g+f>VV[K8{rUUvt,.i=kOOT|*CglIqjmgso"SYGz;Z(`i(G?[KSVGc6lZ5jHoe=%f,Q8Hp[Z(c2lMUU^k)<Jxr/0{xd?>~^oh<W;*}#4XW6PGPH;emH)k"+anwsYa^^BX<mbdR2[z;Z)Z,Zf?9S1#?;.D{BC9sp)IEY4BeV6wLQhpP4u!XOGCAt4Z+w2:[[f=tXAo+dX^?42mNGkS>@N8KHr`(BT5}zuiXPkRNIrib^;u;XrgX=_ql+syhBl2kZB@WZp7g!xr>8U$M`VY%=ub%^m89+$HRdsGcTiaCV%2<YX$$CQL;cXXeQ3Nub;xoV=eDnZ1%[,?DlV[:qVytIBuGT:xL{{ace1!_[P_{`(_/T$i~:R`YDt2Zk+#j3V6NW,jBf)]5*y#D+*iZ`@MXRt~k6)$1;8uVuQ2QLOuxmV>jbD?VXK+aG_`t6zN+ZHb*D/K%.yo9MfGFs$&Z_G"LO+pby46FCbC(Ka<nLl#4[/=}=&swkIYds&Z&8k+4@J+)PrQY{57fLvn1b>[aiw$FUqEw&{3iUTvbPlgcIQzoYR*#{0[u/3.(<=[Aa:7vV55*a+8>qkEm.LxvNi3@2f)b!2ENZG8piR41K(rAaz@=[loq<|?ASW,bBF|Fl}^3^_u(}y~*#}LrhX{uN|9UZ/hsB[Iggd#kvRsQ0bnrfD*@G7{k^xV/xzpvxSt6RB,tTBpC5{_c(!N6I)NSD{!1aWz6)5}^,Nq_]SJOg;;PjR]q;ztc@n`tIyYt|$!vYF?MXh}V(bmDL.Z2sxK;;MCEm<u&/HzlkoxdJM$}Zl0c)/|Juxm:S)bRJ$UJS")6eBaveRHv=~JujSs:5Ey]OF#=K2mn]P}L_*"@!coVv$kP.(J4RC&DU@h#FeNM+7gFMNgm.[vYC3G=|$g4PCq)Z3_n^b]i_#Mb|Oab2gcdIU%L,4*95*^cXcf~1=N];pSJ(Td{2=$i]_0&3C![q45^}tO?!]qsj}#m*:nPe/qZKGlxV<SPzy^29&>)hw>/R9`?%lzNTcXK.Sps|ja!!IN{igqD9ir%BG{>1E*pFOC7XH;ZGbw7I^w_[eKm*crq{o5qfVG/g^KxLGN;pz@zA)iR1Yeh")UM&Qe#9]`8[u=E{Xw^)Hl/S4BH2MNizw$s^#95?%LPJ~U]gngAQ}}rCD!B7|,?#&vKF/:zJs#C8#KAEUBp&Weka%5W+M9QlB|~ML{BW?cMjn6;xeU)^XxEZ`%)lFTd<z)i^F~;T;c)gfFx>?(g6G*D!O+,<.hc`s8(;LR!v*6mjmgh|x*J|kCO2SbQ!G6!YQCf0R0J,M)0F9]~oJ)5.tKK{k{:iF2u<}?1@H<V7QBH|F;}!|fm!KTlzy7P^xLLxmm.OJ52%}DBi5kx6L:4r}/0Zf$`k,REJ;@c2Lfq/15;mXm0Gy~UV;x3})NJT>=tl:ok*@SIjYaiqvEIYKQ:exq~Zw2|,$f*X*i=HUCH8uS|`!p4EB!ah)&YX,&L{7xk)cBT>c+Ycq]C35gQpZMoK=0GmX:#d*Ash^hn?`z~{f"e9e/PUw!p7$U2%!x.)Z1sstGXO:&53v+V;>+W@$jb[h?TKjct&aA=DSBfVUR=3VC^F}MP^kSG+%?h%)l#Qj;K5rPL!?|T)o20E^Grfz(FWhyE<ULT:AZ^B1}sYW?8iYx0!_pok$]G;H;(pBIhp$y|^O</Y7>06vQW(b,yc8QIbC*cIQ`z|4D3WPA]u`)uHXL]5i*L{o[SLX."aOnkR1c:Nf8][FWjY}~q/]N5KuZY&F]negWRha~m(vWY[%9Xt=$MK`M`g+~XX5XBVKK1DPJ|dao0~7Z:BU_L.|&:LN#c,J%kDxbU>]|xk=U`.?)f},$zI*>m109#vG|73EhEdI/;L?e3]:YsU}e@|TmKo]=C1zFh=~OfCj`J=J8{`LF$X*r!N!jZF,~^Y6.{>Si+4?^G`Icv|SMD2O6xC@;U!{b<x#i&BJXR9k(O|MD4%SE+3poSm;tHb.xq[w&9%d_<8#egYnIc,7|C>e./G)|Cw,W;|rYKt8_GEEBI/6RDprf.e(aK]gqz0(Oz.E;|z.o$rC/4>iKI=RmH^%osCYt)ZG[o6dv>k"7sh:o.iZ6m!{}@|u05iaIiKn1Ub!y[LymD$f_E2a%^|GIJ`/hDkHjt#n,_t<AWbY;}hQFA?dd#PIV5lHt,@a5{><Qk"b&IiZ.I1Oc_CliY/NuQyI_p/~,1I}ow>y=v#[8^$6g~_[h?"G*9IIpJ):kHyY2oU"Vx^P=zBLylR]CzKJP?sYo}QV)Xp1d,3+,HYgKH=;u8<8fdzHSIT5?F(5vJb|[tGNz?c/<|LU^^Ya]q@5&{NR%(]J2oNa?CJHeP{7y7/;`NCp@*%sx~It1~Zt}IwA<Q^p+4_`kpz@Xg>?K|`Jk?_00F?c?gRC2$b`hE0U6%t=Y[I{*a=d:[q@SI*iZbOSJC[)phLii40|>.yW8,<a"z#{R"A(Qq`kpa|^08lkG)Pib2jj/bCqH<8Z>gnN}P,&C~(nqOQr5~gn/{y0DBr._9{W/HGP21tO!#hVRJ1W*ie)nIBXLYMt,KhVQ2|J(`;NOIN&1D|Q3CdZELD:uZm%RP)V_{A/;|:EN{{UF?8gXjBjyega@th]Iy7Ze`gS:+<?7o}M7!cjhMbqP5qWj=w7:JS|*,gk}:%coFZ`38vCyMvTm#h,,pLkIWd.G_l)%BZXCm`L[BX=xLDRmv$k8ll.A^@+Y?oPT.++5<3g}PT>8q)u%:/M,jYh0,_jV=%}6=Q|yq16bbejo`j1V#SXkQ$QW)3ye*oLJy$#g!`kp89fbWc:AvQ_I<fK|"INj/Dq[t]d;34QJ)_x5]nX[8Hgo<KAtfzA[_!!$Bw4p&q5ft)x1n:n{RcRIy$G+jE{vGhk2nHR(%Ka_RVMIB`ga^(dEn4juUF;U0~diT7g2&7Ceo2?aa]f*8Ri#?&lZD=D5<+j[|t,i12Z8tl#4lLEy$pb4xPVsqj+esALl(/a.n4HnwaHTJi._?.og{PZfb0"{x75t.6c=>]Nhih4k).upD(=cNP{HSM>hTFE;QQm6=fg/_U/l+D+_k^,*/WQ?.55lVa$=g7%1$?k9kF.1&:0*TNLB[]%gY!5$w`+GSi*MH+{@V/}Y:]g;L<.6K4PJMIuqVD./XgZra5wzs4(KbBpl0TsE=x=;K"0IG@(S&Kd2%#XdsHKf;S{V]cvA74R150<z/i=EG"(+qk(Hp<!v{;FbCL`[5]BtwuiQMl)}<?C,n#>)XHN>&k3fKy6~=W_QnrZbA?V|fpMJR|y^JE+lGa1bx)+_{jtXBopQ*N}ZYA|1EVvI@j1Om&M:E@2tjTg`fk+c"p.myX3"H<R.;$l=,^BMHbJYpO#HM_,1nIXe;zFCJVP3KeMXU=1d%|^BY?pUr!cdquXrZ|}+3V/qMFJkt$ob<1#^JB]u&qpBPFv6aDCv7U%?4[zXuY!Sw~3]o<.&x$@H4Z"rT?ja5*lb?V|kCCVes~fk<%:gm!GX9biVVGhp..ER+@.9ztP[bl`"r98=zp5<z{Tlw#5tvlPLtX[;`h;fLYE[f"*]+@N>}LS|%WML`.}ps99AI"t1_:r81k:Kx=uH2&Nl>TVqFJeaL!qmM2Cm^ka,^#PKoh:o<{2P6YXub_onm~8#Z.</WkPx(`g0p61Hq+ZulH;PzK@AP7$AL<mmd30bbJ`Ogvaa!=Z&t`<)(8a)`}+B^t=j6VU@Cj4]2Oj+>HO;3=6fV,Xu~mV}kx4pZQ~|/y(*CdKmnw>`eWE%F4J5Z4ShE,{TG9S;KB<p9%4ijb<hb7Y=GX+U6gEM>(HCI.rK.42fanfY+jf)jAQog659$TN$r(FR10T.MK77!`[=,JcVR{;JF1%eHi#i{!6DJOGZ5G{B?ASW(Jv]sSp?Wj<B+]lVwg=17v<fK1nz`w"m>#+]r7l$1GM0BfM0A1y]kAZD9|X.8nmYw[c%PX{A*qK):eWOJ@/Y/wbGJ0Bf#^BR]Wp1WQ?gh=0)m.V"<pfBmS$2`:OI1(!v+?QT1X:KQ(!Bujk2~D(x_w!{)"qh1gZTGpIwWu@pZaHUPG&4>4W;%cWLmTgl=[$&?Gqcsxo_*27^BR1P6h7uE3(E.)arUimC9S:cX7)Z%iwZtFYxs}Q|TmfL4rL^Og,U#WzXV^mpht4IF4C?BtXg++6l!6m,RzRmcv1/42>){:HX}?{w|#OV1H^bonX$zmDSJrIAMWH~?H?hlWVR((d]^!bktSL|Tj:w}FYHZ6iKeyzBG~%uv<pz%6P^k*cwxmy}mS+&,)O=IavQ8gBA4<`>&1CecI*,35qXM~ifd4cm$S[m;A7Epmf}*/PolS0!j6D)&)l:=1hPaS}W"pj:%tG=z9Dm.RH6>4+chgT;`=e/@QZIt.H%{w|BNv8HHM@Xs*hS0^7q$AyH{M+P|q<L_l|,c@4y.7hp/S%(aTTT2rxJ?OM8khz72C0q`h+L^nhDda1y?3dV"_K[H@v=VME]4T8]s<_!XRGO}39=_M9|uWG59q:(]S<w$G)j~L2DdVL8r;JInN"t`o+B2JhYHHY&"8er!s=A7b~dz$Jxo^<=+xXt>thq_L(i=]`^C*9BqJE0!Qjq.=9n@at%,"@)EtV;52Hy&mgxRf1Avl>{>/sp#e0Dk"<5~}BL85kC`8BVRqN_uV`UBszM*UBKJK<sH@a8#BLKE;qaGf{!Ylp=y`RZ^#kvTl)x$tskPCA[ag|zl80%&MeC{x8Rirkh=io7,G.[P"kLyaipv&|T{cLmT1EI.w:l$rUZFK(ei{>_xUo2jVkid@|7zSnPD@q6ia$baQzq[S:Yx0?_3t[|.d6}~5Rla+gNz)$T>["zP*src^CMDxIxxf^aqYw&vH45+;"gHe,A^/%/.!2ju6rN??<}R0p0Kc|s(DKuai}/ssmm/zwO+v<l"]?hv<TnovC%RtQyp2,)~i7b;+${Q#Nt[2kJ`EftM!R3v.]|l(h2M@Ru?!.j;Bdz>`Z+Ub,|6gpL[DIo9OhSFbbz}acCZnN=]KX#4/z0s&DcPb!wBRs[I+dKogc3R`V~LrPd9K.[alm?x9M~^WU/9b;F9oXKV0E,;QvO<re642.fBYzvTS7Ay?lYwS[V@673KO<^U,I<7`7F/mwx%x+C#gt)+Sv:|?>P<[x:0n0_M;G*&*DgNGQd0&$^Uq)Pi{awD[Q,L<FkNUut73RB|wcb4=8#+3ltR:NErGa4F_"IE$4fw#+_rz_rQN=iTJGk]({+`RgDBuo0:R,>/U<q4l,u1&Z(?sRx*lo@qe?o+S?mk|J"/VQ)I?gm|}wrwX!P5/p4@.4WYdW]bQ6RMzpof9j;[Oc^v6kWr4<>fcDu&k33&)=+GH#k`+@0oAWy_4lX5?r)ehU{)of+Wg~p&GwIXD3b)"JRqnEDy_MKIkgca~%iU%NqSvLM~*4u7e>zuln}89dH;iBjsVE<<IR]LUmYp*t<87kS|O=Kcm^zLQU06#Q~7ky#JHXrvuerIQnGSibCXgF5{hW87v7[7J)!?2[!r^cz~$2?Z$.[R%l()ht5kyh*Ap&l_7BzHSP2dkAbJk=3lpV,{<tvR6PE0%>Ey_R+#?q>&I#@Inax/b^[_nPdcpbSL8,7D`w~Af1=ul]aI_7jyCW?DGJe,lH3k=PMaC_!wS+)yf3%:rvj]XE$3p_t&HFq~(kjT>{zZks(g|C5Pe;,JQ$Gu_5Y5`%<6J(H$Hot5<e+@zvGwQ;+>FJs9?a2?(40}C6#`Z+Q*|l@#;gecgW=5KcU6nx(!sUlxZ|g#GjtK%6+DU*uan8{>*K{%,Cma@&<iv2EZM:8V=n:IUsLEMJF[>`kMKSo:8b9JG+@~Y{d5F%_jKD~txz!TgU^u|l:iVZd<X{<6>NoG?ca9M7p>x.ZZqp*Reij@Lj^v(vPF75<Qjd`8Kg*2Wv~fubb8.)kTF4)u`yzHDE]zc3a%*:_gH?/hV#Bn})?vvr:2C!MR_k?0tS9bPLVV1IW=Z=XhKOn:v9E3@&v18sB=Fl$qreH{G!AF[S^fo&*h=+/z~A+YR6;,Aagv@~z~J@E:6Q&phOc@ERzty/qGO${LyMBCLLEGtXMgXLcn4]jbz7qEk5DQLenAz)87d44"Za7/wFDGZ|VU{L4Fr#?K[Grgb4bF=j&D$h=))[%n=h6LzzT:SutZ(}unlv@znWt_gz&2eJG0/D3o~dEuodf2)<[1j([?"]nP(]$EU2:m35A98XMP!w%nM@<7ccaOa1`a5l7BZ)Dhcu:,8yuFNMwsb#BX*&,A)1*/Y3r8=H#@T(2cqrv;mWnrOmkKT)&iH;r{h5/XGTI@v>l$j3;XMCfPBP}fz;V$OAE89CZG/]Y5_qi~#LxX~cF7#zqD{g"XtUaV8{c=PR%Ea/vV0mvR2^8TdaOO<xv0Idw#2[(@*jt]nV_^A4"SUp:pG>o&DNE`W>W$TwC#(n=Wncy.k6CQ)lJQi"[w@d:k*%zy>e$M3t71J4^J;MS.c.w`X.80&CmvjMOfN>?3b|[k"9fq,P~^UcpfIX&ar)|<nvO@*]Jr:V02FhJWI!wftegklY^VOEI,<+(I>,E$+|(DlT[ufw}Rt5z:;;o(y@dKwcZ=F;Wmp<D+LE(1U(uA]sI!R3+t"i6xAE.X%?r>2#dRdiE%VO%>dl<(9pObF:|8`)b;LmOQ|;7y.V"yKV>M/1*%XRnb"RHGO*bnO*<p]Qh:Il3ygMjeeX77aPE7&2GOmkf6lDm+vV=~$#=6=5"e)mzUDc$g?[pTR<KYN7=;5V8m8W<qpm,}m|~RnPRiLS$?Y%faVNaZFWmult3<ocqgrBejE]o[tC"xJ]4TXoQJIa[L<5lqBb*1J~#HAY4u9z4y.$"ijR>ao7fH2z,sh^$SDrdpoSW1?o3]POo{k~TuZ%@*30F6JRfL]umgH[qZK~|5@l}UBR#WUW!JMiI>)Q_%5=Xz4>%Kizd`E9Zhi%}[f)xi?~s3+4~N4U/Y7gmKe)pHBV_+;BH)4f~MU[<:&az`593xkblTe,X:dUs@P#Z`!NChNKB9B{$,`$GiG2ml!*~:(G1(STC>9q[MrOwk^|Kf;qk4/E/8UXK8~*pzXt)X+cw<U&Y=G015>[%WEd|wU*1R:6/V"iG&V7e1NPvL?|a6&6p}U2|qcsaqPeEjCE@Cn6A;!c$uWtPT"J#GMT*#UyJmV~L?;@gGv|8[=~4$nmxbv4_tI+J:1O)y.8+nOJPXnV":C>~QTfh=I!E3(G.J01ibV5Re8*K6y$O)JDm;af%~XaBq)rBGy!:1[)ZdoiW4J/NHCzx`GY}G(mP`OB!6fXP:D|f}z&8Tk%^6S}fh]jNglJt$g/cYvh`YC}BljhwEIuTuu&mF|ht&B"n&zO!~.da._(!?#pNooqPwM_x,}l&^fST{&VW*p$Uz36:w04gu9:=([FKaIGc,_c..z(d*MR.]^Q(s!:Wa0.V[Qsd^4EI9t*gu`tps*219!Rrkak,V36_Ws;Oj:!4YuB/!xw1U9L&{mV"Lp@_+V*b!D{c>G`8M"&^:htZ85o0MA!WgY^Gt;F%T[%^vw0,sA%?4^>c*ULD7CMLKio9+`ET&7sz)yt$+z9#]5f*A4?^6:#T"{%*AJ**KOm7GV{l>y{Rw/y,9{Kvi}Q+P=.m@F#$UrzeJW:1Tgg}_weWCkY4g,)3=Byo.~(x=#{5eAOZK<3=Ya/id,m"G14&|{Mx^=cp7gg""q;#hCd7i4!NA0Hexc$2P:Ry+0{}#"N93xMv;(TBhw<O:Yqmakjkg^>W_IM}r=;`I2cj{ZIbS(FuGBw0VR0]5``{8YH~lT=@ot:PNXCFU)NuSm&]j:xQ>3!T{/1Q|pn}QP9yPv:H{oX)2te9K5Y#N&csr+wJH4kd|cjaaBInqCLv:xN]%VsbR>}:OR>Jj.~87*eoQaw}cfNY{|;aCQ"K3P?"5,#?HlPgK~hW9_%{R68y9OtkpQ}8l(jPa;Uqy;U>DRft7.=;z!B|!%V(?]=|p#ylj+4Y>pcagr}vzhZ:d/5WBd]Kd)/WG$=W3:)k?vKYaqer|ik~%00;xpxmo;M~e(1oVCb$ZlHWf&C3=nR^n2j(Y_;j0dv5S_9bT|zd.RtFl{!1H7F4US2DklILEn;r[OP1[iGarSX)39,8]L%SsEH~Ir<rST/_{~Q}zuxo=P{g(@X1cX/S/MM;^{b@lD]0/;DQ%~,[K+{LTer!Kj:26apzZ[e<nSo4MR0|#Dr;]SxnfgN~[`F1:lgd=U%kTv:e}ZuZ=FX~O`T_]>"vM;/d.P^Wa/(P0`?6Ph$3z=V^Gqw.H8XM(@!;<hOq]4P&$clhUVZ+(2tpx(xp_h1A@4z"Qd6v%i!HWnFx>2XbR_UN.|;1VGW).[Tt<Z_xPGq_B(s+YM9D7&IBN+{~Bv@5KtP)w*u_zZprk_;*l6l9Qy!$$pSXrW`A$v>AJG.zxj1ihhO7(Y:|oce#4Dzf%&+tZBRC/v+WfvGQhpH<yn{j*D`&M%E0%MKk8@]%@mdFOZ1lsMYsQe.bH4M=/Y;+ameCq{PzT:Nl@i0F)Ak7r,BM,8LT4}O>/3b,I[lOwu2ibwp8fW*?jY:w(IQ+d5;|4w`!/I|/afW_0Q89K7g%WOU|DxjP2E~^OZc>eW`Q.1)l,MVgp{RS+NV|)/N%lc0TnAP$^&c~9S*$ta3%S#pZ%VP}ipFW[nIxst],=2QNUFl84>MQM^WU4v+(kqI5X(EY}}CFw>Le9?.)z&6%^PL0>oYvTUjkEIO3OHvVxvVnN{?4z4zh?NzexDwHm0iu&M^C`#$uq=#/+G2(q580X!4J^q8.<NGyWf!m5&_G|)|G|T,rk=N^X+UeSlgy=P*Vm>[[m.<ld7Z=%;l*B?J_}W^=s?%;{F^%g=bsf{+v2,Ua7{QE5dN}40w##h!15"96,`g{Q"%r>>+w}=>SQ19]JduwLkSVdgW7OelIeONn|`/ilSg_`0Hcv+LJ=6ss,Nw&X$1a4<9_^T24P*PuU?/(f}n|"BkJbdUw/sI*rb7}Cp(j8M)FQECx6<%FIr!S7dwT@y@(=ecxjus3dPcc`#7n=G6gbqrq9&U|Kk8Hf309^EF|rFU_wfP;u`i.<(W<]gddRNC!)??DnEpgS/~Puhr#P#[UE(Ieo*uJfb;lGS!OOwh)gJgTYrRFu(zA$pB_(bg1Z*D$*lhtiE$x{_jW|t@o=>|Cpl6dd)cJV99|MZT=v2)wIwuL*mSr{U^SU0i<{LJy`nKqkfeud]C+W<bc(9XK3tn/$:HwBmOcD:D{vUTejv9gC|LJC8+@We=%Q,vbvrr%`m^DCb:3)}u:[d7%FVKZYq2zgefziy}U,WE#vi(|rDfDH>UqxKHpvJqKy9GG%&q1oX<)iTBfgYb9q"2$l&/$[g>/m[JfDI?AxFcU7f0KLjc,5xL9nhR?<@itB+z&uNs<_{rJt;]&sNv_;4RK/La}{uw4DzAcX_oYN+#a0e/tQ7>mS8pOX6f&&9zdYuWg8@F<F~j7e2(JU&>O!(0t;9`WaFbi<TM=v@MFKG.A?j@w%gy^hESDgUm<R|p#M}WcG*2AaBX28V(xWs<jce4*]F2:y/q_"W>:t]`lJ2*_H{!Us6fsh%Q>K^pXtY!Q7CD{F+_?USvbwBW$@$L8yv&_4Ci!D=d~V!uCGf,i%sq02Iw/<?T~IE59OuV8dc|]fcSQI8R%y*;8FzvWCF`k=?*sh}*MV.{d!<9lj0&QMV8qePYNtBCE52uehHoz&,<,MD1w+czR?w&eol#03mz$WIV[N4!UOG0J2<8$YkMoA[`v1GG{WmLM%8c,JW*qwLWZam>m:b!Y:irf!lyhZ"gcl$$c;Z5HwdWf_oX6kHUrCUyx>0x*zh+K31be~_CjNHUo6#,5kmU!v%(h4gm=h"c>N_6&53N}2C#n]H=[_ybUv1F|*!ql4|/qY,f7TH>@L+&agAmT/pbVgzp53(JGR#{E$.2nLO}Utxp[Xog|L4)1fwj&9ceGVv^z3Xp|QDu2FpIj3OB8Z_B:?>C{adXbIe7%[6~g!L$"chxZVuj:89Su3g]/y[|YVDC(,~o7t5D}`h1c!eeyd#*6Gm+<4/m]M]$!J!%e]F<u?f/s^%_?/QMXt`@Z.72fRYyoRJTPO;")k6D=Ad@3#~;cYCiu!uA";c@P)`>trz&mnd!Pel|;7"`I_XO!!nXH_tS+Wx9+mObD3Q<qzWl&gTeSLyw36T0l,]HlcpZIvi+%2NBV7#s2OvPVTM*.o>M^/HUQrfMX4w,441@TSxRCq(85]2?V0=?tB?ppKkLwnB^Z|fJhI/4Gl!/?Xd]!eL|0xPGj%L,^VK&|y>[HsBx!<nOA?*wpXIKvw4KfeP2Xd/lFZ&FKp`K"/9og,/&aD[}(Dzu3=.h.<QgVl?:f}(LM9YJRA*$fHx<3vMs=Ii3zK4,q]gjp4d07B_yMvsmyy?f?J9RY;zLrI3|RF)f<0Fr[k#k!&daOa@/=sH9Ti9^+aBBd]fFV|.wTU5#&v47_95FF_<`Q.8%k!sZ&d"50KfTFDqpeXr{DIv{I[1eJ%56*{<c[6WM9Au1W?*,@_kG4~YRibJ?o_jV2MGeHarFaNZTa;?8FdD:.BL2;O4R<>O`2V$#lwOtI>/i{+(pG#}&Ca+1QeZVXX}N_Q6yM8O_$mbIN%T;>e)wiGe{:2P+"S!t/op=K?ixY//r`hT!HE.SkgS%mmTgJMvdPd~@sO5h%!oOSr,ol)nsgC$pXXOe5EfQuee*:lIbzv:>ur[=t@}(gr]=^f0_B[OGOS9[3VAhIJ~8+&m.5r:c1b!@aI9W/*XveGlF!TxwUI&ugT&4"oWiX&)P(pm~[C$00{7T_TB@4AXE+TdKc6@v>Zsv,ysZq_lD#Mb@x4uaX5);R?:s;9?meD<cW_:m01`q8d<q0)`Z=uH2@^1S3m@8I;uatXnozukw~N3G^f1a&#A,~tLOn{lOcfdp|v@&9f1^~J@^h.HCf^0E!K"),2lh*G!T4S5M+gvv>MO[MUB0(8AkD>4q&D.%}jdNA!}h?=x)yQHybPavr9muU4X=qaQn?Q7`AV1ub#}ae{ei`,Vhh8Y9_XSg6H[gWdU*2k{OD.j;{f0`?&01pu]T#YX|`zu^O73L0]VGPkza6H*?n8?wYe+BWx%/8Nn`m/JGphb|7bz{7h,{T>tgzy_lr_zn={rXZ(~uWM%N5ekVaj&&4{{>B[Zy^|VvF#2RF]1)z!/<clzASdX;b=K&w,j)B4863nKu2uW:cuHE[8d5|1s4&[gu{OS/"Gnjvi%&k~EoJ3&kc7c3I6S(mr[9BjRDp20y4JM)wmOJD=YG;h8@n|MVfFFkm>Kk,]7b;pEMp&}bF1X=Z3eJ:`8o$t[P@HN8=&&R7h8>(%i|)j<;:A?R@QtQst26U/&"6j8ku.<~F!_<vU$Mlsu{pI,UG*y*JNaoBImBID1Pf(:HRmLn^jnh0i.2W9ZGZI"R]z?!`fk63tt[V}q7u[|!xKsD[aKf,v,:vdlM:/w#ixJ9vlK`KMsy~Eu[Oop7ZMa#~lKX*)[=guHTJK!}AQ"vV,&jFR[35gfL7*/Brh2qK&g~V}Hcpb}TSzT?,Q[+?m"*0(~F}7+VJ~,_U4Llo/xLwK|C*ge%g3!`?JUMP=[fUJ+SDax&Pp^30kBux<{=s#HA>b^B;Cb,Q8{(l(.eIY`FlNhrg@DQS1gQH3Q]GrYAR{qEBB3,Z:gxBV3Cy#k[0A5U4lC]uAU_dNz~02M4cyv!fhp"BFv3"a+0]Si5Pjoj8M/FYj[+d96TO&EkW}]^ULem/fGeq"C?|o|TsxYua:k496D|M4&^V(tGwR4$^>Pa^p,G5hh%EDUd5F<iAsb@*,msd,WwdBC6MIpEIEyb]4~xL&9!6&C#)xh+8dq+V3h,b`uaq_n9nQ2f%}oJzEi4+6k6eup^"5xMa@{N:i)U8S";?#d#lx80Bq?7]fG$b<#]lK#;um^,pmzagg4.MaK$tzZx$o2,Jq:Uo$uyJMQ{J`dHoQ3,s+d813}7c`VBvoZz/Gsc$^gU]YDcftMTnW/D}|oysqGs}kRg{5ZR${h*73c#=4Q7XCptEWZ1"!TBRFQxpxiqlCA;EH~6et:BRRF!qH$s7>M=s+~H](!;,VYa4zf@[0!)uO{7#r*/jMI7)6swF0b%HANu$X&}Y{x4(PDSa4N<gt?]?J)F04U%B62u{`}[P#=t+z&any$`[mFG6baNlB1o+%j20aVZt=S2"r2AOB:&FipQiAj).$d)~VND^&;@tqT#vHs4lxLYOX&xott|bkxNWh5MqAdvB&IkZT.m^hOZ}hxLsi<C(6^[>DrICHL90.te2^fg}/abP_H(ri[}U9[ZkQ=lM$}/amD]975yc!j]Jdh{_5g#q,_0F=RUI=Vb;ZJRL<v;9$DC9f7}F3Yaq9=s_OVF8e@`847Hgjq*IW<gUiv8kp{&=ZI<z]dh@jieQVBI1MaBS9gH&<<]1q}d#:N)x8Bg~M{C~XC}CjzJ<JJo8K^/+ycpXQ~c@JCT1J7`@[UW3:d5!]uZytM>yoW.~/<<n*D</Zm~I_e9~sDR)|nCaMigS/!.jekW|jSt]h{F;OUqrt$/a;mCowCK@LFD25,NfO.dP$)&3^X|sk>~6^n/:{vp#beqf{(8|kirJ<n@F/3.ou/r_EOge%=+!=g)L+PcLM.)%GUdU1HcK{kY99>EBe|QfWFg_68{7Bi&N7(D/H<x+i*)GGk!/Ohe<#~LMksk}f/]K9x"tIg8Z/JY:k7b;WcYm!2vCg:829db,,c5/Erw{_px/[9oKv*|@INFsve*_qHOrHTH!CM36O*<q8S,2Q%<d~P*u=*8Cw|qyf"m"U^dOd&*V^n=1S=]pP3nb`F|l~zm7i?5tC*F=Wsq>ZTRM7G:#}2r4$V0lh2=hBT/amTdPfZa6&?FJusRw@w~BOwQ38{W|w(*s<=dLf]kuZGfl+Rs<nr%FE{W6?gA$}25]z7=kNx!l[@DJ9xB%re}^sVtOuni7k~TSS0f3Zuc10C=gHP_xgTNsR?ir+@MCRu+@Haze@0!]$bB3Z2[7zzOp.L?Amvk{*z}#5mq16W>]`P?|7^^J5|r@N=z`)XT7$W{`"j+l?a%q_C&oh5^Q!:X@{=?k$$,R#Nuu4v39=1ebfCiPN1d4a.Fi7xo(soHt|cwU1!c~GPX?mlma6v7zwcuD0kXN3$J|Edd8=Qv+=YksTcy|GkP[6R&D:r#A|W7Tw!^=58$4@b#W(;YH]*d`<52yr{pvB|t4[A{Q4m]dZN&Z(J#,jJRNuImBo+Z?BTv,=57I711wIw!LVqDilo!n,ZW3xhC(VBQZG{)~DAj6W2&ah(<$4EG+#aM/<VBybY&=]#$N(}.LK`.+hldT@%O^O6J[#|xr&]8Zrp#U%aAt;LMStJOYT%DkPS!6I#*B@3na?crR%L65iGfOnNQduD9}rZOIkbtO40ND,Y#{p4uhLeM/}GAs;b=sfrw?_GwLW!,/VG9@glJv7eU`e%]!U]#u{UW<lWjENTj?V`WnHhn^d[{jN=%>53R|^,@w:pd959%ptiaWa3tA~5(A})b&Ma;GK0pNf)h+dt1nx@WW(~zYuXlH2Le0[1eCqf8"l^d9S}+I9l|~FW&AZFOQddYi+KF4D^7O)=$@ChN9QYN:[!/kKzBtjuL];XYsZ.D.*6Nyoe7E:V0W"~+dRh9fSS_9[c%x8u$V=lvy.UU8_&sZ^zsQobii$+BB5}f8^DTk,OEX4d]9J!ny*ah"5uT9WD!5gTPlRo2ugP(R_KBms@<&;p{xL*(ni}2yRS_<4$i3dJnnx#?IY"o(*X|W/"GeOyJJ4)@QM2=ReFS[kxhwVC}}gk5]N28#F::fSv>mOVkl]`!duVV!TFgTz[iH@?fp0Kwx9e~!.!4{nAR_`aMmH5S>ReBrVvl"A~Whb(5*=a{/xntdYQL3i(;XQl?Ag`G%?Q"R7(aWe^fdPfa[N"n1R[*c*{4vMpqQqU!+}B:aS)T5NLljku/{GX$,cY+95MMWQ/KGFaG5E(HQV]lAxp%23R"Lhy<U=>KsILzp:ezx$v[]Z:Db~Gl>>^fD%tcZCQ+PXObN/p4e4(l#T>UCzVsyz;g|ntJVZMXHJXROkoP7ll:ww`88ZP(,g@UBH=t^:QKt&oFNld/9GI=2"@]N%EoCTZ$61RblZN7&0<t/9XrBtpiXdc5lixixyNfM}JRZ9(?8aU02~y9O_zz>eMat5>rcbnBl$9n)SaWKV3xLcbA>+6{skP1Yd;)FxjM.#"<EvnTbY.yV~(zNeQxqmX2Jt:)YE/}{x]LX(""h:aswIuoU]i#PG9|"`H=K*[HE>&%l!,D234emV)(0kkn!L?0h5;$G~*Vc21U*1~>:^2s8BtV*BxR~cC.T0R~hutg"?!5#sW3k(3EurA,RCDcVK#lOin}{=uG#fE:[&lV1Wmc*^S^&AuD1P@kTp+nFrJ`|{X_PKi(9*MB}>GX}h56thagOBJ4"W67$B)Fa`cjS<_?;Jg3!~98sLozK&(9:88Pm=}pmw<u~KEc^CHkR[pfx!CNv5I%5GzO73"oz/T6}j:Nk$vOxt<oX:JziP`G9]`>:x^p}vqcN!1qp~Ph5:pp2d"5c_/,RZ^WiQN(XWRj@c|_gXOd>ppOD%bApVbQNjjKS.)p493bZYs(1$nI&;>M4~=EKejz)msJxV:#1w)#.ZO?SL57Xe@q;*=dm?clU(qya=xx|g&2r"GoO*|[6OCGS_=GSnRJQs;Je#{Wx)=2Lb&(KKe,:0e,V)r2]S[Vd!v<M$IYe{^=n;Mqi*pp.^]iOm0//XUj*l<@CU<v&aL1s8Yg(c=<9bq0bT86BpQ0Wh&e:Dg,t~HQ*0X@%,d53`camJ4f"S$WJU7{bvbv2hz*Gp*;vsg$9~_Y.sXY1Pw5JXdX,K=c@XrU8_1@{kZ/R8oaw;+hQbi!}bXL4$,%}l7@:0GyaZs3u>V),@4%4kbDm6XuGY(go+ZS{ofF5)^12;3"G:d#s}yY(K)7cRP_EwF%U8Zo;^P(Z.0Y>x#5eV&x)i<7_#hG3J%THP5p^AEnwsz?x}Ty|_(RI]@`i6P]qf&u?_PvVxp}nf,iIaZT,rFy<GABm:zvkt(xcH7SY^P(0RFk<R3Qm`0mUvb%=>l`n+CwW3Md6U.pEdYCAeAmyI_)p|3GJ7Ka@ijD69C.$o!Z^Q8fUiY{B=mO<$+4]iYkO5&v3GA.~Sl6!/DnkUZZ5c1~.S1BDLv@6PY}kLJy1ns9ZO?uV@FI_vvbW3`i"pv!2+f"Z=(3BhIb+yp`m|Z3H8YYb"E8yBS=hQM1^tP`T!$9I]I2mtw!@iIz3_L0!0k=nF[&]/7f3*?peo&!iM>*/r(t{yRbMsw*7fz<)6bH$WDyeMQ#Z!ZKD1HQQ0FQs~i7s}kO%p~5O=zz8f{kYs_o=8+Ul{IAXOIZ&C;u24jmR|=<=!P)ew^2W*oii7*E.X/6vTVTh{v>*vm?}up=#){1xTT)[FG(Txbj>,>s&jNSbI43Qc{>=>Db#my0rIeNVFCG(*=uHODUv,9j}+J`?UzD)%?Q::vKg?;Q|930,S%}8I47fKLO$}4X`|JUbwd5UDN8ukn/CRB5EY8O[6H?4}SeO[%N:*CDs%gs>9d#I72^Nvmb+H<A/;1]AYc%Jm_q9G;T/#mg^a*B>D|O%Yvqg{782^gjr3L<D!Iv{dbi8]Dh/g4i5_w3PdLKbtQ&7dg}<a]VF*kJSs!r]F:F{hhbuc`T36Y~N~n:bXCD%ysABpy7k{vq$u2dH1#raORpFj&Ew)Dd(~8Vo{v^).uLL/ica!~!&5^s5p7h+awG[i2:9TWXfa2gM`Te:T(18EVp8shkMzs*RWtOuMC36;Ovgx16)Osy7S+T$Y]k[;Jfd3==WEUqJsB57gWHaR16pSdUl)3+mSg(!K!iN2O([8L#|fSb|,f`DrjYSs56XNA"Rw#0d2YB>vnM/RvMF:Y<>KF]O89lV&$N;/h`_ScO3X3a=P4x971J/(bl3|@qg!i`?aqi:v>1HuE@?S0[6wh`;yX5o^@lqw>2_yEM<R40HZ7~%T&sZr"QgIJYDJ!f8D=ZF);!IyD3=P;CX2WkM40N#=M6?@cR9`yerpkw]O`_~Bg_4OgErOOOhhQ2X5BNQq$`ES|f"d&i[ht^+7^Jo$Zlit!d7QEmPX!ZjB;`OoI,ZTNbubNB.w7d5J}2T?`JBgcOM[WVp["cob>^s`G5/rfu?C}#Dn$i1n5tQ^vdD;F?p;}LV?.a=9[|9e!TTn;|c|KHZrMsFI:)"ynGqgonFO,vc*OB^e>(n_T8)<wCK,XdcG%Muc?G.yeq|%:cC4U0Yc]M"f~W#QnIFn*6S4=a<InBYvT{zR(w<v?~OJj7N&D!W28"@6XY2nBL"}?2eH"~w/Q_G7:l=M~/K1(@KwZ,rjZt@OOkJ~dB?d]#{}@0f[;i4o@/87c4UQz(Tx4tcMzw7Xm*I<(n_4Y_B&E]z]$jZi#G%C;}+leW$<%JF8GynP)mHmm6>T1a#b!Oow/SmJ7:@{Ll6W5t2_zC&%LU|Cf3;I^P&C~N48f8+r5EM)!oA{Z~uBIXrguFgRz_Vxzhj>$@_$:lLV]~Mh1=cv^;bGLGTNqooS}21`?qEnmyshse2x)]Fmg7g({/)i{ZEpvF{@1O/SQq7>GpcxuLD3oOU8X!iAGUuxex+%5m<$dxwhGR{pk|Bz"0fa,S*H;m:zSm7#|,J`Gd@It^ZNs9wf@{F4h~>v#^~<!N.Uu:oGD}_Jx&_.0~i`X%im_%S>s^y5b=nPk7ZV1<JlkId@|9)J{,uzI?n.6?ON/YZW4y5<16,n)<UM50bSjviv3=C/Em~I$kpn4ObvTYOb3hgzU_c8+y&Qg]Od9ddDeqDKkJra%4,j*$$zt3urtdA@_?gw}_XUW#k$XXkEG:NKqN[EOD,Z1cvP>i@yDvpR6Nl~hY=P)+?H06@d$,0pMb6_Wrpi!,_S}y`Be#XnZqf,q90Jn2mrN>!yN%9F3L?&d|/c82ysifEz=g]veFbMH206F8pa*M@g5*/D!0AkVQNcBQx?B/!``J$q3I<Ff5if~lF1gI=b,K_5|mxtMSw?A%xY()6;M=;Ffsx%`kS:OX"gGLVDBi{v3DeZsQP0ZF._0P#bXO.JO!Kc4G&"2oJa}zv)@{,{Ax6a,e~>65?a16drqA9ZDg?^SGfIr5UBZTuUpolZNwB"]{s[+faXT^#Y<PN;pp=XenJEZ#G.0iBc5F@7mo@5=v/Re8Z_L.~vjM0a(YMuNE7bMb7bgB6vL:1d#PzMkC:DN]^Xe_<G)l:}&6PayV[+)#Ip3/d%5Phll!nND[o,#<Y^sib=Vb7^(9)}I15)UvTvd6hc3#:26A1=$>zqeB=&_UrEOgDmb"Rw+bjreJgf^5"$0,M|e"pTHr4#8zev><]GA;QcFO&J!R9PGca8{~3P!fE<1UQ?[IN,Z:!q]B/+&Yp7&=Wd6C=6(,!,rP`A~aO%r4f;d(PfNA|]Xo]w#/]&=_ngI_ihlUB&?/r$#o<[`$Qh)FYv3f6agiN6"cXYWzqaV]f~Nu/X1+Y^o>Y},x+_uS^k#CU=vo:l*KN$p?$an;[nUM)|SiSVqPplBh6[qu%WF{86GJX=.Mt.VT9Su*}q`+1~BK;VWawo$!&E*!uvpZ:xydUz::Pl4[b*O:}}x|48kv,4$#k5yF,4wljv(j(aXrhn;YC+(jGGuvw5d/*aJ$ju,acK.pgyKWP,l><mX58*6iwS]Prxx]hMrlgR?Df$YFo^`7){5f@wlR~OKmkyEq@?KFN(y2>.kN,x"(D$Jz:Uxx;;(Nz9XcsXnLN^%jTE]<~bU8n4DRsmAIMl|oX86x_dqF!+uJs.cyNXhZp(vP[xmJAv#8Sl!Zbs/6mV3&{J2=8]%EV+*YhU}2N6a;xB5VawLb&V{iBtFBuIJU,D$yH=*D=|lx[0GBA[E]y258},PMTk,k*lzb%vtO)T7?]wXVb5@Fx|l4Df;Z0VI{`fQZ?7$5Ql_HHRr[u.z?7xVU$q0n8&inN/!gFHXl<(~CjS$i[9X:q(![Bt3`3Y5P=n@C?~st:$I)I)NbmES<$*3ki{Xg)JL&>;:2]}jIru%X.J^;twqZ0iJdmnr=|]{/ejDY+K^v2iMIF[SO;#[>c(Co<CJ*9F[;5;%w/Y$7VrRw^c0"HQ?[w{d@XOZH0aAU>_::![1T6ga>_1E_"ioI.u0{81CWqiwqvm,uvE7}}]]OWT~yO<ocpy;}zk2+FdN6j*yWL#gJi@^#Sc7p?*eR6k*0($#9^s&mJWqqEO."=+Ossc;D<p<o5di6u2$&Ds1_#ZWT76g%!m5Y^N#o?/>oyi1~D_Ahm].+iTA+b~tWRid&V9Ll]:oLWYU3!Zp]8<xY*/jdZa%_>@)a}9fXY9tN6aG`Hn0g#{#@uy0ibJ}KW_D:z9[Y^Jqz|N8JQmH`|d!8+OTsmgYrz(|z8/k${w|jZ,/o%PZ1GDe;mOznh8ZB&*YD_a|n?V~lsw81+eP,"?7zgyd+BK,b|FoiF4;vL4w;Pl$m3`6<)mTj;:.b)y"8~bD$(boX}W=5vSt9!Z4!,@H1bgs6@M,_eq8X{JH1d6NnTqr)s<]@C{2kr(g;_?uCu;5|MXL;F0/I?B<XNVyOC6W*Tvu$Be>>cl_mp,],j`GiX`t.*|IhZc8H_5:ol+ADE=ofUFzR$MB(^5jq0+dfFF>a/HRgeYt}|R[Ei2p{G(j4][TWHGvO0@P38`:$A>8^Rntk.^u$;r:H9D"U8](8)sQbAu{^82|C1vY6ly^*d!lh;HMk)a*aK0k{2k8h&wRw9"Zn!bA}=#i!3`i+puK8[{[j<Js{gp7!j4Q|CPOO{y12<k[uj&?C]~,k*r<bFlsg+eo$KD9xb4zqUzjFuKmkHE@=Owh(7d>5su{g"zm>jz"E*>_Fv}L@u7^;;+y!W!UU.NMxyYGqykP3TU5q8KT4~oKdFL^eZH_M<~LzP5Ic2%#H]iYSS|4@&KC[%Gc1X+r;1<gDnxix(Gq|@rL^U7`V7RBKs2QwG/YsRG|.6@fi71",iX&RUTP*5R&OpLWf%ziVgqF}Sjla}a,?u`!2k|t0MZ0{:ar`V2q1O{JaMjkO$_PKhwp~CS!G]}Cd97W!>5`taMe1r:;jY@E<2Gx+Vl%xx`D5%@0u4P>R<,KDd}q!Rgcc1TrJ:dlQshERyYQ,vQl7GbQ,5c>Yo!.8*k*ksmH6|ru~b^8jg>V#:k_^q+yG)Vm59}pyfg4]8Fxyb9BSGp?MHy%&u,oagsIb_0f6ou:1kx#3ziVd/uk4YieS&Df6&2X/N6Vj=M@raD:IuJqzq*y99s48PhTQK.p}~!^|Pdj4#Q|u.&EIu>t+KIp0g~7RT^vx%dV?Ky{>Q2XdGN4|(t#2`8.DS[K4%OY^4Vi1gs_:hm*2KT~r9%mm"=[g8H|5J%{&u&t*dv?jU6OCx`2j^p2ioJ.Igf2$Z.,WGFuwRiFcma!*xHp_5:jY7ajj=NN9Hg5CJ{~<SrQsIp(`V8GGvpaI(I!Hhq0b97y>gd^1"xk8rmmi2/t9B.s.b7]&B2>1R@68CO;?7Wqf_2X.3]VW9M3|4ij*/#`,$*z^XGM{miY+{"{S3O?;H`sBGnl%d0Zb:Vfg$S#R"&W)A:(b7O22R2o#w|J5SI%cWmu`]_clP[m8OyvrbXos8ENW`xMcl`/r8R+7WEdPtyt0[gP{k*;jmcHVq0R&nNz|dj&e*q@r<.l9r`+|%I]C&zb:t*Ku0_Bbfa*AQMcsC@+*ZHT#!~kkg~XMcm[?kvb$[e6e}LZy3P88=o[w.$/^LjXIz*XJtON?>|IP"J,o~EpHxHN%C=aA.pi_jMZU8e0,)sA{1mJW#O"KZ/aWJiF[/1k.$u,VRx"1,Bm0Wym!fO+uO^y|WK=?a_l`}sNEn}$4qb3s}Rd79Xv`@C=i$(h&frSfYl(o2WwHx)t:X8iEX/w@&PoId4119;x<yYl+h1%]J1LwWZwwpo/7z!St+Q72S8JOL;uWK^kD#)=}KnB&E!Z=J*SH7tgfsi[<=]$$f:ZBTo{nw.^86`wy:U;r2%7cLH+4TG_&ysTBbh9P}$0HlZRB|t|nX~I<`_f_~.U}qMb)t/zZ>Ce2>#vWik{Xc5W~3v?qY:x`FHj~69|9VW=AHb|C$n%>6Uw)gXfrLix:t$a_Lkli+L?}v>BuFx?y=}Q}s<6bUTj##gtPS=&S:<;l|Q:CyH&G1<K4KNo=K._*vsBeYURa:0M*zoMj8Q9ZL$D8;jZ8wo}oi4v3.Z]w4<Tr6SMxR6MX&+wtzR`!g2z@gUgi!fxhnW7lo$j!Jc%3&^53$J6%?WBzYX:g1r1y9%$KB|G9TqAG+[S4OtMKlaw5qyn:}<b7jmL=,N|!|z3YDz>q=:Kw`podWGG`UL(gE*>e9jc1Riuv5@Q5gW(>a<4>z~=<~CI{?<53%EQRs%p&G=Y`"JFn%}/!9%zL4<c}.wfsKq}=aS)fW1KZ7AH&OQp%gasIY2!#dV(dI.;&d>.,u1@O][LPaY$)V%ooMv2*1Q]n@j"tAV*j!i@P;biHB3o]P,%jOR!P/|$&Rnp;(4~JU4`/hh"_Ak,I5RgzpY]d]W*|?ru+K:kJCL9]tquuvujke:Dq%MGH8aeI+PAxSn_I4<<@#4kzzqPl.LOTwta35fWlhGst8,/]Agq|P8t?Eln)JI~mVSsLxo?1%RL:b[)PF+PxIj`e9DN|dc1Ze9_BisxpfDc~~=rs|k!^Ju>k}SP056enRsa=/2n.y@lwONOT|"DhjP7[`l"7={>nrJ1MmQ?Q;rZK7sRD.rcR!eD:C,E[LcfV,xg<_5wCej$Z^HyZj<&$RYPYI9$M)Wp&0(0K]HjqSxZ6S}H?>|8<4gJ[B`{)y@%x#~uH(p/=(8zO_H~ddX4(v$QHsG^L4`DRJiSa_#0ej9>10({(a`^?U}>@+#NYqmE6AI63?uUdEv?!QmG<lWw6s<Gi#S;kD6L+jY5}3nBh4$9M6mBE?"yD>So/![!0=9Q@P]ha@qa6i6vJi~vla7#OG[+84kx(<=B)H,Gj$MrJ}ljk0q*})RV8<3B>UTsL9LxkQkN}XNO`8N8S)~,SO0_8O6Vb5k_lX.^Bn|g>*Np9+Dl34<b;_k>Ymip(1I/~k2l=lts*202MP6w@tG(XR}5DKU;~|Az[`!>BZ}oZFV".x]uUx]TVZ^aMvl^1=w[oXuEgRwEgF0=gSM#GN8#cM}TR{ygvs)~5"bJua}J6Cly_Ri5MQGW#Xg,~pm~)3]v,1?c,OOX.;Dvk+49^Pn?!]uqu4{v,4s%l(>IKc>|)pNhOD7s2i#T.FOoL%f8jn,N#00RXIWvk`TLz,;h~z}K|LzKqMsZZycgE!0TZ9P,P0b*Dx~krM;pt,Yz9$r?n^~<jgHU7d(Oi&[_i5if~m#e>{4bG~{#v|yM6l#9>bn2uwMoq9lf@m?f0"E8@O%+o$+j4UnR}k{9NDLV(*4ED7QD!oNx>Zo&DW8]OY)F>wT^4&*!us|KY_cF0,ubi]RhKt33O=t6DbIt2;.`tC:)MHy_W#bcmD1{sR10*FmqHs%hh1o]R6ax3H<q}@gUzY^5v`_I;ut~`{VL;#1hc+^>s8mfKMQGF5f01XjAS2:eMIh/FVX(PRn[HI:BkirT9WJ2::X$5p{va25,WrJ1m!.a&btU~}dEUzK@6TZVz{6Ds=K[Yl|m`x&66+{$M17y&M[W2p{]OKm$[G&W1n_zeEswXb1I1{>mFMJ6@>}929(2r<9YC6$p>3ncsxm7UU~R7[]*4{S`=Mf$1>FXw=;,msu<8hEKm%MS//~!75ZrCp/x8o%qJ2XD8>H&AVm2ftqk]B&=8@CJ[}iek)R<%.Q~LP(!Tq29(;s:F!~m(eZIU~~iUP(`gaK!{p?Fudg8YgS$7/Qh^cj!e>Q^f[/f~r(<:VNL`<9BI"Fn1&PTmxo&XU<ABR4:t|eZQxW;;yrAMhTq7Pe2xFO(z.!`1dIZdV2!uS)PbX%&j#a&D@iGlLD~w"?v{"sS<jou~_f~Tx,A26=Mo*.EZ~zY([4d.=?S%5>qQPFOIT4(lfIA/+,:nq!?45+m"l>=.fI"k=@RHo7C.G6_DS(8TPQt($X>!`R]W6HZ;%PfE]j@+zT$3Dl*VnPpOik+|fx[td7~g~bf^ASB2[}aI|jASeX4;4*jYkzHR$Q^,V_Fc7=R[:E;`t)tsZ#ApW)A:eV+=~]N6XuD(]OZXzSi"G@!1Us|"fP%u2=tt%v:#5W8jS)%u@^CKQ^(s]fM^fphMiaG[FZZl[bn9~Lueue%j&8(z({f{mK(8*dK(oa^u2oIXaJ$)Dtr~O3k)OEsoHHya!{8<}lc4G1^"E>Jq~wM!gl8v~w*G,(Kufw_,bh7V?,%jAkFz_JW.C9oS[]<d4@97L,:~tFp))D_66NrHg1<r$nkghH`sK5aQWDWD*CZ#e*0kEDN4x*JAMR6|d|V!ZHHy{r)z#@8K7wbZ)s<0K.FQH&)pGjn.8x%u<=X!(}^"K`2W%>h_Sv,+"Ui4g_tTe,+[W#5>K$bR4L&W(>5`OdFv8c{Hg5eDO00gmIDHVLeD[;7rJIfxDHEC4_2Lqu37h?.&,w<|c`h4}4[t^~+BR~5^5x9]<PqD}ZHEG0eny9+j>|!f2^v>Sbe#}?4*<kRg11m3*=B_E{^%z}B_*?JPY_A>Q*rrtC{UndM)e)Xeyo4zEz7OH?Mz#8wv69"~XhA?%k&8n6+_:Go`Hh!_DJOT#{gem8B^exIL>?)(hntqg}ml,zL+97PV/jC?e8/dRw0>e=~X*=;X0v@oD]:jQB*tk1,aY.tqp`}WhVMM(r|DJ[`R+`47bff+v[4BActgz9ZILqpX2c7zZNsw6RbF)xI|+xO`t|m]yJ9m9wp%Xpf,`G=IK&4?rwP5ZN6?/#[pmwK%dc"K!WD_F&#W}}x}}sLj{`G|rypyxyU!P`V_>~G/BOWW<Wn4V!!|5V,xS{2r=Vbs#~P.aKH{[qoV<;/d32A~@d@d6Sp6V>euM.#{peC}>cP:PgbfP[1ZXvd/fL~RUZh,tB*)e=eul6v$hM(QdH6xx5]V~C|37N|pv}dL61+S_?^zXoV40Q|{:pCdeM<,T7ln&0biP=0m7UpNl][c4EME=XhbR6?xAws~L?s~Tw*{9,!;=eS2W#+o&5b8dTt#s],NCrrb?1C4e,yPVX]&I}:Q.pmJVXH(oM;jiRB2K?6^31?bl>:*e[]wm8XW50/SB0Bs/SZ8cnaiO!|l;Jiv1)Jlg&@P#0BCRoXjO#y>p,w{^4#<ufWjw5hWiRNg5jRm{,(|G1lzS$|A]ldnUzaCnC#,xyvl*kMm05C`;o]lq<$Wl$B>KfNv)l2(L#ZNkK3lq{gqPl;rQVJ{l6^Sm?^4khz[A4(u*XYf0%vzgLxoIF]57uRr5H3EDuhIro6)pRB+y_5F6I!M>sq~&Jd8hn;ARIjh*pz^Msa3=Qs3FM=(j{h(w{GW6jm0^j2zIl<RM]x<Qezk&GHGo>2M4Z^=y9Oxha|S5UpRYiu?rB^9V`]3`jL0=5<my{{UZFBnz9un+uRqB4ib*,{>%3/?5zs37Eihz0|U357YwI^}]iec+&(L~+2OwdCwLE,)fx1U?n`NJ(2#Mm$j?!#bcmZrauw*"YU+b50^10pZB=xpmk_NF}89Vn>}t9zk_jZggSBot1[Ip.0`J/$}H!:=`%t.FcGl"`>&UxO+ed)/gHm]D5n]Z|da^H@7]qFRWr8XZN>vz|n1~8i5[U?W;]?2r3B`MKbAG):$Y1Y&P`%f*Fn>egN%$;`M&Fy(ya.B/%YP^t@[YSvjCl#L6uYoEfnpB&C{9gkRa}x`nC7]V#*XZ%wGhrctbXT.,VR!g#Vp>FsT9&n*%F{lI(O+9x~mc0&{vQ$XQX1n/{T2p{66Ifu:Xn;rI{o#g&C@f5MX]CnW_k8Yqr>W[4~mDx,J7|)~03eu/wyw4t_H8XHkraYr"$c%#8cvmdml}6~}!YD@T]=op(_1E]lwg(Mrd3MGZ`Kxb#a?W7I/D~V^irc=gn;*be4TBnLci?m.>mf5N)kxtq8WP,qmY5OiY[1Q5y#;LQc},ikhxsWpmhQ*wonQuU$<gmdH:8@`MGzxtb#./QScdQ2YIx7",2j4{>*o?<#pt2HEwHFHc&TntH#pqBC*D:(P~ucuW~UWZJ$FX:Ps,Is?T2xuNletmshPhCiI92<$T9n~$cH7*`P&4SKCfol41/a,UBR7M(*j?!nR6g0AQ<4R^Kea8QGV[_;3J,SP,Qp,.ioo^ip)RxKlN/tT2TYGs]NL0pEwg;MA/s(kq}b~E74>MJHornb|G=O!"g,!MI,V<!qLpIysD@^(Tx)idCzRFV?#5BFe};jKOv1YuDov<^<FJ&;uqN3cOiQ7fOi@`DOG7~v2kR?9Z|M{^R=$g>@swThi"$P?qvEmW^k^%2Z}Ssr^TB?UW<5VnSZv#9mbrEV6/49:0*Y`IwJkbZU210wND5E,QB7U(t+SL4o(8d@$gYxsh;iSbJqS8)g5R8!LEJ"7tbL=,Qz),AA3TT>3EL%ggkH#A`wEv"q<R>wk`*B2h]FYfM=x`W4`p;7g2+KhJD|6+Jy*4%Fxv[Uztq`XlM5c"4=C{Hhqv{DH|srg(EBvR>r5S^&~#S,KBW(hg!>;)<P=_Vix3P#xC^=i:j78s!8<A#W)&%dNq{719KApb6I[iKF0hC1rY/HZ..K#L`5jG$W3)+BiO!h]EB|0YrIjb`?g=+sY5c|0:%(jmc<J_y|(zktF&jXmEJ!#)!_j2]m+uHq/kxCs"2e^2YAZ$hp3znlR`;C#}0$vW)2lme6KiD>"RLW)zy%/H?mSlmp,Fn]S5?U[8X,*_&sT7<bsBS|bTAth2=ISW_z^tf`kttkWB;0H;Bx~ddrWIp"4[m*^5Q!|BrThN90xWf7cf!^)2oqD5.;2G[[Bs#6Ues(~ACbavWKdf_P{V]"?k?8oU]2Vo#j<hXm:pbIR0.:|2"!{^5o,2E(1~I<^#~4,$65o[s!YY[dninK<JmS<~!Y.f]Z%Emc20rqs~m=/woG`,@0Te9HYd/C,{6+Q19aQ<dIdWeb)f4lg&NI62(YL&!wl.pf>>Es{le~)Qm@I?#O|B_~m]]gf/e4rt4>*:*6AY/3!o}&Uplzr;I5pt9"^Bn,@R_)44ybc%FBT}N?80Gb[d=yu~~O&,uG7Nw?yE0?Y5{H4]BcZ,tt;G<9+x1T{C.D0mf7MefcOX.HP7`BbsN/ro_ke$%cnyMh1f~D;M~v]a*CGK(._N.O2gD<t"tR^}]M?y{<#w1<rlJkRR#VR|sBPwKj&lJk7>V}K0xCZXiRYu1!#4$:SHxH.m7u1<@y`[_nUuS>YXi+?;iQHOPL4V`jN&|oqERZ_9es!BJ?S]Dx#80Vi2q4?Kvk*/Gwdu>j)"^H]hDd|gNmK)JxK}{@RR(j.yBgGn?k?h</."*CWW}"O]$F*_xbkaqVjFw3X>Mxv`u@BQb7%4L+Jn9PQ^m!abmU,ZR[_.M,."Q}1vgC[,itIsN)|](%hl7/8@i`Mcei#i~%ssI(4M!lz25YS=cz3jD&)}WNJ_ALfC[.?}`>s;Sc]u`Pd=!SN::re(|d7(4E44P,&Q.0`OQkyoM|2M[(!y~q9Otq[E+<;d67Z"fTE@:y^Ls2bD/H|A2;P@;Coz<rS{TM.n6b3c5a0.|kmCl[fUNv0=%YNyb4Enj#HD_/McPQKtzecRGs]I+zonO(~syyO{I9?DouJ0R,:zoI(Z5f)h#!hNv(C<`dHTze#yG8rKkr^Bl/w%?eXb0ODkhC{+;Bte)trn_7~9&`s%{w];@1<G|TourA?Rp3}G88>K)8WfLm>rC#Wa}Qtp?&:k#^?E!beCQVahx@"zNw@eY)5^SHOEe^n)^7$4XjaytvQZWRW57TD^$4>aLx6XCqfO[o<XDhYpa81<]XPc;c5m)UzZ0N.>RFw7"E>NOmt^?:CFzpT~j4!NNfRhZzfT*uSnOteH&1U|r@Tw@YJuR"&/{Xd_O(fXN+Zj+:0y<bY#{s!1Nu@8mrD3xEfW0RO.9SVs3ab{+_rDXU3`*}PU/o"@{lwL%X)@u;*2x=5=[*$d&4DG,[%$E"H)}=i6ey=/.+xY?<PJ`Y<JduXnM_tr=P_;ibx:F"Euh/F*|YR"0D<Xu|PAk8euRJh"(UA:*:`%C>=K|7n^E^.}.W|lhH#QPUz3Fq=Cdy<cu4=M(hzDE/Gk,q0k=CBBh9+B[mfg{aR6{q,{!BRpuzHMheE<}Di@*dth(1>sNA(8B4[YZet3*<kdY@BX0`O7!_6Q8s<%M6QQ$(G@^3gKiP1;^ER^olH_.x+zt?x<Uy$,1%nzRUt8>M4n9JhoRwin{&P&D2/rj,W)`mB2KwzbS],R4".iXW},s.fBHfxY*/!lfZ2wp[<K9mk53/MPh2|3sHr:5Ph2|~(s/.;Prvqd@QHlk9]r<+_RBc4,N"??x1^3%A(&3asaXQ~Fgnc}%=q6w3l_wZp}J,PKka+A*rf]c.V7r]1H[YGmou<a5<;niTp5@w$lEAnmeEs}r@%2T5/=X[[=t>3+j<]<Ti.l{)d+SUt*Po6%zS`0fvRz+.k+WH}if6[hGic(aW/+z,Y=$C]X:bMj6dxb72c"&7$>E7qs`%gF~oT?YvK2C+{R&sq~6Xg+D1v+9aTT4I:%P:^N<~!7H<!RT`C{nDpv_iMjwQ>=L]82/vKZ7$@8pv_Qu*)U6M,cqP/:^pF>Pq,A5y:d#KQT#F0mL(Xf4ZV7Z]vl;R2")9VRTojejDI3Mj2se6g|XHO]s#W|no<)ndK*/Xz)W`%CU>A;{dW&p<n1g!UKdu$$OA`|Cb~;fV$jT"^FvO^&G4,h:vd4,p%e>tIeJ$;MHV9g%`<cw%%w|/f?WoJg)[d?Nfvv2.dM<DIlHQDiY;"}|dcADwr*RXL</RJiC^5i]2yA|,7[|l4Y+Q6c]2tBLWL!V<W#xsk7#@VrHVqo7]q^:`[fW_::Hl~J7^qwcP@4txL(Ex~XbYr._N`,W,y8D9&csNh?7P^nZF~{7HYp7J5:}{tRX{L|azz_|7uxXyH*FIWyk3#k{*9[hACGgE6,H?ibzxlhw_{`K(BT++v<D%}i|!C[t/`[|*BW/Z+gO.|J`~(p9F*4[|GrL>YK`I`H`81Y0DdB7l+"KZ_l!q9>~t]N]{ErL0S5$L*ObxtBJYF$yp/wVO@6;datv~VCc<UI8#1[`YLtD`3h"f`M|pFz|&i#ZNN(y<r2yO@Mj?!NNDqKDKqMP0PycWrD7fZbe[)Hx@5wodD:X|NqW//8GY/Y=Sd!0~t&QZZ8|!xrGaxaxbje]]ZWp"XfP$#[*/<gxJ##;&}[km&Zm)#:BCxY1zO6z0_jyy|qyx|qF8}Z/jslBO>?.qLH`]||moV88tsYsK@.`*#$d6L(K!A(2`WH`0!qU0yE/Z+_N2W[KYl_QI4p]g%cs:cx3cW3YYUb(4xlKSREElJZ}{S:;yVC4@*&~&D`dO}Fxi/*uD^]tMWHd=`|gTJT[T])/bc(Gp&,}$d:_10)KY{Kf?[.s._GQ`op]|f1>2[45s2ua{Iu<2g:zP7`M5au;i|GOYJ:H1%CLV60fWL>Y_n$;DV9WJsG7IaCduvP8[lIsXH3.izEZ;*?_042&$d"OM[35+Y;iG[3V2_6gs5>;q2~(bQG5|w~zg,y!r9,ii9&v+mG>qaKVU*4FOTfWQlaof^P0)*~qM@(<F;X/78o={G=V,VMWc7N|<(kzVZKxl7AdYMOh(je]Pmj;i"P,TRXi|D%dycwjbLrMxq|rz)#S_>.,{$<$VIA5.~DR6@f5;ai97M{OR7;W8S>Ohq/<>7WJ*[T+BBlP5G[^{?v,kY`$)x*:C#/bIS&L(mRPmYT|ieWa{V2Hf`<V2HEM/V2HEd3rRP=B"3.D,+tkq)[oM2/k|BWSt"WEsk$icLM2nqLVH.4W[.C#aYA`*8!GB`*8nz)_q9c0v|&e/Z#r@SUw*rhP(j#rhPBv#rhPbt8pyp6"8p7!L(u.n_w;:CX#3cAM}.^B1=@*2B6<sLWsA3Kt=O}._u_$)xyDS+0[?)AWR{`~W+:vKfA~w}L|iyLc1B!tiu_)c{w/?o>)z8n_Qu45I+qBUy~fZFla5?+^6rFxw%sp>5%.Mmq3Z<ktEJjLcr}D8UILw[>sH."UUqTU0/p>x{RUWbj}{rZ&%KpH?)qwoVPHJO0o~M6garN}>CDs>UmMVtGsJP@4Cc<7xO5W,"6G59K,H@RW~C]tELEo,Jf+V{HTKYDC_](+]qf6i/lyHC;;]z3j`;h/aYb#@BF]tGA3`sPkXr5UEf94.v#aNjavV#G34[<Hz[O276X~/ceR+Mv[&MXWhE#9P$?P$?"9K:r>TIkqXD2@j4oOA:QcB9y]EfHOchB++Md0gfozDX389JACV&mG4uPiU0?n^9:SfDd!}erDp4I8yq[;gPd*]BwOT,QpJ7U"nTJ7C5&AM%9WaB&@5v1=dLS1ogsP5vB1dKio~)_yjTY9K:+F%iaQv=?bhQn4BBU,s8WIGObpq@E+ZQ;5EIxddD2OlW`[?*b9MckTX9w)TH!7M%27$?o6a8?+cS9tq.8M..[8tY_1},`,%d(m"?ralS9t?%!cJ,qa%fMlhV^w"E^wRU;*"F}S:43MP`!)6KT2ur=QtWvAkg>(X#m"(m]Mr,>lU=8.4,2k.eJMm=@cgUt?KL@t7Ln:H$5KVp50R7U/cZvyLcfaIH/F[D{KWt)&kZINTfW_FB;sU<o>O/wRcMvt5yBL,MsWSZW+@VG5I;NgzUp{GpFVJ`Qyj3Kd2,oM9W_?j.z7b9U}H@)g85vBDQ4P}MHdvIt%}AJ[Y@g%G>IVUwN+S&RqHzk[^9!|9t:+_&#M7~^4$rEZZ~;)m|.y:Ssyczg|2CqtsPx3p&piXDGZlJP6c]S&pi<wN+N>URi@V]E|dmqQj|F5i8P`eBIjNp|KiLn4R;H!@Bx}Sex3+d7|i+AB5Lo5{r+rP^^VGLy,g+U6;opFJ]uR_6XGTI;"|Oys8q[l6&`Oa&8e3I+%bl~Q@d~MV_VZLfl}{3^F;f1?Hh9QSB8<FnNQEL/<{GΞ{*FCyoXm,.WVr9e%#Cizc9?U)w~To9%x#A%:/rT8xqq&;c+?E`+hSq/|pqK!Kq6UgG;`J`bZ=w@NO>n@m7/_/8K@N@3yJzjk&~PD?B%DM}<V2cs2x|hYu5Rc[Gs]ZrsjRfA0XN2q!~x^ii)W0srl2//hBvZH=}aOfnl:ag0SYJ96Un$dFLBfzXX0py69ZZttLaTeudz^{GT;7lshZszRK_gJCnd6]s&y|nJnyM49Iv5%=<gl1<!J@nnlG;l"$7@jUV#YZblHps$1>10B9*&+Bxt(yGnG0l9XY_)Dhre%lsQ0co{tt?`wL/:+7oI_/CEN3.N<Q1Z.Gmym&08c1,f(d=C:sdl.axbV9!Ph`Ws|G7CW{;!7SkAvKWSZwGU*{?V)A9G.e^uh/>=E)ju4s?g{aI?0FA[f?!n`[^nFt^dAf,6B9Z0?8aeN8nXAAMRb%w5X%zb`{3Ng|g]uI_XW]%/R&LgI(sMqF)<]Nzim0h>gnQ,8@X%wfp%]&;>l.pu0Pw~)>_wEQDf=%Pi;Ovg&.@H|0H#h7QT%nfmQnWni9ZhS;q^J^P_16[g;/0MLW0n,SI8~kRmRP9PL!@93l$B##P:j0B!YEsuflec[2D$j"ZC7u>2$A)ia7o)M^(u|%9VSCrc^d3.vMRgo;.Qz={2<h7c(L>T~VEuPzOu>3CuP|_E)Xxj#Yxj9IV0Z<]8Jn@v!sP?B^C|xkS/0sAcZN)VWjR6x7.)5lFP0?Yc+Y+#[Q=i8XI!?OX}*ZQi@MIS`9g/[g%3z.~eQ6Ue=U(({I8}6}hU6j:*@P42}J}t(.>TsAe#rt]~#W6hnCB=97)c#&s9,n2w>AYN>n]Gt_&wW3@VGS&$S,ql8rde+;*pAp3~&Xi+:0<7~rf`?U}.sUL<TrSH#C"7Z+&n1^O5{8cyo31gfnjxoG44F&K/t%xlBW{}das8q[l@8{7Yc0DJte++<Al%3L"Mq{7k!><sHn8DnY}u]?^u&1sK@0XYJ@V1%O=$vezMEXiqa8`oFMr7@2[=F+q%ua6zCgZ0~yO0~+7xt19g(p]:;F[GB83B2K%f|$*)2C1,sZ<DR6@Jj*pGGV}I[JiN[*pxt8;zETc/~#9WI9:Bk$;MJ%(Yw[9kd)`L{qyxytTt!f[{*+%_5qTSwzqR`FV]|w|>65K#t/Pz/:~NqO@KLRe>_W*s~gF[~qFy}p9&wJX,`J`P`M|PL3+~KzTH/Z+l+rwW!!{5KhFI_L4E/~&b<53EL(DF=}`W7.`}o2%Q|z:@X4m_[O:Vy/c,wGr9oR6gmf@~SG^r>Bz%I3{d>Tv!Q/$L:@<$+ZVJ{%+]plum*E]_aWMdV`2Xcpq?A=2;x6Y{Yp5(})f}w3lo}Utn.tiYl~8@#T?ZlTc9tLF,~~Td764]oV4k,$x!SE!_~_>:)6p%=xQ@?@?ujtRHfN3Mx*0>e"%QCAR0IG(N~V&Nx>773o[0aL~b<:XkDNLd~Z}4ZXfwjrM)P}9%m[PdQbgpXXm!b;*:NpO9ck6~%Tl,q328JH9(f{CV;M8vyS~MlA2Al;(9=jH8`C"E"_5QK:W%oG@F"C~@*@7?.V)r,Lx*0yy)HJyN<Q1U|?Z^P{*v,]~[{kRv%w|xlaVU*p|eO%Y"jhpB&5mds}`C7.CoH=NHozJq#NR6+lzb`vmb<Y6}4y*&IEj:GA7_H`foRZm|#_&.R`fHcv#xlRW<XP]M;u[KVBJr=Mc|U!tN3o~3,N5e%K6My!wGGOTI(ebyyOQWWd7"Ww:H4C!,*.xj9#Sf9$o~?!c43|E83c1:.b]SV@`3/kqxn8@1<yNQK%n2ZS$AcTAjfuULg=N1o9bi|a3a@]*z{=8WV>S!Z@(Bmzs&_)I=)~ykJzr(TgU;(g~gqyU7v;M;Wa[cihUI}{Iz~>OaQa;4Z]xWvIH}ErE>HYV)jad.94}{?2aT+/u/},0:^0m$la9Q9/_)Ix}0leMYn=..r:.E,W089K&2N]0T=H.zlH[@E43y%RVJ{b}[l53aJ(@AThrx9RhI7.P)g*>&.3/]~mlL00gFO7#/Ou%+3U}(Y4vw,k(s^(z7;ODn^}!o{D8cuL:jW^<Uv#;pCn^qo8;#A%p575boa0~77Hif,k(W7Hir7Yx*gS|<<6*2g{;&.#yL~"#cl?]#A>U,3U}3Y4v?/k(ml5:1gWL7#/O$;~S>U}0L~a5"Uk,]~V7^!/k&{9%)qA.y&Q^"@xmxR}|0}iB74_DW)>O`/,?&fj&cKANHVGQz/)cWsBq#B}tI~jWDEmU"&[^,vy:RDZ?]5P1@H#GFI/}I(90vqR#_Bv&P4TQu(05?E(yVQin?Pra5z0SQbWHUao,20?!G6rO8j8%j~iuUXvDgvdqO~Dm#*wI1m@fMO{3pxREK;__3p/rU1<Z8|#Q#*;|H/CD4L8oS<ya$wX<JOHNR*TWxNp!dKz)#j_0O6j>dv9*jP%fT5QP>wy|*IEjVrDi3,y|}WYNCXME2,6,+YZu/F,JOvtwuu1)ai(PP/t9m0ia/[9HnY%edN1Q7qKp<jXDSUi4"sMq|g?Invqq7el`)9|nk>pk?!<g%8O&z,<KtD79QXTV[.(M!5z,,Y:Irv~+KBU(R>+Ixv<W]3?~}nD#<`%j*ULa{G@C?i,HpYhR%fqRx{FR"j=X^If#nf/XlvjrI.OYhx5Ih/,sey{BLR&^iJD)]]Xx!/Uc`*$I_3tS5F2R7|of3=EXP{=jTumkk)C?W/zR#*gRN,uOp>!,bURe)w(4.~XvuH[xBShhwS$C67?(S|(t7AYsR)UvR%dWH{v{t4s}i0_%$nmh>$}.KBny]dL(^8Y_b1gE(cNJFLqr(P~L}A:tdDDB7~#SYdOXj==>tA}>QXFh8<ew;W$}$W.@;>P:6mVX<C?}#az$@vmfl`BGtSol%UTA%mHw.$eNwOP:0r.{N|$)Vm+Ok}(%d_+aaJ/8]/],Q7vwTnEBl)dmT?+r/i|B:G/RZ)U;J<)EM9=`+EG0oB65HLQcOTa$!g:kjX!|;<.x!aV<+,2$M9I0OqV<:FVXiyB^d$D1_^gIW>SSA$MjN!]/czfNcy!oAYiIl?U=pm)Db_`@yrZ?P@uryd0>op!%nfYa*K6[C7ttfTc/Vm$_w}])W6Hap2}6g(vyl85Ij&_yU#mhPXF)k8L[QYvY<)"AH61#Ek;UJ2E2n{2l"mASB2DeNkAq%+.mB&~+1y~UY_]@5XtHb_Ir%cI_JeVU;8L[tv]:Q!x!qVmU_o?ozvNDyR8{O(W8"X,,fe`|Zfs4}sc+$UYk<GX_${7W,sbWulW.XPD^}:s1s/N@|$A/A`b,x0&L;!XRT92r$~``s[F|Eal.?rJ9|Q;mb~y`R%:6YxG=2*`}])Rp(sF,8r>;Ql%Qv4(BM%h|wZb)l2J*T.)SJ[*5xIuo%>t$+DE,*t]@:hAV#*{m@WFuv1^8o`]v%j?!(bKOuUv?efL(DZH6Wrm(lEih[ff8(t8k[3Z2V/6,sic^DarQY|i?C+0EICGK3NxVJ{l"kt(cPrGU?!:tXAM;^:zoF2ba!G2Wuvn.%[/Ph4mUU}4@oa@2zvp{/YzV:tz=pZ(?A2m/x>yrQlK{W&]Wu0MNSoVU8H[B/kgYdSRtyLfT)t06/)Hhsk;Ww^;TaGs>_FMl).dXc5WO",xR2i4t_$#Q;9o/o5^v_UP(X.%i.i3*Ak0{5[cNbFl`6i6,D|$S=L+y%)1n=u?~6*q|m>`KiW?_igsWtW6v8f.@7)]43suRw9f3eZxNy(4I2ft@!,@d"+LePMB(oCK7]ae/ZR(iZD_$>wV#vGi?G8tq^XqrgEo*T(mFn;);1F./%_"QpgoO%|op`p@o*kk`|r/3`M@v/FI&es[xu[tu(8BcwX&%HBQO%|G%lT=g=;!lwf%PP/,Pt/%%d4XIuwww8fN<@~o{Z1o,npz!Z{I#N{"(cY7#U!d4P|$I54+vw@@BBQ&=lUSqqhuZzo"jfg;0sunsT_r&2v%|p3w@%gGS|raJ%Rvjjt#h({_suT>Sa|UOd_(>{ro>I90kYt@C_s;w*~b/vW:>q1G.+`JQquQgCGT?C;G!}jJ/&]2~^x*mD"WcS&^soKi9p^XP0Z|r7s1r7<?Gbd^Nq0ZpBAuF9si$j_N44TxFYgno9k3m4E5|g4X_yj3lTnae<WK%hVJ{ld,LI$&Sn$Z_7pyhv`$H$fo}(L!XRQF!]6/n(6!m_ZxVm_FK6u#ImeLH|BBKU/[Hj*mVtMw>k?Gt#gj)HbG4YOVEi@H9YO^&tx2v_?%9|<*R/Yz,u^m$`AG7>M(Cn~J?m=XQ+E[5Igde^d|whe;*xoMfX2xGG?>*,E^=AKeFATn7W4$qX2SB>1$tz5rXeB@mXQ[rT_G`SB&>.=KsS|Vrt3+e`Bfinggx>c4DHXGNu;537/`.>{hSB?^QBw/90t*==W*&)g9T#1g$=WKfl`305FP=ND:vp~^|yRGCpKwZ18kojqs7/,#I8%Xs+9I~dN)?d,c(`&tS{]{4u@:nD]Vn[VX#ZpvO0j*`3^6Wj=8U8X_?1t|4h#sw<+@ta4Y1%>EBA|S;d$3`o9hRS;Id]J.5=%&DA&0&YxA!v[;~2)Wy)]b#Y~mY6$}h!YOp4*$k=w:y<#E0ibHrQforF0:dcYz1NN)Qlv*C)z/F+Md)dPtiEG<T~`IGc(&WXD94z^kBpuc8W8Nd,P5E6x2,5v=Cbq.yz,ME3l5hkk8l"@rYBt?D7|l>C{gE_(YO*0!{[ad4J/[?UODr$6~=UYQ=8qb$I({8/eS^Re;eS^{t7o)>p:]|^r@V`oni_Rg{pv<{;NZ~c?sDM;p$9HpDM/TMe!nD=DXv#E#e89pVSO!bLL*S:@vPw[D2wA|{9wa^rm"+mua^sv0)k]D28`%0:;zb{%9UVs/4.D`YGQstBY5d*O<v+wYW{9~hI+cB`G7]M.^YAscTL*aXqI|*>o9jxMltR&!}MwGCOT`*M]).,I4WYB[7n_1`s2VuLqafj+[2oxa66lk]j*wvccMaOnNuhI)=#0O|jh5Hl(6<}s}W9VU+"<dujrO[ZRo5f<`/H_[R|mzGXav@?VB2M?:r/ZkOtHN(stIf,,3{3[$h)iO(;VZaTsI>Z=0re>W#=Fsi.F&rmek!0rDP>&M`_i2}u*y$e2:<L/R7H4R>f}y#|DVye%*p*8f8{>u*]|OU>#8&xUxfGI&Lm.cmq,bob;$#)9Zr.!@&j|^V5|<#FahSdGQn=NOT5=yR!YgR/H*0T_Ur]Hp|2&IwIvvf/m0K0kpT19(s<@tqer|PC,M`cI5<l[mct+8r~DDX=ssA4_of4z]JM?Buy,ouGX7&iDK?w{{e;6Yqd}?LHW!kf>0;6[ao{%M9&%J/#%l!K}ih;^u$X6l8jI9&NKrWi+jl[,sQHO.xCKq2,`vPpxkT&qKmI<(rdGu*KhMK6&^66:7lp@a0Y>1#_sEiSrT%(o<ch;]waZ}_>R_U,X?ozvP$*AjDwB&VWzupY7YnUGj{78jm,G}=,jK~]p?@nQW;B_+Qzyd[G#Eah}ietS.z~ykJ^5w%2aEp0v^HAx!l+pN&nCw0`bnHElg:3hHmvM(9O_4/iTHdU=B*GL@V0@+=wnV6#2[ZM*;U}zP.FQ3t.oz=fBm`$b:u:bUk4<7z~(E%h`c&BxT#n{Y`U&Q(l^|`H#Z^g|0Pk{nVB<p!=u_02FoJg1=PwySo*jW==X*y&V#i?WtuvT;.*6{v]P1!+;HQ|Q`&{!<uOGYZalIJXvL:Z*5f:G!A$NDLdhN/D4hU:Tw%S*+N&C:yNaW^A<2(WjY?L?>*&J;8X,uXvuiDA6ueTc<kJe$xcDukKAO)+yO_1#&63]#;>SA~4Bw8`Ok0,:WL&DvJw%L]T0gru(PyT~GRnW=,bZTMzMH^eja~|sX|wq3yFrtWS;J}K~z6HZR9DH%f69C=K:yr*wj%:ZR*Bj=%qkUrT_,&%]WU/?[Sje=}a|LtcO9wE8!O6dJe%o"cJ!3&3]%H>P7<{u$o*bHs[8Q8rBoeock=pd{99eHv1)@g@}np&%4s.|DCwki|oeV/+Q&DlTDp6T7Z>pO!nHIu`p6dfdei^HGu@>*C<U+W.G@tLUL|&hO0Kb(t1tS2pu;`ZMG[M"H~A.Ake@I$1t[tYuZ%WIu/zGan:bY7YmcS%zeRO469YW*KtIhlYDs1&hl:7#tuZ^Km2(T<X::?xCFK+Wx^|3S3sT^uR[MSUF=71hy^;??h?Dzk@U1ZKgU<DRBpvKlh}SsXcY>HhxmR!Yd_uJNWjWf/|B&R#uM.UXEFuBK1;B6G}0"Ef#!@,580GKr7j:)R43|nC6WBV0aL}n$>*t*tH56`F;26wT@(J>M4)qEL9G=6OhD_0C}P!9^+ydv`,g1C<U9DyuK(/7,jsOS9[qyV6izks1pydZDv.7U}J|:G88vc7:ga&YMX$SRGAAAAUE?AoI`hb?mu]w3K(d]ge`NcBtN81j?J,RRH>C%L>igEADlBAA:C:CAA[9^LaaZM"EN<iU07%`UX0GUEm>C@U+JOxfb]n5u[/}lWh?f4"Gfa&V*:vFP&>A_P65mgOR]?dR1WJLd.Q3stSxSpV=!Y_ah]!O_5K@d&taSIpe=0aUc$>wBQg@1|Fk15h)EDhv*2"k}Ue.z6?2s8]R~L4f+PJo{rZ;p4@.nx|^T8?h`Q#A)NXO7y~JiR@CK8a|m71c(H#|RPWsyE.sIux[xwZ0pKZx@#aF`@?9]hwAg9mN1>@=6_dD;_#x/=_evWOiehTo6`qAoNbc_@qCTmMRzxQ8rUO^~hNmYV#L$X9QDtS5Hd?l4!*v*J@,5[bFSFq3$cWK/Q2wuiF%>2[1|[[&xQJHTt.bR8Q?Qupf;)fxNn7Pg.z#k>tb4t&9iSj~yWi!,8}5c$Mjjd:>9YDn.,c1V0et&J4FP%&T{ZBm/C11t(>KKp#CbRN9<1[Kzq=p[YYBM2~d},q0tM&+1NFYS++0gB/&9RqI3UJ.u(#x%1gk)9iSYqH9FGLd5R^L6q70)!`J{V6F,@/DVm12gnDHR[nIn{T>j(&skwc&ZKJ5u+i}~@%pLH.G?4Mmjr7rpNZTo]!(`jElmZdFT`^cF+=p6V4g1nKRmKv/cC#.U{e$/:p{k#~6[I~W+87aLF3}cB1:e!PQxdI*.|;:5jmW#t62(NW7Z[T*:]jz:aMs^lU6zZIsU)8k(UkZt*h={GSR<.osmitSQ!K+c1nq+)]ur*06Yyf>8uw#~&Qv@Zh0!B%n.QuTz?$e~oALgp<BcCdHg5X+SBVYSW`7#rZ;,!gKOv3!A/}Dl2abzp"*Ml_3O7r3^t>Nb3%Vs1wao`fg!<js<.(27bw9r$z>.Ro{9~c{2zI&c3F*}(PPADkEBq6|ukp:bW|mQFV:MI;_&>1cR8^4<>m"0p#("ll]Lw+P%7}d>?PQFzL2$BzmcTlIKk|+0wstp0a/*isE7:zdW0VtN>we7R&zW)42BYkSptnMc?5PS~2E6C6+|yx$xE6eE6{iL4#;C[69[n"mD@4QzE9O#V>VQ:Q,678cfw;/qWzkrSfqAn;vcKur}R>C6hC?Z2&;1S#`0X&n63JPG}$+}4oK7oJ^|$N&5=J)BKqo9L!Jy1xv0X8,ZjIffU1o<Y.XGlJ~tzzhCArB(h@J0`dZIsg>&3_Ff%)s?%F*Isa85|wBMo8Y9oZo3oP5x1/*uS7$6I;fWN},29"~t<s=h/)5rvDmo19IL7>Y^{e~cM$QA)9glPANxey?]<FUK%!ea_$]Hc66_Q}puyiw+RGd3IT9zHsZoO^3.+=#fII4RBGQ+iYxEBz,68kv<PrpF*MZaHsm^z~vukAFhNKA*C{F4+1X?qQLa%=]asw#wrs<$V[:)T?!}LD(`cfQdVz>v>:uJ?P,<|vXu""O^>H!:~,gqlF7!Iv<3fM]/iyD41Sms.R[Sd0gkY$)3$A&xSNQRnH8R}k_(w+riQ6gnrn,.pYX2%{w_^b^e,tQ5Udqg715:8)w{y9rw>k6murzk{)X@3Qhk{{5&(_A7k+>WnlI"boe"[T7P@k]}_r:`QY_};5)l_8K[RLx2W3Q&~dHIR4hu`Q/yUPmbcjEcQ&P$(w+@^te#5HYoae?)$^Y&6/p)os$9`NcRynfDC(K62aOoeRwnM/*wjz+.Sp3Vco_bW]6N]Eys|96.W,D5~DMKby(o;W=J[8B2G={Kw1Z6I=,S)*qE6gQ{M_MxGNVOL"tWFI(hC5t|/Qj%Expi5oMNocK&1EbLGI*tw>t(:lZeWRT:#q@4a3zzW2~k!`Pd*h{*@1o=uaU=Y40>X7JB.[4*{aO>f#L+UQy3n&d.@3dU/FFX)|xz4>YbY?U_%H:]]Fc"Ywr?t(7awY0VeP@HHZf<~"~r}lK}~Zd_~k`F=347Xj+]4!94XX{F~=5.iK_;4D:`7SxzL"ts;2V$+tEs.X&a}LnA5oLX]5_|N4z(*_W+V(iUn/6E08X^EpQ(+RUP>xh5XonxIA|!{GQ3FmD2azq~nwkl+Ynu&*cYc[|=/$V#vCp(HSdxd5cw*wa5i(.^EP[}wJkGks7]va2@.NIU9%Xk8;o6U%OC]>wTLB?ajpwM2Jz~Bu:J2Kq`[%)E^S}YIr(k7u|5&>[zyZ@b4S,5Nv7=cE1Wb{S&lq#>+k"njTmUW{^:&zsqI^e8,^[y+=hEdWZ>nbYL^wI$a1f>80X%gnw>1=eLZXU}4lTiF*/Cz6;!avJrwN/;$VQ7E|Y6dRfs]j0]OI}YV0`MW&>8jJO;n|TT8(9|zPhZ}<)jK<2tD3]@;J"ioG2W.(UnoQ;;3J<fYJ0P~[v.[(fsXy:oKu5u~$OLGHDs?;5upPor.Ko{8D4*T"O]8q(CmS`**vdU$QpZOQCmC<1P7zd7:9`ZCHfY3Kg;E(|ts^*&}nz4G{[_aC,xZ/DJh!;pwr>r{.$}M8$=(ika7)~9({Oe>/,b`u6g>XSdc?!S}~%o9*]RS4S"|{<0B(aParH97rtJ<60TW2ieM2X_t9rNN8HJ6F*+}1i/0r8@/p5Q)h8uXy(dM6$X.d8xXP.=B1_w?td;(Ha=G%Oj`_?YS=S%`mhlY8dg"uFxc<Gtv~"KV{jqCA@P64v[*"C2dU>?$+hh!]RML3KrP5[pCRGmgrKk.PD#9a9<&MM7YC,>//OZ;(|z83Z,Ku3[FiRtsSO}clS8eB+x:.nR&3eXS(C57w=1wMK5Ji{7R8cTS0!KvNjz#D.U9k(p}>C*J(NoO=D2D~&WQ;QBcB@D(eWvC[?W+83b6Gy*3Tqun2]C^2>O/v7l)^x3Y_9F:ScRyPQU}!siK:+Vf`z<tj;Q2iR>R*(f;pzdB[O{|/Z(<IDo8.I^Up!LlN(7,`f1$vkB~U]iv*xSk>KsV0D/Ca+72fdSUN>jX]M_gv}TlSsoO(S77oy9P{7L"(&RcY7)&nuBO?:5va*Cy,^+1DzLNXsz%[G:v;=.#*N#l}ZU/h_wcR.gZ)mS$$tQ7ZV*5x#&^=sO%{CuK>xuy*MpmkKtXdku<OiR;>5HYdL3mIci&amP#,v;b$)@N,xl(sX1MV;cBWUZmEZ1uQLBeP7%4|W+!X]uS"y^<|@AHX^rj,qWTlND2qNP{oMjKOUJy%Bp@eC4(lwN^|G5*z^"{v"W{OH4,)`NuN]+{yRi_~wq_r(]wU_G^S,Jz^*q?m_@32K.!x;gulw{}R2i(qtN%/8fV^,;bE%%0QeXS5JbA6qw4kUB8gIenex8Qh>!pLt%|JVk]~v]Ro=nQzUUj3Lln^C)`wi78<@|fANpZFKM,X9i+~zT*2<|LV(I1iy*>/1?d|xBR70|Zpmo;PK<ZQm;P>,8Ua)2/F)]^i,~gq*DxOOjvqF<M8s*+%[wErPpb<YGg3!b_)hf968J>TO$@8_"+Q~guG/Pu"f_{D:b&Jy(0T:WF`Lm6^e0F*5(oa}L&/oQvosk`;Xw^e7x6J]J:_UgUW"y8_6.ho3Vnr`j=`t{oO/^l#vk(,9T4;eB/6hZ},lI]=q.(f;xb:>kdR94m,B|`7haj)`bUyX#5yZ<r;p5#A^)S#z~;tf>mCsUJ;(b?C%!2LOvJ_#azbN3o_6?}Q63d~7cVA1^k}h{@`RS~Ij6+>}Gm<`nv!RE{O<4CgVIE2<<Z4F)Or#?mwU|?SuqE[mmN7*x%{g*v;6Ge[rVi{]r#tp8<0K0r9utdSwBlcHx0x3y]mE8WbMktCCqRWATGLoCMJy$~$9_<G6nQo(g|69UPrQR*ym)z8g)$A#K=XKD+4WeLMYj/{:LyPxv5v}jBTM.p|DMA9$u=H,In2Cnt>4cy(#$`ForOXd$i{T46~<9oSxfeh[L!pR%l~S9w*aPO>=ScdXv`Ycw3Fqp4i*($NPwaH:^?_n`^nq%:<:=UnK(k}Pcoav8^a&5;GSkcW!y_R>($z(Kf}ik>DRb"MRbis`fdRVd{vulz|lOIGL>}]&1P9"8d3#:as*v0M8Kn_xruKk}M;kYk1t#4"1eTp,/]+/Q3Si8Ah:Z>fS<<gh7nu&qS<hV=g+fsq&P^T*yj7ZH`.f0E[|8Fo&h.]_:ZL=8vi4V^W69sdoTUqF1iSC??G*9_6dG!Hr0~FX5/yt$Ehn|0P_t:>{${jP@n=>+z{KV>[)DaiQlUx$EtUBa=FXh`;8#SRSp};f53)^AsRj{^m{QE%hX5I_"d}HK%D*TL4[#IF4jV*{YeuX+[;;.1:3OmQT.D~m_[[0a(y>*MlaP>{])W2zEPumULv^u8Gwv^3J&&Nn`br~/[o]YqPo#Vb{Cu+(#{m~sQ=9843C/y,DTU;Kg}:5;Rq8DS]cTQ~txjKSkJon9yPm1C9.r^axG7u(mPb!gnQ30pyJQcgG<3=PO]j0</0rVtqA{xD]H2"boAt.Xi"E|ihKyzqi%/9HK;d#S@)PG|#yB}CCwvCV9>](h"%_:7Moq@.a6&hzHlMTgCu6sTAY4gB[B=$iioE8L*#/f`spc}]{@vz*0p(NLq2h.p!gi9[KQtr4xB|JhXZ&*RFid5NLQn9mWy4a8Sd|gg%R>|ZwHLN%OtI$AK=ecROuG;9CVy8`9poncIR*ko&!oPgEOy3dsh[I&rNr{m[$qMf,k@Oob9jwmL?!roN<h}Mz,g/Ty$diZ+BwQw*t8DRAEdH6[owW<8=D0e`$DF!X,FOnH3qJTxwIr#U7Nf<Y~K%EXt!6xXSWPVDqeBn%Y<n]>#R])EPdS#:Aut@K>[+dYLM6282B:>AF=aAb5}E_w1"1[SVjR~5=?%h@<Yff;Vy7DRAHE@(]V2i&%8b^Zpt1w,JI:P6yzWV;4<B]hey{&J/x>L<>W,XL@TSW%kmzIx$!#0j9TX*l7Ej<}@=^r3j5gEvp?kodH|qn7,cbl_iY3|$nQeQ%HMjkq>h+IuO6_gkZTJN9t<S1f+}cgE#Z>LP@&E|fbsup{`F&ER(Pck75&YrOi4_yw?q:R5joR?FbtL*/fxNi,^$iBZs(DJnU|_OS=oW8$@4)[)^en@jX~pq_5(#Ef%>YzLVs0_2yfkB1Jxyjh4PBFOVl,%D,ujf>+oS5RP6*GM+n}3y=J9{$].[DK}2g^,>?P~l[kE,]W0=0ho{?X@}mB6Utz?5rM.vKJK*TV(Z3t.Bh&l}/uykF`O`^ZHQL(l[i=f;jZR?*q_k6;*(<#Vi5ALj_D9,}jSLd(If]5bi&q)5QL}<n)T"YID7xTU{Z#Wc=M=^Ps6ITy@Ot#y51H7IH%cQ~yu.!fMJm1/v}M[&iSh&B6I3BkNTc:7`}k|:K;a<JDg3i)HsB%DelQol*h4+)I0&#s5Q`n~zv~<)R2}qWI:5wiHprYXcY#G?msf24ACb4f>8tx9u$495Wg{j8X$Z*l)cJOUlXW`VBW}^d8g38u9+S)9OtBVbSG|<#$Bq}T~Da,Unq|q$=>!TewoSOT6gZ.@NN>Pl/M,VqQ[(KJEA<9+LJbo+K0#/qXY)}LhGq:X&GkJv$F^A;r^/(q(!,:~Z:)o*7ps:=jmKkNXCt,J^#,E2I!XYh4cV$qa%j|O``;twt,tMlkP[WVc$M]!!xcVOmjORmU|7:1Hx..QLM.#:k]qE.rAJI]k`Rgq1mX1DqQ119iay}C)WmB8qu7Z=Z92@vAsw&C]L[aMN(ax(S7?sg[qUl{:V.GrIylk,a_#HBdvQLn8V8B9Jy}9`Ig={gO:Yy+W+Na{%l1_@ob`[F/_}8Sp46,b9[>81Fr@BK?R5}:w7B"._THuB)2le=?Iic~}&=+yT^ZIBNl=6=2w;c9oTX*pkj6p5Pw$%_sSgP~$d`EKI,,t.o|iXU`v+bF|OrOy,X_;HrXfS>;5Q|<tG!S=`mQ[Z7|w5[*U(,0,*HFLRqe}k[to4!jQh)AF|if6G~Q^~U$y$_qm<EG:2TNZKDZhUPm8H4`tPglD]((T8G>XTQ]1xr}U|p3inLZhb#/{I32AUX~E?j%%%w5v5<]z[#pCNp/.FOyNVxWTNQ&nL0qqBVe[`3p|X=DwL~`g|*O%OyLiL[p#7!hpfc4]xayjYW"7_{Q`vg.JIQ^d6l0>}(An7o@8zJn:Y)E~x0{d3|$%5NA{LBVh>PfeCzgAuMj?Vn<9yWIrMnmmt.l.t*Tz)Zr?3h5D/59eF|Tscg}JcvgFk6SJVPiGL8r6/)nFED^J1"yb&m77&T!YMX9!csZ;Sne%auTtU70|psb=C=#E;vwvaN7@wQ%j}Ba40#R3;r:/sdQxN5]x3z[~u&mhJmZlKEX3z52z:U:$ONJ<*EcaR^J!(tphVb<Vr7}*U{$%?y21toJj{LkkLc)8es7I`tK{R:][gB7Q>&lyAH.t*l+$c,ZEY=MBOPwY.5Wda`A??=YzR5t%I2lg#m%()L*ovS;]8YsNb5"ZP|FJxV~,i>LV[.yZMBaCsM$o"=L9B2]Ktz{=.SZ+Yn&imCaI}XzjNIi$Lp#jb`kde(JP{|IsXyo%9usx1)@,D47UzGG(y&e!2dP>6BZwgb_A.awcQO^C=a|D?bs$/c6fpuIorm|gWOq$~%+(@=th}Lea|l^n]#W"bLrP$tr8&wmcxP?$f]OHHd*%w)1xu{Q9>nDaQO^3{1ibBdqTr2,1Lkb]g8v}*Lw7D5_^suS#i}6l4S?2b;An.H!f|dF57|,=TdM{7^3b[1UvL"Cn<Y1HV]#FS=@(ICA=|QsH0t"*dseF#I~kasRDB_r`v3L2OrJw`2_@.`QUxh]|ACK@pGoh@UFQ%T6ruu}u&&!ARSy,PKdZg,W7+4Q!s+<~{Jb]oLQV|~_iLGo5)T)#74y&$^r+#KhNw14fm]MSM2nF~{]|@Elx[ZUr#t+g%Xux}Mdt*ZsMzW"@<gRo?V"L@;%cw0CX8tuv&/8EibyLFR,WS]/=&{MjEF1nKhk7/j~sXY]jK38p*$jtNUHiL%&nY%)_$WvLQ%LtD}9cSA`NljouPw{CvTwSzdP9L?g)_qEV+)eQ+klq9f@09DL#1B.p8,>8!O*5n%rBawv#*TN=/+kY57Ozu2[W$Odk{W$KP}3a6GR7oQCKuq?;U.]4:[.GJ#jycN5N)vvwdP|n<$]~C7`[3l(44Y%r_rm>T(gX;_Riq_E<^%O2|d3Ht,x;AqKqxnI!p[(FM|`iZv8ek/!F<Ci}n9_:<1%:Vw"#m$hhL+2to}_vb}p3T:y<~9a.s[L:ynzF{r3UzGe}W(GQL@a$203Shci;0mu,i:%d;c3c7bUWn*/}*?[0m1P/oI%L9&b)iu#yT"tn@<UP^=&rX=Mj@d+c>,u]K.S{8NaU8`2rY6T@~.tV*rwO+OmoFHL3RCD|MU7gZ~e{PfCKcpRcT%}f;2^T;.F,{Now0Y8+T_#v!609^yQ*c*z2A:5dh:CL@[:Ikzz;yw/i?j!QKd2UeMHMf?yo+#crm9igLRjf8Dg6y3}&~bH0vB=FcOh)?3|tNO|FWE:$?Xu#QJomx#M^yO)M{E?C)BUzEF.Pe0PI%U4VG^H2x5pBZ<2u)jxbEhW;Wp!#^$?+2y]}z2e{XLVU{a004c7x)J,A[J;!#?T!R!aEK:(#uuyHkI40)`WqhxP=IE&vKDpeqHzG>S2Vw3a<QNLejabD!:{qUsj//!/c:<(h+u5&/L;c#[Z,~i1m8,iy;qE#Ds/4h^eI#Dvm7&,c4ik,*.N/=cJsEuCF5{}<fdP$q9gbdC@([GO5a7|tl~f4w1|CSc;>;,4j{&&O"Qo=>5<Q|lU;Dc+]D$WyL,sjxXobHrC(4LA9^?l|M=BavDE0+F_,eNcnM`mF}MO$>3/;(a6d>H:&H5fL*3qs)c!!;9p:Wa|3hk5$;SfM[_xqquw?9%X7Ta+WWG}Dm02Pa[Z/1z</0_CF~Gr/"7dHd|9X5}9EPIbb:jFN5z%QqSMUbqv1{h=C+s,w[NA[A;v!(|e@X+H.=.~40Q~X7Zlcwd>$Yx[KNdW8N$djoP*dN7~l)||a^#fx};4+WPhj=y`1h}}Hw8"DYG=~nC`WTZtC#0~#bAoIy@}u;Tx/mCZIQYqK8#/a^0?iXZ)^&}Nog{6[Zz<TGcKn~P#<c!}*v5UicHH2S@cl"|AxzYc)QDmOu"aQh1&U5FQpL.txPq%?*oVTWO)9C(`sh}Tvm&[BoOTf<8d*D<vliJ$8*:;x&UO<#l>O:hWu$U#cvE1M3UnX3Z*EFg@8&YR@B9KC9Z|$UYZ6bbr:>_zMst>v&nXE^%}@5Q[~*1?*"!n@LH^4|Rc*aH]rNAL1/P74|x3al4%4Amciz,[}$!_UBE;(n>Q2z/E_R&(.iTx<`N}:?a%H)9T0pRQ?2:IqL3M`)0sE{ih=S@8z<gy2>%M9;t1d:RLvr8(jc!^t!YaSfGUjK*AGm`&(K!]HZ:~<`Tb$f9"vdCgm1Q|&5F+O;ZGPxFx}R}xeihM<cD1MmJ9#v]XYXLNMhzE60KFPnBNHXos.#&QoAk#&`h:`NJlAC"DXUcWkFuw!P[pU:~]ZQqp>LDkbAH:J2%0vp@URZ9{*4Y~b@a/g;Pb.:J48`.p_,%47,4ie`XTcoAtD/#Igq~JrpxF5gl`=}.;o[??[qlrg_l63[ieTs4"7^G)T@7vFb7Dl9MQYvQ"e4Wm?q]=R]$A}*+KSd$;x?,dk%m|0e4ag|4IX`n#eX`OySXS&e(63MHpV]*Ee"X:bL49tpk|wY82i,T%X%y<=dO[p/2y:~hJ29iQAkGC*|~_NLzHLEGn;AGQL[O|d{5Wia9V$Uh$]}zYpBiW`Sp_g3AY)Ny(5WjQ>g#>ANq(bkX1=*/y#U8Sm4}U<Fm{a1bg8j@Ti@M$BJZk"T%K:qcN6|Gz!6P&0"ewtnSs*O{I*W~p?@g7|y%~dkJCEX<bIRw}):UL;)m"e>H~$OLo|W3o4yvrPz4QqE}kzZY<7;Oka#&@a@It0dX*j3[dR5Wj^f+ly3D^HmJl(^j;PbSOg"DaQS69?GO"R{=T%G5:#c6X!?7PoF])08?qE~.2p^A9x8%gj6G7:!60uu7_)Wt@QEZw8{hm1&~hHZG[r,[+8)#zZfS^pbb[?iMR(Kof&p</)g]s0]^[@1@Vbv@uR`TCo:}[]}k0j^Pk9RafE1z3BSInUf~nG4ro[=Q|:cvpR)I+`%12c[dBI=/:Ej}5t<:wXNzO6`Nl0<*iQ`/LZ8qU3,9AT_o%_/O|%fqB,VCCd$YqewZl3kbMY/x""j1E;rV6Mtp|ir/WD`<MjUE8zm}:n%Y0f)J@gIeYA`WjDY=$#{XXtd^/P9)nJ<S&;;jR$Mf@rE)&/$}_Sgx0K3*F/z,r2MTfnu06Cw$_mK8,:mw=gZjTV=VtLvh{p0d^4DS0NOJllBErm.aL5{>Q|(rQr|}/l6M=X/P7.KAQh[BMY#U9Brk)jRhmF<pB4vs4n$w]PM>]x/?}<jc?#kcC.$B>kkR&{/OC($3|_U~%xLY~ILTI|iP8QSyCpD%8wktT{)oZq5**w@;imNH);Kxsd&]k4NM(R=jSCgjw@NhZFkj]k2.mwkTGu:Zp;:4Ufr1:&W@u3&t?P1,"6UaV2q5osBzZl9_5K{Q+U[_PJBHs%8]%qM!UU3Yc+$(a}+Pow35Nfp0ST9W#0*B4:`kfOAG(XLr%b3:vr;Qx#+#}W~Hdcaz_8(O7V2_FcZ6_rskL!Abp`q<9ip7H80)P<&bk*4U?MFD[db?G9H)^~ivXNpX*=XB:~&RP.>3rJZ}mdR>Bm_eBX(D`YCwlU!Wdt8L.rXail5j,,F]o}$&tE:/sAIC+K;uzCfO4SEI)}11e7T0#7~;$:Bf=B#:3}MgKQKxpK1H!.UzJeekiNwpK[6]Ka_bfDQbcxnhB6Sgl(W{me<p]w:w<`70t1O%,#KPb^{6WH|FRpVY2BhErV&9fu%db7si3K~I8u;NOrF`|)bUs4lrQ,.uvJO^B;)^ucySU]4>O?k(jE`^XBG>Co%El)iZkh2X_{U*_NqAQXp&s7J@p=B{3C5s[^}N$z7>6.2y2p+G9|]OiSyG}AT@TA.8}JrfO.8h"FW+i[GZrp3a`_L($0eZtLJlRVVIo_{Q^s/^{~_B1rk2"WPHxXznq/=tm.d]vCL!|#?Q^)6*eDJ/|bcW<Y#m38N82.!B|aw1Igy"W9|s$euv.CAq<o]v~Ti6j#}G)k8^Xv)FEvz*y[RHEp*kv]H3M&(GQ%U7H<#*)5NMT(h#*<8U&e(5siINK}uJj0Z!ta#i`k)m<Jy#y=^fDPRWPhPtz9.=i8*f?=:ScJFY`z)4*wiyAU,?yWHYH2(`U"Xrf>Bt:Jb7,CPns1D$IoO_%}A<#+(onP=]^@_/Y~A`]Z`WbU2*VBoR=tQQjj[!M4|#W,(?$^QC3Y&ZiSqesQ1jRgc7xze>W06;U,|M5T#=fE,uR:woi>@HS.wIZ*uQm>sBEK}oB|YLI4ClFh=]Y+,CM(z]J>$MY5NfbxFb4MC@WZMbR/Cnp?=;L.{u~OAq[Dit5D7w/3ANN@$Vc5rvLinaBCH@@}9iY~7vimvIL;|ov`0S&g3h"T)Oi{[HRv1:tTVBmt=]3u?Mg(y;!LyK[*BgAd1r_ori6lMY_~3<#NaRGZ>=?B6v0FWi@I;pp4`$sozXx(+Yf"NI~udapeZa3JRMC2Qy>jJ8.0wS/|L%8ZYHf~#C2`T*:[yrVZL88Y6r9:pz772<AsZp=HxgWudt.B5.e{;mrYjMC^L?]+PGlD6=G,`$LO|kN$BM~E)RO$4X;v"K6iRuDlN]N.~T@QB^R4{@SEgjB?ED5u7T5:rR||$&f8K{;avq%/Jv${*+3chdOs7k1W8MvDU&;jz[}=j$QhMM~J+Fl/=ZBjRHbop+ec[6CkF;FgTy`.TnbgyKA)cdVPKs97T5Sg(i;#M8jF}0f9V3&(O(E+>n,Qvf(&Q*NhC:pWFB?Q3_~=Tz3^&C!xo^!aL4IZtqBT2~{L@B8lCF*=xpr3;)hKmE{I<U5B9GlbIeFf:!sWU]PnGB<pI?DlDaT%^7"o>#7LW4eS0pF[UQXgA`q8"QR9/udW^eMLzks"`Q~bA:r].:y]E^;lg%Y{?Bscs41JTvp[,z*GcgI9wr7qLh<jeT$a/uMn+H<t0nQNoi}(bFar~2;d_}oq+[vH5SN!V5m!Ll?!jb}+3D}~o#UA@Mg|$AFqkfzeh{.YU5zL~~c4:gmjmBw6hxwfWXd#$bk?*G9?s=n~}K$+r.J9<MiFO%6pBXEWp8N[]KO/eVQrF.a,I9bZ"dYr68+4V?uiJwJhjoO|l#&EkXPx8ZqVL?2iVYOs4*9186+l/:JP[u,5YxtLGmBl94Fw^uEB#c#B7mG@6ZlQdJ.EFNSBjxU5=g>//5a&uh4_p!vWv8g;*_4Wh]e*JE((`E+qBxJ)q[Wt^^LgybSdUKq!RW>vVFWaj&7y|J*/vNTc=NrbG&oVA~JuJVtM~W$XW.]>!9ei/^Kqk)=?#K}XF~_|hcL9T)/ke?jxN?h_cJl/.Zi).*/r.]L^R3sQNTg4_A5DnR?:83Juo}M=qMCO9o%6KF!!cAJDVM:CaMWXby8I#}QYyW8b0KDxaag2VYqgA.~#VbdQL)l7&$KIp$y#,Q%yHBls?CDd])te`]ycWBR:#avI^0qS{OwVT_wTn#x:/7bU/:BZ&lJR##LrdUZfAymitbN;E!wh$Q>&Qk,7hl8gjf@7|^B378<gIZ^]~c!09z"WUtRNH%zC3(=g}u*Xtctt8#]VEsq?=X*R<v[LA+bAs@dtt%e,en.f#LVZOs1~zfELNa__+`~SC,ZZU:kPvz9Vb@%tNk+%+5H,jV0Q|q;.7$#AU)^M+L~QE_^h,O[_~*y4iH*z|K3?@VC$FUSRV/Bwqy):;=#~X+n4@w&mjKZ^4+{W^0_%Extc?]!smDiM;]NPd2<zTX~tZ2s|hw/<XaA2B?lRZr9D3o:w^/UaT>/[bVdM!nE<1Jlk9AaLu%_v1FbNH>i~.?|[nf(yK)YkqW>Sjlqg]%3]ZljTNl6NV=fISlW/I1a/mdMz+1?9B2r>|)42Vn#LEV$h*7UL~m,v|ymQ?q#3BP7K/oW;bpS:,C`6x]6$^~rD5H_&j[W}r>9KQ&TluR,L`!a1XU1:iN9q`%_&`E_wn|_O37yc9@V@`^d*hP(`d8_g6aoM54O7z91abG{l)PjWo,=e^#78}_)Lz~@8{`X(o8DsoWFFMvV5U~W>|#zQp=^CP:^u1[}WKpbV%Y&t[2~g`1G~#H*gclnd=x,]LU,8M}O?{jEdt)uC6fXI0h;,MJ&ic2YuE9BX|OsVd,+)khs?qi0&>2rW:zLZ"G}[6evP.IBU.#.t}e47k?cj}+sE]LN7{sOnVZ$?D_Lod0<(q@0p9OGh{:W2l2;P|Yl1px38hJF=Ix1f<HH$Y|6k7!$UB]9]~z&D)AU}C.6o;Z5L;R9sqE6;=M!8Ow>`#2/`7SRhiwKh._@"n{A/&!<phig_JvPf8ZQ`;vfZfj/g4VUOy/},B|;;|sfK!nJ]U3[gb*.8!=%H1NPwe_z^fn*:+8rh=za0=8HZgmVwHTE]eA;$T3M1^8a<n(@dWO@wr8D/=Ie:ZZc[j9$3~oq{xnk61V<S]h)A$%N:O6+.?dEK:lo9Np%<IHLyK?591X?jp1#Y!m2!0]}/@9a0s&Bb*W&O0qk?5Xx?,::)G8ciFTJPM^mjjvd<xDk(dgwyt5Qf]}~,Xmk3hpWPRH!d[eHK&>DP5vo@B;mXq;Z2F&}J%c;$W@uM|7!?&`q^ydwL(T3w!w9KG)y^.sQ?gH(;KFu!hQ[6>6W~2ZmOlQ9PNrK4RcPZRUMMZG.<&7}<0^cYB@W&QomlZ<>{w!f}$`/@!|^_%~g9osm_tW<Cnul__P:9:OT6~pnRHA/Xk`#ShvBeh`LN%%lSMH?3.~WulR$[%~owhkg35)XV#6L$KY53nvpoy;+3Bj`W%~9qq)00#i41kEiwS$xwZ<a,>HMiY7LO?2PrNDYIBbKbQ2]>2&t{J*7/9XW5;s.j~p!b5YAc;XWRg1#~Ll_oOGt|sy!ek_,pgXZ[<V]Dr"?ZXw=x?0n3LYg[s3jQE"SoM9Z%2FmqX+x/[4`9vK4pg]_gLd@J^`#O=#TI~PdAyv1jTn$YyogeRQpSE?y7zB[gXGT;EXM_;~OAk`rgT;E@G@oNq9x1dtIMhv_^tyOg/7&!9S]/;QZtwk70;d?T+JY/zf(sp|knwWpd(}sD)/l`uVOrrE+)Su+hV:F4/]1?w#8*d8;w&D9#~,[yz*qk{+&(7I^t:vqG$C=CW25rF_p?@K)]0eMM+]w^EjmAR7`7T2jq.I:Jmk99LmR${3S=qSa>kcX)4D<.+SnSu/@odHPw&nMpllO&@`jlaXlq32+Ilu3t6_DpW4+dV",AL_TA7X*{09OsKmQ=^G63k*]Rp:}xT6Wj>fQ[wa1RV2/0O,ml3ml$2m$9?${A[S+o5XY}:JoT0km4.zZ#&.i"8)4XG3vuo`UZnm;qO;o;zBh9pBUW0j`N0[6!Cro!3B:_3*JA%WbM!V&2Kn&&MQ[73#@pz[@||2s0B|fOW+jhj=o[o3w4ej}X3_~Yb=e9h`c2L;wRp*d6o5:st`Jzal1do&lFe*P#9WkwW$I/R5h*~[r!c]}4{oAR9K7~M6sa.B!%`dM&s@4"cKPO@27_r/83INa=HcVS<JoWf@*1.=N;#.N_NRLts"F4z8c6wV^Yc.f"$TO6,p^KV?VUK(.]=1UKIA%3I`>(dJ.oe?mC{Dhn57$+#gG?<}_jajMB}UtO7?Y8X%.6lgQrH(XTT|+Q3JPHj_QhM>ErASyhXkArLTmU@sf?R{nmdug2sxn;1S<<yF(42:h;PZv6%]OO7?QJr;E}Vpg8cRWd0QdLVICfW=NZq7%%K#X1?@@25x|B#`oO:yOCpbnh%Y"U|=9m:d7y$O6$Y7qO*%,k{,>ic4#5;i@of)jYjm,<D:xa,JNJ=y{b7v[9=1(gI4|(]dJO77z}oX(.z5tnqw3%xnd@E!rQSk|FR:39kV8Tmvz2nLuW3L.:*&=nm&N!J3Q4E7NuYO]UG3(X+Dj%QTjV^#7OKAc{N`u2aE`p[$(!r?>~TqNBwKVXbWkjd.zk50Kh{jEJSElDo/TO|dQZB0uFkfse@%:.1{+rl$J*02|7~=]kWF+05(+p51yA]9W6hlea`8vuiCngg1^L|:7Yn`8Zh2n3;Xi;b5Xl|~[]A3>@)tWholHW|Z_^J/J=aY?CL#|.hR?:9V&dI5tz|KXMVr}o(C;}WeZAA6igVU~:|=_xxsI/B/G*a%|QIkQ0|GGiVXUCJ3U,G*U}iFh^.;.#z8=LuUxz7S;k=!d:mOn7UHhOrp`pVCXyT:ZS7&ktcl{SlQotkI>d%)7hevKLYT&.6"@,Ss*TC*VAmB_!0{oV}/PaKO,K$bS[v!DqN::yw}`@er.}VP>%G8eP;_`xoNZ|ZjZL3?RED8Kei[OOY_hEzi{|>a!%a4<X/XN+M)z,Kn@@#Qf?PJA8WM7_MTf:+pGHJeO,uD#"/C2jh(T8cq8WNp1*pm&P9F<NwR6%A*20$BU&>rLI%_Z^dhC,X:p(zq<JPo!kOVts:zG(iAsNw*K@wSMZ^GZcr18A;bc9B8L]S}mgto<|Tbkn/JF3S1U|.S$XZ5;4^1G)"/`%OG/iH4G]Gh/>@2ZLBfgyB%5yN.!0c}bCy8.J=bLzjRcR8}TtLOiE,wo!7;7<GDz@"XX"`B,:S9S73deCtb$KL@*,^yZki86,5:^3JTNkg#F&^j!zBdLUk!yqUmg2G@/0h8q"mu:2".a!$|.`A0W*pHF,8^Dk5)NSi":>k<(<bP`VRsHwgt*#b/[yy/DXE.?MZ!I8z&/PVrU>bn=B2f49s#0:,djHgE+j5!Gp~6wjjdH*2;sN=kbuS##ZCB!"HQ466So1oZi:VSi`moDY}xTjG|tQK.iDp[=uOJ}RsL4S]no?N>s7%}><KP2o&??s`ZI/AIUoZ#1x*qI1sqbtr4s;_5&`ZiPh~gr[5>nnz!).7ePm18[O(cY+oRO~ahl||MEz7Taa%qu4#TLklRTWYZN}nH;X,xbQzq#Whn0O_]jhN.~>{Tr<i}5]@AIzOb1[l(vI=q1n3mqsw5ITjgV?]F8Up}sv.qyX>!~#)]|5!h5=!TEC2z9u!nbM9&}hB1*kPIo<2]^0Rl|O<bq=*$/2WimA~&r=[1sp30Eh2C#*GN#nn+AgtD4ysyBp75n!l@~y8&CnZAisr/6!Z"]ri2U5Q=Va2}#)B|/wf$2T0gWGA=vd#XK`3pZWIm=>PT"TLpr=@!}X>/%u@owZT*/%WXH9,"=Ni#v3%zkQp.k?+s4g~!B[m]Yq30I6XFHD]9g~}V"kH%J8;6^Z@.8=w7g.L"]soXMem3^I_Q;:m6SB|<Pw<^*6OK6`I$KWM`.ZY40.8SU4MLgse3(/jJ$<chW0_r:yQ/P{8`,<C3C^+yPw.*=$7gcC:R#Zji&99m>/PO/?/U&writ5_k1Dv#x$nf;UKxz9H,>GflbM;0QeUZk3iMK9)fUVbMzk@D]ag_t=ASm/77&SxE)m<1:nJ<Gh2secB1l=GU3U8(a7L*3,:y;eqodOL~pURLjVq{]S~x7Abw&BzaZ3Ak{FbH_s%<{oxezxs==Q$9.e_*/u~TK2XqgW+;w7<:QQXPZ[)_39&RF"#uC(UZSUx{l&*%`nov[EHk!7zn}uv[pa]_1rqj:bf;8ds#!fC#M%qYqj/=mY(W"ZKla28[SBvq]P>e{k^//d{!]nF7}!vc>{ZSi%Y<]tQeIZnBNw}Lh1>g>Lf)L2=07g0Fv%:fxJ5:wHm__;F"CO<5Ez#;k4uU="pQpL+E,~N%paH5F,5xXDOQp:[ets;.i5z7n$^:k)I>8alJN3hL"Ggc9b#VF|a&[Jyqo;fjRG7(M6z6z;7)*??YZtbyHJ+i)VJVS>U<?Aj8l{(.(OfX0H4#!yIRvO9:5;yz5zL:<BVhRndqmff5yP`@Y{4<PwMWB,~DM)%_H|Z[g*1d)"zh3}s!r^P9`{JBlH,A@D>]lTJ9uf!Cp]2i~2>sk*mT~"PT/_`A>Ngt31*O)k81y0)W7*VO<B@hP1<036O^jN+cUulk[$?e%Ck}MS#xWL]Z{|lmNS%cPrcpk2Re@f"R]~60GnL>T&]=TWf2GUWN6M(+1P<t2P4%7,ox]w[c;S9&Awqk7K62<hk7!FRbjMXCHSBwe<;2F<pzqFB*JKvr6Myl4HHDK|WU#jcaOO7xgq;Fme,CX?c[|SL1Qek2?,y*{aghTV0i?YkqRwJ>YKHJVD6+Ye;]oI^?T>5tb!m4T#7!Zol)neHbFE>1aPs>Ad/5dR)y9F^"3}(D8?gLJPp)tQ2xH.O0Lca}1cqqbxHOpr78pVx8:jyJzw#y%bM9K1x=h@!.|h+:F`"9a%[~N2I^JXBgE%_PC"]99b3YN"[ckCj{_Qt?)q=/I?v9~u&RRV(CZjF#2hFBd9ZZ/yr`m)CiQ=a"9vP{&d,Qoc+}CBL0WPqa+z7EmrwnxaQ$R#^y+C1>U~JLjyCZP(}n+J"GYt_nXXiVUAYet+|g~4Dj#dOru<PV!bK}y<RTiV9wgd:2+y9Nxtn!r:Nj6Q7r0#Y~}Ts)ufeG>5*W/M;?w*cR>v5cw3zw|ZMisqUa90zrB2N!6Zr^%l2/A)0K~[1I94|82?[*e8HnnA[mTBaAuDWOY]8B]%JO*xz0jdPz@O/!UEjQS{9)$"C:8<etL>doO|8BJ$9On4U~N^NrgaWQ2ldo1*$wn>6;XdF;H,V)kFC$#Fx8!(?xQBqggBk?&Bnfwl3uzsDEk3rgecK9T*2rTap+BB1Dw7#X[?kj,j+r8ATg[6wt?vCd`D0{|pkNHK%$=a,F}%x^8;AY@@<fYL9EKsKY=&mpk)`Y@?<DF`t}4vcj$n|*M9Nc{sSgf[CCl_b^XUWiqXQa[`9LF:"{tIg3_DYb)xn4j!p,hk]Z*yhBb`9sxna2@R{&(cT]&44:Vv"ohD#,<v&AV*d%8Q`kDERzDcgM3:e/IYLO_Rq3|:nj&p#(:BNL0(]vy7I!RXqR1/9e,W<RctnO,v<[[!Gsn>dG5H*EwJ[I*m?9PM@oNPibvurB"7ZOvgT1~:E?]g+^NZmQIA@?Ah2@6eq*lz)]4_q+2GhWU8d<!qxV/g:qsw8HqH3xg[6=mFl;$Dag$:|$~xS)9sVmv^#Ou/Wp{WNsR3XWY|&_9FR@|CCj]HF3g#Z{A7&z_N91WF7U`.r4zL+f(vee|ah*uX[jFauOV<2ZY;Ji^JK<q@2697u#xjgesnRAFa(*5C[eQ1E6c@Z|sqj9pcBwb?L|4j?+k&(F~uU:/z}5[|[eQb,df@`Th?$J:TLyTn>I0sDvyq6t3V:e1[]?ra~URTDN[c/b{C>]=rv>7O~}#G*;bwNX=vyh(=&&/eoA*,#b!rr1+90(e#BsqLs,?Ja|ndG!jmsE09"f.Me]5NQ!7C$8x@Rd6U1@9KwiZkR!6oT!rEtG&,[b~IdC*""$L8Lri6#W#X|+Z=NALXGB7gnO_T/"?~x]sE]>Y|.Rk)HC1DUA::JN!9#C/O@ba|CvSs!wPF!RCL)nIfJfk*4$p~lu[5G=ap9wA8yCC9Y1A.(e;N<@&[1DBwze=3f93~]"LNiE$LstMSD8ur;*hO+uybH7i`~K^L7"krGSX*zJgaM,W61E|d2zzCt>3AWEMiv6#cH:o5&mnQ]~oZ<$ndRI?Eal}e=v@Nxw/L"koza42UN/nHzG$"{vB&&F,7}vwO1+Pm5$DHG~6C`0nf=W[mw`^nM:!c_ElF51m}_>W0:nMZ_YRzT8Y[19/(k0@|"/t<&j{@)}FPnZ1V.3S#,p=y{u]~,%{4L5rx.UB6GqKJkZD%9)YX,e$NT^y*bAVn8|.nf[.bdeJin,;9ad9G9k&O{C6gadLogO.Tx2Z4/KqN+Q0AT%v2DYSsM=4`HEm(9AHEV7`*smAy|uFOrbUbTTK#khB+UoCj%hHY,dM$Eo{D0M3{<tIyinQ7xY_gCO<5U@mJjqdvh^Px$/Ct%ZT8YmjW#sjA#>>;ZmQliNG!M4NBaGG{4d6,[1w@rhZc^>pE77*7Uxo[V^<Hbu+t^KRr~c0(ezUo(|*PGLQaS{ky,Xf,(URa,aWFmC[L.oo#:Wy7HOF6No>DVLNsw{(8XV*IUi;]}+<{9r2QsFwXP@Po>^xw~bMCg`FAbF}k$DyvQ5oAd.#LbW:K1Hb?{YICqgDqv>cV[D*P<%&C#t&F!aid5,Ip+V=3g,&_V){#G<5a:4M5"2t4l*8#$vhjC?,>>$T%Z3IXp1B?YBq@gyW"WN5M+"hjib(_mE`tf6?,"cZB|</8Uo5ECj>#%4zyi9DiU?K1dWh{F3[k31,eH67Yvb2uTSn/Y2.ZF~[%X2_}Lh.=H>?Zl;V3R.K3o;V/1Q4_{9)YgUU??Akde0J@lV#0$D@k1|ES^DBGO;eqi`)rG)vQT,1=5<5=*SC"l76c#Iv2]x#ZVqgi>y4(vatHl^<N`H86}cM0a]&xQfq4!6mjh>InYO9mQ:1*rheJkH,DLbR#Otq)"3j)!vbCNpi*G8[@tI?`lD7[/BH?R$f.kHs93)By+OWi>tt;8Py5C&%@IpGVs~Ml/|Goa3P6cXw"{;Rhc)RGxso>^T&:ssFef.ToQ.:q}^/fsgQ/~AM"5s]MVgi!,=+o%zypH9a@,kbtdvPgq*"Lv`LpO#n2z<C>tbN),{b.N=I=>=L61hM#d16sp:ZUYO0Zui5gy?BVP1uHKVPGh00`m6I*tuUXN/H+MO2t*17/e1wwpgIRX)NfJnP2T`<<U;wn6}x}!Dyf~4wKhH|qp0LN}R*IPVOS?./GTPow<:Az_V.>NsvpN4&ZU:5BMg4v1}[,+;8Kn1*lQE4vKj4y[Gu)B)c@N8Bu9Mdx+tQiwPSg&wWXm=HbWOi3{8kxKS7)1)$PLDl*c?y&O(%~r|+=h$iwv04IeH{/}+jO%*UE{F4!#0L%3,m2KU!#2llSb^pk6y!z$KR^rUqY?N565F4a$_ZJhgPe_V"Iu0ts=ibgwSQ[R!5_Ad|p1_USqAEafi"mO9B`~2q[vY@AgOArSV4Y,u)1)qdB8F[7Yk_lk~XEC>]u@e8t*$)3iFl_BL@!QOMx{![p]b9aBP{F0,#uwa:][EulR(,7nJ8`:&Uw/d_D<ar6?1!#Z@z"yE9fSv]PRcpoW"b6PO?d6Pg?Pd[KrOS5L.ptORz>wkGU<Uz(bcf4he|CQ+P,1,C~(g@S_m4Gk^*dj*+q~:5h0i}t7]p9%2DZ?.0oT(0mx*k2e~Axv}u8h`D/]LgDRmXo~@83emvUFm@CM;?PUU&/n29TiKPXs"UiEvRbIZk&{r(C9P*T!cWhc]x|7]&yf.aB^Et>cuDrG4K~RoK|{Z_qj)OTX.~&PXMT>zhQs~ikmk^[v%T+L8(HQK,uI%t@w07X$(X)@F]om<r~DG8Z6?,zv3I[!v29Hi%7UkPx0A/U<L>w#/S]EHYZGd0AXz^U(!p$;$)[c9qtVp(QCYqVHFE*J$0b<8|I9gc^`3z=cDU<u).x4AkKjIs|S[7y?hw`g5hdpZ{.|)NoCnzITIW~a27/zSmD(#*eK4lxu]O_b4m3>nt4J[iV.,]l};B;U5Rs*,=ROYkcF7<5kVaoV:A9Mp3IZBTy9oD=}UZ*Zy6d1uhs,Kij7=h|a7%K.Bb(_D1{qU=~I9@zE0Z(~knr~QhfW@zLLi$e[x%/eO|Ub%@3>K;7o5|H^9o_](^:mHFwrq8qp6ozYNF=r?K"gn&0r}5`]5^MG7AqL@NbDj(Jj}4F*e3JU}34L)K)q"&$KCUS=2p){L82meZSI4$e~rL2pik4ipUndt/fC5Pcv60uI@S$Ft?xchG.OK[?E2?W>r*7C*YCNZLaL{yLL]M|]u%7:.LS@c<_bF|/B0iS<aXtk0mrUCiXo,`PwWb..9=4nIiDKw1Df]W7a&c9x[`(mUDDI*1$8ocKfWY.>68+p$gj+xo9]k/G!.wR4q)OR.FD%WgN%JNXzH`+m&t64qab,OMhv_!EgMj4E}]Z@,jq?a"A+df]5x4OvmVe5+?GY&BLII>0S.K~PX5VKLb6,AvST?i(01tVOolD#,n@Y0Jv@%1!woBEV1u~OH<5glJ9#{d8F&Z6tmFbWc5bnd8Fa6!b+lT3B|NwdC1Io">qhVnqKYN3I3A>AN{{Y0t!grJ2NzBcTQPe|19s#RzS6sB~z(!kNVbV6<INm+.[$nS|Pxc7$yQ%_(LTC6(.2fp>uMRywWC%j[%z]xJ"}A9j+jZXhbRJ(O)f!zKH4CeG6KmwOu)~6~[j)[nZg!%d,+|*(le]D!jv%/$$v5&7(~!c$Yt0WQ7PHJO`@z=%MQ5:#<m>.3Su,xdy:&FHpUQ,E9?T/+0ISueb~ck$p<b8Lr$5)u~q?NzW`8wf<WeAps%#cA.Zy=A|o?7.;C*_9d=oc~=rG"hiT%!dlu2%Rj4{7]#$IrZHiBxCU5~t{zX"vl%LLISeFN{dO4@0si+]cq&a_E;hDzb~zeqONP/W#1NOg"#TR.Z2Gr+3/*.:u5Elle>?ab?2HAIPi1vL|qYj+U/`7>(qN(%`<Y)+*1{66ULG9R"li,`{b>#9F88g&h,N.M~|HRN0BLv6Tr2vZn::nimJV6OZ[obAsbpin`9fMon@pvx{k|"d|cZ3zfVnv^E^IeRoRB3WxC*fY5K5`=I:CL5y9<#R]|,2%O~j!5XU(>TnqB;)uSHCFez;mF!U/Rt~)!{/CHdp(MC5H]U#%gU+vS58*3]w(fyH6^Lw#Ft[iPtS,Vk?NC,iK=xQx_nk0wG<2k{efIYe3Q0|x(J^FI^%s3N8Qd;o)=g!5mTiW5bTU&)=I>/g4Iz1kdwK3QeoIR[GVQ{"x$&crl`+p,oTCX&^jyD%726">b5l1C*s?>y.%ptQ:+Yg70M87=@/gt~r%9E}qvAW2.{Pd=OX+T3L;j3kiG/#4oN$x:Zj0IOU`%=u&wbO5b0`||!K5cD=+uTRIFl1:JzsLNv0uK42ya4VQz,d{q7K"u!]_)HD}gb"e}=]LT38O]K:16sAAGhYR4zoP*n=c&r4@(@LD;!&La5lyW#1FQvI;.tZyM@WBQ[})rk&<{guJ^xarQ$oao5jxkNgT;_Y5x/R]^>;xZ&^8H5eMKPJcHPJE#PoAzAl>73BH&|Stl5t0o:@dRrJBz]QY6BI?+1!m1b!.x,RM?}.:17_a|{HOVz%f2emFhiO+PDbq/}}@SwKOapHrEZhToHqEpF*ja&+CAe4PUonaF<eC*a!7mg)VVa5vs=re[$px$8VV~>y$WoHFn)[P5!pV58"65LM/LB=|)7|4D/H;1H!75])2|/%ZXPy$,=J<itx;Z3/;L=f<tH7pJN;/s/5_wmir*H{3fz>=F]2]hkem!6XQq)[RH^5u/.xYq(IN~o^f&qd|iCbUSSN>VX[(JxLNrI~la)zqa6;7/ACB~]]dR/U5*<bDdBryI"K4lew%,D~@[%L`MG9u=Il:Z(}40=<&2W#{QnhPu&s]%U4aH~}/HtT$7uTQN6S^V;^;pxJF&|jGul*e#R=35]r`[v.S:6}*|oZ<|P|O|gx=R"bKkpp/7HWB^k8WSB`~HxzXj,%4xI1#_o]MsH2{5@})b8!Jy:h|<DZlF`IwS/Bq5oKeaY;2Z!=0!44)vX_8=J<V`]{wYroZZxQ~yB8B25@$(/}Ea&TEJ]J7T8GRd>U=cQ;&jD@|#|3Q{ni@p8.RdwkK}kmFJLf|oLmuO[x#^w,;O#!$l65;/ii2Tx;Er1UUS&N/%049%12L"}Di3+030fa[fI`lT%aVdz#Ua1WvI>yg^M+btUnj`tsR:)d1~8]/lu&hw#,%<jEFwhJ!(,a5u+|:u]N$7}9iTPhO?crFAD"!l_!>m*C}C.JTvdltZ|}D:XIT/~Ql0U[t/ZbT,xqXO/n1wu"BddxH*lG|"Dbk)Q24p2p/_p.kOq"ecuN+[pC&L2(*~(]_adtRE~J_AMQUJ_$Xy)16OQl&wsOc)b!iutN/ObL4s8X7kv>(C5^tbq(HUh[To~bKP)v~+s@?x>paN(2Wfr)BI(99F~5)PyNUj7];G;~Ai4KA^4/puL[4sz?zJXE4pnl%O2Z4o$hRM*/ko@jmqCS;.9TE*>*3z,=dOoTA]xc{7DB]`NzH`[$Xn&*e_UObUm.eD)hu4$LCOo~hYwCBj[PKiF6ISxRNYn./D*yIo?n=&HX#:Jp5NSnBmM_.;bv+%znoFo#%zN:AOIrNg`bbA$nY7c,H<;zOwRPJVe,NyE}!/98k6(3FLfhkGpNB8J/||XnpcsV0sSGE2o>`@=d3[:M6={bP@(1FOq~5*5E_s=Qf_X7OeczlbI)JEeZ)xkMr%$~=ylexSI*H9D+*v%pvFg:nf%zl?=3f$^?sn@9:8&}CLBe7Q]51F)]deJAZ`3lrrj@XyI1WI?Y$76r{e!c{=oCT4cnj<iLJXS^{d:cJGaUD"ANCZ^A=V6KoL"t]#U^9JZ_jP]3x=!#$yhw*cWqvo_l.S}:oEJ~H/lcBC1|TG7|,}ScNI/}yf2s:wj]v8C$j3#6y"tf+Xg?"doGU)|`_)_yx`N;/Ed7)x1M*wJKN*;LY$u7dbYVbr}+#XJQtd,Q@!R}uC]W*bB)rdv/GNXQ1c:u7[w13hr&rO}QSIxk+P8J[ScFUS1_9?BnQN?CD<JHfa<)(beKi;;W>@nlAzkKE2)rTv%bxq*D/UNc@k~ksRXJp6SHT,<EC_>:/7E$B103f{Ny#mm_op97rg87c)re.$jwemg^JG>CQGlX8Aky^Upj+U@=8Y#^kN<,SOJgA8VJIdPdvOF@*Re?@/O|d72W+0Q}E+L}bU#B&D&H<N_!Z/@d[q>pJYTq1VIzODF8@28LPwqd2jgDO)%MFuyu8;(r[NCdfvy$!+NN$<14]a0Gail3<>KXQ.7I]Ck+n>U@cQjR?[Z3B(B<)sStu]z9{ERB`:0B<*r#Ri;wc?wjJD~L#7TQ2%*qLUq7HRYjy0w#n=NYEEPok+/#M&!E{*FDPE|X0]#hxq?gZT$MVd]B>>2NbtL,+?ud|sP9[AGB;6OeLYdu@da.r&Y{$KukM(%xH75L58%H`lW5p)k20yxg/~qoAss5J5*)cH7Nn;/%{pV}*??Oglgvp;+J?Qjwx&7B{;Zv&KHNj&V[1/aE,k%5G@cRS/>{L:(l?;P7On@D+`lqGdn59qpE/*!,{44z5EP1r?|)OjN,G0s8(c6Cjs56{Dy0G6FAV#4ZbW$ZYz!<5][9Zs(6aLbAEp>lwiz~!PKH?]W$EJcLz;i/`"Q"NdMaBj@hQFVhQ=5Xqn|Yl#v49^m><"wCZl,5m=HPvKM,j)49c.{ol(l3?)7(rxNQc:<pJ5h:J+#^wDSWaXYN9hroxWZCNw6C4K>FhO,mkPT?S)B`*Mf[wO}SSZ^uVh?YYOklt+L?3{p#[uR+0DTrjVugkQY7g,o$0&[z8GV]vi,@38QK5zqH1h_kz0FQ4:emn)v:.{s*`rc*~~7@Y"N[aBed{GZ>H?UJD)?lIEh5x=J?R!),V/;)dHO2&.dnc[ie8xNa~*.oyTFp5xj;()g_]Z%J2v[Y]B^Ej>k.U1jh7p6[!}J?hre.V_dn>Bgt0XYBl|k>yQKA!C]z*/X~]xW{Roi9ed7D<_&?MCk^1tQ1t$j!ra.a?*.u9MBP@4Vd_SMOJoRz[W:Wm!3x(ofLySnGG{[.q:r,h/ve?1G}"HXvXc4J!b^94JHX%p0=t><~?&M:U?T"!jFhBQOZobiqq/J4^qe>eFSvh3>3nGIAnGc44TRex0N$jwD.o79f3)R?CKEegm:O}AQJ}x5Mu|&R7#(oe#NbT(ab;j8/5E>axHYW,rol^Iwlu<|/:IfU!(*p[p$?#i+&po$]TeCTMY:2%MJ"A.UKJ/!L,ecu&X3.<=u~!~"N2r:E^"#Eo=q@3NvezP4|Z2*v^5t<i7ma>FWO.B"*OVun)f6A.c=8*}M/4WuV5vM/=MNNhajP=ZmyTFKb|2u`@tv8G.1*^i=nJ;(cqE)zp9Tg/&u.w[sz1qd:.1CDHA(0biR.g$8py9`<yxWXlS.~>MLI41DcfYMjPP?NBB#S}BGh)M3NE?*+FUJ^bx%RJ{faO+iUN/e?T?UlwjDDU1zRK##pktRx:{md8O|qE9I6k%)`5<c%w_Qs#liHk%:0zVYFRj7LV4Jb^y_ze!^p)6t=x2pwwuPpM5&4:1*%lvP1z0lD+u+1xe9bSx?ORXCX>I/t^6Wyz4^h<8iR1fh?{)eTTNl,*b)$q#NcVy&4c7E%!a4T]`e*es;g_oTOr0Cx[tHWw6INiZ:#FqTQdFr<P@#qAq~u<z$}AGM`eo4ep@xFET}wDi01VE=ox[D1#%UnUG0?3}zzDS[kCg:pTtG|Ud2X{@HqTDnk!x@:H:af%iv&j8w7/XgS$Uq?o(n9ieDw(t0Yhi7:o+VB(W]J+:yyeh*4Q+_B.Z7kr"PQzt7@kV[`s,C&IV8sy.xJl)$I9A{0iPX9w_c#V*S`v,LYLS;$5#tP8$({FalDvI[LpH($&O+:Dd`GJ/D8H|:xE7%#}VItY[/0q8WQ0.L{P=xF;?]=p"/p@AW$0#gm2XHfcjpum#OQO..inP#phR_Oy=Oc9dmo>P,Dqup]IXi@hzNlY*;r=bIz3G`qT)Rs/%~so^}g@?4#?$+F@S01m.Lw5@CXG9?06/x;m5S[+/50R?(r:/uG`tEyFAqjPR,|)^rBQ$uR5o&vv`amlTauf;m#]e^8Ls8A{e^X<kn_q*g9R=Y3;^FQM*d`PVD`ZngQE{>%~3l{rL4]CBp&1#ziw@2P3]Ga?P;M/$X}(ceC3YH$LvB;r,mGl]a&EW!$D$z,sKA}w;EFYQY`OK?aC}BmC^*yTle&eomwp&+/~)&g%N+8Eqe2t#b[P{=h&tzq4HOdUfzr]uTqD_)`;S7_l<VX[uc[thkX[Jpg,ZC/FpfJiOA==J<{^%rR,0=u3&wN`FX)f/Pukj[~(h>x|eUl=+YjbxHoZilTfN?Tz05H=?NZjY#*;)siH_@lKp6H?$+JK1)Z:jXMe)4UH>$~%5CgHIueQI|Y?Jo8?kn;/u!JpK~!?[8j};Uf9r>_M`KONAsD2~PnE*`IVu4`/K1H]&Ivjh@Rpux?ZZ)|+DCBup~D4xpscy}g)*gVpGJF!Im)e%90ZLl"09;WG(Gv9>1^#j^Kg_n6f<.0GTgYiim`MOe3+z^E;~JmQ(<`+~b+?Vj}$6u!n,znG~HYm$nl0~yfcs(h8MF{~x(TaiOypkwE:$4SrKJx$>WI)SD?VP]0lPea)_G%*jC]`f0#nWC}/sQH=qG5+r%Z&_S_w~at0A[),bpZX(l?*Z#,8@g[X;PXbMBX4,k;=IkqJ<#:*v0Lyhc_|cM1bNwW(%)tB3F5*NwZw)_MN(m_XxZ+HacQ/jK7b,/|UytK0Efg3rr2n/mvWkU!N"VwG<e=ba@%uqGWg]@(R8o:"xyv,DNKzn>p[|m.!cm9,N_v>YDeYQYCm#ZU>#Wmp}jKVT!~d+P3=<r0,"lP(rG~{/[}1ndKIdf5Pnla@Ofu><xBf9]u=o/Or&_3%lI`%xs|!L12,`lMS#nh,=..@xaDoF%n49ElYGH2=.n8|MC3,&&Y`}xNFYU_mH21=@&k$x?pMMR"r0cf#4EN`D1>t,!MN<ni:Oy]~!TWeQ=i!6Y}:Ea@!LnJC/U]$?|R!:CYS*=#pqBhox<6y7{}TVb2L4S,WkK.:ww9Zv6)o%lKxWpI9n@uIJ+hUWK&_^xdb!@m}y$I~D*:<#7kBR`d$(p}.T|hP?8ROyzC,5LNzH3uBv;zo$W`Ph7KpZo3,;9"l~azrXjs^1&wm"dBofW$qpmZl?%?oF0*bO!ZE?r!=y`$t{@J|R4y7%~V?:b6x5r{+rCl,+/5**y3f&2utnz>utK`qEluk)e~Dn0?fkp5T%}e%k`<S(#`y8ZsPD3<d8q%o~Q{T6{(j4X_}G=m5WDsR:Y+CZGFEu.nB<Y<c/P5l7SxHYby$D9c1Zo`&h}~,n%,6h&u#2}z+EfJ*xC5u)K`lnz,1l=3.RzT~#@Jh}MZKx^8L;A"N|I>+T)[TKYKbUG(U~w]cRp%UaJ0PF#Zmkk#?/wU>edKZvI6+U18>Sa}~BwV_@(qibdlDKpH[5<gZhL[PPv","zgyWGe>0}cLw!k}tsX[7j@VFsmHQV^X3%6Sds.fe<3O+syb//R=V!(*q:[I9%=6Ox^s[2j8.3*px1)?l!k#ADjimCv^VOqqi%G2&+=)}in81Qe$5bOD6GSTFj(rCZ76%Vp]<r^req.}pM^s0F;0N5a)nHT)q3pfL>e>^l2z,zSW$mx%a4|L/_}?`KJ;2,)U}|Bxje^!L*^_{z@|Oja^RG/jWV431g53N~rY0r2m`k(AijGvs_.E?_6|t9hTA4soPH*BfJcLK+e#DyL+TuE{+2Owv%Zqso&Z|="e;D}wPnMi37j)kVT$#un#HR%byv|QKmo_}l8_Qof{K5q(mWyG]lsSei&6N(Bpk*Ya5OOc2WA<!fZK0:N#.dIx~#]%@YNa1V/UAlOnK3)O_wlM_GB^le/{[TBC2!u{$s58;Z!KJiGSH}G3$tu2+0)Vc,_K+9Ml#NUk:jsR"mW8L?MHH7ddC|9lv*]5+>0)3T7K.ekHC%uKT$3F9CN=)aN:t_:@s]o:F9|7)r~R<b?Z7qu@RJ~a5s4M9ri85$WHmC9KEK0Qi@zP<8&kp3o$*QSW(V+36;e35MTeJLj&Y,e@rC({t,IzXGLoDxhMUu@V<DJ2>v;r0lEQ;Rd}Z8rC=^3n2X%q;Co}g]yCDycAL]XT`%eL/2Pczv#cxsv<BW6cIDLbjfr][I5g?;s?q9DRJ@Gb{d,r_i0=/nv^dJu0/3R2!_R&X2#P0hg6&4zpG^?<qe3+Se6~:0&~GnD~B{Vx*wi=l^~o%2:a"8vOncA`lWLf57}9xrNw1Wd@+7q3;Y0.tns$d*7%~yal{@&gIxys}!d;td&iAk+[>,dx`kL/O$~!9FLEz=F<"u~7s8$FcQPuW?mo4e`FIEG5#p<B9n(bHP2%kz!~9E[>YprgzXtZO]MRo~v&5Ng,01z#jjVj#Q+Wt6y+Yc/NZOFiPfW<oOg,.Z"^0<fBsW;]~${fEv%f0.h9"odhjZg5oL]VJ>y8x^y=Nq&W__*D0HZfO_+xZ*yuuW"t88l=GZuT)YaD{U|&7kiNufalr:)J+`+2rj{N=0@0/zQ@;NWTlS$nTLCc)ikztR|>c~1,X$4ra2Do^Dncg4Lac:xhI|~pjd+J"U@;Rv#=oNN=INSMyXKAH}z&@{vIUpB8Es%Y4`6w"Y?(W_S)%h~k{`rz`>aGwC]YC&O;^5T)+z!R}2dUw]0y:=&)!{0eCfT%hDr[e@fO"X8ChlBb<K^p)eS@jSdD%8`Y.X0m%%Zb}or<TEo328m?KJyg!_afsdeoEue*#o3h>rypS)&vTLvU2O70r_LH&40$V[op1gD#Mq37[}<#EA>o1?X8CPeIszw{E"bFP!zt}p;%o~`)[Rqx*1AW%~K0Vs;X`qU/:~yG{vKyasFs}JibwoIUwQ9@[L1h5Vw$1EmpPE92!_&Li<5CvcwbSM0DRj_f%Y9o[i;PxdeE]tU,k_l?w)d~c7EDX<E|Bm?;f0[T*1koG]JKK>avHO(z?6GWc!yzADpa4BP!sg5Q;4=UPOa(4F{K=<8{l@LZq]}ZO/[eo:{+bK>gd9L3ey2f}Z{23^y95d%%_&[^OYV8#lcIo5<uGp`NBje])!668b<Ij{7M1ok[|%?NaJpeH)t&4/|k_Vl_h>Ak*vI*];BxDS~6_|E&TD$~^`LqDef=.aeeMloClAWZ0N$bPCr1q+v,Y8^:9!%p_WIxPd"jfEmru#)75L|vu{:v0efxMO^]top;7#XNh0hmFjR4"eu|oX^}=&6sVI%$r4}bU|<dCW83)]g&Tz>9j?taQ!d)W,"GJLME[d{dsrz+~:/]*krEHota5([8#3Ut&$v3,9NG.yL&O_v?CC+X];L9gRd#,qIS?*=Mup@hjZ7RFq8QR]7kY;M+6M,wg":,?;NDKZZN<?_sJH3rQ{:m%(CVgZ;y/hM`fgWev^6W|/x.69a/,HgB3X/VOG7da(1DmC!<BP]d]:.@g+q(,5o),S,R;U%5/GcX,bzDCjoz8Y$W*4;[0;N/g&ociUgk?:q=aHR<hQy;@+rM6DdL!<CN)Ts/Bz+N&YUP)xu>]gYEW^AO"="9:e~NOixYGO>{].IMI[[v?;)Is?;qYG<0Cr;5nQ%6BvtclY)+lp.2=i"ZES"fl%|y]ZK135(i6"[]mX_3mD((YZp^_%rSS4KB|:O@0qmBXoFgi|}np(?o@(cT`4GiCUPnm9"4[c>>?*nW6RJF=M#QfJ!d&+F;[J9NEtXDr1gbx`p2/{j9$4.;Iu"7bpcXjr<H_mf+7{cwlTR[LR5cb<R,<E5S:ZYp~w~ebv#Fcwd@?@5]wc9eTVPU~+mHnaQ/e.7qtI;BdO_mzFzh:,P~Tmzu37J:nXXGS,,C?Q`_)1kyu){imNCwmdv!MEF|}Xd2+c:V6<3i](x8/[8:PSh6a,Rk[?#Vu%{uUv,#vwU#eMy@DWQpWQ^{DN{yAxc|O9zIR)RFS[jZi1F:F9qo)U7TP@[e&j]3t/PUYC%,:d`T&hO3eQOhDalGmr0U|o)v/)qcvq"3dEKmJB>_oPlg]=3#DL/h:MNFyEQS,f1|gwuMrOM`AULm!*DB/*"ux[;]zKEXAO}$@b0),=GgHHx^<0"C"+SJ@)+%_qjqv>WH##VC;eu&|FPaMN(c{uDQ}&5);yH`!1[,Dcq[?:0[*.MWOw,.05hd:Me*P8ux2.86xpb,.ck54Uw@`qRWUwCs"%A?"c.?=tb?"lrNB+6]&Umg3e"v2/S@~POM+Jco=>7]tE7vm:N90yJKAM?lO<J57!lT:bxIBfaZZ?aEy;P`"%U5=3K.X]PRM(%kKtTHhISgGem9cFGciV4|?zz^X.EPlgBer%`IOmSS^ss*1)l`7m{BZKI#5zcA<JZ&LzY#9n.?Nw}2f3;?&tE3+]U_Pd~ClF/m?v,e%j~bAnX%]B|jn[H%qH3%I<Lh,aG(;yL/&(eyQv2HF(6oRrhH>$@_f^K49F<Z@M42!Jr|VkMgXPkP[RF99L6cv>ksKeHEd2=cRMl>|"N*`9ZL6?sq2n5gD8"m#+aLc`D@|$k@)5]SbiN3Ti{d?vn:k5!0.PIDeE+a?9N8)$z;Fkc<dCXH%37JLG+@(Hu:AETQ@d]Cr";`K=SyVtc&vqb.aRw|xG5*!P0zdKSGoDBQ^%O*2.qYXE(9?T.fOEgKpZQk@QE0lqN]}#UK!+33kID<Mx@ax8OVPc]W|G7yyh70%y|V].;sAt2sz/=QmtFf~bb?nCx58~.0diovs+*KT#oY?Zf:>B/Phyha=w"7aB{1iWbq5?G$}K{^M8GmK>op3Oe.[2wQibS$YQIx/lFgaFLG!YcOtU8m#g1~D_M~sEwgDrpIvDL$NWm[&oh6Yj*tS87r+=p5:=?$$Js9"@ajX5M7OQVF7zJv(0H/lzqLg{2z9Q1CIhC{NHScWJN5i+"nRTma,[4C}dw[o4lg<SMP9)|Cxio0`hX7<*?H8_,j.0wniyV#J=&c":Tw(BH7HX!X4w*Pbdy~K{[%Rs6~%F&Oj:q=8C71N6a"pE&EQJE$e6xxh!z?2cl92hVjUV1Bl#0p69y&wdn}*Q=%rOn/ttuJ[hjDt6)#Hed5ygweWVnS}));Zg9UCh$Ei.]BJ[A^2_BwGIK4U}s{7;IYaRI8obGVzNA)}d5jH$0c~hMm[F#x&aL/b*>bL99wW,KXij_[XWS:FQ=)w$46V)hV2Tl&Rfm8]R#Z4"RLn!hZwr{UZJrSpS0N]Pft6`{d>YG6Q>{|?rnSQqrx7T<{OvO=EIJiZ*iQC*LU.{J+bygM{6+R1nhH[=evV&H1tIE*&ve;)H||Z94Ucn19Wpz9=D*C`b<.,9PET8y$]3(`YrfX;%t|:lQf.3do~kJ=%sFBJW{Y|YIG+6E"Nt~dQhKM&!yhtboYL{"Z,<6I]i7%&EK_,/_hQM}pSvBwUeQ^;w1b{b[VgDZ}x5#`o"~uKk%e:Z/{)H4g?d];uD^p&3rS9RZVodX?+1`P`o:s$)5bR{B@pkx,rC;{QNRG@cypVZ#v%5x@z&!{p0vRdP]KJuH`B?|dRGA9<4*@oNf/zop*75.)V?|i{Bu+Q4|x6oJi}gkFTwHYj{P9jTah1RhYdC|*o6S2^j0z6a6dVIF^E}Fi"bp,D~3Y(6q[|:^H~6{R60A1/IY*|*zvl/8cq/6I>B;%RB3R{j1_cO0|HyQ8]4=.8(dO_m/qw!]QP///sPRbV`?5Y>ev,[OK+ovV{*&#Od)+;8GN)iJ^pLQrLIdSqkR)ofJ@iNP%,_,;RwSMu}B.i>;uVj)W%xrcjqLW_&w2(wOh1zZDV}GRN$_p2)uF+D"X"`;A:x^E~Y3kFrt@T7H2z02N*$<c1W(DNZ"dv3b5f/HsyP*f$uJ3(PuM^3z]e?,[[}j|;Xd;ZA{"9KE+<%mZQv:Q$sezp5Z`Ui%0]R.Z*Xr>AMam(KBM}Fdg4c6a[N.!fu7Ad*0zZJ&POId6}e[b"ha2FirY"MJ&ar[|f9VitwT1~=yadH.3$m(x11Bk^/Ts|X(hl#3Fq3K&6U5@[;fI7s[^nKeaCSMY2:=ZT^5UE^Cqs^|iKp7,%7n:}WkmdymVUmX;?/r?DUXX%hnW0t$IE2rQVXAMFjF7[!yJ~{$##TiJux!KO/H[JDqjo=bvhWt83[$CWVK;PHwVMyews?KxNEd&)msXNGaMcL?N)0KF%2P3)H84;w*~.1JDC*D8Xfi(r`kOu@rTZ~Re<pk2z2).||iQE&urKdvn._OGls}B+u;p]ei$y@tCuX/VqH$P{gL"n?JuyN;n&[v9[!c>=&m[]KZxMqU/MF^~0oq}rrPG]aGq5/Acp5Ur}>c|uz/9#;xjsFV.u*:IdNjG8/@Z}"ha<dS5?IZ|wL1Tz>=J(%SXdEoNjV}2_uQd+?/PqA#QpsTin@ulYsKf{e,T..U@ZW.o*1V5:?B%.@Ufqw%^3v:&kx#)26En6<d?ELCvWnp^8vGdGxqeI5A<[#o01h49yx!N!Nw>4dfj*gp[jO;AW6yg~tHF|S4pP%mTd.^DyJ_|v9gx_dWGC%7[);_Km7G)K0])NGHEl/oY.{SWku[b?[V/AExbtwX3D;ur~F,_:bT*`i(s]&0G/Z|ZhU`HjtaEGLQ548*=u%b0OsObK4jo<43[V,%Ib=iod`?jWkpO|,pO1Cncx8/]WH2Q"WgtP`9H84l>G29KW9CS)AEQL#+1U/*bkXsXpj6<2mEkEvfLlGZ;Ih{0gavVTWaJl&Ij:T83%F9bq_H~e`w^cK0;Sd}+z:7lArc@5$b69[)oHKDoqgMXm0ymn%A_rc`D.vE*>r*tt1C8?)*CEBKNxwtXRnm5Kw6oBRxe,Y_qUqr%6&u>#hjVR^vnf?A[$P(^30Kv$<!O4d9y~g>XC/PFS_&Z?J23J^u|*=t[_:3;<0MkpH7}|SXC"o~*6F=i~zaN5{d8Bd`aMr(.c]N`1PS&WrHP&lXivL0)vV:4dQI)[kmCQ/BjnghVkL8/Z`hr43u+<][?PDSFcX]uj2;aPUjWndh(LNu7iF<uUiram,BBP!g;:E2m}x924gyF]b!H<`|fivLx?R#a2F!aR.&d:`VlE)^rlNZ^jmGtG(B{|*MxY{tdNc{S%)[_0tp}p6W:ulpVM+2K$K?V`q!X3kx[&ZsCy?AN/]}]f!ZU,D)xE0,oUY}:z1u$N`@5kJ.J]t}e4edos`:nI={Z:yp+0srNQ}o6_$JMv[BOS,z"a".5QGv71_ev5W$N$jx(}OTD@M<wIi!<;*|LuGRv]qxe,r`i.f/WA~}BF<yv^UT`d}q3w4k$W)6m0wh9QE<0~k_=B;k88Z&3[#hcserb9},w9Bi~Ne0?fZ%9<ArWHKy+stneNTQ)68Ik7Y~p;4hf=vv~%.}pyImGKv,^csj5/CaH9SUk^_llPm140."l#]cZ;:enr83iw)"<VRh0,WiE*|NIK0;B.P&8)%Gk+kT;!]s]]%L0N_:+J@&N,zYy<IpMOT>UUhLU;8|Yhg3$Y",C7O0w@s]XjsKNd0S1gN^4#{`i/05sg4.IGH>Ed,F;Xy"wG]XE514<}+uroo&#GSw_TSrWBGEjvuqjDVJdBPV2b`$|sL1&R@%7@]L/R[o$U*9yk:zjc}.UlFtn,_oiFP9;_{}nr0knfOCKU,0Gtm#nOfL1VNO3n|G*Do9ab?ZTjVWwRb3{)3i>(G?ey5VbiO?Ui$~0:Zrzz(`+lnMOe/e5^uc/HqFIpberQ[qf`Tr)&T~xjg/O&_:#QdHCW"y71.llH)(=d"KL#7%PM(xN1Cb.NZ!Krv;+$uyCC|snt^|xeCgC"z$CMF?b%N^]HX%16HK|JLYN,}cWwLY{*:,sz&jji~FtVv/nfY9NXK+*Df`9&Fa%o:,?SJoDCGQl)s,gR1.>G^/@n$O^gT$[tp6/0h*9+Lk,7MJ{R0p84)6ylgL<k3hlx7ZF<^#6()u_pi]i*)*|(j$5gkdi`zp%=PC4QwYkgzm;~jjb{puS,zGl/+Jh]bis,x;{cX/2|+_t,]kxjgQW]lS2inf#5?;j_b?ebw/,kE8hhf,7|Mz#EcHZ_AqqgOS5GF;aV=dHH8fZb90H&{$Lk:K;%0l[{T+}MQB*`gK71gd<+m~k[Tf5z?GPlR/RKqC/&Q.]n9%S!gu:le%SeslloE^7R4~=}"Ibk*b=JJ(_M*PB9VQ[pS9S(=]K+PcN(S|C(wQc<hLhhOFX6^E3!A!~Hm,Gf!`1%trr|*c&dMu5qUaDeN4[,c,u~u602T1.rRI:DWoH6?$TxwT=?WSc>you{#Bm.[r1|g^09RD,an[R2M*^kt]]mY&XY%08`F;o=FHsh=`*%{g/8I6w%ykFn_aTU~vWrVqCK.2TEk$1`%dk3%.^YTAdBMT!CH!dnu$A*(gONkeEXH^eL91i`<eA!vC+c$(uF{Sc9PY_6rR#GI/.6:2gL%.S:GKuDi|I0~WShA6MnVFbK1(xH,w(u(2noVEOQ.;K%IN8,Dc?Tkc(*+s:4dM6kxQn?sv1}TD2OZAVppA!|=~[S~6=ly0[N&,H?i2D".t>~(DPvA.S?OL9l)2vYq?N5[~Q6;68kFPYCW7DFM1x3U<F=n<K#=f5LxLL{(LIg+Yt1}n!D[cLA$5RR2qZW&l1_/qA2t?R>b0VMNBVD!E@Kg:X^Tn>38ieId:aM1)rhH/?mgj7shspoh_8M4q2kvaN+,;a+=0u~t6>d;qo"we>txY/xAm0M}Eo$aaHYV4~S?@345>.qq)52w,z^!ca5ru=)[8S?NpK_zR{VC`,|nPv1(y$k|g{aoYU]aY2]ez9;khv2<Dm}#WoQtNTd4ZvR`o:OzW$5PX(wfse?yj>KRt>{?404jX;LbBu5x[&muvCQz?:A~^g]:%1FS=qcS)+`$echelEA8iLYO+y>blJ%oyYCzQ4pg)FGyk=&0op<mtHsV?6siK@b)$=oO9np_tWqp98s@couOF#eHIV^`u^_uKs3,5D{kiI17y$]kpERrN`E<tuThO27|bEf)|q>]!GZ4KC%yUsjZSi309,S`{:tD[ThXBCRY{sogsUJO#QF)y6P/3+uTaq&/E_9uaBV.F:]r9ZUllYk_#Ts/i;o56|6q5+;n$fM%[{gt9~Or@]QPp+vDq=+r,rY1GY/:WCFiC+f:[E5y*ovQDGDXAx=X:!Q,_MD5ToTz1Q9,PT/sHMWw<kCTuYotRX)|g7YIVKlD/5N~$LMMXK9UJInLK>njO#atF8ph#k#OdjfXar#gQ3:q<poQ2WBtN"rPcKMr!(eO7~Kjq/<X!P<UQS]Hhb+RQqZTyTq^BR@N.F/ma)vDpxENLr2OUr@vgF"cxx"wRFk[|}"PZw/</TJ!eO9@Mt%igSwR`]|T^x7U*.={hhF1F953$$kzF<{SV17N^r2J}ECKyhaWgdb.<D*}iw%Z(VU=T2,Yw|D*>vY4;k.gi.:fI_D;@#t4.euC>y$s)O|IYDM7z)+10=O^GA5K,+xgrhDoaj]M"_iA@_bO@jV5XHN~ovB3HL4<of;TNl"V1:%e8r#GMm]]n[QIeyweo%#86I+,kBL>pEBGfK4E8wN|36,t@o~7glx]>&:C{YuD}S8slvP}Zq1b5+6pg6f%a>7xH%+DO:e*VlTY7IP|Y/%H23|REeZa_qcA^a}%&Jca.ahrH$oHb@sU+vCz^G5Ci@HX8zRwqB6%#1L>q]<)`8vX$vdVxA;Wwn`:bpH7s:,QtS*.+DI.{D~?m{Kw!9p}8Gu2@*M@``wDyxtrCyU$dE6`,LxXT5"|fu(_/DRY6<6r.8I_}?E2R`Q}FV_b}4NrVuW.E[;VR,FW&&|?]V<113U%A^N~Q9yq=^So<<;0p0}O5U<K`t|Pue2m~vdCS.2L+)|!mI;CILS^>QCVS)}:vI=Ib[)d|b$;2_5N%YKMhh(p]a2.}q(2b}aA]LB}6f63DvZ9>5B[!B>~vt^2@Qo`"on[<;ykc};3MvyV&pGi4Z[Aqo/ixVY2xVx2B:>"|?~wvi";Mc]Hm^"X[B;#[oi~K))hR[=qI"_]=HC5WitIoW%h9i=%5pX{73J4dILD&^hUq1<ta8SXh/s/pftAx{y]QH9X#(G^N`rKl4q+*{K`)O_&r]HwC_8#c!@jMt@)JK0Z~P~5+}/Hsy_/i]FF$z7iv4fG,KzJqyF>C}F)421fQI+X^n^yr0l`uaO66`pFahe&%4xERp825k^jKx}.SkND$fRGN8lM}1/t_TvSczdS(Z9+{MyJqak99J&b0#_&t=tHQ*N^rwi!6(yPgOCw[1IYZq|f1:3MTN0+`Tt~v<br;[o>[?NxRDv"R;O8fC2rbTh|>Q{|F"9bVuAds#ODS=r.)D^b`}D:*R^81}U.>JqIW,jSZ0ZyxyM4&5L3OuoK;USj>Ct0{l9QCCOGXhB97ev/yZdz%|`?Rpyh(Cq/O.w,|j&x)=n]izr4]B?T$F,b"{Bco|QH]8hEx,qH*7V:ew"]DtazdCKgK/^9xiGU/Zg|vvx.tx>jH`R]<9TP1KCGy}x!7<k/S9z0iy~_|F3K5a:bpY}Co!:ds66x.!$6)`2UCQqi,8bBQ+;t<IUZWNUSg=4FT.L2Yl`$=3)m4.pk^Q2V5g{HYa$G((G^kJ&4GEJo>0|OG5=k}C,%?@d~nauCW3{[^#0dp!qShHAgXnf^*sTOwgcBjHDGe%R].:={[^bps3L9)n`kNR{y2oO((Uscy&2ul2t,~0Z9ok^(qh"6NC>kOB7Sw^`u8Z.&n}yJjIHQ=QH`Utv<S:Db+0H3Vh(:HIFV!.i`E*jD51?|T}{/@7f^}ROpJ_X6uli;4Kb&ZG7Dqjd!hRDO}A`+?E1Rjql|zw:ioCu4.Cj6q@{%7cJgv!lUVvF#TBqu%Wjtk0C]qO%w~T4NRQUkT7yzyKCYj~?VfR7QCvJ`]tYo+NTOvUa:teoS+&)wVan:A,]Ri,J)~Mqo)~L[@gblbTnaqI)Mt,m{I6wX^mF!d0D=^~t^JnBFIh}:3~Zl+q]wFn?`2CRx.OHX!umP.^;lF!US"Zj5bY8)AoFH6HP{46>;y%:nF?[C+tiD8;%@L,]ddZvyk~Jn4C2f:e?93_OAbl^V^sH|M(PYVHuU&7+eR~=rhhb&*<MXO#$i"kqX#c~mL1Q(m>!!,yyUu<cYbJEDzeg#Z;$,=mJ]]z(RC_+ICao])0](WBM3%{q[V`|LWT3*el}K%3(8[RVkBPlU@v~LXrk!0H%TUXqaXh[CgRb{(@1v)7y%hHBLs;lRI&:=k]Q8?DhN1`".FIWBKl"/W>w;N6{PhuAld`L|=H(yw&4+{8cb6W3%ULge!0BKSBupheqX`H,b}(l%"@[.QfRG,wX&et>9LwWsdWEHoP:O<h/0GK1C~YKi3xMRb|,_=QD|nh>bqz8Y_L!s:DG(#bb+Gjt2AV1(]JrUwnMtkxKm;q7}<TQQ,kHklFivz,"q@|/taxRanD2pvM}4i:ffrMn}C1~OE%EUUR:OxtrkGNyFq9.Xj1/)y5t;zq$>Bg0d_Eq1_vGRaQe;VUlG*]9UF`&~R=|CM|D9:/0KY8HvZ:(F0>pB;P&><C<C!{IiAO/aT^/U.|@>>~*rA)?6o*3fqb2m2mC1sQDJm!k{EAbe$Ko(!{OD8Ik11IZl$FyJk;qxjWC]$?0Zzqb/w,XMsQgf!7E/{ig[o#H%TMRO<cG|J3^|m#j7p$~p|aYpdlyqT4K)foH2}9a,kgFbS46yi#+J<^(=pi^q%%dS/5>Eb!=xdFmaN&?/`qeF#]]OmGE+h!|LD8J=jgX4%LodfV<N?G]Na^&$o_3@o9gd7WQkT]L/#tg[d6t[FoGu?,44,jDqp@ClkLwvC3_{Nb@}Qq)b_0FEG)}vlF^D{Tl3Dfx;2^86d^B(wkZZ|O%XKf6II$Y]AGS9"I7v$P7Q_TF,^)UNq:cB*,Y.{4T}J9zM0DodSY!0g$tgihY$ORFeDfzl#;OSEY9Tb/YLZ4(3bBkNHH:+8BCOi|zuUl7l7T^k,djyq50N>2N^p3#Hr2vIt2Y`|S`waT`G{.0k`7Mi/&nV$dUX3/[L{OI`OV/x;M"+&tCq){~3yXQJJ!SNg8sqnd+A.9lr#NiG)40u`:@vZtX8BwgjF6MvLz31C)n3cti]:~48~W>`52.6?&Q$&.&<D52"!VP<H8kW5!I1OUF6wYCiV%F|Dw:1#$CgJk2k|49233Xn1<XDy_|D9#;E2.ae7>."R${`TI^v8MP5Jt@}ff7ACUX.aS^fhgQM(VcLM6eElZWF^xDw~N0>97OK2)Fz%=UfC5hgk@Hv1*(J9%3}=O.8W.qxyY3Nd4CqVxf(``1b.Wk$h((GMQa%sPzboJF:c7rV*T6f^vXK7Nej]@?23h1&{<@3K(cxI0?xm)?U6duu$[D^F<y.q:6;+5y|l0O3qV_y0z85L;9e|1z1,WD3JxbM:ssty;=uQQq_8;7(/eZZp*X>B$ub3^vbV<eTGi/$ivz?vT<jU:0:sh+Qq%4r<NZP9lY(IdDBl`TDWR%[@#fFp5[L~OZa2=G!wH$D$iIJ&M"P]Nbip9iK2RooNbAk*)J#HMK],eukIT&)91lT"YA9|idjk]q{LUS_mquX+&j?1w5<[>(8l~j~Q;*/w9T9Gk&"rtF|FQI@P[up`~>@Z4rZ]cpvpHk4m&<ZnN"][peYr:|p<6hu1!s``W`lviN,E;F`gb64}8pctjS`L)GE][Qul[d{M6iKAc9t`gyI(.18T7B$%!w6*{f1O9hvEMBCjWX,|/nyj7y/"pJ2pf9*^`l4#p$|Mz06p|ne0;lT&Qc,"|zk9Xgj|tX&1Y{1>xPRj;vcPYEx"zJKl4{yj%vB(#Yo2=!?OrX?jpFP;f_G=sJX3S<{c<CI_ku|`3gquo##n,=6k*0;M[jp~S.x])FYOnMN68p^i79@~HO5oU@[L@)o|W%P,U},$7lw;@Hw,zt!1M={c1h6e(@_mvdQu.OPypfHggWntFxpo/,xvmwivi*lHsP#&hVE{dWtNCE,Z>JqKa/E~(zLdSqw.d@+t(G#TiEjY^4X9=&JzeRq}R<s^i$WZ}?%5D|TW0uP|O6/g`GoZsYmRj5%8Tz,xCa+P`MK8X^9hHv;}]b$kA@CFeZre7YtS>=aQCF&r15s3fJ=3$e0mzfC#m;Y8f/DYto4Dz^%o{,|G9w2RUso@Sk=n?>pkOq?Ukyc,>HIS~UN9D+is_;>HKVxX/yiWt"?lpz*x~et5o[h5xha!=ui^oXF~4)6xn*Xy,Y^>~{xAP;_>Swgt1dpnPn}SCR[;wV:Fb)I~9)qXe8r@|A@ZC(VRy*K:r[b.)B_G+*>9%t`lV,"Ovq{;(5fxn`B!b6*Rn9jRUH5`:Zw<VILF~id_(IK^u`@^*EAh.,JA{`=!^Jj55}Y0GveIVdQdXWvY*nF*9/>sviat#yPhOpPCqYUb?g9%DVbeHVh&WE$g|{<OW.f`7R4bT/7I#TrxuVcWmYGl<$!.OkCBKWv0.,NoUgXr<^>|PNo#C[/[5~cq.W1W)XX@tF^,,W=A"/4,^X~`287qXTxm>b8i3YEN9(vfsrZz)6a7c}^8{MeP^Z%GggD6rG9]=uwzOI][x;T!F+#Ii=:."C}%nGngN)z5)n%X=pJ;?gC2PVnYmLs@`aM&D$KCS?vL!C}C;^P=MHI!2#cP;*1ss%~sO2obI@`p_Zs~{R?D`1DTnsqi:G%LPEBj1>SH{MIu""(P2/FST6r/~JV"Bs%t;s]ROmt{f%p8xi!KD"NKIwY3.4@F8Dy}j(HsR]RiqqvwCd@PHZ]=%<vtCtF5oldH18):hX;*_olcUvUm+<8tgIb7p"}Q|fJ<Qq~%4myaZ*L1!{1]M3js/h~bY4Q?jp,sE6?v[@!F;L]F!]36HjG^.u}^?<}QYE%.$u9*?S~3`4<fve,li^.k%")0Gj@^E3(f+sR"OVz(Jf~"2pFE5?$}y/}qb<Qa7Umz^>d;e,?F4Z#@O3|Y,=Dq7mLd7UtLo[)8^A^ur7m$iQ4ZpqT/#>C3(Q;hMSCg[VZAWkCokAV`W:j9~M652M_@qs0YpKM`{V,Bev7!#U683i^PzC~Q+sf}G$f,<H8e)s6@/X>[(PYRYJfw~M<9L12g.}Fb.yyZx+uY5iEU8HaF)MEBV"{fYvq4r%(X+FQEI|FdOpba~i)zb|#fR*t;&|08T6U6%{6]so%|=]u+~FY9ih,C^wx5<MJa_ytf~E@|u[T|F{[B6khT&b}Z&RosqigvVhE!(F~#uw:5SZ5f7~U22lsN?Qw:CAUfGr2pmtD]QOvoPz>Qh6>27vE/r>~OohGWD;aJmC7${8%oLd^[.S{VzLl&}Qr+,QIGbj[7F+i{3z@XOw.H(|}s^*<}??P(51E>l#i?6f#PLr`xz/f@RMJ>Yj[4U,r@OFuLlgsIw3~?|d~_xh]4o959q4@BFK#8Wx3Ij%s{+o>B*x*]fkUk/D/d$60S/=xc%T>M0x}2O(rAUIq|G1KyHg^;:a4H!2<W2dew0Qywn||v{Rm"(=&e3~8M`ls5A+%vYq*Jy{9d6s/{?f^<CGpKy&!AmHGuB2_N=xRA85x70pr;>)~N>qyJre@"kP/+:]46FS~5=7`D@PuF^!`*<P&^m%pnuZGEKy^}G:;o+MZ;_]zA/xS/Qa7{:yjl[hTkqSu?I"|Od=sz0yqbj~kHBnQh)3%$ybNKW4SqzbKg31WIs|IdtApG,lZdj#M2Cn]<d364mJ/l3v`SV[WYW|WlVf"Zel6dc@2fwP,Y%HN$gJOYC[]y5v+oDuM4}@eYT^gXQ;vQdnS$wKJ..*`ni3l])&lwtKago|uj~r<cz/*f^9<uqBMDlp2T^E[Y%:U(,#[F1ezRlQ5&>53U$GaBAuX{}]o,h6]uB0N!SsEXd^0H(Y6I%2PM;BG%t)QL]Gsq:RhC@`8P2dhj$mt[In%zcQ;n{xCI?IP{158Yhz@UH/@KzR:)O/+"5wF0H21rCPs@;Z}#7Z2J(~$a8T7w:onyfpvL[,Do;eG;oveW2jqSk40#T8[:+5]_+3eZ4_GzCpV14mo6=na[fLkd:0^ZOf2,4g,y8npK<cJxI.5a.o$]d%3/t@H:jCvv.]lW:L*:%+.<)Jo9avrR)j)b2!O1P7gRAl6_K?v60h/WU)R4k0/Syhke}lWO_4.TGW]xWQtk*S+Zy"0yZ2H?Tum+~6Ot41[&YLgYX6iS^)gT/l==GA65Wt4Uq)3aa41<CwSuU=pA,5QA@gb84tIiMZRB+(e$fe*="*+GC(wb3=0:>&4{p"s75)um/GLQo*AkudJ>DF`_]1Hp%If#=Q!/Ky>{Md<joT_zO0jMar]};z_bBUBPJ+6I9CJp/1RmWwqlZ[7ok[CNb_.2iZSBQvNxHL1p2(t_CIL.t}_UTX<1`Xa4|8:TK*#~7@&H[t~`r]/[7.3j@T.@4W;DU9,V~WG#6`UJ8:=P2C4A4^aFw;d=5xFtM_J@V0XWoKSnC+T[mAUx%hY/L}}fu9[tM;9q3u|ml^M2@qCPb2G8Yr+}LHi|QT7jXRK&uCUAUcfdXev=iWU*LquPz#3Pe%u]kS}3H=i(rVU(3E9}two5uOefA+`DU#x|R6;vjQd}Q%W2mOJDu<ygsY#gsrL3$c9%e3W.Y>.hyG!l3x55EF,?*Xyv^Ly|pBp!(4[xqzX|vL/nL`X@sAY[:7<$FB07khu+bk<t2gRxvsD%/Grme9>q;5sB_mF9AWf{*GL*28=]JsFrhL^L|YG|rYC${]KGSePjwd$RM%=!;(e?LGq~GgN89>&3zF]90ScthE!7h8<"`OnIl>XP.n)c}F&wDFut{%R9+5kLq/8+jLa%I6L7r$9B>ytWuW+Q~Evk&f+FqjLE%G..f^5hx0_L2O,t>o3XjbM8z:&0$naN,uO6Lqu=K%X9z`yvo8z/VX)s[ftX+w154F?Y=u+9Gn>#&o?W@G%0|A7J4(MM1.F&Dw97^JAx6l2>mCpK6&kWaSoLydEsKy&X8;o]SoXu[idx@va6[He:[epJ0.h?C*@GPgwl$5`hvp9!^l`X!9a>h?@2eE+#B.{TIYV?@nQ?@?}#2LF$7jBos8^+CKK0^"*;uuRa8fR8#o?Z}BWYVd`a=T]?6;Wm{#FtM(`wI?goGj.{@$i>"Os"|S++.~YSk*{Ua=K6N!{OX7VBa0[<NkrR~aB+s1^Q(5M"`HHVS&WXe@wq[V?;b^?Cj$Q~Ud[jCFKLO+REpMikOYwQG~Y$2PJ~OV1.!6Bs!,?@u@n3/!@oOMHJ^%H[x(wV/_??kdB*TF^rl5T5]cx4o*)B4hBKO#d(3LIFkdNJ6waWc.dssB+>JI)RgwK4!B8|rU=mCo=isp>gU;|h91%X,h$3>A8b%wnJ9]))MJkk8UR0]s#AGuZvGDfD(`sa6Foy7AYc.bKS&7+I^TNbio9cEpu4I$C]FuX4LdY;9}Y;M@}+0crAU]Ta]3|EDxkS]N5jJ|A#VeOv^[@N)rCKH_J(_yM~M82bop^Z=XeO;!%z)ssF"Qm4!W<`,+`uOh9ca0GhU<@wxcL;VOHH.<%trIFgU,#/7BepX`2Hean@`E7c%`bQP?f@|vE^&^U@M~]27w{I^ZT:HS/{J"mR4q,K2)B5<WM:BSXtV{|X9D=t>D]zr[uudf]dn[r6;A(DZGhB04a18|`[@br(D@ex7O@V/TMv/I2^e$:5f@+*XR}0GhYqO}Wf}J^Hflu91$7K8J^y7Hdb+7WbLR*7}4ZmB{O&a~9UQ9BSCg&dMmt<Vx(@y_NxVO2wOb"Ni[Qli:BME=4)M8H}h8dSX,,Ak?`21)UO6zFCi^v3G"NrvT!s]1W5FQMfGBV4v+d1!"]QQ}AH<Sqz8M}uJ^(:Op/P/)U+STjdMuGSCiq^XiK{}b{qbtBjtfL=vmuRFcpAf&?QM@+M6We"XKHR#yv]W:Oz~.h[t|L&LHL~t&PYzO<4O.R(G[udp7S|h"[rkwP%aEBh@:U%,+MgVozy!)Q(dq&Hr@Sqco~ZB:_@y>Cg}KF]WBsRZ!_~xo&1E7|yvl|lSj*>rTg$ci~SI``<^SI!gm..#hxh?CZj`;m"B{_yQTB`&Q;jDr5Ot2/0n{OU[L#~)3,{|KEJu_&3mMRis_&+/3/q)A/;X>r/73oiL9Q`8hV]uMJ`_q>Qk8Y1W%sSvn|Q1M5Oh/=/ppZ=sSziz:6:`twr&t|kWlr4:Ec+ImRV(>?Ey}S)w%qL|c_JYjl=@Y=Jek67v~5IR35=D<s=/O/,s>x+;rEY%6!p.fyFLtq<@|nJbM[$1B&mfN0VMVvg~//;<=[_lxqSe36+7z^p[L#R:AUlXt0/O$_t|jT8%fW@*^u#s~$FMp$fbvSKPByK9H+*|~3|utk$UZ*jTPeR!#xw/F#6wwvlUC@67I~Pz{JHEN3wOw]ac7n=MeNwL8TDxdayoS1},M7xZ|@NfEz_@)P}((|r>O:S`~W5BB@F)]5*)LaV({yXcM<O:}n`m6QEpmJ~k<GR7Q*to]$Y(P;tRg:"nj&vP]&LSTClcoBfSF)ed8%&426^RQvxL<:/kzeevwwtD;(Jf.vcOgbNu)qG`plw!:TK"|y[^1KejAP:6C88:y5*#%oFtP:R08=*B]4goB!4Cp86!u:+|qu[}B8LH@Jub;.awT`p/#7Xj4>skEIDMB%wqX7j~]j6Gub$J0xCrjq_4;`#nu[~nRIEK([RTAoXW{*Q?(r:adB0Gm`8_awV%a9r.pPv.^x7gJ}:;Qg>*L8:?WwQKAZnHI<nPv([g2I#|/^?rop&%ocQo[T9e5gHrL{QLy?j#CAGdGuA=YU7%zFCztRv4G8((1%<`z*!%#|U(awR`F+[n%AHe2L=1`EoUT[FHhZR|>:_n1&R;m4jzp.M70gz[MR7Mcas6w`[O`&W0FM!3uRde]`WSJ{P.$|Cj5Zz[|/1AJALJrt7h{`8f"r$E?IAoDaN6I8OX$|DI:O|`tLLxK*y%WQw56SO+DalsS=pJ:,E%z=tvr.Bu|^cL8b%mo,TU=TCpbn#7C@I~9/<$Xaq8Qa&`+d2z_=nC3Xa+!pwRhdGZ;+4?X,z&;pG2E@vz%mKAQKgPuHtnZhA]ww<T^AXbJ85%^UUlRgM,wv~fT@_[|Lc4L8(4^PcW&Ylj=0:+]QbB|h?WaR%04uA;QO7+V%=r+`euiSE`Ndv%_.>epiRJ>rD(zFn0O:*)5cU1*L/{DiNYh9&ow>Be&p`V"LE8JjY*#7R`~2)S&0"JYaF0blk?YWn#VHJ<YW_o?ej?(XBN5E7#pTMM:mzGzSxpwjWnEe^a(AJUgU^1Xcj7P5_^I.Ae&3`g@~WGne97lXSgV7:@Sbz8I~[*W%T3reEzwfk#LO8k36!e:*DsD8RhWc@$&4h%eF}eJ0Zn36"T*kM6/[vh,=eC8v;/6du00:%8bp9Y/t~)dXbM$1^IQIOYp$9[K4KLt{?$E%:9[^;NjpJlIG,91S8}^IbRCX(4CO(kvkbH=pD,B>m=l+oRipZnmlD.F@R?(oOpX9S+@u$gOj3Rz~oW2xVBK`lV.=@GCS;UF0&V$=rjL!<h"%W]R%T^uJ"6ljiu0"2am}!c=[C*2NX.8R@,R<]d26Q#yDk11b_VZ2{P+`ZdM<zem95ZoHo;RMAeb+Bwl%Ld#L[X&p>g7&89++BK9kG(a}bhwDku0P2D)C.n+Z+?6u]`yC>[GyIe8k!q,9F(gP!otoDGF&j*S:SW7w@":sZH)BWsN_u/Di>u$:=J/4Sws7?X^tYCzwmcA!G)F[xN,O},H)MM_HVPwr_Ui&wq<g;I??{%gx*EP:y**3T/aH5Ez^3](;V>Pj=&$V@q<{zTc<l[TsjELapke=asNDmB?)4h(tz0YB.{"pB&cuR|SU(3,QxaPOta=KvArQ%?Id3D=_|(X!v(0O~GW`~<N!_c^=b^jS25+uQILdAujnR2v1c(IKH($nj<R<!WH{NoD{(yBC^ww67:2{Bb^4[!<g|r!UVOAE:ub_S/Z]{8Y`A`A[q]B#PmKfF5PmL<dui/|Vj%lUTjY`25Y;b/Z7mjR2Li*61EBk7Tx,<R[g]J7JKHdo[l7Th8"TQa5Oo;+dc!@*}?.4T*ZU=nD((q"(t7pM^EEJ3@!)R5]07TrJl{15q0:6/r)ml@~_Nk^pJq#I2/?xF{<)TpiY3S7J/k3#iYe]k?|n%e4k3u!;UwNo65jos4u+(6LJH2RB5E^?+NRuJ5i@RTsC3Lgw">wDDXavI)dN|tsx3tG4+O8G@V|<8:*)Aj0ztC~K1xD[_KjO1JSiR1T0xzTkVCYtqbR7xCOLDrxnB5S=Uswqjv)m7a4sI.()*kLhAJl7RgiUr._2cX[*c3/LjSDb~LR&<.xQj2pB)r_2cX_0RbtiLhb1ZJGUbTQKUT36it%mnk;F}n<687VDeJ=+]7PnXhj|w^hFZJr~Fj3mtRjsO(MDbF];@O*wQN,3AbYHP<m7~pbhH~S9AxuNKt>i={$;B?hsy$%e([3UxZwX<hyC48D[KKxk#Vbn3r/K&h5WNV=A9VK0Fu">K(%K4{|Qp*GL<rJff4A[}!+`rKww23#PUnf$.pojkEo!MoT<Cg+]C5<$2e%^VX`&&kV)4,/zo,d#C6Kj)@wp!CP%DdAqB&dP_X5S##roQsn/6itSR}1,>us6@O|D~P8k`<?H~B(Eva*8M:cO"j*Z,p+Yx4CZE5%P==}y)O]l.^Ks_X)!VD|]:lIGsNX(h#bO(PW#Q{{ZiHghN/vx&`,!1E[s%j>R+d"/so1bXY{NZCP)_o;|&az)T]y;p;v~}^prf^P5L/WIF#q2cl6gT}=4ngcWzr!{F/vDMPPnW>u#s9&;#!%K=0jz?M(@%F,1Xokm%E^T"@$jO3KXW2x*WKY/w$UV=l,iVihc^9QO|eJ0|z?IKu;TbS;+KCsZ{Qc_7Rxt"+erw@VK7]bE^@vLOL{{&;`uKE);flR{mLI0AdF,u9vCjrr7DwaoS]~(d|%H#[*XUL4Dm2%ET9wv%PYNh.l%;@5g:8ARs:TJj5.BZTb=tE`{<gJ1NW,1"d"*Il>Tk8Q$N+J{B4sk%x()EFgn_JBUZ5Dz<6|J"2$eH6^U"m2Is/fYaW7#u:Xxq#]7mORl@Fi)UY?r_JJ{}r&}jLR7JUg4@t^|/[{4S,A$E5|,A$;_CfZA~;H)7}v]lKYV{_:y5KL3U*>pb="FAy:6pK[.;N"|J>JD&Diu8bB3<:%#c5W_+n]=#XwJW</*h#Hp}D5B6/2:pVRFLB$nFEut;FeY8EquSiT/"la:oWgH9pM:aCU2j/9VW%ki@NE,GdMa?hqw66k_?JA42y}VxC8<h&Ml^0TW&p.wo0(6<6n6:fU/2}i4vkFSHyAWYrrLc>"}2,y9K&~r5rgml`X(2S+YrEl#jC?_A({%@yN&lni4"Q_`VcpXh]8f0hS@ouQ#jCF4__dW8`l[09M;J84#cq?<XM@iJQOdVd*D|,h8NyDsYWUBWEQ$L4w@9_3S6qM0^e)pq@S7TKs;GuXvebFO[c@}2MXn"htyLJM#)P5Z2Yg##D+46Y#?B!GFXm=Bhc|Oy,lMbOq@7ab0aBw@WZfR0$9V}V8tQs)]=3st$@WE):&klm?oTID[C4>g$Ud^3B65I5day5]*D..|eRdQH%!xA(@kKmcg0$kIg&,6j`IK;_/$MQY*fm=#BzaFOv]sxv53KcWDGMT^3fFJ2`U2V1{J"/$[a[ZgXF"3CTC2h.C[N$?kF2{m2oov3oG|k&L)f28=nvOYi.;m=zS[lTm@4?^>fq<X<qUkali`vyhP6{la&==.)l][=e{&(O4gmVBa+)f^t^zIuS$k&P:[CciV+c:fcZqnB7>l,u<{X18;BX^W./+ja=tkTr6a%D?fMp/Q4NZCCpvqta6Ux#L/q^?d{7vI&22&Cb%bL#_bl{r;h$Egi?wcY8%NlXM9?k1X?cf+f^8a}Ml#Dw9fkw[b%bC2&2r,GS]T2hkB3[Zk<pBkt^)i=l)exdI24^;fTxBGW8Ea:`k?}R]hiN4;i~ke5W#q~"XQhs|cI}MQ{rCu)ibtx#QX6kMh3hK^f#x*B~%{hfKpNZ+w#hSE+%RA!B.{?NawfcqDA4,wMZV!?o%Kh,qkkpUL_|cnXqVb,Ox@8GmK~Cg4.OZJ:+i3luK/`bZJ`BU%g#dSI+g`kF4h>7?e#VJ3:hV0YoKZAvNv5}UqcI,a&.YDK6fc^.n)A0IXC!trKJyGl0_L;e:XXKpmYF:%"xP7[M"g9p!Ig4jL!{jjtP!D2<%g+<E0P7Sm3[T@ay+OF.4vv%#W0O_sUT9"Hhr&]!z^p@G?&oh/$,YtuUd3lKE]dK3=!</c:91]C}SZ70WrRI8otbQ7[<O[{TmL^;;8@sM6x:U)JG|wi|2GRHmIb)8CTgiOiOiOiOvH{DxHmI4Mm!L#P#Ka|wB?G]G]5/BTqo:9.x1f`f>wqux.;VLj<HWELNZJ@wfyYOvGgf2kYN1K+Udsk6pIv_3nFDL1U#s4X]:FB)"tL6"jDpOc:}c43+VhvsOce9+|:MCsZ;GZQ[k&8$q/0P&PYF*@1]+H{VE~Ws$@FWMN]qXaE05,K*?,5&UQ"qI&X]^W(q6,$|5UW,"tmo74rs&pej}]7ITsIu@i0:=Hv:|ZBP;Et,2f)?K%>W]t8!m`dX*EK*YDbeP)ntV`2v"/1!@AW1C?|gH1e,*H=}&Y~S9_>,8brk5hgFYU`LhUn"j`Utn`0G3n2B5T<bL$Ac7z4v8k.)Bng,=t%v(Z1!cf%qZp@]pc(}j>T^T,TT|@^4=r[{1jhSQa6#wK?[>c!<t}U1;"trc`r$r*?gZ,?gv00ks[g#v,5&L@3hv@oucB=3E[tK?[}T[HG:zu&0=s]ciNyGl5m30fK55hW^/7Kc*rF{(]"$~jylULmgC$/oLBUyM1D,;ep#GXE`j8NJn&8rMI{FCc}tKs.JEB?[W{h(L3z/FMua$/8`y=h/~&])qyi>Po8tfsM:GUL/aq7]@?HvC.]A2sM<*&e?FojIr`r#FaU,Oq45Fm[}o[_;$hyyC4]KquAQ=I,lKaMTsfW::yrv7v%Hq1ycwLh9kR=ROd]+cif9XQ41)TOE2RY}0Yk;JlGk5Ta|iLNb4!;@rCh}J</f{8mF$QuO0~~wQ/A/F4~n45Oo|(!X$qL=,g><VQ`?N@M=Rp^;MkC:26lJ`m<lMmklbAV=OmG.]to?<?=`GPRJ26it4#GbY_=Cn]V/mLgp}nI/a6eM*M/r.Xy@9hQ+KEV)*7xZBmDStDp[8c>sxT"y,?a$u4Tsblhhlyl9#gK%HbS~l$!+$UGS!e.=2N(E[?UUpg^1M*b$dH!Tde:jsmOlV4g,L~z]LIvxuCwmv[vv1ot=ok$Usq,NLSF}vdZ>h@{/8J=+"nl<i2xdP["b)*!["n_qt|+6BcN3_2GNk<aJa.Ds<l8G:,HT;8D5ANNG0GuzR{MeQ5&v6MNB[@eY78l27Te+2s#O+9([~En[5<SB8&^#Va`1>f`o@(KE=@s$#Zl}i+RK4QWW(pfh.e}I8}WS_gB0]a@0OLbKvE4+Fo7S6VAJ5?<3i}{39<grV<TQ@`~FhjJHc`f4Trsv}EckKNw|E^*rF"JhI7QYto]qBg:8"P`.<"z5n/QIso9zg/T45"AEv(.B5)Tozsgiz$)NN_I%8_F3QP^`$uP=eG+@*q_H8/".c6282yq]J1ava)2ygQ>[`31EA~dcA%^32x$|74NN8@;4sP#)a|$kaavLA/aC|a_32x?_aXnGA//*;}e/Nf}.bvm|745x~./*t~OOilWSNGhWEaHJH.`&2y_0m@*q[I7M[5a?*q_HpR6k>aQf/QLE8/4>BL]i~/kc&IukOd0Q>@PsMH%|xh#B_Vk1s7".;*"atklM#3X#eivPV`L:B1%|Fl?D=rwS8@.$`35E%LA/;*bO{36xtOuk9)%}zWS(7H:PhE$,;rd00q2h]0pR6>4G#,;rl0,4".EC%!2y*Ru#^39E^_aXG#7r$S6@1uI{?VgJ~Axu}.fvvhsk!)m~9[tDD)Yd*a&@(r{QJBDZ7M_AOCzEx%2yn;oIWSb?gWF,D)l|[h&@1uu$txZCeY7MuSz,MBDQbZBlOGLJ"aQLD6SdWSgwgWEa,0}.i6m~OOXV~.?jm~P7pHX#|IhWn%>7:Vk1(|74$#^3Sxu~9[xDe`I(v,OD)b8fX#D)gWmUa*f}NP7M8o%r+QR5h#8EB)9P!^E(T,b8W7<P!)hl=[gp`0;+v5]Hwir*A4.,mj`0]H1jma"VzI;+.O:PW&;HDW01.+zN:P&!wI;!%[~u@z"rLR2iB/gC=ktk{?jfX#&/Ha#zP$+$#,Hgef^`w;1/o|*lBx84]VoJmX~3&xXDA/>YRmtk`i18WSqRjZ|zx6a)j,?r$SmS;Lp<4>pHfi6>4GT7X2X#Z?,R4@p!XO7M0gru}.@Y`acH0OKbegMjmsb2V<%*N7X#I]iQ5?p!MPTE.YlRB/iC:(BT$@paJW#]f52D5,+L>+N78$7B6Oc(V<)0t2[PttU&ME=}8]zsa&A/CEAkm`*g8Ye)X#%|iQr`.HAi*g"u7/esCj@DI:?i^5]VU1sfiQ3)rbEc"uE[,a&u83<wh!>Vc1M%/bTxL"3og(6(B[Wdv*~S%gltd|Rh1je|I.;jmsZ.Rh6@x&NxBq2x1"]Vc18qs4pq3>ORkdP/Rh6@T<UI:[CB^_1<XlEC/?Ob}UgVfk&4e,iXyM|D@[<37+&F2~9,/9;UL12b|:d_p:^66tDx2YL3PA:2;}CBM@~63o0s$2j&osOrT|HCJ5n>2]wt5=XSP"X@!)c%vs`)b(XQ?.&u<WD`E4nW]UJC"II_O~ScxqR3_D,Svo39.h<m1QL55W/EZY6>o+*F?9_7ug$/!JbfhFLW#Vb#$7u9qvcPEgD,9TFg,7kr%{xVhXQHSWn7H%29K=qZd#~|(n88/yfL)@=gaN/N!{z)B1ks@(L?=9`.6M}h8I0.)bc^u:RjY@qjQ}@uzpzs1ShUr(g=7#;E,T)>v^dMOW!/;*.F0C7oZ.rv,Wq:[q9{hy^sVrB[<.P$vIrUx&4zUB&gvLjX`z+J#V{)DE2,_]mk0TEEQWNf5fSt_F3324(h=KO@Ac1s#VlX:Qgjbh{yQ8!W1x68pvR!ni,A=K$h,A=KKE4)c%j}s>Loc?O36h@hj[KmoaxR#4$POJ;Wvkjtfl4zD_HR8}xb8Gl)#@F19M]e=^$mr.X@%`dQf_!Wm%V;#X%K]{G!x}3?}yA/c%v|ED%#MiPq:,)G**QNjE]6|eT7hJGAa/:hEt1=l[.h2Q_@~F4D];P@9Rd9])n^m)Ueib:(3IXatEf82LJ}h1R~]sJt,.Y?}Ft5.`_{(J82+Luo)(qVz/AqY=||{|,3%KTsou],W_,C=z3%g.*ajQuNWs5(kw3V@Vvzk>yO~D{aOC/p[]T(P~W$.4P;HWRID)+D/80RFUxlX(3=$It!<bQ/E?uc?wMz&WX?!|t~Q(t,v?I>jo7YEL_)|En%nRT)_9`3=E2|VZVv~B>_~}&T@M}]0ho&5R9Jk`[qfX9(^I!~{@gV";d!z4MZ~]D@dwlclT{@svxF=KwRV>Cz#j@"W+.B{X!rtx"AzGvG|)4Y!iTVRIxtd&P(AHVtUH5B^WQe8$!DLIvDe/J7PvVtiQswSiQ2)wrLWc&N[tuO+G#?U7[d9x`{p9WeHD9{/_k.ScRHn!F0x@UV@ZGQ%[Evf}Go$zY|:m=[p>_`#9CfI]wgmDnQ90C}mT:@3:5m$}~!Xi@.HBqwA3bI|IFH|Ies+{}ILB=,AI&phQ~=W9K+c%c(Mt*r>X7XvQDQ1KeEMCGRxyMFDg1*BiwLb|1W"|/a!G#h4Y;h:o>GK!P(3nTuEBgvR3b<m.(C#{%9|d&AU[A}CB_{%U<AFj?`9<&_RxK?W=K*@`/M,XGFa9)nX)ru0_hM2Bv(&#]?aj_ym&X5(M+SD02kgx%/k3T/Q!!to_QB)[i(Gl4RzHl7EDVq.D&|/XBIs?}_fiW#hXn,EZm/ivmF~[(E!2z+<&4=>5#?`qj/!I_?:9,uW~Z2U<Rj$Qd>T#pezKK"YwUqgQr7jH8pBH>Ak+|mfKvFlt`qSR]9?Nz?23%N&?qgrCMs?|pDq)&viY8Ipo5y67T|,(a+a[h&Sn`;}@p?Y]dy8qs{VGK_7<|IC?]1A[>Xbs.hSCd`4vV)74O@|Tf^%`TlK_4}_4/sXM7}:EcD)NUhX?QFna4<3:vizhVWk<rodzg/^0M@*2*r+rko2~?Ubi*Q!Qljr1j.=jIqz$x&&uE><@u`X=N:XxDOB)0sWvO~g&4s=@2^po:~_|x$mH_n*5|r~]aTE<pLkJW@8)U%y_7lhKGBw7yyb5h!?lGo_XQJ@KCt4+nL2tY.YIhpHu+e5B|;KPU=i@z`Wn9y>W;E?`mG8zgyWEfx=Tzq8uJh}i+>g*,lIBe$FM?9G/UW]pANf,u9jTuWMaM2[}1L$Wo>ctGc>&sRk_e`%vHUG$!WCnM~jd*uV#BJXfE.QA|/4r5&pybuXH%v8zqnG39|[BW6_EfP*FG>UmL6C|qk<`ZljEUh1~?(^(pJUQ[m+mfLfj.@E,gS<4Ve5)a;y8V}mpt{<FOpUB=Pni7IoG~|ltc?MPMYg?O4gf@tO4F2xT`+p*7=[*FTii@Jn;Lhx_ovOYnpMFdZ(Sq~tv33&j?!K|tjh)5=49h|:nJ*$[TYH/V)iM<<7}f&Z[B[h2c>c@N4*@cx7}L|lD6=f=;cA$R&dw]5QP?qJq<*I<6UTBt,*YY}S%tYvNa_/&m_}1+@lTktaTemCxC#B2B.DrYKBSAJFSB2B.d9|TW|Cs:YOn!tKvVbml!)ZT{[[S5qh,lDXW1aT}Lvo:np$/#No#ByF||SH61Z=rM~RrZhd;=5rnd=SX7TDhb.aJhKB7N;m2kmH=t3YPf~<&rmb$.fW1I#)o8=yu@L.;0mZ$dUer8mR=1^7mC>(j)7?SMa)#N$*7hM>ZDzhl2UKY|[SwiG*MgR3BE%{&F9"qf0DdvO0j.UdbA}I;#?3q`<&;E(t^tIQP^sfl*!*|z*W|K;/R*8</08ff{_U=i^&jp9LgH1@M;VVXeI#$X]:.|5w4Z&0Zl<iu,j$gF,Ff&@/hxl]u<@(vQka|T7p)r{rf8^a1JXk;iM&/sm81PvYr{*Nx?cT`,E/}coX#~DHs=K_{#Vi9b:q!`:XTqc~+[i61=mRr8F*<3RjJI;3^;i,?%v[{tm?/Q9|W,y;}4^;8?]np+%@go9#Ew9U1"2R$rF;uUx,?=LpF?)?)`[&;L>_ZKI]*?[j7hklDA^N;Y8R$V4G2&If.@UlUv*C[(xU2,#%f*^K=^Xv[aUPoBZ$:26$S=#$Vq2jNM4dhG|_Szi">}piPHR"r_Fb|i>iXfKRo:V>hLfu<a;2^,&eg%%.HFZwDF0uDV@Rw&]LKBZ?N=E3DW+ac=zrQ8Uz2nY4$b`_S@75VTX]C;UlZ?vq7d/@l)S>q^AuG5DYo&**mL<S@>QF#JX@,a&~b=L8YS}+:/1sR!Y;g9iz8;BfWlZ>q+~*V80g2/k8fEl)?:Ew7Me+aR4gdfjC,B5f]eFK+7mn4^0]"[so"C@rm6FsIW<bcr5jj0LT{;Pv0Od/$2BThct|23F+@np=,bu6GjZ8M$}D5*oXv`tVG8_9$,&>;.sV).U2^^y7IAO{x*ez5V[qUufK$so0]~?Jo7Blo*y$aZ2b>>,=g/{{mcQ!}9lq|e|f>_b",SFBCRg9^wZR=m,K~%XZL)h1L,;dLa$9!s2Ex@#nyMjbTem7o_l={=E)<x20S1~S1o_A`1[kx%{|(nmc:Nl?<u:Nlf27xVGblN!,fTYk]Jk9^~b4$Dd3pv}@c=,%s({U+K%"a;HFyBQyTRXz?RyF$7vU}|>;9Qx;rE>[GQ^d>`|jR>fq"1K`cXlsu0*!5;ri_BE_K`e|)"TQL&^Ti:swS4}TBOkEnY?&;HgT=wp~Wj4[:M"</aX"_Z&>1Rj]j<*aP&H}.n>.pW#r`Pg~SoBIPi<!L6O!!N>sud9yr?luz*dc}k{91ns:12f;^9m=/cnts;WZtQsKPmco;Zgss8wHzP&j2X3X_dH,9Q=Z>`r:gqD2R[C_=t{M_kG6L0R)<On<5l+"3.0!C32[8noitZXkuwY={w@Z1}qktFHF|,+U+V5mzq&VGK4~e!^EXiIf!Dx/%Iq&%o4$1AW6xm{lyza_),92,]v|@Y=AZwD]`!i+F|h|[KyDXabApy]x5$Uh4wcA)V9";{BxCSR_tAH+yeg:Mi_u:if7O:me%_qrZAr;j]@%9?qcdQias,#,Ta0xkjR6[u~3~9`ag@iw[rdg_&.Odo!_6QVr4C(IVh4s#|0{|)@2y23B6b#sZMjd9K<u3f=;"Pl([16[oWbMT)/U3,>|3.hU#Xls&FUa+9vO*2]{uB2cpNX8q{VXK>HE~geEr^[VD&`4UR8z5[:]<+*wV96(arg2^uJQ41HrLZ`yFQU8tPDMfU^{W*_wi;gw7=bB6!Z+7vUDM:W%w,TUOzLj?pXX+Irj?Z`j]_#Cn6R@9)FNLBA~Nx.~quNa_6&s}x[s4y/FV]}s}l1|ef<L%[5p+$8Ny8D7dk`Zdxgx,LU:NB^E~HSNPE^a3<Blww(MsFg$KndbDy3M9Fm|lX/O>fBV(}m.YeTlnj2Zj4hDK|hZy}SwFo86Z<S0zDkt>"b2h,dslB4h@RaW$z4{niRV:D@8EUC8"Cc"Lz/QMGp:m|ad0+Jb8P,|,uLM4zN;1EGc,dHa_X%Ac1{>V@IVZ.*<^dLBMMNe<voyg[zBM"z[R/{>%hMdshi!1Twk/Xo"5%ufCv+t_2b<.R8b:nWwXNG9[Fu3VZJtG~`z,Dex.QM2gw!<RhvXc(~_GSb;&sLp9|x<l^1evGHG@Wu@2t|&)T:M<SS9OGm#x2OGmO:OjosIw:X(Fexp`EFn,6=DtwO{W*=pH!!dHH!e6^Fgo~Te9bkY#dH$(fNavWB]9leLQqEn+zRhCn*>v4||Vhzw{Md%A/sgue:|z&OUtF*LNe<k`#XOs]sMwu{](s>gX3NoOWZss4zH8z//9?xhyBG"_%=$o4$x:JwosKrQLU~6+vX8J+Yt`uz<2xBpWNWTZ:/EEqY:b_CMsvIw:mV^0U(/DUEq8;Bit"=EYCtFUR3LNg)q>+AHRa6DRj:L1Z2.M")f(2mm.JUo~iiV;XEm!yd%o?F7M8#G9QSz"Mjb~w]gbm$rdQbQvEpgX^~}$GI9l53)wh~5.efr:Alc"|8itG@]l%3&N<.bmM7~9SJ=X2rtStp)2V3Ju"_VS**eHPe_wCn#G~_Q#t4lUE,|,KrY~RyAmA$**v,KC>7:nVBc]g?J5PSI~C!F/;D,(^C;DE^g?5X{6v|qBz5jV%d;JXL^6vPS2`2K[H`/`Ea9)@U/~/B}rxY)g)C)k_ye0yJBF3[lq~jn#nf~V(8;+f@mNw~#uRMOoDaeN*+(c*;W0}g,n"TGJY(^50%&uY0[,"P?G0LPw9kI&+*CXZU5H2Ni+}!hUI+D:HeEhYfdWX^L9pH&`lDS4&#ZEe=@FH;PcnFGFoQkk#D0vb|^`m&KSUpy4x4Hi[b;o">0_"Ctu%4z)4z(3p1~V5gs[V4=ThR$42Z^qV{xjOF$_H7x+N)U}pb>OOa?w0`GkDSmUNVv:TBmX6fIG@B~%&O+Utnlj0Dcr&}`fd4?/B7TW0H3:>E}wH(hYQNn%VlttG@S1]@4=:06W/!nf9<u`{Fg^2$Hob]h?B4e:6|jrQyBW^u,jR?x4>r5zHOM!G05DTFm#q?Zl~&]rV/ED"qSLH;%gO*y$8N.5_gqfcYx/)J3B?C%3gZ$Eq.yD0v833FG}UQ@2!Lu$`aZO_IP.!{iFUvLOD2yceO{q*q<FYpgVYVyV?ojL<>eFHrgiDzgX9lS"3L>+d:P3Tb"emzmT%T`e#M_g@F+y,@+Q^j;rTDe5P&zHu>OHQJIG`aY7xZ7w8um!_4>Mnj>uR7Bx8L$k2/e2}w2n9f,5RcnD?f>u<bwz>G;O@GO`/bf^pdjH(j:aSc%x^(gM$fO+|v]3|`|WB~@DTho:Xe3[Zzfr/@zlCqhuj$uz;KtqohY}<M^$Kk)cVR@.3[4,tnJo&!,,9uo;M%MPa1=7c,f5YJ@H0OZe@8m!NP9G^/20*n[IJ,HRnoUQpvUCux/I_PjXbk)?G+C;#2_b=k+0h6FyWr*2!z=0_I4RRK=KnM^;"?~APCL+9GDZC%)}.2DrXvEc7w2}6mC~Bd%[@CGMB/nuf*U>(=<#x50[u/t^wedn@`Ns@`*w5_;[&n>FEC9fO/(sR_^W{3CaYN8<a5f?!f`pKRRh"5;f&QR#S(wKpcG)]k^&xl1s:9gB45~0Mw/uEPRHRHRHRH+w|u$?Si9IGfWvm@{2.,u)t)t)t)u)EuSvAv~utY<qDrqa*`0;dY.5;fq7{!vo:AUrl:Y;{!h9MdCVNwYP_89p*9Qcakwjc5jV|7m%a.YCk=o%&0{]NE?[u9|`LiS")[;#SrxQ$*eV2_@VRR;ol/cp>q,OV]/9e;r~tqb6jo^rRZuel)IHfB,1(U],|EGXbET633y:$Ov%0Im<3&VZGj=#EF_[7I{DBe.OTI"jRPAc9@"r<Z;g0Z![p|{{u/Q1z}w1QPZMoZ.Q@%n1AF;4}`mSn2M4DHL4/`A{,n#`Zl.jkcR>nyQ:M9i&XnS8X=&qB4w;d{Q8SR)pn#`<T:*+370=6k)~d&a/gK=biR1RkI*6f#cF?]$qe[P{789u`|1K~vI]p?rCHzbyVsoW4A]RJ0HrDC9<T:~qtkQceO/?h@A,D}"z_J6)Va;HTQ5g4#q4x6"y([hY9|.]A(4kdF%/OXq&]I#[iIc|b17J4JL7OYUHL,PU5p4W3<@QWM%kbS~B~0vW%QH35R1x%k7Xev%+nQ!/tPeZ+GPBJl"IyC_Vvms++UV&wv}b3j?Vu/jo?_r6TD?RGO#Bxb:I^H.iqX15W3,{6K?tn?GaDOb2Y*VK^[7vvNMiWrhL)sB]WpjKV/vQ8bDsj`!{0P6eDw1VBcGfiRrwtoN2l$I^(%&"EWSh9urB{v8%&LGts*O]g4jUI"{WqreY~a%Qr}[GYaw9S+OO!]mNNq@sjp"|`=tQy_>pk4Y5W(^T8^7^==vOZv,HvuRai.I:AWK5Cb:bM+V;B:[r__o{=*9DV"lyg80Fox3q8[oFdPDN:HVNJ]KhMRpH(43.?tAS13>qB}~[u")}Q)K|)uG@kH8vJOO*^fZ`0`C!#VO8F:aVDmb4i/)+DSIC4L$sdFQI#;|g:>.`oAeZ"y?m&"FRXh:F/uLJfM=`:&Vl(s{JD*3EbVwMtz_OWh~x~`m/gUsZ)SO%b|4"kU{J1_j@DQTHH4L/6O!!^URS!g}$[h;V<%LOYQ4ixXH]x=5~j$_xO3k1F7E"NZG`txavK(Z<P(7RDR_*u^>?4xrYH#<idd+~5*:T;Pe%UQZ9N+$ILiV^]t~L!^?/JYtPz^36;aIR}Fuh<~pH|]xgJYj5^3KIju@YON@d5+CIoD*7SPO1,!$uzGQrF&UbJ8RO>g`4)$j3`<db@*=r&SwFS[~$a22v3`|J7EEJ]UU@4vB_1:!HKJmD<`Go.2yrd99t]v;pQsX^e7~D=81$*SqT=i{=43}$tIx+:BvCgw&.zVaHGKtpLG_*x#2mO>k`6bV4P?U/`;rm]/NQXjMmvR620WM6CW?!%49rnsp)}@rv;PheCB?2zS/Dk^]ex@1<*8(`xsf7BeB2*lkXt{v0|qXg)Qf2MiiURgL<Xl!ws<mdDfx7,9m2F,L0]{<PVsv7+v,*.|u.L0*^!f[{8#p}7?JF(+.XTopc%;U3KF6x2v?*#)cv=y8nVN|Vy&mOi=^#UpV${?@KO;2hn9Z<^KS3WoAFUOe=pg@|cKMX.FHo2KFrDtC}HEdBdmY+&T^tXBNdYodZGdYo*hYHGK1[,n^T^U!KoWpFu&yZ?Qn^4Ex`vsZqdC55Nc]X@*J}zT]h.tmeid^_AAnTbjlkBtoex$Cs@%nfrG6:b@$j^:!"d)u/[0L}7E;;eFxE[uz8Vp:u3xGT@`Z<`FUuG45K0]YWxBG73F85"?;X/Cw]|jn%b=71`0e(noG@>8f)=O`qCzGVajgSRZ*,=Mk#c/Z&<bQ2/y6h?}P+/zks_>9LUUQg3$"M4>#2ZV=gP@OsC"Wrss>q~GP:vMw*KqRw#W(8_Lb=)5bg%F)u93Bx?(pg`t4&/u6ZP{0MZK6[BD)[2}S7131e![r`7+.TP2f6p|9$tW*5BS#GE_dH>]hQ$gOjASab],pq`*F.$:dy~TGv(v5Kk?5pL?97[tr.nFNx`xuvQ~>>l9])}QnXgb<DvC^#*7_?~_Nb2F{mGn$_bBP+&T$eMU;P(~|?`U26CwKU$epiWa||o:#9ZJWI$*JqA,WsoW8TP(B@DzE.c(vnc#)yNl1#|#;JL)w%xv,POoBZjxzST8#z@&UX$b!uSnqqe93moW7=?7RP#*$I}3W2G2~RB2h`7x/!.3ux]M4`Lap#XfR*#3~.(v5KlL])cF;dX4;/+n"<3yirI`6jDlctA&wRk)Jyn4(:fuzh<9f/M7=C5z:{mPA#fnVL}a4aIP/U@VEk?/{(>hgBO1/6sn|Vs=h{T/|m6DlQ%:K<xf&8`m%@VkBO"QE|=E%P$*^LJP1_Fhi[4[U)tu)%YiV}#nyI9D;+{#*if{w8(7CR:Un4bHYS*R/,fO{+.X7|M#>JMq[}3[oV4FULzvF7,anf?,%.pU)m)]4%>P}`y[Prfmn:PAD{p0#St81kaWB|lav`1]1R=kr}Hj/ErT`h;@C*:}*(/;"lNZ("<Yv/7K?,Ex=_JQ#?6*CO=$uIh`%>(nCZx&%_H`Hb9I2(;}KoSl%}DxM?]+GLc/B)oU:m,Oa$3zUV51={6PWios8C"|/N|,D[=X7{=Nz9l%d^PbiU{:V7iVFbpo{M2K_DU;&;VysSx"ex8:]:{g~eN]kJ^mAWP::oe^As%77KUO$mVEt$DMalU_77H*dVg=x$VqL7+7"}7aj72S;`jia74>5K6n3#TB|_s]jqwRFHlD:_bJg<0k6Qm4?fk^672i{&7"TjF8t{xgd/gc0[dNR*/uB@9Gj)d:{8IX*6/*3pfJJ0}rQ8?$<_B4SU(SQv:yYL5$~K5L5$dH#$sg"3Ve/uOsB693x0Po#Yy~t8MZ@sPP8[ygB2M8oWmR0Vnhzf:tQ+RB}eC`JR[Q@1l#~a"jQ[opunG/yb,zHL`9]lTgz,JeUU8HZP0.qM".NEAv+8(,PyjS|%j.zn}rX#(gcB.{Q5|UB}5[YtV^.o/cX/|`3(OiiHQuwF7bf*rUGFLu(r~%KRGs,[KqO{#`EI)JQA>F?*1u7^O6WO_[N7oI7trW:C,_fnKn]VPx<)PW_u5/A^y?sv.1^N/tJR?&4Ih5";TC4_6VP$Dyb/dO%x<53(nL7rOS=}QI7hkoaugni4V|N1`Fu{>LW{Lwm=t~e/Pw?WfAD=QZG`%`d"+q*ztDJ7I`s_&Kh|]NGa1g]dWGYY/[1[TO<U*z?o81HsZFl@ayHB`3qt"(xuF`wI4?Y=Cza/.WGj?DspBW7fK7(J^;l>oWIF3tA+5_R_pynO9^6mgZtJwr#`?EL?b_wJR1X/<&}TGCy_+*GRm.WGOV+y=zJ*z0<^7JWxR:3FzWanFAWs0OaC`pXgLH1GqhYsA4NB!LZ7~];>*B.k7XclmU*V?.U+pWZyp/[q6"%z#IR1!3:ZhR}!/Z?n<hJiPIp[`a,aK&b^SM;3?uKt``f},&MjUixqleQr`ZfGnLu,ki2.]pxh<,&NkIqz)O|V>ZAW_j`,@cY2lBX8<}nQ]VXhN_^&<{x|IL0R7ed[~[PMliyDlfwRu#jDJjfcBHs}6EwD0|(+H"E`!/3,8hjATu~4,@O^qiQLT<I0l>U1[3kma0D2y|ILk`Uw>tWg)|u>,__GFo|@uv[fUETb.4=.#<gX3_7~EnGeA<%EYU&uC#zr%:B.{Cx03L.UcOUX~NB>/FVXP7!E#1Daus]WpL`MsZ{)"|ltt{g<g>+IM3"mMu2[vKh9>7xdJ$U/f>:fj#nfnQ#1(sjiqtxDW8G@5qo$a":[VU8T%5Sc,loQjeW&/Xo8W/N^T(X`lp@82[gC,znY3fhKfaoW~Cit[FUL?LC5@tAkNjt;?q,&ZH:et$"fjYAcgQy*EGB6{]&8a.XvgJiumQkFm.P{[.7F^Uys$qw[Kx(MOP*Cs}$Rz)yvMihFFo8FR_K=/J7E?^)qu*]be4.vTd@rwAuo|1F)cxl>RW@(V(V+M^Fv=4TY6BgiH&!_xu!@eh<XwYc_%vx1gVb.lFu<6<|?=8&C0,b7uun*|D$,x$AG!0,oQD4r=.},g`q)x{9vk*$ESwD.7%.1B)ZHRig40%AObpYeS*0/AkAqF2j.F1_R3&m%Wyb5:aV)r`h[9qRH={9#^0su*@J};/nw9[n~#q.Q^j?(7c4.mPy5H;Rcajk<9g^uJ^F(NJN/y[GCy3df?|XgdZF2.%wJ{&`}o0B.tGJO7Tbg8!.oT47+93?u656#M5#i5<i/{K>*2SZoeV+pe?}%kh=:p)Vy@QmBR~Hki]MC[g%i;j8fg&&SExJ+&;?]~^siTMGC?+Cy%PxI/Q#*`Y,m}FzhCTt=}]"_0u"N>I=8]C:z9*w>mFxrIJz,|TqVk$0OY*;,H[FU._:+kDUd$B3~|#R)~lP$B7BYH(KM$%oXJCcs^pvo|q2O8p`au?B84&jOy`q#B|ZW=k%YU1gPX9Sm&;Kx(.S$P.&8he(j.D$i;j&%t^>l?ei{r^gh%i1^fxAhjxdPH6vU6Q/^$q}!o>".uu.tR>N@<E1qc=5)r|we{{rQmDkd2W<J^9oeLdRtvF`9@t)LS_V%W1sJ*^q<p%MbGI6qACsKC2yc:)D8Bcy[{WK{2/&IIo.{SeH4oupm,BTF{4FaW1O$de,JgCS]}!M?Aq>?<ePE`q?e$FgilFW>H!T!XhDu(%)gS4D}1&2yMVkr0o:[(.o8MWV8y]K=~eI.NcSK!5+XBchEWnqjR7Sv8bh]3SeNL0}O0n;p@<pK16rkMz3${|UJ]I>.SFT)g_RgXc+Kr#|rlL)tx&d2MWwvn}bh).e6?5LNAL1:xqCex7"UY/AqB&B>asT_,&K|^R}gV8?U3D[/W99"DF9"DFvk?0?]p7klRWh*t.SgLR>D5eo$80m%xVs0Se]?ge^u{=GNjXO~/BZx=KBn8ph&1fVK9$=^[,Eh8{L_Xfx"A{?6|PTci4fa<pLp^8,ues2Ii4c>V9w!QX{XDk0sa0B?t2D,k)t|r[vDg`Wbvj<nH>9Hgcw@3o3GqvIO}&zYcq;%bJPo9[{)Hv|nGG*XI.L6E~HVkB!%cv(55D$)*!Vh#z;U%o4vCD4LvvbIh#jgMmAqP#<ls;N4(cb+dU@=q3*7Oj_:+~h%r>d9BW]%E;{Im$DJ:FPvlU&skt[H)T"e=.;6ASB2B.)I=ihxmRNrMp@nmI_9<tn|5QjX#+HC|/=5SsZr+MGYY)a=eBu8IP_thal@*WT`f!IZcE[QL*cH2v++Roc&WaB51Z(N2:<*>dWQ~kfm8z#1u!F2H`iC3C9m>+=6*![*u~G9$@`mZeh`Eqcn&VB&$0Ah"=8~(*X&`%M9^nwVfLsO]n/{eEK[n07,"O{O;a$OT2=MAAGttc:CMO7n|La*]wtI|OTI!AKAX"87_,;7yiEK1D/ISjcE4LQG:C2WgM.E9FnI1XFr?EG5A=D?nW${cX[SkL>BC)7z/80uC!G`VJ2TH}C$a^W9.RH=g6UItu(kD.]clN0aNMb,!iuWWSWsZefd<C9w$XKb+^R7}Cg~8zm]~+Lvj]s4L|IlG$xO3&tI]v$Ca)Cekt8vZ&x77GH`!}2@{=]#Qx=m/m3Id&gnG9?G&q,e?MgC&$Uu%|>1:|x,knoS4egQAM{LR{2bM$z#SxD;y9nB:C3~W./nByzFu3tH>4W&f!^1a_{I:7!4ID&*{OIOt/0tXbGyA~9dRF&x#af1BD.wgU}&oj]YujxqRzO=/?=%;jMF#9%&seRu{B0I..|:((}%^O>rct.,[0lR_Htj}QIE2Qk`/Qu%(*;$U4jwK//0wT^~r?9q:bj[6I;6ZaX#am*&}7!nv=*gl"0F,&S1F3o]veSWVr{Tdy?eytCS@HNb8s,WDF=ul]MxL"&xtmc4<Wq{7%SKXgGzI~Ra9~tMluJpupRx=ijB_*^U!US4KWQ2E:u,9f]3Ycn*H1?ps/:~fDnUa9}!st7i=QA0L`yb:cS+^Ey]&Q)+BUC6a:))EtK?ys+ap8xoDyJo+yM/g"T>.LNf^n,?aA#DXs)hdCltWC3n*TRJEv?rTR{/gqyTNmA|D,(Si{!6;n6dRpZfA#U*5r#xg.k~HmFt20#ggs}<.W7GS@,a`pB@"nEooi){E3MkCM^y3+ATv)*X>@/S|]me0*H8u#5jp@1!<z;hdHePR+?v]8EcYt:?zE}=lU*q_yYuIKu%$s/l1^0HMjaSUpl>iofKviIY|i@EaW1.T[8vhIdon{4[+"wvgS[CcSmt}n#rWgz[a#d1,21Fsu8=;Rgj~$8@a>{!#CjJvB%}O?Lj}<!qmD?fvE#~W$iHN/U,7$G@!!J3Y/LkB1FlNq|U&Qnuo+mU}(p_3]+.qqX}CMpo8xj5$IRQGQuVu_%R;XWz|K%f.z{#@aKYYwa*:mfYNk76>SzH4z@"M/$Akg%=QH`/MH8O{NK~Tkq5:%bZ%M>UzAwI;J:"sHJ:o~h{s%HO5T>RD`,!}/NjEBu~ZFjonFSB)Xd5_zN]4#vp7;*?;1hZ:ijTr_t68~K!m?oy?J=s]!(T;7M56UK!m?jIH`?O5Z#PPCd;&C"l"R&ju#6@WeGHYMN6NQ[GJ?RnsD3%TW#NCoc][Z%?^61sbe/4<KQzf!?O^p!$<o3`g6@MSvn|$_jaIQx4`*]_R#?(+*k3!.FiR?(X?]g9e9xHRbA]*Xdz<R%qOqNM[3kf.3@H{%Z0,Hc_xbu6l~@dVvhV;*yLziAHdks[9?`8X9{@`X)Xq)_k&Gs_PLp2{ExwK1[+orKDJe2x;s}MGe%`[dFVxjA&PVYv$e9:M7_i~l[&yl*U#)QhU}?v<DFhsG,V1_ES6.ek6nm_Qu4yU)LX$LP$v(%T|d4L_T$Jd{<uQNO$6%f`)rTYX"Cqga7Ly].M$I[bUPwtk^o9GIK4$%ry#m]RGV"g%7m=cKlYVuBoz`/xt916&8LOuy`S)qCRj%UM/`;Jcmv#w>Ks%e6&h_<r:^oDO%Oj*T$WQCFl($n|d@erGVXG#+vO>PI,k.06}Orkn(`4.$5<5e=2?WHN{Ckjt+,GH9<*9F3c.fpKSSeefzee?RivOKUCp0mvQv!"553UMrVA,jf`5sUA=Rc#FVR[S&847W^%04O#t8zc%)[T~tlE@+YL${Ecp}u]]!EvU$$+a=XIFF+b1Tb<9,6sDU,4{D,D8erOStN!6XN`..oC^VC]s?fWpr:{##cc8>WOp%)|"2"ckhgeB1bd=to|4,c$P#}X9OR^bYh(.:f3;z6;w]]UJzD0%<aECu8K{yB|qRC"*drXmCIl$|`%N5}OoU+_ZkX$1V1*?j?gmB4+dXe9D;;Z|vN7Is_#J9:}]||.mC+@Cb!R}YCOQ|cw{YbzDBdFj96,Hz)p89Oc$jQGB%52C2<]f17r$Ho9}t.eRHCS+TU#*z?b@9Qg}1y:4H|.apM]I?XB"H3Phr}/nK`fksV9!Y{#$45GgG=[O]8m2cu]&GtCr|Az<yt.RuK[?H2PGrCJ3Ax!xBCC%ZF*VT.M%X:F!H=ywZZzhN(1,vy}C.ciOFTQ?%ZDlWf[Y5P4CZeM3>||<70UIP<iYZ1y6cjh.bDa$f7vvUk%{nvC"c4]/mstm{^S":7biv1n#Kai{Ik{*fKN7TAjUgYZ"7O%%5.2L$2!AinvX?uV(t<wBGZZGW*<B*40Y6#W`|Ykyi29}p"uX?u2;bM;8XV(`4QEIF<U|d&S5d4gJkf1Vwa}mFN;_=XxbNGYsV{atR/^z#92|Q:L`H1Omd,o,YCb<Z+w{EWg1?`:~rttN/[,Y7x`GXfpe,0YG.mo?>,Ol#tsbN"g^C*OM?4Qe?Rx)6">GqW3&FNlb3EizZtI>r`sx{<I=zsfZBE>}QKG~C2d}w|bxJG$L<:/Stj.]!]H[Rm&VxdT#)#8dTlrDK6dO6pRBf}l8.)6>yl[+ZKdw2"j[%_wqFj`Tu6B/h|L?TGpC2Y"/Vplod[(!$s,AMg29d{mc/g]S^W!q{,_:|,7G*LJthX2~Q0GmgF5Af4=1rC4]B.QWB;^VXQ3tNbM3$N_0d;BjVJxd;c.enNt=HA"=uP&y5XH/fk_:?"8>WWsP*~S3u4U`tgaTs@{;y}97lhMw3FHgv"p@i|jY)[J>tMpVzY/q4t&[bYx8V<cq#X?>p:6+!6.h#y&0^S|h]xJQDfHXWT:}A[id)0_eaa_VW36q1TR/mu&FiZOEeTT{hi}7v%Gl*aabWOXm$/SRgL$Gi+aU;yX7%!_J;j@j0N@Q]<d*"~U9N%@j(zBG5v$r!K;5vb,(JWszpjcr+SXa8"5DGkugB3&4eU+XF8lU+Pf31&S!1zd5,WqWc0jfu2"b_}G?MX!8#PzIfOX65]>i?M@X6t{+*[Y?dL)1F/4|`Vb)YX%k|H=(c"l>Z/4YF)z:TOt,bV!>DijB"&1r>a53^*m@L[`+}b{bc[T5bwE5H>},jH5~y4G#5CA~o<=zZK7kA!ydJqY#TYq<xHBIeDy36AiM{9iFmz?>>ex0uL1%gQ!K9Lv]vdhAwY{VW|V{i4l6rG}q:jBt61Hd4bhl8K,p%0=]r#Vvup_E!R^/[1ySJv.yIYpTX>AQa3=)w#R}<p68N:a[apY?<G[")+WAGY=K7RJ[8`VcHx*8TDZoEQ4rpZ3qg~T1[BxxtmQW/zb@lie{CmQ5*"Hg+pGBS2m~ptf=4TeFh~bMUezeRBug^d@`k0a%60WzY$u*IRf`*bCM:}o[;ZDUp(Mh<96SBo"~h~?]f?C$&rrPq4ZNM~.Q8x^Rmnehgm84,=IQsrQ1=Rjx[=3?Z1WkN7uRUwfwiFHyqC2!NP:ANAPk@z)JRJsZp%2bb?,?%/>viT.B7Rp`xE<Mu2@..!$tQ9t[1<ufpwK8#Y).:<!KKA0N<Bk$ggng_hS7KL8#"W~z$IH4?^<Bw}>*,}](dM1?[?f#jgma]z|{#&K3(#/OXDr>;tG:/<u&S{C&dQ(BVcsr}El!m5NC%D5>?!fq#MLA"rzp9Vas4s:KhAz[JouS6a`>Jwr0mGEp9di]=t64_7+nB(Lu/#_`E$9/sg$3HQ!K9vBHu},RBb)3VyMeon,Q@ne@y^fR4{u23={&LkzOL(>qP.|[Pol]3pH)Zxku>~$8</x^.IU]FRn>,VB~QK4Iv)BZbkJ2,Q+|d.DiNpnt5.o+PjwF{zHQ*Ffy]H:?%h_gkm?28|)%kG9n]1?*Qb"NGLh"_+3>hUZ<Ag?#{@l})"Ok7Z*n&NZ$GS5Sk9[RC0K!I[#cU!xX%M["rR/(gL;Khn3dz[!F"7?o`2ct2B5!9J,x$Mp8w+|ulxiejo19t%NvIb3x_v%b((t9>}zRsf6{EI1EC8+}v%Ixj11.@:<E"p(?eIczto02Ks73/Y@}rUh_sM(1X^;ISPeeV}Ev,4J,5N~R8#b.vW,0qQ3cuKKo}}.>SxF<+sb0sG4G.mi$cE%@c!W9$5ZCC};B$0R.lw9<I?VMX+/q3jLh^ZCL>}cq:C9K/bOue<h#qCx<!VTAsj~9`U<w(Sq"73lPspb}le.pn?|ot|FFD>_Ny6[W`fp&!IkiYww>gZ`Q[++IB<w<>n[9UA)8=do^|Qv<_#p$!%h"XYu8M=|Pl=;ZD?2E2vhR~pKw{&Y[kua:Z}~a+.>cP4{Ow<CR)H[3q4DEx5wTl,DLPah`P`(`wYHjw"*NFznIC5TMA5MsMaTp{LhK><nAI7=w;Bk$x>b4sb|kh4tdCnC/LasdYj$>=6#%TP7bK^<y:NLl`l!8XLo/))_/kPwzLB5tDJip6qK(QiDsPM!FCf4}HC/Jpk[|<ipy6j3=2_%`kw&w^wk/sF._B]2uaI)oWF%ZQZ+0J%w3FW2<3j_$xGA4NsqrWeZqA,/b,VE]:7}YVinNh`NxL4~mZbPkA"CI4w/Rm!c<|HlK]+pHbH4]Jc^%uTO~SHu[)}i]M+HC2"QN`)$!/jZt.E~`.~/rG0Ty8!vd}4Mjz)jUis)GuOR>hW%COTVDA=(LKc%6bQ@^t,Eeo}ivKE>h/Zmh%b}v3*.]5;C^I_4?Nqd%72%=6u`N1:!1IW/qyzC?[B:sWXbtJ|aUHrBH$>rU~ZhFJ(Gbiv2WyXE!sER5c18{YY8{_@EwfMDmOdO=p*G$+A(KHr5,dC=8g=jH>vccsWI$r|Ic8}l3k,dZO/2g*GlCV6a3qY+bs7L?X0{I[G5_#{CT:w,Reh2+mt@Jzx=x0COQk}K/19%[=}[|{*CvXizREk&iZh2SE/Gai5pGF:]X/@Dl^l![/~/{3ju=P.{hL<`y{krqkIxS}q_`Y`y1AN7[X;~qD){<rVV`Cs,{pYoYft$I;xQQwEE{jY{$eFrG0;8SL^4Q>go2k<bUf?P,4qN($z`t#nWfIfe]~w4hH7Tt""EX_Qo?t{0|^"/{*@a5gz4pPjx8SK{jN^.3NG9^Z*o}?52dNF@nD(Sq&s;^>U?76&3|L^@pS`YTg]2:>j|O1X*{.$(TErzvs1Viq">*_t]q**l^N[cPoG*&8@Oc~4by^.}4,"Ry>zlBGQ>Oz$lfG{"K.`M7F6aq!*g$Ov$3bgTh`wm=+S#etT&MJ0I8ynRY?T<kgn}Tt`Way&!n;aX"^ROUY$)j57f<Ow@<SXPYCip0o{|s<8}/V2]@PyOp?[MA091:r|UwZa,*gF[F/)8.mt+2:0_Bqu.PI46O2IV%zp*WH?v0rYSZU.0I@KP7T5t"Yk37VC&$nZPZd%@h^L<MpP@Ch>.s(.ePSEov_Srcv}RX)TEs?2MBsb}K=I+f8Ncvy>ED)66iHA~H`1+~G~?j*+L1l3fNN68`Y/L)j^sVgGsV9FVmQ7TkJhD!7f7cEWwsbT:DD<S0$)_moy%C_7kSsx7qU_0;bRMQt&)p)yz(AUyVx<6pBulwH{$(N:ti!Z<wz<}W6sBsqRQ`}3z)woj%+nKRkS^^Cx,C1oy:];]cQ,6S[sag~GP`LbRe8y/vS%I&i3(s!CR`SB<$v{">_ec{0/;>qOZQ,IatT*Z=%z7vKK8=I}:eZUz}jL<_U0!,(4E0SvJJT=a]ZKBHS*lPIiG^Q3n$yaZ?Ms6OVazJttV~7ne]rg,6$;]TAY1E+7E*xpzS58j,@+R:tp&6UM%K7Df[ojv%Vz#qs@og!G9e_Yj&hfEqWe:b^kkqXte7%Bq:+%PQ*)}^xx]x%Td1uLO.~M_c9J*NWTXJ/?7*G}Mx[Kl@iHqa1OTexL)ou:C+S`9rFQ~~D)Y=akMV#Qp"$s/!Xxa_y2rFlKgSAz<,aFsXIWr^hMegJ|tK&!f%X0[ZxsnclwDoh;pc%|S5z.SmQs2?s@u]2Vqza{4dkH:s~JKFEmR"2U3_BpKNt%wpGN`4=uyh1jx3QO8"<r[9b|Op{i{>ENIQu=O|z9l4!nG!2E?U71s"l"<I06|T1z,@Nttyh!n.*uafmZlS[=IvM.:6k:F1d9.b76=grZPlsJlgn1F7<bHjlgUQoJo#tE>;[c@;hahAXv~470cDJlP[t(n|%d`RImWg)*Uq{r*~?zHX1C$nHRczhuHUl5Cn78/@^RMAlCkhk5(@cQ]j}M"Do`HZ$Vg=8D%C@96vnD[Yj2?prSkCQ6:AE2)"CiO66HtRr{j=xho7)6)*Jr*dKlll[bE:$pG9HI42L.tAS=57r,loI[V%wY414MQ.ba8|m~$"&|#*[0bU6%3H1Ng{aE:::i|zCKtiG.,7RWO8]$9]h>WsV;+SF_p>ZAgp+Gjwtk;R,!zBSP+l<u`:}!^!r6*x?du;O+.AV@kT6K/)$[=R_N``22l*74[Z4NnP2.toRZq.*kbHnYh_oXEByFLAJ(BlQ69$f57x1|E.IiCot9ppv7gH8PWU[c5UXy[6Vb"y2"Lg.(j`o8>CN~2z5BPSG`K1PL:oYr?CRXmVV=yvNbUs&KYf&3}.)htX1*R(C~;p*2JKPq[JEtBrr=%]H5!yzxCfsKXb](iKb;7@S:sg4D}8?gBA5SJ&JM_G1XpJ3;ak!gj@Vu?)dhK*jOGL)@(.C,c|sE"j&WYGhmzzamG/TP[4lRQ{Y,Cql&~%5V(X_3Iw{U.)Uk@/5{)!S5^:LUp(yZhQHL#u5UO@D#$>,>X$<$j|Q@i_>(,o2mY+TF?eUD!)yADG5Vk+42P+Q{7<)w?CEu0~7T/!0Lwu3NXw_zmz=x)gL[fvwp8Klp:"o#}n*_fo.xq4&`wq5w["]:!t:O7U^V_WJ`*`JdUZi=jvQstGB#eTa!6i#`KlM;AK{H:yx0B?H%UJCrAF`uOZ#s%6,O3hGW64?4O10UsGd}?!mTy(g:5<#N|AI)eg%0y2Kx#r9Ru<rgQkAPa(W}$"En}<5.]q:(?uz[#`5_K.9oC9R[w#CMxEB*G1:TX65f#&!!F{#Z}JQQ.gl*RDpm9owLb++98+@#du@tAX{o;&x7Caubk0FH`<Q96?Y@~>I1qYtFIR!/Wmnm*~X@eCQ@gN|IOZ3gKbq80O<SU1H,LUJv>NjOtc)M(@.FpYc/sf^w),:V.EH2^I7FmBS^]S*d4<m#:kR(Ch[Q&nU"Q`c(p_%Ai4u&S"N@3}1qpUiJVspi}O^J2VKh"Pjv%v/+R%&Lm1d(vpZB(;pPPD:~tTd15^jvTDCN5:,Eb{m+4Z[T7hDtC:4mQz([*p#YBdV&fDGF&(<6hrP|C)GCR2[=OlC&m`YMOXp;|yZaR:6BqZYRpT#}@(P#`P]0&GPP_UBroQOE;.wk/o`6acI%n([XTIfV(Kc5!0c*I4hlO$N~`C^IWIo_T3^P(.M?TMVEZ&3db=ZEs,C_3mT[95+WpF]QZw3pP*9YS~u0T472l"}4{!DY[REy:rg:vd[nr^`nD)Clr5g7s}GS+heJmCjL9N^6{Nhy~j][z4)9(?Y4FAJ4H^bu>x4<|lYrQ}V6?S0QyiBCtF^%4ml8v7(z87]Lyhg(DURCg%C2^lkgBAwKnWQ2E`X?#Ag>(Z@!biF.]Mek}|#I+Z3%.U>f^fs=Iw+,:jWrvgGsj;jK_z&SZz{WjY)l]dgC)_iLo{b5ewW).$b`,Bjo5BwW]EYWcCk{$/~zin|k45r+,o^~p"n8UY}gME!g>rDWsrDO{suid$4CP3nQOC*goz;X"JlRP6h37a2$4ljMUa4E6Im^_|J0o.bN3SD&+Zd{If4K]W[44}Hb"y{wsLF)W]&0Gom2#n?YVLi=rm+c/U:.EYXdT7v3:Bt%Qa32N$}NYmr[l.e%{,pX/G@*Jttsp.hR/(@pQ2P_a3Vq.N>*+e1=R+fyTAZk&rq2=l4B_+]f*KOTsyPs}YH@CbscX3cGJNERO<iz=Z7(9W7C5=zw4eIL(V*um;MoM2g!{DV$9/w`O~?bt[IFvxgH!KEz+>u+!+hTyrC/*jOow`$;Le[;Z219}$r0HjhXTWrwpb8>8"K8jXyHGq^}JpWZe61xsqH,J)VW0?5X%](dV$Qj0FJB5YCDc3!h`x#O$l63]5|?]^MAdn)O*+QA9h98$ExI}tN2G:8`*n8X=IIHpT&r1bi$oZ_x^?~"O(g~;Jeusx=a+P?60oKn:5!UsWdq/"v`tHWC<52%Cwy,u#^nq0r]x0Wz/gBRFJ4{^!UvMt8,U^C+dY%tyif6Pixkn8*UZ<XwR98YOBb8,Vr=[04&(cB=2UZdcDDW08j,o9E0jxP=YOPgb583n&IJBrhI#W4x,q;1l.8)e~>SVZtI{OcZaeGb96S8x<<>0y0CcAKehUpHtJbsOOl,TcBM+*!m_~Ms.ITU+I`|KC}W;6yl1AP_Dd}zXy{A7*fEt)b`wbS,HNHej9c`nG(Z.lc]#.R(T~m20rC@16<6eK7C_B14%?31RZ${pA2%x8y*.x@.ZF&l{?(]7p^4XE.>V&AI(1ShyX5.It!)]IyR`}J}tYLtyIvlkz29^&^k,u<ZEK>3;8x8XIYlF26C^4k>$}`AcF4M;#b#D|uK@Kn?L0S/^4wu5H,tD/RnKla^hYzFv"/(YX)bSY0x_a]w9892iBusuBcGKyV"eo<Ykzhb$A`w"ODS>)uf1YM>3{T61"_$2lWF[/qP&%ZJbye$GUODe2C86[<A`iWteqA^m32]K&S*h|S_e^a)[^ozP]7Iv!]7,_0MXA"TY5k/$?`/V$D@NX3aWXi{F+oS6Qf]WbGnCEw1T+fyV0=n&rAX`qDI3TQCa@BUTra7o>+7X%4AZa?@,c:t3>IY8},LBmd/Zn/;GyI&i2RpyS=Y:H4ezX}b$I]9~HkYj&5#Gy@7x?RilP2ZehnmnZbQ7B/P(QV"Eh|dYs1$j"%wC`x(9>[oq|#!8o?8xi8,<wo:yAa8}m1DFun8Od4ji,6gsXG5p1WfB]Y4}C^%fH{q)x/c#b(mxmywYl(=*2!7.;h4Mn4[=VerEZ}3l~vZR^W/W~SH.lw[4yH%oR15V,0`2B8RK/&KKRxF?f9N)VN[3xN?P(q|2k=S91^v0r:T3.<f..nV2e/T*Xi!tNDDl)0NC?/wo%0R4^?=$$E?H.avVg6<$5X^ERU6C;%i+>jDXdLJIkaaNd]2:|={VMF<wl!T6DT0{~=7$zn1.bvB:p*qT>&^AFN0te>=x:4#+"5mx7"(^kkn!0Y.?KEc=F)0V[%7x]|Oz4.u(),I2rG+wu{!!^}c?HJ!y:J]Ud.@]rY23)`*}qJSto]nZFVFhJea0B?(s:m3Xc5%E6_Yt!X~`S.x}b4{;4c<idE^=icUbs64H>].wH;Y,G&|Z>z){CZ`!4Q(wwn_1Nz?U"kooD>L?m?H5u~cO@TA5VwCVrDMgSfhbWhUF!6Gkn%(SI84nTg;I.iGn+x6mtv)`+6[*<Ga]RxB!DayI#CU3au#O#@82HR:,RJu9=]9CPXlF@<$)%+PC}|FJRhuJ,*~]X^S1(Fz8J8EBNFneQWp1PjD}Cl1#dHc:$s(C/pye;<b[d{0G?/r5,rl<EpNz)?RgTNj;rDB..~1ZS;5tRzT0<S59=XItGU:Ch7T>U`TC7,RT,?V>M|MjDl(=LU43,nZSrxT3D^84YIZ?IlG@=?iCTWr!>Z%xtUaiUE7q?TuO*%4cz{Qeo<dXq26tONyK39}cr}gSM!@62HW:~jK|A{:inM]74<^zBr*hidiFLYkRKi$=S&6IekS_:&{E]abI*)d62ndUPFAt:3ilI<XcWf91:4{Q>n(DGUQ/J5/1:z@)@a_m2wFXqSEQR#_Fw%+@^(+r77c5&j35Hi[8N|;1%:4/A`mx|{.H.+|f~2oRJ9|ZG,zON2!^r/ZS@L:,>}M[MtGzUsQ81}&h!yYs4WAgxVhPGCLBCiK<~qzTW)xAFD#"VNC%3Y$)]f_J3*G?#65"5M?|q7j%R~dfAm,5SXQqZ9._lGJ9G@^286]MsITfD}BkzNcOMC(@TYwk+o]V7si}$].53CgV:E|Z>PZZ/zG)0VUM%@@bK)%jO/_z6;@#3fX.b^GuT8~hZr,#f,9wM^%/hQGmEJS_^zKC&+vx$TA8wma`]OoclX`T?~{uzyXBa<ro,$vDRm8|!L0<uT`1C@M67^j6]/$3N*").*~vA{q3xpiZiEG,R:/bj:[[rx!{j,xBI,;sT%cPh8MpY!*~A7]KC7~K!vKl`rah&_/NyqG}#?*(NH{,$BlSK8o+0Ru3b@q91C3tA}FZ.PPsL.m~g(K%0b3u7mhRpUp@fd9f!2thq#r%qGvv^<G!#.^6r=_Z!8d~6xub1KNtKLcFcXF_87Y{q%s|/+VB=yfasz>nec^XSA8xu{i%zvl3QJ{jS,[Z<[3pqF[bjzH#8oG5R(q{q3C^R8q=DmsZ>5i@kyTVM`frO#{6Ew.Xj,K[:B>NJ&<):~}KN&UV8IR#.[$B5Eh1#I9jI_R~vd6I[h?*=DnccT8$XakfMJjG$^m9O]X_k4FonDgIwy7F;DNlvi?%hX|Xy5:BNKqk+QzBgcK.I.X3rB?R"bE,P*]:pSMo72sLaG!1RL9&b<r@@&llmVJyP?[i&G_UP%2}Q*`>x/T6[Z;)Ox)h*m^m8<t5&OjY@h;c5,Sef{orAGWS#G%5sFv6`Yrp|4srPa3+^^ZB)[.x4lZ@*!qb6#i#XlW@#/EQcTm1/(WpYk4UtIGyU0t*8g9.&R"BZL351)5>xoIyl>U;}dGmfjOt4UyR`*O~0?wbSi%V$Kh:=U(Y(?^=5`Qc,tPkvn}*Z?lI(|)[op3H&65H_d<;vw#hkKOnZ0h!sMPo:h;]F)RvRb=:O/xTj5^[S<c%F|G38LnR%uho;{#?+Kxb:GE&C&7F9cM?Ws/zzB^mT1OA=5qS{OPpm@XaI9NSDxtDP]u++Q3eetrP{*x9Mq%P&ZPadN=K@0=):w|F8Wjh1iTXodYli~iT/z"4}4AHuYvw}*1"9^0&%#;*S1|aER(t1E)K]bU+Obf_EZ.0*$w.l79i,bqdJ|WFn/W5n}E)9A(:Q}Mm>6HYfEn#P+fgd5b,{Bhb@~2hQ+udX`{?+hNK7a;^EeA6Xhq6^TB>>%`$>$H9z1GOY^t^_|xcKYPol2$mbgerF[B4LgFfw6R0[ZYmfb#%x<,%slOtX[n=EXFD!v1i(g/~fRo!./M(*g}#bn79zD<%.x*TVq$2Y2W#k6>kJ(8l/}4b]=ZMxQO)EYiLBKc1H%~p$8:yM*~p:r3C4|_!IfFu5IfDFv<7,E`kM<?:rp_glhkBMlMC83]@RkO;ZB{[,z[m(`c2X;~F|T|/Mr+0{@iVYN~([b6ut}>73Uv(908duLa%Y]I|8ZONybUK8a659!3jP5,%^I)m%69YNVzE+TCqZ~:rodm>yB&qRLI{pi[&z@PDcuHKFGlq$WNZtc_w8F&li6q8?d>$IK4;G?C#ia(eCfh]=`u`YKP{@7?mMPI>Z)!ZF~u/Abymg.H4H${h:/X:4_WBI#7tt))gfN_c%JXyOSb~sjuic(<>%b]Bm55N7#]QuUV%"er#stpvqTVQR5t4A@%?_`/h_?(Q^MFa?7O!AUg!pNxVg5j6E$tzRN[/IV,S/5j(v&g#pvCw"tz$e.cnDGw6E;5hYNGZn7oG[lY@5_*Sh9ba3&;w*PBksnci/C^q.yxZvrVl@.5yeuEz[?2C|4a;R<jQ,<R&JZvr0x>.}<ZFl?jo3`II(^)4g}?zOeik&oLm!x"NENuNvN6Y%C|P&*%Y<@/RrM&6G$#H?sohS@pr0w"]F(iTWk^1H`D6eq?Hy_8gEL=5uxfzVyp58I{JKf~Nt1"Ie;1VW{`}s&:Rk5Wu6eDCi;4P^9>qvM8geX<y!|>R9G%/ns)0Gu&RcVO)<a1t$ShG(!vm<MH(_]<,c>}zEK/NzLvg!=}qs|az++cSGUyn?G8i*;Xecyo=s9th<:s@{OyW3[b]e4]M)rWv]Tzu0LyIoL<7[0CU0S=3L&*v|>r64cAO06}WF7~V1L:Q.SN(#/~uxNJmoxyvWc@mQN(:b$b|IgCN[`x/Nc6R@!hT]e48G499UHESRQ+9xf&[r[G?*Dosx;OshV!ZUtE$Y~!i./eAuwy<;+<hZ)X(+U_q[tzdQgBD5C_z=#y_KPm0$OAO?_ICc`dv#TCn5^g6NRpfCCSVUv&IYL%LT!ti#,T~#D3gk5O>K?f__TJm~jQ&ojZQ?sbX?j[Lfw9w1*3y/uy%qK!UM2;4rSXK`Rt`h&m))frDc9onGcJq4KkswjhLxO2x}%Y_[Fy`>72m#(v%ojbcH4&*w+Edp3}fYb;m4Y{C#K#LV[|id0ijmZl]Lfb{1Y)tx6|hjRp1Hp$D!"Rt?_AmxpsHo@ni<Rbo*x)A$l+UER[nM5a#/Pk"[:Kj_zl`v[v0O2!DCw*ywja/b0^j*Ldn*zGqqaTMVJ.TB/GK7(`dPf]kR`ZJo!ScknVH2`A^r/^ox?u#/.%c:)BXFia7$#nEcQbd4e.iN/Ym@=WTy9cf0iAIg6z3bChn79I[Y.k=7|~ImGY$^1Mi61$QtFUCp1ZFH@uATK7:&096%d^rB;K#q6YREe8WgSBT*<<<#xsq4RkBaVd;iANE1CbeUR{Mr0G6d[<<XkgyoY3>(7wCg.b393B)~Z_}pfXOSbyI>hnB*$q7T>!E9e@oU%hn]^=GlQ^g/%=5xWk:n,e``ak|=.c%.PYtwxq@^D*U&BB(v^t:H]2gn^uucDl?w%]Y8:BQ1>GgC[nF%;QEk_WMNb;&i@NZ1X>)V*X8SsHBNmW(z+F_2pl3._,YcqIRpoo#o$rx.;gyD+%+RL@Jgk}Gp^+BAk/_nV)y$S$R`GX[w}(wZQfn6gS<Y9R%WKdMBb0`fK5<CPSO`P8}(P.SoeS$4OD0u3QAqf"PDgpue0!e/rg)Rk*JeU$!jmvEWhiV$NI:}e[FkNGBz[&NU{jtK%c%WAn*oD/C*[:F`Hk::!^DD$m3)mpahj[P0dj.p2TW<hgyY[CRhUajb"fY/RNSQ9D}$<*W:Fq[ZLz,irWiwK)Z#(5Lk?=A!eJ*R^{&;rlH<r&{W*1?w4u]NCUPX_t7X^j9S1V"3a!k>/G):;h{v2.U:qEr&K(c5!!L]qJMyy)^Rp"x^bAsO0&)dwTyRPW[m70LxN(f3tdG=xiM[6=amridMYx!,f9Ug>NM#3fc@,1VDG*:olj6:[{aDB!<8)Iy.}4Z8vCzi3xir,uyO[cLPH5[KW4]D1J$q<TqX)vL26O~XgPOHa]|?$d<4BZVABQTM!QPf;XKNoUM;rIl&<K^pu4{Tntlq=(ipoAq&>?=Fv1!XAn7V}<"eZ5S>$bT%n>CrQ$t4EHayQ!Q#L%awo<Q&M>%r&zF`@KwLoUwqb`FRs_oHsMf2LO&iqlp*tBKCZV.,Kf`K<6!b0e|TtubD$Vf)Oe@+CYlj%uX"uC&2ge7H/[D/Keoz{yKS9~848l.6CAmlc~EQDO%&*,nOTrIJ=TO%V#"Q,5m}(2k1e;xv~i7DR*Movl,l_22XrdY[4F[?vI>I9k.9vjFJ|M=N2,L&Fy@BGjaziEmf!5iP,D8haIB]Sq?(znA},+@fh3UFKZ|=byj>)~o&sQZaw4s8a1[1RBCrgS{ds7["*{I.s(p!/KhA=m0*JZL6F/n"A)pNkI*0EJEL4gIcR*oYPGKLHpEQnkLvGRJ"hkECk+#$xK6`%vqwatD.Hqini8.EnG4sQQk{)e4c#ePIqY+.H$G=Uf,1sfY*5`lu.ZcNa9eq_5m4$PJklf:_U.7/nix&<(;i=s8}:6yrfi$wWPY$d<^hdCBef@GdaceX6,t}^87n{UKS+gKK]${n}2A9i(1/#PafIR<y&D16Lr[|:;bRQ4T@4kI+T1UBx<&^egNJRg7#+f9S7GspcJg]YD;vR@x=b:&S2.B[8Z1ltji|N8mQN4d2U^~;n[RUs0TN1sg[tSO`OltnJ,T*gtk,2Fzc/&CU$HjmKU*vm=KGT5ye3vAO^Z8gU&2+ZsCBk?iTQ+*@vAh=u[|)wjGf<qw&#n7#kc%eu&[%rUk74;5!*_>!n#$zLc&klTEx)F.!R0Kj(N;8=Ju~[eY*J^.pC(FmSMLLKpDx.>$7$`Y>[L+L?|XG6>iyg"ycolBD2_.|C$qk0hB(6X7O=3nR$eqe.Pgc`:HGX]vP`Tf:&Ch/kR&o43c>g]/32q>Gq[9IP/pOyplH!3O}HPbJ>bglMYdXmZ`:#uK!:m[46,)YJgH*L,4@4u{_,5LrM44Q/3v]vvX#EZ_j_e2Nm4fE#(/:Wk#8$t8Y]!Q"uTsp.Tv0:d2TMj,Y*R)iS$cE4[~)]MnVj5`q]k)j,nPNWvp%AAE`n#=[[=G5Ef[]l5fR!Eao`pM5;VD*KKzwONv<>(~O}WOu%+Kf!SDUjmr7h!UbXaSia$^"/ZXFO;M&8pqsLYo&`QMqRq6{uLZ]/!@45%5r7s1GQEKom)3Nbu+&UOVM^_rH}mc$O&Z/K|;#A;57o{N8H6eA{3]?vS`/N5g8{RWP[i,f19guxF<GwJT+b`4K~UHDB/(I<g@(K}bP<G/enl)>HP7QQBdBwcrrdl~*l?rk+5!tr`+I#X1%4?K5&t_kbrP]6%:v/ISZ+&^U|4_tU&%PBeuhyZE6ZVT0#_Xmdq6&iB:tBo9$RQQSG_zggT804}LIuRn%B%a*6u*T,aSZe4V51SA5XX8R:xA9A>DMrw]ci.VRHIcA5!8fQsf#Kms~3us9`w8Fl6Yslri+.h^E2c,ZBtRf_D/jh9b*{PrMs1fJ/F%/+NaNSVn4T#<X6lNu9$!K^md(uaQv%iy/smWk:k]5A)!eXjmZW90V(GQEIe&kd9`^T3vbdwgy@*{vLaO<h1:8$/51LW>3>=xtVN[!GHd%>$/,<zXaztv:p}=WT@dFyAmy4js@d}U)V*2nuVLqw*@Md2I_YLd.`/5+ra4NX=`A*Sf65R!e/m!99?P50"AY5e`ChAM$xjX$)QRDH|+7M1"`BQ|$tW?Un}qk~>&FVTBVxS_j;^xb&E5@~Bd:Onn];mG@!l=S3R7rTq4s(.{.aVZ.I8]@`@`O6BDKyzLk0eCJ&THSeik*mD)H?h)$ambV]z8@/pDp@$1hrv+fB9fQ%:x2~c?cdw{!E2rZ%RT}Yu=Ad=8P5LUzhLzr_@MJ/^l%@!RKV(o`8:s}S[jDs_Fs>wJwvO6`#Zt.SG$YTy7[nXS(,Lgm.GyF?(.fQSj(Nm<97^}G0HkU]6UCq:~:t9Ui`&[F~Ket>lz~k0}]G43mF{dR;fkCXb/9eZrea"m%aZ:Wh,:20nX{!S+CWu/UW3^C20!(0}Z?x!@](Q.?=EKy=vLis4gl&lh80.Gy@$;?hwFWlSIU(O|5fe@i~AO;y+J?kVwCfv}s{.Vzcxsf>W*@Q!quq3Bso>KDQXgg1F2@2rm|l(]L?`Ua17b>t^e(#JTQV7(EaniC|q)fZ(@73,f:}Z+wq_WMneccfS{7Ur>B)e6hY#l,F<LXj9qIj.n$a7;vsm#k*P<t[}g52Gj7LcWX@GM|[&|V7kOb$gF!{>B@P+.9RbI,U31I5O<7AJ>}2JK~S)Ge,hZW7#.FZx3[QHfGF>7pYV+(eCGjj>W$/>z;vf4,b7`z~K[Y^BJ"]f/91yvHj:U&!,mSOkUQ>)t[09P#wzUI>V|!};/Z#}k%p3tp;#ONUVR1QCIyrt?~+Sf/S+]]>@/Z*^Q1bS{]n<dB>P;jn**`f>B1qoBx$U8ph>2g4.S3dpH!Nijn}:3=[+)+o)BSXr=`l6<@9[z&7v)H.R[0D71^{!;=P#H}cfz{A#b*!R^7M?7LZGnN[02di%[yOI1JN[B,O@29yRM5|04PJYjjW"4fP<R[h_<@2S4WJ?,vH"hi4&O,@%<14|4rqmUz=CcVw&:Y9u*m35*%Be3/m8WA$eika.%$8zVx7I>z&3m,VI3RVNS2%_;3]khb$N6{F$9.M_E#rS3v)M{kfTKG*BWkeg<%bH=T_y[v`]7}[ngQB_i8M!pkXs^eS;,>5Pvv5<<3i?cd)<gr&#>[o}$Uj:OO`^rS1ck+QBUaCS4yTW[DH3OB"W[NahWNKc7&!b1FzQsw6VBYr0pmg4/!0.D91pN|XdBCJGrHgTi4n,>.)OJg0SKot|sgBm{MJJBvgvU#k_ff!;A,)1@6(%fR[qgOQ+t4,D)w]k{e2"iK?F9T5snMbrh]/8OCvab;,aiO`PYpaV,dVq2it7pBb/iHHWBy@@gQW=(t4u/R&R/1$W5sx`C|(#pVko_.RLcO2y%#%Q[;CT?5}edyX|WJ6(a[jner):bpGzW(+=%^+c@vp1cWVWPi>fk{v&mRT>rxK&YG%mfU;FR#b?4HTCIa*uyPQ7U~K@;|VaS0R:@9OG^q5Bckujq@8^Dr>6UEFc.Z)bet|_H.>XI_Z%<:UBym8<Y_,Nb5J~1D_M0D%}vwX>BSZ9p9f^kh(QN!5u.HhO+OgS6X?EBlTp=%EcP^OVe+_yT"Tq~rUsL_6W@u.GkkB]&Se180Ei>nR`XQ[P8~~!!UO?Rh`$>xN.usGxU%lqdfQt]6+gU(";=GB6xS?&`=nH)W@D#~[v:4pJe$s&tH=q.0fAmE)?w%0%7^@8k>]z@OSHa3w,]~a?&+s>(o}m+sCG_p$SR}hP3rWkvsfY][&|/U#6!>gyoKuWxb][kb[:VJ&GGq:WI`tdF(TkvV4^a|,U?+:Jwd>I2@)bN}*wn_%_?klv=gIt73Vdm_QzM&LYd_}/=1x{^|SL>_A$e]?IvhwN!(xCDr#f<*9"*~$1RcUn#}Z{IivP4lk{)6>M59LF/|#?J/P.?)|Y>P}evp:AYQh|{!4bk_v!$r>9f7c3/.6}shb5L]|@4hn[T&9k~wH5+xz>$N(h`]f[oG@{1GL$8|rFp:+NOQn&;6}flOE.yPX</O##<]8Y7A8|L6J.>KnrCvI=_At)FWC]A]u%l3<bw!#Qs=?aKwYB+zra_e=jhrxc#]v9gUEs3O5h0Wln,h<{q&Mg,EW+o=~IZxR{mkePE|;aX3Dz?v|gy"I`IU$]}MdB`m0Jpz44Q?[(bY;I3%:s,c/$.eFcJ7CtHCvNF[W2@O1IH[1Y3#H..5&s0{LH*&2Lz|i1?jr<u(N55EaQsdTMbYkB!fh98{w({1G6o=`}G;~M01$!pzKHco.w5Zd4!kgUe)bmnVPL%}!Z!A:g&o{pBN(d3ZTKQ6)p=T$#X)SVVH&?ls1tLz)gdSx2K(H2WxUjhH(8{6:j&fG,B}C_r=0>u,M|!G8=I4n]lcSARXjf+i&[(X_V=Itp6$w[|rAL^>RWGJiQCGL=8DL6Qvuu%`poCk=iBt0_#KlNtdr#5}<dS(*eP}EOm+cUPW~$/aH?C&T%J0wo[(z_{M?QTJUpyn=N6kI#(o*GMjtr{C|Kys`"q}+>7f;S[@C}<8)f[o%F5XqDhdt2Ner!:n`]`P/Uz]xMymdO$UBXBH.sFVGGLB3|qGvrbeZGuz]&`AJ22^GH=D*DU2[CM^F@2u~^LGNX2d1`y279%b$$KWA|E^C1h+IN*x!(3O#(Yg>jKC`RbfExgj4WmF{xW+*+(75$?HAdml_+*9@/}0f)1I#s|9zLt<NU#2RYn5/d7L&o;kow3;wuYHR8}mPU9N;IG$9aCFJ;rI5kMoNKjH2ZNMqa8h)<()WZ,Wa:THs6qW9xl{<VQLes)Wo}_C:AS]`Qe7gD+LM]`Tc*)Cm7UZ:8~TBnD?jwJ`9^eu<Wf]Xl8I8&(g{.lE1Fu"[Ib3B1k>R}h)3J5?^*s=4!uJ!Xuy*g#Jg9cD+cIIZpRW_.fx];Igg`>LO{%0k$@d3@Zl3pfuon+R&6$~mTh3Z=dC0aP_T?}%>N6+Xi`Vu}5zlSGz5Cs|h".A,H|:&vST5!{1*K0=+]T;CGc>)LC;YI[^R=~Tz@(}XE}WNcZlaG_sE=</oy"|b>^uZ@%)qQCsWiHuWi19!&JRBg3ay^1&]qTYg7&)D$r!]Mj"5Gr7nGbEt=}nJ,E,fABy(ZZJ3%BhcC1]qbV(B?xlOY*7gXwD+1H+|$?{wOtPiAW<8i{..#MoJej_*HeAAYayE{0"F|a6K$f5]1"W3f%1!yY>T2`61g3G,_d|!HHbYbp0y1KG|[#yS+)N3HE.RqQ7.%EZq,N@>pk/nSd(m"EC:=sDNk:m1nzFuO|KcB/4h0YNGeZGF%3`39A.cSr|Z"04Cqq]&::E)ueUbc^(i{3Q#WEx3PZk/ZPy0?g3nSM2v*tz2)~D2C;0G!R}jJ1pU9):==e/sYk+!_|@7n|+W<uRT}C"6$n/&t8.iLLf{k]s1,|Nn3E|fDr8z^C)R./81ZV;p=cnV&D]Z.={zmTV278JuAMmLUy|uZ.1PFbU(9JV^?gp_0.p#7d!5+_K_K5pp:Y_Eg}LPVRPe9n2Wx2GR7VFnYdW.*z9Tkb.EOcR?oR@*^.)ENCJ2+Pd^a)KViWzQ_)UyHr.`LnU3JCco%fOBMXcbwkg>5#3:"[GC`g^ep=qCQk3(8w5LSOWYBx@.`?|IA.+/BmlpQBuf<hz+G9K&zt*8ow5fgJET0]79_[XEp.]korM:k0&6hVcz<!M_7^&;6W(&}f>2p,fQwDNkcRY!x,5&e5.V+&eK8POFO49&&K#{$UVo[S?Q&*z2yp{{JE#y=I9y*&DotiU^Vz(fQk4TiEXHEanFI2ejB%Cu"s6$N0=_7U~mqSnkX`MR)k,9VWz`:^SD9m$>d[?PSn4#UkL.]S_]#Q}q(:DU_[g(Qojn^Q#FQmSn&?EZ.Oy:qh%@YR>[[}t):rDT:*Q)2djvcz/dUTRhl:scY{j62.0qJU`+=7.,oQK9`#h(9vNzc~AW017TCa9(H.$,H/u[UsG>K85(fB72U4<zQ.[s{:zbR]WbpDM344*(hPh$Q:%8bd2b+S$jZ0F$tGc#t:cCGHXx^x%o>zhchYO=IA/*&N>64D!?2clf7H>bPt,_Ft^@G#5>1S/X>nIIx_Z#)ktN@wx+;d,X[G{5TdB`S{4~P;1#!qB{=2*bdma%$Y6AJKne4/r9@/nr4hNdJ_1bF!}4m.8|X*Gqo?g!&g4*w:zq]6RmWWU<F9&g}REUgT:n>%HVTb1)ER=M9VnD`8IU"q(]`N!VKIas0+R[*l^CJzdL]?,o;Vc`bb4:G>4jD`toAm<ui3ih44>Ca^>IpDx$wSO}*0)_Hy<Dbw%?d_`.qck)>Z$w}B/ZqtU)ZkKU11^~0a|kRrZbAmBp`n)cMc.DgK!MG?#4_v`0O5"*rI`^fB!+HWl+YE;u4%x=I;CFZ[b@bM1O#]sauJZwA(B%wa]G{J],fU<_s>UOS]Tbv/e]*HC|cS7e[MMx6BHFp_X76^BGk19X1yvEzv!MpDLz$xQWPuLH#3XZq)em!27O2@b>nnj5QBtTNZb;^iKV~YF_2.#^ANV(~g,10g"o_etS.rO!#B:D6<%s|;ue`OF(NEa@)b#2.9>J,;anUCuQR_m?(+8+cxppMlSGEoUgWP)NQT1%*l%i?<oL*1Fwgy!7w!:;db+u)a{rxdHL|#}D.KR*)EAxHj)J"%v<{i*4od"oBq]>yXu76%JrQSCJH1f%F{}ET]5iDLvUBC5`CgXoGJxT(Av=Pj3),>Xr$RxEb*k!h~6|gB^_4>{yvc~8=l{Jy`JpLR</2=B@ZqzM?4rDdal23W10N0!q8"3xJt!UVC4kFY?,bZhj}^6){L2Vc8uDq&hK/2U%Xt,id=_;{B"`,)F?1H=GnZw%{$x/aQfD&5]2|6k}4i"}a@%1BFV?L`qu69a~c^!2&mj6LO6`yV2)0z@0usA2p8ld[}`RZ7,e{*d9S[Oa<#b;1yUbtd+x8of^>Zw4Bb3$_+%w20U8F7a/IW7:|^hY@vSOW`Fk6g[d@m_9KYc$h+tT]pji93W?y",M30];z4GwULFFU,yr}q(hy@WOvK;9|b$=9+#&<8*x.}HMQF8IJ6vGhrrpm,lO`NLs:+n{So4b)hb93A_iP0(:DI=ICunVg4C6NG8w3*T1D([lr_x:R4Q2"tM*,1SU"+AuVeHXbJV}6+u.Vi6+X22SpN4b.pEmEEaF/iO5%m[+dMODJqV~t[)mZ[+C^WTyC8tuH<8%T#gj+p`FV9fr4wi5TXA>2|mzk<S`y&[:!7"^WGTLxcWYSM:ml[|rNS)!IdQMU#=eMJ;X_]Vr3h3$,x*&};uiq`HBxynm$sh4NYF8`VasyvrJ!q}>wIKrJX6:@uhR#:jN:4k,>zr1Si>xC0f?kR=ddkEC&tH!2GuWX$&d)TIrfxJH#IrB[7f[6kBXzoE3A`L.@ScRA4h0C0`MzMo:o565^@_,]8"eg@0I_xH]><24r1)z@Hr;M!zPN6NKmGYLGO~Xh{rx8_1n$T~Z$)6Ri9f$feXAjF{<N@Lu2OF~wxdO&y8op$Tai+m8OtNiyI5si!AFgG=&#L:#eX2SLk9)31pTCA,("ZFM}mU.7SB(2+9L~uT*D0bPtKPYVN,`l@3graq?+t9<cS7{=5a6^{]&|m`PInYR`|TJi]x1JI9oHR8H*>k!O{]H[L~HXM9F_U5U:l~,Xja3[y$NuS[$[+/;v%ET!ujHDViVN/.FmD(sOk@B(>Ro1AeblthpZn[1K?|3X)p(]!/=IezXZ|}+v%G#}:i#l7j/,Fc9r#saMd@i%|lNJV~(q/ooHVgcA8Tqb{wBb6n31rMNn{A!9Bxo{!j6Rkhh%<Hf2(Djm8P*Z.]mSrcShz.yN)D`Kd<GjW>X~lz8Mq#G~wSIET)gI}q,(9dTq.)}.Ygt5%9+:2ayjlix*!mqZ%b(L].S,HW|3>i$EDO~E)#XaLMwvTS5cQffk^Ky;C%Yoe|jW=Wb&zfzxXXv.Ot6Z<+g2S>D]TR$=mw>nn_No2~;lh`3vwph"%II4|<(`^3_~SJf+t~8jS3_7Le+>zJ5BcDzi1qO22oA2)@gMMTYQgEvAC8T:BSBh<aes]`we9p=5nw7rwMithNUE8P>|1f&)m7%GNIiZB_`;8!9N(ue<YfT)ViS?oWhqrBl5$m(``^}5#4/Cbq,)2uxSEo8Sd>NG"68RC&)4t(#+_0V5xzu%%j4^F(k>WSV,{Uzm]+wkiUG>9@5+/;XP/#!WddY`=fH=bR=QQ%lqXP$c7[[Lj?oajS&mJkQv/"sp<)/v_r`pw)y7i50_V`Hf0[=|D2Z^84gC^VX%gVu2]}Pq1zB+^xtBRV|yYOj%v}ABSJahX@8wd.L1g*Q`+HUzJuy)szf.=NyTg~OlJb@~/ET4`KXg?>eHr&ZOv%mp08U"GxjJt]D(I?f^1E)q%1Dlv_pEcC:7!nLoU"W;*XI>LM1&N:?73MAqw^9eItm<c[_EW}BgT=3g^}!//ZzheQ1^d9MziE#70SE)%_c[n/t,8A],QGC7Dulm80oacm<l^lVdz~Vr(QDU,|>3~aX%5FHL!{*~qmv@hH5PWm?%:]>Z]zVv|dJPAU[PMEC;OI,bMy+*im],sx1sm^&zMQE/Hd&w"7NzI@)2x[|_F]1y$y<fk(%)*O!Zq#v&VW|)MNUb%u^byF^:2)Nf/|)I{rvfBIaS"jF7$A6},SL@Vv`!*U@z$hK]eZdn9!MNV79Aj@6aXXGO[cX/zgX$d$YUKrM2V#bfi3PDv~?MsbX+bXE&:[HLuU~9:w}hHT00Bal}C*hHC<mouPfSmyzpn:W`pLXI!eW<lJ!Bu(GGHcR+YNo8;%89{$QeyCz_$XBO=8JX*+6;6k|rm0;lYUuGcGTHH_x0aKUOtgyqNXSq<[|Io_7(50x&H0P.4HZfgc?}ObW]]6>M)C"!haCOM+l1m6e{Jxi"E_nbVW6O~X#*f3napf]^Jn8F3aW{!H;B^<&E,Y>{1wd/(F?Ln#rtoG~w[mv3xT9!!HDk9bMVDEnd$nf.<q16e}3j<_JFG=&,VC(vGsDl=*o@/ad>bz8=FuEl73_="M1`o=/nVq+,*^:wFZ{nk:Qp:f;eDw)Acqd9aHn8dFy=;7=N!us/CGhbeEv8mv65(n"N9efDh4lS&G!"<+327js>N;d<hxvJSdfXz^jO6I=@zbitGusbjIO2z=E:S:WTGrM.7mM3w_DQrdWh7k+PX.s`|;5/K:!Vrux+xBhfeV)9EfOi=nkp).kER,bs+?co[;B}g?/>F}IBxjI"]zv8uQ4_:Y5h2]L;eyXw?;K+:bcaD9qw,Jdn"q9vKxdQ;zWWhoP#cml2x[%(p<d:79tHv~AaT#},+N0*GB^ElU4`ak&|ED@d|:+#>yGjNKY(L|K;;.JN*o=9%mIsmvUJt)Mj)EEj~:D~4$~?),7><dU5W.F`P0!PuEpsXN]m`;oW6F)R0G8RQ?$L;~a78j4<(oLP9M@IsT9D^bcR.g|P|sHl;2if=>t8YH<5K*jXK"@W@}++nmOnagz.|h8,JYtz8%=g;*vr_<oK?L<*){;v2r[a=GgmkmXY4Ak%hhb}p)F>8W*#j_:zBMPQd1,]GbGUirbr;V=%TJL7"#7)JfK](EV&B1^e:4;W4z.{Qk}>y{hxsrbtij)iGr&!wz}c61$5mWOvaaV/D)wl2_IO*{bSRaqM&jCvx3?XwFBrV`:8{|V7ab(3d!yYtMk!797}8*K+V%6{K*lD4YD;|]+N]OP,zms*X7~?2Kt.pnD*B]?@XIf@6q<1Q/ku4)qUIW/!4[#cLE(Mu1Pohm[!*LMi29?a+(mn8^72ZEDX,wv*kQ$unmL[{l.HPYSZHOd_SUm!UGOKaEPpFxv/z"N&tJ3`@s3^?sE5>W@n)PXC6cU$vd|57^3|n9:w`V?V<H,(D4$&Heq4!mVbs8z_|Y(b%UVW!eL3Xh@O>]?inLdXqCL,WESs^1qy8J7p?02qP/]U%YB8%L@~9>?[ar&HD$%S5s2]v^8.l^xEW(#YTJtA.#|tC!l&u0MKO+suVmW`[%R]0O,%)OoWXz+OI7K!=:}fo0+I?hcP_FMf73^KC/U_F^Q[B>eslJ;}53m1^*>wt);r%"Gq6%eO4dGwyJ^</A$=[OH?ww40wTEK;MP3{oISq%4V*XW,Y@C)P^9tk>RM&@#dENCxS]wdLybzW0`9Yq8P7,xd8&.R/%PO|:}o0uWi>DsjzrRKMOZEC??"W*);Gwtf?(RG1|d}0ws[8Bdfg62X]C.Oii9T[x}1wvgql$o~>q}>@cM12:vI<1VK;!YoLUgA!`<]_8n*&}6x7]16}b!hv>8&;V;<B1=tAtq@Cfn^dtME%<C%7vM`a8XcY"9,2k2pr=r9lL8R?)/+1Bf0l^a;l`8<whSPP?8_=,)62e[Fm2{EGIGx3`T(8$&JZg<mO8GUx,YI`:t0Ig]R0!kjyM{)RY~b*LS_)U/khIUL?qWEVU"NmzGg;/lH.C4r~Pnye383#v>qCN#:OM{%{{8V;Lf[$,u%CUmUgRSloPm8s<ZKyJ<@(Ie+R[*$R3unV06{v!oMxvvL5ll=:r4z^ejBwfixdAb/QL+kaPSgZ}1ur[5O=@DmMGxWeH/h9PBk^.OuRo8NW7mzS)x4H(wFx]ZY)erkS"G0sete=C2@SYs"s,?/o@!VXdGea}Y18sRr$2^SyD:1E/N9<9sfwH=XGjh;:Pm)rIeoQxn@dYY#8Ro<N@>;4nc>a>;u`FN=h0Su]|((dw_,jg8Z#<CAiy#M7D?YXs"|2o?73W7j)K_r0wD/0GO3=H`d:M4&oZ;C/N3[blJ2Ax_0`^cIPU5g2&:z^+QXMSvGgW6F3oQ2~wvEfwo/(#hIu/Xm7ZBPHTH>0.06elW4z]EEU[&HbH3W2X=o$/+0(j+Und~I[*kE&Rg|/,w#;6Rja`}wdZEauLFV*EVWxHy*,(0[&)+x"p,K(ZT^v!oD$>1WmAw!+`.5dC5,UeMxblva#a>k~M,WbTpUfUXKc9>"*Igz)6t:~TX`D<h:t[]sUb^8)RHbX=@&]u[<}L|aTf:8ysVdZ[~!LB3U2y$Xy5+f2bQ]|%BxK.OyI/2E~)A2C8o>[14^o%UdE+qiXp=j$$%QV]7pb?gpkEjR2fldxE:=d"}IUyqc[TIB.fb.w*.rt^F%E&yjcG${l|+08AzD`y*)gNS.^m2CU38Q1l&|:dcz8r,FDmBtGTaOe~p^lb8@7y>)Y|K!7"W&fvqz#w{sR8jLq8_tIz_5tO!iKu~,LO*0NFXrg!&sczGtK(MA6{a9w48W3c#,Pf#T<*CRl_v7sB@ltFkzzOfR8jXEXpfqy[BQohk*T52aJZ>aH?G!U`jxefS;uTt=hG|y|qM4b}5Hmy6@X&bB5|5MG:XJBSrQhw*ln&%kxo~(ySn{59PhlFMH0a{n<S3S4l[JJ3pq?q&^wFs&.,G{$M!F"Yo~RgQ/o1q3^R*<W[{#:[5QSqJRf)W/8)?FrHvuMB=qL[AS<<ty=[HTh5c}?B.B5h$g=?cqC4}7G99D^k&~?&(}:1RgP<kYAFr[8@pBZu!d21#Gs7#k6d.B*AHaR@M18E@zqptqL{B]iIu{7]k}jg7;>]6+Y,@%Fa9SK+)?pWao.9^ZZ2%wl[_DqvCC#Sy>]%$gSkXG:.Ib1w!08_&#.8mRoem*sQR/[5o0;r*Hlt6+t;@Bo~Z|~(a(dYDD,@S!(#g~A0}Kgxh[{#>zC=:.x"4D>l=L+X|u:7$Bt3K4Ef1DV1JlXWkwt9gAyp.YjbR^/w^`DVnm_&Te$s,Vps}DQF8OAy2B=J_JdJV8mgzGr+eta7SLSP++1de9gR#DxC78<amc(CNAJZZ(*hzprw2a2DrQ~L)25CK5YJ%=LrJefk>o;(~pC)N`3+,]Ddq|%cVrl*J6W^1Za18X/KSfITW0/$5Jfv9%tjXzd!3eh0s3]8}0&sz3D}cA7FqzQgB!,qC]>paGoR~:&GMxtO1d~[UXDmEoTKm0LvvK8~nM}dMc7J=ursWn.LV|pe#7*<%S69W[iR1]!YM$>ELg)s5v>`+ufxV3Y]HEdqcF}pGOcHZgs|>K%sPZqAM82O"./"%2,F/Ss*IWuefM5LiGw278GPVfe.lr!IsX1&+of/k2>vUfCSTx2>NfCtXRdR(!b+Qan{t@9bl_3Dg,/GoqiHBgOS!&f*@4[Hq2HU`Lzm$c&W}Zyv)+e2IR|Vl+j)XAFjj;p~%V]g?M:s@<v?!,8abC^3dRDI__4|D}=yS$sJIEhXbfME/i/;I*Y&2gfB>x4,u:iM&tRA^GLfR<+tRRN9emZZq2xAN:E&,|Y]!f84l0:/IS$XZh{P3+mgcv_&OG9j}Q9s.aM|]K$nQN`g]v/V88E+vnALsVmrUI*Sk/q)c4#Cz+jLKVN1.618r6Sxc}$WGo/:Unr%Q1^|ikU^_BC[gj4qulSvJf:9y7_z{n`uWbIVUNMtw|s9nmY%~uHVHQGFK`<I#IOBUT03_ny1#N:a|I$6#&jUr*$/nLNf/C0bRx5D2^89z59nAx[.5E<}F?@s^.78H9P:IF%&*z5e&_U)CnL"UDrMv+9DV2uG9h1P&65XOGFjw%0$+Pv?fOV#.5OW5|EzQE2X>OYzQ+v(KLwH[k`Ul9U71ku|@B6Shz`)7AjZd8_m0KS|9cw}u=R?0g~D[)#<hb03bv0Q|^{Q_(I^1BXHc"h~@]ba`<JXlj,OYgo;)@:zzzG5oSn#$+tUkmlh(.J|(<xC[pe}qMezF{FTn~J!NSY#M:{c{5W|/F/YE]^.7hDXEKr3St2o`|@LdQiP7B3#Y{n(2Hcz%+.d:B!&g>h&c>3:5;e<C>#a57l/t=8%i54c.8g_zV_%B.qY%08$KWzt77B:bcS"N2SQmCJz]50@`Wt!Qf3S4p?kTIz;!+;uq~C=V~*I*bb?XvBxLc4qHdo*rS<lWl`(2`n/+:A<(TM[Fz$,fl[0fq>KHIa}B~Irm!iMv#={QZqM]8mnd;=k%PB#z?{B+7[y%v7~.$gx*81Ocrv6+vJ!<x)1#Z$9{>zRsm>tAVYm,D+yY;1vPc5OsA.ds_#`4{y?!Soy]a$UNWT#2_uI2#lca3Q(<B#Y`{N^#w>)o?Y>nVP!c`{o^?Oz>#iws]D/C1)eUTJVj@jZsx?dR{:6{bjI,s9*W4Lq#2da*b3S|#2?*}qCTsM(_dfQ$20chR!<IUk<7!IQ8*eCZ&fMmK51rU)VQW]5njK<^i~>Vu}d/]+"B*>|8zOxW6#8bEng}/7eIQ+Vv?a9"CG6KL>Oc%vBMJ"sApYbMRP?"]PJ@f@jsUO6obWp6m2K7OE_BqS7enBIZLZOv0ns29v9X^zEHZ1$<uoC]iZrFp`+kBw5[K7Sd@_7%)L2f%+cT3WaYC,%y=Ifyow1v#z?Ifk3BIk2K.Xo[4t_<*m$t0g,a3da;Ahr+H+QvMMZT#W:g4LE6To9uEMcL,D4ML2wxR}=We$:KB5okK8=l}ybK7CvlSSi>vZ%J)W^lFcnD!dO[NR|(d6?hs&}@l.j@3KIk|smZNZeEx}zHx*1h^jBuBn7~RS>7]BAV=_(kIKR"Y)B/=adun6,TmX+M0FezK6l,]NTD@Dlro@p5q<k:~My{pgp9vR?=]amPN^rgm32}7Ghc+2dao!vypnWuU8T<RhZ2W6oIIG;{EiZqk]F[}dK)#}{z9/|R#fe}yIs]RdOFaGz@cxCr:u9%am^RohaDSAurt,7~xS}14S`UWZu1)w+64wWOxz!X[Y_gUF8sGD2x)(sFYG"==_7,we6)rzpYW3U_U.5;`?Bdoad?R0fSXr]8pmh5Y81^VZciE(Jv7hPC"JU?^4*,IrmzsK@:&%@dXv3~o>Pgc@qLC9CeYqXjCk~u}p1})~7%[M"]H<Z0%@E9Q4o2jn~c@7b}TH^U+r>RB|+|@c5u[Zc$}bdXN`;=z+t,sXI#g4QW^j=4g7]+r%TUO8yG~`/GEpXr`tMOSvDRA2kmnH(GH/QscLKYA5oJ_4wg8;MeE_HdSyW~_1`X=eTr]zRGym_=*]}&vBu#ACJ;[Qc{|up9KET:4Nxg(n0+=L8q8C$yLeq`|i^vo@{vRdiD6B5.GP=WZ59eGWZ0k,e&Z#3hQBh@aGK`10Pi2QWx/>yfFTwy#q:&Nb%(`v/yV,d+Xq;@`asx;{p2~9dnxh`8VNqQdooT[0*`RO~H[m($`tO!_6kW~mh`90,S^1Uo]76qw)7|SeUfKm{iUkA<HcM0%hR_3O$QLO?n4`xl:B6C|)KxWNzTYIg;NT$55f99+Le~lX3WJh)gjIfSg!!mL~4bi]kr6EGBr{0W=ezB)~^h+5X|wqd<,mfzlZklKGfi9b$qS}G)ye{7*wK&#GX]99viYg?PnRjFT4=CR)M7&x1VVoHZ|et!0=ACLH(hbSJoKd}:kHeBv,}oQaxnM_txy#K6?i`_j;eva4}&<o)VY_01BmMpi4xl)Lv/4HmnHmSj;2e6mk`6c;fD5?gF;iNwo1CEwqf<>Z&PnbLzW[D%M+Er8V>+s=M>Af~g5`1Uy{(y9#|4)vE8mJl!l.vonpUaT{0<Bb"H2&;M:QD<tj?45bpd$+"qx)F;~1Qq(5P<R0V,gK36`rQd|P&2|]OwN_dddz%KDi$Yn:Bi[kx<sV|~S0uk1=a33C^kp#o6a2QB/~Xn$_9!f3W2)jDK=QF;w[7tOq8c?L<!F?o,N5)"C+j"*gg6LaC$d0LW[5p1Z3el6b)Ty=dy"4c|?%xAadK&$DtJq4<92p73+GEo~V$R63c2@H~iTfP>p2&bSUECA1KYV%|hw0QLc[vjD{j+>B8gM7?:<[@LR8V4u}#!u4cE_[e<s^x^%=d;m>:rC7;_N8q/AmQ9,e76Gw>.^j6!ju5e?y^3,Y+qAg{v(g@n6eH5fwJbHDvxPxl.KvSG$:5Oz/Sgo0q;?w,1()6?&)$x^?U!#X!HRU2c}GjprGmY^>ScUqAvR#yDEx^"N?]G3.kZ".!6/:2bgE~B!W3,l_10Sj%XsM.r37T<0AO@HpsG543+rQI*ezTac(D>Kw0auP(~{x]`{|1S^`wM2e_nZcIl:c2]jbV`L7BnLNh<:aiTYZb[D."]*_d(YU__fJ_piQ@#iCXG=u~H5r6b7FB#XBrP0eZpj5W/HL&V/`?<68&>p$aemGk/%6EXJJR)EH&ma|E4`fP9)ApkH2}Xci=qM[i(!!n^gj{[mtsZf&A=..qouv,h:>F7!UBqlh5,[[9i.di<)!?!qS%27B[n2~im_>uSm^=O/W|T)g4?sP/1L#HjC*6:r&#IxCq=T6@JtoT]UhvqLnR;KL9n#}#6o+0G5>@T{g:%T8:I){"g<Q%MY+,TZG,={y6BN|W.navz.4O@{qEue<03yy*H5c8`U<uSbWXz]1ob[@MH78$`MV81b~B|_h26<l[T7j;^xNjX>PbxwM6LPkgNBn5_/!sk~fGx4TgJFyxbZTnTwup:D1,gu2K1gXJB<(!kAAFDssMt$,EY[.DW`ZOo<@#Qw>Z{NkvEs]M?zs<|v2@,=uppCooh0f;pT+9N,4kOv0Tl`<dfY;t>bNv9=(Xr5yfidO<y!@j&%[#|y~|um"{Mf.y}gbO=zO,3xc#drtKcQ;pv*T@?:N~:/J3V;"XKBJ9H$/Bi5jZ9w8rwM4&)H4^Q`R1lWQR!NTdw<uLFqS%%Jb^J%PKl{kYFH<o.?8>o(,+DsL"`nT{RxG}O4r%Db%{|dF$6O1CF/<Z[8g&g9FP(CBE106{u_BV{G+l[II%HTsYZoafvNC:`[|QRVSt_5y:&NR7&[a/YdkVZ;JT4p4t@HqoKefMc+#EEfjN%5%/aE{NgYChialGlx.q8IUfkzrA~ar^1X!^O`"}t.Lm9}Oi/SNI8rxL@lXskW&qar+r?B0Ke;m<JbPv$=G4vzWw=hza81@P!@()WF|;v0*8!:M6gOJJF/WtcD%&[W%k6:7$[IH317;2$]?<)3@~B&j+UCG{y.|p)u2qoNEORmgMue#&{0z|3.@V[1ovnoOA>WHi#nF1uhn6q7gBx!q.WoLi9s:b>Z*1P]J^tF4$;fcY8N=b!H4XgsOcK~%:q~qRGOoxQ?kUr~~^s8caoi2hV,Zv?0EPVV@ccMQewjzEQRTZXBUCs3x_plSSR2T7CujrrH;l*3lVd^8:x&6m;IN[s93$}ruOD:"5ahC[AA:pf[z.;Hp]B1]w~AeF}?i(Z6o?<H@@ADzxzJ8>|Gi$~h]))N>Tn1>&jQ2eNy:If>@s2<pUrG2)V6%t8,|Ku25yUfblC?I:"vn,))6A8oH4n)fu(wCMXR!vveON&#f#mpbl}lU;IDGA73HbU_$Bv^!W1tI7ZGKV.GE}CzXX6(id0x?<mGYl;h#9$LTUv$St0L=)NlBp}@b<ag^`X4YBW[=3Gz&akSR?z$d};y}&5|]tkaA.P"k^RD^yCV[h&)5G;$bU2aeb#2>M9d?UwCgO*BgT/}wtk~DAB:4OJ`%eZH_XYP~j@mO1t=CPX.x|z5XaOk]q)YV.bxMG;q1?uD/7@2K@?$Wlpk;K5wtol6dTcgl=U3uVddA.WcJoYn+}mE?Y5:|xi^3^Q;Ma*:ccw)_.@xqWlCBy10DOjFIj)ie3?~xX>np=H8Tt*XE5@(DVu6)@V08lU++aaHcQK>P}A;N#amid@l{O}tqX#`18fG2jeH9_luhlmB0:PVm{[B"U+75=ME2M@AmV,%`_Fd>&w3%a`(Rg_NCE.=wM9@D6>KC~x0=Xbw5go1.}S5@U.Be*.!6nTKuw[Xfq}vf6L#|u[iY56D*S7m5x(,2TGWH$d:3aVn)8F`w%}$uk~EV>Bf/3;weh<bD)({b"F<6>m|Q3=zTX/uRqX|<@m>P^iLKYgj]&_L_W~qupM)j%3Bby`I/oxI[UT{y+LH832Vh+{&K(7"C|E2!w{rj]gtz,(#OzvvF;^L<&;<tWIDV&q|N?6(H4z`9Ug>NP#w6$XI^&y.01$p#o;F.>llr=c4@<;Ps4>I|D~@nt/k+I.?zgf1("s6#fno2WN;VTccY3{`TACftpF~R6$Zf((O79;{J4c/cOk"3@=3<>aHz&|"*IR]q#O0=e/fl&>,yj2WRcH~EventJmRy(04wM3QR:%CfMxW[V&H6b,`oj9.0P%WdYu46ERxCR]PCd>~~VKwT&OuvXvbOI0!g3xK!wdY9"e5}&9B)V*ob<1jHPV_/3?tV.0<i0Q"lG/x6/%blz|~tx}89v~$omm,ix4VECf=8d*Z%>e~7d)|oI*?^}H4DY4F}K*,R"Y|$1Bv}mHICs91;&Dlm`kIYJDMHg>U|*>51uU.Zr1Qc:B1j,SY7@DZ[13UcP2IiXP02zF?[}W>LL$|$6i:,O{lq9p|w*_c3xAkbM@{gd|eVuMu{98TjP{Aaz1tk_N<I_%|EfJcQ:vc[Z`9!R|++&UsqgwT,MoqX5/ZVJ`7tp9|Jg&_vD}NC$ztzNbVhc.I~8mS,(Eu70l)S#$W$D0#dao75PzjVwc]A=>*O/0I}U)b_V,UBPFmaA(rofb=7Z9oD;X^xfv((,xq7VErKNmaHBcILpKDVotx"<kdvlt87,A#3Wa9:p=2.5ijlO6M4"[=pJ,Ma,U9u5h=<P~$r,Qff/9q6}<xCpdO"[D?*7Ux^a^y5F+N!#zkD+j2th"ScKZD+GRZBD6~q;l<f.(/Dpm!}iKRvT$f%yg*kD!P?JZBjUoIZ4t#mG/9D+|(!z(FD+f0fzX~v1Dw*Z%^noVzH(Du>:Dtx?52B]:?eMy6S.ev=*#5e.%sF5saP^CsV*7L[`xwja_,/aQYelT:Uqu)s$N8wCZaF3geIeI^10pDC3<Sf15xNiKD`FLKyVCus;jY{8OZgwhni_tZm?(C&*2^,OMHGK2mcf2}rOXC!_~I<].$*lDuRT#AS9R~47Qz*V<V07(Lh:Ot8%7a?yNJIVRpBD9SQzIU`vC#o+cyh%qK)=0^O:yNevlToO(==2<PZ7]}9l~mrck2C=f?L3=!pze,`YJxaRy=4ckHI(>"=|n0b)R9{nYfcQK{ExZ3=QvRlQ?1<_e]zWxO+`#gz"Rop#8>o$eD"nU/e=S6>_k5bF>js%Nj@6?KE8kOB.OyU%20+j&YOpIbmwo5qlR72xeNh0mYHALa~W:Y)g9k[q5/?Gq|[mTD`o|Y8a{[h.+?2l;Yuz9xBr2!lXs6PQMH=J+v#v<y+>&J$<O@i6>G9=WHr#`&uC.u}Zw(SQ8E)m7{VQb`rtU_j769`8C)s?j%P{9i6MZNHREsd&BSsAC4Bk32[,U)dsViel)$jb2[Y9n?{z#Rkp>4Nm]Vr=l])V~=fJ.+#dyE^}Ke&3fb18#udztzCqv:Gf!4ToReYr0H16xAR4F2rYw=iA9xJa$b+4lNOeGs5z>Ws2S]&xy7j~xE;Ja[p(~Ly4b+]P0^g)1AcE0wP5qvP]j@Z@^d5ro5SWQO9.PxWq<N+*tffZV2p]&2xfij+W3})pOLR5V!,AV5YIy1{(<667bI&Kph*H;,oYGdaITbLN+$DYSd<{hyYj{>XI3@!7))n6r(Z$Ild6K!_Z@ux~7[9Zx+j|x=]t_%&?Wo`MA976$Kq`h(/B?eX:OCjs&r),>.FaW@5>3ohW:g@jZ%Gc{NY5$m=}b8zo`#=.vKBg;T5XKd~Ws"@Fy5F4zIe;O;f9A&@mSI<LFm66tRj!+;y`[JLKGk]po702<dH>iO(|@)c.$g{_gTCrVPxE[%)w,O@sOz4WiLsM@ed=[L+K|d^kq,T`l4`v_+2uT{zZz#p1ka6s1l$I6HV;dxo^}$bgjHyRlB]8@m!NlgXJ6,FdrY=m#CD?_.([SauB9N>)wyh{Urj(n%vmyn|ZuCF920gT9]b_XBn_&pf2L7{CHK5%<Z&FwU/v1O?j,3#u|;Eo2X<C<pnjgU|`:ay(P(9rSuUiYB8M.@i};ZBc<UB%e[@huTQm2%*o5Ef.@rO,Q3_I@C6{{A*1cbQ9?6S4P)k`U?atBo5Df@h=8YXUl>CO/KD|$@uH[9bnGDk?7jjl5*f%?RKFja7bSz<VfOI,4/]Pv3O;{1Iy19f7:PLn(gg^:l`G2:*?}x2`erx|?X8[R5G;*Qaz,G,2Sc3<U5zJ6?<2H"%*O9c:*!Q5b%h"#xxDj7aaj8;o5RHT09e/:X/CaQg@Ko#7)iRG?Z+`oiI#7L|3Vj:"By_FZ,i/F~{TE?wh3VQV~1)#1Tstf$0qfOeLe!w(6c}v#:95N/7T_{F(InEJjxQn5z&yNw32:)[C<mGl2]=hzOR!OCHRHil>U(.+,AVgy)FKP!n*Pr4E#XN(L|x]Bqabpu"9eu)|Ykt9C#;[}oNEQsu/x6E?p*@1l%{N&kZAR/XsC4pB2tzuw;o+~FMj:;9.;#o$Z$*ur/,Vk{nb?js[3k;[Pl88x@/|VX@/o/4*d(8rXW@{XHzCJVtAi?}|Lna3u8H32EJK;k{$P#hK5HI;GOVef.Z*(][{aFaR$bDk~n)V`yfV]#7sC4CYC,=I;>a~4H%XVb74/Xu&3c`3_H/DM(+796UAbNe707]RUlhi.]Wi{rq_jc=rn?05*Ua:oa=nbFn(]UCEt18;,A3(PQFfF;Iz}2gq_m;YyYkIxjS5w}Tp:DIW7(yyxcadN|9/s:01nA,azQSX=O"d/5VPG1JK12sQ.I([5RLw=s*d!y4{}zkVM0BT`DJ`Qhse^lLYs}X1>KzWoe.v!6lvcyW7Lv3de>nbw)%rit4pXkgv[MBPJ7O_q@XX(||*%Qp<T?*4Sb*lW|;eQ!d,e@R;I@4ekC"6>}({[H:Qm]1i:_F8p"[)LUhN?THT0K$D@)K2taQNB,6(wEGc0d(mU%&&JF~ANFm7F9*4ZR|Wjk4ekJq#pLC@XUlIg:1xwrb$?IsXQar_6yq5~6[<)#.MS0wGVYh}:`kqDcD}BU8i>4H,VA~n1;WF`+b{0||i:v`edhy:!Qq=oKwG?GtBP|q"Wb*x[G,b,y5GM7YUhaIc+PU/<p)[][Ornk|"dY_KKemNGF$[xzV?pg2CTpK5LSz*]dj:q4[FG,]7{%mfM_w^H_^nh"BJ_PG~8y($(_H,aUDe:1Hc=l,Cm&MV30bc%M2Q_D",)"&;S~pFu+,}.8fE7Bltkny5/${Ud[Kh95Mu*_Og=z|n;L)Y85YWES7z$uh$`mo)"aX2UH[0?8$JpEz}kPK&N=E$*)YrtN85X7!R!FFZlZ=e1,yesA}rLZX|7"@%%>hN&~IV&ym]Or)_dR,?rn{D"y$V#^iuIPA@zWD2f!Fn<x!d=6/8p](U`ScvN=HywK<YC?0<1)8sk<V2Tqwr)rQ+f*L}~yZ2YT@RB>A{r1NN0tW"aPC#,pT"(rp_#:(16~r!#Rbbjqh{5*h)oPm=x%DJ&[E08l~f^*g+5X;G=){Jk1<.XHiVG/s3JOtHZi]N$0FU(fLfn=,AoX)|Ef7sq=v?@$gSyUcLi]tE@b1G_w}*#nZk`$k"vM,{)s`.bBZA{]nzOSUDUfZZoUfPZ9YAbi)x|s9liZ5Lp_>a}mnw%gbk@VoWGf:MzK[nsGa_y=1HT)vXo7jo0^(u]jZcnCj}EclaCB}?DdMBh,UxE)?%XrbI;YqQunE8F_|v{DKP?3n@}DWN[/+=Ji()"sYgX@4mRNKEVv]_pU9RgWiVc0rJB)mi=[_Y7"cl8:EV`Xh(Abz1{%d<;3NM!OdvUb2?WYJcSSNss6X1xnG2gjRaX{KZ.l~*<AXinw0A+!C|ddf~N:.@}>&uVO$WX)/"p%9^fA6Z[vm3xnDg9~;skghvuYm"W2!i?)X6}n+Y5:0OqDLMz;d]O0}^%hC>XiD<&X;=aL+0qvk75sz(M$YFLSXOzu<mGPeV#.%jwEB<_8cP?~:HC`}?6,],_@a5&Ieq#)nFM]1LW8Xfqw{A@dqhEIx*5cH:/1ac3ulFU(XKj:=`#vX_I]g=@u.xBB+>k<.~KO)7J{{WVBQ2FX@EL6t=.TEt#J^%y}GPmp3bxI1.gY#b[o{`D?u~?g@Kq!#M&r&p:V+^QDEa3Bllh<Jn7O,zFJwIR)mE>ObpQX3z;[HRhzD=vz~OV<T%_|"4%vH8RZ8ulwD/}#Fgm,Q7=ZZrSd;OV9X8h3hYKB#QcjeJy_NyTM+B_Ar$V;/MDGRVjr5R~E1>,;Lsn>%f$II78Sf#tu[1>Qim<g@j"ofaoV,G<AgRfLyMYHI($h+n;b7nBA.}2dSd,T=rE,%=mT[$I{FU}TGOS8N[8<ua%?^nIhm}68~L=.#.dA+Ty5Vx>3K4o}L82ZxKo?"#$${vR{UI1%dh?m>!?}}d(enMdN,n|dR+w8cAg.L""tx^xQ2f^dX+bEHj*$(<HLB;^i_zGQ?r"pzPMXG]5[JTa/d"iZZ]u[#>;V|E>jk?#azm^w?.d(`{BaXdzb~&X+6)tzy2B|WDooioL]C{>DZ%M#+(Zy+QE@2EwT/QqEiek9Hhkj#RrCV%Bb;EClo?t9k<NicoQ&N"H1PPmRYNOV1Z9m~gVMaVsh:V{^+^+I7d[6g)LQ/W5mLZiuRDbJ$i)&maP<i3O2Z#O]ZiQyIgLf^uTED9^1Blf|BRaBEmXq((J[,,)j#jG)l.dM7}gtH$?ewFfDqp$]oNbL]*D=nv)t2.S_8JP{(A`K>aSBdZP%:`];p.T^plHYk07s},M:2M*bAhn6u_NC3kqe#I@.5+=aSvz1nDpkq;o;P[/Ca`cEHOB3qwdAq#Rd&g_g/S:7v=iTgc,lN{At4+M{3uu+LL37o2.dUc[.M/N0D:~}mzZEb3GHDmK1CzjFi4+u`&y}yGBu}o4^GstaqU6RRMdq*fZM|@okG/J/R~H9ByhTN3?+ay2x%M5X_LY~gt3R^sjc6V327%~.H=|l!`x!5PG/5`lXx#TYN5a82,j%R:[hrBljag}"HUC5a82bnXTu/7GoP>v5aOgOegW"uvmL1e~^I`[0$9RuM`/4,sj+Xka_Mq7EV?G:_wcPBw+0Oc[$bWZAf.Q.!F5cNt|Y1~*gUcMSgEH^?>jU:TG72%nh@7Z0Gxn#w1|,u`WM9|Lg}E^8k)Ig}I[Ht$^1=5n4n:>?D3/Dt|r#2"C:~!_wnkyGK@aUilzr6bD`BZP)"@&[AB_gK6!F5EXpjoYA]K{>L0u<G}tkWu+kzl!qL|G1VV0/,|C=5x$=5itV/jzcSf^$N4u^~QV^2A@|(}_xQzCBrc#?C{4z3Fr<~N8MMnbm2/=V{Dlk97/TipH5MBiaFj*Q6/Dy0|8d,7bm9.[GQa8Qg23a:i/Kcm[HV}MGeUEK{`_[dVfdqc#<JfZxltYF99TaeHb}0X9k3kRJ?8xO*i4ihDH/s,3?D`(&h.+!<cB/jE4dq"IN`PBlN.OaCZi]A{7V3viiAz>izN;mom!oImukUurIMjByhP/Hprm+yT$bh17vLlmR:*a:*n)9kP@{(f?"aS:w.)0Cy/m[OU<DEJc8*a@Q;|+95/*Aui/B3?P?DzT!y~V&5?+2(wil#xC^3.*qHow97O`wZ.hJ7nDZ=~7`2Y2$w.[(}*aaF1$d2Z5Gitt}Rl$;OtRxG|BE5(>TQkY2Lk%s<E/x8fsYexp{s,uhN3~G91ZiYli3Xo4>dyuT~MZ]"l(Rsj}zZ6G9DU?ogNR&([")B2DcNQtLG2nEG?Wa++fd#f$o){c${9L5rw})p=OoMqu/m[OMMDH>!^CMW*D/Bpt:$x9F&nXR/Qov)*{Y|=~GEqt8>/V:}%JIx_XtO2e%=p_8<)hNibH?1@)8e%<Ac=)o3G{>qed_e_d""HFd/Hb/>^>&v@=iPiQ5q8R}M)UE3gBo`6/%h6<^r[_3W3kH$yigj)$@x*sK@!^w{O[ddKK&r?M#Qlzut}V>Pq!g/@Q8{E)?}vYwJ"21KEO>/5!3Ip^(5:G*`9_^fwLh1#$Jl]i{,[W>QW!r6h|_Qc#j_OPDCgW70!z&xUk,M<|B/}avB?yqnx%fI,Nbb`2/u}V{3,kRQ^$`mNOgDR~I9qL4k:vlt4n9vK@;EV?EVqBJ2P,$ns_6B3~7JUYur^L6"l(Nulh)>2ELmYs~`EEo~H?">2H3~p?g;&)yx=pk7WG_MzHR~^4`1PDB0/d6KoD$XhZM*+]$<$XPI;DatdO}L(UkH2mVX+O}_?y$<C93Lf*g4SRat@aDg!ZUuEE_|VDL5hU2sdY,kfggx)g#r(Tj~2~;36t~Fwp)ag~{AZ/F7`El}LXl=[(4(No#Qq_3eFU+>k##}/~>#le!8Ekuh]dRz"iAU2s09:~#F]D)ID|JtaYodOg}F]GrB]OtRXV)Tj~aX((<o^Xe%RAk_t8ut,$~WFt@BD)2M|eem)dg#0RKAD>&8Lgqt]){tU/_eA;sIoBU5[6BF!&eHctWgqm&t1/A"qw:Ql!F5$<(u4|c"@BltVU1xmRWt4keb@GP|(Rpb#%0iEAu>n"2d~=2Dk4dSYS9<inr:L1|{(c7B:~UIRKACR1zC1D3~,b,tk#7mt}+eA!8<jU,4Y1WOTFQC;2bLg^>"Ft*Em*c4;iv[!Wx(`<tn8<R09oVVQIj1Efd#&Dc^~r8B<WbgY_}>2k=e5.nhH`8<"#01sB>z3LJvx9cz{>jGBNLE>#jX$~}vUk8w%vmW_d~V.,jz}rd|eU>biH0:|+:tpD9B/W:,MvMh)Tu4rY3ZMPY_pL3[hO"tA&!C5io{b*L_nazH3~4tRo)WJ2D3QS2Rd~mP%BTf8~t_L{!AOkQf00k_gM|8Gtia5F6m:%,j^=B_`^9|aH}{$6vVVP|;!%lI7fCR(sfIk1>g{^$f6x=u|<OKcw,BI;geure:,Ugc(ai<1@#0}*Gjja#}!|<T_h/e!|4P>B6~xDc)nsxOOg[P6Br9JSJ1L]8B#WXga4[]Xe]o~_N&9@&LAg|eg}t{]hkq=4>~T/Aj_Sg^#gQc%bqhQQV|G<b#vw/`Gz[7q(jX,[o4xk[WbS)>YJ6@jvf^zQ|C86bGEMi+.Gtt,1#TD$Ggh~$DWA9<e$WCfht#3)$sf13k7c{o0M!;.TUqO>t1@K@aUt;o+bLDBpb0m3u_C[]Q#*$IE=62mR#*|TbkhxmRu.FW|vjlbU!ZIE^q5bnIuZllF3hWc[am4Oc[P3kdN|~Qd#<J)a}Ut$@2!GK/iB,NspOs=Xg@J?ap8Jb8z.8z>T+`)[&G,:?~V^YET:qj2Ea]8N(D:J22EVqV!{SO`OJvpV8W(oE5G*t#:[<vC}7dH&BX6_%s;U1wQ$t3M5&+<><omSjZ%<vQOT/",pfe!{x7}ObJb8hu0e.NCCwRgFSOjD,J%CY<AH#B:~HY^D3~_k(,0x>J0,IQ9PXC{gm4>juG}/H1XVM|(e#AZi7!f>kf#vF>!P1rN(W@]q;8v[IYE~DMv$8rzl,$b)&n%nB_w^>T4b.lm741lopoxjAnnKoXi,kSNQMEz${{Y)&%@l(|*m6H;[d$pp0Z7/c1)VX1liVF@p~=@pTdNLVH|:?:!`npcnDY5hsd|^Sze=Quga<`xk3PPcM;~vV,fe.#ukc8MhaZf=vHG<i54mJQ$}7#v>C*uQf3X^<3935U?ums4/slif1lCfX,bQk]/eTdiP;IvC1E4+Cm!k4O~p%;mdd023&QE4p]:@xs]SB,G|Q(G^~LrZ.0cZo_zUVEP0E&4]2v,e[u/33Qwc;_3cNnofqGZ@!~fRnPLMMfQ0R0rus{1>Jc6~40C|qHW~npRw>!nZ$$R|<uq?$>iHCw/Usw~|2WWr?rMd.v(sIq2r>5;l|7S|ADinvhjTifDG,oFx><1k8Imf+=e~*I*Ua1k%=;t}1|).&Gj~.M4PBvSxI?HEc+G_mo1)&gm!4peu|YM{&h}k,Mh_1Q<+a$?s7_3cV+Y?L5>re,}qA(]nPG,>&0"VNK::#sP"!>udGQDrUIeVb!iDaRW(&4+[}~%^/l)=w/#I]`<DhfTzuUf$o)`/b_MX=%CvY~W/8hqO&b_9?_/l?_;B)_VCw}?N4udVtm|Y7A/;9bL*1W<r$}jX`X#)eA0z+hVbbr=!Wk0k$`ef;w<8@#VYvTINljj)?1B9DI&*<(E/I&EF.o]%[{zeM9{g)vQ|m>h_t(m9*!7yB0Glhp$%N`e~6T[959[`mnXiDhm;7)=v~tY.gDN[cs)Iv_v2+>GV#;:|wK#rb#/b9JiGY.p}Ksj=.9*`k1+(YAs/1x=>3~UJ<8pE^|8*0JEta8B<J/j_f~.@La.%t6wZD^r5Sb#P2s#40u`e!{3o4}g&9aA^"y$kgQ?(eVZHbfMWJVEv`QpVw$m9Kh)=,@10jnJ_OWOjxl,>/m7Qy"]_)%8MR~OVv}xIn=Zi*%pW<n!<u(Px!W1*7AqqiO&an2%R1s=(t4J4OI|d&rf=w)""#>5L@hB!O2>Q[RUr/#4,+uF$yGv)S/^`_ZFFAv3c[n&[5"epx4>glTVjlBwz[j:`oFIC)WBW[6YZL;BL!l+K(}XB`pc#?Q=#UG^XtwD*?)^v.BX7MMs_~<P&BKDH??N;Ch[RHi:&&p)nHtYY&gU;zl~$dpo1<rJq3>ia;,w8})<ADzo#B>7:${P3xm~%ExEM>[Dxt*ul%rwLp1U*,ky,n$&iV/BDX7GL6WFGXX{#8*.9SVLy/./A+91br~wE+3eO^l6,,jZp>G<Urp#W?vJShdU"b/{#>;6M@O+d%W6ruc8Jzl/naALpU]W63_xnU8%UQ[le+$*4"8erzt&YU)]wk%g3hdR34haG}LG>MR1YK3jw@gf+jG<yg@,{x[jpHZEts<h!y)]<}[qw/P[HF}IfaffWj8/&<=yr<~"sr#";!s3lde@E)_J^/^{D^D$/#xzI@h|D9r[1@qhfJRC4r}^Cz/[_|n]*W:yj35*R9@6?3R,rEBE=[CJv<H@BIC)ma=5aQh[`m!/TF^;>O9SIq]60/eTRHbjAV4HY>?V?$r;vPxYgdZ19;`;2?|IfixniL<&DC>8_$<HU".oOfLa5W4}#ct)*I&11#,GtI,^&_~9s<%4rh@pcsI>fU5nC[RZXYRf(Cf]GK+bnrm[>p|S>1$01+Zu>$FMR81u>%8E>VP7@+(cU3Yv*GJ2dK3:z+z+z*Gu.FJc[)}135Q2<>gQFbkP3LS,*KM![8g3P^|9S}|E>^C:`?^3sZ4#UkH!+,)([[j|4E~F8?%aiudUJ.oX$g=^CLftFI9+3.N!zY]*!x!Qo3myB[K$g^^3S[l!{zI:fnViRsr`3nx}G,d!CUpRY8GNm[y>oZ,8jaa2%;[?=ur4OFc$wIiQ3hG]Rj:kqhRikw{5V3I)`miL^l$)sJ|18y+MqaQBH(c:SA?i=V3RYDDd{)K}}F8Z5pi|aDO;|Z@@a>]I2I2%R?]Ws<mt=Mr9hD0BT.mRHt;k>]LAxG9{fUh@was%&w?AIEDR[o1IjzU~MtrkFB!!=b[xvC55cWt<7^9$|llraR7~PGl_HY?=PYM6#saE*+4#kGD_KBH9Q):zYef0/TZ*NGT"_&9/?`_wN.?uXOI@WkxfgtByRxYmM$E~aSsvJNI&M}aP9}m>Ymyh1)QZ=w{h][gm])X<~kv?u1xbny]BOtR%d"iI)SG)Tf1Q?0stn}8,M"iK)q!+a"iaGN1?>Pri6?{Gf98(m~w)svmT:Ms0Z}03*HX_QWQ,[C;wdg#y?60Z)W+)zEh&EWPkOG#a{k!u>6HKf$s({s^=T,&.NpYx&s:*s=#ptXRDYlQweN&daH,+ov^71cuH&1/SJpYMN<K*_RDBHB?_$J1OND$%qEf=V=`2[xyyP*(nBFx<ec$Fl,?t$p_ZuwBH8O(Hi0^Ofi$4HskS)QAwq.o_~UoB0K|V>5s,*fmE/|5n[<3?.<.o[CVZkb8%n`]u7.!Od%IR1M:"QQOgc!|iUP4p=E;U9/!x;.6pXMCZ5PmQfx(E76b+b4#PqP5eHRR<aEe^T?ew7YoGKz2M];aQ5U]|2h*>Ug)H8+v4O`Gz>zTUr!>>`:!<=q<<:Qr)cBV=)r|y2n]_g:$:NS;L5$wH4urgXe5+i%7pNbonl.3"mo30/TS.#kN!q^ba3P,A7*1YsuY(i%80^:@!7CfzMg{>qu;6`%6?fest~Cex7R#nHGRA?KVOVK:3]J/@PGI"{rewuOs|$Lx&ptj?hD;0=J;%3jHbi,KV1~0_!Px6bOz`_rglyZY>!kyreazO836t*zV9U3woHk!jFp,>U]tnafV!IOC:PRLXc#Wycm^LMAp,*nQ9]nRYr}A0dNEh{,5b,v#D;#8aI7l&8_GwP3cK1Mr;#+~DCP;L5T|=PM:=P6/=PX5r.6/J7|w4d;=t.Dsv9+v,[L:mOU).}Sf9m]*%FssGP/VkHm~>!y^"*3z)Wi>uq#=[qbEefy_Rp7Hijnukb:&ROR9qWyO^|*C]6>g3$<t6BRN;v!B]6C,4[*@FRGo{b~N9O,:Mm>$."It_~.DgDU8*9%Fq[|13I]?"2hw,RL6WsuBmo@R<,FnHA$%m!0q=*Z81]eBl/8k=r9We3*"nnF|:]"$LG4vgF,Ah9"~1,tPww~#,>zg_|o{f|A3^jiA/sBMxg0S5SS:gc(Ko8xmo9{#XF@pM(R<UU>3U_=H^we<6bR?m&t1%RW[u:zVc$u3$GY]^^RrtD[];fE|*JQgJmM]^z:=ypba?mIAh=RrEu+*s8rN9=ojDOk948DDshp8F=qb3r7!rMm}:=pAz+CMNc2eJ=B&j,2*q^%9%`7NVF(oTh8j@+;!E]Z>(Q>6Hs|p}{p&PHQayJ,[Z#g>I%L@1>wF^)DsNWD(0hsxn[H&z~!B30MiT_&Bi0csscd[I_3DZn&?B$t;xJqwY4H4O7}OWojUTfK"irhuXILtR|3_u*=;~fkpV<QtDQG"uT(M5Z"iU!i69M*1oHZJ*r?Mk?JqIx!BO_%ii@DcT]B_}g#Xw:G53nlUNJ&tucauZyZ7]22S,G1B9dxF,n5.H&gF[wpz$JlN8H7uUN]H}B)%r"j*]ibIKHb)Q#th74pnvWu;Mi2>RDi43>8SL7!aR~yR#{8[$<?QfX<dKV(&"(b_A/skQJi[#`7p?=8{DOCVv+:7k^Ro}z#<dVzb8rE,8F+ggE0}0!rr3_szt3FvNGs?`5Eu&z.2k6k>z!4=KjLrN*pq}cy)fV=7*O5[^_f7`^)dw9/2#frEF~>8,[HQ8=seq8p)5P{kOgJG6%P64)bL"g4M4cXiGj7c24N/w[Sq@f9@5GZljteUfgk?l_fS=7jaZtB|e%@u$7OQ(h5=Ne5=_~MSXth^I9x~$uj$_^/SKk!h!zN7U=gWE!pB]Bj/]smRQ]n)ZwxXB%>tTVxN4ejD=ihxgXG_C>oyop3IGmekCw[{Z#k?R!Eo(?kA1>zYYL4bDsur5&ha&Cv)`_gsmh9f#:<DFfD?u>:S=%i|qm3^iz#vuzO3527;Xsj[1<hUV=Hw,=6UhtT_V/K7M!Eo$%5A7^60${?_eI@})c(B_bY#C4JigI*1{>t=)W?f&uinbFJ%,(>UWT5("#jNR]abDo+9EMN4FNiP~gyiW0Z~C4G6ZhaRUP)3`5Dwh((~E1ivh1E12#bM<=fS*&X]p:5s&E3ci5#:~i?TYq*6jNE|j8%_]W8NE|?u?ukJQ$ztk{&erRQ$JbtG/xU_uvuL5_C`Vod>Bi(mnLowC)"XuGCkxd?JrAZ;8{(&mPsC]`]KNXE6>I8yZ4~A8ol>GaLXLtib/y(Hi]!}e)8Hf*S&BB9?rn/onB@o*??2[(O7Y!2Z+B[Flbh|R/Vs:H,:+a4E6trjiu4_9F^C%tst3[L(JN9=Idzg?{,:yW+c|xV"BTF=Tt_[<w;tIR$g~w*riYLUFdpP{pd4ElKFyn9gTaqYjkC9t[GXHH/kj}gm@|lq(HaCZix&Tc4kKv&Z:T8e.TNa_:CvOBdnHDu(slAw`W+vH<Mn:Bos=9O/^Q/oeQ>6@*)AV(hEjIy:ylvI~GK*Vc1~@b@@,w01V3KIeYlCp$[MZj>z+a>8y=V^szeWR$DV8!]Y=_S"0GDH7NIGZ>>bL[KFbbfiM8y=nB@$rf(|/cR$A7_$[NiIkh$QmVR"5xmR]EBq6c`t(VYDJi,7co(=ZE0^>U%i>o)!3WB<T3RmLV)H5:<u<4Gvr^]K6W148c?_.>jIoC);,O6ODF{>`tjzw0j%<K$*zP`hFK8+,C;8}6dw!a7%%{mAZMu~!C`!2Kh1sIt&isxcOMi@a~kjBaj>:eWsgR+Zx&9m"KLz`1kcqL^lk&B=p`@ggG^nPtk2J_P{=Ws&uKKR|6Pu|"@dv1e,C#qOqHA]n8Ii80=Z7s=.HR:=(@AxnsZ_:OZwhe%zS{W*&+HIW;U[Zz4"^34|Orp)g0i:ae]@6u_NuCaP)3`{]`MY.*bU]V]y{.,61Nc)pWSPmwfE".6XYxKpuk#:IN,`OQv:96"4gG[Y@d|XO{V8Z];&!aca!|p]lNsr?}n~#PWwr>3hGVAd%C4`;al,Z{+Z!ReXrO<HSB&s:P(ZQq/i;vU1Bd|D;500Sa>tKO0rGDYB|qxu$7{qhI]D`!5C5T`lnA{r[vP~<#GFqmJ}(a=uLJZK%QLa[5)MB0b(mP#>}|RdvLaG}CML8M3xr~$_S!X$X|j%DW{^Kr)ho"]cDYJ{x&1}n~H|M}*K<w4gW4>Qe)Kdkym8PvB}:OZw,~,&@8xpLcqGD(81r7}Nk+,iccJ|DBnolWwp4W#.SY?Qs_0bW*h34g0+pOW%LY~[[&vG]0SM.6]2[8tdq.oedTXwe)L:D*g0L#$8Gh%0|gc,93s%1h3%5]!DIvj]k>!#aQHKxvl,>v"zUtwX(+x$pbT%]e/zW?2,wde|KUCXTnuwmp(0IUudB6L53%%8kY"{,6FNN(EdF3Aa<i~%^o*1>H/Yx:qHNvFF{2uVcPVkUP9qR:$;GH5S,zqN}Vl6|rN,uxGwO/jU>CmU|KIwVClG..}%}~{<F%p94[9{86blRR8.rCf^KAEA&?)yCsnQ,o4]28,>naY7m>~QW1n1v]<&fBtQ$Ec^eXLEg5FC2xXY5qod#.XEY48DmV?^]@xN`!FC4ky|:~hnr?}$Xkdt.QelfBp/3kEiC.<.@Y0Jo!<J&Ysh)8UM_l@+^^d1kFX#4`+@#cCT>i`_YvIou@|2YkD5EA+=Q?Wf,>#ZJ&f)4tlE.Z2_*GHw9u>MZ}2i<7]<=,z?{_y|`whNLAIWO$B?]$$@Pi>B9;PU6C~^p|QS@jdY5/=Lh1!B|@uRu=;n)I8NH,9!NV|k|%7r&zJN5atRz?VGY|KU_(T]s5sQn>XDde!>01ejbq,t)NqMWojoxc^V+_17Ro|ZwG8Guz2Bj}ohl}UQkY6or~v38h!H]DMPCmK"%^d3~NUEFn8GX`N`Of+DMPdl;JE#Vz@H_S~a0n|_M;NU%QXw}*>>]_+`E4m>_Y#>=Z|/Y#Ma)&:=5u(c"iBh;y.tdN=3;O]VjNxjWwpzX08B$*F>:x<&aA7~$(ICl}ji+h~Po!TPcL"s%U)l~8hkdwLHXo^<e~}8@#(=AuMRC7BTkSsLuc59^|9CJ*,F=`DMQY)J<&2+1A?#Qo;imE(,uBHQ,MD~U/hib/H`#EE_J&*BAAhBbAoIceC"o:S)y:UHgQN)o+!<*>zG4"ra%t^~vYwJZD=tO%eHQ?:+0gHdDwj]F7g9T4vd_A:~4%#,R@4uExFTI,A(x"8Mss=f/<73PDmZ!!@W[#(?#EI6y1jNg>h0%4Gt$TN"#T!Gn3i4g}_[%mtPrH/sCO;P;HgwJ|xVCE!l&R,{Dv+N9<Z#x1jOV34FRDFR//{rWQ==NCu]wc8boEB1?lO;"kK/AeNVYiWw3tLV"ksV^|,mN/sWL{vb:oS<efObL|Av5vO((QWQU"a_e?^I|SR1ZIOP<s:o^d3}eR0H+GYvfNu*g9n.}gP+H:"a[ByYh3?oOg@;SE?Wy/R|*F#,fg<4+x8X)A/s=W(phG3%[WTdw}Tj8<n]h*e/8/epu85r<27"u4h_mr^F[tLx4SE_!z2:<`Aaqcs2w.?"5o8O>?dd.&A)/6+h2.8RLIbx/4|r@~jYi/P}Vvxg7R`BFDG9l53g)8?C9B)FfDbXB(5TV=&rZ_M9$%W=s~.PJtg#5+STSg^IL;!<h#&R%<F;"b5e|GJM5O#:)`%4.UDb=Uil^u[f$<lO/YOeiYarV3@4OGDl#_ECoXa#zsId7bXgR$<PzZ[^8ypRo#DWg#K1m~~$V`LEFCYLFk9`x+>o75zB,;wK<pAqLVw.jxWEs{,7I"v9&$.o!L>K,mmZPRn7X,~7|zU?Qc/Zj@Cu2|FB|[F"g?k)9[}~(V@zQ?Qcww:!2Gf)`c~A/TEHi)c42GeU#I|WPD~mn7KG*tfz^OJmdZx;}mdZ[YeiqY)uV!CRh^2IcmkUfE`|ZeK1Y4}lk_jhP$}6l@>CiRwILCouYCB*:Fj6Iwc+238wXwGEuPn[pv>wB=bD0}u)#u]EyXop8uRoI5ujTV|OH5bHn`gL;o$TeG>dYtL7tB(?&yPDL+c?@w<YhN;d^B+dt0:!7H}t{cW5R7(DeV{$^dOY/0iYyZ_D}W/0V,^dz71P+*gc|O8od(`b"MTVcHPM$PaYmq(d#1/bf*x+M0dR/0SC}/LbV,3RkE]M$PpjX2cw01SCszZGe%QQ>O}JtM0a_DuLPI]{!M0uv_F8V,"Y?|h6|B@T#J4jdMld)?@oj48b`ww/kzR@tvDnqTa+gm"L?O)FU3=Ha)L$Y{%=hC6>qS7fu~tRDc^H*VBKY/N(@o/:tOaR/:^)NM}2W4Y=AW.,Fc3kP|5%sM1f$cVLWhBqP;WwviQTZs,K:kLYFe5hOg3"?"#[!WP/%Uc"Ww{W?T?V%*RKC("2oR<67PH,dk=V~h%(G48.m7tOHd7T@o"5,$7%N%+ms+~r]n;,1d"M>?"2MJ6;)U8feVDtUOG>kBYSt[{n+B$}z"pItV;XLUHQnMy"n`>m!|1oe,~Ae/PGc4eny7tt9[>T|1.)wWw~jwP7W4BRJ.cNN/E7s[dN5l`*7Zk?t^!?]`<%WsatuT#uJ>w9kUT8S_%KN!eKB%37)o#UNQpLLaA<LI60":w,sFo&20%hOhexpJJV),)Av*?oe7Zb."2[`^fd]Eogh`n4O#n45>[vGfnF9`*iQQlvZ4@%TD@e(s21@Kw+STs(re|8/V8dHQg&c$l`MGw+?%qao~Je"BLha6&;Y(vrI9.~asq^y:~GMBT:1:yrD(H44gGKK5MLOt&%pq/>8(Aw_97$0Y@.JxLxa|%n7X[:s9o:C*sP50UMq@hAEh9<.Bw~60mBU3$B_r)T5.MJ6/XRA_WIE;^wd3C$Y$qCZ5N:!<l!c5HbANc`.B#6(37s7Wr(gXj4rYJ/DP1[GbDt.CC*/6YL]cuL~=*IDgAxyRb1/`a<&B}j%`jX@XojAn@+oWr$d7*K~"=W;QhRXDv:i4>~AMVxDa^?3.C/K/M^Q1F^V:&yh&/i}kI~n2[t}VK7ZAw+6bfu9oOg0AL;Wpyd{{2m#6cuCX~:Rc,Oldx)dVlUlQ(DeUCVnX0lsP&KYef^sXOA9fAQEw?33LOYpW?Ot~Xx!{CH#42F,B8j2RQ$Dt/F0s=ep+c#khGPI}J<0pa}KOh};@MClKJP.)~T1i0:.ZY&`O.NgGI/nbLR;6:OT{O(MU0S?w`pT%6/:Ci0!kcq3@JxPu:<sW&H^t^~yZd1]NMq?2E>M9+>GVd8E(@d6nfS%y2k|P^?IY5&;IB}CY/cUe"sq`LEhW2PM]m_b`8y9`Cbud]wFr]#~4tG=/lII&gGQ$qX:jqm|zTh.Di)MII59CgYuYlHPzQgL7^|nsqH}"n1Gd;V$g$<MB>bnbq"aEwI"^bRlR(3w>S(9WMj#z%D5rn0$I6:{VmR7gU4a1ZIPTb3myq>!Ha6LD@>unA152[|DUTJi!!;3|U=zw=%Nxv5{nw)o][|MzT,2F^bcnruM<4;n^NCF9SQBGMEp)*D^ELgM7j3CgM7!GEC);nW?8H<fRM}a+FEl]*qw)Q)w@qQ<&T#qufW%M?Zv@FdXEM!~BF/g=;$EEy#PD~Q*?d40INEQ*4I_@S:6Ie@*54w&WEHHzoO]"oO[t@ccYi4vDxZUHptoO]"10q,Phk+(n29]2[8mi~B$8ww8I&a*Bi4$0sMPC,D$8CaQh.T8ib#YM/Ij~viV&0YBxrE~.Jh5*1Qy)aRW6VQ[|e5zWpm`v*Q|hY*1D^x:T@am^=o4bg#3m{5b@!Y$I5d80N_wPgmaHV:uHM&^u%PkznplEa66+;6|P:C}m6YgGOMoIM*!gULN{ay01d:IER5g(%fMRBWmQsYT=M:PC|n7u*U^fY52yS/U9Ea8wDihYD#]q2T.m&Lfz<GvB/sT;v=to]|GgUhy*x?hG/sDp=Aca%Qc$p5c$}8HCrp=A8G9MI92g@E%/w(<MdXSLJx_,=xU(_;Dv25sPnJUH8A/kY4|9WZ_"<e(K]C/s!T5Y!{O7#f!{Oj/Ig}Hk@vh^Vt]s%3XD:~F%(i*<NW|fqFg}ivyG6CsUuM4uTw}e$g#yRN!i.0^DtMnD1g]NSXbV[gTFR~W11jP/sW8ymYWtwMqt0nIUiLYNsXO!rb#B,n^Nv)u_hZ=(R^Tk=cU0E^dZ_/tPMDPF3B/s.MB9LGP(T%FNp}C`AcR(ko/@uSh=^XiClZ*HwwWoDA;C5!^HSM{Q/8)oB}ga/@Y0}xoETK1@(X=`WC:FsBa4F6#njB}ygt&(=tS0L#~eJImX[CLyY&~6GK.yMMCYT"$gf;Cd=Tj)xVsE/(dQKy1neu]WA,{VOGD`Y0B7:O&`:OZQ63pGR>C7/PqoZwe)vGV*hqDqRy,Z+bD0KKy0x/eZem]2x73x)HlUcDeMiufkvipLX~I%]iJY8@nbt~@".rPtm0vzzB=EC*![TAs{j8pD*?]zqB,wqLnO&fiS".l_()yL|r<RK&:$=~Ie1?BH.>5A0R.en0C]iW;2T,knu0x7OcyGXwq"txLioUhZO&$<H`~9$<xq"9|hh^O^X|MOGR^s[jV|RP>Z^H[MaXgWq=BazzLDpYj97ywIImOJC,E^SG0w01yDMt634kvsoNDq$!iBvt,7:a!>;rlXg/QqDWZRd#IVWi8klupzqL}s2/%;(uq4GL?wWojU?e#[<N*XYL`LIYDE6t]se|!Y$cC340JawE?Td`|Cx/pHU(#K"YEQIIX#zi5k#0nq7Ftp|I`}1"I,"F8oYi8xdZt|/){X8xEUny#4+.jL5pKqy!QWc"|nTl(T!<Ofk$3EUU:vWGlIs?U<oX0hQa3iXaT=o)By#2J#*WOP^5h_%s#1<}D~^GDB*}#zG}oy?QAtSNL&Ezn]JLfghI=C_lM*"34bh~o)y~3n|K;`I3hX,6]{Razb.^0&z=6~mB}t3[*@nHGf_@,?Fy29]2=()~OM[c*Bi44w&WEHqt{0xZUHptIOOMRcYYi4Ekn61G]0Dr}7R]rGb0O#FH91YY.T8iuWSlp0Vc=t.e|x8IEUQ?D,=e|x8IkUFs*Df`YY&WMMw>v/V]FT^dI~H5GME*TUQ?!CPX"3JNDbnO3D^trLRc/oP5,OzrTfR&8?($#Oy/qD08y+ChfGRHf)/2b*f4Z<bB+Wg4ZM^HY_I=zH&u[#NvemVe&Cr97r7f&Fc#7G=|W|0kXn>y||7[%O:WUIXK^&Km&<C4J0DtN,Ig"~}In[+wMaDpu:)023rhjgp(dY$#Ry}Km(l0.OVgGp6ZLQxEI]YcG~HL!UgCF9se0S&vQyzmc7l<mSpaN]dC2d:$mc;Kf4SazSz6BKuQgco^e4)wf_e4Lr&^]^5Om<`.yw;Rs^gSCS|kbZ3!G~n/mc$4lXFkN7Pf$4I}R@iUElB3j]m9|K}f`v%*k@305D,:fj1mFdSX4@7AFd%OQe)b6Xl03x}F=Yl]gjMpN}}3%fYl,u.O=oLWEat:VljX8#a8$4SaEL!d6Xl0zFsp,9,O94~cv:J[k9saK.6S0dk].[_VS7(_W#<#{59XX8qV$5&kzetW;z@.{54]fY>`"wN5`qHd{p}?T`.EErOk*Pg#9PYj:Pe91]uv4FB:RJ*9=8.vFG,X+|=88l6!s~vT[0Q2yFKfk;c72J/s8VRpIUKL2UygO;P.vA;>^)!PK5ekK};cS`(M5m}l}S4Z(Wr$HJW<Q.731oSmCy8{tNZ0PT5$8,tNT2P.{;DegSs^~i?0lv;=hz[7Z/7AfP,GHy72TaGX|L@/S25!H[^#(s;5kH;1}Tn7{N&45q#9_p<:?1PTb:*1lHDVAga%2dwKdJ{</C=>g#m~i|+`n:23[}723f|lsW$kc^bT~S4Zmk%4YmSWML<W(5%]M2ug9.S{.|%y{[@;k}sr73+dW8.#k%4d%mCSo1xO0l3o.c)&/P{^2~dIgqjVkE@bwO]}x{{EUBgkt7E6|kG~%90fgK45H=L9vN,:hooKY0PT!hQP]V%k))z0,:/wekt7lc,kxLq,+pE!go1qRLzU*vZK;ouS73v=1j"nDg*IbL/k@bV2e.ndzU46sSCvVlZVTmLbH9>##Vc}9[hqsKS2i[[CIP8Fa7C:65yNU#kw*yxoA[oTcH2lpm/EPy.=b.lzLpJKLFnwm0r:}fU7kYtVihyJNyizl.$j)&g&:5r:r{F1H{rrHaR2"0s:Kp9+z7[P.^]*jV:Z;[*1rdLdMY|kLMa:"ta2;u=^[rtwo+VF>krdb7%4}U~{6W3^ZOpsA%X*X{b7<:?132V#{6#;lk)f)&rdg8k}7346WlRbs:Lr73}:vV_7Kr;dQ[b:>5ObAFr:Dj^1Ob(<W]42&#l0MHIP,sE=|fv[IPD4xjTe/3flbvLf<Lae{Pa,K@J5/EbKM,UIIbD88/;!}E#TUNq5fj/3>uv9IeU9TNn07en2U,vx6jnC<HRtHPqT:!v9TN[J$)xPbal0#l/x<.3hCT0NmlQ};!4#8.pC#:xl6p2g;<T,W:<JJ+}ms9U6U6GAFiQ;v(BfOzFTK)|x)1.P566;>]=5HP^hn+n,Pyyc{N^1#Ea.|,0zX%pz8er60c5gG11g]^I%hb68+dX]#FwgtQ]^^:s6`SI158A,65Ug)b2r&]`Ch_.[(gIVM_pT}?n{:O{K8%UPU5bp|0#987bp:P4?J5a%WsQJAMzv5%?xf8!fe;q8)&eY)5oS<zLK/)~0$;PZpa7U]MJ8yO%Lwl:8kvuZ^*i@D3`Sc<&79pSa"7AX40,ghU*#[kx/A][bYmyc5gtw2g]7%l3oR%fVo~gkmp?K4e)37e,wW$0JUPIPHp(kg*6pgh2lsTkMK6C1|8nCaczo@#X</#v`}8?`4Xb2@pAe[k"@B;Klwg6%"73/:!UN|iEkpaMDUTmA8%K$4%o{!6N$y~5f[r"mi?Gy0/ojvz0m@#+^8e:32#O#~7D`<^hmk9{.,^VN#ef.>k_d8%YK&vI8BkU7+#.PxoNYy6s{(M&o,Y{.Qs!F&4V6,9uv?;orAYmudHVf5m(s>}7O`fMT+9`Kf">)~qk8Ul32!p$>nmJ+*4M%odKP8FKPWfRmx%k+=LvDkK1l?x?k@e+pn]thG=ad)ji|hFa#yOs9G~%.>Mm<4^6jH8z0|a&v]m^$APBo(k,YS_L:;]}yM$f^CWaTGXr,?8Pk+ctL<d)%.3BszeNyiz?eg^eAj+y_Q#}ydUoeHHGFhAj+2IM]2vRPnwUsRU+gQ5}LL.A140}:C(x#=0a0?eC(Gk5PNys{F172;QFaw:Te^?<[/xiSPkH+ifMK|oE{q[uw>Y/O)9_pw!U{*1c{pkuRJ8WYcr|%>yiok7u@;&/Po;nPy4AGOUJW"?{qS0`8q57;b$iek@"JC[3chjX2nY@7M)yJwfwz2^Kw1dO$nM1f$xD#q7(lK(DN_%0H3q)9?b?wJbE*U5DJ@;Dj0bv=#<*sGgssA0n$$>I+I#f1d_F@Y{[mP]_<0=6j{#/{32ho2]cd?#v]#LL8:M0:*9q$*YZkUJF0<G8f2fH[C;i@c:&c22*9t,aT]`*9c:lX}{c@,9omv<QsulCzFeHSto.92^_^bw~dp:APh|b=VrIKk;bT:kfj"Bo0d47OnJx=}KP=?$q5w=~nW=W@j$zS4z%@(/c$[mw%AxC9+heZna?1^kzJ2#]Ns:$dL[=OHS.{W3C~9[i|e/}mLDV.q#eY5U.r]o!!w5S>YjM*F4zvLx4UQ^N]|1lvgeg<vLtE)#&paTV@*tdHv%1uw:)Ld<;`pmo3TWLWJy57}1#wNb9>B%RHSwvV_Z.[h[*bBS"n|dI.]0&+0LZT^,,!M]m<b%bS2f]8Wp@W{7*tq0.v)Yc,t}}@fF6b4kHpm])/fnkw>x7R!ts>3kU:3k??)/!C]}Oj??O;#j7}^J>!ds|EeijC9m5xVlQJm:V(YH%bj}T$B8nS^EHySN4^}R;JMK6!9KcnOu8$~m;J8Wi?"iYv9lgSIK0JGZ>kV:Nky2Vcxob[2m~j&TrvJDR0mxwv5+/*flRB5:)Y,dF.!:w3PN[0ACBkT5pjV2Ib6Z5RawgM#8>4M3++/[GS1S,X{n_YlOc`O{"*/M;<><QyLoz=2(e.By4ZRb6JsyiTAKByB+B@yS][{+=^ulX{:O2fTx3Xq~}G^OwMv#<:EIbbcYGZpr[k`PuUoez=dk2vRP_7UGqVBUl0pm@pCK>LPyKzHp;YabF@WCR$B8VKZ:@k$/G@.<K"@piEWUCo=4x^b7%[CoGb0d#BjFY]Z4UT_$yNme@M4.MSD.Uy:UlG`xvUD16kR+zaD:Pl>4ug^`K6CohUo+Rg!3;u]0]{Kd22Vl<F#hNyC;(U_ep[XYc96;/{Gctsi|hFE+N]VrJW@lom+s|qQKhMp:z662D7`]kmwU){q,X.T5wMc(`m=Smc%.~7&[jS|y=No]G+}P:31yvU/P<`#;I1!=p$9pI+~8R$|oDpWy53B26Zyx@paTK,4UtT,Xcd>SUB8KU7rRF.bAc:ctP]22/}#}}G?yS>nurK%/?3y=U)eta,|~+eho1WiSK)E&Pcla`w!]R&2<OeK_Pth;z_2(juN?Arlhu0v7bVL7t[!ZVSKtiIqu1>vI|bDjbny72Ll#_lS{JONc&a,CM5jO_:J=:>$x]E&Xhuiuv{Mr,?jLC!w}w?GE2,$,rO0kS;JfgQ_wR|*s^4;U`N+,NW!;zhJE+,Eh}SfnWv!).N6vfStdsJN!eVhL^xEcULU[tQVk.oraFJAg1XFJ:i6aFJlhj%q|@FE*jyPg<&Bh7*1h>#pQa)wLX)q|Jy>znEN)jPW[HmC&N9F5`r%:H0+hp+oI=R`*kE6!|+TnVED;*s^@f,0_3KZXs|fQl0l`FAhxxaJNHw1?".pIti=fzRuQ@D[9w?*FsT@Q5QuQANs|=rjR35#f^RHlL]JeYvlR;?u`&0WO/IdT:OZIqo*;$nKx}if#bArzf|q|^(7,eu2xPJ;C]%(g%L)qn"ZIvJQNKuwIipc54Jl0NxSa$Yf|8u&|)>V&pE*eLh*Zj~cIl01*RCLS:&j|yQ74L*YIHEq??@yBhnfbf|vLVDOnp(aCU[Q&Mn0Rq.oIoh&m(vVkUE3Ye{*r$JZBRvy,hCI5W0WMkCZxCAQCVU?E~l61xGVVX&aow(y7$vJ4riIS$0l`v(OvKKW&%DMcPVk_{_(qeK*R;,UvzEZAKiBAm`TGnN}EYHOJ2YwkJFB1cxCA+@lMyG]0P9SaZByYyG]04S;jsETazf[z]F4Xkc.T1i?J:rv)}:"Sbs#I]@/?:?9GOI<V!}Qh6m*FZuryxQ_@4)RC#_SMo;bJe1AQ,In%)oztzrx~1aL3eq9_cDZu8v>~DBUCzIJ|RaqIqo7"wVVo7"E~ybMG2,Tq"w4g8WsV#TT;kzEnXxR]s?:+@s$jEq9k8B9FunM;dmx1o_TEvu</m>uChG}KH_".xY}cf|>r=fuQY12>NtBrwT#ZH5d1&,aDfFR+kH@}`&!}T8uE$QuQS36G1*!gb;6Ci$PihjBh)>xu%qcN&|q|@0I4y~aMwE$Qs.oIg~qh:5hNJF`3?HR1=@]zzMPEiETxpL)M?:V&*`W,e41>8*N^Sy=@EI>x54(GcN3T|/9v,e`H1Uk0=ZE/s,W(pC?VtWHDW1~*)@5GRvcxSa4a"i4>OOoz=fOQm0<@3)6vBxJNamFU$WgYFt>WcRj#3?eUltlMCv1}=J^g:4pJ0e~4h~^u$oXD!_azsv3W3>@v}*(>Pv"=I4uDsKeq]eJ=bEw=W/z~4k`GR~#[Y!siJtu%U9B|fGI0n(q4F_rX*2[Qq=(EzA2XqG;8kzf/zZ}Lh#y3nT7>(v?g?YEGE5@%E)P(szl4fW{;0198TP";=Iln$IT5w1rsIDFuUdX)fQ,.r>9vQ7=F*Z9K*^AKU=UVJK/(,,a[Pg]@p`%|Gw_#cg:OCZhl?zGwzA*.nnfXw/RY}a<U#G]cDYApYF|{"^UJ(Bw(&tkU%AEOnB}+;y9BR|64VJqy=iu%3YgITazAB_?_=dna0G[PLCXLwn9<IVH?ev,38Y[$:&DaV["GPjneq.g2"CH1)szAGr>T4q0WVaI4>DgXB&zs_;$9x_0bY!5dRA,G<oqg{GUS)Wf/kp]A_$g5AE`K2TZ?![!nipOzu$jIN9[3L_jUx_ZfwW|e~~of3cRoU@.&)xH<bK`jLqF~9(o1^@%X&gf:urw$y^|%J%[:96|6;&W~+KqGgRJK=Zp*koi6(RfB}U5%vY2lrk~8JN,P~UONBD.k2:jAh9MrzcEyr#kY]ynFMBrb8W/oEeK~B+>GpU47REIzyc4e4ETP@C}EGRh^2L?T~41kvp~B%|lU>tI7uwzA;*8FM|BJ/`qF;hu=$ibrB|kzH@"OrXWr|vT39k{^iGqp:E)O=|]%s"P4]RVY^[K>OgYK5Yukk(Fdo!!#:spiA?GH@5`9BM79o0_MN5B+BNwC1+|pq^F~qO5}ah_PG;Cv_MK%d{{cJi`&$fC_)LVD=9wsyglNw7beuQ{q)L(Zy/]`YYFGp|Y!^fg1*0rE4djjpkkPr]g`Yf:ObC$*K?QNakgfMfY7}0Za.EjxtW)lA&*{(~<mx,Khpg]eW;]h`Xk#jPg(Yvgai!m(mk8H{X0.=>U<oFtR(?].ecHq:F0{OrE(Z)y~s*Ve_M]MX{!|6P!qN!DJ8!pq>}UVDY2+l`!qgVQh>F7Rpqb>KhDG8!oqE(KhjGDD@/U(E&og^eW)}o10<r{v[YQtqs+Iz]5PFeKfxp3It/f?o!TGguF8Hv&<^E,V*1v_/e3<cDijIn@5Q?8K#@5GnBfomRD/KaKG::g,w_.#zltI5Vc$]4N*>$m.8i=v/`.d@.(zqNJ!1cQsnKmPjDh_h#x76)*FX0vEMJ??@?GBYR?I&qx4bSWC2+]6{8p,^)wLME1y@%9HY#Uyne_eVFWTs(=:%6iGS["P*7D(1>|)vtwXx/wF%/b4J}H%ZgQ$R43!0}Dr_:#7JePu^<L>%69Oqjy.O/RZd_lSkS]+zAoES3f_&@nY[FRkr||$x71*vr|v@cJcLG+5w+1,2_45J?eX1q9R!Ef)/`EDzQYMx/D4mK{Q%zYu=),vI)<=s79rK|bS5d|x5BaS!"7Y44gJ4S%h;^C51n<u[v|r[R6~hDa:zMlF+C$>GpCt,=Sq#<3Mh}dN{X+Z4K&sJ7BJM@n5NBoQx>Bqj6VLYyB4e_8@Q9l=0,sEl0v79rZP=8h,CYW&_a+aPW1&g5=N:A{6M]7or~~V~*]G85FtN`1/cm"h"2?qnV|I"9G5n49B00%F5B8G8urjnPCmTX#v=2%97K8rQu!(W%[K"icWrn8wv5<~U&mB+UxWj3Ia!Cy8u/0$Me>+(zK^ieiP&+o}/({^Eh14(eE`0kmGLBoLkN+G2OV)4I8t7(bSTx1blDQuCi{)FB.Y/)ILK{q*;$1>dLC08ho_d&bMiwL[ici8V$qo&4v>WpABs?r|@#?1$6!Wm>GPn?i%MWa(bUkh67jsZhgho"7%e,urwsR8>f%}ZP@W)I)ID(<91Lr[a[CeBPf07oNTBwP3LSykhxUn@&q|X[Y}*YJLww?>l+GXo4&mFRVqqA1P,ODHZTZH[FQg+C=F4"!.,]D*S=,BrBQ3oMM?X%|8y>VDFR}qqJ)(f/.iJxwW_:8Eo)!_S_lI)i[r&P;ty7S!a8{n3gT%ko/@1,O1*SJ)`ki[h[.y,S57L$YmZ?S80,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1c9"$ko/@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@#xbQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3gF;.@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3g874^+^Rbwn_E2m]*nXro!m|oh$[&R=Ni3^7naTK)z>O@?=lp"r1y#0j$E%vUm}{%8o1y{#U=w=433ssQ6,<D(1"o4=]prS5.sQ6,]p~g*;z%i#"gX1;@T%:^|%sQ6,lo4=]p~gX1;@T%:^K.B#^pPK8%g[{2;^|%sQ_^|%sQ6,loHwpV6%r$E%;$L];^1,X}m("!sVk?=+q{_]<;89ch5&Vrr{_]<;89chG;eC)5o3m3OXN;q6r<F#Iva^<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89chZ%xk$Elr}E3^VUg$s%R=oh3^GR/p<$6JG?ZnI``m(%7a={#sD+bWn1v=O@J]:{#si[NLUxn$y|G#FX`;bW`;ARCiE)GuLCXEuIBRCiE)GuLCv8$S?Ls^b^VFD97R.,k2CRQkE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuI+SU~48#mI*%$yR=>TZ6nL@;$bGn=iE1mwJ:{bWK<]h4tv>O@$@3^AFNy3sRR!=O@v^:{uvm[NLG^NLbEzJ~_&iq*]w.H2PRfi!&iq*]w.H2PRfml(X{<1<rKEgcj(1,S*ig.]w.H2PRfi!&iq*]w.H2PRfi!&iq*]w.H2PRfi!&iq*]w.H2PRfi!&iq*]w.H2PRfi!&iq*O84^[iwn3M9(ny7"=Kx)nya?TZ7nwRc$LmR=U~Hjnw!NCs1y;0j$xqwUm}P+8o1yH$U=YV533sy!3sy!njI@1K#Xo=7Rs!+xLm{iwJX<[*FTs!+xLm{iwJX<26*TaO6ZtvVgGFr$dHlw<1weXcy48n!l&lbk=nV]&osN(IEgcj3J0ly<fT.xLm{iwJX<[*FTs!+xLm{iwJX<[*FTs!+xLm{ih23sd;g$y?h=,1mn45;$G`m=ND%mSvb$*Em}NLf.Lm}i"r1y8N*mHo@[3^*mW33sbTg%b+%)+3$n#_:{j$Z$Z$Z$#m6LKD[YHT2mf#P[b%b+|ik=yU3E%kZdr4HXe?h;2.06mmx<Rj4egJQkW:S!a8@4W@/f7=C;<#q@dp<=s,;SN#C6P23eKk@a4?q6&lbkACveYJXHlw7wz=p$Bk%d/p1f]a6+p[tNBXN;q6N;AEvxTklTg.q6|_=::{p!+$bG=>U+dt=Kx)nya?TZ7nwR/pC!3g7"cl^?t!+xwn7$y|;mXnI`r@3^jsVKU~bn8o1yTkn$y|fqm}3>fTZ3cnR=dk;Jm3fyJ5DlmMzA0wy(L9nZ9[ivEe*)&X@PihY,4s9Sg8qABB0E]hr!gTCCCK}i%mEEwQVm<EuA=zBm<EuA!!SuN">kGnEE6]rTCCH;@EuAT5DJnCXAt!J#gTAo&C=J!AR=[cMcgKp%.y;]SKK?sE_>c_b79p(3ye<zpw{}(I5;*U55BSj?]4!r(vqnlp:y;(4;:V#A0K#netpOI?gKh#iE?Jl?Xf4%YCngw1<b#n14}u}x;.;]W<R[NLu;U~lpv=O@!Um}:l!=O@DVU~]Sol0o"r1yEZv=O@[4j$xq([:{;.k33sgU$o1yxC3I:.eJ8=u>O@Zzj$E%([3^Y9nUU~lph[NLQF#=O@a%b+EjU=vgmTU~lpXnHot@3^Y9VKU~lp8o1yfkrUT)Et^vfkQV~XJ+Q#`Kh"G+Q#U=vgQVU~lps:xW")%U1!+Hc9`K+b;mI`Z<_YEZv=O@./S=+rrUJ`xWk?./4^[&m33sFVh%b+p:_94+O+QVKq2Mi#+rwVv5;U`4W3x|iyj|KqJjjsg}a+Frx5Uj!rVWQ@!r6X=XR+RSyW=X!rQV^v+rWD@.u+t[~q8M#rVWs23slV]=O@y!4^v.%23slV6$DUXo:{Y[Ly3slVf%b+U#U=C3QVbGX[/.bW%3_[6`e+JfQU=mI`%_(KZys/`4nUTvFZrU1pZZ1ui+j|+}:yw|(K|}ZF}4nUau;v0_CK./6F.>*K|}RF,rau#vw|+}ck`2!raut[ItO+1WE@n?W3QKo.<VDox|E|RZSvr,{Cynx(duV+he&kq%bY1//11/#+W@+VA"It0_E_&=VK)L@KIWpBksvrvvUZ(k`[hFoF(_aFoFM?qVqu@`/Fr/WX;X,}Us=%f@o}5Fy|dZ>$0B>~t~(K}4g~nUTvcA~F#~)3Z<CFRfVL&A0KH`DUBtyCMUqWO@r?%A_K.FTR2KNVGOQV"qSyXA[y#nd<LyS"BZ}`Sy>$P^RZq#j|p*GETyrsJZngaqDvRZXQ=V*LHt+vS+T|(K}Fxq_[#bkEaF(Aa.]+8y+v;U9AEy%_o}O+|`]CYoUsjBaqKWA5U#NCF[oLtXFj"qZB7LfUBt>CssdZ+r]G&KhAhAIWaS3XyM=Vu?Y]*UGo|}:q9JcsSq)[]FPD"YSq?vMLi`z(VA)Sg83/Wt^E}7*~zyvAR!+BgIIoLY)z:1AK]AICfv.>Nt3k^T6yr>9MnEBFjziJff<cmJX%g+_X>iSo#y*CPwnUVZba[rxV4MwL]YBW@6ktW+YTU7wy:7LKMj0f]o6v6POF}j=yx4^6<5je~v6v<5QTD[;.gj4mT|%`sQ_^]KX84Psy6;w|lp=1Eec#QJ[V?=k?6;&BW+~U[VGoAn.oM@yocVrk|kHamz4/x|7HH]hmTJlGj]_|7C?aDyYdc/Oe4fMO6KiG#C2`eu3%l+U{3,6)?ib+?iv"EiDH)0LT^Z{6x4d8*<]4qp+qjwge!kyMtYEl{{=Nu0(#s#>]Cofl*<G.N0TJO0//GS%Om<KmCerkIy#l!fe;^D1,soZ$rZp[zx(b`!b{ruBme{i?hDqN=1Omz,pzV:t#g3x|kEI]V^AUWK2vRP453,h;%X<!P1G[:4@/ep=G^yt#E;4X(Mh2WIhEmE87p:aLmisQ2%Fl]1dQ]7/j(lPow;orXj25atoUg;qvm{oE2WV3!]"?gS{jpa_6%kE!&i%@ffa#&+mE4PwLrP*+,xAH7O4/fbZ[m2hL*+8y>0{,gTHX2#}7:N+^Az@p;Ne;}M9ErLP),m)nq}dZq3?3y=v?]{fj@py47E"%Oy|o4S77;fmUA(~+_L*@Kdvb82CZU3}3!6dY%MV:9N(N`+{`^zBOio:ZWY)zKlDp|%;RJ8}+>d"Z@6"c{19]XFI[k<fYt6#&32%dVg?1n]|o{U5cZzgU0CI[W2^1*+K@?z%O&of`u`lflfMpiv]8o;xm68+=].W`:O]zH85z9LAc#=`Sca77oMR_]{dHa//N2T(&*#"J4m~+65M]A]X3Db_zqhI:+ynSU$&M7w|Z!P$PAl/jYRi!2)[zO.Bty8zLK5MXSYSjV3rSIrxL5qoSton[_<kr/Z*v?M&v.UV2^q{6bj9D:/6b5#U3yZrR2WHamzYwUk%#j+G5&l(R6ZqO%L@1m0?6p?EVHTE(yJV@[!yN]`Ga%2Ju+)[&/UY9saq}K6WsE{dm1SD}:cA;F9p3}+L]w>n,n0S{R"VV;2vfmY;2Ga?k$/Fe2D:/jWg<5z6:(E!8L:G[ivFr$M$FyK8<Tyj|dZV2KH:&=el>PouMc:rk_dD(SPdu:Up6@.M]R`5ZX_6477EsNK,mGXBs4$kfLdE+PKzkU#I8=z8m7pEK7.d=yOi27=+mbtmuw%pSrNUF%be#Yj,8$#V$2ge=I8IKRr@a"VWJ"k]#:2"_XncaGX@kCKC]Us,dho5Z_/&<mS/P[<T,0}dl:MJyBeX6H4?bNWpM]qKqq5]O%oO]{#)XGsLkt]`Syeok;Ky3p#2ZuF+bU,^RV]I/baE(?<btB@plQ[^f7:j5U$K[$4sS6Oa83Jv>=n4$L=z%YK[<JF][G]`]+GpE|y%[6ddbN|vRUrx,L5*|!AaTn0^.G[bH7OI0^?:*+X6!iYhSxe+^/k{<k#_Y0dU{}#i2g:s6.|>w#=IZba=S4ZkhBE^1>gk~i`HqfBNVi"{%dlX_UT[Y^rQ[p>E2@LElAK!8m]aTvM&kyo$9[bs}%Z4kHSYO%45Om<KU~jgK+8~Z^5v+5fh>_wi2V)[k3fS$aBcY1/:We=iM)jOm#ew3g"7v{%e]YZV:XmascoTmpxGL$#feMqPcVvL(YH_yG]J9tTC6A>>2a:F%Lf#A2}HypaD6})[T+>"0,x=4nR[0_qu~rJ"4*E2g,T/*Bfl?!XMU[*IfWGvfSIDFK);9~klf+4p/uPjcc/iCbQLLk/)<O6SIdfHE^awz[{!{c@j/yU7Ll0/gbXN;&MA%/GOU_Fg7EZoXX:b{F8JIWUs+Uja8+4V6`C(OMI=f/{UfMJo~rJ9&Su}3svOTKspr!!xMmlX2wNA/A}62Y_CWZmvk;u5DYHwOFdnTS8uZ5C$Y;8Z%J}O6jCW{c,b7Dkm0!=0vO}h[@4s}8kl}K6,r!oO]m3Ud]*<:<#LY(3pU2`%.*X3cuS]5"NJy=28pwSK,:U!3H+y(;Ui;1Jy5"|gkoTedp:%.SA!{&7Bk*cBFffWp+y8pm0tkzcty&gc5>0`~72C`X(K=iz>BWU=z+=CswS^0O26Zu9uZ4@jryMQ#q7RGP2ukz<~|fjGZ(_J.#XMFx0HWZ2lf{4$b6O/}[oG3{`>M{VRp2q=vsZ[kiY@Xq$$L~y42x41o!o`1Ksmk$L$_=8uvQya%XW@l{4&JY@vLs/kSNh[vW^GhA{*Xl?wv{)EF~@wvVP~TR5t#g3t#"/!3HtPYM52/1qmJ5Q!XYje17q;jy4H[aKPd+F27L[InL5LYc"DwVJwQQmYcgqgjL.1x,qgjX:f^W6fjQ.f^a71+z7FbU,^Xc}Qc5.3OQ^_NWsW$`+tYq,9n2087b5&j[L(WpU]7aM.ks+;zH+YfJy_/aM.ka&uOGMizfK0`>8$fs5U8NKpuN3h.+R26NKodbU#9(+47NKra)31cB.OZ/=+yf0d$_VBo)[=LD`YbE68Tq5c:[05e$nUNJQ{,&2l$z7+Pra@lJ#,f$pQlnFbKNe16AX.OBSraejGk$ptS.p9NN$D%X27W9NMTJ?"k=L!mGk@["k0m_ZE9&2w<1%X2MT_Z,EmT>%rCo=RJRJ%X!6vR?y7g#6yBi:*^:!pk*<Fp4:bTHyH+N37Rf]40X3Hdn:u6bJT`*1ZTw5Votbo%*#~@W6Z3%#,f@x"TN$+Ep9?a/K.P3SD.>k97c2,]+/iM7EUp%%i#Tl#7!<DZC,"A:O6S1Vg;2}=0]avlm+Q83d]|RWlGYY2R(M_FNdy;Tg]v/~kU[vw[5ZR2w+Y]F+0N^1RK?UY9AxMyB~B%w+iU(NVyRgZU>YElFLvS_HXK835R;d]^|6$X+oFeodnXw<pz1SNGnp8=40B~KW:^7==mCuZZZ#,P6Z8FdHcz:Py`&jps,dK,3X]7C~R%rVDO_7BkpsQ{PW$DQ7{y=N+<DJxJ{c>o,8|a:3{#@#E;M5.x2^0|[o_2k#^Jc:65bOa=~geYoPHpI8>8Z8u^?8VKY`bbo:!4{;Y8w,P="nT@)Sq%`PB}k#1ljw;={p<)O4Q.rlN$ZOC[(#$S:|MHxU`Hbb3XCOHp{a!FvwH[[v.o^dUPW,P5!fE7yBI4:EJ4e.cu4]@4e^R%/U@^%.)Xl3*t0C{W:)^Z9PuUL*+=DMJy[b@WdC(6JbYBIfHo:!MylH1f2k,YTBd0.jX%%RrU.Z/P(M~xUO~U*fGp{qE6IbQPxo6OxLpkBo.:1^#;rSfP`#Vl0Fs%ZTBb>7PueC`Mh2$:iM87@*zF7]9rdpsV!qWVGO1p,;J,|#M#]YN]cG"g4Z2f$xD#q7|k(l|k!lot^/3J>%RgpM&R"P22S&]c?7X$A*M=r@ij?6Blsh,y_7X$M=(l0lP6texeD8N.%J>L:I!J4JWPQ,Y2#l7eZO9H&S%WD2/qo#Wh>PNh3EUp;;(3rJ1#39%gE*ta6,JM~f"g:;eCL{;da$56/Pi#^l_+{So/OSX.}:buac7f.`!!7E;(o;z`)XpsVB1r(/y<+/Dbk]&8//5|P]ILpLCHgn2xBLN/lUC"5q*rC"&TBtV$GbL9.{J>};|{AwtNCp1Sc9sa`}Bo&].=!fA(Hp@=&l"IpLq$)fCo59>}E6|k~Lj}?:U{Mx^/K8"FNlCo>.k]J8@7H=(VU@rKRfIz_p,z.OYH;OipN;CGIk,S!n77.OYFxFbs/vvwvNIHwOuMyo:Oip9{wc3~bTRu@OcbE29mimUW==,~bT>f~loo]*M>FTCUjvDt.O"BF[y`$cMcK6C81yL~ul*x;OB<Rbafn;3OC2YLBAE"*cAA)M?TDzyi6*9yyD(=yD@Q?zyZ$o86n5g0FX")AwAAAAAAAAC"1[[oO.|1`K0*{ZZnJD/JP`RdQeM8rbCx_<wv5lFHOR;($ROCg<t(`d^qhjIKWHMp{idL|+)pJ9ZNi|O4+Fx(=IGQ@(dX7nppa1c$Pboa@I14Zc/@1f0.i^/:j~T0LXh)+?=2wy>9AM#<z&q|lb5*tX?#6i|<A0+y,.LcLAbPKW7smW@r_I.>M;b+n/NmGK8*_O/{zDcTe[,VtF5}+,!~8/9mmLTJ;p!x;3^**GA+~@m<#bi^^5$^m#6"ntyRX4>)ZZ=?8f_$b@#Noo]RLZCm`{Rh}WvrYlkOFkT$fwy{j{0`Y[Nzt2;=%RNP3ubEF@?Dy_8+#*7&t%u]ZU/t)p)sqd8brOI;R~9;0ln&RB(.!5]*}/?{nf&htr~+;#2%=cLSj]$~r2C83GOS,/YnTn^t1hHMITD@1s#V*{|QUIG`HzoqG6|E/cAt&d+?p%{F6>e*[KQAca&^%1jP1mBV1C<|s+*@@#47gFT_Pgh"6<:]aaSdBzYL|Itt@u$)px^a4Umv%6#tyk/aB*l9.lD3cJVnSW^J#;Q5(n/%LG?v908+xk@M6BMOy@)D#tI<G#L3`.I<D^s5%>p.zHdI+IqJ3Cp~|r)p0W6=`NswDLOY^`v$T~LH?x/*cI/l_<sb*#7PKtZDepoZT/h/M?VB"L<I.!8jwIS@#!S%*+:nry6F*6()r#zXv|`.dHxn~Xgg.O$BQz*pWJf5fW}ci"m4ct{@v9b|N@{bP]fcnWB3mdX2&xnkS3I=Yk=e`1uYMSV(#)Q4KgK<B+D1D+qM}lF#pRe&1o7Jv`j*xmtO>?>a(I(h%@Kw<}gZ|I`o(8osR+4TF:+|HnLy@Pzqc@x!Fr^dzE~!ySR$~_g_^{&!;tD2aU^+8<zU=84Bj*1;c4EJa/S1q,Ddm$1cs~STzq&D{7Uh]K?dIMX1Zfj,Xuvy|X2{0|cms?C:=9jN:}i9sa|+*|5Gx&myO;%&wGZ++uJUrO2jbOgbj67LP*M*dGnjCGX:qMIg<+>S<9bxzd2pRPU!Fi&MWQ:+V`3MY^D;;&f+K0Qp>2E}U<Io~?|sa/xR2`AIcQa(uQ})e}8lTqCTIVWvUn&zMJiE{|*;w(o,$*uXRln[:Rmqbh*;oob{a#v1b8Yx3tXS]]bK&q@n=#2(Q8(sbel`le_:Ztb|Q+SA)x$Q6P?C8lj>}g6;4VTa!w#eY7~yMjvP%rGSw_#<A~YtX_W_<5b)"Dig+niRgjG$GI9Yq*yR_oQU`du@!24G%TKobN`MzAdUJ3,D9BGz2MHtNM26h._k3+!pc(QRmcZ[99q:Uu]FR%e+L]q3F=@Za|dLi7N^.oq@,ch+Oxu}/WrkP~<fV%omsrUc.p*e;>#:^^XC{pS+l{p1WN?SBmG|8>1Ce8/7KJGC<4*(i9MDe3y(IjuXka,pKDI>F#]Kn6cJ1IdnAL]4ejvjgpw7zu.X?Etm^iOdky/x`lJnT0qU=3N0MON6}1IGn8*r/8=b1ugU0DSqgCdb~ud)|j#xk@oo*!7bCg;(xJk<[o8#@iwH?RvR&!RXQ*q`%@NPal|4WKf}]h//av;K:9^|BD^sW)rJqlle`~k:lI^:hwlriV<7PA9[9sDGe{_$QxxZdh52gsA3}]6d+E}CkO,|fG_tJ1<1=a~=M;lf^ed_Xr5Lbamn}N~=+z,]2Z"P4]8VO*o$Iv~]/_08uD"3I/Cw%WuB]!c&1.k8E{h,wW2r@:tP[RN0`WE[|lic)s|ujOr])1Vq[ol"#;EnpVRP.bY_Mo;i*2^x!m6chue{*d!g^?g1_2*hlJ`ryxnVvJW(}]StTCUZ`k<Qkau>@8vD{]P8[tivoRr[yB1avg?g^Y{rl6`_lt,P;1m|0#buN/_)2U$Do)8znFmJJyA`,j#:]1w$1cR]uS>|!QkA)8_i"B7HT{LLc|8~#Gjo&aam0X(^8lnSQyzET^`Lt@/jP.Vg$+t0"RJ_UR6)KX]/U!h0<8]2UcGxI#ExL(**t81r)%][iGNQl2}~Z`Pc{;Jca^J&d,)>eMOY!C,r,5#1i5+L3!^x$RUFeX*_6u[03.7aG1/ngu(Iy#_MNx|R9.myP.LZ.8n9v?|v)[6Z"`eLKHQu;2j~FT"&]y+>637,I+}r]gxKz(q`MVF~:v+R4f)W6k/B:.iv87LrZ<s4,<^?u/38^pTZ){_B|pl!;~%nz*T6{B6eqpj~gL8a{eVLvj8x+TiS=Fo{,y"N0@.J8YdDo#FL0w:d2_2nJu.9)~}v^;nFqIlNltdFW1rF6cotkKGmd&.C[&hh@Rs3$"wDhTJfR{Cf0#Qx:_~Hji1.{B9~;=T#~p}SXQt4Zs?D*(wN_#=mI,46[p&&t1#xL7Sj<%J+h?Ib+~?3~NFg]k@y#0A;q}t%V8^[o1+8)>sBNI/dN,za*Ryr|ejXed0NHO3%POn_C&cnc~NL7OL[J4TZPpU(DXiqlwXE3s6rL9RC!:nu:iqQ:]X=.IX1OvL1Ww5ZAKJ#G=(`;<{`m.Z[Y`8K!.1=_7$$NdGZg;ML`u6,LVQqb!Q]%aFea.<X[e=MRK?Rr;Y.w9[1o6^_?$go}!cV$G=hhV+7Z_ydlYK$n`2U%UH^BlKasu=i.Ap<)t];$wzz*_gO{(=K$Nw/Ea/9A!1yZu<7aeI6rXZ6&Z?8!Gy,/ob>g*c#!|eQ)74v}~_7%UPA]0m1ld>29>`%aPv~.4/=qm%fu^E~v*f_i`$8}Ih0Y`HfUc?6ds31pUwuebC$0.KcVwhDNh6>piv<1;L5>/J[6q+nI8R2),.1>s`yQYcaf1=U(;mPu"YF$.Bi#WNwr<8=G!,`fC+.m~!lJf[0u4SN.K_jS/ngD<tz9$+6=EP*4ovbmx2nz#GVs:x@OySZb+%0<|Af%ee}_::>?BdBE3$m[v@hQz9ZUScFciCc7=5|Cky2.(14~5gEOi=},lU4=Xj?ienp9a`PF6KO{a*=Q~~Qy!:l{a#Px`Yt{dp]M:hWFG7e)@P+9,e|M>~&Y[Ph/~gImimRbW"r6M*n~GsiMEH0i&aI{Q/c;iH2e*7.7rh0Zbb&SG>x]{DLIM86cVwo5X)u)`eS%ynG?=+D+3l_,)&g~|E0HrhgLBju3r>s;./o|`#"2B;VI{u{)B(B6n/%T!}rLS;Z5L?R,HeSv=a.=v=@JtG~*,6FxPkQ5O@0Aw8P<VkS`@P,>b;(5E[O1{}"]9)hG0WRZYqWYQ_Mtl1b&s5w(Ch<EmqyQE)*4s|gu.8S^j%0ls?^Q}uGkUySdNRiCnXN7VM%?h<u"~k7<6F8|VN3iy|3?]2Wgs_29W9e&;a^kGge$#XAs!UCt4[2`|q@G6+0(8~}8ujgmtl$~(<M4S=*dSXZX%BX~A7,;Z>{&Ju0y4NS&I8HrL]vc@d78G7ULT%{mqjs[&n;o0e4D*av7t;_[zSJ7X.P]pww@]ETCKl`zR61bu)+xF[jNs;,,4APpw4R/Jpn_~,t>F6^}22@Xw;^g%)}gF|e#"nG.~6>7G}@F#/H6FWNVVTPTW;7G$)p*b^C~th<6xLL0F4s]wpb)R:B(7s@S$t.H(tG]2=0ipz39N^5gME_.h+^">bi@7JQxW))e$vOa%wTxoOe"3^X#g#Bd0(d3;+wG59XpF%(BzaY4{CEK&WS>ipkORGg3$|gn=BSkm_q`,:."..8|uR`*{E_%6KJ]{tuQZ&JoSc)J"K|:q|bf][H_YF}$0gCx}_)K%I5U,8DZPvX0+{e(|{i6T_IM<fEiZ7_,?Ux3GB2cd2|#+)L@48:7$QQJwM.g_GJx_U7BL):<Rg#B2=q(:JwhZKO6Z#h$L*s.lA$9l),bN+lWNS90]<`<fnpYFGIJ&0_*}bEmPgowdTI+P_M$7!/*qL.W1QXQ*9|;nABd%6J>|Ugx)nhmD`/&kmY+2`77cD>@W.LU<h&DS=>f[j/Z2pOh!^s[w8Ka*}LR%iBpB7o>S]o)Ngi*[PsXxGw^YM`|au5@yJv#@.EOc*2zKw[@jHrG9SSb5;]B{"u9{#Y1E&yV!<69vwvo<{jyQMdT20$?eM2OiQ~Rv#;9][Pu#s/JyJxq8)N5ERi?GL["cR:`(:%Uw`c|DPGs#QuBZX${4I:wrTq.qLx71gEkyzGhfz?ex[w$Y8f78dar?TZ0u]o&gTp3O]D)EL@;8TtVEgfT@L[r0Khpa_80c~DubIOWL@s<%PiZT^awNqIrG?UUK#+.}*RfF46Q1ZmJ}CBswPC+3]W"w00XRphXQS^ZbxNC]0L/|;kyOpm7%kc+H(|/[=R/.CjaK_628&.).Bf)L*nga$si+OD2OO/L*BuE*|YHK|@4blDy~jSvPA]fV}|qbnL57nQHY;Z7uXZtDK}.`qYN=jbryW}viO2BHJg_)ny+Cpd[/;zA@1{|MPWVa[vGnMZz5j*rY|LTBz&B6T.NspCo|S]B,]tLD$b.Gpl<8u{$Z)D}d8%61zEwSkv>9@uY0g[getUuD^k}GAbrPbY(a(@onX7JZ;DSS$.IKbMQm:P@1jpmuUMyZ[U0q4ZGf?Hb^od(*.]`1HuF.)6H"`@bp5VuYyj4pJ~<3Jb>RXU_q:7*ZVIuOXwZk3u@J2`j}DIp$0S[%:]/hC/U7Lok{A0BxLm6;6*_iU*,Jm}Nid4Z$e9RUH@GW*cp|S<qw_7lL=pb/+F{ID(Q:^Lb.yGE6Fwj5"J8;#=wdX+n[&?yMDNzRfKWwN_ZoboQuk,HxOK+("DW!;ffktn5,WB|N8,MP;9(9vC@h|8~HuW/Z74uJ,;XXD6a2c0.ez(cu?G*x$(aL_@|<[.NaHmsdXoa2<)>T/l}+LH85F:8Ok@ZE"Le3D%_U_FF~p:A>&5@HG2pOmLUdq(3)B%k94N)3F+U:4VE!F>KA[n*Gg,%}#bjcx4@%`|!KeG~Sf5l@/*/p_cA8l.B}jjQvmpiPwTc.oGOBOYvX!vBg^Te4}=mA$kKbc6xag#O;s%^PtsCf9.qnU9]A;syL+TnRGD"w2]KOy_($Mf}<2ovhdP7{<~<JUWh_5}?s+4h:E3z.`3eI!w!dA5&Hsc6SrNZK8)y>n_34dl*O7aI}WZUvtWTxgNH0j:q#`Ti[7,Eyx~{z1aj/aGe2th!K+cplGSrJdnJ.,cZu|q?8K3u&pNjBwKEHGdd+]:4)qElgW:l=`/}Do70@T>W>7*nvPGA":(q#eeFT)tt/aXHVo%Om$a"Dz?jZ`K):%5B0(mfZ7p$s(h+_pybH9ja4>:m,1fCB<~mg~`&Me#$[Jv%(cETCqkR|pV!C5?P(FEm!_xa_lD3Qu[O~h+`E0vLLa.&@^v)[!tjjn[efD!~Dq!t1tY{JLo]%d#WdopVr@ZT?OE3P`Kr2;~^4Uu=c>B|%o?>YI0`?8i5>b|UL7o6!`3db^m*F|[XC},eJMWHTbb9+`cOJ`b2ec<V*(s:uG+xub~;6tI,|SD|%;#PnOOg<nsL{^GuL74l.)$k%eJn%uvi:Q`U0s|aGt{MSta[&f~oJsHd^tyNyNPN^s4+a^+@<N:!}5n9!:b*+cUXE#Rr=~a,5;dqH7G^?H}4,7h:8kU;G2z[+/@q2C60=`;?{tG.$0i+h5|X6R:m.exk~TcXE85,J1=7$J[:vTXvI#?Xaw.f6G,!2I{T^,2TgjM"OQ..!G}I!TL~(Ml/CIE(Thowe^ea2hHhfG;+#~]4,7+Jg;b;SRN7:I0oICdc%5@TfKg^/{|7R%!6%I$5N`hNuLpc8qV"x,(g^96A2OE/](}Ebo07V<IF]piN_s#5Iu]a3`qgzMLr$pVPuiC`w,`VYqHz#M}`Uz{M`8=Gh3H!cy?Sn2HmTN1k0Kf63sX<_JLL3RCzMJ~@Iajmx}b,P5Ky_kf]8PY0/)Es2AzXhh|#V?*DEKdocun@F.MqKP1ZDtX2Uur4z7K|OIsQw_~}y^BuP9FvpAh%^b*B`%D,!om~$gBKLyz~6q]qL1ob*eRT.+VglzCS}e5*j5%QF_`dlka.Yo/nRklL(29DyI4)`bW,$}Zy(.:QCG(*Z544s!*D,R6sV@BLKKPDbcGv|^]oA}`CB8o@f.7I|G.$.gjm9kzKe9Pq7MKz/PCY@?/L.TC#]7RRfWXR}i?(<V=bX*JZd@EH3]zu)P>k:4Bh/_s6*ARY*LVsUi3"5d}.kVtRf47l<&:=9j&}>Nw_d|g|^y:m{^l7ZQ|m:(J+Y$7uM5FN!*0E*#8F>F3=11<)R|fG*x>)*=VAXo7SURs8{uXx/EF``h`wao<tp?zmC%%Y<_|<1riZene}WG6~?KT)`bSpnI;w#Q[kITY"Jp3L)Bi.h`G|LR~{2:e!(E1~EH#s/5hZ3axQVIt3gOidMay%s_g#FMiWeV+ueb|k|5{"n@FdTD+o<WfWtsDULl{N?eTk|#H(J%TA0:X&21F;:[!BuHUM5TjKI6D9U1*<#^oYx.a3>&7d]JDWF?L|5?#Hcab2NK89I9+B{2Tp{r;ff,uJ1%7l9zqY)cvJ[gx+LomogI?f96<vxmfV|Z?7([68M7dIb<1q2}F"5tdvY{?~C>@i9X%6G4=3Y6nP:K],iNLnhA*wr>w.~VG$9e5#.gr"S(JJ$/M%^miH8j>*85Hz="OE&!afMs"&s0|7<ux8y*kyCOiO0|XEZ)[$WWdZufqm_tH[KwUjrM&rUArRV(5c|fDeE[SHD*5$GO%)5&K*LBSZ<gUTpu;y6f%MKK(6vY|mqQ!xgd):/wu|U?hg7xbh)w%Kz`l64$PIn?o&OQB{%n:cO^E8Vb[}fG?@/evem]qHU%1?W|@Mj,k"dLIX9!Ivt|*9f.f2{*D:SU"c{%BR[[cN_r}hJ0d6|oEN_|/#zgL*A`s!wRW04=&vC=#<sJ)CFx?/Eu[W$&Q0f/X=(Ts%de7Ij`/J?,i}][=Vs%0$HKRcTrCGo)51`]u}zToXU&LX,^/G`5R=wCtDF?#;TjN?%:qcqzD]=|Gb4yXiVv$zNfEnza;`o<q(Y=1Y*]m*,yy_2sclZ}R^4~$,2}4M2Q?O~T;Tv`pA7W0z6>F0aRs1>,VP0<#}Z7dqSK[pji?~}u}vO(um^U*[s=}NBn}~Au6w/YzlL^PZnHECY/y`mD"Ud?aFn]rYs83yX~FVwKKr`C&y_%N/"JyHSKQ=ua}(Ovohm~mu8BfFN)qw~G0<tzdg@gCaIFQZkk&h17R;|LDF~Q]P?~)@+WBjlR|!o*YS!vy._:zu5>CN4?{6`lTd:m3#Q|&.PUtxnuEa828Z?FNR!mZsnh<V)fBQa/Z>ef=k7~|%[zPU^ezM0R~%0*Z3TxyIZoGg:IQt1NU!xH>=bBn9]cHMwNv0!YRBW9X"SSI4zC{{w<=Oawz9+d1~zv0b2hCJ/51:6LI&yyQrSX[=&E$o3c!pxm:7?|;sDg{C<)s_@.Sb41cWj>2sKxDUS+H^,}U]56,wK;Tr48sqY_[dIG,$mT`s#yfN"=zX?J654J;e{Si=HPp:z$O;XL,NLh0DCnJ}8rVp$Mt`c9e$.+}b7.wf#Dxag*~C/>9||l#G4GSEq~Sldmsfi!(.WaNyN?>o.KYq?%rCmwEa^25Pnu=^_a)1nz,OmF2TTH$3m.aVUY=z^*fY#7Y~YGO%`5&Ffd}Pfr@(2|z(R;X3(FaPsoxJ;Lk0Q/C:;]QjT"NAQ92DK![50s|m+uq`gli]C,ZNW:nUWRUg2`z/|#}++]iu7BMT*SdWiG^(XM0Fvs(nYxZvc,M$1D#XLuE?}?m9M||CK:s$3}L#!sS!)Us5W,?|26I9aldL2U^;oU;]=PDMQF{mQe_71LNI:[,%C#M1k[8A%y{QA>6,WOi6*nC+v@F(FP{j/@D]fmg?v&5"K6|6yoOxb"*rZ0=Joml7xt"NbuW8nqRSb31g9_gi5DxpeUrucxjS"B[:x1i$NiN/Z>|K(F|ve;Hvf#.,B?*a2OKa%,Lu~K_C^zibHE/=ijMw^stod66O%vg9k]]lr6c#<lcc3F:hpv.fPGfOJ(v5FEvYG{n>@H@T~WX10Gn9BnK)vub]@c`&+H(9q6,qk>N"H(]lI1OyKw`%Gg4Xy4N/%!H=RVrSItk%nkb=P>G:[$oJpj|X7GB_L<Z%"@~84xc~*Z,z>pX1zs+1wVm8d7]lx#qwuO:K8vCtkKKg#rX;n~dV~7+Zc0u{1DAuyStx;uJ<8x7k|&TRavWC8vMK.;?>YRE{aMq2(_TjaQ<J(epN53]=^S)TRN%2+S8Z|!Pf`CC~y+^`OZ,rUWPdO#:OLaMp=G{AJ1`4r1Qu*fB#R8)G#51q0A(cvOhjH%6K%fMJu.L]oJ&|X*m`D`iA.xvCQi}ZHEzgG(n2Ww)hU!|@/sJay<&6e3RH@1h?!A/G)khI,];,lnzGgof$$EVTNHL+j@~^wC/Kc1nVp/16"sr|H=)O[RG+qbAyTH<a8"U~Q`m_EY0}qI8q)y,l!T?C$0}L7W>"BB&N$oXMU?j@RJr00Wxs|5;4]}9~#~2:~jC8#2:&,+^8:CO"ziEm*v?sbf>I;aU7!U4<@#L4pGH{p)tCmI9@6.m/j=UO~4Z0eY7y%43<V,S5N{B%bd|v.(#_P+N>@=_Yb5TDl?YU(N{"=>f!*L)p1rbSAtrTLY8T"C|ezj[ctC06+mL%LBo$KB:Ie:Bk}wP@#Mm*z`.A"wpa(N`lFi=vaEKdrOqjlSv<FYaYEe_htBwEpoJ)P2<W_GzYe7rv^f#@R"`g+:Dw;2[7>WEh3s5NEQsmyVt#*RKtL@$DK;M_6%5PNkB{hQnG5G[&wha_X5zrYW#2SQFq(U<h(A~(EaF^,]>T?&"0#LObq6Ue=f;BuMMCE7Gj4re[O87cH6DA7B#LYC@DbYEY8Q5%)?W(niJ=}|FPKNcV:vJL%L6MK{T|0=cIE6`&{KCSkLxNfD8?jyDNkQ8h1w!.$u1`n7JQ+&a_U_lP_8bh]mY6FUy_3Q/G^=$_:_K9Z{OX|*9u>*@L}Ft9F|QN3HB4}Y.&Y?~"a8qjMLh2nJJ$l[)$m?|93t*vDq+;Z]P]obaoxWlBY_fnb7>.E>;wRFyLU^=;Djdj;3#/0UG0Ka]ctp<,C)H({?mGk^AYLk(xZg5Zo_FAxJaPj,)$=rSSOQff4={~!Cx>U93;aItu_="2Dz4K2dRZ&|xV*RG.zm=F8|g$1NeXL<4yWI!H]Z$7W[ma:cWQ!/04+T~nP%my>KbYPq+NXOCn`zK}F.`nA*WA6]6W}:qbF{|Z[kH2bvjle}GAr^8`a&N4a`qmG3MTq#Que]w|Q23uZK&~dYLZt$2%Zk{"2<jfo%&e@X<#WkJ$u9=#)IN<|eUbW.+dTGka]:ecLYRy%9rz`*yNoh<%0h2}LX<HV)xH+d(92|O0b{J??*@nQj_]WPtGw2yU0K4haU!L`Ukbe+fS,Jz`R;dpp2&+sK6@`F#6&UHlCB;dTzF@K,awa8ELUh,:1F}[eo4xfx*1B3F/PHf{OJSs?NWo`zvC,,&>7%Ts8.{5P=`GGAr4Vy.vtj%PpDXS2gTL8_}$&R:#{C+gi_>IS[BJ|RyQ=ht*Y4jXrN@Wr^k3+U0I?20Z!~hb;H:m_>(A&^dvNe>n~?%%Z4XA;M&d<.a**SjgnA=[=ye,I2vScR=L;nZ==u25)CN)*FX<*P7,u7D[I~OoWDjQX+lZT[M~[a5GNfcjn#`=+)9+32vP<iK|zcGMC(nP@wMy~j[/Q{;hr6^VDjs@ve[6r>]enz?hYX`PtY,U+BwYZlv~RA<+,h63|R9[]KK%jK:WLhzSL{f35/CRD<CB|),:(Cg9U3$}P"k"M/}o5]5f@DE|CLNY^U+!<A/ulYRaP;2#O3aC)w@7i,0YH*DC8OFcjw8M.+,q5WUYH"j:Eynn0ib9[a;NH8$h2H1>{(Z"BMBC?cJX+?xDxdxCFT(:$RFW7ndnn5p9FAzxvsMIbmo,qm2$C~l9P3nW?90D:*kw?"+vZ:Ak%UoIt;:|Ono]D);&;KxQi[h*F*kOkj`Ykr@1GyNPp1,Ui+h~GD]^W300EkkIi#u/ReS8oVvVTbda9e!5.GQKhm[1/m3qB`]~onrY6#gW&?JM3K@@Q0vuZYz&.e7(0PfYGe.3c8%Cz`rPVoHoYT}pdo_!bJNrbY`4n!Nr!_kxDvE#%ACF35`;=QveG"LIhw.}k/Mt9W"}6sM`_M3!{Q,0?wl%y/ss%Q{fIOP,_JL/C~KM==Cq1WXby.QVBq7ljC}h@qoMfPz3ROH#k@R5zIaH;8WO;J_5=F#5Z#iY<q%{=jY=[,wSAPGhmg|S?I,O$c?q`~)A2Yt2}jL?{2!{^(Yu~l}{|blMfHZQ?D][eA]3:EHOUrqA4vsa_*_A:<4gu4vSsSu+]/2Tn@HLCr#,Fl)o~nK=`LEas.Fl+MH.qCIkf3Td%17|~QfvD0het7=`}.h#S;Dy5Txh/I^]Eb<6};]hX7Mq%*NW<n8ihPs&qOuW?4P|DibbHY)U32]IL8gC`!$Z[/6Q<ort0_,I[Lgzad*4@K95bqccgbH"2kusT|DY^]2b|qp_=Rolx2#=7}3Z~(m7N3a"n~NUg`kH}aJ2Q(2s?qf+D243^08(f=)QKo@FC0BgRyl:{&At*$[#!SX&4wwaz~"?.bM{*(LbT8#5iA*[ou?<o56"PW{c?^B0t81$g/VAf|06Xtz!u@1TIb&R/ihVhRn]Q7P@5?/;xspW^/}=o0cuAe1D$@>(*p_l4a)fC^4rj$2Bhdp=kB6~(q[)||[O:,][U~I2&]aO%N[}qL)yZaMaNFHpBS@%z)gYFMm3rf3;5/1gLSDw%B,(6xN!$dd}<D4UB59u[pw0Wlv@sydCM+^7>Kg^U0|RgPBy[wxf$w^lqB_@DaJ0jQUD$Zygaftlo]E}MBfSGI]|gqnSvm(OEIKozQ9/^*C]ZI_rN5&q;(HM(pk=dW9#xJ~igNZQtR$XjE5&@|/{Pb/jk(gu(%bdrp/R!s8WpI9$$!}1gQ~J0B8J`8X)+$(B}M_m0rR_+*%5NW]aD2SNd&ZuYe$AI[4}2UKeK2JOb}e|t$r.OQ0LF.c$fj}FC(WnvuenlvDu[8QepF:"||C{(r{guzft~KN=(=p01[7$g7);okO#2!mdBV{DL[p$c0BK=.Ee4Rd?b>KI6w%kd=YV?Y=2eq19>it=;%w(jEInJ&6i(T2iTTtk2!5`8^XNI71]Q?eK>gt@#thsC%Lr7z9:,9WT}yglB+;qzau`[2[RI75r.9WOKyiVVWCU:I{PEQeI2I2]`vxzo4$]kR|yKuLSEiJ$*gf`8&a[8Se5E~c!06?^2bJzxof)w,gM[,<zv!pv#SB#PVg2~pD9c+^=t5v.D87>xS&6hlR|l0gsH74uU;w9"2~Ys5RURCNl!Y/+D1a9DTsMlNY"!C%>@gIi*iHdw@$nE)Jx]C[DC)14$UXWH/s2Fjow[QECQ%S]0_7:{xKjiF2xGX_Bj(zf0B9UA1_6_W%rkn`5h~[ST=D2B)6;?8jNQ==J,n1`@p>X&f6+$hgUxoQJ2r@8F:P/!u7"j|h}Jb@?Rjlc+9[e~UU{eq6C!VM(BJchA=eDz"d&hbI!mweL*GayfXOTFKvOB"0oYLTC@IoQsMv$fE]vU+Ujz7EQWp;Pf*|xhqK.jsX*AAcs%&FD&S5@2Sn>=qO,ZJ~]!hj"~mK*|qTrIlGeT(DCXA1+*Zmf]t_bZ8aumlv2<a.BO#z3&[xMi,@tN2B*MbBH}X$Tl;g=BVEmN$Tx&cbACm`I^]HAjHQN8oZ!Ws&&/M_)5b[`XyQw2]:BQdM7,s7.N_|<iQ}6Y3644C2Xf,>ht?ngm$nNR+4EyBmG#(3`~/`@wjqn`&I193}qD]pNHjWfJV(Z0pmP|=5x,X|y=x^@I"T.qQt!QVZ&07~BB+scWL4Q$Pey[}rnEVhmR~F)s#&/uU&d=HJ}oLPgzGDBaf$TB|})34PtdPaO0@D"4NvOSUTN&K8W!++i=YDc]J5eh+^oqvFF(d8rNkt7M^h9H3r#}6j0a`>&6y)ZBy+Mx+E>jOH*A;xt%DYv&{pQ]}+YR(b@&do&>);iX)R0=Q#,Dh+Z0XYI`DJav@rDI5pG36/*98&~xYfT<m1.Re&Dp4F<E}b#=,difcj:MY3QXjPn=A{o0|GT<ga(1<+S,o"`1Xb5nx):ovl#sjwr1~)>>L(TM[>f25]TyL<HmWeqEEC6%oG}O25Va/PGz#i0W&&nG,CI:9+P@M~qct..73^]y0_`F$7T3ikYs7G~T0|yZB(WL4p?kDTR+d<cx8LC(B<>v3U<WYNioD3o$P{}gX5nQt#HMsAQ.C|X%jKix4D[kL/+!!YCLR?M|Ub%iN{Op0NR"8jScTqnqZiLw8iQB)|;$^|#u|x(QW9*@v3Q.uLg[_fD88&b.`Bdnx!%v1g%.4]2Iu"(u<QNwEFPniC$OU>Fxa|OzhZ_dVf5:2Q]FD"FtI`btOb`zhT.[u{K]2Z7tH^Vxc>E18T(Umm6Ok<jF/rQyc#2*xOM~1COy=QE=.wbho3?St41sD:jkoz*+Zx,sur0Bk&06%c6uBWXhMK%>zbQ,K8/N+AL,zi7(NB^}.X7&*X>g1Wt##`kxNsJmATDg=VvjeAH4@kY}niHP3)k!#J9NDcQ|#CpQMn~U;txs(~$ZCu>_T+kfxaCjLCZ!P:QhO_zs6j:@x,FC1{~j$!0EJ_;~QSh4L;})[Ps%kfC_wn!5:kt)Y#}ZNuE/|5h)*~zgk|5,L,kW2;_/H6fHw(%hzPKMI_%J3Jx,@/0lSl7nyPY@h5}bvTK%2Z7V3j4q%Mk/?)C.LwaU(%J}tk@}k93~}!OymTRmpL1<mf<2?jF]5d6LlH0A%lZ"a&yj_4PLHDXX(z}0j_=gs^HcJt^m:%QVF9ou7{]tDX>^0cT{eA#wF47Kv/8rB>Z:C5jQK|{X&;xFJ0IMGe6BK;ptTwz_P63!+R;[FPdp@&hrrr+]U/W}9[~>q7EpT$5aE3oBpMe=k9fDrr)|w>SR1;E(OElp7{7zII`AaknB#{B_V%3WF_B5q9^W7A[L#qNUoD($E4ZsYDsD+h{em;QIuAo2BV?0~Qd3{?fK)!a8vCE?p<nm,<|0FHW#qP@8CKHZ[%PH,UKn)9@|Rn@4c5Oa!%;1aHE}%^o:Ut+f~MK;=$|vr=,hHu&EVy{a;PBmrlz|MR""*o,I^zdE*w>zq^A@Cg][}An/N@`rEz|4<1)>wLU3NrxS[m2X,s,(m|dH{koBE!,eMhWZhsw0fyldQC|D)i{._GLu+SN~r~m46lI1het%NU)V$%4bM,w"JT2MVNRh^|Ym]K=N1XB4{6z@UWLc&:d{JwU<,~%BHb<,`2wuBW+mp7X_]X[TC{.@[Np;X)M>W(miwR#Z,=6<hp.`lw2mz@d2p%<p_"2M0uv0aja#R`rgk$]ZBKcw^yzbF4fuSW$s23F7W!yH,Y?!{UUi4HI8TF)l:[/EYK>v*Q//FF!TqKD[x5`RFwZLDA%?w.d0?S=#^,WV=hYZE<n6eZ//[lMBn&iVa>u@dcSNeu+F3s8&LlDaaEq)whZ.eaty/I%K?,(FdFVWR?=+ay+s5_.!v)VHE#i^|e#>H$vN*COqV~}}`H;1xH"!XmJ_k[P>d85_1f6<0(nSqQ&]@<a)C"E!W_@U_8`tF:MLt2pz2y(>LfIvmnzsR`aB&yqK..2c^n,Z%P5MR/o/k10:QVg?]j/>aXr>EU!:MLum#l[FOf|3;vsa@n^pgmd"X!7,]]5fX(poOj#z9oS|~r~ekdqX0*(:lVVJ_nRoHZ$GHjT5N3>B_&m)+{@E?3JOj7)a1#QCgN:N}#g)I4Zx8"BA`9S9D#[BVzLG~ZNMXg/7XZ)taR9f3kq]3sb{T7FNZD`:1A}&gqg2EX)nN!?e6zm^Jm;c4PD~)zYA%)j>/6/$U{%`(#UttRZKI6wTR=>yqye"O2[LXgp<[#A(JFxv02(VUO91`@2.D0<b,2?vR[n7c~68_^.:nJ}.bdF4CK:HC+wv;[iFfh^nmb|31%=p*]Z%PL`iQP*I8<ydPp(}RkALB=G}iD_8PAy;f_<%oIN?Hpb%M!)PX%R3(6(7E{qlw7Q"xFJL}`Eyx4%)Y3eX_6J;zo+`6{d~#dO3SW+tz)~BKejb%A{URp:&4/?aNOyWBqErMUpEUGn*x8/.`^KUCFI/D{#VRDkuf+EfaIF|YQ+q7g2+p;1N.^D!qfJBb7xryk4*HjU;}QL40);AeJkUe}KFthw8t}kpa}?M9}(!:XPf@T:X8%$i@da.ziu7JnnulfQW{u~,T?}2MB]?E"JOxp]Zc1`h"G%Ciufp}N+dDCNna^%tsE$z6H9!X?_x~Yk$OoFQD1~}:c6rg"MzQIL:X3]W+/q++FwC&&nG`z!M.[`nR8FJTN7q<;{$y+,WqRKhDT=aF{ZlTgs]n^Cf|Oik#<y:ivjrtIE7pvn6F/sf?D9SOgPEzW|9M}%BhDBBL+R$bQRe8E%vx?vB20,%Ec5BNKr.m&8/+*j6{=YTgHhexzllFS8G>@Z(#T$BxREF#Cg%=OtYVu((dF/nOyQ3.=Gnk0$EeJ!H0*uS=()S<Ml~Ye/lqVoB|y#^Y$lE0pDwggboIYrxOL]/Sb>ALi(B)DCLgH}pdr*3ivWU]bqeKSS~,Hi*]/6Fu#a$w1l~IaH?r4X;1.EsE;px;CYb)vkl/wH>t3~wE%ECnMHa7yu,0|N7.zNNu3L&?P4"4_J<Is2BaS_PwYa}EZ.X&HPeLi~6WjI(1pP=N7=ql+?^K2UOOcXG>/lM!4QT/R}%Tsq@I7_RenVw^YR|Iy)wEc!FWL^Z:{&lK$m/UflBr[09=z,|U}Q=NUsjDot53.aSBJiDJ>gy1*<fM/[DlXc}aq.xf_FS*2Mw35k;}G2l4CPul_.V~69s:Z"zlg$N.!_,egJO_@S>"DSkl^o=X<WjjYOFEjz{[Ws,Vn3+b$+A<#2v1}D^]"EQU}pvzp><_R4qx(o|&CYKJbKLq,HN)Iy$`!d}<|/}kJzKGn8[UV8A0cI68*Kom&X0$h!O}J$RnS|6eph?xn*a>0bj&0;A]C7&6^2jImVA,@rV7mMl8<`w1(m<sd]Fk]4|6@K^aJ^~~TTM]%N8HOgM_Y)=1lG/ZZVSR}0#c4>lhc{cegeWBC$dZmjKn(6COk%vB.YX,O2KXsS(p|]AXi#>Ylp3c@SLi[`=;%)]<$/O)WuWR5#a(/Z;NqIQHUxeg6jmle2T%mq;jjahA0.9jSbTH6/8jVyBDnmVEFFF|~A+Zv`jL*Uw,ub~j8HoI`cvbFrY]"BW|8$gq#fMu43eD@@O|Sw<<Qa(APOi4J34nw5gdy6}Pk_C9O}gh%2QjL_TqHDDg>tLNR]bX!1D9yO]dDBJd6,QJ;RY=o2FJTA!?%ePue&SlQ=7>]5eCL])3,5#~<48m<S94Cx<m>?!bSnnpkM~qUR2;J@}mt.L%[<Wac1tF!S{@4?:w<4!]]>.UTY:7X/+`.Ul<#j@>m+kQ+qzx>F50Z802jKKZlbv3<[rn$EM*l#YqcMUjqh<!A8U4?,^+EQgfm_95L[,<WHV>!|_k`j!x0amP:gnF4rFDqwJE3yR,#DjXKeW|x>2*Es(CC$W0}FhW[7NFxX=d,U~&L$9R$HOx?>AzQ}EK1z@{!x5NDFv_=kJO[1|_J!jZjspMCE.ooHx[_LD,C/u7q#d)hvr;kwtRk_/E]<Y8b5$e{/wH892w6DJ*uNIW>*Cd+f#0g"2FL6`s*M3qRUkLiWe,}L+EB37i@*$]vlTh@["Kw+F3zY}l$KR%<GX^C74i"jMzI0EL2Ou`quuk0sU/G^wNszJ<N3o3k:/;N{|<Q#.^X7UOVdxu}+7~!`fk[`Du?Sr4[cYGE/Vg]#qrW]f9BUHNajo<#5L%Z,_Yla>8lkgyU6oP[z;hHwYn%L4WoiH+b+.NYlFR!E5kEYGtU]r,3/|bQISE`P95VN2SGSbJK[Q/ZjT?F0Lj,KXCIy0s,6QrXhg]LCJNR1eZ2{~9fyZ!?PEtLOsQpL9MPxlOo;rpe]3DTSVS2M?{mX{ZH%;CRUFx1hpPK{[[6hJo{@NTES.qf&KT0~r>eUpo6z<+h/7|"N#8J*(8eX,wx;~r7b}C*!T&~KzTk:+(F"In!k0+z*RY7M({#vE2ap9)q<?d54!8Kz5;7H/!;Y)aD!>4O#BQ]RdTl&Std>Ox_~Y>w$~5+6Ia{Z@IHUk%Xl:V{Wsn5u[7s$H6u2R$,,.Ew_5iugxBPcd)*To"(W:N^}AwQicF$]{bI{r:;Ranw%C93s6,|kwMBIakH5u1$)E_wD!G;{xGmau`USF|t/8Eg)4#b!BJdZ%vf%i5`$LGD+$#98d#~j_cnTuS}IL}kk[@+N_IvVcGkS8kEub}[M<.186jS.x:Zo.hlSv(P^/L$9oJz1EYDN"4CHWXe0IdacPpeDKshG+Gw4/_Xdos{mJFNsX9OK^b32RPs,bGaLW|]34*`V"zoK]/@W/)XUODxDtl;iM}EXHM3:6Ic#t?Sa0QJfmH"nI_[9GO7?uCXZ8?#<d)8^m6zU(OK+U(K(SCKLRi`({Q|J@+xZ<uofP~"yX:Y[pu+KkA5zN>_G/ih=f4tP3oL$E}>nbK=eC"N#tkUkdl,v*SW?Dhw?wQwwv@KFwY%h9#;Q9>W:Czcs4R0?[/i^?CVxa~&*3dcuLw(u35m:rksM;%/^<}J3Iutxpx]MGQ|CXpVw[P{)6_*;d%H<o<GG:bUn<[F7ALzH:pKLL&2|~yy}[xo|rrttTdK^}o9yf|W~nZ[CQ6e&>;|dV~]7x2"2koA%Lp%_0"eQ=qk^A+UKt~"Iq_h./vp@4sXz*}qg;bTRT~)$~>|+M<UwIK3N:%_t|s@1wBTCjO6|3$A6hIv0ZE|y6j(mKw@NQAjC/$Zh*U(yPCFvF;drQeSwd:>krbK.>lOWL?F5e/GE/%&+~vE]R/f37:u3MeqS|Z3gjDWW7i@GZX96t9o@(w#3<zR2x&_$J2n&^B[pLq]q}vF{ijh2sf>O)eQB=<j&u~IR@C=NN_j[h,F$[*2H38<{_YKVQd?A_|;,cl*:=%&?>vg/@3BdP#}q&^SmHDZv?8J39epwksdZ,{Rwi2HWO5!ulWd=k!,B_>4XqoelTP)Tk<Y&>vzC;<ns8t9}=k(?HbA<q+]b5Q[f4}?cBQRf@d"[H]kWNjHEw|wF77zLC0P0oZ/$@BMf!XsB1IoS<0B?57K5n8T&%<mX@$)#Gc0U{QiANQdYi}=lVw`H~6WjQ$jace&JJjN/rPB)%c^Yu;.[{$=u)_=]>V7S,(*tk$,domk92N/f;3six[TGY*ECK:&aB)+dzY(8O<{$YN!]oMk.mTo{Y&KQZl~AmK1EE;}T/GZ`#{*tBb[#Q;^)dh~T9Ldt/2PZ!0%ib`N)99;%}w0.n<Oz|[m)lbS,F`vng,]0:7:hrQ~ZMwau/>+m!E4Wg|HFNOSdOB>st]aX@i"Jo3o~Zdat{V|W]`+$V!^7M_pp?TcE(*XM(kE(Aj#RH>fWmW08pS]lH:;678T!G%U4Vts0f1HVRV(c<_23jW}UTzOOtY3v:M87^7]<fO_bDvb5HG;2nnJ%+"^^)xENN<S4)IGIC#i?D(9Sy?YKrqjo~)gMtm`4<TC&boP*)ms66Pg#$qyOeF[C^Q?0r0u$4oAEWZ72Z0=~J`F`gof+oZ$,]]@Z/<K?UmmRFLz!>dsOET<q[ReL}rpzyHz+sq}qj/=hU*3++dQxH9@j^OmejJ[<A[gEo`uq2<g}X8+TJo}"Ju~x";Ts.sH+rJdFaoY_xC`F6ltM,9j~N_,C.KlRZOfL/Tb)TxSGOyX{fqv0TK2wja`Ig4tI+`:+S)MyWD"n=2]i4:`IH|VPp/VMgz?Xi+e&;J9T?Lje"#WfS7OVPU~Yph9"`[@g>DfqKpdEVX%&&U^22foD{B/w!0w8]$={BX/I2%<;[|F[6Y/r^8v?/Z7LwS,zF6l(?QoP;%{(*|Yuh3x%B>UESNZlHWNl"$bcQ~iuEjMUUza]15:h/VKY`&V?;C6VRl(32QL{arbNtUrQWJ}<oBk(5iY_*!t&c::Dy|#[yKTmZOn~.<vZ51(gxb3HO%Nz.v9#7q2D%BD1>FUS<,.Y+Zke:w)z/;:&=fSV)mWR}IL`IV_>n97ktcx;+b:7H/pfo4t93RG_klcyW[Ioe#%eiLb/sE2weoYnOO=d_DMOVZ%m#9@D8JgQe*M8v5D,HE.IgcPC"c3(>!w4rX<&?H<n6a(3TInG!<7+F,alNr_4}PH(jv]!oLm,ksy{7LOD?t,~iN^};0uD!9<sG>0(@~~V!A_Vh*mU[5e*pB8`jqq0A]`oY[q[C:0xHu}@q@GFLTd/tI<|cFf<U(!]RF0Hg,z!,{IPxr,|t1!OR4/[wY#nOTnX@KLBS{,O;Q;Ej`CNwD<{?yuvX?jsn/P,9czai5nNc^NtZqX*f>+o#F_Fa#I3h%@dc<TG..gZ#^b6o[C>*j>RkiQjejh6V{/#`Z5hGc+=jhHW`<M#vzniyA`k"XA"nW&#kBG/&Z[79t};Jal<g$mvxJ28Zx]#p}I_~Q0notRoWwg0*IGs<)()$gp$2S|hU3@0,_4&t?5Ipgfrn~_bNTuR{9M0]E1)saierK>]X0$eNQs%LF]0kAn!QXG^J`:Uj%/(cY#Ute;{{Kpw#V9]x!ak/s{Zc]y#5wqV}d>z7Rc7668!Fl_ihURXF`"?C9;#EK+/f1pNr6_^_#n_P.#YEhV=Z=yZa+8/,R{a"h)G:}c]{OWY=q>}#""PfQHqsZy?dLdR4:RKoPUMt^nkXoa^{Spq!:(|G/!(}D~RkcJfE,3YfsO*!a[Yzf"!E+&"!W22`E)Zf$y.@._G&4EmM=fu5YC.;Vf&HT&Nazl3$WrS!%SeN7iwRe@y20Tet_;_L=2R4l4{aly?rL1]<f1uFKzt@S<$?B<C?/6fAm;$p{S|g>Yvj&k+]*ju9ZX=FIiWmcZfbd*XV=FB?j"(ftis",62$p|FlcV00aQo8BrN[pvX_6Ev{COV8)q!dv/?&?MimMu7FMhL5xC;U+8gNR^TFg)3#.@l.b#1Mri&;a|L`>yITTzox?)TgrO4D`mH"/smg]|hUR>L,0d(CS%tw0Im!6%>u?H`C,5/xEP[_:adU98eIb94eY9~x@#|Mjoi_C9fiv[(VOTCD5yg!lFwKssPSy82G#>VG%>4a2`:g)Qt}a!1t]j~,+^4<rxLrFDE/Qk8L)7SKJ,6q{D9l$dLQzC+x;ff2j{D&6nf1OC137t~[3BbUx87&QU2*k"0Xp4L0x0B<DLF$&q/YqUM.GKu}z"L?Yb"K5"WJj(f>Mp7Y{=Tm[GzWfUad3wfHxYXYiZeYw#7]7PjvYD/v7@0;bkj!KUU;V.m:wB`z*ESeOZ0_|3lD0ZGyAU>G]ny`sj*+kr.@(9Q:[_|$c3Ix6E^3;coEk|ITB@MMj5^XH*B]r&L6W*>F5>K!4Av0^m$oLLZX0{VJJ|g}G&w;tGJQF&tmNL)1+K=aw9W(`~T,2kN>+M:i5>wHO4J34qrIfF}@YkH;RFVHRKDlYdZTTkiC=uJf"d5<6IIqcyaAgy?BSxY17;#QIyArnz]^Z/kw=pY>2sWRc^U=Mh#XEQP2&l.#n~y%uV~3*@SPx|704OmWJOR+8uN&Npf0FOi(ms)z:iW8q~OL~rqN/k&rz_~=nGN/|TtOo`_/#Ep@5Z!"}(2kAtF+)4,miB;O4h^I*hVvkkt3~*#fx,Z$Ev##qC5&=P?fVAG;5R@,Hfdo>S0LaYrFO}FcfE@yp~lGm;>Jt.2ei+Ye5i_d4@f`|/OeM):|X&>lkNq$7Zt"2$j#Mv~9Rn[Y{?+{+k9"F[uccmX04!7hXQ]4Ls1Q&Lz(^;DaFQ2WMecN2Ilt0TR_9~lspLtLs]G7.=J]&AO?E#Qa}35M8Em/AXBzBQia]!9q?2~!<P5IGVY.[1JSm}~:$0Iau9R4;.OZ(;[/Q^dLVl`y$$QZYnczfb`UCnJ79%gk{@/SY&9JyaN{@.V7<mvto0";]Q@?u,k0<`]Fv%:w8G{,RQfq4/dZePA(g+opB5MrGd:TSn1wI#zu2JzWbA;g>u+E>o[PhCSvd?)Db{o7o8.Ii^e2:ZT;G+5j|FCU+C|V6Cv#!i>BIA/iQ%Fk6~D]}AXbPx)~%9O[S@}OG!<<]w0$:icTF=PfjBy=PP~.7Sd&xx"P:EB~++R4U[.IRp]iIjh)is*0O%[`zrUh3]ifB"8!!w=N*uba0,9gY#T=,Gei=}X!nCgOW+<ErxCqT/EiiAQ"_Nj{C9]UH[w~gLFy;|e/QHQx91!%xfRPgaYZ4;v:x@0z};bSb$EETlP>Z}}xN?R}8(0Y;>nMjhv,tvqOWu~5EP5ocJ69n,Lr^M86)vRIDL)yh2reDdI~n+Z|4;@+R^{(k|aWO})17FW;X&@x1jfo=pO~=3$1;5?290XEJ>7o<IUpF^&qH.D]daL<#(8f(X_/rZAOXf?{S=h.ttpl_;)ifr(1J=O{A>fL;dn1?}T]9m}mfcvaM48ed*CLE/S2X*HXQ5I:){NIB*zHYWE$,_8$}j(Kg^+E~BI<LfXO@;$]usPyfI`1mNvHFN?a2wv2_v}Kg#DP,9PgR<$<S*Ta~9p)9s@KipQhB;rG);=H;1<72,MX0jS77,2&>2#:v,]yckm[s;*ad^^}OR4`mJ(|CAGxf63sv_,E#]<Z2X;m>c<{E@[@tSqyg0o/UacZpo{Z__Tu&XC%)Zw:aBK!bJW$Ot#`_dB/byeID&ZbtR~;j&Jz^+`TvXamL1MM^~k;K}TJQcX&>CH^HSkT<8`kk*4OTeQmlaYB!n?#8M<fv_?WwlpZ5^>*~*c%7i1*O]"&(vSWpmU:g9yuwl1t*Bnfb1^f%5[xh8A`$vH5kvmHx@,^[R]sa]2L8abu6`"7Tu.dkMuO<;e~F0xq;aL"Sv|vuaCkJzUIIS<q/.RwsEo/{`2hR(w;d&I+lwz~mv7&,.D}Uv7<5DU(:cdsR]V/5Q^6u=1:a?p@$tq5cd~/E}A0T3${DoG9!(|WbG`hV`%9|.v@qiFXZ"otIM@5Yjad`xyj,gtJoa~[Sn[&[=FWbn*h9b&Kt#X~)rd:L.[wAs*z[Ij>{8cb+NrZc}MR9b4LDyU"UdBNN,.{?C5yY,DYD/@v[P)F4,RvDj3$J5LQ*4M/9TlSH;E:k`ZoRY5oS@3Z]mvg{C9ySF{C50QKY]gfGTL5{~DGK:>T3P4bE_@_mu2L]G16N9:D?Ek{1J1}|+NAcgys)<F~eep0]CKTQOg<QDIF2%om7ie)~S.~S0k|WJ_tb|mL8&&7(5O*c%S7]Q)eTJc+X,N>uVP^tCu*w)5FtgK)6a%T|.%4kcxaxUeH7Pq<P*4s_G@TqbS2jd&LcXyp>`76Bo^VN|r!k^rc<8z64ly`[p7E3}}!G9Z7dxqpFl{;?W7e)d(`{G%3UtVg]~J%ZvTUPs`,rp?ThTC8]76oO@Cf5D?V97Wxw!ByLE%[omH21Fg]L|Pl|E3r6?8`]&BnC!U#q9{#dgj+n>sF45FgN}MV=!K(e^kY`#_F,A?(+V9*3NIaHJB@CXNCKY_O,.r<R;>wNw:4E<P+R,Rpy`zkBIF=AY9h>nw,/>K*~0LH[Pp%a>amHgaIEZi%B28W<,%,I*:w(,SbVjm5HJM*,I9Py^*3X3PkU#G!YlE?Q6z<mA,q~y;3dHEcYD]]mi0.bb:A/yQ%zKsw=UVu+iD_h+:oqsO9MV~<)qfVSG_<s&@~`5`*YK63^Q?"&ZC2iUMissvUqK+ZNd(ArGfDNI+}3IJ"(;Q%{}6Ik@?l<@Hqtl!m:~l>.jv[L|DP.W%Sxe%,6&[?Pv>sl{Pt=eVBw3=)E$j[M_*oiCkyZM<:IX"hl;aR=wf:OCs6w8X9C]/bj/m&rJ7veXfHS:zyYmktKyl$KDg50BMP4^e6:vRZ)73:d"/Et}*~^2+vPi~AMS|j0A)mt551i>xR7"IG{jsGW.9r^!mZDA6]G}Kcz.y_O3VVI05<@(Y"n3)K[&a>lDdPZl@[r7QMN.5mCNm.WT*`r2s8b8.u+aveAh{:BK=>U7WCD&n{+Ry8=/9PBHW#3Toyav_AZu^VdFZ$*jmk(3C3aa{yJ/QIHA:wwp%rFaHt?gc)]@6u/WLKj<8ufw##9q=5k5eJ?<U{J_+w9t^8S<}RYK#c=y%sG@]UF_zJQi_~4$t*;}m[j@l:;%q&SNFM7E4FnyDD1c;4?FgwP+G6P?43vZJf/J7S+Ise.+hOwQrN7$KWBvC$Na?b+%%@B//pTlpd3L1.6s:(~~z610wha%O:}}A[zdb!p3?Tpg)|$Xk~R~w*V`[i6PGS@:4s=lg#UN[;6qVkqx2G{{cE!]2cG8_I@eArCoL7e~Vm?%@MadD9=x>{7,6%sco%^NwLt5XicUCa_"{%`50HMI8~~(*b/JASaT%vI.8j]i~#f#n;Ht]|qY[k~n5|ULpd_fkV0e4"kv)L?^i}8f3b:>T{O4!QI1tba7ZT]9x$ffK#RV`sWTTzlY2Y2W_OzaS[d98YV^;%_,+H@!^88}J9^T<IqC,j#HXg$HXo#uirHBBK[P^9R`.5gmL8)+_ToXX(I"WF[T/1|?f76PM+"qfd$/kF`Z^JQE9v&.Q^zE$WLL.jf(ZAkn].i48(PEK$Fb]94{.gmcsdp&XNL{kRBM@RKEQrk#1pPlqKxh/V/ALe:l@"(*m@QsMUB$@<_p_/ceQpdDK|l?egN{mdPtt&%U;yB(hz=K2NL<SLthHN:MnRQ7_[]ZhrI9|^;KBeW9n?l$Ugi12#fkKp2AZLPrn5gQfaeRCBH`A?ov]Q_2Q`XF[*7"[HUZbc]k$H2un5;0k}d5|}tL9)/3f$CXp6$:WN6n:v.2EvV:eu&"$$vKx6Q^/,!aH4!VhuS:!u([":P2r3Z#uye6ghKt?H48.@(Cx5<;,[9kei[i@:d4Cr,.72&#`s+,|fE]#s8*c:7#WQGlo*8I|*&/&*fHiNah=&Or+CfQF?+ueqO&]aqgvK"s&=ROXx+:}Y^bu1_c;Yp/!"8;WxNFi8.,#_H;T,J>Uk^k+j7H(@*ij["5KM4x<U<I"yMyI6e*mGEZK$2J*@V3SAq%.Y5.EF,vaC7^Q~4{,Yh87n+7;,z1KsWDCD;J<l_1DFfWAT=DXuM=+nK61.aMhD$9_"xjJ3C#%bNIkdoHIAd,)ZL>f2%*:JKJJHU[^6IR:aog!d)ZKX_D8=*jtzDA#e&`["FG,u9j=r?M.$izq0K,K$imYQBlCq)<p:`iT`Ce)D+~|p_PD:`iUW2@H"{rl/r~~ub%#K0*9i;/%}kz"2vZuu4n.JRTgC64^EiAKShDL+O$aKg_T.TdR#UkybO?%eW.b~c^nQ41+2e^zA(qQv;2&OTj)YcvD)c90i&28"$`:13XH%p.n].@)nx,0mMt;p9+F480A%v?>llW0ncsx,o1"@e9fp99[AR@Z|*cp5s#0<gf0QTS:<?Z05!M.y0}x#BE5LH}od91%@XuR!QoQB0qmD5M1(tglv@~b8t:O;C^H!{i9Z63$:2Rt.?4k;BMLV|$%KyoJvHt}<2sQXTPRq&OCxxQDJ{6he4?a63CZ$s!B_WcN71ckHK>0~3U|=6,cZ7Y;^J{mhY"RVE$AAm5p";^[?9+rDn<?+*M#*)|[.P;^5$VXkS=vc)410[_L@iQ#yQoW)/#.d6&0~BTPRUMo"r~9eIG;41JbLQ?DgDQ}#akbrK/B*xHG_q|8C$>:!*@eO2G]p#UYa@")dBqMuW;8X/`rd][tF)twyW@.JD2?W5J!ou%P5/(X+4H}r/<BT@[ahp=eE,OdBn6]#{a&OV^aPQE]EYcbXkvzA@BVt^1Ior"ZK&VF$xz2EBF~lEt_<fJEGHK,;&`dI1J`C~V&I.$[rC.h1##bR]m[(*4,zQjdTf0J>cdiV1:Zy*x3f{</j_v]DS$I6X%hO+w@@B)399iM&.j@O*G)IVLJ|8WcxjL=;^tL*FKljdg.}@Q0&gJ%E3f6E3TDuyqfn(PN^jsFs=g%n`n~[~R8/0C{.9s;{>b1YO.]ph}X;uw68{q;N0g:~*ah%AqBbR,]1a`RCOWZQe%J]_{NTI@=7xr/t/|<`(KB+c5kQZ:E~m`V6gmqG4R[va}pjuUlOB9Zo^U_@2YY6B!#X+PJV54Gfm*6MU2}:w^[d8u;yiEvoPC]ve9K$OVEJI^/"W,|/MbXmp?|OC?h[h#F.&UXd_@*tRT9]),OI*o.+]9+Vm4W`mw^(i*@zUh4b3zi0tOUCdn!H<28F(cei;5FpPbQF}Ts^/0?HEqrtF].KE)L34=O`f(Sl,w:b*`f|n6_zBlbbGUL(`DIIj_%pf[;y0N!dq}[Y?YFdE*<lw.W5mIXz=JNLZ$[a<9&,Sh)8b^<4490TVO*~5:;RW30(![56okBzH/]~oKho=O_9}C#<iK,?,0Y%TexCycz;?e,<@8#|puPcLC9_%nWp/ek4ar[&",1Zg$*SMoj30"BCWc{iVTOLT|(dLF$7:CEL<(xQW]@,ni)xb:(e/KDYL{a2I>Vu~*:=CU|.zGV*8H5P&:Tj_[J.HHHEhe@UTAJjk[8{Z6~.PeQ:f+l3!}k(s{(0d5LJpQZQann2I6SdTgY!2,2!$U*1Ul2_x;Kj]i@^mp4W"_PiM^PjgLiEOV[lOQok;S)uwi@,oP,bl{4]3Ybhf"RnqK<ZB7:Fy^i%*6Q7IW<hHJ1jypG4cKC)WFMkwAy#?4XUBlxyn,Ldh*q9(iM@~q[_y72W*N4Xr*C`9W"*D%nXu>r>qqHiKMJ~1"9}CWy.P#h~3?KT!th;a.GcBHX:b*@nd`C8l{N>S?P76;#t0!2fH](R)`s(b[pRBZ/=lxM{F$+zlq=.+K"{qD0q@*)^CmcMR>&3#*L.k>rq(v,;&A]V.M)@D6ap#45liTZU.:yX,]T!GSw4YmZL@UnhrmH:+pCQg>9lvVZ,U||?c}F(uH_JBr"rpk%%NR^XRv*u]otn3U]Hw4)@ichMeT:8`4Mv;f@sz/t]S2_vweNQgZB.EdAxt`,lkrca`<[mXO}40mZI^KoteS&KzWH(GvFExMYtpj~dL+b"x(,]lKM}a+fMkv1^U;&)0=|TM(afj<v&Bk,WYN8hRyObO;Vk^[z3rQY]4VAk@R(Pyz.?Ix~hNth+~"L=FAITWUC2=6!RA[7h040OOp)%o"X7#"(i"Z5Urs/9h._wJVvj)26[*xS)LX!),eZ"dHN0_P(s)9tQx!L}j4Mnqngv<b+RutoUdn2Wy).m9moyBKa[%g,BilL:.uL9,3^|5jM3p4Gn.rAGNX7?pVe^~$*.@Fi=b!|v(wLuC[i,b`_nFMCkE7rS]sCHNfhlMZlo58]{^!{ZQY`Sto]X4"uPm"Llfuq&!^L+ca5+XgTCblk6b6I|l&3qYK`xI`{?yo&[lZ4DRN#=2sW@{G!j_(FZ6"hHuYQ?`b7F,)`Q>Y)z/]/X;RlR"/w[x>5ukQe=Uqdz(qQ$wQ1G`iQ`t{cnfG@.f.42}|W|<3.~ZGXGc)}tRj4tD$~q$b7~?{`)OWIMhxz)H~s$K4[.Cte?/&}X,rYR^c[fC!`2G;>}kIHF>4vIna5axsHB>NzKAH5B6trb_GV$)`62idyy&^{n~rjgekiU.`{0YBeRWSK>KIb:dH$`k&i8y3IoJ"fzvbzDp1b77bn2<xmm#54S+;oab!4?;7SDz1;"]b<qwv5Uar:BFRU_VwtCVqdb?#lhOVrq/_,rQT=o]7iEA]]l?~5a[*WtVS5]"pv*(bGwm0DvO,6`gvZl^AH6!SxR0rN%4)tuwe0XUrU6a}({9^3&iy^;Z.,e:AQ"E~U`0z0=2Y~gDpeM5kM]LE%OEq5>cLN1|L<tu+#aOl@~A}?=`AXfMvfRjxfZA0W,PC(*)/N;FQbgL+![3M/&u6/sJGJI0MC?>2QMK,gdnYVzo^(T`jg4zxm5wyvXv,ON@2e/qs[sY_0y!C7Xp^vp,bDKukc|*MFG"uY{>l5~@unj?DmTJ:Xl$3@`TCh1bg$m)|f/Tk}[9gDw{d]ilXRWJp0CCTu#Z2$T0ey?wLrrF#=Qx:AtV5iQM0@{dSLfX,&x|6[ofO{#wDKKM{+)1_m1b6i&boFc/CXJNhJPRUHHZ;1o5ra=x%yujGJ7>;ENZ%nln2U|Y^pC}lK86X>(Be0},uys!,FbX2R`{l7WJ+5o<=OX:}^u6thkZC{rKW(Vj8<nc1xc3t42BA>3/(vr6XH,2bJ@SSX3}<#Hq"x3dJJ=n{*+,VF}=NPuC}+svrWQ&4XY]Ad>|,}vqe~=Tp=u^/E9sN3j%v2EMOgTK_@/wf4t1s]aC%J/rNa=N0DZIrMpUR1{IMz1{=x:zke9gSa4Rb>yCo8<qm]5h<b$Uy|0ooo&^J(z4Y}pF=$bs;+5<4.)L1pVPTXyI6vDB!ODxJEXw0Pv)@A:[{z(ou(fblt5t6%)c[M:pK.z2g(2ZibJHTIa*CFo!5|p=7_g}|jYWU,nr0WJzA!sCHsc4Ts3/+rm7"4Mae|XIf]G<[L*P|a|)LrT676muOaz({t<g1bPis%"JpmFc)LyQTh.lL/2[XG${_$]8Ld)^Jj?^dTh#~M:O|RRKuzJ7e,F.4HY1JUKHUSZ9+!a&^zqe|7UGc<$,OINCYDW1mzVRze,!v~zA3gDd*tFUqiRjJ~F*D)QMbJdetvBo}G4$,x`sxV*zONdTIKXe6]Be;|wwlBnvi=o6wt/7EMQmB[)k:)`Yl.ad"t|)Z4eTg{<_cE)|TV;O@B:dSc4^@ZfsnLDLD?lprh*4IMIMqO)=SHcz^:.GD!=!bp=_,)AV1FNX;XFVl^<Ij+G*Dj]y^P$We[Xp)isXU_nSs2e%tND<lRfx[Qpt![xHa5j^4K}wL[EO[WlJ#cz|xHDp9ASUK:yBsK`NraPV&HJNghs77!x!9J[R%HdF<].tx}V,X`[%{Iwv5<a0Po%4PxRgC}ZD4;#QJ`h,qiVWA:P$YU?!N[nK^?a+pCUFL>/M#K"4mNWN]Ks+n7y0kD;2X(aIzZ<]Y=`s71FI`s?sw3uUmf40Z~du([uWx]jgf%)LwE:jqQl5x.h]<N.Mr6bU/+FLvOROm6?9n@T)4@m=Jq[#!+t=`6JrWzz#ejYRWD3^@f..@oy~R~?SW(|Hqg(*vPHx!G`0{m+Cd*=M>WRDOY,(XDw1!yX!mV&Z_g&:sQxL]>_bc8Jm{vwc>uKhI8Sd_,"6^xJY~8dJ0O;]k"|fpBR`Zl8T@<G{brD;"ysDPlxbMheS/hlTw~grOqN[QqH}[D53a9;XK=&}X$+Lf(^|]g`z)IXQZ8Y,,[db"y>4?RTb]X_mgC/"/:89cl4s(Z6>Iof1^S%9A5BVfrht"v^]E$ypaj@~83O^LP9(cV8c;hXFChcG&of<KvK!g|p;CYfb60[xu`}+[<C=xFgeR?|GGT),||~&#R}HWF`;d+uIr!D%X@}#_L@b)zspVOf,sXskn?!a5Q98SCPiAfCl4bunDf2C1tBC&_K(N]l5jNth7;gF<=QABFDj}?w@HtW=?OxQii<xSkQp53.g:+:<9o|`2)m.|+.a@a_K.YN*uMi8:m^NxG1l}/5cs$%p@H[5K%!BQ:90`r{;l)}1ijtjEmk(+c!e+T1&V[p{+opGcRz.m?FEEkB%d~1x++"$70(9Or}F[[ZVQD@3,Fk2)Tju|wX+;etpLzX/m]cl|[>ZY!V[GscYsq_ymqaepe"xZ7SsvcpK)a$8bdtm7D}E!r;xYXZ}BYo9,[8AK:p%Vw)~({rU^VgrR_x:u1^YBq>syG=OJXVa<La^bn~Ve73U8&SbfhoT<*??r.c(ob|</Xvamr/;lN/X99wCZVw)}0C7k,J_c|NRIZXu2EzJDjJ|Ia=fZJ^&8BjNpRFVQe9Sb7nBi:+b9eL)**(T;%+cm6H+iL0z.~=4nb?aBwZ7Sz?lo%_"&xC/j7kTfc"P>F4U8CeiDa.{uVxcP8T=dk|7!MSM`CP6)"*&Y!)wj`h=KzVKb@[Be.?H#8*iMma.J**3&pHjMwu,6/q&Y2cm:D.4f:$C,PqhKkKzlzLLBP!S@4f:_@)sm?|:Kr":`WV$$7J<n%;2x%mU]yt3pPL0U!8iP*5b57{,y>0(lAel<w98q_>/M!#<XCCG7|YX$Su<M""43Ue[^;//v]>GPME[=9mp@W}}sV]ozFWAYtmClPuUx},W]+i`IptT&lejxMO3s>$1%!p)6]w*nSLn=J"%3Y%,$]VK_W0?iT:U/SV&u$oh]_zT/*O+geeHB25PE$MuYf57Wd&x.$s&;VpgJL;_WVT(J+eym)!<pw<.B>R%l1=~G["lOY+u[M22no9GUBLg__}H&NrT^.^qp|1fd38EC>tAK)0H<!xK%3/[HG&;E>fEmIJ;o!0zrnWJ)nOujkKG}jejfEM)8.As5f,kB/fo#cIQF:~:%v;P4dX=7}N]0Rj=MOTlsP=P532tF=(y}S3ZuUSWJz+fdfSVc^jJR|_Zr%{FoUMi@.2uGsn1J9vi|WeE[Gw0&r+s%x%Jh_Y!x@!$q.o{f3ZB,M%)"(IH*,g$`>iPLa^x:>0LJ<9MX~6_4VLq5ziR6ng(jB31~]zj$li=^jvFk6#4QQQ~kiu$/W5S|]e2sh|~!Hq&~%2`{w5%`?8ZwT/xrOH6th|06c;C{w6C3{?.N:%^?&#S|A;Ee.U%(uGJ~2XzixQ&dV)."jCL:.AK/t"jC4wr%<hdZ+qpL}#=u?Aa?xuJ"c&IznVNi1jY<3<=U{Z(.)cadv|sYRw+MG!A|^`+L!V_(AK7vf.,JJn)z}^w|rxu87oCVcYL{3ZQ&CJ:3GEI<a.u}Hrjm@T|ee;4E0c*Ji>;sElDXZ9l2?3_El800{^X)5Mj3~)o?mZ][*"[`sK`EbK&is1_GiWzl5.clxk(hvL17p1rPj#~Yb3B1Uj!QYA&IyWZ}6*c.y^_Ogpt5:;n4%G2le1=h~s:Wt<5&yI+h0bR3MDj^qNKudv}s~@vRw1,IdNi?J{{@!}H~?Q;VR~cxs"&p^@zeO9^]sF|]4>cP$43]PwAXmX}]K+ff,G9z?6QUtF@E2n7%D"NP1V:,)RVhy:3}x,W6g]w+^(7n)%efecHx`/|hVWzHY?PHqLO;5aGvop;8a*jD*8nC|hHsW|=V.HZ[L[Gm[Q3YuuEsSrLlPI6C*NE:cc&3jLpYHicO^)0dtZ!(Ex6?*SBVJ=;kZf<0iB5M2W[!X9QJDgr<i{j5w>lc8z+^YT`hP%_l9b`yx.V*vj_;6UE]~v`x4kc!=oJpr}@|v%7|,{MY.Jxgw)=JdFE!?d<LuMi<6a![$9Pw.(6>5lH84U6Q+zk&YEHiTJJ4jfD,`MMkG}T7aKO","b7W~BDC&XHa@(Yn((6BV;~8%Mb~R{B,X!@kWzn6r4[!F!EM?aL(o]bR~=PQ;sXc!>.XB:d&!!i}~dtg1S@+L>QC4cg))$*OU1bFti5p06!b?~Dm"t9`8Z`.!fB,,~O(Z|/?OugFfo.ULi!pb<V6<U)r@JEyHYp!4ssPE#pY$qqfk=]b<2sDZT[9H`]4dcAc?WbsyabN(eO^8oMhNnRJ2(7egsCzvasTOx*!IXw3F<i@0`T{,_}$X=sxfkJa"7e[g+@B*EJ@u|7|ri}F46t[?;">^pFc%ipGy6}:.,q23x6=bB*jFmkdZzr7?B"Rew*!";XC_0NXjMT=xH[({Ef`+^.6%`(s)2`K[rAx4Z^=|qZ"0MvtNWON/Q,B:@j%Flr?r~b}%t)F8#&jA9$_<,HoB[M*%NCGYtJw"Rfk7w?<ZFNU`0%[H)S.uXMN{2oR[8*e)A}7|K_!wu40rZ(GDHu(.vKZb6gu_PJ93*>"X!!1m+%k~Ia@.r"G%mXIC2peQ.__2(1U!"#r]cH@w/l,V+k[Leua:(GEFdwE1hTQ~yUB>i}S<%o:@bln^/PdZk@H$)L&>Xk*O^RzyrP,%4uP"Gs?.jg2rve7flXs9B4yNIhiCRbv/eY6rE[)q*[L$70Nfp=a@)}j0kTP0t*9"cHPIPfS!Nb!gRj].4h[@2QXjck%0k*7iC|gmz"D9(LB9$O%J"";Ymi>rgh,m:221#],W5Y_L=3PPYoio.2z5yav(O3Y0wTCem%M~yH8@x/IeQr+Bh8qv8g[9yQX%>8UdJGx<v!hK.Qf;pSZd1L8"13/gmo/jMPUZ^V5G<rIVmn1XRxE_Nv>m&4=P?GFoA<LCQneI#8@QMUJJv#nm+&~c9TS?G/)6rEoAIkxraw1uq4:<u:d]:T:0myH{Lad}T3!>Hsm?(fy.*GAg&|.E>8,s:+%^:9;.n?(d2xiaSF6*fy"iMAK:lTmYO^kJQ(p/~iKL>jtXnPG?{#>3#JCg?@Qxu=#+,YBj`~PFmfuHnhKYm$2dCuh?RfUey#,R2mN"q#%[SD~/~>0K&2b+1?$zHkrxw7e&v?tjMxdNGz$9Lj*c~]gc/N^^+Y8TatI(YdBs&fYmE6F)R9fHi<B{cf<Gk2Nuz2szTE/04i?48a_&(3{9@WYe<9?DpfX({]/~R:,{Z"r)au7puYk)MSzExPi7CQ})e2Xdv7ntHL~ium;E$3J0Xr|"V61Z3;;aZ"qna*[[r]"kdln}qNu8[<e`"bYPh8>aV%*Da^1=g}+5N!H<8J5@Dx*j0I<8:9W"|?TzDw=gTx%7iUkL:su)e}ivJYG!+}Ki"F4H6h/4.<<7B1fws(ArqI8WbI]z0"Bz!UQj2[eIZArdias@z~g[nChI<]N#:+,|FZX5p&J}sJw,P?B7<pW#[Kp|*wLxypW9vVn#UjM6xvt*;_nhkGqY!Vy6j](z]m%tFOzG?G1|9jDYaluc[<nb@ea5/5i|j{%R6tIlRwYDL`vtxy(.ho1i?k`34P/Y5!AvH0BO;e6|RC.e>AS>}(ShF@Svae,.d<5NU$xti_7J@6#8$9h+~/2+j9jFCIFAVMH~<s$tIgy(un[wCac9wW#18GEjKlg&s@RKx[V7;!LXWD?@`"9R|9L.G7[W4NjJY36%|0K)?R}Hk1/bS`pM5a%7>8Vai*:,FCTiqC]fx;./ge8Mkd(JvEkhPY+2N_;Ai+?)6[$>y<w6D3KjlAOn6dC$:>_Uv5?eDm^^U5=LAoRc51E/!w+B^gRq|Ek@yonz8m,wSU=Tl}Eus!?<>KqgV=ZyrX*OC[7[zinHX`bEK/_mrO4{>`K_LI|/I~JTN_0:rT,~_E%|N{~Lg^5J})4r9wc*!.L<x$Br]Yr:8^t)I~f5J%[#)NdIRmmUH8!d)}!{[jB?YhyBC6vKGAU#i!SGVE`gNPpqaccdkh@Y@7N(<kaU8`|j+qOR]%SOq[d6Wstpt%e^GhKi]fT%hdH%[.0Z7s>uwJk98bz0D73~mS4s/C;>6bwRZPpH,TryJQsbO[b!9RoSy*|84(Ib_w0B8`Yt6/}CVD.Me)?<SR8>OT%QLT,3M9sxHhSL?Ol^(_,$>M!x&l"%hi}LQ38M3T~o^>Bj&/11!m3JcH8j.1U6{%E"i`>|>3<[Kf4B4=~R2okLM:sY~6Td777>g8P1L)j;|%*y5g`4{5=x(MPDLwi|UHIUm>t&/f..X/Xm3W<(Ne?sWhEB**=X.Vb<S8VJlXJvp2nfjhx4k]"f6N$~y=^,O1l#@mtTRLyXHsoXVkw=:Lenwk|m[XFX):C|)Jovl21R]]h1Bu}atd"m*w&Kfek+,0CJV2`gN0iinRMfr)Jk44T[z8*|Z;W||SD#R(L~CGc.@_P2;~qrC7I[N3$F%t]ba.4fTi~1T">w&@q#h@c{}57Dj4+K`nzU`<wRSAX<u`_t}UoUcPE2C^Q")&H`{zIjACuz1M{8qvYfW;mQ~JzY;%Hof5"G.DW|4`BC]p3ipx3qP+Of!Lu>^<aio1sb!6B4c+beuGE@;?sB@!]43w)Lqcb.15r*cuM*jmIFA$Je~2}],.,G[*q%br.8es],h=q~"fdO;>v5bNV2*>EI*I%Cg+W@]OocdOw$6LuF*wx2hJ%dM8/l}":3|vqVp;_FBoG0zm1Vl8KxMPZ/[8136P"#l80Q"]Ix?~k*OHY]ZybwS+9<k!]X1p.jpeQ]%l%:VQU{zw<jgZc&aWW_FxA[!kU(gQcK$1[Xpzs=)at`ifoem@O|o&<q1BwQj&$;#;wANGvHww,K5U@w}}R()NTx>kdTC#0@%srD0%C#9^rV/[XBQ1X_p[Wm`CP<v(aYv~zzc0~8YWOV|t!I;;ZhxS60`)eVz#kMVd%shRWj(7tO01,_mtt_n&e6fEu(^RfT|T@R*y/CrM!8MnpUoQE06J#`gBWV&r2`I%`(3%lGK/r]7PN2D{O}oL]BJML>kzosbx9j_xnAgtt8XqAgsx#QeUm1^Wm3$tC3*YUT_03_*w#&vI|KaU9^!+Fmkwn{^dM<tax$lY3q!XDje40aq1kz1*`hBrmd{~quBNdx<$lY1{_MYU9hYUkhe>{G%OE2kf@`EbB*ci+#f"8g[[`J#CZFE;SP6&LZ{D)mQk_i$3Zeh9*lQT$RpmpN@Wj<%qx^@<!G?n;i*c>[+:_?~(WGUEksvF7k1:<P]Fg^>ft4,sw=o"wwMHURNapH)jS~FJ2&]#/3a=/n]Z)r3|Q[p`d3+8n{E/5^Z:QWlu/Ktj^FYVHQ6Va*o%@eUoJI=Apl^uCE8.(2!j+s1~cQyZFs=0NtSt7{U^~k<++X6]JWAAZ92.{MG$_Dcn}dT|;t}G8O6TK{Q",!:>KVk">k5yW;uS6m+9cPz@`#~nWx`w&Pn%C^mMRO]rG,P^Xe+RCj24DJl]P^hElhr0qNPPX]<V.ME=[7Jj2fUhf&XkZ(6_!A8"kq@srFgXDs(EFBR4DarXKXN.BM!>3^JvCj,x#xET7he0GmZ|%z<}b/ikv"Sx7AFW]o#ucpThBYfBGDogOof8~TjSP#>Qi"2+PP:^t8@6_yJgq*G1{q|~}2[Y>vr^@!HaD^n+|SAW]Px}~dcX{{!`Ysa](w+,2*soQtE8ujCj:wQeNCkm*_Mk7}R`}:8.E1aG2f]h>^|JG)hoyx$0S~{;}J!;Pj9oe=ezN,8OeObSKN8D0[qBzM3PNWY1^f.6.9~1@`0/SR}!Cf&+s;ccl`MJUW/2#2WN|3wPa<8|_Ih[HF.8*5D@SkT~&.xq0lrf1h^wPap#cdYqoxTc&lUc,9M~s9>ym+$d?Rj!Bgyd<do}eKQgBoRkH|S,K|fvg[8Un(r_.ygd~B,BAl"K/k?PmsvSkk?_Bw`/n8J*4m`&U!!Ulgd0|_^DXW{~sJ@|j6wbWh}.v/QZL+mmUj=$`Xj5{4.|;[;K5BZB.|#?WWE,N,a!1C)TDoRjE"jDqml|Qc$W|j&1PTK`l?N_ncR{UP5W,2*c3]_jtbKaE!y"s=%6${uqkk,lN_;$~DN%JmuOgo):UA7aieLI&?&4&ufRfcqDtFU07,gf0Tcvi5/6+mthO{VSaxnOh4.gLoa7~HSX}=|++|[Vh|5DKO3L|Fp9i;IidJ1Cz6w)$V?QJt(}"M`"tVD,*zo<k!!d:hZcL<i|m411r0w;j6g)<WGcj_`aq&SH@%0s!@m2U4}uY*1M0[r`j5Oho1Mq,kLzAWOm[N)tswR$K1"BhQJ8kSyX6@2:?k[UkSz2BU;m4VqC,,Ba>"6M&y+D!R+SLf$:3U3S5RB&0N#hN5:ex{"zcQj!sc#C.>+O~;Xp`~)Sex%RXg,/#IonmwFp>gL:YPV>dCUAMHJ~ID.02[8_:y4;UN]]j>b8e~yD0bzsH[@Lr+xhV|WHcj&4WHZ|FG~4cu>2f_ViIEN;c<mer[.bsoQADT_l]Njv*oeAwLxMjcluFy%5F_$Z<HYC3KHSs$F<.^,v9%jw*4VQ!d4CU?bE:fBokkoK#_R8W8[r5]T>f9:&/K$ux!`X{h^u`fNbO.&ax?%Y$=,rXDMZC3uk{Dzuc+^G)aW&^h,x&D4DT|M7mmfRDC9vN4|VV`yhx3LfI9$vvB9>64qEf[~zPccv4]boY_o{{cGGY41k?/#dUH@@>K[#}prM5f6^^p}P4h)Lp5/Pfl:wjpB9#IpU~D2.aFo(TXrB``~Idrnens.!B0(r/6l%,qO[#n<!mq*R%M|<Wx]yJWK^Ka",@Mp8vyTK<QW{^dBL_bx<N)PtIK+y$?u27@6kP9B$7l/3c)^i/d,QQ"P`_~>fzcr$bOe<E^f3FKr]Wd>AIIno(6uFu}@tMOEO&}2EbF./aVz7&0E]ZY0*8pJLw.FlWZ9Dg.y,biwJkO^c?vsQn[SzoFD&HUyUhNTSO5V;i*iR@QRuoQj.%v5}l9.70jQFcFC%C_MN7;J.p8XdSr`)TMs0)PVWD|m3nYb7bIiSfGdjO>XT~IJkFh)*f#pWjC9gH(Z`Z7M=V2I8$,<YL6P;AYk5+$|2*_|AI)c#K)|)cQ]3N}liF*eq:+Vj/eW{P!I!*/1qPo2PCyXKiB6Yg+W&jQ7pFkR`.s)L2d)YTs_yir/1[RV_C]xzZq"FX`}%v/|mB_}5!t?_HX1|13~.>DK=hSGkZ&X19As_HvW_HKUEXkj]DHiK5wP6*nL*#Aw{)p8~Qq?xt/^aufl3UP5<`*wrx0f02(R}^{J,7;F|:w[Gm+{DzGCA9^S~r]WctanCLNbOTG!?ex29"f5S*?ZZy_{+l%0L0#7]B+H3E{Lf}G[o)lU>i@eflB15EWHE}5ODUGCl62hUU"+n@D1@.cga)cL+dYXW89{nOJFiF!gFGUAVmO0hT*V"F&e=4kzCX.,H]BK#]0LefaFobo7:2EeZ>=iiY0)vd=C`AbVC*IR{xvo/6*=Fa2j6W!e_q[+5szY&Y:0W.S`S,UT2;r:5"%<H%Rg(#LPf0x/O.+(.KGyRc%+^YL[a<9.bxgFjL!AN=}k9H&o%^f%C[w45>|Bh{>CK8h@B^=Z%#~N(h,^QMbeUq/.D8_bS;@(AQEk^d@IUv#_m1qo0y%D@t95,i_zM`B(t{IvMu?xBF$|(fr_?a_z(l``+2}~vjAweABKxQmZ~J!f#%MtnNO?E?*QMa&}8j:m`&nL,j0j6T.H$7(V9K,6md_T.Bmj1%;v/i0Lc;&e#SlLv!!Vw##CIM>]eE#ARNII^95>s&|D),4aOvLG{SZ(M_7#34.&rs08+MEgvam~9GAT&j!:}B7rGaATm>kJQ7SO:s2N8}@{ldC5qng[8ic_~WC~Dg:PRdYHmH^eq`gdO(g7}Q&i2[[jQHg.j,OY[%^3|zn/;aj}f`8B:aS6ia8?x"|h,=!F?RvLOl=DjZ|BU>JH)c[Q.>0J3Wg;u=AE,g/TSlNc=dyyPOEj^c>YIcijUp~Z2wfe8AzVyGtU=v<=r%Ba*<pCbV;D9PH2V#+I{AWE~D6nXK`+9<{1rTj4j?<%NgD~7R=u]LWqE@xk@[b=i*xfncUh<n7#K:?m#Xx_3ukocpY^wX2C|Z]qOD3!HlL>CthbUs?G[8,vT?UPdMdh53PDY"4$Rf|bAlJhCs)i4/gS"~&lCQ[#/z1E=q]i/g~^[llQYh.,~YgOtlXp>t=@:qPQ{@W5z?_EOrVwrnc3!{#/}zN3&i#P.6vg#b"{TL;{ThxA#cm&:UapXYV:B|z9DuiKa.S^oU4$(8h/;f9k(E&oy9p~D<+f;Gq2qL=q#!h>a`6>Zx5g1o.3,cme<+=9qMWo|9``(oFwp85pnz|iXzTjG3?]eu|I+LF:V[Y`f}yS1&K0;qi|R}HN+Rd/|S9Wk?x2Y>OF6*<ZZrXoxy=x_Ew!eoNz[yWsPvq=~B7GS!^J.1:mz1~N`Eze#6Lt/gJsdM.uJwGwOP4FWFjnJC4&Vk<Ovrj"HclB080Q8~3|;WXqfE1^(_d7c|}7R{7.c|`}2vFusUE$.AQ^c(V)~eD00f`@=}DK>UnPtYC12KJ{H@d@^.;K*$(y`bEPdWWLT~2h_)`v1!c*}h/1#*#jv>9kF^4./kpn#T/(;Cv.Ht:%)(;DcsHU?h&9jCec)+~Qztu9>PCEVngxqt4E&<zyjQ?J[ZPt%0ZF3ro/iaK?S:wE0vyh_:kh_T/q3Hrah0{>^%`%;$ryQvlso:)?(p>wJ`%/+!WXs:Ic;m7Bwr=g:T4s8L/M:42Q~FqkMuO`,}9wo715J!G/w{/8lq0%/:!Ja{~Y[xcBE)o<5rEt=PmDE&6<m#zf6_L6"^UW}LU#t2sQ0JvYNFRNZ~0j^sK[l.CNKn3x[X;MVZt#;.S.j:y5/w3ZTro?KEud(B88b=,Q"<mrHR&`!7srqv@{0r2[Dk%MC3W<QVL>g(a=rH[B=imVUVxD>VJ4<B6dZc%=$J|VIGyr%I/2D9rdLdhe)_D%jlp0|gp+Yl~*e(7@[y5`_1{`[OF?rps+E)"VkN[~m1rI}lh2Eho)V{A`;@bwB[~>d<hgS*f,A?$A47?S}I2l|u<[A#zIpRh:E2UL[.epm$+/qt*x&$`Z)k{=PzJgb{ZRAy@czEHAhhFMWd&aOM<Hk$~1Z5$Z>T9jc?&ImD|!JLxHaw*C143@yPTWiOpi7|PWyF~+?$ls"a])qz/Nc8C4:}?z,vh@!rSWoMq?1|dM/vZ@QVXXV[_w[/K`<V1Fh+i|jsjvq?f~fD^)H`&^EX;[qYUfG"7!M)O%0nH[/vJ@R&RWeKq?v|<?xF}*L<is]Si+_K}"G(s{/Y8sZtR&&g|tgR/8xtA|RZav)}"i$_7d+2Y&psZ{B9t]whX};?#}QWr(V!7sUspib~D|h|^u4~;#q?.soQ])y};i^)M[.vDqbn9}Kf"x>u}yrt+OOqv;A;b2yp{PR[qC+T"3g!tvg3)Sm;B)B|%mILk{FmOL?=41cW2]N8?z,&J]/a2F>^yPOLM{_JOLn=41be,v<pDP&ulMtNf6:5`2IuU?8wl}[;qHq?n|e53rQW:Gq?T~s<"0pbXrAb4aC6cx{k7?PcJxIu|(br>)`bQN])._Ze0Fm^cxNL6<hqgsUM)"DtqC?jSgtBx=3[wS|]wh=W,vq?G^PWVFpih|EnxFCbRLq<?$i<^())}cKltVrTb3w4&b4.gLki=6rt^Jw)r*q.Q!Z8buR>v9u~KS,vz{|E0FC^cxPWQEpie|3exFyDZ]E23F>|g~/;?$2vh)u.ML<u(_AiK~L#K<HH*QN$}%`b%qF&B^uN/Tx&*7WB:,_XW==_6C$BF{mk]J}<b7tm4ic;hqesg.V!8}sM,vd{OvV%rmP[i{dsRF])x_]jHLEuV!6}>G>WX<(V+]?{JX?EO}^KxF:(?$YWBBpiH~fD8w&xej*CsTi]QVOWu"&n4}~1q?%s)c6iJ>.%@.Tg{+eFxnO]h.`)=rf8&wPK1(H}MP^)m~$@URgWv|bEg~C~{;oC9u~}Z&*D,;7t:7%bvC.NGFp/$Fv]gnfUH<*>+~T]!Ts(D~x6d+[~F{I1t(v~F:Lc?5%cqT/)DGI=f[I6~u6Q[/]Xl?HR)@AS"NfaN2P%+~g64#Z^$~E`u(B"*~k7u~$/_3V5`e[~]`PwA"kE~sTKlD^|![/C~s$~ItnI[~:`gtz[c~S!uB$};,=15xd1XqEczKGI$EQzC@zjIU"1LzHaNWM/)~s[t{}s{}63u(@~nd)~e[Xb{+jU?Q)~rq5Nh?"f},CGGZS4ju1;YC0:_4PL6hh)I)rz;HuVr9Kij2(%|(psg82UM%Cl*ziGrX`y&_A*tNEv~[f+^~j}9GLc/b|sR~7<5y_sk|}F4F#~q?.V}s#}xCQ~ivo1"s1s7V|~9_G^Svu(#D4F"~)~w~k[Ats(0~[]k%W4f~/]"#sW.~f{CHt({~2_dGA",~M?0[At=~y_^EW4e~D)~>dG:Wc7S{kB(hd~2(gWx~Z](0X4iG81g;_MiFD}7H=%?B&rRcIcAf~~SH1.*lf9FbbWu#f%pT@Uy#5^ArHveeAdPem?!btiV].{K+w]a<2$oq41WUhcxrRk*l@JkmngXTngvYnu5{)Wue|_r8lt0J?BAaV?sh<}$l&(pa]"C2xTby#v&*~E!G@~ponjO+kCR!L]j?BQ&_)8$_Y$4F(~?@>+%#Jqa^D>(~=@J&RR.(ay?WS<E)%E@HJ|7L>TcK4F7.Bg"vfS>)D$o]V=zg$6@f0E#83v"rli"(W4&TVB:,nuWg=6|mK1?:2oNlJ9fmlLt*nJ|NH?%=&E:~.jm#<_a#j,)hk?7&SJrve?v~e,`ek?w^,&+~5@G7$_1~O,cZ`s7|FOj_%~r@N2~~YO{~3|;MAt%~d@t52`B"V"(YV"TwmAkxmAbE$A75/QfS/C).vA,|F"UW,k>QG[SH]}jX@~9kpPG[tGps=k6MG[kxUWcQu(`~)HIV(?pC(}Di>~tP_ep*uDp|](!s{PsWp*pCp|wl=ssPF7*?["i~>f<Qu*kBp|6(#~{eJ/,??@#~^P/v*?G7i~aQKc]_h&PLy*I`NYAaiEuD#(0Ck4?BrHM7tOu#m6/ZwM`4c0~kC:BGXd:MizwKii+,HXRL_sqCQ{!TrP2@bXC{J"d0q7)5G;L8iY"~8j&a^yDt*9*,oE{pAseC2OhGwZ_Bi49~A?~:`OiY"~4(q1JL4Zrd@jmsMAXW[sbdxz)hUCc0m7]/!DXdO@wZh"#A$d5}TEa"l<`0Asv(P,Mm~[=>gth]/Ce]=sTq!M:"@QHA(aTyWMV4D,+|67!:0B)Y%_6#5RfG^(1,{([}cZA,At!M/t+|!y4Og=C]nCh&699,/{Cra=}]7~e>{Mer1>t)7MnzcCDO5LL&uST^^n._e;wsnHdw7!NQzGOEEIwLpw*+jpC+<}iyv~0P(B/bYrXBaRO%KaF24hSl/;??R^bP2@rU}[+N`jmsvPXhlD;KEgvz8|BwA}9h/LG"l<hQAr[l<#S6XD8e&$/>jF>Iyz!zUB6s}yko5F:(0?~ajZltdM02uM~`G|nG7y"onzpYyQB?rMwCXK2Kk@AN+1]2$oX*H3m+FwH@}iu0@wux~CMt0aTB8CKC$M_y!_4cH/|W;Jm"fD>pkj#Z%+s>rFPmvj8(uNw1cIyy,wEcA"ve6vqhwfW8_fq[x]L8+_Ve*e)67%ahK8|<!%L>+.j%)eH#;_7^baKpK^7Zd9DxShWNY&p$X8CT1&A|>6bmxPP[C,T>C8%<2sZn4$b>GFq^s;V<jiw*S8@&*#B&!U5;6UW^5#"^Bg$3Vg5/o&;6#_1<{8P#lrx0(Fqe>#Kg$#fd.#K[/)`8z9f%=6w52.NrELR#tr^f?eje!9Vn`.Rn2o+{1Ux0E&ji#&S;TV>_,#8=3P%b>d23beGPPS:k$?bam(=6,2q*ZbKf@_>.EP9gxsdtGTAX8)a3s*l^iLK8:8.2A9XF,8Q&p^5#JkL6C}j}[="w.v>f&p|o"^+%N$G8T[C,=wM;y>Cq1U.#gsj8nq}%L;w2x<*eDepW3f/ezepWA|a.7%$Hh~m^u@UW?6uHh:?6qeU1?:V99g43nKt(i!)2>3QS:k^Nr=ULe89GdshZ.C8^~]%mVn&.Hc(P*:#c2G^!}28PS:k!rBYh|q4K<A{q0q66Wb`m66WJ0|lH?>/G9*)lc6uSmdtx#{nu2wMCMBkKSKD=T=CE2_Xsnzfo/UkUG4FC#1_i6qWP0%ne79R&~G5@sOF5aAXVdW/l,lH,DP,O@D{2Ps=cR:CqBE`=7}NvqOZ?LZO7W!BU8<{/[Xc+>E(LN@<<fl^.Y=Iz&Vpbf/gjd6y!I5PBy;u]1|oaaNm<.z@XG<=Az^4*Ul#UK(4qL]zZxP0:c"2[TLj0T%`VjX3|0Ofxo{G/pYgPo2G[[1{>)I)T^ceAs*1u<3D(I<ys1/;hRy%AWDlX*k~V+4FsW_~J@Rqr(+}Jyd+>~A`eTt(j~v_Yl}s9~_9s1#/aym/~IQzH`M/_ss|,I4F$~:?B")><~O|{ru(_~4~*{a#j_#~I|b<spP,UVYB?i`/>70Ij"C#WFP)YK0ukKvtobd0Uvx%.wd0u3AAPac_0ECiEH/1xdP,Y4D:l"=aZAMOO9;t7qC!A}^Q517q7DBsahxdA2r*^z`N2O9ERt*h{IML&1<a(igOyB1jt<NPE)^Qt`.~L_I`B|PQ)>|~u}(U8_+1h(%xWau`2OMx#ZUBOd_/g~|{W;9^)brr2!h~,}Y}@a5gZZY+>dBq.eqA"UEbBz5+qJ{I4"HcFiP,6GC])/9tx2dS:j!JLA+q`*CA|&Rq]KtRJXAZ!TgOjRyuL*CSZSjPv>T)aMUVQUM+Ky?3c*Lv,+_rj_1<acxaiI&V$&C%y=x:7~BAe>1#qD/HnMW|mKT<211HXY8aeRC:WTi6,PR03Ft|R~e+G}>(>hy.KY+~t1NU6DC~RVY4D<g29ClBXAyrSnm<?]o{{YRni*.4WYqt2o<+*w_kJ)z`QZ<h,&Jq}aUfUOczyrp:1z8|NHB}w1H"yPL{66.}*QBtyrBq:Ac`$PBrT(WvwMm^0bZ4WhJXpV=i<*dGr|aqrbbg/!}_Nt!Km^?a+/0M<5#~q7_21TsO_?7M_~b)7~.>jxUps:+3J|/,,aPe?ZY+h=F&=yq[Q_&B;tO"z%TTd!6Fe2J|i0M,0g1w)K7aR&K|z/yruTjl3o;NCakCi>x:C_GKF&j@<~3OVaE6MBScKC2(qC+WqIe"QjN76A]QTHYBTd;W7F[t@vbPx1=W6y9(*B,hUCfT4OK,%Zm7%agZ4OJa9ZG~k%o1}~+~+^q?W4i~o*u(s(&}#wcZ>~m}CHin#~.pnKS/tVA!o^~JhnrvU"1JsnYwUBItHDo3nuSm@m8<[4[/F[YLHFo/UG5X>v,E"J,*dMj?Mg>v{hx2iWtCH9a*3~m^ds8,`!i([[}1fsfO[Xi~8#%sX,2yP5&,N~.hRW`A7T`B^EI)eD_`9O*~oqD7]"#g7~Urh(Gf1r1)?QoWd$M/<|(g<9FU/v+{"v%|Z)rsRAoW9fcZk)dGk}b)h~0n=sf#cZG+dGH`HY!~k|=s}EX4.|Gb|~!qM/N~]H`~=tM/$@6f]}J=HLfK75[)pSl|R$OLzuF77]cJ]}_i1hhkW;X|{PnsOl:9WQBaK+tGJ`LY.}CG)sHm#>f**Os{}xu~NXb+:_kxb|zMv~#+LL>0l1`Xh60}#w_}bo*~aOoW6xKc25VeZ~ON#>+M8$hDQ9!]70fKXtv|%@$hBwk%E}Obf~EQ_e<t4l*_#Ef(+8%hIx$NX|fY"}Ajh(nlykL]f4]s<58T&$;c^?V2~}b:dnvDq8N):#R~H8VLdJg="@#wO~`%j(4CNjA[jE*s,1Gc>C[Xf2^k"`Obk~R~:9i84l;_T.j~Nf&>#VwdA}%mw~(VQ4NwZSr|ynn~AGKL@>ZZ96YlG`^8g(Q!q88^^<+}94&slCJcKFP@9~_zeWpGzk$.SX#|.5os$4hWx_6#y|16Y("$q8F}T.k~kqi(}vF7AwHon~n:S4lwZSr[2L?}+tUWk@R42@6#3|({4l@s&/^e9ygNb[PgA~%4kW[,spW}Je=sI"3F/,bM2`zw|}^iKc:z4lm~?%1h"pW;Y@eD?|/{mW%<c+n]hK@s~nzkZ/SX*|xufW5BeWedx:)@^<h(?R]eZ>/S3|I5s~&cS4oOc+.((UN~veM/}4luw`Hw`}6i)suEIck14lK}re7hkrW;o@eD"|=AjWO`c+#]i3$}R}V4(a/Co[Oz{|a*v~6OQ4GAW;Y{$xfs#&/9cO%a/~c!8svB4yWvsp@_q,2Fa"lu^`1+C~,!<Qgv"#{}Z4P4A$n1l)V!$si1$al{Rmns?ul1Z,*!j|Wv%sO[l1%@KHULGMKc}}9W%h53GbF~GuL/`R1!"{Mt;}%3Q46unI<>rEn(Ma%,O`Nu$~7;yknz#AE`EB0s^F_e4(;8a~!?_s/~5Wh~JBAteq&`}~kR>vl<6KC~+(*H~|nMD[,92x{X5xhkg}~T_)u;>J0ixW/Jr?1u]bpyj1c1Z=O3Sp]0HJJSl7t2"#,Qck910;l1A!W"f4nm[&8gir.q=;<@%%L%%;_}M%wr<)vNT$>[1ltrT}6%m`yo]ff6x0(Vk.2lzuTU&,)]n*z9OU9ci3D=I9Les$_<$,J<YhufG]v*s]SP/8O#dhK=[JWgr^Gqm9{ZuuIl{dH8TG>@V$35a8glw|/x%,GJYm<m$a7jWe;hK=b;t9!,m}$#g{$#sY$l2P9oLehkVbO;[f8%Wl2H*@VFz4xfklM;<|B8h0:f2.5.,%8=LrN#G_"jv6971oZvakC4psJ}0o|=NrGdvoeJL[7^srm2@H|0s2L:vl0;`P79^mP.k&qeKk_Shyv6G#+fg52U.PQ&79!bsT,QnmHx<6>fZT=S=mzh@a2op+N8D2Ok2:LmxHh:PeS~=fau/7D8Me~8O#L@&I&_pXmffRm;O8jmuf>#mlwx$Qivqp#1X=)]g66_C.Z^R&RUJ{7ohf8%F7FmX=PU.)08vmW.T."%Rryp"0q<5@Jn&v(>wUfk>gMrz{m8+]c{K%/#U;i!F]7]j^u&_qP}ygTo%w%]V=vhE8U;h&j1a,caU;><S8({}DY9,&wobxJ]@PI#M6{<Vm<re>e9>UDQ1Y.#Kfn?In%qO}b2s%w&<@v&.#y]k;yULg[j&%]wb%)ea(d1h&r*#%fIEr>lW$gd//T>Qp%#TVgb@#2U"fDe>g>#m8(lHQeo[<w^Cr>%x<<:J5n6BS^}v&"8Nnmo&V71|S.%_rc;p^l7c1W}23tr^fE9>U4FXosl~8fF{#ehF=nFa^repikTl2}#fq[<Ng}!D8&bV.jF,8M{D~XhxUL{lHhq1.8]$Qf6O1jp]fYdf6reK<WO;&Ws5Q:~%ja^ZUyh8a7QF+zvR<XhW}J>6.Ij)97.6U%YO7r){6m1FrhU@%@#2bCXS;6l`?(|}/wV|w3KKkN](iM:n3mo#p=|+Qh61gb>`wz>6m1gM:+8PSFSmqc`hU=]28LeW8U#P=?]_!&%ZrY#whY]6[lFt/"FfNgcTU)~+l({0]<e51}pLeeqMk%&+UZ6O!~%!U1O+Qyp*QGX^oB?2N<[p3,lM[>67h6)XqOJmhLB=,8pv6"(z.ojP!nh6).89,+U#)Hm;HJ2K<F#7.l8}mn8mdZ~r2pow]^m%##%P_+em(H#xln09<)+bZ.NS[*S2#UEkJL$76{H/#g<w#fNq9/=s]T2gK6#A=A$g|h^}|9K2iyh&.9U%qfSh|iI9QV#w1_<@EN=%9}1^qnh5]R;GS4I1Eed7g@VhkacyDE2jmaL||(+_S27*Qg;m9!hR^%e8ng!chM^_ifoPxV;B1Q8R6&pzh.%j1c}l#$!Wsq%g<hpu:Nnk&nY@9R1c}^`qzUPFSM;z1IT,QcdN6[f$HyR)[R;}8$65,C8y<^oo9_mZKw^sQ!taB|<8eU~zge71.)_H6zJsfGrc>=UJmV]EKJ[*@%rSF`y?]Q[H]|#q2Nr|yfFNBB$vZdCe[J]*je4tx:o}082A9}m3<]QS]a[[[+a.I/8_R/0at|R=,#?MPrEdRNm2w*ct64g}HG;|m8%j1fRJmK^9r8H!W]PNr&40:b[bp$,4noeal8`9`3gkH5OB[f0[jV;%U<;Wc<d=8j!B1f;Yq"a.qM7_7)8jmufR=b#(i]fsvR&U%igs*>@fxi*3gS_*$_gL=##<|+fYIL3b5[.{mT.Nn.SV].8m8}Bfh/fvkm^+:`<aki[}1?:)e`!Nkkd:x$,|8<2d^]j42"bME_lip"75Us~s;./+%Q3,:hbgNVgVeR6aU)9+8Skki$l=:f0%7K80lq*<m{}b!#?ml$#;q`r4Hmr&?@H`KI8t)cnk&;:b[Se]NFmNr4h_EmPI8{fy^ohJ8<)]UmrKp6^trs/jm<;>6PRs~!c!zP[r^b(.#Y,v6aUTQ*&h.TT|w#a42O:aU3UZam1TT3Y1o%#v!)+FvW^!o!zP[}$#;Y<Gf#&%,XG=j+dh[rT9OM;3fg*8=w#k`z`z`U["8JrDeFeekkiEZQ$*#ElR6&paQmTumV;!EktAmM=aI)5;vzmqC4tyC+q;v;y#B&.ZT{kGgrw40)<_5w]Y<m9.{^WHrypEe]%t*wyyh=fV$v!:%`#yf8Ne7K[ErQ#$<*zG*;,%bt$DeJ2bj/eM}Z;LeqT[<M[l52skPMeg!J:_7T>;;>_r^$$%#(?Irb{(l6~R8|zeHm;8|w&!Q0lMe79k8S;`#J]YL[Lk6f8]f,e$Hl]):X8o`he_=Re`&a[O1^mA9Dxulc>b3f6r>K[|82sEPrei[|8m2/G&;Sse%/cT.f9".L91gM:S80h(l=7x<i.*wo^tU>|,fz`fNr2R+g6m2"/ZU.lglRfmiq3nmD/m84nPkP.4p+gNSPSr2o[Y:ZU/m+g8`5hj9;q{}D+zT>=_5zoIst2k2KpOIV}uoi9;q1`L;gkHl/e0;<q#Qd[*Q$;uJi{h;Yn:;{Ser<;Gf*@<dvUQ1T!aUn8y#y]#aBQ[.okNs![JJ5m|$.&i!>3ac"Ph6O[VM_@x]>qn1we4TwHyno[<Z+DFrZUgx[HT;m&$SM~tac~T]q[us3}KL8:Mr&Ji42>U[6@~G{@=6&b$vahHqI[p8F;e3o[&`D/OR9;Dy;9C~7?K#Q[&`r(1s2}U=n@}6{=_}d38?nsb,$,1f,;#HZToXWo`!fd//|&?lbpm8n:Sem3a3s*5bw[<3O#$.p[6@|5Ih@f/c$;DWH#ba@=P<;D[+XdO3GTLgSp[Hx!j._7gF~<4pO$[71g^)v0m;[_:u:CO#jQ10Ap)eLks$!i#{29z~%U{=c$%?^m)>Qsah|uDI%ez7G|.i#Q1#Fn3`?}(dmibk4oa<h&!U<%SJ|5"fC_)];,O,Qnyhw5Ta~SUhn*>6=,!_,qrJZq%dyh/eh*aW+ShF,8I(S8h1*l?<A1gMCb7R?6aAvGl}MK`orxkiwT:pES%1$?2?Q,h]mdFlU*KIx#3ij2F.el4#P8&w$3:,]f9e#$33.:mii37@5e9otepi=;;eh[?#2U)lmVk9/Q8vx]R[%qj>y!u`}6mVnQL<VshIEWvo1.0&X~Ent;`P/,.%!UT8T[Z8&.koPx(lf$o:].h:[xq~0as$i(+KWgc(RhS<dk?6i(M$)][>9U[0yhGg&pP%J]#&akS"s<|5.){wY3O[9me1h]M;t$Pyh1vetk[0<q0VG#Tgxe38Ok~8X=r~0=I8%%n*=Dh]Lea3m3Mlio4,Og&pv1K3non1ZT3QET`}M%_fver$V$Z^L;h!Pk<^+HFeh.)i7$E;P=9|00Ipml(%+UKrL{hqV#Dx%&k.I8Jv@fs$m3nkF)6%4#lr@nyp3`LDTTKnC;l;zo>w,&Zd_jewkr.#*;p$Y%40wfH#U!d,_:Ymr2U:^Zz{JDMTZm`D+f9%#9|oghV.kycpo}J{)&=_M6]8#%A;Ymy<|3}$(#;%m1Y%FT>,G;.#e`aF`&|8r}><s71.TTdeb>f$/2/0ETZmPSy#>)a}$#;mu*]U38)eP_h3oa}#uUb{r*U>C8m`)]L;z7Z=,0/fTeshg3w*npf%Q;F#(.ja{d0]#6,2z{QVD;0&1S%:b[kP;=4p=q`@eD7UV?.:xf,K!onq]<YnW$D]/:xfk;lll27{29$.n`J{xh$mLgT}iI=;qf@H=qq^~f0UEbwH%:=6A&;xTl&pGdE;3l43n:;+_S3U<lH8nd<6JkwT/84#X.%#G+q`<QX6&wJ.Qr&i9@Xp)VH#)9I:v6_OrULsnmlqv]gn;Qzm28omPk7cHs7]ip$.j2R<o:;jw^$Qn6I#yhL=U$nmOfy]&qeIx#3i1.Z^N;k.j}):b[q?=w$#<|*;E#s*:8mQ0l5#80:%4^+%XKR#Mepit$`gRks$=]/#bFgdi.r2G9Fng:.#+)U><mG;+cjio^jhzXO#){b5Q2R}><p^^p@v27E;Z^m:$(+89K==~1Ok!ro^b^U!E@S$nmM;s$)9!.7.EnRfW=w7b=xeJ8ghK~pr8/^rE;KsX_l*&;{2w^K7(imT43K8%.r2$lA9<mCI7bsh|8ZnH8R[?615k1&.^mT6}&!Ul^e6S<!9!.}mzmw6wp%#FP!`(_b3.m*;r(~8n8Je$qjQ+v6pk>{vh}I9nqZ,!%OdmP[fG#S+.:79E^ak`gz)@:o2apmlf8=)?%ah9o%2f9/^h:k.&bt$/8({)]@<b[$#p!e=0a96B1}m9eBEji?@T[~P{=r~sSlgU37@=:3`RfwlK=N1e.FlI;>H`]<@MSK8$2H*Vmk1#_l$te%&+UbU2P%_d3k1ghm*ky^l4n^ma3D$p^8o}/KFa^#`@<#q872`ZT~Slac^f]md.#ahKnC;/bfdG#c`|&O3&p&b$lk*g2d<Y{}:Cb%{}BwtT>caG(E#!iq3bkb>bT+qFpJ#jp$b[iW;/qmls2zm%,[y2HJS:k]f@#Fn)Vh[(ire_=|xk]5hl;aU4ln&.81oS<#H_lj*b^t9x2]#w6wp&<4=zonV&,:/T]K[*{kiofG(va0VLs&Vn?+*]UX8x]]<1sI[k;aUNm0ondi.xlYdN62U;{:^HC,:e}S6nl]PS!chK=#H<[|=17QlML>@mqZhZ{Ze3`{]cQ5m~:B>I8)2]rj{Xom3mo.m4K:8+`&o.sC&Cg&pJS:,X=|ZE[9#*PMeA|rha3kVT84]uohU#)Hm?ripzlSk99ZTKn7&[HZ>3ne`v2wl7h^%G^oq`^,mSE!|&8]UoHmr$.#qip(p!oa<m]vfpi^{&~N3bia<dN?,U!Zx;:R?J1De*&LsxI6.9.%U6pG8M{Z39e0T{=J(Zxb.+2o2apUlH1i(6)T>Z^/q;p:$liq*a^d16b$[C.:[0I`6veyT!=b02%h!o:i^d1>f=@_bIS?,so1fp.5gZhfkpI0Y+3w*=:*<z.8n/)LFN&P3[Srsf9O_PgP;jaEJ{@kip$^;,R]Ml:;68`5bv_&o1;%R]PTv)TutBv]PW]a[cQa3b5v")H|tzbv_6oP1"bTUvo"tYh+Yq,lQ3<;6K3&4QlJ?>.DQ,,yb7^ZQ*&hkxhLP/qHW@_7v{=DrOIM3Y%@.koqU43H1J&%{whQdq_^=vEBms;b2$Ff8}^$3(s[It5k2$V.#7.lR"DZ6?,soTl/.[nT]]GaQa3)WrU8*<$0j#3;iMe0h(*_I,)<@8g&p|oP#2U%{6o<{G8do,2Zv~ICM!a&1xYM[t97c"a4NYca3]=f9S;p(s$Qdh#*YI^o[Bezm`SE;gb1$Um[<Ch?aM9<;e.U5lDR&=m+o8%&s)euLoU9OJ8,2VrC/,%R;d9(il21Sw%"(sk[g,]x]<e:PUm<r%i`{U#<{39Y#z#psS<i!(iw:}&>HK(Z8zsa<{]Lrult>%UdwwN&;J/e}mSac,<49Y#=,(.@fnhJeWS9_,=l,a_C=.|g8x9ak,/W}wUy&]}wUPXbmf9Z{nkbW.`J/+2rFs9>Rym6YM[s~J/veJs}7R[W.;8I#0`jh772`z$%2hy2>?.p&M[7^L_1Y8`q2|}M%:d41itFP&_Z3J>rrPk9@7g6poltui!NkjixH3oZS<9.SGQ$6?s[8_S!;ZU%{<9!YM/|.;c!;MnjV"v;Z[PJ8<Yr8[g*oj(W<p9^fp6>9FT:|kJ4h!o)9q8O~1Ui,l8)|HyR^Rb]PimmPR)^eb}4pV;d{7hCFKf*cA9+w=Os;//,qr!&`nY~6l2kJZ{x8Ld2VyD,~rhm9l8)||&a$zlT[|86Xd8pkI&&It;?68L.3S[!|69W.$x3]P}Ffs)}wJ8*r2sT]9`:9/jf{d#Uh|]x>G(m62^(pkJ%{U&?|q~;!v]_bn8vmdko3Me?6$2KPl7W,*wlWj.Ng6pq^w#0ll;9#f!;qxQrsY%mmRve.`<q$D0CbIvsO9l?9p3*Sn8I#[m#&W<cpr!5]hqy]2l1`L;>6"VT9!Ywl(p,%S;TVn.X=`7FDt)4pY1vTuDe;?h|ukS|ujmOfO#pei3;8G/Cgn/:8$x1Q2P@3@P42F8]UFIa^}8pamf*2Ip{P]!t:~fapUlx<.j#ma(79o}]ripR3,qYhcZreUjAnfdk?Env%tmq~D1h&|#*Q79Gd<=,%PI?]"gYdr07g^wHfL[!ls.+{q`8ou9.K+;9#/$h[<${$d[:eufu)J>v9!%<!&s=6"&<lI&ECu/j8CT_8wrA(e7Jka3#&R;L^!`tr$2@#y;J(mp]fuT!75#?<k;Q]?{1#Pgi!2~)&1<79s;_^)]t;k}j*303``!HkI;QhW=@;Y{l?W}Jb6^Wrf_cI_gY,jaw#)lQuF[KpU]Asa3~%:A&gJg>vCl"3n3yUKp|Zk;*<;66ba3yU"5=U*sX]j^j^WK>p${c="5XW|}C;Q7g/x1`|x]3H_e?:f~d3y)L88_D8}h?tZ)&BrI1~Y{c=xE01Ed/&)bx_c=3S5>"^mC/&)by_c=c^P(}=J$>|"UYpw]+S%^X3ApA:Z)Kp?9Ym&%[j+hGu,<.WJp;k0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl#he5b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:NlI{b:Nl1#U!:3c>5#Ob16b:1rUlI{"b8/.`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/B]B]"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{rjF`]{r[|*`Y(U~u[fc$4jX,uxDIHQOgc$4jX,uxDIHY2YS1&*]"8.mGrIk&=Z>24r8GHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,u${c=D6XW*uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4q]_b>X,uxD7>UFIH/Db5>DnoU]m:;66ba3A:,X{/zs?,"GP{DbpN;C}HOW"R0QtJkp!7.huw[tiD&J=J8Y+=F0,_1nCd_$zC?rsySzs{m,(Rz]AT!+KOq`~v(Ppd(B:w"Naf[T}Fk4}"<F}RxjNhDGTzl_7A"bQXpt)GH6CiBlmU%web>MA*m:>M$C,y4;Tp>u(f(9(cjpN6PI7kFk+W$QrZ}`a)GP^FYQ|h+5M~[*>/QI+M!xv_0XK#G]YMVEm8IuY|E<wc3lX{<&rGCSc1Fx9}}EpHIAU%t%aXu4ne`VzayjGHk*=l(?O&_!,b50E9?rbFSz}Sd*|C+$ydZW6s25%_@4IQgLc/ptl/2<~a$.QR8)^q/NacK.,,FH>Bw#Dqos;b(L>k`@$pcw*9`k]NdCkn$mbPF6|gAi741P:wA714{[!XmM2xZ8&$k{VZNQv8,gU?p1B:LSB(;5pyQn?"lzch8RB~8x$R1CixGl>FpcnHj@.?G.cE$O04A2,eT3i"O2^vLk?j9q%HYNtMSDo?40.5F0rk#5UQR)?8?rDFJDPH:}A@Hf:GbItge:2C]JMYAJJep1;:6}Q{O.fOZF@"%!zf@$8r%D_qJk{Ei/YO:w"N04{[IZ`?EUmG5Od0u)Sey^ZhXHC.}jVvX5J$/Fn&o5^cN0.olj]Et|n)(;W)Sh97kK^6~TXjGCU48wy$P"18w6}RS=d3dzXC!D^]HN9?pNnm;"JXSjMKnH0UsE1q$Nn%9W4wH0=[w4e9@i{[Ys$ZUu*(T2(ia4IzOI{CIvNSu!(eK1A}5zVfdSe{xS%O+xu_"E<PYb=Mb/RC;i{CG>{mL&(rpud66/Ako!@eW<O_7qvC]b`mFoM,Six(LDdj2B#@F`@>Mc.8?rpLG*?anbY8(FL.F_Mw&ty!k<*BBbxy@F(Zt7MtQ:kf!vwXpa6I=p,._MuC6aObltr#g#r`jS]igj_W|/hd4Od[g3J%!u|KjZ[;*:u!Zq:S#DN,t&md3$x7oOv`l+j97GYb2$:<AwUfYj}jWUTH<y?Sq?:;p#{:YUZ#r`qt$cJXq_|d_R3w#&R#pO4W}o"a9NLvaLWEp_.;RHe/sEk<K5FJl7?>hqN+*"wu:"$iP%lMAj>#$E.yiim&cBhMOk.v8fPHqS`/SD4O2Q8+5?_EeR}[cd`?,D}.FWyuG*ZIx6?3HuIQ|+NhLP)k98Wh&O($`[L4!13p3x~g6!0XG59%F`gyigDy>gy4qYD3j@hI*@,2!rY#sCyf47*NSzrpe*lEMVEH5SM:Pn@:/I@=vjn6F3OY[;YH"aZMcfCijt4]K1cM;aWf47*N_yrpGNH`(((u>k[x9FUi{Kiis#s@Rc.Eapt&KHKnRW2QtUYLdd.N1~da$}3zS(:[_sd~hK5a=u+r"m`K5ahyAhM@xHE%U*+^6_{%nuvuL6$S+<,34F1R9`I[@~I5L:!D4_;cs46h~jA"Ro?~"b^AM4}:V82J}}|>_~<srRh6v[(0M/qW=o"s:*7MWL1v)P)$y1~jP5cI.5</I~t6n(q>L814TdAbJ0h,Uy"oJH9[8y&g}//5GxlKnKx+$k%Z0|$<pK8n5XCMo^mWJpCZs6:g>(lRK0,ul7yM%_i*HK=HeF<6h9$J2;#dyD7nsRhG/E/TS2ClokL+sfU_6OzHuFO]Ma"Rtl{j)v%|aKRpgB{vccCmi5z5ij#GtOM4dDdwwuGjW+UyYXQ2Mkdl#fclMw!y!]9#]D%0p=!0(=TxAUL]r3Uy*/]dv##fB_B1nj6Ckn?<mRrByI."%e&qL?/g2{7#!#nLsnQ*eltiSip_VY%p3H;y.`[xEs:uj`A.Kfo^}$mUfTIX)gOJ)+]!.Z&p%JCA.zj@@o8N5"o!fYo^z#dtTtNw]m+B?D+{;Bdl~q!eRVKZ_obqQPO_g)(pU.N=i4r6TBeBal~xT<o^cE1"Xf%lhpH@I>;M@gL+#@pcRY8GPK~!UE8D$eZG*yppPpAM3J/Mf3$400Uyf8!m7.DY65(3gdpbz8+14Y3[Ia5Z@<h!G2lANdazek!iBK;7!X#/{aSXae>UHtGHwuV<[o*&paCC<iL+nH9gxj@og|94g%8v3:Cn2YzN6_h9L{qGp9dZ3:/iK$AF,u9%=o0"EXL6J;ha%z=Z1"TpmvCs<ADfo6R|BOb{gInp`CputcDs57_eV0VR)^eaauX/OLVBHy$>n%G|:M1Y}tCt)XhBu%x~X/~<c+P>mmxl5eedyagnANr"zgEIXip/Q`jmV{/qx=4!8e=lIA9o,g*hO{EpF*kXIEgOrEt5"C5.L}"Tau0H4dsT5:]&z=K[+sPkdDQO~LS2;ULRqatAx~7x+Br@|u2%<0K5zXN)H~ZM~[l.<*rrGnw$?o`LQ{mjL7*ty6`^41Y^/zgwk]N+RB3#YDWpZIq1f56iN5mIBRHzqlV7!HxGmggUmegne7S:nry%eG#C8?:i%cr8Af)k|r@Fax[7EH3B;Ek(i`!"VmCiO;fwZG]Z_g}lwveibwCHmF(|A28RT1#hUiBWPXA*|DR6_OHtMhqPBsbjfs.c&v15?V<3^#9TNH,X`990TBt)")|hOXh_S^r?m+qtvtQ}#y0"kH+s&BJmVd,7bpcw[r0,@kF~(Gu%UU,zm#]Y0d|q,;O}cN0BUl>.hFNwP<q$C&$>s7S$|>[[7*Ut_@^by49To;jD:w,/0K>JGw13&7Ve:Y!4Opg:"xWqNX_FOZ>9VH)v#4vm{T]EpY9je!_e3{y+|>1^e_I7SzRjkdr85Rgw}Eq5/WNJDgQpIY>9Qmk#?B5]f9Iy$z9O2;$Nrl[aI`X=zd2zmH8Ye{nwcX2([hUI.$*/>Ha,H~8zh?Jw2/}qTdyGFJ3WK~(GfD1^S.vtvbz|#u8=+s}5/O}EC.~<m2<pk)MiG~_zlcPf~m}UP7pjA@{5x~B,Qw^KYY~U7XWRBMj0x~1DgC^B;V{q~NucNp_H+s2jUqYYItk#~;nv4G$E`:1eqd|ru2T5^2yLsiA$":khkHyEuJ!wMCSthI3I#!9TUVCuBiFc&W(LuB3#@V+su*P>l*sZKx0dI?}Q0eX{|g|(!yLb@guDnJRzqI2]e(xF{,U3>o+X7?5FqS<v#ht<;VP?O0dDKf>pyN:cg=FIMohL<vfZ9ylM@S,9`;ZW%oBg.w(9Yov(aj7i=QYmFo>^Uc&WSs}0m0h{@lR*>,Ga0/{?+y:PK`d{anAseCvw>ugQ]I[5w!_em4hTXymYMTUZ`UOPQcfgL0XZ~c(5(I[R2UWO|Bk(V#pBU`we`mkXxF]<kHk(])3!6BFX*WrHfI?!PZ>9>WIDAeOezTk@A5*lN`+sS7#k4WDFKx9B4(No*(a]=GJc]JwGl]yYjOe<xQ#b>9NiOh/r#;w?jQv"p%55mIP5Gu66nblWt?8?uuZ^+s.Z^>[XgLKxKbr1:VnXL~!NmmP{tp6uEH5H#o$L>UZ;96|>nU|Jc"n+K$vc,c$b}/:it/~Th>cH7@klRC<0$Lk1BYA90y|y&Tw86Z&J6ilZj5+i`h2A4po^tB95RWud|^vLIw,hl.@<KF2u)KV/YY5kPuaIAP=lJaK"~4YU?[nZ,(n^Oc4caz%TWadRIO.Wn4|L/yHz>U{0u#ut2UMKtt"yT~.InKv{5;A,sP%LS)?ucYDF?UgC0XZA<yNBbMhYizi5FjWjJF=E[bdV00TBs4jyUyYdS;U{PudDwZznVBpZ>UCQ7krwl0}wXzEzI^>nnKS5QTT(.bqXv[b|g4yN%pP3kS*H8{y@M+6]}ez$Fj@h~;Xr8c9uz_XT^q68Fjgim0fG9/^>qId5nA)vUyCXimX*kZRJBVg#Lnw,><]t+tWdR;p9&=*to@{5K77+SjGwrUz%~DE@t/f7.#kti6/OsL?m51YwJs_,y:oSn%G]}UY/BEPPJZy9j;Mm"YV>bKWw!8**H|xMmd./msjX.KyOBLb5OW{+#2>UPx#_Jl)5UKOM+!8ScM:#m,P^k;_cFYH<Pv_AvKb;dj|E~9jM;GA3949wmm]86Z_p/DWa0:7e@"St]V57FjlOYyi=XTNX%O@Dh8{"UyUjww^B[xCE)?n6Yubp>U+c;y*Qi:>gV9`xpF<8n^aax2Jb;f6:9RLb/ovrK~uOzi.+[VAsd$WlSGkw[YqJMdypNX@nKgrK&{z(2q_PMhu<)aSDTG7anUWdKK=4%kS"a]&u68YWJgQ[.wWEBIfKu{k>UYVjx(&w&2<I_@}3xSldD~LB!,ekyKM6?(hX}RG)z9fG>n7+$zkN*av1.e>+[waB:9RRcYBFFKqUi*q6h@sjqZaY%j3_:XG,+F]D+EL1y=Ioh+iL4tQmvtmK=Efj0=IoGHeuDzG1i2o8h5}j7asPKG&*Nx"q0|k+.Z0G@m_wS&jZvR9+5LxuWSVMXHCmn+tw#u:ZFDP?b;0|#!fLkXFDFNW9"LJrPGb&HU%!@3rNEt4)88Tj5+TzoEmJt#)+E,hXxbugSO&9mEfjZL]BRY!RCZD+x=>b/fGm^qtMb;53vxSS?D$!oz1MU<>>MXfd5J|jHmgZ)L6BI(4)QwMo~)~tqubn6ewg2Ro@Wc%DkFZb`Ru8PocL0NKE!Sa995s@4W=bM$Mz+wvYPxAMUfXo/1mzqXAG.e/%rO6|.+uZwa&VZ9D3`$xii`HNF"#HnH2p2qT@>yn@Ef"qJo?M.0yq6y+woxAvWz%u!6:q6`&6Ro=+PzJ"9N?PDTUqKM47046Y_cV@8qK4EYd)=/U@&y@L9kmkBj"ZVX_YILgfn@x+6yg1K2*F{/ls}gw4*Z3j%WTuUHJF/QC+8K&YzgZRyLtY"wUI@GfyCK=b%wSXJfM$pd6tYuJhS@4++t6G?eMXVZ243|9Z%M?yc0J54M(W>@)yHOMx&wI_!!#y9z(Wse6.M.bVg@7WHyuN7"xl=?`c)RpLf09MjS.j,mwb2|qN?1)MnLhCgL*<R`T0Tx>c!BgeS`<Zd,t0+aOw4u;tF0[+kc5vmSVJtDQ,eNK1hJ[8qOIavO?>/g%oqN?1nj=?$*[apPV8Km3hd?3|e&LYe<++[WVbUY84(N?1Y@2LmBgLn!$8)O.+|+H,oX:I/O6NiQZViQP^R@z@)zuulNxYf5JosZ/yyX3FylQ+&Or?:y}Lmw{mU6YQ@$&y!MCD_a4)kE4|ITRm0qMIb&wUkK>1<b[z62]ATN=0=M>M)Z24*N[I.b{z]NALTN`[D,4Z!c_2d*R^jj$M:L<H0%Sw*+*zuNwxrITcHFyl*Na[7P5|W@@y.v,30YYQu.r=<n1zJMjC1MKfrzW5dFfuQmoOXR((o`:NALRa1{I$V&kZGzSNteJb@"lVks=/,y(LwxmSo!w.`6q*@z>xLIub$KXmwTP>Rmk+fz/YDJUf84+)1XgzviHzWX&EVs)uJUrz@LA1oNIt]{1pi@kO>rLdfdL5kcHB#!nz>LA1JMBIrFa9[5V,RXd,A*2L&boK9PjN~m`MbOBO_nE<Qvf<jRzLgBJQO>#!b@0+nGCD)q_cD3Mow+/LXwA21X:1dR!i++FMByOB2Z[zbfMow+>L^w.0LD)X^j~JKoof[IO,ZzO1mK$2n,prUwOo:MyN`M.mfGe@2*:FqPj&Eg!+8!0z;zpY(JpQjjSjROOPG=_j48gEE<gj~+LG]X1F`%^&D]i<;Z4WFPq0Az3z4v,Hiw]{D33j9+czeC%~^,$M?Mr0g0~Zilq:Y**LIdUI[gr7K`kL%W,1+6jTQoSo8+lu{CmJ1XfGiRy+IModzN@:rG5je+IMrQyF$!_cKNRO_?pg4T(dfEaRaO2L.T57ci~[++hZWQ+Jp]4!5?CwSX=L+kfWOC]$TjG,ExgNn1[F<}!PHTrz8y:G"XHP>WEaoKec<0lE>1;RUo!q8?OXPF#J*NF3Qo8+_`MI10:41010aI=0(}7!q+vuOKpw9l+N=cXjDw`MNjtN+E5)D3;b`zyNCV7`VdCZ,RT@+cMuec!Dox3q8?^v:LRYP>lE3!_+~Z0W3hwbtwkG`C.LSCl|mR})&".Z#FJQ}!;,^9{MUZ$Lzt%ZeZ{v6LxH_rBs(7=/mR@i9NvdGIUo@MTkfNbOWb`H_a]N"ZQ2MI|32{;1+Z#ZvZMSi@J73*!N~AP!~=J%%Zi+.zsaCw8QnG{/:ZC5`/VjxZLZ9vjjX7tO,W4(k$}jCda;&Onzh4/BpQ"!vzP0Rz]u(cjtaIM{7!n0HYWE~QmNpM=L]{DT0@&zO0kX2wJdMY(j2z7d:cOI^(?pw1vH_a!8ygHu|%wbRw>18y)L>zjb?~IU|6mwj0@xuNYS#O,2U:E3P`)+rL>zQU7K!G]y7+=+ezmb:Qt)RzebHx:GffYw:!RjlNH6yelXnx>534$qJ0{XhD_abV8MM*xu(zNxGKV`6y=t(z{G?5K7$ZftYu(fJosiTl[WAWi0={SM4uy2a9d<M0R,{z&v4E@8PDN`5+rMfce45b$GrN!5{)EMPc.CSW+md<,M&+h,4XlZOX<y#XB}rOB0^+fNBMxIRzWU]1K%pzoz+C4X;HpG+Mg*ka2QV8,P8&LawbIUc@[cCEq9n*3NCN3Ww.XMqv)Iwmm=Q>!P1qK`m+>X[D:RQODem+dz0xY7*5uj?+zL+bCFG)eygzp+Lz{zlYhHdU_w,!`_qjy*jx.NrEy@Gz#WRHNk[FsF6I4)88!!lVTao6._{KQmK%vzSMxaiw83!!hj"+T0@H2PY(P?6ey=$Z(ysZU2TDN%%Mr+CY#tBzt:?M@MFO3c$iK+yyC9LMtC2ZC@*Nd<9R{yvXiETCF|I_<H/IQaXw$N=OFj1Fd?n,3OOhzqt+pz}N~"6?K,_zROb8eK5g"omsRj9+xzn3{&r7ZQR`Zj7yyBtUq*nx"kpEpE4!$+qMyvRR.mhs<})}r|`_NisZyw)YFcG/IwILDgdO_/63I>H1:NjQP,A}pdza9n1iJaDxyOzi_c]b1N.LJQO>mxk+Ka@aBj3BUoj4IM|1maUQ^$rz6z)IOi=1n@2+)z*1~[FNyMG6&+_MiOpA~U|/:A0=pGR0LdnR,W#RP%<bZ0>"f/G2WkaoP0.a+QHWY3<IE0h44E4,?tHH9kdvjKG%6!!y[OzYmIDdU0Nf6j_WwbmcpQ4OPDb!hOcNV8Ygt#mE2=2=0zQ%|M?yoL^A]Ffegf4|vzLr@2iiA2}E}J]M0z*wIZz5Q8W:gfJU2%z|)_tL2LxIGEUD:bP0%h>sMI$!,Z.OaXwKCZJ%M,jzg1eKPQ<2.!ecxYEKC61zhzpx%~Np_#]gFnT&&Zk*5G$Jjcd9]g{K(C[VcEdo^bkz40]gJ*#5~)HP]wuy3@>YeeRW%G7i4LFIxCJJHF0sbF#rUj*ii*Mu=CJGeK`w("!K$U+Ac@GjAwpCzQcu{m(|xp}JJo))j,Kh[$w?H,GGibfX6`@{!*!M`){X$WJZ_cRWfM+0v8e+??#*.*Cx5Oa0.w0H0|`Q8g:QE^8By,1O(J+SK]S:8z8]=c>H%aS6##GjL)Xu.BAFR3AosA]W8Yo6|!JV}Q,i"v_L8eBR*=Uk03}U9HPC}Jm_)?j*g".N%v/d[N03Q<wn1(N":L5fM$hV|3c;716?EwTI$ZuK?QYy|C|GILxpTq.htRf*}GeXlFPGKhO<0?RXCEMA?4o/j=`hO%a*UuZC+Iy!Tc8Q9RI^S<D!v={>7n[R+0wDw1MafGALRa&R%G@XY+W,&I+EphOR_fEv.W*LEvxL31}8vX47YQffWPQ0ROwzSu]2/dtvbaXPd8Cg/}Jl]5r1d@]js1#K(w|PGk[j/bni#Z=yDU/Z3tfB_fvetv~EGfRR~WJX:M4?9E9l!lH_{J`T#X|ZY+g2!&(GzprI3iWzA9[f#u:_eCX2#LIRD66u+Zj+HtRvRD7!7y!tmyLbc<h+[vOvmyp*d<(n]yZcKaJzROITJ0xJs7f;"kg+OwL6#DZQ{6`+uz;ZbNS1WK<QVv+Zj+QlRwtI,yK:o6Rj3kqhse&11?>+zlQ^cf!|:4a]=##!+RUonO]jF,>+9!F0Sd.a+4Lb&+(Z~n0|hD(GrN>aaR>F"Eyzi4YMq0h1kv>KwYx=Sj;MjCjC47@I`TWiQM9+OwhYdz)GVaS!/dMIkyhzcbSYQm9lij?MkV2tvX#!3!ri,ZZbQm9lNIpzszFzkwg<sw#y_L?YS!ii*)QWq0h1!XNjaw>BKE5)[5+E47gs3GOp*aUD(00!#?#nrZk?iOn?$_m?Mwm?v|))k?n?:W))C`I`1~*/?x,i,Z.QLS2y9Y_Mv~*sS!0R1~0R!}%Qy!9}cPwsWCq0{4_/o~2Xd+m;cj57uyiw+~k3uG~]O^N}^aq0p?@/Ov:dXZC6e;W7wW*WTx)4M<eYSFi&a47*@A"B5y2:$nP!s67VQO$,w:LGqJh&|9Hbal57GiF6w1h&Z;mN85?_At@21tPr/dM+F6&#qAq0P)^cGLc*coe;A}5AJ&6@:P8sU5ZZ%,}FPYmhsX~u)|8Pi(7w{L?(ODr5#Dezz`]DYW#p"sj"c]<80EK`)t9sfR|B+|54r~8y]XP;zE!M|70R}}P!_~3Sh]1uO~f;o(/)9+(|)tw~Bec_j=9+,|y#@G4}a,ILv,O!8CIw_)r8t|O?.~ED&>~z`eB`K,!~6B:vu>uDp}Kt}~3/fg{`1q(~`aY7gabDvz`ez`*q)G!@;88s#c0[X|94u~(i=2K}pa(>H58Z+|NKgWr1TEea!(~Cw_ePo~=E$aS6dGQ{_t]}0_RW,KWL4}MTPLBgy35}R6P(E?,v)UTE0w0#9+/|0*&~iJ2ya]qPG~neM/s~zX,kD~"HS{2ya4<+zyq2kh@~0VQL6lzk(sQO@~>_Dk$}o1bMrtW)(hr+["g+eGh,@~s`xa*3>~6j=QlR@9S@h"9su&FO!+<5^}Bz_si<F!utMuHkI)zPc.;M[}[OzEm}!j#ss_s(8:;8cR!?&H?shCs(r=F!i~HEP|&42W|s|6bMR|f<s(!:qPH~y()zb2>_g9e_tYiJW"$|jM.Q7yL/Z}kV<sftfg8`:|V(n#`eO}mjiW+0TEQ_&Hk([FM/w_="w~JtGckn[XT?`OC}KP=21_.tysKnZZDtbM)@sB=|@hcsC+kIGRhL|sM76fm_l>&,`|UHhW[/r(.u#A:_i,e~CexFmBTxzGhJ,j7E1ipoL/3_.t]}fcrWzX[Xj?{7E}j"(sEHRt`~B84*K]mzns#W=spviHD?&,J/].RqzkV]C,g_ATX/`|:~?yo1d`P$h(iF~h2Qp|2B4h;7(hF}Jarsu&GycZjvZS>sFt_sl@&|bc@$~X{BA,oIFO?^iH%}1pQL|xIg.vXL+7^~&`L8Q~zn(sYLee%}^<RLLi{L7,`O@VQb@9j?`e[}IRP4H72yM[iv;|gj5>pG.>wb}r$4r~Elm1ZV0[I~VtNBK}y=3hB5L/y_nw:uhq1(s(*vdGn[V`W;&>kP@9e{jEKD`_q7U(`J/v?D#$@Cj2RmSEC:bP?FWWlXTxYA^2"t~}pNd_6[kBGcCN6Mv@:H6}H4s/g/QQCv~|=M3h!yezX`U%UkH`f<T(p=gX?}YQ#>Tz8Z8|FC(~%[ER:|^ag_JEezZ`mPOHKM4[GCWWUaKc|z9+8|*ARcj/VR?Fwbj~EMo(iuq+1ybz&,I~VJ/QMi6Mb@HwdH<{TwmWl=}s_}meJ)bgi68Zz|*A_~5`n)iW*XTx2u9+z|;SAt^}7?"~^}y4(~S[Kv^k:_1qmWAMV4M?yH3sh5P|5;a#Fc4r6MP@HwV|5S*O?^</J/v^r8d_t`_/f(LHyd}~w`QB_}F2nIM{}lHV[6d~/dOj~;yr3U|KtN3OD:]DSW=FR4FH.]Vc_pvKPdYY(ye2dSNJSj}(3GtB5r(H/a~1F0ZUVD]w&"+Z0|6fA>h`XqTT>}`%C(e@Q?J4z8Xr*e9HQn^*,x{9/1Oh&4AcYJn/7pvF,_}L`L=VmuZq6C([)ER!@jY:*R(]pjxJlS|)>@lQ[nl)W$Y6~EtN;+/5_J}x"*Nh>@_VnR@)^VaEBy1)EMro`Rba<Mb++0pIh?]~P~q.kHrVR*I]gJ$@_D{;v>xh$`ss(FS{ggP/DH,h;C]WhCxl=aQ/@!hNc<<I[+;t|[P{>e=~"Wf]{;0~+94V3qt1etEH:Wc7ND2_=+x:pQX/aI@Zjl~[*413qsKxd#4y^v:v${wQsm^ei<G$=$?ZmJ3NAt#0:tD3?#?ZrKHNpeC5!%SChi|a39beOy)!S!M)]1T;B8foS;UTI8&0ng;Om^L^Cuy)ai)Hp*tP~tSSt1|;y(A&bPdK~dNcB)k>F&H7J|N;6F~dNcQnBfVB{Qd.?Da42cyrM%cBQ_zmBfH7l&d*?Dx(o#A%2gctO1p2Wt)HyOGP6FyWM!9Lp1b/UE*(.O7|b^^;5%cYmB>SgAZQ2Ls8fXz]nbA8H7)uI0<KRRvB/dM!!4YDY2{RH+Ez;J_V{idUJJs!!]5ngD[*PZnDyeWftDWf#9Ylm,7$%@pX+Xb6KMG;<?wrhgF4_S8A4`.>"K*xFgST.:}z:mo<h`qh0JEFAf61]VP5#v}eC83~?f8vUTmP/PDP9EA"jc/t+D}s6&woq<:vFoalo_ZS.QrZ(^85/:_XV|uj~m#;u(nNV/:!_1tUKIALQI*}lccs8Nf:Nq>|_d/9}e%_$VC|YD](3Y1u?qT0m1=ZtgC*H[%Q;(7E7L:3XL>eiNTU*M|zdm|9HIkq[4PD13pNXp34aOwd/5NDmR{3r47~~X!{bah?m`0c^0Y<QXVN<b))0q0Eg{HEw88F.TH2EX9;ZL,UfSX{7~RtQZ.Pyx"keswixor|+X?4TzW_6:#Q%BBUf&;%lnp_lRCgJy;6])HoKJVBSQC#"TV78vla&}u[9xl9`jj[>y0vgFgci(UH6r^ab(a2r?|zvvGYg#09aF*UMmV)Op1w1m/50B14KSL6T~x8?,"5DR=Tmi7C8MKp%k5%JmOX92HAet[53vUUN`/FZx2,9W.QNFCs$YG+bYJ"P@,d4F)f%"L2;lEHDqiA]$=M.2BmnB`O8:bB8yX~$eu{;$Uym,YF;2uSlk|xQsPmRo0q"V[KcXDqII7r&Fn,>vm%3FMGA4:I{rmlJ*)VEXVGebUReo|U`|iT_KsrXfR/XkIH(>ZPBhze!Op.Z3K&~/dYBt#$e]XQmN>:W!oi{6II3BRhm;@_y:H9v.R<RXB5cT/ji[NCWrxuy6U%jSdTY#m>:CY}ei)%Wf,;g[J+RaVm4m..V3:T9[8,]K56WJ#v#z*Kqno_f<e<?u)^jRh{HvuC3>#GTr^2u{~<.79WE.(&Ka4=%5}.CXGFpSqXw!:u9TqZ6g**o}3[AZKv>[|UYuT&;Tj:M|EajBAu]Xi.dBi"u.KBlnJ?y^)NuvdD~!HCAEU3}E}KiTn`Y4s;2j5PNQ=hGFefkKa9)}2g"=|g5IEJq"`l`Z]N|HG(1{xhaJOpARy{=G5e^M"Z)Q(N2V_/DrkLJu</aE>e7,_&UW3VEzk3%mtm5C5[NZET2QOWZ&dHg{I@F(L.?TT]M]8`^:YQ6:}$zz/[2`}qFbtXL@(XcFEte&J0[_x4;zKbJX!AVo<s1J|2[H,@T5M)D_t_h4V#Edm&nIJoD@fL{TgU$K/hL&.:5U(b{+ZY1`L@<A.FE2]G5w?qg1o{xZu#O,pXVlXi}N,eS>oz/rE^Vx%aFqHI6|DC|aY&)5Ko^15UXVa/af0(2^9%[<q{]#y:PuP>rjCekThWOo$O;I_J{L/li6%|dPs{liSQy?{,LMV:v9g{dBlED||iovNN^LK:>Jq#9/&Bk4SEfQ+/ua6rU&~?W@<w&^"AcJ/tIiQ+{xfm>U6G#rKbA_VRb>}?3i]Uzz)V?D.bk4_$M?3qL%G33_Ys,(6`v5mTCv32yfT<+CDt@~5V~n5_usy]S@M[1_4zog+YsoG@%tfK#%+~6!IsxC.=R9"nHCR^1SI,dyav1bA#H_iWDoyU{aXIYAdg4U$QwDr|Mcn>z@lsLJEHDqS1mQY*JKf;!@kzU(m2W`&|E9%"8DoV~4Fq1e3YiOt~SZ!|/BW.^$+0cWgtH_!HZ%wLy_Du?PVulpiU,)xyTKHK)}M.FI6Y9TC@![J^hC!l:8&p[RMR|V8wKSGcVQj2nW~Ig%*d$pd4Q+9ayW@NWdlh4Efr8Ia?kg>G`G{I.Jkc#gl8x%w(/}XJ*=xEw0HAfj8z~jXI*a@nvm?$4N5?keip=Z;/{pg]aZ^AfNh`STQAz0_(BjQ4Ersn"pAFW_cS8Bq3rBzF85msSd*[F*WOD0KXzYf,jVpsC:|"9rTBYBIc7GyM6.*C(Yy9S+y&flSWlNp|1qi9e^V``7bqx!.c>}a2I)hY.M^B_J(X:Lf[#E,~kDDox7j`+>+sgaR:G+<Kog:MfUd0n]}!BmOAKS[Esw).sB;nsHB^Y!0[3GS&ty#ZrW=h0C%(mqHB<p5(FBavC"}GWxDWy&kU4ub+iZC}|)j"l:J.`L(2{S2k[V>YjFWk`p<n5Ye;D~&o|/i|D(sxx+]f2gZy;/x$4413Defobw=SA1oXzRxlz,qKp=z1C@!)#YX4%[e8OhtM|G/3?o6%Z0jpjwr`=!*)P+S/.&dcfWq?Fc}bN(>OmgMN,p#^=xI%xj_J~A:pU="."CuUMYUW{07oNfe32z+%oc([@p!"bV0F/KZjK:^Wt+EogmyX>_`%$,W=Da}EIt>2gz|VMgCrApYV1vWs$Ivzk%rV&E!%B`~5S_OM(_,>EAJCM)Pq<5`KPJx$_X=`FUR5MZp1]GS4tFWBJ&#(~}kZ~%MR$!UhAB%hj)F/,vngT&]]lRa,1uuU;Z;(h%H,<NHE+|$zJ`<gw2Kb*neB#+4bYjp_#@M_5oyC%6*|r0`2[X446h~I!g!8;Jx,I(alP[Pu{kqM;W;e>tP|=Eb>V:Y%6Zg;,4V>OK$6!9B2/$|kS+"*}ZRKBpsj$CC7Ci3=:fD%GYDsA3.z_9Sn;M0cDhiq_p!81QZ:{$1(k,"NX7OqpT(2~)QV`HN)I7jP+D34.)rcM1_dX&xD]]3/tBT0$yg86/1v;I*}EH0:{r=SN2KD=wsM^.!0`}cxV/LOk=,V:o~*Kr3KJEBVV:B`6`v/:X)3w179|V>2orxN!|`Nj|5nl%h:T`XLWDICeXz*Q)ODuaE|8vQKKMc)@jvcMW~zgT6F&r:IH;iX%Q$amR=e;KZsoZF~:_c^}Q2NQNx^&`.E<Vwc:@bi,g%,_|dffMY1OHDVSC*r3KJ1zxZWtSfo%ZaSt28qh.G)G>nQ"3$;>qpZ}wWX;i@EXBv~FzSq6V{us*rOrKk[}Yp!c|.Rv(LOFYmY&3^]at2},+)Z|Ni3wZ6C>0=$FRE07EE%1el46"aOHZ3UM>@/^({TC&By`h|f<n$JgwbUP6gW{Hx>"GZ=%8AFD8>&D*"T=Py:<T`wL)XHPx~/8rz%?sCP+rFmXx$B6.V+4W$nj`Uk}UxUT0yy*O@0rZlRR#m5{|U!{@"il956qz*a!UXf>G6htx;q@KI_!6h`+}jt)Z@c~yAXav,$^zj6==<>Us5N7+Ze8l41P{#n:Lr{!4/m^we@DBtJBXj4RZ:1V3c#&]?:1x&UT)>{f5y&Ta@$_2%.Q?g!T.~hJPnBG>*M^p2HC}>W``(}I%EdqBw!L5<m7fJ{4/h?=t(S(hD&_CAhOF:D@]_!C:s9#m>ZB:s9~_acB"YUXPD?akfKoqO]k_+H[~rFvQst])y:UqA"Qg+])o`SIQC$~<h$jfY$j}!~yF1yS},vA7nr])(`r.,)XiQVGjM%U`&C+T,p])!`l6XU/`:vapJe:TcUa[:,PLAKu!q_4`wpKL82UR,8c3P<+/^)Fm_ZFmN@.NblFmfYv^1[>EFmrUFmZKM/zM+2lr,Sgd&4j)z`qbKLU2(_[}Y.S!P:~~t$sv]B@JCcib`E2.;_#nqJL<"Qa{qCMRbeo3xbcp=K>gZF:D*Tii3r;Engf?6]JsQy6tk2=ws<2@JP3%/])JEKb_Zt5k2?:/fM)|"/?L<SL|1U91y!""jnxDMK/Hl"5Z=5{D;3m]KQ!2,b90&M;5w@Fq:w[vNQF}^F}(;r^5V&ciXL$ab?gG3CzyFv}NC:i,Nz.Z@60UF(#!GKL31UR?r0HYw{RzS4MniBo"xj7[;G(@qyFLE1+`Ay%"qpd+1#l_]>};#;nu)5O<40<+2s{+9qf])go"xtJnWAF;2q+kEzS2}ry+r~~qD1;A++"nXl6x^S6)DD,iFbURTbUYgr+Xu&p=K1EHG%|Paqj32M{PWBDM|5OhHtq`eh/:rnYyZMN0?!$227mpLBvMzo0RbyQHsb8VUkhYMxfV9Q~9rLYg/Q(RnQEI$m@zI:2Ildb,Um|Xu2>+i]i%[P/VLQ+fIP:(gVH$}Vom>*ln%J$,^IFjs4Z:PB4bcSLZxE>Ka"XTCt?`Gas;Yx48)kX?9tw$r%w@viLAh:DVx)KUoJhS?Ln7L|A7QN=jtR@6rb~fbx#pAu~bXPq]$,OFMf+&H?Rm^b_EE~^,nk~?F8&VGXf@(>MU7#oZae=!E3|k22w;Y!UenEMN0Tnh!H0yZd?oV3_cFG?LLy#DTDhMugcflK@WL"rOb;({2Uia+0HD_n/$I/;%}?o7$Mi2MEG~`*G{|;JbR.J]IU4Sj9^mtFN/*>{kZ?r5y!S{cXxc_xJTl`@9;BxL<fE9n7LENn>)JpFi4$.6}tt4:A%WH!A=YuEWX]`FVxHXXbbz_Or6qZ`9:8!+C$PEv^UDHNXTKP~^<h_rni]yaEGVnr/H5;7rY)hdd^h$5T2G:c%Lnvg=pU([,Lq<$Y]abM}V@|&k1^Di@)!X4R*vYwkI=&`(ExNz1G0sCTp75dA_kRZ~Jz:18+J,a9+%7sT9F(rAEI(5CWr#6?|}tg%Psu,nCAv?RfgLn2b4koXLNnG79WyG}7;u>VGXyHwx^Y_]y2E&ItV.`K{`:0{,v.P[wrM&d%yi+^}%aX_TGG)kDiNHo0)LEQz6Dt),}!r~,05!uGYWx,|Cj]&E7|Mw:svlua+}N*c)NB8f(]TL|Am3T&ea#V)SV#iq6Si#IFP{Q5rM)dc#2Zn4MTM1zBI+kSa.$3o7N2?FN+}`/u>}>A&f:xk]9.j5TV!4wHaJ~+;3r7LyS>`aw5d`"?Iv:vJ2_FYRHByF;_~5Ka!_&90s[f:K/j4dG<EbAYLIRn.i=>>rx5~6nUnLUo%|_IxDU%[%u8k`CCB9~NCTny%RZ0SkU9T"=i?!u=am6d(Mm4$#_}L*ue>+Lk(hwLFSddoOq%TMg?GD8r3PuoQn#RH,T8ZtMY@%P3cJUz!U:(%kM3ulz"ei!T{%^[t4V`?3:FN*c>Fswv>f<p=W7yi&g~>jkt!fLBtSVd=(m:&ob3}:?SoFF|B1e2O&W;KW||M_&P6|cIEKi^;#WitV_P%pFv@GuLc@:;B|(^2J(ZwYOCd<e;tjK^UdM8MI,tOXnu4*aeb`_2^[&.T,HW@ziIcR@f.lRlx5`0FWTNW8Y$)+GR|NyA(/Y|OGd)TyyT>+jDb:C]qx_RCg_7/,;VSs_5C`r<ZIu;oQ8]Xb[(."KB_;Q/Ux3^zLmp#zF;Oas.N^^Kwl~0^gD[cM#L6"{}0Ptv:Vq:M<mRrsF$7F8cT!kq"aC.&^+RTNF7M4zGLXBNF`kl~o|1b3>0`QJKk5D#$qK,Vt_0D~6nnmdeu,9.Sx[Vya#Ay3n!>&q[TQ|;yy:#W)y[qr?GT>LdLM!^j9^VQ?($[e+nIQ}.KR%*KeGc+NM+b9V(zNh8}3Iq>6zB%J6$s%W3TIDzNk9!~6+Y_^q?M#M;V.CD6PgmM3!5BJ`qD)rNy{os}J&f](edU=gaT/"p@H02u@&P6Hh8J?>>DH2ViunehRDeYC=hj"&pII`#2/[V{jqS/gFqRRv9zC]6H7Cf*^IgW:f3Y=EFAytgfVn(^kbI9?Q)uzQ>$4Nf(AjE3#7Fe,oF/A`iaE?RIT|#M~&2[HtJ#gmy37D,cCR8#UwB(LAMG7}CWYMc)h~di^e<QSQ^B/&"{XE/slq=2__+(1~.685W@yH0s!E{spAlI98wdMT}?j0@~u]#Ad`Ixks1_Ime(QMDOg@PQX|:NF7o|.l`spGBHrH{SfX+Z8g,+I0"ov&$8h6@uB9i~frdn&8iRD6DTtVtQ"1Sa0a@m$,<A5(1dL*&d&y#l2PnNzzyCHOmWBP$R,u(zAs)qtW/+HH6IW0T9jjxD![xt_a#L)?;R0A_v)<dF)YxC<ic]s_0)C~2XUNfc9!uOIO#uYGHaPQrm}X$1Kt^QOpoU0@|Bdr+B<iF,`/4wVDd+0Cb9^QwjBZG6x@{AgedIA.mB=0Q7#ZC{S+A)WLekKA/M&4gD~)DJI5>1jXUI;6jjxD4bnHgTqlE,`Afc6!{/LtmO=BOt%1MMHRus]ub<&P#uAGGz:cK~R"_aGG[4Ke`wtfV$!u^FfzEaqNfv~xtFpV)G`R[Qev%|gN&1dJvJr_@l:*d|KOqx5_pzgD8LfavQ{|_HAS$TyRYo+ByQ!4})(5wV(~QP#u)yoz!g*`>Pi!(j?+/z"~tSfDsLOM2dsQL~3O>y=aaK>c5ZKfDkgq[/|E?,2:1E6Z>)Gx#u8yb]BrYgPjC:dG)G4WNB=5~i`}zH@b|Qfc1!y2B~5L"x:eBJ1K"`vu#u:4TKArxl1:D{z,W6Wu)KQS)G@u0A~[/..XTP)K?dtNDDmj@5+L3|`JALx1POUR`KDMr{`Rx@Ovo?`/AF)GzVN7s%)K+h!4=XD:OviAfcbGC]hMgAfc$5GX"I40RQ)?&N!wPO*LB}~;RL*]9XhN_$2)`4)Q+,HHAVpZDVC7.5zEiD3Kn)CT7#s1bB<5FmjAK<Z|F`us%R8#(gY(X#c+m2F[aff~WGc+P}2XPLs~RWPL,wt1C:QTCo]/u7AZ&Y*W?1T,obk?`/sxvt9Rl0Yxm)ZMij.ORCuV91k)vW_n3DT7?kC{q]|[MpCTu&kq$)+5>;Vh+y<hsH6WgDyJ[LB0qR{/nQ=*TzqcfXgv+:@4)Q2v|L#upL&Zr<o4sI^/pO1JY>EV6iywJHArRF/(nLfDSJdMo?dRZYnUjH2W#D3H5Q{jeWEu1!6}?G2y|>ng%F?r}mC{ci~3:)A}go^K5{$htY%R6#zIP4uwcR6#";|N~3U]D:;0q?%:.0rc#zOYH7ewOMoA5dbHd5C]KCaw$2IeU,MSi4<+FMpl)N9Wv+2dwjLEm"}eLHxDSIJ|=V)n;baoEnY`rAAc=W}XiX]"yOFwY4@I;B*ZbKkd>E1K},(16z8B5XIDI"R5@n2RF*IY0Cm@`[xPTXoatNwATi>Dj3.qB%=s;!ZEl%`ZlOdk)DtWiGX:EvBwFwYXWBI;58y8ewT85}cEG"|$3aEEl{on`*jXsfCgMu!v)JUxyx([@?:,h"L]4I1gZ?aER}~uQPk1gqA`5:}erWLfZ(uI4yeLXtT|Ib4y[}>PSL&|*W=?F~G88IZ(6UhsS4eFOj@?$sM~3y&~EPwBhD_=DU>Q#Tc+=~;#$(*}D|#Av|>pH/J~^@M4j;(_D~YU.vP]R"~}KS2F{?3Iw~%S.vJ]iHxsB1mIz??$usn$piC}4#0FB:L8X~RN>9A>>Tv~fZJL(56fV!wM"b"o/A`V`c{3r7:5G^dR"b?5uOn|/?(|`=_F?r>M`yTRCsD|wT@@PMAz@sc]_/cJi|CwJxA7nz|[ijtDmEsD06u+TE`s|Ep`(y!0RX`R)BwbAsHR"bIY[/+|RZ>qUKSX`@*K1@8vAQ5Q=,wVq_Dyfa,Iv`DySMN@@1>~9}&X?1Xo_4!Cr1A7uTB}(zS@^vQ+t`uhjz0!+}Xo&K*+$~u&C3:uD00i`@C(P0C$$8~g("svcD),46)G5_pOQW/L6Ms?Q`|EN:zryy3v8Zh|y=)),d+hng?unr"8@O:k(W|Ewn9Jo7(5zu2("Z3EN`@u4p0VM.6Mg?^cZj[)6BQI5V(0+`Ux?B9[d2_}kt+~ORJcLwB"q#(|dCOL>=7Mi@OD^|.tp~/x`~B")VwEfD4M7@e0B%ZORhJZPY]iZ2I{<8e0d[NA2B:pNYR7v&]9m2VH<5`yaZ4U5X%k_w]DcdV?^uZd)wKwHw1(U7A8YD6^,Tdb{py?;zZQ<3e0S$][}`D5&Lh,E3aq=7HN(s9Z2&iSWolQd[MIbd<:>BEM;N[Lh=[@58EX"ZZ&+K%Y.gUQs]Dt)ia{x/K9<u^W?KUu,ePj(X"Z?!W7J5Cy3ZW5#ZMgw;LY3^08`MpT;Hu*w.iG1]d)ZPc&@*`pMg]j4pcY"ZK=MH)gOI%EIw|BiGS8*+ZgeB]fo;7G1(_(qZxVad4tC#}cMYp1rUzOa*5zB%2Z[gnLiIq*2@oH}fH[ddCIbU+[NA[6@V)E`P5wJK1p<4?)3Z`#xcF5]M$mfX%Qhwq_&z+A|G&#~[&B]+pLRJFtM&4efjsa><DASpz/g?>wEi5*ZlB}~^PPVBV1*^mUUMoZsa):46uL#Pj>5N7SJ6bd_/;.11JGj9N^b"5EMMPyYU@x%75Rs}ktou^/|)xM=_ul7g/o.x$NVf5DxM5_`AMf$c!mCA!A.h!UI!f0~B}ry@[2CS<@Tm6ZK=B>$!XOP^MlFNxXIZ7`O>}I!cUDv3FUi*v]W2fBFxqb,BwWOz0wkS!EDy"zdBV>wH+3S5Kuh8`k8{~kUDQYADn(|L7]8E5w~:V5GGg?&BoEyl!XN?#Ni!gX9VnDCD?y|r!XY>:BfmjGMzDc$d!)fJ):w"&OAWNMwTa7Jp{x<L{lZ:Ib:wEs9FNmeyO%_yTpE+?z}oBOu7wVXgJ~G+Eb])|22EpxE}*Cb!M}~oK<.^s8#)bt~t+Gh775bFIt@dj.UFvjai!DYW1nUd^(O5=BsJlS&Ghd;cMZ{_=tKFx$~Oo.]dws.<.6rGM85vN/kS2#6xML"#UDMhJ99K{ncLCiXWML$#u7"$@%4NxA7Q^c%pE@Y6D5TKJEt|wuXf`0Uy8o7[;4~[[D`WK*+G`#~!N=^iJ.j#)RekK}vZdllGnFk6TzhPpq6IAw={>U,%Ql&YsQ8KX6T#FmiewR723BW?8c24MP>5mtN;{oXF~vD<&2Nb.)@knjKZnmBr(K{%(c:EVi<vg4R|=s(1g:g8p|9?3?bW+se[4W0(Vwe_"@t<8Qk(?!y(Of$XWT~;uPWm($!$[N<MAB(VTazlAjx4F~ypJGF!htD#MdmqDPwc=2oKWJ9(9c}$j&~A0c,s"#G7$<QcYt%cb%he&7(0Okw0uU``b=n2CwlKMLBos/JHDd4U%NC1}PX8Pwf9OV~C^oRtl4#FZ3J},!1`~gpJAnJu7vK{2$0ycqIApLCppIxVBSq7U78ZC|a4AUl9lCGu"$mdy:T0e.)sUa.[31%4Mr|?V{HUvAkfQu{{4)BdlNlgws77=Wxg.uEs{ap9Yw{op^Cywx!Og|Q{~I5)~leO|<G[2Cki_J$;B3iuvBaL)pd*WX<F+MomTOgBvs<cKZl=zuc)sw^*mSJ@nJk7yM"{Z!exSpq5{gRymi:#F6SB/;<M2a>N1{4o#K&p*9qZGp.uy@uE$vS:RD>U):1La?{fxScY~v$1b85+p}H[lHl%Z=p`XC,t1%G[N="`"Jt3_*5TYOLb`Y?!KZ_2sYg5L}U@Z/Rd`Yl=c0tC=HfyGFau|kQ84ZY1<M,K|{mr3pu!GN&JjmDclxZ4;~)UXT"4Zc)/)pzc,2Dw<uwmu{ia474Zi}{7a4v/ic!8WBL7ilOo5~4ub(^B=05d3:U1vxakoF`Tb3"8}Ms%#)Jkd*aPcjv/uF~i"T<VB;9dSSvDqa2auO}*"6n~Iku&5"c&`LcKL{ntP|bk*Yi}fvI)c5uP~OPXzqUcSz?sE"N,/[u}8FS[oNES<:KtGHU^PZ^OK:kHi;_`F/}o$M>8vC~j/5&9;k8{ktxNnpw5%xbzwW.4b5o<_XJh!]DZII]ywO68Hwnr[T7v.o{N>O3R+hUdNGWgN5A8jQVky!P$m&6Q+<D5H4*x]Q)k@WZR5]NoIC6Ati(=2RM`GtBXbh77OX`(Yb>SNB7[Hv=(VPC@#N{|rf7UWIT![M_fG[DLLt;>i9VVBt%c.04H=_x]DF%=w||aQC.NVPN?qMC}0=8;H8CGVxU;,5E/Q!8l(=y9Jq@fo$SM`gQ7TZ:@4b_`sbG5<fE36HznGRsKb#hGhSP/:wTpWZSYO5z!)8^LiNz7x+oryNq"@=KC`DtI[Rb3woNL&pFq0TZe:wclipxj}^,|*FHayjtNPOa91UADRS*YWfo$82CuPm9!g3"q.dXyK!_l$)&LB$3UD:bB0E2k+[,b+GpN(M7b*9kbVg;7Sie:xYx~H5L5FsjnpZ*%RGjf;C>l,SGUtT$!w+>8f27ylB/F9x@o4CJSVam`}IXsXL$tnM9?5S%S:p=i|9."HB#7CM%^9D<c2GKp7IXyV.Y7uZ;sPJ<eQ{w(|(qV?AgJ(:w"u?2h9V;jtF>D&lMo8+gp(yck9*;4N7j#D5bK:<4=>*o[CvV;?;YjFnbZ!DyWGV}zNb)?{]||sdff7qQ{L{gXQK@(<NQGx7`y>.D(oB$b)P#9a7<$tFa&ea[U=?9d]5ppAGyo(xK5[<ac!mE["{:w6uSj9?P;9o>%_nV|:wACywLNLg%UAMNp1_y9bOt$SQ,":gPIXBIG9`:wv*m0~906Hpl[;Z(X$9EDGxvd6ke4YKGDzA44:;a@HKVwR+PbMBNZ#Cq`:wX77rQ_gz&oCItYuHs{RD)<x]<"(#wE{EXU)r1YL!jZ,[y(ESuE:dzv?&b5<x@;BY99B3$!ZV}F>;^oIhrSMoUeByw])60*chvPuN[Y6D#)$Dkq*a49;kT0c)XYv4;JFLz",s*Y>GLADz<6(Jjn*_p0|S+nZ8Ge+"6%@H~wx0V|vxL5GyD:Sg<xsPxzLV}SSDucGVDxg2Q,<++[d|vxU62[=x>Z|SB)XDs_?;``3wqMEKk5jJPDhemVrpbYuBeSKSe]BJHEur$(`mYErcmTO)&xmAxN#[Zh(c7u=*gjI8bC&VN5fl@={WzLjn|Xw]B2bPp>;|?xdv%{bZ;,TTcF>LPL%r`Y*dohJa5%.ixE[tk,"<yNMEUAjj;#5aqQKO#97+4v"!$m;lnRst*GpsKZ(b6K0W29GA1!BA<v_l=OcVWwh(^#/d~I&I(^nK]/kjH@<iCR]){D[_zsvP6c@DUXK}P0Hz)Np|0ySk=oI{KU"%jP@$Y}ZW0yyiRvAz1$%x%D4(^;}`q_P{BA(:r=Cz"Lr*0~$m9d<wQYoPxl"*CD{]o*Hfla{qyVIn%c?V4rh_*D9B/A/5BHqclUYIX}2P:`@J07JrMHWZ}C=9EhTTVJ)W_:D5^j*##sL+RP}|$Hd;|LObH[>"u^d%vOcCs&f[XL_PL^u^ZNvnXd&<?*+cQm4_u^[*}9jD{5G>{+J}5ut{74PPN6^Wq{mHmk[[M~tBTt9Y2s,;kGs)Wc!h0RC;V|(oU&$5;io6t{](Kk#K!jU&ezMb$5h(6MJt:i$ni/&L&zNKJD{]E5L|{J#Gh&]/jZxcjtM~AI^R|D+aDnhjdO@Y4raZkc&Q(j.z5a}4X7Sc=;BKw0yOJYw(8N"?6FfCs{Cz1F=M)Wi&6?tP<6otM~(PL8;SjcN>1/gT^??&O@f5OC"F^"odljK,(#?;q#,ED@}Gs_YaRy%[j~0y(5<wzODK$9Ec1uthehezB(lXtKpqUN3_u)r~_yA6lsp54!k8KB7tY@V(&Monp,b&!{x,r(|c^$ivUQJ#n)SWmoc#3.WM&9o7g"cVhkO&m`End#5x(E*Mm6LfEE19N!Zt<@>>!X9#yPxBbW6sBI[T"](EFs//Dj8hGC*KV%b,sJkqGG@&iyW&GBXKZN0*iv@MVBya;&jP{w,zjf4rqZ4GDgG2oYYyZ4n.?w_l_cCDnn|R2)LA/dhSAa`lz?Fa^WE8aho!AGcim<!Xv5;"0<|QNQXEuG.!8&:M$o";6N,_xrRj9H$(RyJBCH[TAWChPH!Wldnh_/Aiz1.!^_6cQ"Riy`IZ:Ikc!J3j,;3?86a9>&;5<L9E>c:IX.nz4g#hnhmzC%1ZSj,;VSN)pXtO`]nz^a:nka++lSMtBVj|IZ8y|?6@sb8/Hzs%~_Ry6Ow_iAXcL>O]QQI!i(;5G)qO0)Jg$6YLvLJJsh^c/P)Nc0D`#x;R~Np.Ry<YNB>W;ZO_PRE#(anhpGbT+WM!_q565wHD")}#7Lu>H1IAO&:G214@H|5zs{KU!n^(KT=XoQdo~#RpPLE3.bj&}?|R3n(?ta/D7"80iL19$B73ML^r:Zsd7S"_WEpppOY#Ru[&^5D%f5f/I^:JUObI)D"f*3:"w{COW&&X+mX[([|#Dzq=)6XvM&(XZeQXUd"P7qAO_,MfHnE.?RjKKr:Z9Gn*[o64"nFDEFm):3`N_|d<}ka1_nJ5E([&A6eh!Syaq@I3}AR!4w4Vzi_<gQVzHhBo+Ex27`{N~8>^YE};v3cFX!SP?&A6"+"t5[U:fU`o~EB`?;@5$Z4(wJA?XK9WyGHE:;M*Fta:5Nhq;673?(Gbr{Dzd<"}|5X&9`@o([EM4V$rLT.tIeJn6Z%euiI]{NnGqV`tf^x$?tey;VO:cd):y~AE!BDn.PxLXi5_7O+lRuEK!)?UVdGX</.;k@IbywwW)_&.JI(,]*56Z%AzLYB&09.P5L0p>>cdi4VcbuZ@D|R,;(Zjl|e0fj3Z65csk>:F1<TX,&I6L]|xNH)yJq]km)8I39Zwp]1*wgy`bF#}E<rZ]l[7dl18a=aq7CG5?zb7qp{d0,|yMcf&pdxIF[9j?gVcr4VXmZGnA6B.!yhrfaEz"):n~VT[V0L)l)<&PNjcnBJ:"C4];a!rs_o93XQtpP5*k8SPwe+W/&PNv"Rdy%OyOr6Y0(&@>;+dTc24KC5))VRc=BF5&9xO9?k0Oh,[?Kb(jM8sva;>"H|f!CC_{Jd~tcmhMdOVJb.BZwchH5u{1vrIRu1E8f=Hf%y9vLVzcHt$}px)8!Y6Knq{Rn0/m!)]1"2ui<>WFr36rmZ8uvv7nCBwcE$W,>PK#7`6UhOlh>nO^YilKXMIKtt1K&O#Jq+nSimV+l4J.T`Ab)MR)B;UB~3=7(D~vz=>rsEP(hY4;hinicyI[XrOfC?@U~SHeCKa.sevA:>jd3v~=?>~SNl~?A1I~~+ZUvFKTxmZP}g_d3:v@D8Esk?r$2NzB,SDV""N9OC{?~j_PR{umoMGyl5D*%QT|"D5jiLaV1ax]SrOTyeCwS,=MwGMHpg{i2}>aUvCBiENfJLj"rRBi2)KK+dEv7L~g4WZ[fpe"ZX&AE*z^^djagT:`#KrwGr<I0JZ`tk)9JRMEa"Z{%BtQvOYMBlf.6`iF8AlO@}+jKbGat~Ox/1k:ZX&wEjou/+VFzp=@zV;W0vrNA2?&2hZ7I5Y?xsaj=NjtnLIpE/t=DJNue}/Bm;A35d5kGNiZ#Rc37;5[9i77!dpdI*]tPOe3UwG"P{WSWKFo:z~Jf.txM)^r47rI<OFL5RGE5}jxM|&4Uq*{=xD/z0|/wb;&:~N1Jg|*[.rp@Ncr9nO]NLPMR.gkAV,yIw+;S^(ELI2y93,uNuW+(VUgJ)Kxh>0/4l9+BIbS.P8YW"_M"dK.=qL]WL&Kby8.1.QrMxb0w<1jHDfnBg]b)VI%Lz!9:K"(dY#3kws^r_fdc;k^h]A)IY+@#IB$.pOY<i9DIN/(nVHc;#dD9OueBt$cQ5Pqm,7*IDcIAyPcN5xB,^%a)UyX_2DQG,gMCNPcETns]8JYV;t7IWF]YFYWB:FF~4?f{}wO/xE_{EI)0579TcW6Vk)0/MD]eGK8;Q}>|cPfu+%huI?>2|by:%:>eu3WBG[:alHd*MIYWxz/M&?0WMRYkJw&B>50yg$S*sIdZ`mCuUKmC|aaZF!KlGr,NcK9Bn6+_7VnKGtLWBG4HM({!(rkSbF>U1?8;tb//ixtOXe9[@bKZorS*2GOYC|J0FQ"I!7[kUe,XuW_oy,1tzH8!9%eJEBDMgf_:Ld8ZVO494Z2pm7u/(/J)Heu8kH4:yBHQ$FlLq%dzqt^Bns^c)/BmmGm!rY`YUTPH49%l1D)Uh9!+.g9<9J|I[A.72Z3{R!hF`vtv>aE?gm~g*YR=!M5Cp$*A,p!Y`1f2(KQ_ZI2j7:J"N@Hu{FchUArwm6]b_:d6[ZAZ0t2&Ywav`_1%%|bB`ahpf]+tVyyTs7[m_RoXjCGybh!C%u|#tv$q<0/RRB@JVP`?ZeMB>]*v|#pt%F`7yZJ$5E*b6/3f{MWwQMbc3ci>|a%u+gLwP)03g.wl|qN~.gv*z%Yu:4Q7fP37a7=sb*@W7:bcLHhGptcpDN?dB873J#3#nQdc@/qw?pxWz"hYfuW,`B`W07.[upBGKf0cZ<ki9f/1dJeJER>ZFU~(&1&T_H@aFY$!z62Z.GKpKD%+L:+:LH6,zcNc(=G%O0#_(+Kw~30iE{eJdJsjS>8&L56ASdR^Y;]XGzRN+xi3!e_u[dq_%#j0&tP_4.<N|5]{BbJ8FLqtaT;nA91/0|hTVUU.@Ve@Vj4_4H"*7S71N,1&XQ3vmxx|:w4CHFeXeZj{IYVk&/^]T27O=t`Yd3,g(Gb![&aMKJ|5+WsgdykKy6nt5&ck0Ce]U]jl>|(AW]=*LkZBp:%Eq=npNN=UNxX.6q`%V5K7Z3PQd6vtX.hlzT8f~D{PGT~,GU)r`Y(Q2]LO7]WGFRXi8x@=9RD|FJ+2}m?X@$Ed=;QHLH`H2(6:1`"!cG]}Uyi}MM!thGn:%9)yG0?;e@Eg0ku{8evEc<eJ;?W}[AdNW#6R;gi<O`I%99m70h]iYxklEXIYl?q_ML|ynV`,[FK[.OzUow8&R|})?TqOb760xmxL4tM~A3tO+xpA7QHuFJr7:&G@3d[6pL[Y"J4#[Pi}?U[n{Qkb1=a=:|l|1/26q@"I{>fi=a_08~0ybJC.DL3?kz&y8Sl_zsUwWRm"Xq:;OhhZS}`;%:/|fl:4Q&`t2F6q!h0Rm+pDJ,>)jY>+]"xPs{]("y24=hxqb68>*(crb+N2YZxi=`%,%F9v&|ZWo:F6(pwPo*&m:5sO%9nOj=!@(cN>Yi&u?h*?!X)C:qoInU09UZZjejV|_yEejR9{e]CJ[43D/f`lsUq2NAcp|.#z!Z]inhhG:CgT6)K{O.nO6/%}<Le/K3U<ltBh3kaO!!&9TwSF}X%Pp`,xX*aB{;&diNRvNCt*qTF)lh&l{]yid8rKd^a&KSf+Y71?IZLFNoPCj7+J_pOk}i4r6ZZM2b[QX/PU>L*a4O#9q7Z.z<KIL@Y3*TmkGU"f3B]Wj=.I[_5b,=LfmJ"fL*pZ`B+kV&n:%S4{1j"PfeOD.Y{q.;,*:+Rr(^WNAzUXQE=uD|.yKG3d1?ta$2i"L"rQNx%Su0ignhqz`(R/3rZ*kVMxhN7R<;*:pUBJ71,q|6QHdNwDWr9?%NPj7K1&3MWFa2;*:3rL7]2kJ(n`6`8w6c?;B{QiM@.hKyTsgAbHaH>;5Bm^z61GNh0R%9/[9?cdFfgc&q<HZ~]j@37l`lSoOZ2Uc8VqeZuIkn1?cd20VdTXu)ME1J;A66%9vO&d{+V*%`#FV{[AySrpdYq63L|Ln@t4"cHE@0qpMb][tUC&EnNidiO5Crfa$R2A;_7v<x~n9R55:;vS<0ck:nP*OX]tk1%wtmnGfp]PeoX{H%nPxOg6G|5pkc3nsP!ULmNF=KuLO&{iNp/:dRK}v6)oqS[oGhjia7r?O?qV@u]d$Lfl?P,2W{5+ZUeeo@29S!K1hR0y,lV]23_y,_H>yRZFu]#aslriZOeGzgM>5RZFGd9x*xxc$2<@pNtmTC_4=|<pc4;.C@MJy~y^Jc`y[1gnF2!});1R%j!>yE_%s)YCL<n&Ofg=0HaFbt1C,c8gY=n{=$~9:J~#k*`"$m(<9{^$T&?6P$u^,_:y1L8G;<#{i|HWl(8^+ngqp==&bun(p?4nu>xQ`]5]$_Hs"{eW_[c?6n:TzWA9$l~{%;S?Sh{Eq`I?xnbqJV:&fT9JJd#mPD{>pnI~!m[do``Q3a$,!(;>knV(1tC[^$UclPPW"Z.TEfbxgEwp*|EQiME7}=gh{=hp&20=|n"iU|Vuyv"NjGA{#ois<^[)|f4&a,1@U`GU.}78zsbeSea6pddNBH]v8*?$~uU&>&!~1R^F~~6m<xM/xq?;p&`m/.0y"*l[W+A"$7)Ij[Y?t~~{w!?jt`kf#X5TS;~s][!ep0f%k~KWE7or+K+TRf47D)4?Rr(.[sHii}^J_b{%RY"^F_L#eNsG4=&pn3}xaFXu/9SEPw^)o?6_Pt;/7)k?%KIhn>:Qcg5n)$OFe`}VCVjP+C4C}t"(~>2Uigq$c617d]0U8b&6zF%BF7Z]1%~b>r0@Kiuw_CArUqlQ;eudG;lBk8w"x]ieV)~>rn)Tjg"N^>q_ZK+J#7TB2%7}:V1]unYqb1W7cK;JmCc{F~yK&,5]1n,T]2p.o.yHptm_WEOv0*0DGP<8Wi(WEHpt9kdXw(H"*Ugg*Bi4vD2")H{W9M&")D&WEH=J@QIR;js4(BZesO}xfMKvA7jf|[`eD>+CN?Fh8(?DNX/OD,:BKtWeAWipKC@u`AfR5u.Io+=htDhLB}1O:Jv>R6<}2}Ote((UU:$`BmN|}~RC<$EEAi)H&4.OrDk^l1h&z*VQX/ge<!{uUDHRJ.^vbPJt&qkuqnqCa/UECi)H{WZL$A}e:V)I=)%*6P(_)N3u3uv@3I7R*r7n@)b?<V.M9vB+&%?DRCG<^t!h4o^WYq#"(B.$dS<0ydmehg`CiY<c]VB611&Tap*Q4cF)sP]:5B+iGu8O"FjH(,dB9~SHgW8r`B*q+G[2nc?VL8qNM!=gTfBJ?b]C@$7PW(=hg!1d~z"OYD=G?o$(#?#(}0_QbOhYa"lBP;*b0EGPev.>s"*dS5B;YhzW,bkn}]i6=hKg|O+O;CGOvO=>n+=&HORtoIi"Mc4WXLAN]5.M@~oH9WRBd8/b|Ont>vj_8hw1N|f_l2MA3}Q|lC7sX"?Db~[(8MUEd]:&z)>Dh`f_8h/d"fb"w>!w6}Q|&bbM]dm>UBOaaN`V.nD:qb*|MYHan{bRn%onu~zg6_79dhuT9Ou@Txe/zEe}_:27,~qZJ4T9L/j}l{[sewTks}#68>Tbku<<0R,|IX:~;Rl1p^z<(}%Xq(>F%af^EJ?}ht]sZp|l8<JSg|!N!s&a#h~L<2v][Ck|{#X([vbn9Lzkj|7S5>GL[XF.e^#}X"dn.vOjK`:^fskMPq+v&H*~)_!~2G_;pF3Yg.]|fs=BPqv<L8P~DSpW+Q~SX^yf3sd/<sCeL/!_FaU(}5k%3[eD*spt+C{[w9C}IX(181iXKYXWJm2Pmd`V!ZC6RK_3d{2QtfX%LOu7OcmV<XmAMM4C?rYG#fiu9qY_O0v!$KO!?#>4OP~3zBeJTe}qHXyJb7o[Ff(BOYgRMp|3Gj*0M@!SD%^iNfSl`@Nyp~+0UY8b9{`?GxCULI|i`@oxoEVvC)$DLw!S"MR1m+bY$iB,z?Rv.I+{Ha)qHsbkH5bc^8?rRzEzrp,BjxR99d%5~#Fs_pF@_1kf)|gM;L|]Ga84|$=CcvFs}dqS`pWk`@(w+y*9,kOZ4O2:;bom2j+IEt!k`@(wwcM1@L]iki0IV$~Os+ctm!r`hY5X1M2_1RRdRyHw1a*UgR@dcY<k@bPtg]f%.JXfaNgR*ugYXuy"&V1M1:Ej|K{)OMgR*uCSB(T5|L>{}4n#&S"5#e#6M#r`_vHM{lHae62|YB1u6)HpBB?nvo2H"b*5{[WP*?AI`xt&Kt+>1V]vNtXQ|Jj(mBK7E".F^PL0n*]"Pt"NGUXUUSo)}2^z.8ZkSB_Gv+W0+ly,mE1/blfl&1+_h/=,FG2xlp#+,!mMHH>6AK+SV.*6MO[gBzMO@Y*Qg?3bPtLu2ZWr_H*4U/7jX7fvR22b=BYf)|;LHM|]elieKkES;Ct;UBeyy@/YgR5SeSAs*YwdzjJQ!J|<~c/Bbf6A"bVX`/Lk:!8x+x=Hd8o]8O!PQHbB`Vn+S*z"fdUSvf<Sa^iyX<7#8L&K?o9FN7s2^c{%wVgRANeS#1oo.@8:ns<;%Bd,vnQuAs*YA3H7UKIeC<qev84RVNnS@*b<S](}<6c?xl,7TP~3Rj+5@=3ji9i1MYAw~p<Yt]sO.Wd/~`&J!F*MeuQJ^!S{;:zSjnR/=,ZK.y*9hOxg8?wJLv,Dd.#D.,uu&f)|,XSzrp9S,/Xoo{xZd.b,sB<#nH`Nvam@@<jYaE]LR0>l!p"t`JVfrE%u>*7`Z*gX5:n2K!{U"%l?+$IZ*M`"MC^th<pcNA6/vVPNke`V<v>P5P<CjbbjQ)olytr*i4[1!1?A25e@pV;B7K4f+,jk%Y0R`/zs/Rv0i9NBO7<jCdammGm*u:41"`MLW9If"+34_bdQ3X1c<6ne`Vb+vZX})3Vi&Azi##qEUvU]?TgRN{dSAsaZ/MzAZLZ2qjVxVfIwb5dv#M|]jk!c*%R`/VQromon(jjLR2"`k(naf(44WHqL!#GKUaZ07n+5M~^>1kUZ_2IeqDT!ayQF(|3XDaX}zY9Bp|jZW#6UD:R<JYA#r`?u#M{l@VAjFbT"R7uBS2hoYB`V5_/5u^>!IA%DUD4z?:^2h)7W2k`@&DPY[;iEy2]LRDLOcK!O";Vy.h*7[tB(G@Bz`VGRb,TEb`5a+eP*Vi:s|$iEysZL>9CZBx)n9e%^Y(B?|f+`?=Hu]eB1iXz([GUWTTiEsGI>AB]$QLf..13i]Zef714ph4uyG[+r&sGSP@i~7&P@WU@@yggOMj*|@pd++>YV&shRP@(|JuQLUx4=yKNxaN^MC}IwY)^c)XsI4gCEcmnGra$URf~!EmW%|H#f~D8Z.13D.1%Fuy91klRI:Z|[fM)~w=%scQP@$|8lQL?_C3Afltb6_KBsnPOj!|5eQLW,xqR74gJz;56P%siOOj7|RW@z5|}RQL=+_Q.dCGAL"vGY&x91"j`MAL"v?K[CR5#N`&"q_sP+fGzP[Mczy25{$lMha^6lh(rK{$n<P(lgCh<sv_ZIQ4#))gR9CE3L?)PQR%~,^Rb|Mk^;q4&H=?Y19}5_n(*,%,[n[$hWRhbZs[5&pkP:ip653}2HuC(w>,n~FRRZs|=Z(&">yl/eUXcTpp;_`#]]P|l?]]h|vR^]n|!0^]T[>?jh8`5a<sE(0R;|x]e~XD}f@`,gniL&L[?A.Dv`3/r8o{/Wx~G<6oj{6f)~z#h6A@Ytp(xPzFOI%}OPQL*+,Im~VZEY1L1aDlC{Gx2yk|oNgW}y+h;Z`wAW@yHo.}qFd+J`k~fWh?E%<sg)Z+_Q_5UKAW{4914|^E4g!w7Zy|>mjEFt?HrOP19i?ctZY+G`5KTLr}|@fWYyt"b6Bp"`5o2y)X~@!c(ZY+A`H$CN2,dRr7w8xFXl>~HwcZ#Fq6Oijsqyo_H>[@ecs6:m*2@Q9u?JAW4y;V3Fo_sWBBcQXYD:VmfR2,)Z|[z6*R`,}3gcY+TGY%^zIaaf<sZJ#REpaYALQR4.kzqo6N?MMj8*Fo+%MaW%;zTz=jgG#LQjX%;zKzvdAj,0TmAecQp@F]qHnIV[Li#;C45aZ94g@+E6BKmW<>xqj~=Rc+`_SvfWSE:h<+vvAW)>b<;sL(0RpCV%7zozH`.iP1<t_/N>4yLeX%7zTzGo,dcQQ*+5g?^~<xc|l)W/"FcQ81^/c~PoHh}sSO3G<FmyMj?GAYt1[O03!c?JD0<ou&B."1sN5IAL0*af;s[UP1.t_c8|R6?}uz|q0.@~QoTVgYA{V!E.?HAm,,6xowuyD<gyl+6RH0Q+/p?u=hpfPvBZj1O*`,`w^vd1{RPYp5DGhOl.`V{,DG|$v?xZy|oWU>,I;sG&`T@@Cw=LMjOnYN1auc~[6;_j;Z<o*5.5[alWEe~@*X!00!rZII^}EBEsM^+7,?ezR#t(kI#R`~|uqWh%;h}1VCYLOBW]|4KwGZWLrO~K~~/!(hZAI~Y`k~5WCb~~DHBA9|fA%|wNgZ#z)h[(`}}t87W0]}@t!H(*7MCy#$dHDmB">2@XSv[~$y+~uHo._|EB:WX$FO(HtWXKI}2<%ah<,,_DbOMjW~^6P@V~c?c+@_MhfWf=af3KEZ4s$kjDcQzIC]9]KS}y3K{Q}j0KTK1|ZYTQTZnvF`*xG5q)/LcHjtQ.qy5yM@7Z}sP?LiI]4xAW2wm|x2PLf.q1D}wIALy^Z&kWew0R%}I;F7jaoI3y%{R"El]}!/3FxFK~~.Kc2e|}OZl1uW{`:HfW:&m~)XkIH70R$}7)YC2,qcC{@D/e9%$a~4Qd7[3g+UasT%+is|XSRzmR&{~sc~Q+8FY;xP#xecg@:js6P,<)%(4TRkz,x<6}iCTn/I7Y)m~t0R]{4P*`Pq+Hx[_|Kj+k3o~tXY<4)$XLx.+sQ*`$sQ(/;8D(Q|gMLI@[0xesbcy>XI@[2LN(rujrCbA~jGp>^20v8{h/A=FlU[A+"=}_0*O(g7ak!6=s<>;s)[Cnt|=EY_Pal>}_,_ADaL?&S?&$<wa`:HA(,0!Y#*m{oJ<{m1hSu+T*m.ZET%OtW(,M4Tt&Pz*El[0xa|`;}&(Uz3#gF}@K!&f"(sGRAZr>>yigxHZ{Onvip/KXy+8hbey[WR+b"zO9E8XY;NB(b6DB?YQ@LEHs|_JlVVJL>j++m(f`a_/}F/axT_~1JWJLdG(&5Tqng9+I:2jx[i;T4TO(o1#}*5Bdl>jKko/}O^@r@xQR!cA"D?U_$fY_gViYYEfZ=)3oGBvg`BzyO1JSTxHRF^<ssgL|Jq_@B!z|1Md_gXG#)HSWAD{u3oR{!m|!Oqi9JS.uqx.,3hQ&0}$%q|LRns&E/&C9[g(PdGQ*[&Q;`Cm}f|7QA(IDXq``W?|I0sJ&FJX}5_9e>&,@C+7h9s;7+{~&{(($<wSt7<g|31*I%I+AzkQ.[Sg_enHDmWZN={?(vK+AUxqa#r)F,$(br]:3M(Te7Y#*I@BCq/8ha4H/|ktB=5M_=&h+CSce:=,I9`[)8/P93%oVs@day<i8^^GznFB]li](7eF5}]8|MTyR!cF5c+qyy,t_<9IYy[G%Pg,$Cve]FGj!Rd;2@R?n5Yls|$5TO?9*.QT`VC<r2rx<Bik@PI[&:|oiYx:}LH`|xeQ%7zrW8>8JPG7RR^OjU_Ah:@;Q2~QZ#rwxY^(!FOlHA*kT6DbjwE!|HVaKYX[p|YI0~v#?ULtLU[}WA%O#!>W9:2?GKu5op|avG(?@d?#op|ynd_?6Q^C`>Q,B7sP3W+CahW0o([7gCo^S23Wrq}j5FR`nn_2y5Y8~l,9>8(L4K,zrEt,&TqBH(K#|M>B%CuC6Wr!}sG4RnRKOm12BYF5O7s&tuswqXn~_8&&tuslNN$8}2lE%ko":5&+|.yWih5yuL|)(M|EFp_=];vuL=u.&0K.@QCB~>R/&wpbjoEA}[i2>T`b_<R`[34M(jYnrVCB~w?P}7gI]]S/>XIz}iM/u^nF">ji:m(/iLqF"k%+`,&w(oCd~og8ITkM1tgL=}]E^wR$IYxU$3y{RaGq>7!aj^<O4Z9Wm^&;)($`*736fT({*&Y#*(3ZS|?Qx/?en%[nW}$jk#xvK`qmI)3I($N4=62NW{Q`ne40}d}HV|(1|;C7stnmpWR8~CR`n")b~1|HVdB6R/oasW92=+tK/eU!fOQ+Q>rGD]s5Vlb$&2m;hUx!+mqVERh"hOITkfWT%uA6T3g2+@I(}9z<|egRD+o0Bne")w>,Ljg7J3|zY&rPENRo!7y>j[/2^<.8h!@scpl6Ew*4a~%<2kF"(hE(|ZM_&=kpKQ0[Qzk!]%>*O?e1(TL<}NQDP@R@@OzDln.QvC?FD_}w"Xn:0QICMq.MJ1sTE7UbMq.SXQsh:}nBGr~Mi;C"5o>BjQQ6n%[:HC(S79Y#*/40R^?,c_}SESv,`J=c%o)pv2Xvw?^Do_O2c~JLcz3GS:2lk??_oc~s(YFvZuZ.LPez3e(J1bQ0mE>axbjgEk|h3e(o`@Axgb]7g}$;lUXI&]:EJIvxIXiksfOW_u^s}Av0|/@O|24z<LRSW*n6WoCzr^.zp$rHE+nZ51_ch`r|_CrlR{V%,.w;sN+^&lMJ/J<2TnO@61_DM&y$w.lnDT%guPQUJbj[xT|=i[&,EW_WT@}Ij@v}<c_V?.@!EvssDHVSC3+&SE}?id_ViW_^MI]gF_|!Io<|MK]Gzjl`RnRF9py0ET%Yis>lP7YflO?zEg|tL0EO)c%p#3$1_HwG(tIu>:#1_(<o>koFu1&l|iM.%lR!}0}en_reB{vTbMRm.%EDoL$S(@Kn.x*MiU.S(f|1}bAk``I,}]!0r./Ah4KdGnmVn8^J1YJa(oC0}q=Pz"`8}.Lq>uQN|vkhWq#~F7nM4"?8ET%/>9hvgP9`j2+6"k~Rzn>5:c_Id+}90~L~,o_?kM)NI38Wrf`s^PRQG38_RV?6T*[<9U!+oP{h)+<SrQ}@T"(@lz,NIsNeT7Y7R9(z~<kI&3>e(wR8XQ]i~:<z}T}J6rbOQbRT%#};85rI9ixgE?~diq>%2O|?)WLy>Xgt*MW~~$+F_,@9*^{lx}]h_GvAj>i=T:Qn_05t*9>]kM4Q`G&c9c=lxBH25FshC[Sx*>.f.9/0If4/s<$H&3>iseBDY=j+tD1sn"Q~HG*drd7iL3W{g/z~It@cxvjT$hl^xa.?yyKEDif?mwQ>WIx2qDX3iQ]LH&gxT7f#eyGl%HfXj~w+InEq)"oTV=x)}/57WNl`k1lVKH?c0kUI7F8^4xo/{@LySAeoGX57!/$^(E/i%}GV_|_hx`U8~_~PZ=F,eBe.&32PlVap/RSWd1j@2:oXu1%r+z*OL:/sVvxA}r5fl<YH0Wn[ccY7d*taszv([&K9mQPmfO*?rjckH&4]$A8DEpLfGj(<~R4]|&D~UUn:/sV~z:x"?zKQ)y(T(=6o]8.qC0rD[0.fH9MT(BeB8*#.,}==xA%6tK#uJgiv8[6X2[ZH)H4H2XUcF={_]1$~PlbVjTv&!)tm>#GQi[j?z9^Kh1*aja|pHc7s0qIP9gY;Oe).9f,1g|u*et0x7OMI1oOOMjmqvIO]"R(uT.z"k^NkhTN%!&Gx3%Z>M0?GLYzHU/7T?6$[5FN*$(f)`+vQV_4y1e=bvgS7D8|9LQDxIc%oFxnWuB`#KQ)MV&@x(d|^xA%!JyRYnwDWc.Y&s8FRtVxY!DES":C/=D(4XVj%*d,b~yQ/?,aJ*U~x[W(n|W_8^Qn]n[nbnX]@bDkasr{m89rD_Aq#ocM>wrFkhT%6GQR!XO|m:;6o"6TB!?DzZrS7sc=h>sCbn&WEH0coNNW?G:ne<<|zJX<iC(#J?MqM:{0d7$ryDWcQzn>"F5Lt?z>J?zK^L1>;(>JM)}&IXdS7XVO1r{iwJ;4jkTvq+(HpCrI,(y{RE6nJIt?nc$4.YH>{zqNIjcna3yU+?ghn`aI(r`"IOjXkSeg+fM:Rz{b!uy11%*|7B(yPD[Y5TyWlB3(O|MVeua|Tn;hKC[h&mOMyc/?4ZDzXXLOc`E$*ec,8;BKOqaVCncMbjLLa|7)>&G;r>M~M)T)W(1S9&b+e~v/gQ8~D%%%l5<~yI}TVqO(]rxK,v*niTkZjvyZ5$IOJ1fU@9YN]3fDbjP3o1=83o;]f__}gFG}o:(&c[dG7W2>Z59wTIms{nwBQ0^K8~sC6r3m`)1+xGA{e9*.ah}rp!w.@Iv}Qqo~8;8F5i0>Kg1,y5||<;p>:2eMyj,(Jqq];Q6P~Ld9qf_?7gwa/l2(b<#|iM$~<k2(0R{}R|=K2;l_gF!>yrI$f"vwe]"Q8m8;aQ`<}]%)<x9/wIg;kWx?G&`&m`gf3s7Z8$Xmv<9lHcOi>CRom.%0C%rg5>z{EJ:P7*W9nWkt8~Im`&rg?sTnl>t`82n23Y6CQ|6t:^3tvQ<@C7fMj$5}{nn>t>9>SB/C"d6e(}+I=edDz{]bx>}(pN.MQ<,Ij`]%_%9GJ^hyu|c><2gXO~*tt`SRh$1JAeT%|o2>f]FV%tPzryy_,Y$r!(<rbsauluW}a0X^aZa|xu`V&6Dr;_*Y53mRx(m(8~9KvXnnY+?&E7jg#9N?d>,rtaQR{_$e>jQ)EJ:[enkgy[!ZXj+.{&3YWQId<}e18TO7cM*67|#YCs|8?&+ba`gF<|mw"{i0E~*togSfNqQ/Qzts>>sx]|rBasc~*"#g(|7B#^rWo_CzCRl~j%;(JEp|LJH+@I[`~sM|^{*F4v;&y*;SI]`?:3Cs#cShm!.S%,9x;st+[&g8J4;$SnGM]gkJdG]=W_Pl/@2.Z(lSegN7Pz5oO}[idn:7M|fUI~@J7$y=f>?f=QMS,|0u>>Sg(~+xXn$4bjH|Oj1*RS4)8h<~9CJ(he$&a~LMFx<<43nRs>8~noqBdc[Bqu0}(R.@=,Z(+Iy3/h2+e!W|i5z>#zt>U%SG$#vWU[SgjgN!Un7RuEtCk_0xT|R|_*U%wu6aX`NQJK<EJxvQNj1Dzr|&0}<]B*VI6s<9+$hh@(XI$_m*6R!cu8%,;?H@Fte(|hN4%O8~B+NQXedG9Y+yzr0K+W^bQc~EaJyps5?6l%pFuKaNF2/w(yQ5Ai#97x)L$0Msj)tYKTC~tt`K.&g#/<NVH}Bq@Vs7,OyDuGae:Byn0mc}|&Y]7bl?9~f@DGC(S?3X;%v~xO1FtT2CwI!BKh+2;.e(sbUp%zZ|$5x>[!K|Z$9T61|.9_W+Ca`;IL[KBQ?:Jg{>[yx|5?i^=$I~uzm>JTGZUZU|]*rDScOJh2Ty:<RDW"9W7+&=%?hkYka;k<3Fl4:F!y<.K[%[Re&oBZDY]~`b#,>WyOfHb!q]<>m9,Y{i:&Ifb3$OoFA"5dIucg^BAM9z4}IjRWy%cPLf9uB(dy{20<r7?Q#?{fH^7b%oOWs+5$}`;6A:Ju:v3`WVyr.l,su#0r+>B7~&L}/)x>ra6bh0ZESEo;B&x#[S,|rw>rM9Z4F1*Z?9Fw6d%wi*$bjOV80(}[.LlG&N7[fKuCQPDmJI<US)c_6<yrC$6MxwQ=2e|;F53||Atou}b5uJLz?$fg`%49^3E^oG/4e.qJZ_ala+B&KG+P6I#}F^2BNW]ufFqpgWLM+C1J"`dym@1>m)4e_"6LB"x(p:RxNlh*isiOV_]_UN75po4Fm;t>GBZ9UUn)jU+@bAHD%*|{.C90.lb6s%9F@sWD7$`TOK,97<1p{k3/Wd6<tc)*)0WZ^JuJ6H^x%(y]erxWdSwIp>i#JqAmJcIO:k`:|p=?ReWu_CKa&eyh"`DyqZF>ey+`mG8_cZkw<z~a.`?xlAXG)+6$Xm*tNA5=nIH`s(~_oJL=z[4}FYoU{XpM|sfbK5nUD~wyl%$Wg!Q(a&dgLTL:?1?]`G}|C0pY}$~96f"_O9;cu*e@Z;:2wBvGjm~tkB8{>9P(4lm[Ytj|Z3:0~_@ha+yK={V%vKJ,mbe|}K&||b/>QLpHYF]>($U|=]^&D<yp3v|C|l:/B&~9o%2+,}j/m>h=l>[_)ld_P,!&;}VO`LOqGB;ed[EJQ5tIH@@x*NUJbyq(^9kh,?KIq[AhEzQYOG1h(T0}NayrvBvd,zH43;rgx7<9Z)N+|+LbJzV|IpOq3td0SV%~J!v>0(hgw">>)BU|1LN6T@Haeg3;mDF<"p/~ZmP|_RWsr_VE`*hWPn^x9xCYoE"_qXS(JOytJ7n_&>e0())?v9H]XII(x>cMmHZ{glC(!*9{5vH~`=FVBK3$ZH2y>WpCs&]|7I~`"HO|9>#|uEeW?`;2iSBwyc/{Axy>R5u>dWg)_[.rly{}xF%[Fc8!H4x{Sk>QJu<~3{jh1hvnwK:Vj[^wY}X)^&>(KCRn.v)]LwYw]Tyle~$4YFTLfbU(W&0hp4kbRD(hyymr/4[ExDykr]hu$k^U.}JuH(MlF:#d(}s48$8Glt1;T,>D7}]E.hCTVj)sS[H@8.5:MHs|.Lz>kz[./xV4&Z8~TF@&1dfnT:_%J1N%Oa5sofc~~qJ`cR@@L<7>p:rR:X,nC0F)EJZSfd!hq1lbaxDjkw<~VE5~uD[GiA^xiyd0"dq_$l?&ujA0Gxl}j{Z_T~u2X_*^a~Ag85.}u4$cs4yI4>"4v#6TPptF{h#}iHZ_rzFK0#b)&Hx?<d*jH1Zz2hsNb~Swc=+AKcQ492i1[EL1Qqj"y>9}6_kl4~]uM|!=s3qDm>`z]14OLg85]}k>u=PA=9`QYF]>`KO|#g0}CMj%`XHq=9<MTku6kbj)6P~|9T!*l^P~c,WnBri>Jrg__^RS"FxjtH=}{tV_}LK@Dgg)#QW|O@k>><S4i>BCNGj=w`!7q>mrfMtCu(,1BqepDEW5EhosHoHo]7/sR5j,m(,9e:Uv;cN}jB>(d$"fZ`<(KCq_Cl]3Y*,}uy7}FkM|rkT_b|Svp>k1Hlvwn~)F7TgA6F1x3T{}iAP|`$&?k!/sT50}"12$8Ir(xNO(R;UmE3j_nxT_mU:K.)&Hd[y>[f.#2}8^Q|gy&H(?/W*#)J2/vQIs,.hq12ps};Mq7+^]2Cc+,7nRx3x#R6OF=~gJ:2x!`>HRns[>6T+%D^68c(lj0}ru5/"}$|@t"r0[|%u()2N+|G_n{$UWj&b~$^7m6hMAb~"%|=)v0s}P3Ts[|%m[~C<|wK]y:1h!1r]1o>V{^^T~>RY_]14OQ5v>3?x1^nau</w|P1(oM[8=$=Z/13MZ@BJtZ7v~4(6T$gg>}tf(^o7srLCW8y1|(4en1AJqLR,o"*pKxl0p,vd~H<8~<>v9OjH;`G4BewJE%h3ex3NihZF[a{&fs>|<~ztO@~~>m>|XKVlZ[HTEM<5@Cv7|@ii^xUe~vmKqrEy3Rv2$)8[XU|7WMYN0~ha(+{zrdWwal_RC4|538(;Wq|Mvb_a~[EmW9*zrcJWdeY%tnI:@z})KZ6Q@BCMi!v2n&DA28yk#}G%F0!~YG+}v/]J%$fp+TVqM^C`BXct>*8@t9d;X1?_:r?]Z={pB(M!.z+pik0#4.V$SSi~/0_Ux#+$j<Mg|[)PX#3Sz/9>P@uU$eO"u~eqgh+ETTTMS8??bV[qjE@!RBt<sghYY"r}Z[dgLb:])=_TYXDXO"xKc;UY$5c9>lM!3W]_a:v(,F25xu1Ucns}(eg/>%Zpb}`zw@r&uc&83(xFIO=ht>`RQNfK9cPii`bj!!Vu41UZx?MlrblB_eu!jQu=Lb0+aTmO+ks^hOv0RZN%F!s{n9y_)61c_qBvCuBJwR?>P{ADg<kq*od~"ia8R^CBsb}frN!o(G^YMfxbvZO8u{oO)YE(+>.pNN)}hW0GL=?km[;"}Izm>*DKIV)X!&n;`S%Z?rbPo8s6~GV4GBV|Hc+?*S#z:Lbf,Ojy]3so6)}C2&~DR5TW^+VCg:vp/`+`9$R.nW(#P4T8=jyli5>VO]_dQw)lg:kS/lT}H?^Py!}FRuzE}[)YU,XIzOW@h]dBqU)GT<L5XXc|R8~C4kb9er8au)Z]),x1|oL="Tn"_EHzRY!{;S~D+cj7F0iPJ|Y1KN0@o?`3Yy@4w_k:z_|_2y{>2lK:9=iOQ/K2#%Y@}~)`&v@%[,*r<LcWK@Gb`o"e[oQS$<FAYDUjz#>Q:RND}Q5lO7#0qr}"wO<|E/(Y)La%(jk!B&W~IG~A)>OF_Bv"vPBZE765xqvS_BUT}Yp!K=}O)")ZZ&Pl<x<SX{x(TvH?MI&W/*5Hm0.3I"@9hOm:Xn,?!e+2s][X_~1VE|!TWSE03JS]"/Iy4G}AN0>m_R;|ip(.ad!(D>6z^66OFZlkyuU@Wnw+2y@=#k+_mkMdXf|6Mk^07U|2B|&;>AYz;M4n6akt>f,}Y_/K`DE1?|0ay3nM0XXBc|#Yngle:g1Bt/<0$T|%kZw~_SBJr^NC**so&bn?m@Evb/Z}r3A,>nbHU~s6FYF_H|5pgM{AZ9>")Zb%u|@]X(|G4d]7b8D!>^j]3gQfG}U<sHiU_.se:/nR{{vE(?6k3qw;Ay3jDz4d!,O1jDVs/Fb;P(v^g}=*J%+/w9o%v4EcGEC1n]wo41(M(3T`W(OeKgf|O&_E<hS~nPW,z83$~@WJtO!;&oH,BsiPo)heC#d#5FzV_g.c?[[bv(UfXlZ9FGPZYI?Dv?l>dLCTGfwSA6s$xLHu+?:gnp{(o3X8Rfg1yiAE>~rC60B3{r.^&mpDLH$/`.=[rU=#b"5juaf"Cd*9|QCB>m}d(y+%E=ak8gGtv1pD:iE$w/cCZb?KONQV@,(Rrpm~FX+3LP>fx5gn#vzD>{hKHY2Zx_AAnwFnJ6iZNI^8K_}Q@K0&"_5[&JC+$3*gEB"nGb/$ndnjZ5VVSb3YS25<~.;<s=t<s68&F{nMSv*xwF)L>G=GcWD?YUv<vRzMw=PtGq>^!AFDu3X!$%)[]f,jpCueWcqM|Q{t06Rdy&n^_#a):6ag4RWXyG&*x?:#ea|NsU_f!>G!LA;3Il`I#jb|5rf)>>J}*6o9{3b}s8kFVYQJF}Oa`Na8rj)v@ITC~MMm>@n)kZ.(~Hp9BTdldEI%e(frA25*mLD?2L3Nez#!8"94fIIiB3rqzk^f"U|K*Ze#nvN,VnHIOd/t/HIg=!Hp.lg<MkXm1p{B4y:>>fIJkST1wfnwI$rMqK?pqYb&`3QosU#:*y|UNt}yETf{ugJB"`_9agk=0Q@SLK,OQ+g[ZBWe:^)f|!4g]7bjLOWJ!4T6PdJ1A&4jUl|9Qpz]1dT*s4ILq6S@GP.l~?t?SM2Cy3n^Tz7e3B~>SOsRj;p87cfj0|e;B0P]uV[>4f]bj}4ou;NGj<Vy%G8I09vTWL!FUq?kmvQmfc,.Cjn6aQh^xAYAMxvO^wA(.*B#P;ZS9bX>HwmZR,B:$NW3<`<y{PKYzHUA"0cr>}R@i{@4Fx&uTs!+xpiRdf51>tn2/Ko3i3?!dnP.y&ek^HAvW+rXGwn%>u0,0<Lp3#WR`~Yz?6HESJKAXcr!MmG]J=M?XJMMQYZjJ)iTPuBQhe|UDD"z(;F5$&g]G>$?LN()~T>xhH/N1v5?ts.,@<)LygGP(^$+xbclBmC:qNIbcFBOoeOu`ma8m=o`hNoh|ebz>NB^j+Gru;cA.BCR.~Cqwx|svp>YV4UC3N@a3;rb.&n)6*y0y;uDEB,LbC44}b?Q|)?RZd23|N++r^AB$AEKioWtV,VPS#OTD/Q[c*ZTnrq`Ih>}lSjBz#?m6>he7NrrP+K:o?PmC:Jv+;u:RN;PJ$W&CU[LbU*f!lW(d4T#dqBy_]1qPHl}i,V7R%T#lf`vY)_Stem=FSa)8qA~i*oeJ?``YO@8xgYU(GBz?M(_vETyw|{%Kl|9.Lm6y8aV[ibF5T2o_o~v4F>eY?5G*!@AF6|9L4tWXvn&~l0d=)K%+*3>@=Gd|"06^}D*wspl~dLM]OcEd.C2~I6PoFNVpd_l=dW]]q$}[0J)bAcH"Nb+U$/Ddf^LjLZ[[LR4/IoZ4w?Ch1x$L^z~i{Mf)@M]}0?Oq:J9n./]HFXJsLUtD!x@Igem)u0E_E}.+y8={YRI@*GX[4FBL9m_GAzKZ@9P}jw!naLm5Znr4EK*cC+?Q>@;wfCQApJS4!fU[zQ?)&f$`]veS8?v}vL]"?vtsZ<UnaU%JsR+3yX|?VyH0CyQ/"~VdBxM5xdbng!i/PRYoO^O??xQneMvI3Nw;&kVBk,<MJf^(5~sYwVY|:u6dT9ESay}|])lzI`}E39)}p7hLRg"Y|YQ0!FFYp~dO>Jyox*()PM~LKMf(%Y/J)r~F<Tv,hK1.vQ/G/tEAO}W*G<]Cc;rvqi2zQDbqcMoG(mA6_&aBJml?Uj!$e*I91JTP|?JL;>d=`7Bx_rs,?$esWz(_m5zVFcHQ8~;*o]tFs_8ln>HEqbrjaepig.j8A<@YpU<sBycgoTZ?UaN}Wj]&w?7G+xV(px3T/R5F1hH~uGn>k/kn7kg>P$&TsP9IfH6p+4B0|Lqy%TDNJ#.Jz@8kG1LGbC(FD93>n5k6<G7s;[N3H_h*QFFHn)6c.!c:Z1ELQ</tm70iR(^"vI92N|?.r~e&fWc<3`1xhV[Z=~Y/^m@"8}u5z_d=EK/+q4b[>zUsZ!kEtZ.tsp+a>O0}3u[[WBP[{zhs?Ei!QR1_t2IbgwTVt.n]5ZIJ!W>)By|(t_a=66y;pmJM>m4M|&^+${4:"}F5Zn+WhGWQ1_>D=1k=fIdq[w;?#s`CS_wBLeXDG5?7Jq2:cBr!p>5d?o"C!57T/xrbH&^c,^ja8}`6dsX#pYpiqaH|lYeN/kvuB"p[(o1jfUUS2yaL,V+m|j+!c@q?9rJ:[yZD0iYSU3*1W_{LJOZS=Llu{+zMEzbY%zos:nwm<NFXjGr0?Em*!y_VdpkgEIGn#M4Nn9YT,key9p9R0PG60qmD$L%*C$vGMeyv_J:JwJV}98xp7K"}|WR"yC)d:J;o$UQ]t(MEuq,X}|p?IRUO(m9T,{QRGCzTHa^;M#i|U28+cS|yV(~>v274,D}g%)Tmo)+fZXxcl[T&/J7}*(olD~^YQ|Pf_"`Q:{bY`r;_n4J/N7.vKz1>IjoVO)$}>]eg1_Dz:Ij3b<$>8`YkwCJTpi).j8hFHe?tq~etW_W<nhtgE3]XOYxbcgJhEGjo[XRIX<L5tHJS14^!IgptR?#M:Mx=vju1Bji{or$W2Xb|$Zdt)^n^t3t|#X_0yILaj5<KpdE4d<ow8Zs4=6!$gTKCI$fDI({(y.7j{V}s&NjisLM}P12[JF.I$>%ZE>Zrlj(kx~y5iXP~)2hfsYCzX(ApL=gcaC$U4.*;+AP*ux7b.k~z[|suDU0Fx<f(xUOQVI@gv785i6"?w4}I5%O~bPWn]4|XuS/Iw:~_Vyd@rbD!WW!o={(Q1BYj@`OvBsla)B&FiH(>wTI1{Bn5Ht*[}F.|>AQfLr9{O,(r8BDLS)_v[XQDvY:w81<tGw`bpS`v[t?5?&uvnLfG0Gwk[Qx~B_b|:6/,pZHN4`4Ps#_ZY)4lK?<zS3(/SWhjJ$9^K0Aw3%u{X}l(t?eGsUgD+ro/Cb{M"Q[~3KCjBXV5xIdga``xrVPz(UF[xe2+|2/tcsnOWnkGKI4!K&Lct?azoOOM"~C1{Gj`bBc`k3km&HpN#thZi{KZ])|2MitsrLzH:2~eS7:K+B%GjdR^3T9_[4u?Iu:P#vGMcB3r68FUU9vz}|B3,&$=#hf6MQ1hYmE./y3Gj|K0Y]7b%X=~yI&,?J7URB?1Vn)t[Wh#Dn])%d}C[v;TN0,F<y^|:07/Lb7X"s7lA3@4?hdH|;W/,dL&^K_XSA,@.D?+edUVe>>k$EJ.&+/wI}"UJ`za?a,LKO90?%MEcs:h&Tku:02WrWQn~FkE%z+&r3[[nLRTdM#PRI,82"?Cx.{BD`?y}|[H!k_HUz$>/O9x_K"zDHgzXR?=wAn%!xCGKV25/]C1)/E9.|.pq>q_=u3r9]+NKML;>T>|LBLOnX1:XW!N:JpY[ZI[VLK(3oDGT*2OfWv1U[RA5Gi6v~]Lu>.?%Clo=u:vB/Mx481wW!osNUOq#+F0!<]K|4M}ULRt}~,{(NOeP+p$oQP$$DgE_[hG*=Wk5k{Z"w";`CxLX)(|yM,<<ds_*+9rcZv?"sLRRpz>rcy5j$W]xKy|K6TJ[FF0`Z1g]6js,<lx@NzK5?(TX|^)xa$s0[SQ}~5&f({$#WU)/aF~jB`{my2Lu(+2s5iy/<*Nm_F+H(1M=T@zFD.C};v}y!3U878s@54T;LR7&:3|1C/rlC+>rbqP1h^vG&QHmd,g/tLcP?@RY"s8W"}lejV3!ig:l^RX(I.olFqU;VO;3oGU1fGj$Rf?7L8rVGk^IPW|2B{&]pwn_0.>{D#>eW:m|&6(nZJ*ZdfgKGsJ~9{uHPHe^G6C~r!cdyTkmUV*?O]>hiENa_5+MtIO`M8s$6.Q>m8EAY:+`rx{~V[)/A)hL^./J?rb6XOWUO2}J,>G6|v]pi<rY/Qp<6DC9TMvDE1J5LK@4}vRZ_li&N>AqtyX@}IKe]7bbJc_3X~#oR`UqArgi<2t]Mp1vV*VAGa8b=v@G)=H4~1xDzvn}|0.]&M;W8Cxu<,MXezI^MGY2NgUth7:e2C|B4Y]3)mNH**2MTD.vE9m>xntRhNPvQvGxc#~nIM}?F"F?JA`BU?G:nA"bE8$gfTND}!TKt|.;m2oAtPBNY%yQkt)%@(izLENmxnRu6FTAAO!H"zXcyTJHaE6s#!CDH~lrHW3TvclON;,/I|XNt.L7FQAAAAA4EEA153G_kG)Z2Qc<vH"=Pk"bVbncf}Pt_~Q~HIOP0#<~T5yQc&e+hkWf"4F|HWMPluA2YB%/ZaU)%=oAu4oW{!n5~(i<up|l7NS}|w@WX:DK(VhT$,(we6x&h46E0sTuF|FFScW)%uIRnv!>$U.O`(a(XJzZtpaz3U0H1+<Eg{*CSg3D)uh+szg7WiO^2}0#M:&<V7G{q;6>`Wcp/PO7%kHl^GG3PC#m.pQzNPvj^?#S:m!Lnr*;>s163E:XgpBxp&BHZa)7#FX|Xm+UJV@9uLiOoYQAL64;GN<Re2:zH.LsY1ZPGfNLw6XG^TW/xOL1.ca9/l`u<0{4/^/x5K5$cJ}ZX4p0drF2WPwea)[[c~kb8>9hU%dr/{K_Id|@,;aaXi%a|:Cc3=[,>Lll~`8DRc<3},HHrVw?bT;lllR_(ymLB[EPHWg]z`[BW<BE@=}zd=tfv<6QglWygK<vY4!j^w;~|#JbB^[S#NkeG"!67!li_Oe!@7~n+=xf[LzTyRw(vq&"ZDu^_xRA1xNdo|2a3}Bp%pb`F4:qC<Na8FmFHB`6}y*}o}cEiHpH;y!}F"rTOpJe|qq1?<WWQ4A%G;;WxJ6cF{Y__GdO*~GBC|J.k+a|fX@eG9O$n.[DR&xl6p%Ts_}Oew(wtI#j,Bq&.0Xa|6*KXqSw>dXt6hKzL2}QvrjJjklJ8pa.uB*=K^Y!#Pt%&23x`20p`b6T>s7W*&B0^`idk%JZ:HC4?z6!~kJ;pfIm@]weJ&1RG&:dNl(a[W`t=c0xh<(Z*tKdBtgQ*?b(}9WDc4&)Dw9]a3>4ye)w@N2D%|(Xy[G@q/*cDZnsZhlDpvr6R}]pgwi&uK]]KOLqnB+S(SUnBx~7O*B/md)YkZ!.i4XPqxhi>!<|B+bFc{GZ<UAYykDzeKZ<$>R*HV*nB]_bB$ASq>?L.kJ=F[hF[2]:{Uq[q%CCcKdB9`;]$wr9L|)P"{G^:#tD3v|F`5O}>5S<xOBH>9c*%4hpVr_h_(N;h{GC(0&2v%BJ`^]CYBcGG#s5l~nQ~F%j?mvG>`4K,Snhs[[OZl(XSq|}Yx+)EwueU9;aJ~HZ{A>(VP}!^zgn@a|!V!K!nw;E&Z6uZ2$4jI9rL"<i_j~u)Ec+1LvZ>X<(<!$Mw!qgDqF^$1.iK*g{$wDN!u1VTPW2N8C81|&*E&D[$^&BQ2xYoj6|E_kIa~CW.^v<aI}JYwn.<,k#y8kDowW37mvC^)aE9I4Tni6FGeEXHmv/45p_u]0f.u=~/z3x9wxK(4^qVn/O=KZ3yy43x70vy{uCICoLU661vYo[9eRLv<Win"^`kjP,d2NtqM!MK59+#RB?I9WY(xK;+2ke?>dnuuhFgE!Cj)0$u,bcz6Vh9mb!=C5X{rN&"T^d;1cwT`m_tK(0Tf|tv0qM0"l6{,/Rwq}v.]@8MAHk(mpKX#x7"]=]#N?gp3YlnymgaWe4g?|0#9B|F(C]!?L~q="tt9)1%9Mwt:`zqZ+FO)2|)t>pg9Q?s0%3E,yCPEtB2,81iLq+i!sfQzHFr$N<DaP@=xMBP=qY4nW>z>Ka@~G,[m}L!Vw/+?z{?5R"_wtUs`ply23l+|[M1t<<z{&fsj/Y]]WXY1=K5!}fLECZ`^2Q[gr"C_6ySogd]kdvzey*LAVy&uI#:Bc!J,A8eFEU8"TzkK^}6]z5+;Q.AVd*{qsLWC>X}>r*$np`F_[VbrbzR;R)Cjo?@~&[{T~{_jK;3xLEB*fB0_&D+1ZAZ1Hk^/dCZCONRv$kV]<wtPL7]^3$9s~j$jELYq?GCZozLA(FJH:]>?/`V4K:$5^azt`^}K"C|v>EOpf>!7Vd{v.(9Eo+53yFJyh^z(+C!I)8Kmh&$]o^pEm"M+uf;Z1i>h>:Ppp?@v.5ZOVzgXfn*Y/j&K=peFRXp,Ae4ZhVA$`2M>W/P&`W7CTV&j;l"o>3"9WWLo?n[T6ZD<.L_6i[)VW^DDU>}&R#[]`&^YpA#`~/od_J`PdotU{".O]!~d=3}ivDT!4>}1IopYsq$es]2R4bei2|Gmi3PAup{:i^#@[r)C_28(wb,WghfD&DE8Q1Ut!"AZ{Ay`=mvd(xMS:H_0b>2DC&H29V_3Q/6]lI>}1H%qB_[Ze%>}!j_]PO#I}MX0+W~,[xh[{T:<93W[za}2}l[;7liNIj&Hcdyo4Ov646%nFZ}a)Qt$^Kp=`C[09o4&ts=|`VGMDufM]=m4xG~"(^n/v.G$N4EW3J)b~jvR?cw]d8NYsB?*u[sQMYK~[/C6]~+UBW0mV)T0AF)|PoI2_0D7A]B2Yd&!<Ss"nJ1sE.g#rE0e`]00*!%"~ZeOj^PiaQ[3Q7rz+Sr)FhTD9LMFOYqcM)Bom"Gx^gZk$OzFvjH{yL;.twCyH7wIJ+qec~Lg?3qSn:s{gb<kxHEFf/e1x=9>rHY)WwzR|#*%Bz2s[n/_?O?Yv$q!,9b2W[f#|Qoz0Z+?[jsM~{~C%CfXBAl7`VzKRJ|IYfV0v?)bP#7ki/8!>6eLB{`MWOZeNRRq2!@]C2^TRh$9;`lI9yy7";g#_b1)f"F6_,//e`G~<)sKW/yA46YiZ@|x[KY&v5T:q&K?;*t0.A&)XM<Cq$gTY:EO|^L4iA51dVJ9oL(.uUyDWG8?Ag4gSfNO|`^^0CLxyLqwD0`iGM_z?,V??Xln%D<r@gTLCyV)Bz9x?`*7mXC4*a_7cD7qNXnCsm;6+RP5d[S1C@30&b$HI1Iq1@Ve456]3B/m&d2&&qPkBR9>XT~5q;>(A/Di*6+A5=gk=?Up_pUE[Awd6NqzUs/k?)V0Xs>$["X!x(<:2CD}h,|]ng#:s1Vb{^+l!gDh5`wmUrtfCNg^@t+t>Y{I*!;=N}cK{zv=]ir}}+6{s>agwxV#.TDv^yn+%X*LWu~_EnPrt{_f%T|3M1HYPQOWyG[>h8(+8_!*Vv}Y[[;Mj?=lXxx1hlHcp2a$@*h|nfQS;k(az".(=}l@Yeio,h^E?DU``YM*}_jQGx&<k4&>b4EH>+~%%w7;IPQ]>L7@qoh&Z0V]Q>od_Vm)o*06$<_|Oc{H~Q!?Xr`*<7Mi%NTA0].`9cW/`U}Q9>t?3y7GZTN@`ea<k:K@sSd%0VNbtxlmy&rJEazr>c+Y]22ZI1J5:axyrTVDG=9)clpzf.lQf;ff_EL!_s(jRM!pg3LUR|Bn!~r{p7(2n>fmeuR<[B_,{m5xkjOhf(yh4JWC?uE8>wn41G4`0=z?p)ap~(e2=1yQLP&e}!*(VZ&TIUVA{ull!Mu|oGF~*iX&v2Us>8gT?}yu=~u*|*=?3oq(5VFaj?Gr=8^B"%jN&awQ&OZ8Z#Jh*IR~@&ps7/_/dQ{},a?AHooREMoHX&L%%%vm;1.D>9+ry+~a>Yz@=nGeThNB{~#AS7Mn~{ugxX}Rn+j~Z`H(WAh3Xrq()>!_ifD1hqZLa)d+.D3:ae!It%1*^1EyUG:dDAk5E~o03kF.#iXiV(2[YIUsVOv#srCHWw?l9:BDL"h/e,=7*5nW1rU6y4kn1``7)<@3|Hp"wd]"!vNGCPUCq,VL1>]9|G28Ncoi5`x%V+dhK0_Yeqe@%qN^6JZwh6<SUErYS65#/dY=w,FGjiqCg2_ZP%P=3W|B@{])IK$wGjM)Qr#"j_lgz6Y]cw8aY_U_9i0;nu)<0.<m?e^"rZJ)92dV.hib7Rw]RJB11Q8cM9EK*%i+jduPdiM/Znvcsk2U4$X+vR{=!y,w[WgI0pSg@gqA^}YSz,4VD+7v3rn+v:c<xD_={[Mc#9p_JI@xVk#c7`$fMo=P:K[Fapqasq~KtA=AqVL]d:,,~6%>|j1h{>%o}v+7*s738CY).O,Mlhqm$e6d}_)~KC4<?M"4v^+1;<4Xieq5_5k0LPX$Q"q~o:{f)3Kr`S&}LyP>D2WUS$&E!^i*/V8I!yTy{I[Gb5jc6,H;<i?XY&o/:d^LJ2rD&"3&G9KnhDP;~?+d5Hf`_]pL8Ce$`iq2Q"`#NwSIW5}M@*`)Ia!.!_WZYu^yKJ;Sro):5,awI[G%UccS+Km9Qns"`4DOB@y*/pe(M_<EsKWC3M4PZMD.|p[)K`ek|q*+k?J(9yq_?kW80,iMv*onId^@WV8&apMeU@edcikZ/^$fFNJ3z5{U{%]syMj>50ycU$b8IpFIElJ]#I6U`F.44REfi(=JhSY+N(SzLCl&YO@TI8{:QWK*M3vw2_|Ody<{BF"6ty6l#<h?QY;M;W)nc8Cwoy@AT;?T*S]8&6iES?4&t{L{y^^0U,$G+`G~<^Y&k8`za?sq26zwJ^X&Z3ZC)ev2b~`y4NImF~mi#y}cxfl*qIs7pl=O>,a_Ptg|Yn*ir+S]dZVVT$X;(c;R>1@f[Wc1:594;CZa|ifmKW/+~&SB9cr99DF41/j<Po"MHa..(j9ezS|GILN)m;8+U6yxZYG@T5wc,FptZq)GR}F`ZnBMy68$Bw`&gs}]CprHnuz6|<<2:mT;?zB(%r2eaMI|aag(JcF|m>3(:Mee?&BN`6@q%<d{Omx7dLo<PB:u{Avk$%FqT{H?ZEUY@juQe!<XX/V"]/FL*._I9Fkk5Vd%xt6r]:oz]`k{*v]+XgSgm~ozUY|,i;w2^2Zc`4v^t[R/n+L2?@Rg$cu.1Wj@~0H>y8$R`>e55y`00JS<SDWn;b]BcRdpjP(Fxy3}wm,HT?wp+P2%4wrk*k6u)u5>*ViQ|&qPJvfq}!"PT/0^7pbV2%f}$d]m=PP$*sJxtGv(y+=Qi(7&!pU/w|!`t,}D.GHu1x{iK:%"n)44l_iLF~qB<N(3y%*oVdfakB@uXF^;]GVeC3Y1i(L"A4qzf}n$UIXX_o_~B]K"U8vR2KOI7!^O}i?ecj~*E,`jwz>>xN$$6mI1bw&P_MypUiIE6vUQSskNqYV?nyx2c&^!.]UkRx#1_?I{Qc^3dyFZeW9t8Zz&id}Eno,uxr(wO?4[nVXzNbdh^3<~ILHLP(Jo?w]j#+n3<b8<(*OpVJ=uJ~u7!:0VJsWF]?iNfji/&;,MFr)Lt#S|`1?k._HUOKCrxRr{!+P}#3wVFdhDfVEGxikpR9t>*t5chC0jcQOIY8lPGOF;7GBuL,7D>Jv,0okHV*FB#HpR5^*=J<tXtoZ|"Y{L4j*r#:K}Q=2${7;RR)3@Fn`Ien/]v/LF[{mUuWC#@!Dc_x,&{5Y7(mS]{quoq9.:v|O;$Usg,i{:nEJD~6b(*=ed?Ap9`;^z?.O*%j^JUE.>T0qd}IMdZ1hnWNlQ13RyQ^~`cvnwU9bEUKxa}N8NR;P3lo<qCXx53NDX*{V5+v(Fu(9/1[iLWtjmR0t<eh9dCKL4,HF?g"ldL3p64DLPsM=KkP/artV"P5o}B5h4YeXKp+N~<cM>!^c7vsSvxR6{#Zuxg>1EqXxrxeBwKY+U|hHU3GHk*dnSBc:+m@2LLV+%vlK(cO_o.c<eZ+&Ubjv`nULAh%%htx6Nz<#;p!#{<T/Zfr;IUjcXYX{B9N2U&+6l]eGpUd{j?2#uSeH}">kjX[4b#HC6|s(wxhbNsFBGgt8j9[NNA~O/5Mq7C/JxM?%{[$N75C3V$u1XOO^DYJON~t:9s4>f16u@kkGo/>NDr!N3/oD3#ysCKpQ}UfZ?#/c%URO^QOhve58.DtN_1W`)!UyiF`;KvzlLYr+wGN:^%Sq)RoyF}[(rka~3J6_HDwFJHtkZfn2N<8.c9#tW|(MZAL?OSR^AT%nAMai/"nF_g!1p,F6sz4Gx*aW2,74EhE{x!rk5FX`ttIWCh.ysLwU#U/1$aj<m47?4vy|MC/uw9T*2ia,5i4F/MlK&WRGTB,vz8Np)@$TB.a<koG2J/(:?k:ZmF&EUDOvTL+#_iX?zXRnX$4VEW$KjYL}8?h[9{B?&Cocc5G>fu*.42IqCDkLf8p2A42xFoVhEj61(o/*GjX)t"n9v9VDSau5qH!JTG026"]vW]!F#`t7Q&guI[Q0tZg{raGjrOXUA:![uIH#JjM}aCMLR1[VwS?I]dM[DN">g~D[Gm+<9!(Nvvk#G4m|qSik^IG1*aD7t@(V&6=?tDnzQWx)=pOW~$nzwnGJ~F7?8/*PZ(~z5Q2>/0PsJ_2s+i[](e[uoo1tFCN_J%W{8HdfQ[D8M53![8O^>8{R~)T%=.StP6fUS(wo>!c0L$}Rg9<le"nw%:eSEf3`#f7i,0ic(Ct0r,lr%+&}Dw5B!9US:gKa6+[&nYH6=T)63Oy9O@)4rRBUXy}4o[xg+Qo_G*y?_^_%:({osN/pSvQa~b_]`/Cr1sV/`F0/}o9,~Q@nC~[rE5lno%0+ucsl1KVk(aRx7{3,}JFK&4`PaOys~%t1m}B;`EGrTj[&seS&,2(*3gtoLI6V<"NU_oeaFGX=fsCatH][uJOq,T2A6Oa%M,Dy:NFLS=CFfDV"mFT{4Y=.mkAcfJtO#}$[o_)uMMfYu5E(7+sQ~I!XaEm$XmLIj,4Njm>07Q<K|)_d7Dpg+ZJ9R<("nmr=gGq/Cg)k%J|e:/qrr|H.>hlq&/(Bt0RT{lrhKmGwUK]WQ#t];Q@{B:tPyi|dB~?;vH.}Nv#mC7|A>,@9MB7spq^sb2mS_X<q(}("S)>cJC@,i}pi`J&]K>;PKy"+VU~;el2h,i/`S{>|YAVC"cRrP:dyZnrVw(#>&S)u_=`>Nit7k0_.ZeT*)S[|f&,YLiI&O"C3E$]F@Myv]h:&;+I+V<usB:CbiUG(7nb,clM:P{R?!RW{>G~5d&PNF?rtBh,s1<W]Kd/2nLpx9Iv1/$6guFD()].>;f4[!hL<ZKd^JZ[I10Kl=M:K*o^eBeI>/0)F2ulk0}m;3<mDh.:D(TB+nVTF_J1|^6!)<<<6LCnE.To<UQjqk^V`x[hD**rdq=k/M$@=^qfT#x}@zPU.[OVm0x[/}m`RM*3|Df/1?*$`!nTegGzm.J}"UWn`P~)1u4^n}Wetdt:_Nu{)td9Lfe:J02d(*SL>%u/s+.uqHlj.D@!_mkq:7@1XLLk&%`[2~&Z5{lOp<K#(j4F|0DO.f^~Rl$Dp486&3tj4a3%LRcfH.LBB(Fz2$xy,/[T;gx?#jEc~WfHTrG2G]A@k&ld}|g4)OziUM)gtde#r>}gV^PIvQOI"MgXE:oZNI~6_he%OoUn=QD2j;GZ|@wnP>%xS&,+M}4fzq)t+`tD&<&FsqEZ[?JX`yU<[;gyHCpaZh~2L~fe4&T?r6q3<0r,q!3_aD}ZSz}d|Ww0ZIvL9bL5ko?G#y:%OG@z%HrjI?%ZkMj@edTNGaKTW$YR_f/#dA0)EGU`$j)8&FMf#89YK5"g$wZbIRfS|WO93P{MqZ[Zx1fjwO]+_UIR1]J5{&Uo4O5X`hsjH;aV"UZ3pLhb#4WHGQ;1mqiEM;5@`tic%XUI9TP{#[5(nN#4lZU_)iEZ@xhx<w.tdwQQLaXEPW?8fxYDg+RBZI=y]@cv3,gN..M*mwV??~On:W%sMS}c3rhZnn}Bz|Pll[$s^_<EqcID4{sn4qL@%hk_bp8@HK&z`|YXj=k;Q@NIMF4wym~`I:^p/QtQZzEXBG$mm&y;lSWO`:h*zM8>aBxHddtw%<"8Jc_9Y=ynhFC!*l@]>Kqf=Fo,vnVD[w7F=P@g[2nj)nrRU:|1H|ai$,oC!L,rTv_3NF6q1vd0mFil{+M2um4C1_i7"(#M])}vTO{GFL<At;X(neFli?vfs[][q%?4:n[z{5Nca%Z%*o%E*jcThB0HF|q1>jo~2,F(OLu)]nT8;[ne{Vutvbi;1,P.Qg#@#Gzhy`+h,,)ZD.]{qiT?(H&Jg9Nj,vseM|<o>.Z9]1VE}5$2&CiUM%I7KMUHFQ7`OkgZc+KlZrYXt=R5@$LowJ?ka7UK8gN3ibAJpA*Z2%HZ!muQB`UN>wYi^,l<%=*EZTZn$0MVgS7lyr,;ZTkzpIiU^,"2+mw5=QZ5]~gkl$^fPQk=<5"bD~2{1iDr5~N_u/vb_kL89m1Gy_ylplg:o]Q_K1=s>1%Ga_;,F>&e~SrS^T1:exE1:V<RuY}KK:^5)c$^j{E6/[{X)b.8.=vZ?9w>h21$q<yTSW>{X{q&#J!&D#J0&c1:|!06_zb0iT?iE=/dTv@l4FV1ZDH57_Ct48C:6z`(P9i)B7B!A*[g@I@]F)z@&P5AGQAM9No36N[ms[fPr=w7<b*S@jq+vNqv>|(#]qceP4C>?xb.ve!mFlY9ucqLav@,]U@+&REC?c&5b|_6+,fj_ic&IM0/9}K%p^x(ozRB>;{fE,|CE0aN^RPb`#D0~P|)TW`?nO*OHy!%oShjVtJK].a"z<VL>42)ih=_kn]/N?FN_GZsll{!l;%w/oLxS>P[6[7~liT|R[E_FatM01"umlTL?32Ep[/C%"KTNq*VhaB[7r]$;BzBGJ`hnN0<sahiHI8@*W0{u7Q<x&/xyYLFRFv[&$|VLH%MVc#(2&v]Qd*b}s{!DG)l3f=>3a7>*{s"=UJ3JIK%DXpL~pBUTOpZF&1{{1o.}t,%2}2K:_v~rbhP@o1Je}z4)5X|iW*l!bE5C(fbI5I)_]:Y~z#tyX7C/W@K%/W0rtje?eA09v)c@r~6506&|C1ZO^)@UJBcE%=x9oH|qPL":qa%]l@9cLAWp]4?z)d>7pv:@m`HZX!*s2<_d:{~?%8M>Fa%lNVb,8|*ZBSIlQxvdEdT~vvq,9pm)?1?%{I8b,CRvfn`4v/gkfXnBi=HR@PmEtMt@B$fyi,M=Xo0&|IXPs6L]bQ|&UUB<@I5Xj8/y[=Pe?^arE%1$fj8WGSTn<@Sr4+Kcl=99O6,N$!Jxn{?w#.9pXtW86CpQtOkX,X$,TcRd!<9oX+.w~@nK}0%gVLm+#o+W[+ERb+v@^2m[&S`m1y0kgEHpBs0SNMQ|RsYRNP<}(Kj";|eN&d=E,IWp<Q#7II_2*YAO[#y%G{a9"pn{Gl99ox4o>YXIO4+u;?v;[K}&,%Y/]g)C7+{Gq5<TaBMz%8Y#89$v]9W=Q.>wd<_d!&~,SD4Z(SyzlaB6%]7{n*Bh/PG9A:[<TO[{M+1V7##h.Wbj91n"oaV*ZGjrz6cK&V=?2B4`xB)a/~I|WivVSFvm`_o7R2Y]vc7gQhQ&=L!!/oXgGyx:xi9.j_ogB#Y!b6xLy$@gx,F&PZdiofQ3Hwvt~1TD#WG(>{6=I{!E9OsbYEB623d},D?A3u$HMA>ovYmU[e,N&<!;j&oOF%0YVE8_^:Yf_]JwR=VB,&s.tTishKKO]R^Qhxr";;M077RF&jtd/&2!2g5zPgT^g[L7<g0gqq|<(A`M%<|./MY:VgOU{n3ra`d8DV{uJ$Vyn(KC"Y4N1dK1B;HtcpFirn;<cR&,zr#JF.ECao[b3kvVp+.NI7PArc[H>s.hS9ik]M{ssc}e`F:92ETxEjGO@0RglFU%oLBGgSImi_Ev$`n8j.i~uB?8V0h7|OEUaholx=Sh+r?w^hMRO00cKL3R{L&61`dBjojk?+[yGz[kX^qr%.92w>hldAoYma*JEsVqr)SpF"HDH+DXH!a0<5ukCcgM@tDp*f;p++o^izw8KyA*|eGH/5KtIx6e[^HS/3GP]{;Gv3LjJIiB.cohbchLk{T;F[[tR~GK@Lm)#0[M|<|`*s}*`@u}+4zB:O*{i,)6w)A&Nx@Kc"[xHMd8?XkE%4jFq1#T!nj^j9V>ZV1}FuaQO%[jt8e9]nWhxt?VlaR+/u?!gFK~4/3%4jywDq9$XPz!IoC"5Vi;aIKz)@]tfKa0&/_;}p:6B0cr&AY$wM5|Jr+Pv;Yv/(zg~cQ%naC|;:RzR/Mu*d53/qySEQ$;|M|2_wSZLOdRAMnJ,>>5d>Od!Y&=cnFO)VQZV.S?Dy<l]Yh6D1;J_yG36&E<52`tNz_$9CJ+rI&$u,[a&Qc5r5=gl|KS<q=Pv)J;k9@jtz6H+3<l$fz2S]2@0}ig17%PLOFi6lN"2ftojr$f`f/D8D%~^;h4t]PFT7gr_8q3r#FrHq{3vE_Az;~J=,a#sI2,wJ3/vmyd])5^OulfUZ8pZj[7uxy1O`Oz6~|F92&HeP2Tc~]4LSw.Zy0jY>m[:w1S&B|6EG<Y]+imcRpu@Isn[&M_#{EjH(#o("|%_PhzMkatQGC.jzcDcam3pDqv@LR!1Ljj6KVx?T/rx.J)k)xfUnTx/Euc3N:IHODu3G"44JD6MkbZh*<T|zA#UU0eYb^F[KM6g>Sdbf)`4)Qm5XMuO?T|80v+=7JSr7TTB5*@no)?$=*%L#n)PbW<2gh5&+As_J/uu*s|VHId?@#fq_!AP0{vL^62;yl!`9/"R})bm3)no^lm_Jhq49>sQ"l+jx!p&"J5||~*U]!@TPzIWSvyDMjt0s[Lx?au6&|f#DP9<Wf,e(YmG:_Qt[U}h}FX,B+)``jO"BS+VETLpaeQl^Jv7(0Liq+c0X(*wh[X0I+%qGo5c~),snsG0NvH}e2o&6kup"c}.|d~J4l%?:vO]$?axSH7G|KSPwlJE(dn0H{m8NZX8U|F*Q~Nk]a8I#"#`zbt?u3AeO(AMdg3Z^EMZV45k.Um>qyAc=DY#6lI]xt)mmuu*A(H?A=:)~+hIjIu/l$y[O$]cQ6K.4?66<!_l}Mhq=.5`VHthi`WkT]RR41#I7(<BuLf_:W}c?Y..Ku[#)tLMHo4yuw21L+h>v(s~X@`1||]6[R]G`~HL2(.B|NJ=EdM|PV%Bxb>E899ry4M}|+$c%NV1YYgaYSY8]$HA#65_5$UBBd/u3scT/OHx^u@Nx?EZ,[Z,P8?C(mM60$!EvZn|z`&jZDwT)NelECt:!w|[8Vb+w@pNp&HXYTnzT.E7o_E[(AsHp|i7ZaG?QyRIHB9,M@b`PP{C<,vtVi9EVX0A7%.!`tcXM"|xZWiBA+)l%{@*jfiMb$?+(^E.;T9oSqH3y./7q]%X{]h6&_e(c2VzwbX<j^OhXL8/WU2.k`pY{!IawQO.l4EvuXprG_7;O=L_BNj9*zqFO1`UXLL_O2Jk;t2K`VX{g.Frqe8@gJ:RSLYx@fQn)Ew#gazoyjrQ"YtqM]2<OHq*D1;y9o3QD9&"2K?eT0b{q/mrd?L^ru_&cCFEftC&CZ%FB<(<;LCTV!=<l)L<>4sS*v2uXv&G0YV72Vt]@0lYc3ENE%:A^^p+y2i8l;L~)>2O@+4j{tW(bh$/9j%ROoRd<t13"/lDdUu:|fXzFBQ.<y;26.CL{KkjgD)>a[QZ2&++^OlGyzG_u<[ExD0XWzBV6QNa/~L|!hW_`xPt6J"^_?~gS<TRfFnqK3X=n>*$62%s:u*wNwUVleee"S*0g):QIW}RUzVx<~<oup)M9V3Ma!kavh$UKM8{3c}v7Fo6q`bJ~i[Y2u2EC3u_+"0=qs)]ncjP_<4L:Fwq5FM7J}Jd?gC][jaLa5;b5P0eI!E&$/Gi*x::vqUcpttr(9p;",`0EB91)7}oA5rV_9mlft`M%Gt+$kXDD|_;404|2E8_2m0(U6VugLsa(s{JmrjZe(OvrUZRS]uSnC|*LAId*G7H7e=D!Z#m.<fI0&OJ%O8xB$5!;7kqpvE|"Otd%ndwU7d_6q/SL^gM?rI1^gFx31goRWw{$$]leW,KYKHeNt[qk3O|L^ASv7Q(7nCz0jYBc,jtz|1vpsMkIZBHu5&*Db#6F8Zm^P@@IKeK*GPx=.MQvqoFsa$ygw}Hw!c4hL6(QYAlLjgNhac(0>P|>639W1aj+MYm+?8nZtBFhKg7pO|4[vz:4,JJ8Mi+>7?yoe:EhVuj:Nou:=rGmiLz:9.0Se}BS*:SQ34.=dG5f:CrLI57aQ]/O}[%7.!??Jl9/(=d#Y,L01PDX[+prDLu8{5""L3G0E;$F(,XkP"u<sLD7wQ2lS0Z3vO&4?O+Z_|4DPAd)Q.h$%jX~^w_|0onNL{LQ5{4W=8{9#>db%iwQQExULCDM#L$L/RwSW/g_ZUwDwv`Sgu:bu2@z(oKR.9Z:ydRm+V~4"OV=uPhw(%z$2Ep"cky)oYWNbxz/b"UhU44QD%#b9b!/rjJ}szl=`HBf_dfL6!P!:G!|.f.kXw@"9}}4!NVO@XP|#Q!_}32%iKor::$m:;?^{F:FiD`b)Wg}EmfY7mT?SL:lbb`j|<H#FzJDL+6YfTGiXW^q#k`((OCoZ^,?1$#eGM>b6*g)i;%xWO+[bC!=Ef?Fih[1ZQFUTi6sQbMc3k);>{4qkAfC}gSYQD{lOxn5~T_u2u[T8(e0&SQY=K$|BXFWqEQhp/>OB>)fSdzuqd}4]fdeq}(Py$)3]un[Mfb"E6MagNf`/li8d~,e~i$$ca]LO>$*z&&dqe!949?;%xBpYzczIXrrn2O/>,N*rX.@FS{^@cp*$TNP{vpNFHG?F<Q+002p_l6Ye{zU]RsU;ud(Cv4ldgiVq4!p@}V.R#t4!V(I6sg=1j+&[%Pt#:7(~H`9luGWBh9<Ge(>U.O`Wc7w|vnrnq.wBx~hz^{v_8[:?1O;tJ^.QE^I(AG`@h.n:cckz4rz%J2_luP}~0a>Ufe;n)q2MbTB6Oh6YWqP51{`"V]t)}<WHQ8/87k+IMqO3X)%o{K/9}D$=):Z`8cI2+32?=4I$HhwT<(D5e&]h~NX4[9qKYSKG**}([74zZ1rjj+NMaPb_+UiW/G@0Gj6pIb$bdEiYWRW5`Rj.>Y7U>cu}0*~Qa*9~>m!U:1"I5Zv<_I*#*|R?r5U.Qxl4Q3H:l&]d%?x[[O7)0S[+x{TE[.j``y%[+5H19)bo+vobQ*EZT5{]c8gBZ1o@PQmaxY%MrLMPAER9N&ru3K:Ie9RcLm:xdsM50zgiy:KtrX31^(wqBrpcV7v.8%*7)D(NgjMF;UxkGK~<w00=F!o%}QH4wgxPQh/2DkCI*cHD>2@zAR;>@u63oJ6AhO/CwkSc/#e4Tg"X]lDvrF1NXm+xY*{kuHY2hynXwebos?b9M7(bu?,+cF?vL:BQPlcHCoM$9!|59{sdGULPwj|OS0"`W%jDY&qa0K"qIplOJ/"mW|J@S+TjBgkdWJN%{Tqo5=_zG?IdN7@xYDO:Hj)2>J(F*7y&+yG%:#!|(="QBlv.1OW,7rw02L|Zw0;$jTdK$R1z/4]+sE1yo5@8q3ry+!BZOI=A2AU><4Z5=#6u*$i;_s<7!E`Aw5e:`s^5{4uey7:I;FjCL~B_zn$lB5dYw#@o|kq$9:82}pDgH~G%Po[CkrSkH2C`zp&Iys1xpM{4!LDj!6xG[d=LsaYHkA:4!F591j9x)uYY[|n;/#m,.+!1uUa8o{0&gO7DZ!|`o$wB[lb9.xeIfe=kwQO5fQ.=Cr6q&)(%QUZv|aNEm8Gani_"176Ra)rOAhzuq1`<pQ"zuZ&k!;!%^:7j5s8fxMf[Fkhvc*xihDqj5oRt_=$G|sc`W4Z;|.*7zOR(v7JiP2e"bm4+N_`N<DFw}/a(kU7F/(&$eQGO.nYl,A6|6eDay)QU1|}[37g/r?w"v7d&<%((wnC8KH07<lKvdKtBov"`qC/&cEpcE6?FOb(W=rzt[4#X!oDa,fzFR?$9f$C+,CeV&{xY7?)wmEB)p+D5"hwFfFky_6v}@jJt/`WJW!^m5wKD_XHwJ1"a7bBoY{a]oZ^#C{)a~Au=4]Y|N>nD*PoS/BeBbP{#i81cU1Dkt[}C$n|^EKo5cE5N7aj$kuWhSq=DbwD8&;__qmjZaE]]1$r,!CH9]mCRdv^bQ_vzelIoSfowzHyGKM~DKG=~.cGur{CF(+FqK.;/p.s5U+.]yfR`991u)]#]B!@Yiu!7lz*XRuR51MD^M(DgcV&m5oo4lD0n<;SL9jX|aKfRYwG{Y=G$/uXIAM[p/J8zDC3#@_Cu(b~*zcY94^rjEb6aU/1&ad!FUR$BqM+ftFfB`7[n,*~(K,=Q>ej2Ky?2`?Yh<@bnb,JwpxQuT6<37y/Nwzen$k$:@a{uk/(N;%y8y;5IShx}IfNFi(ZTp29FsoB,zE!tG&Td@D4%|+})EoS{&DWVk}SROH`0<a_2/v?}xpVn:$=[BM)z$Q<(kW:w<(3uDEgLmh^;:Ey[)`xvH8[Nu_!%VMdLSZJ&FmhT%<zt3]y7N]Z]Jp?Xs0@%%*$6#{26&}yx|6?BSGNqH$?yrP~>s)U/*A(a$mRXQofZ;#gb0A>yePabKAMK42/j]FX7G?(RmP,lYDGe!:OY6UZ?kG:Ow)QRbgh1V:dSsu!=WS2WZ)R1dFPvR<iX#g,!icN[K>E=6Ba/Ti_U*,}L[){YwoF<R$D(RoFh@R}1yumz*=4z22EmL49<8$x7)vi9#".tEJU6cCQUL*MR;bjb@,s~7A/,Vv{w{Bo2n*y>S?E.[=Yt<</80[i3@Q6zEOEH1}aBw#a$O;#f[MtX<=Uq)`%E6)]/=Gg8[~n4mh^e9f|Y.N7v21lV<$8wpgz+|&nWJ].mSW?<Qf,5b&,K^C#piYFRuM/]$t(T]938{9;Plj0/1Mtp;WL{r4cXpu)mdv]qi;6{`7^c`Mtt7LzW(SE6_E<j]`$xfbkdQ!=mGp>Mq+k/a<w`/IdO;y,AeViRl`q{DK||1.$J7rjvAtXp&S$8{!a_^TyZn]x8Bt50Vpm@"t<$)6`jye:ptbKFldR7KZ"s:ZDw,e#t/m{7}([5,YYd5!zQb`k$NOJvA`S>ETnYIGn;Ux)*JqbIrtH[rW/8FUp0#Xl&=UT_fN4#;+cFqjgbg[cYLaQv"3yqBNT+/gtUo!^d?/}><vmg&TztLFimRIN4((!l;IzT"cnD{msxKL*m%uk,Z<WE&7B4axzYY0UZfJ3jrwl+"|]6Q:wSZqHvU]7^G%3XT5hRz!ph+O[:Y,Q<91=s&[,>h2$(z<4^x(ENr0Nb,#QAkJ5NIb{P7Ny!8a8}J4pn@yv!0!5%>Cz?#.V40b#9<B0SBU&WCi~0z#znJu9ESzNE11SVd@tbmlZzIpzpj_g:pKq,x}pHjyz`(ff7mpI3mX+}%&vPh>g.}s.W=RfStLC(*=}RJ?6S4%cm25$h?xz&?zg;7kxjv_[%J5~HAC<a$wbG}I(B^95r&8pn^6KgAcawLlv6CN"39_DFgotyaXMfOW5Hd?O|9gCp|g1Uw/<oUq@y$4K`_!ltM:#n*/bu:uc66HG(Di9FR~l}~8*PkBktgv$s;55xzmgczFpw~NQzt0;M^1F#PSEQYkX*3*e3<Zb305ZiN+6OKSh!9QJtq@[2}Gi#+bDF!L4CX;:!%sBD@b2_NI?)dN{S/,((UihNH<XDa[0XEVwqQtNoW)@kmY_eLJoR`~BgV$o"4&]WFwjjoXngDm<GsU.<pPRyoc[1XX$l$0;<pHX7{s*cJAG=uP4OsZp@(;V#q_`Mr)~Vk%1U8*5X!ZEG7quo#?9X~Y3y>5Mx9kjb}{{p[k*~cPd<1$Pb$@#+h`k)]/LF+9QW#tDi<G/H9ybTs[%q/T<<FEg8q%8W<[2MFoM+683_SY6OBKXqS&9P)XxNT4#/"2;x]1%6V&U2[Qsa0r1(56HjC|ytDt*+VuO{D!&(y#6UV{b?0L?P;uK8x+{Q`_4|k10F^M;z.AbY6V#NB{(WrL|=a4~=aQ!5.6qu#l}?D&("/oKa[b0GnaN?9EX5|S?N`P%Z;kYQO=+6ab,6{9{3bK$eIukDP9&n%].fjLZ;8uDz8M^kOPQ&r%#.;Kp7qb)iKWK%j=x4!ud:qJker(/9sRvGQ[1s]}J1_bBB7LHJj&dl$aAg**A`6^"Z~R}yh19N[MY!%0FJv^Q2;0<Vk*!i>):DhT[$5uh01}l~nj]0@k$|{nv@L}Yq2o_scy:WX$d?/51@d}U.`$qZN7MAag,Mk4_L@,R4[|*JLViP+Fk)sf9),GEP65IT"t*;i*UTJOX0ebyGU(Go2A8PM2~+F%1Ek=1~)9`HcJDLE<<p~N?Tz%C9;c!BUu6(i7c~ZjowR9fY`/q.k"~Xh4Kj$OabD!VHzh{04z`sI_HNh@E,P?Tryu8JCFCGYSEjZP5Xi^1kShL@2D;suX7YgNxP{yn@^>e)xU;/Gew*%~6#*r,kx"vIiiU$Adla+IBF9T6{O5"`!oVB)x;z^bpf;?](*!S7r}KzA]jW;uERZw%r9f0MZd,NS@!5jUG)CdMx!|DA!BF:}wZP9Vznul.bv_EKkvwsBBlWQ*h__o{$3>4JpaChF`DyG]g5K5}@#qXx[bK3gYs8=e//9gz.4|QQLnhSpjg|wC^J"v8x/30JgCwv3A9M_K<]HXO?o,b#hBIY!I#zyz[,*^Y6_!|&b9zzl.%*2#&Pp.&9NQ$rBF{!5G_5X#(4p6.id+`yyPS$=N?c18KB+,6kk"dK,B&:EXxgzw}u}>ZmKUZLz^73YCuE/:;A`,.5k=9=SE;HL@7!$<>p^*][=CHHYfjm?}b&|{s^ST:pT]Dg@eg5z/#*jp2o4gw[lhbcLMpq>pju?EDx=N>;wgqo)0Qw#Qv&YmGOVRgJ}r$kNUv<[J|Cr]~Ce("fR=*ong+"TpS1g0MvuIXm~[$TBBu!bczO#C%4m^)~*+#UtJsu#$SBKB}b}jRH6/M?^4V|@$|GZn.R}]gjM>tTF$s~Y%}6E7lekNSf5l~8R|c`[f%sH!tvo^Ugh?;p``:9}~K!jJ=6QUU5Z]>W0B6^;jFw8RN1gcaAIe~$)/4CzEnf2Pv5%gt=b2}hoFEQ~Wr$g0{}N`qWwB"hnQZ0PEk1uM@_Riq?kvp/KM4aMN<q`R]2TM!%tv$?%w^_MNr4wD)|xR)L<;8iNiP;ExyL3hc>4Ri#,LT&a3ZCAI>`~ZynmMyP8x5c)^LIEgI*43HvyzzLE,T4r728d|O8m.k,I?/:|j)PEl>%rQ([:PlSeR%0u5Yh3O<;V6`)MB,m7/5awRJl%jIqZ89y0:N7hOd:1`!bC6&vfJPF{Bx5u6eOt3,?F|$4*IPKwtY1t~5uWnqx#&UxYeb,lv3FGTX6RZ]30vd>G+PVRQvd]Ug:E~fgg_r5oN|u3?`GL!~`Drh1y_Lrr9fXRD+:<tNHD;h1=r<!B|V+ZmsNA7@+GZ(G$_6>fg_CIY(<IN=M(/.I["N1ZD<6dc|#6yDU:}5Ambh%pE@)0uHi+811=R1lWQGU7ei1`&^~x")T9ePF~^>?(h`?}m<72iu91NkU{V1g%36EJp8f+ITt<`U3W)2Z7nlk]wLE4:LBq2Xk.P6}^{es,mfVwwuw~3FiQ<(P?%duI{m%MTWKHIsJOhayFFWjsC|X}pq0).N+JmqXg.!!5)ZN}93ryJk_@,KJ)r;&z*GHcTiOxIuIUQ55ga8H`1u9eaKT.:$viig{d+w:Ai,3MP1Iar3Zs+lyFG<w&|$j?~Ys:IU`bJ~@!~J0RkezkvohiC{Bo3h(O*#+!mUL&5fxg#0p$+V]PUUhlmFcAQf0dSw{vHli`dDK98Mk,a?]XW[vlHH6Lb3#JJxb_K:v8pXII}$J="^8u!Kg5h8?v_G0.=|n*d$dMzO8+k@%c#WV6wIz@X1500c?^C6CF>=;*IA%Xe{`6~VOKl;W?o&o6KoE2)]b,FNByu+EOV.!;UD&4QliPr<k54cLYYtS^B8?vlw$04cB7@nGLB8Rr&,Zit=tcV777<A!FR.o+_X2?E6Fc`_|L#=P&U|sO&hBFSCkcUNpFp@EFJz@/jdF4Z1<en45oO=%:N<%aZ){W;mwvV|49Rf?(N5h5DeBbVyD9SyW[E`I/)|ay=Z4&9N/q~=`Ua)f!z$=8?$("x^^@8zgJb}wW,OcX=7wxH!D@AGi0S47PHM8/boR@"iIE_*eW}1c&uCRjz_jYkuW&Z+{lq7]fNMCkle#yhz9@VQDMdZYRkfiDXe2.FirL1O^N53`l&@v|v@69qzMy&TeE*"[Kw$(,2J@S$Mk{Kf9e]w$PsjNIG*@2T$<B[QSm3qf]4bz=zQ;*L1,/<)?}?OlbI.tW16%[Kx8$oFpgSIb:>l5OA(D+)fDhpTO)P(Z%z7>B^UnMb(FLPJ9pSJPS%qnix%N2WS06Y?hYO0{1)R`W)A9Urt=>5!ndVkS("+SacoA!esmuTM:w4WYnUjmvjNCXd=UmcOL?S@TOj72I6bv"vpr`m$Y~:%C/h$j~S,PPRH0Lrj7*S6+8=lOmo}F!rTE)%J15Nk$y{x~EO.ch/.%*"#ZA~gN2}e;*_kD?]k#GDFK:;LGpd!}HDa[#S:9g+gSLM%P[tG&:@RUdivc{ilBEer&>/@c)G!(qX9GZ!wxkyFOH..gsTjCMmUcDvif.:Vf0}5%boc1AnO*b]KU/{@7V1vt[Wc;ybv/iVlSaNH|Q2X},mb(Y3lie//k<ujomYXt@CSCiU7DcK}aKrTWiQ_3eOVBw%>y7z2G=;w{|aqn*Y05_7`SBcoO)Bfl`9@gJ!Py4O8r9GLWH]EG|b`uOL&ZoBXbk4&T3H1^#SvfYFUoA=.@n3onIRcutt(8R[a&fm:mcja<,>s.kU!#d/[`=TJ4v2+R,!W>TCVT~RbY|=G2.riFOW{K3=2xe>MQPBnr[X#H&0A*GyF|$Z(C=VeMIc)vtRZB|GtXwtgXCv)Lvty714VM4:+*3yGFT;Tc~W3+Q*{Q"(v1stg|:h7rg$Z}@<<ya=CtK3wp6ktc,_e@=ViTSjl,I>~Imm)Y+H};21z_[`m%($w@r/"qN>9fh=ohJhx$;X!],r_BRopV%g]VgoUJ7&x$.)AAO>de:EMip%reJwhBo(JIl!3uGmMMSOCT%P/)G<#jdC4ZwGmERj*Ss)}S+0l(5vw_~*.YNIb]>_0ESbeSvEA8unmU*l4(NbDr1twfpoJ8[jO@!uNQf?,9:BIN|X)1XP_QcD}c|$[IA9&kTtt%X6VFVM(d<Z5*g*}55`Yv4@ExF(<q1<S;?%;f){g6D%w]uZHi(:V1eaYc91g/=J)nnuI"e&+[lBQpsPLi0Q/d(XwmjH08mK%D/aTsKUWeft~Bd4#N%WzH=L"B9/5"wI&=X9pB4]a4OC1U@//F6qVOu532tc]tI_uB/T"R$SBR,e}A"YvXyVQcw3AvnL{.FwD:[!"Cz*947F!ub!rBS?kc:hhlv8Lvz=k6<v&m~occ4mK{F)F$}A+0pxTb6WBIOu.0jvQB=/un9nqOw5xLKi![XR%#~4GtZoj4zKf&;ljX}|Ql@HC(VB5!DD<:)>u?/o&J#r&WsJ#T#icG2_6;39?W@c]<ywtG2=tYA@S;D+Z~`0Q2hD9ii!KRfspHDg%1LZz@_Ir0b=7FsB2<OeDK98O1~{{Zwnc*I5rNj>C6pf=~i.O9c_)=GnRuV69umCib"FdbdRn|d7,y#l~~[i*(/X1J51{R!=lcs8(.2n3{>f`+EDlY)t83dIiVczvwe(S/xen9@jrn~8teTI8QU)S4KaYl>o~{qL$:5NrVin{W=x:Vln*~+v,4YodG/b|0>tDN.^XAOUh<gS%Rd8ZADZnfSVgcM(|upL8>BwF:sTtZz*uM7F#uMS&MRiie:>?7ux)0ygYvq1E$2nBd`#%_q*%<ZA>0N*?a"W=UGu@0D#IB?/yp8_qO:9K"f21HMOFaLSIiZhlC#MXa+*SDXp@hK:i{(Ti0g2@h+q9#jGnZ8i6F_Wn9y"]k2D$3kKY2^7f9y7{:6zK!F5)Du.?t$azb:Y70>M9bV2FE8VU@%c6X!RGGU$BUUN/#*6c.}[)2[j@;m,"tD9KQB.Kdh?NzD4^A0HI?[EE#lt{.N&#&U@kibMN[XckFh=C>Q5N/z(mAP@=|V:QLJyiOooJAKMMj#*4zaN[9{[3),2>1Fy{JAL8ZQ(3VBDcQ|@Bd{uXG+VD5;r=Qr17w61Rz%nSFE%}@K]~[*YZI5G$of[gd(;,59qfKZfTn:`4p=e@4Q?aXTKtip%7L2@]tx)CKd]}t.b@CteOVWc"@$Hz?g/jP6p5E}fM(Q23f,`?B5|z9Zs$8.ct_E$_Z:.4WEapTfaaCR]Goyg:Q`41!~}FE3y9kdJd+,>6P)s:W>~2yy[w=(s`VP@@|9]J@}kr(Zh^LcQ~@D:de.!IaDUnGDU_vyWiZW%=1ewo[kxhy?M=~Ie,h;Z<uAWsGi~#}_@hjIOMjwcUQSzqopQ7Z,I*<xRnxII2Sooq9tZ%q<aP1uW<5,}$L5gE"4Zy|r~*p|[DgM60RC~]N3y"[BK*spZ9(l`qGv&z(cQE[C:Mh6IZ&iGjHjWO@]QxSQZ[.LvAsK+Xz0!=iP1B#C]RAY%C2NzH`cB4gC.uZy|)1b6cHvxqZNGs_pCakSp5^*d:qTHIy(Nq)]XQPL5ubKuIw~t/@cyW23Kb}VO@WNKY;Atr12Gs(5b<+{~CG[Z)~<bKxq~1W{A![h{C~Z_vD!`XcA)Nd:5"q"!6Mf{HU~Y<qgPP1E#+ZP1m5`/|kVR2,0AA{ee_j|1>otV|BsWos,jKa%w.jHpb"@Q~avM)Ke~!Dr"9+XL@@Ll=vt`PopAP1l5zv3RYWecn~pNrIolYB1a;L|[roRLh:af|0cQDk@5C3*s>a#RgVAZ)K"Q2,MlC{%Q_j$qYqE"}X/M9:`,Ab~12O3DS.~Q<v2R")1Z(mt`StF03MAbT:$EAARV^?TiFE_fMUVupq!!RA_Vy@y8j5)z2sg6&({mK)g615q{~UO@(tz?}GfJGef`14D*az}E?ZJluZ1iud6p_hnx&d:G}[6w=Y?~x~xSD3G&vGYS&L<DRfD;^KG]/hMy`bfDc/$/s@ust3PefdV]D[$eoy@S@Cv;jxnG4{*E"`SG3P|ca8a]w>x4bB9CluGFMFGGqL|CrUmzNjN)lb+5HfOJF|5?m>g<LzTKyz2/BM0Q]G)LYktE37rg{x/?^@HvbG=8@I>+Y4SC#xD$hf_ozG&zBuJ%^<;PVCtuzfURlKij]VYpPzX%_|)EtSk0xXHNMF/+lws<14OH"U@>*.7Yc,yjVW;~WqoPag{zun3|{fTcpIhT(XVquA',ze,lr;function Ro(){return lr||(lr=Eo(Lo(Bo))),ze||(ze=Do({wasmBinary:lr,locateFile:void 0})),ze}function ko(){ze&&(ze=void 0);}function Go(p){return {path:p.path,data:`<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="${p.width}" height="${p.height}"></svg>`}}function Fo(p){return p.map(Go)}function Oo(p,c){let d={images:[],files:[],...c};[...d.files,...Fo(d.images)].forEach(s=>p.createFile(s.path,s.data));}var xo=class Ue{constructor(c){this._module=c;}static load(){return Ro().then(c=>new Ue(c))}static unload(){ko();}version(){return this._module.Graphviz.prototype.version()}layout(c,d="svg",s="dot",f){if(!c)return "";let m=new this._module.Graphviz(f?.yInvert?1:0,f?.nop?f?.nop:0),_="",h="";try{Oo(m,f);try{_=m.layout(c,d,s);}catch(M){h=M.message;}h=m.lastError()||h;}finally{this._module.destroy(m);}if(!_&&h)throw Ue.unload(),new Error(h);return _}acyclic(c,d=!1,s=!1){if(!c)return {acyclic:!1,num_rev:0,outFile:""};let f=new this._module.Graphviz,m=!1,_=0,h="",M="";try{try{m=f.acyclic(c,d,s),_=f.acyclic_num_rev,h=f.acyclic_outFile;}catch(I){M=I.message;}M=f.lastError()||M;}finally{this._module.destroy(f);}if(M)throw Ue.unload(),new Error(M);return {acyclic:m,num_rev:_,outFile:h}}tred(c,d=!1,s=!1){if(!c)return {out:"",err:""};let f=new this._module.Graphviz,m="",_="",h="";try{try{f.tred(c,d,s),m=f.tred_out,_=f.tred_err;}catch(M){h=M.message;}h=f.lastError()||h;}finally{this._module.destroy(f);}if(!m&&h)throw Ue.unload(),new Error(h);return {out:m,err:_}}unflatten(c,d=0,s=!1,f=0){if(!c)return "";let m=new this._module.Graphviz,_="",h="";try{try{_=m.unflatten(c,d,s,f);}catch(M){h=M.message;}h=m.lastError()||h;}finally{this._module.destroy(m);}if(!_&&h)throw Ue.unload(),new Error(h);return _}circo(c,d="svg",s){return this.layout(c,d,"circo",s)}dot(c,d="svg",s){return this.layout(c,d,"dot",s)}fdp(c,d="svg",s){return this.layout(c,d,"fdp",s)}sfdp(c,d="svg",s){return this.layout(c,d,"sfdp",s)}neato(c,d="svg",s){return this.layout(c,d,"neato",s)}osage(c,d="svg",s){return this.layout(c,d,"osage",s)}patchwork(c,d="svg",s){return this.layout(c,d,"patchwork",s)}twopi(c,d="svg",s){return this.layout(c,d,"twopi",s)}nop(c){return this.layout(c,"dot","nop")}nop2(c){return this.layout(c,"dot","nop2")}}; + + function shallowCopyObject(obj) { + return Object.assign({}, obj); + } + function roundTo2Decimals(x) { + return Math.round(x * 100.0) / 100.0; + } + + function convertToPathData(originalData, guideData) { + if (originalData.tag == 'polygon') { + var newData = shallowCopyObject(originalData); + newData.tag = 'path'; + var originalAttributes = originalData.attributes; + var newAttributes = shallowCopyObject(originalAttributes); + var newPointsString = originalAttributes.points; + if (guideData.tag == 'polygon') { + var bbox = originalData.bbox; + bbox.cx = bbox.x + bbox.width / 2; + bbox.cy = bbox.y + bbox.height / 2; + var pointsString = originalAttributes.points; + var pointStrings = pointsString.split(' '); + var normPoints = pointStrings.map(function (p) { + var xy = p.split(','); + return [xy[0] - bbox.cx, xy[1] - bbox.cy]; + }); + var x0 = normPoints[normPoints.length - 1][0]; + var y0 = normPoints[normPoints.length - 1][1]; + for (var i = 0; i < normPoints.length; i++, x0 = x1, y0 = y1) { + var x1 = normPoints[i][0]; + var y1 = normPoints[i][1]; + var dx = x1 - x0; + var dy = y1 - y0; + if (dy == 0) { + continue; + } else { + var x2 = x0 - y0 * dx / dy; + } + if (0 <= x2 && x2 < Infinity && (x0 <= x2 && x2 <= x1 || x1 <= x2 && x2 <= x0)) { + break; + } + } + var newPointStrings = [[bbox.cx + x2, bbox.cy + 0].join(',')]; + newPointStrings = newPointStrings.concat(pointStrings.slice(i)); + newPointStrings = newPointStrings.concat(pointStrings.slice(0, i)); + newPointsString = newPointStrings.join(' '); + } + newAttributes['d'] = 'M' + newPointsString + 'z'; + delete newAttributes.points; + newData.attributes = newAttributes; + } else /* if (originalData.tag == 'ellipse') */{ + var newData = shallowCopyObject(originalData); + newData.tag = 'path'; + var originalAttributes = originalData.attributes; + var newAttributes = shallowCopyObject(originalAttributes); + var cx = originalAttributes.cx; + var cy = originalAttributes.cy; + var rx = originalAttributes.rx; + var ry = originalAttributes.ry; + if (guideData.tag == 'polygon') { + var bbox = guideData.bbox; + bbox.cx = bbox.x + bbox.width / 2; + bbox.cy = bbox.y + bbox.height / 2; + var p = guideData.attributes.points.split(' ')[0].split(','); + var sx = p[0]; + var sy = p[1]; + var dx = sx - bbox.cx; + var dy = sy - bbox.cy; + var l = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); + var cosA = dx / l; + var sinA = -dy / l; + } else { + // if (guideData.tag == 'path') { + // FIXME: add support for getting start position from path + var cosA = 1; + var sinA = 0; + } + var x1 = rx * cosA; + var y1 = -ry * sinA; + var x2 = rx * -cosA; + var y2 = -ry * -sinA; + var dx = x2 - x1; + var dy = y2 - y1; + newAttributes['d'] = 'M ' + cx + ' ' + cy + ' m ' + x1 + ',' + y1 + ' a ' + rx + ',' + ry + ' 0 1,0 ' + dx + ',' + dy + ' a ' + rx + ',' + ry + ' 0 1,0 ' + -dx + ',' + -dy + 'z'; + delete newAttributes.cx; + delete newAttributes.cy; + delete newAttributes.rx; + delete newAttributes.ry; + newData.attributes = newAttributes; + } + return newData; + } + function translatePointsAttribute(pointsString, x, y) { + var pointStrings = pointsString.split(' '); + var points = pointStrings.map(function (p) { + return p.split(','); + }); + var points = pointStrings.map(function (p) { + return [roundTo2Decimals(+x + +p.split(',')[0]), roundTo2Decimals(+y + +p.split(',')[1])]; + }); + var pointStrings = points.map(function (p) { + return p.join(','); + }); + var pointsString = pointStrings.join(' '); + return pointsString; + } + function translateDAttribute(d, x, y) { + var pointStrings = d.split(/[A-Z ]/); + pointStrings.shift(); + var commands = d.split(/[^[A-Z ]+/); + var points = pointStrings.map(function (p) { + return p.split(','); + }); + var points = pointStrings.map(function (p) { + return [roundTo2Decimals(+x + +p.split(',')[0]), roundTo2Decimals(+y + +p.split(',')[1])]; + }); + var pointStrings = points.map(function (p) { + return p.join(','); + }); + d = commands.reduce(function (arr, v, i) { + return arr.concat(v, pointStrings[i]); + }, []).join(''); + return d; + } + + function initViz() { + var _this = this; + // force JIT compilation of @hpcc-js/wasm + try { + xo.load().then(function (graphviz) { + graphviz.layout("", "svg", "dot"); + _this.layoutSync = graphviz.layout.bind(graphviz); + if (_this._worker == null) { + _this._dispatch.call("initEnd", _this); + } + if (_this._afterInit) { + _this._afterInit(); + } + }); + // after the port to ESM modules, we don't know how to trigger this so + // we just disable it from coverage + /* c8 ignore start */ + } catch (error) { + // we end up here when the the script tag type used to load + // the "@hpcc-js/wasm" script is not "application/javascript" + // or "text/javascript", but typically "javascript/worker". In + // this case the browser does not load the script since it's + // unnecessary because it's loaded by the web worker + // instead. This is expected so we just ignore the error. + } + /* c8 ignore stop */ + if (this._worker != null) { + var vizURL = this._vizURL; + var graphvizInstance = this; + this._workerPort.onmessage = function (event) { + var callback = graphvizInstance._workerCallbacks.shift(); + callback.call(graphvizInstance, event); + }; + if (!vizURL.match(/^https?:\/\/|^\/\//i)) { + // Local URL. Prepend with local domain to be usable in web worker + vizURL = new window.URL(vizURL, document.location.href).href; + } + postMessage.call(this, { + type: "layout", + dot: "", + engine: 'dot', + vizURL: vizURL + }, function (event) { + switch (event.data.type) { + } + }); + postMessage.call(this, { + type: "version" + }, function (event) { + switch (event.data.type) { + case "version": + graphvizInstance._graphvizVersion = event.data.version; + graphvizInstance._dispatch.call("initEnd", this); + break; + } + }); + } + } + function postMessage(message, callback) { + this._workerCallbacks.push(callback); + this._workerPort.postMessage(message); + } + function layout(src, engine, vizOptions, callback) { + if (this._worker) { + postMessage.call(this, { + type: "layout", + dot: src, + engine: engine, + options: vizOptions + }, function (event) { + callback.call(this, event.data); + }); + } else { + try { + var svgDoc = this.layoutSync(src, "svg", engine, vizOptions); + callback.call(this, { + type: 'done', + svg: svgDoc + }); + } catch (error) { + callback.call(this, { + type: 'error', + error: error.message + }); + } + } + } + function dot (src, callback) { + var graphvizInstance = this; + this._worker; + var engine = this._options.engine; + var images = this._images; + this._dispatch.call("start", this); + this._busy = true; + this._dispatch.call("layoutStart", this); + var vizOptions = { + images: images + }; + if (!this._worker && this.layoutSync == null) { + this._afterInit = this.dot.bind(this, src, callback); + return this; + } + this.layout(src, engine, vizOptions, function (data) { + switch (data.type) { + case "error": + if (graphvizInstance._onerror) { + graphvizInstance._onerror(data.error); + } else { + throw data.error.message; + } + break; + case "done": + var svgDoc = data.svg; + layoutDone.call(this, svgDoc, callback); + break; + } + }); + return this; + } + function layoutDone(svgDoc, callback) { + var keyMode = this._options.keyMode; + var tweenPaths = this._options.tweenPaths; + var tweenShapes = this._options.tweenShapes; + if (typeof this._options.tweenPrecision == 'string' && this._options.tweenPrecision.includes('%')) { + var tweenPrecision = +this._options.tweenPrecision.split('%')[0] / 100; + var tweenPrecisionIsRelative = this._options.tweenPrecision.includes('%'); + } else { + var tweenPrecision = this._options.tweenPrecision; + var tweenPrecisionIsRelative = false; + } + var growEnteringEdges = this._options.growEnteringEdges; + var dictionary = {}; + var prevDictionary = this._dictionary || {}; + var nodeDictionary = {}; + var prevNodeDictionary = this._nodeDictionary || {}; + function setKey(datum, index) { + var tag = datum.tag; + if (keyMode == 'index') { + datum.key = index; + } else if (tag[0] != '#') { + if (keyMode == 'id') { + datum.key = datum.attributes.id; + } else if (keyMode == 'title') { + var title = datum.children.find(function (childData) { + return childData.tag == 'title'; + }); + if (title) { + if (title.children.length > 0) { + datum.key = title.children[0].text; + } else { + datum.key = ''; + } + } + } + } + if (datum.key == null) { + if (tweenShapes) { + if (tag == 'ellipse' || tag == 'polygon') { + tag = 'path'; + } + } + datum.key = tag + '-' + index; + } + } + function setId(datum, parentData) { + var id = (parentData ? parentData.id + '.' : '') + datum.key; + datum.id = id; + } + function addToDictionary(datum) { + dictionary[datum.id] = datum; + } + function calculateAlternativeShapeData(datum, prevDatum) { + if (tweenShapes && datum.id in prevDictionary) { + if ((prevDatum.tag == 'polygon' || prevDatum.tag == 'ellipse' || prevDatum.tag == 'path') && (prevDatum.tag != datum.tag || datum.tag == 'polygon')) { + if (prevDatum.tag != 'path') { + datum.alternativeOld = convertToPathData(prevDatum, datum); + } + if (datum.tag != 'path') { + datum.alternativeNew = convertToPathData(datum, prevDatum); + } + } + } + } + function calculatePathTweenPoints(datum, prevDatum) { + if (tweenPaths && prevDatum && (prevDatum.tag == 'path' || datum.alternativeOld && datum.alternativeOld.tag == 'path')) { + var attribute_d = (datum.alternativeNew || datum).attributes.d; + if (datum.alternativeOld) { + var oldNode = createElementWithAttributes(datum.alternativeOld); + } else { + var oldNode = createElementWithAttributes(prevDatum); + } + (datum.alternativeOld || (datum.alternativeOld = {})).points = pathTweenPoints(oldNode, attribute_d, tweenPrecision, tweenPrecisionIsRelative); + } + } + function postProcessDataPass1Local(datum) { + var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var parentData = arguments.length > 2 ? arguments[2] : undefined; + setKey(datum, index); + setId(datum, parentData); + var id = datum.id; + var prevDatum = prevDictionary[id]; + addToDictionary(datum); + calculateAlternativeShapeData(datum, prevDatum); + calculatePathTweenPoints(datum, prevDatum); + var childTagIndexes = {}; + datum.children.forEach(function (childData) { + var childTag = childData.tag; + if (childTag == 'ellipse' || childTag == 'polygon') { + childTag = 'path'; + } + if (childTagIndexes[childTag] == null) { + childTagIndexes[childTag] = 0; + } + var childIndex = childTagIndexes[childTag]++; + postProcessDataPass1Local(childData, childIndex, datum); + }); + } + function addToNodeDictionary(datum) { + var tag = datum.tag; + if (growEnteringEdges && datum.parent) { + if (datum.parent.attributes["class"] == 'node') { + if (tag == 'title') { + if (datum.children.length > 0) { + var child = datum.children[0]; + var nodeId = child.text; + } else { + var nodeId = ''; + } + nodeDictionary[nodeId] = datum.parent; + } + } + } + } + function extractGrowingEdgesData(datum) { + var id = datum.id; + var tag = datum.tag; + var prevDatum = prevDictionary[id]; + if (growEnteringEdges && !prevDatum && datum.parent) { + if (isEdgeElement(datum)) { + if (tag == 'path' || tag == 'polygon') { + if (tag == 'polygon') { + var path = datum.parent.children.find(function (e) { + return e.tag == 'path'; + }); + if (path) { + datum.totalLength = path.totalLength; + } + } + var title = getEdgeTitle(datum); + var child = title.children[0]; + var nodeIds = child.text.split('->'); + if (nodeIds.length != 2) { + nodeIds = child.text.split('--'); + } + var startNodeId = nodeIds[0]; + var startNode = nodeDictionary[startNodeId]; + if (Object.hasOwn(prevNodeDictionary, startNodeId)) { + var prevStartNode = prevNodeDictionary[startNodeId]; + var i = startNode.children.findIndex(function (element, index) { + return element.tag == 'g'; + }); + if (i >= 0) { + var j = startNode.children[i].children.findIndex(function (element, index) { + return element.tag == 'a'; + }); + startNode = startNode.children[i].children[j]; + } + var i = prevStartNode.children.findIndex(function (element, index) { + return element.tag == 'g'; + }); + if (i >= 0) { + var j = prevStartNode.children[i].children.findIndex(function (element, index) { + return element.tag == 'a'; + }); + prevStartNode = prevStartNode.children[i].children[j]; + } + var startShapes = startNode.children; + for (var i = 0; i < startShapes.length; i++) { + if (startShapes[i].tag == 'polygon' || startShapes[i].tag == 'ellipse' || startShapes[i].tag == 'path' || startShapes[i].tag == 'text') { + var startShape = startShapes[i]; + break; + } + } + var prevStartShapes = prevStartNode.children; + for (var i = 0; i < prevStartShapes.length; i++) { + if (prevStartShapes[i].tag == 'polygon' || prevStartShapes[i].tag == 'ellipse' || prevStartShapes[i].tag == 'path' || prevStartShapes[i].tag == 'text') { + var prevStartShape = prevStartShapes[i]; + break; + } + } + if (prevStartShape && startShape) { + datum.offset = { + x: prevStartShape.center.x - startShape.center.x, + y: prevStartShape.center.y - startShape.center.y + }; + } else { + datum.offset = { + x: 0, + y: 0 + }; + } + } + } + } + } + } + function postProcessDataPass2Global(datum) { + addToNodeDictionary(datum); + extractGrowingEdgesData(datum); + datum.children.forEach(function (childData) { + postProcessDataPass2Global(childData); + }); + } + this._dispatch.call("layoutEnd", this); + var newDoc = d3__namespace.select(document.createDocumentFragment()).append('div'); + var parser = new window.DOMParser(); + var doc = parser.parseFromString(svgDoc, "image/svg+xml"); + newDoc.append(function () { + return doc.documentElement; + }); + var newSvg = newDoc.select('svg'); + var data = extractAllElementsData(newSvg); + this._dispatch.call('dataExtractEnd', this); + postProcessDataPass1Local(data); + this._dispatch.call('dataProcessPass1End', this); + postProcessDataPass2Global(data); + this._dispatch.call('dataProcessPass2End', this); + this._data = data; + this._dictionary = dictionary; + this._nodeDictionary = nodeDictionary; + this._extractData = function (element, childIndex, parentData) { + var data = extractAllElementsData(element); + postProcessDataPass1Local(data, childIndex, parentData); + postProcessDataPass2Global(data); + return data; + }; + this._busy = false; + this._dispatch.call('dataProcessEnd', this); + if (callback) { + callback.call(this); + } + if (this._queue.length > 0) { + var job = this._queue.shift(); + job.call(this); + } + } + + function renderDot (src, callback) { + var graphvizInstance = this; + this.dot(src, render); + function render() { + graphvizInstance.render(callback); + } + return this; + } + + function transition (name) { + if (name instanceof Function) { + this._transitionFactory = name; + } else { + this._transition = d3Transition.transition(name); + } + return this; + } + function active(name) { + var root = this._selection; + var svg = root.selectWithoutDataPropagation("svg"); + if (svg.size() != 0) { + return d3Transition.active(svg.node(), name); + } else { + return null; + } + } + + function options (options) { + if (typeof options == 'undefined') { + return Object.assign({}, this._options); + } else { + for (var _i = 0, _Object$keys = Object.keys(options); _i < _Object$keys.length; _i++) { + var option = _Object$keys[_i]; + this._options[option] = options[option]; + } + return this; + } + } + + function width (width) { + this._options.width = width; + return this; + } + + function height (height) { + this._options.height = height; + return this; + } + + function scale (scale) { + this._options.scale = scale; + return this; + } + + function fit (fit) { + this._options.fit = fit; + return this; + } + + function attributer (callback) { + this._attributer = callback; + return this; + } + + function engine (engine) { + this._options.engine = engine; + return this; + } + + function images (path, width, height) { + this._images.push({ + path: path, + width: width, + height: height + }); + return this; + } + + function keyMode (keyMode) { + if (!this._keyModes.has(keyMode)) { + throw Error('Illegal keyMode: ' + keyMode); + } + if (keyMode != this._options.keyMode && this._data != null) { + throw Error('Too late to change keyMode'); + } + this._options.keyMode = keyMode; + return this; + } + + function fade (enable) { + this._options.fade = enable; + return this; + } + + function tweenPaths (enable) { + this._options.tweenPaths = enable; + return this; + } + + function tweenShapes (enable) { + this._options.tweenShapes = enable; + if (enable) { + this._options.tweenPaths = true; + } + return this; + } + + function convertEqualSidedPolygons (enable) { + this._options.convertEqualSidedPolygons = enable; + return this; + } + + function tweenPrecision (precision) { + this._options.tweenPrecision = precision; + return this; + } + + function growEnteringEdges (enable) { + this._options.growEnteringEdges = enable; + return this; + } + + function on (typenames, callback) { + this._dispatch.on(typenames, callback); + return this; + } + + function onerror (callback) { + this._onerror = callback; + return this; + } + + function logEvents (enable) { + var _this = this; + var t0 = Date.now(); + var times = {}; + var eventTypes = this._eventTypes; + var maxEventTypeLength = Math.max.apply(Math, _toConsumableArray(eventTypes.map(function (eventType) { + return eventType.length; + }))); + var _loop = function _loop(i) { + var eventType = eventTypes[i]; + times[eventType] = []; + graphvizInstance = _this; + _this.on(eventType + '.log', enable ? function () { + var t = Date.now(); + var seqNo = times[eventType].length; + times[eventType].push(t); + var string = ''; + string += 'Event '; + string += d3Format.format(' >2')(i) + ' '; + string += eventType + ' '.repeat(maxEventTypeLength - eventType.length); + string += d3Format.format(' >5')(t - t0) + ' '; + if (eventType != 'initEnd') { + string += d3Format.format(' >5')(t - times['start'][seqNo]); + } + if (eventType == 'dataProcessEnd') { + string += ' prepare ' + d3Format.format(' >5')(t - times['layoutEnd'][seqNo]); + } + if (eventType == 'renderEnd' && graphvizInstance._transition) { + string += ' transition start margin ' + d3Format.format(' >5')(graphvizInstance._transition.delay() - (t - times['renderStart'][seqNo])); + expectedDelay = graphvizInstance._transition.delay(); + expectedDuration = graphvizInstance._transition.duration(); + } + if (eventType == 'transitionStart') { + var actualDelay = t - times['renderStart'][seqNo]; + string += ' transition delay ' + d3Format.format(' >5')(t - times['renderStart'][seqNo]); + string += ' expected ' + d3Format.format(' >5')(expectedDelay); + string += ' diff ' + d3Format.format(' >5')(actualDelay - expectedDelay); + } + if (eventType == 'transitionEnd') { + var actualDuration = t - times['transitionStart'][seqNo]; + string += ' transition duration ' + d3Format.format(' >5')(actualDuration); + string += ' expected ' + d3Format.format(' >5')(expectedDuration); + string += ' diff ' + d3Format.format(' >5')(actualDuration - expectedDuration); + } + console.log(string); + t0 = t; + } : null); + }, + graphvizInstance, + expectedDelay, + expectedDuration; + for (var i = 0; i < eventTypes.length; i++) { + _loop(i); + } + return this; + } + + function destroy () { + delete this._selection.node().__graphviz__; + if (this._worker) { + this._workerPortClose(); + } + return this; + } + + function rotate(x, y, cosA, sinA) { + // (x + j * y) * (cosA + j * sinA) = x * cosA - y * sinA + j * (x * sinA + y * cosA) + y = -y; + sinA = -sinA; + var _ref = [x * cosA - y * sinA, x * sinA + y * cosA]; + x = _ref[0]; + y = _ref[1]; + y = -y; + return [x, y]; + } + + function drawEdge(x1, y1, x2, y2, attributes) { + var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; + attributes = Object.assign({}, attributes); + if (attributes.style && attributes.style.includes('invis')) { + var newEdge = d3__namespace.select(null); + } else { + var root = this._selection; + var svg = root.selectWithoutDataPropagation("svg"); + var graph0 = svg.selectWithoutDataPropagation("g"); + var newEdge0 = createEdge.call(this, attributes); + var edgeData = extractAllElementsData(newEdge0); + var newEdge = graph0.append('g').data([edgeData]); + attributeElement.call(newEdge.node(), edgeData); + _updateEdge.call(this, newEdge, x1, y1, x2, y2, attributes, options); + } + this._drawnEdge = { + g: newEdge, + x1: x1, + y1: y1, + x2: x2, + y2: y2, + attributes: attributes + }; + return this; + } + function updateDrawnEdge(x1, y1, x2, y2) { + var attributes = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; + var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; + if (!this._drawnEdge) { + throw Error('No edge has been drawn'); + } + var edge = this._drawnEdge.g; + attributes = Object.assign(this._drawnEdge.attributes, attributes); + this._drawnEdge.x1 = x1; + this._drawnEdge.y1 = y1; + this._drawnEdge.x2 = x2; + this._drawnEdge.y2 = y2; + if (edge.empty() && !(attributes.style && attributes.style.includes('invis'))) { + var root = this._selection; + var svg = root.selectWithoutDataPropagation("svg"); + var graph0 = svg.selectWithoutDataPropagation("g"); + var edge = graph0.append('g'); + this._drawnEdge.g = edge; + } + if (!edge.empty()) { + _updateEdge.call(this, edge, x1, y1, x2, y2, attributes, options); + } + return this; + } + function _updateEdge(edge, x1, y1, x2, y2, attributes, options) { + var newEdge = createEdge.call(this, attributes); + var edgeData = extractAllElementsData(newEdge); + edge.data([edgeData]); + attributeElement.call(edge.node(), edgeData); + _moveEdge(edge, x1, y1, x2, y2, attributes, options); + } + function _moveEdge(edge, x1, y1, x2, y2, attributes, options) { + var shortening = options.shortening || 0; + var arrowHeadLength = 10; + var arrowHeadWidth = 7; + var margin = 0.1; + var arrowHeadPoints = [[0, -arrowHeadWidth / 2], [arrowHeadLength, 0], [0, arrowHeadWidth / 2], [0, -arrowHeadWidth / 2]]; + var dx = x2 - x1; + var dy = y2 - y1; + var length = Math.sqrt(dx * dx + dy * dy); + if (length == 0) { + var cosA = 1; + var sinA = 0; + } else { + var cosA = dx / length; + var sinA = dy / length; + } + x2 = x1 + (length - shortening - arrowHeadLength - margin) * cosA; + y2 = y1 + (length - shortening - arrowHeadLength - margin) * sinA; + if (attributes.URL || attributes.tooltip) { + var a = edge.selectWithoutDataPropagation("g").selectWithoutDataPropagation("a"); + var line = a.selectWithoutDataPropagation("path"); + var arrowHead = a.selectWithoutDataPropagation("polygon"); + } else { + var line = edge.selectWithoutDataPropagation("path"); + var arrowHead = edge.selectWithoutDataPropagation("polygon"); + } + var path1 = d3Path.path(); + path1.moveTo(x1, y1); + path1.lineTo(x2, y2); + line.attr("d", path1); + x2 = x1 + (length - shortening - arrowHeadLength) * cosA; + y2 = y1 + (length - shortening - arrowHeadLength) * sinA; + for (var i = 0; i < arrowHeadPoints.length; i++) { + var point = arrowHeadPoints[i]; + arrowHeadPoints[i] = rotate(point[0], point[1], cosA, sinA); + } + for (var i = 0; i < arrowHeadPoints.length; i++) { + var point = arrowHeadPoints[i]; + arrowHeadPoints[i] = [x2 + point[0], y2 + point[1]]; + } + var allPoints = []; + for (var i = 0; i < arrowHeadPoints.length; i++) { + var point = arrowHeadPoints[i]; + allPoints.push(point.join(',')); + } + var pointsAttr = allPoints.join(' '); + arrowHead.attr("points", pointsAttr); + return this; + } + function moveDrawnEdgeEndPoint(x2, y2) { + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + if (!this._drawnEdge) { + throw Error('No edge has been drawn'); + } + var edge = this._drawnEdge.g; + var x1 = this._drawnEdge.x1; + var y1 = this._drawnEdge.y1; + var attributes = this._drawnEdge.attributes; + this._drawnEdge.x2 = x2; + this._drawnEdge.y2 = y2; + _moveEdge(edge, x1, y1, x2, y2, attributes, options); + return this; + } + function removeDrawnEdge() { + if (!this._drawnEdge) { + return this; + } + var edge = this._drawnEdge.g; + edge.remove(); + this._drawnEdge = null; + return this; + } + function insertDrawnEdge(name) { + if (!this._drawnEdge) { + throw Error('No edge has been drawn'); + } + var edge = this._drawnEdge.g; + if (edge.empty()) { + return this; + } + this._drawnEdge.attributes; + var title = edge.selectWithoutDataPropagation("title"); + title.text(name); + var root = this._selection; + var svg = root.selectWithoutDataPropagation("svg"); + var graph0 = svg.selectWithoutDataPropagation("g"); + var graph0Datum = graph0.datum(); + var edgeData = this._extractData(edge, graph0Datum.children.length, graph0.datum()); + graph0Datum.children.push(edgeData); + insertAllElementsData(edge, edgeData); + this._drawnEdge = null; + return this; + } + function drawnEdgeSelection() { + if (this._drawnEdge) { + return this._drawnEdge.g; + } else { + return d3__namespace.select(null); + } + } + function createEdge(attributes) { + var attributesString = ''; + for (var _i = 0, _Object$keys = Object.keys(attributes); _i < _Object$keys.length; _i++) { + var name = _Object$keys[_i]; + if (attributes[name] != null) { + attributesString += ' "' + name + '"="' + attributes[name] + '"'; + } + } + var dotSrc = 'digraph {a -> b [' + attributesString + ']}'; + var svgDoc = this.layoutSync(dotSrc, 'svg', 'dot'); + var parser = new window.DOMParser(); + var doc = parser.parseFromString(svgDoc, "image/svg+xml"); + var newDoc = d3__namespace.select(document.createDocumentFragment()).append(function () { + return doc.documentElement; + }); + var edge = newDoc.select('.edge'); + return edge; + } + + function drawNode(x, y, nodeId) { + var attributes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; + attributes = Object.assign({}, attributes); + if (attributes.style && attributes.style.includes('invis')) { + var newNode = d3__namespace.select(null); + } else { + var root = this._selection; + var svg = root.selectWithoutDataPropagation("svg"); + var graph0 = svg.selectWithoutDataPropagation("g"); + var newNode0 = createNode.call(this, nodeId, attributes); + var nodeData = extractAllElementsData(newNode0); + var newNode = graph0.append('g').data([nodeData]); + attributeElement.call(newNode.node(), nodeData); + _updateNode.call(this, newNode, x, y, nodeId, attributes, options); + } + this._drawnNode = { + g: newNode, + nodeId: nodeId, + x: x, + y: y, + attributes: attributes + }; + return this; + } + function updateDrawnNode(x, y, nodeId) { + var attributes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; + if (!this._drawnNode) { + throw Error('No node has been drawn'); + } + var node = this._drawnNode.g; + if (nodeId == null) { + nodeId = this._drawnNode.nodeId; + } + attributes = Object.assign(this._drawnNode.attributes, attributes); + this._drawnNode.nodeId = nodeId; + this._drawnNode.x = x; + this._drawnNode.y = y; + if (node.empty() && !(attributes.style && attributes.style.includes('invis'))) { + var root = this._selection; + var svg = root.selectWithoutDataPropagation("svg"); + var graph0 = svg.selectWithoutDataPropagation("g"); + var node = graph0.append('g'); + this._drawnNode.g = node; + } + if (!node.empty()) { + _updateNode.call(this, node, x, y, nodeId, attributes, options); + } + return this; + } + function _updateNode(node, x, y, nodeId, attributes, options) { + var newNode = createNode.call(this, nodeId, attributes); + var nodeData = extractAllElementsData(newNode); + node.data([nodeData]); + attributeElement.call(node.node(), nodeData); + _moveNode(node, x, y, attributes); + return this; + } + function _moveNode(node, x, y, attributes, options) { + if (attributes.URL || attributes.tooltip) { + var subParent = node.selectWithoutDataPropagation("g").selectWithoutDataPropagation("a"); + } else { + var subParent = node; + } + var svgElements = subParent.selectAll('ellipse,polygon,path,polyline'); + var text = node.selectWithoutDataPropagation("text"); + if (svgElements.size() != 0) { + var bbox = svgElements.node().getBBox(); + bbox.cx = bbox.x + bbox.width / 2; + bbox.cy = bbox.y + bbox.height / 2; + } else if (text.size() != 0) { + bbox = { + x: +text.attr('x'), + y: +text.attr('y'), + width: 0, + height: 0, + cx: +text.attr('x'), + cy: +text.attr('y') + }; + } + svgElements.each(function (data, index) { + var svgElement = d3__namespace.select(this); + if (svgElement.attr("cx")) { + svgElement.attr("cx", roundTo2Decimals(x)).attr("cy", roundTo2Decimals(y)); + } else if (svgElement.attr("points")) { + var pointsString = svgElement.attr('points').trim(); + svgElement.attr("points", translatePointsAttribute(pointsString, x - bbox.cx, y - bbox.cy)); + } else { + var d = svgElement.attr('d'); + svgElement.attr("d", translateDAttribute(d, x - bbox.cx, y - bbox.cy)); + } + }); + if (text.size() != 0) { + text.attr("x", roundTo2Decimals(+text.attr("x") + x - bbox.cx)).attr("y", roundTo2Decimals(+text.attr("y") + y - bbox.cy)); + } + return this; + } + function moveDrawnNode(x, y) { + if (!this._drawnNode) { + throw Error('No node has been drawn'); + } + var node = this._drawnNode.g; + var attributes = this._drawnNode.attributes; + this._drawnNode.x = x; + this._drawnNode.y = y; + if (!node.empty()) { + _moveNode(node, x, y, attributes); + } + return this; + } + function removeDrawnNode() { + if (!this._drawnNode) { + return this; + } + var node = this._drawnNode.g; + if (!node.empty()) { + node.remove(); + } + this._drawnNode = null; + return this; + } + function insertDrawnNode(nodeId) { + if (!this._drawnNode) { + throw Error('No node has been drawn'); + } + if (nodeId == null) { + nodeId = this._drawnNode.nodeId; + } + var node = this._drawnNode.g; + if (node.empty()) { + return this; + } + var attributes = this._drawnNode.attributes; + var title = node.selectWithoutDataPropagation("title"); + title.text(nodeId); + if (attributes.URL || attributes.tooltip) { + var ga = node.selectWithoutDataPropagation("g"); + var a = ga.selectWithoutDataPropagation("a"); + a.selectWithoutDataPropagation('ellipse,polygon,path,polyline'); + var text = a.selectWithoutDataPropagation('text'); + } else { + node.selectWithoutDataPropagation('ellipse,polygon,path,polyline'); + var text = node.selectWithoutDataPropagation('text'); + } + text.text(attributes.label || nodeId); + var root = this._selection; + var svg = root.selectWithoutDataPropagation("svg"); + var graph0 = svg.selectWithoutDataPropagation("g"); + var graph0Datum = graph0.datum(); + var nodeData = this._extractData(node, graph0Datum.children.length, graph0.datum()); + graph0Datum.children.push(nodeData); + insertAllElementsData(node, nodeData); + this._drawnNode = null; + return this; + } + function drawnNodeSelection() { + if (this._drawnNode) { + return this._drawnNode.g; + } else { + return d3__namespace.select(null); + } + } + function createNode(nodeId, attributes) { + var attributesString = ''; + for (var _i = 0, _Object$keys = Object.keys(attributes); _i < _Object$keys.length; _i++) { + var name = _Object$keys[_i]; + if (attributes[name] != null) { + attributesString += ' "' + name + '"="' + attributes[name] + '"'; + } + } + var dotSrc = 'graph {"' + nodeId + '" [' + attributesString + ']}'; + var svgDoc = this.layoutSync(dotSrc, 'svg', 'dot'); + var parser = new window.DOMParser(); + var doc = parser.parseFromString(svgDoc, "image/svg+xml"); + var newDoc = d3__namespace.select(document.createDocumentFragment()).append(function () { + return doc.documentElement; + }); + var node = newDoc.select('.node'); + return node; + } + + /* This file is excluded from coverage because the instrumented code + * translates "self" which gives a reference error. + */ + + /* c8 ignore start */ + + function workerCodeBody(port) { + self.document = {}; // Workaround for "ReferenceError: document is not defined" in hpccWasm + + port.addEventListener('message', function (event) { + var hpccWasm = self["@hpcc-js/wasm"]; + if (hpccWasm == undefined && event.data.vizURL) { + importScripts(event.data.vizURL); + hpccWasm = self["@hpcc-js/wasm"]; + // This is an alternative workaround where wasmFolder() is not needed + // document = {currentScript: {src: event.data.vizURL}}; + } + if (event.data.type == "version") { + hpccWasm.Graphviz.load().then(function (graphviz) { + port.postMessage({ + type: "version", + version: graphviz.version() + }); + }); + return; + } + hpccWasm.Graphviz.load().then(function (graphviz) { + var svg = graphviz.layout(event.data.dot, "svg", event.data.engine, event.data.options); + if (svg) { + port.postMessage({ + type: "done", + svg: svg + }); + } else if (event.data.vizURL) { + port.postMessage({ + type: "init" + }); + } else { + port.postMessage({ + type: "skip" + }); + } + })["catch"](function (error) { + port.postMessage({ + type: "error", + error: error.message + }); + }); + }); + } + function workerCode() { + var port = self; + workerCodeBody(port); + } + function sharedWorkerCode() { + self.onconnect = function (e) { + var port = e.ports[0]; + workerCodeBody(port); + port.start(); + }; + } + + /* c8 ignore stop */ + + var _graphviz$prototype; + function Graphviz(selection, options) { + var _this = this; + this._options = { + useWorker: true, + useSharedWorker: false, + engine: 'dot', + keyMode: 'title', + fade: true, + tweenPaths: true, + tweenShapes: true, + convertEqualSidedPolygons: true, + tweenPrecision: 1, + growEnteringEdges: true, + zoom: true, + zoomScaleExtent: [0.1, 10], + zoomTranslateExtent: [[-Infinity, -Infinity], [+Infinity, +Infinity]], + width: null, + height: null, + scale: 1, + fit: false + }; + if (options instanceof Object) { + for (var _i = 0, _Object$keys = Object.keys(options); _i < _Object$keys.length; _i++) { + var option = _Object$keys[_i]; + this._options[option] = options[option]; + } + } else if (typeof options == 'boolean') { + this._options.useWorker = options; + } + var useWorker = this._options.useWorker; + var useSharedWorker = this._options.useSharedWorker; + if (typeof Worker == 'undefined') { + useWorker = false; + } + if (typeof SharedWorker == 'undefined') { + useSharedWorker = false; + } + if (useWorker || useSharedWorker) { + var scripts = d3__namespace.selectAll('script'); + var vizScript = scripts.filter(function () { + return d3__namespace.select(this).attr('type') == 'javascript/worker' || d3__namespace.select(this).attr('src') && d3__namespace.select(this).attr('src').match(/.*\/@hpcc-js\/wasm/); + }); + if (vizScript.size() == 0) { + console.warn('No script tag of type "javascript/worker" was found and "useWorker" is true. Not using web worker.'); + useWorker = false; + useSharedWorker = false; + } else { + this._vizURL = vizScript.attr('src'); + if (!this._vizURL) { + console.warn('No "src" attribute of was found on the "javascript/worker" script tag and "useWorker" is true. Not using web worker.'); + useWorker = false; + useSharedWorker = false; + } + } + } + if (useSharedWorker) { + var url = 'data:application/javascript;base64,' + btoa(workerCodeBody.toString() + '(' + sharedWorkerCode.toString() + ')()'); + this._worker = this._worker = new SharedWorker(url); + this._workerPort = this._worker.port; + this._workerPortClose = this._worker.port.close.bind(this._workerPort); + this._worker.port.start(); + this._workerCallbacks = []; + } else if (useWorker) { + var blob = new Blob([workerCodeBody.toString() + '(' + workerCode.toString() + ')()']); + var blobURL = window.URL.createObjectURL(blob); + this._worker = new Worker(blobURL); + this._workerPort = this._worker; + this._workerPortClose = this._worker.terminate.bind(this._worker); + this._workerCallbacks = []; + } else { + xo.load().then(function (graphviz) { + _this._graphvizVersion = graphviz.version(); + }.bind(this)); + } + this._selection = selection; + this._active = false; + this._busy = false; + this._jobs = []; + this._queue = []; + this._keyModes = new Set(['title', 'id', 'tag-index', 'index']); + this._images = []; + this._translation = undefined; + this._scale = undefined; + this._eventTypes = ['initEnd', 'start', 'layoutStart', 'layoutEnd', 'dataExtractEnd', 'dataProcessPass1End', 'dataProcessPass2End', 'dataProcessEnd', 'renderStart', 'renderEnd', 'transitionStart', 'transitionEnd', 'restoreEnd', 'end', 'zoom']; + this._dispatch = d3Dispatch.dispatch.apply(void 0, _toConsumableArray(this._eventTypes)); + initViz.call(this); + selection.node().__graphviz__ = this; + } + function graphviz(selector, options) { + var g = d3__namespace.select(selector).graphviz(options); + return g; + } + Graphviz.prototype = graphviz.prototype = (_graphviz$prototype = { + constructor: Graphviz, + engine: engine, + addImage: images, + keyMode: keyMode, + fade: fade, + tweenPaths: tweenPaths, + tweenShapes: tweenShapes, + convertEqualSidedPolygons: convertEqualSidedPolygons, + tweenPrecision: tweenPrecision, + growEnteringEdges: growEnteringEdges, + zoom: zoom, + resetZoom: resetZoom, + zoomBehavior: zoomBehavior, + zoomSelection: zoomSelection, + zoomScaleExtent: zoomScaleExtent, + zoomTranslateExtent: zoomTranslateExtent, + render: render, + layout: layout, + dot: dot, + data: data, + renderDot: renderDot, + transition: transition, + active: active, + options: options, + width: width, + height: height, + scale: scale, + fit: fit, + attributer: attributer, + on: on, + onerror: onerror, + logEvents: logEvents, + destroy: destroy, + drawEdge: drawEdge, + updateDrawnEdge: updateDrawnEdge, + moveDrawnEdgeEndPoint: moveDrawnEdgeEndPoint, + insertDrawnEdge: insertDrawnEdge, + removeDrawnEdge: removeDrawnEdge + }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_graphviz$prototype, "removeDrawnEdge", removeDrawnEdge), "drawnEdgeSelection", drawnEdgeSelection), "drawnEdgeSelection", drawnEdgeSelection), "drawNode", drawNode), "updateDrawnNode", updateDrawnNode), "moveDrawnNode", moveDrawnNode), "insertDrawnNode", insertDrawnNode), "removeDrawnNode", removeDrawnNode), "removeDrawnNode", removeDrawnNode), "drawnNodeSelection", drawnNodeSelection), _defineProperty(_defineProperty(_graphviz$prototype, "drawnNodeSelection", drawnNodeSelection), "graphvizVersion", graphvizVersion)); + + function selection_graphviz (options) { + var g = this.node().__graphviz__; + if (g) { + g.options(options); + // Ensure a possible new initEnd event handler is attached before calling it + d3Timer.timeout(function () { + g._dispatch.call("initEnd", this); + }.bind(this), 0); + } else { + g = new Graphviz(this, options); + } + return g; + } + + function selection_selectWithoutDataPropagation (name) { + return d3__namespace.select(this.size() > 0 ? this.node().querySelector(name) : null); + } + + d3.selection.prototype.graphviz = selection_graphviz; + d3.selection.prototype.selectWithoutDataPropagation = selection_selectWithoutDataPropagation; + + exports.graphviz = graphviz; + +})); +//# sourceMappingURL=d3-graphviz.js.map diff --git a/chapter25/docs/d3.v7.min.js b/chapter25/docs/d3.v7.min.js new file mode 100644 index 00000000..33bb8802 --- /dev/null +++ b/chapter25/docs/d3.v7.min.js @@ -0,0 +1,2 @@ +// https://d3js.org v7.9.0 Copyright 2010-2023 Mike Bostock +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).d3=t.d3||{})}(this,(function(t){"use strict";function n(t,n){return null==t||null==n?NaN:t<n?-1:t>n?1:t>=n?0:NaN}function e(t,n){return null==t||null==n?NaN:n<t?-1:n>t?1:n>=t?0:NaN}function r(t){let r,o,a;function u(t,n,e=0,i=t.length){if(e<i){if(0!==r(n,n))return i;do{const r=e+i>>>1;o(t[r],n)<0?e=r+1:i=r}while(e<i)}return e}return 2!==t.length?(r=n,o=(e,r)=>n(t(e),r),a=(n,e)=>t(n)-e):(r=t===n||t===e?t:i,o=t,a=t),{left:u,center:function(t,n,e=0,r=t.length){const i=u(t,n,e,r-1);return i>e&&a(t[i-1],n)>-a(t[i],n)?i-1:i},right:function(t,n,e=0,i=t.length){if(e<i){if(0!==r(n,n))return i;do{const r=e+i>>>1;o(t[r],n)<=0?e=r+1:i=r}while(e<i)}return e}}}function i(){return 0}function o(t){return null===t?NaN:+t}const a=r(n),u=a.right,c=a.left,f=r(o).center;var s=u;const l=d(y),h=d((function(t){const n=y(t);return(t,e,r,i,o)=>{n(t,e,(r<<=2)+0,(i<<=2)+0,o<<=2),n(t,e,r+1,i+1,o),n(t,e,r+2,i+2,o),n(t,e,r+3,i+3,o)}}));function d(t){return function(n,e,r=e){if(!((e=+e)>=0))throw new RangeError("invalid rx");if(!((r=+r)>=0))throw new RangeError("invalid ry");let{data:i,width:o,height:a}=n;if(!((o=Math.floor(o))>=0))throw new RangeError("invalid width");if(!((a=Math.floor(void 0!==a?a:i.length/o))>=0))throw new RangeError("invalid height");if(!o||!a||!e&&!r)return n;const u=e&&t(e),c=r&&t(r),f=i.slice();return u&&c?(p(u,f,i,o,a),p(u,i,f,o,a),p(u,f,i,o,a),g(c,i,f,o,a),g(c,f,i,o,a),g(c,i,f,o,a)):u?(p(u,i,f,o,a),p(u,f,i,o,a),p(u,i,f,o,a)):c&&(g(c,i,f,o,a),g(c,f,i,o,a),g(c,i,f,o,a)),n}}function p(t,n,e,r,i){for(let o=0,a=r*i;o<a;)t(n,e,o,o+=r,1)}function g(t,n,e,r,i){for(let o=0,a=r*i;o<r;++o)t(n,e,o,o+a,r)}function y(t){const n=Math.floor(t);if(n===t)return function(t){const n=2*t+1;return(e,r,i,o,a)=>{if(!((o-=a)>=i))return;let u=t*r[i];const c=a*t;for(let t=i,n=i+c;t<n;t+=a)u+=r[Math.min(o,t)];for(let t=i,f=o;t<=f;t+=a)u+=r[Math.min(o,t+c)],e[t]=u/n,u-=r[Math.max(i,t-c)]}}(t);const e=t-n,r=2*t+1;return(t,i,o,a,u)=>{if(!((a-=u)>=o))return;let c=n*i[o];const f=u*n,s=f+u;for(let t=o,n=o+f;t<n;t+=u)c+=i[Math.min(a,t)];for(let n=o,l=a;n<=l;n+=u)c+=i[Math.min(a,n+f)],t[n]=(c+e*(i[Math.max(o,n-s)]+i[Math.min(a,n+s)]))/r,c-=i[Math.max(o,n-f)]}}function v(t,n){let e=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&++e;else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(i=+i)>=i&&++e}return e}function _(t){return 0|t.length}function b(t){return!(t>0)}function m(t){return"object"!=typeof t||"length"in t?t:Array.from(t)}function x(t,n){let e,r=0,i=0,o=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(e=n-i,i+=e/++r,o+=e*(n-i));else{let a=-1;for(let u of t)null!=(u=n(u,++a,t))&&(u=+u)>=u&&(e=u-i,i+=e/++r,o+=e*(u-i))}if(r>1)return o/(r-1)}function w(t,n){const e=x(t,n);return e?Math.sqrt(e):e}function M(t,n){let e,r;if(void 0===n)for(const n of t)null!=n&&(void 0===e?n>=n&&(e=r=n):(e>n&&(e=n),r<n&&(r=n)));else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(void 0===e?o>=o&&(e=r=o):(e>o&&(e=o),r<o&&(r=o)))}return[e,r]}class T{constructor(){this._partials=new Float64Array(32),this._n=0}add(t){const n=this._partials;let e=0;for(let r=0;r<this._n&&r<32;r++){const i=n[r],o=t+i,a=Math.abs(t)<Math.abs(i)?t-(o-i):i-(o-t);a&&(n[e++]=a),t=o}return n[e]=t,this._n=e+1,this}valueOf(){const t=this._partials;let n,e,r,i=this._n,o=0;if(i>0){for(o=t[--i];i>0&&(n=o,e=t[--i],o=n+e,r=e-(o-n),!r););i>0&&(r<0&&t[i-1]<0||r>0&&t[i-1]>0)&&(e=2*r,n=o+e,e==n-o&&(o=n))}return o}}class InternMap extends Map{constructor(t,n=N){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(A(this,t))}has(t){return super.has(A(this,t))}set(t,n){return super.set(S(this,t),n)}delete(t){return super.delete(E(this,t))}}class InternSet extends Set{constructor(t,n=N){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const n of t)this.add(n)}has(t){return super.has(A(this,t))}add(t){return super.add(S(this,t))}delete(t){return super.delete(E(this,t))}}function A({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):e}function S({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}function E({_intern:t,_key:n},e){const r=n(e);return t.has(r)&&(e=t.get(r),t.delete(r)),e}function N(t){return null!==t&&"object"==typeof t?t.valueOf():t}function k(t){return t}function C(t,...n){return F(t,k,k,n)}function P(t,...n){return F(t,Array.from,k,n)}function z(t,n){for(let e=1,r=n.length;e<r;++e)t=t.flatMap((t=>t.pop().map((([n,e])=>[...t,n,e]))));return t}function $(t,n,...e){return F(t,k,n,e)}function D(t,n,...e){return F(t,Array.from,n,e)}function R(t){if(1!==t.length)throw new Error("duplicate key");return t[0]}function F(t,n,e,r){return function t(i,o){if(o>=r.length)return e(i);const a=new InternMap,u=r[o++];let c=-1;for(const t of i){const n=u(t,++c,i),e=a.get(n);e?e.push(t):a.set(n,[t])}for(const[n,e]of a)a.set(n,t(e,o));return n(a)}(t,0)}function q(t,n){return Array.from(n,(n=>t[n]))}function U(t,...n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");t=Array.from(t);let[e]=n;if(e&&2!==e.length||n.length>1){const r=Uint32Array.from(t,((t,n)=>n));return n.length>1?(n=n.map((n=>t.map(n))),r.sort(((t,e)=>{for(const r of n){const n=O(r[t],r[e]);if(n)return n}}))):(e=t.map(e),r.sort(((t,n)=>O(e[t],e[n])))),q(t,r)}return t.sort(I(e))}function I(t=n){if(t===n)return O;if("function"!=typeof t)throw new TypeError("compare is not a function");return(n,e)=>{const r=t(n,e);return r||0===r?r:(0===t(e,e))-(0===t(n,n))}}function O(t,n){return(null==t||!(t>=t))-(null==n||!(n>=n))||(t<n?-1:t>n?1:0)}var B=Array.prototype.slice;function Y(t){return()=>t}const L=Math.sqrt(50),j=Math.sqrt(10),H=Math.sqrt(2);function X(t,n,e){const r=(n-t)/Math.max(0,e),i=Math.floor(Math.log10(r)),o=r/Math.pow(10,i),a=o>=L?10:o>=j?5:o>=H?2:1;let u,c,f;return i<0?(f=Math.pow(10,-i)/a,u=Math.round(t*f),c=Math.round(n*f),u/f<t&&++u,c/f>n&&--c,f=-f):(f=Math.pow(10,i)*a,u=Math.round(t/f),c=Math.round(n/f),u*f<t&&++u,c*f>n&&--c),c<u&&.5<=e&&e<2?X(t,n,2*e):[u,c,f]}function G(t,n,e){if(!((e=+e)>0))return[];if((t=+t)===(n=+n))return[t];const r=n<t,[i,o,a]=r?X(n,t,e):X(t,n,e);if(!(o>=i))return[];const u=o-i+1,c=new Array(u);if(r)if(a<0)for(let t=0;t<u;++t)c[t]=(o-t)/-a;else for(let t=0;t<u;++t)c[t]=(o-t)*a;else if(a<0)for(let t=0;t<u;++t)c[t]=(i+t)/-a;else for(let t=0;t<u;++t)c[t]=(i+t)*a;return c}function V(t,n,e){return X(t=+t,n=+n,e=+e)[2]}function W(t,n,e){e=+e;const r=(n=+n)<(t=+t),i=r?V(n,t,e):V(t,n,e);return(r?-1:1)*(i<0?1/-i:i)}function Z(t,n,e){let r;for(;;){const i=V(t,n,e);if(i===r||0===i||!isFinite(i))return[t,n];i>0?(t=Math.floor(t/i)*i,n=Math.ceil(n/i)*i):i<0&&(t=Math.ceil(t*i)/i,n=Math.floor(n*i)/i),r=i}}function K(t){return Math.max(1,Math.ceil(Math.log(v(t))/Math.LN2)+1)}function Q(){var t=k,n=M,e=K;function r(r){Array.isArray(r)||(r=Array.from(r));var i,o,a,u=r.length,c=new Array(u);for(i=0;i<u;++i)c[i]=t(r[i],i,r);var f=n(c),l=f[0],h=f[1],d=e(c,l,h);if(!Array.isArray(d)){const t=h,e=+d;if(n===M&&([l,h]=Z(l,h,e)),(d=G(l,h,e))[0]<=l&&(a=V(l,h,e)),d[d.length-1]>=h)if(t>=h&&n===M){const t=V(l,h,e);isFinite(t)&&(t>0?h=(Math.floor(h/t)+1)*t:t<0&&(h=(Math.ceil(h*-t)+1)/-t))}else d.pop()}for(var p=d.length,g=0,y=p;d[g]<=l;)++g;for(;d[y-1]>h;)--y;(g||y<p)&&(d=d.slice(g,y),p=y-g);var v,_=new Array(p+1);for(i=0;i<=p;++i)(v=_[i]=[]).x0=i>0?d[i-1]:l,v.x1=i<p?d[i]:h;if(isFinite(a)){if(a>0)for(i=0;i<u;++i)null!=(o=c[i])&&l<=o&&o<=h&&_[Math.min(p,Math.floor((o-l)/a))].push(r[i]);else if(a<0)for(i=0;i<u;++i)if(null!=(o=c[i])&&l<=o&&o<=h){const t=Math.floor((l-o)*a);_[Math.min(p,t+(d[t]<=o))].push(r[i])}}else for(i=0;i<u;++i)null!=(o=c[i])&&l<=o&&o<=h&&_[s(d,o,0,p)].push(r[i]);return _}return r.value=function(n){return arguments.length?(t="function"==typeof n?n:Y(n),r):t},r.domain=function(t){return arguments.length?(n="function"==typeof t?t:Y([t[0],t[1]]),r):n},r.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Y(Array.isArray(t)?B.call(t):t),r):e},r}function J(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e<n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e<i||void 0===e&&i>=i)&&(e=i)}return e}function tt(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e<n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e<o||void 0===e&&o>=o)&&(e=o,r=i);return r}function nt(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function et(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e>n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e>o||void 0===e&&o>=o)&&(e=o,r=i);return r}function rt(t,n,e=0,r=1/0,i){if(n=Math.floor(n),e=Math.floor(Math.max(0,e)),r=Math.floor(Math.min(t.length-1,r)),!(e<=n&&n<=r))return t;for(i=void 0===i?O:I(i);r>e;){if(r-e>600){const o=r-e+1,a=n-e+1,u=Math.log(o),c=.5*Math.exp(2*u/3),f=.5*Math.sqrt(u*c*(o-c)/o)*(a-o/2<0?-1:1);rt(t,n,Math.max(e,Math.floor(n-a*c/o+f)),Math.min(r,Math.floor(n+(o-a)*c/o+f)),i)}const o=t[n];let a=e,u=r;for(it(t,e,n),i(t[r],o)>0&&it(t,e,r);a<u;){for(it(t,a,u),++a,--u;i(t[a],o)<0;)++a;for(;i(t[u],o)>0;)--u}0===i(t[e],o)?it(t,e,u):(++u,it(t,u,r)),u<=n&&(e=u+1),n<=u&&(r=u-1)}return t}function it(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function ot(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)>0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)>0:0===e(n,n))&&(r=n,i=!0);return r}function at(t,n,e){if(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r=+r)>=r&&(yield r)}}(t,e)),(r=t.length)&&!isNaN(n=+n)){if(n<=0||r<2)return nt(t);if(n>=1)return J(t);var r,i=(r-1)*n,o=Math.floor(i),a=J(rt(t,o).subarray(0,o+1));return a+(nt(t.subarray(o+1))-a)*(i-o)}}function ut(t,n,e=o){if((r=t.length)&&!isNaN(n=+n)){if(n<=0||r<2)return+e(t[0],0,t);if(n>=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,a=Math.floor(i),u=+e(t[a],a,t);return u+(+e(t[a+1],a+1,t)-u)*(i-a)}}function ct(t,n,e=o){if(!isNaN(n=+n)){if(r=Float64Array.from(t,((n,r)=>o(e(t[r],r,t)))),n<=0)return et(r);if(n>=1)return tt(r);var r,i=Uint32Array.from(t,((t,n)=>n)),a=r.length-1,u=Math.floor(a*n);return rt(i,u,0,a,((t,n)=>O(r[t],r[n]))),(u=ot(i.subarray(0,u+1),(t=>r[t])))>=0?u:-1}}function ft(t){return Array.from(function*(t){for(const n of t)yield*n}(t))}function st(t,n){return[t,n]}function lt(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r<i;)o[r]=t+r*e;return o}function ht(t,e=n){if(1===e.length)return et(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)<0)&&(r=n,i=o);return i}var dt=pt(Math.random);function pt(t){return function(n,e=0,r=n.length){let i=r-(e=+e);for(;i;){const r=t()*i--|0,o=n[i+e];n[i+e]=n[r+e],n[r+e]=o}return n}}function gt(t){if(!(i=t.length))return[];for(var n=-1,e=nt(t,yt),r=new Array(e);++n<e;)for(var i,o=-1,a=r[n]=new Array(i);++o<i;)a[o]=t[o][n];return r}function yt(t){return t.length}function vt(t){return t instanceof InternSet?t:new InternSet(t)}function _t(t,n){const e=t[Symbol.iterator](),r=new Set;for(const t of n){const n=bt(t);if(r.has(n))continue;let i,o;for(;({value:i,done:o}=e.next());){if(o)return!1;const t=bt(i);if(r.add(t),Object.is(n,t))break}}return!0}function bt(t){return null!==t&&"object"==typeof t?t.valueOf():t}function mt(t){return t}var xt=1,wt=2,Mt=3,Tt=4,At=1e-6;function St(t){return"translate("+t+",0)"}function Et(t){return"translate(0,"+t+")"}function Nt(t){return n=>+t(n)}function kt(t,n){return n=Math.max(0,t.bandwidth()-2*n)/2,t.round()&&(n=Math.round(n)),e=>+t(e)+n}function Ct(){return!this.__axis}function Pt(t,n){var e=[],r=null,i=null,o=6,a=6,u=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,f=t===xt||t===Tt?-1:1,s=t===Tt||t===wt?"x":"y",l=t===xt||t===Mt?St:Et;function h(h){var d=null==r?n.ticks?n.ticks.apply(n,e):n.domain():r,p=null==i?n.tickFormat?n.tickFormat.apply(n,e):mt:i,g=Math.max(o,0)+u,y=n.range(),v=+y[0]+c,_=+y[y.length-1]+c,b=(n.bandwidth?kt:Nt)(n.copy(),c),m=h.selection?h.selection():h,x=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(d,n).order(),M=w.exit(),T=w.enter().append("g").attr("class","tick"),A=w.select("line"),S=w.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),w=w.merge(T),A=A.merge(T.append("line").attr("stroke","currentColor").attr(s+"2",f*o)),S=S.merge(T.append("text").attr("fill","currentColor").attr(s,f*g).attr("dy",t===xt?"0em":t===Mt?"0.71em":"0.32em")),h!==m&&(x=x.transition(h),w=w.transition(h),A=A.transition(h),S=S.transition(h),M=M.transition(h).attr("opacity",At).attr("transform",(function(t){return isFinite(t=b(t))?l(t+c):this.getAttribute("transform")})),T.attr("opacity",At).attr("transform",(function(t){var n=this.parentNode.__axis;return l((n&&isFinite(n=n(t))?n:b(t))+c)}))),M.remove(),x.attr("d",t===Tt||t===wt?a?"M"+f*a+","+v+"H"+c+"V"+_+"H"+f*a:"M"+c+","+v+"V"+_:a?"M"+v+","+f*a+"V"+c+"H"+_+"V"+f*a:"M"+v+","+c+"H"+_),w.attr("opacity",1).attr("transform",(function(t){return l(b(t)+c)})),A.attr(s+"2",f*o),S.attr(s,f*g).text(p),m.filter(Ct).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===wt?"start":t===Tt?"end":"middle"),m.each((function(){this.__axis=b}))}return h.scale=function(t){return arguments.length?(n=t,h):n},h.ticks=function(){return e=Array.from(arguments),h},h.tickArguments=function(t){return arguments.length?(e=null==t?[]:Array.from(t),h):e.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:Array.from(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(o=a=+t,h):o},h.tickSizeInner=function(t){return arguments.length?(o=+t,h):o},h.tickSizeOuter=function(t){return arguments.length?(a=+t,h):a},h.tickPadding=function(t){return arguments.length?(u=+t,h):u},h.offset=function(t){return arguments.length?(c=+t,h):c},h}var zt={value:()=>{}};function $t(){for(var t,n=0,e=arguments.length,r={};n<e;++n){if(!(t=arguments[n]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new Dt(r)}function Dt(t){this._=t}function Rt(t,n){for(var e,r=0,i=t.length;r<i;++r)if((e=t[r]).name===n)return e.value}function Ft(t,n,e){for(var r=0,i=t.length;r<i;++r)if(t[r].name===n){t[r]=zt,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=e&&t.push({name:n,value:e}),t}Dt.prototype=$t.prototype={constructor:Dt,on:function(t,n){var e,r,i=this._,o=(r=i,(t+"").trim().split(/^|\s+/).map((function(t){var n="",e=t.indexOf(".");if(e>=0&&(n=t.slice(e+1),t=t.slice(0,e)),t&&!r.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))),a=-1,u=o.length;if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++a<u;)if(e=(t=o[a]).type)i[e]=Ft(i[e],t.name,n);else if(null==n)for(e in i)i[e]=Ft(i[e],t.name,null);return this}for(;++a<u;)if((e=(t=o[a]).type)&&(e=Rt(i[e],t.name)))return e},copy:function(){var t={},n=this._;for(var e in n)t[e]=n[e].slice();return new Dt(t)},call:function(t,n){if((e=arguments.length-2)>0)for(var e,r,i=new Array(e),o=0;o<e;++o)i[o]=arguments[o+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(o=0,e=(r=this._[t]).length;o<e;++o)r[o].value.apply(n,i)},apply:function(t,n,e){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,o=r.length;i<o;++i)r[i].value.apply(n,e)}};var qt="http://www.w3.org/1999/xhtml",Ut={svg:"http://www.w3.org/2000/svg",xhtml:qt,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function It(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Ut.hasOwnProperty(n)?{space:Ut[n],local:t}:t}function Ot(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===qt&&n.documentElement.namespaceURI===qt?n.createElement(t):n.createElementNS(e,t)}}function Bt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Yt(t){var n=It(t);return(n.local?Bt:Ot)(n)}function Lt(){}function jt(t){return null==t?Lt:function(){return this.querySelector(t)}}function Ht(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Xt(){return[]}function Gt(t){return null==t?Xt:function(){return this.querySelectorAll(t)}}function Vt(t){return function(){return this.matches(t)}}function Wt(t){return function(n){return n.matches(t)}}var Zt=Array.prototype.find;function Kt(){return this.firstElementChild}var Qt=Array.prototype.filter;function Jt(){return Array.from(this.children)}function tn(t){return new Array(t.length)}function nn(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function en(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;u<f;++u)(a=n[u])?(a.__data__=o[u],r[u]=a):e[u]=new nn(t,o[u]);for(;u<c;++u)(a=n[u])&&(i[u]=a)}function rn(t,n,e,r,i,o,a){var u,c,f,s=new Map,l=n.length,h=o.length,d=new Array(l);for(u=0;u<l;++u)(c=n[u])&&(d[u]=f=a.call(c,c.__data__,u,n)+"",s.has(f)?i[u]=c:s.set(f,c));for(u=0;u<h;++u)f=a.call(t,o[u],u,o)+"",(c=s.get(f))?(r[u]=c,c.__data__=o[u],s.delete(f)):e[u]=new nn(t,o[u]);for(u=0;u<l;++u)(c=n[u])&&s.get(d[u])===c&&(i[u]=c)}function on(t){return t.__data__}function an(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function un(t,n){return t<n?-1:t>n?1:t>=n?0:NaN}function cn(t){return function(){this.removeAttribute(t)}}function fn(t){return function(){this.removeAttributeNS(t.space,t.local)}}function sn(t,n){return function(){this.setAttribute(t,n)}}function ln(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function hn(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function dn(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function pn(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function gn(t){return function(){this.style.removeProperty(t)}}function yn(t,n,e){return function(){this.style.setProperty(t,n,e)}}function vn(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function _n(t,n){return t.style.getPropertyValue(n)||pn(t).getComputedStyle(t,null).getPropertyValue(n)}function bn(t){return function(){delete this[t]}}function mn(t,n){return function(){this[t]=n}}function xn(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function wn(t){return t.trim().split(/^|\s+/)}function Mn(t){return t.classList||new Tn(t)}function Tn(t){this._node=t,this._names=wn(t.getAttribute("class")||"")}function An(t,n){for(var e=Mn(t),r=-1,i=n.length;++r<i;)e.add(n[r])}function Sn(t,n){for(var e=Mn(t),r=-1,i=n.length;++r<i;)e.remove(n[r])}function En(t){return function(){An(this,t)}}function Nn(t){return function(){Sn(this,t)}}function kn(t,n){return function(){(n.apply(this,arguments)?An:Sn)(this,t)}}function Cn(){this.textContent=""}function Pn(t){return function(){this.textContent=t}}function zn(t){return function(){var n=t.apply(this,arguments);this.textContent=null==n?"":n}}function $n(){this.innerHTML=""}function Dn(t){return function(){this.innerHTML=t}}function Rn(t){return function(){var n=t.apply(this,arguments);this.innerHTML=null==n?"":n}}function Fn(){this.nextSibling&&this.parentNode.appendChild(this)}function qn(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function Un(){return null}function In(){var t=this.parentNode;t&&t.removeChild(this)}function On(){var t=this.cloneNode(!1),n=this.parentNode;return n?n.insertBefore(t,this.nextSibling):t}function Bn(){var t=this.cloneNode(!0),n=this.parentNode;return n?n.insertBefore(t,this.nextSibling):t}function Yn(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r<o;++r)e=n[r],t.type&&e.type!==t.type||e.name!==t.name?n[++i]=e:this.removeEventListener(e.type,e.listener,e.options);++i?n.length=i:delete this.__on}}}function Ln(t,n,e){return function(){var r,i=this.__on,o=function(t){return function(n){t.call(this,n,this.__data__)}}(n);if(i)for(var a=0,u=i.length;a<u;++a)if((r=i[a]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=o,r.options=e),void(r.value=n);this.addEventListener(t.type,o,e),r={type:t.type,name:t.name,value:n,listener:o,options:e},i?i.push(r):this.__on=[r]}}function jn(t,n,e){var r=pn(t),i=r.CustomEvent;"function"==typeof i?i=new i(n,e):(i=r.document.createEvent("Event"),e?(i.initEvent(n,e.bubbles,e.cancelable),i.detail=e.detail):i.initEvent(n,!1,!1)),t.dispatchEvent(i)}function Hn(t,n){return function(){return jn(this,t,n)}}function Xn(t,n){return function(){return jn(this,t,n.apply(this,arguments))}}nn.prototype={constructor:nn,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}},Tn.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var n=this._names.indexOf(t);n>=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Gn=[null];function Vn(t,n){this._groups=t,this._parents=n}function Wn(){return new Vn([[document.documentElement]],Gn)}function Zn(t){return"string"==typeof t?new Vn([[document.querySelector(t)]],[document.documentElement]):new Vn([[t]],Gn)}Vn.prototype=Wn.prototype={constructor:Vn,select:function(t){"function"!=typeof t&&(t=jt(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i<e;++i)for(var o,a,u=n[i],c=u.length,f=r[i]=new Array(c),s=0;s<c;++s)(o=u[s])&&(a=t.call(o,o.__data__,s,u))&&("__data__"in o&&(a.__data__=o.__data__),f[s]=a);return new Vn(r,this._parents)},selectAll:function(t){t="function"==typeof t?function(t){return function(){return Ht(t.apply(this,arguments))}}(t):Gt(t);for(var n=this._groups,e=n.length,r=[],i=[],o=0;o<e;++o)for(var a,u=n[o],c=u.length,f=0;f<c;++f)(a=u[f])&&(r.push(t.call(a,a.__data__,f,u)),i.push(a));return new Vn(r,i)},selectChild:function(t){return this.select(null==t?Kt:function(t){return function(){return Zt.call(this.children,t)}}("function"==typeof t?t:Wt(t)))},selectChildren:function(t){return this.selectAll(null==t?Jt:function(t){return function(){return Qt.call(this.children,t)}}("function"==typeof t?t:Wt(t)))},filter:function(t){"function"!=typeof t&&(t=Vt(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i<e;++i)for(var o,a=n[i],u=a.length,c=r[i]=[],f=0;f<u;++f)(o=a[f])&&t.call(o,o.__data__,f,a)&&c.push(o);return new Vn(r,this._parents)},data:function(t,n){if(!arguments.length)return Array.from(this,on);var e=n?rn:en,r=this._parents,i=this._groups;"function"!=typeof t&&(t=function(t){return function(){return t}}(t));for(var o=i.length,a=new Array(o),u=new Array(o),c=new Array(o),f=0;f<o;++f){var s=r[f],l=i[f],h=l.length,d=an(t.call(s,s&&s.__data__,f,r)),p=d.length,g=u[f]=new Array(p),y=a[f]=new Array(p);e(s,l,g,y,c[f]=new Array(h),d,n);for(var v,_,b=0,m=0;b<p;++b)if(v=g[b]){for(b>=m&&(m=b+1);!(_=y[m])&&++m<p;);v._next=_||null}}return(a=new Vn(a,r))._enter=u,a._exit=c,a},enter:function(){return new Vn(this._enter||this._groups.map(tn),this._parents)},exit:function(){return new Vn(this._exit||this._groups.map(tn),this._parents)},join:function(t,n,e){var r=this.enter(),i=this,o=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=n&&(i=n(i))&&(i=i.selection()),null==e?o.remove():e(o),r&&i?r.merge(i).order():i},merge:function(t){for(var n=t.selection?t.selection():t,e=this._groups,r=n._groups,i=e.length,o=r.length,a=Math.min(i,o),u=new Array(i),c=0;c<a;++c)for(var f,s=e[c],l=r[c],h=s.length,d=u[c]=new Array(h),p=0;p<h;++p)(f=s[p]||l[p])&&(d[p]=f);for(;c<i;++c)u[c]=e[c];return new Vn(u,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,n=-1,e=t.length;++n<e;)for(var r,i=t[n],o=i.length-1,a=i[o];--o>=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=un);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o<r;++o){for(var a,u=e[o],c=u.length,f=i[o]=new Array(c),s=0;s<c;++s)(a=u[s])&&(f[s]=a);f.sort(n)}return new Vn(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,n=0,e=t.length;n<e;++n)for(var r=t[n],i=0,o=r.length;i<o;++i){var a=r[i];if(a)return a}return null},size:function(){let t=0;for(const n of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var n=this._groups,e=0,r=n.length;e<r;++e)for(var i,o=n[e],a=0,u=o.length;a<u;++a)(i=o[a])&&t.call(i,i.__data__,a,o);return this},attr:function(t,n){var e=It(t);if(arguments.length<2){var r=this.node();return e.local?r.getAttributeNS(e.space,e.local):r.getAttribute(e)}return this.each((null==n?e.local?fn:cn:"function"==typeof n?e.local?dn:hn:e.local?ln:sn)(e,n))},style:function(t,n,e){return arguments.length>1?this.each((null==n?gn:"function"==typeof n?vn:yn)(t,n,null==e?"":e)):_n(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?bn:"function"==typeof n?xn:mn)(t,n)):this.node()[t]},classed:function(t,n){var e=wn(t+"");if(arguments.length<2){for(var r=Mn(this.node()),i=-1,o=e.length;++i<o;)if(!r.contains(e[i]))return!1;return!0}return this.each(("function"==typeof n?kn:n?En:Nn)(e,n))},text:function(t){return arguments.length?this.each(null==t?Cn:("function"==typeof t?zn:Pn)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?$n:("function"==typeof t?Rn:Dn)(t)):this.node().innerHTML},raise:function(){return this.each(Fn)},lower:function(){return this.each(qn)},append:function(t){var n="function"==typeof t?t:Yt(t);return this.select((function(){return this.appendChild(n.apply(this,arguments))}))},insert:function(t,n){var e="function"==typeof t?t:Yt(t),r=null==n?Un:"function"==typeof n?n:jt(n);return this.select((function(){return this.insertBefore(e.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(In)},clone:function(t){return this.select(t?Bn:On)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,n,e){var r,i,o=function(t){return t.trim().split(/^|\s+/).map((function(t){var n="",e=t.indexOf(".");return e>=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}(t+""),a=o.length;if(!(arguments.length<2)){for(u=n?Ln:Yn,r=0;r<a;++r)this.each(u(o[r],n,e));return this}var u=this.node().__on;if(u)for(var c,f=0,s=u.length;f<s;++f)for(r=0,c=u[f];r<a;++r)if((i=o[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,n){return this.each(("function"==typeof n?Xn:Hn)(t,n))},[Symbol.iterator]:function*(){for(var t=this._groups,n=0,e=t.length;n<e;++n)for(var r,i=t[n],o=0,a=i.length;o<a;++o)(r=i[o])&&(yield r)}};var Kn=0;function Qn(){return new Jn}function Jn(){this._="@"+(++Kn).toString(36)}function te(t){let n;for(;n=t.sourceEvent;)t=n;return t}function ne(t,n){if(t=te(t),void 0===n&&(n=t.currentTarget),n){var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();return r.x=t.clientX,r.y=t.clientY,[(r=r.matrixTransform(n.getScreenCTM().inverse())).x,r.y]}if(n.getBoundingClientRect){var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}}return[t.pageX,t.pageY]}Jn.prototype=Qn.prototype={constructor:Jn,get:function(t){for(var n=this._;!(n in t);)if(!(t=t.parentNode))return;return t[n]},set:function(t,n){return t[this._]=n},remove:function(t){return this._ in t&&delete t[this._]},toString:function(){return this._}};const ee={passive:!1},re={capture:!0,passive:!1};function ie(t){t.stopImmediatePropagation()}function oe(t){t.preventDefault(),t.stopImmediatePropagation()}function ae(t){var n=t.document.documentElement,e=Zn(t).on("dragstart.drag",oe,re);"onselectstart"in n?e.on("selectstart.drag",oe,re):(n.__noselect=n.style.MozUserSelect,n.style.MozUserSelect="none")}function ue(t,n){var e=t.document.documentElement,r=Zn(t).on("dragstart.drag",null);n&&(r.on("click.drag",oe,re),setTimeout((function(){r.on("click.drag",null)}),0)),"onselectstart"in e?r.on("selectstart.drag",null):(e.style.MozUserSelect=e.__noselect,delete e.__noselect)}var ce=t=>()=>t;function fe(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:c,dy:f,dispatch:s}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:s}})}function se(t){return!t.ctrlKey&&!t.button}function le(){return this.parentNode}function he(t,n){return null==n?{x:t.x,y:t.y}:n}function de(){return navigator.maxTouchPoints||"ontouchstart"in this}function pe(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function ge(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function ye(){}fe.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var ve=.7,_e=1/ve,be="\\s*([+-]?\\d+)\\s*",me="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",xe="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",we=/^#([0-9a-f]{3,8})$/,Me=new RegExp(`^rgb\\(${be},${be},${be}\\)$`),Te=new RegExp(`^rgb\\(${xe},${xe},${xe}\\)$`),Ae=new RegExp(`^rgba\\(${be},${be},${be},${me}\\)$`),Se=new RegExp(`^rgba\\(${xe},${xe},${xe},${me}\\)$`),Ee=new RegExp(`^hsl\\(${me},${xe},${xe}\\)$`),Ne=new RegExp(`^hsla\\(${me},${xe},${xe},${me}\\)$`),ke={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function Ce(){return this.rgb().formatHex()}function Pe(){return this.rgb().formatRgb()}function ze(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=we.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?$e(n):3===e?new qe(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?De(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?De(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=Me.exec(t))?new qe(n[1],n[2],n[3],1):(n=Te.exec(t))?new qe(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=Ae.exec(t))?De(n[1],n[2],n[3],n[4]):(n=Se.exec(t))?De(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=Ee.exec(t))?Le(n[1],n[2]/100,n[3]/100,1):(n=Ne.exec(t))?Le(n[1],n[2]/100,n[3]/100,n[4]):ke.hasOwnProperty(t)?$e(ke[t]):"transparent"===t?new qe(NaN,NaN,NaN,0):null}function $e(t){return new qe(t>>16&255,t>>8&255,255&t,1)}function De(t,n,e,r){return r<=0&&(t=n=e=NaN),new qe(t,n,e,r)}function Re(t){return t instanceof ye||(t=ze(t)),t?new qe((t=t.rgb()).r,t.g,t.b,t.opacity):new qe}function Fe(t,n,e,r){return 1===arguments.length?Re(t):new qe(t,n,e,null==r?1:r)}function qe(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Ue(){return`#${Ye(this.r)}${Ye(this.g)}${Ye(this.b)}`}function Ie(){const t=Oe(this.opacity);return`${1===t?"rgb(":"rgba("}${Be(this.r)}, ${Be(this.g)}, ${Be(this.b)}${1===t?")":`, ${t})`}`}function Oe(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function Be(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function Ye(t){return((t=Be(t))<16?"0":"")+t.toString(16)}function Le(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Xe(t,n,e,r)}function je(t){if(t instanceof Xe)return new Xe(t.h,t.s,t.l,t.opacity);if(t instanceof ye||(t=ze(t)),!t)return new Xe;if(t instanceof Xe)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e<r):e===o?(r-n)/u+2:(n-e)/u+4,u/=c<.5?o+i:2-o-i,a*=60):u=c>0&&c<1?0:a,new Xe(a,u,c,t.opacity)}function He(t,n,e,r){return 1===arguments.length?je(t):new Xe(t,n,e,null==r?1:r)}function Xe(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Ge(t){return(t=(t||0)%360)<0?t+360:t}function Ve(t){return Math.max(0,Math.min(1,t||0))}function We(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}pe(ye,ze,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:Ce,formatHex:Ce,formatHex8:function(){return this.rgb().formatHex8()},formatHsl:function(){return je(this).formatHsl()},formatRgb:Pe,toString:Pe}),pe(qe,Fe,ge(ye,{brighter(t){return t=null==t?_e:Math.pow(_e,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=null==t?ve:Math.pow(ve,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new qe(Be(this.r),Be(this.g),Be(this.b),Oe(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Ue,formatHex:Ue,formatHex8:function(){return`#${Ye(this.r)}${Ye(this.g)}${Ye(this.b)}${Ye(255*(isNaN(this.opacity)?1:this.opacity))}`},formatRgb:Ie,toString:Ie})),pe(Xe,He,ge(ye,{brighter(t){return t=null==t?_e:Math.pow(_e,t),new Xe(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=null==t?ve:Math.pow(ve,t),new Xe(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new qe(We(t>=240?t-240:t+120,i,r),We(t,i,r),We(t<120?t+240:t-120,i,r),this.opacity)},clamp(){return new Xe(Ge(this.h),Ve(this.s),Ve(this.l),Oe(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=Oe(this.opacity);return`${1===t?"hsl(":"hsla("}${Ge(this.h)}, ${100*Ve(this.s)}%, ${100*Ve(this.l)}%${1===t?")":`, ${t})`}`}}));const Ze=Math.PI/180,Ke=180/Math.PI,Qe=.96422,Je=1,tr=.82521,nr=4/29,er=6/29,rr=3*er*er,ir=er*er*er;function or(t){if(t instanceof ur)return new ur(t.l,t.a,t.b,t.opacity);if(t instanceof pr)return gr(t);t instanceof qe||(t=Re(t));var n,e,r=lr(t.r),i=lr(t.g),o=lr(t.b),a=cr((.2225045*r+.7168786*i+.0606169*o)/Je);return r===i&&i===o?n=e=a:(n=cr((.4360747*r+.3850649*i+.1430804*o)/Qe),e=cr((.0139322*r+.0971045*i+.7141733*o)/tr)),new ur(116*a-16,500*(n-a),200*(a-e),t.opacity)}function ar(t,n,e,r){return 1===arguments.length?or(t):new ur(t,n,e,null==r?1:r)}function ur(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function cr(t){return t>ir?Math.pow(t,1/3):t/rr+nr}function fr(t){return t>er?t*t*t:rr*(t-nr)}function sr(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function lr(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function hr(t){if(t instanceof pr)return new pr(t.h,t.c,t.l,t.opacity);if(t instanceof ur||(t=or(t)),0===t.a&&0===t.b)return new pr(NaN,0<t.l&&t.l<100?0:NaN,t.l,t.opacity);var n=Math.atan2(t.b,t.a)*Ke;return new pr(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function dr(t,n,e,r){return 1===arguments.length?hr(t):new pr(t,n,e,null==r?1:r)}function pr(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}function gr(t){if(isNaN(t.h))return new ur(t.l,0,0,t.opacity);var n=t.h*Ze;return new ur(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}pe(ur,ar,ge(ye,{brighter(t){return new ur(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker(t){return new ur(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return new qe(sr(3.1338561*(n=Qe*fr(n))-1.6168667*(t=Je*fr(t))-.4906146*(e=tr*fr(e))),sr(-.9787684*n+1.9161415*t+.033454*e),sr(.0719453*n-.2289914*t+1.4052427*e),this.opacity)}})),pe(pr,dr,ge(ye,{brighter(t){return new pr(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker(t){return new pr(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb(){return gr(this).rgb()}}));var yr=-.14861,vr=1.78277,_r=-.29227,br=-.90649,mr=1.97294,xr=mr*br,wr=mr*vr,Mr=vr*_r-br*yr;function Tr(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Ar)return new Ar(t.h,t.s,t.l,t.opacity);t instanceof qe||(t=Re(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(Mr*r+xr*n-wr*e)/(Mr+xr-wr),o=r-i,a=(mr*(e-i)-_r*o)/br,u=Math.sqrt(a*a+o*o)/(mr*i*(1-i)),c=u?Math.atan2(a,o)*Ke-120:NaN;return new Ar(c<0?c+360:c,u,i,t.opacity)}(t):new Ar(t,n,e,null==r?1:r)}function Ar(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Sr(t,n,e,r,i){var o=t*t,a=o*t;return((1-3*t+3*o-a)*n+(4-6*o+3*a)*e+(1+3*t+3*o-3*a)*r+a*i)/6}function Er(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r<n-1?t[r+2]:2*o-i;return Sr((e-r/n)*n,a,i,o,u)}}function Nr(t){var n=t.length;return function(e){var r=Math.floor(((e%=1)<0?++e:e)*n),i=t[(r+n-1)%n],o=t[r%n],a=t[(r+1)%n],u=t[(r+2)%n];return Sr((e-r/n)*n,i,o,a,u)}}pe(Ar,Tr,ge(ye,{brighter(t){return t=null==t?_e:Math.pow(_e,t),new Ar(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=null==t?ve:Math.pow(ve,t),new Ar(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=isNaN(this.h)?0:(this.h+120)*Ze,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new qe(255*(n+e*(yr*r+vr*i)),255*(n+e*(_r*r+br*i)),255*(n+e*(mr*r)),this.opacity)}}));var kr=t=>()=>t;function Cr(t,n){return function(e){return t+e*n}}function Pr(t,n){var e=n-t;return e?Cr(t,e>180||e<-180?e-360*Math.round(e/360):e):kr(isNaN(t)?n:t)}function zr(t){return 1==(t=+t)?$r:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):kr(isNaN(n)?e:n)}}function $r(t,n){var e=n-t;return e?Cr(t,e):kr(isNaN(t)?n:t)}var Dr=function t(n){var e=zr(n);function r(t,n){var r=e((t=Fe(t)).r,(n=Fe(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=$r(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function Rr(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;e<i;++e)r=Fe(n[e]),o[e]=r.r||0,a[e]=r.g||0,u[e]=r.b||0;return o=t(o),a=t(a),u=t(u),r.opacity=1,function(t){return r.r=o(t),r.g=a(t),r.b=u(t),r+""}}}var Fr=Rr(Er),qr=Rr(Nr);function Ur(t,n){n||(n=[]);var e,r=t?Math.min(n.length,t.length):0,i=n.slice();return function(o){for(e=0;e<r;++e)i[e]=t[e]*(1-o)+n[e]*o;return i}}function Ir(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}function Or(t,n){var e,r=n?n.length:0,i=t?Math.min(r,t.length):0,o=new Array(i),a=new Array(r);for(e=0;e<i;++e)o[e]=Gr(t[e],n[e]);for(;e<r;++e)a[e]=n[e];return function(t){for(e=0;e<i;++e)a[e]=o[e](t);return a}}function Br(t,n){var e=new Date;return t=+t,n=+n,function(r){return e.setTime(t*(1-r)+n*r),e}}function Yr(t,n){return t=+t,n=+n,function(e){return t*(1-e)+n*e}}function Lr(t,n){var e,r={},i={};for(e in null!==t&&"object"==typeof t||(t={}),null!==n&&"object"==typeof n||(n={}),n)e in t?r[e]=Gr(t[e],n[e]):i[e]=n[e];return function(t){for(e in r)i[e]=r[e](t);return i}}var jr=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Hr=new RegExp(jr.source,"g");function Xr(t,n){var e,r,i,o=jr.lastIndex=Hr.lastIndex=0,a=-1,u=[],c=[];for(t+="",n+="";(e=jr.exec(t))&&(r=Hr.exec(n));)(i=r.index)>o&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:Yr(e,r)})),o=Hr.lastIndex;return o<n.length&&(i=n.slice(o),u[a]?u[a]+=i:u[++a]=i),u.length<2?c[0]?function(t){return function(n){return t(n)+""}}(c[0].x):function(t){return function(){return t}}(n):(n=c.length,function(t){for(var e,r=0;r<n;++r)u[(e=c[r]).i]=e.x(t);return u.join("")})}function Gr(t,n){var e,r=typeof n;return null==n||"boolean"===r?kr(n):("number"===r?Yr:"string"===r?(e=ze(n))?(n=e,Dr):Xr:n instanceof ze?Dr:n instanceof Date?Br:Ir(n)?Ur:Array.isArray(n)?Or:"function"!=typeof n.valueOf&&"function"!=typeof n.toString||isNaN(n)?Lr:Yr)(t,n)}function Vr(t,n){return t=+t,n=+n,function(e){return Math.round(t*(1-e)+n*e)}}var Wr,Zr=180/Math.PI,Kr={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Qr(t,n,e,r,i,o){var a,u,c;return(a=Math.sqrt(t*t+n*n))&&(t/=a,n/=a),(c=t*e+n*r)&&(e-=t*c,r-=n*c),(u=Math.sqrt(e*e+r*r))&&(e/=u,r/=u,c/=u),t*r<n*e&&(t=-t,n=-n,c=-c,a=-a),{translateX:i,translateY:o,rotate:Math.atan2(n,t)*Zr,skewX:Math.atan(c)*Zr,scaleX:a,scaleY:u}}function Jr(t,n,e,r){function i(t){return t.length?t.pop()+" ":""}return function(o,a){var u=[],c=[];return o=t(o),a=t(a),function(t,r,i,o,a,u){if(t!==i||r!==o){var c=a.push("translate(",null,n,null,e);u.push({i:c-4,x:Yr(t,i)},{i:c-2,x:Yr(r,o)})}else(i||o)&&a.push("translate("+i+n+o+e)}(o.translateX,o.translateY,a.translateX,a.translateY,u,c),function(t,n,e,o){t!==n?(t-n>180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Yr(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Yr(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:Yr(t,e)},{i:u-2,x:Yr(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e<r;)u[(n=c[e]).i]=n.x(t);return u.join("")}}}var ti=Jr((function(t){const n=new("function"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+"");return n.isIdentity?Kr:Qr(n.a,n.b,n.c,n.d,n.e,n.f)}),"px, ","px)","deg)"),ni=Jr((function(t){return null==t?Kr:(Wr||(Wr=document.createElementNS("http://www.w3.org/2000/svg","g")),Wr.setAttribute("transform",t),(t=Wr.transform.baseVal.consolidate())?Qr((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):Kr)}),", ",")",")");function ei(t){return((t=Math.exp(t))+1/t)/2}var ri=function t(n,e,r){function i(t,i){var o,a,u=t[0],c=t[1],f=t[2],s=i[0],l=i[1],h=i[2],d=s-u,p=l-c,g=d*d+p*p;if(g<1e-12)a=Math.log(h/f)/n,o=function(t){return[u+t*d,c+t*p,f*Math.exp(n*t*a)]};else{var y=Math.sqrt(g),v=(h*h-f*f+r*g)/(2*f*e*y),_=(h*h-f*f-r*g)/(2*h*e*y),b=Math.log(Math.sqrt(v*v+1)-v),m=Math.log(Math.sqrt(_*_+1)-_);a=(m-b)/n,o=function(t){var r=t*a,i=ei(b),o=f/(e*y)*(i*function(t){return((t=Math.exp(2*t))-1)/(t+1)}(n*r+b)-function(t){return((t=Math.exp(t))-1/t)/2}(b));return[u+o*d,c+o*p,f*i/ei(n*r+b)]}}return o.duration=1e3*a*n/Math.SQRT2,o}return i.rho=function(n){var e=Math.max(.001,+n),r=e*e;return t(e,r,r*r)},i}(Math.SQRT2,2,4);function ii(t){return function(n,e){var r=t((n=He(n)).h,(e=He(e)).h),i=$r(n.s,e.s),o=$r(n.l,e.l),a=$r(n.opacity,e.opacity);return function(t){return n.h=r(t),n.s=i(t),n.l=o(t),n.opacity=a(t),n+""}}}var oi=ii(Pr),ai=ii($r);function ui(t){return function(n,e){var r=t((n=dr(n)).h,(e=dr(e)).h),i=$r(n.c,e.c),o=$r(n.l,e.l),a=$r(n.opacity,e.opacity);return function(t){return n.h=r(t),n.c=i(t),n.l=o(t),n.opacity=a(t),n+""}}}var ci=ui(Pr),fi=ui($r);function si(t){return function n(e){function r(n,r){var i=t((n=Tr(n)).h,(r=Tr(r)).h),o=$r(n.s,r.s),a=$r(n.l,r.l),u=$r(n.opacity,r.opacity);return function(t){return n.h=i(t),n.s=o(t),n.l=a(Math.pow(t,e)),n.opacity=u(t),n+""}}return e=+e,r.gamma=n,r}(1)}var li=si(Pr),hi=si($r);function di(t,n){void 0===n&&(n=t,t=Gr);for(var e=0,r=n.length-1,i=n[0],o=new Array(r<0?0:r);e<r;)o[e]=t(i,i=n[++e]);return function(t){var n=Math.max(0,Math.min(r-1,Math.floor(t*=r)));return o[n](t-n)}}var pi,gi,yi=0,vi=0,_i=0,bi=1e3,mi=0,xi=0,wi=0,Mi="object"==typeof performance&&performance.now?performance:Date,Ti="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function Ai(){return xi||(Ti(Si),xi=Mi.now()+wi)}function Si(){xi=0}function Ei(){this._call=this._time=this._next=null}function Ni(t,n,e){var r=new Ei;return r.restart(t,n,e),r}function ki(){Ai(),++yi;for(var t,n=pi;n;)(t=xi-n._time)>=0&&n._call.call(void 0,t),n=n._next;--yi}function Ci(){xi=(mi=Mi.now())+wi,yi=vi=0;try{ki()}finally{yi=0,function(){var t,n,e=pi,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:pi=n);gi=t,zi(r)}(),xi=0}}function Pi(){var t=Mi.now(),n=t-mi;n>bi&&(wi-=n,mi=t)}function zi(t){yi||(vi&&(vi=clearTimeout(vi)),t-xi>24?(t<1/0&&(vi=setTimeout(Ci,t-Mi.now()-wi)),_i&&(_i=clearInterval(_i))):(_i||(mi=Mi.now(),_i=setInterval(Pi,bi)),yi=1,Ti(Ci)))}function $i(t,n,e){var r=new Ei;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}Ei.prototype=Ni.prototype={constructor:Ei,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Ai():+e)+(null==n?0:+n),this._next||gi===this||(gi?gi._next=this:pi=this,gi=this),this._call=t,this._time=e,zi()},stop:function(){this._call&&(this._call=null,this._time=1/0,zi())}};var Di=$t("start","end","cancel","interrupt"),Ri=[],Fi=0,qi=1,Ui=2,Ii=3,Oi=4,Bi=5,Yi=6;function Li(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=qi,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var f,s,l,h;if(e.state!==qi)return c();for(f in i)if((h=i[f]).name===e.name){if(h.state===Ii)return $i(a);h.state===Oi?(h.state=Yi,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+f<n&&(h.state=Yi,h.timer.stop(),h.on.call("cancel",t,t.__data__,h.index,h.group),delete i[f])}if($i((function(){e.state===Ii&&(e.state=Oi,e.timer.restart(u,e.delay,e.time),u(o))})),e.state=Ui,e.on.call("start",t,t.__data__,e.index,e.group),e.state===Ui){for(e.state=Ii,r=new Array(l=e.tween.length),f=0,s=-1;f<l;++f)(h=e.tween[f].value.call(t,t.__data__,e.index,e.group))&&(r[++s]=h);r.length=s+1}}function u(n){for(var i=n<e.duration?e.ease.call(null,n/e.duration):(e.timer.restart(c),e.state=Bi,1),o=-1,a=r.length;++o<a;)r[o].call(t,i);e.state===Bi&&(e.on.call("end",t,t.__data__,e.index,e.group),c())}function c(){for(var r in e.state=Yi,e.timer.stop(),delete i[n],i)return;delete t.__transition}i[n]=e,e.timer=Ni(o,0,e.time)}(t,e,{name:n,index:r,group:i,on:Di,tween:Ri,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:Fi})}function ji(t,n){var e=Xi(t,n);if(e.state>Fi)throw new Error("too late; already scheduled");return e}function Hi(t,n){var e=Xi(t,n);if(e.state>Ii)throw new Error("too late; already running");return e}function Xi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Gi(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>Ui&&e.state<Bi,e.state=Yi,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):a=!1;a&&delete t.__transition}}function Vi(t,n){var e,r;return function(){var i=Hi(this,t),o=i.tween;if(o!==e)for(var a=0,u=(r=e=o).length;a<u;++a)if(r[a].name===n){(r=r.slice()).splice(a,1);break}i.tween=r}}function Wi(t,n,e){var r,i;if("function"!=typeof e)throw new Error;return function(){var o=Hi(this,t),a=o.tween;if(a!==r){i=(r=a).slice();for(var u={name:n,value:e},c=0,f=i.length;c<f;++c)if(i[c].name===n){i[c]=u;break}c===f&&i.push(u)}o.tween=i}}function Zi(t,n,e){var r=t._id;return t.each((function(){var t=Hi(this,r);(t.value||(t.value={}))[n]=e.apply(this,arguments)})),function(t){return Xi(t,r).value[n]}}function Ki(t,n){var e;return("number"==typeof n?Yr:n instanceof ze?Dr:(e=ze(n))?(n=e,Dr):Xr)(t,n)}function Qi(t){return function(){this.removeAttribute(t)}}function Ji(t){return function(){this.removeAttributeNS(t.space,t.local)}}function to(t,n,e){var r,i,o=e+"";return function(){var a=this.getAttribute(t);return a===o?null:a===r?i:i=n(r=a,e)}}function no(t,n,e){var r,i,o=e+"";return function(){var a=this.getAttributeNS(t.space,t.local);return a===o?null:a===r?i:i=n(r=a,e)}}function eo(t,n,e){var r,i,o;return function(){var a,u,c=e(this);if(null!=c)return(a=this.getAttribute(t))===(u=c+"")?null:a===r&&u===i?o:(i=u,o=n(r=a,c));this.removeAttribute(t)}}function ro(t,n,e){var r,i,o;return function(){var a,u,c=e(this);if(null!=c)return(a=this.getAttributeNS(t.space,t.local))===(u=c+"")?null:a===r&&u===i?o:(i=u,o=n(r=a,c));this.removeAttributeNS(t.space,t.local)}}function io(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&function(t,n){return function(e){this.setAttributeNS(t.space,t.local,n.call(this,e))}}(t,i)),e}return i._value=n,i}function oo(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&function(t,n){return function(e){this.setAttribute(t,n.call(this,e))}}(t,i)),e}return i._value=n,i}function ao(t,n){return function(){ji(this,t).delay=+n.apply(this,arguments)}}function uo(t,n){return n=+n,function(){ji(this,t).delay=n}}function co(t,n){return function(){Hi(this,t).duration=+n.apply(this,arguments)}}function fo(t,n){return n=+n,function(){Hi(this,t).duration=n}}var so=Wn.prototype.constructor;function lo(t){return function(){this.style.removeProperty(t)}}var ho=0;function po(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function go(t){return Wn().transition(t)}function yo(){return++ho}var vo=Wn.prototype;po.prototype=go.prototype={constructor:po,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=jt(t));for(var r=this._groups,i=r.length,o=new Array(i),a=0;a<i;++a)for(var u,c,f=r[a],s=f.length,l=o[a]=new Array(s),h=0;h<s;++h)(u=f[h])&&(c=t.call(u,u.__data__,h,f))&&("__data__"in u&&(c.__data__=u.__data__),l[h]=c,Li(l[h],n,e,h,l,Xi(u,e)));return new po(o,this._parents,n,e)},selectAll:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=Gt(t));for(var r=this._groups,i=r.length,o=[],a=[],u=0;u<i;++u)for(var c,f=r[u],s=f.length,l=0;l<s;++l)if(c=f[l]){for(var h,d=t.call(c,c.__data__,l,f),p=Xi(c,e),g=0,y=d.length;g<y;++g)(h=d[g])&&Li(h,n,e,g,d,p);o.push(d),a.push(c)}return new po(o,a,n,e)},selectChild:vo.selectChild,selectChildren:vo.selectChildren,filter:function(t){"function"!=typeof t&&(t=Vt(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i<e;++i)for(var o,a=n[i],u=a.length,c=r[i]=[],f=0;f<u;++f)(o=a[f])&&t.call(o,o.__data__,f,a)&&c.push(o);return new po(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var n=this._groups,e=t._groups,r=n.length,i=e.length,o=Math.min(r,i),a=new Array(r),u=0;u<o;++u)for(var c,f=n[u],s=e[u],l=f.length,h=a[u]=new Array(l),d=0;d<l;++d)(c=f[d]||s[d])&&(h[d]=c);for(;u<r;++u)a[u]=n[u];return new po(a,this._parents,this._name,this._id)},selection:function(){return new so(this._groups,this._parents)},transition:function(){for(var t=this._name,n=this._id,e=yo(),r=this._groups,i=r.length,o=0;o<i;++o)for(var a,u=r[o],c=u.length,f=0;f<c;++f)if(a=u[f]){var s=Xi(a,n);Li(a,t,e,f,u,{time:s.time+s.delay+s.duration,delay:0,duration:s.duration,ease:s.ease})}return new po(r,this._parents,t,e)},call:vo.call,nodes:vo.nodes,node:vo.node,size:vo.size,empty:vo.empty,each:vo.each,on:function(t,n){var e=this._id;return arguments.length<2?Xi(this.node(),e).on.on(t):this.each(function(t,n,e){var r,i,o=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var n=t.indexOf(".");return n>=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?ji:Hi;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}(e,t,n))},attr:function(t,n){var e=It(t),r="transform"===e?ni:Ki;return this.attrTween(t,"function"==typeof n?(e.local?ro:eo)(e,r,Zi(this,"attr."+t,n)):null==n?(e.local?Ji:Qi)(e):(e.local?no:to)(e,r,n))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=It(t);return this.tween(e,(r.local?io:oo)(r,n))},style:function(t,n,e){var r="transform"==(t+="")?ti:Ki;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=_n(this,t),a=(this.style.removeProperty(t),_n(this,t));return o===a?null:o===e&&a===r?i:i=n(e=o,r=a)}}(t,r)).on("end.style."+t,lo(t)):"function"==typeof n?this.styleTween(t,function(t,n,e){var r,i,o;return function(){var a=_n(this,t),u=e(this),c=u+"";return null==u&&(this.style.removeProperty(t),c=u=_n(this,t)),a===c?null:a===r&&c===i?o:(i=c,o=n(r=a,u))}}(t,r,Zi(this,"style."+t,n))).each(function(t,n){var e,r,i,o,a="style."+n,u="end."+a;return function(){var c=Hi(this,t),f=c.on,s=null==c.value[a]?o||(o=lo(n)):void 0;f===e&&i===s||(r=(e=f).copy()).on(u,i=s),c.on=r}}(this._id,t)):this.styleTween(t,function(t,n,e){var r,i,o=e+"";return function(){var a=_n(this,t);return a===o?null:a===r?i:i=n(r=a,e)}}(t,r,n),e).on("end.style."+t,null)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&function(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}(t,o,e)),r}return o._value=n,o}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(Zi(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var n="text";if(arguments.length<1)return(n=this.tween(n))&&n._value;if(null==t)return this.tween(n,null);if("function"!=typeof t)throw new Error;return this.tween(n,function(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&function(t){return function(n){this.textContent=t.call(this,n)}}(r)),n}return r._value=t,r}(t))},remove:function(){return this.on("end.remove",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=Xi(this.node(),e).tween,o=0,a=i.length;o<a;++o)if((r=i[o]).name===t)return r.value;return null}return this.each((null==n?Vi:Wi)(e,t,n))},delay:function(t){var n=this._id;return arguments.length?this.each(("function"==typeof t?ao:uo)(n,t)):Xi(this.node(),n).delay},duration:function(t){var n=this._id;return arguments.length?this.each(("function"==typeof t?co:fo)(n,t)):Xi(this.node(),n).duration},ease:function(t){var n=this._id;return arguments.length?this.each(function(t,n){if("function"!=typeof n)throw new Error;return function(){Hi(this,t).ease=n}}(n,t)):Xi(this.node(),n).ease},easeVarying:function(t){if("function"!=typeof t)throw new Error;return this.each(function(t,n){return function(){var e=n.apply(this,arguments);if("function"!=typeof e)throw new Error;Hi(this,t).ease=e}}(this._id,t))},end:function(){var t,n,e=this,r=e._id,i=e.size();return new Promise((function(o,a){var u={value:a},c={value:function(){0==--i&&o()}};e.each((function(){var e=Hi(this,r),i=e.on;i!==t&&((n=(t=i).copy())._.cancel.push(u),n._.interrupt.push(u),n._.end.push(c)),e.on=n})),0===i&&o()}))},[Symbol.iterator]:vo[Symbol.iterator]};function _o(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}function bo(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var mo=function t(n){function e(t){return Math.pow(t,n)}return n=+n,e.exponent=t,e}(3),xo=function t(n){function e(t){return 1-Math.pow(1-t,n)}return n=+n,e.exponent=t,e}(3),wo=function t(n){function e(t){return((t*=2)<=1?Math.pow(t,n):2-Math.pow(2-t,n))/2}return n=+n,e.exponent=t,e}(3),Mo=Math.PI,To=Mo/2;function Ao(t){return(1-Math.cos(Mo*t))/2}function So(t){return 1.0009775171065494*(Math.pow(2,-10*t)-.0009765625)}function Eo(t){return((t*=2)<=1?So(1-t):2-So(t-1))/2}function No(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}var ko=4/11,Co=6/11,Po=8/11,zo=3/4,$o=9/11,Do=10/11,Ro=15/16,Fo=21/22,qo=63/64,Uo=1/ko/ko;function Io(t){return(t=+t)<ko?Uo*t*t:t<Po?Uo*(t-=Co)*t+zo:t<Do?Uo*(t-=$o)*t+Ro:Uo*(t-=Fo)*t+qo}var Oo=1.70158,Bo=function t(n){function e(t){return(t=+t)*t*(n*(t-1)+t)}return n=+n,e.overshoot=t,e}(Oo),Yo=function t(n){function e(t){return--t*t*((t+1)*n+t)+1}return n=+n,e.overshoot=t,e}(Oo),Lo=function t(n){function e(t){return((t*=2)<1?t*t*((n+1)*t-n):(t-=2)*t*((n+1)*t+n)+2)/2}return n=+n,e.overshoot=t,e}(Oo),jo=2*Math.PI,Ho=function t(n,e){var r=Math.asin(1/(n=Math.max(1,n)))*(e/=jo);function i(t){return n*So(- --t)*Math.sin((r-t)/e)}return i.amplitude=function(n){return t(n,e*jo)},i.period=function(e){return t(n,e)},i}(1,.3),Xo=function t(n,e){var r=Math.asin(1/(n=Math.max(1,n)))*(e/=jo);function i(t){return 1-n*So(t=+t)*Math.sin((t+r)/e)}return i.amplitude=function(n){return t(n,e*jo)},i.period=function(e){return t(n,e)},i}(1,.3),Go=function t(n,e){var r=Math.asin(1/(n=Math.max(1,n)))*(e/=jo);function i(t){return((t=2*t-1)<0?n*So(-t)*Math.sin((r-t)/e):2-n*So(t)*Math.sin((r+t)/e))/2}return i.amplitude=function(n){return t(n,e*jo)},i.period=function(e){return t(n,e)},i}(1,.3),Vo={time:null,delay:0,duration:250,ease:bo};function Wo(t,n){for(var e;!(e=t.__transition)||!(e=e[n]);)if(!(t=t.parentNode))throw new Error(`transition ${n} not found`);return e}Wn.prototype.interrupt=function(t){return this.each((function(){Gi(this,t)}))},Wn.prototype.transition=function(t){var n,e;t instanceof po?(n=t._id,t=t._name):(n=yo(),(e=Vo).time=Ai(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,o=0;o<i;++o)for(var a,u=r[o],c=u.length,f=0;f<c;++f)(a=u[f])&&Li(a,t,n,f,u,e||Wo(a,n));return new po(r,this._parents,t,n)};var Zo=[null];var Ko=t=>()=>t;function Qo(t,{sourceEvent:n,target:e,selection:r,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},selection:{value:r,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function Jo(t){t.preventDefault(),t.stopImmediatePropagation()}var ta={name:"drag"},na={name:"space"},ea={name:"handle"},ra={name:"center"};const{abs:ia,max:oa,min:aa}=Math;function ua(t){return[+t[0],+t[1]]}function ca(t){return[ua(t[0]),ua(t[1])]}var fa={name:"x",handles:["w","e"].map(va),input:function(t,n){return null==t?null:[[+t[0],n[0][1]],[+t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},sa={name:"y",handles:["n","s"].map(va),input:function(t,n){return null==t?null:[[n[0][0],+t[0]],[n[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},la={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(va),input:function(t){return null==t?null:ca(t)},output:function(t){return t}},ha={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},da={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},pa={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},ga={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},ya={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function va(t){return{type:t}}function _a(t){return!t.ctrlKey&&!t.button}function ba(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function ma(){return navigator.maxTouchPoints||"ontouchstart"in this}function xa(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function wa(t){var n,e=ba,r=_a,i=ma,o=!0,a=$t("start","brush","end"),u=6;function c(n){var e=n.property("__brush",g).selectAll(".overlay").data([va("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",ha.overlay).merge(e).each((function(){var t=xa(this).extent;Zn(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),n.selectAll(".selection").data([va("selection")]).enter().append("rect").attr("class","selection").attr("cursor",ha.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=n.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return ha[t.type]})),n.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",h).filter(i).on("touchstart.brush",h).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(){var t=Zn(this),n=xa(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?n[1][0]-u/2:n[0][0]-u/2})).attr("y",(function(t){return"s"===t.type[0]?n[1][1]-u/2:n[0][1]-u/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+u:u})).attr("height",(function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+u:u}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(t,n,e){var r=t.__brush.emitter;return!r||e&&r.clean?new l(t,n,e):r}function l(t,n,e){this.that=t,this.args=n,this.state=t.__brush,this.active=0,this.clean=e}function h(e){if((!n||e.touches)&&r.apply(this,arguments)){var i,a,u,c,l,h,d,p,g,y,v,_=this,b=e.target.__data__.type,m="selection"===(o&&e.metaKey?b="overlay":b)?ta:o&&e.altKey?ra:ea,x=t===sa?null:ga[b],w=t===fa?null:ya[b],M=xa(_),T=M.extent,A=M.selection,S=T[0][0],E=T[0][1],N=T[1][0],k=T[1][1],C=0,P=0,z=x&&w&&o&&e.shiftKey,$=Array.from(e.touches||[e],(t=>{const n=t.identifier;return(t=ne(t,_)).point0=t.slice(),t.identifier=n,t}));Gi(_);var D=s(_,arguments,!0).beforestart();if("overlay"===b){A&&(g=!0);const n=[$[0],$[1]||$[0]];M.selection=A=[[i=t===sa?S:aa(n[0][0],n[1][0]),u=t===fa?E:aa(n[0][1],n[1][1])],[l=t===sa?N:oa(n[0][0],n[1][0]),d=t===fa?k:oa(n[0][1],n[1][1])]],$.length>1&&I(e)}else i=A[0][0],u=A[0][1],l=A[1][0],d=A[1][1];a=i,c=u,h=l,p=d;var R=Zn(_).attr("pointer-events","none"),F=R.selectAll(".overlay").attr("cursor",ha[b]);if(e.touches)D.moved=U,D.ended=O;else{var q=Zn(e.view).on("mousemove.brush",U,!0).on("mouseup.brush",O,!0);o&&q.on("keydown.brush",(function(t){switch(t.keyCode){case 16:z=x&&w;break;case 18:m===ea&&(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=ra,I(t));break;case 32:m!==ea&&m!==ra||(x<0?l=h-C:x>0&&(i=a-C),w<0?d=p-P:w>0&&(u=c-P),m=na,F.attr("cursor",ha.selection),I(t));break;default:return}Jo(t)}),!0).on("keyup.brush",(function(t){switch(t.keyCode){case 16:z&&(y=v=z=!1,I(t));break;case 18:m===ra&&(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=ea,I(t));break;case 32:m===na&&(t.altKey?(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=ra):(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=ea),F.attr("cursor",ha[b]),I(t));break;default:return}Jo(t)}),!0),ae(e.view)}f.call(_),D.start(e,m.name)}function U(t){for(const n of t.changedTouches||[t])for(const t of $)t.identifier===n.identifier&&(t.cur=ne(n,_));if(z&&!y&&!v&&1===$.length){const t=$[0];ia(t.cur[0]-t[0])>ia(t.cur[1]-t[1])?v=!0:y=!0}for(const t of $)t.cur&&(t[0]=t.cur[0],t[1]=t.cur[1]);g=!0,Jo(t),I(t)}function I(t){const n=$[0],e=n.point0;var r;switch(C=n[0]-e[0],P=n[1]-e[1],m){case na:case ta:x&&(C=oa(S-i,aa(N-l,C)),a=i+C,h=l+C),w&&(P=oa(E-u,aa(k-d,P)),c=u+P,p=d+P);break;case ea:$[1]?(x&&(a=oa(S,aa(N,$[0][0])),h=oa(S,aa(N,$[1][0])),x=1),w&&(c=oa(E,aa(k,$[0][1])),p=oa(E,aa(k,$[1][1])),w=1)):(x<0?(C=oa(S-i,aa(N-i,C)),a=i+C,h=l):x>0&&(C=oa(S-l,aa(N-l,C)),a=i,h=l+C),w<0?(P=oa(E-u,aa(k-u,P)),c=u+P,p=d):w>0&&(P=oa(E-d,aa(k-d,P)),c=u,p=d+P));break;case ra:x&&(a=oa(S,aa(N,i-C*x)),h=oa(S,aa(N,l+C*x))),w&&(c=oa(E,aa(k,u-P*w)),p=oa(E,aa(k,d+P*w)))}h<a&&(x*=-1,r=i,i=l,l=r,r=a,a=h,h=r,b in da&&F.attr("cursor",ha[b=da[b]])),p<c&&(w*=-1,r=u,u=d,d=r,r=c,c=p,p=r,b in pa&&F.attr("cursor",ha[b=pa[b]])),M.selection&&(A=M.selection),y&&(a=A[0][0],h=A[1][0]),v&&(c=A[0][1],p=A[1][1]),A[0][0]===a&&A[0][1]===c&&A[1][0]===h&&A[1][1]===p||(M.selection=[[a,c],[h,p]],f.call(_),D.brush(t,m.name))}function O(t){if(function(t){t.stopImmediatePropagation()}(t),t.touches){if(t.touches.length)return;n&&clearTimeout(n),n=setTimeout((function(){n=null}),500)}else ue(t.view,g),q.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null);R.attr("pointer-events","all"),F.attr("cursor",ha.overlay),M.selection&&(A=M.selection),function(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}(A)&&(M.selection=null,f.call(_)),D.end(t,m.name)}}function d(t){s(this,arguments).moved(t)}function p(t){s(this,arguments).ended(t)}function g(){var n=this.__brush||{selection:null};return n.extent=ca(e.apply(this,arguments)),n.dim=t,n}return c.move=function(n,e,r){n.tween?n.on("start.brush",(function(t){s(this,arguments).beforestart().start(t)})).on("interrupt.brush end.brush",(function(t){s(this,arguments).end(t)})).tween("brush",(function(){var n=this,r=n.__brush,i=s(n,arguments),o=r.selection,a=t.input("function"==typeof e?e.apply(this,arguments):e,r.extent),u=Gr(o,a);function c(t){r.selection=1===t&&null===a?null:u(t),f.call(n),i.brush()}return null!==o&&null!==a?c:c(1)})):n.each((function(){var n=this,i=arguments,o=n.__brush,a=t.input("function"==typeof e?e.apply(n,i):e,o.extent),u=s(n,i).beforestart();Gi(n),o.selection=null===a?null:a,f.call(n),u.start(r).brush(r).end(r)}))},c.clear=function(t,n){c.move(t,null,n)},l.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(t,n){return this.starting?(this.starting=!1,this.emit("start",t,n)):this.emit("brush",t),this},brush:function(t,n){return this.emit("brush",t,n),this},end:function(t,n){return 0==--this.active&&(delete this.state.emitter,this.emit("end",t,n)),this},emit:function(n,e,r){var i=Zn(this.that).datum();a.call(n,this.that,new Qo(n,{sourceEvent:e,target:c,selection:t.output(this.state.selection),mode:r,dispatch:a}),i)}},c.extent=function(t){return arguments.length?(e="function"==typeof t?t:Ko(ca(t)),c):e},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:Ko(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:Ko(!!t),c):i},c.handleSize=function(t){return arguments.length?(u=+t,c):u},c.keyModifiers=function(t){return arguments.length?(o=!!t,c):o},c.on=function(){var t=a.on.apply(a,arguments);return t===a?c:t},c}var Ma=Math.abs,Ta=Math.cos,Aa=Math.sin,Sa=Math.PI,Ea=Sa/2,Na=2*Sa,ka=Math.max,Ca=1e-12;function Pa(t,n){return Array.from({length:n-t},((n,e)=>t+e))}function za(t,n){var e=0,r=null,i=null,o=null;function a(a){var u,c=a.length,f=new Array(c),s=Pa(0,c),l=new Array(c*c),h=new Array(c),d=0;a=Float64Array.from({length:c*c},n?(t,n)=>a[n%c][n/c|0]:(t,n)=>a[n/c|0][n%c]);for(let n=0;n<c;++n){let e=0;for(let r=0;r<c;++r)e+=a[n*c+r]+t*a[r*c+n];d+=f[n]=e}u=(d=ka(0,Na-e*c)/d)?e:Na/c;{let n=0;r&&s.sort(((t,n)=>r(f[t],f[n])));for(const e of s){const r=n;if(t){const t=Pa(1+~c,c).filter((t=>t<0?a[~t*c+e]:a[e*c+t]));i&&t.sort(((t,n)=>i(t<0?-a[~t*c+e]:a[e*c+t],n<0?-a[~n*c+e]:a[e*c+n])));for(const r of t)if(r<0){(l[~r*c+e]||(l[~r*c+e]={source:null,target:null})).target={index:e,startAngle:n,endAngle:n+=a[~r*c+e]*d,value:a[~r*c+e]}}else{(l[e*c+r]||(l[e*c+r]={source:null,target:null})).source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}else{const t=Pa(0,c).filter((t=>a[e*c+t]||a[t*c+e]));i&&t.sort(((t,n)=>i(a[e*c+t],a[e*c+n])));for(const r of t){let t;if(e<r?(t=l[e*c+r]||(l[e*c+r]={source:null,target:null}),t.source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}):(t=l[r*c+e]||(l[r*c+e]={source:null,target:null}),t.target={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]},e===r&&(t.source=t.target)),t.source&&t.target&&t.source.value<t.target.value){const n=t.source;t.source=t.target,t.target=n}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}n+=u}}return(l=Object.values(l)).groups=h,o?l.sort(o):l}return a.padAngle=function(t){return arguments.length?(e=ka(0,t),a):e},a.sortGroups=function(t){return arguments.length?(r=t,a):r},a.sortSubgroups=function(t){return arguments.length?(i=t,a):i},a.sortChords=function(t){return arguments.length?(null==t?o=null:(n=t,o=function(t,e){return n(t.source.value+t.target.value,e.source.value+e.target.value)})._=t,a):o&&o._;var n},a}const $a=Math.PI,Da=2*$a,Ra=1e-6,Fa=Da-Ra;function qa(t){this._+=t[0];for(let n=1,e=t.length;n<e;++n)this._+=arguments[n]+t[n]}let Ua=class{constructor(t){this._x0=this._y0=this._x1=this._y1=null,this._="",this._append=null==t?qa:function(t){let n=Math.floor(t);if(!(n>=0))throw new Error(`invalid digits: ${t}`);if(n>15)return qa;const e=10**n;return function(t){this._+=t[0];for(let n=1,r=t.length;n<r;++n)this._+=Math.round(arguments[n]*e)/e+t[n]}}(t)}moveTo(t,n){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._append`Z`)}lineTo(t,n){this._append`L${this._x1=+t},${this._y1=+n}`}quadraticCurveTo(t,n,e,r){this._append`Q${+t},${+n},${this._x1=+e},${this._y1=+r}`}bezierCurveTo(t,n,e,r,i,o){this._append`C${+t},${+n},${+e},${+r},${this._x1=+i},${this._y1=+o}`}arcTo(t,n,e,r,i){if(t=+t,n=+n,e=+e,r=+r,(i=+i)<0)throw new Error(`negative radius: ${i}`);let o=this._x1,a=this._y1,u=e-t,c=r-n,f=o-t,s=a-n,l=f*f+s*s;if(null===this._x1)this._append`M${this._x1=t},${this._y1=n}`;else if(l>Ra)if(Math.abs(s*u-c*f)>Ra&&i){let h=e-o,d=r-a,p=u*u+c*c,g=h*h+d*d,y=Math.sqrt(p),v=Math.sqrt(l),_=i*Math.tan(($a-Math.acos((p+l-g)/(2*y*v)))/2),b=_/v,m=_/y;Math.abs(b-1)>Ra&&this._append`L${t+b*f},${n+b*s}`,this._append`A${i},${i},0,0,${+(s*h>f*d)},${this._x1=t+m*u},${this._y1=n+m*c}`}else this._append`L${this._x1=t},${this._y1=n}`;else;}arc(t,n,e,r,i,o){if(t=+t,n=+n,o=!!o,(e=+e)<0)throw new Error(`negative radius: ${e}`);let a=e*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;null===this._x1?this._append`M${c},${f}`:(Math.abs(this._x1-c)>Ra||Math.abs(this._y1-f)>Ra)&&this._append`L${c},${f}`,e&&(l<0&&(l=l%Da+Da),l>Fa?this._append`A${e},${e},0,1,${s},${t-a},${n-u}A${e},${e},0,1,${s},${this._x1=c},${this._y1=f}`:l>Ra&&this._append`A${e},${e},0,${+(l>=$a)},${s},${this._x1=t+e*Math.cos(i)},${this._y1=n+e*Math.sin(i)}`)}rect(t,n,e,r){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${e=+e}v${+r}h${-e}Z`}toString(){return this._}};function Ia(){return new Ua}Ia.prototype=Ua.prototype;var Oa=Array.prototype.slice;function Ba(t){return function(){return t}}function Ya(t){return t.source}function La(t){return t.target}function ja(t){return t.radius}function Ha(t){return t.startAngle}function Xa(t){return t.endAngle}function Ga(){return 0}function Va(){return 10}function Wa(t){var n=Ya,e=La,r=ja,i=ja,o=Ha,a=Xa,u=Ga,c=null;function f(){var f,s=n.apply(this,arguments),l=e.apply(this,arguments),h=u.apply(this,arguments)/2,d=Oa.call(arguments),p=+r.apply(this,(d[0]=s,d)),g=o.apply(this,d)-Ea,y=a.apply(this,d)-Ea,v=+i.apply(this,(d[0]=l,d)),_=o.apply(this,d)-Ea,b=a.apply(this,d)-Ea;if(c||(c=f=Ia()),h>Ca&&(Ma(y-g)>2*h+Ca?y>g?(g+=h,y-=h):(g-=h,y+=h):g=y=(g+y)/2,Ma(b-_)>2*h+Ca?b>_?(_+=h,b-=h):(_-=h,b+=h):_=b=(_+b)/2),c.moveTo(p*Ta(g),p*Aa(g)),c.arc(0,0,p,g,y),g!==_||y!==b)if(t){var m=v-+t.apply(this,arguments),x=(_+b)/2;c.quadraticCurveTo(0,0,m*Ta(_),m*Aa(_)),c.lineTo(v*Ta(x),v*Aa(x)),c.lineTo(m*Ta(b),m*Aa(b))}else c.quadraticCurveTo(0,0,v*Ta(_),v*Aa(_)),c.arc(0,0,v,_,b);if(c.quadraticCurveTo(0,0,p*Ta(g),p*Aa(g)),c.closePath(),f)return c=null,f+""||null}return t&&(f.headRadius=function(n){return arguments.length?(t="function"==typeof n?n:Ba(+n),f):t}),f.radius=function(t){return arguments.length?(r=i="function"==typeof t?t:Ba(+t),f):r},f.sourceRadius=function(t){return arguments.length?(r="function"==typeof t?t:Ba(+t),f):r},f.targetRadius=function(t){return arguments.length?(i="function"==typeof t?t:Ba(+t),f):i},f.startAngle=function(t){return arguments.length?(o="function"==typeof t?t:Ba(+t),f):o},f.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:Ba(+t),f):a},f.padAngle=function(t){return arguments.length?(u="function"==typeof t?t:Ba(+t),f):u},f.source=function(t){return arguments.length?(n=t,f):n},f.target=function(t){return arguments.length?(e=t,f):e},f.context=function(t){return arguments.length?(c=null==t?null:t,f):c},f}var Za=Array.prototype.slice;function Ka(t,n){return t-n}var Qa=t=>()=>t;function Ja(t,n){for(var e,r=-1,i=n.length;++r<i;)if(e=tu(t,n[r]))return e;return 0}function tu(t,n){for(var e=n[0],r=n[1],i=-1,o=0,a=t.length,u=a-1;o<a;u=o++){var c=t[o],f=c[0],s=c[1],l=t[u],h=l[0],d=l[1];if(nu(c,l,n))return 0;s>r!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function nu(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function eu(){}var ru=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function iu(){var t=1,n=1,e=K,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(Ka);else{const e=M(t,ou);for(n=G(...Z(e[0],e[1],n),n);n[n.length-1]>=e[1];)n.pop();for(;n[1]<e[0];)n.shift()}return n.map((n=>o(t,n)))}function o(e,i){const o=null==i?NaN:+i;if(isNaN(o))throw new Error(`invalid value: ${i}`);var u=[],c=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=au(e[0],r),ru[f<<1].forEach(p);for(;++o<t-1;)c=f,f=au(e[o+1],r),ru[c|f<<1].forEach(p);ru[f|0].forEach(p);for(;++u<n-1;){for(o=-1,f=au(e[u*t+t],r),s=au(e[u*t],r),ru[f<<1|s<<2].forEach(p);++o<t-1;)c=f,f=au(e[u*t+t+o+1],r),l=s,s=au(e[u*t+o+1],r),ru[c|f<<1|s<<2|l<<3].forEach(p);ru[f|s<<3].forEach(p)}o=-1,s=e[u*t]>=r,ru[s<<2].forEach(p);for(;++o<t-1;)l=s,s=au(e[u*t+o+1],r),ru[s<<2|l<<3].forEach(p);function p(t){var n,e,r=[t[0][0]+o,t[0][1]+u],c=[t[1][0]+o,t[1][1]+u],f=a(r),s=a(c);(n=d[f])?(e=h[s])?(delete d[n.end],delete h[e.start],n===e?(n.ring.push(c),i(n.ring)):h[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete d[n.end],n.ring.push(c),d[n.end=s]=n):(n=h[s])?(e=d[f])?(delete h[n.start],delete d[e.end],n===e?(n.ring.push(c),i(n.ring)):h[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete h[n.start],n.ring.unshift(r),h[n.start=f]=n):h[f]=d[s]={start:f,end:s,ring:[r,c]}}ru[s<<3].forEach(p)}(e,o,(function(t){r(t,e,o),function(t){for(var n=0,e=t.length,r=t[e-1][1]*t[0][0]-t[e-1][0]*t[0][1];++n<e;)r+=t[n-1][1]*t[n][0]-t[n-1][0]*t[n][1];return r}(t)>0?u.push([t]):c.push(t)})),c.forEach((function(t){for(var n,e=0,r=u.length;e<r;++e)if(-1!==Ja((n=u[e])[0],t))return void n.push(t)})),{type:"MultiPolygon",value:i,coordinates:u}}function a(n){return 2*n[0]+n[1]*(t+1)*4}function u(e,r,i){e.forEach((function(e){var o=e[0],a=e[1],u=0|o,c=0|a,f=uu(r[c*t+u]);o>0&&o<t&&u===o&&(e[0]=cu(o,uu(r[c*t+u-1]),f,i)),a>0&&a<n&&c===a&&(e[1]=cu(a,uu(r[(c-1)*t+u]),f,i))}))}return i.contour=o,i.size=function(e){if(!arguments.length)return[t,n];var r=Math.floor(e[0]),o=Math.floor(e[1]);if(!(r>=0&&o>=0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?Qa(Za.call(t)):Qa(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:eu,i):r===u},i}function ou(t){return isFinite(t)?t:NaN}function au(t,n){return null!=t&&+t>=n}function uu(t){return null==t||isNaN(t=+t)?-1/0:t}function cu(t,n,e,r){const i=r-n,o=e-n,a=isFinite(i)||isFinite(o)?i/o:Math.sign(i)/Math.sign(o);return isNaN(a)?t:t+a-.5}function fu(t){return t[0]}function su(t){return t[1]}function lu(){return 1}const hu=134217729,du=33306690738754706e-32;function pu(t,n,e,r,i){let o,a,u,c,f=n[0],s=r[0],l=0,h=0;s>f==s>-f?(o=f,f=n[++l]):(o=s,s=r[++h]);let d=0;if(l<t&&h<e)for(s>f==s>-f?(a=f+o,u=o-(a-f),f=n[++l]):(a=s+o,u=o-(a-s),s=r[++h]),o=a,0!==u&&(i[d++]=u);l<t&&h<e;)s>f==s>-f?(a=o+f,c=a-o,u=o-(a-c)+(f-c),f=n[++l]):(a=o+s,c=a-o,u=o-(a-c)+(s-c),s=r[++h]),o=a,0!==u&&(i[d++]=u);for(;l<t;)a=o+f,c=a-o,u=o-(a-c)+(f-c),f=n[++l],o=a,0!==u&&(i[d++]=u);for(;h<e;)a=o+s,c=a-o,u=o-(a-c)+(s-c),s=r[++h],o=a,0!==u&&(i[d++]=u);return 0===o&&0!==d||(i[d++]=o),d}function gu(t){return new Float64Array(t)}const yu=22204460492503146e-32,vu=11093356479670487e-47,_u=gu(4),bu=gu(8),mu=gu(12),xu=gu(16),wu=gu(4);function Mu(t,n,e,r,i,o){const a=(n-o)*(e-i),u=(t-i)*(r-o),c=a-u,f=Math.abs(a+u);return Math.abs(c)>=33306690738754716e-32*f?c:-function(t,n,e,r,i,o,a){let u,c,f,s,l,h,d,p,g,y,v,_,b,m,x,w,M,T;const A=t-i,S=e-i,E=n-o,N=r-o;m=A*N,h=hu*A,d=h-(h-A),p=A-d,h=hu*N,g=h-(h-N),y=N-g,x=p*y-(m-d*g-p*g-d*y),w=E*S,h=hu*E,d=h-(h-E),p=E-d,h=hu*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,_u[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,_u[1]=b-(v+l)+(l-w),T=_+v,l=T-_,_u[2]=_-(T-l)+(v-l),_u[3]=T;let k=function(t,n){let e=n[0];for(let r=1;r<t;r++)e+=n[r];return e}(4,_u),C=yu*a;if(k>=C||-k>=C)return k;if(l=t-A,u=t-(A+l)+(l-i),l=e-S,f=e-(S+l)+(l-i),l=n-E,c=n-(E+l)+(l-o),l=r-N,s=r-(N+l)+(l-o),0===u&&0===c&&0===f&&0===s)return k;if(C=vu*a+du*Math.abs(k),k+=A*s+N*u-(E*f+S*c),k>=C||-k>=C)return k;m=u*N,h=hu*u,d=h-(h-u),p=u-d,h=hu*N,g=h-(h-N),y=N-g,x=p*y-(m-d*g-p*g-d*y),w=c*S,h=hu*c,d=h-(h-c),p=c-d,h=hu*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const P=pu(4,_u,4,wu,bu);m=A*s,h=hu*A,d=h-(h-A),p=A-d,h=hu*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=E*f,h=hu*E,d=h-(h-E),p=E-d,h=hu*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const z=pu(P,bu,4,wu,mu);m=u*s,h=hu*u,d=h-(h-u),p=u-d,h=hu*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=c*f,h=hu*c,d=h-(h-c),p=c-d,h=hu*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const $=pu(z,mu,4,wu,xu);return xu[$-1]}(t,n,e,r,i,o,f)}const Tu=Math.pow(2,-52),Au=new Uint32Array(512);class Su{static from(t,n=zu,e=$u){const r=t.length,i=new Float64Array(2*r);for(let o=0;o<r;o++){const r=t[o];i[2*o]=n(r),i[2*o+1]=e(r)}return new Su(i)}constructor(t){const n=t.length>>1;if(n>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*n-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:e,_hullTri:r,_hullHash:i}=this,o=t.length>>1;let a=1/0,u=1/0,c=-1/0,f=-1/0;for(let n=0;n<o;n++){const e=t[2*n],r=t[2*n+1];e<a&&(a=e),r<u&&(u=r),e>c&&(c=e),r>f&&(f=r),this._ids[n]=n}const s=(a+c)/2,l=(u+f)/2;let h,d,p;for(let n=0,e=1/0;n<o;n++){const r=Eu(s,l,t[2*n],t[2*n+1]);r<e&&(h=n,e=r)}const g=t[2*h],y=t[2*h+1];for(let n=0,e=1/0;n<o;n++){if(n===h)continue;const r=Eu(g,y,t[2*n],t[2*n+1]);r<e&&r>0&&(d=n,e=r)}let v=t[2*d],_=t[2*d+1],b=1/0;for(let n=0;n<o;n++){if(n===h||n===d)continue;const e=ku(g,y,v,_,t[2*n],t[2*n+1]);e<b&&(p=n,b=e)}let m=t[2*p],x=t[2*p+1];if(b===1/0){for(let n=0;n<o;n++)this._dists[n]=t[2*n]-t[0]||t[2*n+1]-t[1];Cu(this._ids,this._dists,0,o-1);const n=new Uint32Array(o);let e=0;for(let t=0,r=-1/0;t<o;t++){const i=this._ids[t],o=this._dists[i];o>r&&(n[e++]=i,r=o)}return this.hull=n.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(Mu(g,y,v,_,m,x)<0){const t=d,n=v,e=_;d=p,v=m,_=x,p=t,m=n,x=e}const w=function(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c),d=t+(f*s-u*l)*h,p=n+(a*l-c*s)*h;return{x:d,y:p}}(g,y,v,_,m,x);this._cx=w.x,this._cy=w.y;for(let n=0;n<o;n++)this._dists[n]=Eu(t[2*n],t[2*n+1],w.x,w.y);Cu(this._ids,this._dists,0,o-1),this._hullStart=h;let M=3;e[h]=n[p]=d,e[d]=n[h]=p,e[p]=n[d]=h,r[h]=0,r[d]=1,r[p]=2,i.fill(-1),i[this._hashKey(g,y)]=h,i[this._hashKey(v,_)]=d,i[this._hashKey(m,x)]=p,this.trianglesLen=0,this._addTriangle(h,d,p,-1,-1,-1);for(let o,a,u=0;u<this._ids.length;u++){const c=this._ids[u],f=t[2*c],s=t[2*c+1];if(u>0&&Math.abs(f-o)<=Tu&&Math.abs(s-a)<=Tu)continue;if(o=f,a=s,c===h||c===d||c===p)continue;let l=0;for(let t=0,n=this._hashKey(f,s);t<this._hashSize&&(l=i[(n+t)%this._hashSize],-1===l||l===e[l]);t++);l=n[l];let g,y=l;for(;g=e[y],Mu(f,s,t[2*y],t[2*y+1],t[2*g],t[2*g+1])>=0;)if(y=g,y===l){y=-1;break}if(-1===y)continue;let v=this._addTriangle(y,c,e[y],-1,-1,r[y]);r[c]=this._legalize(v+2),r[y]=v,M++;let _=e[y];for(;g=e[_],Mu(f,s,t[2*_],t[2*_+1],t[2*g],t[2*g+1])<0;)v=this._addTriangle(_,c,g,r[c],-1,r[_]),r[c]=this._legalize(v+2),e[_]=_,M--,_=g;if(y===l)for(;g=n[y],Mu(f,s,t[2*g],t[2*g+1],t[2*y],t[2*y+1])<0;)v=this._addTriangle(g,c,y,-1,r[y],r[g]),this._legalize(v+2),r[g]=v,e[y]=y,M--,y=g;this._hullStart=n[c]=y,e[y]=n[_]=c,e[c]=_,i[this._hashKey(f,s)]=c,i[this._hashKey(t[2*y],t[2*y+1])]=y}this.hull=new Uint32Array(M);for(let t=0,n=this._hullStart;t<M;t++)this.hull[t]=n,n=e[n];this.triangles=this._triangles.subarray(0,this.trianglesLen),this.halfedges=this._halfedges.subarray(0,this.trianglesLen)}_hashKey(t,n){return Math.floor(function(t,n){const e=t/(Math.abs(t)+Math.abs(n));return(n>0?3-e:1+e)/4}(t-this._cx,n-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:n,_halfedges:e,coords:r}=this;let i=0,o=0;for(;;){const a=e[t],u=t-t%3;if(o=u+(t+2)%3,-1===a){if(0===i)break;t=Au[--i];continue}const c=a-a%3,f=u+(t+1)%3,s=c+(a+2)%3,l=n[o],h=n[t],d=n[f],p=n[s];if(Nu(r[2*l],r[2*l+1],r[2*h],r[2*h+1],r[2*d],r[2*d+1],r[2*p],r[2*p+1])){n[t]=p,n[a]=l;const r=e[s];if(-1===r){let n=this._hullStart;do{if(this._hullTri[n]===s){this._hullTri[n]=t;break}n=this._hullPrev[n]}while(n!==this._hullStart)}this._link(t,r),this._link(a,e[o]),this._link(o,s);const u=c+(a+1)%3;i<Au.length&&(Au[i++]=u)}else{if(0===i)break;t=Au[--i]}}return o}_link(t,n){this._halfedges[t]=n,-1!==n&&(this._halfedges[n]=t)}_addTriangle(t,n,e,r,i,o){const a=this.trianglesLen;return this._triangles[a]=t,this._triangles[a+1]=n,this._triangles[a+2]=e,this._link(a,r),this._link(a+1,i),this._link(a+2,o),this.trianglesLen+=3,a}}function Eu(t,n,e,r){const i=t-e,o=n-r;return i*i+o*o}function Nu(t,n,e,r,i,o,a,u){const c=t-a,f=n-u,s=e-a,l=r-u,h=i-a,d=o-u,p=s*s+l*l,g=h*h+d*d;return c*(l*g-p*d)-f*(s*g-p*h)+(c*c+f*f)*(s*d-l*h)<0}function ku(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c),d=(f*s-u*l)*h,p=(a*l-c*s)*h;return d*d+p*p}function Cu(t,n,e,r){if(r-e<=20)for(let i=e+1;i<=r;i++){const r=t[i],o=n[r];let a=i-1;for(;a>=e&&n[t[a]]>o;)t[a+1]=t[a--];t[a+1]=r}else{let i=e+1,o=r;Pu(t,e+r>>1,i),n[t[e]]>n[t[r]]&&Pu(t,e,r),n[t[i]]>n[t[r]]&&Pu(t,i,r),n[t[e]]>n[t[i]]&&Pu(t,e,i);const a=t[i],u=n[a];for(;;){do{i++}while(n[t[i]]<u);do{o--}while(n[t[o]]>u);if(o<i)break;Pu(t,i,o)}t[e+1]=t[o],t[o]=a,r-i+1>=o-e?(Cu(t,n,i,r),Cu(t,n,e,o-1)):(Cu(t,n,e,o-1),Cu(t,n,i,r))}}function Pu(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function zu(t){return t[0]}function $u(t){return t[1]}const Du=1e-6;class Ru{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,e){const r=(t=+t)+(e=+e),i=n=+n;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${r},${i}`:(Math.abs(this._x1-r)>Du||Math.abs(this._y1-i)>Du)&&(this._+="L"+r+","+i),e&&(this._+=`A${e},${e},0,1,1,${t-e},${n}A${e},${e},0,1,1,${this._x1=r},${this._y1=i}`)}rect(t,n,e,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+e}v${+r}h${-e}Z`}value(){return this._||null}}class Fu{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}class qu{constructor(t,[n,e,r,i]=[0,0,960,500]){if(!((r=+r)>=(n=+n)&&(i=+i)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=r,this.xmin=n,this.ymax=i,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:e},vectors:r}=this;let i,o;const a=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let r,u,c=0,f=0,s=e.length;c<s;c+=3,f+=2){const s=2*e[c],l=2*e[c+1],h=2*e[c+2],d=t[s],p=t[s+1],g=t[l],y=t[l+1],v=t[h],_=t[h+1],b=g-d,m=y-p,x=v-d,w=_-p,M=2*(b*w-m*x);if(Math.abs(M)<1e-9){if(void 0===i){i=o=0;for(const e of n)i+=t[2*e],o+=t[2*e+1];i/=n.length,o/=n.length}const e=1e9*Math.sign((i-d)*w-(o-p)*x);r=(d+v)/2-e*w,u=(p+_)/2+e*x}else{const t=1/M,n=b*b+m*m,e=x*x+w*w;r=d+(w*n-m*e)*t,u=p+(b*e-x*n)*t}a[f]=r,a[f+1]=u}let u,c,f,s=n[n.length-1],l=4*s,h=t[2*s],d=t[2*s+1];r.fill(0);for(let e=0;e<n.length;++e)s=n[e],u=l,c=h,f=d,l=4*s,h=t[2*s],d=t[2*s+1],r[u+2]=r[l]=f-d,r[u+3]=r[l+1]=h-c}render(t){const n=null==t?t=new Ru:void 0,{delaunay:{halfedges:e,inedges:r,hull:i},circumcenters:o,vectors:a}=this;if(i.length<=1)return null;for(let n=0,r=e.length;n<r;++n){const r=e[n];if(r<n)continue;const i=2*Math.floor(n/3),a=2*Math.floor(r/3),u=o[i],c=o[i+1],f=o[a],s=o[a+1];this._renderSegment(u,c,f,s,t)}let u,c=i[i.length-1];for(let n=0;n<i.length;++n){u=c,c=i[n];const e=2*Math.floor(r[c]/3),f=o[e],s=o[e+1],l=4*u,h=this._project(f,s,a[l+2],a[l+3]);h&&this._renderSegment(f,s,h[0],h[1],t)}return n&&n.value()}renderBounds(t){const n=null==t?t=new Ru:void 0;return t.rect(this.xmin,this.ymin,this.xmax-this.xmin,this.ymax-this.ymin),n&&n.value()}renderCell(t,n){const e=null==n?n=new Ru:void 0,r=this._clip(t);if(null===r||!r.length)return;n.moveTo(r[0],r[1]);let i=r.length;for(;r[0]===r[i-2]&&r[1]===r[i-1]&&i>1;)i-=2;for(let t=2;t<i;t+=2)r[t]===r[t-2]&&r[t+1]===r[t-1]||n.lineTo(r[t],r[t+1]);return n.closePath(),e&&e.value()}*cellPolygons(){const{delaunay:{points:t}}=this;for(let n=0,e=t.length/2;n<e;++n){const t=this.cellPolygon(n);t&&(t.index=n,yield t)}}cellPolygon(t){const n=new Fu;return this.renderCell(t,n),n.value()}_renderSegment(t,n,e,r,i){let o;const a=this._regioncode(t,n),u=this._regioncode(e,r);0===a&&0===u?(i.moveTo(t,n),i.lineTo(e,r)):(o=this._clipSegment(t,n,e,r,a,u))&&(i.moveTo(o[0],o[1]),i.lineTo(o[2],o[3]))}contains(t,n,e){return(n=+n)==n&&(e=+e)==e&&this.delaunay._step(t,n,e)===t}*neighbors(t){const n=this._clip(t);if(n)for(const e of this.delaunay.neighbors(t)){const t=this._clip(e);if(t)t:for(let r=0,i=n.length;r<i;r+=2)for(let o=0,a=t.length;o<a;o+=2)if(n[r]===t[o]&&n[r+1]===t[o+1]&&n[(r+2)%i]===t[(o+a-2)%a]&&n[(r+3)%i]===t[(o+a-1)%a]){yield e;break t}}}_cell(t){const{circumcenters:n,delaunay:{inedges:e,halfedges:r,triangles:i}}=this,o=e[t];if(-1===o)return null;const a=[];let u=o;do{const e=Math.floor(u/3);if(a.push(n[2*e],n[2*e+1]),u=u%3==2?u-2:u+1,i[u]!==t)break;u=r[u]}while(u!==o&&-1!==u);return a}_clip(t){if(0===t&&1===this.delaunay.hull.length)return[this.xmax,this.ymin,this.xmax,this.ymax,this.xmin,this.ymax,this.xmin,this.ymin];const n=this._cell(t);if(null===n)return null;const{vectors:e}=this,r=4*t;return this._simplify(e[r]||e[r+1]?this._clipInfinite(t,n,e[r],e[r+1],e[r+2],e[r+3]):this._clipFinite(t,n))}_clipFinite(t,n){const e=n.length;let r,i,o,a,u=null,c=n[e-2],f=n[e-1],s=this._regioncode(c,f),l=0;for(let h=0;h<e;h+=2)if(r=c,i=f,c=n[h],f=n[h+1],o=s,s=this._regioncode(c,f),0===o&&0===s)a=l,l=0,u?u.push(c,f):u=[c,f];else{let n,e,h,d,p;if(0===o){if(null===(n=this._clipSegment(r,i,c,f,o,s)))continue;[e,h,d,p]=n}else{if(null===(n=this._clipSegment(c,f,r,i,s,o)))continue;[d,p,e,h]=n,a=l,l=this._edgecode(e,h),a&&l&&this._edge(t,a,l,u,u.length),u?u.push(e,h):u=[e,h]}a=l,l=this._edgecode(d,p),a&&l&&this._edge(t,a,l,u,u.length),u?u.push(d,p):u=[d,p]}if(u)a=l,l=this._edgecode(u[0],u[1]),a&&l&&this._edge(t,a,l,u,u.length);else if(this.contains(t,(this.xmin+this.xmax)/2,(this.ymin+this.ymax)/2))return[this.xmax,this.ymin,this.xmax,this.ymax,this.xmin,this.ymax,this.xmin,this.ymin];return u}_clipSegment(t,n,e,r,i,o){const a=i<o;for(a&&([t,n,e,r,i,o]=[e,r,t,n,o,i]);;){if(0===i&&0===o)return a?[e,r,t,n]:[t,n,e,r];if(i&o)return null;let u,c,f=i||o;8&f?(u=t+(e-t)*(this.ymax-n)/(r-n),c=this.ymax):4&f?(u=t+(e-t)*(this.ymin-n)/(r-n),c=this.ymin):2&f?(c=n+(r-n)*(this.xmax-t)/(e-t),u=this.xmax):(c=n+(r-n)*(this.xmin-t)/(e-t),u=this.xmin),i?(t=u,n=c,i=this._regioncode(t,n)):(e=u,r=c,o=this._regioncode(e,r))}}_clipInfinite(t,n,e,r,i,o){let a,u=Array.from(n);if((a=this._project(u[0],u[1],e,r))&&u.unshift(a[0],a[1]),(a=this._project(u[u.length-2],u[u.length-1],i,o))&&u.push(a[0],a[1]),u=this._clipFinite(t,u))for(let n,e=0,r=u.length,i=this._edgecode(u[r-2],u[r-1]);e<r;e+=2)n=i,i=this._edgecode(u[e],u[e+1]),n&&i&&(e=this._edge(t,n,i,u,e),r=u.length);else this.contains(t,(this.xmin+this.xmax)/2,(this.ymin+this.ymax)/2)&&(u=[this.xmin,this.ymin,this.xmax,this.ymin,this.xmax,this.ymax,this.xmin,this.ymax]);return u}_edge(t,n,e,r,i){for(;n!==e;){let e,o;switch(n){case 5:n=4;continue;case 4:n=6,e=this.xmax,o=this.ymin;break;case 6:n=2;continue;case 2:n=10,e=this.xmax,o=this.ymax;break;case 10:n=8;continue;case 8:n=9,e=this.xmin,o=this.ymax;break;case 9:n=1;continue;case 1:n=5,e=this.xmin,o=this.ymin}r[i]===e&&r[i+1]===o||!this.contains(t,e,o)||(r.splice(i,0,e,o),i+=2)}return i}_project(t,n,e,r){let i,o,a,u=1/0;if(r<0){if(n<=this.ymin)return null;(i=(this.ymin-n)/r)<u&&(a=this.ymin,o=t+(u=i)*e)}else if(r>0){if(n>=this.ymax)return null;(i=(this.ymax-n)/r)<u&&(a=this.ymax,o=t+(u=i)*e)}if(e>0){if(t>=this.xmax)return null;(i=(this.xmax-t)/e)<u&&(o=this.xmax,a=n+(u=i)*r)}else if(e<0){if(t<=this.xmin)return null;(i=(this.xmin-t)/e)<u&&(o=this.xmin,a=n+(u=i)*r)}return[o,a]}_edgecode(t,n){return(t===this.xmin?1:t===this.xmax?2:0)|(n===this.ymin?4:n===this.ymax?8:0)}_regioncode(t,n){return(t<this.xmin?1:t>this.xmax?2:0)|(n<this.ymin?4:n>this.ymax?8:0)}_simplify(t){if(t&&t.length>4){for(let n=0;n<t.length;n+=2){const e=(n+2)%t.length,r=(n+4)%t.length;(t[n]===t[e]&&t[e]===t[r]||t[n+1]===t[e+1]&&t[e+1]===t[r+1])&&(t.splice(e,2),n-=2)}t.length||(t=null)}return t}}const Uu=2*Math.PI,Iu=Math.pow;function Ou(t){return t[0]}function Bu(t){return t[1]}function Yu(t,n,e){return[t+Math.sin(t+n)*e,n+Math.cos(t-n)*e]}class Lu{static from(t,n=Ou,e=Bu,r){return new Lu("length"in t?function(t,n,e,r){const i=t.length,o=new Float64Array(2*i);for(let a=0;a<i;++a){const i=t[a];o[2*a]=n.call(r,i,a,t),o[2*a+1]=e.call(r,i,a,t)}return o}(t,n,e,r):Float64Array.from(function*(t,n,e,r){let i=0;for(const o of t)yield n.call(r,o,i,t),yield e.call(r,o,i,t),++i}(t,n,e,r)))}constructor(t){this._delaunator=new Su(t),this.inedges=new Int32Array(t.length/2),this._hullIndex=new Int32Array(t.length/2),this.points=this._delaunator.coords,this._init()}update(){return this._delaunator.update(),this._init(),this}_init(){const t=this._delaunator,n=this.points;if(t.hull&&t.hull.length>2&&function(t){const{triangles:n,coords:e}=t;for(let t=0;t<n.length;t+=3){const r=2*n[t],i=2*n[t+1],o=2*n[t+2];if((e[o]-e[r])*(e[i+1]-e[r+1])-(e[i]-e[r])*(e[o+1]-e[r+1])>1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:n.length/2},((t,n)=>n)).sort(((t,e)=>n[2*t]-n[2*e]||n[2*t+1]-n[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],r=[n[2*t],n[2*t+1],n[2*e],n[2*e+1]],i=1e-8*Math.hypot(r[3]-r[1],r[2]-r[0]);for(let t=0,e=n.length/2;t<e;++t){const e=Yu(n[2*t],n[2*t+1],i);n[2*t]=e[0],n[2*t+1]=e[1]}this._delaunator=new Su(n)}else delete this.collinear;const e=this.halfedges=this._delaunator.halfedges,r=this.hull=this._delaunator.hull,i=this.triangles=this._delaunator.triangles,o=this.inedges.fill(-1),a=this._hullIndex.fill(-1);for(let t=0,n=e.length;t<n;++t){const n=i[t%3==2?t-2:t+1];-1!==e[t]&&-1!==o[n]||(o[n]=t)}for(let t=0,n=r.length;t<n;++t)a[r[t]]=t;r.length<=2&&r.length>0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],o[r[0]]=1,2===r.length&&(o[r[1]]=0,this.triangles[1]=r[1],this.triangles[2]=r[1]))}voronoi(t){return new qu(this,t)}*neighbors(t){const{inedges:n,hull:e,_hullIndex:r,halfedges:i,triangles:o,collinear:a}=this;if(a){const n=a.indexOf(t);return n>0&&(yield a[n-1]),void(n<a.length-1&&(yield a[n+1]))}const u=n[t];if(-1===u)return;let c=u,f=-1;do{if(yield f=o[c],c=c%3==2?c-2:c+1,o[c]!==t)return;if(c=i[c],-1===c){const n=e[(r[t]+1)%e.length];return void(n!==f&&(yield n))}}while(c!==u)}find(t,n,e=0){if((t=+t)!=t||(n=+n)!=n)return-1;const r=e;let i;for(;(i=this._step(e,t,n))>=0&&i!==e&&i!==r;)e=i;return i}_step(t,n,e){const{inedges:r,hull:i,_hullIndex:o,halfedges:a,triangles:u,points:c}=this;if(-1===r[t]||!c.length)return(t+1)%(c.length>>1);let f=t,s=Iu(n-c[2*t],2)+Iu(e-c[2*t+1],2);const l=r[t];let h=l;do{let r=u[h];const l=Iu(n-c[2*r],2)+Iu(e-c[2*r+1],2);if(l<s&&(s=l,f=r),h=h%3==2?h-2:h+1,u[h]!==t)break;if(h=a[h],-1===h){if(h=i[(o[t]+1)%i.length],h!==r&&Iu(n-c[2*h],2)+Iu(e-c[2*h+1],2)<s)return h;break}}while(h!==l);return f}render(t){const n=null==t?t=new Ru:void 0,{points:e,halfedges:r,triangles:i}=this;for(let n=0,o=r.length;n<o;++n){const o=r[n];if(o<n)continue;const a=2*i[n],u=2*i[o];t.moveTo(e[a],e[a+1]),t.lineTo(e[u],e[u+1])}return this.renderHull(t),n&&n.value()}renderPoints(t,n){void 0!==n||t&&"function"==typeof t.moveTo||(n=t,t=null),n=null==n?2:+n;const e=null==t?t=new Ru:void 0,{points:r}=this;for(let e=0,i=r.length;e<i;e+=2){const i=r[e],o=r[e+1];t.moveTo(i+n,o),t.arc(i,o,n,0,Uu)}return e&&e.value()}renderHull(t){const n=null==t?t=new Ru:void 0,{hull:e,points:r}=this,i=2*e[0],o=e.length;t.moveTo(r[i],r[i+1]);for(let n=1;n<o;++n){const i=2*e[n];t.lineTo(r[i],r[i+1])}return t.closePath(),n&&n.value()}hullPolygon(){const t=new Fu;return this.renderHull(t),t.value()}renderTriangle(t,n){const e=null==n?n=new Ru:void 0,{points:r,triangles:i}=this,o=2*i[t*=3],a=2*i[t+1],u=2*i[t+2];return n.moveTo(r[o],r[o+1]),n.lineTo(r[a],r[a+1]),n.lineTo(r[u],r[u+1]),n.closePath(),e&&e.value()}*trianglePolygons(){const{triangles:t}=this;for(let n=0,e=t.length/3;n<e;++n)yield this.trianglePolygon(n)}trianglePolygon(t){const n=new Fu;return this.renderTriangle(t,n),n.value()}}var ju={},Hu={},Xu=34,Gu=10,Vu=13;function Wu(t){return new Function("d","return {"+t.map((function(t,n){return JSON.stringify(t)+": d["+n+'] || ""'})).join(",")+"}")}function Zu(t){var n=Object.create(null),e=[];return t.forEach((function(t){for(var r in t)r in n||e.push(n[r]=r)})),e}function Ku(t,n){var e=t+"",r=e.length;return r<n?new Array(n-r+1).join(0)+e:e}function Qu(t){var n,e=t.getUTCHours(),r=t.getUTCMinutes(),i=t.getUTCSeconds(),o=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":((n=t.getUTCFullYear())<0?"-"+Ku(-n,6):n>9999?"+"+Ku(n,6):Ku(n,4))+"-"+Ku(t.getUTCMonth()+1,2)+"-"+Ku(t.getUTCDate(),2)+(o?"T"+Ku(e,2)+":"+Ku(r,2)+":"+Ku(i,2)+"."+Ku(o,3)+"Z":i?"T"+Ku(e,2)+":"+Ku(r,2)+":"+Ku(i,2)+"Z":r||e?"T"+Ku(e,2)+":"+Ku(r,2)+"Z":"")}function Ju(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return Hu;if(f)return f=!1,ju;var n,r,i=a;if(t.charCodeAt(i)===Xu){for(;a++<o&&t.charCodeAt(a)!==Xu||t.charCodeAt(++a)===Xu;);return(n=a)>=o?c=!0:(r=t.charCodeAt(a++))===Gu?f=!0:r===Vu&&(f=!0,t.charCodeAt(a)===Gu&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;a<o;){if((r=t.charCodeAt(n=a++))===Gu)f=!0;else if(r===Vu)f=!0,t.charCodeAt(a)===Gu&&++a;else if(r!==e)continue;return t.slice(i,n)}return c=!0,t.slice(i,o)}for(t.charCodeAt(o-1)===Gu&&--o,t.charCodeAt(o-1)===Vu&&--o;(r=s())!==Hu;){for(var l=[];r!==ju&&r!==Hu;)l.push(r),r=s();n&&null==(l=n(l,u++))||i.push(l)}return i}function i(n,e){return n.map((function(n){return e.map((function(t){return a(n[t])})).join(t)}))}function o(n){return n.map(a).join(t)}function a(t){return null==t?"":t instanceof Date?Qu(t):n.test(t+="")?'"'+t.replace(/"/g,'""')+'"':t}return{parse:function(t,n){var e,i,o=r(t,(function(t,r){if(e)return e(t,r-1);i=t,e=n?function(t,n){var e=Wu(t);return function(r,i){return n(e(r),i,t)}}(t,n):Wu(t)}));return o.columns=i||[],o},parseRows:r,format:function(n,e){return null==e&&(e=Zu(n)),[e.map(a).join(t)].concat(i(n,e)).join("\n")},formatBody:function(t,n){return null==n&&(n=Zu(t)),i(t,n).join("\n")},formatRows:function(t){return t.map(o).join("\n")},formatRow:o,formatValue:a}}var tc=Ju(","),nc=tc.parse,ec=tc.parseRows,rc=tc.format,ic=tc.formatBody,oc=tc.formatRows,ac=tc.formatRow,uc=tc.formatValue,cc=Ju("\t"),fc=cc.parse,sc=cc.parseRows,lc=cc.format,hc=cc.formatBody,dc=cc.formatRows,pc=cc.formatRow,gc=cc.formatValue;const yc=new Date("2019-01-01T00:00").getHours()||new Date("2019-07-01T00:00").getHours();function vc(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.blob()}function _c(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.arrayBuffer()}function bc(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.text()}function mc(t,n){return fetch(t,n).then(bc)}function xc(t){return function(n,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=void 0),mc(n,e).then((function(n){return t(n,r)}))}}var wc=xc(nc),Mc=xc(fc);function Tc(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);if(204!==t.status&&205!==t.status)return t.json()}function Ac(t){return(n,e)=>mc(n,e).then((n=>(new DOMParser).parseFromString(n,t)))}var Sc=Ac("application/xml"),Ec=Ac("text/html"),Nc=Ac("image/svg+xml");function kc(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,c,f,s,l,h,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function Cc(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Pc(t){return t[0]}function zc(t){return t[1]}function $c(t,n,e){var r=new Dc(null==n?Pc:n,null==e?zc:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Dc(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Rc(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var Fc=$c.prototype=Dc.prototype;function qc(t){return function(){return t}}function Uc(t){return 1e-6*(t()-.5)}function Ic(t){return t.x+t.vx}function Oc(t){return t.y+t.vy}function Bc(t){return t.index}function Yc(t,n){var e=t.get(n);if(!e)throw new Error("node not found: "+n);return e}Fc.copy=function(){var t,n,e=new Dc(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Rc(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Rc(n));return e},Fc.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return kc(this.cover(n,e),n,e,t)},Fc.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;e<o;++e)isNaN(r=+this._x.call(null,n=t[e]))||isNaN(i=+this._y.call(null,n))||(a[e]=r,u[e]=i,r<c&&(c=r),r>s&&(s=r),i<f&&(f=i),i>l&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;e<o;++e)kc(this,a[e],u[e],t[e]);return this},Fc.cover=function(t,n){if(isNaN(t=+t)||isNaN(n=+n))return this;var e=this._x0,r=this._y0,i=this._x1,o=this._y1;if(isNaN(e))i=(e=Math.floor(t))+1,o=(r=Math.floor(n))+1;else{for(var a,u,c=i-e||1,f=this._root;e>t||t>=i||r>n||n>=o;)switch(u=(n<r)<<1|t<e,(a=new Array(4))[u]=f,f=a,c*=2,u){case 0:i=e+c,o=r+c;break;case 1:e=i-c,o=r+c;break;case 2:i=e+c,r=o-c;break;case 3:e=i-c,r=o-c}this._root&&this._root.length&&(this._root=f)}return this._x0=e,this._y0=r,this._x1=i,this._y1=o,this},Fc.data=function(){var t=[];return this.visit((function(n){if(!n.length)do{t.push(n.data)}while(n=n.next)})),t},Fc.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},Fc.find=function(t,n,e){var r,i,o,a,u,c,f,s=this._x0,l=this._y0,h=this._x1,d=this._y1,p=[],g=this._root;for(g&&p.push(new Cc(g,s,l,h,d)),null==e?e=1/0:(s=t-e,l=n-e,h=t+e,d=n+e,e*=e);c=p.pop();)if(!(!(g=c.node)||(i=c.x0)>h||(o=c.y0)>d||(a=c.x1)<s||(u=c.y1)<l))if(g.length){var y=(i+a)/2,v=(o+u)/2;p.push(new Cc(g[3],y,v,a,u),new Cc(g[2],i,v,y,u),new Cc(g[1],y,o,a,v),new Cc(g[0],i,o,y,v)),(f=(n>=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,g.data),b=n-+this._y.call(null,g.data),m=_*_+b*b;if(m<e){var x=Math.sqrt(e=m);s=t-x,l=n-x,h=t+x,d=n+x,r=g.data}}return r},Fc.remove=function(t){if(isNaN(o=+this._x.call(null,t))||isNaN(a=+this._y.call(null,t)))return this;var n,e,r,i,o,a,u,c,f,s,l,h,d=this._root,p=this._x0,g=this._y0,y=this._x1,v=this._y1;if(!d)return this;if(d.length)for(;;){if((f=o>=(u=(p+y)/2))?p=u:y=u,(s=a>=(c=(g+v)/2))?g=c:v=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},Fc.removeAll=function(t){for(var n=0,e=t.length;n<e;++n)this.remove(t[n]);return this},Fc.root=function(){return this._root},Fc.size=function(){var t=0;return this.visit((function(n){if(!n.length)do{++t}while(n=n.next)})),t},Fc.visit=function(t){var n,e,r,i,o,a,u=[],c=this._root;for(c&&u.push(new Cc(c,this._x0,this._y0,this._x1,this._y1));n=u.pop();)if(!t(c=n.node,r=n.x0,i=n.y0,o=n.x1,a=n.y1)&&c.length){var f=(r+o)/2,s=(i+a)/2;(e=c[3])&&u.push(new Cc(e,f,s,o,a)),(e=c[2])&&u.push(new Cc(e,r,s,f,a)),(e=c[1])&&u.push(new Cc(e,f,i,o,s)),(e=c[0])&&u.push(new Cc(e,r,i,f,s))}return this},Fc.visitAfter=function(t){var n,e=[],r=[];for(this._root&&e.push(new Cc(this._root,this._x0,this._y0,this._x1,this._y1));n=e.pop();){var i=n.node;if(i.length){var o,a=n.x0,u=n.y0,c=n.x1,f=n.y1,s=(a+c)/2,l=(u+f)/2;(o=i[0])&&e.push(new Cc(o,a,u,s,l)),(o=i[1])&&e.push(new Cc(o,s,u,c,l)),(o=i[2])&&e.push(new Cc(o,a,l,s,f)),(o=i[3])&&e.push(new Cc(o,s,l,c,f))}r.push(n)}for(;n=r.pop();)t(n.node,n.x0,n.y0,n.x1,n.y1);return this},Fc.x=function(t){return arguments.length?(this._x=t,this):this._x},Fc.y=function(t){return arguments.length?(this._y=t,this):this._y};const Lc=1664525,jc=1013904223,Hc=4294967296;function Xc(t){return t.x}function Gc(t){return t.y}var Vc=Math.PI*(3-Math.sqrt(5));function Wc(t,n){if((e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"))<0)return null;var e,r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Zc(t){return(t=Wc(Math.abs(t)))?t[1]:NaN}var Kc,Qc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Jc(t){if(!(n=Qc.exec(t)))throw new Error("invalid format: "+t);var n;return new tf({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function tf(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function nf(t,n){var e=Wc(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}Jc.prototype=tf.prototype,tf.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var ef={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>nf(100*t,n),r:nf,s:function(t,n){var e=Wc(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(Kc=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+Wc(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function rf(t){return t}var of,af=Array.prototype.map,uf=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function cf(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?rf:(n=af.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",a=void 0===t.decimal?".":t.decimal+"",u=void 0===t.numerals?rf:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(af.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",s=void 0===t.nan?"NaN":t.nan+"";function l(t){var n=(t=Jc(t)).fill,e=t.align,l=t.sign,h=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,_=t.type;"n"===_?(g=!0,_="g"):ef[_]||(void 0===y&&(y=12),v=!0,_="g"),(d||"0"===n&&"="===e)&&(d=!0,n="0",e="=");var b="$"===h?i:"#"===h&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",m="$"===h?o:/[%p]/.test(_)?c:"",x=ef[_],w=/[defgprs%]/.test(_);function M(t){var i,o,c,h=b,M=m;if("c"===_)M=x(t)+M,t="";else{var T=(t=+t)<0||1/t<0;if(t=isNaN(t)?s:x(Math.abs(t),y),v&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r<e;++r)switch(t[r]){case".":i=n=r;break;case"0":0===i&&(i=r),n=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),T&&0==+t&&"+"!==l&&(T=!1),h=(T?"("===l?l:f:"-"===l||"("===l?"":l)+h,M=("s"===_?uf[8+Kc/3]:"")+M+(T&&"("===l?")":""),w)for(i=-1,o=t.length;++i<o;)if(48>(c=t.charCodeAt(i))||c>57){M=(46===c?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var A=h.length+t.length+M.length,S=A<p?new Array(p-A+1).join(n):"";switch(g&&d&&(t=r(S+t,S.length?p-M.length:1/0),S=""),e){case"<":t=h+t+M+S;break;case"=":t=h+S+t+M;break;case"^":t=S.slice(0,A=S.length>>1)+h+t+M+S.slice(A);break;default:t=S+h+t+M}return u(t)}return y=void 0===y?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),M.toString=function(){return t+""},M}return{format:l,formatPrefix:function(t,n){var e=l(((t=Jc(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(Zc(n)/3))),i=Math.pow(10,-r),o=uf[8+r/3];return function(t){return e(i*t)+o}}}}function ff(n){return of=cf(n),t.format=of.format,t.formatPrefix=of.formatPrefix,of}function sf(t){return Math.max(0,-Zc(Math.abs(t)))}function lf(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Zc(n)/3)))-Zc(Math.abs(t)))}function hf(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Zc(n)-Zc(t))+1}t.format=void 0,t.formatPrefix=void 0,ff({thousands:",",grouping:[3],currency:["$",""]});var df=1e-6,pf=1e-12,gf=Math.PI,yf=gf/2,vf=gf/4,_f=2*gf,bf=180/gf,mf=gf/180,xf=Math.abs,wf=Math.atan,Mf=Math.atan2,Tf=Math.cos,Af=Math.ceil,Sf=Math.exp,Ef=Math.hypot,Nf=Math.log,kf=Math.pow,Cf=Math.sin,Pf=Math.sign||function(t){return t>0?1:t<0?-1:0},zf=Math.sqrt,$f=Math.tan;function Df(t){return t>1?0:t<-1?gf:Math.acos(t)}function Rf(t){return t>1?yf:t<-1?-yf:Math.asin(t)}function Ff(t){return(t=Cf(t/2))*t}function qf(){}function Uf(t,n){t&&Of.hasOwnProperty(t.type)&&Of[t.type](t,n)}var If={Feature:function(t,n){Uf(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r<i;)Uf(e[r].geometry,n)}},Of={Sphere:function(t,n){n.sphere()},Point:function(t,n){t=t.coordinates,n.point(t[0],t[1],t[2])},MultiPoint:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)t=e[r],n.point(t[0],t[1],t[2])},LineString:function(t,n){Bf(t.coordinates,n,0)},MultiLineString:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)Bf(e[r],n,0)},Polygon:function(t,n){Yf(t.coordinates,n)},MultiPolygon:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)Yf(e[r],n)},GeometryCollection:function(t,n){for(var e=t.geometries,r=-1,i=e.length;++r<i;)Uf(e[r],n)}};function Bf(t,n,e){var r,i=-1,o=t.length-e;for(n.lineStart();++i<o;)r=t[i],n.point(r[0],r[1],r[2]);n.lineEnd()}function Yf(t,n){var e=-1,r=t.length;for(n.polygonStart();++e<r;)Bf(t[e],n,1);n.polygonEnd()}function Lf(t,n){t&&If.hasOwnProperty(t.type)?If[t.type](t,n):Uf(t,n)}var jf,Hf,Xf,Gf,Vf,Wf,Zf,Kf,Qf,Jf,ts,ns,es,rs,is,os,as=new T,us=new T,cs={point:qf,lineStart:qf,lineEnd:qf,polygonStart:function(){as=new T,cs.lineStart=fs,cs.lineEnd=ss},polygonEnd:function(){var t=+as;us.add(t<0?_f+t:t),this.lineStart=this.lineEnd=this.point=qf},sphere:function(){us.add(_f)}};function fs(){cs.point=ls}function ss(){hs(jf,Hf)}function ls(t,n){cs.point=hs,jf=t,Hf=n,Xf=t*=mf,Gf=Tf(n=(n*=mf)/2+vf),Vf=Cf(n)}function hs(t,n){var e=(t*=mf)-Xf,r=e>=0?1:-1,i=r*e,o=Tf(n=(n*=mf)/2+vf),a=Cf(n),u=Vf*a,c=Gf*o+u*Tf(i),f=u*r*Cf(i);as.add(Mf(f,c)),Xf=t,Gf=o,Vf=a}function ds(t){return[Mf(t[1],t[0]),Rf(t[2])]}function ps(t){var n=t[0],e=t[1],r=Tf(e);return[r*Tf(n),r*Cf(n),Cf(e)]}function gs(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function ys(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function vs(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function _s(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function bs(t){var n=zf(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var ms,xs,ws,Ms,Ts,As,Ss,Es,Ns,ks,Cs,Ps,zs,$s,Ds,Rs,Fs={point:qs,lineStart:Is,lineEnd:Os,polygonStart:function(){Fs.point=Bs,Fs.lineStart=Ys,Fs.lineEnd=Ls,rs=new T,cs.polygonStart()},polygonEnd:function(){cs.polygonEnd(),Fs.point=qs,Fs.lineStart=Is,Fs.lineEnd=Os,as<0?(Wf=-(Kf=180),Zf=-(Qf=90)):rs>df?Qf=90:rs<-df&&(Zf=-90),os[0]=Wf,os[1]=Kf},sphere:function(){Wf=-(Kf=180),Zf=-(Qf=90)}};function qs(t,n){is.push(os=[Wf=t,Kf=t]),n<Zf&&(Zf=n),n>Qf&&(Qf=n)}function Us(t,n){var e=ps([t*mf,n*mf]);if(es){var r=ys(es,e),i=ys([r[1],-r[0],0],r);bs(i),i=ds(i);var o,a=t-Jf,u=a>0?1:-1,c=i[0]*bf*u,f=xf(a)>180;f^(u*Jf<c&&c<u*t)?(o=i[1]*bf)>Qf&&(Qf=o):f^(u*Jf<(c=(c+360)%360-180)&&c<u*t)?(o=-i[1]*bf)<Zf&&(Zf=o):(n<Zf&&(Zf=n),n>Qf&&(Qf=n)),f?t<Jf?js(Wf,t)>js(Wf,Kf)&&(Kf=t):js(t,Kf)>js(Wf,Kf)&&(Wf=t):Kf>=Wf?(t<Wf&&(Wf=t),t>Kf&&(Kf=t)):t>Jf?js(Wf,t)>js(Wf,Kf)&&(Kf=t):js(t,Kf)>js(Wf,Kf)&&(Wf=t)}else is.push(os=[Wf=t,Kf=t]);n<Zf&&(Zf=n),n>Qf&&(Qf=n),es=e,Jf=t}function Is(){Fs.point=Us}function Os(){os[0]=Wf,os[1]=Kf,Fs.point=qs,es=null}function Bs(t,n){if(es){var e=t-Jf;rs.add(xf(e)>180?e+(e>0?360:-360):e)}else ts=t,ns=n;cs.point(t,n),Us(t,n)}function Ys(){cs.lineStart()}function Ls(){Bs(ts,ns),cs.lineEnd(),xf(rs)>df&&(Wf=-(Kf=180)),os[0]=Wf,os[1]=Kf,es=null}function js(t,n){return(n-=t)<0?n+360:n}function Hs(t,n){return t[0]-n[0]}function Xs(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var Gs={sphere:qf,point:Vs,lineStart:Zs,lineEnd:Js,polygonStart:function(){Gs.lineStart=tl,Gs.lineEnd=nl},polygonEnd:function(){Gs.lineStart=Zs,Gs.lineEnd=Js}};function Vs(t,n){t*=mf;var e=Tf(n*=mf);Ws(e*Tf(t),e*Cf(t),Cf(n))}function Ws(t,n,e){++ms,ws+=(t-ws)/ms,Ms+=(n-Ms)/ms,Ts+=(e-Ts)/ms}function Zs(){Gs.point=Ks}function Ks(t,n){t*=mf;var e=Tf(n*=mf);$s=e*Tf(t),Ds=e*Cf(t),Rs=Cf(n),Gs.point=Qs,Ws($s,Ds,Rs)}function Qs(t,n){t*=mf;var e=Tf(n*=mf),r=e*Tf(t),i=e*Cf(t),o=Cf(n),a=Mf(zf((a=Ds*o-Rs*i)*a+(a=Rs*r-$s*o)*a+(a=$s*i-Ds*r)*a),$s*r+Ds*i+Rs*o);xs+=a,As+=a*($s+($s=r)),Ss+=a*(Ds+(Ds=i)),Es+=a*(Rs+(Rs=o)),Ws($s,Ds,Rs)}function Js(){Gs.point=Vs}function tl(){Gs.point=el}function nl(){rl(Ps,zs),Gs.point=Vs}function el(t,n){Ps=t,zs=n,t*=mf,n*=mf,Gs.point=rl;var e=Tf(n);$s=e*Tf(t),Ds=e*Cf(t),Rs=Cf(n),Ws($s,Ds,Rs)}function rl(t,n){t*=mf;var e=Tf(n*=mf),r=e*Tf(t),i=e*Cf(t),o=Cf(n),a=Ds*o-Rs*i,u=Rs*r-$s*o,c=$s*i-Ds*r,f=Ef(a,u,c),s=Rf(f),l=f&&-s/f;Ns.add(l*a),ks.add(l*u),Cs.add(l*c),xs+=s,As+=s*($s+($s=r)),Ss+=s*(Ds+(Ds=i)),Es+=s*(Rs+(Rs=o)),Ws($s,Ds,Rs)}function il(t){return function(){return t}}function ol(t,n){function e(e,r){return e=t(e,r),n(e[0],e[1])}return t.invert&&n.invert&&(e.invert=function(e,r){return(e=n.invert(e,r))&&t.invert(e[0],e[1])}),e}function al(t,n){return xf(t)>gf&&(t-=Math.round(t/_f)*_f),[t,n]}function ul(t,n,e){return(t%=_f)?n||e?ol(fl(t),sl(n,e)):fl(t):n||e?sl(n,e):al}function cl(t){return function(n,e){return xf(n+=t)>gf&&(n-=Math.round(n/_f)*_f),[n,e]}}function fl(t){var n=cl(t);return n.invert=cl(-t),n}function sl(t,n){var e=Tf(t),r=Cf(t),i=Tf(n),o=Cf(n);function a(t,n){var a=Tf(n),u=Tf(t)*a,c=Cf(t)*a,f=Cf(n),s=f*e+u*r;return[Mf(c*i-s*o,u*e-f*r),Rf(s*i+c*o)]}return a.invert=function(t,n){var a=Tf(n),u=Tf(t)*a,c=Cf(t)*a,f=Cf(n),s=f*i-c*o;return[Mf(c*i+f*o,u*e+s*r),Rf(s*e-u*r)]},a}function ll(t){function n(n){return(n=t(n[0]*mf,n[1]*mf))[0]*=bf,n[1]*=bf,n}return t=ul(t[0]*mf,t[1]*mf,t.length>2?t[2]*mf:0),n.invert=function(n){return(n=t.invert(n[0]*mf,n[1]*mf))[0]*=bf,n[1]*=bf,n},n}function hl(t,n,e,r,i,o){if(e){var a=Tf(n),u=Cf(n),c=r*e;null==i?(i=n+r*_f,o=n-c/2):(i=dl(a,i),o=dl(a,o),(r>0?i<o:i>o)&&(i+=r*_f));for(var f,s=i;r>0?s>o:s<o;s-=c)f=ds([a,-u*Tf(s),-u*Cf(s)]),t.point(f[0],f[1])}}function dl(t,n){(n=ps(n))[0]-=t,bs(n);var e=Df(-n[1]);return((-n[2]<0?-e:e)+_f-df)%_f}function pl(){var t,n=[];return{point:function(n,e,r){t.push([n,e,r])},lineStart:function(){n.push(t=[])},lineEnd:qf,rejoin:function(){n.length>1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function gl(t,n){return xf(t[0]-n[0])<df&&xf(t[1]-n[1])<df}function yl(t,n,e,r){this.x=t,this.z=n,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function vl(t,n,e,r,i){var o,a,u=[],c=[];if(t.forEach((function(t){if(!((n=t.length-1)<=0)){var n,e,r=t[0],a=t[n];if(gl(r,a)){if(!r[2]&&!a[2]){for(i.lineStart(),o=0;o<n;++o)i.point((r=t[o])[0],r[1]);return void i.lineEnd()}a[0]+=2*df}u.push(e=new yl(r,t,null,!0)),c.push(e.o=new yl(r,null,e,!1)),u.push(e=new yl(a,t,null,!1)),c.push(e.o=new yl(a,null,e,!0))}})),u.length){for(c.sort(n),_l(u),_l(c),o=0,a=c.length;o<a;++o)c[o].e=e=!e;for(var f,s,l=u[0];;){for(var h=l,d=!0;h.v;)if((h=h.n)===l)return;f=h.z,i.lineStart();do{if(h.v=h.o.v=!0,h.e){if(d)for(o=0,a=f.length;o<a;++o)i.point((s=f[o])[0],s[1]);else r(h.x,h.n.x,1,i);h=h.n}else{if(d)for(f=h.p.z,o=f.length-1;o>=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function _l(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r<n;)i.n=e=t[r],e.p=i,i=e;i.n=e=t[0],e.p=i}}function bl(t){return xf(t[0])<=gf?t[0]:Pf(t[0])*((xf(t[0])+gf)%_f-gf)}function ml(t,n){var e=bl(n),r=n[1],i=Cf(r),o=[Cf(e),-Tf(e),0],a=0,u=0,c=new T;1===i?r=yf+df:-1===i&&(r=-yf-df);for(var f=0,s=t.length;f<s;++f)if(h=(l=t[f]).length)for(var l,h,d=l[h-1],p=bl(d),g=d[1]/2+vf,y=Cf(g),v=Tf(g),_=0;_<h;++_,p=m,y=w,v=M,d=b){var b=l[_],m=bl(b),x=b[1]/2+vf,w=Cf(x),M=Tf(x),A=m-p,S=A>=0?1:-1,E=S*A,N=E>gf,k=y*w;if(c.add(Mf(k*S*Cf(E),v*M+k*Tf(E))),a+=N?A+S*_f:A,N^p>=e^m>=e){var C=ys(ps(d),ps(b));bs(C);var P=ys(o,C);bs(P);var z=(N^A>=0?-1:1)*Rf(P[2]);(r>z||r===z&&(C[0]||C[1]))&&(u+=N^A>=0?1:-1)}}return(a<-df||a<df&&c<-pf)^1&u}function xl(t,n,e,r){return function(i){var o,a,u,c=n(i),f=pl(),s=n(f),l=!1,h={point:d,lineStart:g,lineEnd:y,polygonStart:function(){h.point=v,h.lineStart=_,h.lineEnd=b,a=[],o=[]},polygonEnd:function(){h.point=d,h.lineStart=g,h.lineEnd=y,a=ft(a);var t=ml(o,r);a.length?(l||(i.polygonStart(),l=!0),vl(a,Ml,t,e,i)):t&&(l||(i.polygonStart(),l=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),l&&(i.polygonEnd(),l=!1),a=o=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}};function d(n,e){t(n,e)&&i.point(n,e)}function p(t,n){c.point(t,n)}function g(){h.point=p,c.lineStart()}function y(){h.point=d,c.lineEnd()}function v(t,n){u.push([t,n]),s.point(t,n)}function _(){s.lineStart(),u=[]}function b(){v(u[0][0],u[0][1]),s.lineEnd();var t,n,e,r,c=s.clean(),h=f.result(),d=h.length;if(u.pop(),o.push(u),u=null,d)if(1&c){if((n=(e=h[0]).length-1)>0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t<n;++t)i.point((r=e[t])[0],r[1]);i.lineEnd()}}else d>1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(wl))}return h}}function wl(t){return t.length>1}function Ml(t,n){return((t=t.x)[0]<0?t[1]-yf-df:yf-t[1])-((n=n.x)[0]<0?n[1]-yf-df:yf-n[1])}al.invert=al;var Tl=xl((function(){return!0}),(function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?gf:-gf,c=xf(o-e);xf(c-gf)<df?(t.point(e,r=(r+a)/2>0?yf:-yf),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=gf&&(xf(e-i)<df&&(e-=i*df),xf(o-u)<df&&(o-=u*df),r=function(t,n,e,r){var i,o,a=Cf(t-e);return xf(a)>df?wf((Cf(n)*(o=Tf(r))*Cf(e)-Cf(r)*(i=Tf(n))*Cf(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}}),(function(t,n,e,r){var i;if(null==t)i=e*yf,r.point(-gf,i),r.point(0,i),r.point(gf,i),r.point(gf,0),r.point(gf,-i),r.point(0,-i),r.point(-gf,-i),r.point(-gf,0),r.point(-gf,i);else if(xf(t[0]-n[0])>df){var o=t[0]<n[0]?gf:-gf;i=e*o/2,r.point(-o,i),r.point(0,i),r.point(o,i)}else r.point(n[0],n[1])}),[-gf,-yf]);function Al(t){var n=Tf(t),e=2*mf,r=n>0,i=xf(n)>df;function o(t,e){return Tf(t)*Tf(e)>n}function a(t,e,r){var i=[1,0,0],o=ys(ps(t),ps(e)),a=gs(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=ys(i,o),h=_s(i,f);vs(h,_s(o,s));var d=l,p=gs(h,d),g=gs(d,d),y=p*p-g*(gs(h,h)-1);if(!(y<0)){var v=zf(y),_=_s(d,(-p-v)/g);if(vs(_,h),_=ds(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x<m&&(b=m,m=x,x=b);var T=x-m,A=xf(T-gf)<df;if(!A&&M<w&&(b=w,w=M,M=b),A||T<df?A?w+M>0^_[1]<(xf(_[0]-m)<df?w:M):w<=_[1]&&_[1]<=M:T>gf^(m<=_[0]&&_[0]<=x)){var S=_s(d,(-p+v)/g);return vs(S,h),[_,ds(S)]}}}function u(n,e){var i=r?t:gf-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return xl(o,(function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],g=o(l,h),y=r?g?0:u(l,h):g?u(l+(l<0?gf:-gf),h):0;if(!n&&(f=c=g)&&t.lineStart(),g!==c&&(!(d=a(n,p))||gl(n,d)||gl(p,d))&&(p[2]=1),g!==c)s=0,g?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1],2),t.lineEnd()),n=d;else if(i&&n&&r^g){var v;y&e||!(v=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1],3)))}!g||n&&gl(n,p)||t.point(p[0],p[1]),n=p,c=g,e=y},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}}),(function(n,r,i,o){hl(o,t,e,i,n,r)}),r?[0,-t]:[-gf,t-gf])}var Sl,El,Nl,kl,Cl=1e9,Pl=-Cl;function zl(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return xf(r[0]-t)<df?i>0?0:3:xf(r[0]-e)<df?i>0?2:1:xf(r[1]-n)<df?i>0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,g,y,v,_,b=a,m=pl(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(M(l,h),d&&y&&m.rejoin(),c.push(m.result()));x.point=w,y&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;e<i;++e)for(var o,a,u=f[e],c=1,s=u.length,l=u[0],h=l[0],d=l[1];c<s;++c)o=h,a=d,h=(l=u[c])[0],d=l[1],a<=r?d>r&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=ft(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&vl(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),v)l=o,h=a,d=u,v=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&y)b.point(o,a);else{var c=[p=Math.max(Pl,Math.min(Cl,p)),g=Math.max(Pl,Math.min(Cl,g))],m=[o=Math.max(Pl,Math.min(Cl,o)),a=Math.max(Pl,Math.min(Cl,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a<f)return;a<s&&(s=a)}else if(l>0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a<f)return;a<s&&(s=a)}if(a=r-c,h||!(a>0)){if(a/=h,h<0){if(a<f)return;a<s&&(s=a)}else if(h>0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a<f)return;a<s&&(s=a)}return f>0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,g=a,y=u}return x}}var $l={sphere:qf,point:qf,lineStart:function(){$l.point=Rl,$l.lineEnd=Dl},lineEnd:qf,polygonStart:qf,polygonEnd:qf};function Dl(){$l.point=$l.lineEnd=qf}function Rl(t,n){El=t*=mf,Nl=Cf(n*=mf),kl=Tf(n),$l.point=Fl}function Fl(t,n){t*=mf;var e=Cf(n*=mf),r=Tf(n),i=xf(t-El),o=Tf(i),a=r*Cf(i),u=kl*e-Nl*r*o,c=Nl*e+kl*r*o;Sl.add(Mf(zf(a*a+u*u),c)),El=t,Nl=e,kl=r}function ql(t){return Sl=new T,Lf(t,$l),+Sl}var Ul=[null,null],Il={type:"LineString",coordinates:Ul};function Ol(t,n){return Ul[0]=t,Ul[1]=n,ql(Il)}var Bl={Feature:function(t,n){return Ll(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r<i;)if(Ll(e[r].geometry,n))return!0;return!1}},Yl={Sphere:function(){return!0},Point:function(t,n){return jl(t.coordinates,n)},MultiPoint:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)if(jl(e[r],n))return!0;return!1},LineString:function(t,n){return Hl(t.coordinates,n)},MultiLineString:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)if(Hl(e[r],n))return!0;return!1},Polygon:function(t,n){return Xl(t.coordinates,n)},MultiPolygon:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)if(Xl(e[r],n))return!0;return!1},GeometryCollection:function(t,n){for(var e=t.geometries,r=-1,i=e.length;++r<i;)if(Ll(e[r],n))return!0;return!1}};function Ll(t,n){return!(!t||!Yl.hasOwnProperty(t.type))&&Yl[t.type](t,n)}function jl(t,n){return 0===Ol(t,n)}function Hl(t,n){for(var e,r,i,o=0,a=t.length;o<a;o++){if(0===(r=Ol(t[o],n)))return!0;if(o>0&&(i=Ol(t[o],t[o-1]))>0&&e<=i&&r<=i&&(e+r-i)*(1-Math.pow((e-r)/i,2))<pf*i)return!0;e=r}return!1}function Xl(t,n){return!!ml(t.map(Gl),Vl(n))}function Gl(t){return(t=t.map(Vl)).pop(),t}function Vl(t){return[t[0]*mf,t[1]*mf]}function Wl(t,n,e){var r=lt(t,n-df,e).concat(n);return function(t){return r.map((function(n){return[t,n]}))}}function Zl(t,n,e){var r=lt(t,n-df,e).concat(n);return function(t){return r.map((function(n){return[n,t]}))}}function Kl(){var t,n,e,r,i,o,a,u,c,f,s,l,h=10,d=h,p=90,g=360,y=2.5;function v(){return{type:"MultiLineString",coordinates:_()}}function _(){return lt(Af(r/p)*p,e,p).map(s).concat(lt(Af(u/g)*g,a,g).map(l)).concat(lt(Af(n/h)*h,t,h).filter((function(t){return xf(t%p)>df})).map(c)).concat(lt(Af(o/d)*d,i,d).filter((function(t){return xf(t%g)>df})).map(f))}return v.lines=function(){return _().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),v.precision(y)):[[r,u],[e,a]]},v.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),v.precision(y)):[[n,o],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],v):[h,d]},v.precision=function(h){return arguments.length?(y=+h,c=Wl(o,i,90),f=Zl(n,t,y),s=Wl(u,a,90),l=Zl(r,e,y),v):y},v.extentMajor([[-180,-90+df],[180,90-df]]).extentMinor([[-180,-80-df],[180,80+df]])}var Ql,Jl,th,nh,eh=t=>t,rh=new T,ih=new T,oh={point:qf,lineStart:qf,lineEnd:qf,polygonStart:function(){oh.lineStart=ah,oh.lineEnd=fh},polygonEnd:function(){oh.lineStart=oh.lineEnd=oh.point=qf,rh.add(xf(ih)),ih=new T},result:function(){var t=rh/2;return rh=new T,t}};function ah(){oh.point=uh}function uh(t,n){oh.point=ch,Ql=th=t,Jl=nh=n}function ch(t,n){ih.add(nh*t-th*n),th=t,nh=n}function fh(){ch(Ql,Jl)}var sh=oh,lh=1/0,hh=lh,dh=-lh,ph=dh,gh={point:function(t,n){t<lh&&(lh=t);t>dh&&(dh=t);n<hh&&(hh=n);n>ph&&(ph=n)},lineStart:qf,lineEnd:qf,polygonStart:qf,polygonEnd:qf,result:function(){var t=[[lh,hh],[dh,ph]];return dh=ph=-(hh=lh=1/0),t}};var yh,vh,_h,bh,mh=gh,xh=0,wh=0,Mh=0,Th=0,Ah=0,Sh=0,Eh=0,Nh=0,kh=0,Ch={point:Ph,lineStart:zh,lineEnd:Rh,polygonStart:function(){Ch.lineStart=Fh,Ch.lineEnd=qh},polygonEnd:function(){Ch.point=Ph,Ch.lineStart=zh,Ch.lineEnd=Rh},result:function(){var t=kh?[Eh/kh,Nh/kh]:Sh?[Th/Sh,Ah/Sh]:Mh?[xh/Mh,wh/Mh]:[NaN,NaN];return xh=wh=Mh=Th=Ah=Sh=Eh=Nh=kh=0,t}};function Ph(t,n){xh+=t,wh+=n,++Mh}function zh(){Ch.point=$h}function $h(t,n){Ch.point=Dh,Ph(_h=t,bh=n)}function Dh(t,n){var e=t-_h,r=n-bh,i=zf(e*e+r*r);Th+=i*(_h+t)/2,Ah+=i*(bh+n)/2,Sh+=i,Ph(_h=t,bh=n)}function Rh(){Ch.point=Ph}function Fh(){Ch.point=Uh}function qh(){Ih(yh,vh)}function Uh(t,n){Ch.point=Ih,Ph(yh=_h=t,vh=bh=n)}function Ih(t,n){var e=t-_h,r=n-bh,i=zf(e*e+r*r);Th+=i*(_h+t)/2,Ah+=i*(bh+n)/2,Sh+=i,Eh+=(i=bh*t-_h*n)*(_h+t),Nh+=i*(bh+n),kh+=3*i,Ph(_h=t,bh=n)}var Oh=Ch;function Bh(t){this._context=t}Bh.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,_f)}},result:qf};var Yh,Lh,jh,Hh,Xh,Gh=new T,Vh={point:qf,lineStart:function(){Vh.point=Wh},lineEnd:function(){Yh&&Zh(Lh,jh),Vh.point=qf},polygonStart:function(){Yh=!0},polygonEnd:function(){Yh=null},result:function(){var t=+Gh;return Gh=new T,t}};function Wh(t,n){Vh.point=Zh,Lh=Hh=t,jh=Xh=n}function Zh(t,n){Hh-=t,Xh-=n,Gh.add(zf(Hh*Hh+Xh*Xh)),Hh=t,Xh=n}var Kh=Vh;let Qh,Jh,td,nd;class ed{constructor(t){this._append=null==t?rd:function(t){const n=Math.floor(t);if(!(n>=0))throw new RangeError(`invalid digits: ${t}`);if(n>15)return rd;if(n!==Qh){const t=10**n;Qh=n,Jh=function(n){let e=1;this._+=n[0];for(const r=n.length;e<r;++e)this._+=Math.round(arguments[e]*t)/t+n[e]}}return Jh}(t),this._radius=4.5,this._=""}pointRadius(t){return this._radius=+t,this}polygonStart(){this._line=0}polygonEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){0===this._line&&(this._+="Z"),this._point=NaN}point(t,n){switch(this._point){case 0:this._append`M${t},${n}`,this._point=1;break;case 1:this._append`L${t},${n}`;break;default:if(this._append`M${t},${n}`,this._radius!==td||this._append!==Jh){const t=this._radius,n=this._;this._="",this._append`m0,${t}a${t},${t} 0 1,1 0,${-2*t}a${t},${t} 0 1,1 0,${2*t}z`,td=t,Jh=this._append,nd=this._,this._=n}this._+=nd}}result(){const t=this._;return this._="",t.length?t:null}}function rd(t){let n=1;this._+=t[0];for(const e=t.length;n<e;++n)this._+=arguments[n]+t[n]}function id(t){return function(n){var e=new od;for(var r in t)e[r]=t[r];return e.stream=n,e}}function od(){}function ad(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),Lf(e,t.stream(mh)),n(mh.result()),null!=r&&t.clipExtent(r),t}function ud(t,n,e){return ad(t,(function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),a=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,u=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([a,u])}),e)}function cd(t,n,e){return ud(t,[[0,0],n],e)}function fd(t,n,e){return ad(t,(function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,a=-i*e[0][1];t.scale(150*i).translate([o,a])}),e)}function sd(t,n,e){return ad(t,(function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],a=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,a])}),e)}od.prototype={constructor:od,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var ld=16,hd=Tf(30*mf);function dd(t,n){return+n?function(t,n){function e(r,i,o,a,u,c,f,s,l,h,d,p,g,y){var v=f-r,_=s-i,b=v*v+_*_;if(b>4*n&&g--){var m=a+h,x=u+d,w=c+p,M=zf(m*m+x*x+w*w),T=Rf(w/=M),A=xf(xf(w)-1)<df||xf(o-l)<df?(o+l)/2:Mf(x,m),S=t(A,T),E=S[0],N=S[1],k=E-r,C=N-i,P=_*k-v*C;(P*P/b>n||xf((v*k+_*C)/b-.5)>.3||a*h+u*d+c*p<hd)&&(e(r,i,o,a,u,c,E,N,A,m/=M,x/=M,w,g,y),y.point(E,N),e(E,N,A,m,x,w,f,s,l,h,d,p,g,y))}}return function(n){var r,i,o,a,u,c,f,s,l,h,d,p,g={point:y,lineStart:v,lineEnd:b,polygonStart:function(){n.polygonStart(),g.lineStart=m},polygonEnd:function(){n.polygonEnd(),g.lineStart=v}};function y(e,r){e=t(e,r),n.point(e[0],e[1])}function v(){s=NaN,g.point=_,n.lineStart()}function _(r,i){var o=ps([r,i]),a=t(r,i);e(s,l,f,h,d,p,s=a[0],l=a[1],f=r,h=o[0],d=o[1],p=o[2],ld,n),n.point(s,l)}function b(){g.point=y,n.lineEnd()}function m(){v(),g.point=x,g.lineEnd=w}function x(t,n){_(r=t,n),i=s,o=l,a=h,u=d,c=p,g.point=_}function w(){e(s,l,f,h,d,p,i,o,r,a,u,c,ld,n),g.lineEnd=b,b()}return g}}(t,n):function(t){return id({point:function(n,e){n=t(n,e),this.stream.point(n[0],n[1])}})}(t)}var pd=id({point:function(t,n){this.stream.point(t*mf,n*mf)}});function gd(t,n,e,r,i,o){if(!o)return function(t,n,e,r,i){function o(o,a){return[n+t*(o*=r),e-t*(a*=i)]}return o.invert=function(o,a){return[(o-n)/t*r,(e-a)/t*i]},o}(t,n,e,r,i);var a=Tf(o),u=Cf(o),c=a*t,f=u*t,s=a/t,l=u/t,h=(u*e-a*n)/t,d=(u*n+a*e)/t;function p(t,o){return[c*(t*=r)-f*(o*=i)+n,e-f*t-c*o]}return p.invert=function(t,n){return[r*(s*t-l*n+h),i*(d-l*t-s*n)]},p}function yd(t){return vd((function(){return t}))()}function vd(t){var n,e,r,i,o,a,u,c,f,s,l=150,h=480,d=250,p=0,g=0,y=0,v=0,_=0,b=0,m=1,x=1,w=null,M=Tl,T=null,A=eh,S=.5;function E(t){return c(t[0]*mf,t[1]*mf)}function N(t){return(t=c.invert(t[0],t[1]))&&[t[0]*bf,t[1]*bf]}function k(){var t=gd(l,0,0,m,x,b).apply(null,n(p,g)),r=gd(l,h-t[0],d-t[1],m,x,b);return e=ul(y,v,_),u=ol(n,r),c=ol(e,u),a=dd(u,S),C()}function C(){return f=s=null,E}return E.stream=function(t){return f&&s===t?f:f=pd(function(t){return id({point:function(n,e){var r=t(n,e);return this.stream.point(r[0],r[1])}})}(e)(M(a(A(s=t)))))},E.preclip=function(t){return arguments.length?(M=t,w=void 0,C()):M},E.postclip=function(t){return arguments.length?(A=t,T=r=i=o=null,C()):A},E.clipAngle=function(t){return arguments.length?(M=+t?Al(w=t*mf):(w=null,Tl),C()):w*bf},E.clipExtent=function(t){return arguments.length?(A=null==t?(T=r=i=o=null,eh):zl(T=+t[0][0],r=+t[0][1],i=+t[1][0],o=+t[1][1]),C()):null==T?null:[[T,r],[i,o]]},E.scale=function(t){return arguments.length?(l=+t,k()):l},E.translate=function(t){return arguments.length?(h=+t[0],d=+t[1],k()):[h,d]},E.center=function(t){return arguments.length?(p=t[0]%360*mf,g=t[1]%360*mf,k()):[p*bf,g*bf]},E.rotate=function(t){return arguments.length?(y=t[0]%360*mf,v=t[1]%360*mf,_=t.length>2?t[2]%360*mf:0,k()):[y*bf,v*bf,_*bf]},E.angle=function(t){return arguments.length?(b=t%360*mf,k()):b*bf},E.reflectX=function(t){return arguments.length?(m=t?-1:1,k()):m<0},E.reflectY=function(t){return arguments.length?(x=t?-1:1,k()):x<0},E.precision=function(t){return arguments.length?(a=dd(u,S=t*t),C()):zf(S)},E.fitExtent=function(t,n){return ud(E,t,n)},E.fitSize=function(t,n){return cd(E,t,n)},E.fitWidth=function(t,n){return fd(E,t,n)},E.fitHeight=function(t,n){return sd(E,t,n)},function(){return n=t.apply(this,arguments),E.invert=n.invert&&N,k()}}function _d(t){var n=0,e=gf/3,r=vd(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*mf,e=t[1]*mf):[n*bf,e*bf]},i}function bd(t,n){var e=Cf(t),r=(e+Cf(n))/2;if(xf(r)<df)return function(t){var n=Tf(t);function e(t,e){return[t*n,Cf(e)/n]}return e.invert=function(t,e){return[t/n,Rf(e*n)]},e}(t);var i=1+e*(2*r-e),o=zf(i)/r;function a(t,n){var e=zf(i-2*r*Cf(n))/r;return[e*Cf(t*=r),o-e*Tf(t)]}return a.invert=function(t,n){var e=o-n,a=Mf(t,xf(e))*Pf(e);return e*r<0&&(a-=gf*Pf(t)*Pf(e)),[a/r,Rf((i-(t*t+e*e)*r*r)/(2*r))]},a}function md(){return _d(bd).scale(155.424).center([0,33.6442])}function xd(){return md().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])}function wd(t){return function(n,e){var r=Tf(n),i=Tf(e),o=t(r*i);return o===1/0?[2,0]:[o*i*Cf(n),o*Cf(e)]}}function Md(t){return function(n,e){var r=zf(n*n+e*e),i=t(r),o=Cf(i),a=Tf(i);return[Mf(n*o,r*a),Rf(r&&e*o/r)]}}var Td=wd((function(t){return zf(2/(1+t))}));Td.invert=Md((function(t){return 2*Rf(t/2)}));var Ad=wd((function(t){return(t=Df(t))&&t/Cf(t)}));function Sd(t,n){return[t,Nf($f((yf+n)/2))]}function Ed(t){var n,e,r,i=yd(t),o=i.center,a=i.scale,u=i.translate,c=i.clipExtent,f=null;function s(){var o=gf*a(),u=i(ll(i.rotate()).invert([0,0]));return c(null==f?[[u[0]-o,u[1]-o],[u[0]+o,u[1]+o]]:t===Sd?[[Math.max(u[0]-o,f),n],[Math.min(u[0]+o,e),r]]:[[f,Math.max(u[1]-o,n)],[e,Math.min(u[1]+o,r)]])}return i.scale=function(t){return arguments.length?(a(t),s()):a()},i.translate=function(t){return arguments.length?(u(t),s()):u()},i.center=function(t){return arguments.length?(o(t),s()):o()},i.clipExtent=function(t){return arguments.length?(null==t?f=n=e=r=null:(f=+t[0][0],n=+t[0][1],e=+t[1][0],r=+t[1][1]),s()):null==f?null:[[f,n],[e,r]]},s()}function Nd(t){return $f((yf+t)/2)}function kd(t,n){var e=Tf(t),r=t===n?Cf(t):Nf(e/Tf(n))/Nf(Nd(n)/Nd(t)),i=e*kf(Nd(t),r)/r;if(!r)return Sd;function o(t,n){i>0?n<-yf+df&&(n=-yf+df):n>yf-df&&(n=yf-df);var e=i/kf(Nd(n),r);return[e*Cf(r*t),i-e*Tf(r*t)]}return o.invert=function(t,n){var e=i-n,o=Pf(r)*zf(t*t+e*e),a=Mf(t,xf(e))*Pf(e);return e*r<0&&(a-=gf*Pf(t)*Pf(e)),[a/r,2*wf(kf(i/o,1/r))-yf]},o}function Cd(t,n){return[t,n]}function Pd(t,n){var e=Tf(t),r=t===n?Cf(t):(e-Tf(n))/(n-t),i=e/r+t;if(xf(r)<df)return Cd;function o(t,n){var e=i-n,o=r*t;return[e*Cf(o),i-e*Tf(o)]}return o.invert=function(t,n){var e=i-n,o=Mf(t,xf(e))*Pf(e);return e*r<0&&(o-=gf*Pf(t)*Pf(e)),[o/r,i-Pf(r)*zf(t*t+e*e)]},o}Ad.invert=Md((function(t){return t})),Sd.invert=function(t,n){return[t,2*wf(Sf(n))-yf]},Cd.invert=Cd;var zd=1.340264,$d=-.081106,Dd=893e-6,Rd=.003796,Fd=zf(3)/2;function qd(t,n){var e=Rf(Fd*Cf(n)),r=e*e,i=r*r*r;return[t*Tf(e)/(Fd*(zd+3*$d*r+i*(7*Dd+9*Rd*r))),e*(zd+$d*r+i*(Dd+Rd*r))]}function Ud(t,n){var e=Tf(n),r=Tf(t)*e;return[e*Cf(t)/r,Cf(n)/r]}function Id(t,n){var e=n*n,r=e*e;return[t*(.8707-.131979*e+r*(r*(.003971*e-.001529*r)-.013791)),n*(1.007226+e*(.015085+r*(.028874*e-.044475-.005916*r)))]}function Od(t,n){return[Tf(n)*Cf(t),Cf(n)]}function Bd(t,n){var e=Tf(n),r=1+Tf(t)*e;return[e*Cf(t)/r,Cf(n)/r]}function Yd(t,n){return[Nf($f((yf+n)/2)),-t]}function Ld(t,n){return t.parent===n.parent?1:2}function jd(t,n){return t+n.x}function Hd(t,n){return Math.max(t,n.y)}function Xd(t){var n=0,e=t.children,r=e&&e.length;if(r)for(;--r>=0;)n+=e[r].value;else n=1;t.value=n}function Gd(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Wd)):void 0===n&&(n=Vd);for(var e,r,i,o,a,u=new Qd(t),c=[u];e=c.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)c.push(r=i[o]=new Qd(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(Kd)}function Vd(t){return t.children}function Wd(t){return Array.isArray(t)?t[1]:null}function Zd(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Kd(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function Qd(t){this.data=t,this.depth=this.height=0,this.parent=null}function Jd(t){return null==t?null:tp(t)}function tp(t){if("function"!=typeof t)throw new Error;return t}function np(){return 0}function ep(t){return function(){return t}}qd.invert=function(t,n){for(var e,r=n,i=r*r,o=i*i*i,a=0;a<12&&(o=(i=(r-=e=(r*(zd+$d*i+o*(Dd+Rd*i))-n)/(zd+3*$d*i+o*(7*Dd+9*Rd*i)))*r)*i*i,!(xf(e)<pf));++a);return[Fd*t*(zd+3*$d*i+o*(7*Dd+9*Rd*i))/Tf(r),Rf(Cf(r)/Fd)]},Ud.invert=Md(wf),Id.invert=function(t,n){var e,r=n,i=25;do{var o=r*r,a=o*o;r-=e=(r*(1.007226+o*(.015085+a*(.028874*o-.044475-.005916*a)))-n)/(1.007226+o*(.045255+a*(.259866*o-.311325-.005916*11*a)))}while(xf(e)>df&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},Od.invert=Md(Rf),Bd.invert=Md((function(t){return 2*wf(t)})),Yd.invert=function(t,n){return[-n,2*wf(Sf(t))-yf]},Qd.prototype=Gd.prototype={constructor:Qd,count:function(){return this.eachAfter(Xd)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],c=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r<i;++r)a.push(e[r]);for(;o=u.pop();)t.call(n,o,++c,this);return this},eachBefore:function(t,n){for(var e,r,i=this,o=[i],a=-1;i=o.pop();)if(t.call(n,i,++a,this),e=i.children)for(r=e.length-1;r>=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Gd(this).eachBefore(Zd)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;e<r;++e)o.push(n[e])}while(o.length)}};const rp=1664525,ip=1013904223,op=4294967296;function ap(){let t=1;return()=>(t=(rp*t+ip)%op)/op}function up(t,n){for(var e,r,i=0,o=(t=function(t,n){let e,r,i=t.length;for(;i;)r=n()*i--|0,e=t[i],t[i]=t[r],t[r]=e;return t}(Array.from(t),n)).length,a=[];i<o;)e=t[i],r&&sp(r,e)?++i:(r=hp(a=cp(a,e)),i=0);return r}function cp(t,n){var e,r;if(lp(n,t))return[n];for(e=0;e<t.length;++e)if(fp(n,t[e])&&lp(dp(t[e],n),t))return[t[e],n];for(e=0;e<t.length-1;++e)for(r=e+1;r<t.length;++r)if(fp(dp(t[e],t[r]),n)&&fp(dp(t[e],n),t[r])&&fp(dp(t[r],n),t[e])&&lp(pp(t[e],t[r],n),t))return[t[e],t[r],n];throw new Error}function fp(t,n){var e=t.r-n.r,r=n.x-t.x,i=n.y-t.y;return e<0||e*e<r*r+i*i}function sp(t,n){var e=t.r-n.r+1e-9*Math.max(t.r,n.r,1),r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function lp(t,n){for(var e=0;e<n.length;++e)if(!sp(t,n[e]))return!1;return!0}function hp(t){switch(t.length){case 1:return function(t){return{x:t.x,y:t.y,r:t.r}}(t[0]);case 2:return dp(t[0],t[1]);case 3:return pp(t[0],t[1],t[2])}}function dp(t,n){var e=t.x,r=t.y,i=t.r,o=n.x,a=n.y,u=n.r,c=o-e,f=a-r,s=u-i,l=Math.sqrt(c*c+f*f);return{x:(e+o+c/l*s)/2,y:(r+a+f/l*s)/2,r:(l+i+u)/2}}function pp(t,n,e){var r=t.x,i=t.y,o=t.r,a=n.x,u=n.y,c=n.r,f=e.x,s=e.y,l=e.r,h=r-a,d=r-f,p=i-u,g=i-s,y=c-o,v=l-o,_=r*r+i*i-o*o,b=_-a*a-u*u+c*c,m=_-f*f-s*s+l*l,x=d*p-h*g,w=(p*m-g*b)/(2*x)-r,M=(g*y-p*v)/x,T=(d*b-h*m)/(2*x)-i,A=(h*v-d*y)/x,S=M*M+A*A-1,E=2*(o+w*M+T*A),N=w*w+T*T-o*o,k=-(Math.abs(S)>1e-6?(E+Math.sqrt(E*E-4*S*N))/(2*S):N/E);return{x:r+w+M*k,y:i+T+A*k,r:k}}function gp(t,n,e){var r,i,o,a,u=t.x-n.x,c=t.y-n.y,f=u*u+c*c;f?(i=n.r+e.r,i*=i,a=t.r+e.r,i>(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function yp(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function vp(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function _p(t){this._=t,this.next=null,this.previous=null}function bp(t,n){if(!(o=(t=function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}(t)).length))return 0;var e,r,i,o,a,u,c,f,s,l,h;if((e=t[0]).x=0,e.y=0,!(o>1))return e.r;if(r=t[1],e.x=-r.r,r.x=e.r,r.y=0,!(o>2))return e.r+r.r;gp(r,e,i=t[2]),e=new _p(e),r=new _p(r),i=new _p(i),e.next=i.previous=r,r.next=e.previous=i,i.next=r.previous=e;t:for(c=3;c<o;++c){gp(e._,r._,i=t[c]),i=new _p(i),f=r.next,s=e.previous,l=r._.r,h=e._.r;do{if(l<=h){if(yp(f._,i._)){r=f,e.next=r,r.previous=e,--c;continue t}l+=f._.r,f=f.next}else{if(yp(s._,i._)){(e=s).next=r,r.previous=e,--c;continue t}h+=s._.r,s=s.previous}}while(f!==s.next);for(i.previous=e,i.next=r,e.next=r.previous=r=i,a=vp(e);(i=i.next)!==r;)(u=vp(i))<a&&(e=i,a=u);r=e.next}for(e=[r._],i=r;(i=i.next)!==r;)e.push(i._);for(i=up(e,n),c=0;c<o;++c)(e=t[c]).x-=i.x,e.y-=i.y;return i.r}function mp(t){return Math.sqrt(t.value)}function xp(t){return function(n){n.children||(n.r=Math.max(0,+t(n)||0))}}function wp(t,n,e){return function(r){if(i=r.children){var i,o,a,u=i.length,c=t(r)*n||0;if(c)for(o=0;o<u;++o)i[o].r+=c;if(a=bp(i,e),c)for(o=0;o<u;++o)i[o].r-=c;r.r=a+c}}}function Mp(t){return function(n){var e=n.parent;n.r*=t,e&&(n.x=e.x+t*n.x,n.y=e.y+t*n.y)}}function Tp(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function Ap(t,n,e,r,i){for(var o,a=t.children,u=-1,c=a.length,f=t.value&&(r-n)/t.value;++u<c;)(o=a[u]).y0=e,o.y1=i,o.x0=n,o.x1=n+=o.value*f}var Sp={depth:-1},Ep={},Np={};function kp(t){return t.id}function Cp(t){return t.parentId}function Pp(t){let n=t.length;if(n<2)return"";for(;--n>1&&!zp(t,n););return t.slice(0,n)}function zp(t,n){if("/"===t[n]){let e=0;for(;n>0&&"\\"===t[--n];)++e;if(!(1&e))return!0}return!1}function $p(t,n){return t.parent===n.parent?1:2}function Dp(t){var n=t.children;return n?n[0]:t.t}function Rp(t){var n=t.children;return n?n[n.length-1]:t.t}function Fp(t,n,e){var r=e/(n.i-t.i);n.c-=r,n.s+=e,t.c+=r,n.z+=e,n.m+=e}function qp(t,n,e){return t.a.parent===n.parent?t.a:e}function Up(t,n){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=n}function Ip(t,n,e,r,i){for(var o,a=t.children,u=-1,c=a.length,f=t.value&&(i-e)/t.value;++u<c;)(o=a[u]).x0=n,o.x1=r,o.y0=e,o.y1=e+=o.value*f}Up.prototype=Object.create(Qd.prototype);var Op=(1+Math.sqrt(5))/2;function Bp(t,n,e,r,i,o){for(var a,u,c,f,s,l,h,d,p,g,y,v=[],_=n.children,b=0,m=0,x=_.length,w=n.value;b<x;){c=i-e,f=o-r;do{s=_[m++].value}while(!s&&m<x);for(l=h=s,y=s*s*(g=Math.max(f/c,c/f)/(w*t)),p=Math.max(h/y,y/l);m<x;++m){if(s+=u=_[m].value,u<l&&(l=u),u>h&&(h=u),y=s*s*g,(d=Math.max(h/y,y/l))>p){s-=u;break}p=d}v.push(a={value:s,dice:c<f,children:_.slice(b,m)}),a.dice?Ap(a,e,r,i,w?r+=f*s/w:o):Ip(a,e,r,w?e+=c*s/w:i,o),w-=s,b=m}return v}var Yp=function t(n){function e(t,e,r,i,o){Bp(n,t,e,r,i,o)}return e.ratio=function(n){return t((n=+n)>1?n:1)},e}(Op);var Lp=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l<h;){for(c=(u=a[l]).children,f=u.value=0,s=c.length;f<s;++f)u.value+=c[f].value;u.dice?Ap(u,e,r,i,d?r+=(o-r)*u.value/d:o):Ip(u,e,r,d?e+=(i-e)*u.value/d:i,o),d-=u.value}else t._squarify=a=Bp(n,t,e,r,i,o),a.ratio=n}return e.ratio=function(n){return t((n=+n)>1?n:1)},e}(Op);function jp(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function Hp(t,n){return t[0]-n[0]||t[1]-n[1]}function Xp(t){const n=t.length,e=[0,1];let r,i=2;for(r=2;r<n;++r){for(;i>1&&jp(t[e[i-2]],t[e[i-1]],t[r])<=0;)--i;e[i++]=r}return e.slice(0,i)}var Gp=Math.random,Vp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Gp),Wp=function t(n){function e(t,e){return arguments.length<2&&(e=t,t=0),t=Math.floor(t),e=Math.floor(e)-t,function(){return Math.floor(n()*e+t)}}return e.source=t,e}(Gp),Zp=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Gp),Kp=function t(n){var e=Zp.source(n);function r(){var t=e.apply(this,arguments);return function(){return Math.exp(t())}}return r.source=t,r}(Gp),Qp=function t(n){function e(t){return(t=+t)<=0?()=>0:function(){for(var e=0,r=t;r>1;--r)e+=n();return e+r*n()}}return e.source=t,e}(Gp),Jp=function t(n){var e=Qp.source(n);function r(t){if(0==(t=+t))return n;var r=e(t);return function(){return r()/t}}return r.source=t,r}(Gp),tg=function t(n){function e(t){return function(){return-Math.log1p(-n())/t}}return e.source=t,e}(Gp),ng=function t(n){function e(t){if((t=+t)<0)throw new RangeError("invalid alpha");return t=1/-t,function(){return Math.pow(1-n(),t)}}return e.source=t,e}(Gp),eg=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return function(){return Math.floor(n()+t)}}return e.source=t,e}(Gp),rg=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return 0===t?()=>1/0:1===t?()=>1:(t=Math.log1p(-t),function(){return 1+Math.floor(Math.log1p(-n())/t)})}return e.source=t,e}(Gp),ig=function t(n){var e=Zp.source(n)();function r(t,r){if((t=+t)<0)throw new RangeError("invalid k");if(0===t)return()=>0;if(r=null==r?1:+r,1===t)return()=>-Math.log1p(-n())*r;var i=(t<1?t+1:t)-1/3,o=1/(3*Math.sqrt(i)),a=t<1?()=>Math.pow(n(),1/t):()=>1;return function(){do{do{var t=e(),u=1+o*t}while(u<=0);u*=u*u;var c=1-n()}while(c>=1-.0331*t*t*t*t&&Math.log(c)>=.5*t*t+i*(1-u+Math.log(u)));return i*u*a()*r}}return r.source=t,r}(Gp),og=function t(n){var e=ig.source(n);function r(t,n){var r=e(t),i=e(n);return function(){var t=r();return 0===t?0:t/(t+i())}}return r.source=t,r}(Gp),ag=function t(n){var e=rg.source(n),r=og.source(n);function i(t,n){return t=+t,(n=+n)>=1?()=>t:n<=0?()=>0:function(){for(var i=0,o=t,a=n;o*a>16&&o*(1-a)>16;){var u=Math.floor((o+1)*a),c=r(u,o-u+1)();c<=a?(i+=u,o-=u,a=(a-c)/(1-c)):(o=u-1,a/=c)}for(var f=a<.5,s=e(f?a:1-a),l=s(),h=0;l<=o;++h)l+=s();return i+(f?h:o-h)}}return i.source=t,i}(Gp),ug=function t(n){function e(t,e,r){var i;return 0==(t=+t)?i=t=>-Math.log(t):(t=1/t,i=n=>Math.pow(n,t)),e=null==e?0:+e,r=null==r?1:+r,function(){return e+r*i(-Math.log1p(-n()))}}return e.source=t,e}(Gp),cg=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){return t+e*Math.tan(Math.PI*n())}}return e.source=t,e}(Gp),fg=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){var r=n();return t+e*Math.log(r/(1-r))}}return e.source=t,e}(Gp),sg=function t(n){var e=ig.source(n),r=ag.source(n);function i(t){return function(){for(var i=0,o=t;o>16;){var a=Math.floor(.875*o),u=e(a)();if(u>o)return i+r(a-1,o/u)();i+=a,o-=u}for(var c=-Math.log1p(-n()),f=0;c<=o;++f)c-=Math.log1p(-n());return i+f}}return i.source=t,i}(Gp);const lg=1/4294967296;function hg(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function dg(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this}const pg=Symbol("implicit");function gg(){var t=new InternMap,n=[],e=[],r=pg;function i(i){let o=t.get(i);if(void 0===o){if(r!==pg)return r;t.set(i,o=n.push(i)-1)}return e[o%e.length]}return i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=new InternMap;for(const r of e)t.has(r)||t.set(r,n.push(r)-1);return i},i.range=function(t){return arguments.length?(e=Array.from(t),i):e.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return gg(n,e).unknown(r)},hg.apply(i,arguments),i}function yg(){var t,n,e=gg().unknown(void 0),r=e.domain,i=e.range,o=0,a=1,u=!1,c=0,f=0,s=.5;function l(){var e=r().length,l=a<o,h=l?a:o,d=l?o:a;t=(d-h)/Math.max(1,e-c+2*f),u&&(t=Math.floor(t)),h+=(d-h-t*(e-c))*s,n=t*(1-c),u&&(h=Math.round(h),n=Math.round(n));var p=lt(e).map((function(n){return h+t*n}));return i(l?p.reverse():p)}return delete e.unknown,e.domain=function(t){return arguments.length?(r(t),l()):r()},e.range=function(t){return arguments.length?([o,a]=t,o=+o,a=+a,l()):[o,a]},e.rangeRound=function(t){return[o,a]=t,o=+o,a=+a,u=!0,l()},e.bandwidth=function(){return n},e.step=function(){return t},e.round=function(t){return arguments.length?(u=!!t,l()):u},e.padding=function(t){return arguments.length?(c=Math.min(1,f=+t),l()):c},e.paddingInner=function(t){return arguments.length?(c=Math.min(1,t),l()):c},e.paddingOuter=function(t){return arguments.length?(f=+t,l()):f},e.align=function(t){return arguments.length?(s=Math.max(0,Math.min(1,t)),l()):s},e.copy=function(){return yg(r(),[o,a]).round(u).paddingInner(c).paddingOuter(f).align(s)},hg.apply(l(),arguments)}function vg(t){var n=t.copy;return t.padding=t.paddingOuter,delete t.paddingInner,delete t.paddingOuter,t.copy=function(){return vg(n())},t}function _g(t){return+t}var bg=[0,1];function mg(t){return t}function xg(t,n){return(n-=t=+t)?function(e){return(e-t)/n}:function(t){return function(){return t}}(isNaN(n)?NaN:.5)}function wg(t,n,e){var r=t[0],i=t[1],o=n[0],a=n[1];return i<r?(r=xg(i,r),o=e(a,o)):(r=xg(r,i),o=e(o,a)),function(t){return o(r(t))}}function Mg(t,n,e){var r=Math.min(t.length,n.length)-1,i=new Array(r),o=new Array(r),a=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),n=n.slice().reverse());++a<r;)i[a]=xg(t[a],t[a+1]),o[a]=e(n[a],n[a+1]);return function(n){var e=s(t,n,1,r)-1;return o[e](i[e](n))}}function Tg(t,n){return n.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function Ag(){var t,n,e,r,i,o,a=bg,u=bg,c=Gr,f=mg;function s(){var t=Math.min(a.length,u.length);return f!==mg&&(f=function(t,n){var e;return t>n&&(e=t,t=n,n=e),function(e){return Math.max(t,Math.min(n,e))}}(a[0],a[t-1])),r=t>2?Mg:wg,i=o=null,l}function l(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),Yr)))(e)))},l.domain=function(t){return arguments.length?(a=Array.from(t,_g),s()):a.slice()},l.range=function(t){return arguments.length?(u=Array.from(t),s()):u.slice()},l.rangeRound=function(t){return u=Array.from(t),c=Vr,s()},l.clamp=function(t){return arguments.length?(f=!!t||mg,s()):f!==mg},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function Sg(){return Ag()(mg,mg)}function Eg(n,e,r,i){var o,a=W(n,e,r);switch((i=Jc(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=lf(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=hf(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=sf(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function Ng(t){var n=t.domain;return t.ticks=function(t){var e=n();return G(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return Eg(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,c=o[a],f=o[u],s=10;for(f<c&&(i=c,c=f,f=i,i=a,a=u,u=i);s-- >0;){if((i=V(c,f,e))===r)return o[a]=c,o[u]=f,n(o);if(i>0)c=Math.floor(c/i)*i,f=Math.ceil(f/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,f=Math.floor(f*i)/i}r=i}return t},t}function kg(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a<o&&(e=r,r=i,i=e,e=o,o=a,a=e),t[r]=n.floor(o),t[i]=n.ceil(a),t}function Cg(t){return Math.log(t)}function Pg(t){return Math.exp(t)}function zg(t){return-Math.log(-t)}function $g(t){return-Math.exp(-t)}function Dg(t){return isFinite(t)?+("1e"+t):t<0?0:t}function Rg(t){return(n,e)=>-t(-n,e)}function Fg(n){const e=n(Cg,Pg),r=e.domain;let i,o,a=10;function u(){return i=function(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),n=>Math.log(n)/t)}(a),o=function(t){return 10===t?Dg:t===Math.E?Math.exp:n=>Math.pow(t,n)}(a),r()[0]<0?(i=Rg(i),o=Rg(o),n(zg,$g)):n(Cg,Pg),e}return e.base=function(t){return arguments.length?(a=+t,u()):a},e.domain=function(t){return arguments.length?(r(t),u()):r()},e.ticks=t=>{const n=r();let e=n[0],u=n[n.length-1];const c=u<e;c&&([e,u]=[u,e]);let f,s,l=i(e),h=i(u);const d=null==t?10:+t;let p=[];if(!(a%1)&&h-l<d){if(l=Math.floor(l),h=Math.ceil(h),e>0){for(;l<=h;++l)for(f=1;f<a;++f)if(s=l<0?f/o(-l):f*o(l),!(s<e)){if(s>u)break;p.push(s)}}else for(;l<=h;++l)for(f=a-1;f>=1;--f)if(s=l>0?f/o(-l):f*o(l),!(s<e)){if(s>u)break;p.push(s)}2*p.length<d&&(p=G(e,u,d))}else p=G(l,h,Math.min(h-l,d)).map(o);return c?p.reverse():p},e.tickFormat=(n,r)=>{if(null==n&&(n=10),null==r&&(r=10===a?"s":","),"function"!=typeof r&&(a%1||null!=(r=Jc(r)).precision||(r.trim=!0),r=t.format(r)),n===1/0)return r;const u=Math.max(1,a*n/e.ticks().length);return t=>{let n=t/o(Math.round(i(t)));return n*a<a-.5&&(n*=a),n<=u?r(t):""}},e.nice=()=>r(kg(r(),{floor:t=>o(Math.floor(i(t))),ceil:t=>o(Math.ceil(i(t)))})),e}function qg(t){return function(n){return Math.sign(n)*Math.log1p(Math.abs(n/t))}}function Ug(t){return function(n){return Math.sign(n)*Math.expm1(Math.abs(n))*t}}function Ig(t){var n=1,e=t(qg(n),Ug(n));return e.constant=function(e){return arguments.length?t(qg(n=+e),Ug(n)):n},Ng(e)}function Og(t){return function(n){return n<0?-Math.pow(-n,t):Math.pow(n,t)}}function Bg(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function Yg(t){return t<0?-t*t:t*t}function Lg(t){var n=t(mg,mg),e=1;return n.exponent=function(n){return arguments.length?1===(e=+n)?t(mg,mg):.5===e?t(Bg,Yg):t(Og(e),Og(1/e)):e},Ng(n)}function jg(){var t=Lg(Ag());return t.copy=function(){return Tg(t,jg()).exponent(t.exponent())},hg.apply(t,arguments),t}function Hg(t){return Math.sign(t)*t*t}const Xg=new Date,Gg=new Date;function Vg(t,n,e,r){function i(n){return t(n=0===arguments.length?new Date:new Date(+n)),n}return i.floor=n=>(t(n=new Date(+n)),n),i.ceil=e=>(t(e=new Date(e-1)),n(e,1),t(e),e),i.round=t=>{const n=i(t),e=i.ceil(t);return t-n<e-t?n:e},i.offset=(t,e)=>(n(t=new Date(+t),null==e?1:Math.floor(e)),t),i.range=(e,r,o)=>{const a=[];if(e=i.ceil(e),o=null==o?1:Math.floor(o),!(e<r&&o>0))return a;let u;do{a.push(u=new Date(+e)),n(e,o),t(e)}while(u<e&&e<r);return a},i.filter=e=>Vg((n=>{if(n>=n)for(;t(n),!e(n);)n.setTime(n-1)}),((t,r)=>{if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})),e&&(i.count=(n,r)=>(Xg.setTime(+n),Gg.setTime(+r),t(Xg),t(Gg),Math.floor(e(Xg,Gg))),i.every=t=>(t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?n=>r(n)%t==0:n=>i.count(0,n)%t==0):i:null)),i}const Wg=Vg((()=>{}),((t,n)=>{t.setTime(+t+n)}),((t,n)=>n-t));Wg.every=t=>(t=Math.floor(t),isFinite(t)&&t>0?t>1?Vg((n=>{n.setTime(Math.floor(n/t)*t)}),((n,e)=>{n.setTime(+n+e*t)}),((n,e)=>(e-n)/t)):Wg:null);const Zg=Wg.range,Kg=1e3,Qg=6e4,Jg=36e5,ty=864e5,ny=6048e5,ey=2592e6,ry=31536e6,iy=Vg((t=>{t.setTime(t-t.getMilliseconds())}),((t,n)=>{t.setTime(+t+n*Kg)}),((t,n)=>(n-t)/Kg),(t=>t.getUTCSeconds())),oy=iy.range,ay=Vg((t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Kg)}),((t,n)=>{t.setTime(+t+n*Qg)}),((t,n)=>(n-t)/Qg),(t=>t.getMinutes())),uy=ay.range,cy=Vg((t=>{t.setUTCSeconds(0,0)}),((t,n)=>{t.setTime(+t+n*Qg)}),((t,n)=>(n-t)/Qg),(t=>t.getUTCMinutes())),fy=cy.range,sy=Vg((t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Kg-t.getMinutes()*Qg)}),((t,n)=>{t.setTime(+t+n*Jg)}),((t,n)=>(n-t)/Jg),(t=>t.getHours())),ly=sy.range,hy=Vg((t=>{t.setUTCMinutes(0,0,0)}),((t,n)=>{t.setTime(+t+n*Jg)}),((t,n)=>(n-t)/Jg),(t=>t.getUTCHours())),dy=hy.range,py=Vg((t=>t.setHours(0,0,0,0)),((t,n)=>t.setDate(t.getDate()+n)),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qg)/ty),(t=>t.getDate()-1)),gy=py.range,yy=Vg((t=>{t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+n)}),((t,n)=>(n-t)/ty),(t=>t.getUTCDate()-1)),vy=yy.range,_y=Vg((t=>{t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+n)}),((t,n)=>(n-t)/ty),(t=>Math.floor(t/ty))),by=_y.range;function my(t){return Vg((n=>{n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)}),((t,n)=>{t.setDate(t.getDate()+7*n)}),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qg)/ny))}const xy=my(0),wy=my(1),My=my(2),Ty=my(3),Ay=my(4),Sy=my(5),Ey=my(6),Ny=xy.range,ky=wy.range,Cy=My.range,Py=Ty.range,zy=Ay.range,$y=Sy.range,Dy=Ey.range;function Ry(t){return Vg((n=>{n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+7*n)}),((t,n)=>(n-t)/ny))}const Fy=Ry(0),qy=Ry(1),Uy=Ry(2),Iy=Ry(3),Oy=Ry(4),By=Ry(5),Yy=Ry(6),Ly=Fy.range,jy=qy.range,Hy=Uy.range,Xy=Iy.range,Gy=Oy.range,Vy=By.range,Wy=Yy.range,Zy=Vg((t=>{t.setDate(1),t.setHours(0,0,0,0)}),((t,n)=>{t.setMonth(t.getMonth()+n)}),((t,n)=>n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())),(t=>t.getMonth())),Ky=Zy.range,Qy=Vg((t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCMonth(t.getUTCMonth()+n)}),((t,n)=>n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())),(t=>t.getUTCMonth())),Jy=Qy.range,tv=Vg((t=>{t.setMonth(0,1),t.setHours(0,0,0,0)}),((t,n)=>{t.setFullYear(t.getFullYear()+n)}),((t,n)=>n.getFullYear()-t.getFullYear()),(t=>t.getFullYear()));tv.every=t=>isFinite(t=Math.floor(t))&&t>0?Vg((n=>{n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)}),((n,e)=>{n.setFullYear(n.getFullYear()+e*t)})):null;const nv=tv.range,ev=Vg((t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCFullYear(t.getUTCFullYear()+n)}),((t,n)=>n.getUTCFullYear()-t.getUTCFullYear()),(t=>t.getUTCFullYear()));ev.every=t=>isFinite(t=Math.floor(t))&&t>0?Vg((n=>{n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)}),((n,e)=>{n.setUTCFullYear(n.getUTCFullYear()+e*t)})):null;const rv=ev.range;function iv(t,n,e,i,o,a){const u=[[iy,1,Kg],[iy,5,5e3],[iy,15,15e3],[iy,30,3e4],[a,1,Qg],[a,5,3e5],[a,15,9e5],[a,30,18e5],[o,1,Jg],[o,3,108e5],[o,6,216e5],[o,12,432e5],[i,1,ty],[i,2,1728e5],[e,1,ny],[n,1,ey],[n,3,7776e6],[t,1,ry]];function c(n,e,i){const o=Math.abs(e-n)/i,a=r((([,,t])=>t)).right(u,o);if(a===u.length)return t.every(W(n/ry,e/ry,i));if(0===a)return Wg.every(Math.max(W(n,e,i),1));const[c,f]=u[o/u[a-1][2]<u[a][2]/o?a-1:a];return c.every(f)}return[function(t,n,e){const r=n<t;r&&([t,n]=[n,t]);const i=e&&"function"==typeof e.range?e:c(t,n,e),o=i?i.range(t,+n+1):[];return r?o.reverse():o},c]}const[ov,av]=iv(ev,Qy,Fy,_y,hy,cy),[uv,cv]=iv(tv,Zy,xy,py,sy,ay);function fv(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function sv(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function lv(t,n,e){return{y:t,m:n,d:e,H:0,M:0,S:0,L:0}}function hv(t){var n=t.dateTime,e=t.date,r=t.time,i=t.periods,o=t.days,a=t.shortDays,u=t.months,c=t.shortMonths,f=mv(i),s=xv(i),l=mv(o),h=xv(o),d=mv(a),p=xv(a),g=mv(u),y=xv(u),v=mv(c),_=xv(c),b={a:function(t){return a[t.getDay()]},A:function(t){return o[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return u[t.getMonth()]},c:null,d:Yv,e:Yv,f:Gv,g:i_,G:a_,H:Lv,I:jv,j:Hv,L:Xv,m:Vv,M:Wv,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:k_,s:C_,S:Zv,u:Kv,U:Qv,V:t_,w:n_,W:e_,x:null,X:null,y:r_,Y:o_,Z:u_,"%":N_},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:c_,e:c_,f:d_,g:T_,G:S_,H:f_,I:s_,j:l_,L:h_,m:p_,M:g_,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:k_,s:C_,S:y_,u:v_,U:__,V:m_,w:x_,W:w_,x:null,X:null,y:M_,Y:A_,Z:E_,"%":N_},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=_.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return T(t,n,e,r)},d:zv,e:zv,f:Uv,g:Nv,G:Ev,H:Dv,I:Dv,j:$v,L:qv,m:Pv,M:Rv,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s.get(r[0].toLowerCase()),e+r[0].length):-1},q:Cv,Q:Ov,s:Bv,S:Fv,u:Mv,U:Tv,V:Av,w:wv,W:Sv,x:function(t,n,r){return T(t,e,n,r)},X:function(t,n,e){return T(t,r,n,e)},y:Nv,Y:Ev,Z:kv,"%":Iv};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u<f;)37===t.charCodeAt(u)&&(a.push(t.slice(c,u)),null!=(i=pv[r=t.charAt(++u)])?r=t.charAt(++u):i="e"===r?" ":"0",(o=n[r])&&(r=o(e,i)),a.push(r),c=u+1);return a.push(t.slice(c,u)),a.join("")}}function M(t,n){return function(e){var r,i,o=lv(1900,void 0,1);if(T(o,t,e+="",0)!=e.length)return null;if("Q"in o)return new Date(o.Q);if("s"in o)return new Date(1e3*o.s+("L"in o?o.L:0));if(n&&!("Z"in o)&&(o.Z=0),"p"in o&&(o.H=o.H%12+12*o.p),void 0===o.m&&(o.m="q"in o?o.q:0),"V"in o){if(o.V<1||o.V>53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=sv(lv(o.y,0,1))).getUTCDay(),r=i>4||0===i?qy.ceil(r):qy(r),r=yy.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=fv(lv(o.y,0,1))).getDay(),r=i>4||0===i?wy.ceil(r):wy(r),r=py.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?sv(lv(o.y,0,1)).getUTCDay():fv(lv(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,sv(o)):fv(o)}}function T(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a<u;){if(r>=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in pv?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t+="",!0);return n.toString=function(){return t},n}}}var dv,pv={"-":"",_:" ",0:"0"},gv=/^\s*\d+/,yv=/^%/,vv=/[\\^$*+?|[\]().{}]/g;function _v(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o<e?new Array(e-o+1).join(n)+i:i)}function bv(t){return t.replace(vv,"\\$&")}function mv(t){return new RegExp("^(?:"+t.map(bv).join("|")+")","i")}function xv(t){return new Map(t.map(((t,n)=>[t.toLowerCase(),n])))}function wv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function Mv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function Tv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function Av(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function Sv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function Ev(t,n,e){var r=gv.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function Nv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function kv(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function Cv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function Pv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function zv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function $v(t,n,e){var r=gv.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Dv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Rv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Fv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function qv(t,n,e){var r=gv.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function Uv(t,n,e){var r=gv.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Iv(t,n,e){var r=yv.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Ov(t,n,e){var r=gv.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function Bv(t,n,e){var r=gv.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function Yv(t,n){return _v(t.getDate(),n,2)}function Lv(t,n){return _v(t.getHours(),n,2)}function jv(t,n){return _v(t.getHours()%12||12,n,2)}function Hv(t,n){return _v(1+py.count(tv(t),t),n,3)}function Xv(t,n){return _v(t.getMilliseconds(),n,3)}function Gv(t,n){return Xv(t,n)+"000"}function Vv(t,n){return _v(t.getMonth()+1,n,2)}function Wv(t,n){return _v(t.getMinutes(),n,2)}function Zv(t,n){return _v(t.getSeconds(),n,2)}function Kv(t){var n=t.getDay();return 0===n?7:n}function Qv(t,n){return _v(xy.count(tv(t)-1,t),n,2)}function Jv(t){var n=t.getDay();return n>=4||0===n?Ay(t):Ay.ceil(t)}function t_(t,n){return t=Jv(t),_v(Ay.count(tv(t),t)+(4===tv(t).getDay()),n,2)}function n_(t){return t.getDay()}function e_(t,n){return _v(wy.count(tv(t)-1,t),n,2)}function r_(t,n){return _v(t.getFullYear()%100,n,2)}function i_(t,n){return _v((t=Jv(t)).getFullYear()%100,n,2)}function o_(t,n){return _v(t.getFullYear()%1e4,n,4)}function a_(t,n){var e=t.getDay();return _v((t=e>=4||0===e?Ay(t):Ay.ceil(t)).getFullYear()%1e4,n,4)}function u_(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+_v(n/60|0,"0",2)+_v(n%60,"0",2)}function c_(t,n){return _v(t.getUTCDate(),n,2)}function f_(t,n){return _v(t.getUTCHours(),n,2)}function s_(t,n){return _v(t.getUTCHours()%12||12,n,2)}function l_(t,n){return _v(1+yy.count(ev(t),t),n,3)}function h_(t,n){return _v(t.getUTCMilliseconds(),n,3)}function d_(t,n){return h_(t,n)+"000"}function p_(t,n){return _v(t.getUTCMonth()+1,n,2)}function g_(t,n){return _v(t.getUTCMinutes(),n,2)}function y_(t,n){return _v(t.getUTCSeconds(),n,2)}function v_(t){var n=t.getUTCDay();return 0===n?7:n}function __(t,n){return _v(Fy.count(ev(t)-1,t),n,2)}function b_(t){var n=t.getUTCDay();return n>=4||0===n?Oy(t):Oy.ceil(t)}function m_(t,n){return t=b_(t),_v(Oy.count(ev(t),t)+(4===ev(t).getUTCDay()),n,2)}function x_(t){return t.getUTCDay()}function w_(t,n){return _v(qy.count(ev(t)-1,t),n,2)}function M_(t,n){return _v(t.getUTCFullYear()%100,n,2)}function T_(t,n){return _v((t=b_(t)).getUTCFullYear()%100,n,2)}function A_(t,n){return _v(t.getUTCFullYear()%1e4,n,4)}function S_(t,n){var e=t.getUTCDay();return _v((t=e>=4||0===e?Oy(t):Oy.ceil(t)).getUTCFullYear()%1e4,n,4)}function E_(){return"+0000"}function N_(){return"%"}function k_(t){return+t}function C_(t){return Math.floor(+t/1e3)}function P_(n){return dv=hv(n),t.timeFormat=dv.format,t.timeParse=dv.parse,t.utcFormat=dv.utcFormat,t.utcParse=dv.utcParse,dv}t.timeFormat=void 0,t.timeParse=void 0,t.utcFormat=void 0,t.utcParse=void 0,P_({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var z_="%Y-%m-%dT%H:%M:%S.%LZ";var $_=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(z_),D_=$_;var R_=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(z_),F_=R_;function q_(t){return new Date(t)}function U_(t){return t instanceof Date?+t:+new Date(+t)}function I_(t,n,e,r,i,o,a,u,c,f){var s=Sg(),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),g=f("%I:%M"),y=f("%I %p"),v=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y");function x(t){return(c(t)<t?d:u(t)<t?p:a(t)<t?g:o(t)<t?y:r(t)<t?i(t)<t?v:_:e(t)<t?b:m)(t)}return s.invert=function(t){return new Date(l(t))},s.domain=function(t){return arguments.length?h(Array.from(t,U_)):h().map(q_)},s.ticks=function(n){var e=h();return t(e[0],e[e.length-1],null==n?10:n)},s.tickFormat=function(t,n){return null==n?x:f(n)},s.nice=function(t){var e=h();return t&&"function"==typeof t.range||(t=n(e[0],e[e.length-1],null==t?10:t)),t?h(kg(e,t)):s},s.copy=function(){return Tg(s,I_(t,n,e,r,i,o,a,u,c,f))},s}function O_(){var t,n,e,r,i,o=0,a=1,u=mg,c=!1;function f(n){return null==n||isNaN(n=+n)?i:u(0===e?.5:(n=(r(n)-t)*e,c?Math.max(0,Math.min(1,n)):n))}function s(t){return function(n){var e,r;return arguments.length?([e,r]=n,u=t(e,r),f):[u(0),u(1)]}}return f.domain=function(i){return arguments.length?([o,a]=i,t=r(o=+o),n=r(a=+a),e=t===n?0:1/(n-t),f):[o,a]},f.clamp=function(t){return arguments.length?(c=!!t,f):c},f.interpolator=function(t){return arguments.length?(u=t,f):u},f.range=s(Gr),f.rangeRound=s(Vr),f.unknown=function(t){return arguments.length?(i=t,f):i},function(i){return r=i,t=i(o),n=i(a),e=t===n?0:1/(n-t),f}}function B_(t,n){return n.domain(t.domain()).interpolator(t.interpolator()).clamp(t.clamp()).unknown(t.unknown())}function Y_(){var t=Lg(O_());return t.copy=function(){return B_(t,Y_()).exponent(t.exponent())},dg.apply(t,arguments)}function L_(){var t,n,e,r,i,o,a,u=0,c=.5,f=1,s=1,l=mg,h=!1;function d(t){return isNaN(t=+t)?a:(t=.5+((t=+o(t))-n)*(s*t<s*n?r:i),l(h?Math.max(0,Math.min(1,t)):t))}function p(t){return function(n){var e,r,i;return arguments.length?([e,r,i]=n,l=di(t,[e,r,i]),d):[l(0),l(.5),l(1)]}}return d.domain=function(a){return arguments.length?([u,c,f]=a,t=o(u=+u),n=o(c=+c),e=o(f=+f),r=t===n?0:.5/(n-t),i=n===e?0:.5/(e-n),s=n<t?-1:1,d):[u,c,f]},d.clamp=function(t){return arguments.length?(h=!!t,d):h},d.interpolator=function(t){return arguments.length?(l=t,d):l},d.range=p(Gr),d.rangeRound=p(Vr),d.unknown=function(t){return arguments.length?(a=t,d):a},function(a){return o=a,t=a(u),n=a(c),e=a(f),r=t===n?0:.5/(n-t),i=n===e?0:.5/(e-n),s=n<t?-1:1,d}}function j_(){var t=Lg(L_());return t.copy=function(){return B_(t,j_()).exponent(t.exponent())},dg.apply(t,arguments)}function H_(t){for(var n=t.length/6|0,e=new Array(n),r=0;r<n;)e[r]="#"+t.slice(6*r,6*++r);return e}var X_=H_("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),G_=H_("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666"),V_=H_("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666"),W_=H_("4269d0efb118ff725c6cc5b03ca951ff8ab7a463f297bbf59c6b4e9498a0"),Z_=H_("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928"),K_=H_("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2"),Q_=H_("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc"),J_=H_("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999"),tb=H_("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3"),nb=H_("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f"),eb=H_("4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab"),rb=t=>Fr(t[t.length-1]),ib=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(H_),ob=rb(ib),ab=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(H_),ub=rb(ab),cb=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(H_),fb=rb(cb),sb=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(H_),lb=rb(sb),hb=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(H_),db=rb(hb),pb=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(H_),gb=rb(pb),yb=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(H_),vb=rb(yb),_b=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(H_),bb=rb(_b),mb=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(H_),xb=rb(mb),wb=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(H_),Mb=rb(wb),Tb=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(H_),Ab=rb(Tb),Sb=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(H_),Eb=rb(Sb),Nb=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(H_),kb=rb(Nb),Cb=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(H_),Pb=rb(Cb),zb=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(H_),$b=rb(zb),Db=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(H_),Rb=rb(Db),Fb=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(H_),qb=rb(Fb),Ub=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(H_),Ib=rb(Ub),Ob=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(H_),Bb=rb(Ob),Yb=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(H_),Lb=rb(Yb),jb=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(H_),Hb=rb(jb),Xb=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(H_),Gb=rb(Xb),Vb=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(H_),Wb=rb(Vb),Zb=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(H_),Kb=rb(Zb),Qb=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(H_),Jb=rb(Qb),tm=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(H_),nm=rb(tm),em=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(H_),rm=rb(em);var im=hi(Tr(300,.5,0),Tr(-240,.5,1)),om=hi(Tr(-100,.75,.35),Tr(80,1.5,.8)),am=hi(Tr(260,.75,.35),Tr(80,1.5,.8)),um=Tr();var cm=Fe(),fm=Math.PI/3,sm=2*Math.PI/3;function lm(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}var hm=lm(H_("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),dm=lm(H_("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),pm=lm(H_("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),gm=lm(H_("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function ym(t){return function(){return t}}const vm=Math.abs,_m=Math.atan2,bm=Math.cos,mm=Math.max,xm=Math.min,wm=Math.sin,Mm=Math.sqrt,Tm=1e-12,Am=Math.PI,Sm=Am/2,Em=2*Am;function Nm(t){return t>=1?Sm:t<=-1?-Sm:Math.asin(t)}function km(t){let n=3;return t.digits=function(e){if(!arguments.length)return n;if(null==e)n=null;else{const t=Math.floor(e);if(!(t>=0))throw new RangeError(`invalid digits: ${e}`);n=t}return t},()=>new Ua(n)}function Cm(t){return t.innerRadius}function Pm(t){return t.outerRadius}function zm(t){return t.startAngle}function $m(t){return t.endAngle}function Dm(t){return t&&t.padAngle}function Rm(t,n,e,r,i,o,a){var u=t-e,c=n-r,f=(a?o:-o)/Mm(u*u+c*c),s=f*c,l=-f*u,h=t+s,d=n+l,p=e+s,g=r+l,y=(h+p)/2,v=(d+g)/2,_=p-h,b=g-d,m=_*_+b*b,x=i-o,w=h*g-p*d,M=(b<0?-1:1)*Mm(mm(0,x*x*m-w*w)),T=(w*b-_*M)/m,A=(-w*_-b*M)/m,S=(w*b+_*M)/m,E=(-w*_+b*M)/m,N=T-y,k=A-v,C=S-y,P=E-v;return N*N+k*k>C*C+P*P&&(T=S,A=E),{cx:T,cy:A,x01:-s,y01:-l,x11:T*(i/x-1),y11:A*(i/x-1)}}var Fm=Array.prototype.slice;function qm(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Um(t){this._context=t}function Im(t){return new Um(t)}function Om(t){return t[0]}function Bm(t){return t[1]}function Ym(t,n){var e=ym(!0),r=null,i=Im,o=null,a=km(u);function u(u){var c,f,s,l=(u=qm(u)).length,h=!1;for(null==r&&(o=i(s=a())),c=0;c<=l;++c)!(c<l&&e(f=u[c],c,u))===h&&((h=!h)?o.lineStart():o.lineEnd()),h&&o.point(+t(f,c,u),+n(f,c,u));if(s)return o=null,s+""||null}return t="function"==typeof t?t:void 0===t?Om:ym(t),n="function"==typeof n?n:void 0===n?Bm:ym(n),u.x=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),u):t},u.y=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),u):n},u.defined=function(t){return arguments.length?(e="function"==typeof t?t:ym(!!t),u):e},u.curve=function(t){return arguments.length?(i=t,null!=r&&(o=i(r)),u):i},u.context=function(t){return arguments.length?(null==t?r=o=null:o=i(r=t),u):r},u}function Lm(t,n,e){var r=null,i=ym(!0),o=null,a=Im,u=null,c=km(f);function f(f){var s,l,h,d,p,g=(f=qm(f)).length,y=!1,v=new Array(g),_=new Array(g);for(null==o&&(u=a(p=c())),s=0;s<=g;++s){if(!(s<g&&i(d=f[s],s,f))===y)if(y=!y)l=s,u.areaStart(),u.lineStart();else{for(u.lineEnd(),u.lineStart(),h=s-1;h>=l;--h)u.point(v[h],_[h]);u.lineEnd(),u.areaEnd()}y&&(v[s]=+t(d,s,f),_[s]=+n(d,s,f),u.point(r?+r(d,s,f):v[s],e?+e(d,s,f):_[s]))}if(p)return u=null,p+""||null}function s(){return Ym().defined(i).curve(a).context(o)}return t="function"==typeof t?t:void 0===t?Om:ym(+t),n="function"==typeof n?n:ym(void 0===n?0:+n),e="function"==typeof e?e:void 0===e?Bm:ym(+e),f.x=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),r=null,f):t},f.x0=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),f):t},f.x1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:ym(+t),f):r},f.y=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),e=null,f):n},f.y0=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),f):n},f.y1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:ym(+t),f):e},f.lineX0=f.lineY0=function(){return s().x(t).y(n)},f.lineY1=function(){return s().x(t).y(e)},f.lineX1=function(){return s().x(r).y(n)},f.defined=function(t){return arguments.length?(i="function"==typeof t?t:ym(!!t),f):i},f.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),f):a},f.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),f):o},f}function jm(t,n){return n<t?-1:n>t?1:n>=t?0:NaN}function Hm(t){return t}Um.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var Xm=Vm(Im);function Gm(t){this._curve=t}function Vm(t){function n(n){return new Gm(t(n))}return n._curve=t,n}function Wm(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(Vm(t)):n()._curve},t}function Zm(){return Wm(Ym().curve(Xm))}function Km(){var t=Lm().curve(Xm),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Wm(e())},delete t.lineX0,t.lineEndAngle=function(){return Wm(r())},delete t.lineX1,t.lineInnerRadius=function(){return Wm(i())},delete t.lineY0,t.lineOuterRadius=function(){return Wm(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(Vm(t)):n()._curve},t}function Qm(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}Gm.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};class Jm{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}class tx{constructor(t){this._context=t}lineStart(){this._point=0}lineEnd(){}point(t,n){if(t=+t,n=+n,0===this._point)this._point=1;else{const e=Qm(this._x0,this._y0),r=Qm(this._x0,this._y0=(this._y0+n)/2),i=Qm(t,this._y0),o=Qm(t,n);this._context.moveTo(...e),this._context.bezierCurveTo(...r,...i,...o)}this._x0=t,this._y0=n}}function nx(t){return new Jm(t,!0)}function ex(t){return new Jm(t,!1)}function rx(t){return new tx(t)}function ix(t){return t.source}function ox(t){return t.target}function ax(t){let n=ix,e=ox,r=Om,i=Bm,o=null,a=null,u=km(c);function c(){let c;const f=Fm.call(arguments),s=n.apply(this,f),l=e.apply(this,f);if(null==o&&(a=t(c=u())),a.lineStart(),f[0]=s,a.point(+r.apply(this,f),+i.apply(this,f)),f[0]=l,a.point(+r.apply(this,f),+i.apply(this,f)),a.lineEnd(),c)return a=null,c+""||null}return c.source=function(t){return arguments.length?(n=t,c):n},c.target=function(t){return arguments.length?(e=t,c):e},c.x=function(t){return arguments.length?(r="function"==typeof t?t:ym(+t),c):r},c.y=function(t){return arguments.length?(i="function"==typeof t?t:ym(+t),c):i},c.context=function(n){return arguments.length?(null==n?o=a=null:a=t(o=n),c):o},c}const ux=Mm(3);var cx={draw(t,n){const e=.59436*Mm(n+xm(n/28,.75)),r=e/2,i=r*ux;t.moveTo(0,e),t.lineTo(0,-e),t.moveTo(-i,-r),t.lineTo(i,r),t.moveTo(-i,r),t.lineTo(i,-r)}},fx={draw(t,n){const e=Mm(n/Am);t.moveTo(e,0),t.arc(0,0,e,0,Em)}},sx={draw(t,n){const e=Mm(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}};const lx=Mm(1/3),hx=2*lx;var dx={draw(t,n){const e=Mm(n/hx),r=e*lx;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},px={draw(t,n){const e=.62625*Mm(n);t.moveTo(0,-e),t.lineTo(e,0),t.lineTo(0,e),t.lineTo(-e,0),t.closePath()}},gx={draw(t,n){const e=.87559*Mm(n-xm(n/7,2));t.moveTo(-e,0),t.lineTo(e,0),t.moveTo(0,e),t.lineTo(0,-e)}},yx={draw(t,n){const e=Mm(n),r=-e/2;t.rect(r,r,e,e)}},vx={draw(t,n){const e=.4431*Mm(n);t.moveTo(e,e),t.lineTo(e,-e),t.lineTo(-e,-e),t.lineTo(-e,e),t.closePath()}};const _x=wm(Am/10)/wm(7*Am/10),bx=wm(Em/10)*_x,mx=-bm(Em/10)*_x;var xx={draw(t,n){const e=Mm(.8908130915292852*n),r=bx*e,i=mx*e;t.moveTo(0,-e),t.lineTo(r,i);for(let n=1;n<5;++n){const o=Em*n/5,a=bm(o),u=wm(o);t.lineTo(u*e,-a*e),t.lineTo(a*r-u*i,u*r+a*i)}t.closePath()}};const wx=Mm(3);var Mx={draw(t,n){const e=-Mm(n/(3*wx));t.moveTo(0,2*e),t.lineTo(-wx*e,-e),t.lineTo(wx*e,-e),t.closePath()}};const Tx=Mm(3);var Ax={draw(t,n){const e=.6824*Mm(n),r=e/2,i=e*Tx/2;t.moveTo(0,-e),t.lineTo(i,r),t.lineTo(-i,r),t.closePath()}};const Sx=-.5,Ex=Mm(3)/2,Nx=1/Mm(12),kx=3*(Nx/2+1);var Cx={draw(t,n){const e=Mm(n/kx),r=e/2,i=e*Nx,o=r,a=e*Nx+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(Sx*r-Ex*i,Ex*r+Sx*i),t.lineTo(Sx*o-Ex*a,Ex*o+Sx*a),t.lineTo(Sx*u-Ex*c,Ex*u+Sx*c),t.lineTo(Sx*r+Ex*i,Sx*i-Ex*r),t.lineTo(Sx*o+Ex*a,Sx*a-Ex*o),t.lineTo(Sx*u+Ex*c,Sx*c-Ex*u),t.closePath()}},Px={draw(t,n){const e=.6189*Mm(n-xm(n/6,1.7));t.moveTo(-e,-e),t.lineTo(e,e),t.moveTo(-e,e),t.lineTo(e,-e)}};const zx=[fx,sx,dx,yx,xx,Mx,Cx],$x=[fx,gx,Px,Ax,cx,vx,px];function Dx(){}function Rx(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function Fx(t){this._context=t}function qx(t){this._context=t}function Ux(t){this._context=t}function Ix(t,n){this._basis=new Fx(t),this._beta=n}Fx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Rx(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Rx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},qx.prototype={areaStart:Dx,areaEnd:Dx,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Rx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ux.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Rx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ix.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var Ox=function t(n){function e(t){return 1===n?new Fx(t):new Ix(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function Bx(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function Yx(t,n){this._context=t,this._k=(1-n)/6}Yx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Bx(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Bx(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Lx=function t(n){function e(t){return new Yx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function jx(t,n){this._context=t,this._k=(1-n)/6}jx.prototype={areaStart:Dx,areaEnd:Dx,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Bx(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Hx=function t(n){function e(t){return new jx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Xx(t,n){this._context=t,this._k=(1-n)/6}Xx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Bx(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Gx=function t(n){function e(t){return new Xx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Vx(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>Tm){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>Tm){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function Wx(t,n){this._context=t,this._alpha=n}Wx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Vx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Zx=function t(n){function e(t){return n?new Wx(t,n):new Yx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Kx(t,n){this._context=t,this._alpha=n}Kx.prototype={areaStart:Dx,areaEnd:Dx,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Vx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Qx=function t(n){function e(t){return n?new Kx(t,n):new jx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Jx(t,n){this._context=t,this._alpha=n}Jx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Vx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var tw=function t(n){function e(t){return n?new Jx(t,n):new Xx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function nw(t){this._context=t}function ew(t){return t<0?-1:1}function rw(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(ew(o)+ew(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function iw(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function ow(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function aw(t){this._context=t}function uw(t){this._context=new cw(t)}function cw(t){this._context=t}function fw(t){this._context=t}function sw(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n<r-1;++n)i[n]=1,o[n]=4,a[n]=4*t[n]+2*t[n+1];for(i[r-1]=2,o[r-1]=7,a[r-1]=8*t[r-1]+t[r],n=1;n<r;++n)e=i[n]/o[n-1],o[n]-=e,a[n]-=e*a[n-1];for(i[r-1]=a[r-1]/o[r-1],n=r-2;n>=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n<r-1;++n)o[n]=2*t[n+1]-i[n+1];return[i,o]}function lw(t,n){this._context=t,this._t=n}function hw(t,n){if((i=t.length)>1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o<i;++o)for(r=a,a=t[n[o]],e=0;e<u;++e)a[e][1]+=a[e][0]=isNaN(r[e][1])?r[e][0]:r[e][1]}function dw(t){for(var n=t.length,e=new Array(n);--n>=0;)e[n]=n;return e}function pw(t,n){return t[n]}function gw(t){const n=[];return n.key=t,n}function yw(t){var n=t.map(vw);return dw(t).sort((function(t,e){return n[t]-n[e]}))}function vw(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++e<i;)(n=+t[e][1])>o&&(o=n,r=e);return r}function _w(t){var n=t.map(bw);return dw(t).sort((function(t,e){return n[t]-n[e]}))}function bw(t){for(var n,e=0,r=-1,i=t.length;++r<i;)(n=+t[r][1])&&(e+=n);return e}nw.prototype={areaStart:Dx,areaEnd:Dx,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t=+t,n=+n,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}},aw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:ow(this,this._t0,iw(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){var e=NaN;if(n=+n,(t=+t)!==this._x1||n!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,ow(this,iw(this,e=rw(this,t,n)),e);break;default:ow(this,this._t0,e=rw(this,t,n))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n,this._t0=e}}},(uw.prototype=Object.create(aw.prototype)).point=function(t,n){aw.prototype.point.call(this,n,t)},cw.prototype={moveTo:function(t,n){this._context.moveTo(n,t)},closePath:function(){this._context.closePath()},lineTo:function(t,n){this._context.lineTo(n,t)},bezierCurveTo:function(t,n,e,r,i,o){this._context.bezierCurveTo(n,t,r,e,o,i)}},fw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,n=this._y,e=t.length;if(e)if(this._line?this._context.lineTo(t[0],n[0]):this._context.moveTo(t[0],n[0]),2===e)this._context.lineTo(t[1],n[1]);else for(var r=sw(t),i=sw(n),o=0,a=1;a<e;++o,++a)this._context.bezierCurveTo(r[0][o],i[0][o],r[1][o],i[1][o],t[a],n[a]);(this._line||0!==this._line&&1===e)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(t,n){this._x.push(+t),this._y.push(+n)}},lw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&2===this._point&&this._context.lineTo(this._x,this._y),(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var mw=t=>()=>t;function xw(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function ww(t,n,e){this.k=t,this.x=n,this.y=e}ww.prototype={constructor:ww,scale:function(t){return 1===t?this:new ww(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new ww(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Mw=new ww(1,0,0);function Tw(t){for(;!t.__zoom;)if(!(t=t.parentNode))return Mw;return t.__zoom}function Aw(t){t.stopImmediatePropagation()}function Sw(t){t.preventDefault(),t.stopImmediatePropagation()}function Ew(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function Nw(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function kw(){return this.__zoom||Mw}function Cw(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function Pw(){return navigator.maxTouchPoints||"ontouchstart"in this}function zw(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}Tw.prototype=ww.prototype,t.Adder=T,t.Delaunay=Lu,t.FormatSpecifier=tf,t.InternMap=InternMap,t.InternSet=InternSet,t.Node=Qd,t.Path=Ua,t.Voronoi=qu,t.ZoomTransform=ww,t.active=function(t,n){var e,r,i=t.__transition;if(i)for(r in n=null==n?null:n+"",i)if((e=i[r]).state>qi&&e.name===n)return new po([[t]],Zo,n,+r);return null},t.arc=function(){var t=Cm,n=Pm,e=ym(0),r=null,i=zm,o=$m,a=Dm,u=null,c=km(f);function f(){var f,s,l=+t.apply(this,arguments),h=+n.apply(this,arguments),d=i.apply(this,arguments)-Sm,p=o.apply(this,arguments)-Sm,g=vm(p-d),y=p>d;if(u||(u=f=c()),h<l&&(s=h,h=l,l=s),h>Tm)if(g>Em-Tm)u.moveTo(h*bm(d),h*wm(d)),u.arc(0,0,h,d,p,!y),l>Tm&&(u.moveTo(l*bm(p),l*wm(p)),u.arc(0,0,l,p,d,y));else{var v,_,b=d,m=p,x=d,w=p,M=g,T=g,A=a.apply(this,arguments)/2,S=A>Tm&&(r?+r.apply(this,arguments):Mm(l*l+h*h)),E=xm(vm(h-l)/2,+e.apply(this,arguments)),N=E,k=E;if(S>Tm){var C=Nm(S/l*wm(A)),P=Nm(S/h*wm(A));(M-=2*C)>Tm?(x+=C*=y?1:-1,w-=C):(M=0,x=w=(d+p)/2),(T-=2*P)>Tm?(b+=P*=y?1:-1,m-=P):(T=0,b=m=(d+p)/2)}var z=h*bm(b),$=h*wm(b),D=l*bm(w),R=l*wm(w);if(E>Tm){var F,q=h*bm(m),U=h*wm(m),I=l*bm(x),O=l*wm(x);if(g<Am)if(F=function(t,n,e,r,i,o,a,u){var c=e-t,f=r-n,s=a-i,l=u-o,h=l*c-s*f;if(!(h*h<Tm))return[t+(h=(s*(n-o)-l*(t-i))/h)*c,n+h*f]}(z,$,I,O,q,U,D,R)){var B=z-F[0],Y=$-F[1],L=q-F[0],j=U-F[1],H=1/wm(function(t){return t>1?0:t<-1?Am:Math.acos(t)}((B*L+Y*j)/(Mm(B*B+Y*Y)*Mm(L*L+j*j)))/2),X=Mm(F[0]*F[0]+F[1]*F[1]);N=xm(E,(l-X)/(H-1)),k=xm(E,(h-X)/(H+1))}else N=k=0}T>Tm?k>Tm?(v=Rm(I,O,z,$,h,k,y),_=Rm(q,U,D,R,h,k,y),u.moveTo(v.cx+v.x01,v.cy+v.y01),k<E?u.arc(v.cx,v.cy,k,_m(v.y01,v.x01),_m(_.y01,_.x01),!y):(u.arc(v.cx,v.cy,k,_m(v.y01,v.x01),_m(v.y11,v.x11),!y),u.arc(0,0,h,_m(v.cy+v.y11,v.cx+v.x11),_m(_.cy+_.y11,_.cx+_.x11),!y),u.arc(_.cx,_.cy,k,_m(_.y11,_.x11),_m(_.y01,_.x01),!y))):(u.moveTo(z,$),u.arc(0,0,h,b,m,!y)):u.moveTo(z,$),l>Tm&&M>Tm?N>Tm?(v=Rm(D,R,q,U,l,-N,y),_=Rm(z,$,I,O,l,-N,y),u.lineTo(v.cx+v.x01,v.cy+v.y01),N<E?u.arc(v.cx,v.cy,N,_m(v.y01,v.x01),_m(_.y01,_.x01),!y):(u.arc(v.cx,v.cy,N,_m(v.y01,v.x01),_m(v.y11,v.x11),!y),u.arc(0,0,l,_m(v.cy+v.y11,v.cx+v.x11),_m(_.cy+_.y11,_.cx+_.x11),y),u.arc(_.cx,_.cy,N,_m(_.y11,_.x11),_m(_.y01,_.x01),!y))):u.arc(0,0,l,w,x,y):u.lineTo(D,R)}else u.moveTo(0,0);if(u.closePath(),f)return u=null,f+""||null}return f.centroid=function(){var e=(+t.apply(this,arguments)+ +n.apply(this,arguments))/2,r=(+i.apply(this,arguments)+ +o.apply(this,arguments))/2-Am/2;return[bm(r)*e,wm(r)*e]},f.innerRadius=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),f):t},f.outerRadius=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),f):n},f.cornerRadius=function(t){return arguments.length?(e="function"==typeof t?t:ym(+t),f):e},f.padRadius=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:ym(+t),f):r},f.startAngle=function(t){return arguments.length?(i="function"==typeof t?t:ym(+t),f):i},f.endAngle=function(t){return arguments.length?(o="function"==typeof t?t:ym(+t),f):o},f.padAngle=function(t){return arguments.length?(a="function"==typeof t?t:ym(+t),f):a},f.context=function(t){return arguments.length?(u=null==t?null:t,f):u},f},t.area=Lm,t.areaRadial=Km,t.ascending=n,t.autoType=function(t){for(var n in t){var e,r,i=t[n].trim();if(i)if("true"===i)i=!0;else if("false"===i)i=!1;else if("NaN"===i)i=NaN;else if(isNaN(e=+i)){if(!(r=i.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/)))continue;yc&&r[4]&&!r[7]&&(i=i.replace(/-/g,"/").replace(/T/," ")),i=new Date(i)}else i=e;else i=null;t[n]=i}return t},t.axisBottom=function(t){return Pt(Mt,t)},t.axisLeft=function(t){return Pt(Tt,t)},t.axisRight=function(t){return Pt(wt,t)},t.axisTop=function(t){return Pt(xt,t)},t.bin=Q,t.bisect=s,t.bisectCenter=f,t.bisectLeft=c,t.bisectRight=u,t.bisector=r,t.blob=function(t,n){return fetch(t,n).then(vc)},t.blur=function(t,n){if(!((n=+n)>=0))throw new RangeError("invalid r");let e=t.length;if(!((e=Math.floor(e))>=0))throw new RangeError("invalid length");if(!e||!n)return t;const r=y(n),i=t.slice();return r(t,i,0,e,1),r(i,t,0,e,1),r(t,i,0,e,1),t},t.blur2=l,t.blurImage=h,t.brush=function(){return wa(la)},t.brushSelection=function(t){var n=t.__brush;return n?n.dim.output(n.selection):null},t.brushX=function(){return wa(fa)},t.brushY=function(){return wa(sa)},t.buffer=function(t,n){return fetch(t,n).then(_c)},t.chord=function(){return za(!1,!1)},t.chordDirected=function(){return za(!0,!1)},t.chordTranspose=function(){return za(!1,!0)},t.cluster=function(){var t=Ld,n=1,e=1,r=!1;function i(i){var o,a=0;i.eachAfter((function(n){var e=n.children;e?(n.x=function(t){return t.reduce(jd,0)/t.length}(e),n.y=function(t){return 1+t.reduce(Hd,0)}(e)):(n.x=o?a+=t(n,o):0,n.y=0,o=n)}));var u=function(t){for(var n;n=t.children;)t=n[0];return t}(i),c=function(t){for(var n;n=t.children;)t=n[n.length-1];return t}(i),f=u.x-t(u,c)/2,s=c.x+t(c,u)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*n,t.y=(i.y-t.y)*e}:function(t){t.x=(t.x-f)/(s-f)*n,t.y=(1-(i.y?t.y/i.y:1))*e})}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.color=ze,t.contourDensity=function(){var t=fu,n=su,e=lu,r=960,i=500,o=20,a=2,u=3*o,c=r+2*u>>a,f=i+2*u>>a,s=Qa(20);function h(r){var i=new Float32Array(c*f),s=Math.pow(2,-a),h=-1;for(const o of r){var d=(t(o,++h,r)+u)*s,p=(n(o,h,r)+u)*s,g=+e(o,h,r);if(g&&d>=0&&d<c&&p>=0&&p<f){var y=Math.floor(d),v=Math.floor(p),_=d-y-.5,b=p-v-.5;i[y+v*c]+=(1-_)*(1-b)*g,i[y+1+v*c]+=_*(1-b)*g,i[y+1+(v+1)*c]+=_*b*g,i[y+(v+1)*c]+=(1-_)*b*g}}return l({data:i,width:c,height:f},o*s),i}function d(t){var n=h(t),e=s(n),r=Math.pow(2,2*a);return Array.isArray(e)||(e=G(Number.MIN_VALUE,J(n)/r,e)),iu().size([c,f]).thresholds(e.map((t=>t*r)))(n).map(((t,n)=>(t.value=+e[n],p(t))))}function p(t){return t.coordinates.forEach(g),t}function g(t){t.forEach(y)}function y(t){t.forEach(v)}function v(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function _(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,d}return d.contours=function(t){var n=h(t),e=iu().size([c,f]),r=Math.pow(2,2*a),i=t=>{t=+t;var i=p(e.contour(n,t*r));return i.value=t,i};return Object.defineProperty(i,"max",{get:()=>J(n)/r}),i},d.x=function(n){return arguments.length?(t="function"==typeof n?n:Qa(+n),d):t},d.y=function(t){return arguments.length?(n="function"==typeof t?t:Qa(+t),d):n},d.weight=function(t){return arguments.length?(e="function"==typeof t?t:Qa(+t),d):e},d.size=function(t){if(!arguments.length)return[r,i];var n=+t[0],e=+t[1];if(!(n>=0&&e>=0))throw new Error("invalid size");return r=n,i=e,_()},d.cellSize=function(t){if(!arguments.length)return 1<<a;if(!((t=+t)>=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),_()},d.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?Qa(Za.call(t)):Qa(t),d):s},d.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=(Math.sqrt(4*t*t+1)-1)/2,_()},d},t.contours=iu,t.count=v,t.create=function(t){return Zn(Yt(t).call(document.documentElement))},t.creator=Yt,t.cross=function(...t){const n="function"==typeof t[t.length-1]&&function(t){return n=>t(...n)}(t.pop()),e=(t=t.map(m)).map(_),r=t.length-1,i=new Array(r+1).fill(0),o=[];if(r<0||e.some(b))return o;for(;;){o.push(i.map(((n,e)=>t[e][n])));let a=r;for(;++i[a]===e[a];){if(0===a)return n?o.map(n):o;i[a--]=0}}},t.csv=wc,t.csvFormat=rc,t.csvFormatBody=ic,t.csvFormatRow=ac,t.csvFormatRows=oc,t.csvFormatValue=uc,t.csvParse=nc,t.csvParseRows=ec,t.cubehelix=Tr,t.cumsum=function(t,n){var e=0,r=0;return Float64Array.from(t,void 0===n?t=>e+=+t||0:i=>e+=+n(i,r++,t)||0)},t.curveBasis=function(t){return new Fx(t)},t.curveBasisClosed=function(t){return new qx(t)},t.curveBasisOpen=function(t){return new Ux(t)},t.curveBumpX=nx,t.curveBumpY=ex,t.curveBundle=Ox,t.curveCardinal=Lx,t.curveCardinalClosed=Hx,t.curveCardinalOpen=Gx,t.curveCatmullRom=Zx,t.curveCatmullRomClosed=Qx,t.curveCatmullRomOpen=tw,t.curveLinear=Im,t.curveLinearClosed=function(t){return new nw(t)},t.curveMonotoneX=function(t){return new aw(t)},t.curveMonotoneY=function(t){return new uw(t)},t.curveNatural=function(t){return new fw(t)},t.curveStep=function(t){return new lw(t,.5)},t.curveStepAfter=function(t){return new lw(t,1)},t.curveStepBefore=function(t){return new lw(t,0)},t.descending=e,t.deviation=w,t.difference=function(t,...n){t=new InternSet(t);for(const e of n)for(const n of e)t.delete(n);return t},t.disjoint=function(t,n){const e=n[Symbol.iterator](),r=new InternSet;for(const n of t){if(r.has(n))return!1;let t,i;for(;({value:t,done:i}=e.next())&&!i;){if(Object.is(n,t))return!1;r.add(t)}}return!0},t.dispatch=$t,t.drag=function(){var t,n,e,r,i=se,o=le,a=he,u=de,c={},f=$t("start","drag","end"),s=0,l=0;function h(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v,ee).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var c=b(this,o.call(this,a,u),a,u,"mouse");c&&(Zn(a.view).on("mousemove.drag",p,re).on("mouseup.drag",g,re),ae(a.view),ie(a),e=!1,t=a.clientX,n=a.clientY,c("start",a))}}function p(r){if(oe(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>l}c.mouse("drag",r)}function g(t){Zn(t.view).on("mousemove.drag mouseup.drag",null),ue(t.view,e),oe(t),c.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),c=a.length;for(e=0;e<c;++e)(r=b(this,u,t,n,a[e].identifier,a[e]))&&(ie(t),r("start",t,a[e]))}}function v(t){var n,e,r=t.changedTouches,i=r.length;for(n=0;n<i;++n)(e=c[r[n].identifier])&&(oe(t),e("drag",t,r[n]))}function _(t){var n,e,i=t.changedTouches,o=i.length;for(r&&clearTimeout(r),r=setTimeout((function(){r=null}),500),n=0;n<o;++n)(e=c[i[n].identifier])&&(ie(t),e("end",t,i[n]))}function b(t,n,e,r,i,o){var u,l,d,p=f.copy(),g=ne(o||e,n);if(null!=(d=a.call(t,new fe("beforestart",{sourceEvent:e,target:h,identifier:i,active:s,x:g[0],y:g[1],dx:0,dy:0,dispatch:p}),r)))return u=d.x-g[0]||0,l=d.y-g[1]||0,function e(o,a,f){var y,v=g;switch(o){case"start":c[i]=e,y=s++;break;case"end":delete c[i],--s;case"drag":g=ne(f||a,n),y=s}p.call(o,t,new fe(o,{sourceEvent:a,subject:d,target:h,identifier:i,active:y,x:g[0]+u,y:g[1]+l,dx:g[0]-v[0],dy:g[1]-v[1],dispatch:p}),r)}}return h.filter=function(t){return arguments.length?(i="function"==typeof t?t:ce(!!t),h):i},h.container=function(t){return arguments.length?(o="function"==typeof t?t:ce(t),h):o},h.subject=function(t){return arguments.length?(a="function"==typeof t?t:ce(t),h):a},h.touchable=function(t){return arguments.length?(u="function"==typeof t?t:ce(!!t),h):u},h.on=function(){var t=f.on.apply(f,arguments);return t===f?h:t},h.clickDistance=function(t){return arguments.length?(l=(t=+t)*t,h):Math.sqrt(l)},h},t.dragDisable=ae,t.dragEnable=ue,t.dsv=function(t,n,e,r){3===arguments.length&&"function"==typeof e&&(r=e,e=void 0);var i=Ju(t);return mc(n,e).then((function(t){return i.parse(t,r)}))},t.dsvFormat=Ju,t.easeBack=Lo,t.easeBackIn=Bo,t.easeBackInOut=Lo,t.easeBackOut=Yo,t.easeBounce=Io,t.easeBounceIn=function(t){return 1-Io(1-t)},t.easeBounceInOut=function(t){return((t*=2)<=1?1-Io(1-t):Io(t-1)+1)/2},t.easeBounceOut=Io,t.easeCircle=No,t.easeCircleIn=function(t){return 1-Math.sqrt(1-t*t)},t.easeCircleInOut=No,t.easeCircleOut=function(t){return Math.sqrt(1- --t*t)},t.easeCubic=bo,t.easeCubicIn=function(t){return t*t*t},t.easeCubicInOut=bo,t.easeCubicOut=function(t){return--t*t*t+1},t.easeElastic=Xo,t.easeElasticIn=Ho,t.easeElasticInOut=Go,t.easeElasticOut=Xo,t.easeExp=Eo,t.easeExpIn=function(t){return So(1-+t)},t.easeExpInOut=Eo,t.easeExpOut=function(t){return 1-So(t)},t.easeLinear=t=>+t,t.easePoly=wo,t.easePolyIn=mo,t.easePolyInOut=wo,t.easePolyOut=xo,t.easeQuad=_o,t.easeQuadIn=function(t){return t*t},t.easeQuadInOut=_o,t.easeQuadOut=function(t){return t*(2-t)},t.easeSin=Ao,t.easeSinIn=function(t){return 1==+t?1:1-Math.cos(t*To)},t.easeSinInOut=Ao,t.easeSinOut=function(t){return Math.sin(t*To)},t.every=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(!n(r,++e,t))return!1;return!0},t.extent=M,t.fcumsum=function(t,n){const e=new T;let r=-1;return Float64Array.from(t,void 0===n?t=>e.add(+t||0):i=>e.add(+n(i,++r,t)||0))},t.filter=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");const e=[];let r=-1;for(const i of t)n(i,++r,t)&&e.push(i);return e},t.flatGroup=function(t,...n){return z(P(t,...n),n)},t.flatRollup=function(t,n,...e){return z(D(t,n,...e),e)},t.forceCenter=function(t,n){var e,r=1;function i(){var i,o,a=e.length,u=0,c=0;for(i=0;i<a;++i)u+=(o=e[i]).x,c+=o.y;for(u=(u/a-t)*r,c=(c/a-n)*r,i=0;i<a;++i)(o=e[i]).x-=u,o.y-=c}return null==t&&(t=0),null==n&&(n=0),i.initialize=function(t){e=t},i.x=function(n){return arguments.length?(t=+n,i):t},i.y=function(t){return arguments.length?(n=+t,i):n},i.strength=function(t){return arguments.length?(r=+t,i):r},i},t.forceCollide=function(t){var n,e,r,i=1,o=1;function a(){for(var t,a,c,f,s,l,h,d=n.length,p=0;p<o;++p)for(a=$c(n,Ic,Oc).visitAfter(u),t=0;t<d;++t)c=n[t],l=e[c.index],h=l*l,f=c.x+c.vx,s=c.y+c.vy,a.visit(g);function g(t,n,e,o,a){var u=t.data,d=t.r,p=l+d;if(!u)return n>f+p||o<f-p||e>s+p||a<s-p;if(u.index>c.index){var g=f-u.x-u.vx,y=s-u.y-u.vy,v=g*g+y*y;v<p*p&&(0===g&&(v+=(g=Uc(r))*g),0===y&&(v+=(y=Uc(r))*y),v=(p-(v=Math.sqrt(v)))/v*i,c.vx+=(g*=v)*(p=(d*=d)/(h+d)),c.vy+=(y*=v)*p,u.vx-=g*(p=1-p),u.vy-=y*p)}}}function u(t){if(t.data)return t.r=e[t.data.index];for(var n=t.r=0;n<4;++n)t[n]&&t[n].r>t.r&&(t.r=t[n].r)}function c(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r<o;++r)i=n[r],e[i.index]=+t(i,r,n)}}return"function"!=typeof t&&(t=qc(null==t?1:+t)),a.initialize=function(t,e){n=t,r=e,c()},a.iterations=function(t){return arguments.length?(o=+t,a):o},a.strength=function(t){return arguments.length?(i=+t,a):i},a.radius=function(n){return arguments.length?(t="function"==typeof n?n:qc(+n),c(),a):t},a},t.forceLink=function(t){var n,e,r,i,o,a,u=Bc,c=function(t){return 1/Math.min(i[t.source.index],i[t.target.index])},f=qc(30),s=1;function l(r){for(var i=0,u=t.length;i<s;++i)for(var c,f,l,h,d,p,g,y=0;y<u;++y)f=(c=t[y]).source,h=(l=c.target).x+l.vx-f.x-f.vx||Uc(a),d=l.y+l.vy-f.y-f.vy||Uc(a),h*=p=((p=Math.sqrt(h*h+d*d))-e[y])/p*r*n[y],d*=p,l.vx-=h*(g=o[y]),l.vy-=d*g,f.vx+=h*(g=1-g),f.vy+=d*g}function h(){if(r){var a,c,f=r.length,s=t.length,l=new Map(r.map(((t,n)=>[u(t,n,r),t])));for(a=0,i=new Array(f);a<s;++a)(c=t[a]).index=a,"object"!=typeof c.source&&(c.source=Yc(l,c.source)),"object"!=typeof c.target&&(c.target=Yc(l,c.target)),i[c.source.index]=(i[c.source.index]||0)+1,i[c.target.index]=(i[c.target.index]||0)+1;for(a=0,o=new Array(s);a<s;++a)c=t[a],o[a]=i[c.source.index]/(i[c.source.index]+i[c.target.index]);n=new Array(s),d(),e=new Array(s),p()}}function d(){if(r)for(var e=0,i=t.length;e<i;++e)n[e]=+c(t[e],e,t)}function p(){if(r)for(var n=0,i=t.length;n<i;++n)e[n]=+f(t[n],n,t)}return null==t&&(t=[]),l.initialize=function(t,n){r=t,a=n,h()},l.links=function(n){return arguments.length?(t=n,h(),l):t},l.id=function(t){return arguments.length?(u=t,l):u},l.iterations=function(t){return arguments.length?(s=+t,l):s},l.strength=function(t){return arguments.length?(c="function"==typeof t?t:qc(+t),d(),l):c},l.distance=function(t){return arguments.length?(f="function"==typeof t?t:qc(+t),p(),l):f},l},t.forceManyBody=function(){var t,n,e,r,i,o=qc(-30),a=1,u=1/0,c=.81;function f(e){var i,o=t.length,a=$c(t,Xc,Gc).visitAfter(l);for(r=e,i=0;i<o;++i)n=t[i],a.visit(h)}function s(){if(t){var n,e,r=t.length;for(i=new Array(r),n=0;n<r;++n)e=t[n],i[e.index]=+o(e,n,t)}}function l(t){var n,e,r,o,a,u=0,c=0;if(t.length){for(r=o=a=0;a<4;++a)(n=t[a])&&(e=Math.abs(n.value))&&(u+=n.value,c+=e,r+=e*n.x,o+=e*n.y);t.x=r/c,t.y=o/c}else{(n=t).x=n.data.x,n.y=n.data.y;do{u+=i[n.data.index]}while(n=n.next)}t.value=u}function h(t,o,f,s){if(!t.value)return!0;var l=t.x-n.x,h=t.y-n.y,d=s-o,p=l*l+h*h;if(d*d/c<p)return p<u&&(0===l&&(p+=(l=Uc(e))*l),0===h&&(p+=(h=Uc(e))*h),p<a&&(p=Math.sqrt(a*p)),n.vx+=l*t.value*r/p,n.vy+=h*t.value*r/p),!0;if(!(t.length||p>=u)){(t.data!==n||t.next)&&(0===l&&(p+=(l=Uc(e))*l),0===h&&(p+=(h=Uc(e))*h),p<a&&(p=Math.sqrt(a*p)));do{t.data!==n&&(d=i[t.data.index]*r/p,n.vx+=l*d,n.vy+=h*d)}while(t=t.next)}}return f.initialize=function(n,r){t=n,e=r,s()},f.strength=function(t){return arguments.length?(o="function"==typeof t?t:qc(+t),s(),f):o},f.distanceMin=function(t){return arguments.length?(a=t*t,f):Math.sqrt(a)},f.distanceMax=function(t){return arguments.length?(u=t*t,f):Math.sqrt(u)},f.theta=function(t){return arguments.length?(c=t*t,f):Math.sqrt(c)},f},t.forceRadial=function(t,n,e){var r,i,o,a=qc(.1);function u(t){for(var a=0,u=r.length;a<u;++a){var c=r[a],f=c.x-n||1e-6,s=c.y-e||1e-6,l=Math.sqrt(f*f+s*s),h=(o[a]-l)*i[a]*t/l;c.vx+=f*h,c.vy+=s*h}}function c(){if(r){var n,e=r.length;for(i=new Array(e),o=new Array(e),n=0;n<e;++n)o[n]=+t(r[n],n,r),i[n]=isNaN(o[n])?0:+a(r[n],n,r)}}return"function"!=typeof t&&(t=qc(+t)),null==n&&(n=0),null==e&&(e=0),u.initialize=function(t){r=t,c()},u.strength=function(t){return arguments.length?(a="function"==typeof t?t:qc(+t),c(),u):a},u.radius=function(n){return arguments.length?(t="function"==typeof n?n:qc(+n),c(),u):t},u.x=function(t){return arguments.length?(n=+t,u):n},u.y=function(t){return arguments.length?(e=+t,u):e},u},t.forceSimulation=function(t){var n,e=1,r=.001,i=1-Math.pow(r,1/300),o=0,a=.6,u=new Map,c=Ni(l),f=$t("tick","end"),s=function(){let t=1;return()=>(t=(Lc*t+jc)%Hc)/Hc}();function l(){h(),f.call("tick",n),e<r&&(c.stop(),f.call("end",n))}function h(r){var c,f,s=t.length;void 0===r&&(r=1);for(var l=0;l<r;++l)for(e+=(o-e)*i,u.forEach((function(t){t(e)})),c=0;c<s;++c)null==(f=t[c]).fx?f.x+=f.vx*=a:(f.x=f.fx,f.vx=0),null==f.fy?f.y+=f.vy*=a:(f.y=f.fy,f.vy=0);return n}function d(){for(var n,e=0,r=t.length;e<r;++e){if((n=t[e]).index=e,null!=n.fx&&(n.x=n.fx),null!=n.fy&&(n.y=n.fy),isNaN(n.x)||isNaN(n.y)){var i=10*Math.sqrt(.5+e),o=e*Vc;n.x=i*Math.cos(o),n.y=i*Math.sin(o)}(isNaN(n.vx)||isNaN(n.vy))&&(n.vx=n.vy=0)}}function p(n){return n.initialize&&n.initialize(t,s),n}return null==t&&(t=[]),d(),n={tick:h,restart:function(){return c.restart(l),n},stop:function(){return c.stop(),n},nodes:function(e){return arguments.length?(t=e,d(),u.forEach(p),n):t},alpha:function(t){return arguments.length?(e=+t,n):e},alphaMin:function(t){return arguments.length?(r=+t,n):r},alphaDecay:function(t){return arguments.length?(i=+t,n):+i},alphaTarget:function(t){return arguments.length?(o=+t,n):o},velocityDecay:function(t){return arguments.length?(a=1-t,n):1-a},randomSource:function(t){return arguments.length?(s=t,u.forEach(p),n):s},force:function(t,e){return arguments.length>1?(null==e?u.delete(t):u.set(t,p(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f<s;++f)(a=(i=n-(u=t[f]).x)*i+(o=e-u.y)*o)<r&&(c=u,r=a);return c},on:function(t,e){return arguments.length>1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=qc(.1);function o(t){for(var i,o=0,a=n.length;o<a;++o)(i=n[o]).vx+=(r[o]-i.x)*e[o]*t}function a(){if(n){var o,a=n.length;for(e=new Array(a),r=new Array(a),o=0;o<a;++o)e[o]=isNaN(r[o]=+t(n[o],o,n))?0:+i(n[o],o,n)}}return"function"!=typeof t&&(t=qc(null==t?0:+t)),o.initialize=function(t){n=t,a()},o.strength=function(t){return arguments.length?(i="function"==typeof t?t:qc(+t),a(),o):i},o.x=function(n){return arguments.length?(t="function"==typeof n?n:qc(+n),a(),o):t},o},t.forceY=function(t){var n,e,r,i=qc(.1);function o(t){for(var i,o=0,a=n.length;o<a;++o)(i=n[o]).vy+=(r[o]-i.y)*e[o]*t}function a(){if(n){var o,a=n.length;for(e=new Array(a),r=new Array(a),o=0;o<a;++o)e[o]=isNaN(r[o]=+t(n[o],o,n))?0:+i(n[o],o,n)}}return"function"!=typeof t&&(t=qc(null==t?0:+t)),o.initialize=function(t){n=t,a()},o.strength=function(t){return arguments.length?(i="function"==typeof t?t:qc(+t),a(),o):i},o.y=function(n){return arguments.length?(t="function"==typeof n?n:qc(+n),a(),o):t},o},t.formatDefaultLocale=ff,t.formatLocale=cf,t.formatSpecifier=Jc,t.fsum=function(t,n){const e=new T;if(void 0===n)for(let n of t)(n=+n)&&e.add(n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&e.add(i)}return+e},t.geoAlbers=xd,t.geoAlbersUsa=function(){var t,n,e,r,i,o,a=xd(),u=md().rotate([154,0]).center([-2,58.5]).parallels([55,65]),c=md().rotate([157,0]).center([-3,19.9]).parallels([8,18]),f={point:function(t,n){o=[t,n]}};function s(t){var n=t[0],a=t[1];return o=null,e.point(n,a),o||(r.point(n,a),o)||(i.point(n,a),o)}function l(){return t=n=null,s}return s.invert=function(t){var n=a.scale(),e=a.translate(),r=(t[0]-e[0])/n,i=(t[1]-e[1])/n;return(i>=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++e<i;)r[e].point(t,n)},sphere:function(){for(var t=-1;++t<i;)r[t].sphere()},lineStart:function(){for(var t=-1;++t<i;)r[t].lineStart()},lineEnd:function(){for(var t=-1;++t<i;)r[t].lineEnd()},polygonStart:function(){for(var t=-1;++t<i;)r[t].polygonStart()},polygonEnd:function(){for(var t=-1;++t<i;)r[t].polygonEnd()}});var r,i},s.precision=function(t){return arguments.length?(a.precision(t),u.precision(t),c.precision(t),l()):a.precision()},s.scale=function(t){return arguments.length?(a.scale(t),u.scale(.35*t),c.scale(t),s.translate(a.translate())):a.scale()},s.translate=function(t){if(!arguments.length)return a.translate();var n=a.scale(),o=+t[0],s=+t[1];return e=a.translate(t).clipExtent([[o-.455*n,s-.238*n],[o+.455*n,s+.238*n]]).stream(f),r=u.translate([o-.307*n,s+.201*n]).clipExtent([[o-.425*n+df,s+.12*n+df],[o-.214*n-df,s+.234*n-df]]).stream(f),i=c.translate([o-.205*n,s+.212*n]).clipExtent([[o-.214*n+df,s+.166*n+df],[o-.115*n-df,s+.234*n-df]]).stream(f),l()},s.fitExtent=function(t,n){return ud(s,t,n)},s.fitSize=function(t,n){return cd(s,t,n)},s.fitWidth=function(t,n){return fd(s,t,n)},s.fitHeight=function(t,n){return sd(s,t,n)},s.scale(1070)},t.geoArea=function(t){return us=new T,Lf(t,cs),2*us},t.geoAzimuthalEqualArea=function(){return yd(Td).scale(124.75).clipAngle(179.999)},t.geoAzimuthalEqualAreaRaw=Td,t.geoAzimuthalEquidistant=function(){return yd(Ad).scale(79.4188).clipAngle(179.999)},t.geoAzimuthalEquidistantRaw=Ad,t.geoBounds=function(t){var n,e,r,i,o,a,u;if(Qf=Kf=-(Wf=Zf=1/0),is=[],Lf(t,Fs),e=is.length){for(is.sort(Hs),n=1,o=[r=is[0]];n<e;++n)Xs(r,(i=is[n])[0])||Xs(r,i[1])?(js(r[0],i[1])>js(r[0],r[1])&&(r[1]=i[1]),js(i[0],r[1])>js(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=js(r[1],i[0]))>a&&(a=u,Wf=i[0],Kf=r[1])}return is=os=null,Wf===1/0||Zf===1/0?[[NaN,NaN],[NaN,NaN]]:[[Wf,Zf],[Kf,Qf]]},t.geoCentroid=function(t){ms=xs=ws=Ms=Ts=As=Ss=Es=0,Ns=new T,ks=new T,Cs=new T,Lf(t,Gs);var n=+Ns,e=+ks,r=+Cs,i=Ef(n,e,r);return i<pf&&(n=As,e=Ss,r=Es,xs<df&&(n=ws,e=Ms,r=Ts),(i=Ef(n,e,r))<pf)?[NaN,NaN]:[Mf(e,n)*bf,Rf(r/i)*bf]},t.geoCircle=function(){var t,n,e=il([0,0]),r=il(90),i=il(2),o={point:function(e,r){t.push(e=n(e,r)),e[0]*=bf,e[1]*=bf}};function a(){var a=e.apply(this,arguments),u=r.apply(this,arguments)*mf,c=i.apply(this,arguments)*mf;return t=[],n=ul(-a[0]*mf,-a[1]*mf,0).invert,hl(o,u,c,1),a={type:"Polygon",coordinates:[t]},t=n=null,a}return a.center=function(t){return arguments.length?(e="function"==typeof t?t:il([+t[0],+t[1]]),a):e},a.radius=function(t){return arguments.length?(r="function"==typeof t?t:il(+t),a):r},a.precision=function(t){return arguments.length?(i="function"==typeof t?t:il(+t),a):i},a},t.geoClipAntimeridian=Tl,t.geoClipCircle=Al,t.geoClipExtent=function(){var t,n,e,r=0,i=0,o=960,a=500;return e={stream:function(e){return t&&n===e?t:t=zl(r,i,o,a)(n=e)},extent:function(u){return arguments.length?(r=+u[0][0],i=+u[0][1],o=+u[1][0],a=+u[1][1],t=n=null,e):[[r,i],[o,a]]}}},t.geoClipRectangle=zl,t.geoConicConformal=function(){return _d(kd).scale(109.5).parallels([30,30])},t.geoConicConformalRaw=kd,t.geoConicEqualArea=md,t.geoConicEqualAreaRaw=bd,t.geoConicEquidistant=function(){return _d(Pd).scale(131.154).center([0,13.9389])},t.geoConicEquidistantRaw=Pd,t.geoContains=function(t,n){return(t&&Bl.hasOwnProperty(t.type)?Bl[t.type]:Ll)(t,n)},t.geoDistance=Ol,t.geoEqualEarth=function(){return yd(qd).scale(177.158)},t.geoEqualEarthRaw=qd,t.geoEquirectangular=function(){return yd(Cd).scale(152.63)},t.geoEquirectangularRaw=Cd,t.geoGnomonic=function(){return yd(Ud).scale(144.049).clipAngle(60)},t.geoGnomonicRaw=Ud,t.geoGraticule=Kl,t.geoGraticule10=function(){return Kl()()},t.geoIdentity=function(){var t,n,e,r,i,o,a,u=1,c=0,f=0,s=1,l=1,h=0,d=null,p=1,g=1,y=id({point:function(t,n){var e=b([t,n]);this.stream.point(e[0],e[1])}}),v=eh;function _(){return p=u*s,g=u*l,o=a=null,b}function b(e){var r=e[0]*p,i=e[1]*g;if(h){var o=i*t-r*n;r=r*t+i*n,i=o}return[r+c,i+f]}return b.invert=function(e){var r=e[0]-c,i=e[1]-f;if(h){var o=i*t+r*n;r=r*t-i*n,i=o}return[r/p,i/g]},b.stream=function(t){return o&&a===t?o:o=y(v(a=t))},b.postclip=function(t){return arguments.length?(v=t,d=e=r=i=null,_()):v},b.clipExtent=function(t){return arguments.length?(v=null==t?(d=e=r=i=null,eh):zl(d=+t[0][0],e=+t[0][1],r=+t[1][0],i=+t[1][1]),_()):null==d?null:[[d,e],[r,i]]},b.scale=function(t){return arguments.length?(u=+t,_()):u},b.translate=function(t){return arguments.length?(c=+t[0],f=+t[1],_()):[c,f]},b.angle=function(e){return arguments.length?(n=Cf(h=e%360*mf),t=Tf(h),_()):h*bf},b.reflectX=function(t){return arguments.length?(s=t?-1:1,_()):s<0},b.reflectY=function(t){return arguments.length?(l=t?-1:1,_()):l<0},b.fitExtent=function(t,n){return ud(b,t,n)},b.fitSize=function(t,n){return cd(b,t,n)},b.fitWidth=function(t,n){return fd(b,t,n)},b.fitHeight=function(t,n){return sd(b,t,n)},b},t.geoInterpolate=function(t,n){var e=t[0]*mf,r=t[1]*mf,i=n[0]*mf,o=n[1]*mf,a=Tf(r),u=Cf(r),c=Tf(o),f=Cf(o),s=a*Tf(e),l=a*Cf(e),h=c*Tf(i),d=c*Cf(i),p=2*Rf(zf(Ff(o-r)+a*c*Ff(i-e))),g=Cf(p),y=p?function(t){var n=Cf(t*=p)/g,e=Cf(p-t)/g,r=e*s+n*h,i=e*l+n*d,o=e*u+n*f;return[Mf(i,r)*bf,Mf(o,zf(r*r+i*i))*bf]}:function(){return[e*bf,r*bf]};return y.distance=p,y},t.geoLength=ql,t.geoMercator=function(){return Ed(Sd).scale(961/_f)},t.geoMercatorRaw=Sd,t.geoNaturalEarth1=function(){return yd(Id).scale(175.295)},t.geoNaturalEarth1Raw=Id,t.geoOrthographic=function(){return yd(Od).scale(249.5).clipAngle(90+df)},t.geoOrthographicRaw=Od,t.geoPath=function(t,n){let e,r,i=3,o=4.5;function a(t){return t&&("function"==typeof o&&r.pointRadius(+o.apply(this,arguments)),Lf(t,e(r))),r.result()}return a.area=function(t){return Lf(t,e(sh)),sh.result()},a.measure=function(t){return Lf(t,e(Kh)),Kh.result()},a.bounds=function(t){return Lf(t,e(mh)),mh.result()},a.centroid=function(t){return Lf(t,e(Oh)),Oh.result()},a.projection=function(n){return arguments.length?(e=null==n?(t=null,eh):(t=n).stream,a):t},a.context=function(t){return arguments.length?(r=null==t?(n=null,new ed(i)):new Bh(n=t),"function"!=typeof o&&r.pointRadius(o),a):n},a.pointRadius=function(t){return arguments.length?(o="function"==typeof t?t:(r.pointRadius(+t),+t),a):o},a.digits=function(t){if(!arguments.length)return i;if(null==t)i=null;else{const n=Math.floor(t);if(!(n>=0))throw new RangeError(`invalid digits: ${t}`);i=n}return null===n&&(r=new ed(i)),a},a.projection(t).digits(i).context(n)},t.geoProjection=yd,t.geoProjectionMutator=vd,t.geoRotation=ll,t.geoStereographic=function(){return yd(Bd).scale(250).clipAngle(142)},t.geoStereographicRaw=Bd,t.geoStream=Lf,t.geoTransform=function(t){return{stream:id(t)}},t.geoTransverseMercator=function(){var t=Ed(Yd),n=t.center,e=t.rotate;return t.center=function(t){return arguments.length?n([-t[1],t[0]]):[(t=n())[1],-t[0]]},t.rotate=function(t){return arguments.length?e([t[0],t[1],t.length>2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=Yd,t.gray=function(t,n){return new ur(t,0,0,null==n?1:n)},t.greatest=ot,t.greatestIndex=function(t,e=n){if(1===e.length)return tt(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)>0)&&(r=n,i=o);return i},t.group=C,t.groupSort=function(t,e,r){return(2!==e.length?U($(t,e,r),(([t,e],[r,i])=>n(e,i)||n(t,r))):U(C(t,r),(([t,r],[i,o])=>e(r,o)||n(t,i)))).map((([t])=>t))},t.groups=P,t.hcl=dr,t.hierarchy=Gd,t.histogram=Q,t.hsl=He,t.html=Ec,t.image=function(t,n){return new Promise((function(e,r){var i=new Image;for(var o in n)i[o]=n[o];i.onerror=r,i.onload=function(){e(i)},i.src=t}))},t.index=function(t,...n){return F(t,k,R,n)},t.indexes=function(t,...n){return F(t,Array.from,R,n)},t.interpolate=Gr,t.interpolateArray=function(t,n){return(Ir(n)?Ur:Or)(t,n)},t.interpolateBasis=Er,t.interpolateBasisClosed=Nr,t.interpolateBlues=Gb,t.interpolateBrBG=ob,t.interpolateBuGn=Mb,t.interpolateBuPu=Ab,t.interpolateCividis=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},t.interpolateCool=am,t.interpolateCubehelix=li,t.interpolateCubehelixDefault=im,t.interpolateCubehelixLong=hi,t.interpolateDate=Br,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateGnBu=Eb,t.interpolateGreens=Wb,t.interpolateGreys=Kb,t.interpolateHcl=ci,t.interpolateHclLong=fi,t.interpolateHsl=oi,t.interpolateHslLong=ai,t.interpolateHue=function(t,n){var e=Pr(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateInferno=pm,t.interpolateLab=function(t,n){var e=$r((t=ar(t)).l,(n=ar(n)).l),r=$r(t.a,n.a),i=$r(t.b,n.b),o=$r(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateMagma=dm,t.interpolateNumber=Yr,t.interpolateNumberArray=Ur,t.interpolateObject=Lr,t.interpolateOrRd=kb,t.interpolateOranges=rm,t.interpolatePRGn=ub,t.interpolatePiYG=fb,t.interpolatePlasma=gm,t.interpolatePuBu=$b,t.interpolatePuBuGn=Pb,t.interpolatePuOr=lb,t.interpolatePuRd=Rb,t.interpolatePurples=Jb,t.interpolateRainbow=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return um.h=360*t-100,um.s=1.5-1.5*n,um.l=.8-.9*n,um+""},t.interpolateRdBu=db,t.interpolateRdGy=gb,t.interpolateRdPu=qb,t.interpolateRdYlBu=vb,t.interpolateRdYlGn=bb,t.interpolateReds=nm,t.interpolateRgb=Dr,t.interpolateRgbBasis=Fr,t.interpolateRgbBasisClosed=qr,t.interpolateRound=Vr,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,cm.r=255*(n=Math.sin(t))*n,cm.g=255*(n=Math.sin(t+fm))*n,cm.b=255*(n=Math.sin(t+sm))*n,cm+""},t.interpolateSpectral=xb,t.interpolateString=Xr,t.interpolateTransformCss=ti,t.interpolateTransformSvg=ni,t.interpolateTurbo=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"},t.interpolateViridis=hm,t.interpolateWarm=om,t.interpolateYlGn=Bb,t.interpolateYlGnBu=Ib,t.interpolateYlOrBr=Lb,t.interpolateYlOrRd=Hb,t.interpolateZoom=ri,t.interrupt=Gi,t.intersection=function(t,...n){t=new InternSet(t),n=n.map(vt);t:for(const e of t)for(const r of n)if(!r.has(e)){t.delete(e);continue t}return t},t.interval=function(t,n,e){var r=new Ei,i=n;return null==n?(r.restart(t,n,e),r):(r._restart=r.restart,r.restart=function(t,n,e){n=+n,e=null==e?Ai():+e,r._restart((function o(a){a+=i,r._restart(o,i+=n,e),t(a)}),n,e)},r.restart(t,n,e),r)},t.isoFormat=D_,t.isoParse=F_,t.json=function(t,n){return fetch(t,n).then(Tc)},t.lab=ar,t.lch=function(t,n,e,r){return 1===arguments.length?hr(t):new pr(e,n,t,null==r?1:r)},t.least=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)<0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)<0:0===e(n,n))&&(r=n,i=!0);return r},t.leastIndex=ht,t.line=Ym,t.lineRadial=Zm,t.link=ax,t.linkHorizontal=function(){return ax(nx)},t.linkRadial=function(){const t=ax(rx);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.linkVertical=function(){return ax(ex)},t.local=Qn,t.map=function(t,n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");if("function"!=typeof n)throw new TypeError("mapper is not a function");return Array.from(t,((e,r)=>n(e,r,t)))},t.matcher=Vt,t.max=J,t.maxIndex=tt,t.mean=function(t,n){let e=0,r=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(++e,r+=n);else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(o=+o)>=o&&(++e,r+=o)}if(e)return r/e},t.median=function(t,n){return at(t,.5,n)},t.medianIndex=function(t,n){return ct(t,.5,n)},t.merge=ft,t.min=nt,t.minIndex=et,t.mode=function(t,n){const e=new InternMap;if(void 0===n)for(let n of t)null!=n&&n>=n&&e.set(n,(e.get(n)||0)+1);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&i>=i&&e.set(i,(e.get(i)||0)+1)}let r,i=0;for(const[t,n]of e)n>i&&(i=n,r=t);return r},t.namespace=It,t.namespaces=Ut,t.nice=Z,t.now=Ai,t.pack=function(){var t=null,n=1,e=1,r=np;function i(i){const o=ap();return i.x=n/2,i.y=e/2,t?i.eachBefore(xp(t)).eachAfter(wp(r,.5,o)).eachBefore(Mp(1)):i.eachBefore(xp(mp)).eachAfter(wp(np,1,o)).eachAfter(wp(r,i.r/Math.min(n,e),o)).eachBefore(Mp(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=Jd(n),i):t},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:ep(+t),i):r},i},t.packEnclose=function(t){return up(t,ap())},t.packSiblings=function(t){return bp(t,ap()),t},t.pairs=function(t,n=st){const e=[];let r,i=!1;for(const o of t)i&&e.push(n(r,o)),r=o,i=!0;return e},t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&Ap(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a<i&&(i=a=(i+a)/2),u<o&&(o=u=(o+u)/2),r.x0=i,r.y0=o,r.x1=a,r.y1=u}}(n,o)),r&&i.eachBefore(Tp),i}return i.round=function(t){return arguments.length?(r=!!t,i):r},i.size=function(e){return arguments.length?(t=+e[0],n=+e[1],i):[t,n]},i.padding=function(t){return arguments.length?(e=+t,i):e},i},t.path=Ia,t.pathRound=function(t=3){return new Ua(+t)},t.permute=q,t.pie=function(){var t=Hm,n=jm,e=null,r=ym(0),i=ym(Em),o=ym(0);function a(a){var u,c,f,s,l,h=(a=qm(a)).length,d=0,p=new Array(h),g=new Array(h),y=+r.apply(this,arguments),v=Math.min(Em,Math.max(-Em,i.apply(this,arguments)-y)),_=Math.min(Math.abs(v)/h,o.apply(this,arguments)),b=_*(v<0?-1:1);for(u=0;u<h;++u)(l=g[p[u]=u]=+t(a[u],u,a))>0&&(d+=l);for(null!=n?p.sort((function(t,e){return n(g[t],g[e])})):null!=e&&p.sort((function(t,n){return e(a[t],a[n])})),u=0,f=d?(v-h*b)/d:0;u<h;++u,y=s)c=p[u],s=y+((l=g[c])>0?l*f:0)+b,g[c]={data:a[c],index:u,value:l,startAngle:y,endAngle:s,padAngle:_};return g}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:ym(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:ym(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:ym(+t),a):o},a},t.piecewise=di,t.pointRadial=Qm,t.pointer=ne,t.pointers=function(t,n){return t.target&&(t=te(t),void 0===n&&(n=t.currentTarget),t=t.touches||[t]),Array.from(t,(t=>ne(t,n)))},t.polygonArea=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++e<r;)n=i,i=t[e],o+=n[1]*i[0]-n[0]*i[1];return o/2},t.polygonCentroid=function(t){for(var n,e,r=-1,i=t.length,o=0,a=0,u=t[i-1],c=0;++r<i;)n=u,u=t[r],c+=e=n[0]*u[1]-u[0]*n[1],o+=(n[0]+u[0])*e,a+=(n[1]+u[1])*e;return[o/(c*=3),a/c]},t.polygonContains=function(t,n){for(var e,r,i=t.length,o=t[i-1],a=n[0],u=n[1],c=o[0],f=o[1],s=!1,l=0;l<i;++l)e=(o=t[l])[0],(r=o[1])>u!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonHull=function(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n<e;++n)r[n]=[+t[n][0],+t[n][1],n];for(r.sort(Hp),n=0;n<e;++n)i[n]=[r[n][0],-r[n][1]];var o=Xp(r),a=Xp(i),u=a[0]===o[0],c=a[a.length-1]===o[o.length-1],f=[];for(n=o.length-1;n>=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;n<a.length-c;++n)f.push(t[r[a[n]][2]]);return f},t.polygonLength=function(t){for(var n,e,r=-1,i=t.length,o=t[i-1],a=o[0],u=o[1],c=0;++r<i;)n=a,e=u,n-=a=(o=t[r])[0],e-=u=o[1],c+=Math.hypot(n,e);return c},t.precisionFixed=sf,t.precisionPrefix=lf,t.precisionRound=hf,t.quadtree=$c,t.quantile=at,t.quantileIndex=ct,t.quantileSorted=ut,t.quantize=function(t,n){for(var e=new Array(n),r=0;r<n;++r)e[r]=t(r/(n-1));return e},t.quickselect=rt,t.radialArea=Km,t.radialLine=Zm,t.randomBates=Jp,t.randomBernoulli=eg,t.randomBeta=og,t.randomBinomial=ag,t.randomCauchy=cg,t.randomExponential=tg,t.randomGamma=ig,t.randomGeometric=rg,t.randomInt=Wp,t.randomIrwinHall=Qp,t.randomLcg=function(t=Math.random()){let n=0|(0<=t&&t<1?t/lg:Math.abs(t));return()=>(n=1664525*n+1013904223|0,lg*(n>>>0))},t.randomLogNormal=Kp,t.randomLogistic=fg,t.randomNormal=Zp,t.randomPareto=ng,t.randomPoisson=sg,t.randomUniform=Vp,t.randomWeibull=ug,t.range=lt,t.rank=function(t,e=n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");let r=Array.from(t);const i=new Float64Array(r.length);2!==e.length&&(r=r.map(e),e=n);const o=(t,n)=>e(r[t],r[n]);let a,u;return(t=Uint32Array.from(r,((t,n)=>n))).sort(e===n?(t,n)=>O(r[t],r[n]):I(o)),t.forEach(((t,n)=>{const e=o(t,void 0===a?t:a);e>=0?((void 0===a||e>0)&&(a=t,u=n),i[t]=u):i[t]=NaN})),i},t.reduce=function(t,n,e){if("function"!=typeof n)throw new TypeError("reducer is not a function");const r=t[Symbol.iterator]();let i,o,a=-1;if(arguments.length<3){if(({done:i,value:e}=r.next()),i)return;++a}for(;({done:i,value:o}=r.next()),!i;)e=n(e,o,++a,t);return e},t.reverse=function(t){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");return Array.from(t).reverse()},t.rgb=Fe,t.ribbon=function(){return Wa()},t.ribbonArrow=function(){return Wa(Va)},t.rollup=$,t.rollups=D,t.scaleBand=yg,t.scaleDiverging=function t(){var n=Ng(L_()(mg));return n.copy=function(){return B_(n,t())},dg.apply(n,arguments)},t.scaleDivergingLog=function t(){var n=Fg(L_()).domain([.1,1,10]);return n.copy=function(){return B_(n,t()).base(n.base())},dg.apply(n,arguments)},t.scaleDivergingPow=j_,t.scaleDivergingSqrt=function(){return j_.apply(null,arguments).exponent(.5)},t.scaleDivergingSymlog=function t(){var n=Ig(L_());return n.copy=function(){return B_(n,t()).constant(n.constant())},dg.apply(n,arguments)},t.scaleIdentity=function t(n){var e;function r(t){return null==t||isNaN(t=+t)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,_g),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,_g):[0,1],Ng(r)},t.scaleImplicit=pg,t.scaleLinear=function t(){var n=Sg();return n.copy=function(){return Tg(n,t())},hg.apply(n,arguments),Ng(n)},t.scaleLog=function t(){const n=Fg(Ag()).domain([1,10]);return n.copy=()=>Tg(n,t()).base(n.base()),hg.apply(n,arguments),n},t.scaleOrdinal=gg,t.scalePoint=function(){return vg(yg.apply(null,arguments).paddingInner(1))},t.scalePow=jg,t.scaleQuantile=function t(){var e,r=[],i=[],o=[];function a(){var t=0,n=Math.max(1,i.length);for(o=new Array(n-1);++t<n;)o[t-1]=ut(r,t/n);return u}function u(t){return null==t||isNaN(t=+t)?e:i[s(o,t)]}return u.invertExtent=function(t){var n=i.indexOf(t);return n<0?[NaN,NaN]:[n>0?o[n-1]:r[0],n<o.length?o[n]:r[r.length-1]]},u.domain=function(t){if(!arguments.length)return r.slice();r=[];for(let n of t)null==n||isNaN(n=+n)||r.push(n);return r.sort(n),a()},u.range=function(t){return arguments.length?(i=Array.from(t),a()):i.slice()},u.unknown=function(t){return arguments.length?(e=t,u):e},u.quantiles=function(){return o.slice()},u.copy=function(){return t().domain(r).range(i).unknown(e)},hg.apply(u,arguments)},t.scaleQuantize=function t(){var n,e=0,r=1,i=1,o=[.5],a=[0,1];function u(t){return null!=t&&t<=t?a[s(o,t,0,i)]:n}function c(){var t=-1;for(o=new Array(i);++t<i;)o[t]=((t+1)*r-(t-i)*e)/(i+1);return u}return u.domain=function(t){return arguments.length?([e,r]=t,e=+e,r=+r,c()):[e,r]},u.range=function(t){return arguments.length?(i=(a=Array.from(t)).length-1,c()):a.slice()},u.invertExtent=function(t){var n=a.indexOf(t);return n<0?[NaN,NaN]:n<1?[e,o[0]]:n>=i?[o[i-1],r]:[o[n-1],o[n]]},u.unknown=function(t){return arguments.length?(n=t,u):u},u.thresholds=function(){return o.slice()},u.copy=function(){return t().domain([e,r]).range(a).unknown(n)},hg.apply(Ng(u),arguments)},t.scaleRadial=function t(){var n,e=Sg(),r=[0,1],i=!1;function o(t){var r=function(t){return Math.sign(t)*Math.sqrt(Math.abs(t))}(e(t));return isNaN(r)?n:i?Math.round(r):r}return o.invert=function(t){return e.invert(Hg(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,_g)).map(Hg)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},hg.apply(o,arguments),Ng(o)},t.scaleSequential=function t(){var n=Ng(O_()(mg));return n.copy=function(){return B_(n,t())},dg.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Fg(O_()).domain([1,10]);return n.copy=function(){return B_(n,t()).base(n.base())},dg.apply(n,arguments)},t.scaleSequentialPow=Y_,t.scaleSequentialQuantile=function t(){var e=[],r=mg;function i(t){if(null!=t&&!isNaN(t=+t))return r((s(e,t,1)-1)/(e.length-1))}return i.domain=function(t){if(!arguments.length)return e.slice();e=[];for(let n of t)null==n||isNaN(n=+n)||e.push(n);return e.sort(n),i},i.interpolator=function(t){return arguments.length?(r=t,i):r},i.range=function(){return e.map(((t,n)=>r(n/(e.length-1))))},i.quantiles=function(t){return Array.from({length:t+1},((n,r)=>at(e,r/t)))},i.copy=function(){return t(r).domain(e)},dg.apply(i,arguments)},t.scaleSequentialSqrt=function(){return Y_.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Ig(O_());return n.copy=function(){return B_(n,t()).constant(n.constant())},dg.apply(n,arguments)},t.scaleSqrt=function(){return jg.apply(null,arguments).exponent(.5)},t.scaleSymlog=function t(){var n=Ig(Ag());return n.copy=function(){return Tg(n,t()).constant(n.constant())},hg.apply(n,arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],i=1;function o(t){return null!=t&&t<=t?r[s(e,t,0,i)]:n}return o.domain=function(t){return arguments.length?(e=Array.from(t),i=Math.min(e.length,r.length-1),o):e.slice()},o.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),o):r.slice()},o.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t().domain(e).range(r).unknown(n)},hg.apply(o,arguments)},t.scaleTime=function(){return hg.apply(I_(uv,cv,tv,Zy,xy,py,sy,ay,iy,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return hg.apply(I_(ov,av,ev,Qy,Fy,yy,hy,cy,iy,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scan=function(t,n){const e=ht(t,n);return e<0?void 0:e},t.schemeAccent=G_,t.schemeBlues=Xb,t.schemeBrBG=ib,t.schemeBuGn=wb,t.schemeBuPu=Tb,t.schemeCategory10=X_,t.schemeDark2=V_,t.schemeGnBu=Sb,t.schemeGreens=Vb,t.schemeGreys=Zb,t.schemeObservable10=W_,t.schemeOrRd=Nb,t.schemeOranges=em,t.schemePRGn=ab,t.schemePaired=Z_,t.schemePastel1=K_,t.schemePastel2=Q_,t.schemePiYG=cb,t.schemePuBu=zb,t.schemePuBuGn=Cb,t.schemePuOr=sb,t.schemePuRd=Db,t.schemePurples=Qb,t.schemeRdBu=hb,t.schemeRdGy=pb,t.schemeRdPu=Fb,t.schemeRdYlBu=yb,t.schemeRdYlGn=_b,t.schemeReds=tm,t.schemeSet1=J_,t.schemeSet2=tb,t.schemeSet3=nb,t.schemeSpectral=mb,t.schemeTableau10=eb,t.schemeYlGn=Ob,t.schemeYlGnBu=Ub,t.schemeYlOrBr=Yb,t.schemeYlOrRd=jb,t.select=Zn,t.selectAll=function(t){return"string"==typeof t?new Vn([document.querySelectorAll(t)],[document.documentElement]):new Vn([Ht(t)],Gn)},t.selection=Wn,t.selector=jt,t.selectorAll=Gt,t.shuffle=dt,t.shuffler=pt,t.some=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(n(r,++e,t))return!0;return!1},t.sort=U,t.stack=function(){var t=ym([]),n=dw,e=hw,r=pw;function i(i){var o,a,u=Array.from(t.apply(this,arguments),gw),c=u.length,f=-1;for(const t of i)for(o=0,++f;o<c;++o)(u[o][f]=[0,+r(t,u[o].key,f,i)]).data=t;for(o=0,a=qm(n(u));o<c;++o)u[a[o]].index=o;return e(u,a),u}return i.keys=function(n){return arguments.length?(t="function"==typeof n?n:ym(Array.from(n)),i):t},i.value=function(t){return arguments.length?(r="function"==typeof t?t:ym(+t),i):r},i.order=function(t){return arguments.length?(n=null==t?dw:"function"==typeof t?t:ym(Array.from(t)),i):n},i.offset=function(t){return arguments.length?(e=null==t?hw:t,i):e},i},t.stackOffsetDiverging=function(t,n){if((u=t.length)>0)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c<f;++c)for(o=a=0,e=0;e<u;++e)(i=(r=t[n[e]][c])[1]-r[0])>0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):(r[0]=0,r[1]=i)},t.stackOffsetExpand=function(t,n){if((r=t.length)>0){for(var e,r,i,o=0,a=t[0].length;o<a;++o){for(i=e=0;e<r;++e)i+=t[e][o][1]||0;if(i)for(e=0;e<r;++e)t[e][o][1]/=i}hw(t,n)}},t.stackOffsetNone=hw,t.stackOffsetSilhouette=function(t,n){if((e=t.length)>0){for(var e,r=0,i=t[n[0]],o=i.length;r<o;++r){for(var a=0,u=0;a<e;++a)u+=t[a][r][1]||0;i[r][1]+=i[r][0]=-u/2}hw(t,n)}},t.stackOffsetWiggle=function(t,n){if((i=t.length)>0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;a<r;++a){for(var u=0,c=0,f=0;u<i;++u){for(var s=t[n[u]],l=s[a][1]||0,h=(l-(s[a-1][1]||0))/2,d=0;d<u;++d){var p=t[n[d]];h+=(p[a][1]||0)-(p[a-1][1]||0)}c+=l,f+=h*l}e[a-1][1]+=e[a-1][0]=o,c&&(o-=f/c)}e[a-1][1]+=e[a-1][0]=o,hw(t,n)}},t.stackOrderAppearance=yw,t.stackOrderAscending=_w,t.stackOrderDescending=function(t){return _w(t).reverse()},t.stackOrderInsideOut=function(t){var n,e,r=t.length,i=t.map(bw),o=yw(t),a=0,u=0,c=[],f=[];for(n=0;n<r;++n)e=o[n],a<u?(a+=i[e],c.push(e)):(u+=i[e],f.push(e));return f.reverse().concat(c)},t.stackOrderNone=dw,t.stackOrderReverse=function(t){return dw(t).reverse()},t.stratify=function(){var t,n=kp,e=Cp;function r(r){var i,o,a,u,c,f,s,l,h=Array.from(r),d=n,p=e,g=new Map;if(null!=t){const n=h.map(((n,e)=>function(t){t=`${t}`;let n=t.length;zp(t,n-1)&&!zp(t,n-2)&&(t=t.slice(0,-1));return"/"===t[0]?t:`/${t}`}(t(n,e,r)))),e=n.map(Pp),i=new Set(n).add("");for(const t of e)i.has(t)||(i.add(t),n.push(t),e.push(Pp(t)),h.push(Np));d=(t,e)=>n[e],p=(t,n)=>e[n]}for(a=0,i=h.length;a<i;++a)o=h[a],f=h[a]=new Qd(o),null!=(s=d(o,a,r))&&(s+="")&&(l=f.id=s,g.set(l,g.has(l)?Ep:f)),null!=(s=p(o,a,r))&&(s+="")&&(f.parent=s);for(a=0;a<i;++a)if(s=(f=h[a]).parent){if(!(c=g.get(s)))throw new Error("missing: "+s);if(c===Ep)throw new Error("ambiguous: "+s);c.children?c.children.push(f):c.children=[f],f.parent=c}else{if(u)throw new Error("multiple roots");u=f}if(!u)throw new Error("no root");if(null!=t){for(;u.data===Np&&1===u.children.length;)u=u.children[0],--i;for(let t=h.length-1;t>=0&&(f=h[t]).data===Np;--t)f.data=null}if(u.parent=Sp,u.eachBefore((function(t){t.depth=t.parent.depth+1,--i})).eachBefore(Kd),u.parent=null,i>0)throw new Error("cycle");return u}return r.id=function(t){return arguments.length?(n=Jd(t),r):n},r.parentId=function(t){return arguments.length?(e=Jd(t),r):e},r.path=function(n){return arguments.length?(t=Jd(n),r):t},r},t.style=_n,t.subset=function(t,n){return _t(n,t)},t.sum=function(t,n){let e=0;if(void 0===n)for(let n of t)(n=+n)&&(e+=n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&(e+=i)}return e},t.superset=_t,t.svg=Nc,t.symbol=function(t,n){let e=null,r=km(i);function i(){let i;if(e||(e=i=r()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),i)return e=null,i+""||null}return t="function"==typeof t?t:ym(t||fx),n="function"==typeof n?n:ym(void 0===n?64:+n),i.type=function(n){return arguments.length?(t="function"==typeof n?n:ym(n),i):t},i.size=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),i):n},i.context=function(t){return arguments.length?(e=null==t?null:t,i):e},i},t.symbolAsterisk=cx,t.symbolCircle=fx,t.symbolCross=sx,t.symbolDiamond=dx,t.symbolDiamond2=px,t.symbolPlus=gx,t.symbolSquare=yx,t.symbolSquare2=vx,t.symbolStar=xx,t.symbolTimes=Px,t.symbolTriangle=Mx,t.symbolTriangle2=Ax,t.symbolWye=Cx,t.symbolX=Px,t.symbols=zx,t.symbolsFill=zx,t.symbolsStroke=$x,t.text=mc,t.thresholdFreedmanDiaconis=function(t,n,e){const r=v(t),i=at(t,.75)-at(t,.25);return r&&i?Math.ceil((e-n)/(2*i*Math.pow(r,-1/3))):1},t.thresholdScott=function(t,n,e){const r=v(t),i=w(t);return r&&i?Math.ceil((e-n)*Math.cbrt(r)/(3.49*i)):1},t.thresholdSturges=K,t.tickFormat=Eg,t.tickIncrement=V,t.tickStep=W,t.ticks=G,t.timeDay=py,t.timeDays=gy,t.timeFormatDefaultLocale=P_,t.timeFormatLocale=hv,t.timeFriday=Sy,t.timeFridays=$y,t.timeHour=sy,t.timeHours=ly,t.timeInterval=Vg,t.timeMillisecond=Wg,t.timeMilliseconds=Zg,t.timeMinute=ay,t.timeMinutes=uy,t.timeMonday=wy,t.timeMondays=ky,t.timeMonth=Zy,t.timeMonths=Ky,t.timeSaturday=Ey,t.timeSaturdays=Dy,t.timeSecond=iy,t.timeSeconds=oy,t.timeSunday=xy,t.timeSundays=Ny,t.timeThursday=Ay,t.timeThursdays=zy,t.timeTickInterval=cv,t.timeTicks=uv,t.timeTuesday=My,t.timeTuesdays=Cy,t.timeWednesday=Ty,t.timeWednesdays=Py,t.timeWeek=xy,t.timeWeeks=Ny,t.timeYear=tv,t.timeYears=nv,t.timeout=$i,t.timer=Ni,t.timerFlush=ki,t.transition=go,t.transpose=gt,t.tree=function(){var t=$p,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Up(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Up(r[i],i)),e.parent=n;return(a.parent=new Up(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore((function(t){t.x<f.x&&(f=t),t.x>s.x&&(s=t),t.depth>l.depth&&(l=t)}));var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),g=e/(l.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Rp(u),o=Dp(o),u&&o;)c=Dp(c),(a=Rp(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Fp(qp(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Rp(a)&&(a.t=u,a.m+=l-s),o&&!Dp(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Yp,n=!1,e=1,r=1,i=[0],o=np,a=np,u=np,c=np,f=np;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(Tp),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l<r&&(r=l=(r+l)/2),h<s&&(s=h=(s+h)/2),n.x0=r,n.y0=s,n.x1=l,n.y1=h,n.children&&(e=i[n.depth+1]=o(n)/2,r+=f(n)-e,s+=a(n)-e,(l-=u(n)-e)<r&&(r=l=(r+l)/2),(h-=c(n)-e)<s&&(s=h=(s+h)/2),t(n,r,s,l,h))}return s.round=function(t){return arguments.length?(n=!!t,s):n},s.size=function(t){return arguments.length?(e=+t[0],r=+t[1],s):[e,r]},s.tile=function(n){return arguments.length?(t=tp(n),s):t},s.padding=function(t){return arguments.length?s.paddingInner(t).paddingOuter(t):s.paddingInner()},s.paddingInner=function(t){return arguments.length?(o="function"==typeof t?t:ep(+t),s):o},s.paddingOuter=function(t){return arguments.length?s.paddingTop(t).paddingRight(t).paddingBottom(t).paddingLeft(t):s.paddingTop()},s.paddingTop=function(t){return arguments.length?(a="function"==typeof t?t:ep(+t),s):a},s.paddingRight=function(t){return arguments.length?(u="function"==typeof t?t:ep(+t),s):u},s.paddingBottom=function(t){return arguments.length?(c="function"==typeof t?t:ep(+t),s):c},s.paddingLeft=function(t){return arguments.length?(f="function"==typeof t?t:ep(+t),s):f},s},t.treemapBinary=function(t,n,e,r,i){var o,a,u=t.children,c=u.length,f=new Array(c+1);for(f[0]=a=o=0;o<c;++o)f[o+1]=a+=u[o].value;!function t(n,e,r,i,o,a,c){if(n>=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}var l=f[n],h=r/2+l,d=n+1,p=e-1;for(;d<p;){var g=d+p>>>1;f[g]<h?d=g+1:p=g}h-f[d-1]<f[d]-h&&n+1<d&&--d;var y=f[d]-l,v=r-y;if(a-i>c-o){var _=r?(i*v+a*y)/r:a;t(n,d,y,i,o,_,c),t(d,e,v,_,o,a,c)}else{var b=r?(o*v+c*y)/r:c;t(n,d,y,i,o,a,b),t(d,e,v,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=Ap,t.treemapResquarify=Lp,t.treemapSlice=Ip,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Ip:Ap)(t,n,e,r,i)},t.treemapSquarify=Yp,t.tsv=Mc,t.tsvFormat=lc,t.tsvFormatBody=hc,t.tsvFormatRow=pc,t.tsvFormatRows=dc,t.tsvFormatValue=gc,t.tsvParse=fc,t.tsvParseRows=sc,t.union=function(...t){const n=new InternSet;for(const e of t)for(const t of e)n.add(t);return n},t.unixDay=_y,t.unixDays=by,t.utcDay=yy,t.utcDays=vy,t.utcFriday=By,t.utcFridays=Vy,t.utcHour=hy,t.utcHours=dy,t.utcMillisecond=Wg,t.utcMilliseconds=Zg,t.utcMinute=cy,t.utcMinutes=fy,t.utcMonday=qy,t.utcMondays=jy,t.utcMonth=Qy,t.utcMonths=Jy,t.utcSaturday=Yy,t.utcSaturdays=Wy,t.utcSecond=iy,t.utcSeconds=oy,t.utcSunday=Fy,t.utcSundays=Ly,t.utcThursday=Oy,t.utcThursdays=Gy,t.utcTickInterval=av,t.utcTicks=ov,t.utcTuesday=Uy,t.utcTuesdays=Hy,t.utcWednesday=Iy,t.utcWednesdays=Xy,t.utcWeek=Fy,t.utcWeeks=Ly,t.utcYear=ev,t.utcYears=rv,t.variance=x,t.version="7.9.0",t.window=pn,t.xml=Sc,t.zip=function(){return gt(arguments)},t.zoom=function(){var t,n,e,r=Ew,i=Nw,o=zw,a=Cw,u=Pw,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=ri,h=$t("start","zoom","end"),d=500,p=150,g=0,y=10;function v(t){t.property("__zoom",kw).on("wheel.zoom",T,{passive:!1}).on("mousedown.zoom",A).on("dblclick.zoom",S).filter(u).on("touchstart.zoom",E).on("touchmove.zoom",N).on("touchend.zoom touchcancel.zoom",k).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function _(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new ww(n,t.x,t.y)}function b(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new ww(t.k,r,i)}function m(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function x(t,n,e,r){t.on("start.zoom",(function(){w(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){w(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=w(t,o).event(r),u=i.apply(t,o),c=null==e?m(u):"function"==typeof e?e.apply(t,o):e,f=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),s=t.__zoom,h="function"==typeof n?n.apply(t,o):n,d=l(s.invert(c).concat(f/s.k),h.invert(c).concat(f/h.k));return function(t){if(1===t)t=h;else{var n=d(t),e=f/n[2];t=new ww(e,c[0]-n[0]*e,c[1]-n[1]*e)}a.zoom(null,t)}}))}function w(t,n,e){return!e&&t.__zooming||new M(t,n)}function M(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function T(t,...n){if(r.apply(this,arguments)){var e=w(this,n).event(t),i=this.__zoom,u=Math.max(c[0],Math.min(c[1],i.k*Math.pow(2,a.apply(this,arguments)))),s=ne(t);if(e.wheel)e.mouse[0][0]===s[0]&&e.mouse[0][1]===s[1]||(e.mouse[1]=i.invert(e.mouse[0]=s)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[s,i.invert(s)],Gi(this),e.start()}Sw(t),e.wheel=setTimeout((function(){e.wheel=null,e.end()}),p),e.zoom("mouse",o(b(_(i,u),e.mouse[0],e.mouse[1]),e.extent,f))}}function A(t,...n){if(!e&&r.apply(this,arguments)){var i=t.currentTarget,a=w(this,n,!0).event(t),u=Zn(t.view).on("mousemove.zoom",(function(t){if(Sw(t),!a.moved){var n=t.clientX-s,e=t.clientY-l;a.moved=n*n+e*e>g}a.event(t).zoom("mouse",o(b(a.that.__zoom,a.mouse[0]=ne(t,i),a.mouse[1]),a.extent,f))}),!0).on("mouseup.zoom",(function(t){u.on("mousemove.zoom mouseup.zoom",null),ue(t.view,a.moved),Sw(t),a.event(t).end()}),!0),c=ne(t,i),s=t.clientX,l=t.clientY;ae(t.view),Aw(t),a.mouse=[c,this.__zoom.invert(c)],Gi(this),a.start()}}function S(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=ne(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),c=e.k*(t.shiftKey?.5:2),l=o(b(_(e,c),a,u),i.apply(this,n),f);Sw(t),s>0?Zn(this).transition().duration(s).call(x,l,a,t):Zn(this).call(v.transform,l,a,t)}}function E(e,...i){if(r.apply(this,arguments)){var o,a,u,c,f=e.touches,s=f.length,l=w(this,i,e.changedTouches.length===s).event(e);for(Aw(e),a=0;a<s;++a)c=[c=ne(u=f[a],this),this.__zoom.invert(c),u.identifier],l.touch0?l.touch1||l.touch0[2]===c[2]||(l.touch1=c,l.taps=0):(l.touch0=c,o=!0,l.taps=1+!!t);t&&(t=clearTimeout(t)),o&&(l.taps<2&&(n=c[0],t=setTimeout((function(){t=null}),d)),Gi(this),l.start())}}function N(t,...n){if(this.__zooming){var e,r,i,a,u=w(this,n).event(t),c=t.changedTouches,s=c.length;for(Sw(t),e=0;e<s;++e)i=ne(r=c[e],this),u.touch0&&u.touch0[2]===r.identifier?u.touch0[0]=i:u.touch1&&u.touch1[2]===r.identifier&&(u.touch1[0]=i);if(r=u.that.__zoom,u.touch1){var l=u.touch0[0],h=u.touch0[1],d=u.touch1[0],p=u.touch1[1],g=(g=d[0]-l[0])*g+(g=d[1]-l[1])*g,y=(y=p[0]-h[0])*y+(y=p[1]-h[1])*y;r=_(r,Math.sqrt(g/y)),i=[(l[0]+d[0])/2,(l[1]+d[1])/2],a=[(h[0]+p[0])/2,(h[1]+p[1])/2]}else{if(!u.touch0)return;i=u.touch0[0],a=u.touch0[1]}u.zoom("touch",o(b(r,i,a),u.extent,f))}}function k(t,...r){if(this.__zooming){var i,o,a=w(this,r).event(t),u=t.changedTouches,c=u.length;for(Aw(t),e&&clearTimeout(e),e=setTimeout((function(){e=null}),d),i=0;i<c;++i)o=u[i],a.touch0&&a.touch0[2]===o.identifier?delete a.touch0:a.touch1&&a.touch1[2]===o.identifier&&delete a.touch1;if(a.touch1&&!a.touch0&&(a.touch0=a.touch1,delete a.touch1),a.touch0)a.touch0[1]=this.__zoom.invert(a.touch0[0]);else if(a.end(),2===a.taps&&(o=ne(o,this),Math.hypot(n[0]-o[0],n[1]-o[1])<y)){var f=Zn(this).on("dblclick.zoom");f&&f.apply(this,arguments)}}}return v.transform=function(t,n,e,r){var i=t.selection?t.selection():t;i.property("__zoom",kw),t!==i?x(t,n,e,r):i.interrupt().each((function(){w(this,arguments).event(r).start().zoom(null,"function"==typeof n?n.apply(this,arguments):n).end()}))},v.scaleBy=function(t,n,e,r){v.scaleTo(t,(function(){return this.__zoom.k*("function"==typeof n?n.apply(this,arguments):n)}),e,r)},v.scaleTo=function(t,n,e,r){v.transform(t,(function(){var t=i.apply(this,arguments),r=this.__zoom,a=null==e?m(t):"function"==typeof e?e.apply(this,arguments):e,u=r.invert(a),c="function"==typeof n?n.apply(this,arguments):n;return o(b(_(r,c),a,u),t,f)}),e,r)},v.translateBy=function(t,n,e,r){v.transform(t,(function(){return o(this.__zoom.translate("function"==typeof n?n.apply(this,arguments):n,"function"==typeof e?e.apply(this,arguments):e),i.apply(this,arguments),f)}),null,r)},v.translateTo=function(t,n,e,r,a){v.transform(t,(function(){var t=i.apply(this,arguments),a=this.__zoom,u=null==r?m(t):"function"==typeof r?r.apply(this,arguments):r;return o(Mw.translate(u[0],u[1]).scale(a.k).translate("function"==typeof n?-n.apply(this,arguments):-n,"function"==typeof e?-e.apply(this,arguments):-e),t,f)}),r,a)},M.prototype={event:function(t){return t&&(this.sourceEvent=t),this},start:function(){return 1==++this.active&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(t,n){return this.mouse&&"mouse"!==t&&(this.mouse[1]=n.invert(this.mouse[0])),this.touch0&&"touch"!==t&&(this.touch0[1]=n.invert(this.touch0[0])),this.touch1&&"touch"!==t&&(this.touch1[1]=n.invert(this.touch1[0])),this.that.__zoom=n,this.emit("zoom"),this},end:function(){return 0==--this.active&&(delete this.that.__zooming,this.emit("end")),this},emit:function(t){var n=Zn(this.that).datum();h.call(t,this.that,new xw(t,{sourceEvent:this.sourceEvent,target:v,type:t,transform:this.that.__zoom,dispatch:h}),n)}},v.wheelDelta=function(t){return arguments.length?(a="function"==typeof t?t:mw(+t),v):a},v.filter=function(t){return arguments.length?(r="function"==typeof t?t:mw(!!t),v):r},v.touchable=function(t){return arguments.length?(u="function"==typeof t?t:mw(!!t),v):u},v.extent=function(t){return arguments.length?(i="function"==typeof t?t:mw([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),v):i},v.scaleExtent=function(t){return arguments.length?(c[0]=+t[0],c[1]=+t[1],v):[c[0],c[1]]},v.translateExtent=function(t){return arguments.length?(f[0][0]=+t[0][0],f[1][0]=+t[1][0],f[0][1]=+t[0][1],f[1][1]=+t[1][1],v):[[f[0][0],f[0][1]],[f[1][0],f[1][1]]]},v.constrain=function(t){return arguments.length?(o=t,v):o},v.duration=function(t){return arguments.length?(s=+t,v):s},v.interpolate=function(t){return arguments.length?(l=t,v):l},v.on=function(){var t=h.on.apply(h,arguments);return t===h?v:t},v.clickDistance=function(t){return arguments.length?(g=(t=+t)*t,v):Math.sqrt(g)},v.tapDistance=function(t){return arguments.length?(y=+t,v):y},v},t.zoomIdentity=Mw,t.zoomTransform=Tw})); diff --git a/chapter25/docs/examples/A_helloWorld.smp b/chapter25/docs/examples/A_helloWorld.smp new file mode 100644 index 00000000..07ff935f --- /dev/null +++ b/chapter25/docs/examples/A_helloWorld.smp @@ -0,0 +1,3 @@ +// -*- mode: java; -*- +sys.io.p("Hello, World!"); +return 0; diff --git a/chapter25/docs/examples/BubbleSort.smp b/chapter25/docs/examples/BubbleSort.smp new file mode 100644 index 00000000..f2298c0c --- /dev/null +++ b/chapter25/docs/examples/BubbleSort.smp @@ -0,0 +1,35 @@ +// -*- mode: java; -*- + +// An implementation of +// https://sampleprograms.io/projects/bubble-sort/ +// +// Bubble sort, with reading (parsing) and writing (formatting) the data. + +val sort = { sys.aryi64 nums -> + var done = false; + while( !done ) { + done = true; + for( var i = 0; i < nums.len-1; i++ ) + if( nums.buf[i] > nums.buf[i+1] ) { + int tmp = nums.buf[i]; + nums.buf[i] = nums.buf[i+1]; + nums.buf[i+1] = tmp; // Swap + done = false; + } + } +}; + +// Read from stdin a line of the form "[ 4, 3, 2, 1]" and print it sorted. +val main = { -> + // Parse array of numbers from stdin + var nums = sys.aryi64.parseAryI64( sys.io.stdin() ); + if( !nums ) + return sys.io.p("stdin must look like [4, 3, 2, 1]"); + + // Bubble sort array of integers + sort(nums); + + // Print a list of numbers to stdout + nums.str().write(1); + return 0; +}; diff --git a/chapter25/docs/font-awesome-4.7.0.zip b/chapter25/docs/font-awesome-4.7.0.zip Binary files differnew file mode 100644 index 00000000..b6bdb4bd --- /dev/null +++ b/chapter25/docs/font-awesome-4.7.0.zip diff --git a/chapter25/docs/graphviz.umd.js b/chapter25/docs/graphviz.umd.js new file mode 100644 index 00000000..622b4d34 --- /dev/null +++ b/chapter25/docs/graphviz.umd.js @@ -0,0 +1,7 @@ +(function(g,f){if(typeof exports=="object"&&typeof module<"u"){module.exports=f()}else if("function"==typeof define && define.amd){define("@hpcc-js/wasm",f)}else {g["@hpcc-js/wasm"]=f()}}(typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : this,function(){var exports={};var __exports=exports;var module={exports}; +"use strict";var pr=Object.defineProperty;var so=Object.getOwnPropertyDescriptor;var uo=Object.getOwnPropertyNames;var po=Object.prototype.hasOwnProperty;var lo=(p,l)=>{for(var d in l)pr(p,d,{get:l[d],enumerable:!0})},co=(p,l,d,i)=>{if(l&&typeof l=="object"||typeof l=="function")for(let f of uo(l))!po.call(p,f)&&f!==d&&pr(p,f,{get:()=>l[f],enumerable:!(i=so(l,f))||i.enumerable});return p};var fo=p=>co(pr({},"__esModule",{value:!0}),p);var Yo={};lo(Yo,{Graphviz:()=>Zo});module.exports=fo(Yo);var vo=ArrayBuffer,H=Uint8Array,Qe=Uint16Array,mo=Int16Array,qe=Int32Array,lr=function(p,l,d){if(H.prototype.slice)return H.prototype.slice.call(p,l,d);(l==null||l<0)&&(l=0),(d==null||d>p.length)&&(d=p.length);var i=new H(d-l);return i.set(p.subarray(l,d)),i},Ye=function(p,l,d,i){if(H.prototype.fill)return H.prototype.fill.call(p,l,d,i);for((d==null||d<0)&&(d=0),(i==null||i>p.length)&&(i=p.length);d<i;++d)p[d]=l;return p},_o=function(p,l,d,i){if(H.prototype.copyWithin)return H.prototype.copyWithin.call(p,l,d,i);for((d==null||d<0)&&(d=0),(i==null||i>p.length)&&(i=p.length);d<i;)p[l++]=p[d++]},go=["invalid zstd data","window size too large (>2046MB)","invalid block type","FSE accuracy too high","match distance too far back","unexpected EOF"],T=function(p,l,d){var i=new Error(l||go[p]);if(i.code=p,Error.captureStackTrace&&Error.captureStackTrace(i,T),!d)throw i;return i},Pr=function(p,l,d){for(var i=0,f=0;i<d;++i)f|=p[l++]<<(i<<3);return f},ho=function(p,l){return(p[l]|p[l+1]<<8|p[l+2]<<16|p[l+3]<<24)>>>0},wo=function(p,l){var d=p[0]|p[1]<<8|p[2]<<16;if(d==3126568&&p[3]==253){var i=p[4],f=i>>5&1,m=i>>2&1,_=i&3,h=i>>6;i&8&&T(0);var M=6-f,I=_==3?4:_,Q=Pr(p,M,I);M+=I;var Z=h?1<<h:f,Y=Pr(p,M,Z)+(h==1&&256),z=Y;if(!f){var q=1<<10+(p[5]>>3);z=q+(q>>3)*(p[5]&7)}z>2145386496&&T(1);var X=new H((l==1?Y||z:l?0:z)+12);return X[0]=1,X[4]=4,X[8]=8,{b:M+Z,y:0,l:0,d:Q,w:l&&l!=1?l:X.subarray(12),e:z,o:new qe(X.buffer,0,3),u:Y,c:m,m:Math.min(131072,z)}}else if((d>>4|p[3]<<20)==25481893)return ho(p,4)+8;T(0)},Xe=function(p){for(var l=0;1<<l<=p;++l);return l-1},je=function(p,l,d){var i=(l<<3)+4,f=(p[l]&15)+5;f>d&&T(3);for(var m=1<<f,_=m,h=-1,M=-1,I=-1,Q=m,Z=new vo(512+(m<<2)),Y=new mo(Z,0,256),z=new Qe(Z,0,256),q=new Qe(Z,512,m),X=512+(m<<1),V=new H(Z,X,m),re=new H(Z,X+m);h<255&&_>0;){var S=Xe(_+1),P=i>>3,ae=(1<<S+1)-1,J=(p[P]|p[P+1]<<8|p[P+2]<<16)>>(i&7)&ae,w=(1<<S)-1,j=ae-_-1,W=J&w;if(W<j?(i+=S,J=W):(i+=S+1,J>w&&(J-=j)),Y[++h]=--J,J==-1?(_+=J,V[--Q]=h):_-=J,!J)do{var ie=i>>3;M=(p[ie]|p[ie+1]<<8)>>(i&7)&3,i+=2,h+=M}while(M==3)}(h>255||_)&&T(0);for(var b=0,k=(m>>1)+(m>>3)+3,se=m-1,oe=0;oe<=h;++oe){var K=Y[oe];if(K<1){z[oe]=-K;continue}for(I=0;I<K;++I){V[b]=oe;do b=b+k&se;while(b>=Q)}}for(b&&T(0),I=0;I<m;++I){var L=z[V[I]]++,A=re[I]=f-Xe(L);q[I]=(L<<A)-m}return[i+7>>3,{b:f,s:V,n:re,t:q}]},yo=function(p,l){var d=0,i=-1,f=new H(292),m=p[l],_=f.subarray(0,256),h=f.subarray(256,268),M=new Qe(f.buffer,268);if(m<128){var I=je(p,l+1,6),Q=I[0],Z=I[1];l+=m;var Y=Q<<3,z=p[l];z||T(0);for(var q=0,X=0,V=Z.b,re=V,S=(++l<<3)-8+Xe(z);S-=V,!(S<Y);){var P=S>>3;if(q+=(p[P]|p[P+1]<<8)>>(S&7)&(1<<V)-1,_[++i]=Z.s[q],S-=re,S<Y)break;P=S>>3,X+=(p[P]|p[P+1]<<8)>>(S&7)&(1<<re)-1,_[++i]=Z.s[X],V=Z.n[q],q=Z.t[q],re=Z.n[X],X=Z.t[X]}++i>255&&T(0)}else{for(i=m-127;d<i;d+=2){var ae=p[++l];_[d]=ae>>4,_[d+1]=ae&15}++l}var J=0;for(d=0;d<i;++d){var w=_[d];w>11&&T(0),J+=w&&1<<w-1}var j=Xe(J)+1,W=1<<j,ie=W-J;for(ie&ie-1&&T(0),_[i++]=Xe(ie)+1,d=0;d<i;++d){var w=_[d];++h[_[d]=w&&j+1-w]}var b=new H(W<<1),k=b.subarray(0,W),se=b.subarray(W);for(M[j]=0,d=j;d>0;--d){var oe=M[d];Ye(se,d,oe,M[d-1]=oe+h[d]*(1<<j-d))}for(M[0]!=W&&T(0),d=0;d<i;++d){var K=_[d];if(K){var L=M[K];Ye(k,d,L,M[K]=L+(1<<j-K))}}return[l,{n:se,b:j,s:k}]},bo=je(new H([81,16,99,140,49,198,24,99,12,33,196,24,99,102,102,134,70,146,4]),0,6)[1],Eo=je(new H([33,20,196,24,99,140,33,132,16,66,8,33,132,16,66,8,33,68,68,68,68,68,68,68,68,36,9]),0,6)[1],Mo=je(new H([32,132,16,66,102,70,68,68,68,68,36,73,2]),0,5)[1],Jr=function(p,l){for(var d=p.length,i=new qe(d),f=0;f<d;++f)i[f]=l,l+=1<<p[f];return i},dr=new H(new qe([0,0,0,0,16843009,50528770,134678020,202050057,269422093]).buffer,0,36),Do=Jr(dr,0),fr=new H(new qe([0,0,0,0,0,0,0,0,16843009,50528770,117769220,185207048,252579084,16]).buffer,0,53),Xo=Jr(fr,3),Ze=function(p,l,d){var i=p.length,f=l.length,m=p[i-1],_=(1<<d.b)-1,h=-d.b;m||T(0);for(var M=0,I=d.b,Q=(i<<3)-8+Xe(m)-I,Z=-1;Q>h&&Z<f;){var Y=Q>>3,z=(p[Y]|p[Y+1]<<8|p[Y+2]<<16)>>(Q&7);M=(M<<I|z)&_,l[++Z]=d.s[M],Q-=I=d.n[M]}(Q!=h||Z+1!=f)&&T(0)},Lo=function(p,l,d){var i=6,f=l.length,m=f+3>>2,_=m<<1,h=m+_;Ze(p.subarray(i,i+=p[0]|p[1]<<8),l.subarray(0,m),d),Ze(p.subarray(i,i+=p[2]|p[3]<<8),l.subarray(m,_),d),Ze(p.subarray(i,i+=p[4]|p[5]<<8),l.subarray(_,h),d),Ze(p.subarray(i),l.subarray(h),d)},Bo=function(p,l,d){var i,f=l.b,m=p[f],_=m>>1&3;l.l=m&1;var h=m>>3|p[f+1]<<5|p[f+2]<<13,M=(f+=3)+h;if(_==1)return f>=p.length?void 0:(l.b=f+1,d?(Ye(d,p[f],l.y,l.y+=h),d):Ye(new H(h),p[f]));if(!(M>p.length)){if(_==0)return l.b=M,d?(d.set(p.subarray(f,M),l.y),l.y+=h,d):lr(p,f,M);if(_==2){var I=p[f],Q=I&3,Z=I>>2&3,Y=I>>4,z=0,q=0;Q<2?Z&1?Y|=p[++f]<<4|(Z&2&&p[++f]<<12):Y=I>>3:(q=Z,Z<2?(Y|=(p[++f]&63)<<4,z=p[f]>>6|p[++f]<<2):Z==2?(Y|=p[++f]<<4|(p[++f]&3)<<12,z=p[f]>>2|p[++f]<<6):(Y|=p[++f]<<4|(p[++f]&63)<<12,z=p[f]>>6|p[++f]<<2|p[++f]<<10)),++f;var X=d?d.subarray(l.y,l.y+l.m):new H(l.m),V=X.length-Y;if(Q==0)X.set(p.subarray(f,f+=Y),V);else if(Q==1)Ye(X,p[f++],V);else{var re=l.h;if(Q==2){var S=yo(p,f);z+=f-(f=S[0]),l.h=re=S[1]}else re||T(0);(q?Lo:Ze)(p.subarray(f,f+=z),X.subarray(V),re)}var P=p[f++];if(P){P==255?P=(p[f++]|p[f++]<<8)+32512:P>127&&(P=P-128<<8|p[f++]);var ae=p[f++];ae&3&&T(0);for(var J=[Eo,Mo,bo],w=2;w>-1;--w){var j=ae>>(w<<1)+2&3;if(j==1){var W=new H([0,0,p[f++]]);J[w]={s:W.subarray(2,3),n:W.subarray(0,1),t:new Qe(W.buffer,0,1),b:0}}else j==2?(i=je(p,f,9-(w&1)),f=i[0],J[w]=i[1]):j==3&&(l.t||T(0),J[w]=l.t[w])}var ie=l.t=J,b=ie[0],k=ie[1],se=ie[2],oe=p[M-1];oe||T(0);var K=(M<<3)-8+Xe(oe)-se.b,L=K>>3,A=0,Le=(p[L]|p[L+1]<<8)>>(K&7)&(1<<se.b)-1;L=(K-=k.b)>>3;var Be=(p[L]|p[L+1]<<8)>>(K&7)&(1<<k.b)-1;L=(K-=b.b)>>3;var Re=(p[L]|p[L+1]<<8)>>(K&7)&(1<<b.b)-1;for(++P;--P;){var Ne=se.s[Le],Pe=se.n[Le],Ve=b.s[Re],ye=b.n[Re],ke=k.s[Be],be=k.n[Be];L=(K-=ke)>>3;var $e=1<<ke,ce=$e+((p[L]|p[L+1]<<8|p[L+2]<<16|p[L+3]<<24)>>>(K&7)&$e-1);L=(K-=fr[Ve])>>3;var me=Xo[Ve]+((p[L]|p[L+1]<<8|p[L+2]<<16)>>(K&7)&(1<<fr[Ve])-1);L=(K-=dr[Ne])>>3;var _e=Do[Ne]+((p[L]|p[L+1]<<8|p[L+2]<<16)>>(K&7)&(1<<dr[Ne])-1);if(L=(K-=Pe)>>3,Le=se.t[Le]+((p[L]|p[L+1]<<8)>>(K&7)&(1<<Pe)-1),L=(K-=ye)>>3,Re=b.t[Re]+((p[L]|p[L+1]<<8)>>(K&7)&(1<<ye)-1),L=(K-=be)>>3,Be=k.t[Be]+((p[L]|p[L+1]<<8)>>(K&7)&(1<<be)-1),ce>3)l.o[2]=l.o[1],l.o[1]=l.o[0],l.o[0]=ce-=3;else{var Ge=ce-(_e!=0);Ge?(ce=Ge==3?l.o[0]-1:l.o[Ge],Ge>1&&(l.o[2]=l.o[1]),l.o[1]=l.o[0],l.o[0]=ce):ce=l.o[0]}for(var w=0;w<_e;++w)X[A+w]=X[V+w];A+=_e,V+=_e;var Ee=A-ce;if(Ee<0){var De=-Ee,We=l.e+Ee;De>me&&(De=me);for(var w=0;w<De;++w)X[A+w]=l.w[We+w];A+=De,me-=De,Ee=0}for(var w=0;w<me;++w)X[A+w]=X[Ee+w];A+=me}if(A!=V)for(;V<X.length;)X[A++]=X[V++];else A=X.length;d?l.y+=A:X=lr(X,0,A)}else if(d){if(l.y+=Y,V)for(var w=0;w<Y;++w)X[w]=X[V+w]}else V&&(X=lr(X,V));return l.b=M,X}T(2)}},Ro=function(p,l){if(p.length==1)return p[0];for(var d=new H(l),i=0,f=0;i<p.length;++i){var m=p[i];d.set(m,f),f+=m.length}return d};function ko(p,l){for(var d=[],i=+!l,f=0,m=0;p.length;){var _=wo(p,i||l);if(typeof _=="object"){for(i?(l=null,_.w.length==_.u&&(d.push(l=_.w),m+=_.u)):(d.push(l),_.e=0);!_.l;){var h=Bo(p,_,l);h||T(5),l?_.e=_.y:(d.push(h),m+=h.length,_o(_.w,0,h.length),_.w.set(h,_.w.length-h.length))}f=_.b+_.c*4}else f=_;p=p.subarray(f)}return Ro(d,m)}var Go=(()=>{var p=typeof document<"u"?document.currentScript?.src:void 0;return function(l={}){var d,i=l,f,m,_=new Promise((e,r)=>{f=e,m=r}),h=!1,M=Object.assign({},i),I="./this.program",Q=(e,r)=>{throw r},Z="";function Y(e){return Z+e}var z,q,X=console.log.bind(console),V=console.error.bind(console);Object.assign(i,M),M=null;var re;i.wasmBinary&&(re=i.wasmBinary);var S,P=!1,ae;function J(e,r){e||_e(r)}var w,j,W,ie,b,k,se,oe;function K(){var e=S.buffer;i.HEAP8=w=new Int8Array(e),i.HEAP16=W=new Int16Array(e),i.HEAPU8=j=new Uint8Array(e),i.HEAPU16=ie=new Uint16Array(e),i.HEAP32=b=new Int32Array(e),i.HEAPU32=k=new Uint32Array(e),i.HEAPF32=se=new Float32Array(e),i.HEAPF64=oe=new Float64Array(e)}var L=[],A=[],Le=[],Be=!1;function Re(){Ae(L)}function Ne(){Be=!0,!i.noFSInit&&!o.init.initialized&&o.init(),o.ignorePermissions=!1,Me.init(),Ae(A)}function Pe(){Ae(Le)}function Ve(e){A.unshift(e)}var ye=0,ke=null,be=null;function $e(e){return e}function ce(e){ye++}function me(e){if(ye--,ye==0&&(ke!==null&&(clearInterval(ke),ke=null),be)){var r=be;be=null,r()}}function _e(e){e="Aborted("+e+")",V(e),P=!0,ae=1,e+=". Build with -sASSERTIONS for more info.";var r=new WebAssembly.RuntimeError(e);throw m(r),r}var Ge="data:application/octet-stream;base64,",Ee=e=>e.startsWith(Ge),De=e=>e.startsWith("file://");function We(){var e="graphvizlib.wasm";return Ee(e)?e:Y(e)}var Je;function vr(e){if(e==Je&&re)return new Uint8Array(re);if(q)return q(e);throw"both async and sync fetching of the wasm failed"}function Sr(e){return re?Promise.resolve().then(()=>vr(e)):z(e).then(r=>new Uint8Array(r),()=>vr(e))}function mr(e,r,t){return Sr(e).then(n=>WebAssembly.instantiate(n,r)).then(t,n=>{V(`failed to asynchronously prepare wasm: ${n}`),_e(n)})}function Hr(e,r,t,n){return!e&&typeof WebAssembly.instantiateStreaming=="function"&&!Ee(r)&&!De(r)&&typeof fetch=="function"?fetch(r,{credentials:"same-origin"}).then(a=>{var s=WebAssembly.instantiateStreaming(a,t);return s.then(n,function(u){return V(`wasm streaming compile failed: ${u}`),V("falling back to ArrayBuffer instantiation"),mr(r,t,n)})}):mr(r,t,n)}function Tr(){return{a:qt}}function Qr(){var e=Tr();function r(n,a){return N=n.exports,S=N.B,K(),Ve(N.C),me("wasm-instantiate"),N}ce("wasm-instantiate");function t(n){r(n.instance)}return Je||(Je=We()),Hr(re,Je,e,t).catch(m),{}}var R,$,qr={172808:(e,r)=>{var t=ue(e),n=ue(r);o.createPath("/",C.dirname(t)),o.writeFile(C.join("/",t),n)}};function $r(e){this.name="ExitStatus",this.message=`Program terminated with exit(${e})`,this.status=e}var Ae=e=>{for(;e.length>0;)e.shift()(i)};class Wr{constructor(r){this.excPtr=r,this.ptr=r-24}set_type(r){k[this.ptr+4>>2]=r}get_type(){return k[this.ptr+4>>2]}set_destructor(r){k[this.ptr+8>>2]=r}get_destructor(){return k[this.ptr+8>>2]}set_caught(r){r=r?1:0,w[this.ptr+12]=r}get_caught(){return w[this.ptr+12]!=0}set_rethrown(r){r=r?1:0,w[this.ptr+13]=r}get_rethrown(){return w[this.ptr+13]!=0}init(r,t){this.set_adjusted_ptr(0),this.set_type(r),this.set_destructor(t)}set_adjusted_ptr(r){k[this.ptr+16>>2]=r}get_adjusted_ptr(){return k[this.ptr+16>>2]}get_exception_ptr(){var r=zr(this.get_type());if(r)return k[this.excPtr>>2];var t=this.get_adjusted_ptr();return t!==0?t:this.excPtr}}var _r=0,Ar=0,et=(e,r,t)=>{var n=new Wr(e);throw n.init(r,t),_r=e,Ar++,_r},C={isAbs:e=>e.charAt(0)==="/",splitPath:e=>{var r=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return r.exec(e).slice(1)},normalizeArray:(e,r)=>{for(var t=0,n=e.length-1;n>=0;n--){var a=e[n];a==="."?e.splice(n,1):a===".."?(e.splice(n,1),t++):t&&(e.splice(n,1),t--)}if(r)for(;t;t--)e.unshift("..");return e},normalize:e=>{var r=C.isAbs(e),t=e.substr(-1)==="/";return e=C.normalizeArray(e.split("/").filter(n=>!!n),!r).join("/"),!e&&!r&&(e="."),e&&t&&(e+="/"),(r?"/":"")+e},dirname:e=>{var r=C.splitPath(e),t=r[0],n=r[1];return!t&&!n?".":(n&&(n=n.substr(0,n.length-1)),t+n)},basename:e=>{if(e==="/")return"/";e=C.normalize(e),e=e.replace(/\/$/,"");var r=e.lastIndexOf("/");return r===-1?e:e.substr(r+1)},join:(...e)=>C.normalize(e.join("/")),join2:(e,r)=>C.normalize(e+"/"+r)},rt=()=>{if(typeof crypto=="object"&&typeof crypto.getRandomValues=="function")return e=>crypto.getRandomValues(e);_e("initRandomDevice")},gr=e=>(gr=rt())(e),he={resolve:(...e)=>{for(var r="",t=!1,n=e.length-1;n>=-1&&!t;n--){var a=n>=0?e[n]:o.cwd();if(typeof a!="string")throw new TypeError("Arguments to path.resolve must be strings");if(!a)return"";r=a+"/"+r,t=C.isAbs(a)}return r=C.normalizeArray(r.split("/").filter(s=>!!s),!t).join("/"),(t?"/":"")+r||"."},relative:(e,r)=>{e=he.resolve(e).substr(1),r=he.resolve(r).substr(1);function t(E){for(var G=0;G<E.length&&E[G]==="";G++);for(var x=E.length-1;x>=0&&E[x]==="";x--);return G>x?[]:E.slice(G,x-G+1)}for(var n=t(e.split("/")),a=t(r.split("/")),s=Math.min(n.length,a.length),u=s,c=0;c<s;c++)if(n[c]!==a[c]){u=c;break}for(var v=[],c=u;c<n.length;c++)v.push("..");return v=v.concat(a.slice(u)),v.join("/")}},hr=typeof TextDecoder<"u"?new TextDecoder:void 0,Fe=(e,r,t)=>{for(var n=r+t,a=r;e[a]&&!(a>=n);)++a;if(a-r>16&&e.buffer&&hr)return hr.decode(e.subarray(r,a));for(var s="";r<a;){var u=e[r++];if(!(u&128)){s+=String.fromCharCode(u);continue}var c=e[r++]&63;if((u&224)==192){s+=String.fromCharCode((u&31)<<6|c);continue}var v=e[r++]&63;if((u&240)==224?u=(u&15)<<12|c<<6|v:u=(u&7)<<18|c<<12|v<<6|e[r++]&63,u<65536)s+=String.fromCharCode(u);else{var E=u-65536;s+=String.fromCharCode(55296|E>>10,56320|E&1023)}}return s},er=[],rr=e=>{for(var r=0,t=0;t<e.length;++t){var n=e.charCodeAt(t);n<=127?r++:n<=2047?r+=2:n>=55296&&n<=57343?(r+=4,++t):r+=3}return r},tr=(e,r,t,n)=>{if(!(n>0))return 0;for(var a=t,s=t+n-1,u=0;u<e.length;++u){var c=e.charCodeAt(u);if(c>=55296&&c<=57343){var v=e.charCodeAt(++u);c=65536+((c&1023)<<10)|v&1023}if(c<=127){if(t>=s)break;r[t++]=c}else if(c<=2047){if(t+1>=s)break;r[t++]=192|c>>6,r[t++]=128|c&63}else if(c<=65535){if(t+2>=s)break;r[t++]=224|c>>12,r[t++]=128|c>>6&63,r[t++]=128|c&63}else{if(t+3>=s)break;r[t++]=240|c>>18,r[t++]=128|c>>12&63,r[t++]=128|c>>6&63,r[t++]=128|c&63}}return r[t]=0,t-a};function or(e,r,t){var n=t>0?t:rr(e)+1,a=new Array(n),s=tr(e,a,0,a.length);return r&&(a.length=s),a}var tt=()=>{if(!er.length){var e=null;if(!e)return null;er=or(e,!0)}return er.shift()},Me={ttys:[],init(){},shutdown(){},register(e,r){Me.ttys[e]={input:[],output:[],ops:r},o.registerDevice(e,Me.stream_ops)},stream_ops:{open(e){var r=Me.ttys[e.node.rdev];if(!r)throw new o.ErrnoError(43);e.tty=r,e.seekable=!1},close(e){e.tty.ops.fsync(e.tty)},fsync(e){e.tty.ops.fsync(e.tty)},read(e,r,t,n,a){if(!e.tty||!e.tty.ops.get_char)throw new o.ErrnoError(60);for(var s=0,u=0;u<n;u++){var c;try{c=e.tty.ops.get_char(e.tty)}catch{throw new o.ErrnoError(29)}if(c===void 0&&s===0)throw new o.ErrnoError(6);if(c==null)break;s++,r[t+u]=c}return s&&(e.node.timestamp=Date.now()),s},write(e,r,t,n,a){if(!e.tty||!e.tty.ops.put_char)throw new o.ErrnoError(60);try{for(var s=0;s<n;s++)e.tty.ops.put_char(e.tty,r[t+s])}catch{throw new o.ErrnoError(29)}return n&&(e.node.timestamp=Date.now()),s}},default_tty_ops:{get_char(e){return tt()},put_char(e,r){r===null||r===10?(X(Fe(e.output,0)),e.output=[]):r!=0&&e.output.push(r)},fsync(e){e.output&&e.output.length>0&&(X(Fe(e.output,0)),e.output=[])},ioctl_tcgets(e){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(e,r,t){return 0},ioctl_tiocgwinsz(e){return[24,80]}},default_tty1_ops:{put_char(e,r){r===null||r===10?(V(Fe(e.output,0)),e.output=[]):r!=0&&e.output.push(r)},fsync(e){e.output&&e.output.length>0&&(V(Fe(e.output,0)),e.output=[])}}},ot=(e,r)=>(j.fill(0,e,e+r),e),wr=(e,r)=>Math.ceil(e/r)*r,yr=e=>{e=wr(e,65536);var r=Yr(65536,e);return r?ot(r,e):0},D={ops_table:null,mount(e){return D.createNode(null,"/",16895,0)},createNode(e,r,t,n){if(o.isBlkdev(t)||o.isFIFO(t))throw new o.ErrnoError(63);D.ops_table||={dir:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr,lookup:D.node_ops.lookup,mknod:D.node_ops.mknod,rename:D.node_ops.rename,unlink:D.node_ops.unlink,rmdir:D.node_ops.rmdir,readdir:D.node_ops.readdir,symlink:D.node_ops.symlink},stream:{llseek:D.stream_ops.llseek}},file:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr},stream:{llseek:D.stream_ops.llseek,read:D.stream_ops.read,write:D.stream_ops.write,allocate:D.stream_ops.allocate,mmap:D.stream_ops.mmap,msync:D.stream_ops.msync}},link:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr,readlink:D.node_ops.readlink},stream:{}},chrdev:{node:{getattr:D.node_ops.getattr,setattr:D.node_ops.setattr},stream:o.chrdev_stream_ops}};var a=o.createNode(e,r,t,n);return o.isDir(a.mode)?(a.node_ops=D.ops_table.dir.node,a.stream_ops=D.ops_table.dir.stream,a.contents={}):o.isFile(a.mode)?(a.node_ops=D.ops_table.file.node,a.stream_ops=D.ops_table.file.stream,a.usedBytes=0,a.contents=null):o.isLink(a.mode)?(a.node_ops=D.ops_table.link.node,a.stream_ops=D.ops_table.link.stream):o.isChrdev(a.mode)&&(a.node_ops=D.ops_table.chrdev.node,a.stream_ops=D.ops_table.chrdev.stream),a.timestamp=Date.now(),e&&(e.contents[r]=a,e.timestamp=a.timestamp),a},getFileDataAsTypedArray(e){return e.contents?e.contents.subarray?e.contents.subarray(0,e.usedBytes):new Uint8Array(e.contents):new Uint8Array(0)},expandFileStorage(e,r){var t=e.contents?e.contents.length:0;if(!(t>=r)){var n=1048576;r=Math.max(r,t*(t<n?2:1.125)>>>0),t!=0&&(r=Math.max(r,256));var a=e.contents;e.contents=new Uint8Array(r),e.usedBytes>0&&e.contents.set(a.subarray(0,e.usedBytes),0)}},resizeFileStorage(e,r){if(e.usedBytes!=r)if(r==0)e.contents=null,e.usedBytes=0;else{var t=e.contents;e.contents=new Uint8Array(r),t&&e.contents.set(t.subarray(0,Math.min(r,e.usedBytes))),e.usedBytes=r}},node_ops:{getattr(e){var r={};return r.dev=o.isChrdev(e.mode)?e.id:1,r.ino=e.id,r.mode=e.mode,r.nlink=1,r.uid=0,r.gid=0,r.rdev=e.rdev,o.isDir(e.mode)?r.size=4096:o.isFile(e.mode)?r.size=e.usedBytes:o.isLink(e.mode)?r.size=e.link.length:r.size=0,r.atime=new Date(e.timestamp),r.mtime=new Date(e.timestamp),r.ctime=new Date(e.timestamp),r.blksize=4096,r.blocks=Math.ceil(r.size/r.blksize),r},setattr(e,r){r.mode!==void 0&&(e.mode=r.mode),r.timestamp!==void 0&&(e.timestamp=r.timestamp),r.size!==void 0&&D.resizeFileStorage(e,r.size)},lookup(e,r){throw o.genericErrors[44]},mknod(e,r,t,n){return D.createNode(e,r,t,n)},rename(e,r,t){if(o.isDir(e.mode)){var n;try{n=o.lookupNode(r,t)}catch{}if(n)for(var a in n.contents)throw new o.ErrnoError(55)}delete e.parent.contents[e.name],e.parent.timestamp=Date.now(),e.name=t,r.contents[t]=e,r.timestamp=e.parent.timestamp},unlink(e,r){delete e.contents[r],e.timestamp=Date.now()},rmdir(e,r){var t=o.lookupNode(e,r);for(var n in t.contents)throw new o.ErrnoError(55);delete e.contents[r],e.timestamp=Date.now()},readdir(e){var r=[".",".."];for(var t of Object.keys(e.contents))r.push(t);return r},symlink(e,r,t){var n=D.createNode(e,r,41471,0);return n.link=t,n},readlink(e){if(!o.isLink(e.mode))throw new o.ErrnoError(28);return e.link}},stream_ops:{read(e,r,t,n,a){var s=e.node.contents;if(a>=e.node.usedBytes)return 0;var u=Math.min(e.node.usedBytes-a,n);if(u>8&&s.subarray)r.set(s.subarray(a,a+u),t);else for(var c=0;c<u;c++)r[t+c]=s[a+c];return u},write(e,r,t,n,a,s){if(r.buffer===w.buffer&&(s=!1),!n)return 0;var u=e.node;if(u.timestamp=Date.now(),r.subarray&&(!u.contents||u.contents.subarray)){if(s)return u.contents=r.subarray(t,t+n),u.usedBytes=n,n;if(u.usedBytes===0&&a===0)return u.contents=r.slice(t,t+n),u.usedBytes=n,n;if(a+n<=u.usedBytes)return u.contents.set(r.subarray(t,t+n),a),n}if(D.expandFileStorage(u,a+n),u.contents.subarray&&r.subarray)u.contents.set(r.subarray(t,t+n),a);else for(var c=0;c<n;c++)u.contents[a+c]=r[t+c];return u.usedBytes=Math.max(u.usedBytes,a+n),n},llseek(e,r,t){var n=r;if(t===1?n+=e.position:t===2&&o.isFile(e.node.mode)&&(n+=e.node.usedBytes),n<0)throw new o.ErrnoError(28);return n},allocate(e,r,t){D.expandFileStorage(e.node,r+t),e.node.usedBytes=Math.max(e.node.usedBytes,r+t)},mmap(e,r,t,n,a){if(!o.isFile(e.node.mode))throw new o.ErrnoError(43);var s,u,c=e.node.contents;if(!(a&2)&&c.buffer===w.buffer)u=!1,s=c.byteOffset;else{if((t>0||t+r<c.length)&&(c.subarray?c=c.subarray(t,t+r):c=Array.prototype.slice.call(c,t,t+r)),u=!0,s=yr(r),!s)throw new o.ErrnoError(48);w.set(c,s)}return{ptr:s,allocated:u}},msync(e,r,t,n,a){return D.stream_ops.write(e,r,0,n,t,!1),0}}},nt=(e,r,t,n)=>{var a=n?"":`al ${e}`;z(e).then(s=>{r(new Uint8Array(s)),a&&me(a)},s=>{if(t)t();else throw`Loading data file "${e}" failed.`}),a&&ce(a)},at=(e,r,t,n,a,s)=>{o.createDataFile(e,r,t,n,a,s)},it=[],st=(e,r,t,n)=>{typeof Browser<"u"&&Browser.init();var a=!1;return it.forEach(s=>{a||s.canHandle(r)&&(s.handle(e,r,t,n),a=!0)}),a},ut=(e,r,t,n,a,s,u,c,v,E)=>{var G=r?he.resolve(C.join2(e,r)):e,x=`cp ${G}`;function B(y){function O(te){E?.(),c||at(e,r,te,n,a,v),s?.(),me(x)}st(y,G,O,()=>{u?.(),me(x)})||O(y)}ce(x),typeof t=="string"?nt(t,B,u):B(t)},pt=e=>{var r={r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090},t=r[e];if(typeof t>"u")throw new Error(`Unknown file open mode: ${e}`);return t},nr=(e,r)=>{var t=0;return e&&(t|=365),r&&(t|=146),t},o={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:!1,ignorePermissions:!0,ErrnoError:class{constructor(e){this.name="ErrnoError",this.errno=e}},genericErrors:{},filesystems:null,syncFSRequests:0,FSStream:class{constructor(){this.shared={}}get object(){return this.node}set object(e){this.node=e}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(e){this.shared.flags=e}get position(){return this.shared.position}set position(e){this.shared.position=e}},FSNode:class{constructor(e,r,t,n){e||(e=this),this.parent=e,this.mount=e.mount,this.mounted=null,this.id=o.nextInode++,this.name=r,this.mode=t,this.node_ops={},this.stream_ops={},this.rdev=n,this.readMode=365,this.writeMode=146}get read(){return(this.mode&this.readMode)===this.readMode}set read(e){e?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(e){e?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return o.isDir(this.mode)}get isDevice(){return o.isChrdev(this.mode)}},lookupPath(e,r={}){if(e=he.resolve(e),!e)return{path:"",node:null};var t={follow_mount:!0,recurse_count:0};if(r=Object.assign(t,r),r.recurse_count>8)throw new o.ErrnoError(32);for(var n=e.split("/").filter(x=>!!x),a=o.root,s="/",u=0;u<n.length;u++){var c=u===n.length-1;if(c&&r.parent)break;if(a=o.lookupNode(a,n[u]),s=C.join2(s,n[u]),o.isMountpoint(a)&&(!c||c&&r.follow_mount)&&(a=a.mounted.root),!c||r.follow)for(var v=0;o.isLink(a.mode);){var E=o.readlink(s);s=he.resolve(C.dirname(s),E);var G=o.lookupPath(s,{recurse_count:r.recurse_count+1});if(a=G.node,v++>40)throw new o.ErrnoError(32)}}return{path:s,node:a}},getPath(e){for(var r;;){if(o.isRoot(e)){var t=e.mount.mountpoint;return r?t[t.length-1]!=="/"?`${t}/${r}`:t+r:t}r=r?`${e.name}/${r}`:e.name,e=e.parent}},hashName(e,r){for(var t=0,n=0;n<r.length;n++)t=(t<<5)-t+r.charCodeAt(n)|0;return(e+t>>>0)%o.nameTable.length},hashAddNode(e){var r=o.hashName(e.parent.id,e.name);e.name_next=o.nameTable[r],o.nameTable[r]=e},hashRemoveNode(e){var r=o.hashName(e.parent.id,e.name);if(o.nameTable[r]===e)o.nameTable[r]=e.name_next;else for(var t=o.nameTable[r];t;){if(t.name_next===e){t.name_next=e.name_next;break}t=t.name_next}},lookupNode(e,r){var t=o.mayLookup(e);if(t)throw new o.ErrnoError(t);for(var n=o.hashName(e.id,r),a=o.nameTable[n];a;a=a.name_next){var s=a.name;if(a.parent.id===e.id&&s===r)return a}return o.lookup(e,r)},createNode(e,r,t,n){var a=new o.FSNode(e,r,t,n);return o.hashAddNode(a),a},destroyNode(e){o.hashRemoveNode(e)},isRoot(e){return e===e.parent},isMountpoint(e){return!!e.mounted},isFile(e){return(e&61440)===32768},isDir(e){return(e&61440)===16384},isLink(e){return(e&61440)===40960},isChrdev(e){return(e&61440)===8192},isBlkdev(e){return(e&61440)===24576},isFIFO(e){return(e&61440)===4096},isSocket(e){return(e&49152)===49152},flagsToPermissionString(e){var r=["r","w","rw"][e&3];return e&512&&(r+="w"),r},nodePermissions(e,r){return o.ignorePermissions?0:r.includes("r")&&!(e.mode&292)||r.includes("w")&&!(e.mode&146)||r.includes("x")&&!(e.mode&73)?2:0},mayLookup(e){if(!o.isDir(e.mode))return 54;var r=o.nodePermissions(e,"x");return r||(e.node_ops.lookup?0:2)},mayCreate(e,r){try{var t=o.lookupNode(e,r);return 20}catch{}return o.nodePermissions(e,"wx")},mayDelete(e,r,t){var n;try{n=o.lookupNode(e,r)}catch(s){return s.errno}var a=o.nodePermissions(e,"wx");if(a)return a;if(t){if(!o.isDir(n.mode))return 54;if(o.isRoot(n)||o.getPath(n)===o.cwd())return 10}else if(o.isDir(n.mode))return 31;return 0},mayOpen(e,r){return e?o.isLink(e.mode)?32:o.isDir(e.mode)&&(o.flagsToPermissionString(r)!=="r"||r&512)?31:o.nodePermissions(e,o.flagsToPermissionString(r)):44},MAX_OPEN_FDS:4096,nextfd(){for(var e=0;e<=o.MAX_OPEN_FDS;e++)if(!o.streams[e])return e;throw new o.ErrnoError(33)},getStreamChecked(e){var r=o.getStream(e);if(!r)throw new o.ErrnoError(8);return r},getStream:e=>o.streams[e],createStream(e,r=-1){return e=Object.assign(new o.FSStream,e),r==-1&&(r=o.nextfd()),e.fd=r,o.streams[r]=e,e},closeStream(e){o.streams[e]=null},dupStream(e,r=-1){var t=o.createStream(e,r);return t.stream_ops?.dup?.(t),t},chrdev_stream_ops:{open(e){var r=o.getDevice(e.node.rdev);e.stream_ops=r.stream_ops,e.stream_ops.open?.(e)},llseek(){throw new o.ErrnoError(70)}},major:e=>e>>8,minor:e=>e&255,makedev:(e,r)=>e<<8|r,registerDevice(e,r){o.devices[e]={stream_ops:r}},getDevice:e=>o.devices[e],getMounts(e){for(var r=[],t=[e];t.length;){var n=t.pop();r.push(n),t.push(...n.mounts)}return r},syncfs(e,r){typeof e=="function"&&(r=e,e=!1),o.syncFSRequests++,o.syncFSRequests>1&&V(`warning: ${o.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`);var t=o.getMounts(o.root.mount),n=0;function a(u){return o.syncFSRequests--,r(u)}function s(u){if(u)return s.errored?void 0:(s.errored=!0,a(u));++n>=t.length&&a(null)}t.forEach(u=>{if(!u.type.syncfs)return s(null);u.type.syncfs(u,e,s)})},mount(e,r,t){var n=t==="/",a=!t,s;if(n&&o.root)throw new o.ErrnoError(10);if(!n&&!a){var u=o.lookupPath(t,{follow_mount:!1});if(t=u.path,s=u.node,o.isMountpoint(s))throw new o.ErrnoError(10);if(!o.isDir(s.mode))throw new o.ErrnoError(54)}var c={type:e,opts:r,mountpoint:t,mounts:[]},v=e.mount(c);return v.mount=c,c.root=v,n?o.root=v:s&&(s.mounted=c,s.mount&&s.mount.mounts.push(c)),v},unmount(e){var r=o.lookupPath(e,{follow_mount:!1});if(!o.isMountpoint(r.node))throw new o.ErrnoError(28);var t=r.node,n=t.mounted,a=o.getMounts(n);Object.keys(o.nameTable).forEach(u=>{for(var c=o.nameTable[u];c;){var v=c.name_next;a.includes(c.mount)&&o.destroyNode(c),c=v}}),t.mounted=null;var s=t.mount.mounts.indexOf(n);t.mount.mounts.splice(s,1)},lookup(e,r){return e.node_ops.lookup(e,r)},mknod(e,r,t){var n=o.lookupPath(e,{parent:!0}),a=n.node,s=C.basename(e);if(!s||s==="."||s==="..")throw new o.ErrnoError(28);var u=o.mayCreate(a,s);if(u)throw new o.ErrnoError(u);if(!a.node_ops.mknod)throw new o.ErrnoError(63);return a.node_ops.mknod(a,s,r,t)},create(e,r){return r=r!==void 0?r:438,r&=4095,r|=32768,o.mknod(e,r,0)},mkdir(e,r){return r=r!==void 0?r:511,r&=1023,r|=16384,o.mknod(e,r,0)},mkdirTree(e,r){for(var t=e.split("/"),n="",a=0;a<t.length;++a)if(t[a]){n+="/"+t[a];try{o.mkdir(n,r)}catch(s){if(s.errno!=20)throw s}}},mkdev(e,r,t){return typeof t>"u"&&(t=r,r=438),r|=8192,o.mknod(e,r,t)},symlink(e,r){if(!he.resolve(e))throw new o.ErrnoError(44);var t=o.lookupPath(r,{parent:!0}),n=t.node;if(!n)throw new o.ErrnoError(44);var a=C.basename(r),s=o.mayCreate(n,a);if(s)throw new o.ErrnoError(s);if(!n.node_ops.symlink)throw new o.ErrnoError(63);return n.node_ops.symlink(n,a,e)},rename(e,r){var t=C.dirname(e),n=C.dirname(r),a=C.basename(e),s=C.basename(r),u,c,v;if(u=o.lookupPath(e,{parent:!0}),c=u.node,u=o.lookupPath(r,{parent:!0}),v=u.node,!c||!v)throw new o.ErrnoError(44);if(c.mount!==v.mount)throw new o.ErrnoError(75);var E=o.lookupNode(c,a),G=he.relative(e,n);if(G.charAt(0)!==".")throw new o.ErrnoError(28);if(G=he.relative(r,t),G.charAt(0)!==".")throw new o.ErrnoError(55);var x;try{x=o.lookupNode(v,s)}catch{}if(E!==x){var B=o.isDir(E.mode),y=o.mayDelete(c,a,B);if(y)throw new o.ErrnoError(y);if(y=x?o.mayDelete(v,s,B):o.mayCreate(v,s),y)throw new o.ErrnoError(y);if(!c.node_ops.rename)throw new o.ErrnoError(63);if(o.isMountpoint(E)||x&&o.isMountpoint(x))throw new o.ErrnoError(10);if(v!==c&&(y=o.nodePermissions(c,"w"),y))throw new o.ErrnoError(y);o.hashRemoveNode(E);try{c.node_ops.rename(E,v,s),E.parent=v}catch(O){throw O}finally{o.hashAddNode(E)}}},rmdir(e){var r=o.lookupPath(e,{parent:!0}),t=r.node,n=C.basename(e),a=o.lookupNode(t,n),s=o.mayDelete(t,n,!0);if(s)throw new o.ErrnoError(s);if(!t.node_ops.rmdir)throw new o.ErrnoError(63);if(o.isMountpoint(a))throw new o.ErrnoError(10);t.node_ops.rmdir(t,n),o.destroyNode(a)},readdir(e){var r=o.lookupPath(e,{follow:!0}),t=r.node;if(!t.node_ops.readdir)throw new o.ErrnoError(54);return t.node_ops.readdir(t)},unlink(e){var r=o.lookupPath(e,{parent:!0}),t=r.node;if(!t)throw new o.ErrnoError(44);var n=C.basename(e),a=o.lookupNode(t,n),s=o.mayDelete(t,n,!1);if(s)throw new o.ErrnoError(s);if(!t.node_ops.unlink)throw new o.ErrnoError(63);if(o.isMountpoint(a))throw new o.ErrnoError(10);t.node_ops.unlink(t,n),o.destroyNode(a)},readlink(e){var r=o.lookupPath(e),t=r.node;if(!t)throw new o.ErrnoError(44);if(!t.node_ops.readlink)throw new o.ErrnoError(28);return he.resolve(o.getPath(t.parent),t.node_ops.readlink(t))},stat(e,r){var t=o.lookupPath(e,{follow:!r}),n=t.node;if(!n)throw new o.ErrnoError(44);if(!n.node_ops.getattr)throw new o.ErrnoError(63);return n.node_ops.getattr(n)},lstat(e){return o.stat(e,!0)},chmod(e,r,t){var n;if(typeof e=="string"){var a=o.lookupPath(e,{follow:!t});n=a.node}else n=e;if(!n.node_ops.setattr)throw new o.ErrnoError(63);n.node_ops.setattr(n,{mode:r&4095|n.mode&-4096,timestamp:Date.now()})},lchmod(e,r){o.chmod(e,r,!0)},fchmod(e,r){var t=o.getStreamChecked(e);o.chmod(t.node,r)},chown(e,r,t,n){var a;if(typeof e=="string"){var s=o.lookupPath(e,{follow:!n});a=s.node}else a=e;if(!a.node_ops.setattr)throw new o.ErrnoError(63);a.node_ops.setattr(a,{timestamp:Date.now()})},lchown(e,r,t){o.chown(e,r,t,!0)},fchown(e,r,t){var n=o.getStreamChecked(e);o.chown(n.node,r,t)},truncate(e,r){if(r<0)throw new o.ErrnoError(28);var t;if(typeof e=="string"){var n=o.lookupPath(e,{follow:!0});t=n.node}else t=e;if(!t.node_ops.setattr)throw new o.ErrnoError(63);if(o.isDir(t.mode))throw new o.ErrnoError(31);if(!o.isFile(t.mode))throw new o.ErrnoError(28);var a=o.nodePermissions(t,"w");if(a)throw new o.ErrnoError(a);t.node_ops.setattr(t,{size:r,timestamp:Date.now()})},ftruncate(e,r){var t=o.getStreamChecked(e);if(!(t.flags&2097155))throw new o.ErrnoError(28);o.truncate(t.node,r)},utime(e,r,t){var n=o.lookupPath(e,{follow:!0}),a=n.node;a.node_ops.setattr(a,{timestamp:Math.max(r,t)})},open(e,r,t){if(e==="")throw new o.ErrnoError(44);r=typeof r=="string"?pt(r):r,r&64?(t=typeof t>"u"?438:t,t=t&4095|32768):t=0;var n;if(typeof e=="object")n=e;else{e=C.normalize(e);try{var a=o.lookupPath(e,{follow:!(r&131072)});n=a.node}catch{}}var s=!1;if(r&64)if(n){if(r&128)throw new o.ErrnoError(20)}else n=o.mknod(e,t,0),s=!0;if(!n)throw new o.ErrnoError(44);if(o.isChrdev(n.mode)&&(r&=-513),r&65536&&!o.isDir(n.mode))throw new o.ErrnoError(54);if(!s){var u=o.mayOpen(n,r);if(u)throw new o.ErrnoError(u)}r&512&&!s&&o.truncate(n,0),r&=-131713;var c=o.createStream({node:n,path:o.getPath(n),flags:r,seekable:!0,position:0,stream_ops:n.stream_ops,ungotten:[],error:!1});return c.stream_ops.open&&c.stream_ops.open(c),i.logReadFiles&&!(r&1)&&(o.readFiles||(o.readFiles={}),e in o.readFiles||(o.readFiles[e]=1)),c},close(e){if(o.isClosed(e))throw new o.ErrnoError(8);e.getdents&&(e.getdents=null);try{e.stream_ops.close&&e.stream_ops.close(e)}catch(r){throw r}finally{o.closeStream(e.fd)}e.fd=null},isClosed(e){return e.fd===null},llseek(e,r,t){if(o.isClosed(e))throw new o.ErrnoError(8);if(!e.seekable||!e.stream_ops.llseek)throw new o.ErrnoError(70);if(t!=0&&t!=1&&t!=2)throw new o.ErrnoError(28);return e.position=e.stream_ops.llseek(e,r,t),e.ungotten=[],e.position},read(e,r,t,n,a){if(n<0||a<0)throw new o.ErrnoError(28);if(o.isClosed(e))throw new o.ErrnoError(8);if((e.flags&2097155)===1)throw new o.ErrnoError(8);if(o.isDir(e.node.mode))throw new o.ErrnoError(31);if(!e.stream_ops.read)throw new o.ErrnoError(28);var s=typeof a<"u";if(!s)a=e.position;else if(!e.seekable)throw new o.ErrnoError(70);var u=e.stream_ops.read(e,r,t,n,a);return s||(e.position+=u),u},write(e,r,t,n,a,s){if(n<0||a<0)throw new o.ErrnoError(28);if(o.isClosed(e))throw new o.ErrnoError(8);if(!(e.flags&2097155))throw new o.ErrnoError(8);if(o.isDir(e.node.mode))throw new o.ErrnoError(31);if(!e.stream_ops.write)throw new o.ErrnoError(28);e.seekable&&e.flags&1024&&o.llseek(e,0,2);var u=typeof a<"u";if(!u)a=e.position;else if(!e.seekable)throw new o.ErrnoError(70);var c=e.stream_ops.write(e,r,t,n,a,s);return u||(e.position+=c),c},allocate(e,r,t){if(o.isClosed(e))throw new o.ErrnoError(8);if(r<0||t<=0)throw new o.ErrnoError(28);if(!(e.flags&2097155))throw new o.ErrnoError(8);if(!o.isFile(e.node.mode)&&!o.isDir(e.node.mode))throw new o.ErrnoError(43);if(!e.stream_ops.allocate)throw new o.ErrnoError(138);e.stream_ops.allocate(e,r,t)},mmap(e,r,t,n,a){if(n&2&&!(a&2)&&(e.flags&2097155)!==2)throw new o.ErrnoError(2);if((e.flags&2097155)===1)throw new o.ErrnoError(2);if(!e.stream_ops.mmap)throw new o.ErrnoError(43);return e.stream_ops.mmap(e,r,t,n,a)},msync(e,r,t,n,a){return e.stream_ops.msync?e.stream_ops.msync(e,r,t,n,a):0},ioctl(e,r,t){if(!e.stream_ops.ioctl)throw new o.ErrnoError(59);return e.stream_ops.ioctl(e,r,t)},readFile(e,r={}){if(r.flags=r.flags||0,r.encoding=r.encoding||"binary",r.encoding!=="utf8"&&r.encoding!=="binary")throw new Error(`Invalid encoding type "${r.encoding}"`);var t,n=o.open(e,r.flags),a=o.stat(e),s=a.size,u=new Uint8Array(s);return o.read(n,u,0,s,0),r.encoding==="utf8"?t=Fe(u,0):r.encoding==="binary"&&(t=u),o.close(n),t},writeFile(e,r,t={}){t.flags=t.flags||577;var n=o.open(e,t.flags,t.mode);if(typeof r=="string"){var a=new Uint8Array(rr(r)+1),s=tr(r,a,0,a.length);o.write(n,a,0,s,void 0,t.canOwn)}else if(ArrayBuffer.isView(r))o.write(n,r,0,r.byteLength,void 0,t.canOwn);else throw new Error("Unsupported data type");o.close(n)},cwd:()=>o.currentPath,chdir(e){var r=o.lookupPath(e,{follow:!0});if(r.node===null)throw new o.ErrnoError(44);if(!o.isDir(r.node.mode))throw new o.ErrnoError(54);var t=o.nodePermissions(r.node,"x");if(t)throw new o.ErrnoError(t);o.currentPath=r.path},createDefaultDirectories(){o.mkdir("/tmp"),o.mkdir("/home"),o.mkdir("/home/web_user")},createDefaultDevices(){o.mkdir("/dev"),o.registerDevice(o.makedev(1,3),{read:()=>0,write:(n,a,s,u,c)=>u}),o.mkdev("/dev/null",o.makedev(1,3)),Me.register(o.makedev(5,0),Me.default_tty_ops),Me.register(o.makedev(6,0),Me.default_tty1_ops),o.mkdev("/dev/tty",o.makedev(5,0)),o.mkdev("/dev/tty1",o.makedev(6,0));var e=new Uint8Array(1024),r=0,t=()=>(r===0&&(r=gr(e).byteLength),e[--r]);o.createDevice("/dev","random",t),o.createDevice("/dev","urandom",t),o.mkdir("/dev/shm"),o.mkdir("/dev/shm/tmp")},createSpecialDirectories(){o.mkdir("/proc");var e=o.mkdir("/proc/self");o.mkdir("/proc/self/fd"),o.mount({mount(){var r=o.createNode(e,"fd",16895,73);return r.node_ops={lookup(t,n){var a=+n,s=o.getStreamChecked(a),u={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>s.path}};return u.parent=u,u}},r}},{},"/proc/self/fd")},createStandardStreams(){i.stdin?o.createDevice("/dev","stdin",i.stdin):o.symlink("/dev/tty","/dev/stdin"),i.stdout?o.createDevice("/dev","stdout",null,i.stdout):o.symlink("/dev/tty","/dev/stdout"),i.stderr?o.createDevice("/dev","stderr",null,i.stderr):o.symlink("/dev/tty1","/dev/stderr");var e=o.open("/dev/stdin",0),r=o.open("/dev/stdout",1),t=o.open("/dev/stderr",1)},staticInit(){[44].forEach(e=>{o.genericErrors[e]=new o.ErrnoError(e),o.genericErrors[e].stack="<generic error, no stack>"}),o.nameTable=new Array(4096),o.mount(D,{},"/"),o.createDefaultDirectories(),o.createDefaultDevices(),o.createSpecialDirectories(),o.filesystems={MEMFS:D}},init(e,r,t){o.init.initialized=!0,i.stdin=e||i.stdin,i.stdout=r||i.stdout,i.stderr=t||i.stderr,o.createStandardStreams()},quit(){o.init.initialized=!1;for(var e=0;e<o.streams.length;e++){var r=o.streams[e];r&&o.close(r)}},findObject(e,r){var t=o.analyzePath(e,r);return t.exists?t.object:null},analyzePath(e,r){try{var t=o.lookupPath(e,{follow:!r});e=t.path}catch{}var n={isRoot:!1,exists:!1,error:0,name:null,path:null,object:null,parentExists:!1,parentPath:null,parentObject:null};try{var t=o.lookupPath(e,{parent:!0});n.parentExists=!0,n.parentPath=t.path,n.parentObject=t.node,n.name=C.basename(e),t=o.lookupPath(e,{follow:!r}),n.exists=!0,n.path=t.path,n.object=t.node,n.name=t.node.name,n.isRoot=t.path==="/"}catch(a){n.error=a.errno}return n},createPath(e,r,t,n){e=typeof e=="string"?e:o.getPath(e);for(var a=r.split("/").reverse();a.length;){var s=a.pop();if(s){var u=C.join2(e,s);try{o.mkdir(u)}catch{}e=u}}return u},createFile(e,r,t,n,a){var s=C.join2(typeof e=="string"?e:o.getPath(e),r),u=nr(n,a);return o.create(s,u)},createDataFile(e,r,t,n,a,s){var u=r;e&&(e=typeof e=="string"?e:o.getPath(e),u=r?C.join2(e,r):e);var c=nr(n,a),v=o.create(u,c);if(t){if(typeof t=="string"){for(var E=new Array(t.length),G=0,x=t.length;G<x;++G)E[G]=t.charCodeAt(G);t=E}o.chmod(v,c|146);var B=o.open(v,577);o.write(B,t,0,t.length,0,s),o.close(B),o.chmod(v,c)}},createDevice(e,r,t,n){var a=C.join2(typeof e=="string"?e:o.getPath(e),r),s=nr(!!t,!!n);o.createDevice.major||(o.createDevice.major=64);var u=o.makedev(o.createDevice.major++,0);return o.registerDevice(u,{open(c){c.seekable=!1},close(c){n?.buffer?.length&&n(10)},read(c,v,E,G,x){for(var B=0,y=0;y<G;y++){var O;try{O=t()}catch{throw new o.ErrnoError(29)}if(O===void 0&&B===0)throw new o.ErrnoError(6);if(O==null)break;B++,v[E+y]=O}return B&&(c.node.timestamp=Date.now()),B},write(c,v,E,G,x){for(var B=0;B<G;B++)try{n(v[E+B])}catch{throw new o.ErrnoError(29)}return G&&(c.node.timestamp=Date.now()),B}}),o.mkdev(a,s,u)},forceLoadFile(e){if(e.isDevice||e.isFolder||e.link||e.contents)return!0;if(typeof XMLHttpRequest<"u")throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");try{e.contents=q(e.url),e.usedBytes=e.contents.length}catch{throw new o.ErrnoError(29)}},createLazyFile(e,r,t,n,a){class s{constructor(){this.lengthKnown=!1,this.chunks=[]}get(y){if(!(y>this.length-1||y<0)){var O=y%this.chunkSize,te=y/this.chunkSize|0;return this.getter(te)[O]}}setDataGetter(y){this.getter=y}cacheLength(){var y=new XMLHttpRequest;if(y.open("HEAD",t,!1),y.send(null),!(y.status>=200&&y.status<300||y.status===304))throw new Error("Couldn't load "+t+". Status: "+y.status);var O=Number(y.getResponseHeader("Content-length")),te,de=(te=y.getResponseHeader("Accept-Ranges"))&&te==="bytes",ne=(te=y.getResponseHeader("Content-Encoding"))&&te==="gzip",fe=1024*1024;de||(fe=O);var ve=(ge,xe)=>{if(ge>xe)throw new Error("invalid range ("+ge+", "+xe+") or no bytes requested!");if(xe>O-1)throw new Error("only "+O+" bytes available! programmer error!");var ee=new XMLHttpRequest;if(ee.open("GET",t,!1),O!==fe&&ee.setRequestHeader("Range","bytes="+ge+"-"+xe),ee.responseType="arraybuffer",ee.overrideMimeType&&ee.overrideMimeType("text/plain; charset=x-user-defined"),ee.send(null),!(ee.status>=200&&ee.status<300||ee.status===304))throw new Error("Couldn't load "+t+". Status: "+ee.status);return ee.response!==void 0?new Uint8Array(ee.response||[]):or(ee.responseText||"",!0)},Ie=this;Ie.setDataGetter(ge=>{var xe=ge*fe,ee=(ge+1)*fe-1;if(ee=Math.min(ee,O-1),typeof Ie.chunks[ge]>"u"&&(Ie.chunks[ge]=ve(xe,ee)),typeof Ie.chunks[ge]>"u")throw new Error("doXHR failed!");return Ie.chunks[ge]}),(ne||!O)&&(fe=O=1,O=this.getter(0).length,fe=O,X("LazyFiles on gzip forces download of the whole file when length is accessed")),this._length=O,this._chunkSize=fe,this.lengthKnown=!0}get length(){return this.lengthKnown||this.cacheLength(),this._length}get chunkSize(){return this.lengthKnown||this.cacheLength(),this._chunkSize}}if(typeof XMLHttpRequest<"u"){if(!h)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var u=new s,c={isDevice:!1,contents:u}}else var c={isDevice:!1,url:t};var v=o.createFile(e,r,c,n,a);c.contents?v.contents=c.contents:c.url&&(v.contents=null,v.url=c.url),Object.defineProperties(v,{usedBytes:{get:function(){return this.contents.length}}});var E={},G=Object.keys(v.stream_ops);G.forEach(B=>{var y=v.stream_ops[B];E[B]=(...O)=>(o.forceLoadFile(v),y(...O))});function x(B,y,O,te,de){var ne=B.node.contents;if(de>=ne.length)return 0;var fe=Math.min(ne.length-de,te);if(ne.slice)for(var ve=0;ve<fe;ve++)y[O+ve]=ne[de+ve];else for(var ve=0;ve<fe;ve++)y[O+ve]=ne.get(de+ve);return fe}return E.read=(B,y,O,te,de)=>(o.forceLoadFile(v),x(B,y,O,te,de)),E.mmap=(B,y,O,te,de)=>{o.forceLoadFile(v);var ne=yr(y);if(!ne)throw new o.ErrnoError(48);return x(B,w,ne,y,O),{ptr:ne,allocated:!0}},v.stream_ops=E,v}},ue=(e,r)=>e?Fe(j,e,r):"",U={DEFAULT_POLLMASK:5,calculateAt(e,r,t){if(C.isAbs(r))return r;var n;if(e===-100)n=o.cwd();else{var a=U.getStreamFromFD(e);n=a.path}if(r.length==0){if(!t)throw new o.ErrnoError(44);return n}return C.join2(n,r)},doStat(e,r,t){var n=e(r);b[t>>2]=n.dev,b[t+4>>2]=n.mode,k[t+8>>2]=n.nlink,b[t+12>>2]=n.uid,b[t+16>>2]=n.gid,b[t+20>>2]=n.rdev,$=[n.size>>>0,(R=n.size,+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+24>>2]=$[0],b[t+28>>2]=$[1],b[t+32>>2]=4096,b[t+36>>2]=n.blocks;var a=n.atime.getTime(),s=n.mtime.getTime(),u=n.ctime.getTime();return $=[Math.floor(a/1e3)>>>0,(R=Math.floor(a/1e3),+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+40>>2]=$[0],b[t+44>>2]=$[1],k[t+48>>2]=a%1e3*1e3,$=[Math.floor(s/1e3)>>>0,(R=Math.floor(s/1e3),+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+56>>2]=$[0],b[t+60>>2]=$[1],k[t+64>>2]=s%1e3*1e3,$=[Math.floor(u/1e3)>>>0,(R=Math.floor(u/1e3),+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+72>>2]=$[0],b[t+76>>2]=$[1],k[t+80>>2]=u%1e3*1e3,$=[n.ino>>>0,(R=n.ino,+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[t+88>>2]=$[0],b[t+92>>2]=$[1],0},doMsync(e,r,t,n,a){if(!o.isFile(r.node.mode))throw new o.ErrnoError(43);if(n&2)return 0;var s=j.slice(e,e+t);o.msync(r,s,a,t,n)},getStreamFromFD(e){var r=o.getStreamChecked(e);return r},varargs:void 0,getStr(e){var r=ue(e);return r}};function lt(e,r,t,n){try{if(r=U.getStr(r),r=U.calculateAt(e,r),t&-8)return-28;var a=o.lookupPath(r,{follow:!0}),s=a.node;if(!s)return-44;var u="";return t&4&&(u+="r"),t&2&&(u+="w"),t&1&&(u+="x"),u&&o.nodePermissions(s,u)?-2:0}catch(c){if(typeof o>"u"||c.name!=="ErrnoError")throw c;return-c.errno}}function Se(){var e=b[+U.varargs>>2];return U.varargs+=4,e}var Oe=Se;function ct(e,r,t){U.varargs=t;try{var n=U.getStreamFromFD(e);switch(r){case 0:{var a=Se();if(a<0)return-28;for(;o.streams[a];)a++;var s;return s=o.dupStream(n,a),s.fd}case 1:case 2:return 0;case 3:return n.flags;case 4:{var a=Se();return n.flags|=a,0}case 12:{var a=Oe(),u=0;return W[a+u>>1]=2,0}case 13:case 14:return 0}return-28}catch(c){if(typeof o>"u"||c.name!=="ErrnoError")throw c;return-c.errno}}function dt(e,r){try{var t=U.getStreamFromFD(e);return U.doStat(o.stat,t.path,r)}catch(n){if(typeof o>"u"||n.name!=="ErrnoError")throw n;return-n.errno}}function ft(e,r,t){U.varargs=t;try{var n=U.getStreamFromFD(e);switch(r){case 21509:return n.tty?0:-59;case 21505:{if(!n.tty)return-59;if(n.tty.ops.ioctl_tcgets){var a=n.tty.ops.ioctl_tcgets(n),s=Oe();b[s>>2]=a.c_iflag||0,b[s+4>>2]=a.c_oflag||0,b[s+8>>2]=a.c_cflag||0,b[s+12>>2]=a.c_lflag||0;for(var u=0;u<32;u++)w[s+u+17]=a.c_cc[u]||0;return 0}return 0}case 21510:case 21511:case 21512:return n.tty?0:-59;case 21506:case 21507:case 21508:{if(!n.tty)return-59;if(n.tty.ops.ioctl_tcsets){for(var s=Oe(),c=b[s>>2],v=b[s+4>>2],E=b[s+8>>2],G=b[s+12>>2],x=[],u=0;u<32;u++)x.push(w[s+u+17]);return n.tty.ops.ioctl_tcsets(n.tty,r,{c_iflag:c,c_oflag:v,c_cflag:E,c_lflag:G,c_cc:x})}return 0}case 21519:{if(!n.tty)return-59;var s=Oe();return b[s>>2]=0,0}case 21520:return n.tty?-28:-59;case 21531:{var s=Oe();return o.ioctl(n,r,s)}case 21523:{if(!n.tty)return-59;if(n.tty.ops.ioctl_tiocgwinsz){var B=n.tty.ops.ioctl_tiocgwinsz(n.tty),s=Oe();W[s>>1]=B[0],W[s+2>>1]=B[1]}return 0}case 21524:return n.tty?0:-59;case 21515:return n.tty?0:-59;default:return-28}}catch(y){if(typeof o>"u"||y.name!=="ErrnoError")throw y;return-y.errno}}function vt(e,r,t,n){try{r=U.getStr(r);var a=n&256,s=n&4096;return n=n&-6401,r=U.calculateAt(e,r,s),U.doStat(a?o.lstat:o.stat,r,t)}catch(u){if(typeof o>"u"||u.name!=="ErrnoError")throw u;return-u.errno}}function mt(e,r,t,n){U.varargs=n;try{r=U.getStr(r),r=U.calculateAt(e,r);var a=n?Se():0;return o.open(r,t,a).fd}catch(s){if(typeof o>"u"||s.name!=="ErrnoError")throw s;return-s.errno}}var Ke=(e,r,t)=>tr(e,j,r,t);function _t(e,r,t,n){try{if(r=U.getStr(r),r=U.calculateAt(e,r),n<=0)return-28;var a=o.readlink(r),s=Math.min(n,rr(a)),u=w[t+s];return Ke(a,t,n+1),w[t+s]=u,s}catch(c){if(typeof o>"u"||c.name!=="ErrnoError")throw c;return-c.errno}}function gt(e){try{return e=U.getStr(e),o.rmdir(e),0}catch(r){if(typeof o>"u"||r.name!=="ErrnoError")throw r;return-r.errno}}function ht(e,r){try{return e=U.getStr(e),U.doStat(o.stat,e,r)}catch(t){if(typeof o>"u"||t.name!=="ErrnoError")throw t;return-t.errno}}function wt(e,r,t){try{return r=U.getStr(r),r=U.calculateAt(e,r),t===0?o.unlink(r):t===512?o.rmdir(r):_e("Invalid flags passed to unlinkat"),0}catch(n){if(typeof o>"u"||n.name!=="ErrnoError")throw n;return-n.errno}}var yt=()=>{_e("")},bt=1,Et=()=>bt,Mt=(e,r,t)=>j.copyWithin(e,r,r+t),ar=(e,r)=>r+2097152>>>0<4194305-!!e?(e>>>0)+r*4294967296:NaN;function Dt(e,r,t,n,a,s,u,c){var v=ar(a,s);try{if(isNaN(v))return 61;var E=U.getStreamFromFD(n),G=o.mmap(E,e,v,r,t),x=G.ptr;return b[u>>2]=G.allocated,k[c>>2]=x,0}catch(B){if(typeof o>"u"||B.name!=="ErrnoError")throw B;return-B.errno}}function Xt(e,r,t,n,a,s,u){var c=ar(s,u);try{var v=U.getStreamFromFD(a);t&2&&U.doMsync(e,v,r,n,c)}catch(E){if(typeof o>"u"||E.name!=="ErrnoError")throw E;return-E.errno}}var Lt=(e,r,t,n)=>{var a=new Date().getFullYear(),s=new Date(a,0,1),u=new Date(a,6,1),c=s.getTimezoneOffset(),v=u.getTimezoneOffset(),E=Math.max(c,v);k[e>>2]=E*60,b[r>>2]=+(c!=v);var G=y=>{var O=y>=0?"-":"+",te=Math.abs(y),de=String(Math.floor(te/60)).padStart(2,"0"),ne=String(te%60).padStart(2,"0");return`UTC${O}${de}${ne}`},x=G(c),B=G(v);v<c?(Ke(x,t,17),Ke(B,n,17)):(Ke(x,n,17),Ke(B,t,17))},ir=[],Bt=(e,r)=>{ir.length=0;for(var t;t=j[e++];){var n=t!=105;n&=t!=112,r+=n&&r%8?4:0,ir.push(t==112?k[r>>2]:t==105?b[r>>2]:oe[r>>3]),r+=n?8:4}return ir},Rt=(e,r,t)=>{var n=Bt(r,t);return qr[e](...n)},kt=(e,r,t)=>Rt(e,r,t),Gt=()=>Date.now(),Ft;Ft=()=>performance.now();var Ot=()=>2147483648,xt=e=>{var r=S.buffer,t=(e-r.byteLength+65535)/65536;try{return S.grow(t),K(),1}catch{}},Ut=e=>{var r=j.length;e>>>=0;var t=Ot();if(e>t)return!1;for(var n=(v,E)=>v+(E-v%E)%E,a=1;a<=4;a*=2){var s=r*(1+.2/a);s=Math.min(s,e+100663296);var u=Math.min(t,n(Math.max(e,s),65536)),c=xt(u);if(c)return!0}return!1},sr={},Nt=()=>I||"./this.program",Ce=()=>{if(!Ce.strings){var e=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",r={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:e,_:Nt()};for(var t in sr)sr[t]===void 0?delete r[t]:r[t]=sr[t];var n=[];for(var t in r)n.push(`${t}=${r[t]}`);Ce.strings=n}return Ce.strings},Vt=(e,r)=>{for(var t=0;t<e.length;++t)w[r++]=e.charCodeAt(t);w[r]=0},Kt=(e,r)=>{var t=0;return Ce().forEach((n,a)=>{var s=r+t;k[e+a*4>>2]=s,Vt(n,s),t+=n.length+1}),0},Ct=(e,r)=>{var t=Ce();k[e>>2]=t.length;var n=0;return t.forEach(a=>n+=a.length+1),k[r>>2]=n,0},It=0,Zt=()=>It>0,Yt=e=>{ae=e,Zt()||(P=!0),Q(e,new $r(e))},zt=(e,r)=>{ae=e,Yt(e)},jt=zt;function Pt(e){try{var r=U.getStreamFromFD(e);return o.close(r),0}catch(t){if(typeof o>"u"||t.name!=="ErrnoError")throw t;return t.errno}}var Jt=(e,r,t,n)=>{for(var a=0,s=0;s<t;s++){var u=k[r>>2],c=k[r+4>>2];r+=8;var v=o.read(e,w,u,c,n);if(v<0)return-1;if(a+=v,v<c)break;typeof n<"u"&&(n+=v)}return a};function St(e,r,t,n){try{var a=U.getStreamFromFD(e),s=Jt(a,r,t);return k[n>>2]=s,0}catch(u){if(typeof o>"u"||u.name!=="ErrnoError")throw u;return u.errno}}function Ht(e,r,t,n,a){var s=ar(r,t);try{if(isNaN(s))return 61;var u=U.getStreamFromFD(e);return o.llseek(u,s,n),$=[u.position>>>0,(R=u.position,+Math.abs(R)>=1?R>0?+Math.floor(R/4294967296)>>>0:~~+Math.ceil((R-+(~~R>>>0))/4294967296)>>>0:0)],b[a>>2]=$[0],b[a+4>>2]=$[1],u.getdents&&s===0&&n===0&&(u.getdents=null),0}catch(c){if(typeof o>"u"||c.name!=="ErrnoError")throw c;return c.errno}}var Tt=(e,r,t,n)=>{for(var a=0,s=0;s<t;s++){var u=k[r>>2],c=k[r+4>>2];r+=8;var v=o.write(e,w,u,c,n);if(v<0)return-1;a+=v,typeof n<"u"&&(n+=v)}return a};function Qt(e,r,t,n){try{var a=U.getStreamFromFD(e),s=Tt(a,r,t);return k[n>>2]=s,0}catch(u){if(typeof o>"u"||u.name!=="ErrnoError")throw u;return u.errno}}o.createPreloadedFile=ut,o.staticInit();var qt={a:et,k:lt,h:ct,y:dt,z:ft,w:vt,d:mt,r:_t,s:gt,x:ht,t:wt,l:yt,i:Et,j:Mt,m:Dt,n:Xt,p:Lt,A:kt,b:Gt,q:Ut,u:Kt,v:Ct,e:jt,c:Pt,f:St,o:Ht,g:Qt},N=Qr(),$t=()=>($t=N.C)(),Wt=i._webidl_free=e=>(Wt=i._webidl_free=N.D)(e),At=i._free=e=>(At=i._free=N.E)(e),eo=i._webidl_malloc=e=>(eo=i._webidl_malloc=N.F)(e),ro=i._malloc=e=>(ro=i._malloc=N.G)(e),br=i._emscripten_bind_VoidPtr___destroy___0=e=>(br=i._emscripten_bind_VoidPtr___destroy___0=N.H)(e),Er=i._emscripten_bind_Graphviz_Graphviz_2=(e,r)=>(Er=i._emscripten_bind_Graphviz_Graphviz_2=N.I)(e,r),Mr=i._emscripten_bind_Graphviz_version_0=()=>(Mr=i._emscripten_bind_Graphviz_version_0=N.J)(),Dr=i._emscripten_bind_Graphviz_lastError_0=()=>(Dr=i._emscripten_bind_Graphviz_lastError_0=N.K)(),Xr=i._emscripten_bind_Graphviz_createFile_2=(e,r,t)=>(Xr=i._emscripten_bind_Graphviz_createFile_2=N.L)(e,r,t),Lr=i._emscripten_bind_Graphviz_layout_3=(e,r,t,n)=>(Lr=i._emscripten_bind_Graphviz_layout_3=N.M)(e,r,t,n),Br=i._emscripten_bind_Graphviz_acyclic_3=(e,r,t,n)=>(Br=i._emscripten_bind_Graphviz_acyclic_3=N.N)(e,r,t,n),Rr=i._emscripten_bind_Graphviz_tred_3=(e,r,t,n)=>(Rr=i._emscripten_bind_Graphviz_tred_3=N.O)(e,r,t,n),kr=i._emscripten_bind_Graphviz_unflatten_4=(e,r,t,n,a)=>(kr=i._emscripten_bind_Graphviz_unflatten_4=N.P)(e,r,t,n,a),Gr=i._emscripten_bind_Graphviz_get_layout_result_0=e=>(Gr=i._emscripten_bind_Graphviz_get_layout_result_0=N.Q)(e),Fr=i._emscripten_bind_Graphviz_set_layout_result_1=(e,r)=>(Fr=i._emscripten_bind_Graphviz_set_layout_result_1=N.R)(e,r),Or=i._emscripten_bind_Graphviz_get_acyclic_outFile_0=e=>(Or=i._emscripten_bind_Graphviz_get_acyclic_outFile_0=N.S)(e),xr=i._emscripten_bind_Graphviz_set_acyclic_outFile_1=(e,r)=>(xr=i._emscripten_bind_Graphviz_set_acyclic_outFile_1=N.T)(e,r),Ur=i._emscripten_bind_Graphviz_get_acyclic_num_rev_0=e=>(Ur=i._emscripten_bind_Graphviz_get_acyclic_num_rev_0=N.U)(e),Nr=i._emscripten_bind_Graphviz_set_acyclic_num_rev_1=(e,r)=>(Nr=i._emscripten_bind_Graphviz_set_acyclic_num_rev_1=N.V)(e,r),Vr=i._emscripten_bind_Graphviz_get_tred_out_0=e=>(Vr=i._emscripten_bind_Graphviz_get_tred_out_0=N.W)(e),Kr=i._emscripten_bind_Graphviz_set_tred_out_1=(e,r)=>(Kr=i._emscripten_bind_Graphviz_set_tred_out_1=N.X)(e,r),Cr=i._emscripten_bind_Graphviz_get_tred_err_0=e=>(Cr=i._emscripten_bind_Graphviz_get_tred_err_0=N.Y)(e),Ir=i._emscripten_bind_Graphviz_set_tred_err_1=(e,r)=>(Ir=i._emscripten_bind_Graphviz_set_tred_err_1=N.Z)(e,r),Zr=i._emscripten_bind_Graphviz___destroy___0=e=>(Zr=i._emscripten_bind_Graphviz___destroy___0=N._)(e),Yr=(e,r)=>(Yr=N.aa)(e,r),zr=e=>(zr=N.ba)(e);i.UTF8ToString=ue;var He;be=function e(){He||jr(),He||(be=e)};function jr(){if(ye>0||(Re(),ye>0))return;function e(){He||(He=!0,i.calledRun=!0,!P&&(Ne(),f(i),Pe()))}e()}jr();function pe(){}pe.prototype=Object.create(pe.prototype),pe.prototype.constructor=pe,pe.prototype.__class__=pe,pe.__cache__={},i.WrapperObject=pe;function Te(e){return(e||pe).__cache__}i.getCache=Te;function ur(e,r){var t=Te(r),n=t[e];return n||(n=Object.create((r||pe).prototype),n.ptr=e,t[e]=n)}i.wrapPointer=ur;function to(e,r){return ur(e.ptr,r)}i.castObject=to,i.NULL=ur(0);function oo(e){if(!e.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";e.__destroy__(),delete Te(e.__class__)[e.ptr]}i.destroy=oo;function no(e,r){return e.ptr===r.ptr}i.compare=no;function ao(e){return e.ptr}i.getPointer=ao;function io(e){return e.__class__}i.getClass=io;var F={buffer:0,size:0,pos:0,temps:[],needed:0,prepare(){if(F.needed){for(var e=0;e<F.temps.length;e++)i._webidl_free(F.temps[e]);F.temps.length=0,i._webidl_free(F.buffer),F.buffer=0,F.size+=F.needed,F.needed=0}F.buffer||(F.size+=128,F.buffer=i._webidl_malloc(F.size),J(F.buffer)),F.pos=0},alloc(e,r){J(F.buffer);var t=r.BYTES_PER_ELEMENT,n=e.length*t;n=wr(n,8);var a;return F.pos+n>=F.size?(J(n>0),F.needed+=n,a=i._webidl_malloc(n),F.temps.push(a)):(a=F.buffer+F.pos,F.pos+=n),a},copy(e,r,t){t/=r.BYTES_PER_ELEMENT;for(var n=0;n<e.length;n++)r[t+n]=e[n]}};function le(e){if(typeof e=="string"){var r=or(e),t=F.alloc(r,w);return F.copy(r,w,t),t}return e}function we(){throw"cannot construct a VoidPtr, no constructor in IDL"}we.prototype=Object.create(pe.prototype),we.prototype.constructor=we,we.prototype.__class__=we,we.__cache__={},i.VoidPtr=we,we.prototype.__destroy__=we.prototype.__destroy__=function(){var e=this.ptr;br(e)};function g(e,r){e&&typeof e=="object"&&(e=e.ptr),r&&typeof r=="object"&&(r=r.ptr),this.ptr=Er(e,r),Te(g)[this.ptr]=this}return g.prototype=Object.create(pe.prototype),g.prototype.constructor=g,g.prototype.__class__=g,g.__cache__={},i.Graphviz=g,g.prototype.version=g.prototype.version=function(){return ue(Mr())},g.prototype.lastError=g.prototype.lastError=function(){return ue(Dr())},g.prototype.createFile=g.prototype.createFile=function(e,r){var t=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"?r=r.ptr:r=le(r),Xr(t,e,r)},g.prototype.layout=g.prototype.layout=function(e,r,t){var n=this.ptr;return F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"?r=r.ptr:r=le(r),t&&typeof t=="object"?t=t.ptr:t=le(t),ue(Lr(n,e,r,t))},g.prototype.acyclic=g.prototype.acyclic=function(e,r,t){var n=this.ptr;return F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"&&(r=r.ptr),t&&typeof t=="object"&&(t=t.ptr),!!Br(n,e,r,t)},g.prototype.tred=g.prototype.tred=function(e,r,t){var n=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"&&(r=r.ptr),t&&typeof t=="object"&&(t=t.ptr),Rr(n,e,r,t)},g.prototype.unflatten=g.prototype.unflatten=function(e,r,t,n){var a=this.ptr;return F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),r&&typeof r=="object"&&(r=r.ptr),t&&typeof t=="object"&&(t=t.ptr),n&&typeof n=="object"&&(n=n.ptr),ue(kr(a,e,r,t,n))},g.prototype.get_layout_result=g.prototype.get_layout_result=function(){var e=this.ptr;return ue(Gr(e))},g.prototype.set_layout_result=g.prototype.set_layout_result=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),Fr(r,e)},Object.defineProperty(g.prototype,"layout_result",{get:g.prototype.get_layout_result,set:g.prototype.set_layout_result}),g.prototype.get_acyclic_outFile=g.prototype.get_acyclic_outFile=function(){var e=this.ptr;return ue(Or(e))},g.prototype.set_acyclic_outFile=g.prototype.set_acyclic_outFile=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),xr(r,e)},Object.defineProperty(g.prototype,"acyclic_outFile",{get:g.prototype.get_acyclic_outFile,set:g.prototype.set_acyclic_outFile}),g.prototype.get_acyclic_num_rev=g.prototype.get_acyclic_num_rev=function(){var e=this.ptr;return Ur(e)},g.prototype.set_acyclic_num_rev=g.prototype.set_acyclic_num_rev=function(e){var r=this.ptr;e&&typeof e=="object"&&(e=e.ptr),Nr(r,e)},Object.defineProperty(g.prototype,"acyclic_num_rev",{get:g.prototype.get_acyclic_num_rev,set:g.prototype.set_acyclic_num_rev}),g.prototype.get_tred_out=g.prototype.get_tred_out=function(){var e=this.ptr;return ue(Vr(e))},g.prototype.set_tred_out=g.prototype.set_tred_out=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),Kr(r,e)},Object.defineProperty(g.prototype,"tred_out",{get:g.prototype.get_tred_out,set:g.prototype.set_tred_out}),g.prototype.get_tred_err=g.prototype.get_tred_err=function(){var e=this.ptr;return ue(Cr(e))},g.prototype.set_tred_err=g.prototype.set_tred_err=function(e){var r=this.ptr;F.prepare(),e&&typeof e=="object"?e=e.ptr:e=le(e),Ir(r,e)},Object.defineProperty(g.prototype,"tred_err",{get:g.prototype.get_tred_err,set:g.prototype.set_tred_err}),g.prototype.__destroy__=g.prototype.__destroy__=function(){var e=this.ptr;Zr(e)},d=_,d}})(),Fo=Go,Oo='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"';function xo(p){let l=p.length,d=[],i=0,f=0,m=-1;for(let _=0;_<l;_++){let h=Oo.indexOf(p[_]);if(h!==-1)if(m<0)m=h;else{m+=h*91,i|=m<<f,f+=(m&8191)>88?13:14;do d.push(i&255),i>>=8,f-=8;while(f>7);m=-1}}return m>-1&&d.push((i|m<<f)&255),new Uint8Array(d)}var Uo='v7#aSXal?Dk_E:R9nY4y_2i"i;~[]K^kgo:$AFCU{J9IkELi.B"R@=S|<$rj6@;VSL8/Umet8FXV"yuga|s:|JQV!;h,P5abpC[?a*=<u$ZXlD4Lbrxa[Ib/?jYqI)1M;VFbX[JB</82&UrU<zgYYsy3h:`N&O)!;Lg^4#0uD(d1xmoCzK{p?Iyq8+QXYsA1iULN(p:x=#}lUp7wIjp=ug$1OZg_BkIqb_Ax~?]rx^Rk?6{zbKC_4mJ@xg15uTA%Ize/jhEV(!..*!"Jw>c1nj7>l<jb^+#ufsTpbVFn!E?>/~153Sz]>dVL?<tO&mW2lNDzMrphmz:q/S6`o]2>!}DIeTKcnk6:ic!fxa$*(?dlM`efH89dj.[DNfg@Jkl:rw>L6:bw6!JWh{k>e3k8:yB.5je`h89|9H/I(98ri{8]*80r!av]f,2h`5&hPSP}C4|s=%K|R3%>kwl?jD&UK5Aq~d@fD8c6G=2MD84&D9Q*PmG`X:$L9=r$<:(F|V+Rf8:vEe9gB8)ep$[]$9E24&~7*7w>]^1]6rtla.7/%mH}6sxrwkU`qs1MU1|gjoj*C[dy0&O_UTB%.zmd=2[eX>p2?1@KN{5zNpcjV+2&Jg>#P=|{)6kbAPBexKuEG7eTamP8Ae&~hS&vJnQrY,`!X*}{Te@xO|}7Fwu;0R"dSbJ]L2ZxN^EQ+LCrxY9/k37~>ZQ{!wwu`c:n^Xf0"7WBfA!_#9lfb)$ndH3Sr@y]]Ot&/f%!NZ|bWd$`CUXpyHA|_M4fFy9.s@.$H4Jh53nNY9ss~/mm_K;Ukd}n/9>8]TR{8dva1;b:5HFK7XfsuwP0;Ry1*1?8~/Oh<0{@.,%g*gV#XOt_ohd;}i@2O^s&aS2anY1/ss|@B<Ly;fHR#:x~/n?;cltzX|DVh.`8Z+wK98;+R.|oQ=]EbVZu[2B&nk#,Z=ibY%$7!hsTw&M3I+z:lsT@L@I[cY5%Aa:&0SvKd3]Mmm2@9B}Vh|k1mo(vTgRlbU,q4>n7w,=Um_vV7Z7u87=+oTRh<0I]B$5j&q>p2auqxoOMgKAaQ}Olg>kKP:<`^9EQ:ZI}D[i20<j}b2QSHV9QVFvR%f{5r"7AH<s?d[4w8O`i#Yp640f@hk;Um?$bCRw!$fWZ`75Yb)G+jerdNDVKF:d!1S<vls|>j],e;x0_`(?q:nC]f1/hT+8$0f0_)Twl3t$/d}*KOqJGikUZ#6$7?N"bf3o/?>6%]`4&nT2@{S9IH[!7/{82Me%26v/{Rb/$6fF(R2np{f,d_~IbXyvl|<=X*S<dTT!?@TMD2V0^Oy.dmhYuAk@UCrI5v<FQ)m|=p[JMebn3Z)?6<6dq6O1M}=Kq!nVp)_T.bR`jD{.rz.0fcRK#Q:h{^2@4asOlPpSpu`D.IDC8%]"Gd>SFZ[]`EV`EA=?|{qX1a6$l`j!36?,/%zx9]H>rC]GjYu?aUV)i+)|`&U$`]`1<hHV{%D"5T9Qb%wGm*o]!bbC9W^a!05T+?pjTm0=hpLIJLY,,2XBL`qy,&5W}Czl?q7VVG_tlZ	>tKnoq.0py*.],Ra9F$q^v*/cp3hHxp^KURMWc>G>6W*BUfd.BB<N_Y:/+UbjRj^*3!db.,mlYd`w@fGZXq>u9JFY_`kR^:f.1[p[]:Xk$qd@M;Xe(16Hr%KCoH)Oa3#.^<9]M).$ly<QL2qkzYIMD0+0;G}J$M(16D>t*}[&=pZi5=Gc[.nJ]ED/60?m27rK>KG7c4HE|m8K>{Zz3:ne)w7fI)a;Fc)7L&F}c51$z@i+aRz61SGGRBYvNwX_Mzq$zy)?8dq40&1|,%z1VA]OZ_Vln;dZ(64@qjom:mYr3r2j2FNN><}g.+2g6g1pd[=)Gu(o:U4:{+BV_#ZLy8s.Vd2jdT27cugf8&]KeOo,I@+yoSWy$UZ]P|jur"UcEpGOp02gIL:Ud&9Ae!ig8!@oTruE5x|xlT$_iC;iJH85|82e1,PNHN4wqFv_5%7Q*S=e|B8KLq9y@8G^<{_6:xm5t1{re:>Yb#8/6l#[eEF}=M@XOWw`Mr$PlyT]%5KC`h0}ofw_fSt$`8#:dY&Fke;3BN9.z;OIS]Pt%L4@%`6t_;srz2nqD)oSsh_|8m+)25tvYHSR}IZ"033JF8/5t#QeN7]w&!}VPM&x2B%s{O<D1~OiI9NIG7&2b/k9i#QBOiEJg{5zS3NOyI]ZwJr,InxL(okA~<n*4"yQNU:.<bGcet%zY_`,c1(H[5c1(oeVD0kXgi?Bn$xr0i#I46;bgyik953a?!p{;%>Nw7e{q(M(=]S{rqMzKa~krYTcydVeTPq7.L;`2;p0vGGg/#QvBiW?!h2(4+7!JL@M85WhpDG*:XJ3M%JuMNDt_A}]Ct%[!f{1Q}t]mDVyT|W2h1U_j,FJ;5&gIl>$5p7K:*SP"%~8aa8kvu;|dP.flC~l=|I:yM:xG5&0C&=%23%$MyuW2xJAhe"ne^[Hy#/_azlDKq!Gv0?)/}7yl<)4cFQP{UZt;Btx,Fw?fVC0#<T:B%6+sZ&xe<z5ryA.fmYd37NoGQ[`6gvYmwq3,7L3]v&l&NZxseNBH2o$SdnST*<yc3}Jop!1T"Xh(b!4aOTVl^lz`/$tE[li0bzSZ3^F.zHa{Zv!1I2P[4S<dYmA.;Y^RqEk$5$gq?`<9Yl.q4%Y?5@^anqu:j)PKG(}]/}0e=Wo;d`f;nmkmmb6Q*c2#rmhffav:+Kz<b$5tzr|+hJb47GdeH8R4Xp6%~ddOfb];(g$]_0!`w&#&#]<zaGZxpf|N:1OZ_mh8mb54O[GFzKMrYE8$YF0xu%QqqNJ}<9?ahlf}VgqG=yP[X}Ah[ZR~ND%5jpl4n%okyT"Xg6q3ed8+`NWyle6%t,]lkyRbYpHSw%>&MeB8GSY9t0cmHN|otEc,}:@6S~<uay)6KgdVxrYm#NcY=cznjlk1*/*5oKQ!?8Rr}ozKc%yk/8j+;)|<@<c]tmhzzph1_M0!_1dlV?F_;V>84rjj1nGQ252Tx)dZ%)F{uMn8o!muNl7x*.dqsVkNnyFoH)`INu,8vnj@X>VK/e=WNKr4eVPp8:[Z&2C4DS(]M;>1p[jTx~R3o9@iYkpmQ7n.5K,&;;$5PZN+td~qx<+&6eR(#:k@"`V%Xvx~CI/EeVkSV8i{[8f@u6F{08npL?@UY92?5bpy&LhleHRJ{#yjbP0L_Mfy(|Wyzp36W,bYE?*fP]Ybj7w#)Ck<IR.yVYbG`4|GKnQqN],&5z]n/"O(k$G?MO{U3<oDQu0{{8S&|GKn}+vU496~U@N.Nz[0$qSFn2,SM/E&?Zf3u.=qk5zHLOnDeUg:zTD@~T^lWr"p,w(.{=l2cY]SXk~mT*ch]Zr[B2mvJNU:"^NMA;ljkB2,d@AX[ZBJ@5U;lj"^76(2JcQ`0mX>clX>&>0.F7G/nRr6!t}ov})aTPX0dZfy:SOp`=HUGn~4`nYZ&PiSbqI{.&>|x$JcF{yuIqpD$LbzO|pyhfD.sI^<7{7ENu3jKWL*I74Tylg1i0P7cnMlvu.qqw){01h~(@*=*k<^3Bo?t@OG!_L9=,Ky8K{:tYPZ`<o,b/.LiPJY<xy|EQPWqAFMf@^7G5f:6M7ZMJ"kOb{]#Gp1C_x|&cYmKW%zR[/yBqd>x1zT/<Q]_r+a!?wsNSbz{Y0M?q2K)#)^uVEJXq0@"`:nT&ioVr4*.xP<TnEN3[rx{XB%KL_dj.Y:k{j.z^^e,Eg9&Yv$Dee}F3yoTNfd:R)`%)"7d?TGJ}k$LeCW[jT`ZU"8]Im&[I83NM/L29`UG7bxVPFn;U>N`]}z<$PO]ipS#vkX<3}a_XEz9Xg@$9x6v?/nEg[)>%iGn0nG*s)]s.G@]iuMp#?ZU_L%4h$n9:_mJ]42=0xs]^j.&%(mtlgd25c:g<2@q{O|7^J$`si7]y~3{=F`<UeVXp<{B5"[J.WTV7XTIVi{#2z]I9<q9apyDy"_~3f(w3:n$r4eu|/r#f@pLil|oyk./B|ntjz}am&Db^uC>us;:vF(d1;7|}D7q[2VDj;H@5F&[iz=1$_Mb_Vgt*i^iTgh`M5qd>KU=|1d^#f=4`K>uNUoea/6l#BLo%VX]C^!Tv6X]ChauT@)uN*oiYWb)e5SKUhBxirnpRk?16EQ#2A]?kUo[x;R.^4t70I]Zkj2TtMxF#8,>1Qs$zb3E%YmqG{*8zE5X1UmkybT(1@kt&t|M:j.1S7]B^WW?{9DN8zJkyjbazl>2X<srzRmP_c+0@CaoF!jA{o@|bUl2?eU4&ty.t|od0{/c{uO]rV$jQaGin[+N.r}L=9aRnlSJ$u302h0bz^4ZQA|Z>z%;j#19VMJM*+b&rL=dq]{x1]1H3St:$@KY2nSKU@Y>#3q{Hf@qs5QI^GrV>{_,^v&.n_J7GQ*b!IS!94lYX1MUoWaibB%Xsx<vvyr~0y^#a<${5XUS5od1Mm&c>4?xlTe.xfN*7]Yu"{Y[Tl6rP6D?Zr67!tL+H?!%*RjqAFfGXOIbr2RvLfElj??e<+yKxRhc}:I2+6YjJO2:ln+!Y^Jma<1/y;C1sOmIfTX1T=#_3pZq6"e$Avx>af;Tj0zsDx4kEdjkXsOmi98m+uu)Y1pUjrX%PBB3R7"iM#0xhSCVj0X6UiwuJBm+ylEY^eeVj5XQSEg4R=LtKf>kEej(X`T(z5!bzGF>[88s+75KZqD<48<i/aY+H@!_0ig2X"Dq!O2j/BB9QPTSJ+yTa/3LT1p3kztGQRz98bjNXASl398l+oullQ8ZLg9vLYJ%|NJk/LB=gx`!<k/LvTJ|DR2l4nCsq$co;3EU/XBcjTPU9$k7t^I,p2egSe?VvQSf,bvgS<WkFW6J.U/jB7Wy6h#>W<FF92xtg8AlDlv1xV/vBrmwo#<p4WZhGVNP2q4[Eva/*4k>t,NfrcS{W@JX5HbX/;Bje$c8ft4<DQ!;PY/~B@nJ=cSBXFKP[n;5k,X!HyhaC6k1)|K+Ge#EX;Iw<=8Z/WC~N<U/>r41E|b_06kPB!Kx;0EbQ>WwJ`=g^a/7vfV(jbC7kYuaUl5,0PtNvNLap0kf4TANHG!GOMt]AOOdZ4WrA?PZlvWuAcIaScLYAmE8<ZLuAXCNp7yZ"IC=h=x/.NtRB>m5FeVzA#CZR~PYLcADF|Lb4dA"ID8.o0WNB2Ky:Et;*AP}}XLgAfJ*BYLhAnJW!NcRAMDz|/`Ft*B:yMcSAYDF%7yRAcDVRZ4mAtHD35Fe",By6wwOt[B:OxdGt~BQ$5FXA@D|5Zd}L>yk,ZUbpK16nLy%eKs3e7nPy1i]Smg4)/Uy0K18nxEk/ya=$hvGfk!3e!n=E{^m{mgI@8KC?bP>$vvzowYQV{nOFzsj3oILt<DhVoYiNu1Lt]BTfN/VA_DdhXLy&E0OtPCSma]0(LBptBEptFE|(LB?E*B<OWmVxHt}eF!c"REptdEWip`~;ZtqI9(WBAF[t+*;Bb,u2ZI;hsXkD7?ZE(?,7Ktr1dtGF]"KFy]GON~(^ww{GB5$gqysFlC5J{iD,H<m<m<=n/thGYY#Hh/pdsigc^?YDv;We/h8A8F(|Xr~eNYKGrY7D^d@$`XY"|CtxrP]h%ApGlR};<FtYAE&gj|{)Zwy""I*Bwx$IH,(nDYMGNuoQR(tBPc[/VRF*xLkCwTY@+5O<JOb*`JBG]vMX<I@<,7}vNXVFK.RVPcB5v+|)1L7x"KvzJO@L.C#W5dYRJtuc^vnM+E|(iM}CJYzPa4u2^K+CVtfTv7+xATpnAu(FXDNc=xq1C?QCsRRlxwg4tBptSF7jtpScSBQJS6q1bt%EO!:i_94"=E@AB<[h@AOHrfYjsiUt<DLrUE^h^AmHGeq1Wt@DG:i6:hAjIBMg5*:hXBNK9%ww4(yC+QFBK73tZEy]hN;hIB7R{O^95"aFnZLv|9g",C::hN/hRBBGV%A^3(1BOaA$/h=A[GN%{x&T_>5tuvXTD%*[D5dcWxLUm0j|x?;FAH5u)Mk.+[})#4]I|od`}Lj7iueOGa)nG<CiJuTP&d08Hh[F^u7UY!X7~)WGCvYQ{i)0t?MwxXWK,er`;,BDA*?LDF5o*5ti|y>NqD:4c<z+DN:DZbqNSjHXeK7i`J"L]FKZ_IK.iq~X;L!E:lB*w+")=L>Ejq??qN8!zLUF:46lk+quKmxJ3RWzMD4zOmn+&udd$YUjpX[P_wf<@yFEO[f;UjxXrR3X4R<L4J%jR$2kytjJ+GR2p4*wRPx`d#]4/En7XLWAeE}4vW1ApRJ`vW(A/R|LwWSBzT2!wWXBYUvtZ4Ao5E=!}wxW|AbF%P8M:hP)`B%PS6p1*("AddE)p1NtVEVVgoVx[h6AUF~1),2(kC3K7zs1&W*E&e~1};146uuOD?+e8;xnjC$j?frieVaAsBpN2]SVsitL`,n4BBa&vD[vUBLO&mqT9KN!+4gX/>jP22+G<ge<MUBJ[*BXqt*}:o~;}Q[1[*DXmt?adS~;BV=F[*_[O<Mg"KY.dBaBE2/k_9>[*RNkUuawUTK0|e;mxN{BzC+Mu4BXI*qoz:zPQSsYi!_4uii5%!WhLnSMd.xD{F{v7[^9b9SFI([dSO.$Y>q;b*3<~Y|miZ`EtnO6ueO^ywqQ@#Qy!UF+"0v5_mjO&4ic8#2:ec^PTHKhkyxr7DmUyC_PF$nf#1:d0Du<8fB?*|LdAkT10*M$A.RxLio,|BC%|RWX>Hso{dMjP7n?8Aj8[e$LA9(F20q^.&m*R>OdB8Kg[<ruzHH4{<zec64aw7$,ROtQE`Aqj&S>9@i8Xs({B8Ae&pk7O_doM9vlS5xV}N~7Kj:F&6Cp9UU6uKC9kyd0H6yO+waYIri7NAXDwPOvN8aizSH"pAi!ZjXdG8iYvO4]VPdY8?_ISZ/cos3.*c2.yRT+Z&aO<aCd)jW5;;gP&wV_+V<a7ijc%pRFliTcaOAS0@J1ouR>ju:]zXnB{E`Wt*0$%KP^ggL<%n.lGh]0~P8BkAi8)uk<WBT|T^*H3[~%PX?KB2Q3Cj753L*qQ%o$:W$d+:_v+jwldx.wG;]>Kx8H+Lv<lU:Uxq*;DggkdU{j5X8k[IJeyRX8ahkQ$4S<!rW+~7_!4p^%$TpXn`/v/KSe~NT3;opq&BPPp,cPB#NXGdj"NBj2(#y,}7:j!@Z5)im+WIB8j*|./YcEs8z_]=_+*F^IlxisE{"gjLaqL8K|u4js7$S_MXa|Um8f}.8^zhq[K83$+KOrQgXa%Dv0MINp{eeE`]wjmFSxtq!*4/FkQ3GAa|5VXA:{xrQoS2$+Y7iUn*Jyj&|If]o,3jg7UZILD(%C[K/%`{x|FEg$Ky08MjZ#77kdj4E"OKqu:D`|tp{RufOR6$mLD8yQ|fpGPvwH*7mX,(@Aaf8k,CDvIM!8N|T3"UP0Ue$1Z<&%TbDH]f<!Tg1p_&i8K<NcW1:o;$2iQ3lC(K>rkMNNdZDn7_pMS6:gK3:rb8A1"HAvz3>,JrC8K|JPW1Z4rEw@D8,%Lg8^C(|AQK|f8cO1mey_);XBa1J82(z9"!Djy8J?!(M8[CA1xW5FjHa[yqkhV^PdsTDgd/B8+S~lZ8OR0RKNO*Kjgg>TN(/UrGI3k`hGxn0|.#V$`4#"S_C`yrv?17oBF,IL@],R/ikpVR)V@V5;oif9<hRsIKvX1P`)F!DI6l`w5jc4`>O;+_JpV@(&^b73P^;Vs[Oyg1>TK?uuJW?SK&^biPz$t`);4^n8Dn@4DF"g>ry!58^!1$SnzPk_Qr*]dUK|3l@},?B;S9Rr0%]HxP5{L;0*`_472^[wyh1wSgZ!KioQp,x,ta!#>=uL|7c/dUK|uq73+>`P]!sd6|fH*=6%M(xC&o?^$7c{Hp[HMLyI<.:`O}H_Od>L%AT]yr{bDe"@/K%=Ae<&U!78K`Z5s$XaZ?%UK|E]hujH}z3.C`45)W7b~Pksb>c=YW7LA_7}h$>^F4]^1y5DJEFP<`0W=tsl8B]W9II7XT[gEDtj/Tx.mwX68iU*E:`hU*bd`hU*Y:_hU*#5^"V:mwKxfB8NC8f"EtD=.0&JXTC9Ac62GW^G8N/XhJXT|u7NdbkYgRE8W3)7MyV7*K`z/G3clw1v.GPy{oFr0Uk2FZ#}ZBTY(<*NjfMU#2_8ow6.gr1HcR&W]l_:0H:nOf|uIOf36bzc]"IO8NcKQ3&SEH1/}o}6Ap^zkD/[d[;76dU/lwQfViu1r@KO2<r4JpXxK5B]~5l:Vcw@;;+6IHOFH3:SF0sDxbVbP#^8ugG36UeUL{SY+Bi4D,2N1f][!23#7NcKQ3q2Vp|N][d[lwkeI={qMy#dr@9Vulk:LpJ;{q=3L5SY+b{(z/%{ymrVW*K5]P`+grU]7;wc<<Ye6%>(.kPJ^DkJmyY57^BxkXkgdc=+EDC/7HP{ovV9P75<$y#7E>N_X+w7A;Mw1vOF&3t)>*pcbnOH+ZZl5V;$6gCna`aYoGdJrk3ex+kgdcY=`5lvt]]g5UgL)SojMZ$nVOG;Y=4nTYPFi?Fhs!Ub<wI}G9mn,"[N_X?za9J+=zgaKr<e+?y+#h8O}u;Gn28S|;4Vx(}]f)v!zJWw<])eBhJ.O/SYmpcxG5?DqldT|>(z|(}eUs}Voq<tq=hn9k8S&6c^+"$pQSf"?*gD+Qj:7p]uz[@V2n@+>w,6[&:u.g>c;i:!vLk(86l+W1Bnr5Oo;WGEu^_uDRj$o7.Joip&wTGWx[1K[EOwj<`Nt90KVI[,wH"kP@Z~VwQl_]S?<2j)ps5_S&0Nd(EvMVLl]8!7sI{Xy=vyk.]265X;{Mc{5?u6=eTG65c`7t>G7.q9Io}d:5[pT=.Hd8#$BRPOx=:#a^F1zK^6/pAcfS<i,Ki*8Sn5"7VXR?E3Fb>$#f~QOpf%!tkASg$E<z/D8QzK>Z$y),QT"p.JlOi]Ir})SiM<q7vHQfhM95v>Gb3[#9w<.U,>u?W>.z:wxpUZwW)6%pFG*4~c&LIHafYPb{]$#p:/#pE#9Vh,P*@+%(%jO2m6XSp1uWRn#6q|315}Gqnn*t3}TtAJT,%VIkL102.P{kz!^ddo:DC8zVm*x"t5eX,@"mB.}7Q^2`DZPQIR:lT$9N~^0!hc~qPC;[4$z>cCfX23k>;zk4F{O2*(GA>ic$$<u0![v&Ek{>=MXLy?JY"n/Kl>mHnm&qH+JqHSv6xyGP0M6JXLz)yjbzmCJV~#6Jh/TZjgR$0m2+?8NgxL*fOgd"TJb$1)#[L=Txx4nmYJfDON?p!k$#00I&oOj<}?rvUbk9x{b7L(!+f_8@i+PqE{1B]?WhXE?),G>q}+GQFI2^ICj]hfA|j+72a{BWWEJ;S~5B_>0[MC.^hSW2c}UQT]0mL=2$Cr0iObQFbM<g4lh>u]g_a>g<w=UTt@A=))X$I5AgLZ@Sf`8u/N*=dA6:c1!+R]ipvh_)@H><o;F!4}x1)*Bz)+s>**@zBFf^rzYPJ%0kQlQmpHQC#4;b;tZibqII}h#r$jzWOgl^xv742X*:tmE(Kw9pO:`krFs/r7gqk/o7zfTfQD&H(g?#9b:rTHt@Ar8o5j>6`n^N9U#4bp"[iOE#(*83q]K{h9QK"[4@LuA@e_<sYmS!ibR!"@4mVT_*ljT6O#L:6vqlz<F],PE{Ah7Y`gyTD42.gay3BOG$NgZUGBz^t]_+rvFQEQ[(BffFL?M8Q2khq},|r$MVGi0v<w(KXnRt,P|y[YC[7bkB,T+MW+vv}nY:`7wii$oa~+,8!ch]p^@C=%*S(}D/X]Q"5`:7{p42V8I#*jjoaUtnWuSHA+5p.]7%&LYII|7~V|jUF+I8j*R<*c91+Ud[$.`#H}%N"G(=>?W8u0o,IeB8`37erip9~c:!l/_f+jwV_YVYHSmZ"bO5!wQ{<naY=<dY>=E/O5Wk1wlX,T!BSHH2?%9s2Kh.`C8o=^2.~g(.dUuL0M.NNV5rAw">Ol8Hor?CU3}RN_X6YN`*HZG&vZd=r^(ons7U+o<m&bA;R[51wihZyk0M63"L+52^P3rwlH8~Y>Yz*DdcA#}+O4{S3+z9Y]c(qKITQ#,qcPP~)i*Ll>q;_)jovi!%smwOb3F*jUBr#_yZ&lqi(2==*Idb_Zr]gM_e;e2.{U[T*g[$=&^Ze@XB(&ZvaX^99>v*I%71vxF$$uksw=5@&xu!X9$Ex!|9mark^8|.2}&ojm>d"G(<0yLPmYR<8H(<sKY`$ERznBeJ&e#I~O~njm{V^]iO.znOxWD!UPN*=$xf"*iTV#(,PvM8UwK/;<j1.sS6O#nJ}}dE(iSj693W%.a/0<JiJgn?aFW7]6deS}&_i*V}&_MEi0rS>)1K(/G`(]?)~O?KEl9$f})77K0<1KD]l7=aETI6~q(>mykVT9TH%X5qzBX?Wp47>YCppZ/EvzR%Q:%GhO,?ScbVZBvt8;%Jag+PLz=X_36U2={H|R2Ngz8na&^[xBwb!M^cQM7>TM9V]c4tYE;M6`D1XEB{3Bz&JGr7_k_]VVaY3m]<G1&b>/RZZErqUiee<RF<A;1R4i2ta{Y>hj[a*HNegh0MtO]&`=V!+lk+1mXC5@#{FSW2+S/)MsMUzVHRbV=f//}[}1@b.Q(y!kOZ1Xf~!C41CJQ8J{dHlLQ,0]Pl|<BV|%8tyG[oTu6Oa(qjA,LxjHmq[pfJLoM?)GN49%d0@#^<GW|%hZ,ZtB`*J@xgoL~6/nt9teDV$mEq]{{Mt[6Z8GLb`<#6f307]W#9jzJgmfy]B&jEg(J~9||WyG,{},QVeNE|lcPo@y)+Dr;%5R"Jd(ad|>1r82k(@nx:*eva@iq/0;_2[U75/{lj0kY,o@/ni876Hpyn+36yl9y5#kU;Fkp]t;)D@T|bo5)US}w*YTkTX3;m?lk+CJl3HJLY^DaR+Mxy9L}DyJtn)ijqF%qptpBGS4n^?Bqla$I{&.a1x<@gU*]Td).|yN8mE8e%b:X[E#~Y",f:&H7&iU3V*A]a%T*!mr^!NwGMA]vS@]_^~9xU]it_od9e*lt[E=)coYNFEil32<!_w^p"KkwNP+uYv{eO(i$SOu;9vzw.R%jtKn$eo_LQ&YmY]KvYN0!HKV9nb#ycJ*EDwk<wEoz+T`k[tm9Zb2iWn]Z+1S?MiW2ltvj1]4A@E{ZtK/P$++CgPsF1F$:_YsS<HzDNg?|wV.d6*Z3i[..a_uuk0CFY=JdF[kbDgVOSp]Gaoqd@lUVfwWb//YR/M13QDq(~c^D[ib++@6dSk"R>v5SNO@6/F0h[1CFYDn0Cl#05SNn`W^}1[tA~FcTYH&:Nq6)![lJj0CQ>tQTS1P7qErzL3ChDh<Q0q3?:.o[X3UNM29![)?IXla[V~%m]D,uQ,5S""$V+?H^:brt6m4W+8~%GkSi[4xy/Q+bKT?1>A2Khg/NFT^#Hg^MjN,/GTUx@Pa]lr[kP`|bKqY&SK%jtP$ADijB+%7[!sGXj&R3{~p!O*A/n,"g)ED(7<TkQ_lgZ&u<:<INX:b&S_z|L/zjSz>}5q)B5tTUbN*s5)sO@abRuf:|}eajyG5r3G5@wD>.1!BDINI3"eDK/+r;1DL^<Stl3r4M#uVn"kKs4M#]|uhzgw,%.J[14~crJ<zNm+Hs9vz[]w#)CXD&BV<:FPYyrF$Vi|vD$,u}h6y)SnDZX(5Y84bbml4^?@IZB{$c](H0C$cQp+toDXlzb|bG,Dxv?%VeU.8P<Ys83r}T&,WFNF3Ir8]VKB2Eh;)ih.t~B2,2`8_g#TR&P|[9f;F{LpEDQOwo(zBz&?*#Czac.xt&{+k[&{o/fxWG1KzR]M{(ZqVx4@6U$]TN#54h.a^l8Z/2_O?B&Xsd9"4Cws,q;ezJo1Y_%=%eD)+`=>8l^8(*NjNQDA]r;#Ood{<0hD)UwL*i_LQ_M!Og>?#_|V},Q8p|"81Qt;Y)n_B@/oI<YxyQoDKNEGDPwVQ+0.rqFqU&Y=6<HqU&Y;0:*zv:f]Z;xLyAok+UJ<wc>ZxS0uJY,d5+nhpc`]6n2[0Q`C~vvC{Nq"z?cc6121P&:a8wxjkTHe~bCU5gVkXQZP=|bUoWnpjZU(5aG{Q5xI]"duIw4~|dl]ldxNMIz+Kt_e.!g{_$&Rx"^FL:Lw*OVKw(<*MMi*fPV#57e{qIzKWmZ[k3@nlU`d$|i*7LooZ1YW@h,ZpB>M^Br;qTT:{jy+5"CfZ0MHSy=Q*rfQ@EY#c]k*GH0oGVXPDL*~F^j@i_QIMzumzb]FZ#=(W(9y#bjg1ZuAh|zGxLVt9SzFzPEp.SYcWPvay#v7V)?"WMEEuf!(O*#IoL8#ym8hxGYvj&Mg`FHGGDGq7)Lu^B&fw_kh;/2"MxG>iS{JkEUzuEIq7Y=s2n7Kn.kF%mD(=_dM*BUK(&Uzu^c{_nt0tMlfSbm^>2XUp4nE5NSL#_p<cqK$7~|~=zxwA]%:NjZ6B&62._EcJsHmY@s;Ed&vO<wth1X_3uq^u}$A*,{>%7]$p1wRxr51+zPmLQ,e00>pu.FRXi}})%erL^s,@GYh~L[y2=O==w%)M@]*lXbc9g4$ml;WFyfqG"%cNbYq/prx@P3}B_3AJVU96z99OpS28Xj:XyGt8Qqrpk8]YO5b3"B42KgH~o3Y,e%ju!wA7{o18io|feT9L]Z|jM5kV_p/q:/eN6,i%G~A;0!tcF6;VEX:g1_e^wgeAPGaEkZO@k6lH2q|5t5#i%d)PDyI/rsw:GPI:Z+0k@NuT[v>ENtZ.5wd`cT{*h>_y[O3prtU$hqQ#cRH4@$vg1YRzVObVKJg^VVQG2sX#Cj{je`hp!)(#crBcLgm7d8Wp3q`=o]~>a.*54MrG9P6YFQf((znlUV)KX~R~>H7dlYt54m3p5Jm/_iu,ZM/|)eGFn;%%]pwcn*=Ug%39t~a+Im4PfNf@UgBxHd<+B/}?U`8N2|dv~AZRmtP.g`*kg|E;#XjeS1{m$pEIXM)m3O%kh;z?W[91EZ_o,tOC|9o;>B,[x36sanWsA`Z_J]5<g.f.K}w<<^cT[U(Mz^B#/<YVj`,3J1Kvn^AD~]$Dk`0KO%3uO">iQFNX`;S9Zu2xU$$)8hB#"bs2yuh|61$@zBQcZdHg}%*np:F]9QiLxyoZs#()[KlrnuC5l?4LO&=L[B[Kiua>yr^_3yi1P<aWAzPD,IHYo8$*D8+Iu^Tf!*Cy?(1v+j|"1|Iv(<ebOl1uxepEXI8:(j7m&8x]q$NbDWsb3]a^Q=;ya>Z{2cT_3>i}k=MYe{OVu88+_%r|/KkJ5EuQqGEjy~+6`wOkI#s$x,V#s$)Onl&;@S[_f;b.VV<SfKcfO54cC8*MwZw)NimzO0J|TPk=.E#&JC+Y_8;CYr4Uj;_3Q#)LfCq*5],s`UP/hKy1R~n#b5#/U={UP/Bp,"JCdG5u_;n2D!S1gZ[]*Fem2gxg?f:8rp#oTwu=p^GAKe@?4sb2jluFk),gTLxM1#VG7V+I&N/EM9MzLdwxGSpL0qoB5+&r)soMIJ#:yXL>8:mI@n:c+Ksl$7eDI9i#ID}`Zl1)JFfB`44ZPTCxz*.o?0fE$4X_zQNs;zjR/K86OTWM0JK=.(i"."tXEiFRV|)Y9x15v_5C#ma)](erGxn{|%4g3~JX7HhG3,h]HM;:t,mmgRX3/Z:6fNb$829=)@ws%mU1S%PXjdPk(F{1UXRD4v.y61yuj!k^,3krW$U/xqYk$<SG[tPTzuY6m5V;ya[p!Y15ta<Ifv+?7)(g=3e=Q=w+(ec*bsQv5pt$o]ZTlyCbV)C2@SoB|oO*a)(gwlZXz4XG|o=gS@Z1oSHon(*/<@i:y+c*e(:,;r~;{+gq72(BR#V8+^Lv^FZ:{Pr~z|`9y95TB7zMk@VWYl!O6}O45J,VhT7Gr|x2w=yJZVHT__#sjCmg/|euRBb1mWdT$QgYz*G>Wd,i&61cPDQZHOc#PTVx(/8F.LPjuQ9;Mj&Y=8;*t^b!/znDSe(GDU9w<[V#89krbz5jqGr22?Q,gq^Zzpfb7%5Ku7/l3v"bjdr!{p.U^7Gw,V9z%{R*>5i)hh?t<L<ncPWEb?{"_oN^lbc%QnqZ&0W{{3c^`=hYz0p7z1rekgbAgZ)E~^v&P?YSK0EU`=zbN4|}D7Uve&)m59<Vepg&cX8@2IV$Q}`yT=A8Nw[(Sh},s.QS3Jh|<m{Mo{1oY~CxZ;0+CtPTH{EO6U!;I8~J87]IE>j~bYW`*ztvi|DK*&MIPWEr13)6ACV{~@Es:Q*?rS3J>s9,g;6v=2w&1rd`c`@.`Nnv,oKSyr//B>G}`<dV=HweA/J(0A4L})w7^K:?e@AV#/,^|j[?zBZQXy1*EDSe45!!lP^.0S(O;O;@{|Tm+@q&8a]_8^a^E|Fnb22mgnjw1yaYT+5&eF5<77%7V+OlQ{nh,Mg4sBZb7eylS=Bjd#vkWygwy=GRcP|%6Lc]vBJ6Dp.tI_]nDMBq0QI%2|t=mxJ*7+sgaVHS49j>"GaG/,yGo6KCiPau5q`^^B7_`5U0AY2_7w0ST]hu2gC!;o!9n*_q/XA%9oUlT=rh%S4?Tpf.&mK|UY,/!Cw_6Kok0pFyvV.c$1jF~<Ybh.Hx(SEr"b"<~<XBN.ps@U!<]!x*hF.{R#q[M,zm?q!?f5OT?e+iRByvKab!"LFgR!/_e%C?gj">y=ruXKq:QvyGn0XM1MV$+6egS@I&hwCthhFQI%16VI.r>CgG!9m1jZlW64/M15|(8~`=TYZ>oc?8iLDr6X?[j<cOI^;k|yEsA_2!ok_d;fi&/,=8Hp&Y[b#Huc_[,BxI[HJrWlqXaY`+2~%*KFR*$V:yx;@Y:#"T`{[T]<+)(7py0Tz~X17m&I^<NfU.>.e0R=Qh*$kbJc:]LN#9ui5T0rTBo&O+Hx_3_/Qvcpt<f8{WNn8aKL]R[21ttSd5WM]KSC$etP#2&NLmFoB..]0,OaSHC:sPn9^@QjDH9k{yr]??_SGN6:m2%XG;!uw+h_+e%E9[%S365BeJc:M^y+dQgTE_M>W[T<n.D)Z`.)d!s`VRjF|qb#9rR)4.6Kh<`x:]<.nV,A5{PO90?O(`XD3f?jV(Vh_Jd]D:g^h3^MJ~f^uo`|.znO~4PVovF7J09.5^IwmQnx$+QZT5uZ}p*)g/M:z}9MezE18>[>qUMSMi"o8txN<f>q8+7F]IvUcyu"T,8U1_f;zyRL0T(uEAXlwIV$DzM&Qq8hSHhcib!LYFT|(jN:v6HQ.~,^j#d;5e,Ib7eX)mM>~o$zL($@GYlsp*/rmhXy%lL?yMJ{CDM*,)R8[jUr,d2Qu_W/:ROb)au`17#3p0l8=Z=HCW},bYC`)S+P}yFe1f+vi/d7]6T+U6o#K8NAz&;x*7Cr&c1|Czu:8O3ut{.$7qL721=yUCH`%FHhMrXzHPb]#Q.L"O<@1q|yu%PN0ai{L`0#ml5C@5U7;+[m%:[xG}W2ASqL_MAnzji$H`Jg+LV}Eb^2=8Ym"O!PY]kqFQwra{Q:,ED2iGiS)kY,Vd?/GOi{GMhuX,~IL;.zoYHxp_r]l5nuEd8X=Z;T&./Y(5X+XiUH*!3kW(7PaJ%lF/1gJ:;by!Wwm/l;]+&XTb16SlKYzAf6JK$4;NV#@VBHKLL@MfuIAzt7vS!PiNE#n;k{@oqQ]#F`#Cu6+l#{t[k8TH#yh#)j%vBrK67etPa$ft_Oz!$vBr?R4j&[!VfP)HFIk*7pR#NyZmU/Q:[GoPcKnh22@WCNK:F:nsA+v6SB`4l4/mq&0$I7sa[[3Ld{^"l*:*z8}EUJ7?7HV2h9x*RFUJ<zY@/5|hi9$n_n*2+E9]w?AI!?iw19/S1(?6u90)n/QF1mBqJZ0gUH)+/x?8u1W:caA=1v7~3jE_(HI<g]7S0Yb7n)~#?pGa>KJqN=X?|>#&6;[Df@|la}bZ4B0Fjbzs%UDTlW2PW1`4SkITK#Z]_.uoIqNZcWc?z/ixf?7s,,cM[/tQPt+xw(D#<[q|]&k,cv>0X+,JmL1~x5o}Fca?ay5jHZYR4$@kn3J]NB=|]n|n<|/4WVxck3*S"Te/^bdE*50qi0$sRRS.BA"=r,!VlLr04.yzdVzvqlOt`y%yoTkU=}iy<`y68_ids,m%JfeHb4Z+o03+3+g&XQc/j2$3|7;`O<z2hq[pPTBzAMk`}qvb9c_CmYB8[L[*{Eq,yQH6ww<Js5[7NZWuRo"}d&!.]J=$32%c/iNkz:6oV9u..<r_$RaS,qB+$pExB?%I?iOhHw+K3`=8D1(vn=k2|>^xNl;YAhPFK~6E@4=7)E)+Qs)=v1W:B7B_QJKs`5l&W[J2mqK_L;ngr<Y*pm8vRxA~qQ26o8x|uYZ>m?.&SeE(g>:{fz1,2~z#n]Qg5~_g_MT$M?IE$d#CrGQl,ph8)+ibvT2ptIaFRkbc:dwV8Lo{;f>]:QC&NZxsR(K5k"_?Hz!{x}ibe(!7]IyoqhWy%l:g+5,R69?eY!/nj5=],,z+W"1M$52$m#+>z[A.h[P3b><VrhN|v|]<,%ox^._?K|DZbzG5"[Gp~EK]}mb2W!U/bgAe/FXN8KS+UmtL>I060LQHW,X16pJVxr6nX7r]7!WOR7NW("[Z8sW32X^BUAGu9HPv*fxi5lDCzn$Dz5zy=R2ov4gS1!`f)]7i&;6Wi:Lb0NY^XQS{N`?t&C(f9cr70Z^Zc7hQD>6qw88:x`;J2_)<9/2Ti]HuWqaOq:ZZLDbyC6$v(Si+m1;tbvTE[$<=/[5d]|G/yn#v`HpLI6A=^3W@O[W!.hP5Yj_.5:N{UY~;zERRG3>J`rYf+BV,](nX!5N,iAS?R6ttVh3mN`tQ6(?8qG?*iCGX3aKfyC;ggu,hrPPXx!5Y_U"?YCX@wT=>t8Crw?iAMl~,gSi{QX/[~|klPg9Mg]FW=|v1Rp`bZn~1UIN^qu=zDur#bwo@q:qyXI#!q8{8kMcpZ%e$Qnv]FGWwFn/}3wfhub9EMJP?CwdhAhi&H&A&B>ihS_"ZKNrX=gX6wq2N$H/09r&(cBxOlh>`X{Eb0;|={nr%@IE`>cdvT<LKs&M0zZPxHG^&2/>8~/aK0*;[qK]4S(li.!1KZX..dB%[c_JR$;B#nfNRs=aiMT)lFYPDZLdGUQq?pLcV,4aIyqG!bC`3N^;VkL5y0p|O1R>XVQ5yDlEpMPZ<x82a}VeY:tLlJW0iSR6?gV~//)N+#zy&3wK@*2Plf?<=i{WJNm?|c8bPUW[bvoAe9.`V>V>V>V>V>V>Vf]KQ}de:f=bS=?wv"=YRc<S@1!/@li;.}C7`e+X@n?s<dBG|dx9PK3VW*LgF&,4KTR45NVW]kHG%EFI_kH",y|a&4dO@L`/1SI8FW;z)kL}5=a]~XTy2`h{H(,db;ej&xpJ{{aK(fv>iGxKV#Efe@/9w=a]>ylt%qWND*#7:Gna73:Fz(7.%<G=oz!l+yc],p"fuPX317K+(RR)dO@(|KT<fn`h&_JwCZbTDuC0K*L~y^@liZO,||XYFYF;k_6E5lS4jxXI6QCyrU@3R4jli[PhEo6s>7bXFtq,Nm,h|31:!(!vq]*"b9O&|sND{K]/Baqo@>nyt:,Wj5vl+4KJKXd_ic_7yh+o0y|31wSPVR./zR:JGmCq~[*)?G@HBV>qmJ(]u`kCgXWYCL:[g>6QfqQ+?mL`$F`AFBYXp/?NmAMT@AMR@I`L?uN3`cLSzSI8lI{D^Q+.({oFQTce_LL#Qep[3}iW?|>Y`F/[hV2r+&f9)8`QI<!qXN_GmXkl&~Sg;aZ1#J:]6g:kafso*7+)pb?SfWy_<_I7>rraX49,&hoo6yyUn4mhf(e]tJysjWbh^RUp)z`nmfhMTs$ePfd*p@wL.I*&N"x0Wgq]0dKpsV`a+T@[g)E~a9|K1l+m`oQu:HZc1?Om?%qUII:HZaLcVlir[ub1Q%yh+.%s0+}vmRC"dxO?8fwOjxGI?&r%Q3*Z".LGU(.A?vp,B_6{6=Z>m=jwx<.h|,}]pYT%+s1t+loc<AYPZl~tC8C[YjzIN#I/vWCEv#XKI<VM$~$8_OjUF^YjRK%Vmp"6mhQ!,_G1B,{M1?6fiB*reB0vx9230*Mn0<,R%efLZ,MFCu4<PKYLVR(oZ~w0Qc[j3y|3V?F#a38;@I`*2fl7xc2.ugat]>coWs!c7w#G@BRV2aIHy}xwik@_)JIOPoSnP]IYgi77/XP1IGvC{O^{e}Ik;Vi|$S>~lV*LS80FJGyv=)YP?ih`RS}WUtxK$Qx19,MNUSP{}nf9N;O99ZdKOLA"7}DpZg0xr2Tu50,b_=Jpszr}()gNW/jMIQ~NmEo~gak+izZ/R}~z,HNpMOb:y2SUXrB(Ez&m5[3}QAj)a@{Kc[fp5dpXz:?W#&"wmm)}+pFo;3&|@i9oSMxD&4,YEvuNphw0N0`/$gR~CVD}5asjyx<ko#oX$Az@me3D`M65If5uCR`H^mE)FNvccv88(XpomkS/FOu?(<>3L{C7S?DbS%F6Y"ES@979/8uF%9w6k89]KulPyuthdh8b<HnO.]i;vNZNmNVDV;x<q6b3bsvy;/L=CS":CZVl~DgRa[nw;yz]7BR~L>?.1C2*r405Yz?u9JFb*.<K@(ge.C2$i>bNivF<!`V}<(5RRGiUp8gYRqNI+lBJt:CS"e+6?UR;l}6AMnM/thZWO(yY*$q8,QhBH>^iK:)^bCFYw1pz,K<R%i&?@QX;x+iXFF=,i<{h[5~x2L$0*gso0SV(E{pYhD`d8v6g37OhA{u0L#k!XYkTb!7I;isZx/R72*<fmPD0VD8zlfLB%0GW.~L3i4SBhC!n_=ZJuAr@E6y.+_d16(eRXnOD8vmp?NPT2Zf*)<^d7X37AzNbJg|6HVFbJLT@kZ5io$u|h,G$q%%Uo#nytw=cQ3sOK`2@A`I}4w|cfIIu?(}nj>nW%rSNs!_PRKau3Mj9}v1&GByPVUAK}/,<8}FS{#xrnE`<ojF(M^tPLSH;"aGq6bQ3BZL$ddQW,I%TX]CofAP?0?C<P)F<&P4vLUVxaZ[]tu2>q0p$esO]&>v]&D8o]HeT/E,1t=DA84{OfgxcVn9i1,S*gmS?ko[kXfweT)3O{du7V$p2VKMj`~peMT~OCDO7pr2>q(SrmAP#vBHF{/Xe.e~ulP0J!4]#2G=Mom_Oz&j?A*u;e|0J3!4j1s2qCPe,EY7#3TuE;#X7{T,^FBw2goJ:InO`O!XAM@)[&~XD5635<?)m+?U*kMX`GY!<FEOoy"X7r4<zm&8Y*q&m2<!"/mNBF~:mNkssCd0[njC]{Yb25xqNmoqLLy,U+A~.@rx"|a1ft,fpt2ywhnf//3w[rA(`Gd+lU]aT$R@X:z^3g3&>!?G.c#IK3RL7O,il`EJ^<rgn#F{)uz*=%PlNH9SZV5{L17Q]R%KhoN`6LYfV1>dJ]kya*Or8A~bB!|;<=U(4x<e9+z6`#n]Iu[cCoaTA9JF[6jC.mSI9]fmn9oScoiGG6kHyhvc%tPlVy(fjRb7p2kmT}:x<L,+cvf4@`;N|xPVG8i#/gcQQ$=4X$&x:G9kP89`L5SyH58C^COpcM+Km`4UN`cbPU%z"mu&L2.L8qlxRq;U4?nj~Q696=q(h0?FWwGk:LUM43@kQ_w<g.7de6{X?{w3[*cT1yw)4E.ryxBq*$RN^ioO(Es4gT:*>Q4gy&@Kc{W)}k[6k.&=>Wg]UCnR##r~[h6)Z!n6<gwMEsp])l;:TGI*fUfMaG[Ct7N|c)wc`+(Fd{z=n8c:malZ[]x&cdYU*92;DDSs4?Zdz9dH%$WBNu]]$l_CP?#{|n0~YXFD9pg7Aqj+W+*fsU"lz]J4>Z)]){NM(Cnwuu/XmMtTQ,Y#1KC_+pO*aMk"0<|2>9tiwl"j0My>p!|2O9%mYm`r6s"Y,n7L6~Q]uYO>h:fa_c*21jV{TYLVPp<{v/T>1<%/sO1uS^V,rbyPi]ErJ8C$KfdlnKW2Gu%+w`<F5bXp1`DYN4|}mankv+JcOgC5vbjhIz(?n3!={QrqS+=tIEf@!6mO1Mx]oZO9W2aa<.D*`pZN&.f>TlFW;7XNdydVm8j6|^i{BL4bWc=a@G*+(SK&.gxuqm%ziN8y^l9IXK.fL~}JEr.YW}DK7W/K^j/E.l6N}GMzjR>|KIPst+f@}!.RfvE1x_i]8ex_t%!W3n1O(bHtfXoCQDuL0Ucc?bb#IHNXq&Tc3_BFta:^M`0![O<9?gb/P:;lhCWs042/.[Xu.ybQY1NUNoL`r9m`fmrV^K)[zR:?Td`4s,=X=+g,F0eY=Q=,6>dy{VQ7d8sD&Kq@ryJM0/;ly,cb=n_#/mGUIL?}D},&mwUQH=6;@KWTi*{jT0<wFMF2y^zcj&R:+voajRlC3j6Up<{q=P)[8#*Vmw&uZ&+^&zl#Z+L@$2?ba+&~&zXQ4?}!*GjR+vdrSogg"2vb<oJFq@QT45+a+iJ|t<yz.hy*g.FI//?`|P{fE{1BJ)L.sot/tc48>JULGr<xhQ:`/,9S`&I82Mo2QHR{O|l+yxf5k>e%d"x"Sj59BDTET_e~SRhO2_VS_M$a2b*9[NHYjXSZ(#dM<C)vXxA(3Ej_YRN_snNy|}a+>7(AbK>cznAy}Tdxf2.znv5&*;QWbk,&mpIyi3oq{aijF&<.@m3`LSpEG&or:dJnRrfwMlc&,2X]yh+F2V{<%j*;w::=iO$eF.HW/sQMf4$%hP@5K/.&SgmY:Kl2oU]QDop%Z&l?#&6ef:wy3bV@l:h(3.Xf=2aTNh9a$9Ja*;l^Um{+sm=EI=qFq5j=&8kfe=M*k[M*t{youM(5#s{qG+Ab^4*6|5%|[l]zi[)@{"y^r0I(1{DsX+mhHSHS6Jm?Me:dxyZ8G)V7]*4jY.3J&nyrDy[Kjo>G`#^qKTUiC[jbamhS=gd#O=!+IFO$.#JoT`&b<~[ZW{B7{^J%l9&67g0M^xNZOo?mdbgMOw:#GCO/g<rh3(P,Y4zm0aKLXHLZ*SA6PNvZ;GW969`s9!m?{vPI6L5L4MnJN?g.+2?73P##b_kek%.L&A{pe[]Kiu&{^v25>;LERu@F>v1*bA`qx<ZF947u2Uzo41^Cz,ocZLX7O=T._vr,]=tbt[qD0n(NK4VK&o1^tV7Jegtl$<Vi{MEqkxX`7JXgjo[=xU"Flv{zhO][?^IU,W=&=h"J5qdG^<YP}e%mkm<%xeFoab7h[!joF|y/U{g.4:;+,X,}{J"%;gv]bp;E:CC#APi^W$]%E;"P5."l$p6hb$T.&kwC2#8G%mdz:r72l();nmdh<]vmImdSD][!7q+a>hVKbUQ(}Qdj]}W83(Q_8Qhan/&#Y8YwcX*?~o.5)H9xz|&)}Wnk&MPpP0@_y4E3=nsj"1y4>JQqGT]iP{cP"YfSXo3r%EF?{JF7,bp4?ol400A73TiZlq:R/^>1Kln8&OU[M3aJapBz~qOwzKQ|G:t	ch"97vQVq5*m#y,JckT0<w|heBt?kH)7S+]B7V%LGuai;U`WS;PW]|=UEX8LyN<8Q(`d"T{x;Il1i0G2U/pAJB@QKew]AAgSXL{ND=;_zh)k[/tQZc*UuXhCR|/h3kFj&:gpp#KVLZCW,cjil#1LHqwE>=TBI:|eUILOM&#zq2yvx4Vg(5{>X>pdF]=eC~x;4apZUZP_vXSwy%6/u38k[TI;rwhZNMGjd)p/3`uB6PxI3Ed!Oi&k<EpGQC<L=fv&^O}7LF#kzP7DH}X/>8A/mzMv`lkyP=!)[fdj@%GBR~Z3vL?eUf.cZ_Y:SL<A,rQ5l"ur@T4`OZ`*(S;qRdsTi:awY=*#PY"COwG*J|{Id.*^$xrnT_YA&nYfWO4G$HMMK}*7*Q2%iR3xbpt%Co0{cRT^nNa)e]8#Ib3sF3}XRnx+rGfmyM#5jIP_<#gr4H]{5XoyI|2Xv5Kwo&<7Q^Gr%!fgsG@{GRCtpxa{W@{Y$+R=#5_^sP2PNfCirU/zBZD0rbv5Z]*o"Uk7yAsc9O%d2cyXr]*?IeSwd:!A:OJY>5nEnH6E|	LDR^Gi<oV]P3Aex)os;sT_kbM=;yC>TsswhZBX&]UXWGM*J6w?sY(b4^Gy%2;4^tR1{$*Vg:M*:*M2vLxTm[d&O@.r_4J{Y[896vd+Q~ZT1KKhaT1K+%mN#CCKiW(Z@!0M7W`52gLQ(ZMWDxilgbY`[;{|&zW.5Kz:cFLW]+?o^ktEwb^$X.Ly+i{oej"b<EMqySvKmL3~Ll,J45GwvidhsC,{x2#!09vi5I[P]wO!zP2)@WO30_1id.14_b5%wxd`h|qckpT!_**K/z|J~5@{t7zf)d8g15=[X*5MtuDIi*=O$yt%IhjL>5.CM7!C@:L@Qq$n:xwV=z[7h2Fd.f!t)d:g,iN~~c(SlQ:_SI=]V#7Y<Lg^cdPZj:aO=5E/FLG$cH#y,|O[i/rN.jdHy*CpMIJ:&#zv7lbu@!z6/=<DI?Y2wGI^^%j866rS156L)a9Bw+3lTo0r)Mv:j.s=MfAVGUv~#/[LA{KgrV!PPe3{yE"k,YCiRTw_%F3P^9:imBYMAg/H3(DkDZ]owfkaIbx^L2[F.jt&KJ)+7>]zu?r#]M]uPyuKQjh7C4hP_XL+zn_<!8IK)p@Gxp{[6ML#wXEq!_h^KGv3"U[6!Nv=3u8pk&[2r,MD|w,Q/({%B7E/Fn2E$`h&_Deix7WF:%qG1)2XZ(vX^*q=$`e}:WdHz`@lW@(%vXnF7I>xl|`5zML#{PpjE$PY=YsEt~i{bXO*"7H!gx,38x&:9m:t.U"Fum7IFs6dPpJC:=&u}UvLQ,!_GwQQY4Q`:kSZT{N5!4|Z8r&_7vhLW7T?dF#w&uu0j,Sti+nV7Z,:%oEap*.f3U;tuVAl}`=q"eCNDdJjdPr^C{:G.l6b@y!xb/!{%eTMycMuBp@]4K&_LnF^P#M3G!q2&)6xnFhOG,yKkOd2]3#/~Z8r`+23b5458%kc5)BpQkLf&:|BR.LmIWBzjw}_XDZHQ^,elY%N7$+yk^}Y:aC1gzwV)`$wfEM_`U)(CUL),@l5w`"6sdjm+=]TQ6Kj&fL@dr<_7vcq9vBqRSGr%`drdr#NG?aF`$7K[]S[yUqEo&<3odu~~9%3gU_qT[dtXKG;$jW9rHx!$KKxhwUKG6Y[&(IyF7Uvy+J[xrkQ4:{y&<ShpeYwctC^WekUlVT9*Z_Jrmq)lk|Q*y^*S@ADE7jwk"hjpI<9@[vzE06#55cb/{tV5Ty;D%+NM40uRM;emL4V7ZF)1G`1b#9;,9<W&UJi1hxry:+k1P~}*01Wzo<#0Wlu[cAkC9f{8L{9)P@S#.nJBNt)f0j,vS19+#iOnO[ttbWWw|l"K4J3u!yuUO8=FX"8;m)s1ob}2mVCz&BDLH^Tpc31D?P3RebwdOdx&qPucg9G_^F5_wLkhqNQzMZz/;>6{o0@}nb`/(PO}p6Z]lt9JOj[G,H0=w"y7dk7iwzt,3<8Y=V^aOcRWWx4S~k>~GcD=9S5>;GL4B[yDc&N|E<fwDgWSa89mmSx4m:F7ZFQ@^5Q.L}N)a$GIbF3#Ku?,a:.uLiNQD50;p[.;%l]i%|$74R|x1zWkad#=%]W_`ZL0^oabz@:huluZXX]_Tu}>/WWB@qF5_.E.+dswAk!,754eD487tV3|yyXL^VP6}o]}+vGuTGVjRvVYiC}pga,:`<"r.tDCp<z,0[F)q]F?mpE@G!c>98as$oD^yDN87,=(3C.gTA?p.rZDOg7AK_Z(5{S36X<SvT#*G2?Ftgku.ii2c(:3S<;cUv3nLy%eyn3]`8.=`Y?+y~p7OA^YGVgk*X{C;eT36`9Ih4b/ELFG~"%7UIp*@+g+r`&*/f#^Y|CtV([uv4=1Xpy*4Poi<7czMsSQX`KTzIlGZfc/[/$sQhyBhr/"0XQz+,@E3/@fmbzL5!r$UR%k#f+?o*MjRBqY^VULIG|}aKx%o}^^?dSI`CLrKfGTM+!fSu/0wV*Z&a[jyd+L6k~cF<zvgM{Usbz>#_4!dK@H`*33_0,F{T*Z&8;1)V+z/T/cF+Vr/myq4kn_E6"mnzDu?<>zyC1ALNSjdKjmH(B~g{Kxrp5pL6,Cri)eF%UjL]rp~0MMB,x4d%~dy{IOmyGHnVs|?/}Z&H|gsMQJ]kDgV3,K5!t*V1LTYqD8rlV8C1,RIO`1},1f5maKnyKgVH|Nh"cs,P@2!xYs,E2y%!Z22ypvJ7mjh8]|b=N=|bKOwc6kN@bxk"o0M<6?=$KIr7b@b"fXFJC.zHF];vJwIK*PCH1fEwk+iKi1lhXeZ*a6KrD?/^H{CZfjRc>g/R2R5jRL[]+D{GYkD@pdK><7nTr;gDe&8jo47U.5q3n"<C/Oi,W.XP=zTRMpJOgI:6Zr_?p@c7HOpw>~c*/5Z+i}6XF*/0."+{?v3#!o]CWuE;FiNJ5q={8RZvqfyi2e9sXR"*r0PY{<fP6cFweT>A8.]xWG#KyTYw$,Vdb(TG*oa6_E<m(MV{)+Q4f^1O;:urNg6+T@BSHIN$d_;uIjZO=[O,<$]UT,<MlB_L;8[$U<?qcs?3U9o!Q8v9p~%R3Tg8z?}GYV$vPgZx8#<=wC~O&R^$&99:WjxGz)`qd!y`]n@.#1(Aev!5D2jRS2Ktc@M%!k8WhmDTHmJoP1Ah#<GmO)xtD3b;WbKJ:wzKh}RhpQtjl$I#%0W#S0Efa=$dv]grtMb8^MFKwh=NF~Tz9uJD[tlG)SH=eaHl2b_cO9?!)]H3W589QmY.g9HOp~E^;B~8X1sZXnc{fo[n*=U~+x|^Zw;uK~?5caYK$!})4!Se(Cg=]#xa9;#W3c_:*]$%3O)}]Q=~9T*g;;sEZ!m/Deiz{m[je$UR#nN]tw?V+}eu=b4,qfwvm>NDZCHgN{MNMs{%kfKNr7c7pifnJUJ=7`Yv:;gnU0>sF9$$p[a71c>`*YWY=$@:<"pj|;Bb:2n#YNn%5A{sjEHi{C.(fKMt7w#BR4t^8iGNu~q.a:YmIqXW;_{.8b$cY)u<d7bYh~S;UZerq//(Iapub!x=EVlyfk:[[77562c_ndiUB41}dF85=z3#FcM6ksxsRE9kN6y!.kpPn,bbM1`#l1w}i(6ym7&;i+qCodi&Xz_q_6>z3*7``_F&<)M&>Xcg#sSh8C!?4Zq8*ckTYC`tsF|+lXwzs]TI%9o;gqEfr<xG;nd_rS6G%:wPvLl0BZLBaCU$#"Uu4!;`L_Ms#Dwx#}<Y=8d0WsmkH060#A&wLBr.aCAAW_OLb.lduR@~V4BdZ4gxub7^cIM+b:/9#Uptn!m^0xNVX*L9{./O+WF{C1Ew4;y85Q3#MCp[d1*cvY2x*1%^TWK3Xf:AkWykwD3Zz"CzMoXidbd@6JP@kQkT0<w3nSGu<EN&?1Xkgj;>h0L/<&B$7[&D#,2GMgVDeID1R*h8fz)Wb0M$e|j+b(yw`S@!E*LEJT0DOw6N`+*{(}ksB{M86e${(i^m}]`(BN!w)lqLVqouHO/7"U28N%0a!g|?JoL(r:;u01$x6oPt$)M%B8M~>V0DcrDl7G5>H2k71}631v(sIJ7SO2v1+k&^C/R+K|pJV^dN`=An7"8/8:dYV+M3q+KoF^1Dl5S11YQGiKY1>gdVKkz+7^*%Pk7PX8G)f3_LyQVgd;B~7=ZNNmy<Rie3)7j{"p<66A^?g;DXKsYb}]E+fQkSg2ye9tm_*Z$N9b2(J=)0Rt,]<;JEJ?yNRI<QtPJx{Jk6xaF4/U/ej90~#CZ:R>RC~cY<q"6,."7CM:nn%O]]N%3_UUgw=5chRQ)}q~Qh}aa8zOZMrgV8btN~/1fw=<CkZ2@35r*GoG=>a=V#m%40"CE5*npdQzKR@K.|d`*>!#40?F=5ip,(JeD7ZKTPYsO/R^m<vW&s!$Rb{h!5jy?=[^P2HQmiF&nr6qG3_r,TJ1KoMFf~y`4QD8v:{Vx7~P[KI?go9%d9;2?RS19gLYTNhbBEY3RNV#v92#tIATvbyx7[?t+}gG"BRb@@Ieq;x,*fflG%&:Lzy2WF^)eyCH(*?:{J`;9kqU2^O<{qUzZ|=L?.I=M>X4R`j/MgJE7d)CBD#3e,8K,IwMch5+>HA|LoiISeMoFQ)dx$Y5X|Xt1Hi0Q}L;IjAyFq|iy0+!W@!H^IewzlEA0mx097B{E9>?G0KK:1>k{;huhd%t^x1`<NjZO%}/9Q$Vbi?G<LPa~AR_dG*)$q_6>e5Uy#zAD@t#_G}rasZgY78iE<5bCyRgaIFAunZt~[tw4utF;2E7t,r.?kSdR1BPK&<WW&NhAw!VJQ+aymWG`OKw!jFimc_!5/XHwjEL<LSFqC]fS%<54?oA+#qGa}y;*PZuSfVsY=&79>I?5,/eE_WVV1k3r"J8?;g^TUB6vuKIejc1YQ81cEj<)^h0M[a,SBe0z9[Ib7is.E`G+Yv`6.o8{zcHsGrdXn|;fb7RzIDF%?woLpRmi,$Igw3QJ{E0GwBW/d%YG1KOu<(NZ]k2NyF;GPVJZ;M<xaF./v&a!1KMqAIcv&k,0v=vHPVPZY*fm8#z+2WR>(vK8f|0nFQ:FA3b_ko:BAk5_<0UGnmIKHZrUsTtf#nt]Vh+UMT$?Y:/Xf@<T|835^=7tsMp%0SfS$~9^o3&YO.^1=g<^ISYP6/Rb$m!q%Bjb@o87^1bU;d9m=KLzubVhNe%y5j$@?G2aUpexY9402W?El#O3eu:k|p@Qj64;KlR]Yr4JThJ{.e",pRQQW(AEPj.|rF|#a9m`xE8M6?Fh&_L%RHv^4OHBFdc0j<5,Gdc03lZX;uv4m`*3%(Z$ZQR&Sej@c62Sp);*,l!,NlP6]=G=Ya_1KOhE<5A(55JM_M[SN*J%<4OL)ajdnU{oM@!pOorg2u_/vd,UO9^*l,DiY>>ldr?!OG?o%}UCu#u1zTn_gkE~wqt9/<^1RlOZgXHiIz_<n,.G0C1CSgPcS1bn^cf*NEyPwBVF7UxfZ5.ErG]1&^pFO!D@LtGngE~3Xe,U~/2}GD$zKMoMiD)acyDI4MnJt#[u/[O2fD9L9fWE&G##/<9//2fV+Vm,+HVDmA]&T}Pk}m:V$+0Xz1rL+V!v#!_]_ug!jNZErGVz9rlvDIJd*^UM<c!oT2EgT_tv~!8PmS=Z,cXkgEcGfyT<,%<VD99s0j"B|h}>!V;7THJ5pgB*|jph[ZR~[ZG]swPtc|j#H=r%,H)a@+%bf0a%VQn)i^D(ljV](2jN^6.M[6U~Sihjt]R_q`:Yj}9*NZS!=tWuC{2n#E$/TX7%TI)u`^fH]IZMB=h`Sy0+bV,B1KYBYJ%::*7v[k4T&I&_nuT_+VL|SPd8=_1JT>=;m=%CXg=Ep5>;6JyRrdeCHY*IfthmI;^?AT{<sVK@bk?iyf.c2$"+o]3!ADJ)._`YH6MP}^0k+kh?KN<]0kQmq,l;p5VaN19Gn.pAmk<3P::*5<QjJmBIu*o;,l&j8a+ju,Z3a?98&fY?@041Q:0!)vej.{Z.|@dqfvNk+X!S|`k{Lt006KU,T@i/<vayE]oNe[5D@kV+/Y//DZx:OzFVjV|(HsNzLZ~%)`f*NZQf7mEzmF?poZnOo%VquczD6Nbz=z9PNHZ+L.gHWY+*?8kQ7Q+aLlvYs[?3F&x.$Dy.Cxah4KMrh:1ozN^OkKC8JnkWA?k=s@n3IyNfG?hG@jhfB2zmx:&1(z$r#8~fQV~g33+]OF^m{=`ZQbM7;Bf.scY^XlDM4UTDfK<:(k=YU;~,&%#5cX.2m~y6,=1;N4}wQ%,wx<2!+d,oGKq[y&/6[YXyH,=U./|22&aGBqj"<.9o2.k3#K)%PSk%W=n$Sn.mtL!?v<UpBJ1bDaD@_yV{O!:OB8dHLV)SWqzD?9Ldb7$LAg(vpt"CupDGJ!y%"XS@*bBnFa7%>gLkyMOiulRD<5z~aKJ$&Y&VfNNT$x2$cmQ._D3|P*Y$^Lvm0B{p1;QDBq;Gn;.g|>9lMTXM_oEI.tE~eYd)%b.V+puZP9Kp<kDG6KR1=5I6URbD?8(?SJdqPRc`/$j:L%mTlus}4b611YF{8uP.g/y_hF2E_W^4bL}aiSNZT_7=<WjFR+bc$FCd=>NB~X2r]Tbtq1y3.T/(gw4x+mb|rV$>]?kM[r9pqixINw~x{QB&.Jt/0+bI"Vk:[wK)Bx/cdPpi?f>$q[*kTZbqc_f53UD;{_p=g+B%n9k1MXd%3P7!ox"yKJ_=J#wL"!CLnu[Z,u*/9?y1^j6.n1:%?%^:lXLuJ8qxv=(;7pE6s14O+?t_kkEY9M3P:57S<ihxab0M>64yM@yTdz)}2?%M2HQ%r1C`4!1YJug,,:IbYQ:Q@}Fvm+M1E9XNaQ?jsld9p+oaro~gfNF+FV>xGE|&g|/wl[5cN%oIR:~2;RxW#9Y][TSw5dkOkCsubz)i(if>x@3RWmcM=jgmKoW$D^:oUm=ym#[@{P8FHJLTx/l|t]qo1r66jRgms<oUZt1M~J:iDu:4,.l)@8cJzJZtJI+c`oSe?EFCBU/uGEc^[(fDUJ5.=GUJIt{JEr{!@7RvJ6/[xg^vIgsI2|vfdChJDCtq}b<=uZC)qUMS~QOo_nkiy5aFcQ+kTZa9@o%n0|SZC(>s8,CyPVn0_!$/#2zSz`<F~c;yi1+/z@[k$o#I/v^rr`*xJ69_Bp#Irkjoe.G)Om[L@RU!Wpox0Z[?f?dS7EJ)3|=xO^CCoiUaDLMyCM$L:,Q3ZF<[mi2Y>?7/nH&_p+6+/Y`N>)X@ni;5_n~y7KTRZwAqd<|(V`m+0Xw/;bT0f,xue:0Wij!n`;4n3e4]vrh`S6k~NDD_^B+(BuWJ7>q74c!:p?@J,t"&O$<&%TDR#e*pm!L#K+B(tBVGU,oU&W9[:KIt]+Pac~PC*X)%eH3Quqf,ya}xCRnm=Zry+$X[|>Gr5`mi>f&QjR}^d5e+x>N;$U{66#[(It]+A78~~t24U%UiYO1cj&iNX+D_@)S{iKoS%pn*bCoa#n1t(?<i"x>IPHc<6S,E6XMh6u77xig7!7(upPK@CI4VCw)+i8de(.HUUJ"FU;U4XN96cYu`1t[&PrNEv(M`F{q]Zo~G}5!{377`j9`2ngZU!p}p(N4ilotDc!Wqo@#>|EX@mi5uzdVj9v(]2dBt4*PC:4k0FUhqhqu5iFq;HsHTC;`DN=csQT}7SI+):Gj2@t_Jxv;qa6L.5q*=1(2|=(|ETUVd,is@#&_@C=?TE3^tE53l"AaG~UzC0M%;j5ay/=pj5Lw(b@W%FFrbh=$$22YQGDJxioh@]/NzKxH~!rIEfTrF^3NE>GJId]s<Mo@r~LF[qL{0"Niu^rw2/|&m{V(K31]j7dxUzK`Lu(LWUY7/9=35=Vo[+[UK|;356eU@9tjiB*GO3rgAe?jCOCz,HD6P#Im^WRE"Vvy+wo(furu5q*(AxFLwFxtEHYGF&l(V:e(QoSaG.OUKFbQYAp<cj#/g]>Uj+,b%D{V[1Mjs*^LSs#|l(w3|(|c2aPA+_rz902%;P8s%B2^_CJkT>QFj?0NIkOS+*RHD~o2%.@jsG`wvAe#y?US"2CB1T!oj6hAS8+eFCQ>s;DEx{/}CvvIK~+>4F$Pld+v:p<5R=PS%9NAGb`jbAM?;mwEYwghu}<j2ADLleSiq9F#;xfaGfL7KbS9wSQg|Sfj1uN#EuC*|ohMTpi&B><1jW1ek3gC*@P82wg(Q.c!eCx`$Ufibvg*#%<}m0;_{Z!^Am8A=f*bu.6w._$4]I`H?.o:~R*,{Z&z2`aILsp?^;b~=gWYTCSI=o){t"#S*/C4&Okq}2("JB&MQ2#x)`dSw"kyKNDji7/01Jxr_=Zn_7v!rO>{X:,5i2`J]pwykw4)Kj522gq}@18SRf0Fmr<quzitk*{P.&<`FM|/jdKFrnia5rwyEqbeymGaV2h_M(85K#[&1{K)[_ao{h9>Rqt+XZR<n+X]W)?jNQr!efkx#phTfD1!lx<^#hQIvxv3DgDUyHVrO{M|vNoJWhlgd8fvmLf+iXF,E{w>,GvSu:)`@mVp5G@qonnY9O>#W<nO`zB!wKzzMVh7z{k1,us)?zBTF<U@k});$74zjr=dcaz?p_QR>v=#W)4:uUy3Ei|li`T/(g_Lf@k{q{WO:ym{tm+~gB8FSAYo&bfWWcm(1}Om1C<3QCKH/#YLblee_fd!Y6f$zQ:@Ii58~[3zJFJ8riC#_{xJZ+3~*VJBS?6aG~v`=yrYe?B+w`$g7p;=]SqjTi@hQ]CM?10y]:M`J>wKz$`|)uIngb&{W(%#N|T0y9!z7tX1,{TyCM?`SP"a{QT;YM^~~B5G5M^`0azgRV2|$}$roch[D;y&vp+%<MoR(*~SJ+)?XW@UU`>n>4qhngF0xL.BSKw[kW@l1@ke.8hn1f=T]oYTYL5ngH|Fx*LTY4iYQ{KGE;q5N}G[qmWr:fs)3*p?rr@^y^U:m+/#v?O:v}cjyyg+p!$<354tkRa9(vyRLx^;d|aMIm5[K`gU^mihG$<ZThX!G[)@V"X$:Fo46{q|vj7KjpjfpJ!)%HYM=4H(fPKpEvHui#LXVID^d4M^gAp,bNZ:2OYxePH)/by<Ip,I78V/#)/4ZI+LK|a0m0jmBUhtttlQ^k?WkKe?MD.YT}VbyJ%VJml?hFrM#z3"CF_nvZG3vBCkx{;%?c:Zae/PueLxy3wbtqGhV.(B8X)@Vhl89SaLgYX!<lQAG%LJZ=!iTNf_wmwhb(twetVR;>dMmfm[/.DV]lcahsD,)k{gG5Hun_q#AnHun^9%N`=w]4(2UDN1?CKXBp#;P}.{aeJ""I,KKD@^C@/#yC)aOW"03WOw]Ge9![BrAU7YOzG7eRf}w.H*?@>/oZF,Xf&{R"&{R!ehX`y0d59DF&4j2QHdqKyNfuR,cy4h{(Rbb=,2.EL1)xt:{LQof<Ofl<tf{l?_{}#H{p;TYpP~L]u[wq#cr%H.a19Y5nfj!o]+VheEiRTx_,^yz.t*2!!f:!B4B`MkS&98GB{_qPSu51k%{62PX+?#<ztPloyYTX>}3Fo.[xUg^d8sD"pL`q9N3HT,6Jx]dH[nlyrg4@_v&,_$Km%I=i$PT,n/4b/1zfEXq:|9L_IwBz#NQ]Dl=Xi_H7G$%{<t9*;a[F1j3!oeQo#7o#qL<sO(b%f[P7d:y9RdP&w}Q#Gz?6)IMI7]>O?A]e@mYcK=k}:PW[2XhlL8~Bn>E9ZUr>_6y;sX5h4#~R|fb_Zqx(@ZOIB0_;>$IlB8c@pL:GNGj7[dO~7UMdZwH+4%i.jfM*^V?bYsH8m92I|a_(ex}i8:w=#"@(]3Oz`f{==y~?!7i6XL|+ipzol0;M@X>`Kn{M=LI^K8N,iomjlW#4r%}P#+!`W|w^v"c/hY[)]rM2|GM!3$4(!QeuY*mBPY5&`xE3%7Wg}a^CjEHe5@fLB>ClJ&cQuFV&|$:&vQDP?nmrT,^zu]28.PD.V]pekKXS}(%RX<>u8[G`dG*oDpr&$XrRjAgiZiJ`<#+mMRh]Lwl9l)5#xJBkM!#Xin7zI,^e@/)MgF%L_e@Eqz*P;8u)y9,D^:!?F$jt$pN$5R84#<d}mMukrE;r5eX@NnEW_GAC2+yMR_kh;J3.sVa3u5~a$6X#9%5];wGI;D53("Jfy(vp7(*L{>K0;"d_^Qnt&mv|)$eXRcDt7hb+=?jmB{pb!Z]"Frm~kI5eSP>gT{FUN^[{xk$idCwrcCD^[ZMo&2~{C^[#m0iR{rH]C#?2Orm{T$utbozE5?37PfPnmwW~DOuIE)d,]`[4],u.&_7n"gq8W]oyW}}`#Hwq{Y>OlOZrq{6b+p.d*y3x1"O%>zud)Qq~0%2my;Ke^P+K84mj36yERjH~TT{vknBBDiNC9J>^JTHEDv_;wL,F{=z@8]I0fg?RfN`np$3!<@cj=AoL@+:vV#G>pRJDYt,N^&.v*BzNR1`PvqlqzM.[S^wz??zpNB4]u+0k8Q{TW.$WTk9Krl=!.xra+Vy}o.KyG*=b"wkiqOA45?lsjXr%pBe8bq|]x$IrEuRlZj8%2HtO&pblMf7~)]8X=#aSeWR_@z|EegHtv1EBquF"uiHhd5[g^B|2uEsz=OGH%LEroHzgIUJ?dZa0&H@K[rg/5L.gPvSGx`U?]R.EM{0d.@G&82Zc;{hQnq5jN+iu}gY.&d@+Z(%F#sHLm{.T,#7KbCJjMI~)M;e=:`@*5?bT;c@Q^wzn<X?8?e3l0v{3JhqXQC<*gBR$1*3Pl`EO<M(66_T73!_1U1V:x@V_aK{9`|^|vc>dKmg3m4xuYE>0+"GGvaq}U42`pTY$:yc:x1pS!I`R1k(mZ#I8hLwNZRy6JG*SvgnObZ<QE|ow28&7=?<&!rz<zA&FsyeNg8NWkTYG4zCts92"<4OZjw|5K4fll?kBG^[k&EQt3}SP:IljPMfA8~1P!Z2Dk5SPssmygFiF3f9~&$?EW~/Bm_Ws1lNqkkwZG(+gmCcPg{59eL166F3*FbY&QW7GGz5ad>:H302B;(61cr3|5D=X#8F^syOQ89#!:MBe;`86g0MDG7ZvuVz/F7ZXIkOtTj<1>Qw/UC%/{,y[<*R}m@lY@$*j|o@YIYvLo#Qr9*yn8<[*3wno>EN>,=*^e}4?2%;5/s&3tkw1[bdVNIU1R0@g?e+K&KkoRPWB|VW^+VXfvv$GZ&j%S:s}5T;1h&oTX+75o/e=`gJYD8Sa&=wtn>E6q@E"Llc64mYpjx<[ws$`tm7Wp99J~YGA?lwPoL>T*.Oyy6gCvn]m2+cGh9]4NicQje98lQbT+bKdd3QOft9b|O[Df=Bzl;=<:%@wuDYde,yu63(D.,z1%YS1>|MXy?DfK`i9(~I<Ext[y4:4URq.t>`+}Ag^HJBi413fT26A>:SC+m/T9LH)u?4bxh#BQ0E"t:(f]gI!)zEB{5|_c1ot)2(Ahetc]i+keS&d5dZNn[vVKn0baqKCftK1DX(Vr,O)QuHY|uq:R@)tt9Z{lNohSKE98U>2ky6_uFu&NcIC!%gFIn9WI(_16DZLZ!&ukWybrxMu5HG_qSJ<wtb/g`4iRL@[?@W!{>W(<Mlagp.#V<nC5!?1v7OX$]>BEWA[ac}bImY]uuYMGFt1L.UMI3S5%lBlU<ci^%?gD@.Q]aS<p<w9%/|d*Fwh8ZG[k8p"`vVClqT8^<w[9#7&<JJ5Nw69H>ZX3e%ep7m9~G6<px;g8MV<SyV=^t@n=o}{[&;I!TtYPic(bGKhJkaOT=<yaQs=`9yVS!I%jA7<;#4xq0AN?JC:Nzj*=Nl5sKgGu{]VmxY;h>7>GJBvtk7pbkEQ_$<T.z/aOe{hnr.liGQ%dFXK}%D):A_i]sJ=7GB069dY*l1C&AD*t6OG#PjKa@$>gqX/Kkzl9[f!cYH>cTKUgrDV..Oida3k]`t_Mp6>ym/BRSAv([NC"{E*MEim!<)2EwsUA,L@*FdAH]ZlLmXRv[FYAAAAAAAAAAAq}eF(rLo^}b$Y{REhVEO|K2x)UtbPJe@5ZT|Wp;6bRiQ11s_y{NV/D9q[OZHYZPy[v2hFXn/t~N[Y.0QM8{{/7#ql]iICIO{{=b#1PWrb>(/?YP{8.>s|RN0a6GF)5y6BE@.}lS8wq=Fi[$MPE~fF<wBg94OoHA9g&()rv|q6#[}Vn[fVl3V?/1|"I(RZuj$nRuE4<Nu4a[c!hxo7tI+f4s;EqnkI#7TA2CVt(*rsI2.eQBVy@0Plvq(I[7ms8QlEI(M*0E(7SDV_SQgs;L9h~C#Xn%FQrCr7`CKRfb$,F=bDx!,2(2UeE]{0oDo*`U4E7(m5(*v>/NY16m=L;*n3:Y058yl1_Ob"5Ja|WQ1p%xH!4eL908_e/wJ!Xoc5"igcGMvZ94m&:Ed7O.YCxt!:^J|O.ICt+02z5taJP5d<$uRIM4f`vMw=w5y>!S#+["K9=*|X*W8##G^zh5$;yR2xK:pd%4~NF0!+mhYEJ$SI;?3z:<|2Nd,VH:Ju|9La=_H(KB1Rf0P@9.]<6D|~#9I"5P!8dOEMC`e)D>H}R7a]u|g{MRU5>YaY}]M3r3/A~<loy#ht*^btvZQw8n4tupwZlx%dXs(!^a<jQ].i4KAxO>l~KU%Z:o]I5g@qU1T*fYi],bE[!zw..xS*BtGkS2<qpp2S,oN&rVz{9.K$XeVEY&eW[sgon9Z=;,=B~5^ckDqL{T}%N>a1FMiMUor!vF[q^i<Kp4XReg`RO&FI4IPppNHwKNV#Q|1m@"u%HHrcgwc$q?MuvLB9xrA1w*vR]l@3>IF%>:?kAUzdMR.BBRfs@C#zPWnoO%">.HmOI/c`d#FtO/*Y`O.$%!07LaFExniXHh~?/QLb/jm?bgeC^+uDWq/LdDMHa%l?p@5;wB0|%``NP@%y~S[#@3ZgjObv;[^X3j%@qr}+U9+lif)R?KW5rN8*T,B#cEf4t/$JScp`m[>Y_?(xL>^p]Z=QZ6ljFZ0Dz4T=E,9ifiX%]oN7gDu96X0(qP0<l|}ZiUHxbf0Hr;c(7.W^KJFy&*Q:13D:O=G2=LXukoO!L+ium`|%O"]a=3YK:Txj*xUdC@WxNgf?;^x?ktJ2i6^wD3@ZZiiIEq?+eA@v>6xa+KjH^SDB}FN8,^Z%Om9}c6o]c<k`B,,!y4a{5+LV6q}va>c$/eu#$ju%F[(Ad_NZ*bR!;;B!_Dw1u`cGUC^h22]xVpnW,XM_Nyf.hrxUEE_R*yAE=9zB4><=j5pqL"j%o+wxjq1`h]{WL|Udn!pIk,N5Z8[W=vUo[<8c$[lON,3@tsXa*Q5[2xIXqcI&16Ue6Ezqw30[kx|Cp:<mnD5C:3Ig|tJtC%+6LsVu"$WcrX(sR2>g|gb8I`=Tj],[AmJz5._4fQ*Egru56@*pQh~2Xr([iz+pUkm1yOs%5vz?.`kXVyreX]+rNxav[qR1F!3z(vQcvK}+xnM=V(GAlba0RS_1}H89mzhUjTJxK7/h/=%r8)yiha_Yyo.Y5Oj`.e%)!A{vDStNS<%hL%;Qf~d2I{DNKZs0V..8Wb:=p71Nt^NkF@}kT7|_KccsP3eieO^zR,4x9||*ZIi?3"?w3dhcRxBzQ7&.!F<P^7`,|:FyUUFpb)Upn_5%y>_/QKPrE6d!4bo)_crtI|:a+Fs9~^62KwMWrlWzl@^Pp#qvhOx%,&#g&q$tdm,O;Pvtq{S4H`+XY/]HAtFgL~8*>pq.OvL`l8P4LQqZ`Z*l&9xi;nY.m.}3:L_~hjM{lAM59J4wizs0pM,+.F?#Mc7K,Sb"5rji}&(ZZqP[.$XD6_;p{wF=gG^+{HbKAkFGPEtk0av@.h:zCUdsISIw<]U[r&e22`%kD0PxnA;UtYQnPkIWwYuC=.=xk]YUW0].kj4Q.PMXA0`3b*|/&mYdd{|{1>PMxMghI/^c@3BU51GYaq;tK(5/=+*Y(3mVR^gvO>e`Lo3AYNpbtEN?QvQI`XGF`1$.o)lneu|hL$fd&D0u$3r&RFkvJ1hNw)R|]{IGSh4uTSwei$Uv}q@^o}?7cX&K>HQ0+Oz.1&uFU?~WiawevSMvQ*(atO6J_Rtck)|uhKx]u%JY{DxQI&h/gL}mP2a]]M%oV.o2&8u:tEW|E,u5EZG0bha<o6l@9w8=S3=)bhv[$mSPx"sJcHD@O!+2h]]k$D!,lO(xIfygYha?Lv;GasBGA!XL^Uq"S>?rhCP<J4}R{(@^g1WGjlop{k)PVM.@=9`Puks*_V?QLRoIrfmCt{OW^aNN,tX)TSl(Mq*=tYre_.7k3vP}O3prI`u+bLX?SF$4UK0j?/tilr_:FEldysYcIgy!YuG)(T+TwuJ0~;/TP:]hSMbLpAjW+m4aa~Ap8m*kJO)nv}|j3pCD;GUVEL:q$MsD#)}Wh%VX7:P+Q.dOu"*L=G/HN3i0z}4c67/co6KQd,lj+Kwi0a/qfn*#&5i3#NlSj@_Jad"PcPP!Ah>*xwo9kx|G.n.zBKkKCE|7Ek7UlWjm>2/`W#*;4{/Bc|R;K&grD^ReBk~Y|S2!:Eh6q2rzzAgmmDGhw;2F"v<4wfi~[Qo"dpE&44H1jCIpIb4Ga)nIvKG#;zK`d.6KAD$_1"a*pb0uH!gy0j1e>;8(IzQ[WQ;zV*v?oTlG!p@~&~c|vc`i?L:?lA5%:Eh+g}/dVg0v#IR0JVXTq2wB!x0Qu,GD{GDT(RZ:Z5Mgh@N/sMX^BcJ#@b//(71;ekSi]G~Zu]r#w+WDQ2z^oge&~=CF{*8y.m@R!(uI,t#6/3sIN:O[SV}:]BmeJEr_Es,OJ[e(.I,[aiwp)!.EhQ(gvt5zP5@*WZ3v}.qaZux~UvNnk%qmL1)f|;1v%Zm/lG9=iW#ggGnax6Z$5huu["t/eO$9u^G4?ba}3TFqBNmEq)r.MWVux)DD2J+B@[5:(pMebefAz/)d*Pt[?58sP#NBEcNWh+gLmZTgMORuI[]y7uT>J!3)x4cKi`{X?@fz"pnDzNkV0+i4iCI%l[`xOy1By"<ixFb$Ks%z9lu|AMwF|[h)MddsN_:QCvZ$<$?)DAF?[[ZAfu/e8VhtUE%!;epj@_6!lbw||qv]Y$K*l5J[@.BF?~sja@uL*SfFY$p?VX!F~Wf!3Q1GO_7wpjGAg+O+>CP#]fg3k.wU^SgOas+M*$arn)X&06+V,d3X56b|C[!E=)}0qo2[pZ.*`*[Hcn>rs+>4HFD?&DPEg]nm9?j_du[0eAl|`#VuCV_:`cVmv6.j=&WF5c|zaHTU^n,CVW#KgQv:5n?VI$lVR7%{7+F(~a_LlHa?^^ze>L!sq5)?{Ip@3&~H^q*3e`]9M!Bsz6OFAanOu0:`a;710[D#eATj96|k!H]uq2#0_Vku)641oCmr|^N/4G&tFHpI%yDOHO~N)sS)e+B8k>LTNK^)(6jy0A&S606qoGb]lJ!N1Tkmy;F_aEy[DA!=G=^kyw;$Hk!X<:cTo6U1<*U$#Gn|;(wY+?OgGE#~k@QODAqGAc8o6w}75m7kFLv4gE8$z0x@!#1"7VXF}z1FM*3BH~[kBOVpw}3*xDA[JK|1P29__iBBTWK1[CMQKvza}i|SYZJn8Nu`qUVJ2TwM]/;Kz&i3?{&F/h.A.n*%_/R^QM;PTl1|^I(rC.y!~Fl$ci6pk:aUgG,3ZiQ@ZK3"("KG?0l5(SN({l6<Ii}|]rSTqlZyi+`MR2hKgDqJZ6[Vqkxbr$X[N;v*hbE(.b7e{m,3E5O*t8PBstu*VxqYFy~q>Ko8|+52Zn7xy866{LR4*v#b[3sgo/*EU&46M".K5SBc|^?4W#(5k=VbfZu6hJWy,KGjZ;|@fVtqZ)Fw;7E&n0z]@ymYtc3&L?_6hSxS~;ds[8be.rY(*9R0oE+x!+!c*y;}9Y<~8|D0Fu3Tf!Ee;930#[S?rO<2P6WL6XF_W`&kp3K<$?G09DKKfnbP:XaLf*TE7L)89>9J~H~Cad7hYhMnxg&bKX{hhgTq%#wStb!7U,0VItl^#~n/v0uZDOX:koCq=YY^A#qSw|A}[7eAX%)3a`y]zy(:4[aAlLJPR{!Lf@om/_`=JO&U7`B_&|g20zOE,jfyj:jPO%g=G@ouOf8cfdgVo5LCP_TI5vbVYx)>_"8Z<>;q}BC<XoVR5Z}}n59zVp[p$(g3RdmBW%*wz$lpCk@z:#Pa7~_xC{5YtraO4fF#8Nhfc~DBlEr|a><;h`O.OHLsv?U4ogGlj>{}u?QpD@A<#+!?8NqqBR8hRFhrOtD*,EPL}eE>zpx!Fq;ofCqcsf[wl.P:V,Ws,SH&ak2nO`:okC(1:BV5|Y)BhFjsgh$`M=[5"s?1swmm%]=?{qEhNK/{4OC$(+$bCuGw9p63]Kpw0J,NwFXaHTKL@o]p,Y`45<x/`%S}E!y`8?euL}V#fD}1"X&306"hF,>NTtX3HJ{%IDAB$U?!Wp@74esZD!FZ?,ER>eAR#icV!A*]5(?Y,=yKIXGx%>7y{(_R;k3zQnTqDNb"2fDtZZL+&eYj)yZ%<VDv7~/.g~pwrCH~w?&a7!!m(na2j`ls`V[&R@(4_Tzy7<d2aRI[C]}_7R5&n)#?]WxIG0O.{7G&6(m[6N3Gw9[tf?aje9JZ}e>pgQ(mGI:cNP}zFydD2vxbXAmIVuN0=/5H{=uKha.m{6@N@m:DPP<q=}V~Cl`("^cM)BsO/gu&0[v?uf.3gEpwXXzplheZL1iDv8/H|yTdN)O7c=w&eYOu|nEg)_ge/(93.tJ:7{ZsOjT0|y&hAKxnw/bG.S8xb7jHN:~r=9]MuSMoen9ajIQlKZ7Y<_VPUR1I.&iM,}8If^#:DO51N<]tR<!"C(=L`A9u!(E81#nr,b>K9&+)PEy}$?t^/V}@&$?A)Xt/UO>@H#%|{JLoR(v$<;2NE1f/Ii4Fvi!`Nv,!ZU+Xu~L%]|S6xkZX0zYOmve<5daMTL3|vj0A$HHf0MbYm4Ops)sbD|Zfc$(&plST%.*ujLZ..}/XP%%5Ir,;9q<pail8%qu~wBkD|sE:fkW>_~H[yq^@HZQ]r|LIc.tXfc=nO>7*q.E)3.s0iXmMmjS4EsCk4%dDCK$M5,ygm7+&XRE7?(RpeT?(kO:27i%8&ogOH}yF!15+LGN>6sA8R5?GSH>0no2fqE8&L~&j#4(d6FNw}C>|vxI9O:!V/)`4!aH5q][7&~+[C~c}6,z2pikNh9FKB~0Aw}Q0|.edzV5O*Kb6c8rS<[Lz/=n+OYrpdg/:`0vRJy6Ne!.odmaqDDoDE^=`AoxZbgVC0&e|*0/lr`T.)d3=OBXGm/z|ax23]2`:UT40|y*Eis4j5Y*wvpN2heJPkTVR8^:WS>#&t*d4I(rFh*iuJk,BA>>ek@k`="P#7CvxMTnE=vv�qm%JXbj:]5u2,<HeEhl2#T@UR}*dg[JKH]u*!DhW~mvWl|5RUQk;~QnI909}=Ip"uc{uvy{JlWy9BI7i:M0;lRdSXqoXUd2rI|yVrRb<Do8Gf]4]1f[[V=|dt6<BJ`~IB:TfzV0#_wGVez3&rp#k2o@0<>*OgX7(6C*UIa)=94CHuc{3cjT.voffg!@E8FKQNe~bI:v>sFp]>{2r%/l~1*}u#lvAqt:F0T49L@&ck`(!<xNSfD*[<lJ=^4oOqo0hSY/$WQK4O6{G:Be&FWK:TT(#LAFI{Ttx52[#ZAM!?a&3J8x}&$+4!wxKF=_yxSXI:f_fWpMpO/WF,j!R/j%$vo[67qst#rI#`OZ4{~NgWbDw8c,jnT|*fx]!_<Sd`|l*Fv4k`N<h=gpMGPa,c$,66ApWV|c[:|EQJxO/JsRUwQO/RezXuE.$$/T[/r]e;">85>:#~JihO,KM=!~!eEN0MX~Br&@R|UgJ!Mvm<rQgS]nb26DW{PgA>"{BY7"z$w5H1=~a~<{+|f0&L]V*4~@;wH)cg]3]>wnI2A%CT9D)A[T>?)c?,mpGa(Hq@1nO4xgE!p|v98r7c@:;].QZD75op+#l|2[z*K]$@W61!{[w<uCvg]3|80fe`L;R[^p!57yD7Wq6T}qp$`mwP,b{e>663]K2^*ix4X:(xa#xs)|uP*#z}zB{.95r&^.I5tdF?=vvh?>+[XX*e$q|vM:cY1?L.`Z@I&Yr8v{Fw&J1C<a^aqFI%|8GwXzE[b^6`.@Y46{VuFyQx;yZL0L9U//=mp$9Cs*d"7+^(6!$hWlCsE<"U/<|>~(bk&_@oVz0n0NK>/lxIXQ*J)YYV/9Lig@~4h)<HG?jdJ42pKw^QJ(C/^C?+_aP~olbx>R1a?&Njezo`dxid3[8QmkNYp~Usp15E@qxMhRSFwc;OnJH|@*7}b{<]F(+VM7R?6MEt8b1p?]`2Q/a#2ld3P~W~5d1vK7P6;JDOr22C$zcjf%QB2T6)d1bVY+vT1O(uw6GqXV&X(Y4P!O!Y27XnhFKxj85.ugvBI&i?q[%m^,>tw{kcGpr;qj]d@Y)|32m%X|:)_Ie(HKm%):E31_xlaIJV&L[3F){Y2X9O)i~>q3tcb}gVjo4ysom1E[G}?/,7vG[ivnQ>qgYArN[UV4|UTM.Oat2q}^"4kHnTJak9l*di+k*GiKzDpcIG7x_q0j{!~eLDpk`Zf[dPAH8p[vFTb6vb!U8zI!DL!6rDvsUrW3JzfP>|6Rg7jBZa4dmusw7$}gA?u.B5i"m{m3~lamb);_XVROwX8Py%l#=**@sTD)$=+eqH^u?Ov4=Llw>3[~a>U&cidQ)yN0%`w>a+FTzz4:;c(p3rW<ePfTSga!a+@;Y+PCc=4*@n{zlVL<>?$$[C~_Vp"`Yi5]gGYqcur;|AgD7gA4W$5>RXZ0Oi]YFO<1#7ae0=%)X6Dav8^#^:H$ytb5,^Zdz`&%"@PDZ*`Qy$h+0P(5fo@Pma9D)lhx[@1X3I;MeBd8q1g_mTdBMx`k:Q(wiLm;%u*N:z%V,B[(%6cfKQHa`$_c]jqxYJCV)ddtZ$dSvh+JP]{8?rX"C"Svd`LJ];ix/QElFe~kLf}8Jjk%?@>/h[M_l$m#zu9[9""v)=.<kqC^GO|SrZ;r%l|p<v5j%Xx@[)5xm![D,A&h;g5GB#=<BcCj89HFeQD[})c#&!!k?C9pA{{v:3.v?`ru}DKb(VU8g[OarEu:4SQ_Z0o~alj&|hIq3xzrdCBw=u,N)]uuOo#8fj)%=IWv*AM7f3b56_*]ntj|GK9ZI`cyWw%!Pc7I121DztJKP:c9Y`sOn96vQdgSy|L"s?;<4m68]K%{gXVJ_R5wLZ>A"Gw$;#|eLdw^@oAb@*gs@;Rz`G^~2i+<wgOK/(s~u_Q|gnXs6xBK%TgbUKl1sB?@w}`EcoI,Zl#.DtRg2W2.VkQ=sU_2U!/BIQU?LsdaC&`<b,*(j^dTW&#/Qd,qU%Zpv5)X:H`H__aoMmclN+e:SF=<tqpf:w9#NFuAV`Y._K5ioY4g.y2I@k}C2e"6w[%_TIhg~JHX*1>#(8ZS6]Z7j+h,g^ggvxTN$S!;iw6Tx<i~yWXH0wOw21|y+*oiO(jC,spJE7Qge4]<WbU`O_s#qYeYQh#gki*x,kuOD#wn*`9pE!$L)67];)PR%h[7vb[|LYg__YU^N1(E<Fhl!+.}e@AOK6Z1{[;xobIt:YB~gz](dG.k(O?+frB11_nXJ6PMKB((0YC&Rbq4WExZ/M[vkerkMS3sH7C*"PD&Z21jxpG?lH!e&lU#=QxBOj+hLda=$MPIddJ#P_`h}kqk[W/;X2XBF1RC9bCjN+@gtS%H7xBfeS/C@[@I%az]Kr_gZ(<hIh|kvDaqZV#JCdfqAaOWQhXj+]I_o;~f(=2d(L*K<+FBXdbbe4|QCfT]?fee2VybX3xyskr]E[BT[,ev*ctKa8$U$!*Vj^9]WpicHWjp!v?klhev:o~}:H!AqcoQ8vyb;<+NB2Oo!T`33>oyY5s"M,s{w;wGWo6<ees{yh`Qh)2SKAJr*$]=3bQgDr<PIce0C7(H:KsX+>gtl3b2)pxr8c<Tfv2!A[(JueCvh91vKo1a4X!kzM2^"lp)VR{q%]RXrP/dL^i*|v!WEUtGcJ2W;v.NRKU"bY"sGodX.MZYD?9RAe89=lzVFFU]cP#(3n^yOJJlcHI/T1wcoN|]fx2n;EU+UN}8|!%s.;%:aW"p&x{uo>G2kvbkQ+1JChRmwk$EJL{tm&<2A^@(cuD<<5m_5G[eaX[r1$:fQ(RI+{n|ZFV3BPh/~$>PiTMjrpKLBVjB_rn`oD@[m/y*)Z@Vj;:y{45%W[+u@zQUfa}goqg)lP6xA)~P!9L3}SW$yo&zAJMKT/i<!01t/<7i>;O5C;Aa.w}:_x*2t(pBwJL4nC10[<0E|ww6W4`oj&tI*HlPYI=?w?$*QU@gSO3vtIuHKIV~[9@Vx1ciDQDM/>p<Jp)xx@Rh(/);&}f]JB,vziTe7eqyvZe2q&,:O?#jFy4mzOUf,`#(a~,=]`x}jDTn!>kI.$%yQCU_z~PV_hZ?~^`51c)(u@?+I0zg1~V[B`R#6WNEZNh)@^,]s#.8p2SCc.cGV/LoS[D,0Xx$_Lij?tFqMAUFsNu;a|X#A71}p1l~MBb+"09pEsZ$hT$eKw`a9YU+~XdOdM*{KsCV0KRz#;SiY<!>`IuPdsMoi;xn*e/uxdd+6SmP`ruQXg,Uh1$o0qkL7o&Qu,Wb0NV*a`P6:VN&LIfeFcL"u8nF?%@AK:o:zllz/Hs5Zl^<i>4M%wQ@[OM[L.V@YSC;Nzc@sCg3HcV;H0=uxpPaJ}g^Cv}qkegfu9;2|Z[)>8B]2gY=5d9Gj.!QU2|&MXnsz=b^[4%0lwG}^o{yLJJ`%A4B0mSTg,]p":y`WqMED~s&5XpmKY|0$`U2QYok9^pR7:&HPTL6ppS,DPah>r)^~pLCk.g,38HU)M*r]%,Ksg]G3YK(5jkHvy&z[:n5r`Ct*_IEg.M~>?vC>?bqDBKN6yqs[8_<4S,^J:o2$#e!P;OwPV~p)22`;n!W:N|$BplnXDL#m:|b+{&vzk8"t(*dXvzmRp@g&9plgB|q9$C9(6#4*:Kc}X*MaiaDd,#.{+}G^!2OdtToC^m#<M]3/X28dSdvjXfk<pw.bp~M:[yVVk51(gUz~%ciZ8>z>wdMX;=7q(6Q!)s:rvK+SyJJNa_yv;;JLhH`6KncO2@p?p4Z,"Bm?uL8@VMp:]AX</Y/c#X?6wdWXPaEK=A;P!2Y;D/0]aYejgj~fl"v;0g*moe9,&U}N3>?$Y^QRZ,7sZ~gsfHbC2)/$sU;<mPx)FL/TQR$)g(1.ER7O{,wTDR.f:1%1Yn5s&lD$A%Xx8}e=8y*3"32i,5ntT9.m$x*=8O=dL(e^5fj(.Qji"lu;"_M$Kr@5hf4*`/Ry]U?[$<R?Qar]YfS:cgfFi5X0>|?LYzqoiqN@kC)JmdhhhK&6d4]~aXGJ|1~xs)k}74T*J{mJhRn.7f8)F9);S<>fP~ITd~P*.3@,F@PF5qj3$kR]wAByhrq}C^Z?*eZ<ls}K4+"}+m)~~J_3kd,f7](M1p..Zy_8Nn#g|ubjyy9*RFw55M14EJ?NLUs#uVw}dOccklcO1":Q/hOuTz:n.1]UetlXbc6.nw<D^F)4f0XJ9`rvG_<#Q<j4tMj:7LFj]+=I<sV<Zn`:!%M:>1}tNx_oL2RCR{69FZ8LJ2&+3pS6g_/E3F7ZPbf!>bMg@bwJv.X|{c:J,oXbLlTUR~ePk~ONPp1^QJ%R=N8h,E)~`AR.]R96)mz{lR&+17rvG~%xwu(YP(.%O.xO)Q6&g:y"o>6*X}:~*F#lj~!Rx}O?EXPC46d"R6S#gycM8lmY3Sqh:YZS9$z%}P_^`h~MBBJa~HJPB5E6o$rk~k&J|w)V"w*=;q&,w,O7Lf)Gc*{z=+fJLrJ5VGDbHm/U{r+L+(JdJfdmo?ig[GU|p^X[KW]|l$e*5/oVUJZZ/f:`#[!41t>Z8E=egtT_xb/u8JI[anv{o2IKkLZ;!*O~;g""wkURo#kplDCS6HTr?l$Mv*fre%>S_nF!SIvZeP!fx%R0k0p^So}LU2JYg!5MoQ|[O:Af%VTX@4gA{&AVDigvo~=P3V;crspheo*0iqlR&Alfhu!j)t&UY5t`_^Fl*8_`^z`=@6?&q+$/jw=AP)+Zv{qV3k6FuaQsfZHtgnZF.HYZS0?gqBhF9UQWkz!^xVfqXeE=[Pbl4<jzoZAMsT664FFd]AN&1i#Q0jHbcoxokBa*hq_4nk)Px!f)EHjf7>^wp0I/Qe=9;(!u:9f/vG33xfC80Z!KYSo_5Y/%a#m$9t"!0@(Nf_?7~ZjxR^7o~s,S/[O[GI6N6cfKk=Ws2;|)8k,}&`c?gEzxa#sm2F(UF|*@T*1e[R(Ox$&u{^YcBb+`eFg|+6$wFltXS8>xTL,*B|+,Vm{WMXrieFP|30(^+I{_L@8>spl__Tevo&4p]?0|nv=]@CHc*bJ/U][A&;7{ha[%,TW00F#C^/Ib]ZR5VdYR/J@RNs[wC3a}3EhzgIAHDr=,oi$P*D9QB}.z<cX9dguuQ.3E)]mTX2C82{YU>NyYaaU^^nRFW^T3E03ER0E.Dx4.J`Lw&veUpPBvQ:#pD>kOIQ2!~ztMz_BkrkV[3hI%}+Rp{p~CZTFZ>S]/H&$,GN%GLt6jPLvo7;}H*Yvz#(BT@BEUJj/nEauol/e$)p+vPUw,|>3FjoI?e+ivO"diaQ63GAx[_]cn[T6F)0F*rGo9g88]7sJ](P+fc7gt@q2ew.VZRwU81|rO_ut4D2{ion?$dnU&Sw8o,0=(L+]aRC",jX"8lwiU989xTDBeqGziX3WoE`uPc=*|99}>D|<bC/wB!I2rwpd8`rHiBrvJ1kC$<VfhZ!?4yVa5^?h|S}Kr[2.^wJ&FN}:KiQIYq4dCi1R@Lr=N$SiQNr{*K]29Iu<lo[R(9ZnNAI=q<gg.Yu0IE[&Q1]%tY{m2Jm4,L~1zS~{14p._{J||fwhle*3r@o<rJyl+;G=#!/l"aIS^5FF0P(.Dxl<Y68LO)CI57w[?VWbvT~qc_SV)*"GOoxqUM(:F#Mv4>&M:*m0l58,Eao>]~:R}Y*`5r!b1M]W<)r,TL}`erGClFEK/UYBZ[#*`x`yLisI$IY~$d6UF?/4]~Cq=&%?JZSl%}y!D{ZVM)mg_"2a+gcae(9M_X7e[R%Js8]ohCH"eCgrQX&GWm(r"7(*q%eDMt%EnAe0B=,1,nV7[C>i+I8{&VzPn^V^a*T){~MJFDl?RdDD"|jG<]b99BcvE964^}B1KvLC,Jqeg3;tZc74Wa8"r_T"Gx?c,KEcPU7Uyp=mW!P_p/BQks*)Sv@*fE]d}M4R:e~*a@k2%JAx/<GEs)6.6FPRHW<[l}.,[)AzK^]zzXCI?=@h^Dut%7@%|wefkGGrEEM/2hbMt,Z@mFw2fmtFmamoDKIAtt$/B}S^7Fc=37T*#LCS&#/$d%`7RXn4WH^6Vd;F^*@mV*|j4Zl}_B23E0wh%d=@;,?#amcPw$W:3plyWyx&|uI[xz)kKbZ/3uhIhBdFk2ZT:N,6F#a)tBKuwFEig|Aw1dpcX[}vSUL(}|9F`f(/c66WB)(qAzWFHzyK}}MU9~Su1x4v}}s91U!(Jrms7N[jNN0Cc>r{Uzw,roB6a6PoF3Fx"NjD94P7K0dJFYi8]#:v~:)ThZVnj"Ugg90pEUov+W*0[7]T#sD1d%=k%5F}(1^V+C=<t}T_mK`l>dbYQY+a]wOxKq>ZW1>|FwUM.khs.|@@|POTPLtPc.ph=K=vfhI?91NC~J<)M(Pq]9]VyKgz3hINg8@K/5S!@NW.mt,6es1t,UXBps%/%{ZlMG2Z:rAD9lY][N,:f4pNi`+.(D:+qoiNx{@,N:Qi#mPd@b(V3>)>yCUuK8Y@BC5ifeJA|#sV>SY>]DICZpJygtK"8.]FFn4gM@k^(V@,NK)uu;~p#5`_A#/ZH]IR"?mEmHUpyR|LLspwc3xhumlvV+FqJ`:2j4%MJH"=K<=ENsOfOQ1Sg20z}PilX/ADW>aO&I&ruJ1(dUJz4Fr5/X`@7PrV74%#cNR.l{KTRH)z4%%N>KlE=u8o(2{y+?X<IV1pf;*Sok&^*#}hdh|:x|o$W?{D)C@ez]i4W"lfxnWea7R|zC8h*jf(%d2O==}jNb[<82dvM!UI09M/GU*x:q`$bd(rFNI}<b.7WR>L#r9=h3BS~GEIqK;PJ[mFa,1<EnHaKuIC}_([;1d{$$v05T9QOLK_"Az;/l?>Ssi84W{ZO~RwxrA0S&%kRbR!uHe>7ipWH,S4"L)4tInUE?eq]O:<P2]w~4;T^FmqlVIx;q)3|Q1L]8)nIkt;1@&aT:@7deC3%hUO}?u8ei9|.7L*?LWYB"8e<nUEJ5WKl@BQ|C^3D:%E1R$e9Ob,f@ksk%`+6>n5]R|Xg9O~Ip)|)E"IB5:*6@@JQL~^teht]aQTl,f+YsJJ$4S=fOL.@EN0/K,]oW<%w;*aVRhaS@bR0c`*Fow8`U4Kc"KSRbkER>Xq+2*jM[]7(NwH0HU>Ocr#sSUZzw.%(8nC!NU`r7yODaRa4ElAG<K3g:PcD}Wpq.V6OJF>gtinBtD0]tK4H}Sd4}>nag#tXFuS}cmxEv2v+75pL0mA.P5D=ds_li$<iBe;#4TXGb9AR[(6j2r%MJngU,iw.B9b2@MK`ozrhCCroA~`FOr_^i6f3W;2*hBcJ[XO2AsERUO^:mr0HZJLHyfkod@[aI8~;LR0*2o%vwO]hc[07YjRW"CVc/,V"`j;|[?&YC*lE.0^4Yk+sL_`;%OI@Ji.A?xp+BN^SKra*jfOP_2/Uyo;!+>G9#ZxV.({o_SrR:4S6H!c%1]6+y%e.[U)^qq!p~8Yx}i&s+]1J+_$,uFqq1G~V@[1UUMX=J?wVwm[o5|K=lVrK`NgZX6KbuJp7x`:rmO%#Lh~m(W0l:=B!eDe4|k2r@4q>qr4+wn$[(H&fxM@m@&XU=a8nEWsnpux_zS>U[M/k5&/b]h.?F]i2*Vx=wYhW:BNHG/o_B~J]m*{s+p>vGAHrzRA.i[hwz1qQwJR>Op$v#w@vD`eJ(o"F/`e^Q1Bt*S}5?|l[8G`j!.G{CmyX@i;NM1eeQ=Nu+sU$^a?beuLf1ZV54I0(&xo:nX9B6u^rR:/O}H,V#=)eD(n4nc[fqna=3LO1$(,%bH9GtE2xP{G3PL9Sh"@ySp@53H&(+S=,HT91bH!R`8P@Z>0&3O6J22a=DL"fF&7bB/~8Ci:Q.5%wsRAv[$6/sM!hg#f,H4;d1U;h9VoOsCcJ]=l`B3A$2*YsCx[l6u+,SD3m[K:MG8]":2D}0TMSBR&@PD5KRH%*<(l"c$4gV!wF&MR`e7&}!(.OTgJs9QQe|/y0mLU<3WQ_NqNjQVz+[",)kEpksp(DLD#YfiT:v5j3Dr~.X(}}(}#>f8f!T53aCI5P4@:yHO]84&3x"g`WV?W_%`3<>P`wo&in2*o19cPa$n48)#y+ff,/hP8fpel#H1Mr,@$Lg&$$+q4J9nuwr}{fn9`7hIZ=KjN_D#p]gB8,8w#8bz(G9f6NLDp}[q{K]K{G+$LIE|OY%:==8:bzK9JXTwD?y{,ae?orZ.s/6zDBy=c.4.z!=1/Qhg.u}%u]w4|u16v`p^s=,K9@FnQr.duOUyO}iAaC+<XP`ih(,ZB`_wZ>F^4%h^X&y&C#M]G$8(kS0tZp&^SCY#1M3:~ff1JLiE$lV7(>}OmSNqx&2#4B@Jvo&zTObL=M=_/JJ0/*KZaw[J6l^@`(.#`6iiCJJ>y#[imZ`]|p+nDB4[T/?P&]g7HBXr:)Xo[t0(jNh%#TGc|!,0ep<0&AC1k,y]uJS3ZI(=2vu=j?Bt<"/3crwM];zs!ls0S7Ryvnb@dz`(BF.+>FJ7=dGnmWDLye>sb&goSlQvv3z!DGe!ENFW%##xT=fyV^NkPO.NK$LH#3csn)O"}06xmW*#_3:x|9$fP<}[,_<s_snHCngP.#U#%spp9&3F*wK"e$UM,~Z]@UfC`WSV(vNkJ+UZZN:N!f@vYB<gzB<a&rJuLFRkVTO%}*whXHlej8EH,W!n`I@cagf{:sj`CC.mqcpWk*WFFM=;ux(pu_{s+|ov>pT(C<UaZ7@Cai/V9wehsKp%f@v,e&q#`<eoblL.2.<O8cM8ySR@5M#A1Fg^j#wK+gGBw=d}qIp@O#qm[#QHUzd1ZqY6;j.QNnGW5s)~D,un!YwwFs&DmPQ5cUF5Pd#!!KoR/ms,s)$yLtp"Wf`awj8$5,1wjqkvtceyPC_gBvu#cw]WqpF0r3<0B5mE?kJP]}q%Y@j=6axJt$VsvzLkXZ2nBu2YRgz22HWHwJ<4ek3):C{PNpXEPymAlQ=m/zQV%p`yd)*0c{`/>_g9!BwL$p8/0{O<[P0Uet@X][l)B`t1f,}DWc#p4fe8J|a.T4&(#MdO{,T;@OyTMPV.V|5T8;~IF^QS]{>_B.OZokX7jd=R{,h&zuGT?>Manzp!DWh)s~H>@E(+!LK{amk$JZwJp|}c`rplY[)KLA6|ydJM|eq2[^2#n?.rwZN9w?K5B<s:|26["qZzz|Vb`th|xzBBlHY.1um$cll&zAn<G3$3tCyhcUAvIL,F*K!o9b_iz0LVU,;$1=$D/KNZB=dLdJFzIT;xk"?uBD^A9gl,y<64/J4t]kY=BX=@wf(Z/71/VU@b.qzdUHk&5Wy<94C]D57x<2bHd9px;p$:{m6Mlp"8<I*"`::fYYc^9?W#/i5|yR){#O*xY)~yGN5,9w7NE]WjkjK~MFv~{$[<Xci:{=sJ?!JVp*)FfcdX=CH%d0f]hYVp*r<d$.F1|{^zv!lJoPYY2q4(K~dXPAIFk!0/M99gepEQy&]Ef)J;|MeW"N*Y=<74Q`!IO5W(.]iY$H!:r;X?VQwUn;}z41sEL>P`Q{O*%3<I&QvEU7A6BsmGvKtlf49>thX18FCM[IKF^cs8d`e7@IOqO(tls*`/OotY/4Tet>Vgai`2]ZkJaWvoh~YDy5J+>53a!7KMHt`*/:u;IvxGDelNI>yzPfc|^56%]2F0A@sv?3Y?tY[_*&rw;O9)cWP/Qmv8gFV9}@%~jO(zB}ZAL,Q<w37xvYBN97Sw_dSY]$deZ(4,(m6()(CSnsRFqk^lvf*KxXOme4GU%7Dq8(ng~w}?er@^Ft%10Jo/]9a"ws6%.&$(*1F,{dRe*Dr55{7c(qf=5yt==<0w|k[mY4"iIZ!itNK%Ce+YkAt~L|XVuLCZR:8A)/LoD=ESpEG+wc=x]4!eNO?bC}yV.yMFICzc:R{gMioYh[Tm4*)=B{P2rx46;e1&=^5nz27@7Zd,v8}3dl]s_,gqezgC;3u:MU)om.&c|.i)],TW4gk1:Dewk%VbmE!X|/BbCeO~zIe_lmb*/mFnNJ>eltPwqlum$|O$,CsPNw=A4`iJ)bx+X;a^tPV$H&7_noNuu=m@+Y(3w%ENiag:Te&"DHinN=r~V7vq&lz;kU?MQRG]GALs260p/loL_v;UNBRCD[0^wj/fzTSX}0?O5gPss.^S$5AvV,d8?TtLt<@:x_`:&QU_)tCrZX%Qi~CE<TzN1*V"1{$v9B[Y8o#J9~XI!hd^9{Ks,nkhc,diuSisI4L"F_`NLF}3nlJ~aXJM?PPItUH9&%eDi14=f$Isk:0m;5L0^!7NxOS07w#MkACoORO;{$V+Fzzlg7f;:0W1y*|w)0KI.1ia6tjFZK,zVMvhUTkJ5TNBtHw]Fr[$5edS72k;pD9tpEo>u.IbnYTH^<7I>E1]=bDHe#WS,.slT4SOI;@XIB!f,hDN&ekD=^AJD$AGDJQtcNxW}K^WtHr^F7]XKwQS9$PZ`iRw]8xy>ah+z8S.qY8JSs_au=AkOIr(6KGEzN&LopkF(<U7cMY|ZuQ2n+FFO{NK%~(fQv{wr>~8"~W!pZYOFpCZW9STj&Rau@$m|(f*9{/whQCEs"d/b5bv3#1B[?],GmGg4/.x$#g90T/zJk0$uQiY_i}:D::(yfYz$*,YD[w23kOci]s[v+Q#):hT{vP!Bt_3}0]EhD,h21kG1<N~Ap^{5_uF@#:}kgiDB_b&l1r?PquuzxpX?&J^F|P]FLfNDT+%jvxP/~~#Zv}yHA5!DRSi%.zzX{IR}xQ/LNu+*^zPvB`/}auMT8BKlP6UqSmV$9V9Z47Im<D|yvu={dPEs>(3S#R?`fQQyN?X&vI(Uzk_n?KmzRuI2_^7EzBJ/Fb&g7TH>`2(L@i5{e7Un=uy+:+!@wm$GBDs}RfFLFo*bbWC3$;~GeZ&([iDh2gd;:G5!n1Vc2)5:8?Mc@?2_N(7c?#3VVa5fDM,pQX3Hu<6n)C/Ltd7aQ(~r}[^o*nG#Ka."F!01t~]{UTmP?pk*<?MTfPW/3(*Gn&c!=|H[py}26%]P78bhS&(=R+0RR7T>.fF0hYHSoFsX!pkl#/^T)VqK$dCQ93R)gWl)rXGz^wrM|IwW5?qHc=DhoQcHS~f[I5NVivlQ<=z}I9"k(_u~_F(rCXo{I{=J<fq/dwi$*bx#slyC+$&LV`0f(PkOF5/TGklkyK#nCYnX?+Ga;I~stt"2@xkcyVQM"WffyN:2K%%t5:<yO$CD7H]j,9UE1OOiKX<mhLp7H/F4k]BwrS*I!JSINgqu6D}6TMd{Md9b{z`uh^41UPzHbMWJC:2~f2R<ITE_xj//AwPQ9+<$HVuLj%ayogy8~s;870{&#tLhxKh=X@3g<j`()*BQ:")ZksKlo6^2rLeW#xMuX<$1^9}"a.LL[jUZH)5|!Wgy,D69v.0vlt]$2^9wYR9G`I<6}]>!W7d3n=55w{(p_H,3T2^1OHG{v~D>["sFU|rd^o6nZFfk!XMDJ_JLS9%Zs4&p9fiD4FtuoYPo3uB!TNj}YnPwWss%rJ`.(ZP>#Cve/U~xT3o^)6bdlqS;B?It9=V*[t_T<(|j$}3G8Q$5x%IHBIy&UgAEfG?YftBTs0iB;k:7cW]H/P";clQjC,sy#dr/B]O?]Wd`B@Ja;VRH6xRh6&k#U=Nn(z<9LV|N`$ldpi1id;TP|!qGF*PXL!=^Y$>2jPm?h|X/fSwrV`Ae*C[TD`+m:TtVk_zMg(mE5s]yJany4:8:[b/"Ux|}`ID}@O3bM+EE;fjK`Qu?;{>%>oo9yjtC=r=X(wHl<aEi^@|sm.7+T8g?.Gu^0DLb%Fn1F:"h@OmT?(?VN@YPniR*D[DLb}Lg4kMOf`P%1"8)0:f4S}"opCRY1;xjckI|Bd:4q?EK7lG{%iWQPz)H.AZY=6XolWP{H3?7f:*|<jobU&zPW?q6`Kis4|GOy4sz{>h)UL&7EcAtg(TP"^p)zEr:L%:ycc"]iw[gKf&E^b,j@Zu*gP#=N/2up83v_%K55svM%ZOaF1G9)5wMeo7tn[PIS<mgj|Wa+BQw+2WJVd|R+SLC^wL`F(7V5ABSaq]vQC;+.fecn?idg2Y[9S@e2/z#<+!ZG5Ge=v(987&x(5.LZd}K<6Ks0=)~<q4?|&yvK1&,uRx%1o]PHC|Yf.a)4Fx+_3V}7Bm)8U9_WXlli?!PKa1$d<+@#j5&Jz+d_rE{v+Z?SzE5&1g%+sIRv5Vc8mCVeP(Q^1/hx~{K9.GYj4UUI%seJjsQc+88/^s+Cu],A|5E4coOw|[js81vm4M#u(lHb8RM0kZJ)>{vRc"eS"nf>n*M+%}CD@ew,>@eZciZY;(F:td|OajDIQ8z(f+XRStk^1bZN8moTkWSH/wi/<t~5un<63sDju[z}ZZ=<&~>OBY#p/UKP$j6O/Uo)X;00OX!Q4w^Bub(S+.sV`LSgo62kIjXlHASu+H)9(Dp#opP(b@r[|sOwOy0Fa5#H[;j`4gW_b@(x5f.D;)#ybC|WN;0n1Nz3O?NZk:ZeB$;u~p+$}>5EkV&<o0/Q}eF!RjGRR9W^Ukv8_|s8%2EMT;^^{<</]AE6n8vB*bOsntb.gg`SQ}L.Ei;lg2aP7MW[<P&hL|d7s?05f^={WBb@b?XIr|^h1"~x3$Q8$w1#l!t$M3[R{yhgg2dl.jNr@m2K2R>c(kZjTKM@*rtWW?I2eOy=@LwM(:%Z;XhFN:YEh:Po:iosZ[TbzmgpsLooilTi@u9v+i)6mkLGF.Ai1!RN.5$=?~PAbGq^OQ}O+O=|vA:KZ2vJS~>ShS*XnaxZxsgnDQs+iy4`CM7a.DGz4(_QJN.CHwy}Y/Tm<:6eYP][g}Qg)^.)M]xe4.e~mY[dRYQge`V)L"R6/++k;TH_<~0a{2eWZ@5v8|9rv)>C:e:xMqvs|/%&ze4J<2O3.u/"cVC3GO>HhH{D|bcCTu#f!nNwlz#pCm{o4mVmRT]r.y35+>ldN9D],q)"W~`|8fFc~oF@MM7.e4=vj,Yj,.qvGj+M(q$Dp,ia]Oc7iD:Fp!R7?~vW(N,&^<ZvhQ#ZLL;g*gO]BAS9lx@uVJ9!24a~kFz1Z>4tLb#C#He;l;Kz)aRgRak7b.FWeKi@lXO[Z*M7O,|XLl9iQ1DOgoz1{kB;(/n@1o|/h.hPN48b5O^x~vk?dCSZck[^`N+f:73PrD~WSR&Wr<HZ3o_ltf%C^7eUH8byGW!=68K9/jd_p|Wb>=l#^mxzaM9K%J,WX4`k5o,{XktKH6~K9rIwqq^xQ$.vO2kuC<=sXrsNFL{Lbqf>pXktcJO(6pD~t@M/ZLCw<CFl*vTbc%Y^8|Ez%}TF:iF^|r6)/9/Q]i_~rfWxSw~Lg"kKeiJ:wxU_Kz7l?R[9(CqD|Mdec9pX~%c"Uy`Yf&hmK9vzb#ilz~|>B:[]H]arbW3Y4V/.=RdZqLb`Twq*+TjyKvZQX_lk~uM$@vt2V%uhUS8pE2iLmb*oY;96z7ZRbOF8l4kM(,loRrK?:*&zG81Lh2jo[Xc#fWc5U_C4yQ8xqRC_IehYk.CUgQy#/eThDGH;6uB?m`HzazxY4NhRY[gunRDaOdS;X/+d!RwBG3fyW5B5z>Ng0ZO<p)SyJO{%^|iw,0E+Sj#4RkW}UKW@*Kqr(~.jx#p]k1.J|qZLFjPE%xJ_QE#l+sLj?.4s8|KtCjww+"4%1FcG$!/D"STVg3=bS+$v82#@j,z+_&5&Jb]GDCqpQ@G,I~I_cDs/y#G}gTh[&UqgU+05#HP#3Xf`GtRyx:Uo<P)=5IZ8_@_M!{zz=VQt?cZQWynKy&RoVLw03++<#61vF[pLVrEJ|]6g]dW"d+}bmn0zb+?a%Ot(c>[.jHb({DzEV{Yx`FV=.)V?&nt0pP39PJ`UC:;Yo(feeR2Ja[<mvSb36zou#Na"X)5K5I83%}K|Pa83uba/NGr!+_yNwWE9^@+;$>Hss27OO,2:z)p+iqqy4E)vfk?x+L~R^msVjC|`p@Zy)myAM$}s9%]DW{FBk(b1kq_~Co?r.4rt<p>A6/I8K*YyvcZVKIZ2d)Gy=uent|Oy{?MUu<jHrV2~;hRP)^@{Nup5{1&173Bqq,k/$Ej,,6"Rn]Y}]CO0^M)ANLX4_U.9LY7JB<=FMLJ+W3,9>K|E>o{zY8R4KW5=6`uffjy;0&9bztw]s?^x7eu&J_d]b&#/KILye*2yW1igsMF>*]U+Ys2V/&R`XJq}ir8~#:}T*DccGLh2M"]oprtf}9Y;>5sbh_vls3?1rKw9rtupTH+qM:ErT$fd"`)W%fH0x/L%;gq!/Oo:{Hg1nCmkb|fo}c5Xf2uiL7YthJVZ;KlJ2R(j{V4EC{aNcp!9F8XfFD.YP0:#U08<XZ|_~OBIp`6.eVFMtUbU#5Oa%:^Exvea@9f<Lzn@j5[VS|9Xqi*{|_p_9M*{589RD.(sK!1e;mYrqW`c=uvA,@e8WVzbr/h)1$c`!p5FK4wbn#%o1yGQ=.%b5.H|OGQnB</f^@?rrb8*T;dbh!ozifOHY7%M%!zjp@v_qAT|n~DMPb;tD>W@b%S[ME9xh`pVjHfOY;TU{$d^yfn]FdG)8nwJJvO@d)CD@?6)PG[`aF:PM1fC*3,jrI<cd[b4H&:l0,Ilga9Fp!D2JPIsG"TwI6ekLiYU}f9)q;Y)UJ$vD)>Q)[+??imd}7U0oHv/3[#cncEcWV6!n3{r^_P2!jE5k*]LCrL}=S1@8,8zv2JPepM{g1~_h"U9RF`%>$8q`.f.Qx|huAm&%3E:JaIgGSBt.hl_w^vrd($><4qz/gL/GDQYG&e|5,nQgVH^t$3lLWnD)}ri,==F}z,_NbqC1P|fh^7mRcrPWm[<:F9xwY8Yr3f9z<c{4y,5W7G.Xzx1y8p,)1OO1)dC,E6nq!s&IO%"{4jJ[FuVca&WhR7AX]d9&Son7j]P%/kh5N0QF(+yeL/+1{4*">+<Lw1A?M*e*#N#yWBaxPC$^z^@T(J:1ok&a_>fi7*pdB46zz7gFW%z%ppsCf3"LD?xdr""wRu[=n_Ju!2`bCE4_hKWVTB;pt/qZ|dMiUsM%+L"z6B@GA"lS.A(aT={W&shR6S&4P~HKUY<n=o8MLrHhTz[^Bl#Zsk5;O]+xDtG{X_1Z=_ox4[*`s),gv#{@2/L6>)k$fR?tFasL~LJ2Mgs]I4$;OQ849gknl~Z!9CfM6CJn1[Asj8)`r{=l03x=nu^x~p|Vwm8g?t.!JWnfz)wiRC!7y>k4ZG6<=$O9n"#Ou?1VE*Nyw^uBcwSM6I%bz^!w}hoTcw]Rflyz|Re(.jS1)P<Nb@HUW+TL`0k]$~XWm?Xi&fLv6&H4(9Z=57Dg5NztC"+Z9=RV!q7s3_>brbV!=IG6:`%F"e)Zao}vTMB(;0m5*[nZv)7kIeSXikUDnTjeX5m_^FrF*i;tvgY2Y`H@vSC;lUnyOU[E]@EXIa{Lxt`Xh9"XM46v*CN6dkAZc]*Be.,*U~L79ycLqvol;%fM~ee.3EU@>I+4r[CmjcT"i(Hs9<JLWjzbaXmuo@PRf$0_p3i,.2})vVcK<;J6Y?Tl^o0kTbm>Iz_}vr;Sv$OV)~Qia*lcv5(:4xgg%]ps:"D#v=Q6NBx}?![3u.s%(V#O6"<<Cyb.!E5,.R3RL[~[DQgPpg>GF4z7XMI**A,`q3DZX)zkwqx4.mH_2]2l0^_Wt:~4Q<^8+flNELa0taEDwW((5v#e5+r<`^:k2XXim_G*}6iz!pz{]j.q}mJ)"NRD4[!=I0?h}/=@!q$~4j~C&e}4Vd3BI{w.YUWm$jWPG:oe3Rco}yZ?:k/i)$fD.gsjK,b1DtWb7ON^|~&l/)5P9v}dqf8Rd3`]05jY$x&_WU[HJ8s@ZKyHf,oZ"UV`ok}j=r%?RGYNWjBu+_AM`O*QqqwyFjyj8;8yUIga2Z:;fr><05~Nx~+=QT?|XzB7Li&?~G<YUu~ctP"fLCUjFO.t0eJXAJ$Sg~rqQ}d3eF.faqx*{ChcDXfzBV;2E%V=xus,`{3):#Ez8sz}cC$e]Btm},V,5d6Sl%%pAOg2sIE"wJ|<+y[5yOTUs?jIEGC@W+mu&g8Dv<T%A4fEgOz+zx_0/5ve/^Nu=j$dzMB/}9]z59,7QleH5w<M7kw}ZHRbP~a56*6g%U^H80:.]V)KPUtoUWuaY5w,Ty]?LGcJ}WV22q"22o!ENV81D&M+:h<J.{ewatue87PTB`_UklVB,(D4)J4bwvP,w:%s7Q)U>.NZgba5aD,Rh{vP79|?INgUnmVh;a[UGdrVuLCwG/A?M.`Fx8mycxb<d8+#I+vYy0qkL0VZxwpb]#CBk`BY<?G![7I&kQbnkJH^Gv8!~xkA+Ionxo4Ql0R69,+CyZ;P;:Kq=4]|a&+4s98|xAg_2Upp*9yr!w$HEySG/Pq`~46Nf|=M]&RNFo&<L>|vyiQ}tZ9FRH5ufMf?DK>s8}i;:L$(eX,Q9RbEz&qu9O34)/]#~&q2G*N6BxF1XPoe&_<U+gvgEi8av_ALkXL7aY6zobQS:x=^yV_&es[wV+vxvbOTi^2_5G7Kp!!Z+csie3/;7=E$cFmkF3%HT}n`,(L/^g.ei99eI!kTO}8b;UZOt>X)V9WB=|1%F5E>"`qbrZ/zpH6)pjBO[#.#fJ8vX@_iMkF;Tf#xMtSW1Gq}l^VRLeIDYa=ef#[Ut%5e)bEIyf_2(8(XkSLlDzIo;TedC*ZdQTmk}$Z3r7J&GScM6XJbKRh_ks@]i2%n5<r,raK9WMM%sU*yNCF;f<@w.r/V4skhnE2Q(M4&]xL6HWBq#Wbo%&pJ_v])}cxLs46C|6spCI@SAr~a7`S/ThK5J0XSb146/PVhr9C]RMjoi>sJ$HQQo{MYq=3Ej{bx,~UGeD>=6ba<_+_@.gc9&LMzs,m`L8ZY8Dw^e&|nho<A,Nw^)z^1[q!UQPEhr9V!{V(aii8%wDm_`Nr1GGe5OAxwADhdtH4NVsGE_~8c$&+,BZGZaX7;Ibs35#`eB)E2qnc?p`Pd/hq7QKN|sAj<y"Kr)yc.=J"A}!>Q[E1f&[Wu_4(xS|Mu~I(QfCL$)_U+6ki=Ne@`@HTlB]b&I5N;_I"LR{yK^oVbycdI90;H9yHVau|13dKS$!FZ@fzEk!:c#0QOgExGy2OLI?^R]Ud;$IEz8{luFw1]j(@,d[wXpiIUM9i=Mz@X)pYl:iBukKWiKRA_,G>((9O~AuYmN]{3WFq5tf&hi^GcajQ/j&LaKVMrfK$F)<)WtI+MS]?T"`@S~3:TOtj%osDqX`p9#c>%d`$d_iYc!Xiy=e)N,a1#{0^Jn>UmdW|bu836P}f|1Fn%X+{NJ2gitz|uBRZ%1L1gQA=MW>h+15N%!40i#)s<.)Ui}~&22Vx&Wu.d+.exu^p})1//_v?D<DS@5T42Qk1o5EJ2Wo,,T;p2opphS^2`n7cAvXZk]Qc~%>.vA1yLb*/VpKVImSfFYp@LVta/47J;B%W`cw%CW2Dv%J=U4,cV22Iz![^"7F<1M`8"h%BZgd}~u=3[M%U*K!YbJ:E0|=r5mE9U@T[yyt;K:/|[c>PNc?8sHuil|{~|]$ob,:@ucmL|:|Fp~+:9tBCkXB~WiJ9fZYhXBptA)LouaH[$70tKK4l[kl6R:|z[dU]yr1bzx%:JBV8RDB<^t1fbgbB[xATibwBG/u=l_nFCAryd8gdOXJQbW%q!#QGx?>vYH}]=WGlzKm5!5X]]&Dmk=>NVf)Uy9J&d+NLd4.ot##`cUrg>!f.:r5O>/g.ij1H)V8_VEDUm6o!$xm|J_c$~qDrwO9cs[BXfDCQXeDpq,G~nc8<iWx*hcQ;Qs%3a3{&JO:W3upl>U0%(YMlj:e$%L|lAdtB2!K^`y|BJ78.(x_T[d<o|ty#:Rs70QRsM[oQ&mFy+[#Lx;|pM(8[u`?0~$A<<C4|+kYKu8)yNIIEm]a"%l^q4)JSaF+Na4Fh."cSd}/hx~mg@rNX(PJSglG+u2vn<">ai.="8bS<D`hm7S=0feNm^G&JH3B4_RIIa"f5b!wX|%;E:@gJ9sw0.>uTT4Fmo"8Z#_JjWwDQjvp1d|[{D1tK@~XmK$?*XW[y~MveVow23|`I$$f&u<>.8hwQ1V9B2:c")(X?S,*pp3=?4]1K[YPXKvyt[Fnq%vlu!@CUb40s?dBb`+[#G8xmx>s?D#wB5tI}Em9?@Pok;V*T_R2);4|3//5|:OG`{.|qSEz[ohPjJgr69;@[l8{I0]9OHA@vs[Ln++U<ZScnBMgJVbwIEnEcCquLw2X@y)kEx;K[Wanl)64v5Pcp}u/x4]!K}.`<nS/_/IaXbqrp6~uM1sa3PANu}*[eH8QMuiroGzGgsE2B$qii)_./mpzF0(pB9DVpmbs@CTST5duG9C5f|fA/W62I)ONPvfpT{o9yi~Vqts@q@d%7MWYyHGr&VT&N(edwQMj|]X+Fqvij.nU`j}Z_`}2|sqc4=o8Fx"F+._fmY&I2(8i0pg!#ypsE44lr<@Wbd$I<j](tk%.>Q@c_R#zG2tfv?OmNO;g=|`(dnEp,e?GAT{MSmK>^GZa8Gul)QBu@Bm4&!0)w4S$[ECw!e3yzliM@n*WAJ}SJ2K#,>4=1/CS[O:(}1yCFBbFwC]lcRL5nw/G5pQkju_7]BDI9y0SeE$,")Y*`K},7jl~SXCh5|p)#Pgi]G{{i5oTcsu@Y3D^=+7W_fPk4ulvV21w:q}.vmKc^@9!_u(uwj_7Xy5;CBVh<Mvu]^{]/4,jY<$L*eKB[HRQ"r1;.2/krg`@,m;O!MVF~T})v%X&qv*YNOz~`_B{{?(eT}vpZNex=ZqCh95G/mqLmdtK.ZmkVBI(fH81v_~6r}rHLR[h}f[t%bYO2o{xyw1#}E}UMpyw$7vW=wU~f)D0xDW0Sm];g4u.B?P~?i3[j7k>FH#vR#:`IpT@Byr4G]iDZEWQ&Rnv]M%c[%uehTF~/1EhzR~,N%[[>t@da/N.{RgNXC]p`&Q9Cl]^}Q(Dz#[mPq^(=|DxH+>JrM`3`ZP#.wu,MGb}sbJYfOOi83bamO|I,ee]p6{mt6e>^~M`=C(D,woedIC$}H"3}2*xN?1lZUt}jeHPSCVvj:zx:ku*e7,TdwAOlU9y=^Q"@%z#A!&_D0DurXs@W+_8zF(yn)ziYy!wT#DvJ`T>q$K~oQ]v`(X==niVrXfe=[F3*&V~RxjW]=<I;g^tv~EJ8^0*E_az1&f#BfK$6ZW^h{,ZO2n@i30bi=fE_c)l40Ir::jWr}vU_~UOS7V?a5V[8M/g>[%.BZQt3[gVj)_lw<tOsG~82q}vGx21`/[Kw3QeHNog|71ueRq<o.eJ74khViA75*3_zEonlK5@C8.n]pes03M`"JMBpM9DaU&gjetNO$9lBe?mDGDb{&g/h#j*,<E7GMLyT]emZi=u,G*(0Vm=nH1p.NPy#7SRlbsImX,%kiFYvc}BV4VtbQB(@%xhb<PJM[]T!us?C~k}`aL/nT~VA/$r/{fWlm7diZGSem0}CUECp2w;N9!4!j&MH+C=K&&tB<`f3V8%J[~RgL23*9>E<,DC#v?pB&Xh1(7{>XcuPw:[TI/o=0zw%E^Qt^%c_YySZvI&v>]7/H&&1C^5&Hbin(2H8.KzL,D7!b#el/y5Lv{:GDauq_j?`x/5hmA;{5$KJy*l.ojY&Tyv(<h$41&L(oZ8,Nl{M2t1R|h4,C(N[xOA,+:a+_iAY3Dk5:)hX@3%?]yN4r+dQ1MD@fz3zjt(d>L!1#3cL/174bblL{C8]~*.jCOY^tpnvatk|q.Z$jFbw;3Y<rl)Mc0U4&fl9uS*qj]?$5!5nM>[xXQd{>Z7091@~eSkz1h413zS;vHqx3YHDg}@[)V.P>X$&&z}j6CKNh)Dh`jC[dz>Tq`R7Hh}6KeB,~wS{vMxsLHyPoE&,P?1y&"cZrgYuoPCtA2R+_`6f)b^taB^yhifRS`P$f=}L2n!_>.=EkLwj1CnxCrM)uC2B/mJ@?;nO9I[`^`9EsdVBbBnaVGypa9^`3~*#xHm4I28pVpU6W>)#L*9d]bH>%;h"EPt[GH<<!N.aNqEm)RDO0gT,(k>}r9")j%%B2!U$`S5RDf9,c)KwER?LCIZB<S!@rKSX*QT+9?,LZl{%l/8eaE3E@!p>aDm55g8F&Q{sqgmrHhM@k]va7NX2$0{fH2gBQbZRkhsOIiKX<<o;ih~Vp3`k/TL8w#]~D[>RrFE+sr}JuR}8g&`ky(ZDQGh?1FH"oGk+]e,z1_CYjF6)+A<kn*b/S*x=l%>aZY|6n(_6:|U!#An|CD$XI&|7wu(pZ5B`QAG*}Xy3P`V6;VOZN+e_Bx6nJt/dkbb"AaFa6>[.$:0dM|S;0*57YI|w7|x"=+EG3"l%nJS0u<"8q&3+18_d!2IO^bdhe.j5)9@oiz{PNj!AXYJ*<hZtN/XSmL>SqDmE{j`j{Fu$^,zI%P<mEGHfe6r!s|MceURR~j*Z:A6#mWb~[*QL+*KIkHh=!.R_UBt&s^7`[CuWO7tIr/}P|6v7^[mernT;`]7_Y.Z:,P"us5xe3M`+w~L>uN<+9jR~^]O"mGJ"mp*yNQ3b,vUUl#g"/D|%5ro)Jop[W<jB6Atd~978x&:GLt4$o}z_Z*&un=0@HDsgdz44wLH<V:#))XJbd2`_#aq)lS8t}I$c`]@Mc9)i@p732+opRC:DF8J)}^.CP14u0A7MK.aQ+e+H5pT,rHS2+KlHM4n/iRe%_tB=imhnD8UQ{j[AIoFD~fwR4<F^X(i[,H,|W)v*gTq#:B%z;P^J`KdEjAO/H^8D;!^`/9P9%)Q+V.fg.?~lUA.Eu][U^x<:fms{5a5O=zRH1{/z#2ko3$Ha]g{TEZF8Tt{zz&q:}<z@~GjV3WlP;1n*@Ng"*IceW/4Yi{n!vmjqAQ/Uv}1G|NQ}ru`/?]0~4]O71&R,l"F.wj"jiwD4ztDEV![4T8Vp]&V78cN!t!aUR7P%^rpcN6rcEJ<~C@1zWzS(bQ)nBY:XuK{znxHO!xa*wy0cC`+{q&mT<}$^GDr=)yIkqomVyv+H,czX$`h1@fA]YhS_Elx[#lr5zA,9!2@rcl25:Hg`N#SVBB!ysDfy$:x2$x,2[m#lzy](+ij,/Xw)2OI&^vrKWRUCALkv2~KRv|]X.sC^VF^$9G*T]V.(N.+3mL(=NCAs:D;@(ebBLANIFRk+<iv/6C?fp+)&z+TS_TYhUHVOL"dsHAg"~6<*BP>Dp{pYKbUm^oA%<t6_f=71EV9$pGH^z(23)hIp_$?DD4tdFd1]WPDzw&x&~0eCQbNhFGx~i!eJl%=,ISkV2|FAUOhLoMnzi&?.v*B_1<_Y[Cq{7&H=?1}bR`ZJBC{#[9,`:HEM6:]"fUs6v^IS1BPJ=|QEIk@GH)&x8LJx_mxF$V9,7@<7r5+;R,9*Jz?0YHwF9^+tZMO#S"obi.:RyIR~sS8&RsrOEp#TZZ)x1w+&FqPcomy<cqi0iuajTv}1;d0^z(RXC7Ool=IuHxieb0QWR51|Prl<q:54BZF?{C#&+Bo+>I80,s&n"1KT]UW`R;ypiR3?`?D`JQ3bSG%l0;0X,bvVih4JJ~ERSh[n7NYSDtzOS;iQDg0ep4lB]ENS*tcbQ"KXv)(Az7^>v`|^pi2K9I)u}41f.2L]k@@1|WrBEt*4aXKo_5*sP]eGKz?^?}`/iB3kCIBwNjvfu0|<VA<*wBnd[i.UDyq/$mEG;/W}0Z4l3_7kmmlJ.5SD:LX"L$ap$l0?]WjV@00</}XKG;qn8>U|y:h9Pdj9|{ui%2?|1c[u.la~]>HFYrQi6I}d3U*`xwcQ;sT@GjwBn65pApLB>ALmXW6ywEZPU^|Imi((3Kx+zbe:2BWLe#3LmmYlRTd!cKOeq9Hkm7Rpc#2@PIl>Rlw3RN8e0tE6T8D9aST^h(uaSp%DUG/XdgE^_OYeCxnkOTu^g!n1s8P%p#vM.Syu.H0iRQAsiP&qXI[*/hAR3C{!G1}xgMN6uhn(K`CJa3kUT*;EfCx;;dJ~YfnZi#oWUFL;)=RDfwK6Y,k|!5t"*s6,o]jeI@9TuU4R7j}`:~<r&1G{~&8IQQF~>qeDUBDWifoqy5jUS:bv@>7<xa5jOE<A;tEO|1f<XQQ[/PEcw;,oo=atE}8F02&wXxERGI|LV^Q2w=>(|pdVr<&Ohx<O3k(kEf?1ls<uVWu$J:sz9o0W&8mfp|xi~<j[VC5/G"HCe3y@W{N3=[ooGOlYJ|xHKB_Pe&d5b;=p"oZUYs7_6BSj^%d=R/_wxA/J(p^:sn9F0a2k#}W*fIHnhTc(a6iQV6al}7hfCamb167CHO%=$)=1D&a9cDS.e6!h&W0[Y|j>]f%"X#tGkHmdAmt>wCWn;[!CH[|W$oD;0R|/L3OT3R=3EB,[kL:E$N}%D>1C!{Py_[FNGSTA$q>7S{rW=P#M7s&DSO]yYw{0lZ@M?g3xb.E1*5ri;etKKwu,HK1C<K/I@;RPr1Je`2GyVYW_F(]+<`{(g)t,TJ^@NS`KEDS#iR3z5A>en5R3LWt5G5:ZwIB^G{+<e8vAj]3iK)~zB6!Q5VJ`[fCVgd/!|oXA4nihKo.><RG1io_juc{C^B^ZK6<EvfvRB7b:2)djzg%?=}QvV$}I?kC^)XEqu6`^EzvE[0tjf@OJqz34A^%ou++A=Ci)coH=JB_?W*WYw<r<cfcQ7!F~RI*!%N0n,Y0VbCyv5F/2C=n)uIL0|l4fDpSA$#}~.<~<@kDAlUcGO,n0xR_tk;X2qaxGg5_=0~_<eFt4S_~$?#09hcv.%Br,=<3vQBD(a|96BbE`Z1q*R<!N]X)Y?09cm>xYf|s{10n=I6UjE1hl7w.Q.8a83>;/Y<QK|"K"$~G1?2Fm9xF4,iCS.fGi:pUd/3_>g9EE#8:[z>NYfeY6H,a$*alg~,M=6yQ|al!rTH5rls{|FIk]&r0h"txosbd#ulN2iocd1#e}?QdAL2&J(Uf)FgQD`ccW?&3ZWX09WeX,qrQ8M,uJI"n*rF+h,&p7F&m0OyylMA}}>9mjz4DZe4?4oj"nK=z=b:4u($BkuM0cIurEI@t1{%SLH8onC~%~KGLZ"&Ladj5#R,D]QC^(g6JzZDzdpd:WwR&]e>gMVN#GW~2,LC[zs+MbkwP_,1[Vm/I#L2W^rwOS]Oj"G$Il~l=b?bdD>;fY{O$LXbx*$X5WVrGEgico)q#f.}SOl*03=%#03eVA`oDK[WW3J6<)WiXEuY&0E5pMwN}00~L32>IHFL5>TE;>a{kmqT6*Wl1G2q@_V1w)B}{coJQ`MuL;%"9?wG:Jq&vaqL6w/68?&=9h6sp1!k^{W}uIQsM5SkP:>`DA0Y^5y5c}lk)tN.1;Kh}GmXN,:H^.HBYv(vRZeu?ev;"v23a1D^h^O@%A+z9/=#QKw|6CP5A^W]x/n(^TP9dKst%{/0x,|1Hr;[pcC#[3pGmJ5@JVx17wa|H`tqIR|:HXTlVIzs+#~GX(*P[JF"9N7McWqq+}Jgu[aF"!wRqpgv|zN@m#PDD:adui,Wdyqn5At,J_R)C/jR4:~BZYcc8ZFtWRxpc2~xMS9;f$VlL%`OQCqP`j|S]RI&!C#]h3Hxkw`gJr!;hDZSag<=1<c6Tr4w=Ig[DmKgl/pP/{IG?AhL2E]|jB6p0agu4@.sbi?(#Fg*fUl)Jk/nQW{yF,4R0Rg?Pp=pJHr2!KOi87^.fYo7%$oyt5l,?v]tFxNpzAOJxEI>uJz!Ixt*x:[7V3Mk3gd.&S<m%;t(2TIh*EbQ;"mxZ~aL7d4~L3=tsmy;+i+m+w!X6ihCdFL*:).z,Vt.[5itx%7>$0.J&D&&Qux+:tRthmjr*@+UiLxHco}]!`+~1g6Z;Ej0orM[rRm@bxn!I&*|TnK>&gLo|LTh>En0y*i>a`Sz+7,j*GJf,0(K9@D*fd?Xk6IE.DDtV&E>sr^fT:*NiejE5*sZD2MkR]U77q/}TZs1lelTYF!2#:bhgI{L9W,N4a:)p`_uVSP^U=u|U#g1ul!*K^$z=7:;]<Hs[>5V]z;nrpSyMN(^et$Y!{+9{T>1*y?HMI>SmBBwk^KV1^S!TALwRP.~I06br+w?sC%I5::jsNn:mcym!xL=>vDX]jh[2?vu,u#RxNG,y&rRCp^kSbHJ@nVk_^.kjBJEr/bQ&#p5^=cFzJr6"ebHcI2WBkAxVGJY}SX:[A1!tiKT[@I+ESlI6rVV8ei1R("@dBbO{zEvOa]oM2b|,F[jmbu>^90x,$0qaY9a=$.,x=b$4Q@OS>kgEpS|Y[1O9j7x:JG_sRx!4$EK%Dvq=Gc[kx[,A,SFhP2GMlw/c[Ne"M~`Z2N<7t[]=@uIZFzy6Rf<HVIuEwj**k+pb|ss?eMY0|(g!wDc=uW5nWe?@8~=iO|OL;ikdVp9j%N~WQV@4h/Q/U;A`cWvh+,Ao36u)j7WL(7>kFvu#%L"t+`nB1K5.q6tER&^TehSBzR|qG>(89A7?jlc32_:$v"5(OOTn?bo~sYXEDE)=~aRaIH?ySt4JIzlbWx.oHzJnJ7>?;4ryVbTIw(aKTk3v7g_cspsd/U),N,GWZT{&c37]|;_fvmW=dhmWV(j`UrHcffn({<FiUJ}kLl,=<@t%GFV2;P|cpLmtoG<9#dtBpF%6pGGd4RT&V_bLkX#mjNWf_l$`2/Q./QQ7/do@qFh`DE@J1F~"BT:Y%#04+"*`4u;uB3rm$Q8nxO({p2ex&K<yFZd:VbJzn/pgksc,#a[D.NLyk{c;uElczL;*~ohy;eE,`=XWRt`h,*CTMyj2K.q%TuQf74`y"$)=B/;b4iYhtf#S,=,5;u.H_51Ca*^*>br]`H{+Y7xIOm}h|.xCqXylo?i4e8oO,LO|b)7@%L2uJ)S+3x90)R&zq[miwp`cEUqKQI/=Ug,[,i=/!1!.*:WdSIu^|m_34|?<+]qfI=v>t%1s4gUwtar5tRVJ<tR$#_TqG2o++C6He+d?1I/y3vjb!*H^8>RjVYZkTyDS6+6`ACT]o9>@EW8T<d):?[kFH{nzZEM17z{sh9*`!{Eg#xd>H:8j">wldf!hCHK.u/X6dUp!qZ#D~eFll}l]@[8,QNiye.:ou],YME1w|CYoP^=19`sa^bo_VBr};./i~I(|n:?zscZ|E]JvEPx89+1OB*WotE3?Z4TU.U{Y:)ox"X)2N:L[h8CSEqp&Qo{#_NR1#750iNyUCw0oN.kd97{!q/cj?g50Gu]rh@"Q"4{J;hWD<zO%5r&Lp`7oXcKj/T75<#fi)fs&j!(1tlb:+_Ayk0xx@SeMuXRD#5*TS}t2zeZcf`97KsA!<=[PtoB4iVD*x^T&31D2GYs2XE7S=ne2y%8y;~]x#^gnR<(Z]T<O>t^Kd5:sQ9H3<SIVN0e:v^ZnB(5X1ZfBCt@Yf^TMP,O/J9XrJrh?9wCM$F;oXdn3<cuAgEopvK%d}g&`e$IC"`iXKYdU@O$08EO5x;{Rt+ZPJvc8rW!].l,4vnLk[#H2((>wbtJ{2d|4Egwcj(;|bBXw;Hr}$xw>#XMg./x9rVz=yWusUx3P~w4Q~As49P%&sjAYGXt1973x:y+9)Yeaa}curUC`dZ*u|X^k|QaU,~3%x]wTQ"g]G#?Z!^S`25w>}eH>NLA@0&55Xg)RxZ(KU|^"cXx/bYWXX.Fxe0HAKd|S`[#{qXvJgu*w;avi]Aai2In3v&alw6V|#n,NfBv`f0{BbFNZ0r&]*x32O7Qp/84tYt%*7TIO{Nh|[7}Xvh_V=mx!3tl)~vJoP$?,bHdnL8[`^`r,Yx:7Z`%{F2K$EMg&bP/DpjKX?CIWgeqyg!aCF!]R}5uYqr5W1R)/Y+4RIO!O`H^KYp:q#/eEHlTV!>Z,D2yM^%}}=7,1W!I>d,4&L0)s;&<Hl;Dt,*U5wvSXR@SK[nxJl_!#V|LoWm*?o8(WV[i_qXV_UYS2!_aW&VYiTsf@[Qo$T49l2$w52a]+K~"=WB&wwYc:=H&6eGagWpAo%3f1WV[BS)/QN,X3n6f+Md:4pRS}ek|V2(Z]".=sIC<bMbPaA&fa)(G`Yse8J]Jrca(i2UG%Zo?.f$gVt@=`S+f1L{LRyETJ881*OmoL]:^GxA4zE1Ab5^l@?K>zY3oC.>PQ|no/kM|UCd9jlk*{=Io+wF"Fg}T:GL,d.un:n<Gflp?Z%8J|8E_0?F/k,]??<@HX"*{4Y3,O"BCZZ`F{vRE{i[|ENevdkCbY1o&3Sv"|Z=fn:yX,B$NPpf~5/I:$HE{I/<<_FHSpIU@4p+!2ZuXl4x[8J9/<@Ox7p40r)FtRkI/x}w(t0lYwTh4;yvIt0ck;i0Pr#brVtw,5P+r9cVI,Fp5rAFL*e3#Df4F?RkO,21ad(HC$r%(Y5Q:HUQrgFAmWTp0X,>t]M,QcdS3j>FI8V6&y+~nMpPdBgjbU|Vkmx3@N4y2~0L)<5B]JvYNRYz}`/mS@Z<SOKd_eQ(RP`RNIWb!9[{`)V(I`rN5La&A0g%9n19>5(g!@Kd0q;HAAM;:}>xiH.dR&Z[H#*?2bQBzep^4=G1QBX=&ip[W@J#XF)EvwKiTiFlN+swe:pi"y63DQb?7k#CA8N[3#SD(#Ii:^t<|?RKmv%QpNJ0^8s)|iYQkO`5yCJyVq3?U<:>Cm[XIz[Bzkjq;]QRU?uh.u|=E1|F6xeduf&9&/9,)woesW#DL(B+&0a_(9N:CkXs8[XZpv&MF2tKnoA]Tda^)m4OBFbm.CT!Y(&{k+9a=;!"`U.kig)[HW@ej:[2/G$g!J#XK&0XoItf9n,EKcH^/9k3^Ju|?NyKQ?!xjp(:@5+U<q5ufF8)2l%hz9lfGJ%`^T1a}Wdv[ATHCWrk=gOK<(y{;&tRQ*]{o*p$:EPr>Ve/#]E=Sj&q&#z_@yPDR"*X}!!~Kd&4~92QnRtji[05?dz:m:K|5BJH]w~,YVLfD9eAw1dpi[zl:l)Y3EY&g**hSj.$`yjHcuOfQZao/0(u8W)H41jI<Hq8WYP/kk0#W5`{q%A[7}>RGM6/R@5Z9~2#AJ*62Lx?LF=gA1|j/N>KhdC$>c;c|0%*oE9cu@p9w*46l(I>Gwh9@Qi0zDXQ&0eFiW^t4s5<#w}g%.Cq#>6(J}sMf>7yuBPcb20Om<4E/F`~F@k^Hee_QpS&v|&7J1}siGm`ApR+lo3/Hj*M4F@X^yr}9Z<:i1d.}Uft@`46}q7{@}4Rqo,%C~cb;z6kPCzSqc;L~55/ypzV3M._Q!p]*%)G>+"hT3q,@LOBZP,)znkCP3gsI2"KK9xXg3b&sF|NwTwhHLKuH?&.=u4F?G:@>q_?8tXWDrC0j&eA#sK]sB.>h1ECj/)21~i/Zz>CVls_QdHROu_iO}HY8qNwmZd7`2c"HoKey9ik?LT8m?/jlS)DlO"[}z;7~}1T|^A~R?d3h.y9d%g|m}A/EjI]F*]zb|Op,LwpqDH|4M].VIx6+_8*xl600[A6KW>=bcvZwIO]oYxv|EEWz,?XYh/GCipY{~_:8=,!vVhpQ|]!Nv1>t*;^e)[~AtUBH&?lZBj90DaZH^ro*a!PVp;_e(3?!6<49U(bS)H,.NT0Nz$iw:%{ulrd{)xZ|YQH{s2E}~Khkn%;AZgZ[{~{0b!LyoQ{iC=)@V?O9Kf_3Os`!F<$BvWaR#r56kydAf^4+ayYPWwoy]]XTLD?&)P=xX2[*4P(.`c<bWqWpDE`7fYJw~;C6S!s&!#lTdGoNO}Yu/ag$<Y1/jUYv)2~0zj/eR!BgF`Ivivp~s~1m(gVUB&QOE4sA]z1zAVBIVlTdJ9:t`t+rZOh)5l)tW>2IxIOMg9my$m`_S[FU_3F+R[2:JM(GI<2)s!~ZW}^/GM[G#tuFLCHv/q]}St=VJUs6H5ccYzr;5p1tD8=1*yLuwdl%T,#oH![/KP/>PjGAew~~6g@Hkj~SXF=Ne3%)lFd+:NB|n7*3l365KiJ)9$9o6oRFjWK&!|U~kTCeKPjd=Yg5&D:)/ik@El|:9w1?+ZaVE+._Mro8[Fcza2fV<afr)dzW{nB^ix$"2~HY1.8,#RQ~L*0C>^NlR7!^6W7|`Ywg(*7pp,FczlWgwR_xl|V{l2qei3U@VI:*1.iSPLE@k,iKZ(DS2}TGZqlex.V$nnW*HF5K8@M}(Rw$>y7x?UD,MAtT_$O`G5[|A9X<$fE*BIOXkDss.|!XdFh&VP&2MNGghCon|z{c#7m8u:GVX2{H1EVUx$oL7l"^NlJHETguv[T}n~pu.[@s&aU~,iiwUcQ"J1?/*XMU=7N0?_@{_*3*Pq4i&+:KG^s#WogSjuk=*Sduvux8*.)**poqx9xJ{]:sk!aKe,"G!RG&q]hp9]Q0X|G;0~:V%Z`D^Q9xw.j^?~4dM/n!Fc135?Bt`j9QG}_hV|9]bzWrI93"qsH;,tUHH[o3vu9?[=381S;Xz^j,CCp4NnY!qyoGqb1{6%#2Lidk&!aOeD^CsV9;,}Yv[w,M;vb?Vu^h2iQ)xt9:^`Rks1M*[DubsANMG~_j9JI_,s`0n61*)uKMG"$!Rpfd(,RRI5s>Rq=m1G=.%ha,{S%dmSsp03iy9C^3a}(p+1tqzj<ZCvN*!G)f`9lf!m&uy/QUF!HVElD%],5Cm<b;KXE<bz])2*<h5BpVSp9$}XzL?}V777p{XPEMP9M_kxD@Dry1g8v00ZYbpZ4TvHe:44Pw^XQPKNX^+a}*$do]!0ojx~ZrG6Io[9$Ini(f(|^.p!L)7oD:C]CStUtYEEV;/$ld{Z]jXjO6,A.CB5.F8?lRrDv3YL,YjlY)[eI*|bZG^SAE$Mi/^&=JSerES:bB[+e2TXd3^~j9Q8]=rhET~iX?aN7D~]K*xYLi")qwun()_]4|/F{ppK*6mYIO:@,hotJXZ%qH+*/"T<+dIh1%I3<(iN8dYxzRlSpTwq;}v;zq,r>pb2aY>qJXU4%&jB:[!0M3U@01Yy,tv@!UH(~B[^ji!$P2sP#fr0R@&kdH}C&=,{f??Flq4~Z^R7/YP"B(z*+4aEm?i~%]!mY#;,|Ek&[1;EG2d|X;3&Opv/lkYsZg<"d84KASdrGyYi3UYlWM6BY)[R.AG`4+_KbyMp]Hwm7zNgVh8XLrb/Y5D1SKeFF64X@k;KK[6{u<B3L#y0C#{o:5wgYx/fYs&pj&V$xzzm)>gzZ!l"2r31)$C]bh!F*j%?H>%:j[Ti!;%`Z~+;W5~4iOr<XF/8Ra%(wli:K}K!JpuzzE:7xrf@I;x2zMP4EhQU*kLJj+TwT{.:i<!VKQu__tyx[;I$hlWY~WSQOM5Y%Rg<]_IY]$h5:>B<%:PK^nuQn;@;6QGvrLlvgG(JHy@`?Cl,,6o^YG;=,^xFUpf!}d}h~[=[?GU+4_1U+_?nJk^Tl@[VQ<Cj&Y;WraK*[}VNFB*%i#|r$cu&%p9qT~+:EwH)9f@orXghrhh%G"Bdqv=d@*H@iW;|.^hQ&aq4,2C36<(By|r|n%5]`He=2U]G;O~1Nej|,btPvC3V,81LYV!2*lZ*KN{1MG3TjcWC<LJoa5Q+{,U+wx;9I^q6CI?wpJUW4zPHT.vvcCZWa{@X|8M]uDz3VXDFzS(+ks+o"Nx{y`KnI@LJ>ys2ep9_wQ6ML,Z1Rw@/NjWgX1+S51(|eXL>TAGtFh2sOJ`lk8MG"_4n+t+DKmpRLAL(%XtvN;>OAh,(W#mm_b9f,E"^LoBV4SyAB`,H@,AdFZIX2;__&A?`Kywp{B<eKAwB5{@"Sqc[*l6,`e[KT}wup.[@+bhH2C3c`m)!KH/~6A*2#m3+2fB"5dO1Yw7>&?dYh[k*u5Vhs$2e{uJNhWZo&qu^Le[%E01Rc^a~]SoMPTkRoMPsfygaaB@V!".7Xe2PCjK!M73W]|8EqaXFZjX!&H<]7&=}a&.Os)su85.O9*MBq*i{5!=o%WJoy[NG!/=^{1Nu#[]{sH;9Hr&yMj/u_b@.Wmc=9vJgb*r{{P`y+r2q:>xwWpjC|(n6v1:"%%8ys#Dde%efH[q?Lfh.c_G;Y}2[<0Lmz~IPZ:1^MsP55xZ:(tRAQm|[izO^FuiLA93py2wtDve<.YZ3WC.>DA^*2?|9[<V|%F,dcZHkB${{!Kf9+N!FsIQCC>xBqq&R#7.}X3x)a[2ZV65||tbfG!d;D(9d_$cmX,n^B%eWa51C+BW;LRo_@AT8!0%~3et^,4/0p[L29?(ZVWK*2pz}A{G|UG52I4x|Ji^]VTxuO!dY0r^Ro{_8P9ZTYmO`kEN2u.E[T?/DZi]ZVJYUw:+j@<1]C2J4L.X[XyMI#q7,_Wzb%b6X&jV/Df^0Qbf]8zw,ODbjJ|2AyX=^<f}uwjx;2T*s2U*xS[/OIv/$SDMw23*0c`,#["}UcAZ!+0Nc9PHZwyG2*wZU*g!?/z/i4)<Dj@yPl}T~jBuJY_VBu6BecgGI=u5h0{qtGH$#=Y(eO,#;bjN></)?c>n)Sef4MAorX[%2$xllsJ>>xVl!)fG`YDzP.m7q}_dDH=M_F|2t;IW.NAH^evLSu!L?g~aK`E^1iv[o{1#]5/v~jQ5VX]CLi$*F^#rp%R!^O<]{6AIhHTENOaDhHx:ly:K&OrJRPf0Svyz*+D.eOGx@YTif<XLh?Qo0&V9H.R3.OE.GDM7);=DSJY#PT.ZMagtna}MN,}tJB7F"&k&M>LmX#Gdwz_yMD5I=i?,&qKY~,D>0Bt#svn0i]eG32|J`Mawbb"GbKV5?y[LgO&rnxVYimdfbX86a<1ubb]GbKSyr{awrTTNj:}{<0rDqb4/Z@F<m?L[[+.RvHCaD[XOWryG2JpNRT9/+5CRYw|0X(QW"gHpYXGDmDZWIuqIYCwLaSIowJ{E1voBYdef[T}U>Cp3*$W1irWMjj1JeXnBV4w7oDg{e`gGKiEEqYI9<ZP1ow=tK~VI[40paI?*]j6qSy_Z_@$I,cDdq=4[>%B6.Ld:pzo)|`ki?$BXN=T[R6.6?beZFYWb_dt$:MAWJ[[p9sVSv=DObsyq}|(;mK+{Ssg%pw?gol_C0IR0NUP!Y#;Q3M_$l^_o0y+e(:8e,^dc9V$h>;~xN53ug%D$?aQSqpC=n<+j+{]@;Co!e,StX;+K|RD7<y+zZVo[YZM]E^Vy"HV52*r"Nl.<v!#Eu,%M&EzI[c3vg#jNhI&)din$w}N&s&/XY+ain]F0"<Ol(<aFAj%1o9IP!Jo[i[26$e@pwp$4JW^h}|qE~+&DrS&4R>|VTNSR.EW5sZmH_]ro{e(KQZ"=d4gQTNkGi&BFD>#C4(NzX)/i([/48i:th^LantOc#v3DKk6D"+Qcwwb(}I3F%.hr_5M03xGc0mL<_JdY!,m!>G]qZQW#6jM0A_v5a~Hn@13xzF|S_S_S3Z7$"V:Au9.S$vC0Al>z:)|}P,Q^eWny,#{^T!>N,HP4!&!B2usd!&C0+z"^XJ?=XWgrK(jVj;0mm2`hW(JHP5Wlk8]n%rQK_ejwe?ul@M~:PS1p*j}3!&x>O6=uMH&v0`,8gwhjg.>0phU~j^N4z?dECT#@(Glx*6)zU*$IPe4ab#_]26U!Z<wN:EMhpdY~GUCJ<Itzcw#Y,*s]:yTDhj@Yc,gj!PZ`}h#1!&[7B<kcYi}Rzb6p`+4iRm)2#X)x`g128@6j~8vy.x${VhOlBqYRAKaEOve5U<ng/<S*N~wVvOaEB:B)^%7^eag*e8Q<|K>^PNO^tQD<%s%7zpPQ+y]E#0g*).`Uub,%E%w0@8KWL]:N1@.@M|vvw[$?%b=3**c):PK84q6TP,AfsE}nT=tbQaW1;;Fsrv~GJUQej;+6WW1<q%T.nPF&)10jD/0IAF.`tER^*pt7c/nrY!D4"a/_qGA2Nb2)`1d}%[}rndUjh>j;3C&c91KEf`X_zzC{N2>NnyLBTd4C:Y#N@&Z9AnP6Obw{b@<iC+#4h^<3k>j3M/dd#{akl@IzTj#byuy=srQ3mRYajRwZO3ApBv_@W>QMl=R5Iv}yQ&Rb`r:kr<Leu6gJzpps!sr!P!hR[h/{||DGzMw^/i=O/Ro*Na[5HBP/p5vh%c1cv:CJB?.uDYm5$&.&SJ^+e=;|@sy9d?KRd(YiOLx`+^WjGW<^]@Us"r7d!&v06;x)LDAfqH@l/{i2J+81fi^thrD/]7V}OSPn"k/[7&a*rT_=A9_;fy88>gAoG<PDN=kOvVxPlTt4`mB[%7;La9Fq2H[d2%[7H#a4%%O=8Y2+ne!&BeHfx.}!#r2EaXgr$nO!9P#*4I0z?U*%.DPU7RO4Qc4&|Hyuk4=7FC>y7QHY#Zn05xZ(PdWJo|>k?&L3+%9ljT(w2zu"2C|a9lQY5??F|?Y3!!6Y!#t,Kdoa?+y>!w{H5{/j&;L:N^=fIaUxvpVT&ZNws9!?0)Z`&2ZMTNL~~%~u~^wNZIWn^vk$l<yAy|!OI@nD4L(5J}rB&b+&g]kVXQCY#mqn~IZrVYNNZx@[3=}fV,(P=3zu${(LJh;&`y;_;J1X?v%prlOzTcTk*n0IuVvV"eJji`0$:YD~cH:7maq2)D?(.io,!x6s%dh(7=$ci*%%+w#v$bqPt&9jVN5J6_;1Dd+87c$DYd"XmH{8],h;BS:#T!`v2w>XX.3Lc+r{w)Y6,!~g6,/n1:M+<?;#b%y)cW!iH.TrI@{uGWAJg=.])l[]@bxg>rv+Gg2.i4!!F:Qh)FD/4p+t2.v/_P!<DYSM,n{,[CV]nKQS@Wy#XV*tas5Lq3RGG!!R04zI9?Ki%V$<oZj#k/wUIsnC^/Eaf!tiG;Fnb_F22j}N[?,Y2Q7Q5ie@_,:F:j}dfQJJ*.xJSaN^o)HnA]1:(1sGu|bQTvVOp6>B#kWBMddGQZ@/+}_^jxV)G0SR^Z~Dtp|M"B9*qq]=g2?D.FUQu~aNvk>uei=+!qNz&|OS=9qXQ!+i7xC.=TJeBLE/wld`Ea<Qi;S]AqOLfW9+]O+L*^XZjZ4KhRlXg|D."$:Gg~*|}@bn/F$])YbF&7r*xhNHj+%7oa[TP2J]5^m=4ON;aM<<bIu;."fh<z4+]Z3cosOQrdtNn{[C1quBN8[h_V+0Cw;[XWsfXiSv1:o{~2/XqdnHuzh2S%%)4I^V9.R1WmDSo)DknN_1_aIqDF"_/eX?jY~b$.XeI7O!enliZ63@b^3Vc}/)z}zC9D~6A>O#ir5;95ALzI"gNU#8sT9)9sUFGrKJPFg0AEiBb~btGoKnGyqSRzL%wV.+xCGo}9]Tb3#:ESNuUr?b3`AW"Uzp$*/HY%6,|3N/:H{[$ZpcB%`oN6Sel$l+rJgyIqQ(7m#nCwGJ5v4tr~v`;=T4k8$b>O*GngxuH|P^8tmF#VIMA}*~jy9j)<)t"/%EO82q@{W!pz2jk|_b=lytUrv7tw2>WYU5mg8HbfrVQOo+Yn!$cR%]N^c>CG/ebm[Uk.R#34U>RZ/2C$Gr,WEi/;5}h>dF}c@U.C5_L#){V:Vu_ryh9dj3PL|HTSD!b+`&F$k^qZQx"?}i`k^_nR]w9|DlQ@}D0s/}4?sLE{u87y!~>f.bD0uJ4H}snt~F;:](6M](z_SKUw4+KLaleub/_+Ue(_MaD3x))*Q%/rzWV<MD0);oz3._0Kpg|Xtqf]e/vs+r9z=.EN8M2tdERX4)gX;L8_+[JVLA9vZ!2U39=]9v2U9L]0Dm5i5NRLnuZLi"MsgeEvK5;XkK=Dx++eQ@"LF[7Pcr.W$A1Q)_b97vDLuMoQ=G~s9BZ`41!GVsK5GWNh90!|v[AD]K5Ri"_)^Q9NRJ2kaLVE/yM`u_z1<0XTTvTY$0W?eZ!)"Lva]_G0)a+CD<Kbb4EMUtRXhM0p4oFN1XdZL}Ik:GqSFQ#WK56x$egryaZxU+c4HYo"77sJ_ksYEGi32dQbk7Nt4CfT@%h27`{C8XD_n|Yd.=VWFvj>L{iU5,zCe5@E?mT]@<l[B>@7}xA&44@d8?wJT+45FoY)c4)[t5VG,Mup4qQC]WjxpJ|r6C0Lh=4ap$Edv%qlL.|@RHMzXq5F}CnT2;3xHZiF>dh>W5=<0mW=Pyovl:KNm*>$#b[93>s+t9wJkyU+hY:O]_%vRHla"l)Dqnj,+Inby!dSY%RA,XyBtJl?8,^K[nra$RS!{uLh}R<GrI5zI<%*g8shucvLknQt"&A.Q+1lXG)0XkY[}.C[B%ZC_*k*I!Aj:(wS&E]<"?l+:/CU{2@0kZ49|RAR{SAmx6[&N]Wp"pU(;{l5x?59?H_z|"BJmd"Bdv+.I<Vp?zXK$Xg`hP=HPS#K@i$nq#+^ciX~3Zd?jo@7],lI;MzsVj#X!cE]l(h1,Z@C(4_mXa$sEPHNCsKVVhTvFm:=Yi(xum[WZd%)k/?D&jKTU^j5/l)2^t<r{>6crq`^A*U}l7ri_`q6S=]Ufb@&UK?6[K+1@nhm_*]%V%yl?fd?gp#_@Pi!&1_D^dE1K=:E=8GT`P@_/K`<(Jxbh)Xz^<;]>UvO7@DSDL]kQ=yzgP@;)*IMgj},@+DVc4o0x#7y%#{YIjx}:lPg0IqSxGgZyDv#vS42f&8*$jUmH97mavAHz$UFi^upC:xXc$J2Qv9VtgK;e!Lv0YivoOH6|,([pOmz*W<KcQuv>d)#Ob}CX!}vfX~%4LmX("lt<K/JwiPw%7VfsUluWMj}/KUf/D?OS2!Z%<,kVj2@vE5gvLrl+LX`*<$y2(vU0Ht9<@">0O=u$0sGVi;Ei,tPR#8gJ:2^}^%co|VS0I9!Qrmu@.<,"^]IyE?F_bzY"4}=0#tO8/^e^01y9!h%1yc9%`x4<4~xt`t/e5NCpSu_`]A4m57VR>ndzicURb;p?8$cYPmF4:(S|X}M/k0g(xM:ft%7P~|m.,wf~uwP|w1P)5"*5ozaO4=0u;Wj07dRV9!w^=?=g#J8nH`.%,|%Ik5Px#UK,P2k}d{^V%1&8"|%?o1"LlpK5=HWy2naWD0;d,N?l(%7e)U{p1vUcQ^e/KoXYFFFZ}<oTmB&!kU^(dtp9hIKC$e<~M.)<%jd`Z`]Tji/Ps$&`bh7i3nH!d`^oe[pK:*<}p#OgO:N!d&v+69[*f#{kKzNU9d/,$]OJ^X,ZPEm#QwN|pzGr{zW<K!34vU+h(w)0.]pEvqSE%e,gqXK(P>BH[d/bED38zqg?O=3*hD4_cAD%<]D:s$^E1frFR&;KxdZvcnl1,R.@/]&D*6pATj#Ob7biJHYa0,UpZef+XdZ$/h2^a=/EYq>r=_=rLRR6?)(V1@`~^<bf5.p*g#=^e?xqShhLUkP4uqSQ*#|Wgj0n,#&3f/@~1!,rGLG{@kv8V,,`YaikF>Tx^A8;XWyuM8vfr)=nduZ.JPy2m(c[9w&FCdku/{Y],7epQH"7OvVh72cGg1mojYa_]vayc}d3K+0$5yj],{U:l*x>RBvZJQ*34jj);pBtFdE~Y6j?}uL=3z~)MP=1YK.$Pc$J2Lgg2TlHzlQZ?|=|1U1m|K0$C29QNjosU^FmFq8CLh^h}8r@/y9!p>2(9Svl5t%TkCEfdHO_A!5Y(SvSK]V&scz#&f4Nm`<:<+,6jR=HZ;R[/&]icaEop*@ZxXf/7xK5)|#XGWe5m<wc.ed{X&&l(wz4Xx+2s{?yH2?)xk~4["$wZRp++NUz9)H:jKC6=K%#Uj^>%@Eh^>%[|&}G[0IvJN$u;,no4p}alcfbgt%{nBa*+Z1W~r7TvR]kuwz~q;^+T3q<I:,K~B#]>pHPMIh*^)pmEa*GrV=c5R)]vIa"<o7@saMj:rbI:j%E^zc<{kRxt6O>/<UFGtF=4UV5}[1:Ctr=Xc[)Q<1&UaU,V~VF,meholF=+@<u7Y}^q98~<F_cJY|3v8?c]&KK8S^<;:32QjdL/FFs#m^%9`2w;8)]>xV#ZAgw?3GhoeFK:)%tPKhw{^}%j!q%nm+^T8@VihuX)@0m{M%Oouwi`!itx^a>uD?XvY}G/6Ek}dc@Y*0ir^tW*;K|EC!/i5vAT}9of"ekPfoviuC?SXdbom8%F%~>"d[]lG<`~8Y1ZZoa/<b@~|O1LC[0fW2/Q@^#.RHuaQnVLpps)><IZ"F6.*x]F)Cmz(!Gl4`(FaW8=_t({(Y^ycHqSJT@%Rlfht15,/nMVd:!&,5$HSo0;0WSF5M[*y,ya!:B(grj>O6Otygd.o&li4;;P>+w0_Mpp~o@x@PO!%e,@AsXnR%9$egF;<H:Y)=0uwX<WSi"`5.AGr:{y3$J(@g]!1VaUF&2v>jGW6=3a62Em(FM3>ZIM=9rYh&f$+?Q=</~u%jH.eWGbg3d3aBqIi&Tz;!hQGNI:"|E^Nk/iaoio)gZgKQ3*XBSN^ec9{eusLa:q4=c!r`}(+m"nZvd.Yix,J0d!/p.Z`:{5K+,l~q;.1z<OY:pa_t/N!jb#Yk],nD{kzT0P:X;b?Mf3d3dagO"a7@LmvpG^?,EU9z{jhq}!Se>qf&y^OdJa2UD*Kf6l<b|]Wprk^Rh,#&(+KruMzY=rdj]yaRWC8XH6:/jA1k.C"_1?*M_OYEu8u*P6XtN{2_~4E!wGQ`JB7FcciOedJW[8WzZ@7$t!t8*1aE62@>$I#s9;wQPZz?F^#W~ypw/"//^E&(}hSDrDHV]6&E(71eUqAm?(~>_t%SuYvJ,In02:1t{)7U9Zd?"X}a[vkE>DuWgtveWH,M9_FoSRs+pMuZBUe<R`xTc*JX~V}!krk:,|%Ikwsw;):@#Rwk>.*kJ[lF/S9QYa`7@%VJxsUZnSB)(%2Y`79q|rp[iV^/W{92ITF%l0DdK$KGjd}f1AtFa=w&"{:%%Kj3krv#]c<y6EhXnlM6Ta`7=)*a5xhj+UytDnW]5J!+vWZCf_KU+oPBZe+6%7eEU*Di/kNg0:HWrTS#C^nXMshlE$k9./HA@X=?u/J?1u9Vj!_&lM,QX*/+(jtJ@KA5f2@F=)Wr$0YJsX9~@jDKMlW:BXH,h(@qv2@)7hqvQ,P~|pgzaF;yHA.zCD|,CD3q]EObxN:qjPsXgO$}uW.1G{oXo:W}Tqd~7K!A(PfFyvG%NbND|{/>{y<nJ=w{[2UK6+<<"hT)Lz+vaV&e5@g6,WJU=Yov%1dfcaZa4:!MUaI0|vtu%1siow7nEK>Tq;7lEwiGTex/7QBz*/6Mg:!_ADw{8{y7I<_Nuz7jDsMHn0FzFUVXK+?EZH@]pQ.f?1B>gVKZaqCl8p%)+s_;5EU9:Vn&?=V`2672>O2U}gSylp<$MLC:e7A@DX@jx^tUx@NH{1QIhP&w(76Bypxz5_df4w~xhcmp,n+Z?baY(N,,1v;o*IO*JzRl,C3nnQM&B5"WJzF5SDH!,Ma.;m#j}m@bSJ+z{=MH7cFHxc||U7#;[PZe8l~8@#a^W5x&OU#8qp0ntC|:ZbWvj{PcAQv.9lnM*!o9K(%fF^(F:+7Yn!N^2$?RPH_GD&J.;t`H+fabVC/{5/!?%%#]agSX29>x~Z`fqd~Ss.F=bX@:]T3qvh1GZy"C3p@Wig#6E,DD,0or9>6<#&;om3{1+Fn=0y@IR^qQ`I.]E56]?mndEqUQq2ysSa&W<HLb7:<Tb.aiwSz;HYCNLS+pI}Uz:qM.`X}b,R%jlGz8ysT,d@eRFwzw]7`EBD.{s:Wr*Kz}Y)dv|)*I.]O7S6PK.]E5{S0Sie^JLrX1Zn8n;fdGm=l.*.&p}#jBb9*K=&wo"#^2J$dm[M?ZjzfL.n!yZV(2Cy&:)3XGwZ^Y]CV_TrGM04KiavVu8?*Bu~T9;&(7#Q/D32=:nDIZXD@WgGJa:CBYEG"Y73jj^v^v@U6>7~o#R]G;#Bm`g<)8<TE<dun>6GPIoa|E~r:$d;]JtND*Zw=z.D}z`lWY"M/{72/N"L;G`<s#BYp7@/EGb3^NI#]^tTkGse*Tq4}E"ZBV[Taa[T0$}N)XsfoQ?Gb;6hC:g*Tn#%ucFHqhjz!^{{,nV58p]7>9K*@D`:Aq3V}zPv|M$P3q:}lg1qc&@#~HQ*Nk1pp%U86uJ*MPnT^NA(1qCwt44+LYRa"MyYp]D5qjKZ$VqO/m$0HyWDa#D/&0x^JTvk_{6f(n3uMDluD]IH!4#i^[DXL,IR%$7P*HqYq,(!wyy)Ok)l}*tw25{fpGmI@Z_[I&PZ!77C5<8i~[!q[[a__OUVCT}]oMSfWkk{*H@iWhu+AD;FA5$M?tcu$M"caX(NnTAMid6L,nS@83tvz?FV[T~4(PrdXf(P4q@M,jC74qQuLtD9hk(.4lqYcxy<!xX(b&FwD<5$f6Mu.,a_&f0I`<}GGGG66uj|}5n|sF6nJdLD2axDRs^LfhlOLwEV:"/z&xDpK^0.%EM:iJqSWngrsF"]^Lw46R/0Xz^nEg@2Xmr7a`+RywDaz9gR4KeEoGW(#DXY*<WPSlPH:ABzu%|K.B5xIT[99+GOBZ1L6w1D8!dB"Kbz%TJ;s2%ydOV22qmOMYiwIEICvHi"U@[e$G^8=WYeQw}E:F`y$yNb}XzBQ0`k">m/T@JvJ*~}OR@v@$jllG"XAY8fqVI$KoQAWiv4K`1+rNt?ibvD4.kE.L~prjK`[N$1qej}ic8/{0=;cxKp^#a|eUU?A1|z1W3G|c/IH{tZ(PQi0+c1O<=hGwo!7lQIhX/8/rTezIzqnZ3!H)$S?M`/uQqOqhfwy?{OXn>!_4Wrl>{uUr?EecH1F:R8d1])9D00[Lm3B$.)O66^[jVRDKEWE:uQKRB3${jRQDCyE:.gC3:Y9w{]QzxtOV5,ujBlx+3p6NawOyly}cN8GGBjkuD$TVwbq~/1xuU4"8/be8bH$b~t,g1O$S:/s2@@uVm$>ht5(,s`Ik$({!;1z3mYmt,g`2t8+Z(N_{L"B1,1BOW"cD|Y(B{t1toGRYsCV(rJHEDTxvPe~lvnyodL^>poxaM^58Up2<H[}n(bQdFyd$mN$n%V~.qKAZXs#{1#$=GL2W6qo&2hgwlIGNX}*2{]WzmwG:CoQ#WO_62Qb(>O6?O`SZ&Lid;9aXK*L]/K~urbosf6a^*M_>t&l5R~K@[yNR/w?vq+x7!qvi<Ylv/>+Od$<?h@qMM5j).ZiTVWr$?6Bp|8?3t,V>;9n.rD<1C(lTG)p0{!@OcL+bENdY}iJ68I.aNi:X/iGIO,62.Z(NOvSRYN|Gn6L4sTED"{v@3^;d=1W]p@kdGX[bX4;p{RMeH&S;?a;PwmQpa;[+42f.nQJi(9Hp$@M&6K{k]DbLVRzK>giS#Ri+BiG^Uz.Z)6{aDEIxzi%V<|&*y0Yv6#%i4NW<CZZEGDRh$ONz*383@)dM{%7^{@>M)!Nd0Oq,wYt^B_M6.81aDiQ:nKI6}F=];Uv]vo~t1G9s^+qV;;dU~iFTo?rDuT_6zc/j4qu8CU~F5M<l/M4i/l/l{iLJN:xULjVb:3rDDZ5v9Mn,tyjiTMw@V8b`Lj/lw@UPkVh:+%ucHh?q$u<j[Tr!Bs0{+=>MnlWiLJ)1mjw%azliCCb6wp*A[2yZ0v=w;,f):9u=&}}jn@.Wgt&VVK)8A%AKi,pmj%y$0JNk|TIs!@skTGYb6~MU!nI`"n~idSr}oqAkPVGIAAG>,xF+!KDj[fX?Yg,|sf>=RlH1gq+}M>O8&JQFDEjUQKIF04:(szkym7W0<Qyksml``j$nCtYJD%Q?V~]pMy]nbT#9GysRB+l.#L+y@rW[9{qGGTW4_*uNd2=kGv5g]ZPxyOVj5QcikJM!3px=iHitI<9fVsuRC+#fJIq}8iw.0mb$6JZt0qHor?E0Rw0z&Ox@0mEVTIZw7<v,6RqSe$`4zI^lqEJ&x.Qa}1+NUlo:tqETaza:vTp<xO2NH6Y0xLLb%S#Sb2Nb8*df?D"(o+}e@wfL.<6c~HPA[+Ko.kV!N9r+df"4ph}*V!73h`VT,DUMwFtcf/I~I9%kLV2P.l_9l~[}3eh,z^xkPmx;Bfcf$8Nos$)7bK9+qjnCe4bH+ASbc0ucOG}MZ>RHiYXNBBij,JZu$L%Sr::(nzTrMd#DX%?ESOr6Wzc4Ko.GcCCEBX4&|3[VrS4z0Y/2&tna?$]W${;dn[~S:2+<381`T"g>Ak*qKbpG[(W}ltn[nut19yzz,2?=kM:+!Z/2xcgrWnZ;3}|s*d?ZRM/)*yO6Lt_8PDU0gMX+jN&`di%dk^&dk^8Tc,?M`Q18Iqq`jG0s`d83_~6"796~7[Wk*{2vk<e|b.5s=Ul%00rtOiWGQB#(IIGI?s7"7=PW@an[PW1b^Nb+,+By]/;kkR4aw{ij=.?06S<0/Z/YS*oR00Ah+C&`Eml>B9[<Q|Ha?O!fw?pPmQsU1iwL0sRpaVe^`g$do9${?N5M"jteCT2kcX7U~1CZEq`*?o|!F*g><rg0!/U!5C2,sSQOJ<Cbiagf+P,EvrJgl33mP|"~K/7zgNvmi>S|@mb$R0PdG@nua45/nc$cRZ=YN0DeUuDli2:$jHpQgOiLuAn5;5r]LXm5[7TOQeFN0i(i{`;b/tIO[0Yw`H&4KIZAI72YN0N*[D=b"MJH#RJ0<OB/l2u!aCmIMlxR_pMnx^N(W3Ug}R5a#cJz9MRT}Tq&*`zG&,@eqEG@<,:u*le6gO)?Zf2ypW2sKN2ys*DUE46=rq%tuaB75<fRWrp^R2.#b_5bMbj}AkU#G&ewAHxmA|TQsQmd#iS!Y%asvoZET<Y,]CUx[4*!|%as]3`,OY_{1N51,Lj[^bTUYLH8X~7E.n;`_*KBS|`nEIM<ZS<xJCj$W"yR|834K4LOAFe_b+luoyN28~^{r5?hSjS(7C?KMh<J%Jkc@:n>L?`x"mmCxk]*}?Aopum;h_52?EB%!%nI93;b~nSn4qJDv}:^6|BKZ`;j4slOl@}zp3IEnk_hM~TweVv<K2/x&!}Twbiml3.0/xPmQ*8Mu=b5,RAk4stV83",aZG0<5++X_%7r`6=V<rLU`913<Ii=D6biJTQG@Ij%7R(fraa>dE)XK?+[fp$n6(W4L@V_oD:;i@7z,@u8bVw^)pjALJCt;f()8_;^uVs4*R*SUhdqoR0~Vz*N^RU87Od}<Du<rS#I"q!_bA+$I#U+Gyulz.2(tw/W8ELl0v|$yQSC+kx*F0i8Y+}Olt1b5HC8_W@KqwD.%l5UUhgCyqH@OgEE:W{7&NTe7JLZ+Ciq+&9R.DX_>_)>BqShW)WN~6"j38Fi|?L83$=aYy@8k/Y2ZSm/Y`wIa$GEH6KDXexpXX[B"<*?dUd~~mR!Y=L@C%P:ZNf/`>"@gtpFm&G8f;v2i?RaEOv1)aiNR0IaEOv1)aiTfjR,(SQ/R&YfOIN;Y?uuMx/^[wXF67mOJr>s+]6y){`Q2CEMqA`Ja!^)%v>ki/q7yGgi>$@BzJm/YCxTZvYC{#.PqP0L6!&f>A@:kgOFpm#R]zW~y9h[:{vDyd2.aHCW2[.^~/Y,mgODd)/%IkwU4;KUT[Mn=64LPn0zz{c_SSan=eWTUcuoP@U!Y,LJUxDqx*xgp+rqx%F^x^/zx)E|^Z`gM}.C.ChhMS@h$ku~jew!&UG6_3|_*hq`,,bh((v?;*^a~_D5{hS>it#mh$c:?q|~;Cmk&,G&v$I=iF*&hGmAj"h/QYaFLYaR{vil@}w2sK+R^kMX*(4=FXw`1?Cy?T&<CRwoIwzr"rupz!~^$TjP4LPszjYN4m5OCNK+{.6~ZH**q2iFi)gyGW2KGHtkwIX_r=}e&>g_5Y0Wap*bGfMqp&%iu:5XHaD"_KtFoq#O7ek.RW3kd8:OwI:),b*HP4O,gC5R6566+1KDw`.+U7+7|1rm:?;),j|4/^@@6I[jyG}m|UU4e^aC,RMAp(`LUZrf3;1k:SZQ`7UEb9gW^Yu*)f%wMMJEbqQ!;1Q;@TrjU8%E.9VG2eI+r64AD/^ou*ATiGC=i*kD86:mr2*!pH2W^0q!,;e=&Ny=9BW33grYXW#dnD8I$X8ueGwJ<02M^$y3t`"fU#9;18QOUHo]!H#2*2ScDM]HQm,N^$m5x2QZs;,!PX!UwhVd.m/;_7UJ;eWF/;~K=WI%m+;wHO#slV5X}KEZYj8*8/ab2*T49I=_,6<81d!F!ZRhqa27xEu0p_;}?RM_2]TLUAKub$p4#@PO!H@]Pq%!"bf`&9^Y>83)8/.`[QEcFJ3o,n[%!SFaT=?q.NNQSy~Y$kMi]VOgc59rV~%9Nc9I+I|Ey6z.6[{JPW~`jZAH3{MmU9#pq^[.,@#)+ud|UuSe}%(YyvkWG8kG_D`Sy2W>eBqBvL>`._wDfFnI_W){.vrFy^@"Tt|`,eC^>Q=7gAk=xASjg/icd&xf2/,q=Sapn`h1Mkl>^0s{Cp_:7:SmCIB^vo}sq_RNu}?IP;3[9052rA%3TP_)sP1SD]v!KQ|&E%A:b$AoQ{&;]h+A[Hps%;g5=gd!,w,o,eF&y!lOybQa^][rYvPW~QvvK*}A7F@,+f3lN.1S!Y]!q&%bR1VB2%HIq"G3CW9pqG6EMX>u9qov}e|}[s&a__D7B63E=n@53rIIq$)w^8PB)o<h?A(5<qph!Z>eD8mS!()x(Yywh#J;E5SEO;9ohe>qF:bRh]%o:K%*EarmiMZiPO^A&#Qqu"CA.{Tpfc>.7`wk*2#>{6&GH*&4iC<%y/&+ei`n:yr~Ad0)i.j9&<`sAIqt~<Cnfwp=_<j+h5C8>U`{m>qJlj8Hz;$jXO^vX/_I<)PrckB0sgMTu@7oHuprX>Laj$o0^mg:1YD(,mjMS;)$m!Zmk;f;inl9l;yYxq9IGYKEG#lFW&.W=s,_3|2>Zyi(pJ<(<8l9lyv6zWd%){1^$ziywVUdV{`K@?1Ex&5G(,X=94:]}?,zusP`Vlk~prH2q}IkEPtO"0wKdoTqp_!s>=,Ud2jsQ}^Lk;SN:T=?@*Q%VhoB!ejw6ZM(Pm,`^%.m3{.R3<)s#H!=@PnD@eiu^YT%LJ/8R4Z@lgVFmTUy1`]mEL@22f,.4rDmI<G6$dFKX{mNYV9s0KD";t/cZkj=gaPzRPdQC]<9s(DSMo2eD3w6.hDp,}q#}3a&PYavX}*}#@{n,Li9+k@R1&fgq}!!oan27$LhWe.VE^GHf?e"aMNT<:N(zJ0_8B#_}EmaTI,o{M4m|9{|!|VC=|8Tdjd3UM?q99!?}67TQHcdOmBc/xi+}aU4lECB>d>gO~ssOkb~49)d"h;zISsA4WkL#QX4<84D@rfal"%YE|!@O83xj8uIc"0{WE^_Cg5rm3su41|7xJ^+Dm5C0%u=4j+8P,^x6%w~W|yfl:^wpm!syA:Rj;X2JI?xOU`??/YSx&!clh76WnW[$FGE<fHczatuM`j|!XvyXF.hop6`i@*q!(1xp8t]Fq3aa>|4SRtjB6Rv#2BUq?rt2XBKv2i]{v>iD{aYb6tcM"nI*VBHOt!]V)4l@L|~G/oaB5Y*7i5rF`C1M;/L"ZVfY.,swJ:KpV(T*btOaPhwLY%RK18&T[eOZ5X<(mCclf,I$swytofQ:uIB(GVVR^`1NlCfAA<;XIB}?l_qy0LNN%FpW~_:ia@3/ol|PLjOLLje?!(7shErIPMG7LI_0xT@SdJXGAB8v0zzqJFo>)W?aec*x671=F]pEV]COfwe:@%vJ|]_,#CZq[Y^[{=VQPO&jeM${q$p<xPZmo7{Bu6X6q2TkQ3h%P;``0Ca=%g(9A<[mumxezcqS1%C>^SH5B&q5w]E4+GR>DdT@ZH*+&%t6IrD&_x|p>J[pGb.BFq>;b3OuA_V%U%R%[yH=h2kd`ZEN)yOx]6wZnOhfR0GfnyA:/(8Jdwf3NHyXBm7p|j,{UyL.LX,/bzyU]L(@#W~V$&")Wh6g}n0l}dzzUy6;4jj^?]v?gGn:POmrqx2%fOHv]m5D#V<KVG1+1,bQf~5zq%ha$%=].@,Y(z|!>luZm:fEjwn[Q0>c9V=rCT;10Id9twp%SUZse~/5+=_;R^L1V>~Nnywv;6GEHfuw:53qTR_r=JfsoI}wsw{3!:`oj25S;16J!1^MlPz)%Y^zg]8N7.7kOWZg;{y1=S42/5{mTkmI;uv+<.VC"fuPVlnyU+f6%/U^HPr.7b!1g<BJrjyY;R.,}o91oUtf,1(oXNzknNL6RQ1HwhZ:WV0jg];Tagms0v`k$&oC~q0`w%pNx*_O>[tl"k1>^`)=rLmq.)11<*whmMRDLb"~Vn~60$^Y,cV1_%i(3f[@~1?*rGE;x1ZKJP6)~*84>J0BI<~[GpRbt9}p)=:m:]@y0),"|r7?[FZxf8?m(IQLTe>ZG6%Z!,^!v+VdMPcY{^X{4KrK?NUHS_)w{/sH"tS)1qU+?;FMJU+6q6]r9rxE7/cq>dBW!9sFgoqUJkf6uSuU{+Clzz;,jdI6f2GWMW}1|V^zOa45DJdT<qjO3*Bfb(_k~plKFnX%!n)bK_ik|v}6@J|SCZ4qibk?y$$`6qc9L)v6eHH?r5=6cjj^fu//(S.N>6Rz4gCjXJb>Ss#~m]N=0m{bx|X=7oGk8Cy%u5"7;u4KxU6%c[u6u="a{o]IUHz]!*yC?`1E_@cK5=QM(~P,2Pp*)kEw!S9oq/)#Tulq2.BbpcU9`i,$/HQHVVd8F6gyfr:S6ygrA27o*^RFJ?N=whW2;J~2%78)?:sHPY,goadA4P0mt!mv"Xn8G!J_BX_c|:Upd3jlLr@wftaiZlfGS"`7XGfqC"Y2,1[/@JxU:[HsgO^Tq/hYVOg5ZNIPQn.iSNmFga$cj%;<z$gTic@{6Ip3l2]TFA!)~Yp,B>lTNXO7l`![TQZK]*iM8=~%MwTJ[@If_^)##;O1VJ<L"g:I3xiIpmWM$<7./eVUXXpDKy:.A)!o`&~rPEW22vBtSb)/vO`YIZTa*zho5=BuZjAEJpZz}==>>y6Ll4>y<jg>8S<`l0A4~VoUe*0xz,8g[|B?ldHSDd;pJp`LsC|A,$4!B!5i;pJpQX)C~qiT^Yo,.x}{=)bJLb;)>gz%@/d6k^6pb"1tr0"^h^GhV=xX+>ag66"7_n}iDQY+%vga"Dwws7jvbgZ[)PxaAD{UMtLoSMuCgAMA>Ald0DT3IG{WY@m%$y7i?Z`Dcix4Rj4QIupL%&QIo96C>=:2XJlwJ6UM5FHKy<Y(P_I<iey$ko#7%7lP`[@)G<47*/g0kk<Kzn/x.o_$bC010zEBcC_eK;YwrhL8fxj*f7}2sRs8_(6u;M7UtMgo7GOUqIW|o@dx1_8b%Z=rpx7v;6{)kif_[,{$|:)+:]>[ZL:wD]Jk))I)UX>(uRuFgdP0{:PO?Goz]}|{e3vzDz8bOC8b%Zbp#Zb1y2<i88IU_^oJ2]ec2ZVXaq=)1r4&&Gsz}#BxE[HA9_6A.IdXnGpqBOTvR0G!&q"9|j^Cu)KhboWMq6,m,n0,&y6jSC(ywh*t5zUc_C<]vZa9b?FnsB$7vO8H{Qmz`i$G_%Z@sKDo?p;NNVeu^J|H90+NYY0{Y&1uH%,e(8pZk9rUd%RK=)H?L(#x!^i:3,q=6u5L16=ys:uC=DIMti!?</f|lbL~4d!$Lo|]09:hBSpJ0u`*AAN/X!$SD"LA(uIAbpQ7XDYX$6HHG%s6U,YTOoz`+$w}cVk91Ww@6Ax*QR8&{Ye@Cy*1*E0}y4~InR9YaX?~fG%~NoqkZ{9&pDvjZNfMa,QRN#WPHWVU$m/Ig]^3PLGOBOak;WiB++xg<0Ec=Bf4BD3rqqY4m!aejdIR1vL<8(.mE4(PPwjC7jlZS^h&uj44$dhI#O_f|:.ThI8n@7%Wyg29,N99r)/_e<mZlEvo8zNzU3THH2cD;Z1+dcnZOz.%Z9qz7!Z9PM7U&N$7A;E^J%dDl@267dd?_a!5/g4O$7_2i*Qd3aeYJKjL5u4L+r~t@M~&z.JE!^.Si(3g{4;Q+hCV87w/no,)_&$@?TFm09*tttWjE#RN=FQqTa){A|Xq+d(p.:>1e<tDZJC*kHD[jAmzeF30NN<mA8hA8>D@?i+On,%66MOfJo&GKM)%,X<KkAi8cowk~QVjA7QWiGPIE!63}@<bi(Hl4IRDy8<0h`C#/WH^pQ<KD^oQ=B|V2.$I>?`.vbE1n2GJwB]C;i)(Cl]14OzO(OzWOayHvY_liO!9vYQ0=7GrQLzj#YgrZbB@:kY`f~`(Sa1f3*F1Apez6snyC"Eh|.cU}B=@Wz2jZ&mq>Q2T<Sfc95qp1>Z^_:HsZ<{3tBXL*F]<244=93;n%h"8rD.pad.e%x&tBJ]`Nw@g99kr/Ln]i==;g6e^V<UfUk(rAkXmz__`BgL$VGb_$[eh[i~`+n&[&)%g,^=S@:z?DwAA*v$?oDC*neii2zsd#q~!9pn6biKc%2:/(%HAW2h|HED/2=9v~pR(frZn{xb54fN=tjCCtzVk~t=!?SqaQ!7Vj3l{dk;q%}xMFw6?<1k1`T5VS?9&4saH}*s!{3e]B=J?:n?D,IY<5=vz@?VjKKH,CRl=IFY@cV&NSJmd(n"lc+ii}$_)umMf$c.*p7>iMl[5k49WI^/.EEf`Tkbv.+K/AEE{O(IfbtJ%DqB9P>ik5,YrEFgXf&{1<]_ac=vzgrmr67$L^$cRWrw[xrm{~@C,*R!dg0E{DqW.9ChqyT!b8%7_nQ9ep>>{fr8os62(Z6FomFw6X:D!:?jjnfApS^`po=9&|%a%Ogf(6=3hOLAc_pGfPRV>s5ir1oidh7dqCf:r~`dk%}"B[+lr!z&!6~Er6{Er<@R]#2I?)K^4~%x*H6a<l_2Q3TG`eN&7*{:OmVgOpgwh(!wq$c<;s2g]Yxkmz939EFAJ{s5X&=rLxtH>qi}C/Ss/29CCiv/l{<Je]k;<ZSmd]@#{{qF^!VhQ~em$gPDr#YPECc(sR%5i3li#glo{_,7t)+Q[aoF#ngBVx%B$g%;<(<?{zho9;!m(,S)XRP2`ZexdoWcF5C`CJUh<yfHdJs6f~8Tsj8r&8/t{y/zm$9cT@Tn#S7sl*6KJXFS%"f6s:XL)=Dt|ML)qfE=qR_9^t99xrqtW3y;;@%|Yu^&)*^o!MQLS4RIXS(95:/G#j:2m2SrJ56[()xS(*^u`!,WS[?;E7zO*6*E2s.t.;b,un%1YQ4(k~9A,w}XY@3:}+sAJIX4R{l"n!;;COO#UAiWyz!J+vK/Kr3,58Wua)jBr@1wFdSuPQyvu.(E;1x.9Sr}nuBj@n@ItRpRtW+<*},0jGh;:b45ipv`iPs]c3{;I@XZ.%jE|np&%(hN!$;JEj5<Zm:M[D5e,|[dwlrX1/0w*65?o(iJw>XaPuyb?`wl=}G<g|OK~*[j>|pX1&JSG([An7P4ChOuU+XX$LQ{%}NB:k:g*:eGydO`@?ma*q$,MLne#5lNRIcB#]tR<+=N^(|?0gv=?e{dk0IlD`:=03CLR.*RA8XL<;6LU0~p0,C{,WuCchf;IZy{_WpNCD2jqpQ<1<e5TdiezGfkRP7zs{**1cd/}WG6a,(e}12PsE^HRaD|94lBjQ8^/qU4<<j8p$nJSziU#2z82SC!3mEbh[HvlybTYM>h(bTlEiO??OzWx<`:I!_0ZTlu#[Zun91]/*,"wZkdsp.,bnQrIHh[ksp(;@<;lj*]/[zH15u~i#Mz$??fgVUX&u1,YLfi!l`bh<2bJ`1i="w^zP9^{Im~^JTn1A[}u|IR]N7MLqXDA^NJ`M%So*xAOp!te3O79i&W:aFho$}tkq)2cU@fxoplua;1W0}84cB+_R^QM~MzLCU.TiS6,+%+_v8vl{SWu[?X3In&*B@{1;JDE39x$BtDAzL"$AR2P[fDzX1tCx,In~@i??J[/85Ma(EyejY[X=$GUkE]#MA{[O!:WJ9#*hX;?i8EY^$PX{nljMQ(pMQKk0e|``^ai]I;C#2=*{XXk=@j!Fr2PSnH+tUy1T;Zk#lLXR%AUke;fY3P#s)i=@O{,ArXcBr,dP1pCac{PR%qCj{".Yg|n1n*:}gd^0z^YvW|GG5V`%/(4Vl5xo=TSqBJnU#W8nQ+~4MJeOW6@]Iw^GsCk?=VrS?]azpBZJZw(/IPUiSn1;UL2XV/e&TiS,LtgUuLB9C17lip:ugC%rif6dFMaBo|)nxApAi0em<cQ2P1ny7Pnp0"BH6IkL^vB%e}oiQqTJXm%KakUC#}iu`6.?x+F/m1f0_QU_PR%X?)*toR:y%al(d#1:vqdnR9O4Jq|uYJj1r*iSlr(j![R!jaNgT>x,y[hSO34h&CmpowjGt$Bakj{"e{Ib8oHmufbp`7#WMuv=w9hyuARO"`E"wZkZgu&ffvx.*jo~gQ:Ar{Zq,`ws;_VGn[@SUbQ{3"lXIrqbQ38Kkt(O1RWMVl}v_<bd>uEzL[fB)j!n`Hdf>9,o`7$Cu*i*@P6=#j`B?q&`F5#*@,+2glZ%7+rtms{yXq:lfJv<Zhb3Y$mu*[z]0|@`nt|6h)6=IJ9zZ_MusX,Zicis,J0^Sx)V`t1v&0]a=w$f&2ycR~!}i{XjNQXA)+eo~Jw>rqJbQ)#FfdM_!lF|ta@OO|_qbPdVEwR0Iquujo&Hg#psp46KrR_>3f_93Zy:yh?Ws=h)2]}f%v=J_*<.)g{.S*q$A7^_Iaelvj.im59%L=I+wMzPuiWNs|d8IFm5p,+R.URk*0+CMUNH9Jg50g[tFS?qu=y.=]NcRZukd+wKiXkRGt?VV*n~V+U.y7x{n<xUpjRp5b[C+_|;,@34={d5?FU"KzLK5/JVw3xmTxoqO(50<LTnYMT*[}.E]y%!w/<LPSM8@S1,%km|@~;i&W[Z5dOkbyn460!2#*I8ODjM~_2#&s$[Tj)bhECfV)|%^|{J+1<YyleTs&U=sc=k:d0^@A^!Vw;>3Jc46XXc^NU(uo#8Vmt;f8%Dh+W]ei(,Lj728td&%[rd/2vH2FJ*b`:+|#iX*UlB:A}ryzyhxPX+*h;FnJ8E>09nPV1=CPu,w<;SZp9MF%55$qRqkk*_Me/!~KBGcG7@%*y;X!?HoDs&mbi^S51>Di0_ers<.?o,wgPG/dd&:QBC,N4q,#BBa:Gy{mh{&6Q:)y?_]W)K0]!z)e}`jogn8c(,1O|wpb(#~81]=?r!`PW9_u.r||I+!/XF4>d{rSoY~wbBkB`at~R(+?R)+Ufm/%f|NMW?!^bYO5)Xw_II>J6|vLif@L@`axbAri&i}n,cz^6VG:Y=L3i;r+rT`Z.;y|9`kvj=:%Vp[E3EtOs&@)O"xqS/iRvL@!P[{Y_nEaX.WT*A"jQqMd]$1gfB:(#u#VmAc20^K&bft+8Ir9EaWnf+IuyTP8ES5`QT!;&}w"Si083q6<AEyn)8KDf6a<dFJX!ty4i`KUBLwPk5F2gtb},M<O<6=w8m7*HxZ9+/IUt?VdZ38">~vNc$AqLN"t*9H3)hM!4~FCA(]jsLP8ueA[93L6yB?R}SNAEtt2qVj]K6yRfq?bL1E]CKt0A_kep&5]CSetM`tCA?Pys?v.WyJ[`7ydZZWkCgI}+riNBCw0:W!S})|Z*kp<Hcg<gSN>Cr#Jnv__6&Pysgw/Yefy7Ctt*9HZG"tfGB*VAp;O/,,wIftIih|Btdp|Xqi$I=tr4FJ95KBcEAuHYRd7H7Bu+O;c&<@<yPGN7i!0:O8c;$(DtuW:C1PkU6yRf2(aJLBkt^`{edZ(Y>G@4;T8yQkN"Nhi$e_w*zFy41kNJaC0AhA~wgPcA|h5R1BGtDA(]Bt"LAR[tP0dZSX;c6xBu#}$dmx8P#j4D_WuMl4biPiX,7_&g$w{aNEXjBOCcM!^NCKxIZMh,E5QXa!yQVfNzqkg<GCXPZJ&ux%D:km86[!CyEd2dfh)ouN${I2Z:ajiVTe%P%qZG5N61.%fr}b}e=bQcP*~M1U2Kh;qfx#ny#~ldWHg1ww}6gTM=7SuK/xv*uOUld2Kk:Y{l2/}/.gb1({qo,nzweI;u9V[I;S(/Z!(:=%g;Ye6FyG8P,nD9n06!m05RrC&{!OL6lm"ac656`[~/QbX3~{VhfwKvbp{_jXGljy;m:9V`d1,MGre~j`]IFv>Rnp?}r%=Nsf"!(.%j`+=!(;J0+*Zs3Cc=u=mq}Zv)*x1t/y}P3F6^$P`:XcYYSzpjF4y])laDNanR&*Pp!<<3swWt{k";(i)*NwZc,)YBt#9EZv(czyxGf3*#Cve!.,Si:|=/;?HX|2j&q5<i:([q5~8}r!tFA3D>tmK{_mWS4`YgiiD@8jD`B?u|Dwk(D2Z>BC<bKJE4mZMng$~?I)i=}GAoF?t7YU]xpCCEByVq<,@W6rar&TRV4oAUWui)kiu|!<TvC,N,*6<B]|"uB+>rBTAKfb,BZWxl^KtCD+#$|?3t$5S(!DI+.,"FD/p~qo@lv}oxs*xptQhn4q#[<3mdPyCsMm[o(@z=s#)/:%B>uG4Z&R+501vo$%oOb|@6[D?63Z457h>dpQ/{V;>KqT_n+dR3o^hd5Lx*{mMq$p~imXo=igmOw9j@ATXdE]f5qj_=CvX4t}Xt_<_sMTrfClQb{}~nX#jR}sT4(&*W99tg0(k>q!<}S>,xA=5t1~2sba>%N9Ypd(H6v#5IW|zgsdnfA`6_}?==YgqiSn6;G4q@I^f!,#Su&JIQ"Tug_l_kP~k>73kKXQUe"]V_83Os9Fq>*^&pg,2h`m^;=Qd(}]0TikbP:TM2kh)oB2D&"7#YB[+t?E~DkS>x%SKj@c2Op7VMP6uoKmAG>tyt(?!m=wjtkx;oB+3>`!O@=$Fng$m`I5t]NK5&!J"nXu|?~N@Wj_t]A%_TA@j3$(>J|"]xE[r!DoAw:(lUj[r3=E+EDbLp83g~)oF@poG@4J/&wdP~*JL4Sxb^Q*B._=M*ds=;_YWkv1ML&XG@`CRG:^%U2}s:QPn:*U+n|xrtF.qFE`^3;,qCJc~_,yWV6b+&~_Gczz(xSY=H]ZRvCS*aAAKUBA[h?Lq*XxtIYVrtILFAKA>HPh8Z[Hi+:ymR~XAAAAAAAA>ClBM<v#G#`iMEB`;c^ZtaAh$VW=jv+Z[?{w_|>|Tdxnw.*_c.ev,ZCW~tr`{.z$(%p^A%M.bY:18m,Smf$]omw(iB;A[;vS8Q|1~P>>g1l9CpU{TWcU(l4wW!;/oe0V~]8t,%Lk<{<[XI|<4i/Iy/H(X3O*2wCAy!sYDktN,}5H6=9vf0/{l[%?i2mx7BBrm_]J{(/#AT~itX;O`dM;,!LNtm+@gEjO!^;9qwRF<#/4+XDZW*dP3Mb{$y`(7eH#Z3|{[?TH/KwwCH"dCFFrTzn1{`@]rX]AVIkmexx&{GU_.B.Z3kv$YyGs]_7yLWPb*yj)Ab1n~6NtCiTTi%Gq5|8.~@4RT:,9W3JO):Htu*i=7}S:Oh;7Eo;2+mF`3y96!dT=/vt$F+({0I<oyvrO`idxp=xg*l=_ER2kf$(/P>TwFvg_@6YC(Ea8*.EfeELxPFzqTl*#=aJssEm.vx/lJ)0hd*{~>*gw<f8Q<uf9N$Y8j7)>~$lacPzEFd9e[plS26ld1H)jJHp_G8DNb{5_6;&,L17e8X6oq+:;Pt10}~#/TfO)83Nj]@4oi6!g%6YHdZq]V#z?MW/)dh$RrjX`&ZTDx6gk]txdt:gPc)qF~5zzSP[y+0DZ8C,mxi_v3$,#I:2;"?"N$AGD#N<F|O)@EeU/G=~ml%}T@&ZNtWn%3nB.OVgxibG"Z}L923+o}JwP)afygMFc8>Zbt!]5~EoUk?W95>*Pn7"15l2[wr7JtijcMq:ll7=a9T5WGk=wuNB#R&k>^m]l9Ykur[hJRVYXMzng7]G6bh=0N_p5)h4hI[>iJsCuE[yu%C,bTvy.EKThDb{aY_kfG)jmu.AI:SR|,Fn`LsDogu0h{kZao)[3+[F=Y%bJlQ=/XpM=823V4.X^u"h@@|35mwSPi}Kcxc?^"NRztva^/MAv={B.7D1RT^qkxI<CP%u.@I$o,)Au8(EV$Tx!EWR7#J05[jOOLX!P$F@Oa/c/Qe]OBSA<*M.B>fi,Nq`80rJQMDchru_?0n4t(>p{ImrXeu*g?=Halb+;C)=4o<JQf]&p8UDkJ^6otJAbFeh>KUUd*H?:aJ2XNBm^ATb69b9QHx^b>SD"=!y/;{_o}Dk6V:o0"T6[lr2;%ELB?l$p9RB~~w|~G;:PHq((BTmxIazT%ZrD#"yKUmlvx+1~9uXKk]%!#1)h+OdKw=]QDKSOlM`M+G&1O1l{RB0>P4THGa!U~r}Qj]e9/T[%v=81D)}mxJ6g7j!u)vjjJal%_*LaPyUk%|Z9A`q[K(MzHk;72Y)ap2PQ[E(QaqO<:zI3!_[qLB}7Mqo`b=iQ<xB,agmw<P?pKBdsKmvXo|n"x)c6[iX8}|>p2^@DDH:+=?)/GY1|;!?/!J?o0w>BGt%.&i%]WpjAA7w)$%03!}QPxrUVR:LrapMNJB:[JCt(u:?v&@"WHt3)u~9%qp)hN%_sHp0JT_:z4<G/hl&S55:x8R_o%j_Ci?G^_jUkoHKFQ:2JEV)qg.R@qNN105t4+ysQbJIUHa=.JuriO[*S#~x@b1isC#wyo}zKuSOvhCcl0ZCqZ|e#8?Ac?PF}/E`&caouZo4ClqTo)&2R[2(Sm9<!4+oX2p<DX<lS=Jy2x:ZsSy?BF4AD%By]6k@DwIaBb@(sc?|skYo8*`YjM&#xQ=axsHvrV#e,0ZA@LiB2X#E<F;i?#:%NzEF.eeX@_N2JEFq<n(0xg?1)2+@VD3,NbFlXeWSJOOE?}T9wv/0+>yjWjZD^aL^QR5PYYNoGgm@}B&|d=daHf2;>CVK}QiXH/#H<!l(,Q2dm%{Sd!vBPs*B2elejS0RI)tb=Iu[n<nxS7.VgPKei&=57(tU9u7hiD}553ts<}>G2w&?W~k"Hf1^B<Ei"9DfZ})`a//O5i%{@zqeYhYdXV6~XqkQQY9^%[?.ebTezsrLQTKG*]ZRw.Db4K67Nz6DIIt6/Ct]H1i@Q0j1wu.hPw;9ex(dcu+LPJBzQ,>2iNOlBu/3FyPFdcGuBDVq;.<]@E5V2s]|FSYJlVFBB[4emuk+U@4=d|4q]tzPrr#/nU*}vN25$L)._BjHt{g%kr`Kgad%4v0d*ge)8U0"d>SZKSHnNRI+3=+ftKp"V0Pg>ekz"o1hkny]#U:cxDGE_*Go!><3Lk9rl4;%+sf00BpEd"yG"=<n|Rhpn]!NjNc;o0$&o_:p$*f?PwV$!x@d$QysV]^CnT8#bVl[1he~e$9]yleb?T9$JA6pU(+`I/q8C:0n^;#M;&}#bir?Fs31:xRlmsECwWFnl[GPg*/qs+b5H{v<6=}mR/EDs5rb?Dx6}>{7!l)11?Xdx]zLZoYd``+]]ctZem6nenraQK*Y4sEYF77?K*s)U#6|J9r,"<&JQ,F!$EK3B98@(%p,?.s*_|DkhU(:#/1j`2b:0_De:q/2}Lj`S=UmWqlSHJQUFi)$|NOiO*}(H!1PgfjuLxjHn,2/2$_&O{B=S@B."wJThOj%k%gd=d_tS"=!SpxN5HOEBi}9Z8(n1~W}3xN}0RX^a3DbgNng/7l#K!~|{~F_47UI@r!1Zh_eshpmpR/zv!cJVnmhG<u|BHk$N@a%h>~c0E&^z*N/x{nvmb|R}X(MtVQ1x*ro5=M|_]p@LK9"|6&mntQ@T{PIkw*r>>{9p<>@W3{S!<^7fKA3ow+]5c]ma.8:ss#G~NLMI.M>Pj/`C?T${3uJ)zN"D%>;kQZ,RZq[:=NPp/nfg8P1z1,SS":Jk@B+tlvWC1D:P>Z`!EX5JJ*3)*Nla#S(I7!)7qS>H/AS2lk+tJ8Q7E9rGa/]0TjavHUpMi/^)#mF3JXN_s*/;!LHJqR!E5#orNqUsa#T&nVOGDm=!zdGQ!Yp/jNqPj5](3SXs<Lt.BDQ"~m&iHxWJZsv9:3bVUg{BSZn6:_+2~wk]#G_^F@|HBXL5SoRmVy~>2l^Dir[n3.`{}r2VQFEA$D6_/gxqs8NT<0|)#863,r7)8(&$g{X(2fK!.$0i/bB~`@=a&*FG28JLHtqg.1:XM$+:P0bQzp.ZXa5glhCPE|}DxNZ_J`,Yd&vwbkVm$az%eY%*)S{(8u.^7vKEu%|Jv3/eHZOCSjWx9Q<IRMk|S~SvufOJqhjm%Qnu"Sf<YBz7stF%>;bQQsM1DvuR~5%5cRx`YK,>CX|mQQ^$Q/RW#p)giMQjXto>bP~<.B6b>dP0m6dJ])d(Sf.4o2E^pmMyE2AkV[/kP<vp_`{mR}c7*#N3c3msE7Vco^Kj`Rr%tbBWa8xj}&r/?Hj!;t(bmPWb&WVpapQC>u7|q)f]188XisV^Fu*gSVX!,OvEJv){Fi`9wV#)ozr154lmy>v[t++(To,S>s&"{aDz]?EuiJlx5LxG{0{rI=|NvN0<iUUXW"o2gI7k.TLwy!ahWC6Ktge_k6B3[3tYoM(YusL&AjNk~wK+1;inB91/g`Ap{8N422:f6jLTHgd<;+ILdZ4EUeR5>#^)F=L|o)c]gCcj}D[b.[8L})xv{mK]W*M&~F?B|w.^09|wQG.2+De~Ul7|,?3a/zVU6jXaHg:ke</v]B&dKe[/KG~9c:`(FLc0Jrh7nV_[Ysk4!3?[&.@^9TF!p1sYNdFGqL5*0n,FY0}C^$+mycL&m*s"hD*q0:j!@>d,:fNZO]lq.#87j7@+DP*x[F;gIvlu:gaZ}U+B0*qrNB!Z!+>j&:}L1|Ke*nV816FuP$^/5bw2;E*IqEW;XBD}Gt01o)gly&.mojE[U054RJ^M:Qcd7QAYP(GOqcMj9cgA;Z{hv,qy*UHoJZxD*P1QtTP7hCYv]_r&s74qO@A2pD/n>8N1&HPz/w;f?s.LQ>nQgmZh)t26Yp"mhhz;@HkV7sNQ{ExSi"zkkLsV<{yc3k{C;ZTp))4_Yl"N<mAobhoWK?!JnzxQ_8x:8qYu<!EXpxQ0zaN3pyJSh{Du"DwC8tQlD70|<Ds1&l4OsI;H7HUY)]QOuXt<|ouEUq^sBSHi`C5W]BZyr6),!.F7i&cuMKwR1&~`L!4+9Et[i8ym||{ogXd=BR*Jy("oxUv+Q1IP&_M=4GTM6@9tFrIy#Y3/HXlHV@GGY|D*m)4MH1]7ov*uU~ei.%il*G^O&bd~"OW{gH:}f%I{}(Lw7&jD7Ill7^7{8E+TPBNfrE}0>JnzSV<c1c^W>$z#yCxrX,M~f5Z68YBFuu70|bl0n;dFB3wtVYlXW4t[=:r=ENJZ(e)325UNk*jsWQSWK>"L7W&nQ6nA);bu~$:mc|7vZzt$ZD#WAo?0U!w]$NrB^f$+eMyJ?p5=|cb&AO4AmN|E/+}jt?1pa|"#)Vec,x;5)[p),kD0VBLCK_P_^s!QD3y^G(VGv!+B}Jz93#,htgtO,7u%P):Y+@EW)i/)wJHui@<u6erfsFI46|qSwiXSbr"v.?gz,MEz`YEBS#U_&%z2&`<}3=1j`h:]12=""]e|i{J6K|Zg{,@`.w1;"p:E@{sR6O$L(zq22O2|@`oQm$!i*@~j}#/a.&o.j8v*/D$aHA}ZI*L|Oha18(wn)k/+v9(8zst|*gOcFy/NUK&k1{^y6W&z_`q*3<n7,qIawTRcY("LC8lM;Uwm#12dW1OTdN>6Gs(,0{MROm*2aC1;`AP/~Fe4y9hiRXZ:&NgK}|Z!y:%VP|5F]TTkE8r]et#0DBP4lbCXJ>(12qm(SH/+(@[L/EUT$+Uw=on?A@ukPTGX*J^;jL7cWjFTLnn=XgN0/uW2_XPwc=qZ<]0L3q4c8o&M_W7Z(MzP!(%)coqnlK>LsX<2BYbrh(P6kGw*h5E{Z(rW.CuKPHg[U8o=XH,@1zTLP,%@L)$.}c$n#*;rS5N5e|LMLKs>H%?CJDle//gzjhwnhWmKYY9l;#*TQfK&zIImZ3jxYVLawE6INwR^wiP9_T%NuL@nrsV}07!&IC:ZM[8:C*DH3sMu[.EpTW@(&]L]M&jHiNgM>x:%?Ep9gqi^<%l_c<Vl(8(D?)PQATpF3:J:GEHz*"4.xQQU//fSP![`J,O!qY;(/ka/<>;"*UAwNNjIL:_F|X$*CR)shG.9Se71B`ieDSCNbImS[.8UG)&<C.U;}QzpPdG1f:5+7ye@<m#Y)V^tH.D71I!gPA%q%N1;n6VaTy[8EtC>8;#n_<yOxr!L&?uF.Y>GVUCAd<j(NogEeSlMfd#iawQU=0eDP*aY1gwG/VW0xSo+|;<ikGDTnX.p>%4,0P(x&O=W3kjZX7FG$bAW8K078zm2]`t7b{nL_J7T]J.+<YB#^7*hIRbNKA7?+,!n)8q/l%V{>?|*}w2<nXw>JZ<*|dy29~h,VQF(dO2[|tozjK?c</HM@uDYQg]?Bdl^8E~</YwfP[<e}Cw>])zUIpuKSeA1GA277>SHw[L$"^LRZRRdJACmTEpx2.h#*Y],N6_|8o&c`JIgs_)(1v&S`=kas+0~x2iPhtn[VMWa^ROo{I"88FIM%^[!20eP20_.zXjeTSe~TcD]LY]:IwAdt0>gf!8?#+)Jg27O9IKbgOK>}kqG_Vub)FQ~e]#q&|l#DRDCNsT!ufdRFdRlJ:x;+_">^$"zY7KP9o}Nk;u@LVAiU=G5,}do/qt73Q=TYO}CRR/a@:o6;4e_.d}8$}<VYQG^m^xb,][P/1<c@SE]FP,uI+G3&/jt0etHHDwv*@}DZ/9DMzjGbO{bgRhm^1mL~Zi7jH#VN$.,7!B]aj:SE2(]N1AXheU5:x+i/EXlr59(:MF,|T=tu|$}i:C&8L>s`wy+VMZ/:w{TEtZ86Do[V0os?f&$wO+Jar:Ipd_<G=P.lHxj?[3oe(BzOYzQS8;|<w`nDc&k<=#%Gc>90#1V5FyWY/l+7!wG=d9m[KA1URW5Dx=vO#&.h9%x&paf6g8FY|d=f_4NKncE3#lXdE{Xv05XcWR=yNs|u08VeJc{`GbFKV9WU.,X[xv5)at:Mhl39GUk1r>guN85u_:,_FW+Tos,DHF+`gu7OApw9upJr6PF$m+:}6?t_ILW0E!Lg%2CeXS18tE~49".<(lu5XBF%AwYRU47}DcqZfAeL:N`Kc5:nik0jEfZA0!s/(]dmYCMAgQbQ]ZP/hdQiKG/?]Z{Y,R@;Ml;oXNib6c2@FJ+S>{0G]=D&hYPGb9Sl^aRX~c6MCu+PTj&]Z}7kr1vHwiu@sXwNp3HQlqY*#(H|/Gf(W^1ch3s4AhcuWM^LCJH+H9S;Q6<9D^o5QOyPjqtwS@/4thYVtq@RyM?Tn[Q|W)&XOHWe^^F5qI3ao}jL#L;gQBJBYJ}cJ[12!zM|$Rd?]Y+z+c3K6|5X:T?RX`4,>fuf.1;jh,+n!Pmv,!R*>9zWpo3r/28k8j.w8wN~`43dL4E6PbSL}b?a~JQ?Yt;}1xD^lf+`37R(KE|Zzb&Sz@ZcFl^<c;Q1XXt:V7Qw}QqvzK.L2FX/!BL>69JE+a/`=}S_S+B$IFFp$Vn~OG7iE]GIi],16Fvd$_K=L!X#Zoiibt8@/.ai%n>oiofgvc8;TY)F)cX*&^i7v^%=Y`B~M?W+dZ+wt|K*;Tp>D1LY#eOrfW5K%)2;f+LBm9S;c7cHL?YlaF!rKxtN|Mn%%`yZBG*h,Vw`bZ.5|!@6ZvN~*Vr4s9jD98i@r=x)[/(uDNZG1z9}h3Hz*O1d;_vM)7Cwp0K5^Glt|O<pRXKTQ7s15ZzL6iP8mmURv|wDI&JET=oT|TnfA~%X4?ul=t1/`w[RTLbpK;l8yFNfo&SJ443bjB,Cv!}dhWHPe|uN@&YsN#b][8g.#bVv@I*Y6Vss%O*J3rhx:EzJcuuS#mNk3r,utNVyeMvdS?!^jZ^C/F^.g"kUSmx}?,z)CQw_j^YU/kb3Z.k7p`0"m_$,A)urrGzL?^rWhFHgR2@`HO=^$0V]]t;vB+uV,D:EPs(X#M02nw+sR.w`?R{`P#XymQ5K2farrPSw"]"]EOl5l5QEx}.:R_^)R=.+[g0;`~DCIG#|a(Vz+G=IOI)sDQg/`:5P=$5;;bMxR64x7";gI_*NqU>:M2RqxHucnY}tcN|7cJp]*SPIv2zFU!HzquVAI=W&U).FrVwXZAs71Ko_@s_ng1RVX(_H)BvV{.7$/;l)8~ewS>S5jJ4sKaLsdAe(irXk2m[FNACm0bU`V:aI${`bC&]?}03qaU&YHHJd/cqc|?%b.,PudFX=RGb|T2gY%8hn=wTj%)tWOD0p(2w`>82ue]$`.ooSI2m{/f8dkliMMix(E@vko<N+*n68i(N$v~n{k/Kx)5r$i!5QlQ(d)RkU>qK*Rm[omgc@V_mh1z$}wpdxy{u]^~[HGwV8iR<5F0Km0iI5#2Gmyh8qCmE0Y8Rmo#FVpTy`[dT#(cq{x}b04wqD&zDpoH:YXrK7suL"5.r^!!$PHxws_Hh~kv*&&%chm7_Yr;:WNEQy=9~,A?fU!M6!/pI$H=5JptsfdCqjl_Um,Rasw^>El_k}I=je)j8Q=Qf`,F?w7S7f0S72l6w{tVjAOV/R?Tg0j=I}G$omzl*.hVp:?/!W?H3y(u("v9v5f8{s/g+Ran4j%!yKp7Y|7H~k8Q/aH$6.do@tYc(0`xH2V|uefyjsnD>qpXZG>zw_R4i((5)0+&$MHr22w*1Qfp+<:Efleqtv?%pAl><@O84bk7upZ1t[n!whKPv!ANGr%&PHF@Q9pc@"skqQ?{Zk;e{81_kKK3rU4*:*_GCv!Xip?Bd?kDDfPozNC6*:m$hZ!736KHS#*5J@p_?Gq{$;Pb4C?;l;Tv8fw|_*wbpYO8SvLy$`&_%;,JN78rO[w{~A!}kz.J:<{"$~_[nW;2G&<^@l~#WfMlj_Ax0E])EMMtYRh<Ny&d`Fl:*[vk6ilhld|*^7IX*~#hX5`yvjKo3S~JL<.R:R2c]bX%n%D<$!e.BwtMqaW"*5i=uBIN;vq0p8i9LCJAfYa$PjZ{"%TS`U7t!oi+Rd98,_|9ijGOzu(4Lb+Nm?];YqnEd?Fp~r~Q0jC:_UQ]mN/K#BuIe:r]Zkw~oNuk|,?ipn`ojG**nCzpD]WtzIc/4<$5@(9n%,Vi?IX%jAZ.gPe=Q][aK!=[Oa;q;.PG35f5"6~:Vk)n;8M<y3())Px7+x~QwyM%B]Lhp<@K`,t^i#Cd5>%Vs}rK3z2PL"$lID&=6MYR1TX6_YD/`yI@U?;>!]_J/%H/I1xDi|S).iRj1Li?!sjhI=c#{Z;P|[DXQ~TYLQ#M1bGA%Z;:K:L@Re^g5Q;`o3n#Yr&2z]Ek,!#6".].=&PtZTmJ1q6}HbfXI1.qq~E{8D):SnZ~[6a@n28uuRY[<skLmW!^}BcC%wXS<.7]d`5|t_L@gl[?u"d&Z|[yIDdyS/gKlws7K|}sUw7/Fh[3sm$C`nV7X!*""kTjA})7GW0@S]F.+dEMyS1HqcgI"qhze}>7[6pe*pF@j_e/AC7IJpSULHL3y7Vfv%ON3Uz}heZ?ve/~7et6=h2r~}(z!h?eN0V)"6PWPyGd]DWb,c_766L[n^o6,kiNnv^;WzK"VGq&n!N*2e3#wLfM^B"_]h1np#eB)oB8s!1@Puv6/gY2F%Ir3F#x`CBYa"f~7b>H26c3|$nb./JvVkWeEGLd9%>XX>,P_nH!Nd|IYNf),Bk(x~bd:8tkSm`OUCdhFXR,bUOa4Y{4p3*d}G)[6oL.jT+{CMrBaEaV2x3>%+S7PmI+E{|I#j`q+(b#~!{z!dk9W2z$h?w:}NDWiTB"vzZXN?I_c0)[.(?0.ojz40^O{MRLNY)prHp!/rkEBerv>Mx@n)|ni+.AwD%Y,ri_8gj2r"oF4]+o=Y[ab=EwL9Z9&H($?%=fyY)!a9Z6}q0>3:N6~~5*?40m5Csvurp%,VW|&Lwu;1gUNu9{h#qR7_Lgz"XNWiM@Xn<v]m48{@Ox~{GS4nLql/C)gu++8>GIGqi3z/F%JKH`7|^5_I$fO]|8`uq^,!fh}h:]J=J17Scsz@l!lT~sD/=zHT/QC+:7.3J{&[m]Z4DWu_{V~OU}Ut.c9,ZBLUYWE/,IdBX?EMd+8en+h1:Gz|GA#ZHeA#QGx~z`W@!{jtvQ[x0sg?DlUJQ.a}(3l@iq~fng5#.>CS$B1Gl=Q<J?uTdw_w*<?#M)h{n!st|a#t_eIBSEXxM8J&`y<<6]ipEUWo^2/C~XIi[L>S#]lWaEshF_x?zA7R}05MRe]HYG^0`oWIqs3A]&0z8/tkDu^`];C3&p,T]?fm(p?]}LqvK&o??/a!HEK2Z?@2qCSIF[Nl+[dih!#z5QKL1+wG`{/=3>`6&D)WdY52<`|[__{b@cTj;W&/b@q,*_?8{QJIaA~+VQsX5;+"fd,|P_KQc(F9?DbLocF?N=*ZG$twzT3&NaXH_lf>RC.;P.ie:[13(mu/i0?/QMm*YuNR*LFuU>6$S{WUi)~EB%+(K#"HxEdE3jL|J9~37Ph!PBS7Fe0URN!5mZJ<0|)H?x0Fn9Ojq0g)7x{W}Lzh+X<w,u0<6:q{zsH9#Omkn0YF7^:z)<]#m{7K)V<W79)1eJ7i}l[xlFr/yeFh!@mXEE?PZ&~6jR;OzMz.):wq):v|6!f9Sc{sB=t#iMw$H!mK&7g|__iCJwsj$>qzaD9d1q@l~tX1lv#kr{>NXjdH`)Wbx9:hJ6fJ6|ZAEk+K*q.5Z~AC1Ot{FFnq}N@y{<9yMjM"IteWH2v:*a!vLUby5ePR,;|g[~z7P;#ffjBpLya{eH+&P|KEM(oVj;P>/FGshe~6LTiRZ?7[aZS@Hi4hmGK]2fjf#{2?a0N^5_IK$37N])+f&{#&c*DP8UkVI_2m)XBKY#oK|d~zN0{%|H:7N^YqL5]+040K*$`m7H3BnIuv1Azbi%T~hsIAno;+u{,kz[)i<J`5v<8RQC9fG>Z{XfJk={VNJL?_L,?XjnTv_hl=r@`RO0_BiZl4FqNPuPsb*OOeJ>,S+=^Oho+T?PC?^1W;%h.pQh<Tb_!2:[x[lz`!eRg"QbQb&kDIi$F+Xsk(#5/!3P/kY[i>8L%Z*KFZn+N_`7e.RH:6O:tqz7hJj|SPHlh:tF*(:oJ[YG7w$^2LI5>kwY:UaOb_(5l;>GuUh@S6f(g*{?uMxBy/r;|!SS]/n8B#_^Y@nOPIVYk]GCnXKjWafKbwO7Ly,uy%5Lb:SYa]mhu`+Pj0j?8Sd!ex>f)>_EN%OuS_]XIa<{#bS5Wl7hh0Bn0jfHXOvhDX+DU?RNh6Q&AXYe>zb6|S/L+E#u:X.Q>xme.8*^jn#}]H]b6].QA3B0i#GQmrb!ZeKADf~N*`FlKo[ZH[l])r&?BX%M3`Z$fFe`vrPh/hE"h_2%C8/r)_xy:ZQfHlD+r9971(D($&@/>cu),q*vc/kkZwXq&GEu4h{p~n86cwIME4^5i>6_bGn0Vp7K52?56R:,|k}3YyF[YMtGSED9vece:yx~JNE2Vnvc$I|g2+aVhQQtqa,/i=?vi*{zx7dT.m+e|8Y%9{XV2`bk&M!jr^"FvC:.d$+Re.|$9PRs:(ql?f76n78v6.J^:uJrMs(#!V>+muB.jgY(dL&<`@8A5uB4@{%3x.(R~~)#V^XJuyUm!<>KTRKBfQ0Q</F)bm#$Iz(6~.eaJhN]`p(ion7Qiq/]DHr]7!Dh~P:y)+dH8enFfJ#FE{iR>!v7vDMbDFSxIU<`IM($Q?4{^Q;`YK_wkB;^;[|8_1;l4eIA!A&omF$"PYc~gZe<}Y~cCB@PRmnwk>B5L.MZFjB*n2M.hFKN(ekwM&P]a~JCcsxxi.!|PUO0wHXbhGy(!5W$}DO6;@YA]C@:$b@wt?0|/.x*M^1+i$YblknfsFvF2?o%[sjCq1%@@<uq7QZF[[*Kw?Py!otm}M>?GC*$#})e=}h3)4}pHOT3gJi3HO]gD1XaD.#a4,sAUWf.Oy@,W]~D#a^<ML3tuGMuiK%>74o0NvP2G{b.D|p6~)>g5(!vFq.(dH!@bw}!&=j1=obMjmm,t{G/d`5`&Ce@Dm=Yzk6;9uM<v=vc1:RA=wHR<sdsV,]_1`!D:qdj.]Yq`&#GrzVuKj^CNmjl"QVXUQKfIMaEh<S=FzhRiYY?Oa9Iik(Sv"%:i8|:g&5]MB}/,h!qSZ^G6s*N1")Y!cnZkSKI]}RqU(ZV<iirc{:@y}*:<Jnh_[FX&$GQd`+Fts,#O$6xI2v_!E2C)17dO#r{7@miYwo$sW2&Ll7H%"!2I%Nj{LRZq>K;obxnunfdNN+U}Z/AYc|@rJNt3,Txbo$6NT"xP(W3i%w~@M_}3]YNa?!6#Zmf(uZ*(E<{~3c^@G(9N7AhgSB=bX<w4,H>mzK=o@v<Fg9?vDI{<7W],E_mKKK8,4{QuVStzt{]Az&4%/FShDl^*xgdxQ4$*eQ8vTzXyeO=yW@7$gyu0CwnT%y{fcZS/x,SJj@$+{;]];%|09l@*|^Iaq)0J6zWzNw7znud~c7GqEB4<L+4UR$:S9RhX7%.^UbHS|A4CC!vg~$j!=<?hH/PMPe:/<+4ZDuaQzIH`mxunJ/tlX{Zv5npA3::*=zNdO}c7,tq:Yl}Fi8J9*dfm~@ium9K[52s5bk]5k7Oqc+8?d7k^CaC%=ntds}8Sj#Z9zL/n3o&E>WCP;W_I>Hd3/u;`g/jh/M(6617:y:4x#",G}X5`{N5J,yitLYg@JB(k^Z>FpAm=1)&i[[b%UlG6_:zVAE$>@+sVssBksu${+hqz_&aNL_ruB+Cw!Eyd,mfj=<eZi+S)gU!V{^)e(yzQde]d$g$!K%wr^XV5.6b$?rZPVReLj1Bt>eG^Z=JPbxjP952IhGx*:u{L3)CZT{?R(6(O/^KC*_|Wa7;]hUZ7cMcnA.CYy_vH+n&N1:1iYYx0?^AWgPrWe:aSMs*^08?.y|.yg~?e"vq3sU=*8T}E]3+;^nv["DhjDP?{cNJW6;Rr&9n6,y+x2dzGPln/a=94RS`Y!;@@*59SL3j|+31e<V"%P*48XzC@AJA;]gpO|_84YToDXn]>:/og88H%i2^hkH#P`I1f1Pp2Hzj/IhAThX[_Z+`PF@7kLdY+NinWH~_k(+zO_Q4SP?,MxEDd`oVD(M}<HNK00k!hDeGjstyYYdxx4oC1_b^3T`(0eQSbTH19:VXKmt^VJ$ei#l.uZnH!OHq0J`1O*<_s!SeZ2;SJgs>jV6.;um!py{UpQxi`LL<HSF%UZ"`GS`[h3X4yCC,<RJ)1wL2u(L60)o^~:6o7$k_oo6^e&n)Ia8zD?RWpSuz5(8]*DlHd*H)aR_5&PV"[KMg?<fk}b1vfhaMey*{tSKm,pD%Q}V4Z$VC&f]%/)wpClM_,Xj8[6=t#L/AcLGDLY~l(U*xbBI}*#_l|P0)t&yH_L8uy/B}@!;UsQ}&f"i|lM*^X=?@V30R_|^XT&WOLt!oEVk1|45,(E;z<JZ"5"oA]SqwryW$I=HbxlyPvXYF&PxXxDf~tiIS;cdBcl1j,>0ubhMLE5*XEcob3:zpW![Qy~Y@d+*M%/O3~ONsG}wO;]l$1vQtZXqy!<o7[<h>qP>hRE$fDr6nn`|D7GUt)"xeYiIwX1n.%/)[lL2^tUx*Uf=P_^]|1$M/MBSFhc#L,WmJl:kX:!<t2#8R}*t>BWQHCOc)Qt*Wt"T[dd]c1ZaV$~O%0Rs5;C4wG7YkuUr0OQ10tNHBzjLeb<+6rfe`V(y>1]ad|5]F7Hs1w+}969fDYdD~=7?]$Oq4x!BzSDWd/tI*d5:Ms,k?Xm?*i!w{#UpPoJn7b?5kKyL>ZE,m|9,!w4&<?TV^FHt|JU,!Gp`:%HO~Q8v=6CL=Z_7yO:eKdyMw[NCN_ER}1x0@OPz5`Ie?S/p~=p[w5nt[J)hDEC.bC9!"riZFnU1e<mEi7P|Mi^Hw(pVv_q>4x|[bG=9P?}erisCxJ;Wh5wd[OD2Wgxk,MQnehvFM]1`"}$TNniCh?7}Y/ybbS5A)@^I]K#{8oX{.1pel^jznTDK<AVvI^}NAg,<s9(>VPq)X/Kx23SmVwcvU)d1:U.Y1053@=U<C9/o/0&.d?$e$(ZV1*()3yOKpm*Br(I;s}+vdtY<pn3_5$@2F1Ed`J?o^N,h!w&I?rwSCZ<nLsLFy~vwHSf$h*w,Z36!r@|~gP~sxxn{&M<iF{SUm"N3wl<mti_rtW=[yb1b=ve1<8G^,";;zp*Ew7/*!+2&2Rl2;/)89od_W@`W`d8/h=i@PfCpoH+Zs0DLNLL]+`)i^|)H8_oH=;J058w8V9vHQz7riB%eb$t?Yar@rP)Uj.(R4YgW1y6&xX8a1M9Q1%98W5Q}WW`EuYXVr;zT0@!rEW<d6Vf*=(97.<bjOKD4.nUaNLhwEA.VxD~1O@rxyEAhs][qaGr8[kB?LVL{][_nxud&nyZe:4h_~rt*6Jd[RDBnr=_878gs_=jr4SfJ(j2P/;`Xv*9Sx#3J#(V?<m,({@&3OTqMT6+g`=}(5YD7kRPy7AE$bBppyO2]Ckx?Wcj6HR;2aQZ4v0SX3t+<z27x<l]?N~Y8TvbpQR43s[lF[$~fG=ef#sJ"3q2VwJ]ZJ`rUmcd"(8,:[wintf+#h"sW8F<7lOc/F({)q;ivE;5SH(fI5cc1T1l=V4]aff$qZp5B5R^kCrZ7N~*^Cou,g~9q.$ooI4OSn8,H%F]NJ^+Y0$f8a7dFp^Is.bu^}&vu8"|0KYB];rb3<euJB.S!x2:pB.%u}dO%I(a.N%]di(?3xcnHZ0A~1/G+3m5MYDV6<U#e8"tyOd7KF+j(OiDyG;xRqe6*Ba+rCw]sQ@+*:Uf1FFrb[popHyTbhq)8j+V)!?%skE@bRyAl{DtDX|5FV+,lB)y<2J(e]1N1E]+ojQib,C0$W}4P<|NG$<3g!#)6e&BLA$^7}{N:nvl]Xj+4)Q`q?nF}k_i/c`(PX5oV_g"Qr{=u2z>E[Wqm7iOE[s2e#~~sr8t{[|:1]I~k99|9.o`mU/&N^]idb<d4<Xl,|XQ*5mbsUAkuvC7+MsP9)9IhVh9HT8SN>!d"hv.qdy,Iz+F6q~?4b@^Jtm{r6`fzbHY_x)nN~PYPj+#5?_C=60%WdFJnN7]a|HddlK)CVfC_[8[$OrN>~x1BL_PcAU`a;<)bxJO$fu&wvsTS75^BtVkR(Wp+Rnc%.*wNa:*$cF5K7;p_!;w8(q9|$iY<TmXU5Ix{D0?_$~3kHKuE+=ei]u4fzdr/UYzjiK21y3x90&4>({%Hj<853&Vp|.,T6)R1SxSb$k2TD?QC7:.A$#{%qJ@)XH}u_AC:&<3"])tbqW$h{qEu1ra</3K@l0(jO8Ea~NI=dI|VsMvp[nM)&4IWgM/H{x)(PbwMob/w)7Bw1A[ywH9$Jy>5LOxn&1k8F7Yc`0x"mD.n|f`N&1G$b5>hS*Liogz:riiDStp9zLtYR%x/3%l_@XHTVxB&0A=2B"`bsI5b]0x>JA9@qj.]]]x2#+k5/Ye3Q3vW,*4FJo;3#/N3:<]?O=qUkiKj2p?5t$Hg_3k~Zby(Kuud?4fpV)HHP^L!c1mg?QCK/<Rkc~z$"Ws#)K"f3D%Q]Rw2)}{]>;fpBh0k(dXD9kT4ylt4_oO)lM_<h7D<^eSh#4&=)xbk,rT!!GL2(NwwDzg"kRcUD,VYCBE$I7V;9$?c^Bf=n,g+cvbpo;,17QN(Tqj5d@M@s0H6/fM`Uw*oNkqX)D(qJc#sgpQ:Y2uGJxrG!aXWg>`3yP&01Gl1kQss2p<u@v35[MfA[j15#GvO{pdLTBa@RF0;3a"Q[#Sk=KK4i|+V|U3k0P]Em<w:,4MgLZw=t[<]rgjMaE!)UkMcI:Ip2*@h4=rtN+9_`~<|Lc32{shPeA(}Lo0R)oq3";)^zvq~;S&{Y!l!MOQ.)#8!ryvoGwhCP53lP"aWUs#^@V.XG1t+0dMh@&>tGd?QU/%&=6E_lzCGk(:&p5tqG&I^P[i00HN?,Ld>2|9TGv_Oujc:<D/?v$9:r<R)fAhlz?/V5dv^u=^rR%L)k9R,RY#MNqp2xf&i@E^b2BY{Y9J*.N}%9CSa>jJrzbwKF,oLx9MS!Pix;<t.+DB61t3cTxWW&dV^JN+GYmMMF98BJ*(*xVPunO&J&0KX@cE_f@J%Z5019DZWK#f@U;nK<ccb"4]xDce2vwufjvPONZ)DQ[&y:pr)UL^+x]WZ5ee2LIxl0Kgy:m"R.l7$O0}N7)iEGQ3g2v6xz%)0/FjP2ZTA9/KC#Wvrw52~w@ZQCcqL]XU>YvAOr#G$8A&=?Xp]xL=c76Q&ppR.x,_}!`vjhgt(6KCkb0VQ]Kp}]NK&!11:9BFB[bP`(yxP!/!l595D6VQ%$94.PC.N@RM%lfl(*uAY)Pt_0qM{fg!~kSJe7tn>h:}kD2$GqA.f3|QWVMY/.gt5PMzSDtiRJ?oiFnUjlsCi8srMj|N?@waq554%FP/,c[JR=Z<5>~5#I0j<n0SPbt&3Mt"pVSg#J0#0`i,quTxyB]XO1`u8*,cKO]v"z:hFJ`.l=T?HPxqU<mLV8d~q6|7G%WeqSwxDxAo%(N.jqJdQ,B@{>pexM/d_dKeS1495"/UuN_Pc|)ciq^kZ*[Cr]C<evN_qjdWWx9_cEXD;T/QDmC9J!qnd;Ydp;z*)dAmMQY>aj*x($wu$hq6lhg6SizR4KUYE4b&_J7!a:05{EB@E+",<zx>=G^P+DSR5(j;h>N1Dvu=O""0":RTsas4fD5x24pt2SUvXih|D@tdx+Olghes:sMmr0vbtcyGN0!aWT8s]@IB/amg}q+_Fu/vI9xg[N,WOQEhzCI%j|(MN;#)M}NTGUcOkN*7)8Tz*]_)nOK#Kc9~/1u(%k}xfxv3}uH%~E5u+REVnH8j;bH5J^I["Q}Y![A~`oi(F38cW0_}Kld79bO_FoB(47_X)L:83liV(:"]BjN}0E[dOeh:XId;UeT(C)_qSTH5`UC@MD42]4Vw{PMaD)f[r^?bBsBE``NO)CZr%5AS]o>zztS`GD~c%2F/g)?XdCUE<C1qnHf&rg*?Td)QT0@6d#qwUtXMeTlV5pQL,F/iHH|lRzgYp5VAiV8"{MG^)"rx&m*:<HDv294&.?.B;;kXzXo}`=Zf{2"8|WmF.H46eQvv?~GR8*}%IWAdd;l~MO>tHN|yTt;tT4K1Qw^~f5DJ(?$6=%/a|/KhC6M?`oEN"Do%co]>jb#y0yGLT%0f`%cG_6+Lw>&#>Qq~947tY;~Lubh+1aCh<c^(Jnjl#z:M(I2WO&58w+nFjy`UG;t#~H.#XF!04vT/hFbhlDq|5QSrSk<]Hm5djmbrN0H=lDpM/I5R<eO4$%~toJxaC)qr"?otxXg^Sr%P60J$eYQe1A~)HAE)az]6u?:RZ&UtB}2WA)]vW15QOp48elq4Cd!Q>hpSOMTfm5kO3=Om:U+m3E0v*deElwo?Beg1!+bBxq"u^xTtrNjxX[L6Cab&#}xiyUlilU{E2$};"?JXxIcbY=MvB=Dv"$L96Q<F[5bPg3Y]B)x`DmX:U%Jumgz98USka}/f:28+Q]IAu3fKRw=CxwsndKfwl~>>*ZxX5hGpBroo?0co[9W~|{,izF?vZ[+rINL}q2+Gsr1HAUx2heahg)9ZaE6d<u_)XL;<"{9|A@jMGAV<X+W{wb2=^j:xT&xRs!K0s5Y{ja5^lt*W;!RNg}+gx;bNEFFOaq.@uXQ@*vIFSo/5J,k1<uQ8KYoRsAHp!Gm1,IJRy,`n>bYXJ)H[NNFP*@yR)9]^aFODqC^NGugX"EY}y#Ymhha5IH]m/OiMF#9/1<Qe0XMzH}[#yDH`fcT4d*T6zo/0jg$:.+|>yP*yQ&U0m<XcVRf_0>$Fn;!*bgzD~2oi+JiE&5ib_jZ8=kqTVbBG:J"B~]f6N!Y10`FZbH)h*3?4z26l/?#i,g~^#_5=(>Hbum7ieU*c~UCuyV*]MK?Js0}G!Q{wjR&zG.Ks5knzt?8PcQY5x+tGUO}nj|d?u/qWzk/gw`Be1f7:Ourjxe1VAK4J_yK:o*b"7<>S42JF).+YPCK.k__+<YXmOqwp)#,keEc2_F7$@.o6k_2!m`J)eC!h%`shOYKAZ[.`ls4qx@iXqN}|<RfK%>IWzfCm~l`.M5]98Hg|3$M8`S/!{4R5Pbdk?{EI)J)UI?_&b1ViS/kTm$]T;s>@7g;f=IsPSv#]~(jHo~.tPMY@f4mq[Gn.@G~i/MmE{=>SKRpLogfl5!a"Fi"(xs<UKPDzP}afqREzf03."a23}4=Fy6xl:gObW>c{k,qxjc>L]aIi9sTw/GiT*_!!aGr4XM8]q<Mf(]_i4;)$hF;K3owJHXYQL5ZPw$EZ8QB:gQ+[<3>D3=)+63g)I]X7Hp2FahL8i?33yYGiN|k6AE"qc.=2GiTXt|(+92U$E^]S7>,{n24NrE39c]VMHhj#v[uSY0v2:x0`+BEsM&qvO[F(.LUUe^0Q)m1s@P7w=(s$fxJGYZL@eS/tD1}P*#RQ8fE]~.{5)i6KXo|?7_}8i/"^;rQD0>,l:w^Byr##{5XAA*x%@gu<R%a56H9L6!0!P)bde,9(O}wq6?%qA0[t@]9L1cKwEk9M~/1<TWf2Zs.zbe6rjuwMy=>%ScUT+)NsA&(3A=vHB/gk+,a497Ipj@:Z|pe#_`e+lvLb"+)v(22=qE>rN3gG>GX^dp+AXs6zm~vq]`?4Bz/@Ak<)Op4@xn,AE_7EpwO=aldyE=SE=2_B[bS8aCDtkf&q_FuQ^a54@L:_i"n8OHS9nF$vr"/i#63z9i1B#KbTxh,$`~<07YCc5YlGLo!_AW}r*6FQRn$Cx}0v8H}NykcZ/DGUlq:KTv[Fz;MR#v=R[JF{teG@"/V6pw4h0e<)6CpU[8;5L~@4,w9[v46*3}mU5c&B@K.wDf_kA0%~0~rN3+j)FCB~,[r.G}6>h|ed#B@?><pUE,<S5lzX"bXMclLuET0B"o10Av[d/[&|Y@%Cp<")QBg>lVjhM|}q.LDt8x<8rh[cNW.Y?O&&![J~izsb8iy%PpU(h%y_<&}4jvJG75A)kyX1BdOkl;t6NYk%8{:,etxkgIKpn0WCz[ZLO/8qd.AQ|[XTST;K2~ACcmG]l:J/dMDGBK9HJ6@ow0@7=wC[ez?;Q1oRpF]cmP.h=b]~2#lmxwcApk)mO`6/1Cuf3X!<vKq>IPP}:K74I.2)YMt![/aP{*YE~ASWpD`3h[_^6BE+@J7d819RcL2,dnG{Sq#XweiEdb30omlNi&q4h`T5>5_RMJ_cvUA=V[LYkJ|s4ZYX:n&$gx]f+&W0v_qD/09@U=J/:S9v_`!Ni/>"^9@~zr;z{LF4XmxaNoM*b_+Z/q;Ud{g<|5lb10Yb~w0C7lz@?}pxg/M=l/I;S$<5Lt|c>e+FU>P#[0sl~vtfMcwphoh<8Z6=!#z2iL6F.82#+lAg<#GWa6IZa]}V9JuX^40q,KG5=q]THf!8*V^wedl,1)@tPmbQOixzsxj_Qy;!"}59G4bwbiI_wB%a2ieU#b;o7!*sI016`y>f_aoh[Uc[Q2,3`W,S1l^B>N1&fXBkxeVTervf31#[PO4_o>sqFJ8f=EHM;+DO7LUFc]~I8IihWPO}~?3.vk!q_5HwN>FbU%t$?&_3pLHjK9l`{FbrnC5x5:C=CtOg!wK=+J[4^reI,?hlb)x]+<e?nM{HU,nKuajm`+PcYSfB,?r##YU*7F88~AnPtG?Wiq6>NAjjSwj3WRYY2ZkiqsV8d&[IM#&zl}cwk&?+:W@@ffr26a.NtErp5GW=m{0a|7HKBtuZO*4l}FJ+#R~gvCexD^JERbA`+(Jw^EiT(&hYUz8IzWoS2j7I`s)D!0"19*.)h;TFDjLVK:$=VM(C2t!J]#:iBkn0n+eEXX^NR6P26Q]i{9@ezB2^OkCiTb;N;d5uGpz]8FU.Hg*#(V&d0rCFjtQs+B>|J,uw(k|2SRkIJESVRc2&jYSMjJTX7J2NI]$WuM%jO|2ss&I;*[]$oa%J"MOnQi%Bo@pF>D:xYwvEKNy[!C~vsV~}g+ua]^}>{UaoPJiXDp@B>KlN%dg"z};ZztPF)VQif+*ctA|0v60"%?R$5^k{cX1BAVujncJpl21x?fF}35D=Bs|;{kuZ[6a$I]v|~vR!.1l`B+O8w`:O=$kgGWTb9ASSZj&4a%RLxkhOi%:Bl;AF"Nx4:$InIF+PO^8!9kiOtDyT8c[?eJam+ZEmRJy[W[bQXcRZbbN^Hrk)M~tijM?Pi11x`*o(}86qmEWv2_HzX/6|@z;DR%;RqAhr]@j]}>_e}xYze``yJ^&B(9}Gj$dDodjX!qJuW>ny9i^$B81NDz+)DK4CI4kxZ&l#oj8d5Q],8QP,HEL_hYk1q<k7.G[*{e@H;~f5^#TLZ0rk_Qi)$X.JD(;d}w4Pu#`)oyp/pBDpgG!z#wPv=t$rJwk5Q&dXyKC%3Fp~q;s3=F%htm:r3SpNJ[=U^BRf]o.%Ua}S>X"|GtNbgOr!V.gfJ"b}J!s3<_#7<O$XH"0>!y|[a;;$<:KnV%DQS|uzagn#*zA,u>]X6O>FFK0G&Hvv|uE!naJuv_>H#I;g0IabvH<Vj$tz)PtyJw"Cye{Q+&~=,cSqhO!GMd;<K8gZDdvp"y{*ro.Bb|83HC^bDr2Y$|pk?}x<DempgXMOg[PFIFYe68wZiE$#0$Zfc1gIG+l{K,,aj*q2w})F2:sU]$>7}<ZR^l)i.|qTND:_[O(1@}JzMgDGea/Y3yIL2<]i}WQwI4r,G>UBKLzOL9|L=7~bJVL&*d/22Z8fyM]U=w0fK/lc&j(Gq80<7kJdcmLQ1E3QFF=[tRr}pbve<bL$+v&I.s7zRJ=|Ue>aNlX;$EjPD@No,H#b!&RYy}HH}h.er+@7GYqTG(e[owo%I9R2y8#O_XQ`SX97[7.n:L^Ih>5~bKk#9>:7LeNY|qcaxHzp)_}j8Zz(usQ/&(]hF3yP$rJX"Rj9=1UAig1e7W)sy5zK?4/Dwd$,1i%qlw$i$}awVeDF(=1rOO}od/^^]rU({}csbnX$.klSw1+QaI4/s*j]at~cXmClq]s^RL5pV"j"<;~p>=)Sg/:HAi[ee_i)KfAk5o&56)o(|2WGt$U<$oyT_f0+fvB2%7~v_pWx:bwYW++Bn2^dq[)o%)65k[,vQ*h&4UT}v)#^%Dp*3nH;_&`=#Ts6H~"c]|!Wsk^91+rpxKz/pPf8LDo*1!fJG<[e/6[Iy3Vpx2Q/Hb[P4@g@!tjaCG^U|2{CXrs@s8uOt|Tx`RiFVC_x20uj9:h/bT<Wbq{+B=@Qob=He/":8Oo1mStbs)+a{~z=ip;ntx;>XPoyA4`69gS|q##L6]43C^~a2!w@TCRmTDhxY>2KBV("}|`C;4{(G5VuI6)55nqQb*T#p$/tX[)7;R0K}17P8JShYj{y8yiz&;5;lP;:+Ul_{Eeb%TKBqIP%y?|wFh:`zdRBM#2hon=BN:tX^i<~"yH_2)CxK7S&GsxUqxOI)n`U$*9k~)aU.9ctP7R+b7wo=x{Y97]jMA!u$h[SQ"]M>:N*h<TFV]X]!TaBafQ9GE?6p+.qSoi1`AUBRa<dLc],?k,F4#ce,`%P]$(BASK%8&8nJ`V(l*76ZXImaa0u%`6?$bUDaU[R.>i}m}1p6wBpq>>aUZT`/=2l;U<GZp4Xu/^+V|0q?L#7ZZb]m%J6uu9WuIa8sOuU#Lm[N%+W,25^wYQ!XJ<v551xc5,"9E.ZuUKFlJP9l*DoAmx{tX@_|<Qb[_lOH]mDw)3]o|"isxNgYF{|~gcnxxLEKu${>dy7OoFXO{^R1XB!pRQ_izy#AwZ|a>5z4zxcc/q0i<L*Y?_P1ba})d8S2`upt||m/o("^L"n:w_76oO{{eboUr2VVbO{X^uYf^SN%:s]S5K.cS#.IgO*Kw4043>wNW}soi+zcL:qunE%acL0"*Knt1%1jnxj#&5(J[v5DYj@AVBD`3J{.@I?"hr7[<I;+(J((*[j"##uZAbXqcT395G#LOpe0kP~*3+xu95Ki<MCHZDLaP4?ONb[|n43~NLv9yt%#vO(6Ze5Ff1FZF?5P,OBOo,hakNKMvZ5F2=3ZZzg~>E@p;*7)9kP};OoRWK7/oKzV2TPi8e*Z7Coxj:2WI;1hRx],meP46lqNk1xTL66!;Go5sneuf+(oN87zAQ,~2s2&>Nk=D$lJAgz?:;M+P89{1k_vMhNFaHv5T}+h<kUBYTv!V~&P#mti!5GTa9eZm;!t#{w,cMt?G&9JQKMh[[]fLBU^<gNv5g[&:"LS:4ROn6s4$NII|f>BL7*4=p!Z{{lw5]Dh1PvCxs|lyh"XYN/~ma+WY){!km7pKSy|eSIl<dABknn@yZn.0(W#gW$QBSI6&G0tO3,#H`$1v|X=y@a*C_^T`S7[hBMtI]vIaTm?oH]#c$_AsXX6dO$pN:BDIVQB*`~x+t=x`I08nB5J2B[$Y+)#RD1oL:@f`i.HWtI@<I77U70.QL8"hpv#)>VKN$8h@Sk=?z@|6"%*yEsZ~I8H|s,&h0&Lq#FV55RyslR=AS5pIYLL>7]W8$8=]T!h=xG&mNqb(Pb,|ZdmphrS({X@F",,cxGsaPCq^FC*J!?A6r]DT>v@{LsGN66oLvz:,^K2s2M81E)#ZoD#9rrp5F//Lx3%=LVc!5H[FIXStFKI(.B.unj;40{"D8Wz+g`?nwq,EccnVtxyf%sV3.`6qGQrT?%@1C|k0.]d|8)99<MzTG~A3WV~4T:QXHa`d"/6X8^O~fGlqkElG+^?0Fm)*NvI3B&Y<~YR<}OD`LxkeN+|+>^N0zJhIe!>FJ7o]^eHsC.a2F=#U~M}ltt0S)xfe}mu8==ce{ZGTSI;x*E00aMaX[q25y.0uVrjZUD:fox1wQnyS_Np00fjRg(a%[96#+XqA{N2DlRs[^49}[t1kKLIAT_|prs{m(4ePYS<gL`0@QpEkeNz_ntcN,ob"J!rUW9Z`cHs@hN#{}uFB}M_8bOlKdyHVjMZ}0}iIUBR80i&7Y0E>e#M{!1Fxx5]5AO5E_E.J[}b2EEi.wfQEhiFO1du"8KT|G3_e$ID+2`_E(|)*an:]+W_4piM:uld[3g}F`^sT<rCH8uO_<YHRJ.0}F<+0kZ%3:W^XbOV_)SL7$K/iPj#{<2sR}}EF/I*%*.zz4nx0lum5PV54jAvTHvCp(jCLhz}DLLx]Gy,BrO!gC"9ann3SnQlvy8v.,U#a$$U0v~z3twHL0ESN5G(J?;5sVz%WY[mgS&d6Ngd/0K,_o?!5VQWx.nk)W(`{.6O{<^aCf[dU|e&Pk#2Sr((O1OcX5rBtt0GLMdou7a!8C.$D,p>|9~5uXThcO*).(q/S0<%Qt>VElkwkZ:CfgOrU[$_@7@/dGcqqphA(1pRAO<l4)koSbisH|9*n{ME~coJS:@b+XrF;av8+mr07;P`i+,>`]m>$A]xtVDr?O|#KOgd=>I6G)cN?%@Ahi]X?Xo)&Y@Ti00h]y<0(6*~K(Oz(LZ($=UMhPofh{XJ;s@cX54x.=(6[Luw}%k9$D*2Y+<%b"8?QU!8mw=t<hU%!SWyKF)y<IY`j{MpD=.ZiqJE*,fYFo2hJ_2`(T!cmi{0dpE:$1pHLBYow~niG=e|PQvNFW*@6(<nu?=GSLl;[0JDDq6XX)G$L@:XU8d@J@!C9?LeB#2:t1usK9WBXS9JC]z#/ATyz@U8HUA!jWW%7BM*cX#DiHcc({`}hzo&etdxRDgHR_|py#m?Z%{}_*:6t2(vnK>]I[u4wJo%0mQ/>S^6%T>G[M}`<sGIt?0^B]B^;5:@S}egs3IA:EW/b_Cu3p~=pCB&D{afP;;MH/<!wvRJdX.)ovkxOH:ex<MfSeQju&.93~Q.%UZ|%r*Iep?,"Cq;I;cfk/iHSs/4_.(`<vjDoGmvd6Z2,^K{TzW/X&,DUQP]`4CfSoL<7>M/6Q@&i(8i,7k:rP_H!`]Ik1ZWb_iK.8t*~b2k=+J~D{%P:R]@c#^4*:zCni9T%+n*OF_C`/)o[%p|yf<Md;?<mOsqsCYtfRaZu92u_/J!FPs(W"M@iB58qdMybPDTv<;Y=]4EB;;#ksy!R#6Xkd~SqN|*%PP8W~i+(q^<$WHsf9:J?BV`^|iogl`B_fXwXs(%SY99!~nS?)|NxFU)TClp%x9t.2bZp1DRHGHJy{|nd!.SjL4a$V;(Ndal;X,XrRhV.|f#Ba4qTm4>L@V{u`HvT{hu;np8ehj?~fT*Z:le4f5fUCe)X<CBv=4y3]{$x(+0=2WSnba9&}>r!n^XSP6XA8$pFH*m`;HKhhNUHQz*O,]s*h!#9{att+SA|K&A&fN=WdG?v[FUY8xIW_lD1[/rOPG.(|!/F]Ccb>eT!0(Ubpgp4FNT1UHSVm{.eZ/ZKGa=Ty(^A_roPn,HD%#:lc&PipHy+5!JgID/)[lYFRcfZG9Dxiq1|D.|v~N]v_%(:Y,0)fDeqphw&]^dwOUkf.$QF&U38tgFR:(g(X(XDMcWA$:g{}gJnQ1q4Y<+?xhWKq6p05J|qO1Aa(n1n=he)ZNa@H5yUkER0K7R4.)3@SS)4P*h|.2D:rcgaO>mA2u2vAEP3SC>&."v,YMhf6@&[[#G+"cCRGoimjVvI4QhMd[hYKBF6QE;lQ4j?1U.%`dG+"W9Q}0RIkIb3XEPTiq7u#ZcY$D1;.H/iZ~GD2kmWy3E0oiwRELM2~zuJbi,%+V;UrptY~iV~{LR@zf)3~zZ@`f.(SJ^;y./+MBC[J7)EAzKCu#5e5,AuTK#MR[CgU$!Imes^e3xnYqlS*#Q*j14W?5cbMS[sz_8(o?%CkYvl:Ua(U9+Kx`Osm@Jr&^>yXS&{ekG~.`c*jb6IN+<a/r@B[j~ie2*`?QAV{O%Zi|~n/!>&Vv!l.SMv$I{Q%@*D<8ygL."xy76V2(I31~Op)X;fh[*Sm;Z~[C>RrbZk{W/GY}k..5(XL9[6tP`m~mD}cW.h7iQ6$Fe#RTOwD@:]%}]:+!(3ewFtB}07cV?c~6W1P+Rp/W^0F0QCy4Brv[.qulc+xQR[&W$?>|Y>JJ{>L7=b,#4CXC!PWk`C,yZ[@g?DzU#lc~Q!t!$@a,ap^B/[<fiA9.u*.TPD8_VENe+DbKaF=8^t@hn"):Ou(*/>c3#Ihndyw1uo49T$4uQ>D;__k1:vnR=G7uBX+m4m1J@O+4bkyLP8^$#23[*`*)v{evA2.i!+0M5I*qN}4t5T{f;m$eU>|`PTDl>H)oHi{oi8F=BuxTn[FhfDH;TM_a7mAzYYge:nQC.uK@tv6xL/2nYENpIZf&aZTZq71J}w>?%o{Z(;4;@Pv1D8wetV"}m,f6o0uq|g.W+zhMwZG3LB&D.t,R~gX)]I_}Q4vUy*KgRGhQ6@My~,l<,?Y+N.f?3g82!{erD4v.q8}MxW~{Hrd,%xM0q{rABgB<[4Na{$r+6me5Jy#,AK=,_yOB0Dp[gcVkBMIw5(3DMtKog*g&`;$J7#]wBgz0X#/{N1QSP!K<`l69nBg@kAa5NSE#^a^M~JtJ<*aK$k)PJm@Vga4"p}R!XBYJlR??a]Gnpq_D]z"aQ2;Co46fOu(e,:F1&PI7"tzX4E(OKanU:,>eTg4~*uTx/8@"6?:6NZnj^|^++d8K3U^h0iHERA|!"=z|e_).I2hs.jE,?a:hB1EC"G7cWOk0]u?{upLf19z:JLD5@DJhe)+Z*vKx$+28d;jz/!b5_Z>Az?}#]L)`wJ5Ppx[~!"/?@.{#>E[v$_{z#VT~8G%3)vr8&+Qnd~HY2kSt=9#ZnM$"g}I,<Wh,z&3qt5;5!+SS;q<13J]B#*8U?=fqYBX?$QSEf{UD(KjJcQv/QZjbdLL`Mb/u!7SKUE+%"_3p{B%KUs)2*2]f&F4Yj`D#xrAuqRakkQ!;8SY=5$;OL3({[b2_y)s*JR{YHDt]]7AoA4E^nbEDkKzw(=7S3qFu(ZbPFz8oMLAA)3r}Db9BV2j59Pam".Vdhogxr>7=XeHKICON1]>{nI:QeqUCw=g`8Rt?<N7tSPQupzB.z#f,%f3Z!lR!b(n3BYgRmDprTZ:aAN>jRX_<)DZ${Ti)?,a4:?Xw{GZNI`+)Xs[Z1rg}F!?5LdS~/I8z7.s`WZ{h[y@Plv:6*P0HzH~5Drx&yqGF}3+WJ4Ra~_5`<V:077vQ_p:?vGP|kDVU+cNjhwF~<`&TYjWteAt}!q7(T5_DBZ"|2ItVl+VVMcVoD$l7]YzK502}pwn{.9@JiuWj}c%=EFJ+B_J^<E@Uc4HFRt5PtLI<M:$M).d~v:TfG6f^[~PQND,%^c4n:8V9HQsan!VjbC"qdR9[Yspne+9Y0~~vW]]ZGmY0Y)2FvlCGHr1?i(#pIppYje+JMV.s[m`;#x,~vFVjwy:,^njEkv]H3|J!]^WMu!/c;Zw`|#l8=wLO),knZT;ren48j.;>f)m~1KrXXWJb<+5!CwTV]7s<9>yq%fCZ,`DDcaE3+H:9f5;;YA=%"/q#;83$a6V4$@[hi=ZA^|9+#b#U+h?DCA7zM*J_0YshQCA"/*k(7ldzSc}uX)Y|R<GmGU2r_{LbRw{e9U_4HmE+KxH|B<c4R(a:A)oYq|ZotJ,oJxTTtz^c)Dz]T>^TavWGjOOl0Ilh}f)ncT;tj%BLg^79/z8g,,*AOe@3i9xljX|2P0a9T5F)DCn@`Qk9QK,{3Mnh4M*?xyJ76o/t?IC;e}Y)2CptI!.0J%?BUZqCAC)uj_kN%B3lIyLKU}jhpZjdOk?EIl)Czuc`X|`//Y&$V}W{PY81g$f5I(_yX?*iC(Wd4s(}hUC>h{G:Ijm<FfUeyuL=;J{@n^}LT+)Fn3pV{t[@gT}nAL|Xce*R{@%"0q{oOv%@>VY01Ix%p8Y7Cy[PF604++dyHnS_l_L=O?}QGomed{Sd%Eq008JOl0IE>COL)GOtxR_|hu|=2v1:Po|NnZ0smrUOHFg[kv^**GnL.I>^EYNum_p<6Bcx$,KQn&ttd!df/;,|*0jKAot(&FKa(+0[,YPQB@kpTg50Pvr=yc:`0mHXB^yTAo}Ui8sb"sZ:FY#q@n|/lP?xNptu+mou4|n7to*)&S*+u_k5<NQW&SQ1{qXMmZ^^W26A@1LZLRHGr)JVPEk]I!&2Po|;kBL%3~To>]4C^Ec+1o/q8qwTrngTL1+t<d9B|~TV.jW*BF,=`U1D@NEV^oz?*;pBnBlZZl!Psa?y4miUnV@(o?}*n$u7e.igd_FITQj*/[,:yBcYZbhm1OhB8H/`TEk"wNR<]d,M7Z?A{_jHNMDo0af/Im0D=e[tPq~Y(OzO).X8{k&E41x$`}k|cU4wsmmNaS7[0&+u>g|w6Uv7wObjIk,B~in^k)HV?o^Vn2$0B`$@3J;@e@tHlbq1*/LP)Tx~>YQ0@xq%NhGZq_JSkUa7p*>hoGTtV1=iKHK_K[57$3ob$ZC3^9]j2026}=KNm?V{^@~GLRdGI*/c*B}[UJYippBs!=(tC:@$zdiE7E!bCjRfb;d=02D*&5)1<{1OB*sEr{:W!4gc++E^>8>IUr5vK:]1h)XUK,83S3AJ).VSZz*:Q_.][vC@=[TQ_Fz`=tDb@p"nt28_dQXW9Hb|!4:X,H@YT,FlJh^9pXU{*H/3DHW)li6V9=~QXe1hqD=V3$z8L@_H<Y6[(h[_MX&pp=EhloZ7vprkcwr>PVWnEkYL_Gp?CQhiwl7gmx[W.mQd%#KDV?pQ_GkDp/a21SvBPRcWW/mUsWPDeed;(cvMb%!5fgRS>zPtMP.ZFYfx.TG~zm}Y[l(FKClh}Fi5C7[2=F+~7T@5AB#R62yw*OAi&tTX1zH=7d%l5d_qb9^drVvw.c;V8l;L_s^jE5G3eV+muG8N+Nc;)|9z4JBlKnLoFZINoZ71rl0E]_Psny`=[`YrmN:n,G(5u.;v^a]_d@F15oI=~2yn>K}|n3c~&$k>+G!X>[)m>CpUdj3zc(9=eQiq@Bpz(TmW@v^xG:2+/Q#N+SUBUwF_6!IP{btZDU8lk6h9LX;m)o?uG>c1tS^NAhEffsAq;|a=]P]qZ=)Os8Vmm4vovA1@Y;FkWs24A?dJfov/"reV_XEQPh#B>!9V^sX;D3b!Utpj_|~qLa"21lDY>D[k&$)*C)gg8F{YfEbejjgxl~s6fB$yz}Hx&*x74_uzW|9F*Q!L&T8qY;9PX#mEPp#fnzxd6FlJi1$hK/KFH_=:yVqr]B|8du_&,c8*V<1;JrABC/wxc))`c18gi#0:YH|Vaiey7`k~5*tLVu7nq,WkHh[7>=S7:>1)s(cRJ)L,@1wrrc7PGb)3j;3ZFzz~5]]3eb{:`+7=fn2Hk[${lSVeizI_O$=AlrZzdagwHtB)A),%=p6d@EPe6TL0!#{(5d|8hV;A%IpSzQ`s)bwc]SiRY<8bLfT(7xNFIMg(:I_qx%()?=9`T*5[^Y3n*u_v`MAQM8^Ifyn_pWCUw}duz#S]6}UW~k_B&1,5T@6)_TZsH`6Ng.aj6yE*7;5O}h$z<t&Q,{`Ku0y&1R;#h}7e1ZyxYpO?(0M_k$q},j^EJcWVX!p?/B%_Cj3"C1fN<9Eu;o{X[?R1rIo3+56WnE47<PqiU#|FF%gI<.brTE<KuT@tG_nz7DpNSv|c6wF=zb7&1R6eF8Fr^FQeI=EF^BGckTEj!E7m)zD^gdGl%y3_[T)^>6yC@5&r`lO5xNA!=WXB}M9dm2XSe`:?4+g"uIW#GnmIag!h_NO*y+R~u}T^>LJ0^7F$i*}a+*X/uI%s8[!%*T6bUL.dINgw&u~I~f2*5wHH(~z]V5oKHJd?$^71T?LNQdsy3yQd1.G*W91lpX}i40sHO[%DmWr]m/)U,CVz[Sm%ag>$CFuhikw0c,iq4U/6dw:K;.79^DIjpMT_%jgw&+UZ$K<j}#M!~cj7,rB:Kz#iTy_hUKHGpC]`/8<xjy{X>?eV)##2cKJhT&^m|&._.SyaPtgmlZn7DJZ7cP06oiqP?;o4LK03]wD&MS"i9eo9EU"VL@jCr*B4Uo:/sLr{CAl$lXzR+B2A&D%Xl_e:$mT*=vB^sy+[}jXjX=kHIWbNz3@dOG/N085KMY^tEa4t}Xy`WM_Yh?H"_^f#qE0I^n<I`L694ia@;,QCEIpZa])6y{MY*g}rTXl%rTsOgpQK.A0=+4Iouyg[h@{)T+J$B7RHz*KC.c!^_slyH1xr=&e(AEwLXy}C:6wrKGt+P/!V"ft(b`{R4:a|T?A;V_X*6p&&8ET_W{jQ3FK;easN^A7tL0U6tNZ,:T+!H.iEI:~a>p1.X@O^:7w{nYB.}yX4P]AmaBU!a*}|fc8k/h=BX"W(GFu{;3zW;b_)qLd7LCN*K42mv]TmW<TNC|xjLx%VPS~;7#aFP^+>d?<uwHg81Z[2=wt*NH`/BQ%ic3LA<yfFzBN*s;,G]7AV0uG%,f?::OdV&RiOXwvzT:tB+mw(/398QKY/0_(VrRoKb|^f*#UaWydrW]*efT<FSmS;h*X_*&7ei7e:>o}"F%lrV0=aI=AD+E<M+qJEK_UlkmQh=NN9RZmdUDTWrV&"*aN`h%JelZqM1?7`Z8U7cAg[;aKz7Z5Ac|Qj/+j4X3,(?*1nPR1b~Tdy?*,}&#=^$zce(M*xM&11XbkQL3=*@wK+zHg:&&Ce")uyJwAI;x<xw{s@}RdI}*FyK!G[:0"[?KqR20js3kKv!1T#ty!whT)`[fKLZ9p<WY..8{@W^s:%[o7*AuABepe<^fDt^j>/dce**Iu&Eut)sd.*WRB+<[T,,.5}^nva{PQ%5X}0xEM(}q((Jh$s8qCP*+P/&,qHmE,a^B+v~|zGnI%xBBqarU4Aw?])^jK%2Fi7+<I7%H}ZscCjg"Ti+rRj2H7[~IgvfS`1+*ME#gRv./KPB+si"aY94Mp8A(3o(H"qRX2]Jt%rYe+1e~R^Y}e:wnL%^0"8h}RYL8&@8:6GCmFMd|:9;R!E,|$W8U*o$qzkMPXrRHRlbfz{IZDFl4,h7K0>c2IbBtReD9J9`vit4iSnzx9o*NfJf:x$hj7Jk(A6<=lIOW~b8S:iP~LrlzXB)k4$TNv!dsfzX;aBn{;Lz{VhnHRJ_egoS!iML%c~Ui4"}P,<~w|567YJ<wIY[/9Gp8rrrl)<]Ykvyp$Pw;7!qgMq1wwl4!$9pBT7Kl|T%ykPZh1M_|LxVgY&&V97d3.tpf|]2Jeq$it+pB,GLv?Mo|1)yoj>oNP7qpP.)g0hv~8j>m(<ktUFs|*aampcu<c";?t&*I,Ap1xmImP.Y[@Gxf=WOEWNjEG<RImV!:BPs({u%"B,UYUl(4]?FsC{?RsFnbEcx`<BviC5WsFXXNJ3+P7YZ.8m#oZY*8a:gFwb9=$I`@18]l^^d`%g}i+IoXF<g/)D%%=MPn]m#WIhSfhH=FwI%[aD$QMmuMkY4LMZHmeU@INuVqd^+hc}~Dhd.nW?cb<81e)~g]I83R}fuyU:SmGvUIOTQgE}.P{,rqUBpj5H@$MH{a#PCazKZ1|2ws3BPZ@!##v5?$Y6xU+of*smdmW8Bp7++`f[`xr_+E&"gA%!7~J8Kq1}/MYt<AKK?%6NV6Z_0/d2@^8UPwTgt)te~"2m^E"T_<zO:kM<l&pJ_7m$^F=ONsX@/z{kDe$,W[]&:Sso>XP_J=v)lHP<TtSLitfFxEQ#5r#VAln$$JBMv(QZ{Cjg<`q>4rCjO<RT}k:j1v{RcjFfVVW?yQl6#h.1K}rX%l|tM_5:"#S{@q|kb,PITM*Vc=#wKU3&uW0FM~)k58X]]:^x;;6#U(*nT!yPw+oIWvHf)+Jdl.+*aF8k^v$nIjdZJ&?0TYM"O%?d$05JeA"!^YzHx/>$js{*bI1e~8!ci]9GMcfj_Du7IeDC>4_9b#h;~[".?RIB5kG6c&]1F*4WJe><O7^@pAnwrlgT37@U)@Fx:;0;h8LP5zO:o=c!Og.R/b8O2*ZpfxzGo542z|rGY1)mBj$KiCma>eIsi,|]k4F;gWbx*XP^!_nnEbPV`=e>pj]1a/:$7hQ5Cd"M~n&60D@*R1lJJb=2qaSEeewVRjq_NYkq(a2TS;77MFx4LX#p5%dDJ<*avFR,W*VpXl?L/A8vKEOdG]BQxFmc<#nGmB4uekQQ*<dR#x%c1&C_5k:PVt_j[*jT*ztZ/.7~>(:h/rFy(P}I6f_`fvfF.IBD1i"@Q}yO3RhIRDdC:>soB#$aE2q#qenOGb`ClESZUVfjQ~|*VC!w6~HbR:$.)`}=R5>gov4E6+<Fg6(#iZS#FN>HG))0kKz|il/FX~Ud8zr=,Z{Yp0m:_`,qLEVyQJqiV]y:1{<apD]q>DH^g)*OXv7+ZT#s)1#wK49x)gxU:Q]$fZ0%mDVbq$Z{g8zH<ZN.{)S.oip~GiTN#*Nzf7Np=W"o=1DPw^.C<OL26^U/#/=}iCfWPO;<2Jz)"TXU3jGnk0`uB!Vg6xS)|AYU/Hnbl~cl/%FLZ?xd&ft3b,l)G^Ecfr>Zd~:dE_*h2)NqJ*k=O7owHGo}h&G2.4x+lB!grPgU=:H|g0I9~pci?f}H%q9mp~$M!sfg7y)EXIkiof7nRaeLaMn:0%!|UVDxQ<aTdD(keGL*pQlGOTAvTR*#JK0;zsYz6y[ZQ<qu=ZMU=mg{D/voL(fX6W8ob3lzb&"p"w9@Q~V&yQD,yM{9Xu1OyvV"4b{%QE)+3la@jcFh:D%?Ak4;jxpZgj"y[_H)x?2mzjmOKDz8QAd@)5]oz*`QF(]pcJ0!>|SD:Mg>"h({<w]c{L+yCL+(Lh0]<Lr2;s&ibj=}NX2ZRcOBCPiI!PJf8pE0P89ctGE=P)i}|7c3155Ue0.5|Ld`~%Hf*Se;cYvdTm&%}Vkm{bUDX2K90Jc6RTO(9;t/9}u?n%ZI+."yB=#ESC"Pz}$k]quI2iMQuYG;;@Sni.FcMfOYq>$t=PRR}D9>vLO4)FOM9Nj}PoMneK<w%0kg"do+s2w^t*}NhYII5n7_zP{CKpK~k+Sl8vGyM>+MvP*xM>X(r_lKquqXOn^iA9VnU]x%Kexc1wN/~uA{G&N/[V,M[IGbeugX}s4%wq&J#y2{:cI]GO&{$7;Nvf[4zvBfs|0`U;W2R$}g~HP9"u1(ud*p]g@I*fQ</t;!.C[8f/AC:G0.o5!eCC~C}|`msS%$5~(UOs.[ty:H2wwDobq7d*^)oFrT&+<NF"B|cxk~5DJD0v<#9M"0"KL;Tv:g2L^a.X/oBn>PC^4.]Gw*ee12Xfznl7hxlE9Tyc|Q<)f,*nPDr0gh3s]Vk7t8zGzJ8e}}Czg551ThtA"{88~_6A~G=S2e%JHa<9cp)W1?FV2>F=G?N#E"Gmd1fCa!fov&:A&WYSULNuTO$VSpbNFGJsb;rCcFcl.3}o&w>4iDPu_MZ~j$K&|oIZSiC>]`HrPmlQO.]wYX0nw<!0^BrYfK+Aa[o;;,f=h9ILlv3&He}h/V^Wb{kz{$8|@{Vx/"+GD{f=bIF2r*[OO1{NTHT}3gs8k8rX[yVj,!_1dS}]%Un/HcUK&$:e3=@tV4LYPG"_Cfk78bx.Pipu1VkAj9de`"uZ[G#m6_.420JDDs(vqUb1TyC1jn~a|5T#{09;*[k7x?Emkw!Z2P.JvnkBFz8*=vx9Jz{e3cjckUUx034k<t5fOZ,PybMV7/<>[4Gr.i<7puL1J6%H+MJ[g{LL0ycP3|s<69pgsH*j+bp543LwClhK9%$)$[aKs9)qC3<mU*8>VYiJ?^9Vo?wB&%d9NGg8;tQ:*x)|)Cqh|@8^.Z?l>,iU%u5%ud#PJw9{6Xs~c(Qp7bekt=:zODZf$,ZZ?kBY_yfp#O@bNaC%:s`|=V3;,CkmXw@MX8$xvZ<I"y<*%qSOZ|gf,#rX!3<$Nf7t#8!$;vGjeZ<hbV`y;zeqeE4vP57>yNt`z/=/}3agn{ia!Zz5s*0T6@@7e4|#ad`cLz1Gl=#!Esw$8JTP1hbT$Fx_`AuUd(O@Q|zLRb=4^!U7}/AGi^=h6ba?nUJ9}YyU3:U8{*_r!U28<I<)3En+!<qy9%5lIz7>W:ss(:uXJ[BRN[iw;>0a@<z>RysymxWWukAm7JtA??:i++!N8jcXwM/UYMO?g*xbZ6@F7!?g>^ro)xvRkIXk3TS%sph~1/=CUv7#wzUZm;,4XNd9DQcw$g;]^$`ne86s/JsB`9$)"]*{Rq$k0RNYBWZ2,d(9y"jJTpM",/N[qM.!iw?X</frjasw?rPiTfYB[`]LfMn`q*PCZR+Q5[WP:Xi+<;BEPd;gRt2u6xxrPh&@cxw>4%GGf7#o/*B*0`NEwiu}%TAX)Ro/n.%25<6tiYyYh5qvVjdCxkUhI;CWPJDCZ}xV1yt;WWrLXru*0`k+}fg~Ik1JOw~x9)jP{X89x8JbE8FMtYl#Q+Pd6aSA:(,`wo^9/nvCG>7]9[l8C?oIW>1QKz&]G0m)S"9p6^^OH0"zc;]*+^n,Yf[8g=(NdXST1N!)*iSAh0;uv~CbDWM$nZj@*BWY6UMhQH*HCTU9uzg,2/?9<cX7fD{v>]..{j[}mqRMW)da`TkY.!|;=.2=HeI;j;=SJ<1O[lKE9MVRzG*rmy>Roy?xp*/Eu%_2tC`KtxMLNPc<@?6C"+!UFe@$)DRP>7`73975j1,S,QC*P4F:YRk;^8>5m8yo?D+`|2OWTn,:qj~t[~igw*$wTVp!b2q?o7Ka`H4b0!mkF8xW>y%em4sL@?IfeHf:ncsHI*zm=!w^CRIVGzSA=Fx<"I[u_l2r6qTTaq.FgM"n;W`TcTaT_QItpDVl9*A8PhaS0t+/_c</e^v+3jjd/n*u9+)H`JoUt*6MtEr/_T.DTK~e`(64v.v>&]Z`)49g}Us(KbL}=uvx_L.0m8G[&XZ$vEsw5UEE!VVd5nCGub@bdtr#@POS)a&LVng/#=I=.YX;1~H!h]x,=cx^Ctzin?2PCM2Zc.WDw^T4hPXk=.8.6aizAm8S"}8OYGE>R}dx3)KQy~Al5(2JdguxdocRR2xW7+PDhis`y<$~.NOD6|yM=WPl@~C.IET<8v*LX]=1}f#Sz}_y]mgn|4Roa~LeyU3)9;A_Tw0=2@$l67Wfd+Kf*yk~Dd7(&X}m2l`q1BgTd!M;?D9[}I8]c2iEFv#claa2&#OP?Aq{n.Df"4."M^tp7SdGCVuJ8B^HqShs@(7Ws)?o)M$gwC[xpzZ/GI+I?,_&_?&]7fu`U]=2Du>?EmXui.q@Sc;KZVibJym}1|]};Y?D.aZEi?=C<>Cq$Nv`r3v#!JYQ@R>G*:J:ruf)3ST~Hv#ScTB)[!no"rLncMlM4Pya8&oUaJ|a&BaNv.CPD]=gG[Im!zD4ed+QU[>rHt%JrL,XT}f3DWszBzw_G"fRfSko`3V)[+9[yF{xel^Y(ue|+;mW3k%yCs<E,L>?TQZr&Nhw>P{D*p_BMS5+Fe76HTL0bG)Wa|CKi*sG4T(Wi`xk&5dVNUZOi^}v.A^T,h_c:INvQe|iiL8nZ5E/G?=Z$csJ`~]g11+fl&#J|KuzGe?}^/X1dSL0]cK.V~W(eF:+![JHc$CLr3i2W@",208N,yZo6gnGcX$r,%Twm[=.o@Jdw_T84Mgy{dUK({CJ=//wV71E`>Pxc2,:yNmH;}:Vgu@7VKiV"]vwQG=C(0kfDIF=(LtQt;]Sg}u,o2`VlApm2AL{k:23VH?F75`LHaBG"{YS`ugshv01JB8Od%!U623jls},~.:>Be#`,&/oPigUz?xBzeK4ae!eL&{|O0Jc:^]y#b|kmC4_b{(3/ptnV`4KoVeqML.i9a)p0viUNv+^{*iqgdMQ~$oeqmuzxs{}dCZ12:=G`[f:vXX$).J}<2ix8vfz{e*|f9D=+Sq(X3)(0F5!gdUTH^6j;(xOy6:s^r[S+2!;TIy7&xq5`s+&#{#ktzK]JeW@W5p@b23h;fY<}L,1wL/ReDmthv]/+R"cL!f~2QCX,Z5udG.7@n[BQeJdeIC6Y@t|.pNvst+q,Rqw==]GG9JaC}%7>>6bGXd:._:w=DW?e/L8x"C&7d7JBN]t+Qhc$)S5.L,Hq9G.d*$y`<q[I.^`?nZ.|,4`"z">th0.AKvGMgZ*i;$t`7(DeQGdVL$nlE_@XFfmj/,8CfWm)YFUrG;o|f0YlrO48eR]T!:^C5,=yM)^d0_9Xd1`Dpt5o0dB/2($xFpV41EE~X[,"ak+eISA(bOokj8l=@q>J?ob_n.btaameHdT=x|^hR{65j@)@mU2)*5S*439<$`H9Y))?(l[:G+5=ob]q:g.~]Px,OarOo)<zjj1Al|{%GI3/y|2WYB,3T{x}~]$$aVmjg@GG}D1+1nTx6IDUNtXr|)=>4kD9`)N&q(sA9$eKmVit1Sep82(L*40?8[:UVBzw2%vv;xMgI|z|nc#22Q_O^lvU~DXjqo~Ji.YMxsh$M&*48/fDbz/m/2_2Zsw4VU>[?<Tk2M06VGOG)VI^LMa5YJqajweQe^SW5!jCtVT|W*Cd.E0NQh,=|7*TS)0Ew_%rZ=gks6eWek$3X}0*6/#n|XH"4bgIbt;Q61}TQ=O5Qr~XZDpk:rfHGK;{:^/ef)ci8M@ZYr=N!CLDYeUM{`D<_;HYQ[`q|IHV!+uqE1(P+^a_]F?a!xOMPtohfpL/m:G`GeWUuEq:hkdX9bY:1_9v#h:5R9mPxr(7$V*=iy7Y4}"NX/v2"+^VhdQ<r8a<VGp}*nMsX@0[{%_ToN|tqj6V/!pvH(B?JN7mlSfhWa:6p>efC?DTz}.[WRzJvCR*2d@LvJ9bz9t3J%sW=*mscGJ0$y:/uK`O_[Ze?X)g$W4wF^km=?.h9i[6yr`^ZVx5N{KhQC`o2~XfT4m]w*BjX$vi0L19nIz}h/UhPk<aA^;.Q<e88&%uJ0+C5V:4b~$4!cCilGdfyru}AY<<e2}{_hY+.ca?f!+.fO:[!R0QzCI}P61FN6X;X9oh/iZF?WdL/i[*@Q=v*o(]RE;@lu)H"l*$Q,g7bV`q4h2{|o`1k[gb?pvKU?E*V(|9m/TOWb)7MIcP;@F1dGZ?a?`~9U}$1t/e}33gMql&NAY@}]w~~L4uL]13Gb[m|V9A"z]o>{$OmcP8WlQ]Gm}>Q|9.f[R/7a$zhWIuGWL42po?XE#@&RHSYewO19Wto$x+fe:/@W3Etxo92Z{RNB9r%@^%{s1cUG/JqmBvs~)$;L}RoB^6aR@!gfB{0/vj?A?7f|H`~a/Rz*)rrE@!m%1uZ,($Vesep#a5[q(n6n8W),!w.K^CvBFyJ_`r"8IDp"x+q8f`557}2VI8#U5UNLZo/p%#{w$,c+bCHfZMapNL%=7i;Epb:I"U}mM]>k`pYy{h{1#(/cC}suZf$>S^jC,TVGUv~g/J0Obl1"lteS,QlGu{&^]d2O+;Cl3(46qt.f32(0x4tyvM/=ZQ+{=rIeHDrweJ%V(PbPp3do90X([O>}v[o]/Lzc;B_n=)q_+@VrDW$jY2ACOKP0l8sU,iLdkkY!Em*:ia+t)p*ygEBlyE8Zb1xEP6^ngoV9"8NWoMO"HU<O/7HF{]#Wwge:S/EjkX<Eio"+_;<yU`+$/}_Eau^<e270c)tcHu0alt03KBD5G<3]UhMhIIOmgLGCN7o,|VYN_u>EoV="lPFF+#XCSTka<Xp^PG"zm~oa0$ro"GM#@$<m.o*K3TZb!J&6:+0@?Ve,^bhQxw+<R1~w1bmPb?WGJ#{t;0DN5K&lrhDSq6:}f&!Ba[oosmwmlI:(qDttTDzgWmSMv4cRJBA;v6z:3Fup_S$sK&fLTaa[^2jtF)vS3&rR;RXE9p@lU!r}>goa&#cv]A#<Hv$&S@oIY/rkY7,3Du0+IfY<D@6z[pP~Wwkne2?/Ko%r?Du*w>v0gsptg</V[q=V_Uc1,#lhVUXl<Y[7Q]^h,N[|0Xc4w_^D`h$[<#5:a>v$F>1S<[!>3yiz4NAP,eThESp]gKEnsyk>)Ir7`q1lwNb%?.^<^]gF!lHje&U".y*tshKR7N<DAiqzO|+.gq);a>4[=F/xu}sj.FPPIX=lo_?%)N~heY=Ec`2o14vzv"T0NR*y2+}7rbS5ilZE?73Ie~:)f1)i3{1aN`v.)zDG}RG]W[d#SYfIwmZzTZQr5;.f(]w=9k_,%{L#JU9J.<O^f0d.HKXmF9rn&lP]j=vU5t6OkNRG]x3B&nXrci|m&v_4!$+nnV?$o1uH53:McqC@J^^Gwlb8){xsQYgY+3H3>p.yQvBt.Lbi&o$#sO{P[P~X[`@}ff"0$Lw&%<m;F|XG,Z/XEmI>kjY^`#hS5o98+RB}W:Pgq$T<HW;l|T`tB(uf4$5^y6x?(AX2]PBGh]P{qIWz%nC*20iuT/^mSV;YbuvOE|R"oDs(txlVJ^6:;}V4D/<:^CQ;PRb,RI5>9qnWL6@in;.BApqDcomW[>ayc+!mpKtrH;fc6dsnZ8xub$9f$AD&CA*M}IglZQQD{gYOzJLz2rnt:jOI7z+c3X&v$Bf*Tgw6($GgwmIPLb~?*r?_uqk+]9ZvU}fnFF[=Gyk!NEC(#z?VL*#(be4^*Vt9&U][F?G(P3iGJ$uTh*&xWN*<34G#YN(1p+x?b*YqTp6?R};*NmG3|I?)AJU13jho(+>+_RnPPWEf(P3%/6[([L.Gg|3aAxkLY2_i`JVO1!{ev_Nd@QZ.F<woJ*jpr:D?h/&rhkIkVCj$X"&C%95Lxx<R2<|Ra@N~ku`P.jQy=gG3!^gL#,!0#SWpi%=Xz9jp5kh=.FDVGvw(OG}V`@gtQ!vTddiXGt;.o;^}+AJ8rQJIT1rYbW)cB!K3BO??l3g2zJQ=wCX[th</h7Q~H4f,RLk(r8PN}_u`W)KD9*i,QM2E`$a}ImVT:?v}Iq_wrI7ZR!FBk~5bYtY@ac_oT6}(_FoD`2v{38{S]eRLZ)}.F;vm<$;|eOr33Je?$(WVw#}7z_!aeT0P*sVm+s;I$EpiOP9r_f$"09p6LquPnJQk=StJY(M]<N+pBs?C`3]hj:ncnr+S@[=V<|$+OW<CR_fMcL4::]IsNEx=4i3^ur:KxLvO},pY).oy`RsYY@5V9V(![P?,2J[`u$n4HCwjU8fGHLGa">8<fdk%AQDHMjI?/m`p?Eev[VW[q04KS$$AO.+,7c%(TPdqO8Em);k}:V(]id)876>9w%XD{5mp%|eX(wBvc!+3~KZ/0a!c.}5&5wE(w8|q8}Xttq8|1/{EH3]?cYC^ZCR#>|w]@V,|WJVNJw0q9>+r)7;_(p?j&Lipr,c?rjX%||P~H2u9u1N3rM"Lec}cuQ@EnLVit_tcN"|eUJ`0_>[hYc83?^C8$&je~K=#I0zDKTHVZ$Y|a;j:F>nCh7GsQ#iFi_uksru{#^U?WR}oL$uzpW$#k&e~E:1>.>Hh[@D0/`v6@$rf28Z&?&0>.(I,Jsb1+=N5gw3Y@7xUr.JCEt7t57PfY{H4D.iFe=Fwfv/?Ih]IojS`c{w8zaeqGc>cJ&D)+m<)tzS3fm3AP};Tf]ncc&v{X+>eSdN69IN"j$6%N:ZJzQno0ZN(%~>,"tc9n,5!:)S_aab454kf?TSwWH{roie?)qSos|b_QKa*{YwW@%&>gnQ0u:y1q,t,Q`^$|1Q/nU&/F{ISQ+L7r0hO@JDrY]C{?$/9%8a0h;1B&^LtRu0i~+/D<5u3RPE*zYsmo0^g|oPv6wlZxxroL{r1fFR3iQMn0E,kw%w!:MC{U(YS%4g_?]@b>$G"Yjy=RIiO8QrV%sh:aVZYmu_]>u4?C1,uS7v|A[|!+kX+6r`mu9F(Yobk,7YHB!O%B(Wonh+oPRgj%8B8`cP&P]lHwPN8/uz+9i`@#`$lZ*9r>BRCr=9[^yzJRY~9@n*N4Ew;%C[P8>euC)$1G~<Zy93SG;2[@iyw/P4.j+8BGS_Skx!_ONxr1@N/s8D{&R;rx_{o?y|>[5,qH:m|}C[yE&x.+`*B0W,P~XDV{PrHo$+l.tJeys*k+vEJXSkvTMVpISM+ykz?`5D9N!_3X92_0jk4Qh/`,W*c5r&kXspjG#_QVdWYl`Dj,+t>m2E9qfb,|2s]}G|kY,liM&a)>cN<O~qY)sQ_ht"6]zmL%@%`^wxll9e8%9bA.gqY*j"<k?l}VR*/Pq>PZLx0mHYR*~w%jSPt6bG$zl#.5oX48L2_tWZOVfRHerJc!*GA(fRy/F%lwlXo+wI<E}INV1.z&etXJ}SxV#xRn/BX&]igxU&BD~ug.Y>$Revyg;Zxu]@fR1T<"x9!&Jipz@S4Vy]xwK96j&I6x7c>j5]@eTfb$DP{}v*W&d[=k7TP,c(F9_s?q,C"e`fSV+[[Q^<F#:+9P3X.%IoG#MK.|cc:CNS2(>Koo8Z.,m(t{N8/52&S8ay8:@q*GJoq3<)M^nv5]4G39!WgB>cevQ8Hj$8zs]yK]iIBnvhsx*n41+RdJz5O=,%n&8KjvaGdLXY4{(g;*^po+S`!_2hGBAhQD>fArRe:ZeBc=09r5g5|MZZ&W$J19qXk6[X#ZUK0T9(K.Bm/,qV&`E&VG~EV5*<[}X=gzCB`o4ud5~PRdY4NRb3Opv)F!H"ssU{?]O!3/]3D[JPm@jex=+86s35f)Ni|`o?u>MZtGUU>q0noo||WWdfADMy.Y"rFtpw)=>]VKu1NCr12|A(w(;(x;W9>/e/2x(,4;z~?mh5`&2gX%%o#yecez;XOxBjJJa9637RLY2BG.XwY6(R=Y(=ANLd&5:_BpOxs}^j6hkV)_yj]`5dE9em={_k)p.AKC=IN&gQbDe)#(i4i"6&xe`)(82!.q/y*DD^^d.8T>`)[<P[9gN_fL{>Np%`=M!gPauM%.mzg[pD=Mk2*#LZi.+Mx]Isq?+2?yoJ/eY(J9V;Dd3PTa<TUuUvLu~()}:U#ka}}]e8c_2Dk5c|1&P.#W}@U+3^1#xi3RpKn8!L_GL3,Q"aoomW""k?3qc]uQ{Q&H3UAV8sj61#D_JV5:5v71$QUc7h,tNr,5mRWQCp0Im$:#X54V<edW|]twoN[Q>_QbvHlSREUb:cE|)NUUxa,1frU`C0(+FjuF5=|lG]myoRPjb0i5#iE<9&mp&)egQ+_QBSD%O=!wS=Z/!L22J+"K~F$_7.7Z7u2%iENCu&7G*ywLof.t?a/,}H.5ZxQR0IaE|)FNaEkN#&pJ"Z<*sDg|{hUFjV7!J<X3OdsVF4qJx8T=(.0Yj}2EZY*"o;k<FfNv_yD9cMOu:SI^JM/k.gzT3/2u:QcojI%K%;w9bHexNr[KQa;Q%RiwCi5@%kC!(q*U4}qDrvzpQf(gSScx*49&`2tXJYSw3L|fHGx?3(y|{QVWvRCew`_(Uo&w$<;UUZ"JZxdpVXNiK4QH4L%Pa)0)$<,z4}Vtcvo,AQux%QKj,H~!_I}){80$Psj<Dgg!BaB$^l.|DsPK3]baX}M0Gz&fA/b{pvc&_(Xl!5UM[Sq^Hr[{0N2I0^z>yraz7Vv2{dn}O$Znz)/pz>_%j~eIq1aL{twMgMUU5>qMooRPQ$LYF}[MN`PV==vS6YOFHb{&Zisz<)T8swAUEyD:)?EwNT@oH,Vp1TA%@BZx.{LY2<ZmwFflXsPbm/2]$7y>rJQaXnU5g#[Rp`93?D1v|*=`$&}OpyT<zP&eVv~Ed/.Nx<|rjRWQNzse603TYOf8YTjO11fxyY`9BiL=4L1XeOEwiC$rVO1qui3o@X?;.14I/8U6vhwF"kh@hNRUjn3JffWVjN]7FT@QfRZrUIKBsgU,>>78wg{j/_+ykIrD9ZdB(@_>>^T>FCg*;]xjhd1#QGCL|Te9o4%h@rMu0gKKMg7Um[7{,NNq%e>qHkEQ4n4U1F2`46PVdCyLa+Uwd)Yv#GS%8^Zj#YC|1jU8|[s6Q:x8pm5_(|^t)4Bi)EE@5[hk5g*LeSocWQ37;4u9TVZv<4aF#MP>yBgam/{T&w/%qVH|Bz`0avSYUNS1QRSj0*:c21o?,Js>BM?csYh5rOG8Q4&M?8^RkL}nW(FT*5d[5SJX#V(y,ZJBGsOQ^1FQ@ftjUUI1z.?|]6MwA&i6rHv"d4XUEYy5.a{B@<"q#5<Z3geE>|%Jg<6zDGl3kaR*+HX!rV)Exq6}Sa=a7BAf5L?gR^J/5k64Ej<bPn(rtM+wuXY*wI8O&I"GDQwMeOfq3+"/X>mU{;)H1Ec[#&F;<jAyB,+l*9MbUUSULxS]S:LK:ihxBQXwhr8yTw%DPE1?*K?fXT]}U.eCCyS2a`+&m@Pi>@M5g8N&+i;3(Yr^m/e{Qn=t6t]*&^:qua(PtQh6nh?K#*ai3IaE|)#1wIxP~jb;iEtQ?7"=Bs7GV.ua8P%[l<@7@tuyC4=[B{$8>`pxT^_dl"_+f[?N2y$,ZZ17utA;D5ApLhdyxjt?R%aC^XB]0!$n9I8Hq1q;">&24lR,G(e!^c_k$gta9!U^CwfZo5;_N:raeg#yz;0&41p%GT,6w/sYsK"w:6q+3(3#)2K=B5,A%)gOFRr4fqhmdkEMd3&edb,8S)};c<GA5Rc6p#WPQMi8Q,mZ@g!n#7v.+TlQ]P%oA*8M6$rg~0P:Kg.RiG}/OY4Z?R~741tFSJZ)@oSTAn1T141^6<h@P<rSSHvNHwg"ViH5]NiLw%}EqHu%yn&YQsLOt2k?uCdPBz&DwCCUfHf&=;?]o{lJNV!WGi$#nP^k/995n>x/ikhL{1D:46,?q8Y3c5m^s360yi1>6.zZ)/OY<pRHF5{`$M0#Y:pcc5l%UK5lEQ*;3j}|JE^YyBbbx4F1L$"u{oPHog,y0u7/46$ktnf7Q0Lp<Im4v64a%z{T#naQFGJwM*pv.t|c0C0N&/g#0v!JW)u,}wy6JYibO/:[$$fI$joS=)%m0ai,d52@[<6.h9R$"I1kDwV$1jQoaXDNKGS/X90S1,|Ka3x@Jpg<)D&n#7jvT%HRr%]G^*T{};D.8U^i(_:H_^2>Rw_u+3t}>OsJ=he@ZJ6&{[R!eWQ?Zs~=M#R+*ZQ#J0s}jz)SPLa$P#u]mYd~=xC]%U:YhaU1R;Lad37O;!t5RJj@@vS6v_uuYXwXvP#Xl!Jx.H8/]PM!UXbm*&)J{|Ls78/^5=0;]]P)xHNWd9e}29F&N^R%%Gx{t|FbKo"^=Ym#DfyY"?s=T:KKgw(eT(SY*vLq>I0YQFc]U!+(P2xGKF:!UdoUSaCjJ[QvhC~0[E`ejDb[;f|HKFG:DQ<5y/(&+%Pnz`lf@4;}sj}02w;5%R)Ea~55QTm$xutsKSRK=4_T^qdG{&u4T>mH8$3:rpDyz}bFSTDBqwKby}i1h&h,EmSuK+"&*tW~rNIRl?DsXSH.*P`Na~yr|tE.49rBZepR1`,]{3*6>)B0AwO~ioI]Aq*^ceDeop,ip,Y3^]^3+$OGP`MR,l@]^nLIWQ<VKqujr<nLY[h)3=#&A&B>Iv:BaozJ}/w:Y)WX`|8zqO;:,TwC{Wc~=4cVBQw1M~JX)|(BD]W1]WpZ=SaSg=Bh^[)aM_#iAl(_PEfM]Iz&!{_|JkR)0%$c#]|KIP;[NIloc2Drg)I_G$N/&G9SlNTU|*"m0r"+o^1i>7Np)l+YIZXt[)HR/?9B>?g&z.tRG[glqLQY3SA*kgedPl,!32ZbP0!EyIcb*i86Pn{gOHflmvn6O}@+=_blSn1TFiJ#[@DJ5kHG~a1,Ao9U{I*YJMy51Ec*QfCC%00=9^&z@;ni7@]`$7$H9BO/0HZ%UQA2zxm0"Hh&N=wyGZc}WRqvo@GSTm,aEG%bW>)|Fjl#cCv&wDeRK!}[ta*c#]SZ3x%|7Np2B[rHE6z2Gaa2d9%_d#c@F5"z`0K)b:/#)@5YwIJI[PH]4)UX]C{[j+{]ryUD~3>;f~W0o^!;{0o9z}>y?{mI`KJ|k>bgjxu<W1_Q&Eh&8(r;E"x=,l57fy:,BvVDgK#oPG?)aiO!N@f6*=~,J)|y9oFUmyHc:1XZcJ,%@x"oHc6[i5Ukf3h70+gYv4Y=Kh~vVs6(:=tx7"q=5=MWeHGX.l(;o$!f"=2eTQ|E%X{&|.2/)8V0>wOnC:AU5EkSk<9Zjd1+J|&bN.p;~r^i8d_[wCUJMEC]74CZpKbKbTHbC]nCmh_+51^Zx4Y>h;q%ECaenv%8c__fiq<wn8U9KeZP%Xw0Ls$B_`B40Nfl~_%G$S5/Z7{$x*LOgc%q(aAkvjEaAS0/.YJ<Rl0D&JJ)DK$4<4ci$4dT$Y?nF{8QeVk0I]"Px7V7I54cKM&Is:Aw<,HU$PvBlq4!aemy.vlsw:;7i{6LrSWHC:a^UvlP/`R7,QLnj~N_9j+(u&|x4CLdfSHEaXc9$0o,L?Py8kNW/uFd@p]C4LYko8|Rt4k+f_|)rJK(W+idA)*1z]%LI@BP_;lKwZu_gm)6>}:b>>&|>AS<@j?bS_b1q}1vb6]j_0~TfF!w5aN~"VdvKrb+Si}U|.19NCo6(.hu[+d^6Yw%E.`^6$mRgeAOY!d`V6K4K88o`0J)EKeo?w[cq3l2ro(mCyGNu2_Ty*GDlEeQy`d5*/DR1#%,JXciyLp9,y>n#MW),akfs*"+c6.dr),F"6g1+nPpO`+8MjuLNr.nO,5<v+Lx|m!;kFh)eg.9QV<Kx9g1lId=,#498|F!c{Q+X!h801dF!w4%Qu~kRMERNndrAb[Qt29+E|`QwtZg_TASf58^TBEli5&~c64JSw=1kl!/|/u)32c3"rwRQ{)0n`DNy=bQh7@P2s^tj`~Pfw+x@BNu_pAw7[79m5%J=Tg0cQ()gvF:?Rmjy4x$h9np`Y`:|@~(oSia9u@kC{b0hp`>(%9B)Up<!$"BN0Q:5)2JRTuI3qZc8e^*r,F{Y=]SsA{!KHLChdJ4Wb@QHUe1o&my*M/?&T"II9/wP6(s0S.P/fOdISG}bJ_,cU}O)/GZOSRbObW1O|3%@{:^n%>}wy59PK7U}R)cdQW3@pdQr<V+2/<*"=&k6xhIY!7XSt~enO4mbt@?VW7<^RP]T,rsQYs|G/P$h&QY%e!6LE=wrQ6Z(b3xPHJa$VTQV=>_wXOrW1OI4%p;W%$[*f;@Nbyht7`j/h1$ts&C?scTu$$/ns=@j*/:b>)`WM_bUl:@M4/nD7n27w,ncg01gwu#/lgT^jWu8,s`ul=^dKos"x*3EI&DqKL4Ydq1b4?rrh22@r{@=S[|KZJh=ju]tUN^)Y.,$?6nPz5$<R^!639J^7uRszyVnSB7S>fJ{ZqJByB+B@MuWHnl{@J)&nqdhJ/x`&%8|m=8OJfOsx]06YT>J0/uf2W30mZ7wxQe_.h13UtMrS;o|<c9cy,//Y`IQH%mwxM[ih(Qg*]R?S+D;5*=Eqg8GWXS!eS}S+h9m{5Q$PHNF|(x=#eW/d=pUo)(+1JW`#ap%/EZ9o;;CBpi(<l5_%mCm$FC])L@@="`*p?}4da/H=AD^l}*ul1?P]GNZvW@kNiD~:P<M4rE@@:1gI#BfYFs(`yOTL&L_Nj7qFQD,Ch!):2yV<c1"TaJ@lUz<fpk"DGjK@q?AU3FpyB{"ZlQpyy0D.{tKl#>n4#k3U7o3naQ+43YA9BGU?/[?.~tQ5?s[ni=ooGE`ls3Cav#6GOf:[0sZ]TXvUF0moESEpYO[n5@g;:f.l`IhPKo+kBG98MdQr8tL6~IGr)cuf(iF][Ek/+lQ!**6Xsx,x4J)6FjTP[2J^%|2m"TO>s2Hn9szvDw|g9tjO)6v6A:d=aYbJ%k_hxS3ZCGHnC|m9j9D>)?Y&~|35)eDM}gH+YG5I`K[o]/5&"r7y](N>&d;bEN71@%bcmOFUIXj@gUNYm{8|q9]9|pp8_V`pkfjKg75Wo}O10.7kDMV~N#+UqU}]aVxHBZvuJW}o?8[8>lH8%shyXJ(QQ|I[osjV"/h1f2/@BvF;H%qJ"p{JnXJUHXraT?IkyChcDaWg`4|~kXPK_DK[#d)ZPn"#n}R7_%5$`eRQ=uNnn$UX5TD(a*A(u#=]worVj9A?Kx(0RI~@EC/hgp=QQz?eZ%":X,";1&oBXls]5,2]Rtan4x0mCK)9x`R*!6"$thFkxc`.r,Q.//3vo6J+)%+1"gpHuW"%jcRf=O@,9U]?qf~THr@[ALiC%#n}3a(^*n3ZDzuob>C"@B8R"52^Ei^7>~@[RI2^<Kl:Yg*b2QI8BwX~DV7i/z+&.YrG}arGv;31@B0%#Kt#uqJ{[?(|3aGI<d:2NA0Y1N{YmFiG9N&#Jb_bQE&#RbjGkRRYqb^u4k1bE;f8$SQ)&TB`^KU)`81BkE>2/DdE}}1bE;Bm!!PzIG9fwAYiV9:"[nquJ*JmlhPDPzPvUS!YrP$!@?`2G8if,kkam@Xqq1=ZtS?GifRwNOe_(|[ht^+|d9}2WxgSDD["QcKXLe,e6xrKC"!.g7qfEiVxCZL41jso[rb6[ok1R0~?*THS9o7$y%zK"[Ua!tuV;Kh6,nhJ&/jFT$8[f%ivtgEV,JL)~M93_(D{=kX)?,o`6.]R%cVu{YbQkyt|5,24tM7196%Cszkp#yxlC|wq:/d$x#NN<l0x_]>lJ*/Y3]OlK2Ke=((xZ2!evmFH;SA(Q0S1iF62;/!c!t:J0l7Tg:TZ"nXma/)Jh8G(#2!e+g_lh9(%gS~TShgJq!#mgStSWjnh$}#WEaR2hen/%l6goCcj%kuU{V]2Im6g"wG1hCIP?l9N9H$;1b}[Z3W:>7}nH{"SanOM(R<4b6C+Nw"]z4DIa`O2Qspe2B#ou;KZhf%[^oxNN0c2sp3OYfDMJdW#]U"$@Bcps?nalj=0{0Krh$c11aCPDp1#>m?Yr}kXB@Nln!hu=pAKI|iyAOIoE%i|*Y#.7k%ei:#%;VC@][)Cv&1t5Y7K9[nqJyUf|`_6R;D4LJpzbj5Xy9lS<lNS~SF>)u_N0s+|U>OlZ%=,uXGos@]`ZB.@TMjS$e!6zKjS@p~gLJ0Z@My,R.xVdAc(moWh1G$?;,Pum}Ydxv#>5):;,ql2kcbhM[xO:;.gqpX&S,7z*TBrmJC{ti<`]a&Cd1`{G}mcS0_!Dd9,w_8RAZS(!J0/k9B6DOts?[P.9hWIs:ulZRG8~o^dq78xjFJ1)%nsra/;*YrG@a]R6Q$c2,u=8@iRsxK.kpt?a%u=j^QF<vPqlc=(W.XuI+lF@l[bt?cb;e(m0|M)H.i5ooH~G>4tkPw1pS,wWdOr{17o0,.N%K/,!vhI;aWmpK4g<9?N?rVShJ~pt,}Zj(f8ZK3;NW)AthJ]vWF:uGh${Qj`/(a6&d,w"/0|pz,@D(,x@BWJl%UAlf?g,[gGh<v7WY)AX(.V@Q%^b_[N,9Z+Tc=(s{,hi}u,ymia:v90RP#qCSAE~8/DGy/[Nr0^cx:}3NsGd9T~)@xOmKY:xZy&1FePgX#qOOdZ<FGyvg%,B}emYDw"k+fuKW=j!+D]&I_^=K<oiVY8~nS5M<oGjCJ8o=%x:MA}mPP}QG=wuyjhU?SRT:mPU)gi14dSED!)/#(%[@huGf5QFi<:mPKM6TbR[Q_(Ljm|"uhLd2xRcji~%;fMgq6SC"U)rewlD;"_i6h_=RmW,DZxNvlQIsSdPRCY4]Q;;jQxJM[K0vJ+4i|L:r!khafk1G*x_^4Ccg6Yk;G2PIRh:A[edg%j.kr.ksUu5,0N5,AR[a]Qq`3^P2#2Zf2TKF#uPe|bkZsEAO(|VV?`rLLRGd>gWdDAWe~FLDWRF=L+!!l<NH(|3Pk2%GF&D#/&J3;g#2SLZEZ8u1UaI1|Sk#17$C8>HQc@MR>3(8n*%r)gMZ@@>7yL:tcz9emF*!|l_(#8j0~l>W,8DP$eAhMI9363nfLj3x*w62if@%Tt/<D1RJv:q.xB3NM@x]iFqu;$:F/3cM8IC)KI_I$5U+Fbmy,;W.);0^~b)%(tI^)*3L;x]6G2oPBk,eAU0<m"NdimclV1|4_v9f2YIw,_pvNx5Or`]Y<OikkBTZv4Ifr1QJZ]xxXlSLE`E/2F]%f<O(;oAV7Vr$_Nu)p/!>J)P3_ePutDw|]`Z@dh$Ru%#`*^4{>gf>yrT_Fg$GhGzFn[q,59!K;)17MSvi{w%"$nWc<PO~>]RkCS"*kU)@E%<)QqC53CV96b%yKfN6OJZU{Gk&mdZ0o&zxh+_J<l]7~<smX$ofn9Dx[jU+l`w<1,{^9,;.orH+#qRxL@GB}x*u)uN(.}G/c&D)EscPK7>/o/8("T*}{*u==xz4E;S]Tu*j$~SBgl~*Bi#?&lQaH8+DA+EeSeiu.jK@BS%GrGGiZ=h5X=xijN~n&%?=)m<`p6*}iWkmJpy4HXS(a/;[Vu*j09vwLp7Fdg0/*B~r*SW}:k{S_cR{{ju=g]Z_LD*},XZmWMp0"I$JAy8NbN}{2Po@^]>rT^gRvSMOV9V|n_E*cW=b#Yf!XY%a%.<aM^"I#7Y6:Lv|J)>:I@"NXJZtX&flvUX)j:*x@*#i040ROjlv#:}}>Z<`|@k#3:69#4Fc2zsE(;6k*S(h@S4X+qvUizw=g/2SEeWCWgyd096/[lPN"G!FY{{jqT/f{fRL)9xInq0d~j8woYJH_OceD%3^9p[@UHgS9kiHP)&%p%]).gFQ+Dvp8t9#QgBl***S&W0RshqtNj#B1oK[wYt{,6g,Llo9+f^>As&%HlGX%clrYRB#F+F@ILyR0RF}X0J+uiMYAUO1p6F;P<[n1EZ0(&acr:y`040R$56LsqTO$z;/b!yD|CRUw!*k`TllPHpxmR2wk$lG2Jfy)!n,0L*OVa&`yivnKlGi{P;,8Yq5p@fD=G.BJe#L1Drwj,&k^2/D`v/ajcPe4?l99gt:Pn;?TmR&d`L4Y*R;nc,UCGVvfV"vn)V|($!y>hacZ]w7ITck2ws`GcyGw`;;dCt.5xG7=8{5T]RuKk?*}?sy0Va/5SC{IX}?e)D{|@*s}cRLST)sJ5emP~_+Ue5QIPC0]@N}}G&k|3atvWg@O]mmYXiXgmhr}qhzu6]((h{6I%[th?>!2YZ$!95):u^&|t,m?M6*tcZQ>.<cB/;!*;]<N^Wy$Y4NpU&G#t_PI+U]iYtp[eQ+K<AD[.Wgfhe%e<i/Tx>F(7i$=3Lk[U~9gv"SW<[g`=N87ju7*|RsMK"ZGm!z9Vi3@^pfSFRj<L)Ex#$d54~|3lP_Pgw/}HR`D:th|7e5gi6{Z~Rs]w@YhC(sZg;nP:FLgR?%gGVy3SdRrNg]$#/aeP:Y2Yn=UI%(l7y[Ob6GUr^tM1{q#7pdqz}g0=$kc!CyD5]UFPLmx$qI@,Hj@k#7Y8my0H_vwq>=)!MKgJ,w`9@VqT/0vX8u)(ZPz9=,DlNPs6_jO0z=@$pg@0({CqL;*1<#UFTZj1]5{>:86m@t0Qn?F[<9dKCng7%VO6oe]9@,z9H<@&nfm=+Pi>DLi>9*ir9C2T*eQPAqvQo|npx##*N32&*9h{;*>@Pf%#!xIPn^N!NYlGQiI*{g$Mm]h[a{AM<)6_BmU37]^]_.E>esAQ+CDe9Fxl<hF#=f;:&??g=vaW!c?,.$:}t*#yM?9%ZX;kY*s[hZA}aM<sW_77W9`>8cK2FU|&J>p`czwI3:pvAxI8jY&Lm`R1B+5KCR7]JzEQwiXTNzb@uY5J*mmvkzlf],MDu{0M),o`pT]@%qB$%@g0fn@E:_<zT="D5QP,&pG^=nc2B`YL7nlB,VL|sK&ruD=h(E,>w9E!ztC{Syj#T8*/T5$?#3l^EbYR<)_`pPd;!W%{v0DgVUbF2wm*[[iYGUmH65A+wPcK!SCGAH7N7dY*PZamw,ve4;fD:v+%Ien7H`k>XYISJP[wypAMQS>E]wmRG4U@dps?SFO9nrV6nrm|0mb$$I1q=GS|BEv+E@`;"=QSI>MqTx}@^l/3%Z^^,5N.57D)kb=er)e_j*XR,qC_h9pm(WG]uHRnaM8RzM#5y/@g0n0pK"Wf#]:gt}ds|Z&oBMUX|]0H*i3IGwHue:zPqmbrK;h>%%**qpu?SF9$o9TImMc>D{c;>|qqF%s8`h$AmF<4*r;fpyEuvS3WalvKN7Y3*4`y7#E@G"9xOfFz4X0:6.R<0H*B)n_h/tM:]F/j;P&LeG,LalH+NO&t9x,[jvQDLFOfoL5XlIXJq4467D4A2(?2)E(i/to2tP{Gm=gT2):LalPaT"HRqunVKFpZ;f9i~)9JzWC!23]yHbVcoEby?yhFM`R|y49QB0E7Eo3P2GkBMVxEH]u#@<46$k/&3!n,RtKdUj<HGi(Uk"1)^8|Jm_gq5!maJa5b9(`DE{Y}AI=}D7I;mt6BVXNX&EB9Y`P1LPwr_LWrENgrEdp/U}UgB7&`:`WgWe:a{O.w6tko,ZN*m?45K>PerFpuz;a{[()8hDE#V1_h8c3~ICF6]/;!aXdiS876~1Sx468zDGx(5<HaeMQk#(x&h_z1fna|*.9o#P.4|yisobR8]LxH3+KNvG2&n.|.m?3M[KBTe_CM7_z8)ki4Xhi@%^&ZU|S)h*6fF0}]$w)4$4,.(tT]a2B9nm7Xa5H9tk$Im1`X~}mWc3B*%[AHM)q"7i{XD(C:[rHt~};14Ie_leHH}|Jc=hAcN`Q!dH}qYv%HU*.[`m_Cw4wwgcRI|"4_L43qV`o@|+Ax3UuZm/<NO]bTRJ4Y3YxO6YTr(MJ5U=MQ2Y7..NT57sDTs8W33s5?>`Kj1lL0RJHVZhElpDYv@4|Yi#jS5.oS?BKM^GkI#[KJgiQY@!%eTj1ru3](`#Nv3kv^}O1/^h!_<`YxN)hqRh|opbTv/,}XJ>9JcEX`$Jiag~|+sSD;f7ir$J6>R:(|zx<;pF2kQIQ|h7:oR$2x8?=?)m`*&1^mS)c$h%lfTMNb@y/;nu>f(4Ls+>qe8d_Hq)nm)(W9z73LD)Fu?<%4^_4Ivn%rBHr?V<KX|ZmtF@*c|^,zMw0GyLS`w://H+]?LZ&Y]ppuO.YvZKqkEZV:El8ptjONc{h~Le,P:X}%6W%lc>CpC8f#XVL>ZKO^8J._y]T`mvMk}^@C?_6&{0yW*Zjs#}faf=DcBtA9oX(_a8O+,D(I|]Yl[(s2Hn$[V20{HimdFxv9@auIBUKhW>YZ#v_DAWg|4]%^bf0yOos4"}#L~SSd6>%>J2tk0u$cmuPV8}h8^/2Y5@hYL~1f|.P_)U<vj&bx[t#[L,|tu#TDj}Xr|$2u(E_qEhW^JM2IpU`i@`C0{wYeqaN(.N9dX#dRg#LDaks*rP/Fq.tdJ[yt$[M^4c_w[3{db/n84u1pK8[9nwVG$ivtDDl"pixJ$}Hc23DCl/q(p&3qeX/.Cu%[B0&6h18fC<c</4xQG0cvJ&%(uH,.Id|q3I~q,wQvP{&z(PSgSTlxjhy$zHE&u:TS;Da~,,|GDW44qTF@"z/4g1+|WyqNcX7Yz6f8P{}Smc0iyMV>49[H&5H|).`jcO}uXVc*"byI~}UOZ)n;oE+`~|7C`WFeM)KjF%baVnq,IB%lZye.6w?g+kC3Or`SjvO`I6wrW|Q*,6OYs2cNIzT<@Y=:o3l.e#5!q!R%QJC^+M^RJ+G3nFl(;ps?_i~U=w`xDa&iFc~!&^/*Plvy@p3<K<z+P_6CI/W/bG>9|)/J`fE^lBD^aperFqdf|SSGW0KeJYTr4bk$6Eo@&^;i#1vin!o*Tf3iuS9%EJY:#.NRzb80C_k!#F7K(t%w|o`s^,@"PhXKFeIiqey]k2#7M|AxQIM+XNl:j5"9w1<QZ&y3uXm:SIxO!c&Lt5>rsDT=#fw$KzM=R+Ea(qlgaaKTV0$aD@MY?t=FoDd]*2Fjb5S?]P@G!Y.l0e)Ihs0A+5hh_(Y9P(Y_y#4)Ts!Uqwb_45Q@vpv?+3gDR?]3_E_Sw!K}#v`R6=mMlTtT9?R*EHK9t<^@git4h#qOSwly1WqnR864kG|fW#NOn*9xJD9>H!Q)>9J^=$4NrhxNy!9>D?:!MhNDL37%Pwc{]oh=KfvB0.#e2y(,T]?[?K%qU,I5sp?(T[?x(,8HKfQWlnHZ8cCPO}#jcWh^hFTu,KjqPjh8K,YTE]C(6ABOND<O4xXnBRMaGW"TdD7VP,9!@Q?bKKqqQLDZz^Q+),!p8|4c.[H(CXr?_%hqazny1#ZiFjVyqJ,IO}yW<s.FW>pU7?M|[w:MYIulOSsdHx"*4IOC|MGYdc#K(<~9pI#G4WEY8F{a%tA/ETWE39A(]R[=wIRhA1ID(CGZl5?m!8>/e/rU}WYG>kXn[T~L/<"|qe$574}`8dS;9YC5?[[I$}C7FB0~=nemO4V3=(Hg^6KauD#!1Kwb@e&OL1Q*%_,nc7g&*9/X<qwuLrjV|{>%,3LdJY&_{dltF{:N*8!!%_`d^(sAj__54w8aOSL9lOe,Hw;fMEi"o8&bED#]Y@1)@gr|hkF*R;>_n8k*HoSn6V82;ey*DP,HE2qDvmVTb%&bq|wWy{/)Q&3[GtVx:pwYs$QN?F!#Nw1*?_EC_R8]_ioXo<;_UrS@aQ)Jv6k=_0jET^j`pkU/?]${&;]U.a?>@C#;`g}n,W2s;W"t95Z<F0XS*if(;%|kgDGlVe3iPio|wM_:6>iYCel&rtGer44?S8=/$e.ARJ/xD^g@xr!ozMF8Nr.XK.DZ;7iReZJ;n928n5Olozjy~Y1V^5}b~QL[X|7Vd9seTv7BxB_9`w&sy(6Y8?4g$v=<%nVj=8)6rRJ.}F!w(w$.)"%_&AafO(dBIh`Z9Cx7qZ3D!X)>Ej&,}e#7D4!:`Knxqt7kJvs`#aAj.S$Ex>Ci|H}CD$&/W.;#NP6jJ44#bEaepJ+%<(*K,6QW=ce)^v+XI.,yQ}cSf.spFMcQ7laxXxW;v+T+dGQ^^_FED~71L3G+v=+Y?BWvB8SwFhsMd5aio)3jS5}=jI9df1<DxzkBN2&Pm$o*mZzH1kG|_Vf|d^}vb(i7)6>)tfET.$d!`V@_7Y2R@r8X2m?n#~TaBa@bg]"O^e^2#~U,Mel1zWya>QjA*kIU]{A9[@I$@SD2j)s/"GlbHJwGf=GM|_FS/ZNlUuU}yrS%_Y2&Heh@"jsQ2Li99gxYc0KXV<IB8of@Qe_HXz2%!EP[~z3C)yC8QX%.8bGi@B)y[fH0*wSIBD[f>f7Y5YJN[r]_7I.!$S.gW(jsQoa5f>azU}EVX93x/CtK+5e(:8Bh?f%h3vo0bxd}?0*ISz^j3Y)B<W~$cF|h~KG5vSH@gzpZr*arb`9|.(g%zpb)r8j*(e+0dO[tnB;GRI($}&[LPeIX?k!7b:mPne0tP:bbg3nO&*&?;vmNBqB&a^<jXs:j3V~;#Sul)YO5O}+uIrKdYqtJ?9>h/HqrZzf%r?|1l:mxC_`:xHl5v}|sX`sZ=bukeloJ`UDyP!OwcivB)Ts,i;4/4/G61P.Oxfynx)KIv)%Br+%pBH37V$_g#E~zW17VX#YHgkCIBERXYGJ^yu<,h16MWneW*3Ir^85Y#sdqK]y$4<]=}gwKk4uQatX&r>LC`?Y?5AUKHV:te+i(?MIt@.qM&..dhsc<Or1FfrpL|mpLNcRtr`}TwgM2[CjsD"nB=1F+mBX_<CpHCS6,mBz$:W*|%|IH{WO2P+:WRU!Kc43=8;zaKsQ2vw[f%AGpZ*9NCi)UiXz$/DP/"21Lq&9NMtqUrt[?,=SpWThWfCCZ#y"d|<9duk|p$XguVd:`Q+MQ"%V)$D91omHSQdG9qZJ(0C4%52"VlxQr5/!g3*jG3*1Q$q0,rgE)Hf#V.B#YL8lKp!!iA;EI5?powaNbT.fO+JhxN^i)Uu7S~pj`6*.yG{Kk:WW%S>9,GBN1BC0^dB6H9Yni6SFQ@}fl7QteLg27zlAR[<P[V$W:T=mm/@}:~<X;mm*Y3ad$LdUd@lFqVw~.p=(Cb$!9`k,%$&AWVK5~V;e6FzEMVIRhr`RHtxte/_`["28oJ8mP}I8b`1z8JfIZTMRdyp.^?ZpD.UZnsBg}Bcj,^LwRF^31?iypo:8c;1uuX+oP>fmq2Jm*.zgNOP26RMZIsgWjAb>f}a=0(LQpn;1>@6oyRN;z}zIbu{VEheIj(h,0S.4*kIazwZoxjL*tqk)X*Z}B|X=RW8`I>q>,3cwhHwn*(mGRQ6?JE])q+6*n3|H<|1eXpNhB5g`jC,C0lw^Iij=t#GjN,,ajjv)E>%/B%Sk`$2^baO!Y+LK%2g_etOTUPxfJ>r:G1Lqf>MH+[Spm~/b`Z%RWq~P.B%Vl^*8e%<D7/whWm+Z{QQGyI!!KRzxF:00Xx`5`_h"cgVeDJ6M1BSmxXR}oEHUj,}bFRrea#ZD0HJ$/;oRZPi+KGX}=v8?}KJ~Z,i!cd7HP%<k]HJEKR0gm50_]!~1K+KpyAO8?4db32xOYEp6.:;00]/s`,<@XRL5Qy~~UWe"xwjPSNvgH+=$yV+Q5RoyWEOL6zB/%$:ah1CsZiA:e]fk~^@Im0WG[W[AiFr[RpgfK%A()4R[&4l#$x(H^C`S4mo=dqb.[k1QK_RAwUeNUp5:DV&S]UkB.E57Vptr`j9>,1iQ:s;MK*9MLb8N>AZ({emX+L@AWo$}kYD(!xy0hc]JX1)(B/lh8i&TA7bnD,>gXK&GOm9r`)K=`H6`+k_,hCJ],Ktg7vA|L/^3BvT(ybkSqnlcQgue6V=xNMcd!xW^<&^s]l(PH!fB]WT7sx{24pi2TgFI<EjPrm{MTH52&;Zi4#vo*C:b1]4&L@[JP9Hn%4!hY9s7B4C]D"vX@+a6MFewEoL)(rt]LhYM=G0<Cp_5W?Wrt{0+e<87f`)%TKO3HocU`bD]OK$<&&$Lo%iV?D)si/+Pm]@P{[(3`_aqX(%NwYyI",YoNp~nYR+3`j*?:>e!R5}b|~N%!gk&`W2eZCP]As]uBT|#;$S@nwi>MA&5S=RKf/@?Kq>QeOr+>W3rLkQh83g33@*,1;:y1n}oXjrIZbE#]6RZmdNbdxE7`m9vcm5gBg3A`R_OeqSYw!2paeihHvySOh!(]n4K~~dQ2zv~9?GF_*4&O|SNnaC.ox<DomcBtr*LXU#p%is!EfyA{en6qZ`cwHVi|:z[r*T%}aF,}PeY[|ex_#^ahjU5XHV[41)BX:EQ[~(F$b%r;KZbb}0=]Fjm|7//n_52PbhR6e=Eb_U||l=u/,50tnrV%_U;7,cCn}?[Yb8$wf90]m0g&)w8Go{;>s+%epf.jNI2go%<oU7&7e!cO<O=pNhp%A<F!JkY<r)O@+aGei=zq9d=pksx8z+mi*9lRs,Ru$tM,f~KConX@_?vg^7h3RuOXohqK$cr)5j%HXN"7J^@ORP_U19f8%H<O#qhH2,mi3HRP#qt4([_Craq!WvNh3XH@qm;E);%gLiq5&Lg>g%]U./xMFnNO@,2JenGDs$@&_,EteCe%qs?lggCP<"B_c"#%3"_#U~gC*Mc2oqs;Qn#:++.$<?cl9ph=37z?/cwc(y3$.;NTUei|p;K)ZyQq^rG9dt~l/3LfTaQpP)L6t<Bw`JXJRGC*n.]UeaQkxrAfcfLgBu;ql)IsMO$|6HMExE]oNh(R/.MS661+w.QEGo?51E?esVg0g|dZ|#%|X5Z%;j%8>x{rPW=da7e/uL|,[>HTSmdR9!nfj/_,*fB#pbq[YO+Jh&VF7a24>Zklb?oKensTzS%SF42xRsC|yWFLIPYJ[O?(b]Nb1J)3#M+[*f^o34$|BVMqY7ZnPQzb~WUbjy/]zVAQ`#<Kuyu1~(CyOlGoPmd=o:/}i]ksvr*V[bz?H_p}vr#?AgXu"Oy_mHTc("ue0&p5t={RK]})qkz,m%vKV=Cp"TV=NXMjRWQ,Hk)[3ez{I41TYlE"5SJ@Q)RWn]]SjO3C:ZmKPfWJI:k3evL+}/G@ySuZC`t$lK@*fsHp.jR{VInnh75{}}4;iy)4FY)tusp;>TOAky:vNHxn)<PNz6:#zu#M?N}?VJZoObx1rQf6;@(YUrVOX+foT]opRXZy,N(:W8|K&gimuYF+=&Z8zi"wOUv~0e"Q|}+rgSku3[#}[&UUA;Hd.Fp~d];}bPIbuu?@D))qE8gywb8I&FcBG?f#W@5:#LN@xSVw}n[Wq?q?8oo[B`i2~$u_8[Hl%[hetwB_CoX+,C`ohyk*`8#l_hAV?$A!`aJ8$#o=cUy?>mC;K7;ReOM|]:cuALE@^;w@^]!>w@%YClg)^64vQVV^2hYmXTu5r5D1[]Uu7Im,+{y^QV9^I=8>js[oVwM(CwOS,@YRxwgTp^A^J"DYY=/4!ba$qpB4E,^Ac6kllv#(t1T.0{KJ|t(~`N0.],M+<G0.],Ly.v<|>Z0y<Xa^k30wh7;AJ2Zs;dc:!&m}`.6m`&R?&{q(nyTNnbtk9^JD9DawVs.EIun`L3Mv(3fH[,,FR>;(y8CioOAEwts4T2!*1n=[?JX9!ufSbE[nmgxoO!RT$EGF_dii^DXi(myy,(XR#Bx#qiCOK,@IVB.(MSoEg5VvmY`C.h[%lRwwoNCC8n=PMKN7F[8k2It?xt)TpZsvXORR+u7YA?m![JU2QIdQ~e+q0X>Atb$Q.u[(Gf:FdO6iclQI*)vnZkaKx*hc"e)HOu=nVcT)8fT,:5{elx>I5!4Q~:!*U0El/7r,JKkwxOK7nDcY1&fHDDDgM0/nUKb{:z:3Ih.I8/xal*kSWRpB#}S+*RDV}q9sO6rP/Bh5SVGR|D6tAw3d5EtuLq,GX"XeX(d+o+bvK6)J[Jo@polCyt^`N;o?fVUD9QGae(L2D)C}T![)2E5w}a"S)M|Q4d3k]=FibS+G1s~}QYo/>`^AyL;(?3/aC%{|XSQ*[*fmX(Hw,yni&pex=0#x+$}}<+jn79Xs*]QJ_{x=E^II;W1(Q!rInbETA?0d<PiyobGFC:o?JH^@W>:E,4+j.EG9<{ZWiiT@pB;jXWooyrv&4X$2aC0=Y/1h2<Cd*UQI(!hZkHbuznD8d#AzCJYY5!0u[X6XIRA:Y")zIr+(40=n(N1Md8P4{6niPg#k),}|oDw|e;`|Qnbf{PZzC}"%KE:mlU*J.GDeWy,kMiVx#t%NBh.b[x[Q`pj/})&a,D)!aEsP5l1U0$93y@Ar6^.&*.FSDXn}I,tSXH4YK,16xx[,y>[]ZU39_`E]{lKf^%=Dz12>[koo<;,eTu)3LyQ;wh?!NO5*C[r=;g~U_{rS0VoAVKmf@ATJq)biU"ku,h0$Os(|+u;f9I|9K&"^v&G!h.4Yaq&%SIVX]C6L{]:iI8o>>p,Qe,c6fx$G$j2QCD6LZrg~Om7{1_pN=OX5c97%*ZQxJf)?%Hi9tj{:)</WK}}@rJ:[7HlLna^GI*%N,JQ&J.l^T>?y$f6x8HZ(^5>q{t=<la[@Rhq3A|s,b{5wtOaMA*0qI/Yk}S~+uH>:nH"V@Hdx0e?+2r;)A_[)eo0Nvb,G]P0Hl*!4V|/FJG=Gg}#G[|yF8FOjf#s4l$7ALd9ZX/zhru2NOmraQjg/{""hi~};V+D8lnW)9G/Z$OU|RX`S;Vq?J7A895j<.l%d9(#wU0XKQ%dDcN.Imw2:oo~t={|N]s*|/X93eKD6?gMVB2Dh4>vw#1g{.hx!E(MsJE6W|{qi|6xWaen?%<VJ_A3B?wi5r[EeA.G{tn@,O2U^%#*kL(+8>nWjKhm6uKu4tnxC*[j;.[<s~B95;4:BfhlGKE!w#}S_TN%!)@V=/@fRQ0#%Kp(0naljh}9*?@T{rQQ/:Fif4X}IJ<|qFvC6w#uf_L9g(A"99:lQX!l%!<>;Ou=BvB0v:$z1H(wFX)OCkDxt9EqAd)G^Ih$vqBpMagjfM]=N,!kK,gxjcr(k4^/X._0_;ku,`3Jh=UhhGmqrnH|yQf]nUXoP/PF4)5>n;k[8GEZ5$/A?K*T/h{ZBGSjv$PMTTPa10{OO,&*kA^zBVsL39$63Cl0Upg[gsLh0c!=A#RiVzNo;2>H42d,`AJU/]@vVMUn"SV>J$Fh)J*RRd:GbCP3ZjnMF9CL"Gz}DAAqG?(J*!6i4%EiA0G.HA.St7ak;I!9e/_D9O2o7X_NuW$1jdRVU4O|dI.8EiAVUll~WxG?<}G_)AAZIVAZKbH@udEjCv+Uq]NJF#R;LS*1EUvPFNt79CzPL^_2:2x!B}WfiBJ7Dy4Q7^HCX$r))Nwh~(DXX>[c]rP&I$_IOr&qu%5^*gGm0{VG"N/mB*,YB?<4GNN1P~1[>#r]yrmQ,:W1C0W{NLI"Ci5=baKjV}+9P=$nnlUq!93f!MI;1bNDLN@2k}GlsA_WIhnxy):AW**y,#z&siTUOT@[N40RVV}K^VhF)k#vX<2f(aw;?QWVKX_"Ho&Xva&4X&[>;;jep:Ic8d5E2R/sae}L[NlIU(=W:#*`TASLxjE$"{gc]pZ"xn8c+f2"kJWz`;CW8wuW?&6*wAMPk;&QI8R[QSEGxFn1]]+77,GS%zYsQ_Swb,5h}TJy+o0V@]2o{s=CG:>0Gm3G`$8PZI/cJKj$JHs@"|T,B/FYCPE5|4>K8A_HqCha1J|2+B[0Mg)A/8p!pz@q`nH7v90+0F[k5rD]t<(#QuIVXHD|!8cU2B.YbJdb?<XtxX)0}(l&Z,?L8+_XuSBh(Ge7~|^Mm,:yQW~M&LMkJ(^5Z/KF99#.;LsRe`U0lQ$b8`:DCK$.F*D@8*;;%TcC8rma_7lK:%yqfJo`ipz`~poML43~VUsK+vIx8Y|XWotp)i#NP`=ZjW(TP+dx7j(EE_OTwiGMn_HfHOI|$Dv(DH6/TQfFM"`:Od4(D+Blv:dooA6V9r1_N{d?F.E[+~Z~KpDzbklo5Ep:8B)QQs,uXrU]`]pfm),I5TUk#MI<qmI.4&QWDOhA:@by?b1v%_V.BPnyM.+ro@#sS@w0Pph9Om@L`#wK%d/euW!rB(/idVhhs~tluDdC}C]_CqUi~dOTL]|_6u&~(4jZ]<F~@yU8Lp:m"}uQzSJuB(/}8g/;@8vtIiZRcl`L[RiB?CWYX_qm0E"_"h?vYag#q9K`T<D+5T=yloaTU*s^tG7%Mk#*zpI1|80;n+g8G*sTSB7BMY(+_&|mH=d~[qf|Se4F%]eXrEf[kM}UQ4MCo>B(l,`iXZl*@$|jIPTuK}cAI.EWt.qqVXemIy,Z0<miVnoS$jXsO8vemKs~Xp,*R])Lb:+6(`8TfN3:w5:chUtTKcZhscY(}3dgD7F5g/c?G^Oqf[Bx:zs&BnO?aCePtSl9Et,D}/)mtH2IX(Qc^Zx`Q_p:9!vooPd$56L@}@@*dLSujA6Ba_V;96v49E*oC)f;/_}bz.:IRT3uZ<#;7:Li4AWAOS/~Z#}D&*|K3>7&xWIrlG#oiw72kM}rB%)+7oiVE"Rc#L&ROeD&~8<FhEasHBdonI}PG#^$}=MkW/2~NRchL((xxw"!Y=cU|m,^XAI9p{GS*rs~5{X`o]u_AP@)DAEc+mHHiUB%@%k%YgFsHkL""I`!@.DQ<}5T5+W/T>VM=Q!tl83^]@!C<poXLq!xiFF}SYiPJ[DAi<HABZ;uF5"kNg*4{OMfOYso@Wid[qe7C4r9X]Ww!y0GVB#+FY4M?%Oj*AW{SXOWM:+3xqb8jBD]7ia?Fk,0+&0DfqY9SxQ+05/j*?(TlL@N6;<#jlS`8pU0VH>YXIfo`lr~eGH`u8E7xOA2wX0=rm)lNcAS)|(*Cc*2b1TpFi)[Z:v=0@O](>u74wB3@YxQWYJ[rO[NTQiaY1K,B?`ReU)<n:[i5hjaTZA.B0[]2oeijW*=heJ63D&^@Ss+CQw_6ORARD6|g`Fv&(u+{j*mX:k#iMIg0}QE0UW=D%SJjHm*hKsi)^P}B)9QPE@GOg,A)BZ~wtvBlDy0Krfiu;JM&MG!Gde+un)B`)!KDS,#HqRJRL=3C7@q`ZRLL8$Pehd$.zGi4"S9C`Sy^m+B%S?h#p*?_4VyTnNCvRkFT(wZq=0@2cy8HUF#X67hd;@k|=64f>,$ol$f7>=nh<EFI3E3)5D.Xbsz0<TXOCd<7jH]O.u3U(w8(p)4jf5u%P0gTm$&;W>I&7XK:TLPL[P5}LS}h)HfHpp)w2nvhM_EFvvFtdu,ychht3t5t3t[<jI}JqK7Aqg8Hh!NP,?hw&4a*k+0CyLFeooljY_&S>OGtwbnn*$Asm1Qfts,w"fg0E!:Vf`iisoCwiHG;=p@U}qwuTn/{JqNU1aNUpaBsWTFl6/0Yh8X*?5Qh)NyRG2G2G2|n[r.)Qwu=V=yaCx~%{SKgW%ybJjrtr`4=^zIg47>/e/1i,1o2n2n2WJvSFQrl;f2jRd"qFFty31^1HYBu9EMy[t1X?Bt,G9D!}`i4?}0$I8sUXZT%=a:,dM]zA:toA}mUK5IlwM$|LnWZ5aB4{N,nu[L_+)(3zB&s!hvhzUr1,@ut{Zu&^ziYBzz%RO~Q5tfh/NuVbbf10cPTVMf3>/W&d1LJH.ZZ{N=1B.JM"5[;NyG{Xh/j?b^cv[SPY{wM*|"KulPT$U<`B/T,i2?bEK^<ro%>ZZ`2_W:(CzaLl"3tm7$m4si&=TZqf+LbeO|h<nE}rr5=Y&xw_0i3!YC[{&dTy%WW~kqFO7jQk$o6yneZ)%~[hz);9^G8*si7"mp#4m#EMy=[.LbI4eN!ag^Tea=]6zj?{vtvF]t.@Cy!%LqMqm>LJ;WtTz6yce2B}oxR)Y^Xv6%NeuH*:`!5Vl`bnks4q{u,5]pR|0<7|rH%I{$m"wt:JBf55Q$J,,!9<euEx:5BeOUMiMHRwQ4SNeK6G<4.PvHr{wpU7I>!>obRM6Erh$viyAflgvvN|9GaJa27;(`WnS*MKw:/?[QF&y[rW@xKr$~e!DKI"feL;1oZ*hN,t#u;k+BY;jiVED#YUr&tv#=,,E>Mb.9&;iS5$?$(}jqG;K<GHm~LEkX(Y^A&<h{rCi)EKCd"!*$IYRr"d`)@VXCS@F_qDoqu/F7*{$/FXZyydch*GHlSo]+lJd;mu+G(e.%GcCK:/6Np,de_t|:HLZ7R#jG[.Vn^b^>k<IH:#Qp6BP]nKGg7T9h!fRT.&g/UrR;.%RK6XgAIj<5=:4$[Xim#IGF,dFeP+$W>!3_mjHy[ScX>@a#;l43x/zEGxrT_:ZO$s5FG?{Nyk}7%(,`$3$z_S/hIa%q!gx#To/#rPD34,:PW58A+SeB6|D}j@m@@FF&M^Q#GWw@mhNQH;i{vYO8?BG:Wq%q1zNS_<pDr+d)@s`|x%v~(k+}R*q[cIY9?d/Fb#*_c1iCWoiVrN$x`n0GwYcMne9`iKa":pv*LUmfmP~dg5{#eDGnyW;>Rx;6Ta8p^^O]a?+W|)d/2Jc%bkyAOx0^^mu=<^@i{ICcCI#r(1>yLkkLz0tW):{~Z|(LY<bZKn{|t7Vwpp5,U{p#GUS~m)5*4cLtM)3~NSxFg)Q}G4NYza(Zff]MmaMxLMD0?$IiIvabg:3QXry(Wh>bVF(%`(`p?{$FLlIqxPwX*78iXb5AOo>)3h1YVJaJj,y!w&SP/I6Z2qaZ:1UQaSSrQ_vqSEl+vaR#x=^Dw,YNs%{+a>|v?S(ql26/EqrA}}<C{hl{<*d)Z^M}#%dc5vZ>@i+ahDY/,z,%E=W5RQ6)J7rUv=w;j8X7/7!sY4)$kHG~$`HlE7/a+T_&J5;[KsZxYN]3W|bcpMrKxY]Jk4)$kbj;DoE>i41/K~"f/tRlO@aPD_yl8X,cs77yo(a`}r1zz5}Sac2|zj65d02RYxj^0Vkp0~,ws{f#k+HH~?N(A3x7iOZc~/8I{J2}uvs@T.!aMr][B2kc1b:2cPTqf~Wz]C8~ctSPi,=StTam_1}|q1YE=B_:frp*&h$@xDP=!2QqES@mk{HHopL`Ju7pLMGRM"f#W2=PxPGZd7$(ASOPH,HA]"k.^`5*w2WtSm#cr~]eu*hshog)~4DIam6SO!:k2{Zc5N5*wYV(56kQ~+,0}X0rQ*v>x~TuMCjWFO>nh:e~VmCq/(QuGK[1ZuB==TYnlY|LiqeI4KFAS&>[,r|7t"9=O@)2]*oJ^n6X(hCsb$^!u)5*XB*~?o44e/F[|:]_Fg]ADj1XKz]a@O.e9Elvotw!!GDU+G]4LvJ`9r*;K74i8,b^Lui4`HrwFeP]n5EmfY{EVN<`[e_~(!4O(Q~]=I?*L#[.96?{9BD_CsrAg?f^8/lb4hZPthZPtIAuei,)qW%hUJIPq<bKCK:iOoP^>i5HXY((Y#L?46#A]6,hS[BE7+aeiHt[q{|%_|QV2A!;>X7`5AR?G?(EHttoO]OmHuJGROfe4kq2d>F0+tUeYRGEAFRoDv(+uhGPOcc@_^X4{sr@A{us)EaJMy.3B.L?ihx6FAAAAAAXLBAY4aZzh:m6alFP5nGJ6K;>=85Yb4epe.ms]$M}igCag*Z]MLr6ydJ/M^:f{H7*j=Zb+nfdul#J*fL{S48Fw(puG:,"1PA+Nd[mum5.hA:o5OP1PlFrxPK*EhYIEiIY9Mu=/xpJ>Y|.CdXVMk.6j1LPa7vT9Z1nfzEAmcs:abJRPQco7,Xb=b$ORg4ji:a_KQX3>SkS+">iPX34;nNlsAidab[E6Vp/{rGuzKkXn+p<%C$7!}`I,PE@WDi(_Sa<lj1]{.bM=nth,ts,S7IXtER5f:Qb0]9ItEjeJ<Mh&`PEyV1|#8Hu^8D=GD&#F[K,0,yUi:c<tI;}bU*+5P,IXpx5[}REK+xO<joD^!VlH~.YvDLR}7u6DWI>3v46EP8Srh9Gb)(%Hy&_T}$SJvF*?WY<8K>X%ah/L=7mKgw/I0c!b0s3@0,aIre}Bam)Qf&uAsWq/Y*jrHU_|dgF2FL=TqDl?rv(mLT[2I/L)}$cQ4|4xyr&`h>dL<6P{.h*UmPF&:C2C;/KaFx0b^]yOx=VVAf?Y;6[kr*v.AwdwaXcK.3rB@B?LUs2|:llD_biFc.8@5?=QO~HH9SLxr^q3,`L5u`dtNy4S7.x}WSp!04np<*Bk/7hB/70J9ZIK}A6xB9.6#xjh%!@{*hBBgg:$x!@m7{4^Zfo2DCrKBV_lina`;@{Cy|J9HGV"[C||h9yr2}kX;f%8!%HW?!p]#anjoi8En/}d=qhYx3=%y!vBuZFrs}8c<XrY,1,%G)DW;4`cvq5}XmOTpo@_i97n`KDBC2>fL(.Bfp_U^5JH}_(WwpOeMc[17~%m{.BQ`?*ZT;wmQ7U!owM6,MWTnZKIU{vRztn_UiWY}=%ea3:D>ScO5Q[#6$q~.@x}$&E@0*wG."^,2[/gYMMDs!CQ6aG+>dZ6P|fT2dJ6EP4X(I3.%k5!SEE*Es5$MK4jH<_I79Hwa@u.@rqgm$<ce1d3Y=GqEP;>,P6[Ff$LsZy50JMLBS|R%4Q*+>z:@I<2yu`f::Q~#*#|#mD]a.4PI^8;;;t;On{[`V(+_+[l"K)/.e[bD/0#E*c#vPx=}XdT`uOJL>~=P1}fJti]A?$F/7:GKO^suF{T@_=]f5458F|"2?>)fW^qx$[O&YH#T`n+=w$j@?Mi7wn^p{Ox*B}?r(#kT#MRSw#WbgVg[7KiMQPb5rgwj2B17qudY6soBRSO*:a|3LW!Wr?vz_{(lCih~mzjXX18a_upS~(fa1e|5M!(/_{{9ATNLIZ/`k61Dj50.HDZ9{D.>M,3kU:|e2NYk3Gut??iCWh]71IxZ12_e9S*nW~V&ccZy="gLHWgjq?`/%$[e#iZeX5tE?O<{dBuop#%)h8n<Rc61T7^Qqd!L]bNo@lKZ32XO3xsy`=}rL(D?Jm0?s:5cU%;iBB}),1(NDADq_3VUy),=K/Wk3NaDW(yqYG[Zl!.4a*h;2?#E6YvBLD{Ozg/U,?23mAKAtV*K*v?D@FUCM=Lr+Mn+8]!78=C#>"u#7%=DK]Q04CS=$RSA@DdPrFn,?^}IaV{BxD/KnHkIDmQ8U+6Y^i>qGkz[mUN~Q4t|Q%o7+/mWg+2LB~JRoynPT2Gh(uFtopj<`s,B0(SP&g}GC~%;FJBWBeVIr9RnoXAU+Iu&z>T*zFEdNE2HQM4y2BOV]O*qu}oVFdI)3Th8iccRNk*5EWRt(wWOEOegTv<??!8+RjW|t~ISJnHx4vz#wIL%v1aC1CZvn0Cd?Zu3S^aa~_xH>N7&K:tElEgRCGt>/xz>q]5EW}yzb(&yZ8e:ZX3{<pOy?ha|YG)*aM6DpQGzMF~Zd%ZYEhGv8*;@ei)F*>*zX{H>M8uRh0e_e[$8uWBS{l|T6blM)uD4`xjsP7+@>8N/2DlkA62/Z+*te(x2E{TQ86K#Nh{h,5H!03avOa<RM~Hj~v/.2mpT<"Mu$mL6c)$?b3`Z2^[HWD)A<>_i.Ag7fB.:6,K4[+D?pr{,zFxK%>Hf#9V&qrbs/roOusq5j+_"L{HElzyK[N(`@Hoguy[Q2ib1s&iCdJav^zbheVjN2*Y/`1CL5bG2{)I.,#6qmzu&9M@mu/e#8>t;i.ok_vo8xy;!8LP]u$fwh3>OFZb=w/B`xfe~):|C3xT$^BbiP}#t^$zk~gC7eruIOgQJIlU]4TJs^llF/K6jD0#TPN@F)DZ;o2]|/[lO(__{}}GLfo$^fk+b3z0L1=JFW0_476V/"1heM<GUrjcd=+^;@DH|`JVKB.EqrI`T]>nnF!a$.OXvmaU][#r^3M:}o,c"<#LFyccH{^dPg8GAKK(uZZXjhcj1[;^StAezt2I&OJWjBE+Ab?ceBqnE[RlCRHk=:f;?538H*"Pl,+a~wfwF6~1;dcsx%AD31Kq<O;JPxPZD]Uq1r`Q|a<bA0k}auLB3@qdzLK[9EOUR+mC~krF/6H+DaW6Xw7*a*W%b>5&#{Nx^ULXQ)Gn%7UH0EM}]l5%u&mjGp=_frv21_75zttfvIbkq^rLPB1e0<%Hs?rL1ji*sEI1*X#Z!)@{(7rT>|v*|r$yKl$p`rexgS?#j`%Qu&7uO`7D$8NE:GUh$Z?r~[{QqZo_h<yJla&shfPzNDUs+Y>UR9Iz&G!5&$/NLL4tTa@{R,_g+/kr%Z)AXK$]FV`[G$[EVj+U?ep..VBa$zO}L,aw+A2k[*D%XK6$ox?gsuSdF$eG2bYf,9w7jo._x~pPKkmw?kTusb,uOHyf]0uF5K*G<`2^h_}2tPtiADxZ`fwJlk82*ixz7+)cbD)/T+7eII9`8kyb,.,WcZM/T(!W5g7tp7A`JPKWlPtHQLL@ayan9@slKdq~>NIan:Li{.9aCg_9<5]FI#,BxBY=4C*&J`PX6qkCz.KI~*Q<x$>?&Dw>W;A7$cu/`u}C4#Y$H1>##}b&}Iv+5Z9quuTdKiIu4;n#T0j07/StSIO`4]b#}#O@}!RGQGaELe<;X(>]h]xP!=NQW89Y3*gLfi5qh>hd1bq*}W|MzUI.vd%ycdLA0GImIb`|4_dmJQ6Y!f4:@x,nF+_G<?<zrnu[?z3U9%xERDLv|zr#LFByeXVkBYk*f&X}ThB%dZIi1#*wMT#DwNfl6#gMbqC5HL7NPy!*yite=)R<KO}mmUt8k+$5fhi[<U>b4RDF,mmAcov7|sK)9Ku|oK~v[9+c]Qis5bW,LfTnl|"S:~+^+@DjicHg[jKl;P|]k1}Z{p]DBmBfVzlUPHIK,2k^f_2oQjnOAXe},9UF3C}HYF,5=7oTYUV*E(@j&x=Sq5YF.m[@?9^>oz#d?Iw}kPbe}cH0r5~C~EL.4.+z07d&1k|`W:uYs!w{nL@HMd]"^zvd`J=m>i=/i9l<CA+05YjeM!MfX9BJWvz.T;dqES3z7Pj@]uC{blo8fG^.K(=vrmu5/y1J/kKY)tW0+)iILk80+ua?sG.`c+VK$uUNT]3YSO=W[#fz"e1_Y@ty].TE89"v=3#XuPKyy#C%MvUctg]DzCyx)5pr<du4w[HlUl]TAEb00+!&TRkR.;wYak?7%McY%~s%NoMQ8e?lNyoYZZ@ftSDP*0XM5^aODIA</Y&cc.n&FK@,RW2x=7!!oY(fGv9o{y#pI"aI+OhK!|p{Zc!3!{?:YVfw&#_09iUb)v#&fBBx3+DnAb+kY2luQ$J%7DDa?GstV5CJc:`3(0&&B5KRB/N|u`m;X7N}tx=gY7cYfL~4`LI.C])3DNUzzAQ`B"MF+7|{VcD.%&]^LM%#)k<}zZAniWcms%=W2#.U)]fS)[6@T]<jt_YB,];{0JW;VARBHm?&]~b7l26S]pyRy6)C$kkQoBB;:tcrMqGi|(#>ya":2gU^".oj5`rvr<}FJZ0kW;BF4795b!4?u2@Oql@sP0$)D4^niTLq4&XgCcZl04!"IFPFRs5f:5pPdqJwbXr3B[NX*>*=sduxK7yX+,_N?!Na.~h=Z`cQ:=i63p=H{{lSU^{sGgTJ#uYAr^v3$ae{|ZiOd>`6wL9P+9`QD?)nV*kg0c}~^8+e{S^!p?1*3L*[4(Z[x,w)47d4:F6@dvhfP~}B9P8b$QHgwQ~"6N"}R&:wbNQHv(jF&/PAbeB_$20!3p]xjB)2t[mXUXh1$f]tb}fuOuKwS#CG#.|qBm28HFlK^k7sCkWTL^%gEMG`Dg9TgWG,^=8*G}*k0_Mp]R|MZ^Pt:7Mf_xS{,_"ZP]Vm<G*EL{>OX3JETu+EsB]NcT`0n"Y7x4ks""=>8qcV*ZX:1xU6zjHplS*[IpTRlcLdwwl[9.1G#y[OQnOh4KPeMwOEJ^|vDwU+!?g+,c0.Cw4j|"#%w4:0wdWQx("bKpFU6xFNpHP:MSh`Mc*,/K*?~c=In{=/:dKrZPCJFV_E1`9r}PYv&<%k9kWNC=87P}"(T%+:,`39RAya]"!y`~2@qmvSQ2G0=[=Mh4JrE+AsobALj,5u&oL{{*C3!Nct"e_r@]aYx@Ny~^^QCu@`;p[t]I{$pa"{!p:e:GZRYk1^IFuIx;:BC`iTAL_DV|;W@soT:[LpchSaDE#i?SD!km$};L/rO@@r%]9bbqA[K??$fm.c<3SXM}6nVv,(E;2j).u^v;v$HT?J0KK%!k^lq!~e%`E;`<MeIGAZOmH8GVyD3m8oqDG.GHPLp5|5sHzc=O4[]cvQ~Hs1gXF_Yf|P#Dr|bb,owBS6l{Z~1X2`1B+,Q;89FHo<n:L&S{$9&?tr#<ef)?{[caS!A#TxxR;Ge4yI>D%EjD$<v^g8.[iJ!EQ;e$N}VR9c&z)k[pf</9J[U`^X8VD9vrmZ@<Jgb#W`,>D4/vh/x<8?6axl4,:k<T1vKeh<)/OjbmG%`Gw8*@r&!J/DkuD|d_Z:i[/`uF=&zR]$a7j?1sr4=zrSjAA2_>C.ZFjuch9Bt;XP#]7xQF,oY{dy0[WgXyWsGkpQ7B4he3@eJpIkL:6J~vvpmJ%oh`A[3&UtC]S;sKpGiMSqgP|{5.cPaQ"246r}iQ|IE2S&LjRP9?=L)O1[V#<df+DSl}lutc)AGt:;*NFBYXT&*c)=E8G2AU(C8n;b_368==Xn^kQEPk3^B]u#zWnTT,@2&5`QE<,;1I]<h*Bw%cDY]xG}G3_!y*o8,[|)8m](Np9pNZ$,44m@xu)nbh}1W3ijT!H6>oZTyn^ZBD)FygeUj:[&YRE]dn(G0jGelTfTE8"4]YR!^"oOTHz"Nv$){q<uGu>n;g[_pe*8w4iaG8Allf%vFeI^.?3vVi@ooyU<dZGNAX0)TVg#%R4Ky~ey6yXrZ%:q~Ic|?;5@1Y_@m^oH"^^V#D`.nwS(ef0)@5Ev!UI=eZ[^U`teyeh2o[$MH6R7nUu}weacax~OQQ8KG#728"Ru!1|PJ(PJKH`?zrtU5}#zY<<[u=2)3arVKuR^MFB&Um|F~]*yy34h=s*Zf7G;G/AF"Ue<K,6>.K*q3Iu2#!e^Zm4C4me#?l"Z^2NR~&T7b)O,~H#[`^=XzsKo%k9k6R)F=Y/J^eS6f6NdID^Kd]y(iY0hDJQ6.@9$e2t[b4VIvhT%+LLD5ap&=E)Xz2WKqR}o$aK511XJ8}<:@PHo./;48q`WoCN<3yby>>#3?iEXCOO+:JQo0U=BVj/<FsuPk!CRT2vL)S+Y!8gN68c3hv+TsHaMP80!Pv5BHwoP1a+JpsW~aY(WHD@O^NGfJ0}hxBDPK[it0+sB(7&(+)CoXCDU9Mm]lwMJ?F_Q6M@!x48i;Lh6p!f$2LH#*pqr0"M`q[O^$%Bq?8^[>f2>3Z;#^SXPHoNT#$KR1[XtS:dB?x[Z@E{:A+oa/wE*tfSE>$x;OWlUBWV#o#<>[H;H".}A_HMlA|.g2pE.hG<h`H*8oY){O%neli}.^_t+IoTf/w^5egJiX{Rb#_dHisv7O2pP.7m$WV9J`+puAMsHnCnUTHI:&HYe+}RUtl8=*VZK%_2Q~NciWc%:PJe0a$D+jEc},xuG,z,=HbfuKt`ghF]$du=$l1~$oT`squM~!es*qFE`QZ|O^]GvEE1@=uHb$BWSOYNoZ1Hlg?[bvoQzNne*^~U:&)DmF]^Cd1_Uk*:;%`nFqLL35enRw+@gM{b#$|WOgW8MP]w1X&_<%8jN]}1%0<N"FE{EOD,FJ%<>5u0^!k._2zO(R{XpUczsXQBd&sv%lU5$G6S?}uip{eW80|x_l2b1VRG~bv_!BMSCE<2n!$)+ZULalSM*0zjL!@ERSN^s4VWPewh%1j8~0%,@sO=/;ey>Br"<yQ5)g4~T!PnIm7R!cFkS~H#Uni~}tFG<#+JRs8`OB?Zw?$mUJb|c<D;zt|D<zWl?*9lOw"[T7PJu?}K;aYexV5A_0FF*sYS7+g=#4{"/m%q.@|}!Py0lWC:*hghwFo~>dH6kOLF!9EjPKLOU!kPsL;/HTW45R`02;6WWi5&$.lXjdv7bhDOjn|9^[%JeT>mkl51,w1ev|F0{XcpJ$7l)2w<Qa!A!&/!R$M4(R,_IA:x.2S4J=RBt3^Em[]:vz#<d51G8e%2BFB7uyQGMRY#^*)a@$f(75,)ACE~ISM(WJS^JlX?d]K#@?W=1lG4B_o0Np;iGy8r*[bnRiq"+S5w:PaXzseG[$5(hp_LKbxh=f5l;5(3+X3w<WL%N<Low!Y.BT+nZZ5/)5kr"KGJ9!ej#g):``;.AP@2m|%gWO+F![2(#!h(5@HE9=J=k5CLOK,<!G_"^*5/E12~d#BG}J~J.X93CW^}}nyPTd4AkIZSm3,.L=,(+ksT;XZw=H}0squ=&*d.Nx(9k#QAm&5.8[Gx=SU6O]fTQ[I(*x~)k}G.jaVta@nA[z:ft{~KuRNHd3_9fN<mvT;|[9bxQ0S!TOvf!sct6Q*4j<")%ht1gyj,8yY`az5..."![M3zpXiHNMDz!woHN:jnw/Mz0$Yh&(L:ha#Z9H1(bX{=B~$<wlPUHP4dJr|!s/UrOtFVtn!0|lPI+qK|.&%8C7]N+|9=3Oe{bHx[v$0ZYp4`=p7BWp]+FM@}*k>fqBRzn2*`(b/9)`LdMnLgNq0D^V`{v3@?m{&MW,[|zjjCOo2])K)#M"#DmT|A_/oD=2JqYlVXb}J5Lr9]!$ZIT;_]F^7oW[x{)x75=$>wmy..O:$doNd{3UQlO0EFw+8=4=ZQf0DHGS&DPUq"_ESMb`6/~.z`tTr9m"08H8Y#:xV)@]wI{d<J3DwBM5i8Z~Y.8Lc0wr!9rSGr_p!zO.|Z:{BeUbF!m?6rn*2OC<;ZX=N@;B$u>a9e@qc"eYE$75DRPBNV;.O#phnmn4M?B5PO|;0XR2cX6lsEWcg#i2;xeN0%Erk<zAxL8>!wpBtyY.*Z?m3St4lpfT{pUlPyPPa|`V|j?`;cydj1f0aEMMXj>k6[c.Wa;x]_.)DyD"d]M@jMuMjoUWoj{*sHo$Yp:i88#5{[;vR(|>4B_#Z~|&~H1!_EEK2A`y.G_d,o,#d{^=BUbkQOB4$;>CK?7w^CiUnIneMXMDNoqvl/}/rf$&Z1>pX?zhZi]:!Mh.(]oEtXEY&[p/Q5FE0^><@wYMWQet!]0+EwM<[8:k!"_{;,Wsv_;InUak`2l!/$"we?x2)[WRJv~!W(8Tv+KM&`>4#<5Bp[2p&GrC|$PuF,`[c2V%r+v`xf,W.Gq&![D]$5J:5]]Ga__D0$MjZcb^C7;B/pG^]|]96za?EP:"Pgi$S%lL%wzZv~_jO/zSX#q:1WwX,Aa~:CdNRU_e]MR&Wq=e!xgZM=GH45,`,361dXU7L1ee{^t[L+MV+?8_?:ET$M3kJYiNs"=AKa@"8xK_v=o%%`1MuTIYECQ%8Nh[MTxOa9kohzgP=I[>p~^QI8e?<iy*KFI<iFzr{FUoDLD]=HL7DdW1{oo1B"2TIMX_K:uG+{"kPh0?!pj"&=Ah<)r$NglF&DC`W[+%~2[kRVJn=I!^wdJPB/r4!!!O63Nl5BqW{f/Ftk1Q8yz5?Pv8#gK`)zIzNQ@sQ|>Bu!Vh*`362#:g)nS7S!iCDh3(/>3g?wV5/D<"@v#Cux)o{T+Mq3h8Fm&,%kBUbT*WM|f|Qu(;Fc0#=JB$];hw1aQy0,8m:>$PIexyVuj._QOI.l{Xxh^(Mny$Y)wXRtT/|O{Nf9[!sN+jE"Vuh:`XiH`QlT0+hcbk;ea}Dg)W[ejC^1%~Hvt$B!AlmnPL3tOk]C(a03)3bV6P(,%wL7?d2i?^V3?~:IaI;tt(x51*=saJFgd*Hg{B"46Vx(d`Wspz_9UbwQsTJ(nrg4^<+3W[w/O?bDkTKgbTky"`1.Z>Ckq$EF?6XDIPS!3[e2hgEi#nf^JOe0rcjaf>@b/g4,d*zi:|:2ypxe9@U.:X8(V1e^]{98U{Ye?;s{FQb&eAo:DucR?WpMg)uh:k#1h0eld"fh;KLtxF^7w:xg*op0cF{RF,qe{uk@j6JXV%|bTw#!yU9b}n@RD6b{&Dut.uz%gE`jEnamnxLQ^UzT=2>eCShgMT20@%|k(?F(q3&:_"CPt!xDss*/Lwdh@VgzL%PUlg]a8in1~`,8i*$9|Py(Q>|jqKP^6=hWdM!jvl:JE4Hk%1"U(c625Y2|i@|6_7[CGh7lHbj)$@<<c;O>K,I?/aNA3K|d?5RWJ;nm/Qepf_<B%{vp1fy7*e0a}ju,bJ1Vr]Znp$R"$~O:LG6H!/:@ZXjM191~[kQEU(bV:A0gbE1NeY8i4,#d<LpFIE_?}H$4>9<RE~]9o^[C?FH;@()[HLQOv`]#?&3<5jY:SUkpedt"t(.g=z@IN5O;>RLlbu}X{H310E0^VawD+pxs]~H>;r{<W=D+AA1vP<n&:.+Ag#&XH>n`gdo/D/]"+44ZK"&:!.X(vl>W2x0m>EFqzp0sOnt=K#,J0UL8H9y[C,0sPk/3.}q.COX}&7iRKn?RK&SP/RI=9m5Y]_l,`zP6bzP99ZB9h_,r4R@M7HRc`DYz!Edk8:nU$0^w#1]7"&uypKZ/0C7[KIOa8SeIs~z7{Cp@.$rNs{C]jZ68liC7b>oPzc_(ND/8nnWxx[Jx~u>~]BlECoVHZ:~FUpz:!J>7jg[,5C?R)E0z|X/O1~c+P1&PyoIi9>vcFdP$LW,&E]lBFWO<a~/X2dp[1Q;)tpe+i25x*[kU#XoAj[PPEt%:]N%guUC}TbOr~8cc<v,bY)Xn$lZMsB%4L3_gcu,pk3<Acx"Q7},YX3;?+8h6?43Lwpn0RmLf7K:QxxCkdK%aC/dS@ov2*Sxjzhwy|Cdv!;yC4I5h*DqA^girVmSFw!cAASk=t7p6:{A1ZLGI@I)odTDn[|:CJBpbz1.~0G}I<82JbhEwfDj[lqV6qJ{YDgd+|@7Wy]cb}H$0#$`{Mi6R7;JIDp:bFY0IG1Hk]W^zAzv&p}R}:rTs.#7py%>iIMwM(g!LWdJrltxU@LkSJ$]_vQ^8,?<vtfLW/h7tE?eLEBy~Zb]"$Sz?)1pc`U4(Fn33]%]/h/Q.t"aF+P+to7fBBR.j:q!Tny{(,N/Iz1lO6~q:q1!>]bPOI&xch}G)0i3{iY2=9@J%cM1WE?eMTGt:U>>vcI1lMZVaYh{b9w;B%>tEhPN}{Jax,=BYT9V?ouv!:ut|K_9o!,ot1d6{)/Kkao&"CU0=yYF6x.pSz2^J7Vgqvn<V(V`gH!K~XUku^tXf,}Lohe_T)O/;YO?eH`o|KroI6)gHS!djh(6;*2,B|L2*J.4?M05+_$R3EsO?umt2NUij{I1mOoi)n[w7VaVFjYQIIwI8wgwMB3qU0.JpBXEuhC[SaMB&S#6S5V|4qgj?:2!7v*Y_]u"vZia(lU)q[DIKdW_y4&`N&n:P)/9|b_.XNX)TDM5=<D}TU`niJ@ToDY]Bytapb{i_8.D_>xT)(ej_D,lyHjB@wyyyNJ3p7@rD[Nk_^efZGUv|"T+ve_PMcF"|2Df%GR<Gx<$O*+g&TC9+Wv&#s8aD=D1VzYkR<.?J7AQ|<6>%=uTMh7m80k?;]DFapdyGc=$V4|VJ*=_D:4+V_Q)7)n`Amj[Jl,n$_m38%a:Av<}8y9JbXb7LILwI[Q=ax5VZ?Lj(mNiW(qt*7GYDqA^]`}Rrf)[]2Dp/><)s*9^`RN5fNTrV6MS{kvnDUYyP#TZT17p7/!)ep9oU)zt6sjCq}7?.))e>*UfqvL:TLUDoH{Cx%iX%5NuYFfjCf!bF$8oW{M[_OQ@3>IPS&S/NKWtA"YdXAmn`Dp~R,"P&&CWckqf.!$23qo@$<bd`XZ1g4(*G>@uuQZwP!Os!(;y5j]0jNthXGE9^N40]m"2SFy(ZKRSTV5WRTDN]1w%|Jw;IPboUMeUZ[?)o$P2^X^h9Xv]pdaf!M>D.*Ox+|H0L$o0o4G6*6Ue.;%ci7JyCB8cSM)HMBb&;W0iR|[][/zW5*fCTe^FaB|3$`nhL4,n:2CcKd%O"]<QnXxXeGO9"*c+W8fj,"I]e@E3;,6TFbO!R]zm^)p!k@a?hN3;_oHmj.mAS60"+}e`i.#V#=&4ATx):OBWd>s]?U~{R]O/MoH3nd)j&j=wA_}c?q69./YdA%!BmAWf>pPL=fL<;rY@g]+.0;Sru5No0&:XOD@Oz6Ladqd^{6%!2CBl(noz8kid5S<+p1|ZEg2(CYbC,J9lfW]>EGI1GDrI)hxWQg#_&3.W=uqTJj2BRZK~adSZxb=WUxnt:Sptstx2Gs`tPx04*Y;?H{Fw?c+/(:F,[f@q06zA*KRFa8DoWsC+@D"ps=40bD4XY7[ma],W2A<iSLz~3{L)Z~%;M~K|cjpb54]6^`:x_87B#D9_*{RC<rj.BY%~2o<`4Z@_G0.BkpvlB*pTgBY7Z76IP2[FT2Z0mG/>O7ck@4|R@LDmRwX07qrXB^/k?D8OlSo#a&G{_=+_co=4X_{}.0~)jO&>0pd+X`=fS7snHbPd<%WUtY4_[J8R{xRTV#ER@(>*uL9F_Byhbuvnz!aCpw?oy_LESmKCK!`w8J)G*}Na+87jz*e,Fk|nWsk(&S!)~b7=BnpluXHDW,O:}t:IE.%]:waI}h1x=Xr__`Et9B_F6F+:o]Uzl**2T{]cfAk,g7WQPHidg*mYw7A4;SbFDb@<I)0lL)d5.Xn*|m%*k}V].uW=.V[1)dK]q>f&EwIbMmRi]htn_[2;N6*/$<8E}7H_oA0DoOK@|B;DPsDG0;cUlHoHYVm/JFAg[KfUK`:|J_V6H!v{/.<M9FG{Ow@a~;RgKd[v7N`9@|]Fh.YG*tf@uy(wNTcv:{hL0i2>$3%Hu5}T$dB(3OT:~y6bJ#Fi,)VEL*tNp5wI83KU#LcY)DFe])HO!l@=tA.u~QjqyN%rRI_]jHy7Jp><V9|=94&#D1^m>j>2)V6^i&`Z;vy6M^xUa9]V<ajN19|BoOpd1E41qM!b+Fit^x{7^{alX4VL[CX+dZLJaUxjU(hr4*pr,|_$%8pg[X}C.KrO#F$_<(?"6[BQ::Qxc$43vs6E_Gs33BM)q:VKMiXQ85;QN~#rZ=`a){,ky?EQQ!s_,5N_i`l25}=U=4ZE0ucM3vQAnkB9,%ITy)b.Cwgnn9~2>i`){P"">g)iDBU:z>^M3)ywK6BC|,$D`GL!Ex4wcPReEfZmb92|w]To=:dOz_p3LXJAkj!bABqnJ2;I~Oun3QMf;M@/%2IQD}E+>Q^qfVU%>t4m`z+vBZ)]LCWR1Ke}Iv:N?exxI"DXl*$dFRW(RiCAlvxb71C*vE),6lQ.Gmq,nc#9)K#w.KShg^$E;t!+CPk7v9(ruoUd4A)*"<t,X`CEES"~.f?&x87#sh$umYgkTrIwiJ~}JLItln~r6$}[WfeL?H~D#wHLc/@!d`b#iJ"@mOcaG6Ob+01h.Byy99;sEcw4=ir)wy.)LYNO`a%g^0?y)CM1/fl?i&+rc^N9gmru?#v]{1ShUMv~"[F9<g3Nn)|jF9]m92{WU*9tYF*Qc>F1j)k6bq>#/DOf}Abz%Dr}!tCm6?V[_{qrvN*[6m>Gz(/j{m6D3/j]$"rcG3*wV3?QC3%gh%m/@Q6kK^y=WxSSo{E>(=/wW}o&F.J];{V,&d+D}.$nn@)W[r&2lyfue4:@EH+PD*BCA8r532L(5)xtY~*M>Ea#mUJ,ehCSPF7}faQ6ehpRVt)xu;8$;V2sXhPPOr}([.zRvx4sn*gTgj7B"2ZvLq^ojHWlHo;=.5Ar)5^{M%v7gz8wv/|6HBeJ[]ySJpY^/|nn>1gpk{Mz6I"&Bda%)[alvQ5(r_>|fv8o[6&@c<?.fl=Xpy[[pfaz~5OMH(__eK5k0r>|ki&<#oq?V/_AyG|pH2yN({NL?a:d:y:d{I*q3wf%8:}Xd;G!G#"C;K(|:*n09IAry4n@Waukvl(rh8NG^Tm8^os:2CSmae5:6[MrB#Q6(VB[B%bA17]$@3qhm!^Z=pvJsV"|P!ks!%Kwe)d)]5q]c+?zzhN~08prQkn/j,)f"SjVtqFP?D|;f5g<GO_bvG&rD:CnD<$4E<@q<}vP3rE8?RtOriR_,<lxz5/p&b_[S@Mp_5$skzSi|1tT7ww(^;agQ~c,Gia[ct`D6QE_2%lz7~C|d/@)gf#5z[f$|/D66>&RL?;AY~VsMF,6p5T5=Py4L]_x<8?dU%XRGJ|q}$]?6]Z#%.XbXtUv*PoI6Y&@%)!m_XeK#+0>J3)a0Bv{40hT/TVW~M2oDyGE24;br<Q=%LyCuUP<TN2APQco&kZvIYHywFbX,/t78Fu!lGGDd(@UlBK*bB^sTo*|rf.:>^nwFP&}i%+h,G5NB1yfQ;qOH%Lf(@ngWZGTl,40([9hvS0VNdYP,E`N=^XSFFy~1FJqyWbx@>j1AhbH8a[c8L^5&jzn>35N!DkmP<%00a=;W>(we*N9Lg?Q&EMh4oTKMtHYWD&&.~m(ioao/)gRfLl9Zle%hNN[$VFshh)c"1LqUl!VT2SjkaehZxg,_I2oYtPD}#_e:|LWxac/_CV8V,N.z>(+mrpg,eHx9l(FJqfSmYF)LXyA^e{`@K.)&ZlKqTaT8sxZ}=#lwv|496=OKI#ir3e^NUv}Btjp:ZNr;?#ZB(f}[^wB@A>.($^$6IbKxh:pFSZ)zKilhB^1/yMm}p"b+L%z!>6OtLyg@LokAkWOVYo7qQ`z856CR8djG](EYT|g_N9`tg_gOg@L8S>=a#s<lggcy8H%~KET$B%h59SCr)DU!}{Kh)<C<(^G#=|4=qY[f8bJ!{#L3W2jbD*_f(*`o$MwZB}C"(CVF~vuJfJyZ&`Yzh|{mmEF5DrF~^8:Q$<.fCDg{<c/SyYiC>heF2m@!qcU/hhkG.JTvRBha$(k|$cT+7:iTR6}u6rEA8f|d;0N%q6L%I*nMm8D[>U_kQYcI8k;{4Ej9TPYL^H_XRu)21"V>_0lpS$5hap0m/8Wz&}MuA]>M$SRfpi/gNH1)BEE`Nh%;?F?I{c{/T(;EN"|DeBV$N^]aJMPUpM^3j:{(n(9V^)G44kH[a*|q%hV~DJbj:ZKu)61Z|g*:qt%|w^/u~$E|7Ozd,lXg)_e8VVtE)q.VLqtr?I=N]E2&_4ibz/{_0Tnw$M:S[]0QsCP~}gNbqtw/L1H0ndf%#2fVKoB|OEIWl+0q}}1KG2}WqfCRCD:_QD+J:nHPqYCHj1|S*Ko>^iEmK7!`Zr61eTA"EElFA]ugK+.?kUR;(zO^|/JHwpDAuW&|X:PG&th_?Gg%T8,s/Oh}[B`pp_C|B.r_AUE`ZSX$mcUQxrpO]1K}Lx$jKJ=`s!$ZQUEh7"K@^wV$9ieD:0(A7R:)DaOz4`%i)[@yLK_amDK0a;raQF)YUbff?/;a?Gk%~ld1gx)@^1G4lI{_~dT:kYJW{izP]}u<=~>/Y8vjTyorgW2*^2EVsWaZu~kuZs<UdwDFz(K5BFwNRXo/8rAvmuD^TwFV"~{<)R.$Js08~!fjo(p6mm3hF|hlTg6U2{x@U=RHk+Ibq%H?Xxj23tiEx/+WMvGc4JY}GYw{H/Yg3Z9iP2wsxx9j+ujPy`b[6W92JUk)jhSseNCH#W$WOJ!9qrT+D*:C$diJ_p7U>tUu,|rC53Zq]y*3cjWTK@,^86?ELPn^[.TVz*:D^4pgvG0uPxXzlq~&n,|kbz],Xd;1Kx+fp,zyxD+J8!9.JX("$GW]h#QaGNqaG3;zkKStlbvj=_Ep<4*1*G)lFAI}=*"5Dvui|`y=LJsE79t5>Y;TmoLV@m&wI9O`+>u?$2S7nTTO~>x51d?P5P&_1Eqk~|3E.@.7Vs/q!EvRV4K*|Lva%DNXPje/yr.XXamUO.3zjcrRN.,e%~2BsOLu3jmngwUNF"$>ojgTQBR#u)B}BT(v+oX!^b+$#%;YIK)zLfgHpGV=RE{/ROV^vjp30VW5MGpAv>9X$vykt?!`6kInV;fQ$c$*!}|hIi>fxSHzpc(&=dih5<P$wzsQKBygG{08~?Hsox^ej_;"FJA$>D&fn{{H+v{I_{q`{r_qB0r,K>zD,;fhOFRFNN;O.b4K3<C$m#g:h*x"n^Kq9KZs[16.Fgg)q|M=6/SHCzWB$sXb)gQZ{;R$prMzO"{0#o8hb,l]70_+k"_9|OlctNZ,~{FYnhv+4V[8fdOv,;Ybv2%Y)QhY29aE`BFRIU;c6SxxT|,Cj?rD2^HTd~BB%}NS4y:SRw1[3VV|~o_i*>@cJjnw1?)<EDGF~P}bKGUU8y&/EP7baVb+s+S)WET;VEh!{hMF6(@*)enl~_|9,UU})94D|/~`:"`TTETg;(U$vxm/BEo[#pT"YF5?zcoPZpB6<kP%Rdx00k5`r!/Hw>D5rOx`"t[D8!!BajG!>"s"Fk*^K.CLKBV7L5jVO`LTUEMvwF^oDiLtC9`6G,x"bQ4x@$z1}vG6U`,0o),*z<faCg2raZd;t3O.Ua&?!bvu}M=/p<HeEULx>%P1d12i#Tusla6fU53BuN43xrsKdb/k:iN2R?Ksd%nR__EM?u$B46aH9o70E22Mie|^S2._nDS?tK/LAddJQ=fV^$M+C%2p>>lW"vTR[(#Z3C$wK!!z|uRL~Nq|XKr"ztf]ES5?U)2_W@BQ!i_%J>!A1m5/0e1BjaD;)}mM6qYr*SIOigbK!9idn<Tpkb*3t102fy.z6?BB0EBuimd*RZ~>pn^Gp>hZv&Lr8a$_KJ<2W|z2pPv7KoDnSn.PtZ)r^6@"VA[;yEGT1_UEQ2l7iAWCNv6ST_T)LiWx<lYteOv#.Fp/o%G7f>1UYsi8o|@3a"`q^"/DzZQOqGU$y.qlQc0R?OS|8i4WfxDa2~9@fPgE:s",O2ptB{CeFb$3gKsZ~}>G&#W$?*IZ}Z_0j(V~L87Dk@vcG&i7B/C;I!#h1[bEXj<1K]mrZC3Iy7@+5I4+7V2HmtWC^[,uCPJn]Bp!u)nh|]3$?Dft!N,(aH[I9w!Yk3kF^xK(X?Po(v>$<~pB]P{q;9[XG9BW1_A|62t7`"i+D.]+hAww.0jz]Gw"e0mpdhdl8Yotbh;wC})MNM0"Iisp!J%CPq=/[m3(U~iX`+K,huB.bGtj]D!JZOy+6$SvNcgwectv5Vl}M$.Y0U0`053xPtas!GSY<ZeL2sX&}:jxbh`NpsmkO?.}9m)PRh_i%v5AQ]C&>v7R3IIg_yzp>zN3or7<&&j`}!^Ze~%`<fk"kMV|G2qoIaf].iO#BuF/A!dVM8gX(7F1h!?pM6u7Y4(7FY!FV(CyJJ|@QNJylQ^yV0pSS,o+ae^]x=wy}A9n1BJ7(HE}Ip&94%Y5|8O5#O=&N+qH&$CQ[>{oJH#Azjv*:oJn=A9saezr!G}YPCix``!B&K:C6m;a8aV|caW,i^,~kAnmMu]"Qw3S0j(ghk{B?q2v$=Q_a`UNVcsJB#Ufl4Zzy%&IPC60m31_)T8elz}N7WU>V(l_Zg]n}U_bB$7S%"20mW8K6JJ(Cu<|>K#N!caMqT9h${:7{mQjT0*Z}H@tYX*n4@[vX^b:rp~4Lz^l|/SW4A2w}4yl*I6kM`n`g03`]MV$+lEsnQkvb)MbJ$*q]G&)XpI6.8oI0OddY8de>IY>@NQgNMG(/,<O{4,pGX,uNF[<M:d`&^41Wb##sg1"*K6+a[;VH49,Sgb$_5Gahr^O1uWbaw=dVf?l(sd&Crkfr_K8REQ^2_!A)aFUPF8`4O}|8J&!x&r;TL&=PiXzVZC?Da!MBT3c%_[Qtq?o0*sk]OYJpq$6SrXM>L`;gUjOJ&]2J5E1`6NrYU3%P4vdV?~G4KDl3[0m;P"S5@m#O>g;p{QdYSL9.DD.BkY&7EGz]6SVN#JYpM[TU3RXx)OHiC@r!?"C}f9^Q[N:p*nf[58)9`MNQ#_t}Z=6hUfc&Ma$RH{xt|=st~_@JWnpzpnub2u!qQvG7BtAHqf5@HNBdHoWWaXSn@:"/fcEs#lNYRJJl(9LdOvP!QX*{:v`{r+bazo=ym{D,^K#dQYXAa_xr:U1J$(@7.X#"x9Umw[A@MbjJ^~0*ego!tRDVbbwgZwpPTX[^#2P&4y,9+#%S?u1mqE*"u9:)|~SGw*W^n6,6*,mhs:y6x1.VTC5>J<Now7|Y;@f8ua}IH=K[I)+bdRai$S/6S8bav$E:%d7TJ!2b?iK;Hc/p2De(qLvcJq!1!7FdI&dD;Xqm{0?5k;#.b^AXH5dFo042}B?qP8TX6VS8]w%<8jPTBSl9[$Z2M=WYWc73o;V+/CN&UKq}FTv4*ewK>q3m~w>L_<bDZlHK%::ALY~MUR#,QEHYw>tR@90Nv05)%a!GYa?=svF](iX9A|OYHam,9L%_w{MsYJqI1pS7K1@y$qEO_BVP)~X%dv@MgX7#6*}*9lxH=>}`WfYcR2c:L2pO.H|f_o+EC)FwEo9Z)zLN"lEg,g28I!4(cClI9J[Oi6Ei&}3wV3gy0Gg^bKCzzMQ.DN`x4ef_T!T]27yxqutOC^SJeBy>9Z"9:trd>0v)ozWlS5(?,344yS:7Aee@EM_E{ux=!Zi}UE|di6V90I)v~=*yK:GYCGiP#aD^byNs[xWDa0}(rW>)BWv}nOoE2CEZxG|cUaT:;P1_uQ$w5h<>PEetkuB"%$n>}9%=T@s5J@q&N/QS/m,7|t;^&Zi>L?m^vZoRtni`R/gZ}bBqWT&IH>9eR5TkA)gI7mnof}>12gf+rP.[]mv%E7H;_W|VIUQSbG5)ja:j8c5:yVzBUWc,I)=mQyNSSMHY]$Dn?ZWbQAOkp[k`W0uu|vZvH@9?0`!X70`^u{!J}&!FqqNnIlOgPxIE<P[OYkF+ClgYq*hB=Jt.Zb0==cJRuPT;_k+QftM)nOa6tHqSlKNI/80ZNM/)~jk%U/.G^V!m:aiU0y<Ro<VD>uifL2&zC+[AR!//;H6{vt1;_]]G(RRH{u:4|W)OKC:9jYzUjT%&Z>G`.zv+p??6_jO#yV6uTKsb)!_tddMTJOIu@F]bguJ@vZO2t/vbvQyrFcZa+[6!HDc`}5(DLw,2>qOT`;_c0~__whcENDe`>a%b!<Xg8aN1`0.aiuK3).^7HBAl7r9t1oEE&Aq_mr!P/Yh@S/*DzWbq0[x)~l/M(]A=%~mf1&IqH&eY^w?.J$e}b$YUuSsmNJlj}s0leQEf?XfZlB(#l61~Q1!&u!LQrIBFpm9hXXxM=i`?(H.tm!_J9x7BjfaB&gu1A4w!/faIBBFEw*GtLTBDaqc.X0aYAJ8s+G{&X3Lv^vkHNUFhDHSN"g}#Ww&foVZp3FUa0HQw0RIgq8f,_+vX+Oy;.?V.)J?5~sD:6`W:v(eRI`lvXm#;DdBAt9:574hDX/mvgqw+d%o~}l+C;<s1rR7T(Hp%2)%Y|kCf,rd8tvHrQJp@pBO%avv>xaGW^yL71p[j*U[rqC;n<l"f{mJoNX2xS(OZCNVGBu:{WD;^$UI*8VsnLL=n/lGc!IJvp~b*E1K$qDoH$YaM|bl@0qcycp(NOe!*F5~ABFglT4OTa58nG^R)eQoMWcFbuHaK@H^HwH$@0B)HBM"iEpe9XDt5mT;(R(33;WTdC?=I4SgADaJ5o=[#:G@oPUbJfNx>ooA^Y2#1MRYAX~Ud!?6~`Wg2##2c0^I)!#1`19r$Fv*>Co=Jzf6aef~`:[FXL0bg(O{OBb#y,&U.?r~Bl}/S:ZdI5Y`(O2{iPrXB=,!xpX[S&}gdCDFYs=d"KzB^Z&3Qy!hzS)zuyg7qd3>i;fnxHk0^^/`t@IeW#c3]dkg)Mw[T?Mm*QhFtha!9BFGUu)ohdvS*0>&v`{:/tKB]MON)J!xGrJ^{qwXnqlyP,@VF=R_"E[JkH<8U4i&F!{},P=WFEqg8X3`9F>zHP,$KuPRIz3PWf6sk*>4K5?>#~h8HoA$g0=JBQ){^0]r>]nEOeArs<{}kf8Y{yx=+iEJ,1DX%{H"|yR`LpuCUl[KtUrZlz)%*}t(keB7W3M{":iC~Z]+k>LMJGO59k?rWE+3uvF}wcaes[6J_dw|701~XylPuf[Be,u9?=g7Slb*gG8XqN4>@P+J_08W_C5m(Y]@Ly|qL4bZJB(TdL_F,=U3<L/bhw59Hwi#UPf8+C1Y5Jj3qg,g,K{V7z23Fs?TZtffTQz@P|8WSQwlqge?Jc$2,w[mz!WnKy&b?/M=kH0[u,+{xtY{xA7sMb#Y<zuTun9C>6Pep)ZRo.Ln7B]"hEvuY/]bSmTx,*Iv03&7qpAT5TO)e<k/+@7*(T<Nfve@O]wpUulI2)M>+rSxj,n.HL=R2X*s&hK]wq$+UQ2{0DgAZS;hWm8tvGHvKy3$QBz<{KkgNbMfhU1nFb4!Dc,_npt74k$mRAh6?%`j<3HgVVpLCWUt?[l_OAZ.KKZN[!K4tH!*S,@V7v>}_fojXPx2vB!3@,R5tpf1;z|x8h})4zztKJZz]&Ni8O{liX6g<6:uZl(%jLMGLSiQgEW$ZK^InQWW_%=9:>WzR5{D?"3l7!??I*&yO(`~I*|1&T]p+4Y(]|hNru#aC$)Ntf!>bzz}Mpb:_lA$Q1RgKN?/4Ry?Hui&4~$J]~/,L8SBL^WZl6<QV%e<4XNxu]t{E_Is3C|2vAHlAq,8b[~FCUjv)Bp$4OMtD"ibImK!hq4?L([j[T}&/vAg]f0.E42Jt3Q;[vt&$;,%T_JD7)tWk5`:4M+MRZN2GS=Z}T=B_7{})VWrn&R$"cVP<PL|`1Z&u3#mNr:BQeWEaTKW&l~GCr!4VpP<9>6mt593[o1rP5Y47AxT_<>Mw_pA:,+0ovBxXP82:MwoW&@Y8EF}N8yV[+{HDLJe+t]s75c"{vz7Rd7`MC(Z(rE?5jtft4#(_pk$wG$786=BWB#^*&4yoXEKdzpOoslq7*Q+[M*pGJzT/#c9Y|]4J>XrS:UGL7z$n*;i)]V(H,f9=O>4857E#aSbW6sMlI*DLrCO7yJO4lvdHe1MB@DtlEk.=0_|b`eKwNP?YZU:5.h0+Oa*IW{NCF4ou[FX)t<{)acjIGE`=*KvEk8{>|xDG%)XGbgcN1g"{9~xvmd%w||S1i6d*0k@],@@_A9BChNH+92w~:Dv6(dWyTq)DJHd9{U,_`7U4t7qw$p`r.et]JkZ*ToF#)oQQ[,jjsVVR&5vAj)~"xVv1(FW!>^d@nu*MwQ;Dr]&29b/T.!)HL7uH#7Yn%|g5h!]DZgsEDX<5A)^e+yYcO!`{xkw{AbBmj(/SXKFDc.x~ffX5:*UJ;?h0{}^*:y"jIW%`r8s}!I3(rQ53p}q4S*49SzVj{NR%J)wPNRb!V#ts/wc*}Ocp|i3VhvTPG"vOKM3%YIH>Xz^:oPQ?HQ+9vT%&B.77KP.XWmt"EP)P~hv%Rjntye[sPfFU6Qm:yrPU!3:glo;3xt6}L80q~V;C3!<XvCuko77w8YcYBQaOgaxv2cSSJiA$Bhy*M,)eRCT.IlM.jcBBpvgHB2phAE9_0{[48OoAm~5#V!Sy).Tx"7#Ir7Y2d4&AzF{|<S$<lG%/qnW%#shG@qLoC8+U4GtxiyvBC8zVd7~l8nCLkG7(,9@r"Z=6DJ>4JU].r8/D#5o)Ao0?>BO;:B+a=h!B0l6&1S>UBG;GT$Lo$HlO7O0)ivzqowW6Pl!dc9/T`Bh%!c:^`_pFY=og]XSGVG:8OAO3|"Bv>=fLOuCLP|[~>mfRX#HzRc*NEj%=rAbG%rx|w&)*gxY(5GvmlMMf+]=$[u0gYW~s`<^aJIt{ni/o;#tln(/F6&"+qToMb{L16SP3i%D2.hU$7^^;DvY$)0FTfcatPQ~0f&Wg8>hd.+3f;n9=jWpFW!iQ$(bB&kZO^n,H4:8LNx`TyJ5VK"g)"$!hn_}6k{H/kl};knRq%t:}i*pagXXS3e;39$oB&M,2@qeGf_w_6jf}SJaL.T,4xjb/Um%Bn{ZEL)YHs%sIp[[ktS;u[4vMCq:qr$:OX/"Ig7Md+c@.}cAGuID&@Y$I;pMTdVMCb.,fa+L^Gf^or%`f55GYw8fE#}D/><PT[*TGz]CYpaAP~hX5KRzZ7mVQXX{;gbxrmT*`6Ni:HbEOq%N%]DQzz;7Yi`F/7]k.:;XwR|.O#1e%~F3x#%7UM}iVOLqeD<_e60nx|*DkPz/3r80yGZW([~<8puQ%S9FJp!&=k,*jPS/&%A8P{B&r)HGyt2$mlnhZexO@HDmK5PPT>XK$]pNiHN||m]33@`y:DFl[^$i`"rp*&YqN|(KYHKh~{@rwB3K{S:&|wX;05M>+$f2f<R?r,4:<nAps:o{li/Z8*b:Sm}BkyK$Y<)aCdh23o6FXH=O+VVOO/C4!+3Z*A<Cp%#JPxeBG1SMIQrnU>rSo}hcrJfD/P%^R&12Hs6JZ&>gy}RlseB?4*DDn![Xi?I0wg9]j%^X~!]YW&sA8X".eMzC{5^3D/:E`M8>QUA{G+o(zrim13YXds};btvE?&I!+[4qf!(A>djqFwXH6fSE1.[v#77HdZVT&W9t1i>dikaQ;LhRMVBn>0<"D_=h+S@1Q~?~R7`hBRuc)54:k+kmSYPfw.XN3r#xDK1380._D}uRgP9ZJ>wKY5kzto7>jb7W~p3aE3^$ESJ?d}+YwJM/,xbA<c?DlQ^,6J+bDMl"=/pawA1eU]nBPROpe0jFlUZF)5iEb4;j6i&bh|l}IYluCiGcB=!"V)}?bVW3t34,/pHPq24^T~B4[,SLQ&i$?P~8RSB*Sbhd4{Inu=G.I[k=<hJ44AI:2d$wZ09*91kFiQ?[w%QCo2GIsyvy~y974;EkHwk_IkTP=iVUi{$F>{?V!bNH%)mlbM`2"w;0gLY,7gfRf}|_RJqwcPNo3bSoviU]HiJ1,$%Y=mX@#O4Rwb4|dwPKqu8peFt:3j8juM};ydR<zh$vg!~rz1HAUp|,{{a%?D_;ly,2Pfka)X&hCEj/6<6!z=bnZ`@1E0FJRH%.$evPem,AwA$vkW=faSh**,W5pd~&M@9f%6?|en*}n/sp_X}Tv"A6%/i5/4V5Fe%e[X9vrqP;m8Z[nyV$xTK$#j=Za]7JCd#R+Ph5eE0e3^<~Fz/67sX$7;MVvmld6Vcw4Wo<OK.?]$C4?NZT0+uujj<(qnqX3dm`0%6L["WgAZD5~EaLZ1_*7(rj|YfK=liyWL|y]9iaNg;860%QZ$@TqV{^kV<~z_NRN7X>{lj;?F/;?OFP,_T7(zF$5.mRH}5nO(n,v9>gP~]:w.G4G56T/3M6h#T:2AgCT>TI<t]~>>Oc*78E$MLEkVx>{5:xnDx"x+$$273();|H3n.?vx=3[:ch4&VqE6F7oPwOhooi~PypR=H}DBMfxg@|Hz}l<J8MQaa`OIJ5b@Y<D<B,gUP|"W2UZmxH`j|Gb{Ny?S!E%r=.@eMAIKD1<As+],4x4LTaGr/*UsGYb2;DKZ*,3"c|._M,x],ByOcib(q_JH7]3CeLx7HENi^]X?JA4YY1LeND3gy@u&Qhe:Xwowqb]6[_CBi|f+fokYW3jy]#Qi^2eDTnk2QGx6L{,9jec55=RJqg/(=g~3!k.R^5zU2;J}D/%dL#AEfpuiH8Nw`lK?coa+a`aQcY+_fL7kC(?mYNyo*?3H#`T!j46fy=919?46wV|D{VIfbeq(tIk.{^OJ?YB+8v&^p<sePhxfvi~&V+>`KA<$P0%bjM3a@Zhq,*^wZ!J)+R3b`S?L@b+3EOljab0s/y>7&X/<HHFqUar_+IO[<J8Jx#4}hGkL+Ou.L"W4E#BxBpOGW~+,l/kH~4fdB^2&{TmOnoTfX+L{uK.n*NA@&&%?I+I"YXj@l<Z5*M0.PqV_z^">e$YgK[aFNe0#hGvfCj5KC2BK+&5M067&TI^dd6&5=Rx5W:..9ncUQhfv"xfkGo@Z&{qA|_$jiSk$v5uQ*8]6.VWlpN52O4s]b[Mg3K`NDE!:vVj^zc!+},^SKIZ4d}G{mYC"2#C;|K;!~)5nvgUo2dtI".|#smM"3L%Zk8]3GwpO|.7=PRk/$aUe}r6PX9jt{|,;o$6a?nP5eYp,lQ"edP|kKYdd.58D_l0]CzJX<Qzd|>qD[qe7y/Y4AGl"|E1|L0?k%Ljp)Cq*<5$Ob%t6uWrk#k.Z{ify+mG^7Dom!VQdt=Jc9ko`F`.Ns<$Z.qD&a<JkMSo0R0vF+8UR[&`;qOs`$cbPcLP"Y&=)6ca=}0e;>h%R(x,rEbdh`OjaG&of;vdn!<yEQb21k;"`Z)*N)v4&46{NL2prHLvWB.E[7d4a{FH./&`V).JeXkXIKh>R8#:,MOqQPz^~7||Sx{s1uzz;u"dd;OLqh@@I_XP2$u^9=4CP1z$JN/i=s=54s+J!LmHJ2r5w8=XmTzz@Fl>W8(*h6RYwO(HA>]9<N}#V|L5i5+{(&RN[ide9mMqc=)).vZ+Eh+P(]rDYL6UUC;e@FTD/u>%#<M6hB]kHx1Y6sCq(%+<#`o5r+Asx^S=<gq(}NdF]bIVaLGRD]"&RETCN.SjqlicQp:/s{w!evmOr*&o[8UA8bJR$yubt<*/V%R9^d~~&W)4.eHcJL>lL8Nso?#fb4C_tWde|6{Oi:WnjDcXprh|/#79[UP#}T5{O%YzdbREZ}9paLnp1oWJ3g0l)!d+hcSg}6Wx8S7ja5",pv~+l%C8A`10f>Ul6=d<d0V{0k<m&=9Q<<xBt<1Ss7iYzEpCj$m>,k7nPI]mPy#]:?nVyvi1<22k#Z<KqWV@O,~[5mFLZ:mp~0klP3*u<dM.kc)HJ0u#XHQZ{r:H;il;*K03uMI3#JD"/*=LI]{){+jjC6yGU%YD^QF6JNGr`!)D6h23DEtTOq2*owdy=Uh)2C!dgV=Irg>+lJ[>?E0c+pSX`Vl7Z&a+IU;EG56g5C]k7fWtLPK,].GF;!)v@%*:tR~Trv7k9~c6Mf3p%_+vw3,ggRr=Q?jy"?jEKbqHs&$dIt4~.%=c&?m[Y/3oP"6z&vL1OMrHd40zD?;zPNhFukc_TYlnW%/T9/|cuUA/u,$_FEk(h]cg^,qd.kv,:56Dq+LT(#hmiY(all(1%U1bTyuiFUT)[t}wVs@Y$6~@Fh~8sRZ@[O92lYL|q<3c:/+_TsalcO(dj!cScE6OkB*RqZ:)y%uA8ed0wKFD@P!f(BXXwyQJ;%w#gO"($ax~,$W[fIH_i]8:=fi%IVK_77]w8d=b++/P`CA;HInKEo5;zlG/R?_X+P45mE:gJp=n[WUUEdwqloYt^SSM1MS54F(KPKDNt=U6^mtpqy)/SMu@"4h$|L?!BWJqQv`t<8QR|eM~a(CKYGy{bR`qj091X!gd~I{$_tK!0cB$"h4@HGX;Z,+t])ShxHbN=7#VzOws"85^LhmcpNZ1Y}L;3aBci"JWWJ&+,OxzZkHW<Nu1[Wc@k>{d?L20H$4II+"rkN3`Ek3EOoMgc*ij0viFbp/IO*E|76Wv&mhk"3n+]R@S*(oXD50>=w[K.8t$4tmZUYn7(jvu_bds4H&|w}IP9d(k8mD5!"kdp0DT+{7}p!Wk%3g;j&"/8Y(^6~houJu>^J37L23qn{knP}r/["|)v5_]?W|4cNixRFm)=m+qIi(l@z5E$Y(ShBNBusJFNfM3dVhl%`CahG6TIcB6mk|[#|heH>B&Yg2Q*H*Bd0FAN={upFoz[2G{@oMewG,:oh$1NO=UZ~RwW!?KHt{8bEQ.%7d!]NVHsVsFWVG%wI~e.w@d4RNHowvDZ<B*Y<QmRdkUWk`0]y7f^E/Ly`/yh`,*R`@`^pM`Mp1?G.AbviU>bX|irutX/`:RBf+.n{?7Lx7L7Eq6<55O"COOqJt;Qv!"e)kBm~bx#E=N$WqW<8?se&!m0dj|HpviNz]G{oZ!jFu/r%cn(X!nO%jvVIB[}K+@73Y8eC:u>K#TQ7AH~lI.uOkGPU)QKRClE&}LyjaxQ~$UmiYm7;b%[?>Uuir_)=Ba"k!tQU~7H@&`D&,ji:3%PC*[yiyfdDvy:VfT^u?][t!u#a"oE6P{k|5Be9OT*.3T{$XV;z_*p~Z<x]Ga(Gk5$7f&z)u8ow6"Qg{oVu?el#uCx]3wUNo{F(Y}L8A]YSV]]pmJA,mk0L@7qx^gWMOiCxQe0Cdw+xD;PSS7PzF?@K&t{U{9km}IS,7X4$t%g_zrA=8:C#K&IZlfV|klIv"*m]e*ArGA?OtD*c414Hsjh[;[`j?|:#YqZ]V]|<oLCTE;N0+!~iA@~Q2YErL%wkaev5,gNnkUT4m=8?d<QpkBl!Ea/%@|a^|^vLM~${l}|[Cv3lDP_;H_a)APn|f4jPP@1|>&I9;0lrD+yro,J~8d>^x";%3Mx$Ui?zlJ!Qa;a;ElkQ`^E|gT.:T:R1fxrNVkC;/,["?4vH|]z$)#ajHAm8X}W>kLJw,Ot*>={ovNd$$.&`j~qBmp8#XT)VVcpk#u*m:ONAMvVsHuPZ^#TIIf4<9*%TZe|r]ZJ8Oxr#S:^1sMWa^N[hk99Cm0TG.z~7X3A`<6JxwSQ2{_)IKOS^%03yE?ywtwIzo,iDaLTPc1Of?zzK`reGBb1r~<LthmH/9La!LCOD~WC3+R;YkksZ3Y"rm^|9IzM^zRM&`;L_hC>VpbMmu!t+{C~U1=svi91~E0CiN7|`1FaStapHX6?,K0jMXH{t|C{p!jIKcAu,q;5uZe.dKePBz+[|OSgyYar[?ATkYV4T}V68w$}hg.Zx>9?|lg6{DeNv&TV+41|rDG{=+LZtLl2jnBua?k:i59ixvQb+oO5XTg1mBCWHMzYugrMn[>$Dch:KzD/UYRf=n|jtKhHgWj:gsLXMgjiq^D;j`+r+<zOo:k)S|t?U,r/vAn|L2GMEEg_N*JsX~S]U`uv(^NbrDJt5?]yo%EsqKPUWX!sl7(bZ[8yBtR[c<Jj2jMi4>98XK]FMB43_bzG4d3Xy;tS^nvzN={m<Wu[Q|G[?{Kg,DbgJ|d{/7,(LP^NA;<!5=i&s`=P+,;t%JmQ2EcJ7[<qO#P&p/5UcSL_x|a#z1"%y]e{o!d,m1[iQD:V{J0[}T=Zd/!^/yeMYFa!.26Xuc9x"qZqa"u,48x)kKyVy!Kr,,BB0f~(,]gIL:gcoY&W/KteG@F61"R*f]/y*z/U?oIm#bhy}nHl2vXH#V@vXCPVGsHiQ|dOpUb,^:H0,Xhy?`~nHp("Q0I$n?W8&B%Hle]n|9=+BB4xu!.O[g#x$>,@g$dwRxD:=e8~#_v$qm#u2xGz[c4k*x+Fcfs#$PVE:()J[,BCbxZK;an=6^v:rCrkUgtT3,KED%5LY}%+5(P_eFy]E7Q%KV)_K~6^wm!g3z&6uYg/gnLh3LngiPMrtFNP&2]z`?37#@itS?kb1G!k[7~YncGy.fD8p":6xe*?]eJlRUMJMzNE]F(]U6#DeA#l728`[+b,U7Cg6.e/ePuMYgN&DCjaBNLL`$4pM9{x&/#t4M)M*iPgt"b;sqMSH+/zJ!Jzj.znxN7^=6lR^u4zs~LQ#qRMp~i%ryv6N#3>W!1bKntvSqpdNdjS.Da5mm#J9VjM<195QH">8ihHF|@1$8[z~RT5Ik(TpQ;=cql3?Eu&4F)3k8M$g&;S~~t1[_/zW{kXPM+k1%l_cv+zfIlJ!8lbDkUZiff].p(;FqTero}|oU`!*;m8%Uze.&0,>8Rrb;%CaSMa_27H&W.!WdZ,G$g30+z,<rU~rh7/XaD*BYFv`VWkYJxN{L^Zf"F&N=JIOOit@t4iOO<<$1@FP2&JE+b/$"ACV~0C0!x^!I{xLS#mciXl$tbzHaO:NNao7{mJIkq$Lvv?rN*FBu!CX]j^xM51zvu]l01TJj|7PF?MRdy?HnMw3B/df1wRHDI9VFTuQ=Nkrq+D=X>uQ1ax7&p`Ola$?Vici]3w)esShPeDYQFJZWly7*?TmDb(M]9Q"F}I|0qv>Kf;2T?EzyW.Q%9*~GQc$*_.`1k}^6K^n.&FMIE]pTB%bubWtF$2aNrRx=LQ_D[=x%I]~n1x&;jH|Fy1Q"xE4?EB[U#i7r)/2%?VzV:7xO$K>X|iHB];X#zn/`o|C,mC)f0#y&]%]F:X+;g>d4nRXH`|PxOehhXC!FX0jX7(S4a%i#2>*J9VThJ*c|(gH*LA"LSFb|1;e/80MsgQQ9ht4,jQ2]Cg@%l5gW}^jQBN{]<MNnI<NI4=p%Jl#&V5^uO3Alip>`$Lj4SVM+SHl~sgYPyq]29)4z7d+W<>B*ks1:o$6Y:yV>G2k!HQ^MP/q_n|qVHq1rxGSpH25c{07]=0X$2ulY<|Zrtyz=<1[]6rXLbflbk$qd#HP.yei*qk6@~.~6LFkxlv0],#7f#L%f1><ndAT`zm&?!=v<QmQ#Hu8|Uw~!d60`XTT$3SjJlbOr+RuD;7T|#ydGXj=bNc>JsiD~RxF>st*+S!T<i@YjWI_>(8Ye2,@yxT]X1<Cn0w(i$Z1YNx,E[+sCMGx7PDV|:AY%b$Yl!=qu9?n@Gn9vn!ZbCXIThoi#B90|^}cd`R~.yzgP`.iYSzCuB+A?vL[hCi|uvL@YyIz2R|GDl5moTU809q%,IZ$@ozMsZ.%AaDB+)CJc>Xc)ic`j5JHZY}9]<Q6nmF_3tf5Yz*4v%r)<s&^odP3i?gVu)M!jv+.zTbZ5*P*C;2wI7C%sRSz|6Z}8D<aXq#*l_*6yq#s1$4_!,/XRq=Lb!_Ntd:N=VgXv]ge!{A"*S^rX?f3c`N,dZqxJUE+D=!V9cX_3K#i~85aNuo}O&*u|y;Gmqx$WP0Xs&7l)Lrh[06]H*!nt~~ybC.Id2n5SKHstMm{j6S1UGL!v)U(Vc|,=cdA!x=e:_t?$czg*wo0G!D?BU(R[>4aRGz8P7x:9~~y|vA;&Nmt?)~Pu:,k{;({y6u"/uJvTqX8E2heiCDGLd}m#smw%DxUV%BUyJ`]a3iIv<>tVh7.%0"zAjL1r#ilQP5s7fmn]/**X]H[+Exn.)#k31czFA7}9]KVd#8^vw^~}e{Hu@;gHRXTZQ03m/e5~/O%uZ^@0+z10ETm[d>y>(1/`15LoNnn5X](;~F"t#(UR`]EZ~Ocmc<uEOvic7T0y/S?!_Y3"6l+r)vJ+A3hwI7m6mZA<$x}1TrbvQ6J/zJ;bL4l<]+T}R*^3{$IE/OGa;~]Ly`zWNk+DE(f9cBKQH@jmMST5)<wx449MyTUHT~J!{+^Ey4h"rixZ6U3fg4ivvyY9:0g>x4}1*&gQ.^upq;te%][k^ydQ&/j)3j,vWdvTqgWnATtC4dbdNAmkx$Hf^FhpsXQ)Hs/|D~CsYht`dIl%UnMkrD<gFmT"m@7BFt.3_{~N"q4.c|9N8kEnN5pK2rjbiJ(+ypY2vd)hD=bJ{vE0UND1,pm:Tb>ke&e72"<<%{OveEzkTy7*a(5[l.!W/,Dl=S(f:oRyz:1S;e#~[~0_Vh"^%Hn+z$%luccwi}v9U$iX%&pv=l[_e_V?,ElNI$(tVJ22*mw*r%HKhswAWX6gum67#}qxHkeY,<O0wo|u)Wy4&WqYzc%rTFGcAV[C%BO`]&RQIpJWNJP8Kkb=7XXa)H10:"gwcz3!8Ky/3lOIx<Ij@|!ZPTu8ow;MH`>&Ky`[WuyjY71=U@GkoftxUX3L>E8dexhit3M/<zGV~~k7vN+Kbw&OO1KtCLO[41F9+V:6V/&W5U?zrr*sibXlK6HrPaj@@mAJ,;FN?9g9Kf[@8X#f>aXy].2aIb}vKbxNlDG#l.JHGnfOAZy<tg]68tr{7.z|3teEtb,u86Sms1qmDA]QxKydyR#ecvk4n*=#QQJe!$,G<31*=6~o0.uKPSF4<vN(_C?6)G5H%KV(Lao7LD``NQ8#V1z7@}&0%^wB?UO.N3gw|S`r`SM,DiN8UPi8]D7CAM#3I&:IGOK;W1Qme9m/$>y>?4>VCbXp+E1v]y8?,w_tf:QANstOrC.a@h3`@IQ(YM"daE>"InHci((=?q@<D^/,(ik$vH4V","?3:4msY$pk&afavx00(/F0cov;bp!^KOlXE@AFstC>G]RphJ&@>[.cMG*A>=ho%5^o3lxav5JU{+ubMo3E_>oXNgrl?Q.8D~5GOeA|%UwCkQ%D@:Jj<RC=M(#q8yFG%xC$2rJRXa?Z?OQq/"&]:B_a[W1{Jh5&]{b}*!cyxD5$<B1d&#.}oXX4q8q]fsL!n=#Dpa89`$|rqxJN8rz04q`):_?X^mGEshb_)W"rl28kxagHGB~kG4kvBmo/339irl.,)/+GwJNym_[y0{KR1}I0Tn:+FUQ^qB|mKinRds}S?3IaE"+YtD>T1&N0Q{7W7UzU~Wl2+{~Pm%B|R<afPDLRH"ntDKF&#pYVef3Gp+$b*];#;!}bf;$BU;I_)zl!H?VZ#t@jUB^9Q7|nkdr%Zy+pLMdSVu[qA4a6IMY.J]46FmZ<#}>I4k](#DYR}JVVqVbdHv_*!1Q@<R/|aBqYgA{ZqW#4o+;q{h|HAqUSWwMmCNyVRV?=+x16LO8dSzJ3^EC5(9^GF6uIDN<pg?Mj6CqaggNL,<jgUvp9Hp[bDGS1.$]5HIsO&Oc{oS*o+zoejc$`YN"_+AZRlo``kWJFw#ORe3UMB;V$$xp+Ip;z4?=1r=!~_P!!q&iBAy#OLDO{q&tC|Uq]|!P4uvf_K_uPF1*a0]=5c@J{RGM.zd.^3)?sxqq_uj/$;*SXhM~()]BR`Z@etfw<Joax.k>"*R!Peka4,=ePP#{X{#G9rp&DXuQOKWD<?(QdbRb/z@5siayXuaZ61XkyCYs[G4mnY~pe4ww[#zy^QlkzWWKDRKt;p+B]nel[Ro3uerUszmwkdN7g@3rz<8u("hwk&SK;Y,*H19,ZdDyE]Q>y)*xC@!{Stha{sAG&[8I;+`>~.~l!Arb{TMcJpMW$A|@wKJ}=l57c#H?h9FOw<3{_;4!JlU|z*=jEzIkG3cgr@pABAo`7;c~;F/VhrrgSCUA*LMtb)nr=RW+nQ,u/f3R8_i}iIO!!8aBN*{`R5m"sl2*$Yf@Oi%;fQrz66/*6|>X`]?N^~{L!b=`0EI/Fiv*B(0}j4i2FB*_am"P=oQhLVB()qX7M?><}lc|?$~p]NEX"p22.g$)dx:RIUo((r0Lx~qo/S~4UfK2XYh:|@Eb*R*nH%A"ri[oIxGPmOQG5lhyg(6m]Mvpjt{>@],T]?M<0eZ7q=J(Y<TS9%TWDr$47)(988des$!V]!auZGQ6"VRG:6I*3pZAzdniw<Ek9$<V22&[{^FI)g3Thc0`B!sa%7R*mTej4ELkbR9Ayn6p"kK:[18m3$:"S2a5hD}U~dX./<joCo|_i&;lwaT`YbJemuGWD_X"nM7X>UXxA~W8o[NVbjRrrbr1[8:x)DD/xvA9L!WY(q}^k[arUMnf+}iz]Am*TFvcTUcwz?lc`Wt.2Ef*Xuc6/dr{XV9]+q0egQ){D=JdjAzVKN*/0{]a0x:ws/jK*z1w)?AK,Wq}X$sb$Lu%k?(]GipU^36UK<;.o,iBn12,T[&B#&_1==/O<Mg|)[9Z7#E2S}FYU/qdJ39&;Rbt=tXQ,)W,>vi<w*0/(X*fzw[^H.fiW,0}GI}t6jF^O{p$R,h}4/w!|Faw78,lsNc8WOdG#|mfKVhPOj:RQC+hxAl^kX*;f*hotwEX#z03OmFL5dSEo3^;mF1:+[p>:7s^T]yk="W`oIB$CR;;7WpZjU=!N/Kk,K:VX+R(7TU5/.;N}#~pHdL~lj|cDQVAXyx]"m$6R^Rs,F;Jb3Dui7tOFQ6$%?eph]RQ5=WlpeI!jOq{Ss9bk:qw+}[kfM#O5qmHbQ.Ao0Vbgp{c&Gk.fdR?6@;U*8"EYD.W<WNvynk3$@8(J#"33H1&GbKLZa`9~E:GlWwQ7+}%.#o#,[aXG>I:gL%b3!:.q+L_Zw8QG=~Sr>x2WaRF|=xTz^;GBv.1Sb5C*EjEL.B^=Wc<vC*X%p!c!su*^>o^!gQegE?2e>TOlcm.:2Wy8bhl%2$_A`_a9Y_$SD8?W&OfQcRBv7b``k]^9odz&|;"KEo))pelGLW+<d1HHdq1riW(?6p}}3b|Ul8]ujY}EUn2kr,Rb{Rm!,&/sfrGu$?7`)dtR,Fn7*!tZi~Ao91[u[sxes3K30jwNxcr?G=o$O=)Pi.S`MQRJH~~mG|d$uIIjLTBzPh^d/`/c4w6WlFF[#SAL$]=,X^0jgm^`(^os7iYNmij>]RTjLs2,4tUiFiG7{do>tq?v~*!GhSJpx3`$tBI5TE[tf|RYm?k",V=vR$ci?,,b_r/7f_CYI:vi7F,HL5phnJ^=6Y]T)i^8x<@IknA(`alQ0Gez(!5LXaSsWrhyN:_WVypRjvu9;#I7YU4yAsoN4|>"y:A|BqaOL={Gm3nGKmB%L=$7t`4AmwM^%fJtk=GB]UesgIo>R8#)R3v;Uu|aKvk$&5hXG{r]ddJfOCUwD<KTj_?C]ArO%:AkwS+_PyO/MG1ZRi3F=NOgDS0[d*^r,g.ht]Vr~DGm1K><{S1.*%+wGpW~G|x/UtM4"YRjv~4x.C6T6/a8]#iG$gk|y!I?_A"xf?,ZNJjCE3.B7$VN~1A>7PAuSm;e<Np>@xnfPUglz}J_tTau|I,^m&r(/OnFT`cwA)s50YuTb7zXRNudLE<C=cB.m{X$:z%W5?j;nzG17YL*]{b8m9N&&>`h{j2Y5haL@j|$@gn_{!*YR~!%eb=}M!6o+Li1{K$e+dayX;Xn83ftgzOmX_e4)0|b@*gj_uV27&%FaPm{^8%Rf%F=ZMc(;Sm{H|F<+#ZduRNra{+sM3~7]>vu)p]~_J{zGv.g*212~F]*?8n$Cv!1;$ir?82H&l|";p}:,!y8YrF|r^JE>`{re`8dbFFi$^<K@=1::3)JH[:Be[,<e2V>pO0;}SEvjh_]b9#F"d$5KEhi,N=#pr@#W8It,O0~P^^Q6o^J/&)T.<mDXJvguXC_*5xS|Zc4gZJ!$V/8GfiDcGj3ggreW;yH$o&^}XfHG8c/y301^[@.+r[^Kf5oh*+Xl$w*m%v5K~A.nQ@x.NA|7KzW#@.SfhZ7A9CS=EFRG$VSH&CtXJrg8]zG}A1e]OR"=D+|y]yg4grK1^HaaT~}^g}>$rmW$#hbs+9ta;rTPm+SI||:V0YwZM#FI{zIWq8T0Svc/GJcy&(RJI>Y1II/@^U2L=IC5y^yY+nTokf0IrNYWY0Gk5zY1BzY[V,nQa]#@@{)ze.J,X4oOlic.F"Q`[T,t+dEtf7(jdkWwMHebxlulH)<U>wIZMyPD){gkhxs8kC:;7S>K9_pDSc(N7{w4x#DlV`/A15X9e?8H!$uZcKv/LS&UC&f9K.HX}{zd~nsh$cJKB)zduyBxiOMz`suDc#P`X[|?uabB",py,?T_#7~dMAKNC{DY@!a<wvu#ccD,w_B~|U=%JX1C&{AyTW!e]8"Z?fPKm2C+8!YM.%=nM3^/bD!tQ,ixuf+TQKwGmH!AtR(%y&}t/3I7~U<z+Y2VoiS@Tk&[9^=b36G}DBo._q<i5c$Cu45j0BRp%BiJ+B75x+O!^BhrZF,3kv]9RQ@Tu3*:B1`DNG>X[Qra`"3;uHMkm!$lqc!Lw1P{0ZB||j"zFNL.J@P3wps%4y5/`Yd!7knHuJpw#3CyI>~BMNqTyiS3z%8{BmB+fahuEZY8IKw}+4PHY)N]`71shXp,=gpPSAmRAK./;Zp)|>(Zjx?MC}]ISSO/>]63P=M~/PI%7fHgP4,mt@6VHoJ|,H>bXsdYQC#u="#h(88AmU)PTuS#3B(dBzy^Fe4bruZrQD6lv8S9SpPr_A|$dC!u4mS$e^:)d{%{1;=8FANA&I[;8.)dXFsgCCz_?%KB8RXbWmLhj4!wGvhO)I(?i$:kWV2tL,nqyUA|zkGOcg|m8NzN0MxyQfIHg_^*;aug+&@[qE1loD9~Wcfw#opZz^muF=+Oeo<RL:<=c(6dN7&3?oB;M/dWw9AcZ&`jT1DvrpH)|hk;=})#EQvzzVWma+jO^m_1eA~[}%@~%NQbE`Gx#9~:,U0zPcLo)9Gt|9e@e/1cQz@:vxvK3uSc6hj1DHaOni8A]:Rd/KRye);FM}:7RCK"A+yd(MYx7UZ~GC)RroP^mB<V6}oP]*@@CD3I5Oj@*;qXc@K"s/B/>5rO*~]?!cZD[lt!EJeJ>Blyl&_;IJ"<H+Y#~bQVL2bBg|_}gFio`O}l3iT|mOt6F07&z92wqpJuRNtRb^=+H9im}cy&fDwhyPiJn#j<Y9bVhSeg_w3P;y/:dC3L&{.?0xjetd5Ii1vx]f;z1;|ce8.^JG!H166:]oao[DFu3HiAD$/7LV7?K](^a"RC>t?NERS=v/_ik(Fh):Fh(5^E4%a!SKO{j6PaT`Fw4k}.)f^`2AZJnsQPQ2w7{J`GXH>z.))=Tk*^#,vdy0d+;QH2w"[<"ZO<rwP{|ic24@Xq(q]u<]q2Z`G8nrg~>*c+;j"nShg<V!t9yEZMO2=6:bh"M2#cEku`YiOPCW]n|M|KgdGC|S8%rfzXY>KX@=Fv#w8lA#=ZqdLK4Rqp_5N]oF%y[Ak0u?l+!O9fVeos)p2iO0s0gwGu,d3Rf@4M[71^|zdiQlg`PT|l<&<jRD)YG$_=^!f5EIhqfPiRYzvq{0b13U$gn/FSZgGJsoBQT,q>hc*9VLi9k!{Qhl=i?EnWlI$j*bourVf[%GGwf.9!>%Q8+x^*1PDgogKFJk{cva<VnfL|=Y&EnEBRgCJ#e}l6gWHG{E"1SO"Y{|rIRsNp%tm,BV`Z}pNR+T6@*=12Z5&3q3po@xONV~"@Uvs^]_ds2#i$.A|s,83Q[>&Q2I.DZ+Dl(0RwBK)HHi:n*Ei~?;]on>t/CsdC];#Q}NeMF^!@?lHD6p~m:6#2afV^2nyHWqo8JB3d:}=##P>vD>u|>88WcHqo~CBu!RW{;gImr=c13&=O}NX^{Y}l]EJ%sQ#e&MIE=NuZc^$@({vI:Q`"b_8NLmBVv{Qz+~IYX;n!^o%05@@GWD[s/6&w`Rj/(uHJl`JmIqzEI/@oUS,qF6/R+i{W9q^ELpU(h7V9$)0{mR`A]q2{2IWg90w:ELTYJuXAuN5E5w4A*6`]C{OZqpI5/i"nVT?|:<EXc=N^k,GPzc{]w4?%}1Z~8"|n,bdQb`K!6i)2*J/fd5hUd@}:S=vx#:D0+f|,>br*7$siz9u3KOtC%C<|$U(o{h)FNLwsW4VtN,4,PoYbaDmiigw`XpWJTul37Ow/7Z(AB|+[4j:l:tukANs<ZbqW6mXAF=&$(&zM+x$Alq++%"/1#Vv%<8EGR+>lLc@O5cp<[mrA,Nzl[{+#?W8nxeB6@|b1]dPa"hOI^1lOG[{Q)aaAb*ot:Lv%T_G%123M:.:[p!F2YQ0rg^MAMhG>L|w}BJSgQSwO!fgmYq|]gKQ7)g}u`<8Yg}BdN=ni1pTI3lmNq<I9!A6MB7cFmacL=<,8*(S=SP,(`cY8dF4#!)<E.[p8.,PK^;pDjCKL+ls4|>=}+Fuz*BeI$4CWmtQ,Z[I^(b5VQh!71!|u]LWy(F@,SR8<NhI;/|y]r}X2R:D]k>h+q);rfE,?7($I:NOiG53rUAQL1Mo7bS}ODm1M7MkWH0F+DxseutKJ/e0%E,TDnK4[YU2.>diyt7qDaXbLN."FFL3tXmO[Fe14CL;3wy*xq>Bv?C;T:@hp]4n^Xoj*9RjNXmDxf5kkdm6)wYNdI]EG,[0PjptD^QH.yLeO3}>$hnb[O",U}KrZ?GC0g:,~i:&H*TUOFyG;5?guvZzQwtk:.M@Wgjc#.{k2"6<{Kx<@B;{RgEX=H(ic/|~AU$}rO*yWB"bVca#jz=CvKnos|r6%iz?e6i%qe7X+4>d{5z?^>jsu}gPL"br&M$KibGuQnrkgdahq~>|<Tf?"DScJ%zcHp#Xet*N1,&ySYUjlL?3T+H}lOxf1]OXiNX]cqS:k5pl/`{b97(OH{1C0P$G|OHw[M]#_ti|njoqV3Gt0Rs!!O?zBS.6g^e)8Zb?=!~RZGA.Mj|24?/tQE|wRhN6bw;l4]r4GE+j(5fFu},Zq_UTl.:NaQ4cklpkL<qV690ygv%f[s$69lnC<mrfD`y>DU]}p>`e6/0~e>QEm<!*T/vuGu]5u32S,k4~)SA/}`f}m_Ogy=)7pJ0(q(G)GTP.W_ol.&.Lq+WT4+]voZf(lGaEp?FtEK~ALcFPbV:IiQT+te;J.=THZl0BAZwyXR7d8GLtqFc.}i9[qu}gm9EWO,}z@;pt3slmSSVeHvZ&E?*ME9svfYJD0;%[H]?0sNm:ySI*R%(V>{VZV=8UFCXC0J{ev<F^=Y$:2**fY!Jf>kRa;j]SM8>gL=l)3/.%s%UHWRJNG?oD`GfH_Ll3J,|?vW>Bl|,{=G5Xt;xIlf#S$gT@3>L0joUk=U~1aOxP+i=~O#p+m]74RY)evV;7ycMh7|^YvF.dBML3{SKYN{$bfnjuKN?HJIR}cu7jaFLee9Y|zg1Af6i"XdBThe[{>f{=4wm2W#0mcwlup6f&pw?]U0Vx/&Jjg&}r`#dN72xzyWG?@jE$~%#pH5)v]oPTgOAWv[SP2=f3[EJh8y#;&wjdH}uU>M8{K7EqJ)KaxGWh%M$B#IU347"{<CKWVz8tYDCYGO(e^N0zRp)Wh^VC./S%YeXoR{}=1Zt@+I31A8[k#0PT[N!cH,kn+MVIFQAO+(M[dQ=/cwDPwOEZ5D:u8r36VFI)={,/*DUX?c=xJO+g]q`^N#if)Fjx%Zv<b`cYJ8~*W8NiPX<rjr9d0t6&1olIg$qi?%|,;vk<)4A{|>:x6GE)UCa@NHA_O/)zl+U0!/x)#+7(256z2G$/?d@[nBCU@zfZA75%YZ7!qCHA_@5!JC&Iv9H7O(?bhf6)&q7_tmd2]1fAeEBr9Rbt"gGN[Qjpx<BOEdeZ?LKnql#fa?@/;V]j<j:78M~zxPeOrCS#8f)6X+<Ujm|4h;MhzPqNBdC8@=2zQ0b?w8V|`v,Q&dHzFd9#3o."=$8GrJA#]klG^+lNSxm@QM|Rb#"~y^&E*C`vVCfqF3M}2<(KKH.640MLs|`3ABi?n9fR>Mds_}Z7^W:!R(^sktHt"4r=8h?,Y#fBqqn&MO>uS<cj6"H`T2`k9YMJ|q7c8rmcI7PQT&M{2.}afTcVCVu+6_<~3n]c<Da1+^948j(Tps:Q*E!GF(Nn+"GIVhSr>3+4(rWB~(JvgZ5nc~H`ktOE:TU,t)zJ=x#N><y|>>v/H.~x,Vyz"8@nHH)Haz=7Kqbr>.{jhH^VH5Nczq%Fc4D#m_zy@BAyF&)YwgR/W2;>3t[Pj8QnTvs(/lM<3goFvFHcWa^"tONPZGKA^Z8n1M;8Q$y&I/.B@p7Q^?y,Q]An6xGnFXPHX47K_Z34Pg`$q}@wa`a`0vNc:h4Q>QOe?e1(iQ::dv|bM5&0_933pLEY2kGY{7&YhD.vId8gJGfo7>:<7aXG[x,`%7;tKg,+07QE+&$;U*7Za+vANg>JNokhDGawItE^HfFrSq(;mxBQ,aM{)R~dL,a0DU(/QCj8C{VC.^q!&EcKW%o|IFLT,*q}|yC]R,S!85@9RGEw[S6(1+I1N~|52D^e:=Q!}qc,aCQE#6vILSoRYM^_cYy(fykgp071=m}bj(|]3N0pQ:BC8iPM^pLQ6[8pmlA<EdbJ9]mkd|95ZNm;lL_~FTx84%x|K^7rO5UElpZFvL7Q6[}M[W`B7U(MIk<RTz^gl^9eRkn{S#pSPu,^j7u<j&p8dA8O"V1JXTx@cr1&~W&458^o$L<pZ4JjaCY1qMG}1VK/;[<;XaB#bnVg"5Uu}<rR5EIxjPEF=#!vSO#N8v5+!iC>dEX;jX>ua36olPzbcY8KzRpX=n|y`%/u&v0aTD[Wh:Xcpg$qno>^w>S[(M(ThLhw,>wsH$"b&T=TZb&K$40I/*mu*Q4]Iir<UZ!2|VypLGE,rPaTf`qy5bB~hDK[cD5$`I{]^0I!D[s*C$Q20`~/F<!YqbBW6x1)mf^R(Gp}T&KJ2iS.!mAs+Zk63<Yq#*}i+z!yeRA4*AEo3njiv/ac#Npme_>^SiZY9h]T)<h*4JItrKJX~GUycE"3}e,UD[TPj_UEWFH7tO"#{gTCi^ofzNN0=d3M<Uq>9@|S9dL55FH{ibbOD2cM+uHnf6LCiOK2HM&Bo+#3`D#T%|e*RA@n2htYA]El=C.H/o6mXd`?LFjRntWcXRmZ7%)=Q`ien|o]pq78_L0i=1/`|Nt&"isKHaN4*&G_R3{OUxO|CrCD8qdVB@ULY/R;41`p4GRd,$SGMdgm#u$QtWe*ssqyy<&27sW<h#/wrde&C5O`m,eK|4T]bL[zH0JdPE[{&)<jz}&_%K[ZO.i!lQO7#9mO]]PSKE^OO=K72{3;d]4GQ:gf|I_xK.4d=?,cX]PUCaVS[ODp{nL:Ud?3l8VK}&|6fH8rcQ<DPb]tuVt{>c:8+b1X9swc^Jz&X_BCH1C|*$y7G(D4l<4D1wz/PJU&>[$U@FpI!ElEHhU/FJO%?iXxrNfbq7=JB?*Vd+E2:%|lC>pri;n~>KiQ|J<;r5]l0?G3D6,t++l1]blW6.(*zt&K@|9d_{5t45l4~F%5Q(W2;szr?h%FhUL]=#nD#0L;=Kq3rr:53Tb>iCht{`[A8n+H?zk,KP1307DSI!+vY2{7Z1tsN[(n/fK+Z^YvWCxdis!g>j89@={._b)}HTpYs%m`0|Y_]CrI_%cTi/Qu#~sVcYhR$E"~DY^2eI&VwrUc=@:Oe&BLZF[)HI(kkj{UVl6)BYU)R05a3bLS[E]~#QZLtgMe9Cu8Q{stPKO(0m)P&;2{cgiYm^h:AwNfV*~A7iTsNi3tjM]vUK`pmEP=6NE,Sa:+5jAD*7GNPVVt;3Y[#/D@!C*t}.M9"A@vO4GUo<b!YC.OE<PbSij(99gWflm+V"V2}ZZ>:_%g$ox2&_w|]]r6a}issvB%KO?5KTx3}/G1,5T8*3j3)k&))_&CpPuKjh#MO`Vp<Xu.Kau6uivDD!]rb&3/*{>5+KsLJ{r8f;h]l={:]:iu5h`1jS7l1ng_iD?|APrEJj8{<G>+|tc&!k02fGZT8IY4my^:P>L!B(Dr#&:&aeUO?Bk(9r#$/_Rfsn0F)9|kvzT>${SS}91}@."s:PE+iJZ[_Fx@Zz]!Y4QZ+^d}x=@wACALs7MtJw&ukt$`JG$p4N/#Qe>%&>zeYA`r[Xph)6=NTlIHfeI#M}@!Fy9R<HDT[i)"m3|}PPB@w:S!oMh5*VW"/"P!i1"ns1q:RjF}<IT/TBsZ}p&0a#tSU[6=P7"|fHZG~!;s2eh8RdeQKhd`}`|fw<5Gz[x{3>KB?5z^1e5"`"bq["5iiQ!O<kYC%z|C{eCB?7?M=o7}psW:u{Z]^nSy;u+=OVW;|$v,fy$,=Jk+L/%lD#+/[)o3i/(,M+1{#6#"F$PoR,EShFn#.K|~Bxt3KpR~_9fnDx~%>EddRR(3}=s53|m%[}:@#GNM+9w41<iTBNF|`M5$U97DTFSP[mLtsNjb)9|kRypr1D2T37[0%}Ss(Tete5=>m<j_Y##IZt"OUpqCHNmaM1=mR{QEB[9G68YuAe[JE7L5[p/wd#FlyPs(:Ft?2GfcHf~v,GZRdNE!qGe[VNw37(!syusKdl"3wxmaMBHL;&zQz#3,N2uRwciCrAR2;4A?>7IlGx00a</Q.d=R{zyNA3(7|$#3ul(C7hP`bo?WwB`g5M`;Y%vZ:nI6zw],vwG1Fp!wt@H}rY;/JGztQWYKM/%~L/>~R`u7ec[T0}P2|1l_dy9Y&k1VunKips30_;D*>q7WCyn3mXTaT;@mi0bg$i>T:,LG/7+bcPy}wM&S_S"h(fz~ayy_Ca6%{vof>mXI$rPT?_%3vq6CyvtcSr%YoD%WN&<#}<RcxNcm5US<}eJu3B1&#J7SwIj8W61Y9*g#nQ:K77h;ZGqga,^C#vea"{^%c<f1oBt%T13ql`Tdw6f7I].,O]R~Lk;0vNwOHq"tip]+c3|O]rWjWf!NMgYqz;<SouK=<sbWasw,kjV{ykWhyWKD"3JJ,74y+s_4Zcrh:9SBTTin>u=BMy7B~n)Te1Ylu9=8<Z!_vd,77+Dbf@Ib0d0GXv&B$KsC3Y"y0u~uIPp]=050de(23[MHuV)im#p7k/7!+*w:[J%Sk7DU|?!bJqs,S>$.?O)u@3Fw(`<&,0xYp;vfvfSK+b;KP%4VP<%|iORCNhuxH_y?azg)NMcF,%mH8f19.y`8jr2rLQXrHEU4@tKV7D]2bO1.CKcIYv5)jG+e4c6Yq/(tZE)_=?Htp=IYqI,BF]!8lWmh<E=hP/+X^^DYey/<Ci$(0z2vU3vcQxF~<OQ/_F4GM=:5i7BYbXcuWKC]GuA*:QKs,Oi)]Iiw2oFn9{FqC_S4w36t|^>_BebaceXTFl)*XgkRz*uZszqR_Wy.kfYF)nhi})orz)6^U(x]8*+nb):%750mh7Aa44)&{<,D.&GkzI5x7Mo6RR}1[Tg1p>6]2|5/+rBmfgd~ZsEN!kGsmWpt!O;Xv|C<Y?W|5$"S_`vYXt*OWw,)G~x[@V7v@Gc(idkc|~`)G~UYCNE*n))_+~zc>KCc8$TP5oqf|h=W4rFau_qyDXBN5f[~rJ/b_p})5n$yq.@w)GTy5XBsv&]"1qObcUC&;Zvue6CGcFw[@V<r}KWLH.KT;]bhB8S.dUG;RLlOF0!rL*Ahqz~NT&&7R/5nLaMaXRci|}&Cn`O!H};~qFvF&>Wv!t&[0:iz*n%[fm0EB=,F+<%VA+bbfqJW9I%v|6+[=ccuHFgZO^22K_(%s1jo~QG8Hh71S(|+}]is3xpr4fDxWzx^whHi>7uP6[Xw91ZtD5);:p2.hse]v^SBIbovpF4y~9TG:7<:}n_WwIohF>c_EcoObR!/Pp1jjlD6l*M;[O=fBe&PkfcXN|_q#Q}kf6H%@f:PE%<QiQ*9yHQC6_4>i9DakmfpE8AewXE&ta`HKqsHNznUa,M,(1(YS+bb!v^a1Sk[u$(PdH*Mb!UO}:b!w_$xGI)]VQS%QF,*$?/ad,ol(;tgN+p#fe8;RH|Z+JH6Q06ID^opW=O^4+y0<EPKK0SN=Cwj4pr)Cbw;jW@Uyp`aCs7O(J]1qU9>V=+VM|u@ZW(Cki`?S%:frJ1OOhWIL3}Ug1[:37JIXhQgm!W.Vlyu7t:y#532GO=/gl>Jy*K|?RJ^WQc]}<hU3~gTjTg`,C;iFazPgd2F)V?0I|um._a:?**wT%VIsxwQ&9iH%cd5.d`#Uw~gFLgV2QUDp~{U0JJ]2.wiEuY?S>Toji@gbP8Y428!seoKjOs,9!Qq&;il~gljqN;PmZ_[gOdJ&,Qc#s}z.@#qmp{pJ>"$G~m+>y2ODb)D<:,=)MIg2wEQm7i5yyxhkm29<4M3}`OlOL0z=r}0bZw/_J8N,cnV`pL.?s{bZv9sI+|j/aazVWV!If,l]Hc`3f,lk&8^QT$3NI)<,|1g!rfpUJ^59h`^3gf@e[j[P37o)=+/H!K7+;_<D^Sr<.V+{iRy8j8;;{Z/_=2`bz0f?]np&%&"&"Ios,CS5{nx/`fuM2KBWho9ts*@3g}]"g?R4{WPWmb3lCCi<z{N41|yov[=668O^Rbu?aeHY~&_|hP#Y{<^KZl?0/W{pm/M8;ardYDWvOT!SzD&zo|.l@`%6^e_<!qJ6D2k|N(%PdStl%W.UI4#N!gQ&ceH%wq9S|Y^oHPO8}33SR6b>C&z9_;I8Ob<^oNiH.1HhQ,c1SI~*^|.^6#vaji_B_}]i952"Ma0|^K_##1cBY$V710?ea]$}@pG?mm76!$>bRB$A=WzTT[coMJZPew`+;%~[=pL47B5Xwx1q.4<9^;ujw[TdblQ}N)&?Ia<rHGIcVD<3g+)<_<@M"7y!cT+>A]g{YxrVDC`;eo6!L#c4Pu/S6KS3])k+{/2Uo!y5~oK=Ea!vJ9vL(~kz8"ZS<8i8+"{NHTMC^Dwg7&RC`u<>ploW&(2/D=qt`(7eV=_1HH6XH&z&`"1ug{a830RCOX[lOBH"Iul(ohx]N]VpPK.p(2fRf?@&#{tcUjv}CMlW_eE60CiT4e1%Pd:fpr2l:?!b[E.>gtR"jGBk,6?[;S}w6t]4]$@@twGT2"51F45Oos;Te"2PG9Sn~=_eYK_&U>?.80x5.0b)J~wosKwBO1rE;%or979AeAPxZM?n&Mwrd"l3)8gz!z..%ZEV,Ie1):gz%Qz$=)/YY;};#?hq(jxCGEg+7]=;GmD,RmqUfbYms1buUodY0"ImO=&7R8Y6%pu<GUN9c,|La[CJoeQazU1>#!.5zfnnG2?nSi`>O1;90#PTW]=F_c&<w&`&@R8&;N8yAl<d#;G9o4.dUyv,sh.oTc[>=,kf~.+sz0u9*@q[SfG_@%oXgqL:%8%n8M9RFViZEQ37o{I2w{a.Zz]B8>ei9m8Ae@vHi02oGWXoGdrepo4eOb`Pv|YJS>?;j[x0La!jb92>i7N[IAj02+sf5&IuC4~]cc{"QAY~]selo)cb5.sH(;li9?]VR9cz@B&cqzQ9Y(:8/1.&Fbx7yCQ9dE^t(?dS&5:`fZMquU_e{p.V<p;>Pea{XvwFP*x4Vec$C@#c}CU8U6+kKup^.I^H^:i_^g!;iAPa)tb.XUILsa7uEruerw],DZv(tRMxh{*wQEy})M(.SqB$z7NE&17wj?c89u@BhzJG!Q0D|CN7mD*)Eh}OhzMFNfF(ROqQ_2)+9%V8#UTZ~Tcaop]h_@q;)k)I0(yI4vdYD;Kt/ccFtBf}LBaexR)6O|tZjKMdG+U@E#(XOSQbDN`R|B69*wL4OTD8X2_=vZZ<0gQ3#`RlvvtO>.`G|JOS_G7}*<i&rI"OR1;hH#vi?a@~aHcCt6!"Bo?=$H%0/_c+!YrL7["<1J6]e{5/js9[YRGYy`2W.c&R{K4E"ztq#fG"T:3tdSm?I/Ny5/Y3j,"I*IyGT)MedA=7oaY.c%a_8Rvb{o(qX6G>9R{TNewU:^1d*Si?f+Ut[OL&{A{?K#y/}W.UlM+ci7RKf,WRz!XT%QSKD/}l,}IUMtIKo9JoNv,oMMoN>$aTxYvCy"HO||CMXyEJvMQSEwz$B>I<EpQJ<!@=e]i#RNtj38H(1/r4Dzz^qCAFf^bSqDtQJ~tp7]5AgB_0>*Rk6+G*FJ4NeQ<rEH.6]~XFn@SA,IL|:{zClshl3YdK|u@I&!#=U(=6]wKr2tK<zjj1#]1+U+D"98v;s.J2|26^97O;@o6c]?0?SxZ=I28vDt0.+h*ntcn#R((!U@g[Z2}]huSP>$7B}HaJH+|)bmSP>@bj@zM3e~EM71X:$6Dr4Sv=l3WS*_#<lyO/t|7O>MC0|o/t!&FtuiE%O)~:g:$[1f7pdNdaxo_HltBpQnSITEqB^FUZ,@$xOm0|z~rntoFG_wa61]`ov&{)zAa]64kIRZg0ktS.z/n_MP7r=olZ=b4O:+GeRax[F*2(nsUuS*tMwWBeOKQbYhioc5sa!2I6%bpeWnv(?G2W8$aL5_7u<b2U7$:R>JnT.>5]uWzn[+knR"0t12ZAPGd~k73_BQ,z<>xL$^ag&/#K8Ae;etYET&@wfB8|p1;557omR(VY499]Uk[Y,YE<y=0r8)K]mRv@=~a3>t+I|8;[IGawS;1#|H2_R}TBZ5K,k6eOm,^=LjU,1lMfU,12@x};ldRA7owPK!(Sr&851UR/Dc*e!#4Ws|9hk9SuE~{2SQu]e&tnCg<SR{hH;H%Ru4dxg;7p4_,N<iR}^m9/<86_4k+1]`]he6B~n+m9+mtf]!l[9a@{h;^UZ%,9~Ks3XN9=h_Ge`ys4RWS{Ob8!cL5Q>ASlN!*Po`>45b]^.di`S7WQ/P]YN3zd?<xC@Xk[&T[LvY<b#eRdTFiMdLvN%#;:&4VgCqJ.ka:r!4]:/^+4]qip%(5*6cdCwCy=l]qxMX{Ez.o%k`t2]f1]ohKvgR&cfDkJE|<g8wvl%:059#N&uS//oPa[d>bPS$;H(#h)dD0&g30U5UHQ;%i3!vBhHPoeJ9%xcX/n#iFO[NxT^,l)O?%Q:51>>dBt6r!8h_O=lC6/`&wF[f&LhXi6&c}gRvY.1i;_D^o.2dU8+Q:WFVP6,m!FjT:/(wFE!}^7N_.Cd/ZzI%D<zX0_+lo(T7[f]U]xi+pH>}Nf:fEuqhJVX1p5$#I6e70/i2eKO2w;J>Vdow?8HVlv]"ZLkrwB#;GZs(9~Dy}X*6Y=wlp9hF(Wlxge5>4_0nHs$]P9j<R5+Z:Ql02H<;}3p>ffe4fd)AeB8pr#iWF|7EZl@g&?x=!,eB`B;*))mUY#zA%u=*xrU+L.Y;eaY05R,y~prTw/@lruz1&^{{oL)++ik"=n7D3GE9V};GO!tP3y^zFou0a*by+jd0iu]!#*%J0Ns&7>]Y}tcsjH*^V1DW__VLa&Ua_Nfv`H7c^j#;b9eQ3DI==$n3ExOh<F}nSf3UFZh)2_a8!J>;CA6Dy`xswXY;U9dw3yrf93&8/.*O}!,5vm5)B5GbpORF.WQ.ODn=$DdwM$i(rGY:6dM,&`b0$feB&J^<E?0K[wR|=ZOkmxr/QrR}m%R8+]Gf[L_Wvb_ZVvroFGE2qUTg`D@E|,#oii`OvWX$B`jy3z+/G?WnV<@S"YM[:Wz)Z,IMluaNzf:16qJUw^W9oW=yr8!";{oqJ8UR_{9]n!:]PX?tQa[Y0l@L6a{Nikh4D4?lNjkx,11p)Z[khaRtOAeB8zjn&my^ZT[[V%<Ys+.Nh2ozq_kQ5RANk5R5IRwctchXIT;Lke&59EF1WuXQ;Xi#I2VAWVr)}elVY!G=6d73,DoX@]kJ!MNx;HUb~sY>6_S:+S_0nMCVl3UH7z7y_D5)H;JYv87#9]5S6StjmSTZ/B!KJv~+vO20x6]39+e)z?KSw&2/Yzt_:cbs9`f5e33cQN=.@%nw?l$?$+_qY&x<,bYn0`zAzY}4k_ushloYEy>B!S@=en/;v}x0v[+#zcqZey91FFFwT8P5=u"F0!u7/!9Ifwq}p)5R>VI9CJn2I?fk[|bwT|1l%ff&Z.M1zLFc131RC(dzQ%&ygp$vvO*dU#}uPf`!g/R2S=;Y>gW[eXxMjH:qx.HV%3PGw.ji!=x)*[Dtq~Qd*e>=5&=5T:T}[wj&La5K8_429F)k@9i_VK%QCzX)O|td<>l|t/W*xf+Z9gfRzsjZ??6V1qbj5xSV&Vphiq,iyUf_[f5Qp3=wvN}`wT94U5Er{Rs,_!.]=syq<*,T$bY#k7ibOr+bNZ{]=P_c>6^O*x1+dvm@Yjv/FVX~Ne[CKDvt7D(J~R.!p/8uO_hTe:0>uXa6IQ~(T3SGmCTCkG80]W)}tdT+Zcuai8r|m+Z|XTVOv&Q8sR);T3]R;EU9;F#e0H!#47;,(c@&IwEFReki54tH&>gV~z5~7x0NpO}Zp1]_C+dSiaFPC;y&%JnFmat2Rz+Eb&$?0s.e%anw&r||3>y}FPJjvA;lyjzX6Q<$x4Cw`PO$|l5$I=i(r*}nT^{Ox9,/!;cK*h[ioKvV])bU^K8@!TJP>w6hayRMQ]UgLyLWf~t+j9tx$p!U17eYYvY`R"Q4*h>SRDyeO?^xPB>jF{9DR6K?hHslT#QzYx[*NA@Bw{5Til*zd)JaU_8wj{zWpbhG#U1`!8%0aa?X0QIn>k?nLoCb~D;"B]ZXiIrd_mQKPpUM*y/}KPUR&I_HNt3gB+hnMop`k|3XR@EIu~`lQ(sRZ<$lDJu%gE8T*b5wIe)/l{FwJeV6:c&kL1~4f+w1Ud&)umOh7tTi!m/&h6u.4CroZrcevDxnUL>sH0^c#Zj7Gy~TNka|i<Pi,q%>l[/yX_S?[U%J~4+J;0aX8H5dUfYq&[Dn^n[3:4[cV55;oqyBVrzm%B8b94&|q4,1BQIOc(7t{YPDs_DZ~iyU[g+syu$%l3Q}Bj852AenQd3;4<Wa/DFN8*$kN9_(8Bb{YV1vu.5NSJZ`qzrqf~oj/3<3ExU"r3ob$,]#.6`q[<b0Q`7:gB#b@79>)FRJb?YHVm];d]L4ltJk9a=YUH6Bpp9]xobf&NlqL6xi9!XbiY0%f0B5rT+:j>%F;_(dR``o{+1B_`6}%w]xg%0X5.O+)c7:hgv88Ie435uAe38n#~d3d$*as4)f%H(%Q*6E;g5WGK8O!J^j{.KM9b1~UVJ|DExnAJB%F&^uGIEz)"QAs}hT}wCygXx6)pG%>)4{8Rn(>E<6.Z>(/o#M%RrsmCI96`IbW2qw*K5Q=f`72YnaTWPUM.cqjyR`TQcz>%(o+$I8jgV#R>X<EyW]#]k[Y#vCEmH*HEnfo?Rmq8,7.mRnhgif!Pl.qR=Z5[+Wi[S1+E(,qc8Ev!H.5bhRpqZf:4M,0U(VrF$S1J#G;TVS`JDN:>XDSUDhG~%`@r33g9cx5+]/8(%2Bp*DK|M11g9%A2xX0Dd.mfVg3MEc**KyF_^a4]E2"9p>C<I~L32P(/hWz9ZP/`(^W[/18P<EPhbBr}25`|U86]_FtmPGQqdMgN}o^#*,N=I$a9Z4N>Q<Vlt3W@*4X9OU57%?_W4"z%iulVrd/[LLJAnZZ%<4k967H6>]zoQpv+G0s*JMsG/qYFNNauhD9k>yr(E!lCnW%9u^D^EO&eS,%TrcBMNhqKMp]K|@K$H)2mHD9wZqjBU+9gfeMAR%):CI/`GB`l5/}$u.JKIHKFUdb|m#SBTn/;v{h;2(Iw;pKArbb}`I>r6ljEn7wN.&|V#N5EM=0nLNN(G#T`qWf#R)4]hd7:wIg/XlJA5=swLj9D6N#%j8H4LJmtIWb<fv<"j|5!&rf=[!ZCK6C=jN!J<wU*.(jQ!EfT6@uSNC8Rbi9mpaTV)wVE$jSAMF$(i0:=e/F9f]MY0nRWw%!4MU#6GjK(:_WLD4L+a1C6_>rc.%N9_77rBrB{u/+HS4YSsyC]R{J8y_j%Tc4YN.0*27G(X@cNPw*V`")/P9+$+"QL"X)HnAY?L1[&=?if5)1)*~dD2VwJFi.e?FQYbBwuDM>o"V)@2JD)*+@A)vW<~>CP"S[jv;DcX;FNX8[KiM$S29*`U{{r+QImvjmzLld)(E>CdZ}WI2dgEEX8})w{_3X2>LRiSbJaX)OWwyEQ70v5/r&.2vub/VS+6_MU)^(Kio%%XBL@y/3w]Be51e;U]+wO5<~/Hdk5&R>7V^@s,.<h~B$:.l<s!o@}vZEL`l|"A/tUXIurJ<?rOmRPO<Pd&j"EO99ApVYG7]]xU9aWE"}@lT*H1)Cf6a=T@0@9a81F<pu?gqu<m{9rZGiiqrG^!6u=FeU&Kouj)%v1B7MLL6c[B|lYOkUx17kV22c2{GuOG9f?D,I=i4E;!"4E:VWq&sW;3m`77gtm!G7LJLxuimh>z{u0HJZ8#Q+<&z$4g{thyT>Y!:(zs%?IZYiZTvy{G@y<&18f};&U!rWVDrP%hK,jx^=>NlxA_wa$8+K|tVAmBtq[4%?#D1]QTQc;Gdv>quL+BuhJip>kr/!&=xZ8[U{d>{aKS:Fnsfa@!U8`z!MJA8yM(nH+Qi?nLhYr;];C*tCMiZD}rjD/XnVy%|9(dBgJi4L&uwF4p3Tb]#PH@"BjXQ&.[t|/lpB{u&,{(Jv8Vu4$Pjh)WIBS6pw=hVVxB(g[G$Ks#G~FNUG<zCG)(;V?B5oXdC.8|!?rIs!>@$1WL%XP3N*$/]RP322~^mW8UK2gg5#~{_TpOppeXln=a)2N!Y{zV"zv(&nu?Tt97pC=/ATx?CPxhbpLVmYm{B`AInabVedHivY.MeVr2x_VlG322(V#dK@15}1(VXVzF]pemBL3w`l:j3t*2nsPJ|38S}N|B861r!@XZYbQ;FT>I?g5I7xk!^otg0mqLquccu%v`=aOz6/bb1b"Sc3/759@AM`,eJE&MsIjUZw+2&}&3)de!l3u%@O?9A)lQ|]<jsh$qc%[RGq02"$%[SMh@!NY1V>9Qs0%nug7[q<M2]hiUFO:v}G[6}w&`!c8>rPBHWGOEwI,qCic?SgYGK3o%>qqd``$c5>hNBHXzkhs1C8s:d1Xh>;Kx$6I:N#RRE!^e7Hu;J(A:c;<umU?l@2![S%o9LObhNgMXd70Dd%">{5<=#?ANh,m%zj3=%38[Gk2gao$4iH=USG=[&*`dro.71wf0GQF1[]!qidPeFeGd8|yIVw|%nEJBgtN^RE#`FvKBXD)7~{2[e>|0#ILH>RE0FRIwp]MC<Veh^gvHJ2u]A^BVr;=|+TC;8E?eidFv*<X|IK<&bzKO4L]20_tb#BGQ6,ofn!(548pP%f7V99<%JsETW0@ns,bk<UNM.%481+`w?n##E0kw8O^RNI>>(*imfx$q%h;ZbN5p<tBC+N/_GE@PmR?VX$YIXcD6L"a3UkQ@_<ma|[;2iu,+w$/!A*kE}WI&uFntl+;s^Y"w@4"g+Eyzr:t<A>FG%BORFt[)CJ{EgX)O,paJz8VK9BfLVvV1J"`V!G5"PF/NG=iGH2l]Hhn_<"Vcb.VEP!kiL]SYdNDqh:;,~<).zG}%6cdP{l]Iw7F6Y0i)#WN%d?)mq+Myz80.Hv5Z+EGh_hde^:}ji.nRSY7F9C:UKq"a,)T)oi=(7DZoBq|^i|D<z1$F)2_UL>!@XfF/M,jc&Li{HJIOI~N4jF&%~P0#gL+Z;"%)D,We"@`J<5wnmaPIqf2?UkmU.,dcQ_Hn8fLTMCLaP7<4[hMbWzn~AeB89l4fOfJKz@uZ$(Ea6nNAzc8Fmxg,fo1@b94848Kf;/&?{094cH+3NIfFoE<|M)5g,.gx9c.U}lT{ePbHwR6mnh%gJKeB%2*[j[i%OEyWspnuO**8S#y/l%V^Y0<!ASkL5J,dL$&B].[4Z/jL$,N+`pP<y89yl1%Y"%[w]VpvQv1)6&W@6&eH"P"d=w*EF_@.&0~&V;g@BeG:Bg#+k1}|n9S0L?XP{^tQ}r=.nsBCnIS|&q1f5zT%Kn<:nt{I6NMe/H>v|G3k/TOPH:ThLpKSXm?F5<k7_7OM)EZMFR@vV;8zQ?8,eOC.h+7.m9(+Xi4Us~>t@8E&R>ay%h~,E.W0dD0ba~SP6n>1U(q}`@q%/i!,WHjZ/TM?[kG3/gW,^>),W_W<S6dhcObmr@:S(f8.NCEjJRyi$Ee5/eF*CP$C1,h<dU!zS[p%n@V)"!+nB_Aqv,{Dc]naJ<axUai;$`^np|)gwK8zp,{upQN_L:tN4V0mmF/E8au4ZNv&^@VmALJjd9(XS##7r^sfbh6%o,q)%PRyexe&|*L2P81@6XJ.O,B;Psl,%88@TG]rP6d0(fbae?2G/c%H/KgaC!P8BuqyFl+]OF.ZsgX:L~i.C>W/|*m6?"h,B8j*$!GqmPX98@qfpptxHB:`DP`^(eE&R>_,gexXm)N#$@=42P"PI(i4Df*YE8ZTkh<eRnhe^uegm*mqXq@B_]y8&k8fG!N_}8Y)u_pVW#vT=(U}4D;+;TPg;e70"?ADpu%_.m2iYiw4aWBb}u%soj"U9Hr8Hf&P&,+@sSGsb%7hP)%FHy)J"xf:wRK+*3WhOI5%R!ja^gxhCJM0!Ru_T_R=45bf!8OVAD9V<_S.R|D(z3bET{x8mvRRx[!9Zu%`bf66rLC*i"QmYRHJi;#3{HuKM|5pp`8oU+kZk_&eUe>vl_/(WzbER9?0f%%?hLmp*;}4o_@hW9w.!`+>&|:^OCXzAgE3c[%uMQ.3;ee%<kk_h!Vq:bJ".NGgH4eUCt]$fYc32SAqp,9#^PeL.~ii)WIB005y(F0"OQLgV^|15|y+7.p@>lL`eRTQq~u*[~9J]:7$[nJl:<dhP:"jUU*s8fJP8`nLew(!QhY~NLtzo#`tWME*u%dgJ#lb.FDi2Ss{S46AL(oM.][$QUIv/3FK#8~2/TS&"gzJg&Td/>eUQ0YEA?a@J10u;es?^mh=|JfTR97}<]qpM~mqHH%m&qd"=a>zjno`[T03PR05lDSq]$%A/PdO(I|%~O<VD_J`d5}!a&xJQ_9i;e@}4},}_86a{)4EJvgNUP,H;7*}yyU1`w4Y;{6eAVCX,v,<mAEEy*7N+>v?a$JA#Hq.Qyb:^q`7{pTy73H=z.4H2(}f|QiyaXb[T*,nGGCCZ?{e/ID,_A|fp6zCU;"2DK>zOAgH+Xz$JAPX>g$B4}+g><EAwoV:mAM<mA=~gA)cYR][g!M+{%Q])[Oo`gJiNp;ow/gX,uK@t/Smi0?l[4#6Nn<+a!9;4]t&ZRKR1j9(c[FR!;FfE3~+kqCwC9R{uZBXDqDN"M@gIZ"x6as)0E"1|UgzIZpgvIa9*]dZwM*cqIMI;TC@r90bu*7=Tc:p(&rg]0_;sr[2^%ZJt.3UR(obaGii+6@Gu;Iru;%w}[v]wq3QFuWUYz9v]qrd{d%#xHgtB%ZFY.iq/Xp@RF_o^*05L@"_M>RB5Cp06IIag5~H]CYR7q|q|v;U"[Y0qO2X4+7l!+7ye"Z[iqly+<,/n(8w@@fx4{?WA>)#MFF=.%]:;#io[R*26T(3b^eIU^~rXQAC|%8ABHtNBzwgD33ox}fY=hly1rvmknW^fs0:v2&PV9;{N[bx_}$$DB{JCnw<_JYQ0%2a!S{4MV"<)aMa7a]1CVux`P%f!Zn3_M4v[|_c`Vp_M>Lp.,Z%^v2DJI*_3UY:Ht}yG!"T*nCO0<BoLKC`w}A@:RVIdjA`d?oAbUKX!+e|.Y%c3n7R{KP9]o>Pj^l<7TiiE$acRR|f7k.,K4Uky+R+tTN3_;ho[)Kx|R%;MT]]ELg}{Ib4EjWo[z{Hz9iX)I6xC9LIwgt?l9Go#sot[yk*loQv`Y1+3>[l(J%|u=jPae<Ne:fLEu2$i&<&Aqto~g!2CyF+/ggjzOI3VU;ZffNalm4C%<VupD8kwUfeD(gbUr>&Q;!@~l)ncEF6VUNm4L(^d4ZxhtV=^v7U8=s4cgR[.ql.n]^p0#3P>dpT#@VL0I|@bnh3ibB3Vr]xsploB3JUvdzb[:UG?p}[#9nQ@O=i/8o9^}=1uq4to93o?i{BeQ^wDm{I2P_g/Hs=}<d.t.s7i.P6:Zc9D+u%+WmT>p0nE(:*A`gQ+K==_X.$@m7]lLW)nRdpHRdp$Fw^K84KtmwU>g5,Pz5i^q#3@pLsXezYe7dzz_kHL@K*Yy)2sBon9l=q7a_Iwr4!cg"9pFQ<aG)Eu}49>%S)_#=uiDN[d%/kB:F^D}";W^qMv}O^`LzG:hnkg^9n7nl|i"<XB~anIx90E_=#M{w]9vZaMs75r2q~%M_.~TEK"xu0&NOfLT5R$Xd%9=(4o[t9_Xa|onV31C[C@T`{x,=Df`l_h<i1hRP*R*Fg?D^}8If:<KCvS<Y0=f72SL_b@2]DwI8ZGnWLRYT7cT@F3/44x_j/V?sqnuAnpG8@lB`zAeoPC;)8}71#SU]P`ck^(d#*2I"8H_cGA[<C~2CLI%ccjaX&C:Z>Gn<P$G{t/FT$K/ff~^Q&pej/{qF*;7m!:i"N:05iA)_$=h.&>*yXmniMAB!I|p?o:ei*;amB,x}q5kxJ_[iG3Z/H[GnSm8,a7NqrAvil5:##j)n^sip%bvCvNL0,eQMKmNp.`/r,$+LdRbpeH::i&+2@7#*7q^kUBb=C;Jhsy_(WSgcP}M?.`Eig}5l:g[<iJ%wt`h[{BT@lyqJo!QQsb.&pEN}?ZZV[>>NG96n^ho_u,Loyq6mB&N#XdxBSJ<4K|if~BFT8YWTZuwjaLih44d0oyva*V(h.ynUOd5<,UhP7!63`K?:i^K+Ti^Z`mNQ&ccdk!/%@>GF6Qz{wJ;7+0M4pIDPmXj<3}#~u=CSaQ`N#8**iQCupYF]{9p:iHdZ90w%)M>Kp~94CwWK2QI"%FqU=tS%9J<w}J1>34M$V_?8;m@wQd6>TzuIryzlo!6Gk]{?x{bF+;ggY`1Z&!5|.^}I>dB:7(nN{B{:JWB:@3o~OVwl:hdz?AaS[+2$vx|O3h,V1]pL.%g~%k,M?gVXDkq4PI(%/,=}g~?CFlM}*%&^L|Yk54tsrOy>34g2.ozf&r`qTgy8n[R!K#Xn{!qAKbLE@|NF+p]@}Q<UKMRF!F`V#gUb/ro",V#I{5zI"3eUo?:db5`%W)@?NB#bbr?#hsq<X@*r>r3:m.@g@Ir_3b4`C&<40~6;MPN6]Z5tJaf[4,Wigi,~G&eiy*%&tb&+)6L#@>R@EAuh%Tjaf@L=yV@<,Q&$/x<4rj5o5Z}n~2@$D/mF<dKMI|$goFEPn],FQ.awcU%v9"g71~4z$pg|}E.x!*XhtzI8DkP5tx4:jG!,hlopSUj?},zj,c1h:&Vmr;;T!VKECotsF"0"Jc{+RKCW5*:"/7xH1gm?["rl9,+7RPD$27(0quy3Fe>C7%<{K`^[h4!WW*FEL=wO^:qK9U=[Y9^)@c`=z(0N,GgN[vw?J7gV+G8aT0s`VdDt.o/*oB(5v&P]X!`z2mV&*j=HxFaLs0oof%V+@A)X{w$Umg^6/y9fsQ)Qhom9#vuvjl:EG(Nq.#E:@s4vkJq2Zq,F$9t/QGD;@)T7r<#^HuYU!kW)Zb+GTWTq;FL?h1oH6)TrJdn~Q*kdhQ;!nNzSR/y,YBHAm0T~Z|l2U|3PRsmY{v1mx!#`m8.VW27$O2FR{8c&Q=QF.LCEHA|M+&,T!bvNer(A1:gA=$n%8agW#aE~Ew6uQ`uP_OI1qa6F&A_~^dw2qJJ7^=.A_,9)bv}M2U1{?[gj^~_.:U6z[]7}m!xfiX?UkoY;E17NPZ.{=uI{vt^;;1hyY*2Jm[7}OQfX&6)!GDg9SOwmR6QEpB%+8yR?):Cz}Vh:7vGv=sxmr3hHdK6EpVmmrVmsdVqV]BnfdlzWZpymM[}l9Y(mrv&,V!8COE..0T6v~XQ86gVk]IiEhm.8%]r1gk]fYVL9=EZqM#oc39t"=*wqW5I%}K1X1VWK*jq"%ji.<d.@tb%!NKSO?Qkg^Nox@GTYg6+[$k%H!6+n]iSk/J>=|7+^OxM</Vbab*2eyv9uUBDuk]xX&G0=@R%CbU%`p9aPWG:MdGceJ*s_#L3%mE69ukjQ/Pa^[?geyh5yd[i=1SD+y*4dw<>)YVbsCYdv@#!Iq);FT*{SuQj%2@OZ0"JAv[+=ZKS9_iM~1ET:sw)NhHV|eSk25^DXW<L#elTFyU46Cn{bq7PH4~Z`z3+G^Qyc&+)nt^`Zt3!Eqg/wfCVv%q_9q59oinUBJEC[Cbb%gm~nu4]0a+<pW#tw2G7Q9vmu$!{vrUV225~Qu07mw^xx<Us,)9t|mL:fdCl4FIy@~N[s!MvC^C^%1/m&%r^{Z|M!0]UGe%v&e.be99wzSUy~I4xY$uQ0*Z+%<0w%qooheA|49?>n#Swc_a9wXzy?lrXh*?!;9pnH4hgquZonXN@71}ZqNr`;NIB>1z,W3QxGxo~rjfU|$zj,u{ylu;L#@?TNJJQQPjkQMTa%P#|Iz29,!%jviCliv`"OQ<iIS+Hg;y/e!|HT5(@{="cx>~<nPXJn@#6`=Sl#.[Uv{1$7o{.uRi9g)mppKgy&pvIRJK09OUk9*4?PvXR)VC=CHT6y2o,kKY:P~AeATH}JmHb>mMz.dqjBq=pvRy#Cld9A[y@s*MQH.OhVN9asB[I_@S$[IvVu8:^V1[Sf&&=yr2bnB8!WvJOeIWtxgIn2niMH$oT;l)Iki^OK`Yx;F*RI;kdN=}Lj3VU,`AMqJ%Ok910IyUemx(T_%y37N3wtQQC@"%Akj^WO;_z/%qrgfz0QjMPPO^D8}yD[iq[zHK#&q}!]z7_L%ji24ooc.W_yIwvk*Dk<qhzq_?*sw!1x5j3B,y<EnxfOO1FCPC/sX0/v<q,HL5^Z)q!d%q!eGc6;S:%q!`W)+[RR_8CRk7tg7<(!qCjw9(qY!a2OyAf`qm*~InqgRw*.TFS4p<DkHX?ar(32EJM@wV)&#@];9&YsQH<dkNbr`;#90ZrP5`ikDsR4OtF>|$IiC1<6[fYof:>[[S[!YLaD,yanGV#6WmuXh|(;VQ%?bbCw6cJ>hB<+%qc4^kHC.cbKS5vMl:TlqV&QX|vcz^R;_!8e`ou[=B,7/bYE[g/=!9vW)jW3RETmNw|LYDX|tS3U)Nx{Fxb1?eIxb1?g*$n"*s`J|sDE.PX,Q*QeK.aIBM>`R57(Ia5n%XD&J=xHN8voGP@L4G/B6wGV|K!BKE.r0yiGM}lk1}11DCOb~4D<YOaK}Y(!Jw[r_~MIyW}k)Uf.;r0bL1diKwE$,)s7d[DYhsN,EgJnS>kSG*FWmKsd9]452c8f0r^k1wF.MmY5q`R":tNiy7_ttE)$grEQ/_bJiHP,(}ckV8/<y_6w.iNaj%Ua|`?!IL0,igV%930_qF}Brmt?}1Q$V|m#RiELv;DZGS;4X)GAxH~D<q`:CK(7T?0OFzeeeHQ:T1P?0E(Zb`=iQGKxziU"Kq$G6}o[=).C&jhwZ.;&$N?uMjJ2/Tp[>Hj`]hj2X$gVa]o:522A622Cgj|"A5KN<|s_"C=Hv4>.kZ>&ne&9OGus[15%6UEK4aCiKq61{qQ)50}/,T&tC+/Ddu},&{Dn3RV6?8t7,xoiwO9"w|ff]WvtCePk4V@K|^>7e+Wp+Z_x#yTVjH3@yxoXo[)Xb{i)/Ac:;TN[=rd.),/;i/%O,L!Ujff?p}ICVwY@yS,fY7qLoCO/&XJ7RksIX!yGR*PAQ+VcgV!bP/LBN?THbfM/&9C=L.Vp,k1`);kW^)mi4K7.}Bf8hFdNo`DkBlDt_=>^T1FDP=t%"["%A~]!`I(>;`dKC),$5"E>D)BjZFFvU62A4YJ;;dbI!$ks,vUug0{AoW<~S68K5?ADXAu+`syA@Vn>[*?qK_;]P_[B`LyiT~0}`G</FS)5$hh~>B[R17%Yn0@G<k#wbzT%<!];WLk2pI8>i+bd2[p>W;~bJTMiC.+Ee]8[DC9?N_Sc1KREyfls,=$@Y4,In%%[*5EE.e3xaMtT(?@J_VEL1`m|n!h[Ppgt%%|1cpg=ZoFLY`}*^o$`U"gOgA:][U%iRLCs,Bu@bRGv1^IE1)*"oi&WQLmybla^lxRo!%qRhc6jK3S+QJ]:"UXesv{{[U%^LtJ_MBi~D2u1OBe,F4}@z;>oU51e|+6!ej$|QNeM84}`E7DCPaB"G~NPM4BGuN88E8w#MzT1k.[VtSXtHeWEOeW=t~c%<=1dwL,";3(dUl2z)j,~;R&AT8,6c!G]g,hS^U%NSfI^k1rniL_JN=&#CtL;cDK]!GJS@$;$.`fh!Q_,&)>GHcY:8Y1a5fEyd`Ns!KYN0!4P>A0^N<bunK`{iIA^g=+$6!Ywkof#|/^~t}tc%@Io`9O7NsU~D/k~>#0Mu=}"{?]w)FW<>;^nK"_QcOJP>uF!oTR.IXVUi1R~bOZk>+:G%!T=.W&;4MrvH`!XT>l4ig&Sh_GrfY6Y0wmyW=q0zV!fIwh:"SYs+!N8i8:?mkxM9"wlDse$T3kVq]Pxxi>agwH9_Qg}H9_u&WySCM99DJzPWf]d^<3WFrPBsT_Q#}6U/e!"bQ@6OPTWg%S;>/3mE/)NXIbJLu#(S@BuQf_$rotdyG$f:"zlDJHJ^k.uJKB}>"E}^G|Af3!HE]g@]g1)=#Q"|[9s(kN`9#U!ktSUvMZVEUq[Ezz^!jtcH"9N);ORMiHjY9j_XJbd5z^W=C,O/{<ZaC7,a%eRu"ke8LxYi}?,9*8oP/,V;NCiu>Z@4w3>[3R:6VEHR6o];~awp<Dx;U|7d6&Y!F@z!y5qP|fMhb;8"]Sn$lY+]e{,nPUl{Aqkzh10MbV!;Roh3Xw<NN)wtzgxw3cATpga|hLRQ9A`kEK=N1?^fWj%(TMjps)za~*PFNN4&yye90;|hOvCDq`2(:585@B:~WWVEw{fW<UAY#,,Fy$dW5}9<UXvJXWG_RB8vNP7ihqzNsz|GEy*J$}XV6S:CQXij?dXGUB9v8SR>EK]g<dRuSz}pEUC+jInC"O>_XvMQ$uGSIk";7C#*t+lRh5`z3Amq7aCq8",vwg?w](Cyzz#]^h}f(1,_L*zw{4vrY1cqHOEBbuJN{^_kq>R_7?P`huG!C|bR=u|9WkO>a]a*d@b{4(}@e[MO7<"S+mhO6.2;r?Kh/x^YEz:x4~R8amD71J)p*S8=?7%*`s"MPwFO+dOIZMYxoM5yy=+m7;hd,tD03pwF8_g<,ii"BaA&wn;Oo0SRLMm|y+M)EB+T^q{C%W5*H;7zc8%DKl`bp0N,(<b@/|Adz?z?6R(5lx]1q.bHJN5+D0@OvS$2MIcH[Y>uJz7M<sxB0[9nJTmWG2"4%~QwSDDL_b9)C,sHOx&6OC)(;jcX`/h1AQ?Bw(/tA&Nabe|EzQYIuG>ot^N)CX317778A3"<7uf%9.D{GOSNL*EthZ"MfNPNh36zb4<ogK9grU#.2B8lNwtGtRfK2hUq&fy?6BUGfKjq:b8oZM)px9O3Gf1Cq8L<R2hgN~<h#w%c9S(5LhQB52n5g]ze4lFM"`Jz`gRw:e:a}Ux4R,u&c}y$#H*N1>d@/UJK:@`FzwW^koJKXPLP>6UMbfS56^`w.CoUIVgC^N1fukA+q13T!+WK4Wnt/gX7@z/?{0|pBg@Gl?!,$[<+?tpQfRZ6(<iG/<[/Pu@w=Q6,%>,zd)u>p|~7k&hpK,OSu5J>>K969nhV2I8omRsi|^8&DvT{mW.Hup/L]`s:g*;x1c:h;+/jbhLi&O3Z1+=TWOkK!1Td[xcy6T(3f6k&]"%@=sz&T4mH9iZjYZSO&qb?>d}^U(<QvLF1t6kT:jEv#o<z/N`4g[D*KYF+Q/*<(zJc,LC?5#`p{,@tlj0>9inZ2cL>,&yH3$5785uNW8[G9%x>Bea~9w=<hJ=+d;Cncbg?t5]J9gXO3+c9YFU/SHC`f)<*&7nRVtD|&BXVh[yr$9h7*^SgIP;H2QPk<l{SC_$rKHqfy=]euv1_r5^S/lfv7oz^.83oZ;hpz.(&x]8^D~0]*9zFoh>;taF5f%}pF]jO3^8aOZ6%u=8;N:U9`PtqbfR!J4oWJL/[0|xJkpX@.,`)wgs9aR,,t8vb90{i9zgP~AYL_)%!PG{2>crN[X`{g%sEB^4gjq2,]z9O$aRI0Q!KX?3G4hKzzO_S+q29eP;#]}93Ofh%<!/;bN_Sue"!#/b4HK3BMXd.9qu*O8v7tlDf=0Sq;+~vmj.?3h0eAiXaIikK,Ndvnus3M$q011s|6a`T9z58b`{8iKX|vS3d0Fp?vSGf*n&n`vCf+nRV,Dx9oipo=R:CTTfw)owue9<eR*KK~aKB(Izu0nG/E<mwAYmS$V~g.HTR:O($mwXQ:C`RRN)6wwNxH%jnrO.e<:&Ppo4Q`B<X])mOQ2^@(O$2IG)nd]LPMESU@j*5CyI.toPa`B[5!w:;dbuiZ9a2+v`YnE:L7E*F4vs3@]{H?5AE_P*=HHQOMCdMSXibS[fh3oUj9zzs}[J>4*QJ0.[!Xi%P4b%jxQza)y$%C>+sc,[.[x]#YsJ%ff%yZwi3~%PFC19K<~ng0*aV_v_.l`UnzA^]apm`,eC%h>o#.]~=U!cK]4,GpY7;O)UKH.@txT(=3g?ou2O!8%PMzJA%C/5,]+y]ek?T^hd`WBAoO"PA~Jw()lk_,h]0akAExPQCK9P8ZU*t[w`k_w/Y=0]D@{n>D:LGA.Xxg{yq@$|1,^`7q2^op:R@k&]p3QM[*=VYxg9C=p|QM[0(WFj@Z^~=3t@:Z5eKR`]R+}DDhK^mBkhvI3K*@=z|95mpYT)t%2QGRRC@M1xY=@+_#,dC#JMIm9:l`uA_zD6g(8Fw:dNkLlD5JM5;EA=rt8sNsCbFT!M%"Yu1rE|@/g4JT`ZVD7dFEcD%])?$heM.HU[S>>9<6RdKXKh<APUC$k/,WP[h>w[@WjOM$f0ks6pB,0^@&X(,{PosiiJPWEEF:;1`[@s6%@N)UXG76KUp7UFVB+zo4&WuMldNXhpB@TuyByU1j$i&jK1;birw1|}9S6%}))K?fs=[{YPh%gXT9L+&q84iXgA[^DRIpY7rI1u3HzBOAn,|F7*Jj&e_Rkp>x}Bc*F{,<7a`^K~VDlMk$,q!@<Zh>@fYb3L3tx+`#`9K2H),fouo6oJYK_#eH2G3OKs,amyH8R!zK3K>[Spgu;=MuMA/UZO8)OIxjV|]*]5hCSzUEp9F$+Cv2Q3|N*E)cO5#t6p&YsJQMD,,{Iz`zHppzh8M#b6oYOn4?FM$zK";"KAL`#Owb:EG8v|r)]G+)]FZrU21LuKk^J5tCdF.5i6+fp"0mEpOs97.uO#!a0{cr9N[D^R3hU^i|Z?F*mfdR]D}+JUnWuSEt3^1~#C8_i26b:q<IeY|}x$zuTtRc[,,AKLfV^(__`|T/u9)V5d7Ha{RlYk.2v&T6oTo)0mGR`lV,$uCd[v!3Bk{n"5t1)Hl?]~bk})|>T?&?NrI9d&D[$w?i#o8v#dK<hr1Hp:39B,rxy~QGm;B*FyhjiWtri)9See,1ox@FN;BCkBs^J78bTg>[S3+9qsr@+NM1J3h:vY<{q+m5Nzmab*c4kmU#@q<~G)1aJI%+txL~SCpT>:hWfz8M^_kKCfGi,_3=lJt#@;f=l`|n~_B*9{y^otg&0M`$gL}HKLx(x[e=T<oYv.YggdhL:Y0I:u3)Pk[HjFn@At)b1kKla@]6Q<=*`KPNyP0B"Qb^k<Lu$hV#f{c,+6!K#0O=gGzBU!G>!%]km8Qz+;m[4.f,2+<40yDtx}vWT01o$u>]g~j!b@odt"9@o2%X}QPw25"52zjB&[g~a8l")j/J2=Lum+{o4cBa;]puLmM]!YuI2P1t{6,_gKwCnWLO!ih[.=Xq1irIn[4cd,e`PCe,5`*u=[lpC[xizEUya<Q"?LhKn^v}*Z@subnT;k6S(.;QmJ$dj!/OMk_5z4,%!1EwQ,{[lGFkv;iH`D1vvY9Vq!,D`&ZeM:Hvzq.05(T)p1q9Us2@/iWGKI`Qm!aJX|#y8ZE%kEQwb[1v(ghmAXANRXJEN{&H%OEL*l:Q3fH.Le%SM~KF|P&GChU6B`%lK2(Y@QIxH$AFhhf!4._;e[}Zhi$:w/>rXPDx;>!5]<qPX2?:w/>M<]$<YD7VzT1<|?*yZRUeebr}&5)H]/HV1$ogKf3%+Stvv/o:M]S?ID,_A??q`Myn^%+o*kn~[nLKwg[Up1.#af9xn^G{%LTA,Xa_mwa5nEK3>7TdG#7lV{C~}*ukHYEj"6.GF0OmTFhV!8g`}X:g"l__}b75A/>f%t67"P3.]O)v?c%T{p0&6O@5~vM))#D_uknu;}LTRquV1FABzL+C#$~_X~5{!"Y8Lq^73x.$OvxAAXL=h"GC"*B8F)M~h@_2u!0PW@AyC,!5DQa_TEym6=FnR~X$ABAAAAAAAAAD>I,pp"0m@Bm2~K3W%VGxKdwj)ltahdO,Ba4jac3JcaW[3_^t;Mbgi:9?i7|.GEK9JZ2yu=)k=PFhCqdE{2frcHR,f0o_SQvWa.)9cV?q8F9RR1p$>DVFPqo[DdR42=Dsz0j@j:bg1jz}7a}Tdir:yZrLY>[`xF*^X@~ae"QN0jw*boV33A"zX>LTDB%CJ)ZS564wP*ywupJV;D65Z)OFHTesVjz73u}vPvawrITyg`0^JPI!i:+d2JEC_v)Cn#|zgIB2ZOC|Z$LE*(|b7Ol26>fgFYd#piBiv,4d,[UcQYDk,HY2rEw3Mh.fjdGp50Hr7)[c!!08>stR06e1S{id6c(ie2^@4SoE%sDMH]I;myBB%P_LQo7jmAMwtIYO5{t23r+P*mI,OLVN[+y_B}*tn~!(q,{3(+egz(a%KE[nw|bN])!dhR<M{pNcm8=P33+pNLn.9`FBEGnV#<9mFx3^K#o$7HJ?:2MNPr,fX$a7XPbPf@e_4"+e@X)I$w|0^!wxt1#J"pI5;reiPTW/yUrVqPJ3ZI$nNLt,GiMb<vr.n:V"L{Y$j<eaW@5A:go}D<b!a~N4?nUpd}}||Lb_58<F@1J]G(w|E3<]+[8r>h=9T%4uJZ%d)!Qg(>DUWbO],]Vzc[/mOD&(ERlp8TaKKZ1{8D?i)OJv^*5M;L(W)R^kQbhB$k$1hc5RL8Ivc*xpN2lv265BkNHsP=JRxhfM&OS;f5tUi(t,!DAL7/zM@_=YPf`Cz~mNah000)gE&#KK:gkNhA|4&S=kPF3QTWMM|Fh;TR[2Z:gpq+6jl&,&:c]rR[i8M9!`T:*zx$6)/hP1w`m(J/ZFPtb5?F!.WV1jOODOi}h6;;E9>AN4#]b:uv/8?re/I.hLFrobi%e!1aQm2%uUqN*r7^iJ&[4Tvo*MTZa]T.MV)S~D.tZmEc]#I94J6_ohG7cqy[oXJk$BUqUqiifn39i{(duRRmIzw7+KuIYYVM6#b:"%2R7pbpP3uWT&<G1Rm7:_GqV+=cn9)MXk[<16ZG@U6#7Ejt8V&.a)Nw6(`XC&{?ctNZlL^bav)n$?Lj&+ZrkR]/7%aRqrSpZ><wE,_lgpE&!V]pCv>,8alx]<%sobnMHBdJ*dJ!]2~fU4oV>/wy1uet7N@PUAf/_,i=n|O"hXMz0Pq%ioF(_plj+@g^Lq2f/@h<CFA#n&S_rPfy]rbHU0MCxw_iHq2K=]Lf"+3Syrj#Hxum|mS$_u=r|:;bE_mfP8RYk+evS[F}Gz{Ta:zc&]P6tPYGNA]2$^WJR4T}ysa4sFXZeS1ABvdHs[n*ywv_]#DqLIgLfAWVFXcmzy[ZCP@F4=sV?AFo#t;zyQ@:O*|KhhH016Px#[]EUCf&WEoLlC|,IAbRE|D;HP.duL)zn)P}n)Ft`kTPXAg"iI8+WM/TB*;XJ`vf+vh?a,@v^aMk^s*i;/Pu,brNb.|^UFx;iHvjiOP}5]$pT"@t6vU4dLoT?R%zPNe<_GSly.7lK2Ts}r+f]#u0kb&Z^^udBU4M>o`p7YoCGC[PJGX)b(&u$ys/h%FHNTwk`>Vh&.?S*OfEfe&)BU4>9;A[*&TAKsp>>;<cu?,m!4.N{V(E/ZzxtR_u[P]FOY_UWbt{aXwPf[O[%n|X!vXwxrBp,X!&7@l#;vVm}bE#^5LVTSw8L,<#t>He+o)>9d97~SiCQ!Nm#_RtqkznYA<QbTP?IQs5|Lj2oBMTpRH&5_w8uzX3gUzxP~`7L,b+v5fW$n}HhMMSL0cS_S="0vm?vika6"FZo.296$;^#7}}Ih}ET)}hU?2H4DV>K.v@pI,/UsnP&<M`pL$/8L3VRO<%s>r};"]mQ?t_6&GX?iSlWoD=CIrh1YcpN|vn4W!O<tik:1Dc5PC8[trtu[^Bp0+3MC(NH.hoc_Mt>U+E~+YfwR!17;%*7gD47ncO).<z!U6(k;.`R^|{ko|w];oC&;iw9^Jhf.WLXV(Q_KQ}#_k}Yjo*%UiSt|D4F7irT|ivn}{|(dz[Q}x_`kQTRY%]Nfv|bq:ICU|]$V;f|.)fPw<DC9f^@gw1g$L|yXa*|Y)|ayHk`bs{~q<3d9In~fSe`>4mMt,4;m:tcGav6tHR<IZ5,oqxq$*Z/rVrA.=uOMvK01l0nfqTm17/IUBu!?HPxX#lB9(UdUTFby"WuU`Wtv(iX^0L/MoZ_ewm}19b8{CQs/X4MgCJ{(#N&._&[Q_Q9w_Txxcw(8`/Qk|}jW[%:M;0ob+:hb2lnJ(=O1^M[PLdKc=7U.rf+!`6^HH4;Y%^Bhz5L~|J<e9<<1Ee,Gme,A"MxRZ*:t#9Gd~U&32Xvh/_d^%V`V/,PvkbIaxh[H3[mOrKn#=rH(nHF_}4*|GV`9k4JmZ$f#1SEGHcoRMpI}j#3)13rI?f)}X#bO9IYx?F1Yv/,T[fWT9]hn,wNt3(^fEI6Ug^!I|.Pqc<Pt;H4&~qyyR5Hnbc8P4KsOyK0t/43o3.~H|a%(L](ky.LxVR#64L{XHL]"W,#p"hIg>1@j|;/E[,|3tR%;9U0="Zb"9Nf5Vyw+Cm}=OD!jtB"@Vjmtx5fO@:]]6E,$4ANaAQl}D,PX5$dP7AM*a<@VitWam=DoJ})7!,:)a{*F*~QIdUhO,IDU/QhQZA~Ew"2:}n4g#d,R}70Lx$63Y+BH[a;|StsHm{GSNb(52H|v}0+V(7cMY_$ZMt8HJ$ThXz=)272rt2d"J~{)RERBlLa.Xu]v@64yo1CCt:F@yD0U7K:<=@HC@x!Ap``BDeI9~YGgu,+dP/^?YOf79&T}&!Q_i0YTh,HO=ER&}J$"cC!cvF!gS:vXK}<b6+=[y%+;eMz!j2nAXLcikwV?;>RMC]=zn(o_XXivK2GfD8hT4jU,j7"L<B=84>^_~zj)<tNIkt#~c,e*D@%O<Uy0Ld%_vpoN!bqrmxCC0Bx/upKrVz,[io"}avBVWjpjz:0&UTFs~L}:5{UE&j4pSSfo{OhA7.T5~jLan,^Am2~gRGYL?V6n8zR4cvV<O5sJj2a).1TXmz24(u?.$xzzs:stG5o4*hWqLf<}.u74QrFIt@TztxY$T!zMW>=ntHB6Pvb%N=7zz.a3./(C~@?WY62s%}LaqQU<V<ew7cm}6{]0mSIQcLyplqm9B)B<wTRVlh#,d!JJc2!c0eG9Tg!jP3RD]{!!(sxlV[0jnZ=V8{i`)Uf}6W+zfl2hHUE<OQ(Z&+P_#tNa)YUE0uL7}29F5iDoy@xzUM.;Y(X~YhKwL,A~q=Or!0$NF`H,L6um3":/QyP+Bey"c.Un|$O1YUL[@&<]T&lsn[#gQ({c>&M?>jVIdmko]DMCGw.?b#[>3*L$F?ds1{}W=;Y[03|(vF(|vA2HJjQ<9X=;YC+*eFffDXvLJr6yWJsXgWBTh)I.;7fdo:;t9~&,)v=PblBS^_k0:N8T;{"=:~*tK7{C3*{H&jeYeWu&guy=T>BUO|2LMNt|so@!2v!9$KlN1xSR4DpX~pKg>Hlzbes_;Ky7&Fc$BL5D{(!Ts.Y{1:YA>,kuZ.]1SRbfD;r"gzWNSQL/{~Q?.lJ.w0mTFnX|>|^oCm(um2PR6``n}:6tPF=.2<:BdTJv;Dp1[[nR8RD)v/qW3p16P0,^.??3(|A.ew%#]Mmp.ZwRb47j,5GCy9Sx`,O:0%(vTh)8r#D)H6pq{jN*LtSPypRh65Rfh(a"j^qH`nQ@B`4XRNKbYR;3_+$tzR"@LUI9(7m(cTvxPmHp*#8n^#&R.RLN&C+z$lcm?PQzUXB[DQ7[7gKQ.Z?tqg+/{N!P{i0IL*Yni/9p)EJxx/bFisYz,[@}1L.|^3+]<;YGOg>(ru2hR=86)q0&.23YT@c)3&,n2]O805,c%g`0eK["f|op+~y}K!}Xfe44P.)$nKQiZd0*jw^9HZ@K".#e9qka1{KlRFE[6GB6!N!!5}H]CRzq$U1wsu,UyZ@~:u^Q[i4K2`rAiN)m(]1"G}El!a@:ulP3Muf3^7`e*9FrRRn3WIk>r49ys!)6{6IS$z{M#e8n7j&VJVwM*OGKr}jL%y$I"nIRY(@1P`8TcqHS7/?0>zm[1J&2|BX89^S6+`)?#yS.OMX<Ny?o:QJNd9(:Df/|i+]T?QYh5lw0O@/VE2g>6EwIFr7K*`9i$og`NP0!VwItJjW9<LfT.dO+d6y?LLcb$YVu:3J~v?ul7D6TnX|LK)V]NQp^xM9`jgp>cYj]9X[@Job)$Ynskg:DtSJpk;UsMrQJUDC}K}dnu7Q`*:<`4f*9%BD_X[1&6x!{?W+X4}JWE3mq_`$f94Vp,MR_d20.]:{LS]T9Lu]{EFcSRVn^f)en^8EG=ftt+QT4.A&qgt769U{dF6$U(vv$Mb*r:x`7u66aH81:l(edb5P:dXPjMU9uz>O3=<8XEa8?+:%XIWHPK*Q3ync(zE6u"X^ub:IBfy1E2OR7z_E2>Vixt/4^^:@)Y_h1)e>H3weG8I6;,hi_A/JCZ>(I,_i)ZDRNL;K;B4dP*vE:stJ)Qq/l})$EwNG9y:qX?hv5F=6,FBeX?{+$>J><mybyvu=G:FTX)vx5.lPZp$o^Vw0s.vZ3t0g,Pk_Q,+IsX)7dO;Iy[h7~~NssVD"RlgDK+d84FvC~i;?T3=+=onf@X:l%Dnl$SmR1>Z}S5kl.wa!`&&Qtq&aeClPVsEke@Yx.q<XBt}Hu8~r5U6VGlD`g%?;q]0l#c*?/;v#6~|19[F{^3bLp}H7%Hk21#Fpv$GcMRl]a|DK<wW)<;80tWWkWaZfGvTuN*EJigm``:Lpi<4=(/58M+sg+u0fOCN/3TX|+;Pn:}ZQS{6W0`OWe,ns0?3e(+H1n?@6?T9vjHe<^UH!k/t0%bc!OE<&B3}n(8K<r(z%0T!f[zAYsIp"Vc;fqYYf%P@a[tHjBLPt&y]6>GYs$zgR*t!mr_BULtD>zh0%h2@)3+3L%H#xa+l6at|.UMG_0sDD,YDl8I;_{P1KM_lotXE6hxkE^WY1(w)Us)b;4R;X8AN8_SnlrYEf)3uD$cr%VjtNd&!I0f{b1d?adMSrLc_Ul>.W>mjv7LpOKRRpsQ(,j|kWnnIa]2jDo3<3R`[Ke.5le*.2;Mnd/B6{%e;d@k]Fc>sdvD(Y|156"R8@8ZiYa4NNafDfaP>u{<r:/KW8=[aK%;_<ZCu+wM,+B5A&D!Em.k>(p4wBotE&#mD9<3QTPW%G_VfYh|c?flHs2BGYVh^AzFKj2F,R/=l}0?hpSZT`q"~G#P[=rVnL_IFw|hW$TU_4P`R3&_u/hj2%R/T.z:TtN9D[ziF/zJ19!N+V"/=]8]yCzI/<&EI_%]O#w.*{`nP7oD[J3TQ&~ALmvlO[}:yzt+S4ZE!oy}!1Bp(geh]SDK4gKD.&Ht~_o"S6!#7|.|kys)~k6#u*}sTPbOr5"TO_oIqaSeRpQNxb7VYE}ryri[rcM*|c3/6}n5E|U>,{v(;AN#c0njZ]>8Y%N8`fpdM?/se4z;`lng2whZVj&zzJ6vDSz&})zFKp#P+Wb_q;txKD/!5X]&X/bfBwgUH;QX&TKHO$HMe;RIlInr,CIgb9}xeI"Vu2j?eTEvUmB[3;NH6K]WnGP_QiF_NyZ9v5mEVYYg4Idr!9e+HrvhJ2kP@bf"MxKQ=(kDlX>))56nmz$?<TnWrDtbQr|~[@=Lc147`CeVoPBj2Om34MzIAU<6WYjvG&4C0:&fxpO)8Pst?FR2EG[&!49Tbc<x7@GZ|<(#*W8%MZvTQlQGF._i(]&Ye?lcli<"R+n)nP`${Jxg*#Pt*&S?~LV^Z8,2KlqC{9/S#4GEZkG[Z$+fDeo$r]Kt:=XzL+szuhaZqy"|LB=J*G.*wX9y{`v$^%mInk:CwDvJNv|c6jsB,W{A,^bNeWBK|BmIa^@(nZ2+Xjrp=~^&/J;_S`7L;.^vxY`QMO8Fa.Y=K,+N)W8*tztp?]Zeu*;Vr{`6@l/f4(eveLKHaDVdN8`|RYvmGlOVTO0EH?U@YaWEd4Ic`M(c)Pc_"CboX$ht#l^D)N!TTkeJ7n}N+qC<|f_n)M%#h#u(K^{4!ltmNT0t0Z[8O9vT<U4ScIow(x>E&CH(@h0402rmZs"VhOn/uViVNH)cw<AFHpu5KGHQn/4JbEP5~}IPTYBxOhNGg3j=q}NVY1Cw!*:PH5&=!D>UMtMI_cRLhwm"J;N:%u`4AZ(Ne47K<gl;c`#ejOU)GR`0m+_9Rp}c_JB+B}rh`K$Ha>&x%RwDC%SmWe^}0v^}@3w!92!qVc4zg$f=|y4Zo3+6@nhN%5c+~S&=VT+D[w|o2z^1;_r=TX0c,^X[.Ga7|]N%9W$mMW`ML]Z!7A.!!EomrXUTDsFHDUr45YJR5?54)@Uv",sS6WRNd.hTROY<~;C4&qS_$DS/3:?=;1#|^5]mPTYf9L+S/`Gxq8C<}1&GUNATSax+2=W_d[rnGNkBPavNF8Ca:dPJ!yFH/+h]GH7Tx*(t|cnwtIgx#%fq.(QTP=@1wMlj(G"cv,(wPyl5:VH~W&]1V`*ca8%Qw^5LBr@GP0j%48aTaA7y}yl4!0w.0)clkS.?"zu^?*1w^}+VF[=j<FGhvf2(<(HhaSus~>uYq,/gP[)9BRkJ2J/*vMPKjTkn<#;,]*ydNKk4p]5,;T#KJ]!x6OpFydFV&l?@`2t:jV?A0$yjw9}Bzw*4jDV`Ou]_b*Ou]"_3`8%X1h)wM!%l]V,N*Eh]]hAA}QFYMs~kIhZ23kB?"BFzEi1:Yg/WfdXidD{Pd1|6LZDDl>/uqio/8Mv<RM6=uU4DFHAR#fwVdq.sqm!g/Pv2Ol_K0OM~nFX%w>Ffmql$QZ!v~;jZPC~4{F[R/F7LEOD.mS3,9!0z3i7nQoLymFif&,3|:R&8;<?$/FD7p$rGeMHyYy;%Sx(je}gYTGc1#pDOK/IWBgRHLIS{D]iBdWdd%vz4g7VW_|w#V0?3/8TVCvGDVanUUR@;g3[)@g`CCuje0U4)xu#Z0VHzQ0s7#_6,r3d4#ZGx1<#fKY>n<VVx]bIA"wpJ~D)01a]P4.3:9?2.e:<i^t69h=2uHi98egSw@X(Ybl0Q{_Z%$eu,QNK@Wl/DTHf~sN%")t3NhQGnTE8dIf%w;=_`^e5PwFK(=U"6sjmrOT#{h;QQs|y3aBv3nNs@)*2%6QtQREL[y%O)r+c@MOW6z|*a}Q:Hr=,hvTT$"7BvO/${7h,J2GFUv%nel=oS=VQ/zPiFHJV)/V2p:q~<bGGv;{Vjha|dm0ZeB>|L!m6tG>IT(5S</rN#Ik?G,&3_A`2<nNXfBV*sXTKL7H,;A>w8#`h(R6}M.>*F^X@DDj>S>o1H87z/J;l^W[:6$bdH=KJbXuQ"B,t~MDpc&E|=K=xam"IISm9bQ_zcB]$!FVg.y$[u9?Zs/LqL37VX:#:L&c*FXh@}YSe|(e~qXKLiY;c%vWBoi.0..nmU[a](v|t]^%x}bN{IP25ZS9dj9Y/R2/yXTX81R)c@#wNO>~BX$N5yHTU9PqC:2m>WCwn+Yxw[MmpeGa*GeB+>Q~*0x[,wt#<~bX1PP{onKj+!o2%9{rh5|Es({vgn}K5pzxx+a`(2ew}y)m_0Hg.be_T8S9<Kbn+nBzzlVYxuh<gOE!l#=x^y!NT.#yCsx#{Hc<SWQ&vCokpKvo+tt/T+x_1>UB2D@RDV5ev$$)Lsmo837v_~]!x7hF1T1hyub&<Cd*]oSJ@(^[&?WDE{mn?+I9Yb{O"vOmu:aNpv[w6.M$i&lc3KUSHO)P6I:D@H:/uS(A}W.ixh0,sd2(,pg!hgU@MVoPY)56~tu:Wk|5"^`vTgp&~66qi#S=Vo?R;t/NwH`kK3b(UD!PnHw!MT,SR9;n"3:gy6oR74I6J_.~T.ek=lYH8!0CiaIYxoJys%=Y04R`gG!HER^Bqz,atV($#kt`Y)S4exAzSp>)%OTY)Bs]K9f,?@+BE)2LTM=R]]gl`~`ktX~^,xq|gPyy(b/!i(sd9K`^M7;b1Y+k<&%<L?YwP+vJzF[js1O2_70_9m#Q{RE::_`_pPss~$iO"qbE5_,4r|*lxal0k3b"CreMXJmyHqM:R6XLkbU__Z;$B0SW%@*o|]/ej5X@.LobfaTZNK?+0J,4"rYK+VWPuPWLzr|"Yo#_K(D:e8Jp3ulkz@s3>ND9j`).Ao8M]8/bV;mz^kMS8Q&974=+=g+lo#aAI^$6&W$|6]J3;EZO9u_&KcW&WX18u(Z~]w6:SGKR=p2T$[7<QM6EDQ.tIQJ^&g>_ZFyY0D!K,Y"4&gP~T7R:64cvtd0:#<<,Z)_8/Y$w:EHog@5`$:GDIz[A{[.`okH):]!|]v7~U4`Wx+3HFhok[j~.Am#g4^N08)^Ukcn#/xJ22C]=VPXELJ/&ydnuH=)}a)0A>|sEB4&@nYP&SA^%ytvhf{?G"YaWD8iDHdLW{te.$RggyF(CO)5*qx|,HpUW(4v0mSWi/buCt/:a?0nhw>:qcz5O)mH93i9H#Nr5nY+,5}&!PUR.`cQ}F|V55VDei;R;i?l,:Kd+G)In7&s}x!jsO"`TueQaN<#^iW!>Lbbfto#YSIuW9d~<3`g=qB.SF,$Q(EGX&EI?`^pu1Ek@HbRADQGx&m)}`>pLZ!0q5OJMN%$Ut9,lf%!^#f^+8j4jO(M>C,MurcW&i5[xt%xksiv/v;vHmLy@t!,X.N]kl^?ll=St#$/>HC|9RT[p$&Va(wW/fZZp%h#QsVf1`dg1+H+UR]jQRbPXzV[aN<d@Ua=<ag>5}a3UO@8L%9{q*4fz"}DGd4C^#o#K`Y4=3O44*(IpP1%Xzeo2J_oOUEY:>!TU(wpS$+@+CrqIpZ}Exwl}S_75u+u6Pa;g%U_j).f6RN4.QW2i7t%SNrOwA1zNa%{QA}TF18=/e>^6M}#B2z;S*{,?|+Z+_<0jIJ"YP/|8(^TbE1)gtn$YDiHWw|M7?nQc&l;1[/Cs"^f35Ti0y|e[g;dq}ub3Pl^t;Zvb[c@@B&1VWS"9c^{4^#EyE2$0JP0Oyh@$]RV^[,NIJN4l<[_JuPl1QK9NH(KnOY7(ccH#?NaHVVl4W&jns<+2qHOb125dwq$>{#XuuaCcN20,t$/)c2@WX7Fu[@`<N^tj,:Pr.4jUzzzXrv8IJtgK2KqckcIC.0ae9DA7tgj{YSH?Gb*dRT=c,B8&&^ny)`mEY.Z3KYX#8zcS%$Edjlisoj)h3)`BZ`mhi$XcW?!mzwkro^0kPX;{,lfkaW8eFzYO:.&rs,cenRb$$r$$*RI2HMA~bl8#ggQ|A3y,a|eDIy9ruWf!x@8%]C5^8?5D{]yFv_.GG&heXU3IuI3#zz*/{v8o5I1|5e`d!Hr!S6i6zXe["cRUOx_0]q57n!&p&!$&A4&sVtz2Iz4iW9)=#obm5kw|7]UzmT^hp832Nzpet)0;e*ZLH*V_0+x,<"hy0vQ"qf^>TK[!pTx$XTVOLHnNDfg"ci9SLe_]Cr9YWh*fs|doY]lm)aPwQ}%P!~pzfh_lyI<K31~87JX_I|[*x.s(N3Oo+qFFr]7{G!i;1V;euexQy2,)d[a#G{i&jOx4|@1L5ve:/am!SmNRKa~21tNCr_9`=BTgaCCedjbN7KLv_2_T7<`"E}`OjN=_y;A(*duFv4lpK,yN=CX_)PMnU5cW}wAdQ3cxlF7&IUE,4iXa|?%#;12+uS7=fnC:Mt+ZgL4,A2U>}A9Qcl,OsuD@2ELxmTOBW,x`.^0PzVv=#6i{3cZ5,A&kh[}60tpAQREFdRVngk~!;Cel6WO0?P~1TsLNKbUyjEC0<vj`2P/Q$e[pd8,&K@ZKUS<<7YpPkJtYCv<[@C[WfD)D?4z:)rg@0%N>`t;eny9ix@4=8`%K?IF=1v]:hDQlD<Xq6Htu.19htb)1FF/pHJu&l@GSWOwt"0+EIHJOROzi]X|6p]+^/J%5MVAOfKT1rFa%:~z/vHibgdOGvx?bB)1K|x%n[C(ZfhiS&An6v0|Hlo>$3B0:Ae.I;lKt(&Ufv,/cTazR!({%;x+n1<^4C~6MrEODsmVn9))C~*83k^U=p%TnG2oE5D5?7/2I:igYZhgrY}SY;bXjO3KXBEXfP/h(}9e7mISGQpq?E=X,S#{UC5$y.KYEXDv/0`qEcN_Q`D{48d,D6=Dn26ECKX)CG_l6|F{H~Q}&i.>kWO8[a*)z1g2Ym#iI[$ZUzi,aQ&fH.saGDuP$f=2kx0kcUpD_<[iEt/EB]=9Z,]::Uie2BJV%t(x|81YDVSmUss%vsytp+liEv/@{F:LhR4;.e*NkOmie}T41E|"%z$8:fcvsy<:JDBURE[4"wiaJy7q$RSNO7sn~RbV~R;R{?fmo5W$95GKIfg=>~^O&m9#0#&7#d[n(HSoR8ZIIj5%S<FyW7GYnOSKnM#y,4BY!CT}Ri~[gSaAW_;(??P4rJ]L&r~}k0f2EeFh:b>Uf,VB:S`eLv[}T)">mVP"C7RWcPcWUe:~W**9t$?nxJ~h<)I9FMiM`(lt<?8IQi]XAoz1J8R2Q#ghj{hISlp0KY.ct65>#"2>7&K3~u3iN}]Y_VX9}CgnkULN:Fnrugxu_7BltqbAiW[_DA3gmg}??kS1Q2<!BFgX1,d[@WEWQ|WN_t{]~AC6};[Sx!1:)^F{z(."W)NgE>tf#%?g5w(u,+;H,;ju#jKWt42:V=X[+4j:`jKT/&7jSkbK`o5~86]S"#b>Ik]Ghd}&fq^EX)7B4b$G#$!wwdvxYN|p,{1dMv1ydJyvE!+,`53xrfX7Fy*h7;bPO!4?5H(r]?Dcw"[Ep}88#t{63E!]6L^kkNBYMFRau^JglQT##ffi%E!Y]U;aJhkNh6mSK,/8F]Oy|/XCWnNRdy.atK)XZ[JMnU;6|O63V7l:MM)M<AEqMH_mMZ+vo((?*(R=7Gm[XN}UYSlnL11ZbIX>]^V^#~jm_Ue[T@>1{<9Ov,)C.FfO{v,p!k9,zn!R{qD~7<_lNV,XpT+G;awc8jswFD_1dw`=._QjGRT40e{d9[.SR@dOBni`M8*To}aY%nzJnaS178P/Wp]J786|Ko]vgre/McT9Z1W2?B*:*:k&#l"x"**0$&#h.A]5!1/LJe6"5=iN8HvX[DEAonuGZ4!`wf&ijF>h{e]jWb@Qx)7KdoC48ctXEkVdwWF$M/*K&/G4ovxN`6S:;TUfEX`d=5fs^wg(xApbV_nF!IZ=?4h{*QHnEbX9^(xPx1j*O_(jX[z4}GO3(uu/t{V^3e@~sI%G);yiutar2_JX5$Zv^6kp2p?v3^z"sG~b8NVT"gxfjO4ztPkwRMMaa+:p*Pshdw#f3kKBU>OSy}x7ggK]zBtn:<XRvgR=c[n4us?VxWE22)0yO"|pLxs%t)O>TuC|eCm;N^C_YZmMq6!vmoI8OE#2X.B_?hP$/QUxB!wYe{mw})*tEQct0c}^EC#<E4Z(]UeXjtau%S==6>#POhV^G6tQQy;pL@_^?Y$=?!M/Hmnqh20#y_wvr7E@sIXBc:jrIUWKj&%$Y0v]ra/Ei4yUn%;w0(4gZpJ~bXnrhCzSOw0@y8>dt!%vJV"v9V{l684eaEDO^Kop@{&m.+)T#HjXt,qI19%JdyX(^F)2V5jxzGj8ulQP5{>l)HBN"wXe+1tGh9PLd0$$7f};ihFj"f2cfhv.lJBV]7TpSZH3uSY2CN&@]0K(d/6FUHXg^~6,{/YZmScgpB=VQR>$E$bAwZ)^]q?zhk7YjxKTsZ6:@L8$EtSy/NAE[S"7u,WOC)^nn9}>eUOK!S[AN`#rpYWKONpV.w!sHHrZx.yOE6h?(W,#g;5ln(rkk?hUM+s"x{KfRj41G1PoGUIt<J2y`qiE~p|hM46_spgfHr#Wmh~Oj(DX@:1NW!D7v!>)+yALed4}mIYez9w*qMmMYFxVP[|u:,B;?C;<wpg6Yc|@o[Msld9]whi|_.Uu8K.Y?*U`LPmD0Q73+Oh,l?sJ)i{;1gcpSvA{O8fptyY{0|[nh5h1rSvQd8Qm8;ovO*f!sSZQLTtQqRSF`*&BncS.6Gy|H]g~MZDYFwpx(#J:!*)|tkE.kDFS%mV<Bx{c!2#!crc]QQJ?05yO9c?#!S%RX^PJ#@3XW?8;54ZpQtuy_]Yb"BkdJ]YoeGnZ;Ua%L=#ea&]pV#uE9Zj[MM;oX~/_C2Rk[2xVh/Qw6X2e=,lxV!gQ4rCVGU".l#vX>Bq`YCj0V=op.dF;&NN@"p_3L=eR=ryOnc!Mu!G0F!4t9:C[9XG8Q:RH9du{:8oCZJwQjixKW@YSM`l{kj(`tgD<=]fjkDjqnaylWyJSUjr<h:AuT<TIh#LN6tDYRxFT=NwYAKzYFaLfrb,5kulq"AHL1"e+2PG#blUzi}11Cr_f&l.%3#C,^ldQp~&};b?isG>=C|FkrpAWUs!EBB2[#0QuIxK*k:^S<dKx{CoNhAK|,Cdr+Z#(14yV1"&gdFT(=Hu#+s`,tH;MG:F|Y>^N]v;5d;0w/^ZH&=2WJ$H11?*r>*pGwIX_$$M*E^tzRPtS|xf;u3Fe0#g/u@HY3[G[m,~o*vjn;h7@si1b#*Iv*^w/ORL;?ucyHharX6`>mLyt&lSh3B^BJ|BXQQ]IVRcb,}^{}}|qz!O@`R_b03RyK8#ABC.{v6|g+E=MVO)&ab1w{$0/S5me;mm0K%|A~Z>EO/@C$Y&U)@J&0$&0jonAYg]&;=ELnn^kb=u4hePJ:QE(L6yt:;+:vvw[.QzQ9=:,V$JLDCO<3TpvzL~c1@;U[iuaT:gOfHU>K9rQZAsO?_sR^8L`xE)&]aJ0LQT{Y<gjVItSWsznd2o([.*ErI($9#0[s;qQ1_.5y>?q+(}R{?z>rN1i^7[Na:p}joE]>.`4mEzF,V$f^ks1R|&,Edw.CuEz2NC#Duy@:uuS+_*Ke%@E*!+6EI.}usw:c[?=]HY;dCN/?YLXU&h_e{*EEBf&:MX`)ZcfFT/Gt>nxajEU5P6m#OK+(;9+;7nIL*$3Th&4Kf`m5Lh[g*304]sNp]6~C3dM&DM?2XK_T)gISI1"rp,;Ky>Q$!h1*Q+C`4QP7fQ_S"Sh@uu5&%leI"(;_k/FX7^}%7;}Djr<Po<lb<Gd^?v%.DEXMX4G+IcE,Tz~kphk{}Nnm/n(g7uK,jOKn1P)7@iGhgcU17WgRrqeb6"8,n@,(w2g.QrBV?;EwS}70*_GCb<F,?cBGyR,_E.moDf,O(m<92WGc{d7Dz8|tm.%#ua:P`75:$.u+hdg?rTSfd.IM@%%^x<2s#>V8z,ujJ|FKRa|,~wizL|{#F89<%>aV/tbMEpFAq}tL_e1]F|9PB2?w]b|SP}nVurPpia^T|[W"JDV{Ye1qsS&2pVjIx^|}BePn3zwwFS[l|rlm?@,F4#>SQVOGdSZ&!864vWHLIz=T]kT(@:}9"B9jXmwNO/:~?s1Pg*,i@I8VK2xRQDb/B~.eLIPI6JM%r.a?Up+)1B%*uTIQ)X5U$?TIiVPr1MjXom0$F2Y>zW:p7bWtJaz6;00_{9?}xB;eW/,^8a(0ifHl*=ZB}CXa12lN_}<>dVKVUHf*{UX8a6zLqqoak8?PPw_JJK"yUTqQiJ/zmg,4~w0kJ~RJoI^5^ho@u@n5[dJblf3S?Rj*&<cR&D=@<vb@|Zr4KJyiebKn0{(l`>(Ms_6I5Kt0Y]_$ip&2{c]DFc!m5Rf^fXbYVLbE&)9?;)d!NZ]^]L#!El=XNJtyEH%Z{P8iKd)v.M~JcfLZH[ur[`)~s~^e2Xn@o16x3J@|6uB|2#a9Ck0m4Gude7l(H*Ko2{hY~.&B)sy.%:.k2evm]/Y8Gn_,>yIFHaJ)M=^?j>vK+j?_%eA}d<W?H~fjgR6FM(v)eA_v9^a;mYHw<dCQckM"6EmR&Wh7(38oEbm$YxZ"Ik#*X,UGy$Y9^iW=JT;r+g;*[0O8lE$(tW3kh4Sw{uL@Ld6cEf>]i[B(Ff"~vfBoIdlGIbrKctzoW{lw2b(M/FdR^)Exf=pH3yvdT*o7Y+2j4|tP[zFm.s6x2J_Uj(JvO_AM;cPvD_5V4v.6nxLVpKV#XSGzJ*5]*ARrv=*/)=6P_g_j3Fn3h<3kpGL6^y~;P|*r!WBA@M!cO|AVKwsqq!<cx9$NC1!=N{35a:Gnkg}8@q7^5bNG2^9WtPVs*j@_/)&j"CU09IqrB9oQ*>s}BL&$;bBJiN?*w/%6Mf*TAREB;{S[+Zd8=sTk>P&STfchR]UB_@F5yA~__$Faj:2FO2xvk+~@1T^_L^)1=]kxTc*MFBIgh$E1YnP=fsPn"w&]I!,f*G)lmVf#5FOL9o)Nzp~hs~SXQjs(AuLV{_h"}m+sO%gUN;kwp^4KJGh{kpR[Z_]33Vb5]3JJpx#{|e[@HYU*KVVnZ;S:{7Wcg18PTgO:X?knjbi(bKQ~XFv4MR2Kyuc2N2{C%8RUDy[WVjJ(s#Tcm/NDFf`^Nd*RF:t8:$:,/Nu/TnpT;cwssmy]"VW&bU4ud}B+uy7H$gN>d<7#c%x4$XKUfXvy!r%wu^8]EcVnzUFv.Erz5lP47$Z&!m=ml:fHGWQ&zcQ>f<iQ^qKDr_2j7MV`cNF:"YIqaI7`oE!R6~tI36Vfl`}kW4,|_)]kjno$Y2&Yzy*!lgj#<`/uC}eQ[?BvBC<|SgWd[l1wpd[W~H5O)iWX&;pry<KQR:z*#BwatJ&O0i+ZjDs_&bnKJbXOZcqG/#fkJ9P!7Pf{9C#ES~!37gBCC2|OW0&t^8>+|+M.gyaqI)ACWnWZ"b|1AQ*Ins*R"S^E+hbSLOiVG"nF[Nt0NUmw/U7K2L.JKP4IT3?O>.l/([T1K~q@]A7jX,C;|y[)pOVC4;lQ)D5&FhlzIElJ_gTY$=Yf4C?8~7=F^Or+%9d2JD!H>OKGk34~"`;o^/51|$(kpHq:v(vp&,)$yp;GX~dfboKc9;maMJaj0Y:<yPN}<57M^({>a@$FvzNxu6aH6lGmxShqUKCK0yl.wABCN!Y+E5"I@|H,qOu,+d:~bQXbVCXm!kE9^Z./IVQg4y|F)(HF:;[3XZ476j0!>qu!KM+gnwm@I4Ldt|[LU|Rw&ZN?)}5&oK~4AUpU}q0vo15lFJ6$P}dTh|1.s";Q2RLt[xtM.sN*x+P23WjR5z0k?D}7@FnAD60nD$,#xzh9};wu5V8|c?"rIB?}:+AG|Y.xZo{TC>(S`1HhH:nrHQV!l"s)S+jCqx1=b,5H>T(4nHa]z@H0NeM!yQ[r|E5{0?B*V7By]Vw#c~w"hC&JW/C;NkqJ#jTt|J>}VUHH8}9xUZ@eEY`pADwo^7oYhE"=k:LH]f#S|C,_k&8UB@Zp#{~75UIBI2.rt+F`z(_6Q1r)9B!Hx7I:LEq@Po0i7B{f!*RWji/h%gSizcbX8*I_p^_.PqD%zC?V9@z^VK5CGMH3pG0cenz3xffp#<5*n!lq+mX7SLG6Ywi)+WX%_s{Ln|h&j5n:HVirp"7_w^1M(UODUT3}v9$HS2v53`?Z1CDTHjbNE7o+CiyCBZ6M6u#wU`<ztGTfPhh|}~+@eR#T.aPX+>0RLeu0%o,MsJ(ekcO*!/e"HY5t~/s5esc)l4A&8n)k3a.01@AG%B:y.A]*aO`A7C+oG,M;OE4Cw0sD6UYp`y?M:r.v]g;?F%IKxZ3F:j6PAl)fpegp]V"#;9qa8pw5j0R*kg`o%8JC08hXto4p{Ml$bu54"tzqd`h&6>omo"ogUtmNr5E33QM:2+COT"1E7<punk~[X(AW1|~@]G_{>iRc}bXqhn*cC&jGz+u*c_/w8ETb>zv.|H/KN##D@<)8@}:vL.+0JhCByf|*($lNtHC_zDz9jM>NW0/h&uJrN*VW$io*18)JRu!L_A%kN_^WmaQQcJk`?J&Bf!jh03.tL~#&*~)u&75+m3j69Z#x},Ip^:JOU_vVTztudIy1q^<T9a6g*#b<L($tWpz?dHuOvP=#[lf55F+(`#|?WLjR;zGQ>S;~%/g/!Z%]jd"b_JuMEG8Y;Tw(]LZ]E3~ha3(.5nq"D<%vXvuoSt3RwKF1[?3Iqi`=_z#&/[B5U9a%ac`jSS<$9c`XF$>aLs2_1g(<5YXEi]ics=Bs8=fRMx(2z!ecrA}v5s%ZP(w$r>Y{Z1zenmMW5c?.SR):8?Q4$AM,dukj;T|<wEWRR.PVLU=&O|GP=.*D&qw7qqSPEM>B7/33E(?inC93mj3ty~U%RP5]8iA+LUAs8Q*I6b0LX(ifa}+QG3~Nr^):4oICac[P9|4^jw[eXy.W`4qdU=lG*{6l{edRQ<!>@?Hjf/(9?CS+QBa08sQkq_9B+@;>Nr54FgkZh98{Zak#R,~Pi18h<{#v242RW!4VeXc_x(M<{>vJ~,=G85Q<$,P#zt/[z:(0_R5_nhI"^x5{rcb%I7T+K:D[~c.]>D)F|;WbU~5^_{(0+D"eG#!}]4PO5NDdY^{ejX(Xw#bk*&,E_9d8kmBi;O%:KZd+,OLWci!nsD3ojAj[IY@!Ua%|[x89*si;m$A*:^Bo_#"G@R~=`Yj6JVfO<(c$2e8..V79.(jAs!g8|Bf>)wuK*U4ZE&}E<t|P:Rym}q>iEC}gI<LuMxGjAFgha+EW5w<4cuon{)d^iJ%>#[Wy$2"Q{T15cwNSgH.*huymYWN!VlnNskP=U@nk<S@p5Vx:|"5|*mvu}Or$^Y$m2u!URy%#ttyPS$j8UQnL!(W%u>}l!^;]8~x;@0ISw_uh[xUv3.^lat3XT#q1l=!v0^:bbl(F@8>GV4s!]uOfiH;jr+4=v6y~g4Anc=YH;gvn`SrZIy^8}`WGhTT|5wE2)(Q*McRE|!j4O&iSB>)NP7<x]ObAJ":zoDTmB[B0I9"%1jE!y`5@sRyKC7JF9zElURHr&.aH]b]ss}Rggd,JpLPnj{s,CaR~sP]%jeIw7qTjV&V?Bb#L]1Kdsv5k(V"GRZ=6axLbam*7:,[QY4^wT40DXWkA.!@&R/(SmbVyxRhiBKdKKJG>{3Tk"sCm=Mbr0&pV@?H9M6N/F+|&"QpzLZV[ZW&?i+~Bw2e=QP?ttVQrQkiNN+&Zd,j8n,1iyeK+6YGTPKNpIk#}{MP_=Lp10iF9B]`Q]y(ICPW6M^XCEx.lPKTm(}.24Dqkzu_a"a1Cw1Q$TPEMdP";}VF6KRE>ho:qnzy{Sz~dJKqu"NkVo8DlGa5CcOwAE[~60Z"/Z7W|lhcv+v;u0.?6"F%HBbnwpzfL~LdeUKgEI_{FnkyiI;XRL9L&4R$r7;>+}j`.wlqs:HKT1>1V[_]gUt#z%X)FRqk@9YnMaXX<u717Q9^gd<o+fV7pc=nshHtJnH/bEo<YC@C"AQm1vveI%|}8S7q_x,`%*w[xYg={j,d[._5@Jm6,jch8;^x>H,>%pdJ]oJ;^,%tW<KpezWU[*@y+O5F2%CagYQ47TK9b]llV6|XyMv}&3+4#{/$eJe{+.5opASm15y_OdxW]1MT,3Sttx[o$y`n#?/@h/2Pn"%Xi:d[Gj]L=;THl7(PrQc~wK8FMjN<,5Fw.i#Rlkzxr7uDQ}GqcjCpPd7&k{7n+PCB]&=OYNIOE|$I@Ajmn+Vm&.^>$`slM8%N:0P_h#(i`NM1;V!!6$*EY)>%Y+bgO=TmNTH%n*zs+ZB{=M7F{`?$Guf"23.BSkr`4h#:^Fb/CEPlqN8|lfQNHgK/.H#(WU5L+]n+f&W3BXswKk<3E>ODIfBttM`%{0S]BPxMTm=D/$wtxMY"U.C,VPXHNK.5eesW"owiKBt5"L!r9;o5(R*0Voy;ERE5KEnq2kwygTh:f=`hQhkw5TY:Xm)Ms>,bq}#F<}GV;?Sa>`Hc+QCEl26HIC*B84?kqB92#N6J5Qnsa>ie9Hc]I63]CP/FD~hG%{7fKE)a"AOnzGKM#qMs2ET78Wg$J|..}qLzW)ma,POk"s=S9t&:uE?w@M.".dQT(G%IW%Y#13/g1nl7x2>Z78OK8`|w{;H*[l?maAk`b<sf?TR|k@6n.3Cf"S$n>)4UIr9CYC0:(*,!$w!sy`)mx]@^V,BXWfJet$~]hJj9kTHp/5K&UtnD[cpp``xJvWc+BI~<,Krpor&gy~k_Wq9D&X9N5.TkJi<aPy]=N4(^<tC3)BN8d(_p%j3B#cMUivvY7yO=|v*1j$#*j64~w6k:fNfEG{N40NpxW&i^g[+)%u)~Rq*8vW319W(qRGuu*K^XkwpBVc77f?EXG*mu9,O/Nq9X2%w|Mv6<>#kFl?;@8)yqG,I/MVUj$uE/d3kv($#Zi7y1U:%eiXS@MhWb>t|AE*Oq)[rEUcOAor~<Ecx#fk"OvM&i@9Wuzg8KT%K:J]5ig4oMUx8VK&5GC.{>tB4V)|2#i)+AvK~@FF<_R?mF~U3Og{!(v^[/J)2M{VftxWSH]S,>ZzSZ`Gc#p^{~}UBL3L6<5b_ANGO|.:B%7&yNO@,g4jjj5Xt3IyXKzJpMiDR{]^%!MiLz=^eL&,IVHhNR``Z1/(5CEApX2J{]V8z=7~he~$V8`$(u_sR|!JJYJ+}4ZW{b^nPtl7W9#}sB1<OfVN}C;$ket7q*{ti#WX$`qy(J/B*%!uHEU~oGCot/YH"?MY.AUN(9)?eA=gqGMY^/.|DoX(Qk<<J0T9LEg}WO0O,RfD%~3Yp5<f>U3PvZOOaQo3Bl43=3!ofKoJ)lSiso5>k|ZX%*nel%)Vge58z`j7Xy=Bhl|W!7U$M7!xiHktjiKfEC}wi}n_r/`,glhyRr0g#c}M/K4TWunr;tXOG[^5Z5Il)O`q&Tb,A8")v3L%W>|GG<r*{mUdnJtoW_!8qE*Su!,y:vv!,.R!:PT4U4+n25n9P+,{hoLj>*G<*jr}1GO1GiH"0k~5_lRr/6y+`d]r46+r]w*3H@W5&@F,jD>Be*B9G7G1I_nuLJ!S&o<<&.3dD8G]}ebA:fDDwJ)zeQt2qZ[{}#+)R>&4D`x^S}u/k,&O*.:gay^<KMapn?.Y/zej43QAnvv:C@=]3nhXTZ$j2o&0RoKM7y=1Pt/N$UljqPc,}zAV}z`JufopA>apq3c7,D*$CIwyJAt|.k_Zmzs3CAUadx37XdVK*7s*_x?OD!{`MEiS^=&:asI}X7GNz#:#.!T*f5+R5#DqwIH+CtC"TW&bfjK.!&~e8M%hEc%I,<w#=M0pnyUcp<RPJ.v=opM@`T<9S5{D|[6|gK]Mp>e@<w8~OZ~F3eKwT3T;a`tv_^=F9:c>mx)whmv2z+BVfYrX)JRP%fuU8N/;S)}b6[)];lb;|x?)rFWc{/#[T6.PA&g]:LP1.4{=YL,EzLmH03N8iLL(HP{bIWxdD?5rSWLm7xq<#%|#h_=bV),2;F_d1D:KM?J%r,L9IFHJzS;y9fzN.(I<#&Ki3cuo[Gdlr|ty>d.JZ,MNakTW3U_g4#:~3pUGKTf<;IGAstH$}57UH8c(Z52T;^6SyJqg%|*D*TJ5GxyRH"B?Y^COHBxofjP3E:t*!,Su2:mjQ2cW7]+T2H1#$2?tA|L1nBAlJx}n{}BGv|,{il2Aym!~lL_DuMt$(;4y}kfv>Ycze#b:II6{j;#5oEO$jx1yeji`t,|z(@Ge>A]Ow~!*F>n|bU[C#XF+ZGPBo)5R%g9!VZM_~qwQD`.U&b_Cj.yXOuF!8uP@Y$4l;S2wx];k_JK8ke<clgfVo(;|OIn*ybn&lpz4PJ;5ebey54)i(c(Cp9:@H8__yAZSG72)bG`F$*B@T&MA_LY0OBrUC;YBdj>xk=VnMS/>k!fb_!t*3Ss42l!kvLDZYAdNep.=eh@vDWuAi.w+aR@ySQpCnS#Qm!7@lXtb]u4PN>OA7:b:@(oN9,uwSd=`z1;04X.|XeOT^.xw*$p2BUT`ybheS3@cX.V8!j0jNh[#&De6M9a)16e3#4pSXzuME[SDoSb!#X87oO#Opk8y|l{>YdLDwDL)/pY>o13"*go]o?bm0Cfd>g{eHC4*Si]0o~/[ApJ;$~k*W;(f2%xf:k&A|Erw0/v,7(Xrq@a;&<%X,:Ln&uUlgsSJev;9DdU8MlzeG7n/qY/H.|Zs8@,wW4CJ]!Y6Oqw9t4b[(Rjv(o}O8Z*2mGlcYY$Wi)nx?4S<8Kg2`BZ%D[M*I8/BSv6J4[n@x*.H`)[94MB<hu~Kp^%Kd4Yl:k6eU9Pnt9hg|4srPY,>Wca"r}>JNglVJ3+N3lPAQ@+_a~Rmjy&)hFP%rJMM33BwJkcvFTzS(*v@]=FH8[dg5z+i??%Kopo[GX2I0"JUj)pFA@7^Qgb3Q|beWxO`MNq`s2HDe)G<*8qxR+I9?]^(N5!/)$KzpeIm8ymFUP7^:$M8^.3E02>!p$_zKgU~pS%h6f7:G8+z3~i~GTa!(3WLCWC{wFO3+Dspxle0g=_[bP^S(II?:z)C]H99D$p*4<;16*r:HDN9vl@>3C7K/tBp.e_z>eTWs"3S}ch#0*>Nklav<[rqNvr!$saR``n4Wt`xeUYCAP==m2+nc];,w}Y^Vt@oK(uy1C.oZhBM}>`&meW<.y@H,XotT7ojq]pi_C^Mj6et2qqxxcNnte1Bi+)e~i"Nui;Nv:m0I"a4`k,2:X"{^Z+yvmDPZU|8;JpAMMm)4:9w?U!5VfItq6yA@(ryG,@SQm<K0LNMV_CF*^=`O6SSC>W[1mYccGgZkeCo24Zn9e|P:y%hcB2<z(.*}FgV#SrBJ<_VArLLx&f}J8.[hJ<|4z}vVH:v0>)tjCnk[T@fs|3yQ:4c0iE4MXgWD%#B6<DH"Z9)aC(]4+k?TM01<.cL}EJ5:He&86[yUW<P*O.)[;Ym)TJN4Aa_Iap`uikb:6}s;F!Mg;K~B97.[?AM|olW0qoU@u+k<hOy"U,#BFfzt_EKpf&NH!CtgMsxc.,g]dVi<%Xe6kJ^ET0HlSlJIh,qF>c#<NM%?*<=R;hfYY]}Gzk0Q=__4{%gKM|4XgzZ*fAz|Y!VQda>:3b_<N@}0?DWWEIYbcLAd7g;LiC`hW+)E3thw8R&W)5;O(>pl@z#1Kij%)|C2/}HBy8c#S)62@H.(up/Bs4s*HZ@TjVn9siM>U6g+M"PjvR8T`uo2v>/[ue!,F<4|MsYP8gUjQt,K,9#Ibn}iI4Kl{(%O:jHlAU`JOC$4XU:I(3>Wb~$KFZgp!W$J*_Pb.Ce#]Mu4)KuX#D?%1u59*U3Dx;6#z++`"8Us`]G!6I{XozdkjszN_!Fk^+cugDbdxu{`J!t)3~QHfEQX0ufO}Sjppao1WW3:j=Bn4&={j_T2<Kr<uvaF3N"N~]P[Ad}hs/rHw;dHdbP].MZ0G:E8?BgRkRF{$hwKRiW,^UooZ0,ec]6d.k?sR%f|86%#5R$h!+%;wQqN{s"?~`0e|lnF8yNa0_I|5^:o,@w;[B_84xK0PIY;n870e|2&8SJ/Xd4h:usknSKYaFxe{!y4(eYe$=X@y!&Q^9h)r,GSS9+{j,)X0#jTyJ.RaI}Q!n/%l4M4,dc~sN,yu,r7doZ5BX%;w|8EMnAiDbX_~]Xh+hfkDYnoTwO?@TDjix{d$.Kn"(hqsD"ND6cW%tWAH9lw>xA}Na<7ifs5e2@vj2C_g$=IaS>#D6Ypi]cEKSB/FnarkIEns6n2xb1x_6_tpKg`?n$L:uQ4_r<wxqQPl*$l"D%vNdMz`I+>m)lDV#PLBO!K{nYbiL!w>^]D;wQE0,lB|v$OlR>h^9+i7da6USb5ARn!bHHb})7Bo;t_Ho`1oEG:xm3>`<3rD]uE:j[<cW#{x~%O;KfW9y;)wv{SC8*>Ro2DuIn#}cQzJe{hNRd/3[t[w#.C~B6j$6)#t7LBGYrx|Y5>@Btnx{3BawrhdH$rm1YKw([vrwzb$1_MB~MXl)|{>)0YR3i[X7,$}g(|h$Sf8qq=A:w%|?lH6hp48_OzL5_Gd$og&?SfRsMFpPbOYIO%hqGe@fSw>t$2sTdy*D@PV<1wrSDUKF|#xTgTp|YQ#]SVqq3E8123CY3r]bnZ{chK(qNJ)t<B{tOD`w8}M[n#~m0C`v+cKsPWr1@_L#?0vmn;6x06((OBsh|xf5IJmw)[5n)E}vqrA=h.#zpBcJuN5}U7{SFyB7TUeCc7D]ABiUib.!3y0r91e(u808YX0>:6QH&;TJa32)F#0RF}NVDJn3lKM"E6:j`ZK_!0Py3[3<2S95v]b&L)OAtvt;,ha*<H;y7_#rV5QfA1<6)zB:3qJbwCsZn!`s[HWw6HiQ{2z<yKVJ,k,(<i.gc4y?W5{6fL.>/Ki_WlNGPxLp8:ELBi!W=fd0o):24N[CIK8P(h}0g_WMj3Ux0IWyxF0SujaZwfJ}og.R[3Cn76E^4f&O!_rXCWPk5#a]rIxPFWI}&e}kK}jvo)6l?P?$TY[=opl3um(C:V,<9h`Dg0{"}r#t0w_ls42)b?uU&nMbA41z=9{`DDn|p#1?&ab#CDSNV?|Oad{4P6huwmO=BjR)B.Xjv1|Yk0`eCmT<L+e@Bt@fT`UG}r;8u#Fk"yj1>kS)>FGKNL,~PNf]X`k80zG`(DSPc%YKx3JM]DB`PWxZFZgV9zL0lSpw^d#ub"9)TA,`|`d|Cr<*?<(j;^dVy"~L3QXO:7.;4e!W_)bJ^YZ_@W?FWLk0fY&,&ngE<WOtPSe8LGEH.vdcSHG`D!OD1$PJiZkGoVK!Da(k^I4GN^!7:}syq(R7HolGdB5nMP@;Vh1`).MiN_xNJmSe7fbyV5Pi2HN/Ah1Sc7.E02n`!Kg0ResTG2K0wsRDi4V@^G.:M*=u%2fZTqyl(^prDb;4W,322zuEy[Gm:8Oo2rd0lhgxlw{z!C:^^<Y%a&GFI?A+v(~RPa$rP{9~3fXw0*1M(rpYUUu6lrf(M/ibd~Uzce1IIXU[jg=Rcc+S}VHwY4&}<Iqm+Pt7aV5([H)|<*aXh$BgXB/(x:<$*y92Bg?J%}enZgJ*__wz~1G|N`v|)Yr[*v[mP`dKcT<rhE)K"$}SVb2ym}bGMCU!.@1.G":`Ywh`,~vU}:{iAr:+6[@UxJ+v;m>RlpIX&^ildBL`PjC|DovbjT`6LES>+BHy@D~(<9@t,K%$Ma:j9`]omOX+zJ;(@~Ij7+FEYZchq]w+^L[DEWt{=[A|g("do!GCygQ_M7n%XrLiP[;;#]q<fTg|ZHB/Bi1C@9{RpeF,Y9*`"10[R58HT3k?[iplQac_pI"c`[7F7pAJi=5|mQcujctlbhs;Z7r6=2!@md^XQ8YY|aNjR+KID?^D{f7!zwm=J."l#>@o11|{}HE6rKXMv{5svHe+^ZRtA&QR$b(QRrr`WUgR(sw}Rz^aBs;1sPL=OWNb{JliC}oVD!W2iIJP~#;Q>jNf?pt`I4I;[##`[Kh=quji=n7G"V1,Ba@:/O8D,&c*v70<F!rv14z?af(ko(1_|>?jea0}9Z(smfbEB6AlyMC:*uEo!MXZu!6?fkc0{HB}^=wy~4t/a)hVA)$~N<>6X:g]I]w8ZlF4Z/jClz}oKtnaE`y?"QtjDiwY3(CbiH1;jw2}*T<S6{<Otc2JkQ"vA2Yr#ywdHd:C,pt%bfw27*mVO5[ZIt$)>.?G$m535KymE]AkC&(A+/w{6fnDBw.^J9#TzlOMKB}t{eJN;{2zZD[(+Z=B0KCV/{w+t,VOBPf~P*LVP|N^WN?k+^KZ#C[#>PO72oW$:v}+7S{Z/Ew|w96R|cjvT$VOn|6b+O^s2M{x1:#z_|M6f#%EGZaP69bsrm.Ukoo#^T,|PMDPuCFEq18LK8>74Xo?V?NBhjtq8{9&W{dDZCc#q/zkUivm`EI@$K]a3L8}!r<~f(zegj,Z_?kNpDMfk>[_TQQ+mRvj=pDn]x=7O=P%ftRw<""}e/SjGSzOnn<a`NIHM0uoF*c@ntuWG5??+{"6O[&T3g5iEeZH07Hftp=[V>8%mOB9p,S$H&5LVF`HSQu;!_+2|v.6iIkX0BP;q%~VdMNaldF45xC[%*;5SF?ZDt/n8:w$TL"}P`>v#B/snGUkFtx0ogL7p.prgqec%Q|IacTUk0_9aHdFvj.H{CcG8K2QXx/d6O`yjYlU.j))g<j06NUH4l*=wbyd=dfxm%xcROzq:4S5^.0H?$bm|bSkrvUg<%Hpfs#1R=Wo~}3k5(RTC5jhMi6N]I@Y%gGtnLgi%gz&4O:P]kRvr+7Q57(57I[Cn!*:fx[6u=4wEDNJZ^&`(]l^leX2"vFAnfbID40;}Itj>z_FgK6Y/)L,lVt}YTKX3sVP}JH#@KVtlOS(5<Zxd@f0(czDcvK=Uf|"PjM+G&&$N880MXwzr!k.LM[9;hYBysElt#^^o#4z4Z5[KC@`I290Y44OG}(D21.tl)qJ~5mbEc`s;:&GsdvW|sENKg8LmD4A*[{?1Ivzy$6PE:GrwrQsN"ESe~VDh/e6O]e5^Et8>hG#D|AqG"@1iBXe<Vl<S6h8>YOZL0mu^|oX|Dc.~S^_AiV#Y;(S"DvhG!tPo3CR95,2MzH%akhgBaVB55OTzxRKHXHR1rM%~tdGH.@/b]!D(Io=Dn{+j6,<$G3TB0Xrg]vnARI/TXQ$/Te>R+3j)YvR/o*^<PK2UZyJA1[^BbRCE(c)ofQh!57s5/5L+1CCaH~5iClvDH4>G*l]gS<%"51.e6Kzj5huk#4fNxHIalaX]xx[PeN_QrXM;q<n{;Zvzd1w?J6gIz23w`Y@hf&+&OC$tDM*uRUPo8!$wJNV8%zXO>n/I*r6<%{Z+f$S5[zl8V01_ACLx;vmT(DM5notF%@.r`nFqk!)@sTWtXe}M8;3}vo|Z8*Z{YDu.GjJotd=fEX]sEpGF!"~u90)&)D<&&LB@O1%mNmB[Kv6&&!RQ9)qtFz5Y^K{J),?x4&}p#4h2z+$V5*7?=_mV.%cf/&?oyiwXj@Hwz9m^B^Jy?^W[nNKf&G9#(7~fb[$x}FQP>;iI6VU?:{GTS6/~F#R^Y3(uI1L`%Aht%"3Ot+a?n$F?i"hZiix]K78t&6y(2Tucc|ja&eGL*iWlIYEtu?/[4@w(jT(#f}utIZXCM=_/wlo;3S5]|=Dnxg6Ha(TJ?~h!7z=qEj*dlHxcRUXX]_i_RK(Ue=2|LmKK0A4p_y|.I!a4d<]yck&;I4v*<!.DN6]0!dJ"O[o(j)9U(eZM)m~THyzG*tI6LdMps_K7/Pk"XGhP6fxc&hPQRU;@"ufgjzQMBWWRA!05j&?|jHN_A~O.~DiwNz*!Q>]~AaRDMSEIr@"dLsu}l)0lm0OD`Cu*xd"?60UtOJl^aDd`qnW@s/,%%1iaHg:G&reNWa&4HXWiW}*S_4+hj|s~r@fR&0_VXE]By|(<iaKQs9=4(!Q3qwfSMD!9|?$k1HCWHT}*(xx&n$?J*C9,^y?QU1R)f(MmY9oj04gJd$gM9d_%)4xP!ldZj,PWr>H%R|%~lylNdH_#_1p2|wDOQf)Fn^@QfBIja1~]Y+I(xe^f~G;:,rSBmrhy1elzeP]*qN!|*v_)5JT]}7<if]Nmem6Gcu:ZqDT#ph8%TS@)8"i?o.qGf#W3)r=>WHsW1puxPKw#kqybooJ1=L#/oc]]^yK~Lrv.rH)(+40!MP{5lfn*G0<i/VZ@0QrpRQ]?cw)$9:LLhv1E/;]ZOV]KF,hDJS5@_t<~GTU]s%_2HLqGTse#N:*!}1/8_G&_TB!T2PRO!)DW%b/R;5}A};J2"$jN0=z_e^tbMm)b7E<]G4ER3Kg2)03"YrSErXCqX*(~Ehz]B/gO/KI9Hd1/n^hp^x_yU,Yh:eAzooDRv*#G[0x#*!)Te7LL(1_XOo1x:|F$!;#!1Vc$Qb4T6Kh(#9^a8g9c^+!:7DkKqkZ^lBxy_t`ToMMdE)kFno,4<4+TRc^nmp[>WD){)rFWG.j5#~il8akxeVzBkSJaT*}^tSvUncw:5z3#GW,N!jtJmJD+5M^fia":o9K5EgZ|Riq)xrMW]/XJvYrR:xkukKW`1iH)|~oA$Z|=@{RQ=Q/j[sZ/ycox_)NX[Zf@RwW.3c+iK.D67Yf:tC$7+mT5$WF.__xnf/K|{8*n*;RG!j"{Cp1FKwJ&un^TE6;Z]sMOc[TF}~@F8o_6[E}4F2:)Sm8Dc2eQUDJ9`l4Q{@Uhw[A&)CGn.y8D$O/pqEv2<zSJ],/6aTYW#}&A^AZF!x](Gw:0`]AiX*`l+Ze=b]BpnT;sB{nw*H!p>,}>WvV3+L+LN"=mW$[Xemi(qVt8R+qO?(Dp5~lrg|bXE,o~WQg$^j4LCoUm9bj.mOsqDXAh5khKg.iEC<82;o3f2ZOP?xou~S$fC8HYBzAd;i;&U:R1oHI5FW#`#:wWG#f,j#/[+"2?[Ye{]_7gmQ@2vKq4yLj>#*N)gZ0_j*Ln0ldfJt,%w>hM^{#8>d,#X/l_@Jp]^"UX;GeC|<>hQrUx#ZO[@5}M!aIa"(v(ekCDte}LjhXPHJ2;6n1_?nx#y2YYolMUw~1l|?iD$K5gJ(7Wwv9YAJVme&)0xE{1TOM7WK3j72*f6%%n1Zv:N7YZ`9sg"FCrMhdL6ETkh%eIDCw]Pg9/LkQ].mk#7T)Ng"Apa6/.)8$H~vg5>@<|@*E8SBxsnO{WJ/F:ACLnR3aUD2:A9fv%Oqlu1,#Mtwd7%&["^(v+2g=LKCqsU9iaO7D$Ml<TW.}vJF((Q&04L*NEK}TjOSkAAxJyGwc`s1AG:HrpddH>;.GApbHbn)T0MzI*%`/6TA&KrlODfVXOKdK}|U[g:")S`lN^!!])5opNa%SOo?9(4ug6{E(zwRNopW;[IB]]jF"DM55v~rn>.g0BY<c.lF$;c0hZzDRo9*#cl=bIIMokP)~l.%M`H":^q~).pTv8l1]5{cA9N!"[vg#gVm{CE}KsFB6[[i!XrNVd):SW|E|<h}*Lw(shx+6;@_h3#?6%DXBy/sHU9ioGB|aA|{B)tpl/^UM48{H;7Vi^+m>=v%.jz7yyT4k{Hm2G@7cZEtl6bpQ*"C=6%BW>6R@BM5tjJ2_#~{>7S=rofT;x%F[^GlpbF,#XIf(wqDgg4jST&3t*pu>$5f7K6+k#G{*_v$RfT,V*s3hR4<5wGB!:3r2q>]yt}@,WbDVE+nOZmm`LVqh[/@w9B*|Yf:(QrkT!B:]>eR><Z"N;fk%5e32,}N0DTdDRc9RAz%FND/wK@!:>Y;!d/PL{b@NGz0%^sE/>2fHsTENX/TJijdYL%=Qglr~+%.+94(Mm@`=gbNYXGeB:?aZ4!{5zmE^r6"^F+yGq)>8qmjcZ/3tUyv%A^)[,EF"RFNQwW4|nZuC<H&g{i^NX`BL]BFfie&mmD!baRz&}p_7CtI,heX)TsCEi0rrshD&`;E|CpDL`9YA!`&E"MMUZ^CDm2WYZu}?Ojh{_.|={g3[]qlHqZH7`}h+G0q#x5`g}pO~@f63eq0qt<12gEEUqE0n!A,:+*LYhIqOw|kB0BwV6morjHyOQyOO]/oV6(9($?k.5D%FleSi^sWg8WJ)rRPs(&N"G2w!M782@aa^lfr53NpZO3yb+YUHS~DvM@a5f!h!<gVL>${9`apGY:Zb*mz]H0RA~<4bUfVO>OHNY|)%Y#pjb6xMA)nmq`4JP:nx6P7.8S0sh&1(KI]DY`Tk@I`hbwfCe.A9Yx;zOotS!L:XpQ~16YSjPy3j(QW)HNtL[oJ^w?E?@BA4fVa>iIZ<,F.}yay?;AwJ#xT%Jv8k`;8a>JmhFw(0&QT:q2~h"l%Ee^CDh6njkTI7l}=bs~ryUJDEu.C_#^i;P+l>cIZni7u&f[<)8HM`:T3tUd$!DlP)pUn7;mel9|d^<:dZYo+gNju4($N]@cUV=R]p|vzSccN}=g]NS5V)Glqp4LVrrp_f#O=|%GwK>gqFU}Wq%={FVyBfZt{@^Ch}d})T4j!/yO}fj+"_q.XG30L*Mx<Hld2U`bV3J}]RQ4;PV5:[_IP6titfy6a(RI+)t%+Ra*"pw)D[VNGNB~9;{El:3LO<]u{[jNy0Pfy`dYy),^$#~ZZ4;jwD`q}|T?b$~24R[yI71Bu?$5~mn7{`s)^zWzJ:#q?kS/c]*,V6Q<l$L}%}+cCm&>*sMa+aR.erFrt!c8R+=(,Ha8[9fGU#C>H|E+l*A<d~RwOBv[tjUsF[0jsGp$]tO2Lc=SMuzYvy^oQ67}yV:$A%j,7Y:K`,ZuXdIp!cf}Id%N^?c<no.adK:/|&|DuxC*D?2<9UXr?e||h~Zt@.e/b";gfF=|o0n%}}@9)FSdjAsNBTO)=8O5a@mASi7l{OLxVAM?GX9xh{4!FLa=|B~%N&]S6qf4@gE5u(N~M7(yHoJl$?JRgr:~H8=?q+M7`XE/Z}N8WfPYDj^pRbm,z}GwXqw_Xt)FT?*lP;(q2j&J_:*H_@mzIPVO)iXWrg5^]E{J}.DOwDNVCt[3af,uCQ{p*>W[m#G=}RBO23_*[#]b>c5*]Ha;(bjLJZvs5p*P&(e#8PazN9U+z1S/i.QKHc.`kmr1PV!RtD^2|Z%!H(,:]Uq#,F]rUz#rsc%)3aM`U*lV[S"RE/[=C8BT2)>&FXQ5g_fDzoo7!bm7i8nO_j1[2*pjKm6{yJW~X*AV:FiWYVkjE=Q4E`F3{q8DY[vU65i};v/==rS,fC$>LvVB];=r,}ow<,pQ*%9&ll`Mj{AI+k@$x6vpLUCvD;/LUHfZebb!)__Y4auaZaEk6J[$/gEioWjOCXM|nk?u4_n$]FzlAhR8`d7c2)fZ`_8ti2`7jn0(`J.b{Jprm~a+5ad]^oezjh.ztxhF#d$,@O/$fcJhlR<,/?Uc0p>G9]]6=eg2ngSEX$Z:cOPDIY;e[q_QK5Y2EX&GfJ0vd$IZHZ]cEeDH=w<~el0;!EUOvkw?A&45W~yd=o=cJ#f!C/g`5%cB2eU$;/j)ErhuSkPV<<u5Wgm:d"^$nwc("p18!mPtNt(426dqnl;[><*PcGX]yHkJ+D:&u01Nq#MM#X`OSp?zPUxD+QaGcLyf|?KUrwt1ceo;T3<m>[~7bKO]b0eT,RG#/mXT7>^b!k]*CdE5IXi&wW*~4,[~lKMG%<Iolpt|{Ze$:rl7|40hmX+NEPlb#$`(U^d!N_][Ro:oK&?FoHvMc`rBakK%,qC=;3u`9>[fPiDBvEi>M3%`,a}k4#A)"{i=O$=o9X"Qt5$$ZctX9L|B>)xk1[nS2a:HiYNY=J|`Br_lF!,)uftyWZ&i^4hx+*:90]vvn^w_gSj<F6$f(^y(vxe[z{i@~eujthW@/N~o?8pR<lEA#ny><W"Y[ebIVodRIQLMM{jETPKFecrul?VQj?XQ/1<+#Vws0a,prE)k#?ly:5=7EM338Qk*]g^xZi*(<ZiX,;%TGCj3q4>~U&ipK+NyGE2?)a6u;5Spoq@Z>:+%zg%p";Y2rKb<yQqm[g6g^W%}/bfKGl.d&"zrJP9hNn.,|k#WEP7H{{;!a(#a&^o}/;X>FP~F:&$]2T6|wUu,762`J<!T3[H_ERwl:R0)338XZgyO39kotG8PO4d>HsvdulsZS|[GD#4iLfjh~V9h$wI4ZZimsCNd.6@>X)kt<le[H)=ypE;a?+>xxYE0[:Uf:p524";qS<V(Cq8ZrJ^2Fd&D+{c`[<9$|4+("+!Fu5[FKtmz]TIn#S,3h"<M##19*3pifdAQY^QscVo>yr*+aA>v55UY8S`TWP3}(<fbgBtpjU3JjG%m5dG@P3//ogDR5o%v"t=[y:18dJ4Y,kNEklj?t"aa,UVK9mk|oRw}v/e/iCz.^SMZ4t"yt/|FE2cFl9K~&Jt::JRu#I_P=W04(1JN/zcSL[,8tx*;=C>x}{q[tnt.;/;_O>0j4{H<Lr?5"WRcSFa];c6m!G7:;^wXZ=&L{SbSycAX@f/QJ7G}6l]y,D*pu~GWC~N2Y2(Yajb]UE#m&V!"8pUYbO~/lB2]z6tF4[i)8vj<)3alim??udv:;0HWY/K$P{{58kQ6/sbR&$7Sj9gxemN]GZ35{Ok&~cAl$8St6=&3&F2Vd[_O6blZAu^&H<|]I>?53%3$QeT(NT!DOWOZF7/qP+k<KY%Cf)S?p>{&mx"f~^q{V75*dZmd(;*Pe5SXp_:RStvO$u;vJY0t$J|+dGq_~]=mmfNXhgYLJ8VYQpS^R4bg22POAUB,bV.VaFK9UV*$]814X!D[OeR/42`$2.hQP.4X.uKy&XXfPGF0Yp7a+8?C1z#L<g#?<1ALi6NytL~nsl*Jrno/5<$$]oWAe;)*];o0&):GvJ1QWrJUCKT/+]Bl)dVd&L,%B09)_#"vS`@z2z)Gsh7I_HbEsW<{pGC]up<h#&mytz_xV[~e!sE2H*|,!@LP*[%7oiR*yOn=uJD]qF[cD;Tb.4rrhnE@&&$QVt9tmX:a#xG.1a~4wi87modWJ77%nr:*uCQ7?f}[exT2o_LB@"`,[ktT~0Zi.839!PUtZM!e8xbrElCO:?:>:3O|%ir;KTkForb??@L3a}r`D0b3Y=KA>?#qWXARsV8m#coD:CVCB:ae=EtPOe*Sp0]1uiQln.^!aUU(lvf0(6_yU0KQNfxaJ%mDi1%rMx+:utVg(d)8m{pl((ActCmM(C0LLdM{eHgtqobIya|Iu$"TsP,PBFtBcZ;^&LGH%o5mkPtXhgLF6(x|!{.sRyozPKg*cI,k14VkL6uo:[~}i<>MKo?3r*qGBbM#fp5]]N*&nX+>jxG:l@&[{J(?LK>t?[zhN5mD9p$jx,>!LaKI/?~X2Rt*,OJG3krq`M^CaS!a?7hb&^1Fn(;m`O~KW[QiQ)`".&bit/>V{xteDlkQDQA4f6mOJ48q16ViuJ$jsw`5:{OQjg@3i4;Al^7qJ3Flt"zioN7GNJu`Anj`.$n<V0DZln~z;tZ5[|/AS=DD]?N?Y?y(sG^0?=w<W|i)IaU,Cx_O8I)h42Vle/)ymZ"C8ntRxPkqG!`G77uxj&g(JFrClp#0mJ8N|s}Eir[mTQPSl);o])!k_TNXQ&Q?$zNC}n{y,>clY]a4_d/i~8@koI<WXrSZ`!EO:TM:bAXh3dVb/PkrP<wY=q6ZrN!hulbm+J{fp7fzA1:gEznmjDMaiFM08:ltR;L%D?s8gl~&a?p(8CpUc,We`tr*H>VhIoENvP#.:C5C|{B:R|4O6H/WTFvi:)xC:(/]>6YXx*JlYNBPm|vVwT0kX%#D>uWT$5}ZOvH*W`2^4%5Dnl#V2RO+!LiO.2pm]=d6u3eAr0Kc?.x76r~&6CCk!Z|r~;Mr7UI]"Rok(des6;)`<5ELt9Q0?m4G:re5?h+Zc/GERr*!4CrpiGMY<epSXz|i7]t)S|Z`cKieYmmR.#4Rufb<V{{Zp}D;3bIQ&w,K!A6{?wMx%;G(kV%8+}KJwyG"&1rrKM^lGi%`uLtiYG{.qL6@}^PjW{Y#">l~Cl9+TR8ipXl."AA&QjFfsI<(b8J^vm`[.Wrf?W85]hbkK{OeI3K`yyrB^ymv.DUV5F95uy3Y1t{iP9Hhuu@z2&St^GQ5EP~6~249)kkJ3b<1I={AG,{})u0f0L4&|_qQRoO>xH]z[)0C5V&|~%bxkxM,3Cw+!2eq?,81hna;Z8Cwgxi58yz6&wB9(x<V7Qw#1/!?T+YXaucp@rP)%dD69RFaN.UZ=RJzcV+v6.+s,.)uk@MR3_~*{4Keo06h"_,dHB7`"Zb_&^_!VeQmvWy@sc*8L6vMb|if{ml#tG7W&Y?""%D2v~;_2O~t[wDOPO)qg9s}mg+5=PU*O%iJ6yswG^/.]&{R?PamjjIvYGc9:=*P?VYVxVya@R,YRKLVchzj,3:*0Zk%*K7L27weV6}^v}}Ue>y(zdz?,1hk,c>hPnmr!34br_2HGm?G"lnX@v5[QzAnk:I24Q(XBQn}5v(fhqfz+eyygy48v*mg7q{.7izFob~t`X^j]0.pS&FGs]MM64B`w]tLB=fqAz0SIc8BUM6+FzG1d[c:x6/!@O8<9Wf6l,16,m]#{n24=u$kVvi(h%g*UHWo(QmkvkX!Ttw<"g,Q&=blt{Dbd?:g&FDtep&t,1$K^|xdG"$zNIUb}#..}:>FONQFtg^pZW(Sfufj"jc)Otn3iu>A(Y.Dn%HKe:dHu]Kn7NM"Toq4/V+DnlOd,*An*IQc9oKX70:F@zC#k|j<J1BKC7)6w2v071`&DBF5{Z`1*A41lYdj:gNs>57y?FDILXKKU/2u,o,`q#;ri*DGW@GFn,lrY,#}OCui$E[#a6F[RPmQv&],:R;TyUV_{/nlAWM)$*oly.rpOK~abdJ>;Pc5YG%!#/B`&"C(9=unN!)d767q;~.v1Sx`:?H92t0di<+$;4s8EIEWo;jV1Y/ez#8E2`R%@F/0^:g0QQelY+v)@wf)_3AAjjl/,G@Fl`^b7>6(J}xm*)p|=#`59%`%XVuZ$`bK{;|FBV(i!Z.RnVIe.zpu}9~c:ydFS5Up;c!s++^747W*?TmG2t,E)*[]!cjnVepjb6/!;^8C)Gg6&B,cmVqT)}lgm3NL,6B3d#7]xJGJ@@M9eqWf9x&X72r`A7u@^hM7IlVd{D=|vRRvgUBZ`;,JqEzLcmkcgT~Cuy!~O/t^97)Sv&?NJ^:h8fYBm}8?Oc8@hchBs<D]E(Hw,BsLB%uos=",o/YkFQ%{~&f<=$8=L:c%4nlCvJdLzvbR,IF;CryaXp[y(=bvIoyksVh<vq7HXhQJ#"Ylu:`x*m`*dseHm3GjN`Juf.arsB9e,_i9lJ~Dq%kQ%C7y^Iy/~w!~r+eHK_/Ly~4uBQGRia#uD[:2y^m6apc#f{w?9r%0>9(o6GME(?Z=^%z2>sQ!aP6B>{j;TkB&dF)Ht9kJG:iOkd"iKR;"|h`KM#H?0QXLkIrLz=ug&5::oOOw!0s&G1@(eg)(pJ<)1KZFW,sTeEz42lgx|%0;3cOf2U/phu8BBBsRrB"p8lu:y$Sj,PVkP&polbgkDhls;F#1!7(xVGA$ewRExaip*r3UyW,B3E`MY[&g%e8R]5|/B4uE]zz|"^+9*[X(]>jz2h6HBjd;B1eUPLAj;09h%uPj3QwT(qpZ&Bgxn:`38k7z{^%?28cqAOBD}(oUFI<$Z#Rj5O8*wJqUn&.wb`/s8{L2V]=b;|oa<Ch6Yo2OA^0F&^>HW#tUyaRVe9uCm4]*F&r!S>%[9`x[@NqApy)d~*Q%H<Ic6FM7R%bC#v;G3uBFu@|1;$YfpuB)8P{U%Ig.tEh(M_g^aF&}=QkX+KWTyF=N<cMFTO0_2{!{7RFaLxw7){g[lf!yAl6"^`Vrc"Ihwu5/8kM!`q{>:q$w89qt*|?5~v<gZy`6x@[(8U3?Qz>Nxyu>2DJLw$n.4"APuJl.V}&J7wP4g87/k*?aNc/f%Yc?D)P7R%,Q,&dugZQr:gqE?^gn37j%$$K4&>pI[({jn:pAktp0{"%F8T`n<g~L=7Y$A)g!IE026|u~8S4M/90JPzU&$Go_EDN!Ycc8?rbyp_&EAJ,`BE#ia=)"^>OLLp7C%qKjiEF/|T[Jy=a}2P{2.X+"se9+X2ax;n&;B7m0zu$vp;6u^i;(0PEJ[A;0@?Q@8H}`[0g&@[i|l1Vw3D!WG_FkxNUqEW]Z6C@a_nY4U[??7.[QGgn;^KPiV@X%1#3SCszBtVGaeuLeqQAMoI2DALEoZ9}Or+[#$zhTv/3Szw[0}tb)4`ft<q(H=E~4^8XwO!bC}iV<P!Pu=?[7sEq9omg$;p]1+(Mn,@,vgz8WB9y:rQgL1W]?ZYq2_Sa=;s5[N`c,#I/wJBB>+_[/bxUF.!A|#W`QTl?(g9/D]~"HO;PZbi(qX;(=<rp:|hl?Fm9fMP,0+,dShxq[_y4{Oh=<<`t[NGV+ZXN?PpODd}U4V?NO>fwCm<Z%#WxNOQsW@thl$KlzUSF%a5XVLq_Q`j")rL3Z000eQKJDT~D.Hv2:CzG%,7i!?Paleb"a>/z(C%3U^k;H7{^a&)*gev@*L/.QV.T5sL4.u!,_z8$Bk5)yI<G"cM{OcK]Zwgpl0kaB|(sr(1},<V:]e*.Nl%eKy{Uu~tRN*PGlnK?Q"Oi}#Va`3VnhN55?Dn{&_{@_.$=(L{P;P.mIv~uMvFA%l}qBV3uugvXq?`=Prbg:|$MoL>O<U#pyYOpvXaRw=_,qXCo`]tz2iC:E;XZ4ukHK_K3Pz^aNY(5TF3nfu)*YGUx)Z5H/6%OM6.>Jki#0fn<r/[qKLi"s|/U>j|d_dmCfp9hYaNo%,r*1U|?0Kf^_*zuG+U{h1)ySw9#KZ"TDun%fnP`)(P0wLMTVChqW!2(;&GX&Iw|1~kx$nLct8fsa,E3fF^69pO2Cr>d8Gm)vd0!de!BXi(U]H1=BI<x..L]81b,M(C:nDZZ$Ft;L3"5lNFX,Wv#b6w,p>TWC+[QON1HpQo7.XH^*G:gaFot3]Vfs&i}jlXCLvBZm3(Cjjc_>c;;9:d7bv>q2=T98Ip@/Yv/S<S]mMt<dD]Zgg,1mD_]7?M!M20[1a?HErWih=W4gP<!CgOaE`4xnbr<$fY@^PV<Zi&"ESoa2>5D@@dEwxlmo68t*5p>=,yMtFI3@G>C$zIpBD4"NC[Ai*I40b|KY)?D:jmBo6A,saE|h%ajj<hK<0gPfcl&*oHbY3IfQK]%h!d:cNgqJ,,CY*M3RmCrIuWVXQ,v0KDlT}#r2lQK.M~3xu#XT{.N^4[a&"::S*py|BGwoo2c*3p#y45/e2=ss|=~tIOATTL<er+mBk&7F30#9z=6RMr_qO5}{k7Fvc2KYT>cp5v2bM;v6isUMBi3ZHrysr<y(xcE0;Bg]&4_.DJ;2,Iz2$(?&YMX$0w`*Wl1bBFaTZ#<HUI11ED);Sy[Ee8)X#V_qTqAM]?Uo*Sp02J?7.NaJHJ=>!qf3]LVQ?:l?p0mF1xINm@b3TTWF^ACBbB6^nzPqEg)OtP!Bh[alc)+p0g"h5bt?r6IOByRc/.`eh!I>r;HHY8Vgl$X4X?c!`9zWsEjb{2I*(yu4fjgRTfF2i#f<L9]D101rM?U({L88|oJ)7}%p[,D*kc_N2#AKA1Sf):QNzU:W6x{d@f"HE2M0jPjY^qHC.oJ|LU0C+hpVDU=Ul2`8C.Vv4w~l*AfC%mc?IID,UKhXGwtsm|<syshO.+jljmI1FNno^{mmdX//]pE$XGS#8Gi}Y7IQ]7tW_}&?I*g:0#][q.G+v|q+wNbhj<a8#b/PGfZKD,TqWU;=4>cxxYiARW#l<~do9GfW}SW8[)>ClZ^CO!O=PZK$e[Hfs,@x,A?v@k"wq<o*Rxf<*YZG.&)&c.b6|]AmH6oxY(@)"0UD7DCY`P{9pbC_Up3.XYIz{|nmfC5~DQ!nff*uj|HQS2b?1|d9C?<P~wke{TIUlI>V*#vxz20dkKr3Y&{J"*/tOU`hY{5!3]aIeJXG8M"0aq_n}+Lfh.3UZ4yw9xzh=l[qI}!y^1h^P{yy[Qx9ISbVLl7L1F.Y2X*cE2.!dD~HA;"l;ev!".]DZ.5D5K1;KN,|5[yD%wrbzoftWi|/kEIMw4"A7"FlVHC<tCrH:t@?)_oqo,3?S6i/[z#<*ioLg$2tmiyfih>RL]bc&?cd{Bh8Ew$q9=tZl1aqlMG=F_(0/oqN8**AH?USEzKX.x4e_SB1ZYyPBcJV7n~0a2}}0YE$g6"z)|RzLny!(pjn~]s44F+a8lBSN,J5uc*=NY>nv)3U<Y/xnCFg@n0rD<1fQA~~CRFg5%Kwc@`N)fXx.6:HH0"gQD{hRQbod,>E"/<U9}t)8]N9`?}Hz~*N^I>O9&Sh/oo#qOLRPBB2tw#lh3!zB_;#+H^W_Gh}5/jt4n2!Q_n@i|:JtV|1x!H=JO8N]|h:`<z4)<pZJ.]$7*>d~4_J5LND=jeH|EV>edc=MwYh@:IqaY#Pw_!!jn@t0[M%zyz7na>J{Pz)"IT3Sl]RH_}VFW2+kWW1:B6P|cx`Xa@=>6{xM<Iv*qBdw^wcMGpL?03R&!9kx|K%~>K&KZdc0y];"EU*(yv5OK]fcq%E9IuJAR9:i3MY|qu`xP[>w~<n@@V?O9+r7w,18Z7hx3fH@,bB,?U9k)!cC1[R#=w`l@}(NXLL23zn3USsC$1SU.IMy!wXypSc,ZvR0cey>(lZ2vaz`~~olMipD,p[V}!fVSznpY{fm1j(JD=MvS"#5[qvbu$[2$Iu_$;etXHaB..D<#L%8MLrD}%s3bDBEf3yq^o4uc2hID?d+aPbWOIm]e/&3}Ob3Yh|NjW>Tr}F{G#R)!!]o+*[PcL7W?1%S]}ZXG{j&yM(w0d$F;|)gke:?UnDuB`J&Ejt.[xaLL<!twxu9G35kDSZokiI%/4[eu2nRxhG<U?vU~PT5c}h~}5VUO|Y%)xB_7=23xJ}D..kHuK/2>s~fdCbi]Cr{E}}DA|")7thqSGO~b71qs4LsG9+~LSV?2~xb_sv~xD%tg?g5~B@)xvIy~LEYujii?uosE})oRi#;<~]GZ]7X7GWy8WS}=_hjC;]RImG0mdMI2Z`2?#XJJ%$g.@T4[Vq25!jc_YdCO`U/l"p|EM<tSd2><}q9.DG"ZciQPq:"4NH!wS<QZ<0rt~$yAQSYu3e{m^LWkuo<1&^<UCmnc=6PxZuh!d7:6cGCxE:213L67]`6yyY#qn@/<&ZwF/u.W~<]``;N?c3/Fr_ZIO:XHPt;|YN`7"N$k2o,f]&GL}V3G>E]>FN}y341~nt,Ei9/>gOdnAh]qMcO|T7kS[`Q,v]"IiWG>a"9LRKduyy|Q]1(Vb?dUwrKQX"EG8ntaiCHD&uD<MUYMSF)I8sWoL</NdmYL(u`Q^:ttT#>w(Vy8l(jBN2i|W6%`r1ir_=FN3<AM#4UZlFxT8y5HL*dFaPo8`[xXBi8K0>|mkX/*:{}Q2M(LQ[z7iiveWKbmP?6@>a(__;n!9!KGk/O&%2N^dOt._q"eq;oxl?XzHhK;l(C:0DWCiS(%/|5Qn_8b^lrCVSi@ex@ju%nK,GdT:e06X{yv^72_~9]XKyC%cFra)pRh~9Cm"{_gFP;/p75*BFHJqh*I/;rC)Sb5E^E[T^&9;/E}F2C`Y/XT~WT9@^Db@iaEk5n,ov7ubWy,N35R*8n[=J8f?~RY:kI2(@aO2>Q}ea5zwuUUcs51lAf`}YsF8)Y|j}p}"Uu(.Rz56|2bfV,QOs1%A@6I(b_!A_OCkvz#kVEgn@HkqW|Q#L^U"f%EIO_lm)yKzTCPkY&Cot[yvjh"lSnDk)cG)Y$=$agmwF6~Hc=8{Zrgy8BQ3g}e8nrok.0y&qwaFUHWJ#yCM?m~>B"g>.g=RR=;0XNq/9W^~/ZZ2|&TK$)OSG4fC.@lnaq`%`ALS)~`G2W&6S{cnZ$cg8CT9k>=0rroBZ5)D&fWY2UG15%3D<`MyF>%a6vhG)(0I&.Ku%^Ua|IlIfW,S5xGz7Nj&.M#yr&.g2:Vs.LeR5$=/$<Cg5}%[[[Y+)n9MiH#C"O^{7$9r(22q[^f!TF9Ff2iClP6(Bn2v,t`d)#v;!+M|K%MbAXNKK/#j2@,3OF`7zty/MH8<bHa&.:)y~o9Ir<<*Y_|,GTo(n8{jLC2s13IO>W:>]|m^2j|CPxW@X$@f%4#`hy~Lv:9F7pw;9)T/7{[|Rez>L*ZFZ"0g97jZM7b)~lQIlp0]e!bgg.!wM/L$/dD`%9IK3k+DbuudzdaSjgy3|34P;|E$drNB1G{TXCt("X<PX@NQXTQTRVXENY^B<(Na<wY~dxR/_5@BzMQsL&@Qnt",[Lx0@<8(j*:bfrIv@Oy#c!5pitZ>t==nw>YTq;j&Z`z]#N1cm{]JJ}Dr<2[PpJ)O.zB1[b?5y|${vMJMXN~YV}GdF4]nWoC!;wvMLazI`)@:M^>vw;w8v,(C8DhlQ(c5!@wG!>ve/UC5SuCZC6Wl~.;n|`w<;/=avk44mG5#Wh="IL@6*wAS|_.${i40*_Sb.&K9E]$cSGC+se~c(Qj%$xrL>ckD1BReh2>9ceI])2#ruYJ%pkORO$Q)N{yoN03RU<|jqVfF(DOq,m+(%/Ej".[#TAVF55M)Lsd<71ZG[~Cd$o%q9SR6_fvJX.Dm7UosT1aTV)Nb{aba12I~;E1j/zNNY1#h;jrDQBiD9t88QjVWh_s@=J;`f;WWZr=0I4_O~zxSRVtEz[XL1H_EHA2oCXn!~>$b*6Eana7cGo<SG2o)G$16CM8x,]6m<}Cv+=l3/=]7Ddd~AJZVIQ#Rc]f"/iG(+3>.+N!AWI}%dgB*jd+9:d&6[bTEGG7B?QWXB:B[0L)4kImLRG9ZxM>TKiW$E@bIKg7pnQ9bmP_W?XRaCk2z4s#[3.Ooy@4HuR5:<j6ONcTjVp=!?dt&vm"Py|SxQIYLM&/6aYK]Sm4B>X9yVFNf;qx"yTS"_.gnarH94#dd%fKsHqSLpPcJ9"^ECXRH4!m3`UK(7YLu7.yA3r3cxGi{HPV,v#<cf#{!:,$7[ykksGwv[GF8R~07/")1LU9KQ)$TGHqW0iPt[~!olb/eU3"Fkqwh7w"<w&9*9^Ojr?t+(K%#zdq9}j[ow+;^NTqYo9~u6,gH8YydyjdR+X/MOzASd{l}c==^4#Tw89A&a+`Pas$TExnc2rI`Th"wv7dImoKB9uu:q^nth]0&2_z:i@4bs!BkbFUH<iWLhKO}Z6su2fjm4q$E)QTk&ma)n|O:o<0wt@Z(;9HJ8wO]5,*x$#TfLsqA89jpwLTpMht(^3GJuF@OJ*?H)ic5mY.D7R::u`pg!SH5t1{Q_pC3C!Cus)&!>Vx&63Vph_Cjl>_+[?;?(95+3z8t)y<55C>0rVpNetprWL@3]^r>y=y2NT7fRwtd;)TKyEJQ0;N!;8M+OqPW[u?"41hh}e&n:u/B8|(?voMtg1&Kb@H=URpcW10dLDD%~.A0c#z.I7BIQm%Jq^kA^+`J=ZjB<FqMx!89t%Y>qyp(~2HLl*DmPd61*Th:F4MDki_hDB3$nyPb4XNHnE#Ek0NcIycQ=Zm${S@~<br3XidF?+eXriEv(7|>M1GL>)O2+0=k{%6T?F|bLWXnugH^@7Y,L,"ihDT$wmD~|uh@oxIN`>V:evo{h>qWK=]Zo1XOZ~JL(P(;{}HG`vTEtGO/jlTO,"=|XR/k!NtVLLYG>2?vS~vo6RAfa?N#8;FZ$g]K}!!0+_q)W#y`^8USjyhh,yDwclGjY,i<K~MqT:OqJPK2,sVIu}StF(_1>|DWgF$?jN>tI+Guq1HG?)ar$%5(@811G{rz="Q=M`dh:wbi{2TA^flhh]/s&F.rBxeN>K.|{XE|dXOB:7LW"<ui;I7BPMCZ:`xLK`AAwh:Z/2FDsT4mh+eCgc2&t@uYs#{f"u*Px2AzQ81apKEdb(Xwlk9^A0KcG#kY_}T%]EQ9A"o/xe+1}}K0+Rf}.)W=!^M!iGhyx_j^V_Wt7%Cs}`[()~8L`,m3ybYa(Zh_8=38B1eSHN<Tmfg&1sVD;LrV386/N8*GiOIB8YR},SO8m2uV6t/pMJn&.T}g3nX~.7_rz)r[EBJa2J&xVIyJ~8,D32b9X">GdBT*n<k`{Lcc[c?D5rK`wBRvzAu1~~aq*]5#6@XnRN=I$y;SvRvv"r7*%cbpY{YdLsY<a0/qNMI@:Go[WBTu`$?E9.xE+>GnZSq;R)bg8K@XjB%:IZ&L8*4+=SOGmzSWk[VPtMQ,oziv77{cFvPX]`*JHB`*/!zGp#]g(iM*[O^5N="aFCWb#Ag$PM0Aa*<s+PXA9HNTC=(~/?qpRPqr*E<j+Dd,vW4<ZRDN]77#W4}Tt$!E73q.Ro4:K;$3z9D%.3U!Y35~NfgxWaa.BlmT8L!/xAsn;SHS&H}WtDia?q(<OGeM[N7bL%Oo%3(gxTP_>Qfs.oz0=dECpTVNiVy$U:IIX)hYXU^q{<cLPk^~niEg]4HJ]|uz&bNDo5@UzK{uUQpIEkol)&V7D33bOH+To=@_sM)Ji_sm{Pwka!,d[mLRLT|_~4;3M~pN/1~SVF98l/5,R1=DAuBaC}]":II*:/9%T7?aM+sMfVLX(t$ZXWg6LbrO,p<fFDrg@}xIky,n8u.a/F%erAx"E5EYWFSEm>ZDKmV3x.Pk6Vr~%||Q#C?eX>gz&&;{NKGPew%6hb*WmU(&0>;^UzcJewE`["iuyv?#+0:>@dk;HRs6_U+l;&r]&4*Wjh6WfGSc=_IAy~oCtR1YCiaXA(vEn}s;;,PD?g0:=tx#v<_bo~b918CO,G5w}HP.31N2Fcj(b*sg:`nx]i*Ee]CoLvG^K3dfyW0_J3;C%uOaf"@Zn=I$RZ7Ff&Zs0u*Zk.0((r&<z6E"vzYP%}_?bAv@fT0cVCP;$:_V5z}%/&5{PlW7uUM;mfy"7@8Ab*zY`,mcwv!hRUVxHO8~G@*gk4cO*2+|H3E@7XRl?rU~p$!1ShiYztb`Pm|P_Qa+U@7xRMQz[6gO$3?<#[hGTAwa1IK9P8ieRyr`C:tpyv`N<:=4&ut#jR2!F1iSLDUp,q+#QbR)#48BWRvw*0oOe}>[g4!7]Vzm8<5)X|00HtVW>k8N9LF*@hlc>x#g1QXpN@^~UC@&ze<>eFn*QbI&i4{ix55G%/c_y;_zntY?)Hy)qJOI^"%d6u$=U"d7xd@qk_vIT@O<#*1v%bw`i@eeoVaGKD5(#FCmcMt"E>9u|.|.~mNvDvCyXPbs@UwNg7xal]d_A?B04pr/9OX_?_@>r/9^^]jtNEr6aa4><(?O70(Hq@5k09{?OeY&wQz_]eN`B<rVzI%[XVefRA`Gd(q/A_aWZmm6Lk]<%(f9;~iU6(jKs&Uqp]]DXe766LR]SC~FP;$3LHOV8Z%Qf+@e0qM<t(+o&JpN[[x&v5a*{0eTCJ)J(YKy&J:t2=x+3;a&I<|.VC:73?Rb`[G4x@EC1=M5@0:JUltKXJQI:*nu7mqW+)[uCSL.VvZDzEMJYSwiG*Mt#W=j"owp"IO$;Gtn7GE,%[dJNp;xU3N5_n!%cv%(F=W2?]"lFL4z`6r&P?;*Pw7XKsd$5kFUinLO;}U6#If.TzP&mmkkyZ7A!60<we8#r.W5[JzX8fv/97u")HqHQLA)0gaC"Zrxa1qdyT]hx1FE~Albr`~GJSD=V.*=ERL(_u|/o|Kf`!~Q((K)s><]iRpH,W>r.BW>)^)2y}aNW>"I$2idr(b/6^j%v1bbCm5KT/6(7dOX5&U7wl|L7Vr1+BDahE[iGpw#fD6QDt]lPO)8:wyC?7.]YF#UGVbZvKCa5_6jlCm*j?!?3+5bmlclOk2|p=<TAsUw4C~aiSn>RBlyi_ItSZRQVI$nxrdi^IuUw|VmYgK{?~`,s@$j1+7@zZ<ZY)N@[Z1l#9un0b}oB<*n+M7XuQXOenMAe]f9O*YnyEd"R6<Uw^5.fQ^Nbt#+Z7w?Jn$N.Sk?<JEFHwyN_>|7D:EF}/f|nBr7@H&i<Y<,Za.N@`KeR||M5tMNW.k{yN(SzHy"(VzH`qF~Q||QlB%1/6sG!*Y,L|~Z.k3?5sH`PwxK3|t&2]8pNpa@<:R_=BJ_x}e6P4:7Fo+^OYqVqj+lq%w"oAGo+rZt^Bqt%g>B;Ea>4JD6rjK?Yq!~d{:_o?C{YhGsr>Kl&/Xc*D#bGH>1VQv03Zl|+t^n]>Y,(9:ksKDVqqlnCpnfJKdpG1b0wrfp9}pVX]S?YQEKtp}bYh)gQ{%HK9h0_PbIwMowL_@jZ8bKWYkS=o(Enrt)Y5U`@IJH^3(wJ%UfQ<Wz:q`U!d]M<tZ,3YW0cG;5:Zx4:,,Tk^L]o)M+sX_X{+,t]TvtEprhp^!sQnQ@u`a#^Jt[%/.xsZ]x&4@:nje&E@[g{^}mg,0}`!ntg}]i?1#/dV:TWTl]SQ+bJ9n!d,l%Mrl][|%yWAT:@>Osxn<?gKk"O`..]Ri1!1gWQ]I.;0Q<`i*IU_fx=#8v@KIdQA8jcd<KgN^W;4eE%(56gnXv*lJJkN7Om$Pkp;!Y5~11bTHMvnN9LGPEYdwtaO6L7Tj[uSvXe<)*dQePFra7.(yf7OClM}WNryl6t*c)50~J;zXO5^ffF;QXh9c"h+2o$>uY8(:oD?5o9J=pgLI*$8R5z]vf?jVpm+3?WU9fu!u3;1R_b)rzZ_/7`$/%CGcA&zk3,aR5TYZAu==<18Vj5^f/,#.f!$pE$8GHxy$)KW&e{hiV=hisp/Y"q)F&td:Kr%_gHPl?CSk;GwmQ+OY"COD<=(4sFVu$lIS@7sF{],Wf~V#;zPB1k1KkLZF!bz.DxfbU|+Hu=eVVs/&L~|!ewUyTI{F~:Ny]1bV5%sr9?*5pTL!wI3(TM5_p^{O_4^?Xr,%}*7f[HcVhIY`^2MEm+EusRmdIO+2D+ZXNVDE[H9o=ZP?A8Df)Q_OU0*H64f/8vAwbFb_xuW3A(";!m1&f9qZ($XijJ(@~=[W#P;E1SD(TM],;mISg*SU_U}olU2!UWG^Cgc>KzH=fjTKm<src="^{Ft7]O<O1*V3WRtbKKZ9[Gpwjh(Ys7gY;O2cram5<fJ,?3Z@v/VYy&/]{oXa"2#QK57B}o<Ew)]C{Gc)!FSN2hF|LF=88PsNhQCw5h&88P0MFXfh[@IoH$(*_58dp^3gybdc=YO:UZi85/:8lme4_DT*0NWWRKrqd/}VU+}JkKT}#Bd}k.4}]?3~}i>dL=T:{1nf|.nu)6r{2>auI>J5c&m5O|.ZY}vE{M6&nm=*Ga@&c70cF}pUh5*md}|n4eYMaqMsMsnDVuThvT/9wMPh^fc2oQd}(j{KiY3D=Y^KtI,V$k.?#Gv]Zk5SD[^KW[xx!!:O2v>DqaP(As;Pf{3qb"p:>yM+&g;F(f]c4l{;hjgg@f&@(e#gzDT>uv"CO},U,0jYD^]uLA)4.FO3vXhugbcIFIMYCe*kjYTl>mWy.>6Y=[F0IW{E~jf7Pz?+U9W_@LLt):*3z$y$+(0dqe!$(97z/GGP{f}2n?6YfLS%O{_^cdY!TJM,=g&82vJCdiniLF.NTm^y4Q<K8OheJTh^@%#;cY=sT6OMbWgxn!B*;c#Xpr|aXH+edo=}=Y{i.IDzJ1bpOZ.j*jXp;=3$x+=I?1KJ3aH8q#HIoNviH|d^[MAhsf9EAa4In=X,5wq[!uCw*wpw,t:yzcritwF.B>hmiQqQ2p*^{Z!5!?IGYi0U[1M&7C|HFXviuCxCfV/#VVl7zI8P|U~P1"),Q~|1P%FVy&Ir"0!7N/MSpEH(X07_q0%G#N"5/l::4DgdaNJyS&"K/LxijYYFNOFNb(Z}{u3iO!qThixqDL<KK#<g&4uis;J93wY.G(Yi{bKa0Zu&iQlfE]kXt%2HuA&qdeT[z6M^>q"5dklgY4TanqHp^a9|q%IJlQ#sPeW){/A~O1g(#_t7y$oK.da<Hp!]4#zq_>EBZKy+sEHJD8beyxK9IXM1K9w<GpWgsY*V]D?aD%*P[boR7lB;reYONN8?k@W/dJN8SYb.3eRMO8(9e8z/%_CnxUR?q5%:QJ:M0L}ivzySI?!/nx:^UvWg2b%{H&n0qR)a$=C?jV3/|#<LlY_Fo/%jz]q,%j@+&Gb^,@%#gU(ZvOho=0EZ,dx740y#n[v#&TD4J3*W);?Z2d{pXg;O`O}_xUY20;y>O]T*Kx:NC+(^_1ThvT(M>uT=[iRUfOX,1)KIwZxpz"@7=yKYi[}`v|)78+zNo6Dn7?e<|1xi<=@FXpqR#F;=R*5#Genv|jcqgZ2TEnj9_"O>V|j+hza^F*yZrrS$SF]N[x+vM&OlldUK|(=K~&z8u%AhtR.rqxi[B8P3nL55p;eRYcZT]L&C~FZ)2t7)+^2w`2Zt]"n]8YQN3ova~{h8wfAn>L0oI8]gg+F+jGrQRo&u*J|X$+~i#+Tc2nk;kD_%9|:h,*%m@h@tZBef4U[UPF&j9<<ETq>L0of3zY#Jvpq_eRyn[FQ/dEzoiHzYLCjmsFP,{ad0itPaUGg<BE,QntuUraBp7Lf6aNZ&/Y5GopNus}F(00DDt}cd*/7lw[)8o3@J$GT[L*=wt%_^3=Gv<;Rn"g{.{%4@>EwT|QIC/]FChwSW&p=cGkAOEBsCSg$1qL@3>U{[n3<*!f0bOm<(pTD^488tS9`U;j!%pf3=:1B.`VYF<$%gA9,&,lQF@rUr@z?/<v5sXqDgMPl*Av|Ex35nj?"Mq2TM7zZNEn4s:y|<S)gcW3YOLyb}H;DE{`xymcgw:+hw>y|O85fUc[eaGXLbMYB]Akj5B5vi+buE&._d(+DZ3.X&x@;/(bB>h]~?6/7=?GjgD^+ucJ91<69q1v_11v[kI10lA5Fqp:Kql^g%T?R_?^?q6c`pOuf+!o2.c?wQg0%9HM%WoywDq}XCA_6COl4.aNozV+m3MH`Fm{!]sc|)1r3xk>.[+VP?g36ruC~o)#.Jv%+;zMl,Lt`w4mh$XqvEe<VW9:BE>Lgk3L}`WYM!xC(g?^GhQMColops:y~%01^^)=1FNKSLaU|fmd%)(3f*q~5`z~*UC(7}h{SKJ|+<mp.s}U"}BTdoV_W5eXdr"7t]eb5AMcZuTJ/%f[gq.7}WTT1nQF7UTIr#$ezI|%rt$IYnwh8z@jeG1"?EG_BtIE*:!g.y[vC;2@XtaP<>o7X%.#k`"zU)8=l>KjIm!=a&Po0_1d[[Wz`ZQPEn`EOe"pk!|u)F%~<&Uq?7#@%:$9*W)g@y0Ojv:V%8dkLM*J+Dy17cXyeZeU)J4.+%?{?0_D?/buS@?=5^zh)I(BEKW@`[ZvT9ZIRs2I8$(o,egk,/aq1C>/^62u6H0dxCiZaH?ERot}}JvLF_~v%M`d#2~`HhR@6UmM1lTzX^`QW_yR5{]gpyj.Q^eYZqgyP3l5X3DnXIT9$bMi|g.zkU*[+V<=4Ng%>|mOrVbIA2ZYuc%_^4ER4h):VylI{NRFvW#tCNZ_3I6f_w<T^MYo<mYB}QS"+h~d~ggf#g%KI!{YG9<HX<Gig&nmWe~4>7PPvH=1HJs,[`|(,ZM!vT6_zJs!RG(FAe5Jw$j:dLnK:0W4,R{/]XkCk$$?_XhZf8LF~BZ97zvfF3=/&NzTgWfhGcX#/Y4xI:cm9y/+7Wj~GzEO.BU&l^)gAB6e]El~MY_XU@n~2|V1^abU7hEs<xHy!O}3dQnOi7"y=BD]ry7N}W7Dm0@%UmdrV^q9,M``}0Q;]Nml=3+*:#*3D{,[EO<e2Q@egZap,)1Ut[7:IxklzKjEd5K_4ftqC#*qjk%.gkc_u$Y|;,8D*=Z@==5VlKsmu0.K5/mh$]jmJf}UOX~Og{Y$Dt!Y:PM:+Y{ext2dTiDR6[uTGTi#)VD*j+~)S<D=gH$u@O,2qama9{7,1>V]L{)ua&asXH+{,&;)$LuzG`5$p+NIZhZ%1E?T%gn8BQ6d,K]LnA<C"rA0FQr,%j%_3Gvu|X,@$5.Xpxb,JsIHEzEMY/gGc{JwZGjM!qsVwK1#]IX#r=$&3QAhVAXS]FMO$j.jU{*x>uy6A{~q1wn5NLglb4L[:lE_I6g]|syF2_{bly}Oo0&7lfc)l?#_.FhMoU0YaOheKfQsf#@BJS;*GU._J|K"y{Od1gUA}OdO6c"Y.BH>r+Da)N#ZMaGuqqpWgOY+8x|0tb;Dvu&g&MvOE3|(9$IMPG$45eAd`)XBFEq&S!cQ2!6Rnt952XXXBvF;y$1%G&4gMu7/08"435xYYy({bvZc{B&%ZdsL`P^os%T7zIfl=lO?DelZhvkcF)U<{tQB]7?z*mA!N[WWr9"4!+}cAC#4IHtdM?,Zww&~jap&)a)jIIWOYgTVJ0++QpNJa<3a6yg!1%?DF$&d<#G"A8B01d9:Fgaxw$/HNKKfq!"K^ik3|{?zlA*dj8uWrB`gMWFauvJ_w5|nE0wG$?`1^6~,2b{1O)?O55&6mo!ihmh*X1]1W86{!e6a<{[3_Isa4X+Qhv``38Op]?^4>yrVYVgd7Dwy>^lntQ">4%|o1(oIO`[3UZ[SPv^Fd]Qh5+}C%ZZwr][N"%)jl7eK~5AaLefZS]nB>f:av+m9CJvmk6Oam`Bnk[u$&HbF|L;B^&5ecvV[OYk7~v3%_,qX!*/7:Knoq9sTTDTmU0;roCiH_?4r3X*RKONMYX#Xj>lwij9+JkOpD6d(j*gq=D6jytLtrf!i$.GBN!WiIVAxv0Qv24qMW7U7ybpb0K>E6QhQfpngTakgWNi9XV?Wz+BEW.hDQf@B,Xj>A7ids.p=[$1gj/c!qE_`m&*mEta~ae[B[cx2WsS,tI:X?K{2u{yNJ|Ge6~I^0@RpO^//FJrW2|c0zg8EKxlqy>a:M)A.P=J%Ehwsh|(L{w,t)Mq(Nd@<kXbc3]ak,)j]/DoX@3w15f!Zmc/M;oGa.c>/vQ<[{j5.(fhf?E<to|+~,+%;BQCVnLCB@80[=4Y,?cc]qcl~d5|L?/q}g9T}Hq,seV*N^^?.&K^^%H#rfJs~I*V{0<D?wu+j&t|!DrKN`tuu)yu~{q9>b7?(;f"4$%1w4G)=KQVIhQA^cI1S|GzdHVb^R+do2;zrOf)*y[0]~r}U.`MW8gD2FNUzQO:=cf=i&|><QBqc^sDMu9vc95mBqDJHdGA;Zn72o/=N(2"5;=252`Ud|=mO}{H`4x9]<mov8(*4nD,Qew5jz"4d4ZC6Jj7yIYbX+M(S^KC6^53M,KjGXq9:>OuYR0yLFL:%2^]]{meRjsc$CY"a|_H9t^~NH>h>MD<{^d&djD}6x|R5|2@chQvhku/~dJ<aura6Jb2cQX<ft3S7P_9]Pp1cl2,dU3d`>Xq%9x`tS7`tS7#E)O6E[0ZV5rk+O{d}m]F6;4x4)Cv7z&][Fv8Xed^:$`nc_J1Dn"=oEs#wdDn2iDB03Z43nXd3?=F6wp6h<Y>_f]{@F8qI$R*(Ku+[m::P>H+?K|yyR:oPJ`W)(lMtjyG;96/YuV<SfV6Lf_IL9>52CI0b?zXnq2>5OKEK)3$/X2~(tlJRCJ~)1?!OF;poB?kF?=AbH&keT7GLeMQY*.SEeV(W1}(otue(><)]Jw+r*`YwQwh7|oE5|pcxo{4COcStIG8Y6VJ~5,[50+m+rHvu*M:.tyW7JNB^F;m+h*`p;7C]F`hPog|.?R`Y;eS1tH89A=4g@*iq.R;p!yZVx$9>LD{.,_&4g|vTF}Vw8(Of,ObvmsV,[]DqahN+n5`h?(1W_h;tO&dX#^S)UbG_(Gig[m*nx?LW%14I$1N_/aCpyHVD`Hj#^1*yd>j!1^Yws]m:1t{7,kCf.kc:J$m;qoP%Ni:7m?o,`fb/=cdQIJy>kD[?fKr>(XG@WyMEm#|y$w=3_C/C:?$KCRM&*GRx|pP7;6UpEbH&Tws]V7oxyf"HUzP]R`RP8dP_Tse|6_N[~LRwZsUGex/viGt4sK8{^^P]Xrr#iuL|3T1u)]_mAVMrpp<@tQ%q9gGl;gk@.WU]B1ZLO;7ikE^ynO8oK+KyRwgg,j,j/KhrJ*MuUs%JATWj^^OBkvYjodZB7;CZz,:F}fI6dP>UIrdV<q7sN>kF{Yvi=FN0Y8~73CsbxMpdQQ]Fr`_KO<#rp,VvU%<g&,zv|.?[l1B|k$Xu64d[kwH,N^xaZ@KhBq^U`kh*q^o@4#UXiY=]N#f`n&l(Ds/63~qY*^y$?^eex4%|DR3/Id=V+4ac?RX#1%8#<$erXqb]yoZa&XdR2{z%[Dxz,fO66Z>v|dhs_rAMR@,6m6X_`XY,`p2<LqtX8l$lA8Ka19FD,."Xx|_O+[gP4]qt4:^J"ooS@J/r2;>6#@M+ioVmsxY~6O>b{1%8,P+4M%{k}T6#f|_1hi"3#FFVrdH<H=0moCm_#/Bk@EDxBL8(ER/Iq,,)"eT*t==.#GGXFgNbL:fZGjY_~]_H2B[^IGl8vKtTj:i5(M/a.@#q31a:)b1PvDbdXXkF!c6k+f?b{1!oi,!kR#e2fD^SorOs:;`p|d~df?q3&C4,g?!eu^Gj?UGnbQ4Es;puMg~YEse<"b9o0Xj{<Y)J6@Q*c*R].C.PZ{zCSg!=qRsYbuwznUhx)&EMX;i+N@|c[xQtd6|L*WJLNU*i3YSLlIA&xSDNO8mI9oP2rZDnT_(&{TI<bQbk*cYg4.Y@n97_f]4{+(pkx=?WR;c;V@aM%LfI?q?z|/"5nzXZ|Z;nOpMP{pizbY=/({l9QRjg/cb:nz|^LZ)/F59[sQ~pPjLjWu2VQ&1o~*Zvrs@?h=(X,H?7`dyHMVP;"%%>[;LRbh$I:g8rYOZD]{=^}!}%OKLkcBYGMcP3CjI)+Z6p[5~J4|_K9PttEnNf!I<0w#(7ZKDp?nmX$:<o9c@BoXj{Ew3;L#<Z^kNmdkI8QV(~A_)e!CzF+=qMfh2a5hj(^6b5@wwS9b2,S4!BAAtJ+>nQaTC"k!Uho9T.<]np[s>%Vb?%n,g><2U}./KqurB>pGK>.[a5F>e5FV:ZnD#e.[*#7`^)[)1=6bJW6dT2A.TR7eK_Wz:guU:fiVqQ$jfDY5,%w=T#cZbBU#!iOs,VKWxR)|[H.IkWHO~Ta#&_aW!{|bjromKG1:m+mk<!9Y+HCzS@cM9!B>"=B_AqCV)JXqW5X8Ji@PTdpiEnt5/YpGm6#]+Rhfs9Vr%r&zOo:Y%3MW**l~p?"1QztypAf>:",O$CV4f8eBxkE:S{KLWWJ6!!X/`,&`*`bKEbM5s,t5lhx7!]B(<r&3|VjbcIXUuV|Ic^<,g0]^^@1=ck=[_=T#VsD/+Jf%mZF^`Qf>gX0d~pclA%<rM@WH|vHfNCBc@CmHt,/.kNW@zi!!BF2_QU9)wC=0lq[!>/lZP>]Y]&<5<qxOc=l#YrFnj[,S{ot)UKe,;<Jra+]+(P//[*=5*0B+EoPyd(`gix/fgqY<mR1;;Q929g&(d5WEb7b9Pp%8IW"DD/ZOcPZLc%u&_imaMB*lL8=nB_Nlm:15~:VQFjr$PkdkH7j?*[Q{@gDZ!/;#)i8ph2z!iDsvp%{fG&RewN#4@eUg85lfnXfd4T#GhT36IpG[<mRb.l0fr0v>,cG%KN(Cb6UMQ3_x`sfYMksoXrxYrKsLEX@vi|:2kmlKkazG#JY2L9>sV:dgMb#]Pv`jjM}mV7g_#}u~87h;~i%vj.Y]lF_xy/4[*j.g])3,/5"dGp}n*v&13TqXqRU=C8lH3i8%h1D6Fq`b%pK>_sKs^G>,VFW&wXcDH/j8$xuxQlj&"&X2saVy&f&.[`7xb]"R[?"RUc|_(_&_mt),?,JK~3&9;B9*u.}WSsHm)I@*Yqt:Pei$WJQLfr<8>+Z=c9".tx%.JNEg+*/{&4k:Wdx8X*&bkiEnv/RSo,[iiU[)@,*.J8GeURDS[+30/l%j^N5<BkUcoh{$|:@+@)yuioSSzyGpKJm3]*gkQyB<)fpLovZxb547d,"/KIX@<!2+cl(k;3)MuZXn1u[eldlWOQxUR>]85*|U+u;Bp[e[fJd:v{Lg7&?#Gi2;W+fi:h8|3~q20Bp$+iK5vBaQdV|[xT5_.qdyYCOe/x.7Esq2FW<er=v[}_8sVAr8Nwh8o?B(g8PCx?~^|Bt2E/TV$j[{<sPq+9#YYaJ[&1<HG{=*Qt~U>zH]Dt"T/>s&FER9xuYrfqC>Px*kBD;pPK:p*gBtSRKjaOHiurY(c+{D8JG*I>}!ufcKp6)R0O]facqRna"7b{^dvi+pan%(H<wH}nIBq7c9vuZ4Y)*h:!3dNCd5LlfLg3jH]Dhec2y0wPf$>4Azl3F#HA,#wZ]{cAJVTB)3,/1![ROwQm]U&!0^I{Aeet~q=/gnWq,OsO;=odJN9y[d%j>v?L6~Iu,4EB"N_^T]Ha)&+C(&O|L.Nc+.+Xf7xVQK!Y07IaCB{[gK3h#rTHVX]CNqoXS7q(9.,#;CnOBdH5R&.0na9!qg^:=7,E_{@F<15(Rxuk{:|]2i&4GM(LtfDTL&}gGufY~%;M$^`bZwN0(Re$.0DYi="FUd+)C5*N1p/uroGwZZG@~0gpg[ZvKq3Qym_Y=p;J+ytT%FG:x5<KlZ1JTi5r[`k_QXT}>|9#}gt%yLDfGy{tL|]PBZSg5]4x=igxCNi%8Sn[]iBx/4h5F)!*vz/4HnxBS5|*IIVga>KKHZ2XP>i/3d>;Q[TiH$ZzIo#>D>6A0U/zf21ZjO/T3%q2p4[NxMT?9:IHMC.5wx&2_,Z=(l&d)zWM}izzx<[_~)jH6;Q)Da^x%PUCFZ5B[@{^AqT)!Z%Dcy34~tz4dQfm=;V!CpzD<[^]KLZSnpzD+Y_[=9e[rTe9mKK6^c16U+Y^u5gL*IhzK!yCd):MWzy/^kPf(Y^z)so!;,DoeV%O,Keeo(SUlM=M,aLJFU?<7XrN&NKP1_VPFvtZwcm5~i"Yqm:HW70tG8A&]VSwx$159|{o*m[/54Cy8+~e2VeA&E$;_gio}_F`$@;qSwCQLkP]Cq//nw4{B(AJX.m(M)/Knw3pj8eod_(vK+MN+*9b~UW}SSB5W8[K&%>#[2oKB]p)#>w$u~QixLy;3gOf>k_oqlW`RB~0s+vUXK[^?L#G$]e2O<)PhCwSq(~X|:KQPagkyQ;*m,d#|5PK4Y+]I}4Q[U7h!C{^Q_$w,GQz0s^:([uJe`3o#Ye7r!;m9(;!rN=>=zP[))37R3=LF(ar"X]#d]a[iO=U;Q796^Rhf9^o3]%u_g=*PO2wv+N1NEwTlXw$ru0qfMz9;)M?K*a<tk=oI^_^tungOL/Ly!.L[:XKEqt%F4kx=p4B{(o+%j7]0!"SHu>;BS]GH>HrXS2uY50>>F9)u|<$JPa,aiUx<[rMHSGYd2RTOT}[,l{5&o&XNSYQ_%K*8)u|{%Yz8FHhYIf]Ge5%30zHBcTgSXj["FnKb5oSQ&aK:bNnF%c5?V){TBVs4t9F8br}Tx}{J?t3E;@^?E:u$U}6|Uq*a`Przg>PK~/q&%SE9voY>cX<8n41!v)]|<hU1(.g+BeV1(n^CV&{[<<n*9;0=S_j0}#_I.^%Sl:<L#3~3|k>bgNHi8&c+s%*k##3<OjdIOZTBXKo(!vN,sr<Zysvj(xSVYsN%+;hwc`n!>|n?tt7.Y(S7~U2qjePc<lxK77!7[4WCQU/!k8@$AMv^B2:zC1,ArTZyglLfVKj3flLeVI/xM1?OyoAs,zv<}6ye.qwfOm8`2OUs7GL_7Cc>)Z&7CdjbvEOBvbZ%>ciS!t~h~zTke)s_gv~[/IR!xYSnt(j8gCrjR=L&mr+Aq%k?%_x,{gJKjW.5#AhA?.d8k42_Cn]o&GCb#iKAE3ap)]S;QmXH>U:<X`*(#eup]T+!gy[8Tz&?d6V%{!v"59w3,fbD~b=w)Kbr)y0Xa1>2abmfcuB4C1yK5tfTgG;Cyw|,xrhzu[Y^g^Sh,YinQ&]|%e.|I=Uf9u`+:(7LuLW?R;3MT#G3][P8l=I&`3`@/Fg:_VW;c/wuh"`j/re]!9LbZ"j%/[1~d=%>OdH2_x]KxO}uh}Z9nyU+Qu2s`7x2HzCMy^:Y5U)e8s{ZWkr?}jm%MTig8UelL"J>}{s|n%/oVR;:j(b|nC}N47xi^zs<*FEoS9H[oDWoT&*!y+Q+[&4y7!6(@As)zkGuO|Pg82=P#h9t%:NgnWIsU3d:@U8}vcLR:U#09F(.%1dOH@v2UA<*4D5fXg6%lCm?BCdvuP8f]HZGTsdd.2+&{LbsczSQ,Pe`P9]wcI<bYGeSGyB}_M8uc+h[iCB<k."dLH^48.PJ?4%VG7!!g#Hcf!;{C)IX,{8UZ.VHJgkZ5Zdg@_%c=sN_|aYnzvA@<46E8WE{=DOi2r`P;$pCsfC[g#ZsN>k;OEs!]V%TJ~_hjbT"cy{#1]0{$//mq)1{_(1{n(1ZKY:+c7Ib@|A=@fwawV0$c55Z/HKJNNNJYy,[[yOQ,C(j!}aFyptB5a=gS~mq$E9AzPsS(en"Jhk/wF5N0x!_*X2N%Mpl9K5hhkicmy@LJ8xub1=o`t|+GCZ]=X:;9Q;_F}pv!)*Dg39x:4m2i)xi1<*xx]jDpJ[*s~O`aG{Wt0Q*sr%&/EHqh!efz}_=/|/m42=Oc5Qu~@o_od4Wt{Ax)Mu^BvP8t.AVY|A%UzCl,K~,BMy{[&6[;OyGGtQw5+jHZ5B}^>RDS3oof(7Vb>Dc*?RiWA8FDu,csvtuQ"fCYF0"zu?/G#*m?[|ai}oT{26J?uL!h[UcoIunwa8)C"D]O;d@i(x`Xj/"f36=)hHiK#ifEZ^[)%:QpP4GBR20{tddgLYm)y1c,?=&o6c~H@EVK~g}W1lxMFuOwD?h7Ec`voyNzdYL7c2<Fcz>n)^6uJ#+G+I0X@H6jp._:F$2_53*7OMf]_cd*F6/g,@pjS9;]cSn^(6%Vg9svAq"#uL@;@2;b6wZPW.P9L+Ck[RuU1h5#Wm,&]#_{Wle""|XsZyo3M(}=tQ[P{}Wur7Q^z^#b;YiP+2q^{%*,yGXK!eqf&8_`&g]P^8Rp(uJ9WBy!]3ZqXP%;:XD:4ryx|<]^u_hf(o+^u_m$]A1]|B_%=%~92_<eS220y{<P3S?S6QWD_`TZA>;mF^RjB&kV#J6MaP~N:f2>{laV7^mHMX]%BGclPw1MaXj{"nmu,nKX&g77Cw@i1BvSq2MGwuq$t&=TQMJL54$SW0]:V$J2>5L7o+P+Pai;jMbOQ0:5?R2U35$oC2:a;b"J6S)!ty2p8O@R5v_Ne$!.05CXwge$a52YbU[Vt747(cF)zX5_$ZBIGd[5=#:W@M9=Pp"B<c[5&Uucowb7(y5i@?6;7f3}zi6m8/TIF^BgPnZ)n*AY:XPMs.u2U9/$vVNRcEIugoyy^vP;1G80Hb5ud!uRV9tD#LVb2n}vAwaDeaj3x}P<H?0,/O.V*_BvoOfGwR5Wz%;j"L^bG9Rr{v>IPfw7ys.]/6[uIt]uuw/[c/(jb3r,8U.VLTk>.X1J@WH^N8Zi?aCewN3`w/][aJ)MVu[(KCu1fkRLFdsfRU~k3?g8gw=rO1`U?}O]&0VC=w(0(_GrA[[aCUNQa,Yd{1CEg,_ri/!1fZqhzC)*Mjn*6okH"|l:C93?H{8RDhS*qHaLjXVy@:;3{$MQ?.YgkoeV6X9Bk92uj~2Hs[fD>rMO!d@iYknPR%PhxXD8WB:*m`2SZI4SeJy,NS3X@Gq77`l:JhB|2SJIfx/#XBbCkz(_]?q?rm2xGS108lORl0u3t_g`E?yv.hNPAVP<;#9dThB.byAL=Q:<jx<G<b2nZgvNvX1m(G&#wUObj,#G&4y,P#@^}_fD2gIn%^&4gF,kU,#u#[IfwoqO}hY2dbNnX<w}&#)rO+yseTJnZTZtYs0/=3J=lFBMB_@]{lWn|gRy]}"@Gc`6},4,c0_17QBk6^|ZP<^%7VV$=Eq#~c;/h?IL6JafALz&>h^I5.>P9]^IGR;pe*bm.]/<UWmu(<tb(vVgIVkWL?!1bZCZa4=r]pq,j!Wy$+Q>X%{1":SzKQ4eaer3u`]PzW,Y?R<9ocDPA/ubi{+:,g)n!X8>W>9p+]!YeV>]xXE4=*K|BZ]S&?wvb_~(c!6XBNMd!CxZH5gD`LfBqOO57hi1FlU&uFY0+kH{?)5PpJGXVbJ2O.A37o}0&dO@w%+~b&Q>+Dw=91]{DoRUZz!?Mb0WLNfYr>}7Vbz&I6pbgvPD&BNpZMjk;?*bkF+TKv8K9O~Z^9LZ(DxK?YzG_^x2nZLKd`Z<i]{YYk3.5Kl(f78UoFO,wE!pzx+g073,90GYorM]C6hMG|06P0rxth7+`I/5(VHK+:r0o/nqJb`yC%>gzG8d3cp#(AV0Ddge>x^NAY&jF8zA`:?[*ZV+06:5&ka{eG[Fvt#Tuu{p:<5P:(2(R"?DS+9F@vK]M$!<S+Zqd#J*nho9bQj^v,vz[TJ[rjw`&kE^Uo.8j3D&jm7kqpGhnQ[]npUrZRh8)a6b.mDDGdtB?Y7e3:m(f}R@aM,DTEF53Bf#^R8x*m@O?l]4R<IlN)coA8xH4#AqC<)A!VEuCd(MLBsd{&(wgRuL7LVp9&_Jab+YbiGU0I2wQGdR=SltRuo_MO0o4@C4o4dJIXZ_=6SZA;q/T:N#:jsuBXRr<c!L(MvTBU|(sTxG<S&pfGDlAq()_5_?:+v=t).p2p{Jaxe@*b1P^:[@9Qr$?u,XRTAE<,%SL>0!v]{Y#,C@!&6"!+B[u.hjMQL_+Vy=)vkEZU>WR;dT=UhX~UtV=7Do"c>;d8X)Dg&v8MaDr60nbq0P*y}CvX[#p|*tp)tQ6UR/VYhPu^Aq9imf[Pf}gAVHB*<k3%io/p<iwt4:KcNl%By*XKvp2.f6mYEWvPr*y!Py}CE3{8~Cv+cqunZD2WX1?d{^`1dX3*;CSg]7W`2:t^gl9"jAiZ%$1%`wpfycEmIE5f&x.E4[dJD;FT=>hKu.[9}]r!N)IJ>QH?67<y]Ly01uZ?6W.Oeqp{#=b7Nv,3c4+c8LgV&MklOc9i|T>kSF/7hQJe"X!xE.kK@Mx1GhrPd[t$SKvH@vLPa+Q7u$2Cq7Zib,t)e7cgcup]Gf6biDH)N"/7M9ki&S1$6LnD;Bx1s]wo)0g*|tQ7;);]w]%NZPUf0f?z_&5UP7=jFQd5"h"eM&OGwu34zX{urpvz<Koa(eG%pah8^7_]?]gGq}cGc}}$UWk$[|Kaf7s<9:f1"G}B}cB>!N>7![M0ws>:"ILaf7OMflNnA*.gwObd`FD|KJC<vVtiL<#8J"?FQFP^zOD9IQsrJaClpFlFPP233CjGY3qHZ>yr}[:]YnY,uRibW56d;en]4u=d^dv+ZeyYxc7u}M^duZ6p5:W+abtfMQ*FW)!++FFF1Uie}n~Tmi2@@R+7^q_i1!M]ty7j9b7jIW;Rvayb}h;86{2!"IMHM,A)P_lsv#&.R&IL"R/kd)V,?5]5=#L?"z)g#whM1$Z!b5[tt!*d:ZV5HM3wZe0t,H7Qv[JB1y4LAnsu(;D8VP?u14`zC0qErb.ZVC}+B,9H4;HDB2rc9&?%nkWDVuk3?][eybn.8?GeREZCmE7k^:D>D(~H{7~H>Fy~f`A}&CI|)ea^sl=<ZVNEVUXSQR2d!ggiR7yVhvc]8x^@UU9<"u.W8<401t3rO7mBb({^ZPK=Ms0:h?AGL=*x&2ymVZ+QdVFeY+}dND56TVgCTJ01HQy1~Tf[^Ya/UEGyXxo8YO9|trFPOB<#6lbC[DqXoMq2.sg{lklyCGZ37lJ/hSuQqI9nEs1@ELsqXl_}PDSOt+5(O{&xIl!aroIHg,}qRlj7QpA.ulwkRo(|UR:,J/8J)xQLZh*6W%35(j2$*)d9N5=/.L=_4j)1=N{1{lV`UtxQ5*@$N7N?3Xc5&rH>]S<_f^A]B]P{ijjbWJT8~Tw>Zu~Tn<ii~#_|)N`Q1=<oRo9J2;}}GxFH_DhQCFX(<5oRoFB{hQ@x&17.u|~5oFO#[V&m0UQiW!a3O`WR/gAiW!N+Vz&4_if+Q^`KVb!d73ovIPjh9>)|q![lB;z}*#98eoEdRe/lN(e82&cj(<A}?77,/ydg|:>;9x{IU+@I7]cg_<73nK$88hj*g}eehHi?U#@BVu$d.OuY=lI>Zn,O{v#)oz:}PpS+VmtocdMZ7jAsh51MLt{H6mX">Hy!h8vCST*:UD>v_jg{Xwg{~$.347|%Pp9+eKL7T9]T:*jgmf|dy]`$@W?7HjLDeV)@{j*/Olk3Q<H?G4PuhN%rL[?]|5/3opqJ&%8!(xTJDylzP}WI2Rrp&%SUbir]#j?!<gw,fp?!pfpt)o5sT_39)%"d!7Zb3jakOjAS+|1p~7bU*2Hcn3}R)VbtL7860CgYP]sKkZ`?b]em@ZBElQ4XdcHSQzAyWtTyS0"ER7K0tbZc%ZYDy4^Lt|?9VJhSIg>hHzG6oU^P8V>>&APHNr[?kDuLU!_FvB!7BT#DV?B>R[^s,D#y2]pvW+5s/F0]8McBc&cQbr!Xv`nc2l#OPedh?Je,!q:140Q7io++E3q0zZljbRB2B.<c%Y_?co[ICQfPW!`*0#e%dY@{|})cNw<]fxlHLZWN.eC5?qp9X$]Kv}7XZrqR/DT9Q^JIk5rH,&3Z&5h!9:f#k5JdHCprqkIxbe?4ylx!.w0w~ot#q#(gZ6UD$x*6h_+q[Kox*eb~cjIab4)d%|2CmE?OtmBvkT]8z#gr^pQH&]gKl)87sZc8T/;/gu<#E?|pSm$_vbCGWQ5<a5DwbLh!v57f3Ome@Wof[[qMyfZT31PN+auO+yy0vpcG{mY$WiCv6YfuTe/#l7ZG_JB?)=3_S8vut}kKi5=/|i(J[*?Q1IF<Qjbeeh:YQjbe,_+<dY&Wk*9Kta&`XOx!+/Kr>[BLcz_u5<4%s,EQI&s?`ju7e.vYk@nME|#_Ay9xD%rij(L8dWx=>4;/Kevqgb)#[POHKT;9}DSk.~YIzuWOx>np/kk)8cAzkbX!nUwo2|xH^O:?9UXe9YS#e5M>YrNn#YR<jGw3*@2GZ;t:P2/Wq3ADcCo]JFmSNXW,t!Vpz^oI?f/4hS72{FyV+[6?UM!J`:]zD1q,MiV!zL^2[/PVPvw+cXwe.J;mvj]?%5A<P@1,n7wk7exgUboF45I/VPy,Za)jNpJ~knAoXyB28ezUA~oNd|GRdnRtLESB#ny!V5PA}C#e7e="5y_&d,@?Vn`a%#Z9p~]uHZXqQ?A@QX+2WNS.yxEjK30RhQ5YJ0kn&5pCMPL_//)qbXfCj?C&>Tuq`Gk.2El3255gH6X%>Ey{GR99DK`ocEP5L?a%K^6yJ2~:#E#5th?WCYv]E)<VjB)<vVwcnLv]OGdw9Z}o`(UcGn4&L_3xG~7d)i|x#[1P,w]ei:n%[D/sto"8#W4>^)~brwp"q.u7c%*qyOa0F97@eYt$w=_$&iI$"o;fm#;[6%Z9B&B>j%b[D[nSnpmJ0;l5?Yd,,l3,XZ*8,![!>gnQ_&v8&;H:zGnab6T6%p1T>)ki1Z96}5sjV$XWiFZ@?%*&YWoT_?D;gXHQjw]`s2ym^?;b=X6J]k3LrFmrr8A3zMX56`!M=*qdIeY[2ZA[)<!};b*<PVokimwpaIGq2=d7`43~SE&EQtW+}d!@t7Ewms|2i/HKh/|a,wGHMG2{!Jsy1Ii4q+%j,&*2J^Qr]..2<_OeU`/FC$">Js[(DILJs,*94+&;~qw/D/pk##kxP&0fSl?Wj*`cXs0p^NkL4m1KPB&N6JK0f<*86jpt^N5in/me=N{1>@zYDP^kUO1HL!E[^3<:h{goU0OUKan]Qre:MmEjAlo/"+D3fqgUY;4xQBvnUBtfN.kje6ib^m;=yj`V0|#sOc:CF$wWnYSUc#yw8?},NpH)n,b2XKEnD7+WV*E+bxJ(cg*^V5GY$7@?kO6s/`Ipy9?Y>cjP&Q7n1.~%T%z;Ao),?U~,S3Zf6g<vfEaZkiIJ,MeVre[}c,k`0NA[b9)Cp3p}Zdpf7h9_@Rfop0V:.L}G0;@+{_2N}EG%Z_gS{16b&gD*/8_&9|(BVtXPv0*eBPWfBPPKa,bpF8YkcJkf)ryjteL70f9Nh@Wx|p(|o)I[?V75LS$Nz_hl/{WVJrTV_ymke*VEe8!8Tw6sI`7xYx.CLw.CYZv$b70%s,PDWzZd2A(HRZ.=vZ<Ge$wa[p8vq4b%)m(Tbl@dlO[9{nC`b~siI}?bItHYsXR[nb~o4F|*f8FA|{cTgAt%YB>s+rB+J_w,#jiJfyl9IKM?K1#Yk++T#nD:8r>r6nx^3LeCW/^Rz;REqR[nGxoZLb8ME33K~Ma,^[g{{G;CcJYQSFMI{z,1szMu4wlL:}EG{),l:s^!y"vT#4nMd%.UH(vcHm.>Zcx/PS{;%x/To5Z;gzKfkZ2%fL>Tr]NcM&`qY40L7"A_7!w0nfPNfDsa@to5rF_JCKwg98[d#E))U|hCt50wD?k&d|[f:VH<;@)TD),q"PK@fR%rMQ#/F[],|)w>$Z"t00XD]1}h!L#4gv]2DnUO1/8#}`cEe,wp65opIbz.axXBj9zp"`.kDi<aNMN9LuHyqEQS`]AQ.h8JEnMZNgC<j&nY@OJ,p?VfZMpM+_;OnR:C1gNVf]6T{C6@}(OlpAAmCVknFYx#MwxjNb2,hI~y2A_Jz!Qzzt|y!A_J/3b=Gv$791*v?,$IbK,!M/BDTyI~@CLN#nPr">?ft,ep>/J*~z?"^Mlp.>Ei^^n]@c^UHPjSaq|UX$Pf#5D(v.GFbpYJuv&^^_nO#h?X|`q|fj{%~>h7t9=oWd7eqXV,e]Llxj+aL0+XphV#vLJoaJl0Y5W]MGh[@bd02(b"eV]EKSs8@Vbdf5od0rdFw#*i[l8Vo)=D|)<+@77a4&gF}Osvw/oe|X]/^/zfuMA3[m)v}x%`+q|nvX#Mdy{7Xu)y~$_VdSYQMbkJU@q)5,F=xQ9xEbX9)*]R)J]W`^_6RFE5ynWGLqJF|Rfo.jd9vX{XV9iYRSJ4J2PLH8.|M:yeBV2{lu1m}dcd$iW1~$7;+f8]5!k?LN=GM&Hq,QN/%o#{s4ZM7^/cb?sLV>n7{>K~t7MbhV2{$~a;;D>*h.U|VZcURS5viWJX*/Hy&]}o]?[363hc@,,*G`3N`u%Lix}xdBuR{YTl@Cz@@bcHZ7;y^faA4NAhT8Xp(:xyuQtP(.Z5ZX=?(eTG9P^k$cLYdO$uS<%jZMACG%,><^;/Q]JIL.n>MX9:Zh=yyN]!l,)S$vn2}x26TRch?u7)6[aSYU>N6%mb}*wC3}nf>!.N%Bz_Y$hkC@$>D[|:LRPndc<q*765.)m,UpmvFvd]aERL$NxUY0vH6J0fOLr<BQ#p5~PdaVE}zq|z@b}n556`@/IXPX&Uuy^k?(Y&&(w6WL[xiG@afPHK6@*Or2B~$D+F*.[~Edo`f(Z.Gx&215tMMbf8@mcakT8vS1C~MY!52XJ(E]3Zy7~5m7y6yS"E"|=_^+w/q#PY^M(BO&1$8/gL}CuUUivYfK?]N)gKm9</)_svA~!7[J{o/>t5}l!A_J</bbOW?~U,"/Nl#AgrMHjSxLlSaq/{A@9X+00l4|{t15K&m(d,HMZ<J:U)/{M.[M8zVu0soHy4s3ehhnKY|[1Wu&0w$MxL9:t_1^~Y1o{Tz/PLsT>B{?l5Ela,>d&09!DwmKw0/6,p>5~gYO}oq+"`iQ#e)gv9M#d,=.eVvlg*DzVLi1sm!YAqJ04dPbk1>i#nDLzU)3iu1m_$USw2}u=xqlF69#[z).X%Tlto5EsLP%)}k,J:&0c~p6XYGc"JR_S%6Gp0o$@m8aCW`n}vDnULK&Rs(jjVj`>7i.*jwSG%n7hbA|PAM[?f3X4i7nl4yuuVU&mO|CGrtd9)m3&@sTf?3&dO$(,/j[A_D=`F*.jaBTq>_3zWouxL26D>ogO9)X~r}kOQ,0sqnd[bJ2P..$Mq/Yup?ke]Jw+x;CIBxyAb})w[I0.0u%3;//7/1d9^RWGMYs:kej;Z]SrHE|(>/:UDU^G%jftmNHOfNv8uFS?7k/$T(REnBqKog)fi5Laqsu>ysJSS:G@]SQ/$$]zd$z4:(<@/)fM~quG(UZ8lg#duKD?EdY*LU@{jH$1BE{mlK|QPM}.)5UXJxTa3]kx`LZ@f6L"P2;xvn,Hyo?oR)%Q61;J$4IwXX?Ta]@s:qHxpll+AgAAAvVBAYc6"|LJYSw3Kzg"$!AKAMR+fWwFY*cnL(5:y.LwAAAAAAAuWXL5Z.zTSwmjYkD=w31MF*Ibam1)!?$_EZ@@MpuUViSG$wJ=pcRKc=%#(x*41&[/>@?=@e98yr@B]DkP>E|ups>]ZqOJ%(3L4k]rD*whacQlnXX&y~UeB0YkeUtqB+xt]<+|ZR5)%<+FLuR&1wUj@~PJ8Mt.L)6#W;,+WCi;xo:Mqvt#F,<=(|0~*?|&H/dn+W=KaMMU8.zi49`P#/I`ul9/~f.^LhO]AHVvuC9/r57zJ[1N_.NZ>|URBI5K+;e^zz(*BC6]V|}=]^t6c;Kk|KD8j~uH;*K[8MOXoD<CaEIh%q7L],UnD$!w;v0X,ojgTlQ^#HET85U[_91j18sem0qAK[>ZyJwk])MJc=FO2~~T~n]}+H_,!TJx=Wdb/4zge`>cYI$<#jvOF{*~CT6J,7$=q[Pww*oA?q,JM5iEdhepXEtc8{bt~^D(4r9>C]B)e2/!/ul|5;uYa=2,*_T!qHvZ=8/]ieMy(~t,ONOh[9T7gTcRp"kobM5JG]pjQ|#,^r`$;@9)X*Bx*!^Kmg>34zoP_TX$%+U=XFx8>VWIn87h"2T>w>oAMem8KuGEunZjrr/m@4[;XgSZk=:`NM28M(WQu&%W!dW+mcd<8:Lkc4j/1J~KtzHjbF*={_dx1Rg5F"jMc&chJOMSKyH4/xW*/R[$yW|YDre>3&Ts:F;M5~i6/,o18CCP6UNrz~6Fz{F&nVHIz>r(?4NSTC2k2/u3Bn4^sT[tjT{fiYFsl(5Fd6G{tI6THCtXIRIjV;ZC>zi"K^t1O}JOYpUE.5I!%v8S_NQ<tEJ4KIjo3T/eheG9<9!NlIHx#}JNZ.D_z}S=Mdf{HJl.TJxz/:PqcEQ&VibBMGWSu&(]vOa`4sY@2=A4$jv6=`%aZddJ4ZNTK(U~j*2G;0"cX"%zlhn]x>h,.eaUsF!Op+Ik[JuH.>`j$vejD{`+CCK6zuI`c2A`s]r<b^5S+j*k!F;}EL3~4mzX+<|iyg]ETT/c!EVRr|DE,tYN5Q)Q2IA8Z4#yU|<~gsACjYutL5t1LB>Yy*/)g]*f{d1ESf#*p$li?oDHDhZmb|L2dzTFwCI:}K8w/>oa&;maFM,REmoGsK:qzUNJmqd~/7~P9Ic/yTQ&n1;mP{gL}&l|b3}+t16RKuy2fS]IkB2I&<^x)%I%4OetZ6A+k&K1m8`Wvl6;`GL^5roU7/zlP8FYUvDH|mfW[/z!eEG[ov`P|YLQx0=vkR&fa;A]w@IHEUgQsaR"2*T5;*%.=Oi~Wvn}klZ#]n2qnk!eOr`H,4yHnybd"J(Y@j1=oMx;z7vsy8EoU.X}$h?GeL,WU5I~DqYVKi<ssExt<^BF=:j7drS`4O@`Y5NBAT[do#1f<d>r5K?ui.y]ZXnc%=2r=bkiE:$Mu]upNImi?F=l<.jXh!V*gq+c_pOM?GNetjkrsfIU1+|$h1SB0g~{9<zb`G<)*O34o(fKlEOJ5Vwe${<*4HcQ|Ht7<ei"@|q|6zVU~a2O_O2TFffJfUW8]ut%fRAn6$L/l~[]&oQ%X?RO>`fa%Lr!25_obBR,|6S.#r^EOu~W7C+wO<6qdyh:q+Pl!E+]UWB<GJHh[7FPY"}:DJQs_FeA4_L#j%r7GzH!y^vtsaC(b8nJa>S[CQI<aNqxZ/w>_SwEsz0@Wp<UqG*6ikM,PYP6HhLRblUgA!.]L/hw}k=tvGE%;+TpysM1SzggLfDH`#|FbC9*?&;2n*RFPeil2Go^P~c,c<vjSgR:4XOqlb_F*&^"PPRh6)%:#9VY;:FCkgz"9[:B,=Y~1cy+p7n2xUuVU#stU%UZ"")eC$w:S[%;QN8KK5"+I]xD~tB%LYmeeNOMMSQ[&H`i)p&OenTNozPMm3YWmuGYv^!d0vX=O9x~#~Eu!4ORx9)O@t#^oly1.E8w={:Tz7QT(Ern,2X.Y{E$JUdu%OvS6+v1TvlAFuc:`g$hl]!Qb1oWyuF9:MgWn]eyl>ZKFR^iH/;v(VsuF"o{Q",o{7DC0I}o$XEA$LND>%L$z2gAicE!y^ZoMNUGF@[l+23tx4Pp#=f:A_<puely=83:@L2_#~.vop5dll{+|m?lz<6B@h{iCUz(DIi[xZ:re1IMytf*Y*gMnt${V,TDN:~1hz3C)/HF,?,wk@a1)q]8:%Wd.){6i$F|i?zFipEAnJe:E7B^HJLC)C+0W8,(_zCd{ZRd3"Kp#kDQ;4>Sc5*%aOd>Rs!G|$o>NX.8s"Q#w|SYP(,o6Z/isB(&MmGp*YF}NRYYLRb=qTqG<ZCO=;YbG1E(FZQRN>]k<HF%6#@EgtIU7iw%CZr,$NV}4p`@t{BH_S8(b|t9?#![}!BDU~6h;D8dh~>*%5N<U]AQru#7y!)6*l,?aapGVGg}.;fR,%fV)<CW5L<r7JDsc;*&wdN/*xDo%}T[=VOp^/%j[MVJ3!)V@G%+O&!gvw{Swl*Aa+MJ*zLmJ|sN2R3!F1Dj#E&nR5s[)N}5<dh6XO+RH<3b;Gpwul"<1lQ2e|ThJwmq_Z+hy.tc]{A{CMB{Etp.i{wDDNj4fD:m;*uFcu3L*ua3+)hw5rfS{/4KkZ0tl[FoOa2ahCFvRMoPo>{*+ZY7MFYh(}W61a0IWr|1d?E?xLXd|4+RL}~g2Z3^4_k>/00,7e/))/acsP%AdI<b$AEU8T7{4W#`;kyYti36~tH%AcZ(uSXOL=Z2)m^;!Q(HxM|aoEFvre#S#]KTDi}wmnj{_FiQ{xbPnJl^Q{}~;$g_|]JOg[m2eu1i|.p3IX[q=|fr{baFRz9];7ZKFvmmli43MiUw7K?6jxVRJ(JlsV.Y!J5X[`|(^+DS.R"1[AgJd.ypl~.f^~x<f0ZE2:7GGbTc5Th`aewZxkps),Gqp?87`_SI,"A<3,V*2*MIY.KbDYA<W%Oaia.Ed7U"B8umvjnaBfYD|b&YZE;0h1hMszj._NE1>b5R50G!!u<tKhd*.e{VErHSwVrV3D~<E2Z{5`^fa%mm.FB34a@K:aH`@R9aD%~7~.G$}uHC`Ndb|AL!t9},CCrr}wSQbUCA<hw=BaCR3b?JE}jR?N}lCTff>OOxVR=u{k*6G[>![dG)uh)bx<>P*@MX/w!xR>h[QjCjJBJuVfT3FndoG#UPuUeyHC]O1CD}HUr{{ZJ;1;+HCD@Coxo}.XX(>UY0~L>VBT1Fm3gyz=EjzF]M%<.Y@EPIITC"Z3ae5dkS?zep7[0:k)Yv9rE[mJ&,fh5l7iUNz:&,W+gC8P~!Ns9w2v{%:c`+@hNA{YYnWxnM,3Q9x]]7B]8!"hU8vAj.)4~#MOSv*Vr(oo}UBTUaevueS}cV)P.s!Jba?EiIc;Fx6N7Ny=Twvu1IO(Y?Zbg`CaF[>;^4{=m3{abLyL2wIdF|PX<#<IUIh";FBo9NP7[a32_VcXsMi1dWdB{s@1*/HQ!B4wCZ/=,>1>oX@wFzSu?+?3`1E1(3#KkMh?B;#/Aa,E@/~[S1Y)VU5/+:4]4zB|hwL(4Hzk^y,d@Hs}z_fQ~}PxyPn?U(dY}&,8&EvV(2?0;&WX(Zg}&VRl!HrMFU(s59/OB.k|o#0*)T0T<PIho.@L7R6^;cs~7"dpzJvbaT)"p,t>[6kFD_q.yY$Hr`.xv!sR=E&Xc}Feg+>(DWxVB32E4tx7"TE,%Tl;]NMa4a4Wd/$f=24.s0GE)~4`E@n>j[Y)W|=kql:,xx8Qa2m/QLvxy"4sp89`?yLTlxV(})sisb_L$2O#HMhfQRUF(04dfjv%?1X>QV`h=|`1%*>Rv2+K{iT7Wc?YR[JPtF5b}GXi>^?7r+e_4F5]sV)G#oR+Fw<}vXzI2/<:|MG*xt~d<T&PC8fUf?i02.`_/~b&d.3Ci_i!$dO<{mP>1%Y:cp_^$|LDWX+)agS>QJ_fe~~Lrj/Qo:v,J~YP{DHM([fAIrVJ/~qK]J*`y6}1}b1O.QW&sZf7*#SnDc3P_Gl~1Ov:In0a36P:=Az`@s`gq`(Sa"/i6w>%g1w9k`Jx&l5rpYCI<0dOAb?`.}ITS5`qGltS7bpf9p6gK]ODsW6#D*lmj*S?4v.ncr#|SI[zXK$E:6/#XPXMwdV55hFT<[7b;*Us3N)S5$dm._cWL#i0Qfb[8FteLeyni^0gGI/&K{a6fL>]Yn_Bdiw#1Y@laXP>L.$|)o}mnz|Uj<EVEjW1#px;Y+1N_"FAX3@A=`]cBK?oZpCM^#o1D@aQ+!cR$1I(@KdO/MepTNv*4T!]tzD]56B?*)hB*hlZrBSN#6m,&6(6=}707Bn4^gfO|Ej;>"o:~*6n~5`~7!d)989t_TR(^VVp=f,mKHT:@|8+l+Rc5e4AbyY&=[|J0`R[^21Qn<)A;GaoS8jq2}DSbEUWQ<g5VfI~fk?d(7TS^l.&6F*pa87HV?_(>2DTd^b_PxbBJy?#5T0JDG9(W0q~k&RZ34Ex8+mK=E12xt_mU~j[vJlT[=<S1WSI5N:_4i{YE5p,t#c.SV^!?s#r#*$1Q63pJXpu~W9~d(IjHMN|/:QAkTv;:otd`gZ,K!+mD33R9o4#$T~NWN<Do>tDocnLxIjG#1p]>nnxex[Z5Ke|nfaJ1|I_dIgj0#B@;Z::pLa#fYlM2@D2]RSa}w7K:"1}4vr>MobhkW:V*BE]W}B<$lrqN3LWW`_tRx9@0C)Qt+yO;5hIq`a)K@ia,n;hZZY<NqaBVw=xNU6&xN{pdoJ(?KO_q7NC)qD*rh1b#c?HL[l^pb9UZKwu}hN=5x<&D=YhRqudO3[":c{yB+qhX)!CN3pIHn6yJ"G(Gv"?p}7QPL6U*MuTB=,ZbD{DXVvbl&E)~%M}P}umYiEnJ&;H6C@o{ycIBlPuU"XeqxFM5UVvHsXr2|G&hpxr>PrDRls|6sf;PwSk!yVLOPvHdI&/u#Z`_bNv{5xPS`i*"P)bRJXqnXH!c]sK,~e>s.JCWQJ%<Y)GYO3!i"_ExXCGy$7jP*P[]OgIq/zx_ifk=L!).+978UW6k~!aD`N_.$,kZ|]guGfR?Kr]D#<yYssiNkRb~"#2PmyTLE*^lkpg!!:;O9cq!n=$vfgBMwuU3z{FXE+xE+EHQT7%8*>>#rFQWolQ@XAziUS{m@Sy]&MUi^XxIP3NC.x`n37rnYL/Z6SU614iADd^`>JE!h9IuyMmH6FM%?H!Z?&bt}tC14KBcsIew,2aj]&JiA#C]56my.+q;*5.0EZt(.>)[MU.IHj[:`hc&[5HG1N7:PQWb9DwtoNwvbwwt}3hx+o!$l0]"qRE^jG~jMSxwS01r";3Ym3%w3K7?t&&%5JvvtBxr&gP==Wet;yJ7L=.DLQOj,US1fuRG7H/5JM<nV_uSraPsW3*%?E|m]YMyq([]Q$RZHk.#&NpSW/,><Ti,/,Nitd_ymETXZ{DSs]bCy)nsf=cW%zIo)z_>^FO^kBeNsXk)8i3bSBJ[c>hZj6F`)TAEfp"s"UEHd:x%5Zhz|bg%o0:U54/HS?@K[RB`ax)mnciXyE3BS&.Ohyx6701GH>#C9>P{?a5Vr7H9[H]%1W}!q=x0HN]J<)s<0v?_CDX5)p0yzJMZ,@j`lpP:i{3/exzvpdm:v2F=*`j4^AG/ncP,L|a?Jm5Y*{XJo(|T5}3&5VoR("I(4t+9H^Q4Wwk/u8JpbzS<Twv{3b=.P)gjLa5nP;wY3LQ}Rg!TXY:uIWo8aZ)phGpIH:3uf^~!kN(}3V~+`I:dg=UX2}n!}>u$5G!63vZT{iq*=5uD~<b>@M7DNN}2vETkqKK"]$14%S&d3DY;c83c7]KzRN,o}S$sT{{b}|KWNcP7;QDM]oZ+/,B36P9t{j49L_wjFA;&<bMFR:0/$c.$mgaF=9>>5CD2v:{=05l`mjnnjUnHLI<I>rvX+(V)wYk)4@!$@Dlq"=2)6nMy|tUsNuS,g3/7.bg0Ybdf4$%gnN+]A4Ffo#w$r?.#<rM0$3g;i)kFa?)qlv$62;bk.P+X0<3$i%^nLaQuLT:hWVsjI:3joY6LzDS/</M77JV=/+]rc38?mm&My2dMFd)mJ6Q8,Q;[@;{0d,,m:q65nY&4!T045FU>k:H1W/{]6H@Xx.WuQWYpe`nJfy(caW!a(d.PH+ypJ997ToN@TGH:K#?}xOxb46l0PyK0p{Vl0Up|P4m3{Xouc$@A*3%X}L!G}>mZ9p&_r.2OOs?B&]il,s2<c%:LtWZ,X?,^]6JOZ32kT.k|LeoP3WNMJfwtneEb5"4,>]$oagN%"Rm,)e!N.OL|AwBvH!b[Alg^$7>c@w?%2e7QrwVvOzzBBJU_aBV.5zb?Hu^y9/p]S;}@y8Sl*?$wpY3j_W1SI635Y^X0gM&d%G#qFh1t?cfI/pI)KTE>6pb[c+K?2Z8TaZ1C&s//5WuUiC8@9Ip3%@iCV6ZQyr!<1v"tE&EHxyEwjE;9fYqR#EH|ogPgBTR}Znng*}wQPHvw`$RH"ThiDMVqou3~D^/%S,Ud2kWqoRDPMv]X3+RTAakC5VN7Gi5?U;E9`:!Pd#nr{)3U^>$hkCpqti,<KoxXD+X|zreHaIx+0y^8%[HYS1(wLhgjnK_q0K1?m_altLr)/sRXLbEBes<uKc0yYHPyJ6~#9WO_|qq|Ls5v|p+CpJ4f_aiUO_eDuuZZf!)Kv^&^6*#QFEvO`VXLpL(T&]XKJ1X((b_c?x*Wt[i#r%uPJQ:Oc$AZDS>AuTyV(UXG9;$ap>EjktwScP_>L$naI2%MCWPwG`BN#B|$(*RDxb?w[13$.%%lr&;8{cpM8qGFcgQewD}>#/gcb0u`&jlJRd0x&uCYmg2!K2k80c"X~Us)eR#b/M+p*{Z<o`SS}8;Fce6t^r:MXA8fzFqhOH9gU*dPz!$KA2BNUo),A#8BEq%{f|`d<[|A*p~=OYWp@VOHyi`Izf83zdo=hM`2sXr)/Z"DmLaR2@YJ,P.c)B.dkkHVu5kz*Jr^l02`RpRH>drcUUrN1($)`Hr]lPDF;3nz7/G?"+SL&B7Frc%?,{cPVv2Ku(H<Ez.Q>2)v(#<t0%&VtFz0[uNdZIl=mm,YE1bl4iKix3X4K&]Y`W$g>{kEM4aK6K7aa$g9@B@SQ(^7WsV3Y$!pB8qP9xgrI#>Ndj?skEYT;jM};0<<]PI0*PNZ(5YGA__yniE}H^LrJ3t<T[s(7%mZ`<]zS/"X0nD>I=1$.ZXXDTbrKIN}2Y52o4Mi#MSf.*$Yuh*0OHVCHv`%@B5E[t&Dl_K4u8!p;_~*;_o+haiFxX>G$57eq?Krv1JS"Yvf@(T9qD"r0qw!$k*F|CEqd57MfNn|j~ffK3#]ifVCf7P?nnw?kB2O5D"yjP[L|j0/aj[4u*]9$BX*&.xQh>w9T.RI:m}bAH{vY&VP};3$ow;;<[?&(p.IZz4"hF@vy>*_(!|bqj2~D#y3WFUI~)"t;a_0iMt&@eA8~^#WCF%TEkXQ(_DoGj_d~DS$,iR!q$sM2~.[(${Qe)Um84?A<lQH>;iFT_9:X*T1JzeUR&GQ=2PG+K757/8K0[<$m4M34dn!oR3jh85#uk)y9p{zpL>p>:&%fUV3fzc9KEeTV3{t+m!_6XVekaAQEZO;gy^/.W7>fZo1i*,>LiTc#S|(8#rJOHas^C/{9>N3Gt:@V9v,J7pP4{7wd@]BtQkct(gv!Rd}M(M;Kd#Qm]M}AF/hsm#:L`"bFwy_F"Ag]iHi7`wdm1]`m6stk!M!,d<?1FTITZmUgW){#zz3Hh[W6S(?.E|^Zj;^8_yVME4Q$&Cl(e=17Dx&gm]oBfIc]q:wOgz4vqBHsXa?<*A4ilSuKlNKia!1^|%i^kl$8;9:{~!];xJrG%@:@u9tL/6Gh)]5V>fK<<_/p1x0o<1my6mGfgbEoWR]0nE31|s_fNUCc2Ibl%DN#|/0hM|WiO+=,hKcl|]j(NXb<Un);FL0Az#vT%R4TytuFJX0i34qZdl`)]e3}yH~=WjXpqCrsQJny$[`(NNAr%1iBEI~EM}2UBh}]K6Sh9^ZpWme52AAXL6*VK%nVNX,"(*tfd:+pERtf}h9?V:h2`%g`fqn/;c{{BDOTj{ONZHFl~.G9[t,69wj(*f9Y@RH/5*No$j#.%/uK:$_*+u`6m$oT6eFD?yAIE,DfQgUb7W&86|AGP$nN0..vEwhPg,xO8FS"g`o?"wo3,M%(=T~M>m9pV{]4pR+G3PB_]CJb|^>d[HDDCXDbq3Lj<&^|(lM1TO]dZd6:m/,0yjx>;z7C&o4iNnkyE#OkT_EQHz{DCXgKAR}w8P<Bn>LFhgK>+_`m(8uQ^{vK,K0g<q{@tV4T&d&N7r(=}I0tS<:z`I;a|qL%=iW1UH:wGXOV]~N_V(Yw_Z>aFs&R;DyTCaSaA$}:~^DK_#^]o_(;H;FNcp{0y|OJ_H6Yo<*F)n`08^v;>%@P,qxhuCri.5~+sW`,*HQb85*rp$8eBFZ"$`:Qe7*t=d,eW]9.c?mIVuWE>eArGt>:**vq7S|*`wYC4aL_%s?3Vun|ge/nGofQ*p78by|%:uotX>8=jW%Aa)awVef7!L:_L(=JH=8*h_i$mUp:I$(.M9JYbZY+TXD^j8|=65n_;=ee0`35,3@{Bv49`?uVIz*ZCpwzgWOkTSSG0jO~}XYZ^6og[xS^jN"%dGd~SQoGw!ndGyM;DAtI{).zn)q7gNDU*.@w`SfJ*!Of*/1&0oO5.o4`+eR(B}:uN`h@bC&8i9n9rIj}_I2JXu!?hH^t{j;SB>L&V1Y6+OG8eC#2Prm]Q(vH7/~r{I$t`BPCr]zDmQ`{yC:Z`8o=B}=b+9v_qO#Hz`U`(Lo};.KB27N@opv&4Yz]seOg&=qs7U;y4}|[s`?=$7@*dxIuD!EzFwRNmz7mOtf,(M}dMZaJ~]uLj|t5%_a&|$wZIIsUwt8WtOsaRMt+@!w5}D,&wdLcgCiL/vAmP?[H1)iCv*(fK_Q>?AwnlpS`"(AM&[G=c^3vPU)]ikSR^:7zDN)mz&GJ)MG%T?E,l%x!PRA:DHITp(BHK:%0vR>gdrvmj@O5?W8XU.nw3U_d[(33PkyX/xEEw7m?/{rTR1LJhUS/l1R4`hA(!&H8&"j32B,RO]vPN^{+@z7/L;u27bG{q}frenb|Ql:_%t%QD_,+S{$~rF7?bxb5+j4sJSLmZIS;)0Bvji~mg`p?j1pS@`[P.dXZ5@[u%VKAK;{4^rQjp</!<azt6@Z.6YLi~Yt~[~mM%d51<(tS&gsykI,ci0E"$sy|>0pcSifd!;Y50"]oc1>y2VIK4$U%)q_k^~kcEr7xz5=U0yDaw!i{hI%.!6VAL4sSNk}](rJ7_x_7=<>YDxLVjWNRXwB:Z9tLw|Jo#gEJL6+$3iV`I,Dd^sOVH#3$@|*CFJI;J=LBDFIOhZs+D0tA$_6i~L:|*#aQh?IIs]n<q{!&#HQN2"Ed~D<735/BC,tAT%caL6mqum?wz:`zs:F@igj~|m2+W,SIWyOXT=C|J5zZgQCPDWh)U9.e9,#5E4:8+cHu9TE=p)`$<1u*F%ZvH8cfgQ|@W2W,GU]#F9!Mu>E;mUks&iRG/IqV69}K%42q0ciOM)8Tq_Xk{B.bm}KyPRR[v}cZRbJD_d/+e1mEytQ%SS{o!tRj`UsbEC"r;Qc7#pDQD{1:hm%XEvOa"e@"9O]=WdN_+H%tfr]?1sX1XoW<9]S/P4WMJVe?d4iy7<%pT!GYaD`3uQ<%djRfP*2.^d$S6HZ8Z~:v4cVl@W.K*F{~o){M?2%9r!}/3c/F4{|En)c)2IO`i8;yd<nn!txwMKP4Ev?8p^&/lJ6lFB~kXPcCM*zWo(/W0O.Nw:!5dWjJJls[j{}uqv(s;47iDL<nH]4(rjg)5gZM"`@=Oa2)[XunN~&abkKI,Y5^Yy({q.mZ1DmhGx131pe`K,%J&hTK$SDk;NiWdnv/;BLNXeE^KkiZOp&@*<W2Gp%g2subNqE*P~"1<QEZCe7~w,UuGA^xv/s[jlo|d/$j+cY`O.WWbPK%wA%Vz@+e_B*Gbx@x!uJzqE@(pl<CGzcyirMB8P[UeXJ[7^OfhwSdrxOg}*o5><f!$J[!+gcyMF|;:Sux*&)im`,Y+$s;D%Xc0v:XB)fZK2.2vzCJ3L$;odf}e*Ib`&=%C5bK1i3%l2Egn]Zd4qf8^c7gr#z^p|Pea#bz"+h;i=xF1j1OC,33b`lGM80)M3VEp_".$JpdQ^*/.|5ktIe*b$/X7n{Vsc,B^t;#CD#AJxgC5:]rVv{G3TcUjVa6uq)*FPg%X?x+97PSx@00;)hH$qE({Xn+1mc5*JtP3B7(mVxb#$2{GSO&9J%wu@&cwyL9U2I>*VV~<:s7!k|f[1[&$nBbs8Jms]tGl<agt1,rz](U2#wox$+Mize?ng6=K5K=[Ey}=8_9x$4,jD?~E{c0*=||+)E+O7FR"NyD?vS9G{a(,pjv=TsZ>y{.[WT4zTh3.$66LRe_O#6U`fY6=z~~~`iFynN%A9:tvy7?66R2TgXYK0DD?7XE4ub*YaKL~$BUaG+oI,6l.G)N8t/]z[*WL4gh#,nrP]8GM]c>/z^><ejpysVDOZrUHuI"}Em3zt>!KXOqX,C0?)3qG@KUR9]QgX4EkLB0{[Bn<Ip0W/l=}FFbtPe<R<TaScJFZF?yVkz&zyBUcuYdf6>*mZG?7L0uqpH0p$u^ux|/8>0jzD=;sUS)<ZU"@QBi^$&A41|fhWZ$O1)_K$v^d!Mmg(y~H?5?{aXw+7X*j.Wq!3}sBEf>={9N%=;1D~`g*Q)S+9y._/o`U}d|P;CU+^<uE.z{_f>!97Q;{h>@NfxA(nV:&$C73.NgroX{,FXn5];zt9KNtlbQgdj(ju/QNY^p<yWRlRJ*6VfA{"P:yo>aJJieLH:%2(!_^g2SzI.*Z?QtYt.|Nmct^Mt/_EpAj(#9zLJj(5XJTKetd21zMdnp@_Qx4rX!@pr/LGzW:_RcvnH.@E#PX][/<bV86$(g{Vq<Tq:f4R@%vjC[z*^?3_F0yney1cxm23(=LUJa>|P]4#]s*$SBg/F&(!/!zq>2V(<0Vyj[IEx!$S1kC#wk=~ey_,r^j*NP=#/LwX+0X0!j!K]6(+M$Q5$lp~,W~a_Cy.e/tZlEfRPC^doD)8/gpDm/&R^OsEW@5RKmNPO=<n^<DC:xL54bv.F@]qci?/XgpZ!qs`b?3I(pt!%oIa;?AEP00jbW&EB(a,4*@"SkCWsxT*1RNg#A@(X@vMsR<0x~(]"|Hoc!E1Zta7?6p{".VT{d?>fO_k6JnQVdsKiXaX3RsDRC&IQZ,vZ#5rPIS$65!*G^Y2)16TvI#;.<S$Y:jR9=7%<0lzim{89PIJli`_QU|.^Xtdt/CLMl7vs:W+$~q{UD#"Z@F/[v%I[PIo*M8R?z#Ji{RY/X3|gklY0Kvlc`yG8nO`9$GcG_f>SLuf^8o?WryaE?Xv"T2h$cn55Q2;y:3^f|&)jfvJ$&7xw*<hrFFg"X|#@$}#C(,U?/2+$.K2g7j{cX49)BQL^TpaWr:8YA4|x7RWynif5{xjWl>Z16R+[LPwHsID5s)uF2YH*T$3K+iPF#ejuOzB7aF<%L#3N$e!m`h2y*1_JrO#)$K?ChajS}/|qB+(~d9`aYsTZKh8V&(ve(+ib``MJ**b$42l*:JjmlZ*<[^/_`qEFZ29$;YVytBf;kd=W)s8U9S~XNV0M2=_2r9].peYi*HO{xbk%&R??/B1p5uk$/]:WL"j(^(@SMP(X`!rG!F{tXvU.(^!eR3>.=s;bs~n"]5J:5U%pK]T*Cu<g2c7PX[DgeDlD{r7DCRADEq~]|9+ovgHN`PP}ip!q|2jQOMoWo%v~PQHWC__m2YmhSejiv>@B^B(Da>So=HjBsL1O%T*}GLPU:3wpI$tcAp4"GAiY&`!`wWmSQn9bU{M1H_oS2UC{JgmjB.G~buu=ZcvnKf+y#diywcw/R)xN|J<y7paE}3tme*Q##i]lY!NznAUgKlfEf5BexOS4Jyf9Pp!qRk8ZGchZorbTM8}aJ+UwN*u7@I:iRUS?M.(Kgk<&eAi*VwOHcJv.ob2<4$|FA/TVylqd,JB}5qzDfvr6+Z?]a+LhFdT?1Xg@!wvk"]63SeE}f7q~T>8!>xOz!)eD41>qKwhJY}k*&C2lxEt<r%b#PiAQrC=wB~:^sc;I?lA!uT[v=)ZX,sH*z+>0v[srpa*XH)@!GlBV+WpIU|OP%=:P5a6@}2)cx%HD7a;=|2NvSFJod8t]I;Viy*.5.?|9`S&K!j#B[{$+p~96g_#:4g4Yu;9~ohXqs!ukai;Pw5,>{4N`E_stkO*R,4zfXkw&,T9J<mj5vMA(a;UoAR<sf^zk$jdXuya5^o*smN+hQX$E/([2V_}#P0@JEw>g>kIQSw6xPy*A9pzE@,DbH)fQHQZ/Rg3He/9ew_$a{sb[_A|?FkdzDR$ED9>Rbb[ORco+zhnu5I1c*BfFP9#:oK$OaH,j5dtNEX!}^y2u{>Z7u1a)O6`[NXk8)TeTc]uY`3.#rv0*k+sTRlT+Z*Bc;PyBtb_gAHnM9nw6sXpYm2bapEogDg^v}XH85gE/5G:&+2/R=hY"{21):_oo@Ggjd^;E2xYn0v}]XHiHY*Fkz?4>hz~+xN<KU5(gGzZLeUxamwv7&{54,S2L7>p7X1.MextC9hebWwvA*)sEE>CXeDu?y2!:DUQhE8OT@`QBzw3,Epgdd&?DN1|bqS~/d76=|77R2|k:uVN%w+yI}3ap9yYh#uE9(Y+Bf$;ZKAvjDvDrm062j^Nu6XRoP&|ER%0vA}dE4MXz;H2b@NR6yRekV5Z`$HAi@@q@1kqx6a`7!5vgXK)qBPfpF?MDQM%5MdAgT=s]5.Zi2(JRWvR!Kr2jT4g%8%|pE{8#KF8{a3|Nb$f=.gmzqKO=y_TCP6lh2G`caCaWZ2hyj;axj2ywepYI>t/LGiw&VXG,H$=12PpOPtt2Ud/U{vNQ@3%kbc)+y(qWA(W%8rY<|*(tg;!XSXB:]Qtj&gh~p7fY7:Kaxq^DWass@ZPKE]&EslhlvB|K^Z0Ko`h;f;_1HnC{c~w!K+zC<f{)vJX1h6PecJLt0x+{ZPqW0{Iwgpy%|n4:m#;XS8/v@:+<2X9d|Xo49Em%JB&(l!/mT8F#Qdte$?"N7s4@epd%bkDx|YaCKdKMfSHtSqp":E.KYF2uy)V%Tg&iQZAr&4f78s&uXxXrkttMLE@{v+eV#Cl"uXRmreL}!!8pLnN!nLO8+xe3~&>;`[z;L37i~)3:jQ}64{atLf[!Bs[6P,BIr)"oK+%!d<6i}CO[ILVZyi`{hbvk#*7sT].t%u{{DfgF0fv{1=Ua>)hfgUT[fJflIS&h{lIG*$4?yWG8Ob7<Jyx3s(gMRHSpf=~N:IjnpYTNR7s?,Zmx4*KW2yHmj!Z(Q}7#$S$($Sp@>O`.zV9caG~V5kZ$"BXbJ;wnT5A9MHDu51cXk8IcY;XQV<"*:r5+k?v[UWgbKiB`3q{<D.6DP>{+/Rl)ce1VfP<Q{{T~7Et.iSfc`P+wog.JJZe"x=7|DpS&;&ww.nfj!AsnF|goKU=C/=nj!maI$z^&2h$hJK;|h{K2`c$,NnnRSaT9?JnkLYAV[*Q8b8!uJY.bIIWiOtX2rvbyK_F*~,sd#5nh>?Cv?T=JQ~g<"s}IuH+T:=;uR{yNyt`gB0.AQxSkXrbZ&atwsty2b]UH`Z.@g!_y=O!*CP!5yEcYRyCmCh}RONt/xDXSI2J$^t#eUCL8<mi{+LTY7}V,Cvc&mv`AT5eR,D+gAKn;N/<_mc]ez;/mDF]o[`g$x`Fn#BG;V8NN/$*Pco}CyG[2]`VKd1=ULL[t/cP#XJ7QuL^~<mJWZA{eSL$l@%y4QJ?9c/Ba^@UZF9gBE7:cP6SKT}XNG/1KDoz[x{18YNx,5X)?}:d}bRmCeLS[b%bR[%+(0d89<4hBJA^SS}Q;/ufFljm9_60f)4Mvb&|4MfL<._&yH7@IQvL=,&_o>Ox89+VZMu!]eo]9!RtgMgIx;Kl~.C0jKa]z|xb}cm!o+rY,%pI.v1ZwwM]~8{]TM5Yh]!Avm#!6HkT,tKsG!%1]Cf9v>>Wc<,XEk(KME?Lh=VW!T7f5((G]CH^[rZv,>)dyRaVzlI6P0i[{Y)Ql(d[Mu7}1wJaZ"#_{i=hDDWU%vX|YN&sez)S`%4naZpF98.%]^Xz6~92z@Od1*Pl,2s6}AXcH?!+Gl2)Cgl6<ZZxb[tcz4Y5ngj<0@&S4#6V{K7!O4m!Fe#P#WcJtJ*;OPkYt5B1#?G]_?w1fTJy8OSEx#^Lc8TBdvTFiU&jN+q2VPv#QHi;#(y?kn,58p9nqqYXNzT]5SX:DhI;=K!eQ7:oL~gKO.~R%!~>08{JLCV.{vkbslq:hW:&IgfxQi.7"k~$tYz[ce7(j1D"fYYjc(2K;&D7rc+$d>q"azCt]>c)z5rOjyfS@Db*oa)VkPZDiN/k!t&NkM2f&I)3$sG1X&dT{$<~XF3m,+B2Vm*nErS0Xa_y&kmbwclazuza7RRQuM1Vj<J(I%Pz4_F:TPR$~sJqp/^{>ij7p5P/TP2+M4Q)EOved7m|s]CZSX|im|T_V~CmZ7ztu&9m9LhP67C;EVap;d:r0#,(AuJ(ci*P.OtPb(R#YDQJgXnv3C}e;2qLO:V:2ts4fr=C=PVl4Oj*G}VCl6:BMnQ~+$#eL0y}5~L/TMJo#t7k<N8HJ[,`HWO"+%cdX|ewgi_6&$L]&>!!R%`&%DZ8p)]eZ>2wDrn]K&aX88*TV}I>p[fSy}i*Uf}ZWl>Zw2QUES=_s#2p:+h!EA%$GYlp)>BmG!9dIcSlT$^~Z>%ES%J}=@A}GqK?LwcdAmayBP!:3@k2G9Ky_jOM;(?6%>u&#=!D~(Y_>Z7we;q?5hmOZJp6U/+F/MmY,4X~,hB}~<)z+t>;xV#LN_#QZgk.N)aVN^."=0pwlh|occSH*d2R8[&VykhAg<u3M"zkxv"]Ix%|%?3I.P~t@*fC3HCBe~F7Nu4wg[7a>+o<?K.W,K{*_]<6VY,K&!keC8c=*Om+P%S<o4e[pt7_fF@XE%3yFZM!K}K[Fw?;B@*J0bn?V7>Ov!O%9#BCZWe|T$i_|2>4L?(w/7gule.5FLLUUUV~8$p+$p#`>1y0u|Ce^0Z`<buHr9t98"*Ma0CZ}AoZAN:n_MF6$w&@)qo>zg).1Sx~(zw@K6e,1Za#)oO?hbv0_e)ADts8AdSgn<2QJ(+uXu]6`.L1xY*HV#M5!:,J=[jcjZCMt>Q<?0#=h?dlr:71Vic0c/KH)hFYZ~5c>q/=iz>sK1?FShx5z&iQFeetoKH/>?l[j}0xh/:Y,[7oQs&mJ4=i9;Wk5A3i4`GmNi^vu]RsoCignXOYdM)O0{QGP9xr$>;6~]<qCaYk7qpx;2i[`;DB(nG*p+jc!Ovi[(Y{`)qVjZfo4u1Nb?tvGo+CMS[1hYqB/Nsm|MxKSr^PTEkS5.y}OW$cb#Q;wk$%f>tQ&LO)EZqJo}:wZW4770sOM*gAruIBy<qI)KwWNH>f$OGC??%LCSZ1d2vK,CsH)UwbpU&VLc;Q+]s1e_pU3pgljDXYH54,%+<[Q1KF{TR<[Fv,qu<N%;h.&bY>:ngX?WhtIG9u!X*jGV)ZE4y"2|sv.KE7*#lUOQY(Zr(kKgS{b:Mh8&+D?P;K^=&sfWPuE"1%62QdGxJoE1y*?&7(32|.SukZDf$He@ym0{TFRl1y4X%](rXcJEZoidS7FC{%7`lfZu*Qd"nGd2=2L6)ba6yFFT||uRIjBK@s85k3&<D8wY@05ep{ImRT(5T)sU/f.Ezf*<g7qPrp?xJhw{"")q]44:.rr5~V+moL_`q=JKd5*hQH~hzYYRDCA26=Wd1o3Z{A|F`"BA_28Wf,m5lDw?.wY/htfA$z!S%5F8vM"2puK[oa1+QJ1r[@l`yp#yQ$TuQ}}GI19jZValPlBaK;?C1yYs(REc`T:~1@gG>yyo@RQZ0}({1fN6+`^Uwj"7Q%W[=qZX;Hj}HnYmqr3f,MiPh86~_Vg,P9Ov/,8U9e%19_:#+{ng*QL27wC%a>fKLxm?/Vu`iP}[RnWHUD!P?skHOUgiFJ81l>TtU{.vhLiwDV^i4z_qJ(?{_Nm4Ye:2uVUe6=g&+/oD.Om=;[|}p"$"01;;sVK:B$=.W~)w#Fq38B^2NO.2BFzZ%O$auL$orqReV.5]rodf)7NtL*h<!Y+6o(t*3otO|5qvTt3zW#)[Y$0f3tv@P=t6cNaEMtZLSTRi(|,]{2&Ve3aY^;@Jk1(oW:+JQakN!)@`w(_SF#|SlMi0ewliG6SI3`mJRg{zX+e((70#x|R3(zr6gG*.]Y.P^lj=5fhdhJ.z$|Gsr9SL2=!s+5BSWhY_Lnc:s$=Gn~_dXkS8Z6elWtWs]=HenJKp0j4$TO[8>~VN)kVR2~:.zQ9{uYcXY/mBIEC/$L8T$FJ9N.6T<@;alPnm594@)me?{7dZS60n&WghTtN~}9!uK4;E7mV2t9~(({H!5Acw4KVo>b#T}yiQU&eJN!=Ou^p!GvZ|S;yGuTm]A1:2fk(J$T|jcZ!Ivu|[E1e)wwS,LHOC!{D^l76(8XbgBi);x~H})0Y,EK!t*g/uT5C~.}5!X#mN(>Ks=]Ct]7g57v}aqh6Io#FjxreUoDb=z@=dkI[q%O%$YKELfg@e@{vFNQ#(CC&JLLD@eHi%G{wKOAl1(brn]8D)UBXN7xw7Zal8Jud(wgp:$`?XeX]]`^Kq=G(;(gB+}tT^QC8khP8h?DRgP,@$I*,;d[g8z]h$`So@+vhc:OQ6Vip)6p$2e5Jy04i*._h3wqeCm&Pftx[<`je0m60C5n<?QoE%6s(Jo<a/bm9C?GYekk_gFBLZ#D8w)r",jY?uly+Y9%x/TY|55"9i96l,F@:TCw2,8YEdD@KVefm,j>bsos5uBXw$M!kDb_*:$;Tm0+~CluAQ%HjG4{k4Io^5g4^Egn*ReN>QadxCj"pD2}sIpxNPT>j8W,4y,]s3X7X|2<{=D_4U,epr_&$~?mKRAvel#4R*HsQ1F?OQUTXlpomCAw~7[pm6CQB3R#M0s6:y>n<JH5g3hGiR+w?wgsNpw_p/H}GwC0Y(Ou~@hAM!IY3NzoP&VkbXLy&!oAk~@VOzSmuR<~>}V}u8:*0^%R_BG*tq|BLDnwi+T/b<P?QE^3^M>px3Kw>qdBq6oRLV<>H2bV7a/XM!AE~9lHe:{3^P8qanW.,Z)gAF4GG!OoDTcI}=h*S%p;2NJmP8I}lU5l!jl==}yq#=iJ4^[jD$+,ELw3][xPD0Q@Cvh|@NdyivW0,o*q`r)3AbS4WV:=/fpoQkJ(@d?p^UDx~lZ&TE9egJ7T<Q+d[b:2k?L;Tcz2#bGjz#)xqi~ZsTV/]s4ke~#"OQz/=y"iQ7=c!;<T$M:a!NLN$S4^=xO7NcuA<VZ@6p+~p77wIHY/#R!fm[jiktS!_FpF=f`{n7%,IkYv#F~%`Kjp5*{#lpEW:6<2Gts?@/?GZz?O>4#JvXKKlpgslbk>zRMHeLXdEJ*Pn<u12Bu1$^u!1Gdq$q%L[FZ!!qTF#DtQ5{>s2tcqwg}CTJJF~Mw20NJ8<C+"KFF=HxQg)LL.m5@=3s,D?EytSMMsn%(#zYB!d2?Q^N9A$GwG!BNbe2n`?8hP`@)u?wH)Pc(i@Fr/BEccldS"9o19~/E![82,6$"XKUqL{oW:sjyS{>9n+&O/m9|}TZVn9bq`[8[fi.QESeKe^0,Z[6?*oO!%a4g*,RDACY1e@^N#B&,A(!9{NTDi/%Du@GZ&C.()P/.caZ6r9i,^g*{)>avAMiUycDU;7mu,L1rxpo6e6*ZW~1$a:VR!}K#X=[egX?tBdOK=0w;h!(<0}P^]&Fuu[IX_Mhl#US5V;F<"jjopDdRfljU}ZaIr(Bu;>/6kPFUpOUm9cNa7M6z{he3)naz3s+%JKwEc{BE)m1~wh;eLslNPjlr+7jH`VF(;t.gz~t1y2qU"SywqLmSZxt@Ct@:we+w"v"[zKZEV4/6V;.}}V:QFIc8LhP`u#+@{Y1l2{4i55H7apiOV:[q_<L<^K>),%v=9HyUCi`~Bh4Dv>4%Z<R~tcQ]68a#lJ:#]$5~8egdr}>:c4:_>@BuS8~_<mhT?PLKA^JKJ(r9/h8ic|md*$!;BcetM"Sj+u95^y32nkiDrXj&_l`B~*DkN9F0SKCc"&OFUu~I6,iGPRf(khy^{|=5KQ*"bV2I@2DWjd[L7=tBCQ+oYMLMdtPe/Ua%h+"5V>`5Dj/bj.AW_6?%rX$z*]eGb2ocywd"mI;Xa+*k7X,W!C8l{Dc_pC?nTGoSX/;`1oEUDjZt^ZkULBj|Ub#{G^9S]3zdNSyZM2iN:%q=DH8f>b3j1m)SYTg=*0KbKest{e5q6~aqYC4"F<~D;X6(6*:Tx>d;jBgq:G1XJJB(n{<3dyY/?uYSz[(}%En[$X?q9fGBCI[$OG;M7vCF=TTBTRu,Y=v!E?+KJ0^ii||(!2QW6nFc?aewrL058G0=?F2[181<,^PA~$:l%3Z&9LR;ZQP[h.FJ5zay"2jd`>>xwd1n>GQR+$iNM={*`Efj^az:w=HFwDo21YR~Kx7y"a}Z))wo@6%4RVMf^g~&9R)q~lfYMpHX8|x20$mKqC:^sc.yehB.`y:*u.8ml8bslC+:4Bt%[uWnHztyn;%h>Ep_>pskX8qFNo!c|e^&jVMrXj%G!})jc@uOx;E7oK>2OEaSRBR=ln=?8yD4Jb:pVt%C%:@#p|:1w:G2KK2npuaBR`E=E6:%?.`x6=AcvGz.$fi?Z9ZC)&|3jh;`bQDIUieFi4A53<Q+E~=;"Pz<+<qvhV6kOZ<>]k[h$r~)s7r7rfbBOjLgXZI[xvN>H?[)>zdDn!Ufg$@XKQg2{Nxt}$2:*P_*GeFWtt.*f`3AdMpcE<)wDL5MteP9(>fWRX=,ZY~"hwER`oJQ?MebXB/]5!avwjxMk0}_4i~J)=+"r&^yKGMG5GRMA[%V[gVkZtTI4jc|TC0#0s`mGG<.[D^kp(ST@EuVTGgd{Rhwey}utmH(nW%IAmBDvN^81KwTJxra1/RGm_tMQBvv9TnSMIRBt4xQ6q1;fSs}9n;YG3/Sks1/KYTDz1*>_DK^*{gh5t1s}0iK$G)G_gmRfx12q2IQ^^2RN*fPr_=gd[to?DjOh/x(+v:3;^#kF*e845c1S`0%a[/8ALsxGPNySWbcz/Bs}WgznmdLX&gx&:58_2:>5A4&*Pj2NV2+UYrJWGy2.l=T@gDelc+:!>BIA1z$Zn|inD*853m,_CMa1)!$^8lr<7IV2gajH1>!#[>LQv3U~,@C05g}89j82I`e8`2D6Mx;&4Tq>&a)jLD>q2f;JG8:[SAMj6W]FDb"!deN7&?g<JAoR,^5Ij,Q&V%]KYnveQSS4s;`~X+d#qExX]9M`f9Ea0amwf[#0o`C=)i}3gETnyt1RVvzG=DPl!@r~QZW$m3QZM/+mK0~,:Od!6d|>`m%^J%)]bOB2D{>MMzI&X9}@3kgJB,y$b|Mfbn"dFUdqbL2_pHgHMd0sU1VYH8x7^EO|BKkHZVfGA)e.wYA#[TNy`Xm3G!E2GGqnnxeFQs+nd,#Y]?H/aWM:!77lY&fCda]N/HKAuofnu+d$w%5$g&xCc7lrHF"tRo{PRX{fha;@,h/Sj<mrnD>%QQa44)fUaj"`|t#TOba?Y^;"6iHSfQ=aO,3JlOjJNJC2Sn7cf`ZI)*4cl,)Gq*WaL|8K]ACrt(}HQb,A"%[a`9l2:`tD[~[8cEMQvx~M>eCtl$R)=OetVok8S7w6ip:8s)B9^9auKUY&UKtQZx`#xSr`0$6v?S#?10#^^Uf=u7xiO!c(FRg6TS@E.j+Yd]:xIg>JxYia4YZ(E;!S_[LZJz)qJi#f<_}TFA]?Gt?w,Li:V&2xB6Re40:1m#;kbXW^(NY_y(nyaU?8R`Ev<OeOBeo,O#d8}7K"[+@y!dIgNH@6ViNaPV/MESJXdE=I~GyCezN8ZYM~PNZtxO)+YTC{SjEpn3)l($aLe/!]wO.uSBH.#!N9Y1|>!;SVFkb"mwFf0WEf{6@3TEu,%HPI:0VkB^(C(Gw0/2qr9_D<|D%KxC/6G9Z*BE}le`:VF6,(/8NDG>+g71V#WxBU.q*PMJ^;WT(IO&lZZ4#`YrN$crOgS$sXk6eGo4YT;}o<Uk&i{@^nI0VB;MkB>!V_^iUNa&"]`"&l4osA>)uGyastOx(0z|>Chqm{o$}+7lHL/&>%r,LMabrSbqQ!ct(g3xuuv)pB%Sm`J7f#FH?EZ_u2Q,2^Uw~0v:pj_iwlPez(J(n]GB1/Tol:|}q7<;f8~Yqa^30Z6NJOdq)d~OlY%bYm;r>gvn$6:@!3).a[]T[0pfU:]oxwzeQn2>w7Zjq>RY*f~bp>VuUPK^1bdJ#p(;U*dpfXolx$C:f3zkRUGSh!47i_XTe[;+t=^%n3Zzv>a7GhE)yqSM_Wvq7AERU8bEHk|*0XmP8u|<u>Ik/MG0m4=>{H}k0Kj4Aan$N6[a9nS=v9d"DpX&i@uXJ9RwwVQrH2y0]1DaM4d8c+<CvQx:=L!)Llmc.IH}D65hJ;YPujR"PI/Mr9sAOSd=Lohu"YAYaCW/EIBVgWZJe5YJ>Dlpt_y_0Q013u;?@X&hqjDG@v3.Tgz/05{HdG?dLlioJ`E/:G?2cCEWB)`XL$0m~{N&uwW|ir{y]FE$Tc*8qYzTGc|>O^(4<&Vn;07*ZsBY}7~3y7Y%<Na(BFPe8ulud_=X?OW4t[a7fvSp}DS>|F{&GKjuBWFOjbFR.(w1SW;Zf?k[,"o_zs^)9qy!Z+%]Zj,TnkqGhH^O4XK[fFPRBSL~G8*CE0Mrd4M>.%.86dO1ezOUQV:)7fv]I^_!z,.SFa13wJb:mpqwB@$8;}&DlIrWGQWL?P6<(Tq)i__E#fICnw^}Dy[,s(DbW;%@cpuxcNstiKh:/Gh4m!MwfCm`M[gmaaDRO[X:JD!j,jki{h61Q1frMCO5=b2tN[fk^"(|H>TA_k``Qb8kGW66Ah^s[uJ<IP3i60ipQ`.`xeP*cbebG%Q<RtN3.51e7FV@lyDa!xgRl~ekAg"aIyFqeA7XXm.+,;z^`(UBX5L6X&]x"QsUoLQnMGvp#HUAf?PyCEh;NL(*Hr`T7LlYX:i`ZJ;XhVf7}{![@N5U9L*4_3^.HlbVFhc#yQ:F)4+P?%6<}(?/xFj,cr"`LcWwK[V>Q@Ubf/0WQbUpV2F@!@$kNVVN`PMflPfH$U]JMs99D6=B/ay(SQ7Om})>/?I@/UUR#t)&8$MXE07"C!.,xO:k{[m,^ZNY1+S4lu_A$kfI,jMll?)"@0$8Dq{helDeT?K|;|ewGTeiT$e$jdYKfUdl@vgg!l<)0h#+Ka>H/baUv.!~1{aCZ`q92A6;Y.k$aNyd$iwPp>l}qjc@DP7#vpzk`r39c4a0oa<P8X0?h_?!#W9etzZl,n>Yr~jXVD~aUO&Rj<"iBUptztLBWM)zcjX6QQ|?X@y`i"6SNeG+B=y6#^$Iz5A^I+)$SGpEFuLpUxx58!!4r%=wg.Dx$IxHYG$RGT{Jw8""JGW+OL(nR^@(aSiAcA_(f3@56D8&7xom9(#?vQh.3L}u3@7AvC%]6(yA_[[JC4Q^Mx263ore+Y!HfSsh6>HTf1L+ju_jc:?ZaD0d5dK<rlA^ivJ%Ua5f*&6RZ_S7nJRV]kqHNVU4qz|QT!p`W2$odIumkc<DmE,4)I}c:k=kiU_c^uLm2ABckU[@G(c=|*(%@yYo$T*z=2O,/^v?m~1dePMBCDE8seES8Wpwn~ROl=ofFhCEx+VVJ36tm{K6kAa`hFDG_#jbS*Vo}3cC3}&eEyP*Cpu9qZ?H#$gag6af5C|CdAQr#>RawyObM=koZcUtEu>6q<8hY|;[Rn!Ki$fbG:b"p@?UcsoJkru[gykNU/rj@b"Qc0]<2~$Qo9Tk{Wq%&SaRwqa!4/[,bA)uf=a:?ki4I,YA$A74mc?fltsyd}L6[oOnDK$y^]P?Y~?(i&uSjqZd,]9ZKEw#h8sw6DX6TO2;2A(N,}<Bb#6A$m,[4{V2Fz&5QTI)pAMjXr(%a*Q#<I/1b4Qo}oN(ew`h7m+iWY%tQ*R3jsy#Fr9v}F3trRW/[UPCOx;V(YQl:zY).R$.U*Vec*)[~zE9]YKf"Q4Orf}q&FPfU!]9Lr&EgUx&N_H!HJeB&63<[U]1[qiTY>Z_CmweeTT]go6)*l8#c*6FAcM]G3nM!+sea5yqH6N%Yiy>Kg%6_z[#A>_uM%)M[mB(tgx3ZY[=B8y[@$0J4lp3k,:@M0Zr4^LI;}|Om0_{Xye)Q5|1UBfpNcWMCfqH5RJe[o5(!m:o:mK!8Vd9VKnyg;i]Ul[AZVa@*>Bu[5]<rUZjmgQxsL|0O?E%iTAb,U/nDA@Nc|r0HpV&>{vh9!oj|{>o82SH)Q"*xMly];X)jzIcS&c0PSm:]d8y|6!,bS;wp,?($]g9P9}z*FnmOSIC)TKjS+in+&Vq;/Cx0C{L20j2eB(iw?2=ukU!I1w9d*m3Ni{@5*0;LtN$5c+~v2i"9ckZ6!={%r!/p4*"K13s|}%XOh/;B31rRs~T`T|.`SNT~1WsUY([n$opR>U7}%&N_.dD]=,`P|_XtCqQs}+Vv~txk3ZZ@Z{DzA!HO/0(mdW236C1l2]zXhs?@GJL9!!s]J3I,?<FlYivUw9ew"]B*}f!JdN?InzQ~ZOT7X`@k!EG_+C6OIO^D{C])6oNAM$oJAOMo]tCl?U>Lhu9NIf3#?$F]zh?dt)0)7g+@Lm;#!BZ7hEwu}K/]SVaKS8e2H!nm3qM4jL}!3X+E>pvDak}#a3:4)b(f6Pg}u/~<:`1ve#N,hiRJ:zK1F6HB]G_DOie/4yb#xjjH#OZe$k)9#Zy@RWtQBGA6u0P1W1ZD*9}(99sXW=p#YXy?rEM50<g_L1K4"qNmFEGw<)82%#`IwaxV3eCTyk7AggIn;YR$,DlxeDAYWsUo3GWE|DTKgcb$ZR``SJ)!V|)Tfr^|UEQ{k`HQ{1}5k1k4kd&!5%X!*Obv5A$++;gtIzuq)$!4Wxzz1I`SrTeYGBTae>@*sQe~l4d|qtGUJY`QE$mbh*5tYaPl?Hps1|n`v|=hkX>FG?<2]q>MAQ8:?,RoIbH;J1x=ZqX;OEjv*LxpKu6dBXx?4~vCd(,Ct{z0UsNpdHu(__&U^];lVIcfiXJNR9ca@TsiwkL#^mTiJUXw}*u(%uWlE|H$t}%(Pc<UH!7Rg=|HwdXo"&9[h[j|d4o%*k`(w]f&u{fq]yV4:eECKOf&35MEKNEf{g8ct3GoQcToq|Wq:=>7I^i$CYSm$=ve<G]G|)>/wtiw=t?pEw/[",LoK!^0GTKfy9ORWfeL6+wNBh~pIV$_2`5lu3gXfG?utr}W~@M~|nn;v/sL]=JIZ_8I}~);.Z+2~ZoU,MS%yJA&O*BC+1)SE;R?bSjqTX2s/k&NE?d4JY>;Kg[cH[R/%83[N0uqM4.dP8v+xiNr?#O&kjm1zwrpzc&cHh~@p2qL?aNEoD8LmS$@64#LhD79ZID@6)u~dZAk]TYGP&o!BTzT}MqIBiaDeq<`p2A%.Gmov[#t"V::HRWQ]ND0@6#|:;KhA/rR=T!daC]iw[1@1["qnqdf+DnG_r7,u&.0sOh:KR,I01+wr"jqb|H[B^{`Z,w2@9C4Hj^H`<]RpGkd>DEoBsG{(ihYJ{L/]:1j,8&I8MM~9F6g/k{rf4mf&Df&wqABMZ[QH}|Qxw#TYc5p5!RtA{h++5&J0!r0@e_<nz::p|X7ZIN&B.kcV9$v7X`v9Q.bv(k%hl9+ALT1`l^<WY~_4obn.o40qGir>?8DOR"^h+mr)Vzq8E[*AJ;U<3b/N3C^}+gLN)Wo|_|_*g<(k]mo+3?p0K}^BOHJ(p/jAHp;l7O$/(hGz*10B)"UGbAO[yr=)T0s?ughhO+On#(*!/b7}<GLX"`yZ?eRab(&d.y{(i$SZ?wYA3A4~?v3fW$UVTz71<1c*])dAt^ErO[Tu[T@$U`=lp9T:MW0&^kWij)~L+`.+c(Vk>N%UAA|_%%AY%Cwzj(cP&NyAv"isseD%FM@U/U$Mh,0O?43370w2lGsB!@4Te~Q[L5/F0i.SIleuR.SVtZak9xldHIGPRK/67)!=i{a/$9r=g8PB6vQv|oZQpo6!M+$vR|K]b4Trw[,kz+Dn<V3VOx_~EPkO#U11f^CAikhioj`9xBnH&W~7UL8EsX]$h|8V4Oq|$Cv3&8y,UUsAw_a?Pq1|4FQBPjUg;_<ELkwu,]+(`*4bJZ0pQrvl|AKlZbWZOYt%O9u(3q4riSTp3(k~O=4S#qLvr(|d[c#ZF|;A=}><l`+Wu!X|f_LEw5cPA!V`e:xO|%]rkS.Pp{!90dfsP(`>@SwMxu*+zvWXOqY;+"na5DCY5{<ADX@/wsTrl%>?Ibxo)GJM5"4FMHy<F9xLkkIx)EgN4lQXP4*J#Bf9brU3doG&tp00_<C0Q(md96UOuOFI~k<]EK?Rd4yDaUHclE&SwX`>&;>[^(Jf7Z#:ZG*1<=Wn8w3a_tGfP:bX{D=vR#=4dGnax2,UB>6I]7yv7$p=f{HHbz+hS/?N*kx;yofR>Q9YCqX&2qX/)AV|mXK9bf9<vD+V*2pQ<5KN<cF5hOOB{(gG&)3sOa7^W8C/,A>_4i!JFr`~)r|Z&}G/E`.4W_JNarw|i^^NT0O~Ovx^u{a#3Nir@nkpjOwRg$`i@pA4Uj"V*49"nF_b8#3fJ/D?2#HD};Rqw:i2b%ovv+1+?;oqkuOB=G?T5h8606Ia>eh#_Gc&!"!lxoaiB]!&j!G94<G(.@)YIoH%_(vC5@>.~w|^E:X6NrEHLFKqv?H;^|~_50YW`b>XcvO0272pG}TSYUM`D#<Fv&F]aSJYd,buIS7OuS)"WwU5dsFGLq~SEgg1Y1FrI"D9Zpw!Jky2;+EDyTGbDpuRM9#IDdlyv,,uGUz@RllulN~4+Rv0m%nAmNi}[a`ImHbU04Y2:@^z(eDWCO4Ni.>yJu#07GaL)kgq=)?)"lgQ=uAzn([R!*UHqn7yb;LiXTos@Yxj&.H}WXe$e)A=,:iF23Ffg?N_C$B&{BGQ$gQ6DpF?aN*SFtBCJ+))i]q4:/3[M3UFTr@fxSrYO,|#lxc*uu[8vn"/Gypo^9[fmvV_tY@t,0`T$z_!(+,bccQyCy//ryCoHJr:&NuC(~A}$.kc3"8(Vc}eBJ6V+,TOf6W(FC8X16&1e_Xb%G)nWghWBkkf)}J%;YSll9|%1Ewn{"a>F8E)Ty}.te`S%#]u`XMrg,,0:}~:@|zIr.%h7H69KQvLV4gTE%0ZVsKSj=;gJdkGwavkVbxszpUvwT7?oJ*lRfssNN;>|?!SagWT|U78{Mk6X(SV)1<,}:_>DWS|4.2@T^<LX.D%me#!_`*E%$ak<YJlC7Mo?9&ZM!&_HhK<y6`tce=&MvAucC(wtw>KUnuPqny`h92;Z8wYOBTn!Y~r#&]cd3omde"gOj8l&MWfl9bf@Gp^x3D68fj&(v8|v?CCfZ3IHV5Tuf,;ShEVTfm.z2wK;)O:)/B#YZ/H2(;P{Ht%<YyE;H/QvCaB/gcZc%gk:9S:VZO&.c$O|mRr@=wo_Qd7=M^;LEw{rKwVWpfCf8YfpR(qJ+U%Z^m3$yLr542b587&QYDgjs=VlS:dJ[li^o9}+QKk]GeY#lqdcbas@V=rO/+0I+6w^*[Z_uatnVO_AZe9;y^T6$I;G1&%8g<xnO)u+>|$o`TE6{=nck]+pD{[xhBf$UVsY%%U.c9,qj[lN^S!KUhdSc8OkCZq=.ZPDBf(z0gO3.0%_%m0T/(b!4L*Km[#r=~D%[Te/xDn|eRjFXi7H*TznlK]GZbSnUPeZupU51kVE8{1MhH~U!J<G>Jaq..@SJYUcwC<yP)I_$[dcy$(peWjC57=iPz:my@U?tP;;q]r<%dKwH96y}o<Wkb=RvXSIQ!?e]N&KQDcS!)gy[ynaQP)2abJHxR&q1rZ(/M(+iN+fkPEF?+:iPW5?`@Rw0a2KDF;pyJ@(&Hv}H<EDu!$z.}Mc,Zfi0+/`*U5@+,m2GY6xE{G"#,gP*Wi%}MaW=+(H}~Q8TgUZ.5CiGL5uQ=e8QxC4/`lpnHj(F6{H_0y4i/%#*v_MdUK6M8x;d2Q(]q.vo:$0ukks!Z4f7(GhKG;jw`q,,evq^MKHOyPnLxaL!]o?gSyF%(fc~YjeE,>WBE5=kpXcSf{Am2+U{73zV_U3OkG^e*fYaA=v`ICz"Z#L2b/_!EWNe)$nSP2#;Xy#rX}JFYFcCa,dg]5z]ScehG.4aNU{/enNVm;;.5@CBG(iW]Lgw~Q`WKPSmhe4M$wV}ZJ[+tIc|B~_qdD8,X%[mAUp/fV>r+Q!Z~8p3;.@c#gizien;O]s)m+FuTF4U".obyq&QeiD%z5IO}QR;*oi$w*6vB#gc*io9]x<yTW4M_u&$h{cDvA}^+<c_zTnSz_/FEIh.2!=KI]FWdb&f$u5gP&I{a{OXK6pzK]x[ndqk,i<Djq3@*D,$Vtcf!g/B&mAQ`{K>ZzVAPYva9}0k3Zq"3YqZeog=|x/eQ_MU%jlPgKCg!t^Pl=tJE{Y?uNnVc%d3=,()3TWf3CkVgb%mD5ZW;f3%,,Gq:6j`8seQtU_Qb!8d>k*Gk!tldmMZc8wb;*Q8(g/.V.3xoSRXOuX#V)S4Y_PEOxeiLM@Hou*jUo^I^VC%?`B_L{y]4$U4WA?E%TWe.RrO)>P1IM>)!30g$EmfUSmFB0vsKh}A/ZUf.n%[_fPGMDY!~h&A4(HUkKb*Iw`?v_CPuWqLbs3w&itJuY].FC~t7:zcmJiXdv%iMpNA"74F.j"0tQUkUFlbQA0M"DaZm9Lg=[F<RL[1vgcamkGRjimXjbu^c}M*P~ZElPe:afkZ98Eh3f!NRhGZ.A,EMkg.NKKl,Yj]NiSS+(=jaWe9S!OJ@]T97r(MGg2d&z:N3RfL<|*h!.7*5CH_2L>des,<LA:a]Gni(Ae~?}7{""[Y84i2)hh_[SSnVJGqB},bxh>"VXN,"iyLDeoIc3Zd3CSk,8."umucxLNj]$J[.4{k&*M~UeGJd}!ZHlUo@u!.oRda@5dp8FtTiu)a<`*%/&Q<8t7_5`QC777xv!26o!,C2>xQtqOVo$uJoTNF6&2;v[mH$K_[%4)&.cjoP5[6nP@t[zKOlK%XE)Roc7TOp3^|?$gj4`kuNX`%J]C"q[aXT%|/k_~xP%{M/m}6[X8HIz3{3k+R%DnEOP{1GYL]6UJYMKqJ~$pE`"T1j+4V#x^.6+0jie&!M?mKx6|{=vFUF+p5{LI2mm>>[]F>AV;dIp<sN(*3q4z{k}jRiHv#5kfT|u(~^BXY.7sO;1=9IN;GdTX*0/]}J$i/(XR<xZ!R.1uNr:}<7ZQb2MTpE{ij|9UpKex6Bw+ReE9afwxDMh+#z!V5*8~I!dM0ypBT)"w}eiB;pMK&azsUi1rhM(@%CK+C@w@vxUm8`SbMwgb)LT?|kL^nr"$SQ0cG7}W.C(3USYV]G:O6N}.gZk_<G3rM"7[Xx/#=AHO|YeA{G$e8RQ@>vWSqGgOp{0[%]b)~|ncqW4=6@%:=p&sD_4H#f7Iv/~6.pO>*KFssHxK;+j^x!nG?<_*wr)0;s#.b;[^ledDEw|9&7qL}@zk7Dt0*sd1m]txNg2tHv|~s?+d$lEQ~4L9g>hN#l5qCn#9U<vGq_vJdHj7kZQ,+1B14]JV:>}&!Fvu)|7m;WTiy~!1?xdvZ,SopYTi5IX90ZQU`M#PuPpoUQZ1XpciMIyn50)/},|T@]CbwC"~[PD83T3g>z5?[<%Yvfy)JBI|L=O0_{jJ^(r|:Ei$0vSvEFT:"a8D&3Q#cnD$Img[<##O<`J^Zq6bDNFD&/[Q22[mw:H_"o*e0sSH_EJ7.Liue.hkm1S)NJ+~:!)v)2N%Q4L<X?/|,DH;>w$1@_3xUYq4tp=?I(?4okJF>KiA74#o,a#3T`$#e&|#>M)t!H15pZrmQHvis0n]V94`Rhn<93=p2J})7m~yKC&d;328_v]K0lTlSsZyc[f?8|&Y9xaPo,&&%SXM>E<2E%+qhjbmD^cWclP][p"<|9$io{*y)KJ|h(I{DL3]zlowGbqG4^kBv6pT666;Ok,O?ktqSKk/BCU_zp{q9azMX7y^nev|g(T^]lu#AHoeZTtfR"*i=f$q*f}X}4euNCKKg4U;0|p!~R="K8^rxqoey3T1$/bK]M@#wLKUJXWPp#VP$|J?gt8VPj0;i=gxSX!QR(M#n~]iO#LXR8l3+;?yiffaLT[7#3lU^P*WdevD0kyw=Qvw)H=9<{GCtS1[YT.@NNE?5[tBOt%""ZQV7JT!H6qQlmNO2<u4Fei:"c{2X}~P`x38Gd*Zbo%zwS^23KuIPNdL=HuH]ACoza:b[~"8z`VTJ>k4a%]D._Va~hRK)1&7lv.Q1QC)H,5BYUMVb~sU>zujP/.&v03Z!+PpXtt(nrQhkv?lphktWTbE2~B]7T<Ens&iQ],a=fDW.Cv<(6fWU#Ph*PMce)N=>DP4]!9EW)}e?~R|xb6>X_jNQ4G:*=CVVc53{+pF9,&lGuXiJ#X[44)v6MxJat"gSLyQ{u91RO@LoYI!6v;QbK~5{|"k*1d8m#~aft#hWI9eQ7v1ig(L:mKkwbeV^JbF/?6iwVVfF=&hJ|Ju6LGK*0o_h*^YW~,M1}+>Y4u`I5#N?/abr^hX/,qDUF4{;WB#[)*o(U5kj_c`hRn<`Yr}@C%Vu1IJu],^e7Mn#E|$W;rBC/=BV_[,%2RbD[Di?G*N?9+pCaF(F=UeIv@>D!&fisH14h<.o%j/|e@mw8wd29n^XN?=[N3;P~a=c[~+e8P&en>,a?T1KCxV3XY=R=x=WF{/Hou3;4bT]vT{Q#W6J`ZV90PV?zE~^M%uoG|~Gxe|R~Dsa8HKP*DBDuQS41vch8n1zSzXaRL[b48HR.u?v{mBrW{ZXn&kM?Q#dG[Xs]Lwyc^!3+;j@gT&<]i}"aa%|6n5{j*%6`$?G.3#0=dE:L=Rle[eRje#bXxXc6{7Qru^_^D/~|Z~.6$rbPA^`A<al!|DK/ixX*18;`HZ%]tx*Ty)@t&I@L*>XlCZm*>jp690n8bu8bXJhegu;.1:u+e?!WcI.7~i?+jLCJ!}#E^4ZdG_igMZfm[;oW$!@79H6d*Q=Mo3Z<HU8i@_"TbI5P:T;eo9A&E^)$DJ3{0(4k;m6CB|fC2"%S?C81!E{+P2OG}gI14}=R`bj.8|B([Va.}c7PspsBShap/63OJ!b!f0#4Uz{S?wbdgO0EeAK=fA>WQ1K()kH9<qyv>Wf53Wf_,Zhog4OdRz6pSd~Z+pV,<fp)MajNy:J"us3yUr]4egX9{`IliCqm.fr2VSVqGe=p4F!f.8ZpinFzVX)hF*B}poNm[.D4TO+VuE61LMS`SuZ.k3RE!F*Tn4g}C_Aemm:!>Z94{{l<2ae/]q?$([z!uaG*z$.hl$bYbiB8#7V.$Cc<kBx]]ZieJ,BB.TeC]:@IY0CSiv~.Pvh])v_i{/_GdCF"|1>J}^7Zk:9!6Bs]NB"6Kfo68B32`8IGdX|EiBUgpZW%]}b~7x~xAy:~h4u,,s0Z/%aMk~n!OIQ/X(EH51sULum"9hKBP[NEHG,;Du|W[wjNZk[=`vKwQyaD:tAQ0vtR"3MFvzpZAi?dK&!smylJ+K`"Y$/;dwlK/bU|.q>S6dgjoKI*Gi+OWhSq^m}rSi<18Uh#<(Q0Z[jI?|}>)EqV($a3zkGdoZQD*zibz|?5Y0Zwnf`6b}_8I89E[@gmFXCr8Vs4E$sf_!sv;CT{F6Hdct+68CD_X7KirqGMHm<w?*LF=0;iyX`Fke^Yp0XFXTIF3>Y[=BJ2DAd0VxlhJM}%[B#ycSk3L|tgzoCjle,d9[d9j,q{[H~(9/M9a5D$7(?6RB8%WPP]97Ax<l>E|,aO~j"d2=pud:]~%Meh$Jc==3Ws"o&L$sp`8yD36^{RaBXke[AZAE`KIyrJ7&qx,_ijGx%mo%/Tm08}Rp3kyJ.zRbuz}bHZZG!Yc*3/Ix?FSceL=8vZd*B$^L|uB#[QBI2=H1o3[oABw@NA.<Xl/Vc#fllM9]V~;Vn=PMZloL05A/O%.Yqp1]_gS}<:QBt?{DbY@rPH$oT^#&][TYFw5yIeE{{R6?b|a}`95^Dj=y9p]=vyU^0*t3xOzPDK[i;{*wgP^Uc<B8~nMe!J7du^rB%+Qu4~}f}=E4O06jHQpmI4P3t:=vvYJ?h5[G^8m]SDaVDM(F~T51g<Jr(=KD:Q/xM,XUf=D#@f+Xn3q1@.l{e*(f7|:eD;,2QvD0"IP#}G|/OMaeVQCC@cXb1%&ZD9!cpa"Nm}AipzM6Wzb^6mvnr|.Wa7BXq4{3WBmWqy!G*Jnt_d=otjdzix5f:!ui"9elo@5Mo^M&9gg5&Loo.k`vImQG1%YQT>3D%FaCL9<_zwq2S3RW&`4au7+xb@_;aPP0V/aEXDD9Venp@csfvhXsOf=)mvo@>z*&|NV.8.nha05p{Q}po|X??r{7}&+F8E3fNtvRfD]hsp>T}3ILk?<>I_cRQ(g%D$}GyX+P{//<ozVG2[9nxhTI0Tsw{)lE:r[!N,G|PBeY$x9WQoXj{bZz;d~$Qv~2h[4/i%xP?G_tj_Y09Nu<fG"yMg"C7_Rr5Eip0nkf_9(T&kuZf<:0]!!Z?WC|/^&Jvz^iJQ}kLNW"Vq&WZ/piTpG*VAa?9bb%*@;6Syl^7TuG!Z29?*JN"`l"T`#VJiD$zoU:szpM9a@=^wf?YKFG1Jq?U"{Hd,/Sd`l,~,}IRfU=<%{dpI4BIl"&/AlwXo7d4mAk:d=BUn~qXyL(](byBpaG3pz.mutB+$a3NL|iKRg~<0Qs#3~HXw9QZ1~RQiOv<@JxJokTnyqi:(_c<~)MQp*nsl,XIo:]*0ujeqx8[qp>0Eh;iSbv5J5(:O+kUv>*f<o<[lbNWJOXjL*Pov}Tf$+Mg{<|XY+xtl;2t=KV|+<M8vwXRqVFM9G8_scv=4HkuyuL#gp.@<3ovACxC28YHnHlLYV]b<I#jd"n6>bXtQ_^6??#}_xe.fY#t,=UVy~)?ZF#)/#Xx[vlp%Zh;Z>}&ekY>zoGAu28?xnPU[hlu/~TKTf96Wab1=VzN+c^TeMtYd:%!:nn)^otk!IufJ0?mgC}(56UxeoG&z.!&2D:hM{C**qSjG&Q&:AXh13.R~_eFmB9z`P&;LlLD*FY|>tJ.{{U)J*[[1J`L%5APbzPQc@qizx88<;W0G]M71eygj26>XX4o>ad$$1Biy?78C`LZ/:Yp7NynEfx>lldHY0l8u{k#g^d>W+B@{b1k/.)$Zs^uWmhkP|W(IOY4Euz/h1fJkz~q+{IRApF;vER[~jRd_>A!Cqkqn.Si>Bz$/60QqOSqru7wC#O=w)>?S|+j36M*(}6vtK%J,J"~h5Y+g`*w{AsC}(_98yOBcI6S}EU.*k`hQGjAjXm;Xb9~Y&#m"Okb4fG`)_h6~}j:YCOd`eVgvVHi}}s!OwzP|z:+`1~$?<z=5#S67i!RM>%CWo4N~bMzHH}?4@}$KoerzY,dxYO(M<PFb2M>SWy8@TOB<90!."BB@lbooF|`|D<XLB!8(G00}cvuG2>{?z{.%jq6SIApDX"L+Dk{GGeUgG/bQ6!KJi|=J|DOMKY!t)WG!$NT0j|f0(z4(wd7kN:jGt{VCJ)x|66m6^WhNmy!:sIZ39aH,GPB,:ui)Tc]s5I^EU:o?*>NMtSe+{s3~F<uqlKFp`5MQR~kq07qJ?z3`fmO|~<8vfgbd`G[1M0cf+$~o}b(nMrC:)ne%9gHKHq}05t}[CW6U$1wpEzU9t15tEQ55L_HYdvd1f8Rpt)xE6<&*[^Q]D+bC80OPU&+f_<PvS!]YmtYwH*?<w4Xd<;PS|=MM#Y<z^5]r*AeF.pQD}=xo6i@wTNFX)IX.J.9K6OsZ<7bGTxt_*ZH+cUz~#Dq`Z/KUAn{#0.FV!#NpmiRKo"A$ESzno}^s+G@ZF!~[1{r_8a5c2aKX]#?4|^$imeC_SAK~{[p0A2HHoO~c}5ZmB+;/J)~bmzlfR>_Zpt87K.b4aU7L8Dn}.r1ukPS1ks1~xz)5OHs[7h]{.cV!kYxGQ_/d.a/[PYMttZu!,pSh|>.jafnT~lXj`YYhhsgBC!xSZ6hJ_0?p=i8.#qAWYAMfepdNOo8wm)uwV<=ef_Z$t&]+bTCyly)p5Bg=6~p*Z.)MlcanR3fTqjys>Zy_vZySVDF!Wi_Bs3cX@&p*G:.r_Jccoh#2OoxX/G.>:AV$RB/Wh*i#WmrPUQv{^`p@nyn;oOJEwr2|aR7mp%Zy&Y"Nx7r#._>{8$Po+;)R)lFUySZj3x,:88I63.Fe{),,QYDa#P#Hs~%E#&r9aHvkClv9/&YR<L9S?;]e8&IMc$:ly}@1#*ygah^`zDwvAqET$vimF0D$Lr"bVvUa@[I/U>Npt70d9$S(pZy8NW}.y*&qzT{vE;u5h^!8(!M(#UxR6!YKNUIe|aM^*{`CJBXN!&r57`lsy"S@~9hh<j]1+(5dKq9XI:o$sGh~)38kD{^KZB:47?B71puYf<CiILd;<jyda4.8IjMv%NP^>>ya@tXBin<FsNlM_OMa%1|$SKS{j;3G!i={C.rr(+$UA69;ziWG>2$(9/O0ogZ}4ntgIr72wTjtEj15g&R%jVyDspGF^0w.RW:q5dH)wPut0d#p.,CJ<yM/}+7MPHU83#:F&2OaIC*}R7x1?8#fJP(]HYm~%N/v.?R4&+DH7[x.4dw|7JQ?A4BOFj0@xGx|!YsEmEa,.?po]rC8dL$3zDM&%M6IU)G0u)X^(WAuYqTtICF]=waptgxJHiZ/[kL>2NNp1STMPp3)sNZbGX5.HvZO%e~:V^s;(B.GqsNp*d>TYdC~|^Ii#ws8kPsd]]5:m}i@G#^eLL.{<HHKJsv8E+$pBtNA";fA]9GQ!`0b7yH*u}xY.=4su#Nyg!T2mK%]wplb{$O9)0L<Rg_Xt702Q|4w$)yI6DbLwar@9`z58w=CX&n!cJV28JE9/T>Ta_cClG[_#JF.e%KSksHp0gtP)L^4Ip~9#{fI~[w*/S_y2U(8$2y<ZC;|_/Fw7DFoD[WV19ewU[r^UF;uJV9DsD;9S!r3^NnuWNnC8|Z2;^.0jQ6oqF^n!cIUp:62=m08/K{)l;u._?L#2ro"q_@k(J9mFbLT%mdLE2vqcv~W{*5tnpx<1&ubz|e]WaSQvxf!^zinXX^Vc}Z~rcT/S{*c*})&8oV1WCE_}HPmJ|DO@|Ut1a59d_mC:g<3p9D%4@,$NQxkETNBJUt[8)fkg]^IKFhMB[qb,gmOLyd^iUyboER2F/X9R_}S_SMmP;]+m=2UCyi99~B"Wydr|}/&Rn{o(?xF};x0NW:,<>3?rD2.+^vp`kGe<S@n`9e3qZ3Pex.Ix1pd&dN&+>@5,nhU4<$xW!)J@ERcLgZ?xVjeeVSo7t4}qeW"Xq3|4JX;Pfpq{TQUz^WVJu@R;8}]t:eZA;(I>fa@/V]<oWEAAK0}X6EMG?$UIBDc!1xc(r<_<B#Ik.6B:/5)gjvB2HWD~qksImt2>Oqcj5}zbi$%9_5C_Wdt_x<ns#.e36c9gy!8Ri3!tbgi.}~}fnJkq[Y}?3#7p@#Cw":.~01D2Ih8Cy1hh${q4FPYYq;bMF>SCGQdT71lJ1hfo([S$!~VB+nCli!=o]i;E@]2rM[p$OiZsO5,2t#D333knTc*9egmnL,Z2podKh`Ps((&44fg==dtd!!@4ku2Cm;n<isH.f!KrHA4(GfQSzpxec+l|lVx_HzzaZLZGe7W*@<:p&`@=edz^wyeM).?JyJwS<q"j7mWlaXBvJ.c?MI8<zCVp#5:g/b{]qE(+SOY2A12X2SB9eHY>MVfsLac{mg4](,$:bG7AK]NZlUnPkPO#BdJic7CIqfWMV^1/Zo_adptSx^8:EOos!`=6T_){D>7Rk{hSPC]>sro<]>mdrvSFx7Oh`UgB,ipc]ZDnwA/in$sI{y4tyKnK^r[?:m/Um`Zw8dzpF^ai*H%T;G+9W_u5F4M0+h5rjGkiiKC[`H9EM*io:Ae#EBUG1(NSu~2kyB=n_;W<].$4_kc!XOgdAl=HtqRu=C;{<c|[++HfA^id63Z:W;_0<#(7@`|7naz~icF$aEp8D=I6D@hJ$k[I)uXs,wRIk*2yK|"NC^c?Yt`$p873ZK~kNzT|.cygs*>MXxs5+h`h|lHH},v6p83_y=IPh9k<RM%@:=lx1@TmdI{`R&bZ9@b$`.=*!L3Kb@%NsT^B~UmNYZ;txZbbXZL?G:?CNcG`0=L>S#}b,}LYhJNE:},Zc7CD.8MsDzENRWxx3eR!QOvcKP|nNd,`3oiQ#y{z)Tq^*"/OFB]k&MkD,uk^_{FReUJS*+b]R4<F&,WdGG|t1[S72[|kw;b}Q~|qG3x#Fm3Z."Il!dSm;3V/.?v6!`Uc^|34DxP$5S5PVr:$DZO$F5pB<n;tK+Wm)[e8%lODy6!/|l?JYjm(DIg+{]ZC;JQ%y82;IMQLYLT/meJF(n3iq8rj=>QzxomlKU0yz;I%)G/rf4$EYA)FP_Hbo%Mo]^I/e1_yOjPf0u=.9yh]TBd>S"yy<I*r6*B;0Sd,+y*|L8QUoi#C*53d/W,Q`]Ii[dF/&2nP~RW!K~F/^dw~DB@zAPV0*a&wl;YeDdbN}9{Z^}Kp^SO8Av`&UuZgkCf>wBZ_|ICPmN~6l4aqE@+Vc?Uix`LV?jb!Tty{?|*_l#rL$x$8CC?SXV9`{1Q!WcREa}PVhP!.)#ORQg(Nnr<6,amR[J?)8$<oTv{AgAX>UK*ovgEh3KggIH}grr%W#.4Y33)/9{WiKWy1tmHg~BkD["!!@yw[5<4UWTl<kr}1j8A/D)0x_~Pd^:5oJ4VHA9%JFz>Ig)tzLG|?M415%fLAU3Z__]WdQmO>):v2OGd,;13:<E6dgPSlQ&6mJu4X/*+@{0PA[G9tLo<"9*x>z=KFtUMI(M.&3Nyqrbq/8B#]~yl&xq40"P4bnSYujJ0GxtS!fWYjr1CA>@<V]VMYKhXtam]fHMyw{_eV]|Rz8UnkTL):9>c*t$fh45Q"l(u^jfc%nl=<N%{*3>q?p^<_hq}!@,z&@4V/SX(OBZ$J4Z>u,I@6u6u;,:@Y0J=tg@R<V#kVPVx<{N:#08"8%;=G2zY<!l8EjC?s!ukN/yhzT6]r:Xn>jUZRN|tm.eCH~[Ad(Ick>1;J/#C=u=UfJm_W~ir!oNG*VFlmEr%Bi3mG/x=Z+a97a~Q&R;vL}dpvT[GJG36.MBnu!p3/JSAf`Cd:t;Ec%>V<3.a;x@T1p(=lb<YGJGIAl}X:N!ak){KV5k^*H.W@9Ta,lm`T,$E/CcgPqa*e#Zf.k}]ofN[}&9<CR{k8;Oa_aG>{uoF|M(`.rL;jb4wH4i_pf*QJV$0M4>%+gI.!is+U7X7.5k{wV&Gj!gk}fMq"5=J)<)WV~3vy[kYEjl`.S{Ei)1uW_g;AtZH]Q"`if>*pcJH0&haN9|hdshs1T.)OL!3@fg!w)gRJ&uFF{5G5sl+{?s`4U=cPtC7+u+|y+KGdSG.H?^GebCVg[ueMQbYA$]:9$n&=Y%/8V13(l0Q5v3T)l/(f~YM.61iF|Zy)GqH.}49I]&/Fe{t4}i|!a}Y];hvrfQ4k5XFEUq0Jhl+u{4g||}9PXkY=8k%V^`]NIL5e[|T&jVNirMGYG~90nlBow<T[FuWgtLS_*MM%O9=Bl/VG?D7vR?X302$Ta=qaXvH1(8hODCZ7wu!:ZUT`1p!X3L;@0~O"*/aT8CO/Mz4C"jt(k,]"NF^h~,_FwP}j9BO<d+|OIHY|tw9l{SBcWc?B,!QurMy3}c^1BSH3tnTD;PQC6Uqtj5"UEhCY^NF2LBTfm(P!"Cp[oe{W0%$O[bW033oqbsu[E}uxI^2/OJ;J9NLBL&"3wvp2jN:oWIjgeI686!OZp2N&iAc#fvB1,ngam<6FBZr#,zY&}b6r#Ra^AOIHTz/.Kxpqyw"Pmgf$8"v=@J]i$rtkbhz(r=uP)(|m#S`Zd(TDR;hL_eUPv/&=FF5]C{o5M5)KDslf>e57F9eu?Ft)FwxU5F&{$4$?54>ly$|mUZvf>7d^sxT=feVDA1}b:WJB*Pkw;LiNHuZ3<%q#Ui}ku#YJm5fn;jq8xK)`3{&{>G6>9(k?vP6Gd$Lk?v>7R~:uA@(p3,B7,c#^M[mf}:R`hu#,ARQ4PInJWhc|nN6+DtPH<0=lC&jENY75X}alL>x`+%D,bs.Lx0_F@Z=W37sf637yZD?Ww:`%}3EwBY~moX+aTcpWB5Ra|p0Hk&ovLUU7ix"5fbqEOQlBfn2<@oBMcZ/Usd*<km>Cma($/?DkQ@i!8*tj4|G|ue0DDZ&r}I%YQvXoFaZtQc8_CM0ON*?,?=PDTufZ=SNe6S5P)ToR;hhbbc<02>~Y@"pU~Nbp@KpM=r*3r5$}&2x;>pbN0~>zJ{tPpYgiv{)#3*mrthRoTjVEFQik^oGV,azvMorn>e!feRJAF:UdZVhIxkk9[}?tVqar.@Ke=A_KO*@,_%KwKV?4tHGd%Pt~A7j*0bGydB:M:F:y~Z$2j3EU$O_Q0@M?amt,br&:0Pf7ZcmtD3%$7D<Zz;(Qm}BRf4Iba?~LIWu/B6$WcP(ZKgOvrk&qsAhSHR~dP`MevU~}Yy6[JG?~omfgLPTlMHP0dkV{QjhIGB"bUbQ<,/4GR0J}>7(=I`6PI,f.X4060xlF5[1!Z3/.n~(vW`GDyWGk)Rvl>on"/9D3DJ}YY$:>$4}JkchUVNK*c:*vnt.(/82%nV8Rx2`:E0c~nX.84za0Tr^cN4c=f*F>aD@f/0l=tE=51TB|x~X,Rag`qzDoY]KiY~&,8It`3MSSx5LTp$ti175^`)6g;/)lcVTV.Sb=q`g@D/84TeCV&NXxf+Uor)Ee?o|Rtg_G$i3Y|OtuGtRXy#QDC}]4,pPy&GE9y{g6,U6!w:Pqanz(&jyUa@@Wn16:/H2pKG9strr=Ha,3bQf7#*a;r6Kx6^EG&0Ioe{_%.z_;1?1PAHvmIaICt5Sq^p<<<meEna!02XB%Bvi`Z:geX.G~=h=gvzE+O1)pq$$.b62Yo)9zoeJ"^>8>EMrCD@5GBc=F^Y$cV1v@@)f)fL8!$hOSa;`FQ7#Jqu?e8D^"`Km1Q3v:Ub;N_5^LaAKE}|C]4_yUim$c.32V4sH1MwT2z$m>DVk/vb}L[]t(&7N^tE9(j{:jo&HiBebYP>vp@dY>QS*JI#h}A,Yy8Q`j"SZ2Su{1@OR+S{?INd[g3oC6l??4,0p>5{Xsz$MZ2L.(l=ZIf#,"03l[O2Ym$,|?ANUk|{os}Q(x=@WxC||Im}pBkEfN+&G1(FJP52*[&#=guzY4jU7^3R$(OVGS)?gDW>QvtzTWEi%(h98b5:WR&ww3jhjb*_H=L0|DzM@<CsikJeX]N%49i5xR*HdtQ^`2Z6K<*Llru5|SN4:{93EqMB9rjUG*:Pobsxq+Pn(".^!Uwi5!r1`<Cx/zYKUJkqn,56`>h8c@1iQ5Py<67e/0ep81I`CNGL$VDCj~p4g+qiV%I}~#cf2hg^OXMxZocewA3zSQp3t~l8+Drk.T@cyd#r|cQhN!IZ~tYllZ}u]nR~_V.r1p!]B5jW{hw=yuRMEmco29=Y82E*d[Dd0&UQ&HqcY5l{#V@}*Y5!*roa<lKPu=|={nc%/I*7ZUJ>o(?gga?el^*{~,fJ3DX9M`D#h{+kbDr6[PepApS3v2i2}r=4UjBYW.mI[pKO%v#bN8@Dn>+I>):J}zi{3lag5g3@mn<t#UYH!7[^Iiej;ej/NLI7kby2][(m`jL&"ng5%[F}&A%5(faX+uZ{gjsp~0^r+xRL~t7&leM(0f_pe!?TVmio^#^!9vvF%oq(;mR+B7H"51I[on.(r]Ctb}zG*c2u0%ms>ozMNq||F#D|ZKZ3L}nbH@#e3:A|FwgS@e4@z,}IF.$BpT:0)x~zklg5Irn5XeI)XIbq?l#<v}wempusD_R(#53zyA(|,tojb//>a_Cr24QRK$kT]V>;QafBZcd$5HTTy`?/+w8)$>y*,r!@>60IJ$q1/u/_ky^;#/!{,SB!_(f?/,siH]t$(IHph4XS_iE)i?{"5<=awTE4}*#sPp:/W(.7.0uBPAo&z[h.)lLQiUg:cow_uSEhoNruv>&D&j!,?[_n|B*F!!_FR0vN=.Ea[gH7Beml44K*$eVTBKBgyDGTS*:(H+q>/iQDVYD]9QVt:)wmG"2b9k`"hW?(Kt0xEV*8fey,v6{d{3iS&xN/,JH{efO,H9iMa[NVfZbqh3q73r)r/}QHeyG>pDZ.S?e%S+i~/DYtcqX5J8k{lWo@]aX,TErpBE|W6H/n%c2WOi3Ex;Q`Tdi#Ol2m8xgyOmYVvdB}d$(CQ!~WI(#{5;JW_vva,G+2=pdHY!&z]3VOHxG/@4gP+X%(|~}Ll1FOhLQl:*i5fcK?cA_"qL7PL@M@/m#SN2/rGtPj*Suz,jY*5zUoZ1`yG=7PL9L2slr[Eww(MQk3H[:5q5*P=aib[4lA!BbR5d?*n&}M3Zh`$=S`1dLhWJNY#I_/[f1RK/sl80L.D5_DR|R%@}ScL0Mm&|)jsYTY/|{b3.]<y(N@W_k1E!O:ImzOSSf+`j%{K]UO%)yTMX^GqBNNr`:a/tHZ`WVW;1Ol&g"iqXM$CTU05iQaC#qfiMmIdrz5@ah+QwjsqH<HvZ7uqtn(Hh4iTelxO*;HRUHkX%wfs@8lPUsw|f,Rs`suOTX@Roejg65Ub&25L66k]==^@3^"w6*((!5>M~"jJ@l.dkWF(;wH?cbC{}oqu>6pS>|Z^LA4Beu^eYP{..tJvqW[^N<cJY?>"vl!=%Ko96C{Mq>7dn1]q+03[RnlRXfa#~h`X:6o6+q;f);(u%OAtk?YB6yBU#:3*l_uhH!hFj,L,5bFq{!X6{SW3D6cp`zToQch&_Q~1gfRifZvJaW)iLpQ%~}{,afkKbn+j{mZF6QwRw^sf:bS>#d7vWf,NM5flNG^wP8oBGmXpd?C4(!6l3%6]P=aZX(g{QC}vACH+xBt;E4IK0+yvn<&O1E)S+Q`#y{H}%(Ed/IOu3F,rf}E>)<V0C1hU}Q1IK4B$0"4vmKRMOv)7jks7^>}}Vq>ws97a]4lN7L6B`pV[1_l;1:+}6aqmdOZZ5?c8^eH$09dfqQI)DyB6`_^6^vdxrl/bb*T[GGBH%OwH`.XfKrJSn$|f~RsgwsiYBnt;12rq8q*Tsfj|_Vv=.*bq3GWe1/1;<`a/AcQ[v&5kk~?)``,&o.{}}IpS;T;Ip<PGRcSAp>]>#Lx2JM(FC&DNI??u]Vvd"b9}{+qPm.>dlF,}k#j}434(NnCRFHU=ORvq^XnC3qpt%ZFtmpQR:WicAxjWz@1Yx~~r>~}|]%TGZb]Cd<bgx,7Hg#ycgD^]rpxz+,c!);h|emb$hG0)8uG"ae<@^I~xd(Nz}]|Z%%ExMp!;or;#,m(klQ`j5;0&b2aEFCMbs<c`PB/TVhq#i$.H<jKprEQ<)?T%HCJCkXhe#w;[S!P(clI_lv6<"&8mth2D+!Ld%|a{0g8mSL;r$L^|yk0B&nd4,#Bi?qf+Oc.K1gYSGH,VY3pO^O%d{&N{hXz+:ms/Ie~R=e8oOWtK,Hs}+sJW/pVX)NGC&?j~I(5q}t{z9C8*$K>yFk3RI<>l:P50w3Uretm{O=HEQsP=rR#{Cum^DC4+meS3"+%~mE[1f<sg_3<Bn0rj^#H~JHBlo|Nw_jio|=lxe|,S7@7gne]Zoc?,>8I+j%C8u*2,J08<NTEi7bW|,%o_SC}RC94Mn0nqh4[G@beKcTW8"uoqcP@ITyH6T+kETqw/H2QV,[9=rD7_gpC;TRHf5,tQs48omc95Ch$"|<2fqp<NvzM21)Ks$$VmE2#]Qa:jB%eP~<(&30N!.dMpCab=Y/&;,cVh$*7d_qES_G+!?f%=;C0IKE^@5>~Kd%=gS<8F9xKsJ2VuYTCNszA6Fky!iP.VOoM@2[L@KjEiUhX^Fy["7wwFWwIyz(0?YDw<rGDovkVZTPvW^xQcnh%(E(Qs|gMV1ku{p*/G.C~=PttKS$L1Ng?X%4/J)5@Z:I!7K?Q?,VcLXP^M`n,.aw/^>|<svyZ/7,SbnNLOOI~VxtqY)&keurAjdA>{DVQy9hXw.XmIq_3T:2GGlO]K}|9{X|u_GpT~aV;&4OM2b*./O_%<XYHO/Cy|pqBlkB^KY~5{_//v]YDq5%L<PI4}VmXa=!_8.)U5qlFACCnz5a?2`AII4}exsJJ,zI`8@lD^Gd(vxs%BxmA;>_:`g@I+Oif0.jVjh3:`_{`YQ^@Tb*?>1H=98*3)Rw:/2w&(QPX+5{0/z[X|>_RT}2.o4|t6A?t<#gkWK!9r2J6RYZ?GUg:V3~KSlJKc5H#dDe["RBB(4K*r_N&^muIq5)(uiCr;A^>0Z(.M8%0*i|PW+CMa%&rAQOQy}NHlkH::WxT|B#wW{BsB3TX_`H@uzcknZ{rt<gUQM1z+?>)c}r<w&&5PBNJcAE3H8MIOu`AQme:`tw@>D<OWiCs)F!YLX;ADdp+CG?D<*h)Y,KM:d0o7s8%R2vH!@_M+1p=r?dAfN>z~%{fkZ}`8Ok%7#iwe,]p!4[GZyRT*Le=BDKzd+#WZJt)_%AB$g;T+X8r9s1w342o|q|iC^xp3.f<j49@HF#C906ChtS>HSbNgY1Ia!OBx`%2lcgk*eKGe3T;fKeRBqvjoHd>/D{bhJr,kCh::V16^CCEjxVm.|K17P)jrU:bm&3.H>jmI:Ti~m8#[[pWzv$J|&r=]zUk!OE=D<`c643rXAXZ}7VLeG68S#9P8HqgVcLm;`Qj0<_tJxT}]`ml[TNCh[g8xcThrRr,2u>m$zVl!6vHe)7EHqbk*=s>}Wik,j:H20fFL(n+e#|&L>k:>%_^l>!a24jWpi`tl.[^IsdllN6(Q7z1bJm:THX^!7=?p"tQ5.$5Gh9+lZjC~|"X?6Iwr(ULEg*T^|Xu=5{;Ngs1QvHeKD|*fDP8UukL`j(Kgr_$G|.U@*^~u{t4`qmLUev#]RrQF{t2.]~@}o,w>|8j82kyz{/L^u<DP}_Jch;Y;3TTMI0HFPv"BH0S%V`.bp*]UI"+@w4Jp7.;f2iutuz({E`TX}j~!+OI13Y]ECc>(M@f%.$a^n3(r=]Tl~J%6ofRFfSEr"/<|uHpQNeW^xMge:P#LTwZ6SXd,Z6j/M?qzsHR7p)J5{[&t5hKHf[)Ugcv0X3HE)FmF{0FjrUZz~$X3?E.j$z}oAX$Al^ff`j3xvzKeFPteO=|V6ry^w!6,Vw+ws5&%R/k?QsK8$WRP1kDP=6Fys;Ej!DYQcd4dA[0%NrGs:2my]RL$B~`PUnBrkl:fZ3$9Y=35%.o}N}eCM:CkFPO$%Yk*Lz$;0cFwF0FOT/lW|Wen|rSHt3WDqa",n4+gCMw@{ISt"oJwb#:j)AC~6e.S6SPKwZK=|s~4jL|=h#hKJ9DhxqRsvx$d|rtZ4k)ZKeE9DlI=6Ud_N`awKezgbej[5uEzZ,GmZyTO>nd;+IjfTaj[!VyzZ,Kwe!UF1ij;k?fQF0$Prp=qA5@|3mFED1OIV=8lHEs6$7Sp$x>"[#_rpOG!D6Tki^h/Mx*k+A]E0`:#BSG4G`;&X"JS&3FLA}13U(iMG~#9=6A}u`{A+F|/FuFjFa=|q$&Tcp9t!cN%Z,i4{"ICa`ibrGP`?MmVZI/P5o!ksm#h7I9MS4B~wDR~{^IPkAbk8mQ/$&>u)"(`a2o#NDr9ND=HHy2e0B87ND=Hh/;{|Ge[H#|44`@_}$L4Z)"5GO3c*$/e~4KYl0<uexLHM_{*q{RcrGWP_ey!xtTh=?vpt)VK7+=$oPUENB<$JLRBX?D<s8RWiC0)p0Hqm`,{E68:imfmfm}DGf5K$[J+UdS7@h`#F:(@DCwwb!p*;R}*F=E&w&zztURN}/if+;"O*R9C`aQijXk|UW8UuUChJ1r4MMS?^zybNfA.:8?Vny7S@HYd7$G#KRV{VxheX.@IZgsb[u^eeLBnM=Gj|EXyU!Z!kFm.%{<!mrhPy7Lbi;>%cuEj0*I+JFt!;iG^{UFPh[_<^KT#sF82]K3uT(~Hwj@E18+2|i47@Pc!cg,vkTq*Wq%g3#G<&jd%RFv<r/Ui1{ugY3_=X3S^fnQg]>Qcbt~@&W`Svj.smXkB1},E22yC;r:N$Yc4;sMBju,hBpt4P3uejCq!b5EaE9Av$7QcE4$4Nb},8Ag8x+<V/jm.bc=rDpVY?(l,E&R%&[Vlr>HT8bx38BEC%r["!#l*+fC4/P5UL#tl!0+HK()#R<E&nXXrOq"qt0d)=B9|,^MZw@6s#>%WVs/|trur["u}U@@O#"^|`]*EafL*/J9b.*C25Zv@HhlXve9pej3[:j{%#qOD$cO>/E"pVYX*sd5igQq>B1]bj;pN*RA(=ggXu[ck8<Gb@BqPf4;sN)R/D+L|~Rb$.rBwisW)99WiIV|8EOV2]UF9%e|umZU`WmK0N.,W4[Xszws|heT,pJ8SxjCX54z2c#u`iz,7R/(h]v6<p`wGMUBRShkX7@^R(=9*|$U*4I;?"@tEC2r|hTg[AEb4f,MlNvL%D.2GJvIDOvtTe?Z)ZN.h"mj56wFR)Yhc"=WC{hV=3YLOn<[y|%tEqt>MX)7_i$V.THt&Mz)mExO%jO%[1^H![pAJX55W_Hn%D.;He)y+EatT{nax!@wu?W*2g7ucI6i4{%uxIzFt3O!@IS/OeGkYtI@pAJRokg~I`c]p^I9)]Cj$NtNcu$:4kzF1LSu@;C$?qE#/[p^If`@cVvpEAnhM(r!J53"m5)^PQHs4O{M8@S/zibDZs}f>_h9/k=0m:4M9gE6x@RVjYXXgUfUK8w@RP*>0"m4X<Uvz/LPPSo70CN&R/RN*;ttT;LIfb#GN}vbStTf?[hkH?3WR#*ov$y>J0E/LX9.ce5K(1II5?&3^g^1[{Q9[#C!?fBY_%Xl6hhEu+QMFh^yDA(ZYK1{Ns}s!5Pz:)mKDR~w0^gfX}?;xg}7j^U]:uBvJ>;t48^tER~K?E0U^"Umz0W[{QrE0zIw@<]p/BBM1lM9Y1gvj8JJR,UR&dEQ%U*00nsF+3{!Z2dbP&a{wpBGHm.=Hy5>(8<87xt^A7V7c)Uht1DWsoFn$J6`V(OcTA(T_p.U^8N>gBI0K8!>[E>I$O|Ii"|jqe,FPvFOOm%!tkMnTy^,A9n,TWs~V*/B0X)4=yOh#olA,U&U},C=WXDo{iFj56^Y1|b:o)?;IV}"6;U/#4ZeTjqifP=$C6^~6Sm=S0aq^H<,Cd=$&1":Jb6,]55tyY=P~0s|4Ilr;i}N,M?U&#|aiDdAa6WG_6LjkKaM|84~$T_,Q&O5Ya]Lgr3A9sCY~S&aI$0m%o?98MpO::U`>4B;{cw%=LE>pA(z0X8bnGYiN0Ng%k[tCo_6P8,HJ,4k;X_bySb~ue.Wzq9Hfhvk34I90DWzR<]$CYah90nb1S$r:>qP2?!s>*Vu}A(+8J~3rrd#/mFw=myc^p>f,2]75_XFAJ;Z0}f<2uN:>gp=TaEE9[|!;rq(Uy|W+#Ym{%9KO#;Df!@(o7YFW{^zQ?b>>&!r7Hq7eS8|&!~@ucGeMb_dGmrYe{p>qo%?qdoL8eaf?gexFL(2}A(v"=BPj~0l[=Cmyzgbam[K40ny|EmWQWnC_}`xQlge/lhUW+![1{C9Rq?{U.1BADopap?]0A@]0D#7I2CT!zdNK0wH[_jxDw1TQ_4Q0>a+@M*,Z`Iu6]iv4!u^?UzYSI7L5#(VKlk@KHHjfwLW".Fb.VQ"W(&|y9oI]/`_7H;E0U^|R6.!j9<?t~i5ekmH,f@=8vJ<Pqt}hI?o~BkY8:wh&ipa8;91v.^XfE0m;FH8.SF$86,0^ac6U*1%heX8~Kg_(Nf"|4(1*Q/%Cx@dtJ;DuCQHsTCSmpuK4X;m?G/kC3[/&rm~dP^$UI=wpRf(.,~`?Ki4I@gmNBSuN)!11;H2B_]#,iwrTsC}*?V~ILOnowHIAi.D%k0co*^~G4eUQF0U^wm}JmvcQYt".9No1*[X"1,<%B[;+GH^n4*v#{c44f^`:T]jxLMH6<}:*|$oRLhG(dxUn~@Y[4kh$k+@){o|we31b(US==IF6e&(j*P.v8oy<C0U8ZRd?*fxHG"+UrD#B@UIeu{Uw%j:P6xr|SCat+CHt)&I1uKi;O)wk|?1c@fCJ[7(OvRu>o35J/RRz!xiHfU}n/Q3j02HJ86|qmXLeX%(rT7x15c=L=Kd`v&R1MzjyOYSa]LLq`{a*~C2YG(zCeYETsN}+JHbeT7J=.5#8jf^a,d#3V/0>mMg/+_rH>I<&1x/O4{<N{F9af39j#dx7A[N]m&_1sp}Zw;C6QX%szG,OC,UrrB./NgI%}bPr+HJO06R/1E%k}Q8/`&1YSUSM[(js7X#<aMNy1Y4s,{;PLFR7b8QXI;24]/2x/Xqy+J<ioj9[`y{7lSwNu?fG#v"9[dMOrVOW2K?AP>QQJd5I357XTlGJ.u7W"Fh53wS:(T1Ef,acpYz_vDGA5_+B5BRu*krnZ|xd$FjyN%CAgIM)Bi/)yCRF?Eg}XRhF6LkUkYf/%5}w4LB7[Q>/+O#vrZ`A08^v"Fa1gi]v^:};~l&KPX{dFto4_cLHjv3w2L&}RFgup6wEaMrI3XpnYx&SjF{thF+I:7ZF1c7"kg!CqX&rEQRZe&|e]C8ufLBfK1tARJm"N?HUhF1LgzxB()2_s82O]L1PiK6ClF]k3R~F3)"v[Xw4,hBv{8f4ZqUE+4NO$QY@wc?D.):aYA}4E50E~:MH`_+62/K0zBG"6r(KbzBZ&Wyu)u7Lw1v)7ixNjvJ?=MF`ZRB`{9.ub|{Eu{NvAwJ?nOQJHJ"tfcqVRFhRgs{GVXHY0HYLKOVH2@k#6CplJBjt[QzyJ??GVBE)?y1+rBs4sBNt.ohFJ?VXOoJ76[kUO)JA?WOBbc$v{owIlv9caL5tk)ZFb)t@}q0w<[6Y!+=IXvFgiC@yOilB1Wz_nDOC*uG$nD^t2/ydiFddhu{j2O;B.>?7nTwM:chZxNK]zKVt1nFKF?8KvnmngT3DXi=`LHOUhNO2z4=>Rd)z$4r_HzZOwQhF|F(F.)j#2e;vftSPQOOGe"oAuV;Xk#c05tF<gGiyV3ILoExL=Jiy++bS}4}(?>u43ws]QZ*yC<>GUw7Iky7|Z4&Keu<oVR!Cn?PeQiYe3LWoSWR]zK=tZjx?lns8!C,vs]3w5WbLfGXDuOpVaY}ehT1X5WwWz_7Hh)Yv8EZB.KCd&$<P$/#TxNRZCdZ8WqT,YOTXfD4)qLjy@I*HsA7iIR?`JV(j?Z3|Ewi66uoSgHJwR`QTFbl3Ucny!V`?"B{a2QwNehC.zbKO;jmFcqNT5OsBc5z7FdUz[7=7)r0GlM3p2JfVKe$/x9XSQSteA7s3U3yFa2uE&Q?}1gX2?LwHHohG_bx&Q%5UI71ya=[#%FA:*?lsI4Yt&8tOA:&83Q0u/[!3aY7Yod>@qwkRFH+c+fLJj&xQDP_gIHoi=wt};&DcuOs<Et(%X~M8?^"Cypb|__?^qV~%|!==7v^tD/ubC;^Li;oXvk/k"h*e(z#2g0aowI5.CN|&?oUOEIBpAS_Lsj6<;"NL}1X9rjg+wY+BvCA?7pBCMYMYX{meT6I>Hs0I,<m+%jR+cileduq0%jUK`I%F#Wp@FsMO/WiLC/Jd3PI3N/#VG8CeXal!D(`]!0g,GSV16iJYq34+(:TWo5cO?]!i*~Rhb7r7X5D6Ks39^6q<+IrRf;wiq3YNOSw#X=B$RJCL`iBEN2Z}k^.%|1c!tN9x@pj9*)Rl|f5^:iLC4LW&atv~9`pKSL_Q{jL}D:+~8GB~abj+Y8r@f[S;W^G>61f}1*V8Y%gJ`+@=`*s#,ixyVIq7DhkYw@[&|8#sKE5rsf3F)+,U_J22gk`Iz8U{F6,Uf1ykLO3(1DWs1"s2naEV=ag4M(HW3#]IF^%%K?K^zllnpKE9kY`kMPt};&*P9LplWTNs@Bz*2pWthVfU~WW~(97IW,1&]UA5^^V8Zpa#i&Hb_vNTI|mIB)?aL+^F)|~3hk$$;dz.#63VT)Y=k`q9X=ZlJjvdEb=<o&:^O.3;RJ).v.VNy#cCM(}J`Jn!lG8`m<^GwBz00yuNcEn$D<*SZ_t0X:vnTY1*K<tkS&Hs47oG,wzyBl!1)e23:{BppYp~MgIJXR`T:Sf>({!65iqxWQx]S^q}k27F?!V]aIv=6t"*eK;nP|titR~x(L@/LS1avN2,n:O.fTpH}9B#cF:DT<@1E:",jcHf#,R>L$h&D,%,QhmoIvE:,<$[HM021,7&,3*n[g1WG8XWj2MuZ2jFDc=K}>FZ4=<%{}$]:)XIsIJG59==xnxBE:=]cu%U"V3%p9jQZe]!CYXQD/8W$o+?6"OstevoOdZ.Xg_=72Nb9rKPHs5S,y4`>S!v3$|"k!sK5sTn<HRGjfm?CTWcN6Wn0PQ#p)v%W;%@~0#x]!c@=3TZND=pO83~pD5oDW2SV?)$m;O>w9+wYra(|0iV*%c27@tKPKG8.P5_b~^i&_.obiT!~&atip("<#apRCh=R}62JS~KTka}g[DAL(aru7qYw!AVMyvK:028$nPsw1>IZ2In?rS`/*gc![l#:gSW/WwZC;A5v]][.L./?8W8A@0dRlMrgSP.Kem1;biZ_6d#!EffC;w<t_oX_16VGh8H^_axqY@B1u*D^Z8x+wHuFX8XV8~BX#pSl5/YzEbWWmDP.12(`C}/0+e4.Lg4j6:{OPh9_>/ZXsj/gWs<2W&vH?"{VAu.D"M)ZCC]+%##N=kmxuE7G=(j?H=fUy}IUvUv*/SPPDE_8)|^[&yiZ_ABL%MlKsT=P;_$4[q}aeopK/ux`]/Pa,A+.{3^!P*^h["Lio2g+v}wJc:k7CFGfUz="1Nee.c+!*?,0.smS(1yLW?e)2H;h)eTGJry|oG(e`!+w#`h+!hbfd>;n2(&hH2vo8Y0yg:%qNhf$/uS.J$`:lTSKK7ivjZgh`E{2X+kBfh2`1x(mk+bFjNwiaczfVl#Mkg^"WIe"vN/1PYF[OmeFPPE?NhZ.@/ZoOio2gJD3=be)ff3Fp<8qK:y`5;<KwH+0O/wRB4nhucTzqwBlT~vC!&MJ3tK#(_.K}]^AYZ4ah)4BVd,"90*#Zz.<]A$4U%}1C`9Ja"^vFPu<kT9(jFwCmD><EBYZ41gf3>&.HJ$Qfmu(JFYu6p*c+In6t9pb$6^N`07!!m*Eq!Lr9CO]7AphxJ`1j{C2kQgF=SBJ.DM`GPZ<d8$u|nQ,`"Y6;+O{6&$,]&TU~x|q&te!?h."pB&gB4k~?65~_ES^%|mf6lM.PysX<n:sQ+iYFe}2o~K(8Qdq3P=7U=M~9.yi!)djuIv:^5rR[}Sk2fxfV8Qc$#]x(z.jnX.I1O4CC/jn&mgrcTy$THh{Ry@CrskP:/lM.)^COR:sYv7,&T<=3Nb16?S(es7|q49cW>eYcS<(Li|PO{6BWkl73}$&j(bOI@6C/.l3/8C<{S">|*#iP`+?7n4,Mxy$*GVdKDU3+mRYuAUughxYMe7$5#1s=S_cqe^"{x0jucvu||2heRxBxf`8b.KAkxG.eb=uYjum$ruPy+;)vliyBT?W#T_{%sFuW$$9{Un!s+oV#}wyP|e&TjAP&Mn0E[A{lVtJ,JhN0^y*DO4]!_>Nj65KInVpZxCQI`fAO3,0Zltjl_F}RiEY_ie%Pk)~SHvGQ;HNV%{ow*C0h)W|s!/wVuT{}a$Hq*j:BGaI9Jq:fKW.V5?7>E&G9s$&X)?pf?XDI(UwFg4s#uQo4:e.=D{fZRXgR|sUBe_&Hu~IJt~u,63$T*Hu_Ly+.k_4W0Z.WM~F*"ZV~UXwx"zAG=sdG_HMLalX|FZOoVH{h#>0Bo~xBR~k#7?;2<wwvng5.}[H*exckAjGL5ly<V!7tUzf3?&[^/ZfmUrvU(A~Du$p({Me)P87ax$,pW*EU,/<jif55t&h3rGbAJa0eUn_VMK#9m;1q49N;+n/L5l$shZW"b>ujD,|%1NVE}>f<DUPGUnT4DUPG"~4wnYO(7ID:/{EZjrhaV;e}#M!LL]m1A:i^uz4wd{KLLUr`DqeMHrm&/relluy{kZs>Z3kO,MwVc+Dy++.C;a,):gv>N(<H~@zU"5?M?&E])Zk@v00O&07IT:%!SX`|,2Lz"uH`HouM!!SXA2_QYadBO0*?8YFpR^/y;f4|`PTZ0e]|zy2_E8`NvGZf*@9?9@/hd@t@V/ApLU)6vsJe(91f=_Nr`+#ZKe(9.ie*e,ETF</N3M%RTvc7JcSawyPPL>`P4aX;H;3pc(:Mhnc;Y/C3MwNe}sV*@oq#[qc^_F%x%hJ!aO8jf@b{Qcuk[EM,fcpI}qJuQ>eV}_iYbqnqc>%8gmYOby@fLnah}e1q6Ud[Py&2X;73,wTZuk>4(T7*[NSedaD:1P^Z@Ya{Hrq^+#|q1mXoZxLdUWw9DwoUW*fvnzv}qgrG;/[$iXv=_FnGY|FZg]Ao(ufzk0g$Hqx5T.NLg9ywGG+BNmhcO+Ly$:n"/%@MP,&mF03./X1<U.7m?/U(_BcKj}+YS_|x5.uhDZkqd[d*!vFQ}R~ULiGN%5ym_D5^Upi~jFP`dnQVrv>zDVR:.YF$p!{WG)8"sh$$:+>~V#$BsCo*/JN2Bl!%q860o,x^D,w{#?5$4|<U1aX5eU<g#.>;T@^=F#%=@;2l3Kreu*BT{H_Dbga1*{oQj#9VZfm<CV8Pb(n1(F"tGg:K}1%R,PQI;@f2uxG&o|GSwHQ9?pHs#Yc[LWwUb#oez6nY/6JLJZl|_r%>^Kt:!uDy!4FPNbmDH3;=R/FWRb|rk5Tt&**Bn~n%.@2`g,4^xmKr:29^O8_0}CO1q>yK@pS(,kx<O7pU*G8GEapK/Z`%1S*l*1:AYY<W`C8;+v["Z;Nlhc=a$Bc$o,N>C,4H!^r"&msh6.`HaW9oD&;g>SSf?g.={H/g.[H<3kSr^MwxF$*7:`}u*Rkh|U8ileAiS^3z>,_+M8<*0r>FR;sHn%<F`o5?UfRF!@}R6c%r3eAI6cN]JpJ2GF9eH`{O9pP)>p8nA>f9A?d;k"$AR[Z531K7,!qfFh`1:6S/25`/_0x8i4Id4=M6^3wXDna_8<M?tq223pdB{*2sJ7zj7!o!<)<rh]FhU_*Bm`L!lg[d0,)kKeUIYQe|13|jKwjUX31K!<!;PIt~iUg^`C/^9v{C>m[$J5&AcFu>C7V8jh[Z4378Yd(kJkYohNd31:l)m!Y&JKtx_1WEPE9?*:C22/YQbD2lR,sYv>&W)?~tnjD,||x3@RB.A2t,>yUIHEY.14J/pLf#|f5U09tNGQ4@^ADa!jt3r#v9ePC3];|62NN|L>9>Sa9=/_em:fjk+VcLjh8DB*k8DwLCF9rbr7`*9{lBH4Fep*CfEYY3=&yHd^pUr_?Xx"x?tzUfgZFMK{g&Ik17{5U$Ikb5(.b.I6*w(8pyOS%|w&H0OxNT6RwSY/o,[{%`x|%_Xu,tE8p8z9l$|MY+"|Ph^8bNap!s[85^U3]ke[d;CPFP|VA25^*9&)U1+e"nm:aLCp_>&WDlA?BpyUuOd`G6#i7&=xOjobb&I6L@20Gb*q,9J^JVEN.IDeG&u=A%qwJ2,pn7nC#PR])Qp}Qcma`wo=+2Dl!lkZ%8%ncF6f%>vh(WiNbRDXdg+C/)*B|k5ucFS]:V`*$l9M%kE>L:>$:RHOgQSM[;G|C(GH!bhmyXnEm`L@CzXc1,xy<(DLF%*`_VB[2?=]VVm0];^CGK<ou3THg$wvJ%j3}o(r/Z)6F/%|@vHv}aVhas`dC5o}~Byn^G<XWsd)pp6E4l7O]kj!R~^8Vv:B7;S~wXR~^!d%Fl@FnQ1o(U9Z8v0*u`=$BP;@P&Zpxa*vdQbQ3oEH85Yd?39VcLDzs!sK~{[29K(|WmD>[:6#m<|sG8wUB_cuc,MpnlK6bm~W<$_=pnZ|LX^hyN|+*m#9a?_!WpDspq6T3q&0#1s48v`{a9+oKks?$^9*{R1kD7*!*B`UDW?+g;uI&Rh.mq8@^V|Kyel}7*McuyMU5[gewz:^Xf/@2.MR)m[ov0M%H89[m}/7"myXf7DN=!B;X6[G]!1gQU?nKFK+NIwHux$wB9hhoKtxh9W6G/0?PP^~8o0uk:DHVm)T?TG}Zn=wayh!1,{7Po35{+#BF`f#I?<mQk@HWK).[Tpwaw&7|Z|H?b8b3?`7?tW8qYT`TfAovyNH>po6nn>p6@nFaX]^l_]|gzYf=@SX"I=8l`ujP2nF<siFf(P%`t&Oik76,O~6=|";1wiK4kMLDzqGKe:k83beGR7nk`@W*.IpqOUD]FL;B4v3=(z/nQg[#00}|ZJ21#V)f5F$p;Y8s*=}=F.PG4W"E6_7s_aQh&.HT%c2wPmoWim_b,z{&yvDb{[DKOL;XW;KBgEPB!gw^K?Dm%vHgjq4!|oOUD]F}U%aW$ftj1|$RGSVgf6Zcp;9&3+sFrAihwG?bdpi1]$$:^sQ(MudX:jjY5a?P;kJ=[%3a6RjSkN)@HvP}i+Fs}7.J@`73O$:tbwf=d&DJLh$}+{wD4_p6@I:q=^STu&7{wH+kN&^C/w~hGQ!1,rK}o8n}3:.@x"A9}sZ7)aQZ]n~IYcek!.=~*E(Oi{UQ%~O&f!hr37t&7=v8jMLw+y]^^l_`64eHV:!2eK8hX39W6IKmf]abPt2hXEAI=<v0`lTmM!1A%t*EAfd>Eq=;mJn2<d@VA[r)1VE6Qd!W>R[lTAn2oabWvRk"XKq%22MhlD"!,?[V#6Sb6_T3gzO>$a2~c4iZt~r<)f)qp&_">)=[gn=7>rE05GhtTDIMxTyg[E@Kg![X=Q#@_N[}uE^Ofz>+#Kv*2!,H`F}d22ipKeBuWa]v43%?i5=(C+ucWA$1,%Pw+,j<d`hrUXJGuYa#?;oY!Vt".M.)>BuC|_<`,[,J;B,%x%%lXI@7l;i3R6/>,lD]CruyvKDT&Q$nq:1=Gs~g[aLaI8G13b4xJ6@_GHBXDg`TXfhUlKh]]8j`cLVorho~KM?k?k{fB"?Jif3A).KdV=y(&GI%0dbFhvbhvzpm?=f,n?gmFQ0(g?=G}at~1:.sOn%;sU/&Bm8]q_B(PJ7nP3[:jpx{_)Ix6L&?BAOlKg$VwIyrGDe5BeUF52feyv=rp+c9]+2WiA`dNA`)b+^9&3TEL{SealbRK4T}vn=s>W6NgJNYol2na9.wm~$1b=25Bxlo!EDhXn=1A4]w!VK*)5;s3cJs8,T;]"UWY%InYJwBZ_/HmSqnqPOOB7as{_FR?Ly7#o4xS~t0taQW/*os1QVa14Ksqm5d41Vi?6xhSAg^H*iA^zrJYxIc@lt3*8wmT.tn&[rdt%?K[L^{G<Zk!cFPEPwxlMI}_p^?XM:AMRI:;ZLzS5/HF6qYc&0%tPq+a99V3M1fKpoINtOE;gP+.+mZf^b=0!`A^vpi3*<iif)4+X0HFvUE;mv9o>8${|+7]8WOYN6"5vZ!%HiDE+TR)G^P(+].Lu?PZV3^AZ*eVl$3]2Q4tDh<!/ZtZ8P*EnD2|&F`2[4)]24m>fr!ndi8ndv+P?0X2EOD^ap.N7O~+Yl>J<47@$E7;z<5NU57o?!:Sy+5^,vC@hb~=@;WZQx(;xoCXeKQNmunxAZD:,1|+O!H<Wdy|H=.%L$x!jR!Vg368w1,]@3E4tnU<^M?<RS$|;l^S"fPrF:Jjd0HF01PySmvq_u=GURKqzi<^!0QFs%Z3vo;:!lNd}Sg25$F/#3np?p")Km*FTbcZ%7h7`9[Py2~+ihEoz*?iZ3G85a7alwX,`3d8.PsE7ff%lCFPhkP+P;xh+DfR{BEdb%ty73@W?iO/*JAksh>b}hE^)^sNFSlJm2nfnd`.ym1#8R1I"Y8$dey8@r)IQsn2d0{BN%VPt!pW_)rBC.xI[{U+quKRz">%3T<<~`~pal3W7g/a^G]Z]m=JR@HPb0J*AVc90FI]bAtvxbr?pF:Sr9|x!(Tv`JTL,;]0fxk4WXazv7,<[@RG`,aOxyFU_Tg.aSn:f<85,)[is=E4A);`v42qpzXapL+EAg6YeU8cJ#rw2.a/fTw["E;RmjBI<m,O.9UF~[=5*3E6|!d$M1ol7Ot/<G@C"1p`GEo67Q,e~Zpud/k{=1Zr]OAe=hi#fhnhmYXZxO$<298z9Qrx6O#(SU;iBw2)62qaHH_L#aqy_Z&F`@M$pwYT~aFG>0RMA_pk62`a_WIK!.as_rE@O/_DV>`w|pF&|USfg"tTEFzPoKS89gt>Sh,gY{2p1<)#euEq!yLPsJ+^$_z@>3{UBC2=>QW,h0eHw,Q5?3E6CHt#<r*u$6!2{%N5wx<&qY1>1nlK[oh|n#J1wvmT$JX071xp8z_M@]D={!v,WEG=+&(@QU_VYz_@wNUF:FSpH=:&AYkfH=[V,Nnx+*Vz5Jr,!v"N)SE=BhC0fSz/!6U*G[&2iUx:K5QDhzHrmp=@BX/@_ND%D71CUl/t!1ruRCB:pZh]+Nah!b)gx&*p|$7.De5,%3n$v}=0H8UM*#2a=.Kedacx7uo^6**HV7E[Ak]C`yQ%Toz7YPQkQNcEIS$oRtH9N>Hh).4B[/w`2.9t5%Tg=[<mnO<Rx#5dg`;9hXOACR?i&&_o^G@Xb9i{4eOb]g~;8@D%=]W7R{b*vj+51Km,_rgMDg!LA[)qCdolP1SNLF",8d:b3GovNm.#QFp%.;x6vZo=BHwmi%ty]B4rA}G=8Fs}cuC6sJ3/`VbI>*{?/O|~1+)mbYYN`,/)[i8da;xPCd_xH5+KA/yG~@Es"UMpB2b]],|iJe*,@cB5"$osI5[}LhB5iVW=RN#x2v}%w|jjCT!)drBwr`UfD/R8t1vuveCfta912j5o22!>S6OYjft44??i@Xe[[H#aoVi|h<xZK2`dIi^MJLzi2WU*1C3WU*UBH](bV?=nVXeX=08<,?aDf/c2=[Uv*B^*:_@DOU,|L7$t61Ehc#$rBw<iL8#D>_~t_!fA2fY1A!k0NLtpj%du7B4K*F&_Hh;%@Q&LyQRs[#n+QGC&f#=&9YV[fk[Q/x:h23RC&`.SoyQ3Ai:w)MR+UYxVdIAD7>owsoH=40SZC*[RqE%)ZyfQ9J>}N"Zbeh%iY!&e@!.MK8A;R5EzR*,?geP,<bH{Pf?d;)i(]dboSN6I!Pv+p*}:3G>x4ZI#@q4OR}xRqE%)Rz6ag{GBMTtee4Gf/o!lP0/o/_i}Y3n^Obyveva^Xp%3m=99OJCv|$jjg.#dc[8!bvk$MOF0>+@Hn=_.eU|IF22$CS0ad"Gi]@%|&FYuznaT4LquzLl=dbNx|jyu|CO*7TwI6LTj7aw#uJgiwp%6iXJdyVXWU?Cx,6shrxFhJ3,6ujz4SUHH7tEFa*)WY10pc2E=VP3d(^hh^^LW(UlO?!x3I=&m:{xZ@hUw>CfM+5YhU6pav};&kzAGwqMa0w>auZMaGa8Ph@#&22y/Yn]/h(DYN~wG0sfaNde<U(EN0@:bg<FgbpDMUFpK.p&eD=lcW#MZ9k/c!<PsDB;KZn|=DY`Nno:rC}roRa%b^TvU1_Mk6dB5IjGzX#Hw`/:4PaYVTa`+;UGMxbNVvfMiZCf:EqOY~.b5?01/!<)@kTQ3Ez0v7P5s|bEQ"41Q7C%wM0G`*tC#BkaM@W"j=:u!ZOb.XTfi/wTSe.@0r,baf6|@7t_IT&zT)7x;5mvmA(j(XV}0fN%Ptew^0P",aBHe}acr[dDv25a{A,@&G:~zMtC}4;gYzqI?PPe%=XHDY^D,coQrSovkz+9o?HDAo#,"t9x+IxPQ%9Dk]!HFdNq{K|Ps`?%jOzGf++qX(N)oW0)nSet8Y(_n{9D~aCTbxK"e<*ne}e<*_HqIyzg[XH=ic@Z[}#_rF@En$1QS@9F<;U}b?C^xt>R5|]2[7GzfwB_m;_iXMkypw/Z*h|ugNb7Yecc[XaC;}m%GJOkFt=1rArr64qdaeUB%INlKnQLSF@/:+daw]J<z?WZNN+,kReKe21bY+!R8k*]p?1=tvVk9,zPl57&Rd0ojWkLlx2=ZOf9B=_v|Z9}+8Lc>c&S?au^.*lVhHgVsTm7eFGvb.A]onr76lBW,1}Hcb_|%[k=5I?NlRwp;<_9&w/=>&6iB#dB_tTy>e7}{E0nq&|3LY}_j#LDVd=/K&K0DYB2Xi;v=}KwCl(aQG0nKnER!q7XKOgrRoRQ5"+s]=jxyN(J+iF~J%nn:0&ftg3C%P<b@dp(YL|.U=f$,De|MS|lfZ)to@mGfo>amM0:2>;K`#KKLt{"z}T)ir8n[<!``8>s&P$n:c(^G}{%0s;DQ0:0r?Y*_XF]:+R!LY071lNi3G@~@T/+o4[6h0_M&bp{|u+8$=CR_MZHmx4nSv*:<+/;?RxHi;bu_6vrRTNF!~hWooROo|_6*$Ih.W.dRLzHho9ARK4dkT,%+I/9y})`tbqb,HEgPGvPGdzOv!*YQZr@0LaZ+gH!{uXLWKK6~)8%UdVYlx2J2>^V8a1h0aIbKa]v*I)I)=a;t7AEVl5Uz0ucxZ1H6#PUXi;k(AIr{!LJ5#Q&L%Rr+]:/P?0_7eUZ`$c8vHF(Y8)J>/2`z#@accH!WE1miQllBj`]:3Q]]@200tC0xW)a0SnCr;W6^1HVrJqt{[HGK?go9&;?]npQ#1xwH.K./tiIchkR;Ne/A(@FP*;s,O2F0)^{UgE7|@mwNx>@XB,*+)gglrIGP8,C.qY}x)lt)imo|JEVfCV?bod."U0leAu_|@K./VqU82}|p+!0gOg{RW#!pNO)Jd!AX)Cc"]K%dgF$d3kt+IOUt#V!C8gzhq_k4Oq:Wyz8pJz3G/Us#_%PaR3t#=/Ye]tJXLu.q>TPu<{mevK_pF,{E:FLuDJxDP[gHP[kkw@=X??4EU!Z)r3XMt==WKiU7ScKK%R]Gl0v6MUgx^MN*ph1*q9]UD6H23p}[Qx$~68,a:U,<m4+lAs)`f8Z$"!_]%jG__!]aXnMN0#g}X.GQ$Lne|B;Uw2Lll)i;mQ?00}*#3?Z<tu]`BE.xX)VG+E%Wp:2.Dd4=sD_dp4nMXv,(8b9*U&@H7^]CFM|!GlBuM~t`Wk;w&8,@sOAM<xReoHo}oK1H$9+MwlHohx]=`=Kj]Cd<wKB+g,2IE?}(kK^@*q4#f3RS@pdox0@]#`+Bf,|^"|5HRSTs[z1eqdFL~$led;T?;%(R:T6*+*7YiT9&Og;s3gbk3lTcUh.;S(j8D6maH1o,wmT~Zf9&Xke,nf+*c!~&+M}=_vvrrefOH6dN=+])DLU[Kho9)FW)r6z6(+E.:^bD{{fxo9qqupNt=Pg@xu]9~^)O2eVn?=uL*ea$3TTO,c$+8b;bOLWX^vv2Rw,U)_vvZ+sf%P}pP4WV4tvtnh0x]1BHZddz4m^V5S!?4RRC2WfWh18``|%=p::|m{$Q~C:]xW$?sy~r!;Mcl`qzoTOp4e;_CfC,>8^sLp*Gwt`3,6Oa^61>Rc<wg=B8C88t"F@XQ|wB(k7:/X*tGLCIWFW%./d7x:^U8`lo:$TXl/RGQ4yOOM}(jq?vKzVRvAh3tPcpA!?vNEC&L5:y??"qE{g+=Cda[t:oBu(t~ErU~;msr_lGiBYJb)abFQCKK`aXe68D@Kedd~V>rSdJOQ$z0#QNu4>iL("fF,SPN(0JFY<dEk*WT_p1,r3kmJi6c;R/08h43&p2r6UPG.u<bSxXauUuSuljf*t>kLwVWtk4hc>@2N(YaUydpfa16^A_Y0y@?0:5qdbp64p0iLM|WucH9m~m|r2r~1wL|`aunB6P5BSPq"LVBV?1akTa?2I3p*Se"2K7fb<v1q<i6+7IM1>V/DpB:tZvqsBZvU%AX/^$]6|(k&L.gT8LXZ+)cz#nHI_[{{!*)_FHIEEN7.!(Hp^Kmyxa9eASAmy2@cN96ObyRM%(qFiiSq0^qwBM~97wNe8$I2GaHjnq=_I:79*vnzfro_.;w<+nuMbF7M5>:MM^G(l}TbZ;tD2vi6VMg^m!vN5Yx2nC5l5xd5/s>rmz]]y]1n^9RP]Uydm?Qll!FSlJ7:LyISfUjzk}tZT=<RhK}KY$gOita}SIj?b1h6JPlJ713Luq{Z+DTOc#$h31;3x|+Cz)YZ7h&tVQj$&jsrG7VeFd[)RRv7:EWE]5U:G0yBon>hmlMip`OOVZ~q&FR)0el6{EATt8_,roOO["lw/=%s}zu|S1!kKO9Y`hX;fvBy4@U2orv:#SV7h4"+/*|z(IGH[M;ZgcZ/0.}2bK&$l5uO{h@cjS&I&d(*oq(tm}X7_Ctc5ajq#DS#x9#vL_aIHV]RBv2nV=vqSd=iRJg`L3QfkTi_Xc$8:GPUmqkT+Iv4UanBO#vvRl&y"GlJC];`O1HM`NVG+yn%J4R%i8bMW_@raEy^aFb;#fG.&UYqmBl%^/XBg,7Nj`y0~0)J<&pr76w<34^0_rv<}a{j)?f?"U^7SGd{}O:B>YXJ`c>x.;{d*DTb%h6@kc_q,+sL2"lwh&dfkT|B*"`vRQpagax[oH=V4]z9B`y)e$fo!%gq9gG1;dx="mJ&`f9urdZz}Ka8K:Q:#nw9,{k$n5ty@ec`t8YHE[Hc|C3gw[7OQQOf<@17.t0QJXQ%sQ+A8*OR43b,z{^c=*r=3(>#(O7+?>M;My_u#rG,+O/p~KZ%3sF[lrt0k^v*Vn+=A}.&{kFwl(k^.]SF0}(O][g{vf9Wx;8Wu)^n.ap4ubC/#p7$gt_m.=`9ScWYfx5laC^n:5v$K;?A0d!Yw#2%]7]`dm[!3%vHYz6^*dKCTby!ht^x|O^?PhM=i1;el$]51:}/n%LF~D7"XPrE|Sfu^N8i<4eXE;7IADYPEpTk|u:^dCz#1,pu_+F>Bx[%{^@]&mf)4(gO&FTbsg64v4b3gtIX^9:_(A/G4BVa0ns3kwXgRbBh2cz8T&QtlwAFTbJ/|r]XIOS#4oUd>t~vtgy}0AZ8K3Tq~g5leMRS9GW8&g79j&C%x~@@T%n*]U&.m!V5plf)_bonNsr_17R.?@]_7y!9?CM1`WrD+0K/PZXsa#N!E>L$4oEPx^ztdf9u$q4ojKV&pZClx[b{3%+|q}:Ct$Ngd%1~!qHtK^9ueUk4u.ypY;Jt(%aegTEE/6CI#a~,>C^!Ptl[*#P8%adYY;~`(TkovZ5a_rZ``9]c``dFYcPGqx|O<"[D?}CZ/8fI+vF^#08/&tUzeNbl:4)_U&c]*|R>C:B:bleR/jukI}4<M{a+1>juQ=h4#H[6hyUTZ^y7nSECI,K)s8Qbd@Wd<d;d7Ji43&uDm`80xH:dvLlJ25&4%(/((n9ndmiY{+&D]wKh.sV;gjT:lor7jXgQSZ0e$qw]nkd@(OIb|:LR_Jki~BfysV94wwv5KK^pgg/PRr1)]ZOh`h[0OO#rx5oAb{#B>YhnbXg>#TGxczd{c17wJP,qJK+NP*1gi1PhIv09RdkP<>.HOHd|=O6WN;K^V>6Qle/MSkq<*h/zlJwo,L<UO&Q3(puUt#!yl]V>aQ*uA;t8mxG~9M~XvU2DTFllm]xG(1^@#PiI^IL@X;!!6qBF1DX/bQ#M!{t)B.aQ$PTwF2Et9@pLBow[4]@m#=$z&Pp,6h8q6+K6Y=d72MaP_$|2IJNch4"K@GzpzRh=rB5r1,ki+A*v1)5`SE:%/hmlm}Nz*s@Vn?a?a_aLX%iR$1F3G&[O_[~d155^mGTEr4R6?^np7=[Oa;}o/:/HldGO;,LAV/sG<@PIcN5l6&Lsr_hKp,S^Q+c[JI52l/vqUU9sT_2[i_,V]4K()#bjS=LXWEpN}AnjmsP*1H{Wxw?}v+/ezg2KG3Da+G}u1/;`LPgr(T/+K(}N,sfjda*b*ju~i9Y?w)~^`D_CfL4?S)Dp8o0]mY+e)q_rquq=I=My#kjM@$FqIJNcASK+}hX`i5TDvgNc]a^HaU[RvU4=LN:`1Y6(Qf4}BH#_2t(Zu73[Ko`db~FVu.D+*B.?l`_9+|N{;<~HkX?k+D]Y&;|Sb9@@=x~ud8I8ooTLMy(J0/IfxjPT0s`K3/K*J<(_gTJPn/R!;O,x]X7!BgUg!k3kif#tk<s5UO+U,@3&E1i7Eo]J>?4T1v*/bTwo4yDghSx~>my6eYeS_xHkVCb7/YweJnuG@~F1.%L5g#1UCWk_<REH:sR0aDZlUASafmE)K{?N4k&uZ"jg@}FG.6_x*6W.*sx]UQVc,JA`v}9J_ziVFop>guz~I=zkHMU8LGN*_*rm/REHE4@=U>M1)zaoH0Vm}aOHOQh#fJ=Soi?,qMTk&05w*B+vp(.;a=5VM`#:Y0_s{X~?&c<J^Ef0I!_x}*s^fm8&G8emu<s$Yr1Jd()Dlvb.gu2~P#W1F$8C{}<(&vHlAUDy"jzupr`zM2`U:V$foe0:jx}HjV@C4L77Vdb^}]_=0DKV+cS7Cj;9B&Wrl#_5{t5UEXt*~&fYxCxm3KfksP`Dt]m4mR0{t#^1(G1=5wn!=o_;9a;ZXt"oNrxVz^o9D@9ZEK~6d[SPfDGP1V(Wjz(Ir<N)|TiibAnTw05M%u(s7RVCH5E,m`Md`L}1pq7#z%{BaKeeJC[|X&TTb.Dyd%b1"mc@!n$UpU$fR8<?x_+;HTLG)m)1Le_1NQqeUX.S[?GIf%j8*8ixdbZBB(_0<Br.;qW5eU=qk*Y$%1N/QhL(8W#f1qJK]Px0J8+aGO^UPoZ0A_LO_*?gP3TpOz,ER^*PFSwHX&x*X^L>xIZ.<0gU,.v{=)~y,cgF:XsdN6$n]8_ip9I[Dn<oQK!Bn&G&(.%h5KgOqPMeuff/.Rxk=iw66l2@uOF]a*#_<o$OVc[mW0<Zg6Y>?8Dw*o3R>4`k:mh)x1aYy%!]XFRPbeX*[]ka00ML<33!dNQI>r0!@!n`kMl"j[e1X}{HgAnTBc|$.@z72&J2LKBcWI(uB[W]&OQ39b}@<neLWsM23Pcc>@AWT848487rT<t.a6W_^DSI1ipKZh6smpnhDL>k/k<@|!l{J#t6elAs~ACtpDHe/8<$&bFj=rz:*42.y{0fBSZpgeh4%?"pW*~F^UpZ59K?Lh(;rh;kL6!R/P3aO2tjL6=6~_hYDi]nQjW,`n0v14quffkSf3Lv~As#Y3138o;u,rEN:P[VtL@QM2%ZL1|1aH/*MpH}8w,d*u1R#9ez>/i3Ls8wb|_uBveY;wZfY;;]z*$Q;{t#%;_tYP3Td>_tcIE0OF)&n1ZRd/r{acyv;<c]8LY#SD@@nyzayK)FVRL*QXl^lnE;)FijGX/dc>.,rYwDiVF[&GfjLP@QHDGVqG2sn#=tY+&wdU1SHa+B)rPz|a?,F/%*phlnxgC;gz3]!X$J%PiSU!I%@Vdik`EVkW)8Xu8iH!Wz_oBOKsb{qe+%[;e{Gb$p<?W.pkr2J+"MNWG<vfY("k^O8k!a87lO0,<S&xCJZ82<J2of&_q9p,`W=.U&#bH8Z2J}2sT+S&sQFGaK&s~gC:lfw>mFG"*cTZl`*MA:k&S1#HKYzE3{@qE}n36`g,#6+^ty>B?{XdT)LSdoV)Nke.~b.d,noe*^LWS%#+ZTUF)j:%ez]*y;&#l%,+6xo%vf*[#_Di7)GehvO+Rk~gefTx#hP3|vYRC|&1Z^tQB*n,%j?!QUfqN8R]IPHs(/Qv?!SW5&puLj,9c!HJ?gGLHk~g&?x<+S7@imxP(z}n9*6)XlEr^s{iPj,1=j_ih3qKs]6^fG)qKmomHYkyzy_@G~_cTZt%}efst2m]mGXSN`m[#{p3C9(l1Z|tbe~7fjvCI+:K1)/(xRB]"dgao62@qL_gnVLW;MA:}{CVraW8p!7@`X0Fh&Ah[%n2)yG4jkB}@W19$Dad&MOk(P}%@y9j8qd.`W"GCm%J2L//jW4geahe</hjfP1)5(wI#iPiaa7u|Of`]Q6@1Q2qnex$2^QU%YQ3KK|2hp5<"5(+|kX2?/;k_Tq0l2%Z`knZnXiIwz6Owz)Qa66Mp{XNTajYdHB(&yLTPcbMLak*leG5oeYbpHT]"oX1}H]<%jeg_7@dp6}:>Y}@(qcQarSWHf*:c`^yV1>b0}GV&lQf[iBe!8~t.@ry(/d;`t.@H&go2g^OiSJ<f_9].<jKb3+d%jz+w7VvD27tX<)fxksxl$nxc.=RUq4Vq0J:KV;!9R:@<dVMM,&~p0jGCirSWQPQ#,?p"e%bq}{o`<w@~b!0z"M)co"N4D~xNe0P~7l`;"<vGn>V%Y?Tk8AX>rO~|x/_9^gL@a,n(N;+kj~=88=5y*zeF$fm%i:z1b)kt{KTVr.:0B@rx[;.DuRhte8l*^X@efNo5^l:q$fm>tviZf|u{&*wAyTf*;))/I,8#)/8(el`VsVT%ge$N1mbB}p5PESs:<r!No)&Iy4:5HX@5b:[J$"],m};r>{/e%v+=a!eYS<7(O4GwWw[PHhs$9+POYQU=aOY,=w+0$?z.}[T"%@xBW/@4~2X~%XsKK0hX:{tgiFck!5RtQN+?<QC%@ji4GLqFAGKKbNY4Rr+wM1kC(z.zd|yIT5k?kKemktk[O^a4RBIOZL!2G(!i2C0l]fmE)cY#hg217nmR]%|w0?4(*(,61O,p6/??@98}N4)9}~G6qaNT1mA]q!*wInMs6`[NeI8TtjKi$~5LN.i@`K45fuB/UzD;Gi3r>awbH/iY%$Uwg>:Jry*6nx5;IdYl0RhM)upT:p*287W$$!GXBZuU(`Lt*QF_c?fY8?Z94@3Leb$E+O=0mA%6J#I?EJWgEwp*l1x1Ar=~NPmpg`GEP#.z:eiXy(:ouoPmR#*%1outVQKWp9k:HQb);44sbhBFXH?|apaEF=mbKjNg&*BLeR=$P!Yt#irGO_RRO`vuvx<Fb43]<gpG`;&~5GgRuM>dEF](t:5x+3k+D))edvG==yUwx04"o*r4&"^meTc8m!/r>RnWtrMhqt)Np_><rIF}_LhV7Lw<>ug:,Ie8aCclSIye!!fa{}D<X">qgQ_Jz9%$GkCyG%xHieY34u24[QxYoDeZV?(%CyaO[ORJaQ#|H:6K6mjWjHa}F)7uFDeqkZ]``/bAKJZ+aX$*:lQ+x/{x_w!d00(cQ5bj4{HG+ngE0Y9H,$g&P?$6@YVLGO^Q+$a=am4bc5#U8/|)0q}~?&uGk&!a/jM]625BuG]ud4iPuVX|^RnE44dHn}5zr;DkYb/c~<h,F7u)$(0>OE]6~fNUYSacO*PK8~J#/_qsP1lLR;0@@`[@=l>,cD&nR(n40X,Do_4P+_f/4|[^H`Upa(^Asp=tUm(|4cPUU$4g|fqQVIHq7y:o9E<1~84dVf/Lg]!/5#Sb,jF3gS<_9<WK=.2@S>uGmJ.OImN_4GD^923syjzP(et8>",#WlQb$8oE*=iSCJj2WncAS(UQ|AlE*_d>et}`;LW.,zqz/GBhR[4[u^!XXFw>7(vmXJ{?T"&[k[MR"yH@Ju>>X[uW3C8/avZ<5!zsGe;rz}pN=E]]e{7_IE30]7?`goG`wFHMFhs)<OH{dGeb*n}XaX|7Q|Cib=uYz>R4r)2yEE+jble]t7"!7wdxc,v$&RV?BMNIG$dLC>gGw>7}oJNwE&Pm^OKwCJK[NXeR?yAW6wjUqnt0pQiV|~w#<+`_0/$C#d]Gen/[H>|U$^zzBQ$x5KL*"p%Vn&e>)XJrN8v*GIgWq22{/5mV/_DGZdZpyQj]!!anDKK^N=Q?b>hug@4RUl*r&%|.#X=~w%E?N#f5BTL#<rj<t.X0c1JC}WR.E)jID8}Ty}>O5>H0Df:~LesD#Sz2R?VHX.%y9l8V!^]<&8{9OAJ9|$I6CkJGAJNi8CA2sG!uJ>4,L2]}UrN8[d6rEZEgT}Bs?_ft$ZO.atK81/W~Kgc6a#tqoZ`]pDi/Frd/*Ihw&OcW+[6DkVj#NA#)9wTzRYD>11Q||uA+ukBHOEBE0JywLquUqc8ev8U+w2gG6=%@lc@@rtC@,vul,m/xPty4x*c4s^Fxv!q9Kh2p)Np[(?Xx4ShnfCG6NLpR5o~ev$PCz&P.:$#;_]sp^g;#<Fq4aA+8TTR0c]Lg6YP@x#WyQ=L4v:*1)A.|&5431di9(oc,%T;E:!q}K_fb/*R/x02~ol=Z=CTt3n>1l{U3u^R$m<@GD#orgyN|(|*_I:*lR#*qqOpfO0LV,FR5;FQbD}[8gxx=Y!N#8bctupw3%FFi|A8]OfRp@)%q;TR)p37vz({Bh(~qoEKkGTD@!39NC~Et0wKYutKf*ae!tT]),J^g"],]@1?=DhC{A8r7#Tcx^D&Ml7&6M/obdfikWRAlgNvo}S0;r=M.,/4xj@*Gco}xHGy3vHoeUDd?RkHQF0BIEN?>@i6%YZ49{TpK1RN/p8DXj)P`a]LK+L5x>}8ZVI}:V|B.?ciiEyZQxEYKH7eft<[v/PTx0gYncPT#bWKjv;V*D9YBnIBpT#HC.zUa]c:G8sBplA*/bCy?L<Q_EVmS~2:<$8Fb3#*vxR$;h1|a*x+0$:Wd$AUoJgw"uv~MMsw@.XBP];jK^7tR!J<GQt6gDBJT/Q3i%Y%oG#FIl]pyA[I,rK+B@x|81[Qhe04^N$?k0>"AW+~Z@1vr3:6e;)n(8+X+R2K>"6.p1&VsWJM>r<_BsR``d*nicJ;i[I=0<aE>HuPza~EC}~OacYhj*E,C@em$j47p;b{.^dL%.]"Nb16b:5fU:iaJO"v6*ND|gW*%_^r`GrVCx[%Q[8>J9z52OUdKH<*..8/T47xWRugRy;Y1c$8X1:)V67i^M1VlaK7o:We<1h%`U_`ZPqo!ihr)5smg{2pl]h1vf#w{5Gx!1W%c$DO]@Lzmo$VTp7X/4Y_h6s*AOd6<^hjyB(%nj]wze/w*2bcEQJ],#Q}/%X3F]X3]/dkx@ZjuQP.25hrY.>jR#&#;=ORCfyeC=sK^R9kFj"m;Rvg!p:~OB,:+XEdMSDI_*6c*z?!;0`,3>$PJ,cEg%k=~n4q2p/rIx!jH.qY0P&<")ai>}=P=]1f]&n{,)0m+PQh/;yfI&G61pwz#IE+/O?,FSB[G^P<}cvfbk{Hja_(]]{kRW|*D=gIh$A!fyj!S{R<0IClwO+,/4&;h9gx6$H$,5MwpYq9[_t,QfSwg<qI6)/:j&pLzrD~;*O8[8W8)il,kf)g;_y?&j3+188Q&8R^B+B.u6p)hXLe(?vmF0B>F?aFGrQ6pL]{^p[kZ8<30]%+8b1FI^@3;!yQ`&~U.3JdO5&P.,%+>aAe.NM*SUsF)cjOvx3<wU9!6kp||ZoJC2)k(ITc!wYCmdl}/`d$5pvjKhc|3$Uj<x$Ox?Rd]Lqu/F3lWh:k~3wrdkwa7w0UyRcxxcEFbIY7%s*cvVHcv?K4r`k9kgC0kqZgwKy/{0(@N`y$Fpt,EdXR2D"%NpK3yi$:q`>rJa@@tt0fPNp`3g;_Uq}jPM0KvOXOSU3p2QAGEF*:hI&Se#dkUf7z%YWQZiXWWQ0xDGo~+0vd`zB:g7.[TW1YUU7iH1XY0MnaQM9#xR0/dkOx@o7V<fGS22D&^roOF,YO>.moRfbxy/D5TfT<Z/O.&^)oH9^x_6>YDn?#1XZ$&lb..&okh!Brr=:*Rz>@dfxgd;r^A^3vuK0!%~sfrmHcv[jixf^c,]^!weW_ZHETy3@6y4j:gCd@eU9U,gpxwH2P%?g5?#?4Y%N2}4aJ`Q{8"Yy^C61#<HIJLs5{th/{Z,pjz4cVM3_>G&j1ppD{z[PG0XA%sQ*Qw&Jd/4Sfn78Z?Cp|Qanj7"NR^cY";$=dbrllwIA+%0@mhur2V`$TcKT2r2u5VKGFwDX/lfV>Ly0u6kX6(<,+gC>FMZrUYGv_|V7ldk+Rwmk=j[>CBnF{}i{*lpL^U<sQrnQ^hcY(K2JD9:X3"4JwmAlBD?EADXLCiG+MNf2"6i:%tF@A+u5]pD$yIkBo5D%I"LiAAAAAAAAAAADP"Cgeb9))C8*vVji_+0*:$W~,0Nu`yJ"Qz`I)n?ICEj&rI~bNuqcK{(@$+t"[PeWOP$$f]&hSyW]6QC1X[iZn:`m6fOEiiLH5ww;=&[Urc%f#9dVNW.C6)W^=>cs<Lr!P@LY]u*NMf<b0U_]D%hc#$^bk:GtB#Tg5y,VV3AR`c>`TTUPieQ>+Tv%T;UN#&^x#[KqI?`y{N)VNk#f|PkTU6B48sl5kH|vh{OxbOnH,[spmy]JjOYF{}.BvVs5W<#ksBNCqE:H]q(aRkvMXnF[|h?F)rWFP^MrqrB{M;^d6V7jU2lQVh)Z;16w90~4}w5zJ5YnTwucS>>&4sC@ixmnl.hU79W>T_GSWy*XB~QO1kx*kwy`YWNDn@qd5{Gkuw(]Xf_KaynoVGWFyVnpt*J=?IOq1wIECe.J,nXUdI*~7)Uu1m:QB`B$R(z9hz"ux_+;ZPC!=MC?%VkXn/[u7SlmO>7fNftX72q6!t^%/_I*4z|7Qhe4=%1]1;O@:%*+0;:)<%#$V%wVJ!d(Z{8Am,qL/MX#l1D"q?tY=RPKkx71I>ZUz=C6g}I2dz_S0:ZlXQkFsiW=`C,1u;[J{LL=!;zSgot.;n6ZC*_q,W%CCBVR^kYk2.*2X9wig!VQ&@a+<]yro)~dr)thXdi@lPP~}g^kdf~@B_?;i$jy&:+WT=/VG=Ba9a<Im;#9p]$`99,Wu,[L{#BEW6,zK9N6*bM8YI^xnFoLA~:"a=sA{/27,wUiE*AQY>dux{zm42vjd4d+dY>Mm6F;!}xOU`k9VA2~mfxfH|mS_c3knIlb+TT3K8J`.60S]MtIMBR2c2zX78dXbLr_K7{Kl#fkriIU7KZF`+kt/Xqnj+QeEc;98]U#K!(j/M!TYq3MLE.g|N,]xy8nSG5]!]Lx!mWYuA<=#{U9+oR=kd6+dF_Yl8!b7JpU!(5lE:eC!~g7lh4E/kipU4qx%xkUybDq#L}V$]5m@2*Fi7|rXH9?Y;Ce*}Dfj%)=`5m&&2d|Sk1G0{"~Ehs<*"}h[,hO+^[&@]>vC*D@I;4wJ`:Y/gB+(Y]^>fN:+FA,|<o*EV2|mL!x#&qJsX)us#g$~Y&Bd@q(kX~jB#``7UI6;{/WjU{_*j9+t0#2d]i=f6JMe&1:H03s:wp7LM`I&+Qd&`E{2c+h(BPgT~J*$~5RPm<R!)6V#}dDh(Wn*1>^dSbIh%f+#rLR$RdnzafN+tL_$,C6Vy2@g3j6SQTsE]8yl?{W/3v|G]0gC>bG7:~}Q{3g).?SsLQH]1Kr|>fj1i5Ddpc&[)^(k)uM]Xl/:nh|S}beUxRve6t%([Ozgb5$)i=N]tjLzO{?nqPl}!*jPm#%s,}3n)p{|;w3_^9U9MhhMm{I@tbeIovNc1m2zT(eCG_KP~>}K4@=$IFHD^8x>Uz~tS4[]SM#wU+raByPogbEJCT1*^orJu`Q<JZIiDFy0g(3#g?edFMLyI~rT{B`>;1~6t<$r`~#<Be8zqC#nLESbb_;eaySqE;@Qb#9m+qNKQ<c+k5^m}?$2PDfpJM:KHuBjOze%k0csrX4?"q>WJ^G$C3ZSw4|KitXGeRm&$WT!#}Nu+7L8)R;Y7dsMG/H~Rj|]Ax~urK$&!GQa7B:6w7lw%7y#"mC"uY+PDFku=()@R<9NFRm$RX=T}lz?/o^F:4H|q3Q]`/vx5(GL%zQ"O#CX!s%IIa%~ZNkI){f=t~Ph]xYGJto%0_xh*]/ig00kpeO]u;yq,1Ky`c9C)f3*#Mj(CM"|zNiA@JZy=nZGd.SGQL$N>;.qDuziO6oNN>8W@=GifD:5p2I2pNZS"/8=30RBG=f.JM_I^0p=n0co+y*wlFNi4W2iKSg1RSn<[|KwK3%/2)^VG{GuY)I7eE_5W]8yfn@QX5ms+cTQ{H8p`)8??6>;%<a?`dg<W,SQS6_|vE4CVVbqFJBmfX`[npP6`s<FG}[>r{|E{ZwblLA/K(*qPKQ[Id"!|=iEU9^2Csp[=2E8ba8.HB%H]37_;Q[:390}vg9isv}OjpRkT+~0,H}~4*^Na:}G)xDK9NP*8{)]75i)au?>!cG,ZR0bnGXz@j7~9`L>x5``a/L9I@,)y2L*"q[9r(?m_W&|%VpW2[et(d<$f:AIe~zH[_!~!w,9^N;j#BiI)cLVh=pkRmy)x#[)=Ac@pbkrbBu,jt;,&E02j$03DqroWZ&Hplu>IF3_"ihbItX?a_NoxWKtbkN%gPLZg(0L%t#%xz+u9Ci.5WJ+A/OPby$3NHjvbXYKr.P&>tZ]RB<&3tPe~,1jZ04x>l6vU+cEC+!O3f*ET79wAyO`iN$$sFam+%[FJ#y"V66E0LNv>dgonmSCbwF8Ggm222|xo~uN27Itfz;[u{gqrvbs7P;7V<bvfY"|HH*S@eelmfdG*=<IlyH:XmPLS)!r{KP/#Zf(ulf7W0[.SEl;QZ|^pDrY=b?=xl#`Re!AB4c]{7Q_X</P!!=|_%ytFgY(&8|CC2%u3h|3Z#0AP=t`Yf,+dUVKgyC`lR_6E~tDoZw<y$tY>_L~J,GV]2U=}N!9Z"7+hG`piSYw,<!uOeZ;qZCXRz;uO90H5#q;;DJZqWN,oB@^hvPgzI|Q>_{n;ytNrV.a4%r~NzCHkW;^ItBVYU%dLP>E;yvF%!Yr4Up~bgq%501aK,8Z^*=Oa]FkCd35CW2D|8;FCbBNiG"Dv`R$?)lFYcw(z|,yw{T6p012<Esu?VHT!IBcJlQ<3&@(wcd}`VAeG.bz044~{:3Vm/%<OK6[z0WBX7Zys?LDq/O/R]lFSJ~@"XGKj%{r/?0D+/~F/OoYWN=Q4P,g6dsjRQs|rcfKo$64D$mg`j}!K2F?H]$F&1g^h<E5{kzXXV<+.o$gTD}AiE,YKy0Kbq#"pJ6Z2_jP[j,i+s~_v+L<Dpe}3^M!"z?O!YE~|Q.RT{?kXDLV#MCi_M?noIgT_I=RbqFb<k0Ve148<%sH:gnwhV1bF9K@i~fgGz~y]zH)ohI,{Ltx&<UyU~HO.I!jf~L]K]b)U4HDG6bua47ZOW)^:|uO}v0kwdq{V$rtx&9g:VS>OFo4!=5OhU8MpSUsf;aryH8KPE]Fy!A1Z^ubTae^E2$&"vtbd%_#9Cq&|~s>m^uT*xcO?V[j+WI"{6$D6jtiv7yu91;itIg4~Hc;+BJ=t57=N"60BB6/LOOrw|!m~?z_>60Mz`cX}Q*~vxP=bHY(S$sw{c[|NDc?DZmL=BP]hT..0<ug5;8@O<HTIzPtO4YNK@uIPD0A[/30=P%:mV%|O`#MDESIrTC3@,GVUE{`dH+#+M#Wn/7:m?y[Tm"##@%tP97*N@BO">;5I:vn/*F.a<beOV8oii}&EJJ.[a*D6%A:41h|,B.OwG<J&~^lZLuk3LpS$X,Q1nJaeyUl"E+h$Zk6E6TFpk?H<<,l0g!Rt!R;d`#t8^@2t5RpmNv@&ddm/KhSyD)9tEukWnPyk)mbe86;hBPe^0I+C$Q%~lUK$B$"[V8>Y)D#0x"Ib]dIOC7k$=jh_ti[qO3}VK[2A@OI+rf^j|U@b4pE%,EuxozKXm&Y;|#1K3,5{0#x%FM2k"LT=vN.7roxe3d$![&x{4P{si]2i.8}f/gCn!M"d4fLPzgOJ?f}*PsB[vxfIzuXfe|@j+3!Ounxv(M7pge~/Kz[&j},52e1@iySLWhOO[X3|JEHC_)$Bv`>APU?|V^]>nYjxm`d?*qHRRvNl0`i5,_`{#9?]?5IBlUDc_Q&bnL@1WrZ[Mn.$u;Kr3jg:VB!ZAa4$Gf@Q8DAhrAHwN@i2ub/&NR:(HGgSRGTY.tpQl=ax._n(tO29FtqXrRq{dg.Teo5(WYts:#=yu~.MuR8?IisK(YM;bP=S5,:z>_4G,qVk{e,FG8**u=9M}7?($V1,|R,l)x;p;U?VDyOZ;$fS(4nCxfm_OZ2T}ehpbxi5o1#?F.dedD)f0ueWVLDE`0?C=x!EGla66u/t&}5[J&aMShwRAR"GzbPC804y.HD2f2`$nxz:uN3rUF42[R;T7TpY5aap{&2p`?3xNk(H}M5*Ko5,4vy+xP_"(3WOIQp|z9kZUXWc6h84iv&8g0dL)S3Je/%9U(4KL"Vx"z?BKhWMfo?F~=>IwmdFK`b~%bQEZfm5rzQ{8oe1Y{o7Q5Xpj,=32<!jXG(ot]r(VO<v`[jH^=`*JzYdnI6JPQNr][Quqwim*C49PDVtE|&~f3okr>*pR,gWpGD%eMAK1/s1I=L!s$NxI:6O9e8jI*#%M}q%JQ(I,2O@m#zRWv1}!.2T*BY=`eW8mQTC8}Lc~jPuCa(n4".PE#~yDjbd=m"P:rowXHF++Blzd6|Bl+$kM}9r]t7.7K!Os@h!Fxgf4/NFdc{+mtW~ESu:)9p54ndp^x7wr3>srE^7_7L?Uh{sC.|9K417J<8VH3;GMx/gLt(w|h}UZ{KbC,$dbgcCH[n;BEPD3Qj9t4zV9jaT>B8@k31TEgFl);#DUqzP7m/qQKVD?*!p@^5gi,VX[$$~G|/VW4k#39k5+>YlZ)+%kk;7(Ydff1pm50Z*,T8wKnfr|stNw1IfJ>a^"E|r2C>nOVlD,3utIwP|mQW8Bmbx*KNdcZXmN31#;R<la2jXaM_/CEtr~Jlz(;^x*(HNX5}a`Xo|B5qB#Ilv[A#Hjj?O2|Y460F%04:=w$AAPlw&4DD)%#c>pn~6z2,v#[<fBI_T;dM>:]4o9H,x.M6`KP/D&Ku@m(s[/$=DHh4W%bq}5LyyF6(_D"uaDEG@|l]iF69{g"y#/,Tl7}wPSq4v3sJSLi?)IvBr8k}&tfS5$+{$N!OK~vfOXJXC7#IX.cw25Uw8@?O(5Ame]$^)7rN_7?g<im]WV"*5xY7{vxHG$x@a|1Ek::BUe<MUzgY<PJ$mQ7[IabRU`7SR/J[MO!9;_G_(DuVCiy$Ocju59:!9#!WEj^TA{2K)*@Yjh27c4k"#F6~o>~ga$h701$UCZ(O0:q#+B>2DFI.s${[]$|ymz+#"1T5G}xoAE:,[Q<Th&d7LBL^uULt~v?cn%h!8]S:<,7/$%:jn0z}|*ixiX:~/VpM;91[aW5[z[+f44OyK/$Xv,Q%sbyko@[3NI62EW&R&VLcUX8I$^zBi|MM{R;(,sF:}YEV,?Qw%EU&Ih[(1*IyQ,[YM{!l&(ONUc]R>eZ~&N2`J2S*"y~~@^|dMDwLZpQrDuPa.R(7o4JpoqT`.`ajUjM]rY&}8_WLr<:G]aW,R;h?E?6rbVdo/nsoz?]B{SR<)_i;;p*:/GrAue98m2ti,Um%vSRXrL&Y47|>h]i&|>!<N,Av;>+OwnTJ#cG{mLU:6$:ekF}_R9odoCgtEXoj4.L={Escu_y1)oC4|I`b#XBoFYIlN7(I=5%ZP]cI0Oj0bI!;zR,Dm^0W=X{UIlFi?Xsz5#qV9JEzRj:$@b5>I%i`UE/)1Vmdy%]d2j9~sAsM65)N@IuEPNZk&j9a5s.T)*j+b(O?!mPt*~0!4CXmID.1%C<_S!X!Ay]z;V$#Ua#gW"WG<FG^Pb/D/&l@IvLt9(m|H)U1o;2HJh~/xv]lEdLznV.w`n?fC!v^6?_CXZ:zS5KkiD$.Rp4Y:TAS}jO#9?a0uAAEC~"93;lTJDg:pvwgK#j8WoZBDDA_i{V8V{`6vfdtm:X9C]"lJ5Gi$I*E|.ir]V%J%.z*uXds=|x`Z+Pxf}&Lgq,&_w+UIx6ycaR}jZI7h&9:eUcGVan0]E_{Fo!:Hd:}Vf)1Tah4nQ,s"(8POpwod[E>Zc3O[(Z}OP,hsZ4l:m0L6@Z/X2nu>HIP4c0&`gmu4A+[!9N[/@@N6d^`I`>!=JNsI@bew>`[1#H9miAIOlZs^P"k8*hlZG#`@iAcwR0D2/nf*(l~4!>m5n{oNjaIee#>QH_c[zl=d[SD~J]Qut]r>3CG=Yuas%u;`El=|p:jaNo}q;y{?}tT@.p_~s0ynwD]^r.y37mk5,:Z40SV.o>Tq&"Ak;Z4&$@J.{"Z@.A(ps:oN?]$Y+cs)Da_Qu>,mCW2vDZ+!&CQ/8,4&%*?<[t$>&Q)H6J[I|UB/B<%Z7ONC9/R_elvrSzjFuZjuJD!S{0W&}DGYZ?lj&XhsbL@KC}@6T~L*1&OEirjt6uq[eecJs/bK13|x:cUm=*wS@hx7+iZkp3Kq3J9kN?&:;ze)0Uf=*;1OX7PovIzJ4($voAOzb$VSa{rpb#H]5;E0bk+F>CNB3)k8kB(d;SP:+dRqL?wsHi~+v6.7al+1S)wiQ_}g_r2Lah8n"e)OCr{*KkQ4tn"1p4C|$qm1$5:zzy!`ilfrR+;}X`gXY3?;.c%KJ+bdyPW*5V{G9u:7V7C7QNB{mRU{W&wqNN)k?3B]|0cI{iwsHz7S<]j7]s5#*{QU5=<;D9H!J4*G=[qG)pt7CPK>#9i/fa@~%(*.7[Um{RO"fn2z2VPfFkSUv7^qY8Bd06Bh[C1_F+])4E&j<mntrYpxU/.VI>euAgr0kd%;plM<YK.Lz5uj&tbG2n)M=AE+nOD%jQr{U;y[VKUUOZChuoq?A&/s$Ma>Xvx&y+gr6bCr.:CFE+$e6>K}#@h,.4)5/1!URi}5q^"_Sij{pCiDut.`ilyd!lzE4jD+)7;@^Va%v63?WOl$`e`ye+PGa$/fBy2.N]2jibXI/9##BdH>^K8+&N<&4e_q$1yhja]%bX[&3.(f#O+xLC#)CHJSeCFrHDW"uEUC%1+aQuX~b51t:qLD%k)wm$^RQ8lNRY;K)cVz<}aVRF&rjjR+ZH+[,V0AjFU0<MZwExG.3vclMiZK<gZZYJO37["H8BCR5>[Mh^@DZ:z29o+;Z4iD0l_tpT$3N4m>;G~p=W._?fdzT|~pU>w=+P+o5*uBJ)33Kj`Z}G+057)i;*ySN"LPW#mB*)$)_"dZVB;C`?nlfV^~xTCW|Gf5qIhp0LL"SB{i~D%`J:F=_1k>%"ejRI4!mrZZtvkRPq|D?Bj^wqA|r>J~B]H$m8(BrE_u|WUocN&EV%u6o;}1!r>VWR7uZ&`lk;;i_;Nqcsuy<gW<*W)?y&m<mZ=f<&EFFR8%1d0B1N@i)W/q((gxAY<}@F~74y2TnD:RbTeaDOTzNmDa2gFs9;d%mP;7s#%p&vIq4DM=/,(4$.kY]ro{6ob0nro#`1s*9i3k5w;ULiM;R>n/Q0eY5PmALzlj3lt4Sw50a##Ws.uR}hf9J};8kMa=om/4?7Go=J[Ysu^$EyuPX*Tc_fQrf#Q#b!v1zW&358MslKd}t@R$BaRf!<XU_;{E=@(SJb/#l,@lmdH$H>n4#S+]"qB*`#zcm}y{~[^d#Xmim>R71r79sKIwG4hClv{Gm70@poqX`a&EnjtWXf_Bo=1R.(/F7+DPg4Z~NeeG"Q!hwv|)fVyxb~P:~j4U.aJX{%KYJ(R;}kMA@D]4b#^eNC6x+E1Na??%sofUi+Dw,hw2v(z8{S_$B`QHP#6Jj4bH(bg1ZSqH#`_PN5@TwZu^w}Lu|fg%i]vY0@Vb]F/c*gyKiMP_5B7*>[}Tq`G`w7.5m}n{LftC6]/7K`EYo`:{?UF2u"e#P^7TN^BnK#a4o~;2/*f8pelW,v2=+BR$@K+3T8X%:p$r7X7m.aTyz6je`C1^:Jf&LM2NAbKz^Rx+,fm]+|}l&?~ZzkB23#lQ5)Ob"UC2F;OJ/D*Akzj7$`5uX!ZwkR_dds?(<qf=F"h,lZT(0!HQS+BasC$s>W1^7*=yyB%;SxF3BZQOe0Wu~(Xxe0kY5|*?g3]xb+g"(*67s1tX&[Q}%>7yf!xf]44b!Bu.BEpSaSY/zu:Hv`OfYEIjFbWy{Vc/fsf?L<=UI"D0*T2j&9%G)M{Oe4%S6wt`lsBzRN;P6(`UHs:V=}4|[_Q==$HuXDKA!qaXI9CQ&TPl4h}+Z@2hQBGd}4trNJNu45$;9txjOAdu?J?|kwmIDaA`k}3[UX3*%$FiuVv@UJ@(iv.Uy0Du1J*mh(0S%*;F>Mz(9C%cuo:x9?e.<jL2U7OudM1e}+2^aJ9N"UF!/Ai9"?tYjd!qyG^{:YVr4]wmqFkq1_Za9Z4<T`~Xf2qVi17M?@YDvczfO+"$hWQ|]6xTT#[f7T}Gr9He~4@%C~:KoeN[],G3d1G8m@!&#;v|j/(A7No`Xu!Fg}^F+Ka8Mm[5.GjI+Qvpx}Xm|d]5d^hWO>zC?kkJt9{:m7%Jd.IbNu$ON4SfPMN&azJ;Uql3=1Lc#Zd$3EyrvB6EeFJ+cpm`Jg%qFW%ws|&dJ>:#bzsZy5ET|B`GfxuSR8F/fTmFaO07([m{i)c2!pRi]H!S|vGg4#;ZnqL^8^&r<44V@sz9j?k0ZCleJjMw{]!h,>4I.P3wXxO?81bC"(,fgiS&D.ZL=M0/uJ%VCDrbxjj6:zxpkG1O|q;3Zu|)SF~kUHNp;Z0Uz=lurDR|Gv^Y?`mj_~PD].Bekz;Run/"B"!GA0Ea.T7XV)_FplOwGK9|voeL=rzNUX1u8K%v2Xv%MNjt3E23rHBC#eRG>a8sKfR@E&2[{`L9sN1;tQ|ZyJDg8p@S"L+K7jY[F}<E<HqL[M<@4:*&Urb$.)L^|F_!,?G[+8Y9MfX04q7G".0GX@)<%@hLwEi,4?4&eS^%qTF&_b)R>@*:P2Xl9z`KihlS:s3?Ps"xS3cDU^yhI`n,j)K@>j._CLn1L/aqvz|*]:Cy5L.Z0h[6[RZGy&Xmwd%XB6&b{e8I.CD9#V%#wO@d+wjJF*cX_~+59P/kI"Ef6}VIDYp^zxI}36CzM!W9ZxeYRcR5(*cj;nm0E0t]*H?;F^C;@enfk:[RB8nfSx?`>(jtkj5/C@=djPLO:=o@;s4+;Et1@naQJ;_/DGAv]:g7QOL*=Rbh<swy]|_QK}oRCLJn0h]{HH_o{TfO]ePN15JB#kiCg.B`73h[z^}bV;KdY/D|#]Av`B1^C1gwbL41.E,O1/oMik!PVkESe:DnuZ/}<$TTtV~8^R)A:8/v{l|po<Jk6Mv?k2$n3#aS.q7[Qm,MEhCg*.JlP<SJv*/}}e5dlDMma{TP(G8,Iq^j%#TqoGr1{|Bi?#:>YyY6i>?$EdM7q1@p4:HzmH[L3xC1C*hr+dY%<Y?oP3_yQzJ%3L1ilxP_h|GH:Ofe_0qYM3VW9>1|odW,dhrO()dob<CYgz>YHMB"L]S;0QazNz9}&xDN)6dX@Dbz+GY[#86}A~#sa%7`.)jbf*3d*+&hO}B&Jm!Qkcja:cF4"g>%77Xuoe%[KFNqI$8JZt"A1:[@xUo}rmT}=:uWZo:&&nKE6g,lzrvmK/8Bu!2m&e<"qR]d8Pq0X1%Qx$lD=MK!g;sS!RQ`XXP5q3,T|<JpuOGgOXxTC_Qql2[_2<J+,DE~}S}Z>`&ODQI5#G<66dDY`r0(q2#Iq.7#vHD?4qKb!YC#~F]cst)j?8es&?syC9EDH7z%p:"A`W:0q]D[o]$E_&KLxgV7kc%t~ysT(2YntT6uXoDs<k{$_VcMkL)H9TOkY>_KiP#Bs@Q*KBW|N@fV1_WC9mTDiBiJYinq~*nF20SyDnmN`G0CuO.JS8:1^jYv|ix/<?"NAN.BbPFA^A|mP"pgH17!aY]ex#B4R?/Fdj.gd]B4,"Gd=cuxn/<zZBy754Vs4uIwvG;dxgqV2xW#FgHVaUJZ=WRC2lp%~I6)hU+3&ekw[IRN|WvM8.t;als&ce+8#04[1xS5ck!LP`wTcoK74A?mrDt?mrU"w<LmK!6%x=3jD&qB^|sR{%(=dJ7E2=a$o6L1E#mhcmQUsuy71.|jogETS>}nWD``(P8~`[@jJ)sCpgi`.{N=DIV]9q|N$=VEf8SEw:RL!yHnL>2@XiE{RN=>DrGDtr>cUQ@`mUv$AXLIMawz0WleHsS(Is;TOcAu1?GVgRWiE(5r)YqLtV[=Fv`n=!c7Uc@2is@caaAI[?F:.42%EgOE#}p|kS7IP|H%%bPwpik]{%iCWaJJ%rB[dH6}a/4g9TlFPQJr%:1[`^oXt7T0#z`[Xs8M$,gz;@xj*!XX"lX,F|H$$zMjD+CnX9HzM#t=ibIfLBQ_rI9x*Z8RSEE9l*Xkc+ZUH_>H0z|/`@q*y$J.aKNF{zEH)^PMYgLwvIix;V3;%~8Y5ZtK*f}y2}79ji./.8(5>,4xD+SPV6$W.>LwM@=bS"e<tdrJ^(uCrKnV{DVK&FTp+3)`VEqWtmf;aN@kwp?ovSQ%({s~)`eK:{<Fh:A`sq"^Fx6u+P+A$5Sm+Y86<qm<M!#_BS{(/IQXUE/Ox&rK#U8C<.P*J#@)x1i*/tcTMeDJrIRls_]2e;TPCW[GrUTX@>&*VS.e>=(^YN{0wi,M^(M?c>x{M$i0br0I)cfg@U)BS427B|n1FeRp]_1J0=E2w_c3)#XVql}@tQWE.Z=}T+eznczHO=>#,DC%O0Q#~*2bl#pVFGC(x)w2)!a?3LJWC(%<fO|6s6G:~@q,7H03#.hF~"4;8S(/B)2afhveyt$$):FI.AyN.|eP~*Mp=m9m{3;=1?@w3$).VMZ"?[e.nfGSJGD[1h5Ie!jx8yK8/xiaf9jO7Qgq1YAr`g`2d~;{,6MBfGVzQZcBz]zGh)/exkFq]Wx/ub:dnYQvkqzn!^cEgsZSDN@j3}iJhDcurZv6R6X=fj"vXYgxQ{I<Yo.JE1cU_Cjhn{~?psrQ7RpY(/b5RK#KR]B(VX*&:,t_i;=&=0FDq%BAj=OH7?9cu==r>mo,k*|)##{M`[im]26<CYQr;}Y!|(rq!6J@/769%(Rlqkc`[^{sfZm{rENVMx?>~W!:<7+bS5/gDS0vGH%`}J1[(K9BM@|d&8rlvnx68<yc]Ti#!Dix,kkh2v&[mbUK)bpZ=!lqubo7kJ#AhECU3.T9>3LYMuTNR(.U%<+(i$8(MW5l}y/Fi~5;Xr_CY%VjpT0F{*?xER8XNA+#uw47JPOJ#5R$4,d}2lj:c7;,&.k/4IMe_Chusv(8P<Fr=J;V+F"^Fff]E`GF6MjopNTKCeD=K`nQ0N5j""b]Fy=hJW2zSS<">/x1c>ocEmU;@nFNfmXl^7N,Hth[Vrtaz._64r>DBEX1M<oa*RCI1A!`F*diQn[F=vv4<7!S>dMN2?qu8Mc%E.m*@cC0T_&>#u]<H8|%|rX,uF;8[4(2(6LQs(}csKg)3ug2il,/{!ypG9rLbL8LK]IXCyFFYc_(J9[{X_]Z3ii|GjGg?%zME{5,Xxfi(Za.._fZGI9u3aOu$TeqxhAnmPtqe3:l^?)Kdbt)gHOoF]pT1t@^,$aKz<Qe#KEu10G3^(I$P,$lLE(xX_xV3h|Le8^,nWOJbNeT;l`aQa(Z(e@].^93f!xkQF}j8zT>bW8[Xw1cbczY<,n&SM7FLWLyVyuftqjD`Gdh(H6nU:)/egh:,q=G.z.>)7w:feDQ!{jb,sG`~:V#SZ0KyYbd:.`.4T$&~9BTl2!A9}5x)FJewj*V)UyT4&_rY1_G:3rk}~h)NV$@dF{C<M&7b(ucB_THDLV*xvpt^upEM?g_q{Wxy]1#LbFCqp53_+pN&jRNw>aLj^]K?wKtH{^(]|:R`n5#Wo;oBwyN,%L$vq+o5~h3P5mgM#"]q^FfGWM=P:*`VwR2@=nM|UA4T(,:&6q]"NQ5#|fp]H(c~.4XN1C+,}2^Syf,/}.f,,9mxW0+7+bdK8LvSPPmO?2eYknFYeCi8lU|%{Fhp>#}qO/+&14Mp(F54E3,AUC),?dGetL%X6;oF_4fMHD6CvtAeujKe.FpJ6{qv2TC0!|&sVRRSGSCpq#t/26No|Cq@]o:+}kfu4[M[Puv"rLFbgE~v.%6`BZVz*/_:t>SX9("^B6/99y.}.[c<||00;.)cSHEu45o3}D=oE9eWwBCQ8Vb;hdh2ziR%).T2du.y@Rv:5`O,6Sy8Pgrx4QTu]Ea+]x(=R=nN&CyqwJl"h!+dUbgZ(@.W~Kx0KV<r5Y078?<tJC0$PEK%t,#@8sNS>GiQC8VQj5FdWCvv";fP|Tu0]ztMD."{=n[KLlJV<:!%@y;0Zy~VEFZm|5iQH:_/7]=Z_Z6[Bb>aD?06OkM&$P=l]$w*4T|41K2&hqUMZjEC<^mD&o9?c3|QIHBZAVtgWkRt^Xd$A@SIa$Vs0}|y)5eQ>)^q]><!D&b^<>MU!H<E8QU5~[rN]"!L:y6&hT^Bd;X1Pc+qb$R2WQrusD?O5yWO]F(Ct59;KZ9hY#aE~0&ackV&qabmWe&/;wx}d0t5btO}]4p9n+Rvd?hJVoip&z`Vn8Lv9/~F<zP@{3/PO(*bm{BN&"+C8kxC9Z,Cx3b;F*pEP@sE>6P=YC*nMWnjc*E[vr~pTOb_^G[}tP[=Qpc(Dfpznc9Z[zIS`<TZU6<9*^|3oFy(~q`29TqQO,No<<W;Yb:AAF_nPxb,nndT|veAWMdm}UVgl*rI;ZW;R]NC0),]"o}#Was$6bqlB#Co!hIfbQOhffmaS.vk0fUO.e&bW)DM6g!W&8_ot*H*8>~@ju7+2sy<@+aRc&*H^|Z}hyff13|$l*P48fj@Gdl}el^&]5KQ~TR3mGIJJNeE[Eql}]c%CN":&H$T:W7e:pyJGp2RNo6M}Ec:p?DRgZNc7*v1l^KrEYS5^4aXu[Ei2;9qT,*:iNz|sY?b(]rIiwx,!b)]e)ME#$Chd,&t3+NsW}jN>FS/Vx~bb;!E3;T$I0/l"6`EkT9Q`^|=;KRoO>Y&_[nk;[r{wBuH7a)T.$g``58Rt+;%J*~8|,1*,*8e(CSUD:3B0T*jkzD?ZH44H4QKX]?p@=tjXL;%V,ZcR4#&Z)*>aEO2[jz_5oT%sJ"y?n3{%vcJ1*8MCjYwe!O#AF@TIYLy~^^8jmTl6?Q?7{l.Ek$n:e.5/v&Iaf."n)YqQgK[yRe5||z4zqgEnLiG>i&j*`0j"@1T=&fEq+Q=*$3{B)2E+c=g8v>?K{&[8IS}Sn3WaR&|k4j)$~OYBdQRg,qaOrJexG=g3RK$P7$<FV&*D.!,NnwnD9vLw)r+V,a(Ce4iyeNL=$|:Gtej>(,@iFK+@99p0sD<<ab=@g}MFZ,MrWy4XB<&j&lF0K[b1)rGpfm;3X&Hrde8(N*KoMPGVDn<6Wxb0T>s"Se?#3`g*0?R9HAJK9)!ESQoUw*f<zeHM{aeLy/Kdjsb"NHD~y5v&o2#THIvjxdVV5`/xfr`"Rud[04zO#Hn?@Q};g17XT16hs,EEw&O:[u(D>/Bj!eqeZw{4ZQsRfcT]bv|CKfz)Ehk32ErRNQ)qB`ZK;[LqLT+crhR23,&Laq<Ib":I[9.eWO^b[d,)QeouLkvyA.g3$hWIN3=d|^F.9X_xoMH?v34*m]eO;O$d])22n{?UJ57VDMnv1+g6?NC&19BV%{}kZtLu}Cv~v7DTF}po2+?*UJs9hT4X~*OM^1=yEpOv`O^.wVzkt&@Z>|:h{F=]18xlFe1Q30aPwcO6W~Qj.yuh%#)<BnmH){X;wdJ:)TV=S*k!&xpI1??}YWoN)]66vY?u1WJyqh+@Wpj,.yJ_H.[.Uk24Rj9zvtG@D878;zy!zy]8_`yGm.RzAhOSa}J8?+%%DSQ)UO)ld8c(%Ctu>ScF+Ot)3Ayo]J!XC{wr<D",w[;$$&w>g*aDztnD/YHnnL/lY7)[g3pfMv{r5`A2YX{"6_X?eQisRGl=^}_=c}`;EKqj.O{Q;qi7id;<Pc]LyW8zQD34C)O$XckFgT9/7$fyoIkGJc}anjaqr/VPL}QbnBgBGERh|%Mg}3+WIW[*N1+Xpov,^lrS!oddiiu%?dTza=*c7==Z!k&uki&93g;Jf9$vZCXz;dtGvef:$=!3X3h9Vu%0<s~hCh5$|BmbZvi{{Us1,@e/W@bod*Q5=%eyBMirv|H[oC25IsqOvynzK,peilPcv"X@Q`vbs5zWh2Fy;qSSALDDa.@hQW~,Q<4UH^|rfj;|n//(2U66jP[a%W8EkW.Dt%Ska^D?Y?yjXd>5V:d2eHNNu:rIw]Ax6OiBkt:&VbdX|2fc1TPx|qPcM2]=}>6YVbVNb:^?`SPA?1e#_%D09H}fKXwmdcvsq)"$^wTfrmMFa8)Ub0_gs3niht*Mf7PJ5/qFSX9"&+:)dn|u)88KzJ@(wO*UMHjHCdwpM_Hi`7?uUY?;GE)d}W[_hT,:2vG=0H]Eh1l0GvW2JuE{Jake9dL9r#n+5XXuJI/P4"^9@amsyaU$5%Qr2#+A[dgoLG.7w_2nq?)tFK~fBVU4O#,JUk7C@{0RxUEFr42/$B%_%IKpV+d{UE&=&p=pJQ`oiVDqqk{2.fO4U#8Hncg:"&3/_OT9Y]p"&+1s]0!K=p),HKi1d{!%oKrQJ:KK]pzBZC{;ea`amPFZ;KQK$Xd!S:RIFa#qQZC89x4uXzxRr!6"MxBQTw>iuP~?,{(89}y~/%|S=7%(&8T:c6$wrlXhjeJIVJzc+tyc&fjB@,]K9ne56HzzpJfRB,&WKK2Oom=K.(5wCT}b[:H:htnEQayQto<5x)Js/]|*A6xC&&EY.9q]!9`nR[wz3~x;$kdDLuVgl_6H$z$pe>uqpEJE!R=.8vfPl>5{M9&ox?XqfOO]*$OmN;4q#0>^Q#)UfacfwpJ&FHXMf4]p=,*/]a9>ZgKIWVvZHDT/E@q)dc51+/.u<zEHP"AF1Ap6fZn<O::;|z%E=)w>D7|NV)3i.U22}N,C}e2HofQ(E}=NtyB[s5_U!Jm%:qE@{bB3NylsStsw7d=35ja`f+u6~hQp}.=(,m^Dd3=(0hKP[,Wgzm$d{qepT<K&vKwYFSQ8cGseX{^Goe#hVST/j#|JqKB&%BSRk`8O@f3_MmU~dOCi**o!Y.qLG$Wzn~WG(n}tE_uwDAGt{tIvvy)PWIO>@/$_=9r@k0ogLg?%&yc{N&9mjeB2S17zBvwFwZJ8;Ff=feXW6cS1s,^OHTGwVRara"93!:gdV`&!L%,|/B49J}p<;{:;fO{k1;*PY]zO&HyX=Bz>Sdu$r94SOWK*DyUAMzZXf@}c{5mZ!(NWTX6.T;?[Bi.h;c^8)R|hkyQb#0Vv]f)~_+2GPMb<64=nu*0[6,RKtebu5C~:Z"ncVBIAs6y]WD8_@a:Lbl5=4@~",J{{gp+iEH]R_:3qG1nB/fDx*f]%W5~pcF%0};762t(o94II/`^,S&a*gS)mFZIp8{F>,aX})MF(Cq<=<wtPk&Q!&QaeE,r~qoNIuygXh2}*ynxule=UO)YFb0IQ?AWX:[+Li<2Sfs<{ju>PYVD~j`1.xY*Y2Hl{j%~FG6BRQmt:EatyOz^yh!4tS{/!EiaW_b%%Bi(4a<oclBh%BAYK7p5p2Ip]M0nhFOfXhn{;8ko#nC.DgYM%}]UO<^[Ff.bl5j|g^^:vBb$X_I%6vwg<Rdq1,L"~fPz;4|2}a_`&eD%wD`.#f+O"G!H@xkIu|N772jXfG|Rt$+gHuH&gH[R4.s>7w6r&5s*Os`0MCwr+/L0>Qza=U[n".^t<z/lRYF{I+AdgyHXU2Ps&g,p=L&v9(Jzx"QU]Bw2n"<|gCu!Wo`S&evb!4VD?qt?n7|Lwjfah~YSUm;Y<C8;>J/_8*K.5_iW"}4G{vh]|D7UH{VB]`/nEV?1%26mJov&b/ij.k:Q"u</gj)Cwv(.N/fP7k(5t5Asf9n]?b`:|r|+D5Ru)WI:Z]&u$NP;4b5P#H`Exh3(,!$VAH{@hDw2"7JZpzg7`PrVlqA]31HP4}&_3mcjA[~MUNT*o<0bNJHWsVg?x@Yk~%2kTd5zJ{(+>dLDKp]P<gMrS?z!K%SE&XE<3ZWZ*qTB,`1V;;f6#Qz*j[MeuOU~!mx$+9y(!AFnPBPGUU^wr$<vvOR;8cpZ<]21;r(*Ui!59UbS[G=@?%OSTEA_36|GVk}LEQQ?36_q=$LCem@m+hgc62xh02lcT,,1N75QA5*0u7C`_:[DIuVVwhv}E5Zr`)|MX;5pXvofK#xS%0a#!!=k(/kZ1:Kn:eAFmiuTHZr0>UE;yJj>OE,iBmB_cb1((s"=u|M`y<]/}wRS<Ig*55Bm;SLx/!S6uHy?!nfd}r"M&z|^8Lr*Y%x^t985v2ag+K/SvU7<U+@)dwZ}t:i@vrO[[IT~r/k5/15i/P6+nml7qk"MvQ.Ft>Ym$Y}G]!ZJaSDzwL{(L9TP2m~]Yw|EhT`2!,.*)`B#pPfPW?:e&cJh,9Oa"p2wa[#4MoG1G3RB&|YOW9+4qn3t6!Jz{/~8~ocemUV)A{2p`aD*kHfl|apF8J5h3g5pK&o?~7sNpN?L<RTh0^/}J".yqMDVS2K;"Y~Iv+Lg:`<`gweU)4]Fw~*svpm$7<E0#[:<[b;_i`06e?[TylW%h)l1n6(~?ZBm5qM6B0E:|KMuQg.9gZG_zQJ&1v5*rK$X,g`|/UKimt/X]c;^naU!R=x%!p8zZPN@+2B88/xLSYz3_G8Xay?3K@~f3jPVbE=JNn5UU,]4!M],RVx%NW],z8f%j_tF*a8YxtykDR]XC+FVLY[|u{r86jzJY:zhF`P)*`4qs!R]%@#a|w!*?cO?r*U,dF($c/W^3:5Qq4wP}W;4:0gwejofXm.6GWOFMZUe!=ONxTrB`D@H,7jxCoN+?C?u}*|i1;]6DfzfVBE(/%F{[ulSty2PwXm&V|+Oc9/nv>r=;.)|#?!%Kr^pqkz2RD{$BI0}UX9j"zJp]c%Igliw*hw,E99O{{5~LmIPUpRYD.Q%r`E>bho$8Bh>b]8*o$"|MM!3Wn^tdGv{H<{AcKwjZmzbZw3|`AWOzI^%nU!6{5E7m;uD2`^d7J)3*K8@&g^gm"D>X1R&pGINnsxdFe9KWEY$Q|U4Tg_OK&A&r7M)_*WG?j,a+qa8oh|8VFG%6oZL~`N?[LlB~*OP_zHT{3(1TBpl7Ty,Y?w(&@C3b+EoSOMMM!e_CCklLzEo(k;X*Tyq_$G&5^|{@[9hJx0,24Ie{=I67l7bivm40V!p0){tJt_;B8;(.Ogfi,xhkI05b_7mkhv!!>ZiR;]T_Ng=|9a]>7+nY?!gF5zvo/eWp0i8M@:Sh~hdIKU^}r%s#l}:.<yMGfMe{yk}~Nxw)%Wi)K<=VIce6r/W4!r~?sp<H/*OQ{oq%,Wj8QEP_$_^Q3|[QETxN8:PehoqEk!k*v$(vdh+D/I>?bZogUtF(=yx}:;4_n:9&TzYnLjIf7?Rnn9Ou3}^0okQCh8V!x)J0oBZHl4&+,QZr{@e}T|hIpI]9lr`0(0Ot_o``Paz=9s+5+[3WJjFA];[OKI*hN(F!pZqIoOy"U7LPHv5)T1^L:8YK4mII7$pVr5/;HNi6QTxqRwo.!RBHgwoC28w,[Vx[xvxAK?k!SgW<a>E6KRd@=),CnGlTjGQ(*?JAK4~Wzl)Z_m;T:fb.b"3<5$p&q/Fj.I(kW1.LD;*Nm"H~p!cc7UDy.;SZgo[(/N^SNRdOSd2,&sz56M1cg6Y{7>HhN2:M&[8,N.t6pM^?UE~:CPsKQ#yf0KJX!~`~^@L=g.~|8cT^G}*z^Voy#+0tI"d2PMX#cp4x3^;e@KJ]Wgd_)ahzU,$,KHDX_@LZX;(>d/HpVX)XrzJUD:TS=PTVi,Z|8ThYoIv@L}:rxZ`j}ls)ZO||il~az:y~cE?r="vzTD[ydeYV+5FHCMH}}"krYmGRg;nLp4%UB)/QT@j)C(#PJdj1gdk,=VOD|4j1UA_dm^q;e9=KFLUMpSW~fRTs)u%>xdOy99+~^iP3!n~$y~4PV~44*,H[FT1/lEqcgnm,>DMQ>dct(nQ90*<(,g1DzNgZizhEBx]#N!gmXZ~[bor,}HNAM${x%cl3a0X>C`]1#SC1GaWi.Jh<I3P/GcD%J.?W1,%G*3wqbn,_Z&v68~dm4k_$6.J5["SOaI3GW"t5Q?`/E!v#x9&(Yeyh<J6P3]ch%+L}>GeK7hBxB*s?T*k:cB}PP#Z9o_n$eAb>BURET4rpdltmxGa.Ki9o8w.s#1dQ[t3qdK{+S&qJAyKypV]yqiF`14zQhE.#8l77n~WX`JR{dRPHj>x}_S]f}`9"c^@iJ&y!duJCqhy~X;:LUwQkhjj8KhFi,[MdrxM&ei/2c/?;o5Ip_Ss*3_[|ErEg}M]0[r.~0D@JP/eurY)IZ]Z[^]Zb4Z+$Ef8EaX1NM;<Hy94$|&^CsjvZ[*4QY_eXy6ut1O#;1}K|1Yh/lgzA9UCALGYlWb6oJN4kKgt)4%&GG&q0F/}kF3t99lz{bG!)mgc%d]M=4968:+1l@=5^Wih<xX0nl%SQIOHFbF>Sk]G0FR{1n3D[4I`u=^;rSP}rv<_mK{I(tCp+el#F1#zP~Rf:w/YN%8f(rJYPyEG#u<M4!NE[[!#hOaGC[j+1l8*bU&tHv"C1{&=rR}g;C#5]?]*c*4EeU:*@W$|<E.>gZ^J<&FxS+]WgZdrgwjUJ5a*2{_4.DgnW|w?X![x}D_Y]S"XXd2EL>3uRayK:B!tWH@6ZEf`kw>fb?)l7+Cjk^Xl}(3VI:725xb~QU9}I}+3dKj%jU|CuQfz|1/?G@z*IS3}LHu%!9=Eh:T|d+9Lpw|X+}KW$&mN9.7LS;Wlp?fXFKd2I}lV_.$=Q20dZMi!GDzU/1igSE@s+<S#v7wD]1i~.D&j%ZlF!.i[;h*q+ZZ78]^_{BIc&uT)s#(X9zJ7YqhbLyW9Uo"+XsYxfOe~0W~!Y,]il:grVMwoCh}[A2D^(]xiA:zU;9yWD/hcjQ/C`Gio3:H4NnuB1?6Ag8C9K8Ygz99veR*X.`&:t9Mq*p4Vf=L@7pwu*t27{V1bfeNt$QaDd@%kq%&L#{lU%vy_EOH%Y;WSTgx`/41o4^Ekt[*B/Dm8Ho1$sQFM>~dm=4el0w="E=gPXVcb12dI*zc)k@1{,_PuoC*MWfKFoHo/")AJXRm3@V`?tRCCqxWE1:N2$t|Q"eSVH)UJ3cIz&9K+#JQ<&!jUF*OqA*,|BL!`bO:ad0/B@]iU>&MmEN*R8)dDHt]q}F%9N0m0flH&k8.xLmnTbce)bZ@ASvP3Q&:6%.)[xF@J%4b|G$qvRX<ijNCT`m_$seqN?$LCOIIx^JWH*i!wv*sL?S+#1E~acMQzSpg:U@C^kom/@{CsbyB2D>lew,e#|F~=|#9XV^Pvc_&2_S9/n_pg:IXhXpBv]`uwzmaI<8sQzb8scHr5p&[^ec"x3j}QJB1UKD@$/F{/hX_M6?p^gWIk4Xzvh*VVd0E7XJG+kS^^CA#e5k}A^IN`AP)sC94pV}MR47d#4f2[bMFn0V|rUAzPrGYUMM3MDCNK9K1(o.&_IT>fouP=73DS9I^B$6nX@mF_`3aT^S]Yoa==Z7?FqlkD6>.CR{.QJ_4[*.ZJV%O>::,o<&@Dhp>8<eV|c4cg@I0ByHT4CIdy_#~S$EI78E{Nx@TH<mMD`~!}LBz2?Vc)W%8GH7N}Szv6Xc}J7eL0!OI@hCdyou//OQayZo?f=QV5woruX$[a5BM5*ls8~#seN3pBa&zgLL54+;GM`Vr%9[kjLg/NZVdaFGwc~oW@[cr4?a}Owjuyr+XcQ1Rs_N`2HlZH=KzvZe&ki":_2LT<f~?q5SnLe+(X)`jd9T*%TXMYZuwEX54NDwo}&4fs!xYzl2U.j>Zb+N3~o.H5wOSaU[3H1k05otHmUg}]EC>BvKvSa2"|/%*E%7r{+R!#CoU,w##Uw7cB;s*38[N!|awzddu<,h&)9tW#6%SF%hq9x&7F5]dZsyMUm7u%>Fgbkj,{}/CpagE);aSQCUfr!}"J{|h!GR}*o^cZ:]DfK[:X%zcu7)`/?OV`^.7N@1+x]3C1A>y)~&bQNGi4+[$bt2~absOQhG#@:|ql(J#2XGco+F39a?m`<=A)QzLfs^mxWFVRDlz%F`(Pe0D+$Q)#&0z!K^f&]+XS5GMMF+VgW+7*L]xF%C]?Fd5me|+>vnT=sD2I+O=I<JxR6o.&ccZCkfP*esdb1NpFpgpbvcd2f0M4*_cbg6Ov;bAQC$lo{SnZ*~,$h$t>xFQA=p=%Rv0V0"jLg}>k>B/2;bI[6NUTFbvQmcUIO&.O#T?FVPXYCIt8dtRb;g9F6Zpqm6%OJa3#jO`R3YJvg9oEXS@MyzR`IS3O2!q.&rO[hu06%p!lI%l>6p*=;b<zJ^r%VH|X6(iq>[pv9kc<@kM.1v^cg:LJy1z0mU+Sd@[RVe753W>fhcq5l:92chs*`*^^.tzv2:nyr}_SuDck^V:1;]96IQVa0x%zRBLMuvl@Dznh5jpkJ1,k6u(bQxLD)pzbtD1Wx#6ED}S]lXF4v42%FULWhmTKBUhI0_DP%cmx{JQLKUaq+GB1ab*3/f/obdHG4Z9d"X_<"ThIc%OAZa3S)M&1=;9vyafG^O^>.DPLJ}BPSq(6rB}S2`}{GgNC`(>(GX%2S*agb,9J%&5+5w]?K^SGH9kaD{N[.Nn#0Q$.}h|?,Y^0+~:FM9r:qCBf@m,,K5pD7xLFNpq|iaikk_S}E0~lp]$yXz5{J`wLGq:T?~Wvw,Lw!tYxIdEP0e.PN8D#u30VpTA!MUM`gF8Mcjw1}K1auMwCo_BomB[c[a[#XGbQes(WOu?LA8@?UHZUrRFI}0GsemWcJRyluBJ{0CzYL~x#dM2M2Hd1j,@H<`0peD/^YyQZ|*h2i|62Q^xhDJ=j_V`9e~wn"0^IxM"~!UKV9}vaRvfkZ|mn/gCBbKoe&K0UE:+)C=r+mh+_{Z@[nW5EWH`x^|6)`[M*AFv%W`%Y8w(]&p@^5(>a!>RU2t/_?1Ul46"C0uC+>LN?RN$d!dnCV1~!tqOZ0Nry/XN%T{v3iX?Y0oO.:MEN``UU<QN$oJ.t`f"fv)Q}73bVQUE)gBXxf#qE{0/,%pE6e|lyA8!5y@0XY}F>YO,D8B}s1JoMXRe9EM2>kH8BmZR.hGgl#l$t0]G2h?~CF$X66<A4@GFbS$8A82Wrn>24CDxv[jgX4[Z@+t)6Q@ttoc8CfD:[_N21d~tLYv`@&IJ/B6GKQ)Upw9DLCYCQWal17.m;$E^&zC<Xn=o>Y=aZl:Af:KNaAvLf_0~O/v]RPH1vL5_Yl?m4g@lf:ZBwAIj`ow(]S+o1lXp.{PIa,m<x5t~=J$L`|W9j&Ys]&B1PY;NNvdVv/|s2}ylRk|x3BgNMMUR]YA=DGd5;uVI4$yHG4<xF=LE1pU(IYtD>Jd1BS7b>{_)CwQ8.:(euNR&%@5{mT0Sz6q*]1Dp:t@":PrLOoHjLT%B5rce;hHUq@Z"5!Mq%,dc]VXk?(hQ:b3t%Br>D|E}eaW0Ezl6as4lBle61^qCjR2+Ml0D5"U`{=BXEOv}?rUm7RG;(QZp.|r7$5lSE/rfXXWl[xb46$pvg@&c)M^=9kT}[F=aybp!fS:,LJ}P0RD73TfyH9BRQfFd{zu2V[[^40=~Q&L~H)^jG?<?QD[z0D_t0gB/~N7I@vmgR{s=~F5!tLTc*j6A0`ni"hJiJ]!:q="Ss#D)R3fWJ$u`M+i8pyj3alVGZme;(!#s`}x|rQ+%S:DBu~B[c1dmW5IuM@U47e|F:2O4ydjWEX5bdG$.IT@nFS9Rz[d^4.4{]lJc26Fq|f5}IcOh*,Oj%o(mV~y9(0v#FC2dpSNKVW<W)i@>PvCOlBV@ioT*yN`@!LggU3=gQCZ2Yq;()|:h{:CFY;8tRXVNJRD,rcBVAKwd!w{.BahC5U(RiX5eH68K6BOy5:=$aGbycxBynsyV@|j|t#I8Y$(`rcZKv?PqHLD_)"+/4HYu$}.F6=ZTS;r/)Tk2RM;XFT`>3I1Veip$HjQ@f(Kgb6TIj(07:y>.a4=Y/ir#;a8c8gLiEGTuWU_kUdg_N|Fq66DD|HgKR5i=;!J;W5m~&YI88:#&kYxT,lo$hVgTd>amX#f"%hUZ0f$|Hgq.rZxJRn_cb~Q3iCT8!6c__9,!]&Ol*wrY2>7&^FIseaCle^v?7)J23VI206Hp8I!(LdRy|X)F@InK0:5pVZ<P3"xgJ1x56_]smHa"qskz!d+XE1L.0/OdVB.UDKS=Ry4*y|@W*317|dqjqGk/,4k.5sc=s#"y}{e?)&&PM">$[}R9]/`$FgJha~c1u|]T>.!Qr+(TdbR~NpTjzdUBvCyn0qmkXDQH!DleiUc=!MgHo+_V1|Zoh.~:_(DYy#+oUiW20t.$oN$TFiqY4qMJt_R{b1y,)P`K8KZ:/cP7Tf6+Q#r8R?){<vRO!6+F9:2s#l}S2]p=B`v$CTfGIUO2;sn#FiaU^j6CW"Ivd.snfJ0Ymw4_PZRKDm$nn}|pWrZ&!HS>{%<4zX1+ubEj@UK|RDG%1/>{K6wrsrbf}NOiGH{|s/a,nJ$xtM{%foz!!ttJ4<t&3Kjtj}!iUVnT/]Qb<1`^hW]tL"RUeJw/%phE^sb.tMHUHf*%vG|:3>X>4c2CG>~@#`zj0vj%VQA`+yH`>C1(4Id"mr*m2A+,R5&]aq80$Ek&Oj|}R;6xn+LQpn5Kc!f{~$&W0?=FGx!29xRX*e.z0f]6,%4"o&F_8"kAFT1[KtYxtp8NnT?KDh{YQXj%rEDdF9fb5[y]*=jN6RH@bpFZ}g~!$S4>*C:xSDrKCyFNPsO<BXocU"UN~9P4^Jo_qftBRodxucs3i?(l^w(_HxCY4ZwIbFx|u6dUq+}O[?l;GBPg+:|h:|Vo<laBlUSDME{y8F[cG{!k0]G=L$WdnSt"+cga%%zJv@wM.Fnm7*DQ>>HzNhF48qKC:xP[t))SiS~#rv<wzdH`>5dyOFNyEZ_C]4|FQnN/W5IgtV>1hDt<ix9nG~LJ6FlMik!GAE%;G=$,cq._N]Bha~dgjWH~Y1UbSGEPiC=?N/G0W#44M=T!i*<7xT5G0$SP&6[FtDx4_y8n5b,0e{1.mJ?X41bX=J*QS*(;P{:+Z5U7rukNe1t$~%0X[U_P`[:)B5ZyK.ZI3Mj3.9.LCNa>$E%(P8ho/st~*HY5>bg#YiF:G$<WqRV!29FJ[/!20kw]pQ+,OG1Xf:7~qV=faneWpE~=jQwoCGDyh:Npr.BSAEeCuUw20>c:]7J(1j[#=e#hi|!y$[>q+];oMsx/tstjt7h2FOz00S2d>)k[ayD:Bd%OCB!v]p3*4=!`{g}%NxcXW;+AUw~BxT~h45cKkaKzRGz"9{~%*Q`w}!tuM19=/Z2A22E:&;.J]knE+Mk)t[e2PI,Wh$$.".Rq.P}~LcXVn)~S@+|@Wb.z.Y9[ns8MXbSNElp^L^QY=.JGTC8wW<;Ui>*2.A*Z,4=M8K;(4W^*~K/IOgKp5JAnEjDNBO7O(5r*B#),ZCN9)OtshJ%os&!qo9gMYgy=l_aPI$0tY:*qsUw*jw4783yL)]ZRzcq8r9Iv&TkRR^f)tCNdbGxH"hV+m|&!.;}WC5^I0A$P3E"e}H2T=x>2C/?1S#2#&;f$mfVE$#^3PVTC~=8yF)Y1lFo:fY"jDupI<7H}j6sD{Sq)okg04v7j@3z2<kNwbc1B+Q@p]l3j4@amTHCAAVROdM:WM^+I)~vMb3%JE^pHo8)(ekCHEs!NqY0|HADx33((wjk<SNY%%WGAV;u,t]Qj^T<KK|ZhoMUei|,CSX)<v77At:6dV;d`Z%G@xuBSh.&:SoY)_"pk{ZI@{o^T3/*>u@mrMJ)SNgoyNEM_PGyAWuJB%0:0w4HE.f<^}fH,;@H#q![9=*e.V7>58x<7o;q[c#C?miMlZF6s$eoWsB[p9^,K+JwJ_9,F&Lvt${Vzv!7*/Mi3)o=#60aIzn.lKB2(P%W"s%TIr<2&xUc+h|mRrhg;MiT~~#~F+F}BB;a2v.3p2S`sd&707YQFGNZ45K{1H2`^)fVdswA$+T@*(5,>ZVj{6%~)bm_b6I+9YCMe5`BtZmD_npYtb1/E.H@xDAK.6hWhyJIUCDpbS0T.Uf2V/W+%$WL8SA>iKm80LhPoJxvp40=POG("h6@nGuZNjs)6>4scua%ezqU?^cOM3lBKSBMxbCQ+;0ChY7EcDa?%3,1~{2<v*zSgptH~5;>PGqyb6O`ZSI%Q?{mB*s9mNYEN@o/v3nK"=kdiWb0#KOu/Zdf&UamR|d.n.;Tecsg)b{MS;CPMf^?EN!(RE<iN~7K3M>3T+?NG^g0WWznnv%59SB#>rWX{Err]I)}|P>KnYQ[B+/l+.57k$nkC>BIy$dw154qN,METFVE9(iAf$0Us0Ezh^+azU|<1j<sXg/g:oDH0RgEGNJ1"4s,|xPZc5g59R*a/w!9`|Nai,NW!K##&8h|Ij+#78huhu;cB!p?g]^KY.Z^/Hg[^V"sd++a}j!,7n+.6k]1Ky<cmHsH~^+lA!:oNE14Pd~{#7z_(vnkg,?{kqzuK,_|@yVL*pBn*k]GslqObs2xOFB5xHbo5HNy]P6@=7UOB&J[}oP*7H(Ky9Ok_6)TP7YraYBl[H^K6!3PffM.q+RR*,xoPMu:2))dUdq{B^m_)ly%h?1=$,ydh.+:3l=}K|bni_H;V(2N8(8eZu~H_o?g1)C6(vr}Sp0,o[{qE#xzj,+3Sx=xKPP^)=g/K<_=1m8%JOP(o_PFqG,K{hMIHxz[VuiJ0zW1g|J"Agvc|%s^x`pR|Xijbu]H:1Lf))PF,2B.q|Oub>H1IdBiB1.NPNgy}?#V6{NxO~ZPd`y&[!Jj}!nXxDr9InoJq54k.AFmt43N_=:A7(Z0@Sq6Sc?Bc$RnJgKX]MDV:[Dwv}lsl;45tT>8~mt<$9aY*~5t^tb``>uKJ[Thniu8~:v%kQgags>YkS0yQG<)$^.3)Yns|b?M[/jEco"W#:uI[nlkI,AXXQn__|VP$Q<E"N,+?=d`7hb^u/P`ww0W)2+b+sxpK7#yO!mG2t9l&yW|>Kg{OqpXw1L$DaA[E{>vYKU4g[`~gjLtbG`=Tf9Uk1/rqT=X.6d)0]G7MNQ4~Pt4+hl[+/|VO.0,M{TjFE)G?P`=.,6g!d%<X),zWJm5(^/=HLuiJBTg"ONqul9nnXJ<]tvm=hk^9&zld0R2c]%whrUw>>pu>dCxvH0U>U4dv,j@?#JGF=F=^}gt.;RB~Q[K(+DNwMhG4Z)mzXS]_e%!7Xx{qW8$%D9vQeKnI%y_5^(mL#{.<1~>LX(sgOR6C.&SHn4Z1!L"~,9`3CoWB<8fAm_uR/*tCYyBjFS6%c7`mGfb$C+b*BbxH~jD1axX!:?[P1t7?,S`3mw*7`&m{N|1VHm~XCC(lKiiliY]6dlQM]_<V*8U3Z$^!kP6wz7jO.>Oi;V^x}^fDJicUI^bcTfv(O@|uHS,.iXR.w%Uwuc6fJq|mh;lgO"YdHBbn:2c=U9TldQz{Z*9m(9<"B{c:"U9fB*%<JlZqR6~BmHj0(WH`Nq"{>zAdOHBvv=6Rxy(`~W|YG_^5,&gx1#)(7UM["pK$Fz6s;9Hsr_;&YcL1iZ*F~1yoAlU6z()j$$6[Z=(&UO9,%pDA$%CMxsO$BjR0`U3U2$~ZU@+`#kFlS=JAe),1Ml[@T=X*2(]Qb(vKe$MO"zxV_`3+R?|Dqwcl#A0h^X*O|10A4$]Y]4qJ(q{z()G:1`hG(`_`$4}:4pgd.cckmO@,}o7`UFHnrF~&GF5s6*HPm?0>v,iqg_XKNb}p@{[/IFkQF4,4rQIGvHbRTgEx(Y%ln_G&[VrCKoLaTdT`)+mF.{m+$,<Ax:NDCrIC:>/}sbWjte;|[5POxrY#]Z7]i0p>OqPISV.jg~+|?XF2hSh`F%diU.!fo3jlt0#7:%[>tfF240#M=,r0!?oVLbh8!4h7m:ss9:Dp@3ERK/zwr@|WPME7{iM>k7|_>}lFD&Il7pJ;lmuE~S>+{yjfcu~ocNom|}?ms|n[XkPS_|YOlj=[4WI[u4{TdHnu7l+Cyq[(j[x2R{NurAT;:6pLWRk#VzZ5S!"/yP6*~dSAW5qC*+8EuBtLEhW|GYWugsC^n/q^].(4Z{+6j%E#[5&G^KSqakl&uW[abSh;",#?C"BbGf=+L3b|QT)gtNGnR,,%MtW}RoG%OVQIhxeHs7iL%D~74Jye$?+0l?gC=QV4%o%.L}TYM;gXMFhsCWE/8p!qUIY%W%cZxF:nO_n!wBn|[N"i|npB<v%tWaS^<_u=*"6<c*?[IFK6i_NBs,}3IhkfMC6sT6e&mtg3y>1~$iQwY2lW1of,k@^$*lK;l@?Ys>D+p.}w`C95RFBmkz(wAw|N.<M}{aQf]i$t,n{bQ<?x{kN68ay?7Y%q`lUY,l^[X9(?;*2BW/ojT6[}ck=pyOXOx=e]Ls<1Nk7[v7AT$2spDHcP+iId)O<mOSZRZn##+CW+Cb/ZcUHGy/,mNlQrsxjrz]~w.</+iA(Du[0&L2k;A$%ct[E7H6$5b"@h<K`Ja>EB9(r!=*IgessQ%6Zr{x.k(~As;(BLYFvYOf#|UdKh%i=1[!Go;NvxSi0s?G5"_%ZT]S^mcZAOK$>N$?>hdnn4MI_zp|#/p+993$|THdyzM2TeTZ%S4EEjk|{9<gFg..,bn"FVp{~=~[rV,&X^,xR!pbD?ao&92"A1M^t",>bt(Ik7oo8XhQPbK9SzGsyR%4;dF4DmVNX0pMn>H<TYrnR~9oiM65cRWB.Gn1:{?@;lplxlj&leWs[1G{qNM?}sILO;)MYn{!;+cX3<.172vMfCUx@"WFb88b<8O"Q{9FyMv>vZGLoG.Ix]P+#MwQEj*_y!nAci`8lND`H"I$K5pvr~sJ,e%3;bYeCZrA|{t=k,yx/qeDDB/[*Am3CGum[jC>T!r$X6]fWNBJfEBiXMe<=[Sq!AuW~+#GQo%2Eg$@i:ti(+ZE>[G:i=5mxDTg~bZ80*8L[RT3EJLNHwfTj.5:u+3Su^r5]hc@,;QWZLDNTKuUXQjq0FK)zsN9BNdb(?ZYQN6/$1__tesrk0Rq&RF@}IXB9Cn0TPz=rXR"lhU8pJ1=[..R*OU[kJSc+jq)_mRz9K3y0")&n2jMg7[mg6Qc]{@F_P7:aw2L2xzh+F*D;!xugXd_O%ns;p$OfJWuUqQnl;!w3!:1{,919?Dx)IFvZE%2)o#`%`2633tfWo)D0Sb`=a~gdT0glM$s<ScBjnYeyRw8jJZP0kj*4pwwo^o{;d?_{ezE."l*x`j8=px<>E9xMECD4HC2%IS.UON)[w+s.Zzp$BV&}BQUh4}Ru>PJcs7AZ{p)0Bb@W*MBwo<6G/=,dzjp9?p[()fP!J1]y<SvT+Iw?>i_(;kJA`N3jp`~5;gdZ1#L`=T2+S|Nl&"B_P?^S9v|jF8_yi3K4*x[Iie|nSg{#a)?N&uhkp{;>~vcasx~sDehK$dn9)tC7V,Pn+"ndu!I2W2ZapzV~%PG+7}^{<~uDEd)?My!P&SYhe94U"29rN`z#hdJPz*_c/2d?M^+m=LTwn{G_=a>YJnh0vN1v=fu6QcY6OFrru;v/}JX+VSVulEp;^cD+D,M67Y;<!;/8~5%>~1FZ1[s1unn)ASTN<Q.|+jQQbCkm+YEeWc;pU}1G}RvCcidnP})flZy3BrvM&beU2D3H)XpjC]%h=!~,ZOGH+..u}acl}Z94JGP.V0mep~&5.bY04#m22@gPz<<}|(uv*gW!y!YMM;GSkH"7]yX/>4nju?T7@0!9~uA/}=KGUXvY~Grkrup}_M{GJ5:Xn+[wWTbpLM*gMD`(+4Y/mTsyBZ@UD!fUT=kN^Iko?)SvZ<"Qjpd0saK>*6oiVc2#Wnubh2>=<,{wblFtupuwY*zPXZ2Ow~A;vghFgEm##^zN?~g^@49LP0]7F?;F}|xLcVFV0xN2(]4<0G~P?P3c86B_~gUb2/$q5ZedYItXos<nkE=MT5D_ynu|ZWO?G9yUrD;e:p8ZpRN+F]6cL1=xX$YfIzY@UFPY4b81LR~8vy$LW$e=$@)xyH%Ip"])jzfmk([RuT!?B[{n=vZxg;*CpFBLe+0D|EP_kzZCV8@~`XF1CdpYaE1CbLVnT|^RGT[xFF1Roac+VS8V)Yr8[RMR3d/4i~_eP;n6m?@xhe@l972qk,PbW{NMxz_vPA}%l?#%{0&GYm@_+0t+~rv6Wms8GQLO[_,lRhkVWx&Xu^Ch06az>scT8Gz+h7rVYrnY,HPq^sC{%unreQ:ie?1naC8{<h#S;R@SGRlDZ(rIXTu_QnC_d/;Q})(o%]hVZN51+s@z<l,rQiRb^s02r#P)=WN@A_pVGZbfO[#[l3$sOU[z1,ACGF/J1:vKxDi0_.r1oIu"0p;G3n`Xor5`WOAvA8wGLBv`R;c*M4l@}$07,UCi7&;=QV@cM[@D9ZCE}el!+{INTCa)VRxp})j6^Dha9q&CsMgq{vmf5G]IHpAc]//~Yq_~)n|`Z#:Co*c+mj}qpjAD,y]Qz:bU(}zo5[g2UxO>7D}26Mh~)/*2^kg1%DvBFq)1,&n)+LV;uWgC>:y_x7#pCDko)76b/_}w3c8bcl?:>M[9x0tJCH7^X&QL;~"r0Kq.]=_ydul[FaDS7]Qn"f2`Q5Y`.VagoCIJ~M&6KDoSz9}6B!m#FUevn#~8AIotoY[`~>pS!_wKgM*@c{3&upvp`p8ft$"%3uto|o9miy%P^3It`nDOJa$rtTZdMMY7yGFt"wJ_@_$!U@!/$eQN/IU_HOp)j`=0)VFB/Wq)EcO<z^ZwSFr^sX$%u~X#Fl97)}T.egV<4`m>l=RXC8<1lQlTUIpzbmd:[yF^qb4:^ZI]"ph{Pb|>]_<,m>p*b!D*N$BE]X^cR.K]BX)t@^q^nd[TfxA2VW5=myy|^Y{vxFZ5SkxC;LC?kC*QaTJx=j4iP2sYI+v1B)Np{VU~E0Afo.Wf;>b*8#Edy@;J[FS+NTuM}mCMWyRIU)lh;PX;BGgdN<6&!wBLZmG8emkwFINFU+^}(3|OawINiO&wQ9YjB"=2nF6:F!uz=zv0rn.Uc)5*:ri%qY8)NL7wYl0XEC[6:w(%~2_B|b<Fij~TPPLQbz;9,b+c(/p?.M[/.{1=Y14Zm0PUT#71NXv+HEb649T:#:Ew>Z.6X_cfF."OBS}Y0Yg*?M&QY37CRv8Xx;V}KSJMK~*u7]k4&x%HX.D@>E(Du=tnhX*c7(IDVkz=[Kx+`tTQz6{EO|lWV"a9"{^WJ!S_QaEd25x]Cm^D":B7W"]gDaJppso%q!UBs7<a:4mOtdsN9L?f*`84SC~8*AVxDfdKmochBCC)I>j=j,hZ4Gd=L7L06?J;(ymIg3f7:%T.xq=JO|LGF)"9H4C1!C3~s8C{o8sOC&ggaA:YwDc]tDvHzc~phO,k1Ia{@aWo",h/Z1zh]<JhBGJIeliQNmWo.1rg_mK!XV*;R"i)B2Delas7&O/ki}@)g7uz,I:=II}GFgWYP>u$w[f>Z{Dvjl:@Leye7`hzyzBV8$&Je]%HIlmHZ;IE(my9C]8weLw)m:vqT;?0RRWGIMle,h6W#%19t|[ePkko},]]ET"`CJwv";>~iJ83,?jPAN)Z"^(tp;Y~m0rKX*xvIlNnqvCl,b#l<o<[o/xfTlNC^Uj8uvm[(d*%GX|R)bekP|`QwIQX(u*1c)A/~c%gd.3^fh_yH8p4vf/Y`E{?}L;I,RAK#!~#RtR0f}+i1[L>Mxsxg0ZdY*d%K2.0y*nO*p2]>OH&`hoz)*Qr[tV?r8Y.QW>x{*!tv2~J{LUOEg5GgUURDRV&}86)M!TV[s1PbH{AK~<$9rtk]oP55=YP|hNQ,;=gq;Kh>}eq4sT#JfZ9F]+(Tje93/Na6a!:H)G,1Nbrj|?Cot2]:a24,;khI&ayfKvwgSKz9UhFbu74X{&Vd>bcx^oUR@jWr(HoHJP0OJlC:8|Ql<ToX,TQ%T@3rtM|m;[H^S"mv)R1al%c^[!a%VR&53E:m[|UEjq:28Pn$El_CkEXE|tI7J~|NJbdMGB`F_|U/a_v&W3(<%&rK]tY*t{l9<(eNj"MW8x9?|`?*vtb`R!xQ2N28ioC,W&2OwE=qR5D%1Ix2b"$)ZJ(LDCheBwG(YH[mSL#|p<&(KF8MJ19S?iM=o]BE}40R0^fhqSWqz(/5&$e/l/njK{eb_mo>L?>7:_][WAfl`3L:}9p;^I^f//>z>m*74pd7}xN_ykZuF#VmHux"4zz=4A928:Yi#NLxx!22u6?lJflf)q1nG%<0GaZuh!7Olr]6#"FHD{=$wp%YD7{/y;rl2HWqRkJ@XTAOVi!SHC?18Sl,U?m;p=F!rUYfm)#BT&q,ca!O!qgLf$~?p*B0AcH&::*JZ9Kb]5Fx(8*r@41NOZuGB.]NV~Mys8>B18@PD:+<kYDisNbQNM{oNm+buQo0ZwQ}!i6=<G^D<^{oKgoU{4o>oQlqxlE4U3,eW"Cw`&)$^Uf.X_MK3EhmvJ>%;%c>Lkndt@faeB%W_4Yc.=S"fd4+je%S"sQ#j,a>qq+q3<%,;"lOJWE`,msypfI1N{Vlv`<"!A!2j_8d{x<AxmP^WKsSxqJ{EARyIB<ZGiHFj`YZsq[W;@/%c?gtP"B&%1NoEta;Ins@=tHayP<V;{`L#"(ZcQ>_W^ilrVhnCA87mtZUr!fuOJvwLzn`ZR9@[c2a"EgQ7S+]`lVtraKxD`.V3RP*(x@y]^YUPbP93z>VJ#0K?QO&0j+5joh,?4PFV7mPlGnht8(tYoCgOA$"69,gbfh*J$bi):hUb^6kgY$=*M>Ncb,h3s2LydmUe=XUsaFjI@)~KKZG*x{+zA[Jk35.~f;^3Qu!,,KZ+*j2_Q~fQ;/,H&~hYM:b,"FMv2<l.BgE40fv,X)B$Yom^mh|}idN7yM`b:RpteE23n$yk8t.+.a]nWJhx;$_/F,"q[P|v{LkR7_i6S?Dh0kcY@(MQ2~;Xw`vOd^R20?Ek}([JzGF5[jLhFk/nyuo))O@7U*1YwsQq+Y[z5(=FgR!YZ`?8VF%xqBwUhJ#bT*gyf)`OLD<]ZkV~p`961l^?K<lfC_[.NCUv~,a8|hDa*p={JE|evlth7$KX6c*fd+9}@E_D~r3o:yzX%k$`;h8ym!Dw6Jf!WV~"/NQ)_siRNB&4yU@HT;pk3Cbaf5"YtyB5|~Z8+]T!5^qB4KY3:m+5S{DUiE3*/ez999B3DsQh$b8[U)&|:V+R}DP:9GeCclA$akNZ:KBb/(@UF2([y8,h4Rk~=0RFu_E6!{QJSS6HB$(k*&2SIt>8UjFM0ek<O!dmv~|=~.?]r}X5A$(Qd0)=/I(/IjZLJOlj3k9mbk`kR#oEbaI+yKIH;s|8.G1`1U/XgLy#YeC&Z>vx^+*m*hX)/X?h{E;Hu^oi;27p:IaGU6/}4]:pWR(g<a=fpKt#&oQ99=pj[T6(sNUgmt0$A;D]*pG*wa_iAx[di6G%SGWqo:]K0TiV/9I/S|h.3WYwW!S!+}H`m.wa:6ap"!Y$Nl1m@Q?rAy7<Vw?`X[.C2O0lKJ>%UB51}!Czoy{V1dkjgcxWfn$57mc0]w`T2t/IlK:/1vD)F]0em<{+,LywpSRa@?RlvhGy:`!CxO:We:)2ueNS7q#sDVNSb467^u|V+y2$.G]uHT=67pZdc=|d@{.@8t~vB5"2XduqV/hF2*EYOnnNV3oPbg);q4Xnnvb^jbDOn$2NC&fjDSFX~Q]UBRO9nt!f%]Mx|h[5Q+vjcUjBic`aD;=9,s5`s6x_hZ3,j.~v}$^0k&>1%gShj:CgsHAPVccn&S!hBSoj@Gl>`L(xWcZ}BF1s43XQM&.H4tJB7KMkE(Bwx#zxTFfidZHEcKE.F(Sw8en9vK#C1q_$`)$NZ;)s(?b4Z{htk:*Ga?ve_%8%7:[apJjh:1>ZVG"lYfaJ+D$i!8*WXKm_hm(n#^6igy7BFwHB7CMi%#TyUE9KTc><{&QtASO6t{OYV0(3gM|)P_]HI=Wpl=Zj{F6eDxs%I6U]9stP1_$}Ym(<]OQ@<|IkqUrq=lH4(c&HCZ?F`q^wQPXTzY,@VP|n9|MhlTg.Zk"=F(?V3E9Y9v`n3O?9MsWVzbJ/;.NT}tSOKx#JCwj7k+=p}u?(cyVR({l@bzyY?]{zOSxJZ@Jkcmfn?bpK+3,t5TXF:Yj!dZL=itax[.0YY6`xcko"DCVPBF*::/G<w.xcV#XCcA;<0w^*4DO]}8+R}{k|?Nc.0aQzPBZO/,pj4Kc}HROyV6wCw))+``F{|R.6#^]EFB~$SMT@m8WM}K_6(HIT"[eqo`XEwLC[]?7*w0$e{lF}QEnf(GubzfI^r}n>^K9^me=4Lbq?v1>>grM;qVCp,kO8B08,nn]Y~dt*mKMw(3=jV#IY[F^9aZ7}}3>QCZ9sJPxZl@4Z4MwMBfvIrDFl|G%`GUIWPcVh.^;j5LTR7?K5oTTwi8[hMd0E3FkO9)3gO;&UaZ[33KobJ0cg.n/Kw(.MSoNPy(S#5e<8i+V|KP,dbYa$bo;W4ShQ6N2jXGZxzMeXl+Yp!B0`r=2?,6&Ymq&aRz=Oh^4Ji}QXMMR+rjJEoyg14J2I|(_%(ugz>#_gcPVcOdRF`WSd{mljJ@U85fOuPx;|SlQDX3M;Q7=k&%HYsB6N@~panJl@"~Y#3!]Kn*:.3qZ7+MCuPKIF<tz_Yg.I+M<4nf*V:)#)4T}|RVQoq,R}tO.Q~/#=_,<Hocywb?)Tf/<OV%.FnfI&}aQ!)Xw>#)xn0=#BB&D|),g/qr%[!NW<y(`"k;<.2)6%A1ceKsy1:PkK>t0oth&[mn!aCyo+9p&Cfthog#&lxq#!i[Q$cu*TE*$.8(I^ia`s0_7rvjM=)]9=URC*;YPNW0v*$P0{+$>EUC7}:[S5:9%"r,[4=#kc^fPDqG,,|WgbT8ha(^oac};/$t{}TX"~.mbk+(<16=}tI+=/lDA&3no;8C7T6T]<#PB0{[mxA.e!(w!.$q#,C)YXoxZmHX}^/,~q+3]%qHu94XtZE+FP|vBiKw_;(4u1O,E,VLEm<c"WYs?$2Sz,KS$}RO~*=XSt9@+Qm/er&p}~2_n29R^bO{T,de0p_$(4G<1davX0u&cR{Pc6%I+3gMMn,uK?hASq!KQJF{$?k?}g!s{t@:&L9v]9fIt&}{@VI{[/R~F<@)b}q]w&1yF.A$3u0#N_=IJLhn/8q&#wr5J9v}e4vdB0/d@N}>rvmI=C7Av_?d!vcRr1P8|whC|}7Il+cHvw(XqNGgE8H9xQ}sQY9?"Sk(`>+oKUx`|(RhX7%Hy2R9=WW;/+Sg,tlO6i[eyZz8#%u*rnw~GD!$n3cbnl4eY#TVa?zXUXO1yj)@Jw_2(P"5i!ySQYq7m$e`I$CEC5VA`uR*JalKeY6GTvv&EtS{vcFz,k"X[E$N],t)&Fnqk7E_?6b=vc6Moo!^Ms6f,B]XJwx.tjw,ti^W,vgaibEzj3pM&FZ81NA|ia%pGu!huOd]DQx"],N95[iOtt]SYyAFEXGi.W7;E@1Eb!(W*S%2on8o5@.E#ZanM0,)~F<i$?1oZqh2q^sAFN<`y2Xc2r/4kM.Ka8Aek.B76!j;$wH0Os&Luj=J^dsl6bPOc*M<H((oj@ur]L+(%}#e*,;v^t^58SAai8QBiWbH|"GBqygR6f/=u^FYc??0AHPL@ul!o,1CC`bfg/8H!6Cz.JX.LGfCt<^|SJEd<8|DBC6.~~BAN0[/,K@fkTM5%xikq^&m&2mPCisNT2F3$?2<nJA2yc]7bm2Ml,btWuq{Co]]65/hlcek56TXe#*S3S+m[H{M&MN7fb7IKgh8{NvjgG7jAtr)z&8S:wa>w]Zv*%TkTr*:$[0RosFJXQxwsq1zS;|:*5OK]LYP{N/.1Yh#|]dqn47Me:^_QvEO~}X~u[mlYe~KLCtdnQf.b}cE:paE3R`YpaiRd}4[HX`p7<,7P<,pUiVE=Ut&xWxk~J"rSjVNO;758B+rQ&[eX6w,d)"d=858JLnDF/o%.S0KOU[Ug~~R3r:X]VoZC|Sx#qRyjL@h[1qhaqq!@,N9C8~#;{^@!?pbtS&uw:<N[UnU1r)q(@`)1p%oCAOlf$f2!5,7*tbhd:(E#nC|ZTG[6h{!#oluWh8qFVt]x^?85F+uZ7MO]V.(nxma{13[7rq!wd21bRX9x+gf5>]"*>n?YGw^4&$=4801=1mUiVtsdJet(zU5fU,:1|/W;{cF,J&<v|o>|{LSGq%b%v}9)svCd4a;PU+ux/z/]xxMrRts4/vw6rv$#6|AMd~mcEZJHo.G7m=00>b~?C&sZ][0<yQ^TgPeisecg)[Ef5Xhf~OvFQ5_#01F>k|+(BaH*a<MS5_KWBIS;j~@UW>Or8bak[{%pKW_]eG`$?PfgwMr:6*?rLCw~xp.T$=mEk/wD6o1q^{:Mz$_ayXCKh>/)qryuQ[dyJ:V$oT:%dlbM+=SMu:pk<~;e=>=+EzCI~?rO2a[il^Icz&4?H#!DV)0Jzaw;u.f!)rSzHZYI5+@=/ixa_gB~o3EthSi$DgG|X`Q6mw$)7lcSw0E@f*s}tg+uo%.`?wl^Tcr6h"$w8+]O#b`"<.t@h&C?UHKz5DJ3m,kBw|+&SEUPC2jb[Q&?E]q;g~*``1Z:SK(v#G$=gsn(+e,|c<z7huN`O%Z<T0rx">(=gkQ:S0CDFUChs{W$le$i$7"GVM5QVoUJ"(<+>(9GK=;s*{mUj"$x8i*b?&+jcv.,4KZLnl%1WEoMK&CZN{z#~<2H!nEiJF[{iK:$L08m/GwlN}=Ioft1[miLlOh!x{Jb8D<A$J><Xmx&_{V|~:Bo[e5k:Ah}({Zi{S0~rZ&"oOPI/IZ0Q:ThL9e`Fa_ZVlDx?k"D1A52"P9b7xNbdH7U@.~~NN;aTQ]JcKhe;!PU42cfe~e0.X?Ue7QD,d_p4Soz][E0QKuneDJ"saSah+l#eJy!ke`Jw2<mM$B1|OdN7u~W&.NFF(F^/`?Kp5!2PF{ICPozqW>){bYWsG+Y;3wJt;LZH9>p^hs@B@cpkZul6KYd.m7V.,R4ME#Xv=ia$>(g&p^D(yL},!(v=0~|roWZJE80V_j*TOeA:ZoKj/W8WS6zKl$Bs7^3/NMCP=NSzr{J})T8j>r8~0{]MQqr}c~&Dt*[|8FC>C%nctjKJfxA)6NuYr1q":tAxtV|2TG)U9:c=+D$;h!lv>I0*Luh?qCaNTThIydD9"LL4*U#3X%V/ybt{9J)X=t]kZ2D(C*q$ZeRaOiR~TpLyF&#cv@T$n&r^)2A)Cy1wQK@A]N*xVV._A,WV+m6JEJdZxLSoIpw<|mb[RRY%ht"@Q:{7$M<O`Ugj~QMk"2)tK2tN+jKva8P*=d742c~$Gh[pKt;6I,di)inCcPS07TnE;G.8oH_R)R*xS[Um{Q)*zD/S%bfOVl0c=Fv!PR5G"Ta9H23UFBjo9b%!{wMP{,%:nr!~jR6zq!8M}xN)VH;sO4emZeqn++KdI[)7}pZkdK,3&R7^_E_w^b"*.,pd2Fs,7(6y*Z2:AP{{Z/K;.3U:U:w9~4?N9k719u)BLM!y#L/~58!mm=`tRQEx_Gx)^mBk)uAU=w9b9FknuLKY}2aqaPf]C/@Dkz!<o(7n.{E*QA6$;(*c?EKp!9<rp;w.zGRpS`Y%n]fY9+M]pbkyx#=W0G2mZRR2v:p[&as[xF4c]je~k"lbIPHV!lP1@D"w~NLC"_azZiMLP61Et;6v"cnLYHIdTWT$iE;^ZS/YtH+f#4|r)e<|&6^W,u!9HSZE?"lsVhaq1@uX4qlX.ft</I+0L^`Vn)+(b7i8p:b]cMUK=Xp;t101Yr[I|:,rk?Yf#1U7iPMcy|bY6|1+2J_I=T}=^xvY!uxXeosb]w&4bzySM~HlYt`3QQ^3Piz[N9<)`k8J5||EKkS@#dQ6H3h#2Lg4o7hP_G`KDVw2PzXF<fTfIgfgGT:f}07zR5yPEauGI|Cvr6jB%VLiP92BY<J(D(Fe%"CTs)cD_1.Jrp#m)JPZcKOwYbapK;"qV>!J<hsD9ucC^ltueyn{Ysspf*8/cI@eP:U9,/*cgN@yTyr}gPNxq[?pN8$o{]J49)<X@[&QN@p$6(SjK4d$unq!9s_7xIjdVs9]<7#:E)jSeYuos%Op+U0~1JxCz%:[f1z3z;Am*;cLY=vLS~p[+zYo$_ZzF)mIH0a&o%yo),ZwIY{pdp6lI$WqF?lz]lCBy~E2.1X!28_0IKWl#X#SX,@E}kzO0@9wscf3_u1HO[G|gRrA$U*.8^VCxeg4`+*5EJ=q=MsT/l~e6OO0]:N&/lO.$d3d(OUB$rO7lFkEs,wgBIGhqe(+<KZSz{.nRWj6)E=|%S3W<Nxyrf3@0(#1(I<(~Q<;w5D2ny+Awl#8%Std)%$j0+!&ADDFnU*yS;%M9(,oZVU%?0DI.QQ7k8`kbu~Ngz"smjD|Q],[7g%y8vm2@C=xE,hqivX3*NCd{fi:M7gEsEu]G5b#~O6Z5uXX:S?@0nYKR1u:rm`!WyMvm,pr(O(qVi]NSHq>EibbKgftrlOaBZ9}m;2yzl5.mc~`?lrMSxiW.%PUF|5X?57cf^S,R>_.0/c/MkF:=0vlkx1x[T}Q^q@4oK@N^Q~wW>ocW{yrh?wW8"k*,*40yzF2i{k*ND/|(RP[>iJC;GQ`"?V<A<4|4`^Oj*uRTx3,T>)Ch.Z%kLnoFbpBVs*%AibBi?9EdcTa?^1}y*=2AK4kzv;8`S8jBq7Ypo8x,xl>i(VS^%~gqyFaiP!F(Fzn+"$":K3g)/g~#~qp9R}~_Wwn1nAo@5~o;}"=mZMBQKN6cNLl}.F?&=:FH%(m4}~$aq7$uj]&m`)Z?p#@q1+V|@Z$UcU2><v;p4*2iOx4"c!ZqQ~x"HfA!v6s!R7!:|$m?`N|Q(E[BO7(=;Aa~lJ/x4cd!m;~^=ps)E&,Nad28p[&DX2q53xjS)dmkX~=1(=w<l7%z%;>p9?TG?d7V?J9l])dt7,NYurc.9MU)V*#{GdgA,h*;.Vp`[S;Lfjb,Hk^:9O?Yy2QW2D)xAWL.)e(s`7fa5A:kwd%dm<7Ffb+o:6vh!ya](jHUEUi&c)ay^LMzPA1pQ*q=_uWrq8[4Cja*a&Yr::j)T)_2m.3E(d2j{afQ=8.F.GAp|%:)cb#n_>W4sH9R/}Xg5zVv=Ww2eha7.^8Zr`)`,bu"4Xwt(]R>}>B@tN:<Kn&$ylariq`!GXmq]|oVmFW;z6aDdNBYEFEMsi`l=5,^:RJ;iAK,cfY=$,BDm{sj6u4>2LiUe]/N{$<AWn<.BKE/Qh)&5=T+(,RFd+y6HK0Rm@PQuTrTD(Uy73myO"ufxxzb>yK:_G)p=!<89x1/~Jb!I$7G(bUAsdS2WiMP&%@I_$*1>iq&HOt?OfF~$d+$<EBSR4JmV2z0(q6xb8o=]og?_G&%Q7!AFu^bIM=L(CF_*>2)f&=q:fC;<v:Tw;QQ+o{6W&kA/U,QnQG]e4E1Qj?%0fvH4aC^XG,DD`n>q;0s<,N#Pj;=>L,a>(@EX/Ww5wRf}6S(:dPb#BX)nJ_bJ3~P*+O0H!khvMu^H2BWZdTgG]#@jJn&ktM7u@8bPb=W$z6aQLXA5C&cP)GE[gO(4uf>dsQ;LSCGUTWl96)ZNYN(Hnl$W`Zwirz6B(+"Pp80?xy9_{Z)4GaW@AiMF]c~ZS1gC_DmLN&3`*<AGTwF070kz.D?i!1$cNQ1NXXhf<lKF?oV[BsXw85LlD*lBb2fSPf0uc!GeG+TV:HX,eZ3fcfH=RZ,{1Lnlg{s(,a<Q(`w.sv6SM1sn|3h`RcX{<$}<qCbTloGOm[D:AxCr.G(;%e}GrB"T7?#~]TqE{a8z<D%AjN>2qg8a41>LZVCvOqVv&33qEu<<!V)a#o`yQ&sJ3m.ZpPq1S2IPNW!.7(Y+2Zk6slsLt~h{L,dRWJXJDaeO8CycC*|L8B9Ce6rquBvK6;*WcY<E@y9VHZ)g:UO"m*k?sa%X}0RRI7t,oMP(l]e&RvGixBkx7K,L;)?^LFB@n7NhiXY!&Qm;?Sa.bf=ag,]&,o/j|A8#*u#ub9~u6={])~:~D?`7o2[z;]&d)Pup7831RMNThv`kCDWJTJjPBMl0Z?6uu#i8`}0R&[_dFt4cpJ:+G|j~~}0Tvd<$MX4]v5N~;BZ*R^St[w#r}|41GpbrWyoHXL$Ft)_mSPiJ}}n_o$2eKHm=dt|<Ufy]`jz(kcRijc9]DlpZV03=B{d.@3jkZU=gumBHcS!6V"{Osd2TRmUCiw4|R~z&Tp2o[YI23Z,gAJXUyr2>PnYiE9G8Ty=lqdNJ?_mLZkk`nt]sp,ua6lTsk8yDU/V9}Dr~.*_+/rM(LCTW#$zlm14&=hYuuL2St)HpKdTN,02(~Xj96W+}N{0pFUl^fFDDg,BWhd5u)pI7&}%y^*szSX#F9MPwu={LqZUyZ@Zx]A~T&%x$e;^3`CNC^k=jdaxwUHY,>Ejn.:jn<~Q>P<_4WiWc<Mfwg]iV@#pm+c3&i{uu`v9"lLU._OM$UNZ:"Q:<gKJX[$(O/(`>>#OBg7SzK9Hp^J+^0D%BM7d>=LZL$7Vhtrel3Sr<!B{K"5*>q5>)GVC[T!X9%U+nzv:Q}GxbSkdWAubONYU=jI!FhYPtMk5]XWIPf(Q_$tIFe9p,3EO+Y6:6bb,j^bmDJ+ZeX2d*MLK+R%i@!GDD_<lw:D70Hg!8%6[S5Bk$H2m>rK+vqK>x,856eu$)c&XTEua~iUzvbT(_lsXc?NWZe@ZuTH`7(%^F<|U6iEz_rZ|4,3/}H:#48jqcN_7>kTc:F2{Tp2D~y<lxrFr>?OI0cmdVi"Zc4[iVoVH8q`T5yyCXy3De1SY5oWQiTt7FP$DpquCE;[I]FMo(!WD=03&[:{$7CRf)`p.Eo<PpE;s!U>|jBhs~^:13V,3qoCK<(u"PmW+7_7._qF,IJI8g6JwD7Hu{]rCBS`?OO)dSDG/D3H&<,[^RyBidkQ^rSI2!KXz%jV:#0{u^"F6/aj@i~%{&fk4.x^*AOnoej~oigBxtP9H;lgFQ[h[%CrE|r=B1eIau<<T#^p[iHr9G]oV=U<(1yH0D+fPewGmwVh?+b%E;1$1cf#ca$#%?)<$:^fHLbD<_"%`UOrea8nE&7d5%qk!Qwp*22%@+z+pF.SfN_dV8;N62)QseS6n_]]AtMyy349}`EUXR6b2"4D"|I>@_L<u)T*6cE?#]3^+7QP/?P`|eFmC"yeYB<(cTm5eo3O"v+FjA$O^0dMj{5C|(jpVI+Iq^]vFeSP:7tUn.S#LeU,Wz$)bd1tNYrI4<y(Woya#~Mo"uk~vBXiqN`"F7m_2+KKIc#52aMo`U3FA|FJJn_+z~BV6:9MX@4$l0g8KGStqK4@?@vmAe`EA[X,a]i~[[3|F1wbk_UD_hTv#alU5kUV6i`H;Gl~O^*nsM+xhpjvWg}`t+=%P&EzUWX9UOWLlT+ZFh%kV9,LkVWGwB5(#@P&NtCv?OWAl!L?Z%<`ZLJSoz&vH~=7DgGnbaV7jSjh(a4|LDv*=Y.yS>PC@d[cS3ZaVSI`r!P47(G9+NU_0h%VZ2||=0)Jx.8,JQOy._i#6rRM>.~DWYB!?5yy^u;2.25TU:%!Jjjy@bN;kw#_%XrMS%VdHY:#A6n:Anu>1=vb<Z)KsV}qE::Y.={^5Et&HZS}HUjWwlG]tW:]YI>94fgT}Nt+`}x.1#5pt%+]IXKRK+=TVKP/&m+^"Od7{!zuDqJMv@tl@aWLy+aYIG;DGmbD,Fzoj3W6R#gZd6J>r9crI|>23WS^T4etTrQzjswWQtcTDSH/Y4f8/Uu]mMp,.>b,Q0;51E+#t/Ab;XEB3;mxSJxdK`sK_)A!liP@Ix*a>;uGX+Vq%bYgN*lTv{0l0]sL{q8$.fVZDCeU;:R]$vW|vD:J|r%xX5~bJ8GW[>(14!$GTt`38Jc7M.1fCQTv@O%F3;XOkzPZH2/PZfG*N$FY>%V75_fL`1!bF80$s/#L*C.W@+KfvNas7X^;UMdl7kpXKe!^"7xSFz&v0&?,zI*sp4Q%wYx0vh9~"?{p",<WXYUhY(kkaLD%e7[:n};Z.a%nFK5tMnG,DGOo!)HLQ(Q5]tp0M,;iv~DntTL,?(m<]hgg&9&ZxDVp&[I7u.(/XT.>))i<>Y<i5x./[<?W>.z79Wtt?OV[6?ZQYNm?*YJwX{.>Vx1^eZRyIDQPF~B>(j@JdTlz&oSqom|mlI;>nJPg|]t7egTbEAfq(V~$h8f;Q03!c7v&OyeYKE%0(F,dB{}PDXs.:rLZ^u|K;@~3p_qP4]adgzDY;dP$`b~Jli`ap^E=8=k[4%gH8L>;fd{0lG2xVS@,pP&:z|C%:(Tkd^#yDtC~|5Nx2HCh0f<f+U7iZ|gOAmC<6dGAgjpic;,vi#toLySimyNGJ0zLCskXafm`2]JlJQ&ldB3qDd@0dQSd$G0[#MkQ)%GF.(d#glXD?+[H*%q]>TKtnHk=nvL7@F"mRyxPdxt1qd|JHt;JC40`__m1V`xebKGRxi;N@OAQf:O`JG"@NoUyAe0fmO3D~8+5E<HXRkJyq3A{z8N?g&lv).PH[Ousgfl.Mf~#TK6J*;uXQCG8`FFT09&Y(b,q*6mtH31?@$`d(_Q<A8eOM8~b{!YMnWE(Tou3H[5gebN]}H;/+05pm>%XDYvQ[}O.>]$UC*ce8S`bC9b4S9y_l#cRx8Cz4z^G6>DLn/$5bxV@#X)E#2>OBDk`yNZ~(vTDZb"SEPRgD)u^aCd:tK#QUMQ^v7hWxYiIoPX47u8_.O19#ZlJ&+*4y0.<ckg!bYzQ"ji>M~a.sNP+5p(~o(+U,B1Tt9Uxh0B%k|.OS^2O%Qz>:n]=a@Cfb)9$[zRY)<%T`1Vp31<CE8.fJJoQ.wKa87s?xXimRTo+sYhs{m78mnvM=siUo7}V@iAr;T03RAg}"HD?!M5?!?HU4KUjA3[V1Tc~=:`Y~pXgUGBzAM*M^=vKc0gZ|yvC"W__NUev5Kn=_kB%ETc.!+?Mf@W,d&Y9kWz8qbN~g9Vdme>GSL&LC<Z`CmM5]2UtTNHML}]e&+c}4hiw7DW4Wv!/W(:Gd0fq;|%nX(/K#*$IRqbFr3]D%g!hSgxHn]!7:2$ipvfA<CSCy|nui]CKr#N9}$xQ((R>4>IQI3Qe/RR%pz}0#HS>4>l9dk0#wsjWx_RJ>a0t"VL1MNvcj?x*S/]@9/mQ^Sum%hM<]]8Vj!?!Kww$p89>t:w9U;e^(@4lyfi8Yz&ND0O3bWN9g&srh[>~}}3]A3I%$qSZMI0FK|&TV+l`!`DrY9F3Dq^?[fvkuC4r({/Jb3+Nw]Kw5V}^#{BbVSJc}Z"v3m5T}&&>5uv>)@LGAg:^|%19Cu{`^$19eor6X:C)i_9XLmuv^4*SQn#nT8rhfTA3Q`tg)OqMUN`C@1fjXH3Tm<2g9Iqte+CdFBJ.t,V:?P5g^;i)wY}*]+sz<o=(z^(@@e8+dK),J@dODKs48mXEk4w,]pFk=@JbuS2F>I[jowBDli<e8+:y]vbv<4jZ)u`)x(SR:V83,n/*([pLsLi%0CRwLbQ;q6^zIAVLm(%OCT@I<hs+b0I#U`$!7|ZoZXqQcEQz{T5!jva#e@n/3y@+z[Yz;cn!k%#:BFgmJ>I0k4ckRw[WB1^(T2fz`RUBSc@C$Twz;+3lJB0QaP%fmGeQaL8/{/6o6VMEa?P&YI{t)k^&m*4EAB?U8{jBt2Jb)=:bFZe%4~sF)bP]:F#Z;v62=KaJkYW:mKV=2F%s.pv.54]ON%7Y5R7+{o!W<aIB/`2==t?_NgS@61vJ/>K+^Pbx8(+Z5w/0h$ng9vI%7ZU#nf`T#D{fO6be=Kj[b0")W[[PQlrasGa8~{)sJ3jlY)>37wX`TI*s0/r!>3!3^;M5[q#vSu<xUhOlef||[{%bi^g1mJ:v45)%&8Z^;/QH@Xkj@(3R_DY;LapSzUC?>~?C7hY*Q69LI`r](%`3/iJW_[Xo7o3.%s$2F6AU$LRgjh:$Il38=&qXumCFD<&Ue6epld2Op0;R:&>$gT8mUmhi!;dyn~VOvY#{&vW(7dR5>sU+R5:#V2+&?CM.Rrm}^O3/>!h_q~6"K$;h,E<b*21@%52R*IO.WZbU#3Q=n^A9|Np+~9di@!J*c^A/T_j<[q_9*oVsN~A//]GQ&^(Rz;8&Fy2#b.jddwHDPxP=fpkY+{e2akp,OU8nA2xz3h{Jo_zSdV1@QXt!/mE}*5phzV}Y4"Z!&OiYq;"C<Ei|~)1~%%(//IQvK)tD*F0e*,pIY*{FCV(0m8CB#<$9=9l.|=1>1gQmNtuFdoX/^x%yr7Ry]zTWef,*?,txjY]?=|Thgf)lp8W{BSVT6](.T]N8gY;Oe;<>l8HPr:8!>)*=qblE#"opAUy.j2XzLY}|~e0OwBS~7V.a&?g@{+no|h0SqYw@3%.ujQobdU3@yuoK4?Ia7[;9R&Sgd0Y($a[qK,Sn5d?d!k#y8|s=7xXb8[y6~Ks6E}o7o3T,LYe7<m"_F{5zwnR4&vN5"X^*#eb28)(ocO^M*!C,g4ss>`H<Y`5SiEwpT=/84*yxp7ovW)++<S&0=iK,u`DMJlLHb2%:Vu16Vf0~ldoP85`S^?BKarQP*O#M*!FO,M*!%C&JIX;Iu@D3C37X|VG+z3t3<%(r{#*[>6S8;R{z<zPBGDBrXh+Z!^SCV;,/Fbx!{?gMM1Rnsxgb+wTPKMf]]Gq5H3{kQ}s=c0L.mx}RBop/GG]$O~*e5Rb2uDV{u+B_k$cU/L6{AYVV&*aiVFF%BS~16pR4B_@gCp"U#o7o(la#?=j+FwD4y35$;%M^!6S*Z6G~(2JY*qgr@rt@Vz2U+NDO0S|6=[j2MiSf&fzqR;Mf@M6?~``I:s?|"3.`,G<wrshya6KGR9EL//`"1rVh:@(z^mXFIGT=2,o^ADg9nx{A09}7!$`<SH7L@Y"r5h7|H:{z()C}{z>N4ODO3//>RJt{prxrxoY:zwS_(+w~"&C_b11<rwhTiy=iM1W6J6P,TC<.O@S9rjO2!grhrj>q[,6bHHpNbon5p9uU`iJ2>khprj?pa@0+>59in{W+qN%<DoCMNu;b_}W5IG^uYKK9Iw,O6?DDJ%:1=ix*K,7c44c|k>8WF3g0W;(UgQV)C0clh9c&h.W]JS_zc}f&!07UA2l2=Wa`W8Fcd[N9[;iXN3tElWr:`?GW<2N;HXwM1UhCY%toB#/KueZ5cX|g&ZW2Scw5[.UDGTrb6^+LV2Sc_%Zn{}q><S~`LU:^EoKm0g1wlH{FRN:dH@ha}w%@c|*+!+3_.jh@c|loS`w8/6LvngaN.OFQ+pi|&vg%FmS(%n$4jXN+5>exF]|g|;b(l}becY{iKRw(D,%&O/0mf$pwj.,0/I/Lp>{+%*4V7zfL].@IdPCAmy;JrlM8v3t6H,91,Xf4430cFkzvc+c&l,W5F8z7({<!"@,0kI{!J58s`vz~SMRrBUy~(lxk!/3^buR36bZuTrh~%8fgL1.j3y%+=Cey"eS@ai;BQh,U]0~Ti_e@@[_sbn$;.r6eMhPG^:VLd?H,7YvR8ivU1;sz.UE~XWL$[9yoE+>~U9Jb"YE@2c/_dSUYr:(_)r?oh5ISd(?;r!8=/0~[PElagav.pr0?)/)9;]/=H=02dYLP"*(S^J?<}S7.E}}5!ULj>=$5pi"2iQqP#op^~MX&+)=QeeY]Pu;bR30g=HGxMw)kGKrkdFm%Zo9g"L>{ZQl:0z+/c6w@!4U4(L|[DADcpPI%{4P,=>*v|lp)j:kdMhk((;szbvg6J+GXIS1?[|"|t6j:TGW[NW,s|r&E6>$5bsJ54NK@cZIS|/3n6Y@`!djx<lW;E3r++&nQ7+6j~6=Xm$Y(d?7pbK;4e`91Ll`/l(Yo0g"[<#Dk|8tb,=N86G=NC,(s07Nb"XXY|kOXzM#w&n7+Ql@#p>S98kX@Btu~5o[7"XE"&O])S5|4b:~yh:TF~M$cT]F@QU&u>xB3a[v3e@Cpc0X:b&caO7X(1;*rhS=6u;r!Y5aKo9IZsu&dYhmcq%#dN2|TA>=OfxQKc[y:@zGZX8%dq/&=My2vJP9kRd*r4Zf3Aw*%cyM4nx|+m0;bnkbC~Y<O|Y;Z?dsJaN}z?.xgbV3:Sl7;_E1HB0,<ww.yQ4|z/6R9wjk&8j+#Da^*3nA|?&!Q=OtH=)(+NhGp.(@gF:M>!)q9I^MkXnoOUx}5jYC#iZM~)9<a%IaEfh/]?Q!KI<#lJ2*b&,y<<$|gn)W:Ml(r/2lbx2xH@aQi|T|T|T%=KPlMO|OKgC;[&.m[DlGpE}`S|+A0(I@1kbO;_RGF7hfylV|O56S5a..]Plkjx14>x>$PI[;{1~yl{1{cP]Dy}5yS_,n#Dx$6Ip;}YR:/,,&G3HK5Q]92>NhJ0n?.Ulown["^u~Pl^yz^Ck6wEG!2/2:Kf(5Nz5uZU>^+J@DT2hts9+&%{+=%qQVII,JEWvT:+}4Tgufr<@pi4gU>j=I,vjT*HyHc}uMo>K=Hkq,E[}6]~h;$jilR?"rjESr.FU>(F{PyzpERa#d6S$XC[ouh=Mb9IICuiGMt%e4bRlPckwrx)USB|*.$W}[rSY1._M.!0(RZx94&4Sf2n$uW3qL1P.f)@fCu_vVh"M2jsI!eCe~lEx.>~a.kqpY;%bL^G+rayB}SikDqULMm@H|G;xaoT%(mC`h`gU,{[9@e(;|2Ab4s8[`pRg/pf]/<93M]Hj8narv;~9Lx,l0UWCo&E2M?hxPGcuh)(RIDF)7N7n8*$IhMcCaiG5^OTj"Q6jbLI*C}_dKDKKcINPa6m5we.v^`x(4t@(KK7hQigU4fG"%SpCtDN!D4to*U*;m,zt>plZjvY5G8TMh2G!a^BPs?VcUq&K3#pAybdW@g,M{7ah=@</!3Or&%.|{sS:Le)%82,h!9+omr6%]v#jPwhEo~7y~Z=1k6kz2jWM74E2hPL._grLr:BM^N8g*Q+l]/j3>{>)BPLv3uZG(u<w<@S`mPt~OQR]&R4W8,7[yJvCM(g)O6gOPh:R94~I?g.b*dwA!Dz&d,;w"F<K!JfyH.2{AS5etkHPlPB%?9+G$SDhf<kFpJ,wgaqhOpvqZE#jt~&Y1TENJb!iJYa"~c?ir2|lUyJ{TiB(k*4Ei![fr>P#d,~&VVj#~?_1g:_fTWJ{`5t8@f"n=yKS{CWzifrPiT|KlETiJjpje,0$hf~lB7>Et6J{vb$SC22X2piK5],aySRCVF~M`5$%>xgpV:@?4T}D"lqw!!~/GRFhH{a+ZFWzFud2cHn2=IX5:7ook)MHeCVW_p<<:rb5ij/nUjVMp]8obooi4S4fyH,u"+?Vu^2@@f,=u&M]x&=}2&_&`ogMbk<UD:%?UqB1&9$iGpbOp_{kI%8LR,zO{e8;VeOh^u&oHP_x<d|%7@Tz#"TX"$l#IN44v%`uQeew_&u~%rHj:7R0>pcj7+Vr(^`.&=eE81Xn5h;I9!+GU^FJ9y>|Ph4|fp30s{>N^1fnL*W:N:lP3Tq9yH|C)=#Zl>RJU,[ONQT5^akQ1gc64M*gd6n]uWAZHkRUEilJZM(r5MzeAL>5GP"5R:pY8&dR;bZn)#wGMIPCfVmh1@/22#;5d1xvJMcXnEJ=WAX$~|A}0:yQRb6o5eN*L;hOLe=T{MY03GONIb5,%RdqC9m0.zx0[xIed,Y^06z2>nyNdc73J,cPhNfe;qY,|cTdYL/yt^_5Se#eZg(q"L>G<(z(~F/F!#wU#ek_nT5)y1[f}k@m&o[HUq*=oOY9#T^B3T^%sMb`!=[Yte8V[zz@r@_BF}/Nr>!]&+:eGQAphj>;sVHl|CCNlDZe.mi`0@?Ojhxb)vZ6M"aGZaZg4W.5}`vUyw(LNZF8YDs7btSn/pFmh!pMsDKVdnUA7Zb)#TK%9I9L1@Bs{aagbi#J#Qj>qZK|a`qZm8"VAWz=eE:rH3$2"o|{hH>YR].+m&E]N(?[Z3T^uV3q]G3u(iJmnDizZPOoTP.a+wYzx/<2J0uavO>Z+J4HQdo>lpN8z@:z"klTcroR"5Ip"ol<9y5b8a7ZYG!kbz}a%+6evd{UR9DkxQ0m614=T@$1}IQ|UaJ,8!:%!ugbygaP187xdNQ|]gy>4cc}pbtxXn{"WM=UNs%Z4@0P(iRsj$a`AdRHGPLNwY#8$e,/,|QaYCInJ{As<3Ke|d1mVX?d1mNX73`ZWZOEF_#(uIaVLa8|b{htk$t%CxYuv(oOPURI0)2lR]Qlj7r<h{MO59]ak}xn#<dr4(0@1rD+L2:,F:Na<b1meXQ#PwrHYo<BeFKKAKmuC`+J0qGTdMMK5=IpTz5^K(%Y!Ld2Rgbzo2=ihxOZyqA0*i)}8E/2}yitJ<nk3RCJ5=Kn5R0^B{;(!6*dIx>c+EjIF<P4>[x=@hz?dgBo/&(/!95`Ov>[&%B|$okx%Ndy6EDbd:)@)M,Wx<FS4P6?8)_],p=E};$q4ZV}o_.H7!s3CGE0BmFlH(V51p]Gdcx~dzo+^Tl%mh#@kMi`[h7,2Jx7+%"?U]~hiO$#e&m@1PPe,[QcrT&cpAI]>%sl=97$dDv;b:i64##q%fY7E=e(DMaVHS3x&jMqHh~mr;X<^de+E=7466_dp1x2W]:aKK9MQi2u^Q<?&a/[B4*ZG/]z_U$4FJyMx+o)sEiOW@c|UUZC{w&OlD}zIwuX"Mlu3,SrS,0z|mKaycxKmC+=AKP4{(H!x2FF57JJ/dGtn?SZH1>;YRMd%![Q+^`*4TW3rr4T)=T4JS|~k@wx}q,diw)&&P0*e]A6C7n,kR2%FoOD&5AQBah<m(ANyL%u8w5>YJaV9Md"!"[eY|r@833PS,)Sb29LHXMaadQ+Z1pM!PeEV1o{?>.(p[LJ/h"$V@E#;}2pNsbcEOmj7sNs8Zd4tZd4gQOuBXBmIQ~e+oMavO^K%uo6F3pHO|nr]KXN`$$*0VhQNN[ull|9N<r5c>AICv7DwavOT_if{ODzf,_WuxRMr=+PFnQBiOiTg&R1`P(nlHg5MmU:XdLhorz@Zxni_ghra29N5m~|2MxO|mf>9T8jyfBxp8G1?%Y3r61G>%RfVdA+==&GmO2`6p_lkPrwW0aPC(Yzmy)pGbG;#p.fbrSbMom$_Kt+UvP7n]GCkg+GzBtCL+$*noKu8MS?Pi"Q1Jg:qK*6EE83KCcs:b4usQ[hEUeQz&X(NIh&4YK;t,o`2gM)<[NF>lShJrj]5%LN$Po_R>!8+><[{$cEQCp@gg.(quUE5vKx$mq^Y>E12gkml0k*%f);6x~+j(w%zlBNCN75U{Ain1T$#zk]#q7Q/a{P/a6QKR9!<a&(q>w0rdiHeKylLe{P4nMal;R>e8s0&aU``P%qARz&NI8Wx`>mxYlSO5<ZX>KzwIK;Z;R>o=<@$qU`Sh0$T,poRwGP_xCi.eTUuB}%19go3g^+j6m0MNk[z[4hhF^~DykFpS,BQpp{fw@#p!t{gwxZ<55prV0$T(@@MR&q3G%q@h!G=>(e?mt{n@Gd&,.l&eB)5l+8Csr7MNP]]nj}NlwO}w@zagKGOW?O|6<S,f/$6bp8ug?oJnb[1Ezc5@FD}.zvyih@{GD#Xb>{MMotPmir=x]?r*GnM;aAp]z&X3eZ0.B+.w;qG+8o.k8qx8H+ep#;|^I!n/(R[{S_*+=cW7H_thb2,NnkP6H1Fy{|t67+n]Z0.u06.eN6iEBy)X@*G4hF6=9Oa2,sG+$k{/&j)r9,2g;Ao&e.k?j2wj~=/LBj@%U`.k(Cx="wlbYWeF;:1tP/+MjMKu))2%xR4?<]6.rOdV9}_]@Ocp?Pp]3$m$`%{fu)Y[/I4n,@kgIv}v;:sD1D4Ci]*FyUU%Scb6g0B%8:+TPY"w,?Vd:b(,dx1*^y+tr,<P#S)AvY~ay.?v%BwyK5AhjdW511Ic5%U9gox5Y<3V/i)`p2/s="n?q(L<6"MXc/vVn9VEJCudb?T<p}FHg?1bB$`r6R4,"b~>?hLpV+4h^.0^p;K|UFG5u;y1eQ!0"F)SPB5E<b!%,dzFql|2f}uNf[?!_>YEZjuK:/$r;z/}VKK~a2!vj(d(J,SpeU`^4#3b(R&j!^8o>`(LQ,<HQO?09$7.gPAZ7$ofB+n!s3ek(0zm&,M5O=(RR77.QI`4s#$4+Y%gX$~MJ&UZ5^"gI,?].yM+MvqG9]4aiA!O!n6Wv{2=Nn$47z!r=)bf%3Gf4vFk@#@PGlK9EOV#W8i`s|p:6X*9hJs&x=%:"mtIv}v%140H$0&}R6&?=}C;_804:#t,Y5&NYW.Y]hZJE`^Fnb(AJZ]hVf76G0^K4/Z`UANl#}5!^`qnAU=7!JIwD,^4)s!JpUN^&dgR$0/(J]vXO(k$IutMJa>J(QW6PVrl&x!jD+1;o~fJ4/e_PB{z!^_0Wx:IwljNpE)nm:Z`WK$>$k.k6q2Egn:em/L8B,j27Zb%qbq,[II3pGB@miAK+^l?U+4h.d^)<p5+~{|^UctD+&.9)JGz/=W~L[Aei%p:Hpl9}p=mBoXy}#;Qk{EIF&amT^R3qoA+GW&Vf.^!?@"}5,iwf=nxzd>*`#y@ocii=x><{`bS${Caj6O![Ea($@Jxumd9{{7?j/Ls7$Pou<>g9b0@k``J80V&"?+=vjZqV6wA%K?dA=sT3`v3j))@r/g&(p1f"0$p;!4|P[xn;qpp5(S{}anD{H0;xZKJf@nqN9|k6t9hlL31#H?T#3PhT"V{E1hxjGP"PAvZUK9[FDf(%h`G^|]8?}pU5Dl1|@fNurg1T;6Q|rbgr?TX"EmEqJibY>X`xnoB?SStncyr,ad._O+37,m0;b0gMOn@UvIJ3&R3r1@Kh0fUonmscR[v#`6.Ob@Q!&HE1R:%P){gB`I=>g72:)i/(#De_g7K*7"8Le<Ch&j(9Ndt/&xh8[,9!J7b`]V1p$?Y*E{?%#tr)=+@A6[e6Y!dQ:6w#&Z#$>f.U:W.Q[En{`,pzOMmVo@B;"3r&%~P:V8300,9`E!>32Gocl2h3^Rv,U0><(Ed_w$,jjzLQvZVhY9<Vd}^"N@1}cq0gr]0xI=n%Z`=;.d2ew#GF`Jh.{BJqzp(P|b0>@_/4>f$??G}0=@3l$19vlsvfiKTcmL&FbVS7&/J_D(6_dC+BI1vXQE%N%.1R!Z;p/I)1a5*QmF0a9#z+MFB}jp|2ig)5RiJ1)^Zx+`tQQh.ZjjSp"%!G8hL*EQa&uwa]MxH@B;xD0?qM%!Y]>Q@4LorcEvL&H|$y,+/(_,svmG832Pu;b$gta.]3:+HD&=Q_GweR>eEApXHYNTkY/SvtU3n|mFY.nPQQ3%]QNQ6A{By6ba;vFw33NQeST;bX3AmM,e<Oahi$%B|S_*s[[>g!V/E"Tbxwc;Zs.Zuo!"+i(F=N0xp28r`F[Ngub4KMzsw&weV9=?]l?B&x%<p<[xt),djC|=#;kG;c5l^em92VmE9y`31<[^mr`h&oHdR$F;dCQkPh&U(i{OKHsI+j(S.Z$_fK3HEqgWUb/CY?xR>NP*CWuj(cQG"_*S=>g.%s?Pn50LIK>l,W%7TiWE}[v/2fuS]|^z=3[JW>24|kVqhnXM{5$`l3qv[i`AP;bRKbL.e`c*Q(LH}:W<Ql8JK}JeWLN!iAPl*z[vQi!uDKN7QL$o<J@J{~aRn_}|*joZg%=t~*/uL64amaPRV8k)WL)G=vjNk_UvK,dt|NU>>k;XTGgy!%b]4h&iZhy{FlI@1Kme+Cd!G8<=qS:)uj?))POX@K18t9tZ%:M4+$/<?shp<mIL!6j.{AP`?K3vn_w=$.X"vY<<;mO2Ybe4ODPpf:1S$]|wP||?H)P;1H~"3nAO]=Vm&(orq.y]Od6#4gNg*S(Q)pOJ?&dUV4&Y+s^HAc>;%~DtE#3$~O/8{Rb^$I#TM8<I(k[02727<3/g"QS(ed)4Y:#^d71=4p0Zn}@~J=Zub29p`ws#qyee,MshkNsn&v0Bg3ct;x;hKzvbk"%Lk2C5)>dd$;WXPYP+*daO9iLLJjmnm/W{_6J`270rd{nFk8M,pKq97.U={b`<xa;qhOl"b4*SfTN_Z4#s=G64BC/[qkDm!sF%1bZkj,o.>Y?+.tUe[K]sciQ8uTxRI}A@q&ZA1$$nk.{@4mj)_K4}u,^h9y@Y{%!?bra:B%;?]|L!5@z$+WHFG63Ei&s)*IpAwz74&MTm^f+mSH<!%K8t)f?H,n0C>Ie/R`<4dq!swWns|"IL5Ssh,k`DOD1>Z+v/6_6sSfV25x@B4Xo`{$<P38.+jJFh._<N0~J$2j&xx,39nDN@J#aw/qE|GfV&*4X3@Vz0c:td?j[ImsKrdI&j:3R:X!>db%.~qJM]{)35L6!yDtMX03b|+<o&}G3s@JbnjC,uD=]6|D+B@J%XoSg=l)P%`A)A)mkp."(4h;&9{9qAfpPm{FWR45~O>L4VBt.BSWYB@|+G`#;:2PW8[]!lx^3<$gBy2!qhx,O6?}YvwmI+6R0wha%/Y9VgO3,ssJ]{Pvbz@;+IHRW*uW)$I#Y=6^3EYXLP&NJGSAG~cIaFxUxM~p(XSB2#{BF?7zpL+4=,@Sr]rzK7$(.1~a.s{LB7"}o?KwgQNNoY%=DN1[#E3I!Ve87~ZzlwI_ne>VwmS7`#{{1<9F1!jQn|u}#iJ.(Wv>YASUy;CdL5zy6,sM%4+u6W#"j,=6j95CbGVTOK%Dzo,>GW=Nhb9$S[+6&^RfzczCb=;DiWbi$K*2oleZ{R;o<(UBeK;H~6xE#u)a8z>7l`](),pgk2~eoM~F$g,ElEx)^0s+H>;=SHu[U7hzIrOIy7gd;P45d}KELP+Sge._G"xs{g}JVeV9o?iQgelVaS2Aklg8WyJn*7Y/}"G{p36lI`4rxqcw[wlQW56JCHO^/}$/)9Uex(sd7~PXGT!pM`Fj<a9$!|226Y0Z+}7|7*1bZs5$2I&nC7!S5cXh<Pvk,(?>i@zq*&*(N^w`a+sDbqs~`zg*?p#z{I~n2ugBG3*D~Ze|O;]tv_m81(mu5nM=3DdMS]=&bF+b`=62/=4tK_Ly$kw1),Ntbo6a:sO?9ia/}"h%4&HyONp]0Jr:awD,;sD](u6/F}5a2W1T`)mhX4/(De7;l6<}|eV~8trqs&Yh9!8<;e&?Z.mE:s.wKqJi.yB)`=!J*;Gf;}mQMGR[#D8EPcb4T3/7o2.dU.Nu>1jNb[5%Jx1J,~q*NSzvn{2VPnEqTpMd.`4_C|=r2rl=(?NRSi1bR<o^FK@P+dy5KrsoTk@~hly/6I8F90Sk;"wIBDd4m:Qr{i9zB<_BbU*mpp<`Dq=H{5EUbseZxs^51k1:k!7"d{HX%Gn_&oz+2}8FM$0z`V#BbQ,_1nhE9`h.pHFDI5*XY+Ynz,n=Bk/ZxYP=Q<^[$~)JOOMYXB^^Y#*vHEN`=;#m`,#g$Ne;I4Woa$Y<0r3Ge&bl9:Nf20t_s5N]hfvN8H>0s{V:rD~u~;F{iVFazFqre>4+$D$|/ce;R]d`j[rI{j8q;#v`%C[U4a_ZffV}1/so[Alt^N*p*k53RH@XK._;SQ:}(ZI~4<Tz]lgDX&B!*9o}u&~_DyfSr8zDCKNGypXFW*DwLooWbn?YEDs,w.z$ZK}ZmbyRV2oB`7(/Lg<o2Jx:JR8ZxMSxHa[CJ~kFdaNWQEELvK?6(nOpFM"3N!!0J@wdlmQt&H(;odO0@.eGdB=R>yir44Oz#1;SSj,CD)zCicZ!Pf>$G7OwNVdg/tnvJ(Z}#_WkK,@mMe)kG_(O)]xrXrp9XFiMP>S,G=KMmJb=i3=_G>o0(wg(Dzj3nvJ|Q?3t)QmvbF5>G`yK^CNj,BUwBF<iGpMhM%nIb$ISjNgv^P.wQvDJ`brhuVR#*$IS;XZf36uMS45]G4b73Y.$U6Pj)EK5*r6fZPHa70&693x6ibop,>$H+dRwzRykGFYY9]8SygX1j^g$v735G>3Vs@if@mu<*=bN`|@v=Yb=%Nlb*>U,0]PBw<bG{gfk*MmqoRsc*>U2w/Nx5zH@R<aWm;0*+k!AjJgbxki3wg!s5hxP[[PI]&(L17U9f.I4ohIoSe&(a.1~,j`"|a2>.$"zQ*MP[,*L%k(E9Y+F8%SKZ#9J:CDa[5U#!m9;fSyE#iWdHe)K?}UgOSkYz@N8%QbT=9+WiTZ32GY2@`QUE`fy9Zg{ejde<HSvaq^e]d6/6QqaR^]My?y+<7zXUDv6n}5BpCkP4a3JWBa1<[}+z/ztf5,{cb2%pPu/%Tb(*uO~8@,B*pPQzdT*d3saT,ZwZ:>jxI{1(;K^kl2#>}8RT46V/63v`".K5I^;XP=~9?6OLuy%:L984_SHz[0U<VzTRc.5f`Wv@_):bw`QV`r`bCLYVNwQi<qbk*o!WhtwwG7RmQbVxha*>AU&T$DB0D.L_x5&~/@jS7~}KkX0[tXv@Rmk2D/pa)o>^`{R]D#l%+G@P.^~+ttPlg@WJ7|Xd|Gfl.>^l8`lrixb`|*Z=;z/}X]+giRdF@;&Hj)mCqhE;3[5q!3@`$i9Bau1;hyOZ^&q&FK0N5"{.C,Kl/r;./5dZgpoO//]umYht$sQ9hrfJRV6H>3(bp3mme1/|I:8Y~sJoc6C`8UQ3i+[AV>Sl?pquKQ|oKY^Oj);J6UxKxf[kowc6P2Bx0m~[$X.tw|Z2Z;/(Ff7&fJlFyriCr6RlT*#agN0EhyNKUJu?RVE~MHax{i;:hMsd0d(0h_a=3b0rbYP<N4bEvM)>n<RTo`b7tmmBL5r?6uL_157!a7N=I*^7T@ld[EUa.ZFa<7Sym8s"J)3J(.$5wv3rRuc!<Zj(IXQ<F3EJc*K0hY_L#21;xdowo"w2,B8_(ckw4PR,ZOY0_6=$#SS^SiZRwiorQBi!.lQgaL6N*,Bf:Y6nJ2XGgU6e_Qn!ABZAIn.o9WGcws?9saVR6R2[ZtB,6?Fqd$uDGK+="E/|:ID,X<J;=6cT1f?qy~"k4q1M1Y^3IxjtaCsE@qV"{}yWKHqV@s%jGO`|q[Bc$o<_=VcZ#waSpW.3CO)u_J/s(NvwDZiv$cP^RCT3tSuvxDmtd/Y4p:A~NV0~DBIn:2i/5f?FPxN8:9CL+w*.Azyl{{T"xTi2)P3iTc(Bj)*0$d)$$)h@mCod!(e167=>#hb|5YNaIl"Rz[7TX!&;{vnr5@snkT)0He#.oI{w2n00%T`;Ov>(2NO|xSTH&_yM),lIdK>4J1;hMfXvR8|TB|Tno8F2%4QQVEYb[%]o+H:plv"2?LI%1ZoOO1<[ZR@ihxTRs(|[C~_x~D6:N[x~j?w.N_=70(`&qlq13OkPvrkPc=.Y}1fP^UMIetahn1:cp,9ay3{ZeAn=5nnD>4gnnP:{7Vwr|wrr9O"zUSd/*9`/m*fDbQ66mvk]P6A%&HbobVSq@61^.qL_2*btX]A#x9Z,S,u_n5y#M&^VK5{kvdL,{Gc7K,{G8b!,KzjJs(7qkzQ&T`mMkriQxnF&kf4|kw6@gM5@k#=>~5n_U/XVi>[|Q5xJ;bg1qdHMK`2aeE_9y!`&{e+s$L`YS/gd?~4Js%j8]8.0z@yZm8=YTRbZ<3T`{@+dRn8;}3BN!nPptYv+k9LROsLair&fcn^/xiag"rrOmDHw],k@se&0fo#/l[##$ILAz7XMv>?#|[,ifYrYXhHCt>^zi0)*REA]RGI9AcXh4HkNUS:e2U*mD)zs,*{nE7T(ay{Iv@P*9|B/`[kw6gNP+3A9&R"5o13Rxze*"qs]C"IL|p.<5M0;/NCh]onM;2GKL54&z&aeD~/#?[{_]5px"`b2?e:TvmHUDVedZH+g(YlbdU}`neyjtSmf~u$XLio8fr0)}H>#>Lb!sOBQ6Gt!x$|?*=/*kJ5Mze)r5hLGypunE6sD%!MdPeq8^2&&$Do0B6JbE6NWja,9LR+ERiC}}H7!&N9dt^![vJs6f%|N++vlLNW#E3X3a#:/_a;blW|KozLqU6w+]^U`rJ{ZkM)Z^,;giPgeEl,]Y1.w6>(K|+cwDW)&B{=YaP.ajJ.zIU)PI;Q>;w$zeV"/?{@H;)EGvOaa`s4Z&|ZO}(@2Vpc6F=6dL0f5Anpy,g;_/z;2T2dx;2~)e>#m#nz*fx<TDuqjqb4=^H/<2{+P5v{1sIpWu3cz!(GW/u*(lV)NUxXF$$Kb(,W[,R<y{vw{d@y0<I<u0b]ESOfSuJm?#RMg:>~JIy5o0IEZEY#Z75IcxZX57:>69#^"`KmE9o$|w[,ko`{U1=i)MkKFxyAI*nMMtwQo95;bV01z`VWdHk?k,wiA"ciU!3a0M4@kC5JfX&GsV2ks=%=9>rlNiey?&5|oRbw3G@29YsI_[f9[<jOOXW;c`DiE`iiy~H9J4Yp=%ZV2?`nk`o64Go@|oWaR5^;7|/(^&;,As{Bm#{;e|VXM"5n)p1M.F|4bTlud`=Thx<!{FZI[YocU@18q~c<IBxXoJb@/jGVul;G?]eI:DZ;U&8!P]<3/,nZ^E6N6u:C78u&uTDx1bLNV~o_QdV*)JYK}}cRDo&Nbzw78b:Nl1#g9b:f?}VM?~Vmctja[1oo9=6s,Nl1#G8b:@at6s,=|.Ef*}6kdLqB#bKh#fn"^)o`av]/Y;d@Y!o`1o{1by.Dz77g.lZ0mcYy#c>vppTxuYbzdTNNKIF:]pj+E}BO!@N?I7a3q>1xh<X@{$PLv{1+B8{+PBveN[e=b9U0jw&Ee6Vc[<g&QZnAiCQ5@Wp1(uy20}x&cB4#%]/O,=_8c>aEp{e5WNDb}i)}T./;^USI(biZxOG%c(,DzziqumKd@F;cShHwi>k.aR,8|eO,bzlLe#O%]>?Tz*fc[s@?XtD6K@ftnCgu@>BfsfmSy.=(2YEzpD7T!`dK+b))niCSj;3z0&*cu_0n=^I3Y=LTx<iDRjN*Ln?^L$F?/n<Ud[zI^L`>]<SeET#srq*FJ2BziMjhpln&OHwN`<I+L@lfh_i3+KKPC&OrYtVT9MxTi<Y+#3cDdbqqg]QFj,p~9l9}aLv1wjK<|>uUUQ{bOc`=Nr0Wa4Kw~>!<L)#ScA6~E;i5KOEVuHD,U}CwJ/frDO^rg+GfN|k~5CIAZaJ[FE:"q%RAPT{@]=FN,~wk;gT|B1Mx,)M1?+aYghU!B;[d!+C6K&|61C)OAPZo:%">>35ARM=;q&8f+=z!nxamN/#AR~{e7?,29J*dlnlRRl[</TwxIIy]bwh?g$&s]z_K^8qpjba[e?;V;=QmSR_VrBxW;NL/9"(MNq=T@y`<F(%=rlN%;/@_T3Z<U!G?]m`nS$q&J`.<=a:D)&.FkihHk[U9YbiMK!Y)azJ%m/Figj,wLYiag2VBJbja;&?A}[OEv!|J1>C@(p_*F;t(ssIH`"}MIh&2SomRLEpce/CD~q.7kH!e:#QM!9:v_zVe6G%;$.$;en_4q&gh}WQ)isQNU0j?1]p(Dyq]Cr^u<:#qj|Znh?{NF1vr+r6$e6_2^N3Y)f&$sHaD"DY?>7,z"DArxmR#*$I.%X+]]OX#IH@(TTQnk..GSIa:[`*m(>pLeZO(1^{l@")lFDrCS#DeE[mBj*UF7},oPvANTs7f@7H..1}r24bY8l%r_NO@uw|3LL}x9Xb7<1+CW?HIP,6g0EdmQvewbe"+zg)V$[h6auPTy*c^vU)1yID_@?SfG[#jMymUw]Ltm/CKYVR|}jnEnx=prC*jv_F|c;1(//n9nIb6a[#Ap_QW(RgTecIwp)l=8_j}P6,><{f~mN&mOQ3|Y4V.5`0YZY,hZ%%ySVI[9g3mIB&mmfMtb:U4iEZJ6X*c!hX1j<ssT"^op3vM}w~O{u+/$/)WW"#4(px[0yfr:W(#jE~kA(RxS]hsk.+tE!v_c6W=zwq/h;rSkzSfVx@JH)&+PkzaJ2vn7mCW[UIf&"8@<01!BPqzHq/}#&%F&|FA_x,QK_&TrczT9x(%J_+F!/s($Q&"CtV_x`LKX?;s{;biF~Gs|Hq={~L[BUJXGQ_T"xTX`UMa7,ko8Es(^NG^C=ony?tP,1y0=z`Pt0=EO6|H!qk{Zu@z],1/{:4y3{{]az6k,s{P"tK{4B%D}~dM2>@L@zeI6!Vu~&Rsr;07q2TeUH3>C8t$!gT9eC[^Lq}FP<3q6lF&nHk?!(;G3)aMjM;Lz64i)9k}F;:cb16Bgb28>,fPID+wH#C<]G%m%YT#xnE0!BF@dv5n(p3[r@K#JUg(Rq35]Rx~yUsF0UjyW%}t.$"yr1~c}h=M|9J?I.E:U`{V5pZa_,&u|s8]*%Z<K}iXt.c%@8XLDqXO2whG;TO9G}BFF*FcE=;roR>EGYjlfJ`p=@W/xZ0&?Q2bPj<o5qe|giRKw~Z;Oh2Z8jL6xa0`;J,XrO9]BU_5Hw{[hIR)jex&JZkZ!.m?*Kax]5?N9NRQzq<Ck"tf,[P6nd!Ad0PJ;%;<6c,a8!%L0UH}e0N}nTDjeYErfF2E(Sz,USpBtroqTB;XD.nf9~2#ExXSNMY~EbD>1%wP/xOrCj5,BCY|%jnX#DL3>YA!$xE@"hkkG2]RO=.TlaY%senjq~$2`N$SKQ{E%gOg^q9D^:#55Bw@R~mpLINLlpG+6L7PV:xCeKF}7(SqGeq64K)G6%n3e[X$S.>cde:xU[Jvz]OM+F~@vQ^6D3eFNLg]a4b1bpCq6`p?HY0WEbRA<qxnhCvdJ,$(45^5*0@3?^])6Sqci]acsr_/%>gUM?0oD?thjc0f,0YHwy)E*+4+`N[OlyS^>pwKp!)iM*(f5w6,U?~wK>~vq$hH.o0;bm!KrjPyfqd*THeJfi0Jkg`Xbui1+HvA4/&V3:]x]Pq`8ygo9YZ+L(w/?e$6^u^2fZ<=ocI_6SbfSb2)N>P16#{/<KZdU5M8x4h(R1sOxq,_!kJDaW3wQ4pd(N~seh[e{o4@kT*_LIA5{^av);Y>4tU6)?]r=|[|q;K!@:<$*JN_64Nxd;YyG!LYea?q%95v#rgDB"w;bgJw(V0?hr:S"Y!S&WMyIJ29{fa/cb=Hk}`3R*$9&LJScMQsI,9,0$TSyR9U9]!QTu1u#rnO*}!n2C}camgJSSGCw?,lE<$Fh?2$I[8Iv;"YD.<T*Q,|e.fdQv&xVwUu1`DSQ:F@3XVp/IKUx{%6aB+9f_sKWHst@w@/(5sh}+{zFur%=4p9{gOL@1t1BcCyQdCE:"|)<K;fzW0t#^8ClT~6Y+;1&YJP7;hrI1$T0B:/Y:s(x!P|X[|q+})99a(dj,XxF9&BX^LfTuakwj*fJ6uC`z@;1f5U1/0f/Fn>e~JX^Bt?R(!`hw?Wn$*bmIpo5?M9Y;eE;O!Z2KETwe8!Z9+4Q"4R>S^uRV1l^vpkRHfev+%eiz#R:VP4qOhaItr=oZ+,43)O68@OeugD+ulI|gCB,2Vv5)%>[KlZL}[;IFgYD}P]kWH^uC9%PL89i^Ta.?x8!J:Q:A5K[NUuzWjo+Dz}PDFU:vc`dhR&Bq|nrpv(%R70GP{&>c[1fwH(N4[K"u<v}^zUjc4"LM;1F0*B^*Wb$k&c,/Rdxo+DTO^p48,Gf7%ja0upzwPhWxv|EZK>TIEb_InD~j:g?waZ6Ua+=o/P68J`=s)Wf*Jdq]D~iEn=f[]n[&`8cbp*ntrwMS<E|qfsh:HQd7$RZ!Tt`{<|8vEs*v32)>qt;7w?OfRe*W0lGhs|5_eZDD1i.&^H[o&VHp{F4=al&jhvu"U60j8c7QB(V6q?5<H]$=i6mn=B]iu#<0m8g7`asZMGR[#FWXsvSQdlb]o`,)`Vl6vqhS4{*!Q#@k,m(S=},<Lc1`aUJ@1)R(R*pU!La.t;70z`ffo(ingSl[jf_RiS_}{)LM#cUBy,lfUTfcp1e7EtKRw(&9.C`rrM=dWfUj%2.iV#l)VC|z&zlf_Xsx3U!hVrqQ{`ot.Fj=9xl5sY~@X{r%rT~5>s*ZgWyYn!UYerg|8(o+}qtVgJpM=Iq=/=g0rJy.jits3#8*hug>+if=hIy2H(,A(p$eE.[v"+?G$hNT`tj!xw*fn+VXg7^eo_!Lea2m:D)^<m}Py5n/0Hw(?2}J+Z]b;G@S;i%i,rr+UtbuoHk<XG~n+jLtFfb+jq<ZDgU#Xfbw5P<SgbijNQiS]<nYnvl)#jn"<KIiRhw~r9$.M8OZL$VfDk9.$lFs_rK;.s,eBbFJquqN]m>45Q([Uc>!&8n9^@krw/j.tkrA%(&R>2mYTRV+n%UK<k0<pn$U^YEx&[Dwd$oS_U]_^w&d,"r_@|+D3I*tQ#wwxrY4O(R$J7@iu+wm!%4iyDSv,^lOPFoTQjDq;@W]QKm}e~G,BfnB!.a0PFjXFDrtHZFPZ<.d~lb,s5iGpfGST+i),]1`H!!S54(_YBT|H&iSDLA!VEUw?+CE/PoVbr@B*[]a()l(z:%4&}9R:=4_6U!{r_h|}VT29@#gthRkt*o&@Zg!q;L;7Hcke(<9f5dOV#KMVVo;/hgNsEKUC=.AzS*{UbPMe:eCDIQ8th!,@f"Bj/[/Nkm~*N?$Sh?fh4r!^]6Z0}Spb?;wNY@(SVLL$g_;6=2F`7x>U>QZik,;6bMR$2mF,a{_SH2:;7OxKKSL_[<mIN?A>(#jn4T`PVR#*Tk/YtZ2:&<mKsVU8re|89b[qkUu7=h_B2PJYr|]G?_+zIwG01]xw9eVoWOd(BIb*8XN0)d|+w@yD6Ke8!x<$M5X`#xqtI/^]^Nf%!,ZXT^:+WX4_*iil^vf[&.SY)HULd[S0*Ss3oYB](NCz<uS?t:nrn`WUonm`tf@{=fkw}+=*a7]0":XY9o!xzc#4h6>Nd3WvYh4elSr]4ov:%mP.*+@!@Ua`jrF1.%3bB>/]{Q_q0/B|n8C7snYX@N>`Z}D=>{)^qr_M41&7sw$[4/:?h,%"y${zrK=Ik:r6q=,n*jr#eVnuPQ;o=KNfno.p?}(m$Wf$:pkU[1z^`a@=Aih7xzB>P@,TdZ..+<d#tJOS:1r=B_`)BzC|/NQi<q0mY%Y6C7I!Dmr#6,*b])YD,Se)&0Aa=%&xw+@q.nu^FM<9WDL+*Le)Uel:1?,1/k_:4fugiw|pZ)@gRyZ}H4x@?OrVo`_Nl?:kNSaR/1L4%W3tP,ANwb>h|}+&2Z8W8@LD^Zbo*z|k[z6Fht|MOUP@t{Xd6f6fMJ[]P37o_ii3}.d>48yxyR"Dy{eOqpMai3&)fr@ou$7AWrB2K3c1A2Z@lB9[)ZA{>f$2eI@3^Y"@Fy)*aT3h7zB_:?T<*.&VQ*6c]78JF8?!y<J$m7^j6H@zj7g5uZCABtFcAAGH=TDzjX5Y8F%;87!DaSDHGdMS:@HuZRG6lxmRdLBGAAAAAAAA.QE!|hnmNDHItja)t24h|=>Df5IJBBygJ_Q7S`.K+Qrl8kY>qZ;EmG)6M3kZx!Dui]FEW,*;+@!oX;cP61v|l<2csLI}u1ZG`Oi)Hzwd}dj3U(bctcfYu3AnLK/44.2z%U3ck|iXdhpch$+>vH)RmN|qh6Q$(W3QfVjuuRf%FH2uGJ2b@HW]AY]pH|3+et|aaEjyW"ZbNO@eTh{ombW!ln&LMF5)|eGSv]wVm0azBJU<m@PN:=ip%2pk]:~mJd3SY&6qio46*bRD~!p99J6i|3zC))i(Je1`8@jn]%ARHLwK1iL8%#G6GU>;fyp=c7kwrS*FDP}}wKj](i<wf?rNN@:([My_!s0dJTPR6r~c$?W#BcH6C)o)+=#)?/F8X*|YWew|haa)TIJ7qw?{yJxT}`%>6BB.Hs+7"itcU_fPwMNHk`~|{usp77V;HW~MSsIOS1ZG/."B,Bs6:6W`=<oFf$)_)n*weZX&nuwQ7X,^Q`%P33l7uq75ia<~)_~p`P^NhB7zf.vRTw#H#w"b|LL^N<7WOaY,?]Rf{2;Gb*wx>v|ga=Kn=_?>lU%ebW"fOSU,r0$wOf)jJS]>805F_%Cg_Et<VIiUw<:=Nv~;rgn||U!B%s_pQVW%2R[3{M!/}UA=yE%SC?TFr|^ZGPFw9S3LpnSaNCRi3DKxz3feoOE69%[@ee[ZvjS@RaX6medN(|IRxKny,HGi^&L7:KU4}c"$sQdp+jNf*Komnjpkn[~LNReh4^zzx8V#m2^}suM9II9jpHOU52q.Ev!+ET3],n;>geks5UK0>EG=fynkro9Z>B|mM^~v%Hr6~/*]!<dI1o5TW`zleuvvT_YB;CJ!?$"n*)av6`QUWoUSY*lTC?k_PeMyfhJe/_gR5,v8LQHWs_*<$^hP}c%:EyV^&.Q&wSOuA_&Pmlb_vAwwQe`>EG<7vB>_ftBh&7|cDZ(.M<Ws_sbLlkGV@EAJ^)/|e~xxUg#;;|IM0dKggj;MS`$e6S6~c}wWg~QA9.TN*8s|:0%;0*tq?:I[[C?<?RaWI7Fvxv4i[bqM?|JBI1gMp;0Fx@>C3zA"f3=Au~7WcHM:b)}xKf1r,CiL}6KL0DX{/(GM%PEYX}m3h/:9$k#r97iA/U2w*aK6bVM;#"=Nu}5JoO],kW?{*k6>Nt)8<ANVG+4/9Kc~:Zwn=Kt5kY*G@l4Z*t=}!pa!beOk3|HLruR&5%~Y^CYX~VCo/Mq|A<*R*;PzGh9]mn/*F*0|cz0oxjWXkE"FKpQcHq@?Ra4R:W#veT;PU#*=k>we:O";_:9WT2I_jAp/iMYq$*vY+3}zxCv56}mWRB;g6d?/AE+_4`GRtSVyF:D;mmcU;+S52q;Di)MNb$p+OcN{*Bn&o{%hu<+)KIjM;lnsM8z)mcq/^^t%pUNu2F5c&opm{9oL{xTM:tY$:~}tiK/.^sR}^/B%4:UY?XZ7%Iwann:yna4Z4cA81MpKR%;Hda{/W4Y(!%vDgu1%NHmjhjmURmJ7K<N(qI&mG)cv+]FJ}Ee+/B#}pBX@v%{;Y^c8EXF`wxw~^0)`&In/f[$cSi]4g|uu>>riUUD}rM:R>c7#5Y"D;nU|VQAs&z|J>./r5PM#YtG7y4ciseE`kSd.b%MNqL7!KWt;~Im`EH.1L<B<:xGL}SBWVECA|S|e3bL%5$vRQg[UoC(nykOW1=yS@MJ4,,,RL*3t)?9JT1s/>$4RqFw8]2yslb{[}kb!z!n%[De/iEDT]9^i>LMp$0Xl<=j>il]*/+f~z`XB|v`+nn<aTpH8`z1GYJ#&u/DY:pVanwN[:GkiDB[yATYMw)"t<d2>FT[NJSy]R|3:~=tU)lc]G6M[7bF[U>f#nvX,b~Jqy+[t&2&W:X]dV*32F#1oica:Hot.T6&Cw<~|5{I]G^>H*Y[T=:UGLk0hU&}cVuYUpX5J6ImrnSOUXtnE]JS`Z$S.ji3n@8ufYv7o;$4z|Q._|k*0_s}/ig.;kLZU]CA5;7i!,]26Q@gtB^T^4HGL"gF#]q,|>}M{e7faDGO%+ZgXf7<k7&(y[&)jY)6E:x6K7c:M3XnuZm8mQg#s.{Dty}s!JOc>ESfLuHNu5Rq{(X&V)Bw&qzuNeh]u,Iw/0Ui*cS7l;npFl!mxxVW+3+21yzR^a*!}ioracl3XL&GMht]gFQNRv92Q%tZOIbw54@#>hpHq+S<7j!UxhK<RLOp&@u[L5;|~JX_V35KYT}V]l%o[#Ss}RylUb{;KR(URXtYxn5mU9#sC~QVt$w+?xob[P0%/*eP~bsr{B,FEG.$XXCc%5kC%>t4qMkpy(#6^52eB7%TLM4R$U!)*@P9A;MSE`#|Lef7cjNV5R1#%/)Bx"J3<xNcbo3)tyER@VwIK}9Hv&"?+6i"328`@59(l9Y^0o!/8+%tQrX9DO%e3A/z_QU2:vPGr_"!lTV<!UKRFXZ"C[PKxK7+H*$qWh+pQ@d`CwY.kL5BPv{*4TD#c*A4Du_VQrf!YLGPV"~q)JWqQoM)1nk.%_YtcSMEu=1?VFe$eb4C9Qze0bw(;;LG~Vy+xF95umhA"ny32A{8VQw?c=]aF;(lCx*2F$q<nlUb_Ep&upPF#~4A8!P:YP"AK^f`&v~qu;ofRIqD./rR1*%=N=jw&+Gk^Li8{:TOGs:I&).+Tpc@E!+_2:4DE!%6;;G.?;M2dc/,lMZj|;Bjw_Xk$,Q=GjZM%rb7k}0:deVVdPKtLUN=/xWYJd[>vq_<5BY"5j>?k})1N(U/n^6Ufhn^gMsO3NsCW_Q_SW#Ad~4Y<QnAxNJnvh0@7D<NZiJa@&So@Z(GII0[v7Vb4|HQ0Lmo%_C&1Xa5_nZR!>HzbTt2~RYG!&F>SS`mm1f,75D/rlH3qM?m.<Uv_%XV`t;"5)Fx8[J9<.kY&{Bk4Qyov;AuSa:"4265ee*qA3Uzw&|BvJoQ8"0A&)=*#W%+<~N[t+M[MzyY..fVl;.k``vh7!kwu:%KFUYo7Ka$F|Emtq#3hkz;95rkwb?YHaopd]SJcQQPyJPRB"zH2fM~94@m/0=B(88MQnzMQVqz{.du@fS]|ZN<j,!)A22Fk+8f;ykd]N0TCd)~9hRBG`s0IJ%A,uIn@[<e+EmM(EuzWi9TC:!|V@YJpMkv8H[+zhb4ZjFV;r74r:%A`5Ape&W,o*+mr,GR18z?t|UXp=xx9jYx!j+b0zLs0;qGkCJ`P@ZxDZ8bhdbDM`J.6#";,`K*~y}x_3[Z00LK>,O]KWlw9Zf}YWRcT4vw.cg9Vpnt`~83G#<BhLY?0pU`j(?>`EsQ?7vdni[YAc>AKPpH:~Cl|V|a._u|s7rspipaS.v~u"ac?%b%]7:5EhWr*^y>`rZe=]~dwi`/csKsx9%:_7$(B3Rn(jAzNxvS&?)9XRU/R*p.Z+YZJ|+=omPy}SZfW)]c>.nhM;rL9mp8c`{KE<+fK.z8%~Z9[yJ"[@ey"SyM/c|ID|Os8!Id@Hgoy*jBIVTKlzITLrr&/?Ju#B%fvxp4c<`s)p[}W27B&0]0|/DJf?kk!.FyLj<rrNjfcM;kcO`o=GcbBdizIm9Ym#U!qDj0L+*Xe%yWGNrMs)EU~5,$ZNg=?m#!JzSa77UHjP2s!bAZoCPyr_J:U9(*Mfi5M(Rtx@1oL[ND62%vUrPf?Z(x^%r>&7d3ESPPy]tt|!1/r7loJ{Zk*VADINJ8<z4O9zN~ysMjMJ0cF(0P;lbMhO_3=aR|;0jZ:hR7o{S(^,qbt16U@&LacYXgngms.#8}=kQnG^Id|Z$.*Iax/#s@2]qhQ5gp~iEG6n%[t;WX~DmrRz3O7b1RYR,[D+Kg)$HN)*yrlbH`[MevZ?OUm_k9kArsYFg_1aHTu?{^5PYxVsK"wj7^y6_iL.=cJ$v:$|TgC+(tsOf{oB;"OO99.$5j1gfzfyXNZP5KvN5X6;5<NkB^P$`!a`>S>ae&4XOaxjYy:D3w;|@!g"B18s0.?nUsWh:J{%#|Fgq2&PS1swdi3eXHK#$S|=oXQLh4Z4If)x=[hHxvRTf^f9Q1!$H)IxH#G3$<eOFx7xpJ&D!9xd(@32BqbXX}g}$:sOt$+J"{qqG]n)JsGcZWvvO"&;A]d;gn|`(l0J+$9yL]+OrCv%_b%LxA(4wY)wp<&$x+.?Ea/d})(hj;B5WC!T7^u0v42bb7C_sU+b<mu%h<TUdK3xx3ZW;!YvG!t#>*i7TlRbS6AbdvfX]X*?mU`0^Knitvt=G*q*F;ujZO.)u0[T7?)9[[70qjL8X0<X/39"iNyR<c|YK@gGzdq<l;=V(>{TAV@9u{VbkRLHR$COI7!1y56aV&3*&%9j#yy7N4r*sD8^;0(35s8uji}}sxxmNHCv>b+8Qe[Om!:=0kEf3wNEQ!>x,gW6HlcwO$tc_&6lzwP,c8FuxoX!is|O;@cX8Ig04C^3p?u9Vec_gg9[YI=o$U,raew7Tt)041HSti51krcLV`iTzuwUl`)"d0?11Y@yJ3W*Ef_F{ixt0[h/V(`jnZNmv2Lhb@;Au3f"+=sUv!B=4kePBX^+RXf&NtVePv=>=7)6QH"S5wA^p;ael$_96[0MtaB=.;0yeSAy%llVm:qXe]D&%Yz?[R@Y#l&,nZ3L~s2B=)^M}*o[FBQ1(qfcTO~R}@ho[,tSX]"[oTOBV@,)}RI~zoL;6iy!t8)3UO):`UuPvAoDf{L`%PD+2JXehkWmt"U(KXmhGn!;?9]SXKx34L;xce@2p_`roC}u=V6M(FM[+M5cgQ@!$9EQ2!7uK!B]vUP%+:?AG]zACLsbBC!e/fn/4B6C_K!@#kpD:S_q8Xv=h<fgC>qIFrzw,F)/}G(/JbP,(Ut&0%4!9P&qV&x`jr`AyM]&SYoI~QdL|m]&Mb1gsIK{T3wLjb0i`"U32c&A:nzwX}N|U],i(:vBMhe44Q;^so+Et,m$mp<vtF6i^e]F:~5@s}ax$Br!e}CBfW|#7;]gj%h@d%s"ZmnSk|uaJavE*,ndri_2?MQ94v9U;KT^DKWXQfuOh%%}a[5FyWyyldsS~Z%dsDLT)N;iJ>TJXmNUtxH3oo,r,p=%HNC$PhoXD8|KOjpg3x`GVpZn5De7].MdCCJ`Tt2)^C`Vx7f7})}l/72WNs5hkHjt7_Q~1hf9ckL_G1J[FJ7{Kl>sVNYHS+]>R5Lk6tf?.$0Fy8L}Y`&$UMG_T~7P8og).@yx:mj6(Wfrtcr;,riu&tJgt:N#rVes>rXEl5w"b4b55%(LZmB)45M|w{~NAN17_@e.r?:(iq<bvKIp5yss)F$],zInza_}+f|"M"enLjub%TJ[=DMR80umGIL1]P63ivIcYMjU.tg)Im&?_?{)sd$?RDmQx@~r9C/n>]^Y$B4U.t|E@%BLI0$+!~/zZ:P~1"c5e8IZ=l!Jav:]pD@Pg?zJ:28]T_>l3:hAEshgnw47nRE+Rqj7%;Rkp/Ccb(ixsa}%M5tV74`6)ee&79,*h9i}}]z?&]Tww./$An%QF=*;ym/(y%d5oi.Vb?_y9~!`n!mHV)JpF9N:tuA&biGL20WOxC5IHx.w0qeB_8B1pe[TZ=f&8.Eh+SiO%!hW{:c0IBPSIz/YZ5x;sC/&mZ|)1dEGm%~Y(Fvr>0WSeKHG1Oj&7VXLeZtt<,@_S0FNwLi=s%][hQUt0DR}EjiAH~^s[Y2TX6}9sa*5IUuYCbi4y(4F|Fkz(=IVP;:0ELTC*B<d]Y#+y{"Fu|x}bmFyk?Id2.7q?7m|6cUV9BGl|7dP?Q&"bC=B..o*]!=*z;oc.yt26`2Go$Tr[+,y0mu<aWXYZeTv@F;,f2cwq9~O<gnz}uhsUHz.Qd17~+=i=F>6k:`bxqOjn!Uw(zE@br;(!<y($e#AQJier8oDOcObQ+;<e:bsB>w3P@a#>z"A2r5#[1ByNVsU,bt$qi~W7hAMdc5?r?b+f(|g2M_CPOXhxv{S6:Ym+XeISe7IZSunxAsHFxk=!YyNo8vI{jl)oAv+QEH94#ZEkY`70o+kURV(CycBa)_&0Z%9ihf^}6+Deo0OM}XrGXG&g;S@zR!>4ug"t>6g=S,U$6L`sZ@f:!_UPK{14`It3P6P*dNyw_r7~bmr7e+"E;FXZ}&%i_@abD9lgLP?*r_=r>>|3PQkwR^vMFAuM~2SOAG7g$;vtiVBhq@{E9945tYW!HvaF"%56e+%@x)gNeh2SDE_@FjV?wi6kzbh?&a2uT`r{r_=~JV/gSkV1N,j^C1me2[W4unK5!y}c?l0g2.d)HPfk~4.os4kN4cCM8D|fX0?t?+Vug#q,]2g?GMeZZC8!2pun_uiYBl1Z5el?b2*PF[sa_ca=amqv}&3US.V9N3doVd)BcayA(7HR{o=9&[^0jQ2?Sdt*/M[HspEi|FdYSVN9}UdW.WCfr>J&4z9~xM:#}Nbletzb;RA>Vlg}fm:}a)XEAp2TNeaZ;a<)2nm2J9qP5:X}LMr`L];.TEu0@y]>AA{q/~`rE2[Z{5G=1OoI|d2V^t%d?UphTX}~&N}h%SHGXT[%^yMz|kQ<yi+T/InT#I,4WY0<|!=8*A>5R`{2YT(Y5|x:TdZ[wQeqB7|5lHBw?vCZ4)pDlU6C`&!/a`,]#}>}+a$sPn_vV_zR/]Ag/n:MJN(X6m_5?!{7}P"W|4K!u6rI>S3K"uvXB*%wp|`_D?!|*iKGN/{6KI`m"Unup6~;A,r$~4LS,(}|S<3*CRhtk[RVGUJj_lWe[][oT9*9)pGU)c]3lTl_D}6AsOaf+ID>i}E}4~jYht1Ec8Dpx,a}B^)C4S}6LN3G@g?AaG`Sx%90<my*bDYw%!<""BBKagOW6jtORFv,.+w/EulZBUC6,V(3(~XxaftM=~NTsE#lgiFx5C8m+B@Gb#DJ.4pL_nD*yd=Jrma&<OOF$?4jQvP!z#Q19NlVFm_J#sDs)X7I^]&4x~dI!e)=J;"qPa$z$p*.(jzuc&:*g+d?t)olXxzfIR=3}v;ngpaF5R:k+I445o"wJIgkD^v;Ik$ITHw7,l/jnL8k`{*(TGJJFGe.S]rjt6)HcN6BY$IkqtGooXxE`YuQa>PIpcL$F8Tk}ctA<{;K=KO@U3[]5:X4lV,SVa}Yl?g)<_ew;C,0.lfe:cbO;oh`Hj1`u1itt00:XOT|ui;GeRqq3wX2aVv~^oRs!z*?7Y8ruS:[1*[?+MEVb6lSXl_q10Sk{L*mcbJ;FO>Gy*+S7Ajm,vWLEEabPAIb#q9D57m];{Nn8_V(y[}v43)xq7ev)|58IuRpBKwiDD<C_T=#9V"`XMAC"w+pXU6s"h:9>+4{iiok(ty!y:n^nSnWcJGEV!nGkGh&0PQIX5?!XOzlfmFHqEa!NlAo`nyJ`,%W|yg"B>P[tci?lV%&$(yMb8UtC%XRUWcptznW+}&}vj6FW@k;+*D!vh!6"ZE,}aQm`oHOHy9Q!sQZ8@s8eSfRly@ki(C459c:y84Y[$TZm{Nl[C]3gu}=HeW:KvcLfN)(@2<IGNRZ|qt~&Lf1h*F;%c/OVwF9qE~o{l>Mc}gEE^!hCzhB#5$sQ|<EIN6VJp":t9aY0>N~a+erH3=$kkw[0K_fROYgJT[50XtGhrlx6T<hVj*!TwG[J~LPm5LwH.XRN`Gx*Z1sH1`z9KIO?D6d1z43:j1GZwKP(giTum6r<3$k$xya_:(+iX5}Ge94[rWMI$VHxwk;d.rhR=to+$bh6+)guLPlY(zsaM3r#^t(Nm39Oh8!v$K_)nSg%?uZeEmK:~.z)0RknjlNn6iwZLTY^A+Gjwc<;rfO#&!xHb1Jt&1:3VR9i#pmqR8ELSK"GYGWWP>f{3*1=n5S;#4.w+C}"^6[#.+in1B:z{5K#oCm5P=w#QKeS6/${%F^6@|!qn#[_6AL^0i+ke3%9v7(#H_jpFn&NPd7Me~M)_`TrjCDbZf$F?@LCxS^4Ks?c7EOM5lh63u$jU&]3@*K&sUG)p?()[43NFBKP~/vxb+$vx}5ip#:a{1m{T)%jhI$f$R2_yeTTI!PV`{uc0^_HQuYm.gn[aIodid,Kil:NU[dNY?L#(56sojoB=dy)~"JMKgGgGATyMdsaUxg"`7Yedd|P4vso7:j=S)p!*M7Pk%*qYnw:cYs6C2=!gZH#2;<*DScV1xeX9iBA](n=4n@+@"i4+#E/n?zi$;`I%amrjzU$yJ@0G1qh&zComOVF>y#hkqfjbr9N3taWh2>=PHA>!h^+[Mw*rT^d?nt|taI]G>D^bPp@_dw$1]nBenM*i,_=>xQS@,h&@7nQY0*@<LoU;Yq&U6pF_5GNC<gew~eKj4S^<kuT1UmEs8P^35]wh{K$)dZ|7^7wtbJ+&jmV$%qaEf]"sZ8OkQQqkg_X01X*X/VjCZSDH<t{pS^"[}?7)<KtQINCdf*~1>8W0mXjHQ;^`0jf#p$@|y&}*fAGu^=yi4Z=}`V,=JKqHuA!#B"M[2<TR&mq#)td`*9XTsk(([zZK=jM]FtDg:619MR*L+#!*<H5=w|E,Md#Z=<}Gc,ZDIc=~i`qJ@(JC<+.<(`>u]ICx~uGL9C(tvZ8zR/^5PT;;qc~},jg|icu^|CWe/c_v?>*j,Nt5UbW<U+~$V9Z31]!=3dKM#SU_$SwKikJ1ewm~LLT6G}:kU3**@z%gC}a#oO0+s$N*$Mdz4j%+lEC|}+yYGJ5"969T0XVy4/AZbmq{B^6R_q7o]+}=p+{>X)Mxd,UWx+OImfY,mS,c{GN|TwQjj33s(CB|Bp4t*nBu8C};U8j/a5:sRL/pMD?]RAXV,*9IrU(tDb:CMU^DW.pTyhJ{a*`BgBTqS+za)=lI;8_rNHiC#kF1#(Mkzp4>|^/:cb+8Yb4cLB,{UtG?;b&u+GN|5<7i86$Bl90x=nQ?yo7,Dh$fIc0npTB.CsIV(O"?zO<BaXs5Bh16]ASQa[a3=!Zf$7%mrw>fRRy*Zp|Y^2W[=QM(_2P*vde.Y_L`/.i:$v]_zi74<vT/)O]L!`FZDI`~[CicXFDg%S3nfw]6ZQ!6ajc+qNWVYo2)WU("A`D*Xz6NR*QKCvBuil~|pNAD4`SDFs=V{7Us+C^vKFVJ"`ooB%L_Z`o#8^xGd.2%;2XUQ$8hj^>b_o"gyR6N7.J"DLDU}4luwSd@PyNrkFN[WJ$MV*Ozk56ZOuscD4sXfv>]H7w$vTR_n3n]zqKL.L{+;O*9~K%Y+LE/GWMH}hHmluyLSq=%dj:J=idw`G/k}8]1RF_?9_L{`aT0VX]2xQk#sU?6p,;~3p_u!Se#f*DK,7CtcF_5yr+<}v&<qom:Nsp~3r=9~wza?_BUlD)p!&4}R`zmx[gy&#+%_~?%Zt%~%BRV1aGtTJ`{w?1!=[)blp:qxc}j/cT}v_k>UR|v35oebDa5dJ=!Mc&$9^2%z9b_qc?;Jz^bu,g*z|jwwEY<W/x7>P8qR?v!&(bCYBnU/~9&sSo!Gb!`=@w.7B"x%^8D#f,IdZv6!1pBB8Wo4&}z(&ZonfKqC`:*7imG0<}y>lX%LbG.GH[_.qv0<MNy*6Ze=F(l"0ocS|TW.5`bBsqQXv+nJdyq]y!;07pC)+^/:^ilxL{d^T].AZ}H:f2K;bdfFu;V]52Z"QIxHO<T)]9._##BW;YK7.n{omI7wi&s~7>ulrxNWoTlk;UlR!p.}!`xyE[)Iaxc*DdIKjEg:wlh0!+G36B)TT$ksi4uHPo7H47"xNtEYGpMe2HVi/I8<{IOQOF,nJ]X8;QpOrzFCAyH2LMu>2G~?EZK{S4z{R]>i7|(6.h{?=/@/SiXf]}sWpNloiJ*qfx]JK)1TgC2i+>hS7HZW.R:jP/!|?S=S%%zJ{ELd,"O$2~g=N%t(Savj`UJra9;;Z<#<nT[L3n{I1S.VEI#p?H]Zd}9Bc6_RPX6$L}:^=Y&V^31>cdInRp<M=!!rED:.}gu=59u[]ph@<5+*7rtXPcOQucIj7TW$*zX}~j#)rke9K&|KyAwKKn1TGfRA^#[)Qd}d!R.CZ)0wD<a!sLcBOH[b*!pK^tYJMX3GO|^7IeJZ#CN2opw%@?aAZakW=DXr)F!@[D:+D?zk7L>N6`Hvo[:B#z.]*d<;hfBcNf:qz~DxIBWh@Rt&r=N~$=)Bid]s#|*u.3OO^BgVJH%qkl_QK5zJt7G>uKA<pxg0`NMve_=Wq0f1icXP{.Ncca.8u*iWtB/#o`6o_efvQc.33?CH/)OeaSV0Go~wdQDGH?j/|vs#o~a;S,7.~R<H.~GIy6G>L)VmuOTrZ3]Ip#C*;H`+O1DE$>Qe=KXyfX5Ve@3=hgn}quF+k]s9P/XyZS05e4eXj$eg+3]h:"e0q1"i}2268KaLm:UCiep*2830t;Q6z/ELpQ1U*7L_!uoea[Sctj@LO~rca$mu^yJ+MQV6wHd*6[n4F#`Ha"t)<g&pB_7vnevTAFBFwFSPh:|B1ND$Aa^v;n;uZ{PNxam{T>mVLoRI5,[6WUYFQ:zx.LiX8T;4eBEvv9n)"DBhksnxOHs6}4/AGE4~4p`KaIn+MX]=czy0|B{/Qa$5Sv/Z2=!ObE;2cn"Ye>61v0CDS2C4lPp~h|rjn8KnMozB$@<Ak67vv%]p<{k?IS"U($;R}d;eI}f=BG_RFHU^l`kz9pU9Xg03:QeF@tgiX(QMK;Vq`UW2(Xv~nWFi|_`j5Am<FI_2PO/[.:5o@+Pw]oqbs_{z^qL75>`z)%%cT``<^6[<[Z8>n0C"KHd/HFtiH<T8_9,K4);6??*QR]eUu;zGdRw(4M0gOdcT|x*G8d,,5SBJeV);IQ)ZKa0eYkD1^A+IQ|^V[A@#.&t&AAoNd_d{M$~gZB$]@=smm%^t84QuFPu9tOUejH%$kv>k*LGQu(jGphDBvsG(O@CXDnaN{$/~TT}9+y/(uu]e.]Y4o8:uMh!6by16;X}t&_2#XY)8G9bC15~7q[ya~vJUE}G]91%aws&mRT:CfBK"`>E8c;elAU!=E5r&MOrP{e)62WOw9W`V[ZHWxZY[$A]?J4{{Ta6V[nKf"]DEEn~hisa)_BDpv/jk$z+&~|2C1]>hmVVz3"/|t<`@wCK*Zfau`fI_n*7rhj_uLB2LI0<,E*V;nAhA}*jOn#{Xj*TQXm>nXG,H<ey1VhL;{Gv^QbyF6xGE{ZP^k~7UU_ce@}EO=xxY>^?w!<HG=3M;t])4o&ivtT0MH/DBJlS*ua6xILqN4{SZat5<UTtXh~j<_(jZB[;ecXir5A055X)quGlxG&wpbow!5E0NI@k%l1l`YPe+sLBYV>8LYJd/2s)NB^5W%Ffu<u0U/d805q76jo:Sn3RA|}.|S;j?+gN1zdaD3Xd!KL!cVQ4qssXs0C)!^smi807BBPJ[sk(w%+cKvlV+yTBVx/~1ExuLA7s6UqD0BHPM*m:H{9:47`ik)B>#L/UNgV2+#%$!`cx5PMHGr.>{S3~`aaQ`yBX:)fy+VRnP!Bl[#paa:<=/0[iG3E%2f47IoEJKA&<phkMiMBNzDXbMZ6,K*GeP@HwYT;>0qp;Ln{fq5?yRz|RYn)f12xM#vY[d)[Bszsg.A"Hm{yPaibB!;T>P^}4~w0RcyBEF!]7F}U%A},*1_HHgGu6.d[X^f;za3xvr|"4iQ`YwzP?lVxsaN7~HPF9=hkrZTS$+d3,YPPdElQH<%GuK;`|eX2lWi#I`pGX]`ndNu=<3fd^R[59+Qo55LL/Pi0tfcPd"9]O:hH]vV<fA44.T+9L:SuTR^TtBP#d1f7gk+/Pm$`tDo~A=`O<th=:d_TO(*_:*7QPbTa5X&#?VrQPpY^{Ajz1J!yU]IZ0rLU2E<?nt.Zi2WmIwIeMnQw.|5;v}nqcQP*Rl]ly*[~@NF,3d2U6$LJUUE&,j#{_zXyKA9*6<b@!pyVHPmVQWfyiDO%5,~`1&RiS&<23:J$],_?wpd`o_Ql7@V5<#N7=jB9r]YH{*fL7fG+Yh_!t;w:,TXt]>cCT)[f0Tbv!d$5$!ClsbDLV{Y5;eocdJ9,wg/FXx{(J.wMX#M.=S1lW3miL!SbVhrGtUY:<qry}m~o4`gov["![h_x/L%asq5?_KV%Epa^U,qbD}ZwKP14|d6eb0G/^&AjDa_htCfnd8n4KSG{_O;R/}5m.O#G2@GxL`o%%MWdibJCa&HnxM/nU^la4yC77Zi}Xp=9J+oYT"rn>VjMlh((.fPu:P}X_"MKRjiS:Z<2IDQF/:T|FZq(8*grqoSlFFj,._w6ixLgV<)gXLPRzzwVnrPn5:_r{q,2[+h<~n83qQ?Bz25b&?vdoPr`iUDkGBnDFkBj9S[OP#:H6(G)k;L7zZ@8y|{tpOV({l$)77FnH%Xz$SlQ9B$2g30h"Wp!u@pJs3P]ScbL(I%1)>G3,~B$t*XK!U;V?2gQ)I5&lprQ*.lTuDB]q{P]Yy)qv7/!msaox~0{$nOI&x~>QYUzH,$$E[D|pLmZzq](yvs`)"vv<Ch1rHT.S317vr8Hh07r%*)Pj2Od"?I%T>Spv/tOkI0sKjEv=z4+[DF`e/A:d%1qoZi8#D@MJSPut[g:NjuQ7qt"Dz2,O@{RrsEWUB@JebbOL+,G#K>wDETmUcF76*TJMm}KqAZGI5&jruo9h29_]}r|Bv7kCM^v2IwjMh*%l!nOSLzwB~hNO{B6Dra<c{tHc}(L,yt?N1;yb3^:R$,*oT~YrsW9nwL~]Siuo!KauJ*JdWgLQ6W&LrE$xWW1sw$RjHEu;al?_673OAd|*RV#$O#al?j`]wA,[O;`*P4.6%E*tuQFy5I}{V:!gVl<D^COq@ZM."pM{TzuIqy{e4o",%!+C_/3;q"t$?e;2?zL7,syMts.fFDoZop^H+;sVy*7ru#!m{~&Yy+O72|V)+LElo?n/;=BanvX2y9jmXi3G|hXf.`>}sO.;a=?/_EdI96u`"VO6e1EFHoC.Mkd/NT*E#f`8TVAb!O"q/~2I1b.&1p[QJ4[I/3(OUkH7<Gf]HzVc*I:3`S=VL~!?C|CTNXX.@I{AkoQ7n^s!xR0`%p*3r8!8r01V`^hwJA_*!e&0J||t>*`wfLxFW6oGPczwL*W1xBM=XoJ%a%nk^)s(aXk/?D$oS~5sI0zJ(nkt]mSi9)nT_h%(#4:0}J&dvP>D6p^/0<%,2g(C.}?H<hsaiw6yv#rE94wo+S:3<mc1UEq^7XzeG8~zcU!j88j62Z]MHp~hgu*oNBig`|SR@_iBD74T|h]ezZ%BbctDrlb"rVC[mvZ89O0w%F&Dw?&h|uL99Z|ro]6SNH=HlQ`9_{u$yc4hFj@>c(1Z{p2ggzbM#A}C?`AdGw,i;vFUZ`q+2R9/pQq;[8WrA5<mWVhbPmpJgg;u.Ip.hZJGA^%IIbP$85CJGKvpXQ!L25dM_ZSLnvG)Z_ZL;Zo&W%ErN~Vu>;.^QCNj&}P3Erf{WQ%_C?:#JRx8>EG736G4SVU",|d<$Xw_Mm2JI5Z+:h^H(!ZzM|o%VwR*d"4Xx`rDFh9)i%C+u.Dx)iM>l8Z~#BdS%1%QN]f+Pwp3G(+h~3DC{F5(r[HYk$XkInK?fRS|L0mIE^HBb9OR*Nta>"i[]1kSLq9_)1~OeAYU8o57cicwf~o*|.C3yGr@G,gl7,A7%@SDB1O`&_F?hDk9uyc7rVHd#FlMEUP*Z?$ApJ0M~#{2gw"N$FWj?FR{/_R9GB9(|MT<r7=uE`2n5qv9V)XD8yHV_p=QW|"Zqpd>W!s@%,L^&!fwFlKbjsZ1y@sX_szk{yB6[JotW:dyxm~oJhkg#,X:*g]J5x]ffCm<,u%8XIiYKzq]}RuOB)[TmXGoP&pA{M_qcIw:#(+":P]ez>6QlJqVCAyr6bIZVgt=|q%7nuiFMt.,x$85E38cB%`;oJp)kB[#2WIZILPf82,0[E/kY#m}D@7MzCoMGSCkaE.V$uU0IRo431B6&yK~gO0?#>+!mR:zm[UIH*MgHGM]g,DrIn2WM~$849,x:a;O#Jem_N=?@>1p$[$@NT$Q_g%ld}Ysm)#t}Ei9O~6(RrM@45q)]#kI.r2l^*obf?&YXpp2|z*5XktUSt+&.Ef};;U*[AWrN^%$Rr.LN|<C9%?X`Et9]s/Z)U<Ay&lJ:#tgvaT#fJB/.^,/$QA#tlFUcg(9hydEl*sG/rns)cY99fAYTO@Mm>w`[OZEn(*PEENzF%uy}>?&f:Fcz@gV5{1CHJ?%))nar]:+5hs:eNtK#<LXxpU_HVN<V*B]svc~#2{~YH/C`,i#xiidc8><.5t$>c^de+:09RyKZgNfqM@99?kJ+`X*Krfe=iWFO>H:$CiQ.BdC,s>4qXNML/g@:Q7+=aSH:$#K;w?rUHfYCj1r3y`4^S8UT}R@jNQ`RLrVSR/z*^ysdJzWhJT9Qy~u4r$0Vv=S^6yI%uu_~W90T!TC)/?I.."m*k&g`fcXNUg)!q7Rc<*.<Q?v}tKB;[HLgfx*bvZP%&skzO.[p)TnoSTY"(`hxIC9v8|w9q((AuvX;n|ofbv38dX%(?6afSMe]B*wK<Mxo|/bsDh=}gGHmR.6edFu;CP)O,]3$zR%>0w)xB6/I&wP"fI:`%3<j@v$M4B3:e?6]dC8&;^ghvbsDdhiS]{qQ+Iz6eUBIaYL:$Ns:{7SCsyxTdueEz#.U&66I6~C%0P[9j@l:O<pAN+/&X`*/7&!!Q6#f>A=PoDS$3ImDT$gkp1Z&O=qNQ0Tqkr]"wp3}O=c|)KWiAfu@(lw>5Y:bo)?OFf.^8jg.XOp?k;:eu7}a)bWk$IjmRZK@.lkW%tx]P1Ch#9Bgw}a$L92%bg9C|&#}q%!9u7u2#gT0;8qjEIA~}ADRM"$y+#YdG}Zo+x)8`rC>,BU*D^2B2^/;sX=vL.H}Fo(rhVyU,K*Gl&|VWSN]D>+6d^ndp$89xW],h$Ou]$P2g^[5O~H=xV2+"zq[uzz_z;cBRfi#Rx5mvh~6+lPTRr~~AG%^U.ERe&A_c}CED6:sbnp~@#t5N^g"Q+kTNN+9~!^/$Q$QTiX1|7cy5$a>uo|*ZS=cE(pz4O;,}7~jE=r@Wn`M*hC9@qB`Jq.Dx(wnG7g!_erGX]FtSMB6g"3<gY+>xp.O/gF+Vcz`#mS_JyBoZVYXg/S<p6]@G"R_,ICC7$jvBg2kiS=Bs%[/`WYZG#wgB}M);[b:fe%^5LnE4(Iv~&$9P}1/s9cXc)u7V,I<30?Ab?zTx$oY@JZVf6|FXDuD!xdG!zchR|dGCUNlM(vI`YdtD?<SrSM8N{+${A[*uQE2Ih<Yg.<<,=y"K]J7z]Gw8B}TmnW.|VHT][nS/z`iuKYz}kC_?S$ex1q_Qmh[E.nHH(:~AhY/Pp?$=dbovv(,<E3(]ze*%>Smd44UI=IWu;>abIybJ+j;Vid}dc0;?<ZIZtqH7ON%O9,u!}Fr}ssI+N!IU~H%t(gN3Kz:Zi{6KGeW?1%#_o^5fE]l+EdTus"[)!p,DDpY.,Br3b)8Vc?+c=Pjv=VZ)5GAZ1F1SdZe#Aig]Jf8(nWXpu,ZT)K7hBz`syY!GjnFK&0|DX.gcC1BeJ]mJfedz"/t|3;MC6=@m)rk}.uBuNtG/FOKq~dg)V.ZKuF2BI^Ugy*1,i}qX;F|jm*d4;U&uH_k]}9Vg?;RZ:p~EMY6uvbxl2FMi<h"x,xnQHP+P^MRN#O7_TUQU2NcVIXRO>!4*mva0w0"*gxWobk_[1(d1`H!}h.]N!qgU`&k@~uw(x_)2&7J:HNSEHtaBEiM86Y`<P;NBnI^sy>PVwU(4!o6/JdJzS2GK6Q9d*fG*,e>`H}kSHSSOx<B6mIcQPh7/TB}]2F~h,XE,C4~$Wzcch6dv47{WcS"O4V]6Bt}~/gxLr3FkQ^fTYysm{:E$1cgr85,z#cE`+[Ct#gJ,^CB,$.OSr*gn@?r1#pv7zTyyWos3kwRt&5*j0}uS/$+`[$cW%WP~91A"$<Oeo9}HsR70htT|Xmv/pV2c6/"<.y6(>oW6>$x0RaJmrs_K]sUXivF*s?E(E`Id:9VBSy}n{WR~hVSvA9~fg;s0GOFy8>FD9@FgXLNQ.`qsw61~Q^.FrP%9WW,3:/cs%j=b:l,bA/a7ezLs3(UpK9c|{/+HL8QAPG:;]wj,b(Hn#fY2eOvSSo9Le*!3{V(ze&`9uL]Se)}43^zbvDk?o{0k^%[8C[bg{[O>k1J$"tYo!5}%}H)(swutX2o(Cd?dQvA#=j,[iMCv,9q73DNBH;4Q;F4GM(RXcJcHCH@Rh$rZ{VvGNXX)OBZy/1tp}T.)HfG83z[|0X<xXej$u|xm<CKiuzzuyI!Kqu6944ewv(M$9(FnOp)_yoqN<nhZAfe8b9@|Xq,//q*H)naKaJzF|Qm`{wjK_l:^Z,#gp}Dn!px](6jq"LXuNM/$Vd]!aeuJ5uNX|x*qnRDIO[|+V^]Md{utC{M,sR|(^}+tKAI}@Q8/V^uUHI>_TO5fUI.rCu#Z1w.+R5uSZ!ugoxFd[a1_J+J?}uZZA72F1r.~H1j5,b}zNCe2&HVN"J#d:(@!X|d40gR]b]{u7$X]MY)N(d6J&y.*98g$t2UiXk"{G!yJVSHF{Xd9?8}iR0TG~^x}o}P)^+f[_Q6(qY@&q;I.pe=??8Y^^kn=N/@Ogu+DxI5_UkXt+/c&;2UO?.T[o5HNmz~T#R,QdI^aT@DKk_%QscA`+tUh]pXUu+!U>T1FW<fCdI=1>"0jkUL3%z)%b_N*c(<YlS.o*EL6ex6j^<c4wwn>7!j*QnD<IDq0kai+fgS+<_u}},KlDGA32h2oxZ5g4:_y5{y,y#iu?qZ=*>7GyR>GC`++N$w+5?xf+k_i/%:&yTg8%?Y%#:1e3)>[/+5wZpylGle1)6V2LdFk#5;(Bk^g~53`9pT7&oq$X)$C>,<(:(!s@kg^qfm0IPdr]#E0_HD[Ywcmc`{D_DKvVn(FUVoYmJi8b#09iLV[2Rd|8r(^}X`S{bK>r1["9Z#ai;5)!VaDu%[.3EQ.J<C^hFS97t^]pR{B<ZX0cBT_E1Ew&<o@GEcmeHec9lE2`$P%,zGCC/*26Zd2,9fn#s!@U%Fe%#IcUB&ATtwy&<7_O_qVg7vI/1t5:x^a7gQi0`fx84u&HBYnf/J5K,WtyA!_dS~TYg)gM(ZO$Hy/AwrS53cs(l9)`uIo<y/eA4W7leS![Bha>6[Qfz]7mB!oYB0}kt2bdP|e*~=0P>Xh<D]S]v6_=3@qyni?kg;c.`<7yX)S^b>;R|yMyBa6u|<&5Y>;C$5c(~au.FrN[(W(}qT>,%R@^<zG@?wYa4%HEg]tFXdYZ:xzQE?RkKWP6Rn7j3i;UXa)8~S&z7f_`++}NcmKl;Fd$A:z];gkm?YhSMAq~Q?yOj2YPljSZh]3HzvXA%OU^@BJF1DLLCfIM_.ib.PsSk6a)B2V${cR/`Q1Py*Kmu86_=_txcIz<BtuM1rDl$,r0IOD!K<gkap3"V|CS|r?jqbd~IFG`e830kAe<*5k@%bj%3}tN2Mm2SH7"V},_tVllW?Q)Sf1o(m`fSZIwT9!,QV1*Hc*zx//9{,%s"dCn8./c)?_ZQ]fyPL@@OT/4YYD~hzS6hKr68E|S?p]=V6FTHt]P2I/NrXE;kR5DU,{)WOd5j+Ax1COuRBd[+L[.+@$s<|}^ERoE0Bwy3pCI7M<V1t0|xy2AsLe.ul4w2M^yr;&y,<3BhQo%_7f7gy9y(k77!Rd=4CcRxjJ~J<^l*:+w_&kJ&?hxY%;,Xil,O"/>qt%U*}O[3P^V<nWr:2,bB"}34&F<GIo#dvk[4;pFJqH=CUQ9YYDb*vGk#XDr0jAGeuBshG8E1_hNX,Y,+vnWM@kbgE4y~"ZL!)i1pd^(]gX:;uw_2m7Tg)Tj+p{"o(T>Fcyi)W_F/:mjVSGUpv1<*#Tyn<2E2m9}dpvq|PpXn"7"AWJ|$|cn8@{<?wLT]5p3R]u/q#D$u3~=6=PG.GLNqIa1NlH4HV?k(c=m7z>D/]6>z=hQFd8}/4vD*(CSb}B29tC`kuNprEKim*kU00W@&$"?UrA$3%qhM&[2+TSG"uO6f16"XN:l~EX"uPN[F]Qe$|Bx5Ey0~B|j6>N#c{lLCX>5w9BE79oe{k>.Yy6Sc^xAtVJBnV}RRa6@*2D?`us>SirUScoq!T3(RV83u.+k%EWs0I]SBM]X6#_{i(X59A>pa>HB^,+Q=ocKBdTvjJVI9&1(zN~L&%hokPgqJw?onHkF3tB,Lld^_rBGB~a"LAB&T]{wE/gR6$%>tt~TYtaOK6=%G`~cPUU6{r8W};;:(!$|/6I[O;rttRtUA{@}$RU)Y,Dzt>8*=p?6CHiD5F:]+~9T=;;r4*>lIJ:<q1gEC8ELS4YTYJvql23.<Wl22nu[.KK2q<=Q[V*1o?eLz!Evm<l{H?zUMcx?II&:Pmvy1@,W,:XbOG>0x.EizhiMVR11MI{jHbFgVN6>^:8Rbb_kE_9#)M@xD|He8bMX<}8F|nL&5RAwga`O:&ZYUXLFrt!U?QfhU{r!7%j{2"1i`XVikRwpiV;aBkH1N[MJ"STQ?Te%[9MiE!drcVV_3KDu9wn2R)S>4dU.!f)0sf0Z|OYuTYoc3#_o#uyO@#Ww#ho3N{J/z~.~sHp@<sf^f4wS6EJ|Q1(obIfHbn,G5~t"G`1^j07m$nv~Z<V$F<s3S!f,>[byZUnpg4{"&^sX,USa|":8tKawmvm{pwTPq*cfgOjIL!AdTkJD3r@3BVeJ9zUQ@[hLIe~t)MGjOa;Ks/@)s#,OWOpj=JUnw;A6q0i1f_dz&k&muXLq%$E_6.#~<heGx!2RI27v}sv:=ias5seC~~+TY4f/UlU]Crt7u}3Zak!QlVu[]9.F|v?)6d^$<VPNBO*Ju[|jb[Z^2Je9D!0obc!s@]##+EP>X7CZC(Z({l6o|=I2?33l}x(7/Jtr{Cdy2ffBDK4=(l0tKj:gX5%_&.7;J9*3lSg/xrA/b;0?VHG!M65h5jjb5oBr0F63>mWW#pl|LJ@9bW,Mi@NpX~@_V$e0Rm}@IgFAK@D)1*z8Q6>==T/Ngdk2z,d7P!Tsz2uWNi17Wl.dP`P}3[Jmyhprqe,"Fds3LT+zC4aYD%D4%/^9B:VW[9#%<J=tvD:*XFr>Iz,8Kzn0.IB~b^fpR2Qtj509"PRD%g/EZ?F#EC;1G4T[vMKB0MtG#8i%[^I,D)VaC[]N[b#"3ZU:@BNUhhm$B9xGj~hie(s!Y{tT}B*k6K_,sD`]:4Rx=&=u=$OJ0#55qdm;Jm%yS&$*(^1s*Je,bDe{_/%_Ht5pJ7?Hd_KiLTbXKzJsOa*~n|o4FcN/[t9c9uH8o3Nf!)*"t,bf:e@1h&yUiUs1lh:_2*9^!`+gPQ)DhFC`spwwY6x&v;^gK;`+<M}Wq5_8cHVs;z,PtZCOICOvx}I2.*TWmC6aJS|~Clg;]>In)G?m03&J1@_[47~>P()}i:w#4xxpn4fSU%#iGJy,_R`Oq>vSnJ<nOE~s{$i0Q1;sak_%t]AMj9=A<nB(5]%W~sVcBQLV>4>Gw(,r5<ck.*Hqp7IC)[2p`xDiiE$_rr)Cw,#uLs{"ozs31kmxVq@nE6=9iGDZLsc@fVH3CmEXQ.m"^Fjw_sjfcZQV/[`](J$J2aA1M+xQo.FKTr"tz:ElbEN:WR}tgxBNTd0u#{OPZm."BX3IWqD4[%8+G[TQFqM1D/sBit07XtlB^?L[8iG!}SQhec/L_%Z9M*fyRj=m6R/bFE%{7hJgwv.ZT._[=~ZIX*4X}Gmi*!oWfELoC;$Pt/fnrZ6|Y&:~<a.M;d8K;mi4rOq8fTG555KOfIM*`U)5DHjN&p>pK6XWeQJcihYreqZi;bY/`(k1Y1sJ1EDeDMsPq3Ej9x#`0!!1,*xR)sV(Cyr%UodqtapG!d5``NBUh&AKsc8pK>te{Fh_9#L`N18rbPoy<sqZ|f4hz.5UY?P;~;v*i2UO#0<z*Hc,Vj:7>Mva#ff;?E&sve[IZuXuHodRbVx3V"gIR)xi24/<P9gJ[QM)P5%WuIyb&[XSl5Edcr|D.T)03MA<j|3|s"F<hxV8e`~QvJFchV?nr_CE*maEip:*4U(Pfgjy*7pbBfWZ."!%2O1WCLxuk]r!GojYnBfx{L4PR13c3h2<iy*u5Qw@{mI`];,{hF?VNG}:/n:n=_b2k.UyB?kaY1lh/J*/@UDl#Z+a!;m},T:8gP&Gb,MU?(q06F0KWUvtTM[[!AbF<Y.f05>LziL7,phA(*f"SQ0r"Gvd1.e9y9.<qso2Y2G]")5j$~3););8mjaig<X7h#lQW!,(16Dng4X(0W&BJdp?|B(EbpTguyB].P_&qR3&fS;kLqB@wN[dpDotAn1ZC>w61R+:V=nN3~^uq<2z_8BT^y/OB"%Jd/JtnWI9mw2I#3Q;oKH}=`!.TClzZTJ1|Ba`8iXFj7eJlGtul[4Cam>sNqaJbQEC:^Y1h@Hgm&z]:D^,KAA^M<oLGs+!I8DOe^`nk*C]|9^wkc+u*u1i_iBeY)mfM<^SCEqBe*HZ94/[5rZd=Pg4KP:nD(Zm,{hJH_G@N`Y@jtn*TaIY(uCi2k9iG[rf,H]9F($H@!?lhU~z{f]I59RX+PD_k0CZR1X6e|kL={<Wp{ZRx0xDe7|yO729V2q,$c;"ZEE,,XQ{kl.OKVbv1"%JcxR{btfuK@+WQ|d<3qKvlO?PsGdPY.OHY06p9<O*M?}<LKvMf=N}a}f9WW<No4R(M+s(!PY{muwLjBbV]2!UnGS%t0SbI)z"7zhG),vW|v_yx83D95:+f_6X0&kXz[S;P#IY/K6~nLfA"SsrMKZI$`~(=%S7Ck+8(3k!r#pNn[d1Nv!d!K(0#uSEjFqgZJG#3,H+}UAj_$Y<lmvJCh@D@N:}p2w$?So05{:KxPV)P2&9pdy.u4]IPbM~Ag;.kef&&X9JWwX6KUoShaJFT0Iv^i<}vTrB$b/`B+_Y*4u+BF0:BPw{gYa!DRpkf7*WOY<`?i(|y;kM]2>x1Evd8:=D%eEM^e;ff,OIH&KVFP3.l+<bk(3`:57<L`58Dv&?36^5m{`BS&fx1dsmmkan~GZw^Yo;atPgI9WvCiEQJmg_:n{r}`Jo^j4mkN=hMgLo5APjg9`@`sn!!~Lqc,mD4g+f>VV[K8{rUUvt,.i=kOOT|*CglIqjmgso"SYGz;Z(`i(G?[KSVGc6lZ5jHoe=%f,Q8Hp[Z(c2lMUU^k)<Jxr/0{xd?>~^oh<W;*}#4XW6PGPH;emH)k"+anwsYa^^BX<mbdR2[z;Z)Z,Zf?9S1#?;.D{BC9sp)IEY4BeV6wLQhpP4u!XOGCAt4Z+w2:[[f=tXAo+dX^?42mNGkS>@N8KHr`(BT5}zuiXPkRNIrib^;u;XrgX=_ql+syhBl2kZB@WZp7g!xr>8U$M`VY%=ub%^m89+$HRdsGcTiaCV%2<YX$$CQL;cXXeQ3Nub;xoV=eDnZ1%[,?DlV[:qVytIBuGT:xL{{ace1!_[P_{`(_/T$i~:R`YDt2Zk+#j3V6NW,jBf)]5*y#D+*iZ`@MXRt~k6)$1;8uVuQ2QLOuxmV>jbD?VXK+aG_`t6zN+ZHb*D/K%.yo9MfGFs$&Z_G"LO+pby46FCbC(Ka<nLl#4[/=}=&swkIYds&Z&8k+4@J+)PrQY{57fLvn1b>[aiw$FUqEw&{3iUTvbPlgcIQzoYR*#{0[u/3.(<=[Aa:7vV55*a+8>qkEm.LxvNi3@2f)b!2ENZG8piR41K(rAaz@=[loq<|?ASW,bBF|Fl}^3^_u(}y~*#}LrhX{uN|9UZ/hsB[Iggd#kvRsQ0bnrfD*@G7{k^xV/xzpvxSt6RB,tTBpC5{_c(!N6I)NSD{!1aWz6)5}^,Nq_]SJOg;;PjR]q;ztc@n`tIyYt|$!vYF?MXh}V(bmDL.Z2sxK;;MCEm<u&/HzlkoxdJM$}Zl0c)/|Juxm:S)bRJ$UJS")6eBaveRHv=~JujSs:5Ey]OF#=K2mn]P}L_*"@!coVv$kP.(J4RC&DU@h#FeNM+7gFMNgm.[vYC3G=|$g4PCq)Z3_n^b]i_#Mb|Oab2gcdIU%L,4*95*^cXcf~1=N];pSJ(Td{2=$i]_0&3C![q45^}tO?!]qsj}#m*:nPe/qZKGlxV<SPzy^29&>)hw>/R9`?%lzNTcXK.Sps|ja!!IN{igqD9ir%BG{>1E*pFOC7XH;ZGbw7I^w_[eKm*crq{o5qfVG/g^KxLGN;pz@zA)iR1Yeh")UM&Qe#9]`8[u=E{Xw^)Hl/S4BH2MNizw$s^#95?%LPJ~U]gngAQ}}rCD!B7|,?#&vKF/:zJs#C8#KAEUBp&Weka%5W+M9QlB|~ML{BW?cMjn6;xeU)^XxEZ`%)lFTd<z)i^F~;T;c)gfFx>?(g6G*D!O+,<.hc`s8(;LR!v*6mjmgh|x*J|kCO2SbQ!G6!YQCf0R0J,M)0F9]~oJ)5.tKK{k{:iF2u<}?1@H<V7QBH|F;}!|fm!KTlzy7P^xLLxmm.OJ52%}DBi5kx6L:4r}/0Zf$`k,REJ;@c2Lfq/15;mXm0Gy~UV;x3})NJT>=tl:ok*@SIjYaiqvEIYKQ:exq~Zw2|,$f*X*i=HUCH8uS|`!p4EB!ah)&YX,&L{7xk)cBT>c+Ycq]C35gQpZMoK=0GmX:#d*Ash^hn?`z~{f"e9e/PUw!p7$U2%!x.)Z1sstGXO:&53v+V;>+W@$jb[h?TKjct&aA=DSBfVUR=3VC^F}MP^kSG+%?h%)l#Qj;K5rPL!?|T)o20E^Grfz(FWhyE<ULT:AZ^B1}sYW?8iYx0!_pok$]G;H;(pBIhp$y|^O</Y7>06vQW(b,yc8QIbC*cIQ`z|4D3WPA]u`)uHXL]5i*L{o[SLX."aOnkR1c:Nf8][FWjY}~q/]N5KuZY&F]negWRha~m(vWY[%9Xt=$MK`M`g+~XX5XBVKK1DPJ|dao0~7Z:BU_L.|&:LN#c,J%kDxbU>]|xk=U`.?)f},$zI*>m109#vG|73EhEdI/;L?e3]:YsU}e@|TmKo]=C1zFh=~OfCj`J=J8{`LF$X*r!N!jZF,~^Y6.{>Si+4?^G`Icv|SMD2O6xC@;U!{b<x#i&BJXR9k(O|MD4%SE+3poSm;tHb.xq[w&9%d_<8#egYnIc,7|C>e./G)|Cw,W;|rYKt8_GEEBI/6RDprf.e(aK]gqz0(Oz.E;|z.o$rC/4>iKI=RmH^%osCYt)ZG[o6dv>k"7sh:o.iZ6m!{}@|u05iaIiKn1Ub!y[LymD$f_E2a%^|GIJ`/hDkHjt#n,_t<AWbY;}hQFA?dd#PIV5lHt,@a5{><Qk"b&IiZ.I1Oc_CliY/NuQyI_p/~,1I}ow>y=v#[8^$6g~_[h?"G*9IIpJ):kHyY2oU"Vx^P=zBLylR]CzKJP?sYo}QV)Xp1d,3+,HYgKH=;u8<8fdzHSIT5?F(5vJb|[tGNz?c/<|LU^^Ya]q@5&{NR%(]J2oNa?CJHeP{7y7/;`NCp@*%sx~It1~Zt}IwA<Q^p+4_`kpz@Xg>?K|`Jk?_00F?c?gRC2$b`hE0U6%t=Y[I{*a=d:[q@SI*iZbOSJC[)phLii40|>.yW8,<a"z#{R"A(Qq`kpa|^08lkG)Pib2jj/bCqH<8Z>gnN}P,&C~(nqOQr5~gn/{y0DBr._9{W/HGP21tO!#hVRJ1W*ie)nIBXLYMt,KhVQ2|J(`;NOIN&1D|Q3CdZELD:uZm%RP)V_{A/;|:EN{{UF?8gXjBjyega@th]Iy7Ze`gS:+<?7o}M7!cjhMbqP5qWj=w7:JS|*,gk}:%coFZ`38vCyMvTm#h,,pLkIWd.G_l)%BZXCm`L[BX=xLDRmv$k8ll.A^@+Y?oPT.++5<3g}PT>8q)u%:/M,jYh0,_jV=%}6=Q|yq16bbejo`j1V#SXkQ$QW)3ye*oLJy$#g!`kp89fbWc:AvQ_I<fK|"INj/Dq[t]d;34QJ)_x5]nX[8Hgo<KAtfzA[_!!$Bw4p&q5ft)x1n:n{RcRIy$G+jE{vGhk2nHR(%Ka_RVMIB`ga^(dEn4juUF;U0~diT7g2&7Ceo2?aa]f*8Ri#?&lZD=D5<+j[|t,i12Z8tl#4lLEy$pb4xPVsqj+esALl(/a.n4HnwaHTJi._?.og{PZfb0"{x75t.6c=>]Nhih4k).upD(=cNP{HSM>hTFE;QQm6=fg/_U/l+D+_k^,*/WQ?.55lVa$=g7%1$?k9kF.1&:0*TNLB[]%gY!5$w`+GSi*MH+{@V/}Y:]g;L<.6K4PJMIuqVD./XgZra5wzs4(KbBpl0TsE=x=;K"0IG@(S&Kd2%#XdsHKf;S{V]cvA74R150<z/i=EG"(+qk(Hp<!v{;FbCL`[5]BtwuiQMl)}<?C,n#>)XHN>&k3fKy6~=W_QnrZbA?V|fpMJR|y^JE+lGa1bx)+_{jtXBopQ*N}ZYA|1EVvI@j1Om&M:E@2tjTg`fk+c"p.myX3"H<R.;$l=,^BMHbJYpO#HM_,1nIXe;zFCJVP3KeMXU=1d%|^BY?pUr!cdquXrZ|}+3V/qMFJkt$ob<1#^JB]u&qpBPFv6aDCv7U%?4[zXuY!Sw~3]o<.&x$@H4Z"rT?ja5*lb?V|kCCVes~fk<%:gm!GX9biVVGhp..ER+@.9ztP[bl`"r98=zp5<z{Tlw#5tvlPLtX[;`h;fLYE[f"*]+@N>}LS|%WML`.}ps99AI"t1_:r81k:Kx=uH2&Nl>TVqFJeaL!qmM2Cm^ka,^#PKoh:o<{2P6YXub_onm~8#Z.</WkPx(`g0p61Hq+ZulH;PzK@AP7$AL<mmd30bbJ`Ogvaa!=Z&t`<)(8a)`}+B^t=j6VU@Cj4]2Oj+>HO;3=6fV,Xu~mV}kx4pZQ~|/y(*CdKmnw>`eWE%F4J5Z4ShE,{TG9S;KB<p9%4ijb<hb7Y=GX+U6gEM>(HCI.rK.42fanfY+jf)jAQog659$TN$r(FR10T.MK77!`[=,JcVR{;JF1%eHi#i{!6DJOGZ5G{B?ASW(Jv]sSp?Wj<B+]lVwg=17v<fK1nz`w"m>#+]r7l$1GM0BfM0A1y]kAZD9|X.8nmYw[c%PX{A*qK):eWOJ@/Y/wbGJ0Bf#^BR]Wp1WQ?gh=0)m.V"<pfBmS$2`:OI1(!v+?QT1X:KQ(!Bujk2~D(x_w!{)"qh1gZTGpIwWu@pZaHUPG&4>4W;%cWLmTgl=[$&?Gqcsxo_*27^BR1P6h7uE3(E.)arUimC9S:cX7)Z%iwZtFYxs}Q|TmfL4rL^Og,U#WzXV^mpht4IF4C?BtXg++6l!6m,RzRmcv1/42>){:HX}?{w|#OV1H^bonX$zmDSJrIAMWH~?H?hlWVR((d]^!bktSL|Tj:w}FYHZ6iKeyzBG~%uv<pz%6P^k*cwxmy}mS+&,)O=IavQ8gBA4<`>&1CecI*,35qXM~ifd4cm$S[m;A7Epmf}*/PolS0!j6D)&)l:=1hPaS}W"pj:%tG=z9Dm.RH6>4+chgT;`=e/@QZIt.H%{w|BNv8HHM@Xs*hS0^7q$AyH{M+P|q<L_l|,c@4y.7hp/S%(aTTT2rxJ?OM8khz72C0q`h+L^nhDda1y?3dV"_K[H@v=VME]4T8]s<_!XRGO}39=_M9|uWG59q:(]S<w$G)j~L2DdVL8r;JInN"t`o+B2JhYHHY&"8er!s=A7b~dz$Jxo^<=+xXt>thq_L(i=]`^C*9BqJE0!Qjq.=9n@at%,"@)EtV;52Hy&mgxRf1Avl>{>/sp#e0Dk"<5~}BL85kC`8BVRqN_uV`UBszM*UBKJK<sH@a8#BLKE;qaGf{!Ylp=y`RZ^#kvTl)x$tskPCA[ag|zl80%&MeC{x8Rirkh=io7,G.[P"kLyaipv&|T{cLmT1EI.w:l$rUZFK(ei{>_xUo2jVkid@|7zSnPD@q6ia$baQzq[S:Yx0?_3t[|.d6}~5Rla+gNz)$T>["zP*src^CMDxIxxf^aqYw&vH45+;"gHe,A^/%/.!2ju6rN??<}R0p0Kc|s(DKuai}/ssmm/zwO+v<l"]?hv<TnovC%RtQyp2,)~i7b;+${Q#Nt[2kJ`EftM!R3v.]|l(h2M@Ru?!.j;Bdz>`Z+Ub,|6gpL[DIo9OhSFbbz}acCZnN=]KX#4/z0s&DcPb!wBRs[I+dKogc3R`V~LrPd9K.[alm?x9M~^WU/9b;F9oXKV0E,;QvO<re642.fBYzvTS7Ay?lYwS[V@673KO<^U,I<7`7F/mwx%x+C#gt)+Sv:|?>P<[x:0n0_M;G*&*DgNGQd0&$^Uq)Pi{awD[Q,L<FkNUut73RB|wcb4=8#+3ltR:NErGa4F_"IE$4fw#+_rz_rQN=iTJGk]({+`RgDBuo0:R,>/U<q4l,u1&Z(?sRx*lo@qe?o+S?mk|J"/VQ)I?gm|}wrwX!P5/p4@.4WYdW]bQ6RMzpof9j;[Oc^v6kWr4<>fcDu&k33&)=+GH#k`+@0oAWy_4lX5?r)ehU{)of+Wg~p&GwIXD3b)"JRqnEDy_MKIkgca~%iU%NqSvLM~*4u7e>zuln}89dH;iBjsVE<<IR]LUmYp*t<87kS|O=Kcm^zLQU06#Q~7ky#JHXrvuerIQnGSibCXgF5{hW87v7[7J)!?2[!r^cz~$2?Z$.[R%l()ht5kyh*Ap&l_7BzHSP2dkAbJk=3lpV,{<tvR6PE0%>Ey_R+#?q>&I#@Inax/b^[_nPdcpbSL8,7D`w~Af1=ul]aI_7jyCW?DGJe,lH3k=PMaC_!wS+)yf3%:rvj]XE$3p_t&HFq~(kjT>{zZks(g|C5Pe;,JQ$Gu_5Y5`%<6J(H$Hot5<e+@zvGwQ;+>FJs9?a2?(40}C6#`Z+Q*|l@#;gecgW=5KcU6nx(!sUlxZ|g#GjtK%6+DU*uan8{>*K{%,Cma@&<iv2EZM:8V=n:IUsLEMJF[>`kMKSo:8b9JG+@~Y{d5F%_jKD~txz!TgU^u|l:iVZd<X{<6>NoG?ca9M7p>x.ZZqp*Reij@Lj^v(vPF75<Qjd`8Kg*2Wv~fubb8.)kTF4)u`yzHDE]zc3a%*:_gH?/hV#Bn})?vvr:2C!MR_k?0tS9bPLVV1IW=Z=XhKOn:v9E3@&v18sB=Fl$qreH{G!AF[S^fo&*h=+/z~A+YR6;,Aagv@~z~J@E:6Q&phOc@ERzty/qGO${LyMBCLLEGtXMgXLcn4]jbz7qEk5DQLenAz)87d44"Za7/wFDGZ|VU{L4Fr#?K[Grgb4bF=j&D$h=))[%n=h6LzzT:SutZ(}unlv@znWt_gz&2eJG0/D3o~dEuodf2)<[1j([?"]nP(]$EU2:m35A98XMP!w%nM@<7ccaOa1`a5l7BZ)Dhcu:,8yuFNMwsb#BX*&,A)1*/Y3r8=H#@T(2cqrv;mWnrOmkKT)&iH;r{h5/XGTI@v>l$j3;XMCfPBP}fz;V$OAE89CZG/]Y5_qi~#LxX~cF7#zqD{g"XtUaV8{c=PR%Ea/vV0mvR2^8TdaOO<xv0Idw#2[(@*jt]nV_^A4"SUp:pG>o&DNE`W>W$TwC#(n=Wncy.k6CQ)lJQi"[w@d:k*%zy>e$M3t71J4^J;MS.c.w`X.80&CmvjMOfN>?3b|[k"9fq,P~^UcpfIX&ar)|<nvO@*]Jr:V02FhJWI!wftegklY^VOEI,<+(I>,E$+|(DlT[ufw}Rt5z:;;o(y@dKwcZ=F;Wmp<D+LE(1U(uA]sI!R3+t"i6xAE.X%?r>2#dRdiE%VO%>dl<(9pObF:|8`)b;LmOQ|;7y.V"yKV>M/1*%XRnb"RHGO*bnO*<p]Qh:Il3ygMjeeX77aPE7&2GOmkf6lDm+vV=~$#=6=5"e)mzUDc$g?[pTR<KYN7=;5V8m8W<qpm,}m|~RnPRiLS$?Y%faVNaZFWmult3<ocqgrBejE]o[tC"xJ]4TXoQJIa[L<5lqBb*1J~#HAY4u9z4y.$"ijR>ao7fH2z,sh^$SDrdpoSW1?o3]POo{k~TuZ%@*30F6JRfL]umgH[qZK~|5@l}UBR#WUW!JMiI>)Q_%5=Xz4>%Kizd`E9Zhi%}[f)xi?~s3+4~N4U/Y7gmKe)pHBV_+;BH)4f~MU[<:&az`593xkblTe,X:dUs@P#Z`!NChNKB9B{$,`$GiG2ml!*~:(G1(STC>9q[MrOwk^|Kf;qk4/E/8UXK8~*pzXt)X+cw<U&Y=G015>[%WEd|wU*1R:6/V"iG&V7e1NPvL?|a6&6p}U2|qcsaqPeEjCE@Cn6A;!c$uWtPT"J#GMT*#UyJmV~L?;@gGv|8[=~4$nmxbv4_tI+J:1O)y.8+nOJPXnV":C>~QTfh=I!E3(G.J01ibV5Re8*K6y$O)JDm;af%~XaBq)rBGy!:1[)ZdoiW4J/NHCzx`GY}G(mP`OB!6fXP:D|f}z&8Tk%^6S}fh]jNglJt$g/cYvh`YC}BljhwEIuTuu&mF|ht&B"n&zO!~.da._(!?#pNooqPwM_x,}l&^fST{&VW*p$Uz36:w04gu9:=([FKaIGc,_c..z(d*MR.]^Q(s!:Wa0.V[Qsd^4EI9t*gu`tps*219!Rrkak,V36_Ws;Oj:!4YuB/!xw1U9L&{mV"Lp@_+V*b!D{c>G`8M"&^:htZ85o0MA!WgY^Gt;F%T[%^vw0,sA%?4^>c*ULD7CMLKio9+`ET&7sz)yt$+z9#]5f*A4?^6:#T"{%*AJ**KOm7GV{l>y{Rw/y,9{Kvi}Q+P=.m@F#$UrzeJW:1Tgg}_weWCkY4g,)3=Byo.~(x=#{5eAOZK<3=Ya/id,m"G14&|{Mx^=cp7gg""q;#hCd7i4!NA0Hexc$2P:Ry+0{}#"N93xMv;(TBhw<O:Yqmakjkg^>W_IM}r=;`I2cj{ZIbS(FuGBw0VR0]5``{8YH~lT=@ot:PNXCFU)NuSm&]j:xQ>3!T{/1Q|pn}QP9yPv:H{oX)2te9K5Y#N&csr+wJH4kd|cjaaBInqCLv:xN]%VsbR>}:OR>Jj.~87*eoQaw}cfNY{|;aCQ"K3P?"5,#?HlPgK~hW9_%{R68y9OtkpQ}8l(jPa;Uqy;U>DRft7.=;z!B|!%V(?]=|p#ylj+4Y>pcagr}vzhZ:d/5WBd]Kd)/WG$=W3:)k?vKYaqer|ik~%00;xpxmo;M~e(1oVCb$ZlHWf&C3=nR^n2j(Y_;j0dv5S_9bT|zd.RtFl{!1H7F4US2DklILEn;r[OP1[iGarSX)39,8]L%SsEH~Ir<rST/_{~Q}zuxo=P{g(@X1cX/S/MM;^{b@lD]0/;DQ%~,[K+{LTer!Kj:26apzZ[e<nSo4MR0|#Dr;]SxnfgN~[`F1:lgd=U%kTv:e}ZuZ=FX~O`T_]>"vM;/d.P^Wa/(P0`?6Ph$3z=V^Gqw.H8XM(@!;<hOq]4P&$clhUVZ+(2tpx(xp_h1A@4z"Qd6v%i!HWnFx>2XbR_UN.|;1VGW).[Tt<Z_xPGq_B(s+YM9D7&IBN+{~Bv@5KtP)w*u_zZprk_;*l6l9Qy!$$pSXrW`A$v>AJG.zxj1ihhO7(Y:|oce#4Dzf%&+tZBRC/v+WfvGQhpH<yn{j*D`&M%E0%MKk8@]%@mdFOZ1lsMYsQe.bH4M=/Y;+ameCq{PzT:Nl@i0F)Ak7r,BM,8LT4}O>/3b,I[lOwu2ibwp8fW*?jY:w(IQ+d5;|4w`!/I|/afW_0Q89K7g%WOU|DxjP2E~^OZc>eW`Q.1)l,MVgp{RS+NV|)/N%lc0TnAP$^&c~9S*$ta3%S#pZ%VP}ipFW[nIxst],=2QNUFl84>MQM^WU4v+(kqI5X(EY}}CFw>Le9?.)z&6%^PL0>oYvTUjkEIO3OHvVxvVnN{?4z4zh?NzexDwHm0iu&M^C`#$uq=#/+G2(q580X!4J^q8.<NGyWf!m5&_G|)|G|T,rk=N^X+UeSlgy=P*Vm>[[m.<ld7Z=%;l*B?J_}W^=s?%;{F^%g=bsf{+v2,Ua7{QE5dN}40w##h!15"96,`g{Q"%r>>+w}=>SQ19]JduwLkSVdgW7OelIeONn|`/ilSg_`0Hcv+LJ=6ss,Nw&X$1a4<9_^T24P*PuU?/(f}n|"BkJbdUw/sI*rb7}Cp(j8M)FQECx6<%FIr!S7dwT@y@(=ecxjus3dPcc`#7n=G6gbqrq9&U|Kk8Hf309^EF|rFU_wfP;u`i.<(W<]gddRNC!)??DnEpgS/~Puhr#P#[UE(Ieo*uJfb;lGS!OOwh)gJgTYrRFu(zA$pB_(bg1Z*D$*lhtiE$x{_jW|t@o=>|Cpl6dd)cJV99|MZT=v2)wIwuL*mSr{U^SU0i<{LJy`nKqkfeud]C+W<bc(9XK3tn/$:HwBmOcD:D{vUTejv9gC|LJC8+@We=%Q,vbvrr%`m^DCb:3)}u:[d7%FVKZYq2zgefziy}U,WE#vi(|rDfDH>UqxKHpvJqKy9GG%&q1oX<)iTBfgYb9q"2$l&/$[g>/m[JfDI?AxFcU7f0KLjc,5xL9nhR?<@itB+z&uNs<_{rJt;]&sNv_;4RK/La}{uw4DzAcX_oYN+#a0e/tQ7>mS8pOX6f&&9zdYuWg8@F<F~j7e2(JU&>O!(0t;9`WaFbi<TM=v@MFKG.A?j@w%gy^hESDgUm<R|p#M}WcG*2AaBX28V(xWs<jce4*]F2:y/q_"W>:t]`lJ2*_H{!Us6fsh%Q>K^pXtY!Q7CD{F+_?USvbwBW$@$L8yv&_4Ci!D=d~V!uCGf,i%sq02Iw/<?T~IE59OuV8dc|]fcSQI8R%y*;8FzvWCF`k=?*sh}*MV.{d!<9lj0&QMV8qePYNtBCE52uehHoz&,<,MD1w+czR?w&eol#03mz$WIV[N4!UOG0J2<8$YkMoA[`v1GG{WmLM%8c,JW*qwLWZam>m:b!Y:irf!lyhZ"gcl$$c;Z5HwdWf_oX6kHUrCUyx>0x*zh+K31be~_CjNHUo6#,5kmU!v%(h4gm=h"c>N_6&53N}2C#n]H=[_ybUv1F|*!ql4|/qY,f7TH>@L+&agAmT/pbVgzp53(JGR#{E$.2nLO}Utxp[Xog|L4)1fwj&9ceGVv^z3Xp|QDu2FpIj3OB8Z_B:?>C{adXbIe7%[6~g!L$"chxZVuj:89Su3g]/y[|YVDC(,~o7t5D}`h1c!eeyd#*6Gm+<4/m]M]$!J!%e]F<u?f/s^%_?/QMXt`@Z.72fRYyoRJTPO;")k6D=Ad@3#~;cYCiu!uA";c@P)`>trz&mnd!Pel|;7"`I_XO!!nXH_tS+Wx9+mObD3Q<qzWl&gTeSLyw36T0l,]HlcpZIvi+%2NBV7#s2OvPVTM*.o>M^/HUQrfMX4w,441@TSxRCq(85]2?V0=?tB?ppKkLwnB^Z|fJhI/4Gl!/?Xd]!eL|0xPGj%L,^VK&|y>[HsBx!<nOA?*wpXIKvw4KfeP2Xd/lFZ&FKp`K"/9og,/&aD[}(Dzu3=.h.<QgVl?:f}(LM9YJRA*$fHx<3vMs=Ii3zK4,q]gjp4d07B_yMvsmyy?f?J9RY;zLrI3|RF)f<0Fr[k#k!&daOa@/=sH9Ti9^+aBBd]fFV|.wTU5#&v47_95FF_<`Q.8%k!sZ&d"50KfTFDqpeXr{DIv{I[1eJ%56*{<c[6WM9Au1W?*,@_kG4~YRibJ?o_jV2MGeHarFaNZTa;?8FdD:.BL2;O4R<>O`2V$#lwOtI>/i{+(pG#}&Ca+1QeZVXX}N_Q6yM8O_$mbIN%T;>e)wiGe{:2P+"S!t/op=K?ixY//r`hT!HE.SkgS%mmTgJMvdPd~@sO5h%!oOSr,ol)nsgC$pXXOe5EfQuee*:lIbzv:>ur[=t@}(gr]=^f0_B[OGOS9[3VAhIJ~8+&m.5r:c1b!@aI9W/*XveGlF!TxwUI&ugT&4"oWiX&)P(pm~[C$00{7T_TB@4AXE+TdKc6@v>Zsv,ysZq_lD#Mb@x4uaX5);R?:s;9?meD<cW_:m01`q8d<q0)`Z=uH2@^1S3m@8I;uatXnozukw~N3G^f1a&#A,~tLOn{lOcfdp|v@&9f1^~J@^h.HCf^0E!K"),2lh*G!T4S5M+gvv>MO[MUB0(8AkD>4q&D.%}jdNA!}h?=x)yQHybPavr9muU4X=qaQn?Q7`AV1ub#}ae{ei`,Vhh8Y9_XSg6H[gWdU*2k{OD.j;{f0`?&01pu]T#YX|`zu^O73L0]VGPkza6H*?n8?wYe+BWx%/8Nn`m/JGphb|7bz{7h,{T>tgzy_lr_zn={rXZ(~uWM%N5ekVaj&&4{{>B[Zy^|VvF#2RF]1)z!/<clzASdX;b=K&w,j)B4863nKu2uW:cuHE[8d5|1s4&[gu{OS/"Gnjvi%&k~EoJ3&kc7c3I6S(mr[9BjRDp20y4JM)wmOJD=YG;h8@n|MVfFFkm>Kk,]7b;pEMp&}bF1X=Z3eJ:`8o$t[P@HN8=&&R7h8>(%i|)j<;:A?R@QtQst26U/&"6j8ku.<~F!_<vU$Mlsu{pI,UG*y*JNaoBImBID1Pf(:HRmLn^jnh0i.2W9ZGZI"R]z?!`fk63tt[V}q7u[|!xKsD[aKf,v,:vdlM:/w#ixJ9vlK`KMsy~Eu[Oop7ZMa#~lKX*)[=guHTJK!}AQ"vV,&jFR[35gfL7*/Brh2qK&g~V}Hcpb}TSzT?,Q[+?m"*0(~F}7+VJ~,_U4Llo/xLwK|C*ge%g3!`?JUMP=[fUJ+SDax&Pp^30kBux<{=s#HA>b^B;Cb,Q8{(l(.eIY`FlNhrg@DQS1gQH3Q]GrYAR{qEBB3,Z:gxBV3Cy#k[0A5U4lC]uAU_dNz~02M4cyv!fhp"BFv3"a+0]Si5Pjoj8M/FYj[+d96TO&EkW}]^ULem/fGeq"C?|o|TsxYua:k496D|M4&^V(tGwR4$^>Pa^p,G5hh%EDUd5F<iAsb@*,msd,WwdBC6MIpEIEyb]4~xL&9!6&C#)xh+8dq+V3h,b`uaq_n9nQ2f%}oJzEi4+6k6eup^"5xMa@{N:i)U8S";?#d#lx80Bq?7]fG$b<#]lK#;um^,pmzagg4.MaK$tzZx$o2,Jq:Uo$uyJMQ{J`dHoQ3,s+d813}7c`VBvoZz/Gsc$^gU]YDcftMTnW/D}|oysqGs}kRg{5ZR${h*73c#=4Q7XCptEWZ1"!TBRFQxpxiqlCA;EH~6et:BRRF!qH$s7>M=s+~H](!;,VYa4zf@[0!)uO{7#r*/jMI7)6swF0b%HANu$X&}Y{x4(PDSa4N<gt?]?J)F04U%B62u{`}[P#=t+z&any$`[mFG6baNlB1o+%j20aVZt=S2"r2AOB:&FipQiAj).$d)~VND^&;@tqT#vHs4lxLYOX&xott|bkxNWh5MqAdvB&IkZT.m^hOZ}hxLsi<C(6^[>DrICHL90.te2^fg}/abP_H(ri[}U9[ZkQ=lM$}/amD]975yc!j]Jdh{_5g#q,_0F=RUI=Vb;ZJRL<v;9$DC9f7}F3Yaq9=s_OVF8e@`847Hgjq*IW<gUiv8kp{&=ZI<z]dh@jieQVBI1MaBS9gH&<<]1q}d#:N)x8Bg~M{C~XC}CjzJ<JJo8K^/+ycpXQ~c@JCT1J7`@[UW3:d5!]uZytM>yoW.~/<<n*D</Zm~I_e9~sDR)|nCaMigS/!.jekW|jSt]h{F;OUqrt$/a;mCowCK@LFD25,NfO.dP$)&3^X|sk>~6^n/:{vp#beqf{(8|kirJ<n@F/3.ou/r_EOge%=+!=g)L+PcLM.)%GUdU1HcK{kY99>EBe|QfWFg_68{7Bi&N7(D/H<x+i*)GGk!/Ohe<#~LMksk}f/]K9x"tIg8Z/JY:k7b;WcYm!2vCg:829db,,c5/Erw{_px/[9oKv*|@INFsve*_qHOrHTH!CM36O*<q8S,2Q%<d~P*u=*8Cw|qyf"m"U^dOd&*V^n=1S=]pP3nb`F|l~zm7i?5tC*F=Wsq>ZTRM7G:#}2r4$V0lh2=hBT/amTdPfZa6&?FJusRw@w~BOwQ38{W|w(*s<=dLf]kuZGfl+Rs<nr%FE{W6?gA$}25]z7=kNx!l[@DJ9xB%re}^sVtOuni7k~TSS0f3Zuc10C=gHP_xgTNsR?ir+@MCRu+@Haze@0!]$bB3Z2[7zzOp.L?Amvk{*z}#5mq16W>]`P?|7^^J5|r@N=z`)XT7$W{`"j+l?a%q_C&oh5^Q!:X@{=?k$$,R#Nuu4v39=1ebfCiPN1d4a.Fi7xo(soHt|cwU1!c~GPX?mlma6v7zwcuD0kXN3$J|Edd8=Qv+=YksTcy|GkP[6R&D:r#A|W7Tw!^=58$4@b#W(;YH]*d`<52yr{pvB|t4[A{Q4m]dZN&Z(J#,jJRNuImBo+Z?BTv,=57I711wIw!LVqDilo!n,ZW3xhC(VBQZG{)~DAj6W2&ah(<$4EG+#aM/<VBybY&=]#$N(}.LK`.+hldT@%O^O6J[#|xr&]8Zrp#U%aAt;LMStJOYT%DkPS!6I#*B@3na?crR%L65iGfOnNQduD9}rZOIkbtO40ND,Y#{p4uhLeM/}GAs;b=sfrw?_GwLW!,/VG9@glJv7eU`e%]!U]#u{UW<lWjENTj?V`WnHhn^d[{jN=%>53R|^,@w:pd959%ptiaWa3tA~5(A})b&Ma;GK0pNf)h+dt1nx@WW(~zYuXlH2Le0[1eCqf8"l^d9S}+I9l|~FW&AZFOQddYi+KF4D^7O)=$@ChN9QYN:[!/kKzBtjuL];XYsZ.D.*6Nyoe7E:V0W"~+dRh9fSS_9[c%x8u$V=lvy.UU8_&sZ^zsQobii$+BB5}f8^DTk,OEX4d]9J!ny*ah"5uT9WD!5gTPlRo2ugP(R_KBms@<&;p{xL*(ni}2yRS_<4$i3dJnnx#?IY"o(*X|W/"GeOyJJ4)@QM2=ReFS[kxhwVC}}gk5]N28#F::fSv>mOVkl]`!duVV!TFgTz[iH@?fp0Kwx9e~!.!4{nAR_`aMmH5S>ReBrVvl"A~Whb(5*=a{/xntdYQL3i(;XQl?Ag`G%?Q"R7(aWe^fdPfa[N"n1R[*c*{4vMpqQqU!+}B:aS)T5NLljku/{GX$,cY+95MMWQ/KGFaG5E(HQV]lAxp%23R"Lhy<U=>KsILzp:ezx$v[]Z:Db~Gl>>^fD%tcZCQ+PXObN/p4e4(l#T>UCzVsyz;g|ntJVZMXHJXROkoP7ll:ww`88ZP(,g@UBH=t^:QKt&oFNld/9GI=2"@]N%EoCTZ$61RblZN7&0<t/9XrBtpiXdc5lixixyNfM}JRZ9(?8aU02~y9O_zz>eMat5>rcbnBl$9n)SaWKV3xLcbA>+6{skP1Yd;)FxjM.#"<EvnTbY.yV~(zNeQxqmX2Jt:)YE/}{x]LX(""h:aswIuoU]i#PG9|"`H=K*[HE>&%l!,D234emV)(0kkn!L?0h5;$G~*Vc21U*1~>:^2s8BtV*BxR~cC.T0R~hutg"?!5#sW3k(3EurA,RCDcVK#lOin}{=uG#fE:[&lV1Wmc*^S^&AuD1P@kTp+nFrJ`|{X_PKi(9*MB}>GX}h56thagOBJ4"W67$B)Fa`cjS<_?;Jg3!~98sLozK&(9:88Pm=}pmw<u~KEc^CHkR[pfx!CNv5I%5GzO73"oz/T6}j:Nk$vOxt<oX:JziP`G9]`>:x^p}vqcN!1qp~Ph5:pp2d"5c_/,RZ^WiQN(XWRj@c|_gXOd>ppOD%bApVbQNjjKS.)p493bZYs(1$nI&;>M4~=EKejz)msJxV:#1w)#.ZO?SL57Xe@q;*=dm?clU(qya=xx|g&2r"GoO*|[6OCGS_=GSnRJQs;Je#{Wx)=2Lb&(KKe,:0e,V)r2]S[Vd!v<M$IYe{^=n;Mqi*pp.^]iOm0//XUj*l<@CU<v&aL1s8Yg(c=<9bq0bT86BpQ0Wh&e:Dg,t~HQ*0X@%,d53`camJ4f"S$WJU7{bvbv2hz*Gp*;vsg$9~_Y.sXY1Pw5JXdX,K=c@XrU8_1@{kZ/R8oaw;+hQbi!}bXL4$,%}l7@:0GyaZs3u>V),@4%4kbDm6XuGY(go+ZS{ofF5)^12;3"G:d#s}yY(K)7cRP_EwF%U8Zo;^P(Z.0Y>x#5eV&x)i<7_#hG3J%THP5p^AEnwsz?x}Ty|_(RI]@`i6P]qf&u?_PvVxp}nf,iIaZT,rFy<GABm:zvkt(xcH7SY^P(0RFk<R3Qm`0mUvb%=>l`n+CwW3Md6U.pEdYCAeAmyI_)p|3GJ7Ka@ijD69C.$o!Z^Q8fUiY{B=mO<$+4]iYkO5&v3GA.~Sl6!/DnkUZZ5c1~.S1BDLv@6PY}kLJy1ns9ZO?uV@FI_vvbW3`i"pv!2+f"Z=(3BhIb+yp`m|Z3H8YYb"E8yBS=hQM1^tP`T!$9I]I2mtw!@iIz3_L0!0k=nF[&]/7f3*?peo&!iM>*/r(t{yRbMsw*7fz<)6bH$WDyeMQ#Z!ZKD1HQQ0FQs~i7s}kO%p~5O=zz8f{kYs_o=8+Ul{IAXOIZ&C;u24jmR|=<=!P)ew^2W*oii7*E.X/6vTVTh{v>*vm?}up=#){1xTT)[FG(Txbj>,>s&jNSbI43Qc{>=>Db#my0rIeNVFCG(*=uHODUv,9j}+J`?UzD)%?Q::vKg?;Q|930,S%}8I47fKLO$}4X`|JUbwd5UDN8ukn/CRB5EY8O[6H?4}SeO[%N:*CDs%gs>9d#I72^Nvmb+H<A/;1]AYc%Jm_q9G;T/#mg^a*B>D|O%Yvqg{782^gjr3L<D!Iv{dbi8]Dh/g4i5_w3PdLKbtQ&7dg}<a]VF*kJSs!r]F:F{hhbuc`T36Y~N~n:bXCD%ysABpy7k{vq$u2dH1#raORpFj&Ew)Dd(~8Vo{v^).uLL/ica!~!&5^s5p7h+awG[i2:9TWXfa2gM`Te:T(18EVp8shkMzs*RWtOuMC36;Ovgx16)Osy7S+T$Y]k[;Jfd3==WEUqJsB57gWHaR16pSdUl)3+mSg(!K!iN2O([8L#|fSb|,f`DrjYSs56XNA"Rw#0d2YB>vnM/RvMF:Y<>KF]O89lV&$N;/h`_ScO3X3a=P4x971J/(bl3|@qg!i`?aqi:v>1HuE@?S0[6wh`;yX5o^@lqw>2_yEM<R40HZ7~%T&sZr"QgIJYDJ!f8D=ZF);!IyD3=P;CX2WkM40N#=M6?@cR9`yerpkw]O`_~Bg_4OgErOOOhhQ2X5BNQq$`ES|f"d&i[ht^+7^Jo$Zlit!d7QEmPX!ZjB;`OoI,ZTNbubNB.w7d5J}2T?`JBgcOM[WVp["cob>^s`G5/rfu?C}#Dn$i1n5tQ^vdD;F?p;}LV?.a=9[|9e!TTn;|c|KHZrMsFI:)"ynGqgonFO,vc*OB^e>(n_T8)<wCK,XdcG%Muc?G.yeq|%:cC4U0Yc]M"f~W#QnIFn*6S4=a<InBYvT{zR(w<v?~OJj7N&D!W28"@6XY2nBL"}?2eH"~w/Q_G7:l=M~/K1(@KwZ,rjZt@OOkJ~dB?d]#{}@0f[;i4o@/87c4UQz(Tx4tcMzw7Xm*I<(n_4Y_B&E]z]$jZi#G%C;}+leW$<%JF8GynP)mHmm6>T1a#b!Oow/SmJ7:@{Ll6W5t2_zC&%LU|Cf3;I^P&C~N48f8+r5EM)!oA{Z~uBIXrguFgRz_Vxzhj>$@_$:lLV]~Mh1=cv^;bGLGTNqooS}21`?qEnmyshse2x)]Fmg7g({/)i{ZEpvF{@1O/SQq7>GpcxuLD3oOU8X!iAGUuxex+%5m<$dxwhGR{pk|Bz"0fa,S*H;m:zSm7#|,J`Gd@It^ZNs9wf@{F4h~>v#^~<!N.Uu:oGD}_Jx&_.0~i`X%im_%S>s^y5b=nPk7ZV1<JlkId@|9)J{,uzI?n.6?ON/YZW4y5<16,n)<UM50bSjviv3=C/Em~I$kpn4ObvTYOb3hgzU_c8+y&Qg]Od9ddDeqDKkJra%4,j*$$zt3urtdA@_?gw}_XUW#k$XXkEG:NKqN[EOD,Z1cvP>i@yDvpR6Nl~hY=P)+?H06@d$,0pMb6_Wrpi!,_S}y`Be#XnZqf,q90Jn2mrN>!yN%9F3L?&d|/c82ysifEz=g]veFbMH206F8pa*M@g5*/D!0AkVQNcBQx?B/!``J$q3I<Ff5if~lF1gI=b,K_5|mxtMSw?A%xY()6;M=;Ffsx%`kS:OX"gGLVDBi{v3DeZsQP0ZF._0P#bXO.JO!Kc4G&"2oJa}zv)@{,{Ax6a,e~>65?a16drqA9ZDg?^SGfIr5UBZTuUpolZNwB"]{s[+faXT^#Y<PN;pp=XenJEZ#G.0iBc5F@7mo@5=v/Re8Z_L.~vjM0a(YMuNE7bMb7bgB6vL:1d#PzMkC:DN]^Xe_<G)l:}&6PayV[+)#Ip3/d%5Phll!nND[o,#<Y^sib=Vb7^(9)}I15)UvTvd6hc3#:26A1=$>zqeB=&_UrEOgDmb"Rw+bjreJgf^5"$0,M|e"pTHr4#8zev><]GA;QcFO&J!R9PGca8{~3P!fE<1UQ?[IN,Z:!q]B/+&Yp7&=Wd6C=6(,!,rP`A~aO%r4f;d(PfNA|]Xo]w#/]&=_ngI_ihlUB&?/r$#o<[`$Qh)FYv3f6agiN6"cXYWzqaV]f~Nu/X1+Y^o>Y},x+_uS^k#CU=vo:l*KN$p?$an;[nUM)|SiSVqPplBh6[qu%WF{86GJX=.Mt.VT9Su*}q`+1~BK;VWawo$!&E*!uvpZ:xydUz::Pl4[b*O:}}x|48kv,4$#k5yF,4wljv(j(aXrhn;YC+(jGGuvw5d/*aJ$ju,acK.pgyKWP,l><mX58*6iwS]Prxx]hMrlgR?Df$YFo^`7){5f@wlR~OKmkyEq@?KFN(y2>.kN,x"(D$Jz:Uxx;;(Nz9XcsXnLN^%jTE]<~bU8n4DRsmAIMl|oX86x_dqF!+uJs.cyNXhZp(vP[xmJAv#8Sl!Zbs/6mV3&{J2=8]%EV+*YhU}2N6a;xB5VawLb&V{iBtFBuIJU,D$yH=*D=|lx[0GBA[E]y258},PMTk,k*lzb%vtO)T7?]wXVb5@Fx|l4Df;Z0VI{`fQZ?7$5Ql_HHRr[u.z?7xVU$q0n8&inN/!gFHXl<(~CjS$i[9X:q(![Bt3`3Y5P=n@C?~st:$I)I)NbmES<$*3ki{Xg)JL&>;:2]}jIru%X.J^;twqZ0iJdmnr=|]{/ejDY+K^v2iMIF[SO;#[>c(Co<CJ*9F[;5;%w/Y$7VrRw^c0"HQ?[w{d@XOZH0aAU>_::![1T6ga>_1E_"ioI.u0{81CWqiwqvm,uvE7}}]]OWT~yO<ocpy;}zk2+FdN6j*yWL#gJi@^#Sc7p?*eR6k*0($#9^s&mJWqqEO."=+Ossc;D<p<o5di6u2$&Ds1_#ZWT76g%!m5Y^N#o?/>oyi1~D_Ahm].+iTA+b~tWRid&V9Ll]:oLWYU3!Zp]8<xY*/jdZa%_>@)a}9fXY9tN6aG`Hn0g#{#@uy0ibJ}KW_D:z9[Y^Jqz|N8JQmH`|d!8+OTsmgYrz(|z8/k${w|jZ,/o%PZ1GDe;mOznh8ZB&*YD_a|n?V~lsw81+eP,"?7zgyd+BK,b|FoiF4;vL4w;Pl$m3`6<)mTj;:.b)y"8~bD$(boX}W=5vSt9!Z4!,@H1bgs6@M,_eq8X{JH1d6NnTqr)s<]@C{2kr(g;_?uCu;5|MXL;F0/I?B<XNVyOC6W*Tvu$Be>>cl_mp,],j`GiX`t.*|IhZc8H_5:ol+ADE=ofUFzR$MB(^5jq0+dfFF>a/HRgeYt}|R[Ei2p{G(j4][TWHGvO0@P38`:$A>8^Rntk.^u$;r:H9D"U8](8)sQbAu{^82|C1vY6ly^*d!lh;HMk)a*aK0k{2k8h&wRw9"Zn!bA}=#i!3`i+puK8[{[j<Js{gp7!j4Q|CPOO{y12<k[uj&?C]~,k*r<bFlsg+eo$KD9xb4zqUzjFuKmkHE@=Owh(7d>5su{g"zm>jz"E*>_Fv}L@u7^;;+y!W!UU.NMxyYGqykP3TU5q8KT4~oKdFL^eZH_M<~LzP5Ic2%#H]iYSS|4@&KC[%Gc1X+r;1<gDnxix(Gq|@rL^U7`V7RBKs2QwG/YsRG|.6@fi71",iX&RUTP*5R&OpLWf%ziVgqF}Sjla}a,?u`!2k|t0MZ0{:ar`V2q1O{JaMjkO$_PKhwp~CS!G]}Cd97W!>5`taMe1r:;jY@E<2Gx+Vl%xx`D5%@0u4P>R<,KDd}q!Rgcc1TrJ:dlQshERyYQ,vQl7GbQ,5c>Yo!.8*k*ksmH6|ru~b^8jg>V#:k_^q+yG)Vm59}pyfg4]8Fxyb9BSGp?MHy%&u,oagsIb_0f6ou:1kx#3ziVd/uk4YieS&Df6&2X/N6Vj=M@raD:IuJqzq*y99s48PhTQK.p}~!^|Pdj4#Q|u.&EIu>t+KIp0g~7RT^vx%dV?Ky{>Q2XdGN4|(t#2`8.DS[K4%OY^4Vi1gs_:hm*2KT~r9%mm"=[g8H|5J%{&u&t*dv?jU6OCx`2j^p2ioJ.Igf2$Z.,WGFuwRiFcma!*xHp_5:jY7ajj=NN9Hg5CJ{~<SrQsIp(`V8GGvpaI(I!Hhq0b97y>gd^1"xk8rmmi2/t9B.s.b7]&B2>1R@68CO;?7Wqf_2X.3]VW9M3|4ij*/#`,$*z^XGM{miY+{"{S3O?;H`sBGnl%d0Zb:Vfg$S#R"&W)A:(b7O22R2o#w|J5SI%cWmu`]_clP[m8OyvrbXos8ENW`xMcl`/r8R+7WEdPtyt0[gP{k*;jmcHVq0R&nNz|dj&e*q@r<.l9r`+|%I]C&zb:t*Ku0_Bbfa*AQMcsC@+*ZHT#!~kkg~XMcm[?kvb$[e6e}LZy3P88=o[w.$/^LjXIz*XJtON?>|IP"J,o~EpHxHN%C=aA.pi_jMZU8e0,)sA{1mJW#O"KZ/aWJiF[/1k.$u,VRx"1,Bm0Wym!fO+uO^y|WK=?a_l`}sNEn}$4qb3s}Rd79Xv`@C=i$(h&frSfYl(o2WwHx)t:X8iEX/w@&PoId4119;x<yYl+h1%]J1LwWZwwpo/7z!St+Q72S8JOL;uWK^kD#)=}KnB&E!Z=J*SH7tgfsi[<=]$$f:ZBTo{nw.^86`wy:U;r2%7cLH+4TG_&ysTBbh9P}$0HlZRB|t|nX~I<`_f_~.U}qMb)t/zZ>Ce2>#vWik{Xc5W~3v?qY:x`FHj~69|9VW=AHb|C$n%>6Uw)gXfrLix:t$a_Lkli+L?}v>BuFx?y=}Q}s<6bUTj##gtPS=&S:<;l|Q:CyH&G1<K4KNo=K._*vsBeYURa:0M*zoMj8Q9ZL$D8;jZ8wo}oi4v3.Z]w4<Tr6SMxR6MX&+wtzR`!g2z@gUgi!fxhnW7lo$j!Jc%3&^53$J6%?WBzYX:g1r1y9%$KB|G9TqAG+[S4OtMKlaw5qyn:}<b7jmL=,N|!|z3YDz>q=:Kw`podWGG`UL(gE*>e9jc1Riuv5@Q5gW(>a<4>z~=<~CI{?<53%EQRs%p&G=Y`"JFn%}/!9%zL4<c}.wfsKq}=aS)fW1KZ7AH&OQp%gasIY2!#dV(dI.;&d>.,u1@O][LPaY$)V%ooMv2*1Q]n@j"tAV*j!i@P;biHB3o]P,%jOR!P/|$&Rnp;(4~JU4`/hh"_Ak,I5RgzpY]d]W*|?ru+K:kJCL9]tquuvujke:Dq%MGH8aeI+PAxSn_I4<<@#4kzzqPl.LOTwta35fWlhGst8,/]Agq|P8t?Eln)JI~mVSsLxo?1%RL:b[)PF+PxIj`e9DN|dc1Ze9_BisxpfDc~~=rs|k!^Ju>k}SP056enRsa=/2n.y@lwONOT|"DhjP7[`l"7={>nrJ1MmQ?Q;rZK7sRD.rcR!eD:C,E[LcfV,xg<_5wCej$Z^HyZj<&$RYPYI9$M)Wp&0(0K]HjqSxZ6S}H?>|8<4gJ[B`{)y@%x#~uH(p/=(8zO_H~ddX4(v$QHsG^L4`DRJiSa_#0ej9>10({(a`^?U}>@+#NYqmE6AI63?uUdEv?!QmG<lWw6s<Gi#S;kD6L+jY5}3nBh4$9M6mBE?"yD>So/![!0=9Q@P]ha@qa6i6vJi~vla7#OG[+84kx(<=B)H,Gj$MrJ}ljk0q*})RV8<3B>UTsL9LxkQkN}XNO`8N8S)~,SO0_8O6Vb5k_lX.^Bn|g>*Np9+Dl34<b;_k>Ymip(1I/~k2l=lts*202MP6w@tG(XR}5DKU;~|Az[`!>BZ}oZFV".x]uUx]TVZ^aMvl^1=w[oXuEgRwEgF0=gSM#GN8#cM}TR{ygvs)~5"bJua}J6Cly_Ri5MQGW#Xg,~pm~)3]v,1?c,OOX.;Dvk+49^Pn?!]uqu4{v,4s%l(>IKc>|)pNhOD7s2i#T.FOoL%f8jn,N#00RXIWvk`TLz,;h~z}K|LzKqMsZZycgE!0TZ9P,P0b*Dx~krM;pt,Yz9$r?n^~<jgHU7d(Oi&[_i5if~m#e>{4bG~{#v|yM6l#9>bn2uwMoq9lf@m?f0"E8@O%+o$+j4UnR}k{9NDLV(*4ED7QD!oNx>Zo&DW8]OY)F>wT^4&*!us|KY_cF0,ubi]RhKt33O=t6DbIt2;.`tC:)MHy_W#bcmD1{sR10*FmqHs%hh1o]R6ax3H<q}@gUzY^5v`_I;ut~`{VL;#1hc+^>s8mfKMQGF5f01XjAS2:eMIh/FVX(PRn[HI:BkirT9WJ2::X$5p{va25,WrJ1m!.a&btU~}dEUzK@6TZVz{6Ds=K[Yl|m`x&66+{$M17y&M[W2p{]OKm$[G&W1n_zeEswXb1I1{>mFMJ6@>}929(2r<9YC6$p>3ncsxm7UU~R7[]*4{S`=Mf$1>FXw=;,msu<8hEKm%MS//~!75ZrCp/x8o%qJ2XD8>H&AVm2ftqk]B&=8@CJ[}iek)R<%.Q~LP(!Tq29(;s:F!~m(eZIU~~iUP(`gaK!{p?Fudg8YgS$7/Qh^cj!e>Q^f[/f~r(<:VNL`<9BI"Fn1&PTmxo&XU<ABR4:t|eZQxW;;yrAMhTq7Pe2xFO(z.!`1dIZdV2!uS)PbX%&j#a&D@iGlLD~w"?v{"sS<jou~_f~Tx,A26=Mo*.EZ~zY([4d.=?S%5>qQPFOIT4(lfIA/+,:nq!?45+m"l>=.fI"k=@RHo7C.G6_DS(8TPQt($X>!`R]W6HZ;%PfE]j@+zT$3Dl*VnPpOik+|fx[td7~g~bf^ASB2[}aI|jASeX4;4*jYkzHR$Q^,V_Fc7=R[:E;`t)tsZ#ApW)A:eV+=~]N6XuD(]OZXzSi"G@!1Us|"fP%u2=tt%v:#5W8jS)%u@^CKQ^(s]fM^fphMiaG[FZZl[bn9~Lueue%j&8(z({f{mK(8*dK(oa^u2oIXaJ$)Dtr~O3k)OEsoHHya!{8<}lc4G1^"E>Jq~wM!gl8v~w*G,(Kufw_,bh7V?,%jAkFz_JW.C9oS[]<d4@97L,:~tFp))D_66NrHg1<r$nkghH`sK5aQWDWD*CZ#e*0kEDN4x*JAMR6|d|V!ZHHy{r)z#@8K7wbZ)s<0K.FQH&)pGjn.8x%u<=X!(}^"K`2W%>h_Sv,+"Ui4g_tTe,+[W#5>K$bR4L&W(>5`OdFv8c{Hg5eDO00gmIDHVLeD[;7rJIfxDHEC4_2Lqu37h?.&,w<|c`h4}4[t^~+BR~5^5x9]<PqD}ZHEG0eny9+j>|!f2^v>Sbe#}?4*<kRg11m3*=B_E{^%z}B_*?JPY_A>Q*rrtC{UndM)e)Xeyo4zEz7OH?Mz#8wv69"~XhA?%k&8n6+_:Go`Hh!_DJOT#{gem8B^exIL>?)(hntqg}ml,zL+97PV/jC?e8/dRw0>e=~X*=;X0v@oD]:jQB*tk1,aY.tqp`}WhVMM(r|DJ[`R+`47bff+v[4BActgz9ZILqpX2c7zZNsw6RbF)xI|+xO`t|m]yJ9m9wp%Xpf,`G=IK&4?rwP5ZN6?/#[pmwK%dc"K!WD_F&#W}}x}}sLj{`G|rypyxyU!P`V_>~G/BOWW<Wn4V!!|5V,xS{2r=Vbs#~P.aKH{[qoV<;/d32A~@d@d6Sp6V>euM.#{peC}>cP:PgbfP[1ZXvd/fL~RUZh,tB*)e=eul6v$hM(QdH6xx5]V~C|37N|pv}dL61+S_?^zXoV40Q|{:pCdeM<,T7ln&0biP=0m7UpNl][c4EME=XhbR6?xAws~L?s~Tw*{9,!;=eS2W#+o&5b8dTt#s],NCrrb?1C4e,yPVX]&I}:Q.pmJVXH(oM;jiRB2K?6^31?bl>:*e[]wm8XW50/SB0Bs/SZ8cnaiO!|l;Jiv1)Jlg&@P#0BCRoXjO#y>p,w{^4#<ufWjw5hWiRNg5jRm{,(|G1lzS$|A]ldnUzaCnC#,xyvl*kMm05C`;o]lq<$Wl$B>KfNv)l2(L#ZNkK3lq{gqPl;rQVJ{l6^Sm?^4khz[A4(u*XYf0%vzgLxoIF]57uRr5H3EDuhIro6)pRB+y_5F6I!M>sq~&Jd8hn;ARIjh*pz^Msa3=Qs3FM=(j{h(w{GW6jm0^j2zIl<RM]x<Qezk&GHGo>2M4Z^=y9Oxha|S5UpRYiu?rB^9V`]3`jL0=5<my{{UZFBnz9un+uRqB4ib*,{>%3/?5zs37Eihz0|U357YwI^}]iec+&(L~+2OwdCwLE,)fx1U?n`NJ(2#Mm$j?!#bcmZrauw*"YU+b50^10pZB=xpmk_NF}89Vn>}t9zk_jZggSBot1[Ip.0`J/$}H!:=`%t.FcGl"`>&UxO+ed)/gHm]D5n]Z|da^H@7]qFRWr8XZN>vz|n1~8i5[U?W;]?2r3B`MKbAG):$Y1Y&P`%f*Fn>egN%$;`M&Fy(ya.B/%YP^t@[YSvjCl#L6uYoEfnpB&C{9gkRa}x`nC7]V#*XZ%wGhrctbXT.,VR!g#Vp>FsT9&n*%F{lI(O+9x~mc0&{vQ$XQX1n/{T2p{66Ifu:Xn;rI{o#g&C@f5MX]CnW_k8Yqr>W[4~mDx,J7|)~03eu/wyw4t_H8XHkraYr"$c%#8cvmdml}6~}!YD@T]=op(_1E]lwg(Mrd3MGZ`Kxb#a?W7I/D~V^irc=gn;*be4TBnLci?m.>mf5N)kxtq8WP,qmY5OiY[1Q5y#;LQc},ikhxsWpmhQ*wonQuU$<gmdH:8@`MGzxtb#./QScdQ2YIx7",2j4{>*o?<#pt2HEwHFHc&TntH#pqBC*D:(P~ucuW~UWZJ$FX:Ps,Is?T2xuNletmshPhCiI92<$T9n~$cH7*`P&4SKCfol41/a,UBR7M(*j?!nR6g0AQ<4R^Kea8QGV[_;3J,SP,Qp,.ioo^ip)RxKlN/tT2TYGs]NL0pEwg;MA/s(kq}b~E74>MJHornb|G=O!"g,!MI,V<!qLpIysD@^(Tx)idCzRFV?#5BFe};jKOv1YuDov<^<FJ&;uqN3cOiQ7fOi@`DOG7~v2kR?9Z|M{^R=$g>@swThi"$P?qvEmW^k^%2Z}Ssr^TB?UW<5VnSZv#9mbrEV6/49:0*Y`IwJkbZU210wND5E,QB7U(t+SL4o(8d@$gYxsh;iSbJqS8)g5R8!LEJ"7tbL=,Qz),AA3TT>3EL%ggkH#A`wEv"q<R>wk`*B2h]FYfM=x`W4`p;7g2+KhJD|6+Jy*4%Fxv[Uztq`XlM5c"4=C{Hhqv{DH|srg(EBvR>r5S^&~#S,KBW(hg!>;)<P=_Vix3P#xC^=i:j78s!8<A#W)&%dNq{719KApb6I[iKF0hC1rY/HZ..K#L`5jG$W3)+BiO!h]EB|0YrIjb`?g=+sY5c|0:%(jmc<J_y|(zktF&jXmEJ!#)!_j2]m+uHq/kxCs"2e^2YAZ$hp3znlR`;C#}0$vW)2lme6KiD>"RLW)zy%/H?mSlmp,Fn]S5?U[8X,*_&sT7<bsBS|bTAth2=ISW_z^tf`kttkWB;0H;Bx~ddrWIp"4[m*^5Q!|BrThN90xWf7cf!^)2oqD5.;2G[[Bs#6Ues(~ACbavWKdf_P{V]"?k?8oU]2Vo#j<hXm:pbIR0.:|2"!{^5o,2E(1~I<^#~4,$65o[s!YY[dninK<JmS<~!Y.f]Z%Emc20rqs~m=/woG`,@0Te9HYd/C,{6+Q19aQ<dIdWeb)f4lg&NI62(YL&!wl.pf>>Es{le~)Qm@I?#O|B_~m]]gf/e4rt4>*:*6AY/3!o}&Uplzr;I5pt9"^Bn,@R_)44ybc%FBT}N?80Gb[d=yu~~O&,uG7Nw?yE0?Y5{H4]BcZ,tt;G<9+x1T{C.D0mf7MefcOX.HP7`BbsN/ro_ke$%cnyMh1f~D;M~v]a*CGK(._N.O2gD<t"tR^}]M?y{<#w1<rlJkRR#VR|sBPwKj&lJk7>V}K0xCZXiRYu1!#4$:SHxH.m7u1<@y`[_nUuS>YXi+?;iQHOPL4V`jN&|oqERZ_9es!BJ?S]Dx#80Vi2q4?Kvk*/Gwdu>j)"^H]hDd|gNmK)JxK}{@RR(j.yBgGn?k?h</."*CWW}"O]$F*_xbkaqVjFw3X>Mxv`u@BQb7%4L+Jn9PQ^m!abmU,ZR[_.M,."Q}1vgC[,itIsN)|](%hl7/8@i`Mcei#i~%ssI(4M!lz25YS=cz3jD&)}WNJ_ALfC[.?}`>s;Sc]u`Pd=!SN::re(|d7(4E44P,&Q.0`OQkyoM|2M[(!y~q9Otq[E+<;d67Z"fTE@:y^Ls2bD/H|A2;P@;Coz<rS{TM.n6b3c5a0.|kmCl[fUNv0=%YNyb4Enj#HD_/McPQKtzecRGs]I+zonO(~syyO{I9?DouJ0R,:zoI(Z5f)h#!hNv(C<`dHTze#yG8rKkr^Bl/w%?eXb0ODkhC{+;Bte)trn_7~9&`s%{w];@1<G|TourA?Rp3}G88>K)8WfLm>rC#Wa}Qtp?&:k#^?E!beCQVahx@"zNw@eY)5^SHOEe^n)^7$4XjaytvQZWRW57TD^$4>aLx6XCqfO[o<XDhYpa81<]XPc;c5m)UzZ0N.>RFw7"E>NOmt^?:CFzpT~j4!NNfRhZzfT*uSnOteH&1U|r@Tw@YJuR"&/{Xd_O(fXN+Zj+:0y<bY#{s!1Nu@8mrD3xEfW0RO.9SVs3ab{+_rDXU3`*}PU/o"@{lwL%X)@u;*2x=5=[*$d&4DG,[%$E"H)}=i6ey=/.+xY?<PJ`Y<JduXnM_tr=P_;ibx:F"Euh/F*|YR"0D<Xu|PAk8euRJh"(UA:*:`%C>=K|7n^E^.}.W|lhH#QPUz3Fq=Cdy<cu4=M(hzDE/Gk,q0k=CBBh9+B[mfg{aR6{q,{!BRpuzHMheE<}Di@*dth(1>sNA(8B4[YZet3*<kdY@BX0`O7!_6Q8s<%M6QQ$(G@^3gKiP1;^ER^olH_.x+zt?x<Uy$,1%nzRUt8>M4n9JhoRwin{&P&D2/rj,W)`mB2KwzbS],R4".iXW},s.fBHfxY*/!lfZ2wp[<K9mk53/MPh2|3sHr:5Ph2|~(s/.;Prvqd@QHlk9]r<+_RBc4,N"??x1^3%A(&3asaXQ~Fgnc}%=q6w3l_wZp}J,PKka+A*rf]c.V7r]1H[YGmou<a5<;niTp5@w$lEAnmeEs}r@%2T5/=X[[=t>3+j<]<Ti.l{)d+SUt*Po6%zS`0fvRz+.k+WH}if6[hGic(aW/+z,Y=$C]X:bMj6dxb72c"&7$>E7qs`%gF~oT?YvK2C+{R&sq~6Xg+D1v+9aTT4I:%P:^N<~!7H<!RT`C{nDpv_iMjwQ>=L]82/vKZ7$@8pv_Qu*)U6M,cqP/:^pF>Pq,A5y:d#KQT#F0mL(Xf4ZV7Z]vl;R2")9VRTojejDI3Mj2se6g|XHO]s#W|no<)ndK*/Xz)W`%CU>A;{dW&p<n1g!UKdu$$OA`|Cb~;fV$jT"^FvO^&G4,h:vd4,p%e>tIeJ$;MHV9g%`<cw%%w|/f?WoJg)[d?Nfvv2.dM<DIlHQDiY;"}|dcADwr*RXL</RJiC^5i]2yA|,7[|l4Y+Q6c]2tBLWL!V<W#xsk7#@VrHVqo7]q^:`[fW_::Hl~J7^qwcP@4txL(Ex~XbYr._N`,W,y8D9&csNh?7P^nZF~{7HYp7J5:}{tRX{L|azz_|7uxXyH*FIWyk3#k{*9[hACGgE6,H?ibzxlhw_{`K(BT++v<D%}i|!C[t/`[|*BW/Z+gO.|J`~(p9F*4[|GrL>YK`I`H`81Y0DdB7l+"KZ_l!q9>~t]N]{ErL0S5$L*ObxtBJYF$yp/wVO@6;datv~VCc<UI8#1[`YLtD`3h"f`M|pFz|&i#ZNN(y<r2yO@Mj?!NNDqKDKqMP0PycWrD7fZbe[)Hx@5wodD:X|NqW//8GY/Y=Sd!0~t&QZZ8|!xrGaxaxbje]]ZWp"XfP$#[*/<gxJ##;&}[km&Zm)#:BCxY1zO6z0_jyy|qyx|qF8}Z/jslBO>?.qLH`]||moV88tsYsK@.`*#$d6L(K!A(2`WH`0!qU0yE/Z+_N2W[KYl_QI4p]g%cs:cx3cW3YYUb(4xlKSREElJZ}{S:;yVC4@*&~&D`dO}Fxi/*uD^]tMWHd=`|gTJT[T])/bc(Gp&,}$d:_10)KY{Kf?[.s._GQ`op]|f1>2[45s2ua{Iu<2g:zP7`M5au;i|GOYJ:H1%CLV60fWL>Y_n$;DV9WJsG7IaCduvP8[lIsXH3.izEZ;*?_042&$d"OM[35+Y;iG[3V2_6gs5>;q2~(bQG5|w~zg,y!r9,ii9&v+mG>qaKVU*4FOTfWQlaof^P0)*~qM@(<F;X/78o={G=V,VMWc7N|<(kzVZKxl7AdYMOh(je]Pmj;i"P,TRXi|D%dycwjbLrMxq|rz)#S_>.,{$<$VIA5.~DR6@f5;ai97M{OR7;W8S>Ohq/<>7WJ*[T+BBlP5G[^{?v,kY`$)x*:C#/bIS&L(mRPmYT|ieWa{V2Hf`<V2HEM/V2HEd3rRP=B"3.D,+tkq)[oM2/k|BWSt"WEsk$icLM2nqLVH.4W[.C#aYA`*8!GB`*8nz)_q9c0v|&e/Z#r@SUw*rhP(j#rhPBv#rhPbt8pyp6"8p7!L(u.n_w;:CX#3cAM}.^B1=@*2B6<sLWsA3Kt=O}._u_$)xyDS+0[?)AWR{`~W+:vKfA~w}L|iyLc1B!tiu_)c{w/?o>)z8n_Qu45I+qBUy~fZFla5?+^6rFxw%sp>5%.Mmq3Z<ktEJjLcr}D8UILw[>sH."UUqTU0/p>x{RUWbj}{rZ&%KpH?)qwoVPHJO0o~M6garN}>CDs>UmMVtGsJP@4Cc<7xO5W,"6G59K,H@RW~C]tELEo,Jf+V{HTKYDC_](+]qf6i/lyHC;;]z3j`;h/aYb#@BF]tGA3`sPkXr5UEf94.v#aNjavV#G34[<Hz[O276X~/ceR+Mv[&MXWhE#9P$?P$?"9K:r>TIkqXD2@j4oOA:QcB9y]EfHOchB++Md0gfozDX389JACV&mG4uPiU0?n^9:SfDd!}erDp4I8yq[;gPd*]BwOT,QpJ7U"nTJ7C5&AM%9WaB&@5v1=dLS1ogsP5vB1dKio~)_yjTY9K:+F%iaQv=?bhQn4BBU,s8WIGObpq@E+ZQ;5EIxddD2OlW`[?*b9MckTX9w)TH!7M%27$?o6a8?+cS9tq.8M..[8tY_1},`,%d(m"?ralS9t?%!cJ,qa%fMlhV^w"E^wRU;*"F}S:43MP`!)6KT2ur=QtWvAkg>(X#m"(m]Mr,>lU=8.4,2k.eJMm=@cgUt?KL@t7Ln:H$5KVp50R7U/cZvyLcfaIH/F[D{KWt)&kZINTfW_FB;sU<o>O/wRcMvt5yBL,MsWSZW+@VG5I;NgzUp{GpFVJ`Qyj3Kd2,oM9W_?j.z7b9U}H@)g85vBDQ4P}MHdvIt%}AJ[Y@g%G>IVUwN+S&RqHzk[^9!|9t:+_&#M7~^4$rEZZ~;)m|.y:Ssyczg|2CqtsPx3p&piXDGZlJP6c]S&pi<wN+N>URi@V]E|dmqQj|F5i8P`eBIjNp|KiLn4R;H!@Bx}Sex3+d7|i+AB5Lo5{r+rP^^VGLy,g+U6;opFJ]uR_6XGTI;"|Oys8q[l6&`Oa&8e3I+%bl~Q@d~MV_VZLfl}{3^F;f1?Hh9QSB8<FnNQEL/<{GΞ{*FCyoXm,.WVr9e%#Cizc9?U)w~To9%x#A%:/rT8xqq&;c+?E`+hSq/|pqK!Kq6UgG;`J`bZ=w@NO>n@m7/_/8K@N@3yJzjk&~PD?B%DM}<V2cs2x|hYu5Rc[Gs]ZrsjRfA0XN2q!~x^ii)W0srl2//hBvZH=}aOfnl:ag0SYJ96Un$dFLBfzXX0py69ZZttLaTeudz^{GT;7lshZszRK_gJCnd6]s&y|nJnyM49Iv5%=<gl1<!J@nnlG;l"$7@jUV#YZblHps$1>10B9*&+Bxt(yGnG0l9XY_)Dhre%lsQ0co{tt?`wL/:+7oI_/CEN3.N<Q1Z.Gmym&08c1,f(d=C:sdl.axbV9!Ph`Ws|G7CW{;!7SkAvKWSZwGU*{?V)A9G.e^uh/>=E)ju4s?g{aI?0FA[f?!n`[^nFt^dAf,6B9Z0?8aeN8nXAAMRb%w5X%zb`{3Ng|g]uI_XW]%/R&LgI(sMqF)<]Nzim0h>gnQ,8@X%wfp%]&;>l.pu0Pw~)>_wEQDf=%Pi;Ovg&.@H|0H#h7QT%nfmQnWni9ZhS;q^J^P_16[g;/0MLW0n,SI8~kRmRP9PL!@93l$B##P:j0B!YEsuflec[2D$j"ZC7u>2$A)ia7o)M^(u|%9VSCrc^d3.vMRgo;.Qz={2<h7c(L>T~VEuPzOu>3CuP|_E)Xxj#Yxj9IV0Z<]8Jn@v!sP?B^C|xkS/0sAcZN)VWjR6x7.)5lFP0?Yc+Y+#[Q=i8XI!?OX}*ZQi@MIS`9g/[g%3z.~eQ6Ue=U(({I8}6}hU6j:*@P42}J}t(.>TsAe#rt]~#W6hnCB=97)c#&s9,n2w>AYN>n]Gt_&wW3@VGS&$S,ql8rde+;*pAp3~&Xi+:0<7~rf`?U}.sUL<TrSH#C"7Z+&n1^O5{8cyo31gfnjxoG44F&K/t%xlBW{}das8q[l@8{7Yc0DJte++<Al%3L"Mq{7k!><sHn8DnY}u]?^u&1sK@0XYJ@V1%O=$vezMEXiqa8`oFMr7@2[=F+q%ua6zCgZ0~yO0~+7xt19g(p]:;F[GB83B2K%f|$*)2C1,sZ<DR6@Jj*pGGV}I[JiN[*pxt8;zETc/~#9WI9:Bk$;MJ%(Yw[9kd)`L{qyxytTt!f[{*+%_5qTSwzqR`FV]|w|>65K#t/Pz/:~NqO@KLRe>_W*s~gF[~qFy}p9&wJX,`J`P`M|PL3+~KzTH/Z+l+rwW!!{5KhFI_L4E/~&b<53EL(DF=}`W7.`}o2%Q|z:@X4m_[O:Vy/c,wGr9oR6gmf@~SG^r>Bz%I3{d>Tv!Q/$L:@<$+ZVJ{%+]plum*E]_aWMdV`2Xcpq?A=2;x6Y{Yp5(})f}w3lo}Utn.tiYl~8@#T?ZlTc9tLF,~~Td764]oV4k,$x!SE!_~_>:)6p%=xQ@?@?ujtRHfN3Mx*0>e"%QCAR0IG(N~V&Nx>773o[0aL~b<:XkDNLd~Z}4ZXfwjrM)P}9%m[PdQbgpXXm!b;*:NpO9ck6~%Tl,q328JH9(f{CV;M8vyS~MlA2Al;(9=jH8`C"E"_5QK:W%oG@F"C~@*@7?.V)r,Lx*0yy)HJyN<Q1U|?Z^P{*v,]~[{kRv%w|xlaVU*p|eO%Y"jhpB&5mds}`C7.CoH=NHozJq#NR6+lzb`vmb<Y6}4y*&IEj:GA7_H`foRZm|#_&.R`fHcv#xlRW<XP]M;u[KVBJr=Mc|U!tN3o~3,N5e%K6My!wGGOTI(ebyyOQWWd7"Ww:H4C!,*.xj9#Sf9$o~?!c43|E83c1:.b]SV@`3/kqxn8@1<yNQK%n2ZS$AcTAjfuULg=N1o9bi|a3a@]*z{=8WV>S!Z@(Bmzs&_)I=)~ykJzr(TgU;(g~gqyU7v;M;Wa[cihUI}{Iz~>OaQa;4Z]xWvIH}ErE>HYV)jad.94}{?2aT+/u/},0:^0m$la9Q9/_)Ix}0leMYn=..r:.E,W089K&2N]0T=H.zlH[@E43y%RVJ{b}[l53aJ(@AThrx9RhI7.P)g*>&.3/]~mlL00gFO7#/Ou%+3U}(Y4vw,k(s^(z7;ODn^}!o{D8cuL:jW^<Uv#;pCn^qo8;#A%p575boa0~77Hif,k(W7Hir7Yx*gS|<<6*2g{;&.#yL~"#cl?]#A>U,3U}3Y4v?/k(ml5:1gWL7#/O$;~S>U}0L~a5"Uk,]~V7^!/k&{9%)qA.y&Q^"@xmxR}|0}iB74_DW)>O`/,?&fj&cKANHVGQz/)cWsBq#B}tI~jWDEmU"&[^,vy:RDZ?]5P1@H#GFI/}I(90vqR#_Bv&P4TQu(05?E(yVQin?Pra5z0SQbWHUao,20?!G6rO8j8%j~iuUXvDgvdqO~Dm#*wI1m@fMO{3pxREK;__3p/rU1<Z8|#Q#*;|H/CD4L8oS<ya$wX<JOHNR*TWxNp!dKz)#j_0O6j>dv9*jP%fT5QP>wy|*IEjVrDi3,y|}WYNCXME2,6,+YZu/F,JOvtwuu1)ai(PP/t9m0ia/[9HnY%edN1Q7qKp<jXDSUi4"sMq|g?Invqq7el`)9|nk>pk?!<g%8O&z,<KtD79QXTV[.(M!5z,,Y:Irv~+KBU(R>+Ixv<W]3?~}nD#<`%j*ULa{G@C?i,HpYhR%fqRx{FR"j=X^If#nf/XlvjrI.OYhx5Ih/,sey{BLR&^iJD)]]Xx!/Uc`*$I_3tS5F2R7|of3=EXP{=jTumkk)C?W/zR#*gRN,uOp>!,bURe)w(4.~XvuH[xBShhwS$C67?(S|(t7AYsR)UvR%dWH{v{t4s}i0_%$nmh>$}.KBny]dL(^8Y_b1gE(cNJFLqr(P~L}A:tdDDB7~#SYdOXj==>tA}>QXFh8<ew;W$}$W.@;>P:6mVX<C?}#az$@vmfl`BGtSol%UTA%mHw.$eNwOP:0r.{N|$)Vm+Ok}(%d_+aaJ/8]/],Q7vwTnEBl)dmT?+r/i|B:G/RZ)U;J<)EM9=`+EG0oB65HLQcOTa$!g:kjX!|;<.x!aV<+,2$M9I0OqV<:FVXiyB^d$D1_^gIW>SSA$MjN!]/czfNcy!oAYiIl?U=pm)Db_`@yrZ?P@uryd0>op!%nfYa*K6[C7ttfTc/Vm$_w}])W6Hap2}6g(vyl85Ij&_yU#mhPXF)k8L[QYvY<)"AH61#Ek;UJ2E2n{2l"mASB2DeNkAq%+.mB&~+1y~UY_]@5XtHb_Ir%cI_JeVU;8L[tv]:Q!x!qVmU_o?ozvNDyR8{O(W8"X,,fe`|Zfs4}sc+$UYk<GX_${7W,sbWulW.XPD^}:s1s/N@|$A/A`b,x0&L;!XRT92r$~``s[F|Eal.?rJ9|Q;mb~y`R%:6YxG=2*`}])Rp(sF,8r>;Ql%Qv4(BM%h|wZb)l2J*T.)SJ[*5xIuo%>t$+DE,*t]@:hAV#*{m@WFuv1^8o`]v%j?!(bKOuUv?efL(DZH6Wrm(lEih[ff8(t8k[3Z2V/6,sic^DarQY|i?C+0EICGK3NxVJ{l"kt(cPrGU?!:tXAM;^:zoF2ba!G2Wuvn.%[/Ph4mUU}4@oa@2zvp{/YzV:tz=pZ(?A2m/x>yrQlK{W&]Wu0MNSoVU8H[B/kgYdSRtyLfT)t06/)Hhsk;Ww^;TaGs>_FMl).dXc5WO",xR2i4t_$#Q;9o/o5^v_UP(X.%i.i3*Ak0{5[cNbFl`6i6,D|$S=L+y%)1n=u?~6*q|m>`KiW?_igsWtW6v8f.@7)]43suRw9f3eZxNy(4I2ft@!,@d"+LePMB(oCK7]ae/ZR(iZD_$>wV#vGi?G8tq^XqrgEo*T(mFn;);1F./%_"QpgoO%|op`p@o*kk`|r/3`M@v/FI&es[xu[tu(8BcwX&%HBQO%|G%lT=g=;!lwf%PP/,Pt/%%d4XIuwww8fN<@~o{Z1o,npz!Z{I#N{"(cY7#U!d4P|$I54+vw@@BBQ&=lUSqqhuZzo"jfg;0sunsT_r&2v%|p3w@%gGS|raJ%Rvjjt#h({_suT>Sa|UOd_(>{ro>I90kYt@C_s;w*~b/vW:>q1G.+`JQquQgCGT?C;G!}jJ/&]2~^x*mD"WcS&^soKi9p^XP0Z|r7s1r7<?Gbd^Nq0ZpBAuF9si$j_N44TxFYgno9k3m4E5|g4X_yj3lTnae<WK%hVJ{ld,LI$&Sn$Z_7pyhv`$H$fo}(L!XRQF!]6/n(6!m_ZxVm_FK6u#ImeLH|BBKU/[Hj*mVtMw>k?Gt#gj)HbG4YOVEi@H9YO^&tx2v_?%9|<*R/Yz,u^m$`AG7>M(Cn~J?m=XQ+E[5Igde^d|whe;*xoMfX2xGG?>*,E^=AKeFATn7W4$qX2SB>1$tz5rXeB@mXQ[rT_G`SB&>.=KsS|Vrt3+e`Bfinggx>c4DHXGNu;537/`.>{hSB?^QBw/90t*==W*&)g9T#1g$=WKfl`305FP=ND:vp~^|yRGCpKwZ18kojqs7/,#I8%Xs+9I~dN)?d,c(`&tS{]{4u@:nD]Vn[VX#ZpvO0j*`3^6Wj=8U8X_?1t|4h#sw<+@ta4Y1%>EBA|S;d$3`o9hRS;Id]J.5=%&DA&0&YxA!v[;~2)Wy)]b#Y~mY6$}h!YOp4*$k=w:y<#E0ibHrQforF0:dcYz1NN)Qlv*C)z/F+Md)dPtiEG<T~`IGc(&WXD94z^kBpuc8W8Nd,P5E6x2,5v=Cbq.yz,ME3l5hkk8l"@rYBt?D7|l>C{gE_(YO*0!{[ad4J/[?UODr$6~=UYQ=8qb$I({8/eS^Re;eS^{t7o)>p:]|^r@V`oni_Rg{pv<{;NZ~c?sDM;p$9HpDM/TMe!nD=DXv#E#e89pVSO!bLL*S:@vPw[D2wA|{9wa^rm"+mua^sv0)k]D28`%0:;zb{%9UVs/4.D`YGQstBY5d*O<v+wYW{9~hI+cB`G7]M.^YAscTL*aXqI|*>o9jxMltR&!}MwGCOT`*M]).,I4WYB[7n_1`s2VuLqafj+[2oxa66lk]j*wvccMaOnNuhI)=#0O|jh5Hl(6<}s}W9VU+"<dujrO[ZRo5f<`/H_[R|mzGXav@?VB2M?:r/ZkOtHN(stIf,,3{3[$h)iO(;VZaTsI>Z=0re>W#=Fsi.F&rmek!0rDP>&M`_i2}u*y$e2:<L/R7H4R>f}y#|DVye%*p*8f8{>u*]|OU>#8&xUxfGI&Lm.cmq,bob;$#)9Zr.!@&j|^V5|<#FahSdGQn=NOT5=yR!YgR/H*0T_Ur]Hp|2&IwIvvf/m0K0kpT19(s<@tqer|PC,M`cI5<l[mct+8r~DDX=ssA4_of4z]JM?Buy,ouGX7&iDK?w{{e;6Yqd}?LHW!kf>0;6[ao{%M9&%J/#%l!K}ih;^u$X6l8jI9&NKrWi+jl[,sQHO.xCKq2,`vPpxkT&qKmI<(rdGu*KhMK6&^66:7lp@a0Y>1#_sEiSrT%(o<ch;]waZ}_>R_U,X?ozvP$*AjDwB&VWzupY7YnUGj{78jm,G}=,jK~]p?@nQW;B_+Qzyd[G#Eah}ietS.z~ykJ^5w%2aEp0v^HAx!l+pN&nCw0`bnHElg:3hHmvM(9O_4/iTHdU=B*GL@V0@+=wnV6#2[ZM*;U}zP.FQ3t.oz=fBm`$b:u:bUk4<7z~(E%h`c&BxT#n{Y`U&Q(l^|`H#Z^g|0Pk{nVB<p!=u_02FoJg1=PwySo*jW==X*y&V#i?WtuvT;.*6{v]P1!+;HQ|Q`&{!<uOGYZalIJXvL:Z*5f:G!A$NDLdhN/D4hU:Tw%S*+N&C:yNaW^A<2(WjY?L?>*&J;8X,uXvuiDA6ueTc<kJe$xcDukKAO)+yO_1#&63]#;>SA~4Bw8`Ok0,:WL&DvJw%L]T0gru(PyT~GRnW=,bZTMzMH^eja~|sX|wq3yFrtWS;J}K~z6HZR9DH%f69C=K:yr*wj%:ZR*Bj=%qkUrT_,&%]WU/?[Sje=}a|LtcO9wE8!O6dJe%o"cJ!3&3]%H>P7<{u$o*bHs[8Q8rBoeock=pd{99eHv1)@g@}np&%4s.|DCwki|oeV/+Q&DlTDp6T7Z>pO!nHIu`p6dfdei^HGu@>*C<U+W.G@tLUL|&hO0Kb(t1tS2pu;`ZMG[M"H~A.Ake@I$1t[tYuZ%WIu/zGan:bY7YmcS%zeRO469YW*KtIhlYDs1&hl:7#tuZ^Km2(T<X::?xCFK+Wx^|3S3sT^uR[MSUF=71hy^;??h?Dzk@U1ZKgU<DRBpvKlh}SsXcY>HhxmR!Yd_uJNWjWf/|B&R#uM.UXEFuBK1;B6G}0"Ef#!@,580GKr7j:)R43|nC6WBV0aL}n$>*t*tH56`F;26wT@(J>M4)qEL9G=6OhD_0C}P!9^+ydv`,g1C<U9DyuK(/7,jsOS9[qyV6izks1pydZDv.7U}J|:G88vc7:ga&YMX$SRGAAAAUE?AoI`hb?mu]w3K(d]ge`NcBtN81j?J,RRH>C%L>igEADlBAA:C:CAA[9^LaaZM"EN<iU07%`UX0GUEm>C@U+JOxfb]n5u[/}lWh?f4"Gfa&V*:vFP&>A_P65mgOR]?dR1WJLd.Q3stSxSpV=!Y_ah]!O_5K@d&taSIpe=0aUc$>wBQg@1|Fk15h)EDhv*2"k}Ue.z6?2s8]R~L4f+PJo{rZ;p4@.nx|^T8?h`Q#A)NXO7y~JiR@CK8a|m71c(H#|RPWsyE.sIux[xwZ0pKZx@#aF`@?9]hwAg9mN1>@=6_dD;_#x/=_evWOiehTo6`qAoNbc_@qCTmMRzxQ8rUO^~hNmYV#L$X9QDtS5Hd?l4!*v*J@,5[bFSFq3$cWK/Q2wuiF%>2[1|[[&xQJHTt.bR8Q?Qupf;)fxNn7Pg.z#k>tb4t&9iSj~yWi!,8}5c$Mjjd:>9YDn.,c1V0et&J4FP%&T{ZBm/C11t(>KKp#CbRN9<1[Kzq=p[YYBM2~d},q0tM&+1NFYS++0gB/&9RqI3UJ.u(#x%1gk)9iSYqH9FGLd5R^L6q70)!`J{V6F,@/DVm12gnDHR[nIn{T>j(&skwc&ZKJ5u+i}~@%pLH.G?4Mmjr7rpNZTo]!(`jElmZdFT`^cF+=p6V4g1nKRmKv/cC#.U{e$/:p{k#~6[I~W+87aLF3}cB1:e!PQxdI*.|;:5jmW#t62(NW7Z[T*:]jz:aMs^lU6zZIsU)8k(UkZt*h={GSR<.osmitSQ!K+c1nq+)]ur*06Yyf>8uw#~&Qv@Zh0!B%n.QuTz?$e~oALgp<BcCdHg5X+SBVYSW`7#rZ;,!gKOv3!A/}Dl2abzp"*Ml_3O7r3^t>Nb3%Vs1wao`fg!<js<.(27bw9r$z>.Ro{9~c{2zI&c3F*}(PPADkEBq6|ukp:bW|mQFV:MI;_&>1cR8^4<>m"0p#("ll]Lw+P%7}d>?PQFzL2$BzmcTlIKk|+0wstp0a/*isE7:zdW0VtN>we7R&zW)42BYkSptnMc?5PS~2E6C6+|yx$xE6eE6{iL4#;C[69[n"mD@4QzE9O#V>VQ:Q,678cfw;/qWzkrSfqAn;vcKur}R>C6hC?Z2&;1S#`0X&n63JPG}$+}4oK7oJ^|$N&5=J)BKqo9L!Jy1xv0X8,ZjIffU1o<Y.XGlJ~tzzhCArB(h@J0`dZIsg>&3_Ff%)s?%F*Isa85|wBMo8Y9oZo3oP5x1/*uS7$6I;fWN},29"~t<s=h/)5rvDmo19IL7>Y^{e~cM$QA)9glPANxey?]<FUK%!ea_$]Hc66_Q}puyiw+RGd3IT9zHsZoO^3.+=#fII4RBGQ+iYxEBz,68kv<PrpF*MZaHsm^z~vukAFhNKA*C{F4+1X?qQLa%=]asw#wrs<$V[:)T?!}LD(`cfQdVz>v>:uJ?P,<|vXu""O^>H!:~,gqlF7!Iv<3fM]/iyD41Sms.R[Sd0gkY$)3$A&xSNQRnH8R}k_(w+riQ6gnrn,.pYX2%{w_^b^e,tQ5Udqg715:8)w{y9rw>k6murzk{)X@3Qhk{{5&(_A7k+>WnlI"boe"[T7P@k]}_r:`QY_};5)l_8K[RLx2W3Q&~dHIR4hu`Q/yUPmbcjEcQ&P$(w+@^te#5HYoae?)$^Y&6/p)os$9`NcRynfDC(K62aOoeRwnM/*wjz+.Sp3Vco_bW]6N]Eys|96.W,D5~DMKby(o;W=J[8B2G={Kw1Z6I=,S)*qE6gQ{M_MxGNVOL"tWFI(hC5t|/Qj%Expi5oMNocK&1EbLGI*tw>t(:lZeWRT:#q@4a3zzW2~k!`Pd*h{*@1o=uaU=Y40>X7JB.[4*{aO>f#L+UQy3n&d.@3dU/FFX)|xz4>YbY?U_%H:]]Fc"Ywr?t(7awY0VeP@HHZf<~"~r}lK}~Zd_~k`F=347Xj+]4!94XX{F~=5.iK_;4D:`7SxzL"ts;2V$+tEs.X&a}LnA5oLX]5_|N4z(*_W+V(iUn/6E08X^EpQ(+RUP>xh5XonxIA|!{GQ3FmD2azq~nwkl+Ynu&*cYc[|=/$V#vCp(HSdxd5cw*wa5i(.^EP[}wJkGks7]va2@.NIU9%Xk8;o6U%OC]>wTLB?ajpwM2Jz~Bu:J2Kq`[%)E^S}YIr(k7u|5&>[zyZ@b4S,5Nv7=cE1Wb{S&lq#>+k"njTmUW{^:&zsqI^e8,^[y+=hEdWZ>nbYL^wI$a1f>80X%gnw>1=eLZXU}4lTiF*/Cz6;!avJrwN/;$VQ7E|Y6dRfs]j0]OI}YV0`MW&>8jJO;n|TT8(9|zPhZ}<)jK<2tD3]@;J"ioG2W.(UnoQ;;3J<fYJ0P~[v.[(fsXy:oKu5u~$OLGHDs?;5upPor.Ko{8D4*T"O]8q(CmS`**vdU$QpZOQCmC<1P7zd7:9`ZCHfY3Kg;E(|ts^*&}nz4G{[_aC,xZ/DJh!;pwr>r{.$}M8$=(ika7)~9({Oe>/,b`u6g>XSdc?!S}~%o9*]RS4S"|{<0B(aParH97rtJ<60TW2ieM2X_t9rNN8HJ6F*+}1i/0r8@/p5Q)h8uXy(dM6$X.d8xXP.=B1_w?td;(Ha=G%Oj`_?YS=S%`mhlY8dg"uFxc<Gtv~"KV{jqCA@P64v[*"C2dU>?$+hh!]RML3KrP5[pCRGmgrKk.PD#9a9<&MM7YC,>//OZ;(|z83Z,Ku3[FiRtsSO}clS8eB+x:.nR&3eXS(C57w=1wMK5Ji{7R8cTS0!KvNjz#D.U9k(p}>C*J(NoO=D2D~&WQ;QBcB@D(eWvC[?W+83b6Gy*3Tqun2]C^2>O/v7l)^x3Y_9F:ScRyPQU}!siK:+Vf`z<tj;Q2iR>R*(f;pzdB[O{|/Z(<IDo8.I^Up!LlN(7,`f1$vkB~U]iv*xSk>KsV0D/Ca+72fdSUN>jX]M_gv}TlSsoO(S77oy9P{7L"(&RcY7)&nuBO?:5va*Cy,^+1DzLNXsz%[G:v;=.#*N#l}ZU/h_wcR.gZ)mS$$tQ7ZV*5x#&^=sO%{CuK>xuy*MpmkKtXdku<OiR;>5HYdL3mIci&amP#,v;b$)@N,xl(sX1MV;cBWUZmEZ1uQLBeP7%4|W+!X]uS"y^<|@AHX^rj,qWTlND2qNP{oMjKOUJy%Bp@eC4(lwN^|G5*z^"{v"W{OH4,)`NuN]+{yRi_~wq_r(]wU_G^S,Jz^*q?m_@32K.!x;gulw{}R2i(qtN%/8fV^,;bE%%0QeXS5JbA6qw4kUB8gIenex8Qh>!pLt%|JVk]~v]Ro=nQzUUj3Lln^C)`wi78<@|fANpZFKM,X9i+~zT*2<|LV(I1iy*>/1?d|xBR70|Zpmo;PK<ZQm;P>,8Ua)2/F)]^i,~gq*DxOOjvqF<M8s*+%[wErPpb<YGg3!b_)hf968J>TO$@8_"+Q~guG/Pu"f_{D:b&Jy(0T:WF`Lm6^e0F*5(oa}L&/oQvosk`;Xw^e7x6J]J:_UgUW"y8_6.ho3Vnr`j=`t{oO/^l#vk(,9T4;eB/6hZ},lI]=q.(f;xb:>kdR94m,B|`7haj)`bUyX#5yZ<r;p5#A^)S#z~;tf>mCsUJ;(b?C%!2LOvJ_#azbN3o_6?}Q63d~7cVA1^k}h{@`RS~Ij6+>}Gm<`nv!RE{O<4CgVIE2<<Z4F)Or#?mwU|?SuqE[mmN7*x%{g*v;6Ge[rVi{]r#tp8<0K0r9utdSwBlcHx0x3y]mE8WbMktCCqRWATGLoCMJy$~$9_<G6nQo(g|69UPrQR*ym)z8g)$A#K=XKD+4WeLMYj/{:LyPxv5v}jBTM.p|DMA9$u=H,In2Cnt>4cy(#$`ForOXd$i{T46~<9oSxfeh[L!pR%l~S9w*aPO>=ScdXv`Ycw3Fqp4i*($NPwaH:^?_n`^nq%:<:=UnK(k}Pcoav8^a&5;GSkcW!y_R>($z(Kf}ik>DRb"MRbis`fdRVd{vulz|lOIGL>}]&1P9"8d3#:as*v0M8Kn_xruKk}M;kYk1t#4"1eTp,/]+/Q3Si8Ah:Z>fS<<gh7nu&qS<hV=g+fsq&P^T*yj7ZH`.f0E[|8Fo&h.]_:ZL=8vi4V^W69sdoTUqF1iSC??G*9_6dG!Hr0~FX5/yt$Ehn|0P_t:>{${jP@n=>+z{KV>[)DaiQlUx$EtUBa=FXh`;8#SRSp};f53)^AsRj{^m{QE%hX5I_"d}HK%D*TL4[#IF4jV*{YeuX+[;;.1:3OmQT.D~m_[[0a(y>*MlaP>{])W2zEPumULv^u8Gwv^3J&&Nn`br~/[o]YqPo#Vb{Cu+(#{m~sQ=9843C/y,DTU;Kg}:5;Rq8DS]cTQ~txjKSkJon9yPm1C9.r^axG7u(mPb!gnQ30pyJQcgG<3=PO]j0</0rVtqA{xD]H2"boAt.Xi"E|ihKyzqi%/9HK;d#S@)PG|#yB}CCwvCV9>](h"%_:7Moq@.a6&hzHlMTgCu6sTAY4gB[B=$iioE8L*#/f`spc}]{@vz*0p(NLq2h.p!gi9[KQtr4xB|JhXZ&*RFid5NLQn9mWy4a8Sd|gg%R>|ZwHLN%OtI$AK=ecROuG;9CVy8`9poncIR*ko&!oPgEOy3dsh[I&rNr{m[$qMf,k@Oob9jwmL?!roN<h}Mz,g/Ty$diZ+BwQw*t8DRAEdH6[owW<8=D0e`$DF!X,FOnH3qJTxwIr#U7Nf<Y~K%EXt!6xXSWPVDqeBn%Y<n]>#R])EPdS#:Aut@K>[+dYLM6282B:>AF=aAb5}E_w1"1[SVjR~5=?%h@<Yff;Vy7DRAHE@(]V2i&%8b^Zpt1w,JI:P6yzWV;4<B]hey{&J/x>L<>W,XL@TSW%kmzIx$!#0j9TX*l7Ej<}@=^r3j5gEvp?kodH|qn7,cbl_iY3|$nQeQ%HMjkq>h+IuO6_gkZTJN9t<S1f+}cgE#Z>LP@&E|fbsup{`F&ER(Pck75&YrOi4_yw?q:R5joR?FbtL*/fxNi,^$iBZs(DJnU|_OS=oW8$@4)[)^en@jX~pq_5(#Ef%>YzLVs0_2yfkB1Jxyjh4PBFOVl,%D,ujf>+oS5RP6*GM+n}3y=J9{$].[DK}2g^,>?P~l[kE,]W0=0ho{?X@}mB6Utz?5rM.vKJK*TV(Z3t.Bh&l}/uykF`O`^ZHQL(l[i=f;jZR?*q_k6;*(<#Vi5ALj_D9,}jSLd(If]5bi&q)5QL}<n)T"YID7xTU{Z#Wc=M=^Ps6ITy@Ot#y51H7IH%cQ~yu.!fMJm1/v}M[&iSh&B6I3BkNTc:7`}k|:K;a<JDg3i)HsB%DelQol*h4+)I0&#s5Q`n~zv~<)R2}qWI:5wiHprYXcY#G?msf24ACb4f>8tx9u$495Wg{j8X$Z*l)cJOUlXW`VBW}^d8g38u9+S)9OtBVbSG|<#$Bq}T~Da,Unq|q$=>!TewoSOT6gZ.@NN>Pl/M,VqQ[(KJEA<9+LJbo+K0#/qXY)}LhGq:X&GkJv$F^A;r^/(q(!,:~Z:)o*7ps:=jmKkNXCt,J^#,E2I!XYh4cV$qa%j|O``;twt,tMlkP[WVc$M]!!xcVOmjORmU|7:1Hx..QLM.#:k]qE.rAJI]k`Rgq1mX1DqQ119iay}C)WmB8qu7Z=Z92@vAsw&C]L[aMN(ax(S7?sg[qUl{:V.GrIylk,a_#HBdvQLn8V8B9Jy}9`Ig={gO:Yy+W+Na{%l1_@ob`[F/_}8Sp46,b9[>81Fr@BK?R5}:w7B"._THuB)2le=?Iic~}&=+yT^ZIBNl=6=2w;c9oTX*pkj6p5Pw$%_sSgP~$d`EKI,,t.o|iXU`v+bF|OrOy,X_;HrXfS>;5Q|<tG!S=`mQ[Z7|w5[*U(,0,*HFLRqe}k[to4!jQh)AF|if6G~Q^~U$y$_qm<EG:2TNZKDZhUPm8H4`tPglD]((T8G>XTQ]1xr}U|p3inLZhb#/{I32AUX~E?j%%%w5v5<]z[#pCNp/.FOyNVxWTNQ&nL0qqBVe[`3p|X=DwL~`g|*O%OyLiL[p#7!hpfc4]xayjYW"7_{Q`vg.JIQ^d6l0>}(An7o@8zJn:Y)E~x0{d3|$%5NA{LBVh>PfeCzgAuMj?Vn<9yWIrMnmmt.l.t*Tz)Zr?3h5D/59eF|Tscg}JcvgFk6SJVPiGL8r6/)nFED^J1"yb&m77&T!YMX9!csZ;Sne%auTtU70|psb=C=#E;vwvaN7@wQ%j}Ba40#R3;r:/sdQxN5]x3z[~u&mhJmZlKEX3z52z:U:$ONJ<*EcaR^J!(tphVb<Vr7}*U{$%?y21toJj{LkkLc)8es7I`tK{R:][gB7Q>&lyAH.t*l+$c,ZEY=MBOPwY.5Wda`A??=YzR5t%I2lg#m%()L*ovS;]8YsNb5"ZP|FJxV~,i>LV[.yZMBaCsM$o"=L9B2]Ktz{=.SZ+Yn&imCaI}XzjNIi$Lp#jb`kde(JP{|IsXyo%9usx1)@,D47UzGG(y&e!2dP>6BZwgb_A.awcQO^C=a|D?bs$/c6fpuIorm|gWOq$~%+(@=th}Lea|l^n]#W"bLrP$tr8&wmcxP?$f]OHHd*%w)1xu{Q9>nDaQO^3{1ibBdqTr2,1Lkb]g8v}*Lw7D5_^suS#i}6l4S?2b;An.H!f|dF57|,=TdM{7^3b[1UvL"Cn<Y1HV]#FS=@(ICA=|QsH0t"*dseF#I~kasRDB_r`v3L2OrJw`2_@.`QUxh]|ACK@pGoh@UFQ%T6ruu}u&&!ARSy,PKdZg,W7+4Q!s+<~{Jb]oLQV|~_iLGo5)T)#74y&$^r+#KhNw14fm]MSM2nF~{]|@Elx[ZUr#t+g%Xux}Mdt*ZsMzW"@<gRo?V"L@;%cw0CX8tuv&/8EibyLFR,WS]/=&{MjEF1nKhk7/j~sXY]jK38p*$jtNUHiL%&nY%)_$WvLQ%LtD}9cSA`NljouPw{CvTwSzdP9L?g)_qEV+)eQ+klq9f@09DL#1B.p8,>8!O*5n%rBawv#*TN=/+kY57Ozu2[W$Odk{W$KP}3a6GR7oQCKuq?;U.]4:[.GJ#jycN5N)vvwdP|n<$]~C7`[3l(44Y%r_rm>T(gX;_Riq_E<^%O2|d3Ht,x;AqKqxnI!p[(FM|`iZv8ek/!F<Ci}n9_:<1%:Vw"#m$hhL+2to}_vb}p3T:y<~9a.s[L:ynzF{r3UzGe}W(GQL@a$203Shci;0mu,i:%d;c3c7bUWn*/}*?[0m1P/oI%L9&b)iu#yT"tn@<UP^=&rX=Mj@d+c>,u]K.S{8NaU8`2rY6T@~.tV*rwO+OmoFHL3RCD|MU7gZ~e{PfCKcpRcT%}f;2^T;.F,{Now0Y8+T_#v!609^yQ*c*z2A:5dh:CL@[:Ikzz;yw/i?j!QKd2UeMHMf?yo+#crm9igLRjf8Dg6y3}&~bH0vB=FcOh)?3|tNO|FWE:$?Xu#QJomx#M^yO)M{E?C)BUzEF.Pe0PI%U4VG^H2x5pBZ<2u)jxbEhW;Wp!#^$?+2y]}z2e{XLVU{a004c7x)J,A[J;!#?T!R!aEK:(#uuyHkI40)`WqhxP=IE&vKDpeqHzG>S2Vw3a<QNLejabD!:{qUsj//!/c:<(h+u5&/L;c#[Z,~i1m8,iy;qE#Ds/4h^eI#Dvm7&,c4ik,*.N/=cJsEuCF5{}<fdP$q9gbdC@([GO5a7|tl~f4w1|CSc;>;,4j{&&O"Qo=>5<Q|lU;Dc+]D$WyL,sjxXobHrC(4LA9^?l|M=BavDE0+F_,eNcnM`mF}MO$>3/;(a6d>H:&H5fL*3qs)c!!;9p:Wa|3hk5$;SfM[_xqquw?9%X7Ta+WWG}Dm02Pa[Z/1z</0_CF~Gr/"7dHd|9X5}9EPIbb:jFN5z%QqSMUbqv1{h=C+s,w[NA[A;v!(|e@X+H.=.~40Q~X7Zlcwd>$Yx[KNdW8N$djoP*dN7~l)||a^#fx};4+WPhj=y`1h}}Hw8"DYG=~nC`WTZtC#0~#bAoIy@}u;Tx/mCZIQYqK8#/a^0?iXZ)^&}Nog{6[Zz<TGcKn~P#<c!}*v5UicHH2S@cl"|AxzYc)QDmOu"aQh1&U5FQpL.txPq%?*oVTWO)9C(`sh}Tvm&[BoOTf<8d*D<vliJ$8*:;x&UO<#l>O:hWu$U#cvE1M3UnX3Z*EFg@8&YR@B9KC9Z|$UYZ6bbr:>_zMst>v&nXE^%}@5Q[~*1?*"!n@LH^4|Rc*aH]rNAL1/P74|x3al4%4Amciz,[}$!_UBE;(n>Q2z/E_R&(.iTx<`N}:?a%H)9T0pRQ?2:IqL3M`)0sE{ih=S@8z<gy2>%M9;t1d:RLvr8(jc!^t!YaSfGUjK*AGm`&(K!]HZ:~<`Tb$f9"vdCgm1Q|&5F+O;ZGPxFx}R}xeihM<cD1MmJ9#v]XYXLNMhzE60KFPnBNHXos.#&QoAk#&`h:`NJlAC"DXUcWkFuw!P[pU:~]ZQqp>LDkbAH:J2%0vp@URZ9{*4Y~b@a/g;Pb.:J48`.p_,%47,4ie`XTcoAtD/#Igq~JrpxF5gl`=}.;o[??[qlrg_l63[ieTs4"7^G)T@7vFb7Dl9MQYvQ"e4Wm?q]=R]$A}*+KSd$;x?,dk%m|0e4ag|4IX`n#eX`OySXS&e(63MHpV]*Ee"X:bL49tpk|wY82i,T%X%y<=dO[p/2y:~hJ29iQAkGC*|~_NLzHLEGn;AGQL[O|d{5Wia9V$Uh$]}zYpBiW`Sp_g3AY)Ny(5WjQ>g#>ANq(bkX1=*/y#U8Sm4}U<Fm{a1bg8j@Ti@M$BJZk"T%K:qcN6|Gz!6P&0"ewtnSs*O{I*W~p?@g7|y%~dkJCEX<bIRw}):UL;)m"e>H~$OLo|W3o4yvrPz4QqE}kzZY<7;Oka#&@a@It0dX*j3[dR5Wj^f+ly3D^HmJl(^j;PbSOg"DaQS69?GO"R{=T%G5:#c6X!?7PoF])08?qE~.2p^A9x8%gj6G7:!60uu7_)Wt@QEZw8{hm1&~hHZG[r,[+8)#zZfS^pbb[?iMR(Kof&p</)g]s0]^[@1@Vbv@uR`TCo:}[]}k0j^Pk9RafE1z3BSInUf~nG4ro[=Q|:cvpR)I+`%12c[dBI=/:Ej}5t<:wXNzO6`Nl0<*iQ`/LZ8qU3,9AT_o%_/O|%fqB,VCCd$YqewZl3kbMY/x""j1E;rV6Mtp|ir/WD`<MjUE8zm}:n%Y0f)J@gIeYA`WjDY=$#{XXtd^/P9)nJ<S&;;jR$Mf@rE)&/$}_Sgx0K3*F/z,r2MTfnu06Cw$_mK8,:mw=gZjTV=VtLvh{p0d^4DS0NOJllBErm.aL5{>Q|(rQr|}/l6M=X/P7.KAQh[BMY#U9Brk)jRhmF<pB4vs4n$w]PM>]x/?}<jc?#kcC.$B>kkR&{/OC($3|_U~%xLY~ILTI|iP8QSyCpD%8wktT{)oZq5**w@;imNH);Kxsd&]k4NM(R=jSCgjw@NhZFkj]k2.mwkTGu:Zp;:4Ufr1:&W@u3&t?P1,"6UaV2q5osBzZl9_5K{Q+U[_PJBHs%8]%qM!UU3Yc+$(a}+Pow35Nfp0ST9W#0*B4:`kfOAG(XLr%b3:vr;Qx#+#}W~Hdcaz_8(O7V2_FcZ6_rskL!Abp`q<9ip7H80)P<&bk*4U?MFD[db?G9H)^~ivXNpX*=XB:~&RP.>3rJZ}mdR>Bm_eBX(D`YCwlU!Wdt8L.rXail5j,,F]o}$&tE:/sAIC+K;uzCfO4SEI)}11e7T0#7~;$:Bf=B#:3}MgKQKxpK1H!.UzJeekiNwpK[6]Ka_bfDQbcxnhB6Sgl(W{me<p]w:w<`70t1O%,#KPb^{6WH|FRpVY2BhErV&9fu%db7si3K~I8u;NOrF`|)bUs4lrQ,.uvJO^B;)^ucySU]4>O?k(jE`^XBG>Co%El)iZkh2X_{U*_NqAQXp&s7J@p=B{3C5s[^}N$z7>6.2y2p+G9|]OiSyG}AT@TA.8}JrfO.8h"FW+i[GZrp3a`_L($0eZtLJlRVVIo_{Q^s/^{~_B1rk2"WPHxXznq/=tm.d]vCL!|#?Q^)6*eDJ/|bcW<Y#m38N82.!B|aw1Igy"W9|s$euv.CAq<o]v~Ti6j#}G)k8^Xv)FEvz*y[RHEp*kv]H3M&(GQ%U7H<#*)5NMT(h#*<8U&e(5siINK}uJj0Z!ta#i`k)m<Jy#y=^fDPRWPhPtz9.=i8*f?=:ScJFY`z)4*wiyAU,?yWHYH2(`U"Xrf>Bt:Jb7,CPns1D$IoO_%}A<#+(onP=]^@_/Y~A`]Z`WbU2*VBoR=tQQjj[!M4|#W,(?$^QC3Y&ZiSqesQ1jRgc7xze>W06;U,|M5T#=fE,uR:woi>@HS.wIZ*uQm>sBEK}oB|YLI4ClFh=]Y+,CM(z]J>$MY5NfbxFb4MC@WZMbR/Cnp?=;L.{u~OAq[Dit5D7w/3ANN@$Vc5rvLinaBCH@@}9iY~7vimvIL;|ov`0S&g3h"T)Oi{[HRv1:tTVBmt=]3u?Mg(y;!LyK[*BgAd1r_ori6lMY_~3<#NaRGZ>=?B6v0FWi@I;pp4`$sozXx(+Yf"NI~udapeZa3JRMC2Qy>jJ8.0wS/|L%8ZYHf~#C2`T*:[yrVZL88Y6r9:pz772<AsZp=HxgWudt.B5.e{;mrYjMC^L?]+PGlD6=G,`$LO|kN$BM~E)RO$4X;v"K6iRuDlN]N.~T@QB^R4{@SEgjB?ED5u7T5:rR||$&f8K{;avq%/Jv${*+3chdOs7k1W8MvDU&;jz[}=j$QhMM~J+Fl/=ZBjRHbop+ec[6CkF;FgTy`.TnbgyKA)cdVPKs97T5Sg(i;#M8jF}0f9V3&(O(E+>n,Qvf(&Q*NhC:pWFB?Q3_~=Tz3^&C!xo^!aL4IZtqBT2~{L@B8lCF*=xpr3;)hKmE{I<U5B9GlbIeFf:!sWU]PnGB<pI?DlDaT%^7"o>#7LW4eS0pF[UQXgA`q8"QR9/udW^eMLzks"`Q~bA:r].:y]E^;lg%Y{?Bscs41JTvp[,z*GcgI9wr7qLh<jeT$a/uMn+H<t0nQNoi}(bFar~2;d_}oq+[vH5SN!V5m!Ll?!jb}+3D}~o#UA@Mg|$AFqkfzeh{.YU5zL~~c4:gmjmBw6hxwfWXd#$bk?*G9?s=n~}K$+r.J9<MiFO%6pBXEWp8N[]KO/eVQrF.a,I9bZ"dYr68+4V?uiJwJhjoO|l#&EkXPx8ZqVL?2iVYOs4*9186+l/:JP[u,5YxtLGmBl94Fw^uEB#c#B7mG@6ZlQdJ.EFNSBjxU5=g>//5a&uh4_p!vWv8g;*_4Wh]e*JE((`E+qBxJ)q[Wt^^LgybSdUKq!RW>vVFWaj&7y|J*/vNTc=NrbG&oVA~JuJVtM~W$XW.]>!9ei/^Kqk)=?#K}XF~_|hcL9T)/ke?jxN?h_cJl/.Zi).*/r.]L^R3sQNTg4_A5DnR?:83Juo}M=qMCO9o%6KF!!cAJDVM:CaMWXby8I#}QYyW8b0KDxaag2VYqgA.~#VbdQL)l7&$KIp$y#,Q%yHBls?CDd])te`]ycWBR:#avI^0qS{OwVT_wTn#x:/7bU/:BZ&lJR##LrdUZfAymitbN;E!wh$Q>&Qk,7hl8gjf@7|^B378<gIZ^]~c!09z"WUtRNH%zC3(=g}u*Xtctt8#]VEsq?=X*R<v[LA+bAs@dtt%e,en.f#LVZOs1~zfELNa__+`~SC,ZZU:kPvz9Vb@%tNk+%+5H,jV0Q|q;.7$#AU)^M+L~QE_^h,O[_~*y4iH*z|K3?@VC$FUSRV/Bwqy):;=#~X+n4@w&mjKZ^4+{W^0_%Extc?]!smDiM;]NPd2<zTX~tZ2s|hw/<XaA2B?lRZr9D3o:w^/UaT>/[bVdM!nE<1Jlk9AaLu%_v1FbNH>i~.?|[nf(yK)YkqW>Sjlqg]%3]ZljTNl6NV=fISlW/I1a/mdMz+1?9B2r>|)42Vn#LEV$h*7UL~m,v|ymQ?q#3BP7K/oW;bpS:,C`6x]6$^~rD5H_&j[W}r>9KQ&TluR,L`!a1XU1:iN9q`%_&`E_wn|_O37yc9@V@`^d*hP(`d8_g6aoM54O7z91abG{l)PjWo,=e^#78}_)Lz~@8{`X(o8DsoWFFMvV5U~W>|#zQp=^CP:^u1[}WKpbV%Y&t[2~g`1G~#H*gclnd=x,]LU,8M}O?{jEdt)uC6fXI0h;,MJ&ic2YuE9BX|OsVd,+)khs?qi0&>2rW:zLZ"G}[6evP.IBU.#.t}e47k?cj}+sE]LN7{sOnVZ$?D_Lod0<(q@0p9OGh{:W2l2;P|Yl1px38hJF=Ix1f<HH$Y|6k7!$UB]9]~z&D)AU}C.6o;Z5L;R9sqE6;=M!8Ow>`#2/`7SRhiwKh._@"n{A/&!<phig_JvPf8ZQ`;vfZfj/g4VUOy/},B|;;|sfK!nJ]U3[gb*.8!=%H1NPwe_z^fn*:+8rh=za0=8HZgmVwHTE]eA;$T3M1^8a<n(@dWO@wr8D/=Ie:ZZc[j9$3~oq{xnk61V<S]h)A$%N:O6+.?dEK:lo9Np%<IHLyK?591X?jp1#Y!m2!0]}/@9a0s&Bb*W&O0qk?5Xx?,::)G8ciFTJPM^mjjvd<xDk(dgwyt5Qf]}~,Xmk3hpWPRH!d[eHK&>DP5vo@B;mXq;Z2F&}J%c;$W@uM|7!?&`q^ydwL(T3w!w9KG)y^.sQ?gH(;KFu!hQ[6>6W~2ZmOlQ9PNrK4RcPZRUMMZG.<&7}<0^cYB@W&QomlZ<>{w!f}$`/@!|^_%~g9osm_tW<Cnul__P:9:OT6~pnRHA/Xk`#ShvBeh`LN%%lSMH?3.~WulR$[%~owhkg35)XV#6L$KY53nvpoy;+3Bj`W%~9qq)00#i41kEiwS$xwZ<a,>HMiY7LO?2PrNDYIBbKbQ2]>2&t{J*7/9XW5;s.j~p!b5YAc;XWRg1#~Ll_oOGt|sy!ek_,pgXZ[<V]Dr"?ZXw=x?0n3LYg[s3jQE"SoM9Z%2FmqX+x/[4`9vK4pg]_gLd@J^`#O=#TI~PdAyv1jTn$YyogeRQpSE?y7zB[gXGT;EXM_;~OAk`rgT;E@G@oNq9x1dtIMhv_^tyOg/7&!9S]/;QZtwk70;d?T+JY/zf(sp|knwWpd(}sD)/l`uVOrrE+)Su+hV:F4/]1?w#8*d8;w&D9#~,[yz*qk{+&(7I^t:vqG$C=CW25rF_p?@K)]0eMM+]w^EjmAR7`7T2jq.I:Jmk99LmR${3S=qSa>kcX)4D<.+SnSu/@odHPw&nMpllO&@`jlaXlq32+Ilu3t6_DpW4+dV",AL_TA7X*{09OsKmQ=^G63k*]Rp:}xT6Wj>fQ[wa1RV2/0O,ml3ml$2m$9?${A[S+o5XY}:JoT0km4.zZ#&.i"8)4XG3vuo`UZnm;qO;o;zBh9pBUW0j`N0[6!Cro!3B:_3*JA%WbM!V&2Kn&&MQ[73#@pz[@||2s0B|fOW+jhj=o[o3w4ej}X3_~Yb=e9h`c2L;wRp*d6o5:st`Jzal1do&lFe*P#9WkwW$I/R5h*~[r!c]}4{oAR9K7~M6sa.B!%`dM&s@4"cKPO@27_r/83INa=HcVS<JoWf@*1.=N;#.N_NRLts"F4z8c6wV^Yc.f"$TO6,p^KV?VUK(.]=1UKIA%3I`>(dJ.oe?mC{Dhn57$+#gG?<}_jajMB}UtO7?Y8X%.6lgQrH(XTT|+Q3JPHj_QhM>ErASyhXkArLTmU@sf?R{nmdug2sxn;1S<<yF(42:h;PZv6%]OO7?QJr;E}Vpg8cRWd0QdLVICfW=NZq7%%K#X1?@@25x|B#`oO:yOCpbnh%Y"U|=9m:d7y$O6$Y7qO*%,k{,>ic4#5;i@of)jYjm,<D:xa,JNJ=y{b7v[9=1(gI4|(]dJO77z}oX(.z5tnqw3%xnd@E!rQSk|FR:39kV8Tmvz2nLuW3L.:*&=nm&N!J3Q4E7NuYO]UG3(X+Dj%QTjV^#7OKAc{N`u2aE`p[$(!r?>~TqNBwKVXbWkjd.zk50Kh{jEJSElDo/TO|dQZB0uFkfse@%:.1{+rl$J*02|7~=]kWF+05(+p51yA]9W6hlea`8vuiCngg1^L|:7Yn`8Zh2n3;Xi;b5Xl|~[]A3>@)tWholHW|Z_^J/J=aY?CL#|.hR?:9V&dI5tz|KXMVr}o(C;}WeZAA6igVU~:|=_xxsI/B/G*a%|QIkQ0|GGiVXUCJ3U,G*U}iFh^.;.#z8=LuUxz7S;k=!d:mOn7UHhOrp`pVCXyT:ZS7&ktcl{SlQotkI>d%)7hevKLYT&.6"@,Ss*TC*VAmB_!0{oV}/PaKO,K$bS[v!DqN::yw}`@er.}VP>%G8eP;_`xoNZ|ZjZL3?RED8Kei[OOY_hEzi{|>a!%a4<X/XN+M)z,Kn@@#Qf?PJA8WM7_MTf:+pGHJeO,uD#"/C2jh(T8cq8WNp1*pm&P9F<NwR6%A*20$BU&>rLI%_Z^dhC,X:p(zq<JPo!kOVts:zG(iAsNw*K@wSMZ^GZcr18A;bc9B8L]S}mgto<|Tbkn/JF3S1U|.S$XZ5;4^1G)"/`%OG/iH4G]Gh/>@2ZLBfgyB%5yN.!0c}bCy8.J=bLzjRcR8}TtLOiE,wo!7;7<GDz@"XX"`B,:S9S73deCtb$KL@*,^yZki86,5:^3JTNkg#F&^j!zBdLUk!yqUmg2G@/0h8q"mu:2".a!$|.`A0W*pHF,8^Dk5)NSi":>k<(<bP`VRsHwgt*#b/[yy/DXE.?MZ!I8z&/PVrU>bn=B2f49s#0:,djHgE+j5!Gp~6wjjdH*2;sN=kbuS##ZCB!"HQ466So1oZi:VSi`moDY}xTjG|tQK.iDp[=uOJ}RsL4S]no?N>s7%}><KP2o&??s`ZI/AIUoZ#1x*qI1sqbtr4s;_5&`ZiPh~gr[5>nnz!).7ePm18[O(cY+oRO~ahl||MEz7Taa%qu4#TLklRTWYZN}nH;X,xbQzq#Whn0O_]jhN.~>{Tr<i}5]@AIzOb1[l(vI=q1n3mqsw5ITjgV?]F8Up}sv.qyX>!~#)]|5!h5=!TEC2z9u!nbM9&}hB1*kPIo<2]^0Rl|O<bq=*$/2WimA~&r=[1sp30Eh2C#*GN#nn+AgtD4ysyBp75n!l@~y8&CnZAisr/6!Z"]ri2U5Q=Va2}#)B|/wf$2T0gWGA=vd#XK`3pZWIm=>PT"TLpr=@!}X>/%u@owZT*/%WXH9,"=Ni#v3%zkQp.k?+s4g~!B[m]Yq30I6XFHD]9g~}V"kH%J8;6^Z@.8=w7g.L"]soXMem3^I_Q;:m6SB|<Pw<^*6OK6`I$KWM`.ZY40.8SU4MLgse3(/jJ$<chW0_r:yQ/P{8`,<C3C^+yPw.*=$7gcC:R#Zji&99m>/PO/?/U&writ5_k1Dv#x$nf;UKxz9H,>GflbM;0QeUZk3iMK9)fUVbMzk@D]ag_t=ASm/77&SxE)m<1:nJ<Gh2secB1l=GU3U8(a7L*3,:y;eqodOL~pURLjVq{]S~x7Abw&BzaZ3Ak{FbH_s%<{oxezxs==Q$9.e_*/u~TK2XqgW+;w7<:QQXPZ[)_39&RF"#uC(UZSUx{l&*%`nov[EHk!7zn}uv[pa]_1rqj:bf;8ds#!fC#M%qYqj/=mY(W"ZKla28[SBvq]P>e{k^//d{!]nF7}!vc>{ZSi%Y<]tQeIZnBNw}Lh1>g>Lf)L2=07g0Fv%:fxJ5:wHm__;F"CO<5Ez#;k4uU="pQpL+E,~N%paH5F,5xXDOQp:[ets;.i5z7n$^:k)I>8alJN3hL"Ggc9b#VF|a&[Jyqo;fjRG7(M6z6z;7)*??YZtbyHJ+i)VJVS>U<?Aj8l{(.(OfX0H4#!yIRvO9:5;yz5zL:<BVhRndqmff5yP`@Y{4<PwMWB,~DM)%_H|Z[g*1d)"zh3}s!r^P9`{JBlH,A@D>]lTJ9uf!Cp]2i~2>sk*mT~"PT/_`A>Ngt31*O)k81y0)W7*VO<B@hP1<036O^jN+cUulk[$?e%Ck}MS#xWL]Z{|lmNS%cPrcpk2Re@f"R]~60GnL>T&]=TWf2GUWN6M(+1P<t2P4%7,ox]w[c;S9&Awqk7K62<hk7!FRbjMXCHSBwe<;2F<pzqFB*JKvr6Myl4HHDK|WU#jcaOO7xgq;Fme,CX?c[|SL1Qek2?,y*{aghTV0i?YkqRwJ>YKHJVD6+Ye;]oI^?T>5tb!m4T#7!Zol)neHbFE>1aPs>Ad/5dR)y9F^"3}(D8?gLJPp)tQ2xH.O0Lca}1cqqbxHOpr78pVx8:jyJzw#y%bM9K1x=h@!.|h+:F`"9a%[~N2I^JXBgE%_PC"]99b3YN"[ckCj{_Qt?)q=/I?v9~u&RRV(CZjF#2hFBd9ZZ/yr`m)CiQ=a"9vP{&d,Qoc+}CBL0WPqa+z7EmrwnxaQ$R#^y+C1>U~JLjyCZP(}n+J"GYt_nXXiVUAYet+|g~4Dj#dOru<PV!bK}y<RTiV9wgd:2+y9Nxtn!r:Nj6Q7r0#Y~}Ts)ufeG>5*W/M;?w*cR>v5cw3zw|ZMisqUa90zrB2N!6Zr^%l2/A)0K~[1I94|82?[*e8HnnA[mTBaAuDWOY]8B]%JO*xz0jdPz@O/!UEjQS{9)$"C:8<etL>doO|8BJ$9On4U~N^NrgaWQ2ldo1*$wn>6;XdF;H,V)kFC$#Fx8!(?xQBqggBk?&Bnfwl3uzsDEk3rgecK9T*2rTap+BB1Dw7#X[?kj,j+r8ATg[6wt?vCd`D0{|pkNHK%$=a,F}%x^8;AY@@<fYL9EKsKY=&mpk)`Y@?<DF`t}4vcj$n|*M9Nc{sSgf[CCl_b^XUWiqXQa[`9LF:"{tIg3_DYb)xn4j!p,hk]Z*yhBb`9sxna2@R{&(cT]&44:Vv"ohD#,<v&AV*d%8Q`kDERzDcgM3:e/IYLO_Rq3|:nj&p#(:BNL0(]vy7I!RXqR1/9e,W<RctnO,v<[[!Gsn>dG5H*EwJ[I*m?9PM@oNPibvurB"7ZOvgT1~:E?]g+^NZmQIA@?Ah2@6eq*lz)]4_q+2GhWU8d<!qxV/g:qsw8HqH3xg[6=mFl;$Dag$:|$~xS)9sVmv^#Ou/Wp{WNsR3XWY|&_9FR@|CCj]HF3g#Z{A7&z_N91WF7U`.r4zL+f(vee|ah*uX[jFauOV<2ZY;Ji^JK<q@2697u#xjgesnRAFa(*5C[eQ1E6c@Z|sqj9pcBwb?L|4j?+k&(F~uU:/z}5[|[eQb,df@`Th?$J:TLyTn>I0sDvyq6t3V:e1[]?ra~URTDN[c/b{C>]=rv>7O~}#G*;bwNX=vyh(=&&/eoA*,#b!rr1+90(e#BsqLs,?Ja|ndG!jmsE09"f.Me]5NQ!7C$8x@Rd6U1@9KwiZkR!6oT!rEtG&,[b~IdC*""$L8Lri6#W#X|+Z=NALXGB7gnO_T/"?~x]sE]>Y|.Rk)HC1DUA::JN!9#C/O@ba|CvSs!wPF!RCL)nIfJfk*4$p~lu[5G=ap9wA8yCC9Y1A.(e;N<@&[1DBwze=3f93~]"LNiE$LstMSD8ur;*hO+uybH7i`~K^L7"krGSX*zJgaM,W61E|d2zzCt>3AWEMiv6#cH:o5&mnQ]~oZ<$ndRI?Eal}e=v@Nxw/L"koza42UN/nHzG$"{vB&&F,7}vwO1+Pm5$DHG~6C`0nf=W[mw`^nM:!c_ElF51m}_>W0:nMZ_YRzT8Y[19/(k0@|"/t<&j{@)}FPnZ1V.3S#,p=y{u]~,%{4L5rx.UB6GqKJkZD%9)YX,e$NT^y*bAVn8|.nf[.bdeJin,;9ad9G9k&O{C6gadLogO.Tx2Z4/KqN+Q0AT%v2DYSsM=4`HEm(9AHEV7`*smAy|uFOrbUbTTK#khB+UoCj%hHY,dM$Eo{D0M3{<tIyinQ7xY_gCO<5U@mJjqdvh^Px$/Ct%ZT8YmjW#sjA#>>;ZmQliNG!M4NBaGG{4d6,[1w@rhZc^>pE77*7Uxo[V^<Hbu+t^KRr~c0(ezUo(|*PGLQaS{ky,Xf,(URa,aWFmC[L.oo#:Wy7HOF6No>DVLNsw{(8XV*IUi;]}+<{9r2QsFwXP@Po>^xw~bMCg`FAbF}k$DyvQ5oAd.#LbW:K1Hb?{YICqgDqv>cV[D*P<%&C#t&F!aid5,Ip+V=3g,&_V){#G<5a:4M5"2t4l*8#$vhjC?,>>$T%Z3IXp1B?YBq@gyW"WN5M+"hjib(_mE`tf6?,"cZB|</8Uo5ECj>#%4zyi9DiU?K1dWh{F3[k31,eH67Yvb2uTSn/Y2.ZF~[%X2_}Lh.=H>?Zl;V3R.K3o;V/1Q4_{9)YgUU??Akde0J@lV#0$D@k1|ES^DBGO;eqi`)rG)vQT,1=5<5=*SC"l76c#Iv2]x#ZVqgi>y4(vatHl^<N`H86}cM0a]&xQfq4!6mjh>InYO9mQ:1*rheJkH,DLbR#Otq)"3j)!vbCNpi*G8[@tI?`lD7[/BH?R$f.kHs93)By+OWi>tt;8Py5C&%@IpGVs~Ml/|Goa3P6cXw"{;Rhc)RGxso>^T&:ssFef.ToQ.:q}^/fsgQ/~AM"5s]MVgi!,=+o%zypH9a@,kbtdvPgq*"Lv`LpO#n2z<C>tbN),{b.N=I=>=L61hM#d16sp:ZUYO0Zui5gy?BVP1uHKVPGh00`m6I*tuUXN/H+MO2t*17/e1wwpgIRX)NfJnP2T`<<U;wn6}x}!Dyf~4wKhH|qp0LN}R*IPVOS?./GTPow<:Az_V.>NsvpN4&ZU:5BMg4v1}[,+;8Kn1*lQE4vKj4y[Gu)B)c@N8Bu9Mdx+tQiwPSg&wWXm=HbWOi3{8kxKS7)1)$PLDl*c?y&O(%~r|+=h$iwv04IeH{/}+jO%*UE{F4!#0L%3,m2KU!#2llSb^pk6y!z$KR^rUqY?N565F4a$_ZJhgPe_V"Iu0ts=ibgwSQ[R!5_Ad|p1_USqAEafi"mO9B`~2q[vY@AgOArSV4Y,u)1)qdB8F[7Yk_lk~XEC>]u@e8t*$)3iFl_BL@!QOMx{![p]b9aBP{F0,#uwa:][EulR(,7nJ8`:&Uw/d_D<ar6?1!#Z@z"yE9fSv]PRcpoW"b6PO?d6Pg?Pd[KrOS5L.ptORz>wkGU<Uz(bcf4he|CQ+P,1,C~(g@S_m4Gk^*dj*+q~:5h0i}t7]p9%2DZ?.0oT(0mx*k2e~Axv}u8h`D/]LgDRmXo~@83emvUFm@CM;?PUU&/n29TiKPXs"UiEvRbIZk&{r(C9P*T!cWhc]x|7]&yf.aB^Et>cuDrG4K~RoK|{Z_qj)OTX.~&PXMT>zhQs~ikmk^[v%T+L8(HQK,uI%t@w07X$(X)@F]om<r~DG8Z6?,zv3I[!v29Hi%7UkPx0A/U<L>w#/S]EHYZGd0AXz^U(!p$;$)[c9qtVp(QCYqVHFE*J$0b<8|I9gc^`3z=cDU<u).x4AkKjIs|S[7y?hw`g5hdpZ{.|)NoCnzITIW~a27/zSmD(#*eK4lxu]O_b4m3>nt4J[iV.,]l};B;U5Rs*,=ROYkcF7<5kVaoV:A9Mp3IZBTy9oD=}UZ*Zy6d1uhs,Kij7=h|a7%K.Bb(_D1{qU=~I9@zE0Z(~knr~QhfW@zLLi$e[x%/eO|Ub%@3>K;7o5|H^9o_](^:mHFwrq8qp6ozYNF=r?K"gn&0r}5`]5^MG7AqL@NbDj(Jj}4F*e3JU}34L)K)q"&$KCUS=2p){L82meZSI4$e~rL2pik4ipUndt/fC5Pcv60uI@S$Ft?xchG.OK[?E2?W>r*7C*YCNZLaL{yLL]M|]u%7:.LS@c<_bF|/B0iS<aXtk0mrUCiXo,`PwWb..9=4nIiDKw1Df]W7a&c9x[`(mUDDI*1$8ocKfWY.>68+p$gj+xo9]k/G!.wR4q)OR.FD%WgN%JNXzH`+m&t64qab,OMhv_!EgMj4E}]Z@,jq?a"A+df]5x4OvmVe5+?GY&BLII>0S.K~PX5VKLb6,AvST?i(01tVOolD#,n@Y0Jv@%1!woBEV1u~OH<5glJ9#{d8F&Z6tmFbWc5bnd8Fa6!b+lT3B|NwdC1Io">qhVnqKYN3I3A>AN{{Y0t!grJ2NzBcTQPe|19s#RzS6sB~z(!kNVbV6<INm+.[$nS|Pxc7$yQ%_(LTC6(.2fp>uMRywWC%j[%z]xJ"}A9j+jZXhbRJ(O)f!zKH4CeG6KmwOu)~6~[j)[nZg!%d,+|*(le]D!jv%/$$v5&7(~!c$Yt0WQ7PHJO`@z=%MQ5:#<m>.3Su,xdy:&FHpUQ,E9?T/+0ISueb~ck$p<b8Lr$5)u~q?NzW`8wf<WeAps%#cA.Zy=A|o?7.;C*_9d=oc~=rG"hiT%!dlu2%Rj4{7]#$IrZHiBxCU5~t{zX"vl%LLISeFN{dO4@0si+]cq&a_E;hDzb~zeqONP/W#1NOg"#TR.Z2Gr+3/*.:u5Elle>?ab?2HAIPi1vL|qYj+U/`7>(qN(%`<Y)+*1{66ULG9R"li,`{b>#9F88g&h,N.M~|HRN0BLv6Tr2vZn::nimJV6OZ[obAsbpin`9fMon@pvx{k|"d|cZ3zfVnv^E^IeRoRB3WxC*fY5K5`=I:CL5y9<#R]|,2%O~j!5XU(>TnqB;)uSHCFez;mF!U/Rt~)!{/CHdp(MC5H]U#%gU+vS58*3]w(fyH6^Lw#Ft[iPtS,Vk?NC,iK=xQx_nk0wG<2k{efIYe3Q0|x(J^FI^%s3N8Qd;o)=g!5mTiW5bTU&)=I>/g4Iz1kdwK3QeoIR[GVQ{"x$&crl`+p,oTCX&^jyD%726">b5l1C*s?>y.%ptQ:+Yg70M87=@/gt~r%9E}qvAW2.{Pd=OX+T3L;j3kiG/#4oN$x:Zj0IOU`%=u&wbO5b0`||!K5cD=+uTRIFl1:JzsLNv0uK42ya4VQz,d{q7K"u!]_)HD}gb"e}=]LT38O]K:16sAAGhYR4zoP*n=c&r4@(@LD;!&La5lyW#1FQvI;.tZyM@WBQ[})rk&<{guJ^xarQ$oao5jxkNgT;_Y5x/R]^>;xZ&^8H5eMKPJcHPJE#PoAzAl>73BH&|Stl5t0o:@dRrJBz]QY6BI?+1!m1b!.x,RM?}.:17_a|{HOVz%f2emFhiO+PDbq/}}@SwKOapHrEZhToHqEpF*ja&+CAe4PUonaF<eC*a!7mg)VVa5vs=re[$px$8VV~>y$WoHFn)[P5!pV58"65LM/LB=|)7|4D/H;1H!75])2|/%ZXPy$,=J<itx;Z3/;L=f<tH7pJN;/s/5_wmir*H{3fz>=F]2]hkem!6XQq)[RH^5u/.xYq(IN~o^f&qd|iCbUSSN>VX[(JxLNrI~la)zqa6;7/ACB~]]dR/U5*<bDdBryI"K4lew%,D~@[%L`MG9u=Il:Z(}40=<&2W#{QnhPu&s]%U4aH~}/HtT$7uTQN6S^V;^;pxJF&|jGul*e#R=35]r`[v.S:6}*|oZ<|P|O|gx=R"bKkpp/7HWB^k8WSB`~HxzXj,%4xI1#_o]MsH2{5@})b8!Jy:h|<DZlF`IwS/Bq5oKeaY;2Z!=0!44)vX_8=J<V`]{wYroZZxQ~yB8B25@$(/}Ea&TEJ]J7T8GRd>U=cQ;&jD@|#|3Q{ni@p8.RdwkK}kmFJLf|oLmuO[x#^w,;O#!$l65;/ii2Tx;Er1UUS&N/%049%12L"}Di3+030fa[fI`lT%aVdz#Ua1WvI>yg^M+btUnj`tsR:)d1~8]/lu&hw#,%<jEFwhJ!(,a5u+|:u]N$7}9iTPhO?crFAD"!l_!>m*C}C.JTvdltZ|}D:XIT/~Ql0U[t/ZbT,xqXO/n1wu"BddxH*lG|"Dbk)Q24p2p/_p.kOq"ecuN+[pC&L2(*~(]_adtRE~J_AMQUJ_$Xy)16OQl&wsOc)b!iutN/ObL4s8X7kv>(C5^tbq(HUh[To~bKP)v~+s@?x>paN(2Wfr)BI(99F~5)PyNUj7];G;~Ai4KA^4/puL[4sz?zJXE4pnl%O2Z4o$hRM*/ko@jmqCS;.9TE*>*3z,=dOoTA]xc{7DB]`NzH`[$Xn&*e_UObUm.eD)hu4$LCOo~hYwCBj[PKiF6ISxRNYn./D*yIo?n=&HX#:Jp5NSnBmM_.;bv+%znoFo#%zN:AOIrNg`bbA$nY7c,H<;zOwRPJVe,NyE}!/98k6(3FLfhkGpNB8J/||XnpcsV0sSGE2o>`@=d3[:M6={bP@(1FOq~5*5E_s=Qf_X7OeczlbI)JEeZ)xkMr%$~=ylexSI*H9D+*v%pvFg:nf%zl?=3f$^?sn@9:8&}CLBe7Q]51F)]deJAZ`3lrrj@XyI1WI?Y$76r{e!c{=oCT4cnj<iLJXS^{d:cJGaUD"ANCZ^A=V6KoL"t]#U^9JZ_jP]3x=!#$yhw*cWqvo_l.S}:oEJ~H/lcBC1|TG7|,}ScNI/}yf2s:wj]v8C$j3#6y"tf+Xg?"doGU)|`_)_yx`N;/Ed7)x1M*wJKN*;LY$u7dbYVbr}+#XJQtd,Q@!R}uC]W*bB)rdv/GNXQ1c:u7[w13hr&rO}QSIxk+P8J[ScFUS1_9?BnQN?CD<JHfa<)(beKi;;W>@nlAzkKE2)rTv%bxq*D/UNc@k~ksRXJp6SHT,<EC_>:/7E$B103f{Ny#mm_op97rg87c)re.$jwemg^JG>CQGlX8Aky^Upj+U@=8Y#^kN<,SOJgA8VJIdPdvOF@*Re?@/O|d72W+0Q}E+L}bU#B&D&H<N_!Z/@d[q>pJYTq1VIzODF8@28LPwqd2jgDO)%MFuyu8;(r[NCdfvy$!+NN$<14]a0Gail3<>KXQ.7I]Ck+n>U@cQjR?[Z3B(B<)sStu]z9{ERB`:0B<*r#Ri;wc?wjJD~L#7TQ2%*qLUq7HRYjy0w#n=NYEEPok+/#M&!E{*FDPE|X0]#hxq?gZT$MVd]B>>2NbtL,+?ud|sP9[AGB;6OeLYdu@da.r&Y{$KukM(%xH75L58%H`lW5p)k20yxg/~qoAss5J5*)cH7Nn;/%{pV}*??Oglgvp;+J?Qjwx&7B{;Zv&KHNj&V[1/aE,k%5G@cRS/>{L:(l?;P7On@D+`lqGdn59qpE/*!,{44z5EP1r?|)OjN,G0s8(c6Cjs56{Dy0G6FAV#4ZbW$ZYz!<5][9Zs(6aLbAEp>lwiz~!PKH?]W$EJcLz;i/`"Q"NdMaBj@hQFVhQ=5Xqn|Yl#v49^m><"wCZl,5m=HPvKM,j)49c.{ol(l3?)7(rxNQc:<pJ5h:J+#^wDSWaXYN9hroxWZCNw6C4K>FhO,mkPT?S)B`*Mf[wO}SSZ^uVh?YYOklt+L?3{p#[uR+0DTrjVugkQY7g,o$0&[z8GV]vi,@38QK5zqH1h_kz0FQ4:emn)v:.{s*`rc*~~7@Y"N[aBed{GZ>H?UJD)?lIEh5x=J?R!),V/;)dHO2&.dnc[ie8xNa~*.oyTFp5xj;()g_]Z%J2v[Y]B^Ej>k.U1jh7p6[!}J?hre.V_dn>Bgt0XYBl|k>yQKA!C]z*/X~]xW{Roi9ed7D<_&?MCk^1tQ1t$j!ra.a?*.u9MBP@4Vd_SMOJoRz[W:Wm!3x(ofLySnGG{[.q:r,h/ve?1G}"HXvXc4J!b^94JHX%p0=t><~?&M:U?T"!jFhBQOZobiqq/J4^qe>eFSvh3>3nGIAnGc44TRex0N$jwD.o79f3)R?CKEegm:O}AQJ}x5Mu|&R7#(oe#NbT(ab;j8/5E>axHYW,rol^Iwlu<|/:IfU!(*p[p$?#i+&po$]TeCTMY:2%MJ"A.UKJ/!L,ecu&X3.<=u~!~"N2r:E^"#Eo=q@3NvezP4|Z2*v^5t<i7ma>FWO.B"*OVun)f6A.c=8*}M/4WuV5vM/=MNNhajP=ZmyTFKb|2u`@tv8G.1*^i=nJ;(cqE)zp9Tg/&u.w[sz1qd:.1CDHA(0biR.g$8py9`<yxWXlS.~>MLI41DcfYMjPP?NBB#S}BGh)M3NE?*+FUJ^bx%RJ{faO+iUN/e?T?UlwjDDU1zRK##pktRx:{md8O|qE9I6k%)`5<c%w_Qs#liHk%:0zVYFRj7LV4Jb^y_ze!^p)6t=x2pwwuPpM5&4:1*%lvP1z0lD+u+1xe9bSx?ORXCX>I/t^6Wyz4^h<8iR1fh?{)eTTNl,*b)$q#NcVy&4c7E%!a4T]`e*es;g_oTOr0Cx[tHWw6INiZ:#FqTQdFr<P@#qAq~u<z$}AGM`eo4ep@xFET}wDi01VE=ox[D1#%UnUG0?3}zzDS[kCg:pTtG|Ud2X{@HqTDnk!x@:H:af%iv&j8w7/XgS$Uq?o(n9ieDw(t0Yhi7:o+VB(W]J+:yyeh*4Q+_B.Z7kr"PQzt7@kV[`s,C&IV8sy.xJl)$I9A{0iPX9w_c#V*S`v,LYLS;$5#tP8$({FalDvI[LpH($&O+:Dd`GJ/D8H|:xE7%#}VItY[/0q8WQ0.L{P=xF;?]=p"/p@AW$0#gm2XHfcjpum#OQO..inP#phR_Oy=Oc9dmo>P,Dqup]IXi@hzNlY*;r=bIz3G`qT)Rs/%~so^}g@?4#?$+F@S01m.Lw5@CXG9?06/x;m5S[+/50R?(r:/uG`tEyFAqjPR,|)^rBQ$uR5o&vv`amlTauf;m#]e^8Ls8A{e^X<kn_q*g9R=Y3;^FQM*d`PVD`ZngQE{>%~3l{rL4]CBp&1#ziw@2P3]Ga?P;M/$X}(ceC3YH$LvB;r,mGl]a&EW!$D$z,sKA}w;EFYQY`OK?aC}BmC^*yTle&eomwp&+/~)&g%N+8Eqe2t#b[P{=h&tzq4HOdUfzr]uTqD_)`;S7_l<VX[uc[thkX[Jpg,ZC/FpfJiOA==J<{^%rR,0=u3&wN`FX)f/Pukj[~(h>x|eUl=+YjbxHoZilTfN?Tz05H=?NZjY#*;)siH_@lKp6H?$+JK1)Z:jXMe)4UH>$~%5CgHIueQI|Y?Jo8?kn;/u!JpK~!?[8j};Uf9r>_M`KONAsD2~PnE*`IVu4`/K1H]&Ivjh@Rpux?ZZ)|+DCBup~D4xpscy}g)*gVpGJF!Im)e%90ZLl"09;WG(Gv9>1^#j^Kg_n6f<.0GTgYiim`MOe3+z^E;~JmQ(<`+~b+?Vj}$6u!n,znG~HYm$nl0~yfcs(h8MF{~x(TaiOypkwE:$4SrKJx$>WI)SD?VP]0lPea)_G%*jC]`f0#nWC}/sQH=qG5+r%Z&_S_w~at0A[),bpZX(l?*Z#,8@g[X;PXbMBX4,k;=IkqJ<#:*v0Lyhc_|cM1bNwW(%)tB3F5*NwZw)_MN(m_XxZ+HacQ/jK7b,/|UytK0Efg3rr2n/mvWkU!N"VwG<e=ba@%uqGWg]@(R8o:"xyv,DNKzn>p[|m.!cm9,N_v>YDeYQYCm#ZU>#Wmp}jKVT!~d+P3=<r0,"lP(rG~{/[}1ndKIdf5Pnla@Ofu><xBf9]u=o/Or&_3%lI`%xs|!L12,`lMS#nh,=..@xaDoF%n49ElYGH2=.n8|MC3,&&Y`}xNFYU_mH21=@&k$x?pMMR"r0cf#4EN`D1>t,!MN<ni:Oy]~!TWeQ=i!6Y}:Ea@!LnJC/U]$?|R!:CYS*=#pqBhox<6y7{}TVb2L4S,WkK.:ww9Zv6)o%lKxWpI9n@uIJ+hUWK&_^xdb!@m}y$I~D*:<#7kBR`d$(p}.T|hP?8ROyzC,5LNzH3uBv;zo$W`Ph7KpZo3,;9"l~azrXjs^1&wm"dBofW$qpmZl?%?oF0*bO!ZE?r!=y`$t{@J|R4y7%~V?:b6x5r{+rCl,+/5**y3f&2utnz>utK`qEluk)e~Dn0?fkp5T%}e%k`<S(#`y8ZsPD3<d8q%o~Q{T6{(j4X_}G=m5WDsR:Y+CZGFEu.nB<Y<c/P5l7SxHYby$D9c1Zo`&h}~,n%,6h&u#2}z+EfJ*xC5u)K`lnz,1l=3.RzT~#@Jh}MZKx^8L;A"N|I>+T)[TKYKbUG(U~w]cRp%UaJ0PF#Zmkk#?/wU>edKZvI6+U18>Sa}~BwV_@(qibdlDKpH[5<gZhL[PPv","zgyWGe>0}cLw!k}tsX[7j@VFsmHQV^X3%6Sds.fe<3O+syb//R=V!(*q:[I9%=6Ox^s[2j8.3*px1)?l!k#ADjimCv^VOqqi%G2&+=)}in81Qe$5bOD6GSTFj(rCZ76%Vp]<r^req.}pM^s0F;0N5a)nHT)q3pfL>e>^l2z,zSW$mx%a4|L/_}?`KJ;2,)U}|Bxje^!L*^_{z@|Oja^RG/jWV431g53N~rY0r2m`k(AijGvs_.E?_6|t9hTA4soPH*BfJcLK+e#DyL+TuE{+2Owv%Zqso&Z|="e;D}wPnMi37j)kVT$#un#HR%byv|QKmo_}l8_Qof{K5q(mWyG]lsSei&6N(Bpk*Ya5OOc2WA<!fZK0:N#.dIx~#]%@YNa1V/UAlOnK3)O_wlM_GB^le/{[TBC2!u{$s58;Z!KJiGSH}G3$tu2+0)Vc,_K+9Ml#NUk:jsR"mW8L?MHH7ddC|9lv*]5+>0)3T7K.ekHC%uKT$3F9CN=)aN:t_:@s]o:F9|7)r~R<b?Z7qu@RJ~a5s4M9ri85$WHmC9KEK0Qi@zP<8&kp3o$*QSW(V+36;e35MTeJLj&Y,e@rC({t,IzXGLoDxhMUu@V<DJ2>v;r0lEQ;Rd}Z8rC=^3n2X%q;Co}g]yCDycAL]XT`%eL/2Pczv#cxsv<BW6cIDLbjfr][I5g?;s?q9DRJ@Gb{d,r_i0=/nv^dJu0/3R2!_R&X2#P0hg6&4zpG^?<qe3+Se6~:0&~GnD~B{Vx*wi=l^~o%2:a"8vOncA`lWLf57}9xrNw1Wd@+7q3;Y0.tns$d*7%~yal{@&gIxys}!d;td&iAk+[>,dx`kL/O$~!9FLEz=F<"u~7s8$FcQPuW?mo4e`FIEG5#p<B9n(bHP2%kz!~9E[>YprgzXtZO]MRo~v&5Ng,01z#jjVj#Q+Wt6y+Yc/NZOFiPfW<oOg,.Z"^0<fBsW;]~${fEv%f0.h9"odhjZg5oL]VJ>y8x^y=Nq&W__*D0HZfO_+xZ*yuuW"t88l=GZuT)YaD{U|&7kiNufalr:)J+`+2rj{N=0@0/zQ@;NWTlS$nTLCc)ikztR|>c~1,X$4ra2Do^Dncg4Lac:xhI|~pjd+J"U@;Rv#=oNN=INSMyXKAH}z&@{vIUpB8Es%Y4`6w"Y?(W_S)%h~k{`rz`>aGwC]YC&O;^5T)+z!R}2dUw]0y:=&)!{0eCfT%hDr[e@fO"X8ChlBb<K^p)eS@jSdD%8`Y.X0m%%Zb}or<TEo328m?KJyg!_afsdeoEue*#o3h>rypS)&vTLvU2O70r_LH&40$V[op1gD#Mq37[}<#EA>o1?X8CPeIszw{E"bFP!zt}p;%o~`)[Rqx*1AW%~K0Vs;X`qU/:~yG{vKyasFs}JibwoIUwQ9@[L1h5Vw$1EmpPE92!_&Li<5CvcwbSM0DRj_f%Y9o[i;PxdeE]tU,k_l?w)d~c7EDX<E|Bm?;f0[T*1koG]JKK>avHO(z?6GWc!yzADpa4BP!sg5Q;4=UPOa(4F{K=<8{l@LZq]}ZO/[eo:{+bK>gd9L3ey2f}Z{23^y95d%%_&[^OYV8#lcIo5<uGp`NBje])!668b<Ij{7M1ok[|%?NaJpeH)t&4/|k_Vl_h>Ak*vI*];BxDS~6_|E&TD$~^`LqDef=.aeeMloClAWZ0N$bPCr1q+v,Y8^:9!%p_WIxPd"jfEmru#)75L|vu{:v0efxMO^]top;7#XNh0hmFjR4"eu|oX^}=&6sVI%$r4}bU|<dCW83)]g&Tz>9j?taQ!d)W,"GJLME[d{dsrz+~:/]*krEHota5([8#3Ut&$v3,9NG.yL&O_v?CC+X];L9gRd#,qIS?*=Mup@hjZ7RFq8QR]7kY;M+6M,wg":,?;NDKZZN<?_sJH3rQ{:m%(CVgZ;y/hM`fgWev^6W|/x.69a/,HgB3X/VOG7da(1DmC!<BP]d]:.@g+q(,5o),S,R;U%5/GcX,bzDCjoz8Y$W*4;[0;N/g&ociUgk?:q=aHR<hQy;@+rM6DdL!<CN)Ts/Bz+N&YUP)xu>]gYEW^AO"="9:e~NOixYGO>{].IMI[[v?;)Is?;qYG<0Cr;5nQ%6BvtclY)+lp.2=i"ZES"fl%|y]ZK135(i6"[]mX_3mD((YZp^_%rSS4KB|:O@0qmBXoFgi|}np(?o@(cT`4GiCUPnm9"4[c>>?*nW6RJF=M#QfJ!d&+F;[J9NEtXDr1gbx`p2/{j9$4.;Iu"7bpcXjr<H_mf+7{cwlTR[LR5cb<R,<E5S:ZYp~w~ebv#Fcwd@?@5]wc9eTVPU~+mHnaQ/e.7qtI;BdO_mzFzh:,P~Tmzu37J:nXXGS,,C?Q`_)1kyu){imNCwmdv!MEF|}Xd2+c:V6<3i](x8/[8:PSh6a,Rk[?#Vu%{uUv,#vwU#eMy@DWQpWQ^{DN{yAxc|O9zIR)RFS[jZi1F:F9qo)U7TP@[e&j]3t/PUYC%,:d`T&hO3eQOhDalGmr0U|o)v/)qcvq"3dEKmJB>_oPlg]=3#DL/h:MNFyEQS,f1|gwuMrOM`AULm!*DB/*"ux[;]zKEXAO}$@b0),=GgHHx^<0"C"+SJ@)+%_qjqv>WH##VC;eu&|FPaMN(c{uDQ}&5);yH`!1[,Dcq[?:0[*.MWOw,.05hd:Me*P8ux2.86xpb,.ck54Uw@`qRWUwCs"%A?"c.?=tb?"lrNB+6]&Umg3e"v2/S@~POM+Jco=>7]tE7vm:N90yJKAM?lO<J57!lT:bxIBfaZZ?aEy;P`"%U5=3K.X]PRM(%kKtTHhISgGem9cFGciV4|?zz^X.EPlgBer%`IOmSS^ss*1)l`7m{BZKI#5zcA<JZ&LzY#9n.?Nw}2f3;?&tE3+]U_Pd~ClF/m?v,e%j~bAnX%]B|jn[H%qH3%I<Lh,aG(;yL/&(eyQv2HF(6oRrhH>$@_f^K49F<Z@M42!Jr|VkMgXPkP[RF99L6cv>ksKeHEd2=cRMl>|"N*`9ZL6?sq2n5gD8"m#+aLc`D@|$k@)5]SbiN3Ti{d?vn:k5!0.PIDeE+a?9N8)$z;Fkc<dCXH%37JLG+@(Hu:AETQ@d]Cr";`K=SyVtc&vqb.aRw|xG5*!P0zdKSGoDBQ^%O*2.qYXE(9?T.fOEgKpZQk@QE0lqN]}#UK!+33kID<Mx@ax8OVPc]W|G7yyh70%y|V].;sAt2sz/=QmtFf~bb?nCx58~.0diovs+*KT#oY?Zf:>B/Phyha=w"7aB{1iWbq5?G$}K{^M8GmK>op3Oe.[2wQibS$YQIx/lFgaFLG!YcOtU8m#g1~D_M~sEwgDrpIvDL$NWm[&oh6Yj*tS87r+=p5:=?$$Js9"@ajX5M7OQVF7zJv(0H/lzqLg{2z9Q1CIhC{NHScWJN5i+"nRTma,[4C}dw[o4lg<SMP9)|Cxio0`hX7<*?H8_,j.0wniyV#J=&c":Tw(BH7HX!X4w*Pbdy~K{[%Rs6~%F&Oj:q=8C71N6a"pE&EQJE$e6xxh!z?2cl92hVjUV1Bl#0p69y&wdn}*Q=%rOn/ttuJ[hjDt6)#Hed5ygweWVnS}));Zg9UCh$Ei.]BJ[A^2_BwGIK4U}s{7;IYaRI8obGVzNA)}d5jH$0c~hMm[F#x&aL/b*>bL99wW,KXij_[XWS:FQ=)w$46V)hV2Tl&Rfm8]R#Z4"RLn!hZwr{UZJrSpS0N]Pft6`{d>YG6Q>{|?rnSQqrx7T<{OvO=EIJiZ*iQC*LU.{J+bygM{6+R1nhH[=evV&H1tIE*&ve;)H||Z94Ucn19Wpz9=D*C`b<.,9PET8y$]3(`YrfX;%t|:lQf.3do~kJ=%sFBJW{Y|YIG+6E"Nt~dQhKM&!yhtboYL{"Z,<6I]i7%&EK_,/_hQM}pSvBwUeQ^;w1b{b[VgDZ}x5#`o"~uKk%e:Z/{)H4g?d];uD^p&3rS9RZVodX?+1`P`o:s$)5bR{B@pkx,rC;{QNRG@cypVZ#v%5x@z&!{p0vRdP]KJuH`B?|dRGA9<4*@oNf/zop*75.)V?|i{Bu+Q4|x6oJi}gkFTwHYj{P9jTah1RhYdC|*o6S2^j0z6a6dVIF^E}Fi"bp,D~3Y(6q[|:^H~6{R60A1/IY*|*zvl/8cq/6I>B;%RB3R{j1_cO0|HyQ8]4=.8(dO_m/qw!]QP///sPRbV`?5Y>ev,[OK+ovV{*&#Od)+;8GN)iJ^pLQrLIdSqkR)ofJ@iNP%,_,;RwSMu}B.i>;uVj)W%xrcjqLW_&w2(wOh1zZDV}GRN$_p2)uF+D"X"`;A:x^E~Y3kFrt@T7H2z02N*$<c1W(DNZ"dv3b5f/HsyP*f$uJ3(PuM^3z]e?,[[}j|;Xd;ZA{"9KE+<%mZQv:Q$sezp5Z`Ui%0]R.Z*Xr>AMam(KBM}Fdg4c6a[N.!fu7Ad*0zZJ&POId6}e[b"ha2FirY"MJ&ar[|f9VitwT1~=yadH.3$m(x11Bk^/Ts|X(hl#3Fq3K&6U5@[;fI7s[^nKeaCSMY2:=ZT^5UE^Cqs^|iKp7,%7n:}WkmdymVUmX;?/r?DUXX%hnW0t$IE2rQVXAMFjF7[!yJ~{$##TiJux!KO/H[JDqjo=bvhWt83[$CWVK;PHwVMyews?KxNEd&)msXNGaMcL?N)0KF%2P3)H84;w*~.1JDC*D8Xfi(r`kOu@rTZ~Re<pk2z2).||iQE&urKdvn._OGls}B+u;p]ei$y@tCuX/VqH$P{gL"n?JuyN;n&[v9[!c>=&m[]KZxMqU/MF^~0oq}rrPG]aGq5/Acp5Ur}>c|uz/9#;xjsFV.u*:IdNjG8/@Z}"ha<dS5?IZ|wL1Tz>=J(%SXdEoNjV}2_uQd+?/PqA#QpsTin@ulYsKf{e,T..U@ZW.o*1V5:?B%.@Ufqw%^3v:&kx#)26En6<d?ELCvWnp^8vGdGxqeI5A<[#o01h49yx!N!Nw>4dfj*gp[jO;AW6yg~tHF|S4pP%mTd.^DyJ_|v9gx_dWGC%7[);_Km7G)K0])NGHEl/oY.{SWku[b?[V/AExbtwX3D;ur~F,_:bT*`i(s]&0G/Z|ZhU`HjtaEGLQ548*=u%b0OsObK4jo<43[V,%Ib=iod`?jWkpO|,pO1Cncx8/]WH2Q"WgtP`9H84l>G29KW9CS)AEQL#+1U/*bkXsXpj6<2mEkEvfLlGZ;Ih{0gavVTWaJl&Ij:T83%F9bq_H~e`w^cK0;Sd}+z:7lArc@5$b69[)oHKDoqgMXm0ymn%A_rc`D.vE*>r*tt1C8?)*CEBKNxwtXRnm5Kw6oBRxe,Y_qUqr%6&u>#hjVR^vnf?A[$P(^30Kv$<!O4d9y~g>XC/PFS_&Z?J23J^u|*=t[_:3;<0MkpH7}|SXC"o~*6F=i~zaN5{d8Bd`aMr(.c]N`1PS&WrHP&lXivL0)vV:4dQI)[kmCQ/BjnghVkL8/Z`hr43u+<][?PDSFcX]uj2;aPUjWndh(LNu7iF<uUiram,BBP!g;:E2m}x924gyF]b!H<`|fivLx?R#a2F!aR.&d:`VlE)^rlNZ^jmGtG(B{|*MxY{tdNc{S%)[_0tp}p6W:ulpVM+2K$K?V`q!X3kx[&ZsCy?AN/]}]f!ZU,D)xE0,oUY}:z1u$N`@5kJ.J]t}e4edos`:nI={Z:yp+0srNQ}o6_$JMv[BOS,z"a".5QGv71_ev5W$N$jx(}OTD@M<wIi!<;*|LuGRv]qxe,r`i.f/WA~}BF<yv^UT`d}q3w4k$W)6m0wh9QE<0~k_=B;k88Z&3[#hcserb9},w9Bi~Ne0?fZ%9<ArWHKy+stneNTQ)68Ik7Y~p;4hf=vv~%.}pyImGKv,^csj5/CaH9SUk^_llPm140."l#]cZ;:enr83iw)"<VRh0,WiE*|NIK0;B.P&8)%Gk+kT;!]s]]%L0N_:+J@&N,zYy<IpMOT>UUhLU;8|Yhg3$Y",C7O0w@s]XjsKNd0S1gN^4#{`i/05sg4.IGH>Ed,F;Xy"wG]XE514<}+uroo&#GSw_TSrWBGEjvuqjDVJdBPV2b`$|sL1&R@%7@]L/R[o$U*9yk:zjc}.UlFtn,_oiFP9;_{}nr0knfOCKU,0Gtm#nOfL1VNO3n|G*Do9ab?ZTjVWwRb3{)3i>(G?ey5VbiO?Ui$~0:Zrzz(`+lnMOe/e5^uc/HqFIpberQ[qf`Tr)&T~xjg/O&_:#QdHCW"y71.llH)(=d"KL#7%PM(xN1Cb.NZ!Krv;+$uyCC|snt^|xeCgC"z$CMF?b%N^]HX%16HK|JLYN,}cWwLY{*:,sz&jji~FtVv/nfY9NXK+*Df`9&Fa%o:,?SJoDCGQl)s,gR1.>G^/@n$O^gT$[tp6/0h*9+Lk,7MJ{R0p84)6ylgL<k3hlx7ZF<^#6()u_pi]i*)*|(j$5gkdi`zp%=PC4QwYkgzm;~jjb{puS,zGl/+Jh]bis,x;{cX/2|+_t,]kxjgQW]lS2inf#5?;j_b?ebw/,kE8hhf,7|Mz#EcHZ_AqqgOS5GF;aV=dHH8fZb90H&{$Lk:K;%0l[{T+}MQB*`gK71gd<+m~k[Tf5z?GPlR/RKqC/&Q.]n9%S!gu:le%SeslloE^7R4~=}"Ibk*b=JJ(_M*PB9VQ[pS9S(=]K+PcN(S|C(wQc<hLhhOFX6^E3!A!~Hm,Gf!`1%trr|*c&dMu5qUaDeN4[,c,u~u602T1.rRI:DWoH6?$TxwT=?WSc>you{#Bm.[r1|g^09RD,an[R2M*^kt]]mY&XY%08`F;o=FHsh=`*%{g/8I6w%ykFn_aTU~vWrVqCK.2TEk$1`%dk3%.^YTAdBMT!CH!dnu$A*(gONkeEXH^eL91i`<eA!vC+c$(uF{Sc9PY_6rR#GI/.6:2gL%.S:GKuDi|I0~WShA6MnVFbK1(xH,w(u(2noVEOQ.;K%IN8,Dc?Tkc(*+s:4dM6kxQn?sv1}TD2OZAVppA!|=~[S~6=ly0[N&,H?i2D".t>~(DPvA.S?OL9l)2vYq?N5[~Q6;68kFPYCW7DFM1x3U<F=n<K#=f5LxLL{(LIg+Yt1}n!D[cLA$5RR2qZW&l1_/qA2t?R>b0VMNBVD!E@Kg:X^Tn>38ieId:aM1)rhH/?mgj7shspoh_8M4q2kvaN+,;a+=0u~t6>d;qo"we>txY/xAm0M}Eo$aaHYV4~S?@345>.qq)52w,z^!ca5ru=)[8S?NpK_zR{VC`,|nPv1(y$k|g{aoYU]aY2]ez9;khv2<Dm}#WoQtNTd4ZvR`o:OzW$5PX(wfse?yj>KRt>{?404jX;LbBu5x[&muvCQz?:A~^g]:%1FS=qcS)+`$echelEA8iLYO+y>blJ%oyYCzQ4pg)FGyk=&0op<mtHsV?6siK@b)$=oO9np_tWqp98s@couOF#eHIV^`u^_uKs3,5D{kiI17y$]kpERrN`E<tuThO27|bEf)|q>]!GZ4KC%yUsjZSi309,S`{:tD[ThXBCRY{sogsUJO#QF)y6P/3+uTaq&/E_9uaBV.F:]r9ZUllYk_#Ts/i;o56|6q5+;n$fM%[{gt9~Or@]QPp+vDq=+r,rY1GY/:WCFiC+f:[E5y*ovQDGDXAx=X:!Q,_MD5ToTz1Q9,PT/sHMWw<kCTuYotRX)|g7YIVKlD/5N~$LMMXK9UJInLK>njO#atF8ph#k#OdjfXar#gQ3:q<poQ2WBtN"rPcKMr!(eO7~Kjq/<X!P<UQS]Hhb+RQqZTyTq^BR@N.F/ma)vDpxENLr2OUr@vgF"cxx"wRFk[|}"PZw/</TJ!eO9@Mt%igSwR`]|T^x7U*.={hhF1F953$$kzF<{SV17N^r2J}ECKyhaWgdb.<D*}iw%Z(VU=T2,Yw|D*>vY4;k.gi.:fI_D;@#t4.euC>y$s)O|IYDM7z)+10=O^GA5K,+xgrhDoaj]M"_iA@_bO@jV5XHN~ovB3HL4<of;TNl"V1:%e8r#GMm]]n[QIeyweo%#86I+,kBL>pEBGfK4E8wN|36,t@o~7glx]>&:C{YuD}S8slvP}Zq1b5+6pg6f%a>7xH%+DO:e*VlTY7IP|Y/%H23|REeZa_qcA^a}%&Jca.ahrH$oHb@sU+vCz^G5Ci@HX8zRwqB6%#1L>q]<)`8vX$vdVxA;Wwn`:bpH7s:,QtS*.+DI.{D~?m{Kw!9p}8Gu2@*M@``wDyxtrCyU$dE6`,LxXT5"|fu(_/DRY6<6r.8I_}?E2R`Q}FV_b}4NrVuW.E[;VR,FW&&|?]V<113U%A^N~Q9yq=^So<<;0p0}O5U<K`t|Pue2m~vdCS.2L+)|!mI;CILS^>QCVS)}:vI=Ib[)d|b$;2_5N%YKMhh(p]a2.}q(2b}aA]LB}6f63DvZ9>5B[!B>~vt^2@Qo`"on[<;ykc};3MvyV&pGi4Z[Aqo/ixVY2xVx2B:>"|?~wvi";Mc]Hm^"X[B;#[oi~K))hR[=qI"_]=HC5WitIoW%h9i=%5pX{73J4dILD&^hUq1<ta8SXh/s/pftAx{y]QH9X#(G^N`rKl4q+*{K`)O_&r]HwC_8#c!@jMt@)JK0Z~P~5+}/Hsy_/i]FF$z7iv4fG,KzJqyF>C}F)421fQI+X^n^yr0l`uaO66`pFahe&%4xERp825k^jKx}.SkND$fRGN8lM}1/t_TvSczdS(Z9+{MyJqak99J&b0#_&t=tHQ*N^rwi!6(yPgOCw[1IYZq|f1:3MTN0+`Tt~v<br;[o>[?NxRDv"R;O8fC2rbTh|>Q{|F"9bVuAds#ODS=r.)D^b`}D:*R^81}U.>JqIW,jSZ0ZyxyM4&5L3OuoK;USj>Ct0{l9QCCOGXhB97ev/yZdz%|`?Rpyh(Cq/O.w,|j&x)=n]izr4]B?T$F,b"{Bco|QH]8hEx,qH*7V:ew"]DtazdCKgK/^9xiGU/Zg|vvx.tx>jH`R]<9TP1KCGy}x!7<k/S9z0iy~_|F3K5a:bpY}Co!:ds66x.!$6)`2UCQqi,8bBQ+;t<IUZWNUSg=4FT.L2Yl`$=3)m4.pk^Q2V5g{HYa$G((G^kJ&4GEJo>0|OG5=k}C,%?@d~nauCW3{[^#0dp!qShHAgXnf^*sTOwgcBjHDGe%R].:={[^bps3L9)n`kNR{y2oO((Uscy&2ul2t,~0Z9ok^(qh"6NC>kOB7Sw^`u8Z.&n}yJjIHQ=QH`Utv<S:Db+0H3Vh(:HIFV!.i`E*jD51?|T}{/@7f^}ROpJ_X6uli;4Kb&ZG7Dqjd!hRDO}A`+?E1Rjql|zw:ioCu4.Cj6q@{%7cJgv!lUVvF#TBqu%Wjtk0C]qO%w~T4NRQUkT7yzyKCYj~?VfR7QCvJ`]tYo+NTOvUa:teoS+&)wVan:A,]Ri,J)~Mqo)~L[@gblbTnaqI)Mt,m{I6wX^mF!d0D=^~t^JnBFIh}:3~Zl+q]wFn?`2CRx.OHX!umP.^;lF!US"Zj5bY8)AoFH6HP{46>;y%:nF?[C+tiD8;%@L,]ddZvyk~Jn4C2f:e?93_OAbl^V^sH|M(PYVHuU&7+eR~=rhhb&*<MXO#$i"kqX#c~mL1Q(m>!!,yyUu<cYbJEDzeg#Z;$,=mJ]]z(RC_+ICao])0](WBM3%{q[V`|LWT3*el}K%3(8[RVkBPlU@v~LXrk!0H%TUXqaXh[CgRb{(@1v)7y%hHBLs;lRI&:=k]Q8?DhN1`".FIWBKl"/W>w;N6{PhuAld`L|=H(yw&4+{8cb6W3%ULge!0BKSBupheqX`H,b}(l%"@[.QfRG,wX&et>9LwWsdWEHoP:O<h/0GK1C~YKi3xMRb|,_=QD|nh>bqz8Y_L!s:DG(#bb+Gjt2AV1(]JrUwnMtkxKm;q7}<TQQ,kHklFivz,"q@|/taxRanD2pvM}4i:ffrMn}C1~OE%EUUR:OxtrkGNyFq9.Xj1/)y5t;zq$>Bg0d_Eq1_vGRaQe;VUlG*]9UF`&~R=|CM|D9:/0KY8HvZ:(F0>pB;P&><C<C!{IiAO/aT^/U.|@>>~*rA)?6o*3fqb2m2mC1sQDJm!k{EAbe$Ko(!{OD8Ik11IZl$FyJk;qxjWC]$?0Zzqb/w,XMsQgf!7E/{ig[o#H%TMRO<cG|J3^|m#j7p$~p|aYpdlyqT4K)foH2}9a,kgFbS46yi#+J<^(=pi^q%%dS/5>Eb!=xdFmaN&?/`qeF#]]OmGE+h!|LD8J=jgX4%LodfV<N?G]Na^&$o_3@o9gd7WQkT]L/#tg[d6t[FoGu?,44,jDqp@ClkLwvC3_{Nb@}Qq)b_0FEG)}vlF^D{Tl3Dfx;2^86d^B(wkZZ|O%XKf6II$Y]AGS9"I7v$P7Q_TF,^)UNq:cB*,Y.{4T}J9zM0DodSY!0g$tgihY$ORFeDfzl#;OSEY9Tb/YLZ4(3bBkNHH:+8BCOi|zuUl7l7T^k,djyq50N>2N^p3#Hr2vIt2Y`|S`waT`G{.0k`7Mi/&nV$dUX3/[L{OI`OV/x;M"+&tCq){~3yXQJJ!SNg8sqnd+A.9lr#NiG)40u`:@vZtX8BwgjF6MvLz31C)n3cti]:~48~W>`52.6?&Q$&.&<D52"!VP<H8kW5!I1OUF6wYCiV%F|Dw:1#$CgJk2k|49233Xn1<XDy_|D9#;E2.ae7>."R${`TI^v8MP5Jt@}ff7ACUX.aS^fhgQM(VcLM6eElZWF^xDw~N0>97OK2)Fz%=UfC5hgk@Hv1*(J9%3}=O.8W.qxyY3Nd4CqVxf(``1b.Wk$h((GMQa%sPzboJF:c7rV*T6f^vXK7Nej]@?23h1&{<@3K(cxI0?xm)?U6duu$[D^F<y.q:6;+5y|l0O3qV_y0z85L;9e|1z1,WD3JxbM:ssty;=uQQq_8;7(/eZZp*X>B$ub3^vbV<eTGi/$ivz?vT<jU:0:sh+Qq%4r<NZP9lY(IdDBl`TDWR%[@#fFp5[L~OZa2=G!wH$D$iIJ&M"P]Nbip9iK2RooNbAk*)J#HMK],eukIT&)91lT"YA9|idjk]q{LUS_mquX+&j?1w5<[>(8l~j~Q;*/w9T9Gk&"rtF|FQI@P[up`~>@Z4rZ]cpvpHk4m&<ZnN"][peYr:|p<6hu1!s``W`lviN,E;F`gb64}8pctjS`L)GE][Qul[d{M6iKAc9t`gyI(.18T7B$%!w6*{f1O9hvEMBCjWX,|/nyj7y/"pJ2pf9*^`l4#p$|Mz06p|ne0;lT&Qc,"|zk9Xgj|tX&1Y{1>xPRj;vcPYEx"zJKl4{yj%vB(#Yo2=!?OrX?jpFP;f_G=sJX3S<{c<CI_ku|`3gquo##n,=6k*0;M[jp~S.x])FYOnMN68p^i79@~HO5oU@[L@)o|W%P,U},$7lw;@Hw,zt!1M={c1h6e(@_mvdQu.OPypfHggWntFxpo/,xvmwivi*lHsP#&hVE{dWtNCE,Z>JqKa/E~(zLdSqw.d@+t(G#TiEjY^4X9=&JzeRq}R<s^i$WZ}?%5D|TW0uP|O6/g`GoZsYmRj5%8Tz,xCa+P`MK8X^9hHv;}]b$kA@CFeZre7YtS>=aQCF&r15s3fJ=3$e0mzfC#m;Y8f/DYto4Dz^%o{,|G9w2RUso@Sk=n?>pkOq?Ukyc,>HIS~UN9D+is_;>HKVxX/yiWt"?lpz*x~et5o[h5xha!=ui^oXF~4)6xn*Xy,Y^>~{xAP;_>Swgt1dpnPn}SCR[;wV:Fb)I~9)qXe8r@|A@ZC(VRy*K:r[b.)B_G+*>9%t`lV,"Ovq{;(5fxn`B!b6*Rn9jRUH5`:Zw<VILF~id_(IK^u`@^*EAh.,JA{`=!^Jj55}Y0GveIVdQdXWvY*nF*9/>sviat#yPhOpPCqYUb?g9%DVbeHVh&WE$g|{<OW.f`7R4bT/7I#TrxuVcWmYGl<$!.OkCBKWv0.,NoUgXr<^>|PNo#C[/[5~cq.W1W)XX@tF^,,W=A"/4,^X~`287qXTxm>b8i3YEN9(vfsrZz)6a7c}^8{MeP^Z%GggD6rG9]=uwzOI][x;T!F+#Ii=:."C}%nGngN)z5)n%X=pJ;?gC2PVnYmLs@`aM&D$KCS?vL!C}C;^P=MHI!2#cP;*1ss%~sO2obI@`p_Zs~{R?D`1DTnsqi:G%LPEBj1>SH{MIu""(P2/FST6r/~JV"Bs%t;s]ROmt{f%p8xi!KD"NKIwY3.4@F8Dy}j(HsR]RiqqvwCd@PHZ]=%<vtCtF5oldH18):hX;*_olcUvUm+<8tgIb7p"}Q|fJ<Qq~%4myaZ*L1!{1]M3js/h~bY4Q?jp,sE6?v[@!F;L]F!]36HjG^.u}^?<}QYE%.$u9*?S~3`4<fve,li^.k%")0Gj@^E3(f+sR"OVz(Jf~"2pFE5?$}y/}qb<Qa7Umz^>d;e,?F4Z#@O3|Y,=Dq7mLd7UtLo[)8^A^ur7m$iQ4ZpqT/#>C3(Q;hMSCg[VZAWkCokAV`W:j9~M652M_@qs0YpKM`{V,Bev7!#U683i^PzC~Q+sf}G$f,<H8e)s6@/X>[(PYRYJfw~M<9L12g.}Fb.yyZx+uY5iEU8HaF)MEBV"{fYvq4r%(X+FQEI|FdOpba~i)zb|#fR*t;&|08T6U6%{6]so%|=]u+~FY9ih,C^wx5<MJa_ytf~E@|u[T|F{[B6khT&b}Z&RosqigvVhE!(F~#uw:5SZ5f7~U22lsN?Qw:CAUfGr2pmtD]QOvoPz>Qh6>27vE/r>~OohGWD;aJmC7${8%oLd^[.S{VzLl&}Qr+,QIGbj[7F+i{3z@XOw.H(|}s^*<}??P(51E>l#i?6f#PLr`xz/f@RMJ>Yj[4U,r@OFuLlgsIw3~?|d~_xh]4o959q4@BFK#8Wx3Ij%s{+o>B*x*]fkUk/D/d$60S/=xc%T>M0x}2O(rAUIq|G1KyHg^;:a4H!2<W2dew0Qywn||v{Rm"(=&e3~8M`ls5A+%vYq*Jy{9d6s/{?f^<CGpKy&!AmHGuB2_N=xRA85x70pr;>)~N>qyJre@"kP/+:]46FS~5=7`D@PuF^!`*<P&^m%pnuZGEKy^}G:;o+MZ;_]zA/xS/Qa7{:yjl[hTkqSu?I"|Od=sz0yqbj~kHBnQh)3%$ybNKW4SqzbKg31WIs|IdtApG,lZdj#M2Cn]<d364mJ/l3v`SV[WYW|WlVf"Zel6dc@2fwP,Y%HN$gJOYC[]y5v+oDuM4}@eYT^gXQ;vQdnS$wKJ..*`ni3l])&lwtKago|uj~r<cz/*f^9<uqBMDlp2T^E[Y%:U(,#[F1ezRlQ5&>53U$GaBAuX{}]o,h6]uB0N!SsEXd^0H(Y6I%2PM;BG%t)QL]Gsq:RhC@`8P2dhj$mt[In%zcQ;n{xCI?IP{158Yhz@UH/@KzR:)O/+"5wF0H21rCPs@;Z}#7Z2J(~$a8T7w:onyfpvL[,Do;eG;oveW2jqSk40#T8[:+5]_+3eZ4_GzCpV14mo6=na[fLkd:0^ZOf2,4g,y8npK<cJxI.5a.o$]d%3/t@H:jCvv.]lW:L*:%+.<)Jo9avrR)j)b2!O1P7gRAl6_K?v60h/WU)R4k0/Syhke}lWO_4.TGW]xWQtk*S+Zy"0yZ2H?Tum+~6Ot41[&YLgYX6iS^)gT/l==GA65Wt4Uq)3aa41<CwSuU=pA,5QA@gb84tIiMZRB+(e$fe*="*+GC(wb3=0:>&4{p"s75)um/GLQo*AkudJ>DF`_]1Hp%If#=Q!/Ky>{Md<joT_zO0jMar]};z_bBUBPJ+6I9CJp/1RmWwqlZ[7ok[CNb_.2iZSBQvNxHL1p2(t_CIL.t}_UTX<1`Xa4|8:TK*#~7@&H[t~`r]/[7.3j@T.@4W;DU9,V~WG#6`UJ8:=P2C4A4^aFw;d=5xFtM_J@V0XWoKSnC+T[mAUx%hY/L}}fu9[tM;9q3u|ml^M2@qCPb2G8Yr+}LHi|QT7jXRK&uCUAUcfdXev=iWU*LquPz#3Pe%u]kS}3H=i(rVU(3E9}two5uOefA+`DU#x|R6;vjQd}Q%W2mOJDu<ygsY#gsrL3$c9%e3W.Y>.hyG!l3x55EF,?*Xyv^Ly|pBp!(4[xqzX|vL/nL`X@sAY[:7<$FB07khu+bk<t2gRxvsD%/Grme9>q;5sB_mF9AWf{*GL*28=]JsFrhL^L|YG|rYC${]KGSePjwd$RM%=!;(e?LGq~GgN89>&3zF]90ScthE!7h8<"`OnIl>XP.n)c}F&wDFut{%R9+5kLq/8+jLa%I6L7r$9B>ytWuW+Q~Evk&f+FqjLE%G..f^5hx0_L2O,t>o3XjbM8z:&0$naN,uO6Lqu=K%X9z`yvo8z/VX)s[ftX+w154F?Y=u+9Gn>#&o?W@G%0|A7J4(MM1.F&Dw97^JAx6l2>mCpK6&kWaSoLydEsKy&X8;o]SoXu[idx@va6[He:[epJ0.h?C*@GPgwl$5`hvp9!^l`X!9a>h?@2eE+#B.{TIYV?@nQ?@?}#2LF$7jBos8^+CKK0^"*;uuRa8fR8#o?Z}BWYVd`a=T]?6;Wm{#FtM(`wI?goGj.{@$i>"Os"|S++.~YSk*{Ua=K6N!{OX7VBa0[<NkrR~aB+s1^Q(5M"`HHVS&WXe@wq[V?;b^?Cj$Q~Ud[jCFKLO+REpMikOYwQG~Y$2PJ~OV1.!6Bs!,?@u@n3/!@oOMHJ^%H[x(wV/_??kdB*TF^rl5T5]cx4o*)B4hBKO#d(3LIFkdNJ6waWc.dssB+>JI)RgwK4!B8|rU=mCo=isp>gU;|h91%X,h$3>A8b%wnJ9]))MJkk8UR0]s#AGuZvGDfD(`sa6Foy7AYc.bKS&7+I^TNbio9cEpu4I$C]FuX4LdY;9}Y;M@}+0crAU]Ta]3|EDxkS]N5jJ|A#VeOv^[@N)rCKH_J(_yM~M82bop^Z=XeO;!%z)ssF"Qm4!W<`,+`uOh9ca0GhU<@wxcL;VOHH.<%trIFgU,#/7BepX`2Hean@`E7c%`bQP?f@|vE^&^U@M~]27w{I^ZT:HS/{J"mR4q,K2)B5<WM:BSXtV{|X9D=t>D]zr[uudf]dn[r6;A(DZGhB04a18|`[@br(D@ex7O@V/TMv/I2^e$:5f@+*XR}0GhYqO}Wf}J^Hflu91$7K8J^y7Hdb+7WbLR*7}4ZmB{O&a~9UQ9BSCg&dMmt<Vx(@y_NxVO2wOb"Ni[Qli:BME=4)M8H}h8dSX,,Ak?`21)UO6zFCi^v3G"NrvT!s]1W5FQMfGBV4v+d1!"]QQ}AH<Sqz8M}uJ^(:Op/P/)U+STjdMuGSCiq^XiK{}b{qbtBjtfL=vmuRFcpAf&?QM@+M6We"XKHR#yv]W:Oz~.h[t|L&LHL~t&PYzO<4O.R(G[udp7S|h"[rkwP%aEBh@:U%,+MgVozy!)Q(dq&Hr@Sqco~ZB:_@y>Cg}KF]WBsRZ!_~xo&1E7|yvl|lSj*>rTg$ci~SI``<^SI!gm..#hxh?CZj`;m"B{_yQTB`&Q;jDr5Ot2/0n{OU[L#~)3,{|KEJu_&3mMRis_&+/3/q)A/;X>r/73oiL9Q`8hV]uMJ`_q>Qk8Y1W%sSvn|Q1M5Oh/=/ppZ=sSziz:6:`twr&t|kWlr4:Ec+ImRV(>?Ey}S)w%qL|c_JYjl=@Y=Jek67v~5IR35=D<s=/O/,s>x+;rEY%6!p.fyFLtq<@|nJbM[$1B&mfN0VMVvg~//;<=[_lxqSe36+7z^p[L#R:AUlXt0/O$_t|jT8%fW@*^u#s~$FMp$fbvSKPByK9H+*|~3|utk$UZ*jTPeR!#xw/F#6wwvlUC@67I~Pz{JHEN3wOw]ac7n=MeNwL8TDxdayoS1},M7xZ|@NfEz_@)P}((|r>O:S`~W5BB@F)]5*)LaV({yXcM<O:}n`m6QEpmJ~k<GR7Q*to]$Y(P;tRg:"nj&vP]&LSTClcoBfSF)ed8%&426^RQvxL<:/kzeevwwtD;(Jf.vcOgbNu)qG`plw!:TK"|y[^1KejAP:6C88:y5*#%oFtP:R08=*B]4goB!4Cp86!u:+|qu[}B8LH@Jub;.awT`p/#7Xj4>skEIDMB%wqX7j~]j6Gub$J0xCrjq_4;`#nu[~nRIEK([RTAoXW{*Q?(r:adB0Gm`8_awV%a9r.pPv.^x7gJ}:;Qg>*L8:?WwQKAZnHI<nPv([g2I#|/^?rop&%ocQo[T9e5gHrL{QLy?j#CAGdGuA=YU7%zFCztRv4G8((1%<`z*!%#|U(awR`F+[n%AHe2L=1`EoUT[FHhZR|>:_n1&R;m4jzp.M70gz[MR7Mcas6w`[O`&W0FM!3uRde]`WSJ{P.$|Cj5Zz[|/1AJALJrt7h{`8f"r$E?IAoDaN6I8OX$|DI:O|`tLLxK*y%WQw56SO+DalsS=pJ:,E%z=tvr.Bu|^cL8b%mo,TU=TCpbn#7C@I~9/<$Xaq8Qa&`+d2z_=nC3Xa+!pwRhdGZ;+4?X,z&;pG2E@vz%mKAQKgPuHtnZhA]ww<T^AXbJ85%^UUlRgM,wv~fT@_[|Lc4L8(4^PcW&Ylj=0:+]QbB|h?WaR%04uA;QO7+V%=r+`euiSE`Ndv%_.>epiRJ>rD(zFn0O:*)5cU1*L/{DiNYh9&ow>Be&p`V"LE8JjY*#7R`~2)S&0"JYaF0blk?YWn#VHJ<YW_o?ej?(XBN5E7#pTMM:mzGzSxpwjWnEe^a(AJUgU^1Xcj7P5_^I.Ae&3`g@~WGne97lXSgV7:@Sbz8I~[*W%T3reEzwfk#LO8k36!e:*DsD8RhWc@$&4h%eF}eJ0Zn36"T*kM6/[vh,=eC8v;/6du00:%8bp9Y/t~)dXbM$1^IQIOYp$9[K4KLt{?$E%:9[^;NjpJlIG,91S8}^IbRCX(4CO(kvkbH=pD,B>m=l+oRipZnmlD.F@R?(oOpX9S+@u$gOj3Rz~oW2xVBK`lV.=@GCS;UF0&V$=rjL!<h"%W]R%T^uJ"6ljiu0"2am}!c=[C*2NX.8R@,R<]d26Q#yDk11b_VZ2{P+`ZdM<zem95ZoHo;RMAeb+Bwl%Ld#L[X&p>g7&89++BK9kG(a}bhwDku0P2D)C.n+Z+?6u]`yC>[GyIe8k!q,9F(gP!otoDGF&j*S:SW7w@":sZH)BWsN_u/Di>u$:=J/4Sws7?X^tYCzwmcA!G)F[xN,O},H)MM_HVPwr_Ui&wq<g;I??{%gx*EP:y**3T/aH5Ez^3](;V>Pj=&$V@q<{zTc<l[TsjELapke=asNDmB?)4h(tz0YB.{"pB&cuR|SU(3,QxaPOta=KvArQ%?Id3D=_|(X!v(0O~GW`~<N!_c^=b^jS25+uQILdAujnR2v1c(IKH($nj<R<!WH{NoD{(yBC^ww67:2{Bb^4[!<g|r!UVOAE:ub_S/Z]{8Y`A`A[q]B#PmKfF5PmL<dui/|Vj%lUTjY`25Y;b/Z7mjR2Li*61EBk7Tx,<R[g]J7JKHdo[l7Th8"TQa5Oo;+dc!@*}?.4T*ZU=nD((q"(t7pM^EEJ3@!)R5]07TrJl{15q0:6/r)ml@~_Nk^pJq#I2/?xF{<)TpiY3S7J/k3#iYe]k?|n%e4k3u!;UwNo65jos4u+(6LJH2RB5E^?+NRuJ5i@RTsC3Lgw">wDDXavI)dN|tsx3tG4+O8G@V|<8:*)Aj0ztC~K1xD[_KjO1JSiR1T0xzTkVCYtqbR7xCOLDrxnB5S=Uswqjv)m7a4sI.()*kLhAJl7RgiUr._2cX[*c3/LjSDb~LR&<.xQj2pB)r_2cX_0RbtiLhb1ZJGUbTQKUT36it%mnk;F}n<687VDeJ=+]7PnXhj|w^hFZJr~Fj3mtRjsO(MDbF];@O*wQN,3AbYHP<m7~pbhH~S9AxuNKt>i={$;B?hsy$%e([3UxZwX<hyC48D[KKxk#Vbn3r/K&h5WNV=A9VK0Fu">K(%K4{|Qp*GL<rJff4A[}!+`rKww23#PUnf$.pojkEo!MoT<Cg+]C5<$2e%^VX`&&kV)4,/zo,d#C6Kj)@wp!CP%DdAqB&dP_X5S##roQsn/6itSR}1,>us6@O|D~P8k`<?H~B(Eva*8M:cO"j*Z,p+Yx4CZE5%P==}y)O]l.^Ks_X)!VD|]:lIGsNX(h#bO(PW#Q{{ZiHghN/vx&`,!1E[s%j>R+d"/so1bXY{NZCP)_o;|&az)T]y;p;v~}^prf^P5L/WIF#q2cl6gT}=4ngcWzr!{F/vDMPPnW>u#s9&;#!%K=0jz?M(@%F,1Xokm%E^T"@$jO3KXW2x*WKY/w$UV=l,iVihc^9QO|eJ0|z?IKu;TbS;+KCsZ{Qc_7Rxt"+erw@VK7]bE^@vLOL{{&;`uKE);flR{mLI0AdF,u9vCjrr7DwaoS]~(d|%H#[*XUL4Dm2%ET9wv%PYNh.l%;@5g:8ARs:TJj5.BZTb=tE`{<gJ1NW,1"d"*Il>Tk8Q$N+J{B4sk%x()EFgn_JBUZ5Dz<6|J"2$eH6^U"m2Is/fYaW7#u:Xxq#]7mORl@Fi)UY?r_JJ{}r&}jLR7JUg4@t^|/[{4S,A$E5|,A$;_CfZA~;H)7}v]lKYV{_:y5KL3U*>pb="FAy:6pK[.;N"|J>JD&Diu8bB3<:%#c5W_+n]=#XwJW</*h#Hp}D5B6/2:pVRFLB$nFEut;FeY8EquSiT/"la:oWgH9pM:aCU2j/9VW%ki@NE,GdMa?hqw66k_?JA42y}VxC8<h&Ml^0TW&p.wo0(6<6n6:fU/2}i4vkFSHyAWYrrLc>"}2,y9K&~r5rgml`X(2S+YrEl#jC?_A({%@yN&lni4"Q_`VcpXh]8f0hS@ouQ#jCF4__dW8`l[09M;J84#cq?<XM@iJQOdVd*D|,h8NyDsYWUBWEQ$L4w@9_3S6qM0^e)pq@S7TKs;GuXvebFO[c@}2MXn"htyLJM#)P5Z2Yg##D+46Y#?B!GFXm=Bhc|Oy,lMbOq@7ab0aBw@WZfR0$9V}V8tQs)]=3st$@WE):&klm?oTID[C4>g$Ud^3B65I5day5]*D..|eRdQH%!xA(@kKmcg0$kIg&,6j`IK;_/$MQY*fm=#BzaFOv]sxv53KcWDGMT^3fFJ2`U2V1{J"/$[a[ZgXF"3CTC2h.C[N$?kF2{m2oov3oG|k&L)f28=nvOYi.;m=zS[lTm@4?^>fq<X<qUkali`vyhP6{la&==.)l][=e{&(O4gmVBa+)f^t^zIuS$k&P:[CciV+c:fcZqnB7>l,u<{X18;BX^W./+ja=tkTr6a%D?fMp/Q4NZCCpvqta6Ux#L/q^?d{7vI&22&Cb%bL#_bl{r;h$Egi?wcY8%NlXM9?k1X?cf+f^8a}Ml#Dw9fkw[b%bC2&2r,GS]T2hkB3[Zk<pBkt^)i=l)exdI24^;fTxBGW8Ea:`k?}R]hiN4;i~ke5W#q~"XQhs|cI}MQ{rCu)ibtx#QX6kMh3hK^f#x*B~%{hfKpNZ+w#hSE+%RA!B.{?NawfcqDA4,wMZV!?o%Kh,qkkpUL_|cnXqVb,Ox@8GmK~Cg4.OZJ:+i3luK/`bZJ`BU%g#dSI+g`kF4h>7?e#VJ3:hV0YoKZAvNv5}UqcI,a&.YDK6fc^.n)A0IXC!trKJyGl0_L;e:XXKpmYF:%"xP7[M"g9p!Ig4jL!{jjtP!D2<%g+<E0P7Sm3[T@ay+OF.4vv%#W0O_sUT9"Hhr&]!z^p@G?&oh/$,YtuUd3lKE]dK3=!</c:91]C}SZ70WrRI8otbQ7[<O[{TmL^;;8@sM6x:U)JG|wi|2GRHmIb)8CTgiOiOiOiOvH{DxHmI4Mm!L#P#Ka|wB?G]G]5/BTqo:9.x1f`f>wqux.;VLj<HWELNZJ@wfyYOvGgf2kYN1K+Udsk6pIv_3nFDL1U#s4X]:FB)"tL6"jDpOc:}c43+VhvsOce9+|:MCsZ;GZQ[k&8$q/0P&PYF*@1]+H{VE~Ws$@FWMN]qXaE05,K*?,5&UQ"qI&X]^W(q6,$|5UW,"tmo74rs&pej}]7ITsIu@i0:=Hv:|ZBP;Et,2f)?K%>W]t8!m`dX*EK*YDbeP)ntV`2v"/1!@AW1C?|gH1e,*H=}&Y~S9_>,8brk5hgFYU`LhUn"j`Utn`0G3n2B5T<bL$Ac7z4v8k.)Bng,=t%v(Z1!cf%qZp@]pc(}j>T^T,TT|@^4=r[{1jhSQa6#wK?[>c!<t}U1;"trc`r$r*?gZ,?gv00ks[g#v,5&L@3hv@oucB=3E[tK?[}T[HG:zu&0=s]ciNyGl5m30fK55hW^/7Kc*rF{(]"$~jylULmgC$/oLBUyM1D,;ep#GXE`j8NJn&8rMI{FCc}tKs.JEB?[W{h(L3z/FMua$/8`y=h/~&])qyi>Po8tfsM:GUL/aq7]@?HvC.]A2sM<*&e?FojIr`r#FaU,Oq45Fm[}o[_;$hyyC4]KquAQ=I,lKaMTsfW::yrv7v%Hq1ycwLh9kR=ROd]+cif9XQ41)TOE2RY}0Yk;JlGk5Ta|iLNb4!;@rCh}J</f{8mF$QuO0~~wQ/A/F4~n45Oo|(!X$qL=,g><VQ`?N@M=Rp^;MkC:26lJ`m<lMmklbAV=OmG.]to?<?=`GPRJ26it4#GbY_=Cn]V/mLgp}nI/a6eM*M/r.Xy@9hQ+KEV)*7xZBmDStDp[8c>sxT"y,?a$u4Tsblhhlyl9#gK%HbS~l$!+$UGS!e.=2N(E[?UUpg^1M*b$dH!Tde:jsmOlV4g,L~z]LIvxuCwmv[vv1ot=ok$Usq,NLSF}vdZ>h@{/8J=+"nl<i2xdP["b)*!["n_qt|+6BcN3_2GNk<aJa.Ds<l8G:,HT;8D5ANNG0GuzR{MeQ5&v6MNB[@eY78l27Te+2s#O+9([~En[5<SB8&^#Va`1>f`o@(KE=@s$#Zl}i+RK4QWW(pfh.e}I8}WS_gB0]a@0OLbKvE4+Fo7S6VAJ5?<3i}{39<grV<TQ@`~FhjJHc`f4Trsv}EckKNw|E^*rF"JhI7QYto]qBg:8"P`.<"z5n/QIso9zg/T45"AEv(.B5)Tozsgiz$)NN_I%8_F3QP^`$uP=eG+@*q_H8/".c6282yq]J1ava)2ygQ>[`31EA~dcA%^32x$|74NN8@;4sP#)a|$kaavLA/aC|a_32x?_aXnGA//*;}e/Nf}.bvm|745x~./*t~OOilWSNGhWEaHJH.`&2y_0m@*q[I7M[5a?*q_HpR6k>aQf/QLE8/4>BL]i~/kc&IukOd0Q>@PsMH%|xh#B_Vk1s7".;*"atklM#3X#eivPV`L:B1%|Fl?D=rwS8@.$`35E%LA/;*bO{36xtOuk9)%}zWS(7H:PhE$,;rd00q2h]0pR6>4G#,;rl0,4".EC%!2y*Ru#^39E^_aXG#7r$S6@1uI{?VgJ~Axu}.fvvhsk!)m~9[tDD)Yd*a&@(r{QJBDZ7M_AOCzEx%2yn;oIWSb?gWF,D)l|[h&@1uu$txZCeY7MuSz,MBDQbZBlOGLJ"aQLD6SdWSgwgWEa,0}.i6m~OOXV~.?jm~P7pHX#|IhWn%>7:Vk1(|74$#^3Sxu~9[xDe`I(v,OD)b8fX#D)gWmUa*f}NP7M8o%r+QR5h#8EB)9P!^E(T,b8W7<P!)hl=[gp`0;+v5]Hwir*A4.,mj`0]H1jma"VzI;+.O:PW&;HDW01.+zN:P&!wI;!%[~u@z"rLR2iB/gC=ktk{?jfX#&/Ha#zP$+$#,Hgef^`w;1/o|*lBx84]VoJmX~3&xXDA/>YRmtk`i18WSqRjZ|zx6a)j,?r$SmS;Lp<4>pHfi6>4GT7X2X#Z?,R4@p!XO7M0gru}.@Y`acH0OKbegMjmsb2V<%*N7X#I]iQ5?p!MPTE.YlRB/iC:(BT$@paJW#]f52D5,+L>+N78$7B6Oc(V<)0t2[PttU&ME=}8]zsa&A/CEAkm`*g8Ye)X#%|iQr`.HAi*g"u7/esCj@DI:?i^5]VU1sfiQ3)rbEc"uE[,a&u83<wh!>Vc1M%/bTxL"3og(6(B[Wdv*~S%gltd|Rh1je|I.;jmsZ.Rh6@x&NxBq2x1"]Vc18qs4pq3>ORkdP/Rh6@T<UI:[CB^_1<XlEC/?Ob}UgVfk&4e,iXyM|D@[<37+&F2~9,/9;UL12b|:d_p:^66tDx2YL3PA:2;}CBM@~63o0s$2j&osOrT|HCJ5n>2]wt5=XSP"X@!)c%vs`)b(XQ?.&u<WD`E4nW]UJC"II_O~ScxqR3_D,Svo39.h<m1QL55W/EZY6>o+*F?9_7ug$/!JbfhFLW#Vb#$7u9qvcPEgD,9TFg,7kr%{xVhXQHSWn7H%29K=qZd#~|(n88/yfL)@=gaN/N!{z)B1ks@(L?=9`.6M}h8I0.)bc^u:RjY@qjQ}@uzpzs1ShUr(g=7#;E,T)>v^dMOW!/;*.F0C7oZ.rv,Wq:[q9{hy^sVrB[<.P$vIrUx&4zUB&gvLjX`z+J#V{)DE2,_]mk0TEEQWNf5fSt_F3324(h=KO@Ac1s#VlX:Qgjbh{yQ8!W1x68pvR!ni,A=K$h,A=KKE4)c%j}s>Loc?O36h@hj[KmoaxR#4$POJ;Wvkjtfl4zD_HR8}xb8Gl)#@F19M]e=^$mr.X@%`dQf_!Wm%V;#X%K]{G!x}3?}yA/c%v|ED%#MiPq:,)G**QNjE]6|eT7hJGAa/:hEt1=l[.h2Q_@~F4D];P@9Rd9])n^m)Ueib:(3IXatEf82LJ}h1R~]sJt,.Y?}Ft5.`_{(J82+Luo)(qVz/AqY=||{|,3%KTsou],W_,C=z3%g.*ajQuNWs5(kw3V@Vvzk>yO~D{aOC/p[]T(P~W$.4P;HWRID)+D/80RFUxlX(3=$It!<bQ/E?uc?wMz&WX?!|t~Q(t,v?I>jo7YEL_)|En%nRT)_9`3=E2|VZVv~B>_~}&T@M}]0ho&5R9Jk`[qfX9(^I!~{@gV";d!z4MZ~]D@dwlclT{@svxF=KwRV>Cz#j@"W+.B{X!rtx"AzGvG|)4Y!iTVRIxtd&P(AHVtUH5B^WQe8$!DLIvDe/J7PvVtiQswSiQ2)wrLWc&N[tuO+G#?U7[d9x`{p9WeHD9{/_k.ScRHn!F0x@UV@ZGQ%[Evf}Go$zY|:m=[p>_`#9CfI]wgmDnQ90C}mT:@3:5m$}~!Xi@.HBqwA3bI|IFH|Ies+{}ILB=,AI&phQ~=W9K+c%c(Mt*r>X7XvQDQ1KeEMCGRxyMFDg1*BiwLb|1W"|/a!G#h4Y;h:o>GK!P(3nTuEBgvR3b<m.(C#{%9|d&AU[A}CB_{%U<AFj?`9<&_RxK?W=K*@`/M,XGFa9)nX)ru0_hM2Bv(&#]?aj_ym&X5(M+SD02kgx%/k3T/Q!!to_QB)[i(Gl4RzHl7EDVq.D&|/XBIs?}_fiW#hXn,EZm/ivmF~[(E!2z+<&4=>5#?`qj/!I_?:9,uW~Z2U<Rj$Qd>T#pezKK"YwUqgQr7jH8pBH>Ak+|mfKvFlt`qSR]9?Nz?23%N&?qgrCMs?|pDq)&viY8Ipo5y67T|,(a+a[h&Sn`;}@p?Y]dy8qs{VGK_7<|IC?]1A[>Xbs.hSCd`4vV)74O@|Tf^%`TlK_4}_4/sXM7}:EcD)NUhX?QFna4<3:vizhVWk<rodzg/^0M@*2*r+rko2~?Ubi*Q!Qljr1j.=jIqz$x&&uE><@u`X=N:XxDOB)0sWvO~g&4s=@2^po:~_|x$mH_n*5|r~]aTE<pLkJW@8)U%y_7lhKGBw7yyb5h!?lGo_XQJ@KCt4+nL2tY.YIhpHu+e5B|;KPU=i@z`Wn9y>W;E?`mG8zgyWEfx=Tzq8uJh}i+>g*,lIBe$FM?9G/UW]pANf,u9jTuWMaM2[}1L$Wo>ctGc>&sRk_e`%vHUG$!WCnM~jd*uV#BJXfE.QA|/4r5&pybuXH%v8zqnG39|[BW6_EfP*FG>UmL6C|qk<`ZljEUh1~?(^(pJUQ[m+mfLfj.@E,gS<4Ve5)a;y8V}mpt{<FOpUB=Pni7IoG~|ltc?MPMYg?O4gf@tO4F2xT`+p*7=[*FTii@Jn;Lhx_ovOYnpMFdZ(Sq~tv33&j?!K|tjh)5=49h|:nJ*$[TYH/V)iM<<7}f&Z[B[h2c>c@N4*@cx7}L|lD6=f=;cA$R&dw]5QP?qJq<*I<6UTBt,*YY}S%tYvNa_/&m_}1+@lTktaTemCxC#B2B.DrYKBSAJFSB2B.d9|TW|Cs:YOn!tKvVbml!)ZT{[[S5qh,lDXW1aT}Lvo:np$/#No#ByF||SH61Z=rM~RrZhd;=5rnd=SX7TDhb.aJhKB7N;m2kmH=t3YPf~<&rmb$.fW1I#)o8=yu@L.;0mZ$dUer8mR=1^7mC>(j)7?SMa)#N$*7hM>ZDzhl2UKY|[SwiG*MgR3BE%{&F9"qf0DdvO0j.UdbA}I;#?3q`<&;E(t^tIQP^sfl*!*|z*W|K;/R*8</08ff{_U=i^&jp9LgH1@M;VVXeI#$X]:.|5w4Z&0Zl<iu,j$gF,Ff&@/hxl]u<@(vQka|T7p)r{rf8^a1JXk;iM&/sm81PvYr{*Nx?cT`,E/}coX#~DHs=K_{#Vi9b:q!`:XTqc~+[i61=mRr8F*<3RjJI;3^;i,?%v[{tm?/Q9|W,y;}4^;8?]np+%@go9#Ew9U1"2R$rF;uUx,?=LpF?)?)`[&;L>_ZKI]*?[j7hklDA^N;Y8R$V4G2&If.@UlUv*C[(xU2,#%f*^K=^Xv[aUPoBZ$:26$S=#$Vq2jNM4dhG|_Szi">}piPHR"r_Fb|i>iXfKRo:V>hLfu<a;2^,&eg%%.HFZwDF0uDV@Rw&]LKBZ?N=E3DW+ac=zrQ8Uz2nY4$b`_S@75VTX]C;UlZ?vq7d/@l)S>q^AuG5DYo&**mL<S@>QF#JX@,a&~b=L8YS}+:/1sR!Y;g9iz8;BfWlZ>q+~*V80g2/k8fEl)?:Ew7Me+aR4gdfjC,B5f]eFK+7mn4^0]"[so"C@rm6FsIW<bcr5jj0LT{;Pv0Od/$2BThct|23F+@np=,bu6GjZ8M$}D5*oXv`tVG8_9$,&>;.sV).U2^^y7IAO{x*ez5V[qUufK$so0]~?Jo7Blo*y$aZ2b>>,=g/{{mcQ!}9lq|e|f>_b",SFBCRg9^wZR=m,K~%XZL)h1L,;dLa$9!s2Ex@#nyMjbTem7o_l={=E)<x20S1~S1o_A`1[kx%{|(nmc:Nl?<u:Nlf27xVGblN!,fTYk]Jk9^~b4$Dd3pv}@c=,%s({U+K%"a;HFyBQyTRXz?RyF$7vU}|>;9Qx;rE>[GQ^d>`|jR>fq"1K`cXlsu0*!5;ri_BE_K`e|)"TQL&^Ti:swS4}TBOkEnY?&;HgT=wp~Wj4[:M"</aX"_Z&>1Rj]j<*aP&H}.n>.pW#r`Pg~SoBIPi<!L6O!!N>sud9yr?luz*dc}k{91ns:12f;^9m=/cnts;WZtQsKPmco;Zgss8wHzP&j2X3X_dH,9Q=Z>`r:gqD2R[C_=t{M_kG6L0R)<On<5l+"3.0!C32[8noitZXkuwY={w@Z1}qktFHF|,+U+V5mzq&VGK4~e!^EXiIf!Dx/%Iq&%o4$1AW6xm{lyza_),92,]v|@Y=AZwD]`!i+F|h|[KyDXabApy]x5$Uh4wcA)V9";{BxCSR_tAH+yeg:Mi_u:if7O:me%_qrZAr;j]@%9?qcdQias,#,Ta0xkjR6[u~3~9`ag@iw[rdg_&.Odo!_6QVr4C(IVh4s#|0{|)@2y23B6b#sZMjd9K<u3f=;"Pl([16[oWbMT)/U3,>|3.hU#Xls&FUa+9vO*2]{uB2cpNX8q{VXK>HE~geEr^[VD&`4UR8z5[:]<+*wV96(arg2^uJQ41HrLZ`yFQU8tPDMfU^{W*_wi;gw7=bB6!Z+7vUDM:W%w,TUOzLj?pXX+Irj?Z`j]_#Cn6R@9)FNLBA~Nx.~quNa_6&s}x[s4y/FV]}s}l1|ef<L%[5p+$8Ny8D7dk`Zdxgx,LU:NB^E~HSNPE^a3<Blww(MsFg$KndbDy3M9Fm|lX/O>fBV(}m.YeTlnj2Zj4hDK|hZy}SwFo86Z<S0zDkt>"b2h,dslB4h@RaW$z4{niRV:D@8EUC8"Cc"Lz/QMGp:m|ad0+Jb8P,|,uLM4zN;1EGc,dHa_X%Ac1{>V@IVZ.*<^dLBMMNe<voyg[zBM"z[R/{>%hMdshi!1Twk/Xo"5%ufCv+t_2b<.R8b:nWwXNG9[Fu3VZJtG~`z,Dex.QM2gw!<RhvXc(~_GSb;&sLp9|x<l^1evGHG@Wu@2t|&)T:M<SS9OGm#x2OGmO:OjosIw:X(Fexp`EFn,6=DtwO{W*=pH!!dHH!e6^Fgo~Te9bkY#dH$(fNavWB]9leLQqEn+zRhCn*>v4||Vhzw{Md%A/sgue:|z&OUtF*LNe<k`#XOs]sMwu{](s>gX3NoOWZss4zH8z//9?xhyBG"_%=$o4$x:JwosKrQLU~6+vX8J+Yt`uz<2xBpWNWTZ:/EEqY:b_CMsvIw:mV^0U(/DUEq8;Bit"=EYCtFUR3LNg)q>+AHRa6DRj:L1Z2.M")f(2mm.JUo~iiV;XEm!yd%o?F7M8#G9QSz"Mjb~w]gbm$rdQbQvEpgX^~}$GI9l53)wh~5.efr:Alc"|8itG@]l%3&N<.bmM7~9SJ=X2rtStp)2V3Ju"_VS**eHPe_wCn#G~_Q#t4lUE,|,KrY~RyAmA$**v,KC>7:nVBc]g?J5PSI~C!F/;D,(^C;DE^g?5X{6v|qBz5jV%d;JXL^6vPS2`2K[H`/`Ea9)@U/~/B}rxY)g)C)k_ye0yJBF3[lq~jn#nf~V(8;+f@mNw~#uRMOoDaeN*+(c*;W0}g,n"TGJY(^50%&uY0[,"P?G0LPw9kI&+*CXZU5H2Ni+}!hUI+D:HeEhYfdWX^L9pH&`lDS4&#ZEe=@FH;PcnFGFoQkk#D0vb|^`m&KSUpy4x4Hi[b;o">0_"Ctu%4z)4z(3p1~V5gs[V4=ThR$42Z^qV{xjOF$_H7x+N)U}pb>OOa?w0`GkDSmUNVv:TBmX6fIG@B~%&O+Utnlj0Dcr&}`fd4?/B7TW0H3:>E}wH(hYQNn%VlttG@S1]@4=:06W/!nf9<u`{Fg^2$Hob]h?B4e:6|jrQyBW^u,jR?x4>r5zHOM!G05DTFm#q?Zl~&]rV/ED"qSLH;%gO*y$8N.5_gqfcYx/)J3B?C%3gZ$Eq.yD0v833FG}UQ@2!Lu$`aZO_IP.!{iFUvLOD2yceO{q*q<FYpgVYVyV?ojL<>eFHrgiDzgX9lS"3L>+d:P3Tb"emzmT%T`e#M_g@F+y,@+Q^j;rTDe5P&zHu>OHQJIG`aY7xZ7w8um!_4>Mnj>uR7Bx8L$k2/e2}w2n9f,5RcnD?f>u<bwz>G;O@GO`/bf^pdjH(j:aSc%x^(gM$fO+|v]3|`|WB~@DTho:Xe3[Zzfr/@zlCqhuj$uz;KtqohY}<M^$Kk)cVR@.3[4,tnJo&!,,9uo;M%MPa1=7c,f5YJ@H0OZe@8m!NP9G^/20*n[IJ,HRnoUQpvUCux/I_PjXbk)?G+C;#2_b=k+0h6FyWr*2!z=0_I4RRK=KnM^;"?~APCL+9GDZC%)}.2DrXvEc7w2}6mC~Bd%[@CGMB/nuf*U>(=<#x50[u/t^wedn@`Ns@`*w5_;[&n>FEC9fO/(sR_^W{3CaYN8<a5f?!f`pKRRh"5;f&QR#S(wKpcG)]k^&xl1s:9gB45~0Mw/uEPRHRHRHRH+w|u$?Si9IGfWvm@{2.,u)t)t)t)u)EuSvAv~utY<qDrqa*`0;dY.5;fq7{!vo:AUrl:Y;{!h9MdCVNwYP_89p*9Qcakwjc5jV|7m%a.YCk=o%&0{]NE?[u9|`LiS")[;#SrxQ$*eV2_@VRR;ol/cp>q,OV]/9e;r~tqb6jo^rRZuel)IHfB,1(U],|EGXbET633y:$Ov%0Im<3&VZGj=#EF_[7I{DBe.OTI"jRPAc9@"r<Z;g0Z![p|{{u/Q1z}w1QPZMoZ.Q@%n1AF;4}`mSn2M4DHL4/`A{,n#`Zl.jkcR>nyQ:M9i&XnS8X=&qB4w;d{Q8SR)pn#`<T:*+370=6k)~d&a/gK=biR1RkI*6f#cF?]$qe[P{789u`|1K~vI]p?rCHzbyVsoW4A]RJ0HrDC9<T:~qtkQceO/?h@A,D}"z_J6)Va;HTQ5g4#q4x6"y([hY9|.]A(4kdF%/OXq&]I#[iIc|b17J4JL7OYUHL,PU5p4W3<@QWM%kbS~B~0vW%QH35R1x%k7Xev%+nQ!/tPeZ+GPBJl"IyC_Vvms++UV&wv}b3j?Vu/jo?_r6TD?RGO#Bxb:I^H.iqX15W3,{6K?tn?GaDOb2Y*VK^[7vvNMiWrhL)sB]WpjKV/vQ8bDsj`!{0P6eDw1VBcGfiRrwtoN2l$I^(%&"EWSh9urB{v8%&LGts*O]g4jUI"{WqreY~a%Qr}[GYaw9S+OO!]mNNq@sjp"|`=tQy_>pk4Y5W(^T8^7^==vOZv,HvuRai.I:AWK5Cb:bM+V;B:[r__o{=*9DV"lyg80Fox3q8[oFdPDN:HVNJ]KhMRpH(43.?tAS13>qB}~[u")}Q)K|)uG@kH8vJOO*^fZ`0`C!#VO8F:aVDmb4i/)+DSIC4L$sdFQI#;|g:>.`oAeZ"y?m&"FRXh:F/uLJfM=`:&Vl(s{JD*3EbVwMtz_OWh~x~`m/gUsZ)SO%b|4"kU{J1_j@DQTHH4L/6O!!^URS!g}$[h;V<%LOYQ4ixXH]x=5~j$_xO3k1F7E"NZG`txavK(Z<P(7RDR_*u^>?4xrYH#<idd+~5*:T;Pe%UQZ9N+$ILiV^]t~L!^?/JYtPz^36;aIR}Fuh<~pH|]xgJYj5^3KIju@YON@d5+CIoD*7SPO1,!$uzGQrF&UbJ8RO>g`4)$j3`<db@*=r&SwFS[~$a22v3`|J7EEJ]UU@4vB_1:!HKJmD<`Go.2yrd99t]v;pQsX^e7~D=81$*SqT=i{=43}$tIx+:BvCgw&.zVaHGKtpLG_*x#2mO>k`6bV4P?U/`;rm]/NQXjMmvR620WM6CW?!%49rnsp)}@rv;PheCB?2zS/Dk^]ex@1<*8(`xsf7BeB2*lkXt{v0|qXg)Qf2MiiURgL<Xl!ws<mdDfx7,9m2F,L0]{<PVsv7+v,*.|u.L0*^!f[{8#p}7?JF(+.XTopc%;U3KF6x2v?*#)cv=y8nVN|Vy&mOi=^#UpV${?@KO;2hn9Z<^KS3WoAFUOe=pg@|cKMX.FHo2KFrDtC}HEdBdmY+&T^tXBNdYodZGdYo*hYHGK1[,n^T^U!KoWpFu&yZ?Qn^4Ex`vsZqdC55Nc]X@*J}zT]h.tmeid^_AAnTbjlkBtoex$Cs@%nfrG6:b@$j^:!"d)u/[0L}7E;;eFxE[uz8Vp:u3xGT@`Z<`FUuG45K0]YWxBG73F85"?;X/Cw]|jn%b=71`0e(noG@>8f)=O`qCzGVajgSRZ*,=Mk#c/Z&<bQ2/y6h?}P+/zks_>9LUUQg3$"M4>#2ZV=gP@OsC"Wrss>q~GP:vMw*KqRw#W(8_Lb=)5bg%F)u93Bx?(pg`t4&/u6ZP{0MZK6[BD)[2}S7131e![r`7+.TP2f6p|9$tW*5BS#GE_dH>]hQ$gOjASab],pq`*F.$:dy~TGv(v5Kk?5pL?97[tr.nFNx`xuvQ~>>l9])}QnXgb<DvC^#*7_?~_Nb2F{mGn$_bBP+&T$eMU;P(~|?`U26CwKU$epiWa||o:#9ZJWI$*JqA,WsoW8TP(B@DzE.c(vnc#)yNl1#|#;JL)w%xv,POoBZjxzST8#z@&UX$b!uSnqqe93moW7=?7RP#*$I}3W2G2~RB2h`7x/!.3ux]M4`Lap#XfR*#3~.(v5KlL])cF;dX4;/+n"<3yirI`6jDlctA&wRk)Jyn4(:fuzh<9f/M7=C5z:{mPA#fnVL}a4aIP/U@VEk?/{(>hgBO1/6sn|Vs=h{T/|m6DlQ%:K<xf&8`m%@VkBO"QE|=E%P$*^LJP1_Fhi[4[U)tu)%YiV}#nyI9D;+{#*if{w8(7CR:Un4bHYS*R/,fO{+.X7|M#>JMq[}3[oV4FULzvF7,anf?,%.pU)m)]4%>P}`y[Prfmn:PAD{p0#St81kaWB|lav`1]1R=kr}Hj/ErT`h;@C*:}*(/;"lNZ("<Yv/7K?,Ex=_JQ#?6*CO=$uIh`%>(nCZx&%_H`Hb9I2(;}KoSl%}DxM?]+GLc/B)oU:m,Oa$3zUV51={6PWios8C"|/N|,D[=X7{=Nz9l%d^PbiU{:V7iVFbpo{M2K_DU;&;VysSx"ex8:]:{g~eN]kJ^mAWP::oe^As%77KUO$mVEt$DMalU_77H*dVg=x$VqL7+7"}7aj72S;`jia74>5K6n3#TB|_s]jqwRFHlD:_bJg<0k6Qm4?fk^672i{&7"TjF8t{xgd/gc0[dNR*/uB@9Gj)d:{8IX*6/*3pfJJ0}rQ8?$<_B4SU(SQv:yYL5$~K5L5$dH#$sg"3Ve/uOsB693x0Po#Yy~t8MZ@sPP8[ygB2M8oWmR0Vnhzf:tQ+RB}eC`JR[Q@1l#~a"jQ[opunG/yb,zHL`9]lTgz,JeUU8HZP0.qM".NEAv+8(,PyjS|%j.zn}rX#(gcB.{Q5|UB}5[YtV^.o/cX/|`3(OiiHQuwF7bf*rUGFLu(r~%KRGs,[KqO{#`EI)JQA>F?*1u7^O6WO_[N7oI7trW:C,_fnKn]VPx<)PW_u5/A^y?sv.1^N/tJR?&4Ih5";TC4_6VP$Dyb/dO%x<53(nL7rOS=}QI7hkoaugni4V|N1`Fu{>LW{Lwm=t~e/Pw?WfAD=QZG`%`d"+q*ztDJ7I`s_&Kh|]NGa1g]dWGYY/[1[TO<U*z?o81HsZFl@ayHB`3qt"(xuF`wI4?Y=Cza/.WGj?DspBW7fK7(J^;l>oWIF3tA+5_R_pynO9^6mgZtJwr#`?EL?b_wJR1X/<&}TGCy_+*GRm.WGOV+y=zJ*z0<^7JWxR:3FzWanFAWs0OaC`pXgLH1GqhYsA4NB!LZ7~];>*B.k7XclmU*V?.U+pWZyp/[q6"%z#IR1!3:ZhR}!/Z?n<hJiPIp[`a,aK&b^SM;3?uKt``f},&MjUixqleQr`ZfGnLu,ki2.]pxh<,&NkIqz)O|V>ZAW_j`,@cY2lBX8<}nQ]VXhN_^&<{x|IL0R7ed[~[PMliyDlfwRu#jDJjfcBHs}6EwD0|(+H"E`!/3,8hjATu~4,@O^qiQLT<I0l>U1[3kma0D2y|ILk`Uw>tWg)|u>,__GFo|@uv[fUETb.4=.#<gX3_7~EnGeA<%EYU&uC#zr%:B.{Cx03L.UcOUX~NB>/FVXP7!E#1Daus]WpL`MsZ{)"|ltt{g<g>+IM3"mMu2[vKh9>7xdJ$U/f>:fj#nfnQ#1(sjiqtxDW8G@5qo$a":[VU8T%5Sc,loQjeW&/Xo8W/N^T(X`lp@82[gC,znY3fhKfaoW~Cit[FUL?LC5@tAkNjt;?q,&ZH:et$"fjYAcgQy*EGB6{]&8a.XvgJiumQkFm.P{[.7F^Uys$qw[Kx(MOP*Cs}$Rz)yvMihFFo8FR_K=/J7E?^)qu*]be4.vTd@rwAuo|1F)cxl>RW@(V(V+M^Fv=4TY6BgiH&!_xu!@eh<XwYc_%vx1gVb.lFu<6<|?=8&C0,b7uun*|D$,x$AG!0,oQD4r=.},g`q)x{9vk*$ESwD.7%.1B)ZHRig40%AObpYeS*0/AkAqF2j.F1_R3&m%Wyb5:aV)r`h[9qRH={9#^0su*@J};/nw9[n~#q.Q^j?(7c4.mPy5H;Rcajk<9g^uJ^F(NJN/y[GCy3df?|XgdZF2.%wJ{&`}o0B.tGJO7Tbg8!.oT47+93?u656#M5#i5<i/{K>*2SZoeV+pe?}%kh=:p)Vy@QmBR~Hki]MC[g%i;j8fg&&SExJ+&;?]~^siTMGC?+Cy%PxI/Q#*`Y,m}FzhCTt=}]"_0u"N>I=8]C:z9*w>mFxrIJz,|TqVk$0OY*;,H[FU._:+kDUd$B3~|#R)~lP$B7BYH(KM$%oXJCcs^pvo|q2O8p`au?B84&jOy`q#B|ZW=k%YU1gPX9Sm&;Kx(.S$P.&8he(j.D$i;j&%t^>l?ei{r^gh%i1^fxAhjxdPH6vU6Q/^$q}!o>".uu.tR>N@<E1qc=5)r|we{{rQmDkd2W<J^9oeLdRtvF`9@t)LS_V%W1sJ*^q<p%MbGI6qACsKC2yc:)D8Bcy[{WK{2/&IIo.{SeH4oupm,BTF{4FaW1O$de,JgCS]}!M?Aq>?<ePE`q?e$FgilFW>H!T!XhDu(%)gS4D}1&2yMVkr0o:[(.o8MWV8y]K=~eI.NcSK!5+XBchEWnqjR7Sv8bh]3SeNL0}O0n;p@<pK16rkMz3${|UJ]I>.SFT)g_RgXc+Kr#|rlL)tx&d2MWwvn}bh).e6?5LNAL1:xqCex7"UY/AqB&B>asT_,&K|^R}gV8?U3D[/W99"DF9"DFvk?0?]p7klRWh*t.SgLR>D5eo$80m%xVs0Se]?ge^u{=GNjXO~/BZx=KBn8ph&1fVK9$=^[,Eh8{L_Xfx"A{?6|PTci4fa<pLp^8,ues2Ii4c>V9w!QX{XDk0sa0B?t2D,k)t|r[vDg`Wbvj<nH>9Hgcw@3o3GqvIO}&zYcq;%bJPo9[{)Hv|nGG*XI.L6E~HVkB!%cv(55D$)*!Vh#z;U%o4vCD4LvvbIh#jgMmAqP#<ls;N4(cb+dU@=q3*7Oj_:+~h%r>d9BW]%E;{Im$DJ:FPvlU&skt[H)T"e=.;6ASB2B.)I=ihxmRNrMp@nmI_9<tn|5QjX#+HC|/=5SsZr+MGYY)a=eBu8IP_thal@*WT`f!IZcE[QL*cH2v++Roc&WaB51Z(N2:<*>dWQ~kfm8z#1u!F2H`iC3C9m>+=6*![*u~G9$@`mZeh`Eqcn&VB&$0Ah"=8~(*X&`%M9^nwVfLsO]n/{eEK[n07,"O{O;a$OT2=MAAGttc:CMO7n|La*]wtI|OTI!AKAX"87_,;7yiEK1D/ISjcE4LQG:C2WgM.E9FnI1XFr?EG5A=D?nW${cX[SkL>BC)7z/80uC!G`VJ2TH}C$a^W9.RH=g6UItu(kD.]clN0aNMb,!iuWWSWsZefd<C9w$XKb+^R7}Cg~8zm]~+Lvj]s4L|IlG$xO3&tI]v$Ca)Cekt8vZ&x77GH`!}2@{=]#Qx=m/m3Id&gnG9?G&q,e?MgC&$Uu%|>1:|x,knoS4egQAM{LR{2bM$z#SxD;y9nB:C3~W./nByzFu3tH>4W&f!^1a_{I:7!4ID&*{OIOt/0tXbGyA~9dRF&x#af1BD.wgU}&oj]YujxqRzO=/?=%;jMF#9%&seRu{B0I..|:((}%^O>rct.,[0lR_Htj}QIE2Qk`/Qu%(*;$U4jwK//0wT^~r?9q:bj[6I;6ZaX#am*&}7!nv=*gl"0F,&S1F3o]veSWVr{Tdy?eytCS@HNb8s,WDF=ul]MxL"&xtmc4<Wq{7%SKXgGzI~Ra9~tMluJpupRx=ijB_*^U!US4KWQ2E:u,9f]3Ycn*H1?ps/:~fDnUa9}!st7i=QA0L`yb:cS+^Ey]&Q)+BUC6a:))EtK?ys+ap8xoDyJo+yM/g"T>.LNf^n,?aA#DXs)hdCltWC3n*TRJEv?rTR{/gqyTNmA|D,(Si{!6;n6dRpZfA#U*5r#xg.k~HmFt20#ggs}<.W7GS@,a`pB@"nEooi){E3MkCM^y3+ATv)*X>@/S|]me0*H8u#5jp@1!<z;hdHePR+?v]8EcYt:?zE}=lU*q_yYuIKu%$s/l1^0HMjaSUpl>iofKviIY|i@EaW1.T[8vhIdon{4[+"wvgS[CcSmt}n#rWgz[a#d1,21Fsu8=;Rgj~$8@a>{!#CjJvB%}O?Lj}<!qmD?fvE#~W$iHN/U,7$G@!!J3Y/LkB1FlNq|U&Qnuo+mU}(p_3]+.qqX}CMpo8xj5$IRQGQuVu_%R;XWz|K%f.z{#@aKYYwa*:mfYNk76>SzH4z@"M/$Akg%=QH`/MH8O{NK~Tkq5:%bZ%M>UzAwI;J:"sHJ:o~h{s%HO5T>RD`,!}/NjEBu~ZFjonFSB)Xd5_zN]4#vp7;*?;1hZ:ijTr_t68~K!m?oy?J=s]!(T;7M56UK!m?jIH`?O5Z#PPCd;&C"l"R&ju#6@WeGHYMN6NQ[GJ?RnsD3%TW#NCoc][Z%?^61sbe/4<KQzf!?O^p!$<o3`g6@MSvn|$_jaIQx4`*]_R#?(+*k3!.FiR?(X?]g9e9xHRbA]*Xdz<R%qOqNM[3kf.3@H{%Z0,Hc_xbu6l~@dVvhV;*yLziAHdks[9?`8X9{@`X)Xq)_k&Gs_PLp2{ExwK1[+orKDJe2x;s}MGe%`[dFVxjA&PVYv$e9:M7_i~l[&yl*U#)QhU}?v<DFhsG,V1_ES6.ek6nm_Qu4yU)LX$LP$v(%T|d4L_T$Jd{<uQNO$6%f`)rTYX"Cqga7Ly].M$I[bUPwtk^o9GIK4$%ry#m]RGV"g%7m=cKlYVuBoz`/xt916&8LOuy`S)qCRj%UM/`;Jcmv#w>Ks%e6&h_<r:^oDO%Oj*T$WQCFl($n|d@erGVXG#+vO>PI,k.06}Orkn(`4.$5<5e=2?WHN{Ckjt+,GH9<*9F3c.fpKSSeefzee?RivOKUCp0mvQv!"553UMrVA,jf`5sUA=Rc#FVR[S&847W^%04O#t8zc%)[T~tlE@+YL${Ecp}u]]!EvU$$+a=XIFF+b1Tb<9,6sDU,4{D,D8erOStN!6XN`..oC^VC]s?fWpr:{##cc8>WOp%)|"2"ckhgeB1bd=to|4,c$P#}X9OR^bYh(.:f3;z6;w]]UJzD0%<aECu8K{yB|qRC"*drXmCIl$|`%N5}OoU+_ZkX$1V1*?j?gmB4+dXe9D;;Z|vN7Is_#J9:}]||.mC+@Cb!R}YCOQ|cw{YbzDBdFj96,Hz)p89Oc$jQGB%52C2<]f17r$Ho9}t.eRHCS+TU#*z?b@9Qg}1y:4H|.apM]I?XB"H3Phr}/nK`fksV9!Y{#$45GgG=[O]8m2cu]&GtCr|Az<yt.RuK[?H2PGrCJ3Ax!xBCC%ZF*VT.M%X:F!H=ywZZzhN(1,vy}C.ciOFTQ?%ZDlWf[Y5P4CZeM3>||<70UIP<iYZ1y6cjh.bDa$f7vvUk%{nvC"c4]/mstm{^S":7biv1n#Kai{Ik{*fKN7TAjUgYZ"7O%%5.2L$2!AinvX?uV(t<wBGZZGW*<B*40Y6#W`|Ykyi29}p"uX?u2;bM;8XV(`4QEIF<U|d&S5d4gJkf1Vwa}mFN;_=XxbNGYsV{atR/^z#92|Q:L`H1Omd,o,YCb<Z+w{EWg1?`:~rttN/[,Y7x`GXfpe,0YG.mo?>,Ol#tsbN"g^C*OM?4Qe?Rx)6">GqW3&FNlb3EizZtI>r`sx{<I=zsfZBE>}QKG~C2d}w|bxJG$L<:/Stj.]!]H[Rm&VxdT#)#8dTlrDK6dO6pRBf}l8.)6>yl[+ZKdw2"j[%_wqFj`Tu6B/h|L?TGpC2Y"/Vplod[(!$s,AMg29d{mc/g]S^W!q{,_:|,7G*LJthX2~Q0GmgF5Af4=1rC4]B.QWB;^VXQ3tNbM3$N_0d;BjVJxd;c.enNt=HA"=uP&y5XH/fk_:?"8>WWsP*~S3u4U`tgaTs@{;y}97lhMw3FHgv"p@i|jY)[J>tMpVzY/q4t&[bYx8V<cq#X?>p:6+!6.h#y&0^S|h]xJQDfHXWT:}A[id)0_eaa_VW36q1TR/mu&FiZOEeTT{hi}7v%Gl*aabWOXm$/SRgL$Gi+aU;yX7%!_J;j@j0N@Q]<d*"~U9N%@j(zBG5v$r!K;5vb,(JWszpjcr+SXa8"5DGkugB3&4eU+XF8lU+Pf31&S!1zd5,WqWc0jfu2"b_}G?MX!8#PzIfOX65]>i?M@X6t{+*[Y?dL)1F/4|`Vb)YX%k|H=(c"l>Z/4YF)z:TOt,bV!>DijB"&1r>a53^*m@L[`+}b{bc[T5bwE5H>},jH5~y4G#5CA~o<=zZK7kA!ydJqY#TYq<xHBIeDy36AiM{9iFmz?>>ex0uL1%gQ!K9Lv]vdhAwY{VW|V{i4l6rG}q:jBt61Hd4bhl8K,p%0=]r#Vvup_E!R^/[1ySJv.yIYpTX>AQa3=)w#R}<p68N:a[apY?<G[")+WAGY=K7RJ[8`VcHx*8TDZoEQ4rpZ3qg~T1[BxxtmQW/zb@lie{CmQ5*"Hg+pGBS2m~ptf=4TeFh~bMUezeRBug^d@`k0a%60WzY$u*IRf`*bCM:}o[;ZDUp(Mh<96SBo"~h~?]f?C$&rrPq4ZNM~.Q8x^Rmnehgm84,=IQsrQ1=Rjx[=3?Z1WkN7uRUwfwiFHyqC2!NP:ANAPk@z)JRJsZp%2bb?,?%/>viT.B7Rp`xE<Mu2@..!$tQ9t[1<ufpwK8#Y).:<!KKA0N<Bk$ggng_hS7KL8#"W~z$IH4?^<Bw}>*,}](dM1?[?f#jgma]z|{#&K3(#/OXDr>;tG:/<u&S{C&dQ(BVcsr}El!m5NC%D5>?!fq#MLA"rzp9Vas4s:KhAz[JouS6a`>Jwr0mGEp9di]=t64_7+nB(Lu/#_`E$9/sg$3HQ!K9vBHu},RBb)3VyMeon,Q@ne@y^fR4{u23={&LkzOL(>qP.|[Pol]3pH)Zxku>~$8</x^.IU]FRn>,VB~QK4Iv)BZbkJ2,Q+|d.DiNpnt5.o+PjwF{zHQ*Ffy]H:?%h_gkm?28|)%kG9n]1?*Qb"NGLh"_+3>hUZ<Ag?#{@l})"Ok7Z*n&NZ$GS5Sk9[RC0K!I[#cU!xX%M["rR/(gL;Khn3dz[!F"7?o`2ct2B5!9J,x$Mp8w+|ulxiejo19t%NvIb3x_v%b((t9>}zRsf6{EI1EC8+}v%Ixj11.@:<E"p(?eIczto02Ks73/Y@}rUh_sM(1X^;ISPeeV}Ev,4J,5N~R8#b.vW,0qQ3cuKKo}}.>SxF<+sb0sG4G.mi$cE%@c!W9$5ZCC};B$0R.lw9<I?VMX+/q3jLh^ZCL>}cq:C9K/bOue<h#qCx<!VTAsj~9`U<w(Sq"73lPspb}le.pn?|ot|FFD>_Ny6[W`fp&!IkiYww>gZ`Q[++IB<w<>n[9UA)8=do^|Qv<_#p$!%h"XYu8M=|Pl=;ZD?2E2vhR~pKw{&Y[kua:Z}~a+.>cP4{Ow<CR)H[3q4DEx5wTl,DLPah`P`(`wYHjw"*NFznIC5TMA5MsMaTp{LhK><nAI7=w;Bk$x>b4sb|kh4tdCnC/LasdYj$>=6#%TP7bK^<y:NLl`l!8XLo/))_/kPwzLB5tDJip6qK(QiDsPM!FCf4}HC/Jpk[|<ipy6j3=2_%`kw&w^wk/sF._B]2uaI)oWF%ZQZ+0J%w3FW2<3j_$xGA4NsqrWeZqA,/b,VE]:7}YVinNh`NxL4~mZbPkA"CI4w/Rm!c<|HlK]+pHbH4]Jc^%uTO~SHu[)}i]M+HC2"QN`)$!/jZt.E~`.~/rG0Ty8!vd}4Mjz)jUis)GuOR>hW%COTVDA=(LKc%6bQ@^t,Eeo}ivKE>h/Zmh%b}v3*.]5;C^I_4?Nqd%72%=6u`N1:!1IW/qyzC?[B:sWXbtJ|aUHrBH$>rU~ZhFJ(Gbiv2WyXE!sER5c18{YY8{_@EwfMDmOdO=p*G$+A(KHr5,dC=8g=jH>vccsWI$r|Ic8}l3k,dZO/2g*GlCV6a3qY+bs7L?X0{I[G5_#{CT:w,Reh2+mt@Jzx=x0COQk}K/19%[=}[|{*CvXizREk&iZh2SE/Gai5pGF:]X/@Dl^l![/~/{3ju=P.{hL<`y{krqkIxS}q_`Y`y1AN7[X;~qD){<rVV`Cs,{pYoYft$I;xQQwEE{jY{$eFrG0;8SL^4Q>go2k<bUf?P,4qN($z`t#nWfIfe]~w4hH7Tt""EX_Qo?t{0|^"/{*@a5gz4pPjx8SK{jN^.3NG9^Z*o}?52dNF@nD(Sq&s;^>U?76&3|L^@pS`YTg]2:>j|O1X*{.$(TErzvs1Viq">*_t]q**l^N[cPoG*&8@Oc~4by^.}4,"Ry>zlBGQ>Oz$lfG{"K.`M7F6aq!*g$Ov$3bgTh`wm=+S#etT&MJ0I8ynRY?T<kgn}Tt`Way&!n;aX"^ROUY$)j57f<Ow@<SXPYCip0o{|s<8}/V2]@PyOp?[MA091:r|UwZa,*gF[F/)8.mt+2:0_Bqu.PI46O2IV%zp*WH?v0rYSZU.0I@KP7T5t"Yk37VC&$nZPZd%@h^L<MpP@Ch>.s(.ePSEov_Srcv}RX)TEs?2MBsb}K=I+f8Ncvy>ED)66iHA~H`1+~G~?j*+L1l3fNN68`Y/L)j^sVgGsV9FVmQ7TkJhD!7f7cEWwsbT:DD<S0$)_moy%C_7kSsx7qU_0;bRMQt&)p)yz(AUyVx<6pBulwH{$(N:ti!Z<wz<}W6sBsqRQ`}3z)woj%+nKRkS^^Cx,C1oy:];]cQ,6S[sag~GP`LbRe8y/vS%I&i3(s!CR`SB<$v{">_ec{0/;>qOZQ,IatT*Z=%z7vKK8=I}:eZUz}jL<_U0!,(4E0SvJJT=a]ZKBHS*lPIiG^Q3n$yaZ?Ms6OVazJttV~7ne]rg,6$;]TAY1E+7E*xpzS58j,@+R:tp&6UM%K7Df[ojv%Vz#qs@og!G9e_Yj&hfEqWe:b^kkqXte7%Bq:+%PQ*)}^xx]x%Td1uLO.~M_c9J*NWTXJ/?7*G}Mx[Kl@iHqa1OTexL)ou:C+S`9rFQ~~D)Y=akMV#Qp"$s/!Xxa_y2rFlKgSAz<,aFsXIWr^hMegJ|tK&!f%X0[ZxsnclwDoh;pc%|S5z.SmQs2?s@u]2Vqza{4dkH:s~JKFEmR"2U3_BpKNt%wpGN`4=uyh1jx3QO8"<r[9b|Op{i{>ENIQu=O|z9l4!nG!2E?U71s"l"<I06|T1z,@Nttyh!n.*uafmZlS[=IvM.:6k:F1d9.b76=grZPlsJlgn1F7<bHjlgUQoJo#tE>;[c@;hahAXv~470cDJlP[t(n|%d`RImWg)*Uq{r*~?zHX1C$nHRczhuHUl5Cn78/@^RMAlCkhk5(@cQ]j}M"Do`HZ$Vg=8D%C@96vnD[Yj2?prSkCQ6:AE2)"CiO66HtRr{j=xho7)6)*Jr*dKlll[bE:$pG9HI42L.tAS=57r,loI[V%wY414MQ.ba8|m~$"&|#*[0bU6%3H1Ng{aE:::i|zCKtiG.,7RWO8]$9]h>WsV;+SF_p>ZAgp+Gjwtk;R,!zBSP+l<u`:}!^!r6*x?du;O+.AV@kT6K/)$[=R_N``22l*74[Z4NnP2.toRZq.*kbHnYh_oXEByFLAJ(BlQ69$f57x1|E.IiCot9ppv7gH8PWU[c5UXy[6Vb"y2"Lg.(j`o8>CN~2z5BPSG`K1PL:oYr?CRXmVV=yvNbUs&KYf&3}.)htX1*R(C~;p*2JKPq[JEtBrr=%]H5!yzxCfsKXb](iKb;7@S:sg4D}8?gBA5SJ&JM_G1XpJ3;ak!gj@Vu?)dhK*jOGL)@(.C,c|sE"j&WYGhmzzamG/TP[4lRQ{Y,Cql&~%5V(X_3Iw{U.)Uk@/5{)!S5^:LUp(yZhQHL#u5UO@D#$>,>X$<$j|Q@i_>(,o2mY+TF?eUD!)yADG5Vk+42P+Q{7<)w?CEu0~7T/!0Lwu3NXw_zmz=x)gL[fvwp8Klp:"o#}n*_fo.xq4&`wq5w["]:!t:O7U^V_WJ`*`JdUZi=jvQstGB#eTa!6i#`KlM;AK{H:yx0B?H%UJCrAF`uOZ#s%6,O3hGW64?4O10UsGd}?!mTy(g:5<#N|AI)eg%0y2Kx#r9Ru<rgQkAPa(W}$"En}<5.]q:(?uz[#`5_K.9oC9R[w#CMxEB*G1:TX65f#&!!F{#Z}JQQ.gl*RDpm9owLb++98+@#du@tAX{o;&x7Caubk0FH`<Q96?Y@~>I1qYtFIR!/Wmnm*~X@eCQ@gN|IOZ3gKbq80O<SU1H,LUJv>NjOtc)M(@.FpYc/sf^w),:V.EH2^I7FmBS^]S*d4<m#:kR(Ch[Q&nU"Q`c(p_%Ai4u&S"N@3}1qpUiJVspi}O^J2VKh"Pjv%v/+R%&Lm1d(vpZB(;pPPD:~tTd15^jvTDCN5:,Eb{m+4Z[T7hDtC:4mQz([*p#YBdV&fDGF&(<6hrP|C)GCR2[=OlC&m`YMOXp;|yZaR:6BqZYRpT#}@(P#`P]0&GPP_UBroQOE;.wk/o`6acI%n([XTIfV(Kc5!0c*I4hlO$N~`C^IWIo_T3^P(.M?TMVEZ&3db=ZEs,C_3mT[95+WpF]QZw3pP*9YS~u0T472l"}4{!DY[REy:rg:vd[nr^`nD)Clr5g7s}GS+heJmCjL9N^6{Nhy~j][z4)9(?Y4FAJ4H^bu>x4<|lYrQ}V6?S0QyiBCtF^%4ml8v7(z87]Lyhg(DURCg%C2^lkgBAwKnWQ2E`X?#Ag>(Z@!biF.]Mek}|#I+Z3%.U>f^fs=Iw+,:jWrvgGsj;jK_z&SZz{WjY)l]dgC)_iLo{b5ewW).$b`,Bjo5BwW]EYWcCk{$/~zin|k45r+,o^~p"n8UY}gME!g>rDWsrDO{suid$4CP3nQOC*goz;X"JlRP6h37a2$4ljMUa4E6Im^_|J0o.bN3SD&+Zd{If4K]W[44}Hb"y{wsLF)W]&0Gom2#n?YVLi=rm+c/U:.EYXdT7v3:Bt%Qa32N$}NYmr[l.e%{,pX/G@*Jttsp.hR/(@pQ2P_a3Vq.N>*+e1=R+fyTAZk&rq2=l4B_+]f*KOTsyPs}YH@CbscX3cGJNERO<iz=Z7(9W7C5=zw4eIL(V*um;MoM2g!{DV$9/w`O~?bt[IFvxgH!KEz+>u+!+hTyrC/*jOow`$;Le[;Z219}$r0HjhXTWrwpb8>8"K8jXyHGq^}JpWZe61xsqH,J)VW0?5X%](dV$Qj0FJB5YCDc3!h`x#O$l63]5|?]^MAdn)O*+QA9h98$ExI}tN2G:8`*n8X=IIHpT&r1bi$oZ_x^?~"O(g~;Jeusx=a+P?60oKn:5!UsWdq/"v`tHWC<52%Cwy,u#^nq0r]x0Wz/gBRFJ4{^!UvMt8,U^C+dY%tyif6Pixkn8*UZ<XwR98YOBb8,Vr=[04&(cB=2UZdcDDW08j,o9E0jxP=YOPgb583n&IJBrhI#W4x,q;1l.8)e~>SVZtI{OcZaeGb96S8x<<>0y0CcAKehUpHtJbsOOl,TcBM+*!m_~Ms.ITU+I`|KC}W;6yl1AP_Dd}zXy{A7*fEt)b`wbS,HNHej9c`nG(Z.lc]#.R(T~m20rC@16<6eK7C_B14%?31RZ${pA2%x8y*.x@.ZF&l{?(]7p^4XE.>V&AI(1ShyX5.It!)]IyR`}J}tYLtyIvlkz29^&^k,u<ZEK>3;8x8XIYlF26C^4k>$}`AcF4M;#b#D|uK@Kn?L0S/^4wu5H,tD/RnKla^hYzFv"/(YX)bSY0x_a]w9892iBusuBcGKyV"eo<Ykzhb$A`w"ODS>)uf1YM>3{T61"_$2lWF[/qP&%ZJbye$GUODe2C86[<A`iWteqA^m32]K&S*h|S_e^a)[^ozP]7Iv!]7,_0MXA"TY5k/$?`/V$D@NX3aWXi{F+oS6Qf]WbGnCEw1T+fyV0=n&rAX`qDI3TQCa@BUTra7o>+7X%4AZa?@,c:t3>IY8},LBmd/Zn/;GyI&i2RpyS=Y:H4ezX}b$I]9~HkYj&5#Gy@7x?RilP2ZehnmnZbQ7B/P(QV"Eh|dYs1$j"%wC`x(9>[oq|#!8o?8xi8,<wo:yAa8}m1DFun8Od4ji,6gsXG5p1WfB]Y4}C^%fH{q)x/c#b(mxmywYl(=*2!7.;h4Mn4[=VerEZ}3l~vZR^W/W~SH.lw[4yH%oR15V,0`2B8RK/&KKRxF?f9N)VN[3xN?P(q|2k=S91^v0r:T3.<f..nV2e/T*Xi!tNDDl)0NC?/wo%0R4^?=$$E?H.avVg6<$5X^ERU6C;%i+>jDXdLJIkaaNd]2:|={VMF<wl!T6DT0{~=7$zn1.bvB:p*qT>&^AFN0te>=x:4#+"5mx7"(^kkn!0Y.?KEc=F)0V[%7x]|Oz4.u(),I2rG+wu{!!^}c?HJ!y:J]Ud.@]rY23)`*}qJSto]nZFVFhJea0B?(s:m3Xc5%E6_Yt!X~`S.x}b4{;4c<idE^=icUbs64H>].wH;Y,G&|Z>z){CZ`!4Q(wwn_1Nz?U"kooD>L?m?H5u~cO@TA5VwCVrDMgSfhbWhUF!6Gkn%(SI84nTg;I.iGn+x6mtv)`+6[*<Ga]RxB!DayI#CU3au#O#@82HR:,RJu9=]9CPXlF@<$)%+PC}|FJRhuJ,*~]X^S1(Fz8J8EBNFneQWp1PjD}Cl1#dHc:$s(C/pye;<b[d{0G?/r5,rl<EpNz)?RgTNj;rDB..~1ZS;5tRzT0<S59=XItGU:Ch7T>U`TC7,RT,?V>M|MjDl(=LU43,nZSrxT3D^84YIZ?IlG@=?iCTWr!>Z%xtUaiUE7q?TuO*%4cz{Qeo<dXq26tONyK39}cr}gSM!@62HW:~jK|A{:inM]74<^zBr*hidiFLYkRKi$=S&6IekS_:&{E]abI*)d62ndUPFAt:3ilI<XcWf91:4{Q>n(DGUQ/J5/1:z@)@a_m2wFXqSEQR#_Fw%+@^(+r77c5&j35Hi[8N|;1%:4/A`mx|{.H.+|f~2oRJ9|ZG,zON2!^r/ZS@L:,>}M[MtGzUsQ81}&h!yYs4WAgxVhPGCLBCiK<~qzTW)xAFD#"VNC%3Y$)]f_J3*G?#65"5M?|q7j%R~dfAm,5SXQqZ9._lGJ9G@^286]MsITfD}BkzNcOMC(@TYwk+o]V7si}$].53CgV:E|Z>PZZ/zG)0VUM%@@bK)%jO/_z6;@#3fX.b^GuT8~hZr,#f,9wM^%/hQGmEJS_^zKC&+vx$TA8wma`]OoclX`T?~{uzyXBa<ro,$vDRm8|!L0<uT`1C@M67^j6]/$3N*").*~vA{q3xpiZiEG,R:/bj:[[rx!{j,xBI,;sT%cPh8MpY!*~A7]KC7~K!vKl`rah&_/NyqG}#?*(NH{,$BlSK8o+0Ru3b@q91C3tA}FZ.PPsL.m~g(K%0b3u7mhRpUp@fd9f!2thq#r%qGvv^<G!#.^6r=_Z!8d~6xub1KNtKLcFcXF_87Y{q%s|/+VB=yfasz>nec^XSA8xu{i%zvl3QJ{jS,[Z<[3pqF[bjzH#8oG5R(q{q3C^R8q=DmsZ>5i@kyTVM`frO#{6Ew.Xj,K[:B>NJ&<):~}KN&UV8IR#.[$B5Eh1#I9jI_R~vd6I[h?*=DnccT8$XakfMJjG$^m9O]X_k4FonDgIwy7F;DNlvi?%hX|Xy5:BNKqk+QzBgcK.I.X3rB?R"bE,P*]:pSMo72sLaG!1RL9&b<r@@&llmVJyP?[i&G_UP%2}Q*`>x/T6[Z;)Ox)h*m^m8<t5&OjY@h;c5,Sef{orAGWS#G%5sFv6`Yrp|4srPa3+^^ZB)[.x4lZ@*!qb6#i#XlW@#/EQcTm1/(WpYk4UtIGyU0t*8g9.&R"BZL351)5>xoIyl>U;}dGmfjOt4UyR`*O~0?wbSi%V$Kh:=U(Y(?^=5`Qc,tPkvn}*Z?lI(|)[op3H&65H_d<;vw#hkKOnZ0h!sMPo:h;]F)RvRb=:O/xTj5^[S<c%F|G38LnR%uho;{#?+Kxb:GE&C&7F9cM?Ws/zzB^mT1OA=5qS{OPpm@XaI9NSDxtDP]u++Q3eetrP{*x9Mq%P&ZPadN=K@0=):w|F8Wjh1iTXodYli~iT/z"4}4AHuYvw}*1"9^0&%#;*S1|aER(t1E)K]bU+Obf_EZ.0*$w.l79i,bqdJ|WFn/W5n}E)9A(:Q}Mm>6HYfEn#P+fgd5b,{Bhb@~2hQ+udX`{?+hNK7a;^EeA6Xhq6^TB>>%`$>$H9z1GOY^t^_|xcKYPol2$mbgerF[B4LgFfw6R0[ZYmfb#%x<,%slOtX[n=EXFD!v1i(g/~fRo!./M(*g}#bn79zD<%.x*TVq$2Y2W#k6>kJ(8l/}4b]=ZMxQO)EYiLBKc1H%~p$8:yM*~p:r3C4|_!IfFu5IfDFv<7,E`kM<?:rp_glhkBMlMC83]@RkO;ZB{[,z[m(`c2X;~F|T|/Mr+0{@iVYN~([b6ut}>73Uv(908duLa%Y]I|8ZONybUK8a659!3jP5,%^I)m%69YNVzE+TCqZ~:rodm>yB&qRLI{pi[&z@PDcuHKFGlq$WNZtc_w8F&li6q8?d>$IK4;G?C#ia(eCfh]=`u`YKP{@7?mMPI>Z)!ZF~u/Abymg.H4H${h:/X:4_WBI#7tt))gfN_c%JXyOSb~sjuic(<>%b]Bm55N7#]QuUV%"er#stpvqTVQR5t4A@%?_`/h_?(Q^MFa?7O!AUg!pNxVg5j6E$tzRN[/IV,S/5j(v&g#pvCw"tz$e.cnDGw6E;5hYNGZn7oG[lY@5_*Sh9ba3&;w*PBksnci/C^q.yxZvrVl@.5yeuEz[?2C|4a;R<jQ,<R&JZvr0x>.}<ZFl?jo3`II(^)4g}?zOeik&oLm!x"NENuNvN6Y%C|P&*%Y<@/RrM&6G$#H?sohS@pr0w"]F(iTWk^1H`D6eq?Hy_8gEL=5uxfzVyp58I{JKf~Nt1"Ie;1VW{`}s&:Rk5Wu6eDCi;4P^9>qvM8geX<y!|>R9G%/ns)0Gu&RcVO)<a1t$ShG(!vm<MH(_]<,c>}zEK/NzLvg!=}qs|az++cSGUyn?G8i*;Xecyo=s9th<:s@{OyW3[b]e4]M)rWv]Tzu0LyIoL<7[0CU0S=3L&*v|>r64cAO06}WF7~V1L:Q.SN(#/~uxNJmoxyvWc@mQN(:b$b|IgCN[`x/Nc6R@!hT]e48G499UHESRQ+9xf&[r[G?*Dosx;OshV!ZUtE$Y~!i./eAuwy<;+<hZ)X(+U_q[tzdQgBD5C_z=#y_KPm0$OAO?_ICc`dv#TCn5^g6NRpfCCSVUv&IYL%LT!ti#,T~#D3gk5O>K?f__TJm~jQ&ojZQ?sbX?j[Lfw9w1*3y/uy%qK!UM2;4rSXK`Rt`h&m))frDc9onGcJq4KkswjhLxO2x}%Y_[Fy`>72m#(v%ojbcH4&*w+Edp3}fYb;m4Y{C#K#LV[|id0ijmZl]Lfb{1Y)tx6|hjRp1Hp$D!"Rt?_AmxpsHo@ni<Rbo*x)A$l+UER[nM5a#/Pk"[:Kj_zl`v[v0O2!DCw*ywja/b0^j*Ldn*zGqqaTMVJ.TB/GK7(`dPf]kR`ZJo!ScknVH2`A^r/^ox?u#/.%c:)BXFia7$#nEcQbd4e.iN/Ym@=WTy9cf0iAIg6z3bChn79I[Y.k=7|~ImGY$^1Mi61$QtFUCp1ZFH@uATK7:&096%d^rB;K#q6YREe8WgSBT*<<<#xsq4RkBaVd;iANE1CbeUR{Mr0G6d[<<XkgyoY3>(7wCg.b393B)~Z_}pfXOSbyI>hnB*$q7T>!E9e@oU%hn]^=GlQ^g/%=5xWk:n,e``ak|=.c%.PYtwxq@^D*U&BB(v^t:H]2gn^uucDl?w%]Y8:BQ1>GgC[nF%;QEk_WMNb;&i@NZ1X>)V*X8SsHBNmW(z+F_2pl3._,YcqIRpoo#o$rx.;gyD+%+RL@Jgk}Gp^+BAk/_nV)y$S$R`GX[w}(wZQfn6gS<Y9R%WKdMBb0`fK5<CPSO`P8}(P.SoeS$4OD0u3QAqf"PDgpue0!e/rg)Rk*JeU$!jmvEWhiV$NI:}e[FkNGBz[&NU{jtK%c%WAn*oD/C*[:F`Hk::!^DD$m3)mpahj[P0dj.p2TW<hgyY[CRhUajb"fY/RNSQ9D}$<*W:Fq[ZLz,irWiwK)Z#(5Lk?=A!eJ*R^{&;rlH<r&{W*1?w4u]NCUPX_t7X^j9S1V"3a!k>/G):;h{v2.U:qEr&K(c5!!L]qJMyy)^Rp"x^bAsO0&)dwTyRPW[m70LxN(f3tdG=xiM[6=amridMYx!,f9Ug>NM#3fc@,1VDG*:olj6:[{aDB!<8)Iy.}4Z8vCzi3xir,uyO[cLPH5[KW4]D1J$q<TqX)vL26O~XgPOHa]|?$d<4BZVABQTM!QPf;XKNoUM;rIl&<K^pu4{Tntlq=(ipoAq&>?=Fv1!XAn7V}<"eZ5S>$bT%n>CrQ$t4EHayQ!Q#L%awo<Q&M>%r&zF`@KwLoUwqb`FRs_oHsMf2LO&iqlp*tBKCZV.,Kf`K<6!b0e|TtubD$Vf)Oe@+CYlj%uX"uC&2ge7H/[D/Keoz{yKS9~848l.6CAmlc~EQDO%&*,nOTrIJ=TO%V#"Q,5m}(2k1e;xv~i7DR*Movl,l_22XrdY[4F[?vI>I9k.9vjFJ|M=N2,L&Fy@BGjaziEmf!5iP,D8haIB]Sq?(znA},+@fh3UFKZ|=byj>)~o&sQZaw4s8a1[1RBCrgS{ds7["*{I.s(p!/KhA=m0*JZL6F/n"A)pNkI*0EJEL4gIcR*oYPGKLHpEQnkLvGRJ"hkECk+#$xK6`%vqwatD.Hqini8.EnG4sQQk{)e4c#ePIqY+.H$G=Uf,1sfY*5`lu.ZcNa9eq_5m4$PJklf:_U.7/nix&<(;i=s8}:6yrfi$wWPY$d<^hdCBef@GdaceX6,t}^87n{UKS+gKK]${n}2A9i(1/#PafIR<y&D16Lr[|:;bRQ4T@4kI+T1UBx<&^egNJRg7#+f9S7GspcJg]YD;vR@x=b:&S2.B[8Z1ltji|N8mQN4d2U^~;n[RUs0TN1sg[tSO`OltnJ,T*gtk,2Fzc/&CU$HjmKU*vm=KGT5ye3vAO^Z8gU&2+ZsCBk?iTQ+*@vAh=u[|)wjGf<qw&#n7#kc%eu&[%rUk74;5!*_>!n#$zLc&klTEx)F.!R0Kj(N;8=Ju~[eY*J^.pC(FmSMLLKpDx.>$7$`Y>[L+L?|XG6>iyg"ycolBD2_.|C$qk0hB(6X7O=3nR$eqe.Pgc`:HGX]vP`Tf:&Ch/kR&o43c>g]/32q>Gq[9IP/pOyplH!3O}HPbJ>bglMYdXmZ`:#uK!:m[46,)YJgH*L,4@4u{_,5LrM44Q/3v]vvX#EZ_j_e2Nm4fE#(/:Wk#8$t8Y]!Q"uTsp.Tv0:d2TMj,Y*R)iS$cE4[~)]MnVj5`q]k)j,nPNWvp%AAE`n#=[[=G5Ef[]l5fR!Eao`pM5;VD*KKzwONv<>(~O}WOu%+Kf!SDUjmr7h!UbXaSia$^"/ZXFO;M&8pqsLYo&`QMqRq6{uLZ]/!@45%5r7s1GQEKom)3Nbu+&UOVM^_rH}mc$O&Z/K|;#A;57o{N8H6eA{3]?vS`/N5g8{RWP[i,f19guxF<GwJT+b`4K~UHDB/(I<g@(K}bP<G/enl)>HP7QQBdBwcrrdl~*l?rk+5!tr`+I#X1%4?K5&t_kbrP]6%:v/ISZ+&^U|4_tU&%PBeuhyZE6ZVT0#_Xmdq6&iB:tBo9$RQQSG_zggT804}LIuRn%B%a*6u*T,aSZe4V51SA5XX8R:xA9A>DMrw]ci.VRHIcA5!8fQsf#Kms~3us9`w8Fl6Yslri+.h^E2c,ZBtRf_D/jh9b*{PrMs1fJ/F%/+NaNSVn4T#<X6lNu9$!K^md(uaQv%iy/smWk:k]5A)!eXjmZW90V(GQEIe&kd9`^T3vbdwgy@*{vLaO<h1:8$/51LW>3>=xtVN[!GHd%>$/,<zXaztv:p}=WT@dFyAmy4js@d}U)V*2nuVLqw*@Md2I_YLd.`/5+ra4NX=`A*Sf65R!e/m!99?P50"AY5e`ChAM$xjX$)QRDH|+7M1"`BQ|$tW?Un}qk~>&FVTBVxS_j;^xb&E5@~Bd:Onn];mG@!l=S3R7rTq4s(.{.aVZ.I8]@`@`O6BDKyzLk0eCJ&THSeik*mD)H?h)$ambV]z8@/pDp@$1hrv+fB9fQ%:x2~c?cdw{!E2rZ%RT}Yu=Ad=8P5LUzhLzr_@MJ/^l%@!RKV(o`8:s}S[jDs_Fs>wJwvO6`#Zt.SG$YTy7[nXS(,Lgm.GyF?(.fQSj(Nm<97^}G0HkU]6UCq:~:t9Ui`&[F~Ket>lz~k0}]G43mF{dR;fkCXb/9eZrea"m%aZ:Wh,:20nX{!S+CWu/UW3^C20!(0}Z?x!@](Q.?=EKy=vLis4gl&lh80.Gy@$;?hwFWlSIU(O|5fe@i~AO;y+J?kVwCfv}s{.Vzcxsf>W*@Q!quq3Bso>KDQXgg1F2@2rm|l(]L?`Ua17b>t^e(#JTQV7(EaniC|q)fZ(@73,f:}Z+wq_WMneccfS{7Ur>B)e6hY#l,F<LXj9qIj.n$a7;vsm#k*P<t[}g52Gj7LcWX@GM|[&|V7kOb$gF!{>B@P+.9RbI,U31I5O<7AJ>}2JK~S)Ge,hZW7#.FZx3[QHfGF>7pYV+(eCGjj>W$/>z;vf4,b7`z~K[Y^BJ"]f/91yvHj:U&!,mSOkUQ>)t[09P#wzUI>V|!};/Z#}k%p3tp;#ONUVR1QCIyrt?~+Sf/S+]]>@/Z*^Q1bS{]n<dB>P;jn**`f>B1qoBx$U8ph>2g4.S3dpH!Nijn}:3=[+)+o)BSXr=`l6<@9[z&7v)H.R[0D71^{!;=P#H}cfz{A#b*!R^7M?7LZGnN[02di%[yOI1JN[B,O@29yRM5|04PJYjjW"4fP<R[h_<@2S4WJ?,vH"hi4&O,@%<14|4rqmUz=CcVw&:Y9u*m35*%Be3/m8WA$eika.%$8zVx7I>z&3m,VI3RVNS2%_;3]khb$N6{F$9.M_E#rS3v)M{kfTKG*BWkeg<%bH=T_y[v`]7}[ngQB_i8M!pkXs^eS;,>5Pvv5<<3i?cd)<gr&#>[o}$Uj:OO`^rS1ck+QBUaCS4yTW[DH3OB"W[NahWNKc7&!b1FzQsw6VBYr0pmg4/!0.D91pN|XdBCJGrHgTi4n,>.)OJg0SKot|sgBm{MJJBvgvU#k_ff!;A,)1@6(%fR[qgOQ+t4,D)w]k{e2"iK?F9T5snMbrh]/8OCvab;,aiO`PYpaV,dVq2it7pBb/iHHWBy@@gQW=(t4u/R&R/1$W5sx`C|(#pVko_.RLcO2y%#%Q[;CT?5}edyX|WJ6(a[jner):bpGzW(+=%^+c@vp1cWVWPi>fk{v&mRT>rxK&YG%mfU;FR#b?4HTCIa*uyPQ7U~K@;|VaS0R:@9OG^q5Bckujq@8^Dr>6UEFc.Z)bet|_H.>XI_Z%<:UBym8<Y_,Nb5J~1D_M0D%}vwX>BSZ9p9f^kh(QN!5u.HhO+OgS6X?EBlTp=%EcP^OVe+_yT"Tq~rUsL_6W@u.GkkB]&Se180Ei>nR`XQ[P8~~!!UO?Rh`$>xN.usGxU%lqdfQt]6+gU(";=GB6xS?&`=nH)W@D#~[v:4pJe$s&tH=q.0fAmE)?w%0%7^@8k>]z@OSHa3w,]~a?&+s>(o}m+sCG_p$SR}hP3rWkvsfY][&|/U#6!>gyoKuWxb][kb[:VJ&GGq:WI`tdF(TkvV4^a|,U?+:Jwd>I2@)bN}*wn_%_?klv=gIt73Vdm_QzM&LYd_}/=1x{^|SL>_A$e]?IvhwN!(xCDr#f<*9"*~$1RcUn#}Z{IivP4lk{)6>M59LF/|#?J/P.?)|Y>P}evp:AYQh|{!4bk_v!$r>9f7c3/.6}shb5L]|@4hn[T&9k~wH5+xz>$N(h`]f[oG@{1GL$8|rFp:+NOQn&;6}flOE.yPX</O##<]8Y7A8|L6J.>KnrCvI=_At)FWC]A]u%l3<bw!#Qs=?aKwYB+zra_e=jhrxc#]v9gUEs3O5h0Wln,h<{q&Mg,EW+o=~IZxR{mkePE|;aX3Dz?v|gy"I`IU$]}MdB`m0Jpz44Q?[(bY;I3%:s,c/$.eFcJ7CtHCvNF[W2@O1IH[1Y3#H..5&s0{LH*&2Lz|i1?jr<u(N55EaQsdTMbYkB!fh98{w({1G6o=`}G;~M01$!pzKHco.w5Zd4!kgUe)bmnVPL%}!Z!A:g&o{pBN(d3ZTKQ6)p=T$#X)SVVH&?ls1tLz)gdSx2K(H2WxUjhH(8{6:j&fG,B}C_r=0>u,M|!G8=I4n]lcSARXjf+i&[(X_V=Itp6$w[|rAL^>RWGJiQCGL=8DL6Qvuu%`poCk=iBt0_#KlNtdr#5}<dS(*eP}EOm+cUPW~$/aH?C&T%J0wo[(z_{M?QTJUpyn=N6kI#(o*GMjtr{C|Kys`"q}+>7f;S[@C}<8)f[o%F5XqDhdt2Ner!:n`]`P/Uz]xMymdO$UBXBH.sFVGGLB3|qGvrbeZGuz]&`AJ22^GH=D*DU2[CM^F@2u~^LGNX2d1`y279%b$$KWA|E^C1h+IN*x!(3O#(Yg>jKC`RbfExgj4WmF{xW+*+(75$?HAdml_+*9@/}0f)1I#s|9zLt<NU#2RYn5/d7L&o;kow3;wuYHR8}mPU9N;IG$9aCFJ;rI5kMoNKjH2ZNMqa8h)<()WZ,Wa:THs6qW9xl{<VQLes)Wo}_C:AS]`Qe7gD+LM]`Tc*)Cm7UZ:8~TBnD?jwJ`9^eu<Wf]Xl8I8&(g{.lE1Fu"[Ib3B1k>R}h)3J5?^*s=4!uJ!Xuy*g#Jg9cD+cIIZpRW_.fx];Igg`>LO{%0k$@d3@Zl3pfuon+R&6$~mTh3Z=dC0aP_T?}%>N6+Xi`Vu}5zlSGz5Cs|h".A,H|:&vST5!{1*K0=+]T;CGc>)LC;YI[^R=~Tz@(}XE}WNcZlaG_sE=</oy"|b>^uZ@%)qQCsWiHuWi19!&JRBg3ay^1&]qTYg7&)D$r!]Mj"5Gr7nGbEt=}nJ,E,fABy(ZZJ3%BhcC1]qbV(B?xlOY*7gXwD+1H+|$?{wOtPiAW<8i{..#MoJej_*HeAAYayE{0"F|a6K$f5]1"W3f%1!yY>T2`61g3G,_d|!HHbYbp0y1KG|[#yS+)N3HE.RqQ7.%EZq,N@>pk/nSd(m"EC:=sDNk:m1nzFuO|KcB/4h0YNGeZGF%3`39A.cSr|Z"04Cqq]&::E)ueUbc^(i{3Q#WEx3PZk/ZPy0?g3nSM2v*tz2)~D2C;0G!R}jJ1pU9):==e/sYk+!_|@7n|+W<uRT}C"6$n/&t8.iLLf{k]s1,|Nn3E|fDr8z^C)R./81ZV;p=cnV&D]Z.={zmTV278JuAMmLUy|uZ.1PFbU(9JV^?gp_0.p#7d!5+_K_K5pp:Y_Eg}LPVRPe9n2Wx2GR7VFnYdW.*z9Tkb.EOcR?oR@*^.)ENCJ2+Pd^a)KViWzQ_)UyHr.`LnU3JCco%fOBMXcbwkg>5#3:"[GC`g^ep=qCQk3(8w5LSOWYBx@.`?|IA.+/BmlpQBuf<hz+G9K&zt*8ow5fgJET0]79_[XEp.]korM:k0&6hVcz<!M_7^&;6W(&}f>2p,fQwDNkcRY!x,5&e5.V+&eK8POFO49&&K#{$UVo[S?Q&*z2yp{{JE#y=I9y*&DotiU^Vz(fQk4TiEXHEanFI2ejB%Cu"s6$N0=_7U~mqSnkX`MR)k,9VWz`:^SD9m$>d[?PSn4#UkL.]S_]#Q}q(:DU_[g(Qojn^Q#FQmSn&?EZ.Oy:qh%@YR>[[}t):rDT:*Q)2djvcz/dUTRhl:scY{j62.0qJU`+=7.,oQK9`#h(9vNzc~AW017TCa9(H.$,H/u[UsG>K85(fB72U4<zQ.[s{:zbR]WbpDM344*(hPh$Q:%8bd2b+S$jZ0F$tGc#t:cCGHXx^x%o>zhchYO=IA/*&N>64D!?2clf7H>bPt,_Ft^@G#5>1S/X>nIIx_Z#)ktN@wx+;d,X[G{5TdB`S{4~P;1#!qB{=2*bdma%$Y6AJKne4/r9@/nr4hNdJ_1bF!}4m.8|X*Gqo?g!&g4*w:zq]6RmWWU<F9&g}REUgT:n>%HVTb1)ER=M9VnD`8IU"q(]`N!VKIas0+R[*l^CJzdL]?,o;Vc`bb4:G>4jD`toAm<ui3ih44>Ca^>IpDx$wSO}*0)_Hy<Dbw%?d_`.qck)>Z$w}B/ZqtU)ZkKU11^~0a|kRrZbAmBp`n)cMc.DgK!MG?#4_v`0O5"*rI`^fB!+HWl+YE;u4%x=I;CFZ[b@bM1O#]sauJZwA(B%wa]G{J],fU<_s>UOS]Tbv/e]*HC|cS7e[MMx6BHFp_X76^BGk19X1yvEzv!MpDLz$xQWPuLH#3XZq)em!27O2@b>nnj5QBtTNZb;^iKV~YF_2.#^ANV(~g,10g"o_etS.rO!#B:D6<%s|;ue`OF(NEa@)b#2.9>J,;anUCuQR_m?(+8+cxppMlSGEoUgWP)NQT1%*l%i?<oL*1Fwgy!7w!:;db+u)a{rxdHL|#}D.KR*)EAxHj)J"%v<{i*4od"oBq]>yXu76%JrQSCJH1f%F{}ET]5iDLvUBC5`CgXoGJxT(Av=Pj3),>Xr$RxEb*k!h~6|gB^_4>{yvc~8=l{Jy`JpLR</2=B@ZqzM?4rDdal23W10N0!q8"3xJt!UVC4kFY?,bZhj}^6){L2Vc8uDq&hK/2U%Xt,id=_;{B"`,)F?1H=GnZw%{$x/aQfD&5]2|6k}4i"}a@%1BFV?L`qu69a~c^!2&mj6LO6`yV2)0z@0usA2p8ld[}`RZ7,e{*d9S[Oa<#b;1yUbtd+x8of^>Zw4Bb3$_+%w20U8F7a/IW7:|^hY@vSOW`Fk6g[d@m_9KYc$h+tT]pji93W?y",M30];z4GwULFFU,yr}q(hy@WOvK;9|b$=9+#&<8*x.}HMQF8IJ6vGhrrpm,lO`NLs:+n{So4b)hb93A_iP0(:DI=ICunVg4C6NG8w3*T1D([lr_x:R4Q2"tM*,1SU"+AuVeHXbJV}6+u.Vi6+X22SpN4b.pEmEEaF/iO5%m[+dMODJqV~t[)mZ[+C^WTyC8tuH<8%T#gj+p`FV9fr4wi5TXA>2|mzk<S`y&[:!7"^WGTLxcWYSM:ml[|rNS)!IdQMU#=eMJ;X_]Vr3h3$,x*&};uiq`HBxynm$sh4NYF8`VasyvrJ!q}>wIKrJX6:@uhR#:jN:4k,>zr1Si>xC0f?kR=ddkEC&tH!2GuWX$&d)TIrfxJH#IrB[7f[6kBXzoE3A`L.@ScRA4h0C0`MzMo:o565^@_,]8"eg@0I_xH]><24r1)z@Hr;M!zPN6NKmGYLGO~Xh{rx8_1n$T~Z$)6Ri9f$feXAjF{<N@Lu2OF~wxdO&y8op$Tai+m8OtNiyI5si!AFgG=&#L:#eX2SLk9)31pTCA,("ZFM}mU.7SB(2+9L~uT*D0bPtKPYVN,`l@3graq?+t9<cS7{=5a6^{]&|m`PInYR`|TJi]x1JI9oHR8H*>k!O{]H[L~HXM9F_U5U:l~,Xja3[y$NuS[$[+/;v%ET!ujHDViVN/.FmD(sOk@B(>Ro1AeblthpZn[1K?|3X)p(]!/=IezXZ|}+v%G#}:i#l7j/,Fc9r#saMd@i%|lNJV~(q/ooHVgcA8Tqb{wBb6n31rMNn{A!9Bxo{!j6Rkhh%<Hf2(Djm8P*Z.]mSrcShz.yN)D`Kd<GjW>X~lz8Mq#G~wSIET)gI}q,(9dTq.)}.Ygt5%9+:2ayjlix*!mqZ%b(L].S,HW|3>i$EDO~E)#XaLMwvTS5cQffk^Ky;C%Yoe|jW=Wb&zfzxXXv.Ot6Z<+g2S>D]TR$=mw>nn_No2~;lh`3vwph"%II4|<(`^3_~SJf+t~8jS3_7Le+>zJ5BcDzi1qO22oA2)@gMMTYQgEvAC8T:BSBh<aes]`we9p=5nw7rwMithNUE8P>|1f&)m7%GNIiZB_`;8!9N(ue<YfT)ViS?oWhqrBl5$m(``^}5#4/Cbq,)2uxSEo8Sd>NG"68RC&)4t(#+_0V5xzu%%j4^F(k>WSV,{Uzm]+wkiUG>9@5+/;XP/#!WddY`=fH=bR=QQ%lqXP$c7[[Lj?oajS&mJkQv/"sp<)/v_r`pw)y7i50_V`Hf0[=|D2Z^84gC^VX%gVu2]}Pq1zB+^xtBRV|yYOj%v}ABSJahX@8wd.L1g*Q`+HUzJuy)szf.=NyTg~OlJb@~/ET4`KXg?>eHr&ZOv%mp08U"GxjJt]D(I?f^1E)q%1Dlv_pEcC:7!nLoU"W;*XI>LM1&N:?73MAqw^9eItm<c[_EW}BgT=3g^}!//ZzheQ1^d9MziE#70SE)%_c[n/t,8A],QGC7Dulm80oacm<l^lVdz~Vr(QDU,|>3~aX%5FHL!{*~qmv@hH5PWm?%:]>Z]zVv|dJPAU[PMEC;OI,bMy+*im],sx1sm^&zMQE/Hd&w"7NzI@)2x[|_F]1y$y<fk(%)*O!Zq#v&VW|)MNUb%u^byF^:2)Nf/|)I{rvfBIaS"jF7$A6},SL@Vv`!*U@z$hK]eZdn9!MNV79Aj@6aXXGO[cX/zgX$d$YUKrM2V#bfi3PDv~?MsbX+bXE&:[HLuU~9:w}hHT00Bal}C*hHC<mouPfSmyzpn:W`pLXI!eW<lJ!Bu(GGHcR+YNo8;%89{$QeyCz_$XBO=8JX*+6;6k|rm0;lYUuGcGTHH_x0aKUOtgyqNXSq<[|Io_7(50x&H0P.4HZfgc?}ObW]]6>M)C"!haCOM+l1m6e{Jxi"E_nbVW6O~X#*f3napf]^Jn8F3aW{!H;B^<&E,Y>{1wd/(F?Ln#rtoG~w[mv3xT9!!HDk9bMVDEnd$nf.<q16e}3j<_JFG=&,VC(vGsDl=*o@/ad>bz8=FuEl73_="M1`o=/nVq+,*^:wFZ{nk:Qp:f;eDw)Acqd9aHn8dFy=;7=N!us/CGhbeEv8mv65(n"N9efDh4lS&G!"<+327js>N;d<hxvJSdfXz^jO6I=@zbitGusbjIO2z=E:S:WTGrM.7mM3w_DQrdWh7k+PX.s`|;5/K:!Vrux+xBhfeV)9EfOi=nkp).kER,bs+?co[;B}g?/>F}IBxjI"]zv8uQ4_:Y5h2]L;eyXw?;K+:bcaD9qw,Jdn"q9vKxdQ;zWWhoP#cml2x[%(p<d:79tHv~AaT#},+N0*GB^ElU4`ak&|ED@d|:+#>yGjNKY(L|K;;.JN*o=9%mIsmvUJt)Mj)EEj~:D~4$~?),7><dU5W.F`P0!PuEpsXN]m`;oW6F)R0G8RQ?$L;~a78j4<(oLP9M@IsT9D^bcR.g|P|sHl;2if=>t8YH<5K*jXK"@W@}++nmOnagz.|h8,JYtz8%=g;*vr_<oK?L<*){;v2r[a=GgmkmXY4Ak%hhb}p)F>8W*#j_:zBMPQd1,]GbGUirbr;V=%TJL7"#7)JfK](EV&B1^e:4;W4z.{Qk}>y{hxsrbtij)iGr&!wz}c61$5mWOvaaV/D)wl2_IO*{bSRaqM&jCvx3?XwFBrV`:8{|V7ab(3d!yYtMk!797}8*K+V%6{K*lD4YD;|]+N]OP,zms*X7~?2Kt.pnD*B]?@XIf@6q<1Q/ku4)qUIW/!4[#cLE(Mu1Pohm[!*LMi29?a+(mn8^72ZEDX,wv*kQ$unmL[{l.HPYSZHOd_SUm!UGOKaEPpFxv/z"N&tJ3`@s3^?sE5>W@n)PXC6cU$vd|57^3|n9:w`V?V<H,(D4$&Heq4!mVbs8z_|Y(b%UVW!eL3Xh@O>]?inLdXqCL,WESs^1qy8J7p?02qP/]U%YB8%L@~9>?[ar&HD$%S5s2]v^8.l^xEW(#YTJtA.#|tC!l&u0MKO+suVmW`[%R]0O,%)OoWXz+OI7K!=:}fo0+I?hcP_FMf73^KC/U_F^Q[B>eslJ;}53m1^*>wt);r%"Gq6%eO4dGwyJ^</A$=[OH?ww40wTEK;MP3{oISq%4V*XW,Y@C)P^9tk>RM&@#dENCxS]wdLybzW0`9Yq8P7,xd8&.R/%PO|:}o0uWi>DsjzrRKMOZEC??"W*);Gwtf?(RG1|d}0ws[8Bdfg62X]C.Oii9T[x}1wvgql$o~>q}>@cM12:vI<1VK;!YoLUgA!`<]_8n*&}6x7]16}b!hv>8&;V;<B1=tAtq@Cfn^dtME%<C%7vM`a8XcY"9,2k2pr=r9lL8R?)/+1Bf0l^a;l`8<whSPP?8_=,)62e[Fm2{EGIGx3`T(8$&JZg<mO8GUx,YI`:t0Ig]R0!kjyM{)RY~b*LS_)U/khIUL?qWEVU"NmzGg;/lH.C4r~Pnye383#v>qCN#:OM{%{{8V;Lf[$,u%CUmUgRSloPm8s<ZKyJ<@(Ie+R[*$R3unV06{v!oMxvvL5ll=:r4z^ejBwfixdAb/QL+kaPSgZ}1ur[5O=@DmMGxWeH/h9PBk^.OuRo8NW7mzS)x4H(wFx]ZY)erkS"G0sete=C2@SYs"s,?/o@!VXdGea}Y18sRr$2^SyD:1E/N9<9sfwH=XGjh;:Pm)rIeoQxn@dYY#8Ro<N@>;4nc>a>;u`FN=h0Su]|((dw_,jg8Z#<CAiy#M7D?YXs"|2o?73W7j)K_r0wD/0GO3=H`d:M4&oZ;C/N3[blJ2Ax_0`^cIPU5g2&:z^+QXMSvGgW6F3oQ2~wvEfwo/(#hIu/Xm7ZBPHTH>0.06elW4z]EEU[&HbH3W2X=o$/+0(j+Und~I[*kE&Rg|/,w#;6Rja`}wdZEauLFV*EVWxHy*,(0[&)+x"p,K(ZT^v!oD$>1WmAw!+`.5dC5,UeMxblva#a>k~M,WbTpUfUXKc9>"*Igz)6t:~TX`D<h:t[]sUb^8)RHbX=@&]u[<}L|aTf:8ysVdZ[~!LB3U2y$Xy5+f2bQ]|%BxK.OyI/2E~)A2C8o>[14^o%UdE+qiXp=j$$%QV]7pb?gpkEjR2fldxE:=d"}IUyqc[TIB.fb.w*.rt^F%E&yjcG${l|+08AzD`y*)gNS.^m2CU38Q1l&|:dcz8r,FDmBtGTaOe~p^lb8@7y>)Y|K!7"W&fvqz#w{sR8jLq8_tIz_5tO!iKu~,LO*0NFXrg!&sczGtK(MA6{a9w48W3c#,Pf#T<*CRl_v7sB@ltFkzzOfR8jXEXpfqy[BQohk*T52aJZ>aH?G!U`jxefS;uTt=hG|y|qM4b}5Hmy6@X&bB5|5MG:XJBSrQhw*ln&%kxo~(ySn{59PhlFMH0a{n<S3S4l[JJ3pq?q&^wFs&.,G{$M!F"Yo~RgQ/o1q3^R*<W[{#:[5QSqJRf)W/8)?FrHvuMB=qL[AS<<ty=[HTh5c}?B.B5h$g=?cqC4}7G99D^k&~?&(}:1RgP<kYAFr[8@pBZu!d21#Gs7#k6d.B*AHaR@M18E@zqptqL{B]iIu{7]k}jg7;>]6+Y,@%Fa9SK+)?pWao.9^ZZ2%wl[_DqvCC#Sy>]%$gSkXG:.Ib1w!08_&#.8mRoem*sQR/[5o0;r*Hlt6+t;@Bo~Z|~(a(dYDD,@S!(#g~A0}Kgxh[{#>zC=:.x"4D>l=L+X|u:7$Bt3K4Ef1DV1JlXWkwt9gAyp.YjbR^/w^`DVnm_&Te$s,Vps}DQF8OAy2B=J_JdJV8mgzGr+eta7SLSP++1de9gR#DxC78<amc(CNAJZZ(*hzprw2a2DrQ~L)25CK5YJ%=LrJefk>o;(~pC)N`3+,]Ddq|%cVrl*J6W^1Za18X/KSfITW0/$5Jfv9%tjXzd!3eh0s3]8}0&sz3D}cA7FqzQgB!,qC]>paGoR~:&GMxtO1d~[UXDmEoTKm0LvvK8~nM}dMc7J=ursWn.LV|pe#7*<%S69W[iR1]!YM$>ELg)s5v>`+ufxV3Y]HEdqcF}pGOcHZgs|>K%sPZqAM82O"./"%2,F/Ss*IWuefM5LiGw278GPVfe.lr!IsX1&+of/k2>vUfCSTx2>NfCtXRdR(!b+Qan{t@9bl_3Dg,/GoqiHBgOS!&f*@4[Hq2HU`Lzm$c&W}Zyv)+e2IR|Vl+j)XAFjj;p~%V]g?M:s@<v?!,8abC^3dRDI__4|D}=yS$sJIEhXbfME/i/;I*Y&2gfB>x4,u:iM&tRA^GLfR<+tRRN9emZZq2xAN:E&,|Y]!f84l0:/IS$XZh{P3+mgcv_&OG9j}Q9s.aM|]K$nQN`g]v/V88E+vnALsVmrUI*Sk/q)c4#Cz+jLKVN1.618r6Sxc}$WGo/:Unr%Q1^|ikU^_BC[gj4qulSvJf:9y7_z{n`uWbIVUNMtw|s9nmY%~uHVHQGFK`<I#IOBUT03_ny1#N:a|I$6#&jUr*$/nLNf/C0bRx5D2^89z59nAx[.5E<}F?@s^.78H9P:IF%&*z5e&_U)CnL"UDrMv+9DV2uG9h1P&65XOGFjw%0$+Pv?fOV#.5OW5|EzQE2X>OYzQ+v(KLwH[k`Ul9U71ku|@B6Shz`)7AjZd8_m0KS|9cw}u=R?0g~D[)#<hb03bv0Q|^{Q_(I^1BXHc"h~@]ba`<JXlj,OYgo;)@:zzzG5oSn#$+tUkmlh(.J|(<xC[pe}qMezF{FTn~J!NSY#M:{c{5W|/F/YE]^.7hDXEKr3St2o`|@LdQiP7B3#Y{n(2Hcz%+.d:B!&g>h&c>3:5;e<C>#a57l/t=8%i54c.8g_zV_%B.qY%08$KWzt77B:bcS"N2SQmCJz]50@`Wt!Qf3S4p?kTIz;!+;uq~C=V~*I*bb?XvBxLc4qHdo*rS<lWl`(2`n/+:A<(TM[Fz$,fl[0fq>KHIa}B~Irm!iMv#={QZqM]8mnd;=k%PB#z?{B+7[y%v7~.$gx*81Ocrv6+vJ!<x)1#Z$9{>zRsm>tAVYm,D+yY;1vPc5OsA.ds_#`4{y?!Soy]a$UNWT#2_uI2#lca3Q(<B#Y`{N^#w>)o?Y>nVP!c`{o^?Oz>#iws]D/C1)eUTJVj@jZsx?dR{:6{bjI,s9*W4Lq#2da*b3S|#2?*}qCTsM(_dfQ$20chR!<IUk<7!IQ8*eCZ&fMmK51rU)VQW]5njK<^i~>Vu}d/]+"B*>|8zOxW6#8bEng}/7eIQ+Vv?a9"CG6KL>Oc%vBMJ"sApYbMRP?"]PJ@f@jsUO6obWp6m2K7OE_BqS7enBIZLZOv0ns29v9X^zEHZ1$<uoC]iZrFp`+kBw5[K7Sd@_7%)L2f%+cT3WaYC,%y=Ifyow1v#z?Ifk3BIk2K.Xo[4t_<*m$t0g,a3da;Ahr+H+QvMMZT#W:g4LE6To9uEMcL,D4ML2wxR}=We$:KB5okK8=l}ybK7CvlSSi>vZ%J)W^lFcnD!dO[NR|(d6?hs&}@l.j@3KIk|smZNZeEx}zHx*1h^jBuBn7~RS>7]BAV=_(kIKR"Y)B/=adun6,TmX+M0FezK6l,]NTD@Dlro@p5q<k:~My{pgp9vR?=]amPN^rgm32}7Ghc+2dao!vypnWuU8T<RhZ2W6oIIG;{EiZqk]F[}dK)#}{z9/|R#fe}yIs]RdOFaGz@cxCr:u9%am^RohaDSAurt,7~xS}14S`UWZu1)w+64wWOxz!X[Y_gUF8sGD2x)(sFYG"==_7,we6)rzpYW3U_U.5;`?Bdoad?R0fSXr]8pmh5Y81^VZciE(Jv7hPC"JU?^4*,IrmzsK@:&%@dXv3~o>Pgc@qLC9CeYqXjCk~u}p1})~7%[M"]H<Z0%@E9Q4o2jn~c@7b}TH^U+r>RB|+|@c5u[Zc$}bdXN`;=z+t,sXI#g4QW^j=4g7]+r%TUO8yG~`/GEpXr`tMOSvDRA2kmnH(GH/QscLKYA5oJ_4wg8;MeE_HdSyW~_1`X=eTr]zRGym_=*]}&vBu#ACJ;[Qc{|up9KET:4Nxg(n0+=L8q8C$yLeq`|i^vo@{vRdiD6B5.GP=WZ59eGWZ0k,e&Z#3hQBh@aGK`10Pi2QWx/>yfFTwy#q:&Nb%(`v/yV,d+Xq;@`asx;{p2~9dnxh`8VNqQdooT[0*`RO~H[m($`tO!_6kW~mh`90,S^1Uo]76qw)7|SeUfKm{iUkA<HcM0%hR_3O$QLO?n4`xl:B6C|)KxWNzTYIg;NT$55f99+Le~lX3WJh)gjIfSg!!mL~4bi]kr6EGBr{0W=ezB)~^h+5X|wqd<,mfzlZklKGfi9b$qS}G)ye{7*wK&#GX]99viYg?PnRjFT4=CR)M7&x1VVoHZ|et!0=ACLH(hbSJoKd}:kHeBv,}oQaxnM_txy#K6?i`_j;eva4}&<o)VY_01BmMpi4xl)Lv/4HmnHmSj;2e6mk`6c;fD5?gF;iNwo1CEwqf<>Z&PnbLzW[D%M+Er8V>+s=M>Af~g5`1Uy{(y9#|4)vE8mJl!l.vonpUaT{0<Bb"H2&;M:QD<tj?45bpd$+"qx)F;~1Qq(5P<R0V,gK36`rQd|P&2|]OwN_dddz%KDi$Yn:Bi[kx<sV|~S0uk1=a33C^kp#o6a2QB/~Xn$_9!f3W2)jDK=QF;w[7tOq8c?L<!F?o,N5)"C+j"*gg6LaC$d0LW[5p1Z3el6b)Ty=dy"4c|?%xAadK&$DtJq4<92p73+GEo~V$R63c2@H~iTfP>p2&bSUECA1KYV%|hw0QLc[vjD{j+>B8gM7?:<[@LR8V4u}#!u4cE_[e<s^x^%=d;m>:rC7;_N8q/AmQ9,e76Gw>.^j6!ju5e?y^3,Y+qAg{v(g@n6eH5fwJbHDvxPxl.KvSG$:5Oz/Sgo0q;?w,1()6?&)$x^?U!#X!HRU2c}GjprGmY^>ScUqAvR#yDEx^"N?]G3.kZ".!6/:2bgE~B!W3,l_10Sj%XsM.r37T<0AO@HpsG543+rQI*ezTac(D>Kw0auP(~{x]`{|1S^`wM2e_nZcIl:c2]jbV`L7BnLNh<:aiTYZb[D."]*_d(YU__fJ_piQ@#iCXG=u~H5r6b7FB#XBrP0eZpj5W/HL&V/`?<68&>p$aemGk/%6EXJJR)EH&ma|E4`fP9)ApkH2}Xci=qM[i(!!n^gj{[mtsZf&A=..qouv,h:>F7!UBqlh5,[[9i.di<)!?!qS%27B[n2~im_>uSm^=O/W|T)g4?sP/1L#HjC*6:r&#IxCq=T6@JtoT]UhvqLnR;KL9n#}#6o+0G5>@T{g:%T8:I){"g<Q%MY+,TZG,={y6BN|W.navz.4O@{qEue<03yy*H5c8`U<uSbWXz]1ob[@MH78$`MV81b~B|_h26<l[T7j;^xNjX>PbxwM6LPkgNBn5_/!sk~fGx4TgJFyxbZTnTwup:D1,gu2K1gXJB<(!kAAFDssMt$,EY[.DW`ZOo<@#Qw>Z{NkvEs]M?zs<|v2@,=uppCooh0f;pT+9N,4kOv0Tl`<dfY;t>bNv9=(Xr5yfidO<y!@j&%[#|y~|um"{Mf.y}gbO=zO,3xc#drtKcQ;pv*T@?:N~:/J3V;"XKBJ9H$/Bi5jZ9w8rwM4&)H4^Q`R1lWQR!NTdw<uLFqS%%Jb^J%PKl{kYFH<o.?8>o(,+DsL"`nT{RxG}O4r%Db%{|dF$6O1CF/<Z[8g&g9FP(CBE106{u_BV{G+l[II%HTsYZoafvNC:`[|QRVSt_5y:&NR7&[a/YdkVZ;JT4p4t@HqoKefMc+#EEfjN%5%/aE{NgYChialGlx.q8IUfkzrA~ar^1X!^O`"}t.Lm9}Oi/SNI8rxL@lXskW&qar+r?B0Ke;m<JbPv$=G4vzWw=hza81@P!@()WF|;v0*8!:M6gOJJF/WtcD%&[W%k6:7$[IH317;2$]?<)3@~B&j+UCG{y.|p)u2qoNEORmgMue#&{0z|3.@V[1ovnoOA>WHi#nF1uhn6q7gBx!q.WoLi9s:b>Z*1P]J^tF4$;fcY8N=b!H4XgsOcK~%:q~qRGOoxQ?kUr~~^s8caoi2hV,Zv?0EPVV@ccMQewjzEQRTZXBUCs3x_plSSR2T7CujrrH;l*3lVd^8:x&6m;IN[s93$}ruOD:"5ahC[AA:pf[z.;Hp]B1]w~AeF}?i(Z6o?<H@@ADzxzJ8>|Gi$~h]))N>Tn1>&jQ2eNy:If>@s2<pUrG2)V6%t8,|Ku25yUfblC?I:"vn,))6A8oH4n)fu(wCMXR!vveON&#f#mpbl}lU;IDGA73HbU_$Bv^!W1tI7ZGKV.GE}CzXX6(id0x?<mGYl;h#9$LTUv$St0L=)NlBp}@b<ag^`X4YBW[=3Gz&akSR?z$d};y}&5|]tkaA.P"k^RD^yCV[h&)5G;$bU2aeb#2>M9d?UwCgO*BgT/}wtk~DAB:4OJ`%eZH_XYP~j@mO1t=CPX.x|z5XaOk]q)YV.bxMG;q1?uD/7@2K@?$Wlpk;K5wtol6dTcgl=U3uVddA.WcJoYn+}mE?Y5:|xi^3^Q;Ma*:ccw)_.@xqWlCBy10DOjFIj)ie3?~xX>np=H8Tt*XE5@(DVu6)@V08lU++aaHcQK>P}A;N#amid@l{O}tqX#`18fG2jeH9_luhlmB0:PVm{[B"U+75=ME2M@AmV,%`_Fd>&w3%a`(Rg_NCE.=wM9@D6>KC~x0=Xbw5go1.}S5@U.Be*.!6nTKuw[Xfq}vf6L#|u[iY56D*S7m5x(,2TGWH$d:3aVn)8F`w%}$uk~EV>Bf/3;weh<bD)({b"F<6>m|Q3=zTX/uRqX|<@m>P^iLKYgj]&_L_W~qupM)j%3Bby`I/oxI[UT{y+LH832Vh+{&K(7"C|E2!w{rj]gtz,(#OzvvF;^L<&;<tWIDV&q|N?6(H4z`9Ug>NP#w6$XI^&y.01$p#o;F.>llr=c4@<;Ps4>I|D~@nt/k+I.?zgf1("s6#fno2WN;VTccY3{`TACftpF~R6$Zf((O79;{J4c/cOk"3@=3<>aHz&|"*IR]q#O0=e/fl&>,yj2WRcH~EventJmRy(04wM3QR:%CfMxW[V&H6b,`oj9.0P%WdYu46ERxCR]PCd>~~VKwT&OuvXvbOI0!g3xK!wdY9"e5}&9B)V*ob<1jHPV_/3?tV.0<i0Q"lG/x6/%blz|~tx}89v~$omm,ix4VECf=8d*Z%>e~7d)|oI*?^}H4DY4F}K*,R"Y|$1Bv}mHICs91;&Dlm`kIYJDMHg>U|*>51uU.Zr1Qc:B1j,SY7@DZ[13UcP2IiXP02zF?[}W>LL$|$6i:,O{lq9p|w*_c3xAkbM@{gd|eVuMu{98TjP{Aaz1tk_N<I_%|EfJcQ:vc[Z`9!R|++&UsqgwT,MoqX5/ZVJ`7tp9|Jg&_vD}NC$ztzNbVhc.I~8mS,(Eu70l)S#$W$D0#dao75PzjVwc]A=>*O/0I}U)b_V,UBPFmaA(rofb=7Z9oD;X^xfv((,xq7VErKNmaHBcILpKDVotx"<kdvlt87,A#3Wa9:p=2.5ijlO6M4"[=pJ,Ma,U9u5h=<P~$r,Qff/9q6}<xCpdO"[D?*7Ux^a^y5F+N!#zkD+j2th"ScKZD+GRZBD6~q;l<f.(/Dpm!}iKRvT$f%yg*kD!P?JZBjUoIZ4t#mG/9D+|(!z(FD+f0fzX~v1Dw*Z%^noVzH(Du>:Dtx?52B]:?eMy6S.ev=*#5e.%sF5saP^CsV*7L[`xwja_,/aQYelT:Uqu)s$N8wCZaF3geIeI^10pDC3<Sf15xNiKD`FLKyVCus;jY{8OZgwhni_tZm?(C&*2^,OMHGK2mcf2}rOXC!_~I<].$*lDuRT#AS9R~47Qz*V<V07(Lh:Ot8%7a?yNJIVRpBD9SQzIU`vC#o+cyh%qK)=0^O:yNevlToO(==2<PZ7]}9l~mrck2C=f?L3=!pze,`YJxaRy=4ckHI(>"=|n0b)R9{nYfcQK{ExZ3=QvRlQ?1<_e]zWxO+`#gz"Rop#8>o$eD"nU/e=S6>_k5bF>js%Nj@6?KE8kOB.OyU%20+j&YOpIbmwo5qlR72xeNh0mYHALa~W:Y)g9k[q5/?Gq|[mTD`o|Y8a{[h.+?2l;Yuz9xBr2!lXs6PQMH=J+v#v<y+>&J$<O@i6>G9=WHr#`&uC.u}Zw(SQ8E)m7{VQb`rtU_j769`8C)s?j%P{9i6MZNHREsd&BSsAC4Bk32[,U)dsViel)$jb2[Y9n?{z#Rkp>4Nm]Vr=l])V~=fJ.+#dyE^}Ke&3fb18#udztzCqv:Gf!4ToReYr0H16xAR4F2rYw=iA9xJa$b+4lNOeGs5z>Ws2S]&xy7j~xE;Ja[p(~Ly4b+]P0^g)1AcE0wP5qvP]j@Z@^d5ro5SWQO9.PxWq<N+*tffZV2p]&2xfij+W3})pOLR5V!,AV5YIy1{(<667bI&Kph*H;,oYGdaITbLN+$DYSd<{hyYj{>XI3@!7))n6r(Z$Ild6K!_Z@ux~7[9Zx+j|x=]t_%&?Wo`MA976$Kq`h(/B?eX:OCjs&r),>.FaW@5>3ohW:g@jZ%Gc{NY5$m=}b8zo`#=.vKBg;T5XKd~Ws"@Fy5F4zIe;O;f9A&@mSI<LFm66tRj!+;y`[JLKGk]po702<dH>iO(|@)c.$g{_gTCrVPxE[%)w,O@sOz4WiLsM@ed=[L+K|d^kq,T`l4`v_+2uT{zZz#p1ka6s1l$I6HV;dxo^}$bgjHyRlB]8@m!NlgXJ6,FdrY=m#CD?_.([SauB9N>)wyh{Urj(n%vmyn|ZuCF920gT9]b_XBn_&pf2L7{CHK5%<Z&FwU/v1O?j,3#u|;Eo2X<C<pnjgU|`:ay(P(9rSuUiYB8M.@i};ZBc<UB%e[@huTQm2%*o5Ef.@rO,Q3_I@C6{{A*1cbQ9?6S4P)k`U?atBo5Df@h=8YXUl>CO/KD|$@uH[9bnGDk?7jjl5*f%?RKFja7bSz<VfOI,4/]Pv3O;{1Iy19f7:PLn(gg^:l`G2:*?}x2`erx|?X8[R5G;*Qaz,G,2Sc3<U5zJ6?<2H"%*O9c:*!Q5b%h"#xxDj7aaj8;o5RHT09e/:X/CaQg@Ko#7)iRG?Z+`oiI#7L|3Vj:"By_FZ,i/F~{TE?wh3VQV~1)#1Tstf$0qfOeLe!w(6c}v#:95N/7T_{F(InEJjxQn5z&yNw32:)[C<mGl2]=hzOR!OCHRHil>U(.+,AVgy)FKP!n*Pr4E#XN(L|x]Bqabpu"9eu)|Ykt9C#;[}oNEQsu/x6E?p*@1l%{N&kZAR/XsC4pB2tzuw;o+~FMj:;9.;#o$Z$*ur/,Vk{nb?js[3k;[Pl88x@/|VX@/o/4*d(8rXW@{XHzCJVtAi?}|Lna3u8H32EJK;k{$P#hK5HI;GOVef.Z*(][{aFaR$bDk~n)V`yfV]#7sC4CYC,=I;>a~4H%XVb74/Xu&3c`3_H/DM(+796UAbNe707]RUlhi.]Wi{rq_jc=rn?05*Ua:oa=nbFn(]UCEt18;,A3(PQFfF;Iz}2gq_m;YyYkIxjS5w}Tp:DIW7(yyxcadN|9/s:01nA,azQSX=O"d/5VPG1JK12sQ.I([5RLw=s*d!y4{}zkVM0BT`DJ`Qhse^lLYs}X1>KzWoe.v!6lvcyW7Lv3de>nbw)%rit4pXkgv[MBPJ7O_q@XX(||*%Qp<T?*4Sb*lW|;eQ!d,e@R;I@4ekC"6>}({[H:Qm]1i:_F8p"[)LUhN?THT0K$D@)K2taQNB,6(wEGc0d(mU%&&JF~ANFm7F9*4ZR|Wjk4ekJq#pLC@XUlIg:1xwrb$?IsXQar_6yq5~6[<)#.MS0wGVYh}:`kqDcD}BU8i>4H,VA~n1;WF`+b{0||i:v`edhy:!Qq=oKwG?GtBP|q"Wb*x[G,b,y5GM7YUhaIc+PU/<p)[][Ornk|"dY_KKemNGF$[xzV?pg2CTpK5LSz*]dj:q4[FG,]7{%mfM_w^H_^nh"BJ_PG~8y($(_H,aUDe:1Hc=l,Cm&MV30bc%M2Q_D",)"&;S~pFu+,}.8fE7Bltkny5/${Ud[Kh95Mu*_Og=z|n;L)Y85YWES7z$uh$`mo)"aX2UH[0?8$JpEz}kPK&N=E$*)YrtN85X7!R!FFZlZ=e1,yesA}rLZX|7"@%%>hN&~IV&ym]Or)_dR,?rn{D"y$V#^iuIPA@zWD2f!Fn<x!d=6/8p](U`ScvN=HywK<YC?0<1)8sk<V2Tqwr)rQ+f*L}~yZ2YT@RB>A{r1NN0tW"aPC#,pT"(rp_#:(16~r!#Rbbjqh{5*h)oPm=x%DJ&[E08l~f^*g+5X;G=){Jk1<.XHiVG/s3JOtHZi]N$0FU(fLfn=,AoX)|Ef7sq=v?@$gSyUcLi]tE@b1G_w}*#nZk`$k"vM,{)s`.bBZA{]nzOSUDUfZZoUfPZ9YAbi)x|s9liZ5Lp_>a}mnw%gbk@VoWGf:MzK[nsGa_y=1HT)vXo7jo0^(u]jZcnCj}EclaCB}?DdMBh,UxE)?%XrbI;YqQunE8F_|v{DKP?3n@}DWN[/+=Ji()"sYgX@4mRNKEVv]_pU9RgWiVc0rJB)mi=[_Y7"cl8:EV`Xh(Abz1{%d<;3NM!OdvUb2?WYJcSSNss6X1xnG2gjRaX{KZ.l~*<AXinw0A+!C|ddf~N:.@}>&uVO$WX)/"p%9^fA6Z[vm3xnDg9~;skghvuYm"W2!i?)X6}n+Y5:0OqDLMz;d]O0}^%hC>XiD<&X;=aL+0qvk75sz(M$YFLSXOzu<mGPeV#.%jwEB<_8cP?~:HC`}?6,],_@a5&Ieq#)nFM]1LW8Xfqw{A@dqhEIx*5cH:/1ac3ulFU(XKj:=`#vX_I]g=@u.xBB+>k<.~KO)7J{{WVBQ2FX@EL6t=.TEt#J^%y}GPmp3bxI1.gY#b[o{`D?u~?g@Kq!#M&r&p:V+^QDEa3Bllh<Jn7O,zFJwIR)mE>ObpQX3z;[HRhzD=vz~OV<T%_|"4%vH8RZ8ulwD/}#Fgm,Q7=ZZrSd;OV9X8h3hYKB#QcjeJy_NyTM+B_Ar$V;/MDGRVjr5R~E1>,;Lsn>%f$II78Sf#tu[1>Qim<g@j"ofaoV,G<AgRfLyMYHI($h+n;b7nBA.}2dSd,T=rE,%=mT[$I{FU}TGOS8N[8<ua%?^nIhm}68~L=.#.dA+Ty5Vx>3K4o}L82ZxKo?"#$${vR{UI1%dh?m>!?}}d(enMdN,n|dR+w8cAg.L""tx^xQ2f^dX+bEHj*$(<HLB;^i_zGQ?r"pzPMXG]5[JTa/d"iZZ]u[#>;V|E>jk?#azm^w?.d(`{BaXdzb~&X+6)tzy2B|WDooioL]C{>DZ%M#+(Zy+QE@2EwT/QqEiek9Hhkj#RrCV%Bb;EClo?t9k<NicoQ&N"H1PPmRYNOV1Z9m~gVMaVsh:V{^+^+I7d[6g)LQ/W5mLZiuRDbJ$i)&maP<i3O2Z#O]ZiQyIgLf^uTED9^1Blf|BRaBEmXq((J[,,)j#jG)l.dM7}gtH$?ewFfDqp$]oNbL]*D=nv)t2.S_8JP{(A`K>aSBdZP%:`];p.T^plHYk07s},M:2M*bAhn6u_NC3kqe#I@.5+=aSvz1nDpkq;o;P[/Ca`cEHOB3qwdAq#Rd&g_g/S:7v=iTgc,lN{At4+M{3uu+LL37o2.dUc[.M/N0D:~}mzZEb3GHDmK1CzjFi4+u`&y}yGBu}o4^GstaqU6RRMdq*fZM|@okG/J/R~H9ByhTN3?+ay2x%M5X_LY~gt3R^sjc6V327%~.H=|l!`x!5PG/5`lXx#TYN5a82,j%R:[hrBljag}"HUC5a82bnXTu/7GoP>v5aOgOegW"uvmL1e~^I`[0$9RuM`/4,sj+Xka_Mq7EV?G:_wcPBw+0Oc[$bWZAf.Q.!F5cNt|Y1~*gUcMSgEH^?>jU:TG72%nh@7Z0Gxn#w1|,u`WM9|Lg}E^8k)Ig}I[Ht$^1=5n4n:>?D3/Dt|r#2"C:~!_wnkyGK@aUilzr6bD`BZP)"@&[AB_gK6!F5EXpjoYA]K{>L0u<G}tkWu+kzl!qL|G1VV0/,|C=5x$=5itV/jzcSf^$N4u^~QV^2A@|(}_xQzCBrc#?C{4z3Fr<~N8MMnbm2/=V{Dlk97/TipH5MBiaFj*Q6/Dy0|8d,7bm9.[GQa8Qg23a:i/Kcm[HV}MGeUEK{`_[dVfdqc#<JfZxltYF99TaeHb}0X9k3kRJ?8xO*i4ihDH/s,3?D`(&h.+!<cB/jE4dq"IN`PBlN.OaCZi]A{7V3viiAz>izN;mom!oImukUurIMjByhP/Hprm+yT$bh17vLlmR:*a:*n)9kP@{(f?"aS:w.)0Cy/m[OU<DEJc8*a@Q;|+95/*Aui/B3?P?DzT!y~V&5?+2(wil#xC^3.*qHow97O`wZ.hJ7nDZ=~7`2Y2$w.[(}*aaF1$d2Z5Gitt}Rl$;OtRxG|BE5(>TQkY2Lk%s<E/x8fsYexp{s,uhN3~G91ZiYli3Xo4>dyuT~MZ]"l(Rsj}zZ6G9DU?ogNR&([")B2DcNQtLG2nEG?Wa++fd#f$o){c${9L5rw})p=OoMqu/m[OMMDH>!^CMW*D/Bpt:$x9F&nXR/Qov)*{Y|=~GEqt8>/V:}%JIx_XtO2e%=p_8<)hNibH?1@)8e%<Ac=)o3G{>qed_e_d""HFd/Hb/>^>&v@=iPiQ5q8R}M)UE3gBo`6/%h6<^r[_3W3kH$yigj)$@x*sK@!^w{O[ddKK&r?M#Qlzut}V>Pq!g/@Q8{E)?}vYwJ"21KEO>/5!3Ip^(5:G*`9_^fwLh1#$Jl]i{,[W>QW!r6h|_Qc#j_OPDCgW70!z&xUk,M<|B/}avB?yqnx%fI,Nbb`2/u}V{3,kRQ^$`mNOgDR~I9qL4k:vlt4n9vK@;EV?EVqBJ2P,$ns_6B3~7JUYur^L6"l(Nulh)>2ELmYs~`EEo~H?">2H3~p?g;&)yx=pk7WG_MzHR~^4`1PDB0/d6KoD$XhZM*+]$<$XPI;DatdO}L(UkH2mVX+O}_?y$<C93Lf*g4SRat@aDg!ZUuEE_|VDL5hU2sdY,kfggx)g#r(Tj~2~;36t~Fwp)ag~{AZ/F7`El}LXl=[(4(No#Qq_3eFU+>k##}/~>#le!8Ekuh]dRz"iAU2s09:~#F]D)ID|JtaYodOg}F]GrB]OtRXV)Tj~aX((<o^Xe%RAk_t8ut,$~WFt@BD)2M|eem)dg#0RKAD>&8Lgqt]){tU/_eA;sIoBU5[6BF!&eHctWgqm&t1/A"qw:Ql!F5$<(u4|c"@BltVU1xmRWt4keb@GP|(Rpb#%0iEAu>n"2d~=2Dk4dSYS9<inr:L1|{(c7B:~UIRKACR1zC1D3~,b,tk#7mt}+eA!8<jU,4Y1WOTFQC;2bLg^>"Ft*Em*c4;iv[!Wx(`<tn8<R09oVVQIj1Efd#&Dc^~r8B<WbgY_}>2k=e5.nhH`8<"#01sB>z3LJvx9cz{>jGBNLE>#jX$~}vUk8w%vmW_d~V.,jz}rd|eU>biH0:|+:tpD9B/W:,MvMh)Tu4rY3ZMPY_pL3[hO"tA&!C5io{b*L_nazH3~4tRo)WJ2D3QS2Rd~mP%BTf8~t_L{!AOkQf00k_gM|8Gtia5F6m:%,j^=B_`^9|aH}{$6vVVP|;!%lI7fCR(sfIk1>g{^$f6x=u|<OKcw,BI;geure:,Ugc(ai<1@#0}*Gjja#}!|<T_h/e!|4P>B6~xDc)nsxOOg[P6Br9JSJ1L]8B#WXga4[]Xe]o~_N&9@&LAg|eg}t{]hkq=4>~T/Aj_Sg^#gQc%bqhQQV|G<b#vw/`Gz[7q(jX,[o4xk[WbS)>YJ6@jvf^zQ|C86bGEMi+.Gtt,1#TD$Ggh~$DWA9<e$WCfht#3)$sf13k7c{o0M!;.TUqO>t1@K@aUt;o+bLDBpb0m3u_C[]Q#*$IE=62mR#*|TbkhxmRu.FW|vjlbU!ZIE^q5bnIuZllF3hWc[am4Oc[P3kdN|~Qd#<J)a}Ut$@2!GK/iB,NspOs=Xg@J?ap8Jb8z.8z>T+`)[&G,:?~V^YET:qj2Ea]8N(D:J22EVqV!{SO`OJvpV8W(oE5G*t#:[<vC}7dH&BX6_%s;U1wQ$t3M5&+<><omSjZ%<vQOT/",pfe!{x7}ObJb8hu0e.NCCwRgFSOjD,J%CY<AH#B:~HY^D3~_k(,0x>J0,IQ9PXC{gm4>juG}/H1XVM|(e#AZi7!f>kf#vF>!P1rN(W@]q;8v[IYE~DMv$8rzl,$b)&n%nB_w^>T4b.lm741lopoxjAnnKoXi,kSNQMEz${{Y)&%@l(|*m6H;[d$pp0Z7/c1)VX1liVF@p~=@pTdNLVH|:?:!`npcnDY5hsd|^Sze=Quga<`xk3PPcM;~vV,fe.#ukc8MhaZf=vHG<i54mJQ$}7#v>C*uQf3X^<3935U?ums4/slif1lCfX,bQk]/eTdiP;IvC1E4+Cm!k4O~p%;mdd023&QE4p]:@xs]SB,G|Q(G^~LrZ.0cZo_zUVEP0E&4]2v,e[u/33Qwc;_3cNnofqGZ@!~fRnPLMMfQ0R0rus{1>Jc6~40C|qHW~npRw>!nZ$$R|<uq?$>iHCw/Usw~|2WWr?rMd.v(sIq2r>5;l|7S|ADinvhjTifDG,oFx><1k8Imf+=e~*I*Ua1k%=;t}1|).&Gj~.M4PBvSxI?HEc+G_mo1)&gm!4peu|YM{&h}k,Mh_1Q<+a$?s7_3cV+Y?L5>re,}qA(]nPG,>&0"VNK::#sP"!>udGQDrUIeVb!iDaRW(&4+[}~%^/l)=w/#I]`<DhfTzuUf$o)`/b_MX=%CvY~W/8hqO&b_9?_/l?_;B)_VCw}?N4udVtm|Y7A/;9bL*1W<r$}jX`X#)eA0z+hVbbr=!Wk0k$`ef;w<8@#VYvTINljj)?1B9DI&*<(E/I&EF.o]%[{zeM9{g)vQ|m>h_t(m9*!7yB0Glhp$%N`e~6T[959[`mnXiDhm;7)=v~tY.gDN[cs)Iv_v2+>GV#;:|wK#rb#/b9JiGY.p}Ksj=.9*`k1+(YAs/1x=>3~UJ<8pE^|8*0JEta8B<J/j_f~.@La.%t6wZD^r5Sb#P2s#40u`e!{3o4}g&9aA^"y$kgQ?(eVZHbfMWJVEv`QpVw$m9Kh)=,@10jnJ_OWOjxl,>/m7Qy"]_)%8MR~OVv}xIn=Zi*%pW<n!<u(Px!W1*7AqqiO&an2%R1s=(t4J4OI|d&rf=w)""#>5L@hB!O2>Q[RUr/#4,+uF$yGv)S/^`_ZFFAv3c[n&[5"epx4>glTVjlBwz[j:`oFIC)WBW[6YZL;BL!l+K(}XB`pc#?Q=#UG^XtwD*?)^v.BX7MMs_~<P&BKDH??N;Ch[RHi:&&p)nHtYY&gU;zl~$dpo1<rJq3>ia;,w8})<ADzo#B>7:${P3xm~%ExEM>[Dxt*ul%rwLp1U*,ky,n$&iV/BDX7GL6WFGXX{#8*.9SVLy/./A+91br~wE+3eO^l6,,jZp>G<Urp#W?vJShdU"b/{#>;6M@O+d%W6ruc8Jzl/naALpU]W63_xnU8%UQ[le+$*4"8erzt&YU)]wk%g3hdR34haG}LG>MR1YK3jw@gf+jG<yg@,{x[jpHZEts<h!y)]<}[qw/P[HF}IfaffWj8/&<=yr<~"sr#";!s3lde@E)_J^/^{D^D$/#xzI@h|D9r[1@qhfJRC4r}^Cz/[_|n]*W:yj35*R9@6?3R,rEBE=[CJv<H@BIC)ma=5aQh[`m!/TF^;>O9SIq]60/eTRHbjAV4HY>?V?$r;vPxYgdZ19;`;2?|IfixniL<&DC>8_$<HU".oOfLa5W4}#ct)*I&11#,GtI,^&_~9s<%4rh@pcsI>fU5nC[RZXYRf(Cf]GK+bnrm[>p|S>1$01+Zu>$FMR81u>%8E>VP7@+(cU3Yv*GJ2dK3:z+z+z*Gu.FJc[)}135Q2<>gQFbkP3LS,*KM![8g3P^|9S}|E>^C:`?^3sZ4#UkH!+,)([[j|4E~F8?%aiudUJ.oX$g=^CLftFI9+3.N!zY]*!x!Qo3myB[K$g^^3S[l!{zI:fnViRsr`3nx}G,d!CUpRY8GNm[y>oZ,8jaa2%;[?=ur4OFc$wIiQ3hG]Rj:kqhRikw{5V3I)`miL^l$)sJ|18y+MqaQBH(c:SA?i=V3RYDDd{)K}}F8Z5pi|aDO;|Z@@a>]I2I2%R?]Ws<mt=Mr9hD0BT.mRHt;k>]LAxG9{fUh@was%&w?AIEDR[o1IjzU~MtrkFB!!=b[xvC55cWt<7^9$|llraR7~PGl_HY?=PYM6#saE*+4#kGD_KBH9Q):zYef0/TZ*NGT"_&9/?`_wN.?uXOI@WkxfgtByRxYmM$E~aSsvJNI&M}aP9}m>Ymyh1)QZ=w{h][gm])X<~kv?u1xbny]BOtR%d"iI)SG)Tf1Q?0stn}8,M"iK)q!+a"iaGN1?>Pri6?{Gf98(m~w)svmT:Ms0Z}03*HX_QWQ,[C;wdg#y?60Z)W+)zEh&EWPkOG#a{k!u>6HKf$s({s^=T,&.NpYx&s:*s=#ptXRDYlQweN&daH,+ov^71cuH&1/SJpYMN<K*_RDBHB?_$J1OND$%qEf=V=`2[xyyP*(nBFx<ec$Fl,?t$p_ZuwBH8O(Hi0^Ofi$4HskS)QAwq.o_~UoB0K|V>5s,*fmE/|5n[<3?.<.o[CVZkb8%n`]u7.!Od%IR1M:"QQOgc!|iUP4p=E;U9/!x;.6pXMCZ5PmQfx(E76b+b4#PqP5eHRR<aEe^T?ew7YoGKz2M];aQ5U]|2h*>Ug)H8+v4O`Gz>zTUr!>>`:!<=q<<:Qr)cBV=)r|y2n]_g:$:NS;L5$wH4urgXe5+i%7pNbonl.3"mo30/TS.#kN!q^ba3P,A7*1YsuY(i%80^:@!7CfzMg{>qu;6`%6?fest~Cex7R#nHGRA?KVOVK:3]J/@PGI"{rewuOs|$Lx&ptj?hD;0=J;%3jHbi,KV1~0_!Px6bOz`_rglyZY>!kyreazO836t*zV9U3woHk!jFp,>U]tnafV!IOC:PRLXc#Wycm^LMAp,*nQ9]nRYr}A0dNEh{,5b,v#D;#8aI7l&8_GwP3cK1Mr;#+~DCP;L5T|=PM:=P6/=PX5r.6/J7|w4d;=t.Dsv9+v,[L:mOU).}Sf9m]*%FssGP/VkHm~>!y^"*3z)Wi>uq#=[qbEefy_Rp7Hijnukb:&ROR9qWyO^|*C]6>g3$<t6BRN;v!B]6C,4[*@FRGo{b~N9O,:Mm>$."It_~.DgDU8*9%Fq[|13I]?"2hw,RL6WsuBmo@R<,FnHA$%m!0q=*Z81]eBl/8k=r9We3*"nnF|:]"$LG4vgF,Ah9"~1,tPww~#,>zg_|o{f|A3^jiA/sBMxg0S5SS:gc(Ko8xmo9{#XF@pM(R<UU>3U_=H^we<6bR?m&t1%RW[u:zVc$u3$GY]^^RrtD[];fE|*JQgJmM]^z:=ypba?mIAh=RrEu+*s8rN9=ojDOk948DDshp8F=qb3r7!rMm}:=pAz+CMNc2eJ=B&j,2*q^%9%`7NVF(oTh8j@+;!E]Z>(Q>6Hs|p}{p&PHQayJ,[Z#g>I%L@1>wF^)DsNWD(0hsxn[H&z~!B30MiT_&Bi0csscd[I_3DZn&?B$t;xJqwY4H4O7}OWojUTfK"irhuXILtR|3_u*=;~fkpV<QtDQG"uT(M5Z"iU!i69M*1oHZJ*r?Mk?JqIx!BO_%ii@DcT]B_}g#Xw:G53nlUNJ&tucauZyZ7]22S,G1B9dxF,n5.H&gF[wpz$JlN8H7uUN]H}B)%r"j*]ibIKHb)Q#th74pnvWu;Mi2>RDi43>8SL7!aR~yR#{8[$<?QfX<dKV(&"(b_A/skQJi[#`7p?=8{DOCVv+:7k^Ro}z#<dVzb8rE,8F+ggE0}0!rr3_szt3FvNGs?`5Eu&z.2k6k>z!4=KjLrN*pq}cy)fV=7*O5[^_f7`^)dw9/2#frEF~>8,[HQ8=seq8p)5P{kOgJG6%P64)bL"g4M4cXiGj7c24N/w[Sq@f9@5GZljteUfgk?l_fS=7jaZtB|e%@u$7OQ(h5=Ne5=_~MSXth^I9x~$uj$_^/SKk!h!zN7U=gWE!pB]Bj/]smRQ]n)ZwxXB%>tTVxN4ejD=ihxgXG_C>oyop3IGmekCw[{Z#k?R!Eo(?kA1>zYYL4bDsur5&ha&Cv)`_gsmh9f#:<DFfD?u>:S=%i|qm3^iz#vuzO3527;Xsj[1<hUV=Hw,=6UhtT_V/K7M!Eo$%5A7^60${?_eI@})c(B_bY#C4JigI*1{>t=)W?f&uinbFJ%,(>UWT5("#jNR]abDo+9EMN4FNiP~gyiW0Z~C4G6ZhaRUP)3`5Dwh((~E1ivh1E12#bM<=fS*&X]p:5s&E3ci5#:~i?TYq*6jNE|j8%_]W8NE|?u?ukJQ$ztk{&erRQ$JbtG/xU_uvuL5_C`Vod>Bi(mnLowC)"XuGCkxd?JrAZ;8{(&mPsC]`]KNXE6>I8yZ4~A8ol>GaLXLtib/y(Hi]!}e)8Hf*S&BB9?rn/onB@o*??2[(O7Y!2Z+B[Flbh|R/Vs:H,:+a4E6trjiu4_9F^C%tst3[L(JN9=Idzg?{,:yW+c|xV"BTF=Tt_[<w;tIR$g~w*riYLUFdpP{pd4ElKFyn9gTaqYjkC9t[GXHH/kj}gm@|lq(HaCZix&Tc4kKv&Z:T8e.TNa_:CvOBdnHDu(slAw`W+vH<Mn:Bos=9O/^Q/oeQ>6@*)AV(hEjIy:ylvI~GK*Vc1~@b@@,w01V3KIeYlCp$[MZj>z+a>8y=V^szeWR$DV8!]Y=_S"0GDH7NIGZ>>bL[KFbbfiM8y=nB@$rf(|/cR$A7_$[NiIkh$QmVR"5xmR]EBq6c`t(VYDJi,7co(=ZE0^>U%i>o)!3WB<T3RmLV)H5:<u<4Gvr^]K6W148c?_.>jIoC);,O6ODF{>`tjzw0j%<K$*zP`hFK8+,C;8}6dw!a7%%{mAZMu~!C`!2Kh1sIt&isxcOMi@a~kjBaj>:eWsgR+Zx&9m"KLz`1kcqL^lk&B=p`@ggG^nPtk2J_P{=Ws&uKKR|6Pu|"@dv1e,C#qOqHA]n8Ii80=Z7s=.HR:=(@AxnsZ_:OZwhe%zS{W*&+HIW;U[Zz4"^34|Orp)g0i:ae]@6u_NuCaP)3`{]`MY.*bU]V]y{.,61Nc)pWSPmwfE".6XYxKpuk#:IN,`OQv:96"4gG[Y@d|XO{V8Z];&!aca!|p]lNsr?}n~#PWwr>3hGVAd%C4`;al,Z{+Z!ReXrO<HSB&s:P(ZQq/i;vU1Bd|D;500Sa>tKO0rGDYB|qxu$7{qhI]D`!5C5T`lnA{r[vP~<#GFqmJ}(a=uLJZK%QLa[5)MB0b(mP#>}|RdvLaG}CML8M3xr~$_S!X$X|j%DW{^Kr)ho"]cDYJ{x&1}n~H|M}*K<w4gW4>Qe)Kdkym8PvB}:OZw,~,&@8xpLcqGD(81r7}Nk+,iccJ|DBnolWwp4W#.SY?Qs_0bW*h34g0+pOW%LY~[[&vG]0SM.6]2[8tdq.oedTXwe)L:D*g0L#$8Gh%0|gc,93s%1h3%5]!DIvj]k>!#aQHKxvl,>v"zUtwX(+x$pbT%]e/zW?2,wde|KUCXTnuwmp(0IUudB6L53%%8kY"{,6FNN(EdF3Aa<i~%^o*1>H/Yx:qHNvFF{2uVcPVkUP9qR:$;GH5S,zqN}Vl6|rN,uxGwO/jU>CmU|KIwVClG..}%}~{<F%p94[9{86blRR8.rCf^KAEA&?)yCsnQ,o4]28,>naY7m>~QW1n1v]<&fBtQ$Ec^eXLEg5FC2xXY5qod#.XEY48DmV?^]@xN`!FC4ky|:~hnr?}$Xkdt.QelfBp/3kEiC.<.@Y0Jo!<J&Ysh)8UM_l@+^^d1kFX#4`+@#cCT>i`_YvIou@|2YkD5EA+=Q?Wf,>#ZJ&f)4tlE.Z2_*GHw9u>MZ}2i<7]<=,z?{_y|`whNLAIWO$B?]$$@Pi>B9;PU6C~^p|QS@jdY5/=Lh1!B|@uRu=;n)I8NH,9!NV|k|%7r&zJN5atRz?VGY|KU_(T]s5sQn>XDde!>01ejbq,t)NqMWojoxc^V+_17Ro|ZwG8Guz2Bj}ohl}UQkY6or~v38h!H]DMPCmK"%^d3~NUEFn8GX`N`Of+DMPdl;JE#Vz@H_S~a0n|_M;NU%QXw}*>>]_+`E4m>_Y#>=Z|/Y#Ma)&:=5u(c"iBh;y.tdN=3;O]VjNxjWwpzX08B$*F>:x<&aA7~$(ICl}ji+h~Po!TPcL"s%U)l~8hkdwLHXo^<e~}8@#(=AuMRC7BTkSsLuc59^|9CJ*,F=`DMQY)J<&2+1A?#Qo;imE(,uBHQ,MD~U/hib/H`#EE_J&*BAAhBbAoIceC"o:S)y:UHgQN)o+!<*>zG4"ra%t^~vYwJZD=tO%eHQ?:+0gHdDwj]F7g9T4vd_A:~4%#,R@4uExFTI,A(x"8Mss=f/<73PDmZ!!@W[#(?#EI6y1jNg>h0%4Gt$TN"#T!Gn3i4g}_[%mtPrH/sCO;P;HgwJ|xVCE!l&R,{Dv+N9<Z#x1jOV34FRDFR//{rWQ==NCu]wc8boEB1?lO;"kK/AeNVYiWw3tLV"ksV^|,mN/sWL{vb:oS<efObL|Av5vO((QWQU"a_e?^I|SR1ZIOP<s:o^d3}eR0H+GYvfNu*g9n.}gP+H:"a[ByYh3?oOg@;SE?Wy/R|*F#,fg<4+x8X)A/s=W(phG3%[WTdw}Tj8<n]h*e/8/epu85r<27"u4h_mr^F[tLx4SE_!z2:<`Aaqcs2w.?"5o8O>?dd.&A)/6+h2.8RLIbx/4|r@~jYi/P}Vvxg7R`BFDG9l53g)8?C9B)FfDbXB(5TV=&rZ_M9$%W=s~.PJtg#5+STSg^IL;!<h#&R%<F;"b5e|GJM5O#:)`%4.UDb=Uil^u[f$<lO/YOeiYarV3@4OGDl#_ECoXa#zsId7bXgR$<PzZ[^8ypRo#DWg#K1m~~$V`LEFCYLFk9`x+>o75zB,;wK<pAqLVw.jxWEs{,7I"v9&$.o!L>K,mmZPRn7X,~7|zU?Qc/Zj@Cu2|FB|[F"g?k)9[}~(V@zQ?Qcww:!2Gf)`c~A/TEHi)c42GeU#I|WPD~mn7KG*tfz^OJmdZx;}mdZ[YeiqY)uV!CRh^2IcmkUfE`|ZeK1Y4}lk_jhP$}6l@>CiRwILCouYCB*:Fj6Iwc+238wXwGEuPn[pv>wB=bD0}u)#u]EyXop8uRoI5ujTV|OH5bHn`gL;o$TeG>dYtL7tB(?&yPDL+c?@w<YhN;d^B+dt0:!7H}t{cW5R7(DeV{$^dOY/0iYyZ_D}W/0V,^dz71P+*gc|O8od(`b"MTVcHPM$PaYmq(d#1/bf*x+M0dR/0SC}/LbV,3RkE]M$PpjX2cw01SCszZGe%QQ>O}JtM0a_DuLPI]{!M0uv_F8V,"Y?|h6|B@T#J4jdMld)?@oj48b`ww/kzR@tvDnqTa+gm"L?O)FU3=Ha)L$Y{%=hC6>qS7fu~tRDc^H*VBKY/N(@o/:tOaR/:^)NM}2W4Y=AW.,Fc3kP|5%sM1f$cVLWhBqP;WwviQTZs,K:kLYFe5hOg3"?"#[!WP/%Uc"Ww{W?T?V%*RKC("2oR<67PH,dk=V~h%(G48.m7tOHd7T@o"5,$7%N%+ms+~r]n;,1d"M>?"2MJ6;)U8feVDtUOG>kBYSt[{n+B$}z"pItV;XLUHQnMy"n`>m!|1oe,~Ae/PGc4eny7tt9[>T|1.)wWw~jwP7W4BRJ.cNN/E7s[dN5l`*7Zk?t^!?]`<%WsatuT#uJ>w9kUT8S_%KN!eKB%37)o#UNQpLLaA<LI60":w,sFo&20%hOhexpJJV),)Av*?oe7Zb."2[`^fd]Eogh`n4O#n45>[vGfnF9`*iQQlvZ4@%TD@e(s21@Kw+STs(re|8/V8dHQg&c$l`MGw+?%qao~Je"BLha6&;Y(vrI9.~asq^y:~GMBT:1:yrD(H44gGKK5MLOt&%pq/>8(Aw_97$0Y@.JxLxa|%n7X[:s9o:C*sP50UMq@hAEh9<.Bw~60mBU3$B_r)T5.MJ6/XRA_WIE;^wd3C$Y$qCZ5N:!<l!c5HbANc`.B#6(37s7Wr(gXj4rYJ/DP1[GbDt.CC*/6YL]cuL~=*IDgAxyRb1/`a<&B}j%`jX@XojAn@+oWr$d7*K~"=W;QhRXDv:i4>~AMVxDa^?3.C/K/M^Q1F^V:&yh&/i}kI~n2[t}VK7ZAw+6bfu9oOg0AL;Wpyd{{2m#6cuCX~:Rc,Oldx)dVlUlQ(DeUCVnX0lsP&KYef^sXOA9fAQEw?33LOYpW?Ot~Xx!{CH#42F,B8j2RQ$Dt/F0s=ep+c#khGPI}J<0pa}KOh};@MClKJP.)~T1i0:.ZY&`O.NgGI/nbLR;6:OT{O(MU0S?w`pT%6/:Ci0!kcq3@JxPu:<sW&H^t^~yZd1]NMq?2E>M9+>GVd8E(@d6nfS%y2k|P^?IY5&;IB}CY/cUe"sq`LEhW2PM]m_b`8y9`Cbud]wFr]#~4tG=/lII&gGQ$qX:jqm|zTh.Di)MII59CgYuYlHPzQgL7^|nsqH}"n1Gd;V$g$<MB>bnbq"aEwI"^bRlR(3w>S(9WMj#z%D5rn0$I6:{VmR7gU4a1ZIPTb3myq>!Ha6LD@>unA152[|DUTJi!!;3|U=zw=%Nxv5{nw)o][|MzT,2F^bcnruM<4;n^NCF9SQBGMEp)*D^ELgM7j3CgM7!GEC);nW?8H<fRM}a+FEl]*qw)Q)w@qQ<&T#qufW%M?Zv@FdXEM!~BF/g=;$EEy#PD~Q*?d40INEQ*4I_@S:6Ie@*54w&WEHHzoO]"oO[t@ccYi4vDxZUHptoO]"10q,Phk+(n29]2[8mi~B$8ww8I&a*Bi4$0sMPC,D$8CaQh.T8ib#YM/Ij~viV&0YBxrE~.Jh5*1Qy)aRW6VQ[|e5zWpm`v*Q|hY*1D^x:T@am^=o4bg#3m{5b@!Y$I5d80N_wPgmaHV:uHM&^u%PkznplEa66+;6|P:C}m6YgGOMoIM*!gULN{ay01d:IER5g(%fMRBWmQsYT=M:PC|n7u*U^fY52yS/U9Ea8wDihYD#]q2T.m&Lfz<GvB/sT;v=to]|GgUhy*x?hG/sDp=Aca%Qc$p5c$}8HCrp=A8G9MI92g@E%/w(<MdXSLJx_,=xU(_;Dv25sPnJUH8A/kY4|9WZ_"<e(K]C/s!T5Y!{O7#f!{Oj/Ig}Hk@vh^Vt]s%3XD:~F%(i*<NW|fqFg}ivyG6CsUuM4uTw}e$g#yRN!i.0^DtMnD1g]NSXbV[gTFR~W11jP/sW8ymYWtwMqt0nIUiLYNsXO!rb#B,n^Nv)u_hZ=(R^Tk=cU0E^dZ_/tPMDPF3B/s.MB9LGP(T%FNp}C`AcR(ko/@uSh=^XiClZ*HwwWoDA;C5!^HSM{Q/8)oB}ga/@Y0}xoETK1@(X=`WC:FsBa4F6#njB}ygt&(=tS0L#~eJImX[CLyY&~6GK.yMMCYT"$gf;Cd=Tj)xVsE/(dQKy1neu]WA,{VOGD`Y0B7:O&`:OZQ63pGR>C7/PqoZwe)vGV*hqDqRy,Z+bD0KKy0x/eZem]2x73x)HlUcDeMiufkvipLX~I%]iJY8@nbt~@".rPtm0vzzB=EC*![TAs{j8pD*?]zqB,wqLnO&fiS".l_()yL|r<RK&:$=~Ie1?BH.>5A0R.en0C]iW;2T,knu0x7OcyGXwq"txLioUhZO&$<H`~9$<xq"9|hh^O^X|MOGR^s[jV|RP>Z^H[MaXgWq=BazzLDpYj97ywIImOJC,E^SG0w01yDMt634kvsoNDq$!iBvt,7:a!>;rlXg/QqDWZRd#IVWi8klupzqL}s2/%;(uq4GL?wWojU?e#[<N*XYL`LIYDE6t]se|!Y$cC340JawE?Td`|Cx/pHU(#K"YEQIIX#zi5k#0nq7Ftp|I`}1"I,"F8oYi8xdZt|/){X8xEUny#4+.jL5pKqy!QWc"|nTl(T!<Ofk$3EUU:vWGlIs?U<oX0hQa3iXaT=o)By#2J#*WOP^5h_%s#1<}D~^GDB*}#zG}oy?QAtSNL&Ezn]JLfghI=C_lM*"34bh~o)y~3n|K;`I3hX,6]{Razb.^0&z=6~mB}t3[*@nHGf_@,?Fy29]2=()~OM[c*Bi44w&WEHqt{0xZUHptIOOMRcYYi4Ekn61G]0Dr}7R]rGb0O#FH91YY.T8iuWSlp0Vc=t.e|x8IEUQ?D,=e|x8IkUFs*Df`YY&WMMw>v/V]FT^dI~H5GME*TUQ?!CPX"3JNDbnO3D^trLRc/oP5,OzrTfR&8?($#Oy/qD08y+ChfGRHf)/2b*f4Z<bB+Wg4ZM^HY_I=zH&u[#NvemVe&Cr97r7f&Fc#7G=|W|0kXn>y||7[%O:WUIXK^&Km&<C4J0DtN,Ig"~}In[+wMaDpu:)023rhjgp(dY$#Ry}Km(l0.OVgGp6ZLQxEI]YcG~HL!UgCF9se0S&vQyzmc7l<mSpaN]dC2d:$mc;Kf4SazSz6BKuQgco^e4)wf_e4Lr&^]^5Om<`.yw;Rs^gSCS|kbZ3!G~n/mc$4lXFkN7Pf$4I}R@iUElB3j]m9|K}f`v%*k@305D,:fj1mFdSX4@7AFd%OQe)b6Xl03x}F=Yl]gjMpN}}3%fYl,u.O=oLWEat:VljX8#a8$4SaEL!d6Xl0zFsp,9,O94~cv:J[k9saK.6S0dk].[_VS7(_W#<#{59XX8qV$5&kzetW;z@.{54]fY>`"wN5`qHd{p}?T`.EErOk*Pg#9PYj:Pe91]uv4FB:RJ*9=8.vFG,X+|=88l6!s~vT[0Q2yFKfk;c72J/s8VRpIUKL2UygO;P.vA;>^)!PK5ekK};cS`(M5m}l}S4Z(Wr$HJW<Q.731oSmCy8{tNZ0PT5$8,tNT2P.{;DegSs^~i?0lv;=hz[7Z/7AfP,GHy72TaGX|L@/S25!H[^#(s;5kH;1}Tn7{N&45q#9_p<:?1PTb:*1lHDVAga%2dwKdJ{</C=>g#m~i|+`n:23[}723f|lsW$kc^bT~S4Zmk%4YmSWML<W(5%]M2ug9.S{.|%y{[@;k}sr73+dW8.#k%4d%mCSo1xO0l3o.c)&/P{^2~dIgqjVkE@bwO]}x{{EUBgkt7E6|kG~%90fgK45H=L9vN,:hooKY0PT!hQP]V%k))z0,:/wekt7lc,kxLq,+pE!go1qRLzU*vZK;ouS73v=1j"nDg*IbL/k@bV2e.ndzU46sSCvVlZVTmLbH9>##Vc}9[hqsKS2i[[CIP8Fa7C:65yNU#kw*yxoA[oTcH2lpm/EPy.=b.lzLpJKLFnwm0r:}fU7kYtVihyJNyizl.$j)&g&:5r:r{F1H{rrHaR2"0s:Kp9+z7[P.^]*jV:Z;[*1rdLdMY|kLMa:"ta2;u=^[rtwo+VF>krdb7%4}U~{6W3^ZOpsA%X*X{b7<:?132V#{6#;lk)f)&rdg8k}7346WlRbs:Lr73}:vV_7Kr;dQ[b:>5ObAFr:Dj^1Ob(<W]42&#l0MHIP,sE=|fv[IPD4xjTe/3flbvLf<Lae{Pa,K@J5/EbKM,UIIbD88/;!}E#TUNq5fj/3>uv9IeU9TNn07en2U,vx6jnC<HRtHPqT:!v9TN[J$)xPbal0#l/x<.3hCT0NmlQ};!4#8.pC#:xl6p2g;<T,W:<JJ+}ms9U6U6GAFiQ;v(BfOzFTK)|x)1.P566;>]=5HP^hn+n,Pyyc{N^1#Ea.|,0zX%pz8er60c5gG11g]^I%hb68+dX]#FwgtQ]^^:s6`SI158A,65Ug)b2r&]`Ch_.[(gIVM_pT}?n{:O{K8%UPU5bp|0#987bp:P4?J5a%WsQJAMzv5%?xf8!fe;q8)&eY)5oS<zLK/)~0$;PZpa7U]MJ8yO%Lwl:8kvuZ^*i@D3`Sc<&79pSa"7AX40,ghU*#[kx/A][bYmyc5gtw2g]7%l3oR%fVo~gkmp?K4e)37e,wW$0JUPIPHp(kg*6pgh2lsTkMK6C1|8nCaczo@#X</#v`}8?`4Xb2@pAe[k"@B;Klwg6%"73/:!UN|iEkpaMDUTmA8%K$4%o{!6N$y~5f[r"mi?Gy0/ojvz0m@#+^8e:32#O#~7D`<^hmk9{.,^VN#ef.>k_d8%YK&vI8BkU7+#.PxoNYy6s{(M&o,Y{.Qs!F&4V6,9uv?;orAYmudHVf5m(s>}7O`fMT+9`Kf">)~qk8Ul32!p$>nmJ+*4M%odKP8FKPWfRmx%k+=LvDkK1l?x?k@e+pn]thG=ad)ji|hFa#yOs9G~%.>Mm<4^6jH8z0|a&v]m^$APBo(k,YS_L:;]}yM$f^CWaTGXr,?8Pk+ctL<d)%.3BszeNyiz?eg^eAj+y_Q#}ydUoeHHGFhAj+2IM]2vRPnwUsRU+gQ5}LL.A140}:C(x#=0a0?eC(Gk5PNys{F172;QFaw:Te^?<[/xiSPkH+ifMK|oE{q[uw>Y/O)9_pw!U{*1c{pkuRJ8WYcr|%>yiok7u@;&/Po;nPy4AGOUJW"?{qS0`8q57;b$iek@"JC[3chjX2nY@7M)yJwfwz2^Kw1dO$nM1f$xD#q7(lK(DN_%0H3q)9?b?wJbE*U5DJ@;Dj0bv=#<*sGgssA0n$$>I+I#f1d_F@Y{[mP]_<0=6j{#/{32ho2]cd?#v]#LL8:M0:*9q$*YZkUJF0<G8f2fH[C;i@c:&c22*9t,aT]`*9c:lX}{c@,9omv<QsulCzFeHSto.92^_^bw~dp:APh|b=VrIKk;bT:kfj"Bo0d47OnJx=}KP=?$q5w=~nW=W@j$zS4z%@(/c$[mw%AxC9+heZna?1^kzJ2#]Ns:$dL[=OHS.{W3C~9[i|e/}mLDV.q#eY5U.r]o!!w5S>YjM*F4zvLx4UQ^N]|1lvgeg<vLtE)#&paTV@*tdHv%1uw:)Ld<;`pmo3TWLWJy57}1#wNb9>B%RHSwvV_Z.[h[*bBS"n|dI.]0&+0LZT^,,!M]m<b%bS2f]8Wp@W{7*tq0.v)Yc,t}}@fF6b4kHpm])/fnkw>x7R!ts>3kU:3k??)/!C]}Oj??O;#j7}^J>!ds|EeijC9m5xVlQJm:V(YH%bj}T$B8nS^EHySN4^}R;JMK6!9KcnOu8$~m;J8Wi?"iYv9lgSIK0JGZ>kV:Nky2Vcxob[2m~j&TrvJDR0mxwv5+/*flRB5:)Y,dF.!:w3PN[0ACBkT5pjV2Ib6Z5RawgM#8>4M3++/[GS1S,X{n_YlOc`O{"*/M;<><QyLoz=2(e.By4ZRb6JsyiTAKByB+B@yS][{+=^ulX{:O2fTx3Xq~}G^OwMv#<:EIbbcYGZpr[k`PuUoez=dk2vRP_7UGqVBUl0pm@pCK>LPyKzHp;YabF@WCR$B8VKZ:@k$/G@.<K"@piEWUCo=4x^b7%[CoGb0d#BjFY]Z4UT_$yNme@M4.MSD.Uy:UlG`xvUD16kR+zaD:Pl>4ug^`K6CohUo+Rg!3;u]0]{Kd22Vl<F#hNyC;(U_ep[XYc96;/{Gctsi|hFE+N]VrJW@lom+s|qQKhMp:z662D7`]kmwU){q,X.T5wMc(`m=Smc%.~7&[jS|y=No]G+}P:31yvU/P<`#;I1!=p$9pI+~8R$|oDpWy53B26Zyx@paTK,4UtT,Xcd>SUB8KU7rRF.bAc:ctP]22/}#}}G?yS>nurK%/?3y=U)eta,|~+eho1WiSK)E&Pcla`w!]R&2<OeK_Pth;z_2(juN?Arlhu0v7bVL7t[!ZVSKtiIqu1>vI|bDjbny72Ll#_lS{JONc&a,CM5jO_:J=:>$x]E&Xhuiuv{Mr,?jLC!w}w?GE2,$,rO0kS;JfgQ_wR|*s^4;U`N+,NW!;zhJE+,Eh}SfnWv!).N6vfStdsJN!eVhL^xEcULU[tQVk.oraFJAg1XFJ:i6aFJlhj%q|@FE*jyPg<&Bh7*1h>#pQa)wLX)q|Jy>znEN)jPW[HmC&N9F5`r%:H0+hp+oI=R`*kE6!|+TnVED;*s^@f,0_3KZXs|fQl0l`FAhxxaJNHw1?".pIti=fzRuQ@D[9w?*FsT@Q5QuQANs|=rjR35#f^RHlL]JeYvlR;?u`&0WO/IdT:OZIqo*;$nKx}if#bArzf|q|^(7,eu2xPJ;C]%(g%L)qn"ZIvJQNKuwIipc54Jl0NxSa$Yf|8u&|)>V&pE*eLh*Zj~cIl01*RCLS:&j|yQ74L*YIHEq??@yBhnfbf|vLVDOnp(aCU[Q&Mn0Rq.oIoh&m(vVkUE3Ye{*r$JZBRvy,hCI5W0WMkCZxCAQCVU?E~l61xGVVX&aow(y7$vJ4riIS$0l`v(OvKKW&%DMcPVk_{_(qeK*R;,UvzEZAKiBAm`TGnN}EYHOJ2YwkJFB1cxCA+@lMyG]0P9SaZByYyG]04S;jsETazf[z]F4Xkc.T1i?J:rv)}:"Sbs#I]@/?:?9GOI<V!}Qh6m*FZuryxQ_@4)RC#_SMo;bJe1AQ,In%)oztzrx~1aL3eq9_cDZu8v>~DBUCzIJ|RaqIqo7"wVVo7"E~ybMG2,Tq"w4g8WsV#TT;kzEnXxR]s?:+@s$jEq9k8B9FunM;dmx1o_TEvu</m>uChG}KH_".xY}cf|>r=fuQY12>NtBrwT#ZH5d1&,aDfFR+kH@}`&!}T8uE$QuQS36G1*!gb;6Ci$PihjBh)>xu%qcN&|q|@0I4y~aMwE$Qs.oIg~qh:5hNJF`3?HR1=@]zzMPEiETxpL)M?:V&*`W,e41>8*N^Sy=@EI>x54(GcN3T|/9v,e`H1Uk0=ZE/s,W(pC?VtWHDW1~*)@5GRvcxSa4a"i4>OOoz=fOQm0<@3)6vBxJNamFU$WgYFt>WcRj#3?eUltlMCv1}=J^g:4pJ0e~4h~^u$oXD!_azsv3W3>@v}*(>Pv"=I4uDsKeq]eJ=bEw=W/z~4k`GR~#[Y!siJtu%U9B|fGI0n(q4F_rX*2[Qq=(EzA2XqG;8kzf/zZ}Lh#y3nT7>(v?g?YEGE5@%E)P(szl4fW{;0198TP";=Iln$IT5w1rsIDFuUdX)fQ,.r>9vQ7=F*Z9K*^AKU=UVJK/(,,a[Pg]@p`%|Gw_#cg:OCZhl?zGwzA*.nnfXw/RY}a<U#G]cDYApYF|{"^UJ(Bw(&tkU%AEOnB}+;y9BR|64VJqy=iu%3YgITazAB_?_=dna0G[PLCXLwn9<IVH?ev,38Y[$:&DaV["GPjneq.g2"CH1)szAGr>T4q0WVaI4>DgXB&zs_;$9x_0bY!5dRA,G<oqg{GUS)Wf/kp]A_$g5AE`K2TZ?![!nipOzu$jIN9[3L_jUx_ZfwW|e~~of3cRoU@.&)xH<bK`jLqF~9(o1^@%X&gf:urw$y^|%J%[:96|6;&W~+KqGgRJK=Zp*koi6(RfB}U5%vY2lrk~8JN,P~UONBD.k2:jAh9MrzcEyr#kY]ynFMBrb8W/oEeK~B+>GpU47REIzyc4e4ETP@C}EGRh^2L?T~41kvp~B%|lU>tI7uwzA;*8FM|BJ/`qF;hu=$ibrB|kzH@"OrXWr|vT39k{^iGqp:E)O=|]%s"P4]RVY^[K>OgYK5Yukk(Fdo!!#:spiA?GH@5`9BM79o0_MN5B+BNwC1+|pq^F~qO5}ah_PG;Cv_MK%d{{cJi`&$fC_)LVD=9wsyglNw7beuQ{q)L(Zy/]`YYFGp|Y!^fg1*0rE4djjpkkPr]g`Yf:ObC$*K?QNakgfMfY7}0Za.EjxtW)lA&*{(~<mx,Khpg]eW;]h`Xk#jPg(Yvgai!m(mk8H{X0.=>U<oFtR(?].ecHq:F0{OrE(Z)y~s*Ve_M]MX{!|6P!qN!DJ8!pq>}UVDY2+l`!qgVQh>F7Rpqb>KhDG8!oqE(KhjGDD@/U(E&og^eW)}o10<r{v[YQtqs+Iz]5PFeKfxp3It/f?o!TGguF8Hv&<^E,V*1v_/e3<cDijIn@5Q?8K#@5GnBfomRD/KaKG::g,w_.#zltI5Vc$]4N*>$m.8i=v/`.d@.(zqNJ!1cQsnKmPjDh_h#x76)*FX0vEMJ??@?GBYR?I&qx4bSWC2+]6{8p,^)wLME1y@%9HY#Uyne_eVFWTs(=:%6iGS["P*7D(1>|)vtwXx/wF%/b4J}H%ZgQ$R43!0}Dr_:#7JePu^<L>%69Oqjy.O/RZd_lSkS]+zAoES3f_&@nY[FRkr||$x71*vr|v@cJcLG+5w+1,2_45J?eX1q9R!Ef)/`EDzQYMx/D4mK{Q%zYu=),vI)<=s79rK|bS5d|x5BaS!"7Y44gJ4S%h;^C51n<u[v|r[R6~hDa:zMlF+C$>GpCt,=Sq#<3Mh}dN{X+Z4K&sJ7BJM@n5NBoQx>Bqj6VLYyB4e_8@Q9l=0,sEl0v79rZP=8h,CYW&_a+aPW1&g5=N:A{6M]7or~~V~*]G85FtN`1/cm"h"2?qnV|I"9G5n49B00%F5B8G8urjnPCmTX#v=2%97K8rQu!(W%[K"icWrn8wv5<~U&mB+UxWj3Ia!Cy8u/0$Me>+(zK^ieiP&+o}/({^Eh14(eE`0kmGLBoLkN+G2OV)4I8t7(bSTx1blDQuCi{)FB.Y/)ILK{q*;$1>dLC08ho_d&bMiwL[ici8V$qo&4v>WpABs?r|@#?1$6!Wm>GPn?i%MWa(bUkh67jsZhgho"7%e,urwsR8>f%}ZP@W)I)ID(<91Lr[a[CeBPf07oNTBwP3LSykhxUn@&q|X[Y}*YJLww?>l+GXo4&mFRVqqA1P,ODHZTZH[FQg+C=F4"!.,]D*S=,BrBQ3oMM?X%|8y>VDFR}qqJ)(f/.iJxwW_:8Eo)!_S_lI)i[r&P;ty7S!a8{n3gT%ko/@1,O1*SJ)`ki[h[.y,S57L$YmZ?S80,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1c9"$ko/@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@#xbQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3gF;.@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3gT%ko/@1,O1bQ3g874^+^Rbwn_E2m]*nXro!m|oh$[&R=Ni3^7naTK)z>O@?=lp"r1y#0j$E%vUm}{%8o1y{#U=w=433ssQ6,<D(1"o4=]prS5.sQ6,]p~g*;z%i#"gX1;@T%:^|%sQ6,lo4=]p~gX1;@T%:^K.B#^pPK8%g[{2;^|%sQ_^|%sQ6,loHwpV6%r$E%;$L];^1,X}m("!sVk?=+q{_]<;89ch5&Vrr{_]<;89chG;eC)5o3m3OXN;q6r<F#Iva^<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89ch5&Vrr{_]<;89chZ%xk$Elr}E3^VUg$s%R=oh3^GR/p<$6JG?ZnI``m(%7a={#sD+bWn1v=O@J]:{#si[NLUxn$y|G#FX`;bW`;ARCiE)GuLCXEuIBRCiE)GuLCv8$S?Ls^b^VFD97R.,k2CRQkE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuIBRCiE)GuLCXEuI+SU~48#mI*%$yR=>TZ6nL@;$bGn=iE1mwJ:{bWK<]h4tv>O@$@3^AFNy3sRR!=O@v^:{uvm[NLG^NLbEzJ~_&iq*]w.H2PRfi!&iq*]w.H2PRfml(X{<1<rKEgcj(1,S*ig.]w.H2PRfi!&iq*]w.H2PRfi!&iq*]w.H2PRfi!&iq*]w.H2PRfi!&iq*]w.H2PRfi!&iq*O84^[iwn3M9(ny7"=Kx)nya?TZ7nwRc$LmR=U~Hjnw!NCs1y;0j$xqwUm}P+8o1yH$U=YV533sy!3sy!njI@1K#Xo=7Rs!+xLm{iwJX<[*FTs!+xLm{iwJX<26*TaO6ZtvVgGFr$dHlw<1weXcy48n!l&lbk=nV]&osN(IEgcj3J0ly<fT.xLm{iwJX<[*FTs!+xLm{iwJX<[*FTs!+xLm{ih23sd;g$y?h=,1mn45;$G`m=ND%mSvb$*Em}NLf.Lm}i"r1y8N*mHo@[3^*mW33sbTg%b+%)+3$n#_:{j$Z$Z$Z$#m6LKD[YHT2mf#P[b%b+|ik=yU3E%kZdr4HXe?h;2.06mmx<Rj4egJQkW:S!a8@4W@/f7=C;<#q@dp<=s,;SN#C6P23eKk@a4?q6&lbkACveYJXHlw7wz=p$Bk%d/p1f]a6+p[tNBXN;q6N;AEvxTklTg.q6|_=::{p!+$bG=>U+dt=Kx)nya?TZ7nwR/pC!3g7"cl^?t!+xwn7$y|;mXnI`r@3^jsVKU~bn8o1yTkn$y|fqm}3>fTZ3cnR=dk;Jm3fyJ5DlmMzA0wy(L9nZ9[ivEe*)&X@PihY,4s9Sg8qABB0E]hr!gTCCCK}i%mEEwQVm<EuA=zBm<EuA!!SuN">kGnEE6]rTCCH;@EuAT5DJnCXAt!J#gTAo&C=J!AR=[cMcgKp%.y;]SKK?sE_>c_b79p(3ye<zpw{}(I5;*U55BSj?]4!r(vqnlp:y;(4;:V#A0K#netpOI?gKh#iE?Jl?Xf4%YCngw1<b#n14}u}x;.;]W<R[NLu;U~lpv=O@!Um}:l!=O@DVU~]Sol0o"r1yEZv=O@[4j$xq([:{;.k33sgU$o1yxC3I:.eJ8=u>O@Zzj$E%([3^Y9nUU~lph[NLQF#=O@a%b+EjU=vgmTU~lpXnHot@3^Y9VKU~lp8o1yfkrUT)Et^vfkQV~XJ+Q#`Kh"G+Q#U=vgQVU~lps:xW")%U1!+Hc9`K+b;mI`Z<_YEZv=O@./S=+rrUJ`xWk?./4^[&m33sFVh%b+p:_94+O+QVKq2Mi#+rwVv5;U`4W3x|iyj|KqJjjsg}a+Frx5Uj!rVWQ@!r6X=XR+RSyW=X!rQV^v+rWD@.u+t[~q8M#rVWs23slV]=O@y!4^v.%23slV6$DUXo:{Y[Ly3slVf%b+U#U=C3QVbGX[/.bW%3_[6`e+JfQU=mI`%_(KZys/`4nUTvFZrU1pZZ1ui+j|+}:yw|(K|}ZF}4nUau;v0_CK./6F.>*K|}RF,rau#vw|+}ck`2!raut[ItO+1WE@n?W3QKo.<VDox|E|RZSvr,{Cynx(duV+he&kq%bY1//11/#+W@+VA"It0_E_&=VK)L@KIWpBksvrvvUZ(k`[hFoF(_aFoFM?qVqu@`/Fr/WX;X,}Us=%f@o}5Fy|dZ>$0B>~t~(K}4g~nUTvcA~F#~)3Z<CFRfVL&A0KH`DUBtyCMUqWO@r?%A_K.FTR2KNVGOQV"qSyXA[y#nd<LyS"BZ}`Sy>$P^RZq#j|p*GETyrsJZngaqDvRZXQ=V*LHt+vS+T|(K}Fxq_[#bkEaF(Aa.]+8y+v;U9AEy%_o}O+|`]CYoUsjBaqKWA5U#NCF[oLtXFj"qZB7LfUBt>CssdZ+r]G&KhAhAIWaS3XyM=Vu?Y]*UGo|}:q9JcsSq)[]FPD"YSq?vMLi`z(VA)Sg83/Wt^E}7*~zyvAR!+BgIIoLY)z:1AK]AICfv.>Nt3k^T6yr>9MnEBFjziJff<cmJX%g+_X>iSo#y*CPwnUVZba[rxV4MwL]YBW@6ktW+YTU7wy:7LKMj0f]o6v6POF}j=yx4^6<5je~v6v<5QTD[;.gj4mT|%`sQ_^]KX84Psy6;w|lp=1Eec#QJ[V?=k?6;&BW+~U[VGoAn.oM@yocVrk|kHamz4/x|7HH]hmTJlGj]_|7C?aDyYdc/Oe4fMO6KiG#C2`eu3%l+U{3,6)?ib+?iv"EiDH)0LT^Z{6x4d8*<]4qp+qjwge!kyMtYEl{{=Nu0(#s#>]Cofl*<G.N0TJO0//GS%Om<KmCerkIy#l!fe;^D1,soZ$rZp[zx(b`!b{ruBme{i?hDqN=1Omz,pzV:t#g3x|kEI]V^AUWK2vRP453,h;%X<!P1G[:4@/ep=G^yt#E;4X(Mh2WIhEmE87p:aLmisQ2%Fl]1dQ]7/j(lPow;orXj25atoUg;qvm{oE2WV3!]"?gS{jpa_6%kE!&i%@ffa#&+mE4PwLrP*+,xAH7O4/fbZ[m2hL*+8y>0{,gTHX2#}7:N+^Az@p;Ne;}M9ErLP),m)nq}dZq3?3y=v?]{fj@py47E"%Oy|o4S77;fmUA(~+_L*@Kdvb82CZU3}3!6dY%MV:9N(N`+{`^zBOio:ZWY)zKlDp|%;RJ8}+>d"Z@6"c{19]XFI[k<fYt6#&32%dVg?1n]|o{U5cZzgU0CI[W2^1*+K@?z%O&of`u`lflfMpiv]8o;xm68+=].W`:O]zH85z9LAc#=`Sca77oMR_]{dHa//N2T(&*#"J4m~+65M]A]X3Db_zqhI:+ynSU$&M7w|Z!P$PAl/jYRi!2)[zO.Bty8zLK5MXSYSjV3rSIrxL5qoSton[_<kr/Z*v?M&v.UV2^q{6bj9D:/6b5#U3yZrR2WHamzYwUk%#j+G5&l(R6ZqO%L@1m0?6p?EVHTE(yJV@[!yN]`Ga%2Ju+)[&/UY9saq}K6WsE{dm1SD}:cA;F9p3}+L]w>n,n0S{R"VV;2vfmY;2Ga?k$/Fe2D:/jWg<5z6:(E!8L:G[ivFr$M$FyK8<Tyj|dZV2KH:&=el>PouMc:rk_dD(SPdu:Up6@.M]R`5ZX_6477EsNK,mGXBs4$kfLdE+PKzkU#I8=z8m7pEK7.d=yOi27=+mbtmuw%pSrNUF%be#Yj,8$#V$2ge=I8IKRr@a"VWJ"k]#:2"_XncaGX@kCKC]Us,dho5Z_/&<mS/P[<T,0}dl:MJyBeX6H4?bNWpM]qKqq5]O%oO]{#)XGsLkt]`Syeok;Ky3p#2ZuF+bU,^RV]I/baE(?<btB@plQ[^f7:j5U$K[$4sS6Oa83Jv>=n4$L=z%YK[<JF][G]`]+GpE|y%[6ddbN|vRUrx,L5*|!AaTn0^.G[bH7OI0^?:*+X6!iYhSxe+^/k{<k#_Y0dU{}#i2g:s6.|>w#=IZba=S4ZkhBE^1>gk~i`HqfBNVi"{%dlX_UT[Y^rQ[p>E2@LElAK!8m]aTvM&kyo$9[bs}%Z4kHSYO%45Om<KU~jgK+8~Z^5v+5fh>_wi2V)[k3fS$aBcY1/:We=iM)jOm#ew3g"7v{%e]YZV:XmascoTmpxGL$#feMqPcVvL(YH_yG]J9tTC6A>>2a:F%Lf#A2}HypaD6})[T+>"0,x=4nR[0_qu~rJ"4*E2g,T/*Bfl?!XMU[*IfWGvfSIDFK);9~klf+4p/uPjcc/iCbQLLk/)<O6SIdfHE^awz[{!{c@j/yU7Ll0/gbXN;&MA%/GOU_Fg7EZoXX:b{F8JIWUs+Uja8+4V6`C(OMI=f/{UfMJo~rJ9&Su}3svOTKspr!!xMmlX2wNA/A}62Y_CWZmvk;u5DYHwOFdnTS8uZ5C$Y;8Z%J}O6jCW{c,b7Dkm0!=0vO}h[@4s}8kl}K6,r!oO]m3Ud]*<:<#LY(3pU2`%.*X3cuS]5"NJy=28pwSK,:U!3H+y(;Ui;1Jy5"|gkoTedp:%.SA!{&7Bk*cBFffWp+y8pm0tkzcty&gc5>0`~72C`X(K=iz>BWU=z+=CswS^0O26Zu9uZ4@jryMQ#q7RGP2ukz<~|fjGZ(_J.#XMFx0HWZ2lf{4$b6O/}[oG3{`>M{VRp2q=vsZ[kiY@Xq$$L~y42x41o!o`1Ksmk$L$_=8uvQya%XW@l{4&JY@vLs/kSNh[vW^GhA{*Xl?wv{)EF~@wvVP~TR5t#g3t#"/!3HtPYM52/1qmJ5Q!XYje17q;jy4H[aKPd+F27L[InL5LYc"DwVJwQQmYcgqgjL.1x,qgjX:f^W6fjQ.f^a71+z7FbU,^Xc}Qc5.3OQ^_NWsW$`+tYq,9n2087b5&j[L(WpU]7aM.ks+;zH+YfJy_/aM.ka&uOGMizfK0`>8$fs5U8NKpuN3h.+R26NKodbU#9(+47NKra)31cB.OZ/=+yf0d$_VBo)[=LD`YbE68Tq5c:[05e$nUNJQ{,&2l$z7+Pra@lJ#,f$pQlnFbKNe16AX.OBSraejGk$ptS.p9NN$D%X27W9NMTJ?"k=L!mGk@["k0m_ZE9&2w<1%X2MT_Z,EmT>%rCo=RJRJ%X!6vR?y7g#6yBi:*^:!pk*<Fp4:bTHyH+N37Rf]40X3Hdn:u6bJT`*1ZTw5Votbo%*#~@W6Z3%#,f@x"TN$+Ep9?a/K.P3SD.>k97c2,]+/iM7EUp%%i#Tl#7!<DZC,"A:O6S1Vg;2}=0]avlm+Q83d]|RWlGYY2R(M_FNdy;Tg]v/~kU[vw[5ZR2w+Y]F+0N^1RK?UY9AxMyB~B%w+iU(NVyRgZU>YElFLvS_HXK835R;d]^|6$X+oFeodnXw<pz1SNGnp8=40B~KW:^7==mCuZZZ#,P6Z8FdHcz:Py`&jps,dK,3X]7C~R%rVDO_7BkpsQ{PW$DQ7{y=N+<DJxJ{c>o,8|a:3{#@#E;M5.x2^0|[o_2k#^Jc:65bOa=~geYoPHpI8>8Z8u^?8VKY`bbo:!4{;Y8w,P="nT@)Sq%`PB}k#1ljw;={p<)O4Q.rlN$ZOC[(#$S:|MHxU`Hbb3XCOHp{a!FvwH[[v.o^dUPW,P5!fE7yBI4:EJ4e.cu4]@4e^R%/U@^%.)Xl3*t0C{W:)^Z9PuUL*+=DMJy[b@WdC(6JbYBIfHo:!MylH1f2k,YTBd0.jX%%RrU.Z/P(M~xUO~U*fGp{qE6IbQPxo6OxLpkBo.:1^#;rSfP`#Vl0Fs%ZTBb>7PueC`Mh2$:iM87@*zF7]9rdpsV!qWVGO1p,;J,|#M#]YN]cG"g4Z2f$xD#q7|k(l|k!lot^/3J>%RgpM&R"P22S&]c?7X$A*M=r@ij?6Blsh,y_7X$M=(l0lP6texeD8N.%J>L:I!J4JWPQ,Y2#l7eZO9H&S%WD2/qo#Wh>PNh3EUp;;(3rJ1#39%gE*ta6,JM~f"g:;eCL{;da$56/Pi#^l_+{So/OSX.}:buac7f.`!!7E;(o;z`)XpsVB1r(/y<+/Dbk]&8//5|P]ILpLCHgn2xBLN/lUC"5q*rC"&TBtV$GbL9.{J>};|{AwtNCp1Sc9sa`}Bo&].=!fA(Hp@=&l"IpLq$)fCo59>}E6|k~Lj}?:U{Mx^/K8"FNlCo>.k]J8@7H=(VU@rKRfIz_p,z.OYH;OipN;CGIk,S!n77.OYFxFbs/vvwvNIHwOuMyo:Oip9{wc3~bTRu@OcbE29mimUW==,~bT>f~loo]*M>FTCUjvDt.O"BF[y`$cMcK6C81yL~ul*x;OB<Rbafn;3OC2YLBAE"*cAA)M?TDzyi6*9yyD(=yD@Q?zyZ$o86n5g0FX")AwAAAAAAAAC"1[[oO.|1`K0*{ZZnJD/JP`RdQeM8rbCx_<wv5lFHOR;($ROCg<t(`d^qhjIKWHMp{idL|+)pJ9ZNi|O4+Fx(=IGQ@(dX7nppa1c$Pboa@I14Zc/@1f0.i^/:j~T0LXh)+?=2wy>9AM#<z&q|lb5*tX?#6i|<A0+y,.LcLAbPKW7smW@r_I.>M;b+n/NmGK8*_O/{zDcTe[,VtF5}+,!~8/9mmLTJ;p!x;3^**GA+~@m<#bi^^5$^m#6"ntyRX4>)ZZ=?8f_$b@#Noo]RLZCm`{Rh}WvrYlkOFkT$fwy{j{0`Y[Nzt2;=%RNP3ubEF@?Dy_8+#*7&t%u]ZU/t)p)sqd8brOI;R~9;0ln&RB(.!5]*}/?{nf&htr~+;#2%=cLSj]$~r2C83GOS,/YnTn^t1hHMITD@1s#V*{|QUIG`HzoqG6|E/cAt&d+?p%{F6>e*[KQAca&^%1jP1mBV1C<|s+*@@#47gFT_Pgh"6<:]aaSdBzYL|Itt@u$)px^a4Umv%6#tyk/aB*l9.lD3cJVnSW^J#;Q5(n/%LG?v908+xk@M6BMOy@)D#tI<G#L3`.I<D^s5%>p.zHdI+IqJ3Cp~|r)p0W6=`NswDLOY^`v$T~LH?x/*cI/l_<sb*#7PKtZDepoZT/h/M?VB"L<I.!8jwIS@#!S%*+:nry6F*6()r#zXv|`.dHxn~Xgg.O$BQz*pWJf5fW}ci"m4ct{@v9b|N@{bP]fcnWB3mdX2&xnkS3I=Yk=e`1uYMSV(#)Q4KgK<B+D1D+qM}lF#pRe&1o7Jv`j*xmtO>?>a(I(h%@Kw<}gZ|I`o(8osR+4TF:+|HnLy@Pzqc@x!Fr^dzE~!ySR$~_g_^{&!;tD2aU^+8<zU=84Bj*1;c4EJa/S1q,Ddm$1cs~STzq&D{7Uh]K?dIMX1Zfj,Xuvy|X2{0|cms?C:=9jN:}i9sa|+*|5Gx&myO;%&wGZ++uJUrO2jbOgbj67LP*M*dGnjCGX:qMIg<+>S<9bxzd2pRPU!Fi&MWQ:+V`3MY^D;;&f+K0Qp>2E}U<Io~?|sa/xR2`AIcQa(uQ})e}8lTqCTIVWvUn&zMJiE{|*;w(o,$*uXRln[:Rmqbh*;oob{a#v1b8Yx3tXS]]bK&q@n=#2(Q8(sbel`le_:Ztb|Q+SA)x$Q6P?C8lj>}g6;4VTa!w#eY7~yMjvP%rGSw_#<A~YtX_W_<5b)"Dig+niRgjG$GI9Yq*yR_oQU`du@!24G%TKobN`MzAdUJ3,D9BGz2MHtNM26h._k3+!pc(QRmcZ[99q:Uu]FR%e+L]q3F=@Za|dLi7N^.oq@,ch+Oxu}/WrkP~<fV%omsrUc.p*e;>#:^^XC{pS+l{p1WN?SBmG|8>1Ce8/7KJGC<4*(i9MDe3y(IjuXka,pKDI>F#]Kn6cJ1IdnAL]4ejvjgpw7zu.X?Etm^iOdky/x`lJnT0qU=3N0MON6}1IGn8*r/8=b1ugU0DSqgCdb~ud)|j#xk@oo*!7bCg;(xJk<[o8#@iwH?RvR&!RXQ*q`%@NPal|4WKf}]h//av;K:9^|BD^sW)rJqlle`~k:lI^:hwlriV<7PA9[9sDGe{_$QxxZdh52gsA3}]6d+E}CkO,|fG_tJ1<1=a~=M;lf^ed_Xr5Lbamn}N~=+z,]2Z"P4]8VO*o$Iv~]/_08uD"3I/Cw%WuB]!c&1.k8E{h,wW2r@:tP[RN0`WE[|lic)s|ujOr])1Vq[ol"#;EnpVRP.bY_Mo;i*2^x!m6chue{*d!g^?g1_2*hlJ`ryxnVvJW(}]StTCUZ`k<Qkau>@8vD{]P8[tivoRr[yB1avg?g^Y{rl6`_lt,P;1m|0#buN/_)2U$Do)8znFmJJyA`,j#:]1w$1cR]uS>|!QkA)8_i"B7HT{LLc|8~#Gjo&aam0X(^8lnSQyzET^`Lt@/jP.Vg$+t0"RJ_UR6)KX]/U!h0<8]2UcGxI#ExL(**t81r)%][iGNQl2}~Z`Pc{;Jca^J&d,)>eMOY!C,r,5#1i5+L3!^x$RUFeX*_6u[03.7aG1/ngu(Iy#_MNx|R9.myP.LZ.8n9v?|v)[6Z"`eLKHQu;2j~FT"&]y+>637,I+}r]gxKz(q`MVF~:v+R4f)W6k/B:.iv87LrZ<s4,<^?u/38^pTZ){_B|pl!;~%nz*T6{B6eqpj~gL8a{eVLvj8x+TiS=Fo{,y"N0@.J8YdDo#FL0w:d2_2nJu.9)~}v^;nFqIlNltdFW1rF6cotkKGmd&.C[&hh@Rs3$"wDhTJfR{Cf0#Qx:_~Hji1.{B9~;=T#~p}SXQt4Zs?D*(wN_#=mI,46[p&&t1#xL7Sj<%J+h?Ib+~?3~NFg]k@y#0A;q}t%V8^[o1+8)>sBNI/dN,za*Ryr|ejXed0NHO3%POn_C&cnc~NL7OL[J4TZPpU(DXiqlwXE3s6rL9RC!:nu:iqQ:]X=.IX1OvL1Ww5ZAKJ#G=(`;<{`m.Z[Y`8K!.1=_7$$NdGZg;ML`u6,LVQqb!Q]%aFea.<X[e=MRK?Rr;Y.w9[1o6^_?$go}!cV$G=hhV+7Z_ydlYK$n`2U%UH^BlKasu=i.Ap<)t];$wzz*_gO{(=K$Nw/Ea/9A!1yZu<7aeI6rXZ6&Z?8!Gy,/ob>g*c#!|eQ)74v}~_7%UPA]0m1ld>29>`%aPv~.4/=qm%fu^E~v*f_i`$8}Ih0Y`HfUc?6ds31pUwuebC$0.KcVwhDNh6>piv<1;L5>/J[6q+nI8R2),.1>s`yQYcaf1=U(;mPu"YF$.Bi#WNwr<8=G!,`fC+.m~!lJf[0u4SN.K_jS/ngD<tz9$+6=EP*4ovbmx2nz#GVs:x@OySZb+%0<|Af%ee}_::>?BdBE3$m[v@hQz9ZUScFciCc7=5|Cky2.(14~5gEOi=},lU4=Xj?ienp9a`PF6KO{a*=Q~~Qy!:l{a#Px`Yt{dp]M:hWFG7e)@P+9,e|M>~&Y[Ph/~gImimRbW"r6M*n~GsiMEH0i&aI{Q/c;iH2e*7.7rh0Zbb&SG>x]{DLIM86cVwo5X)u)`eS%ynG?=+D+3l_,)&g~|E0HrhgLBju3r>s;./o|`#"2B;VI{u{)B(B6n/%T!}rLS;Z5L?R,HeSv=a.=v=@JtG~*,6FxPkQ5O@0Aw8P<VkS`@P,>b;(5E[O1{}"]9)hG0WRZYqWYQ_Mtl1b&s5w(Ch<EmqyQE)*4s|gu.8S^j%0ls?^Q}uGkUySdNRiCnXN7VM%?h<u"~k7<6F8|VN3iy|3?]2Wgs_29W9e&;a^kGge$#XAs!UCt4[2`|q@G6+0(8~}8ujgmtl$~(<M4S=*dSXZX%BX~A7,;Z>{&Ju0y4NS&I8HrL]vc@d78G7ULT%{mqjs[&n;o0e4D*av7t;_[zSJ7X.P]pww@]ETCKl`zR61bu)+xF[jNs;,,4APpw4R/Jpn_~,t>F6^}22@Xw;^g%)}gF|e#"nG.~6>7G}@F#/H6FWNVVTPTW;7G$)p*b^C~th<6xLL0F4s]wpb)R:B(7s@S$t.H(tG]2=0ipz39N^5gME_.h+^">bi@7JQxW))e$vOa%wTxoOe"3^X#g#Bd0(d3;+wG59XpF%(BzaY4{CEK&WS>ipkORGg3$|gn=BSkm_q`,:."..8|uR`*{E_%6KJ]{tuQZ&JoSc)J"K|:q|bf][H_YF}$0gCx}_)K%I5U,8DZPvX0+{e(|{i6T_IM<fEiZ7_,?Ux3GB2cd2|#+)L@48:7$QQJwM.g_GJx_U7BL):<Rg#B2=q(:JwhZKO6Z#h$L*s.lA$9l),bN+lWNS90]<`<fnpYFGIJ&0_*}bEmPgowdTI+P_M$7!/*qL.W1QXQ*9|;nABd%6J>|Ugx)nhmD`/&kmY+2`77cD>@W.LU<h&DS=>f[j/Z2pOh!^s[w8Ka*}LR%iBpB7o>S]o)Ngi*[PsXxGw^YM`|au5@yJv#@.EOc*2zKw[@jHrG9SSb5;]B{"u9{#Y1E&yV!<69vwvo<{jyQMdT20$?eM2OiQ~Rv#;9][Pu#s/JyJxq8)N5ERi?GL["cR:`(:%Uw`c|DPGs#QuBZX${4I:wrTq.qLx71gEkyzGhfz?ex[w$Y8f78dar?TZ0u]o&gTp3O]D)EL@;8TtVEgfT@L[r0Khpa_80c~DubIOWL@s<%PiZT^awNqIrG?UUK#+.}*RfF46Q1ZmJ}CBswPC+3]W"w00XRphXQS^ZbxNC]0L/|;kyOpm7%kc+H(|/[=R/.CjaK_628&.).Bf)L*nga$si+OD2OO/L*BuE*|YHK|@4blDy~jSvPA]fV}|qbnL57nQHY;Z7uXZtDK}.`qYN=jbryW}viO2BHJg_)ny+Cpd[/;zA@1{|MPWVa[vGnMZz5j*rY|LTBz&B6T.NspCo|S]B,]tLD$b.Gpl<8u{$Z)D}d8%61zEwSkv>9@uY0g[getUuD^k}GAbrPbY(a(@onX7JZ;DSS$.IKbMQm:P@1jpmuUMyZ[U0q4ZGf?Hb^od(*.]`1HuF.)6H"`@bp5VuYyj4pJ~<3Jb>RXU_q:7*ZVIuOXwZk3u@J2`j}DIp$0S[%:]/hC/U7Lok{A0BxLm6;6*_iU*,Jm}Nid4Z$e9RUH@GW*cp|S<qw_7lL=pb/+F{ID(Q:^Lb.yGE6Fwj5"J8;#=wdX+n[&?yMDNzRfKWwN_ZoboQuk,HxOK+("DW!;ffktn5,WB|N8,MP;9(9vC@h|8~HuW/Z74uJ,;XXD6a2c0.ez(cu?G*x$(aL_@|<[.NaHmsdXoa2<)>T/l}+LH85F:8Ok@ZE"Le3D%_U_FF~p:A>&5@HG2pOmLUdq(3)B%k94N)3F+U:4VE!F>KA[n*Gg,%}#bjcx4@%`|!KeG~Sf5l@/*/p_cA8l.B}jjQvmpiPwTc.oGOBOYvX!vBg^Te4}=mA$kKbc6xag#O;s%^PtsCf9.qnU9]A;syL+TnRGD"w2]KOy_($Mf}<2ovhdP7{<~<JUWh_5}?s+4h:E3z.`3eI!w!dA5&Hsc6SrNZK8)y>n_34dl*O7aI}WZUvtWTxgNH0j:q#`Ti[7,Eyx~{z1aj/aGe2th!K+cplGSrJdnJ.,cZu|q?8K3u&pNjBwKEHGdd+]:4)qElgW:l=`/}Do70@T>W>7*nvPGA":(q#eeFT)tt/aXHVo%Om$a"Dz?jZ`K):%5B0(mfZ7p$s(h+_pybH9ja4>:m,1fCB<~mg~`&Me#$[Jv%(cETCqkR|pV!C5?P(FEm!_xa_lD3Qu[O~h+`E0vLLa.&@^v)[!tjjn[efD!~Dq!t1tY{JLo]%d#WdopVr@ZT?OE3P`Kr2;~^4Uu=c>B|%o?>YI0`?8i5>b|UL7o6!`3db^m*F|[XC},eJMWHTbb9+`cOJ`b2ec<V*(s:uG+xub~;6tI,|SD|%;#PnOOg<nsL{^GuL74l.)$k%eJn%uvi:Q`U0s|aGt{MSta[&f~oJsHd^tyNyNPN^s4+a^+@<N:!}5n9!:b*+cUXE#Rr=~a,5;dqH7G^?H}4,7h:8kU;G2z[+/@q2C60=`;?{tG.$0i+h5|X6R:m.exk~TcXE85,J1=7$J[:vTXvI#?Xaw.f6G,!2I{T^,2TgjM"OQ..!G}I!TL~(Ml/CIE(Thowe^ea2hHhfG;+#~]4,7+Jg;b;SRN7:I0oICdc%5@TfKg^/{|7R%!6%I$5N`hNuLpc8qV"x,(g^96A2OE/](}Ebo07V<IF]piN_s#5Iu]a3`qgzMLr$pVPuiC`w,`VYqHz#M}`Uz{M`8=Gh3H!cy?Sn2HmTN1k0Kf63sX<_JLL3RCzMJ~@Iajmx}b,P5Ky_kf]8PY0/)Es2AzXhh|#V?*DEKdocun@F.MqKP1ZDtX2Uur4z7K|OIsQw_~}y^BuP9FvpAh%^b*B`%D,!om~$gBKLyz~6q]qL1ob*eRT.+VglzCS}e5*j5%QF_`dlka.Yo/nRklL(29DyI4)`bW,$}Zy(.:QCG(*Z544s!*D,R6sV@BLKKPDbcGv|^]oA}`CB8o@f.7I|G.$.gjm9kzKe9Pq7MKz/PCY@?/L.TC#]7RRfWXR}i?(<V=bX*JZd@EH3]zu)P>k:4Bh/_s6*ARY*LVsUi3"5d}.kVtRf47l<&:=9j&}>Nw_d|g|^y:m{^l7ZQ|m:(J+Y$7uM5FN!*0E*#8F>F3=11<)R|fG*x>)*=VAXo7SURs8{uXx/EF``h`wao<tp?zmC%%Y<_|<1riZene}WG6~?KT)`bSpnI;w#Q[kITY"Jp3L)Bi.h`G|LR~{2:e!(E1~EH#s/5hZ3axQVIt3gOidMay%s_g#FMiWeV+ueb|k|5{"n@FdTD+o<WfWtsDULl{N?eTk|#H(J%TA0:X&21F;:[!BuHUM5TjKI6D9U1*<#^oYx.a3>&7d]JDWF?L|5?#Hcab2NK89I9+B{2Tp{r;ff,uJ1%7l9zqY)cvJ[gx+LomogI?f96<vxmfV|Z?7([68M7dIb<1q2}F"5tdvY{?~C>@i9X%6G4=3Y6nP:K],iNLnhA*wr>w.~VG$9e5#.gr"S(JJ$/M%^miH8j>*85Hz="OE&!afMs"&s0|7<ux8y*kyCOiO0|XEZ)[$WWdZufqm_tH[KwUjrM&rUArRV(5c|fDeE[SHD*5$GO%)5&K*LBSZ<gUTpu;y6f%MKK(6vY|mqQ!xgd):/wu|U?hg7xbh)w%Kz`l64$PIn?o&OQB{%n:cO^E8Vb[}fG?@/evem]qHU%1?W|@Mj,k"dLIX9!Ivt|*9f.f2{*D:SU"c{%BR[[cN_r}hJ0d6|oEN_|/#zgL*A`s!wRW04=&vC=#<sJ)CFx?/Eu[W$&Q0f/X=(Ts%de7Ij`/J?,i}][=Vs%0$HKRcTrCGo)51`]u}zToXU&LX,^/G`5R=wCtDF?#;TjN?%:qcqzD]=|Gb4yXiVv$zNfEnza;`o<q(Y=1Y*]m*,yy_2sclZ}R^4~$,2}4M2Q?O~T;Tv`pA7W0z6>F0aRs1>,VP0<#}Z7dqSK[pji?~}u}vO(um^U*[s=}NBn}~Au6w/YzlL^PZnHECY/y`mD"Ud?aFn]rYs83yX~FVwKKr`C&y_%N/"JyHSKQ=ua}(Ovohm~mu8BfFN)qw~G0<tzdg@gCaIFQZkk&h17R;|LDF~Q]P?~)@+WBjlR|!o*YS!vy._:zu5>CN4?{6`lTd:m3#Q|&.PUtxnuEa828Z?FNR!mZsnh<V)fBQa/Z>ef=k7~|%[zPU^ezM0R~%0*Z3TxyIZoGg:IQt1NU!xH>=bBn9]cHMwNv0!YRBW9X"SSI4zC{{w<=Oawz9+d1~zv0b2hCJ/51:6LI&yyQrSX[=&E$o3c!pxm:7?|;sDg{C<)s_@.Sb41cWj>2sKxDUS+H^,}U]56,wK;Tr48sqY_[dIG,$mT`s#yfN"=zX?J654J;e{Si=HPp:z$O;XL,NLh0DCnJ}8rVp$Mt`c9e$.+}b7.wf#Dxag*~C/>9||l#G4GSEq~Sldmsfi!(.WaNyN?>o.KYq?%rCmwEa^25Pnu=^_a)1nz,OmF2TTH$3m.aVUY=z^*fY#7Y~YGO%`5&Ffd}Pfr@(2|z(R;X3(FaPsoxJ;Lk0Q/C:;]QjT"NAQ92DK![50s|m+uq`gli]C,ZNW:nUWRUg2`z/|#}++]iu7BMT*SdWiG^(XM0Fvs(nYxZvc,M$1D#XLuE?}?m9M||CK:s$3}L#!sS!)Us5W,?|26I9aldL2U^;oU;]=PDMQF{mQe_71LNI:[,%C#M1k[8A%y{QA>6,WOi6*nC+v@F(FP{j/@D]fmg?v&5"K6|6yoOxb"*rZ0=Joml7xt"NbuW8nqRSb31g9_gi5DxpeUrucxjS"B[:x1i$NiN/Z>|K(F|ve;Hvf#.,B?*a2OKa%,Lu~K_C^zibHE/=ijMw^stod66O%vg9k]]lr6c#<lcc3F:hpv.fPGfOJ(v5FEvYG{n>@H@T~WX10Gn9BnK)vub]@c`&+H(9q6,qk>N"H(]lI1OyKw`%Gg4Xy4N/%!H=RVrSItk%nkb=P>G:[$oJpj|X7GB_L<Z%"@~84xc~*Z,z>pX1zs+1wVm8d7]lx#qwuO:K8vCtkKKg#rX;n~dV~7+Zc0u{1DAuyStx;uJ<8x7k|&TRavWC8vMK.;?>YRE{aMq2(_TjaQ<J(epN53]=^S)TRN%2+S8Z|!Pf`CC~y+^`OZ,rUWPdO#:OLaMp=G{AJ1`4r1Qu*fB#R8)G#51q0A(cvOhjH%6K%fMJu.L]oJ&|X*m`D`iA.xvCQi}ZHEzgG(n2Ww)hU!|@/sJay<&6e3RH@1h?!A/G)khI,];,lnzGgof$$EVTNHL+j@~^wC/Kc1nVp/16"sr|H=)O[RG+qbAyTH<a8"U~Q`m_EY0}qI8q)y,l!T?C$0}L7W>"BB&N$oXMU?j@RJr00Wxs|5;4]}9~#~2:~jC8#2:&,+^8:CO"ziEm*v?sbf>I;aU7!U4<@#L4pGH{p)tCmI9@6.m/j=UO~4Z0eY7y%43<V,S5N{B%bd|v.(#_P+N>@=_Yb5TDl?YU(N{"=>f!*L)p1rbSAtrTLY8T"C|ezj[ctC06+mL%LBo$KB:Ie:Bk}wP@#Mm*z`.A"wpa(N`lFi=vaEKdrOqjlSv<FYaYEe_htBwEpoJ)P2<W_GzYe7rv^f#@R"`g+:Dw;2[7>WEh3s5NEQsmyVt#*RKtL@$DK;M_6%5PNkB{hQnG5G[&wha_X5zrYW#2SQFq(U<h(A~(EaF^,]>T?&"0#LObq6Ue=f;BuMMCE7Gj4re[O87cH6DA7B#LYC@DbYEY8Q5%)?W(niJ=}|FPKNcV:vJL%L6MK{T|0=cIE6`&{KCSkLxNfD8?jyDNkQ8h1w!.$u1`n7JQ+&a_U_lP_8bh]mY6FUy_3Q/G^=$_:_K9Z{OX|*9u>*@L}Ft9F|QN3HB4}Y.&Y?~"a8qjMLh2nJJ$l[)$m?|93t*vDq+;Z]P]obaoxWlBY_fnb7>.E>;wRFyLU^=;Djdj;3#/0UG0Ka]ctp<,C)H({?mGk^AYLk(xZg5Zo_FAxJaPj,)$=rSSOQff4={~!Cx>U93;aItu_="2Dz4K2dRZ&|xV*RG.zm=F8|g$1NeXL<4yWI!H]Z$7W[ma:cWQ!/04+T~nP%my>KbYPq+NXOCn`zK}F.`nA*WA6]6W}:qbF{|Z[kH2bvjle}GAr^8`a&N4a`qmG3MTq#Que]w|Q23uZK&~dYLZt$2%Zk{"2<jfo%&e@X<#WkJ$u9=#)IN<|eUbW.+dTGka]:ecLYRy%9rz`*yNoh<%0h2}LX<HV)xH+d(92|O0b{J??*@nQj_]WPtGw2yU0K4haU!L`Ukbe+fS,Jz`R;dpp2&+sK6@`F#6&UHlCB;dTzF@K,awa8ELUh,:1F}[eo4xfx*1B3F/PHf{OJSs?NWo`zvC,,&>7%Ts8.{5P=`GGAr4Vy.vtj%PpDXS2gTL8_}$&R:#{C+gi_>IS[BJ|RyQ=ht*Y4jXrN@Wr^k3+U0I?20Z!~hb;H:m_>(A&^dvNe>n~?%%Z4XA;M&d<.a**SjgnA=[=ye,I2vScR=L;nZ==u25)CN)*FX<*P7,u7D[I~OoWDjQX+lZT[M~[a5GNfcjn#`=+)9+32vP<iK|zcGMC(nP@wMy~j[/Q{;hr6^VDjs@ve[6r>]enz?hYX`PtY,U+BwYZlv~RA<+,h63|R9[]KK%jK:WLhzSL{f35/CRD<CB|),:(Cg9U3$}P"k"M/}o5]5f@DE|CLNY^U+!<A/ulYRaP;2#O3aC)w@7i,0YH*DC8OFcjw8M.+,q5WUYH"j:Eynn0ib9[a;NH8$h2H1>{(Z"BMBC?cJX+?xDxdxCFT(:$RFW7ndnn5p9FAzxvsMIbmo,qm2$C~l9P3nW?90D:*kw?"+vZ:Ak%UoIt;:|Ono]D);&;KxQi[h*F*kOkj`Ykr@1GyNPp1,Ui+h~GD]^W300EkkIi#u/ReS8oVvVTbda9e!5.GQKhm[1/m3qB`]~onrY6#gW&?JM3K@@Q0vuZYz&.e7(0PfYGe.3c8%Cz`rPVoHoYT}pdo_!bJNrbY`4n!Nr!_kxDvE#%ACF35`;=QveG"LIhw.}k/Mt9W"}6sM`_M3!{Q,0?wl%y/ss%Q{fIOP,_JL/C~KM==Cq1WXby.QVBq7ljC}h@qoMfPz3ROH#k@R5zIaH;8WO;J_5=F#5Z#iY<q%{=jY=[,wSAPGhmg|S?I,O$c?q`~)A2Yt2}jL?{2!{^(Yu~l}{|blMfHZQ?D][eA]3:EHOUrqA4vsa_*_A:<4gu4vSsSu+]/2Tn@HLCr#,Fl)o~nK=`LEas.Fl+MH.qCIkf3Td%17|~QfvD0het7=`}.h#S;Dy5Txh/I^]Eb<6};]hX7Mq%*NW<n8ihPs&qOuW?4P|DibbHY)U32]IL8gC`!$Z[/6Q<ort0_,I[Lgzad*4@K95bqccgbH"2kusT|DY^]2b|qp_=Rolx2#=7}3Z~(m7N3a"n~NUg`kH}aJ2Q(2s?qf+D243^08(f=)QKo@FC0BgRyl:{&At*$[#!SX&4wwaz~"?.bM{*(LbT8#5iA*[ou?<o56"PW{c?^B0t81$g/VAf|06Xtz!u@1TIb&R/ihVhRn]Q7P@5?/;xspW^/}=o0cuAe1D$@>(*p_l4a)fC^4rj$2Bhdp=kB6~(q[)||[O:,][U~I2&]aO%N[}qL)yZaMaNFHpBS@%z)gYFMm3rf3;5/1gLSDw%B,(6xN!$dd}<D4UB59u[pw0Wlv@sydCM+^7>Kg^U0|RgPBy[wxf$w^lqB_@DaJ0jQUD$Zygaftlo]E}MBfSGI]|gqnSvm(OEIKozQ9/^*C]ZI_rN5&q;(HM(pk=dW9#xJ~igNZQtR$XjE5&@|/{Pb/jk(gu(%bdrp/R!s8WpI9$$!}1gQ~J0B8J`8X)+$(B}M_m0rR_+*%5NW]aD2SNd&ZuYe$AI[4}2UKeK2JOb}e|t$r.OQ0LF.c$fj}FC(WnvuenlvDu[8QepF:"||C{(r{guzft~KN=(=p01[7$g7);okO#2!mdBV{DL[p$c0BK=.Ee4Rd?b>KI6w%kd=YV?Y=2eq19>it=;%w(jEInJ&6i(T2iTTtk2!5`8^XNI71]Q?eK>gt@#thsC%Lr7z9:,9WT}yglB+;qzau`[2[RI75r.9WOKyiVVWCU:I{PEQeI2I2]`vxzo4$]kR|yKuLSEiJ$*gf`8&a[8Se5E~c!06?^2bJzxof)w,gM[,<zv!pv#SB#PVg2~pD9c+^=t5v.D87>xS&6hlR|l0gsH74uU;w9"2~Ys5RURCNl!Y/+D1a9DTsMlNY"!C%>@gIi*iHdw@$nE)Jx]C[DC)14$UXWH/s2Fjow[QECQ%S]0_7:{xKjiF2xGX_Bj(zf0B9UA1_6_W%rkn`5h~[ST=D2B)6;?8jNQ==J,n1`@p>X&f6+$hgUxoQJ2r@8F:P/!u7"j|h}Jb@?Rjlc+9[e~UU{eq6C!VM(BJchA=eDz"d&hbI!mweL*GayfXOTFKvOB"0oYLTC@IoQsMv$fE]vU+Ujz7EQWp;Pf*|xhqK.jsX*AAcs%&FD&S5@2Sn>=qO,ZJ~]!hj"~mK*|qTrIlGeT(DCXA1+*Zmf]t_bZ8aumlv2<a.BO#z3&[xMi,@tN2B*MbBH}X$Tl;g=BVEmN$Tx&cbACm`I^]HAjHQN8oZ!Ws&&/M_)5b[`XyQw2]:BQdM7,s7.N_|<iQ}6Y3644C2Xf,>ht?ngm$nNR+4EyBmG#(3`~/`@wjqn`&I193}qD]pNHjWfJV(Z0pmP|=5x,X|y=x^@I"T.qQt!QVZ&07~BB+scWL4Q$Pey[}rnEVhmR~F)s#&/uU&d=HJ}oLPgzGDBaf$TB|})34PtdPaO0@D"4NvOSUTN&K8W!++i=YDc]J5eh+^oqvFF(d8rNkt7M^h9H3r#}6j0a`>&6y)ZBy+Mx+E>jOH*A;xt%DYv&{pQ]}+YR(b@&do&>);iX)R0=Q#,Dh+Z0XYI`DJav@rDI5pG36/*98&~xYfT<m1.Re&Dp4F<E}b#=,difcj:MY3QXjPn=A{o0|GT<ga(1<+S,o"`1Xb5nx):ovl#sjwr1~)>>L(TM[>f25]TyL<HmWeqEEC6%oG}O25Va/PGz#i0W&&nG,CI:9+P@M~qct..73^]y0_`F$7T3ikYs7G~T0|yZB(WL4p?kDTR+d<cx8LC(B<>v3U<WYNioD3o$P{}gX5nQt#HMsAQ.C|X%jKix4D[kL/+!!YCLR?M|Ub%iN{Op0NR"8jScTqnqZiLw8iQB)|;$^|#u|x(QW9*@v3Q.uLg[_fD88&b.`Bdnx!%v1g%.4]2Iu"(u<QNwEFPniC$OU>Fxa|OzhZ_dVf5:2Q]FD"FtI`btOb`zhT.[u{K]2Z7tH^Vxc>E18T(Umm6Ok<jF/rQyc#2*xOM~1COy=QE=.wbho3?St41sD:jkoz*+Zx,sur0Bk&06%c6uBWXhMK%>zbQ,K8/N+AL,zi7(NB^}.X7&*X>g1Wt##`kxNsJmATDg=VvjeAH4@kY}niHP3)k!#J9NDcQ|#CpQMn~U;txs(~$ZCu>_T+kfxaCjLCZ!P:QhO_zs6j:@x,FC1{~j$!0EJ_;~QSh4L;})[Ps%kfC_wn!5:kt)Y#}ZNuE/|5h)*~zgk|5,L,kW2;_/H6fHw(%hzPKMI_%J3Jx,@/0lSl7nyPY@h5}bvTK%2Z7V3j4q%Mk/?)C.LwaU(%J}tk@}k93~}!OymTRmpL1<mf<2?jF]5d6LlH0A%lZ"a&yj_4PLHDXX(z}0j_=gs^HcJt^m:%QVF9ou7{]tDX>^0cT{eA#wF47Kv/8rB>Z:C5jQK|{X&;xFJ0IMGe6BK;ptTwz_P63!+R;[FPdp@&hrrr+]U/W}9[~>q7EpT$5aE3oBpMe=k9fDrr)|w>SR1;E(OElp7{7zII`AaknB#{B_V%3WF_B5q9^W7A[L#qNUoD($E4ZsYDsD+h{em;QIuAo2BV?0~Qd3{?fK)!a8vCE?p<nm,<|0FHW#qP@8CKHZ[%PH,UKn)9@|Rn@4c5Oa!%;1aHE}%^o:Ut+f~MK;=$|vr=,hHu&EVy{a;PBmrlz|MR""*o,I^zdE*w>zq^A@Cg][}An/N@`rEz|4<1)>wLU3NrxS[m2X,s,(m|dH{koBE!,eMhWZhsw0fyldQC|D)i{._GLu+SN~r~m46lI1het%NU)V$%4bM,w"JT2MVNRh^|Ym]K=N1XB4{6z@UWLc&:d{JwU<,~%BHb<,`2wuBW+mp7X_]X[TC{.@[Np;X)M>W(miwR#Z,=6<hp.`lw2mz@d2p%<p_"2M0uv0aja#R`rgk$]ZBKcw^yzbF4fuSW$s23F7W!yH,Y?!{UUi4HI8TF)l:[/EYK>v*Q//FF!TqKD[x5`RFwZLDA%?w.d0?S=#^,WV=hYZE<n6eZ//[lMBn&iVa>u@dcSNeu+F3s8&LlDaaEq)whZ.eaty/I%K?,(FdFVWR?=+ay+s5_.!v)VHE#i^|e#>H$vN*COqV~}}`H;1xH"!XmJ_k[P>d85_1f6<0(nSqQ&]@<a)C"E!W_@U_8`tF:MLt2pz2y(>LfIvmnzsR`aB&yqK..2c^n,Z%P5MR/o/k10:QVg?]j/>aXr>EU!:MLum#l[FOf|3;vsa@n^pgmd"X!7,]]5fX(poOj#z9oS|~r~ekdqX0*(:lVVJ_nRoHZ$GHjT5N3>B_&m)+{@E?3JOj7)a1#QCgN:N}#g)I4Zx8"BA`9S9D#[BVzLG~ZNMXg/7XZ)taR9f3kq]3sb{T7FNZD`:1A}&gqg2EX)nN!?e6zm^Jm;c4PD~)zYA%)j>/6/$U{%`(#UttRZKI6wTR=>yqye"O2[LXgp<[#A(JFxv02(VUO91`@2.D0<b,2?vR[n7c~68_^.:nJ}.bdF4CK:HC+wv;[iFfh^nmb|31%=p*]Z%PL`iQP*I8<ydPp(}RkALB=G}iD_8PAy;f_<%oIN?Hpb%M!)PX%R3(6(7E{qlw7Q"xFJL}`Eyx4%)Y3eX_6J;zo+`6{d~#dO3SW+tz)~BKejb%A{URp:&4/?aNOyWBqErMUpEUGn*x8/.`^KUCFI/D{#VRDkuf+EfaIF|YQ+q7g2+p;1N.^D!qfJBb7xryk4*HjU;}QL40);AeJkUe}KFthw8t}kpa}?M9}(!:XPf@T:X8%$i@da.ziu7JnnulfQW{u~,T?}2MB]?E"JOxp]Zc1`h"G%Ciufp}N+dDCNna^%tsE$z6H9!X?_x~Yk$OoFQD1~}:c6rg"MzQIL:X3]W+/q++FwC&&nG`z!M.[`nR8FJTN7q<;{$y+,WqRKhDT=aF{ZlTgs]n^Cf|Oik#<y:ivjrtIE7pvn6F/sf?D9SOgPEzW|9M}%BhDBBL+R$bQRe8E%vx?vB20,%Ec5BNKr.m&8/+*j6{=YTgHhexzllFS8G>@Z(#T$BxREF#Cg%=OtYVu((dF/nOyQ3.=Gnk0$EeJ!H0*uS=()S<Ml~Ye/lqVoB|y#^Y$lE0pDwggboIYrxOL]/Sb>ALi(B)DCLgH}pdr*3ivWU]bqeKSS~,Hi*]/6Fu#a$w1l~IaH?r4X;1.EsE;px;CYb)vkl/wH>t3~wE%ECnMHa7yu,0|N7.zNNu3L&?P4"4_J<Is2BaS_PwYa}EZ.X&HPeLi~6WjI(1pP=N7=ql+?^K2UOOcXG>/lM!4QT/R}%Tsq@I7_RenVw^YR|Iy)wEc!FWL^Z:{&lK$m/UflBr[09=z,|U}Q=NUsjDot53.aSBJiDJ>gy1*<fM/[DlXc}aq.xf_FS*2Mw35k;}G2l4CPul_.V~69s:Z"zlg$N.!_,egJO_@S>"DSkl^o=X<WjjYOFEjz{[Ws,Vn3+b$+A<#2v1}D^]"EQU}pvzp><_R4qx(o|&CYKJbKLq,HN)Iy$`!d}<|/}kJzKGn8[UV8A0cI68*Kom&X0$h!O}J$RnS|6eph?xn*a>0bj&0;A]C7&6^2jImVA,@rV7mMl8<`w1(m<sd]Fk]4|6@K^aJ^~~TTM]%N8HOgM_Y)=1lG/ZZVSR}0#c4>lhc{cegeWBC$dZmjKn(6COk%vB.YX,O2KXsS(p|]AXi#>Ylp3c@SLi[`=;%)]<$/O)WuWR5#a(/Z;NqIQHUxeg6jmle2T%mq;jjahA0.9jSbTH6/8jVyBDnmVEFFF|~A+Zv`jL*Uw,ub~j8HoI`cvbFrY]"BW|8$gq#fMu43eD@@O|Sw<<Qa(APOi4J34nw5gdy6}Pk_C9O}gh%2QjL_TqHDDg>tLNR]bX!1D9yO]dDBJd6,QJ;RY=o2FJTA!?%ePue&SlQ=7>]5eCL])3,5#~<48m<S94Cx<m>?!bSnnpkM~qUR2;J@}mt.L%[<Wac1tF!S{@4?:w<4!]]>.UTY:7X/+`.Ul<#j@>m+kQ+qzx>F50Z802jKKZlbv3<[rn$EM*l#YqcMUjqh<!A8U4?,^+EQgfm_95L[,<WHV>!|_k`j!x0amP:gnF4rFDqwJE3yR,#DjXKeW|x>2*Es(CC$W0}FhW[7NFxX=d,U~&L$9R$HOx?>AzQ}EK1z@{!x5NDFv_=kJO[1|_J!jZjspMCE.ooHx[_LD,C/u7q#d)hvr;kwtRk_/E]<Y8b5$e{/wH892w6DJ*uNIW>*Cd+f#0g"2FL6`s*M3qRUkLiWe,}L+EB37i@*$]vlTh@["Kw+F3zY}l$KR%<GX^C74i"jMzI0EL2Ou`quuk0sU/G^wNszJ<N3o3k:/;N{|<Q#.^X7UOVdxu}+7~!`fk[`Du?Sr4[cYGE/Vg]#qrW]f9BUHNajo<#5L%Z,_Yla>8lkgyU6oP[z;hHwYn%L4WoiH+b+.NYlFR!E5kEYGtU]r,3/|bQISE`P95VN2SGSbJK[Q/ZjT?F0Lj,KXCIy0s,6QrXhg]LCJNR1eZ2{~9fyZ!?PEtLOsQpL9MPxlOo;rpe]3DTSVS2M?{mX{ZH%;CRUFx1hpPK{[[6hJo{@NTES.qf&KT0~r>eUpo6z<+h/7|"N#8J*(8eX,wx;~r7b}C*!T&~KzTk:+(F"In!k0+z*RY7M({#vE2ap9)q<?d54!8Kz5;7H/!;Y)aD!>4O#BQ]RdTl&Std>Ox_~Y>w$~5+6Ia{Z@IHUk%Xl:V{Wsn5u[7s$H6u2R$,,.Ew_5iugxBPcd)*To"(W:N^}AwQicF$]{bI{r:;Ranw%C93s6,|kwMBIakH5u1$)E_wD!G;{xGmau`USF|t/8Eg)4#b!BJdZ%vf%i5`$LGD+$#98d#~j_cnTuS}IL}kk[@+N_IvVcGkS8kEub}[M<.186jS.x:Zo.hlSv(P^/L$9oJz1EYDN"4CHWXe0IdacPpeDKshG+Gw4/_Xdos{mJFNsX9OK^b32RPs,bGaLW|]34*`V"zoK]/@W/)XUODxDtl;iM}EXHM3:6Ic#t?Sa0QJfmH"nI_[9GO7?uCXZ8?#<d)8^m6zU(OK+U(K(SCKLRi`({Q|J@+xZ<uofP~"yX:Y[pu+KkA5zN>_G/ih=f4tP3oL$E}>nbK=eC"N#tkUkdl,v*SW?Dhw?wQwwv@KFwY%h9#;Q9>W:Czcs4R0?[/i^?CVxa~&*3dcuLw(u35m:rksM;%/^<}J3Iutxpx]MGQ|CXpVw[P{)6_*;d%H<o<GG:bUn<[F7ALzH:pKLL&2|~yy}[xo|rrttTdK^}o9yf|W~nZ[CQ6e&>;|dV~]7x2"2koA%Lp%_0"eQ=qk^A+UKt~"Iq_h./vp@4sXz*}qg;bTRT~)$~>|+M<UwIK3N:%_t|s@1wBTCjO6|3$A6hIv0ZE|y6j(mKw@NQAjC/$Zh*U(yPCFvF;drQeSwd:>krbK.>lOWL?F5e/GE/%&+~vE]R/f37:u3MeqS|Z3gjDWW7i@GZX96t9o@(w#3<zR2x&_$J2n&^B[pLq]q}vF{ijh2sf>O)eQB=<j&u~IR@C=NN_j[h,F$[*2H38<{_YKVQd?A_|;,cl*:=%&?>vg/@3BdP#}q&^SmHDZv?8J39epwksdZ,{Rwi2HWO5!ulWd=k!,B_>4XqoelTP)Tk<Y&>vzC;<ns8t9}=k(?HbA<q+]b5Q[f4}?cBQRf@d"[H]kWNjHEw|wF77zLC0P0oZ/$@BMf!XsB1IoS<0B?57K5n8T&%<mX@$)#Gc0U{QiANQdYi}=lVw`H~6WjQ$jace&JJjN/rPB)%c^Yu;.[{$=u)_=]>V7S,(*tk$,domk92N/f;3six[TGY*ECK:&aB)+dzY(8O<{$YN!]oMk.mTo{Y&KQZl~AmK1EE;}T/GZ`#{*tBb[#Q;^)dh~T9Ldt/2PZ!0%ib`N)99;%}w0.n<Oz|[m)lbS,F`vng,]0:7:hrQ~ZMwau/>+m!E4Wg|HFNOSdOB>st]aX@i"Jo3o~Zdat{V|W]`+$V!^7M_pp?TcE(*XM(kE(Aj#RH>fWmW08pS]lH:;678T!G%U4Vts0f1HVRV(c<_23jW}UTzOOtY3v:M87^7]<fO_bDvb5HG;2nnJ%+"^^)xENN<S4)IGIC#i?D(9Sy?YKrqjo~)gMtm`4<TC&boP*)ms66Pg#$qyOeF[C^Q?0r0u$4oAEWZ72Z0=~J`F`gof+oZ$,]]@Z/<K?UmmRFLz!>dsOET<q[ReL}rpzyHz+sq}qj/=hU*3++dQxH9@j^OmejJ[<A[gEo`uq2<g}X8+TJo}"Ju~x";Ts.sH+rJdFaoY_xC`F6ltM,9j~N_,C.KlRZOfL/Tb)TxSGOyX{fqv0TK2wja`Ig4tI+`:+S)MyWD"n=2]i4:`IH|VPp/VMgz?Xi+e&;J9T?Lje"#WfS7OVPU~Yph9"`[@g>DfqKpdEVX%&&U^22foD{B/w!0w8]$={BX/I2%<;[|F[6Y/r^8v?/Z7LwS,zF6l(?QoP;%{(*|Yuh3x%B>UESNZlHWNl"$bcQ~iuEjMUUza]15:h/VKY`&V?;C6VRl(32QL{arbNtUrQWJ}<oBk(5iY_*!t&c::Dy|#[yKTmZOn~.<vZ51(gxb3HO%Nz.v9#7q2D%BD1>FUS<,.Y+Zke:w)z/;:&=fSV)mWR}IL`IV_>n97ktcx;+b:7H/pfo4t93RG_klcyW[Ioe#%eiLb/sE2weoYnOO=d_DMOVZ%m#9@D8JgQe*M8v5D,HE.IgcPC"c3(>!w4rX<&?H<n6a(3TInG!<7+F,alNr_4}PH(jv]!oLm,ksy{7LOD?t,~iN^};0uD!9<sG>0(@~~V!A_Vh*mU[5e*pB8`jqq0A]`oY[q[C:0xHu}@q@GFLTd/tI<|cFf<U(!]RF0Hg,z!,{IPxr,|t1!OR4/[wY#nOTnX@KLBS{,O;Q;Ej`CNwD<{?yuvX?jsn/P,9czai5nNc^NtZqX*f>+o#F_Fa#I3h%@dc<TG..gZ#^b6o[C>*j>RkiQjejh6V{/#`Z5hGc+=jhHW`<M#vzniyA`k"XA"nW&#kBG/&Z[79t};Jal<g$mvxJ28Zx]#p}I_~Q0notRoWwg0*IGs<)()$gp$2S|hU3@0,_4&t?5Ipgfrn~_bNTuR{9M0]E1)saierK>]X0$eNQs%LF]0kAn!QXG^J`:Uj%/(cY#Ute;{{Kpw#V9]x!ak/s{Zc]y#5wqV}d>z7Rc7668!Fl_ihURXF`"?C9;#EK+/f1pNr6_^_#n_P.#YEhV=Z=yZa+8/,R{a"h)G:}c]{OWY=q>}#""PfQHqsZy?dLdR4:RKoPUMt^nkXoa^{Spq!:(|G/!(}D~RkcJfE,3YfsO*!a[Yzf"!E+&"!W22`E)Zf$y.@._G&4EmM=fu5YC.;Vf&HT&Nazl3$WrS!%SeN7iwRe@y20Tet_;_L=2R4l4{aly?rL1]<f1uFKzt@S<$?B<C?/6fAm;$p{S|g>Yvj&k+]*ju9ZX=FIiWmcZfbd*XV=FB?j"(ftis",62$p|FlcV00aQo8BrN[pvX_6Ev{COV8)q!dv/?&?MimMu7FMhL5xC;U+8gNR^TFg)3#.@l.b#1Mri&;a|L`>yITTzox?)TgrO4D`mH"/smg]|hUR>L,0d(CS%tw0Im!6%>u?H`C,5/xEP[_:adU98eIb94eY9~x@#|Mjoi_C9fiv[(VOTCD5yg!lFwKssPSy82G#>VG%>4a2`:g)Qt}a!1t]j~,+^4<rxLrFDE/Qk8L)7SKJ,6q{D9l$dLQzC+x;ff2j{D&6nf1OC137t~[3BbUx87&QU2*k"0Xp4L0x0B<DLF$&q/YqUM.GKu}z"L?Yb"K5"WJj(f>Mp7Y{=Tm[GzWfUad3wfHxYXYiZeYw#7]7PjvYD/v7@0;bkj!KUU;V.m:wB`z*ESeOZ0_|3lD0ZGyAU>G]ny`sj*+kr.@(9Q:[_|$c3Ix6E^3;coEk|ITB@MMj5^XH*B]r&L6W*>F5>K!4Av0^m$oLLZX0{VJJ|g}G&w;tGJQF&tmNL)1+K=aw9W(`~T,2kN>+M:i5>wHO4J34qrIfF}@YkH;RFVHRKDlYdZTTkiC=uJf"d5<6IIqcyaAgy?BSxY17;#QIyArnz]^Z/kw=pY>2sWRc^U=Mh#XEQP2&l.#n~y%uV~3*@SPx|704OmWJOR+8uN&Npf0FOi(ms)z:iW8q~OL~rqN/k&rz_~=nGN/|TtOo`_/#Ep@5Z!"}(2kAtF+)4,miB;O4h^I*hVvkkt3~*#fx,Z$Ev##qC5&=P?fVAG;5R@,Hfdo>S0LaYrFO}FcfE@yp~lGm;>Jt.2ei+Ye5i_d4@f`|/OeM):|X&>lkNq$7Zt"2$j#Mv~9Rn[Y{?+{+k9"F[uccmX04!7hXQ]4Ls1Q&Lz(^;DaFQ2WMecN2Ilt0TR_9~lspLtLs]G7.=J]&AO?E#Qa}35M8Em/AXBzBQia]!9q?2~!<P5IGVY.[1JSm}~:$0Iau9R4;.OZ(;[/Q^dLVl`y$$QZYnczfb`UCnJ79%gk{@/SY&9JyaN{@.V7<mvto0";]Q@?u,k0<`]Fv%:w8G{,RQfq4/dZePA(g+opB5MrGd:TSn1wI#zu2JzWbA;g>u+E>o[PhCSvd?)Db{o7o8.Ii^e2:ZT;G+5j|FCU+C|V6Cv#!i>BIA/iQ%Fk6~D]}AXbPx)~%9O[S@}OG!<<]w0$:icTF=PfjBy=PP~.7Sd&xx"P:EB~++R4U[.IRp]iIjh)is*0O%[`zrUh3]ifB"8!!w=N*uba0,9gY#T=,Gei=}X!nCgOW+<ErxCqT/EiiAQ"_Nj{C9]UH[w~gLFy;|e/QHQx91!%xfRPgaYZ4;v:x@0z};bSb$EETlP>Z}}xN?R}8(0Y;>nMjhv,tvqOWu~5EP5ocJ69n,Lr^M86)vRIDL)yh2reDdI~n+Z|4;@+R^{(k|aWO})17FW;X&@x1jfo=pO~=3$1;5?290XEJ>7o<IUpF^&qH.D]daL<#(8f(X_/rZAOXf?{S=h.ttpl_;)ifr(1J=O{A>fL;dn1?}T]9m}mfcvaM48ed*CLE/S2X*HXQ5I:){NIB*zHYWE$,_8$}j(Kg^+E~BI<LfXO@;$]usPyfI`1mNvHFN?a2wv2_v}Kg#DP,9PgR<$<S*Ta~9p)9s@KipQhB;rG);=H;1<72,MX0jS77,2&>2#:v,]yckm[s;*ad^^}OR4`mJ(|CAGxf63sv_,E#]<Z2X;m>c<{E@[@tSqyg0o/UacZpo{Z__Tu&XC%)Zw:aBK!bJW$Ot#`_dB/byeID&ZbtR~;j&Jz^+`TvXamL1MM^~k;K}TJQcX&>CH^HSkT<8`kk*4OTeQmlaYB!n?#8M<fv_?WwlpZ5^>*~*c%7i1*O]"&(vSWpmU:g9yuwl1t*Bnfb1^f%5[xh8A`$vH5kvmHx@,^[R]sa]2L8abu6`"7Tu.dkMuO<;e~F0xq;aL"Sv|vuaCkJzUIIS<q/.RwsEo/{`2hR(w;d&I+lwz~mv7&,.D}Uv7<5DU(:cdsR]V/5Q^6u=1:a?p@$tq5cd~/E}A0T3${DoG9!(|WbG`hV`%9|.v@qiFXZ"otIM@5Yjad`xyj,gtJoa~[Sn[&[=FWbn*h9b&Kt#X~)rd:L.[wAs*z[Ij>{8cb+NrZc}MR9b4LDyU"UdBNN,.{?C5yY,DYD/@v[P)F4,RvDj3$J5LQ*4M/9TlSH;E:k`ZoRY5oS@3Z]mvg{C9ySF{C50QKY]gfGTL5{~DGK:>T3P4bE_@_mu2L]G16N9:D?Ek{1J1}|+NAcgys)<F~eep0]CKTQOg<QDIF2%om7ie)~S.~S0k|WJ_tb|mL8&&7(5O*c%S7]Q)eTJc+X,N>uVP^tCu*w)5FtgK)6a%T|.%4kcxaxUeH7Pq<P*4s_G@TqbS2jd&LcXyp>`76Bo^VN|r!k^rc<8z64ly`[p7E3}}!G9Z7dxqpFl{;?W7e)d(`{G%3UtVg]~J%ZvTUPs`,rp?ThTC8]76oO@Cf5D?V97Wxw!ByLE%[omH21Fg]L|Pl|E3r6?8`]&BnC!U#q9{#dgj+n>sF45FgN}MV=!K(e^kY`#_F,A?(+V9*3NIaHJB@CXNCKY_O,.r<R;>wNw:4E<P+R,Rpy`zkBIF=AY9h>nw,/>K*~0LH[Pp%a>amHgaIEZi%B28W<,%,I*:w(,SbVjm5HJM*,I9Py^*3X3PkU#G!YlE?Q6z<mA,q~y;3dHEcYD]]mi0.bb:A/yQ%zKsw=UVu+iD_h+:oqsO9MV~<)qfVSG_<s&@~`5`*YK63^Q?"&ZC2iUMissvUqK+ZNd(ArGfDNI+}3IJ"(;Q%{}6Ik@?l<@Hqtl!m:~l>.jv[L|DP.W%Sxe%,6&[?Pv>sl{Pt=eVBw3=)E$j[M_*oiCkyZM<:IX"hl;aR=wf:OCs6w8X9C]/bj/m&rJ7veXfHS:zyYmktKyl$KDg50BMP4^e6:vRZ)73:d"/Et}*~^2+vPi~AMS|j0A)mt551i>xR7"IG{jsGW.9r^!mZDA6]G}Kcz.y_O3VVI05<@(Y"n3)K[&a>lDdPZl@[r7QMN.5mCNm.WT*`r2s8b8.u+aveAh{:BK=>U7WCD&n{+Ry8=/9PBHW#3Toyav_AZu^VdFZ$*jmk(3C3aa{yJ/QIHA:wwp%rFaHt?gc)]@6u/WLKj<8ufw##9q=5k5eJ?<U{J_+w9t^8S<}RYK#c=y%sG@]UF_zJQi_~4$t*;}m[j@l:;%q&SNFM7E4FnyDD1c;4?FgwP+G6P?43vZJf/J7S+Ise.+hOwQrN7$KWBvC$Na?b+%%@B//pTlpd3L1.6s:(~~z610wha%O:}}A[zdb!p3?Tpg)|$Xk~R~w*V`[i6PGS@:4s=lg#UN[;6qVkqx2G{{cE!]2cG8_I@eArCoL7e~Vm?%@MadD9=x>{7,6%sco%^NwLt5XicUCa_"{%`50HMI8~~(*b/JASaT%vI.8j]i~#f#n;Ht]|qY[k~n5|ULpd_fkV0e4"kv)L?^i}8f3b:>T{O4!QI1tba7ZT]9x$ffK#RV`sWTTzlY2Y2W_OzaS[d98YV^;%_,+H@!^88}J9^T<IqC,j#HXg$HXo#uirHBBK[P^9R`.5gmL8)+_ToXX(I"WF[T/1|?f76PM+"qfd$/kF`Z^JQE9v&.Q^zE$WLL.jf(ZAkn].i48(PEK$Fb]94{.gmcsdp&XNL{kRBM@RKEQrk#1pPlqKxh/V/ALe:l@"(*m@QsMUB$@<_p_/ceQpdDK|l?egN{mdPtt&%U;yB(hz=K2NL<SLthHN:MnRQ7_[]ZhrI9|^;KBeW9n?l$Ugi12#fkKp2AZLPrn5gQfaeRCBH`A?ov]Q_2Q`XF[*7"[HUZbc]k$H2un5;0k}d5|}tL9)/3f$CXp6$:WN6n:v.2EvV:eu&"$$vKx6Q^/,!aH4!VhuS:!u([":P2r3Z#uye6ghKt?H48.@(Cx5<;,[9kei[i@:d4Cr,.72&#`s+,|fE]#s8*c:7#WQGlo*8I|*&/&*fHiNah=&Or+CfQF?+ueqO&]aqgvK"s&=ROXx+:}Y^bu1_c;Yp/!"8;WxNFi8.,#_H;T,J>Uk^k+j7H(@*ij["5KM4x<U<I"yMyI6e*mGEZK$2J*@V3SAq%.Y5.EF,vaC7^Q~4{,Yh87n+7;,z1KsWDCD;J<l_1DFfWAT=DXuM=+nK61.aMhD$9_"xjJ3C#%bNIkdoHIAd,)ZL>f2%*:JKJJHU[^6IR:aog!d)ZKX_D8=*jtzDA#e&`["FG,u9j=r?M.$izq0K,K$imYQBlCq)<p:`iT`Ce)D+~|p_PD:`iUW2@H"{rl/r~~ub%#K0*9i;/%}kz"2vZuu4n.JRTgC64^EiAKShDL+O$aKg_T.TdR#UkybO?%eW.b~c^nQ41+2e^zA(qQv;2&OTj)YcvD)c90i&28"$`:13XH%p.n].@)nx,0mMt;p9+F480A%v?>llW0ncsx,o1"@e9fp99[AR@Z|*cp5s#0<gf0QTS:<?Z05!M.y0}x#BE5LH}od91%@XuR!QoQB0qmD5M1(tglv@~b8t:O;C^H!{i9Z63$:2Rt.?4k;BMLV|$%KyoJvHt}<2sQXTPRq&OCxxQDJ{6he4?a63CZ$s!B_WcN71ckHK>0~3U|=6,cZ7Y;^J{mhY"RVE$AAm5p";^[?9+rDn<?+*M#*)|[.P;^5$VXkS=vc)410[_L@iQ#yQoW)/#.d6&0~BTPRUMo"r~9eIG;41JbLQ?DgDQ}#akbrK/B*xHG_q|8C$>:!*@eO2G]p#UYa@")dBqMuW;8X/`rd][tF)twyW@.JD2?W5J!ou%P5/(X+4H}r/<BT@[ahp=eE,OdBn6]#{a&OV^aPQE]EYcbXkvzA@BVt^1Ior"ZK&VF$xz2EBF~lEt_<fJEGHK,;&`dI1J`C~V&I.$[rC.h1##bR]m[(*4,zQjdTf0J>cdiV1:Zy*x3f{</j_v]DS$I6X%hO+w@@B)399iM&.j@O*G)IVLJ|8WcxjL=;^tL*FKljdg.}@Q0&gJ%E3f6E3TDuyqfn(PN^jsFs=g%n`n~[~R8/0C{.9s;{>b1YO.]ph}X;uw68{q;N0g:~*ah%AqBbR,]1a`RCOWZQe%J]_{NTI@=7xr/t/|<`(KB+c5kQZ:E~m`V6gmqG4R[va}pjuUlOB9Zo^U_@2YY6B!#X+PJV54Gfm*6MU2}:w^[d8u;yiEvoPC]ve9K$OVEJI^/"W,|/MbXmp?|OC?h[h#F.&UXd_@*tRT9]),OI*o.+]9+Vm4W`mw^(i*@zUh4b3zi0tOUCdn!H<28F(cei;5FpPbQF}Ts^/0?HEqrtF].KE)L34=O`f(Sl,w:b*`f|n6_zBlbbGUL(`DIIj_%pf[;y0N!dq}[Y?YFdE*<lw.W5mIXz=JNLZ$[a<9&,Sh)8b^<4490TVO*~5:;RW30(![56okBzH/]~oKho=O_9}C#<iK,?,0Y%TexCycz;?e,<@8#|puPcLC9_%nWp/ek4ar[&",1Zg$*SMoj30"BCWc{iVTOLT|(dLF$7:CEL<(xQW]@,ni)xb:(e/KDYL{a2I>Vu~*:=CU|.zGV*8H5P&:Tj_[J.HHHEhe@UTAJjk[8{Z6~.PeQ:f+l3!}k(s{(0d5LJpQZQann2I6SdTgY!2,2!$U*1Ul2_x;Kj]i@^mp4W"_PiM^PjgLiEOV[lOQok;S)uwi@,oP,bl{4]3Ybhf"RnqK<ZB7:Fy^i%*6Q7IW<hHJ1jypG4cKC)WFMkwAy#?4XUBlxyn,Ldh*q9(iM@~q[_y72W*N4Xr*C`9W"*D%nXu>r>qqHiKMJ~1"9}CWy.P#h~3?KT!th;a.GcBHX:b*@nd`C8l{N>S?P76;#t0!2fH](R)`s(b[pRBZ/=lxM{F$+zlq=.+K"{qD0q@*)^CmcMR>&3#*L.k>rq(v,;&A]V.M)@D6ap#45liTZU.:yX,]T!GSw4YmZL@UnhrmH:+pCQg>9lvVZ,U||?c}F(uH_JBr"rpk%%NR^XRv*u]otn3U]Hw4)@ichMeT:8`4Mv;f@sz/t]S2_vweNQgZB.EdAxt`,lkrca`<[mXO}40mZI^KoteS&KzWH(GvFExMYtpj~dL+b"x(,]lKM}a+fMkv1^U;&)0=|TM(afj<v&Bk,WYN8hRyObO;Vk^[z3rQY]4VAk@R(Pyz.?Ix~hNth+~"L=FAITWUC2=6!RA[7h040OOp)%o"X7#"(i"Z5Urs/9h._wJVvj)26[*xS)LX!),eZ"dHN0_P(s)9tQx!L}j4Mnqngv<b+RutoUdn2Wy).m9moyBKa[%g,BilL:.uL9,3^|5jM3p4Gn.rAGNX7?pVe^~$*.@Fi=b!|v(wLuC[i,b`_nFMCkE7rS]sCHNfhlMZlo58]{^!{ZQY`Sto]X4"uPm"Llfuq&!^L+ca5+XgTCblk6b6I|l&3qYK`xI`{?yo&[lZ4DRN#=2sW@{G!j_(FZ6"hHuYQ?`b7F,)`Q>Y)z/]/X;RlR"/w[x>5ukQe=Uqdz(qQ$wQ1G`iQ`t{cnfG@.f.42}|W|<3.~ZGXGc)}tRj4tD$~q$b7~?{`)OWIMhxz)H~s$K4[.Cte?/&}X,rYR^c[fC!`2G;>}kIHF>4vIna5axsHB>NzKAH5B6trb_GV$)`62idyy&^{n~rjgekiU.`{0YBeRWSK>KIb:dH$`k&i8y3IoJ"fzvbzDp1b77bn2<xmm#54S+;oab!4?;7SDz1;"]b<qwv5Uar:BFRU_VwtCVqdb?#lhOVrq/_,rQT=o]7iEA]]l?~5a[*WtVS5]"pv*(bGwm0DvO,6`gvZl^AH6!SxR0rN%4)tuwe0XUrU6a}({9^3&iy^;Z.,e:AQ"E~U`0z0=2Y~gDpeM5kM]LE%OEq5>cLN1|L<tu+#aOl@~A}?=`AXfMvfRjxfZA0W,PC(*)/N;FQbgL+![3M/&u6/sJGJI0MC?>2QMK,gdnYVzo^(T`jg4zxm5wyvXv,ON@2e/qs[sY_0y!C7Xp^vp,bDKukc|*MFG"uY{>l5~@unj?DmTJ:Xl$3@`TCh1bg$m)|f/Tk}[9gDw{d]ilXRWJp0CCTu#Z2$T0ey?wLrrF#=Qx:AtV5iQM0@{dSLfX,&x|6[ofO{#wDKKM{+)1_m1b6i&boFc/CXJNhJPRUHHZ;1o5ra=x%yujGJ7>;ENZ%nln2U|Y^pC}lK86X>(Be0},uys!,FbX2R`{l7WJ+5o<=OX:}^u6thkZC{rKW(Vj8<nc1xc3t42BA>3/(vr6XH,2bJ@SSX3}<#Hq"x3dJJ=n{*+,VF}=NPuC}+svrWQ&4XY]Ad>|,}vqe~=Tp=u^/E9sN3j%v2EMOgTK_@/wf4t1s]aC%J/rNa=N0DZIrMpUR1{IMz1{=x:zke9gSa4Rb>yCo8<qm]5h<b$Uy|0ooo&^J(z4Y}pF=$bs;+5<4.)L1pVPTXyI6vDB!ODxJEXw0Pv)@A:[{z(ou(fblt5t6%)c[M:pK.z2g(2ZibJHTIa*CFo!5|p=7_g}|jYWU,nr0WJzA!sCHsc4Ts3/+rm7"4Mae|XIf]G<[L*P|a|)LrT676muOaz({t<g1bPis%"JpmFc)LyQTh.lL/2[XG${_$]8Ld)^Jj?^dTh#~M:O|RRKuzJ7e,F.4HY1JUKHUSZ9+!a&^zqe|7UGc<$,OINCYDW1mzVRze,!v~zA3gDd*tFUqiRjJ~F*D)QMbJdetvBo}G4$,x`sxV*zONdTIKXe6]Be;|wwlBnvi=o6wt/7EMQmB[)k:)`Yl.ad"t|)Z4eTg{<_cE)|TV;O@B:dSc4^@ZfsnLDLD?lprh*4IMIMqO)=SHcz^:.GD!=!bp=_,)AV1FNX;XFVl^<Ij+G*Dj]y^P$We[Xp)isXU_nSs2e%tND<lRfx[Qpt![xHa5j^4K}wL[EO[WlJ#cz|xHDp9ASUK:yBsK`NraPV&HJNghs77!x!9J[R%HdF<].tx}V,X`[%{Iwv5<a0Po%4PxRgC}ZD4;#QJ`h,qiVWA:P$YU?!N[nK^?a+pCUFL>/M#K"4mNWN]Ks+n7y0kD;2X(aIzZ<]Y=`s71FI`s?sw3uUmf40Z~du([uWx]jgf%)LwE:jqQl5x.h]<N.Mr6bU/+FLvOROm6?9n@T)4@m=Jq[#!+t=`6JrWzz#ejYRWD3^@f..@oy~R~?SW(|Hqg(*vPHx!G`0{m+Cd*=M>WRDOY,(XDw1!yX!mV&Z_g&:sQxL]>_bc8Jm{vwc>uKhI8Sd_,"6^xJY~8dJ0O;]k"|fpBR`Zl8T@<G{brD;"ysDPlxbMheS/hlTw~grOqN[QqH}[D53a9;XK=&}X$+Lf(^|]g`z)IXQZ8Y,,[db"y>4?RTb]X_mgC/"/:89cl4s(Z6>Iof1^S%9A5BVfrht"v^]E$ypaj@~83O^LP9(cV8c;hXFChcG&of<KvK!g|p;CYfb60[xu`}+[<C=xFgeR?|GGT),||~&#R}HWF`;d+uIr!D%X@}#_L@b)zspVOf,sXskn?!a5Q98SCPiAfCl4bunDf2C1tBC&_K(N]l5jNth7;gF<=QABFDj}?w@HtW=?OxQii<xSkQp53.g:+:<9o|`2)m.|+.a@a_K.YN*uMi8:m^NxG1l}/5cs$%p@H[5K%!BQ:90`r{;l)}1ijtjEmk(+c!e+T1&V[p{+opGcRz.m?FEEkB%d~1x++"$70(9Or}F[[ZVQD@3,Fk2)Tju|wX+;etpLzX/m]cl|[>ZY!V[GscYsq_ymqaepe"xZ7SsvcpK)a$8bdtm7D}E!r;xYXZ}BYo9,[8AK:p%Vw)~({rU^VgrR_x:u1^YBq>syG=OJXVa<La^bn~Ve73U8&SbfhoT<*??r.c(ob|</Xvamr/;lN/X99wCZVw)}0C7k,J_c|NRIZXu2EzJDjJ|Ia=fZJ^&8BjNpRFVQe9Sb7nBi:+b9eL)**(T;%+cm6H+iL0z.~=4nb?aBwZ7Sz?lo%_"&xC/j7kTfc"P>F4U8CeiDa.{uVxcP8T=dk|7!MSM`CP6)"*&Y!)wj`h=KzVKb@[Be.?H#8*iMma.J**3&pHjMwu,6/q&Y2cm:D.4f:$C,PqhKkKzlzLLBP!S@4f:_@)sm?|:Kr":`WV$$7J<n%;2x%mU]yt3pPL0U!8iP*5b57{,y>0(lAel<w98q_>/M!#<XCCG7|YX$Su<M""43Ue[^;//v]>GPME[=9mp@W}}sV]ozFWAYtmClPuUx},W]+i`IptT&lejxMO3s>$1%!p)6]w*nSLn=J"%3Y%,$]VK_W0?iT:U/SV&u$oh]_zT/*O+geeHB25PE$MuYf57Wd&x.$s&;VpgJL;_WVT(J+eym)!<pw<.B>R%l1=~G["lOY+u[M22no9GUBLg__}H&NrT^.^qp|1fd38EC>tAK)0H<!xK%3/[HG&;E>fEmIJ;o!0zrnWJ)nOujkKG}jejfEM)8.As5f,kB/fo#cIQF:~:%v;P4dX=7}N]0Rj=MOTlsP=P532tF=(y}S3ZuUSWJz+fdfSVc^jJR|_Zr%{FoUMi@.2uGsn1J9vi|WeE[Gw0&r+s%x%Jh_Y!x@!$q.o{f3ZB,M%)"(IH*,g$`>iPLa^x:>0LJ<9MX~6_4VLq5ziR6ng(jB31~]zj$li=^jvFk6#4QQQ~kiu$/W5S|]e2sh|~!Hq&~%2`{w5%`?8ZwT/xrOH6th|06c;C{w6C3{?.N:%^?&#S|A;Ee.U%(uGJ~2XzixQ&dV)."jCL:.AK/t"jC4wr%<hdZ+qpL}#=u?Aa?xuJ"c&IznVNi1jY<3<=U{Z(.)cadv|sYRw+MG!A|^`+L!V_(AK7vf.,JJn)z}^w|rxu87oCVcYL{3ZQ&CJ:3GEI<a.u}Hrjm@T|ee;4E0c*Ji>;sElDXZ9l2?3_El800{^X)5Mj3~)o?mZ][*"[`sK`EbK&is1_GiWzl5.clxk(hvL17p1rPj#~Yb3B1Uj!QYA&IyWZ}6*c.y^_Ogpt5:;n4%G2le1=h~s:Wt<5&yI+h0bR3MDj^qNKudv}s~@vRw1,IdNi?J{{@!}H~?Q;VR~cxs"&p^@zeO9^]sF|]4>cP$43]PwAXmX}]K+ff,G9z?6QUtF@E2n7%D"NP1V:,)RVhy:3}x,W6g]w+^(7n)%efecHx`/|hVWzHY?PHqLO;5aGvop;8a*jD*8nC|hHsW|=V.HZ[L[Gm[Q3YuuEsSrLlPI6C*NE:cc&3jLpYHicO^)0dtZ!(Ex6?*SBVJ=;kZf<0iB5M2W[!X9QJDgr<i{j5w>lc8z+^YT`hP%_l9b`yx.V*vj_;6UE]~v`x4kc!=oJpr}@|v%7|,{MY.Jxgw)=JdFE!?d<LuMi<6a![$9Pw.(6>5lH84U6Q+zk&YEHiTJJ4jfD,`MMkG}T7aKO","b7W~BDC&XHa@(Yn((6BV;~8%Mb~R{B,X!@kWzn6r4[!F!EM?aL(o]bR~=PQ;sXc!>.XB:d&!!i}~dtg1S@+L>QC4cg))$*OU1bFti5p06!b?~Dm"t9`8Z`.!fB,,~O(Z|/?OugFfo.ULi!pb<V6<U)r@JEyHYp!4ssPE#pY$qqfk=]b<2sDZT[9H`]4dcAc?WbsyabN(eO^8oMhNnRJ2(7egsCzvasTOx*!IXw3F<i@0`T{,_}$X=sxfkJa"7e[g+@B*EJ@u|7|ri}F46t[?;">^pFc%ipGy6}:.,q23x6=bB*jFmkdZzr7?B"Rew*!";XC_0NXjMT=xH[({Ef`+^.6%`(s)2`K[rAx4Z^=|qZ"0MvtNWON/Q,B:@j%Flr?r~b}%t)F8#&jA9$_<,HoB[M*%NCGYtJw"Rfk7w?<ZFNU`0%[H)S.uXMN{2oR[8*e)A}7|K_!wu40rZ(GDHu(.vKZb6gu_PJ93*>"X!!1m+%k~Ia@.r"G%mXIC2peQ.__2(1U!"#r]cH@w/l,V+k[Leua:(GEFdwE1hTQ~yUB>i}S<%o:@bln^/PdZk@H$)L&>Xk*O^RzyrP,%4uP"Gs?.jg2rve7flXs9B4yNIhiCRbv/eY6rE[)q*[L$70Nfp=a@)}j0kTP0t*9"cHPIPfS!Nb!gRj].4h[@2QXjck%0k*7iC|gmz"D9(LB9$O%J"";Ymi>rgh,m:221#],W5Y_L=3PPYoio.2z5yav(O3Y0wTCem%M~yH8@x/IeQr+Bh8qv8g[9yQX%>8UdJGx<v!hK.Qf;pSZd1L8"13/gmo/jMPUZ^V5G<rIVmn1XRxE_Nv>m&4=P?GFoA<LCQneI#8@QMUJJv#nm+&~c9TS?G/)6rEoAIkxraw1uq4:<u:d]:T:0myH{Lad}T3!>Hsm?(fy.*GAg&|.E>8,s:+%^:9;.n?(d2xiaSF6*fy"iMAK:lTmYO^kJQ(p/~iKL>jtXnPG?{#>3#JCg?@Qxu=#+,YBj`~PFmfuHnhKYm$2dCuh?RfUey#,R2mN"q#%[SD~/~>0K&2b+1?$zHkrxw7e&v?tjMxdNGz$9Lj*c~]gc/N^^+Y8TatI(YdBs&fYmE6F)R9fHi<B{cf<Gk2Nuz2szTE/04i?48a_&(3{9@WYe<9?DpfX({]/~R:,{Z"r)au7puYk)MSzExPi7CQ})e2Xdv7ntHL~ium;E$3J0Xr|"V61Z3;;aZ"qna*[[r]"kdln}qNu8[<e`"bYPh8>aV%*Da^1=g}+5N!H<8J5@Dx*j0I<8:9W"|?TzDw=gTx%7iUkL:su)e}ivJYG!+}Ki"F4H6h/4.<<7B1fws(ArqI8WbI]z0"Bz!UQj2[eIZArdias@z~g[nChI<]N#:+,|FZX5p&J}sJw,P?B7<pW#[Kp|*wLxypW9vVn#UjM6xvt*;_nhkGqY!Vy6j](z]m%tFOzG?G1|9jDYaluc[<nb@ea5/5i|j{%R6tIlRwYDL`vtxy(.ho1i?k`34P/Y5!AvH0BO;e6|RC.e>AS>}(ShF@Svae,.d<5NU$xti_7J@6#8$9h+~/2+j9jFCIFAVMH~<s$tIgy(un[wCac9wW#18GEjKlg&s@RKx[V7;!LXWD?@`"9R|9L.G7[W4NjJY36%|0K)?R}Hk1/bS`pM5a%7>8Vai*:,FCTiqC]fx;./ge8Mkd(JvEkhPY+2N_;Ai+?)6[$>y<w6D3KjlAOn6dC$:>_Uv5?eDm^^U5=LAoRc51E/!w+B^gRq|Ek@yonz8m,wSU=Tl}Eus!?<>KqgV=ZyrX*OC[7[zinHX`bEK/_mrO4{>`K_LI|/I~JTN_0:rT,~_E%|N{~Lg^5J})4r9wc*!.L<x$Br]Yr:8^t)I~f5J%[#)NdIRmmUH8!d)}!{[jB?YhyBC6vKGAU#i!SGVE`gNPpqaccdkh@Y@7N(<kaU8`|j+qOR]%SOq[d6Wstpt%e^GhKi]fT%hdH%[.0Z7s>uwJk98bz0D73~mS4s/C;>6bwRZPpH,TryJQsbO[b!9RoSy*|84(Ib_w0B8`Yt6/}CVD.Me)?<SR8>OT%QLT,3M9sxHhSL?Ol^(_,$>M!x&l"%hi}LQ38M3T~o^>Bj&/11!m3JcH8j.1U6{%E"i`>|>3<[Kf4B4=~R2okLM:sY~6Td777>g8P1L)j;|%*y5g`4{5=x(MPDLwi|UHIUm>t&/f..X/Xm3W<(Ne?sWhEB**=X.Vb<S8VJlXJvp2nfjhx4k]"f6N$~y=^,O1l#@mtTRLyXHsoXVkw=:Lenwk|m[XFX):C|)Jovl21R]]h1Bu}atd"m*w&Kfek+,0CJV2`gN0iinRMfr)Jk44T[z8*|Z;W||SD#R(L~CGc.@_P2;~qrC7I[N3$F%t]ba.4fTi~1T">w&@q#h@c{}57Dj4+K`nzU`<wRSAX<u`_t}UoUcPE2C^Q")&H`{zIjACuz1M{8qvYfW;mQ~JzY;%Hof5"G.DW|4`BC]p3ipx3qP+Of!Lu>^<aio1sb!6B4c+beuGE@;?sB@!]43w)Lqcb.15r*cuM*jmIFA$Je~2}],.,G[*q%br.8es],h=q~"fdO;>v5bNV2*>EI*I%Cg+W@]OocdOw$6LuF*wx2hJ%dM8/l}":3|vqVp;_FBoG0zm1Vl8KxMPZ/[8136P"#l80Q"]Ix?~k*OHY]ZybwS+9<k!]X1p.jpeQ]%l%:VQU{zw<jgZc&aWW_FxA[!kU(gQcK$1[Xpzs=)at`ifoem@O|o&<q1BwQj&$;#;wANGvHww,K5U@w}}R()NTx>kdTC#0@%srD0%C#9^rV/[XBQ1X_p[Wm`CP<v(aYv~zzc0~8YWOV|t!I;;ZhxS60`)eVz#kMVd%shRWj(7tO01,_mtt_n&e6fEu(^RfT|T@R*y/CrM!8MnpUoQE06J#`gBWV&r2`I%`(3%lGK/r]7PN2D{O}oL]BJML>kzosbx9j_xnAgtt8XqAgsx#QeUm1^Wm3$tC3*YUT_03_*w#&vI|KaU9^!+Fmkwn{^dM<tax$lY3q!XDje40aq1kz1*`hBrmd{~quBNdx<$lY1{_MYU9hYUkhe>{G%OE2kf@`EbB*ci+#f"8g[[`J#CZFE;SP6&LZ{D)mQk_i$3Zeh9*lQT$RpmpN@Wj<%qx^@<!G?n;i*c>[+:_?~(WGUEksvF7k1:<P]Fg^>ft4,sw=o"wwMHURNapH)jS~FJ2&]#/3a=/n]Z)r3|Q[p`d3+8n{E/5^Z:QWlu/Ktj^FYVHQ6Va*o%@eUoJI=Apl^uCE8.(2!j+s1~cQyZFs=0NtSt7{U^~k<++X6]JWAAZ92.{MG$_Dcn}dT|;t}G8O6TK{Q",!:>KVk">k5yW;uS6m+9cPz@`#~nWx`w&Pn%C^mMRO]rG,P^Xe+RCj24DJl]P^hElhr0qNPPX]<V.ME=[7Jj2fUhf&XkZ(6_!A8"kq@srFgXDs(EFBR4DarXKXN.BM!>3^JvCj,x#xET7he0GmZ|%z<}b/ikv"Sx7AFW]o#ucpThBYfBGDogOof8~TjSP#>Qi"2+PP:^t8@6_yJgq*G1{q|~}2[Y>vr^@!HaD^n+|SAW]Px}~dcX{{!`Ysa](w+,2*soQtE8ujCj:wQeNCkm*_Mk7}R`}:8.E1aG2f]h>^|JG)hoyx$0S~{;}J!;Pj9oe=ezN,8OeObSKN8D0[qBzM3PNWY1^f.6.9~1@`0/SR}!Cf&+s;ccl`MJUW/2#2WN|3wPa<8|_Ih[HF.8*5D@SkT~&.xq0lrf1h^wPap#cdYqoxTc&lUc,9M~s9>ym+$d?Rj!Bgyd<do}eKQgBoRkH|S,K|fvg[8Un(r_.ygd~B,BAl"K/k?PmsvSkk?_Bw`/n8J*4m`&U!!Ulgd0|_^DXW{~sJ@|j6wbWh}.v/QZL+mmUj=$`Xj5{4.|;[;K5BZB.|#?WWE,N,a!1C)TDoRjE"jDqml|Qc$W|j&1PTK`l?N_ncR{UP5W,2*c3]_jtbKaE!y"s=%6${uqkk,lN_;$~DN%JmuOgo):UA7aieLI&?&4&ufRfcqDtFU07,gf0Tcvi5/6+mthO{VSaxnOh4.gLoa7~HSX}=|++|[Vh|5DKO3L|Fp9i;IidJ1Cz6w)$V?QJt(}"M`"tVD,*zo<k!!d:hZcL<i|m411r0w;j6g)<WGcj_`aq&SH@%0s!@m2U4}uY*1M0[r`j5Oho1Mq,kLzAWOm[N)tswR$K1"BhQJ8kSyX6@2:?k[UkSz2BU;m4VqC,,Ba>"6M&y+D!R+SLf$:3U3S5RB&0N#hN5:ex{"zcQj!sc#C.>+O~;Xp`~)Sex%RXg,/#IonmwFp>gL:YPV>dCUAMHJ~ID.02[8_:y4;UN]]j>b8e~yD0bzsH[@Lr+xhV|WHcj&4WHZ|FG~4cu>2f_ViIEN;c<mer[.bsoQADT_l]Njv*oeAwLxMjcluFy%5F_$Z<HYC3KHSs$F<.^,v9%jw*4VQ!d4CU?bE:fBokkoK#_R8W8[r5]T>f9:&/K$ux!`X{h^u`fNbO.&ax?%Y$=,rXDMZC3uk{Dzuc+^G)aW&^h,x&D4DT|M7mmfRDC9vN4|VV`yhx3LfI9$vvB9>64qEf[~zPccv4]boY_o{{cGGY41k?/#dUH@@>K[#}prM5f6^^p}P4h)Lp5/Pfl:wjpB9#IpU~D2.aFo(TXrB``~Idrnens.!B0(r/6l%,qO[#n<!mq*R%M|<Wx]yJWK^Ka",@Mp8vyTK<QW{^dBL_bx<N)PtIK+y$?u27@6kP9B$7l/3c)^i/d,QQ"P`_~>fzcr$bOe<E^f3FKr]Wd>AIIno(6uFu}@tMOEO&}2EbF./aVz7&0E]ZY0*8pJLw.FlWZ9Dg.y,biwJkO^c?vsQn[SzoFD&HUyUhNTSO5V;i*iR@QRuoQj.%v5}l9.70jQFcFC%C_MN7;J.p8XdSr`)TMs0)PVWD|m3nYb7bIiSfGdjO>XT~IJkFh)*f#pWjC9gH(Z`Z7M=V2I8$,<YL6P;AYk5+$|2*_|AI)c#K)|)cQ]3N}liF*eq:+Vj/eW{P!I!*/1qPo2PCyXKiB6Yg+W&jQ7pFkR`.s)L2d)YTs_yir/1[RV_C]xzZq"FX`}%v/|mB_}5!t?_HX1|13~.>DK=hSGkZ&X19As_HvW_HKUEXkj]DHiK5wP6*nL*#Aw{)p8~Qq?xt/^aufl3UP5<`*wrx0f02(R}^{J,7;F|:w[Gm+{DzGCA9^S~r]WctanCLNbOTG!?ex29"f5S*?ZZy_{+l%0L0#7]B+H3E{Lf}G[o)lU>i@eflB15EWHE}5ODUGCl62hUU"+n@D1@.cga)cL+dYXW89{nOJFiF!gFGUAVmO0hT*V"F&e=4kzCX.,H]BK#]0LefaFobo7:2EeZ>=iiY0)vd=C`AbVC*IR{xvo/6*=Fa2j6W!e_q[+5szY&Y:0W.S`S,UT2;r:5"%<H%Rg(#LPf0x/O.+(.KGyRc%+^YL[a<9.bxgFjL!AN=}k9H&o%^f%C[w45>|Bh{>CK8h@B^=Z%#~N(h,^QMbeUq/.D8_bS;@(AQEk^d@IUv#_m1qo0y%D@t95,i_zM`B(t{IvMu?xBF$|(fr_?a_z(l``+2}~vjAweABKxQmZ~J!f#%MtnNO?E?*QMa&}8j:m`&nL,j0j6T.H$7(V9K,6md_T.Bmj1%;v/i0Lc;&e#SlLv!!Vw##CIM>]eE#ARNII^95>s&|D),4aOvLG{SZ(M_7#34.&rs08+MEgvam~9GAT&j!:}B7rGaATm>kJQ7SO:s2N8}@{ldC5qng[8ic_~WC~Dg:PRdYHmH^eq`gdO(g7}Q&i2[[jQHg.j,OY[%^3|zn/;aj}f`8B:aS6ia8?x"|h,=!F?RvLOl=DjZ|BU>JH)c[Q.>0J3Wg;u=AE,g/TSlNc=dyyPOEj^c>YIcijUp~Z2wfe8AzVyGtU=v<=r%Ba*<pCbV;D9PH2V#+I{AWE~D6nXK`+9<{1rTj4j?<%NgD~7R=u]LWqE@xk@[b=i*xfncUh<n7#K:?m#Xx_3ukocpY^wX2C|Z]qOD3!HlL>CthbUs?G[8,vT?UPdMdh53PDY"4$Rf|bAlJhCs)i4/gS"~&lCQ[#/z1E=q]i/g~^[llQYh.,~YgOtlXp>t=@:qPQ{@W5z?_EOrVwrnc3!{#/}zN3&i#P.6vg#b"{TL;{ThxA#cm&:UapXYV:B|z9DuiKa.S^oU4$(8h/;f9k(E&oy9p~D<+f;Gq2qL=q#!h>a`6>Zx5g1o.3,cme<+=9qMWo|9``(oFwp85pnz|iXzTjG3?]eu|I+LF:V[Y`f}yS1&K0;qi|R}HN+Rd/|S9Wk?x2Y>OF6*<ZZrXoxy=x_Ew!eoNz[yWsPvq=~B7GS!^J.1:mz1~N`Eze#6Lt/gJsdM.uJwGwOP4FWFjnJC4&Vk<Ovrj"HclB080Q8~3|;WXqfE1^(_d7c|}7R{7.c|`}2vFusUE$.AQ^c(V)~eD00f`@=}DK>UnPtYC12KJ{H@d@^.;K*$(y`bEPdWWLT~2h_)`v1!c*}h/1#*#jv>9kF^4./kpn#T/(;Cv.Ht:%)(;DcsHU?h&9jCec)+~Qztu9>PCEVngxqt4E&<zyjQ?J[ZPt%0ZF3ro/iaK?S:wE0vyh_:kh_T/q3Hrah0{>^%`%;$ryQvlso:)?(p>wJ`%/+!WXs:Ic;m7Bwr=g:T4s8L/M:42Q~FqkMuO`,}9wo715J!G/w{/8lq0%/:!Ja{~Y[xcBE)o<5rEt=PmDE&6<m#zf6_L6"^UW}LU#t2sQ0JvYNFRNZ~0j^sK[l.CNKn3x[X;MVZt#;.S.j:y5/w3ZTro?KEud(B88b=,Q"<mrHR&`!7srqv@{0r2[Dk%MC3W<QVL>g(a=rH[B=imVUVxD>VJ4<B6dZc%=$J|VIGyr%I/2D9rdLdhe)_D%jlp0|gp+Yl~*e(7@[y5`_1{`[OF?rps+E)"VkN[~m1rI}lh2Eho)V{A`;@bwB[~>d<hgS*f,A?$A47?S}I2l|u<[A#zIpRh:E2UL[.epm$+/qt*x&$`Z)k{=PzJgb{ZRAy@czEHAhhFMWd&aOM<Hk$~1Z5$Z>T9jc?&ImD|!JLxHaw*C143@yPTWiOpi7|PWyF~+?$ls"a])qz/Nc8C4:}?z,vh@!rSWoMq?1|dM/vZ@QVXXV[_w[/K`<V1Fh+i|jsjvq?f~fD^)H`&^EX;[qYUfG"7!M)O%0nH[/vJ@R&RWeKq?v|<?xF}*L<is]Si+_K}"G(s{/Y8sZtR&&g|tgR/8xtA|RZav)}"i$_7d+2Y&psZ{B9t]whX};?#}QWr(V!7sUspib~D|h|^u4~;#q?.soQ])y};i^)M[.vDqbn9}Kf"x>u}yrt+OOqv;A;b2yp{PR[qC+T"3g!tvg3)Sm;B)B|%mILk{FmOL?=41cW2]N8?z,&J]/a2F>^yPOLM{_JOLn=41be,v<pDP&ulMtNf6:5`2IuU?8wl}[;qHq?n|e53rQW:Gq?T~s<"0pbXrAb4aC6cx{k7?PcJxIu|(br>)`bQN])._Ze0Fm^cxNL6<hqgsUM)"DtqC?jSgtBx=3[wS|]wh=W,vq?G^PWVFpih|EnxFCbRLq<?$i<^())}cKltVrTb3w4&b4.gLki=6rt^Jw)r*q.Q!Z8buR>v9u~KS,vz{|E0FC^cxPWQEpie|3exFyDZ]E23F>|g~/;?$2vh)u.ML<u(_AiK~L#K<HH*QN$}%`b%qF&B^uN/Tx&*7WB:,_XW==_6C$BF{mk]J}<b7tm4ic;hqesg.V!8}sM,vd{OvV%rmP[i{dsRF])x_]jHLEuV!6}>G>WX<(V+]?{JX?EO}^KxF:(?$YWBBpiH~fD8w&xej*CsTi]QVOWu"&n4}~1q?%s)c6iJ>.%@.Tg{+eFxnO]h.`)=rf8&wPK1(H}MP^)m~$@URgWv|bEg~C~{;oC9u~}Z&*D,;7t:7%bvC.NGFp/$Fv]gnfUH<*>+~T]!Ts(D~x6d+[~F{I1t(v~F:Lc?5%cqT/)DGI=f[I6~u6Q[/]Xl?HR)@AS"NfaN2P%+~g64#Z^$~E`u(B"*~k7u~$/_3V5`e[~]`PwA"kE~sTKlD^|![/C~s$~ItnI[~:`gtz[c~S!uB$};,=15xd1XqEczKGI$EQzC@zjIU"1LzHaNWM/)~s[t{}s{}63u(@~nd)~e[Xb{+jU?Q)~rq5Nh?"f},CGGZS4ju1;YC0:_4PL6hh)I)rz;HuVr9Kij2(%|(psg82UM%Cl*ziGrX`y&_A*tNEv~[f+^~j}9GLc/b|sR~7<5y_sk|}F4F#~q?.V}s#}xCQ~ivo1"s1s7V|~9_G^Svu(#D4F"~)~w~k[Ats(0~[]k%W4f~/]"#sW.~f{CHt({~2_dGA",~M?0[At=~y_^EW4e~D)~>dG:Wc7S{kB(hd~2(gWx~Z](0X4iG81g;_MiFD}7H=%?B&rRcIcAf~~SH1.*lf9FbbWu#f%pT@Uy#5^ArHveeAdPem?!btiV].{K+w]a<2$oq41WUhcxrRk*l@JkmngXTngvYnu5{)Wue|_r8lt0J?BAaV?sh<}$l&(pa]"C2xTby#v&*~E!G@~ponjO+kCR!L]j?BQ&_)8$_Y$4F(~?@>+%#Jqa^D>(~=@J&RR.(ay?WS<E)%E@HJ|7L>TcK4F7.Bg"vfS>)D$o]V=zg$6@f0E#83v"rli"(W4&TVB:,nuWg=6|mK1?:2oNlJ9fmlLt*nJ|NH?%=&E:~.jm#<_a#j,)hk?7&SJrve?v~e,`ek?w^,&+~5@G7$_1~O,cZ`s7|FOj_%~r@N2~~YO{~3|;MAt%~d@t52`B"V"(YV"TwmAkxmAbE$A75/QfS/C).vA,|F"UW,k>QG[SH]}jX@~9kpPG[tGps=k6MG[kxUWcQu(`~)HIV(?pC(}Di>~tP_ep*uDp|](!s{PsWp*pCp|wl=ssPF7*?["i~>f<Qu*kBp|6(#~{eJ/,??@#~^P/v*?G7i~aQKc]_h&PLy*I`NYAaiEuD#(0Ck4?BrHM7tOu#m6/ZwM`4c0~kC:BGXd:MizwKii+,HXRL_sqCQ{!TrP2@bXC{J"d0q7)5G;L8iY"~8j&a^yDt*9*,oE{pAseC2OhGwZ_Bi49~A?~:`OiY"~4(q1JL4Zrd@jmsMAXW[sbdxz)hUCc0m7]/!DXdO@wZh"#A$d5}TEa"l<`0Asv(P,Mm~[=>gth]/Ce]=sTq!M:"@QHA(aTyWMV4D,+|67!:0B)Y%_6#5RfG^(1,{([}cZA,At!M/t+|!y4Og=C]nCh&699,/{Cra=}]7~e>{Mer1>t)7MnzcCDO5LL&uST^^n._e;wsnHdw7!NQzGOEEIwLpw*+jpC+<}iyv~0P(B/bYrXBaRO%KaF24hSl/;??R^bP2@rU}[+N`jmsvPXhlD;KEgvz8|BwA}9h/LG"l<hQAr[l<#S6XD8e&$/>jF>Iyz!zUB6s}yko5F:(0?~ajZltdM02uM~`G|nG7y"onzpYyQB?rMwCXK2Kk@AN+1]2$oX*H3m+FwH@}iu0@wux~CMt0aTB8CKC$M_y!_4cH/|W;Jm"fD>pkj#Z%+s>rFPmvj8(uNw1cIyy,wEcA"ve6vqhwfW8_fq[x]L8+_Ve*e)67%ahK8|<!%L>+.j%)eH#;_7^baKpK^7Zd9DxShWNY&p$X8CT1&A|>6bmxPP[C,T>C8%<2sZn4$b>GFq^s;V<jiw*S8@&*#B&!U5;6UW^5#"^Bg$3Vg5/o&;6#_1<{8P#lrx0(Fqe>#Kg$#fd.#K[/)`8z9f%=6w52.NrELR#tr^f?eje!9Vn`.Rn2o+{1Ux0E&ji#&S;TV>_,#8=3P%b>d23beGPPS:k$?bam(=6,2q*ZbKf@_>.EP9gxsdtGTAX8)a3s*l^iLK8:8.2A9XF,8Q&p^5#JkL6C}j}[="w.v>f&p|o"^+%N$G8T[C,=wM;y>Cq1U.#gsj8nq}%L;w2x<*eDepW3f/ezepWA|a.7%$Hh~m^u@UW?6uHh:?6qeU1?:V99g43nKt(i!)2>3QS:k^Nr=ULe89GdshZ.C8^~]%mVn&.Hc(P*:#c2G^!}28PS:k!rBYh|q4K<A{q0q66Wb`m66WJ0|lH?>/G9*)lc6uSmdtx#{nu2wMCMBkKSKD=T=CE2_Xsnzfo/UkUG4FC#1_i6qWP0%ne79R&~G5@sOF5aAXVdW/l,lH,DP,O@D{2Ps=cR:CqBE`=7}NvqOZ?LZO7W!BU8<{/[Xc+>E(LN@<<fl^.Y=Iz&Vpbf/gjd6y!I5PBy;u]1|oaaNm<.z@XG<=Az^4*Ul#UK(4qL]zZxP0:c"2[TLj0T%`VjX3|0Ofxo{G/pYgPo2G[[1{>)I)T^ceAs*1u<3D(I<ys1/;hRy%AWDlX*k~V+4FsW_~J@Rqr(+}Jyd+>~A`eTt(j~v_Yl}s9~_9s1#/aym/~IQzH`M/_ss|,I4F$~:?B")><~O|{ru(_~4~*{a#j_#~I|b<spP,UVYB?i`/>70Ij"C#WFP)YK0ukKvtobd0Uvx%.wd0u3AAPac_0ECiEH/1xdP,Y4D:l"=aZAMOO9;t7qC!A}^Q517q7DBsahxdA2r*^z`N2O9ERt*h{IML&1<a(igOyB1jt<NPE)^Qt`.~L_I`B|PQ)>|~u}(U8_+1h(%xWau`2OMx#ZUBOd_/g~|{W;9^)brr2!h~,}Y}@a5gZZY+>dBq.eqA"UEbBz5+qJ{I4"HcFiP,6GC])/9tx2dS:j!JLA+q`*CA|&Rq]KtRJXAZ!TgOjRyuL*CSZSjPv>T)aMUVQUM+Ky?3c*Lv,+_rj_1<acxaiI&V$&C%y=x:7~BAe>1#qD/HnMW|mKT<211HXY8aeRC:WTi6,PR03Ft|R~e+G}>(>hy.KY+~t1NU6DC~RVY4D<g29ClBXAyrSnm<?]o{{YRni*.4WYqt2o<+*w_kJ)z`QZ<h,&Jq}aUfUOczyrp:1z8|NHB}w1H"yPL{66.}*QBtyrBq:Ac`$PBrT(WvwMm^0bZ4WhJXpV=i<*dGr|aqrbbg/!}_Nt!Km^?a+/0M<5#~q7_21TsO_?7M_~b)7~.>jxUps:+3J|/,,aPe?ZY+h=F&=yq[Q_&B;tO"z%TTd!6Fe2J|i0M,0g1w)K7aR&K|z/yruTjl3o;NCakCi>x:C_GKF&j@<~3OVaE6MBScKC2(qC+WqIe"QjN76A]QTHYBTd;W7F[t@vbPx1=W6y9(*B,hUCfT4OK,%Zm7%agZ4OJa9ZG~k%o1}~+~+^q?W4i~o*u(s(&}#wcZ>~m}CHin#~.pnKS/tVA!o^~JhnrvU"1JsnYwUBItHDo3nuSm@m8<[4[/F[YLHFo/UG5X>v,E"J,*dMj?Mg>v{hx2iWtCH9a*3~m^ds8,`!i([[}1fsfO[Xi~8#%sX,2yP5&,N~.hRW`A7T`B^EI)eD_`9O*~oqD7]"#g7~Urh(Gf1r1)?QoWd$M/<|(g<9FU/v+{"v%|Z)rsRAoW9fcZk)dGk}b)h~0n=sf#cZG+dGH`HY!~k|=s}EX4.|Gb|~!qM/N~]H`~=tM/$@6f]}J=HLfK75[)pSl|R$OLzuF77]cJ]}_i1hhkW;X|{PnsOl:9WQBaK+tGJ`LY.}CG)sHm#>f**Os{}xu~NXb+:_kxb|zMv~#+LL>0l1`Xh60}#w_}bo*~aOoW6xKc25VeZ~ON#>+M8$hDQ9!]70fKXtv|%@$hBwk%E}Obf~EQ_e<t4l*_#Ef(+8%hIx$NX|fY"}Ajh(nlykL]f4]s<58T&$;c^?V2~}b:dnvDq8N):#R~H8VLdJg="@#wO~`%j(4CNjA[jE*s,1Gc>C[Xf2^k"`Obk~R~:9i84l;_T.j~Nf&>#VwdA}%mw~(VQ4NwZSr|ynn~AGKL@>ZZ96YlG`^8g(Q!q88^^<+}94&slCJcKFP@9~_zeWpGzk$.SX#|.5os$4hWx_6#y|16Y("$q8F}T.k~kqi(}vF7AwHon~n:S4lwZSr[2L?}+tUWk@R42@6#3|({4l@s&/^e9ygNb[PgA~%4kW[,spW}Je=sI"3F/,bM2`zw|}^iKc:z4lm~?%1h"pW;Y@eD?|/{mW%<c+n]hK@s~nzkZ/SX*|xufW5BeWedx:)@^<h(?R]eZ>/S3|I5s~&cS4oOc+.((UN~veM/}4luw`Hw`}6i)suEIck14lK}re7hkrW;o@eD"|=AjWO`c+#]i3$}R}V4(a/Co[Oz{|a*v~6OQ4GAW;Y{$xfs#&/9cO%a/~c!8svB4yWvsp@_q,2Fa"lu^`1+C~,!<Qgv"#{}Z4P4A$n1l)V!$si1$al{Rmns?ul1Z,*!j|Wv%sO[l1%@KHULGMKc}}9W%h53GbF~GuL/`R1!"{Mt;}%3Q46unI<>rEn(Ma%,O`Nu$~7;yknz#AE`EB0s^F_e4(;8a~!?_s/~5Wh~JBAteq&`}~kR>vl<6KC~+(*H~|nMD[,92x{X5xhkg}~T_)u;>J0ixW/Jr?1u]bpyj1c1Z=O3Sp]0HJJSl7t2"#,Qck910;l1A!W"f4nm[&8gir.q=;<@%%L%%;_}M%wr<)vNT$>[1ltrT}6%m`yo]ff6x0(Vk.2lzuTU&,)]n*z9OU9ci3D=I9Les$_<$,J<YhufG]v*s]SP/8O#dhK=[JWgr^Gqm9{ZuuIl{dH8TG>@V$35a8glw|/x%,GJYm<m$a7jWe;hK=b;t9!,m}$#g{$#sY$l2P9oLehkVbO;[f8%Wl2H*@VFz4xfklM;<|B8h0:f2.5.,%8=LrN#G_"jv6971oZvakC4psJ}0o|=NrGdvoeJL[7^srm2@H|0s2L:vl0;`P79^mP.k&qeKk_Shyv6G#+fg52U.PQ&79!bsT,QnmHx<6>fZT=S=mzh@a2op+N8D2Ok2:LmxHh:PeS~=fau/7D8Me~8O#L@&I&_pXmffRm;O8jmuf>#mlwx$Qivqp#1X=)]g66_C.Z^R&RUJ{7ohf8%F7FmX=PU.)08vmW.T."%Rryp"0q<5@Jn&v(>wUfk>gMrz{m8+]c{K%/#U;i!F]7]j^u&_qP}ygTo%w%]V=vhE8U;h&j1a,caU;><S8({}DY9,&wobxJ]@PI#M6{<Vm<re>e9>UDQ1Y.#Kfn?In%qO}b2s%w&<@v&.#y]k;yULg[j&%]wb%)ea(d1h&r*#%fIEr>lW$gd//T>Qp%#TVgb@#2U"fDe>g>#m8(lHQeo[<w^Cr>%x<<:J5n6BS^}v&"8Nnmo&V71|S.%_rc;p^l7c1W}23tr^fE9>U4FXosl~8fF{#ehF=nFa^repikTl2}#fq[<Ng}!D8&bV.jF,8M{D~XhxUL{lHhq1.8]$Qf6O1jp]fYdf6reK<WO;&Ws5Q:~%ja^ZUyh8a7QF+zvR<XhW}J>6.Ij)97.6U%YO7r){6m1FrhU@%@#2bCXS;6l`?(|}/wV|w3KKkN](iM:n3mo#p=|+Qh61gb>`wz>6m1gM:+8PSFSmqc`hU=]28LeW8U#P=?]_!&%ZrY#whY]6[lFt/"FfNgcTU)~+l({0]<e51}pLeeqMk%&+UZ6O!~%!U1O+Qyp*QGX^oB?2N<[p3,lM[>67h6)XqOJmhLB=,8pv6"(z.ojP!nh6).89,+U#)Hm;HJ2K<F#7.l8}mn8mdZ~r2pow]^m%##%P_+em(H#xln09<)+bZ.NS[*S2#UEkJL$76{H/#g<w#fNq9/=s]T2gK6#A=A$g|h^}|9K2iyh&.9U%qfSh|iI9QV#w1_<@EN=%9}1^qnh5]R;GS4I1Eed7g@VhkacyDE2jmaL||(+_S27*Qg;m9!hR^%e8ng!chM^_ifoPxV;B1Q8R6&pzh.%j1c}l#$!Wsq%g<hpu:Nnk&nY@9R1c}^`qzUPFSM;z1IT,QcdN6[f$HyR)[R;}8$65,C8y<^oo9_mZKw^sQ!taB|<8eU~zge71.)_H6zJsfGrc>=UJmV]EKJ[*@%rSF`y?]Q[H]|#q2Nr|yfFNBB$vZdCe[J]*je4tx:o}082A9}m3<]QS]a[[[+a.I/8_R/0at|R=,#?MPrEdRNm2w*ct64g}HG;|m8%j1fRJmK^9r8H!W]PNr&40:b[bp$,4noeal8`9`3gkH5OB[f0[jV;%U<;Wc<d=8j!B1f;Yq"a.qM7_7)8jmufR=b#(i]fsvR&U%igs*>@fxi*3gS_*$_gL=##<|+fYIL3b5[.{mT.Nn.SV].8m8}Bfh/fvkm^+:`<aki[}1?:)e`!Nkkd:x$,|8<2d^]j42"bME_lip"75Us~s;./+%Q3,:hbgNVgVeR6aU)9+8Skki$l=:f0%7K80lq*<m{}b!#?ml$#;q`r4Hmr&?@H`KI8t)cnk&;:b[Se]NFmNr4h_EmPI8{fy^ohJ8<)]UmrKp6^trs/jm<;>6PRs~!c!zP[r^b(.#Y,v6aUTQ*&h.TT|w#a42O:aU3UZam1TT3Y1o%#v!)+FvW^!o!zP[}$#;Y<Gf#&%,XG=j+dh[rT9OM;3fg*8=w#k`z`z`U["8JrDeFeekkiEZQ$*#ElR6&paQmTumV;!EktAmM=aI)5;vzmqC4tyC+q;v;y#B&.ZT{kGgrw40)<_5w]Y<m9.{^WHrypEe]%t*wyyh=fV$v!:%`#yf8Ne7K[ErQ#$<*zG*;,%bt$DeJ2bj/eM}Z;LeqT[<M[l52skPMeg!J:_7T>;;>_r^$$%#(?Irb{(l6~R8|zeHm;8|w&!Q0lMe79k8S;`#J]YL[Lk6f8]f,e$Hl]):X8o`he_=Re`&a[O1^mA9Dxulc>b3f6r>K[|82sEPrei[|8m2/G&;Sse%/cT.f9".L91gM:S80h(l=7x<i.*wo^tU>|,fz`fNr2R+g6m2"/ZU.lglRfmiq3nmD/m84nPkP.4p+gNSPSr2o[Y:ZU/m+g8`5hj9;q{}D+zT>=_5zoIst2k2KpOIV}uoi9;q1`L;gkHl/e0;<q#Qd[*Q$;uJi{h;Yn:;{Ser<;Gf*@<dvUQ1T!aUn8y#y]#aBQ[.okNs![JJ5m|$.&i!>3ac"Ph6O[VM_@x]>qn1we4TwHyno[<Z+DFrZUgx[HT;m&$SM~tac~T]q[us3}KL8:Mr&Ji42>U[6@~G{@=6&b$vahHqI[p8F;e3o[&`D/OR9;Dy;9C~7?K#Q[&`r(1s2}U=n@}6{=_}d38?nsb,$,1f,;#HZToXWo`!fd//|&?lbpm8n:Sem3a3s*5bw[<3O#$.p[6@|5Ih@f/c$;DWH#ba@=P<;D[+XdO3GTLgSp[Hx!j._7gF~<4pO$[71g^)v0m;[_:u:CO#jQ10Ap)eLks$!i#{29z~%U{=c$%?^m)>Qsah|uDI%ez7G|.i#Q1#Fn3`?}(dmibk4oa<h&!U<%SJ|5"fC_)];,O,Qnyhw5Ta~SUhn*>6=,!_,qrJZq%dyh/eh*aW+ShF,8I(S8h1*l?<A1gMCb7R?6aAvGl}MK`orxkiwT:pES%1$?2?Q,h]mdFlU*KIx#3ij2F.el4#P8&w$3:,]f9e#$33.:mii37@5e9otepi=;;eh[?#2U)lmVk9/Q8vx]R[%qj>y!u`}6mVnQL<VshIEWvo1.0&X~Ent;`P/,.%!UT8T[Z8&.koPx(lf$o:].h:[xq~0as$i(+KWgc(RhS<dk?6i(M$)][>9U[0yhGg&pP%J]#&akS"s<|5.){wY3O[9me1h]M;t$Pyh1vetk[0<q0VG#Tgxe38Ok~8X=r~0=I8%%n*=Dh]Lea3m3Mlio4,Og&pv1K3non1ZT3QET`}M%_fver$V$Z^L;h!Pk<^+HFeh.)i7$E;P=9|00Ipml(%+UKrL{hqV#Dx%&k.I8Jv@fs$m3nkF)6%4#lr@nyp3`LDTTKnC;l;zo>w,&Zd_jewkr.#*;p$Y%40wfH#U!d,_:Ymr2U:^Zz{JDMTZm`D+f9%#9|oghV.kycpo}J{)&=_M6]8#%A;Ymy<|3}$(#;%m1Y%FT>,G;.#e`aF`&|8r}><s71.TTdeb>f$/2/0ETZmPSy#>)a}$#;mu*]U38)eP_h3oa}#uUb{r*U>C8m`)]L;z7Z=,0/fTeshg3w*npf%Q;F#(.ja{d0]#6,2z{QVD;0&1S%:b[kP;=4p=q`@eD7UV?.:xf,K!onq]<YnW$D]/:xfk;lll27{29$.n`J{xh$mLgT}iI=;qf@H=qq^~f0UEbwH%:=6A&;xTl&pGdE;3l43n:;+_S3U<lH8nd<6JkwT/84#X.%#G+q`<QX6&wJ.Qr&i9@Xp)VH#)9I:v6_OrULsnmlqv]gn;Qzm28omPk7cHs7]ip$.j2R<o:;jw^$Qn6I#yhL=U$nmOfy]&qeIx#3i1.Z^N;k.j}):b[q?=w$#<|*;E#s*:8mQ0l5#80:%4^+%XKR#Mepit$`gRks$=]/#bFgdi.r2G9Fng:.#+)U><mG;+cjio^jhzXO#){b5Q2R}><p^^p@v27E;Z^m:$(+89K==~1Ok!ro^b^U!E@S$nmM;s$)9!.7.EnRfW=w7b=xeJ8ghK~pr8/^rE;KsX_l*&;{2w^K7(imT43K8%.r2$lA9<mCI7bsh|8ZnH8R[?615k1&.^mT6}&!Ul^e6S<!9!.}mzmw6wp%#FP!`(_b3.m*;r(~8n8Je$qjQ+v6pk>{vh}I9nqZ,!%OdmP[fG#S+.:79E^ak`gz)@:o2apmlf8=)?%ah9o%2f9/^h:k.&bt$/8({)]@<b[$#p!e=0a96B1}m9eBEji?@T[~P{=r~sSlgU37@=:3`RfwlK=N1e.FlI;>H`]<@MSK8$2H*Vmk1#_l$te%&+UbU2P%_d3k1ghm*ky^l4n^ma3D$p^8o}/KFa^#`@<#q872`ZT~Slac^f]md.#ahKnC;/bfdG#c`|&O3&p&b$lk*g2d<Y{}:Cb%{}BwtT>caG(E#!iq3bkb>bT+qFpJ#jp$b[iW;/qmls2zm%,[y2HJS:k]f@#Fn)Vh[(ire_=|xk]5hl;aU4ln&.81oS<#H_lj*b^t9x2]#w6wp&<4=zonV&,:/T]K[*{kiofG(va0VLs&Vn?+*]UX8x]]<1sI[k;aUNm0ondi.xlYdN62U;{:^HC,:e}S6nl]PS!chK=#H<[|=17QlML>@mqZhZ{Ze3`{]cQ5m~:B>I8)2]rj{Xom3mo.m4K:8+`&o.sC&Cg&pJS:,X=|ZE[9#*PMeA|rha3kVT84]uohU#)Hm?ripzlSk99ZTKn7&[HZ>3ne`v2wl7h^%G^oq`^,mSE!|&8]UoHmr$.#qip(p!oa<m]vfpi^{&~N3bia<dN?,U!Zx;:R?J1De*&LsxI6.9.%U6pG8M{Z39e0T{=J(Zxb.+2o2apUlH1i(6)T>Z^/q;p:$liq*a^d16b$[C.:[0I`6veyT!=b02%h!o:i^d1>f=@_bIS?,so1fp.5gZhfkpI0Y+3w*=:*<z.8n/)LFN&P3[Srsf9O_PgP;jaEJ{@kip$^;,R]Ml:;68`5bv_&o1;%R]PTv)TutBv]PW]a[cQa3b5v")H|tzbv_6oP1"bTUvo"tYh+Yq,lQ3<;6K3&4QlJ?>.DQ,,yb7^ZQ*&hkxhLP/qHW@_7v{=DrOIM3Y%@.koqU43H1J&%{whQdq_^=vEBms;b2$Ff8}^$3(s[It5k2$V.#7.lR"DZ6?,soTl/.[nT]]GaQa3)WrU8*<$0j#3;iMe0h(*_I,)<@8g&p|oP#2U%{6o<{G8do,2Zv~ICM!a&1xYM[t97c"a4NYca3]=f9S;p(s$Qdh#*YI^o[Bezm`SE;gb1$Um[<Ch?aM9<;e.U5lDR&=m+o8%&s)euLoU9OJ8,2VrC/,%R;d9(il21Sw%"(sk[g,]x]<e:PUm<r%i`{U#<{39Y#z#psS<i!(iw:}&>HK(Z8zsa<{]Lrult>%UdwwN&;J/e}mSac,<49Y#=,(.@fnhJeWS9_,=l,a_C=.|g8x9ak,/W}wUy&]}wUPXbmf9Z{nkbW.`J/+2rFs9>Rym6YM[s~J/veJs}7R[W.;8I#0`jh772`z$%2hy2>?.p&M[7^L_1Y8`q2|}M%:d41itFP&_Z3J>rrPk9@7g6poltui!NkjixH3oZS<9.SGQ$6?s[8_S!;ZU%{<9!YM/|.;c!;MnjV"v;Z[PJ8<Yr8[g*oj(W<p9^fp6>9FT:|kJ4h!o)9q8O~1Ui,l8)|HyR^Rb]PimmPR)^eb}4pV;d{7hCFKf*cA9+w=Os;//,qr!&`nY~6l2kJZ{x8Ld2VyD,~rhm9l8)||&a$zlT[|86Xd8pkI&&It;?68L.3S[!|69W.$x3]P}Ffs)}wJ8*r2sT]9`:9/jf{d#Uh|]x>G(m62^(pkJ%{U&?|q~;!v]_bn8vmdko3Me?6$2KPl7W,*wlWj.Ng6pq^w#0ll;9#f!;qxQrsY%mmRve.`<q$D0CbIvsO9l?9p3*Sn8I#[m#&W<cpr!5]hqy]2l1`L;>6"VT9!Ywl(p,%S;TVn.X=`7FDt)4pY1vTuDe;?h|ukS|ujmOfO#pei3;8G/Cgn/:8$x1Q2P@3@P42F8]UFIa^}8pamf*2Ip{P]!t:~fapUlx<.j#ma(79o}]ripR3,qYhcZreUjAnfdk?Env%tmq~D1h&|#*Q79Gd<=,%PI?]"gYdr07g^wHfL[!ls.+{q`8ou9.K+;9#/$h[<${$d[:eufu)J>v9!%<!&s=6"&<lI&ECu/j8CT_8wrA(e7Jka3#&R;L^!`tr$2@#y;J(mp]fuT!75#?<k;Q]?{1#Pgi!2~)&1<79s;_^)]t;k}j*303``!HkI;QhW=@;Y{l?W}Jb6^Wrf_cI_gY,jaw#)lQuF[KpU]Asa3~%:A&gJg>vCl"3n3yUKp|Zk;*<;66ba3yU"5=U*sX]j^j^WK>p${c="5XW|}C;Q7g/x1`|x]3H_e?:f~d3y)L88_D8}h?tZ)&BrI1~Y{c=xE01Ed/&)bx_c=3S5>"^mC/&)by_c=c^P(}=J$>|"UYpw]+S%^X3ApA:Z)Kp?9Ym&%[j+hGu,<.WJp;k0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl#he5b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:Nl0Sb2Nb16b:NlI{b:Nl1#U!:3c>5#Ob16b:1rUlI{"b8/.`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/B]B]"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{r[|*`u[A/"3}V{rjF`]{r[|*`Y(U~u[fc$4jX,uxDIHQOgc$4jX,uxDIHY2YS1&*]"8.mGrIk&=Z>24r8GHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,u${c=D6XW*uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4jX,uxDIHQOgc$4q]_b>X,uxD7>UFIH/Db5>DnoU]m:;66ba3A:,X{/zs?,"GP{DbpN;C}HOW"R0QtJkp!7.huw[tiD&J=J8Y+=F0,_1nCd_$zC?rsySzs{m,(Rz]AT!+KOq`~v(Ppd(B:w"Naf[T}Fk4}"<F}RxjNhDGTzl_7A"bQXpt)GH6CiBlmU%web>MA*m:>M$C,y4;Tp>u(f(9(cjpN6PI7kFk+W$QrZ}`a)GP^FYQ|h+5M~[*>/QI+M!xv_0XK#G]YMVEm8IuY|E<wc3lX{<&rGCSc1Fx9}}EpHIAU%t%aXu4ne`VzayjGHk*=l(?O&_!,b50E9?rbFSz}Sd*|C+$ydZW6s25%_@4IQgLc/ptl/2<~a$.QR8)^q/NacK.,,FH>Bw#Dqos;b(L>k`@$pcw*9`k]NdCkn$mbPF6|gAi741P:wA714{[!XmM2xZ8&$k{VZNQv8,gU?p1B:LSB(;5pyQn?"lzch8RB~8x$R1CixGl>FpcnHj@.?G.cE$O04A2,eT3i"O2^vLk?j9q%HYNtMSDo?40.5F0rk#5UQR)?8?rDFJDPH:}A@Hf:GbItge:2C]JMYAJJep1;:6}Q{O.fOZF@"%!zf@$8r%D_qJk{Ei/YO:w"N04{[IZ`?EUmG5Od0u)Sey^ZhXHC.}jVvX5J$/Fn&o5^cN0.olj]Et|n)(;W)Sh97kK^6~TXjGCU48wy$P"18w6}RS=d3dzXC!D^]HN9?pNnm;"JXSjMKnH0UsE1q$Nn%9W4wH0=[w4e9@i{[Ys$ZUu*(T2(ia4IzOI{CIvNSu!(eK1A}5zVfdSe{xS%O+xu_"E<PYb=Mb/RC;i{CG>{mL&(rpud66/Ako!@eW<O_7qvC]b`mFoM,Six(LDdj2B#@F`@>Mc.8?rpLG*?anbY8(FL.F_Mw&ty!k<*BBbxy@F(Zt7MtQ:kf!vwXpa6I=p,._MuC6aObltr#g#r`jS]igj_W|/hd4Od[g3J%!u|KjZ[;*:u!Zq:S#DN,t&md3$x7oOv`l+j97GYb2$:<AwUfYj}jWUTH<y?Sq?:;p#{:YUZ#r`qt$cJXq_|d_R3w#&R#pO4W}o"a9NLvaLWEp_.;RHe/sEk<K5FJl7?>hqN+*"wu:"$iP%lMAj>#$E.yiim&cBhMOk.v8fPHqS`/SD4O2Q8+5?_EeR}[cd`?,D}.FWyuG*ZIx6?3HuIQ|+NhLP)k98Wh&O($`[L4!13p3x~g6!0XG59%F`gyigDy>gy4qYD3j@hI*@,2!rY#sCyf47*NSzrpe*lEMVEH5SM:Pn@:/I@=vjn6F3OY[;YH"aZMcfCijt4]K1cM;aWf47*N_yrpGNH`(((u>k[x9FUi{Kiis#s@Rc.Eapt&KHKnRW2QtUYLdd.N1~da$}3zS(:[_sd~hK5a=u+r"m`K5ahyAhM@xHE%U*+^6_{%nuvuL6$S+<,34F1R9`I[@~I5L:!D4_;cs46h~jA"Ro?~"b^AM4}:V82J}}|>_~<srRh6v[(0M/qW=o"s:*7MWL1v)P)$y1~jP5cI.5</I~t6n(q>L814TdAbJ0h,Uy"oJH9[8y&g}//5GxlKnKx+$k%Z0|$<pK8n5XCMo^mWJpCZs6:g>(lRK0,ul7yM%_i*HK=HeF<6h9$J2;#dyD7nsRhG/E/TS2ClokL+sfU_6OzHuFO]Ma"Rtl{j)v%|aKRpgB{vccCmi5z5ij#GtOM4dDdwwuGjW+UyYXQ2Mkdl#fclMw!y!]9#]D%0p=!0(=TxAUL]r3Uy*/]dv##fB_B1nj6Ckn?<mRrByI."%e&qL?/g2{7#!#nLsnQ*eltiSip_VY%p3H;y.`[xEs:uj`A.Kfo^}$mUfTIX)gOJ)+]!.Z&p%JCA.zj@@o8N5"o!fYo^z#dtTtNw]m+B?D+{;Bdl~q!eRVKZ_obqQPO_g)(pU.N=i4r6TBeBal~xT<o^cE1"Xf%lhpH@I>;M@gL+#@pcRY8GPK~!UE8D$eZG*yppPpAM3J/Mf3$400Uyf8!m7.DY65(3gdpbz8+14Y3[Ia5Z@<h!G2lANdazek!iBK;7!X#/{aSXae>UHtGHwuV<[o*&paCC<iL+nH9gxj@og|94g%8v3:Cn2YzN6_h9L{qGp9dZ3:/iK$AF,u9%=o0"EXL6J;ha%z=Z1"TpmvCs<ADfo6R|BOb{gInp`CputcDs57_eV0VR)^eaauX/OLVBHy$>n%G|:M1Y}tCt)XhBu%x~X/~<c+P>mmxl5eedyagnANr"zgEIXip/Q`jmV{/qx=4!8e=lIA9o,g*hO{EpF*kXIEgOrEt5"C5.L}"Tau0H4dsT5:]&z=K[+sPkdDQO~LS2;ULRqatAx~7x+Br@|u2%<0K5zXN)H~ZM~[l.<*rrGnw$?o`LQ{mjL7*ty6`^41Y^/zgwk]N+RB3#YDWpZIq1f56iN5mIBRHzqlV7!HxGmggUmegne7S:nry%eG#C8?:i%cr8Af)k|r@Fax[7EH3B;Ek(i`!"VmCiO;fwZG]Z_g}lwveibwCHmF(|A28RT1#hUiBWPXA*|DR6_OHtMhqPBsbjfs.c&v15?V<3^#9TNH,X`990TBt)")|hOXh_S^r?m+qtvtQ}#y0"kH+s&BJmVd,7bpcw[r0,@kF~(Gu%UU,zm#]Y0d|q,;O}cN0BUl>.hFNwP<q$C&$>s7S$|>[[7*Ut_@^by49To;jD:w,/0K>JGw13&7Ve:Y!4Opg:"xWqNX_FOZ>9VH)v#4vm{T]EpY9je!_e3{y+|>1^e_I7SzRjkdr85Rgw}Eq5/WNJDgQpIY>9Qmk#?B5]f9Iy$z9O2;$Nrl[aI`X=zd2zmH8Ye{nwcX2([hUI.$*/>Ha,H~8zh?Jw2/}qTdyGFJ3WK~(GfD1^S.vtvbz|#u8=+s}5/O}EC.~<m2<pk)MiG~_zlcPf~m}UP7pjA@{5x~B,Qw^KYY~U7XWRBMj0x~1DgC^B;V{q~NucNp_H+s2jUqYYItk#~;nv4G$E`:1eqd|ru2T5^2yLsiA$":khkHyEuJ!wMCSthI3I#!9TUVCuBiFc&W(LuB3#@V+su*P>l*sZKx0dI?}Q0eX{|g|(!yLb@guDnJRzqI2]e(xF{,U3>o+X7?5FqS<v#ht<;VP?O0dDKf>pyN:cg=FIMohL<vfZ9ylM@S,9`;ZW%oBg.w(9Yov(aj7i=QYmFo>^Uc&WSs}0m0h{@lR*>,Ga0/{?+y:PK`d{anAseCvw>ugQ]I[5w!_em4hTXymYMTUZ`UOPQcfgL0XZ~c(5(I[R2UWO|Bk(V#pBU`we`mkXxF]<kHk(])3!6BFX*WrHfI?!PZ>9>WIDAeOezTk@A5*lN`+sS7#k4WDFKx9B4(No*(a]=GJc]JwGl]yYjOe<xQ#b>9NiOh/r#;w?jQv"p%55mIP5Gu66nblWt?8?uuZ^+s.Z^>[XgLKxKbr1:VnXL~!NmmP{tp6uEH5H#o$L>UZ;96|>nU|Jc"n+K$vc,c$b}/:it/~Th>cH7@klRC<0$Lk1BYA90y|y&Tw86Z&J6ilZj5+i`h2A4po^tB95RWud|^vLIw,hl.@<KF2u)KV/YY5kPuaIAP=lJaK"~4YU?[nZ,(n^Oc4caz%TWadRIO.Wn4|L/yHz>U{0u#ut2UMKtt"yT~.InKv{5;A,sP%LS)?ucYDF?UgC0XZA<yNBbMhYizi5FjWjJF=E[bdV00TBs4jyUyYdS;U{PudDwZznVBpZ>UCQ7krwl0}wXzEzI^>nnKS5QTT(.bqXv[b|g4yN%pP3kS*H8{y@M+6]}ez$Fj@h~;Xr8c9uz_XT^q68Fjgim0fG9/^>qId5nA)vUyCXimX*kZRJBVg#Lnw,><]t+tWdR;p9&=*to@{5K77+SjGwrUz%~DE@t/f7.#kti6/OsL?m51YwJs_,y:oSn%G]}UY/BEPPJZy9j;Mm"YV>bKWw!8**H|xMmd./msjX.KyOBLb5OW{+#2>UPx#_Jl)5UKOM+!8ScM:#m,P^k;_cFYH<Pv_AvKb;dj|E~9jM;GA3949wmm]86Z_p/DWa0:7e@"St]V57FjlOYyi=XTNX%O@Dh8{"UyUjww^B[xCE)?n6Yubp>U+c;y*Qi:>gV9`xpF<8n^aax2Jb;f6:9RLb/ovrK~uOzi.+[VAsd$WlSGkw[YqJMdypNX@nKgrK&{z(2q_PMhu<)aSDTG7anUWdKK=4%kS"a]&u68YWJgQ[.wWEBIfKu{k>UYVjx(&w&2<I_@}3xSldD~LB!,ekyKM6?(hX}RG)z9fG>n7+$zkN*av1.e>+[waB:9RRcYBFFKqUi*q6h@sjqZaY%j3_:XG,+F]D+EL1y=Ioh+iL4tQmvtmK=Efj0=IoGHeuDzG1i2o8h5}j7asPKG&*Nx"q0|k+.Z0G@m_wS&jZvR9+5LxuWSVMXHCmn+tw#u:ZFDP?b;0|#!fLkXFDFNW9"LJrPGb&HU%!@3rNEt4)88Tj5+TzoEmJt#)+E,hXxbugSO&9mEfjZL]BRY!RCZD+x=>b/fGm^qtMb;53vxSS?D$!oz1MU<>>MXfd5J|jHmgZ)L6BI(4)QwMo~)~tqubn6ewg2Ro@Wc%DkFZb`Ru8PocL0NKE!Sa995s@4W=bM$Mz+wvYPxAMUfXo/1mzqXAG.e/%rO6|.+uZwa&VZ9D3`$xii`HNF"#HnH2p2qT@>yn@Ef"qJo?M.0yq6y+woxAvWz%u!6:q6`&6Ro=+PzJ"9N?PDTUqKM47046Y_cV@8qK4EYd)=/U@&y@L9kmkBj"ZVX_YILgfn@x+6yg1K2*F{/ls}gw4*Z3j%WTuUHJF/QC+8K&YzgZRyLtY"wUI@GfyCK=b%wSXJfM$pd6tYuJhS@4++t6G?eMXVZ243|9Z%M?yc0J54M(W>@)yHOMx&wI_!!#y9z(Wse6.M.bVg@7WHyuN7"xl=?`c)RpLf09MjS.j,mwb2|qN?1)MnLhCgL*<R`T0Tx>c!BgeS`<Zd,t0+aOw4u;tF0[+kc5vmSVJtDQ,eNK1hJ[8qOIavO?>/g%oqN?1nj=?$*[apPV8Km3hd?3|e&LYe<++[WVbUY84(N?1Y@2LmBgLn!$8)O.+|+H,oX:I/O6NiQZViQP^R@z@)zuulNxYf5JosZ/yyX3FylQ+&Or?:y}Lmw{mU6YQ@$&y!MCD_a4)kE4|ITRm0qMIb&wUkK>1<b[z62]ATN=0=M>M)Z24*N[I.b{z]NALTN`[D,4Z!c_2d*R^jj$M:L<H0%Sw*+*zuNwxrITcHFyl*Na[7P5|W@@y.v,30YYQu.r=<n1zJMjC1MKfrzW5dFfuQmoOXR((o`:NALRa1{I$V&kZGzSNteJb@"lVks=/,y(LwxmSo!w.`6q*@z>xLIub$KXmwTP>Rmk+fz/YDJUf84+)1XgzviHzWX&EVs)uJUrz@LA1oNIt]{1pi@kO>rLdfdL5kcHB#!nz>LA1JMBIrFa9[5V,RXd,A*2L&boK9PjN~m`MbOBO_nE<Qvf<jRzLgBJQO>#!b@0+nGCD)q_cD3Mow+/LXwA21X:1dR!i++FMByOB2Z[zbfMow+>L^w.0LD)X^j~JKoof[IO,ZzO1mK$2n,prUwOo:MyN`M.mfGe@2*:FqPj&Eg!+8!0z;zpY(JpQjjSjROOPG=_j48gEE<gj~+LG]X1F`%^&D]i<;Z4WFPq0Az3z4v,Hiw]{D33j9+czeC%~^,$M?Mr0g0~Zilq:Y**LIdUI[gr7K`kL%W,1+6jTQoSo8+lu{CmJ1XfGiRy+IModzN@:rG5je+IMrQyF$!_cKNRO_?pg4T(dfEaRaO2L.T57ci~[++hZWQ+Jp]4!5?CwSX=L+kfWOC]$TjG,ExgNn1[F<}!PHTrz8y:G"XHP>WEaoKec<0lE>1;RUo!q8?OXPF#J*NF3Qo8+_`MI10:41010aI=0(}7!q+vuOKpw9l+N=cXjDw`MNjtN+E5)D3;b`zyNCV7`VdCZ,RT@+cMuec!Dox3q8?^v:LRYP>lE3!_+~Z0W3hwbtwkG`C.LSCl|mR})&".Z#FJQ}!;,^9{MUZ$Lzt%ZeZ{v6LxH_rBs(7=/mR@i9NvdGIUo@MTkfNbOWb`H_a]N"ZQ2MI|32{;1+Z#ZvZMSi@J73*!N~AP!~=J%%Zi+.zsaCw8QnG{/:ZC5`/VjxZLZ9vjjX7tO,W4(k$}jCda;&Onzh4/BpQ"!vzP0Rz]u(cjtaIM{7!n0HYWE~QmNpM=L]{DT0@&zO0kX2wJdMY(j2z7d:cOI^(?pw1vH_a!8ygHu|%wbRw>18y)L>zjb?~IU|6mwj0@xuNYS#O,2U:E3P`)+rL>zQU7K!G]y7+=+ezmb:Qt)RzebHx:GffYw:!RjlNH6yelXnx>534$qJ0{XhD_abV8MM*xu(zNxGKV`6y=t(z{G?5K7$ZftYu(fJosiTl[WAWi0={SM4uy2a9d<M0R,{z&v4E@8PDN`5+rMfce45b$GrN!5{)EMPc.CSW+md<,M&+h,4XlZOX<y#XB}rOB0^+fNBMxIRzWU]1K%pzoz+C4X;HpG+Mg*ka2QV8,P8&LawbIUc@[cCEq9n*3NCN3Ww.XMqv)Iwmm=Q>!P1qK`m+>X[D:RQODem+dz0xY7*5uj?+zL+bCFG)eygzp+Lz{zlYhHdU_w,!`_qjy*jx.NrEy@Gz#WRHNk[FsF6I4)88!!lVTao6._{KQmK%vzSMxaiw83!!hj"+T0@H2PY(P?6ey=$Z(ysZU2TDN%%Mr+CY#tBzt:?M@MFO3c$iK+yyC9LMtC2ZC@*Nd<9R{yvXiETCF|I_<H/IQaXw$N=OFj1Fd?n,3OOhzqt+pz}N~"6?K,_zROb8eK5g"omsRj9+xzn3{&r7ZQR`Zj7yyBtUq*nx"kpEpE4!$+qMyvRR.mhs<})}r|`_NisZyw)YFcG/IwILDgdO_/63I>H1:NjQP,A}pdza9n1iJaDxyOzi_c]b1N.LJQO>mxk+Ka@aBj3BUoj4IM|1maUQ^$rz6z)IOi=1n@2+)z*1~[FNyMG6&+_MiOpA~U|/:A0=pGR0LdnR,W#RP%<bZ0>"f/G2WkaoP0.a+QHWY3<IE0h44E4,?tHH9kdvjKG%6!!y[OzYmIDdU0Nf6j_WwbmcpQ4OPDb!hOcNV8Ygt#mE2=2=0zQ%|M?yoL^A]Ffegf4|vzLr@2iiA2}E}J]M0z*wIZz5Q8W:gfJU2%z|)_tL2LxIGEUD:bP0%h>sMI$!,Z.OaXwKCZJ%M,jzg1eKPQ<2.!ecxYEKC61zhzpx%~Np_#]gFnT&&Zk*5G$Jjcd9]g{K(C[VcEdo^bkz40]gJ*#5~)HP]wuy3@>YeeRW%G7i4LFIxCJJHF0sbF#rUj*ii*Mu=CJGeK`w("!K$U+Ac@GjAwpCzQcu{m(|xp}JJo))j,Kh[$w?H,GGibfX6`@{!*!M`){X$WJZ_cRWfM+0v8e+??#*.*Cx5Oa0.w0H0|`Q8g:QE^8By,1O(J+SK]S:8z8]=c>H%aS6##GjL)Xu.BAFR3AosA]W8Yo6|!JV}Q,i"v_L8eBR*=Uk03}U9HPC}Jm_)?j*g".N%v/d[N03Q<wn1(N":L5fM$hV|3c;716?EwTI$ZuK?QYy|C|GILxpTq.htRf*}GeXlFPGKhO<0?RXCEMA?4o/j=`hO%a*UuZC+Iy!Tc8Q9RI^S<D!v={>7n[R+0wDw1MafGALRa&R%G@XY+W,&I+EphOR_fEv.W*LEvxL31}8vX47YQffWPQ0ROwzSu]2/dtvbaXPd8Cg/}Jl]5r1d@]js1#K(w|PGk[j/bni#Z=yDU/Z3tfB_fvetv~EGfRR~WJX:M4?9E9l!lH_{J`T#X|ZY+g2!&(GzprI3iWzA9[f#u:_eCX2#LIRD66u+Zj+HtRvRD7!7y!tmyLbc<h+[vOvmyp*d<(n]yZcKaJzROITJ0xJs7f;"kg+OwL6#DZQ{6`+uz;ZbNS1WK<QVv+Zj+QlRwtI,yK:o6Rj3kqhse&11?>+zlQ^cf!|:4a]=##!+RUonO]jF,>+9!F0Sd.a+4Lb&+(Z~n0|hD(GrN>aaR>F"Eyzi4YMq0h1kv>KwYx=Sj;MjCjC47@I`TWiQM9+OwhYdz)GVaS!/dMIkyhzcbSYQm9lij?MkV2tvX#!3!ri,ZZbQm9lNIpzszFzkwg<sw#y_L?YS!ii*)QWq0h1!XNjaw>BKE5)[5+E47gs3GOp*aUD(00!#?#nrZk?iOn?$_m?Mwm?v|))k?n?:W))C`I`1~*/?x,i,Z.QLS2y9Y_Mv~*sS!0R1~0R!}%Qy!9}cPwsWCq0{4_/o~2Xd+m;cj57uyiw+~k3uG~]O^N}^aq0p?@/Ov:dXZC6e;W7wW*WTx)4M<eYSFi&a47*@A"B5y2:$nP!s67VQO$,w:LGqJh&|9Hbal57GiF6w1h&Z;mN85?_At@21tPr/dM+F6&#qAq0P)^cGLc*coe;A}5AJ&6@:P8sU5ZZ%,}FPYmhsX~u)|8Pi(7w{L?(ODr5#Dezz`]DYW#p"sj"c]<80EK`)t9sfR|B+|54r~8y]XP;zE!M|70R}}P!_~3Sh]1uO~f;o(/)9+(|)tw~Bec_j=9+,|y#@G4}a,ILv,O!8CIw_)r8t|O?.~ED&>~z`eB`K,!~6B:vu>uDp}Kt}~3/fg{`1q(~`aY7gabDvz`ez`*q)G!@;88s#c0[X|94u~(i=2K}pa(>H58Z+|NKgWr1TEea!(~Cw_ePo~=E$aS6dGQ{_t]}0_RW,KWL4}MTPLBgy35}R6P(E?,v)UTE0w0#9+/|0*&~iJ2ya]qPG~neM/s~zX,kD~"HS{2ya4<+zyq2kh@~0VQL6lzk(sQO@~>_Dk$}o1bMrtW)(hr+["g+eGh,@~s`xa*3>~6j=QlR@9S@h"9su&FO!+<5^}Bz_si<F!utMuHkI)zPc.;M[}[OzEm}!j#ss_s(8:;8cR!?&H?shCs(r=F!i~HEP|&42W|s|6bMR|f<s(!:qPH~y()zb2>_g9e_tYiJW"$|jM.Q7yL/Z}kV<sftfg8`:|V(n#`eO}mjiW+0TEQ_&Hk([FM/w_="w~JtGckn[XT?`OC}KP=21_.tysKnZZDtbM)@sB=|@hcsC+kIGRhL|sM76fm_l>&,`|UHhW[/r(.u#A:_i,e~CexFmBTxzGhJ,j7E1ipoL/3_.t]}fcrWzX[Xj?{7E}j"(sEHRt`~B84*K]mzns#W=spviHD?&,J/].RqzkV]C,g_ATX/`|:~?yo1d`P$h(iF~h2Qp|2B4h;7(hF}Jarsu&GycZjvZS>sFt_sl@&|bc@$~X{BA,oIFO?^iH%}1pQL|xIg.vXL+7^~&`L8Q~zn(sYLee%}^<RLLi{L7,`O@VQb@9j?`e[}IRP4H72yM[iv;|gj5>pG.>wb}r$4r~Elm1ZV0[I~VtNBK}y=3hB5L/y_nw:uhq1(s(*vdGn[V`W;&>kP@9e{jEKD`_q7U(`J/v?D#$@Cj2RmSEC:bP?FWWlXTxYA^2"t~}pNd_6[kBGcCN6Mv@:H6}H4s/g/QQCv~|=M3h!yezX`U%UkH`f<T(p=gX?}YQ#>Tz8Z8|FC(~%[ER:|^ag_JEezZ`mPOHKM4[GCWWUaKc|z9+8|*ARcj/VR?Fwbj~EMo(iuq+1ybz&,I~VJ/QMi6Mb@HwdH<{TwmWl=}s_}meJ)bgi68Zz|*A_~5`n)iW*XTx2u9+z|;SAt^}7?"~^}y4(~S[Kv^k:_1qmWAMV4M?yH3sh5P|5;a#Fc4r6MP@HwV|5S*O?^</J/v^r8d_t`_/f(LHyd}~w`QB_}F2nIM{}lHV[6d~/dOj~;yr3U|KtN3OD:]DSW=FR4FH.]Vc_pvKPdYY(ye2dSNJSj}(3GtB5r(H/a~1F0ZUVD]w&"+Z0|6fA>h`XqTT>}`%C(e@Q?J4z8Xr*e9HQn^*,x{9/1Oh&4AcYJn/7pvF,_}L`L=VmuZq6C([)ER!@jY:*R(]pjxJlS|)>@lQ[nl)W$Y6~EtN;+/5_J}x"*Nh>@_VnR@)^VaEBy1)EMro`Rba<Mb++0pIh?]~P~q.kHrVR*I]gJ$@_D{;v>xh$`ss(FS{ggP/DH,h;C]WhCxl=aQ/@!hNc<<I[+;t|[P{>e=~"Wf]{;0~+94V3qt1etEH:Wc7ND2_=+x:pQX/aI@Zjl~[*413qsKxd#4y^v:v${wQsm^ei<G$=$?ZmJ3NAt#0:tD3?#?ZrKHNpeC5!%SChi|a39beOy)!S!M)]1T;B8foS;UTI8&0ng;Om^L^Cuy)ai)Hp*tP~tSSt1|;y(A&bPdK~dNcB)k>F&H7J|N;6F~dNcQnBfVB{Qd.?Da42cyrM%cBQ_zmBfH7l&d*?Dx(o#A%2gctO1p2Wt)HyOGP6FyWM!9Lp1b/UE*(.O7|b^^;5%cYmB>SgAZQ2Ls8fXz]nbA8H7)uI0<KRRvB/dM!!4YDY2{RH+Ez;J_V{idUJJs!!]5ngD[*PZnDyeWftDWf#9Ylm,7$%@pX+Xb6KMG;<?wrhgF4_S8A4`.>"K*xFgST.:}z:mo<h`qh0JEFAf61]VP5#v}eC83~?f8vUTmP/PDP9EA"jc/t+D}s6&woq<:vFoalo_ZS.QrZ(^85/:_XV|uj~m#;u(nNV/:!_1tUKIALQI*}lccs8Nf:Nq>|_d/9}e%_$VC|YD](3Y1u?qT0m1=ZtgC*H[%Q;(7E7L:3XL>eiNTU*M|zdm|9HIkq[4PD13pNXp34aOwd/5NDmR{3r47~~X!{bah?m`0c^0Y<QXVN<b))0q0Eg{HEw88F.TH2EX9;ZL,UfSX{7~RtQZ.Pyx"keswixor|+X?4TzW_6:#Q%BBUf&;%lnp_lRCgJy;6])HoKJVBSQC#"TV78vla&}u[9xl9`jj[>y0vgFgci(UH6r^ab(a2r?|zvvGYg#09aF*UMmV)Op1w1m/50B14KSL6T~x8?,"5DR=Tmi7C8MKp%k5%JmOX92HAet[53vUUN`/FZx2,9W.QNFCs$YG+bYJ"P@,d4F)f%"L2;lEHDqiA]$=M.2BmnB`O8:bB8yX~$eu{;$Uym,YF;2uSlk|xQsPmRo0q"V[KcXDqII7r&Fn,>vm%3FMGA4:I{rmlJ*)VEXVGebUReo|U`|iT_KsrXfR/XkIH(>ZPBhze!Op.Z3K&~/dYBt#$e]XQmN>:W!oi{6II3BRhm;@_y:H9v.R<RXB5cT/ji[NCWrxuy6U%jSdTY#m>:CY}ei)%Wf,;g[J+RaVm4m..V3:T9[8,]K56WJ#v#z*Kqno_f<e<?u)^jRh{HvuC3>#GTr^2u{~<.79WE.(&Ka4=%5}.CXGFpSqXw!:u9TqZ6g**o}3[AZKv>[|UYuT&;Tj:M|EajBAu]Xi.dBi"u.KBlnJ?y^)NuvdD~!HCAEU3}E}KiTn`Y4s;2j5PNQ=hGFefkKa9)}2g"=|g5IEJq"`l`Z]N|HG(1{xhaJOpARy{=G5e^M"Z)Q(N2V_/DrkLJu</aE>e7,_&UW3VEzk3%mtm5C5[NZET2QOWZ&dHg{I@F(L.?TT]M]8`^:YQ6:}$zz/[2`}qFbtXL@(XcFEte&J0[_x4;zKbJX!AVo<s1J|2[H,@T5M)D_t_h4V#Edm&nIJoD@fL{TgU$K/hL&.:5U(b{+ZY1`L@<A.FE2]G5w?qg1o{xZu#O,pXVlXi}N,eS>oz/rE^Vx%aFqHI6|DC|aY&)5Ko^15UXVa/af0(2^9%[<q{]#y:PuP>rjCekThWOo$O;I_J{L/li6%|dPs{liSQy?{,LMV:v9g{dBlED||iovNN^LK:>Jq#9/&Bk4SEfQ+/ua6rU&~?W@<w&^"AcJ/tIiQ+{xfm>U6G#rKbA_VRb>}?3i]Uzz)V?D.bk4_$M?3qL%G33_Ys,(6`v5mTCv32yfT<+CDt@~5V~n5_usy]S@M[1_4zog+YsoG@%tfK#%+~6!IsxC.=R9"nHCR^1SI,dyav1bA#H_iWDoyU{aXIYAdg4U$QwDr|Mcn>z@lsLJEHDqS1mQY*JKf;!@kzU(m2W`&|E9%"8DoV~4Fq1e3YiOt~SZ!|/BW.^$+0cWgtH_!HZ%wLy_Du?PVulpiU,)xyTKHK)}M.FI6Y9TC@![J^hC!l:8&p[RMR|V8wKSGcVQj2nW~Ig%*d$pd4Q+9ayW@NWdlh4Efr8Ia?kg>G`G{I.Jkc#gl8x%w(/}XJ*=xEw0HAfj8z~jXI*a@nvm?$4N5?keip=Z;/{pg]aZ^AfNh`STQAz0_(BjQ4Ersn"pAFW_cS8Bq3rBzF85msSd*[F*WOD0KXzYf,jVpsC:|"9rTBYBIc7GyM6.*C(Yy9S+y&flSWlNp|1qi9e^V``7bqx!.c>}a2I)hY.M^B_J(X:Lf[#E,~kDDox7j`+>+sgaR:G+<Kog:MfUd0n]}!BmOAKS[Esw).sB;nsHB^Y!0[3GS&ty#ZrW=h0C%(mqHB<p5(FBavC"}GWxDWy&kU4ub+iZC}|)j"l:J.`L(2{S2k[V>YjFWk`p<n5Ye;D~&o|/i|D(sxx+]f2gZy;/x$4413Defobw=SA1oXzRxlz,qKp=z1C@!)#YX4%[e8OhtM|G/3?o6%Z0jpjwr`=!*)P+S/.&dcfWq?Fc}bN(>OmgMN,p#^=xI%xj_J~A:pU="."CuUMYUW{07oNfe32z+%oc([@p!"bV0F/KZjK:^Wt+EogmyX>_`%$,W=Da}EIt>2gz|VMgCrApYV1vWs$Ivzk%rV&E!%B`~5S_OM(_,>EAJCM)Pq<5`KPJx$_X=`FUR5MZp1]GS4tFWBJ&#(~}kZ~%MR$!UhAB%hj)F/,vngT&]]lRa,1uuU;Z;(h%H,<NHE+|$zJ`<gw2Kb*neB#+4bYjp_#@M_5oyC%6*|r0`2[X446h~I!g!8;Jx,I(alP[Pu{kqM;W;e>tP|=Eb>V:Y%6Zg;,4V>OK$6!9B2/$|kS+"*}ZRKBpsj$CC7Ci3=:fD%GYDsA3.z_9Sn;M0cDhiq_p!81QZ:{$1(k,"NX7OqpT(2~)QV`HN)I7jP+D34.)rcM1_dX&xD]]3/tBT0$yg86/1v;I*}EH0:{r=SN2KD=wsM^.!0`}cxV/LOk=,V:o~*Kr3KJEBVV:B`6`v/:X)3w179|V>2orxN!|`Nj|5nl%h:T`XLWDICeXz*Q)ODuaE|8vQKKMc)@jvcMW~zgT6F&r:IH;iX%Q$amR=e;KZsoZF~:_c^}Q2NQNx^&`.E<Vwc:@bi,g%,_|dffMY1OHDVSC*r3KJ1zxZWtSfo%ZaSt28qh.G)G>nQ"3$;>qpZ}wWX;i@EXBv~FzSq6V{us*rOrKk[}Yp!c|.Rv(LOFYmY&3^]at2},+)Z|Ni3wZ6C>0=$FRE07EE%1el46"aOHZ3UM>@/^({TC&By`h|f<n$JgwbUP6gW{Hx>"GZ=%8AFD8>&D*"T=Py:<T`wL)XHPx~/8rz%?sCP+rFmXx$B6.V+4W$nj`Uk}UxUT0yy*O@0rZlRR#m5{|U!{@"il956qz*a!UXf>G6htx;q@KI_!6h`+}jt)Z@c~yAXav,$^zj6==<>Us5N7+Ze8l41P{#n:Lr{!4/m^we@DBtJBXj4RZ:1V3c#&]?:1x&UT)>{f5y&Ta@$_2%.Q?g!T.~hJPnBG>*M^p2HC}>W``(}I%EdqBw!L5<m7fJ{4/h?=t(S(hD&_CAhOF:D@]_!C:s9#m>ZB:s9~_acB"YUXPD?akfKoqO]k_+H[~rFvQst])y:UqA"Qg+])o`SIQC$~<h$jfY$j}!~yF1yS},vA7nr])(`r.,)XiQVGjM%U`&C+T,p])!`l6XU/`:vapJe:TcUa[:,PLAKu!q_4`wpKL82UR,8c3P<+/^)Fm_ZFmN@.NblFmfYv^1[>EFmrUFmZKM/zM+2lr,Sgd&4j)z`qbKLU2(_[}Y.S!P:~~t$sv]B@JCcib`E2.;_#nqJL<"Qa{qCMRbeo3xbcp=K>gZF:D*Tii3r;Engf?6]JsQy6tk2=ws<2@JP3%/])JEKb_Zt5k2?:/fM)|"/?L<SL|1U91y!""jnxDMK/Hl"5Z=5{D;3m]KQ!2,b90&M;5w@Fq:w[vNQF}^F}(;r^5V&ciXL$ab?gG3CzyFv}NC:i,Nz.Z@60UF(#!GKL31UR?r0HYw{RzS4MniBo"xj7[;G(@qyFLE1+`Ay%"qpd+1#l_]>};#;nu)5O<40<+2s{+9qf])go"xtJnWAF;2q+kEzS2}ry+r~~qD1;A++"nXl6x^S6)DD,iFbURTbUYgr+Xu&p=K1EHG%|Paqj32M{PWBDM|5OhHtq`eh/:rnYyZMN0?!$227mpLBvMzo0RbyQHsb8VUkhYMxfV9Q~9rLYg/Q(RnQEI$m@zI:2Ildb,Um|Xu2>+i]i%[P/VLQ+fIP:(gVH$}Vom>*ln%J$,^IFjs4Z:PB4bcSLZxE>Ka"XTCt?`Gas;Yx48)kX?9tw$r%w@viLAh:DVx)KUoJhS?Ln7L|A7QN=jtR@6rb~fbx#pAu~bXPq]$,OFMf+&H?Rm^b_EE~^,nk~?F8&VGXf@(>MU7#oZae=!E3|k22w;Y!UenEMN0Tnh!H0yZd?oV3_cFG?LLy#DTDhMugcflK@WL"rOb;({2Uia+0HD_n/$I/;%}?o7$Mi2MEG~`*G{|;JbR.J]IU4Sj9^mtFN/*>{kZ?r5y!S{cXxc_xJTl`@9;BxL<fE9n7LENn>)JpFi4$.6}tt4:A%WH!A=YuEWX]`FVxHXXbbz_Or6qZ`9:8!+C$PEv^UDHNXTKP~^<h_rni]yaEGVnr/H5;7rY)hdd^h$5T2G:c%Lnvg=pU([,Lq<$Y]abM}V@|&k1^Di@)!X4R*vYwkI=&`(ExNz1G0sCTp75dA_kRZ~Jz:18+J,a9+%7sT9F(rAEI(5CWr#6?|}tg%Psu,nCAv?RfgLn2b4koXLNnG79WyG}7;u>VGXyHwx^Y_]y2E&ItV.`K{`:0{,v.P[wrM&d%yi+^}%aX_TGG)kDiNHo0)LEQz6Dt),}!r~,05!uGYWx,|Cj]&E7|Mw:svlua+}N*c)NB8f(]TL|Am3T&ea#V)SV#iq6Si#IFP{Q5rM)dc#2Zn4MTM1zBI+kSa.$3o7N2?FN+}`/u>}>A&f:xk]9.j5TV!4wHaJ~+;3r7LyS>`aw5d`"?Iv:vJ2_FYRHByF;_~5Ka!_&90s[f:K/j4dG<EbAYLIRn.i=>>rx5~6nUnLUo%|_IxDU%[%u8k`CCB9~NCTny%RZ0SkU9T"=i?!u=am6d(Mm4$#_}L*ue>+Lk(hwLFSddoOq%TMg?GD8r3PuoQn#RH,T8ZtMY@%P3cJUz!U:(%kM3ulz"ei!T{%^[t4V`?3:FN*c>Fswv>f<p=W7yi&g~>jkt!fLBtSVd=(m:&ob3}:?SoFF|B1e2O&W;KW||M_&P6|cIEKi^;#WitV_P%pFv@GuLc@:;B|(^2J(ZwYOCd<e;tjK^UdM8MI,tOXnu4*aeb`_2^[&.T,HW@ziIcR@f.lRlx5`0FWTNW8Y$)+GR|NyA(/Y|OGd)TyyT>+jDb:C]qx_RCg_7/,;VSs_5C`r<ZIu;oQ8]Xb[(."KB_;Q/Ux3^zLmp#zF;Oas.N^^Kwl~0^gD[cM#L6"{}0Ptv:Vq:M<mRrsF$7F8cT!kq"aC.&^+RTNF7M4zGLXBNF`kl~o|1b3>0`QJKk5D#$qK,Vt_0D~6nnmdeu,9.Sx[Vya#Ay3n!>&q[TQ|;yy:#W)y[qr?GT>LdLM!^j9^VQ?($[e+nIQ}.KR%*KeGc+NM+b9V(zNh8}3Iq>6zB%J6$s%W3TIDzNk9!~6+Y_^q?M#M;V.CD6PgmM3!5BJ`qD)rNy{os}J&f](edU=gaT/"p@H02u@&P6Hh8J?>>DH2ViunehRDeYC=hj"&pII`#2/[V{jqS/gFqRRv9zC]6H7Cf*^IgW:f3Y=EFAytgfVn(^kbI9?Q)uzQ>$4Nf(AjE3#7Fe,oF/A`iaE?RIT|#M~&2[HtJ#gmy37D,cCR8#UwB(LAMG7}CWYMc)h~di^e<QSQ^B/&"{XE/slq=2__+(1~.685W@yH0s!E{spAlI98wdMT}?j0@~u]#Ad`Ixks1_Ime(QMDOg@PQX|:NF7o|.l`spGBHrH{SfX+Z8g,+I0"ov&$8h6@uB9i~frdn&8iRD6DTtVtQ"1Sa0a@m$,<A5(1dL*&d&y#l2PnNzzyCHOmWBP$R,u(zAs)qtW/+HH6IW0T9jjxD![xt_a#L)?;R0A_v)<dF)YxC<ic]s_0)C~2XUNfc9!uOIO#uYGHaPQrm}X$1Kt^QOpoU0@|Bdr+B<iF,`/4wVDd+0Cb9^QwjBZG6x@{AgedIA.mB=0Q7#ZC{S+A)WLekKA/M&4gD~)DJI5>1jXUI;6jjxD4bnHgTqlE,`Afc6!{/LtmO=BOt%1MMHRus]ub<&P#uAGGz:cK~R"_aGG[4Ke`wtfV$!u^FfzEaqNfv~xtFpV)G`R[Qev%|gN&1dJvJr_@l:*d|KOqx5_pzgD8LfavQ{|_HAS$TyRYo+ByQ!4})(5wV(~QP#u)yoz!g*`>Pi!(j?+/z"~tSfDsLOM2dsQL~3O>y=aaK>c5ZKfDkgq[/|E?,2:1E6Z>)Gx#u8yb]BrYgPjC:dG)G4WNB=5~i`}zH@b|Qfc1!y2B~5L"x:eBJ1K"`vu#u:4TKArxl1:D{z,W6Wu)KQS)G@u0A~[/..XTP)K?dtNDDmj@5+L3|`JALx1POUR`KDMr{`Rx@Ovo?`/AF)GzVN7s%)K+h!4=XD:OviAfcbGC]hMgAfc$5GX"I40RQ)?&N!wPO*LB}~;RL*]9XhN_$2)`4)Q+,HHAVpZDVC7.5zEiD3Kn)CT7#s1bB<5FmjAK<Z|F`us%R8#(gY(X#c+m2F[aff~WGc+P}2XPLs~RWPL,wt1C:QTCo]/u7AZ&Y*W?1T,obk?`/sxvt9Rl0Yxm)ZMij.ORCuV91k)vW_n3DT7?kC{q]|[MpCTu&kq$)+5>;Vh+y<hsH6WgDyJ[LB0qR{/nQ=*TzqcfXgv+:@4)Q2v|L#upL&Zr<o4sI^/pO1JY>EV6iywJHArRF/(nLfDSJdMo?dRZYnUjH2W#D3H5Q{jeWEu1!6}?G2y|>ng%F?r}mC{ci~3:)A}go^K5{$htY%R6#zIP4uwcR6#";|N~3U]D:;0q?%:.0rc#zOYH7ewOMoA5dbHd5C]KCaw$2IeU,MSi4<+FMpl)N9Wv+2dwjLEm"}eLHxDSIJ|=V)n;baoEnY`rAAc=W}XiX]"yOFwY4@I;B*ZbKkd>E1K},(16z8B5XIDI"R5@n2RF*IY0Cm@`[xPTXoatNwATi>Dj3.qB%=s;!ZEl%`ZlOdk)DtWiGX:EvBwFwYXWBI;58y8ewT85}cEG"|$3aEEl{on`*jXsfCgMu!v)JUxyx([@?:,h"L]4I1gZ?aER}~uQPk1gqA`5:}erWLfZ(uI4yeLXtT|Ib4y[}>PSL&|*W=?F~G88IZ(6UhsS4eFOj@?$sM~3y&~EPwBhD_=DU>Q#Tc+=~;#$(*}D|#Av|>pH/J~^@M4j;(_D~YU.vP]R"~}KS2F{?3Iw~%S.vJ]iHxsB1mIz??$usn$piC}4#0FB:L8X~RN>9A>>Tv~fZJL(56fV!wM"b"o/A`V`c{3r7:5G^dR"b?5uOn|/?(|`=_F?r>M`yTRCsD|wT@@PMAz@sc]_/cJi|CwJxA7nz|[ijtDmEsD06u+TE`s|Ep`(y!0RX`R)BwbAsHR"bIY[/+|RZ>qUKSX`@*K1@8vAQ5Q=,wVq_Dyfa,Iv`DySMN@@1>~9}&X?1Xo_4!Cr1A7uTB}(zS@^vQ+t`uhjz0!+}Xo&K*+$~u&C3:uD00i`@C(P0C$$8~g("svcD),46)G5_pOQW/L6Ms?Q`|EN:zryy3v8Zh|y=)),d+hng?unr"8@O:k(W|Ewn9Jo7(5zu2("Z3EN`@u4p0VM.6Mg?^cZj[)6BQI5V(0+`Ux?B9[d2_}kt+~ORJcLwB"q#(|dCOL>=7Mi@OD^|.tp~/x`~B")VwEfD4M7@e0B%ZORhJZPY]iZ2I{<8e0d[NA2B:pNYR7v&]9m2VH<5`yaZ4U5X%k_w]DcdV?^uZd)wKwHw1(U7A8YD6^,Tdb{py?;zZQ<3e0S$][}`D5&Lh,E3aq=7HN(s9Z2&iSWolQd[MIbd<:>BEM;N[Lh=[@58EX"ZZ&+K%Y.gUQs]Dt)ia{x/K9<u^W?KUu,ePj(X"Z?!W7J5Cy3ZW5#ZMgw;LY3^08`MpT;Hu*w.iG1]d)ZPc&@*`pMg]j4pcY"ZK=MH)gOI%EIw|BiGS8*+ZgeB]fo;7G1(_(qZxVad4tC#}cMYp1rUzOa*5zB%2Z[gnLiIq*2@oH}fH[ddCIbU+[NA[6@V)E`P5wJK1p<4?)3Z`#xcF5]M$mfX%Qhwq_&z+A|G&#~[&B]+pLRJFtM&4efjsa><DASpz/g?>wEi5*ZlB}~^PPVBV1*^mUUMoZsa):46uL#Pj>5N7SJ6bd_/;.11JGj9N^b"5EMMPyYU@x%75Rs}ktou^/|)xM=_ul7g/o.x$NVf5DxM5_`AMf$c!mCA!A.h!UI!f0~B}ry@[2CS<@Tm6ZK=B>$!XOP^MlFNxXIZ7`O>}I!cUDv3FUi*v]W2fBFxqb,BwWOz0wkS!EDy"zdBV>wH+3S5Kuh8`k8{~kUDQYADn(|L7]8E5w~:V5GGg?&BoEyl!XN?#Ni!gX9VnDCD?y|r!XY>:BfmjGMzDc$d!)fJ):w"&OAWNMwTa7Jp{x<L{lZ:Ib:wEs9FNmeyO%_yTpE+?z}oBOu7wVXgJ~G+Eb])|22EpxE}*Cb!M}~oK<.^s8#)bt~t+Gh775bFIt@dj.UFvjai!DYW1nUd^(O5=BsJlS&Ghd;cMZ{_=tKFx$~Oo.]dws.<.6rGM85vN/kS2#6xML"#UDMhJ99K{ncLCiXWML$#u7"$@%4NxA7Q^c%pE@Y6D5TKJEt|wuXf`0Uy8o7[;4~[[D`WK*+G`#~!N=^iJ.j#)RekK}vZdllGnFk6TzhPpq6IAw={>U,%Ql&YsQ8KX6T#FmiewR723BW?8c24MP>5mtN;{oXF~vD<&2Nb.)@knjKZnmBr(K{%(c:EVi<vg4R|=s(1g:g8p|9?3?bW+se[4W0(Vwe_"@t<8Qk(?!y(Of$XWT~;uPWm($!$[N<MAB(VTazlAjx4F~ypJGF!htD#MdmqDPwc=2oKWJ9(9c}$j&~A0c,s"#G7$<QcYt%cb%he&7(0Okw0uU``b=n2CwlKMLBos/JHDd4U%NC1}PX8Pwf9OV~C^oRtl4#FZ3J},!1`~gpJAnJu7vK{2$0ycqIApLCppIxVBSq7U78ZC|a4AUl9lCGu"$mdy:T0e.)sUa.[31%4Mr|?V{HUvAkfQu{{4)BdlNlgws77=Wxg.uEs{ap9Yw{op^Cywx!Og|Q{~I5)~leO|<G[2Cki_J$;B3iuvBaL)pd*WX<F+MomTOgBvs<cKZl=zuc)sw^*mSJ@nJk7yM"{Z!exSpq5{gRymi:#F6SB/;<M2a>N1{4o#K&p*9qZGp.uy@uE$vS:RD>U):1La?{fxScY~v$1b85+p}H[lHl%Z=p`XC,t1%G[N="`"Jt3_*5TYOLb`Y?!KZ_2sYg5L}U@Z/Rd`Yl=c0tC=HfyGFau|kQ84ZY1<M,K|{mr3pu!GN&JjmDclxZ4;~)UXT"4Zc)/)pzc,2Dw<uwmu{ia474Zi}{7a4v/ic!8WBL7ilOo5~4ub(^B=05d3:U1vxakoF`Tb3"8}Ms%#)Jkd*aPcjv/uF~i"T<VB;9dSSvDqa2auO}*"6n~Iku&5"c&`LcKL{ntP|bk*Yi}fvI)c5uP~OPXzqUcSz?sE"N,/[u}8FS[oNES<:KtGHU^PZ^OK:kHi;_`F/}o$M>8vC~j/5&9;k8{ktxNnpw5%xbzwW.4b5o<_XJh!]DZII]ywO68Hwnr[T7v.o{N>O3R+hUdNGWgN5A8jQVky!P$m&6Q+<D5H4*x]Q)k@WZR5]NoIC6Ati(=2RM`GtBXbh77OX`(Yb>SNB7[Hv=(VPC@#N{|rf7UWIT![M_fG[DLLt;>i9VVBt%c.04H=_x]DF%=w||aQC.NVPN?qMC}0=8;H8CGVxU;,5E/Q!8l(=y9Jq@fo$SM`gQ7TZ:@4b_`sbG5<fE36HznGRsKb#hGhSP/:wTpWZSYO5z!)8^LiNz7x+oryNq"@=KC`DtI[Rb3woNL&pFq0TZe:wclipxj}^,|*FHayjtNPOa91UADRS*YWfo$82CuPm9!g3"q.dXyK!_l$)&LB$3UD:bB0E2k+[,b+GpN(M7b*9kbVg;7Sie:xYx~H5L5FsjnpZ*%RGjf;C>l,SGUtT$!w+>8f27ylB/F9x@o4CJSVam`}IXsXL$tnM9?5S%S:p=i|9."HB#7CM%^9D<c2GKp7IXyV.Y7uZ;sPJ<eQ{w(|(qV?AgJ(:w"u?2h9V;jtF>D&lMo8+gp(yck9*;4N7j#D5bK:<4=>*o[CvV;?;YjFnbZ!DyWGV}zNb)?{]||sdff7qQ{L{gXQK@(<NQGx7`y>.D(oB$b)P#9a7<$tFa&ea[U=?9d]5ppAGyo(xK5[<ac!mE["{:w6uSj9?P;9o>%_nV|:wACywLNLg%UAMNp1_y9bOt$SQ,":gPIXBIG9`:wv*m0~906Hpl[;Z(X$9EDGxvd6ke4YKGDzA44:;a@HKVwR+PbMBNZ#Cq`:wX77rQ_gz&oCItYuHs{RD)<x]<"(#wE{EXU)r1YL!jZ,[y(ESuE:dzv?&b5<x@;BY99B3$!ZV}F>;^oIhrSMoUeByw])60*chvPuN[Y6D#)$Dkq*a49;kT0c)XYv4;JFLz",s*Y>GLADz<6(Jjn*_p0|S+nZ8Ge+"6%@H~wx0V|vxL5GyD:Sg<xsPxzLV}SSDucGVDxg2Q,<++[d|vxU62[=x>Z|SB)XDs_?;``3wqMEKk5jJPDhemVrpbYuBeSKSe]BJHEur$(`mYErcmTO)&xmAxN#[Zh(c7u=*gjI8bC&VN5fl@={WzLjn|Xw]B2bPp>;|?xdv%{bZ;,TTcF>LPL%r`Y*dohJa5%.ixE[tk,"<yNMEUAjj;#5aqQKO#97+4v"!$m;lnRst*GpsKZ(b6K0W29GA1!BA<v_l=OcVWwh(^#/d~I&I(^nK]/kjH@<iCR]){D[_zsvP6c@DUXK}P0Hz)Np|0ySk=oI{KU"%jP@$Y}ZW0yyiRvAz1$%x%D4(^;}`q_P{BA(:r=Cz"Lr*0~$m9d<wQYoPxl"*CD{]o*Hfla{qyVIn%c?V4rh_*D9B/A/5BHqclUYIX}2P:`@J07JrMHWZ}C=9EhTTVJ)W_:D5^j*##sL+RP}|$Hd;|LObH[>"u^d%vOcCs&f[XL_PL^u^ZNvnXd&<?*+cQm4_u^[*}9jD{5G>{+J}5ut{74PPN6^Wq{mHmk[[M~tBTt9Y2s,;kGs)Wc!h0RC;V|(oU&$5;io6t{](Kk#K!jU&ezMb$5h(6MJt:i$ni/&L&zNKJD{]E5L|{J#Gh&]/jZxcjtM~AI^R|D+aDnhjdO@Y4raZkc&Q(j.z5a}4X7Sc=;BKw0yOJYw(8N"?6FfCs{Cz1F=M)Wi&6?tP<6otM~(PL8;SjcN>1/gT^??&O@f5OC"F^"odljK,(#?;q#,ED@}Gs_YaRy%[j~0y(5<wzODK$9Ec1uthehezB(lXtKpqUN3_u)r~_yA6lsp54!k8KB7tY@V(&Monp,b&!{x,r(|c^$ivUQJ#n)SWmoc#3.WM&9o7g"cVhkO&m`End#5x(E*Mm6LfEE19N!Zt<@>>!X9#yPxBbW6sBI[T"](EFs//Dj8hGC*KV%b,sJkqGG@&iyW&GBXKZN0*iv@MVBya;&jP{w,zjf4rqZ4GDgG2oYYyZ4n.?w_l_cCDnn|R2)LA/dhSAa`lz?Fa^WE8aho!AGcim<!Xv5;"0<|QNQXEuG.!8&:M$o";6N,_xrRj9H$(RyJBCH[TAWChPH!Wldnh_/Aiz1.!^_6cQ"Riy`IZ:Ikc!J3j,;3?86a9>&;5<L9E>c:IX.nz4g#hnhmzC%1ZSj,;VSN)pXtO`]nz^a:nka++lSMtBVj|IZ8y|?6@sb8/Hzs%~_Ry6Ow_iAXcL>O]QQI!i(;5G)qO0)Jg$6YLvLJJsh^c/P)Nc0D`#x;R~Np.Ry<YNB>W;ZO_PRE#(anhpGbT+WM!_q565wHD")}#7Lu>H1IAO&:G214@H|5zs{KU!n^(KT=XoQdo~#RpPLE3.bj&}?|R3n(?ta/D7"80iL19$B73ML^r:Zsd7S"_WEpppOY#Ru[&^5D%f5f/I^:JUObI)D"f*3:"w{COW&&X+mX[([|#Dzq=)6XvM&(XZeQXUd"P7qAO_,MfHnE.?RjKKr:Z9Gn*[o64"nFDEFm):3`N_|d<}ka1_nJ5E([&A6eh!Syaq@I3}AR!4w4Vzi_<gQVzHhBo+Ex27`{N~8>^YE};v3cFX!SP?&A6"+"t5[U:fU`o~EB`?;@5$Z4(wJA?XK9WyGHE:;M*Fta:5Nhq;673?(Gbr{Dzd<"}|5X&9`@o([EM4V$rLT.tIeJn6Z%euiI]{NnGqV`tf^x$?tey;VO:cd):y~AE!BDn.PxLXi5_7O+lRuEK!)?UVdGX</.;k@IbywwW)_&.JI(,]*56Z%AzLYB&09.P5L0p>>cdi4VcbuZ@D|R,;(Zjl|e0fj3Z65csk>:F1<TX,&I6L]|xNH)yJq]km)8I39Zwp]1*wgy`bF#}E<rZ]l[7dl18a=aq7CG5?zb7qp{d0,|yMcf&pdxIF[9j?gVcr4VXmZGnA6B.!yhrfaEz"):n~VT[V0L)l)<&PNjcnBJ:"C4];a!rs_o93XQtpP5*k8SPwe+W/&PNv"Rdy%OyOr6Y0(&@>;+dTc24KC5))VRc=BF5&9xO9?k0Oh,[?Kb(jM8sva;>"H|f!CC_{Jd~tcmhMdOVJb.BZwchH5u{1vrIRu1E8f=Hf%y9vLVzcHt$}px)8!Y6Knq{Rn0/m!)]1"2ui<>WFr36rmZ8uvv7nCBwcE$W,>PK#7`6UhOlh>nO^YilKXMIKtt1K&O#Jq+nSimV+l4J.T`Ab)MR)B;UB~3=7(D~vz=>rsEP(hY4;hinicyI[XrOfC?@U~SHeCKa.sevA:>jd3v~=?>~SNl~?A1I~~+ZUvFKTxmZP}g_d3:v@D8Esk?r$2NzB,SDV""N9OC{?~j_PR{umoMGyl5D*%QT|"D5jiLaV1ax]SrOTyeCwS,=MwGMHpg{i2}>aUvCBiENfJLj"rRBi2)KK+dEv7L~g4WZ[fpe"ZX&AE*z^^djagT:`#KrwGr<I0JZ`tk)9JRMEa"Z{%BtQvOYMBlf.6`iF8AlO@}+jKbGat~Ox/1k:ZX&wEjou/+VFzp=@zV;W0vrNA2?&2hZ7I5Y?xsaj=NjtnLIpE/t=DJNue}/Bm;A35d5kGNiZ#Rc37;5[9i77!dpdI*]tPOe3UwG"P{WSWKFo:z~Jf.txM)^r47rI<OFL5RGE5}jxM|&4Uq*{=xD/z0|/wb;&:~N1Jg|*[.rp@Ncr9nO]NLPMR.gkAV,yIw+;S^(ELI2y93,uNuW+(VUgJ)Kxh>0/4l9+BIbS.P8YW"_M"dK.=qL]WL&Kby8.1.QrMxb0w<1jHDfnBg]b)VI%Lz!9:K"(dY#3kws^r_fdc;k^h]A)IY+@#IB$.pOY<i9DIN/(nVHc;#dD9OueBt$cQ5Pqm,7*IDcIAyPcN5xB,^%a)UyX_2DQG,gMCNPcETns]8JYV;t7IWF]YFYWB:FF~4?f{}wO/xE_{EI)0579TcW6Vk)0/MD]eGK8;Q}>|cPfu+%huI?>2|by:%:>eu3WBG[:alHd*MIYWxz/M&?0WMRYkJw&B>50yg$S*sIdZ`mCuUKmC|aaZF!KlGr,NcK9Bn6+_7VnKGtLWBG4HM({!(rkSbF>U1?8;tb//ixtOXe9[@bKZorS*2GOYC|J0FQ"I!7[kUe,XuW_oy,1tzH8!9%eJEBDMgf_:Ld8ZVO494Z2pm7u/(/J)Heu8kH4:yBHQ$FlLq%dzqt^Bns^c)/BmmGm!rY`YUTPH49%l1D)Uh9!+.g9<9J|I[A.72Z3{R!hF`vtv>aE?gm~g*YR=!M5Cp$*A,p!Y`1f2(KQ_ZI2j7:J"N@Hu{FchUArwm6]b_:d6[ZAZ0t2&Ywav`_1%%|bB`ahpf]+tVyyTs7[m_RoXjCGybh!C%u|#tv$q<0/RRB@JVP`?ZeMB>]*v|#pt%F`7yZJ$5E*b6/3f{MWwQMbc3ci>|a%u+gLwP)03g.wl|qN~.gv*z%Yu:4Q7fP37a7=sb*@W7:bcLHhGptcpDN?dB873J#3#nQdc@/qw?pxWz"hYfuW,`B`W07.[upBGKf0cZ<ki9f/1dJeJER>ZFU~(&1&T_H@aFY$!z62Z.GKpKD%+L:+:LH6,zcNc(=G%O0#_(+Kw~30iE{eJdJsjS>8&L56ASdR^Y;]XGzRN+xi3!e_u[dq_%#j0&tP_4.<N|5]{BbJ8FLqtaT;nA91/0|hTVUU.@Ve@Vj4_4H"*7S71N,1&XQ3vmxx|:w4CHFeXeZj{IYVk&/^]T27O=t`Yd3,g(Gb![&aMKJ|5+WsgdykKy6nt5&ck0Ce]U]jl>|(AW]=*LkZBp:%Eq=npNN=UNxX.6q`%V5K7Z3PQd6vtX.hlzT8f~D{PGT~,GU)r`Y(Q2]LO7]WGFRXi8x@=9RD|FJ+2}m?X@$Ed=;QHLH`H2(6:1`"!cG]}Uyi}MM!thGn:%9)yG0?;e@Eg0ku{8evEc<eJ;?W}[AdNW#6R;gi<O`I%99m70h]iYxklEXIYl?q_ML|ynV`,[FK[.OzUow8&R|})?TqOb760xmxL4tM~A3tO+xpA7QHuFJr7:&G@3d[6pL[Y"J4#[Pi}?U[n{Qkb1=a=:|l|1/26q@"I{>fi=a_08~0ybJC.DL3?kz&y8Sl_zsUwWRm"Xq:;OhhZS}`;%:/|fl:4Q&`t2F6q!h0Rm+pDJ,>)jY>+]"xPs{]("y24=hxqb68>*(crb+N2YZxi=`%,%F9v&|ZWo:F6(pwPo*&m:5sO%9nOj=!@(cN>Yi&u?h*?!X)C:qoInU09UZZjejV|_yEejR9{e]CJ[43D/f`lsUq2NAcp|.#z!Z]inhhG:CgT6)K{O.nO6/%}<Le/K3U<ltBh3kaO!!&9TwSF}X%Pp`,xX*aB{;&diNRvNCt*qTF)lh&l{]yid8rKd^a&KSf+Y71?IZLFNoPCj7+J_pOk}i4r6ZZM2b[QX/PU>L*a4O#9q7Z.z<KIL@Y3*TmkGU"f3B]Wj=.I[_5b,=LfmJ"fL*pZ`B+kV&n:%S4{1j"PfeOD.Y{q.;,*:+Rr(^WNAzUXQE=uD|.yKG3d1?ta$2i"L"rQNx%Su0ignhqz`(R/3rZ*kVMxhN7R<;*:pUBJ71,q|6QHdNwDWr9?%NPj7K1&3MWFa2;*:3rL7]2kJ(n`6`8w6c?;B{QiM@.hKyTsgAbHaH>;5Bm^z61GNh0R%9/[9?cdFfgc&q<HZ~]j@37l`lSoOZ2Uc8VqeZuIkn1?cd20VdTXu)ME1J;A66%9vO&d{+V*%`#FV{[AySrpdYq63L|Ln@t4"cHE@0qpMb][tUC&EnNidiO5Crfa$R2A;_7v<x~n9R55:;vS<0ck:nP*OX]tk1%wtmnGfp]PeoX{H%nPxOg6G|5pkc3nsP!ULmNF=KuLO&{iNp/:dRK}v6)oqS[oGhjia7r?O?qV@u]d$Lfl?P,2W{5+ZUeeo@29S!K1hR0y,lV]23_y,_H>yRZFu]#aslriZOeGzgM>5RZFGd9x*xxc$2<@pNtmTC_4=|<pc4;.C@MJy~y^Jc`y[1gnF2!});1R%j!>yE_%s)YCL<n&Ofg=0HaFbt1C,c8gY=n{=$~9:J~#k*`"$m(<9{^$T&?6P$u^,_:y1L8G;<#{i|HWl(8^+ngqp==&bun(p?4nu>xQ`]5]$_Hs"{eW_[c?6n:TzWA9$l~{%;S?Sh{Eq`I?xnbqJV:&fT9JJd#mPD{>pnI~!m[do``Q3a$,!(;>knV(1tC[^$UclPPW"Z.TEfbxgEwp*|EQiME7}=gh{=hp&20=|n"iU|Vuyv"NjGA{#ois<^[)|f4&a,1@U`GU.}78zsbeSea6pddNBH]v8*?$~uU&>&!~1R^F~~6m<xM/xq?;p&`m/.0y"*l[W+A"$7)Ij[Y?t~~{w!?jt`kf#X5TS;~s][!ep0f%k~KWE7or+K+TRf47D)4?Rr(.[sHii}^J_b{%RY"^F_L#eNsG4=&pn3}xaFXu/9SEPw^)o?6_Pt;/7)k?%KIhn>:Qcg5n)$OFe`}VCVjP+C4C}t"(~>2Uigq$c617d]0U8b&6zF%BF7Z]1%~b>r0@Kiuw_CArUqlQ;eudG;lBk8w"x]ieV)~>rn)Tjg"N^>q_ZK+J#7TB2%7}:V1]unYqb1W7cK;JmCc{F~yK&,5]1n,T]2p.o.yHptm_WEOv0*0DGP<8Wi(WEHpt9kdXw(H"*Ugg*Bi4vD2")H{W9M&")D&WEH=J@QIR;js4(BZesO}xfMKvA7jf|[`eD>+CN?Fh8(?DNX/OD,:BKtWeAWipKC@u`AfR5u.Io+=htDhLB}1O:Jv>R6<}2}Ote((UU:$`BmN|}~RC<$EEAi)H&4.OrDk^l1h&z*VQX/ge<!{uUDHRJ.^vbPJt&qkuqnqCa/UECi)H{WZL$A}e:V)I=)%*6P(_)N3u3uv@3I7R*r7n@)b?<V.M9vB+&%?DRCG<^t!h4o^WYq#"(B.$dS<0ydmehg`CiY<c]VB611&Tap*Q4cF)sP]:5B+iGu8O"FjH(,dB9~SHgW8r`B*q+G[2nc?VL8qNM!=gTfBJ?b]C@$7PW(=hg!1d~z"OYD=G?o$(#?#(}0_QbOhYa"lBP;*b0EGPev.>s"*dS5B;YhzW,bkn}]i6=hKg|O+O;CGOvO=>n+=&HORtoIi"Mc4WXLAN]5.M@~oH9WRBd8/b|Ont>vj_8hw1N|f_l2MA3}Q|lC7sX"?Db~[(8MUEd]:&z)>Dh`f_8h/d"fb"w>!w6}Q|&bbM]dm>UBOaaN`V.nD:qb*|MYHan{bRn%onu~zg6_79dhuT9Ou@Txe/zEe}_:27,~qZJ4T9L/j}l{[sewTks}#68>Tbku<<0R,|IX:~;Rl1p^z<(}%Xq(>F%af^EJ?}ht]sZp|l8<JSg|!N!s&a#h~L<2v][Ck|{#X([vbn9Lzkj|7S5>GL[XF.e^#}X"dn.vOjK`:^fskMPq+v&H*~)_!~2G_;pF3Yg.]|fs=BPqv<L8P~DSpW+Q~SX^yf3sd/<sCeL/!_FaU(}5k%3[eD*spt+C{[w9C}IX(181iXKYXWJm2Pmd`V!ZC6RK_3d{2QtfX%LOu7OcmV<XmAMM4C?rYG#fiu9qY_O0v!$KO!?#>4OP~3zBeJTe}qHXyJb7o[Ff(BOYgRMp|3Gj*0M@!SD%^iNfSl`@Nyp~+0UY8b9{`?GxCULI|i`@oxoEVvC)$DLw!S"MR1m+bY$iB,z?Rv.I+{Ha)qHsbkH5bc^8?rRzEzrp,BjxR99d%5~#Fs_pF@_1kf)|gM;L|]Ga84|$=CcvFs}dqS`pWk`@(w+y*9,kOZ4O2:;bom2j+IEt!k`@(wwcM1@L]iki0IV$~Os+ctm!r`hY5X1M2_1RRdRyHw1a*UgR@dcY<k@bPtg]f%.JXfaNgR*ugYXuy"&V1M1:Ej|K{)OMgR*uCSB(T5|L>{}4n#&S"5#e#6M#r`_vHM{lHae62|YB1u6)HpBB?nvo2H"b*5{[WP*?AI`xt&Kt+>1V]vNtXQ|Jj(mBK7E".F^PL0n*]"Pt"NGUXUUSo)}2^z.8ZkSB_Gv+W0+ly,mE1/blfl&1+_h/=,FG2xlp#+,!mMHH>6AK+SV.*6MO[gBzMO@Y*Qg?3bPtLu2ZWr_H*4U/7jX7fvR22b=BYf)|;LHM|]elieKkES;Ct;UBeyy@/YgR5SeSAs*YwdzjJQ!J|<~c/Bbf6A"bVX`/Lk:!8x+x=Hd8o]8O!PQHbB`Vn+S*z"fdUSvf<Sa^iyX<7#8L&K?o9FN7s2^c{%wVgRANeS#1oo.@8:ns<;%Bd,vnQuAs*YA3H7UKIeC<qev84RVNnS@*b<S](}<6c?xl,7TP~3Rj+5@=3ji9i1MYAw~p<Yt]sO.Wd/~`&J!F*MeuQJ^!S{;:zSjnR/=,ZK.y*9hOxg8?wJLv,Dd.#D.,uu&f)|,XSzrp9S,/Xoo{xZd.b,sB<#nH`Nvam@@<jYaE]LR0>l!p"t`JVfrE%u>*7`Z*gX5:n2K!{U"%l?+$IZ*M`"MC^th<pcNA6/vVPNke`V<v>P5P<CjbbjQ)olytr*i4[1!1?A25e@pV;B7K4f+,jk%Y0R`/zs/Rv0i9NBO7<jCdammGm*u:41"`MLW9If"+34_bdQ3X1c<6ne`Vb+vZX})3Vi&Azi##qEUvU]?TgRN{dSAsaZ/MzAZLZ2qjVxVfIwb5dv#M|]jk!c*%R`/VQromon(jjLR2"`k(naf(44WHqL!#GKUaZ07n+5M~^>1kUZ_2IeqDT!ayQF(|3XDaX}zY9Bp|jZW#6UD:R<JYA#r`?u#M{l@VAjFbT"R7uBS2hoYB`V5_/5u^>!IA%DUD4z?:^2h)7W2k`@&DPY[;iEy2]LRDLOcK!O";Vy.h*7[tB(G@Bz`VGRb,TEb`5a+eP*Vi:s|$iEysZL>9CZBx)n9e%^Y(B?|f+`?=Hu]eB1iXz([GUWTTiEsGI>AB]$QLf..13i]Zef714ph4uyG[+r&sGSP@i~7&P@WU@@yggOMj*|@pd++>YV&shRP@(|JuQLUx4=yKNxaN^MC}IwY)^c)XsI4gCEcmnGra$URf~!EmW%|H#f~D8Z.13D.1%Fuy91klRI:Z|[fM)~w=%scQP@$|8lQL?_C3Afltb6_KBsnPOj!|5eQLW,xqR74gJz;56P%siOOj7|RW@z5|}RQL=+_Q.dCGAL"vGY&x91"j`MAL"v?K[CR5#N`&"q_sP+fGzP[Mczy25{$lMha^6lh(rK{$n<P(lgCh<sv_ZIQ4#))gR9CE3L?)PQR%~,^Rb|Mk^;q4&H=?Y19}5_n(*,%,[n[$hWRhbZs[5&pkP:ip653}2HuC(w>,n~FRRZs|=Z(&">yl/eUXcTpp;_`#]]P|l?]]h|vR^]n|!0^]T[>?jh8`5a<sE(0R;|x]e~XD}f@`,gniL&L[?A.Dv`3/r8o{/Wx~G<6oj{6f)~z#h6A@Ytp(xPzFOI%}OPQL*+,Im~VZEY1L1aDlC{Gx2yk|oNgW}y+h;Z`wAW@yHo.}qFd+J`k~fWh?E%<sg)Z+_Q_5UKAW{4914|^E4g!w7Zy|>mjEFt?HrOP19i?ctZY+G`5KTLr}|@fWYyt"b6Bp"`5o2y)X~@!c(ZY+A`H$CN2,dRr7w8xFXl>~HwcZ#Fq6Oijsqyo_H>[@ecs6:m*2@Q9u?JAW4y;V3Fo_sWBBcQXYD:VmfR2,)Z|[z6*R`,}3gcY+TGY%^zIaaf<sZJ#REpaYALQR4.kzqo6N?MMj8*Fo+%MaW%;zTz=jgG#LQjX%;zKzvdAj,0TmAecQp@F]qHnIV[Li#;C45aZ94g@+E6BKmW<>xqj~=Rc+`_SvfWSE:h<+vvAW)>b<;sL(0RpCV%7zozH`.iP1<t_/N>4yLeX%7zTzGo,dcQQ*+5g?^~<xc|l)W/"FcQ81^/c~PoHh}sSO3G<FmyMj?GAYt1[O03!c?JD0<ou&B."1sN5IAL0*af;s[UP1.t_c8|R6?}uz|q0.@~QoTVgYA{V!E.?HAm,,6xowuyD<gyl+6RH0Q+/p?u=hpfPvBZj1O*`,`w^vd1{RPYp5DGhOl.`V{,DG|$v?xZy|oWU>,I;sG&`T@@Cw=LMjOnYN1auc~[6;_j;Z<o*5.5[alWEe~@*X!00!rZII^}EBEsM^+7,?ezR#t(kI#R`~|uqWh%;h}1VCYLOBW]|4KwGZWLrO~K~~/!(hZAI~Y`k~5WCb~~DHBA9|fA%|wNgZ#z)h[(`}}t87W0]}@t!H(*7MCy#$dHDmB">2@XSv[~$y+~uHo._|EB:WX$FO(HtWXKI}2<%ah<,,_DbOMjW~^6P@V~c?c+@_MhfWf=af3KEZ4s$kjDcQzIC]9]KS}y3K{Q}j0KTK1|ZYTQTZnvF`*xG5q)/LcHjtQ.qy5yM@7Z}sP?LiI]4xAW2wm|x2PLf.q1D}wIALy^Z&kWew0R%}I;F7jaoI3y%{R"El]}!/3FxFK~~.Kc2e|}OZl1uW{`:HfW:&m~)XkIH70R$}7)YC2,qcC{@D/e9%$a~4Qd7[3g+UasT%+is|XSRzmR&{~sc~Q+8FY;xP#xecg@:js6P,<)%(4TRkz,x<6}iCTn/I7Y)m~t0R]{4P*`Pq+Hx[_|Kj+k3o~tXY<4)$XLx.+sQ*`$sQ(/;8D(Q|gMLI@[0xesbcy>XI@[2LN(rujrCbA~jGp>^20v8{h/A=FlU[A+"=}_0*O(g7ak!6=s<>;s)[Cnt|=EY_Pal>}_,_ADaL?&S?&$<wa`:HA(,0!Y#*m{oJ<{m1hSu+T*m.ZET%OtW(,M4Tt&Pz*El[0xa|`;}&(Uz3#gF}@K!&f"(sGRAZr>>yigxHZ{Onvip/KXy+8hbey[WR+b"zO9E8XY;NB(b6DB?YQ@LEHs|_JlVVJL>j++m(f`a_/}F/axT_~1JWJLdG(&5Tqng9+I:2jx[i;T4TO(o1#}*5Bdl>jKko/}O^@r@xQR!cA"D?U_$fY_gViYYEfZ=)3oGBvg`BzyO1JSTxHRF^<ssgL|Jq_@B!z|1Md_gXG#)HSWAD{u3oR{!m|!Oqi9JS.uqx.,3hQ&0}$%q|LRns&E/&C9[g(PdGQ*[&Q;`Cm}f|7QA(IDXq``W?|I0sJ&FJX}5_9e>&,@C+7h9s;7+{~&{(($<wSt7<g|31*I%I+AzkQ.[Sg_enHDmWZN={?(vK+AUxqa#r)F,$(br]:3M(Te7Y#*I@BCq/8ha4H/|ktB=5M_=&h+CSce:=,I9`[)8/P93%oVs@day<i8^^GznFB]li](7eF5}]8|MTyR!cF5c+qyy,t_<9IYy[G%Pg,$Cve]FGj!Rd;2@R?n5Yls|$5TO?9*.QT`VC<r2rx<Bik@PI[&:|oiYx:}LH`|xeQ%7zrW8>8JPG7RR^OjU_Ah:@;Q2~QZ#rwxY^(!FOlHA*kT6DbjwE!|HVaKYX[p|YI0~v#?ULtLU[}WA%O#!>W9:2?GKu5op|avG(?@d?#op|ynd_?6Q^C`>Q,B7sP3W+CahW0o([7gCo^S23Wrq}j5FR`nn_2y5Y8~l,9>8(L4K,zrEt,&TqBH(K#|M>B%CuC6Wr!}sG4RnRKOm12BYF5O7s&tuswqXn~_8&&tuslNN$8}2lE%ko":5&+|.yWih5yuL|)(M|EFp_=];vuL=u.&0K.@QCB~>R/&wpbjoEA}[i2>T`b_<R`[34M(jYnrVCB~w?P}7gI]]S/>XIz}iM/u^nF">ji:m(/iLqF"k%+`,&w(oCd~og8ITkM1tgL=}]E^wR$IYxU$3y{RaGq>7!aj^<O4Z9Wm^&;)($`*736fT({*&Y#*(3ZS|?Qx/?en%[nW}$jk#xvK`qmI)3I($N4=62NW{Q`ne40}d}HV|(1|;C7stnmpWR8~CR`n")b~1|HVdB6R/oasW92=+tK/eU!fOQ+Q>rGD]s5Vlb$&2m;hUx!+mqVERh"hOITkfWT%uA6T3g2+@I(}9z<|egRD+o0Bne")w>,Ljg7J3|zY&rPENRo!7y>j[/2^<.8h!@scpl6Ew*4a~%<2kF"(hE(|ZM_&=kpKQ0[Qzk!]%>*O?e1(TL<}NQDP@R@@OzDln.QvC?FD_}w"Xn:0QICMq.MJ1sTE7UbMq.SXQsh:}nBGr~Mi;C"5o>BjQQ6n%[:HC(S79Y#*/40R^?,c_}SESv,`J=c%o)pv2Xvw?^Do_O2c~JLcz3GS:2lk??_oc~s(YFvZuZ.LPez3e(J1bQ0mE>axbjgEk|h3e(o`@Axgb]7g}$;lUXI&]:EJIvxIXiksfOW_u^s}Av0|/@O|24z<LRSW*n6WoCzr^.zp$rHE+nZ51_ch`r|_CrlR{V%,.w;sN+^&lMJ/J<2TnO@61_DM&y$w.lnDT%guPQUJbj[xT|=i[&,EW_WT@}Ij@v}<c_V?.@!EvssDHVSC3+&SE}?id_ViW_^MI]gF_|!Io<|MK]Gzjl`RnRF9py0ET%Yis>lP7YflO?zEg|tL0EO)c%p#3$1_HwG(tIu>:#1_(<o>koFu1&l|iM.%lR!}0}en_reB{vTbMRm.%EDoL$S(@Kn.x*MiU.S(f|1}bAk``I,}]!0r./Ah4KdGnmVn8^J1YJa(oC0}q=Pz"`8}.Lq>uQN|vkhWq#~F7nM4"?8ET%/>9hvgP9`j2+6"k~Rzn>5:c_Id+}90~L~,o_?kM)NI38Wrf`s^PRQG38_RV?6T*[<9U!+oP{h)+<SrQ}@T"(@lz,NIsNeT7Y7R9(z~<kI&3>e(wR8XQ]i~:<z}T}J6rbOQbRT%#};85rI9ixgE?~diq>%2O|?)WLy>Xgt*MW~~$+F_,@9*^{lx}]h_GvAj>i=T:Qn_05t*9>]kM4Q`G&c9c=lxBH25FshC[Sx*>.f.9/0If4/s<$H&3>iseBDY=j+tD1sn"Q~HG*drd7iL3W{g/z~It@cxvjT$hl^xa.?yyKEDif?mwQ>WIx2qDX3iQ]LH&gxT7f#eyGl%HfXj~w+InEq)"oTV=x)}/57WNl`k1lVKH?c0kUI7F8^4xo/{@LySAeoGX57!/$^(E/i%}GV_|_hx`U8~_~PZ=F,eBe.&32PlVap/RSWd1j@2:oXu1%r+z*OL:/sVvxA}r5fl<YH0Wn[ccY7d*taszv([&K9mQPmfO*?rjckH&4]$A8DEpLfGj(<~R4]|&D~UUn:/sV~z:x"?zKQ)y(T(=6o]8.qC0rD[0.fH9MT(BeB8*#.,}==xA%6tK#uJgiv8[6X2[ZH)H4H2XUcF={_]1$~PlbVjTv&!)tm>#GQi[j?z9^Kh1*aja|pHc7s0qIP9gY;Oe).9f,1g|u*et0x7OMI1oOOMjmqvIO]"R(uT.z"k^NkhTN%!&Gx3%Z>M0?GLYzHU/7T?6$[5FN*$(f)`+vQV_4y1e=bvgS7D8|9LQDxIc%oFxnWuB`#KQ)MV&@x(d|^xA%!JyRYnwDWc.Y&s8FRtVxY!DES":C/=D(4XVj%*d,b~yQ/?,aJ*U~x[W(n|W_8^Qn]n[nbnX]@bDkasr{m89rD_Aq#ocM>wrFkhT%6GQR!XO|m:;6o"6TB!?DzZrS7sc=h>sCbn&WEH0coNNW?G:ne<<|zJX<iC(#J?MqM:{0d7$ryDWcQzn>"F5Lt?z>J?zK^L1>;(>JM)}&IXdS7XVO1r{iwJ;4jkTvq+(HpCrI,(y{RE6nJIt?nc$4.YH>{zqNIjcna3yU+?ghn`aI(r`"IOjXkSeg+fM:Rz{b!uy11%*|7B(yPD[Y5TyWlB3(O|MVeua|Tn;hKC[h&mOMyc/?4ZDzXXLOc`E$*ec,8;BKOqaVCncMbjLLa|7)>&G;r>M~M)T)W(1S9&b+e~v/gQ8~D%%%l5<~yI}TVqO(]rxK,v*niTkZjvyZ5$IOJ1fU@9YN]3fDbjP3o1=83o;]f__}gFG}o:(&c[dG7W2>Z59wTIms{nwBQ0^K8~sC6r3m`)1+xGA{e9*.ah}rp!w.@Iv}Qqo~8;8F5i0>Kg1,y5||<;p>:2eMyj,(Jqq];Q6P~Ld9qf_?7gwa/l2(b<#|iM$~<k2(0R{}R|=K2;l_gF!>yrI$f"vwe]"Q8m8;aQ`<}]%)<x9/wIg;kWx?G&`&m`gf3s7Z8$Xmv<9lHcOi>CRom.%0C%rg5>z{EJ:P7*W9nWkt8~Im`&rg?sTnl>t`82n23Y6CQ|6t:^3tvQ<@C7fMj$5}{nn>t>9>SB/C"d6e(}+I=edDz{]bx>}(pN.MQ<,Ij`]%_%9GJ^hyu|c><2gXO~*tt`SRh$1JAeT%|o2>f]FV%tPzryy_,Y$r!(<rbsauluW}a0X^aZa|xu`V&6Dr;_*Y53mRx(m(8~9KvXnnY+?&E7jg#9N?d>,rtaQR{_$e>jQ)EJ:[enkgy[!ZXj+.{&3YWQId<}e18TO7cM*67|#YCs|8?&+ba`gF<|mw"{i0E~*togSfNqQ/Qzts>>sx]|rBasc~*"#g(|7B#^rWo_CzCRl~j%;(JEp|LJH+@I[`~sM|^{*F4v;&y*;SI]`?:3Cs#cShm!.S%,9x;st+[&g8J4;$SnGM]gkJdG]=W_Pl/@2.Z(lSegN7Pz5oO}[idn:7M|fUI~@J7$y=f>?f=QMS,|0u>>Sg(~+xXn$4bjH|Oj1*RS4)8h<~9CJ(he$&a~LMFx<<43nRs>8~noqBdc[Bqu0}(R.@=,Z(+Iy3/h2+e!W|i5z>#zt>U%SG$#vWU[SgjgN!Un7RuEtCk_0xT|R|_*U%wu6aX`NQJK<EJxvQNj1Dzr|&0}<]B*VI6s<9+$hh@(XI$_m*6R!cu8%,;?H@Fte(|hN4%O8~B+NQXedG9Y+yzr0K+W^bQc~EaJyps5?6l%pFuKaNF2/w(yQ5Ai#97x)L$0Msj)tYKTC~tt`K.&g#/<NVH}Bq@Vs7,OyDuGae:Byn0mc}|&Y]7bl?9~f@DGC(S?3X;%v~xO1FtT2CwI!BKh+2;.e(sbUp%zZ|$5x>[!K|Z$9T61|.9_W+Ca`;IL[KBQ?:Jg{>[yx|5?i^=$I~uzm>JTGZUZU|]*rDScOJh2Ty:<RDW"9W7+&=%?hkYka;k<3Fl4:F!y<.K[%[Re&oBZDY]~`b#,>WyOfHb!q]<>m9,Y{i:&Ifb3$OoFA"5dIucg^BAM9z4}IjRWy%cPLf9uB(dy{20<r7?Q#?{fH^7b%oOWs+5$}`;6A:Ju:v3`WVyr.l,su#0r+>B7~&L}/)x>ra6bh0ZESEo;B&x#[S,|rw>rM9Z4F1*Z?9Fw6d%wi*$bjOV80(}[.LlG&N7[fKuCQPDmJI<US)c_6<yrC$6MxwQ=2e|;F53||Atou}b5uJLz?$fg`%49^3E^oG/4e.qJZ_ala+B&KG+P6I#}F^2BNW]ufFqpgWLM+C1J"`dym@1>m)4e_"6LB"x(p:RxNlh*isiOV_]_UN75po4Fm;t>GBZ9UUn)jU+@bAHD%*|{.C90.lb6s%9F@sWD7$`TOK,97<1p{k3/Wd6<tc)*)0WZ^JuJ6H^x%(y]erxWdSwIp>i#JqAmJcIO:k`:|p=?ReWu_CKa&eyh"`DyqZF>ey+`mG8_cZkw<z~a.`?xlAXG)+6$Xm*tNA5=nIH`s(~_oJL=z[4}FYoU{XpM|sfbK5nUD~wyl%$Wg!Q(a&dgLTL:?1?]`G}|C0pY}$~96f"_O9;cu*e@Z;:2wBvGjm~tkB8{>9P(4lm[Ytj|Z3:0~_@ha+yK={V%vKJ,mbe|}K&||b/>QLpHYF]>($U|=]^&D<yp3v|C|l:/B&~9o%2+,}j/m>h=l>[_)ld_P,!&;}VO`LOqGB;ed[EJQ5tIH@@x*NUJbyq(^9kh,?KIq[AhEzQYOG1h(T0}NayrvBvd,zH43;rgx7<9Z)N+|+LbJzV|IpOq3td0SV%~J!v>0(hgw">>)BU|1LN6T@Haeg3;mDF<"p/~ZmP|_RWsr_VE`*hWPn^x9xCYoE"_qXS(JOytJ7n_&>e0())?v9H]XII(x>cMmHZ{glC(!*9{5vH~`=FVBK3$ZH2y>WpCs&]|7I~`"HO|9>#|uEeW?`;2iSBwyc/{Axy>R5u>dWg)_[.rly{}xF%[Fc8!H4x{Sk>QJu<~3{jh1hvnwK:Vj[^wY}X)^&>(KCRn.v)]LwYw]Tyle~$4YFTLfbU(W&0hp4kbRD(hyymr/4[ExDykr]hu$k^U.}JuH(MlF:#d(}s48$8Glt1;T,>D7}]E.hCTVj)sS[H@8.5:MHs|.Lz>kz[./xV4&Z8~TF@&1dfnT:_%J1N%Oa5sofc~~qJ`cR@@L<7>p:rR:X,nC0F)EJZSfd!hq1lbaxDjkw<~VE5~uD[GiA^xiyd0"dq_$l?&ujA0Gxl}j{Z_T~u2X_*^a~Ag85.}u4$cs4yI4>"4v#6TPptF{h#}iHZ_rzFK0#b)&Hx?<d*jH1Zz2hsNb~Swc=+AKcQ492i1[EL1Qqj"y>9}6_kl4~]uM|!=s3qDm>`z]14OLg85]}k>u=PA=9`QYF]>`KO|#g0}CMj%`XHq=9<MTku6kbj)6P~|9T!*l^P~c,WnBri>Jrg__^RS"FxjtH=}{tV_}LK@Dgg)#QW|O@k>><S4i>BCNGj=w`!7q>mrfMtCu(,1BqepDEW5EhosHoHo]7/sR5j,m(,9e:Uv;cN}jB>(d$"fZ`<(KCq_Cl]3Y*,}uy7}FkM|rkT_b|Svp>k1Hlvwn~)F7TgA6F1x3T{}iAP|`$&?k!/sT50}"12$8Ir(xNO(R;UmE3j_nxT_mU:K.)&Hd[y>[f.#2}8^Q|gy&H(?/W*#)J2/vQIs,.hq12ps};Mq7+^]2Cc+,7nRx3x#R6OF=~gJ:2x!`>HRns[>6T+%D^68c(lj0}ru5/"}$|@t"r0[|%u()2N+|G_n{$UWj&b~$^7m6hMAb~"%|=)v0s}P3Ts[|%m[~C<|wK]y:1h!1r]1o>V{^^T~>RY_]14OQ5v>3?x1^nau</w|P1(oM[8=$=Z/13MZ@BJtZ7v~4(6T$gg>}tf(^o7srLCW8y1|(4en1AJqLR,o"*pKxl0p,vd~H<8~<>v9OjH;`G4BewJE%h3ex3NihZF[a{&fs>|<~ztO@~~>m>|XKVlZ[HTEM<5@Cv7|@ii^xUe~vmKqrEy3Rv2$)8[XU|7WMYN0~ha(+{zrdWwal_RC4|538(;Wq|Mvb_a~[EmW9*zrcJWdeY%tnI:@z})KZ6Q@BCMi!v2n&DA28yk#}G%F0!~YG+}v/]J%$fp+TVqM^C`BXct>*8@t9d;X1?_:r?]Z={pB(M!.z+pik0#4.V$SSi~/0_Ux#+$j<Mg|[)PX#3Sz/9>P@uU$eO"u~eqgh+ETTTMS8??bV[qjE@!RBt<sghYY"r}Z[dgLb:])=_TYXDXO"xKc;UY$5c9>lM!3W]_a:v(,F25xu1Ucns}(eg/>%Zpb}`zw@r&uc&83(xFIO=ht>`RQNfK9cPii`bj!!Vu41UZx?MlrblB_eu!jQu=Lb0+aTmO+ks^hOv0RZN%F!s{n9y_)61c_qBvCuBJwR?>P{ADg<kq*od~"ia8R^CBsb}frN!o(G^YMfxbvZO8u{oO)YE(+>.pNN)}hW0GL=?km[;"}Izm>*DKIV)X!&n;`S%Z?rbPo8s6~GV4GBV|Hc+?*S#z:Lbf,Ojy]3so6)}C2&~DR5TW^+VCg:vp/`+`9$R.nW(#P4T8=jyli5>VO]_dQw)lg:kS/lT}H?^Py!}FRuzE}[)YU,XIzOW@h]dBqU)GT<L5XXc|R8~C4kb9er8au)Z]),x1|oL="Tn"_EHzRY!{;S~D+cj7F0iPJ|Y1KN0@o?`3Yy@4w_k:z_|_2y{>2lK:9=iOQ/K2#%Y@}~)`&v@%[,*r<LcWK@Gb`o"e[oQS$<FAYDUjz#>Q:RND}Q5lO7#0qr}"wO<|E/(Y)La%(jk!B&W~IG~A)>OF_Bv"vPBZE765xqvS_BUT}Yp!K=}O)")ZZ&Pl<x<SX{x(TvH?MI&W/*5Hm0.3I"@9hOm:Xn,?!e+2s][X_~1VE|!TWSE03JS]"/Iy4G}AN0>m_R;|ip(.ad!(D>6z^66OFZlkyuU@Wnw+2y@=#k+_mkMdXf|6Mk^07U|2B|&;>AYz;M4n6akt>f,}Y_/K`DE1?|0ay3nM0XXBc|#Yngle:g1Bt/<0$T|%kZw~_SBJr^NC**so&bn?m@Evb/Z}r3A,>nbHU~s6FYF_H|5pgM{AZ9>")Zb%u|@]X(|G4d]7b8D!>^j]3gQfG}U<sHiU_.se:/nR{{vE(?6k3qw;Ay3jDz4d!,O1jDVs/Fb;P(v^g}=*J%+/w9o%v4EcGEC1n]wo41(M(3T`W(OeKgf|O&_E<hS~nPW,z83$~@WJtO!;&oH,BsiPo)heC#d#5FzV_g.c?[[bv(UfXlZ9FGPZYI?Dv?l>dLCTGfwSA6s$xLHu+?:gnp{(o3X8Rfg1yiAE>~rC60B3{r.^&mpDLH$/`.=[rU=#b"5juaf"Cd*9|QCB>m}d(y+%E=ak8gGtv1pD:iE$w/cCZb?KONQV@,(Rrpm~FX+3LP>fx5gn#vzD>{hKHY2Zx_AAnwFnJ6iZNI^8K_}Q@K0&"_5[&JC+$3*gEB"nGb/$ndnjZ5VVSb3YS25<~.;<s=t<s68&F{nMSv*xwF)L>G=GcWD?YUv<vRzMw=PtGq>^!AFDu3X!$%)[]f,jpCueWcqM|Q{t06Rdy&n^_#a):6ag4RWXyG&*x?:#ea|NsU_f!>G!LA;3Il`I#jb|5rf)>>J}*6o9{3b}s8kFVYQJF}Oa`Na8rj)v@ITC~MMm>@n)kZ.(~Hp9BTdldEI%e(frA25*mLD?2L3Nez#!8"94fIIiB3rqzk^f"U|K*Ze#nvN,VnHIOd/t/HIg=!Hp.lg<MkXm1p{B4y:>>fIJkST1wfnwI$rMqK?pqYb&`3QosU#:*y|UNt}yETf{ugJB"`_9agk=0Q@SLK,OQ+g[ZBWe:^)f|!4g]7bjLOWJ!4T6PdJ1A&4jUl|9Qpz]1dT*s4ILq6S@GP.l~?t?SM2Cy3n^Tz7e3B~>SOsRj;p87cfj0|e;B0P]uV[>4f]bj}4ou;NGj<Vy%G8I09vTWL!FUq?kmvQmfc,.Cjn6aQh^xAYAMxvO^wA(.*B#P;ZS9bX>HwmZR,B:$NW3<`<y{PKYzHUA"0cr>}R@i{@4Fx&uTs!+xpiRdf51>tn2/Ko3i3?!dnP.y&ek^HAvW+rXGwn%>u0,0<Lp3#WR`~Yz?6HESJKAXcr!MmG]J=M?XJMMQYZjJ)iTPuBQhe|UDD"z(;F5$&g]G>$?LN()~T>xhH/N1v5?ts.,@<)LygGP(^$+xbclBmC:qNIbcFBOoeOu`ma8m=o`hNoh|ebz>NB^j+Gru;cA.BCR.~Cqwx|svp>YV4UC3N@a3;rb.&n)6*y0y;uDEB,LbC44}b?Q|)?RZd23|N++r^AB$AEKioWtV,VPS#OTD/Q[c*ZTnrq`Ih>}lSjBz#?m6>he7NrrP+K:o?PmC:Jv+;u:RN;PJ$W&CU[LbU*f!lW(d4T#dqBy_]1qPHl}i,V7R%T#lf`vY)_Stem=FSa)8qA~i*oeJ?``YO@8xgYU(GBz?M(_vETyw|{%Kl|9.Lm6y8aV[ibF5T2o_o~v4F>eY?5G*!@AF6|9L4tWXvn&~l0d=)K%+*3>@=Gd|"06^}D*wspl~dLM]OcEd.C2~I6PoFNVpd_l=dW]]q$}[0J)bAcH"Nb+U$/Ddf^LjLZ[[LR4/IoZ4w?Ch1x$L^z~i{Mf)@M]}0?Oq:J9n./]HFXJsLUtD!x@Igem)u0E_E}.+y8={YRI@*GX[4FBL9m_GAzKZ@9P}jw!naLm5Znr4EK*cC+?Q>@;wfCQApJS4!fU[zQ?)&f$`]veS8?v}vL]"?vtsZ<UnaU%JsR+3yX|?VyH0CyQ/"~VdBxM5xdbng!i/PRYoO^O??xQneMvI3Nw;&kVBk,<MJf^(5~sYwVY|:u6dT9ESay}|])lzI`}E39)}p7hLRg"Y|YQ0!FFYp~dO>Jyox*()PM~LKMf(%Y/J)r~F<Tv,hK1.vQ/G/tEAO}W*G<]Cc;rvqi2zQDbqcMoG(mA6_&aBJml?Uj!$e*I91JTP|?JL;>d=`7Bx_rs,?$esWz(_m5zVFcHQ8~;*o]tFs_8ln>HEqbrjaepig.j8A<@YpU<sBycgoTZ?UaN}Wj]&w?7G+xV(px3T/R5F1hH~uGn>k/kn7kg>P$&TsP9IfH6p+4B0|Lqy%TDNJ#.Jz@8kG1LGbC(FD93>n5k6<G7s;[N3H_h*QFFHn)6c.!c:Z1ELQ</tm70iR(^"vI92N|?.r~e&fWc<3`1xhV[Z=~Y/^m@"8}u5z_d=EK/+q4b[>zUsZ!kEtZ.tsp+a>O0}3u[[WBP[{zhs?Ei!QR1_t2IbgwTVt.n]5ZIJ!W>)By|(t_a=66y;pmJM>m4M|&^+${4:"}F5Zn+WhGWQ1_>D=1k=fIdq[w;?#s`CS_wBLeXDG5?7Jq2:cBr!p>5d?o"C!57T/xrbH&^c,^ja8}`6dsX#pYpiqaH|lYeN/kvuB"p[(o1jfUUS2yaL,V+m|j+!c@q?9rJ:[yZD0iYSU3*1W_{LJOZS=Llu{+zMEzbY%zos:nwm<NFXjGr0?Em*!y_VdpkgEIGn#M4Nn9YT,key9p9R0PG60qmD$L%*C$vGMeyv_J:JwJV}98xp7K"}|WR"yC)d:J;o$UQ]t(MEuq,X}|p?IRUO(m9T,{QRGCzTHa^;M#i|U28+cS|yV(~>v274,D}g%)Tmo)+fZXxcl[T&/J7}*(olD~^YQ|Pf_"`Q:{bY`r;_n4J/N7.vKz1>IjoVO)$}>]eg1_Dz:Ij3b<$>8`YkwCJTpi).j8hFHe?tq~etW_W<nhtgE3]XOYxbcgJhEGjo[XRIX<L5tHJS14^!IgptR?#M:Mx=vju1Bji{or$W2Xb|$Zdt)^n^t3t|#X_0yILaj5<KpdE4d<ow8Zs4=6!$gTKCI$fDI({(y.7j{V}s&NjisLM}P12[JF.I$>%ZE>Zrlj(kx~y5iXP~)2hfsYCzX(ApL=gcaC$U4.*;+AP*ux7b.k~z[|suDU0Fx<f(xUOQVI@gv785i6"?w4}I5%O~bPWn]4|XuS/Iw:~_Vyd@rbD!WW!o={(Q1BYj@`OvBsla)B&FiH(>wTI1{Bn5Ht*[}F.|>AQfLr9{O,(r8BDLS)_v[XQDvY:w81<tGw`bpS`v[t?5?&uvnLfG0Gwk[Qx~B_b|:6/,pZHN4`4Ps#_ZY)4lK?<zS3(/SWhjJ$9^K0Aw3%u{X}l(t?eGsUgD+ro/Cb{M"Q[~3KCjBXV5xIdga``xrVPz(UF[xe2+|2/tcsnOWnkGKI4!K&Lct?azoOOM"~C1{Gj`bBc`k3km&HpN#thZi{KZ])|2MitsrLzH:2~eS7:K+B%GjdR^3T9_[4u?Iu:P#vGMcB3r68FUU9vz}|B3,&$=#hf6MQ1hYmE./y3Gj|K0Y]7b%X=~yI&,?J7URB?1Vn)t[Wh#Dn])%d}C[v;TN0,F<y^|:07/Lb7X"s7lA3@4?hdH|;W/,dL&^K_XSA,@.D?+edUVe>>k$EJ.&+/wI}"UJ`za?a,LKO90?%MEcs:h&Tku:02WrWQn~FkE%z+&r3[[nLRTdM#PRI,82"?Cx.{BD`?y}|[H!k_HUz$>/O9x_K"zDHgzXR?=wAn%!xCGKV25/]C1)/E9.|.pq>q_=u3r9]+NKML;>T>|LBLOnX1:XW!N:JpY[ZI[VLK(3oDGT*2OfWv1U[RA5Gi6v~]Lu>.?%Clo=u:vB/Mx481wW!osNUOq#+F0!<]K|4M}ULRt}~,{(NOeP+p$oQP$$DgE_[hG*=Wk5k{Z"w";`CxLX)(|yM,<<ds_*+9rcZv?"sLRRpz>rcy5j$W]xKy|K6TJ[FF0`Z1g]6js,<lx@NzK5?(TX|^)xa$s0[SQ}~5&f({$#WU)/aF~jB`{my2Lu(+2s5iy/<*Nm_F+H(1M=T@zFD.C};v}y!3U878s@54T;LR7&:3|1C/rlC+>rbqP1h^vG&QHmd,g/tLcP?@RY"s8W"}lejV3!ig:l^RX(I.olFqU;VO;3oGU1fGj$Rf?7L8rVGk^IPW|2B{&]pwn_0.>{D#>eW:m|&6(nZJ*ZdfgKGsJ~9{uHPHe^G6C~r!cdyTkmUV*?O]>hiENa_5+MtIO`M8s$6.Q>m8EAY:+`rx{~V[)/A)hL^./J?rb6XOWUO2}J,>G6|v]pi<rY/Qp<6DC9TMvDE1J5LK@4}vRZ_li&N>AqtyX@}IKe]7bbJc_3X~#oR`UqArgi<2t]Mp1vV*VAGa8b=v@G)=H4~1xDzvn}|0.]&M;W8Cxu<,MXezI^MGY2NgUth7:e2C|B4Y]3)mNH**2MTD.vE9m>xntRhNPvQvGxc#~nIM}?F"F?JA`BU?G:nA"bE8$gfTND}!TKt|.;m2oAtPBNY%yQkt)%@(izLENmxnRu6FTAAO!H"zXcyTJHaE6s#!CDH~lrHW3TvclON;,/I|XNt.L7FQAAAAA4EEA153G_kG)Z2Qc<vH"=Pk"bVbncf}Pt_~Q~HIOP0#<~T5yQc&e+hkWf"4F|HWMPluA2YB%/ZaU)%=oAu4oW{!n5~(i<up|l7NS}|w@WX:DK(VhT$,(we6x&h46E0sTuF|FFScW)%uIRnv!>$U.O`(a(XJzZtpaz3U0H1+<Eg{*CSg3D)uh+szg7WiO^2}0#M:&<V7G{q;6>`Wcp/PO7%kHl^GG3PC#m.pQzNPvj^?#S:m!Lnr*;>s163E:XgpBxp&BHZa)7#FX|Xm+UJV@9uLiOoYQAL64;GN<Re2:zH.LsY1ZPGfNLw6XG^TW/xOL1.ca9/l`u<0{4/^/x5K5$cJ}ZX4p0drF2WPwea)[[c~kb8>9hU%dr/{K_Id|@,;aaXi%a|:Cc3=[,>Lll~`8DRc<3},HHrVw?bT;lllR_(ymLB[EPHWg]z`[BW<BE@=}zd=tfv<6QglWygK<vY4!j^w;~|#JbB^[S#NkeG"!67!li_Oe!@7~n+=xf[LzTyRw(vq&"ZDu^_xRA1xNdo|2a3}Bp%pb`F4:qC<Na8FmFHB`6}y*}o}cEiHpH;y!}F"rTOpJe|qq1?<WWQ4A%G;;WxJ6cF{Y__GdO*~GBC|J.k+a|fX@eG9O$n.[DR&xl6p%Ts_}Oew(wtI#j,Bq&.0Xa|6*KXqSw>dXt6hKzL2}QvrjJjklJ8pa.uB*=K^Y!#Pt%&23x`20p`b6T>s7W*&B0^`idk%JZ:HC4?z6!~kJ;pfIm@]weJ&1RG&:dNl(a[W`t=c0xh<(Z*tKdBtgQ*?b(}9WDc4&)Dw9]a3>4ye)w@N2D%|(Xy[G@q/*cDZnsZhlDpvr6R}]pgwi&uK]]KOLqnB+S(SUnBx~7O*B/md)YkZ!.i4XPqxhi>!<|B+bFc{GZ<UAYykDzeKZ<$>R*HV*nB]_bB$ASq>?L.kJ=F[hF[2]:{Uq[q%CCcKdB9`;]$wr9L|)P"{G^:#tD3v|F`5O}>5S<xOBH>9c*%4hpVr_h_(N;h{GC(0&2v%BJ`^]CYBcGG#s5l~nQ~F%j?mvG>`4K,Snhs[[OZl(XSq|}Yx+)EwueU9;aJ~HZ{A>(VP}!^zgn@a|!V!K!nw;E&Z6uZ2$4jI9rL"<i_j~u)Ec+1LvZ>X<(<!$Mw!qgDqF^$1.iK*g{$wDN!u1VTPW2N8C81|&*E&D[$^&BQ2xYoj6|E_kIa~CW.^v<aI}JYwn.<,k#y8kDowW37mvC^)aE9I4Tni6FGeEXHmv/45p_u]0f.u=~/z3x9wxK(4^qVn/O=KZ3yy43x70vy{uCICoLU661vYo[9eRLv<Win"^`kjP,d2NtqM!MK59+#RB?I9WY(xK;+2ke?>dnuuhFgE!Cj)0$u,bcz6Vh9mb!=C5X{rN&"T^d;1cwT`m_tK(0Tf|tv0qM0"l6{,/Rwq}v.]@8MAHk(mpKX#x7"]=]#N?gp3YlnymgaWe4g?|0#9B|F(C]!?L~q="tt9)1%9Mwt:`zqZ+FO)2|)t>pg9Q?s0%3E,yCPEtB2,81iLq+i!sfQzHFr$N<DaP@=xMBP=qY4nW>z>Ka@~G,[m}L!Vw/+?z{?5R"_wtUs`ply23l+|[M1t<<z{&fsj/Y]]WXY1=K5!}fLECZ`^2Q[gr"C_6ySogd]kdvzey*LAVy&uI#:Bc!J,A8eFEU8"TzkK^}6]z5+;Q.AVd*{qsLWC>X}>r*$np`F_[VbrbzR;R)Cjo?@~&[{T~{_jK;3xLEB*fB0_&D+1ZAZ1Hk^/dCZCONRv$kV]<wtPL7]^3$9s~j$jELYq?GCZozLA(FJH:]>?/`V4K:$5^azt`^}K"C|v>EOpf>!7Vd{v.(9Eo+53yFJyh^z(+C!I)8Kmh&$]o^pEm"M+uf;Z1i>h>:Ppp?@v.5ZOVzgXfn*Y/j&K=peFRXp,Ae4ZhVA$`2M>W/P&`W7CTV&j;l"o>3"9WWLo?n[T6ZD<.L_6i[)VW^DDU>}&R#[]`&^YpA#`~/od_J`PdotU{".O]!~d=3}ivDT!4>}1IopYsq$es]2R4bei2|Gmi3PAup{:i^#@[r)C_28(wb,WghfD&DE8Q1Ut!"AZ{Ay`=mvd(xMS:H_0b>2DC&H29V_3Q/6]lI>}1H%qB_[Ze%>}!j_]PO#I}MX0+W~,[xh[{T:<93W[za}2}l[;7liNIj&Hcdyo4Ov646%nFZ}a)Qt$^Kp=`C[09o4&ts=|`VGMDufM]=m4xG~"(^n/v.G$N4EW3J)b~jvR?cw]d8NYsB?*u[sQMYK~[/C6]~+UBW0mV)T0AF)|PoI2_0D7A]B2Yd&!<Ss"nJ1sE.g#rE0e`]00*!%"~ZeOj^PiaQ[3Q7rz+Sr)FhTD9LMFOYqcM)Bom"Gx^gZk$OzFvjH{yL;.twCyH7wIJ+qec~Lg?3qSn:s{gb<kxHEFf/e1x=9>rHY)WwzR|#*%Bz2s[n/_?O?Yv$q!,9b2W[f#|Qoz0Z+?[jsM~{~C%CfXBAl7`VzKRJ|IYfV0v?)bP#7ki/8!>6eLB{`MWOZeNRRq2!@]C2^TRh$9;`lI9yy7";g#_b1)f"F6_,//e`G~<)sKW/yA46YiZ@|x[KY&v5T:q&K?;*t0.A&)XM<Cq$gTY:EO|^L4iA51dVJ9oL(.uUyDWG8?Ag4gSfNO|`^^0CLxyLqwD0`iGM_z?,V??Xln%D<r@gTLCyV)Bz9x?`*7mXC4*a_7cD7qNXnCsm;6+RP5d[S1C@30&b$HI1Iq1@Ve456]3B/m&d2&&qPkBR9>XT~5q;>(A/Di*6+A5=gk=?Up_pUE[Awd6NqzUs/k?)V0Xs>$["X!x(<:2CD}h,|]ng#:s1Vb{^+l!gDh5`wmUrtfCNg^@t+t>Y{I*!;=N}cK{zv=]ir}}+6{s>agwxV#.TDv^yn+%X*LWu~_EnPrt{_f%T|3M1HYPQOWyG[>h8(+8_!*Vv}Y[[;Mj?=lXxx1hlHcp2a$@*h|nfQS;k(az".(=}l@Yeio,h^E?DU``YM*}_jQGx&<k4&>b4EH>+~%%w7;IPQ]>L7@qoh&Z0V]Q>od_Vm)o*06$<_|Oc{H~Q!?Xr`*<7Mi%NTA0].`9cW/`U}Q9>t?3y7GZTN@`ea<k:K@sSd%0VNbtxlmy&rJEazr>c+Y]22ZI1J5:axyrTVDG=9)clpzf.lQf;ff_EL!_s(jRM!pg3LUR|Bn!~r{p7(2n>fmeuR<[B_,{m5xkjOhf(yh4JWC?uE8>wn41G4`0=z?p)ap~(e2=1yQLP&e}!*(VZ&TIUVA{ull!Mu|oGF~*iX&v2Us>8gT?}yu=~u*|*=?3oq(5VFaj?Gr=8^B"%jN&awQ&OZ8Z#Jh*IR~@&ps7/_/dQ{},a?AHooREMoHX&L%%%vm;1.D>9+ry+~a>Yz@=nGeThNB{~#AS7Mn~{ugxX}Rn+j~Z`H(WAh3Xrq()>!_ifD1hqZLa)d+.D3:ae!It%1*^1EyUG:dDAk5E~o03kF.#iXiV(2[YIUsVOv#srCHWw?l9:BDL"h/e,=7*5nW1rU6y4kn1``7)<@3|Hp"wd]"!vNGCPUCq,VL1>]9|G28Ncoi5`x%V+dhK0_Yeqe@%qN^6JZwh6<SUErYS65#/dY=w,FGjiqCg2_ZP%P=3W|B@{])IK$wGjM)Qr#"j_lgz6Y]cw8aY_U_9i0;nu)<0.<m?e^"rZJ)92dV.hib7Rw]RJB11Q8cM9EK*%i+jduPdiM/Znvcsk2U4$X+vR{=!y,w[WgI0pSg@gqA^}YSz,4VD+7v3rn+v:c<xD_={[Mc#9p_JI@xVk#c7`$fMo=P:K[Fapqasq~KtA=AqVL]d:,,~6%>|j1h{>%o}v+7*s738CY).O,Mlhqm$e6d}_)~KC4<?M"4v^+1;<4Xieq5_5k0LPX$Q"q~o:{f)3Kr`S&}LyP>D2WUS$&E!^i*/V8I!yTy{I[Gb5jc6,H;<i?XY&o/:d^LJ2rD&"3&G9KnhDP;~?+d5Hf`_]pL8Ce$`iq2Q"`#NwSIW5}M@*`)Ia!.!_WZYu^yKJ;Sro):5,awI[G%UccS+Km9Qns"`4DOB@y*/pe(M_<EsKWC3M4PZMD.|p[)K`ek|q*+k?J(9yq_?kW80,iMv*onId^@WV8&apMeU@edcikZ/^$fFNJ3z5{U{%]syMj>50ycU$b8IpFIElJ]#I6U`F.44REfi(=JhSY+N(SzLCl&YO@TI8{:QWK*M3vw2_|Ody<{BF"6ty6l#<h?QY;M;W)nc8Cwoy@AT;?T*S]8&6iES?4&t{L{y^^0U,$G+`G~<^Y&k8`za?sq26zwJ^X&Z3ZC)ev2b~`y4NImF~mi#y}cxfl*qIs7pl=O>,a_Ptg|Yn*ir+S]dZVVT$X;(c;R>1@f[Wc1:594;CZa|ifmKW/+~&SB9cr99DF41/j<Po"MHa..(j9ezS|GILN)m;8+U6yxZYG@T5wc,FptZq)GR}F`ZnBMy68$Bw`&gs}]CprHnuz6|<<2:mT;?zB(%r2eaMI|aag(JcF|m>3(:Mee?&BN`6@q%<d{Omx7dLo<PB:u{Avk$%FqT{H?ZEUY@juQe!<XX/V"]/FL*._I9Fkk5Vd%xt6r]:oz]`k{*v]+XgSgm~ozUY|,i;w2^2Zc`4v^t[R/n+L2?@Rg$cu.1Wj@~0H>y8$R`>e55y`00JS<SDWn;b]BcRdpjP(Fxy3}wm,HT?wp+P2%4wrk*k6u)u5>*ViQ|&qPJvfq}!"PT/0^7pbV2%f}$d]m=PP$*sJxtGv(y+=Qi(7&!pU/w|!`t,}D.GHu1x{iK:%"n)44l_iLF~qB<N(3y%*oVdfakB@uXF^;]GVeC3Y1i(L"A4qzf}n$UIXX_o_~B]K"U8vR2KOI7!^O}i?ecj~*E,`jwz>>xN$$6mI1bw&P_MypUiIE6vUQSskNqYV?nyx2c&^!.]UkRx#1_?I{Qc^3dyFZeW9t8Zz&id}Eno,uxr(wO?4[nVXzNbdh^3<~ILHLP(Jo?w]j#+n3<b8<(*OpVJ=uJ~u7!:0VJsWF]?iNfji/&;,MFr)Lt#S|`1?k._HUOKCrxRr{!+P}#3wVFdhDfVEGxikpR9t>*t5chC0jcQOIY8lPGOF;7GBuL,7D>Jv,0okHV*FB#HpR5^*=J<tXtoZ|"Y{L4j*r#:K}Q=2${7;RR)3@Fn`Ien/]v/LF[{mUuWC#@!Dc_x,&{5Y7(mS]{quoq9.:v|O;$Usg,i{:nEJD~6b(*=ed?Ap9`;^z?.O*%j^JUE.>T0qd}IMdZ1hnWNlQ13RyQ^~`cvnwU9bEUKxa}N8NR;P3lo<qCXx53NDX*{V5+v(Fu(9/1[iLWtjmR0t<eh9dCKL4,HF?g"ldL3p64DLPsM=KkP/artV"P5o}B5h4YeXKp+N~<cM>!^c7vsSvxR6{#Zuxg>1EqXxrxeBwKY+U|hHU3GHk*dnSBc:+m@2LLV+%vlK(cO_o.c<eZ+&Ubjv`nULAh%%htx6Nz<#;p!#{<T/Zfr;IUjcXYX{B9N2U&+6l]eGpUd{j?2#uSeH}">kjX[4b#HC6|s(wxhbNsFBGgt8j9[NNA~O/5Mq7C/JxM?%{[$N75C3V$u1XOO^DYJON~t:9s4>f16u@kkGo/>NDr!N3/oD3#ysCKpQ}UfZ?#/c%URO^QOhve58.DtN_1W`)!UyiF`;KvzlLYr+wGN:^%Sq)RoyF}[(rka~3J6_HDwFJHtkZfn2N<8.c9#tW|(MZAL?OSR^AT%nAMai/"nF_g!1p,F6sz4Gx*aW2,74EhE{x!rk5FX`ttIWCh.ysLwU#U/1$aj<m47?4vy|MC/uw9T*2ia,5i4F/MlK&WRGTB,vz8Np)@$TB.a<koG2J/(:?k:ZmF&EUDOvTL+#_iX?zXRnX$4VEW$KjYL}8?h[9{B?&Cocc5G>fu*.42IqCDkLf8p2A42xFoVhEj61(o/*GjX)t"n9v9VDSau5qH!JTG026"]vW]!F#`t7Q&guI[Q0tZg{raGjrOXUA:![uIH#JjM}aCMLR1[VwS?I]dM[DN">g~D[Gm+<9!(Nvvk#G4m|qSik^IG1*aD7t@(V&6=?tDnzQWx)=pOW~$nzwnGJ~F7?8/*PZ(~z5Q2>/0PsJ_2s+i[](e[uoo1tFCN_J%W{8HdfQ[D8M53![8O^>8{R~)T%=.StP6fUS(wo>!c0L$}Rg9<le"nw%:eSEf3`#f7i,0ic(Ct0r,lr%+&}Dw5B!9US:gKa6+[&nYH6=T)63Oy9O@)4rRBUXy}4o[xg+Qo_G*y?_^_%:({osN/pSvQa~b_]`/Cr1sV/`F0/}o9,~Q@nC~[rE5lno%0+ucsl1KVk(aRx7{3,}JFK&4`PaOys~%t1m}B;`EGrTj[&seS&,2(*3gtoLI6V<"NU_oeaFGX=fsCatH][uJOq,T2A6Oa%M,Dy:NFLS=CFfDV"mFT{4Y=.mkAcfJtO#}$[o_)uMMfYu5E(7+sQ~I!XaEm$XmLIj,4Njm>07Q<K|)_d7Dpg+ZJ9R<("nmr=gGq/Cg)k%J|e:/qrr|H.>hlq&/(Bt0RT{lrhKmGwUK]WQ#t];Q@{B:tPyi|dB~?;vH.}Nv#mC7|A>,@9MB7spq^sb2mS_X<q(}("S)>cJC@,i}pi`J&]K>;PKy"+VU~;el2h,i/`S{>|YAVC"cRrP:dyZnrVw(#>&S)u_=`>Nit7k0_.ZeT*)S[|f&,YLiI&O"C3E$]F@Myv]h:&;+I+V<usB:CbiUG(7nb,clM:P{R?!RW{>G~5d&PNF?rtBh,s1<W]Kd/2nLpx9Iv1/$6guFD()].>;f4[!hL<ZKd^JZ[I10Kl=M:K*o^eBeI>/0)F2ulk0}m;3<mDh.:D(TB+nVTF_J1|^6!)<<<6LCnE.To<UQjqk^V`x[hD**rdq=k/M$@=^qfT#x}@zPU.[OVm0x[/}m`RM*3|Df/1?*$`!nTegGzm.J}"UWn`P~)1u4^n}Wetdt:_Nu{)td9Lfe:J02d(*SL>%u/s+.uqHlj.D@!_mkq:7@1XLLk&%`[2~&Z5{lOp<K#(j4F|0DO.f^~Rl$Dp486&3tj4a3%LRcfH.LBB(Fz2$xy,/[T;gx?#jEc~WfHTrG2G]A@k&ld}|g4)OziUM)gtde#r>}gV^PIvQOI"MgXE:oZNI~6_he%OoUn=QD2j;GZ|@wnP>%xS&,+M}4fzq)t+`tD&<&FsqEZ[?JX`yU<[;gyHCpaZh~2L~fe4&T?r6q3<0r,q!3_aD}ZSz}d|Ww0ZIvL9bL5ko?G#y:%OG@z%HrjI?%ZkMj@edTNGaKTW$YR_f/#dA0)EGU`$j)8&FMf#89YK5"g$wZbIRfS|WO93P{MqZ[Zx1fjwO]+_UIR1]J5{&Uo4O5X`hsjH;aV"UZ3pLhb#4WHGQ;1mqiEM;5@`tic%XUI9TP{#[5(nN#4lZU_)iEZ@xhx<w.tdwQQLaXEPW?8fxYDg+RBZI=y]@cv3,gN..M*mwV??~On:W%sMS}c3rhZnn}Bz|Pll[$s^_<EqcID4{sn4qL@%hk_bp8@HK&z`|YXj=k;Q@NIMF4wym~`I:^p/QtQZzEXBG$mm&y;lSWO`:h*zM8>aBxHddtw%<"8Jc_9Y=ynhFC!*l@]>Kqf=Fo,vnVD[w7F=P@g[2nj)nrRU:|1H|ai$,oC!L,rTv_3NF6q1vd0mFil{+M2um4C1_i7"(#M])}vTO{GFL<At;X(neFli?vfs[][q%?4:n[z{5Nca%Z%*o%E*jcThB0HF|q1>jo~2,F(OLu)]nT8;[ne{Vutvbi;1,P.Qg#@#Gzhy`+h,,)ZD.]{qiT?(H&Jg9Nj,vseM|<o>.Z9]1VE}5$2&CiUM%I7KMUHFQ7`OkgZc+KlZrYXt=R5@$LowJ?ka7UK8gN3ibAJpA*Z2%HZ!muQB`UN>wYi^,l<%=*EZTZn$0MVgS7lyr,;ZTkzpIiU^,"2+mw5=QZ5]~gkl$^fPQk=<5"bD~2{1iDr5~N_u/vb_kL89m1Gy_ylplg:o]Q_K1=s>1%Ga_;,F>&e~SrS^T1:exE1:V<RuY}KK:^5)c$^j{E6/[{X)b.8.=vZ?9w>h21$q<yTSW>{X{q&#J!&D#J0&c1:|!06_zb0iT?iE=/dTv@l4FV1ZDH57_Ct48C:6z`(P9i)B7B!A*[g@I@]F)z@&P5AGQAM9No36N[ms[fPr=w7<b*S@jq+vNqv>|(#]qceP4C>?xb.ve!mFlY9ucqLav@,]U@+&REC?c&5b|_6+,fj_ic&IM0/9}K%p^x(ozRB>;{fE,|CE0aN^RPb`#D0~P|)TW`?nO*OHy!%oShjVtJK].a"z<VL>42)ih=_kn]/N?FN_GZsll{!l;%w/oLxS>P[6[7~liT|R[E_FatM01"umlTL?32Ep[/C%"KTNq*VhaB[7r]$;BzBGJ`hnN0<sahiHI8@*W0{u7Q<x&/xyYLFRFv[&$|VLH%MVc#(2&v]Qd*b}s{!DG)l3f=>3a7>*{s"=UJ3JIK%DXpL~pBUTOpZF&1{{1o.}t,%2}2K:_v~rbhP@o1Je}z4)5X|iW*l!bE5C(fbI5I)_]:Y~z#tyX7C/W@K%/W0rtje?eA09v)c@r~6506&|C1ZO^)@UJBcE%=x9oH|qPL":qa%]l@9cLAWp]4?z)d>7pv:@m`HZX!*s2<_d:{~?%8M>Fa%lNVb,8|*ZBSIlQxvdEdT~vvq,9pm)?1?%{I8b,CRvfn`4v/gkfXnBi=HR@PmEtMt@B$fyi,M=Xo0&|IXPs6L]bQ|&UUB<@I5Xj8/y[=Pe?^arE%1$fj8WGSTn<@Sr4+Kcl=99O6,N$!Jxn{?w#.9pXtW86CpQtOkX,X$,TcRd!<9oX+.w~@nK}0%gVLm+#o+W[+ERb+v@^2m[&S`m1y0kgEHpBs0SNMQ|RsYRNP<}(Kj";|eN&d=E,IWp<Q#7II_2*YAO[#y%G{a9"pn{Gl99ox4o>YXIO4+u;?v;[K}&,%Y/]g)C7+{Gq5<TaBMz%8Y#89$v]9W=Q.>wd<_d!&~,SD4Z(SyzlaB6%]7{n*Bh/PG9A:[<TO[{M+1V7##h.Wbj91n"oaV*ZGjrz6cK&V=?2B4`xB)a/~I|WivVSFvm`_o7R2Y]vc7gQhQ&=L!!/oXgGyx:xi9.j_ogB#Y!b6xLy$@gx,F&PZdiofQ3Hwvt~1TD#WG(>{6=I{!E9OsbYEB623d},D?A3u$HMA>ovYmU[e,N&<!;j&oOF%0YVE8_^:Yf_]JwR=VB,&s.tTishKKO]R^Qhxr";;M077RF&jtd/&2!2g5zPgT^g[L7<g0gqq|<(A`M%<|./MY:VgOU{n3ra`d8DV{uJ$Vyn(KC"Y4N1dK1B;HtcpFirn;<cR&,zr#JF.ECao[b3kvVp+.NI7PArc[H>s.hS9ik]M{ssc}e`F:92ETxEjGO@0RglFU%oLBGgSImi_Ev$`n8j.i~uB?8V0h7|OEUaholx=Sh+r?w^hMRO00cKL3R{L&61`dBjojk?+[yGz[kX^qr%.92w>hldAoYma*JEsVqr)SpF"HDH+DXH!a0<5ukCcgM@tDp*f;p++o^izw8KyA*|eGH/5KtIx6e[^HS/3GP]{;Gv3LjJIiB.cohbchLk{T;F[[tR~GK@Lm)#0[M|<|`*s}*`@u}+4zB:O*{i,)6w)A&Nx@Kc"[xHMd8?XkE%4jFq1#T!nj^j9V>ZV1}FuaQO%[jt8e9]nWhxt?VlaR+/u?!gFK~4/3%4jywDq9$XPz!IoC"5Vi;aIKz)@]tfKa0&/_;}p:6B0cr&AY$wM5|Jr+Pv;Yv/(zg~cQ%naC|;:RzR/Mu*d53/qySEQ$;|M|2_wSZLOdRAMnJ,>>5d>Od!Y&=cnFO)VQZV.S?Dy<l]Yh6D1;J_yG36&E<52`tNz_$9CJ+rI&$u,[a&Qc5r5=gl|KS<q=Pv)J;k9@jtz6H+3<l$fz2S]2@0}ig17%PLOFi6lN"2ftojr$f`f/D8D%~^;h4t]PFT7gr_8q3r#FrHq{3vE_Az;~J=,a#sI2,wJ3/vmyd])5^OulfUZ8pZj[7uxy1O`Oz6~|F92&HeP2Tc~]4LSw.Zy0jY>m[:w1S&B|6EG<Y]+imcRpu@Isn[&M_#{EjH(#o("|%_PhzMkatQGC.jzcDcam3pDqv@LR!1Ljj6KVx?T/rx.J)k)xfUnTx/Euc3N:IHODu3G"44JD6MkbZh*<T|zA#UU0eYb^F[KM6g>Sdbf)`4)Qm5XMuO?T|80v+=7JSr7TTB5*@no)?$=*%L#n)PbW<2gh5&+As_J/uu*s|VHId?@#fq_!AP0{vL^62;yl!`9/"R})bm3)no^lm_Jhq49>sQ"l+jx!p&"J5||~*U]!@TPzIWSvyDMjt0s[Lx?au6&|f#DP9<Wf,e(YmG:_Qt[U}h}FX,B+)``jO"BS+VETLpaeQl^Jv7(0Liq+c0X(*wh[X0I+%qGo5c~),snsG0NvH}e2o&6kup"c}.|d~J4l%?:vO]$?axSH7G|KSPwlJE(dn0H{m8NZX8U|F*Q~Nk]a8I#"#`zbt?u3AeO(AMdg3Z^EMZV45k.Um>qyAc=DY#6lI]xt)mmuu*A(H?A=:)~+hIjIu/l$y[O$]cQ6K.4?66<!_l}Mhq=.5`VHthi`WkT]RR41#I7(<BuLf_:W}c?Y..Ku[#)tLMHo4yuw21L+h>v(s~X@`1||]6[R]G`~HL2(.B|NJ=EdM|PV%Bxb>E899ry4M}|+$c%NV1YYgaYSY8]$HA#65_5$UBBd/u3scT/OHx^u@Nx?EZ,[Z,P8?C(mM60$!EvZn|z`&jZDwT)NelECt:!w|[8Vb+w@pNp&HXYTnzT.E7o_E[(AsHp|i7ZaG?QyRIHB9,M@b`PP{C<,vtVi9EVX0A7%.!`tcXM"|xZWiBA+)l%{@*jfiMb$?+(^E.;T9oSqH3y./7q]%X{]h6&_e(c2VzwbX<j^OhXL8/WU2.k`pY{!IawQO.l4EvuXprG_7;O=L_BNj9*zqFO1`UXLL_O2Jk;t2K`VX{g.Frqe8@gJ:RSLYx@fQn)Ew#gazoyjrQ"YtqM]2<OHq*D1;y9o3QD9&"2K?eT0b{q/mrd?L^ru_&cCFEftC&CZ%FB<(<;LCTV!=<l)L<>4sS*v2uXv&G0YV72Vt]@0lYc3ENE%:A^^p+y2i8l;L~)>2O@+4j{tW(bh$/9j%ROoRd<t13"/lDdUu:|fXzFBQ.<y;26.CL{KkjgD)>a[QZ2&++^OlGyzG_u<[ExD0XWzBV6QNa/~L|!hW_`xPt6J"^_?~gS<TRfFnqK3X=n>*$62%s:u*wNwUVleee"S*0g):QIW}RUzVx<~<oup)M9V3Ma!kavh$UKM8{3c}v7Fo6q`bJ~i[Y2u2EC3u_+"0=qs)]ncjP_<4L:Fwq5FM7J}Jd?gC][jaLa5;b5P0eI!E&$/Gi*x::vqUcpttr(9p;",`0EB91)7}oA5rV_9mlft`M%Gt+$kXDD|_;404|2E8_2m0(U6VugLsa(s{JmrjZe(OvrUZRS]uSnC|*LAId*G7H7e=D!Z#m.<fI0&OJ%O8xB$5!;7kqpvE|"Otd%ndwU7d_6q/SL^gM?rI1^gFx31goRWw{$$]leW,KYKHeNt[qk3O|L^ASv7Q(7nCz0jYBc,jtz|1vpsMkIZBHu5&*Db#6F8Zm^P@@IKeK*GPx=.MQvqoFsa$ygw}Hw!c4hL6(QYAlLjgNhac(0>P|>639W1aj+MYm+?8nZtBFhKg7pO|4[vz:4,JJ8Mi+>7?yoe:EhVuj:Nou:=rGmiLz:9.0Se}BS*:SQ34.=dG5f:CrLI57aQ]/O}[%7.!??Jl9/(=d#Y,L01PDX[+prDLu8{5""L3G0E;$F(,XkP"u<sLD7wQ2lS0Z3vO&4?O+Z_|4DPAd)Q.h$%jX~^w_|0onNL{LQ5{4W=8{9#>db%iwQQExULCDM#L$L/RwSW/g_ZUwDwv`Sgu:bu2@z(oKR.9Z:ydRm+V~4"OV=uPhw(%z$2Ep"cky)oYWNbxz/b"UhU44QD%#b9b!/rjJ}szl=`HBf_dfL6!P!:G!|.f.kXw@"9}}4!NVO@XP|#Q!_}32%iKor::$m:;?^{F:FiD`b)Wg}EmfY7mT?SL:lbb`j|<H#FzJDL+6YfTGiXW^q#k`((OCoZ^,?1$#eGM>b6*g)i;%xWO+[bC!=Ef?Fih[1ZQFUTi6sQbMc3k);>{4qkAfC}gSYQD{lOxn5~T_u2u[T8(e0&SQY=K$|BXFWqEQhp/>OB>)fSdzuqd}4]fdeq}(Py$)3]un[Mfb"E6MagNf`/li8d~,e~i$$ca]LO>$*z&&dqe!949?;%xBpYzczIXrrn2O/>,N*rX.@FS{^@cp*$TNP{vpNFHG?F<Q+002p_l6Ye{zU]RsU;ud(Cv4ldgiVq4!p@}V.R#t4!V(I6sg=1j+&[%Pt#:7(~H`9luGWBh9<Ge(>U.O`Wc7w|vnrnq.wBx~hz^{v_8[:?1O;tJ^.QE^I(AG`@h.n:cckz4rz%J2_luP}~0a>Ufe;n)q2MbTB6Oh6YWqP51{`"V]t)}<WHQ8/87k+IMqO3X)%o{K/9}D$=):Z`8cI2+32?=4I$HhwT<(D5e&]h~NX4[9qKYSKG**}([74zZ1rjj+NMaPb_+UiW/G@0Gj6pIb$bdEiYWRW5`Rj.>Y7U>cu}0*~Qa*9~>m!U:1"I5Zv<_I*#*|R?r5U.Qxl4Q3H:l&]d%?x[[O7)0S[+x{TE[.j``y%[+5H19)bo+vobQ*EZT5{]c8gBZ1o@PQmaxY%MrLMPAER9N&ru3K:Ie9RcLm:xdsM50zgiy:KtrX31^(wqBrpcV7v.8%*7)D(NgjMF;UxkGK~<w00=F!o%}QH4wgxPQh/2DkCI*cHD>2@zAR;>@u63oJ6AhO/CwkSc/#e4Tg"X]lDvrF1NXm+xY*{kuHY2hynXwebos?b9M7(bu?,+cF?vL:BQPlcHCoM$9!|59{sdGULPwj|OS0"`W%jDY&qa0K"qIplOJ/"mW|J@S+TjBgkdWJN%{Tqo5=_zG?IdN7@xYDO:Hj)2>J(F*7y&+yG%:#!|(="QBlv.1OW,7rw02L|Zw0;$jTdK$R1z/4]+sE1yo5@8q3ry+!BZOI=A2AU><4Z5=#6u*$i;_s<7!E`Aw5e:`s^5{4uey7:I;FjCL~B_zn$lB5dYw#@o|kq$9:82}pDgH~G%Po[CkrSkH2C`zp&Iys1xpM{4!LDj!6xG[d=LsaYHkA:4!F591j9x)uYY[|n;/#m,.+!1uUa8o{0&gO7DZ!|`o$wB[lb9.xeIfe=kwQO5fQ.=Cr6q&)(%QUZv|aNEm8Gani_"176Ra)rOAhzuq1`<pQ"zuZ&k!;!%^:7j5s8fxMf[Fkhvc*xihDqj5oRt_=$G|sc`W4Z;|.*7zOR(v7JiP2e"bm4+N_`N<DFw}/a(kU7F/(&$eQGO.nYl,A6|6eDay)QU1|}[37g/r?w"v7d&<%((wnC8KH07<lKvdKtBov"`qC/&cEpcE6?FOb(W=rzt[4#X!oDa,fzFR?$9f$C+,CeV&{xY7?)wmEB)p+D5"hwFfFky_6v}@jJt/`WJW!^m5wKD_XHwJ1"a7bBoY{a]oZ^#C{)a~Au=4]Y|N>nD*PoS/BeBbP{#i81cU1Dkt[}C$n|^EKo5cE5N7aj$kuWhSq=DbwD8&;__qmjZaE]]1$r,!CH9]mCRdv^bQ_vzelIoSfowzHyGKM~DKG=~.cGur{CF(+FqK.;/p.s5U+.]yfR`991u)]#]B!@Yiu!7lz*XRuR51MD^M(DgcV&m5oo4lD0n<;SL9jX|aKfRYwG{Y=G$/uXIAM[p/J8zDC3#@_Cu(b~*zcY94^rjEb6aU/1&ad!FUR$BqM+ftFfB`7[n,*~(K,=Q>ej2Ky?2`?Yh<@bnb,JwpxQuT6<37y/Nwzen$k$:@a{uk/(N;%y8y;5IShx}IfNFi(ZTp29FsoB,zE!tG&Td@D4%|+})EoS{&DWVk}SROH`0<a_2/v?}xpVn:$=[BM)z$Q<(kW:w<(3uDEgLmh^;:Ey[)`xvH8[Nu_!%VMdLSZJ&FmhT%<zt3]y7N]Z]Jp?Xs0@%%*$6#{26&}yx|6?BSGNqH$?yrP~>s)U/*A(a$mRXQofZ;#gb0A>yePabKAMK42/j]FX7G?(RmP,lYDGe!:OY6UZ?kG:Ow)QRbgh1V:dSsu!=WS2WZ)R1dFPvR<iX#g,!icN[K>E=6Ba/Ti_U*,}L[){YwoF<R$D(RoFh@R}1yumz*=4z22EmL49<8$x7)vi9#".tEJU6cCQUL*MR;bjb@,s~7A/,Vv{w{Bo2n*y>S?E.[=Yt<</80[i3@Q6zEOEH1}aBw#a$O;#f[MtX<=Uq)`%E6)]/=Gg8[~n4mh^e9f|Y.N7v21lV<$8wpgz+|&nWJ].mSW?<Qf,5b&,K^C#piYFRuM/]$t(T]938{9;Plj0/1Mtp;WL{r4cXpu)mdv]qi;6{`7^c`Mtt7LzW(SE6_E<j]`$xfbkdQ!=mGp>Mq+k/a<w`/IdO;y,AeViRl`q{DK||1.$J7rjvAtXp&S$8{!a_^TyZn]x8Bt50Vpm@"t<$)6`jye:ptbKFldR7KZ"s:ZDw,e#t/m{7}([5,YYd5!zQb`k$NOJvA`S>ETnYIGn;Ux)*JqbIrtH[rW/8FUp0#Xl&=UT_fN4#;+cFqjgbg[cYLaQv"3yqBNT+/gtUo!^d?/}><vmg&TztLFimRIN4((!l;IzT"cnD{msxKL*m%uk,Z<WE&7B4axzYY0UZfJ3jrwl+"|]6Q:wSZqHvU]7^G%3XT5hRz!ph+O[:Y,Q<91=s&[,>h2$(z<4^x(ENr0Nb,#QAkJ5NIb{P7Ny!8a8}J4pn@yv!0!5%>Cz?#.V40b#9<B0SBU&WCi~0z#znJu9ESzNE11SVd@tbmlZzIpzpj_g:pKq,x}pHjyz`(ff7mpI3mX+}%&vPh>g.}s.W=RfStLC(*=}RJ?6S4%cm25$h?xz&?zg;7kxjv_[%J5~HAC<a$wbG}I(B^95r&8pn^6KgAcawLlv6CN"39_DFgotyaXMfOW5Hd?O|9gCp|g1Uw/<oUq@y$4K`_!ltM:#n*/bu:uc66HG(Di9FR~l}~8*PkBktgv$s;55xzmgczFpw~NQzt0;M^1F#PSEQYkX*3*e3<Zb305ZiN+6OKSh!9QJtq@[2}Gi#+bDF!L4CX;:!%sBD@b2_NI?)dN{S/,((UihNH<XDa[0XEVwqQtNoW)@kmY_eLJoR`~BgV$o"4&]WFwjjoXngDm<GsU.<pPRyoc[1XX$l$0;<pHX7{s*cJAG=uP4OsZp@(;V#q_`Mr)~Vk%1U8*5X!ZEG7quo#?9X~Y3y>5Mx9kjb}{{p[k*~cPd<1$Pb$@#+h`k)]/LF+9QW#tDi<G/H9ybTs[%q/T<<FEg8q%8W<[2MFoM+683_SY6OBKXqS&9P)XxNT4#/"2;x]1%6V&U2[Qsa0r1(56HjC|ytDt*+VuO{D!&(y#6UV{b?0L?P;uK8x+{Q`_4|k10F^M;z.AbY6V#NB{(WrL|=a4~=aQ!5.6qu#l}?D&("/oKa[b0GnaN?9EX5|S?N`P%Z;kYQO=+6ab,6{9{3bK$eIukDP9&n%].fjLZ;8uDz8M^kOPQ&r%#.;Kp7qb)iKWK%j=x4!ud:qJker(/9sRvGQ[1s]}J1_bBB7LHJj&dl$aAg**A`6^"Z~R}yh19N[MY!%0FJv^Q2;0<Vk*!i>):DhT[$5uh01}l~nj]0@k$|{nv@L}Yq2o_scy:WX$d?/51@d}U.`$qZN7MAag,Mk4_L@,R4[|*JLViP+Fk)sf9),GEP65IT"t*;i*UTJOX0ebyGU(Go2A8PM2~+F%1Ek=1~)9`HcJDLE<<p~N?Tz%C9;c!BUu6(i7c~ZjowR9fY`/q.k"~Xh4Kj$OabD!VHzh{04z`sI_HNh@E,P?Tryu8JCFCGYSEjZP5Xi^1kShL@2D;suX7YgNxP{yn@^>e)xU;/Gew*%~6#*r,kx"vIiiU$Adla+IBF9T6{O5"`!oVB)x;z^bpf;?](*!S7r}KzA]jW;uERZw%r9f0MZd,NS@!5jUG)CdMx!|DA!BF:}wZP9Vznul.bv_EKkvwsBBlWQ*h__o{$3>4JpaChF`DyG]g5K5}@#qXx[bK3gYs8=e//9gz.4|QQLnhSpjg|wC^J"v8x/30JgCwv3A9M_K<]HXO?o,b#hBIY!I#zyz[,*^Y6_!|&b9zzl.%*2#&Pp.&9NQ$rBF{!5G_5X#(4p6.id+`yyPS$=N?c18KB+,6kk"dK,B&:EXxgzw}u}>ZmKUZLz^73YCuE/:;A`,.5k=9=SE;HL@7!$<>p^*][=CHHYfjm?}b&|{s^ST:pT]Dg@eg5z/#*jp2o4gw[lhbcLMpq>pju?EDx=N>;wgqo)0Qw#Qv&YmGOVRgJ}r$kNUv<[J|Cr]~Ce("fR=*ong+"TpS1g0MvuIXm~[$TBBu!bczO#C%4m^)~*+#UtJsu#$SBKB}b}jRH6/M?^4V|@$|GZn.R}]gjM>tTF$s~Y%}6E7lekNSf5l~8R|c`[f%sH!tvo^Ugh?;p``:9}~K!jJ=6QUU5Z]>W0B6^;jFw8RN1gcaAIe~$)/4CzEnf2Pv5%gt=b2}hoFEQ~Wr$g0{}N`qWwB"hnQZ0PEk1uM@_Riq?kvp/KM4aMN<q`R]2TM!%tv$?%w^_MNr4wD)|xR)L<;8iNiP;ExyL3hc>4Ri#,LT&a3ZCAI>`~ZynmMyP8x5c)^LIEgI*43HvyzzLE,T4r728d|O8m.k,I?/:|j)PEl>%rQ([:PlSeR%0u5Yh3O<;V6`)MB,m7/5awRJl%jIqZ89y0:N7hOd:1`!bC6&vfJPF{Bx5u6eOt3,?F|$4*IPKwtY1t~5uWnqx#&UxYeb,lv3FGTX6RZ]30vd>G+PVRQvd]Ug:E~fgg_r5oN|u3?`GL!~`Drh1y_Lrr9fXRD+:<tNHD;h1=r<!B|V+ZmsNA7@+GZ(G$_6>fg_CIY(<IN=M(/.I["N1ZD<6dc|#6yDU:}5Ambh%pE@)0uHi+811=R1lWQGU7ei1`&^~x")T9ePF~^>?(h`?}m<72iu91NkU{V1g%36EJp8f+ITt<`U3W)2Z7nlk]wLE4:LBq2Xk.P6}^{es,mfVwwuw~3FiQ<(P?%duI{m%MTWKHIsJOhayFFWjsC|X}pq0).N+JmqXg.!!5)ZN}93ryJk_@,KJ)r;&z*GHcTiOxIuIUQ55ga8H`1u9eaKT.:$viig{d+w:Ai,3MP1Iar3Zs+lyFG<w&|$j?~Ys:IU`bJ~@!~J0RkezkvohiC{Bo3h(O*#+!mUL&5fxg#0p$+V]PUUhlmFcAQf0dSw{vHli`dDK98Mk,a?]XW[vlHH6Lb3#JJxb_K:v8pXII}$J="^8u!Kg5h8?v_G0.=|n*d$dMzO8+k@%c#WV6wIz@X1500c?^C6CF>=;*IA%Xe{`6~VOKl;W?o&o6KoE2)]b,FNByu+EOV.!;UD&4QliPr<k54cLYYtS^B8?vlw$04cB7@nGLB8Rr&,Zit=tcV777<A!FR.o+_X2?E6Fc`_|L#=P&U|sO&hBFSCkcUNpFp@EFJz@/jdF4Z1<en45oO=%:N<%aZ){W;mwvV|49Rf?(N5h5DeBbVyD9SyW[E`I/)|ay=Z4&9N/q~=`Ua)f!z$=8?$("x^^@8zgJb}wW,OcX=7wxH!D@AGi0S47PHM8/boR@"iIE_*eW}1c&uCRjz_jYkuW&Z+{lq7]fNMCkle#yhz9@VQDMdZYRkfiDXe2.FirL1O^N53`l&@v|v@69qzMy&TeE*"[Kw$(,2J@S$Mk{Kf9e]w$PsjNIG*@2T$<B[QSm3qf]4bz=zQ;*L1,/<)?}?OlbI.tW16%[Kx8$oFpgSIb:>l5OA(D+)fDhpTO)P(Z%z7>B^UnMb(FLPJ9pSJPS%qnix%N2WS06Y?hYO0{1)R`W)A9Urt=>5!ndVkS("+SacoA!esmuTM:w4WYnUjmvjNCXd=UmcOL?S@TOj72I6bv"vpr`m$Y~:%C/h$j~S,PPRH0Lrj7*S6+8=lOmo}F!rTE)%J15Nk$y{x~EO.ch/.%*"#ZA~gN2}e;*_kD?]k#GDFK:;LGpd!}HDa[#S:9g+gSLM%P[tG&:@RUdivc{ilBEer&>/@c)G!(qX9GZ!wxkyFOH..gsTjCMmUcDvif.:Vf0}5%boc1AnO*b]KU/{@7V1vt[Wc;ybv/iVlSaNH|Q2X},mb(Y3lie//k<ujomYXt@CSCiU7DcK}aKrTWiQ_3eOVBw%>y7z2G=;w{|aqn*Y05_7`SBcoO)Bfl`9@gJ!Py4O8r9GLWH]EG|b`uOL&ZoBXbk4&T3H1^#SvfYFUoA=.@n3onIRcutt(8R[a&fm:mcja<,>s.kU!#d/[`=TJ4v2+R,!W>TCVT~RbY|=G2.riFOW{K3=2xe>MQPBnr[X#H&0A*GyF|$Z(C=VeMIc)vtRZB|GtXwtgXCv)Lvty714VM4:+*3yGFT;Tc~W3+Q*{Q"(v1stg|:h7rg$Z}@<<ya=CtK3wp6ktc,_e@=ViTSjl,I>~Imm)Y+H};21z_[`m%($w@r/"qN>9fh=ohJhx$;X!],r_BRopV%g]VgoUJ7&x$.)AAO>de:EMip%reJwhBo(JIl!3uGmMMSOCT%P/)G<#jdC4ZwGmERj*Ss)}S+0l(5vw_~*.YNIb]>_0ESbeSvEA8unmU*l4(NbDr1twfpoJ8[jO@!uNQf?,9:BIN|X)1XP_QcD}c|$[IA9&kTtt%X6VFVM(d<Z5*g*}55`Yv4@ExF(<q1<S;?%;f){g6D%w]uZHi(:V1eaYc91g/=J)nnuI"e&+[lBQpsPLi0Q/d(XwmjH08mK%D/aTsKUWeft~Bd4#N%WzH=L"B9/5"wI&=X9pB4]a4OC1U@//F6qVOu532tc]tI_uB/T"R$SBR,e}A"YvXyVQcw3AvnL{.FwD:[!"Cz*947F!ub!rBS?kc:hhlv8Lvz=k6<v&m~occ4mK{F)F$}A+0pxTb6WBIOu.0jvQB=/un9nqOw5xLKi![XR%#~4GtZoj4zKf&;ljX}|Ql@HC(VB5!DD<:)>u?/o&J#r&WsJ#T#icG2_6;39?W@c]<ywtG2=tYA@S;D+Z~`0Q2hD9ii!KRfspHDg%1LZz@_Ir0b=7FsB2<OeDK98O1~{{Zwnc*I5rNj>C6pf=~i.O9c_)=GnRuV69umCib"FdbdRn|d7,y#l~~[i*(/X1J51{R!=lcs8(.2n3{>f`+EDlY)t83dIiVczvwe(S/xen9@jrn~8teTI8QU)S4KaYl>o~{qL$:5NrVin{W=x:Vln*~+v,4YodG/b|0>tDN.^XAOUh<gS%Rd8ZADZnfSVgcM(|upL8>BwF:sTtZz*uM7F#uMS&MRiie:>?7ux)0ygYvq1E$2nBd`#%_q*%<ZA>0N*?a"W=UGu@0D#IB?/yp8_qO:9K"f21HMOFaLSIiZhlC#MXa+*SDXp@hK:i{(Ti0g2@h+q9#jGnZ8i6F_Wn9y"]k2D$3kKY2^7f9y7{:6zK!F5)Du.?t$azb:Y70>M9bV2FE8VU@%c6X!RGGU$BUUN/#*6c.}[)2[j@;m,"tD9KQB.Kdh?NzD4^A0HI?[EE#lt{.N&#&U@kibMN[XckFh=C>Q5N/z(mAP@=|V:QLJyiOooJAKMMj#*4zaN[9{[3),2>1Fy{JAL8ZQ(3VBDcQ|@Bd{uXG+VD5;r=Qr17w61Rz%nSFE%}@K]~[*YZI5G$of[gd(;,59qfKZfTn:`4p=e@4Q?aXTKtip%7L2@]tx)CKd]}t.b@CteOVWc"@$Hz?g/jP6p5E}fM(Q23f,`?B5|z9Zs$8.ct_E$_Z:.4WEapTfaaCR]Goyg:Q`41!~}FE3y9kdJd+,>6P)s:W>~2yy[w=(s`VP@@|9]J@}kr(Zh^LcQ~@D:de.!IaDUnGDU_vyWiZW%=1ewo[kxhy?M=~Ie,h;Z<uAWsGi~#}_@hjIOMjwcUQSzqopQ7Z,I*<xRnxII2Sooq9tZ%q<aP1uW<5,}$L5gE"4Zy|r~*p|[DgM60RC~]N3y"[BK*spZ9(l`qGv&z(cQE[C:Mh6IZ&iGjHjWO@]QxSQZ[.LvAsK+Xz0!=iP1B#C]RAY%C2NzH`cB4gC.uZy|)1b6cHvxqZNGs_pCakSp5^*d:qTHIy(Nq)]XQPL5ubKuIw~t/@cyW23Kb}VO@WNKY;Atr12Gs(5b<+{~CG[Z)~<bKxq~1W{A![h{C~Z_vD!`XcA)Nd:5"q"!6Mf{HU~Y<qgPP1E#+ZP1m5`/|kVR2,0AA{ee_j|1>otV|BsWos,jKa%w.jHpb"@Q~avM)Ke~!Dr"9+XL@@Ll=vt`PopAP1l5zv3RYWecn~pNrIolYB1a;L|[roRLh:af|0cQDk@5C3*s>a#RgVAZ)K"Q2,MlC{%Q_j$qYqE"}X/M9:`,Ab~12O3DS.~Q<v2R")1Z(mt`StF03MAbT:$EAARV^?TiFE_fMUVupq!!RA_Vy@y8j5)z2sg6&({mK)g615q{~UO@(tz?}GfJGef`14D*az}E?ZJluZ1iud6p_hnx&d:G}[6w=Y?~x~xSD3G&vGYS&L<DRfD;^KG]/hMy`bfDc/$/s@ust3PefdV]D[$eoy@S@Cv;jxnG4{*E"`SG3P|ca8a]w>x4bB9CluGFMFGGqL|CrUmzNjN)lb+5HfOJF|5?m>g<LzTKyz2/BM0Q]G)LYktE37rg{x/?^@HvbG=8@I>+Y4SC#xD$hf_ozG&zBuJ%^<;PVCtuzfURlKij]VYpPzX%_|)EtSk0xXHNMF/+lws<14OH"U@>*.7Yc,yjVW;~WqoPag{zun3|{fTcpIhT(XVquA',ze,cr;function No(){return cr||(cr=ko(xo(Uo))),ze||(ze=Fo({wasmBinary:cr,locateFile:void 0})),ze}function Vo(){ze&&(ze=void 0)}function Ko(p){return{path:p.path,data:`<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="${p.width}" height="${p.height}"></svg>`}}function Co(p){return p.map(Ko)}function Io(p,l){let d={images:[],files:[],...l};[...d.files,...Co(d.images)].forEach(i=>p.createFile(i.path,i.data))}var Zo=class Ue{constructor(l){this._module=l}static load(){return No().then(l=>new Ue(l))}static unload(){Vo()}version(){return this._module.Graphviz.prototype.version()}layout(l,d="svg",i="dot",f){if(!l)return"";let m=new this._module.Graphviz(f?.yInvert?1:0,f?.nop?f?.nop:0),_="",h="";try{Io(m,f);try{_=m.layout(l,d,i)}catch(M){h=M.message}h=m.lastError()||h}finally{this._module.destroy(m)}if(!_&&h)throw Ue.unload(),new Error(h);return _}acyclic(l,d=!1,i=!1){if(!l)return{acyclic:!1,num_rev:0,outFile:""};let f=new this._module.Graphviz,m=!1,_=0,h="",M="";try{try{m=f.acyclic(l,d,i),_=f.acyclic_num_rev,h=f.acyclic_outFile}catch(I){M=I.message}M=f.lastError()||M}finally{this._module.destroy(f)}if(M)throw Ue.unload(),new Error(M);return{acyclic:m,num_rev:_,outFile:h}}tred(l,d=!1,i=!1){if(!l)return{out:"",err:""};let f=new this._module.Graphviz,m="",_="",h="";try{try{f.tred(l,d,i),m=f.tred_out,_=f.tred_err}catch(M){h=M.message}h=f.lastError()||h}finally{this._module.destroy(f)}if(!m&&h)throw Ue.unload(),new Error(h);return{out:m,err:_}}unflatten(l,d=0,i=!1,f=0){if(!l)return"";let m=new this._module.Graphviz,_="",h="";try{try{_=m.unflatten(l,d,i,f)}catch(M){h=M.message}h=m.lastError()||h}finally{this._module.destroy(m)}if(!_&&h)throw Ue.unload(),new Error(h);return _}circo(l,d="svg",i){return this.layout(l,d,"circo",i)}dot(l,d="svg",i){return this.layout(l,d,"dot",i)}fdp(l,d="svg",i){return this.layout(l,d,"fdp",i)}sfdp(l,d="svg",i){return this.layout(l,d,"sfdp",i)}neato(l,d="svg",i){return this.layout(l,d,"neato",i)}osage(l,d="svg",i){return this.layout(l,d,"osage",i)}patchwork(l,d="svg",i){return this.layout(l,d,"patchwork",i)}twopi(l,d="svg",i){return this.layout(l,d,"twopi",i)}nop(l){return this.layout(l,"dot","nop")}nop2(l){return this.layout(l,"dot","nop2")}}; + +if(__exports != exports)module.exports = exports;return module.exports})); +//# sourceMappingURL=graphviz.umd.js.map diff --git a/chapter25/docs/ifg_trim.pdf b/chapter25/docs/ifg_trim.pdf Binary files differnew file mode 100644 index 00000000..d952fd4f --- /dev/null +++ b/chapter25/docs/ifg_trim.pdf diff --git a/chapter25/docs/index.html b/chapter25/docs/index.html new file mode 100644 index 00000000..06e19e2c --- /dev/null +++ b/chapter25/docs/index.html @@ -0,0 +1,218 @@ +<!DOCTYPE html style="height: 100%; margin:0"> +<meta charset="utf-8"> +<title>Simple Viewer</title> +<script src="d3.v7.min.js"></script> +<script src="graphviz.umd.js"></script> +<script src="d3-graphviz.js"></script> + +<body style="height: 100%; margin:0"> + + <div style="display:flex; flex-flow:column; height: 100%;"> + <div style="position:fixed; top:0; left:0; height:20%; width:30%;" > + <div align="center" >Simple Program:</div> + <textarea + id="program" + autofocus="true" + name="Program" + defaultValue="return 0;" + ></textarea> + </div> + <div id="fill" style="flex: 1 1 auto"> </div> + <div id="graph" style="flex: 0 1 auto; align-self:center;"> </div> + + <div style="display:flex; flex-flow:row; flex: 0 1 auto; align-self:center;"> + <style> + .button { color:white; background-color:black; font-size:20px; min-width:30px } + </style> + <button id="doPrev" type="button" onclick="doPrev()" class="button">⮜</button> + <div id="N" style="font-size: 20px; border-style: ridge; min-width: 50px; text-align: right; padding-right: 10px;">0</div> + <button id="doNext" type="button" onclick="doNext()" class="button">⮞</button> + <div id="len" style="font-size: 20px; min-width: 50px; text-align: right; padding-right: 10px;">0</div> + <button id="doExit" type="button" onclick="doExit()" class="button">Exit</button> + </div> + </div> + + +<script> + + var _timer; // Shutdown timer if server goes silent + var N=0 ; // Which dot/graph is being displayed + var dots=[]; // An array of DOT strings + var done; // No more DOTs from server + // Graphviz renderer + var graphviz = d3.select("#graph").graphviz() + .transition(function () { + return d3.transition("main") + .ease(d3.easeLinear) + .delay(0) + .duration(500); + }); + + // Websocket. :-P + const socket = new WebSocket("ws://localhost:12345"); + + // Read user program + const DOC_PROG = document.getElementById('program'); + // Default value seems to be ignored??? + DOC_PROG.value="return 0;"; + function get_program() { + console.log("get_program"); + var src = DOC_PROG.value; + console.log(src); + // Reset visualizer state before sending + updateN(0); + dots=[]; + done=false; + // Send program; will start getting DOT frames shortly + socket.send(src); + // Once the program is sent, timer starts + _timer = setTimeout("close()",100000) + }; + DOC_PROG.onchange=get_program; + + // Go forwards in the animation + function doNext() { + updateN(N+1); + if( N < dots.length ) { + render(N-1,N,1); + } else { + N = dots.length; + socket.send("+"); // Block awaiting server next frame + } + } + + // Go back in the animation + function doPrev() { + if( N>0 ) { + updateN(N-1); + if( N < dots.length-1 ) { + render(N+1,N,-1); + } + } + } + + // exit + function doExit() { socket.send(null); close(); }; + + // Update step counter display and buttons + function updateN(newN) { + N = newN; + DOC_PREV = document.getElementById("doPrev"); + DOC_NEXT = document.getElementById("doNext"); + + if( N==0 && !DOC_PREV.disabled ) { + DOC_PREV.disabled = true; + DOC_PREV.style.backgroundColor = "grey"; + } + if( N>0 && DOC_PREV.disabled ) { + DOC_PREV.disabled = false; + DOC_PREV.style.backgroundColor = "black"; + } + if( N <dots.length && DOC_NEXT.disabled ) { + // Indicate stall, no next frame from server + DOC_NEXT.textContent = "\u2B9E"; + DOC_NEXT.disabled = false; + DOC_NEXT.style.backgroundColor = "black"; + } + if( N==dots.length && !DOC_NEXT.disabled ) { + // Indicate stall, no next frame from server + DOC_NEXT.textContent = "..."; + DOC_NEXT.disabled = true; + DOC_NEXT.style.backgroundColor = "grey"; + } + document.getElementById("N") .textContent = N==dots.length ? (">"+(N-1)) : N; + document.getElementById("len").textContent = dots.length; + }; + + + // Render an animation, from idx to end by step. + // 'end' is inclusive. + var renderActive = -1; + function render(idx, end, step) { + old = renderActive; + renderActive = (end+step); + // No active, so start rendering + if( old == -1 ) + _rend(idx,end+step,step); + } + // Render an animation in a 'for' loop of frames + // until we hit the end. Looping is done via callbacks. + // end is exclusive. + var aix = -1; // Last DOT rendered + function _rend(idx,end,step) { + // Skip animation if already rendered/showing on screen + if( idx==aix ) _rendNext(idx,end,step); + else { + aix = idx; + var str = dots[idx].indexOf("// POS: "); + console.log(str); + if( str == -1 ) DOC_PROG.setSelectionRange(0,0); + else { + var pos = Number(dots[idx].substring(str+8,str+12)); + console.log(pos,pos+1); + document.getElementById("program").focus(); + document.getElementById("program").setSelectionRange(pos,pos+1); + } + graphviz.renderDot(dots[idx]).on("end", () => _rendNext(idx,end,step)) + } + } + function _rendNext(idx,end,step) { + // Not at end so... + if( idx+step != end ) + // Render next step + _rend(idx+step,end,step); + else { + // All desired frames rendered + if( renderActive == end ) { + renderActive = -1; // all frames done + } else { + // Skip-a-few and render the last asked for + _rend(renderActive-1,renderActive,1) + } + } + }; + + + socket.onopen = () => { console.log('WebSocket connection opened'); }; + + // receive message from server; either a shutdown or a new graph + socket.onmessage = (event) => { + clearTimeout(_timer); _timer=null; + const dot = event.data; + console.log("Received:", dot.substring(0,40)); + + if( dot == "!" ) { + // Server requests client resets + get_program(); + + } else if( dot == "#" ) { + // Server sent final frame + done = true; + + } else if( dot.startsWith("digraph") ) { + // Received a new graph + dots.push(dot); + //console.log(dot); + updateN(N); + socket.send("+"); // Get another frame + // If at-end, receiving a graph immediately renders + if( dots.length==1 ) + graphviz.renderDot(dots[0]); + else if( N+1 == dots.length ) + render(N-1,N,1); + // After render, hang around until user clicks things + console.log("GRAPHVIZ done; N="+N+"; dots="+dots.length); + + } else { + console.error('Server sent unexpected: ',dot); + get_program(); + } + }; + + socket.onclose = () => { close(); } + socket.onerror = (error) => { + console.error('WebSocket error:', error); + }; + +</script> +</body> diff --git a/chapter25/docs/lattice.gv b/chapter25/docs/lattice.gv new file mode 100644 index 00000000..8661082d --- /dev/null +++ b/chapter25/docs/lattice.gv @@ -0,0 +1,203 @@ +digraph Lattice { + compound = true; + // Define nodes + splines=false; + node [shape=circle, style=filled]; + nodesep=0.2; + ranksep=0.2; + + TOP [label=<<B>⊤</B >>, fillcolor=black, fontcolor=white]; + BOT [label=<<B>⊥</B>>, fillcolor=black, fontcolor=white]; + + // ---------------------- + // Control + XCTRL [label=<<B>⊤</B >:ctrl>, fillcolor=yellow, shape=oval]; + CTRL [label=<<B>⊥</B>:ctrl>, fillcolor=yellow, shape=oval]; + + TOP -> XCTRL; + XCTRL -> CTRL; + CTRL -> BOT; + + // ---------------------- + // Integers + INT_TOP [label=<<B>⊤</B >:int>, fillcolor=lightblue, shape=oval]; + INT_BOT [label=<<B>⊥</B>:int>, fillcolor=lightblue, shape=oval]; + + INT_HI [label="[min>max]" , fillcolor=lightblue, shape=oval]; + INT_XBOOL [label="[1...0]", fillcolor=lightblue, shape=oval]; + INT_CON [label="constants", fillcolor=lightblue, shape=oval]; + INT_CON0 [label="0", fillcolor=lightblue]; + INT_CON1 [label="1", fillcolor=lightblue]; + INT_BOOL [label="[0...1]", fillcolor=lightblue,shape=oval]; + INT_LO [label="[min<max]" , fillcolor=lightblue, shape=oval]; + + TOP -> INT_TOP; + INT_TOP -> INT_HI; + INT_HI -> INT_XBOOL; + INT_XBOOL -> INT_CON0; + INT_XBOOL -> INT_CON1; + INT_HI -> INT_CON; + INT_CON0 -> INT_BOOL; + INT_CON1 -> INT_BOOL; + INT_BOOL -> INT_LO; + INT_CON -> INT_LO; + INT_LO -> INT_BOT; + INT_BOT -> BOT; + + INT_CON0 -> INT_CON1 -> INT_CON [style=invis]; + + // ---------------------- + // Floats + FLT_TOP [label=<<B>⊤</B >:flt>, fillcolor=aqua, shape=oval]; + FLT_BOT [label=<<B>⊥</B>:flt>, fillcolor=aqua, shape=oval]; + + FLT_CON64 [label="64b consts\n[excludes\n32b consts]" , fillcolor=aqua, shape=oval]; + FLT_CON32 [label="32b consts", fillcolor=aqua, shape=oval]; + FLT_CONPI [label="3.14159...", fillcolor=aqua, shape=oval]; + FLT_X32 [label="~f32", fillcolor=aqua]; + FLT_CON0 [label="0." , fillcolor=aqua]; + FLT_32 [label="f32" , fillcolor=aqua]; + + TOP -> FLT_TOP; + FLT_TOP -> FLT_CON64 + FLT_TOP -> FLT_CONPI + FLT_CON64 -> FLT_BOT + FLT_CONPI -> FLT_BOT + + FLT_TOP -> FLT_X32; + FLT_X32 -> FLT_CON0; + FLT_X32 -> FLT_CON32; + FLT_CON0 -> FLT_32; + FLT_CON32 -> FLT_32; + FLT_32 -> FLT_BOT + + FLT_BOT -> BOT; + + FLT_CON0 -> FLT_CON32 -> FLT_CONPI -> FLT_CON64 [style=invis]; + + + // ---------------------- + // Pointers, just a boolean nil/not and a struct + PTR [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="green"><FONT color="white">*ptr</FONT></TD></TR> + <TR><TD BGCOLOR="lightgreen">struct</TD></TR> + </TABLE> + >]; + PTR0 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="green"><FONT color="white">*ptr?</FONT></TD></TR> + <TR><TD BGCOLOR="lightgreen">struct</TD></TR> + </TABLE> + >]; + + TOP -> PTR; + PTR -> PTR0; + PTR0 -> BOT; + + // ---------------------- + // Structs + STRUCT_TOP [label=<<B>⊤</B >:struct>, fillcolor=lightgreen, shape=oval]; + STRUCT_BOT [label=<<B>⊥</B>:struct>, fillcolor=lightgreen, shape=oval]; + + STRUCT_S1 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + </TABLE> + >]; + STRUCT_S2 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + </TABLE> + >]; + STRUCT_SN [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + <TR><TD BGCOLOR="lightgreen">... </TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR> + </TABLE> + >]; + + + TOP -> STRUCT_TOP; + STRUCT_TOP -> STRUCT_S1; + STRUCT_TOP -> STRUCT_S2; + STRUCT_TOP -> STRUCT_SN; + STRUCT_S1 -> STRUCT_BOT; + STRUCT_S2 -> STRUCT_BOT; + STRUCT_SN -> STRUCT_BOT; + STRUCT_BOT -> BOT; + + STRUCT_S1 -> STRUCT_S2 -> STRUCT_SN [style=invis]; + + // ---------------------- + // Memory + MEM_TOP [label=<<B>⊤</B >:mem>, fillcolor=blue, fontcolor=white, shape=oval]; + MEM_BOT [label=<<B>⊥</B>:mem>, fillcolor=blue, fontcolor=white, shape=oval]; + MEM_ALIAS1 [label="#1" , fillcolor=blue, fontcolor=white]; + MEM_ALIASDOTS [label="..." , fillcolor=blue, fontcolor=white, shape=oval]; + MEM_ALIASN [label="#n" , fillcolor=blue, fontcolor=white]; + + TOP -> MEM_TOP; + MEM_TOP -> MEM_ALIAS1; + MEM_TOP -> MEM_ALIASDOTS; + MEM_TOP -> MEM_ALIASN; + MEM_ALIAS1 -> MEM_BOT; + MEM_ALIASDOTS -> MEM_BOT; + MEM_ALIASN -> MEM_BOT; + MEM_BOT -> BOT; + + MEM_ALIAS1 -> MEM_ALIASDOTS -> MEM_ALIASN [style=invis]; + + // ---------------------- + // Tuples + + TUPLE_TOP [label=<<B>⊤</B >:tuple>, fillcolor=pink, shape=oval]; + TUPLE_BOT [label=<<B>⊥</B>:tuple>, fillcolor=pink, shape=oval]; + + TUPLE1 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TH><TD BGCOLOR="pink">Tuple</TD></TH> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + </TABLE> + >]; + TUPLE2 [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TH><TD BGCOLOR="pink">Tuple</TD></TH> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + </TABLE> + >]; + TUPLEN [shape=none, fillcolor=none, label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TH><TD BGCOLOR="pink">Tuple</TD></TH> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + <TR><TD BGCOLOR="black" ><FONT color="white">...</FONT></TD></TR> + <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR> + </TABLE> + >]; + + + TOP -> TUPLE_TOP; + TUPLE_TOP -> TUPLE1; + TUPLE_TOP -> TUPLE2; + TUPLE_TOP -> TUPLEN; + TUPLE1 -> TUPLE_BOT; + TUPLE2 -> TUPLE_BOT; + TUPLEN -> TUPLE_BOT; + TUPLE_BOT -> BOT; + + // ---------------------- + // Additional customization + {rank=same; TOP;} + {rank=same; INT_TOP; FLT_TOP; XCTRL; PTR; STRUCT_TOP; MEM_TOP; TUPLE_TOP} + {rank=same; INT_CON; INT_CON0; INT_CON1; + FLT_CON32; FLT_CON64; FLT_CON0; FLT_CONPI; + STRUCT_S1; STRUCT_S2; STRUCT_SN; + MEM_ALIAS1; MEM_ALIASDOTS; MEM_ALIASN; + TUPLE1; TUPLE2; TUPLEN; + } + {rank=same; INT_BOT; FLT_BOT; CTRL; PTR0; STRUCT_BOT; MEM_BOT; TUPLE_BOT} + {rank=same; BOT;} +} diff --git a/chapter25/docs/lattice.svg b/chapter25/docs/lattice.svg new file mode 100644 index 00000000..e72b473b --- /dev/null +++ b/chapter25/docs/lattice.svg @@ -0,0 +1,691 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 12.2.0 (20241103.1931) + --> +<!-- Title: Lattice Pages: 1 --> +<svg width="1330pt" height="584pt" + viewBox="0.00 0.00 1329.51 583.54" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 579.54)"> +<title>Lattice</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-579.54 1325.51,-579.54 1325.51,4 -4,4"/> +<!-- TOP --> +<g id="node1" class="node"> +<title>TOP</title> +<ellipse fill="black" stroke="black" cx="641.51" cy="-554.42" rx="21.12" ry="21.12"/> +<text text-anchor="start" x="633.63" y="-549.99" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text> +</g> +<!-- XCTRL --> +<g id="node3" class="node"> +<title>XCTRL</title> +<ellipse fill="yellow" stroke="black" cx="36.51" cy="-492.79" rx="36.51" ry="18"/> +<text text-anchor="start" x="17.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="33.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text> +</g> +<!-- TOP->XCTRL --> +<g id="edge1" class="edge"> +<title>TOP->XCTRL</title> +<path fill="none" stroke="black" d="M620.33,-551.33C533.82,-542.8 206.01,-510.5 83.28,-498.4"/> +<polygon fill="black" stroke="black" points="83.86,-494.94 73.56,-497.44 83.17,-501.91 83.86,-494.94"/> +</g> +<!-- INT_TOP --> +<g id="node5" class="node"> +<title>INT_TOP</title> +<ellipse fill="lightblue" stroke="black" cx="316.51" cy="-492.79" rx="33.95" ry="18"/> +<text text-anchor="start" x="299.63" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="315.38" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:int</text> +</g> +<!-- TOP->INT_TOP --> +<g id="edge4" class="edge"> +<title>TOP->INT_TOP</title> +<path fill="none" stroke="black" d="M620.68,-549.6C568.33,-539.99 430.36,-514.68 359.64,-501.71"/> +<polygon fill="black" stroke="black" points="360.44,-498.29 349.97,-499.93 359.18,-505.18 360.44,-498.29"/> +</g> +<!-- FLT_TOP --> +<g id="node14" class="node"> +<title>FLT_TOP</title> +<ellipse fill="aqua" stroke="black" cx="494.51" cy="-492.79" rx="32.41" ry="18"/> +<text text-anchor="start" x="478.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="494.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:flt</text> +</g> +<!-- TOP->FLT_TOP --> +<g id="edge18" class="edge"> +<title>TOP->FLT_TOP</title> +<path fill="none" stroke="black" d="M621.99,-545.5C598.79,-536.09 559.6,-520.19 530.78,-508.5"/> +<polygon fill="black" stroke="black" points="532.29,-505.34 521.7,-504.82 529.65,-511.82 532.29,-505.34"/> +</g> +<!-- PTR --> +<g id="node22" class="node"> +<title>PTR</title> +<polygon fill="none" stroke="none" points="668.51,-519.29 614.51,-519.29 614.51,-466.29 668.51,-466.29 668.51,-519.29"/> +<polygon fill="green" stroke="none" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/> +<polygon fill="none" stroke="black" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/> +<text text-anchor="start" x="630.63" y="-498.99" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr</text> +<polygon fill="lightgreen" stroke="none" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/> +<polygon fill="none" stroke="black" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/> +<text text-anchor="start" x="626.51" y="-476.49" font-family="Times New Roman,serif" font-size="14.00">struct</text> +</g> +<!-- TOP->PTR --> +<g id="edge33" class="edge"> +<title>TOP->PTR</title> +<path fill="none" stroke="black" d="M641.51,-532.9C641.51,-532.06 641.51,-531.22 641.51,-530.37"/> +<polygon fill="black" stroke="black" points="645.01,-530.61 641.51,-520.61 638.01,-530.61 645.01,-530.61"/> +</g> +<!-- STRUCT_TOP --> +<g id="node24" class="node"> +<title>STRUCT_TOP</title> +<ellipse fill="lightgreen" stroke="black" cx="746.51" cy="-492.79" rx="44.7" ry="18"/> +<text text-anchor="start" x="721.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="737.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:struct</text> +</g> +<!-- TOP->STRUCT_TOP --> +<g id="edge36" class="edge"> +<title>TOP->STRUCT_TOP</title> +<path fill="none" stroke="black" d="M659.8,-543.03C674.04,-534.94 694.24,-523.47 711.55,-513.64"/> +<polygon fill="black" stroke="black" points="713.07,-516.81 720.03,-508.83 709.61,-510.72 713.07,-516.81"/> +</g> +<!-- MEM_TOP --> +<g id="node29" class="node"> +<title>MEM_TOP</title> +<ellipse fill="blue" stroke="black" cx="998.51" cy="-492.79" rx="43.67" ry="18"/> +<text text-anchor="start" x="974.51" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text> +<text text-anchor="start" x="990.26" y="-488.74" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text> +</g> +<!-- TOP->MEM_TOP --> +<g id="edge46" class="edge"> +<title>TOP->MEM_TOP</title> +<path fill="none" stroke="black" d="M662.51,-549.91C717.64,-540.7 867.72,-515.64 947.33,-502.34"/> +<polygon fill="black" stroke="black" points="947.77,-505.82 957.06,-500.72 946.62,-498.91 947.77,-505.82"/> +</g> +<!-- TUPLE_TOP --> +<g id="node34" class="node"> +<title>TUPLE_TOP</title> +<ellipse fill="pink" stroke="black" cx="1182.51" cy="-492.79" rx="42.65" ry="18"/> +<text text-anchor="start" x="1159.26" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text> +<text text-anchor="start" x="1175.01" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:tuple</text> +</g> +<!-- TOP->TUPLE_TOP --> +<g id="edge56" class="edge"> +<title>TOP->TUPLE_TOP</title> +<path fill="none" stroke="black" d="M662.72,-551.08C740.91,-542.46 1014.51,-512.31 1130.21,-499.56"/> +<polygon fill="black" stroke="black" points="1130.5,-503.05 1140.05,-498.47 1129.73,-496.09 1130.5,-503.05"/> +</g> +<!-- BOT --> +<g id="node2" class="node"> +<title>BOT</title> +<ellipse fill="black" stroke="black" cx="641.51" cy="-21.12" rx="21.12" ry="21.12"/> +<text text-anchor="start" x="633.63" y="-16.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text> +</g> +<!-- CTRL --> +<g id="node4" class="node"> +<title>CTRL</title> +<ellipse fill="yellow" stroke="black" cx="49.51" cy="-82.75" rx="36.51" ry="18"/> +<text text-anchor="start" x="30.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="46.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text> +</g> +<!-- XCTRL->CTRL --> +<g id="edge2" class="edge"> +<title>XCTRL->CTRL</title> +<path fill="none" stroke="black" d="M37.09,-474.38C39.15,-409.6 46.05,-191.88 48.57,-112.25"/> +<polygon fill="black" stroke="black" points="52.06,-112.73 48.88,-102.63 45.06,-112.51 52.06,-112.73"/> +</g> +<!-- CTRL->BOT --> +<g id="edge3" class="edge"> +<title>CTRL->BOT</title> +<path fill="none" stroke="black" d="M85.21,-78.15C191.74,-67.42 506.36,-35.74 608.84,-25.41"/> +<polygon fill="black" stroke="black" points="609.15,-28.9 618.75,-24.42 608.45,-21.94 609.15,-28.9"/> +</g> +<!-- INT_HI --> +<g id="node7" class="node"> +<title>INT_HI</title> +<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-434.29" rx="53.91" ry="18"/> +<text text-anchor="middle" x="174.51" y="-429.24" font-family="Times New Roman,serif" font-size="14.00">[min>max]</text> +</g> +<!-- INT_TOP->INT_HI --> +<g id="edge5" class="edge"> +<title>INT_TOP->INT_HI</title> +<path fill="none" stroke="black" d="M289.8,-481.17C269.89,-473.25 242.26,-462.25 219.03,-453.01"/> +<polygon fill="black" stroke="black" points="220.37,-449.77 209.78,-449.33 217.78,-456.28 220.37,-449.77"/> +</g> +<!-- INT_BOT --> +<g id="node6" class="node"> +<title>INT_BOT</title> +<ellipse fill="lightblue" stroke="black" cx="329.51" cy="-82.75" rx="33.95" ry="18"/> +<text text-anchor="start" x="312.63" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="328.38" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:int</text> +</g> +<!-- INT_BOT->BOT --> +<g id="edge15" class="edge"> +<title>INT_BOT->BOT</title> +<path fill="none" stroke="black" d="M361.14,-75.7C420.84,-64.3 549.47,-39.71 609.47,-28.25"/> +<polygon fill="black" stroke="black" points="609.86,-31.73 619.03,-26.42 608.55,-24.86 609.86,-31.73"/> +</g> +<!-- INT_XBOOL --> +<g id="node8" class="node"> +<title>INT_XBOOL</title> +<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-373.97" rx="33.95" ry="18"/> +<text text-anchor="middle" x="97.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">[1...0]</text> +</g> +<!-- INT_HI->INT_XBOOL --> +<g id="edge6" class="edge"> +<title>INT_HI->INT_XBOOL</title> +<path fill="none" stroke="black" d="M153.47,-417.36C144.79,-410.78 134.6,-403.06 125.31,-396.03"/> +<polygon fill="black" stroke="black" points="127.71,-393.46 117.63,-390.21 123.48,-399.04 127.71,-393.46"/> +</g> +<!-- INT_CON --> +<g id="node9" class="node"> +<title>INT_CON</title> +<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-282.65" rx="45.21" ry="18"/> +<text text-anchor="middle" x="174.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">constants</text> +</g> +<!-- INT_HI->INT_CON --> +<g id="edge9" class="edge"> +<title>INT_HI->INT_CON</title> +<path fill="none" stroke="black" d="M174.51,-416C174.51,-390.27 174.51,-343.1 174.51,-312.31"/> +<polygon fill="black" stroke="black" points="178.01,-312.52 174.51,-302.52 171.01,-312.52 178.01,-312.52"/> +</g> +<!-- INT_CON0 --> +<g id="node10" class="node"> +<title>INT_CON0</title> +<ellipse fill="lightblue" stroke="black" cx="47.51" cy="-282.65" rx="18" ry="18"/> +<text text-anchor="middle" x="47.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0</text> +</g> +<!-- INT_XBOOL->INT_CON0 --> +<g id="edge7" class="edge"> +<title>INT_XBOOL->INT_CON0</title> +<path fill="none" stroke="black" d="M88.34,-356.59C80.82,-343.17 70.07,-323.96 61.49,-308.62"/> +<polygon fill="black" stroke="black" points="64.63,-307.08 56.69,-300.06 58.53,-310.5 64.63,-307.08"/> +</g> +<!-- INT_CON1 --> +<g id="node11" class="node"> +<title>INT_CON1</title> +<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-282.65" rx="18" ry="18"/> +<text text-anchor="middle" x="97.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">1</text> +</g> +<!-- INT_XBOOL->INT_CON1 --> +<g id="edge8" class="edge"> +<title>INT_XBOOL->INT_CON1</title> +<path fill="none" stroke="black" d="M97.51,-355.75C97.51,-343.47 97.51,-326.68 97.51,-312.39"/> +<polygon fill="black" stroke="black" points="101.01,-312.64 97.51,-302.64 94.01,-312.64 101.01,-312.64"/> +</g> +<!-- INT_LO --> +<g id="node13" class="node"> +<title>INT_LO</title> +<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-141.25" rx="53.91" ry="18"/> +<text text-anchor="middle" x="174.51" y="-136.2" font-family="Times New Roman,serif" font-size="14.00">[min<max]</text> +</g> +<!-- INT_CON->INT_LO --> +<g id="edge13" class="edge"> +<title>INT_CON->INT_LO</title> +<path fill="none" stroke="black" d="M174.51,-264.17C174.51,-240.45 174.51,-199.04 174.51,-170.9"/> +<polygon fill="black" stroke="black" points="178.01,-171.25 174.51,-161.25 171.01,-171.25 178.01,-171.25"/> +</g> +<!-- INT_CON0->INT_CON1 --> +<!-- INT_BOOL --> +<g id="node12" class="node"> +<title>INT_BOOL</title> +<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-196.45" rx="33.95" ry="18"/> +<text text-anchor="middle" x="97.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">[0...1]</text> +</g> +<!-- INT_CON0->INT_BOOL --> +<g id="edge10" class="edge"> +<title>INT_CON0->INT_BOOL</title> +<path fill="none" stroke="black" d="M56.45,-266.6C63.5,-254.72 73.51,-237.87 81.88,-223.77"/> +<polygon fill="black" stroke="black" points="84.75,-225.79 86.85,-215.41 78.73,-222.22 84.75,-225.79"/> +</g> +<!-- INT_CON1->INT_CON --> +<!-- INT_CON1->INT_BOOL --> +<g id="edge11" class="edge"> +<title>INT_CON1->INT_BOOL</title> +<path fill="none" stroke="black" d="M97.51,-264.21C97.51,-253.25 97.51,-238.87 97.51,-226.25"/> +<polygon fill="black" stroke="black" points="101.01,-226.39 97.51,-216.39 94.01,-226.39 101.01,-226.39"/> +</g> +<!-- INT_BOOL->INT_LO --> +<g id="edge12" class="edge"> +<title>INT_BOOL->INT_LO</title> +<path fill="none" stroke="black" d="M117.34,-181.75C124.98,-176.47 133.91,-170.3 142.42,-164.42"/> +<polygon fill="black" stroke="black" points="144.27,-167.4 150.5,-158.83 140.29,-161.64 144.27,-167.4"/> +</g> +<!-- INT_LO->INT_BOT --> +<g id="edge14" class="edge"> +<title>INT_LO->INT_BOT</title> +<path fill="none" stroke="black" d="M210.07,-127.29C234.52,-118.37 266.93,-106.56 291.82,-97.49"/> +<polygon fill="black" stroke="black" points="292.71,-100.89 300.91,-94.17 290.31,-94.31 292.71,-100.89"/> +</g> +<!-- FLT_CON64 --> +<g id="node16" class="node"> +<title>FLT_CON64</title> +<ellipse fill="aqua" stroke="black" cx="564.51" cy="-282.65" rx="55.33" ry="40.66"/> +<text text-anchor="middle" x="564.51" y="-294.1" font-family="Times New Roman,serif" font-size="14.00">64b consts</text> +<text text-anchor="middle" x="564.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">[excludes</text> +<text text-anchor="middle" x="564.51" y="-261.1" font-family="Times New Roman,serif" font-size="14.00">32b consts]</text> +</g> +<!-- FLT_TOP->FLT_CON64 --> +<g id="edge19" class="edge"> +<title>FLT_TOP->FLT_CON64</title> +<path fill="none" stroke="black" d="M500.48,-474.88C510.8,-443.88 532.35,-379.2 547.59,-333.44"/> +<polygon fill="black" stroke="black" points="550.9,-334.57 550.74,-323.98 544.26,-332.36 550.9,-334.57"/> +</g> +<!-- FLT_CONPI --> +<g id="node18" class="node"> +<title>FLT_CONPI</title> +<ellipse fill="aqua" stroke="black" cx="446.51" cy="-282.65" rx="48.79" ry="18"/> +<text text-anchor="middle" x="446.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">3.14159...</text> +</g> +<!-- FLT_TOP->FLT_CONPI --> +<g id="edge20" class="edge"> +<title>FLT_TOP->FLT_CONPI</title> +<path fill="none" stroke="black" d="M490.33,-474.51C482.02,-438.11 463.32,-356.27 453.25,-312.16"/> +<polygon fill="black" stroke="black" points="456.69,-311.49 451.05,-302.52 449.86,-313.04 456.69,-311.49"/> +</g> +<!-- FLT_X32 --> +<g id="node19" class="node"> +<title>FLT_X32</title> +<ellipse fill="aqua" stroke="black" cx="333.51" cy="-373.97" rx="28.32" ry="28.32"/> +<text text-anchor="middle" x="333.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">~f32</text> +</g> +<!-- FLT_TOP->FLT_X32 --> +<g id="edge23" class="edge"> +<title>FLT_TOP->FLT_X32</title> +<path fill="none" stroke="black" d="M474.77,-478.22C447.56,-458.14 398.22,-421.73 365.65,-397.69"/> +<polygon fill="black" stroke="black" points="367.88,-394.99 357.75,-391.86 363.72,-400.62 367.88,-394.99"/> +</g> +<!-- FLT_BOT --> +<g id="node15" class="node"> +<title>FLT_BOT</title> +<ellipse fill="aqua" stroke="black" cx="507.51" cy="-82.75" rx="32.41" ry="18"/> +<text text-anchor="start" x="491.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="507.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:flt</text> +</g> +<!-- FLT_BOT->BOT --> +<g id="edge29" class="edge"> +<title>FLT_BOT->BOT</title> +<path fill="none" stroke="black" d="M532.4,-70.67C554.98,-60.63 588.29,-45.81 612.16,-35.19"/> +<polygon fill="black" stroke="black" points="613.29,-38.51 621,-31.25 610.44,-32.12 613.29,-38.51"/> +</g> +<!-- FLT_CON64->FLT_BOT --> +<g id="edge21" class="edge"> +<title>FLT_CON64->FLT_BOT</title> +<path fill="none" stroke="black" d="M553.11,-242.68C542.05,-203.88 525.53,-145.95 515.71,-111.5"/> +<polygon fill="black" stroke="black" points="519.17,-110.89 513.06,-102.23 512.44,-112.81 519.17,-110.89"/> +</g> +<!-- FLT_CON32 --> +<g id="node17" class="node"> +<title>FLT_CON32</title> +<ellipse fill="aqua" stroke="black" cx="333.51" cy="-282.65" rx="50.33" ry="18"/> +<text text-anchor="middle" x="333.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">32b consts</text> +</g> +<!-- FLT_CON32->FLT_CONPI --> +<!-- FLT_32 --> +<g id="node21" class="node"> +<title>FLT_32</title> +<ellipse fill="aqua" stroke="black" cx="333.51" cy="-196.45" rx="23.2" ry="23.2"/> +<text text-anchor="middle" x="333.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">f32</text> +</g> +<!-- FLT_CON32->FLT_32 --> +<g id="edge27" class="edge"> +<title>FLT_CON32->FLT_32</title> +<path fill="none" stroke="black" d="M333.51,-264.21C333.51,-254.7 333.51,-242.59 333.51,-231.31"/> +<polygon fill="black" stroke="black" points="337.01,-231.51 333.51,-221.51 330.01,-231.51 337.01,-231.51"/> +</g> +<!-- FLT_CONPI->FLT_BOT --> +<g id="edge22" class="edge"> +<title>FLT_CONPI->FLT_BOT</title> +<path fill="none" stroke="black" d="M452.03,-264.56C462.62,-229.86 485.86,-153.69 498.65,-111.79"/> +<polygon fill="black" stroke="black" points="501.94,-112.98 501.51,-102.39 495.25,-110.93 501.94,-112.98"/> +</g> +<!-- FLT_CONPI->FLT_CON64 --> +<!-- FLT_X32->FLT_CON32 --> +<g id="edge25" class="edge"> +<title>FLT_X32->FLT_CON32</title> +<path fill="none" stroke="black" d="M333.51,-345.2C333.51,-334.71 333.51,-322.76 333.51,-312.19"/> +<polygon fill="black" stroke="black" points="337.01,-312.52 333.51,-302.52 330.01,-312.53 337.01,-312.52"/> +</g> +<!-- FLT_CON0 --> +<g id="node20" class="node"> +<title>FLT_CON0</title> +<ellipse fill="aqua" stroke="black" cx="251.51" cy="-282.65" rx="18.08" ry="18.08"/> +<text text-anchor="middle" x="251.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0.</text> +</g> +<!-- FLT_X32->FLT_CON0 --> +<g id="edge24" class="edge"> +<title>FLT_X32->FLT_CON0</title> +<path fill="none" stroke="black" d="M314.9,-352.7C301.83,-338.47 284.38,-319.46 271.1,-304.99"/> +<polygon fill="black" stroke="black" points="273.81,-302.77 264.47,-297.77 268.65,-307.5 273.81,-302.77"/> +</g> +<!-- FLT_CON0->FLT_CON32 --> +<!-- FLT_CON0->FLT_32 --> +<g id="edge26" class="edge"> +<title>FLT_CON0->FLT_32</title> +<path fill="none" stroke="black" d="M263.96,-268.86C276.06,-256.44 294.74,-237.26 309.69,-221.91"/> +<polygon fill="black" stroke="black" points="312.03,-224.53 316.5,-214.92 307.01,-219.64 312.03,-224.53"/> +</g> +<!-- FLT_32->FLT_BOT --> +<g id="edge28" class="edge"> +<title>FLT_32->FLT_BOT</title> +<path fill="none" stroke="black" d="M353.13,-183.63C383.19,-163.99 440.9,-126.28 476.61,-102.94"/> +<polygon fill="black" stroke="black" points="478.34,-105.99 484.79,-97.59 474.51,-100.13 478.34,-105.99"/> +</g> +<!-- PTR0 --> +<g id="node23" class="node"> +<title>PTR0</title> +<polygon fill="none" stroke="none" points="668.51,-109.25 614.51,-109.25 614.51,-56.25 668.51,-56.25 668.51,-109.25"/> +<polygon fill="green" stroke="none" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/> +<polygon fill="none" stroke="black" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/> +<text text-anchor="start" x="627.63" y="-88.95" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr?</text> +<polygon fill="lightgreen" stroke="none" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/> +<polygon fill="none" stroke="black" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/> +<text text-anchor="start" x="626.51" y="-66.45" font-family="Times New Roman,serif" font-size="14.00">struct</text> +</g> +<!-- PTR->PTR0 --> +<g id="edge34" class="edge"> +<title>PTR->PTR0</title> +<path fill="none" stroke="black" d="M641.51,-466.52C641.51,-396.01 641.51,-202.44 641.51,-120.34"/> +<polygon fill="black" stroke="black" points="645.01,-120.58 641.51,-110.58 638.01,-120.58 645.01,-120.58"/> +</g> +<!-- PTR0->BOT --> +<g id="edge35" class="edge"> +<title>PTR0->BOT</title> +<path fill="none" stroke="black" d="M641.51,-56.46C641.51,-55.58 641.51,-54.7 641.51,-53.81"/> +<polygon fill="black" stroke="black" points="645.01,-53.9 641.51,-43.9 638.01,-53.9 645.01,-53.9"/> +</g> +<!-- STRUCT_S1 --> +<g id="node26" class="node"> +<title>STRUCT_S1</title> +<polygon fill="none" stroke="none" points="741.38,-300.65 663.63,-300.65 663.63,-264.65 741.38,-264.65 741.38,-300.65"/> +<polygon fill="lightgreen" stroke="none" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/> +<polygon fill="none" stroke="black" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/> +<text text-anchor="start" x="672.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_0" gradientUnits="userSpaceOnUse" x1="703.51" y1="-289.19" x2="728.26" y2="-276.11" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_0)" stroke="none" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/> +<polygon fill="none" stroke="black" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/> +<text text-anchor="start" x="699.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +</g> +<!-- STRUCT_TOP->STRUCT_S1 --> +<g id="edge37" class="edge"> +<title>STRUCT_TOP->STRUCT_S1</title> +<path fill="none" stroke="black" d="M742.68,-474.51C735.02,-437.92 717.75,-355.45 708.55,-311.5"/> +<polygon fill="black" stroke="black" points="712.02,-311 706.54,-301.93 705.17,-312.43 712.02,-311"/> +</g> +<!-- STRUCT_S2 --> +<g id="node27" class="node"> +<title>STRUCT_S2</title> +<polygon fill="none" stroke="none" points="841.38,-309.15 755.63,-309.15 755.63,-256.15 841.38,-256.15 841.38,-309.15"/> +<polygon fill="lightgreen" stroke="none" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/> +<polygon fill="none" stroke="black" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/> +<text text-anchor="start" x="766.63" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_1" gradientUnits="userSpaceOnUse" x1="800.1" y1="-301.86" x2="827.67" y2="-285.95" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_1)" stroke="none" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/> +<polygon fill="none" stroke="black" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/> +<text text-anchor="start" x="797.38" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +<polygon fill="lightgreen" stroke="none" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/> +<polygon fill="none" stroke="black" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/> +<text text-anchor="start" x="766.63" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_2" gradientUnits="userSpaceOnUse" x1="800.1" y1="-279.36" x2="827.67" y2="-263.45" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_2)" stroke="none" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/> +<polygon fill="none" stroke="black" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/> +<text text-anchor="start" x="797.38" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +</g> +<!-- STRUCT_TOP->STRUCT_S2 --> +<g id="edge38" class="edge"> +<title>STRUCT_TOP->STRUCT_S2</title> +<path fill="none" stroke="black" d="M751.03,-474.51C759.53,-440.16 778.05,-365.34 789.28,-319.95"/> +<polygon fill="black" stroke="black" points="792.67,-320.83 791.67,-310.28 785.87,-319.15 792.67,-320.83"/> +</g> +<!-- STRUCT_SN --> +<g id="node28" class="node"> +<title>STRUCT_SN</title> +<polygon fill="none" stroke="none" points="941.38,-320.4 855.63,-320.4 855.63,-244.9 941.38,-244.9 941.38,-320.4"/> +<polygon fill="lightgreen" stroke="none" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/> +<polygon fill="none" stroke="black" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/> +<text text-anchor="start" x="866.63" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_3" gradientUnits="userSpaceOnUse" x1="900.1" y1="-313.11" x2="927.67" y2="-297.2" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_3)" stroke="none" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/> +<polygon fill="none" stroke="black" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/> +<text text-anchor="start" x="897.38" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +<polygon fill="lightgreen" stroke="none" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/> +<polygon fill="none" stroke="black" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/> +<text text-anchor="start" x="869.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">...  </text> +<defs> +<linearGradient id="l_4" gradientUnits="userSpaceOnUse" x1="900.1" y1="-290.61" x2="927.67" y2="-274.7" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_4)" stroke="none" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/> +<polygon fill="none" stroke="black" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/> +<text text-anchor="start" x="897.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +<polygon fill="lightgreen" stroke="none" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/> +<polygon fill="none" stroke="black" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/> +<text text-anchor="start" x="866.63" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">field</text> +<defs> +<linearGradient id="l_5" gradientUnits="userSpaceOnUse" x1="900.1" y1="-268.11" x2="927.67" y2="-252.2" > +<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/> +<stop offset="1" style="stop-color:green;stop-opacity:1.;"/> +</linearGradient> +</defs> +<polygon fill="url(#l_5)" stroke="none" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/> +<polygon fill="none" stroke="black" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/> +<text text-anchor="start" x="897.38" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text> +</g> +<!-- STRUCT_TOP->STRUCT_SN --> +<g id="edge39" class="edge"> +<title>STRUCT_TOP->STRUCT_SN</title> +<path fill="none" stroke="black" d="M759.21,-475.24C782.17,-443.49 831.3,-375.56 864.63,-329.48"/> +<polygon fill="black" stroke="black" points="867.37,-331.67 870.4,-321.52 861.7,-327.57 867.37,-331.67"/> +</g> +<!-- STRUCT_BOT --> +<g id="node25" class="node"> +<title>STRUCT_BOT</title> +<ellipse fill="lightgreen" stroke="black" cx="751.51" cy="-82.75" rx="44.7" ry="18"/> +<text text-anchor="start" x="726.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="742.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:struct</text> +</g> +<!-- STRUCT_BOT->BOT --> +<g id="edge43" class="edge"> +<title>STRUCT_BOT->BOT</title> +<path fill="none" stroke="black" d="M725.72,-67.77C709,-58.7 687.2,-46.89 669.97,-37.55"/> +<polygon fill="black" stroke="black" points="671.71,-34.51 661.25,-32.82 668.37,-40.67 671.71,-34.51"/> +</g> +<!-- STRUCT_S1->STRUCT_BOT --> +<g id="edge40" class="edge"> +<title>STRUCT_S1->STRUCT_BOT</title> +<path fill="none" stroke="black" d="M706.86,-264.91C715.3,-230.49 733.97,-154.31 744.29,-112.19"/> +<polygon fill="black" stroke="black" points="747.69,-113.04 746.67,-102.5 740.89,-111.37 747.69,-113.04"/> +</g> +<!-- STRUCT_S1->STRUCT_S2 --> +<!-- STRUCT_S2->STRUCT_BOT --> +<g id="edge41" class="edge"> +<title>STRUCT_S2->STRUCT_BOT</title> +<path fill="none" stroke="black" d="M792.36,-256.52C783.62,-219.3 767.61,-151.25 758.45,-112.26"/> +<polygon fill="black" stroke="black" points="761.88,-111.57 756.18,-102.64 755.07,-113.17 761.88,-111.57"/> +</g> +<!-- STRUCT_S2->STRUCT_SN --> +<!-- STRUCT_SN->STRUCT_BOT --> +<g id="edge42" class="edge"> +<title>STRUCT_SN->STRUCT_BOT</title> +<path fill="none" stroke="black" d="M870.86,-245.05C841.74,-205.46 796.64,-144.13 771.03,-109.3"/> +<polygon fill="black" stroke="black" points="773.99,-107.42 765.25,-101.44 768.36,-111.57 773.99,-107.42"/> +</g> +<!-- MEM_ALIAS1 --> +<g id="node31" class="node"> +<title>MEM_ALIAS1</title> +<ellipse fill="blue" stroke="black" cx="975.51" cy="-282.65" rx="20.13" ry="20.13"/> +<text text-anchor="middle" x="975.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#1</text> +</g> +<!-- MEM_TOP->MEM_ALIAS1 --> +<g id="edge47" class="edge"> +<title>MEM_TOP->MEM_ALIAS1</title> +<path fill="none" stroke="black" d="M996.51,-474.51C992.58,-438.66 983.84,-358.73 978.96,-314.18"/> +<polygon fill="black" stroke="black" points="982.45,-313.94 977.89,-304.38 975.5,-314.7 982.45,-313.94"/> +</g> +<!-- MEM_ALIASDOTS --> +<g id="node32" class="node"> +<title>MEM_ALIASDOTS</title> +<ellipse fill="blue" stroke="black" cx="1036.51" cy="-282.65" rx="27" ry="18"/> +<text text-anchor="middle" x="1036.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text> +</g> +<!-- MEM_TOP->MEM_ALIASDOTS --> +<g id="edge48" class="edge"> +<title>MEM_TOP->MEM_ALIASDOTS</title> +<path fill="none" stroke="black" d="M1001.81,-474.51C1008.41,-438.02 1023.27,-355.86 1031.23,-311.83"/> +<polygon fill="black" stroke="black" points="1034.63,-312.69 1032.97,-302.23 1027.74,-311.45 1034.63,-312.69"/> +</g> +<!-- MEM_ALIASN --> +<g id="node33" class="node"> +<title>MEM_ALIASN</title> +<ellipse fill="blue" stroke="black" cx="1097.51" cy="-282.65" rx="20.13" ry="20.13"/> +<text text-anchor="middle" x="1097.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#n</text> +</g> +<!-- MEM_TOP->MEM_ALIASN --> +<g id="edge49" class="edge"> +<title>MEM_TOP->MEM_ALIASN</title> +<path fill="none" stroke="black" d="M1006.95,-474.88C1024.11,-438.46 1063.18,-355.52 1083.95,-311.44"/> +<polygon fill="black" stroke="black" points="1087.07,-313.03 1088.16,-302.49 1080.73,-310.05 1087.07,-313.03"/> +</g> +<!-- MEM_BOT --> +<g id="node30" class="node"> +<title>MEM_BOT</title> +<ellipse fill="blue" stroke="black" cx="998.51" cy="-82.75" rx="43.67" ry="18"/> +<text text-anchor="start" x="974.51" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text> +<text text-anchor="start" x="990.26" y="-78.7" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text> +</g> +<!-- MEM_BOT->BOT --> +<g id="edge53" class="edge"> +<title>MEM_BOT->BOT</title> +<path fill="none" stroke="black" d="M958.6,-75.08C887.17,-63.15 738.86,-38.38 673.56,-27.48"/> +<polygon fill="black" stroke="black" points="674.3,-24.05 663.86,-25.86 673.15,-30.96 674.3,-24.05"/> +</g> +<!-- MEM_ALIAS1->MEM_BOT --> +<g id="edge50" class="edge"> +<title>MEM_ALIAS1->MEM_BOT</title> +<path fill="none" stroke="black" d="M977.84,-262.38C981.92,-226.96 990.35,-153.62 995.09,-112.45"/> +<polygon fill="black" stroke="black" points="998.57,-112.87 996.23,-102.53 991.61,-112.07 998.57,-112.87"/> +</g> +<!-- MEM_ALIAS1->MEM_ALIASDOTS --> +<!-- MEM_ALIASDOTS->MEM_BOT --> +<g id="edge51" class="edge"> +<title>MEM_ALIASDOTS->MEM_BOT</title> +<path fill="none" stroke="black" d="M1033.07,-264.56C1026.49,-229.93 1012.06,-154.01 1004.08,-112.06"/> +<polygon fill="black" stroke="black" points="1007.56,-111.6 1002.25,-102.43 1000.68,-112.91 1007.56,-111.6"/> +</g> +<!-- MEM_ALIASDOTS->MEM_ALIASN --> +<!-- MEM_ALIASN->MEM_BOT --> +<g id="edge52" class="edge"> +<title>MEM_ALIASN->MEM_BOT</title> +<path fill="none" stroke="black" d="M1088.37,-264.2C1070.97,-229.07 1032.98,-152.35 1012.41,-110.82"/> +<polygon fill="black" stroke="black" points="1015.58,-109.34 1008.01,-101.93 1009.31,-112.45 1015.58,-109.34"/> +</g> +<!-- TUPLE1 --> +<g id="node36" class="node"> +<title>TUPLE1</title> +<polygon fill="none" stroke="none" points="1185.51,-309.15 1131.51,-309.15 1131.51,-256.15 1185.51,-256.15 1185.51,-309.15"/> +<polygon fill="pink" stroke="none" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/> +<polygon fill="none" stroke="black" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/> +<text text-anchor="start" x="1142.76" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">Tuple</text> +<polygon fill="black" stroke="none" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/> +<polygon fill="none" stroke="black" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/> +<text text-anchor="start" x="1146.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +</g> +<!-- TUPLE_TOP->TUPLE1 --> +<g id="edge57" class="edge"> +<title>TUPLE_TOP->TUPLE1</title> +<path fill="none" stroke="black" d="M1180.42,-474.51C1176.51,-440.23 1167.99,-365.66 1162.8,-320.24"/> +<polygon fill="black" stroke="black" points="1166.28,-319.86 1161.67,-310.32 1159.33,-320.65 1166.28,-319.86"/> +</g> +<!-- TUPLE2 --> +<g id="node37" class="node"> +<title>TUPLE2</title> +<polygon fill="none" stroke="none" points="1253.51,-320.4 1199.51,-320.4 1199.51,-244.9 1253.51,-244.9 1253.51,-320.4"/> +<polygon fill="pink" stroke="none" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/> +<polygon fill="none" stroke="black" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/> +<text text-anchor="start" x="1210.76" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">Tuple</text> +<polygon fill="black" stroke="none" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/> +<polygon fill="none" stroke="black" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/> +<text text-anchor="start" x="1214.88" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +<polygon fill="black" stroke="none" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/> +<polygon fill="none" stroke="black" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/> +<text text-anchor="start" x="1214.88" y="-255.1" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +</g> +<!-- TUPLE_TOP->TUPLE2 --> +<g id="edge58" class="edge"> +<title>TUPLE_TOP->TUPLE2</title> +<path fill="none" stroke="black" d="M1186.34,-474.51C1192.95,-442.94 1206.72,-377.19 1216.28,-331.51"/> +<polygon fill="black" stroke="black" points="1219.68,-332.35 1218.3,-321.85 1212.83,-330.92 1219.68,-332.35"/> +</g> +<!-- TUPLEN --> +<g id="node38" class="node"> +<title>TUPLEN</title> +<polygon fill="none" stroke="none" points="1321.51,-331.65 1267.51,-331.65 1267.51,-233.65 1321.51,-233.65 1321.51,-331.65"/> +<polygon fill="pink" stroke="none" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/> +<polygon fill="none" stroke="black" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/> +<text text-anchor="start" x="1278.76" y="-311.35" font-family="Times New Roman,serif" font-size="14.00">Tuple</text> +<polygon fill="black" stroke="none" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/> +<polygon fill="none" stroke="black" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/> +<text text-anchor="start" x="1282.88" y="-288.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +<polygon fill="black" stroke="none" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/> +<polygon fill="none" stroke="black" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/> +<text text-anchor="start" x="1288.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text> +<polygon fill="black" stroke="none" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/> +<polygon fill="none" stroke="black" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/> +<text text-anchor="start" x="1282.88" y="-243.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text> +</g> +<!-- TUPLE_TOP->TUPLEN --> +<g id="edge59" class="edge"> +<title>TUPLE_TOP->TUPLEN</title> +<path fill="none" stroke="black" d="M1192.06,-474.88C1207.65,-445.62 1239.24,-386.36 1263.24,-341.32"/> +<polygon fill="black" stroke="black" points="1266.22,-343.18 1267.83,-332.71 1260.04,-339.88 1266.22,-343.18"/> +</g> +<!-- TUPLE_BOT --> +<g id="node35" class="node"> +<title>TUPLE_BOT</title> +<ellipse fill="pink" stroke="black" cx="1192.51" cy="-82.75" rx="42.65" ry="18"/> +<text text-anchor="start" x="1169.26" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text> +<text text-anchor="start" x="1185.01" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:tuple</text> +</g> +<!-- TUPLE_BOT->BOT --> +<g id="edge63" class="edge"> +<title>TUPLE_BOT->BOT</title> +<path fill="none" stroke="black" d="M1151.54,-77.32C1046.53,-65.95 769.32,-35.95 674.14,-25.65"/> +<polygon fill="black" stroke="black" points="674.52,-22.18 664.2,-24.58 673.77,-29.14 674.52,-22.18"/> +</g> +<!-- TUPLE1->TUPLE_BOT --> +<g id="edge60" class="edge"> +<title>TUPLE1->TUPLE_BOT</title> +<path fill="none" stroke="black" d="M1162.95,-256.52C1169.28,-219.3 1180.86,-151.25 1187.49,-112.26"/> +<polygon fill="black" stroke="black" points="1190.9,-113.1 1189.12,-102.66 1184,-111.93 1190.9,-113.1"/> +</g> +<!-- TUPLE2->TUPLE_BOT --> +<g id="edge61" class="edge"> +<title>TUPLE2->TUPLE_BOT</title> +<path fill="none" stroke="black" d="M1220.11,-245.05C1213.55,-206.44 1203.46,-147.16 1197.47,-111.94"/> +<polygon fill="black" stroke="black" points="1200.99,-111.74 1195.86,-102.47 1194.09,-112.92 1200.99,-111.74"/> +</g> +<!-- TUPLEN->TUPLE_BOT --> +<g id="edge62" class="edge"> +<title>TUPLEN->TUPLE_BOT</title> +<path fill="none" stroke="black" d="M1269.56,-233.75C1249.85,-195.12 1223.04,-142.58 1206.8,-110.75"/> +<polygon fill="black" stroke="black" points="1210.07,-109.46 1202.41,-102.15 1203.83,-112.65 1210.07,-109.46"/> +</g> +</g> +</svg> diff --git a/chapter25/docs/lattice2.gv b/chapter25/docs/lattice2.gv new file mode 100644 index 00000000..6aa37fa0 --- /dev/null +++ b/chapter25/docs/lattice2.gv @@ -0,0 +1,85 @@ +digraph Lattice { + compound = true; + // Define nodes + splines=false; + node [shape=oval, style=filled]; + nodesep=0.2; + ranksep=0.4; + + TOP [label="~scalar\n+int+tmp+0", fillcolor=black, fontcolor=white, group=center]; + + // ------------------------ + PTRHQ [label="~tmp\n+tmp+0", fillcolor=green, fontcolor=white, group=left]; + TOP -> PTRHQ + + VALH_ [label="~nscalar\n+int+tmp", fillcolor=white, group=center]; + TOP -> VALH_ + + INTHQ [label="~int\n+int+0", fillcolor=lightblue, group=right]; + TOP -> INTHQ + + LEFTHI [label="junk", style=invis, group=junkleft] + + + PTRHQ -> LEFTHI-> VALH_ -> INTHQ [style=invis]; + { rank=same; PTRHQ; LEFTHI; VALH_; INTHQ } + + // ------------------------ + PTRH_ [label="~ntmp\n+tmp", fillcolor=green, fontcolor=white, group=left]; + VALH_ -> PTRH_ + PTRHQ -> PTRH_ + + INTH_ [label="~nint\n+int", fillcolor=lightblue, group=right]; + VALH_ -> INTH_ + INTHQ -> INTH_ + { rank=same; PTRH_; INTH_ } + + // ------------------------ + NILX_ [label="~nil\n[int,tmp]+0", fillcolor=aqua, group=junkleft]; + INTHQ -> NILX_ + PTRHQ -> NILX_ + + NIL__ [label="nil\n[+int+tmp],0", fillcolor=pink]; + VALH_ -> NIL__; + + NILX_ -> NIL__ [style=invis]; + { rank=same; NIL__; NILX_ } + + // ------------------------ + PTRL_ [label="ntmp\ntmp", fillcolor=green, fontcolor=white, group=left]; + PTRH_ -> PTRL_ + + INTL_ [label="nint\nint", fillcolor=lightblue, group=right]; + INTH_ -> INTL_ + { rank=same; PTRL_; INTL_ } + + // ------------------------ + PTRL0 [label="tmp\ntmp,0", fillcolor=green, fontcolor=white, group=left]; + PTRL_ -> PTRL0 + NIL__ -> PTRL0 + + VALL_ [label="nscalar\nint,tmp", fillcolor=white, group=center]; + PTRL_ -> VALL_ + INTL_ -> VALL_ + NILX_ -> VALL_ + + INTL0 [label="int\nint,0", fillcolor=lightblue, group=right]; + INTL_ -> INTL0 + NIL__ -> INTL0 + + PTRL0 -> VALL_ -> INTL0 [style=invis]; + { rank=same; PTRL0; VALL_; INTL0 } + + // ------------------------ + BOT [label="scalar\nint,tmp,0", fillcolor=black, fontcolor=white, group=center]; + PTRL0 -> BOT + INTL0 -> BOT + VALL_ -> BOT + + // ------------------------ + // Verticle alignment + TOP -> VALH_ -> VALL_ -> BOT [style=invis]; + PTRHQ -> PTRH_ -> PTRL_ -> PTRL0 [style=invis]; + INTHQ -> INTH_ -> INTL_ -> INTL0 [style=invis]; + LEFTHI -> NILX_ [style=invis] +} diff --git a/chapter25/docs/lattice2.svg b/chapter25/docs/lattice2.svg new file mode 100644 index 00000000..8b682aa7 --- /dev/null +++ b/chapter25/docs/lattice2.svg @@ -0,0 +1,260 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 12.2.0 (20241103.1931) + --> +<!-- Title: Lattice Pages: 1 --> +<svg width="478pt" height="501pt" + viewBox="0.00 0.00 477.96 500.90" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 496.9)"> +<title>Lattice</title> +<polygon fill="white" stroke="none" points="-4,4 -4,-496.9 473.96,-496.9 473.96,4 -4,4"/> +<!-- TOP --> +<g id="node1" class="node"> +<title>TOP</title> +<ellipse fill="black" stroke="black" cx="240.13" cy="-463.91" rx="59.04" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-467.11" font-family="Times New Roman,serif" font-size="14.00" fill="white">~scalar</text> +<text text-anchor="middle" x="240.13" y="-450.61" font-family="Times New Roman,serif" font-size="14.00" fill="white">+int+tmp+0</text> +</g> +<!-- PTRHQ --> +<g id="node2" class="node"> +<title>PTRHQ</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-376.92" rx="43.13" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00" fill="white">~tmp</text> +<text text-anchor="middle" x="43.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp+0</text> +</g> +<!-- TOP->PTRHQ --> +<g id="edge1" class="edge"> +<title>TOP->PTRHQ</title> +<path fill="none" stroke="black" d="M196.41,-444.04C164.74,-430.38 121.99,-411.94 89.68,-398"/> +<polygon fill="black" stroke="black" points="91.11,-394.81 80.55,-394.06 88.34,-401.24 91.11,-394.81"/> +</g> +<!-- VALH_ --> +<g id="node3" class="node"> +<title>VALH_</title> +<ellipse fill="white" stroke="black" cx="240.13" cy="-376.92" rx="48.44" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~nscalar</text> +<text text-anchor="middle" x="240.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+tmp</text> +</g> +<!-- TOP->VALH_ --> +<g id="edge2" class="edge"> +<title>TOP->VALH_</title> +<path fill="none" stroke="black" d="M235.15,-434.61C234.93,-429.02 234.85,-423.06 234.9,-417.18"/> +<polygon fill="black" stroke="black" points="238.39,-417.69 235.12,-407.61 231.39,-417.52 238.39,-417.69"/> +</g> +<!-- TOP->VALH_ --> +<!-- INTHQ --> +<g id="node4" class="node"> +<title>INTHQ</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-376.92" rx="37.83" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~int</text> +<text text-anchor="middle" x="432.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+0</text> +</g> +<!-- TOP->INTHQ --> +<g id="edge3" class="edge"> +<title>TOP->INTHQ</title> +<path fill="none" stroke="black" d="M283.22,-443.83C314.91,-429.81 357.78,-410.83 389.39,-396.84"/> +<polygon fill="black" stroke="black" points="390.53,-400.16 398.26,-392.92 387.7,-393.76 390.53,-400.16"/> +</g> +<!-- LEFTHI --> +<!-- PTRHQ->LEFTHI --> +<!-- PTRH_ --> +<g id="node6" class="node"> +<title>PTRH_</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-289.94" rx="36.77" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00" fill="white">~ntmp</text> +<text text-anchor="middle" x="43.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp</text> +</g> +<!-- PTRHQ->PTRH_ --> +<g id="edge8" class="edge"> +<title>PTRHQ->PTRH_</title> +<path fill="none" stroke="black" d="M38.15,-347.62C37.93,-342.03 37.85,-336.07 37.9,-330.2"/> +<polygon fill="black" stroke="black" points="41.39,-330.71 38.12,-320.63 34.39,-330.54 41.39,-330.71"/> +</g> +<!-- PTRHQ->PTRH_ --> +<!-- NILX_ --> +<g id="node8" class="node"> +<title>NILX_</title> +<ellipse fill="aqua" stroke="black" cx="150.13" cy="-289.94" rx="56.39" ry="28.99"/> +<text text-anchor="middle" x="150.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nil</text> +<text text-anchor="middle" x="150.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[int,tmp]+0</text> +</g> +<!-- PTRHQ->NILX_ --> +<g id="edge12" class="edge"> +<title>PTRHQ->NILX_</title> +<path fill="none" stroke="black" d="M70.41,-354.26C82.91,-344.33 97.94,-332.4 111.56,-321.58"/> +<polygon fill="black" stroke="black" points="113.43,-324.56 119.09,-315.6 109.08,-319.08 113.43,-324.56"/> +</g> +<!-- VALH_->INTHQ --> +<!-- VALH_->PTRH_ --> +<g id="edge7" class="edge"> +<title>VALH_->PTRH_</title> +<path fill="none" stroke="black" d="M201.21,-359.13C167.78,-344.71 119.68,-323.96 85.33,-309.14"/> +<polygon fill="black" stroke="black" points="87.1,-306.09 76.53,-305.35 84.33,-312.52 87.1,-306.09"/> +</g> +<!-- INTH_ --> +<g id="node7" class="node"> +<title>INTH_</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-289.94" rx="31.47" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nint</text> +<text text-anchor="middle" x="432.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">+int</text> +</g> +<!-- VALH_->INTH_ --> +<g id="edge9" class="edge"> +<title>VALH_->INTH_</title> +<path fill="none" stroke="black" d="M278.53,-358.93C311.9,-344.16 360.02,-322.86 393.48,-308.05"/> +<polygon fill="black" stroke="black" points="394.67,-311.35 402.4,-304.1 391.84,-304.95 394.67,-311.35"/> +</g> +<!-- NIL__ --> +<g id="node9" class="node"> +<title>NIL__</title> +<ellipse fill="pink" stroke="black" cx="324.13" cy="-289.94" rx="62.23" ry="28.99"/> +<text text-anchor="middle" x="324.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">nil</text> +<text text-anchor="middle" x="324.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[+int+tmp],0</text> +</g> +<!-- VALH_->NIL__ --> +<g id="edge13" class="edge"> +<title>VALH_->NIL__</title> +<path fill="none" stroke="black" d="M264.43,-351.34C272.67,-343.01 282.01,-333.55 290.83,-324.64"/> +<polygon fill="black" stroke="black" points="293.12,-327.3 297.66,-317.72 288.14,-322.38 293.12,-327.3"/> +</g> +<!-- VALL_ --> +<g id="node13" class="node"> +<title>VALL_</title> +<ellipse fill="white" stroke="black" cx="240.13" cy="-115.97" rx="39.42" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">nscalar</text> +<text text-anchor="middle" x="240.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,tmp</text> +</g> +<!-- VALH_->VALL_ --> +<!-- INTHQ->INTH_ --> +<g id="edge10" class="edge"> +<title>INTHQ->INTH_</title> +<path fill="none" stroke="black" d="M427.17,-348.1C426.94,-342.37 426.85,-336.25 426.9,-330.21"/> +<polygon fill="black" stroke="black" points="430.39,-330.42 427.13,-320.34 423.39,-330.26 430.39,-330.42"/> +</g> +<!-- INTHQ->INTH_ --> +<!-- INTHQ->NILX_ --> +<g id="edge11" class="edge"> +<title>INTHQ->NILX_</title> +<path fill="none" stroke="black" d="M396.99,-365.33C350,-351.17 266.03,-325.87 209.23,-308.75"/> +<polygon fill="black" stroke="black" points="210.28,-305.41 199.69,-305.88 208.26,-312.11 210.28,-305.41"/> +</g> +<!-- LEFTHI->VALH_ --> +<!-- LEFTHI->NILX_ --> +<!-- PTRL_ --> +<g id="node10" class="node"> +<title>PTRL_</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-202.96" rx="31.47" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00" fill="white">ntmp</text> +<text text-anchor="middle" x="43.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text> +</g> +<!-- PTRH_->PTRL_ --> +<g id="edge15" class="edge"> +<title>PTRH_->PTRL_</title> +<path fill="none" stroke="black" d="M38.17,-261.11C37.94,-255.39 37.85,-249.26 37.9,-243.22"/> +<polygon fill="black" stroke="black" points="41.39,-243.44 38.13,-233.36 34.39,-243.27 41.39,-243.44"/> +</g> +<!-- PTRH_->PTRL_ --> +<!-- INTL_ --> +<g id="node11" class="node"> +<title>INTL_</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-202.96" rx="27" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00">nint</text> +<text text-anchor="middle" x="432.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00">int</text> +</g> +<!-- INTH_->INTL_ --> +<g id="edge16" class="edge"> +<title>INTH_->INTL_</title> +<path fill="none" stroke="black" d="M427.17,-261.11C426.94,-255.39 426.85,-249.26 426.9,-243.22"/> +<polygon fill="black" stroke="black" points="430.39,-243.44 427.13,-233.36 423.39,-243.27 430.39,-243.44"/> +</g> +<!-- INTH_->INTL_ --> +<!-- NILX_->NIL__ --> +<!-- NILX_->VALL_ --> +<g id="edge21" class="edge"> +<title>NILX_->VALL_</title> +<path fill="none" stroke="black" d="M164.79,-261.6C180.03,-232.15 203.95,-185.91 220.74,-153.45"/> +<polygon fill="black" stroke="black" points="223.74,-155.27 225.23,-144.78 217.53,-152.06 223.74,-155.27"/> +</g> +<!-- PTRL0 --> +<g id="node12" class="node"> +<title>PTRL0</title> +<ellipse fill="green" stroke="black" cx="43.13" cy="-115.97" rx="34.12" ry="28.99"/> +<text text-anchor="middle" x="43.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text> +<text text-anchor="middle" x="43.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp,0</text> +</g> +<!-- NIL__->PTRL0 --> +<g id="edge18" class="edge"> +<title>NIL__->PTRL0</title> +<path fill="none" stroke="black" d="M286.24,-266.48C232.36,-233.13 134.24,-172.38 80.6,-139.17"/> +<polygon fill="black" stroke="black" points="82.76,-136.39 72.41,-134.1 79.07,-142.34 82.76,-136.39"/> +</g> +<!-- INTL0 --> +<g id="node14" class="node"> +<title>INTL0</title> +<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-115.97" rx="28.81" ry="28.99"/> +<text text-anchor="middle" x="432.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">int</text> +<text text-anchor="middle" x="432.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,0</text> +</g> +<!-- NIL__->INTL0 --> +<g id="edge23" class="edge"> +<title>NIL__->INTL0</title> +<path fill="none" stroke="black" d="M341.49,-261.99C360.34,-231.61 390.49,-183.05 410.79,-150.35"/> +<polygon fill="black" stroke="black" points="413.65,-152.38 415.95,-142.04 407.7,-148.69 413.65,-152.38"/> +</g> +<!-- PTRL_->PTRL0 --> +<g id="edge17" class="edge"> +<title>PTRL_->PTRL0</title> +<path fill="none" stroke="black" d="M38.17,-174.13C37.94,-168.47 37.85,-162.41 37.9,-156.43"/> +<polygon fill="black" stroke="black" points="41.39,-156.75 38.12,-146.67 34.39,-156.58 41.39,-156.75"/> +</g> +<!-- PTRL_->PTRL0 --> +<!-- PTRL_->VALL_ --> +<g id="edge19" class="edge"> +<title>PTRL_->VALL_</title> +<path fill="none" stroke="black" d="M71.36,-189.78C103.94,-175.73 157.92,-152.44 196.04,-135.99"/> +<polygon fill="black" stroke="black" points="197.16,-139.32 204.96,-132.15 194.39,-132.9 197.16,-139.32"/> +</g> +<!-- INTL_->VALL_ --> +<g id="edge20" class="edge"> +<title>INTL_->VALL_</title> +<path fill="none" stroke="black" d="M407.43,-191.02C376.23,-177.21 322.01,-153.22 283.77,-136.29"/> +<polygon fill="black" stroke="black" points="285.39,-133.18 274.83,-132.33 282.55,-139.58 285.39,-133.18"/> +</g> +<!-- INTL_->INTL0 --> +<g id="edge22" class="edge"> +<title>INTL_->INTL0</title> +<path fill="none" stroke="black" d="M427.17,-174.13C426.94,-168.41 426.85,-162.28 426.9,-156.24"/> +<polygon fill="black" stroke="black" points="430.39,-156.46 427.13,-146.38 423.39,-156.29 430.39,-156.46"/> +</g> +<!-- INTL_->INTL0 --> +<!-- PTRL0->VALL_ --> +<!-- BOT --> +<g id="node15" class="node"> +<title>BOT</title> +<ellipse fill="black" stroke="black" cx="240.13" cy="-28.99" rx="46.85" ry="28.99"/> +<text text-anchor="middle" x="240.13" y="-32.19" font-family="Times New Roman,serif" font-size="14.00" fill="white">scalar</text> +<text text-anchor="middle" x="240.13" y="-15.69" font-family="Times New Roman,serif" font-size="14.00" fill="white">int,tmp,0</text> +</g> +<!-- PTRL0->BOT --> +<g id="edge26" class="edge"> +<title>PTRL0->BOT</title> +<path fill="none" stroke="black" d="M73.48,-101.88C105.02,-88.28 154.83,-66.79 191.79,-50.85"/> +<polygon fill="black" stroke="black" points="193.14,-54.08 200.94,-46.9 190.37,-47.65 193.14,-54.08"/> +</g> +<!-- VALL_->INTL0 --> +<!-- VALL_->BOT --> +<g id="edge28" class="edge"> +<title>VALL_->BOT</title> +<path fill="none" stroke="black" d="M235.17,-87.15C234.94,-81.48 234.85,-75.42 234.9,-69.44"/> +<polygon fill="black" stroke="black" points="238.39,-69.76 235.12,-59.68 231.39,-69.6 238.39,-69.76"/> +</g> +<!-- VALL_->BOT --> +<!-- INTL0->BOT --> +<g id="edge27" class="edge"> +<title>INTL0->BOT</title> +<path fill="none" stroke="black" d="M405.84,-103.34C375.52,-89.91 325.06,-67.58 287.78,-51.08"/> +<polygon fill="black" stroke="black" points="289.59,-48.05 279.03,-47.21 286.75,-54.45 289.59,-48.05"/> +</g> +</g> +</svg> diff --git a/chapter25/docs/pardoe.docx b/chapter25/docs/pardoe.docx Binary files differnew file mode 100644 index 00000000..24fde921 --- /dev/null +++ b/chapter25/docs/pardoe.docx diff --git a/chapter25/docs/webtcp.min.js b/chapter25/docs/webtcp.min.js new file mode 100644 index 00000000..bf23ee5a --- /dev/null +++ b/chapter25/docs/webtcp.min.js @@ -0,0 +1,65 @@ +function Http(j){this.cID=Math.random().toString(36).substr(2);this.webtcp=j;this.webtcp.httpClients[this.cID]=this;this.createPacket=function(){return{cID:this.cID,type:"http",host:this.remoteAddress,port:this.remotePort,method:null,data:null}};this.ondata=function(){};this.get=function(g,c){this.on("data",c);this.rpc("httpGet",[g])};this.post=function(g,c,j){this.on("data",j);this.rpc("httpPost",[g,c])}}Http.prototype=new WebTCPIO;function Socket(j,g,c,p){this.sID=Math.random().toString(36).substr(2);this.webtcp=j;this.closed=this.ready=!1;this.options=p;this.sockOpts={_updating:!1};this.remoteAddress=g;this.remotePort=c;this.webtcp.sockets[this.sID]=this;this.createPacket=function(){return{id:Math.random().toString(36).substr(2),sID:this.sID,type:"tcp",options:this.options,host:this.remoteAddress,port:this.remotePort,data:null}};this.onconnect=function(){this.webtcp.sockets[sID].ready=!0;this.webtcp.sockets[sID].closed=!1}; +this.ondata=function(){};this.onend=function(){};this.ontimeout=function(){};this.ondrain=function(){};this.onclose=function(){this.webtcp.sockets[sID].ready=!1;this.webtcp.sockets[sID].closed=!0};this.getSockOpts=function(){this.sockOpts._updating=!0;this.rpc("getSockOpts")};this.onSockOptsRcv=function(c){this.sockOpts=c;this.sockOpts._updating=!1}}Socket.prototype=new WebTCPIO;var JSON;JSON||(JSON={}); +(function(){function j(c,p){var q,t,n,x,i=k,e,l=p[c];l&&"object"==typeof l&&"function"==typeof l.toJSON&&(l=l.toJSON(c));"function"==typeof r&&(l=r.call(p,c,l));switch(typeof l){case "string":return g(l);case "number":return isFinite(l)?String(l):"null";case "boolean":case "null":return String(l);case "object":if(!l)return"null";k+=v;e=[];if("[object Array]"===Object.prototype.toString.apply(l)){x=l.length;for(q=0;q<x;q+=1)e[q]=j(q,l)||"null";n=0===e.length?"[]":k?"[\n"+k+e.join(",\n"+k)+"\n"+i+"]": +"["+e.join(",")+"]";k=i;return n}if(r&&"object"==typeof r){x=r.length;for(q=0;q<x;q+=1)"string"==typeof r[q]&&(t=r[q],n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n))}else for(t in l)Object.prototype.hasOwnProperty.call(l,t)&&(n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n));n=0===e.length?"{}":k?"{\n"+k+e.join(",\n"+k)+"\n"+i+"}":"{"+e.join(",")+"}";k=i;return n}}function g(c){n.lastIndex=0;return n.test(c)?'"'+c.replace(n,function(c){var g=D[c];return"string"==typeof g?g:"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)})+ +'"':'"'+c+'"'}function c(c){return 10>c?"0"+c:c}"use strict";"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+c(this.getUTCMonth()+1)+"-"+c(this.getUTCDate())+"T"+c(this.getUTCHours())+":"+c(this.getUTCMinutes())+":"+c(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var p=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +n=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,k,v,D={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},r;"function"!=typeof JSON.stringify&&(JSON.stringify=function(c,g,p){var n;v=k="";if("number"==typeof p)for(n=0;n<p;n+=1)v+=" ";else"string"==typeof p&&(v=p);r=g;if(!g||"function"==typeof g||"object"==typeof g&&"number"==typeof g.length)return j("",{"":c});throw Error("JSON.stringify");});"function"!= +typeof JSON.parse&&(JSON.parse=function(c,g){function j(c,k){var i,e,l=c[k];if(l&&"object"==typeof l)for(i in l)Object.prototype.hasOwnProperty.call(l,i)&&(e=j(l,i),void 0!==e?l[i]=e:delete l[i]);return g.call(c,k,l)}var k,c=String(c);p.lastIndex=0;p.test(c)&&(c=c.replace(p,function(c){return"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(c.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, +"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return k=eval("("+c+")"),"function"==typeof g?j({"":k},""):k;throw new SyntaxError("JSON.parse");})})(); +SockJS=function(){var j=document,g=window,c={},p=function(){};p.prototype.addEventListener=function(a,b){this._listeners||(this._listeners={});a in this._listeners||(this._listeners[a]=[]);var f=this._listeners[a];-1===c.arrIndexOf(f,b)&&f.push(b)};p.prototype.removeEventListener=function(a,b){if(this._listeners&&a in this._listeners){var f=this._listeners[a],d=c.arrIndexOf(f,b);-1!==d&&(1<f.length?this._listeners[a]=f.slice(0,d).concat(f.slice(d+1)):delete this._listeners[a])}};p.prototype.dispatchEvent= +function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d<this._listeners[b].length;d++)this._listeners[b][d].apply(this,c)};var n=function(a,b){this.type=a;if("undefined"!=typeof b)for(var c in b)b.hasOwnProperty(c)&&(this[c]=b[c])};n.prototype.toString=function(){var a=[],b;for(b in this)if(this.hasOwnProperty(b)){var c=this[b];"function"==typeof c&&(c="[function]");a.push(b+"="+c)}return"SimpleEvent("+ +a.join(", ")+")"};var k=function(a){this.events=a||[]};k.prototype.emit=function(a){var b=Array.prototype.slice.call(arguments,1);!this.nuked&&this["on"+a]&&this["on"+a].apply(this,b);-1===c.arrIndexOf(this.events,a)&&c.log("Event "+JSON.stringify(a)+" not listed "+JSON.stringify(this.events)+" in "+this)};k.prototype.nuke=function(){this.nuked=!0;for(var a=0;a<this.events.length;a++)delete this[this.events[a]]};c.random_string=function(a,b){var b=b||37,c,d=[];for(c=0;c<a;c++)d.push("abcdefghijklmnopqrstuvwxyz0123456789_".substr(Math.floor(Math.random()* +b),1));return d.join("")};c.random_number=function(a){return Math.floor(Math.random()*a)};c.random_number_string=function(a){var b=(""+(a-1)).length;return(Array(b+1).join("0")+c.random_number(a)).slice(-b)};c.getOrigin=function(a){return(a+"/").split("/").slice(0,3).join("/")};c.isSameOriginUrl=function(a,b){return b||(b=g.location.href),a.split("/").slice(0,3).join("/")===b.split("/").slice(0,3).join("/")};c.getParentDomain=function(a){return/^[0-9.]*$/.test(a)||/^\[/.test(a)||!/[.]/.test(a)?a: +a.split(".").slice(1).join(".")};c.objectExtend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a};c.polluteGlobalNamespace=function(){"_jp"in g||(g._jp={})};c.closeFrame=function(a,b){return"c"+JSON.stringify([a,b])};c.userSetCode=function(a){return 1E3===a||3E3<=a&&4999>=a};c.countRTO=function(a){var b;return 100<a?b=3*a:b=a+200,b};c.log=function(){g.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)};c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b, +arguments)}};c.flatUrl=function(a){return-1===a.indexOf("?")&&-1===a.indexOf("#")};c.amendUrl=function(a){var b=j.location;if(!a)throw Error("Wrong url for SockJS");if(!c.flatUrl(a))throw Error("Only basic urls are supported in SockJS");return 0===a.indexOf("//")&&(a=b.protocol+a),0===a.indexOf("/")&&(a=b.protocol+"//"+b.host+a),a=a.replace(/[/]+$/,""),a};c.arrIndexOf=function(a,b){for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1};c.arrSkip=function(a,b){var f=c.arrIndexOf(a,b);return-1=== +f?a.slice():a.slice(0,f).concat(a.slice(f+1))};c.isArray=Array.isArray||function(a){return 0<={}.toString.call(a).indexOf("Array")};c.delay=function(a,b){return"function"==typeof a&&(b=a,a=0),setTimeout(b,a)};var v=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,D={"\x00":"\\u0000","\u0001":"\\u0001","\u0002":"\\u0002","\u0003":"\\u0003","\u0004":"\\u0004","\u0005":"\\u0005","\u0006":"\\u0006","\u0007":"\\u0007","\b":"\\b", +"\t":"\\t","\n":"\\n","\x0B":"\\u000b","\f":"\\f","\r":"\\r","\u000e":"\\u000e","\u000f":"\\u000f","\u0010":"\\u0010","\u0011":"\\u0011","\u0012":"\\u0012","\u0013":"\\u0013","\u0014":"\\u0014","\u0015":"\\u0015","\u0016":"\\u0016","\u0017":"\\u0017","\u0018":"\\u0018","\u0019":"\\u0019","\u001a":"\\u001a","\u001b":"\\u001b","\u001c":"\\u001c","\u001d":"\\u001d","\u001e":"\\u001e","\u001f":"\\u001f",'"':'\\"',"\\":"\\\\","\u007f":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082", +"\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086","\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a","\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e","\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092","\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096","\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a","\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d", +"\u009e":"\\u009e","\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063", +"\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd", +"\ufffe":"\\ufffe","\uffff":"\\uffff"},r=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g, +B,M=JSON&&JSON.stringify||function(a){return v.lastIndex=0,v.test(a)&&(a=a.replace(v,function(a){return D[a]})),'"'+a+'"'};c.quote=function(a){a=M(a);r.lastIndex=0;if(r.test(a)){if(!B){var b,c={},d=[];for(b=0;65536>b;b++)d.push(String.fromCharCode(b));B=(r.lastIndex=0,d.join("").replace(r,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),r.lastIndex=0,c)}a=a.replace(r,function(a){return B[a]})}return a};var q="websocket xdr-streaming xhr-streaming iframe-eventsource iframe-htmlfile xdr-polling xhr-polling iframe-xhr-polling jsonp-polling".split(" "); +c.probeProtocols=function(){for(var a={},b=0;b<q.length;b++){var c=q[b];a[c]=e[c]&&e[c].enabled()}return a};c.detectProtocols=function(a,b,c){var d={},h=[];b||(b=q);for(var m=0;m<b.length;m++){var g=b[m];d[g]=a[g]}var e=function(a){var b=a.shift();d[b]?h.push(b):0<a.length&&e(a)};return!1!==c.websocket&&e(["websocket"]),d["xhr-streaming"]&&!c.null_origin?h.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-streaming"):e(["iframe-eventsource","iframe-htmlfile"]), +d["xhr-polling"]&&!c.null_origin?h.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-polling"):e(["iframe-xhr-polling","jsonp-polling"]),h};c.createHook=function(){var a="a"+c.random_string(8);if(!("_sockjs_global"in g)){var b={};g._sockjs_global=function(a){return a in b||(b[a]={id:a,del:function(){delete b[a]}}),b[a]}}return g._sockjs_global(a)};c.attachMessage=function(a){c.attachEvent("message",a)};c.attachEvent=function(a,b){"undefined"!=typeof g.addEventListener? +g.addEventListener(a,b,!1):(j.attachEvent("on"+a,b),g.attachEvent("on"+a,b))};c.detachMessage=function(a){c.detachEvent("message",a)};c.detachEvent=function(a,b){"undefined"!=typeof g.addEventListener?g.removeEventListener(a,b,!1):(j.detachEvent("on"+a,b),g.detachEvent("on"+a,b))};var t={},C=!1,x=function(){for(var a in t)t[a](),delete t[a]},i=function(){C||(C=!0,x())};c.attachEvent("beforeunload",i);c.attachEvent("unload",i);c.unload_add=function(a){var b=c.random_string(8);return t[b]=a,C&&c.delay(x), +b};c.unload_del=function(a){a in t&&delete t[a]};c.createIframe=function(a,b){var f=j.createElement("iframe"),d,h,m=function(){clearTimeout(d);try{f.onload=null}catch(a){}f.onerror=null},g=function(){f&&(m(),setTimeout(function(){f&&f.parentNode.removeChild(f);f=null},0),c.unload_del(h))};return f.src=a,f.style.display="none",f.style.position="absolute",f.onerror=function(){f&&(g(),b("onerror"))},f.onload=function(){clearTimeout(d);d=setTimeout(function(){f&&(g(),b("onload timeout"))},2E3)},j.body.appendChild(f), +d=setTimeout(function(){f&&(g(),b("timeout"))},15E3),h=c.unload_add(g),{post:function(a,b){try{f&&f.contentWindow&&f.contentWindow.postMessage(a,b)}catch(c){}},cleanup:g,loaded:m}};c.createHtmlfile=function(a,b){var f=new ActiveXObject("htmlfile"),d,h,m,e=function(){clearTimeout(d)},j=function(){f&&(e(),c.unload_del(h),m.parentNode.removeChild(m),m=f=null,CollectGarbage())};f.open();f.write('<html><script>document.domain="'+document.domain+'";<\/script></html>');f.close();f.parentWindow._jp=g._jp; +var i=f.createElement("div");return f.body.appendChild(i),m=f.createElement("iframe"),i.appendChild(m),m.src=a,d=setTimeout(function(){f&&(j(),b("timeout"))},15E3),h=c.unload_add(j),{post:function(a,b){try{m&&m.contentWindow&&m.contentWindow.postMessage(a,b)}catch(c){}},cleanup:j,loaded:e}};i=function(){};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f,d){var h=this;try{h.xhr=new XMLHttpRequest}catch(m){}if(!h.xhr)try{h.xhr=new g.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}if(g.ActiveXObject|| +g.XDomainRequest)b+=(-1===b.indexOf("?")?"?":"&")+"t="+ +new Date;h.unload_ref=c.unload_add(function(){h._cleanup(!0)});try{h.xhr.open(a,b,!0)}catch(j){h.emit("finish",0,"");h._cleanup();return}if(!d||!d.no_credentials)h.xhr.withCredentials="true";if(d&&d.headers)for(var i in d.headers)h.xhr.setRequestHeader(i,d.headers[i]);h.xhr.onreadystatechange=function(){if(h.xhr){var a=h.xhr;switch(a.readyState){case 3:try{var b=a.status,c=a.responseText}catch(d){}c&&0<c.length&&h.emit("chunk",b,c);break;case 4:h.emit("finish", +a.status,a.responseText),h._cleanup(!1)}}};h.xhr.send(f)};i.prototype._cleanup=function(a){if(this.xhr){c.unload_del(this.unload_ref);this.xhr.onreadystatechange=function(){};if(a)try{this.xhr.abort()}catch(b){}this.unload_ref=this.xhr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};(c.XHRCorsObject=function(){var a=this,b=arguments;c.delay(function(){a._start.apply(a,b)})}).prototype=new i;(c.XHRLocalObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f,{no_credentials:!0})})}).prototype= +new i;i=c.XDRObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f)})};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f){var d=this,h=new XDomainRequest,b=b+((-1===b.indexOf("?")?"?":"&")+"t="+ +new Date),g=h.ontimeout=h.onerror=function(){d.emit("finish",0,"");d._cleanup(!1)};h.onprogress=function(){d.emit("chunk",200,h.responseText)};h.onload=function(){d.emit("finish",200,h.responseText);d._cleanup(!1)};d.xdr=h;d.unload_ref=c.unload_add(function(){d._cleanup(!0)}); +try{d.xdr.open(a,b),d.xdr.send(f)}catch(e){g()}};i.prototype._cleanup=function(a){if(this.xdr){c.unload_del(this.unload_ref);this.xdr.ontimeout=this.xdr.onerror=this.xdr.onprogress=this.xdr.onload=null;if(a)try{this.xdr.abort()}catch(b){}this.unload_ref=this.xdr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};c.isXHRCorsCapable=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest?1:g.XDomainRequest&&j.domain?2:s.enabled()?3:4};var e=function(a,b,f){var d=this, +h;d._options={devel:!1,debug:!1,protocols_whitelist:[],info:void 0,rtt:void 0};f&&c.objectExtend(d._options,f);d._base_url=c.amendUrl(a);d._server=d._options.server||c.random_number_string(1E3);d._options.protocols_whitelist&&d._options.protocols_whitelist.length?h=d._options.protocols_whitelist:("string"==typeof b&&0<b.length?h=[b]:c.isArray(b)?h=b:h=null,h&&d._debug('Deprecated API: Use "protocols_whitelist" option instead of supplying protocol list as a second parameter to SockJS constructor.')); +d._protocols=[];d.protocol=null;d.readyState=e.CONNECTING;d._ir=N(d._base_url);d._ir.onfinish=function(a,b){d._ir=null;a?(d._options.info&&(a=c.objectExtend(a,d._options.info)),d._options.rtt&&(b=d._options.rtt),d._applyInfo(a,b,h),d._didClose()):d._didClose(1002,"Can't connect to server",!0)}};e.prototype=new p;e.version="0.3.2";e.CONNECTING=0;e.OPEN=1;e.CLOSING=2;e.CLOSED=3;e.prototype._debug=function(){this._options.debug&&c.log.apply(c,arguments)};e.prototype._dispatchOpen=function(){this.readyState=== +e.CONNECTING?(this._transport_tref&&(clearTimeout(this._transport_tref),this._transport_tref=null),this.readyState=e.OPEN,this.dispatchEvent(new n("open"))):this._didClose(1006,"Server lost session")};e.prototype._dispatchMessage=function(a){this.readyState===e.OPEN&&this.dispatchEvent(new n("message",{data:a}))};e.prototype._dispatchHeartbeat=function(){this.readyState===e.OPEN&&this.dispatchEvent(new n("heartbeat",{}))};e.prototype._didClose=function(a,b,f){var d=this;if(d.readyState!==e.CONNECTING&& +d.readyState!==e.OPEN&&d.readyState!==e.CLOSING)throw Error("INVALID_STATE_ERR");d._ir&&(d._ir.nuke(),d._ir=null);d._transport&&(d._transport.doCleanup(),d._transport=null);var h=new n("close",{code:a,reason:b,wasClean:c.userSetCode(a)});if(!c.userSetCode(a)&&d.readyState===e.CONNECTING&&!f){if(d._try_next_protocol(h))return;h=new n("close",{code:2E3,reason:"All transports failed",wasClean:!1,last_event:h})}d.readyState=e.CLOSED;c.delay(function(){d.dispatchEvent(h)})};e.prototype._didMessage=function(a){switch(a.slice(0, +1)){case "o":this._dispatchOpen();break;case "a":for(var a=JSON.parse(a.slice(1)||"[]"),b=0;b<a.length;b++)this._dispatchMessage(a[b]);break;case "m":a=JSON.parse(a.slice(1)||"null");this._dispatchMessage(a);break;case "c":a=JSON.parse(a.slice(1)||"[]");this._didClose(a[0],a[1]);break;case "h":this._dispatchHeartbeat()}};e.prototype._try_next_protocol=function(a){var b=this;b.protocol&&(b._debug("Closed transport:",b.protocol,""+a),b.protocol=null);for(b._transport_tref&&(clearTimeout(b._transport_tref), +b._transport_tref=null);;){a=b.protocol=b._protocols.shift();if(!a)return!1;if(e[a]&&!0===e[a].need_body&&(!j.body||"undefined"!=typeof j.readyState&&"complete"!==j.readyState))return b._protocols.unshift(a),b.protocol="waiting-for-load",c.attachEvent("load",function(){b._try_next_protocol()}),!0;if(e[a]&&e[a].enabled(b._options)){b._transport_tref=c.delay((b._options.rto||0)*(e[a].roundTrips||1)||5E3,function(){b.readyState===e.CONNECTING&&b._didClose(2007,"Transport timeouted")});var f=c.random_string(8), +f=b._base_url+"/"+b._server+"/"+f;return b._debug("Opening transport:",a," url:"+f," RTO:"+b._options.rto),b._transport=new e[a](b,f,b._base_url),!0}b._debug("Skipping transport:",a)}};e.prototype.close=function(a,b){if(a&&!c.userSetCode(a))throw Error("INVALID_ACCESS_ERR");return this.readyState!==e.CONNECTING&&this.readyState!==e.OPEN?!1:(this.readyState=e.CLOSING,this._didClose(a||1E3,b||"Normal closure"),!0)};e.prototype.send=function(a){if(this.readyState===e.CONNECTING)throw Error("INVALID_STATE_ERR"); +return this.readyState===e.OPEN&&this._transport.doSend(c.quote(""+a)),!0};e.prototype._applyInfo=function(a,b,f){this._options.info=a;this._options.rtt=b;this._options.rto=c.countRTO(b);this._options.info.null_origin=!j.domain;b=c.probeProtocols();this._protocols=c.detectProtocols(b,f,a)};i=e.websocket=function(a,b){var f=this,d=b+"/websocket";"https"===d.slice(0,5)?d="wss"+d.slice(5):d="ws"+d.slice(4);f.ri=a;f.url=d;f.ws=new (g.WebSocket||g.MozWebSocket)(f.url);f.ws.onmessage=function(a){f.ri._didMessage(a.data)}; +f.unload_ref=c.unload_add(function(){f.ws.close()});f.ws.onclose=function(){f.ri._didMessage(c.closeFrame(1006,"WebSocket connection broken"))}};i.prototype.doSend=function(a){this.ws.send("["+a+"]")};i.prototype.doCleanup=function(){var a=this.ws;a&&(a.onmessage=a.onclose=null,a.close(),c.unload_del(this.unload_ref),this.unload_ref=this.ri=this.ws=null)};i.enabled=function(){return!!g.WebSocket||!!g.MozWebSocket};i.roundTrips=2;var l=function(){};l.prototype.send_constructor=function(a){this.send_buffer= +[];this.sender=a};l.prototype.doSend=function(a){this.send_buffer.push(a);this.send_stop||this.send_schedule()};l.prototype.send_schedule_wait=function(){var a=this,b;a.send_stop=function(){a.send_stop=null;clearTimeout(b)};b=c.delay(25,function(){a.send_stop=null;a.send_schedule()})};l.prototype.send_schedule=function(){var a=this;if(0<a.send_buffer.length){var b="["+a.send_buffer.join(",")+"]";a.send_stop=a.sender(a.trans_url,b,function(){a.send_stop=null;a.send_schedule_wait()});a.send_buffer= +[]}};l.prototype.send_destructor=function(){this._send_stop&&this._send_stop();this._send_stop=null};var O=function(a,b,f){if(!("_send_form"in this)){var d=this._send_form=j.createElement("form"),h=this._send_area=j.createElement("textarea");h.name="d";d.style.display="none";d.style.position="absolute";d.method="POST";d.enctype="application/x-www-form-urlencoded";d.acceptCharset="UTF-8";d.appendChild(h);j.body.appendChild(d)}var d=this._send_form,h=this._send_area,g="a"+c.random_string(8);d.target= +g;d.action=a+"/jsonp_send?i="+g;var e;try{e=j.createElement('<iframe name="'+g+'">')}catch(i){e=j.createElement("iframe"),e.name=g}e.id=g;d.appendChild(e);e.style.display="none";try{h.value=b}catch(k){c.log("Your browser is seriously broken. Go home! "+k.message)}d.submit();var l=function(){e.onerror&&(e.onreadystatechange=e.onerror=e.onload=null,c.delay(500,function(){e.parentNode.removeChild(e);e=null}),h.value="",f())};return e.onerror=e.onload=l,e.onreadystatechange=function(){"complete"==e.readyState&& +l()},l},P=function(a){return function(b,c,d){return(new a("POST",b+"/xhr_send",c)).onfinish=function(a){d(a)},function(a){d(0,a)}}},i=e["jsonp-polling"]=function(a,b){c.polluteGlobalNamespace();this.ri=a;this.trans_url=b;this.send_constructor(O);this._schedule_recv()};i.prototype=new l;i.prototype._schedule_recv=function(){var a=this,b=a.trans_url+"/jsonp",f="a"+c.random_string(6),b=b+"?c="+escape("_jp."+f),d=function(b){delete g._jp[f];a._recv_stop=null;b&&(a._is_closing||a.ri._didMessage(b));a._is_closing|| +a._schedule_recv()},e,m=j.createElement("script"),i,k=function(a){i&&(i.parentNode.removeChild(i),i=null);m&&(clearTimeout(e),m.parentNode.removeChild(m),m.onreadystatechange=m.onerror=m.onload=m.onclick=null,m=null,d(a),d=null)},l=!1,p=null;m.id="a"+c.random_string(8);m.src=b;m.type="text/javascript";m.charset="UTF-8";m.onerror=function(){p||(p=setTimeout(function(){l||k(c.closeFrame(1006,"JSONP script loaded abnormally (onerror)"))},1E3))};m.onload=function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (onload)"))}; +m.onreadystatechange=function(){if(/loaded|closed/.test(m.readyState)){if(m&&m.htmlFor&&m.onclick){l=!0;try{m.onclick()}catch(a){}}m&&k(c.closeFrame(1006,"JSONP script loaded abnormally (onreadystatechange)"))}};if("undefined"==typeof m.async&&j.attachEvent)if(/opera/i.test(navigator.userAgent))i=j.createElement("script"),i.text="try{var a = document.getElementById('"+m.id+"'); if(a)a.onerror();}catch(x){};",m.async=i.async=!1;else{try{m.htmlFor=m.id,m.event="onclick"}catch(n){}m.async=!0}"undefined"!= +typeof m.async&&(m.async=!0);e=setTimeout(function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (timeout)"))},35E3);b=j.getElementsByTagName("head")[0];b=(b.insertBefore(m,b.firstChild),i&&b.insertBefore(i,b.firstChild),k);g._jp[f]=b;a._recv_stop=function(){g._jp[f]&&g._jp[f](c.closeFrame(1E3,"JSONP user aborted read"))}};i.enabled=function(){return!0};i.need_body=!0;i.prototype.doCleanup=function(){this._is_closing=!0;this._recv_stop&&this._recv_stop();this.ri=this._recv_stop=null;this.send_destructor()}; +i=function(){};i.prototype=new l;i.prototype.run=function(a,b,c,d,e){this.ri=a;this.trans_url=b;this.send_constructor(P(e));this.poll=new E(a,d,b+c,e)};i.prototype.doCleanup=function(){this.poll&&(this.poll.abort(),this.poll=null)};var w=e["xhr-streaming"]=function(a,b){this.run(a,b,"/xhr_streaming",y,c.XHRCorsObject)};w.prototype=new i;w.enabled=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest&&!/opera/i.test(navigator.userAgent)};w.roundTrips=2;w.need_body=!0;l=e["xdr-streaming"]= +function(a,b){this.run(a,b,"/xhr_streaming",y,c.XDRObject)};l.prototype=new i;l.enabled=function(){return!!g.XDomainRequest};l.roundTrips=2;var F=e["xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRCorsObject)};F.prototype=new i;F.enabled=w.enabled;F.roundTrips=2;w=e["xdr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XDRObject)};w.prototype=new i;w.enabled=l.enabled;w.roundTrips=2;var s=function(){};s.prototype.i_constructor=function(a,b,f){var d=this;d.ri=a;d.origin=c.getOrigin(f);d.base_url= +f;d.trans_url=b;a=f+"/iframe.html";d.ri._options.devel&&(a+="?t="+ +new Date);d.window_id=c.random_string(8);a+="#"+d.window_id;d.iframeObj=c.createIframe(a,function(a){d.ri._didClose(1006,"Unable to load an iframe ("+a+")")});d.onmessage_cb=c.bind(d.onmessage,d);c.attachMessage(d.onmessage_cb)};s.prototype.doCleanup=function(){if(this.iframeObj){c.detachMessage(this.onmessage_cb);try{this.iframeObj.iframe.contentWindow&&this.postMessage("c")}catch(a){}this.iframeObj.cleanup();this.onmessage_cb=this.iframeObj= +this.iframeObj=null}};s.prototype.onmessage=function(a){if(a.origin===this.origin){var b=a.data.slice(0,8),c=a.data.slice(8,9),a=a.data.slice(9);if(b===this.window_id)switch(c){case "s":this.iframeObj.loaded();this.postMessage("s",JSON.stringify([e.version,this.protocol,this.trans_url,this.base_url]));break;case "t":this.ri._didMessage(a)}}};s.prototype.postMessage=function(a,b){this.iframeObj.post(this.window_id+a+(b||""),this.origin)};s.prototype.doSend=function(a){this.postMessage("m",a)};s.enabled= +function(){var a=navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Konqueror");return("function"==typeof g.postMessage||"object"==typeof g.postMessage)&&!a};var G,H=function(a,b){parent!==g?parent.postMessage(G+a+(b||""),"*"):c.log("Can't postMessage, no parent window.",a,b)},u=function(){};u.prototype._didClose=function(a,b){H("t",c.closeFrame(a,b))};u.prototype._didMessage=function(a){H("t",a)};u.prototype._doSend=function(a){this._transport.doSend(a)};u.prototype._doCleanup=function(){this._transport.doCleanup()}; +c.parent_origin=void 0;e.bootstrap_iframe=function(){var a;G=j.location.hash.slice(1);c.attachMessage(function(b){if(b.source===parent&&("undefined"==typeof c.parent_origin&&(c.parent_origin=b.origin),b.origin===c.parent_origin)){var f=b.data.slice(0,8),d=b.data.slice(8,9),b=b.data.slice(9);if(f===G)switch(d){case "s":var h=JSON.parse(b),f=h[0],d=h[1],b=h[2],h=h[3];f!==e.version&&c.log('Incompatibile SockJS! Main site uses: "'+f+'", the iframe: "'+e.version+'".');if(!c.flatUrl(b)||!c.flatUrl(h)){c.log("Only basic urls are supported in SockJS"); +break}if(!c.isSameOriginUrl(b)||!c.isSameOriginUrl(h)){c.log("Can't connect to different domain from within an iframe. ("+JSON.stringify([g.location.href,b,h])+")");break}a=new u;a._transport=new u[d](a,b,h);break;case "m":a._doSend(b);break;case "c":a&&a._doCleanup(),a=null}}});H("s")};var z=function(a,b){var f=this;c.delay(function(){f.doXhr(a,b)})};z.prototype=new k(["finish"]);z.prototype.doXhr=function(a,b){var f=this,d=(new Date).getTime(),e=new b("GET",a+"/info"),g=c.delay(8E3,function(){e.ontimeout()}); +e.onfinish=function(a,b){clearTimeout(g);g=null;if(200===a){var c=(new Date).getTime()-d,e=JSON.parse(b);"object"!=typeof e&&(e={});f.emit("finish",e,c)}else f.emit("finish")};e.ontimeout=function(){e.close();f.emit("finish")}};var K=function(a){var b=this,f=function(){var c=new s;c.protocol="w-iframe-info-receiver";var f=function(a){"string"==typeof a&&"m"===a.substr(0,1)?(a=JSON.parse(a.substr(1)),b.emit("finish",a[0],a[1])):b.emit("finish");c.doCleanup();c=null};c.i_constructor({_options:{},_didClose:f, +_didMessage:f},a,a)};j.body?f():c.attachEvent("load",f)};K.prototype=new k(["finish"]);var L=function(){var a=this;c.delay(function(){a.emit("finish",{},2E3)})};L.prototype=new k(["finish"]);var N=function(a){if(c.isSameOriginUrl(a))return new z(a,c.XHRLocalObject);switch(c.isXHRCorsCapable()){case 1:return new z(a,c.XHRLocalObject);case 2:return new z(a,c.XDRObject);case 3:return new K(a);default:return new L}};(u["w-iframe-info-receiver"]=function(a,b,f){(new z(f,c.XHRLocalObject)).onfinish=function(b, +c){a._didMessage("m"+JSON.stringify([b,c]));a._didClose()}}).prototype.doCleanup=function(){};k=e["iframe-eventsource"]=function(){this.protocol="w-iframe-eventsource";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return"EventSource"in g&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-eventsource"]=function(a,b){this.run(a,b,"/eventsource",I,c.XHRLocalObject)}).prototype=new i;k=e["iframe-xhr-polling"]=function(){this.protocol="w-iframe-xhr-polling";this.i_constructor.apply(this, +arguments)};k.prototype=new s;k.enabled=function(){return g.XMLHttpRequest&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRLocalObject)}).prototype=new i;k=e["iframe-htmlfile"]=function(){this.protocol="w-iframe-htmlfile";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-htmlfile"]=function(a,b){this.run(a,b,"/htmlfile",J,c.XHRLocalObject)}).prototype= +new i;var E=function(a,b,c,d){this.ri=a;this.Receiver=b;this.recv_url=c;this.AjaxObject=d;this._scheduleRecv()};E.prototype._scheduleRecv=function(){var a=this,b=a.poll=new a.Receiver(a.recv_url,a.AjaxObject);b.onmessage=function(b){a.ri._didMessage(b.data)};b.onclose=function(c){a.poll=b=b.onmessage=b.onclose=null;a.poll_is_closing||("permanent"===c.reason?a.ri._didClose(1006,"Polling error ("+c.reason+")"):a._scheduleRecv())}};E.prototype.abort=function(){this.poll_is_closing=!0;this.poll&&this.poll.abort()}; +var I=function(a){var b=this,f=new EventSource(a);f.onmessage=function(a){b.dispatchEvent(new n("message",{data:unescape(a.data)}))};b.es_close=f.onerror=function(a,e){var g=e?"user":2!==f.readyState?"network":"permanent";b.es_close=f.onmessage=f.onerror=null;f.close();f=null;c.delay(200,function(){b.dispatchEvent(new n("close",{reason:g}))})}};I.prototype=new p;I.prototype.abort=function(){this.es_close&&this.es_close({},!0)};var A,J=function(a){var b=this;c.polluteGlobalNamespace();b.id="a"+c.random_string(6, +26);a+=(-1===a.indexOf("?")?"?":"&")+"c="+escape("_jp."+b.id);if(void 0===A)if("ActiveXObject"in g)try{A=!!new ActiveXObject("htmlfile")}catch(f){}else A=!1;var d=A?c.createHtmlfile:c.createIframe,e;g._jp[b.id]={start:function(){e.loaded()},message:function(a){b.dispatchEvent(new n("message",{data:a}))},stop:function(){b.iframe_close({},"network")}};b.iframe_close=function(a,c){e.cleanup();b.iframe_close=e=null;delete g._jp[b.id];b.dispatchEvent(new n("close",{reason:c}))};e=d(a,function(){b.iframe_close({}, +"permanent")})};J.prototype=new p;J.prototype.abort=function(){this.iframe_close&&this.iframe_close({},"user")};var y=function(a,b){var c=this,d=0;c.xo=new b("POST",a,null);c.xo.onchunk=function(a,b){if(200===a)for(;;){var e=b.slice(d),g=e.indexOf("\n");if(-1===g)break;d+=g+1;e=e.slice(0,g);c.dispatchEvent(new n("message",{data:e}))}};c.xo.onfinish=function(a,b){c.xo.onchunk(a,b);c.xo=null;c.dispatchEvent(new n("close",{reason:200===a?"network":"permanent"}))}};return y.prototype=new p,y.prototype.abort= +function(){this.xo&&(this.xo.close(),this.dispatchEvent(new n("close",{reason:"user"})),this.xo=null)},e.getUtils=function(){return c},e.getIframeTransport=function(){return s},e}();"_sockjs_onload"in window&&setTimeout(_sockjs_onload,1);"function"==typeof define&&define.amd&&define("sockjs",[],function(){return SockJS});function WebTCP(j,g){this.sockets={};this.httpClients={};this.sock=new SockJS("http://"+j+":"+g+"/bridge");this.ready=!1;this.outputBuffer=[];var c=this;this.sock.onopen=function(){c.ready=!0;c.processBuffer()};this.processBuffer=function(){if(0<this.outputBuffer.length&&this.ready)for(var c in this.outputBuffer)this.sock.send(this.outputBuffer[c])};this.sock.onmessage=function(g){g.data=JSON.parse(g.data);g.data.sID?(sID=g.data.sID,c.sockets[sID].onEvent(g.data.eventName,g.data.data)):g.data.cID&& +(cID=g.data.cID,c.httpClients[cID].onEvent(g.data.eventName,g.data.data))};this.sock.onclose=function(){console.log("close")};this.createSocket=function(c,g,j){return new Socket(this,c,g,j)};this.createHTTPClient=function(){return new Http(this)}};function WebTCPIO(){this.onEvent=function(j,g){this["on"+j](g);if(this["on"+j+"Custom"])this["on"+j+"Custom"](g)};this.on=function(j,g){this["on"+j+"Custom"]=g};this.rpc=function(j,g){var c=this.createPacket();c.command=j;c.args=g;this.send(JSON.stringify(c));return c.id};this.write=function(j){var g=this.createPacket();g.data=j;this.send(JSON.stringify(g));return g.id};this.send=function(j){if(this.webtcp.ready)this.webtcp.sock.send(j);else{if(this.webtcp.outputBuffer>this.webtcp.BUFF_SIZE)throw"Output buffer is already full, but sockJS connection is not ready yet"; +this.webtcp.outputBuffer.push(j)}};this.onerror=function(j){throw j;}};
\ No newline at end of file diff --git a/chapter25/module.md b/chapter25/module.md new file mode 100644 index 00000000..d7bed18c --- /dev/null +++ b/chapter25/module.md @@ -0,0 +1,277 @@ + +# Modules for Simple + +Goals: +- 1-liner valid program (for easy tutorial/newbie starts) +- - Compile and run with minimal effort +- Separate compilation and namespaces; external libs +- - Compile to ELF or other linkable/executable format +- Easy private fields & classes +- Output files are compatible with existing .o/.exe/lib/dll infrastructure +- No `#include` or `import` files +- Structure / file system heirarchy per module +- - Perhaps many files make a module +- - easy namespaces within a module +- - Nested names in parent file or nested file +- Class initializers guarenteed: +- - execute exactly once +- - lazily on first touch +- - complete on or before first instances are made +- - - (an exception is allowed for a golden instance in `<clinit>`) + + +--- +### Goal: Valid 1-liner, minimal effort + +This makes a module and a class `hello` out of the current working directory +name. Compiling without more arguments also links and executes. + +File: `hello.smp` +``` +sys.io.p("Hello World!"); +``` + +Compile and run: +``` +> smp hello.smp +Hello World! +``` + +Module and class name is `hello` extracted from file name, compiled in memory +and `exec'd`. No files made, this is strictly a run and gun mode. + + +--- +### Goal: separate compilation + +Name a build directory target during compilation to get `.o` files. +The build target directory structure will match the source, and +can be placed anywhere. + +As before, since no module name is mentioned on the compile line, `Pet` becomes +both the module and a class. + +File: Pet.smp +``` +// Class initializer <clinit> is any code outside a struct declaration +// and is guaranteed to run-once on first touch. + +// static fields listed here: +int default_leggies = 4; + +// A default (no argument) <init> is code inside struct with same name as the +// file; this is the same behavior as field initializers for Java, although +// any code is allowed, not just expressions. + +struct Pet { + str name; + int leggies = default_leggies; +} + +float yet_another_static_field; +``` + +Compile to `Pet.o` in the `build/` directory: +``` +> smp Pet.smp -o build/ +> ls build +Pet.o +``` + + +--- +### Goal: nested namespaces + +This makes a module and class `Pet` based on the file name in `myRepo`. It +also makes a nested class `Pet.Vet`. These are used from a different module +`Owner`. + + +Directory: `~/myRepo/` +``` +~/myRepo/ + Pet.smp + build/ + +~/otherRepo/ + Owner.smp +``` + +File: `myRepo/Pet.smp` +``` +int default_leggies = 4; + +struct Pet { + str name; + int leggies = default_leggies; +} + +// Nested class Vet inside of file Pet +struct Vet { + str name; + str address; + long credit_card_num; +} +``` + +File: `otherRepo/Owner.smp` +``` +Pet dog = Pet{name="Fido"}; +Pet snake = Pet{name="slither", leggies = 0; }; +Pet.Vet vet = Pet.Vet{name="Linda"; address="101 Main St"; credit_card_num=1234}; + +sys.io.p("I took "+dog.name+" to "+vet.name+"\n"); +``` + +Compile to `Pet.o` in the `build/` directory; since the compile line does not +mention some other module root, `Pet` must be the module (and is also a class). +Since `Pet` is the module, `Vet` is a class within the module `Pet`. + +``` +> smp Pet.smp -o build/ +> ls build +Pet.o +``` + +The code for both `Pet` and `Pet.Vet` are in the same `Pet.o` file. + +Compile and run `Owner.smp`, specifying the `Pet` repo to satisfy the free +variables `Pet` and `Pet.Vet`. + +``` +> smp Owner.smp -lib ~/myRepo/build +I took Fido to Linda +``` + +--- +### Goal: nested namespace in a nested file + +Same as the last example but with nested files: + +``` +~/myRepo + Pet.smp + Pet/ + Vet.smp + build/ + Pet.o + Pet/ + Vet.o + +~/otherRepo + Owner.smp +``` + + +File: Pet.smp +``` +int default_leggies = 4; + +struct Pet { + str name; + int leggies = default_leggies; +} +``` + +File: Pet/Vet.smp +``` +// Nested class Vet inside of directory Pet +struct Vet { + str name; + str address; + long credit_card_num; +} +``` + +Compile and run `Owner.smp`: +``` +> smp Owner.smp -lib ~/myRepo/build +I took Fido to Linda +``` + + +--- +### Goal: Class Initializers are Lazy + +``` +Parent.smp +Parent/ + ChildA.smp + ChildB.smp +``` + +File: `Parent.smp` +``` +sys.io.p("In Parent <clinit>"); +sys.io.p(ChildB.str); +``` + +File: `ChildA.smp` +``` +// <clinit> for ChildA +sys.io.p("In ChildA <clinit>"); +str str = "A"; +``` + +File: `ChildB.smp` +``` +// <clinit> for ChildB +sys.io.p("In ChildB <clinit>"); +str str = "B"; +``` + +Compile and run: +``` +> smp Parent.smp +In Parent <clinit> +In ChildB <clinit> +B + +``` + +### Goal: Easy private fields + +Leading underscore makes field or class private. + +### Goal: similar to C compiler driver syntax + +``` +Simple --cpu x86_64_v2 --abi Win64 --norun -o lib/lib/sys_x86_64_v2_win64.o src/main/smp/sys.smp +``` + +Module not mentioned, so top-level file is module: `sys` +Module directory not mentioned, so is prefix of file name `src/main/smp`. +Build dir not mentioned, so is prefix of output file: `lib/` +Output file is default `smp.o`, here renamed. + + + +One <clinit> can refer to another, and will be executed as first touched. +Parents must be touched before children by design. +However a child A can refer to a child B. +Touching Parent.B means +- Parent is touched first, and begins <clinit> +- Anything it touches, gets recursively <clinit> including e.g. A or B +- Parent completes. +- Nothing in a <clinit> can refer to another *module*. +- Static functions *can* refer to another module, but then can be executed... + +Goal: deterministic <clinit> within a module. + History: plenty of times I've had complex <clinit> orders, and then discovered + some referred static-final-fields are not yet init'd when child uses. + SubGoal: deterministic <clinit> *order*, Parent before Child, etc. + Issue: mid-Parent <clinit>, refers to Child, so Child <clinit> before Parent completes. + SubAnswer: Since parent first, and "parent knows child", this is OK. + SubIssue: this does not work across modules; one module is not a parent of another + Issue: Parent fcns called during <clinit> can non-determistic init child. + "fcn = { -> rand ? A.fld : B.fld }" + Answer?: Parent <clinit> *may not* direct or indirect refer to a child. + Example: `B b = new B; // Illegal ref to child in Parent.<clinit>` + Example: `B? b = fcn(); fcn = { -> rand ? null : new B; } // Illegal call to fcn referring to B inside Parent <clinit>` + Example: `B? b; fcn = { -> new B; } // OK, no B.<clinit> during Parent.<clinit>` + +Goal: external linkage also enjoys the 1-shot init feature. + Answer?: extern call to 'Foo.foo()' first checks Foo.<clinit> + has run, before calling `Foo.foo()`. Optional: Caller is + modified to bypass <clinit> check after success. +
\ No newline at end of file diff --git a/chapter25/parser-simplification-plan.md b/chapter25/parser-simplification-plan.md new file mode 100644 index 00000000..986643ea --- /dev/null +++ b/chapter25/parser-simplification-plan.md @@ -0,0 +1,371 @@ +# Direct-to-SSA Parser Simplification Plan + +## Purpose + +The constructor rewrite exposed a deeper problem: the one-pass parser sometimes +uses provisional type information to choose SSA graph structure. Forward +references make that information incomplete and parse-order dependent. + +The design rule for this work is: + +> Syntax and resolved lexical bindings determine graph shape. Types refine and +> validate the graph, but provisional types do not decide its shape. + +This is an experiment, not an irreversible cleanup. We need to be able to +abandon the simplification completely, or restore individual mechanisms that +turn out to have been necessary. + +## Preserve the old design before removing it + +Do not preserve old code as large commented-out blocks. Git is the archive; +the working implementation should remain small enough to understand. + +Before implementation begins: + +1. Confirm the worktree is clean and record the current failing-test baseline. +2. Create a named, permanent anchor at the last commit before simplification, + for example an annotated tag `ch25-parser-before-simplification`. +3. Do the experiment on a dedicated branch, for example + `ch25-parser-simplification`. +4. Push the anchor and branch if a remote backup is desired before large + deletions. +5. Never squash the experimental commits until the design has proved itself. + +The anchor is the complete "this was a giant mistake" escape hatch. Switching +back to a branch made from that anchor restores the old implementation exactly. +No reset of the experimental branch is required. + +Removed mechanisms must each have their own small commit. A commit should +remove one idea, not mix its deletion with several replacements. Suggested +commit sequence: + +1. Add characterization tests and inventories. +2. Remove one old constraint mechanism. +3. Make weak-input computation total. +4. Add the smallest replacement demanded by a failing test. + +This history supports several levels of recovery: + +- Recover the entire old design by creating or switching to a branch at the + preservation tag. +- Restore a complete deleted file from the tag with `git restore --source`. +- Restore selected hunks interactively with `git restore -p --source`. +- Reintroduce a logically complete mechanism by reverting its deletion commit + or cherry-picking a later restoration commit. +- Compare the experiment with the old implementation at any time using the + preservation tag as the diff base. + +Avoid using a stash as the archive: stashes are unnamed working state, are easy +to drop, and do not provide the reviewable sequence needed for selective +restoration. + +When an old mechanism is restored, bring it back in a new, named commit and +record the test that required it. Do not silently copy code out of history. +That preserves evidence about which complexity is essential. + +## Working discipline + +Work through `tests_raw0` chapter by chapter. Keep all earlier chapters passing +before advancing. The expected difficulty landmarks are Chapter 10, where +functions are exercised seriously, and Chapter 13, where recursive forward +references and cyclic types become prominent. + +Use the following test ladder: + +1. The single test or test class driving the current change. +2. The current chapter. +3. All earlier raw0 chapters. +4. `tests_raw0` when a milestone is reached. +5. `TypeTest` after every change to `Type`, `Field`, lattice operations, + interning inputs, equality, hashing, or duality. + +Golden-string mismatches are not a gate during structural work unless the +string reveals a semantic or graph-shape regression. Record and defer expected +text churn rather than continually rewriting goldens. + +Treat these as compiler invariant failures, not ordinary test failures: + +- `compute()` must be monotonic, including when inputs are weak or unresolved. +- `Opto.worklistCheck` failures require finding the non-monotonic computation; + they are not assertions to weaken. +- IterPeeps' `Non-monotonic peep` check applies the same rule to iterative + transformations. +- `ideal()` runs only during iterative optimization, not Opto, but its + replacement must preserve the type knowledge already established. + +Commit whenever the test frontier advances or one architectural mechanism is +removed. Each commit message should state the highest passing chapter and +whether `TypeTest` was run. + +## Phase 0: Baseline and inventory + +1. Fix the local `_Scan` constructor migration so it does not obscure parser + work. +2. Record the current raw0 failures by chapter and test name. +3. Inventory parser-written constraints and type-sharpening side channels: + `_con` fields, mutable signatures, parser mutations of `TYPES`, forced memory + types, and assertions that assume already-sharp inputs. +4. For every candidate deletion, record its writers, readers, and the first + test that appears to need it. +5. Add small characterization tests when behavior is important but undocumented. + +The inventory is a deletion checklist, not a commitment to replace every item. +Code that no passing test requires stays deleted. + +## Phase 1: Remove parser-supplied node constraints + +Start with `_con`-style fields in `PhiNode`, `LoadNode`, `StoreNode`, and related +nodes. + +For each node kind: + +1. Delete the parser writer and the node field in a narrow commit. +2. Make `compute()` accept the weakest legal inputs. A `Type.BOTTOM` input must + produce a conservative weak output instead of triggering a cast or sharpness + assertion. +3. Separate structural slot invariants from type-sharpness assumptions. It is + valid to know that an input position is the memory edge without knowing a + sharp `TypeMem` for it. +4. Restore only the structure that a failing test proves necessary. + +If an ordinary Phi cannot preserve the structural fact that it merges memory, +introduce a `MemPhiNode` or an explicit immutable Phi kind. It may lift weak +inputs to the weakest memory type. It must not contain a parser-chosen sharp +lower bound disguised as another `_con`. + +### Keep arithmetic flavor out of the parser + +Arithmetic syntax determines the operation (`+`, `-`, `*`, `/`, unary `-`), +but not necessarily its integer or floating-point machine flavor. The current +`Node.widen()` is a parser-time semantic rewrite: it inspects the operands' +current types, replaces integer nodes with FP nodes, and inserts `ToFloatNode`s. +A weak forward-reference input can therefore permanently choose the wrong SSA +shape. + +Replace this decision with a generic or unresolved arithmetic node, or make the +syntax-level arithmetic node itself generic until its operand family resolves. +It must: + +- compute a weak result while either operand family is unknown; +- resolve monotonically to integer or floating-point arithmetic when sufficient + information arrives; +- insert integer-to-float conversions only after the FP choice is definitive; +- report mixed or invalid operands during type checking rather than guessing + during parsing; and +- be completely resolved before instruction selection. + +Compound assignments and increment/decrement use the same mechanism; they must +not retain a separate parser-time `widen()` decision path. + +## Phase 2: Separate name binding from type inference + +Locals remain define-before-use. The unresolved case is an unqualified field +name used inside a function before a later instance or class field declaration +determines its owner. + +Introduce unresolved SSA operations rather than guessing from provisional +types: + +- `UnresolvedLoad` records the identifier, source position, definitive lexical + scope object, ordered possible base pointers, and bulk memory input. +- `UnresolvedStore` records the same binding information plus its value and the + constructor context needed to decide initialization legality later. +- Closing the relevant scope resolves all pending operations by lexical order. +- Resolution selects the base pointer and alias and rewrites the node to the + ordinary resolved operation. + +Do not use pointer-type sharpening to choose ordinary lexical bindings. Types +may reject a resolved operation, but the closed scope is authoritative about +which declaration the name denotes. + +The `init` bit on a resolved Store is decided at this same point. It is true +only when the winning declaration belongs to the receiver being initialized by +the current constructor. Merely being textually inside some constructor is not +enough. + +## Phase 3: Recover alias precision after unresolved stores + +Before binding, an unknown store uses bulk alias `#1` and conservatively updates +bulk memory. Preserve enough information in `UnresolvedStore` to recover the +pre-store alias bundle when its target becomes known. + +After resolution, Opto may transform a Phi of memory merges into a merge of +per-alias Phis: + +```text +Phi(MemMerge(entry...), MemMerge(back...)) + => +MemMerge(aliasA = Phi(entryA, backA), + aliasB = Phi(entryB, backB), ...) +``` + +For a resolved store to alias A, alias A's loop backedge passes through the +store; unrelated aliases bypass it. + +Requirements for this idealization: + +- Handle missing slices through the MemMerge default/bulk input. +- Create alias Phis on demand; aliases need not all be known at loop creation. +- Memoize a replacement loop Phi before recursively processing its backedge. +- Use only the canonical direction shown above to prevent transform oscillation. +- Keep unresolved memory operations resistant to destructive idealization until + their owning scope closes. + +Pre-splitting memory during parsing remains an eager optimization when aliases +are already known. This transform is the lazy equivalent when they are not. + +## Phase 4: Keep call shape syntactic + +Call SSA shape must not depend on whether a provisional function type currently +looks like an instance function. + +The syntax `ptr.fld(args)` always supplies `ptr` in the hidden self slot: + +```text +call(load(ptr, fld), self=ptr, args...) +``` + +This remains true if the field later resolves to a stored top-level function; +that function ignores the universal self argument. An ordinary call through a +separately loaded function value uses the normal non-instance/default self. + +Function types validate calls after construction. They do not change call +arity or add/remove SSA inputs after resolution. + +## Phase 5: Split declared function contracts from inferred signatures + +Audit `FunNode._sig` after the earlier constraints are removed. + +Function arguments have authoritative declared syntax, while returns are graph +derived. A declared forward type such as `B` has stable symbolic identity but +only a provisional current realization. + +Represent the external/default caller with a non-constant source node tied to +the declared type binding: + +```text +UnknownCaller(B-binding) +``` + +Its computed runtime value type begins as weak open `B{...}` and monotonically +sharpens when the binding resolves. Updating the binding schedules its users. +Private functions with completely known call sites may omit this default caller +and sharpen their parameters from actual arguments. Escaping functions retain +it as their worst-case caller. + +Classify every use of `_sig` as one of: + +- immutable declared argument contract; +- cached graph-derived information; +- unresolved symbolic binding. + +Delete cached derived state. Keep authoritative declarations explicitly. +Derive return information from Fun/Parm/Return graph edges. Recursive functions +must converge through weak-input-tolerant `compute()` rather than parser mutation +of a parallel signature cache. + +## Phase 6: Make field finality a four-state type fact + +Use two bits in `Field`, with accessors hiding the representation: + +```text +00 unknown +01 declared mutable +10 declared final +11 conflicting finality (BAD_FINAL) +``` + +The discovery-combination operation combines evidence; with this encoding it +can normally be bitwise OR. `UNKNOWN` is the identity and `BAD_FINAL` is +absorbing. Confirm the operation matches the repository's meet/join lattice +orientation. + +Add the bits to equality, hashing, duality, printing, and all Field construction +paths. A weak forward reference carries `UNKNOWN`, never guessed mutable. +Conflicting authoritative declarations produce `BAD_FINAL`; computation remains +total and type checking later reports the program error. + +Add representative Fields for all four states to TypeTest's `gather()` universe. +Verify pairwise operations plus commutativity, associativity, idempotence, duals, +hashing, and interning behavior. Cyclic handling and interning should fall out +of the existing machinery, but TypeTest must demonstrate that they do. + +Final-store validation happens only after binding and type closure: + +- final and initializing store: legal; +- final and non-initializing store: error; +- mutable field: initialization marker is irrelevant; +- bad finality: declaration/type error. + +## Phase 7: Preserve cyclic type closure + +Keep `Type.closeOver()` responsible for tying recursive named types together. +For mutually recursive A/B structures it must collect partial versions, merge +same-name fields (including four-state finality), remove the open marker when +the declaration is complete, and make recursive occurrences reference the same +canonical Java object. + +Do not make SSA construction depend on whether `closeOver()` has already run. +Closing types sharpens existing nodes and wakes their users; it does not rebuild +parser-chosen graph topology. + +Chapter 13 is the first major acceptance gate for this phase. + +## Phase 8: Advance the chapter frontier + +For each failing chapter: + +1. Reduce the first semantic or invariant failure to the smallest useful test. +2. Decide whether it demonstrates missing required structure or merely an old + golden string. +3. Add back only the smallest mechanism justified by the test. +4. Run the chapter, all earlier chapters, and TypeTest when applicable. +5. Commit the new frontier before moving on. + +Expected order of pressure: + +- Chapters 1-9: establish that weak-input computation did not disturb basics. +- Chapter 10: functions, calls, and parameter computation. +- Chapter 13: recursive forward functions and cyclic structures. +- Later chapters: fields, finality, memory alias recovery, escaping functions, + and separate compilation interactions. + +Do not begin by restoring later-chapter machinery preemptively. + +## Decision gates and rollback criteria + +Pause and compare against the preservation anchor when any of these occurs: + +- A replacement requires more state or special cases than the mechanism removed. +- Parser graph shape still depends on provisional types. +- Alias recovery requires global reconstruction with no monotonic canonical form. +- Recursive type or function convergence requires non-monotonic mutation. +- Earlier chapters repeatedly regress as later mechanisms are added. + +At a gate, choose explicitly among: + +1. Continue with the simplified mechanism. +2. Restore one old component and document the test proving its value. +3. Start a narrower experiment branch from the preservation anchor. +4. Abandon the experiment and resume from the preserved design. + +Abandoning the experiment is a supported result, not a failed recovery. The +tag, unsquashed commits, and narrow deletions guarantee that both the complete +old parser and individual removed mechanisms remain available. + +## Completion criteria + +The simplification is successful when: + +- `tests_raw0`, then `tests_raw1`, and finally the broader suite pass apart from + deliberately deferred golden updates; +- `TypeTest` passes with all finality states included; +- no parser decision about SSA topology depends on a provisional inferred type; +- unresolved lexical operations resolve deterministically at scope close; +- memory precision lost during unresolved parsing is recovered monotonically; +- calls obtain self from syntax, not inferred callee kind; +- declared function arguments remain authoritative without a parallel mutable + inferred signature; +- removed code that was never demanded by tests remains absent; and +- the preservation anchor and commit sequence still provide complete and + selective rollback paths. diff --git a/chapter25/pom.xml b/chapter25/pom.xml new file mode 100644 index 00000000..6a59188f --- /dev/null +++ b/chapter25/pom.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.seaofnodes</groupId> + <artifactId>simple</artifactId> + <version>1.0</version> + </parent> + <artifactId>chapter23</artifactId> + <packaging>jar</packaging> + <name>Chapter 23</name> +</project> diff --git a/chapter25/simple.bat b/chapter25/simple.bat new file mode 100644 index 00000000..8308e1aa --- /dev/null +++ b/chapter25/simple.bat @@ -0,0 +1,10 @@ +@echo off +setlocal + +set "HERE=%~dp0" +set "RELEASE=%HERE%build\release" +set "JAR=%RELEASE%\Simple.jar" + +if not defined SIMPLE_HOME set "SIMPLE_HOME=%RELEASE%" + +java -jar "%JAR%" %* diff --git a/chapter25/simple.ps1 b/chapter25/simple.ps1 new file mode 100644 index 00000000..d7ea0678 --- /dev/null +++ b/chapter25/simple.ps1 @@ -0,0 +1,10 @@ +$Here = Split-Path -Parent $MyInvocation.MyCommand.Path +$Release = Join-Path $Here "build\release" +$Jar = Join-Path $Release "Simple.jar" + +if (-not $env:SIMPLE_HOME) { + $env:SIMPLE_HOME = $Release +} + +& java -jar $Jar @args +exit $LASTEXITCODE diff --git a/chapter25/simple.sh b/chapter25/simple.sh new file mode 100644 index 00000000..32552ecc --- /dev/null +++ b/chapter25/simple.sh @@ -0,0 +1,12 @@ +#! /bin/sh +here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +release="$here/build/release" +jar="$release/Simple.jar" + +if command -v cygpath >/dev/null 2>&1; then + release=$(cygpath -w "$release") + jar=$(cygpath -w "$jar") +fi + +export SIMPLE_HOME="${SIMPLE_HOME:-$release}" +exec java -jar "$jar" "$@" diff --git a/chapter25/src/main/java/com/seaofnodes/simple/IterPeeps.java b/chapter25/src/main/java/com/seaofnodes/simple/IterPeeps.java new file mode 100644 index 00000000..608f368d --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/IterPeeps.java @@ -0,0 +1,230 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Opto; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.JSViewer; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Random; + +/** + * The IterPeeps runs after parsing. It iterates the peepholes to a fixed point + * so that no more peepholes apply. This should be linear because peepholes rarely + * (never?) increase code size. The graph should monotonically reduce in some + * dimension, which is usually size. It might also reduce in e.g. number of + * MulNodes or Load/Store nodes, swapping out more "expensive" Nodes for cheaper + * ones. + * <br> + * The theoretical overall worklist is mindless just grabbing the next thing and + * doing it. If the graph changes, put the neighbors on the worklist. + * <br> + * Lather, Rinse, Repeat until the worklist runs dry. + * <p> + * The main issues we have to deal with: + * + * <ul> + * <li>Nodes have uses; replacing some set of Nodes with another requires more graph + * reworking. Not rocket science, but it can be fiddly. Its helpful to have a + * small set of graph munging utilities, and the strong invariant that the graph + * is stable and correct between peepholes. In our case `Node.subsume` does + * most of the munging, building on our prior stable Node utilities.</li> + * + * <li>Changing a Node also changes the graph "neighborhood". The neighbors need to + * be checked to see if THEY can also peephole, and so on. After any peephole + * or graph update we put a Nodes uses and defs on the worklist.</li> + * + * <li>Our strong invariant is that for all Nodes, either they are on the worklist + * OR no peephole applies. This invariant is easy to check, although expensive. + * Basically the normal "iterate peepholes to a fixed point" is linear, and this + * check is linear at each peephole step... so quadratic overall. It's a useful + * assert, but one we can disable once the overall algorithm is stable - and + * then turn it back on again when some new set of peepholes is misbehaving. + * The code for this is turned on in `IterPeeps.iterate` as `assert + * progressOnList(stop);`</li> + * </ul> + */ +public class IterPeeps { + + public final WorkList<Node> _work; + + public IterPeeps( long seed ) { _work = new WorkList<>(seed); } + + @SuppressWarnings("unchecked") + public <N extends Node> N add( N n ) { return (N)_work.push(n); } + + public void addAll( Ary<Node> ary ) { _work.addAll(ary); } + + /** + * Iterate peepholes to a fixed point + */ + public void iterate( CodeGen code ) { + assert progressOnList(code, _work, true); + int cnt=0; + + Node n; + while( (n=_work.pop()) != null ) { + if( n.isDead() ) continue; + cnt++; // Useful for debugging, searching which peephole broke things + Node x = n.peepholeOpt(); + if( x != null ) { + if( x.isDead() ) continue; + // peepholeOpt can return brand-new nodes, needing an initial type set + if( x._type==null ) x.setType(x.compute()); + // Changes require neighbors onto the worklist + if( x != n || !(x instanceof ConstantNode) ) { + // All outputs of n (changing node) not x (prior existing node). + for( Node z : n._outputs ) _work.push(z); + // Everybody gets a free "go again" in case they didn't get + // made in their final form. + _work.push(x); + // If the result is not self, revisit all inputs (because + // there's a new user), and replace in the graph. + if( x != n ) { + for( Node z : n. _inputs ) _work.push(z); + for( Node z : x. _inputs ) _work.push(z); + for( Node z : x._outputs ) _work.push(z); + n.subsume(x); + } + } + // If there are distant neighbors, move to worklist + n.moveDepsToWorklist(); + JSViewer.show(); // Show again + assert progressOnList(code, _work, true); // Very expensive assert + } + if( n.isUnused() && !(n instanceof StopNode) ) + n.kill(); // Just plain dead + } + + } + + // Visit ALL nodes and confirm the invariant: + // Either you are on the _work worklist OR running `iter()` makes no progress. + + // This invariant ensures that no progress is missed, i.e., when the + // worklist is empty we have indeed done all that can be done. To help + // with debugging, the {@code assert} is broken out in a place where it is easy to + // stop if a change is found. + + // Also, the normal usage of `iter()` may attempt peepholes with distance + // neighbors and these should fail, but will then try to add dependencies + // {@link #Node.addDep} which is a side effect in an assert. The {@link + // #midAssert} is used to stop this side effect. + // Pessimistic solver assert + public static boolean progressOnList(CodeGen code, WorkList<Node> list, boolean dir ) { + code._midAssert = true; + Node changed = code._stop.walk( n -> { + Node m = n; + Type nval = n.compute(); + + // Types must be forwards, even if on the worklist. + boolean checkType = !n.iskeep() || + n._nid <= 8 || + (n instanceof ProjNode && n.in(0) instanceof StartNode); + + boolean monotonic = dir + ? nval.isa(n._type) // Pesi: new value lifts over old + : n._type.isa(nval); // Opto: new value falls over old + assert !checkType || monotonic : "Non-monotonic peep: "+n+"#"+n._nid+" old="+n._type+" new="+nval+" inputs="+inputTypes(n)+" peep="+m; + if( list.on(n) ) + return null; + + assert n.nOuts() > 0 : "Unused live node: "+n; + m = n.peepholeOpt(); + assert m==null : "Peep fired and not on worklist, "+n.getClass().getSimpleName()+"#"+n._nid+" -> "+m; + return m; + }); + code._midAssert = false; + return changed==null; + } + + private static String inputTypes(Node n) { + StringBuilder sb = new StringBuilder("["); + for( Node in : n._inputs ) + sb.append(in==null ? "null" : in.getClass().getSimpleName()+"#"+in._nid+"="+in+":"+in._type+":keep="+in.iskeep()).append(','); + return sb.append(']').toString(); + } + + /** + * Classic WorkList, with a fast add/remove, dup removal, random pull. + * The Node's nid is used to check membership in the worklist. + */ + @SuppressWarnings("unchecked") + public static class WorkList<E extends Node> { + + private Node[] _es; + private int _len; + private final BitSet _on; // Bit set if Node._nid is on WorkList + private final Random _R; // For randomizing pull from the WorkList + private final long _seed; + + /* Useful stat - how many nodes are processed in the post parse iterative opt */ + private long _totalWork = 0; + + public WorkList() { this(123); } + WorkList(long seed) { + _es = new Node[1]; + _len=0; + _on = new BitSet(); + _seed = seed; + _R = new Random(); + _R.setSeed(_seed); + } + + /** + * Pushes a Node on the WorkList, ensuring no duplicates + * If Node is null it will not be added. + */ + public E push( E x ) { + if( x==null ) return null; + int idx = x._nid; + if( !_on.get(idx) ) { + _on.set(idx); + if( _len==_es.length ) + _es = Arrays.copyOf(_es,_len<<1); + _es[_len++] = x; + _totalWork++; + } + return x; + } + + public void addAll( Ary<E> ary ) { + for( E n : ary ) + push(n); + } + public void addAll( E[] es ) { + for( E n : es ) + push(n); + } + + + /** + * True if Node is on the WorkList + */ + public boolean on( E x ) { return _on.get(x._nid); } + boolean isEmpty() { return _len==0; } + Node[] asAry() { return Arrays.copyOf(_es,_len); } + + /** + * Removes a random Node from the WorkList; null if WorkList is empty + */ + public E pop() { + if( _len == 0 ) return null; + int idx = _R.nextInt(_len); + E x = (E)_es[idx]; + _es[idx] = _es[--_len]; // Compress array + _on.clear(x._nid); + return x; + } + + public void clear() { + _len = 0; + _on.clear(); + _R.setSeed(_seed); + _totalWork = 0; + } + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java new file mode 100644 index 00000000..98f8698f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java @@ -0,0 +1,2748 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.GraphVisualizer; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.node.ScopeNode.Kind; + +import static com.seaofnodes.simple.util.Utils.TODO; + +import java.util.*; + +/** + * The Parser converts a Simple source program to the Sea of Nodes intermediate + * representation directly in one pass. There is no intermediate Abstract + * Syntax Tree structure. + * <p> + * This is a simple recursive descent parser. All lexical analysis is done here as well. + */ +public class Parser { + + // Compile driver + public final CodeGen _code; + + // The Lexer. Thin wrapper over a byte[] buffer with a cursor. + private Lexer _lexer; + + // Class prefix string; TODO: use something short: "%" + public static final String CLZ = "class:"; + private static final String clzPrefix = CLZ; + public static String addClzPrefix( String x ) { + assert !x.startsWith(clzPrefix); + return (clzPrefix+x).intern(); + } + public static boolean startsClzPrefix( String s ) { + return s.startsWith(clzPrefix); + } + public static boolean isPublicClassName( String name ) { + if( startsClzPrefix(name) ) name = name.substring(clzPrefix.length()); + return !name.isEmpty() && name.charAt(0)!='_' && name.indexOf("._")==-1; + } + + // Source file for compilation + public CompUnit _ref; + + // Current classname being parsed + private String _nestedType; + /** + * Current ScopeNode - ScopeNodes change as we parse code, but at any point of time + * there is one current ScopeNode. The reason the current ScopeNode can change is to do with how + * we handle branching. See {@link #parseIf()}. + * <p> + * Each ScopeNode contains a stack of lexical scopes, each scope is a symbol table that binds + * variable names to Nodes. The top of this stack represents current scope. + * <p> + * We keep a list of all ScopeNodes so that we can show them in graphs. + * @see #parseIf() + * @see #_xScopes + */ + public ScopeNode _scope; + + /** + * List of keywords disallowed as identifiers + */ + public static final HashSet<String> KEYWORDS = new HashSet<>(){{ + add("bool"); + add("break"); + add("byte"); + add("continue"); + add("else"); + add("f32"); + add("f64"); + add("false"); + add("flt"); + add("i16"); + add("i32"); + add("i64"); + add("i8"); + add("if"); + add("int"); + add("new"); + add("null"); + add("return"); + add("struct"); + add("true"); + add("u1"); + add("u16"); + add("u32"); + add("u8"); + add("while"); + }}; + + /** + * We clone ScopeNodes when control flows branch; it is useful to have + * a list of all active ScopeNodes for purposes of visualization of the SoN graph + */ + public final Stack<ScopeNode> _xScopes = new Stack<>(); + + ScopeNode _breakScope; // Merge all the while-breaks here + ScopeNode _continueScope; // Merge all the while-continues here + ScopeNode _returnScope; // Merge all the function exits here + TypeStruct _ctorOpenStruct; // Open struct being initialized by an allocation constructor block. + + // Mapping from a type name to a Type. The string name matches + // `type.str()` call. + public static final HashMap<String, Type> TYPES = new HashMap<>(); + public static final HashMap<String, Type> INIT_TYPES = + new HashMap<>() {{ + put("bool",TypeInteger.U1 ); + put("byte",TypeInteger.U8 ); + put("f32" ,TypeFloat .F32); + put("f64" ,TypeFloat .F64); + put("flt" ,TypeFloat .F64); + put("i16" ,TypeInteger.I16); + put("i32" ,TypeInteger.I32); + put("i64" ,TypeInteger.BOT); + put("i8" ,TypeInteger.I8 ); + put("int" ,TypeInteger.BOT); + put("u1" ,TypeInteger.U1 ); + put("u16" ,TypeInteger.U16); + put("u32" ,TypeInteger.U32); + put("u8" ,TypeInteger.U8 ); + put("val" ,Type.TOP); // Marker type, indicates type inference + put("var" ,Type.BOTTOM); // Marker type, indicates type inference + }}; + + + public Parser(CodeGen code ) { + _code = code; + } + + @Override + public String toString() { return _lexer==null ? "" : _lexer.toString(); } + + // Debugging utility to find a Node by index + public Node f(int nid) { return _code.f(nid); } + + // Read and set CTRL + private Node ctrl() { return _scope.ctrl(); } + private <N extends Node> N ctrl(N n) { return _scope.ctrl(n); } + // Read and set Memory + private Node mem() { return _scope.mem(1); } + private void mem(Node mem) { _scope.mem(mem); } + // Structural label for precise memory Phis. The '$' prefix cannot be + // referenced in Simple source. + public static String memName(int alias) { return ("$"+alias).intern(); } + + // Ordinary parsing carries one whole-memory value. Alias partitioning is + // graph optimization, not parser state. + private void storeMem( Node st, int alias, Node prior ) { + mem(alias==1 ? st : mergeAlias(prior,alias,st)); + } + + // A semantic graph partition representing the complete memory after a + // precise alias update. ScopeNode treats this as one opaque value. + private Node mergeAlias(Node bulk, int alias, Node precise) { + assert bulk != null : "Missing bulk memory for alias #"+alias; + MemMergeNode merge = new MemMergeNode(false,null,bulk); + merge.alias(alias,precise); + return peep(merge); + } + + + public Ary<FRefNode> parse( CompUnit ref ) { + assert _scope == null && _breakScope == null && _continueScope == null && _returnScope == null; + _lexer = new Lexer(ref._src); + // Starting Scope has control, memory, initial arguments + _scope = new ScopeNode(); + _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer); + _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer); + // Track active scopes for Graph display + _xScopes.push(_scope); + ctrl(_code.XCTRL); + mem(con(TypeMem.BOT)); + + // File-level struct declaration + _ref = ref; + _nestedType = ref._cname; + String typeName = addClzPrefix(ref._cname); + + ReturnNode clzret = parseStruct( true, typeName ); + + if( !_lexer.isEOF() ) throw _errorSyntax("unexpected"); + + // At the top scope and every new var should be a FRef, and these + // should immediately resolve to a file-based name which is a Simple + // class type. + Ary<FRefNode> frefs = new Ary<>(FRefNode.class); + for( int i=2; i<_scope._vars._len; i++ ) { + FRefNode fref = (FRefNode)_scope._inputs.at(i); + CompUnit cu = ParseAll.findCompUnitOrThrow(_code,_ref,fref._name); + fref._con = TypeMemPtr.make((byte)2,TypeStruct.make(addClzPrefix(cu._cname),true),true); + frefs.push(fref); + } + + // The class being published + ref._clz = (TypeStruct)TYPES.get(typeName); + + // Clean up and reset + _xScopes.pop(); + _scope.kill(); + _scope = null; + + return frefs; + } + + + + /** + * Parses a function body, assuming the header is parsed. + */ + private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, String[] ids) { + // Record & restore the existing scope vars. The function parse might + // update fields, so when the parse is done reset back to the pre-parse + // state. + Node[] preParse = _scope.save(); + + // Parse function body normally + ReturnNode ret = _parseFunctionBody(null, sig,loc,ids); + + // Reset all fields to pre-parse days + _scope.restore(preParse); + return ret; + } + + + // Parse a function body + + // Stack the {break,continue,return} scopes and a new lexical scope. + // Build a FunNode and parameters, extending scope by parm names. + // Parse the function body + // - while( not end-of-function ) + // - - parseStatement + // - - If isInit, also incrementally extend the self-type. + // If isInit + // - upgrade and close the self-type + // - Gather fields and store values + // - <init> returns self, not last + // Unstack the {break,continue,return} scopes. + + private ReturnNode _parseFunctionBody( String funName, TypeFunPtr sig, Lexer loc, String[] ids) { + boolean isInit = FunNode.isInit(funName); + boolean isCtor = isInit || isExplicitConstructorName(funName); + String typeName = isCtor ? ((TypeMemPtr)sig._sig[0])._obj._name : null; + + // Stack parser state on the local Java stack, and unstack it later + _scope.push(new Kind.Func(funName)); + ScopeNode breakScope = _breakScope; _breakScope = null; + ScopeNode continueScope = _continueScope; _continueScope = null; + ScopeNode returnScope = _returnScope; _returnScope = null; + + int oldUID = _code.UID(); // Used to approximate function size + FunNode fun = (FunNode)peep(new FunNode(loc(),sig, funName, _ref, null, _ref._start)); + // Once the function header is available, install in linker table - + // allowing recursive functions. Linker matches on declared args and + // exact fidx, and ignores the return (because the fidx will only match + // the exact single function). + _code.link(fun); + + Node rpc = new ParmNode("$rpc",0,TypeRPC.BOT,fun,ConstantNode.seed(TypeRPC.BOT).peephole()).peephole(); + + // Pre-call the function from Start, with worse-case arguments. This + // represents all the future, yet-to-be-parsed functions calls and + // external calls. + _scope.ctrl(fun); // Scope control from function + // Private mem alias tracking per function + Node defaultMem = new ProjNode(_ref._start,1,ScopeNode.MEM0).peephole(); + Node privMem = new ParmNode(ScopeNode.MEM0,1,TypeMem.BOT,fun,defaultMem).peephole(); + mem(privMem); + // All args, "as-if" called externally + for( int i=0; i<ids.length; i++ ) { + Type t = sig.arg(i); + // Args take a default input if called from Start/external-world + _scope.define(ids[i], t, i==0 && ids[0]=="self", new ParmNode(ids[i],i+2,t,fun,ConstantNode.seed(t).peephole()).peephole(), loc); + } + + // Parse the function body. + Node last=_code.ZERO; // Last statement as the default result + + int nvar = 0; + while (!peek('}') && !_lexer.isEOF()) { + // Parse a statement; record last statement as default return + last = _code.add(parseStatement()); + // For <init> and <clinit>, local vars are really struct fields and + // get exposed as discovered. This allows late local functions to + // use early declared fields. + if( isInit ) + nvar = updateSelfAsFieldsDiscovered( typeName, nvar ); + } + + ParmNode self = fun.parm(2); + // Last expression is the return + if( ctrl()._type==Type.CONTROL ) { + // Constructors get special return types + if( fun.isClz() ) { + // <clinit> only returns an integer, which becomes an OS exit code + // if the <clinit> is run directly + //if( !(last._type instanceof TypeInteger) ) + // last = _code.ZERO; + assert last!=null; + } else if( isCtor ) { + last = fun.parm(3); + } + addReturn(last); + // exit path is now dead (nothing live after a "return") + } + if( isCtor ) + upgradeSelfTypeAndStoreFields( typeName, fun.isClz(), self, fun.parm(3), isInit ); + + // Build a return from the _returnScope. + // Can be no returns for never-exit functions + Node rctl = _returnScope==null ? _code.XCTRL : _returnScope.ctrl().peephole(); + Node rmem = rctl==_code.XCTRL ? con(TypeMem.TOP) : _returnScope.mem(); + Node expr = rctl==_code.XCTRL ? con(Type.TOP) : _returnScope._inputs.last().peephole(); + ReturnNode ret = (ReturnNode)peep(new ReturnNode(rctl, rmem, expr, rpc, fun)); + fun.setRet(ret); + // Approximate function size, for inlining heuristics + fun._approxUIDs = _code.UID() - oldUID; + for( Node cend : ret.outs() ) + if( cend instanceof CallEndNode ) + throw TODO(); //_code.add(cend); // have call ends ALREADY? just barely made the Return + + // Export (and keep-alive) functions until prove dead + _ref.addFun(_code,fun); + + // Unstack parser state + if( _returnScope != null ) _returnScope.kill(); + _returnScope = returnScope; + _continueScope = continueScope; + _breakScope = breakScope; + _scope.pop(); + + return ret; + } + + private int updateSelfAsFieldsDiscovered( String typeName, int nvar ) { + int lex = _scope.klast()._lexSize + 2/*Skip self and selfMem*/; + // Load self type in and out of the TYPES hashtable. + TypeStruct tself = (TypeStruct)TYPES.get(typeName); + // Start looking for newly declared vars at the lexical scope start, + // skipping two ("self" and "selfmem") and fields seen already. + while( lex + nvar < _scope.nIns() ) { + Var v = _scope.var(lex + nvar++); + // Field with sharper type + Field old = tself.field(v._name); + Field fld = old == null + ? Field.make(v._name,v.type(),_code.alias(_ref._cname),v._final) + : Field.make(v._name,v.type(), old._alias, old._final); + tself = tself.addOrUpdate(fld); + } + TYPES.put(typeName,tself); + return nvar; + } + + + void upgradeSelfTypeAndStoreFields( String typeName, boolean isClz, ParmNode self, ParmNode smem, boolean upgrade ) { + int lex = _scope.klast()._lexSize; + assert _scope.var(lex)._name=="self"; + TypeStruct tself = (TypeStruct)TYPES.get(typeName); + int base = lex+2; // Skip ctrl, mem + if( upgrade ) { + // Default <init>/<clinit> defines the struct and can sharpen fields. + // A named constructor only initializes the already-closed struct. + for( int i=0; i<tself.nkids(); i++ ) { + Field tfld = tself._fields[i]; + int vidx = fieldVarIdx(base,tfld._fname); + if( vidx == -1 ) + continue; + Var v = _scope.var(vidx); + // Forward-ref fields where placed in tself but will get promoted + // to an outer scope - and should not be in tself. + if( v._fref ) + tself = tself.remove(i--); + else if( v.type() != tfld._t ) { + assert v.type().isa(tfld._t); + tself = tself.replace( tfld.makeFrom( v.type() ) ); + } + } + + // Close Struct type after parsing struct body. + TYPES.put( tself._name, tself = tself.close() ); + // Improve self, selfMem types in scope. + // Class self-type is a singleton pointer. Instance <init> receives the + // broad allocation pointer, while private memory tracks initialized fields. + self._declaredType = self._type = isClz ? TypeMemPtr.make((byte)2,tself,true) : TypeMemPtr.make(constructorRecv(tself)); + FunNode fun = self.fun(); + fun.setSig(fun.sig().makeFrom(self._type,0)); + if( !isClz ) // <init> returns upgraded private memory + smem._declaredType = smem._type = TypeMem.makePrivate(tself); + } + + // When can _returnScope be null here? A never-exit constructor will + // not have any returns, and thus no need to gather values and store + // them into the (never) constructed object + if( _returnScope==null ) + return; + + // A MemMerge to gather field updates as stores. Classes update the + // normal public memory. <init> updates the private memory that got + // passed in - is treated like a normal argument and not like memory. + Node mmm = isClz ? _returnScope.mem(1) : smem; + + + // Store constructor results into fields + for( int i=0; i<tself.nkids(); i++ ) { + Field fld = tself._fields[i]; + int vidx = upgrade + ? fieldVarIdx(base,fld._fname) + : fieldVarIdxBefore(lex,fld._fname); + if( vidx == -1 ) + continue; + Var v = _scope.var(vidx); + if( v._fref ) continue; // Forward refs not declared here + // NewNode's TOP field is the zero-code poison for a required + // field. The parser Var retains the typed nullable state for + // early-read diagnostics; only a user constructor writes memory. + if( upgrade && v._uninit ) continue; + Node val = _returnScope.in(vidx); + if( !upgrade && v._uninit && val._type instanceof TypeNil tn && tn.nullable() ) + throw error("'"+tself._name+"' is not fully initialized, field '"+ + fld._fname+"' is only partially set in the constructor",v._loc); + // Store value into extended struct + Node prior = mmm.keep(); + Node st = peep(new StoreNode(null, fld._fname, fld._alias, fld._t, null, prior, self, off(tself,fld._fname), val, fld._final)); + mmm = mergeAlias(prior,fld._alias,st); + prior.unkeep(); + } + mmm = mmm.peephole(); + + if( !isClz ) + // Stuff private mem into normal expression return value. + _returnScope.setDef(_returnScope.nIns()-1,mmm); + else + // Parser memory is one opaque whole-memory edge. Publish the + // completed class-field aggregate as the <clinit> return memory. + _returnScope.mem(mmm); + //_code.add(mmm); + } + + private int fieldVarIdx( int base, String name ) { + for( int i=base; i<_scope.nIns(); i++ ) + if( _scope.var(i)._name == name ) + return i; + return -1; + } + + // Named constructors update field Vars from the enclosing initializer + // scope. Constructor arguments and locals begin at limit and must not + // shadow the field state gathered into private memory. + private int fieldVarIdxBefore( int limit, String name ) { + for( int i=limit-1; i>=0; i-- ) + if( _scope.var(i)._name == name ) + return i; + return -1; + } + + private boolean isConstructorDecl(String name) { + TypeStruct self = _scope.constructorSelf(); + return self != null && name == initFieldName(self._name); + } + + private boolean inExplicitConstructor() { + if( !(_scope.klast() instanceof Kind.Func func) ) + return false; + return isExplicitConstructorName(func._name); + } + + private boolean isExplicitConstructorName(String funName) { + TypeStruct self = _scope.constructorSelf(); + return self != null && funName == (self._name+"."+initFieldName(self._name)).intern(); + } + + private static TypeStruct constructorRecv(TypeStruct self) { + Field[] fs = new Field[self._fields.length]; + for( int i=0; i<fs.length; i++ ) { + Field fld = self._fields[i]; + fs[i] = fld._t instanceof TypeFunPtr ? fld : fld.makeFrom(fld._t.glb(true)); + } + return TypeStruct.make(self._name,self._open,fs); + } + + + /** + * Parses a block + * + * <pre> + * '{' statements '}' + * </pre> + * Does not parse the opening or closing '{}' + * @return a {@link Node} or {@code null} + */ + private Node parseBlock(Kind kind) { + // Enter a new scope + _scope.push(kind); + Node last = _code.ZERO; + while (!peek('}') && !_lexer.isEOF()) { + _code.add(last); + last = parseStatement(); + } + // Exit scope + last.keep(); + _scope.pop(); + return last.unkeep(); + } + + /** + * Parses a statement + * + * <pre> + * returnStatement | declStatement | blockStatement | ifStatement | expressionStatement + * </pre> + * @return a {@link Node} or {@code null} + */ + private Node parseStatement() { + if( matchx("return") ) return parseReturn(); + else if (matchx("if") ) return parseIf(); + else if (matchx("while") ) return parseWhile(); + else if (matchx("for") ) return parseFor(); + else if (matchx("break") ) return parseBreak(); + else if (matchx("continue")) return parseContinue(); + else if (matchx("struct") ) return parseStruct(); + else if (inStructDeclaration() && matchx("new")) return parseConstructorDeclaration(); + else if (matchx("#showGraph")) return require(showGraph(),";"); + else if (matchx(";") ) return _code.ZERO; // Empty statement + // Break ambiguity around leading function types and starting a block + else if (peek('{') && !isTypeFun() ) { + match("{"); + return require(parseBlock(new Kind.Block()),"}"); + } + // Declaration or normal assignment/expression + else return parseDeclarationStatement(); + } + + private boolean inStructDeclaration() { + return _scope.klast() instanceof Kind.Func fun && FunNode.isInstance(fun._name); + } + + // Constructor declaration: new TypeName = { args -> body }; + private Node parseConstructorDeclaration() { + TypeStruct self = _scope.constructorSelf(); + assert self != null; + String name = requireId(); + String expect = initFieldName(self._name); + if( name != expect ) + throw error("Constructor name '" + name + "' must match type name '" + expect + "'"); + require("="); + Node ctor = constructorFunc(name); + TypeFunPtr sig = (TypeFunPtr)ctor._type; + String clzName = addClzPrefix(self._name); + TypeStruct clz = (TypeStruct)TYPES.get(clzName); + Field old = clz==null ? null : clz.field("<ctor>"); + if( old != null ) + throw error("Redefining constructor '" + name + "'"); + Field fld = Field.make("<ctor>",sig,_code.alias(_ref._cname),true); + TYPES.put(clzName,clz==null ? TypeStruct.make(clzName,true,fld) : clz.add(fld)); + return require(ctor,";"); + } + + /** + * Parses a while statement + * + * <pre> + * while ( expression ) statement + * </pre> + * @return a {@link Node}, never {@code null} + */ + private Node parseWhile() { + require("("); + return parseLooping(false); + } + + + /** + * Parses a for statement + * + * <pre> + * for( var x=init; test; incr ) body + * </pre> + * @return a {@link Node}, never {@code null} + */ + private Node parseFor() { + // { var x=init,y=init,...; + // while( test ) { + // body; + // next; + // } + // } + require("("); + _scope.push(new Kind.Block()); // Scope for the index variables + if( !match(";") ) // Can be empty init "for(;test;next) body" + parseDeclarationStatement(); // Non-empty init + Node rez = parseLooping(true); + _scope.pop(); // Exit index variable scope + return rez; + } + + // Shared by `for` and `while` + private Node parseLooping( boolean doFor ) { + + var savedContinueScope = _continueScope; + var savedBreakScope = _breakScope; + + // Loop region has two control inputs, the first is the entry + // point, and second is back edge that is set after loop is parsed + // (see end_loop() call below). Note that the absence of back edge is + // used as an indicator to switch off peepholes of the region and + // associated phis; see {@code inProgress()}. + + ctrl(new LoopNode(loc(),null,ctrl()).peephole()); // Note we set back edge to null here + + // At loop head, we clone the current Scope (this includes all + // names in every nesting level within the Scope). + // We create phis eagerly for all the names we find, see dup(). + + // Save the current scope as the loop head + ScopeNode head = _scope.keep(); + // Clone the head Scope to create a new Scope for the body. + // Create phis eagerly as part of cloning + _xScopes.push(_scope = _scope.dup(true)); // The true argument triggers creating phis + + // Parse predicate + var pred = peek(';') ? con(1) : parseAsgn(); + require( doFor ? ";" : ")" ); + + // IfNode takes current control and predicate + Node ifNode = new IfNode(ctrl(), pred.keep()).peephole(); + // Setup projection nodes + Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep(); + Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole(); + + // for( ;;next ) body + int nextPos = -1, nextEnd = -1; + if( doFor ) { + // Skip the next expression and parse it later + nextPos = pos(); + skipAsgn(); + nextEnd = pos(); + require(")"); + } + + // Clone the body Scope to create the break/exit Scope which accounts for any + // side effects in the predicate. The break/exit Scope will be the final + // scope after the loop, and its control input is the False branch of + // the loop predicate. Note that body Scope is still our current scope. + ctrl(ifF); + _xScopes.push(_breakScope = _scope.dup()); + _breakScope.addGuards(ifF,pred,true); // Up-cast predicate + + // No continues yet + _continueScope = null; + + // Parse the true side, which corresponds to loop body + // Our current scope is the body Scope + ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection + _scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate + parseStatement().isKill(); // Parse loop body + _scope.removeGuards(ifT); + + // Merge the loop bottom into other continue statements + if (_continueScope != null) { + _continueScope = jumpTo(_continueScope); + _scope.kill(); + _scope = _continueScope; + } + + // Now append the next code onto the body code + if( doFor ) { + int old = pos(nextPos); + if( !peek(')') ) + parseAsgn(); + if( pos() != nextEnd ) + throw errorSyntax( "Unexpected code after expression" ); + pos(old); + } + + // The true branch loops back, so whatever is current _scope.ctrl gets + // added to head loop as input. endLoop() updates the head scope, and + // goes through all the phis that were created earlier. For each phi, + // it sets the second input to the corresponding input from the back + // edge. If the phi is redundant, it is replaced by its sole input. + var exit = _breakScope; + head.endLoop(_scope, exit); + head.unkeep().kill(); + + _xScopes.pop(); // Cleanup + _xScopes.pop(); // Cleanup + + _continueScope = savedContinueScope; + _breakScope = savedBreakScope; + + // At exit the false control is the current control, and + // the scope is the exit scope after the exit test. + // During sys parsing, there is no xscope here. + if( !_xScopes.isEmpty() ) { + _xScopes.pop(); + _xScopes.push( exit ); + } + _scope = exit; + return _code.ZERO; + } + + private ScopeNode jumpTo(ScopeNode toScope) { + ScopeNode cur = _scope.dup(); + ctrl(_code.XCTRL); // Kill current scope + // Prune nested lexical scopes that have depth > than the loop head + // We use _breakScope as a proxy for the loop head scope to obtain the depth + while( cur.depth() > _breakScope.depth() ) + cur.pop(); + // If this is a continue then first time the target is null + // So we just use the pruned current scope as the base for the + // "continue" + if( toScope == null ) + return cur; + // toScope is either the break scope, or a scope that was created here + assert toScope.depth() <= _breakScope.depth(); + toScope.ctrl(toScope.mergeScopes(cur,loc()).peephole()); + return toScope; + } + + private void checkLoopActive() { if (_breakScope == null) throw error("No active loop for a break or continue"); } + + private Node parseContinue() { checkLoopActive(); _continueScope = require(jumpTo( _continueScope ),";"); return _code.ZERO; } + private Node parseBreak () { + checkLoopActive(); + // At the time of the break, and loop-exit conditions are only valid if + // they are ALSO valid at the break. It is the intersection of + // conditions here, not the union. + _breakScope.removeGuards(_breakScope.ctrl()); + _breakScope = require(jumpTo(_breakScope ),";"); + _breakScope.addGuards(_breakScope.ctrl(), null, false); + return _code.ZERO; + } + + // Look for an unbalanced `)`, skipping balanced + private void skipAsgn() { + int paren=0; + while( true ) + // Next X char handles skipping complex comments + switch( _lexer.nextXChar() ) { + case Character.MAX_VALUE: + throw TODO(); + case ')': + if( --paren<0 ) { + posT( pos() - 1 ); + return; // Leave the `)` behind + } + break; + case '(': paren ++; break; + default: break; + } + } + + + /** + * Parses a statement + * + * <pre> + * if ( expression ) statement [else statement] + * </pre> + * @return a {@link Node}, never {@code null} + */ + private Node parseIf() { + // Parse predicate + require("("); + var pred = require(parseAsgn(), ")"); + return parseTrinary(pred,"else"); + } + + // Parse a conditional expression, merging results. + private Node parseTrinary( Node pred, String fside ) { + // IfNode takes current control and predicate + pred.keep(); + Node ifNode = new IfNode(ctrl(), pred).peephole(); + // Setup projection nodes + Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep(); + Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep(); + // In if true branch, the ifT proj node becomes the ctrl + // But first clone the scope and set it as current + ScopeNode fScope = _scope.dup(); // Duplicate current scope + _xScopes.push(fScope); // For graph visualization we need all scopes + + // Parse the true side + ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection + _scope.addGuards(ifT,pred,false); // Up-cast predicate + // Parse true-side flavor + Node lhs = switch( fside ) { + case "else" -> parseStatement(); // if( pred ) stmts; + case ":" -> parseAsgn(); // pred ? asgn; + case "&&" -> parseLogical(); // pred && expr + case "||" -> pred; // pred || expr_is_ignored + default -> throw TODO(); + }; + lhs.keep(); + _scope.removeGuards(ifT); + + // See if a one-sided def was made: "if(pred) int x = 1;" and throw. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + fScope.balanceIf(_scope); + + ScopeNode tScope = _scope; + + // Parse the false side + _scope = fScope; // Restore scope, then parse else block if any + ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection + // Up-cast predicate, even if not else clause, because predicate can + // remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;` + _scope.addGuards(ifF,pred,true); + // Parse false-side flavor + boolean doRHS = false; // RHS is optional for if/else and trinary + Node rhs = switch( fside ) { + case "else" -> (doRHS=match(fside)) ? parseStatement() : con(lhs._type.makeZero()); + case ":" -> (doRHS=match(fside)) ? parseAsgn() : con(lhs._type.makeZero()); + case "&&" -> rhs = pred; + case "||" -> rhs = parseLogical(); + default -> throw TODO(); + }; + rhs.keep(); + _scope.removeGuards(ifF); + if( doRHS ) + fScope = _scope; + pred.unkeep(); + + // Check the trinary widening int/flt + if( !fside.equals("else") ) { + rhs = widenInt( rhs.unkeep(), lhs._type ).keep(); + lhs = widenInt( lhs.unkeep(), rhs._type ).keep(); + } + + _scope = tScope; + _xScopes.pop(); // Discard pushed from graph display + // See if a one-sided def was made: "if(pred) int x = 1;" and throw. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + tScope.balanceIf(fScope); + + // Merge results + RegionNode r = ctrl(tScope.mergeScopes(fScope,loc())); + Node ret = peep(new PhiNode("",r,lhs.unkeep(),rhs.unkeep())); + // Immediately fail e.g. `arg ? 7 : ptr` + ParseException err; + if( !fside.equals("else") && (err=ret.err()) !=null ) throw err; + r.peephole(); + return ret; + } + + /** + * Parses a return statement; "return" already parsed. + * The $ctrl edge is killed. + * + * <pre> + * 'return' expr ; + * </pre> + * @return an expression {@link Node}, never {@code null} + */ + private Node parseReturn() { + var expr = require(parseAsgn(), ";"); + return addReturn(expr); + } + private Node addReturn(Node expr) { + expr.keep(); + // Need default memory, since it can be lazy, need to force + // a non-lazy Phi + mem(); // Force a pending lazy bulk-memory Phi + // For <init> and <clinit> - all fields in scope will be live-on-exit + // and stored into `self` and need to have lazy-phi's inserted. + int lexN = _scope.enclosingFuncOrDecl(); + Kind.Func k = (Kind.Func)_scope._kinds.at(lexN); + if( FunNode.isInit(k._name) ) { + int nestedLexSize = lexN+1 < _scope.depth() ? _scope._kinds.at(lexN+1)._lexSize : _scope.nIns(); + for( int i=k._lexSize; i<nestedLexSize; i++ ) + _scope.update(_scope.var(i),null); + if( _returnScope != null ) { + Var vexpr = _returnScope._vars.pop(); + Node oldX = _returnScope.removeLast(); + int rlen = _returnScope.nIns(); + while( rlen < nestedLexSize ) { + _returnScope._vars.add(_scope.var(rlen)); + _returnScope.addDef(con(_scope.var(rlen++).type().makeZero())); + } + _returnScope._vars.add(vexpr); + _returnScope.addDef(oldX); + } + } + + // No prior merge point? Just clone and hang on to it + if( _returnScope == null ) { + _returnScope = _scope.dup(); + while( lexN+1 < _returnScope.depth() ) + _returnScope._pop(); // Pop a nested block scope until we hit the function scope + _returnScope.define("$expr", expr._type.glb(false), true, expr, null); + + } else { + // For <init> and <clinit> - ALL FIELDS IN LAST SCOPE are live + // and need to have lazy-phi's inserted. + + // And fields might not match.... will need matching Var/define with default values + int rlen = _returnScope.nIns()-1; + RegionNode r = ctrl(new RegionNode(null, null,_returnScope.ctrl(), _scope.ctrl()).init().keep()); + _returnScope._merge(_scope, r, rlen); + Node oldExpr = _returnScope.in(rlen); + _returnScope.setDef(rlen,new PhiNode("$expr", r, oldExpr, expr).peephole()); + _code.add(r); + _returnScope.ctrl(r.unkeep()); + } + ctrl(_code.XCTRL); // Kill control + return expr.unkeep(); + } + + /** + * Dumps out the node graph + * @return {@code null} + */ + Node showGraph() { + System.out.println(new GraphVisualizer().generateDotOutput(_ref,_scope,_xScopes)); + return null; + } + + /** Parse: [name '='] expr + */ + private Node parseAsgn() { + int old = pos(); + String name = _lexer.matchId(); + // Just a plain expression, no assignment. + // Distinguish `var==expr` from `var=expr` + if( name==null || KEYWORDS.contains(name) || !matchOpx('=','=') ) + { pos(old); return parseExpression(); } + + // Find variable to update + Var def = _scope.lookup(name); + if( def==null ) + def = defineOpenConstructorField(name); + if( def==null ) + throw error("Undefined name '" + name + "'"); + + // TOP fields are for late-initialized fields; these have never + // been written to, and this must be the final write. Other writes + // outside the constructor need to check the final bit. + if( _scope.in(def._idx)._type!=Type.TOP && def._final && + // Inside an allocation, final assign is OK, outside nope. + // The alloc() call added the allocation scope + !(_scope.inConstructor() && def._idx >= _scope.klast()._lexSize) && + !inExplicitConstructor() ) + throw error("Cannot reassign final '"+name+"'"); + + // Parse assignment expression + Node expr = parseAsgn(); + + // Lift expression, based on type + Type decl = def.type(); // Declared type + Node lift = convertExpr(expr.keep(), decl).keep(); + // Update + _scope.update(name,lift); + + // Check for storing into an instance field instead of a local var + int kx = _scope.kindx(def); // Declaration scope + int fx = _scope.enclosingFunction(); // Enclosing function scope + Kind kk = _scope._kinds.at(kx); + Kind fk = _scope._kinds.at(fx); + // Access instance field from 'self' + if( kx+1 == fx && kk instanceof Kind.Func inst && FunNode.isInstance(inst._name) && + fk instanceof Kind.Func method && !inExplicitConstructor() ) { + // Named field offset on an unknown struct type + TypeMemPtr self = (TypeMemPtr)_scope.var(fk._lexSize).type(); + TypeStruct sobj = (TypeStruct)TYPES.get(self._obj._name); + Field fld = sobj.field(name); + int alias = fld._alias; + Node ptr = _scope.in(method._lexSize); + Node off = fldoff(ptr,name); + Node prior = mem().keep(); + Node st = new StoreNode(loc(), name, alias, decl, ctrl(), prior, ptr, off, lift, inExplicitConstructor()).peephole().keep(); + storeMem(st,alias,prior); + st.unkeep(); + prior.unkeep(); + } + lift.unkill(); + + + // Return un-lifted expr + return expr.unkeep(); + } + + // Widen ints to floats; narrow wide int types. + // Early error if types do not match variable. + private Node convertExpr( Node expr, Type t ) { + // A FunPtr carries the semantic edge to its function's Return. Do not + // hide that edge behind a Convert which can fold to a plain constant. + if( expr instanceof FunPtrNode && t instanceof TypeFunPtr ) + return expr; + // Auto-widen array to i64 (cast ptr to raw int bits) + if( t == TypeInteger.BOT && expr._type instanceof TypeMemPtr tmp && tmp._obj.isAry() ) + return peep(new AddNode(peep(new PtrToIntNode(expr)),off(tmp._obj,"[]"))); + // Null check has to eventually pass + if( t == Type.NIL && expr._type instanceof TypeMemPtr tmp && tmp.notNull() ) + return peep(new CheckCastNode(t,null,expr)); + // Widen ints to floats, narrow wide types + return peep(new ConvertNode(t,expr)); + } + + private Node widenInt( Node expr, Type t ) { + return (expr._type instanceof TypeInteger || expr._type==Type.NIL) && t instanceof TypeFloat + ? peep(new ToFloatNode(expr)) : expr; + } + + /** + * Parse declaration or expression statement + * declStmt = type var['=' exprAsgn][, var['=' exprAsgn]]* ';' | exprAsgn ';' + * <p> + * exprAsgn = var '=' exprAsgn | expr + */ + private Node parseDeclarationStatement() { + int old = pos(); + Type t = type(); + if( peek('.') ) // Ambiguity static vars: "type.var", parse as expression + { pos(old); t=null; } + if( t == null ) + return require(parseAsgn(),";"); + + // now parse var['=' asgnexpr] in a loop + Node n = parseDeclaration(t); + while( match(",") ) + n = parseDeclaration(t); + return require(n,";"); + } + + /** Parse final: [!]var['=' asgn] + */ + private Node parseDeclaration(Type t) { + assert t!=null; + // Has var/val instead of a user-declared type + boolean inferType = t==Type.TOP || t==Type.BOTTOM; + boolean hasBang = match("!"); + Lexer loc = loc(); + String name = requireId(); + // Optional initializing expression follows + boolean xfinal = false; + boolean fld_final = false; // Field is final, but not deeply final + boolean uninit = false; // Required non-null constructor field + Node expr; + if( match("=") ) { + if( isExternDecl() ) { + expr = externDecl(name,t); + t = expr._type; // Upgrade declared type to the exact extern decl type + } else if( isConstructorDecl(name) ) { + expr = constructorFunc(name); + } else { + expr = parseAsgn(); + } + // TOP means val and val is always final + xfinal = (t==Type.TOP) || + expr instanceof ExternNode || + // BOTTOM is var and var is always not-final + (t!=Type.BOTTOM && + // no Bang AND + !hasBang && + // not-null (expecting null to be set to not-null) + expr._type != Type.NIL && + // Pointers are final by default; int/flt are not-final by default. + (t instanceof TypeNil)); + + // var/val, then type comes from expression + if( inferType ) { + if( expr._type==Type.NIL ) + throw error("a not-null/non-zero expression"); + t = expr._type; + if( !xfinal ) t = t.glb(false); // Widen if not final + } + + // Final is deep on ptrs + if( xfinal && t instanceof TypeMemPtr tmp ) { + t = tmp.makeRO(); + expr = peep(new ReadOnlyNode(expr)); + } + + // expr is a constant function + if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) { + FunNode fun = _code.link(tfp); + if( fun != null ) + fun.setName(name); // Assign debug name to Simple function + } + + } else { + // Since no initializer, not-final but might need an initializer + + // Need an expression to infer the type. + // Also, if not-null then need an initializing expression. + // Allowed in a class def, because type/init will happen in the constructor. + if( (inferType || (t instanceof TypeNil tn && !tn.nullable() )) && !_scope.inConstructor() ) + throw errorSyntax("=expression"); + // Initial value for uninitialized struct fields. + expr = switch( t ) { + // Required pointers retain their declared shape, but include + // null until every constructor path assigns them. + case TypeNil tn -> tn.nullable() ? con(Type.NIL) : con(t.meet(Type.NIL)); + case TypeInteger ti -> _code.ZERO; + case TypeFloat tf -> con(TypeFloat.FZERO); + // Bottom signals type inference: they must be initialized in + // the constructor and that's when we'll discover the type. + case Type tt -> { assert tt==Type.BOTTOM; yield con(tt); } + }; + // Every non-null pointer without a default is constructor-required. + // Without `!` it is also shallow-final; mutability and definite + // initialization are independent properties. + if( t instanceof TypeNil tn && !tn.nullable() ) { + uninit = true; + if( !hasBang ) fld_final = true; + } + } + + // Lift expression, based on type + // Do not let an authoritative Convert hide the nullable + // not-yet-initialized state from constructor-local type flow. + Node lift = uninit ? expr : convertExpr(expr, t); + + // Define a new name + if( !_scope.define(name,t,xfinal || fld_final,lift, loc) ) + throw error("Redefining name '" + name + "'", loc); + _scope.lookup(name)._uninit = uninit; + return lift; + } + + + + /** + * Parse a struct declaration, and return the following statement. + * Structs cannot be redefined, but can be nested. + * + * @return zero + */ + private Node parseStruct( ) { + // keyword "struct" already parsed, so expect the struct name next. + String typeName = requireId(); + require("{"); + + // create type class:NESTED.typeName; no fields. + + String old = _nestedType; + String fullName = _scope.depth()==1 && _nestedType.endsWith(typeName) ? old : (old+"."+typeName).intern(); + _nestedType = fullName; // Recursive structs start with this as their basename + + ReturnNode ret = parseStruct( false, fullName ); + + _nestedType = old; + + // Insert a field into class:NESTED.typeName of "val typeName = fcn-ptr-to-init" + // Function does malloc internally. + // Future self: means subtyping has to deal with "who does the malloc" + String clzName = addClzPrefix(fullName); + TypeFunPtr sig = ret.fun().sig(); + TypeStruct ts = (TypeStruct)TYPES.get(clzName); + String initName = initFieldName(fullName); + Field oldFld = ts==null ? null : ts.field(initName); + Field fld = oldFld==null + ? Field.make(initName,sig,_code.alias(_ref._cname),true) + : oldFld.makeFrom(sig); + Field ctorFld = ts==null ? null : ts.field("<ctor>"); + ts = ctorFld==null + ? TypeStruct.make(clzName,false,fld) + : TypeStruct.make(clzName,false,fld,ctorFld); + TYPES.put(clzName,ts); + + // Insert a field in the containing class with the nested class type. + // This is basically sugar for "val CLZNAME = <init>" + // and is a nicer version of updateSelfAsFieldsDiscovered. + _scope.define(typeName, sig, true, new FunPtrNode(sig,_code._start,ret).peephole(), loc()); + + require("}"); + return require(_code.ZERO,";"); + } + + // Parse a struct declaration (not an allocation); file-level is a class-init, and scope structs are normal + private ReturnNode parseStruct( boolean isClz, String typeName ) { + // Record & restore global state set during parsing the <init> code + Node oldCtrl= _scope.ctrl().keep(); + Node oldMem = _scope.mem ().keep(); + + // Make the future clazz/instance struct. + TypeStruct tself = (TypeStruct)TYPES.get(typeName); + if( tself==null ) + TYPES.put(typeName, tself = TypeStruct.make(typeName,true)); + else { + assert tself._open; + TYPES.put(typeName,tself = tself.defined()); + } + + // Signature of the default constructor + TypeFunPtr sig = structInitSig(isClz,tself); + // Function and argument names of default constructor + String fname = (typeName + (isClz ? ".<clinit>" : ".<init>")).intern(); + String[] ids = new String[]{"self", isClz ? "arg" : "#selfMem"}; + + // Struct decls look like function bodies. Parse function body normally. + ReturnNode ret = _parseFunctionBody(fname,sig,loc(),ids); + // Unwind global state. + ctrl(oldCtrl.unkeep()); + mem (oldMem .unkeep()); + return ret; + } + + // <cl/init> signature: { self arg/selfMem -> BOT/selfMem } + // Self-memory is the rare *private* (never aliased) memory for the new object. + private TypeFunPtr structInitSig( boolean isClz, TypeStruct tself ) { + // Private uninitialized memory from new object, no escaped values. + TypeMem privMem = isClz ? null : TypeMem.make(1,tself,true,isClz,false,null,null); + Type targ = isClz ? TypeInteger.BOT : privMem; + // Return for private memory has to name all escaped fields, which are not known yet. + // Escape ALL fields, and sharpen later. + Type tret = isClz ? Type.BOTTOM : TypeMem.makePrivate(tself); + Type[] targs = new Type[]{ TypeMemPtr.make((byte)2,tself,isClz), targ }; + // FIDX for FREFs is in the global namespace, needs the correct clz name. + String clzBare = tself._name; + int order = 3; // 3 is always the <init> order, 2nd in the file just behind the <clinit> + if( isClz ) { + assert startsClzPrefix( clzBare ); + clzBare = clzBare.substring(clzPrefix.length()).intern(); + order = 2; // 2 is always the <clinit> order, first in file and skipping reserved #1 + } + return TypeFunPtr.make1((byte)2, true, targs, tret, _code.fidx(clzBare,order)); + } + + // A class type carries the constructor function as a required final field. + private TypeStruct classStruct( String typeName, TypeFunPtr init, boolean open ) { + return TypeStruct.make(addClzPrefix(typeName),open,Field.make(initFieldName(typeName),init,_code.alias(_ref._cname),true)); + } + + private static String initFieldName( String typeName ) { + if( startsClzPrefix(typeName) ) + typeName = typeName.substring(clzPrefix.length()); + int idx = typeName.lastIndexOf('.'); + return (idx == -1 ? typeName : typeName.substring(idx+1)).intern(); + } + + private TypeStruct latestStruct( TypeStruct ts ) { + Type t = TYPES.get(ts._name); + return t instanceof TypeStruct ts2 ? ts2 : ts; + } + + private Field fieldOrOpen( TypeStruct ts, String name, boolean xfinal ) { + ts = latestStruct(ts); + Field fld = ts.field(name); + if( fld != null ) return fld; + if( !ts._open || !ts._fref ) return null; + fld = Field.make(name,Type.BOTTOM,_code.alias(_ref._cname),xfinal); + TYPES.put(ts._name,ts = ts.add(fld)); + if( _ctorOpenStruct != null && _ctorOpenStruct._name == ts._name ) + _ctorOpenStruct = ts; + return fld; + } + + private Var defineOpenConstructorField( String name ) { + if( _ctorOpenStruct==null ) return null; + Field fld = fieldOrOpen(_ctorOpenStruct,name,true); + if( fld == null ) return null; + _scope.define(name,fld._t,fld._final,con(Type.TOP),loc()); + return _scope.lookup(name); + } + + + // Parse and return a type or null. Valid types always are followed by an + // 'id' which the caller must parse. This lets us distinguish forward ref + // types (which ARE valid here) from local vars in an (optional) forward + // ref type position. + + // t = int|i8|i16|i32|i64|u8|u16|u32|u64|byte|bool | flt|f32|f64 | val | var | struct[?] + + // Structs have nested names, and can be discovered with a partial name + // match. Example, searching for a type name "C.D" in the namespace "A.B". + // This will match "A.B.C.D", then "A.C.D", then "C.D". + + // Will also match external names like "sys.io" or "externModule.subtype.subtype" + + private Type type() { + // Only type with a leading `{` is a function pointer... + if( peek('{') ) return typeFunPtr(); + + // Otherwise you get a type name + int old1 = pos(); + String tname = _lexer.matchId(); + if( tname==null ) return null; + + // Convert the type name to a type. + Type t0 = TYPES.get(tname), t1 = t0; + // No new types as keywords + if( KEYWORDS.contains(tname) ) { + if( t0 == null ) return posT(old1); + // Something like "int" or "f64" or "var". + // Can be "int[]" so still need to check array-ness + } else { + if( t0 == Type.BOTTOM || t0 == Type.TOP ) return t0; // var/val type inference + + if( t0 != null ) { + while( true ) { + int old2 = pos(); + if( !match(".") ) break; + String sname = matchId(); + if( sname==null ) { pos(old2); break; } + sname = tname+"."+sname; + Type t = TYPES.get(sname); + if( t==null ) { pos(old2); break; } + tname = sname; + t0 = t; + } + } else { + // Type A.B.C starts with just A. Search our nested type path, + // e.g. path "X.Y.Z", in order: + + // If A==X its now a local type. + // If another dot, repeat for B==Y then C==Z + // Else if AX.B exists, done + // Else AX.B is a local forward ref. + // + // Else its a short cut form, one of: + // X.Y.Z.A + // X.Y.A + // X.A + // Else this all failed, assume a forward ref with A as an external symbol: + // A + + String root = _nestedType; + int ridx = root.indexOf('.'); + if( ridx != -1 ) root = root.substring(0,ridx); + if( tname.equals(root) ) { + boolean localForward = false; + while( true ) { + int old2 = pos(); + if( !match(".") ) break; + String sname = matchId(); + if( sname==null ) { pos(old2); break; } + sname = tname+"."+sname; + Type t = TYPES.get(sname); + if( t==null && !(_nestedType.equals(tname) || _nestedType.startsWith(tname+".")) && !localForward ) { + pos(old2); + break; + } + tname = sname; + t0 = t; + localForward |= t==null; + } + } else { + String nest = _nestedType, fullq = tname; + while( true ) { + fullq = nest+"."+tname; // Full qualified name + t0 = TYPES.get(fullq); + if( t0 != null ) + { tname = fullq; break; } // Search succeeded + // Subtract a layer from nested typenames + int idx = nest.lastIndexOf('.'); + if( idx== -1 ) break; // Search failed; leave as an external symbol + nest = nest.substring(0,idx); + } + if( t0==null ) { + CompUnit cu = ParseAll.findCompUnit(_code,_ref,tname); + if( cu != null ) { + tname = cu._cname; + t0 = TYPES.get(tname); + } + while( true ) { + int old2 = pos(); + if( !match(".") ) break; + String sname = matchId(); + if( sname==null ) { pos(old2); break; } + tname = tname+"."+sname; + t0 = TYPES.get(tname); + } + } else { + while( true ) { + int old2 = pos(); + if( !match(".") ) break; + String sname = matchId(); + if( sname==null ) { pos(old2); break; } + sname = tname+"."+sname; + Type t = TYPES.get(sname); + if( t==null ) { pos(old2); break; } + tname = sname; + t0 = t; + } + } + } + } + + tname = tname.intern(); + // Still no type found? Assume forward reference + t1 = t0 == null + ? TypeStruct.forward(tname) // Null: assume a forward ref type + : t0; + } + + // Structs are always actually references + if( t1 instanceof TypeStruct ts1 ) + t1 = TypeMemPtr.make(ts1); + + // Nest arrays and '?' as needed + Type t2 = t1; + while( true ) { + if( match("?") ) { + if( t2 instanceof TypeMemPtr tmp ) { + if( tmp.nullable() ) throw error("Type "+t2+" already allows null"); + t2 = tmp.makeNullable(); + } else + throw error("Type "+t0+" cannot be null"); + } else if( match("[~]") ) { + t2 = TypeMemPtr.make(typeAry(t2,true)); + } else if( match("[]") ) { + t2 = TypeMemPtr.make(typeAry(t2,false)); + } else + break; + } + + // Check no forward ref + if( t0 != null ) return t2; + // Check valid forward ref, after parsing all the type extra bits. + // Cannot check earlier, because cannot find required 'id' until after "[]?" syntax + int old2 = pos(); + match("!"); + String id = _lexer.matchId(); + if( !(peek(',') || peek(';') || peek('=') || match("->")) || id==null ) + return posT(old1); // Reset lexer to reparse + pos(old2); // Reset lexer to reparse + // Yes a forward ref, so declare it + forwardRefType(tname,(TypeStruct)((TypeMemPtr)t1)._obj); + // Return the (array, final) type + return t2; + } + + private TypeMemPtr forwardRefType( String tname, TypeStruct tself ) { + TYPES.put(tname,tself); + String clzName = addClzPrefix( tname ); + TYPES.put(clzName, classStruct(tname,structInitSig(false,tself),true)); + return TypeMemPtr.make(tself); + } + + private TypeMemPtr forwardRefType( String tname ) { + tname = fullTypeName(tname); + return forwardRefType(tname,TypeStruct.forward(tname)); + } + + private String fullTypeName( String tname ) { + String nest = _nestedType, fullq; + while( true ) { + fullq = nest+"."+tname; + if( TYPES.get(fullq) != null ) + return fullq.intern(); + int idx = nest.lastIndexOf('.'); + if( idx== -1 ) return fullq.intern(); + nest = nest.substring(0,idx); + } + } + + // Make an array type of t. Always record a mutable version, + // but return the requested version. + private TypeStruct typeAry( Type t, boolean efinal ) { + if( t instanceof TypeMemPtr tmp && tmp.notNull() ) + throw error("Arrays of reference types must always be nullable"); + String tname = ("[]"+t.str()).intern(); + TypeStruct ta = (TypeStruct)TYPES.get(tname); + if( ta==null ) { + // Primitive arrays use the null compunit + String rcname = TYPES.get(t) instanceof TypeStruct ? _ref._cname : ("_."+t).intern(); + ta = TypeStruct.makeAry(tname,TypeInteger.U32,_code.alias(rcname),t,_code.alias(rcname), false ); + TYPES.put(tname,ta); + } + if( !efinal ) return ta; + // Already have the aliases, just efinal is wrong + return TypeStruct.makeAry(tname,TypeInteger.U32,ta._fields[0]._alias,t,ta._fields[1]._alias,true ); + } + + private Type implicitSelfType() { + TypeStruct self = _scope.constructorSelf(); + // Ordinary instance methods accept the declared object shape. The + // constructor-only receiver starts at the low/default field values, + // and is built explicitly by constructorFunc(). + return self == null ? TypePtr.PTR : TypeMemPtr.make(self); + } + + // A function type is `{ type... -> type }` or `{ type }`. + private Type typeFunPtr() { + int old = pos(); // Record lexer position + match("{"); // Skip already-peeked '{' + Type t0 = type(); // Either return or first arg + if( t0==null ) return posT(old); // Not a function + Ary<Type> ts = new Ary<>(Type.class); + // Always reserve a receiver slot. Outside constructor scopes it is + // broad and unused, so call sites can uniformly pass their self. + ts.push(implicitSelfType()); + if( match("}") ) // No-arg function { -> type } + return TypeFunPtr.make(match("?"),ts.asAry(),t0); + ts.push(t0); // First argument + while( true ) { + if( match("->") ) { // End of arguments, parse return + Type ret = type(); + if( ret==null || !match("}") ) + return posT(old); // Not a function + return TypeFunPtr.make(match("?"),ts.asAry(),ret); + } + Type t1 = type(); + if( t1==null ) return posT(old); // Not a function + ts.push(t1); + } + } + + // True if a TypeFunPtr, without advancing parser + private boolean isTypeFun() { + int old = pos(); + if( typeFunPtr()==null ) return false; + pos(old); + return true; + } + + /** + * Parse an expression of the form: + * + * <pre> + * expr : bitwise [? expr [: expr]] + * </pre> + * @return an expression {@link Node}, never {@code null} + */ + private Node parseExpression() { + Node expr = parseLogical(); + return match("?") ? parseTrinary(expr,":") : expr; + } + + /** + * Parse an bitwise expression + * + * <pre> + * bitwise : compareExpr (('&' | '|' | '^') compareExpr)* + * </pre> + * @return a bitwise expression {@link Node}, never {@code null} + */ + + private Node parseLogical() { + Node lhs = parseBitwise(); + while (true) { + if( false ) ; + else if( match("&&") ) lhs = parseTrinary(lhs, "&&"); + else if( match("||") ) lhs = parseTrinary(lhs, "||"); + else break; + } + return lhs; + } + + private Node parseBitwise() { + Node lhs = parseEquality(); + while( true ) { + if( false ) ; + else if( matchOp('&') ) lhs = new AndNode(loc(),lhs,null); + else if( matchOp('|') ) lhs = new OrNode(loc(),lhs,null); + else if( match ("^") ) lhs = new XorNode(loc(),lhs,null); + else break; + lhs.setDef(2,parseEquality()); + lhs = peep(lhs); + } + return lhs; + } + + + + /** + * Parse an eq/ne expression + */ + private Node parseEquality() { + Node lhs = parseComparison(); + while( true ) { + boolean eq = false; + if( match("==") ) eq = true; + else if( !match("!=") ) break; + lhs.keep(); + Node rhs = parseComparison(); + lhs = peep(new BoolNode.EQ(lhs.unkeep(),rhs)); + if( !eq ) lhs = peep(new NotNode(lhs)); + } + return lhs; + } + + /** + * Parse an expression of the form: + * + * <pre> + * expr : shiftExpr < shiftExpr <= shiftExpr... + * expr : shiftExpr > shiftExpr >= shiftExpr... + * </pre> + * @return an comparator expression {@link Node}, never {@code null} + */ + private Node parseComparison() { + Node lhs = parseShift(); + int dir = parseCompDir(); + // No compare + if( dir==0 ) return lhs; + + // Compare + Node rhs = parseShift().keep(); + Node cmp = makeCompBool(dir,lhs,rhs); // Convert to a bool + + // Stacked compares? + int dir0 = parseCompDir(); + if( dir0 == 0 ) { + rhs.unkeep(); + if( rhs != cmp ) rhs.isKill(); + return cmp; + } + + // rhs is keeped() and becomes lhs + // cmp is NOT keeped() and is the last test + Node ifNode = new IfNode(ctrl(), cmp).peephole(); + Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole(); + Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole(); + // False side does nothing but capture memory & side-effects + ScopeNode fail = _scope.dup(); + fail.ctrl(ifF); + + // Loop over stacked compares. Each loop moves the old RHS to become + // the new LHS, so e.g. (a < b < c < d) compares (LHS:a < RHS:b) then + // compares (LHS:b < RHS:c) then (LHS:c < RHS:d), etc. + while( dir0 != 0 ) { + if( Math.abs(dir) != Math.abs(dir0) ) + throw error("Mixing relational directions in a chained relational test"); + // True side parses next arm of test + ctrl(ifT); + lhs = rhs; // lhs is keeped() and is old RHS + rhs = parseShift().keep(); + cmp = makeCompBool(dir,lhs.unkeep(),rhs); // Convert to a bool + ifNode = new IfNode(ctrl(), cmp).peephole(); + ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole(); + ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole(); + // Merge result into the fail case + ctrl(ifF); + fail.mergeScopes(_scope.dup(), loc()).peephole(); + // Check next stacked compare + dir0 = parseCompDir(); + } + rhs.unkill(); + ctrl(ifT); + RegionNode r = fail.mergeScopes(_scope, loc()).init(); + _scope = fail; + return new PhiNode("", r,_code.ZERO,con(1)).peephole(); + } + + private int parseCompDir() { + if( match("<=") ) return -1; // Negative for "equals-or-less" + if( match(">=") ) return -2; // 1 for <, 2 for > + if( match("<" ) ) return 1; // Positive for just "less" + if( match(">" ) ) return 2; + return 0; + } + + private Node makeCompBool( int dir0, Node lhs, Node rhs ) { + // Convert to a bool + if( Math.abs(dir0)==2 ) // Swap direction + { Node tmp = lhs; lhs = rhs; rhs = tmp; } + lhs = peep(dir0 < 0 ? new BoolNode.LE(lhs,rhs) : new BoolNode.LT(lhs,rhs)); + return peep(lhs); + } + + /** + * Parse an additive expression + * + * <pre> + * shiftExpr : additiveExpr (('<<' | '>>' | '>>>') additiveExpr)* + * </pre> + * @return a shift expression {@link Node}, never {@code null} + */ + private Node parseShift() { + Node lhs = parseAddition(); + while( true ) { + if( false ) ; + else if( match("<<") ) lhs = new ShlNode(loc(),lhs,null); + else if( match(">>>")) lhs = new ShrNode(loc(),lhs,null); + else if( match(">>") ) lhs = new SarNode(loc(),lhs,null); + else break; + lhs.setDef(2,parseAddition()); + ParseException err; + if( (err=lhs.err()) != null ) + throw err; + lhs = peep(lhs); + } + return lhs; + } + + /** + * Parse an additive expression + * + * <pre> + * additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)* + * </pre> + * @return an add expression {@link Node}, never {@code null} + */ + private Node parseAddition() { + Node lhs = parseMultiplication(); + while( true ) { + if( false ) ; + else if( match("+") ) lhs = new AddNode(lhs,null); + else if( match("-") ) lhs = new SubNode(lhs,null); + else break; + lhs.setDef(2,parseMultiplication()); + lhs = peep(lhs); + } + return lhs; + } + + /** + * Parse an multiplicativeExpr expression + * + * <pre> + * multiplicativeExpr : unaryExpr (('*' | '/') unaryExpr)* + * </pre> + * @return a multiply expression {@link Node}, never {@code null} + */ + private Node parseMultiplication() { + var lhs = parseUnary(); + while( true ) { + if( false ) ; + else if( match("*") ) lhs = new MulNode(lhs,null); + else if( match("/") ) lhs = new DivNode(lhs,null); + else break; + lhs.setDef(2,parseUnary()); + lhs = peep(lhs); + } + return lhs; + } + + /** + * Parse a unary minus expression. + * + * <pre> + * unaryExpr : ('-') unaryExpr | '!') unaryExpr | postfixExpr | primaryExpr | '--' Id | '++' Id + * </pre> + * @return a unary expression {@link Node}, never {@code null} + */ + private Node parseUnary() { + // Pre-dec/pre-inc + int old = pos(); + if( match("--") || match("++") ) { + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + String name = _lexer.matchId(); + if( name!=null ) { + Var n = _scope.lookup(name); + if( n != null && !(n.type() instanceof TypeMemPtr) ) { + if( n._final ) + throw error("Cannot reassign final '"+n._name+"'"); + Node expr = zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); + _scope.update(n,expr); + return expr; + } + } + // Reset, try again + pos(old); + } + if (match("-")) return peep(new MinusNode(parseUnary())); + if (match("!")) return peep(new NotNode(parseUnary())); + // Else be a primary expression + return parsePrimary(); + } + + /** + * Parse a primary expression: + * + * <pre> + * primaryExpr : integerLiteral | "string" | 'char' | true | false | null | + * new Type | '(' expression ')' | Id['++','--'] | + * type['.' Id]* + * </pre> + * @return a primary {@link Node}, never {@code null} + */ + private Node parsePrimary() { + if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); + if( _lexer.peek('"') ) return newString(parseString()); + if( matchx("true" ) ) return con(1); + if( matchx("false") ) return _code.ZERO; + if( matchx("null" ) ) return con(Type.NIL); + if( match ("'" ) ) return parseChar(); + if( match ("(" ) ) return parsePostfix(require(parseAsgn(), ")")); + if( matchx("new" ) ) return parsePostfix(alloc()); + if( match ("{" ) ) return parsePostfix(require(func(),"}")); + + int pos = pos(); + // Expect an identifier now + String id = _lexer.matchId(); + if( id == null || KEYWORDS.contains(id) ) + throw errorSyntax("an identifier or expression"); + int pos2 = pos(); + // Attempt a local var lookup first + Var var = _scope.lookup(id); + if( var==null ) { + // Not a local var, try again as a type name + pos(pos); + Type t = type(); + if( t!=null ) { + // Static <clinit> typename lookup + if( peek('.') ) // type.fld, which is really a field lookup in the class object + return parsePostfix(con(t)); + // Direct reference to a <clinit> type. Convert the instance + // type to a class type name + if( !(t instanceof TypeMemPtr tmp) ) { + //return new FRefNode(addClzPrefix(tmp._obj._name), loc()).peephole(); + // Not a local var, but yes a bare type... disallow a local + // FREF and return null, not-a-primary parse. + pos(pos); + return null; + } + } + + // Insert FREF outside enclosing Kind.Func scope. + pos(pos2); + t = FRefNode.FREF_TYPE; + // Check if this is a class. + CompUnit cu = ParseAll.findCompUnit(_code,_ref,id); + if( cu!=null ) { + // Must be a final class-type + String clzName = addClzPrefix(cu._cname); + TypeStruct clz = (TypeStruct)TYPES.get(clzName); + if( clz == null ) + TYPES.put(clzName,clz = TypeStruct.make(clzName,true)); + t = TypeMemPtr.make((byte)2,clz,true); + } + // Define the FRef + var = _scope.defineFRef(id,t,cu!=null,loc()); + } + + // Load local value + Node rvalue = _scope.in(var); + if( var._fref ) + return parsePostfix(rvalue); + // Required constructor fields begin as their declared pointer type + // widened to include null. Inside the explicit constructor, any read + // before all paths replace that value observes a partially initialized + // object. Ordinary methods run only after construction is complete; + // their enclosing declaration scope still carries the same Var marker. + if( inExplicitConstructor() && var._uninit && rvalue._type instanceof TypeNil tn && tn.nullable() ) + throw error("Field '"+id+"' might not be initialized",var._loc); + + // Check for an instance field load + int kx = _scope.kindx(var); // Declaration scope + int fx = _scope.enclosingFunction(); // Enclosing function scope + Kind kk = _scope._kinds.at(kx); + Kind fk = _scope._kinds.at(fx); + // Access instance field from 'self' + if( kx+1 == fx && kk instanceof Kind.Func inst && FunNode.isInstance(inst._name) && fk instanceof Kind.Func method ) + return parsePostfixName(_scope.in(method._lexSize),id); + + // Check for a function-escaping variable; these require true + // closures. Final constants are OK; final vars require a hidden var + // argument; not-final fields can just use an explicit struct arg. + if( kx < fx && // Out of scope + !(var._final && rvalue._type.isConstant()) ) { // Not a final constant + if( kx > 0 ) // Global scope is OK, only ever one of these (not one per function invoke) + throw error("Variable '"+var._name+"' is out of function scope and must be a final constant"); + + // var.idx==2 is self argument, 3 is args, 4 or more are now static globals + if( var._idx >= 4 ) { + // Read from global scope. + // Surely a nicer way to get the top-level scope compile name? + Var clzptr = _scope.var( 2 ); + // Get the most recent sharpen global + TypeStruct clz = (TypeStruct) TYPES.get( ((TypeMemPtr) clzptr.type())._obj._name ); + TypeMemPtr clztmp = TypeMemPtr.make( (byte) 2, clz, true ); + return parsePostfixName( con( clztmp ), id ); + //return parsePostfixName(_scope.in(2),id); + } + } + + // Check for assign-update, x += e0; + char ch = _lexer.matchOperAssign(); + if( ch==0 ) { // Normal primary, check for postfix updates + int selfx = _scope.kindFcn(var)._lexSize; + // Might be a method call from inside a method, so no explicit "self". + // Pass the in-scope "self" + return parsePostfixMethod(rvalue,_scope.in(selfx)); + } + // Assign-update direct into Scope + Node op = opAssign(ch,rvalue, var.type() ); + _scope.update(var,op); + return postfix(ch) ? rvalue.unkeep() : op; + } + + // Check for assign-update, "x += e0". + // Returns "x + e0"; the caller assigns value. + // if ch is postfix, then lhs is kept and caller will unkeep and return it. + private Node opAssign( char ch, Node lhs, Type t ) { + // RHS of the update. + lhs.keep(); // Alive across parseAsgn + Node rhs = + (byte)ch == 1 ? con( 1) : // var++ + (byte)ch == -1 ? con(-1) : // var-- + parseAsgn(); // var op= rhs + if( !postfix(ch) ) lhs.unkeep(); // Allow to die in next peep + // 4 cases: + // int + int ==>> narrow int + // int + flt ==>> error, caller must fail assigning flt into int + // flt + int ==>> use float op, wrap toFloat() + // flt + flt ==>> use float op + Node op = switch(ch) { + case 1, (char)-1, + '+' -> new AddNode(lhs,rhs); + case '-' -> new SubNode(lhs,rhs); + case '*' -> new MulNode(lhs,rhs); + case '/' -> new DivNode(lhs,rhs); + default -> throw TODO(); + }; + // Convert to float ops, or narrow int types; error if not declared type. + // Also, if postfix LHS is still keep() + return convertExpr(peep(op),t.glb(false)); + } + + + /** + Parse an allocation + */ + private Node alloc() { + Type t = type(); + if( t==null ) { + String tname = _lexer.matchId(); + if( tname==null || KEYWORDS.contains(tname) ) + throw error("Expected a type"); + t = forwardRefType(tname); + } + // Parse ary[ length_expr ] + if( match("[") ) { + if( !t.makeZero().isa(t) ) + throw error("Cannot allocate a non-nullable, since arrays are always zero/null filled"); + Node len = parseAsgn().keep(); + require("]"); + return allocArray(typeAry(t,false),len); + } else { + if( !(t instanceof TypeMemPtr tmp) ) + throw error("Cannot allocate a "+t.str()); + return allocStruct(tmp._obj); + } + } + + private Node allocStruct(TypeStruct ts) { + Ary<Node> ctorArgs = constructorArgs(); // CNC TODO- BAD THREADING SELF-MEM + Node size = off(ts, " len"); + + // Build a NewNode; takes in ctrl and size. + // Produces a ptr and a private mem. + NewNode nnn = new NewNode(ts, ctrl(), size ).init(); + ProjNode self = new ProjNode(nnn,0,ts.str()).init().keep(); + ProjNode smem = new ProjNode(nnn,1,"#selfMem").init(); + + // Find a "class:XXX" struct, with field "XXX" function ptr as the <init> + TypeStruct clz = (TypeStruct)TYPES.get(addClzPrefix(ts._name)); + if( clz==null ) + throw error("Unknown struct type '" + ts._name + "'"); + TypeFunPtr init = (TypeFunPtr)clz.field(initFieldName(ts._name))._t; + + // Call construct <init>($ctrl,$mem,NewNode.self,NewNode.#selfMem) and + // encourage inlining + Node initSelf = self._type.isa(init.arg(0)) ? self : peep(new CheckCastNode(init.arg(0),ctrl(),self)); + Ary<Node> args = new Ary<>(Node.class){{add(ctrl()); add(mem()); add(initSelf); add(smem); add(ConstantNode.seed(init)); }}; + Node selfMem = functionCall( args ); + // The returned value is a merge of private *Memory* and NOT some Scalar + + // Optional user constructor; same as the default constructor; private + // memory going in and out. + Field ctor = clz.field("<ctor>"); + if( ctor != null ) + selfMem = constructorCall((TypeFunPtr)ctor._t,self,selfMem,ctorArgs); + else if( !ctorArgs.isEmpty() ) + throw error("Constructor arguments for '" + ts._name + "' but no constructor is defined"); + if( match("{") ) + throw error("Inline constructor blocks are no longer supported; define a constructor in '" + ts._name + "'"); + + // Might be TOP if parsing in dead/unreachable code + if( selfMem._type == Type.TOP ) { + _code.add(selfMem); + return self.unkeep(); + } + + // Escape all new aliases. EscapeNode inputs are the self pointer, the + // merged private memory, then all the named public aliases. The + // output is all the newly merged public aliases - but not actually + // bulk memory. + selfMem.keep(); + for( Field fld : ts._fields ) { + Field fld2 = fld._final ? fld : (Field)fld.glb(true); + Node prior = mem(); + Node esc = peep(new EscapeNode(fld2,self,selfMem,prior)); + mem(mergeAlias(prior,fld._alias,esc)); + } + + if( selfMem.unkeep().isUnused() ) selfMem.kill(); + else _code.add(selfMem); + return self.unkeep(); + } + + private Ary<Node> constructorArgs() { + Ary<Node> args = new Ary<>(Node.class); + if( !match("(") ) + return args; + while( !peek(')') ) { + Node arg = parseAsgn(); + if( arg==null ) break; + args.push(arg.keep()); + if( !match(",") ) break; + } + require(")"); + return args; + } + + // Constructors are called with two extra arguments: self and selfMem. + private Node constructorCall(TypeFunPtr ctor, Node self, Node selfMem, Ary<Node> ctorArgs) { + Ary<Node> args = new Ary<>(Node.class); + Node ctorSelf = self._type.isa(ctor.arg(0)) + ? self + : peep(new CheckCastNode(ctor.arg(0),ctrl(),self)); + args.add(ctrl()); + args.add(mem()); + args.add(ctorSelf); + args.add(selfMem); + for( Node arg : ctorArgs ) + args.add(arg.unkeep()); + args.add(ConstantNode.seed(ctor)); + + CallNode call = (CallNode)new CallNode(loc(), args.asAry()).peephole(); + CallEndNode cend = (CallEndNode)new CallEndNode(call,TypeRPC.constant(_code.rpc(_ref._cname))).peephole(); + call.peephole(); + cend = (CallEndNode)cend.keep().peephole(); + ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole()); + mem (new ProjNode(cend,1,ScopeNode.MEM0).peephole()); + return new ProjNode(cend.unkeep(),2,"#selfMem").peephole(); + } + + private Node allocArray(TypeStruct ts, Node len) { + Field lenFld = ts.field("#" ); + Field bodyFld = ts.field("[]"); + + ConFldOffNode base = off(ts, "[]"); + int scale = ts.aryScale(); + Node size = peep(new AddNode(base,peep(new ShlNode(null,len,con(scale))))); + + // Build a NewNode; takes in and puts out all aliases. + NewNode nnn = new NewNode(ts, ctrl(), size ).init(); + ProjNode self = new ProjNode(nnn,0,ts.str()).init().keep(); + ProjNode smem = new ProjNode(nnn,1,"#selfMem").init().keep(); + + // Store length. Rest of array is zero'd via CALLOC during CodeGen. + // Length is casted to sanity. + // TODO: Needs runtime check. + Node st = peep(new StoreNode(null, "#", 1, TypeInteger.U32, null, smem, self, off(ts,"#"), len.unkeep(), true)); + + // TODO: Allow add-on "constructor" to init the array elements + + Node prior = mem(); + Node esc = peep(new EscapeNode(lenFld,self,st,prior)); + mem(mergeAlias(prior,lenFld._alias,esc)); + prior = mem(); + esc = peep(new EscapeNode(bodyFld,self,smem,prior)); + mem(mergeAlias(prior,bodyFld._alias,esc)); + smem.unkeep(); + return self.unkeep(); + } + + private Node newString(String s) { + TypeStruct ts = typeAry(TypeInteger.U8,true); + int lenAlias = ts.field("#" )._alias; + int elemAlias = ts.field("[]")._alias; + TypeConAryB body = TypeConAryB.make(s); + TypeInteger slen = TypeInteger.constant(s.length()); + // Make a TMP, not-null (byte)2, singleton (true) (requires text + // strings are hash-interned), with a TypeStruct having a constant + // array body. + TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[]u8", slen, lenAlias, body, elemAlias, true),true); + assert str.isConstant(); + return con(str); + } + + /** + * Parse postfix expression; this can be a field expression, an array + * lookup or a postfix operator like '#' + * + * <pre> + * expr ('.' FIELD)* [ = expr ] // Field reference + * expr '#' // Postfix unary read operator + * expr ['++' | '--' ] // Postfix unary write operator + * expr ('[' expr ']')* = [ = expr ] // Array reference + * expr '(' [args,]* ')' // Function call + * </pre> + */ + private Node parsePostfixMethod(Node expr, Node self) { + // TODO: This is too broad because the parser lacks strong types here. + // Some normal functions can end up with a "self" argument. + + // Self method call? Self is a TMP-not-CLZ, then assume and pass self. + if( match("(") ) { + expr = parsePostfix(require(functionCall(expr,self),")")); + } else + self.isKill(); // Not a method call, no need for self + return parsePostfix(expr); + } + private Node parsePostfix(Node expr) { + String name; + if( match(".") ) name = requireId(); + else if( match("#") ) name = "#"; + else if( match("[") ) name = "[]"; + // Can get here without an explicit receiver: "f()" or + // "self.method()(next)(next)(next)". Pass the current function self. + else if( match("(") ) return parsePostfix(require(functionCall(expr,defaultSelf()),")")); + else return expr; // No postfix + + return parsePostfixName(expr,name); + } + + private Node defaultSelf() { + return _scope.in(_scope._kinds.at(_scope.enclosingFuncOrDecl())._lexSize); + } + + /** + * Parse postfix expression; this can be a field expression, an array + * lookup or a postfix operator like '#' + * + * <pre> + * expr ['++' | '--' ] // Postfix unary write operator + * expr [ [op]= expr ] // Field reference + * expr ('[' expr ']')* [ [op]= expr ] // Array reference + * </pre> + */ + + private Node parsePostfixName(Node expr, String name) { + // Keep expr across possible updates + expr.keep(); + + // Get the best known TypeStruct from a pointer type. + TypeStruct ts = null, ts2; + if( expr._type instanceof TypeMemPtr tmp && (ts2=((TypeStruct)TYPES.get(tmp._obj._name))) != null ) + ts = (TypeStruct)tmp._obj.join(ts2); + // A Load's flow type is intentionally not promised during parsing, + // but its declaring field type is still known. Recover that static + // type for chained accesses such as `_grow(1).buf[len++]`; otherwise + // the array Store is parsed as alias #1 and can hide the independent + // side effect of the postfix `len++`. + if( ts==null && expr instanceof LoadNode load ) { + TypeStruct ots = load.off() instanceof ConFldOffNode coff ? coff._ts : null; + if( ots==null && load.ptr()._type instanceof TypeMemPtr owner ) { + Type owner0 = TYPES.get(owner._obj._name); + ots = owner0 instanceof TypeStruct obj ? obj : owner._obj; + } + if( ots != null ) { + Field loaded = ots.field(load._name); + if( loaded != null && loaded._t instanceof TypeMemPtr ltmp ) { + Type loaded0 = TYPES.get(ltmp._obj._name); + ts = loaded0 instanceof TypeStruct obj ? obj : ltmp._obj; + } + } + } + + Field fld = ts==null ? null : ts.field(name); + if( fld==null && ts!=null ) + fld = fieldOrOpen(ts,name,false); + // With no field, we will update the bulk memory + int alias = fld==null ? 1 : fld._alias; + Type decl = fld==null ? Type.BOTTOM : fld._t; + + // Field offset; fixed for structs, computed for arrays + Node off = (name=="[]" + // Array element math, with unknown base offset and shift amount. + ? peep(new AddNode(fldoff(expr,"[]"),peep(new ShlNode(null,require(parseAsgn(),"]"),fldoff(expr,"<<"))))) + // Named field offset on an unknown struct type + : fldoff(expr,name)).keep(); + + // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment + if( matchOpx('=','=') ) { + // Field assignment + Node val = parseAsgn().keep(); + // Lift value for store + Node lift = fld==null ? val : new ConvertNode(fld._t,val).peephole(); + // Memory for store, post assignment expression + Node mem = mem(); + // Store to field + mem.keep(); + Node st = new StoreNode(loc(), name, alias, decl, ctrl(), mem, expr.unkeep(), off.unkeep(), lift, false).peephole().keep(); + storeMem(st,alias,mem); + st.unkeep(); + mem.unkeep(); + + return val.unkeep(); // "obj.a = expr" returns the expression while updating memory + } + + // Memory for load + Node mem = mem(); + // Loading from a constant array, the declared type is the + // meet-over-elements and not the array itself. + if( name=="[]" && decl instanceof TypeConAry conary ) + decl = conary.elem(); + + // Load field + Node load = peep(new LoadNode(loc(),name, alias, ctrl(), mem, expr, off)); + + // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" + char ch = _lexer.matchOperAssign(); + if( ch!=0 ) { + if( fld!=null && decl==Type.BOTTOM ) + throw error( "'" + ts._name + "' is not fully initialized, field '" + fld._fname + "' needs to be set in a constructor" ); + Node op = opAssign(ch,load, decl ); + mem.keep(); + Node st = new StoreNode(loc(), name, alias, decl.glb(true), ctrl(), mem, expr.unkeep(), off.unkeep(), op, false).peephole().keep(); + storeMem(st,alias,mem); + st.unkeep(); + mem.unkeep(); + + load = postfix(ch) ? load.unkeep() : op; + // And use the original loaded value as the result + return load; + } + off.unkill(); + // Might be a method call, so pass expr as 'self' + return parsePostfixMethod(load,expr.unkeep()); + } + + + // zero/sign extend. "i" is limited to either classic unsigned (min==0) or + // classic signed (min=minus-power-of-2); max=power-of-2-minus-1. + private Node zsMask(Node val, Type t ) { + if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) { + if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) ) + return val; + // Float rounding + return peep(new RoundF32Node(val)); + } + if( t0._min==0 ) // Unsigned + return peep(new AndNode(null,val,con(t0._max))); + // Signed extension + int shift = Long.numberOfLeadingZeros(t0._max)-1; + Node shf = con(shift); + if( shf._type==TypeInteger.ZERO ) + return val; + return peep(new SarNode(null,peep(new ShlNode(null,val,shf.keep())),shf.unkeep())); + } + + /** + * Parse a function body; the caller will parse the surrounding "{}" + * + * <pre> + * { [type arg,]* -> expr } + * { expr } // The no-argument function + * </pre> + */ + private Node func() { + Ary<Type> ts = new Ary<>(Type.class); + Ary<String> ids = new Ary<>(String.class); + Type self = implicitSelfType(); + ts.push(self); + ids.push(self == TypePtr.PTR ? "#self" : "self"); + + // Parse other arguments + _lexer.skipWhiteSpace(); + Lexer loc = loc(); // First argument location + while( true ) { + Type t = type(); // Arg type + if( t==null ) break; + String id = requireId(); + ts .push(t ); // Push type/arg pairs + ids.push(id); + match(","); + } + require("->"); + // Make a concrete function type, with a fidx + TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),Type.BOTTOM,_code.fidx(_ref._cname)); + ReturnNode ret = parseFunctionBody(tfp,loc,ids.asAry()); + return new FunPtrNode(tfp,_code._start,ret).peephole(); + } + + private Node constructorFunc(String name) { + require("{"); + Ary<Type> ts = new Ary<>(Type.class); + Ary<String> ids = new Ary<>(String.class); + TypeStruct self = _scope.constructorSelf(); + assert self != null; + ts.push(TypeMemPtr.make(constructorRecv(self))); + ids.push("self"); + // Explicit constructors use the same hidden allocation arguments as + // <init>: public memory is the call memory edge (index 1), followed by + // self (index 2) and the private, unescaped object memory (index 3). + ts.push(TypeMem.makePrivate(self)); + ids.push("#selfMem"); + + _lexer.skipWhiteSpace(); + Lexer loc = loc(); + while( true ) { + Type t = type(); + if( t==null ) break; + String id = requireId(); + ts .push(t ); + ids.push(id); + match(","); + } + require("->"); + TypeFunPtr tfp = TypeFunPtr.make1((byte)2,true,ts.asAry(),TypeMem.makePrivate(self),_code.fidx(_ref._cname)); + Node[] preParse = _scope.save(); + ReturnNode ret = _parseFunctionBody((self._name+"."+name).intern(),tfp,loc,ids.asAry()); + _scope.restore(preParse); + require("}"); + return new FunPtrNode(tfp,_code._start,ret).peephole(); + } + + /** + * Parse function call arguments; caller will parse the surrounding "()" + * <pre> + * ( arg* ) + * </pre> + */ + private Node functionCall(Node fcn, Node self) { + if( fcn._type == Type.NIL ) + throw error("Calling a null function pointer"); + fcn.keep(); // Keep while parsing args + + Ary<Node> args = new Ary<>( Node.class ); + args.push(null); // Space for ctrl,mem + args.push(null); + if( self != null ) // Method call has a self + args.push(self.keep()); + while( !peek(')') ) { + Node arg = parseAsgn(); + if( arg==null ) break; + args.push(arg.keep()); + if( !match(",") ) break; + } + // Control & memory after parsing args + args.set(0,ctrl().keep()); + args.set(1,mem().keep()); + args.push(fcn); // Function pointer + // Unkeep them all + for( Node arg : args ) + arg.unkeep(); + return functionCall(args); + } + + private Node functionCall( Ary<Node> args ) { + // Dead into the call? Skip all the node gen + if( ctrl()._type == Type.XCONTROL ) { + for( Node arg : args ) + if( arg.isUnused() ) + arg.kill(); + return con(Type.TOP); + } + + // Into the call + CallNode call = (CallNode)new CallNode(loc(), args.asAry()).peephole(); + // Post-call setup + CallEndNode cend = (CallEndNode)new CallEndNode(call,TypeRPC.constant(_code.rpc(_ref._cname))).peephole(); + call.peephole(); // Rerun peeps after CallEnd, allows early inlining + cend = (CallEndNode)cend.keep().peephole(); // TODO: Might inline + // Control from CallEnd + ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole()); + // Memory from CallEnd + Node mem = new ProjNode(cend,1,ScopeNode.MEM0).peephole(); + mem(mem); + // Call result + return new ProjNode(cend.unkeep(),2,"#2").peephole(); + } + + // Just after parsing "type foo = " can parse `"C"` + private boolean isExternDecl( ) { + int old = pos(); + String s = parseString(); + if( "C".equals(s) ) return true; + pos(old); + return false; + } + // External linked constant + private ConstantNode externDecl( String ex, Type t ) { + if( t instanceof TypeFunPtr tfp ) { // Generic TFP from type parse + int fidx = _code.fidx(_ref._cname); // Get a FIDX + tfp = tfp.makeFrom(fidx); // Become a constant + _code.externFunc(fidx,ex); // Map fidx to extern name + t = tfp; + } + return (ExternNode)(new ExternNode(t,ex).peephole()); + } + + /** + * Parse integer literal + * + * <pre> + * integerLiteral: [1-9][0-9]* | [0] + * floatLiteral: [digits].[digits]?[e [digits]]?[f]? + * </pre> + */ + private ConstantNode parseLiteral() { return con(_lexer.parseNumber()); } + ConstantNode con( long con ) { return _code.con(con); } + ConstantNode con( Type t ) { return _code.con(t ); } + // Field offset for a known type (but unknown offset) + public static ConFldOffNode off( TypeStruct ts, String fname ) { + return (ConFldOffNode)(new ConFldOffNode(ts,fname).peephole()); + } + // Field offset for an unknown type + public Node fldoff( Node n, String name ) { + return new FldOffNode(n,name,loc()).peephole(); + } + public Node peep( Node n ) { + // Peephole, then improve with lexically scoped guards + return _scope.upcastGuard(n.peephole()); + } + + // Parse a string or null + private String parseString() { + if( !peek('"') ) return null; + _lexer.inc(); + int start = pos(); + while( !_lexer.isEOF() && _lexer.nextChar()!= '"' ) ; + if( _lexer.isEOF() ) + throw error("Unclosed string"); + return new String(_lexer._input,start,pos()-start-1); + } + + // Already parsed "'" + private Node parseChar() { + return require(con(TypeInteger.constant(_lexer.nextChar())),"'"); + } + + private boolean noLowerCase(String s) { + for( int i=0; i<s.length(); i++ ) + if( Character.isLowerCase(s.charAt(i)) ) + return false; + return true; + } + + + ////////////////////////////////// + // Utilities for lexical analysis + + // Return true and skip if "syntax" is next in the stream. + private boolean match (String syntax) { return _lexer.match (syntax); } + // Match must be "exact", not be followed by more id letters + private boolean matchx(String syntax) { return _lexer.matchx(syntax); } + private boolean matchOp(char c0 ) { return _lexer.matchOp(c0); } + private boolean matchOpx(char c0, char c1) { return _lexer.matchOpx(c0,c1); } + // Return true and do NOT skip if 'ch' is next + private boolean peek(char ch) { return _lexer.peek(ch); } + private boolean peekIsId() { return _lexer.peekIsId(); } + + // ch is +/- 1, means oper++ or oper-- means postfix + private static boolean postfix(char ch) { + return (byte)ch== 1 || (byte)ch== -1; + } + + public int pos() { return _lexer._position; } + private int pos(int pos) { + int old = _lexer._position; + _lexer._position = pos; + return old; + } + private Type posT(int pos) { _lexer._position = pos; return null; } + // Source code location + Lexer loc() { return new Lexer(_lexer); } + + + // Require and return an identifier + private String requireId() { + String id = _lexer.matchId(); + if (id != null && !KEYWORDS.contains(id) ) return id.intern(); + throw error("Expected an identifier, found '"+id+"'"); + } + + private String matchId() { + int old = pos(); + String id = _lexer.matchId(); + if( id==null ) return null; + if( !KEYWORDS.contains(id) ) return id; + pos(old); + return null; + } + + // Require an exact match + private Parser require(String syntax) { require(null, syntax); return this; } + private <N> N require(N n, String syntax) { + if (match(syntax)) return n; + throw errorSyntax(syntax); + } + + //////////////////////////////////// + // Lexer components + + // Lexer provides low level access to the raw file bytes, peeks and matches + // short strings, parses numbers, skips comments and whitespace, tracks + // line numbers, allows the parse position to be saved and restored, and + // serves as a location indicator for errors. + + public static class Lexer { + + // Input buffer; an array of text bytes read from a file or a string + private final byte[] _input; + // Tracks current position in input buffer + private int _position = 0; + + // + private int _line_number = 1; + // Start of current line + private int _line_start = 0; + + /** True when this location precedes {@code that} in the same source. */ + public boolean before(Lexer that) { + return that != null && _input == that._input && _position < that._position; + } + + /** + * Record the source text for lexing + */ + public Lexer(String source) { + this(source.getBytes()); + } + + /** + * Direct from disk file source + */ + public Lexer(byte[] buf) { + _input = buf; + } + + /** + * Copy a lexer from a lexer + */ + private Lexer(Lexer l) { + _input = l._input; + _position = l._position; + _line_number = l._line_number; + _line_start = l._line_start; + } + + // Very handy in the debugger, shows the unparsed program + @Override + public String toString() { + return new String(_input, _position, _input.length - _position); + } + + // True if at EOF + private boolean isEOF() { + return _position >= _input.length; + } + + // Peek next character, or report EOF + private char peek() { + return isEOF() ? Character.MAX_VALUE // Special value that causes parsing to terminate + : (char) _input[_position]; + } + // Just crash if misused + public byte peek(int off) { return _input[_position+off]; } + + private void inc() { + if( _position++ < _input.length && _input[_position-1] == '\n' ) { + _line_number++; + _line_start = _position; + } + } + // Does not honor LF, so caller must roll back position on a LF + private char nextChar() { + char ch = peek(); + inc(); + return ch; + } + + // True if a white space + private boolean isWhiteSpace() { + return peek() <= ' '; // Includes all the use space, tab, newline, CR + } + + /** + * Return the next non-white-space character + */ + private void skipWhiteSpace() { + while( true ) { + if( isWhiteSpace() ) inc(); + // Skip // to end of line + else if( _position+2 < _input.length && + _input[_position ] == '/' && + _input[_position+1] == '/') { + inc(); inc(); + while( !isEOF() && _input[_position] != '\n' ) + inc(); + } else if( _position+2 < _input.length && + _input[_position ] == '/' && + _input[_position+1] == '*') { + // Skip /*comment*/ + while( !isEOF() && !(_input[_position-1] == '*' && _input[_position] == '/')) + inc(); + inc(); + } else break; + } + } + + // Next non-white-space character, or EOF + public char nextXChar() { skipWhiteSpace(); return nextChar(); } + + // Return true, if we find "syntax" after skipping white space; also + // then advance the cursor past syntax. + // Return false otherwise, and do not advance the cursor. + boolean match(String syntax) { + assert syntax.indexOf('\n')==-1; // No newlines in match + skipWhiteSpace(); + int len = syntax.length(); + if (_position + len > _input.length) return false; + for (int i = 0; i < len; i++) + if ((char) _input[_position + i] != syntax.charAt(i)) + return false; + _position += len; + return true; + } + + // Match must be exact and not followed by more ID characters. + // Prevents identifier "ifxy" from matching an "if" statement. + boolean matchx(String syntax) { + if( !match(syntax) ) return false; + if( !isIdLetter(peek()) ) return true; + _position -= syntax.length(); + return false; + } + // Match this char, and the next char must be different. + // Handles '&&' vs '&' + boolean matchOp( char c0 ) { + skipWhiteSpace(); + if( _position+1 >= _input.length || _input[_position]!=c0 || _input[_position+1]==c0 ) + return false; + inc(); + return true; + } + // Match these two characters in a row + boolean matchOpx(char c0, char c1) { + skipWhiteSpace(); + if( _position+1 >= _input.length || _input[_position]!=c0 || _input[_position+1]==c1 ) + return false; + inc(); + return true; + } + + private boolean peek(char ch) { + skipWhiteSpace(); + return peek()==ch; + } + + boolean peekIsId() { + skipWhiteSpace(); + return isIdStart(peek()); + } + + // Return an identifier or null + String matchId() { + return peekIsId() ? parseId() : null; + } + + // Used for errors + String getAnyNextToken() { + if (isEOF()) return "EOF"; + if (isIdStart(peek())) return parseId(); + if (isNumber(peek())) return parseNumberString(); + if (isPunctuation(peek())) return parsePunctuation(); + return String.valueOf(peek()); + } + + + boolean isNumber(char ch) {return Character.isDigit(ch);} + + // Return a constant Type, either TypeInteger or TypeFloat + private Type parseNumber() { + int old = _position; + int len = isLongOrDouble(); + boolean f32 = peek()=='f'; + if( f32 ) nextChar(); + int n = Math.abs(len); + if( f32 ) + return TypeFloat.constant(Float.parseFloat(new String(_input,old,n))); + if( len > 0 ) { + if( len > 1 && _input[old]=='0' ) + throw error("Syntax error: integer values cannot start with '0'",this); + long i = Long.parseLong(new String(_input,old,len)); + return TypeInteger.constant(i); + } + return TypeFloat.constant(Double.parseDouble(new String(_input,old,n))); + } + private String parseNumberString() { + int old = _position; + int len = Math.abs(isLongOrDouble()); + if( peek()=='f' ) { + nextChar(); + len++; + } + return new String(_input,old,len); + } + + // Return +len that ends a long + // Return -len that ends a double + private int isLongOrDouble() { + int old = _position; + char c; + while( Character.isDigit(c=nextChar()) ) ; + if( !(c=='e' || c=='.') ) + return --_position - old; + if( peek() == '-' ) nextChar(); + while( Character.isDigit(c=nextChar()) || c=='e' || c=='.' ) ; + return -(--_position - old); + } + + // First letter of an identifier + private boolean isIdStart(char ch) { + return Character.isAlphabetic(ch) || ch == '_'; + } + + // All characters of an identifier, e.g. "_x123" + private boolean isIdLetter(char ch) { + return Character.isLetterOrDigit(ch) || ch == '_'; + } + + private String parseId() { + int start = _position; + while (isIdLetter(nextChar())) ; + return new String(_input, start, --_position - start).intern(); + } + + // + private boolean isPunctuation(char ch) { + return "=;[]<>()+-/*&|^".indexOf(ch) != -1; + } + + private String parsePunctuation() { + int start = _position; + return new String(_input, start, 1); + } + + // Next oper= character, or 0. + // As a convenience, mark "++" as a char 1 and "--" as char -1 (65535) + public char matchOperAssign() { + skipWhiteSpace(); + if( _position+2 >= _input.length ) return 0; + char ch0 = (char)_input[_position]; + if( "+-/*&|^".indexOf(ch0) == -1 ) return 0; + char ch1 = (char)_input[_position+1]; + if( ch1 == '=' ) { _position += 2; return ch0; } + if( isIdLetter((char)_input[_position+2]) ) return 0; + if( ch0 == '+' && ch1 == '+' ) { _position += 2; return (char) 1; } + if( ch0 == '-' && ch1 == '-' ) { _position += 2; return (char)-1; } + return 0; + } + } + + ParseException errorSyntax(String syntax) { return _errorSyntax("expected `"+syntax+"`"); } + private ParseException _errorSyntax(String msg) { + return error("Syntax error, "+msg+" but found `" + _lexer.getAnyNextToken()+"`"); + } + ParseException error(String msg) { return error(msg,_lexer); } + public static ParseException error(String msg, Lexer loc) { return new ParseException(msg,loc); } + + public static class ParseException extends RuntimeException { + public final Lexer _loc; + // file:line:charoff err + //String msg = "src:"+_line_number+":"+(_position-_line_start)+" "+errorMessage; + ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; } + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Var.java b/chapter25/src/main/java/com/seaofnodes/simple/Var.java new file mode 100644 index 00000000..1906ce15 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/Var.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; + + +/** + * The tracked fields are now complex enough to deserve a array-of-structs layout + */ +public class Var { + + public final String _name; // Declared name + // These fields are not final for forward reference late updates or + // promotions. + public int _idx; // index in containing scope + private Type _type; // Declared type + public boolean _final; // Final field + public boolean _fref; // Forward ref + // Parser-only definite-initialization marker. The current binding's + // nullable type carries the path-sensitive state through SSA merges. + public boolean _uninit; + public Parser.Lexer _loc; // Source location + + public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc ) { + this(idx,name,type,xfinal,loc,false); + } + public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc, boolean fref) { + _idx = idx; + _name = name; + _type = type; + _final = xfinal; + _fref = fref; + _loc = loc; + } + public Type type() { return _type; } + public Type _type() { return _type; } // ScopeNode printer needs to have no side effects + + // Forward reference variables (not types) must be BOTTOM and + // distinct from inferred variables + public boolean isFRef() { return _fref; } + + public void defFRef( Type type, boolean xfinal, Parser.Lexer loc ) { + assert isFRef() && xfinal; + _type = type; + _final = true; + _fref = false; + _loc = loc; + } + + @Override public String toString() { + return (_fref ? "FREF " : "")+(_uninit ? "UNINIT " : "")+_type.toString()+(_final ? " ": " !")+_name; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java new file mode 100644 index 00000000..911e69f6 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java @@ -0,0 +1,96 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeMem; + +abstract public class BuildLRG { + // Compute live ranges in a single forwards pass. Every def is a new live + // range except live ranges are joined at Phi. Also, gather the + // intersection of register masks in each live range. Example: "free + // always zero" register forces register zero, and calling conventions + // typically force registers. + + // Sets FAILED to the set of hard-conflicts (means no need for an IFG, + // since it will not color, just split the conflicted ranges now). Returns + // true if no hard-conflicts, although we still might not color. + public static boolean run(int round, RegAlloc alloc) { + FunNode fun = null; + for( Node bb : alloc._code._cfg ) { + if( bb instanceof FunNode fun2 ) + fun = fun2; + for( Node n : bb.outs() ) { + if( n instanceof PhiNode phi && !(phi._type instanceof TypeMem) ) { + if( phi instanceof MachNode mach && mach.outregmap()==null ) + continue; + // All Phi inputs end up with the same LRG. + // Pass 1: find any pre-existing LRG, to avoid make-then-Union a LRG + LRG lrg = alloc.lrg(phi); + if( lrg == null ) + for( int i=1; i<phi.nIns(); i++ ) + if( (lrg = alloc.lrg(phi.in(i))) != null ) + break; + // If none, make one. + if( lrg==null ) lrg = alloc.newLRG(n,fun); + if( phi instanceof MachNode ) defLRG(alloc,n,fun); + // Pass 2: everybody uses the same LRG + lrg=alloc.union(lrg,phi); + for( int i=phi instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ ) + lrg=alloc.union(lrg,n.in(i)); + if( lrg._mask!=null && lrg._mask.isEmpty() ) + alloc.fail(lrg); + + } else if( n instanceof MachNode mach ) { + // Attempt to commute ops to keep live ranges compatible. + if( mach.commutes() && n.nOuts()==1 ) { + int uidx = n.out(0)._inputs.find(n); + RegMask mask1 = n.in (1) instanceof MachNode machx ? machx.outregmap() : alloc.lrg(n.in(1))._mask; + RegMask mask2 = n.in (2) instanceof MachNode machx ? machx.outregmap() : alloc.lrg(n.in(2))._mask; + RegMask masko = n.out(0) instanceof MachNode machx ? machx.regmap(uidx): mach.outregmap(); + if( !mask1.overlap(masko) && mask2.overlap(masko) ) + n.swap12(); + } + + // Define live range + defLRG(alloc,n,fun); + + // Now, look in the opposite direction. How are incoming + // LRGs affected by this node: For all uses, make live lrgs + for( int i=1; i<n.nIns(); i++ ) + if( n.in(i)!=null ) { + LRG lrg2 = alloc.lrg(n.in(i)); + if( lrg2 != null ) { // Anti-dep or other, no LRG + RegMask use_mask = mach.regmap(i); // use_mask is also null for anti-dep + if( use_mask!=null && !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) ) + alloc.fail(lrg2); // Empty register mask, must split + } + } + + } + + // MultiNodes have projections which set registers + if( n instanceof MultiNode && !(n instanceof CFGNode) ) + for( Node proj : n.outs() ) + if( proj instanceof MachNode ) + defLRG(alloc,proj,fun); + } + } + + // Collect live ranges + alloc.unify(); + + return alloc.success(); + } + + private static void defLRG( RegAlloc alloc, Node n, FunNode fun ) { + MachNode mach = (MachNode)n; + RegMask def_mask = mach.outregmap(); + if( def_mask == null ) return; + LRG lrg = mach.twoAddress() == 0 + ? alloc.newLRG(n,fun) // Define a new LRG for N + : alloc.lrg2(n,mach.twoAddress()); // Use the matching 2-adr input + // Record mask and mach + if( !lrg.machDef(mach,def_mask.size1()).and(def_mask) ) + alloc.fail(lrg); // Empty register mask, must split + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java new file mode 100644 index 00000000..689ceceb --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java @@ -0,0 +1,105 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.IdentityHashMap; + +// Coalesce copies +abstract public class Coalesce { + + public static boolean coalesce( int round, RegAlloc alloc ) { + + // Walk all the splits, looking for coalesce chances + boolean progress = false; + for( CFGNode bb : alloc._code._cfg ) { + outer: + for( int j=0; j<bb.nOuts(); j++ ) { + Node n = bb.out(j); + if( !(n instanceof SplitNode) ) continue; + LRG v1 = alloc.lrg(n); + LRG v2 = alloc.lrg(n.in(1)); + if( v1 != v2 ) { + LRG ov1 = v1, ov2 = v2; + // Get the smaller neighbor count in v1 + if( v1.nadj() > v2.nadj() ) + { v1 = v2; v2 = alloc.lrg(n); } + int v2len = v2._adj==null ? 0 : v2._adj._len; + // See that they do not conflict (coalescing would make a self-conflict) + if( v1._adj!=null && v2._adj!=null && v1._mask.overlap(v2._mask) ) { + // Walk the smaller neighbors, and add to the larger. + // Check for direct conflicts along the way. + for( LRG v3 : v1._adj ) { + if( v3==v2 ) { + // Direct conflict, skip this LRG pair + v2._adj.setLen(v2len); + continue outer; + } + // Add missing LRGs from V1 to V2 + if( v2._adj.find(v3)== -1 ) + v2._adj.push(v3); + } + } + // Most constrained mask + RegMask mask = v1._mask==v2._mask ? v1._mask : v1._mask.copy().and(v2._mask); + // Check for capacity + if( v2.nadj() >= mask.size() ) { + // Fails capacity, will not be trivial colorable + if( v2._adj!=null ) v2._adj.setLen(v2len); + continue; + } + + // Union lrgs. + progress = true; + + // Since `n` is dying, remove from the LRGS BEFORE doing + // the union; this will let the union code keep the + // matching bits from the other side, preserving some + // info for biased coloring + if( ov1. _machDef==n ) ov1. _machDef = null; + if( ov1._splitDef==n ) ov1._splitDef = null; + if( ov2. _machUse==n ) ov2. _machUse = null; + if( ov2._splitUse==n ) ov2._splitUse = null; + // Keep larger adjacency list. + Ary<LRG> larger = v2._adj; + v2._adj = null; + + // Union lrgs. Swaps again, based on small lrg + LRG vWin = v1.union(v2); + vWin._adj = larger; + LRG vLose = v1.leader() ? v2 : v1; + + // LRGs below v2len were always in v2, but maybe not in v1. + // LRGs above v2len are in v1 but were not in v2. + + // Walk v2's neighbors, replacing vLose with vWin. + if( larger != null ) + for( LRG vn : larger ) { + int idx = vn._adj.find(vLose); + if( idx != -1 ) + vn._adj.del(idx); + if( vn._adj.find(vWin) == -1 ) + vn._adj.add(vWin); + } + } else { + + // Since `n` is dying, remove from the LRGS + if( v1. _machDef==n ) v1. _machDef=null; + if( v1. _machUse==n ) v1. _machUse=null; + if( v1._splitDef==n ) v1._splitDef=null; + if( v1._splitUse==n ) v1._splitUse=null; + } + + // Remove junk split + n.removeSplit(); + j--; + } + } + + if( progress ) + alloc.unify(); + + return true; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java new file mode 100644 index 00000000..85407ddc --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -0,0 +1,1050 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.IterPeeps; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.*; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + + +// Compile Driver for a Compilation Unit +@SuppressWarnings("unchecked") +public class CodeGen { + + // Last created CodeGen as a global; used all over to avoid passing about a "context". + public static CodeGen CODE; + + // Location of built-in CPU ports + public static final String PORTS = "com.seaofnodes.simple.node.cpus"; + + // Compile phases + public enum Phase { + Parse, // Parse ASCII text into Sea-of-Nodes IR + Iter, // Pessimistic peepholes after parse + Opto, // Run ideal optimizations + TypeCheck, // Last check for bad programs + LoopTree, // Build a loop tree; break infinite loops + Serialize, // Serialize public IR for future compiles to link + Unlink, // Unlink call sites before machine code generation + Select, // Convert to target hardware nodes + Schedule, // Global schedule (code motion) nodes + LocalSched, // Local schedule + RegAlloc, // Register allocation + Encoding, // Encoding + Export, // Export + LastPhase // After last phase + } + public Phase _phase; + + // --------------------------- + // Module Source Root + public final String _modDir; + // Module Build Root + public final String _buildDir; + // Search path from CWD for `.o` files containing external symbols and types. + // This can contain archives, zips and other .o file containers. + public final Ary<String> _externPaths; + + // Dotted path from module root to file containing the source, sans ".smp". + // e.g. module root: "sys", nested source name "sys.io". + public final String _srcName; + + // Only available for tests, otherwise source comes from the CompUnit + private final String _src; + + // Current Working Directory; default module base + private final String _cwd; + + // Compilation Units in this compile; one per source/object file + public HashMap<String,CompUnit> _compunits; + // Test shortcut for only one compilation unit + public CompUnit compunit() { + assert _compunits.size()==1; + for( CompUnit cu : _compunits.values() ) + return cu; + throw Utils.TODO(); + } + + // True when fidx is published through a public field of a public class. + // TypeStruct.fidxs() already excludes private (underscore) fields. + public boolean publicFIDX(int fidx) { + // A public class initializer is itself an external root; unlike + // constructors and methods, it is not reached through a class field. + FunNode fun = _linker.atX(fidx); + if( fun != null && fun.isClz() && fun.isPublic() ) + return true; + // Constructors and methods live on their class objects, including + // nested classes which do not have their own CompUnit. + for( Type t : Parser.TYPES.values() ) + if( t instanceof TypeStruct clz && + Parser.startsClzPrefix(clz._name) && + Parser.isPublicClassName(clz._name) && + resolvedClass(clz) && + publicClassFIDX(clz,fidx) ) + return true; + return false; + } + + private static boolean publicClassFIDX(TypeStruct clz, int fidx) { + boolean hasCtor = clz.field("<ctor>") != null; + for( Field fld : clz._fields ) + if( fld._fname.charAt(0)!='_' && fld._t instanceof TypeFunPtr tfp && + // With a user constructor, class:N.N is the private <init>. + !(hasCtor && clz.hiddenInit(fld)) && + XInt.bit(tfp._fidxs,fidx) ) + return true; + return false; + } + + public static boolean hasUserConstructor(TypeStruct self) { + Type t = Parser.TYPES.get(Parser.addClzPrefix(self._name)); + return t instanceof TypeStruct clz && clz.field("<ctor>") != null; + } + + // A forward reference gets a class object and constructor signature so + // parsing can proceed, but it has no callable definition yet. + private static boolean resolvedClass(TypeStruct clz) { + Type t = Parser.TYPES.get(clz._name.substring(Parser.CLZ.length())); + return !(t instanceof TypeStruct ts) || !ts._fref; + } + + private int[] _publicFIDXs, _publicAliases; + + /** + * Freeze the externally visible portion of the program after ParseAll has + * loaded every compilation unit and closed/upgraded all types. Later + * optimization can remove linker entries and create inline-only fidxs; + * neither operation changes this interface. + */ + void freezePublicInterface() { + assert _publicFIDXs==null && _publicAliases==null; + int[] fidxs = XInt.EMPTY; + for( int fidx=1; fidx<_linker._len; fidx++ ) + if( publicFIDX(fidx) ) + fidxs = XInt.make(fidxs,fidx); + int[] aliases = XInt.EMPTY; + for( Type t : Parser.TYPES.values() ) + if( t instanceof TypeStruct clz && + Parser.startsClzPrefix(clz._name) && + Parser.isPublicClassName(clz._name) && + resolvedClass(clz) ) { + boolean hasCtor = clz.field("<ctor>") != null; + for( Field fld : clz._fields ) + if( fld._fname.charAt(0)!='_' && fld._t!=Type.TOP && + !(hasCtor && clz.hiddenInit(fld)) ) + aliases = XInt.make(aliases,fld._alias); + } + _publicFIDXs = fidxs; + _publicAliases = aliases; + } + + public int[] publicFIDXs() { assert _publicFIDXs !=null; return _publicFIDXs; } + public int[] publicAliases(){ assert _publicAliases!=null; return _publicAliases; } + public boolean publicInterfaceFrozen() { return _publicFIDXs != null; } + + // --------------------------- + + // Very common nodes, cached here + public final ConstantNode ZERO; + public final XCtrlNode XCTRL; + + public Node constant( Type t ) { + assert _phase==null || _phase.ordinal() <= Phase.Opto.ordinal(); + return ConstantNode.make(t).peephole(); + } + public ConstantNode con( Type t ) { return (ConstantNode)constant(t); } + public ConstantNode con( long con ) { return con==0 ? ZERO : con(TypeInteger.constant(con)); } + + + // --------------------------- + // Test setup; no module nor file with specific argument + public CodeGen( String src ) { this(src, 126L ); } + + // Test setup; no module nor file; can alter seed & argument; can re-run same CodeGen + public CodeGen( String src, Type arg ) { + this(null,null,null,null, src, 126L, arg); + } + // Test setup; no module nor file; can alter seed & argument; can re-run same CodeGen + public CodeGen( String src, long workListSeed ) { + this(null,null,null,null, src, workListSeed, TypeInteger.BOT); + } + + // Generic CodeGen, including full module setup + public CodeGen( String modDir, String buildDir, Ary<String> externPaths, + String srcName, String src, long workListSeed, Type arg ) { + // Public singleton to avoid passing about this state to a huge count + // of places. Probably becomes a TLS at some point. + CODE = this; + Type.reset(); // Reset to recover from failed test + + _cwd = System.getProperty("user.dir")+"/"; + _modDir = modDir == null ? _cwd : modDir; + _buildDir = buildDir == null ? _cwd : buildDir; + _externPaths = externPaths; + _srcName = srcName; + _callingConv = null; // Calling convention + // Source code from test strings, not files + _src = src; + // All the compilation units + _compunits = new HashMap<>(); + _phase = null; + // Start GVN table + _gvn = new HashMap<>(); + _iter = new IterPeeps(workListSeed); + // End points of graph + _stop = new StopNode().init(); + _start = new StartNode(null,_stop,arg).init(); + ZERO = con(TypeInteger.ZERO).keep(); + XCTRL = new XCtrlNode().peephole().keep(); + P = new Parser(this); + } + + + // Run requested phases. + + // No code emission, just IR generation + public CodeGen driver( Phase phase ) { return driver(phase,null,null,false,false,0); } + // No object file writing, but code generation for a specific cpu/os pair (allows emulation) + public CodeGen driver( Phase phase, String cpu, String callingConv ) { + if( phase.ordinal() < Phase.Export.ordinal() ) + // No code outputted + return driver(phase,cpu,callingConv,false,false,0); + if( _srcName == null ) + throw new RuntimeException("No source filename provided, so do not know how to name the obj file"); + boolean emitEntrySymbol = !_srcName.contains("."); + return driver( phase, cpu, callingConv, false, emitEntrySymbol, 0 ); + } + // Write an object file for a specific cpu/os pair + + public CodeGen driver( String cpu, String callingConv, boolean inMemory, boolean emitEntrySymbol ) { return driver(Phase.Export,cpu,callingConv,inMemory,emitEntrySymbol,0); } + // Generic driver + public CodeGen driver( Phase phase, String cpu, String callingConv, boolean inMemory, boolean emitEntrySymbol, int dump ) { + int p1 = phase.ordinal(), p2 = _phase==null ? -1 : _phase.ordinal(); + if( p2 < p1 && p2 < Phase.Parse .ordinal() ) { parse(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.Iter .ordinal() ) { iter(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.Opto .ordinal() ) { opto(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.TypeCheck .ordinal() ) { typeCheck(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.LoopTree .ordinal() ) { loopTree(); p2 = dump(dump); } + if( p2 < p1 && p1 >= Phase.Encoding .ordinal() ) { unlinkImports(); p2 = dump(dump); } + if( p2 < p1 && p1 >= Phase.Encoding .ordinal() ) { serialize(); p2 = dump(dump); } // Include ideal graph in object file + if( p2 < p1 && p2 < Phase.Unlink .ordinal() ) { unlink(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.Select .ordinal() && cpu != null ) { instSelect(cpu,callingConv); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.Schedule .ordinal() ) { GCM(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.LocalSched.ordinal() ) { localSched();p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.RegAlloc .ordinal() ) { regAlloc(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.Encoding .ordinal() ) { encode(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.Export .ordinal() ) { exportELF(inMemory,emitEntrySymbol); p2 = dump(dump); } + return this; + } + + public String entryClinitName() { + return Parser.addClzPrefix(_srcName==null ? "Test" : _srcName)+".<clinit>"; + } + + // Verbose printing during compilation + private int dump(int dump) { + int p2 = _phase.ordinal(); + if( (dump & (1<<p2)) == 0 ) return p2; + if( (dump & (1<<29)) != 0 ) { + String fn = ""+p2+"-"+_phase+".dot"; + try { + Files.writeString(Path.of(fn), + new GraphVisualizer().generateDotOutput(compunit(), null, null)); + } catch(IOException e) { throw Utils.TODO("Cannot write DOT file"); } + return p2; + } + + if( (dump & (1<<30)) != 0 ) + System.err.println("After "+_phase+":"); + System.err.println(IRPrinter.prettyPrint(this)); + return p2; + } + + + // Record times by phase + public final long[] _times = new long[Phase.LastPhase.ordinal()]; + + // --------------------------- + /** + * A counter, for unique node id generation. Starting with value 1, to + * avoid bugs confusing node ID 0 with uninitialized values. + */ + public int _uid = 1; + public int UID() { return _uid; } + public int getUID() { + return _uid++; + } + + + // These next fields all 2-way map some *global* program feature to a + // local dense index, suitable for packing in BitSets. The mapping takes a + // global value - always a {source file/class} name, and an order number + // counting up per feature in the same file. The dense local index can be + // different in different compilations. + + // The global info allows loading the same remote class info from different + // paths and aligning them. Example: Compiling A loads pre-compiled B and + // C, both of which load pre-compiled D. The 2 different D loads unify via + // this global info. + + // These mappings are all trivial identities when compiling one file; they + // only become complex when loading separately compiled code. + + // Compute local function index (FIDX) from global function info. This is + // called *in order* during parsing, and that order is part of the global + // unique mapping + public final GlobalBits _aliases = new GlobalBits(); + public int alias(String clz) { return _aliases.next(clz); } + + // Compute local function index (FIDX) from global function info. This is + // called *in order* during parsing, and that order is part of the global + // unique mapping + public final GlobalBits _fidxs = new GlobalBits(); + // Return local index for specific global file & order. Used to find fidxs for e.g. FREF class <init> fcns + public int fidx( String clz, int order ) { return _fidxs.next(clz, order); } + // New fidx in the current file + public int fidx( String clz ) { return fidx(clz, -1); } + + // Compute local RPC index from global RPC info, one per call + public final GlobalBits _rpcs = new GlobalBits(); + public int rpc( String clz ) { return _rpcs.next(clz); } + + + // idepths are cached and valid until *inserting* CFG edges (deleting is + // OK). This happens with inlining, which bumps the version to bulk + // invalidate the idepth caches. + private int _iDepthVersion = 0; + public void invalidateIDepthCaches() { _iDepthVersion++; } + public boolean validIDepth(int idepth) { + if( idepth==0 ) return false; + if( _iDepthVersion==0 ) return true; + return (idepth%100)==_iDepthVersion; + } + public int iDepthAt(int idepth) { + return 100*idepth+_iDepthVersion; + } + public int iDepthFrom(int idepth) { + assert idepth==0 || validIDepth(idepth); + return idepth+100; + } + + // Popular visit bitset, declared here, so it gets reused all over + public final BitSet _visit = new BitSet(); + public BitSet visit() { assert _visit.isEmpty(); return _visit; } + + // Start and Stop; end points of the generated IR + public StartNode _start; + public StopNode _stop; + + // Global Value Numbering. Hash over opcode and inputs; hits in this table + // are structurally equal. + public final HashMap<Node,Node> _gvn; + + // Reverse from a constant function pointer to the IR function being called. + // Error to call with a non-constant TFP + public FunNode link( TypeFunPtr tfp ) { return link(tfp.fidx()); } + // Read-only linker lookup, suitable for printers and debugger display. + // In particular, do not lazily remove a partially dead function. + public FunNode lookupFun(int fidx) { return _linker.atX(fidx); } + public FunNode lookupFun(TypeFunPtr tfp) { return lookupFun(tfp.fidx()); } + // Return the FunNode from a fidx + public FunNode link( int fidx ) { + FunNode fun =_linker.atX(fidx); + if( fun!=null && fun.isDead() ) { + _linker.setX(fidx,null); fun = null; } + return fun; + } + + // Insert linker mapping from constant function signature to the function + // being called. + public void link(FunNode fun) { + int fidx = fun.sig().fidx(); + _linker.setX(fidx,fun); + } + + // "Linker" mapping from constant TypeFunPtrs to heads of function. These + // TFPs all have exact single fidxs and their return is wiped to BOTTOM (so + // the return is not part of the match). + public final Ary<FunNode> _linker = new Ary<>(FunNode.class); + + // Extern function declarations; input is the FIDX assigned to the name. + final HashMap<Integer,String> _externFunc = new HashMap<>(); + public void externFunc(int fidx, String ex) { + assert !_externFunc.containsKey(fidx); + _externFunc.put(fidx,ex); + } + public String externFunc(int fidx) { return _externFunc.get(fidx); } + + public String funcName(int fidx ) { + FunNode fun = link(fidx); + if( fun!=null ) return fun._name; + return externFunc(fidx); + } + + public boolean owns(FunNode fun) { + return fun._compunit != null && fun._compunit._src != null; + } + + + // --------------------------- + // Parser object + public final Parser P; + + // Parse ASCII text into Sea-of-Nodes IR + public CodeGen parse() { + assert _phase == null; + _phase = Phase.Parse; + long t0 = System.currentTimeMillis(); + + Parser.TYPES.clear(); + Parser.TYPES.putAll(Parser.INIT_TYPES); + if( _src != null ) { + // No source file, just the source itself. + ParseAll.parseSource(this,_srcName==null ? "Test" : _srcName,_src); + } else { + // Path from module root to source file + ParseAll.parsePath(this,_srcName); + } + + _times[Phase.Parse.ordinal()] = System.currentTimeMillis() - t0; + JSViewer.show(); + return this; + } + + + // --------------------------- + // Iterator peepholes. + public final IterPeeps _iter; + // Stop tracking deps while assertin + public boolean _midAssert; + // Statistics on peepholes + public int _iter_cnt, _iter_nop_cnt; + // Stat gathering + public void iterCnt() { if( !_midAssert ) _iter_cnt++; } + public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; } + + // Pessimistic peepholes after parsing. This lifts loaded and parsed + // types before the optimistic Opto pass resets types and lets them fall. + public CodeGen iter() { + assert _phase == Phase.Parse; + _phase = Phase.Iter; + long t0 = System.currentTimeMillis(); + + // The phase shift closes all parser-in-progress states and freezes + // unknown external inputs. Imported nodes carry post-Opto escape + // precision, but Iter is a fresh pessimistic solve: non-singleton + // memory must restart at FULL so newly discovered private escapes can + // only be removed, never added. Seed the complete graph once. + _start.walk(n -> { + n._type = iterEscapeReset(n._type); + add(n); + return null; + }); + _iter.iterate(this); + assert Opto.fixedPointCheck(this); + + _times[Phase.Iter.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + + private static Type iterEscapeReset(Type t) { + if( t instanceof TypeMem mem ) + return mem._one ? mem : mem.makeFrom(XInt.FULL,XInt.FULL); + if( t instanceof TypeTuple tt ) { + TypeTuple rez = tt; + for( int i=0; i<tt._types.length; i++ ) { + Type tx = iterEscapeReset(tt._types[i]); + if( tx != tt._types[i] ) rez = rez.makeFrom(i,tx); + } + return rez; + } + return t; + } + + + // Run ideal optimizations + public CodeGen opto() { + if( _phase == Phase.Parse ) + iter(); + assert _phase == Phase.Iter; + _phase = Phase.Opto; + long t0 = System.currentTimeMillis(); + + Opto.opto(this); + + _times[Phase.Opto.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + public <N extends Node> N add( N n ) { return _iter.add(n); } + public void addAll( Ary<Node> ary ) { _iter.addAll(ary); } + public void addAll( Node n ) { _iter.add(n); _iter.addAll(n._inputs); } + + // --------------------------- + // Last check for bad programs + public CodeGen typeCheck() { + // Demand phase Opto for cleaning up dead control flow at least, + // required for the following GCM. + assert _phase.ordinal() <= Phase.Opto.ordinal(); + _phase = Phase.TypeCheck; + long t0 = System.currentTimeMillis(); + + final Ary<Node> errs = new Ary<>(Node.class); + _stop.walk( n -> { + if( n.err() != null ) + errs.add(n); + return null; + }); + String unresolved = unresolvedTypeName(); + _times[Phase.TypeCheck.ordinal()] = System.currentTimeMillis() - t0; + if( !errs.isEmpty() ) { + // Prefer an originating error over a secondary error caused by a + // BOTTOM input. If all errors are downstream-poisoned, retain + // the old first-error behavior. + Node min = null; + for( Node n : errs ) + if( noBottomInputs(n) ) { + min = n; + break; + } + if( min==null ) + min = errs.at(0); + for( Node n : errs ) + if( n!=min && + noBottomInputs(n) && + ((n instanceof MemOpNode nmop && + min instanceof MemOpNode mmop && + nmop._loc.before(mmop._loc)) || + (n instanceof CFGNode ncfg && + min instanceof CFGNode mincfg && + ncfg.idepth() < mincfg.idepth())) ) + min = n; + throw min.err(); + } + if( unresolved != null ) + throw Parser.error("Unknown struct type '"+unresolved+"'",null); + return this; + } + + private static boolean noBottomInputs(Node n) { + for( Node def : n._inputs ) + if( def!=null && !(def instanceof FldOffNode) && def._type==Type.BOTTOM ) + return false; + return true; + } + + // Forward-ref structs are open while parsing. They must all be resolved + // to closed structs before leaving type checking. + private String unresolvedTypeName() { + String[] unresolved = new String[1]; + _stop.walk(n -> { + TypeStruct ts = requiredStruct(n); + if( unresolved[0]==null && ts!=null && ts._fref ) + unresolved[0] = ts._name; + return null; + }); + return unresolved[0]; + } + + public static TypeStruct requiredStruct(Node n) { + if( n instanceof NewNode alloc ) return alloc._ts; + if( n instanceof ConFldOffNode off ) return off._ts; + if( n instanceof FldOffNode off && off.in(0)._type instanceof TypeMemPtr ptr ) return ptr._obj; + return null; + } + + // --------------------------- + // Build the loop tree; break never-exit loops + public CodeGen loopTree() { + assert _phase.ordinal() <= Phase.Serialize.ordinal(); + _phase = Phase.LoopTree; + long t0 = System.currentTimeMillis(); + // Build the loop tree, fix never-exit loops + _start.buildLoopTree( _linker, _stop); + // Invariant theory: No more type or code changes after Opto. The + // LoopTree stuff is used to do layouts now, but not core opts. TODO: + // at some future date we'll do loop opts (e.g. peeling, unrolling) + // that will totally impact code shape, types and core opts - but this + // will be done in tandem with Opto changes. + //_iter.iterate(this); + _times[Phase.LoopTree.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + + // --------------------------- + public BAOS _serial; + public void serialize() { + assert _phase.ordinal() <= Phase.Serialize.ordinal(); + _phase = Phase.Serialize; + long t0 = System.currentTimeMillis(); + // Does not change compiler phase; just records IR + Serialize.serialize(this); + _times[Phase.Serialize.ordinal()] = System.currentTimeMillis() - t0; + } + + // Unlink imported function bodies before serialization. Calls to them + // remain as normal calls through their function pointer, and codegen later + // turns them into external relocations. + private void unlinkImports() { + assert _phase.ordinal() <= Phase.Serialize.ordinal(); + for( FunNode fun : _linker ) { + if( fun==null || fun.isDead() || owns(fun) ) + continue; + for( int i=1; i<fun.nIns(); i++ ) + if( fun.in(i) instanceof CallNode call ) + call.unlink(fun,i--); + } + //_iter.iterate(this); + } + + // --------------------------- + // Code generation CPU target + public Machine _mach; + // Chosen calling convention (usually either Win64 or SystemV) + public String _callingConv; + public String _cCallingConv; + // Callee save registers + public RegMask _callerSave; + public RegMask _cCallerSave; + + // All returns have the following inputs: + // 0 - ctrl + // 1 - memory + // 2 - varies with returning GPR,FPR + // 3 - RPC + // 4+- Caller save registers + public RegMask[] _retMasks; + // Return Program Counter + public RegMask _rpcMask; + + // Convert to target hardware nodes + public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); } + public CodeGen instSelect( String cpu, String callingConv, String base ) { + assert _phase.ordinal() <= Phase.Unlink.ordinal(); + _phase = Phase.Select; + + _callingConv = callingConv; + + // Not timing the class load... + // Look for CPU in fixed named place: + // com.seaofnodes.simple.node.cpus."cpu"."cpu.class" + String clzFile = base+"."+cpu+"."+cpu; + try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); } + catch( Exception e ) { throw new RuntimeException(e); } + + _cCallingConv = _mach.cCallingConv(callingConv); + + // Build global copies of common register masks. + long callerSave = _mach.callerSave(); + long cCallerSave= _mach.callerSave(_cCallingConv); + long neverSave = _mach. neverSave(); + int maxReg = Math.min(64,_mach.regs().length); + assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave + assert maxReg>=64 || (-1L << maxReg & cCallerSave)==0; // No stack slots in cCallerSave + _callerSave = new RegMask(callerSave); + _cCallerSave= new RegMask(cCallerSave); + + // Build a Return RegMask array. All returns have the following inputs: + // 0 - ctrl + // 1 - memory + // 2 - varies with returning GPR,FPR + // 3 - RPC + // 4+- Caller save registers + _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4]; + for( int reg=0, i=4; reg<maxReg; reg++ ) + if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) + _retMasks[i++] = new RegMask(reg); + _rpcMask = new RegMask(_mach.rpc()); + _retMasks[3] = _rpcMask; + + // Convert to machine ops + long t0 = System.currentTimeMillis(); + _uid = 1; // All new machine nodes reset numbering + var map = new IdentityHashMap<Node,Node>(); + _instSelect( _stop, map ); + _stop = (StopNode)map.get(_stop ); + StartNode start = (StartNode)map.get(_start); + _start = start==null ? new StartNode(_start) : start; + _instOuts(_stop,visit()); + _visit.clear(); + + // Replace the CompUnit stop (and list of functions) + // with hardware-specific ones + for( CompUnit cu : _compunits.values() ) { + cu._start= (StartCUNode)map.get(cu._start); + cu._stop = (StopCUNode )map.get(cu._stop ); + } + + _times[Phase.Select.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + + // Walk all ideal nodes, recursively mapping ideal to machine nodes, then + // make a machine node for "this". + private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) { + if( n==null ) return null; + Node x = map.get(n); + if( x !=null ) return x; // Been there, done that + + // If n is a MachNode already, then its part of a multi-node expansion. + // It does not need instruction selection (already selected!) + // but it does need its inputs walked. + if( n instanceof MachNode ) { + for( int i=0; i < n.nIns(); i++ ) + n._inputs.set(i, _instSelect(n.in(i),map) ); + return n; + } + + // Produce a machine node from n; map it to flag as done so stops cycles. + map.put(n, x=_mach.instSelect(n) ); + // Carry loop-tree and pre-order info across the ideal->mach transition + if( n instanceof CFGNode ncfg && x instanceof CFGNode xcfg ) { + xcfg._ltree = ncfg._ltree; + xcfg._pre = ncfg._pre; + } + // Walk machine op and replace inputs with mapped inputs + for( int i=0; i < x.nIns(); i++ ) + x._inputs.set(i, _instSelect(x.in(i),map) ); + // Post selection action + if( x instanceof MachNode mach ) { + if( n instanceof ReturnNode ret ) + ((ReturnNode)mach)._fun = (FunNode)map.get(ret._fun); + mach.postSelect(this); // Post selection action + } + + return x; + } + + // Walk all machine Nodes, and set their output edges + private void _instOuts( Node n, BitSet visit ) { + if( visit.get(n._nid) ) return; + visit.set(n._nid); + for( Node in : n._inputs ) + if( in!=null ) { + in._outputs.push(n); + // CallNode special: outputs are partially ordered; CallEnd in slot 0 + if( in instanceof CallNode call && n instanceof CallEndNode cend && call.out(0) != cend ) + call._outputs.swap(0,call.nOuts()-1); + _instOuts(in,visit); + } + } + + // --------------------------- + public void unlink() { + assert _phase.ordinal() <= Phase.Select.ordinal(); + _phase = Phase.Unlink; + long t0 = System.currentTimeMillis(); + + // The remaining passes assume all calls are unlinked; i.e. we are throwing + // away the Call Graph here. Functions only reachable from internal calls + // need to be re-hooked to stop/start less they go dead. + for( FunNode fun : _linker ) { + // Already linked to start, not going dead + if( fun==null || fun.isDead() || fun._type.isHigh() ) + continue; + + // Imported functions are not emitted by this object. Unlink any + // concrete calls so they become normal external relocations. + if( !owns(fun) ) { + while( fun.nIns() > 1 ) { + if( fun.in(1) instanceof CallNode call ) + call.unlink(fun,1); + else + fun.removeDeadPath(1); + } + StartCUNode start = fun._compunit._start; + StopCUNode stop = fun._compunit._stop; + // Unhook the Stop->Return edge also + ReturnNode ret = fun.ret(); + int idx = stop._inputs.find(ret); + if( idx != -1 ) { + stop._inputs.del(idx); + ret.delUse(stop); + } + // + if( stop.nIns()==0 ) { + idx = _stop._inputs.find(stop); + if( idx != -1 ) + _stop.delDef(idx); + if( start.nIns() > 0 && start.in(0) != null ) + start.setDef(0,null); + if( start.nIns() > 1 && start.in(1) != null ) + start.setDef(1,null); + } + continue; + } + + // Insert the post-Opto code-generation hook. Unlike an Opto-time + // Start input, this no longer carries unknown-caller semantics. + ReturnNode ret = fun.ret(); + StartNode start = fun._compunit._start; + assert start!=null; + if( fun.in(1) != start ) { + fun.insertDef(1,start); + for( Node use : fun._outputs ) + if( use instanceof ParmNode parm ) + parm.insertDef(1,ConstantNode.raw(parm._type)); + if( fun._compunit._stop._inputs.find(ret) == -1 ) + fun._compunit._stop.addDef(ret); + } + // Unlink from Call + for( int i=2; i<fun.nIns(); i++ ) + ((CallNode)fun.in(i)).unlink(fun,i--); + if( fun.rpc()==null ) { // Ensure valid RPC + // First make sure a valid RPC; single-call into multi-fun will have each + // function having a single call site, so the RPC becomes a constant and + // folds - but since multiple targets, the Call never inlines. Recreate a + // valid RPC so codegen (and Eval2) understands the calling convention. + ParmNode rpc = new ParmNode( "$rpc", 0, ret.rpc()._type, fun ); + rpc.addDef(ret.rpc()); + ret.setDef( 3, rpc.init() ); + } + } + + _times[Phase.Unlink.ordinal()] = System.currentTimeMillis() - t0; + } + + // --------------------------- + // Control Flow Graph in Reverse Post Order. + public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class); + + // Global schedule (code motion) nodes + public CodeGen GCM() { return GCM(false); } + public CodeGen GCM( boolean show) { + assert _phase.ordinal() <= Phase.Select.ordinal(); + _phase = Phase.Schedule; + long t0 = System.currentTimeMillis(); + + GlobalCodeMotion.buildCFG(this); + _times[Phase.Schedule.ordinal()] = System.currentTimeMillis() - t0; + if( show ) + System.out.println(new GraphVisualizer().generateDotOutput(compunit(),null,null)); + return this; + } + + // --------------------------- + // Local (basic block) scheduler phase, a classic list scheduler + public CodeGen localSched() { + assert _phase == Phase.Schedule; + _phase = Phase.LocalSched; + long t0 = System.currentTimeMillis(); + ListScheduler.sched(this); + _times[Phase.LocalSched.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + + + // --------------------------- + // Register Allocation + public RegAlloc _regAlloc; + public CodeGen regAlloc() { + assert _phase == Phase.LocalSched; + _phase = Phase.RegAlloc; + long t0 = System.currentTimeMillis(); + _regAlloc = new RegAlloc(this); + _regAlloc.regAlloc(); + _times[Phase.RegAlloc.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + + // Human-readable register name + public String reg(Node n) { + if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) { + String s = _regAlloc.reg(n); + if( s!=null ) return s; + } + return "N"+ n._nid; + } + + + // --------------------------- + // Encoding + public Encoding _encoding; + public CodeGen encode() { + assert _phase == Phase.RegAlloc; + _phase = Phase.Encoding; + long t0 = System.currentTimeMillis(); + + _encoding = new Encoding(this).encode(); + + _times[Phase.Encoding.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + + // --------------------------- + // Exporting to external formats + ElfWriter _elf; + public CodeGen exportELF( boolean inMemory, boolean emitEntrySymbol ) { + assert _phase == Phase.Encoding; + _phase = Phase.Export; + long t0 = System.currentTimeMillis(); + if( _encoding!=null ) { + if( inMemory ) + new LinkMem(this).link(_encoding); // In memory patching + else + _elf = new ElfWriter(this).export(emitEntrySymbol); + } + _times[Phase.Export.ordinal()] = System.currentTimeMillis() - t0; + return this; + } + + + // --------------------------- + + // Search a external path list, each path is recursively searched for .o + // files, which are partially loaded and searched for public symbols. + + // This is a state machine which lazily searches down the set of search + // paths until the requested module is class is found. + + // Map from external Strings to either partially read Simple ElfFile or ExternNode + public final HashMap<String,Object> _externSymbols = new HashMap<>(); + + // State machine elements; the outermost element is _externPaths + private int _extPathIdx; // Search index into the extern path list + private final Ary<File> _files = new Ary<>(File.class); // Files in the current extern path being searched + private int _extFileIdx; // Index into _files + + public ElfReader findExternalSimple( String name ) { + return findExternalSymbol(name) instanceof ElfReader elf ? elf : null; + } + + public ExternNode findExternal( String name ) { + return findExternalSymbol(name) instanceof ExternNode extern ? extern : null; + } + + // Search the search-path for the name; return an ElfReader if found in a + // Simple-made ELF; return an ExternNode if from another ELF, or null if + // not found. + private Object findExternalSymbol( String name ) { + // State Machine! + + while( true ) { + // Check for an immediate hit + switch( _externSymbols.get(name) ) { + + case ExternNode extern: + // Name maps to an ExternNode + return extern; + + case ElfReader elf: + return elf; + + case null: + // Name is unknown. Advance the file-system search, pulling + // out new ELF files from the search directory list and loading + // their published symbols. + + // The last directory was mined out, so get the next search + // directory and find all ELF-like files. + if( _extFileIdx >= _files._len ) { + if( _externPaths==null || _extPathIdx >= _externPaths._len ) + return null; // A true miss in the whole search path + loadExtPath(); // Load next batch file of files + break; + } + // Load and parse an ELF header + ElfReader elf = getElf(_files.at(_extFileIdx++)); + if( elf == null ) break; // Not an ELF + // Load public symbols from the ELF + elf.loadPublicSymbols(); + if( elf._strs==null ) break; // No .simple section with strings + // Put public symbol in global table; first names shadow all others + // Map from symbol string to ELFReader; most lookups + // will miss and no need to unpack ElfReader types + _externSymbols.putIfAbsent(elf._strs[1],elf); + for( String str : elf._strs ) + if( str.startsWith(Parser.CLZ) ) + _externSymbols.putIfAbsent(str,elf); + break; + + default: throw Utils.TODO("should not reach here"); + } + } + } + + // Load all the .o files in the next external path. + private void loadExtPath() { + _files.clear(); + _extFileIdx = 0; + String extpath = _externPaths.at(_extPathIdx++); + File f = new File(extpath); + if( !f.isAbsolute() ) + f = new File(_cwd+extpath); + fillExtFiles(f); + } + + // Recursive search (TODO: gzip, archives) and gather all .o files. + private void fillExtFiles(File dir) { + if( dir.isDirectory() ) + for( File f : dir.listFiles() ) + fillExtFiles(f); + else if( dir.getName().endsWith(".o") ) + _files.add(dir); + } + + // Map from object file names to ElfReaders + public final HashMap<String,ElfReader> _elfs = new HashMap<>(); + ElfReader getElf(File f) { + ElfReader elf = _elfs.get(f.getPath()); + if( elf != null ) + return elf; + _elfs.put(f.getPath(), elf = ElfReader.load(f,null)); + return elf; + } + + // --------------------------- + public boolean _asmLittle=true; + public String asm() { return asm(new SB()).toString(); } + SB asm(SB sb) { + ASMPrinter.print(sb,this); + return sb; + } + + + // Testing shortcuts + public Node ctrl() { return compunit()._stop.ret().ctrl(); } + public Node expr() { return compunit()._stop.ret().expr(); } + public String print() { return compunit()._stop.print(); } + + // Debugging helper + @Override public String toString() { + if( _phase!=null && _phase.ordinal() > Phase.Schedule.ordinal() ) + return IRPrinter.prettyPrint( this ); + if( _stop == null ) + return "No StopNode"; + return _stop.p(4999); + } + + // Debugging helper + public Node f(int idx) { + for( CompUnit cu : _compunits.values() ) { + Node n = (cu._stop == null ? _stop : cu._stop).find(idx); + if( n != null ) return n; + } + return null; + } + + + String printCFG() { + if( _cfg==null ) return "no CFG"; + SB sb = new SB(); + for( CFGNode cfg : _cfg ) { + sb.fix(8,""+cfg._idepth); + IRPrinter.printLine( cfg, sb ); + } + return sb.toString(); + } + + public static void print_as_hex(Encoding enc) { + for (byte b : enc._bits.toByteArray()) { + System.out.print(String.format("%02X", b)); + } + System.out.println(); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java new file mode 100644 index 00000000..2681066e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java @@ -0,0 +1,138 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeStruct; +import com.seaofnodes.simple.util.*; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import static com.seaofnodes.simple.codegen.CodeGen.CODE; + +// A compilation-unit, which is an external reference and source and object +// files. These are mirrored over a file system tree (so form a tree), both +// the source tree and build/object tree. +public class CompUnit { + public final CompUnit _par; // Parent, e.g. A/B + final String _fname; // Full slashed file name starting from module root, e.g. A/B/C + final public String _cname; // Full dotted class name starting from module root, e.g. A.B.C + final String _name; // Base name, e.g. C + final ExternNode _ext; // Found in external libs; e.g. libc + final File _smp; // Simple source file, e.g. module_root/A/B/C.smp + public final File _obj; // ELF output file, e.g. build/A/B/C.o + final public String _src; // Source code, from file or test case + //public Encoding _encoding; // Encoding for output + + public TypeStruct _clz; // The one TypeStruct being published + //BAOS _serial; // Serialized IR for this ELF file + Ary<CompUnit> _deps; // CompUnits that this CompUnit depends on + + //// List of symbols exported by this compilation unit, and their Node + //// definitions. + //public HashMap<String,Node> _exported; + // Per-Compilation-Unit Start/StopNodes, keeping alive all exported Nodes. + // Null means no code loaded (yet). + public StartCUNode _start; + public StopCUNode _stop ; + + + // Discovered source file. Source is loaded if required to parse, and null + // otherwise. + CompUnit( CompUnit par, String fname, String cname, String name ) throws IOException { + _par = par; + _fname= fname; + _cname= cname; + _name = name; + _ext = null; + _smp = new File( CODE. _modDir + "/" + fname + ".smp"); + _obj = new File( CODE._buildDir + "/" + fname + ".o" ); + + boolean mustCompile = _smp.exists() && // Has source to compile + ( !_obj.exists() || // No object file + _smp.lastModified() > _obj.lastModified() || // Out-of-date with source? + checkDependentObjs() ); // Out-of-date with other objs? + _src = mustCompile ? new String(Files.readAllBytes(_smp.toPath())) : null; + } + + // Test case. No source file. Source code passed in as a String. Fake + // class name. + CompUnit( String fname, String src ) { + _par = null; + _name = fname; + _fname= fname; + _cname= fname; + _ext = null; + _smp = null; + _obj = null; + _src = src; + } + + // External CompUnit, pulled in from an external library like libc + CompUnit( ExternNode ext, String symbol ) { + _par = null; + _name = symbol; + _fname= _name; + _cname= null; + _ext = ext; + _smp = null; + _obj = null; + _src = null; + } + + // External Simple object file, or namespace parent, found on an external search path. + CompUnit( File obj, CompUnit par, String fname, String cname, String name ) { + _par = par; + _name = name; + _fname= fname; + _cname= cname; + _ext = null; + _smp = null; + _obj = obj; + _src = null; + } + + // Return true if _obj is out-of-date relative to dependents. + private boolean checkDependentObjs() { + ElfReader elf = CODE.getElf(_obj); + elf.loadPublicSymbols(); + for( String dep : elf._deps ) { + File obj = new File(CODE._buildDir+"/"+dep+".o"); + if( obj.lastModified() > _obj.lastModified() ) // Out-of-date with deps? + return true; + } + return false; + } + + // Add a discovered dependency, for later writing out in an ELF file, ".simple" section + void addDep(CompUnit dep) { + if( _deps == null ) _deps = new Ary<>(CompUnit.class); + _deps.add(dep); + } + + public void addFun(CodeGen code, FunNode fun) { + assert fun.in(1)==_start; + _stop.addDef(fun.ret()); + code.add(_stop); + _stop.addDep(fun); + } + + void setStart(CodeGen code) { + _stop = new StopCUNode().init(); + _start = new StartCUNode(code._start,_stop, Type.BOTTOM, _fname).init(); + code._stop.addDef(_stop); + } + + + @Override public String toString() { + String x = "{"+_cname; + if( _smp!=null && _smp.exists()) x += ",hasSMP"; + if( _obj!=null && _obj.exists()) x += ",hasObj"; + if( _src!=null ) x += ",hasSrc"; + if( _ext != null ) x += ",isExt"; + x += "}"; + return x; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java new file mode 100644 index 00000000..9d9b8a1c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java @@ -0,0 +1,87 @@ +package com.seaofnodes.simple.codegen; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class ElfExpr { + public static void main(String[] args) { + try (FileOutputStream fos = new FileOutputStream("hello.o")) { + ByteBuffer buffer = ByteBuffer.allocate(512); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // === ELF HEADER (64 bytes) === + buffer.put(new byte[]{ + 0x7F, 'E', 'L', 'F', // ELF Magic + 2, 1, 1, 0, // 64-bit, little-endian, version + 0, 0, 0, 0, 0, 0, 0, // ABI padding + 2, 0, // Type: Executable (ET_EXEC) + 0x3E, 0x00, // Machine: x86-64 + 1, 0, 0, 0, // ELF version + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Entry point + 0, 0, 0, 0, 0, 0, 0, 0, // Program header offset + 64, 0, 0, 0, 0, 0, 0, 0, // Section header offset + 0, 0, 0, 0, // Flags + 64, 0, // ELF header size + 0, 0, // Program header entry size + 0, 0, // Number of program header entries + 40, 0, // Section header entry size + 2, 0, // Number of section headers + 1, 0 // Section header string table index + }); + + // === .TEXT SECTION (Executable Code) === + byte[] textSection = { + // sys_write (write(1, message, 13)) + (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 + (byte) 0x48, (byte) 0xC7, (byte) 0xC7, 0x01, 0x00, 0x00, 0x00, // mov rdi, 1 + (byte) 0x48, (byte) 0xBE, (byte)0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rsi, message + (byte) 0x48, (byte) 0xC7, (byte) 0xC2, 0x0D, 0x00, 0x00, 0x00, // mov rdx, 13 + (byte) 0x0F, (byte) 0x05, // syscall + + // sys_exit (exit(0)) + (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x3C, 0x00, 0x00, 0x00, // mov rax, 60 + (byte) 0x48, (byte) 0x31, (byte) 0xFF, // xor rdi, rdi + (byte) 0x0F, (byte) 0x05 // syscall + }; + + // Append section data + buffer.position(256); + buffer.put(textSection); + + // === STRING MESSAGE (.data Section) === + byte[] message = "Hello, World!\n".getBytes(); + buffer.position(400); + buffer.put(message); + + // === SECTION HEADERS === + buffer.position(64); + + // Null Section (required as first entry) + buffer.put(new byte[40]); + + // .text Section + buffer.putInt(1); // Offset in section string table (".text") + buffer.putInt(1); // Type: SHT_PROGBITS + buffer.putLong(6); // Flags: Executable | Allocatable + buffer.putLong(0); // Virtual address + buffer.putLong(256); // Offset to section data + buffer.putLong(textSection.length); // Section size + buffer.putInt(0); // Link + buffer.putInt(0); // Info + buffer.putLong(4); // Address alignment + buffer.putLong(0); // No extra entries + + // === SECTION HEADER STRING TABLE === + buffer.position(500); + buffer.put(".text".getBytes()); + buffer.put((byte) 0x00); // Null terminator + + fos.write(buffer.array()); + System.out.println("ELF object file 'hello.o' created."); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfReader.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfReader.java new file mode 100644 index 00000000..534bb10f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfReader.java @@ -0,0 +1,234 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeMemPtr; +import com.seaofnodes.simple.type.TypeStruct; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.io.*; +import java.nio.file.Files; +import java.util.HashMap; + +public class ElfReader { + + public final File _file; + public final byte[] _buf; + int _x; // Cursor into buffer + // ELF Header + public final ElfHeader _header; + // All sections + public final Section[] _sections; + // Simple section, if available + public final Section _simple; + // Byte array in/out stream - used as a reader + public final BAOS _bais; + // Compilation unit; written into FunNodes + public final CompUnit _compunit; + + // Strings from the Simple section, either small public set or whole set + public String[] _strs; + // Dependent file names + public String[] _deps; + // Only published symbol this file + public TypeStruct _clz; + // Canonical named types, distinct from optimized same-named graph states. + public TypeStruct[] _pubTypes; + // Nodes read in this Elf + public Ary<Node> _nodes; + + // Load an Elf file (mostly lazily) + public static ElfReader load( File f, CompUnit cu ) { + try { return new ElfReader(f,cu); } + catch( IOException ioe ) { return null; } + } + + // Load the Simple section from the ELF file + private ElfReader( File f, CompUnit cu ) throws IOException { + _file = f; + _compunit = cu; + _buf = Files.readAllBytes(f.toPath()); + _header = new ElfHeader(); + // No attempt is made to parse the program header table. + _sections = new Section[_header._shnum]; + for( int i=0; i<_header._shnum; i++ ) + _sections[i] = new Section(i); + + // Load section names + Section secstr = _sections[_header._shstrndx]; + for( Section sec : _sections ) + if( sec._type != 0 ) + sec._name = secstr.str(sec._noff); + + Section simple = null; + for( Section sec : _sections ) + if( ".simple".equals(sec._name) ) + simple = sec; + _simple = simple; + _bais = new BAOS(_buf, (int)_simple._offset); + } + + // For Serialize self-test + ElfReader( BAOS bais ) { + _file = null; + _compunit = null; + _buf = bais.buf(); + _header = null; + _sections = null; + _simple = null; + _bais = bais; + } + + // Load the public symbols and dependent objs, but not the whole IR + void loadPublicSymbols() { + // Check already loaded and cached + if( _strs == null && _simple != null ) + // Read only the published public strings, for speed purposes when + // scanning a large count of ELF files but getting hits in only a + // few of them. i.e. optimizing (prematurely?) for the traditional + // case of linking a small file against a large library + Serialize.read_public_strs(this); + } + + // Loads the entire IR + public TypeStruct loadSimple(CodeGen code) { + Serialize.readAll(code, this, code._aliases, code._fidxs, code._rpcs); + return _clz; + } + + // Loads strings, mappings, and types, but not Nodes. Used after the + // public-string scan finds a matching Simple ELF, so parsing can resolve + // imported field/array aliases before the full graph is linked in later. + public TypeStruct loadPublicTypes(CodeGen code) { + Serialize.readTypes(code, this, code._aliases, code._fidxs, code._rpcs); + return _clz; + } + + int u1() { return _buf[_x++]&0xFF; } + int magic() { // Big endian + return (u1() << 24) | (u1() << 16) | (u1() << 8) | u1(); + } + int u2() { return u1() | (u1()<<8); } // Little endian + int u4() { // Little endian + return u1() | (u1() << 8) | (u1() << 16) | (u1() << 24); + } + long u8() { return u4() | ((long)u4()<<32); } + void skip(int skip) { _x += skip; } + + + public class ElfHeader { + final int _magic; + final boolean _format32; // Format32 or format64 + final byte _osabi; + final boolean _little; // True if little endian, false if big endian + final char _type; + final char _machine; + final long _entry; // Process entry point + final long _phoff; // Program header table offset, from file start + final long _shoff; // Section header table offset, from file start + final int _flags; // Flags + final int _phnum; // Number of program header entries + final int _shentsize; + final int _shnum; // Number of section header entries + final int _shstrndx; // Section header index contains section names + + + ElfHeader() throws IOException { + _x = 0; + if( (_magic = magic()) != 0x7F454C46 ) bad("magic number",_magic); + byte EI_CLASS = (byte)u1(); + if( EI_CLASS != 1 && EI_CLASS != 2 ) bad("EI_CLASS",EI_CLASS); + _format32 = (EI_CLASS==1); + byte EI_DATA = (byte)u1(); + if( EI_DATA != 1 && EI_DATA != 2 ) bad("EI_DATA",EI_DATA); + _little = EI_DATA == 1; + if( u1() != 1 ) bad("version",1); + if( (_osabi=(byte)u1()) != 0 ) bad("EI_VERSION",_osabi); + byte abiversion = (byte)u1(); + if( abiversion != 0 ) bad("EI_ABIVERSION",abiversion); + skip(7); // Pad + + // Endian matters now + if( !_little ) throw new IOException("TODO: Big endian"); + if( (_type = (char)u2()) != 1 ) bad("ET_REL",_type); + if( (_machine = (char)u2()) != 0x3E ) bad("AMD x86-64",_machine); + int ver = u4(); + if( ver != 1 ) bad("Version",ver); + // Format32 matters now + if( _format32 ) bad("Format",1); + _entry = u8(); // Process entry point + _phoff = u8(); // Program header table offset + _shoff = u8(); // Section header table offset + _flags = u4(); // Flags + int ehsize = u2(); + if( ehsize != 0x40 ) bad("EHSIZE",ehsize); + int phentsize = u2(); + if( phentsize != 0x38 && phentsize != 0 ) bad("PHENTSIZE",phentsize); + _phnum = u2(); + _shentsize = u2(); + if( _shentsize != 0x40 ) bad("SHENTSIZE",_shentsize); + _shnum = u2(); + // String section + _shstrndx = u2(); + assert _x == ehsize; + } + } + + public class Section { + final int _noff; // Offset in string section for name + final int _type; // Section type + final long _flags; + final long _addr; // Offset in virtual mem, if loaded + final long _offset; // Offset in file + final long _size; // Size in bytes + final int _link; // Associated section index, varies + final int _info; // Varies + final byte _align; // Log2 alignment + final int _esize; // Element size, if non-zero + String _name; + + Section(int sidx) throws IOException { + _x = (int)(_header._shoff + (sidx * _header._shentsize)); + _noff = u4(); + _type = u4(); + _flags = u8(); + _addr = u8(); + _offset= u8(); + _size = u8(); + _link = u4(); + _info = u4(); + long align = u8(); + if( align==0 ) align=1; + _align = (byte)Long.numberOfTrailingZeros(align); + if( (1L << _align ) != align ) + throw new IOException("Bad section alignment: "+align); + _esize = (int)u8(); + } + + String str(int off) { + assert _type == 3/*SHT_STRTAB*/; // String table section header + int start = (int)(_offset+off); + int x = start; + while( _buf[x++] !=0 ) ; + return new String(_buf,start,x-start-1); + } + } + + private static void bad(String msg, int val) throws IOException { + throw new IOException("Bad "+msg+": "+val); + } + + + + //public TypeMemPtr lookup( String name ) { + // if( _simple == null ) return null; + // BAOS baos = new BAOS(_buf, (int)_simple._offset); + // Ary<String> strs = Serialize.read_strs(baos); + // + // + // + // Ary<Node> nodes = Serialize.read_types(baos); + // + // throw Utils.TODO("Elf load symbol "+name); + //} +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java new file mode 100644 index 00000000..508cdcca --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java @@ -0,0 +1,387 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeStruct; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.BAOS; +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.HashMap; + +public class ElfWriter { + + final CodeGen _code; + Ary<Section> _sections; + StringSection _strtab; + + ElfWriter( CodeGen code ) { + _code = code; + } + + // ------------------------------------------------------------------------ + abstract public class Section { + static final int SHT_NULL = 0; + static final int SHT_PROGBITS= 1; + static final int SHT_SYMTAB = 2; + static final int SHT_STRTAB = 3; + static final int SHT_RELA = 4; + static final int SHT_SIMPLE = 10; // Simple section + + final String _name; + final int _type, _entsize, _index, _name_pos, _flags; + int _link, _info; + int _file_offset; + + Section(String name, int type, int entsize, int flags) { + _name = name; + _type = type; + _entsize = entsize; + _flags = flags; + _index = _sections._len; + _sections.push(this); + _name_pos = name.isEmpty() + ? 0 // Null section + : _strtab == null + ? 1 // StringSection + // All the other sections + : _strtab.writeCString(name); + } + + void writeHeader(ByteBuffer out) { + out.putInt(_name_pos); // name + out.putInt(_type); // type + out.putLong(_flags); // flags + out.putLong(0); // addr + out.putLong(_file_offset); // offset + out.putLong(size()); // size + out.putInt(_link); // link + out.putInt(_info); // info + out.putLong(16); // addralign + out.putLong(_entsize); // Fixed-size element size + } + + abstract void write(ByteBuffer out); + abstract int size(); + } + + // ------------------------------------------------------------------------ + // Generic data sections; carries about a BAOS for bits + public class DataSection extends Section { + static final int SHF_WRITE = 1; + static final int SHF_ALLOC = 2; + static final int SHF_EXECINSTR = 4; + static final int SHF_INFO_LINK = 64; + + final BAOS _contents; + + DataSection( String name, int type, int flags, int entsize ) { this(name, type, flags, entsize, new BAOS() ); } + DataSection( String name, int type, BAOS contents, int flags ) { this(name, type, flags, 0, contents ); } + private DataSection( String name, int type, int flags, int entsize, BAOS contents ) { + super(name, type, entsize, flags); + _contents = contents; + } + + @Override void write(ByteBuffer out) { out.put(_contents.toByteArray()); } + @Override int size() { return _contents.size(); } + public void write2( int op ) { + _contents.write(op ); + _contents.write(op>> 8); + } + public void write4( int op ) { + _contents.write(op ); + _contents.write(op>> 8); + _contents.write(op>>16); + _contents.write(op>>24); + } + public void write8( long i64 ) { + write4( (int) i64 ); + write4( (int)(i64>>32)); + } + + } + + // ------------------------------------------------------------------------ + // Section to collect and de-dup strings + public class StringSection extends DataSection { + final HashMap<String,Integer> _dedup = new HashMap<>(); + + StringSection() { + super(".strtab",SHT_STRTAB, 0, 0 ); + int emptyoff = writeCString(""); // Always empty string at offset 0 + assert emptyoff == 0; + int namepos = writeCString(".strtab"); + assert namepos == _name_pos; + } + + public int writeCString(String name) { + if( name==null ) return 0; // Anonymous functions have no name + Integer ii = _dedup.get(name); + if( ii != null ) return ii; + // place name in the string table + int pos = _contents.size(); + _dedup.put(name,pos); + byte[] name_bytes = name.getBytes(); + _contents.write(name_bytes); + // the strings are null terminated + _contents.write(0); + return pos; + } + } + + // ------------------------------------------------------------------------ + // The relocations; 3 words per relocation + public class ReloSection extends DataSection { + ReloSection( int info ) { + super(".rela.text", SHT_RELA, SHF_INFO_LINK, 24/*entsize*/); + _link = 2; + _info = info; + } + + void writeReloOffPlus(int offset, int symidx, byte relo) { + assert symidx > 0; + write8(offset); + write8(((long)symidx << 32L) | relo); + write8(-4); + } + } + + + // ------------------------------------------------------------------------ + // Symbols section; emit the symbols into the data section directly + public class SymbolSection extends DataSection { + public static final int SYMBOL_SIZE = 4+1+1+2+8+8; + final BAOS _globals; + final int _nlocals; + + SymbolSection(int nlocals) { + super(".symtab", SHT_SYMTAB, 0/*flags*/, SYMBOL_SIZE); + _globals = new BAOS(); + _nlocals = nlocals; + } + + // Global symbol index + int gidx() { return _nlocals+(_globals.size()/SYMBOL_SIZE); } + + public int symbol( String name, int shndx, int bind, int type, long value, long size ) { + int gidx = gidx(); // Global index before I write to _globals + BAOS baos = bind==SYM_BIND_GLOBAL ? _globals : _contents; + write4(baos,_strtab.writeCString(name)); + int info = (bind << 4) | (type & 0xF); + baos.write(info); + baos.write(0); // other/visibility + write2(baos,shndx); // section index + write8(baos,value); + write8(baos,size); + return bind==SYM_BIND_GLOBAL ? gidx : -99; + } + + public int symbol( String name, int shndx, int bind, int type ) { + return symbol(name,shndx,bind,type,0,0); + } + + // creates function and stores where it starts. + // Filters to just this class.obj file. + void encodeFunctions(StopNode stop, Encoding enc, int text_idx) { + for( Node stopcu : stop._inputs ) { + for( Node ret : stopcu._inputs ) { + FunNode fun = ((ReturnNode)ret).fun(); + if( !_code.owns(fun) ) + continue; + int end = enc.opStart(fun.ret()) + enc.opLen(fun.ret()); + long value = enc.opStart(fun); + long size = end - value; + if( fun._name != null ) // Anonymous functions have no name + symbol(fun._name, text_idx, SYM_BIND_GLOBAL, SYM_TYPE_FUNC, value, size); + } + } + } + + // Insert a default-main for top-level compilations, if the top-level + // does not declare a main. + void symbolEntryClinit(int text_idx, Encoding enc) { + FunNode clinit = null; + String entryName = _code.entryClinitName(); + for( FunNode fun : _code._linker ) + if( fun != null && _code.owns(fun) ) { + if( "main".equals(fun._name) ) + return; // Top-level declared a main, take the existing one + if( entryName.equals(fun._name) ) + clinit = fun; // Keep top-level entry, if we don't find an explicit main + } + // No top-level main, so the top-level clinit becomes a default main + if( clinit != null ) { + assert enc.opStart(clinit) == 0; + symbol("main",text_idx, SYM_BIND_GLOBAL, SYM_TYPE_FUNC, enc.opStart(clinit), 0); + return; + } + throw new IllegalStateException("Missing entry <clinit> "+entryName); + } + + static void write2( BAOS bits, int op ) { + bits.write(op ); + bits.write(op>> 8); + } + static void write4( BAOS bits, int op ) { + bits.write(op ); + bits.write(op>> 8); + bits.write(op>>16); + bits.write(op>>24); + } + static void write8( BAOS bits, long i64 ) { + write4(bits, (int) i64 ); + write4(bits, (int)(i64>>32)); + } + + @Override + void writeHeader(ByteBuffer out) { + // points to string table section + _link = _strtab._index; + _info = _nlocals; + super.writeHeader(out); + } + + @Override void write(ByteBuffer out) { + out.put(_contents.toByteArray()); + out.put(_globals .toByteArray()); + } + @Override int size() { + return _contents.size() + _globals.size(); + } + } + + public static final int SYM_BIND_LOCAL = 0; + public static final int SYM_BIND_GLOBAL = 1; + + public static final int SYM_TYPE_NOTYPE = 0; + public static final int SYM_TYPE_OBJECT = 1; + public static final int SYM_TYPE_FUNC = 2; + public static final int SYM_TYPE_SECTION = 3; + + public static final int SECTION_HDR_SIZE = 64; + + // ------------------------------------------------------------------------ + public class SimpleSection extends DataSection { + SimpleSection(BAOS serial) { super(".simple", SHT_SIMPLE, serial, 0); } + } + + + // ------------------------------------------------------------------------ + + public ElfWriter export(boolean emitEntrySymbol) { + // Sections are created in the order they are emitted. + _sections = new Ary<>(Section.class); + + // Null section + Section nullSection = new DataSection("",Section.SHT_NULL,0,0); + + // String section next; written out early and available to record/dedup + // strings for all remaining sections + _strtab = new StringSection(); + + // place all the symbols + int nlocals = 1/*null*/+7/*number of sections*/+1/*Simple section*/; + SymbolSection symbols = new SymbolSection(nlocals); + + // we've already constructed these entire sections in the encoding phase + Encoding enc = _code._encoding; + DataSection text = new DataSection(".text" , Section.SHT_PROGBITS, enc._bits , DataSection.SHF_ALLOC | DataSection.SHF_WRITE | DataSection.SHF_EXECINSTR ); + // Constant pool + DataSection rodata = new DataSection(".rodata", Section.SHT_PROGBITS, enc._cpool, DataSection.SHF_ALLOC ); + // Static/global pool + DataSection rwdata = new DataSection(".data" , Section.SHT_PROGBITS, enc._sdata, DataSection.SHF_ALLOC | DataSection.SHF_WRITE ); + + + // populate function symbols + symbols.encodeFunctions(_code._stop, _code._encoding, text._index); + // Export the source compilation unit <clinit> as the C runtime "main". + // Encoding keeps this function at text offset zero. + if( emitEntrySymbol ) + symbols.symbolEntryClinit(text._index,enc); + + // create external .text relocations + ReloSection relocations = new ReloSection(text._index); + + for( Node n : enc._externals.keySet()) { + String extern = enc._externals.get(n); + int symidx = symbols.symbol(extern, 0, SYM_BIND_GLOBAL, SYM_TYPE_NOTYPE); + int offset = enc.opStart(n) + enc.opLen(n) - 4; + relocations.writeReloOffPlus(offset, symidx, (byte)4/*PLT32*/); + } + + // Write relocations for the constant pool + for( Encoding.Relo relo : enc._bigCons.values() ) { + boolean ro = !(relo._t instanceof TypeStruct ts) || ts.isConstant(); + DataSection data = ro ? rodata : rwdata; + int symidx = symbols.symbol("CPOOL$"+symbols.gidx(), data._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC, relo._target, relo._t.size()); + relocations.writeReloOffPlus(relo._opStart+relo._off, symidx, relo._elf ); + } + + // Create a "Simple" section for Simple types and ir + SimpleSection simple = new SimpleSection(_code._serial); + + // Write the local section header symbols + assert nlocals == 1+_sections._len; + symbols.symbol("",0,0,0); // Null symbol at index 0 + for( Section s : _sections ) + symbols.symbol(s._name, s._index, SYM_BIND_LOCAL, SYM_TYPE_SECTION, 0, s.size()); + + int size = 64; // ELF header size + // size of all section data + for( Section s : _sections ) { + s._file_offset = size; + size += s.size(); + } + // headers go at the very end + int shdr_offset = size; + size += SECTION_HDR_SIZE*_sections.len(); + + ByteBuffer out = ByteBuffer.allocate(size); + out.order(ByteOrder.LITTLE_ENDIAN); + + // ELF header + out.put(new byte[]{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0 }); + out.put(new byte[8]); + out.putShort((short)1 /* ET_REL */); // type + out.putShort((short)62 /* AMD64 */); // machine + out.putInt(1); // version + out.putLong(0); // entry + out.putLong(0); // phoff + out.putLong(shdr_offset); // shoff + out.putInt(0); // flags + out.putShort((short)64); // ehsize + out.putShort((short)0); // phentsize + out.putShort((short)0); // phnum + out.putShort((short)64); // shentsize + out.putShort((short) _sections.len()); // shnum + out.putShort((short) _strtab._index); // shstrndx + + // raw section data + for( Section s : _sections ) + s.write(out); + + // Section headers + for( Section s : _sections ) + s.writeHeader(out); + + assert out.position() == size; + + String objName = _code._buildDir + "/" + _code._srcName + ".o"; + File file = new File(objName); + if( file.getParentFile()!=null ) + file.getParentFile().mkdirs(); + try { + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); + bos.write(out.array()); + bos.close(); + } catch( IOException ioe ) { + // Caller does not want to deal, but it is a failed compilation in any case + throw new RuntimeException(ioe); + } + // Reset for next ELF + _sections = null; + _strtab = null; + return this; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java new file mode 100644 index 00000000..4c7475fd --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java @@ -0,0 +1,606 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.IRPrinter; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.*; +import java.util.*; + + +/** + * Instruction encodings + * <p> + * This class holds the encoding bits, plus the relocation information needed + * to move the code to a non-zero offset, or write to an external file (ELF). + * <p> + * There are also a bunch of generic utilities for managing bits and bytes + * common in all encodings. + */ +public class Encoding { + + // Top-level program graph structure + public final CodeGen _code; + + // CFG Nodes in RPO order, per-encoding. + public Ary<CFGNode> _cfg; + + // Instruction bytes. The registers are encoded already. Relocatable data + // is in a fixed format depending on the kind of relocation. + + // - RIP-relative offsets into the same chunk are just encoded with their + // chunk relative offsets; no relocation info is required. + + // - Fixed address targets in the same encoding will be correct for a zero + // base; moving the entire encoding requires adding the new base address. + + // - RIP-relative to external chunks have a zero offset; the matching + // relocation info will be used to patch the correct value. + + public final BAOS _bits = new BAOS(); // Instructions / encodings + public final BAOS _cpool = new BAOS(); // Constant r/o pool + public final BAOS _sdata = new BAOS(); // Static r/w pool + public final HashMap<Node,Relo> _bigCons = new HashMap<>(); + + // Big Constant relocation info. + public static class Relo { + public final Node _op; + public final Type _t; // Constant type + public final byte _off; // Offset from start of opcode + public final byte _elf; // ELF relocation type, e.g. 2/PC32 + public int _target; // Where constant is finally placed + public int _opStart; // Opcode start + Relo( Node op, Type t, byte off, byte elf ) { + _op=op; _t=t; _off=off; _elf=elf; + } + } + + Encoding( CodeGen code ) { + _code = code; + } + + // Shortcut to the defining register + public short reg(Node n) { + return _code._regAlloc.regnum(n); + } + + public int read1(int idx) { + byte[] buf = _bits.buf(); + return (buf[idx]&0xFF); + } + public int read2(int idx) { + byte[] buf = _bits.buf(); + return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8; + } + public int read4(int idx) { + byte[] buf = _bits.buf(); + return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8 | (buf[idx+2]&0xFF)<<16 | (buf[idx+3]&0xFF)<<24; + } + public long read8(int idx) { + return (read4(idx) & 0xFFFFFFFFL) | read4(idx+4); + } + + // This buffer is invalid/moving until after all encodings are written + public byte[] bits() { return _bits.buf(); } + + // 4 byte little-endian write + public void patch4( int idx, int val ) { + byte[] buf = _bits.buf(); + buf[idx ] = (byte)(val ); + buf[idx+1] = (byte)(val>> 8); + buf[idx+2] = (byte)(val>>16); + buf[idx+3] = (byte)(val>>24); + } + + static void padN(int n, BAOS bits) { + while( (bits.size()+n-1 & -n) > bits.size() ) + bits.write(0); + } + + // Convenience for writing log-N + static void addN( int log, long x, BAOS bits ) { + for( int i=0; i < 1<<log; i++ ) { + bits.write((int)x); + x >>= 8; + } + } + + public Encoding add1( int op ) { addN(0,op,_bits); return this; } + public Encoding add2( int op ) { addN(1,op,_bits); return this; } + public Encoding add4( int op ) { addN(2,op,_bits); return this; } + public Encoding add8(long op ) { addN(3,op,_bits); return this; } + + private final static int SENTINEL = -1; + private final IntHashMap _opStart = new IntHashMap(SENTINEL); + private final IntHashMap _opLen = new IntHashMap(SENTINEL); + + + // Read op start for n + public int opStart(Node n) { return _opStart.get(n._nid); } + // Read op length for n + public byte opLen (Node n) { return (byte)_opLen.get(n._nid); } + + // Set op start for n + public void opStart(Node n, int off) { + assert off != SENTINEL; + _opStart.put(n._nid,off); + } + + // Set op start for n + public void opStartAdd(Node n, int off) { + if( off==0 ) return; + int old = _opStart.get(n._nid); + assert old != SENTINEL && old+off != SENTINEL; + _opStart.put(n._nid,old+off); + } + + // Set op length for n + public void opLen(Node n, byte len) { + assert len != SENTINEL; + _opLen.put(n._nid,len); + } + + // Nodes need "relocation" patching; things done after code is placed. + // Record src and dst Nodes. + private final HashMap<Node,CFGNode> _internals = new HashMap<>(); + // Source is a Call, destination in the Fun. + public Encoding relo( CallNode call ) { + FunNode fun = _code.link(call.tfp()); + if( fun != null && !_code.owns(fun) ) + _externals.put(call,fun._name); + else + _internals.put(call,fun); + return this; + } + // Code address as a constant + public Encoding relo( FunPtrNode fptr ) { + TypeFunPtr tfp = (TypeFunPtr)fptr._type; + FunNode fun = _code.link(tfp); + if( fun != null && !_code.owns(fun) ) + _externals.put(fptr,fun._name); + else + _internals.put(fptr,fun); + return this; + } + // Local jump + public void jump( CFGNode jmp, CFGNode dst ) { + _internals.put(jmp,dst.uctrlSkipEmpty()); + } + + // External references only located by Strings a link-time + public final HashMap<Node,String> _externals = new HashMap<>(); + public Encoding external( Node n, String extern ) { + _externals.put(n,extern); + return this; + } + + // Store t as a 32/64 bit constant in the code space; generate RIP-relative + // addressing to load it. Type is stored in either the .rodata or .data. + public void largeConstant( Node relo, Type t, int off, int elf ) { + // TODO: Any-old struct in the cpool + assert t.isConstant() || (t instanceof TypeStruct ts && Parser.startsClzPrefix(ts._name)); + assert (byte)off == off; + assert (byte)elf == elf; + _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf)); + } + + // -------------------------------------------------- + Encoding encode( ) { + // Basic block layout: negate branches to keep blocks in-order; insert + // unconditional jumps. Attempt to keep backwards branches taken, + // forwards not-taken (this is the default prediction on most + // hardware). Layout is still Reverse Post Order but with more + // restrictions. + basicBlockLayout( ); + + // Write encoding bits in order into a big byte array. + // Record opcode start and length. + writeEncodings(); + + // Short-form RIP-relative support: replace long encodings with short + // encodings and compact the code, changing all the offsets. + compactShortForm(); + + // Write the constant pool + writeConstantPool(_cpool,true ); + // Write the static memory + writeConstantPool(_sdata,false); + + // Patch RIP-relative and local encodings now. + patchLocalRelocations(); + + return this; + } + + // -------------------------------------------------- + // Basic block layout: negate branches to keep blocks in-order; insert + // unconditional jumps. Attempt to keep backwards branches taken, forwards + // not-taken (this is the default prediction on most hardware). Layout is + // still Reverse Post Order but with more restrictions. + private void basicBlockLayout( ) { + IdentityHashMap<LoopNode,Ary<CFGNode>> rpos = new IdentityHashMap<>(); + _cfg = new Ary<>(CFGNode.class); + rpos.put(_code._start.loop(),_cfg); + BitSet visit = _code.visit(); + + // Do them all except the <clinit>s + Ary<FunNode> clinits = new Ary<>(FunNode.class); + FunNode entry = null; + String entryName = _code.entryClinitName(); + for( FunNode fun : _code._linker ) { + if( fun != null && !fun.isDead() && _code.owns(fun) ) { + if( fun.isClz() ) { + if( fun._name.equals(entryName) ) + entry = fun; + else + clinits.add(fun); + } else { + int x = _cfg._len; + _rpo_cfg(fun,visit,rpos); + assert _cfg.at(x) instanceof ReturnNode; + } + } + } + // Now the <clinit>s last, so when reversed they become first. The + // program entry <clinit> is last of all, so it lands at offset zero. + for( FunNode clinit : clinits ) { + int x = _cfg._len; + _rpo_cfg(clinit,visit,rpos); + assert _cfg.at(x) instanceof ReturnNode; + } + if( entry != null ) { + int x = _cfg._len; + _rpo_cfg(entry,visit,rpos); + assert _cfg.at(x) instanceof ReturnNode; + } + + + // Reverse in-place + for( int i=0; i< _cfg.size()>>1; i++ ) + _cfg.swap(i,_cfg.size()-1-i); + visit.clear(); + } + + + private void _rpo_cfg(CFGNode bb, BitSet visit, IdentityHashMap<LoopNode,Ary<CFGNode>> rpos ) { + if( bb==null || visit.get(bb._nid) ) return; + visit.set(bb._nid); + CFGNode next = bb.uctrl(); + + // Loops run an inner "rpo_cfg", then append the entire loop body in + // place. This keeps loop bodies completely contained, although if + // Some Day Later we have real profile data we ought to arrange the + // branch orderings based on frequency. + if( bb instanceof LoopNode loop ) { + Ary<CFGNode> body = new Ary<>(CFGNode.class); // Private RPO for the loop + rpos.put(loop.loop(),body); // Find it via loop tree + _rpo_cfg(next,visit,rpos); // RPO the loop + body.add(loop); // Include loop last (first in RPO) + Ary<CFGNode> outer = rpos.get(loop.cfg(1).loop()); + outer.addAll(body); // Append to original CFG + return; + } + + // IfNodes visit 2 sides, and may choose to reorder them in the RPO + if( bb instanceof IfNode iff ) { + // Pick out the T/F projections + CProjNode t = iff.cproj(0); + CProjNode f = iff.cproj(1); + // Invert the branch or not + if( shouldInvert(t,f,iff.loopDepth()) ) { + iff.negate(); + t.invert(); + f.invert(); + CProjNode tmp=f; f=t; t=tmp; // Swap t/f + } + + // Whichever side is visited first becomes last in the RPO. With + // no loops, visit the False side last (so True side first) so that + // when the False RPO visit returns, the IF is immediately next. + // When the RPO is reversed, the fall-through path will always be + // following the IF. + _rpo_cfg(t,visit,rpos); // True side first + next = f; // False side last + } + + // If the *next* BB has already been visited, and we are not already a + // jump, we may need an unconditional forwards jump here + Ary<CFGNode> rpo = rpos.get(bb.loop()); + if( next!=null && visit.get(next._nid) && !(bb instanceof IfNode) ) { + boolean needJump = next instanceof LoopNode + // Empty blocks from an IF will invert the IF and backwards + // branch to the loop head. If not empty, or not an IF + // will need a jump. + ? (bb.nOuts()>1 || !(bb.cfg0() instanceof IfNode) ) + // Forwards jump. If all the blocks, in RPO order, to our + // target are empty, we will fall in and not need a jump. + : !backwardsEmptyScan(rpo,next); + if( needJump ) { + CFGNode jmp = _code._mach.jump(); + jmp._ltree = bb._ltree; + jmp.setDefX(0,bb); + next.setDef(next._inputs.find(bb),jmp); + rpo.add(jmp); + } + } + + _rpo_cfg(next,visit,rpos); + rpo.add(bb); + } + + // Should this test be inverted? + private static boolean shouldInvert(CFGNode t, CFGNode f, int bld) { + int tld = t.loopDepth(), fld = f.loopDepth(); + // These next two are symmetric and can happen in any order; if `tld < + // bld` is true, then `fld < bld` must be false, or else both directions + // exit the loop... and the IF test would not be in the loop. + + // true to exit a loop usually (and false falls into Yet Another Loop + // Block), invert if false is taking an empty backwards branch. + if( tld < bld ) return forwardsEmptyScan(f,bld); + // false to exit a loop, normally invert to true (except empty back) + if( fld < bld ) return !forwardsEmptyScan(t,bld); + + // Not exiting the loop; staying at this depth (or going deeper loop). + + // Jump/true to an empty backwards block + if( forwardsEmptyScan(t,bld) ) return false; + if( forwardsEmptyScan(f,bld) ) return true ; + + // Fall/false into a full block, Jump/true to an empty block. + if( f.nOuts()>1 && t.nOuts()==1 ) return false; + if( t.nOuts()>1 && f.nOuts()==1 ) return true ; + // Jump to a merge point, assuming other things are jumping there as well + if( f.out(0) instanceof RegionNode ) return true ; + if( t.out(0) instanceof RegionNode ) return false; + // Everything else equal, use pre-order + return t._pre > f._pre; + } + + // Do we continue forwards from 'c' to the Loop back edge, without doing + // anything else? Then this branch can directly jump to the loop head. + private static boolean forwardsEmptyScan( CFGNode c, int bld ) { + if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false; + return c.uctrl() instanceof RegionNode cfg && + ((cfg instanceof LoopNode && cfg._ltree==c._ltree) || forwardsEmptyScan(cfg,bld)); + } + + // Is the CFG from "next" to the end empty? This means jumping to "next" + // will naturally fall into the end. + private static boolean backwardsEmptyScan(Ary<CFGNode> rpo, CFGNode next) { + for( int i=rpo._len-1; rpo.at(i)!=next; i-- ) + if( rpo.at(i).nOuts()!=1 ) + return false; + return true; + } + + // -------------------------------------------------- + // Write encoding bits in order into a big byte array. + // Record opcode start and length. + + // Current function is used by the spill-op encodings to query the stack + // frame layout and get the spill offsets. + private void writeEncodings() { + for( CFGNode bb : _cfg ) { + if( !(bb instanceof MachNode mach0) ) + opStart(bb, _bits.size()); + else if( bb instanceof FunNode ) { + opStart(bb, _bits.size()); + mach0.encoding( this ); + opLen(bb, (byte) (_bits.size() - opStart(bb))); + } + for( Node n : bb._outputs ) { + if( n instanceof MachNode mach && !(n instanceof FunNode) && + // FunPtrs are encoding in the block of their control, not + // the block of the ReturnNode input - which is also a CFG. + !(n instanceof FunPtrNode && bb instanceof ReturnNode) ) { + opStart(n, _bits.size()); + mach.encoding( this ); + opLen(n, (byte) (_bits.size() - opStart(n))); + } + } + } + } + + // -------------------------------------------------- + // Short-form RIP-relative support: replace short encodings with long + // encodings and expand the code, changing all the offsets. + private void compactShortForm() { + int len = _cfg._len; + int[] oldStarts = new int[len]; + for( int i=0; i<len; i++ ) + oldStarts[i] = opStart(_cfg.at(i)); + + // TODO: Rewrite this algo to use the small "_jmps" list of just the + // jumps instead of walking all blocks. + // TODO2: Allow short encodings on non-jumps. This algo doesn't + // really care jumps vs other rip-relative things. + + // Check the size of short jumps, adjust the block starts for a whole + // pass over all blocks. If some jumps go long, they might stretch the + // distance for other jumps, so another pass is needed. + int slide = -1; + while( slide != 0) { // While no fails + slide = 0; + for( int i=0; i<len; i++ ) { + CFGNode bb = _cfg.at(i); + opStartAdd(bb, slide); + // Slide down all other (non-CFG) ops in the block + for( Node n : bb._outputs ) + if( n instanceof MachNode && !(n instanceof CFGNode) && + // Match writeEncodings(): FunPtrs are encoded in the + // block of their control, not again at the Return. + !(n instanceof FunPtrNode && bb instanceof ReturnNode) ) + opStartAdd(n, slide); + if( bb instanceof RIPRelSize riprel ) { + CFGNode target = (bb instanceof IfNode iff ? iff.cproj(0) : (CFGNode)bb.out(0)).uctrlSkipEmpty(); + // Delta is from opStart to opStart. X86 at least counts + // the delta from the opEnd, but we don't have the end until + // we decide the size - so the encSize has to deal + int delta = opStart(target) - opStart(bb); + byte opLen = riprel.encSize(delta); + // Recorded size is smaller than the current size? + if( opLen(bb) < opLen ) { + // Start sliding the code down; record slide amount and new size + slide += opLen - opLen(bb); + opLen(bb, opLen); + } + } + } + } + + + // Go again, inserting padding on function headers. Since no + // short-jumps span function headers, the padding will not make any + // short jumps fail. + for( int i=0; i<len; i++ ) { + CFGNode bb = _cfg.at(i); + // Functions pad to align 16 + if( bb instanceof FunNode ) { + int newStart = opStart(bb)+slide; + slide += (newStart+15 & -16)-newStart; + } + opStartAdd(bb, slide); + for( Node n : bb._outputs ) + if( n instanceof MachNode && !(n instanceof CFGNode) && + !(n instanceof FunPtrNode && bb instanceof ReturnNode) ) + opStartAdd(n, slide); + } + + // Copy/slide the bits to make space for all the longer branches + int grow = opStart(_cfg.at(len-1)) - oldStarts[len-1]; + if( grow > 0 ) { // If no short-form ops, nothing to do here + int end = _bits.size(); + byte[] bits = new byte[end+grow]; + for( int i=len-1; i>=0; i-- ) { + int start = oldStarts[i]; + if( start==0 && i>1 ) continue; + int oldStart = oldStarts[i]; + int newStart = opStart(_cfg.at(i)); + System.arraycopy(_bits.buf(),oldStart,bits,newStart,end-start); + end = start; + } + _bits.set(bits,bits.length); + } + + } + + + // -------------------------------------------------- + // Patch local encodings now + void patchLocalRelocations() { + // Walk the local code-address relocations + for( Node src : _internals.keySet() ) { + int start = opStart(src); + Node dst = _internals.get(src); + // If function is entirely dead, only the function pointer remains + // and, it can only be used to test against zero or equals to + // another function pointer... i.e., there Is No Code Here. + int target = dst == null ? start : opStart(dst); + ((RIPRelSize)src).patch(this, start, opLen(src), target - start); + } + } + + // -------------------------------------------------- + // Write the constant pool into the BAOS and optionally patch locally + void writeConstantPool( BAOS bits, boolean ro ) { + padN(16,bits); + + // radix sort the big constants by alignment + Ary<Relo>[] raligns = new Ary[5]; + for( Node op : _bigCons.keySet() ) { + Relo relo = _bigCons.get(op); + // non-constant structs in the r/w data, everything else in r/o data + if( (relo._t instanceof TypeStruct ts && !ts.isConstant()) == ro ) + continue; + int align = relo._t.alignment(); + Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>(Relo.class)) : raligns[align]; + relos.add(relo); + } + + + // Types can be used more than once; collapse the dups + HashMap<Type,Integer> targets = new HashMap<>(); + + // By alignment + for( int align = raligns.length-1; align >= 0; align-- ) { + Ary<Relo> relos = raligns[align]; + if( relos == null ) continue; + for( Relo relo : relos ) { + // Map from relo to constant start and patch + Integer target = targets.get(relo._t); + if( target==null ) { + targets.put(relo._t,target = bits.size()); + // Write constant into constant pool + switch( relo._t ) { + case TypeTuple tt -> throw Utils.TODO("no tuples here, use structs instead"); + case TypeStruct ts -> addStruct(bits,ts); + // Simple primitive (e.g. larger int, float, function ptr) + default -> addN(align,relo._t,bits); + } + } + // Record target address and opcode start. + // Target is relative to the cpool/sdata start. + relo._target = target; + relo._opStart= opStart(relo._op); + } + } + + } + + // Emit a single scalar as bits + static void addN( int log, Type t, BAOS bits ) { + long x = switch( t ) { + case TypeInteger ti -> ti.value(); + case TypeFloat tf -> log==3 + ? Double.doubleToRawLongBits( tf.value()) + : Float.floatToRawIntBits((float)tf.value()); + case TypeFunPtr tfp -> 0; // These need to be relocated + case TypeMemPtr tmp -> 0; // These need to be relocated + default -> { if( t==Type.NIL ) yield 0; else throw Utils.TODO(); } + }; + addN(log,x,bits); + } + + // Structs use internal field layout + private void addStruct( BAOS bits, TypeStruct ts ) { + // Field order by offset + int[] layout = ts.layout(); + int off=0; // offset in the struct + for( int fn=0; fn<ts._fields.length; fn++ ) { + Field f = ts._fields[layout[fn]]; + int foff = ts. offset(layout[fn]); + // Pad up to field + while( off < foff ) { bits.write(0); off++; }; + // Constant array fields are special + if( f._fname=="[]" ) { // Must be a constant array + ((TypeConAry)f._t).write(bits); + off += ((TypeConAry)f._t).len(); + } else { + int log = f._t.log_size(); + addN(log,f._t.isConstant() ? f._t : f._t.makeZero(),bits); + off += 1<<log; + } + } + } + + + // A series of libc/external calls that Simple can link against in a JIT. + // Since no runtime in the JVM process, using magic numbers for the CPU + // emulators to pick up on. + public static int SENTINEL_CALLOC = -4; + public static int SENTINEL_WRITE = -8; + + void patchGlobalRelocations() { + for( Node src : _externals.keySet() ) { + int start = opStart(src); + String dst = _externals.get(src); + int target = switch( dst ) { + case "calloc" -> SENTINEL_CALLOC; + case "write" -> SENTINEL_WRITE ; + default -> throw Utils.TODO(); + }; + ((RIPRelSize)src).patch(this, start, opLen(src), target - start); + } + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java new file mode 100644 index 00000000..b0060024 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java @@ -0,0 +1,171 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.util.*; +import java.util.HashMap; +import java.util.Arrays; +import java.util.Objects; + + +// This utility 2-way maps some *global* program feature to a local dense +// index, suitable for packing in BitSets. The mapping takes a global value - +// always a {source file/class} name and an order number counting up per +// feature in the same file. The dense local index can be different in +// different compilations. + +// The global info allows loading the same remote class info from different +// paths and aligning them. Example: Compiling A loads pre-compiled B and C, +// both of which load pre-compiled D. The 2 different D loads unify via this +// global info. + +// These mappings are all trivial identities when compiling one file; they +// only become complex when loading seperately compiled code. + +public class GlobalBits { + public static final int RESERVED = 2; + private final HashMap<String,int[]> _clzOrder2Local = new HashMap<>(); + private final Ary<String> _local2Clz = new Ary<>(String.class); + private final AryInt _local2Order = new AryInt(); + // Local dense index + private int _local; + + GlobalBits() { + _local = RESERVED; + // Reserved null-class bits are global identities and are never + // remapped. + int[] xs = new int[RESERVED]; + xs[0] = RESERVED; // Per-class next order counter + for( int i=0; i<RESERVED; i++ ) { + if( i > 0 ) xs[i] = i; + _local2Clz .setX(i,null); + _local2Order.setX(i,i); + } + _clzOrder2Local.put(null,xs); + } + + // Get a next local index for this clz + int next( String clz ) { return next(clz,-1); } + + // Given a clz and order, return an existing local index or make a new + // mapping and return that. + int next(String clz, int order ) { + // Get the per-clz Order->Local mapping + int[] xs = _clzOrder2Local.get(clz); + if( xs == null ) + xs = new int[]{RESERVED,1}; // Make, if it does not exist + // Caller does not have an order in mind, just take the next one stored + // in xs[0] + if( order == -1 ) + order = xs[0]++; + // Order exists from prior compilation, but not recorded locally + else if( order >= xs[0] ) + xs[0] = order+1; + // Grow as needed + while( xs.length <= order ) + xs = Arrays.copyOf(xs,xs.length*2); + // Have a local mapping already? + if( xs[order] != 0 ) + return xs[order]; + int local = _local++; + bind(local,clz,order); + return local; // The dense local index + } + + // Bind a specific dense local index to {clz,order}. Used by both fresh + // allocation and deserialization, where local indexes must not compact + // across holes because XInt bitsets are serialized against this space. + private void bind( int local, String clz, int order ) { + if( _local <= local ) + _local = local+1; + _local2Clz .setX(local,clz ); + _local2Order.setX(local,order); + + // Inline-only fidx holes are represented as {null,0}. They need the + // reverse mapping above, but no forward map because xs[0] is the + // per-class next-order counter. + if( clz==null && order==0 ) + return; + + int[] xs = _clzOrder2Local.get(clz); + if( xs == null ) + xs = new int[]{RESERVED,1}; + if( order >= xs[0] ) + xs[0] = order+1; + while( xs.length <= order ) + xs = Arrays.copyOf(xs,xs.length*2); + _clzOrder2Local.put(clz,xs); + xs[order] = local; + } + + // A file-local function that will inline, and never write to disk + public int nextInline( ) { + int local = _local++; + _local2Clz .setX(local,null); + _local2Order.setX(local,0); + return local; + } + // Get a next local index for the same clz as this old index + public int next( int old ) { return next(_local2Clz.at(old)); } + + // Map a file-local dense index to this compilation unit's local dense index. + public int map( GlobalBits fileBits, int fileLocal ) { + if( fileLocal >= fileBits._local2Clz.size() ) + throw new ArrayIndexOutOfBoundsException(""+fileLocal+" >= "+fileBits._local2Clz.size()); + return findOrNext(fileBits._local2Clz.at(fileLocal), fileBits._local2Order.at(fileLocal)); + } + + // Map a global {clz,order} to a local one; keep any existing mapping or + // make a new one as needed. + private int findOrNext(String clz, int order ) { + // Reserved null-class identities are not represented in xs[order], + // because xs[0] is the next-order counter. + if( clz==null && order < _local2Clz.size() && _local2Order.at(order)==order && _local2Clz.at(order)==null ) + return order; + int[] xs = _clzOrder2Local.get(clz); + if( xs != null && order < xs.length ) { + int local = xs[order]; + if( local < _local2Clz.size() && _local2Order.at(local)==order && Objects.equals(_local2Clz.at(local),clz) ) + return local; + } + return next(clz,order); + } + + // Write out to BAOS enough bits to unwind the local back to global index + void packed( BAOS baos, HashMap<String,Integer> strs ) { + // Unwind a local index to the clz. + baos.packed2(_local2Clz.size()); + for( int i=RESERVED; i<_local2Clz.size(); i++ ) { + String clz = _local2Clz.at(i); + baos.packed2(clz==null ? 0 : strs.get(clz)); + baos.packed2( _local2Order.at(i) ); + } + } + + void gather( HashMap<String,Integer> strs ) { + for( int i=RESERVED; i<_local2Clz.size(); i++ ) + Serialize.gather(strs,_local2Clz.at(i)); + } + + // Read in the BAIS bits needed to make a file-local -> global mapping + static GlobalBits packed( BAOS bais, String[] strs ) { + var gb = new GlobalBits(); + int n = bais.packed2(); + for( int i=RESERVED; i<n; i++ ) { + int sx = bais.packed2(); + String clz = strs[sx]; + if( clz.isEmpty() ) clz = null; + int order = bais.packed2(); + gb.bind(i,clz,order); + } + return gb; + } + + @Override public String toString() { + SB sb = new SB().p('['); + for( int i=0; i<_local2Clz.size(); i++ ) { + if( i > 0 ) sb.p(','); + sb.p(i).p(" <-> ").p(_local2Clz.at(i)).p('#').p(_local2Order.at(i)); + } + return sb.p(']').toString(); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java new file mode 100644 index 00000000..5d8ed03c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -0,0 +1,376 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.IterPeeps.WorkList; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import java.util.*; + +public abstract class GlobalCodeMotion { + + // Arrange that the existing isCFG() Nodes form a valid CFG. The + // Node.use(0) is always a block tail (either IfNode or head of the + // following block). There are no unreachable infinite loops. + public static void buildCFG( CodeGen code ) { + Ary<CFGNode> rpo = new Ary<>(CFGNode.class); + _rpo_cfg(null, code._start, code.visit(), rpo); + // Reverse in-place + for( int i=0; i< rpo.size()>>1; i++ ) + rpo.swap(i,rpo.size()-1-i); + // Set global CFG + code._cfg = rpo; + + schedEarly(code); + + // Break up shared global constants by functions + breakUpGlobalConstants(code._start ); + + code._visit.clear(); + schedLate(code); + } + + // Post-Order of CFG + private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, Ary<CFGNode> rpo) { + if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) ) + return; // Been there, done that + if( def instanceof ReturnNode && use instanceof CallEndNode ) + return; + assert !( def instanceof CallNode && use instanceof FunNode ); // All calls unwired now + visit.set(cfg._nid); + for( Node useuse : cfg._outputs ) + _rpo_cfg(cfg,useuse,visit,rpo); + rpo.add(cfg); + } + + // Break up shared global constants by functions + private static void breakUpGlobalConstants( Node start ) { + + // For all global constants + for( int i=0; i< start.nOuts(); i++ ) { + Node con = start.out(i); + // Instruction selection can expand one shared TypeNode constant + // into a constant-valued machine expression. Split either form + // before scheduling, since separate functions have no CFG LCA. + if( !(con instanceof CFGNode) && + ((con._type.isHighOrConst() && con.isConst()) || + con instanceof MachNode mach && mach.isClone() && con._type.isConstant()) ) { + breakUpGlobalConstantSingle( con ); + i--; // Removed a global constant, re-run same index + } + } + } + + + private static void breakUpGlobalConstantSingle( Node con ) { + // While constant has users in different functions + while( true ) { + // Find a function user, and another function + FunNode fun = null; + boolean done=true; + for( Node use : con.outs() ) { + if( use == null ) continue; + FunNode fun2 = useFun(use); + if( fun2==null || !CodeGen.CODE.owns(fun2) ) continue; + if( fun==null || fun==fun2 ) fun=fun2; + else { done=false; break; } + } + // Single function user, so this constant is not shared + if( done ) { + con.setDef(0,fun); + return; + } + // Move function users to a private constant + Node con2 = con.copy(); // Private constant clone + // Node copies made through Node(Node) carry unregistered inputs, + // while some machine copies construct fresh registered edges. + // Detach correctly in either case before pinning to the function. + Node old = con2.in(0); + if( old != null && old._outputs.find(con2) != -1 ) + con2.setDef(0,null); + else + con2._inputs.set(0,null); + con2.setDef(0,fun); + // Move function users to this private constant + for( int j=0; j<con._outputs._len; j++ ) { + Node use = con.out(j); + if( use == null ) continue; + FunNode fun2 = useFun(use); + if( fun2==null || !CodeGen.CODE.owns(fun2) ) continue; + if( fun2==fun ) { + use.setDef(use._inputs.find(con),con2); + j--; + } + } + } + } + + private static FunNode useFun(Node use) { + if( use instanceof ReturnNode ret ) + return ret.fun(); + if( use instanceof ParmNode parm ) + return parm.fun(); + CFGNode cfg = use.cfg0(); + if( cfg==null ) return null; + FunNode fun = cfg.fun(); + // Use itself is not in a function... which makes it a 2-part + // constant such as happens on some chips where large constants have + // to be built up in parts. PtrToInt followed by the array-body Add is + // another zero-code/global-value chain, so walk through any number of + // single-use pieces until reaching the function-owned use. + if( fun==null ) { + do { + assert use.nOuts()==1; + use = use.out(0); + cfg = use.cfg0(); + fun = cfg==null ? null : cfg.fun(); + } while( fun==null ); + } + return fun; + } + + + // ------------------------------------------------------------------------ + // Visit all nodes in CFG Reverse Post-Order, essentially defs before uses + // (except at loops). Since defs are visited first - and hoisted as early + // as possible, when we come to a use we place it just after its deepest + // input. + private static void schedEarly(CodeGen code) { + // Reverse Post-Order on CFG + for( CFGNode cfg : code._cfg ) { + cfg.loopDepth(); + for( Node n : cfg._inputs ) + _schedEarly(n,code ); + if( cfg instanceof RegionNode ) + for( Node phi : cfg._outputs ) + if( phi instanceof PhiNode ) + _schedEarly(phi,code ); + } + } + + private static void _schedEarly(Node n, CodeGen code) { + if( n==null || code._visit.get(n._nid) ) return; // Been there, done that + assert !(n instanceof CFGNode); + code._visit.set(n._nid); + // Schedule not-pinned not-CFG inputs before self. Since skipping + // Pinned, this never walks the backedge of Phis (and thus spins around + // a data-only loop), eventually attempting relying on some pre-visited- + // not-post-visited data op with no scheduled control. + for( Node def : n._inputs ) + if( def!=null && !(def instanceof PhiNode) ) + _schedEarly(def,code); + + // If not-pinned (e.g. constants, projections, phi) and not-CFG + if( !n.isPinned() ) { + // Schedule at deepest input + CFGNode early = code._start; // Maximally early, lowest idepth + if( n.in(0) instanceof CFGNode cfg ) early = cfg; + for( int i=1; i<n.nIns(); i++ ) + if( n.in(i)!=null && n.in(i).cfg0().idepth() > early.idepth() ) + early = n.in(i).cfg0(); // Latest/deepest input + n.setDef(0,early); + } + } + + // ------------------------------------------------------------------------ + private static void schedLate( CodeGen code ) { + CFGNode[] late = new CFGNode[code.UID()]; + Node[] ns = new Node[code.UID()]; + // Breadth-first scheduling + breadth(code._stop,ns,late); + + // Copy the best placement choice into the control slot + for( int i=0; i<late.length; i++ ) + if( ns[i] != null && !(ns[i] instanceof ProjNode) ) + ns[i].setDef(0,late[i]); + } + + private static void breadth(Node stop, Node[] ns, CFGNode[] late) { + // Things on the worklist have some (but perhaps not all) uses done. + WorkList<Node> work = new WorkList<>(); + work.push(stop); + Node n; + outer: + while( (n = work.pop()) != null ) { + assert late[n._nid]==null; // No double visit + // These I know the late schedule of, and need to set early for loops + if( n instanceof CFGNode cfg ) late[n._nid] = cfg.blockHead() ? cfg : cfg.cfg(0); + else if( n instanceof PhiNode phi ) late[n._nid] = phi.region(); + else if( n instanceof ProjNode && n.in(0) instanceof CFGNode cfg ) late[n._nid] = cfg; + else { + + // All uses done? + for( Node use : n._outputs ) + if( use!=null && late[use._nid]==null ) + continue outer; // Nope, await all uses done + + // Loads need their memory inputs' uses also done + if( n instanceof MemOpNode ld && ld._isLoad ) + for( Node memuse : ld.mem()._outputs ) + if( late[memuse._nid]==null && + // New makes new memory, never crushes load memory + !(memuse instanceof NewNode) && + // Load-use directly defines memory + (memuse._type instanceof TypeMem || + // Load-use directly defines memory + memuse instanceof CallNode || + // Load-use indirectly defines memory + (memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem)) ) + continue outer; + + // All uses done, schedule + _doSchedLate(n,ns,late); + } + + // Walk all inputs and put on worklist, as their last-use might now be done + for( Node def : n._inputs ) { + if( def!=null && late[def._nid]==null ) + work.push(def); + // if the def has a load use, maybe the load can fire + if( def!=null ) + for( Node out : def._outputs ) + if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null ) + work.push(ld); + } + + if( n instanceof LoopNode loop ) { + for( Node phi : loop._outputs ) { + if( phi instanceof PhiNode && late[phi._nid]==null ) + work.push(phi); + } + } + } + } + + private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late) { + // Walk uses, gathering the LCA (Least Common Ancestor) of uses + CFGNode early = n.in(0) instanceof CFGNode cfg ? cfg : n.in(0).cfg0(); + assert early != null; + CFGNode lca = null; + for( Node use : n._outputs ) + if( use != null ) + lca = use_block(n,use, late)._idom(lca,null); + + // Loads may need anti-dependencies, raising their LCA + if( n instanceof MemOpNode load && load._isLoad ) + lca = find_anti_dep(lca,load,early,late); + + + // Nodes setting a single register and getting killed will stay close + // to the uses, since they will be forced to spill anyway. The kill + // check is very weak, and some may be hoisted only to spill in the RA. + if( n instanceof MachNode mach ) { + RegMask out = mach.outregmap(); + if( out!=null && out.size1() ) { + int reg = mach.outregmap().firstReg(); + // Look for kills + outer: + for( CFGNode lca2=lca; lca2 != early; lca2 = lca2.idom() ) { + if( lca2 instanceof MachNode mach2 ) { + for( int i=1; i<lca2.nIns(); i++ ) { + RegMask mask = mach2.regmap(i); + if( mask!=null && mask.test(reg) ) { + early = lca2 instanceof IfNode ? lca2.idom() : lca2; + break outer; + } + } + RegMask kill = mach2.killmap(); + if( kill != null ) + throw Utils.TODO(); + } + } + } + } + + // Walk up from the LCA to the early, looking for best place. This is + // the lowest execution frequency, approximated by least loop depth and + // deepest control flow. + CFGNode best = lca; + lca = lca.idom(); // Already found best for starting LCA + for( ; lca != early.idom(); lca = lca.idom() ) + if( better(lca,best) ) + best = lca; + assert !(best instanceof IfNode); + ns [n._nid] = n; + late[n._nid] = best; + } + + // Block of use. Normally from late[] schedule, except for Phis, which go + // to the matching Region input. + private static CFGNode use_block(Node n, Node use, CFGNode[] late) { + if( use instanceof ParmNode parm && n.cfg0().fun()==parm.fun() ) + return late[use._nid]; + if( !(use instanceof PhiNode phi) ) + return late[use._nid]; + CFGNode found=null; + for( int i=1; i<phi.nIns(); i++ ) + if( phi.in(i)==n ) + found = phi.region().cfg(i)._idom(found,null); + + assert found!=null; + return found; + } + + + // Least loop depth first, then largest idepth + private static boolean better( CFGNode lca, CFGNode best ) { + return lca.loopDepth() < best.loopDepth() || + lca instanceof NeverNode || + lca.idepth() > best.idepth() || + best instanceof IfNode; + } + + private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) { + // We could skip final-field loads here. + // Walk LCA->early, flagging Load's block location choices + for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) + cfg._anti = load._nid; + // Walk load->mem uses, looking for Stores causing an anti-dep + for( Node mem : load.mem()._outputs ) { + switch( mem ) { + case MemOpNode st: + if( !st._isLoad && load._alias == st._alias ) { + assert late[mem._nid] != null; + lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st ); + } + break; // Loads do not cause anti-deps on other loads + case EscapeNode esc: + if( load._alias == esc.fld()._alias ) { + assert late[mem._nid] != null; + lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, mem ); + } + break; + case CallNode call: + assert late[call._nid]!=null; + lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call); + break; + case PhiNode phi: + // Repeat anti-dep for matching Phi inputs. + // No anti-dep edges but may raise the LCA. + for( int i=1; i<phi.nIns(); i++ ) + if( phi.in(i)==load.mem() ) + lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null); + break; + case NewNode st: break; + case ReturnNode ret: break; // Load must already be ahead of Return + case MemMergeNode ret: break; // Mem uses now on ScopeMin + case NeverNode never: break; + default: throw Utils.TODO(); + } + } + return lca; + } + + // + private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) { + // Store and Load overlap, need anti-dependence + if( stblk._anti==load._nid ) { + lca = stblk._idom(lca,null); // Raise Loads LCA + if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved, + st.addDef(load); // Add anti-dep as well + return lca; // Cap this stores' anti-dep to here + } + return lca; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/IFG.java new file mode 100644 index 00000000..1d88597c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/IFG.java @@ -0,0 +1,556 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.IdentityHashMap; + +// Interference Graph +abstract public class IFG { + // Interference Graph Support + + // Map from a Basic Block to Live-Out: {a map from a Live Range to a Def} + private static final IdentityHashMap<CFGNode,IdentityHashMap<LRG,Node>> BBOUTS = new IdentityHashMap<>(); + static void resetBBLiveOut() { + for( IdentityHashMap<LRG,Node> bbout : BBOUTS.values() ) + bbout.clear(); + } + static final IdentityHashMap<LRG,Node> TMP = new IdentityHashMap<>(); + + // Inteference Graph: Array of Bitsets + static final Ary<BitSet> IFG = new Ary<>(BitSet.class); + static void resetIFG() { + for( BitSet bs : IFG ) + if( bs!=null ) bs.clear(); + } + + // Set matching bit + private static void addIFG( LRG lrg0, LRG lrg1 ) { + short x0 = lrg0._lrg, x1 = lrg1._lrg; + // Triangulate + if( x0 < x1 ) _addIFG(x0,x1); + else _addIFG(x1,x0); + } + // Add lrg1 to lrg0's conflict set + private static void _addIFG( short x0, short x1 ) { + BitSet ifg = IFG.atX(x0); + if( ifg==null ) + IFG.setX(x0,ifg = new BitSet()); + ifg.set(x1); + } + + + private static final Ary<CFGNode> WORK = new Ary<>(CFGNode.class); + static void pushWork(CFGNode bb) { + if( WORK.find(bb) == -1 ) + WORK.push(bb); + } + + // ------------------------------------------------------------------------ + // Visit all blocks, using the live-out LRGs per-block and doing a backwards + // walk over each block. At the end of the walk, push the live-out sets to + // prior blocks. Due to loops, no single visitation order suffices. This + // problem can be fairly efficiently solved with a proper LIVE calculation + // and bitsets. In the interests of simplicity (and assumption of smaller + // programs) I am skipping LIVE and directly computing it during the IFG + // building. + + // Start by setting the live-out of the exit block (to empty), and putting + // it on a worklist. + + // - Pull from worklist a block who's live-out has changed + // - Walk backwards, adding interferences and computing live. + // - At block head, "push" the new live-out to prior blocks. + // - - If they pick up new live-outs, put them on the worklist. + + public static boolean build( int round, RegAlloc alloc ) { + // Reset all to empty + resetBBLiveOut(); + resetIFG(); + WORK.clear(); + + // Last block has nothing live out + for( CFGNode bb : alloc._code._cfg ) + if( bb.blockHead()) + WORK.push( bb ); + + // Process blocks until no more changes + while( !WORK.isEmpty() ) + do_block(round,alloc,WORK.pop()); + + if( alloc.success() ) + convert2DAdjacency(alloc); + return alloc.success(); + } + + // Walk one block backwards, compute live-in from live-out, and build IFG + static void do_block( int round, RegAlloc alloc, CFGNode bb ) { + assert bb.blockHead(); + IdentityHashMap<LRG,Node> live_out = BBOUTS.get(bb); + TMP.clear(); + if( live_out != null ) + TMP.putAll(live_out); // Copy bits to temp + + // A backwards walk over instructions in the basic block + for( int inum = bb.nOuts()-1; inum >= 0; inum-- ) { + Node n = bb.out(inum); + if( n.in(0) != bb ) continue; + // In a backwards walk, proj users come before the node itself + if( n instanceof MultiNode ) + for( Node proj : n.outs() ) + if( proj instanceof ProjNode ) + do_node(alloc,proj); + if( n instanceof MachNode ) + do_node(alloc,n); + } + + // The bb head kills register, e.g. a CallEnd and caller-save registers + if( bb instanceof MachNode mach ) + kills(alloc,mach); + + // Push live-sets backwards to priors in CFG. + if( bb instanceof RegionNode ) + for( int i=1; i<bb.nIns(); i++ ) + mergeLiveOut(alloc,bb,i); + else + mergeLiveOut(alloc,bb,0); + } + + private static void do_node(RegAlloc alloc, Node n) { + // Defining means killing live LRG + LRG lrg = alloc.lrg(n); + if( lrg != null ) { + // Check for def-side self-conflict live ranges. These must split, + // and only happens during the first round a particular LRG splits. + selfConflict(alloc,n,lrg); + TMP.remove(lrg); // Kill def + } + + // Phis use and define the same live range, i.e. these LRGs already + // marked conflicted, no need to mark again + if( n instanceof PhiNode ) + return; + + // Kill any killed registers; milli-code routines like New can kill + // will not being a CFG. + if( n instanceof MachNode m ) + kills(alloc,m); + + // Interfere n with all live + if( lrg!=null ) { + // Interfere n with all live + for( LRG tlrg : TMP.keySet() ) { + assert tlrg.leader(); + // Skip self + if( lrg != tlrg && + // And register sets overlap + lrg._mask.overlap(tlrg._mask) ) + // Then tlrg and lrg interfere. If lrg needs the single + // last tlrg register at some point, either tlrg or lrg + // must fail. If *n* (a subset of lrg) needs the single + // last tlrg register then only tlrg must fail. + if( ((MachNode)n).outregmap().size1() ) { + if( !tlrg.clr(lrg._mask.firstReg()) ) // Clear bit, no interference + alloc.fail(tlrg); // Clearing drives mask to empty + } else addIFG(lrg,tlrg); // Add interference + } + } + + + // Check for self-conflict live ranges. These must split, and only + // happens during the first round when a particular LRG splits. + // Also record use-side spills for biased coloring. + // Then make all inputs live. + for( int i=1; i<n.nIns(); i++ ) { + Node def = n.in(i); + if( def==null ) continue; + LRG lrg1 = alloc.lrg(def); + if( lrg1==null ) continue; // Anti-dependence, etc, no register + + // Check use-side for self-conflict; if so we MUST split + selfConflict(alloc,def,lrg1); + + // Look for a must-use single register conflicting with some other must-def. + if( n instanceof MachNode m ) { + RegMask ni_mask = m.regmap(i); + if( ni_mask!=null && ni_mask.size1() ) { // Must-use single register + // Search all current live + for( LRG tlrg : TMP.keySet() ) { + assert tlrg.leader(); + Node live = TMP.get(tlrg); + if( live != def && live instanceof MachNode lmach && lmach.outregmap().overlap(ni_mask) ) { + // Deny the register, since it absolutely must be used here + if( !tlrg.clr(ni_mask.firstReg()) ) + // Then direct reg-reg conflict between use here (at n.in(i)) and def (of tlrg) there. + // Fail the older live range, it must move its register. + alloc.fail( tlrg ); + } + } + } + } + + // All inputs live - except in self conflicts, where we keep the prior def alive + // until it goes. + TMP.put(lrg1,def); + } + } + + // Single-register defines *must* have their register; other live ranges + // *must* avoid this register, so instead of interfering we remove the + // single register from the other rmask. + private static void kills( RegAlloc alloc, MachNode m ) { + RegMask killMask = m.killmap(); + if( killMask==null ) return; + // Kill registers with all live + for( LRG tlrg : TMP.keySet() ) { + assert tlrg.leader(); + // Always, tlrg cannot use kills + if( tlrg._mask.overlap(killMask) ) { + // Disallow clone-ables from killing registers. Just fail + // them and re-clone closer to target... so no kill. + // Special case for Intel XOR used to zero. + Node n = (Node)m; + CFGNode effUseBlk = n.out(0) instanceof PhiNode phi ? phi.region().cfg(phi._inputs.find(n)) : n.out(0).cfg0(); + if( m.isClone() && // Must be clonable + (n.nOuts()>1 || // Has many users OR + // Only 1 user but effective use is remote block + effUseBlk != n.cfg0() )) + // Then fail the clonable; it should split or move + alloc.fail(alloc.lrg((Node)m)); + // Else clonable cannot move + else if( !tlrg.sub(killMask) ) + alloc.fail(tlrg); + } + } + } + + // Check for self-conflict live ranges. These must split, and only happens + // during the first round a particular LRG splits. + private static void selfConflict(RegAlloc alloc, Node n, LRG lrg) { + selfConflict(alloc,n,lrg,TMP.get(lrg)); + } + private static void selfConflict(RegAlloc alloc, Node n, LRG lrg, Node prior) { + // Casts never produce a *new* value, just lift our understanding of the value. + // They do not force self-conflicts. + if( prior!=null && prior != n && !(n instanceof CheckCastNode) ) { + lrg.selfConflict(prior); + lrg.selfConflict(n); + alloc.fail(lrg); // 2 unrelated values live at once same live range; self-conflict + } + } + + // Merge TMP into bb's live-out set; if changes put bb on WORK + private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) { + CFGNode bb = priorbb.cfg(i); + if( bb == null ) return; // Start has no prior + while( !bb.blockHead() ) bb = bb.cfg0(); + + // Lazy get live-out set for bb + IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() ); + + for( LRG lrg : TMP.keySet() ) { + Node def = TMP.get(lrg); + // Effective def comes from phi input from prior block + if( def instanceof PhiNode phi && phi.cfg0()==priorbb ) { + assert i!=0; + def = phi.in( i ); + } + Node def_bb = lrgs.get(lrg); + if( def_bb==null ) { + lrgs.put(lrg,def); + pushWork(bb); + } else { + // Alive twice with different definitions; self-conflict + selfConflict(alloc,def,lrg,def_bb); + } + } + } + + + static void convert2DAdjacency( RegAlloc alloc ) { + // Convert the 2-D array of bits (a 1-D array of BitSets) into an + // adjacency matrix. + int maxlrg = alloc._LRGS.length; + for( int i=1; i<maxlrg; i++ ) { + BitSet ifg = IFG.atX(i); + if( ifg != null ) { + LRG lrg0 = alloc._LRGS[i]; + for( int lrg = ifg.nextSetBit(0); lrg>=0; lrg=ifg.nextSetBit(lrg+1) ) { + LRG lrg1 = alloc._LRGS[lrg]; + lrg0.addNeighbor(lrg1); + lrg1.addNeighbor(lrg0); + } + } + } + } + + + // ------------------------------------------------------------------------ + // Color the inference graph. + + // Coloring works by removing "trivial" live ranges from the IFG - those + // live ranges with fewer neighbors than available colors (registers). + // These are trivial because even if every neighbor takes a unique color, + // there's at least one more available to color this live range. + + // If we hit a clique of non-trivial live ranges, we pick one to be "at + // risk" of not-coloring. Good choices include live ranges with a large + // area and few hot uses. + + // Then we reverse and put-back live ranges - picking a color as we go. + // If there's no spare color we'll have to spill this at-risk live range. + + public static boolean color(int round, RegAlloc alloc) { + int maxlrg = alloc._LRGS.length, nlrgs=0; + for( int i=1; i<maxlrg; i++ ) + if( alloc._LRGS[i] != null ) + nlrgs++; + + // Simplify + + // Walk all the LRGS looking for some trivial ones to start with. + // During this pass all LRGS are broken into 3 disjoint sets: + // - trivial, removed from IFG; color_stack[0 to sptr] + // - trivial, not (yet) removed from IFG; color_stack[sptr to swork] + // - unknown; color_stack[work to maxlrg] + + // Gather all not-unified (not-null); separate trivial and non-trivial set. + int sptr = 0, swork = 0; + LRG[] color_stack = new LRG[nlrgs]; + for( int i=0, j=0; i<maxlrg; i++ ) { + LRG lrg = alloc._LRGS[i]; + if( lrg==null ) continue; // Unified lrgs are null here + color_stack[j++] = lrg; + if( lrg.lowDegree() ) swap(color_stack, swork++, j-1); + } + + // Pull all lrgs from IFG, in trivial order if possible + while( sptr < color_stack.length ) { + // Swap best color up front + pickColor(color_stack,sptr,swork); + + // Pick a trivial lrg, and (temporarily) remove from the IFG. + LRG lrg = color_stack[sptr++]; + // If sptr was swork, then pulled an at-risk lrg + if( sptr > swork ) + swork = sptr; + + // Walk all neighbors and remove + if( lrg._adj != null ) { + for( LRG nlrg : lrg._adj ) { + // Remove and compress out neighbor + if( nlrg.removeCompress(lrg) ) { + // Neighbor is just exactly going trivial as 'lrg' is removed from IFG + // Find "j" position in the color_stack + int jx = swork; while( color_stack[jx] != nlrg ) jx++; + // Add trivial neighbor to trivial list. Pull lrg j out of + // unknown set, since its now in the trivial set + swap(color_stack, swork++, jx); + } + } + } + } + + // Reverse simplify (unstack the color stack), and set colors (registers) for live ranges + while( sptr > 0 ) { + LRG lrg = color_stack[--sptr]; + if( lrg==null ) continue; + RegMaskRW rmask = lrg._mask.copy(); + // Walk neighbors and remove adjacent colors + if( lrg._adj!=null ) { + for( LRG nlrg : lrg._adj ) { + nlrg.reinsert(lrg); + int reg = nlrg._reg; + if( reg!= -1 ) // Failed neighbors do not count + rmask.clr(reg); // Remove neighbor from my choices + } + } + // At-risk live-range did not color? + if( rmask.isEmpty() ) { + alloc.fail(lrg); + lrg._reg = -1; + } else { + // Pick first available register + short reg = rmask.firstReg(); + // Pick a "good" color from the choices. Typically, biased-coloring + // removes some over-spilling. + if( rmask.size() > 1 ) reg = biasColor(alloc,lrg,reg,rmask); + lrg._reg = reg; // Assign the register + } + } + + return alloc.success(); + } + + // Pick LRG from color stack + private static void pickColor(LRG[] color_stack, int sptr, int swork) { + // Out of trivial colorable, pick an at-risk to pull + if( sptr==swork ) + swap(color_stack,sptr,pickRisky(color_stack,sptr)); + // When coloring, we'd like to give more choices; so when coloring we'd + // like to see the single-def first (since no choices anyway), then + // non-split related (so more live ranges get colored), then + // split-related last, so they have more colors to bias towards. + + // Working in reverse, pick first split-related with many regs, then + // those with some regs, then single-def. + int bidx=sptr; + LRG best=color_stack[bidx]; + for( int idx = sptr+1; idx < swork; idx++ ) + if( betterLRG(best,color_stack[idx]) ) + best = color_stack[bidx=idx]; + if( bidx != sptr ) + swap(color_stack,sptr,bidx); // Pick best at sptr + } + + // Returns true if 'lrg' is better than 'best' + private static boolean betterLRG( LRG best, LRG lrg ) { + // If single-def varies, keep the not-single-def + if( best.size1() != lrg.size1() ) + return best.size1(); + // If hasSplit varies, keep the hasSplit + if( best.hasSplit() != lrg.hasSplit() ) + return lrg.hasSplit(); + // If both are single register (so both sizes are 1), + // return a multi-use over a multi-def + if( best.size1() ) + return !best._multiUse && lrg._multiUse; + // Keep large register count + return best.size() < lrg.size(); + } + + // Pick a live range that hasn't already spilled, or has a single-def- + // single-use that are not adjacent. + private static int pickRisky( LRG[] color_stack, int sptr ) { + int best=sptr; + int bestScore = pickRiskyScore(color_stack[best]); + for( int i=sptr+1; i<color_stack.length; i++ ) { + if( bestScore == 1000000 ) return best; // Already max score + int iScore = pickRiskyScore(color_stack[i]); + if( iScore > bestScore ) + { best = i; bestScore = iScore; } + } + return best; + } + + // Pick a live range to pull, that might not color. + // + // Picking a live range with a very large span, with defs and uses outside + // loops means spilling a relative cheap live range and getting that + // register over a large area. + // + // Picking a live range that is very close to coloring might allow it to + // color despite being risky. + private static int pickRiskyScore( LRG lrg ) { + // Pick single-def clonables that are not right next to their single-use. + // Failing to color these will clone them closer to their uses. + if( !lrg._multiDef && lrg._machDef.isClone() && lrg._machUse != null ) { + Node def = ((Node)lrg._machDef); + Node use = ((Node)lrg._machUse); + CFGNode cfg = def.cfg0(); + if( cfg != use.cfg0() || // Different blocks OR + // Same block, but not close + cfg._outputs.find(def) < cfg._outputs.find(use)+1 ) + return 1000000; + } + + // Always pick callee-save registers as being very large area recovered + // and very cheap to spill. + if( lrg._machDef instanceof CalleeSaveNode ) + return 1000000-2-lrg._mask.firstReg(); + if( lrg._splitDef != null && lrg._splitDef. in(1) instanceof CalleeSaveNode && + lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode ) + return 1000000-1; + + // TODO: cost/benefit model. Perhaps counting loop-depth (freq) of def/use for cost + // and "area" for benefit + return 1000 + (lrg._multiUse ? -100 : 0); + } + + private static short biasColor( RegAlloc alloc, LRG lrg, short reg, RegMask mask ) { + if( mask.size1() ) return reg; + // Check chain of splits up the def-chain. Take first allocated + // register, and if it's available in the mask, take it. + Node def = lrg._splitDef, use = lrg._splitUse; + int tidx=0, cnt=0; + + while( def != null || use != null ) { + if( cnt++ > 10 ) break; + + if( def != null ) { + short bias = biasColor( alloc, def, mask ); + if( bias >= 0 ) return bias; // Good bias + if( bias == -2 ) def = null; // Kill this side, no more searching + else if( (tidx=biasable(def)) == 0 ) def = null; + } + + if( use != null ) { + short bias = biasColor( alloc, use, mask ); + if( bias >= 0 ) return bias; // Good bias + if( bias == -2 ) use = null; // Kill this side, no more searching + else if( biasable(use)==0 ) use = null; + } + + if( def != null ) { + short bias = biasColorNeighbors( alloc, def, mask ); + if( bias >= 0 ) return bias; + // Advance def side + def = def.in(tidx); + if( alloc.lrg(def)==null ) def=null; + } + + if( use != null ) { + short bias = biasColorNeighbors( alloc, use, mask ); + if( bias >= 0 ) return bias; + use = use.out(0); + if( biasable(use)==0 ) use=null; + } + + } + return mask.firstReg(); + } + + private static int biasable(Node split) { + if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1 + if( split instanceof PhiNode phi ) return phi.region() instanceof LoopNode ? 2 : 1; // Yes biasable, advance is slot 1 + if( !(split instanceof MachNode mach) ) return 0; // Not biasable + return mach.twoAddress(); // Only biasable if 2-addr + } + + // 3-way return: + // - good bias reg, take it & exit + // - this path is cutoff; do not search here anymore + // - advance this side + private static short biasColor( RegAlloc alloc, Node split, RegMask mask ) { + short bias = alloc.lrg(split)._reg; + if( bias != -1 ) { + if( mask.test(bias) ) return bias; // Good bias + else return -2; // Kill this side + } else return -1; // Advance this side + } + + // Check if we can match "split" color, or else trim "mask" to colors + // "split" might get. + private static short biasColorNeighbors( RegAlloc alloc, Node split, RegMask mask ) { + LRG slrg = alloc.lrg(split); + if( slrg._adj == null ) return -1; // No trimming + + // Can I limit my own choices to valid neighbor choices? + for( LRG alrg : slrg._adj ) { + short reg = alrg._reg; + if( reg == -1 && alrg._mask.size1() ) + reg = alrg._mask.firstReg(); + if( reg != -1 ) { + mask.clr(alrg._reg); + if( mask.size1() ) + return mask.firstReg(); + } + } + return -1; // No obvious color choice, but mask got trimmed + } + + private static void swap( LRG[] ary, int x, int y ) { + LRG tmp = ary[x]; ary[x] = ary[y]; ary[y] = tmp; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java new file mode 100644 index 00000000..3a4596fe --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java @@ -0,0 +1,238 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.IdentityHashMap; + +/** A Live Range + * <p> + * A live range is a set of nodes and edges which must get the same register. + * Live ranges form an interconnected web with almost no limits on their + * shape. Live ranges also gather the set of register constraints from all + * their parts. + * <p> + * Most of the fields in this class are for spill heuristics, but there are a + * few key ones that define what a LRG is. LRGs have a unique dense integer + * `_lrg` number which names the LRG. New `_lrg` numbers come from the + * `RegAlloc._lrg_num` counter. LRGs can be unioned together - + * (<a href="https://en.wikipedia.org/wiki/Disjoint-set_data_structure">this is the Union-Find algorithm</a>) + * - and when this happens the lower numbered `_lrg` wins. Unioning only + * happens during `BuildLRG` and happens because either a `Phi` is forcing all + * its inputs and outputs into the same register, or because of a 2-address + * instruction. LRGs have matching `union` and `find` calls, and a set + * `_leader` field. + */ +public class LRG { + + // Dense live range numbers + final short _lrg; + + // U-F leader; null if leader + LRG _leader; + + // Choosen register + short _reg; + + // Count of single-register defs and uses + short _1regDefCnt, _1regUseCnt; + // Count of all defs, uses. Mostly interested 1 vs many + boolean _multiDef, _multiUse; + + // A sample MachNode def in the live range + public MachNode _machDef, _machUse; + short _uidx; // _machUse input + + // Some splits used in biased coloring + MachConcreteNode _splitDef, _splitUse; + + // All the self-conflicting defs for this live range + IdentityHashMap<Node,String> _selfConflicts; + + // AND of all masks involved; null if none have been applied yet + RegMask _mask; + + // Adjacent Live Range neighbors. Only valid during coloring + Ary<LRG> _adj; + + // Function fully containing the live range; used to find the stack frame + // layout, for spill op offsets. + final public FunNode _fun; + + public int nadj() { return _adj==null ? 0 : _adj._len; } + + void addNeighbor(LRG lrg) { + if( _adj==null ) _adj = new Ary<>(LRG.class); + _adj.push(lrg); + } + + // Remove and compress neighbor list; store ex-neighbor past + // end for unwinding Simplify. True if exactly going low-degree + boolean removeCompress( LRG lrg ) { + _adj.swap(_adj.find(lrg),_adj._len-1); + return _adj._len-- == _mask.size(); + } + + void reinsert( LRG lrg ) { + assert _adj._es[_adj._len]==lrg; + _adj._len++; + } + + // More registers than neighbors + boolean lowDegree() { return nadj() < _mask.size(); } + + LRG( short lrg, FunNode fun ) { _lrg = lrg; _reg = -1; _fun = fun; } + + boolean leader() { return _leader == null; } + + // Fast-path Find from the Union-Find algorithm + LRG find() { + if( _leader==null ) return this; // I am the leader + if( _leader._leader==null ) // I point to the leader + return _leader; + return _rollup(); + } + // Slow-path rollup of U-F + private LRG _rollup() { + LRG ldr = _leader._leader; + // Roll-up + while( ldr._leader!=null ) ldr = ldr._leader; + LRG l2 = this; + while( l2 != ldr ) { + LRG l3 = l2._leader; + l2._leader = ldr; + l2 = l3; + } + return ldr; + } + + // Union `this` and `lrg`, keeping the lower numbered _lrg. + // Includes a number of fast-path cutouts. + LRG union( LRG lrg ) { + assert leader(); + if( lrg==null ) return this; + lrg = lrg.find(); + if( lrg==this ) return this; + return _lrg < lrg._lrg ? _union(lrg) : lrg._union(this); + } + // Union `this` and `lrg`, folding together all stats. + private LRG _union( LRG lrg ) { + assert _fun == lrg._fun; + // Set U-F leader + lrg._leader = this; + // Fold together stats + if( _machDef==null ) { + _machDef = lrg._machDef; + } else if( lrg._machDef!=null ) { + if( _machDef != lrg._machDef ) _multiDef=true; + if( _1regDefCnt==0 ) + _machDef = lrg._machDef; + else if( _machDef==lrg._machDef ) + _1regDefCnt--; + } + _1regDefCnt += lrg._1regDefCnt; + _multiDef |= lrg._multiDef; + + if( _machUse==null ) { + _machUse = lrg._machUse; + _uidx = lrg._uidx; + } else if( lrg._machUse!=null ) { + if( _machUse != lrg._machUse ) _multiUse=true; + if( _1regUseCnt==0 ) { + _machUse = lrg._machUse; + _uidx = lrg._uidx; + } + else if( _machUse==lrg._machUse ) + _1regUseCnt--; + } + _1regUseCnt += lrg._1regUseCnt; + _multiUse |= lrg._multiUse; + + // Fold deepest Split + _splitDef = deepSplit(_splitDef,lrg._splitDef); + _splitUse = deepSplit(_splitUse,lrg._splitUse); + + // Fold together masks + RegMask mask = _mask.and(lrg._mask); + if( mask==null ) + mask = _mask.copy().and(lrg._mask); + _mask = mask; + return this; + } + + private static MachConcreteNode deepSplit( MachConcreteNode s0, MachConcreteNode s1 ) { + return s0==null || (s1!=null && s0.cfg0().loopDepth() < s1.cfg0().loopDepth()) ? s1 : s0; + } + + // Record any Mach def for spilling heuristics + LRG machDef( MachNode def, boolean size1 ) { + if( _machDef!=null && _machDef!=def ) + _multiDef = true; + if( _machDef==null || size1 ) + _machDef = def; + if( size1 ) + _1regDefCnt++; + if( def instanceof SplitNode split ) + _splitDef = deepSplit(_splitDef,split); + return this; + } + + // Record any Mach use for spilling heuristics + LRG machUse( MachNode use, short uidx, boolean size1 ) { + if( _machUse!=null && _machUse!=use ) + _multiUse = true; + if( _machUse==null || size1 ) + { _machUse = use; _uidx = uidx; } + if( size1 ) + _1regUseCnt++; + if( use instanceof SplitNode split ) + _splitUse = deepSplit(_splitUse,split); + return this; + } + + boolean hasSplit() { return _splitDef != null || _splitUse != null; } + short size() { return _mask.size(); } + boolean size1() { return _mask.size1(); } + + + void selfConflict( Node def ) { + if( _selfConflicts == null ) _selfConflicts = new IdentityHashMap<>(); + _selfConflicts.put(def,""); + } + + + // Record intersection of all register masks. + // True if still has registers + boolean and( RegMask mask ) { + RegMask mask2 = mask.and(_mask); + if( mask2==null ) + mask2 = _mask.copy().and(mask); + _mask = mask2; + return !_mask.isEmpty(); + } + // Remove this singular register + // True if still has registers + boolean clr( int reg ) { + if( _mask.clr(reg) ) return true; + _mask = _mask.copy(); // Need a mutable copy + return _mask.clr(reg); + } + // Subtract mask (AND complement) + // True if still has registers + boolean sub( RegMask mask ) { + RegMask mask2 = _mask.sub(mask); + if( mask2==null ) + mask2 = _mask.copy().sub(mask); + _mask = mask2; + return !_mask.isEmpty(); + } + + @Override public String toString() { return toString(new SB()).toString(); } + + public SB toString( SB sb ) { + sb.p("V").p(_lrg); + if( _mask!=null ) _mask.toString(sb); + return sb; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java new file mode 100644 index 00000000..e0b59395 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeStruct; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// In-memory linking +public class LinkMem { + final CodeGen _code; + LinkMem( CodeGen code ) { _code = code; } + + public CodeGen link(Encoding enc) { + // Patch external calls internally + enc.patchGlobalRelocations(); + + // Place constant pool after code, static data after constants + int code = 0; // Code start + int cpool = (enc._bits .size() + code + 15) & -16; // pad to 16 + int sdata = (enc._cpool.size() + cpool + 15) & -16; + + + // Patch local ops, e.g. loading float constants from the constant pool + for( Node op : enc._bigCons.keySet() ) { + Encoding.Relo relo = enc._bigCons.get(op); + boolean ro = !(relo._t instanceof TypeStruct ts) || ts.isConstant(); + int target = relo._target+(ro ? cpool : sdata); + ((RIPRelSize)relo._op).patch(enc, relo._opStart, enc.opLen(relo._op), target - relo._opStart); + } + + return _code; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java new file mode 100644 index 00000000..b6b3a22c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java @@ -0,0 +1,261 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.IterPeeps.WorkList; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import java.util.*; + +public abstract class ListScheduler { + + // eXtra per-node stuff for scheduling. + // Nodes are all block-local, and projections fold into their parent. + private static final IdentityHashMap<Node,XSched> XS = new IdentityHashMap<>(); + private static class XSched { + + Node _n; // Node this extra is for + int _bcnt; // Not-ready not-scheduled inputs + int _rcnt; // Ready not-scheduled inputs + boolean _ruse; // Node IS a "remote use", uses a other-block value + boolean _rdef; // Node IS a "remote def" of a value used in other block + boolean _single; // Defines a single register, likely to conflict + + static final Ary<XSched> FREE = new Ary<>(XSched.class); + static void alloc(CFGNode bb, Node n) { + XSched x = FREE.pop(); + XS.put(n, (x==null ? new XSched() : x).init(bb,n)); + } + void free() { _n = null; FREE.push(this); } + + static XSched get(Node n) { return n==null ? null : XS.get(n instanceof ProjNode && !(n.in(0) instanceof CFGNode) ? n.in(0) : n); } + + private XSched init(CFGNode bb, Node n) { + _n = n; + _bcnt = 0; + _ruse = _rdef = _single = false; + + // Count block-locals and remote inputs + if( !(n instanceof PhiNode) ) + for( Node def : n._inputs ) + if( def != null && def != bb ) { + if( def.cfg0()==bb ) _bcnt++; // Raise block-local input count + else if( !def.isMem() ) _ruse = true; // Remote register user + } + // Count remote outputs and allowed registers + if( n instanceof MultiNode ) + for( Node use : n._outputs ) + computeSingleRDef(bb,use); + else + computeSingleRDef(bb,n); + + if( n instanceof NewNode || n instanceof CallNode ) + bb.fun()._hasCalls = true; + return this; + } + + private void computeSingleRDef(CFGNode bb, Node n) { + // See if this is 2-input, and that input is single-def + if( n instanceof MachNode mach && mach.twoAddress() != 0 ) { + XSched xs = XS.get(n.in(mach.twoAddress())); + if( xs != null ) + _single = xs._single; + } + // Visit all outputs + RegMaskRW rmask = !_single && n instanceof MachNode mach && mach.outregmap() != null ? mach.outregmap().copy() : null; + for( Node use : n._outputs ) { + // Remote use, so this is a remote def + if( use!=null && use.cfg0()!=bb ) _rdef = true; + // And mask between n's def mask and all n's users masks' + if( use instanceof MachNode mach && rmask != null ) + for( int i=1; i<use.nIns(); i++ ) + if( use.in(i)==n ) + rmask.and(mach.regmap(i)); + } + // Defines in a single register + _single |= rmask!=null && rmask.size1(); + } + + // If _bcnt==0, declare ready; move user bcnts into rcnts. + boolean isReady() { + if( _bcnt > 0 || _rcnt > 0 ) return false; + if( _n instanceof MultiNode ) + for( Node use : _n._outputs ) + if( use instanceof ProjNode ) readyUp(use); + // anti-dep from a multireg directly + else _readyUp(use); + else + readyUp(_n); + return true; + } + private static void readyUp(Node n) { + for( Node use : n._outputs ) + _readyUp(use); + } + private static void _readyUp(Node use) { + XSched xs = get(use); + if( xs!=null && !(use instanceof PhiNode) ) + { assert xs._bcnt>0; xs._bcnt--; xs._rcnt++; } + } + + boolean decIsReady() { + assert _rcnt > 0; + _rcnt--; + return isReady(); + } + } + + + // List schedule every block + public static void sched( CodeGen code ) { + XS.clear(); + for( CFGNode bb : code._cfg ) + if( bb.blockHead() ) + local(bb,false); + } + + // Classic list scheduler + private static void local( CFGNode bb, boolean trace ) { + assert XS.isEmpty(); + int len = bb.nOuts(); + + // Count block-locals, remote inputs and outputs + for( int i=0; i<len; i++ ) + XSched.alloc(bb,bb.out(i)); + + // Nodes are ready if they are not used by other nodes in this block. + // Move ready up front. + int ready = 0; + for( int i=0; i<len; i++ ) + if( XSched.get(bb.out(i)).isReady() ) // No block-local inputs? + bb._outputs.swap(ready++,i); // Move into ready set + assert len==0 || ready > 0; + + // Classic list scheduler. Behind sched is scheduled. Between sched + // and ready have zero counts and are ready to schedule. Ahead of + // ready is undefined. + // + // As nodes are scheduled, the nodes they use decrement their bcnts[]. Once + // that gets to 0, all the nodes using it are scheduled, so it's ready. + int sched = 0; + while( sched < len ) { + assert sched < ready; + int pick = best(bb._outputs,sched,ready,trace); // Pick best + Node best = bb._outputs.swap(pick,sched++); // Swap best to head of ready and move into the scheduled set + // Lower ready count of users, and since projections are ready as + // soon as their multi is ready, lower those now too. + for( Node use : best._outputs ) + if( use instanceof ProjNode ) + for( Node useuse : use._outputs ) + ready = ready(bb,useuse,ready); + else + ready = ready(bb,use,ready); + } + + // Reset for next block + for( XSched x : XS.values() ) + x.free(); + XS.clear(); + } + + private static int ready(CFGNode bb, Node use, int ready) { + if( use!=null && use.in(0)==bb && !(use instanceof PhiNode) && XSched.get(use).decIsReady() ) + bb._outputs.set(ready++, use); // Became ready, move into ready set + //bb._outputs.swap(ready++,use); // Became ready, move into ready set + return ready; + } + + + // Pick best between sched and ready. + private static int best( Ary<Node> blk, int sched, int ready, boolean trace ) { + int pick = sched; + Node p = blk.at(pick); + int score = score(p); + if( trace ) { System.out.printf("%4d N%d ",score,p._nid); System.out.println(p); } + for( int i=sched+1; i<ready; i++ ) { + Node n = blk.at(i); + int nscore = score(n); + if( trace ) { System.out.printf("%4d N%d ",nscore,n._nid); System.out.println(n); } + if( nscore > score ) + { score = nscore; pick = i; p = n; } + } + if( trace ) { System.out.printf("Pick: N%d %s\n\n",p._nid,p); } + return pick; + } + + // Highest score wins. Max at 1000, min at 10, except specials. + private static final int[] CNT = new int[3]; + static int score( Node n ) { + if( n instanceof ProjNode ) return 1001; // Pinned at block entry + if( n instanceof CProjNode ) return 1001; // Pinned at block entry + if( n instanceof PhiNode ) return 1000; + if( n instanceof CFGNode ) return 1; // Pinned at block exit + + int score = 500; + // Ideal nodes ignore register pressure scheduling + if( !(n instanceof MachNode) ) + return score; + + // Register pressure local scheduling: avoid overlapping specialized + // registers usages. + + // Subtract 10 (delay) if this op forces a live range to exist that + // cannot be resolved immediately; i.e., live count goes up. Scale to + // 20 if multi-def. + + // Subtract 100 if this op forces a single-def live range to exist + // which might conflict on the same register with other live ranges. + // Defines a single register based on def & uses, and the output is not + // ready. Scale to 200 for multi-def. + CNT[1]=CNT[2]=0; + XSched xn = XSched.get(n); + // If defining a remote value, just generically stall alot. Value is + // used in a later block, can we delay until the end of this block? + if( xn._rdef ) score = 200; // If defining a remote value, just generically stall alot + boolean flags = false; + if( n instanceof MultiNode ) { + for( Node use : n._outputs ) + flags |= singleUseNotReady( use, xn._single ); + } else + flags |= singleUseNotReady( n, xn._single ); + score += -10 * Math.min( CNT[1], 2 ); + score += -100 * Math.min( CNT[2], 2 ); + if( flags ) return 10; + + // Add 10 if this op ends a single-def live range, add 20 for 2 or more. + // Scale to 100,200 if the single-def is also single-register. + CNT[1]=CNT[2]=0; + for( int i=1; i<n.nIns(); i++ ) { + XSched xd = XSched.get( n.in(i) ); + if( xd != null && n.in(i).nOuts()==1 ) + CNT[xd._single || ((MachNode)n).regmap(i)!=null && ((MachNode)n).regmap(i).size1() ? 2 : 1]++; + } + score += 10 * Math.min( CNT[1], 2 ); + score += 100 * Math.min( CNT[2], 2 ); + + // Nothing special + assert 10 <= score && score <= 990; + return score; + } + + // Single use, and that use is not ready to fire once this one does. If + // true, stalling 'n' might reduce 'n's lifetime. Stall by 1 (2 if single) + // per not-ready. + + // If CFG user in this block, it must be last, so definitely stall + // single-def (which stalls flags on x86,arm until the jmp). + private static boolean singleUseNotReady( Node n, boolean single ) { + // If n can rematerialize, assume allocator will split into private uses + // as needed. No impact on local scheduling. + if( n.nOuts()>1 && n instanceof MachNode mach && mach.isClone() ) + return false; + for( Node use : n.outs() ) { + XSched xu = XSched.get(use); + if( xu != null && xu._n instanceof CFGNode ) return true; + if( xu != null && xu._bcnt > 0 ) + CNT[single ? 2 : 1]++; // Since bcnt>0 or CFG, stall until user is more ready + } + return false; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Machine.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Machine.java new file mode 100644 index 00000000..91d07d57 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Machine.java @@ -0,0 +1,115 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; + +abstract public class Machine { + // Human readable machine name. Something like "x86-64" or "arm" or "risc5" + public abstract String name(); + + // Default opcode size for printing; 4 bytes for most 32-bit risc chips + public int defaultOpSize() { return 4; } + + // Human-readable name for a register number, e.g. "RAX" or "R0" + public abstract String[] regs(); + + // Create a split op; any register to any register, including stack slots. + // The FunNode holds the stack layout after RegAlloc, which in turn + // dictates the stack slot (the frame offset) for stack splits. + public abstract SplitNode split( LRG lrg, String kind, byte round ); + + // List of caller-save registers, as a 64bit mask + public abstract long callerSave(); + public long callerSave(String conv) { return callerSave(); } + + // List of never-save registers, e.g. RSP or a ZERO register if you have one + public abstract long neverSave(); + + // Call Argument Mask. Passed in the function signature and argument + // number (2-based; 0 is for control and 1 for memory). Also passed in a 0 + // for the function itself, or for *outgoing* calls, the maximum stack slot + // given to the incoming function arguments (stack slots reserved for + // incoming arguments). + public abstract RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot); + public RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot, String conv) { return callArgMask(tfp,arg,maxArgSlot); } + + // Return register mask, based on signature (GPR vs FPR) + public abstract RegMask retMask(TypeFunPtr tfp); + + // Return PC register + public abstract int rpc(); + + // Return a MachNode unconditional branch + public abstract CFGNode jump(); + + // Instruction select from ideal nodes + public abstract Node instSelect( Node n ); + + /** Stack Slot Numbering + +RA gives all registers a number, starting at 0. Stack slots continue this +numbering going up from the last register number. Common register numbers are +0-15 for GPRs, 16-31 for FPRs, 32 for flags, and stack slots starting at +register#33 going up. These numbers are machine-specific, YMMV, etc; e.g. RPC +is only in stack slot 0 on X86; other cpus start with the rpc in a register +which may spill into any generic spill slot. + +For ease of reading and printing, stack slots start again at slot#0 - but +during RA they are actually biased by CPU.MAX_REG. + +Stack Layout during Reg Alloc: + + Slot Value Description + ---- ------- ----------- +MAXREG+ R+1 argR maxArgSlot(calleR tfp), can be 0 + ... +MAXREG+ 1 arg0 + ---- -- Caller ------ +MAXREG+ 0 RPC +MAXREG+ R+E+N PAD optional alignment + +MAXREG+ + Spills Caller Frame Space; N spills +MAXREG+ R+E+1 Spills + +MAXREG+ R+E argE maxArgSlot(calleE tfp), can be 0 + ... +MAXREG+ R+2 arg0 + ---- -- Callee ------- + ... + +Slots defined by maxArgSlot do not have to contain the actual passed argument; +they can be "shadow slots". If they exist, they are property of the frame / +FunNode where the TFP comes from. + +For a given calleR, there are generally many calleE's and they all share the +low-range stack slots. + +Example:, Win64, main(int arg) { calloc(1,size); } + + Post-Warp: SP+56 shadow-main-arg +Pre-alloc Post-Alloc SP+48 RPC +--- ------- ---------- ------------- +40 spill#3 SP+40 SP+40 spill#3 +39 spill#2 SP+32 "" +38 spill#1 SP+24 "" +37 spill#0 SP+16 "" spill#0 +36 shadow-calloc#size SP+ 8 SP+ 8 shadow-calloc#size +35 shadow-calloc#1 SP+ 0 SP+ 0 shadow-calloc#1 +--- --- FRAME BASE ---------- ---- FRAME BASE ------- +34 shadow-main-arg SP+56 +33 RPC SP+48 + + + */ + + + // Maximum stack slot (or 0) for the args in this TFP. This will include + // shadow slots if defined in the ABI, even if all arguments are passed in + // registers. + public abstract short maxArgSlot(TypeFunPtr tfp); + public short maxArgSlot(TypeFunPtr tfp, String conv) { return maxArgSlot(tfp); } + + // Calling convention used for external C symbols. + public String cCallingConv(String callingConv) { return callingConv; } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Module.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Module.java new file mode 100644 index 00000000..a5d85dbc --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Module.java @@ -0,0 +1,98 @@ +package com.seaofnodes.simple.codegen; + + +/*** + Modules!?!?!? + + Goal: under no circumstances can you load from an uninitialized variable + (although you can see default inits for fields that have defaults). + Goal: nested tree-structured name spaces, to control complexity. + Goal: field decl order within a file is fairly flexible. + + A Module a root-level SMP file, and the full file path must be provided on + the command line. All other Simple structs are relative to this file. + + Example: "simple /user/Dave/hello.smp" + + Defines a module "hello" in directory "/user/Dave", compiles and produces + a `hello` or `hello.exe` output file, and runs it. + + A SMP file has some <finit> file-init code which is static-global. + This code is executed on "first touch" of the file (defined below). + + Example: hello.smp contains: + sys.io.p("Hello World!"); + On first-touch, will print "Hello World!". + + A SMP file is a lexical scope, and code inside it can reference those names. + + Example: hello.smp contains: + val greet = "Hello World!"; // Declare file-scoped variable + sys.io.p(greet); // Reference file-scoped variable + + Static code is NOT allowed to have forward-references AND be executed + before the <finit> finishes initializing all variables. After var + initialization, a singleton instance of the implied file struct is made, + and then <finit> code can call anything. + + Example: + sys.io.p(greet); // Illegal forward reference + val greet = "Oops!"; + + Example: + val delay = { -> greet }; // Legal forward ref, but cannot execute (yet) + val greet = "Hello World!"; // Ref is resolved + sys.io.p(delay()); // Allowed to call delay() + val nprimes = { N -> + // compute number of primes < N + } + // Can otherwise execute any code in a <finit> + sys.io.p(nprimes(100)); + + Inside a directory, you can have sibling SMP files: + File hello.smp contains: + sys.io.p(I9A.GREETING); // File hello references sibling file I9A static globals + File I9A.smp contains: + val GREETING = translate("Hello World!"); + val translate = { str msg -> ... }; + + <finit> code cannot reference sibling files (nor child files, see below) + until all static variables are initialized. This prevents cyclic + initialization bugs, where file A uses vars from file B and vice-versa. + + File Chicken.smp contains: + val lay = Egg.hatch; // Illegal, defining var 'lay' after calling sibling Egg + File Egg.smp contains: + val hatch = Chicken.lay; // Illegal + + You can make cross references, if they get delayed until after all fields + are declared. + + File Chicken.smp contains: + int lay = 2; + lay = Egg.egg; + + File Egg.smp contains: + int egg = 3; + egg = Chicken.lay; + + The problem here, of course, is that you need to touch one of Chicken or + Egg first. + + + Inside a SMP file, you can define structs; structs are also a lexical + scope. Bare code in the struct is part of the struct's <init> and only + runs when an instance is created. Structs do not have static <clinit> (nor + <finit>). Structs <init> cannot have any forward references, and locally + declared functions cannot have both forward references and be executed (or + passed along to a possibly-executing function). + + In the same directory as a SMP file, Simple allows other directories with + names matching SMP files. Inside these dirs can be more SMP files that are + lexically nested, similar to embedded structs. + + +*/ + +public abstract class Module { +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java new file mode 100644 index 00000000..b88067c4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java @@ -0,0 +1,360 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; + +import java.util.Arrays; +import java.util.BitSet; + +// Optimistic Algorithm +abstract public class Opto { + + // Optimistic analysis - extended SCCP + + // Start all Types at TOP, and propagate changes until stable. Unlike a + // normal Sparse Conditional Constant Propagation, this version is + // interprocedural, computing effectively CFA0 in linear time using the + // normal SCCP algorithm. + + // This algo also builds a Call Graph; as Calls become reachable (CONTROL + // not TOP nor XCONTROL), they "link" against the reaching function + // pointers - adding edges from the Call to the Fun, and from the Return to + // the CallEnd. This allows the normal algorithm to use the normal edge + // propagation rules as function argument types stabilize. In the end, we + // get a fairly precise set of Call Graph edges between CallNodes and + // FunNodes. + public static void opto(CodeGen code) { + // Reset Types before running the worklist algorithm + Ary<Type> oldTypes = new Ary<>(Type.class); // Used for asserts only + resetToTop(code,oldTypes); + unlinkStart(code); + + // OPTIMISTIC INTERPROCEDURAL SCCP + sccp(code, oldTypes); + + // After SCCP add everything changed to the worklist and see if + // peepholes can make more progress. + moveChangesToWorklist(code, oldTypes); + + // SCCP can leave a recursive numeric component at BOTTOM with no + // arithmetic mode selected. Resolve the whole component from all + // available int/float evidence before IterPeeps consumes the types. + resolveNumericModes(code); + + // Progress with improved types. E.g. since we have a full Call Graph + // here, we might do better inlining. + code._iter.iterate(code); + + // TODO: + // loop unroll, peel, RCE, etc + + // Freeze field sizes; do struct layouts; convert field offsets into + // constants. + fieldSizes(code); + // Propagate field size constants + code._iter.iterate(code); + + + // Retain surviving functions in their CompUnits for later code + // emission. Start inputs are semantic: they mean unknown callers. + for( FunNode fun : code._linker ) + if( fun != null && !fun.isDead() && !fun._type.isHigh() ) + keepInCompUnit(code,fun); + + for( CompUnit cu : code._compunits.values() ) { + ProjNode mem = cu._start.proj(1); + if( mem != null ) mem.unkeep(); + } + + // To help with testing, sort StopNode Returns by NID + for( Node stop : code._stop._inputs ) + Arrays.sort(stop._inputs._es,0,stop.nIns(), + (x,y) -> ((((ReturnNode)x).fun().isClz() ? -1 : x._nid) - + (((ReturnNode)y).fun().isClz() ? -1 : y._nid) )); + } + + // Reset Types before running the worklist algorithm + private static void resetToTop(CodeGen code, Ary<Type> oldTypes ) { + code._start.walk( x -> { + oldTypes.setX(x._nid,x._type); // Record original types for asserts + x._type = Type.TOP; // Reset all Nodes to TOP + code._iter.add(x); // Visit everybody at least once; most Nodes produce better than TOP + return null; + } ); + } + + + // If wholeWorld, remove the Start inputs to functions - they are not + // called unless they get linked (hence go dead and can be removed). If + // not wholeWorld, keep only public methods (and not the std lib). + private static void unlinkStart(CodeGen code) { + // Keep the Start & Start.ProjMem alive even as we remove all uses + for( CompUnit cu : code._compunits.values() ) { + ProjNode mem = cu._start.proj(1); + if( mem!=null ) mem.keep(); + } + + // Iterate the Stop-of-Stops + for( Node stop : code._stop._inputs ) { + for( int i=0; i<stop.nIns(); i++ ) { + ReturnNode ret = (ReturnNode)stop.in(i); + FunNode fun = ret.fun(); + // Start is a semantic unknown-caller edge. Public functions + // keep it; closed-world functions must be reached by a + // concrete Call edge discovered during SCCP. + if( !(code.owns(fun) && fun.isClz()) && + !code.publicFIDX(fun.sig().fidx()) && + fun.in(1) instanceof StartNode ) { + fun.removeDeadPath(1); + // Unhook from Stop without treating the ReturnNode as + // DEAD. It stays in limbo until Opto links when a caller + // is discovered (or not, and the function is really dead). + stop._inputs.del(i--); + ret.delUse(stop); + } + // Unlink the existing conservative call linkages. + for( int j=1; j<fun.nIns(); j++ ) + if( fun.in(j) instanceof CallNode call ) + call.unlink(fun,j--); + } + } + } + + // As part of building a CallGraph, when opto finds a function ptr flowing + // into a Call, link the Call and Fun. + private static void linkCG(CodeGen code, TypeFunPtr tfp, CallNode call) { + if( tfp.nargs() != call.nargs() ) return; // Error calls hit this + int[] fidxs = tfp.fidxs(); + for( int fidx = XInt.next(fidxs,0); fidx >=0; fidx = XInt.next(fidxs,fidx) ) { + // unlinkStart deliberately leaves uncalled functions in a + // temporarily dead limbo. A newly discovered call revives them; + // do not use the cleanup lookup which removes dead linker entries. + FunNode fun = code.lookupFun(fidx); + // null here means an external function; i.e. this Call + // calls to an outside library and all its arguments escape. + if( fun != null && !call.linked(fun) ) { + call.link( fun ); + code._iter.add(fun); + code._iter.addAll(fun._outputs); + } + } + } + + // Start added some FIDXs, the same functions now can be reached from start + // and called by anybody. Add the CG edges. + private static void linkStart( CodeGen code, TypeTuple tt ) { + TypeMem tmem = (TypeMem)tt._types[1]; + int[] fidxs = tmem._escFs; + if( XInt.isHigh(fidxs) ) { + // A scalar BOTTOM can flow into memory while compiling an error + // program. It exposes all public functions, not private fields. + for( FunNode fun : code._linker ) + if( fun != null && code.publicFIDX(fun.sig().fidx()) ) + linkStart(code,fun); + return; + } + for( int fidx = XInt.next(fidxs,0); fidx >=0; fidx = XInt.next(fidxs,fidx) ) { + FunNode fun = code._linker.atX(fidx); + if( fun==null ) assert code._externFunc.containsKey(fidx); + else linkStart(code,fun); + } + } + + // Function is now reachable by unknown callers through Start. The + // default inputs must therefore use conservative declared types. + private static void linkStart( CodeGen code, FunNode fun ) { + assert !fun.isDead(); + StartCUNode start = fun._compunit._start; + if( fun.nIns() < 2 || fun.in(1) != start ) { + assert !start.isDead(); + keepInCompUnit(code,fun); + // Function is reachable by any *remote* caller who gets the pointer! + fun.insertDef(1,start); + code._iter.add(fun); + for( Node p : fun._outputs ) + if( p instanceof ParmNode parm ) { + Node defalt = parm._idx==1 ? start.proj(1) : ConstantNode.seed(parm._declaredType).peephole(); + parm.insertDef(1,defalt); + code.add(parm); + } + } + } + + // Keep a surviving function rooted in its compilation unit without + // claiming that it has unknown callers. + private static void keepInCompUnit(CodeGen code, FunNode fun) { + StopCUNode stop = fun._compunit._stop; + if( stop._inputs.find(fun.ret()) == -1 ) + code.add(stop).addDef(fun.ret()); + } + + + // The core SCCP algorithm + private static void sccp( CodeGen code, Ary<Type> oldTypes ) { + // Iter workList is empty at this point + // OPTIMISTIC PASS + Node n; + int count = 0; // Debug counter + while( (n = code._iter._work.pop()) != null ) { + if( n.isDead() ) continue; + count++; + Type oval = n._type, nval = n.compute(); + if( oval == nval ) continue; + assert oval.isa(nval) : "Non-monotonic SCCP: "+n.getClass().getSimpleName()+"#"+n._nid+" "+n+" old="+oval+" new="+nval; // Types start high and always fall + Type pesiVal = oldTypes.atX(n._nid); + assert pesiVal==null || nval.isa(pesiVal) : "SCCP below pessimistic type: "+n.getClass().getSimpleName()+"#"+n._nid+" "+n+" pessimistic="+pesiVal+" new="+nval; // Never fall worse than the pessimistic pass + n._type = nval; + + // Now we have a series of stanzas where we lazily create the Call + // Graph - adding graph edges between every call site and called + // function, including external (unknown) callers. These edges are + // not in from the start because they are *too many*; we would get + // O(n^2) edges. So all along we have been treating them as + // "virtual" CFG edges - a FunNode taking a Start input is treated + // as if every call on the planet might call it. Now we make these + // CG edges *concrete*, adding them back as we discover that a call + // calls a particular function. + + // If a TFP adds a new function input to a call, link to the new + // Fun. This adds new edges to the graph allowing argument values + // to flow from calls into functions. The added edges are + // effectively a refined Call Graph. + if( nval instanceof TypeFunPtr tfp ) { + for( Node use : n._outputs ) + if( !use._type.isHigh() && use instanceof CallNode call && call.fptr() == n ) + linkCG(code,tfp,call); + } + + // Link a Call which becomes alive. Like above, adds new Call + // Graph edges to the graph. + if( n instanceof CallNode call && oval.isHigh() && !nval.isHigh() && call.fptr()._type instanceof TypeFunPtr ) + linkCG(code,call.tfp(),call); + + // If an otherwise-dead function pointer escapes, any future linked + // caller might find and call it. Force the function to be alive + // and called by Start. + if( n instanceof StartNode && n._type instanceof TypeTuple tt ) + linkStart(code,tt); + + + // Since n._type changed, visit all output neighbors + code._iter.addAll(n._outputs); + n.moveDepsToWorklist(code._iter); + // Quadratic (expensive) small-step assert + assert worklistCheck(code); + } + } + + // Quadratic (expensive) fixed-point assert + public static boolean fixedPointCheck(CodeGen code) { + return fixedPointCheck(code,code._stop); + } + public static boolean fixedPointCheck(CodeGen code, Node root) { + root.walk( x -> { + Type xval = x.compute(); + assert xval == x._type; + return null; + }); + return true; + } + + // Quadratic (expensive) small-step assert for SCCP, where changed nodes + // may already be on the worklist. + private static boolean worklistCheck(CodeGen code) { + code._midAssert = true; + Node root = code._start; + root.walk( x -> { + Type xval = x.compute(); + assert xval == x._type || (x._type.isa( xval ) && code._iter._work.on( x )) + : "Non-monotonic or missed SCCP work: "+x.getClass().getSimpleName()+"#"+x._nid+" "+x+" old="+x._type+" new="+xval; + return null; + }); + code._midAssert = false; + return true; + } + + // After SCCP add everything changed to the worklist and see if peepholes + // can make more progress. + private static void moveChangesToWorklist(CodeGen code, Ary<Type> oldTypes) { + code._start.walk( x -> { + assert x.compute() == x._type; // Hit the fixed point + assert x._nid >= oldTypes._len || x._type.isa(oldTypes.at(x._nid)); // Hit at least the bottom-up type + code.add(x); + return null; + }); + } + + // Resolve arithmetic modes which are ambiguous only because they belong to + // a recursive component. Float wins because integer operands can be + // converted to float. + private static void resolveNumericModes(CodeGen code) { + final BitSet visited = new BitSet(); + code._stop.walk(n -> { + if( n instanceof ModeNode modeNode && modeNode.mode()==0 ) { + visited.clear(); + visited.set(n._nid); // Inspect inputs, not Bool's integer result + int mode = 0; + for( int i=1; i<n.nIns(); i++ ) + mode |= numericEvidence(n.in(i),visited); + if( mode != 0 ) { + // If both evidence, FP wins + modeNode.setMode((byte)((mode&2)==2 ? 2 : 1)).init(); + code.add(n); + } + } + return null; + }); + } + + // Gather numeric evidence through Phi and interprocedural return cycles. + // The visited set makes self- and mutually-recursive calls finite. + private static int numericEvidence(Node n, BitSet visited) { + if( n==null || visited.get(n._nid) ) return 0; + visited.set(n._nid); + + Type t = n._type; + if( t instanceof TypeFloat ) return 2; + if( t instanceof TypeInteger ) return 1; + if( t!=Type.BOTTOM && !(t instanceof TypeTuple) ) + return 0; // Something unrelated, like a ptr + + int evidence = 0; + switch( n ) { + case ModeNode modeNode -> evidence = modeNode.mode(); + case PhiNode phi -> {} + case ReturnNode ret -> {} + case ProjNode proj when proj._idx==2 && proj.in(0) instanceof CallEndNode cend -> { + for( int i=1; i<cend.nIns(); i++ ) + evidence |= numericEvidence(cend.in(i),visited); + } + case ConvertNode convert -> { + Type dst = convert.dst(); + evidence = dst instanceof TypeFloat ? 2 : + dst instanceof TypeInteger ? 1 : 0; + } + default -> { return 0; } + } + for( int i=1; i<n.nIns(); i++ ) + evidence |= numericEvidence(n.in(i),visited); + + return evidence; + } + + + // Freeze field sizes; do struct layouts; convert field offsets into + // constants. + private static void fieldSizes( CodeGen code ) { + for( int i=0; i<code._start.nOuts(); i++ ) { + Node use = code._start.out(i); + if( use instanceof ConFldOffNode off ) { + Node con = off.asOffset(); + if( con != null ) { // Can be null for trying to reference missing field + off.subsume( con ); + i--; + } + } + } + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java new file mode 100644 index 00000000..191016be --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java @@ -0,0 +1,379 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.IterPeeps; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.*; +import java.io.File; +import java.io.IOException; +import java.util.*; + +// Parse a given file, and all its dependencies +public abstract class ParseAll { + + // Worklist of symbols to search and files to parse + private static final Ary<CompUnit> WORK = new Ary<>(CompUnit.class); + + private static final Ary<CompUnit> NEEDS_LOAD = new Ary<>(CompUnit.class); + + // Return a compilation unit (cached or new). If new, it does not necessarily + // need to be compiled. + public static CompUnit makeCUnit( CodeGen code, CompUnit par, String name ) throws IOException { + return makeCUnit(code,par,name,null,false); + } + + private static CompUnit makeCUnit( CodeGen code, CompUnit par, String name, File obj ) throws IOException { + return makeCUnit(code,par,name,obj,obj!=null); + } + + private static CompUnit makeCUnit( CodeGen code, CompUnit par, String name, File obj, boolean external ) throws IOException { + // Slashed file name? Move leading parts into parent CUs. + int idx = name.indexOf("/"); + if( idx >= 0 ) { + String head = name.substring(0,idx); + CompUnit sub = makeCUnit(code,par,head,external ? externalObj(code,par,head) : null,external); + return makeCUnit(code,sub,name.substring(idx+1).intern(),obj,external); + } + + String fname = par==null ? name : (par._fname + "/" + name).intern(); + CompUnit cunit = code._compunits.get(fname); + if( cunit != null ) return cunit; + String cname = par==null ? name : (par._cname + "." + name).intern(); + code._compunits.put(fname, cunit = external ? new CompUnit(obj,par,fname,cname,name) : new CompUnit(par,fname,cname,name)); + return cunit; + } + + // Parse a top-level string, used for e.g. testing + static void parseSource( CodeGen code, String src ) { + parseSource(code,"Test",src); + } + + // Parse a top-level string with an explicit compilation-unit name. + static void parseSource( CodeGen code, String fname, String src ) { + CompUnit cunit = new CompUnit(fname,src); + code._compunits.put(cunit._fname,cunit); + parseAll(code,cunit); + } + + // module_root/.../src.smp is source file name. + static void parsePath(CodeGen code, String fname) { + try { + // Split and build the parent-path down to the source file. + CompUnit cu = makeCUnit( code, null, fname ); + // Compile this CompUnit + parseAll(code,cu); + } catch( IOException ioe ) { + throw new RuntimeException(ioe); + } + } + + private static void parseAll(CodeGen code, CompUnit cunit) { + WORK.clear(); // Cleanup from prior tests + NEEDS_LOAD.clear(); + // Prime the pump with the first CompUnit + WORK.add(cunit); + // Work through all unparsed compilation units and parse + while( !WORK.isEmpty() ) { + CompUnit cu = WORK.remove(0); + // No source code? Means we do not need to parse, but instead need to load the symbols as-if we parsed + if( cu._src==null || cu._src.isEmpty() ) + loadDeps(code,cu); // Load recursive dependents + else + parseOne( code, cu ); // Parse + } + + // Load all pre-compiled CompUnits + for( CompUnit cu : NEEDS_LOAD ) + loadCompUnit(code,cu); + if( !NEEDS_LOAD.isEmpty() ) + code._stop.walk(n -> { + if( n instanceof CallNode ) + code.add(n); + return null; + }); + + // Many types will have been declared cyclically either internally or + // across multiple compilation units. + + // Close over all recursive types, and upgrade TYPES + Ary<TypeStruct> ary = new Ary<>(TypeStruct.class); + for( Type t : Parser.TYPES.values() ) + if( t instanceof TypeStruct ts ) + ary.add(ts); + TypeStruct[] tss = Type.closeOver(ary.asAry(),Parser.TYPES); + for( TypeStruct ts : tss ) + Parser.TYPES.put(ts._name,ts); + for( CompUnit cu : code._compunits.values() ) + if( cu._ext==null ) + cu._clz = (TypeStruct)Parser.TYPES.get(Parser.addClzPrefix(cu._cname)); + + // Walk over all Nodes, and upgrade the internal constants to the + // closed-over types. + code._stop.walk( (Node n) -> { + n.upgradeType(Parser.TYPES); + if( n instanceof FRefNode fref ) { + TypeStruct clz = (TypeStruct)Parser.TYPES.get(fref._name); + if( clz == null ) + return null; + // Class pointer: + TypeMemPtr clzptr = TypeMemPtr.make((byte)2,clz,true); + // Class pointer constant: + ConstantNode clzCon = code.con(clzptr); + // Replace and optimize + fref.addDef(clzCon); + code.add(fref); + } + // Adds sort by NIDs, which just got shuffled + if( n instanceof AddNode add ) + code.add(add); + return null; + } ); + + // The complete public interface is now known. Keep this independent + // of later graph/linker liveness so SCCP computations remain local and + // monotonic. + code.freezePublicInterface(); + } + + // Parse one Simple source code file. Add all the FRefs produced to the worklist. + private static void parseOne( CodeGen code, CompUnit cunit ) { + cunit.setStart(code); + // Parse a source file, return missing external references + Ary<FRefNode> frefs = code.P.parse(cunit); + + // Find all missing external references, or complain. This can trigger more parses. + while( !frefs.isEmpty() ) { + FRefNode fref = frefs.remove(0); + // Symbol not found in source code (or we would not be here). + // Search the module for the fref + try { + CompUnit xcunit = findCUnitModule(code,cunit,fref._name); + if( xcunit == null ) + xcunit = findCUnitExternalSimple(code,fref._name); + if( xcunit != null ) { + // Replace the FRef with the discovered class name. + fref._name = Parser.addClzPrefix(xcunit._cname); + cunit.addDep(xcunit); + } else { + throw new RuntimeException("Undefined name '" + fref._name +"'"); + } + } catch( IOException ioe ) { + throw new RuntimeException(ioe); + } + } + addRequiredTypes(code,cunit); + } + + private static void addRequiredTypes(CodeGen code, CompUnit cunit) { + LinkedHashSet<String> required = new LinkedHashSet<>(); + code._stop.walk(n -> { + TypeStruct ts = CodeGen.requiredStruct(n); + if( ts!=null && ts._fref ) required.add(ts._name); + return null; + }); + for( String typeName : required ) { + try { + CompUnit xcunit = findRequiredType(code,cunit,typeName); + if( xcunit != null ) + cunit.addDep(xcunit); + } catch( IOException ioe ) { + throw new RuntimeException(ioe); + } + } + } + + private static CompUnit findRequiredType(CodeGen code, CompUnit cunit, String typeName) throws IOException { + for( CompUnit cu = cunit; cu != null; cu = cu._par ) { + String symbol = typeName.startsWith(cu._cname+".") + ? typeName.substring(cu._cname.length()+1) + : typeName; + CompUnit xcunit = findCUnitModule(code,cu,symbol); + if( xcunit != null ) + return xcunit; + } + return findCUnitExternalSimple(code,typeName); + } + + // Load the public symbols for this CompUnit, and add dependent CompUnits to the NEEDS_LOAD list. + private static void loadDeps(CodeGen code, CompUnit cunit) { + // Load IR after parsing + if( NEEDS_LOAD.find(cunit) != -1 ) + return; // Already on the to-be-loaded list? + try { + NEEDS_LOAD.add(cunit); + // Load dependent classes + ElfReader elf = ElfReader.load(cunit._obj,cunit); + elf.loadPublicSymbols(); + for( String fname : elf._deps ) { + // Check CompUnit for being up to date + CompUnit sub = makeCUnit(code,null,fname,null/*depObj(code,cunit,fname)*/); + // If not seen before or out of date, parse the new nested cunit + if( sub._clz == null && sub._par == null ) + WORK.add(sub); + } + } catch( IOException ioe ) { + throw new RuntimeException(ioe); + } + } + + private static File depObj(CodeGen code, CompUnit cunit, String fname) { + if( cunit._smp != null ) + return null; + ElfReader elf = code.findExternalSimple(Parser.addClzPrefix(fname.replace('/','.'))); + if( elf == null ) + throw new RuntimeException("Cannot find external Simple dependency '"+fname+"'"); + return elf._file; + } + + private static File externalObj(CodeGen code, CompUnit par, String name) { + String cname = par==null ? name : par._cname + "." + name; + ElfReader elf = code.findExternalSimple(Parser.addClzPrefix(cname)); + return elf == null ? null : elf._file; + } + + private static void loadCompUnit(CodeGen code, CompUnit cunit) { + // Expecting a previously compiled object file + assert cunit._obj != null; + // Extract published symbols and put them in the internal tables; + // cross-references will pick them up there. + ElfReader elf = ElfReader.load(cunit._obj,cunit); + cunit._clz = elf.loadSimple(code); + StartNode lstart = (StartNode)elf._nodes.at(0); + StopNode lstop = ( StopNode)elf._nodes.last(); + + // Add all new loaded functions to the linker table + for( Node n : lstop._inputs ) { + StopCUNode stop = (StopCUNode)n; + StartCUNode start = stop.start(); + CompUnit cu = code._compunits.get(start._fname); + cu._start = start; + cu._stop = stop ; + + for( Node ret : stop._inputs ) { + FunNode fun = ((ReturnNode)ret).fun(); + code.link(fun); + } + } + + // Hook the new code into the existing code graph + for( Node lstopcu : lstop._inputs ) + code._stop.addDef(lstopcu); + lstart.subsume(code._start); + + // Add the new top-level loaded class + Parser.TYPES.put(cunit._clz._name,cunit._clz); + Field inst = cunit._clz.field(cunit._name); + if( inst != null ) // Also the instance type, if a constructor exists + Parser.TYPES.put(cunit._cname, ((TypeMem)((TypeFunPtr)inst._t)._ret)._t); + } + + // Symbol not found in source code (or we would not be here). + // Search the module for the fref + public static CompUnit findCompUnitOrThrow(CodeGen code, CompUnit cu, String symbol) { + CompUnit cu2 = findCompUnit(code,cu,symbol); + if( cu2 != null ) return cu2; + throw new RuntimeException("Undefined name '" + symbol +"'"); + } + + public static CompUnit findCompUnit(CodeGen code, CompUnit cu, String symbol) { + // Symbol not found in source code (or we would not be here). + // Search the module for the fref + try { + CompUnit local = findCUnitModule(code,cu,symbol); + if( local != null ) + return local; + return findCUnitExternalSimple(code,symbol); + } catch( IOException ioe ) { + throw new RuntimeException(ioe); + } + } + + // Find the symbol in module, starting from cunit. This searches up the nested + // class chain and sideways 1 step, so that any prefix of the class chain can + // be skipped as a shortcut. Symbol can have dotted names, which then go + // down into classes. e.g. + // A/X/Y exists. + // A/B contains X.Y, which is a shortcut for A.X.Y + + // However if A/B contains "Y", the search would be for A.B.Y then A.Y both of + // which do not exist. + private static CompUnit findCUnitModule( CodeGen code, CompUnit cunit, String symbol ) throws IOException { + if( cunit == null ) // Ran off top of module scope? + return null; // Failed search + + // Check self + if( cunit._name.equals(symbol) ) + return cunit; + + // If symbol is itself nested (has embedded dots), move the leading part + // over to the CompUnit path. So e.g.: + // CompUnit{A.B}, Symbol="X.Y" + // becomes + // CompUnit{A.B.X}, Symbol="Y" + int dotIdx = symbol.indexOf("."); + if( dotIdx != -1 ) { + String pre = symbol.substring(0,dotIdx); + String rest = symbol.substring(dotIdx+1 ); + String fname = cunit._fname + "/" + pre; + File f = new File( code._modDir + "/" + fname + ".smp" ); + if( f.exists() ) { + CompUnit sub = makeCUnit(code,cunit,pre); + // Note that "sub" itself is not put on the to-be-parsed-list. We're + // referring to a child class and do not need to parse the middles. + CompUnit dcunit = findCUnitModule(code,sub,rest); + if( dcunit != null ) + return dcunit; // Nested dot search found cunit + } + } + + // Look in mod / fname / name / symbol.smp + String fname = cunit._fname + "/" + symbol; + File f = new File( code._modDir + "/" + fname + ".smp" ); + if( f.exists() ) { + CompUnit sub = makeCUnit(code,cunit,symbol); + // If not seen before or out of date, parse the new nested cunit + if( sub._clz == null && WORK.find(sub) == -1 ) + WORK.add(sub); + return sub; + } + + // Look up the scope + return findCUnitModule( code, cunit._par, symbol ); + } + + // Search external Simple objects only. C linkage uses explicit "C" + // declarations, which produce ExternNodes and are resolved by the linker. + public static CompUnit findCUnitExternalSimple( CodeGen code, String symbol ) { + String clzName = symbol.startsWith(Parser.CLZ) ? symbol : Parser.addClzPrefix(symbol); + String cname = clzName.substring(Parser.CLZ.length()); + String fname = cname.replace('.','/'); + + CompUnit cunit = code._compunits.get(fname); + if( cunit != null ) + return cunit; + + ElfReader elf = code.findExternalSimple(clzName); + if( elf == null ) + return null; + + try { + cunit = makeCUnit(code,null,fname,elf._file); + } catch( IOException ioe ) { + throw new RuntimeException(ioe); + } + ElfReader typedElf = ElfReader.load(cunit._obj,cunit); + TypeStruct clz = typedElf == null ? null : typedElf.loadPublicTypes(code); + if( clz != null ) + Parser.TYPES.put(clz._name,clz); + else + clz = (TypeStruct)Parser.TYPES.get(clzName); + if( clz == null ) + Parser.TYPES.put(clzName,clz = TypeStruct.make(clzName,true)); + cunit._clz = clz; + if( WORK.find(cunit) == -1 && NEEDS_LOAD.find(cunit) == -1 ) + WORK.add(cunit); + return cunit; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java new file mode 100644 index 00000000..48319006 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java @@ -0,0 +1,14 @@ +package com.seaofnodes.simple.codegen; + +// This Node rip-relative encodings has sizes that vary by delta +public interface RIPRelSize { + // delta is the distance from the opcode *start* to the target start. Each + // hardware target adjusts as needed. X86 measures from the opcode *end* + // and small jumps are 2 bytes, so they'll need to subtract 2. + byte encSize(int delta); + + // Patch full encoding in. Used for both short and long forms. delta + // again is from the opcode *start*. + void patch( Encoding enc, int opStart, int opLen, int delta ); + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java new file mode 100644 index 00000000..4c00391c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -0,0 +1,646 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import com.seaofnodes.simple.node.*; +import java.util.Arrays; +import java.util.BitSet; +import java.util.IdentityHashMap; + +/** + * "Briggs/Chaitin/Click". + * Graph coloring. + * <p> + * Every def and every use have a bit-set of allowed registers. + * Fully general to bizarre chips. + * Multiple outputs fully supported, e.g. add-with-carry or combined div/rem or "xor rax,rax" setting both rax and flags. + * <p> + * Intel 2-address accumulator-style ops fully supported. + * Op-to-stack-spill supported. + * All addressing modes supported. + * Register pairs could be supported with some grief. + * <p> + * Splitting instead of spilling (simpler implementation). + * Stack slots are "just another register" (tiny stack frames, simpler implementation). + * Stack/unstack during coloring with a marvelous trick (simpler implementation). + * <p> + * Both bitset and adjacency list formats for the interference graph; one of + * the few times it's faster to change data structures mid-flight rather than + * just wrap one of the two. + * <p> + * Liveness computation and interference graph built in the same one pass (one + * fewer passes per round of coloring). + * <p> + * Single-register def or use live ranges deny neighbors their required + * register and thus do not interfere, vs interfering and denying the color + * and coloring time. Basically all function calls do this, but many oddball + * registers also, e.g. older div/mod/mul ops. (5x smaller IFG, the only + * O(n^2) part of this operation). + */ + +public class RegAlloc { + // Main Coloring Algorithm: + // Repeat until colored: + // Build Live Ranges (LRGs) + // - Intersect allowed registers + // If hard conflicts (LRGs with no allowed registers) + // - Pre-split conflicted LRGs, and repeat + // Build Interference Graph (IFG) + // - Self conflicts split now + // Color (remove trivial first then any until empty; reverse assign colors + // - If color fails: + // - - Split uncolorable LRGs + + // Top-level program graph structure + final CodeGen _code; + + public int _spills, _spillScaled; + + private boolean _done; + + // ----------------------- + // Live ranges with self-conflicts or no allowed registers + private final IdentityHashMap<LRG,String> _failed = new IdentityHashMap<>(); + void fail( LRG lrg ) { + assert lrg.leader(); + _failed.put(lrg,""); + } + boolean success() { return _failed.isEmpty(); } + public boolean done() { return _done; } + + + // ----------------------- + // Map from Nodes to Live Ranges + private final IdentityHashMap<Node,LRG> _lrgs = new IdentityHashMap<>(); + short _lrg_num; + + // Define a new LRG, and assign n + LRG newLRG( Node n, FunNode fun ) { + LRG lrg = lrg(n); + if( lrg!=null ) return lrg; + lrg = new LRG(_lrg_num++, fun); + LRG old = _lrgs.put(n,lrg); assert old==null; + return lrg; + } + + // LRG for n + public LRG lrg( Node n ) { + LRG lrg = _lrgs.get(n); + if( lrg==null ) return null; + LRG lrg2 = lrg.find(); + if( lrg != lrg2 ) + _lrgs.put(n,lrg2); + return lrg2; + } + + // Find LRG for n.in(idx), and also map n to it + LRG lrg2( Node n, int idx ) { + LRG lrg = lrg(n.in(idx)); + return union(lrg,n); + } + + // Union any lrg for n with lrg and map to the union + LRG union( LRG lrg, Node n ) { + LRG lrgn = _lrgs.get(n); + LRG lrg3 = lrg.union(lrgn); + _lrgs.put(n,lrg3); + return lrg3; + } + + // Force all unified to roll up; collect live ranges + LRG[] _LRGS; + void unify() { + Ary<LRG> lrgs = new Ary<>(LRG.class); + for( Node n : _lrgs.keySet() ) { + LRG lrg = lrg(n); + lrgs.setX(lrg._lrg,lrg); + } + _LRGS = lrgs.asAry(); + // Remove unified lrgs from failed set also + for( LRG lrg : _failed.keySet().toArray(new LRG[0]) ) { + if( !lrg.leader() ) { + LRG lrg2 = lrg.find(); + _failed.remove(lrg); + _failed.put(lrg2,""); + } + } + } + + + public short regnum( Node n ) { + LRG lrg = lrg(n); + return lrg==null ? -1 : lrg._reg; + } + + // Printable register number for node n + String reg( Node n ) { + LRG lrg = lrg(n); + return lrg==null ? null : reg(lrg); + } + String reg( LRG lrg ) { + // No register yet, use LRG + if( lrg._reg == -1 ) return "V"+lrg._lrg; + // Chosen machine register unless stack-slot and past RA + String[] regs = _code._mach.regs(); + if( lrg._reg < regs.length || _code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() || lrg._fun==null ) + return RegMask.reg(regs,lrg._reg); + // Stack-slot past RA uses the frame layout logic + return "[rsp+"+lrg._fun.computeStackOffset(_code,lrg._reg)+"]"; + } + + // ----------------------- + RegAlloc( CodeGen code ) { _code = code; } + + public void regAlloc() { + // Insert callee-save registers + String[] regs = _code._mach.regs(); + long neverSave= _code._mach.neverSave(); + for( CFGNode bb : _code._cfg ) + if( bb instanceof FunNode fun ) { + ReturnNode ret = fun.ret(); + int len = Math.min(regs.length,64); + for( int reg=0; reg<len; reg++ ) + if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) { + ret.addDef(new CalleeSaveNode(fun,reg,regs[reg])); + assert ret.regmap(ret.nIns()-1).firstReg()==reg; + } + } + // Cache reg masks for New and Call + for( CFGNode bb : _code._cfg ) { + if( bb instanceof CallEndMach cend ) cend.cacheRegs(_code); + for( Node n : bb._outputs ) + if( n instanceof NewNode nnn ) nnn.cacheRegs(_code); + } + + // Top driver: repeated rounds of coloring and splitting. + byte round=0; + while( !graphColor(round) ) { + split(round); + if( round >= 7 ) // Really expect to be done soon + throw Utils.TODO("Allocator taking too long"); + round++; + } + postColor(); // Remove no-op spills + _done = true; + } + + private boolean graphColor(byte round) { + _failed.clear(); + _lrgs.clear(); + _lrg_num = 1; + _LRGS=null; + + return + // Build Live Ranges + BuildLRG.run(round,this) && // if no hard register conflicts + // Build Interference Graph + IFG.build(round,this) && // If no self conflicts or uncolorable + // Conservative coalesce copies + Coalesce.coalesce(round,this) && + // Color attempt + IFG.color(round,this); // If colorable + } + + // ----------------------- + // Split conflicted live ranges. + void split(byte round) { + + // In C2, all splits are handling in one pass over the program. Here, + // in the name of clarity, we'll handle each failing live range + // independently... which generally requires a full pass over the + // program for each failing live range. i.e., might be a lot of + // passes. + + // Sort, to avoid non-deterministic HashMap ordering + LRG[] splits = _failed.keySet().toArray(new LRG[0]); + Arrays.sort(splits, (x,y) -> x._lrg - y._lrg ); + for( LRG lrg : splits ) + split(round,lrg); + } + + // Split this live range, top level heuristic + boolean split( byte round, LRG lrg ) { + assert lrg.leader(); // Already rolled up + + if( lrg._selfConflicts != null ) + return splitSelfConflict(round,lrg); + + // Register mask when empty; split around defs and uses with limited + // register masks. + if( lrg._mask.isEmpty() && (!lrg._multiDef || lrg._1regUseCnt==1) ) { + if( lrg._1regDefCnt <= 1 && + lrg._1regUseCnt <= 1 && + (lrg._1regDefCnt + lrg._1regUseCnt) > 0 ) + return splitEmptyMaskSimple(round,lrg); + // Repeated single-reg uses from a single def. Special for archs + // with more fixed regs. + if( !lrg._multiDef && lrg._1regDefCnt <= 1 && lrg._1regUseCnt > 2 ) + if( splitEmptyMaskByUse(round,lrg) ) + return true; + } + + // Generic split-by-loop depth. + return splitByLoop(round,lrg); + } + + // Split live range with an empty mask. Specifically forces splits at + // single-register defs or uses and not elsewhere. + boolean splitEmptyMaskSimple( byte round, LRG lrg ) { + // Live range has a single-def single-register, and/or a single-use + // single-register. Split after the def and before the use. Does not + // require a full pass. + + // Compute def nOuts *before* replacing those uses with a split + int defNouts = ((Node)lrg._machDef).nOuts(); + // Split just after def + if( lrg._1regDefCnt==1 && !lrg._machDef.isClone() ) + // Force must-split, even if a prior split same block because register + // conflicts. Example: + // alloc + // V1/rax - forced by alloc + // alloc + // V2/rax - kills prior RAX + // st4 [V1],len - No good, must split around + insertAfterAndReplace( makeSplit("def/empty1",round,lrg), (Node)lrg._machDef, false/*true*/); + // Split just before use + if( lrg._machUse!=null && (lrg._1regUseCnt==1 || (lrg._1regDefCnt==1 && defNouts==1)) ) + insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg); + return true; + } + + // Single-def live range with an empty mask. There are many single-reg + // uses. Theory is there's many repeats of the same reg amongst the uses. + // In of splitting once per use, start by splitting into groups based on + // required input register. + boolean splitEmptyMaskByUse( byte round, LRG lrg ) { + Node def = (Node)lrg._machDef; + + // Look at each use, and break into non-overlapping register classes. + Ary<RegMask> rclass = new Ary<>(RegMask.class); + boolean done=false; + int ncalls=0; + while( !done ) { + done = true; + for( Node use : def._outputs ) + if( use instanceof MachNode mach ) { + if( mach instanceof CallNode ) ncalls++; + for( int i=1; i<use.nIns(); i++ ) + if( use.in(i)==def ) + done = putIntoRegClass( rclass, mach.regmap(i) ); + } + } + + // See how many register classes we split into. Generally not + // productive to split like this across calls, which are going to kill + // all registers anyways. + if( rclass._len <= 1 || ncalls > 1 ) return false; + + // Split by class + Ary<Node> ns = new Ary<>(Node.class); + for( RegMask rmask : rclass ) { + ns.addAll(def._outputs); + Node split = makeSplit(def,"popular",round,lrg,rmask); + split.insertAfter( def ); + if( split.nIns()>1 ) split.setDef(1,def); + // all uses by class to split + for( int j=0; j < def._outputs._len; j++ ) { + Node use = def._outputs.at(j); + if( use instanceof MachNode mach && use!=split ) { + // Check all use inputs for n, in case there's several + for( int i = 1; i < use.nIns(); i++ ) + // Find a def input, and check register class + if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) ) { + RegMask m = mach.regmap( i ); + if( m!=null && mach.regmap( i ).overlap( rmask ) ) + // Modify use to use the split version specialized to this rclass + { use.setDefOrdered( i, split ); j--; break; } + } + } + } + ns.clear(); + } + return true; + } + + + // Put use into a register class, perhaps adding a class or perhaps + // narrowing a class (and causing a repeat) + private static boolean putIntoRegClass( Ary<RegMask> rclass, RegMask rmask ) { + for( int i=0; i<rclass._len; i++ ) { + RegMask omask = rclass.at(i); + if( omask.and(rmask) == omask ) return true; // Within the same register class + if( omask.overlap(rmask) ) { + rclass.set(i,new RegMask(omask.copy().and(rmask))); + return false; // Need go again + } + } + // Add a new class, no need to go again + rclass.push(rmask); + return true; + } + + // Self conflicts require Phis (or two-address). + // Insert a split after every def. + boolean splitSelfConflict( byte round, LRG lrg ) { + // Sort conflict set, so we're deterministic + Node[] conflicts = lrg._selfConflicts.keySet().toArray(new Node[0]); + Arrays.sort(conflicts, (x,y) -> x._nid - y._nid ); + + // For all conflicts + for( Node def : conflicts ) { + assert lrg(def)==lrg; // Might be conflict use-side + // Split before each use that extends the live range; i.e. is a + // Phi or two-address + for( int i=0; i<def._outputs._len; i++ ) { + Node use = def.out(i); + if( (use instanceof PhiNode phi && + !(phi.region() instanceof LoopNode loop && phi.in(2)==def && def.cfg0().idepth() > loop.idepth() ) ) || + (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) ) + insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg ); + } + // Split after the Phi which extends the LRG. Split also before + // Phi slot 1 (and not all inputs), because Phis extend the live range. + // TODO: split before all inputs (except the last; at least 1 split here must be extra) + if( def instanceof PhiNode phi && !(def instanceof ParmNode) ) { + SplitNode split = makeSplit("def/self",round,lrg); + insertAfterAndReplace(split,def,false); + if( split.nOuts()==0 ) + split.killOrdered(); + insertBefore(phi,1,"use/self/phi",round,lrg); + } + // Split before two-address ops which extend the live range + if( def instanceof MachNode mach && mach.twoAddress()!= 0 ) + insertBefore(def,mach.twoAddress(),"use/self/two",round,lrg); + } + return true; + } + + + // Generic: split around the outermost loop with non-split def/uses. This + // covers both self-conflicts (once we split deep enough) and register + // pressure issues. + boolean splitByLoop( byte round, LRG lrg ) { + findAllLRG(lrg); + + // Find min loop depth for all non-split defs and uses. + long ld = (-1L<<32) | 9999; + for( Node n : _ns ) { + if( lrg(n)==lrg ) // This is a LRG def + ld = ldepth(ld,n,n.cfg0()); + // PhiNodes check all CFG inputs + if( n instanceof PhiNode phi ) { + for( int i=1; i<n.nIns(); i++ ) + ld = ldepth(ld, phi.in(i), phi.region().cfg(i)); + } else { + // Others check uses + for( int i=1; i<n.nIns(); i++ ) + if( lrgSame(n.in(i),lrg) ) // This is a LRG use + ld = ldepth(ld,n,n.cfg0()); + } + } + int min = (int)ld; + int max = (int)(ld>>32); + + + // If the minLoopDepth is less than the maxLoopDepth: for-all defs and + // uses, if at minLoopDepth or lower, split after def and before use. + for( Node n : _ns ) { + if( n instanceof SplitNode && min!=max ) continue; // Ignoring splits; since spilling need to split in a deeper loop + if( n.isDead() ) continue; // Some Cloneable went dead by other spill changes + // If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user, + // commute the inputs... which chops the LHS live ranges' upper bound to just the RHS. + if( n instanceof MachNode mach && lrg(n)==lrg && mach.twoAddress()==1 && mach.commutes() && n.in(2).nOuts()==1 ) + n.swap12(); + + if( lrg(n)==lrg && // This is a LRG def + // At loop boundary, or splitting in inner loop + (min==max || n.cfg0().loopDepth() <= min) ) { + // Cloneable constants will be cloned at uses, not after def + if( !(n instanceof MachNode mach && mach.isClone()) && + // Single user is already a split adjacent + !(n.nOuts()==1 && n.out(0) instanceof SplitNode split && sameBlockNoClobber(split) ) ) + // Split after def in min loop nest + insertAfterAndReplace( makeSplit("def/loop",round,lrg), n,true); + } + + // PhiNodes check all CFG inputs + if( n instanceof PhiNode phi && !(n instanceof ParmNode)) { + for( int i=1; i<n.nIns(); i++ ) + // No split in front of a split + if( !(n.in(i) instanceof SplitNode) && + // splitting in inner loop or at loop border + (min==max || phi.region().cfg(i).loopDepth() <= min) && + // and not around the backedge of a loop (bad place to force a split, hard to remove) + !(phi.region() instanceof LoopNode && i==2 && (phi.in(i) instanceof PhiNode pp && pp.region()==phi.region())) ) + // Split before phi-use in prior block + insertBefore(phi,i, "use/loop/phi",round,lrg); + + } else { + // Others check uses + for( int i=1; i<n.nIns(); i++ ) { + // This is a LRG use + // splitting in inner loop or at loop border + if( lrgSame( n.in( i ), lrg ) && + (min == max || (n.in(i) instanceof MachNode mach && mach.isClone()) || n.cfg0().loopDepth() <= min) ) + // Split before in this block + insertBefore( n, i, "use/loop/use", round,lrg, false ); + } + } + } + return true; + } + + private boolean lrgSame(Node x, LRG lrg) { + LRG xlrg = lrg(x); + while( xlrg==null && x instanceof SplitNode ) + xlrg=lrg(x = x.in(1)); + return xlrg==lrg; + } + + private static long ldepth( long ld, Node n, CFGNode cfg ) { + // Do not count splits + if( n instanceof SplitNode ) return ld; + // Collect min/max loop depth + int min = (int)ld; + int max = (int)(ld>>32); + int d = cfg.loopDepth(); + // if n will lower the min loop and is in the tail end of the loop + // header, splitting "around" the loop will not help. Treat n as being + // in the loop. + if( d < min ) { + if( cfg.uctrl() instanceof LoopNode loop && loop.entry()==cfg ) { + for( int i=cfg.nOuts()-2; i>=0; i-- ) { + Node out = cfg.out(i); + if( n==out ) + { d = loop.loopDepth(); break; } // Treat n as being "in the loop" + if( !((out instanceof MachNode mach && mach.isClone()) || out instanceof SplitNode ) ) + break; // Treat b as "normal", out of loop + } + } + } + + // lower min, raise max, and re-fold + min = Math.min(min,d); + max = Math.max(max,d); + return ((long)max<<32) | min; + } + + // Find all members of a live range, both defs and uses + private final Ary<Node> _ns = new Ary<>(Node.class); + void findAllLRG( LRG lrg ) { + _ns.clear(); + int wd = 0; + if( lrg._machDef != null ) _ns.push((Node)lrg._machDef); + if( lrg._machUse != null ) _ns.push((Node)lrg._machUse); + while( wd < _ns._len ) { + Node n = _ns.at(wd++); + if( lrg(n)!=lrg ) continue; + for( Node def : n._inputs ) + if( lrg(def)==lrg && _ns.find(def)== -1 ) + _ns.push(def); + for( Node use : n._outputs ) + if( _ns.find(use)== -1 ) + _ns.push(use); + } + for( Node n : _ns ) assert !n.isDead(); + } + + void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip) { + Node def = n.in(i); + // Effective block for use + CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0(); + // Use-side RegMask, if available + RegMask umask = n instanceof MachNode mach ? mach.regmap(i) : null; + // Def is a split ? + if( skip && def instanceof SplitNode ) { + boolean singleReg = umask!=null && umask.size1(); + // Same block, multiple registers, split is only used by n, + // assume this is good enough and do not split again. + if( cfg==def.cfg0() && def.nOuts()==1 && !singleReg ) + return; + } + makeSplit(def,kind,round,lrg,umask).insertBefore(n, i); + // Skip split-of-split same block + if( skip && def instanceof SplitNode && cfg==def.cfg0() ) + n.in(i).setDefOrdered(1,def.in(1)); + } + void insertBefore(Node n, int i, String kind, byte round, LRG lrg) { + insertBefore(n,i,kind,round,lrg,true); + } + + // Replace uses of `def` with `split`, and insert `split` immediately after + // `def` in the basic block. + public void insertAfterAndReplace( Node split, Node def, boolean must ) { + split.insertAfter(def); + if( split.nIns()>1 ) split.setDef(1,def); + for( int j=def.nOuts()-1; j>=0; j-- ) { + Node use = def.out(j); + if( use==split ) continue; // Skip self + // Can we avoid a split of a split? 'this' split is used by + // another split in the same block. + if( !must && use instanceof SplitNode split2 && sameBlockNoClobber(split2) ) + continue; + int idx = use._inputs.find(def); + use.setDefOrdered(idx,split); + if( j < def.nOuts() ) j++; + } + } + + private Node makeSplit( Node def, String kind, byte round, LRG lrg, RegMask umask ) { + // Clone simple constants if possible + Node split = def instanceof MachNode mach && mach.isClone() && (umask==null || mach.outregmap().overlap(umask)) + ? mach.copy() + : _code._mach.split(lrg,kind,round); + _lrgs.put(split,lrg); + return split; + } + private SplitNode makeSplit( String kind, byte round, LRG lrg ) { + SplitNode split = _code._mach.split(lrg,kind,round); + _lrgs.put(split,lrg); + return split; + } + + + // ----------------------- + // POST PASS: Remove empty spills that biased-coloring made + private void postColor() { + int maxReg = -1; + for( CFGNode bb : _code._cfg ) { // For all ops + if( bb instanceof FunNode ) + maxReg = -1; // Reset for new function + // Compute frame size, based on arguments and largest reg seen + if( bb instanceof ReturnNode ret ) + ret.fun().computeFrameAdjust(_code,maxReg); + // Raise frame size by max stack args passed, even if ignored + if( bb instanceof CallEndMach cend ) + maxReg = Math.max(maxReg,cend._xslot); + + for( int j=0; j<bb.nOuts(); j++ ) { + Node n = bb.out(j); + if( lrg(n)!=null ) + maxReg = Math.max(maxReg,lrg(n)._reg+1); + // Raise frame size by max stack args passed to New + if( n instanceof NewNode nnn ) + maxReg = Math.max(maxReg,nnn._xslot); + + if( !(n instanceof SplitNode ) ) continue; + int defreg = lrg(n )._reg; + int usereg = lrg(n.in(1))._reg; + // Attempt to bypass split + if( defreg != usereg && splitBypass(bb,j,n,defreg) ) + usereg = lrg(n.in(1))._reg; + + // Split has same reg? Useless! Can remove it! + if( defreg == usereg ) { + n.removeSplit(); + j--; + continue; + } + // Split is kept; count against split score + _spills++; + _spillScaled += (1<<bb.loopDepth()*3); + assert _spillScaled >= 0; + } + } + } + + private boolean splitBypass( CFGNode bb, int j, Node lo, int defreg ) { + // Attempt to bypass split + Node hi = lo.in(1); + while( true ) { + if( !(hi instanceof SplitNode && lo.cfg0() == hi.cfg0()) ) + return false; + if( lrg(hi.in(1))._reg==defreg ) + break; + hi = hi.in(1); + } + // Check no clobbers + for( int idx = j-1; bb.out(idx) != hi; idx++) { + Node n = bb.out(idx); + if( lrg(n)!=null && lrg(n)._reg == defreg ) + return false; // Clobbered + } + lo.setDefOrdered(1,hi.in(1)); + return true; + } + + + public boolean sameBlockNoClobber( SplitNode split ) { + Node def = split.in(1); + CFGNode cfg = def.cfg0(); + if( cfg != split.cfg0() ) return false; // Not same block + // Get multinode head + Node def0 = def instanceof ProjNode ? def.in(0) : def; + int defreg = lrg(def)._reg; + if( defreg == -1 ) defreg = lrg(def)._mask.firstReg(); + if( defreg == -1 ) return false; // no allowed registers -> clobbered + for( int idx = cfg._outputs.find(split) -1; idx >= 0; idx-- ) { + Node n = cfg.out(idx); + if( n==def0 ) return true; // No clobbers + if( lrg(n) == lrg(def) ) return false; // Self conflict + if( lrg(n)!=null && lrg(n)._reg == defreg ) + return false; // Clobbered + } + throw Utils.TODO(); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegMask.java new file mode 100644 index 00000000..81e47e8e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegMask.java @@ -0,0 +1,125 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +/** RegMask + * A "register mask" - 1 bit set for each allowed register. In addition, + * "stack slot" registers may be allowed, effectively making the set infinite. + * <p> + * For smaller and simpler machines it suffices to make such masks an i64 or + * i128 (64- or 128-bit integers), and this presentation is by far the better + * way to go... if all register allocations can fit in this bit limitation. + * The allocator will need bits for stack-based parameters and for splits + * which cannot get a register. For a 32-register machine like the X86, add 1 + * for flags - gives 33 registers. Using a Java `long` has 64 bits, leaving + * 31 for spills and stack passing. This is adequate for nearly all + * allocations; only the largest allocations will run this out. However, if + * we move to a chip with 64 registers we'll immediately run out, and need at + * least a 128 bit mask. Since you cannot *return* a 128 bit value directly + * in Java, Simple will pick up a `RegMask` class object. +*/ +public class RegMask { + + long _bits0, _bits1; + + private static final RegMask EMPTY = new RegMask(0L); + public static final RegMask FULL = new RegMask(-1L); + + public RegMask(int bit) { + if( bit < 64 ) _bits0 = 1L<<bit; + else _bits1 = 1L<<(bit-64); + } + public RegMask(long bits ) { _bits0 = bits; } + public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; } + public RegMask(RegMask r) { _bits0 = r._bits0; _bits1 = r._bits1; } + + // AND, with copy-on-write if changing + RegMask and( RegMask mask ) { + if( mask==null ) return this; + long bits0 = _bits0 & mask._bits0; + long bits1 = _bits1 & mask._bits1; + if( bits0==_bits0 && bits1==_bits1 ) return this; + if( bits0==mask._bits0 && bits1==mask._bits1 ) return mask; + if( bits0==0 && bits1==0 ) return EMPTY; + // Update-in-place a mutable mask, or make a defensive copy + return null; + } + RegMask sub( RegMask mask ) { + if( mask==null ) return this; + long bits0 = _bits0 & ~mask._bits0; + long bits1 = _bits1 & ~mask._bits1; + if( bits0==_bits0 && bits1==_bits1 ) return this; + if( bits0==mask._bits0 && bits1==mask._bits1 ) return mask; + if( bits0==0 && bits1==0 ) return EMPTY; + // Update-in-place a mutable mask, or make a defensive copy + return null; + } + + // Fails if bit is set, because this is immutable + public boolean clr( int reg ) { + if( reg < 64 ) return ((_bits0 >> reg)&1)==0; + if( reg < 128 ) return ((_bits1 >> reg)&1)==0; + return true; + } + + + public short firstReg() { + if( isEmpty() ) return -1; + return (short)(_bits0 != 0 + ? Long.numberOfTrailingZeros(_bits0) + : Long.numberOfTrailingZeros(_bits1)+64); + } + public short nextReg(short reg) { + if( reg<64 ) { + long bits0 = _bits0 >> (reg+1); + if( bits0!=0 ) + return (short)(reg+1+Long.numberOfTrailingZeros(bits0)); + reg=64; + } + long bits1 = _bits1 >> (reg-64+1); + if( bits1!=0 ) + return (short)(reg+1+Long.numberOfTrailingZeros(bits1)); + return -1; + } + + boolean isEmpty() { return _bits0==0 && _bits1==0; } + + public boolean test( int reg ) { + return ((reg<64 ? (_bits0 >> reg) : (_bits1 >> (reg-64))) & 1) != 0; + } + + // checks if the 2 masks have at least 1 bit in common + public boolean overlap( RegMask mask ) { return (_bits0 & mask._bits0)!=0 || (_bits1 & mask._bits1)!=0; } + + // Defensive writable copy + public RegMaskRW copy() { return new RegMaskRW( _bits0, _bits1 ); } + + // Has exactly 1 bit set + public boolean size1() { + return ((_bits0 & -_bits0)==_bits0 && _bits1==0) || + ((_bits1 & -_bits1)==_bits1 && _bits0==0); + } + + // Cardinality + public short size() { return (short)(Long.bitCount(_bits0)+Long.bitCount(_bits1)); } + + @Override public String toString() { return toString(new SB()).toString(); } + public SB toString(SB sb) { + Machine mach = CodeGen.CODE._mach; + if( _bits0==0 && _bits1==0 ) return sb.p("[]"); + sb.p("["); + String[] regs = mach.regs(); + for( int i=0; i<64; i++ ) + if( ((_bits0 >> i)&1) != 0 ) + sb.p(reg(regs,i)).p(","); + for( int i=0; i<64; i++ ) + if( ((_bits1 >> i)&1) != 0 ) + sb.p(reg(regs,i+64)).p(","); + return sb.unchar().p("]"); + } + + public static String reg(String[] regs, int i) { + return i<regs.length ? regs[i] : "[stk#"+i+"]"; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java new file mode 100644 index 00000000..1da9a3b1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.codegen; + +// Mutable regmask(writable) +public class RegMaskRW extends RegMask { + public RegMaskRW(long x, long y) { super(x,y); } + // clears bit at position r. Returns true if the mask is still not empty. + public boolean clr(int r) { + if( r < 64 ) _bits0 &= ~(1L<<(r )); + else _bits1 &= ~(1L<<(r-64)); + return _bits0!=0 || _bits1!=0; + } + // sets bit at position r. + public void set(int r) { + if( r < 64 ) _bits0 |= (1L<<(r )); + else _bits1 |= (1L<<(r-64)); + } + @Override RegMaskRW and( RegMask mask ) { + if( mask==null ) return this; + _bits0 &= mask._bits0; + _bits1 &= mask._bits1; + return this; + } + @Override RegMaskRW sub( RegMask mask ) { + if( mask==null ) return this; + _bits0 &= ~mask._bits0; + _bits1 &= ~mask._bits1; + return this; + } + public RegMaskRW or( RegMask mask ) { + if( mask!=null ) { + _bits0 |= mask._bits0; + _bits1 |= mask._bits1; + } + return this; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java new file mode 100644 index 00000000..8014bb2c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java @@ -0,0 +1,617 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.*; +import java.util.*; + +abstract public class Serialize { + + static void serialize( CodeGen code ) { + + // Get all Nodes in a sane order + Ary<Node> nodes = nodeOrder(code); + + // Array of dependent file names + HashSet<String> xdeps = new HashSet<>(); + for( CompUnit cu : code._compunits.values() ) { + if( cu._deps != null ) + for( CompUnit cudep : cu._deps ) + xdeps.add(cudep._fname); + } + String[] deps = xdeps.toArray(new String[xdeps.size()]); + + // Top-level clazz being reported + String topName = code._srcName == null ? "Test" : code._srcName; + CompUnit top = code._compunits.get(topName); + TypeStruct clz = top._clz; + + // Preserve which same-named structs are the source-level declarations. + // Optimized graph types can carry sharper field states (e.g. len==0), + // and cannot be used to reconstruct this mapping during loading. + TreeMap<String,TypeStruct> named = new TreeMap<>(); + for( Type type : Parser.TYPES.values() ) + if( type instanceof TypeStruct ts ) + named.put(ts._name,ts); + TypeStruct[] pubTypes = named.values().toArray(new TypeStruct[0]); + + // Compress into bytes + BAOS baos = write(nodes, clz, pubTypes, deps, code._aliases, code._fidxs, code._rpcs, code._externFunc); + + // --- Expensive bijection assert + if( false ) { + // Inflate into POJOs; renumbers everything + ElfReader elf = new ElfReader(new BAOS(baos.toByteArray())); + readAll(code,elf, code._aliases, code._fidxs, code._rpcs); + BAOS baos2 = write(elf._nodes,elf._clz,elf._pubTypes,elf._deps, code._aliases, code._fidxs, code._rpcs, code._externFunc); + + // Bi-jection + for( int i=0; i<baos.size(); i++ ) + assert baos.buf()[i]==baos2.buf()[i] : "bijection mismatch at "+i+": "+(baos.buf()[i]&0xFF)+" != "+(baos2.buf()[i]&0xFF); + assert baos.size()==baos2.size() : "bijection size mismatch: "+baos.size()+" != "+baos2.size(); + } + // --- Expensive bijection assert + + // Record serialized IR for later ELF writing + code._serial = baos; + } + + // -------------------------------------------------- + static BAOS write(Ary<Node> nodes, TypeStruct clz, TypeStruct[] pubTypes, String[] depobjs, GlobalBits aliases, GlobalBits fidxs, GlobalBits rpcs, HashMap<Integer,String> externFunc) { + // Initialize the mapping from bits/tags to types + Type.TAGOFFS(); + + BAOS baos = new BAOS(); + // A - Print a header + baos.write('C').write('0').write('D').write('E'); + + // Count unique Types + var types = new HashMap<Type,Integer>(); + // Count published types *first*, so can align with published strings + types.put(Type.TOP,0); + types.put( clz, types.size() ); + // Have to visit the innards specially, since they did not get the + // normal recursive visit when first touching a type, because the + // published types are packed early to align with their string names so + // lookup in ElfReader can be fast. + for( int i=0; i<clz.nkids(); i++ ) + clz.at(i).gather(types); + // Gather types from all sources + for( Node n : nodes ) { + n._type.gather(types); + if( n instanceof CallEndNode cend ) cend._rpc.gather(types); + if( n instanceof FunNode fun ) fun.sig().gather(types); + if( n instanceof ParmNode parm ) parm._declaredType.gather(types); + if( n instanceof TypeNode con ) con._con .gather(types); + } + // Only record canonical declarations already reachable from this + // object's graph. Pulling unrelated parser types into this closure + // can include open or partially built types from other units. + Ary<TypeStruct> writtenPubs = new Ary<>(TypeStruct.class); + for( TypeStruct ts : pubTypes ) + if( types.containsKey(ts) ) + writtenPubs.add(ts); + // Radix sort by Type ID# + Type[] atypes = new Type[types.size()]; + for( Type t : types.keySet() ) + atypes[types.get(t)] = t; + + // Count all unique Strings + var strs = new HashMap<String,Integer>(); + strs.put("",0); // Null string is always 0 + // Find and count *published* Strings first + gather(strs,clz._name); + // Find and count dependent object file string names + if( depobjs != null ) + for( String fname : depobjs ) + gather(strs,fname); + // Count strings from all types + for( Type t : atypes ) { + if( t instanceof Field fld ) gather(strs,fld._fname); + if( t instanceof TypeStruct ts ) gather(strs,ts . _name); + } + // Count strings used by local->global bit mappings. + aliases.gather(strs); + fidxs .gather(strs); + rpcs .gather(strs); + for( String extern : externFunc.values() ) + gather(strs,extern); + // Count strings from nodes + for( Node n : nodes ) + n.gather(strs); + // Radix sort by string ID# + String[] astrs = new String[strs.size()]; + for( String s : strs.keySet() ) + astrs[strs.get(s)] = s; + + // Check that published strings and types align + assert astrs[1] == ((TypeStruct)atypes[1])._name; + if( depobjs != null ) + for( int i=0; i<depobjs.length; i++ ) + assert astrs[1+i+1] == depobjs[i]; + + + // B - Write unique strings + baos.packed4(depobjs==null ? 0 : depobjs.length); + baos.packed4(strs.size()); + // Write in ID# order; the first depobjs.length of these are dependent strings + for( String s : astrs ) + baos.packedS(s); + + // B.2 - Write the local->global mappings + aliases.packed(baos,strs); + fidxs .packed(baos,strs); + rpcs .packed(baos,strs); + ArrayList<Integer> externFidxs = new ArrayList<>(externFunc.keySet()); + Collections.sort(externFidxs); + baos.packed2(externFidxs.size()); + for( int fidx : externFidxs ) { + baos.packed2(fidx); + baos.packed2(strs.get(externFunc.get(fidx))); + } + + // C - Write unique Types + baos.packed4(atypes.length); + baos.packed2(writtenPubs._len); + for( TypeStruct ts : writtenPubs ) + baos.packed4(types.get(ts)); + // Write Types in ID# order, no children + for( Type t : atypes ) + t.packed(baos,strs); + // Write Types in ID# order, only child IDs + for( Type t : atypes ) { + int nkids = t.nkids(); + for( int i=0; i<nkids; i++ ) + baos.packed4(types.get(t.at(i))); + } + + + // D - Write the nodes. Since this is expected to be the bulk of the + // data, we might want to explore various options. + + // How many nodes? + baos.packed4(nodes._len); + + // Radix sort by Node ID#, new compressed NIDs + IdentityHashMap<Node,Integer> anodes = new IdentityHashMap<>(); + for( Node n : nodes ) + anodes.put(n,anodes.size()+1); // +1 bias, reserve 0 for null + + // First cut: 1 byte opcode, optional per-node info, includes variable nIns + for( Node n : nodes ) { + baos.packed1(n.serialTag().ordinal()); + baos.packed2(types.get(n._type)); + n.packed(baos,strs,types,anodes); + } + // Write out the input indices packed. + for( Node n : nodes ) { + for( int j=0; j<n.nIns(); j++ ) { + Node def = n.in(j); + Integer idx = def==null ? Integer.valueOf(0) : anodes.get(def); + assert idx != null : "Missing serialized input for "+n+" input "+j+" def "+def; + baos.packed2(idx); + } + if( n instanceof FunNode fun ) + baos.packed2(anodes.get(fun.ret())); + } + + return baos; + } + public static <T> void gather( HashMap<T,Integer> strs, T s) { + if( s!=null && !strs.containsKey(s) ) + strs.put(s,strs.size()); + } + public static void gather( AryInt is, int s) { + if( is.atX(s)==0 ) { + int cnt=0; + for( int i=1; i<is._len; i++ ) + if( is._es[i] != 0 ) + cnt++; + is.setX(s,cnt+1); + } + } + + // Returned array of *public* strings only. + static void read_public_strs(ElfReader elf) { + BAOS bais = elf._bais; + Type.TAGOFFS(); + + // A - Read a header + if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='E' ) + throw new IllegalArgumentException("Missing magic word"); + + // B - Packed read of #strings, then strings + int ndependents = bais.packed4(); // Number of dependent object files + int nstrs = bais.packed4(); // Total number of strings + String[] strs = new String[nstrs]; + for( int i=0; i<nstrs; i++ ) + strs[i] = bais.packedS(); + elf._strs = strs; + + // Collect dependent file names + elf._deps = new String[ndependents]; + for( int i=0; i<ndependents; i++ ) + elf._deps[i] = strs[1+i+1]; + } + + // -------------------------------------------------- + record TypeRead(Type[] types, String[] strs, GlobalBits fileAliases, GlobalBits fileFidxs, GlobalBits fileRpcs) {} + + static TypeRead readTypes( CodeGen code, ElfReader elf, GlobalBits aliases, GlobalBits fidxs, GlobalBits rpcs ) { + BAOS bais = elf._bais; + // Initialize the mapping from bits/tags to types + Type.TAGOFFS(); + + // If not read the strings already, read them now + String[] strs = elf._strs; + if( strs == null ) { + // A - Read a header + if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != 'E' ) + throw new IllegalArgumentException( "Missing magic word" ); + + // B - Packed read of #strings, then strings + int ndependents = bais.packed4(); // Number of dependent object files + int nstrs = bais.packed4(); // Total number of strings + elf._strs = strs = new String[nstrs]; + for( int i = 0; i < nstrs; i++ ) + strs[i] = bais.packedS(); + + // Collect dependent file names + elf._deps = new String[ndependents]; + for( int i = 0; i < ndependents; i++ ) + elf._deps[i] = strs[1 + i + 1]; + } + + // B.2 - Read the local->global mappings. Further down we will map + // Elf file-local -> global, then global -> CodeGen.CODE local + GlobalBits fileAliases = GlobalBits.packed(bais,strs); + GlobalBits fileFidxs = GlobalBits.packed(bais,strs); + GlobalBits fileRpcs = GlobalBits.packed(bais,strs); + int nexterns = bais.packed2(); + for( int i=0; i<nexterns; i++ ) { + int fileFidx = bais.packed2(); + String extern = strs[bais.packed2()]; + int fidx = fileFidx < GlobalBits.RESERVED ? fileFidx : fidxs.map(fileFidxs,fileFidx); + String old = code.externFunc(fidx); + if( old == null ) + code.externFunc(fidx,extern); + else + assert old.equals(extern); + } + + + // C - Packed read of #types, then types + int ntypes = bais.packed4(); + int npubTypes = bais.packed2(); + int[] pubTypeIds = new int[npubTypes]; + for( int i=0; i<npubTypes; i++ ) + pubTypeIds[i] = bais.packed4(); + Type[] types = Type.packed(bais,strs,ntypes, Parser.TYPES, + fileAliases,aliases, + fileFidxs ,fidxs, + fileRpcs ,rpcs); + + // aliases.set(0,0); + // Check and collect first published symbols map to TypeStructs + elf._clz = (TypeStruct)types[1/*skip zero*/]; + assert elf._clz._name==strs[1/*skip null ptr*/]; + for( Type type : types ) + if( type instanceof TypeStruct ts ) + Parser.TYPES.put(ts._name,ts); + for( Type type : types ) { + if( !(type instanceof TypeStruct ts) || !Parser.startsClzPrefix(ts._name) ) + continue; + String cname = ts._name.substring(Parser.CLZ.length()); + int dot = cname.lastIndexOf('.'); + String name = dot == -1 ? cname : cname.substring(dot+1); + Field inst = ts.field(name.intern()); + if( inst != null && inst._t instanceof TypeFunPtr tfp && tfp._ret instanceof TypeMem mem && mem._t instanceof TypeStruct its ) + Parser.TYPES.put(cname,its); + } + elf._pubTypes = new TypeStruct[npubTypes]; + for( int i=0; i<npubTypes; i++ ) { + TypeStruct ts = elf._pubTypes[i] = (TypeStruct)types[pubTypeIds[i]]; + Parser.TYPES.put(ts._name,ts); + } + return new TypeRead(types,strs,fileAliases,fileFidxs,fileRpcs); + } + + static void readAll( CodeGen code, ElfReader elf, GlobalBits aliases, GlobalBits fidxs, GlobalBits rpcs ) { + BAOS bais = elf._bais; + TypeRead tread = readTypes(code, elf, aliases, fidxs, rpcs); + Type[] types = tread.types(); + String[] strs = tread.strs(); + GlobalBits fileAliases = tread.fileAliases(); + + // D - Read the nodes + + // Number of nodes + int num = bais.packed4(); + // Packed array of nodes + Ary<Node> nodes = new Ary<>(Node.class); + CompUnit cu = null; + for( int i=0; i<num; i++ ) { + Node.Tag tag = Node.Tag.VALS[bais.packed1()]; + Type t = types[bais.packed2()]; + Node n = tag.make(bais,strs,types,fileAliases,aliases); + n._type = t; + nodes.push(n); + // A little extra for FunNodes + if( n instanceof StartCUNode startcu ) { + cu = code._compunits.get(startcu._fname==null ? (code._srcName==null ? "Test" : code._srcName) : startcu._fname); + assert cu != null; + } + if( n instanceof StopCUNode stopcu ) { + cu = null; + } + if( n instanceof FunNode fun ) { + assert cu != null; + fun._compunit = cu; + } + if( n instanceof ExternNode ext && ext._con instanceof TypeFunPtr tfp && tfp.isConstant() ) { + String old = code.externFunc(tfp.fidx()); + if( old == null ) + code.externFunc(tfp.fidx(), ext._extern); + else + assert old.equals(ext._extern); + } + } + + // Node edges + for( Node n : nodes ) { + int len = n.nIns(); + for( int i=0; i<len; i++ ) { + int idx = bais.packed2(); + Node def = idx==0 ? null : nodes.at(idx-1); + // MemMerge precise aliases are encoded by their input slot. + // Remap populated file-local slots into this compilation's + // local alias numbering; null holes carry no identity. + int local = n instanceof MemMergeNode && i >= GlobalBits.RESERVED && def != null + ? aliases.map(fileAliases,i) + : i; + n.setDefX(local,def); + // Call's output[0] is always the CallEnd + if( n instanceof CallEndNode cend && i==0 ) { + var outs = cend.call()._outputs; + outs.swap(0,outs._len-1); + } + } + // A little extra for FunNodes + if( n instanceof FunNode fun ) { + // Return is hooked like an edge + ReturnNode ret = (ReturnNode)nodes.at(bais.packed2()-1); + fun.setRet(ret); + ret._fun = fun; + } + } + // Add edge from Start to Stop + StartNode start = (StartNode)nodes.at(0); + StopNode stop = (StopNode)nodes.last(); + start.setDef(1,stop); + + assert check(code,nodes); + elf._nodes = nodes; + } + + // Check loaded types and nodes are consistent + private static boolean check( CodeGen code, Ary<Node> nodes ) { + // Loaded nodes are complete, unlike nodes still being built by the + // parser. Recompute them with post-parse semantics. The compilation- + // unit Stop is the exception: its escape type depends on the complete + // program's public interface, which ParseAll freezes only after every + // compilation unit has been loaded. + boolean good = true; + CodeGen.Phase phase = code._phase; + code._phase = CodeGen.Phase.Opto; + try { + for( Node n : nodes ) { + if( n instanceof StopCUNode && !code.publicInterfaceFrozen() ) + continue; + Type nval = n.compute(); + if( nval != n._type ) { + System.err.println("Node "+n+" read in as "+n._type+" but computes as "+nval); + good = false; + } + } + } finally { + code._phase = phase; + } + return good; + } + + // -------------------------------------------------- + public static Ary<Node> nodeOrder( CodeGen code, CompUnit cu ) { + assert code._start._ltree != null; // Uses loop tree + BitSet visit = code.visit(); + Ary<Node> nodes = new Ary<>(Node.class); + visit.set(code._stop._nid); + // Walk all listed functions, in this single compilation unit + for( FunNode fun : code._linker ) + if( fun!=null && !fun.isDead() && fun._compunit == cu ) + _funWalk(fun,nodes,visit); + + // Just constants used by the listed functions + Ary<Node> cons = new Ary<>(Node.class); + cons.add(code._start); + for( Node n : code._start._outputs ) { + if( n instanceof ConstantNode con && !visit.get(con._nid) ) { + for( Node use : con.outs() ) { + // Special case for Cast:BOT which is basically a double-indirect constant + if( use instanceof CheckCastNode cast && cast.in(0)==null ) + for( Node useuse : cast.outs() ) + conUsed(cast,useuse,visit,cons); + // PtrToInt is another zero-code value rooted at a global + // constant. Its array-body Add is the value used by the + // function, so include that extra link in the ownership + // test as well. + if( use instanceof PtrToIntNode ptr ) + for( Node add : ptr.outs() ) { + for( Node addUse : add.outs() ) + conUsed(add,addUse,visit,cons); + conUsed(ptr,add,visit,cons); + } + // Constant used by somebody, so flag for output + conUsed(con,use,visit,cons); + } + } + } + // StartCU and memory projection + cons.add(cu._start); + for( Node n : cu._start._outputs ) + if( n instanceof ProjNode ) cons.add(n); + // All the innards of functions + cons.addAll(nodes); + // StopCU, Stop + cons.add(cu._stop); + cons.add(code._stop); + visit.clear(); + return cons; + } + + private static void conUsed( Node con, Node use, BitSet visit, Ary<Node> cons ) { + if( use == null || !visit.get(use._nid) || visit.get(con._nid) ) return; + cons.add(con); + visit.set(con._nid); + } + + // -------------------------------------------------- + public static Ary<Node> nodeOrder( CodeGen code ) { + assert code._start._ltree != null; // Uses loop tree + Ary<Node> nodes = new Ary<>(Node.class); + BitSet visit = code.visit(); + nodes.add(code._start); + + // All the global constants + for( Node n : code._start._outputs ) { + if( n.isConst() ) { + visit.set( n._nid ); + nodes.add( n ); + // Special for Cast:BOT + for( Node cast : n._outputs ) + if( cast != null && cast.nIns() == 2 && cast.in( 0 ) == null ) { + visit.set( cast._nid ); + nodes.add( cast ); + // Array pointer conversion adds the body offset after + // the zero-code PtrToInt. Both nodes are global-value + // descendants of the constant and otherwise have no + // function-local root from which to walk them. + if( cast instanceof PtrToIntNode ) + for( Node add : cast.outs() ) + if( add instanceof AddNode && !visit.get(add._nid) ) { + visit.set(add._nid); + nodes.add(add); + } + } + } + } + + // All the CompUnits + for( CompUnit cu : code._compunits.values() ) { + // Whole CompUnits have no users, went dead + if( cu._start != null && !cu._start.isDead() ) { + nodes.add(cu._start); + // Memory proj + nodes.add(cu._start.proj(1)); + // All the functions, including internal ones, in comp-unit order + for( FunNode fun : code._linker ) + if( fun!=null && !fun.isDead() && fun._compunit == cu ) + _funWalk(fun,nodes,visit); + nodes.add(cu._stop); + } + } + + nodes.add(code._stop); + visit.clear(); + return nodes; + } + + // Walk and print whole functions at a time, in CG order + private static void _funWalk( FunNode fun, Ary<Node> rpo, BitSet visit ) { + int start = rpo._len; + _funRPO(fun,rpo,visit); + int len = rpo._len - start; + for( int i=0; i< len>>1; i++ ) + rpo.swap(start+i,start+len-1-i); + } + + // Walk and gather RPO nodes. + private static void _funRPO(Node n, Ary<Node> rpo, BitSet visit ) { + if( visit.get(n._nid) ) return; // Been there, done that + visit.set(n._nid); // Stop recursion + // Walk outputs ordered + if( n instanceof CFGNode ncfg ) { + // If nodes walk outer loops before inner, so they hit the + // Return first, so Return is at the bottom of the RPO. + if( n instanceof IfNode iff ) { + CProjNode c0 = iff.cproj(0); + CProjNode c1 = iff.cproj(1); + if( c0._ltree.depth() > c1._ltree.depth() ) + { c0 = c1; c1 = iff.cproj(0); } + _funRPO(c0,rpo,visit); + _funRPO(c1,rpo,visit); + } else if( n instanceof StopNode ) { + // nothing + } else { + // CFG to CFG before CFG to data + for( Node use : n._outputs ) + if( use instanceof CFGNode usecfg && !ncfg.skip(usecfg) ) // Do not walk from Call to Fun + _funRPO(use,rpo,visit); + } + // Walk CFG to data eventually + for( Node use : n._outputs ) + if( !(use instanceof CFGNode) ) + _funRPO(use,rpo,visit); + } else { + // Do not walk from a non-CFG to a CFG; CFGs walk to CFGs to + // preserve CFG order. Do not walk *into* Phis; wait for the + // Region to walk the Phis (otherwise you visit some of the + // Phis ahead of others forcing the Phis to be spread around). + for( Node use : n._outputs ) { + if( use!=null && !(use instanceof CFGNode) && !(use instanceof PhiNode) ) + _funRPO(use,rpo,visit); + } + } + + // If parent is a Multi, do not add (yet). + // If self is a Multi, add self and children. + if( multiChild(n) || n instanceof CallEndNode ) { + // nothing; delay until Multi-head or CallNode + } else if( n instanceof MultiNode ) { + // Now lump all multi/projections together + printMulti(n,rpo); + } else if( n instanceof CallNode call ) { + printMulti(call.cend(),rpo); + rpo.add(call); + } else if( n instanceof RegionNode ) { + // Now lump all Phis together + int old = rpo._len; + for( Node use : n._outputs ) + if( use instanceof PhiNode ) + rpo.add(use); + // Sort by label? Want Phis before control outputs + Arrays.sort( rpo._es, old, rpo._len, (x,y) -> y.label().compareTo(x.label()) ); + rpo.add(n); + } else if( n instanceof StopNode ) { + // nothing + } else { + rpo.add(n); // Post-order add + } + } + + private static boolean multiChild(Node n) { + return n instanceof Proj || n instanceof PhiNode; + } + + private static void printMulti(Node n, Ary<Node> rpo) { + // Now lump all multi/projections together + for( Node use : n._outputs ) + rpo.add(use); + // Sort by projection order + if( !(n instanceof StartNode) ) + Arrays.sort( rpo._es,rpo._len-n.nOuts(),rpo._len, + (x,y) -> x instanceof ProjNode px && y instanceof ProjNode py ? (py.idx() - px.idx()) : (y._nid - x._nid) ); + rpo.add(n); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/AddNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/AddNode.java new file mode 100644 index 00000000..46607c23 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -0,0 +1,179 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; + +public class AddNode extends ArithNode { + @Override boolean allowFloat() { return true; } + public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public AddNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); } + @Override public Tag serialTag() { return Tag.Add; } + + @Override public String label() { return "Add"; } + @Override public String op() { return "+"; } + + @Override long doOp( long x, long y ) { return x + y; } + @Override double doOp(double x, double y ) { return x + y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { + // Fold ranges like {0-1} + {2-3} into {2-4}. + if( !overflow(x._min,y._min) && + !overflow(x._max,y._max) ) + return TypeInteger.make(x._min+y._min,x._max+y._max); + return TypeInteger.BOT; + } + + @Override + public Node idealize () { + // Settle the one-shot arithmetic mode before applying family-specific + // algebraic identities. + if( _mode==0 ) + return super.idealize(); + + Node lhs = in(1); + Node rhs = in(2); + if( rhs instanceof AddNode add && add.err()!=null ) + return null; + Type t2 = rhs._type; + + // Add of 0. We do not check for (0+x) because this will already + // canonicalize to (x+0) + if( t2 == TypeInteger.ZERO ) + return lhs; + + // Add of same to a multiply by 2 + if( lhs==rhs && _mode==1 ) + return new ShlNode(null,lhs,CodeGen.CODE.con(1)); + + // Goal: a left-spine set of adds, with constants on the rhs (which then fold). + + // Move non-adds to RHS + if( !(lhs instanceof AddNode) && rhs instanceof AddNode ) + return swap12(); + + // x+(-y) becomes x-y + if( rhs instanceof MinusNode minus ) + return new SubNode(lhs,minus.in(1),_mode); + + // Now we might see (add add non) or (add non non) or (add add add) but never (add non add) + + // Do we have x + (y + z) ? + // Swap to (x + y) + z + // Rotate (add add add) to remove the add on RHS + if( rhs instanceof AddNode add ) + return copy(copy(lhs,add.in(1)).peephole(), add.in(2)); + + // Now we might see (add add non) or (add non non) but never (add non add) nor (add add add) + if( !(lhs instanceof AddNode) ) { + // Rotate; look for (add (phi cons) con/(phi cons)) + if( spine_cmp(lhs,rhs,this) ) return swap12(); + Node n = phiCon(this,true); + return n == null ? super.idealize() : n; + } + + // Now we only see (add add non) + + // Dead data cycle; comes about from dead infinite loops. Do nothing, + // the loop will peep as dead after a bit. + if( lhs.nIns()>=2 && lhs.in(1) == lhs ) + return null; + + // Do we have (x + con1) + con2? + // Replace with (x + (con1+con2) which then fold the constants + // lhs.in(2) is con1 here + // If lhs.in(2) is not a constant, we add ourselves as a dependency + // because if it later became a constant then we could make this + // transformation. + if( addDep(lhs.in(2))._type.isConstant() && rhs._type.isConstant() ) + return copy(lhs.in(1),copy(lhs.in(2),rhs).peephole()); + + + // Do we have ((x + (phi cons)) + con) ? + // Do we have ((x + (phi cons)) + (phi cons)) ? + // Push constant up through the phi: x + (phi con0+con0 con1+con1...) + Node phicon = phiCon(this,true); + if( phicon!=null ) return phicon; + + // Now we sort along the spine via rotates, to gather similar things together. + + // Do we rotate (x + y) + z + // into (x + z) + y ? + if( spine_cmp(lhs.in(2),rhs,this) ) + return copy(copy(lhs.in(1),rhs).peephole(),lhs.in(2)); + + return super.idealize(); + } + + // Rotation is only valid for associative ops, e.g. Add, Mul, And, Or, Xor. + // Do we have ((phi cons)|(x + (phi cons)) + con|(phi cons)|(op y con)) ? + // Push constant up through the phi: x + (phi con0+con0 con1+con1...) + static Node phiCon(Node op, boolean rotate) { + Node lhs = op.in(1); + Node rhs = op.in(2); + if( rhs._type==TypeInteger.TOP ) return null; + // LHS is either a Phi of constants, or another op with Phi of constants + PhiNode lphi = pcon(lhs,op); + if( rotate && lphi==null && lhs.nIns() > 2 ) { + // Only valid to rotate constants if both are same associative ops + if( lhs.getClass() != op.getClass() ) return null; + lphi = pcon(lhs.in(2),op); // Will rotate with the Phi push + } + if( lphi==null ) return null; + if( lphi.region().nIns() <=2 ) return null; // Phi is collapsing + + // RHS is a constant or a Phi of constants + if( !(rhs instanceof ConstantNode) && pcon(rhs,op)==null ) + return null; + + // If both are Phis, must be same Region + if( rhs instanceof PhiNode && lphi.in(0) != rhs.in(0) ) + return null; + + // Note that this is the exact reverse of Phi pulling a common op down + // to reduce total op-count. We don't get in an endless push-up + // push-down peephole cycle because the constants all fold first. + Node[] ns = new Node[lphi.nIns()]; + ns[0] = lphi.in(0); + // Push constant up through the phi: x + (phi con0+con0 con1+con1...) + for( int i=1; i<ns.length; i++ ) + ns[i] = op.copy(lphi.in(i), rhs instanceof PhiNode ? rhs.in(i) : rhs).peephole(); + String label = lphi._label + (rhs instanceof PhiNode rphi ? rphi._label : ""); + Node phi = new PhiNode(label,ns).peephole(); + // Rotate needs another op, otherwise just the phi + return lhs==lphi ? phi : op.copy(lhs.in(1),phi); + } + + /** + * Tests if the op is a phi and has all constant inputs. + * If not, returns null. + * If op is a phi, but its inputs are not all constants, then dep is added as + * a dependency to the phi's non-const input, because if later the input turn into a constant + * dep can make progress. + */ + static PhiNode pcon(Node op, Node dep) { + return op instanceof PhiNode phi && !(phi instanceof ParmNode) && phi.allCons(dep) ? phi : null; + } + + // Compare two off-spine nodes and decide what order they should be in. + // Do we rotate ((x + hi) + lo) into ((x + lo) + hi) ? + // Generally constants always go right, then Phi-of-constants, then muls, then others. + // Ties with in a category sort by node ID. + // TRUE if swapping hi and lo. + static boolean spine_cmp( Node hi, Node lo, Node dep ) { + if( lo._type.isConstant() || lo instanceof ConFldOffNode ) return false; + if( hi._type.isConstant() || hi instanceof ConFldOffNode ) return true ; + + if( lo instanceof PhiNode lphi && lphi.region()._type==Type.XCONTROL ) return false; + if( hi instanceof PhiNode hphi && hphi.region()._type==Type.XCONTROL ) return false; + + if( lo instanceof PhiNode && lo.allCons(dep) ) return false; + if( hi instanceof PhiNode && hi.allCons(dep) ) return true ; + + if( lo instanceof PhiNode && !(hi instanceof PhiNode) ) return true; + if( hi instanceof PhiNode && !(lo instanceof PhiNode) ) return false; + + // Same category of "others" + return lo._nid > hi._nid; + } + + @Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs,_mode); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/AndNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/AndNode.java new file mode 100644 index 00000000..083a9a8c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/AndNode.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class AndNode extends ArithNode { + public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } + @Override public Tag serialTag() { return Tag.And; } + + @Override public String op() { return "&"; } + @Override public String glabel() { return "&"; } + + @Override long doOp( long x, long y ) { return x & y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { + // Sharpen allowed bits if either value is narrowed + long mask = x.mask() & y.mask(); + return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask); + } + + @Override + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + Type t1 = lhs._type; + Type t2 = rhs._type; + if( !(t1 instanceof TypeInteger && t2 instanceof TypeInteger t2i) ) + return null; // Malformed, e.g. (17 & 3.14) + + // And of -1. We do not check for (-1&x) because this will already + // canonicalize to (x&-1). We do not check for zero, because + // the compute() call will return a zero already. + if( t2i.isConstant() && t2i.value()==-1 ) + return lhs; + + // Move constants to RHS: con*arg becomes arg*con + if( t1.isConstant() && !t2.isConstant() ) + return swap12(); + + // Do we have ((x & (phi cons)) & con) ? + // Do we have ((x & (phi cons)) & (phi cons)) ? + // Push constant up through the phi: x & (phi con0&con0 con1&con1...) + Node phicon = AddNode.phiCon(this,true); + if( phicon!=null ) + return phicon; + + return super.idealize(); + } + @Override Node copy(Node lhs, Node rhs) { return new AndNode(_loc,lhs,rhs); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java new file mode 100644 index 00000000..8c21aef7 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java @@ -0,0 +1,198 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +public abstract class ArithNode extends Node implements ModeNode { + // Source location for late reported errors + Parser.Lexer _loc; + // Mode is 0 for unknown, 1 for int, 2 for flt + byte _mode; + + public ArithNode( Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; } + public ArithNode( Parser.Lexer loc, Node lhs, Node rhs, byte mode) { this(loc,lhs,rhs); _mode=mode; } + @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) { + baos.packed1(_mode); + } + @Override public String glabel() { return op(); } + abstract String op(); + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + in(1)._print0(sb.append("("), visited); + in(2)._print0(sb.append(op()), visited); + return sb.append(")"); + } + + abstract long doOp(long x, long y); + abstract TypeInteger doOp(TypeInteger x, TypeInteger y); + double doOp(double x, double y) { throw Utils.TODO(); } + + @Override public byte mode() { return _mode; } + @Override public Node setMode(byte mode) { + assert _mode==0 && (mode==1 || mode==2); + unlock(); // _mode participates in GVN hash and equality + _mode = mode; + return this; + } + static byte mode( Type t1, Type t2 ) { + if( t1 instanceof TypeFloat || t2 instanceof TypeFloat ) return 2; + if( t1 instanceof TypeInteger && t2 instanceof TypeInteger ) return 1; + if( t1 instanceof TypeMemPtr && t2 instanceof TypeMemPtr ) return 1; + if( t1 instanceof TypeFunPtr && t2 instanceof TypeFunPtr ) return 1; + return 0; + } + + // Check that a settled mode agrees with all data inputs. Takes the Node + // instead of a varargs list both to avoid allocation and to keep this ready + // to become shared mode-aware Node behavior later. + static Parser.ParseException nodeErr(Node n, Parser.Lexer loc, String op, byte mode, boolean allowFloat) { + Type bad = null; + for( int i=1; i<n.nIns(); i++ ) { + Type t = n.in(i)._type; + if( t.isHigh() ) + return null; + // A weak input is not evidence of a bad mode while the graph is + // still being built. Loop Phis commonly await their backedge. + if( CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal() && t == Type.BOTTOM ) + return null; + bad = switch( t ) { + case TypeInteger ti -> mode==1 ? null : t; + case TypeMemPtr ti -> mode==1 ? null : t; + case TypeFunPtr ti -> mode==1 ? null : t; + case TypeFloat tf -> mode==2 ? null : t; + default -> t; + }; + if( bad != null ) break; + } + boolean unsupportedFloat = false; + if( !allowFloat ) + for( int i=1; i<n.nIns(); i++ ) + if( n.in(i)._type instanceof TypeFloat ) { + bad = n.in(i)._type; + unsupportedFloat = true; + break; + } + if( bad == null ) + return null; + if( unsupportedFloat || n instanceof BoolNode ) + return Parser.error("Cannot '"+op+"' "+bad,loc); + if( n.nIns()==3 ) + return Parser.error("Cannot "+n.in(1)._type+" "+op+" "+n.in(2)._type,loc); + return Parser.error("Cannot '"+op+"' "+bad,loc); + } + + // Arithmetic subclasses which have a floating-point interpretation override. + boolean allowFloat() { return false; } + + // Generic airthmetic op math: high returns high; low returns low; 2 + // constants fold; only 2 non-constants call specialized math. + @Override + public final Type compute() { + Type t1 = in(1)._type, t2 = in(2)._type; + if( t1.isHigh() || t2.isHigh() ) + return _mode==0 ? Type.TOP : + _mode==1 ? TypeInteger.TOP : + TypeFloat.F64.dual(); + byte mode = _mode==0 ? mode(t1,t2) : _mode; + if( mode==0 ) + return Type.BOTTOM; + if( mode==1 ) { + if( t1 instanceof TypeInteger x && + t2 instanceof TypeInteger y ) + return x.isConstant() && y.isConstant() + ? con(x,y) + : doOp(x,y).makeWide( (byte)Math.max(x._widen,y._widen) ); + return TypeInteger.BOT; + } + // FP handling + if( t1 instanceof TypeFloat f1 && + t2 instanceof TypeFloat f2 ) { + if( f1.isConstant() && f2.isConstant() ) + return TypeFloat.constant(doOp(f1.value(),f2.value())); + return t1.meet(t2); + } + return TypeFloat.F64; + } + + private TypeInteger con( Type t1, Type t2 ) { + return TypeInteger.constant( doOp( ((TypeInteger)t1).value(), ((TypeInteger)t2).value()) ); + } + + // Check for "op(Phi(con,x),Phi(con,y))" and push-up through the Phi. + // Note that this is the exact reverse of Phi pulling a common op down + // to reduce total op-count. We don't get in an endless push-up + // push-down peephole cycle because the constants all fold first. + // Returns "Phi(op(con,con),op(x,y))". + + // Expected to be called after other ideal checks are done. + @Override public Node idealize() { + Type t1 = in(1)._type, t2 = in(2)._type; + + // Can we decide int vs flt? + if( _mode==0 ) { + byte mode = mode(t1,t2); + if( mode!=0 ) return setMode(mode).init(); + } + if( _mode==2 ) { + if( t1 instanceof TypeInteger ) return copy(new ToFloatNode(in(1)).peephole(),in(2)); + if( t2 instanceof TypeInteger ) return copy(in(1),new ToFloatNode(in(2)).peephole()); + } + + if( in(1) instanceof PhiNode lhs && + in(2) instanceof PhiNode rhs && + lhs.nIns() >= 2 && !lhs.inProgress() && + // Disallow with self-looping phi; these will collapse + (lhs.nIns() > 2 && lhs.in(2)!=lhs && rhs.nIns() > 2 && rhs.in(2)!=rhs) ) { + Node r = lhs.region(); + if( r==rhs.region() && !(r instanceof FunNode) && + // For loops, the backedge *must* fold away, lest we unroll forever + (!(r instanceof LoopNode) || isCon(lhs,rhs,2)) ) { + // Profit check: only 1 instance of `this` will remain, all the + // others will fold to constants. For loops, *all* must fold away + int cnt=0; + for( int i=1; i<lhs.nIns(); i++ ) + if( isCon(lhs,rhs,i) ) + cnt++; + if( lhs.nIns()-1 - cnt <= 1 ) { + // Profit! Push up-phi and fold + Node[] ns = new Node[lhs.nIns()]; + ns[0] = lhs.in(0); + for( int i=1; i<lhs.nIns(); i++ ) { + ns[i] = isCon(lhs,rhs,i) + ? ConstantNode.make(con(lhs.in(i)._type, rhs.in(i)._type)).peephole() + : copy(lhs.in(i), rhs.in(i)).peephole(); + } + String label = lhs._label==rhs._label ? lhs._label : lhs._label + rhs._label; + return new PhiNode(label, ns).peephole(); + } + } + } + + return null; + } + + static boolean isCon( PhiNode lhs, PhiNode rhs, int i ) { + return lhs.in(i)._type.isConstant() && rhs.in(i)._type.isConstant(); + } + + + static boolean overflow( long x, long y ) { + if( (x ^ y ) < 0 ) return false; // unequal signs, never overflow + return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow + } + + @Override public Parser.ParseException err() { + return nodeErr(this,_loc,op(),_mode,allowFloat()); + } + + @Override public boolean eq( Node n ) { return _mode==((ArithNode)n)._mode; } + @Override public int hash() { return _mode; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/BoolNode.java new file mode 100644 index 00000000..c8722f67 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -0,0 +1,195 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; +import static com.seaofnodes.simple.type.TypeInteger.*; + +abstract public class BoolNode extends Node implements ModeNode { + + // Mode is 0 for unknown, 1 for int, 2 for flt + byte _mode; + + public BoolNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public BoolNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs); _mode = mode; } + @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) { + baos.packed1(_mode); + } + + abstract public String op(); // String opcode name + + @Override + public String glabel() { return op(); } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + in(1)._print0(sb.append("("), visited); + in(2)._print0(sb.append(op()), visited); + return sb.append(")"); + } + + @Override + public TypeInteger compute() { + Type t1 = in(1)._type; + Type t2 = in(2)._type; + // Exactly equals? + if( t1.isHigh() || t2.isHigh() ) + return (TypeInteger)BOOL.dual(); + if( in(1)==in(2) ) + // LT fails, both EQ and LE succeed + return this instanceof LT ? FALSE : TRUE; + byte mode = _mode==0 ? ArithNode.mode(t1,t2) : _mode; + if( mode==0 ) + return BOOL; + // Handle integer compares + if( mode==1 ) { + if( t1 instanceof TypeInteger i1 && + t2 instanceof TypeInteger i2 ) + return doOp(i1,i2); + return BOOL; + } + // Floating compare. Mixed inputs are converted in idealize(). + if( t1 instanceof TypeFloat f1 && + t2 instanceof TypeFloat f2 && + f1.isConstant() && f2.isConstant() ) + return doOp(f1.value(), f2.value()) ? TRUE : FALSE; + return BOOL; + } + + TypeInteger doOp(TypeInteger t1, TypeInteger t2) { throw Utils.TODO(); } + boolean doOp(double lhs, double rhs) { throw Utils.TODO(); } + public boolean isFloat() { assert _mode!=0; return _mode==2; } + @Override public byte mode() { return _mode; } + @Override public Node setMode(byte mode) { + assert _mode==0 && (mode==1 || mode==2); + unlock(); // _mode participates in GVN hash and equality + _mode = mode; + return this; + } + + @Override + public Node idealize() { + // Compare of same + if( in(1)==in(2) ) + return this instanceof LT ? CodeGen.CODE.ZERO : ConstantNode.make(TRUE); + // Can we decide int vs flt? + Type t1 = in(1)._type, t2 = in(2)._type; + if( _mode==0 ) { + byte mode = ArithNode.mode(t1,t2); + if( mode!=0 ) return setMode(mode).init(); + } + if( _mode==2 ) { + if( t1 instanceof TypeInteger ) return copy(new ToFloatNode(in(1)).peephole(),in(2)); + if( t2 instanceof TypeInteger ) return copy(in(1),new ToFloatNode(in(2)).peephole()); + } + + + // Equals pushes constant to the right; 5==X becomes X==5. + if( this instanceof EQ ) { + if( !(in(2) instanceof ConstantNode) ) { + // con==noncon becomes noncon==con + if( in(1) instanceof ConstantNode || in(1)._nid > in(2)._nid ) + // Equals sorts by NID otherwise: non.high == non.low becomes non.low == non.high + return new EQ(in(2),in(1)); + } + // Equals X==0 becomes a !X + if( (in(2)._type == ZERO || in(2)._type == Type.NIL) ) + return new NotNode(in(1)); + } + + // Do we have ((x * (phi cons)) * con) ? + // Do we have ((x * (phi cons)) * (phi cons)) ? + // Push constant up through the phi: x * (phi con0*con0 con1*con1...) + Node phicon = AddNode.phiCon(this,this instanceof EQ); + if( phicon!=null ) return phicon; + + return null; + } + + @Override public boolean eq( Node n ) { return _mode==((BoolNode)n)._mode; } + @Override public int hash() { return _mode; } + @Override public Parser.ParseException err() { + return ArithNode.nodeErr(this,null,op(),_mode,!(this instanceof ULT)); + } + + public static class EQ extends BoolNode { + public EQ(Node lhs, Node rhs) { super(lhs,rhs); } + public EQ(Node lhs, Node rhs, byte mode) { super(lhs,rhs, mode); } + @Override public Tag serialTag() { return Tag.EQ; } + public String op() { return "=="; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( i1==i2 && i1.isConstant() ) return TRUE; + if( i1._max < i2._min || i1._min > i2._max ) return FALSE; + return BOOL; + } + @Override boolean doOp(double lhs, double rhs) { return lhs==rhs; } + Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs,_mode); } + } + + public static class NE extends BoolNode { + public NE(Node lhs, Node rhs) { super(lhs,rhs); } + public NE(Node lhs, Node rhs, byte mode) { super(lhs,rhs,mode); } + @Override public Tag serialTag() { return Tag.NE; } + public String op() { return "!="; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( i1==i2 && i1.isConstant() ) return TRUE; + if( i1._max < i2._min || i1._min > i2._max ) return FALSE; + return BOOL; + } + @Override boolean doOp(double lhs, double rhs) { return lhs!=rhs; } + Node copy(Node lhs, Node rhs) { return new NE(lhs,rhs,_mode); } + } + + public static class LT extends BoolNode { + public LT(Node lhs, Node rhs) { super(lhs,rhs); } + public LT(Node lhs, Node rhs, byte mode) { super(lhs,rhs, mode); } + @Override public Tag serialTag() { return Tag.LT; } + public String op() { return "<" ; } + public String glabel() { return "<"; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( i1._max < i2._min ) return TRUE; + if( i1._min >= i2._max ) return FALSE; + return BOOL; + } + @Override boolean doOp(double lhs, double rhs) { return lhs<rhs; } + Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs,_mode); } + } + + public static class LE extends BoolNode { + public LE(Node lhs, Node rhs) { super(lhs,rhs); } + public LE(Node lhs, Node rhs, byte mode) { super(lhs,rhs,mode); } + @Override public Tag serialTag() { return Tag.LE; } + public String op() { return "<="; } + public String glabel() { return "<="; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( i1._max <= i2._min ) return TRUE; + if( i1._min > i2._max ) return FALSE; + return BOOL; + } + @Override boolean doOp(double lhs, double rhs) { return lhs<=rhs; } + Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs,_mode); } + } + + // Unsigned less that, for range checks. Not directly user writable. + public static class ULT extends BoolNode { + public ULT(Node lhs, Node rhs) { super(lhs,rhs,(byte)1); } + public ULT(Node lhs, Node rhs, byte mode) { + super(lhs,rhs,(byte)1); + assert mode==1; + } + @Override public Tag serialTag() { return Tag.ULT; } + public String op() { return "u<" ; } + public String glabel() { return "u<"; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE; + if( Long.compareUnsigned(i1._min,i2._max) >= 0 ) return FALSE; + return BOOL; + } + Node copy(Node lhs, Node rhs) { return new ULT(lhs,rhs); } + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java new file mode 100644 index 00000000..c4a13375 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java @@ -0,0 +1,312 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.GlobalBits; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeMem; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * A Phi for bulk memory. It covers all aliases except {@link #_aliases}; + * those aliases must be represented by parallel precise MemPhis at the same + * program slice. + */ +public class BulkMemPhiNode extends PhiNode { + + private static final BitSet EMPTY = new BitSet(); + + // Excluded Aliases; empty means NO exclusions. + // Directly visible by design. Empty sets share EMPTY; splitAlias performs + // copy-on-first-write and removes the node from GVN before mutation. + public BitSet _aliases; + + public BulkMemPhiNode(String label, Node... inputs) { + this(label,EMPTY,inputs); + } + + private BulkMemPhiNode(String label, BitSet aliases, Node... inputs) { + super(label, inputs); + _aliases = aliases; + } + + public BulkMemPhiNode(BulkMemPhiNode phi) { + super(phi,phi._label); + _aliases = phi._aliases.isEmpty() ? EMPTY : (BitSet)phi._aliases.clone(); + } + + public BulkMemPhiNode(RegionNode r, Node sample) { + super(r,sample); + _aliases = EMPTY; + } + + public boolean isSplit(int alias) { return _aliases.get(alias); } + + /** + * Record that an alias has been split out. The graph rewrite installing + * its parallel precise Phi must happen before this semantic change. + */ + public void splitAlias(int alias) { + assert alias > 1; + if( _aliases.get(alias) ) return; + unlock(); + if( _aliases==EMPTY ) + _aliases = new BitSet(); + _aliases.set(alias); + } + + @Override public Tag serialTag() { return Tag.BulkMemPhi; } + + @Override + public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, + IdentityHashMap<Node,Integer> anodes) { + super.packed(baos,strs,types,anodes); + baos.packed2(_aliases.cardinality()); + for( int alias = _aliases.nextSetBit(0); alias >= 0; alias = _aliases.nextSetBit(alias+1) ) + baos.packed2(alias); + } + + static Node make(BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases) { + Node[] ins = new Node[bais.packed1()]; + String label = strs[bais.packed2()]; + BitSet bits = new BitSet(); + for( int i=bais.packed2(); i>0; i-- ) { + int alias = bais.packed2(); + if( alias >= GlobalBits.RESERVED ) alias = aliases.map(fileAliases,alias); + bits.set(alias); + } + return new BulkMemPhiNode(label,bits.isEmpty() ? EMPTY : bits,ins); + } + + @Override + public Type compute() { + Type t = super.compute(); + return t instanceof TypeMem mem ? mem : + t==Type.BOTTOM ? TypeMem.BOT : + TypeMem.TOP; + } + + @Override + public Node idealize() { + Node progress = super.idealize(); + if( progress != null ) return progress; + + if( !(region() instanceof RegionNode r ) ) + return in(1); // Input has collapse to e.g. starting control. + if( r.inProgress() || r.nIns()<=1 ) + return null; // Input is in-progress + if( nOuts()==0 ) return null; + if( nIns() <= 2 ) return null; + + // "Peek through" a MemMerge that covers this alias set on its default + for( int i=1; i<nIns(); i++ ) + if( in(i) instanceof MemMergeNode mmm && canPeek(mmm) ) { + setDef(i,mmm.in(1)); + return this; + } + + // If any input or output uses or defines a specific alias we cover, + // make a private MemPhi for it, and add it to the excluded list. + for( int i=1; i<nIns(); i++ ) { + int alias = inputAlias(in(i)); + if( alias!=0 ) + return slice(alias); + } + + for( int i=0; i<nOuts(); i++ ) { + int alias = outputAlias(out(i)); + if( alias!=0 ) + return slice(alias); + } + + return null; + } + + static public boolean checkMem(Node n, int alias) { + for( Node m : n._inputs ) + // Bulk memory supplies this alias + if( m instanceof BulkMemPhiNode bulk ) { + if( bulk._aliases.get(alias) ) + return false; + } else if( m instanceof MemMergeNode mmm && + mmm.in(1) instanceof BulkMemPhiNode bulk && + bulk._aliases.get(alias) && + (alias >= mmm.nIns() || mmm.in(alias)==null) ) + return false; + return true; + } + + // True if the MemMerge has no precise slice still covered by this Phi. + private boolean canPeek(MemMergeNode mmm) { + return missingAlias(mmm,false)==0; + } + + // Return an alias required by an input but still covered by this Phi, or 0. + private int inputAlias(Node n) { + return switch(n) { + case ParmNode p -> { assert p._idx==1; yield 0; } + case ScopeNode scope -> 0; + case ReturnNode ret -> 0; + case ProjNode proj -> 0; + case ConstantNode con -> { + assert !(con._con instanceof TypeMem tmem && tmem._alias != 1); + yield 0; + } + case MemMergeNode mmm -> missingAlias(mmm,false); + case MemOpNode mem -> unsplit(mem._alias); + case BulkMemPhiNode bulk -> missingAlias(bulk); + default -> throw Utils.TODO(); + }; + } + + // Return an alias required by a user but still covered by this Phi, or 0. + private int outputAlias(Node use) { + use = addDep(use); // User alias sharpening changes this decision + return switch(use) { + case ScopeNode scope -> 0; + case ParmNode parm -> 0; + case MemMergeNode mmm -> mmm.in(1)==this ? missingAlias(mmm,true) : 0; + case BulkMemPhiNode bulk -> missingAlias(bulk); + case MemOpNode mem -> unsplit(mem._alias); + case MemPhiNode phi -> unsplit(phi._alias); + case EscapeNode esc -> esc.pub()==this ? unsplit(esc.fld()._alias) : 0; + default -> { + assert use instanceof ReturnNode || use instanceof CallNode + : "Unexpected bulk-memory user "+use.getClass().getSimpleName()+"#"+use._nid+": "+use; + yield 0; + } + }; + } + + // Alias zero/one is bulk, and an already excluded precise alias cannot + // trigger another split of this Phi. + private int unsplit(int alias) { + return alias <= 1 || _aliases.get(alias) ? 0 : alias; + } + + // First alias excluded by the neighboring bulk Phi but not by this one. + private int missingAlias(BulkMemPhiNode bulk) { + for( int alias = bulk._aliases.nextSetBit(0); alias >= 0; alias = bulk._aliases.nextSetBit(alias+1) ) + if( !_aliases.get(alias) ) + return alias; + return 0; + } + + // First precise MemMerge slice still covered by this Phi. When inspecting + // a user, ignore slots which point back to this Phi. + private int missingAlias(MemMergeNode mmm, boolean user) { + for( int alias=2; alias<mmm.nIns(); alias++ ) + if( mmm.in(alias)!=null && + mmm.alias(alias)!=mmm.in(1) && + (!user || mmm.in(alias)!=this) && + !_aliases.get(alias) ) + return alias; + return 0; + } + + // Slice out given alias + private MemMergeNode slice( int alias ) { + assert !_aliases.get(alias); + assert !hasDup(alias); + // Inputs are the same; MemPhi will sharpen his own inputs + MemPhiNode mphi = new MemPhiNode("$"+alias,alias); + for( int i=0; i<nIns(); i++ ) mphi.addDef(in(i)); + Node mem = mphi.peephole(); + assert ((TypeMem)mem._type)._alias==alias; + + BitSet aliases = ((BitSet)_aliases.clone()); + aliases.set(alias); + BulkMemPhiNode bphi = new BulkMemPhiNode(_label,aliases); + for( int i=0; i<nIns(); i++ ) + bphi.addDef(in(i)); + // Installing bphi creates a new bulk user with a different exclusion + // set. Queue the original memory inputs before bphi itself can peep + // away and hide those neighbor relationships. + for( int i=1; i<bphi.nIns(); i++ ) + CodeGen.CODE.add(bphi.in(i)); + Node bulk = bphi.peephole(); + CodeGen.CODE.add(bulk); + CodeGen.CODE.add(mphi); + return aggregate(bulk,alias,mem); + } + + // Build aggregate memory over a bulk Phi, preserving every precise slice + // which the bulk has already excluded. The named alias is newly supplied + // (or replaces its old slice). + MemMergeNode aggregate(Node bulk, int alias, Node precise) { + MemMergeNode mmm = new MemMergeNode(false,null,bulk); + for( int old = _aliases.nextSetBit(0); old >= 0; old = _aliases.nextSetBit(old+1) ) + if( old != alias ) + mmm.alias(old,precisePhi(old)); + mmm.alias(alias,precise); + assert checkMerge(mmm); + mmm.init(); + // Slicing changes representation, not the set of values in memory. + // Newly exposed precise inputs can carry conservative bulk escape + // summaries; do not let those invent escapes absent from this Phi. + if( _type instanceof TypeMem old && mmm._type instanceof TypeMem mem ) + mmm._type = TypeMem.make(mem._alias,mem._t,mem._one,mem._clz, + mem._final,old._escFs,old._escAs); + return mmm; + } + + // Find the precise Phi parallel to this bulk Phi at the same merge. + private Node precisePhi(int alias) { + Node found = null; + for( Node use : region().outs() ) + if( use instanceof MemPhiNode mphi && mphi._alias==alias ) { + assert found==null; + found = mphi; + } + // The parallel Phi may already have collapsed to a common input. Do + // not recover it from downstream MemMerges: they are distinct program + // points and can legitimately carry different values for this alias. + // Reconstruct from this Phi's merge inputs instead. MemPhi selects + // the alias from each input MemMerge and collapses when, for example, + // the alias is loop-invariant here. + if( found==null ) { + MemPhiNode mphi = new MemPhiNode("$"+alias,alias); + mphi.addDef(region()); + for( int i=1; i<nIns(); i++ ) + mphi.addDef(preciseInput(in(i),alias)); + found = mphi.peephole(); + CodeGen.CODE.add(found); + } + return found; + } + + // Select an alias already split out of a predecessor's bulk memory. + private Node preciseInput(Node mem, int alias) { + if( mem instanceof MemMergeNode mmm ) + return mmm.alias(alias); + if( mem instanceof BulkMemPhiNode bulk && bulk.isSplit(alias) ) + return bulk.precisePhi(alias); + return mem; + } + + // Every alias excluded by a bulk default must be supplied explicitly. + static boolean checkMerge(MemMergeNode mmm) { + if( !(mmm.in(1) instanceof BulkMemPhiNode bulk) ) return true; + for( int alias = bulk._aliases.nextSetBit(0); alias >= 0; alias = bulk._aliases.nextSetBit(alias+1) ) + if( alias >= mmm.nIns() || mmm.in(alias)==null ) + return false; + return true; + } + + private boolean hasDup( int alias ) { + for( Node use : region().outs() ) + if( use instanceof MemPhiNode mphi && mphi._alias==alias ) + return true; + return false; + } + + @Override public boolean eq(Node n) { + return _aliases.equals(((BulkMemPhiNode)n)._aliases) && super.eq(n); + } + + @Override int hash() { return _aliases.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java new file mode 100644 index 00000000..2b668da8 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -0,0 +1,227 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Ary; + +import java.util.BitSet; +import java.util.IdentityHashMap; + +/** Control Flow Graph Nodes + * <p> + * CFG nodes have a immediate dominator depth (idepth) and a loop nesting + * depth(loop_depth). + * <p> + * idepth is computed lazily upon first request, and is valid even in the + * Parser, and is used by peepholes during parsing and afterward. + * <p> + * loop_depth is computed after optimization as part of scheduling. + * + * Start - Block head; has constants and trailing Funs + * Region - Block head; has Phis. Includes Fun and Parms. + * CallEnd - Block head; followed by CProj and Proj; also linked RPC Parms + * CProj - Block head + * + * If - Block tail; followed by CProj same block + * Call - Block tail; followed by CallEnd and linked Funs + * New/intrinsic - If followed by CProj: block tail else mid-block; followed by Proj + * + */ +public abstract class CFGNode extends Node { + + public CFGNode(Node... nodes) { super(nodes); } + public CFGNode(CFGNode cfg) { + super(cfg); + if( cfg != null ) { + _idepth = cfg._idepth; + _ltree = cfg._ltree; + _pre = cfg._pre; + } + } + + public CFGNode cfg(int idx) { return (CFGNode)in(idx); } + + // Block head is Start, Region, CProj, but not e.g. If, Return, Stop + public boolean blockHead() { return false; } + + // Get the one control following; error to call with more than one e.g. an + // IfNode or other multi-way branch. + public CFGNode uctrl() { + CFGNode c = null; + for( Node n : _outputs ) + if( n instanceof CFGNode cfg ) + { assert c==null; c = cfg; } + return c; + } + + // Used by the encoding / final BB layout + public CFGNode uctrlSkipEmpty() { + CFGNode x = this, y; + while( x.nOuts() == 1 && (y=x.uctrl())!=null && !(y instanceof CallNode) ) // Skip empty blocks + x = y; + return x; + } + + // ------------------------------------------------------------------------ + /** + * Immediate dominator tree depth, used to approximate a real IDOM during + * parsing where we do not have the whole program, and also peepholes + * change the CFG incrementally. + * <p> + * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>} + */ + public int _idepth; + public int idepth() { + return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); + } + + // Return the immediate dominator of this Node and compute dom tree depth. + public CFGNode idom(Node dep) { return cfg(0); } + public final CFGNode idom() { return idom(null); } + + // Return the LCA of two idoms + public CFGNode _idom(CFGNode rhs, Node dep) { + if( rhs==null ) return this; + CFGNode lhs = this; + while( lhs != rhs ) { + var comp = lhs.idepth() - rhs.idepth(); + if( comp >= 0 ) lhs = (dep==null ? lhs : dep.addDep(lhs)).idom(); + if( comp <= 0 ) rhs = (dep==null ? rhs : dep.addDep(rhs)).idom(); + if( lhs==null || rhs==null ) return null; + } + return lhs; + } + + // Anti-dependence field support + public int _anti; // Per-CFG field to help find anti-deps + + // Find nearest enclosing FunNode + public FunNode fun() { + CFGNode cfg = this; + while( !(cfg instanceof FunNode fun) ) { + if( cfg==null ) return null; + cfg = cfg.idom(); + } + return fun; + } + + // ------------------------------------------------------------------------ + // Loop nesting + public LoopNode loop() { return _ltree._head; } + public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); } + + public LoopTree _ltree; + public int _pre; // Pre-order numbers for loop tree finding + public static class LoopTree { + public LoopTree _par; + public final LoopNode _head; + public int _depth; + LoopTree(LoopNode head) { _head = head; } + @Override public String toString() { return label(_head); } + public int depth() { + return _depth==0 ? (_par==null ? 0 : (_depth = _par.depth()+1)) : _depth; + } + } + + // ------------------------------------------------------------------------ + // Tag all CFG Nodes with their containing LoopNode; LoopNodes themselves + // also refer to *their* containing LoopNode, as well as have their depth. + // Start is a LoopNode which contains all at depth 1. + public void buildLoopTree( Ary<FunNode> funs, StopNode stop) { + _ltree = stop._ltree = CodeGen.CODE.XCTRL._ltree = new LoopTree((StartNode)this); + Ary<FunNode> work = new Ary<>(FunNode.class); + + BitSet post = new BitSet(); + int pre = 1; + _pre = pre++; + for( FunNode fun : funs ) { + if( fun!=null && !fun.isDead() && !fun._type.isHigh() ) { + work.push(fun); + if(fun.in(1) instanceof StartCUNode start && start._pre==0 ) { + start._pre = pre++; + start._ltree = _ltree; + post.set(start._nid); + } + }; + } + + for( int i=0; i<work._len; i++ ) + pre = work.at(i)._bltWalk(pre,work.at(i),stop, post, work); + } + + int _bltWalk( int pre, FunNode fun, StopNode stop, BitSet post, Ary<FunNode> work ) { + // Pre-walked? + if( _pre!=0 ) return pre; + _pre = pre++; + // Pre-walk + for( Node use : _outputs ) + if( use instanceof CFGNode usecfg ) { + if( skip( usecfg ) ) { + if( usecfg._pre==0 && usecfg instanceof FunNode fun2 ) work.add( fun2 ); + } else + pre = usecfg._bltWalk( pre, use instanceof FunNode fuse ? fuse : fun, stop, post, work ); + } + + if( this instanceof ReturnNode ret ) { + ret._ltree = stop._ltree; + } else { + // Post-order work: find innermost loop + LoopTree inner = null, ltree; + for( Node use : _outputs ) { + if( !(use instanceof CFGNode usecfg) ) continue; + if( skip(usecfg) ) continue; + if( usecfg._type == Type.XCONTROL || // Do not walk dead control + usecfg._type == TypeTuple.IF_NEITHER ) // Nor dead IFs + continue; + // Child visited but not post-visited? + if( !post.get(usecfg._nid) ) { + // Must be a backedge to a LoopNode then + ltree = usecfg._ltree = new LoopTree((LoopNode)usecfg); + } else { + // Take child's loop choice, which must exist + ltree = usecfg._ltree; + // If falling into a loop, use the target loop's parent instead + if( ltree._head == usecfg ) { + if( ltree._par == null ) + // This loop never had an If test choose to take its + // exit, i.e. it is a no-exit infinite loop. + ltree._par = ltree._head.forceExit(fun,stop)._ltree; + ltree = ltree._par; + } + } + // Sort inner loops. The decision point is some branch far removed + // from either loop head OR either backedge so requires pre-order + // numbers to figure out innermost. + if( inner == null ) { inner = ltree; continue; } + if( inner == ltree ) continue; // No change + LoopTree outer = ltree._head._pre > inner._head._pre ? inner : ltree; + inner = ltree._head._pre > inner._head._pre ? ltree : inner; + inner._par = outer; + } + // Set selected loop + if( inner!=null ) + _ltree = inner; + } + // Tag as post-walked + post.set(_nid); + return pre; + } + + public boolean skip(CFGNode usecfg) { + // Only walk control users that are alive. + // Do not walk from a Call to linked Fun's. + return usecfg instanceof XCtrlNode || + (this instanceof CallNode && usecfg instanceof FunNode) || + (this instanceof ReturnNode && usecfg instanceof CallEndNode); + } + + public static String label( CFGNode target ) { + return switch(target) { + case StartNode start -> start.label(); + case FunNode fun -> fun.label(); + case LoopNode loop -> "LOOP"+target._nid; + default -> "L"+target._nid; + }; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java new file mode 100644 index 00000000..b74619a3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java @@ -0,0 +1,84 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Serialize; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeTuple; +import com.seaofnodes.simple.util.BAOS; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +public class CProjNode extends CFGNode implements Proj { + + // Which slice of the incoming multipart value + public int _idx; + + // Debugging label + public String _label; + + public CProjNode(Node ctrl, int idx, String label) { + super(ctrl); + _idx = idx; + _label = label; + } + public CProjNode(CProjNode c) { super(c); _idx = c._idx; _label = c._label; } + @Override public Tag serialTag() { return Tag.CProj; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed1(_idx); + baos.packed2(_label==null ? 0 : strs.get(_label)); + } + static Node make( BAOS bais, String[] strs ) { + return new CProjNode(null, bais.packed1(), strs[bais.packed2()] ); + } + + @Override public String label() { return _label; } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); } + + @Override public boolean blockHead() { return true; } + + public CFGNode ctrl() { return cfg(0); } + + @Override + public Type compute() { + Type t = ctrl()._type; + return t instanceof TypeTuple tt ? tt._types[_idx] : (t.isHigh() ? Type.TOP : Type.BOTTOM); + } + + @Override + public Node idealize() { + if( ctrl()._type instanceof TypeTuple tt ) { + if( tt._types[_idx]==Type.XCONTROL ) + return CodeGen.CODE.XCTRL; // We are dead + if( ctrl() instanceof IfNode && tt._types[1-_idx]==Type.XCONTROL ) // Only true for IfNodes + return ctrl().in(0); // We become our input control + } + + // Flip a negating if-test, to remove the not + if( ctrl() instanceof IfNode iff && iff.pred() != null && addDep(iff.pred()) instanceof NotNode not ) + return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True"); + + // Copy of some other input + return ((MultiNode)ctrl()).pcopy(_idx); + } + + // Only called during basic-block layout, inverts a T/F CProj + public void invert() { + _label = _idx == 0 ? "False" : "True"; + _idx = 1-_idx; + } + + @Override + public boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; } + + @Override + int hash() { return _idx; } + + @Override public int idx() { return _idx; } + + @Override public void gather( HashMap<String,Integer> strs ) { + Serialize.gather(strs,_label); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndMach.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndMach.java new file mode 100644 index 00000000..05707132 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndMach.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.util.SB; + +public class CallEndMach extends CallEndNode implements MachNode { + public CallEndMach( CallEndNode n ) { super(n); } + // MachNode specifics, shared across all CPUs + public int _xslot; + private RegMask _retMask; + private RegMask _kills; + public void cacheRegs(CodeGen code) { + // Return mask depends on TFP (either GPR or FPR) + _retMask = code._mach.retMask(call().tfp()); + // Kill mask is all caller-saves, and any mirror stack slots for args + // in registers. + RegMaskRW kills = (call().external() ? code._cCallerSave : code._callerSave).copy(); + // Start of stack slots + int maxReg = code._mach.regs().length; + // Incoming function arg slots, all low numbered in the RA + int fslot = fun()._maxArgSlot; + // Killed slots for this calls outgoing args + int xslot = call().external() + ? code._mach.maxArgSlot(call().tfp(),code._cCallingConv) + : code._mach.maxArgSlot(call().tfp()); + _xslot = (maxReg+fslot)+xslot; + for( int i=0; i<xslot; i++ ) + kills.set((maxReg+fslot)+i); + _kills = kills; + } + public String op() { return "cend"; } + public RegMask regmap(int i) { return null; } + public RegMask outregmap() { return null; } + public RegMask outregmap(int idx) { return idx==2 ? _retMask : null; } + public RegMask killmap() { return _kills; } + public void encoding( Encoding enc ) { } + public void asm(CodeGen code, SB sb) { } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java new file mode 100644 index 00000000..8e7f47f3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -0,0 +1,275 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * CallEnd + */ +public class CallEndNode extends CFGNode implements MultiNode { + + // When set true, this Call/CallEnd/Fun/Return is being trivially inlined + boolean _folding; + public final TypeRPC _rpc; + + public CallEndNode(CallNode call, TypeRPC rpc) { + super(new Node[]{call}); + _rpc = rpc; + } + public CallEndNode(CallEndNode cend) { super(cend); _rpc = cend._rpc; } + public CallEndNode(TypeRPC rpc) { + super(new Node[0]); + _rpc = TypeRPC.constant(CodeGen.CODE._rpcs.next(rpc.rpc())); + } + private CallEndNode(int nIns, TypeRPC rpc) { + super(new Node[nIns]); + _rpc = rpc; + } + @Override public Tag serialTag() { return Tag.CallEnd; } + public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed1(nIns()); + // Linked CallEnds depend on Return types which depend on CallEnds; + // break the cycle + baos.packed2(types.get(_type)); + baos.packed2(types.get(_rpc)); + } + static Node make( BAOS bais, Type[] types ) { + int nIns = bais.packed1(); + Type type = types[bais.packed2()]; + Node cend = new CallEndNode(nIns,(TypeRPC)types[bais.packed2()]); + cend._type = type; + return cend; + } + + @Override public boolean blockHead() { return true; } + + public CallNode call() { return (CallNode)in(0); } + boolean folding() { return _folding; } + + @Override public CFGNode idom(Node dep) { + // Folding the idom is the one inlining Return + return _folding ? cfg(1) : super.idom(dep); + } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("cend( "); + sb.append( in(0) instanceof CallNode ? "Call, " : "----, "); + for( int i=1; i<nIns()-1; i++ ) + in(i)._print0(sb,visited).append(","); + sb.setLength(sb.length()-1); + return sb.append(")"); + } + + @Override + public Type compute() { + if( !(in(0) instanceof CallNode call) || call._type != Type.CONTROL ) + return TypeTuple.STATE.dual(); + // Grab the TFP and use the functions declared return type. + // If the call.fptr() is a FRef, return will be BOTTOM. + Type ftype = addDep(call.fptr())._type; + if( !(ftype instanceof TypeFunPtr tfp) ) + return ftype.isHigh() ? TypeTuple.STATE.dual() : TypeTuple.STATE; + // Mid-fold, just take the one single callers' return type + if( _folding ) { + TypeTuple tt = (TypeTuple)in(1)._type; + return tt.makeFrom(2,tt.ret().join(tfp._ret)); + } + + // Here, if I can figure out I've linked *all* callers, then I can meet + // across the linked returns and join with the function return type. + + // External functions have an exact FIDX but deliberately no local + // FunNode/Return edge. They conservatively crush public memory and + // produce their declared return type. A missing non-external target + // is still an unresolved/error call. + int externs = 0; + if( !XInt.isHigh(tfp.fidxs()) ) + for( int fidx = XInt.next(tfp.fidxs(),0); fidx >= 0; fidx = XInt.next(tfp.fidxs(),fidx) ) + if( CodeGen.CODE.externFunc(fidx)!=null ) + externs++; + boolean hasExtern = externs != 0; + + // If we have an error-call, e.g. wrong args, then we never link. + // Pre-Opto this looks like a missing target fcn, and we assume it will + // appear later - meanwhile, we use a conservative approx of memory + // effects. + if( (nIns()-1)+externs < tfp.nfcns() ) { + // If before SCCP, we might call extras or also the unknown target. + StartNode start = CodeGen.CODE._start; + addDep(start); + Type tmem = start._type instanceof TypeTuple tt ? tt._types[1] : (start._type.isHigh() ? TypeMem.TOP : TypeMem.BOT); + Type ret = tfp.ret(); + // If during Opto, assume call won't be called and thus won't return anything. + if( CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal() ) + ret = Type.TOP; + return TypeTuple.make(Type.CONTROL,tmem,ret); + } + + // A linked function for every concrete function + TypeTuple state = hasExtern + ? TypeTuple.make(Type.CONTROL,TypeMem.BOT,tfp.ret()) + : TypeTuple.STATE.dual(); + for( int i=1; i<nIns(); i++ ) + state = (TypeTuple)state.meet(in(i)._type); + // At least as good as the TFP + return state.makeFrom(2,state.ret().join(tfp.ret())); + } + + @Override + public Node idealize() { + + // Worklist-based inlining. Cannot inline if folding, or calling + // multiple targets or no CallNode (malformed because dying). + if( !_folding && nIns()==2 && in(0) instanceof CallNode call ) { + Node fptr = call.fptr(); + if( fptr.isConst() && // We have an immediate call + // Function is being called, and its not-null + fptr._type instanceof TypeFunPtr tfp && tfp.notNull() && !tfp.isHigh() && + // Arguments are correct + call.nargs()==tfp.nargs() && goodArgs(call,tfp) ) { + ReturnNode ret = (ReturnNode)in(1); + FunNode fun = ret.fun(); + int isTrivial = trivialInlining( fptr, fun ); + + // Encouraged inlining because small size and constructor. + int maxSize = fun._name!=null && fun.isInit() && !fun.isClz() ? 200 : 100; + if( isTrivial==1 && fun._approxUIDs < maxSize ) { + assert fun.sig().isa(tfp); + assert !CodeGen.CODE._midAssert; // Triggered inlining + CodeGen.CODE.add(fun); + // Remove the existing function linkage + call.unlink_all(); + // Clone the function body + FunNode fun2 = fun.copyBody(); + // Call uses the unique new function + Node fptr2 = new FunPtrNode(fun2.sig(),CodeGen.CODE._start,fun2.ret()).peephole(); + call.setDef(call.nIns()-1,fptr2); + // Link to the new function + call.link(fun2); + fun = fun2; + assert trivialInlining( fptr2, fun2 )==0; + isTrivial = 0; + } + + // Trivial inlining: call site calls a single function; single function + // is only called by this call site. + if( isTrivial==0 ) + return doTrivialInlining(fun); + + } else { // Function not (yet) a constant function pointer + addDep(fptr); + } + } + + return null; + } + + boolean goodArgs( CallNode call, TypeFunPtr tfp ) { + for( int i=0; i<tfp.nargs(); i++ ) { + Node arg = call.arg(i+2); + // The graph's function-pointer type can still contain open forward + // references here. Compare against its recursively upgraded form, + // so deep mutability requirements participate before inlining. + Type formal = tfp.arg(i).upgradeType(Parser.TYPES); + if( !arg._type.isa(formal) ) + { addDep(arg); return false; } + } + return true; + } + + + // Check for trivial inlining: call only calls fun; fun only called by + // call. Returns 0 for trivial, +1 for not-trivial because fptr/fun, and + // -1 for not-trivial because idoms. + public int trivialInlining( Node fptr, FunNode fun ) { + // Heuristic forced inlining off via name + if( fun._name != null && + fun._name.endsWith("_noInline") ) + return -1; + + if( !fun.sig().isa(fptr._type) ) + return -1; // Stall until these align + + // Disallow self-recursive inlining (loop unrolling by another name). + for( int i=1; i < fun.nIns(); i++ ) + if( fun.cfg(i).fun()==fun ) // Check for linked call input inside "fun" + { addDep(fun); return -1; } + + CFGNode idom = call(), prior = this; + while( !(idom instanceof FunNode) ) { + if( idom==null ) { + addDep(prior); + if( prior instanceof RegionNode && !(prior instanceof StartNode) ) + for( int i=1; i<prior.nIns(); i++ ) + addDep(prior.in(i)); + return -1; + } + prior = idom; + idom = idom.idom(); + } + + + // If the *inlined* function is mid-collapse, also do not inline (yet) + idom = fun.ret(); + while( idom != fun ) { + if( idom==null ) return -1; // Forced off, half-folded call + if( idom instanceof FunNode fun2 ) + { addDep(fun2); return -1; } + if( idom instanceof CallEndNode cend && cend._folding ) + { addDep(cend); return -1; } + idom = idom.idom(); + } + + // Only fun user is this call + if( fun.nIns() > 2 ) { addDep(fun); return 1; } + // Trivial inlining: call site calls a single function; single function + // is only called by this call site. + assert fun.in(1)==call(); + + return 0; + } + + + // Do trivial inlining. Inlining does not need to clone code, merely + // triggers folding the Call/Fun and Return/CallEnd away. + private Node doTrivialInlining( FunNode fun ) { + assert !CodeGen.CODE._midAssert; // Triggered inlining + // Add the size heuristic to grow caller, preventing self-recursive + // functions from endlessly inlining. + fun()._approxUIDs += fun._approxUIDs; + // Trivial inline: rewrite + _folding = true; + // Rewrite Fun so the normal RegionNode ideal collapses + fun._folding = true; + fun.setDef(1,call().ctrl());// Bypass the Call; + fun.ret().setDef(3,null); // Return is folding also + + CodeGen.CODE.addAll(fun._outputs); + // Repeat defs 1 layer down, for users of Parm (Phis) + for( Node parm : fun._outputs ) + if( parm instanceof ParmNode ) + CodeGen.CODE.addAll(parm._outputs); + + // Inlining immediately blows all cache idepth fields past the inline point. + // Bump the global version number invalidating them en-masse. + CodeGen.CODE.invalidateIDepthCaches(); + return this; + } + + + @Override public Node pcopy(int idx) { + return _folding ? in(1).in(idx) : null; + } + + @Override public Node copy() { + return _folding ? super.copy() : new CallEndNode(_rpc); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java new file mode 100644 index 00000000..f2825650 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -0,0 +1,192 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.type.TypeMem; +import com.seaofnodes.simple.type.XInt; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * Call + */ +public class CallNode extends CFGNode { + + // Source location for late reported errors + public final Parser.Lexer _loc; + + public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public CallNode(CallNode call) { super(call); _loc = call._loc; } + @Override public Tag serialTag() { return Tag.Call; } + public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); } + static Node make( BAOS bais ) { return new CallNode(null,new Node[bais.packed1()]); } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + String fname = name(); + if( fname == null ) fptr()._print0(sb,visited); + else sb.append(fname); + sb.append("( "); + for( int i=2; i<nIns()-1; i++ ) + in(i)._print0(sb,visited).append(","); + sb.setLength(sb.length()-1); + return sb.append(")"); + } + public String name() { + if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() ) { + FunNode fun = CodeGen.CODE.lookupFun(tfp); + if( fun !=null ) return fun._name; + if( fptr() instanceof ExternNode ex ) return ex._extern; + } + return null; + } + + + + Node ctrl() { return in(0); } + Node mem () { return in(1); } + // Args mapped 1-to-1 on inputs, so conceptually start at 2 + public Node arg(int idx) { return in(idx); } + // Same arg accounting as TFPs, although the numbering starts at 2 + public int nargs() { return nIns()-3; } // Minus control, memory, fptr + // args from input 2 to last; last is function input + public Node fptr() { return _inputs.last(); } + // Error if not a TFP + public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; } + + // Call is to an externally supplied code + public boolean external() { return false; } + + + // Find the Call End from the Call + public CallEndNode cend() { + // Always in use slot 0 + if( nOuts()>0 && out(0) instanceof CallEndNode cend ) { + assert _cend()==cend; + return cend; + } else { + assert _cend()==null; + return null; + } + } + private CallEndNode _cend() { + CallEndNode cend=null; + for( Node n : _outputs ) + if( n instanceof CallEndNode cend0 ) + { assert cend == null; cend = cend0; } + return cend; + } + + // Get the one control following; error to call with more than one e.g. an + // IfNode or other multi-way branch. + @Override public CFGNode uctrl() { return cend(); } + + + @Override + public Type compute() { + return ctrl()._type; + } + + @Override + public Node idealize() { + CallEndNode cend = cend(); + if( cend==null ) return null; // Still building + + // Link: call calls target function. Linking makes the target FunNode + // point to this Call, and all his Parms point to the call arguments; + // also the CallEnd points to the Return. + Node progress = null; + if( fptr()._type instanceof TypeFunPtr tfp && tfp.nargs() == nargs() ) { + // If fidxs is negative, then infinite unknown functions + int[] fidxs = tfp.fidxs(); + if( !XInt.isHigh(fidxs) ) { + // Walk the bits and link + for( int fidx = XInt.next(fidxs,0); fidx > 0; fidx = XInt.next(fidxs,fidx) ) { + FunNode fun = CodeGen.CODE.link(fidx); + if( fun!=null && !fun._folding && !linked(fun) ) + progress = link(fun); + } + } + } + + return progress; + } + + // True if Fun is linked to this Call + public boolean linked( FunNode fun ) { + for( Node n : fun._inputs ) + if( n == this ) + return true; + return false; + } + + // Link so this calls fun + public Node link( FunNode fun ) { + assert !linked(fun); + fun.addDef(this); + for( Node use : fun._outputs ) + if( use instanceof ParmNode parm ) + parm.addDef(parm._idx==0 ? ConstantNode.seed(cend()._rpc).peephole() : arg(parm._idx)); + // Call end points to function return + CodeGen.CODE.add(cend()).addDef(fun.ret()); + return this; + } + + // Unlink a single function + public void unlink( FunNode fun, int path ) { + assert linked(fun); + for( Node use : fun._outputs ) + if( use instanceof ParmNode ) + use.delDef(path); + fun.delDef(path); + CodeGen.CODE.add(fun); + CallEndNode cend = cend(); + ReturnNode ret = fun.ret().keep(); + cend.delDef(cend._inputs.find(ret)); + CodeGen.CODE.add(cend); + assert !linked(fun); + ret.unkeep(); + } + + + // Unlink all linked functions + public void unlink_all() { + for( int i=0; i<_outputs._len; i++ ) + if( out(i) instanceof FunNode fun ) { + int path = fun._inputs.find(this); + unlink(fun,path); + i--; + } + } + + @Override + public Parser.ParseException err() { + if( fptr()._type == Type.BOTTOM ) + return null; // Wrong, but failing earlier + if( !(fptr()._type instanceof TypeFunPtr tfp) ) + throw Utils.TODO(); + if( !tfp.notNull() ) + return Parser.error( "Might be null calling "+tfp, _loc); + if( nargs() != tfp.nargs() ) + return Parser.error( "Expecting "+tfp.nargs()+" arguments, but found "+nargs(), _loc); + + // Check for args + for( int i=0; i<tfp.nargs(); i++ ) + if( !arg(i+2)._type.isa(tfp.arg(i)) ) { + // Constructors return private memory and carry hidden self and + // selfMem arguments ahead of the source-level arguments. + int argno = i - (tfp.ret() instanceof TypeMem ? 2 : 0); + return Parser.error( "Argument #" + argno + " isa " + arg(i+2)._type + ", but must be a " + tfp.arg(i), _loc ); + } + + if( tfp.isHigh() ) + throw Utils.TODO(); // Infinite unknown TFPs? Should be fairly precise CG + + return null; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java new file mode 100644 index 00000000..0afc5827 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java @@ -0,0 +1,23 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.type.Type; + +public class CalleeSaveNode extends ProjNode implements MachNode { + final RegMask _mask; + public CalleeSaveNode(FunNode fun, int reg, String label) { + super(fun,reg,label); + fun._outputs.pop(); + int i=0; + while( fun.out(i) instanceof PhiNode ) i++; + fun._outputs.insert(this,i); + _mask = new RegMask(reg); + } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return _mask; } + @Override public void encoding( Encoding enc ) { } + + @Override public Type compute() { return Type.BOTTOM; } + @Override public Node idealize() { return null; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CheckCastMach.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CheckCastMach.java new file mode 100644 index 00000000..9943c7b4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CheckCastMach.java @@ -0,0 +1,20 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; + +/** + * Cast a pointer to read-only + */ +public class CheckCastMach extends CheckCastNode implements MachNode { + public CheckCastMach( CheckCastNode n ) { super(n); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress( ) { return in(1)._type==Type.BOTTOM ? 0 : 1; } + @Override public void encoding( Encoding enc ) { } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CheckCastNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CheckCastNode.java new file mode 100644 index 00000000..900ee70e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CheckCastNode.java @@ -0,0 +1,82 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +// Upcast (join) the input to a t. Used after guard test to lift an input. +// Can also be used to make a type-assertion if ctrl is null. +public class CheckCastNode extends TypeNode { + public CheckCastNode(Type t, Node ctrl, Node in) { super(t,ctrl, in); } + + public CheckCastNode(CheckCastNode c) { + super(c); // Call parent copy constructor + setType(compute()); // Ensure type is recomputed + } + @Override public Tag serialTag() { return Tag.CheckCast; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed2(types.get(_con)); // NPE if fails lookup + } + static Node make( BAOS bais, Type[] types) { return new CheckCastNode(types[bais.packed2()], null, null); } + + @Override public String label() { return "("+_con.str()+")"; } + + @Override + public String uniqueName() { return "CheckCast_" + _nid; } + + @Override public boolean isPinned() { return in(0)!=null; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return in(1)._print0(sb.append(label()), visited); + } + + @Override + public Type compute() { + // Cast array to int + Type t1 = in(1)._type; + if( _con == TypeInteger.BOT && t1 instanceof TypeMemPtr tmp && tmp._obj.isAry() ) + return _con; + + // If unconditional, it must collapse (or error) + if( in(0)==null ) + return _con; + // If conditional, return the join. The guard test ensures + // the join is correct. + return t1.join(_con); + } + + @Override + public Node idealize() { + return in(1)._type.isa(_con) ? in(1) : null; + } + + @Override + public Parser.ParseException err() { + // Has a condition to test, so OK + if( in(0) != null ) return null; + if( in(1)._type != Type.BOTTOM ) + // No condition to test, so this must optimize away + return Parser.error( "Type " + in(1)._type.str() + " is not of declared type " + _con.str(), null ); + // Only happens for un-initialized refs that must init. + // Allow uses of initializing stores, but other uses represent a + // partially initialized field, and should error + boolean bad = false; + StoreNode init=null; + for( Node use : _outputs ) + if( (use instanceof StoreNode st && st._init && !((TypeMemPtr)st.ptr()._type)._obj._name.startsWith("class") ) ) + init = st; + else if( (use instanceof StoreNode st && st.val() instanceof CheckCastNode cast && cast.in(1)._type == Type.BOTTOM) ) + /*bogus init store in constructor*/; + else bad = true; + if( !bad ) return null; + if( init==null ) return null; // Error somewhere else + // Expecting an init store + TypeMemPtr ptr = (TypeMemPtr)init.ptr()._type; + throw Parser.error("'"+ptr._obj._name+"' is not fully initialized, field '" + init._name + "' is only partially set in the constructor", init._loc); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java new file mode 100644 index 00000000..11a16d28 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java @@ -0,0 +1,73 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeStruct; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; + +/** + * A field offset. This becomes a constant *after* other optimizations which + * determine field sizes - some fields might be dead, or hold only zeros or + * small values. + */ + +public class ConFldOffNode extends ConstantNode { + public TypeStruct _ts; // Struct holding field + public final String _fname; // Field name + public ConFldOffNode( TypeStruct ts, String fname ) { super(TypeInteger.BOT); _ts = ts; _fname = fname; } + @Override public Tag serialTag() { throw Utils.TODO("should not reach here"); } + + @Override public String label() { + return _fname == " len" ? "sizeof("+_ts._name+")" : "#"+_fname; + } + @Override public String glabel() { return label(); } + @Override public String uniqueName() { return "Off_" + _nid; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return sb.append(label()); + } + + @Override public Node idealize() { + // Some array offsets are known early + if( _ts.isAry() ) { + // Length always at offset zero + if( _fname==" len" ) + return CodeGen.CODE.ZERO; + // The shift depends on the element + if( _fname=="<<" ) + return Node.con(_ts.aryScale()); + // The base depends on the layout (alignment for large elements may + // involve padding) and MAY depend on the length if I go for + // smaller array lengths. + } + return null; + } + + // Convert field offset to an integer + public Node asOffset() { + int fldx = _fname==" len" ? _ts._fields.length : _ts.find(_fname); + if( fldx == -1 ) return null; + return ConstantNode.make(TypeInteger.constant(_ts.offset(fldx))).peephole(); + } + + // Upgrade the internal type + @Override boolean _upgradeType( HashMap<String,Type> TYPES) { + boolean progress = super._upgradeType(TYPES); + TypeStruct ts = (TypeStruct)_ts.upgradeType(TYPES); + if( ts == _ts ) return progress; + _ts = ts; + return true; + } + + @Override public boolean eq(Node n) { + ConFldOffNode off = (ConFldOffNode)n; // Invariant + return _ts._name==off._ts._name && _fname.equals(off._fname) && super.eq(n); + } + @Override int hash() { return super.hash() * _ts._name.hashCode() * _fname.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java new file mode 100644 index 00000000..feed8c5c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java @@ -0,0 +1,88 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.BAOS; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * A Constant node represents a constant value. + * <p> + * Constants have no semantic inputs. However, we set Start as an input to + * Constants to enable a forward graph walk. This edge carries no semantic + * meaning, and it is present <em>solely</em> to allow visitation. + * <p> + * The Constant's value is the value stored in it. + */ + +public class ConstantNode extends TypeNode { + public ConstantNode( Type type ) { + this(type,false); + } + protected ConstantNode( Type type, boolean raw ) { + super(type,new Node[]{CodeGen.CODE._start}); + assert raw || !(type instanceof TypeFunPtr tfp && tfp.isConstant()) : + "Unique function identities require a Return-linked FunPtrNode"; + _type = type; + } + public ConstantNode( ConstantNode con ) { super(con); } + @Override public Tag serialTag() { return Tag.Con; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed2(types.get(_con)); // NPE if fails lookup + } + static Node make( BAOS bais, Type[] types) { return raw(types[bais.packed2()]); } + + // Semantic constant construction through Opto. A unique internal + // function identity carries a lifetime edge to the function's Return. + public static Node make( Type type ) { + if( type==Type. CONTROL ) return new CtrlNode(); + if( type==Type.XCONTROL ) return new XCtrlNode(); + if( CodeGen.CODE._phase != null && + CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) + return raw(type); + if( type instanceof TypeFunPtr tfp && tfp.isConstant() ) { + FunNode fun = CodeGen.CODE.lookupFun(tfp); + if( fun != null ) + return new FunPtrNode(tfp,CodeGen.CODE._start,fun.ret()).init(); + // TODO: A singleton FIDX can also emerge after merging and + // guarding several function pointers. Preserve that identity + // even if its internal Return is no longer available. + return raw(type); + } + return new ConstantNode(type); + } + + // Exact construction for deserialization and graph-incomplete machine + // selection. No semantic classification, compute, idealize, or GVN. + public static ConstantNode raw( Type type ) { return new ConstantNode(type,true); } + public static ConstantNode raw( ConstantNode con ) { return new ConstantNode(con); } + + // Analysis input created from whole cloth, rather than a first-class + // runtime value. In particular, a constructor call descriptor does not + // own the constructor function; the class FunPtr does. + public static ConstantNode seed( Type type ) { return new ConstantNode(type,true); } + + @Override public String label() { return "#"+_con; } + @Override public String glabel() { return "#"+_con.gprint(); } + @Override public String uniqueName() { return "Con_" + _nid; } + @Override public Node copy() { return raw(this); } + + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( _con instanceof TypeFunPtr tfp && tfp.isConstant() ) { + FunNode fun = CodeGen.CODE.lookupFun(tfp); + if( fun!=null && fun._name != null ) + return sb.append("{ ").append(fun._name).append("}"); + } + return sb.append(_con==null ? "---" : _con.toString()); + } + + @Override public boolean isConst() { return true; } + @Override public Type compute() { return _con; } + @Override public Node idealize() { return null; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java new file mode 100644 index 00000000..4e8e93ce --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java @@ -0,0 +1,96 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; + +import java.util.BitSet; +import java.util.HashMap; + +// Convert a value to an authoritative destination type. The destination is +// known structurally from a declaration; the source family might sharpen later. +public class ConvertNode extends Node { + private Type _dst; + + public ConvertNode(Type dst, Node val) { + super(null,val); + // Folding this conversion would replace the Return-linked FunPtrNode + // with an unhooked ConstantNode. + assert !(dst instanceof TypeFunPtr && val instanceof FunPtrNode); + _dst = dst; + } + + public Type dst() { return _dst; } + public Node val() { return in(1); } + + @Override public String label() { return "Convert_"+_dst.str(); } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return val()._print0(sb.append(label()).append("("),visited).append(")"); + } + + // The declaration fixes the result family during pessimistic parsing. + // Optimistic SCCP must retain stronger facts already inside that family. + @Override public Type compute() { + Type src = val()._type; + if( CodeGen.CODE._phase == CodeGen.Phase.Opto ) { + if( src.isHigh() ) return Type.TOP; + if( src.isa(_dst) ) return src; + } + return _dst; + } + + @Override public Node idealize() { + Type src = val()._type; + if( src==Type.BOTTOM || src==Type.TOP ) return null; + if( src.isa(_dst) ) return val(); + + // Managed arrays are represented by an object pointer, while C wants + // the address of the first element. The representation cast itself is + // zero-code; the Add skips the array header. + if( _dst==TypeInteger.BOT && src instanceof TypeMemPtr tmp && tmp._obj.isAry() ) + return new AddNode(new PtrToIntNode(val()).peephole(),Parser.off(tmp._obj,"[]")); + + // Integer (and nil-as-zero) to floating point. + if( (src instanceof TypeInteger || src==Type.NIL) && _dst instanceof TypeFloat ) + return new ToFloatNode(val()); + + // Narrow integers produce the declared sign/zero extension. + if( src instanceof TypeInteger && _dst instanceof TypeInteger dst ) { + if( dst._min==0 ) + return new AndNode(null,val(),con(dst._max)); + int shift = Long.numberOfLeadingZeros(dst._max)-1; + Node shf = con(shift); + if( shf._type==TypeInteger.ZERO ) return val(); + return new SarNode(null,new ShlNode(null,val(),shf.keep()).peephole(),shf.unkeep()); + } + + // Narrow f64 to f32. + if( src instanceof TypeFloat && _dst instanceof TypeFloat ) + return new RoundF32Node(val()); + + // Wait for more source information, or report an error after Opto. + return null; + } + + @Override boolean _upgradeType(HashMap<String,Type> TYPES) { + Type dst = _dst.upgradeType(TYPES); + if( dst==_dst ) return false; + unlock(); + _dst = dst; + return true; + } + + @Override public Parser.ParseException err() { + if( val()._type==Type.BOTTOM || + val()._type instanceof TypeNil src && src.nullable() && _dst instanceof TypeNil dst && dst.notNull() ) { + Node out = out(0); + if( out instanceof PhiNode phi ) out = phi.out(0); + if( out instanceof StoreNode st ) + return Parser.error( "'" + ((TypeMemPtr)st.ptr()._type)._obj._name + "' is not fully initialized, field '" + st._name + "' is only partially set in the constructor", null ); + } + return Parser.error("Type "+val()._type.str()+" is not of declared type "+_dst.str(),null); + } + + @Override public boolean eq(Node n) { return _dst==((ConvertNode)n)._dst; } + @Override int hash() { return _dst.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CtrlNode.java new file mode 100644 index 00000000..39c95aa4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CtrlNode.java @@ -0,0 +1,16 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; + +public class CtrlNode extends CFGNode { + public CtrlNode() { super(CodeGen.CODE._start); } + @Override public Tag serialTag() { throw com.seaofnodes.simple.util.Utils.TODO(); } + @Override public String label() { return "Ctrl"; } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); } + @Override public boolean isConst() { return true; } + @Override public Type compute() { return Type.CONTROL; } + @Override public Node idealize() { return null; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/DivNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/DivNode.java new file mode 100644 index 00000000..dcc9a27a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/DivNode.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeFloat; +import java.util.BitSet; + +public class DivNode extends ArithNode { + @Override boolean allowFloat() { return true; } + public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public DivNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); } + @Override public Tag serialTag() { return Tag.Div; } + + @Override public String op() { return _mode==1 ? "//" : "/"; } + + @Override long doOp( long x, long y ) { return y==0 ? 0 : x / y; } + @Override double doOp(double x, double y ) { return x / y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { + return TypeInteger.BOT; + } + + @Override + public Node idealize() { + // Div of 1. + if( in(2)._type == TypeInteger.TRUE ) + return in(1); + + // Div of constant + if( _mode==2 ) { + if( in(2)._type == TypeFloat.constant(1.) ) + return in(1); + if( in(2)._type instanceof TypeFloat f && f.isConstant() ) + return new MulNode(in(1),ConstantNode.make(TypeFloat.constant(1.0/f.value())).peephole(),_mode); + } + + return super.idealize(); + } + + @Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs,_mode); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/EscapeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/EscapeNode.java new file mode 100644 index 00000000..df654291 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/EscapeNode.java @@ -0,0 +1,114 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.*; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * Merge public and private aliases. + * 0 - control + * 1 - private pointer + * 2 - private alias + * 3 - public alias + */ +public class EscapeNode extends TypeNode { + + public EscapeNode(Field fld, Node self, Node priv, Node pub ) { + super(fld,null,self,priv,pub); + } + public EscapeNode(EscapeNode esc ) { + super(esc); // Copy constructor + // Except we do not want/need self ptr past Opto. + // *NOTE* the weird direct stomp; this is correct because this + // constructor does NOT set the backedges, because it is used to + // copy-construct an entire graph and ALL edges will get rewritten. + _inputs.set(1,null); + _con = esc._con; + } + + // Pointer to some private (unescaped) memory from a NewNode + public Node self() { return in(1); } + public Node priv() { return in(2); } + public Node pub () { return in(3); } + public Field fld() { return (Field)_con; } + + @Override public Tag serialTag() { return Tag.Escape; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed2(types.get(_con)); + } + static Node make( BAOS bais, Type[] types) { + return new EscapeNode((Field)types[bais.packed2()], null,null,null); + } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("Esc#").append(fld()._alias).append(" {"); + if( self()==null ) sb.append("---"); + else self()._print0(sb,visited).append(","); + priv()._print0(sb,visited).append("}, "); + pub ()._print0(sb,visited); + return sb; + } + + @Override + public Type compute() { + if( priv()._type.isHigh() ) return TypeMem.TOP; + if( pub ()._type.isHigh() ) return TypeMem.TOP; + TypeMem mpriv = (TypeMem)priv()._type; + TypeMem mpub = (TypeMem)pub ()._type; + // Private memory comes from an allocation or other non-aliased source. + // Public memory comes from anywhere, might be bulk memory with a very weak mem type. + Field fld = fld(); + assert mpriv._alias==1 || mpriv._alias==fld._alias; + assert mpub ._alias==1 || mpub ._alias==fld._alias; + Type tpriv = mpriv._t instanceof TypeStruct ts && ts.findAlias(fld._alias)!=-1 + ? ts.at(ts.findAlias(fld._alias))._t + : mpriv._t; + assert !(mpub._t instanceof TypeStruct); // Never expect this, but if it starts to happen, need to optimize + Type tpub = mpub._t; + + // t is meet of pub & priv. Pub memory can be very weak (e.g. alias 1) + // and we're no worse than GLB of private + Type t = tpriv.meet(tpub).join(fld._t); + int[] fidxes = XInt.meet(mpriv._escFs, mpub._escFs); + int[] aliases = XInt.meet(mpriv._escAs, mpub._escAs); + return TypeMem.make(fld._alias, t, false, false, fld._final, fidxes, aliases).escapesFrom(t); + } + + @Override public Node idealize() { + // Sharpen private memory input + if( priv() instanceof MemMergeNode mmm ) { + setDef(2,mmm.alias(fld()._alias)); + return this; + } + // Sharpen public memory input + if( pub() instanceof MemMergeNode mmm ) { + setDef(3,mmm.alias(fld()._alias)); + return this; + } + // New allocation is dead + if( self()._type.isHigh() ) + return pub(); + return null; + } + +// Upgrade the internal type +@Override boolean _upgradeType( HashMap<String,Type> TYPES) { + Type t = _con.upgradeType(TYPES); + if( t == _con ) return false; + unlock(); // Unlock before changing _con + _con = t; + return true; +} + + @Override public boolean eq(Node n) { + if( fld()._alias!=((EscapeNode)n).fld()._alias ) + return false; + assert _con == ((EscapeNode)n)._con; + return true; + } + + @Override int hash() { return _con.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ExternNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ExternNode.java new file mode 100644 index 00000000..9768f41f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ExternNode.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.Serialize; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.SB; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + A constant with external linkage. + */ + +public class ExternNode extends ConstantNode { + public final String _extern; + // An exact external function identity has no local FunNode/Return to own. + // Its lifetime and eventual relocation are carried by the extern symbol + // and CodeGen's FIDX-to-extern mapping instead. + public ExternNode(Type t, String ex) { super(t,true); _extern = ex; } + @Override public Tag serialTag() { return Tag.Extern; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed2(types.get(_con )); + baos.packed2( strs.get(_extern)); + } + static Node make( BAOS bais, String[] strs, Type[] types) { + return new ExternNode(types[bais.packed2()], strs[bais.packed2()]); + } + + @Override public String label() { return "#"+_con+":"+_extern; } + @Override public String glabel() { return _con.print(new SB().p("#"), new BitSet(), true).p(":").p(_extern).toString(); } + @Override public String uniqueName() { return "Extern_" + _nid; } + + @Override public boolean eq(Node n) { return this==n; } + @Override public void gather( HashMap<String,Integer> strs ) { + Serialize.gather(strs,_extern); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FRefNode.java new file mode 100644 index 00000000..723e0007 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FRefNode.java @@ -0,0 +1,45 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Var; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; + +/** + * A Forward Reference. Its any final constant, including functions. When + * the Def finally appears its plugged into the forward reference, which then + * peepholes to the Def. + + * FRefs can be external references, to other Simple defined values in ELF + * files, or external FFI/C libs. If external, a file system lookup needs to + * find it in another file, and the name (one time) change to match the found + * location. + * + * Example, a ambiguous name "foo" is looked up externally; it might resolve + * to "A.B.foo" or "libc.foo" or the like; the name is changed to match. + */ +public class FRefNode extends ConstantNode { + public static final Type FREF_TYPE = Type.BOTTOM; + public String _name; + public final Parser.Lexer _loc; // Source location + + public FRefNode( String name, Parser.Lexer loc ) { super(FREF_TYPE); _name = name; _loc = loc; } + + @Override public String label() { return "FRef"+_name; } + + @Override public String uniqueName() { return "FRef_" + _nid; } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return sb.append("FRef_").append(_name); + } + + @Override public Node idealize() { + // When FRef finds its definition, idealize to it + return nIns()==1 ? null : in(1); + } + + public Parser.ParseException err() { return Parser.error("Undefined name '"+_name+"'",_loc); } + + @Override public boolean eq(Node n) { return this==n; } + @Override int hash() { return _name.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/FldOffNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FldOffNode.java new file mode 100644 index 00000000..3a44f588 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FldOffNode.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; + +/** + * A field offset. This becomes a constant *after* other optimizations which + * determine field sizes - some fields might be dead, or hold only zeros or + * small values. + */ + +public class FldOffNode extends TypeNode { + public final String _fname; // Field name + public final Parser.Lexer _loc; // + public FldOffNode( Node base, String fname, Parser.Lexer loc ) { super(TypeInteger.BOT,base); _fname = fname; _loc=loc; } + @Override public Tag serialTag() { throw Utils.TODO("should not reach here"); } + + @Override public String label() { + return _fname == " len" ? "sizeof()" : "#"+_fname; + } + @Override public String glabel() { return label(); } + @Override public String uniqueName() { return "Off_" + _nid; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return sb.append(label()); + } + + @Override public Type compute() { + return Type.BOTTOM; + } + + @Override public Node idealize() { + // When base() gets typed, this xforms into a ConFldOffNode + if( in(0)._type instanceof TypeMemPtr tmp ) + return new ConFldOffNode(tmp._obj,_fname); + return null; + } + + @Override public boolean eq(Node n) { + FldOffNode off = (FldOffNode)n; // Invariant + return _fname.equals(off._fname) && super.eq(n); + } + @Override int hash() { return super.hash() * _fname.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java new file mode 100644 index 00000000..a9d03543 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -0,0 +1,448 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; +import static com.seaofnodes.simple.codegen.CodeGen.CODE; + +public class FunNode extends RegionNode { + + // When set true, this Call/CallEnd/Fun/Return is being trivially inlined + public boolean _folding; + + private TypeFunPtr _sig; // Initial signature + private ReturnNode _ret; // Return pointer + + public String _name; // Debug name + + public int _approxUIDs; // Approximate function size, used as a inlining heuristic + public CompUnit _compunit; // Defined in source file + public final boolean _extern; // External function; no code, name only + + private FunNode( Parser.Lexer loc, Node[] nodes, TypeFunPtr sig, String name, CompUnit compunit, boolean ext ) { + super(loc,nodes); + _name = name; + _sig = sig; + _extern = ext; + _compunit = compunit; + } + public FunNode( Parser.Lexer loc, TypeFunPtr sig, String name, CompUnit compunit, Node... nodes ) { this(loc,nodes,sig,name,compunit,false); } + public FunNode( TypeFunPtr sig, String name, boolean ext ) { + this(null,null,sig,name,null,ext); + assert ext; // No need for ext + throw Utils.TODO(); + } + public FunNode( FunNode fun ) { + super( fun, fun._loc ); + _sig = fun.sig(); + _name = fun._name; + _compunit = fun._compunit; + _extern = fun._extern; + } + @Override public Tag serialTag() { return Tag.Fun; } + @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes ) { + assert !_folding; + baos.packed1(nIns()); // Number of linked calls + baos.packed2(types.get(_sig)); // NPE if fails lookup + baos.packed2(_name==null ? 0 : strs.get(_name)); + baos.packed2(_approxUIDs); + } + static Node make( BAOS bais, String[] strs, Type[] types) { + Node[] ins = new Node[bais.packed1()]; + TypeFunPtr sig = (TypeFunPtr)types[bais.packed2()]; + String name = strs[bais.packed2()]; + FunNode fun = new FunNode(null,sig,name,null,ins); + fun._approxUIDs = bais.packed2(); + return fun; + } + + @Override public String label() { return _name == null ? "$fun"+_sig.fidx() : _name; } + + // Find the one CFG user from Fun. It's not always the Return, but always + // the Return *is* a CFG user of Fun. + @Override public CFGNode uctrl() { + for( Node n : _outputs ) + if( n instanceof CFGNode cfg && + (cfg instanceof RegionNode || cfg.cfg0()==this) ) + return cfg; + return null; + } + + public ParmNode parm(int idx) { + ParmNode pidx = null; + for( Node n : _outputs ) + if( n instanceof ParmNode parm && parm._idx==idx ) + { assert pidx==null; pidx=parm; } + return pidx; + } + public ParmNode rpc() { return parm(0); } + + // Cannot create the Return and Fun at the same time; one has to be first. + // So setting the return requires a second step. + public void setRet(ReturnNode ret) { _ret=ret; } + public ReturnNode ret() { assert _ret!=null; return _ret; } + + // Signature can improve over time + public TypeFunPtr sig() { return _sig; } + public void setSig( TypeFunPtr sig ) { + assert sig.isa(_sig); + if( _sig != sig ) { + CODE.add(this); + // Changing the signature can allow more inlining + if( _ret != null ) + for( Node use : ret().outs() ) + if( use instanceof CallEndNode || use instanceof FunPtrNode ) + CODE.add(use); + _sig = sig; + unlock(); + } + } + + @Override boolean _upgradeType( HashMap<String,Type> TYPES) { + TypeFunPtr sig = (TypeFunPtr)_sig.upgradeType(TYPES); + if( sig == _sig ) return false; + unlock(); + _sig = sig; + return true; + } + + public void setName( String name ) { + if( _name==null ) + _name = _compunit==null || _compunit._cname==null || _compunit._cname.indexOf('.')==-1 + ? name + : (_compunit._cname+"."+name).intern(); + } + + @Override + public Type compute() { + // Only dead if no callers after SCCP + if( unknownCallers() ) + return Type.CONTROL; + Type t = Type.XCONTROL; + for( int i = 1; i < nIns(); i++) { + // Since there are no unknown callers, the Start input is only a + // fake hook and will be a Tuple with XControl, so ignore it. Need + // to be called from someplace other than Start. + if( !(in(i) instanceof StartNode) ) + t = t.meet(in(i)._type); + } + return t; + } + + @Override + public Node idealize() { + + // Some linked path dies, except unknown caller hooks never kill Start. + Node progress = deadPath(unknownCallers()); + if( progress!=null ) { + if( nIns()==2 && in(1) instanceof CallNode call ) + CODE.add(call.cend()); // If no Start and one call, check for inline + return progress; + } + + // Upgrade inferred or user-written return type to actual + if( _ret!=null && _ret._type instanceof TypeTuple tt && + tt.ret() != _sig.ret() && tt.ret().isa(_sig.ret()) ) { + setSig(_sig.makeFrom(tt.ret())); + return this; + } + + // Attempt to get rid of the unknown caller. + // - Must be past Parser, which invents new calls "from whole cloth". + // - FIDX is not in the escape set + // - Not called anywhere else; post Opto called-not-escaped functions need to still be hooked to start + if( in(1) instanceof StartCUNode start ) { + boolean preOpto = CodeGen.CODE._phase == null || + CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal(); + if( !start.escapedFIDX(_sig.fidx()) && nIns()<=2 && _ret!=null && + !(preOpto && hasFunPtr()) ) { + _compunit._stop.delDef(_compunit._stop._inputs.find(ret())); + return removeDeadPath(1); + } else { + addDep(start); + } + } + + + // If no default/unknown caller, use the normal RegionNode ideal rules + // to collapse + if( unknownCallers() ) return null; + + // If folding (already inlined) down to a single input, become that input. + // Cannot collapse if not-inlining, and might not inline if call site + // calls multiple targets. + if( _folding && nIns()==2 && !hasPhi() ) { + CODE.add( CODE._stop ); // Stop will remove dead path + CODE.add( _ret ); // Return will compute to TOP control + return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own + } + + return null; + } + + // Before SCCP has discovered all Call edges, a live function pointer is + // enough reason to retain the unknown-caller Start edge. + private boolean hasFunPtr() { + for( Node use : ret().outs() ) + if( use instanceof FunPtrNode && !use.isDead() ) { + addDep(use); + return true; + } + return false; + } + + // Bypass Region idom, always assume depth == 1, one more than Start, + // unless folding + @Override public int idepth() { + if( _folding ) return super.idepth(); + return CodeGen.CODE.iDepthAt(1); + } + + // Bypass Region idom, always assume idom is Start + @Override public CFGNode idom(Node dep) { + return _folding ? cfg(1) : null; + } + + // Always in-progress until we run out of unknown callers + public boolean unknownCallers() { return nIns()>=2 && in(1) instanceof StartNode; } + + public boolean isModInit( ) { + // The one top-level <clinit> with no internal dots: + // "sys.<clinit>" is a module, but "sys.io.<clinit>" is not. + return isClz() && _name.indexOf('.')==_name.lastIndexOf('.'); + } + public boolean isClz ( ) { return isClz (_name); } + public boolean isInit( ) { return isInit(_name); } + public boolean isInstance() { return isInstance(_name); } + public static boolean isClz (String name ) { return name!=null && name.endsWith(".<clinit>"); } + public static boolean isInit(String name ) { return name!=null && name.endsWith("init>"); } + public static boolean isInstance(String name ) { return name!=null && name.endsWith(".<init>"); } + + // Function is public (callable from Start directly). + public boolean isPublic( ) { + // Never true for anonymous functions + if( _name == null ) return false; + // Only public for <clinit> and <init>, all others + // are found via field loads. The <init> Start hook also supplies its + // conservative pre-Opto construction inputs; StopCU separately keeps + // its private-memory return from escaping as a public result. + if( !isInit() ) return false; + // Private class, has to be found via program text, not linker + if( _name.charAt(0)=='_' ) return false; + + // No instances of "...clz1._clz2..." where the class name path + // includes a private class. + if( _name.indexOf("._") != -1 ) return false; + + // Public class <cl/init> + return true; + } + + @Override public boolean inProgress() { + return unknownCallers() && CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal(); + } + + // Build the function body set + public BitSet body() { + // Reverse up (stop to start) CFG only, collect bitmap. + BitSet cfgs = new BitSet(); + cfgs.set(_nid); + walkUp(ret(),cfgs ); + + // Top down (start to stop) all flavors. CFG limit to bitmap. + // If data use bottoms out in wrong CFG, returns false - but tries all outputs. + // If any output hits an in-CFG use (e.g. phi), then keep node. + BitSet body = new BitSet(); + BitSet visit = new BitSet(); + int old; + do { + old = body.cardinality(); + visit.clear(); + walkDown(this, cfgs, body, visit); + } while( old != body.cardinality() ); + return body; + } + + private static void walkUp(CFGNode n, BitSet cfgs) { + if( cfgs.get(n._nid) ) return; + cfgs.set(n._nid); + if( n instanceof RegionNode r ) + for( int i=1; i<n.nIns(); i++ ) + walkUp(n.cfg(i),cfgs); + else walkUp(n.cfg0(),cfgs); + } + + private static boolean walkDown( Node n, BitSet cfgs, BitSet body, BitSet visit ) { + if( n==null ) return false; + if( visit.get(n._nid) ) return body.get(n._nid); + visit.set(n._nid); + // Visit self as CFG outside the function + if( n instanceof CFGNode && !cfgs.get(n._nid) && unfolded( n ) ) + return false; + // Pretend the NewNode is a CFG, so its projections stay with it in loops + if( n instanceof NewNode nnn ) cfgs.set(n._nid); + // Visit n.cfg() outside of function + if( n.in(0)!=null && !cfgs.get(n.in(0)._nid) && unfolded( n.in( 0 ) ) ) + return false; + // Phis inside the function must have their body flag set BEFORE + // recursion walks around a loop and finds them again. + if( n instanceof PhiNode phi && cfgs.get(phi.in(0)._nid) ) + body.set(phi._nid); // Find data cycles + boolean in = n.in(0)!=null || n instanceof CFGNode; + for( Node use : n._outputs ) { + // Will hit a backedge, so 'n' MUST be in the function, and must be + // set before the cyclic walk finds 'n' again. + if( use instanceof PhiNode puse && puse.in(0) instanceof LoopNode loop && puse.in(2)==n ) + body.set(n._nid); // Set before walk + in |= walkDown(use,cfgs,body,visit); + } + if( in ) body.set(n._nid); + return in; + } + + // A CFG is folding, and so is basically Data + private static boolean unfolded( Node cfg) { + if( cfg instanceof CallNode call && call.cend().folding() ) return false; + if( cfg instanceof CallEndNode cend && cend.folding() ) return false; + if( cfg instanceof FunNode fun && fun._folding ) return false; + if( cfg instanceof ReturnNode ret && ret._fun._folding ) return false; + return true; + } + + // Clone function body. Give function a new FIDX. + FunNode copyBody() { + // Build the function body BitSet + BitSet body = body(); + assert body.cardinality() < 100; + // Walk the body, cloning + IdentityHashMap<Node,Node> map = new IdentityHashMap<>(); + BitSet visit = CodeGen.CODE.visit(); + bodyCopy( visit, body, map, this ); + visit.clear(); + bodyEdge( visit, body, map, this ); + visit.clear(); + assert map.size()==body.cardinality(); + + // New function/return cross-link each other + FunNode fun2 = (FunNode )map.get(this); + ReturnNode ret2 = (ReturnNode)map.get(_ret); + fun2._ret = ret2; + ret2._fun = fun2; + + // Remove all callers - this is a private copy + while( fun2.nIns() > 1 ) + if( fun2.in(1) instanceof CallNode call && body.get(call._nid) ) + call.unlink(fun2,1); // Recursive calls unlink + else fun2.removeDeadPath(1); // Start and external calls can just be removed + + // Flip to a new FIDX to avoid confusion with the old one. + // This is a non-monotonic (sideways) type move, only applicable + // because fun2 is new. The fidx is also a purely local fiction, + // since this body will inline and the fidx goes dead. + fun2._sig = _sig.makeFrom(CodeGen.CODE._fidxs.nextInline()); + return fun2; + } + + // Clone small function + private void bodyCopy( BitSet visit, BitSet body, IdentityHashMap<Node,Node> map, Node n ) { + if( n==null ) return; + if( !body.get(n._nid) ) return; // Not part of the body + if( visit.get(n._nid) ) return; // Been there, done that + visit.set(n._nid); + Node m = n.copy(); + CodeGen.CODE.add(m); + map.put(n,m); + m._type = n._type; + for( Node x : n._outputs ) + bodyCopy(visit,body,map,x); + } + private void bodyEdge( BitSet visit, BitSet body, IdentityHashMap<Node,Node> map, Node n ) { + if( n==null ) return; + if( !body.get(n._nid) ) return; // Not part of the body + if( visit.get(n._nid) ) return; // Been there, done that + visit.set(n._nid); + Node m = map.get(n); + // The cloned Call should not clone any linked edges. + if( n instanceof CallEndNode cend && !cend._folding ) { + m.addDef( map.get(cend.call()) ); + } else { + for( Node e : n._inputs ) { + Node x = e; + if( e != null ) { + x = map.get(e); + if( x == null ) x = e; + } + m.addDef(x); + } + } + for( Node x : n._outputs ) + bodyEdge(visit,body,map,x); + } + + + // FunNodes must match signature (equivalent: no 2 FunNodes are ever GVN'able) + @Override public boolean eq( Node n ) { + return _sig == ((FunNode)n)._sig; + } + + @Override public int hash() { + return _sig.hashCode(); + } + + // ------------ + // MachNode specifics, shared across all CPUs + + // Function may call other functions? X86 requires 16b aligned SP on + // outbound calls, but not leaf. Set during an unrelated RA scan. + public boolean _hasCalls; + + // Maximum incoming argument slot, based on function type + public int _maxArgSlot; + + // Frame adjust, including padding, set during early Encoding + public int _frameAdjust; + + public String op() { return _frameAdjust==0 ? "entry" : "subi"; } + public void postSelect(CodeGen code) { + code.link(this); + // Max slot seen past space reserved for incoming arguments + _maxArgSlot = code._mach.maxArgSlot(sig()); + } + public RegMask regmap(int i) { return null; } + public RegMask outregmap() { return null; } + public void encoding( Encoding enc ) { throw Utils.TODO(); } + public void asm(CodeGen code, SB sb) { + if( _frameAdjust!=0 ) + sb.p("rsp -= #").p(_frameAdjust); + } + + public void computeFrameAdjust(CodeGen code, int maxReg) { + // Max slot seen, or 0. + int maxSlot = Math.max(maxReg - code._mach.regs().length,_maxArgSlot); + // Frame adjust before rounding, can be negative for no spills + int size = maxSlot - _maxArgSlot; + _frameAdjust = size*8; + } + + // Given a high register number and during encoding compute the stack + // pointer offset. Pre-RA we lack the fun._frameAdjust. We also need a + // FunNode. + public int computeStackOffset(CodeGen code, int reg) { + String[] regs = code._mach.regs(); + int stackSlot = reg - regs.length; + int slotRotate = stackSlot < _maxArgSlot + ? stackSlot + (_frameAdjust>>3) + : stackSlot - _maxArgSlot; + return slotRotate*8; + } + + @Override public void gather( HashMap<String,Integer> strs ) { + Serialize.gather(strs,_name); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java new file mode 100644 index 00000000..33757640 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java @@ -0,0 +1,65 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +// Constant function pointer whose type follows the callee signature. +public class FunPtrNode extends TypeNode { + public FunPtrNode(TypeFunPtr tfp, StartNode start, ReturnNode ret) { + super(tfp,start,ret); + } + public FunPtrNode(FunPtrNode fptr) { super(fptr); } + @Override public Tag serialTag() { return Tag.FunPtr; } + @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) { + baos.packed2(types.get(_con)); + } + static Node make(BAOS bais, Type[] types) { + return new FunPtrNode((TypeFunPtr)types[bais.packed2()],null,null); + } + public ReturnNode ret() { return (ReturnNode)in(1); } + public FunNode fun() { return ret().fun(); } + + @Override public String label() { return "#"+_con; } + @Override public String glabel() { return "#"+_con.gprint(); } + @Override public String uniqueName() { return "FunPtr_" + _nid; } + @Override public Node copy() { + FunPtrNode fptr = new FunPtrNode((TypeFunPtr)_con,null,null); + fptr._type = _type; + return fptr; + } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + FunNode fun = CodeGen.CODE.lookupFun(((TypeFunPtr)_con).fidx()); + return fun!=null && fun._name!=null + ? sb.append("{ ").append(fun._name).append("}") + : sb.append(_con); + } + + @Override public boolean isConst() { return true; } + @Override public boolean isPinned() { return true; } + @Override public Type compute() { + ReturnNode ret = ret(); + return ret == null ? _con : ret.fun().sig(); + } + @Override public Node idealize() { + ReturnNode ret = ret(); + if( ret==null ) return null; + // Function died (never executed), but fcn ptr is alive. + // Can be checked for null, or for unequals another FunPtr. + FunNode fun = addDep(ret.fun()); + TypeFunPtr sig = fun.sig(); + if( sig != _con ) + { liftType(sig); return this; } + // TODO: If the function body dies but this value remains live only as + // a null/equality sentinel, preserve the identity without the Return. + if( fun.isDead() ) + { setDef(1,null); liftType(sig.makeFrom(Type.TOP)); return this; } + return null; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/GuardMach.java b/chapter25/src/main/java/com/seaofnodes/simple/node/GuardMach.java new file mode 100644 index 00000000..202b1ab8 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/GuardMach.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; + +public class GuardMach extends GuardNode implements MachNode { + public GuardMach(GuardNode guard) { super(guard); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress() { return in(1)._type==Type.BOTTOM ? 0 : 1; } + @Override public void encoding(Encoding enc) { } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/GuardNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/GuardNode.java new file mode 100644 index 00000000..e3c6f0e2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/GuardNode.java @@ -0,0 +1,61 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeScalar; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +// A branch-proven refinement to zero/null or non-zero/non-null. The value +// family is deliberately taken from the input when it becomes known. +public class GuardNode extends Node { + public final boolean _nonZero; + + public GuardNode(boolean nonZero, Node ctrl, Node val) { + super(ctrl,val); + _nonZero = nonZero; + } + + public GuardNode(GuardNode guard) { + super(guard); + _nonZero = guard._nonZero; + } + + @Override public Tag serialTag() { return Tag.Guard; } + @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) { + baos.write(_nonZero ? 1 : 0); + } + static Node make(BAOS bais) { return new GuardNode(bais.read()!=0,null,null); } + + @Override public String label() { return _nonZero ? "(!=0)" : "(==0)"; } + @Override public String uniqueName() { return "Guard_"+_nid; } + @Override public boolean isPinned() { return true; } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return in(1)._print0(sb.append(label()),visited); + } + + @Override public Type compute() { + Type t = in(1)._type; + if( t==Type.BOTTOM || t==Type.TOP ) return t; + // The scalar envelope does not identify a value family. Preserve it + // until SCCP learns whether this is an int, float, pointer, or fptr; + // choosing generic ptr here can be weaker than a later concrete type. + if( t==TypeScalar.BOT || t==TypeScalar.TOP ) return t; + Type t2 = _nonZero ? t.nonZero() : t.makeZero(); + return t2==null ? (_type==null ? Type.TOP : _type) : t2; // No sane answer for e.g. not-zero of a zero + } + + @Override public Node idealize() { + if( in(1)._type==Type.BOTTOM || in(1)._type==Type.TOP ) return null; + if( _type.isHighOrConst() && in(1)._type.isa(_type) ) + return in(1); + return null; + } + + @Override public boolean eq(Node n) { return _nonZero==((GuardNode)n)._nonZero; } + @Override int hash() { return _nonZero ? 1 : 0; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/IfNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/IfNode.java new file mode 100644 index 00000000..d8d72411 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -0,0 +1,99 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +public class IfNode extends CFGNode implements MultiNode { + + public IfNode(Node ctrl, Node pred) { + super(ctrl, pred); + CodeGen.CODE.add(this); // Because idoms are complex, just add it + } + public IfNode(IfNode iff) { super(iff); } + @Override public Tag serialTag() { return Tag.If; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("if( "); + if( in(1)==null ) sb.append("never"); + else in(1)._print0(sb, visited); + return sb.append(" )"); + } + + public Node ctrl() { return in(0); } + public Node pred() { return in(1); } + + // No one unique control follows + @Override public CFGNode uctrl() { return null; } + + @Override + public Type compute() { + // If the If node is not reachable then neither is any following Proj + assert !_inputs.isEmpty(); + if (ctrl()._type != Type.CONTROL && ctrl()._type != Type.BOTTOM ) + return TypeTuple.IF_NEITHER; + Node pred = pred(); + Type t = pred._type; + // High types mean NEITHER side is reachable. + // Wait until the type falls to decide which way to go. + if( t.isHigh() ) + return TypeTuple.IF_NEITHER; + // If constant is 0 then false branch is reachable + // Else true branch is reachable + if( t.isConstant() ) + return (t==Type.NIL || t==TypeInteger.ZERO || (t instanceof TypeFunPtr tfp && tfp._fidxs== XInt.EMPTY) ) ? TypeTuple.IF_FALSE : TypeTuple.IF_TRUE; + // If adding a zero makes a difference, the predicate must not have a zero/null + if( !t.makeZero().isa(t) ) + return TypeTuple.IF_TRUE; + + return TypeTuple.IF_BOTH; + } + + @Override + public Node idealize() { + // Hunt up the immediate dominator tree. If we find an identical if + // test on either the true or false branch, that side wins. + if( !pred()._type.isHighOrConst() ) + for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() ) + if( addDep(dom) instanceof IfNode iff && + prior instanceof CProjNode prj && + (addDep(iff.pred())==pred() || + pred() instanceof GuardNode cast && cast._nonZero && iff.pred()==cast.in(1) ) ) { + setDef(1,con( prj._idx==0 ? 1 : 0 )); + return this; + } + return null; + } + + // MachNode variants need to support this and negate the conditional test. + // The following CProjs will be inverted by the caller. + public void negate() { throw Utils.TODO(); } + + // Negate the sense of a test + public static String negate( String bop ) { + return switch( bop ) { + case "<" -> ">="; + case "<=" -> ">" ; + case "==" -> "!="; + case "!=" -> "=="; + case ">" -> "<="; + case ">=" -> "<" ; + default -> throw Utils.TODO(); + }; + } + + // Swap compare operands + public static String swap( String bop ) { + return switch( bop ) { + case "<" -> ">" ; + case "<=" -> ">="; + case "==" -> "=="; + case "!=" -> "!="; + case ">" -> "<" ; + case ">=" -> "<="; + default -> throw Utils.TODO(); + }; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java new file mode 100644 index 00000000..8b8bcaac --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -0,0 +1,332 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.GlobalBits; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +/** + * Load represents extracting a value from inside a memory object, + * in chapter 10 this means Struct fields. + */ +public class LoadNode extends MemOpNode { + + /** + * Load a value from a ptr.field. + * + * @param name The field we are loading + * @param mem The memory alias node - this is updated after a Store + * @param ptr The ptr to the struct base from where we load a field + * @param off The offset inside the struct base + */ + public LoadNode(Parser.Lexer loc, String name, int alias, Node ctrl, Node mem, Node ptr, Node off) { + super(loc, name, alias, true, Type.BOTTOM, ctrl, mem, ptr, off); + } + LoadNode( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) { + super(bais,strs,types,fileAliases,aliases,true); + _con = Type.BOTTOM; // Serialized slot retained while MemOps share TypeNode + } + @Override public Tag serialTag() { return Tag.Load; } + + // GraphVis DOT code (must be valid Java identifiers) and debugger labels + @Override public String label() { return "ld_"+mlabel(); } + // GraphVis node-internal labels + @Override public String glabel() { return "." +_name; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } + + @Override + public Type compute() { + Type mem0 = aliasMem()._type; + Type ptr0 = ptr()._type; + // Validate argument types + if( ptr0.isHigh() ) + return TypeScalar.TOP; + if( !(mem0 instanceof TypeMem mem) ) + return mem0.isHigh() ? TypeScalar.TOP : TypeScalar.BOT; + if( ptr0 == Type.NIL ) + return TypeScalar.TOP; + if( !(ptr0 instanceof TypeMemPtr ptr) ) + return mem0.isHigh() ? TypeScalar.TOP : scalar(mem._t); + + // Load field from object + TypeMemPtr tmp = ptr; + Field pfld = tmp._obj.field(_name); + // No field? Open objects might yet get the field when falling; + // closed objects with missing field are an error. + if( pfld == null ) + return tmp.isHigh() || tmp._obj.isHigh() ? TypeScalar.TOP : TypeScalar.BOT; + + Type t = pfld._t; + // Load member of constant array + if( t instanceof TypeConAry ary ) + t = ary.elem(); // TODO: if offset is known, can peek the constant + + // Now, do the same for memory + if( mem._t instanceof TypeStruct ts ) { + assert ts._name==tmp._obj._name; + Field mfld = ts.field(_name); + // Lift from declared type and memory input + if( mfld != null ) + t = t.join(mfld._t); + } else if( mem._alias==_alias ) { + t = t.join(mem._t); + } else if( mem._t == Type.TOP ) { + return TypeScalar.TOP; + } + + // A deeply read-only base produces a deeply read-only value. The + // generic declared field type must not cast this information away. + if( ptr0.isFinal() ) + t = t.makeRO(); + return scalar(t); + } + + // Loads produce ordinary scalar values. Field/memory joins can still + // expose the enclosing global lattice bounds through an uninitialized + // Type.TOP/BOTTOM field; keep those results inside the scalar envelope. + private static Type scalar(Type t) { + return t==Type.TOP ? TypeScalar.TOP : t==Type.BOTTOM ? TypeScalar.BOT : t; + } + + @Override + public Node idealize() { + Node ptr = ptr(); + Node mem = aliasMem(); + + // Loads into structs do not need a ctrl edge, as null-ptr checking is + // baked into the type system. Loads into arrays DO need the ctrl + // edge, at least until proper range-checking is in place. + if( in(0)!=null && ptr()._type instanceof TypeMemPtr tmp && + // Never can be an array + (!tmp._obj._open || (tmp._obj._fields.length > 1 && tmp._obj._fields[0]._fname != "#")) ) { + setDef(0,null); + return this; + } + + // Forward-ref loads eventually sharpen to a declared type + Field fld; + if( ptr()._type instanceof TypeMemPtr tmp && + (fld=tmp._obj.field(_name)) != null && + _alias != fld._alias) { + assert _alias==1 || _alias == fld._alias; + unlock(); // Alias participates in GVN semantics + _alias = fld._alias; + // Bulk memory may already have inspected this user. Revisit it + // once the declaring shape is stable; an open forward reference + // can still change this Load's semantics. + if( !tmp._obj._open && !tmp._obj._fref ) + CodeGen.CODE.add(mem()); + return this; + } + // Must sharpen alias first + if( _alias == 1 ) + return null; + + // Simple Load-after-Store on same address. + if( mem instanceof StoreNode st && + ptr == st.nnptr() && + off() == st.off() ) { // Must check same object + assert _name.equals(st._name); // Equiv class aliasing is perfect + return extend(st.val()); + } + + // Expose the same effective memory input already observed by compute. + if( mem != mem() ) { + for( Node ld : mem._outputs ) + if( ld instanceof LoadNode ) + CodeGen.CODE.add(ld); + if( mem instanceof BulkMemPhiNode ) CodeGen.CODE.add(mem); + setDef(1,mem); + return this; + } + + // Uplift control to a prior dominating load. + for( Node memuse : mem._outputs ) + // Find a prior load, has same mem,ptr,off but higher ctrl + if( memuse != this && memuse instanceof LoadNode ld && ptr==ld.ptr() && off()==ld.off() && + cfg0()!=null && cfg0()._idom(ld.cfg0(),this) == ld.cfg0() ) // Higher control means load is legal earlier + return ld; + + // Load-after-Store on same address, but bypassing provably unrelated + // stores. This is a more complex superset of the above two peeps. + // "Provably unrelated" is really weak. + if( ptr instanceof ReadOnlyNode ro ) + ptr = ro.in(1); + outer: + while( true ) { + switch( mem ) { + case StoreNode st: + if( ptr == addDep(st.ptr()) && off() == st.off() ) + return extend(castRO(st.val())); // Proved equal + // Can we prove unequal? Offsets do not overlap? + if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap + !neverAlias(ptr,st.ptr()) ) { // And might alias + addDep( off()); // Offsets can fold, proving unequal + addDep(st.off()); // Offsets can fold, proving unequal + break outer; // Cannot tell, stop trying + } + // Pointers cannot overlap + mem = st.mem(); // Proved never equal + break; + case PhiNode phi: + // Assume related + addDep(phi); + break outer; + case ConstantNode con: + // Load from constant memory + if( con._con instanceof TypeMem tmem ) + return ConstantNode.make(tmem._t); + break outer; // Assume shortly dead + case ProjNode mproj: // Memory projection + switch( mproj.in(0) ) { + case NewNode nnn1: + // Direct load from e.g. new array elements + assert _name=="[]"; + Type decl = declaredType(); + return decl==Type.BOTTOM ? null : ConstantNode.make(decl.makeZero()); + case StartNode start: break outer; + case CallEndNode cend: addDep(mproj); break outer; // TODO: Bypass no-alias call + default: throw Utils.TODO(); + } + case CheckCastNode cast: mem = cast.in(1); break; + case MemMergeNode merge: mem = merge.alias(_alias); break; + case EscapeNode esc: + if( esc.self()==ptr ) // Proved equal + { mem = esc.priv(); break; } + // Two NewNodes are always unequal + if( esc.self().in(0) instanceof NewNode && ptr.in(0) instanceof NewNode ) + { mem = esc.pub(); break; } + // TODO: Can we prove unequal? + break outer; + + default: + throw Utils.TODO(); + } + } + + // Push a Load up through a Phi, as long as it collapses on at least + // one arm. If at a Loop, the backedge MUST collapse - else we risk + // spinning the same transform around the loop indefinitely. + // BEFORE (2 Sts, 1 Ld): AFTER (1 St, 0 Ld): + // if( pred ) ptr.x = e0; val = pred ? e0 + // else ptr.x = e1; : e1; + // val = ptr.x; ptr.x = val; + if( mem() instanceof PhiNode memphi && memphi.region()._type == Type.CONTROL && memphi.nIns()== 3 && + // Offset can be hoisted + off() instanceof ConstantNode && + // Not control dependent + in(0)==null && + // Pointer can be hoisted + hoistPtr(ptr,memphi) ) { + + // Profit on RHS/Loop backedge + if( profit(memphi,2) || + // Else must not be a loop to count profit on LHS. + (!(memphi.region() instanceof LoopNode) && profit(memphi,1)) ) { + // profit() peepholes inputs and can restore a required control + // edge. Recheck before manufacturing control-free loads. + if( in(0)!=null ) + return null; + Node ld1 = ld(1); + Node ld2 = ld(2); + PhiNode phi = new PhiNode(_name, memphi.region(),ld1,ld2); + phi.setType(phi.compute().join(_type)); + return phi; + } + } + + return null; + } + + // Semantic memory input for this Load. Compute defines behavior from + // this view; ideal merely exposes the same edge in the graph. + private Node aliasMem() { + Node mem = mem(); + return _alias != 1 && mem instanceof MemMergeNode merge + ? merge.alias(_alias) + : mem; + } + + private Node ld( int idx ) { + Node mem = mem(), ptr = ptr(); + assert in(0)==null; + return new LoadNode(_loc,_name,_alias,null,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr, off()).peephole(); + } + + private static boolean neverAlias( Node ptr1, Node ptr2 ) { + return ptr1.in(0) != ptr2.in(0) && + // Unrelated allocations + ptr1 instanceof ProjNode && ptr1.in(0) instanceof NewNode && + ptr2 instanceof ProjNode && ptr2.in(0) instanceof NewNode; + } + + private static boolean hoistPtr(Node ptr, PhiNode memphi ) { + // Can I hoist ptr above the Region? + if( !(memphi.region() instanceof RegionNode r) ) + return false; // Dead or dying Region/Phi + // If ptr from same Region, then yes, just use hoisted split pointers + if( ptr instanceof PhiNode pphi && pphi.region() == r ) + return true; + + // No, so can we lift this ptr? + CFGNode cptr = ptr.cfg0(); + if( cptr != null ) + // Pointer is controlled high + // TODO: Really needs to be the LCA of all inputs is high + return cptr.idepth() <= r.idepth(); + + // Dunno without a longer walk + return false; + } + + // Profitable if we find a matching Store on this Phi arm. + private boolean profit(PhiNode phi, int idx) { + Node px = phi.in(idx); + if( px==null ) return false; + if( px._type instanceof TypeMem mem && mem._t.isHighOrConst() ) + // To avoid cyclic pushing a Load up then down, getting here means + // the load *must* replace with the high/constant. + return true; + if( px instanceof StoreNode st1 && ptr()==addDep(st1.nnptr() )&& off()==st1.off() ) + // To avoid cyclic pushing a Load up then down, getting here means + // the load *must* match against the Store + return true; + addDep(px); + return false; + } + + // Read-Only is a deep property, and cannot be cast-away + private Node castRO(Node rez) { + if( ptr()._type.isFinal() && !rez._type.isFinal() ) + return new ReadOnlyNode(rez).peephole(); + return rez; + } + + // When a load bypasses a store, the store might truncate bits - and the + // load will need to zero/sign-extend. + private Node extend(Node val) { + if( !(declaredType() instanceof TypeInteger ti) ) return val; + if( ti._min==0 ) // Unsigned + return new AndNode(null,val,con(ti._max)); + // Signed extension + int shift = Long.numberOfLeadingZeros(ti._max)-1; + Node shf = con(shift); + if( shf._type==TypeInteger.ZERO ) { + // Val can be stale and on worklist. Preserve momnotoncity and bring it up to date + if( val._type.isa(_type) ) + return val; + Node val2 = val.peephole(); + assert val._type.isa(_type); + return val2; + } + Node shl = new ShlNode(null,val,shf.keep()).peephole(); + return new SarNode(null,shl,shf.unkeep()); + } + + @Override public int log_size() { return declaredType().log_size(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/LoopNode.java new file mode 100644 index 00000000..44ca35d8 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -0,0 +1,110 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeMem; +import com.seaofnodes.simple.util.BAOS; + +import java.util.HashMap; +import java.util.IdentityHashMap; + +public class LoopNode extends RegionNode { + public LoopNode( Parser.Lexer loc, Node xctrl, Node entry ) { super(loc,xctrl,entry,null); } + public LoopNode( LoopNode loop ) { super(loop); } + @Override public Tag serialTag() { return Tag.Loop; } + public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { } + + public CFGNode entry() { return cfg(1); } + public CFGNode back () { return cfg(2); } + + @Override + public Type compute() { + if( inProgress() ) return Type.CONTROL; + return entry()._type; + } + + // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) + @Override public int idepth() { + return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); + } + // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) + @Override public CFGNode idom(Node dep) { return entry(); } + + // If this is an unreachable loop, it may not have an exit. If it does not + // (i.e., infinite loop), force an exit to make it reachable. + public StopNode forceExit( FunNode fun, StopNode stop ) { + // Walk the backedge, then immediate dominator tree util we hit this + // Loop again. If we ever hit a CProj from an If (as opposed to + // directly on the If) we found our exit. + CFGNode x = back(); + while( x != this ) { + if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) { + CFGNode other = iff.cproj(1-exit._idx); + if( other!=null && other._ltree != _ltree && nested(_ltree,other._ltree) ) + return stop; // Found an exit, not an infinite loop + } + x = x.idom(); + } + // Found a no-exit loop. Insert an exit + NeverNode iff = (NeverNode)new NeverNode(1.2f,back()).peephole(); // Ideal never-branch + CProjNode t = new CProjNode(iff,0,"True" ).init(); + CProjNode f = new CProjNode(iff,1,"False").init(); + setDef(2,t); // True continues loop, False (never) exits loop + ReturnNode ret = fun.ret(); + iff._ltree = t._ltree = _ltree; + ret._ltree = f._ltree = stop._ltree; + + // Now fold control into the exit. Might have 1 valid exit, or an + // XCtrl or a bunch of prior NeverNode exits. + Node top = ConstantNode.make(Type.TOP).peephole(); + Node memout = new MemMergeNode(false); + memout.addDef(f); // placeholder for control + memout.addDef(top); + for( Node u : _outputs ) + if( u instanceof PhiNode phi && phi._type.isa(TypeMem.BOT) ) + memout.addDef(phi); + memout.init(); + + Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr(); + if( ctrl!=null && ctrl._type != Type.XCONTROL ) { + // Perfect aligned exit? + if( !(ctrl instanceof RegionNode r && + mem instanceof PhiNode pmem && pmem.region()==r && + expr instanceof PhiNode prez && prez.region()==r ) ) { + // Nope, insert an aligned exit layer + RegionNode r = new RegionNode(_loc,null,ctrl).init(); + ctrl = r; r._ltree = stop._ltree; + mem = new BulkMemPhiNode(r,mem ).init(); + expr = new PhiNode(r,expr).init(); + } + // Append new Never exit + ctrl.keep().addDef(f ); + mem .keep().addDef(memout); + expr.keep().addDef(top); + } else { + ctrl = f .keep(); + mem = memout.keep(); + expr = top .keep(); + } + ret.setDef(0,ctrl.unkeep()); + ret.setDef(1,mem .unkeep()); + ret.setDef(2,expr.unkeep()); + // Force sane types to exit; might locally break monotonicity + ret.init(); + ret.out(0).init(); + stop.init(); + CodeGen.CODE.add(CodeGen.CODE._start); + CodeGen.CODE.add(mem); + + return stop; + } + + private static boolean nested(LoopTree inner, LoopTree outer) { + for( ; inner!=null; inner = inner._par ) + if( inner == outer ) + return true; + return false; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java new file mode 100644 index 00000000..9440a7ea --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java @@ -0,0 +1,27 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.Utils; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +// Generic machine-specific class, has a few Node implementations that have to +// exist (abstract) but are not useful past the optimizer. +public abstract class MachConcreteNode extends Node implements MachNode { + + public MachConcreteNode(Node node) { super(node); } + public MachConcreteNode(Node[]nodes) { super(nodes); } + + @Override public String label() { return op(); } + @Override public Type compute () { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("(").append(op()).append(","); + for( int i=1; i<nIns(); i++ ) + (in(i)==null ? sb.append(" ---") : in(i)._print0(sb, visited)).append(","); + sb.setLength(sb.length()-1); + return sb.append(")"); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MachNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MachNode.java new file mode 100644 index 00000000..025b5dc4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MachNode.java @@ -0,0 +1,60 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public interface MachNode { + + // Easy access to the abstract Machine + default Machine machine() { return CodeGen.CODE._mach; } + + // Run a post-instruction-selection action + default void postSelect( CodeGen code ) { } + + // Register mask allowed on input i. 0 for no register. + RegMask regmap( int i ); + // Register mask allowed as a result. 0 for no register. + RegMask outregmap(); + // Multi-reg-defining machine op; idx comes from Proj. + // Sometimes these Projs have no uses, and just exist to kill a register. + default RegMask outregmap(int idx) { throw Utils.TODO(); } + + // Set of killed registers + default RegMask killmap() { return null; } + + // If this op updates the same register as input 'idx', e.g. many X86 "R1 += + // R2" style ops. This forces the output live range to equal the input live + // range, so they both get the same register. + + // This can hold true for projections, if the instruction updates many + // registers that it also takes in. Note that ops also use the same + // registers for both inputs and outputs but these are pinned and without any + // choices (e.g. always RCX for both an input and output) and are better + // served with just declaring as a fixed register. + + // Returns either 0 for not-two-adr or the updated input; for projections + // this is their Multi's updated input. + default int twoAddress( ) { return 0; } + // Ok to switch arguments + default boolean commutes() { return false; } + + // Instructions cheaper to recreate than to spill, such as loading small constants + default boolean isClone() { return false; } + + // Make a clone of a cheap instruction + default Node copy() { return null; } + + // Encoding is appended, relocations gathered. + void encoding( Encoding enc ); + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "R17=[R18+12] // Load array base". + // General form: "dst=src+src". + default void asm(CodeGen code, SB sb) { throw Utils.TODO(); } + + // Human-readable opcode; e.g. "ld4" or "call". + default String op() { throw Utils.TODO(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java new file mode 100644 index 00000000..a88748b5 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java @@ -0,0 +1,258 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; + +import java.util.*; + +/** + * Memory Merge - a merge of many aliases into a "fat memory". All aliases + * are here, but most will be lazy - take the default fat memory. + */ +public class MemMergeNode extends Node { + + /* + * In-Progress is the mutable construction form. No optimizations are + * allowed. If not "in progress" then normal peeps work. + */ + public final boolean _inProgress; + + public MemMergeNode( boolean inProgress) { _type = TypeMem.BOT; _inProgress = inProgress; } + public MemMergeNode( boolean inProgress, Node ...nodes) { super(nodes); _type = TypeMem.BOT; _inProgress = inProgress; } + public MemMergeNode(MemMergeNode mem) { super(mem); _inProgress = false; } + @Override public Tag serialTag() { return Tag.MemMerge; } + public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); } + static Node make( BAOS bais ) { Node mem = new MemMergeNode(false); mem.setDefX(bais.packed1()-1,null); return mem; } + + + // The mutable construction form is "in progress"; otherwise this is a + // graph-semantic memory merge. + boolean inProgress() { return _inProgress; } + + @Override public String label() { return "ALLMEM"; } + @Override public boolean isMem() { return true; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append(_inProgress ? "Merge[" : "MEM[ "); + for( int j=2; j<nIns(); j++ ) { + sb.append(j); + sb.append(":"); + Node n = in(j); + while( n instanceof ScopeNode loop ) { + sb.append("Lazy_"); + n = loop.mem(j); + } + if( n==null ) sb.append("___ "); + else n._print0(sb, visited).append(" "); + } + sb.setLength(sb.length()-1); + return sb.append("]"); + } + + // Make a memory merge: no longer a Scope really, tracking memory state but + // not related to the parser in any way. + public Node merge() { + // Force default memory to not be lazy + MemMergeNode merge = new MemMergeNode(false); + for( Node n : _inputs ) + merge.addDef(n); + for( int i=1; i<nIns(); i++ ) + merge._mem(i,null); + return merge.peephole(); + } + + + @Override public Type compute() { + Type tmem = in(1)._type; + if( !(tmem instanceof TypeMem defmem) ) + // A MemMerge is structurally memory even while its default input + // is a weak, not-yet-specialized Phi. + return tmem.isHigh() ? TypeMem.TOP : TypeMem.BOT; + // Is this a single private instance memory? + if( defmem._one ) { + if( !(defmem._t instanceof TypeStruct ts) ) + return defmem; + // Perfect singleton memory, so all updates are parallel and + // independent and stack. + for( int i=2; i<nIns(); i++ ) { + Node in = in(i); + while( true ) { + if( in !=null && in._type.isHigh() ) + return TypeMem.TOP; + if( in instanceof MemMergeNode mem ) { + in = mem.in(i); + } else break; + } + if( in instanceof StoreNode st ) { + Type val = ((TypeMem)st._type)._t; + Field old = ts.field(st._name); + // Lazy add an expected field for open structs. + if( old == null && ts._open ) { + old = Field.make(st._name,Type.BOTTOM,st._alias,st._init); + ts = ts.add(old); + } + Field fld = old.makeFrom(val); + // TODO: val leaks into perfect singleton memory, along with its fidxs and aliases + ts = ts.replace(fld); + } + } + return TypeMem.makePrivate(ts); + + } else { + // Default mixed mem is just BOT + TypeMem mem = defmem; + boolean progress = true; + while( progress ) { + progress = false; + for( int i=2; i<nIns(); i++ ) { + // Has this alias escaped already? + if( XInt.bit(mem._escAs,i) ) { + Node n = in(i); + if( n != null && !n._type.isHigh() ) { + TypeMem mem2 = (TypeMem)mem.meet(n._type); + // More escape aliases + if( mem2 != mem ) + { mem = mem2; progress = true; } + } + } + } + } + return mem; + } + } + + @Override public Node idealize() { + if( inProgress() ) return null; + if( nIns()==0 ) return null; + + // Fold defaults into the default + boolean progress=false, allDefault=true; + for( int i=2; i<nIns(); i++ ) { + if( in(i) instanceof CheckCastNode cast ) + { setDef(i,cast.in(1)); progress=true; } + if( in(1) == in(i) && in(1) != null ) { setDef(i,null); progress=true; } + else { allDefault=false; } + } + + // If not merging any memory (all memory is just the default) + if( allDefault ) + return in(1); // Become default memory + + // Collapse stacked merged-mem + if( in(1) instanceof MemMergeNode mem ) { + // Goal is to swap my default mem with mem's default mem + for( int i=2; i<mem.nIns(); i++ ) { + if( mem.in(i) != null ) { + // deeper default mem has a non-default + if( i>=nIns() || in(i)==null ) + setDefX(i,mem.in(i)); + } + } + CodeGen.CODE.add(mem.in(1)); + setDef(1,mem.in(1)); + return this; + } + + return progress ? this : null; + } + + + public Node in( Var v ) { return in(v._idx); } + + public Node alias( int alias ) { + assert !(in(1) instanceof BulkMemPhiNode bulk && bulk.isSplit(alias) && + (alias >= nIns() || in(alias)==null)); + return alias < nIns() && in(alias)!=null ? in(alias) : in(1); + } + + public Node alias( int alias, Node st ) { return setDefX(alias,st); } + + // Read or update from memory. + // A shared implementation allows us to create lazy phis both during + // lookups and updates; the lazy phi creation is part of chapter 8. + Node _mem( int alias, Node st ) { + // Memory projections are made lazily; if one does not exist + // then it must be START.proj(1) + Node old = alias(alias); + assert !(old instanceof ScopeNode); // Parser lazy memory is handled by ScopeNode + // Memory projections are made lazily; expand as needed + return st==null ? old : alias(alias,st); // Not lazy, so this is the answer + } + + // Remove lazy Phis on all aliases + public void removeLazyAll() { + for( int i = 1; i < nIns(); i++) + _mem(i,null); + } + + public void _merge( MemMergeNode that, RegionNode r) { + assert _inProgress; + int len = Math.max(nIns(),that.nIns()); + for( int i = 1; i < len; i++) + if( alias(i) != that.alias(i) ) { // No need for redundant Phis + // If we are in lazy phi mode we need to a lookup + // by alias as it will trigger a phi creation + Node lhs = this._mem(i,null); + Node rhs = that._mem(i,null); + alias(i, new MemPhiNode(Parser.memName(i), i, r, lhs, rhs).peephole()); + } + } + + // Fill in the backedge of any inserted Phis. Keep precise aliases for + // what is known at the loop head. For aliases discovered in the loop + // body, gather them into a actual (not inProgress) MemMerge and merge into + // the default alias. + void _endLoopMem( ScopeNode scope, MemMergeNode back, MemMergeNode exit ) { + assert _inProgress && back._inProgress && exit._inProgress; + Node exit_def = exit.alias(1); + MemMergeNode more = null; // Dont often need the "more discovered" merge, make it lazy + int len = Math.max(nIns(),back.nIns()); + for( int i=2; i<len; i++ ) { + if( !(i < this.nIns() && this.in(i)!=null) && // Alias is not known to loop head + (i < back.nIns() && back.in(i)!=null) ) { // Alias IS known to loop back + // Gather + if( more==null ) { + more = new MemMergeNode(false); + more.alias(1,back.in(1)); + back.alias(1,more); + } + more.alias(i,back.in(i)); + back.setDef(i,null); + } + } + + for( int i=1; i<nIns(); i++ ) { + if( in(i) instanceof PhiNode phi && phi.region()==scope.ctrl() ) { + assert phi.in(2)==null; + phi.setDef(2,back.alias(i)==scope ? phi : back.alias(i)); // Fill backedge + } + if( exit_def == scope ) // Replace a lazy-phi on the exit path also + exit.alias(i,in(i)); + } + } + + // Now one-time do a useless-phi removal + void _useless( ) { + for( int i=1; i<nIns(); i++ ) { + if( in(i) instanceof PhiNode phi ) { + // Do an eager useless-phi removal + Node in = phi.peephole(); + CodeGen.CODE.addAll(phi._outputs); + phi.moveDepsToWorklist(); + if( in != phi ) { + if( !phi.iskeep() ) // Keeping phi around for parser elsewhere + phi.subsume(in); + setDef(i,in); // Set the update back into Scope + } + } + } + } + + @Override public boolean eq( Node n ) { + return this==n || !_inProgress; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java new file mode 100644 index 00000000..d824bc3a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -0,0 +1,131 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.GlobalBits; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.lang.StringBuilder; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * Convenience common base for Load and Store. + * + * 0 - Control + * 1 - Memory + * 2 - Base of object, OOP + * 3 - Offset, integer types + * 4 - Value, for Stores only + */ +public abstract class MemOpNode extends TypeNode { + + // The equivalence alias class + public int _alias; + + // True if load-like, false if store-like. + // Used on CPU-specific combined memory+arithmetic ops. + // + // Stores produce memory (maybe as part of a tuple with other things), + // loads do not. + // + // Loads might pick up anti-dependencies on prior Stores, and never cause an + // anti-dependence themselves. + // + // Stores must maximally sink to the least dominator use. Loads can be + // opportunistically hoisted. + public final boolean _isLoad; + + // Field name. + public final String _name; + // Source location for late reported errors + public final Parser.Lexer _loc; + + public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node ctrl, Node mem, Node ptr, Node off) { + super(glb, ctrl, mem, ptr, off); + _name = name; + _alias = alias; + _loc = loc; + _isLoad = isLoad; + } + public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node ctrl, Node mem, Node ptr, Node off, Node value) { + this(loc, name, alias, isLoad, glb, ctrl, mem, ptr, off); + addDef(value); + } + public MemOpNode( Node ideal, MemOpNode mop ) { + super(ideal,mop instanceof LoadNode ld ? ld.declaredType() : mop._con); + _name = mop._name; + _alias = mop._alias; + _loc = mop._loc; + _isLoad= mop._isLoad; + } + + MemOpNode( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases, boolean isLoad ) { + this(null, + strs[bais.packed2()], + mapAlias(bais,fileAliases,aliases),isLoad, + types[bais.packed2()],null,null,null,null); + } + + private static int mapAlias( BAOS bais, GlobalBits fileAliases, GlobalBits aliases ) { + int alias = bais.packed2(); + return alias < GlobalBits.RESERVED ? alias : aliases.map(fileAliases,alias); + } + + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed2(_name==null ? 0 : strs.get(_name)); + baos.packed2(_alias); + baos.packed2(types.get(_con)); // NPE if fails lookup + assert _isLoad == this instanceof LoadNode; // No machine ops + } + + static String mlabel(String name) { return "[]"==name ? "ary" : ("#"==name ? "len" : name); } + String mlabel() { return mlabel(_name); } + + public Node mem() { return in(1); } + public Node ptr() { return in(2); } + public Node off() { return in(3); } + + // Current declared field type. This is deliberately a live lookup: + // cyclic/forward types can sharpen by replacing the pointer's struct. + public Type declaredType() { + if( !(ptr()._type instanceof TypeMemPtr tmp) ) + return Type.BOTTOM; + Field fld = tmp._obj.field(_name); + return fld == null || fld._t == null ? Type.BOTTOM : fld._t; + } + + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited); } + public StringBuilder _printMach( StringBuilder sb, BitSet visited ) { throw Utils.TODO(); } + public int log_size() { return _con.log_size(); } + + @Override + public boolean eq(Node n) { + MemOpNode mem = (MemOpNode)n; // Invariant + return _alias==mem._alias && super.eq(mem); + } + + @Override + int hash() { return _alias ^ super.hash(); } + + @Override + public Parser.ParseException err() { + Type ptr = ptr()._type; + // Already an error, but better error messages come from elsewhere + if( ptr == Type.BOTTOM ) return null; + if( ptr.isHigh() ) return null; // Assume it will fall to not-null + // Better be a not-nil TMP + if( !(ptr instanceof TypeMemPtr tmp && tmp.notNull()) ) + return Parser.error( "Might be null accessing '" + _name + "'",_loc); + if( tmp._obj.field(_name)==null && + CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) + return Parser.error("Accessing unknown field '"+_name+"' from '*"+tmp._obj._name+"'",_loc); + // Sane field + if( off() instanceof ConFldOffNode && CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) + return Parser.error("Accessing unknown field '"+_name+"' from '*"+tmp._obj._name+"'",_loc); + return null; + + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java new file mode 100644 index 00000000..e8e2592c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java @@ -0,0 +1,72 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeMem; +import com.seaofnodes.simple.codegen.GlobalBits; +import com.seaofnodes.simple.util.BAOS; + +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** A Phi for one precise memory alias. */ +public class MemPhiNode extends PhiNode { + + public final int _alias; + + public MemPhiNode(String label, int alias, Node... inputs) { + super(label, inputs); + assert alias > 1; + _alias = alias; + } + + public MemPhiNode(MemPhiNode phi) { + super(phi,phi._label); + _alias = phi._alias; + } + + @Override public Tag serialTag() { return Tag.MemPhi; } + + @Override + public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, + IdentityHashMap<Node,Integer> anodes) { + super.packed(baos,strs,types,anodes); + baos.packed2(_alias); + } + + static Node make(BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases) { + Node[] ins = new Node[bais.packed1()]; + String label = strs[bais.packed2()]; + int alias = bais.packed2(); + if( alias >= GlobalBits.RESERVED ) alias = aliases.map(fileAliases,alias); + return new MemPhiNode(label,alias,ins); + } + + @Override + public Type compute() { + Type t = super.compute(); + assert BulkMemPhiNode.checkMem(this,_alias); + if( !(t instanceof TypeMem tmem) ) return t; + assert tmem._alias==1 || tmem._alias==_alias; + // This Phi structurally knows its alias, but not whether the field is + // final. Do not infer slice finality from evolving input types. + return TypeMem.make(_alias,tmem._t,tmem._one,tmem._clz,false,tmem._escFs,tmem._escAs); + } + + @Override + public Node idealize() { + // A precise memory Phi can always bypass a MemMerge and select its + // matching slice. + for( int i=1; i<nIns(); i++ ) + if( in(i) instanceof MemMergeNode mem ) { + setDef(i,mem.alias(_alias)); + return this; + } + return super.idealize(); + } + + @Override public boolean eq(Node n) { + return _alias==((MemPhiNode)n)._alias && super.eq(n); + } + + @Override int hash() { return _alias; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MinusNode.java new file mode 100644 index 00000000..6101fba2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MinusNode.java @@ -0,0 +1,78 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +public class MinusNode extends Node implements ModeNode { + // Mode is 0 for unknown, 1 for int, 2 for flt + byte _mode; + + public MinusNode(Node in) { super(null, in); } + public MinusNode(Node in, byte mode) { super(null, in); _mode = mode; } + @Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes) { + baos.packed1(_mode); + } + @Override public Tag serialTag() { return Tag.Minus; } + + @Override public String glabel() { return "-"; } + @Override public byte mode() { return _mode; } + @Override public Node setMode(byte mode) { + assert _mode==0 && (mode==1 || mode==2); + unlock(); // _mode participates in GVN hash and equality + _mode = mode; + return this; + } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + in(1)._print0(sb.append("(-"), visited); + return sb.append(")"); + } + + @Override + public Type compute() { + Type t = in(1)._type; + if( t.isHigh() ) + return _mode==0 ? Type.TOP : + _mode==1 ? TypeInteger.TOP : + t instanceof TypeFloat ? t : TypeFloat.F64.dual(); + if( _mode==0 ) + return Type.BOTTOM; + if( _mode==1 ) { + if( t instanceof TypeInteger i0 && + i0._min != Long.MIN_VALUE && i0._max != Long.MIN_VALUE ) + return TypeInteger.make(-i0._max,-i0._min,i0._widen); + return TypeInteger.BOT; + } + if( t instanceof TypeFloat f ) + return f.isConstant() ? TypeFloat.constant(-f.value()) : f; + return TypeFloat.F64; + } + + @Override + public Node idealize() { + // -(-x) is x + if( in(1) instanceof MinusNode minus ) + return minus.in(1); + + // Can we decide int vs flt? + if( _mode==0 ) { + byte mode = (byte)( + in(1)._type instanceof TypeInteger ? 1 : + in(1)._type instanceof TypeFloat ? 2 : 0); + if( mode!=0 ) return setMode(mode).init(); + } + + return null; + } + @Override public Parser.ParseException err() { + return ArithNode.nodeErr(this,null,"-",_mode,true); + } + @Override public boolean eq( Node n ) { return _mode==((MinusNode)n)._mode; } + @Override public int hash() { return _mode; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ModeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ModeNode.java new file mode 100644 index 00000000..dc635447 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ModeNode.java @@ -0,0 +1,12 @@ +package com.seaofnodes.simple.node; + +/** + * A node whose numeric input family is selected once: unknown, integer, or + * floating point. Implementors remain ordinary Nodes; this interface only + * centralizes the structural mode contract shared by arithmetic, comparisons, + * and unary minus. + */ +public interface ModeNode { + byte mode(); + Node setMode(byte mode); +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MulNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MulNode.java new file mode 100644 index 00000000..e773ed57 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MulNode.java @@ -0,0 +1,65 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.util.BitSet; + +public class MulNode extends ArithNode { + @Override boolean allowFloat() { return true; } + public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public MulNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs, mode); } + @Override public Tag serialTag() { return Tag.Mul; } + + @Override public String op() { return "*"; } + + @Override long doOp( long x, long y ) { return x * y; } + @Override double doOp(double x, double y ) { return x * y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { + return TypeInteger.BOT; + } + + @Override + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + Type t1 = lhs._type; + Type t2 = rhs._type; + + // Move constants to RHS: con*arg becomes arg*con + if ( t1.isConstant() && !t2.isConstant() ) + return swap12(); + + // Multiply by constant + if ( t2.isConstant() && t2 instanceof TypeInteger i ) { + // Mul of 1. We do not check for (1*x) because this will already + // canonicalize to (x*1) + long c = i.value(); + if( c==1 ) return lhs; + if( c==0 ) return CodeGen.CODE.ZERO; + // Mul by a power of 2, +/-1. Bit patterns more complex than this + // are unlikely to win on an X86 vs the normal "imul", and so + // become machine-specific. + if( (c & (c-1)) == 0 ) + return new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(c))); + // 2**n + 1, e.g. 9 + long d = c-1; + if( (d & (d-1)) == 0 ) + return new AddNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(d))).peephole(),lhs); + // 2**n - 1, e.g. 31 + long e = c+1; + if( (e & (e-1)) == 0 ) + return new SubNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(e))).peephole(),lhs); + + } + + // Do we have ((x * (phi cons)) * con) ? + // Do we have ((x * (phi cons)) * (phi cons)) ? + // Push constant up through the phi: x * (phi con0*con0 con1*con1...) + Node phicon = AddNode.phiCon(this,true); + if( phicon!=null ) return phicon; + + return super.idealize(); + } + @Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs,_mode); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java new file mode 100644 index 00000000..b4d6d78e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.util.Ary; + + +public interface MultiNode { + + abstract Ary<Node> outs(); + + // Find a projection by index + default ProjNode proj( int idx ) { + for( Node out : outs() ) + if( out instanceof ProjNode prj && prj._idx==idx ) + return prj; + return null; + } + + // Find a projection by index + default CProjNode cproj( int idx ) { + for( Node out : outs() ) + if( out instanceof CProjNode prj && prj._idx==idx ) + return prj; + return null; + } + + // Return not-null if this projection index is a ideal copy. + // Called by ProjNode ideal and used to collapse Multis. + default Node pcopy(int idx) { return null; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/NeverNode.java new file mode 100644 index 00000000..e2c279d2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/NeverNode.java @@ -0,0 +1,23 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeTuple; + +import java.util.BitSet; + +// "Never true" for infinite loop exits +public class NeverNode extends IfNode { + public NeverNode(NeverNode ctrl) { super(ctrl); } + public NeverNode(float ignore, Node ctrl) { super(ctrl,null); } + @Override public Tag serialTag() { return Tag.Never; } + + @Override public String label() { return "Never"; } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); } + + @Override public Type compute() { return TypeTuple.IF_BOTH; } + + @Override public Node idealize() { return null; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/NewNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/NewNode.java new file mode 100644 index 00000000..26a9c0fd --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/NewNode.java @@ -0,0 +1,104 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.SB; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * Allocation! Allocate a chunk of memory, no init. + * Takes in ctrl and size, and TypeStruct. + * Produces a ptr and private memory. +*/ +public class NewNode extends Node implements MultiNode { + public TypeStruct _ts; + public NewNode( TypeStruct ts, Node ctrl, Node size ) { super(ctrl,size); _ts = ts; } + + public Node ctrl() { return in(0); } + public Node size() { return in(1); } + + // --- Serialization + public NewNode(NewNode nnn) { super(nnn); _ts = nnn._ts; } + @Override public Tag serialTag() { return Tag.New; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed2(types.get(_ts)); + } + static Node make( BAOS bais, Type[] types) { + TypeStruct ts = (TypeStruct)types[bais.packed2()]; + return new NewNode(ts,null,null); + } + + @Override public String glabel() { + return _ts.isAry() ? "ary_"+_ts._fields[1]._t.str() : _ts.str(); + } + @Override public String label() { return "new_"+glabel(); } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("new "); + return sb.append(_ts.str()); + } + + @Override public TypeTuple compute() { + if( ctrl()._type.isHigh() ) + return TypeTuple.DEAD_NEW; + TypeStruct ts = _ts.isAry() ? _ts : _ts.makeInit(); + return TypeTuple.make(TypeMemPtr.make(_ts), + // Arrays get pre-zeroed by constructor. + // Objects start with default-zero memory for + // fields that can be zero-initialized. This is + // still private memory, so no aliases escape yet. + TypeMem.make(1,ts,true,false,false,null,null)); + } + + @Override public Node idealize() { return null; } + + @Override boolean _upgradeType( HashMap<String,Type> TYPES) { + Type old = _ts; _ts = (TypeStruct)_ts.upgradeType(TYPES); return old != _ts; + } + + // Two NewNodes are never equal; NewNodes always make independent pointers + // so they are not functional - they internally bump-ptr allocate always a + // new pointer. + @Override public boolean eq(Node n) { return this==n; } + + @Override int hash() { return _ts.hashCode(); } + + // ------------ + // MachNode specifics, shared across all CPUs + public int _arg2Reg, _xslot; + private RegMask _arg3Mask; + private RegMask _retMask; + private RegMask _kills; + public void cacheRegs(CodeGen code) { + _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2,0,code._cCallingConv).firstReg(); + _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3,0,code._cCallingConv); + // Return mask depends on TFP (either GPR or FPR) + _retMask = code._mach.retMask(TypeFunPtr.CALLOC); + // Kill mask is all caller-saves, and any mirror stack slots for args + // in registers. + RegMaskRW kills = code._cCallerSave.copy(); + // Start of stack slots + int maxReg = code._mach.regs().length; + // Incoming function arg slots, all low numbered in the RA + int fslot = cfg0().fun()._maxArgSlot; + // Killed slots for this calls outgoing args + int xslot = code._mach.maxArgSlot(TypeFunPtr.CALLOC,code._cCallingConv); + _xslot = (maxReg+fslot)+xslot; + for( int i=0; i<xslot; i++ ) + kills.set((maxReg+fslot)+i); + _kills = kills; + } + public String op() { return "alloc"; } + public RegMask regmap(int i) { return i==1 ? _arg3Mask : null; } + public RegMask outregmap() { return null; } + public RegMask outregmap(int idx) { return idx==0 ? _retMask : null; } + public RegMask killmap() { return _kills; } + public void asm(CodeGen code, SB sb) { + sb.p("#malloc, ").p(code.reg(size())); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java new file mode 100644 index 00000000..986754f4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java @@ -0,0 +1,828 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.IterPeeps; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.GlobalBits; +import com.seaofnodes.simple.print.IRPrinter; +import com.seaofnodes.simple.print.JSViewer; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.*; +import java.util.*; +import java.util.function.Function; +import static com.seaofnodes.simple.codegen.CodeGen.CODE; + +/** + * All Nodes in the Sea of Nodes IR inherit from the Node class. + * The Node class provides common functionality used by all subtypes. + * Subtypes of Node specialize by overriding methods. + */ +public abstract class Node implements Cloneable { + + /** + * Each node has a unique dense Node ID within a compilation context + * The ID is useful for debugging, for using as an offset in a bitvector, + * as well as for computing equality of nodes (to be implemented later). + */ + public int _nid; + + /** + * Inputs to the node. These are use-def references to Nodes. + * <p> + * Generally fixed length, ordered, nulls allowed, no unused trailing space. + * Ordering is required because e.g. "a/b" is different from "b/a". + * The first input (offset 0) is often a {@link CFGNode} node. + */ + public Ary<Node> _inputs; + + /** + * Outputs reference Nodes that are not null and have this Node as an + * input. These nodes are users of this node, thus these are def-use + * references to Nodes. + * <p> + * Outputs directly match inputs, making a directed graph that can be + * walked in either direction. These outputs are typically used for + * efficient optimizations but otherwise have no semantics meaning. + */ + public Ary<Node> _outputs; + + + /** + * Current computed type for this Node. This value changes as the graph + * changes and more knowledge is gained about the program. + */ + public Type _type; + + + Node(Node... inputs) { + _nid = CODE.getUID(); // allocate unique dense ID + _inputs = new Ary<>(Node.class); + Collections.addAll(_inputs,inputs); + _outputs = new Ary<>(Node.class); + for( Node n : _inputs ) + if( n != null ) + n.addUse( this ); + } + + // Make a Node using the existing arrays of nodes. + // Used by any pass rewriting all Node classes but not the edges. + Node( Node n ) { + assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Select.ordinal(); + _nid = CODE.getUID(); // allocate unique dense ID + _inputs = new Ary<>(n==null ? new Node[0] : n._inputs.asAry()); + _outputs = new Ary<>(Node.class); + _type = n==null ? Type.BOTTOM : n._type; + _deps = null; + _hash = 0; + } + + // Disk/serialized opcode tags + public enum Tag { + Add,And,EQ,NE,LT,LE,ULT, + CallEnd,Call,CheckCast,ConFldOff,Con,CProj, + Div,Escape,Extern,Fun,FunPtr,If,Load,Loop, + MemMerge,Minus,Mul, + New,Never,Not,Or,Parm,Phi,Proj, + ReadOnly,Return,Region,RoundF32, + Sar,Shl,Shr,Start,Stop,Store,Struct,Sub,ToFloat, + XCtrl,Xor,StartCU,StopCU,Guard,MemPhi,BulkMemPhi,PtrToInt; + public static final Tag[] VALS = values(); + public Node make( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) { + return switch(this) { + case Add -> new AddNode(null,null,(byte)bais.packed1()); + case And -> { int mode=bais.packed1(); assert mode==1; yield new AndNode(null,null,null); } + case Div -> new DivNode(null,null,(byte)bais.packed1()); + case If -> new IfNode(null,null); + case Loop -> new LoopNode(null,null,null); + case Minus -> new MinusNode(null,(byte)bais.packed1()); + case Mul -> new MulNode(null,null,(byte)bais.packed1()); + case Never -> new NeverNode(1.2f, null); + case Not -> new NotNode(null); + case Or -> { int mode=bais.packed1(); assert mode==1; yield new OrNode(null,null,null); } + case ReadOnly-> new ReadOnlyNode((Node)null); + case Return ->new ReturnNode(null,null,null,null,null); + case RoundF32-> new RoundF32Node(null); + case PtrToInt-> new PtrToIntNode((Node)null); + case Sar -> { int mode=bais.packed1(); assert mode==1; yield new SarNode(null,null,null); } + case Shl -> { int mode=bais.packed1(); assert mode==1; yield new ShlNode(null,null,null); } + case Shr -> { int mode=bais.packed1(); assert mode==1; yield new ShrNode(null,null,null); } + case Start -> new StartNode(null,null,TypeInteger.BOT); + case Sub -> new SubNode(null,null,(byte)bais.packed1()); + case ToFloat-> new ToFloatNode(null); + case XCtrl -> new XCtrlNode(); + case Xor -> { int mode=bais.packed1(); assert mode==1; yield new XorNode(null,null,null); } + case Guard -> GuardNode.make(bais); + + case EQ -> new BoolNode.EQ (null,null,(byte)bais.packed1()); + case NE -> new BoolNode.NE (null,null,(byte)bais.packed1()); + case LT -> new BoolNode.LT (null,null,(byte)bais.packed1()); + case LE -> new BoolNode.LE (null,null,(byte)bais.packed1()); + case ULT -> new BoolNode.ULT(null,null,(byte)bais.packed1()); + + case Call -> CallNode.make(bais); + case CallEnd-> CallEndNode.make(bais ,types); + case CheckCast -> CheckCastNode.make(bais,types); + case CProj -> CProjNode.make(bais,strs); + case Con -> ConstantNode.make(bais ,types); + case Escape-> EscapeNode.make(bais ,types); + case Extern-> ExternNode.make(bais,strs,types); + case FunPtr-> FunPtrNode.make(bais, types); + case Fun -> FunNode.make(bais,strs,types); + case Load -> new LoadNode(bais,strs,types,fileAliases,aliases); + case MemMerge->MemMergeNode.make(bais); + case MemPhi-> MemPhiNode.make(bais,strs,types,fileAliases,aliases); + case BulkMemPhi-> BulkMemPhiNode.make(bais,strs,types,fileAliases,aliases); + case New -> NewNode.make(bais ,types); + case Parm -> ParmNode.make(bais,strs,types); + case Phi -> PhiNode.make(bais,strs,types); + case Proj -> ProjNode.make(bais,strs); + case Store -> new StoreNode(bais,strs,types,fileAliases,aliases); + case Stop -> StopNode.make(bais); + case StartCU-> StartCUNode.make(bais,strs,types); + case StopCU -> StopCUNode.make(bais); + case Struct-> StructNode.make(bais, types); + case Region-> RegionNode.make(bais); + + default -> throw Utils.TODO(); + }; + } + }; + public Tag serialTag() { throw Utils.TODO(); } + // Serialize extra data, including input counts + public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {} + + // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ" + public String label() { return serialTag().toString(); } + + // Unique label for graph visualization, e.g. "Add12" or "Region30" or "EQ99" + public String uniqueName() { + // Get rid of $ as graphviz doesn't like it + String label = label().replaceAll("\\$", ""); + return label + _nid; + } + + // Graphical label, e.g. "+" or "Region" or "==" + public String glabel() { return label(); } + + // Extra fun stuff, for assembly printing. Jump labels, parser locations, + // variable types, etc. + public String comment() { return null; } + + // ------------------------------------------------------------------------ + + // Debugger Printing. + + // {@code toString} is what you get in the debugger. It has to print 1 + // line (because this is what a debugger typically displays by default) and + // has to be robust with broken graph/nodes. + @Override + public final String toString() { return print(); } + + // This is a *deep* print. We print with a mutually recursive tik-tok + // style; the common _print0 calls the per-Node _print1, which calls back + // to _print0; + public final String print() { + return _print0(new StringBuilder(), new BitSet()).toString(); + } + + // This is the common print: check for repeats, check for DEAD and print + // "DEAD" else call the per-Node print1. + public final StringBuilder _print0(StringBuilder sb, BitSet visited) { + if( visited.get(_nid) && !(this instanceof ConstantNode) ) + return sb.append(label()); + visited.set(_nid); + return isDead() + ? sb.append(uniqueName()).append(":DEAD") + : _print1(sb, visited); + } + // Every Node implements this; a partial-line recursive print + abstract public StringBuilder _print1(StringBuilder sb, BitSet visited); + + public String p(int depth) { return IRPrinter.prettyPrint(this,depth); } + + public boolean isConst() { return false; } + + // ------------------------------------------------------------------------ + // Graph Node & Edge manipulation + + /** + * Gets the ith input node + * @param i Offset of the input node + * @return Input node or null + */ + public Node in(int i) { return _inputs.get(i); } + public Node out(int i) { return _outputs.get(i); } + public final Ary<Node> outs() { return _outputs; } + + public int nIns() { return _inputs.size(); } + + public int nOuts() { return _outputs.size(); } + + public boolean isUnused() { return nOuts() == 0; } + + public CFGNode cfg0() { return (CFGNode)in(0); } + + /** + * Change a <em>def</em> into a Node. Keeps the edges correct, by removing + * the corresponding <em>use->def</em> edge. This may make the original + * <em>def</em> go dead. This function is co-recursive with {@link #kill}. + * <p> + + * This method is the normal path for altering a Node, because it does the + * proper default edge maintenance. It also <em>immediately</em> kills + * Nodes that lose their last use; at times care must be taken to avoid + * killing Nodes that are being used without having an output Node. This + * definitely happens in the middle of recursive {@link #peephole} calls. + * + * @param idx which def to set + * @param new_def the new definition + * @return new_def for flow coding + */ + public <N extends Node> N setDef(int idx, N new_def ) { + unlock(); + Node old_def = in(idx); + if( old_def == new_def ) return new_def; // No change + // If new def is not null, add the corresponding def->use edge + // This needs to happen before removing the old node's def->use edge as + // the new_def might get killed if the old node kills it recursively. + if( new_def != null ) + new_def.addUse(this); + // Set the new_def over the old (killed) edge + _inputs.set(idx,new_def); + if( old_def != null ) { // If the old def exists, remove a def->use edge + if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead + old_def.kill(); // Kill old def + else CODE.add(old_def); // Else old lost a use, so onto worklist + } + moveDepsToWorklist(); + // Return new_def for easy flow-coding + return new_def; + } + public <N extends Node> N setDefX(int idx, N new_def ) { + while( nIns() <= idx ) addDef(null); + return setDef(idx,new_def); + } + + // Remove the numbered input, compressing the inputs in-place. This + // shuffles the order deterministically - which is suitable for Region and + // Phi, but not for every Node. If the def goes dead, it is recursively + // killed, which may include 'this' Node. + public Node delDef(int idx) { + unlock(); + Node old_def = in(idx); + _inputs.del(idx); + if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead + old_def.kill(); // Kill old def + old_def.moveDepsToWorklist(); + return this; + } + + // Insert the numbered input, sliding other inputs to the right + public void insertDef(int idx, Node new_def) { + _inputs.insert(null,idx); + setDef(idx,new_def); + } + + + /** + * Add a new def to an existing Node. Keep the edges correct by + * adding the corresponding <em>def->use</em> edge. + * + * @param new_def the new definition, appended to the end of existing definitions + * @return new_def for flow coding + */ + public Node addDef(Node new_def) { + unlock(); + // Add use->def edge + _inputs.add(new_def); + // If new def is not null, add the corresponding def->use edge + if( new_def != null ) + new_def.addUse(this); + return new_def; + } + + // Breaks the edge invariants, used temporarily + @SuppressWarnings("unchecked") + protected <N extends Node> N addUse(Node n) { _outputs.add(n); return (N)this; } + + // Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place. + // Return true if the output list is empty afterward. + // Error is 'use' does not exist; ok for 'use' to be null. + public boolean delUse( Node use ) { + _outputs.del(_outputs.find(use)); + return _outputs.isEmpty(); + } + + // Shortcut for "popping" until n nodes. A "pop" is basically a + // setDef(last,null) followed by lowering the nIns() count. + public void popUntil(int n) { + unlock(); + while( nIns() > n ) { + Node old_def = _inputs.pop(); + if( old_def != null && // If it exists and + old_def.delUse(this) ) // If we removed the last use, the old def is now dead + old_def.kill(); // Kill old def + } + } + public Node removeLast() { + Node old_def = _inputs.pop(); + if( old_def != null ) + old_def.delUse(this); + return old_def; + } + + /** + * Kill a Node with no <em>uses</em>, by setting all of its <em>defs</em> + * to null. This may recursively kill more Nodes, and is basically dead + * code elimination. + */ + public void kill( ) { + assert isUnused(); // Has no uses, so it is dead + unlock(); + moveDepsToWorklist(); + _type=null; // Flag as dead + while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes + Node old_def = _inputs.removeLast(); + // Revisit neighbor because removed use + if( old_def != null && CODE.add(old_def).delUse(this) ) + old_def.kill(); // If we removed the last use, the old def is now dead + } + assert isDead(); // Really dead now + } + + // Preserve CFG use-ordering when killing + public void killOrdered() { + CFGNode cfg = cfg0(); + cfg._outputs.remove(cfg._outputs.find(this)); + _inputs.set(0,null); + kill(); + } + + + // Mostly used for asserts and printing. + public boolean isDead() { return isUnused() && nIns()==0 && _type==null; } + + // Shortcuts to stop DCE mid-parse + // Add bogus null use to keep node alive + public <N extends Node> N keep() { return addUse(null); } + // Remove bogus null. + @SuppressWarnings("unchecked") + public <N extends Node> N unkeep() { + delUse(null); + return (N)this; + } + // Test "keep" status + public boolean iskeep() { return _outputs.find(null) != -1; } + public void unkill() { + if( unkeep().isUnused() ) + kill(); + } + public void isKill() { + if( isUnused() ) + kill(); + } + + + // Replace self with nnn in the graph, making 'this' go dead + public void subsume( Node nnn ) { + assert nnn!=this; + while( nOuts() > 0 ) { + Node n = _outputs.removeLast(); + n.unlock(); + int idx = n._inputs.find(this); + n._inputs.set(idx,nnn); + nnn.addUse(n); + CODE.add(n); + CODE.addAll(n._outputs); + } + kill(); + } + + // insert `this` immediately after `def` in the same basic block. + public void insertAfter( Node def ) { + CFGNode cfg = def.cfg0(); + int i = cfg._outputs.find(def)+1; + if( cfg instanceof CallEndNode ) { + cfg = cfg.uctrl(); i=0; + } else if( def.in(0) instanceof MultiNode ) { + assert i==0; + i = cfg._outputs.find(def.in(0))+1; + } + + while( cfg.out(i) instanceof PhiNode || cfg.out(i) instanceof CalleeSaveNode ) i++; + cfg._outputs.insert(this,i); + _inputs.set(0,cfg); + } + + // Insert this in front of use.in(uidx) with this, and insert this + // immediately before use in the basic block. + public void insertBefore( Node use, int uidx ) { + CFGNode cfg = use.cfg0(); + int i; + if( use instanceof PhiNode phi ) { + cfg = phi.region().cfg(uidx); + if( cfg instanceof CProjNode && cfg.in(0) instanceof NeverNode nvr ) + cfg = nvr.cfg0(); + i = cfg.nOuts()-1; + } else { + i = cfg._outputs.find(use); + } + cfg._outputs.insert(this,i); + _inputs.set(0,cfg); + if( _inputs._len > 1 && this instanceof SplitNode ) + setDefOrdered(1,use.in(uidx)); + use.setDefOrdered(uidx,this); + } + + public void setDefOrdered( int idx, Node def) { + // If old is dying, remove from CFG ordered + Node old = in(idx); + if( old!=null && old.nOuts()==1 ) { + CFGNode cfg = old.cfg0(); + if( cfg!=null ) { + cfg._outputs.remove(cfg._outputs.find(old)); + old._inputs.set(0,null); + } + } + setDef(idx,def); + } + + public void removeSplit() { + CFGNode cfg = cfg0(); + cfg._outputs.remove(cfg._outputs.find(this)); + _inputs.set(0,null); + if( _inputs._len > 1 ) subsume(in(1)); + } + + // ------------------------------------------------------------------------ + // Graph-based optimizations + + /** + * Try to peephole at this node and return a better replacement Node. + * Always returns some not-null Node (often this). + */ + public final Node peephole( ) { + if( _type==null ) // Brand-new node, never peeped before + JSViewer.show(); + Node n = peepholeOpt(); + if( n!=null ) // Made progress? + JSViewer.show(); // Show again + return n==null ? this : deadCodeElim(n._nid >= _nid ? n.peephole() : n); // Cannot return null for no-progress + } + + /** + * Try to peephole at this node and return a better replacement Node if + * possible. We compute a {@link Type} and then check and replace: + * <ul> + * <li>if the Type {@link Type#isConstant}, we replace with a {@link ConstantNode}</li> + * <li>in a future chapter we will look for a + * <a href="https://en.wikipedia.org/wiki/Common_subexpression_elimination">Common Subexpression</a> + * to eliminate.</li> + * <li>we ask the Node for a better replacement. The "better replacement" + * is things like {@code (1+2)} becomes {@code 3} and {@code (1+(x+2))} becomes + * {@code (x+(1+2))}. By canonicalizing expressions we fold common addressing + * math constants, remove algebraic identities and generally simplify the + * code. </li> + * + * Unlike peephole above, this explicitly returns null for no-change, or not-null + * for a better replacement (which can be this). + * </ul> + */ + public final Node peepholeOpt( ) { + CODE.iterCnt(); + // Compute initial or improved Type + Type old = setType(compute()); + + // Replace constant computations from non-constants with a constant + // node. If peeps are disabled, still allow high Phis to collapse; + // they typically come from dead Regions, and we want the Region to + // collapse, which requires the Phis to die first. + if( !isConst() && !(this instanceof ParmNode parm && parm.inProgress()) && + // A high Load through a bad pointer still owns the source error; + // do not replace it with an unlocated constant before typeCheck. + !(this instanceof LoadNode && err()!=null) && + _type.isHighOrConst() ) + return ConstantNode.make(_type).peephole(); + + // Global Value Numbering + if( _hash==0 ) { + Node n = CODE._gvn.get(this); // Will set _hash as a side effect + if( n==null ) + CODE._gvn.put(this,this); // Put in table now + else { + // Because of random worklist ordering, the two equal nodes + // might have different types. Because of monotonicity, both + // types are valid. To preserve monotonicity, the resulting + // shared Node has to have the best of both types. + n.setType(n._type.join(_type)); + _hash = 0; // Clear, since it never went in the table + return deadCodeElim(n);// Return previous; does Common Subexpression Elimination + } + } + + // Ask each node for a better replacement + Node n = idealize(); + if( n != null ) // Something changed + return n; // Report progress + + if( old!=_type ) return this; // Report progress; + CODE.iterNop(); + return null; // No progress + } + + // m is the new Node, self is the old. + // Return 'm', which may have zero uses but is alive nonetheless. + // If self has zero uses (and is not 'm'), {@link #kill} self. + private Node deadCodeElim(Node m) { + // If self is going dead and not being returned here (Nodes returned + // from peephole commonly have no uses (yet)), then kill self. + if( m != this && isUnused() && !isDead() ) { + // Killing self - and since self recursively kills self's inputs we + // might end up killing 'm', which we are returning as a live Node. + // So we add a bogus extra null output edge to stop kill(). + m.keep(); // Keep m alive + kill(); // Kill self because replacing with 'm' + m.unkeep(); // Okay to peephole m + } + return m; + } + + /** + * This function needs to be + * <a href="https://en.wikipedia.org/wiki/Monotonic_function">Monotonic</a> + * as it is part of a Monotone Analysis Framework. + * <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">See for example this set of slides</a>. + * <p> + * For Chapter 2, all our Types are really integer constants, and so all + * the needed properties are trivially true, and we can ignore the high + * theory. Much later on, this will become important and allow us to do + * many fancy complex optimizations trivially... because theory. + * <p> + * {@link #compute()} needs to be stand-alone, and cannot recursively call compute + * on its inputs, because programs are cyclic (have loops!) and this will just + * infinitely recurse until stack overflow. Instead, compute typically + * computes a new type from the {@link #_type} field of its inputs. + */ + public abstract Type compute(); + + // Set the type. Assert monotonic progress. + // If changing, add users to worklist. + public Type setType(Type type) { + Type old = _type; + assert old == null || type.isa(old) : "Monotonicity test failed: "+this+"#"+_nid+" old="+old+" new="+type; + //assert !type.hasClosedBuilder0(); // New types should not have closed builders + if( old == type ) return old; + _type = type; // Set _type late for easier assert debugging + CODE.addAll(_outputs); + moveDepsToWorklist(); + return old; + } + + @SuppressWarnings("unchecked") + public <N extends Node> N init() { _type = compute(); return (N)this; } + + /** + * This function rewrites the current Node into a more "idealized" form. + * This is the bulk of our peephole rewrite rules, and we use this to + * e.g. turn arbitrary collections of adds and multiplies with mixed + * constants into a normal form that's easy for hardware to implement. + * Example: An array addressing expression: + * <pre> ary[idx+1]</pre> + * might turn into Sea-of-Nodes IR: + * <pre> (ary+12)+((idx+1) * 4)</pre> + * This expression can then be idealized into: + * <pre> ary + ((idx*4) + (12 + (1*4)))</pre> + * And more folding: + * <pre> ary + ((idx<<2) + 16)</pre> + * And during code-gen: + * <pre> MOV4 Rary,[Ridx<<2 +16] // or some such hardware-specific notation </pre> + * <p> + * {@link #idealize} has a very specific calling convention: + * <ul> + * <li>If NO change is made, return {@code null} + * <li>If ANY change is made, return not-null; this can be {@code this} + * <li>The returned Node does NOT call {@link #peephole} on itself; the {@link #peephole} call will recursively peephole it. + * <li>Any NEW nodes that are not directly returned DO call {@link #peephole}. + * </ul> + * <p> + * Examples: + * <table border="3"> + * <tr><th> before </th><th> after </th><th>return </th><th>comment </th></tr> + * <tr><td>{@code (x+5) }</td><td>{@code (x+5) }</td><td>{@code null }</td><td>No change</td></tr> + * <tr><td>{@code (5+x) }</td><td>{@code (x+5) }</td><td>{@code this }</td><td>Swapped arguments</td></tr> + * <tr><td>{@code ((x+1)+2)}</td><td>{@code (x+(1+2))}</td><td>{@code (x+_) }</td><td>Returns 2 new Nodes</td></tr> + * </table> + * + * The last entry deserves more discussion. The new Node {@code (1+2)} + * created in {@link #idealize} calls {@link #peephole} (which then folds + * into a constant). The other new Node {@code (x+3)} does not call + * peephole, because it is returned and peephole itself will recursively + * call peephole. + * <p> + * Since idealize calls peephole and peephole calls idealize, you must be + * careful that all idealizations are <em>monotonic</em>: all transforms + * remove some feature, so that the set of available transforms always + * shrinks. If you don't, you risk an infinite peephole loop! + * + * @return Either a new or changed node, or null for no changes. + */ + public abstract Node idealize(); + + + // Some of the peephole rules get complex, and search further afield than + // just the nearest neighbor. These peepholes can fail the pattern match + // on a node some distance away, and if that node ever changes we should + // retry the peephole. Track a set of Nodes dependent on `this`, and + // revisit them if `this` changes. + Ary<Node> _deps; + + private boolean addDepImpl( Node dep ) { + // Running peepholes during the big assert cannot have side effects + // like adding dependencies. + if( CODE._midAssert ) return false; + if( dep._deps==null ) dep._deps = new Ary<>(Node.class); + if( dep._deps .find(this) != -1 ) return false; // Already on list + if( dep._outputs.find(this) != -1 ) return false; + return true; + } + + /** + * Add a node to the list of dependencies. Only add it if it's not an input + * or output of this node, that is, it is at least one step away. The node + * being added must benefit from this node being peepholed. + */ + public <N extends Node> N addDep( N dep ) { + if( !addDepImpl(dep) ) return dep; + if( dep._inputs .find(this) != -1 ) return dep; // No need for deps on immediate neighbors + dep._deps.add(this); + return dep; + } + + public <N extends Node> N addDepForwards( N dep ) { + if( !addDepImpl(dep) ) return dep; + dep._deps.add(this); + return dep; + } + + // Move the dependents onto a worklist, and clear for future dependents. + public void moveDepsToWorklist( ) { moveDepsToWorklist(CODE._iter); } + public void moveDepsToWorklist( IterPeeps iter ) { + if( _deps == null ) return; + iter.addAll(_deps); + _deps.clear(); + } + + + // Two nodes are equal if they have the same inputs and the same "opcode" + // which means the same Java class, plus same internal parts. + @Override public final boolean equals( Object o ) { + if( o==this ) return true; + if( o.getClass() != getClass() ) return false; + Node n = (Node)o; + int len = _inputs.size(); + if( len != n._inputs.size() ) return false; + for( int i=0; i<len; i++ ) + if( in(i) != n.in(i) ) + return false; + return eq(n); + } + // Subclasses add extra checks (such as ConstantNodes have same constant), + // and can assume "this!=n" and has the same Java class. + public boolean eq( Node n ) { return true; } + + + // Cached hash. If zero, then not computed AND this Node is NOT in the GVN + // table - and can have its edges hacked (which will change his hash + // anyway). If Non-Zero then this Node is IN the GVN table, or is being + // probed to see if it can be inserted. His edges are "locked", because + // hacking his edges will change his hash. + int _hash; + + // If the _hash is set, then the Node is in the GVN table; remove it. + void unlock() { + if( _hash==0 ) return; + Node old = CODE._gvn.remove(this); // Pull from table + assert old==this; + _hash=0; // Out of table now + } + + + // Hash of opcode and inputs + @Override public final int hashCode() { + if( _hash != 0 ) return _hash; + int hash = hash(); + for( Node n : _inputs ) + if( n != null ) + hash = hash ^ (hash<<17) ^ (hash>>13) ^ n._nid; + if( hash==0 ) hash = 0xDEADBEEF; // Bad hash, so use some junky thing + return (_hash=hash); + } + // Subclasses add extra hash info (such as ConstantNodes constant) + int hash() { return 0; } + + // ------------------------------------------------------------------------ + // + + /** Is this Node Memory related */ + public boolean isMem() { return false; } + + /** Pinned in the schedule; these are data nodes whose input#0 is not allowed to change */ + public boolean isPinned() { return false; } + + // ------------------------------------------------------------------------ + // Peephole utilities + + // Swap inputs without letting either input go dead during the swap. + public Node swap12() { + unlock(); // Hash is order dependent + _inputs.swap(1,2); + return this; + } + + /** + * Does this node contain all constants? Ignores in(0), as is usually + * control. In an input is not a constant, we add dep as a dependency to + * it because dep can make progress if the input becomes a constant later. + * It is sufficient for one of the non-const inputs to have the dependency, + * so we don't bother checking the rest. + */ + boolean allCons(Node dep) { + for( int i=1; i<nIns(); i++ ) + if( !(in(i)._type.isConstant()) ) { + dep.addDep(in(i)); // If in(i) becomes a constant later, will trigger some peephole + return false; + } + return true; + } + + + // Make a shallow copy (same class) of this Node, with given inputs and + // empty outputs and a new Node ID. The original inputs are ignored. + // Does not need to be implemented in isCFG() nodes. + Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); } + + public Node copy() { + Node n; + try { n = (Node)clone(); } + catch( Exception e ) { throw new RuntimeException(e); } + n._nid = CODE.getUID(); // allocate unique dense ID + n._inputs = new Ary<>(Node.class); + n._outputs = new Ary<>(Node.class); + n._deps = null; + n._hash = 0; + return n; + } + + // Report any post-optimize errors + public Parser.ParseException err() { return null; } + + // Common integer constants + public static ConstantNode con(long x) { return (ConstantNode)ConstantNode.make(TypeInteger.constant(x)).peephole(); } + + // Utility to walk the entire graph applying a function; return the first + // not-null result. + final public <E> E walk( Function<Node,E> pred ) { + assert CODE._visit.isEmpty(); + E rez = _walk(pred); + CODE._visit.clear(); + return rez; + } + + private <E> E _walk( Function<Node,E> pred ) { + if( CODE._visit.get(_nid) ) return null; // Been there, done that + CODE._visit.set(_nid); + E x = pred.apply(this); + if( x != null ) return x; + for( Node def : _inputs ) if( def != null && (x = def._walk(pred)) != null ) return x; + for( Node use : _outputs ) if( use != null && (x = use._walk(pred)) != null ) return x; + return null; + } + + public void gather(HashMap<String,Integer> strs ) { } + + // If a Type upgrade happens, put on worklist + boolean _upgradeType( HashMap<String,Type> TYPES ) { return false; } + public final void upgradeType( HashMap<String,Type> TYPES ) { + Type old = _type; _type = _type.upgradeType(TYPES); + boolean progress = _upgradeType(TYPES); + if( progress || old != _type ) { + CODE.add(this); + CODE.addAll(_outputs); + if( _deps!=null ) CODE.addAll(_deps); + } + } + + /** + * Debugging utility to find a Node by index + */ + public Node find(int nid) { return _find(nid, new BitSet()); } + private Node _find(int nid, BitSet bs) { + if( bs.get(_nid) ) return null; // Been there, done that + bs.set(_nid); + if( _nid==nid ) return this; + Node x; + for( Node def : _inputs ) if( def != null && (x = def._find(nid,bs)) != null ) return x; + for( Node use : _outputs ) if( use != null && (x = use._find(nid,bs)) != null ) return x; + return null; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/NotNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/NotNode.java new file mode 100644 index 00000000..22b5bffe --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/NotNode.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +public class NotNode extends Node { + public NotNode(Node in) { super(null, in); } + @Override public Tag serialTag() { return Tag.Not; } + + @Override public String glabel() { return "!"; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + in(1)._print0(sb.append("(!"), visited); + return sb.append(")"); + } + + @Override + public TypeInteger compute() { + Type t0 = in(1)._type; + if( t0.isHigh() ) return (TypeInteger)TypeInteger.BOOL.dual(); + if( t0 == Type.NIL || t0 == TypeInteger.ZERO ) return TypeInteger.TRUE; + if( t0 instanceof TypeNil tn && tn.notNull() ) return TypeInteger.FALSE; + if( t0 instanceof TypeInteger i && (i._min > 0 || i._max < 0) ) return TypeInteger.FALSE; + return TypeInteger.BOOL; + } + + @Override + public Node idealize() { return null; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/OrNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/OrNode.java new file mode 100644 index 00000000..ba250c26 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/OrNode.java @@ -0,0 +1,46 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class OrNode extends ArithNode { + public OrNode( Parser.Lexer loc, Node lhs, Node rhs) { super(loc,lhs, rhs, (byte)1); } + @Override public Tag serialTag() { return Tag.Or; } + + @Override public String label() { return "Or"; } + @Override public String op() { return "|"; } + @Override public String glabel() { return "|"; } + + + @Override long doOp( long x, long y ) { return x | y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { + return TypeInteger.BOT; + } + + @Override + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + Type t1 = lhs._type; + Type t2 = rhs._type; + + // Or of 0. We do not check for (0|x) because this will already + // canonicalize to (x|0) + if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 ) + return lhs; + + // Move constants to RHS: con*arg becomes arg*con + if ( t1.isConstant() && !t2.isConstant() ) + return swap12(); + + // Do we have ((x | (phi cons)) | con) ? + // Do we have ((x | (phi cons)) | (phi cons)) ? + // Push constant up through the phi: x | (phi con0|con0 con1|con1...) + Node phicon = AddNode.phiCon(this,true); + if( phicon!=null ) return phicon; + + return super.idealize(); + } + @Override Node copy(Node lhs, Node rhs) { return new OrNode(_loc,lhs,rhs); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java new file mode 100644 index 00000000..6488b827 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -0,0 +1,87 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +public class ParmNode extends PhiNode { + + // Argument indices are mapped one-to-one on CallNode inputs + public final int _idx; // Argument index + public Type _declaredType; + + public ParmNode(String label, int idx, Type declaredType, Node... inputs) { + super(label, inputs); + _idx = idx; + _declaredType = declaredType; + _type = declaredType; + } + public ParmNode(ParmNode parm) { super(parm, parm._label ); _idx = parm._idx; _declaredType = parm._declaredType; } + @Override public Tag serialTag() { return Tag.Parm; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed1(nIns()); // Number of linked calls + baos.packed2(_label==null ? 0 : strs.get(_label)); + baos.packed2(types.get(_declaredType)); // NPE if fails lookup + baos.packed1(_idx); + } + static Node make( BAOS bais, String[] strs, Type[] types) { + Node[] ins = new Node[bais.packed1()]; + String label = strs[bais.packed2()]; + Type minType = types[bais.packed2()]; + return new ParmNode(label, bais.packed1(), minType, ins); + } + + @Override public String label() { return MemOpNode.mlabel(_label); } + + @Override public String glabel() { return _label; } + + @Override protected Type declaredType() { return _declaredType; } + @Override protected Type constrain(Type t) { return t.join(_declaredType); } + + @Override boolean _upgradeType( HashMap<String,Type> TYPES) { + Type t = _declaredType.upgradeType(TYPES); + if( t == _declaredType ) return false; + unlock(); + _declaredType = t; + return true; + } + + public FunNode fun() { return (FunNode)in(0); } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( fun()._name!=null && fun()._name.endsWith("<clinit>") && _label.equals("arg") ) + return sb.append("arg"); + sb.append("Parm_").append(_label).append("("); + for( Node in : _inputs ) { + if (in == null) sb.append("____"); + else in._print0(sb, visited); + sb.append(","); + } + sb.setLength(sb.length()-1); + sb.append(")"); + return sb; + } + + + // Always in-progress until we run out of unknown callers + @Override public boolean inProgress() { return in(0) instanceof FunNode fun && fun.inProgress(); } + + @Override + public Node idealize() { + if( !(region() instanceof FunNode) ) + return in(1); // Input has collapse to e.g. starting control. + // If function is folding, do all possible peeps + if( fun()._folding ) return super.idealize(); + + // Skip most phi optimizations on parms + return null; + } + + @Override public boolean eq( Node n ) { + return ((ParmNode)n)._idx==_idx && super.eq(n); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java new file mode 100644 index 00000000..2c57eb8b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -0,0 +1,294 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.Serialize; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.SB; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +public class PhiNode extends Node { + + public final String _label; + + public PhiNode( String label, Node... inputs) { + super(inputs); + _label = label; + } + public static PhiNode make(String label, Type minType, Node... inputs) { + return minType instanceof TypeMem mem + ? mem._alias==1 + ? new BulkMemPhiNode(label,inputs) + : new MemPhiNode(label,mem._alias,inputs) + : new PhiNode(label, inputs); + } + // Used by ParmNode + public PhiNode(PhiNode phi, String label) { super(phi); _label = label; } + // Used by instruction Selection + public PhiNode(PhiNode phi) { this(phi,phi._label); } + // Used by the infinite-loop exit breaker + public PhiNode(RegionNode r, Node sample) { + super(new Node[]{r}); + _label = ""; + _type = sample._type; + while( nIns() < r.nIns() ) + addDef(sample); + } + @Override public Tag serialTag() { return Tag.Phi; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed1(nIns()); + baos.packed2(_label==null ? 0 : strs.get(_label)); + } + static Node make( BAOS bais, String[] strs, Type[] types) { + Node[] ins = new Node[bais.packed1()]; + return new PhiNode(strs[bais.packed2()], ins); + } + + @Override public String label() { return "Phi_"+MemOpNode.mlabel(_label); } + + @Override public String glabel() { return "φ_"+_label; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( !(region() instanceof RegionNode r) || r.inProgress() ) + sb.append("Z"); + sb.append("Phi("); + for( Node in : _inputs ) { + if (in == null) sb.append("____"); + else in._print0(sb, visited); + sb.append(","); + } + sb.setLength(sb.length()-1); + sb.append(")"); + return sb; + } + + public CFGNode region() { return (CFGNode)in(0); } + @Override public boolean isMem() { return _type instanceof TypeMem; } + @Override public boolean isPinned() { return true; } + boolean isRPC() { return false; } + + @Override + public Type compute() { + if( !(region() instanceof RegionNode r) ) + return region()._type==Type.XCONTROL || region()._type==Type.TOP ? (_type instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type; + // During parsing Phis have to be computed type pessimistically. + if( r.inProgress() ) + return declaredType(); + // Set type to local top of the starting type + Type t = Type.TOP; + for( int i = 1; i < nIns(); i++ ) { + // If the region's control input is live, add this as a dependency + // to the control because we can be peeped should it become dead. + Type ctrl = addDep(r.in(i))._type; + if( ctrl != Type.XCONTROL && ctrl != Type.TOP ) { + if( in(i)._type==Type.BOTTOM ) + return Type.BOTTOM; + t = t.meet(in(i)._type); + } + } + Type declared = declaredType(); + Type newt = constrain(t); + + // phi loop widening part + if( region() instanceof LoopNode && // Only around loops + newt instanceof TypeInteger newi && + // Types changed and are falling (the optimistic case, expected to fall forever) + newi != _type ) { + if( !newi.isConstant() && (!(_type instanceof TypeInteger oldi) || newi._widen <= oldi._widen) ) + return newi.same_but_slightly_wider_than( + declared instanceof TypeInteger ? declared : TypeInteger.BOT); + } + + return newt; + } + + // Ordinary Phis have no parser-supplied type constraint. + protected Type declaredType() { return Type.BOTTOM; } + protected Type constrain(Type t) { return t; } + + @Override + public Node idealize() { + if( !(region() instanceof RegionNode r ) ) + return in(1); // Input has collapse to e.g. starting control. + if( r.inProgress() || r.nIns()<=1 || nOuts()==0 ) + return null; // Input is in-progress + + // If we have only a single unique input, become it. + Node live = singleUniqueInput(); + if( live != null ) + return live; + + // No bother if region is going to fold dead paths soon + for( int i=1; i<nIns(); i++ ) + if( r.in(i)._type == Type.XCONTROL ) + return null; + + // Generic "pull down op" + Node progress; + if( same_op() && (progress = drop_same_op()) != null ) + return progress; + + // If merging Phi(ZERO, guardNZ(N)) - we are losing the cast JOIN effects, so just remove. + if( nIns()==3 ) { + if( in(1) instanceof GuardNode cast && cast._nonZero && in(2)._type.makeZero()==in(2)._type && cast.in(1)!=this ) return cast.in(1); + if( in(2) instanceof GuardNode cast && cast._nonZero && in(1)._type.makeZero()==in(1)._type && cast.in(1)!=this ) return cast.in(1); + } + + // If merging a null-checked null and the checked value, just use the value. + // if( val ) ..; phi(Region,False=0/null,True=val); + // then replace with plain val. + if( nIns()==3 ) { + int nullx = -1; + if( in(1)._type == in(1)._type.makeZero() ) nullx = 1; + if( in(2)._type == in(2)._type.makeZero() ) nullx = 2; + if( nullx != -1 ) { + Node val = in(3-nullx); + if( val instanceof GuardNode cast && cast._nonZero ) + val = cast.in(1); + Node ridom = r.idom(this); + if( ridom instanceof IfNode iff && addDep(iff.pred())==val ) { + // Must walk the idom on the null side to make sure we hit False. + CFGNode idom = r.cfg(nullx); + while( idom != null && idom.nIns() > 0 && idom.in(0) != iff ) idom = idom.idom(); + if( idom instanceof CProjNode proj && proj._idx==1 ) + return val; + } else if( ridom != null ) addDep(ridom); + } + } + + return null; + } + + // Same op on all Phi paths; all ops have only the Phi as a use. + // None have a control input. + private boolean same_op() { + Node busy=null; + for( int i=1; i<nIns(); i++ ) { + Node op = in(i); + if( op.nOuts()==0 ) + { op.kill(); return false; } + if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() ) + return false; // Wrong class or CFG bound or mismatched inputs + if( in(1) instanceof MemOpNode mem ) { + // Mismatched aliases + if( mem._alias != ((MemOpNode)op)._alias ) return false; + // Load is clobbered somewhere, and can not be pulled forward past the Phi? + if( mem instanceof LoadNode ) + for( Node use : op.outs() ) + if( use instanceof StoreNode ) + return false; + } + if( in(1) instanceof EscapeNode ) + return false; + if( in(1) instanceof MemMergeNode ) + return false; // Have to keep aliases straight + if( op.nOuts() > 1 ) { + if( busy==null ) busy = op; + else { // Too many users, but addDep in case lose users + for( Node out : op._outputs ) + if( out!=null && out!=this ) + addDep(out); + for( Node out : busy._outputs ) + if( out!=null && out!=this ) + addDep(out); + return false; + } + } + for( int j=1; j<in(1).nIns(); j++ ) + if( op.in(j) instanceof ScopeNode || (op.in(j)==null ^ in(1).in(j)==null) ) + return false; // Lazy Phi input + } + return true; + } + + private Node drop_same_op() { + assert !(in(1) instanceof CFGNode); + Node op = in(1); + Node cp = op.copy(); + cp._type = null; // Fresh type + cp.addDef(null); // No control + + for( int j=1; j<op.nIns(); j++ ) { + boolean needsPhi = false; + Node x = op.in(j); // Jth input from sample #1 + for( int i=2; i<nIns(); i++ ) + if( in(i).in(j) != x ) + { needsPhi=true; break; } + if( needsPhi ) { + x = make(_label,op.in(j)._type.glb(false)); + x.addDef(region()); + for( int i=1; i<nIns(); i++ ) + x.addDef(in(i).in(j)); + x = x.peephole(); + } + cp.addDef(x); + } + // Test not running backwards, which can happen for e.g. And's + if( cp.compute().isa(compute()) ) + return cp; + cp.kill(); + return null; + } + + /** + * If only single unique input, return it + */ + private Node singleUniqueInput() { + if( region() instanceof LoopNode loop && loop.entry()._type == Type.XCONTROL ) + return null; // Dead entry loops just ignore and let the loop collapse + Node live = null; + for( int i=1; i<nIns(); i++ ) { + // If the region's control input is live, add this as a dependency + // to the control because we can be peeped should it become dead. + if( addDep(region().in(i))._type != Type.XCONTROL && in(i) != this ) + if( live == null || live == in(i) ) live = in(i); + else return null; + } + return live; + } + + @Override + boolean allCons(Node dep) { + if( !(region() instanceof RegionNode r) ) return false; + // When the region completes (is no longer in progress) the Phi can + // become a "all constants" Phi, and the "dep" might make progress. + dep.addDep(this); + if( r.inProgress() ) return false; + return super.allCons(dep); + } + + // True if last input is null + public boolean inProgress() { + return in(nIns()-1) == null; + } + + // Never equal if inProgress. + @Override public boolean eq( Node n ) { + return !inProgress() && super.eq(n); + } + + @Override + public Parser.ParseException err() { + if( _type != Type.BOTTOM && _type != TypeScalar.BOT ) return null; + + // Scalar BOT means we mixed scalar families, e.g. int and ptr. + // Global BOTTOM retains the same role for non-scalar families. + for( int i=1; i<nIns(); i++ ) + // Already an error, but better error messages come from elsewhere + if( in(i)._type == Type.BOTTOM || in(i)._type == TypeScalar.BOT ) + return null; + + SB sb = new SB().p("No common type amongst "); + for( int i=1; i<nIns(); i++ ) + sb.p(in(i)._type.toString()).p(" and "); + return Parser.error(sb.unchar(5).toString(),null); + } + + @Override public void gather( HashMap<String,Integer> strs ) { + Serialize.gather(strs,_label); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/Proj.java b/chapter25/src/main/java/com/seaofnodes/simple/node/Proj.java new file mode 100644 index 00000000..c8b9378f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/Proj.java @@ -0,0 +1,2 @@ +package com.seaofnodes.simple.node; +public interface Proj { int idx(); } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java new file mode 100644 index 00000000..97b9ed45 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java @@ -0,0 +1,71 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.Serialize; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeMem; +import com.seaofnodes.simple.type.TypeTuple; +import com.seaofnodes.simple.util.BAOS; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +public class ProjNode extends Node implements Proj { + + // Which slice of the incoming multipart value + public final int _idx; + + // Debugging label + public final String _label; + + public ProjNode(Node ctrl, int idx, String label) { + super(new Node[]{ctrl}); + _idx = idx; + _label = label; + } + public ProjNode(ProjNode p) { super(p); _idx = p._idx; _label = p._label; } + @Override public Tag serialTag() { return Tag.Proj; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + baos.packed1(_idx); + baos.packed2(_label==null ? 0 : strs.get(_label)); + } + static Node make( BAOS bais, String[] strs ) { + return new ProjNode(null, bais.packed1(), strs[bais.packed2()] ); + } + + @Override public String label() { return _label; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( _label != null ) return sb.append(_label); + if( in(0) instanceof CallEndNode cend && cend.call()!=null ) + return cend.call()._print0(sb,visited); + return sb.append("LONELY PROJ"); + } + + @Override public CFGNode cfg0() { + return in(0) instanceof CFGNode cfg ? cfg : in(0).cfg0(); + } + + @Override public boolean isMem() { return _type instanceof TypeMem; } + @Override public boolean isPinned() { return true; } + + @Override + public Type compute() { + Type t = in(0)._type; + return t instanceof TypeTuple tt ? tt._types[_idx] : t.oob(); + } + + @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); } + + @Override + public boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; } + + @Override + int hash() { return _idx; } + + @Override public int idx() { return _idx; } + + @Override public void gather( HashMap<String,Integer> strs ) { + Serialize.gather(strs,_label); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/PtrToIntMach.java b/chapter25/src/main/java/com/seaofnodes/simple/node/PtrToIntMach.java new file mode 100644 index 00000000..9a9b94fd --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/PtrToIntMach.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.util.SB; + +/** Zero-code machine form of {@link PtrToIntNode}. */ +public class PtrToIntMach extends PtrToIntNode implements MachNode { + public PtrToIntMach(PtrToIntNode ptr) { super(ptr); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress() { return 1; } + @Override public void encoding(Encoding enc) { } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java new file mode 100644 index 00000000..d781620b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeMemPtr; + +import java.util.BitSet; + +/** + * Reinterpret a non-null managed pointer as its raw integer address. + * + * This node changes representation families but emits no code. In particular, + * it is not a {@link CheckCastNode}: no control-dependent proof can turn a + * pointer into an integer. Array-to-C-pointer conversion adds the array body + * offset separately. + */ +public class PtrToIntNode extends Node { + public PtrToIntNode(Node ptr) { super(null,ptr); } + public PtrToIntNode(PtrToIntNode ptr) { super(ptr); } + + @Override public Tag serialTag() { return Tag.PtrToInt; } + @Override public String glabel() { return "(i64*)"; } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return in(1)._print0(sb.append("(i64*)"),visited); + } + + @Override public Type compute() { + return in(1)._type.isHigh() ? TypeInteger.TOP : TypeInteger.BOT; + } + + @Override public Node idealize() { return null; } + + @Override public Parser.ParseException err() { + Type t = in(1)._type; + if( t instanceof TypeMemPtr ptr ) + return ptr.notNull() ? null : Parser.error("Cannot convert a nullable pointer to an integer",null); + return Parser.error("Cannot convert "+t.str()+" to an integer pointer",null); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java new file mode 100644 index 00000000..a35f5a1d --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.util.SB; + +/** + * Cast a pointer to read-only + */ +public class ReadOnlyMach extends ReadOnlyNode implements MachNode { + public ReadOnlyMach( ReadOnlyNode n ) { super(n); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress( ) { return 1; } + @Override public void encoding( Encoding enc ) { } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java new file mode 100644 index 00000000..c36be85a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeMemPtr; +import java.util.BitSet; + +/** + * Cast a pointer to read-only + */ +public class ReadOnlyNode extends Node { + public ReadOnlyNode( Node n ) { super(null,n); } + public ReadOnlyNode( ReadOnlyNode n ) { super(n); } + @Override public Tag serialTag() { return Tag.ReadOnly; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return in(1)._print0(sb.append("(const)"),visited); + } + @Override public Type compute() { + Type t = in(1)._type; + return t instanceof TypeMemPtr tmp ? tmp.makeRO() : t; + } + + @Override public Node idealize() { + if( in(1)._type instanceof TypeMemPtr tmp && tmp.isFinal() ) + return in(1); + return null; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/RegionNode.java new file mode 100644 index 00000000..dc48f6f2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -0,0 +1,175 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.BAOS; +import java.util.*; + +public class RegionNode extends CFGNode { + // Source location for late discovered errors + public Parser.Lexer _loc; + + public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public RegionNode(RegionNode r, Parser.Lexer loc) { super(r); _loc = loc; } + public RegionNode(RegionNode r) { super(r); if( r!=null ) _loc = r._loc; } + @Override public Tag serialTag() { return Tag.Region; } + public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); } + static Node make( BAOS bais ) { return new RegionNode(null,new Node[bais.packed1()]); } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return sb.append(label()); + } + + @Override public boolean blockHead() { return true; } + + @Override + public Type compute() { + if( inProgress() ) return Type.CONTROL; + Type t = Type.XCONTROL; + for (int i = 1; i < nIns(); i++) + t = t.meet(in(i)._type); + return t; + } + + @Override + public Node idealize() { + if( inProgress() ) return null; + + Node progress = deadPath(false); + if( progress!=null ) return progress; + + // If down to a single input, become that input + if( nIns()==2 && !hasPhi() ) + return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own + + // If a CFG diamond with no merging, delete: "if( pred ) {} else {};" + if( !hasPhi() && nIns()>3 && // No Phi users, just a control user + in(1) instanceof CProjNode p1 && + in(2) instanceof CProjNode p2 && + addDep(p1.in(0))==addDep(p2.in(0)) && + p1.in(0) instanceof IfNode iff ) { + // Replace with the iff.ctrl directly + if( nIns()==3 ) return iff.ctrl(); + // Just delete the path for fat Regions + setDef(1,iff.ctrl()); + return delDef(2); + } + + // Flatten stacked regions (no loops involved) + if( getClass() == RegionNode.class && nIns()>2 ) { + for( int i=1; i<nIns(); i++ ) + if( cfg(i) instanceof RegionNode region && region.getClass() == RegionNode.class && region.nIns()>2 && !hasMidUser(region) ) { + assert !region.inProgress(); + // Fold Phis + for( Node use : _outputs ) { + if( use instanceof PhiNode phi ) { + PhiNode phi2 = phi.in(i) instanceof PhiNode phi2x && phi2x.region()==region ? phi2x : null; + for( int j=1; j<region.nIns(); j++ ) + phi.addDef(phi2==null ? phi.in(i) : phi2.in(j)); + CodeGen.CODE.add(phi); + } + } + + // Fold Region + for( int j=1; j<region.nIns(); j++ ) + addDef(region.in(j)); + setDef(i,CodeGen.CODE.XCTRL); + return this; + } + } + + return null; + } + + Node deadPath(boolean skipStart) { + // Delete dead paths into a Region + int path = findDeadInput(skipStart ? 2 :1); + if( path==0 ) return null; + + // Do not delete the entry path of a loop (ok to remove the back edge + // and make the loop a single-entry Region which folds away the Loop). + // Folding the entry path confused the loop structure, moving the + // backedge to the entry point. + if( this instanceof LoopNode loop && loop.entry()==in(path) ) + return null; + return removeDeadPath(path); + } + + public Node removeDeadPath(int path) { + // Cannot use the obvious output iterator here, because a Phi deleting + // an input might recursively delete *itself*. This shuffles the + // output array, and we might miss iterating an unrelated Phi. So on + // rare occasions we repeat the loop to get all the Phis. + int nouts = 0; + while( nouts != nOuts() ) { + nouts = nOuts(); + for( int i=0; i<nOuts(); i++ ) + if( out(i) instanceof PhiNode phi && phi.nIns()==nIns() ) { + CodeGen.CODE.addAll(phi.in(path)._outputs); + CodeGen.CODE.addAll(phi.delDef(path)._outputs); + } + } + return isDead() ? CodeGen.CODE.XCTRL : delDef(path); + } + + private int findDeadInput(int startIdx) { + for( int i=startIdx; i<nIns(); i++ ) + if( in(i)._type==Type.XCONTROL ) + return i; + return 0; // All inputs alive + } + + boolean hasPhi() { + for( Node phi : _outputs ) + if( phi instanceof PhiNode ) + return true; + return false; + } + + // Is there a value in-between 2 Regions? Difficult to correctly fuse + // without extensive testing, so just ignore. + boolean hasMidUser(RegionNode r) { + for( Node use : r._outputs ) { + if( use==this ) continue; + if( use instanceof PhiNode ) { + for( Node data : use._outputs ) { + if( !(data instanceof PhiNode phi2) || phi2.region()!=this ) + { addDep(use); if( data!=null ) addDep(data); return true; } + } + } else + { addDep(use); return true; } // Control user + } + return false; + } + + + // Immediate dominator of Region is a little more complicated. + @Override public int idepth() { + if( CodeGen.CODE.validIDepth(_idepth) ) + return _idepth; + int d=0; + for( Node n : _inputs ) + if( n!=null ) + d = Math.max(d,CodeGen.CODE.iDepthFrom(((CFGNode)n).idepth())); + return _idepth = d; + } + + @Override public CFGNode idom(Node dep) { + CFGNode lca = null; + // Walk the LHS & RHS idom trees in parallel until they match, or either fails. + // Because this does not cache, it can be linear in the size of the program. + for( int i=1; i<nIns(); i++ ) + if( !cfg(i)._type.isHigh() ) + lca = cfg(i)._idom(lca,dep); + return lca; + } + + // True if last input is null + public boolean inProgress() { return nIns()>1 && in(nIns()-1) == null; } + + // Never equal if inProgress + @Override public boolean eq( Node n ) { return !inProgress(); } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ReturnNode.java new file mode 100644 index 00000000..93cf1b91 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -0,0 +1,134 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +/** + * The Return node has two inputs. The first input is a control node and the + * second is the data node that supplies the return value. + * <p> + * In this presentation, Return functions as a Stop node, since multiple <code>return</code> statements are not possible. + * The Stop node will be introduced in Chapter 6 when we implement <code>if</code> statements. + * <p> + * The Return's output is the value from the data node. + */ +public class ReturnNode extends CFGNode { + + public FunNode _fun; + + public ReturnNode(Node ctrl, Node mem, Node data, Node rpc, FunNode fun ) { + super(ctrl, mem, data, rpc); + _fun = fun; + } + public ReturnNode( ReturnNode ret, FunNode fun ) { super(ret); _fun = fun; } + @Override public Tag serialTag() { return Tag.Return; } + + public Node ctrl() { return in(0); } + public Node mem () { return in(1); } + public Node expr() { return in(2); } + public Node rpc () { return in(3); } + @Override public FunNode fun() { return _fun; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("return "); + if( expr()==null ) sb.append("----"); + else expr()._print0(sb, visited); + return sb.append(";"); + } + + // No one unique control follows; can be many call end sites + @Override public CFGNode uctrl() { return null; } + + @Override + public Type compute() { + return TypeTuple.make(ctrl()._type,mem()._type,expr()._type); + } + + @Override public Node idealize() { + if( _fun.isDead() ) return null; + + // Upgrade signature based on return type + Type ret = expr()._type; + TypeFunPtr fcn = _fun.sig(); + if( ret != fcn.ret() && ret.isa(fcn.ret()) ) + _fun.setSig(fcn.makeFrom(ret)); + + // If dead (cannot be reached; infinite loop), kill the exit values + if( ctrl()._type==Type.XCONTROL && + !(mem() instanceof ConstantNode && expr() instanceof ConstantNode) ) { + Node top = ConstantNode.make(Type.TOP).peephole(); + setDef(1,top); + setDef(2,top); + return this; + } + + return null; + } + + @Override public Parser.ParseException err() { + if( ctrl()._type == Type.CONTROL && + expr()._type == Type.TOP ) + return Parser.error("No defined return type",null); + // With no user constructor, <init> doubles as the public no-arg + // constructor. Required fields remain TOP in its private memory: a + // typed parser poison which deliberately emitted no Store or code. + if( ctrl()._type == Type.CONTROL && _fun.isInstance() ) { + TypeStruct self = ((TypeMemPtr)_fun.sig().arg(0))._obj; + if( !CodeGen.hasUserConstructor(self) && expr()._type instanceof TypeMem mem ) + for( Field fld : self._fields ) { + if( !(fld._t instanceof TypeNil tn && tn.notNull()) ) continue; + Type actual = mem._alias==1 && mem._t instanceof TypeStruct ts + ? ts.field(fld._fname)._t + : mem._alias==fld._alias ? mem._t : Type.TOP; + if( actual==Type.TOP || actual instanceof TypeNil atn && atn.nullable() ) + return Parser.error("'"+self._name+"' is not fully initialized, field '"+ + fld._fname+"' is only partially set in the constructor",null); + } + } + return null; + } + + @Override public boolean eq( Node n ) { + return !_fun.isDead() && !((ReturnNode)n)._fun.isDead(); + } + + // ------------ + // MachNode specifics, shared across all CPUs + public String op() { + return _fun._frameAdjust > 0 ? "epilog" : "ret "; + } + // Correct Nodes outside the normal edges + public void postSelect(CodeGen code) { + _fun.setRet(this); + } + public RegMask regmap(int i) { + return i==2 + ? CodeGen.CODE._mach.retMask(_fun.sig()) + : CodeGen.CODE._retMasks[i]; + } + public RegMask outregmap() { return null; } + public void encoding( Encoding enc ) { throw Utils.TODO(); } + public void asm(CodeGen code, SB sb) { + int frameAdjust = fun()._frameAdjust; + if( frameAdjust>0 ) + sb.p("\n").p("rsp += #").p(frameAdjust).p("\nret "); + // Post code-gen, just print the "ret" + if( code._phase.ordinal() < CodeGen.Phase.RegAlloc.ordinal() || + (code._phase.ordinal() == CodeGen.Phase.RegAlloc.ordinal() && !code._regAlloc.done()) ) + // Prints return reg (either RAX or XMM0), RPC and then the + // callee-save registers. + for( int i=2; i<nIns(); i++ ) + sb.p(code.reg(in(i))).p(" "); + // If we did not get the expected rpc, print which one we got + else if( code._regAlloc.regnum(rpc()) != code._mach.rpc() ) + sb.p("[").p(code.reg(rpc())).p("]"); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java b/chapter25/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java new file mode 100644 index 00000000..322a6bc0 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java @@ -0,0 +1,37 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; + +import java.util.BitSet; + +public class RoundF32Node extends Node { + public RoundF32Node(Node lhs) { super(null, lhs); } + @Override public Tag serialTag() { return Tag.RoundF32; } + + @Override public String glabel() { return "(f32)"; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return in(1)._print0(sb.append("((f32)"), visited).append(")"); + } + + @Override + public Type compute() { + if (in(1)._type instanceof TypeFloat i0 && i0.isConstant() ) + return TypeFloat.constant((float)i0.value()); + return in(1)._type; + } + + @Override + public Node idealize() { + Node lhs = in(1); + Type t1 = lhs._type; + + // RoundF32 of float + if( t1 instanceof TypeFloat tf && tf._sz==32 ) + return lhs; + + return null; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/SarNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/SarNode.java new file mode 100644 index 00000000..8ad5988d --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/SarNode.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class SarNode extends ArithNode { + public SarNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } + @Override public Tag serialTag() { return Tag.Sar; } + + @Override public String op() { return ">>"; } + @Override public String glabel() { return ">>"; } + + @Override long doOp( long x, long y ) { return x >> y; } + @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { + if( y.isConstant() ) { + int log = (int)y.value(); + return TypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1); + } + return TypeInteger.BOT; + } + + @Override + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + Type t2 = rhs._type; + + // Sar of 0. + if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 ) + return lhs; + + // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5) + + return super.idealize(); + } + @Override Node copy(Node lhs, Node rhs) { return new SarNode(_loc,lhs,rhs); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ScopeNode.java new file mode 100644 index 00000000..58508a1c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -0,0 +1,562 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Var; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.ParseAll; +import com.seaofnodes.simple.codegen.CompUnit; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import java.util.*; + +/** + * The Scope node is purely a parser helper - it tracks names to nodes with a + * stack of variables. + */ +public class ScopeNode extends MemMergeNode { + + /** + * The control is a name that binds to the currently active control + * node in the graph + */ + public static final String CTRL = "$ctrl"; + public static final String ARG0 = "arg"; + public static final String MEM0 = "$mem"; + + // All active/live variables in all nested scopes, all run together. + // Maps 1-to-1 to the _inputs array. + public final Ary<Var> _vars; + + // Lexical scope is typed: + public static class Kind { + // Basic block scoping + public static class Block extends Kind { } + // Constructor/initializer block for an allocated struct. + public static class Constructor extends Block { + public Constructor(TypeStruct self) { _self = self; } + public final TypeStruct _self; + } + // Function scope + public static class Func extends Kind { public Func(String name) { _name=name;} public final String _name; } + + public int _lexSize; // Number of Vars in this scope + } + public final Ary<Kind> _kinds; + // Lexical scope nesting depth + public int depth() { return _kinds._len; } + public Kind klast() { return _kinds.last(); } + + // Extra guards; tested predicates and cast results + private final Ary<Node> _guards; + + // A new ScopeNode + public ScopeNode() { + super(true); + _vars = new Ary<>(Var .class); + _kinds = new Ary<>(Kind.class); + _guards = new Ary<>(Node.class); + } + + @Override public String label() { return "Scope"; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("Scope[ "); + int j=1; + for( int i=0; i<nIns(); i++ ) { + if( j < depth() && i == _kinds.at(j)._lexSize ) { sb.append("| "); j++; } + Var v = var(i); + sb.append(v._type()); + sb.append(" "); + if( v._final ) sb.append("!"); + sb.append(v._name); + sb.append("="); + Node n = in(i); + while( n instanceof ScopeNode loop ) { + sb.append("Lazy_"); + n = loop.in(i); + } + if( n==null ) sb.append("___"); + else n._print0(sb, visited); + sb.append(", "); + } + sb.setLength(sb.length()-2); + return sb.append("]"); + } + + + public Node ctrl() { return in(0); } + public Node mem() { return in(1); } + public Var var(int i) { return _vars.at(i); } + + /** + * The ctrl of a ScopeNode is always bound to the currently active + * control node in the graph, via a special name '$ctrl' that is not + * a valid identifier in the language grammar and hence cannot be + * referenced in Simple code. + * + * @param n The node to be bound to '$ctrl' + * + * @return Node that was bound + */ + public <N extends Node> N ctrl(N n) { return setDef(0,n); } + public Node mem(Node n) { return setDef(1,n); } + + public void push( Kind kind ) { + kind._lexSize = _vars.size(); + _kinds.push(kind); + } + + // Pop a lexical scope + public void pop() { + promote(null); // Promote forward references to the next outer scope + _pop(); + } + // Pop a scope, no promote FRefs + public void _pop() { + int n = _kinds.pop()._lexSize; + popUntil(n); // Pop off inputs going out of scope + _vars.setLen(n); // Pop off variables going out of scope + } + + + // Look for forward references in the last lexical scope and promote to the + // next outer lexical scope. At the last scope declare them an error. + public void promote(CodeGen code) { + Kind kind = klast(); + int n = kind._lexSize; + for( int i=n; i<nIns(); i++ ) { + Var v = var(i); + Node vn = in(i); + if( !v.isFRef() ) continue; + // Shuffle vars and inputs to move the forward reference to + // *before* the start of the using scope ("as if" it was not + // actually a forward reference). + for( int j=n; j<=i; j++ ) { + Var vtmp = var(j); + Node ntmp = in (j); + v._idx = j; + _vars .set(j,v ); + _inputs.set(j,vn); + v = vtmp; + vn = ntmp; + } + kind._lexSize = ++n; + } + } + + + public boolean inFunction () { return klast() instanceof Kind.Func; } + // Is any enclosing scope a constructor? + public boolean inConstructor() { return constructorSelf() != null; } + // Nearest constructor self type, if any. + public TypeStruct constructorSelf() { + for( int i=depth()-1; i>=0; i-- ) + if( _kinds.at(i) instanceof Kind.Constructor ctor ) + return ctor._self; + else if( _kinds.at(i) instanceof Kind.Func func && FunNode.isInstance(func._name) ) + return ((TypeMemPtr)var(_kinds.at(i)._lexSize).type())._obj; + return null; + } + + // Find nearest enclosing function scope + public int enclosingFunction() { + for( int i=depth()-1; i>=0; i-- ) + if( _kinds.at(i) instanceof Kind.Func func && !FunNode.isInstance(func._name) ) + return i; + throw Utils.TODO("Should not reach here"); + } + + // Find nearest enclosing function or declaration scope + public int enclosingFuncOrDecl() { + for( int i=depth()-1; i>=0; i-- ) + if( _kinds.at(i) instanceof Kind.Func func ) + return i; + throw Utils.TODO("Should not reach here"); + } + + // Which lexical scope contains this Var? + public int kindx( Var v ) { + for( int i=depth()-1; i>=0; i-- ) + if( v._idx >= _kinds.at(i)._lexSize ) + return i; + return -1; + } + + // Which lexical scope contains this Var? + public Kind kind( Var v ) { + int kx = kindx(v); + return kx == -1 ? null : _kinds.at(kx); + } + + public int kindFcnx( Var v ) { + for( int i=depth()-1; i>=0; i-- ) + if( v._idx >= _kinds.at(i)._lexSize && _kinds.at(i) instanceof Kind.Func ) + return i; + throw Utils.TODO("Should not reach here"); + } + + // Which function scope contains this Var? + public Kind kindFcn( Var v ) { + int kx = kindFcnx(v); + return kx == -1 ? null : _kinds.at(kx); + } + + // Find name in reverse, return an index into _vars or -1. Linear scan + // instead of hashtable, but probably doesn't matter until the scan + // typically hits many dozens of variables. + public int find( String name ) { + for( int i=_vars.size()-1; i>=0; i-- ) + if( var(i)._name.equals(name) ) + return i; + return -1; + } + + /** + * Create a new variable name in the current scope + */ + public boolean define( String name, Type declaredType, boolean xfinal, Node init, Parser.Lexer loc ) { + assert _kinds.isEmpty() || name!=MEM0 ; // Later scopes do not define memory + if( depth() > 0 ) + for( int i=_vars.size()-1; i>=klast()._lexSize; i-- ) { + Var n = var(i); + if( n._name.equals(name) ) { + if( !n.isFRef() ) return false; // Double define + FRefNode fref = (FRefNode)in(n._idx); // Get forward ref + if( !xfinal || !declaredType.isConstant() ) throw fref.err(); // Must be a final constant + n.defFRef(declaredType,xfinal,loc); // Declare full correct type, final, source location + setDef(n._idx,fref.addDef(init)); // Set FRef to defined; tell parser also + return true; + } + } + Var v = new Var(nIns(),name,declaredType,xfinal,loc,init==CodeGen.CODE.XCTRL); + _vars.add(v); + // Creating a forward reference + if( init==CodeGen.CODE.XCTRL ) + //init = new FRefNode(v).init(); + throw Utils.TODO(); + addDef(init); + return true; + } + + // Normal lookup was tried and failed. Insert a forward ref outside any + // enclosing Kind.Func scope, skipping any nested Blocks or Allocs and + // return the Var. May insert at the outermost scope, which means this + // must be defined externally, or it's an error. + + public Var defineFRef( String id, Type t, boolean xfinal, Parser.Lexer loc ) { + // Kind/Lexical scope index + int kidx = enclosingFunction(); + int idx = _kinds.at(kidx)._lexSize; + Var var = new Var(idx,id,t,xfinal,loc,true); + FRefNode fref = new FRefNode(id,loc).init(); + fref._type = fref._con = t; + // Insert in the lex scope just prior to kidx + insert(var,fref,kidx); + return var; + } + + + // Parser state carries one complete, conservative memory value. Precise + // alias partitions belong to the graph and are introduced by peepholes. + public Node mem( int alias ) { + assert alias == 1; + return in(update(var(1),null)); + } + public void mem( int alias, Node st ) { + assert alias == 1; + update(var(1),st); + } + + + /** + * Lookup a name in all scopes starting from most deeply nested. + * + * @param name Name to be looked up + * @return null if not found, or the implementing Node + */ + public Var lookup( String name ) { + int idx = find(name); + // -1 is missed in all scopes, not found + return idx == -1 ? null : update(var(idx),null); + } + + /** + * Redefine an existing name + * + * @param name Name being redefined + * @param n The node to bind to the name + */ + public void update( String name, Node n ) { + int idx = find(name); + assert idx>=0; + update(var(idx),n); + } + + public Var update( Var v, Node st ) { + Node old = in(v._idx); + if( old instanceof ScopeNode loop ) { + // Lazy Phi! + Node def = loop.in(v._idx); + old = def instanceof PhiNode phi && loop.ctrl()==phi.region() + // Loop already has a real Phi, use it + ? def + // Set real Phi in the loop head + // The phi takes its one input (no backedge yet) from a recursive + // lookup, which might have insert a Phi in every loop nest. + : loop.setDef(v._idx,PhiNode.make(v._name, v.type(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).init()); + setDef(v._idx,old); + } + //assert !v._final || st==null; + if( st!=null ) setDef(v._idx,st); // Set new value + return v; + } + + /** + * Duplicate a ScopeNode; including all levels, up to Nodes. So this is + * neither shallow (would dup the Scope but not the internal HashMap + * tables), nor deep (would dup the Scope, the HashMap tables, but then + * also the program Nodes). + * <p> + * If the {@code loop} flag is set, the edges are filled in as the original + * Scope, as an indication of Lazy Phis at loop heads. The goal here is to + * not make Phis at loop heads for variables which are never touched in the + * loop body. + * <p> + * The new Scope is a full-fledged Node with proper use<->def edges. + */ + public ScopeNode dup() { return dup(false); } + public ScopeNode dup(boolean loop) { + ScopeNode dup = new ScopeNode(); + // Our goals are: + // 1) duplicate the name bindings of the ScopeNode across all stack levels + // 2) Make the new ScopeNode a user of all the nodes bound + // 3) Ensure that the order of defs is the same to allow easy merging + dup._vars .addAll(_vars ); + dup._kinds .addAll(_kinds ); + dup._guards .addAll(_guards ); + // The dup'd guards all need dup'd keepers, to keep proper accounting + // when later removing all guards + for( Node n : _guards ) + if( !(n instanceof CFGNode) ) + n.keep(); + dup.addDef(ctrl()); // Control input is just copied + + // Memory is one ordinary parser-state value. At loop heads the + // Scope sentinel triggers the same lazy-Phi machinery as variables. + dup.addDef(loop ? this : mem()); + + // Copy of other inputs + for( int i=2; i<nIns(); i++ ) + // For lazy phis on loops we use a sentinel + // that will trigger phi creation on update + dup.addDef(loop && !var(i)._final ? this : in(i)); + return dup; + } + + /** + * Merges the names whose node bindings differ, by creating Phi node for such names + * The names could occur at all stack levels, but a given name can only differ in the + * innermost stack level where the name is bound. + * + * @param that The ScopeNode to be merged into this + * @return A new node representing the merge point + */ + public RegionNode mergeScopes(ScopeNode that, Parser.Lexer loc) { + RegionNode r = ctrl(new RegionNode(loc,null,ctrl(), that.ctrl()).keep()); + this._merge(that,r); + that.kill(); // Kill merged scope + CodeGen.CODE.add(r); + return r.unkeep(); + } + + public void _merge(ScopeNode that, RegionNode r) { + _merge(that,r,nIns()); + } + public void _merge(ScopeNode that, RegionNode r, int max ) { + for( int i = 1; i < max; i++) + if( in(i) != that.in(i) ) { // No need for redundant Phis + // If we are in lazy phi mode we need to a lookup + // by name as it will trigger a phi creation + Var v = var(i); + Node lhs = this.in(this.update(v,null)); + Node rhs = that.in(that.update(v,null)); + setDef(i, PhiNode.make(v._name, v.type(), r, lhs, rhs).peephole()); + } + } + + // Balance arms of an IF. Extra lonely defs are thrown: "if(pred) int x;". + // Forward refs are copied to the other side, "as if" they were there all along. + // 'this' scope is the default, 'scope' might have frefs. + public void balanceIf( ScopeNode scope ) { + if( nIns()==scope.nIns() ) + return; // Fast-path cutout + int kidx = enclosingFunction(); + assert _kinds.at(kidx) == scope._kinds.at(kidx); + if( kidx==0 ) kidx = 1; + for( int i = _kinds.at(kidx-1)._lexSize; i < scope.nIns(); i++ ) { + Var n = scope.var(i); + if( i<nIns() && var(i)==n ) continue; + if( n.isFRef() ) { // RHS has forward refs + _vars.insert(n,i); + insertDef(i,scope.in(i)); + } else + throw Parser.error("Cannot define a '"+n._name+"' on one arm of an if",n._loc); + } + } + + + // ------------------------------------------------ + // peephole the backedge scope into this loop head scope + // We set the second input to the phi from the back edge (i.e. loop body) + public void endLoop(ScopeNode back, ScopeNode exit ) { + Node ctrl = ctrl(); + assert ctrl instanceof LoopNode loop && loop.inProgress(); + ctrl.setDef(2,back.ctrl()); + + this._endLoop( this, back, exit); + back.kill(); // Loop backedge is dead + // Now one-time do a useless-phi removal + this._useless(); + } + + // Fill in the backedge of any inserted Phis + void _endLoop( ScopeNode scope, Node back, Node exit ) { + for( int i=1; i<nIns(); i++ ) { + if( var(i)._final ) continue; // Final vars did not get modified in the loop + Node n = in(i); + if( var(i).type().isHighOrConst() ) { // Cannot lift higher than a constant, so no Phi + n.subsume(n=ConstantNode.make(var(i).type()).init()); + } else if( back.in(i) != scope ) { + PhiNode phi = (PhiNode)n; + assert phi.region()==scope.ctrl() && phi.in(2)==null; + phi.setDef(2,back.in(i)); // Fill backedge + } + if( exit.in(i) == scope ) // Replace a lazy-phi on the exit path also + exit.setDef(i,n); + } + } + + // ------------------------------------------------ + // Up-casting: using the results of an If to improve a value. + // E.g. "if( ptr ) ptr.field;" is legal because ptr is known not-null. + public void addGuards( Node ctrl, Node pred, boolean invert ) { + assert ctrl instanceof CFGNode; + _guards.add(ctrl); // Marker to distinguish 0,1,2 guards + // add pred & its cast to the normal input list, with special Vars + if( pred==null || pred.isDead() ) + return; // Dead, do not add any guards + // Invert the If conditional + if( invert ) + pred = pred instanceof NotNode not ? not.in(1) : CodeGen.CODE.add(new NotNode(pred).peephole()); + Node zeroPred = pred instanceof NotNode not ? not.in(1) : null; + // This is a zero/null test. + // Compute the positive test type. + _addGuard(true,ctrl,pred); + + // Compute the negative test type. + if( zeroPred != null ) + _addGuard(false,ctrl,zeroPred); + } + + private void _addGuard(boolean nonZero, Node ctrl, Node pred) { + Node guard = new GuardNode(nonZero,ctrl,pred).peephole(); + if( guard != pred ) { + pred.keep(); + guard.keep(); + _guards.add(pred); + _guards.add(guard); + replace(pred,guard); + } + } + + + // Remove matching pred/cast pairs from this guarded region. + public void removeGuards( Node ctrl ) { + assert ctrl instanceof CFGNode; + // 0,1 or 2 guards + while( true ) { + Node g = _guards.pop(); + if( g == ctrl ) break; + if( g instanceof CFGNode ) continue; + g .unkill(); // Pop/kill cast + _guards.pop().unkill(); // Pop/kill pred + } + } + + // If we find a guarded instance of pred, replace with the upcasted version + public Node upcastGuard( Node pred ) { + // If finding an instanceof pred, replace with cast. + // Otherwise, just pred itself. + int i = _guards._len; + while( i > 0 ) { + Node cast = _guards.at(--i); + if( cast instanceof CFGNode ) continue; // Marker between guard sets + Node xpred = _guards.at(--i); + if( xpred == pred ) + return cast; + } + return pred; + } + + // Kill guards also + @Override public void kill() { + for( Node n : _guards ) + if( !(n instanceof CFGNode) ) + n.unkill(); + _guards.clear(); + // Can have lazy uses remaining + if( isUnused() ) + super.kill(); + } + + + private void replace( Node old, Node cast ) { + assert old!=null && old!=cast; + for( int i=0; i<nIns(); i++ ) + if( in(i)==old ) + setDef(i,cast); + } + + // ------------------------------------------------------------------------ + // Insert a top-level public external symbol, "as-if" some #include/import + // was executed at the top of tile. + public Var insertExtern( ExternNode ext ) { + // Size of top-level scope + int lexSize = _kinds.at(0)._lexSize; + Var var = new Var(lexSize,ext._extern,ext._con,true,null); + insert(var,ext,0); + return var; + } + + // ------------------------------------------------------------------------ + // Insert a Var with Node n in the lex scope just prior to kidx + private void insert( Var var, Node n, int kidx ) { + // Insert just prior to scope kidx + int nidx = _kinds.at(kidx)._lexSize; + _vars.insert(var,nidx); + insertDef(nidx,n); + // All outer Kinds lexical offsets bump by one + for( int i = kidx; i < depth(); i++ ) + _kinds.at(i)._lexSize++; + // All Vars outside the innermost lexical scope bump out one + for( int i=nidx+1; i<_vars._len; i++ ) + var(i)._idx++; + } + + // Save the existing Node state to an array + public Node[] save() { + Node[] olds = new Node[nIns()]; + for( int i=0; i<nIns(); i++ ) + olds[i] = in(i)==null ? null : in(i).keep(); + return olds; + } + + // Restore saved Node state back into scope + public void restore( Node[] olds ) { + assert olds.length<=nIns(); + for( int i=0; i<olds.length; i++ ) + setDef(i,olds[i]==null ? null : olds[i].unkeep()); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ShlNode.java new file mode 100644 index 00000000..459b26eb --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ShlNode.java @@ -0,0 +1,51 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class ShlNode extends ArithNode { + public ShlNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } + @Override public Tag serialTag() { return Tag.Shl; } + + @Override public String op() { return "<<"; } + @Override public String glabel() { return "<<"; } + + @Override long doOp( long x, long y ) { return x << y; } + @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { + if( x == TypeInteger.ZERO ) + return TypeInteger.ZERO; + if( y.isConstant() ) { + int shf = (int)y.value(); + // If no overflow, shift endpoints + if( !(((x._min<<shf)>>shf) != x._min || + ((x._max<<shf)>>shf) != x._max ) ) + return TypeInteger.make(x._min<<shf,x._max<<shf); + } + return TypeInteger.BOT; + } + + @Override + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + + if( rhs._type instanceof TypeInteger shl && shl.isConstant() ) { + // Shl of 0. + if( (shl.value()&63)==0 ) + return lhs; + // (x + c) << i => (x << i) + (c << i) + if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof TypeInteger c && c.isConstant() ) { + long sum = c.value() << shl.value(); + if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE ) + return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), CodeGen.CODE.con(sum) ); + } + } + + // TODO: x << 3 << (y ? 1 : 2) ==> x << (y ? 4 : 5) + + return super.idealize(); + } + @Override Node copy(Node lhs, Node rhs) { return new ShlNode(_loc,lhs,rhs); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ShrNode.java new file mode 100644 index 00000000..5e4870d1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class ShrNode extends ArithNode { + public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } + @Override public Tag serialTag() { return Tag.Shr; } + + @Override public String op() { return ">>>"; } + @Override public String glabel() { return ">>>"; } + + @Override long doOp( long x, long y ) { return x >>> y; } + @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { + return x == TypeInteger.ZERO ? x : TypeInteger.BOT; + } + + @Override + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + Type t2 = rhs._type; + + // Shr of 0. + if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 ) + return lhs; + + // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5) + + return super.idealize(); + } + @Override Node copy(Node lhs, Node rhs) { return new ShrNode(_loc,lhs,rhs); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java new file mode 100644 index 00000000..4b58187f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.LRG; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; +import java.util.BitSet; + +public abstract class SplitNode extends MachConcreteNode { + public final LRG _lrg; // Live range for split + public final String _kind; // Kind of split + public final byte _round; // Debugging info for which RegAlloc round + public SplitNode(LRG lrg,String kind, byte round, Node[] nodes) { super(nodes); _lrg=lrg; _kind = kind; _round = round; } + @Override public String op() { return "mov"; } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("mov("); + if( in(1) == null ) sb.append("---"); + else in(1)._print0(sb,visited); + return sb.append(")"); + } + public FunNode fun(Encoding enc) { return enc._code._regAlloc.lrg(this)._fun; } + @Override public Type compute() { return in(0)._type; } + @Override public Node idealize() { return null; } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + @Override public String comment() { return _kind + " #"+ _round; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StartCUNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StartCUNode.java new file mode 100644 index 00000000..d828c5f1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StartCUNode.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Serialize; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.BAOS; + +import java.util.HashMap; +import java.util.IdentityHashMap; + +// Start of a single compilation unit, distinct from the outer whole-program Start. +public class StartCUNode extends StartNode { + public final String _fname; + public StartCUNode(StartNode start, StopCUNode stop, Type arg, String fname) { super(start,stop,arg); _fname = fname; } + public StartCUNode(StartCUNode start) { super(start); _fname = start._fname; } + @Override public Tag serialTag() { return Tag.StartCU; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes ) { + baos.packed2(types.get(_arg)); + baos.packed2(strs.get(_fname)); + } + static Node make( BAOS bais, String[] strs, Type[] types ) { + return new StartCUNode(null,null,types[bais.packed2()],strs[bais.packed2()]); + } + + @Override public CFGNode cfg0() { return (CFGNode)in(0); } + + // IDom is cfg0() and depth is always Start+1 which is 0+1 or 1 + @Override public int idepth() { return CodeGen.CODE.iDepthAt(1); } + @Override public CFGNode idom(Node dep) { return cfg0(); } + @Override public void gather( HashMap<String,Integer> strs ) { + Serialize.gather(strs,_fname); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java new file mode 100644 index 00000000..7d2b2773 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -0,0 +1,87 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; +import java.util.Arrays; +import java.util.BitSet; +import java.util.HashSet; + +/** + * The Start node represents the external world calling, be it calling the + * entry <clinit> or just some linked caller calling. + * <p> + * Start initially has 1 input (arg) from outside and the initial control. + * In ch10 we also add mem aliases as structs get defined; each field in struct + * adds a distinct alias to Start's tuple. + * <p> + * By ch25, Start is the external world (or linker) calling with everything + * that has escaped. Start is NOT the entry <clinit> itself, which can assume + * no memory nor fidxs exist nor escaped. Start gets escaped things from Stop; + * together these close the Grand Cycle between the code we know and the code + * we don't. + */ +public class StartNode extends LoopNode implements MultiNode { + + final Type _arg; + + public StartNode(StartNode start, StopNode stop, Type arg) { super(null,start,stop); _arg = arg; } + public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; } + @Override public Tag serialTag() { return Tag.Start; } + public Type arg() { return _arg; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return sb.append(label()); + } + + @Override public CFGNode cfg0() { return null; } + + // Get the one control following; error to call with more than one e.g. an + // IfNode or other multi-way branch. For Start, it's the class init for + // this compilation unit. + @Override public CFGNode uctrl() { + // Find module.<clinit>, it's the start. + CFGNode C = null; + for( Node use : _outputs ) + if( use instanceof FunNode fun && fun.isModInit() ) + { assert C==null; C = fun; } + if( C==null ) + for( FunNode fun : CodeGen.CODE._linker ) + if( fun!=null && !fun.isDead() && fun.isModInit() ) + { assert C==null; C = fun; } + return C; + } + + boolean escapedFIDX(int fidx) { + // Public class fields are callable by the unknown external world + // regardless of the current SCCP approximation of Start memory. + if( CodeGen.CODE.publicFIDX(fidx) ) return true; + if( !(_type instanceof TypeTuple tt) ) + return !_type.isHigh(); // TOP never escapes, BOTTOM always escapes + TypeMem tmem = (TypeMem)tt._types[1]; + if( tmem == TypeMem.BOT ) return false; + if( tmem == TypeMem.TOP ) return false; + if( XInt.isHigh(tmem._escFs) ) + return false; + return XInt.bit(tmem._escFs,fidx); + } + + @Override public TypeTuple compute() { + StopNode stop = (StopNode)in(1); + TypeMem tmem = stop._type instanceof TypeTuple tt + ? tt.mem() + : stop._type.isHigh() ? TypeMem.START : TypeMem.BOT; + assert tmem._alias==1 && !tmem._one; + return TypeTuple.make(Type.CONTROL,tmem,_arg); + } + + @Override public Node idealize() { return null; } + + // No immediate dominator, and idepth==0 + @Override public int idepth() { return CodeGen.CODE.iDepthAt(0); } + @Override public CFGNode idom(Node dep) { return null; } + + @Override public boolean eq( Node n ) { return true; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java new file mode 100644 index 00000000..ab0f5da6 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java @@ -0,0 +1,122 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; + +import java.util.HashMap; +import java.util.IdentityHashMap; + +// Stop of a single compilation unit, distinct from the outer whole-program Stop. +public class StopCUNode extends StopNode { + public StopCUNode() { super(); } + public StopCUNode(StopNode stop) { super(stop); } + @Override public Tag serialTag() { return Tag.StopCU; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); } + static Node make( BAOS bais ) { + StopCUNode stop = new StopCUNode(); + stop.setDefX(bais.packed1()-1,null); + return stop; + } + + public StartCUNode start() { + for( Node use : _outputs ) + if( use instanceof StartCUNode start ) + return start; + return null; + } + + @Override + public TypeTuple compute() { + // During Parsing, new Stops and Returns are being added. This Stop + // cannot know if more are coming, so must assume the worst. + if( CodeGen.CODE._phase ==null || CodeGen.CODE._phase == CodeGen.Phase.Parse ) + return TypeTuple.STATE; + + StartCUNode start = start(); + // Memory start from all returns + TypeMem xmem = TypeMem.START; + // Escape summaries are monotone discovery facts. In Opto, a Return + // can widen from a precise set to FULL; the public-only normalization + // below must not forget private bits already discovered precisely from + // that Return on an earlier SCCP step. + if( CodeGen.CODE._phase == CodeGen.Phase.Opto && + _type instanceof TypeTuple old && old.mem() instanceof TypeMem omem ) + xmem = xmem.makeFrom(XInt.meet(xmem._escFs,omem._escFs), + XInt.meet(xmem._escAs,omem._escAs)); + + for( Node def : _inputs ) { + ReturnNode ret = (ReturnNode)def; + if( ret._type == Type.TOP ) continue; + if( !(ret._type instanceof TypeTuple tret) ) + return TypeTuple.STATE; // Must be Type.BOTTOM + + FunNode fun = ret.fun(); + boolean noUserCtor = fun.isInstance() && + !CodeGen.hasUserConstructor(((TypeMemPtr)fun.sig().arg(0))._obj); + // Instance <init> is an allocation helper, never an externally + // callable constructor when a user constructor exists. Otherwise + // the same no-arg <init> is the public constructor. + boolean pub = fun.isPublic() && (fun.isClz() || noUserCtor); + TypeFunPtr sig = fun.sig(); + // If public init, the function escapes and the related class escapes + if( pub ) { + xmem = xmem.escapes(sig); + // Classes also escape their public clazz pointer, which then + // escapes all their public fields. Private fields do not + // escape, and are only available from the local class code. + if( fun.isClz() ) + xmem = xmem.escapesAliases(sig._sig[0]); + } + + // The instance <init> Start edge supplies conservative pre-Opto + // inputs, but its private-memory result is consumed only by the + // allocation sequence and is never an externally visible return. + if( fun.isInstance() && !noUserCtor ) continue; + + // Does this function escape? If not, then no need to MEET + // here as its effects are not visible to the outside world. + addDepForwards(start); // Check again if function escapes + if( pub || start.escapedFIDX(sig.fidx()) ) { + if( tret.ctl().isHigh() ) continue; // Return not reachable + // Capture (precisely?) all escaping pointer aliases and fidxs. + // Escaped fidxs means the linked world can call that function; + // escaped aliases means the linked world can R/W those aliases. + xmem = xmem.escapes(tret.ret()); + xmem = publicBottom(xmem); + xmem = (TypeMem)xmem.meet(publicBottom(tret.mem(),xmem)); + } + } + assert xmem._alias==1 && !xmem._one; + return TypeTuple.make(Type.CONTROL,xmem,Type.BOTTOM); + } + + // FULL escape sets arise from global BOTTOM. Across a compilation-unit + // boundary global BOTTOM exposes every public field, while retaining any + // private escapes already discovered precisely. + private TypeMem publicBottom(TypeMem mem) { return publicBottom(mem,mem); } + private TypeMem publicBottom(TypeMem mem, TypeMem prior) { + // The pessimistic Iter pass naturally lifts transient FULL inputs to + // precise finite sets. Replacing FULL early there would let a later + // input refinement add bits, reversing Iter's monotonic direction. + // This normalization is needed only for Opto's optimistic grand cycle. + if( CodeGen.CODE._phase != CodeGen.Phase.Opto ) return mem; + int[] escFs = XInt.isHigh(mem._escFs) + ? XInt.meet(prior._escFs,CodeGen.CODE.publicFIDXs()) + : mem._escFs; + int[] escAs = XInt.isHigh(mem._escAs) + ? XInt.meet(prior._escAs,CodeGen.CODE.publicAliases()) + : mem._escAs; + return escFs==mem._escFs && escAs==mem._escAs ? mem : mem.makeFrom(escFs,escAs); + } + + @Override + public Node idealize() { + int len = nIns(); + for( int i=0; i<nIns(); i++ ) + if( in(i) instanceof ReturnNode ret && addDep(ret.fun()).isDead() ) + delDef(i--); + if( len != nIns() ) return this; + return null; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StopNode.java new file mode 100644 index 00000000..45f5519e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StopNode.java @@ -0,0 +1,100 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +// A collection of function ReturnNodes for a compilation unit. +public class StopNode extends CFGNode { + + public StopNode() { + super(); + _type = compute(); + } + public StopNode(StopNode stop) { super(stop); } + @Override public Tag serialTag() { return Tag.Stop; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); } + static Node make( BAOS bais ) { + StopNode stop = new StopNode(); + stop.setDefX(bais.packed1()-1,null); + return stop; + } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + // For the sake of many old tests, and single value prints as "return val" + ReturnNode ret1 = null; + for( Node n : _inputs ) { + if( n instanceof ReturnNode ret ) { + if( ret1 != null ) { ret1=null; break; } + ret1 = ret; + } + } + if( ret1!=null ) return ret1._print0(sb,visited); + + sb.append("Stop[ "); + for( Node n : _inputs ) + if( n instanceof ReturnNode ret ) { + String name = ret.fun()._name; + if( name== null || !name.startsWith("sys.") ) + ret._print0(sb, visited).append(" "); + } + return sb.append("]"); + } + + @Override public boolean blockHead() { return true; } + + + // If a single Return, return it. + // Otherwise, null because ambiguous. + public ReturnNode ret() { + ReturnNode ret1 = null; + for( Node n : _inputs ) { + // TODO: remove isPublic + if( n instanceof ReturnNode ret && ret.fun().isPublic() ) { + if( ret1 != null ) return null; // Ambiguous + ret1 = ret; + } + } + return ret1; + } + + @Override + public TypeTuple compute() { + // During Parsing, new Stops and Returns are being added. This Stop + // cannot know if more are coming, so must assume the worst. + if( CodeGen.CODE._phase ==null || CodeGen.CODE._phase == CodeGen.Phase.Parse ) + return TypeTuple.STATE; + + // Just meet-over-inputs. + TypeTuple tt = TypeTuple.STOP_HIGH; + for( Node def : _inputs ) + tt = (TypeTuple)tt.meet(def._type); + assert tt.mem()._alias==1 && !tt.mem()._one; + return tt; + } + + @Override public Node idealize() { + int len = nIns(); + for( int i=0; i<nIns(); i++ ) + if( in(i).nIns()==0 ) + delDef(i--); + if( len != nIns() ) return this; + return null; + } + + @Override public int idepth() { + if( _idepth!=0 ) return _idepth; + int d=0; + for( Node ret : _inputs ) + // All public results are hooked on the Stop, including data + // values, so must check CFG + if( ret instanceof CFGNode cfg ) + d = Math.max(d,cfg.idepth()+1); + return _idepth=d; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java new file mode 100644 index 00000000..c0006511 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -0,0 +1,321 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.GlobalBits; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * Store represents setting a value to a memory based object, in chapter 10 + * this means a field inside a struct. + */ +public class StoreNode extends MemOpNode { + + final boolean _init; // Initializing writes are allowed to write null + // Semantic store width in bytes. Zero is the sole undecided state; + // otherwise hardware and ideal Stores are the 1/2/4/8-byte variants. + private byte _size; + + /** + * @param name The struct field we are assigning to + * @param mem The memory alias node - this is updated after a Store + * @param ptr The ptr to the struct base where we will store a value + * @param off The offset inside the struct base + * @param value Value to be stored + */ + public StoreNode(Parser.Lexer loc, String name, int alias, Type glb, Node ctrl, Node mem, Node ptr, Node off, Node value, boolean init) { + this(loc,name,alias,glb,ctrl,mem,ptr,off,value,init,(byte)0); + } + private StoreNode(Parser.Lexer loc, String name, int alias, Type glb, Node ctrl, Node mem, Node ptr, Node off, Node value, boolean init, byte size) { + super(loc, name, alias, false, glb, ctrl, mem, ptr, off, value); + _init = init; + _size = size; + } + StoreNode( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) { + super(bais,strs,types,fileAliases,aliases,false); + addDef(null); + _init = bais.read() != 0; + _size = (byte)bais.read(); + } + @Override public Tag serialTag() { return Tag.Store; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + super.packed(baos,strs,types, anodes ); + baos.write(_init ? 1 : 0); + baos.write(_size); + } + + // GraphVis DOT code and debugger labels + @Override public String label() { return "st_"+mlabel(); } + // GraphVis node-internal labels + @Override public String glabel() { return "." +_name+"="; } + @Override public boolean isMem() { return true; } + + public Node nnptr() { return ptr() instanceof GuardNode cast && cast._nonZero ? cast.in(1) : ptr(); } + public Node val() { return in(4); } + public int storeSize() { assert _size==1 || _size==2 || _size==4 || _size==8; return _size; } + @Override public int log_size() { return Integer.numberOfTrailingZeros(storeSize()); } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return sb.append(".").append(_name).append("=").append( val()).append(";"); + } + + @Override + public Type compute() { + Type val = val()._type; + Type mem0 = aliasMem()._type; + Type ptr0 = ptr()._type; + // Validate argument types + if( ptr0.isHigh() ) + return TypeMem.TOP; + if( !(mem0 instanceof TypeMem mem) || + !(ptr0 instanceof TypeMemPtr ptr) ) + return mem0.isHigh() || ptr0==Type.NIL ? TypeMem.TOP : TypeMem.BOT; + + // Sharpen memory value; required for narrowing stores where the parser + // inserts zero/sign masking and somebody reads the TypeMem type. + Type decl = declaredType(); + if( decl != Type.BOTTOM && decl != Type.TOP ) + val = val.join(decl); + //if( err()!=null ) + // val = Type.BOTTOM; + // Allocation uses a private TypeMem and nothing else does. This + // memory is truly private; a temporary singleton until it escapes - + // which is never does in a constructor. + if( !_name.equals("[]") && mem._one ) + // Just track the stored value + return TypeMem.make(_alias,val,true,false,_init,null,null).escapesFrom(val); + + // Normal aliasing Store. + assert mem._alias==1 || mem._alias==_alias; // Perfect aliasing + + // Single alias store. If memory is fat, attempt to get a narrow slice. + Type tfld = mem._alias==1 && mem._t instanceof TypeStruct ts + ? ts._fields[ts.findAlias(_alias)]._t + : mem._t; + + // Same alias, meet into other fields. + Type t = val.meet(tfld); + // Bulk memory's final bit describes its conservative remainder, not + // this newly precise slice. Slice finality comes from the field. + Field fld = ptr._obj.field(_name); + boolean xfinal = fld != null && fld._final; + return TypeMem.make(_alias,t,mem._one,mem._clz,xfinal,mem._escFs,mem._escAs).escapesFrom(val); + } + + @Override + public Node idealize() { + assert !(mem() instanceof CheckCastNode); + + // Freeze the semantic Store variant as soon as the pointer exposes a + // canonical field declaration. Waiting until after structural memory + // peeps can lose the declaration to a flow-sensitive constant field + // state. Imported Stores already carry a serialized non-zero width. + Type storage = storageType(); + if( _size==0 && storage!=Type.BOTTOM && storage!=Type.TOP ) { + unlock(); // Width participates in GVN identity. + _size = storeSize(storage); + return init(); + } + + // A precise Store can confirm its alias from a precise memory input. + // Do not sharpen an alias-#1 (whole-memory) Store in place: users of + // that Store already treat it as the complete memory state. The + // pointer/field path below replaces it with a precise Store wrapped in + // a MemMerge, preserving any other precise aliases in its input. + if( mem()._type instanceof TypeMem mem ) { + assert mem._alias > 0; + if( _alias != 1 && mem._alias != 1 ) + assert _alias == mem._alias; + } + + // Forward-ref loads eventually sharpen to a declared type + Field fld; + if( _alias==1 && ptr()._type instanceof TypeMemPtr tmp && (fld=tmp._obj.field(_name)) != null ) { + // Expand Store(bulkMem,alias#1) into MemMerge(bulkMem,#N:Store(bulkMem,alias#N)). + + // Normally I'd like to just drop in this xform and move on, but + // users of the bulk Store memory might nontheless depend on the + // sharp memory type (e.g. I64) from the stores memory. Using the + // MemMerge means using the bulk memory which e.g. might be BOT. + // Check for store users with the same alias, and use the new store + // directly, preserving the sharper graph. + Type decl = storageType(); + byte size = _size==0 && decl!=Type.BOTTOM && decl!=Type.TOP ? storeSize(decl) : _size; + Node st = new StoreNode(_loc,_name,fld._alias,fld._t,in(0),mem(),ptr(),off(),val(),_init,size).peephole(); + for( int i=0; i<nOuts(); i++ ) { + Node use = out(i); + if( useAlias(use)==fld._alias ) { + int idx = use._inputs.find(this); + assert idx != -1; + use.setDef(idx,st); + i--; // setDef removed use from this Store's outputs + } + } + MemMergeNode mmm = mem() instanceof BulkMemPhiNode bulk + ? bulk.aggregate(mem(),fld._alias,st) + : new MemMergeNode(false,null,mem()); + if( !(mem() instanceof BulkMemPhiNode) ) + mmm.alias(fld._alias,st); + return mmm.init(); + } + // Expose the same effective memory input already observed by compute. + Node aliasMem = aliasMem(); + if( aliasMem != mem() ) { + setDef(1,aliasMem); + CodeGen.CODE.add(aliasMem); + return this; + } + + // Simple store-after-store on same address. Should pick up the + // required init-store being stomped by a first user store. + if( mem() instanceof StoreNode st && + ptr()==st.ptr() && // Must check same object + off()==st.off() && // And same offset (could be "same alias" but this handles arrays to same index) + ptr()._type instanceof TypeMemPtr && // No bother if weird dead pointers + // Must have exactly one use of "this" or you get weird + // non-serializable memory effects in the worse case. + checkOnlyUse(st) ) { + assert _name==st._name; // Equiv class aliasing is perfect + setDef(1,st.mem()); + return this; + } + + // Simple store-after-MemMerge to a known alias can bypass. Happens when inlining. + if( _alias != 1 && mem() instanceof MemMergeNode mem ) { + setDef(1,mem.alias(_alias)); + return this; + } + + // Resolve the semantic Store variant exactly once, after structural + // memory/alias peeps. This avoids publishing a half-sharpened Store + // while BulkMemPhi is constructing precise slices. + if( storage != Type.BOTTOM && storage != Type.TOP ) + assert _size==storeSize(storage) : "Store width changed for '"+_name+"': "+_size+" from "+storage; + + // Value is automatically truncated by narrow store + if( _size!=0 && val() instanceof AndNode and && and.in(2)._type.isConstant() ) { + int log = log_size(); + if( log<3 ) { // And-mask vs narrow store + long mask = ((TypeInteger)and.in(2)._type).value(); + long bits = (1L<<(8<<log))-1; + // Mask does not mask any of the stored bits + if( (bits&mask)==bits ) + // So and-mask is already covered by the store + { setDef(4,and.in(1)); return this; } + } + } + + // Store will chop high order bits off; math to change those bits can be dropped. + if( _size!=0 && val() instanceof SarNode shr && + shr.in(1) instanceof ShlNode shl && + shr.in(2)._type.isConstant() && + shl.in(2)._type.isConstant() ) { + TypeInteger shrC = (TypeInteger) shr.in(2)._type; + + // size of the thing that sign-extends + int base_size = (1 << shr.in(1)._type.log_size()) << 3; + int not_affected_bits = base_size - (int) shrC.value(); + int store_size = (1 << log_size()) << 3; + // if the store is unrelated to the shift amount, then get rid of the shift + if( shl.in(2)._type == shr.in(2)._type && shrC.value() >= store_size && not_affected_bits >= store_size) { + setDef(4, shl.in(1)); + return this; + } + } + + return null; + } + + private static byte storeSize(Type decl) { + // `decl` is the target field declaration, not the stored value. In + // particular a constant zero stored into u32 is still a 4-byte Store; + // taking the constant's GLB here would incorrectly select i64. + byte size = (byte)(1 << decl.log_size()); + assert size==1 || size==2 || size==4 || size==8; + return size; + } + + // Flow-sensitive pointer types can carry a current field value (e.g. the + // constant zero in a u32 field). Storage width comes from the canonical + // struct declaration. If the pointer/definition is not available yet, + // leave the Store undecided instead of consulting the value or `_con`. + private Type storageType() { + // Freshly parsed field stores retain the declaring structure on their + // symbolic offset even after the pointer's flow type sharpens. + if( off() instanceof ConFldOffNode coff ) { + Field fld = coff._ts.field(_name); + if( fld != null && fld._t != null ) return fld._t; + } + if( !(ptr()._type instanceof TypeMemPtr tmp) ) return Type.BOTTOM; + Type base = Parser.TYPES.get(tmp._obj._name); + TypeStruct obj = base instanceof TypeStruct ts ? ts : tmp._obj; + Field fld = obj.field(_name); + // Imported optimized structs can expose the current constant field + // value instead of its declaration. A constant has no storage width; + // an imported Store already carries its serialized, frozen `_size`. + return fld == null || fld._t == null || fld._t.isConstant() ? Type.BOTTOM : fld._t; + } + + // Alias required by a direct consumer of this Store's memory result. + // Keep in sync with BulkMemPhiNode.outputAlias. + private int useAlias(Node use) { + return switch( use ) { + case MemOpNode mem -> mem._alias; + case MemPhiNode phi -> phi._alias; + case EscapeNode esc -> esc.pub()==this ? esc.fld()._alias : 0; + default -> 0; + }; + } + + // Select a precise alias from a whole-memory partition. Alias #1 is the + // bulk remainder itself. + private static Node memSlice( Node mem, int alias ) { + return alias != 1 && mem instanceof MemMergeNode merge + ? merge.alias(alias) + : mem; + } + + // Semantic memory input for this Store. Compute defines behavior from + // this view; ideal exposes the same edge in the graph. + private Node aliasMem() { + Node mem = mem(); + if( _alias != 1 && mem instanceof MemMergeNode merge ) + mem = merge.alias(_alias); + return mem; + } + + // Check that "mem" has no uses except "this" + private boolean checkOnlyUse(Node mem) { + if( mem.nOuts()==1 ) return true; + // Add deps on the other uses (can be e.g. ScopeNode mid-parse) so that + // when the other uses go away we can retry. + for( Node use : mem._outputs ) + if( use != this ) + addDep(use); + return false; + } + + @Override + public Parser.ParseException err() { + Parser.ParseException err = super.err(); + if( err != null ) return err; + if( _size==0 && CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) + throw Utils.TODO("Failed to decide Store size"); + if( ptr()._type == Type.TOP ) + return null; // This means we have an error input, report elsewhere + TypeMemPtr tmp = (TypeMemPtr)ptr()._type; + Field f = tmp._obj.field(_name); + if( f!=null && f._final && !_init ) + return Parser.error("Cannot modify final field '"+_name+"'",_loc); + return null; + } + + @Override public boolean eq(Node n) { return _size==((StoreNode)n)._size && super.eq(n); } + @Override int hash() { return super.hash() ^ _size; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StructNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StructNode.java new file mode 100644 index 00000000..4dcc695f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StructNode.java @@ -0,0 +1,60 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * Build a compound object + */ +public class StructNode extends Node { + + public final TypeStruct _ts; + public StructNode(TypeStruct ts) { _ts=ts; assert !ts._open; } + @Override public Tag serialTag() { return Tag.Struct; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { + //baos.packed1(nIns()); + //baos.packed2(_label==null ? 0 : strs.get(_label)); + //baos.packed2(types.get(_type)); // Write _type not _minType, which can be higher + throw Utils.TODO(); + } + static Node make( BAOS bais, Type[] types) { + //Node[] ins = new Node[bais.packed1()]; + //return new StructNode(types[bais.packed2()], ins); + throw Utils.TODO(); + } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + if( _ts==null ) return sb.append("STRUCT?"); + sb.append(_ts._name).append(" { "); + for( int i=0; i<nIns(); i++ ) { + sb.append(_ts._fields[i]._fname).append(":"); + sb.append(in(i)==null ? Type.BOTTOM : in(i)._type); + sb.append("; "); + } + sb.setLength(sb.length()-2); + return sb.append("}"); + } + + @Override + public TypeStruct compute() { + if( _ts==null ) return TypeStruct.BOT; + Field[] fs = new Field[_ts._fields.length]; + for( int i=0; i<fs.length; i++ ) + fs[i] = _ts._fields[i].makeFrom(in(i)==null ? Type.TOP : in(i)._type); + return TypeStruct.make(_ts._name,false,fs); + } + + @Override + public Node idealize() { return null; } + + @Override + public boolean eq(Node n) { return _ts == ((StructNode)n)._ts; } + + @Override + int hash() { return _ts==null ? 0 : _ts.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/SubNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/SubNode.java new file mode 100644 index 00000000..aba1df90 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/SubNode.java @@ -0,0 +1,45 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.util.BitSet; + +public class SubNode extends ArithNode { + @Override boolean allowFloat() { return true; } + public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + public SubNode(Node lhs, Node rhs, byte mode) { super(null, lhs, rhs,mode); } + @Override public Tag serialTag() { return Tag.Sub; } + + @Override public String op() { return "-"; } + + @Override long doOp( long x, long y ) { return x - y; } + @Override double doOp(double x, double y ) { return x - y; } + @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { + // Sub of same is 0 + if( in(1)==in(2) ) + return TypeInteger.ZERO; + // Fold ranges like {2-3} - {0-1} into {1-3}. + if( !AddNode.overflow(x._min,-y._max) && + !AddNode.overflow(x._max,-y._min) && + y._min != Long.MIN_VALUE ) + return TypeInteger.make(x._min-y._max,x._max-y._min); + + return TypeInteger.BOT; + } + + @Override + public Node idealize() { + // x - (-y) is x+y + if( in(2) instanceof MinusNode minus ) + return new AddNode(in(1),minus.in(1)); + + // (-x) - y is -(x+y) + if( in(1) instanceof MinusNode minus ) + return new MinusNode(new AddNode(minus.in(1),in(2)).peephole()); + + return super.idealize(); + } + + @Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs,_mode); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java new file mode 100644 index 00000000..2d8cc788 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.type.TypeInteger; + +import java.util.BitSet; + +public class ToFloatNode extends Node { + public ToFloatNode(Node lhs) { super(null, lhs); } + @Override public Tag serialTag() { return Tag.ToFloat; } + + @Override public String glabel() { return "(flt)"; } + + @Override + public StringBuilder _print1(StringBuilder sb, BitSet visited) { + return in(1)._print0(sb.append("(flt)"), visited); + } + + @Override + public Type compute() { + if( in(1)._type.isHigh() ) return TypeFloat.F64.dual(); + if( in(1)._type == Type.NIL ) return TypeFloat.FZERO; + if( in(1)._type instanceof TypeInteger i0 && i0.isConstant() ) + return TypeFloat.constant(i0.value()); + return TypeFloat.F64; + } + + @Override public Node idealize() { return null; } + @Override Node copy(Node lhs, Node rhs) { return new ToFloatNode(lhs); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/TypeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/TypeNode.java new file mode 100644 index 00000000..3ab2d6f9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/TypeNode.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; +import java.util.HashMap; + +// Node carries an extra internal type +public abstract class TypeNode extends Node { + public Type _con; + public TypeNode(Type t, Node... nodes) { super(nodes); assert t!=null; _con = t; } + public TypeNode(TypeNode tn) { super(tn); _con = tn._con; } + // Copy edges from ideal, but 'this' is a Type-Mach-Node (e.g. MemOpX86) and needs a _con + public TypeNode(Node ideal, Type con) { super(ideal); _con = con; } + + // Upgrade the internal type + @Override boolean _upgradeType( HashMap<String,Type> TYPES) { + return liftType(_con.upgradeType(TYPES)); + } + + // Lift type + public boolean liftType( Type t ) { + if( t == _con ) return false; + unlock(); // Unlock before changing _con + _con = t; + return true; + } + + @Override public boolean eq(Node n) { return _con==((TypeNode)n)._con; } + @Override int hash() { return _con.hashCode(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java new file mode 100644 index 00000000..a8c06b37 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java @@ -0,0 +1,15 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; + +public class XCtrlNode extends CFGNode { + public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); } + @Override public Tag serialTag() { return Tag.XCtrl; } + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); } + @Override public boolean isConst() { return true; } + @Override public Type compute() { return Type.XCONTROL; } + @Override public Node idealize() { return null; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/XorNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/XorNode.java new file mode 100644 index 00000000..95d4461e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/XorNode.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class XorNode extends ArithNode { + public XorNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs, (byte)1); } + @Override public Tag serialTag() { return Tag.Xor; } + + @Override public String op() { return "^"; } + @Override public String glabel() { return "^"; } + + @Override long doOp( long x, long y ) { return x ^ y; } + @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { + return TypeInteger.BOT; + } + + @Override + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + Type t1 = lhs._type; + Type t2 = rhs._type; + + // Xor of 0. We do not check for (0^x) because this will already + // canonicalize to (x^0) + if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 ) + return lhs; + + // Move constants to RHS: con*arg becomes arg*con + if ( t1.isConstant() && !t2.isConstant() ) + return swap12(); + + // Do we have ((x ^ (phi cons)) ^ con) ? + // Do we have ((x ^ (phi cons)) ^ (phi cons)) ? + // Push constant up through the phi: x ^ (phi con0^con0 con1^con1...) + Node phicon = AddNode.phiCon(this,true); + if( phicon!=null ) return phicon; + + return super.idealize(); + } + @Override Node copy(Node lhs, Node rhs) { return new XorNode(_loc,lhs,rhs); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java new file mode 100644 index 00000000..1c865254 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java @@ -0,0 +1,20 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class AddARM extends MachConcreteNode implements MachNode { + AddARM( Node add) { super(add); } + @Override public String op() { return "add"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + // ADD (shifted register) + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_ADD); } + // General form: "rd = rs1 + rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java new file mode 100644 index 00000000..eb75c1a0 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class AddFARM extends MachConcreteNode implements MachNode { + AddFARM(Node addf) { super(addf); } + @Override public String op() { return "addf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } + + //FADD (scalar) + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_OP_ADD);} + + // Default on double precision for now(64 bits) + // General form: "addf rd = src1 + src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java new file mode 100644 index 00000000..93c9e8c3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java @@ -0,0 +1,27 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; + +public class AddIARM extends MachConcreteNode implements MachNode { + final int _imm; + AddIARM(Node add, int imm) { + super(add); + _inputs.pop(); + _imm = imm; + } + @Override public String op() { + return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi"); + } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + //ADD (immediate) + @Override public void encoding( Encoding enc ) { + arm.imm_inst(enc,this, in(1), arm.OPI_ADD,_imm); + } + // General form: "addi rd = rs1 + imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java new file mode 100644 index 00000000..77115d54 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java @@ -0,0 +1,21 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.SB; + +public class AndARM extends MachConcreteNode implements MachNode { + AndARM(Node and) { super(and); } + @Override public String op() { return "and"; } + @Override public String glabel() { return "&"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + // AND (shifted register) + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_AND); } + // General form: #rd = rs1 & rs2 + @Override public void asm(CodeGen code, SB sb){ + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java new file mode 100644 index 00000000..5b739cd3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java @@ -0,0 +1,24 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class AndIARM extends MachConcreteNode implements MachNode { + final int _imm; + AndIARM(Node and, int imm) { + super(and); + _inputs.pop(); + _imm = imm; + } + @Override public String op() { return "andi"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + arm.imm_inst_n(enc,this, in(1), arm.OPI_AND,_imm); + } + // General form: "andi rd = rs1 & imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #").p(arm.decodeImm12(_imm)); + } +}
\ No newline at end of file diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java new file mode 100644 index 00000000..b83fb3cb --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java @@ -0,0 +1,18 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Arithmetic Shift Right (register) +public class AsrARM extends MachConcreteNode implements MachNode { + AsrARM(Node asr) {super(asr);} + @Override public String op() { return "sar"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } + @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_ASR); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java new file mode 100644 index 00000000..38373e67 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.SB; + +public class AsrIARM extends MachConcreteNode implements MachNode { + final int _imm; + AsrIARM(Node asr, int imm) { + super(asr); + _inputs.pop(); + _imm = imm; + } + @Override public String op() { return "asri"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + short rd = enc.reg(this); + short rn = enc.reg(in(1)); + assert _imm > 0; + enc.add4(arm.imm_shift(arm.OPI_ASR,_imm, 0b111111, rn, rd)); + } + // General form: "asri rd = rs1 >> imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #").p(_imm); + } +}
\ No newline at end of file diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java new file mode 100644 index 00000000..5cf8a89c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java @@ -0,0 +1,79 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Jump on flags, uses flags +public class BranchARM extends IfNode implements MachNode, RIPRelSize { + String _bop; + final boolean _fp; + BranchARM(IfNode iff, String bop, boolean fp ) { + super(iff); + _bop = bop; + _fp = fp; + } + @Override public String op() { return "j"+_bop; } + @Override public String label() { return op(); } + + @Override public void postSelect(CodeGen code) { + Node set = in(1); + if( set==null ) return; // Never-node cutout + Node cmp = set.in(1); + // Bypass an expected Set and just reference the cmp directly + if( set instanceof SetARM) + _inputs.set(1,cmp); + else + throw Utils.TODO(); + } + + @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; } + @Override public RegMask outregmap() { return null; } + @Override public void negate() { _bop = negate(_bop); } + + @Override public void encoding( Encoding enc ) { + // Assuming that condition flags are already set. These flags are set + // by comparison (or sub). No need for regs because it uses flags + if( in(1)!=null ) { + // B.cond + enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop, _fp)) ); + } else { + if( _bop=="!=" ) return; // Inverted, no code + enc.add4(arm.b(arm.OP_UJMP, 0)); + } + enc.jump(this,cproj(0)); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code + if( -(1<<19) <= delta && delta < (1<<19) ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch + if( opLen==4 ) { + enc.patch4(opStart,arm.b_cond(arm.OP_BRANCH, delta, arm.make_condition(_bop, _fp))); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + if( in(1)!=null ) { // Never-node + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(src).p(" "); + } else if( _bop=="!=" ) { + sb.p("never"); + return; + } + CFGNode prj = cproj(0).uctrlSkipEmpty(); + if( !prj.blockHead() ) prj = prj.cfg0(); + sb.p(label(prj)); + } + @Override public String comment() { return "L"+cproj(1)._nid; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java new file mode 100644 index 00000000..8f070525 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java @@ -0,0 +1,49 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.SB; + +public class CallARM extends CallNode implements MachNode, RIPRelSize { + final TypeFunPtr _tfp; + final String _name; + CallARM(CallNode call, TypeFunPtr tfp) { + super(call); + _inputs.pop(); // Pop constant target + assert tfp.isConstant(); + _tfp = tfp; + _name = CodeGen.CODE.funcName(tfp.fidx()); + } + + @Override public String op() { return "call"; } + @Override public String label() { return op(); } + @Override public String name() { return _name; } + @Override public TypeFunPtr tfp() { return _tfp; } + @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); } + @Override public RegMask outregmap() { return null; } + @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr + + @Override public void encoding( Encoding enc ) { + FunNode fun = CodeGen.CODE.link(_tfp); + if( fun==null ) enc.external(this,_name); + else enc.relo(this); + // BL + enc.add4(arm.b(arm.OP_CALL,0)); // Target patched at link time + } + + // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding + @Override public byte encSize(int delta) { return 4; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart,arm.b(arm.OP_CALL,delta)); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(_name).p(" "); + for( int i=0; i<nargs(); i++ ) + sb.p(code.reg(arg(i+2))).p(", "); + sb.unchar(2); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java new file mode 100644 index 00000000..c84aa9e5 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java @@ -0,0 +1,32 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class CallRRARM extends CallNode implements MachNode { + CallRRARM(CallNode call) { super(call); } + @Override public String op() { return "callr"; } + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { + return i==_inputs._len + ? arm.RMASK // Function call target + : arm.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument + } + @Override public RegMask outregmap() { return null; } + + @Override public void encoding( Encoding enc ) { + // Needs a register, typically a jump-and-link-register opcode + // blr + short self = enc.reg(this); + enc.add4(arm.blr(arm.OP_CALLRARM, self)); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(fptr())).p(" "); + for( int i=0; i<nargs(); i++ ) + sb.p(code.reg(arg(i))).p(", "); + sb.unchar(2); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java new file mode 100644 index 00000000..794ff0c9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class CmpARM extends MachConcreteNode implements MachNode { + CmpARM( Node cmp ) { super(cmp); } + @Override public String op() { return "cmp"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.FLAGS_MASK; } + + // SUBS (shifted register) + @Override public void encoding( Encoding enc ) { arm.r_reg_subs(enc,this,arm.OP_CMP); } + + // General form: "cmp rs1, rs2" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="flags" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java new file mode 100644 index 00000000..67ffc27b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// compare instruction on float regs input(D-registers) +public class CmpFARM extends MachConcreteNode implements MachNode{ + CmpFARM(Node cmp) {super(cmp);} + @Override public String op() { return "cmpf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } + @Override public RegMask outregmap() { return arm.FLAGS_MASK; } + + // FCMP + @Override public void encoding( Encoding enc ) { arm.f_cmp(enc,this); } + // General form: "cmp d0, d1" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="flags" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java new file mode 100644 index 00000000..f84a1649 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Compare with immediate. +// CMP (immediate) - SUBS +public class CmpIARM extends MachConcreteNode implements MachNode { + final int _imm; + final String _bop; + CmpIARM(BoolNode bool, int imm) { + super(bool); + _inputs.pop(); + _bop = bool.op(); + _imm = imm; + } + CmpIARM( CmpIARM cmp ) { + super((Node)null); + addDef(null); + addDef(cmp.in(1)); + _bop = cmp._bop; + _imm = cmp._imm; + } + @Override public String op() { return _imm==0 ? "test" : "cmp"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.FLAGS_MASK; } + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new CmpIARM(this); } + + @Override public void encoding( Encoding enc ) { arm.imm_inst_subs(enc,in(1),in(1), arm.OPI_CMP,_imm); } + + // General form: "cmp rs1, 1" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="flags" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))); + if( _imm != 0 ) sb.p(", #").p(_imm); + } + } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java new file mode 100644 index 00000000..de254eeb --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class DivARM extends MachConcreteNode implements MachNode { + DivARM( Node div ) { super(div); } + @Override public String op() { return "div"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + // SDIV + @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_DIV,3); } + // General form: "div dst /= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java new file mode 100644 index 00000000..813bebe4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class DivFARM extends MachConcreteNode implements MachNode { + DivFARM( Node divf) { super(divf); } + @Override public String op() { return "divf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } + + // FDIV (scalar) + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_DIV); } + // General form: "VDIF = dst /= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/FltARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/FltARM.java new file mode 100644 index 00000000..42553868 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/FltARM.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +//FMOV (scalar, immediate) +//Floating-point move immediate. +public class FltARM extends ConstantNode implements MachNode, RIPRelSize { + final String _ext; + FltARM( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { return "ld8"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return arm.DMASK; } + @Override public boolean isClone() { return true; } + @Override public FltARM copy() { return new FltARM(this,_ext); } + + @Override public void encoding( Encoding enc ) { + if( _ext!=null ) throw Utils.TODO(); + enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/); + short dst = (short)(enc.reg(this) - arm.D_OFFSET); + enc.add4(arm.load_pc(arm.OPF_ARM, 0, dst)); + } + + // Delta is from opcode start. + @Override public byte encSize(int delta) { return 4; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = (short)(enc.reg(this) - arm.D_OFFSET); + enc.patch4(opStart, arm.load_pc(arm.OPF_ARM, delta, dst)); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java new file mode 100644 index 00000000..44a417f9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class FunARM extends FunNode implements MachNode { + FunARM(FunNode fun) { super(fun); } + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); + } + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz >= 1L<<12 ) throw Utils.TODO(); + enc.add4(arm.imm_inst(arm.OPI_ADD, -sz&0xFFF, arm.RSP, arm.RSP)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java new file mode 100644 index 00000000..f1fa162f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class I2F8ARM extends MachConcreteNode implements MachNode { + I2F8ARM(Node i2f8) { super(i2f8); } + @Override public String op() { return "cvt"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } + @Override public void encoding( Encoding enc ) { + // SCVTF + short self = (short)(enc.reg(this )-arm.D_OFFSET); + short reg1 = enc.reg(in(1)); + int body = arm.float_cast(arm.OP_FLOAT_C, 1, reg1, self); + enc.add4(body); + } + + // General form: "i2f8 (flt)int_value" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java new file mode 100644 index 00000000..dbed9487 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java @@ -0,0 +1,70 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Integer constants +public class IntARM extends ConstantNode implements MachNode { + final String _ext; + IntARM( ConstantNode con, String ext ) { super(con); _ext = ext; } + + @Override public String op() { return "ldi"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return arm.WMASK; } + + @Override public boolean isClone() { return true; } + @Override public IntARM copy() { return new IntARM(this,_ext); } + + @Override public void encoding( Encoding enc ) { + if( _ext!=null ) throw Utils.TODO(); + short self = enc.reg(this); + long x = _con==Type.NIL ? 0 : ((TypeInteger)_con).value(); + int nb0 = 0; + int nb1 = 0; + // Count number of 0000 and FFFF blocks + for (int i=0; i<64; i+=16) { + int block = (int)(x >> i) & 0xFFFF; + if (block == 0) nb0++; + if (block == 0xFFFF) nb1++; + } + int pattern; + int op; + if(nb0 >= nb1) { + // More 0 blocks then F blocks, use movz + pattern = 0; + op = arm.OP_MOVZ; + } else { + // More F blocks then 0 blocks, use movn + pattern = 0xFFFF; + op = arm.OP_MOVN; + } + int invert = pattern; + for (int i=0; i<4; i++) { + int block = (int)x & 0xFFFF; + x >>= 16; + if (block != pattern) { + enc.add4(arm.mov(op, i, block ^ invert, self)); + op = arm.OP_MOVK; + invert = 0; + } + } + if (op != arm.OP_MOVK) { + // All blocks are the same, special case + enc.add4(arm.mov(op, 0, 0, self)); + } + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + _con.print(sb.p(reg).p(" = #")); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java new file mode 100644 index 00000000..f8c9bfef --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.LoadNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.util.SB; + +// Load memory addressing on ARM +// Support imm, reg(direct), or reg+off(indirect) addressing +// Base = base - base pointer, offset is added to base +// idx = null +// off = off - offset added to base + +public class LoadARM extends MemOpARM { + LoadARM(LoadNode ld, Node base, Node idx, int off) { + super(ld, base, idx, off, 0); + } + @Override public String op() { return "ld"+_sz; } + @Override public RegMask outregmap() { return arm.MEM_MASK; } + + private static final int[] OP_LOADS = new int[]{ arm.OP_LOAD_IMM_8, arm.OP_LOAD_IMM_16, arm.OP_LOAD_IMM_32, arm.OP_LOAD_IMM_64, }; + private int imm_op() { + return _con == TypeFloat.F32 ? arm.OPF_LOAD_IMM_32 + : _con == TypeFloat.F64 ? arm.OPF_LOAD_IMM_64 + : OP_LOADS[_con.log_size()]; + } + + private static final int[] OP_LOAD_RS = new int[]{ arm.OP_LOAD_R_8 , arm.OP_LOAD_R_16 , arm.OP_LOAD_R_32 , arm.OP_LOAD_R_64, }; + + // ldr(immediate - unsigned offset) | ldr(register) + @Override public void encoding( Encoding enc ) { + ldst_encode(enc, imm_op(), OP_LOAD_RS[_con.log_size()], this, size()); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(","); + asm_address(code,sb); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java new file mode 100644 index 00000000..80ce493f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Logical Shift Left (register) +public class LslARM extends MachConcreteNode implements MachNode { + LslARM(Node asr) {super(asr);} + @Override public String op() { return "shr"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSL); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java new file mode 100644 index 00000000..7fe16cbe --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.SB; + +public class LslIARM extends MachConcreteNode implements MachNode { + final int _imm; + LslIARM(Node lsl, int imm) { + super(lsl); + _inputs.pop(); + _imm = imm; + } + @Override public String op() { return "lsli"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + short rd = enc.reg(this); + short rn = enc.reg(in(1)); + assert _imm > 0; + // UBFM <Xd>, <Xn>, #(-<shift> MOD 64), #(63-<shift>) + // immr must be (-<shift> MOD 64) = 64 - shift + enc.add4(arm.imm_shift(arm.OPI_LSL, 64 - _imm, (64 - _imm) - 1, rn, rd)); + } + // General form: "lsli rd = rs1 << imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #").p(_imm); + } +}
\ No newline at end of file diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java new file mode 100644 index 00000000..fc005e04 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Logical Shift Right (register) +public class LsrARM extends MachConcreteNode implements MachNode { + LsrARM(Node asr) {super(asr);} + @Override public String op() { return "shr"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSR); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java new file mode 100644 index 00000000..78844b0a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// logical right shift immediate +public class LsrIARM extends MachConcreteNode implements MachNode { + final int _imm; + LsrIARM(Node lsr, int imm) { + super(lsr); + _inputs.pop(); + _imm = imm; + } + @Override public String op() { return "lsri"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + short rd = enc.reg(this); + short rn = enc.reg(in(1)); + assert _imm > 0; + enc.add4(arm.imm_shift(arm.OPI_LSR,_imm, 0b111111, rn,rd)); + } + // General form: "lsri rd = rs1 >>> imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #").p(_imm); + } +}
\ No newline at end of file diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java new file mode 100644 index 00000000..08bdf974 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java @@ -0,0 +1,71 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.lang.StringBuilder; +import java.util.BitSet; + +public abstract class MemOpARM extends MemOpNode implements MachNode { + final int _off; // Limit 9 bits sized, or (13 bits<<logsize) unsigned + final int _imm; // Limit ? bits + final int _bytes; + final char _sz; + MemOpARM(MemOpNode mop, Node ptr, Node idx, int off, int imm) { + super(mop,mop); + assert ptr._type instanceof TypeMemPtr && !(ptr instanceof AddNode); + assert ptr() == ptr; + _inputs.setX(2, ptr); + _inputs.setX(3, idx); + _off = off; + _imm = imm; + _bytes = mop instanceof StoreNode st ? st.storeSize() : 1<<_con.log_size(); + _sz = (char)('0'+_bytes); + } + + // Store-based flavors have a value edge + MemOpARM( MemOpNode mop, Node ptr, Node idx, int off, int imm, Node val ) { + this(mop,ptr,idx,off,imm); + _inputs.setX(4,val); + } + + @Override public String label() { return op();} + Node val() { return in(4); } // Only for stores + + @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } + + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + + int size() { return _bytes; } + + // Wider mask to store both GPRs and FPRs + @Override public RegMask regmap(int i) { + // 0 - ctrl + if( i==1 ) return null; // memory + if( i==2 ) return arm.RMASK; // ptr/base + if( i==3 ) return arm.RMASK; // off/index + if( i==4 ) return arm.RMASK; // value + return null; // Anti-dependence + } + + // Shared encoding for loads and stores(int and float) + public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, int size) { + short ptr = enc.reg(ptr()); + short off = enc.reg(off()); + short val = enc.reg(xval); + int body = off() == null + ? arm.load_str_imm(opcode_imm, _off, ptr, val, size) + : arm.indr_adr(opcode_reg, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val); + enc.add4(body); + } + + SB asm_address(CodeGen code, SB sb) { + sb.p("[").p(code.reg(ptr())).p("+"); + if( off() != null ) sb.p(code.reg(off())); + else sb.p(_off); + return sb.p("]"); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java new file mode 100644 index 00000000..a23ec75b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// mulh signed multiply instruction(no-imm form) +public class MulARM extends MachConcreteNode implements MachNode { + MulARM(Node mul) {super(mul);} + @Override public String op() { return "mul"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_MUL,31); } + // General form: "rd = rs1 * rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java new file mode 100644 index 00000000..73ebbf00 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java @@ -0,0 +1,18 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class MulFARM extends MachConcreteNode implements MachNode{ + MulFARM(Node mulf) {super(mulf);} + @Override public String op() { return "mulf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_MUL); } + // Default on double precision for now(64 bits) + // General form: "VMUL.f32 rd = src1 * src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java new file mode 100644 index 00000000..881b41f9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; + +public class NegARM extends MachConcreteNode implements MachNode { + NegARM(Node sub) { + super(sub); + //_inputs.pop(); + } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + // reverse subtract with immediate 0 + arm.imm_inst(enc,this, in(1), 0b00100110, 0); + } + // General form: "subi rd = rs1 - imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = -").p(code.reg(in(1))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java new file mode 100644 index 00000000..df2a6bc9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class NewARM extends NewNode implements MachNode, RIPRelSize { + // A pre-zeroed chunk of memory. + NewARM(NewNode nnn) { super(nnn); } + @Override public void encoding( Encoding enc ) { + // BL(branch with link) + enc.external(this,"calloc"). + add4(arm.mov(arm.OP_MOVZ, 0, 1, 0)). // movz x0,#1 + add4(arm.b(arm.OP_CALL, 0)); + } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public byte encSize(int delta) { return 4*2; } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public void patch(Encoding enc, int opStart, int opLen, int delta ) { + // Negative patches are JIT emulator targets. + if( opStart+delta < 0 ) { + enc.patch4(opStart+4, arm.b_calloc(arm.OP_CALL, delta-4)); + } else { + throw Utils.TODO(); + } + } + // General form: "alloc #bytes PC" + @Override public void asm(CodeGen code, SB sb) { + sb.p("\n"); // Multi-op trigger + sb.p("ldi ").p(code._mach.regs()[_arg2Reg]).p(" = #1\n"); + sb.p("call #calloc"); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java new file mode 100644 index 00000000..d78421ca --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java @@ -0,0 +1,27 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.util.SB; + +public class NotARM extends MachConcreteNode implements MachNode{ + NotARM(NotNode not) {super(not);} + @Override public String op() { return "not"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public RegMask killmap() { return arm.FLAGS_MASK; } + @Override public void encoding( Encoding enc ) { + // subs xzr, rs, #0 + // cset rd, eq // Set rd to 1 if rs == 0 (equal), else 0 + // subtracting zero from rs will just yield rs, it sets the zero flag and then it's used in cset + short self = enc.reg(this ); + short reg1 = enc.reg(in(1)); + int subs = arm.imm_inst(arm.OP_SUBS, 0, reg1, self); + enc.add4(subs); + int cset = arm.cond_set(arm.OP_CSET, 31, arm.COND.EQ, 31, reg1); + enc.add4(cset); + } + + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java new file mode 100644 index 00000000..e8245749 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java @@ -0,0 +1,20 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Not using the ORRS variant. +public class OrARM extends MachConcreteNode implements MachNode { + OrARM(Node and) { super(and); } + @Override public String op() { return "or"; } + @Override public String glabel() { return "|"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_OR); } + // General form: #rd = rs1 & rs2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java new file mode 100644 index 00000000..a48caa51 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java @@ -0,0 +1,24 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class OrIARM extends MachConcreteNode implements MachNode { + final int _imm; + OrIARM(Node or, int imm) { + super(or); + _inputs.pop(); + _imm = imm; + } + @Override public String op() { return "ori"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + arm.imm_inst_n(enc, this, in(1), arm.OPI_OR, _imm); + } + // General form: "ori rd = rs1 | imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java new file mode 100644 index 00000000..b419fe0a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ParmNode; + +public class ParmARM extends ParmNode implements MachNode { + final RegMask _rmask; + ParmARM(ParmNode parm) { + super(parm); + _rmask = arm.callInMask(fun().sig(),_idx,0); + } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return _rmask; } + @Override public void encoding( Encoding enc ) { } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java new file mode 100644 index 00000000..4b7d6f13 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java @@ -0,0 +1,15 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ProjNode; + +public class ProjARM extends ProjNode implements MachNode { + ProjARM( ProjNode p ) { super(p); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { + return ((MachNode)in(0)).outregmap(_idx); + } + @Override public void encoding( Encoding enc ) { } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java new file mode 100644 index 00000000..2136ebf5 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java @@ -0,0 +1,15 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; + +public class RetARM extends ReturnNode implements MachNode { + RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } + @Override public void encoding( Encoding enc ) { + int frameAdjust = ((FunARM)fun())._frameAdjust; + if( frameAdjust > 0 ) + enc.add4(arm.imm_inst(arm.OPI_ADD, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP)); + enc.add4(arm.ret(arm.OP_RET)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java new file mode 100644 index 00000000..c597b9dd --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java @@ -0,0 +1,35 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// conditional set? +// e.g CSET, stores flag info into GPRS after compare +public class SetARM extends MachConcreteNode implements MachNode { + final String _bop; // One of <,<=,== + final boolean _fp; // FP compare (unsigned encoding) + SetARM(Node cmp, String bop, boolean fp) { + super(cmp); + _inputs.setLen(1); // Pop the cmp inputs + // Replace with the matched cmp + _inputs.push(cmp); + _bop = bop; + _fp = fp; + } + @Override public String op() { return "set"+_bop; } + @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + @Override public void encoding( Encoding enc ) { + // Using RM==RN==31 implies a *zero* value, not R31. + int body = arm.cset(arm.OP_CSET,0b11111, arm.make_condition(_bop, _fp), 0b011111, enc.reg(this)); + enc.add4(body); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)); + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(" = ").p(src); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java new file mode 100644 index 00000000..be975fb6 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java @@ -0,0 +1,59 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.SplitNode; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class SplitARM extends SplitNode { + SplitARM(LRG lrg, String kind, byte round) { super(lrg,kind,round,new Node[2]);} + @Override public RegMask regmap(int i) { return arm.SPLIT_MASK; } + @Override public RegMask outregmap() { return arm.SPLIT_MASK; } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + + boolean dstX = dst >= arm.D_OFFSET; + boolean srcX = src >= arm.D_OFFSET; + + if (dst >= arm.MAX_REG) { + // store to sp + if(src >= arm.MAX_REG) { + throw Utils.TODO(); + } + int off = fun(enc).computeStackOffset(enc._code,dst); + if( srcX ) src -= arm.D_OFFSET; + enc.add4(arm.load_str_imm(arm.OP_STORE_IMM_64, off, arm.RSP, src, 8)); + return; + } + + if(src >= arm.MAX_REG) { + // Load from SP + int off = fun(enc).computeStackOffset(enc._code,src); + if( dstX ) dst -= arm.D_OFFSET; + enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM_64, off, arm.RSP, dst, 8)); + return; + } + + // pick opcode based on regs + if(!dstX && !srcX) { + // GPR->GPR + enc.add4(arm.mov_reg(arm.OP_MOV, src, dst)); + } else if(dstX && srcX) { + // FPR->FPR + // fmov reg + enc.add4(arm.f_mov_reg(arm.OP_FMOV_REG, src - arm.D_OFFSET,dst - arm.D_OFFSET)); + } else if(!srcX && dstX) { + // GPR->FPR + // FMOV(general) 64 bits to DOUBLE-PRECISION + enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b111, src, dst - arm.D_OFFSET)); + } else if( srcX && !dstX) { + //FPF->GPR + // FMOV(general) DOUBLE-PRECISION to 64 bits + enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst)); + } + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java new file mode 100644 index 00000000..b11a2c89 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.StoreNode; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.util.SB; + +// Store memory addressing on ARM +// Support imm, reg(direct), or reg+off(indirect) addressing +// base - base pointer, offset is added to base +// null - index never allowed (no [reg+reg] mode) +// off - offset added to base +// imm - immediate value to store, only if val is null +// val - value to store or null + +//e.g s.cs[0] = 67; // C +// base = s.cs, off = 4, imm = 67, val = null +public class StoreARM extends MemOpARM { + StoreARM(StoreNode st, Node base, Node idx, int off, Node val) { + super(st, base, idx, off, 0, val); + } + @Override public String op() { return "st"+_sz; } + @Override public RegMask outregmap() { return null; } + + private static final int[] OP_STORES = new int[]{ arm.OP_STORE_IMM_8, arm.OP_STORE_IMM_16, arm.OP_STORE_IMM_32, arm.OP_STORE_IMM_64, }; + private int imm_op() { + return val()!=null && val()._type instanceof TypeFloat + ? (_bytes==4 ? arm.OPF_STORE_IMM_32 : arm.OPF_STORE_IMM_64) + : OP_STORES[Integer.numberOfTrailingZeros(_bytes)]; + } + + private static final int[] OP_STORE_RS = new int[]{ arm.OP_STORE_R_8, arm.OP_STORE_R_16, arm.OP_STORE_R_32, arm.OP_STORE_R_64, }; + + @Override public void encoding( Encoding enc ) { + ldst_encode(enc, imm_op(), OP_STORE_RS[Integer.numberOfTrailingZeros(_bytes)], val(), size()); + } + @Override public void asm(CodeGen code, SB sb) { + asm_address(code,sb).p(","); + if( val()==null ) sb.p("#").p(_imm); + else sb.p(code.reg(val())); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java new file mode 100644 index 00000000..e3b95e50 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class SubARM extends MachConcreteNode implements MachNode { + SubARM(Node sub) { super(sub); } + @Override public String op() { return "sub"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_SUB); } + + // General form: "sub # rd = rs1 - rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java new file mode 100644 index 00000000..a252e542 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java @@ -0,0 +1,18 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class SubFARM extends MachConcreteNode implements MachNode { + SubFARM(Node subf) {super(subf);} + @Override public String op() { return "subf"; } + @Override public RegMask regmap(int i) { return arm.DMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_SUB); } + // Default on double precision for now(64 bits) + // General form: "vsub.f32 rd = src1 + src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java new file mode 100644 index 00000000..b50107c9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; + +public class SubIARM extends MachConcreteNode implements MachNode { + final int _imm; + SubIARM(Node sub, int imm) { + super(sub); + _inputs.pop(); + _imm = imm; + } + @Override public String op() { return _imm == -1 ? "dec" : "subi"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + @Override public void encoding( Encoding enc ) { + arm.imm_inst(enc,this, in(1), arm.OPI_SUB,_imm); + } + + // General form: "subi rd = rs1 - imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java new file mode 100644 index 00000000..9d490c8b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class TFPARM extends FunPtrNode implements MachNode, RIPRelSize { + final String _ext; + // Pointer to a Simple function + TFPARM( FunPtrNode fptr ) { super(fptr); _ext = null; } + // Pointer to an Extern "C" function + TFPARM( TypeFunPtr fptr, String ext ) { super(fptr,CodeGen.CODE._start,null); _type = fptr; _ext = ext; } + @Override public String op() { return "ldx"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public boolean isClone() { return true; } + @Override public TFPARM copy() { return new TFPARM((TypeFunPtr)_con,_ext); } + @Override public void encoding( Encoding enc ) { + if( _ext==null ) enc.relo(this); // Internal patch + else enc.external(this,_ext); // ELF-file external patch + short dst = enc.reg(this); + // adrp x0, 0 + enc.add4(arm.adrp(1,0, arm.OP_ADRP, 0,dst)); + // add x0, x0, 0 + arm.imm_inst(enc,arm.OPI_ADD,0, dst); + } + + @Override public byte encSize(int delta) { + return 8; + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short rpc = enc.reg(this); + if(opLen == 8 ) { + arm.patch_adrp_add(enc, opStart, delta, rpc); + } else { + // should not happen as one instruction is 4 byte, and TFP arm encodes 2. + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + _type.print(sb.p(reg).p(" #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java new file mode 100644 index 00000000..f86d85dc --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeMemPtr; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class TMPARM extends ConstantNode implements MachNode, RIPRelSize { + final String _ext; + TMPARM( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { return "ldp"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public boolean isClone() { return true; } + @Override public TMPARM copy() { return new TMPARM(this,_ext); } + @Override public void encoding( Encoding enc ) { + if( _ext!=null ) throw Utils.TODO(); + enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1); + short dst = enc.reg(this); + // adrp x0, 0 + enc.add4(arm.adrp(1,0, arm.OP_ADRP, 0,dst)); + // add x0, x0, 0 + arm.imm_inst(enc,arm.OPI_ADD,0, dst); + } + + @Override public byte encSize(int delta) { + return 8; + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = enc.reg(this); + if(opLen == 8 ) { + arm.patch_adrp_add(enc, opStart, delta, dst); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + _con.print(sb.p(reg).p(" #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java new file mode 100644 index 00000000..2dd5fd6b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java @@ -0,0 +1,49 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +// unconditional jump +public class UJmpARM extends CFGNode implements MachNode, RIPRelSize { + UJmpARM() { } + @Override public String op() { return "jmp"; } + @Override public String label() { return op(); } + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { + return sb.append("jmp "); + } + @Override public RegMask regmap(int i) {return null; } + @Override public RegMask outregmap() { return null; } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + @Override public void encoding( Encoding enc ) { + enc.jump(this,uctrl()); + int body = arm.b(arm.OP_UJMP, 0); + enc.add4(body); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( -(1<<26) <= delta && delta < (1<<26) ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + if( opLen==4 ) { + enc.patch4(opStart,arm.b(arm.OP_UJMP, delta)); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + CFGNode target = uctrl(); + //assert target.nOuts() > 1; // Should optimize jmp to empty targets + sb.p(label(target)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java new file mode 100644 index 00000000..52bda021 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class XorARM extends MachConcreteNode implements MachNode{ + XorARM(Node xor) {super(xor);} + @Override public String op() { return "xor"; } + @Override public String glabel() { return "^"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_XOR); } + // General form: "rd = x1 ^ x2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java new file mode 100644 index 00000000..23b29644 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; + +public class XorIARM extends MachConcreteNode implements MachNode { + final int _imm; + XorIARM(Node xor, int imm) { + super(xor); + _inputs.pop(); + _imm = imm; + } + + @Override public String op() { return "xori"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + + // General form: "xori rd = rs1 ^ imm" + @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), arm.OPI_XOR,_imm); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java new file mode 100644 index 00000000..852fbdb1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -0,0 +1,875 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; + +public class arm extends Machine { + public arm( CodeGen code ) { + if( !"SystemV".equals(code._callingConv) ) + throw new IllegalArgumentException("Unknown calling convention "+code._callingConv); + } + + // ARM64 + @Override public String name() { return "arm"; } + + // GPR(S) + public static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7; + public static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15; + public static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23; + public static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31; + + // Floating point registers + public static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39; + public static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47; + public static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55; + public static final int D24 = 56, D25 = 57, D26 = 58, D27 = 59, D28 = 60, D29 = 61, D30 = 62, D31 = 63; + + static final int FLAGS = 64; + static final int MAX_REG = 65; + public static final int D_OFFSET = 32; + + static final String[] REGS = new String[] { + "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", + "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", + "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", + "X24", "X25", "X26", "X27", "X28", "X29", "RPC", "RSP", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", + "D16", "D17", "D18", "D19", "D20", "D21", "D22", "D23", + "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31", + "flags" + }; + @Override public String[] regs() { return REGS; } + + // from (x0-x30) + // General purpose register mask: pointers and ints, not floats + static final long RD_BITS = 0xFFFFFFFFL; + static final RegMask RMASK = new RegMask(RD_BITS); + static final long WR_BITS = 0x7FFFFFFFL; // All the GPRs, not RSP + static final RegMask WMASK = new RegMask(WR_BITS); + + // Float mask from(d0–d31) + static final long FP_BITS = 0xFFFFFFFFL<<D0; + static final RegMask DMASK = new RegMask(FP_BITS); + + // Load/store mask; both GPR and FPR + static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS); + + static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/); + + static final RegMask FLAGS_MASK = new RegMask(FLAGS); + + // Arguments masks + static final RegMask X0_MASK = new RegMask(X0); + static final RegMask X1_MASK = new RegMask(X1); + static final RegMask X2_MASK = new RegMask(X2); + static final RegMask X3_MASK = new RegMask(X3); + static final RegMask X4_MASK = new RegMask(X4); + static final RegMask X5_MASK = new RegMask(X5); + static final RegMask X6_MASK = new RegMask(X6); + static final RegMask X7_MASK = new RegMask(X7); + + // Arguments(float) masks + static final RegMask D0_MASK = new RegMask(D0); + static final RegMask D1_MASK = new RegMask(D1); + static final RegMask D2_MASK = new RegMask(D2); + static final RegMask D3_MASK = new RegMask(D3); + static final RegMask D4_MASK = new RegMask(D4); + static final RegMask D5_MASK = new RegMask(D5); + static final RegMask D6_MASK = new RegMask(D6); + static final RegMask D7_MASK = new RegMask(D7); + + // major opcode: OP + public static int OP_ADD = 0b10_001_011; + public static int OPF_ADD = 0b00_011_110; + public static int OPF_OP_ADD = 0b00_101_0; + public static int OPI_ADD = 0b10_010_00100; + + public static int OPI_SUB = 0b11_010_00100; + public static int OP_UJMP = 0b000101; + + public static int OP_ADRP = 0b10000; + + public static int OP_ASR = 0b1010; + public static int OPI_ASR = 0b10_0100_1101; + + public static int OP_LSL = 0b1000; + public static int OPI_LSL =0b1101001101; + + public static int OP_LSR = 0b1001; + public static int OPI_LSR = 0b1101001101; + + public static int OP_BRANCH = 0b01_0101_00; + public static int OP_CALL = 0b10_010_1; + + public static int OP_CALLRARM = 0b1101011000111111000000; + + public static int OP_SUBS = 0b1111000100; + public static int OP_CSET = 0b10011010100; + public static int OP_CMP = 0b11101011; + public static int OPI_CMP = 0b1111000100; + + public static int OP_DIV = 0b10011010110; + public static int OPF_DIV = 0b000110; + + public static int OP_MUL = 0b10011011000; + public static int OPF_MUL = 0b10; + + public static int OPF_ARM = 0b01011100; + + public static int OP_FLOAT_C = 0b10011110; + + public static int OP_XOR = 0b11001010; + public static int OPI_XOR = 0b110100100; + + public static int OP_AND = 0b10_0010_10; + public static int OPI_AND = 0b10_0100_100; + + public static int OP_OR = 0b10101010; + public static int OPI_OR = 0b101100100; + + public static int OP_SUB = 0b11001011; + public static int OPF_SUB = 0b1110; + + public static int OP_MOVK = 0b111100101; + public static int OP_MOVN = 0b100100101; + public static int OP_MOVZ = 0b110100101; + + public static int OP_RET = 0b1101011001011111000000; + // Store/load opcodes + public static int OP_LOAD_R_64 = 0b11111000011; + public static int OP_LOAD_R_32 = 0b10111000011; + public static int OP_LOAD_R_16 = 0b01111000010; + public static int OP_LOAD_R_8 = 0b00111000011; + + public static int OP_LOAD_IMM_64 = 0b1111100101; + public static int OP_LOAD_IMM_32 = 0b1011100101; + public static int OP_LOAD_IMM_16 = 0b0111100101; + public static int OP_LOAD_IMM_8 = 0b0011100101; + + // load + public static int OPF_LOAD_R_64 = 0b11111100011; + public static int OPF_LOAD_R_32 = 0b10111100011; + + public static int OPF_LOAD_IMM_64 = 0b1111110101; + public static int OPF_LOAD_IMM_32 = 0b1011110101; + + public static int OP_STORE_R_64 = 0b11111000001; + public static int OP_STORE_R_32 = 0b10111000001; + public static int OP_STORE_R_16 = 0b01111000001; + public static int OP_STORE_R_8 = 0b00111000001; + + public static int OP_STORE_IMM_64 = 0b1111100100; + public static int OP_STORE_IMM_32 = 0b1011100100; + public static int OP_STORE_IMM_16 = 0b0111100100; + public static int OP_STORE_IMM_8 = 0b0011100100; + + public static int OPF_STORE_R_64 = 0b11111100001; + public static int OPF_STORE_R_32 = 0b10111100001; + + public static int OPF_STORE_IMM_32 = 0b1111110100; + public static int OPF_STORE_IMM_64 = 0b1111110100; + + + public static int OP_FMOV = 0b10011110; + public static int OP_FMOV_REG = 0b00011110; + // https://docsmirror.github.io/A64/2023-06/mov_orr_log_shift.html + public static int OP_MOV = 0b10101010000; + // Calling convention; returns a machine-specific register + // for incoming argument idx. + // index 0 for control, 1 for memory, real args start at index 2 + static final RegMask[] CALLINMASK = new RegMask[] { + X0_MASK, + X1_MASK, + X2_MASK, + X3_MASK, + X4_MASK, + X5_MASK, + X6_MASK, + X7_MASK, + }; + static final RegMask[] XMMS = new RegMask[] { + D0_MASK, + D1_MASK, + D2_MASK, + D3_MASK, + D4_MASK, + D5_MASK, + D6_MASK, + D7_MASK, + }; + + // ARM ENCODING + public enum OPTION { + UXTB, + UXTH, + UXTX, + SXTB, + SXTH, + SXTW, + SXTX, + } + + static public int cset(int opcode, int rm, COND cond, int rn, int rd) { + assert 0 <= rm && rm < 32; + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + // Is one of the standard conditions, excluding AL and NV, encoded in the "cond" field with its least significant bit inverted. + int actualCond = cond.ordinal() ^ 1; + return (opcode << 21) | (rm << 16) | (actualCond << 12) | (0b01 << 10) | (rn << 5) | rd; + } + + static public int cset(int opcode, COND cond, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 16) | (cond.ordinal() << 12) | (rn << 5) | rd; + } + + public enum STORE_LOAD_OPTION { + UXTW, // base+ u32 index [<< logsize] + UXTX, // base+ u64 index [<< logsize] + SXTW, // base+ s32 index [<< logsize] + SXTX, // base+ s64 index [<< logsize] + } + + public enum COND { + EQ, + NE, + CS, + CC, + MI, + PL, + VS, + VC, + HI, + LS, + GE, + LT, + GT, + LE, + AL, + NV + } + + // True if signed 9-bit immediate + private static boolean imm9(TypeInteger ti) { + // 55 = 64-9 + return ti.isConstant() && ((ti.value()<<55)>>55) == ti.value(); + } + // True if signed 12-bit immediate + private static boolean imm12(TypeInteger ti) { + // 52 = 64-12 + return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value(); + } + + + // Can we encode this in ARM's 12-bit LOGICAL immediate form? + // Some combination of shifted bit-masks. + private static int imm12Logical(TypeInteger ti) { + if( !ti.isConstant() ) return -1; + if( !ti.isConstant() ) return -1; + long val = ti.value(); + if (val == 0 || val == -1) return -1; // Special cases are not allowed + int immr = 0; + // Rotate until we have 0[...]1 + + // Rotate until: + // The number is negative (MSB is 1) + // Or the LSB is not 1 + while (val < 0 || (val & 1)==0) { + // circular rotation + val = (val >>> 63) | (val << 1); + immr++; + } + // Start by assuming that val might be made of two 32-bit chunks that repeat. + int size = 32; + long pattern = val; + // Is upper half of pattern the same as the lower? + while ((pattern & ((1L<<size)-1)) == (pattern >> size)) { + // Then only take one half + pattern >>= size; + size >>= 1; + } + size <<= 1; + int imms = Long.bitCount(pattern); + // Pattern should now be zeros followed by ones 0000011111 + if (pattern != (1L<<imms)-1) return -1; + imms--; + if (size == 64) return 0x1000 | immr << 6 | imms; + return (32-size)<<1 | immr << 6 | imms; + } + + // sh is encoded in opcode + public static int imm_inst(int opcode, int imm12, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert opcode >=0 && imm12 >= 0; // Caller zeros high order bits + return (opcode << 22) | (imm12 << 10) | (rn << 5) | rd; + } + + public static int imm_shift(int opcode, int imm, int imms, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 22) | (1 << 22) | (imm << 16) | (imms << 10) | (rn << 5) | rd; + } + + public static void imm_inst(Encoding enc, Node n,Node n2, int opcode, int imm12) { + short self = enc.reg(n); + short reg1 = enc.reg(n2); + + int body = imm_inst(opcode, imm12&0xFFF, reg1, self); + enc.add4(body); + } + + public static void imm_inst_subs(Encoding enc, Node n,Node n2, int opcode, int imm12) { + short reg1 = enc.reg(n2); + // 31 = 11111 + int body = imm_inst(opcode, imm12&0xFFF, reg1, 31); + enc.add4(body); + } + + // for cases where rs1 and dst are the same, eg add x0, x0, 1 + public static void imm_inst(Encoding enc, int opcode, int imm12, int self) { + int body = imm_inst(opcode, imm12&0xFFF, self, self); + enc.add4(body); + } + + public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) { + short self = enc.reg(n); + short reg1 = enc.reg(n2); + int body = imm_inst_n(opcode, imm13, reg1, self); + enc.add4(body); + } + + // nth bit comes from immediate and not opcode + public static int imm_inst_n(int opcode, int imm13, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert 0 <= imm13 && imm13 <= 0x1FFF; + return (opcode << 23) | (imm13 << 10) | (rn << 5) | rd; + } + + public static int imm_inst_l(int opcode, int imm12, int self) { + int body = imm_inst(opcode, imm12&0xFFF, self, self); + return body; + } + + // for cases where rs1 and dst are the same, eg add x0, x0, 1 + public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + int body = imm_inst(opcode, imm12&0xFFF, reg1, self); + return body; + } + + // for normal add, reg1, reg2 cases (reg-to-reg) + // using shifted-reg form + public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd) { + assert 0 <= rm && rm < 32; + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) | (rn << 5) | rd; + } + public static void r_reg(Encoding enc, Node n, int opcode) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + int body = r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self); + enc.add4(body); + } + + public static void r_reg_subs(Encoding enc, Node n, int opcode) { + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + // 31 = 11111 + int body = r_reg(opcode, 0, reg2, 0, reg1, 31); + enc.add4(body); + } + + public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rm && rm < 32; + assert 0 <= rd && rd < 32; + return (opcode << 21) | (rm << 16) | (op2 << 10) | (rn << 5) | rd; + } + public static void shift_reg(Encoding enc, Node n, int op2) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + int body = shift_reg(0b10011010110, reg2, op2, reg1, self); + enc.add4(body); + } + + // MUL can be considered an alias for MADD with the third operand Ra being set to 0 + public static int madd(int opcode, int rm, int ra, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert 0 <= rm && rm < 32; + return (opcode << 21) | (rm << 16) | (ra << 10) | (rn << 5) | rd; + } + public static void madd(Encoding enc, Node n, int opcode, int ra) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + int body = madd(opcode, reg2, ra, reg1, self); + enc.add4(body); + } + + // encodes movk, movn, and movz + public static int mov(int opcode, int shift, int imm16, int rd) { + assert 0 <= rd && rd < 32; + return (opcode << 23) | (shift << 21) | (imm16 << 5) | rd; + } + + public static int mov_reg(int opcode, int src, int rd) { + assert 0 <= rd && rd < 32; + return (opcode << 21) | (src << 16) | 0b11111 << 5 | rd; + } + + public static int ret(int opcode) { + return (opcode << 10); + } + + // FMOV (scalar, immediate) + public static int f_scalar(int opcode, int ftype, int rm, int op, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + assert 0 <= rm && rm < 32; + return (opcode << 24) | (ftype << 22) | (1 << 21) | (rm << 16) | (op << 10) | (rn << 5) | rd; + } + + public static int f_mov_reg(int opcode, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 24) | (0b01100000010000 << 10) | (rn << 5) | rd; + } + public static int f_mov_general(int opcode, int ftype, int rmode, int opcode1, int rn, int rd) { + assert 0 <= rn && rn < 32; + assert 0 <= rd && rd < 32; + return (opcode << 24) |(ftype << 22) | (1 << 21) | (rmode << 19) | (opcode1 << 16) | (rn << 5) | rd; + } + public static void f_scalar(Encoding enc, Node n, int op ) { + short self = (short)(enc.reg(n) -D_OFFSET); + short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); + short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET); + int body = f_scalar(0b00011110, 1, reg2, op, reg1, self); + enc.add4(body); + } + + // share same encoding with LDR (literal, SIMD&FP) flavour + public static int load_pc(int opcode, int offset, int rt) { + offset>>=2; + return (opcode << 24) | (offset << 5) | rt; + } + // int l + public static int adrp(int op, int imlo,int opcode, int imhi, int rd) { + assert 0 <= rd && rd < 32; + assert 0 <= imlo && imlo <= 0x3; // 2 bits + assert 0 <= imhi && imhi <= 0x7FFFF; // 19 bits + return (op << 31) | (imlo << 29) |(opcode << 24) | (imhi << 5) | rd; + } + + public static void patch_adrp_add(Encoding enc, int opStart, int delta, int rd) { + int target = opStart + delta; + int pageDelta = ((target & ~0xFFF) - (opStart & ~0xFFF)) >> 12; + int imm21 = pageDelta & 0x1FFFFF; + enc.patch4(opStart , adrp(1, imm21 & 3, OP_ADRP, imm21 >>> 2, rd)); + enc.patch4(opStart+4, imm_inst_l(OPI_ADD, target & 0xFFF, rd)); + } + + // [Rptr+Roff] + public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s, int ptr, int rt) { + assert 0 <= ptr && ptr < 32; + assert 0 <= rt && rt < 32; + return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt; + } + // [Rptr+imm9] + public static int load_str_imm(int opcode, int imm12, int ptr, int rt, int size) { + assert 0 <= ptr && ptr < 32; + assert 0 <= rt && rt < 32; + + if(size == 8) imm12 = imm12 >> 3; + if(size == 4) imm12 = imm12 >> 2; + if(size == 2) imm12 = imm12 >> 1; + // size == 1 imm12 = imm12 + return (opcode << 22) | ((imm12) << 10) | (ptr << 5) | rt; + } + + // encoding for vcvt, size is encoded in operand + // <Qd>, <Qm> + // F32.S32 + //encoded as op = 0b00, size = 0b10. + // VCVT<c>.<Td>.<Tm> <Dd>, <Dm> + // opcode is broken down into 4 pieces + public static int f_convert(int opcode_1, int opcode_2, int opcode_3, int opcode_4, int vd, int vm) { + return (opcode_1 << 28) | (opcode_2 << 24) | (opcode_3 << 20) | (opcode_4 << 16) | + (vd << 12) | (0x01100010 << 4) | vm; + } + public static int float_cast(int opcode, int ftype, int rn, int rd) { + assert 0 <= rd && rd < 32; + assert 0 <= rn && rn < 32; + return (opcode << 24) | (ftype << 22) | (2176 << 10) | (rn << 5) | rd; + } + + + // ftype = 3 + public static int f_cmp(int opcode, int ftype, int rm, int rn) { + assert 0 <= rn && rn < 32; + assert 0 <= rm && rm < 32; + // Todo: |8 is not needed + return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5); + } + public static void f_cmp(Encoding enc, Node n) { + short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); + short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET); + int body = f_cmp(0b00011110, 3, reg2, reg1); + enc.add4(body); + } + + public static COND make_condition(String bop, boolean fp) { + // One of {N,Z,C} and !V + // OR { !N, !Z, C, V } + return fp + ? switch (bop) { // FP uses unsigned encodings + case "==" -> COND.EQ; // Z + case "!=" -> COND.NE; // !Z + case "<" -> COND.MI; // N + case "<=" -> COND.LS; // Z || !C + case ">=" -> COND.PL; // !N + case ">" -> COND.HI; // !Z && C + default -> throw Utils.TODO(); + } + : switch (bop) { + case "==" -> COND.EQ; // Z + case "!=" -> COND.NE; // !Z + case "<" -> COND.LT; // N != V + case "<=" -> COND.LE; // Z || N != V + case ">=" -> COND.GE; // N == V + case ">" -> COND.GT; // !Z && N == V + default -> throw Utils.TODO(); + }; + } + + public static int b_cond(int opcode, int delta, COND cond) { + // 24-5 == 19bits offset range + assert -(1<<19) <= delta && delta < (1<<19); + assert (delta&3)==0; + delta>>=2; + delta &= (1L<<19)-1; // Zero extend + return (opcode << 24) | ((delta)<< 5) | cond.ordinal(); + } + + public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) { + assert 0 <= rd && rd < 32; + assert 0 <= rn && rn < 32; + return (opcode << 21) | (rm << 16) | (cond.ordinal() << 12) | (rn << 5) | rd; + } + + // Branch with Link to Register calls a subroutine at an address in a register, setting register X30 to PC+4. + public static int blr(int opcode, int rd) { + assert 0 <= rd && rd < 32; + return opcode << 10 | rd << 5; + } + public static int b(int opcode, int delta) { + assert -(1<<26) <= delta && delta < (1<<26); + assert (delta&3)==0; + delta>>=2; + delta &= (1L<<26)-1; // Zero extend + return (opcode << 26) | delta; + } + // no aligned assert(assert (delta&3)==0;) + public static int b_calloc(int opcode, int delta) { + assert -(1<<26) <= delta && delta < (1<<26); + delta>>=2; + delta &= (1L<<26)-1; // Zero extend + return (opcode << 26) | delta; + } + + public static long decodeImm12(int imm12) { + int immr = (imm12 >> 6) & 0x3F; + int imms = imm12 & 0x3F; + int size; + if ((imm12 & 0x1000) != 0) { + size = 64; + } else { + size = 31-(imms >> 1); + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size++; + imms &= ~((32-size) << 1); + } + long val = (2L << imms)-1; + while (size < 64) { + val |= val << size; + size <<= 1; + } + val = (val >>> immr) | val << (64-immr); + return val; + } + + @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { + if( idx==0 ) return CodeGen.CODE._rpcMask; + if( idx==1 ) return null; + // Count floats in signature up to index + if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence + boolean hidden = hiddenSelf(tfp); + int sigidx = idx-2; + if( hidden ) { + if( sigidx==0 ) return null; + sigidx--; + } + // Count floats in signature up to index + int fcnt=0; + for( int i=hidden ? 1 : 0; i<idx-2; i++ ) + if( tfp.arg(i) instanceof TypeFloat) + fcnt++; + // Floats up to XMMS in XMM registers + if( tfp.arg(idx-2) instanceof TypeFloat ) { + if( fcnt < XMMS.length ) + return XMMS[fcnt]; + } else { + RegMask[] cargs = CALLINMASK; + if( sigidx-fcnt < cargs.length ) + return cargs[sigidx-fcnt]; + } + // Pass on stack slot (8 and higher) + if( maxArgSlot>0 ) throw Utils.TODO(); + return new RegMask(MAX_REG + 1 + sigidx); + } + + // Return the max stack slot used by this signature, or 0 + @Override public short maxArgSlot( TypeFunPtr tfp ) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=hiddenSelf(tfp) ? 1 : 0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; + } + int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0); + return (short)nstk; + } + private static boolean hiddenSelf(TypeFunPtr tfp) { + return tfp.nargs() > 0 && tfp.arg(0) == TypePtr.PTR; + } + + private static final long CALLEE_SAVE = + 1L<<X19 | + 1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 | + 1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 | + 1L<<X28 | + 1L<<D9 | 1L<<D10 | 1L<<D11 | + 1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15; + static final long CALLER_SAVE = ~CALLEE_SAVE & ~(1L<<RSP); + @Override public long callerSave() { return CALLER_SAVE; } + @Override public long neverSave() { return 1L<<RSP; } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK; + } + @Override public int rpc() { return X30; } + + // Create a split op; any register to any register, including stack slots + @Override public SplitNode split(LRG lrg, String kind, byte round) { return new SplitARM(lrg,kind,round); } + + // Return a MachNode unconditional branch + @Override public CFGNode jump() { + return new UJmpARM(); + } + + // Instruction selection + @Override public Node instSelect(Node n ) { + return switch( n ) { + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CheckCastNode cast -> new CheckCastMach(cast); + case PtrToIntNode ptr -> new PtrToIntMach(ptr); + case GuardNode guard -> new GuardMach(guard); + case CallEndNode cend-> new CallEndMach(cend); + case CProjNode c -> new CProjNode(c); + case ConstantNode con-> con(con); + case DivNode div -> div.mode()==2 ? new DivFARM(div) : new DivARM(div); + case EscapeNode esc -> new EscapeNode(esc); + case FunNode fun -> new FunARM(fun); + case FunPtrNode fptr -> fptr(fptr); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem-> new MemMergeNode(mem); + case MinusNode neg -> { + if( neg.mode()==2 ) throw Utils.TODO(); + yield new NegARM(neg); + } + case MulNode mul -> mul.mode()==2 ? new MulFARM(mul) : new MulARM(mul); + case NewNode nnn -> new NewARM(nnn); + case NotNode not -> new NotARM(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmARM(parm); + case BulkMemPhiNode phi -> new BulkMemPhiNode(phi); + case MemPhiNode phi -> new MemPhiNode(phi); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read -> new ReadOnlyMach(read); + case ReturnNode ret -> new RetARM(ret,ret.fun()); + case SarNode sar -> asr(sar); + case ShlNode shl -> lsl(shl); + case ShrNode shr -> lsr(shr); + case StartCUNode start -> new StartCUNode(start); + case StopCUNode stop -> new StopCUNode(stop); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubNode sub -> sub.mode()==2 ? new SubFARM(sub) : sub(sub); + case ToFloatNode tfn -> new I2F8ARM(tfn); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region-> new RegionNode(region); + default -> throw Utils.TODO(); + }; + } + + private Node cmp(BoolNode bool){ + Node cmp = _cmp(bool); + return new SetARM(cmp, bool.op(), cmp instanceof CmpFARM); + } + private Node _cmp(BoolNode bool) { + if( bool.isFloat() ) + return new CmpFARM(bool); + return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new CmpIARM(bool, (int)ti.value()) + : new CmpARM(bool); + } + + private Node ld(LoadNode ld) { + return new LoadARM(address(ld), ld.ptr(), idx, off); + } + + private Node jmp(IfNode iff) { + // If/Bool combos will match to a Cmp/Set which sets flags. + // Most general arith ops will also set flags, which the Jmp needs directly. + // Loads do not set the flags, and will need an explicit TEST + String op = "!="; + boolean fp = false; + if( iff.in(1) instanceof BoolNode bool ) { op = bool.op(); fp = bool.isFloat(); } + else if( iff.in(1)==null ) op = "=="; // Never-node cutout + else iff.setDef(1, new BoolNode.NE(iff.in(1), + ConstantNode.raw(TypeInteger.ZERO), + (byte)(iff.in(1)._type instanceof TypeFloat ? 2 : 1))); + return new BranchARM(iff, op, fp); + } + + private Node add(AddNode add) { + if( add.mode()==2 ) + return new AddFARM(add); + return add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti) + ? new AddIARM(add, (int)ti.value()) + : new AddARM(add); + } + + private Node sub(SubNode sub) { + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) + ? new SubIARM(sub, (int)(ti.value())) + : new SubARM(sub); + } + + private Node con( ConstantNode con ) { + if( !con._con.isConstant() ) + return ConstantNode.raw(con); // Default unknown caller inputs + String ext = con instanceof ExternNode ext0 ? ext0._extern : null; + return switch( con._con ) { + case TypeInteger ti -> new IntARM(con,ext); + case TypeFloat tf -> new FltARM(con,ext); + case TypeMemPtr tmp -> new TMPARM(con,ext); + case TypeFunPtr tfp -> new TFPARM(tfp,ext); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> t==Type.NIL ? new IntARM(con,ext) : ConstantNode.raw(con); + }; + } + + private Node fptr( FunPtrNode con ) { + return new TFPARM(con); + } + + private Node call(CallNode call){ + return call.fptr() instanceof FunPtrNode fptr + ? new CallARM(call, (TypeFunPtr)fptr._type) + : call.fptr() instanceof ConstantNode con + ? new CallARM(call, (TypeFunPtr)con._type) + : new CallRRARM(call); + } + + private Node or(OrNode or) { + int imm12; + return or.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 + ? new OrIARM(or, imm12) + : new OrARM(or); + } + + private Node xor(XorNode xor) { + int imm12; + return xor.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 + ? new XorIARM(xor, imm12) + : new XorARM(xor); + } + + private Node and(AndNode and) { + int imm12; + return and.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 + ? new AndIARM(and, imm12) + : new AndARM(and); + } + + private Node asr(SarNode asr) { + return asr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 + ? new AsrIARM(asr, (int)ti.value()) + : new AsrARM(asr); + } + + private Node lsl(ShlNode lsl) { + return lsl.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 + ? new LslIARM(lsl, (int)ti.value()) + : new LslARM(lsl); + } + + private Node lsr(ShrNode lsr) { + return lsr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 + ? new LsrIARM(lsr, (int)ti.value()) + : new LsrARM(lsr); + } + + private Node prj(ProjNode prj) { + return prj.in(0) instanceof StartNode ? new ProjNode(prj) : new ProjARM(prj); + } + + + private static int off; + private static Node idx; + private Node st(StoreNode st) { + return new StoreARM(address(st),st.ptr(),idx,off,st.val()); + } + + // Gather addressing mode bits prior to constructing. This is a builder + // pattern, but saving the bits in a *local* *global* here to keep mess + // contained. + private <N extends MemOpNode> N address(N mop ) { + off = 0; // Reset + idx = null; + Node base = mop.ptr(); + // Skip/throw-away a ReadOnly, only used to typecheck + if( base instanceof ReadOnlyNode read ) base = read.in(1); + assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived + if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm9(ti) ) { + off = (int)ti.value(); + assert off == ti.value(); // In 32-bit range + } else { + idx = mop.off(); + } + return mop; + } + + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java new file mode 100644 index 00000000..1c157bc1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.FunPtrNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Add upper 20bits to PC. Immediate comes from the relocation info. +public class AUIPC extends FunPtrNode implements MachNode, RIPRelSize { + AUIPC( FunPtrNode fptr ) { super(fptr); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public AUIPC copy() { return new AUIPC(this); } + @Override public String op() { return "auipc"; } + @Override public void encoding( Encoding enc ) { + enc.relo(this); + short dst = enc.reg(this); + enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); + } + + // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding + @Override public byte encSize(int delta) { return 4; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + //short rpc = enc.reg(this); + //// High half is where the TFP constant used to be, the last input + //short auipc = enc.reg(in(_inputs._len-1)); + //enc.patch4(opStart,riscv.i_type(0x67, rpc, 0, auipc, delta)); + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + sb.p(reg).p(" = PC+#"); + _type.print(sb); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java new file mode 100644 index 00000000..8636df9f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// fadd.d +public class AddFRISC extends MachConcreteNode implements MachNode { + AddFRISC(Node addf) {super(addf);} + @Override public String op() { return "addf"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + @Override public RegMask outregmap() { return riscv.FMASK; } + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,1); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java new file mode 100644 index 00000000..511b0583 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java @@ -0,0 +1,21 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class AddIRISC extends ImmRISC { + // Used to inst-selection as a direct match against an ideal Add/Sub + public AddIRISC( Node add, int imm12, boolean pop ) { super(add,imm12,pop); } + @Override public String op() { return "addi"; } + @Override public String glabel() { return "+"; } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() {return 0;} + @Override public AddIRISC copy() { + // Clone the AddI, using the same inputs-only code used during inst select. + // Output edges are missing. + AddIRISC add = new AddIRISC(in(1),_imm12,false); + // Copys happen when output edges should be valid, so correct missing output edges. + in(1)._outputs.add(add); + return add; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java new file mode 100644 index 00000000..7010916f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java @@ -0,0 +1,21 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class AddRISC extends MachConcreteNode implements MachNode { + public AddRISC( Node add ) { super(add); } + AddRISC( Node base, Node off ) { + super(new Node[3]); + _inputs.set(1,base); + _inputs.set(2,off ); + } + @Override public String op() { return "add"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java new file mode 100644 index 00000000..842c8bdc --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java @@ -0,0 +1,12 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class AndIRISC extends ImmRISC { + public AndIRISC( Node and, int imm) { super(and,imm); } + @Override public String op() { return "andi"; } + @Override public String glabel() { return "&"; } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() {return 7;} +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java new file mode 100644 index 00000000..1845985e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java @@ -0,0 +1,16 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class AndRISC extends MachConcreteNode implements MachNode { + public AndRISC(Node and) { super(and); } + @Override public String op() { return "and"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b111,0); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java new file mode 100644 index 00000000..b7284265 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java @@ -0,0 +1,93 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +// Conditional branch such as: BEQ +public class BranchRISC extends IfNode implements MachNode, RIPRelSize { + String _bop; + // label is obtained implicitly + public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { + super(iff); + _bop = bop; + _inputs.setX(1,n1); + _inputs.setX(2,n2); + } + + @Override public String op() { return "b"+_bop; } + @Override public String label() { return op(); } + @Override public String comment() { return "L"+cproj(1)._nid; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return null; } + @Override public void negate() { + _bop = negate(_bop); + // Cannot encode ">" or "<=", so flip these + if( _bop.equals(">") || _bop.equals("<=") ) { + swap12(); // Cannot negate the test, so swap the operands + _bop = swap(_bop); // Swap test to match + } + } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + sb.append("if( "); + if( in(1)==null ) sb.append("0"); + else in(1)._print0(sb,visited); + sb.append(_bop); + if( in(2)==null ) sb.append("0"); + else in(2)._print0(sb,visited); + return sb.append(" )"); + } + + @Override public void encoding( Encoding enc ) { + if( in(1)==null && _bop=="!=" ) return; // Inverted never-node, no code + enc.jump(this,cproj(0)); + if( in(1)==null ) // Never node + enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); + else { + // Todo: relocs (for offset - immf) + short src1 = enc.reg(in(1)); + short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); + enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0)); + } + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code + if( -4*1024 <= delta && delta < 4*1024 ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch + if( in(1)==null ) { // Never node + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); + } else { + short src1 = enc.reg(in(1)); + short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); + if( opLen==4 ) { + enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta)); + } else { + throw Utils.TODO(); + } + } + } + + @Override public void asm(CodeGen code, SB sb) { + if( in(1)==null && _bop=="!=" ) { + sb.p("never"); + return; + } + String src1 = in(1)==null ? "#0" : code.reg(in(1)); + String src2 = in(2)==null ? "#0" : code.reg(in(2)); + sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" "); + CFGNode prj = cproj(0).uctrlSkipEmpty(); + if( !prj.blockHead() ) prj = prj.cfg0(); + sb.p(label(prj)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java new file mode 100644 index 00000000..8e1b4a48 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -0,0 +1,61 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class CallRISC extends CallNode implements MachNode, RIPRelSize { + final TypeFunPtr _tfp; + final String _name; + CallRISC( CallNode call, TypeFunPtr tfp ) { + super(call); + assert tfp.isConstant(); + _inputs.pop(); // Pop constant target + _tfp = tfp; + _name = CodeGen.CODE.funcName(tfp.fidx()); + } + + @Override public String op() { return "call"; } + @Override public String label() { return op(); } + @Override public String name() { return _name; } + @Override public TypeFunPtr tfp() { return _tfp; } + @Override public RegMask regmap(int i) { return riscv.callInMask(_tfp,i,fun()._maxArgSlot); } + @Override public RegMask outregmap() { return riscv.RPC_MASK; } + @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr + + @Override public void encoding( Encoding enc ) { + // Short form +/-4K: beq r0,r0,imm12 + // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] + FunNode fun = CodeGen.CODE.link(_tfp); + if( fun==null ) enc.external(this,_name); + else enc.relo(this); + short rpc = enc.reg(this); + enc.add4(riscv.j_type(riscv.OP_JAL, rpc, 0)); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short rpc = enc.reg(this); + if( opLen==4 ) { + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, rpc, delta)); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(_name).p(" "); + for( int i=0; i<nargs(); i++ ) + sb.p(code.reg(arg(i+2))).p(", "); + sb.unchar(2); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java new file mode 100644 index 00000000..7369f9f5 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class CallRRISC extends CallNode implements MachNode { + CallRRISC( CallNode call ) { super(call); } + @Override public String op() { return "callr"; } + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { + return i==_inputs._len-1 + ? riscv.RMASK // Function call target + : riscv.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument + } + @Override public RegMask outregmap() { return riscv.RPC_MASK; } + + @Override public void encoding( Encoding enc ) { + short rpc = enc.reg(this); + short src = enc.reg(in(_inputs._len-1)); + int body = riscv.i_type(riscv.OP_JALR, rpc, 0, src, 0); + enc.add4(body); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(fptr())).p(" "); + for( int i=0; i<nargs(); i++ ) + sb.p(code.reg(arg(i))).p(", "); + sb.unchar(2); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java new file mode 100644 index 00000000..68ed6f6c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// fdiv.d +public class DivFRISC extends MachConcreteNode implements MachNode{ + DivFRISC(Node divf) {super(divf);} + @Override public String op() { return "divf"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + @Override public RegMask outregmap() { return riscv.FMASK; } + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RUP,13); } + // Default on double precision for now(64 bits) + // General form: "fdiv.d rd = src1 / src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java new file mode 100644 index 00000000..515ffa47 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; + +public class DivRISC extends MachConcreteNode implements MachNode{ + public DivRISC(Node div) {super(div);} + @Override public String op() { return "div"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,1); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java new file mode 100644 index 00000000..c1a9b018 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java @@ -0,0 +1,47 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class FltRISC extends ConstantNode implements MachNode, RIPRelSize { + final String _ext; + FltRISC( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { return "ld8"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.FMASK; } + @Override public boolean isClone() { return true; } + @Override public FltRISC copy() { return new FltRISC(this,_ext); } + + @Override public void encoding( Encoding enc ) { + enc.largeConstant(this,_con, 0, -1); + short dst = (short)(enc.reg(this) - riscv.F_OFFSET); + short tmp = (short)riscv.T6; + // AUIPC dst,#hi20_constant_pool + enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0)); + // Load dst,[dst+#low12_constant_pool] + enc.add4(riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, 0)); + } + @Override public RegMask killmap() { return new RegMask(riscv.T6); } + + // Delta is from opcode start. + @Override public byte encSize(int delta) { return 8; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = (short)(enc.reg(this) - riscv.F_OFFSET); + short tmp = (short)riscv.T6; + // AUIPC dst,#hi20_constant_pool + enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12)); + // Load dst,[dst+#low12_constant_pool] + enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, delta & 0xFFF)); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java new file mode 100644 index 00000000..6fce6244 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class FunRISC extends FunNode implements MachNode { + FunRISC(FunNode fun) {super(fun);} + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); + } + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz >= 1L<<12 ) throw Utils.TODO(); + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, -sz & 0xFFF)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java new file mode 100644 index 00000000..db0547fe --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java @@ -0,0 +1,23 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// fcvt.d.w +// Converts a 32-bit signed integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd. +public class I2F8RISC extends MachConcreteNode implements MachNode { + I2F8RISC(Node i2f8) {super(i2f8);} + @Override public String op() { return "cvtf"; } + @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.FMASK; } + @Override public void encoding( Encoding enc ) { + short dst = (short)(enc.reg(this )-riscv.F_OFFSET); + short src1 = enc.reg(in(1)); + int body = riscv.r_type(riscv.OP_FP,dst,riscv.RM.RNE.ordinal(),src1,0,0x69); + enc.add4(body); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java new file mode 100644 index 00000000..f2beb25c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; +import java.util.BitSet; + +abstract public class ImmRISC extends MachConcreteNode implements MachNode { + public final int _imm12; + ImmRISC( Node add, int imm12, boolean pop ) { + super(add); + _imm12 = imm12; + // If pop, we are moving from an ideal Node to MachNode; the ideal Node + // inputs got copied here, but we are absorbing the immediate into the + // MachNode and so do not want the constant input. + if( pop ) _inputs.pop(); + // if NOT pop, we are the 2nd-half of a 2-part expansion, reset the + // input edges to no-control + else { _inputs.set(0,null); _inputs.setX(1,add); } + } + ImmRISC( Node add, int imm12 ) { this(add,imm12,true); } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { + // Allow cloning of the 2nd half of a 2-part constant; this might + // trigger cloning the first part also. + return in(1) instanceof LUI || in(1) instanceof AUIPC; + } + + @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { + int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits + in(1)._print0(sb.append("( "), visited); + return sb.append(String.format(" %s #%d )",glabel(),imm12)); + } + + abstract int opcode(); + abstract int func3(); + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + enc.add4(riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF)); + } + + // General form: "addi rd = rs1 + imm" + @Override public void asm(CodeGen code, SB sb) { + int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(imm12); + if( in(1) instanceof LUI lui ) + sb.p(" // #").hex4((int)(((TypeInteger)lui._con).value()) + imm12); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java new file mode 100644 index 00000000..e6b4c4ed --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Special instruction for loading 8 byte constants from the constant pool. + +public class Int8RISC extends ConstantNode implements MachNode, RIPRelSize { + final String _ext; + Int8RISC( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { return "ld8"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public Int8RISC copy() { return new Int8RISC(this,_ext); } + + @Override public byte encSize(int delta) { return 8; } + + @Override public void encoding( Encoding enc ) { + if( _ext!=null ) throw Utils.TODO(); + short dst = enc.reg(this); + short tmp = (short)riscv.T6; + enc.largeConstant(this,_con, 0, -1); + // AUIPC dst,#hi20_constant_pool + enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0)); + // Load dst,[dst+#low12_constant_pool] + enc.add4(riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, 0)); + } + @Override public RegMask killmap() { return new RegMask(riscv.T6); } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = enc.reg(this); + short tmp = (short)riscv.T6; + // AUIPC dst,#hi20_constant_pool + enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12)); + // Load dst,[dst+#low12_constant_pool] + enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, delta & 0xFFF)); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java new file mode 100644 index 00000000..b795ba96 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; + +// 12-bit integer constant. Larger constants are made up in the instruction +// selection by adding with a LUI. +public class IntRISC extends ConstantNode implements MachNode { + IntRISC(ConstantNode con) { super(con); } + @Override public String op() { return "ldi"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public IntRISC copy() { return new IntRISC(this); } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); + int val = _con==Type.NIL ? 0 : (int)(((TypeInteger)_con).value() & 0xFFF); + // Explicit truncation of larger immediates; this will sign-extend on + // load and this is handled during instruction selection. + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, val)); + } + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + _con.print(sb.p(reg).p(" = #")); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java new file mode 100644 index 00000000..7333f2b7 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java @@ -0,0 +1,32 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; + +// Load upper 20bits. +public class LUI extends ConstantNode implements MachNode { + public LUI( int imm20 ) { + super(TypeInteger.constant(imm20)); + assert (imm20 & 0xFFF)==0; + } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public LUI copy() { return new LUI((int)((TypeInteger)_con).value()); } + @Override public String op() { return "lui"; } + @Override public void encoding( Encoding enc ) { + long x = ((TypeInteger)_con).value(); + int imm20 = (int)(x>>12) & 0xFFFFF; + short dst = enc.reg(this); + int lui = riscv.u_type(riscv.OP_LUI, dst, imm20); + enc.add4(lui); + } + + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + sb.p(reg).p(" = #").hex4((int)(((TypeInteger)_con).value())); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java new file mode 100644 index 00000000..592cdb84 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java @@ -0,0 +1,64 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Load memory addressing on RISC +// Support imm, reg(direct), or reg+off(indirect) addressing +// Base = base - base pointer, offset is added to base +// idx = null +// off = off - imm12 added to base +public class LoadRISC extends MemOpRISC { + public LoadRISC(LoadNode ld, Node base, int off) { super(ld, base, off, null); } + @Override public String op() { return "ld" +_sz; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.MEM_MASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + int op = dst >= riscv.F_OFFSET ? riscv.OP_LOADFP : riscv.OP_LOAD; + if( dst >= riscv.F_OFFSET ) dst -= riscv.F_OFFSET; + enc.add4(riscv.i_type(op, dst, func3(), ptr, _off)); + } + @Override int func3() { + int func3 = -1; + // no unsigned flavour for store, so both signed and unsigned trigger the same + Type declType = _con; + if( declType == TypeInteger. I8 ) func3=0; // LB + if( declType == TypeInteger.I16 ) func3=1; // LH + if( declType == TypeInteger.I32 ) func3=2; // LW + if( declType == TypeInteger.BOT ) func3=3; // LD + if( declType == TypeInteger. U8 ) func3=4; // LBU + if( declType == TypeInteger.BOOL) func3=4; // LBU + if( declType == TypeInteger.U16 ) func3=5; // LHU + if( declType == TypeInteger.U32 ) func3=6; // LWU + if( declType instanceof TypeInteger ti ) { + if( -128 <= ti._min && ti._max < 128 ) func3 = 0; // LB + } + + // float + if( declType == TypeFloat.F32) func3 = 2; // fLW fSW + if( declType == TypeFloat.F64) func3 = 3; // fLD fSD + + if( declType instanceof TypeMemPtr) func3 = 3; // 8 byte pointers (pick ld) + if( declType instanceof TypeFunPtr) func3 = 3; // 8 byte function pointers + if( declType instanceof TypeConAry ary ) + func3 = switch( ary.log_size() ) { + case 0 -> 4; // LBU + case 1 -> 5; // LHU + case 2 -> 2; // LW + case 3 -> 3; // LD + default -> throw Utils.TODO("RISC-V constant-array load for "+declType); + }; + if( func3 == -1 ) throw Utils.TODO("RISC-V load for "+declType); + return func3; + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(","); + asm_address(code,sb); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java new file mode 100644 index 00000000..79f22fb8 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import java.util.BitSet; + +public abstract class MemOpRISC extends MemOpNode implements MachNode { + final int _off; // Limit 12 bits + final char _sz; + final int _bytes; + MemOpRISC(MemOpNode mop, Node base, int off, Node val) { + super(mop,mop); + assert base._type instanceof TypeMemPtr; + _inputs.setX(2, base ); // Base can be an Add, is no longer raw object base + _inputs.setX(3, null); // Never an index + _inputs.setX(4, val ); + _off = off; + _bytes = mop instanceof StoreNode st ? st.storeSize() : 1<<_con.log_size(); + _sz = (char)('0'+_bytes); + } + + @Override public String label() { return op(); } + Node val() { return in(4); } // Only for stores + + @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } + + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + + // func3 is based on load/store size and extend + abstract int func3(); + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + // 0 - ctrl + // 1 - memory + if( i==2 ) return riscv.RMASK; // base + // 2 - index + if( i==4 ) return riscv.MEM_MASK; // value + throw Utils.TODO(); + } + + SB asm_address(CodeGen code, SB sb) { + sb.p("[").p(code.reg(ptr())).p("+"); + return sb.p(_off).p("]"); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java new file mode 100644 index 00000000..5cc40079 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// fmul.d +public class MulFRISC extends MachConcreteNode implements MachNode { + MulFRISC(Node mulf) {super(mulf);} + @Override public String op() { return "mulf"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + @Override public RegMask outregmap() { return riscv.FMASK; } + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,9); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java new file mode 100644 index 00000000..9cb9ace7 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// mulh signed multiply instruction(no-imm form) +public class MulRISC extends MachConcreteNode implements MachNode{ + public MulRISC(Node mul) {super(mul);} + @Override public String op() { return "mul"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,1); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java new file mode 100644 index 00000000..d8183d09 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java @@ -0,0 +1,20 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class NegRISC extends MachConcreteNode implements MachNode { + public NegRISC( Node neg ) { super(neg); } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + riscv.r_type(riscv.OP,dst,0,0,src,0x20); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = -").p(code.reg(in(1))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java new file mode 100644 index 00000000..4a6583ce --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.ASMPrinter; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class NewRISC extends NewNode implements MachNode, RIPRelSize { + // A pre-zeroed chunk of memory. + NewRISC( NewNode nnn ) { super(nnn); } + @Override public void encoding( Encoding enc ) { + // Generic external encoding; 2 ops. + enc.external(this,"calloc"); + // A1 is a caller-save, allowed to crush building external address + // auipc + enc.add4(riscv.i_type(riscv.OP_IMM , _arg2Reg , 0, riscv.ZERO, 1)); + enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A2 , 0)); + enc.add4(riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, 0)); + } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public byte encSize(int delta) { return 4*3; } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + // Negative patches are JIT emulator targets. + if( opStart+delta < 0 ) { + delta -= 4; // Offset from the AUIPC start + int imm20 = delta>>12; + if( ((delta>>11)&1)==1 ) imm20++; // Correct accidental sign extension + int imm12 = delta&0xFFF; + enc.patch4(opStart+4,riscv.u_type(riscv.OP_AUIPC, riscv.A2 , imm20)); + enc.patch4(opStart+8,riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, imm12)); + } else + throw Utils.TODO(); + } + + // General form: "alloc #bytes PC" + @Override public void asm(CodeGen code, SB sb) { + sb.p("\n"); // Multi-op trigger + sb.p("ldi ").p(code._mach.regs()[_arg2Reg]).p(" = #1\n"); + sb.p("auipc a2=#calloc\n"); + sb.p("call a2+#calloc"); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java new file mode 100644 index 00000000..1369ee76 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java @@ -0,0 +1,20 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.NotNode; +import com.seaofnodes.simple.util.SB; + +public class NotRISC extends MachConcreteNode implements MachNode { + public NotRISC(NotNode not) { super(not); } + @Override public String op() { return "not"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 2, src, 1)); + } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java new file mode 100644 index 00000000..963b9d50 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java @@ -0,0 +1,12 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class OrIRISC extends ImmRISC { + public OrIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 6; } + @Override public String glabel() { return "|"; } + @Override public String op() { return "ori"; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java new file mode 100644 index 00000000..a310a1f4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java @@ -0,0 +1,16 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class OrRISC extends MachConcreteNode implements MachNode { + public OrRISC(Node or) { super(or); } + @Override public String op() { return "or"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,6,0); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java new file mode 100644 index 00000000..0f8db4e5 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ParmNode; + +public class ParmRISC extends ParmNode implements MachNode { + final RegMask _rmask; + ParmRISC(ParmNode parm) { + super(parm); + _rmask = riscv.callInMask(fun().sig(),_idx,0); + } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return _rmask; } + @Override public void encoding( Encoding enc ) { } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java new file mode 100644 index 00000000..2f9ac469 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java @@ -0,0 +1,15 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ProjNode; + +public class ProjRISC extends ProjNode implements MachNode { + ProjRISC(ProjNode p) { super(p); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { + return ((MachNode)in(0)).outregmap(_idx); + } + @Override public void encoding( Encoding enc ) { } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java new file mode 100644 index 00000000..7e4631d3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java @@ -0,0 +1,21 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ReturnNode; +import com.seaofnodes.simple.util.Utils; + +public class RetRISC extends ReturnNode implements MachNode { + public RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } + @Override public void encoding( Encoding enc ) { + int sz = fun()._frameAdjust; + if( sz >= 1L<<12 ) throw Utils.TODO(); + if( sz != 0 ) + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, sz & 0xFFF)); + short rpc = enc.reg(rpc()); + enc.add4(riscv.i_type(riscv.OP_JALR, riscv.ZERO, 0, rpc, 0)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java new file mode 100644 index 00000000..3424cd01 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.BoolNode; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; + +// corresponds to feq.d = equal +// fle.d = less than or equal +// flt.d = less than +public class SetFRISC extends MachConcreteNode implements MachNode { + final String _bop; // One of <,<=,== + SetFRISC( BoolNode bool ) { + super(bool); + assert bool.isFloat(); + _bop = bool.op(); + } + @Override public String op() { return "set"+_bop; } + @Override public RegMask regmap(int i) { return riscv.FMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src1 = (short)(enc.reg(in(1))-riscv.F_OFFSET); + short src2 = (short)(enc.reg(in(2))-riscv.F_OFFSET); + int body = riscv.r_type(riscv.OP_FP,dst,riscv.fsetop(_bop),src1,src2,0b1010001/*FCMP*/); + enc.add4(body); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java new file mode 100644 index 00000000..690e2102 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.util.Utils; +import com.seaofnodes.simple.node.Node; + +// corresponds to slti,sltiu +public class SetIRISC extends ImmRISC { + final boolean _unsigned; // slti vs sltiu + public SetIRISC( Node src, int imm12, boolean unsigned ) { + super(src,imm12); + _unsigned = unsigned; + } + @Override public String op() { return "slt" + (_unsigned ? "u":"") + "i"; } + @Override public String glabel() { return "<"+ (_unsigned ? "u":""); } + @Override int opcode() { return 0b0010011; } + @Override int func3() { return _unsigned ? 0b011 : 0b010; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java new file mode 100644 index 00000000..848cc59f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java @@ -0,0 +1,20 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// corresponds to slt sltu +public class SetRISC extends MachConcreteNode implements MachNode { + final boolean _unsigned; + public SetRISC( Node cmp, boolean unsigned ) { super(cmp); _unsigned=unsigned; } + @Override public String op() { return "slt" + (_unsigned ? "u":""); } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { + riscv.r_type(enc,this,_unsigned ? 0b011 : 0b010,0); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" < ").p(code.reg(in(2))); + } +}
\ No newline at end of file diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java new file mode 100644 index 00000000..12c0ddec --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java @@ -0,0 +1,12 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class SllIRISC extends ImmRISC { + public SllIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 1; } + @Override public String glabel() { return "<<"; } + @Override public String op() { return "slli"; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java new file mode 100644 index 00000000..5a4d4cc6 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Shift left logical +public class SllRISC extends MachConcreteNode implements MachNode { + public SllRISC(Node sll) { super(sll); } + @Override public String op() { return "shl"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,1,0); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java new file mode 100644 index 00000000..58fa8a4b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java @@ -0,0 +1,60 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.SplitNode; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class SplitRISC extends SplitNode { + SplitRISC( LRG lrg, String kind, byte round ) { super(lrg, kind,round, new Node[2]); } + @Override public RegMask regmap(int i) { return riscv.SPLIT_MASK; } + @Override public RegMask outregmap() { return riscv.SPLIT_MASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + + // Flag for being either XMM or stack + boolean dstX = dst >= riscv.F_OFFSET; + boolean srcX = src >= riscv.F_OFFSET; + + if( dst >= riscv.MAX_REG ) { + // Store to SP + if( src >= riscv.MAX_REG ) { + throw Utils.TODO(); // Very rare stack-stack move + } + int off = fun(enc).computeStackOffset(enc._code,dst); + int op = srcX ? riscv.OP_STOREFP : riscv.OP_STORE; + if( srcX ) src -= riscv.F_OFFSET; + enc.add4(riscv.s_type(op, 0b011, riscv.RSP, src, off)); + return; + } + if( src >= riscv.MAX_REG ) { + // Load from SP + int off = fun(enc).computeStackOffset(enc._code,src); + int op = dstX ? riscv.OP_LOADFP : riscv.OP_LOAD; + if( dstX ) dst -= riscv.F_OFFSET; + enc.add4(riscv.i_type(op, dst, 0b011, riscv.RSP, off)); + return; + } + // pick opcode based on regs + if( !dstX && !srcX ) { + // GPR->GPR + enc.add4(riscv.r_type(riscv.OP,dst,0,src,riscv.ZERO,0)); + } else if( dstX && srcX ) { + // FPR->FPR + src -= riscv.F_OFFSET; + dst -= riscv.F_OFFSET; + enc.add4(riscv.r_type(riscv.OP_FP, dst, 0b000, src, src, 0b0010101)); // dst = FMIN(src,src) + } else if(!srcX && dstX) { + //GPR->FPR + // fmv.d.x + enc.add4(riscv.r_type(riscv.OP_FP, dst - riscv.F_OFFSET, 0, src, 0, 0b1111001)); + } else if(srcX && !dstX) { + //FPR->GPR + //fmv.x.d + enc.add4(riscv.r_type(riscv.OP_FP, dst, 0, src - riscv.F_OFFSET, 0, 0b1110001)); + } + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java new file mode 100644 index 00000000..fcfa0774 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java @@ -0,0 +1,14 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class SraIRISC extends ImmRISC { + public SraIRISC( Node and, int imm) { + super(and,imm | (0x20 << 5)); + } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 5;} + @Override public String glabel() { return ">>"; } + @Override public String op() { return "srai"; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java new file mode 100644 index 00000000..e2e34415 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Right Shift Arithmetic +public class SraRISC extends MachConcreteNode implements MachNode { + public SraRISC(Node sra) { super(sra); } + @Override public String op() { return "sar"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b101,0b00100000); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java new file mode 100644 index 00000000..2f73e139 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java @@ -0,0 +1,12 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class SrlIRISC extends ImmRISC { + public SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 5; } + @Override public String glabel() { return ">>>"; } + @Override public String op() { return "srli"; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java new file mode 100644 index 00000000..1eccc1c8 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Right Shift Logical +public class SrlRISC extends MachConcreteNode implements MachNode { + public SrlRISC(Node srli) { super(srli); } + @Override public String op() { return "shr"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,7,0); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java new file mode 100644 index 00000000..ebd6a5ae --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Store memory addressing on ARM +// Support imm, reg(direct), or reg+off(indirect) addressing +// Base = base - base pointer, offset is added to base +// idx = null +// off = off - offset added to base) +// imm = imm - immediate value to store +// val = Node of immediate value to store(null if its a constant immediate) + +//e.g s.cs[0] = 67; // C +// base = s.cs, off = 4, imm = 67, val = null + +// sw rs2,offset(rs1) +public class StoreRISC extends MemOpRISC { + StoreRISC( StoreNode st, Node base, int off, Node val ) { super(st, base, off, val); } + @Override public String op() { return "st"+_sz; } + @Override public RegMask regmap(int i) { + // 0 - ctrl + if( i==1 ) return null; // mem + if( i==2 ) return riscv.RMASK; // ptr + // 2 - index + if( i==4 ) return riscv.MEM_MASK; // Wide mask to store GPR and FPR + return null; // Anti-dependence + } + @Override public RegMask outregmap() { return null; } + + @Override public void encoding( Encoding enc ) { + short val = enc.reg(val()); + short ptr = enc.reg(ptr()); + int op = val >= riscv.F_OFFSET ? riscv.OP_STOREFP : riscv.OP_STORE; + if( val >= riscv.F_OFFSET ) val -= riscv.F_OFFSET; + enc.add4(riscv.s_type(op, func3()&7, ptr, val == -1 ? riscv.ZERO : val, _off)); + } + + // func3 is based on load/store size and extend + @Override int func3() { + return Integer.numberOfTrailingZeros(_bytes); + } + + @Override public void asm(CodeGen code, SB sb) { + asm_address(code,sb).p(","); + if( val()==null ) sb.p("#").p("0"); + else sb.p(code.reg(val())); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java new file mode 100644 index 00000000..da3e92f4 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// fsub.d +public class SubFRISC extends MachConcreteNode implements MachNode { + SubFRISC(Node subf) {super(subf);} + @Override public String op() { return "subf"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + @Override public RegMask outregmap() { return riscv.FMASK; } + @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,5); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java new file mode 100644 index 00000000..2adfce23 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java @@ -0,0 +1,16 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class SubRISC extends MachConcreteNode implements MachNode { + public SubRISC( Node sub ) { super(sub); } + @Override public String op() { return "sub"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0x20); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java new file mode 100644 index 00000000..1f269152 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java @@ -0,0 +1,55 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class TFPRISC extends FunPtrNode implements MachNode, RIPRelSize { + final String _ext; + // Pointer to a Simple function + TFPRISC( FunPtrNode fptr ) { super(fptr); _ext = null; } + // Pointer to an Extern "C" function + TFPRISC( TypeFunPtr fptr, String ext ) { super(fptr,CodeGen.CODE._start,null); _type = fptr; _ext = ext; } + @Override public String op() { return "ldx"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public TFPRISC copy() { return new TFPRISC((TypeFunPtr)_con,_ext); } + @Override public void encoding( Encoding enc ) { + if( _ext==null ) enc.relo(this); // Internal patch + else enc.external(this,_ext); // ELF-file external patch + // TODO: 1 op encoding, plus a TODO if it does not fit + short dst = enc.reg(this); + // auipc t0,0 + enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); + // addi t1,t0 + #0 + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0)); + } + + @Override public byte encSize(int delta) { return 8; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short rpc = enc.reg(this); + if( opLen==8 ) { + int hi20 = (delta + 0x800) >> 12; + int lo12 = delta - (hi20 << 12); + // AUIPC (upper 20 bits) + // opstart of add + enc.patch4(opStart ,riscv.u_type(riscv.OP_AUIPC, rpc, hi20)); + // addi(low 12 bits) + enc.patch4(opStart+4,riscv.i_type(riscv.OP_IMM, rpc, 0, rpc, lo12 & 0xFFF)); + // addi + } else { + // should not happen as one instruction is 4 byte, and TFP arm encodes 2. + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + _type.print(sb.p(code.reg(this)).p(" #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java new file mode 100644 index 00000000..aa001e1e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeMemPtr; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize { + final String _ext; + TMPRISC( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { return "ldp"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public boolean isClone() { return true; } + @Override public TMPRISC copy() { return new TMPRISC(this,_ext); } + @Override public void encoding( Encoding enc ) { + if( _ext!=null ) throw Utils.TODO(); + enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1); + short dst = enc.reg(this); + // AUIPC dst,#hi20_constant_pool + enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); + // addi dst,[dst+#low12_constant_pool] + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0)); + } + + @Override public byte encSize(int delta) { return 8; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + short dst = enc.reg(this); + // AUIPC dst,#hi20_constant_pool + enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, dst, delta>>12)); + // Load dst,[dst+#low12_constant_pool] + enc.patch4(opStart+4, riscv.i_type(riscv.OP_IMM, dst, 0, dst, delta & 0xFFF)); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java new file mode 100644 index 00000000..c4d5d8af --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java @@ -0,0 +1,47 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +// unconditional jump +public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize { + @Override public String op() { return "jmp"; } + @Override public String label() { return op(); } + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); } + @Override public RegMask regmap(int i) {return null; } + @Override public RegMask outregmap() { return null; } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + @Override public void encoding( Encoding enc ) { + // Short form +/-4K: beq r0,r0,imm12 + // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] + enc.jump(this,uctrl()); + enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4; + // 2 word encoding needs a tmp register, must teach RA + throw Utils.TODO(); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + if( opLen==4 ) { + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); + } else { + throw Utils.TODO(); + } + } + + @Override public void asm(CodeGen code, SB sb) { + CFGNode target = uctrl(); + //assert target.nOuts() > 1; // Should optimize jmp to empty targets + sb.p(label(target)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java new file mode 100644 index 00000000..19ea6bc7 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java @@ -0,0 +1,13 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class XorIRISC extends ImmRISC { + public XorIRISC( Node and, int imm) { super(and,imm); } + public XorIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); } + @Override int opcode() { return riscv.OP_IMM; } + @Override int func3() { return 4; } + @Override public String glabel() { return "^"; } + @Override public String op() { return "xori"; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java new file mode 100644 index 00000000..413a7c3b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java @@ -0,0 +1,16 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class XorRISC extends MachConcreteNode implements MachNode { + public XorRISC(Node or) { super(or); } + @Override public String op() { return "xor"; } + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,0); } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java new file mode 100644 index 00000000..078a7693 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -0,0 +1,566 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; + +public class riscv extends Machine { + + public riscv(CodeGen code) { + if( !"SystemV".equals(code._callingConv) ) + throw new IllegalArgumentException("Unknown calling convention "+code._callingConv); + } + + @Override public String name() {return "riscv";} + + // Using ABI names instead of register names + public static int ZERO = 0, RPC= 1, RSP= 2, S12= 3, S13= 4, T0 = 5, T1 = 6, T2 = 7; + public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15; + public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23; + public static int S8 = 24, S9 = 25, S10= 26, S11= 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31; + + // FP registers + public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; + public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; + public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; + public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; + + static final int MAX_REG = 64; + + public static final int F_OFFSET = 32; + + static final String[] REGS = new String[] { + //"zero","rpc" , "rsp" , "s12" , "s13" , "t0" , "t1" , "t2" , + //"s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , + //"a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" , + //"s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" , + //"f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , + //"fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , + //"fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , + //"fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" + // Register-based naming hella easier to debug than "official" register + // names when single-stepping in debugger. + "zero", "r1" , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" , + "r8" , "r9" , "r10" , "r11", "r12", "r13", "r14", "r15", + "r16" , "r17" , "r18" , "r19", "r20", "r21", "r22", "r23", + "r24" , "r25" , "r26" , "r27", "r28", "r29", "r30", "r31", + "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , + "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , + "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , + "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" + }; + @Override public String[] regs() { return REGS; } + + // General purpose register mask: pointers and ints, not floats + static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs + static final RegMask RMASK = new RegMask(RD_BITS); + + static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and RSP + static final RegMask WMASK = new RegMask(WR_BITS); + // Float mask from(f0-ft10). TODO: ft10,ft11 needs a larger RegMask + static final long FP_BITS = 0b11111111111111111111111111111111L<<F0; + static final RegMask FMASK = new RegMask(FP_BITS); + + // Load/store mask; both GPR and FPR + static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS); + + static final RegMask SPLIT_MASK = new RegMask( WR_BITS | FP_BITS, -1L ); + + + // Arguments masks + public static RegMask A0_MASK = new RegMask(A0); + public static RegMask A1_MASK = new RegMask(A1); + public static RegMask A2_MASK = new RegMask(A2); + public static RegMask A3_MASK = new RegMask(A3); + public static RegMask A4_MASK = new RegMask(A4); + public static RegMask A5_MASK = new RegMask(A5); + public static RegMask A6_MASK = new RegMask(A6); + public static RegMask A7_MASK = new RegMask(A7); + + public static final RegMask RPC_MASK = new RegMask(RPC); + + // Float arguments masks + public static RegMask FA0_MASK = new RegMask(FA0); + public static RegMask FA1_MASK = new RegMask(FA1); + public static RegMask FA2_MASK = new RegMask(FA2); + public static RegMask FA3_MASK = new RegMask(FA3); + public static RegMask FA4_MASK = new RegMask(FA4); + public static RegMask FA5_MASK = new RegMask(FA5); + public static RegMask FA6_MASK = new RegMask(FA6); + public static RegMask FA7_MASK = new RegMask(FA7); + + // Int/ptr arguments calling convention + static RegMask[] CALLINMASK = new RegMask[] { + A0_MASK, + A1_MASK, + A2_MASK, + A3_MASK, + A4_MASK, + A5_MASK, + A6_MASK, + A7_MASK + }; + + static RegMask[] XMMS = new RegMask[] { + FA0_MASK, + FA1_MASK, + FA2_MASK, + FA3_MASK, + FA4_MASK, + FA5_MASK, + FA6_MASK, + FA7_MASK + }; + + // major opcode: OP + public static int OP_LOAD = 0b00_000_11; + public static int OP_LOADFP = 0b00_001_11; + public static int OP_CUSTOM0 = 0b00_010_11; + public static int OP_IMM = 0b00_100_11; + public static int OP_AUIPC = 0b00_101_11; + + public static int OP_STORE = 0b01_000_11; + public static int OP_STOREFP = 0b01_001_11; + public static int OP_CUSTOM1 = 0b01_010_11; + public static int OP = 0b01_100_11; + public static int OP_LUI = 0b01_101_11; + + public static int OP_CUSTOM2 = 0b10_010_11; + public static int OP_FP = 0b10_100_11; + + public static int OP_BRANCH = 0b11_000_11; + public static int OP_JALR = 0b11_001_11; + public static int OP_RESERVED= 0b11_010_11; + public static int OP_JAL = 0b11_011_11; + + + // Since riscv instructions are fixed we can just or them together + public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) { + assert 0 <= rs1 && rs1 < 32; + assert 0 <= rs2 && rs2 < 32; + assert 0 <= rd && rd < 32; + return (func7 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode; + } + public static void r_type(Encoding enc, Node n, int func3, int func7) { + short dst = enc.reg(n); + short src1 = enc.reg(n.in(1)); + short src2 = enc.reg(n.in(2)); + int body = r_type(OP,dst,func3,src1,src2,func7); + enc.add4(body); + } + + public static void rf_type(Encoding enc, Node n, RM func3, int func7) { + short dst = (short)(enc.reg(n )-F_OFFSET); + short src1 = (short)(enc.reg(n.in(1))-F_OFFSET); + short src2 = (short)(enc.reg(n.in(2))-F_OFFSET); + int body = r_type(OP_FP,dst,func3.ordinal(),src1,src2,func7); + enc.add4(body); + } + + + public static int u_type(int opcode, int rd, int imm20) { + assert 0 <= rd && rd < 32; + return (imm20 << 12) | (rd << 7) | opcode; + } + + public static int j_type(int opcode, int rd, int delta) { + assert -(1L<<20) <= delta && delta < (1L<<20); + assert 0 <= rd && rd < 32; + // Messy branch offset encoding + // 31 30-21 20 19-12 11-7 6-0 + // 20 10- 1 11 19-12 rpc JAL + assert (delta&1)==0; // Low bit is always zero, not encoded + int imm10_01 = (delta>> 1) & 0x3FF; + int imm11 = (delta>>11) & 1; + int imm12_19 = (delta>>12) & 0xFF; + int imm20 = (delta>>19) & 1; + int bits = imm20<<19 | imm10_01 << 9 | imm11 << 8 | imm12_19; + return bits << 12 | rd << 7 | opcode; + } + + + public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) { + assert 0 <= rd && rd < 32; + assert 0 <= rs1 && rs1 < 32; + assert opcode >= 0 && func3 >=0 && imm12 >= 0; // Zero-extend by caller + return (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode; + } + + // S-type instructions(store) + public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) { + assert 0 <= rs1 && rs1 < 32; + assert 0 <= rs2 && rs2 < 32; + assert 0 <= func3; + + assert imm12 >= 0; // Masked to high zero bits by caller + int imm_lo = imm12 & 0x1F; + int imm_hi = imm12 >> 5; + return (imm_hi << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm_lo << 7) | opcode; + } + + // BRANCH + public static int b_type(int opcode, int func3, short rs1, short rs2, int delta) { + assert 0 <= rs1 && rs1 < 32; + assert 0 <= rs2 && rs2 < 32; + assert -4*1024 <= delta && delta < 4*1024; + assert (delta&1)==0; // Low bit is always zero, not encoded + // Messy branch offset encoding + // 31 30 29 28 27 26 25 24-20 19-15 14-12 11 10 9 8 7 6-0 + // 12 10 9 8 7 6 5 SRC2 SRC1 FUNC3 4 3 2 1 11 OP + int imm4_1 = (delta>> 1) & 0xF; + int imm10_5= (delta>> 5) &0x3F; + int imm11 = (delta>>11) & 1; + int imm12 = (delta>>12) & 1; + int imm5 = imm4_1<<1 | imm11; + int imm7 = imm12<<6 | imm10_5; + return (imm7 << 25 ) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm5 << 7) | opcode; + } + + public enum RM { + RNE, // Round to Nearest, ties to Even + RTZ, // Round towards Zero + RDN, // Round Down + RUP, // Round Up + DIRECT, // Round to Nearest, ties to Max Magnitude + RESERVED1, // Reserved for futue use + RESERVED2, // Reserved for future use + DYN, // In instruction’s rm field, selects dynamic rounding mode; In Rounding Mode register, reserved + } + + // Since opcode is the same just return back func3 + static public int jumpop(String op) { + return switch(op) { + case "==" -> 0x0; + case "!=" -> 0x1; + case "<" -> 0x4; + case ">=" -> 0x5; + case "u<" -> 0x6; + case "u<=" -> 0x7; + default -> throw Utils.TODO(); + }; + } + + // Since opcode is the same just return back func3 + static public int fsetop(String op) { + return switch(op) { + case "<" -> 1; + case "<=" -> 0; + case "==" -> 2; + default -> throw Utils.TODO(); + }; + } + + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { + if( idx==0 ) return RPC_MASK; + if( idx==1 ) return null; + if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence + boolean hidden = hiddenSelf(tfp); + int sigidx = idx-2; + if( hidden ) { + if( sigidx==0 ) return null; + sigidx--; + } + // Count floats in signature up to index + int fcnt=0; + for( int i=hidden ? 1 : 0; i<idx-2; i++ ) + if( tfp.arg(i) instanceof TypeFloat ) + fcnt++; + // Floats up to XMMS in XMM registers + if( tfp.arg(idx-2) instanceof TypeFloat ) { + if( fcnt < XMMS.length ) + return XMMS[fcnt]; + } else { + RegMask[] cargs = CALLINMASK; + if( sigidx-fcnt < cargs.length ) + return cargs[sigidx-fcnt]; + } + // Pass on stack slot(8 and higher) + if( maxArgSlot>0 ) throw Utils.TODO(); + return new RegMask(MAX_REG + 1 + sigidx); + } + + @Override public short maxArgSlot( TypeFunPtr tfp ) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=hiddenSelf(tfp) ? 1 : 0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; + } + int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0); + return (short)nstk; + } + private static boolean hiddenSelf(TypeFunPtr tfp) { + return tfp.nargs() > 0 && tfp.arg(0) == TypePtr.PTR; + } + + static final long CALLER_SAVE = + (1L<<RPC) | + (1L<<A0 ) | (1L<<A1 ) | (1L<<A2 ) | (1L<<A3 ) | (1L<<A4 ) | (1L<<A5 ) | (1L<<A6 ) | (1L<<A7 ) | + (1L<<T0 ) | (1L<<T1 ) | (1L<<T2 ) | (1L<<T3 ) | (1L<<T4 ) | (1L<<T5 ) | (1L<<T6 ) | + (1L<<FA0) | (1L<<FA1) | (1L<<FA2) | (1L<<FA3) | (1L<<FA4) | (1L<<FA5) | (1L<<FA6) | (1L<<FA7) | + (1L<<F0 ) | (1L<<F1 ) | (1L<<F2 ) | (1L<<F3 ) | (1L<<F4 ) | (1L<<F5 ) | (1L<<F6 ) | (1L<<F7 ) | + (1L<<FT8) | (1L<<FT9) | (1L<<FT10)| (1L<<FT11); + + @Override public long callerSave() { return CALLER_SAVE; } + @Override public long neverSave() { return (1L<<RSP) | (1L<<ZERO); } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK; + } + @Override public int rpc() { return RPC; } + + + // Return a MachNode unconditional branch + @Override public CFGNode jump() { return new UJmpRISC(); } + + // Create a split op; any register to any register, including stack slots + @Override public SplitNode split(LRG lrg, String kind, byte round ) { return new SplitRISC(lrg, kind,round); } + + // True if signed 12-bit immediate + public static boolean imm12(TypeInteger ti) { + // 52 = 64-12 + return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value(); + } + // True if HIGH 20-bit signed immediate, with all zeros low. + public static boolean imm20Exact(TypeInteger ti) { + // shift left 32 to clear out the upper 32 bits. + // shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits. + // shift left 12 to re-center the bits. + return ti.isConstant() && (((ti.value()<<32)>>44)<<12) == ti.value(); + } + + @Override public Node instSelect( Node n ) { + return switch (n) { + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CheckCastNode cast -> new CheckCastMach(cast); + case PtrToIntNode ptr -> new PtrToIntMach(ptr); + case GuardNode guard -> new GuardMach(guard); + case CallEndNode cend -> new CallEndMach(cend); + case CProjNode c -> new CProjNode(c); + case ConstantNode con -> con(con); + case DivNode div -> div.mode()==2 ? new DivFRISC(div) : new DivRISC(div); + case EscapeNode esc -> new EscapeNode(esc); + case FunNode fun -> new FunRISC(fun); + case FunPtrNode fptr -> fptr(fptr); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MinusNode neg -> { + if( neg.mode()==2 ) throw Utils.TODO(); + yield new NegRISC(neg); + } + case MulNode mul -> mul.mode()==2 ? new MulFRISC(mul) : new MulRISC(mul); + case NewNode nnn -> nnn(nnn); + case NotNode not -> new NotRISC(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmRISC(parm); + case BulkMemPhiNode phi-> new BulkMemPhiNode(phi); + case MemPhiNode phi -> new MemPhiNode(phi); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read-> new ReadOnlyMach(read); + case ReturnNode ret -> new RetRISC(ret, ret.fun()); + case SarNode sar -> sra(sar); + case ShlNode shl -> sll(shl); + case ShrNode shr -> srl(shr); + case StartCUNode start -> new StartCUNode(start); + case StopCUNode stop -> new StopCUNode(stop); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubNode sub -> sub.mode()==2 ? new SubFRISC(sub) : sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region -> new RegionNode(region); + default -> throw Utils.TODO(); + }; + } + + private Node add(AddNode add) { + if( add.mode()==2 ) + return new AddFRISC(add); + if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof TypeInteger ti && imm12(ti) ) + return new AddIRISC(add, (int)ti.value(),true); + return new AddRISC(add); + } + + private Node and(AndNode and) { + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + if( imm12(ti) ) + return new AndIRISC(and, (int)ti.value()); + // Could be any size low bit mask + if( ti.value() == 0xFFFFFFFFL ) + return new SrlIRISC(new SllIRISC(and,32),32,false); + } + return new AndRISC(and); + } + + private Node call(CallNode call) { + return call.fptr() instanceof FunPtrNode fptr + ? new CallRISC(call, (TypeFunPtr)fptr._type) + : call.fptr() instanceof ConstantNode con + ? new CallRISC(call, (TypeFunPtr)con._type) + : new CallRRISC(call); + } + private Node nnn(NewNode nnn) { + // TODO: pass in the TFP for alloc + return new NewRISC(nnn); + } + + private Node cmp(BoolNode bool) { + // Float variant directly implemented in hardware + if( bool.isFloat() ) + return new SetFRISC(bool); + + // Only < and <u are implemented in hardware. + // x < y - as-is + // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` + // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` + + // x > y - swap; y < x + // x >= y - swap and negate; !(x < y); `slt tmp=y,x;` then NOT. + // x != y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` then NOT. + + // The ">", ">=" and "!=" in Simple include a NotNode, which can be + // implemented with a XOR. If one of the above is followed by a NOT + // we can remove the double XOR in the encodings. + + return switch( bool.op() ) { + case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) + ? new SetIRISC(bool, (int)ti.value(),false) + : new SetRISC(bool, false); + // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` + case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1,false); + // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` + case "==" -> new SetIRISC(new SubRISC(bool),1,true); + default -> throw Utils.TODO(); + }; + } + + private Node con( ConstantNode con ) { + if( !con._con.isConstant() ) + return ConstantNode.raw(con); // Default unknown caller inputs + String ext = con instanceof ExternNode ext0 ? ext0._extern : null; + return switch( con._con ) { + case TypeInteger ti -> { + // External; without knowing the size assume we have to load something + if( ext!=null ) yield new Int8RISC(con,ext); + if( imm12(ti) ) yield new IntRISC(con); + long x = ti.value(); + if( imm20Exact(ti) ) yield new LUI((int)x); + if( (x<<32)>>32 == x ) { // Signed lower 32-bit immediate + // Here, the low 12 bits get sign-extended, which means if + // bit11 is set, the value is negative and lowers the LUI + // value. Add a bit 12 to compensate + if( ((x>>11)&1)==1 ) x += 0x1000; + yield new AddIRISC(new LUI((int)(x & ~0xFFF)), (int)(x & 0xFFF),false); + } + // Need more complex sequence for larger constants... or a load + // from a constant pool, which does not need an extra register + yield new Int8RISC(con,null); + } + // Load from constant pool + case TypeFloat tf -> new FltRISC(con,ext); + case TypeMemPtr tmp -> new TMPRISC(con,ext); + case TypeFunPtr tfp -> new TFPRISC(tfp,ext); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> t==Type.NIL ? new IntRISC(con) : ConstantNode.raw(con); + }; + } + + private Node fptr( FunPtrNode con ) { + return new TFPRISC(con); + } + + private Node jmp( IfNode iff ) { + if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) { + // if less than or equal switch inputs + String bop = bool.op(); + if( bop.equals("<=") || bop.equals(">") ) + return new BranchRISC(iff, IfNode.swap(bop), bool.in(2), bool.in(1)); + return new BranchRISC(iff, bop, bool.in(1), bool.in(2)); + } + // Vs zero + return new BranchRISC(iff, iff.in(1)==null ? "==" : "!=", iff.in(1), null); + } + + private Node or(OrNode or) { + if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) + return new OrIRISC(or, (int)ti.value()); + return new OrRISC(or); + } + + private Node xor(XorNode xor) { + if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) + return new XorIRISC(xor, (int)ti.value()); + return new XorRISC(xor); + } + + private Node sra(SarNode sar) { + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) + return new SraIRISC(sar, (int)ti.value()); + return new SraRISC(sar); + } + + private Node srl(ShrNode shr) { + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) + return new SrlIRISC(shr, (int)ti.value(),true); + return new SrlRISC(shr); + } + + private Node sll(ShlNode sll) { + if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)) + return new SllIRISC(sll, (int)ti.value()); + return new SllRISC(sll); + } + + private Node sub(SubNode sub) { + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) + ? new AddIRISC(sub, (int)(-ti.value()),true) + : new SubRISC(sub); + } + + private Node i2f8(ToFloatNode tfn) { + assert tfn.in(1)._type instanceof TypeInteger ti; + return new I2F8RISC(tfn); + } + + private Node prj(ProjNode prj) { + return prj.in(0) instanceof StartNode ? new ProjNode(prj) : new ProjRISC(prj); + } + + private Node ld(LoadNode ld) { + return new LoadRISC(ld,address(ld),off); + } + + private Node st(StoreNode st) { + Node xval = st.val() instanceof ConstantNode con && con._con == TypeInteger.ZERO ? null : st.val(); + return new StoreRISC(st,address(st), off, xval); + } + + // Gather addressing mode bits prior to constructing. This is a builder + // pattern, but saving the bits in a *local* *global* here to keep mess + // contained. + private static int off; + private Node address( MemOpNode mop ) { + off = 0; // Reset + Node base = mop.ptr(); + // Skip/throw-away a ReadOnly, only used to typecheck + if( base instanceof ReadOnlyNode read ) base = read.in(1); + assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived + if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) { + off = (int)ti.value(); + } else { + base = new AddRISC(base,mop.off()); + base._type = mop.ptr()._type; + } + return base; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java new file mode 100644 index 00000000..093e2bd2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class AddFMemX86 extends MemOpX86 { + AddFMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) { + super(add,ld, base, idx, off, scale, 0, val ); + } + @Override public String op() { return "addf"+_sz; } + @Override public RegMask regmap(int i) { + if( i==1 ) return null; // Memory + if( i==2 ) return x86_64_v2.RMASK; // base + if( i==3 ) return x86_64_v2.RMASK; // index + if( i==4 ) return x86_64_v2.XMASK; // value + throw Utils.TODO(); + } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + @Override public int twoAddress() { return 4; } + @Override public void encoding( Encoding enc ) { + // addsd xmm0, DWORD PTR [rdi+0xc] + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + // F opcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, ptr, idx, false, enc); + + // FP ADD + enc.add1(0x0F).add1(0x58); + + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc); + } + + // General form: "add dst = src + [base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = "); + sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + "); + asm_address(code,sb); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java new file mode 100644 index 00000000..02e0c7ea --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class AddFX86 extends MachConcreteNode implements MachNode { + AddFX86( Node addf ) { super(addf); } + @Override public String op() { return "addf"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + @Override public int twoAddress() { return 1; } + @Override public boolean commutes() { return true; } + + @Override public void encoding( Encoding enc ) { + // F2 0F 58 /r ADDSD xmm1, xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x58); + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + + // General form: "addf dst += src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" += ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java new file mode 100644 index 00000000..0ba390c6 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java @@ -0,0 +1,24 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class AddIX86 extends ImmX86 { + AddIX86( Node add, int imm ) { super(add,imm); } + @Override public String op() { + return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi"); + } + @Override public String glabel() { return "+"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 0; } + @Override public final void encoding( Encoding enc ) { + if( _imm== 1 || _imm== -1 ) { + short dst = enc.reg(this); // Also src1 + enc.add1( x86_64_v2.rex(0,dst,0)); + enc.add1(0xFF); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, _imm==1 ? 0 : 1, dst) ); + } else super.encoding(enc); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java new file mode 100644 index 00000000..3ee721e3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class AddMemX86 extends MemOpX86 { + AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) { + super(add,ld, base, idx, off, scale, imm, val ); + } + @Override public String op() { return "add"+_sz; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 4; } + @Override public void encoding( Encoding enc ) { + // add something to register from memory + // add eax,DWORD PTR [rdi+0xc] + // REX.W + 03 /r ADD r64, r/m64 + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + + enc.add1(x86_64_v2.rex(dst, ptr, idx)); + // opcode + enc.add1(0x03); + + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc); + } + + // General form: "add dst += [base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" += "); + asm_address(code,sb); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java new file mode 100644 index 00000000..84543e8f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class AddX86 extends RegX86 { + AddX86( Node add ) { super(add); } + @Override public String op() { return "add"; } + @Override public String glabel() { return "+"; } + @Override int opcode() { return 0x03; } + @Override public boolean commutes() { return true; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java new file mode 100644 index 00000000..3c1b567c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class AndIX86 extends ImmX86 { + AndIX86( Node add, int imm ) { super(add,imm); } + @Override public String op() { return "andi"; } + @Override public String glabel() { return "&"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 4; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java new file mode 100644 index 00000000..dedbce45 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class AndX86 extends RegX86 { + AndX86( Node add ) { super(add); } + @Override public String op() { return "and"; } + @Override public String glabel() { return "&"; } + @Override int opcode() { return 0x23; } + @Override public boolean commutes() { return true; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java new file mode 100644 index 00000000..14b8e4da --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.CallNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; + +public class CallRX86 extends CallNode implements MachNode { + CallRX86( CallNode call ) { super(call); } + @Override public String op() { return "callr"; } + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { + return i==_inputs._len + ? x86_64_v2.WMASK // Function call target + : x86_64_v2.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument + } + @Override public RegMask outregmap() { return null; } + @Override public void encoding( Encoding enc ) { + // FF /2 CALL r/m64 + // calls the function in the register + short src = enc.reg(fptr()); + enc.add1(0xFF); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT,2,src)); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(fptr())).p(" "); + for( int i=0; i<nargs(); i++ ) + sb.p(code.reg(arg(i))).p(" "); + sb.unchar(2); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java new file mode 100644 index 00000000..989895b2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java @@ -0,0 +1,54 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.SB; + +public class CallX86 extends CallNode implements MachNode, RIPRelSize { + final TypeFunPtr _tfp; + final String _name; + final boolean _extern; + CallX86( CallNode call, TypeFunPtr tfp ) { + super(call); + _inputs.pop(); // Pop constant target + assert tfp.isConstant(); + _tfp = tfp; + _name = CodeGen.CODE.funcName(tfp.fidx()); + _extern = CodeGen.CODE.externFunc(tfp.fidx()) != null; + } + @Override public String op() { return "call"; } + @Override public String label() { return op(); } + @Override public String name() { return _name; } + @Override public TypeFunPtr tfp() { return _tfp; } + @Override public RegMask regmap(int i) { + return _extern + ? x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot,CodeGen.CODE._cCallingConv) + : x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot); + } + @Override public RegMask outregmap() { return null; } + @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr + @Override public boolean external() { return _extern; } + + @Override public void encoding( Encoding enc ) { + FunNode fun = CodeGen.CODE.link(_tfp); + if( fun==null ) enc.external(this,_name); + else enc.relo(this); + enc.add1(0xe8).add4(0); + } + + // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding + @Override public byte encSize(int delta) { return 5; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart+1, delta-5); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(_name).p(" "); + for( int i=0; i<nargs(); i++ ) + sb.p(code.reg(in(i+2))).p(", "); + sb.unchar(2); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java new file mode 100644 index 00000000..5b97ef63 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java @@ -0,0 +1,32 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class CmpFX86 extends MachConcreteNode implements MachNode { + CmpFX86( Node cmp ) { super(cmp); } + @Override public String op() { return "cmpf"; } + @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; } + @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + + @Override public void encoding( Encoding enc ) { + // UCOMISD + short src1 = (short)(enc.reg(in(1)) - x86_64_v2.XMM_OFFSET); + short src2 = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + // float opcode + enc.add1(0x66); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(src1, src2, 0, false, enc); + + enc.add1(0x0F).add1(0x2E); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src1, src2)); + } + + // General form: "cmp src1,src2" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="flags" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java new file mode 100644 index 00000000..8b8318eb --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +// Compare immediate. Sets flags. +public class CmpIX86 extends MachConcreteNode implements MachNode { + final int _imm; + CmpIX86( Node cmp, int imm, boolean swap ) { + super(cmp); + _inputs.del(swap ? 1 : 2); + _imm = imm; + } + CmpIX86( CmpIX86 cmp ) { + super((Node)null); + addDef(null); + addDef(cmp.in(1)); + _imm = cmp._imm; + } + + @Override public String op() { return _imm==0 ? "test" : "cmp"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new CmpIX86(this); } + + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(in(1)); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + // opcode; 0x81 or 0x83; 0x69 or 0x6B + enc.add1( 0x81 + (x86_64_v2.imm8(_imm) ? 2 : 0) ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst) ); + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="flags" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))); + if( _imm != 0 ) sb.p(", #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java new file mode 100644 index 00000000..24812fea --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java @@ -0,0 +1,78 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.util.SB; + +public class CmpMemX86 extends MemOpX86 { + final boolean _swap; // Op switched LHS, RHS + CmpMemX86( BoolNode bool, LoadNode ld, Node base, Node idx, int off, int scale, int imm, Node val, boolean swap ) { + super(bool,ld, base, idx, off, scale, imm, val ); + _swap = swap; + assert !_swap || val() != null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate + } + @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; } + @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + @Override public void encoding( Encoding enc ) { + // REX.W + 81 /7 id CMP r/m64, imm32 | REX.W + 39 /r CMP r/m64,r64 + // CMP [mem], imm32 + + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + short src = enc.reg(val()); + + if(src == -1) { + assert !_swap; + int log = _con.log_size(); + x86_64_v2.rexF(-1, ptr, idx, log==3, enc); + switch(log) { + case 0: enc.add1(0x80); break; + case 1: enc.add1(0x66).add1(0x81); break; + case 2: enc.add1(0x81); break; + case 3: enc.add1(0x81); break; + } + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, 7, enc); + switch(log) { + case 0: enc.add1(_imm); break; + case 1: enc.add2(_imm); break; + case 2: enc.add4(_imm); break; + case 3: enc.add8(_imm); break; + } + } else { + encVal(enc, _con, ptr, idx, src, _off, _scale, _swap); + } + } + + static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale, boolean _swap) { + if(decl instanceof TypeFloat) { + src -= (short)x86_64_v2.XMM_OFFSET; + enc.add1(decl==TypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2); + } else { + int log = decl.log_size(); + x86_64_v2.rexF(src, ptr, idx, log == 3, enc); + int swap = _swap ? 2 : 0; + switch(log) { + case 0: enc.add1(0x38+swap); break; + case 1: enc.add1(0x66).add1(0x39+swap); break; + case 2: enc.add1(0x39+swap); break; + case 3: enc.add1(0x39+swap); break; + } + } + x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc); + } + + // General form: "cmp dst = src, [base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="flags" ) sb.p(dst).p(" = "); + if( _swap ) asm_address(code,sb).p(","); + if( val()==null ) { + if( _imm!=0 ) sb.p("#").p(_imm); + } else { + sb.p(code.reg(val())); + } + if( !_swap ) asm_address(code,sb.p(", ")); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java new file mode 100644 index 00000000..e7305cdf --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java @@ -0,0 +1,32 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class CmpX86 extends MachConcreteNode implements MachNode { + CmpX86( Node add ) { super(add); } + @Override public String op() { return "cmp"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + // This one is marginal: cloning a Cmp[reg,reg] means stretching the + // lifetimes of 2 normal registers in exchange for shortening a flags + // register lifetime. Spilling flags is (probably) relatively expensive + // compared to some normal registers - and those normal registers might not + // be spilling! + @Override public boolean isClone() { return true; } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(in(1)); + short src = enc.reg(in(2)); + + enc.add1(x86_64_v2.rex(dst, src, 0)); + enc.add1(0x3B); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="flags" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java new file mode 100644 index 00000000..3427aea5 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class DivFX86 extends MachConcreteNode implements MachNode { + DivFX86( Node divf) { super(divf); } + @Override public String op() { return "divf"; } + @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + @Override public int twoAddress() { return 1; } + + @Override public void encoding( Encoding enc ) { + // F2 0F 5E /r DIVSD xmm1, xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x5E); + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + + + // General form: "divf dst /= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" /= ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java new file mode 100644 index 00000000..b5904a7f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class DivX86 extends MachConcreteNode implements MachNode { + DivX86( Node div ) { super(div); } + @Override public String op() { return "div"; } + @Override public RegMask regmap(int i) { + return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.DIV_MASK; + } + @Override public RegMask outregmap() { return x86_64_v2.RAX_MASK; } + @Override public RegMask killmap() { return x86_64_v2.RDX_MASK; } + + @Override public void encoding( Encoding enc ) { + // REX.W + F7 /7 IDIV r/m64 + short src = enc.reg(in(2)); + // sign extend rax before 128/64 bits division + // sign extend rax to rdx:rax + enc.add1(x86_64_v2.REX_W); + enc.add1(0x99); + enc.add1(x86_64_v2.rex(0, src, 0)); + // divide + enc.add1(0xF7); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x07, src)); + } + // General form: "div dst /= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } + @Override public String comment() { return "kill rdx"; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java new file mode 100644 index 00000000..15e95374 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class FltX86 extends ConstantNode implements MachNode, RIPRelSize{ + private byte _encSize; + final String _ext; + FltX86( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { return "ld8"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new FltX86(this,_ext); } + + @Override public void encoding( Encoding enc ) { + if( _ext!=null ) throw Utils.TODO(); + // Load the constant into a FPR + // movsd xmm, [rip + 0] + // F2 0F 10 /r MOVSD xmm1, m64 + _encSize = 1/*Fop*/+0/*REX*/+2/*op*/+1/*MODRM*/+4/*offset*/; + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + _encSize += x86_64_v2.rexF(dst, 0, 0, false, enc); + enc.add1(0x0F).add1(0x10); + // hard-code rip here + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x05)); + enc.add4(0); + enc.largeConstant(this,_con,_encSize-4,2/*ELF encoding PC32*/); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { + // TODO: 1-byte RIP offset? + return (byte)_encSize; + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart+opLen-4,delta-opLen); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java new file mode 100644 index 00000000..1724109a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java @@ -0,0 +1,35 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.util.SB; + +public class FunX86 extends FunNode implements MachNode { + FunX86( FunNode fun ) { super(fun); } + @Override public void postSelect(CodeGen code) { + super.postSelect(code); + _maxArgSlot = code._mach.maxArgSlot(sig(),code._callingConv); + // One more for RPC pre-pushed; X86 special + _maxArgSlot++; + } + + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + // Alignment, but off by RPC + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8+8) & -16)-8; + } + + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + // opcode: 0x83, addi rsp with immediate 8 + enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b101, x86_64_v2.RSP) ); + if( x86_64_v2.imm8(sz) ) enc.add1(sz); + else enc.add4(sz); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java new file mode 100644 index 00000000..9f3d7905 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class I2f8X86 extends MachConcreteNode implements MachNode { + I2f8X86(Node i2f8 ) { super(i2f8); } + @Override public String op() { return "cvtf"; } + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + + @Override public void encoding( Encoding enc ) { + // F2 0F 2A /r CVTSI2SD xmm1, r32/m32 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = enc.reg(in(1)); + + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, true, enc); + + enc.add1(0x0F).add1(0x2A); + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java new file mode 100644 index 00000000..5e406911 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; + +public abstract class ImmX86 extends MachConcreteNode implements MachNode { + final int _imm; + ImmX86( Node add, int imm ) { + super(add); + _inputs.pop(); // Pop constant input off + _imm = imm; + } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + + abstract int opcode(); + abstract int mod(); + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + // opcode; 0x81 or 0x83; 0x69 or 0x6B + enc.add1( opcode() + (x86_64_v2.imm8(_imm) ? 2 : 0) ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + + // General form: "addi dst += #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java new file mode 100644 index 00000000..1973ea4e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java @@ -0,0 +1,79 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Integer constants +public class IntX86 extends ConstantNode implements MachNode { + final String _ext; + IntX86( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { + return _con == Type.NIL || _con == TypeInteger.ZERO ? "xor" : "ldi"; + } + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new IntX86(this,_ext); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Zero-set uses XOR kills flags + @Override public RegMask killmap() { + return _con == Type.NIL || _con == TypeInteger.ZERO ? x86_64_v2.FLAGS_MASK : null; + } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this); + // Short form for zero + if( _con==Type.NIL || _con==TypeInteger.ZERO ) { + // XOR dst,dst. Can skip REX is dst is low 8, makes this a 32b + // xor, which will also zero the high bits. + if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); + enc.add1(0x33); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); + return; + } + + if( _ext!=null ) throw Utils.TODO(); + // Simply move the constant into a GPR + // Conditional encoding based on 64 or 32 bits + //REX.W + C7 /0 id MOV r/m64, imm32 + + long imm = ((TypeInteger)_con).value(); + if (Integer.MIN_VALUE <= imm && imm < 0) { + // We need sign extension, so use imm32 into 64 bit register + // REX.W + C7 /0 id MOV r/m64, imm32 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0xC7); // 32 bits encoding + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x00, dst)); + enc.add4((int)imm); + } else if (0 <= imm && imm <= 0xFFFFFFFFL) { + // We want zero extension into 64 bit register + // so move 32 bit into 32 bit register which zeros + // the upper bits in the 64 bit register. + // B8+ rd id MOV r32, imm32 + if (dst >= 8) enc.add1(0x41); + enc.add1(0xB8 + (dst & 0x07)); + enc.add4((int)imm); + } else { + // Just write the full 64 bit constant + // REX.W + B8+ rd io MOV r64, imm64 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0xB8 + (dst & 0x07)); + enc.add8(imm); // 64 bits encoding + } + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + if( _con == Type.NIL || _con == TypeInteger.ZERO ) + sb.p(reg).p(",").p(reg); + else + _con.print(sb.p(reg).p(" = #")); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java new file mode 100644 index 00000000..de27aa5c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java @@ -0,0 +1,81 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Jump on flags, uses flags +public class JmpX86 extends IfNode implements MachNode, RIPRelSize { + String _bop; + JmpX86( IfNode iff, String bop ) { + super(iff); + _bop = bop; + } + @Override public String op() { return "j"+_bop; } + @Override public String label() { return op(); } + @Override public void postSelect(CodeGen code) { + Node set = in(1); + if( set==null ) return; // Never-node cutout + Node cmp = set.in(1); + // Bypass an expected Set and just reference the cmp directly + if( set instanceof SetX86 setx && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) { + _inputs.set( 1, cmp ); + _bop = setx._bop; + } else + throw Utils.TODO(); + } + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } + @Override public RegMask outregmap() { return null; } + @Override public void negate() { _bop = negate(_bop); } + + @Override public void encoding( Encoding enc ) { + if( in(1)!=null ) { + int op = x86_64_v2.jumpop(_bop); + enc.add1(op-16).add1(0); // Short form jump + } else { + if( _bop=="!=" ) return; // Inverted, no code + enc.add1(0xEB).add1(0); // Never-node + } + enc.jump(this,cproj(0)); + } + + // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding + @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code + return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6); + } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch + byte[] bits = enc.bits(); + if( opLen==2 ) { + assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; + delta -= 2; // Offset from opcode END + assert (byte)delta==delta; + bits[opStart+1] = (byte)delta; + } else { + assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; + delta -= 6; // Offset from opcode END + bits[opStart] = 0x0F; + bits[opStart+1] = (byte)(in(1)==null ? 0xE9 : x86_64_v2.jumpop(_bop)); + enc.patch4(opStart+2,delta); + } + } + + @Override public void asm(CodeGen code, SB sb) { + if( in(1)!=null ) { // Never-node + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(src).p(" "); + } else if( _bop=="!=" ) { + sb.p("never"); + return; + } + CFGNode prj = cproj(0).uctrlSkipEmpty(); // 0 is True is jump target + if( !prj.blockHead() ) prj = prj.cfg0(); + sb.p(label(prj)); + } + + @Override public String comment() { return "L"+cproj(1)._nid; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java new file mode 100644 index 00000000..2cd6018f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java @@ -0,0 +1,55 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class LeaX86 extends MachConcreteNode implements MachNode { + final int _scale; + final int _offset; + LeaX86( Node add, Node base, Node idx, int scale, int offset ) { + super(add); + assert scale==0 || scale==1 || scale==2 || scale==3; + _inputs.pop(); + _inputs.pop(); + _inputs.push(base); + _inputs.push(idx); + _scale = scale; + _offset = offset; + } + + @Override public String op() { return "lea"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + @Override public void encoding( Encoding enc ) { + // REX.W + 8D /r LEA r64,m + short dst = enc.reg(this); + short ptr = enc.reg(in(1)); + short idx = enc.reg(in(2)); + // ptr is null + // just do: [(index * s) + disp32] + if( ptr == -1 ) { + enc.add1(x86_64_v2.rex(dst, 0, idx)); + enc.add1(0x8D); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x04)); + enc.add1(x86_64_v2.sib(_scale, idx, x86_64_v2.RBP)); + enc.add4(_offset); + return; + } + + enc.add1(x86_64_v2.rex(dst, ptr, idx)); + enc.add1(0x8D); // opcode + // rsp is hard-coded here(0x04) + x86_64_v2.indirectAdr(_scale, idx, ptr, _offset, dst, enc); + } + + // General form: "lea dst = base + 4*idx + 12" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = "); + if( in(1) != null ) + sb.p(code.reg(in(1))).p(" + "); + sb.p(code.reg(in(2))).p("<<").p(_scale); + if( _offset!=0 ) sb.p(" + #").p(_offset); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java new file mode 100644 index 00000000..c55002c1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java @@ -0,0 +1,57 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.LoadNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class LoadX86 extends MemOpX86 { + LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) { + super(ld,ld, base, idx, off, scale, 0); + } + @Override public String op() { return "ld"+_sz; } + @Override public RegMask outregmap() { return _con instanceof TypeFloat ? x86_64_v2.XMASK : x86_64_v2.WMASK; } + @Override public void encoding( Encoding enc ) { + // REX.W + 8B /r MOV r64, r/m64 + // Zero extension for u8, u16 and u32 but sign extension i8, i16, i32 + // Use movsx and movzx + short dst = enc.reg(this ); + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + enc(enc, _con, dst, ptr, idx, _off, _scale); + } + + static void enc( Encoding enc, Type decl, short dst, short ptr, short idx, int off, int scale ) { + if( decl == TypeFloat.F32) enc.add1(0xF3); + if( decl == TypeFloat.F64) enc.add1(0xF2); + + if( decl.isa(TypeFloat.F64) ) + dst -= (short)x86_64_v2.XMM_OFFSET; + + x86_64_v2.rexF(dst, ptr, idx, decl != TypeInteger.U32 && decl != TypeFloat.F32 && decl != TypeFloat.F64, enc); + + if( false ) ; + else if( decl == TypeFloat.F32 ) enc.add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32 + else if( decl == TypeFloat.F64 ) enc.add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64 + else if( decl.isa(TypeInteger.I8)) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8 + else if( decl == TypeInteger.I16 ) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16 + else if( decl == TypeInteger.I32 ) enc.add1(0x63); // sign extend: REX.W + 63 /r MOVSXD r64, r/m32 + else if( decl == TypeInteger.U8 ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8 + else if( decl == TypeInteger.U16 ) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16 + // Covers U32, I64/BOT, TMP + else if( decl.log_size()>=2 ) enc.add1(0x8B); // zero extend: 8B /r MOV r32, r/m32 + else throw Utils.TODO(); + + // includes modrm internally + x86_64_v2.indirectAdr(scale, idx, ptr, off, dst, enc); + } + + + // General form: "ldN dst,[base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(","); + asm_address(code,sb); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java new file mode 100644 index 00000000..bd56d022 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class MemAddX86 extends MemOpX86 { + MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { + super(st, st, base, idx, off, scale, imm, val ); + } + @Override public String op() { + return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + @Override public void encoding( Encoding enc ) { + // add something to memory + // REX.W + 01 /r | REX.W + 81 /0 id + // ADD [mem], imm32/reg + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + short src = enc.reg(val()); + + enc.add1(x86_64_v2.rex(src, ptr, idx)); + // opcode + enc.add1( src == -1 ? (_imm==1 ? 0xFF : 0x81): 0x01); + + // includes modrm + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc); + if( src == -1 && _imm!=1 ) enc.add4(_imm); + } + // General form: "add [base + idx<<2 + 12] += src" + @Override public void asm(CodeGen code, SB sb) { + asm_address(code,sb); + if( val()==null ) { + if( _imm != 1 && _imm != -1 ) sb.p(" += #").p(_imm); + } else sb.p(" += ").p(code.reg(val())); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java new file mode 100644 index 00000000..6069c07b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java @@ -0,0 +1,89 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.lang.StringBuilder; +import java.util.BitSet; + +// Generic X86 memory operand base. +// inputs: +// ctrl - will appear for possibly RCE (i.e. array ops) +// mem - memory dependence edge +// base - Basic object pointer +// idx/null - Scaled index offset, or null if none +// val/null - Value to store, as part of an op-to-mem or op-from-mem. Null for loads, or if an immediate is being used +// Constants: +// offset - offset added to base. Can be zero. +// scale - scale on index; only 0,1,2,4,8 allowed, 0 is only when index is null +// imm - immediate value to store or op-to-mem, and only when val is null +public abstract class MemOpX86 extends MemOpNode implements MachNode { + final int _off; // Limit 32 bits + final int _scale; // Limit 0,1,2,3 + final int _imm; // Limit 32 bits + final char _sz; // Handy print name + MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) { + super(op,mop); + assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); + assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3); + + // Copy memory parts from e.g. the LoadNode over the opcode, e.g. an Add + if( op != mop ) { + _inputs.set(0,mop.in(0)); // Control from mem op + _inputs.set(1,mop.in(1)); // Memory from mem op + _inputs.set(2,base); // Base handed in + } + + assert ptr() == base; + _inputs.setX(3,idx); + _off = off; + _scale = scale; + _imm = imm; + int size = mop instanceof StoreNode st ? st.storeSize() : 1<<_con.log_size(); + _sz = (char)('0'+size); + } + + // Store-based flavors have a value edge + MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm, Node val ) { + this(op,mop,base,idx,off,scale,imm); + _inputs.setX(4,val); + } + + Node idx() { return in(3); } + Node val() { return in(4); } // Only for stores, including op-to-memory + + @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { + return sb.append(".").append(_name); + } + + @Override public String label() { return op(); } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + if( i==1 ) return null; // Memory + if( i==2 ) return x86_64_v2.RMASK; // base in GPR + if( i==3 ) return x86_64_v2.RMASK; // index in GPR + if( i==4 ) return _sz >= 2 + ? x86_64_v2.MEM_MASK // value in GPR or XMM + : x86_64_v2.RMASK; // Bytes and shorts in GPR only + return null; // Anti-dependence + } + + // "[base + idx<<2 + 12]" + SB asm_address(CodeGen code, SB sb) { + sb.p("[").p(code.reg(ptr())); + if( idx() != null ) { + sb.p("+").p(code.reg(idx())); + if( _scale != 0 ) + sb.p("*").p((1<<_scale)); + } + if( _off != 0 ) + sb.p("+").p(_off); + return sb.p("]"); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java new file mode 100644 index 00000000..7932c893 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java @@ -0,0 +1,32 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class MulFX86 extends MachConcreteNode implements MachNode { + MulFX86( Node mulf) { super(mulf); } + @Override public String op() { return "mulf"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + @Override public int twoAddress() { return 1; } + @Override public boolean commutes() { return true; } + + @Override public void encoding( Encoding enc ) { + // F2 0F 59 /r MULSD xmm1,xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x59).add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + + // General form: "mulf dst *= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" *= ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java new file mode 100644 index 00000000..1e9e708f --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class MulIX86 extends MachConcreteNode implements MachNode { + final int _imm; + MulIX86( Node add, int imm ) { + super(add); + _inputs.pop(); // Pop the constant input + _imm = imm; // Record the constant + } + @Override public String op() { return "muli"; } + @Override public String glabel() { return "*"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Rare 3-address form + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(this ); // Also src1 + short src = enc.reg(in(1)); + enc.add1(x86_64_v2.rex(dst, src, 0)); + // opcode; 0x69 or 0x6B + enc.add1( 0x69 + (x86_64_v2.imm8(_imm) ? 2 : 0) ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src) ); + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + // General form: "addi dst += #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java new file mode 100644 index 00000000..4d2a11ed --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class MulX86 extends MachConcreteNode implements MachNode { + MulX86( Node mul ) { super(mul); } + @Override public String op() { return "mul"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } + @Override public boolean commutes() { return true; } + + @Override public void encoding( Encoding enc ) { + // REX.W + 0F AF /r IMUL r64, r/m64 + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(dst, src, 0)); + enc.add1(0x0F); // opcode + enc.add1(0xAF); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + + // General form: "mul dst *= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java new file mode 100644 index 00000000..0c2baced --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class NegX86 extends MachConcreteNode implements MachNode { + NegX86(MinusNode not) { super(not); } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + @Override public int twoAddress() { return 1; } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0xF7); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 3, dst)); + } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java new file mode 100644 index 00000000..40a38809 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.SB; + +public class NewX86 extends NewNode implements MachNode { + // A pre-zeroed chunk of memory. + NewX86( NewNode nnn ) { super(nnn); } + @Override public void encoding( Encoding enc ) { + enc.external(this,"calloc"); + // ldi rcx,#1 // number of elements to calloc + enc.add1(0xB8 + _arg2Reg).add4(1); + // E8 cd CALL rel32; + enc.add1(0xE8); + enc.add4(0); // offset + } + // General form: "alloc #bytes PC" + @Override public void asm(CodeGen code, SB sb) { + sb.p("\n"); // Multi-op trigger + sb.p("ldi ").p(code._mach.regs()[_arg2Reg]).p(" = #1\n"); + sb.p("call #calloc"); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java new file mode 100644 index 00000000..d9056be9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class NotX86 extends MachConcreteNode implements MachNode { + NotX86(NotNode not) { super(not); } + @Override public String op() { return "not"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + + @Override public void encoding( Encoding enc ) { + assert !(in(1) instanceof NotNode); // Cleared out by peeps + assert !(in(1) instanceof BoolNode); // Cleared out by peeps + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + + // Pre-zero using XOR dst,dst; since zero'd will not have a + // byte-dependency from the setz. Can skip REX is dst is low 8, makes + // this a 32b xor, which will also zero the high bits. + if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); + enc.add1(0x33); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); + + // test rdi,rdi + enc.add1(x86_64_v2.rex(src, src, 0)); + enc.add1(0x85); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src, src)); + + // setz (sete dil) + enc.add1(x86_64_v2.rex(dst, 0, 0)); + enc.add1(0x0F); + enc.add1(0x94); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, 0)); + } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java new file mode 100644 index 00000000..15f4824d --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class OrIX86 extends ImmX86 { + OrIX86( Node add, int imm ) { super(add,imm); } + @Override public String op() { return "ori"; } + @Override public String glabel() { return "|"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 1; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java new file mode 100644 index 00000000..bc8f0ea8 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class OrX86 extends RegX86 { + OrX86( Node add ) { super(add); } + @Override public String op() { return "or"; } + @Override public String glabel() { return "|"; } + @Override int opcode() { return 0x0B; } + @Override public boolean commutes() { return true; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java new file mode 100644 index 00000000..5cfdd804 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java @@ -0,0 +1,17 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ParmNode; + +public class ParmX86 extends ParmNode implements MachNode { + final RegMask _rmask; + ParmX86( ParmNode parm ) { + super(parm); + _rmask = x86_64_v2.callInMask(fun().sig(),_idx,1/*RPC*/); + } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return _rmask; } + @Override public void encoding( Encoding enc ) { } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java new file mode 100644 index 00000000..046c7b4c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java @@ -0,0 +1,15 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ProjNode; + +public class ProjX86 extends ProjNode implements MachNode { + ProjX86( ProjNode p ) { super(p); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { + return ((MachNode)in(0)).outregmap(_idx); + } + @Override public void encoding( Encoding enc ) {} +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java new file mode 100644 index 00000000..5a475efe --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java @@ -0,0 +1,27 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public abstract class RegX86 extends MachConcreteNode { + RegX86( Node add ) { super(add); } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + @Override public int twoAddress() { return 1; } + abstract int opcode(); + @Override public void encoding( Encoding enc ) { + // REX.W + 01 /r + short dst = enc.reg(in(1)); // src1 + short src = enc.reg(in(2)); // src2 + + enc.add1(x86_64_v2.rex(dst, src, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + // General form: "add dst += src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" ").p(glabel()).p("= ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java new file mode 100644 index 00000000..a35d43a2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java @@ -0,0 +1,19 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; + +public class RetX86 extends ReturnNode implements MachNode { + RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); } + @Override public void encoding( Encoding enc ) { + int sz = fun()._frameAdjust; + if( sz != 0 ) { + enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b000, x86_64_v2.RSP) ); + if( x86_64_v2.imm8(sz) ) enc.add1(sz); + else enc.add4(sz); + } + enc.add1(0xC3); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java new file mode 100644 index 00000000..95e0c336 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.SB; + +// Arithmetic Right Shift +public class SarIX86 extends MachConcreteNode implements MachNode { + final int _imm; + SarIX86( Node sari, int imm ) { super(sari); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; } + @Override public String op() { return "sari"; } + @Override public String glabel() { return ">>"; } + int opcode() { return 0xC1; } + int mod() { return 7; } + + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } + + @Override public void encoding(Encoding enc) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1( opcode()); + + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java new file mode 100644 index 00000000..4cc8b558 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java @@ -0,0 +1,24 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class SarX86 extends RegX86 { + SarX86( Node add ) { super(add); } + @Override public String op() { return "sar"; } + @Override public String glabel() { return ">>"; } + @Override public RegMask regmap(int i) { + if (i == 1) return x86_64_v2.WMASK; + if (i == 2) return x86_64_v2.RCX_MASK; + throw Utils.TODO(); + } + @Override int opcode() { return 0xD3; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java new file mode 100644 index 00000000..177e35ab --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java @@ -0,0 +1,55 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Corresponds to the x86 instruction "sete && setne". +// Use result of comparison without jump. +public class SetX86 extends MachConcreteNode implements MachNode { + final String _bop; // One of <,<=,== + final boolean _unsigned; + // Constructor expects input is an X86 and not an Ideal node. + SetX86( Node cmp, String bop, boolean unsigned ) { + super(cmp); + _inputs.setLen(1); // Pop the cmp inputs + // Replace with the matched cmp + _inputs.push(cmp); + _bop = bop; + _unsigned = unsigned; + } + @Override public String op() { return "set"+_bop; } + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + @Override public void encoding( Encoding enc ) { + // REX + 0F 94 + short dst = enc.reg(this ); + + // Optional rex, for dst + if( dst >= 4 ) enc.add1(x86_64_v2.rex(0, dst, 0, false)); + enc.add1(0x0F); // opcode + enc.add1(switch (_bop) { + case "==" -> 0x94; // SETE + case "<" -> _unsigned ? 0x92 : 0x9C; // SETB /SETL + case "<=" -> _unsigned ? 0x96 : 0x9E; // SETBE/SETLE + case ">=" -> _unsigned ? 0x93 : 0x9D; // SETAE/SETGE + case ">" -> _unsigned ? 0x97 : 0x9F; // SETA /SETG + default -> throw Utils.TODO(); + }); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst)); + + // low 8 bites are set, now zero extend for next instruction + if( dst >= 4 ) enc.add1(x86_64_v2.rex(dst, dst, 0, false)); + enc.add1(0x0F); // opcode + enc.add1(0xB6); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)); + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(" = ").p(src); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java new file mode 100644 index 00000000..f7b2f28a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.SB; + +public class ShlIX86 extends MachConcreteNode implements MachNode { + final int _imm; + ShlIX86( Node shli, int imm ) { + super(shli); + assert x86_64_v2.imm8(imm); + _inputs.pop(); // Pop constant input off + _imm = imm; + } + public int opcode() { return 0xC1; } + public int mod() { return 4; } + + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } + + @Override public String op() { return "shli"; } + @Override public String glabel() { return "<<"; } + @Override public void encoding(Encoding enc) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1( opcode()); + + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java new file mode 100644 index 00000000..5fee72d9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java @@ -0,0 +1,24 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class ShlX86 extends RegX86 { + ShlX86( Node add ) { super(add); } + @Override public String op() { return "shl"; } + @Override public String glabel() { return "<<"; } + @Override public RegMask regmap(int i) { + if (i == 1) return x86_64_v2.WMASK; + if (i == 2) return x86_64_v2.RCX_MASK; + throw Utils.TODO(); + } + @Override int opcode() { return 0xD3; } + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 4, dst)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java new file mode 100644 index 00000000..7e0378b1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.SB; + +public class ShrIX86 extends MachConcreteNode implements MachNode { + final int _imm; + ShrIX86( Node shri, int imm ) { super(shri); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; } + @Override public String op() { return "shri"; } + @Override public String glabel() { return ">>>"; } + int opcode() { return 0xC1; } + int mod() { return 5; } + + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } + + @Override public void encoding(Encoding enc) { + short dst = enc.reg(this); // Also src1 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1( opcode()); + + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); + + // immediate(4 bytes) 32 bits or (1 byte)8 bits + if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); + else enc.add4(_imm); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java new file mode 100644 index 00000000..67dcf055 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java @@ -0,0 +1,24 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.util.Utils; + +public class ShrX86 extends RegX86 { + ShrX86( Node add ) { super(add); } + @Override public String op() { return "shr"; } + @Override public String glabel() { return ">>>"; } + @Override public RegMask regmap(int i) { + if (i == 1) return x86_64_v2.WMASK; + if (i == 2) return x86_64_v2.RCX_MASK; + throw Utils.TODO(); + } + @Override int opcode() { return 0xD3; } + @Override public final void encoding( Encoding enc ) { + short dst = enc.reg(this ); // src1 + short src = enc.reg(in(2)); // src2 + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(opcode()); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 5, dst)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java new file mode 100644 index 00000000..bc3d421d --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java @@ -0,0 +1,100 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.SplitNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class SplitX86 extends SplitNode { + SplitX86( LRG lrg, String kind, byte round ) { super(lrg,kind,round, new Node[2]); } + @Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; } + @Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; } + + // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm, + // flags->flags. + @Override public void encoding( Encoding enc ) { + // REX.W + 8B /r MOV r64, r/m64 + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + if( dst == x86_64_v2.FLAGS ) { + // mov reg, flags + // push rcx + // popf (Pop the top of the stack into the FLAGS register) + // 50+rd PUSH r64 + x86_64_v2.rexF(0,src,0,false,enc); + enc.add1(0x50 + src); + // popf + enc.add1(0x9D); + return; + } + if( src == x86_64_v2.FLAGS ) { + // mov flags, reg + // pushf; pop reg + enc.add1(0x9C); + // 58+ rd POP r64 + x86_64_v2.rexF(0,dst,0,false, enc); + enc.add1(0x58 + dst); + return; + } + + // Flag for being either XMM or stack + boolean dstX = dst >= x86_64_v2.XMM_OFFSET; + boolean srcX = src >= x86_64_v2.XMM_OFFSET; + + // Stack spills + if( dst >= x86_64_v2.MAX_REG ) { + int off = fun(enc).computeStackOffset(enc._code,dst); + if( src >= x86_64_v2.MAX_REG ) { + // Rare stack-stack move. push [RSP+soff]; pop [RSP+doff] + int soff = fun(enc).computeStackOffset(enc._code,src); + enc.add1(0xFF); + x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, soff, 6, enc); + enc.add1(0x8F); + x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, off, 0, enc); + return; + } + StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0); + return; + } + if( src >= x86_64_v2.MAX_REG ) { + int off = fun(enc).computeStackOffset(enc._code,src); + LoadX86.enc(enc, dstX ? TypeFloat.F64 : TypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0); + return; + } + + // reg-reg move. Adjust numbering for GPR vs FPR reg set + if( dstX ) dst -= (short) x86_64_v2.XMM_OFFSET; + if( srcX ) src -= (short) x86_64_v2.XMM_OFFSET; + + // 0x66 if moving between register classes + if( dstX ^ srcX ) enc.add1(0x66); + if( !dstX && srcX ) + { short tmp=src; src=dst; dst=tmp; } + enc.add1(x86_64_v2.rex(dst, src, 0)); + + // pick opcode based on regs + if( !dstX && !srcX ) { + // reg->reg (MOV r64, r/m64) + enc.add1(0x8B); + } else if( dstX && srcX ) { + // xmm->xmm (NP 0F 28 /r MOVAPS xmm1, xmm2/m128) + enc.add1(0x0F); + enc.add1(0x28); + } else if( dstX && !srcX ) { + // reg->xmm (66 REX.W 0F 6E /r MOVQ xmm, r/m64) + enc.add1(0x0F); + enc.add1(0x6E); + } else if( !dstX && srcX ) { + // xmm->reg(66 0F 7E /r MOVQ r/m64, xmm) + enc.add1(0x0F); + enc.add1(0x7E); + } + + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java new file mode 100644 index 00000000..faedeb8b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java @@ -0,0 +1,85 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.StoreNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.util.SB; +import java.util.BitSet; + +public class StoreX86 extends MemOpX86 { + StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { + super(st,st, base, idx, off, scale, imm, val); + } + @Override public String op() { return "st"+_sz; } + @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { + Node val = val(); + sb.append(".").append(_name).append("="); + if( val==null ) sb.append(_imm); + else val._print0(sb,visited); + return sb.append(";"); + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + @Override public void encoding( Encoding enc ) { + // REX.W + C7 /0 id MOV r/m64, imm32 | + // REX.W + 89 /r MOV r/m64, r64 + short ptr = enc.reg(ptr()); + short idx = enc.reg(idx()); + short src = enc.reg(val()); + + if( src == -1 ) { + int log = Integer.numberOfTrailingZeros(_sz-'0'); + x86_64_v2.rexF(-1, ptr, idx, log==3, enc); + switch( log ) { + case 0: enc .add1(0xC6); break; + case 1: enc.add1(0x66).add1(0xC7); break; + case 2: enc .add1(0xC7); break; + case 3: enc .add1(0xC7); break; + } + x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc); + switch( log ) { + case 0: enc.add1(_imm); break; + case 1: enc.add2(_imm); break; + case 2: enc.add4(_imm); break; + case 3: enc.add4(_imm); break; // Limit of a 4 byte immediate + } + } else { + encVal(enc,_con,ptr,idx,src,_off,_scale); + } + } + + // Non-immediate encoding + static void encVal( Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) { + int log = decl.log_size(); + // Float reg being stored + if( src >= x86_64_v2.XMM_OFFSET ) { + src -= (short)x86_64_v2.XMM_OFFSET; + assert log == 2 || log == 3; + enc.add1( log==2 ? 0xF3 : 0xF2 ); + x86_64_v2.rexF(src,ptr,idx,false,enc); + enc.add1(0x0F).add1(0x11); + } else { + // byte stores from sil, dil, bpl, spl need a rex + if( log == 0 && src >= x86_64_v2.RSP ) enc.add1(x86_64_v2.rex(src,ptr,idx,false)); + else x86_64_v2.rexF(src,ptr,idx,log==3,enc); + switch( log ) { + case 0: enc .add1(0x88); break; + case 1: enc.add1(0x66).add1(0x89); break; + case 2: enc .add1(0x89); break; + case 3: enc .add1(0x89); break; + } + } + x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc); + } + + + // General form: "stN [base + idx<<2 + 12],val" + @Override public void asm(CodeGen code, SB sb) { + asm_address(code,sb).p(","); + if( val()==null ) sb.p("#").p(_imm); + else sb.p(code.reg(val())); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java new file mode 100644 index 00000000..b6530a87 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.SB; + +public class SubFX86 extends MachConcreteNode implements MachNode { + SubFX86( Node subf) { super(subf); } + @Override public String op() { return "subf"; } + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + @Override public int twoAddress() { return 1; } + + @Override public void encoding( Encoding enc ) { + // F2 0F 5C /r SUBSD xmm1, xmm2/m64 + short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); + short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); + // Fopcode + enc.add1(0xF2); + // rex prefix must come next (REX.W is not set) + x86_64_v2.rexF(dst, src, 0, false, enc); + + enc.add1(0x0F).add1(0x5C); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); + } + + // General form: "subf dst -= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" -= ").p(code.reg(in(2))); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java new file mode 100644 index 00000000..8da34bf5 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java @@ -0,0 +1,10 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class SubX86 extends RegX86 { + SubX86( Node add ) { super(add); } + @Override public String op() { return "sub"; } + @Override public String glabel() { return "-"; } + @Override int opcode() { return 0x2B; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java new file mode 100644 index 00000000..cbed5996 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java @@ -0,0 +1,55 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +// Function constants +public class TFPX86 extends FunPtrNode implements MachNode, RIPRelSize { + private byte _opLen; + final String _ext; // External name + // Pointer to a Simple function + TFPX86( FunPtrNode fptr ) { super(fptr); _ext = null; } + // Pointer to an Extern "C" function + TFPX86( TypeFunPtr fptr, String ext ) { super(fptr,CodeGen.CODE._start,null); _type = fptr; _ext = ext; } + @Override public String op() { return "ldx"; } + @Override public String label() { return op(); } + @Override public boolean isClone() { return true; } + @Override public TFPX86 copy() { return new TFPX86((TypeFunPtr)_con,_ext); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public void encoding( Encoding enc ) { + if( _ext==null ) enc.relo(this); // Internal patch + else enc.external(this,_ext); // ELF-file external patch + // lea to load function pointer address + // lea rax, [rip+disp32] + short dst = enc.reg(this); + // 0 or 1 for REX depending on the dst. + // Zero/sign extend should be fine, so not wide. + _opLen = (byte)(6+ x86_64_v2.rexF(dst, 0, 0, true, enc)); + enc.add1(0x8D); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)); + enc.add4(0); + } + + @Override public byte encSize(int delta) { return _opLen; } + + // Delta is from opcode start, X86 expects it from opcode end + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart + opLen - 4, delta - opLen); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)); + if( _ext == null ) _type.print(sb.p(" #")); + else sb.p(_ext); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java new file mode 100644 index 00000000..cc8741b3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeMemPtr; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; + +public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{ + private byte _opLen; + final String _ext; + TMPX86( ConstantNode con, String ext ) { super(con); _ext = ext; } + @Override public String op() { return "ldp"; } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public boolean isClone() { return true; } + @Override public Node copy() { return new TMPX86(this,_ext); } + + @Override public void encoding( Encoding enc ) { + if( _ext!=null ) throw Utils.TODO(); + short dst = enc.reg(this); + // 0 or 1 for REX depending on the dst. + _opLen = (byte)(6+ x86_64_v2.rexF(dst, 0, 0, true, enc)); + enc.add1(0x8D).add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)).add4(0); + enc.largeConstant(this,((TypeMemPtr)_con)._obj, _opLen-4, 2/*ELF encoding PC32*/); + } + + // Delta is from opcode start + @Override public byte encSize(int delta) { return _opLen; } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + enc.patch4(opStart + opLen - 4, delta - opLen); + } + + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" = #")); + } + @Override public boolean eq(Node n) { return this==n; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java new file mode 100644 index 00000000..92d2a87d --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java @@ -0,0 +1,54 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.BitSet; + +// unconditional jump +public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize { + UJmpX86() { } + @Override public String op() { return "jmp"; } + @Override public String label() { return op(); } + @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { + return sb.append("jmp "); + } + @Override public RegMask regmap(int i) {return null; } + @Override public RegMask outregmap() { return null; } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + @Override public void encoding( Encoding enc ) { + enc.jump(this,uctrl()); + // Short-form jump + enc.add1(0xEB).add1(0); + //// E9 cd JMP rel32 + //enc.add1(0xE9).add4(0); + } + + // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding + @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 5); } + + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + byte[] bits = enc.bits(); + if( opLen==2 ) { + assert bits[opStart] == (byte)0xEB; + delta -= 2; // Offset from opcode END + assert (byte)delta==delta; + bits[opStart+1] = (byte)delta; + } else { + assert bits[opStart] == (byte)0xEB; + bits[opStart] = (byte)0xE9; // Long form + delta -= 5; // Offset from opcode END + enc.patch4(opStart+1,delta); + } + } + + @Override public void asm(CodeGen code, SB sb) { + CFGNode target = uctrl(); + //assert target.nOuts() > 1; // Should optimize jmp to empty targets + sb.p(label(target)); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java new file mode 100644 index 00000000..2d989a50 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class XorIX86 extends ImmX86 { + XorIX86( Node add, int imm ) { super(add,imm); } + @Override public String op() { return "xori"; } + @Override public String glabel() { return "^"; } + @Override int opcode() { return 0x81; } + @Override int mod() { return 6; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java new file mode 100644 index 00000000..86703903 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.node.Node; + +public class XorX86 extends RegX86 { + XorX86( Node add ) { super(add); } + @Override public String op() { return "xor"; } + @Override public String glabel() { return "^"; } + @Override int opcode() { return 0x33; } + @Override public boolean commutes() { return true; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java new file mode 100644 index 00000000..a1ad17dc --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -0,0 +1,680 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.Utils; + +public class x86_64_v2 extends Machine { + public x86_64_v2( CodeGen code ) {} + // X86-64 V2. Includes e.g. SSE4.2 and POPCNT. + @Override public String name() { return "x86_64_v2"; } + @Override public int defaultOpSize() { return 5; } + + public static final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7; + public static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15; + + public static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23; + public static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31; + public static final int FLAGS = 32; + public static final int MAX_REG = 33; + public static final int RPC = 33; + + // Human-readable name for a register number, e.g. "RAX". + public static final String[] REGS = new String[]{ + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15", + "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" , + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", + "flags", + }; + // Hard crash for bad register number, fix yer bugs! + @Override public String[] regs() { return REGS; } + + public static int XMM_OFFSET = 16; + // General purpose register mask: pointers and ints, not floats + static final long RD_BITS = 0b1111111111111111L; // All the GPRs + static RegMask RMASK = new RegMask(RD_BITS); + // No RSP in the *write* general set. + static final long WR_BITS = 0b1111111111101111L; // All the GPRs minus RSP + static RegMask WMASK = new RegMask(WR_BITS); + // Xmm register mask + static final long FP_BITS = 0b1111111111111111L << XMM0; // All the XMMs + static RegMask XMASK = new RegMask(FP_BITS); + static RegMask FLAGS_MASK = new RegMask(FLAGS); + static RegMask RPC_MASK = new RegMask(RPC); + // Divide mask: exclude RDX:RAX + static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L<<RAX) & ~(1L<<RDX)); + + static final long SPILLS = -(1L << MAX_REG); + static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L ); + + // Load/store mask; both GPR and FPR + static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS); + + public static RegMask RAX_MASK = new RegMask(RAX); + public static RegMask RCX_MASK = new RegMask(RCX); + public static RegMask RDX_MASK = new RegMask(RDX); + public static RegMask RDI_MASK = new RegMask(RDI); + public static RegMask R08_MASK = new RegMask(R08); + public static RegMask R09_MASK = new RegMask(R09); + public static RegMask RSI_MASK = new RegMask(RSI); + + public static RegMask XMM0_MASK = new RegMask(XMM0); + + // Encoding + public static int REX = 0x40; + public static int REX_W = 0x48; + public static int REX_WR = 0x4C; + public static int REX_WRB= 0x4D; + public static int REX_WB = 0x49; + + public enum MOD { + INDIRECT, // [mem] + INDIRECT_disp8, // [mem + 0x12] + INDIRECT_disp32, // [mem + 0x12345678] + DIRECT, // mem + }; + + // opcode included here + // 0F 84 cd JE rel32 + // 0F 85 cd JNE rel32 + // 0F 8F cd JG rel32 + // 0F 8C cd JL rel32 + // 0F 8E cd JLE rel32 + // 0F 8D cd JGE rel32 + static public int jumpop(String op) { + return switch (op) { + case "==" -> 0x84; + case "!=" -> 0x85; + case ">" -> 0x8F; + case "<" -> 0x8C; + case "<=" -> 0x8E; + case ">=" -> 0x8D; + default -> throw new IllegalArgumentException("Too many arguments"); + }; + } + + public static int modrm(MOD mod, int reg, int m_r) { + if( reg == -1 ) reg=0; // Missing reg in this flavor + // combine all the bits + assert 0 <= reg && reg < 16; + assert 0 <= m_r && m_r < 16; + return (mod.ordinal() << 6) | ((reg & 0x07) << 3) | m_r & 0x07; + } + + // 00 000 000 + // same bit-layout as modrm + public static int sib(int scale, int index, int base) { + assert 0 <= base && base < 16; + assert 0 <= index && index < 16; + return (scale << 6) | ((index & 0x07) << 3) | base & 0x07; + } + + // reg1 is reg(R) + // reg2 is r/mem(B) + // reg3 is X(index) + // reg4 is X(base) + + // 0 denotes no direct register + public static int rex(int reg, int ptr, int idx, boolean wide) { + // assuming 64 bit by default so: 0100 1000 + assert -1 <= reg && reg < 16; + assert -1 <= ptr && ptr < 16; + assert -1 <= idx && idx < 16; + + int rex = wide ? REX_W : REX; + if( 8 <= reg ) rex |= 0b00000100; // REX.R + if( 8 <= ptr ) rex |= 0b00000001; // REX.B + if( 8 <= idx ) rex |= 0b00000010; // REX.X + return rex; + } + + public static int rex(int reg, int ptr, int idx) { + return rex(reg, ptr, idx, true); + } + + // rex for floats. Return size (0 or 1) + // don't need to use REX if 0x40. + public static byte rexF(int reg, int ptr, int idx, boolean wide, Encoding enc) { + int rex = rex(reg, ptr, idx, wide); + if (rex == REX) return 0; + enc.add1(rex); + return 1; + } + // Function used for encoding indirect memory addresses + // Does not always generate SIB byte e.g index == -1. + // -1 denotes empty value, not set - note 0 is different from -1 as it can represent rax. + // Looks for best mod locally + public static void indirectAdr( int scale, short index, short base, int offset, int reg, Encoding enc ) { + // Assume indirect + assert 0 <= base && base < 16; + assert -1 <= index && index < 16 && index != RSP; + + MOD mod = MOD.INDIRECT; + // is 1 byte enough or need more? + if( offset != 0 ) + mod = imm8(offset) + ? MOD.INDIRECT_disp8 + : MOD.INDIRECT_disp32; + + if( mod == MOD.INDIRECT && (base == RBP || base == R13) ) { + mod = MOD.INDIRECT_disp8; + } else if( index == -1 && (base == RSP || base == R12) ) { + index = RSP; + } + + if( index == -1 ) { + enc.add1(modrm(mod, reg, base)); + } else { + enc.add1(modrm(mod, reg, x86_64_v2.RSP)); + enc.add1(sib(scale, index, base)); + } + + if( mod == MOD.INDIRECT_disp8 ) { + enc.add1(offset); + } else if( mod == MOD.INDIRECT_disp32 ) { + enc.add4(offset); + } + } + + + // Limit of float args passed in registers + static RegMask[] XMMS8 = new RegMask[]{ + new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), + new RegMask(XMM4), new RegMask(XMM5), new RegMask(XMM6), new RegMask(XMM7), + }; + + // Map from function signature and argument index to register. + // Used to set input registers to CallNodes, and ParmNode outputs. + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot, String conv ) { return callInMask(tfp,idx,maxArgSlot,conv); } + static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { + return callInMask(tfp,idx,maxArgSlot,CodeGen.CODE._callingConv); + } + static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot, String conv ) { + if( idx==0 ) return RPC_MASK; + if( idx==1 ) return null; + if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence + return switch( conv ) { + case "SystemV" -> callSys5 (tfp,idx,maxArgSlot); + case "win64" -> callWin64(tfp,idx,maxArgSlot); + default -> throw Utils.TODO(); + }; + } + + // Maximum stack args used by this signature + @Override public short maxArgSlot( TypeFunPtr tfp ) { + return maxArgSlot(tfp,CodeGen.CODE._callingConv); + } + @Override public short maxArgSlot( TypeFunPtr tfp, String conv ) { + return switch( conv ) { + case "SystemV" -> maxArgSlotSys5 (tfp); + case "win64" -> maxArgSlotWin64(tfp); + default -> throw Utils.TODO(); + }; + } + + // Win64 - max *4* args in registers of all kinds; after that on stack. + // Stack args land in increasing memory order with empty/mirror slots for + // every register. + // + // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) + // + // -- Prior Frame -- + // i6 + // f5 + // i4 + // empty mirror, XMM3= f3 + // empty mirror, R08 = i2 + // empty mirror, XMM1= f1 + // empty mirror, RCX = i0 + // -- Caller Frame; 16b align -- + // RPC + // PAD/ALIGN + // -- Callee Frame; 16b align -- + static RegMask[] WIN64_CALL = new RegMask[] { + RCX_MASK, + RDX_MASK, + R08_MASK, + R09_MASK, + }; + + static RegMask callWin64(TypeFunPtr tfp, int idx, int maxArgSlot ) { + // idx 2,3,4,5 passed in registers, with stack slot mirrors. + // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC). + int sigidx = idx-2; + if( hiddenSelf(tfp) ) { + if( sigidx==0 ) return null; + sigidx--; + } + if( sigidx >= 4 ) + return new RegMask(MAX_REG+maxArgSlot+sigidx); + return tfp.arg(idx-2) instanceof TypeFloat + ? XMMS8 [sigidx] + : WIN64_CALL[sigidx]; + } + static short maxArgSlotWin64(TypeFunPtr tfp) { + return (short)Math.max(4,tfp.nargs() - (hiddenSelf(tfp) ? 1 : 0)); + } + static short maxIncomingArgSlotWin64(TypeFunPtr tfp) { + return (short)Math.max(0,tfp.nargs() - (hiddenSelf(tfp) ? 1 : 0) - 4); + } + + // Sys5: max 6 GPRs and 8 FPRS filled first. Extra args land in increasing + // memory order as needed - no mirror space. + // + // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) + // RDI =i0, RSI =i2, RDX =i4, RCX =i6, R08 =i8, R09 =i10 + // XMM0=f1, XMM1=f3, XMM2=f5, XMM3=f7, XMM4=f9, XMM5=f11, XMM6=f13, XMM7=f15 + // + // -- Prior Frame -- + // f18 + // i18 + // f17 + // i16 + // i14 + // i12 + // -- Caller Frame; 16b align -- + // RPC + // PAD/ALIGN + // -- Callee Frame; 16b align -- + // SystemV(param passing) + static RegMask[] SYS5_CALL = new RegMask[] { + RDI_MASK, + RSI_MASK, + RDX_MASK, + RCX_MASK, + R08_MASK, + R09_MASK, + }; + + static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) { + // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09 + // First 8 floats passed in registers: xmm0-xmm7 + int sigidx = idx-2; + if( hiddenSelf(tfp) ) { + if( sigidx==0 ) return null; + sigidx--; + } + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=hiddenSelf(tfp) ? 1 : 0; i<idx-2; i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; + } + int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0); + return tfp.arg(idx-2) instanceof TypeFloat + ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk) + : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk); + } + static short maxArgSlotSys5(TypeFunPtr tfp) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=hiddenSelf(tfp) ? 1 : 0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; + } + int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0); + return (short)nstk; + } + private static boolean hiddenSelf(TypeFunPtr tfp) { + return tfp.nargs() > 0 && tfp.arg(0) == TypePtr.PTR; + } + + static final long SYSTEM5_CALLER_SAVE = + (1L << RAX) | (1L << RCX) | (1L << RDX) | + (1L << RDI) | (1L << RSI) | + (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11) | + (1L << FLAGS) | // Flags are killed + // All FP regs are killed + FP_BITS; + + static final long WIN64_CALLER_SAVE = + (1L<< RAX) | (1L<< RCX) | (1L<< RDX) | + (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) | + (1L<<FLAGS)| // Flags are killed + // Only XMM0-XMM5 are killed; XMM6-XMM15 are preserved + (1L<<XMM0) | (1L<<XMM1) | (1L<<XMM2) | (1L<<XMM3) | + (1L<<XMM4) | (1L<<XMM5) ; + + @Override public long callerSave() { + return callerSave(CodeGen.CODE._callingConv); + } + @Override public long callerSave(String conv) { + return switch (conv) { + case "SystemV" -> SYSTEM5_CALLER_SAVE; + case "win64" -> WIN64_CALLER_SAVE; + default -> throw new IllegalArgumentException("Unknown calling convention: " + conv); + }; + } + @Override public String cCallingConv(String callingConv) { + return callingConv; + } + @Override public long neverSave() { return 1L<<RSP; } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK; + } + @Override public int rpc() { return RPC; } + + + // Create a split op; any register to any register, including stack slots + @Override public SplitNode split(LRG lrg, String kind, byte round ) { + return new SplitX86(lrg,kind, round); + } + + // Return a MachNode unconditional branch + @Override public CFGNode jump() { + return new UJmpX86(); + } + + // Instruction selection + @Override + public Node instSelect(Node n) { + return switch (n) { + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CProjNode c -> new CProjNode(c); + case CallEndNode cend -> new CallEndMach(cend); + case CallNode call -> call(call); + case CheckCastNode cast -> new CheckCastMach(cast); + case PtrToIntNode ptr -> new PtrToIntMach(ptr); + case GuardNode guard -> new GuardMach(guard); + case ConstantNode con -> con(con); + case DivNode div -> div.mode()==2 ? new DivFX86(div) : new DivX86(div); + case EscapeNode esc -> new EscapeNode(esc); + case FunNode fun -> new FunX86(fun); + case FunPtrNode fptr -> fptr(fptr); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MinusNode neg -> { + if( neg.mode()==2 ) throw Utils.TODO(); + yield new NegX86(neg); + } + case MulNode mul -> mul.mode()==2 ? new MulFX86(mul) : mul(mul); + case NewNode nnn -> new NewX86(nnn); + case NotNode not -> new NotX86(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmX86(parm); + case BulkMemPhiNode phi-> new BulkMemPhiNode(phi); + case MemPhiNode phi -> new MemPhiNode(phi); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read-> new ReadOnlyMach(read); + case ReturnNode ret -> new RetX86(ret, ret.fun()); + case SarNode sar -> sar(sar); + case ShlNode shl -> shl(shl); + case ShrNode shr -> shr(shr); + case StartCUNode start -> new StartCUNode(start); + case StopCUNode stop -> new StopCUNode(stop); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubNode sub -> sub.mode()==2 ? new SubFX86(sub) : sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case XCtrlNode x -> ConstantNode.raw(Type.XCONTROL); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region -> new RegionNode(region); + default -> throw Utils.TODO(); + }; + } + + public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; } + + public static boolean imm32( long imm ) { return (int)imm==imm; } + + // Attempt a full LEA-style break down. + private Node add(AddNode add) { + if( add.mode()==2 ) + return new AddFX86(add); + Node lhs = add.in(1); + Node rhs = add.in(2); + if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && ld.declaredType().log_size() >= 3) + return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, 0, rhs); + // Attempt a full LEA-style break down. + // Returns one of AddX86, AddIX86, LeaX86, or LHS + if( rhs instanceof ConstantNode off2 && off2._con instanceof TypeInteger toff ) { + long imm = toff.value(); + assert imm!=0; // Folded in peeps + if( (int)imm != imm ) // Full 64bit immediate + return new AddX86(add); + // Now imm <= 32bits + if( lhs instanceof AddNode ladd ) + // ((base + (idx << scale)) + off) + return _lea(add, ladd.in(1), ladd.in(2), (int)imm); + if( lhs instanceof ShlNode shift ) + // (idx << scale) + off; no base + return _lea(add, null, shift, (int)imm); + + // lhs + rhs1 + return new AddIX86(add, (int)imm); + } + return _lea(add, lhs, rhs, 0); + } + + + private Node _lea(Node add, Node base, Node idx, int off) { + int scale = 0; + if( base instanceof ShlNode && !(idx instanceof ShlNode) ) + throw Utils.TODO(); // Bug in canonicalization, should on RHS + if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && + shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { + idx = shift.in(1); + scale = ((int) tscale.value()); + } + // (base + idx) + off + return off == 0 && scale == 0 + ? new AddX86(add) + : new LeaX86(add, base, idx, scale, off); + } + + + private Node and(AndNode and) { + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new AndIX86(and, (int)ti.value()); + return new AndX86(and); + } + + private Node call(CallNode call) { + return call.fptr() instanceof FunPtrNode con + ? new CallX86(call, (TypeFunPtr)con._type) + : call.fptr() instanceof ConstantNode con && con._type instanceof TypeFunPtr + ? new CallX86(call, (TypeFunPtr)con._type) + : new CallRX86(call); + } + + // Because X86 flags, a normal ideal Bool is 2 X86 ops: a "cmp" and at "setz". + // Ideal If reading from a setz will skip it and use the "cmp" instead. + private static boolean swap, unsigned; + private Node cmp( BoolNode bool ) { + swap = unsigned = false; + Node cmp = _cmp(bool); + return new SetX86(cmp, swap ? IfNode.swap(bool.op()) : bool.op(), unsigned); + } + + private Node _cmp(BoolNode bool) { + // Float variant + if( bool.isFloat() ) { + unsigned = true; + return new CmpFX86(bool); + } + Node lhs = bool.in(1); + Node rhs = bool.in(2); + + // Since cmp does not record a BOP, SetX/Jmp need to know if the CMP is swapped. + + // Vs memory + if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && rhs._type.isa(ld.declaredType()) ) + return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val, false); + + // Operands swap in the encoding directly, no need for Set/Jmp to swap `bop` + if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld.declaredType()) && + (val!=null || bool.op()=="==" || bool.op()=="!=") ) + return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true ); + + // Vs immediate + if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new CmpIX86(bool, (int)ti.value(), false); + + // Operand swap compare; Set and Jmp need to swap `bop` + if( lhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new CmpIX86(bool, (int)ti.value(), swap=true); + + // x vs y + return new CmpX86(bool); + } + + private Node con( ConstantNode con ) { + if( !con._con.isConstant() ) + return ConstantNode.raw(con); // Default unknown caller inputs + String ext = con instanceof ExternNode ext0 ? ext0._extern : null; + return switch (con._con) { + case TypeInteger ti -> new IntX86(con,ext); + case TypeFloat tf -> new FltX86(con,ext); + case TypeMemPtr tmp -> new TMPX86(con,ext); + case TypeFunPtr tfp -> new TFPX86(tfp,ext); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> t == Type.NIL ? new IntX86(con,null) : ConstantNode.raw(con); + }; + } + + private Node fptr( FunPtrNode con ) { + return new TFPX86(con); + } + + private Node i2f8(ToFloatNode tfn) { + assert tfn.in(1)._type instanceof TypeInteger ti; + return new I2f8X86(tfn); + } + + private Node jmp(IfNode iff) { + // If/Bool combos will match to a Cmp/Set which sets flags. + // Most general arith ops will also set flags, which the Jmp needs directly. + // Loads do not set the flags, and will need an explicit TEST + String op = "!="; + if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(bool.op()) : bool.op(); + else if( iff.in(1)==null ) op = "=="; // Never-node cutout + else iff.setDef(1, new BoolNode.NE(iff.in(1), + ConstantNode.raw(TypeInteger.ZERO), + (byte)(iff.in(1)._type instanceof TypeFloat ? 2 : 1))); + return new JmpX86(iff, op); + } + + private Node ld(LoadNode ld) { + return new LoadX86(address(ld), ld.ptr(), idx, off, scale); + } + + private Node mul(MulNode mul) { + if( mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new MulIX86(mul, (int)ti.value()); + return new MulX86(mul); + } + + private Node or(OrNode or) { + if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new OrIX86(or, (int)ti.value()); + return new OrX86(or); + } + + private Node prj( ProjNode prj ) { + return prj.in(0) instanceof StartNode ? new ProjNode(prj) : new ProjX86(prj); + } + + private Node sar(SarNode sar) { + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value())) + return new SarIX86(sar, (int)(ti.value() & 0xff) ); + return new SarX86(sar); + } + + private Node shl(ShlNode shl) { + if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value())) + return new ShlIX86(shl, (int)(ti.value() & 0xff) ); + return new ShlX86(shl); + } + + private Node shr( ShrNode shr ) { + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()) ) + return new ShrIX86(shr, (int)(ti.value() & 0xff) ); + return new ShrX86(shr); + } + + private Node st( StoreNode st ) { + // Look for "*ptr op= val" + Node op = st.val(); + if( op instanceof AddNode ) { + if( op.in(1) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val); + if( op.in(2) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(1)), val); + } + return new StoreX86(address(st), st.ptr(), idx, off, scale, imm(st.val()), val); + } + private static boolean stld_match(StoreNode st, LoadNode ld ) { + return + ld.in(0) == st.in(0) && + ld.mem() == st.mem() && + ld.ptr() == st.ptr() && + ld.off() == st.off(); + } + + + private Node sub (SubNode sub ){ + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new AddIX86(sub, (int)-ti.value()) + : new SubX86(sub); + } + + private Node xor (XorNode xor){ + if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) + return new XorIX86(xor, (int)ti.value()); + return new XorX86(xor); + } + + // Gather X86 addressing mode bits prior to constructing. This is a + // builder pattern, but saving the bits in a *local* *global* here to keep + // mess contained. + private static int off, scale, imm; + private static Node idx, val; + private <N extends MemOpNode > N address(N mop) { + off = scale = imm = 0; // Reset + idx = val = null; + Node base = mop.ptr(); + // Skip/throw-away a ReadOnly, only used to typecheck + if(base instanceof ReadOnlyNode read) base = read.in(1); + assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived + if(mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) { + off = (int) ti.value(); + assert off == ti.value(); // In 32-bit range + idx = add.in(1); + if(idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && + shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3) { + idx = shift.in(1); + scale = (int) tscale.value(); + } + } else { + if(mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti) { + off = (int) ti.value(); + assert off == ti.value(); // In 32-bit range + } else { + idx = mop.off(); + } + } + return mop; + } + + private int imm( Node xval ) { + assert val == null && imm == 0; + if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti) { + val = null; + imm = (int) ti.value(); + assert imm == ti.value(); // In 32-bit range + } else { + val = xval; + } + return imm; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java new file mode 100644 index 00000000..206983a0 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java @@ -0,0 +1,302 @@ +package com.seaofnodes.simple.print; + +import com.seaofnodes.simple.codegen.CompUnit; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding.Relo; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.*; +import java.util.HashMap; +import java.util.HashSet; + +public abstract class ASMPrinter { + + public static SB print(SB sb, CodeGen code ) { + Encoding enc = code._encoding; + if( enc == null ) return sb.p("No encoding for ").p(code._srcName); + + // instruction address + int iadr = 0; + // Print all functions in order + for( int i=0; i<enc._cfg._len; i++ ) + if( enc._cfg.at(i) instanceof FunNode fun ) + iadr = print(iadr,sb,code,enc,fun,i); + + // constant pools + iadr = (iadr+15)&-16; // pad to 16 + if( enc._cpool.size()>0 ) + iadr = printConstantPool(iadr, sb, enc._cpool,enc._bigCons,true ,"Constant Pool"); + + iadr = (iadr+15)&-16; // pad to 16 + if( enc._sdata.size()>0 ) + iadr = printConstantPool(iadr, sb,enc._sdata,enc._bigCons,false, "Static Data" ); + + return sb; + } + + + static int printConstantPool( int iadr, SB sb, BAOS bits, HashMap<Node,Relo> bigCons, boolean ro, String msg ) { + sb.p("--- ").p(msg).p(" ------").nl(); + // radix sort the big constants by alignment + Ary<Relo>[] raligns = new Ary[5]; + for( Node op : bigCons.keySet() ) { + Relo relo = bigCons.get(op); + // non-constant structs in the r/w data, everything else in r/o data + if( (relo._t instanceof TypeStruct ts && !ts.isConstant()) == ro ) + continue; + int align = relo._t.alignment(); + Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align]; + relos.add(relo); + } + + HashSet<Type> targets = new HashSet<>(); + + // By alignment + int dadr=0; + for( int align = 4; align >= 0; align-- ) { + Ary<Relo> relos = raligns[align]; + if( relos == null ) continue; + for( Relo relo : relos ) { + if( targets.contains(relo._t) ) continue; + targets.add(relo._t); + sb.hex2(iadr+dadr).p(" "); + if( relo._t instanceof TypeStruct ts ) { + int sz = ts.size(); + int log = 1<<align; + sz = (sz + (log -1)) & -log; // Round up final padding + for( int i=0; i<sz; i++ ) + sb.hex1(bits.read1(dadr++)); + } else { + // Simple primitive (e.g. larger int, float) + pN(bits,sb,dadr,align).fix(9-(1<<align),""); + dadr += (1<<align); + } + relo._t.print(sb.p('\t')).nl(); + } + } + return iadr+dadr; + } + + + private static SB pN( BAOS bits, SB sb, int dadr, int log ) { + switch( log ) { + case 0: sb.hex1(bits.read1(dadr)); break; + case 1: sb.hex2(bits.read2(dadr)); break; + case 2: sb.hex4(bits.read4(dadr)); break; + case 3: sb.hex8(bits.read8(dadr)); break; + } + return sb; + } + + + private static int print(int iadr, SB sb, CodeGen code, Encoding enc, FunNode fun, int cfgidx) { + // Function header + sb.nl().p("---"); + if( fun._name != null ) sb.p(fun._name).p(" "); + fun.sig().print(sb); + sb.p("---------------------------").nl(); + iadr = (iadr + 15)&-16; // All function entries padded to 16 align + + if( fun._frameAdjust != 0 ) + iadr = doInst(iadr,sb,code,enc, cfgidx,fun,true,enc!=null); + while( !(enc._cfg.at(cfgidx) instanceof ReturnNode) ) + iadr = doBlock(iadr,sb,code,enc,fun,cfgidx++); + + // Function separator + sb.p("---"); + fun.sig().print(sb); + sb.p("---------------------------").nl(); + return iadr; + } + + static private final int opWidth = 5; + static private final int argWidth = 30; + static int doBlock(int iadr, SB sb, CodeGen code, Encoding enc, FunNode fun, int cfgidx) { + final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2; + CFGNode bb = enc._cfg.at(cfgidx); + if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode) && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode )) + sb.p(label(bb)).p(":").nl(); + if( bb instanceof CallNode ) return iadr; + final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal(); + final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal() || + (code._phase.ordinal() == CodeGen.Phase.RegAlloc.ordinal() && code._regAlloc.done()); + + boolean once=false; + for( Node n : bb.outs() ) { + if( !(n instanceof PhiNode phi) ) continue; + if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones + // Post-RegAlloc phi prints all on one line + if( postAlloc ) { + if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); } + sb.p(phi._label).p(':').p(code.reg(phi)).p(','); + } else { + // Pre-RegAlloc phi prints one line per + sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ").p(code.reg(phi)); + if( phi.getClass() == PhiNode.class ) { + sb.p(" = phi( "); + for( int i=1; i<phi.nIns(); i++ ) + sb.p("N").p(phi.in(i)._nid).p(","); + sb.unchar().p(" )"); + } + sb.nl(); + } + } + if( once ) sb.unchar().nl(); + + // All the non-phis + for( int i=0; i<bb.nOuts(); i++ ) + if( !(bb.out(i) instanceof PhiNode) ) + iadr = doInst(iadr, sb,code, enc, cfgidx, bb.out(i),postAlloc, postEncode ); + + return iadr; + } + + static int doInst( int iadr, SB sb, CodeGen code, Encoding enc, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) { + if( n==null || n instanceof CProjNode ) return iadr; + if( postAlloc && n instanceof CalleeSaveNode ) return iadr; + if( postEncode && n instanceof ProjNode ) return iadr; + if( n instanceof MemMergeNode ) return iadr; + if( n instanceof EscapeNode ) return iadr; + if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders + final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize(); + final int encWidth = dopz*2; + + // All blocks ending in a Region will need to either fall into or jump + // to this block. Until the post-reg-alloc block layout cleanup, we + // need to assume a jump. There's no real hardware op here, yet. + if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) { + if( postEncode ) return iadr; // All jumps inserted already + while( cfgidx < enc._cfg._len-1 ) { + CFGNode next = enc._cfg.at(++cfgidx); + if( next == n ) return iadr; // Fall-through, no branch + if( next.nOuts()>1 ) + break; // Has code in the block, need to jump around + // No code in the block, can fall through it + } + sb.hex2(iadr++).p(" ").fix(encWidth,"??").p(" ").fix(opWidth,"JMP").p(" ").fix(argWidth,label(cfg)).nl(); + return iadr; + } + + // ProjNodes following a multi (e.g. Call or New results), + // get indent slightly and just print their index & node# + if( n instanceof ProjNode proj ) { + if( proj._type instanceof TypeMem ) return iadr; // Nothing for the hidden ones + sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,code.reg(n)).p(" // "); + if( proj._label != null ) sb.p(proj._label); + sb.nl(); + return iadr; + } + + // ADDR ENCODING Op--- dst = src op src // Comment + // 1234 abcdefgh ld4 RAX = [Rbase + off] // Comment + sb.hex2(iadr).p(" "); + + // Encoding + int fatEncoding = 0; + if( enc != null ) { + int size = enc.opLen(n); + if( code._asmLittle ) + for( int i=0; i<Math.min(size,dopz); i++ ) + sb.hex1(enc._bits.buf()[iadr++]); + else { + iadr += Math.min(size,dopz); + for( int i=0; i<Math.min(size,dopz); i++ ) + sb.hex1(enc._bits.buf()[iadr-i-1]); + } + for( int i=size*2; i<encWidth; i++ ) + sb.p(" "); + fatEncoding = size - (encWidth>>1); // Not-printed parts of encoding + } else + sb.fix(encWidth,""); + sb.p(" "); + + // Op; generally "ld4" or "call" + sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()).p(" "); + + // General asm args + String isMultiOp = null; + if( n instanceof MachNode mach ) { + int old = sb.len(); + mach.asm(code,sb); + int len = sb.len(); + isMultiOp = isMultiOp(sb,old,len); + sb.fix(argWidth-(len-old),""); // Pad out + + } else if( !(n._type instanceof TypeMem) ) { + // Room for some inputs + sb.fix(5, n._nid+":" ); + int off = 0; + for( int i=0; i<n.nIns(); i++ ) { + sb.fix(5, n.in(i) == null ? "___" : String.valueOf(n.in(i)._nid)); + off += 5; + if( off >= argWidth ) break; + } + if( off < argWidth ) sb.fix(argWidth-off,""); + } + sb.p(" "); + + // Comment + String comment = n.comment(); + if( comment!=null ) sb.p("// ").p(comment); + + sb.nl(); + + // Printing more op bits than fit + if( isMultiOp != null ) { + // Multiple ops, template style, no RA, no scheduling. Print out + // one-line-per-newline, with encoding bits up front. + int size = enc != null ? enc.opLen(n) : 0; + int off = Math.min(size,dopz); + while( isMultiOp!=null ) { + sb.hex2(iadr).p(" "); + int len = Math.min(size-off,dopz); + for( int i=0; i<len; i++ ) + sb.hex1(enc._bits.buf()[iadr++]); + off += len; + for( int i=len; i<dopz; i++ ) sb.p(" "); + sb.p(" "); + int x = isMultiOp.indexOf('\n'); + if( x== -1 ) { // Last line + sb.p(isMultiOp).nl(); + isMultiOp = null; + } else { + sb.p(isMultiOp.substring(0,x+1)); // Includes the newline + isMultiOp = isMultiOp.substring(x+1); + } + + } + + } else if( fatEncoding > 0 ) { + // Extra bytes past the default encoding width, all put on a line by + // themselves. X86 special for super long encodings + sb.hex2(iadr).p(" "); + for( int i=0; i<fatEncoding; i++ ) + sb.hex1(enc._bits.buf()[iadr++]); + sb.nl(); + } + + + // MultiNodes are immediately followed by projection(s) + if( !(n instanceof CFGNode) && n instanceof MultiNode ) { + for( Node proj : n._outputs ) { + if( proj instanceof ProjNode ) // it could also be an ante-dep + doInst(iadr,sb,code,enc, cfgidx, proj,postAlloc,postEncode); + } + } + + return iadr; + } + + private static String label( CFGNode bb ) { + return (bb instanceof LoopNode ? "LOOP" : "L")+bb._nid; + } + + private static String isMultiOp(SB sb, int old, int len) { + if( old==len || sb.at(old)!='\n' ) return null; + String rest = sb.subString(old+1,len); + sb.setLen(old); + return rest; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java b/chapter25/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java new file mode 100644 index 00000000..ad2df8b6 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java @@ -0,0 +1,288 @@ +package com.seaofnodes.simple.print; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CompUnit; +import com.seaofnodes.simple.node.*; +import java.util.*; +import static com.seaofnodes.simple.util.Utils.TODO; + +/** + * Simple visualizer that outputs GraphViz dot format. + * The dot output must be saved to a file and run manually via dot to generate the SVG output. + * Currently, this is done manually. + */ +public class GraphVisualizer { + + /** + * If set to true we put the control nodes in a separate cluster from + * data nodes. + */ + boolean _separateControlCluster = false; + + public GraphVisualizer(boolean separateControlCluster) { this._separateControlCluster = separateControlCluster; } + public GraphVisualizer() { this(false); } + + public String generateDotOutput(Parser parse) { return generateDotOutput(parse._ref,parse._scope,parse._xScopes); } + public String generateDotOutput(CompUnit cu, Node scope, Stack<ScopeNode> xScopes) { + + // Since the graph has cycles, we need to create a flat list of all the + // nodes in the graph. + Collection<Node> all = findAll(xScopes, cu._stop, scope); + StringBuilder sb = new StringBuilder(); + sb.append("digraph chapter18 {\n"); + sb.append("/*\n"); + sb.append(cu._src); + sb.append("\n*/\n"); + + // To keep the Scopes below the graph and pointing up into the graph we + // need to group the Nodes in a subgraph cluster, and the scopes into a + // different subgraph cluster. THEN we can draw edges between the + // scopes and nodes. If we try to cross subgraph cluster borders while + // still making the subgraphs DOT gets confused. + sb.append("\trankdir=BT;\n"); // Force Nodes before Scopes + + // CNC - turned off Apr/8/2024, gives more flex in the layout and + // removes some of the more ludicrous layout choices. + // Preserve node input order + //sb.append("\tordering=\"in\";\n"); + + // Merge multiple edges hitting the same node. Makes common shared + // nodes much prettier to look at. + sb.append("\tconcentrate=\"true\";\n"); + + // Force nested scopes to order + sb.append("\tcompound=\"true\";\n"); + + // Just the Nodes first, in a cluster no edges + nodes(sb, all); + + // Now the scopes, in a cluster no edges + if( xScopes != null ) + for( ScopeNode xscope : xScopes ) + if( !xscope.isDead() ) + scope( sb, xscope ); + + // Walk the Node edges + nodeEdges(sb, all); + + // Walk the active Scope edges + if( xScopes != null ) + for( ScopeNode xscope : xScopes ) + if( !xscope.isDead() ) + scopeEdges( sb, xscope ); + + sb.append("}\n"); + return sb.toString(); + } + + private void nodesByCluster(StringBuilder sb, boolean doCtrl, Collection<Node> all) { + if (!_separateControlCluster && doCtrl) // all nodes in 1 cluster + return; + // Just the Nodes first, in a cluster no edges + sb.append(doCtrl ? "\tsubgraph cluster_Controls {\n" : "\tsubgraph cluster_Nodes {\n"); // Magic "cluster_" in the subgraph name + for (Node n : all) { + if( n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode || n instanceof XCtrlNode ) + continue; // Do not emit, rolled into MultiNode or Scope cluster already + if( _separateControlCluster && doCtrl && !(n instanceof CFGNode) ) continue; + if( _separateControlCluster && !doCtrl && (n instanceof CFGNode) ) continue; + sb.append("\t\t").append(n.uniqueName()).append(" [ "); + String lab = n.glabel(); + if (n instanceof MultiNode) { + // Make a box with the MultiNode on top, and all the projections on the bottom + sb.append("shape=plaintext label=<\n"); + sb.append("\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\n"); + sb.append("\t\t\t<TR><TD BGCOLOR=\"yellow\">").append(lab).append("</TD></TR>\n"); + sb.append("\t\t\t<TR>"); + boolean doProjTable = false; + n._outputs.sort((x,y) -> x instanceof ProjNode xp && y instanceof ProjNode yp ? (xp._idx - yp._idx) : (x._nid - y._nid)); + for (Node use : n._outputs) { + if (use instanceof ProjNode proj) { + if (!doProjTable) { + doProjTable = true; + sb.append("<TD>").append("\n"); + sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n"); + sb.append("\t\t\t\t<TR>"); + } + sb.append("<TD PORT=\"p").append(proj._idx).append("\">").append(proj.glabel()).append("</TD>"); + } + if (use instanceof CProjNode proj) { + if (!doProjTable) { + doProjTable = true; + sb.append("<TD>").append("\n"); + sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n"); + sb.append("\t\t\t\t<TR>"); + } + sb.append("<TD PORT=\"p").append(proj._idx).append("\" BGCOLOR=\"yellow\">").append(proj.glabel()).append("</TD>"); + } + } + if (doProjTable) { + sb.append("</TR>").append("\n"); + sb.append("\t\t\t\t</TABLE>").append("\n"); + sb.append("\t\t\t</TD>"); + } + sb.append("</TR>\n"); + sb.append("\t\t\t</TABLE>>\n\t\t"); + + } else { + // control nodes have box shape + // other nodes are ellipses, i.e. default shape + if( n instanceof CFGNode ) sb.append("shape=box style=filled fillcolor=yellow "); + else if (n instanceof PhiNode) sb.append("style=filled fillcolor=lightyellow "); + sb.append("label=\"").append(lab).append("\" "); + } + sb.append("];\n"); + } + if (!_separateControlCluster) { + // Force Region & Phis to line up + for (Node n : all) { + if (n instanceof RegionNode region) { + sb.append("\t\t{ rank=same; "); + sb.append(region.uniqueName()).append(";"); + for (Node phi : region._outputs) + if (phi instanceof PhiNode) sb.append(phi.uniqueName()).append(";"); + sb.append("}\n"); + } + } + } + sb.append("\t}\n"); // End Node cluster + } + + private void nodes(StringBuilder sb, Collection<Node> all) { + nodesByCluster(sb, true, all); + nodesByCluster(sb, false, all); + } + + // Build a nested scope display, walking the _prev edge + private void scope( StringBuilder sb, ScopeNode scope ) { + sb.append("\tnode [shape=plaintext];\n"); + int last = scope.nIns(); + int max = scope.depth(); + for( int i = 0; i < max; i++ ) { + int level = max-i-1; + String scopeName = makeScopeName(scope, level); + sb.append("\tsubgraph cluster_").append(scopeName).append(" {\n"); // Magic "cluster_" in the subgraph name + // Special for memory ScopeMinNode + if( level==0 ) { + Node n = scope.mem(); + sb.append("\t\t").append(n.uniqueName()).append(" [label=\"").append(n.glabel()).append("\"];\n"); + } + sb.append("\t\t").append(scopeName).append(" [label=<\n"); + sb.append("\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n"); + // Add the scope level + sb.append("\t\t\t<TR><TD BGCOLOR=\"cyan\">").append(level).append("</TD>"); + int lexStart = scope._kinds.at(level)._lexSize; + for( int j=lexStart; j<last; j++ ) { + var v = scope._vars.at(j); + sb.append("<TD PORT=\"").append(makePortName(scopeName, v._name)).append("\">").append(v._name).append("</TD>"); + } + last = lexStart; + sb.append("</TR>\n"); + sb.append("\t\t\t</TABLE>>];\n"); + } + // Scope clusters nest, so the graphics shows the nested scopes, so + // they are not closed as they are printed; so they just keep nesting. + // We close them all at once here. + sb.append( "\t}\n".repeat( max ) ); // End all Scope clusters + } + + private String makeScopeName(ScopeNode sn, int level) { return sn.uniqueName() + "_" + level; } + private String makePortName(String scopeName, String varName) { return scopeName + "_" + varName; } + + // Walk the node edges + private void nodeEdges(StringBuilder sb, Collection<Node> all) { + // All them edge labels + sb.append("\tedge [ fontname=Helvetica, fontsize=8 ];\n"); + for( Node n : all ) { + // Do not display the Constant->Start edge; + // ProjNodes handled by Multi; + // ScopeNodes are done separately + if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeNode || n instanceof XCtrlNode ) + continue; + for( int i=0; i<n.nIns(); i++ ) { + Node def = n.in(i); + if( n instanceof PhiNode && def instanceof RegionNode ) { + // Draw a dotted use->def edge from Phi to Region. + sb.append('\t').append(n.uniqueName()); + sb.append(" -> "); + sb.append(def.uniqueName()); + sb.append(" [style=dotted taillabel=").append(i).append("];\n"); + } else if( def != null ) { + // Most edges land here use->def + sb.append('\t').append(n.uniqueName()).append(" -> "); + if( def instanceof CProjNode proj ) { + String mname = proj.ctrl().uniqueName(); + sb.append(mname).append(":p").append(proj._idx); + } else if( def instanceof ProjNode proj ) { + String mname = proj.in(0).uniqueName(); + sb.append(mname).append(":p").append(proj._idx); + } else sb.append(def.uniqueName()); + // Number edges, so we can see how they track + sb.append("[taillabel=").append(i); + // The edge from New to ctrl is just for anchoring the New + if ( n instanceof NewNode ) + sb.append(" color=green"); + // control edges are colored red + else if( def instanceof CFGNode ) + sb.append(" color=red"); + else if( def.isMem() ) + sb.append(" color=blue"); + // Backedges do not add a ranking constraint + if( i==2 && (n instanceof PhiNode || n instanceof LoopNode) ) + sb.append(" constraint=false"); + sb.append("];\n"); + } + } + } + } + + // Walk the scope edges + private void scopeEdges( StringBuilder sb, ScopeNode scope ) { + sb.append("\tedge [style=dashed color=cornflowerblue];\n"); + int level=0; + for( var v : scope._vars ) { + Node def = scope.in(v._idx); + while( def instanceof ScopeNode lazy ) + def = lazy.in(v._idx); + if( def==null ) continue; + while( level < scope.depth() && v._idx >= scope._kinds.at(level)._lexSize ) + level++; + String scopeName = makeScopeName(scope, level-1); + sb.append("\t") + .append(scopeName).append(":") + .append('"').append(makePortName(scopeName, v._name)).append('"') // wrap port name with quotes because $ctrl is not valid unquoted + .append(" -> "); + if( def instanceof CProjNode proj ) { + sb.append(def.in(0).uniqueName()).append(":p").append(proj._idx); + } else if( def instanceof ProjNode proj ) { + sb.append(def.in(0).uniqueName()).append(":p").append(proj._idx); + } else sb.append(def.uniqueName()); + sb.append(";\n"); + } + } + + /** + * Finds all nodes in the graph. + */ + public static Collection<Node> findAll(Stack<ScopeNode> ss, Node... ns) { + final HashMap<Integer, Node> all = new HashMap<>(); + for( Node n : ns ) + walk(all, n); + if( ss != null ) + for( Node n : ss ) + walk(all, n); + return all.values(); + } + + /** + * Walk a subgraph and populate distinct nodes in the all list. + */ + private static void walk(HashMap<Integer,Node> all, Node n) { + if(n == null ) return; + if (all.get(n._nid) != null) return; // Been there, done that + all.put(n._nid, n); + for (Node c : n._inputs) + walk(all, c); + for( Node c : n._outputs ) + walk(all, c); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/chapter25/src/main/java/com/seaofnodes/simple/print/IRPrinter.java new file mode 100644 index 00000000..7c0f84f3 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/print/IRPrinter.java @@ -0,0 +1,172 @@ +package com.seaofnodes.simple.print; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Serialize; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.SB; + +import java.util.*; + +public abstract class IRPrinter { + + // Print a node on 1 line, columnar aligned, as: + // NNID NNAME DDEF DDEF [[ UUSE UUSE ]] TYPE + // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt + public static SB printLine( Node n, SB sb ) { + if( n==null ) return sb; + sb.p("%4d %-7.7s ".formatted(n._nid,n.label())); + if( n._inputs==null ) + return sb.p("DEAD\n"); + for( Node def : n._inputs ) + sb.p(def==null ? "____" : "%4d".formatted(def._nid)) + // Lazy Phi indicator + .p(n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " "); + for( int i = n._inputs.size(); i<4; i++ ) + sb.p(" "); + sb.p(" [[ "); + for( Node use : n._outputs ) + sb.p(use==null ? "____ " : "%4d ".formatted(use._nid)); + int lim = 6 - Math.max(n._inputs.size(),4); + for( int i = n._outputs.size(); i<lim; i++ ) + sb.p(" "); + sb.p(" ]] "); + if( n._type!= null ) sb.p(n._type.str()); + return sb.p("\n"); + } + + // Bulk whole program pretty print + public static String prettyPrint( CodeGen code ) { + // Loop-tree for nodeOrder + CFGNode.LoopTree old = code._start._ltree; + if( old==null ) + code._start.buildLoopTree( code._linker, code._stop); + Ary<Node> nodes = Serialize.nodeOrder(code); + if( old == null ) + code._start._ltree = null; + SB sb = new SB(); + Node prior=null; + for( Node n : nodes ) { + if( n instanceof FunNode fun ) { + sb.nl().p("--- "); + fun.sig().print(sb.p(fun._name==null ? "" : fun._name).p(" ")); + sb.p("----------------------\n"); + } + if( n instanceof MultiNode || n instanceof RegionNode || n instanceof CallNode || + (isMultiChild(prior) && !isMultiChild(n)) ) + sb.nl(); + printLine(n,sb); + if( n instanceof ReturnNode ret ) + sb.p("--- ").p(ret._fun._name==null ? "" : ret._fun._name).p(" ----------------------\n"); + prior = n; + } + return sb.toString(); + } + + private static boolean isMultiHead(Node n) { + return n instanceof RegionNode || n instanceof MultiNode; + } + + private static boolean isMultiChild(Node n) { + return n!=null && n.nIns() > 0 && n.in(0) != null && isMultiHead(n.in(0)); + } + + + // ---------------------------------------- + // Another bulk pretty-printer. Makes more effort at basic-block grouping. + public static String prettyPrint(Node node, int depth) { + // Convert just that set to a post-order + Ary<Node> post = new Ary<>(Node.class); + Ary<Node> rets = new Ary<>(Node.class); + var visit = new IdentityHashMap<Node,Integer>(); + assert depth < 5000; + postOrd( node, 0, depth, visit, post, rets); + // Function calls all print independent + while( rets.size()>0 ) + postOrd( rets.pop(), 0, depth, visit, post, rets ); + + + // Reverse the post-order walk + SB sb = new SB(); + for( int i=0; i<post._len; i++ ) { + Node n = post.at(i); + // Split ahead of a Region/MultiNode + if( n instanceof RegionNode || n instanceof MultiNode || + (i>0 && isMultiChild(post.at(i-1)) && !isMultiChild(n)) ) + sb.nl(); + if( n instanceof FunNode fun ) + fun.sig().print(sb.p("--- ").p(fun._name==null ? "" : fun._name).p(" ")).p("----------------------\n"); + printLine( n, sb ); // Print head + if( n instanceof ReturnNode ret ) { + FunNode fun = ret.fun(); + sb.p("--- ").p(fun==null ? "" : fun._name).p("----------------------\n"); + } + } + return sb.toString(); + } + + + // keep the largest PO depth?; probably want *shortest* + private static void postOrd( Node n, int d, int cutoff, IdentityHashMap<Node,Integer> visit, Ary<Node> post, Ary<Node> rets ) { + if( n==null ) return; + if( d >= cutoff ) return; // Too deep + Integer depth = visit.get(n); + if( depth!=null ) return; // Been there, done that + + // Not a multi-child (Phi of a Region or Proj of a MultiNode) + if( !isMultiHead(n) && !isMultiChild(n) ) { + visit.put(n,d); // Visit node N and depth D + for( Node def : n._inputs ) // Children before visit + postOrd(def, d+1, cutoff, visit, post, rets); + post.add(n); // Post-order add + return; + } + + // Multi-child; print the parent and all children together + Node multi = isMultiHead(n) ? n : n.in(0); + // Multi + children are visited all at once + visit.put(multi,d+1); + for( Node out : multi.outs() ) + visit.put(out,d); + + // Order to visit all the children + Node[] outs = multi.outs().asAry(); + Arrays.sort(outs, Comparator.comparingInt( IRPrinter::sortOrder ) ); + + // Do not walk out of Functions, this makes functions stand-alone + // but otherwise unordered + if( !(multi instanceof FunNode fun && fun._folding) ) { + // Visit all children inputs all at once + for( Node out : outs ) { + // A FunNode is both a multi-head AND a multi-child from Start + if( !isMultiHead(out) && out != null ) { + for( Node def : out._inputs ) { + postOrd(def, d+1, cutoff, visit, post, rets); + } + } + } + // Visit multi inputs + for( Node def : multi._inputs ) { + if( multi instanceof CallEndNode && def instanceof ReturnNode ret && !ret._fun._folding ) { + rets.add(ret); // But save the Return for a separate function print + } else { + postOrd(def, d+2, cutoff, visit, post, rets); + } + } + } + + // Post visit the multi and all children + post.add(multi); + for( Node out : outs ) + if( !isMultiHead(out) ) + post.add(out); + } + + static int sortOrder( Node n ) { + if( n instanceof ProjNode proj ) return proj._idx; + if( n instanceof PhiNode phi ) return phi._nid; + if( n == null ) return 0; + return n._nid+1000000; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/JSViewer.java b/chapter25/src/main/java/com/seaofnodes/simple/print/JSViewer.java new file mode 100644 index 00000000..0f34af2e --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/print/JSViewer.java @@ -0,0 +1,404 @@ +package com.seaofnodes.simple.print; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.Stack; +import static com.seaofnodes.simple.codegen.CodeGen.CODE; + +public class JSViewer implements AutoCloseable { + // Display programs in an endless loop + public static void main( String[] args ) throws Exception { + try(var js = new JSViewer()) { + js.run(); + } + } + + static boolean SHOW; + + // WebSocket to the browser for display + static SimpleWebSocket SERVER; + + static int N; // Dot frames + + JSViewer() throws Exception { + // Launch server; handshake + SERVER = new SimpleWebSocket(Paths.get("docs/index.html").toUri(),12345) ; + } + + void run( ) throws Exception { + SERVER.put("!"); + while( true ) { + String src = SERVER.get(); + switch( src ) { + case null: return; + case "null": return; + case "+": break; // Client requests more frames, but we send them all anyway + default: + System.out.println(src); + try { + N=0; + // Use parser scope, xscope when building views + CodeGen code = new CodeGen(src); + SHOW = true; + show(); + // Parse program, generating views at every parse point + code.parse(); + // No longer user parse internal state when building views + code.opto(); + + // Catch and ignore Parser errors + } catch(RuntimeException re) { + System.err.println(re); + } finally { + SHOW = false; + SERVER.put("#"); // Final frame + } + break; + } + } + } + + @Override public void close() throws IOException { + SERVER.close(); + SERVER=null; + } + + public static void show() { if( SERVER!=null && SHOW ) _show(); } + private static void _show() { + // Skip util we at least get the Parse object made + if( CODE._phase==null || CODE._phase.ordinal() < CodeGen.Phase.Parse.ordinal() ) + return; + boolean midParse = CODE._phase == CodeGen.Phase.Parse; + Stack<ScopeNode> xScopes = midParse ? CODE.P._xScopes : null; + + Collection<Node> all = GraphVisualizer.findAll(xScopes, CODE._stop, midParse ? CODE.P._scope: null); + SB sb = new SB(); + sb.p("digraph view_").p(N++).p(" {\n").ii(); + if( midParse ) + sb.i().p("// POS: ").p(CODE.P.pos()).nl(); + + // To keep the Scopes below the graph and pointing up into the graph we + // need to group the Nodes in a subgraph cluster, and the scopes into a + // different subgraph cluster. THEN we can draw edges between the + // scopes and nodes. If we try to cross subgraph cluster borders while + // still making the subgraphs DOT gets confused. + sb.i().p("rankdir=BT;\n"); // Force Nodes before Scopes + + // CNC - turned off Apr/8/2024, gives more flex in the layout and + // removes some of the more ludicrous layout choices. + // Preserve node input order + //sb.append("ordering=\"in\";\n"); + + // Merge multiple edges hitting the same node. Makes common shared + // nodes much prettier to look at. + sb.i().p("concentrate=\"true\";\n"); + + // Force nested scopes to order + sb.i().p("compound=\"true\";\n"); + + // Just the Nodes first, in a cluster no edges + nodes(sb, all); + + // Now the scopes, in a cluster no edges + if( midParse ) + for( ScopeNode xscope : xScopes ) + if( !xscope.isDead() ) + scope( sb, xscope ); + + // Walk the Node edges + nodeEdges(sb, midParse && !xScopes.empty() ? xScopes.peek() : null, all); + + // Walk the active Scope edges + if( midParse ) + for( ScopeNode xscope : xScopes ) + if( !xscope.isDead() ) + scopeEdges( sb, xscope ); + + + sb.p("}\n").di(); + // Tell client another DOT frame + String dot = sb.toString(); + System.out.println(dot); + try { + SERVER.put(dot); + } catch( IOException ioe ) { + try { SERVER.close();} catch( IOException ignored ){} + } + } + + + private static void nodes(SB sb, Collection<Node> all) { + // Just the Nodes first, in a cluster no edges + sb.i().p("subgraph cluster_Nodes {\n").ii(); // Magic "cluster_" in the subgraph name + for( Node n : all ) { + if( n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode ) + continue; // Do not emit, rolled into MultiNode or Scope cluster already + sb.i().p(n.uniqueName()).p(" [ "); + if( n instanceof MultiNode && !(n instanceof StartNode) ) { + // Make a box with the MultiNode on top, and all the projections on the bottom + sb. p("shape=plaintext label=<\n").ii(); + sb.i().p("<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\n"); + // Row over single cell for node label + cell(sb.i().p("<TR>"), n.glabel(), n, null).p("</TR>\n"); + // Row over single cell, for nested table + sb.i().p("<TR><TD>\n").ii(); + sb.i().p("<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").p("\n"); + sb.i().p("<TR>"); + n._outputs.sort((x,y) -> x instanceof ProjNode xp && y instanceof ProjNode yp ? (xp._idx - yp._idx) : ((x==null ? 99999 : x._nid) - (y==null ? 99999 : y._nid))); + boolean empty_row=true; + for( Node use : n._outputs ) { + int idx = idx(n); + if( idx != -1 ) { + cell(sb,use.glabel(),use,"p"+idx); + empty_row=false; + } + } + // At least one cell on row + if( empty_row ) sb.p("<TD></TD>"); + sb. p("</TR>").p("\n"); + sb.i().p("</TABLE>").p("\n").di(); + sb.i().p("</TD></TR>\n"); + sb.i().p("</TABLE>>\n").di(); + sb.i().p("];\n"); + + } else { + // control nodes have box shape + // other nodes are ellipses, i.e. default shape + //else if (n instanceof PhiNode) sb.p("style=filled fillcolor=lightyellow "); + node(sb,n.glabel(),n); + sb.p("];\n"); + } + } + // Force Region & Phis to line up + for (Node n : all) { + if (n instanceof RegionNode region) { + sb.i().p("{ rank=same; "); + sb.p(region.uniqueName()).p(";"); + for (Node phi : region._outputs) + if (phi instanceof PhiNode) sb.p(phi.uniqueName()).p(";"); + sb.p("}\n"); + } + } + sb.di().i().p("}").nl(); // End Node cluster + } + + // Build a nested scope display, walking the _prev edge + private static void scope( SB sb, ScopeNode scope ) { + sb.i().p("node [shape=plaintext];\n"); + int last = scope.nIns(); + int max = scope.depth()+1; // One more than lexsize + for( int i = 0; i < max; i++ ) { + int level = max-i-1; + String scopeName = makeScopeName(scope, level); + sb.i().p("subgraph cluster_").p(scopeName).p(" {\n").ii(); // Magic "cluster_" in the subgraph name + sb.i().p(scopeName).p(" [label=<\n").ii(); + sb.i().p("<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n"); + int lexStart = level==0 ? 0 : scope._kinds.at(level-1)._lexSize; + + // Special for memory ScopeMinNode + Node n = scope.nIns()>1 ? scope.mem() : null; + if( level==0 && n instanceof MemMergeNode merge && n.nIns()>2 ) { + sb.i().p("<TR>"); + for( int m=2; m<n.nIns(); m++ ) + cell(sb,"#"+m,merge.alias(m),"m"+m); + sb.p("</TR>\n"); // End scope level + } + + // Scope variables, empty if none + if( lexStart<last ) { + sb.i().p("<TR>\n"); + for( int j=lexStart; j<last; j++ ) { + if( scope.in(j)==null ) continue; + var v = scope._vars.at(j); + cell(sb.i(),v._name,scope.in(j),makePortName(scopeName, v._name)).nl(); + } + last = lexStart; + sb.i().p("</TR>\n"); + } + sb.i().p("</TABLE>>];\n").di(); + // Scope label + sb.i().p(scopeName).p("A [label=\"").p(level).p("\" shape=doublecircle style=filled fillcolor=aqua fontsize=10 margin=0 width=0.2 ];\n"); + } + // Scope clusters nest, so the graphics shows the nested scopes, so + // they are not closed as they are printed; so they just keep nesting. + // We close them all at once here. + for( int i=0; i<max; i++ ) + sb.di().i().p("}\n"); + } + + // Append a cell, with color + private static SB cell(SB sb, String text, Node n, String port) { + sb.p("<TD"); + String color = color(n); + sb.p(" BGCOLOR=\"").p(color).p("\""); + if( port!=null ) sb.p(" PORT=\"").p(port).p("\""); + return colorcell(sb.p(">"),text,n,dark(color)).p("</TD>"); + } + private static SB node(SB sb, String text, Node n) { + String color = color(n); + sb.p("style=filled fillcolor=").p(color).p(" "); + String shape = switch( n ) { + case FunNode fun -> "shape=box peripheries=2"; + case CFGNode cfg -> "shape=box"; + default -> ""; + }; + sb.p(shape).p(" "); + return colorcell(sb.p("label=<"),text,n,dark(color)).p(">"); + } + + private static String color(Node n) { + return switch( n ) { + case CFGNode cfg -> "yellow"; + case NewNode nnn -> "lightgreen"; + case StructNode s -> "lightgreen"; + default -> typeColor(n._type); + }; + } + private static String typeColor(Type t) { + return t==null ? "white" : switch( t ) { + case TypeMem mem -> "blue"; + case TypeMemPtr tmp -> "green"; + case TypeFunPtr tfp -> "orange"; + case TypeRPC rpc -> "antiquewhite"; + case TypeInteger ti -> "lightblue"; + case TypeFloat tf -> "aqua"; + case Type tn -> tn==Type.NIL ? "lightgray" : "red"; + }; + } + private static final String[] DARKS = new String[]{"blue","green","black"}; + private static boolean dark(String color) { return Utils.find(DARKS,color) != -1; } + + + + // Called with an open div + private static SB colorcell(SB sb, String text, Node n, boolean dark) { + if( dark ) sb.p("<font color=\"white\">"); + sb.p(text); + if( n._type!=null ) { + sb.p("<br /><font point-size=\"10\">"); + n._type.gprint(sb); + sb.p("</font>"); + } + if( dark ) sb.p("</font>"); + return sb; + } + + + private static String makeScopeName(ScopeNode sn, int level) { return sn.uniqueName() + "_" + level; } + private static String makePortName(String scopeName, String varName) { return scopeName + "_" + varName; } + + // Walk the node edges + private static void nodeEdges(SB sb, ScopeNode scope, Collection<Node> all) { + // All them edge labels + String scopeName = scope!=null ? makeScopeName(scope, 0) : null; + sb.i().p("edge [ fontname=Helvetica, fontsize=8 ];\n"); + for( Node n : all ) { + // Do not display the Constant->Start edge; + if( n instanceof ConstantNode || + n instanceof XCtrlNode || + // ProjNodes handled by Multi; + n instanceof ProjNode || + n instanceof CProjNode || + // ScopeNodes are done separately + n instanceof ScopeNode || + n instanceof MemMergeNode + ) + continue; + if( n.isDead() ) + throw Utils.TODO(); + // Normal edges + for( int i=0; i<n.nIns(); i++ ) { + Node def = n.in(i); + if( def==null ) continue; + sb.i().p(n.uniqueName()).p(" -> "); + if( n instanceof PhiNode && def instanceof RegionNode ) { + // Draw a dotted use->def edge from Phi to Region. + sb.p(def.uniqueName()); + sb.p(" [style=dotted taillabel=").p(i).p("];\n"); + } else { + // Most edges land here use->def + if( def instanceof CProjNode proj ) { + String mname = proj.ctrl().uniqueName(); + sb.p(mname).p(":p").p(proj._idx); + } else if( def instanceof ProjNode proj ) { + String mname = proj.in(0).uniqueName(); + sb.p(mname).p(":p").p(proj._idx); + } else sb.p(def.uniqueName()); + // Number edges, so we can see how they track + sb.p("[taillabel=").p(i); + // Color the edge + nodeEdgeColor(sb,def._type); + // Backedges do not add a ranking constraint + if( i==2 && (n instanceof PhiNode || n instanceof LoopNode) ) + sb.p(" constraint=false"); + sb.p("];\n"); + } + } + + // Bonus edge if hooked by parser + if( (n.iskeep() || n.isUnused()) && scopeName != null ) { + sb.i().p(scopeName).p(" -> ").p(n.uniqueName()).p(" [ style=dashed color=grey];\n"); + } + // Force functions above return + if( n instanceof ReturnNode ret && !ret.fun().isDead() ) + sb.i().p(ret.ctrl().uniqueName()).p(" -> ").p(ret.fun().uniqueName()).p("[style=invis]\n"); + } + } + + private static void nodeEdgeColor(SB sb, Type t) { + sb.p(" color=").p(typeColor(t)); + } + + // Walk the scope edges + private static void scopeEdges( SB sb, ScopeNode scope ) { + sb.i().p("edge [style=dashed color=cornflowerblue];\n"); + int level=0; + for( var v : scope._vars ) { + if( v._idx==1 ) continue; // Memory done separately + Node def = scope.in(v._idx); + while( def instanceof ScopeNode lazy ) + def = lazy.in(v._idx); + if( def==null ) continue; + while( level < scope.depth() && v._idx >= scope._kinds.at(level)._lexSize ) + level++; + String scopeName = makeScopeName(scope, level); + sb.i() + .p(scopeName).p(":") + .p('"').p(makePortName(scopeName, v._name)).p('"') // wrap port name with quotes because $ctrl is not valid unquoted + .p(" -> "); + defPort(sb,def).p(";\n"); + } + + // Memory + if( scope.nIns()>1 && scope.mem() instanceof MemMergeNode n ) { + for( int i=2; i<n.nIns(); i++ ) { + Node def = n.in(i); + while( def instanceof ScopeNode lazy ) + def = lazy.in(i); + if( def==null ) continue; + String scopeName = makeScopeName(scope, 0); + sb.i().p(scopeName).p(":m").p(i).p(" -> "); + defPort(sb,def).p(";\n"); + } + } + + } + + // (C)Projection index or -1 + private static int idx(Node n) { + return n instanceof ProjNode p ? p._idx : (n instanceof CProjNode cp ? cp._idx : -1); + } + + private static SB defPort(SB sb, Node def) { + int idx = idx(def); + return idx != -1 + ? sb.p(def.in(0).uniqueName()).p(":p").p(idx) + : sb.p(def.uniqueName()); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java b/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java new file mode 100644 index 00000000..4077533d --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java @@ -0,0 +1,149 @@ +package com.seaofnodes.simple.print; + +import com.seaofnodes.simple.util.Utils; +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +// Why-Oh-Why is this needed? + +// Pass in a URI and a port. +// Opens the client; client has to open a websocket at the given port. +// Connects, handshakes. +// Thereafter you can call get/put to swap frames with client. + +class SimpleWebSocket extends ServerSocket { + final InputStream _in; + final OutputStream _out; + + SimpleWebSocket(URI uri, int port) throws IOException, NoSuchAlgorithmException { + super(port); + // Launch client + java.awt.Desktop.getDesktop().browse(uri); + // Look for client; get in/out streams + Socket sock = accept(); + _in = sock. getInputStream(); + _out = sock.getOutputStream(); + + + // Establish web socket with handshake; easier with a buffered reader + BufferedReader br = new BufferedReader(new InputStreamReader(_in)); + String key = null; + while( br.ready() ) { + String line = br.readLine(); + if( line.startsWith("Sec-WebSocket-Key: ") ) + key = line.substring(19); + } + assert key!=null; + // Magic handshake + byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n" + + "Connection: Upgrade\r\n" + + "Upgrade: websocket\r\n" + + "Sec-WebSocket-Accept: " + + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-1").digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes( StandardCharsets.UTF_8 ))) + + "\r\n\r\n").getBytes( StandardCharsets.UTF_8 ); + _out.write(response,0,response.length); + } + + final byte[] _ins = new byte[1024]; + final byte[] _str = new byte[1024]; + final byte[] _key = new byte[4]; + int _x, _len; + private int rawget() { assert _x<_len; return _ins[_x++]&0xFF; } + + // TODO: Flip to Direct Byte Buffer, this is just a poor-mans version + private void flipRead() throws IOException { + // Move _x to _len to offset 0 + System.arraycopy(_ins,_x,_ins,0,(_len-_x)); + _len = _len-_x; + _x=0; + // Read more + int len = _in.read(_ins,_len,_ins.length-_len); + if( len == -1 ) + throw Utils.TODO(); // EOF + _len += len; + } + // Block until you read at least len + private void atLeast(int len) throws IOException { + if( _x+len <= _len ) return; + flipRead(); + } + + + // Classic read from browser client. Encoded, so decoded here + public String get() throws IOException { + atLeast(1); // Need 1 byte for opcode + int op = rawget(); + if( (op&0x80)!=0x80 ) // FIN bit not set? + throw Utils.TODO( "Multi-part from client: " + op ); // Multipart message + op &= 0x7F; // Strip FIN bit + switch( op&15) { + case 1: { + atLeast(5); // Min message length + // Message length + int len = rawget()-128; + if( len > 125 ) { + if( len > 126 ) + throw Utils.TODO(); // Multi-byte length + len = (rawget()<<8) | rawget(); + } + atLeast(len+4); // Rest of message + // Encoding key + _key[0] = (byte)rawget(); + _key[1] = (byte)rawget(); + _key[2] = (byte)rawget(); + _key[3] = (byte)rawget(); + + // Decode bytes + for( int i=0; i<len; i++ ) + _str[i] = (byte)(rawget() ^ _key[i & 3]); + + return new String(_str,0,len); + } + // Client closes + case 8: return null; + // Ping + case 9: + // Send a PONG back. TODO: echo PING data in the PONG + _out.write(0x8A); // FIN + PONG + _out.write(0x00); // LENGTH==0 + return get(); + // Not (yet) implemented: continuation frames, ping/pong + default: + throw Utils.TODO("Unexpected op from client: "+op); // Multi-part message + } + } + + // Classic put to browser client. No encoding. + public void put(String msg) throws IOException { + int len = msg.length(); + if( len <= 125 ) { + _out.write(129); // FIN, opcode 1 - whole text message + _out.write(len); + for( int i=0; i<msg.length(); i++ ) + _out.write(msg.charAt(i)); + return; + } + if( len > 65535 ) + throw Utils.TODO("MOAR LENGTH"); + _out.write(129); // FIN, opcode 1 - whole text message + _out.write(126); // Length is 16b + _out.write(len>>>8); + _out.write(len&255); + for( int i=0; i<msg.length(); i++ ) + _out.write(msg.charAt(i)); + } + + @Override public void close() throws IOException { + if( _out != null ) { + _out.write(0b10001000); + _out.flush(); + } + super.close(); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/sys.java b/chapter25/src/main/java/com/seaofnodes/simple/sys.java new file mode 100644 index 00000000..45106770 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/sys.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + + +/** Default included namespaces for Simple. + + You can think of this as {@code #include <stdio.h>} for C or {@code import java.lang.*} for Java. + */ +public abstract class sys { + + public static final String SYS; + + static { + try { + SYS = Files.readString(Path.of("src/main/smp/sys.smp")); + } + catch( Exception e ) { + throw new RuntimeException(e); + } + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/Field.java b/chapter25/src/main/java/com/seaofnodes/simple/type/Field.java new file mode 100644 index 00000000..b6ea5843 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/Field.java @@ -0,0 +1,148 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.AryInt; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.SB; +import java.util.*; + +/** + * Represents a field in a struct. This is not a Type in the type system. + */ +public class Field extends Type { + static final Field[] EMPTY = new Field[0]; + + // The pair {fieldName,type} uniquely identifies a field. + + // Field name + public String _fname; + // Type of the field + public Type _t; + // Unique memory alias, not sensibly part of a "type" but very convenient here. + public int _alias; + // Field is only written in the constructor + public boolean _final; + + private static final Ary<Field> FREE = new Ary<>(Field.class); + private Field(String fname, Type type, int alias, boolean xfinal ) { super(TFLD); init(fname,type,alias,xfinal); } + private Field init(String fname, Type type, int alias, boolean xfinal ) { + _fname = fname; + _t = type; + _alias = alias; + _final = xfinal; + return this; + } + + // Return a filled-in Field; either from free list or alloc new. + private static Field malloc(String fname, Type type, int alias, boolean xfinal ) { + return FREE.isEmpty() ? new Field(fname,type,alias,xfinal) : FREE.pop().init(fname,type,alias,xfinal); + } + // Malloc-from + public Field malloc( ) { return malloc(_fname,null,_alias,_final); } + @Override Field free(Type t) { + Field f = (Field)t; + assert !f.isFree() && !f._terned; + f._t = null; + f._dual = null; + f._hash = 0; + FREE.push(f); + return this; + } + @Override boolean isFree() { return _t ==null; } + + // Make and intern with listed fields + public static Field make( String fname, Type type, int alias, boolean xfinal ) { + Field f = malloc(fname,type,alias,xfinal); + Field f2 = f.intern(); + if( f2==f ) return f; + return VISIT.isEmpty() ? f2.free(f) : f2.delayFree(f); + } + public Field makeFrom( Type type ) { + return type == _t ? this : make(_fname,type,_alias,_final); + } + public Field makeFrom( boolean xfinal ) { + return xfinal == _final ? this : make(_fname,_t,_alias,xfinal); + } + + // Cycle-making-breaking + void setType(Type t) { assert _t ==null; _t = t; } + + public static final Field BOT = make(" ",Type.BOTTOM,-999,false); + public static final Field TEST = make("test",Type.NIL,-2,false); + public static final Field TEST2 = make("test",Type.NIL,-2,true); + public static final Field FLT = make("flt ",TypeFloat.F32,-2,false); + public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST2); ts.add(FLT); } + + @Override Field xmeet( Type that ) { + Field fld = (Field)that; // Invariant + if( this==BOT || fld==BOT ) return BOT; + if( this==BOT.dual() ) return fld ; + if( fld ==BOT.dual() ) return this; + if( _fname!=fld._fname ) { assert !_fname.equals(fld._fname); return BOT; } + assert _alias==fld._alias; + return make(_fname, _t.meet(fld._t ), _alias, _final | fld._final); + } + + @Override + Field xdual() { return malloc(_fname, _t.dual(),_alias,!_final); } + + @Override Field rdual() { + if( _dual!=null ) return dual(); + assert !_terned; + Field d = malloc(_fname,null,_alias,!_final); + (_dual = d)._dual = this; // Cross link duals + d._t = _t._terned ? _t.dual() : _t.rdual(); + return d; + } + + @Override boolean _isConstant() { return _t._isConstant(); } + @Override boolean _isFinal() { return _final && _t._isFinal(); } + @Override Field _makeRO() { return _final ? this : make(_fname, _t._makeRO(),_alias,true); } + @Override boolean _isGLB(boolean mem) { return _t._isGLB(true); } + @Override public Type _glb(boolean mem) { return make(_fname,_t._glb(true),_alias,true); } + @Override Field _close( String name, HashMap<String, Type> TYPES ) { return makeFrom( _t._close(name, TYPES )); } + + @Override Type _upgradeType(HashMap<String,Type> TYPES) { + return makeFrom(_t._upgradeType(TYPES)); + } + + // Override in subclasses + int hash() { return _fname.hashCode() ^ _t.hashCode() ^ _alias ^ (_final ? 1024 : 0); } + + private boolean static_eq( Field f ) { + return _fname.equals(f._fname) && _alias==f._alias && _final==f._final && _t !=null && f._t !=null; + } + @Override boolean eq( Type t ) { + Field f = (Field)t; + return static_eq(f) && _t ==f._t; + } + @Override boolean cycle_eq(Type t) { + if( this==t ) return true; + Field f = (Field)t; + return static_eq(f) && _t.cycle_eq(f._t ); + } + + @Override public int nkids() { return 1; } + @Override public Type at( int idx ) { return _t; } + @Override public void set( int idx, Type t ) { _t = t; } + // Tags: final/!final; +alias+name + @Override int TAGOFF() { return 2; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + baos.write(TAGOFFS[_type] + (_final ? 1 : 0)); + baos.packed2(_alias); + baos.packed2(strs.get(_fname)); + } + static Field packed( int tag, BAOS bais, String[] strs ) { + int alias = bais.packed2(); + String fname = strs[bais.packed2()]; + return malloc(fname,null,alias,(tag&1)==1); + } + + @Override + public SB _print( SB sb, BitSet visit, boolean html ) { + sb.p(_final?"":"!").p(_fname).p(":").p(_alias).p(" : "); + return _t ==null ? sb.p("---") : _t.print(sb,visit,html); + } + + @Override public String str() { return (_final?"":"!")+_fname; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java new file mode 100644 index 00000000..fd3a7c59 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java @@ -0,0 +1,682 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.codegen.GlobalBits; +import com.seaofnodes.simple.util.*; +import java.util.*; + +/** + * These types are part of a Monotone Analysis Framework, + * @see <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">see for example this set of slides</a>. + * <p> + * The types form a lattice; @see <a href="https://en.wikipedia.org/wiki/Lattice_(order)">a symmetric complete bounded (ranked) lattice.</a> + * <p> + * This wild lattice theory will be needed later to allow us to easily beef up + * the analysis and optimization of the Simple compiler... but we don't need it + * now, just know that it is coming along in a later Chapter. + * <p>g + * One of the fun things here is that while the theory is deep and subtle, the + * actual implementation is darn near trivial and is generally really obvious + * what we're doing with it. Right now, it's just simple integer math to do + * simple constant folding e.g. 1+2 == 3 stuff. + */ + +public class Type /*implements Cloneable*/ { + // Interning tables + + // Main intern table; a collection of all unique types. + static final HashMap<Type,Type> INTERN = new HashMap<>(); + // Starting intern table; used to rapidly reset the main table between tests + private static final HashMap<Type,Type> INTERN0= new HashMap<>(); + // Lifetime management during cyclic installs is complex; this is used to + // track created-but-already-interned objects + private static final Ary<Type> FREES = new Ary<>(Type.class); + // "visit" bit management, based solely on _uid + static final BitSet BITS = new BitSet(); + + // "visit" bit management. Key varies by kind of visit: + + // - "close" an open cyclic type. Used by the Parser, it will hit on + // same-named structs, even if the structs have different states of + // completion/fields. + // - "uid/name" - Either the UID or name works (unlike "close" which must + // use the name). Used to make cyclic copies with changes, like make + // read-only where there should be only 1 copy of a name in a cycle. + // - "pair of uids" - For recursive MEET, where we need to key off two types + static final HashMap<Object,Type> VISIT = new HashMap<>(); + + // Every Type has a UID to allow visit bits on cyclic visits + private static int UID=1; + public char _uid; // Unique ID for every type + + // ---------------------------------------------------------- + // Simple types are implemented fully here. "Simple" means: the code and + // type hierarchy are simple, not that the Type is conceptually simple. + static final byte TBOT = 0; // Bottom (ALL) + static final byte TTOP = 1; // Top (ANY) + static final byte TCTRL = 2; // Ctrl flow bottom + static final byte TXCTRL = 3; // Ctrl flow top (mini-lattice: any-xctrl-ctrl-all) + static final byte TNIL = 4; // low null of all flavors + static final byte TXNIL = 5; // high or choice null + static final byte TSIMPLE = 5; // End of the Simple Types + + static final byte TSCALAR = 6; // Integer, float, memory pointer or function pointer + static final byte TPTR = 7; // All nil-able pointer values + static final byte TINT = 8; // All Integers; see TypeInteger + static final byte TFLT = 9; // All Floats ; see TypeFloat + static final byte TCONARY =10; // Constant array + static final byte TRPC =11; // Return Program Control (Return PC or RPC) + + static final byte TCYCLIC =12; // Has internal pointers, needs recursive treatment + static final byte TMEMPTR =12; // Memory pointer to a struct type + static final byte TFUNPTR =13; // Function pointer; unique signature and code address (just a bit) + static final byte TMEM =14; // All memory (alias 0) or A slice of memory - with specific alias + static final byte TFLD =15; // Named fields in structs + static final byte TSTRUCT =16; // Structs; tuples with named fields + static final byte TTUPLE =17; // Tuples; finite collections of unrelated Types, kept in parallel + + static final byte TMAX =18; + + // Basic RTTI, useful for a lot of fast tests. + public final byte _type; + public boolean _terned; + + public boolean is_simple() { return _type <= TSIMPLE; } + public boolean is_nokids() { return _type < TCYCLIC; } + private static final String[] STRS = new String[]{"Bot","Top","Ctrl","~Ctrl","null","~nil"}; + protected Type(byte type) { + _type = type; // RTTI + _uid = (char)UID++; // A unique ID for every type + if( UID > 32000 ) + type = type; + assert _uid!=0; // Overflow + } + + public static final Type BOTTOM = new Type( TBOT ).intern(); // ALL + public static final Type TOP = BOTTOM.dual(); + public static final Type CONTROL = new Type( TCTRL ).intern(); // Ctrl + public static final Type XCONTROL = CONTROL.dual(); + public static final Type NIL = new Type( TNIL ).intern(); // low null of all flavors + public static final Type XNIL = NIL.dual(); + public static Type[] gather() { + ArrayList<Type> ts = new ArrayList<>(); + ts.add(BOTTOM); + ts.add(CONTROL); + ts.add(NIL); + TypeScalar.gather(ts); + TypeNil.gather(ts); + TypePtr.gather(ts); + TypeInteger.gather(ts); + TypeFloat.gather(ts); + TypeMemPtr.gather(ts); + TypeFunPtr.gather(ts); + TypeMem.gather(ts); + Field.gather(ts); + TypeStruct.gather(ts); + TypeTuple.gather(ts); + TypeRPC.gather(ts); + TypeConAry.gather(ts); + + int sz = ts.size(); + for( int i = 0; i < sz; i++ ) + ts.add(ts.get(i).dual()); + return ts.toArray(new Type[ts.size()]); + } + + // Is high or on the lattice centerline. + public boolean isHigh () { return _type==TTOP || _type==TXCTRL || _type==TXNIL; } + public boolean isHighOrConst() { return isHigh() || isConstant(); } + + // ---------------------------------------------------------- + + // Notes on Type interning: At the moment it is not easy to reset the + // interned types because we hold static references to several types and + // these are scattered around. This means the INTERN cache will retain all + // types from every run of the Parser. For this to work correctly types + // must be rigorous about defining when they are the same. Also types need + // to be immutable once defined. The rationale for interning is + // *correctness* with cyclic type definitions. Simple structural recursive + // checks go exponential with merely sharing, but with cycles they will + // stack overflow and crash. Interning means we do not need to have sharing + // checks with every type compare, only during interning. + + // Factory method which interns "this" + @SuppressWarnings("unchecked") + public <T extends Type> T intern() { + //assert check(); + assert !_terned; // Do not ask for already-interned + T t2 = (T)INTERN.get(this); + if( t2!=null ) { + assert t2._dual != null; // Prior is complete with dual + assert this != t2; // Do not hashcons twice, should not get self back + return t2; // Return prior + } + // Mid recursive type construction + if( !VISIT.isEmpty() && _type >= TCYCLIC ) { + // No intern attempt at all + return (T)this; + } + // Not in type table + _dual = null; // No dual yet + INTERN.put(this,this); // Put in table without dual + _terned = true; + //Util.hash_quality_check_per(INTERN,"INTERN"); + T d = xdual(); // Compute dual without requiring table lookup, and not setting name + _dual = d; + if( this==d ) return d; // Self-symmetric? Dual is self + assert !equals(d); // Self-symmetric is handled by caller + assert d._dual==null; // Else dual-dual not computed yet + assert INTERN.get(d)==null; + d._dual = (T)this; + INTERN.put(d,d); // Install dual also + d._terned = true; + //assert check(); + return (T)this; + } + + // Recursive visit elsewhere, interns with dual already + <T extends Type> T _intern() { + //assert check(); + assert !_terned; + Type t = INTERN.get(this); + if( t!=null ) return (T)t.delayFree(this).delayFree(_dual); + assert !INTERN.containsKey( _dual ); + INTERN.put(this,this); + INTERN.put(_dual,_dual); + _terned = _dual._terned = true; + //assert check(); + return (T)this; + } + + private static boolean check() { + for( Type t : INTERN.keySet() ) { + Type t2 = INTERN.get(t); + assert t==t2; + } + return true; + } + + public static Type find(int uid) { + for( Type t : INTERN.keySet() ) + if( t._uid==uid ) return t; + return null; + } + + + int _hash; // Hash cache; not-zero when set. + @Override + public final int hashCode() { + if( _hash!=0 ) return _hash; + _hash = hash(); + if( _hash==0 ) _hash = 0xDEADBEEF; // Bad hash from subclass; use some junk thing + return _hash; + } + // Override in subclasses + int hash() { return _type; } + + @Override + public final boolean equals( Object o ) { + if( o==this ) return true; + if( !(o instanceof Type t)) return false; + if( _type != t._type ) return false; + return eq(t); + } + // Overridden in subclasses; subclass can assume "this!=t" and java classes are same + boolean eq(Type t) { return this==t; } + boolean cycle_eq(Type t) { assert is_nokids(); return _type==t._type && eq(t); } + // A pair of uids + int pid( Type that ) { + return _uid < that._uid ? (_uid<<16 | that._uid) : (that._uid<<16 | _uid); + } + + // At/Set child at 'idx' to t + public Type at( int idx ) { throw Utils.TODO(); } + public void set( int idx, Type t ) { throw Utils.TODO(); } + public int nkids() { return 0; } // Number of kids + + + // Clear and re-insert the basic Type INTERN table + public static void reset() { + VISIT.clear(); + FREES.clear(); + if( INTERN0.isEmpty() ) { + // First time run all <clinit> and collect basic types + Type t = TypeRPC .BOT; // Force class load, intern + Type u = TypeTuple.BOT; // Force class load, intern + Type w = TypePtr .PTR; // Force class load, intern + INTERN0.putAll(INTERN); + } else { + // Later times, reset back to first time + VISIT.put(0,CONTROL); // Defeat assert + for( Iterator<Type> it = INTERN.keySet().iterator(); it.hasNext(); ) { + Type t = it.next(); + if( !INTERN0.containsKey(t) ) + { t._terned=false; t.free(t); } + it.remove(); + } + VISIT.clear(); + INTERN.putAll(INTERN0); + } + } + + // ---------------------------------------------------------- + public final Type meet(Type t) { + assert _terned && t._terned; + // Shortcut for the self case + if( t == this ) return this; + // Same-type is always safe in the subclasses + if( _type==t._type ) return xmeet(t); + // TypeNil vs TypeNil meet + if( this instanceof TypeNil ptr0 && t instanceof TypeNil ptr1 ) + return ptr0.nmeet(ptr1); + // Reverse; xmeet 2nd arg is never "is_simple" and never equal to "this". + if( is_simple() ) return this.xmeet(t ); + if( t.is_simple() ) return t .xmeet(this); + // TypeConAry meet its element type is OK + if( this instanceof TypeConAry tcon && tcon.elemT() == t._type ) return tcon.ymeet(t); + if( t instanceof TypeConAry tcon && tcon.elemT() == _type ) return tcon.ymeet(this); + // Two distinct scalar families meet at the scalar envelope. + if( this instanceof TypeScalar scalar0 && t instanceof TypeScalar scalar1 ) + return scalar0.smeet(scalar1); + // Mixing 2 unrelated types + return Type.BOTTOM; + } + + // Compute meet right now. Overridden in subclasses. + // Handle cases where 'this.is_simple()' and unequal to 't'. + // Subclassed xmeet calls can assert that '!t.is_simple()'. + Type xmeet(Type t) { + assert is_simple(); // Should be overridden in subclass + // ANY meet anything is thing; thing meet ALL is ALL + if( _type==TBOT || t._type==TTOP ) return this; + if( _type==TTOP || t._type==TBOT ) return t; + + // RHS TypeNil vs NIL/XNIL + if( _type==TNIL || _type==TXNIL ) { + if( t instanceof TypeNil ptr ) return _type==TNIL ? ptr.meet0() : ptr.meetX(); + if( t instanceof TypeScalar scalar ) { + if( t._type==TSCALAR ) return t.isHigh() ? this : t; + return TypeScalar.BOT; + } + return t._type==(_type==TNIL ? TXNIL : TNIL) ? TypePtr.PTR : BOTTOM; + } + + // 'this' is only {TCTRL,TXCTRL} + // Other non-simple RHS things bottom out + if( !t.is_simple() ) return BOTTOM; + // If RHS is NIL/XNIL + if( t._type==TNIL || t._type==TXNIL ) return BOTTOM; + // Both are {TCTRL,TXCTRL} and unequal to each other + return _type==TCTRL || t._type==TCTRL ? CONTROL : XCONTROL; + } + + + // The dual is pre-computed on creation and available "for free" thereafter + Type _dual; + public final <T extends Type> T dual() { return (T)_dual; } + + <T extends Type> T xdual() { + return (T)new Type(switch( _type ) { + case TBOT -> TTOP; + case TTOP -> TBOT; + case TCTRL -> TXCTRL; + case TXCTRL-> TCTRL; + case TNIL -> TXNIL; + case TXNIL -> TNIL; + default -> throw Utils.TODO(); // Should not reach here + }); + } + + + // ---------------------------------------------------------- + // Our lattice is defined with a MEET and a DUAL. + // JOIN is dual of meet of both duals. + public final Type join(Type t) { + if( this==t ) return this; + return dual().meet(t.dual()).dual(); + } + + // True if this "isa" t; e.g. 17 isa TypeInteger.BOT + public boolean isa( Type t ) { return meet(t)==t; } + + public Type nonZero() { return this==NIL ? TypePtr.XNPTR : TypePtr.NPTR; } + + // Make a zero version of this type, 0 for integers and null for pointers. + public Type makeZero() { return Type.NIL; } + + //// Is forward-reference + //public boolean isFRef() { return false; } + + // Cap at limits + public Type oob() { return isHigh() ? TOP : BOTTOM; } + + // ---------------------------------------------------------- + + // Cyclic types! Flag the start of a cyclic type by putting a sentinel in + // VISIT (and eventually clearing VISIT when done). Then do a normal + // recursive descent visit of all types; this will accumulate types without + // interning them. When done we have to visit the possibly cyclic type and + // intern the whole cycle, possibly hitting the entire cycle on a prior + // interned cycle. Any sub-part might also be interned, including whole + // disjoint cycles. + + // E.g. a new type: "A<->B -> C1<->D1" gets created; the "C1<->D1" cycle + // already exists as "C0<->D0", but the "A<->B" cycle is new. The "A<->B" + // cycle will get interned - except the pointer to C1 needs to get replaced + // with C0. The "C1<->D1" cycle gets freed and "B" will point to the + // "C0<->D0" cycle. + + Type recurOpen() { assert VISIT.isEmpty(); VISIT.put(0L,BOTTOM); return this; } + Type recurClose() { return recurClose(null); } + Type recurClose(Type[] types) { + VISIT.remove(0L); // Just ignore the sentinel + Ary<Type> ts = new Ary<>(Type.class); + ts.addAll(VISIT.values()); + if( ts.find(this)== -1 ) ts.add(this); + + // Pass #1: replace already interned fields + assert BITS.isEmpty(); + for( int i=0; i<ts._len; i++ ) + if( ts.at(i).tern()._terned ) + ts.del(i--); + BITS.clear(); + + // Pass#2: compute recursive duals + for( Type t : ts ) + t.rdual(); + + // Pass#3: Recursive install + for( Type t : ts ) + if( !t._terned && INTERN.get(t)==null ) + t.install(); + // Upgrade the result + Type rez = INTERN.get(this); + assert rez!=null; + if( types!=null ) + for( int i=0; i<types.length; i++ ) + types[i] = INTERN.get(types[i]); + + // Pass#4?: Free up any created-but-already interned + for( Type t : FREES ) + t.rfree(); + FREES.clear(); + VISIT.clear(); + return rez; + } + + // Read-only recursive visits do not *make* cyclic types and so the cleanup + // is much easier. + static boolean recurClose(boolean rez) { VISIT.clear(); return rez; } + + final Type tern() { + if( _terned ) return this; + if( !BITS.get(_uid) ) { + BITS.set(_uid); + int nkids = nkids(); + for( int i=0; i<nkids; i++ ) + set(i,at(i).tern()); + } + Type told = INTERN.get(this); + return told==null ? this : told.delayFree(this); + } + + // Recursive dual. Visit all types recursively. Each visited type is + // *new*, and hits in the intern table, or not. If not, we recursively + // call rdual to get its dual, and cross-link the duals. If hitting, we + // use the intern type and mark the prior type for freeing. + Type rdual() { assert !_terned; return xdual(); } + + // Recursively free type and children + final void rfree() { + if( isFree() ) return; + int nkids = nkids(); + for( int i=0; i<nkids; i++ ) + if( !at(i)._terned ) + at(i).rfree(); + free(this); + } + + Type free(Type free) { + return this; + } + boolean isFree() { return false; } + + final <T extends Type> T delayFree(Type free) { + assert !free._terned; + FREES.push(free); + return (T)this; + } + + final Type install() { + if( this instanceof TypeStruct ) { + Type x = _intern(); // Stop the recursion + if( x!=this ) return x.delayFree(this); + } + int nkids = nkids(); + for( int i=0; i<nkids; i++ ) { + Type kid = at(i); + if( !kid._terned ) { + Type ikid = kid.install(); + if( ikid != kid ) { + set(i,ikid); + _dual.set(i,ikid.dual()); + } + } + } + return this instanceof TypeStruct || _terned ? this : _intern(); + } + + // Strict constant values, things on the lattice centerline. + // Excludes both high and low values + public final boolean isConstant() { return recurClose(recurOpen()._isConstant()); } + boolean _isConstant() { return _type==TNIL; } + + // Are all reachable struct Fields are final? + public final boolean isFinal() { return recurClose(recurOpen()._isFinal()); } + boolean _isFinal() { assert is_nokids(); return true; } + + public final Type makeRO() { + if( isFinal() ) return this; + return recurOpen()._makeRO().recurClose(); + } + Type _makeRO() { return this; } + + // Compute greatest lower bound in the lattice. If values are in memory, + // ints and floats cannot widen. + public final boolean isGLB(boolean mem) { return recurClose(recurOpen()._isGLB(mem)); }; + boolean _isGLB(boolean mem) { + return switch(_type) { + case TBOT -> false; + case TSCALAR -> this==TypeScalar.BOT; + case TNIL -> false; + case TCTRL -> true; + case TXCTRL -> false; + case TXNIL -> false; + case TTOP -> false; + default -> throw Utils.TODO(); + }; + } + public final Type glb(boolean mem) { + if( isGLB(mem) ) return this; + Type glb = recurOpen()._glb(mem).recurClose(); + assert this.isa(glb); + return glb; + } + Type _glb(boolean mem) { assert is_simple(); return Type.BOTTOM; } + + // Bulk close-over all recursive types + public static TypeStruct[] closeOver(TypeStruct[] ts, HashMap<String,Type> TYPES) { + BOTTOM.recurOpen(); + for( TypeStruct t : ts ) + t._close(null, TYPES); + for( int i=0; i<ts.length; i++ ) { + // Parser only tracks the mutable name of arrays, so the closeOver + // returns that one. However, internal types might have immutable + // versions. + String name = ts[i].isAry() ? (ts[i]._name+"!").intern() : ts[i]._name; + ts[i] = (TypeStruct)VISIT.get(name); + } + BOTTOM.recurClose(ts); + return ts; + } + + Type _close( String name, HashMap<String, Type> TYPES ) { return this; } + public Type widen() { return this; } + + // Replace recursively all TypeBuilders with cyclic TypeStructs + public Type upgradeType(HashMap<String,Type> TYPES) { + return recurOpen()._upgradeType(TYPES).recurClose(); + } + Type _upgradeType(HashMap<String,Type> TYPES) { assert is_nokids(); return this; } + + + // Recursively gather all types + public void gather(HashMap<Type,Integer> types ) { + if( types.containsKey(this) ) return; // TEST + types.put(this,types.size()); // SET + int nkids = nkids(); + for( int i=0; i<nkids; i++ ) + at(i).gather(types); + } + + // Compute serialization byte tag compression space + final static int[] TAGOFFS = new int[TMAX+1]; + public static int[] TAGOFFS() { + if( TAGOFFS[TMAX-1]==0 ) + // One entry per unique tag-space, using the BOT value from that + // tag-space except for the first tag-space which uses 1 tag for + // multiple Types instead of 1 Type having multiple tags. Has to + // be ordered to match _type enum. + for( Type t : new Type[]{Type.XNIL,TypeScalar.BOT,TypePtr.PTR,TypeInteger.BOT,TypeFloat.F64,TypeConAryB.ABC,TypeRPC.BOT,TypeMemPtr.BOT,TypeFunPtr.BOT,TypeMem.BOT,Field.BOT,TypeStruct.BOT,TypeTuple.BOT} ) + TAGOFFS[t._type+1] = TAGOFFS[t._type] + t.TAGOFF(); + assert TAGOFFS[TMAX] <= 255; + return TAGOFFS; + } + // Reserve 6 tags, 0-5, for plain Types + int TAGOFF() { assert is_simple(); return 6; } + + public void packed( BAOS baos, HashMap<String,Integer> strs ) { + assert is_simple(); + baos.write(_type); + } + static Type packed( int tag ) { + return switch( tag ) { + case TBOT -> BOTTOM; + case TTOP -> TOP; + case TCTRL -> CONTROL; + case TXCTRL->XCONTROL; + case TNIL -> NIL; + default -> throw Utils.TODO(); + }; + } + + // Produce a type with null child types + static Type packed( BAOS bais, String[] strs ) { + int x = bais.read(); + int type = Arrays.binarySearch( TAGOFFS, x ); + if( type < 0 ) type = -(type+1)-1; + int off = x - TAGOFFS[type]; + return switch( type ) { + case 0, 1, 2, 3, 4, 5 -> Type.packed( off ); + case TSCALAR -> TypeScalar .packed( off ); + case TPTR -> TypePtr .packed( off ); + case TINT -> TypeInteger.packed( off, bais ); + case TFLT -> TypeFloat .packed( off, bais ); + case TCONARY -> TypeConAry .packed( off, bais ); + case TRPC -> TypeRPC .packed( off, bais ); + case TMEMPTR -> TypeMemPtr .packed( off, bais ); + case TFUNPTR -> TypeFunPtr .packed( off, bais ); + case TMEM -> TypeMem .packed( off, bais ); + case TFLD -> Field .packed( off, bais, strs ); + case TSTRUCT -> TypeStruct .packed( off, bais, strs ); + case TTUPLE -> TypeTuple .packed( off, bais ); + default -> throw Utils.TODO(); + }; + } + + // Read a packed Type array + public static Type[] packed( BAOS bais, String[] strs, int ntypes, HashMap<String,Type> existingTypes, + GlobalBits fileAliases, GlobalBits aliases, + GlobalBits fileFidxs , GlobalBits fidxs , + GlobalBits fileRpcs , GlobalBits rpcs ) { + Type[] types = new Type[ntypes]; + // Read Types in ID# order, no children + for( int i=0; i<ntypes; i++ ) + types[i] = Type.packed(bais,strs); + // Start possibly recursive type collection + BOTTOM.recurOpen(); + // Read Types in ID# order, only children + for( int i=0; i<ntypes; i++ ) { + VISIT.put(types[i]._uid,types[i]); + int nkids = types[i].nkids(); + for( int j=0; j<nkids; j++ ) + types[i].set(j,types[bais.packed4()]); + } + // Remap file-local aliases/fidxs/rpcs into this compilation unit's + // local dense indexes via their global identities. + for( int i=0; i<ntypes; i++ ) { + if( types[i] instanceof Field fld ) { + if( fld._alias >= GlobalBits.RESERVED ) + fld._alias = aliases.map(fileAliases,fld._alias); + } + if( types[i] instanceof TypeFunPtr tfp ) + tfp._fidxs = remapBits(fileFidxs,fidxs,tfp._fidxs,GlobalBits.RESERVED); + if( types[i] instanceof TypeRPC rpc ) + rpc._rpcs = remapBits(fileRpcs,rpcs,rpc._rpcs,GlobalBits.RESERVED); + if( types[i] instanceof TypeMem mem ) { + if( mem._alias >= GlobalBits.RESERVED ) + mem._alias = aliases.map(fileAliases,mem._alias); + mem._escFs = remapBits(fileFidxs ,fidxs ,mem._escFs,GlobalBits.RESERVED); + mem._escAs = remapBits(fileAliases,aliases,mem._escAs,GlobalBits.RESERVED); + } + } + + // Intern them all at once + BOTTOM.recurClose(types); + return types; + } + + private static int[] remapBits(GlobalBits fileBits, GlobalBits bits, int[] fileLocals, int firstMapped ) { + if( fileLocals == XInt.EMPTY || fileLocals == XInt.FULL ) + return fileLocals; + assert !XInt.isHigh(fileLocals); + int[] locals = XInt.EMPTY; + for( int fileLocal = XInt.next(fileLocals,-1); fileLocal >=0; fileLocal = XInt.next(fileLocals,fileLocal) ) + locals = XInt.make(locals,fileLocal < firstMapped ? fileLocal : bits.map(fileBits,fileLocal)); + return locals; + } + + + // ---------------------------------------------------------- + + // Size in bits to hold an instance of this type. + // Sizes are expected to be between 1 and 64 bits. + // Size 0 means this either takes no space (such as a known-zero field) + // or isn't a scalar to be stored in memory. + public int log_size () { return 3; } // log-size of a type; log_size for a struct is usually undefined + public int size() { return 1<<log_size(); } + public int alignment() { return log_size(); } // alignment; for structs, max align of Fields + + // ---------------------------------------------------------- + // Useful in the debugger, which calls toString everywhere. + // This is a more verbose dev-friendly print. + @Override + public final String toString() { + return print(new SB(), new BitSet(), false).toString(); + } + public final String gprint() { + return print(new SB(), new BitSet(), true).toString(); + } + public final SB print(SB sb) { return print(sb, new BitSet(), false); } + public final SB gprint(SB sb) { return print(sb, new BitSet(), true ); } + + // Used during recursive printing, or combined with SB Node printing + public final SB print(SB sb, BitSet visit, boolean html ) { + if( visit.get(_uid) ) return sb.p(str()); + visit.set(_uid); + return _print(sb,visit,html); + } + SB _print(SB sb, BitSet visit, boolean html ) { return sb.p(str()); } + + // This is used by error messages, and is a shorted print. + public String str() { return STRS[_type]; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java new file mode 100644 index 00000000..26fa4a67 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java @@ -0,0 +1,95 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.AryInt; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Represents a constant array of primitives + */ +public abstract class TypeConAry<A> extends TypeScalar { + boolean _any; + // One of byte,short,int,long,float,double array + public final A _ary; + + TypeConAry( boolean any, A ary ) { super(TCONARY); _any = any; _ary = ary; } + public static void gather(ArrayList<Type> ts) { + TypeConAryB.gather(ts); + TypeConAryI.gather(ts); + } + + @Override public String str() { return (_any?"~":"") + "[]"; } + @Override TypeConAry xdual() { return this; } + + @Override Type xmeet(Type t) { + TypeConAry ary = (TypeConAry)t; // Invariant + assert _ary!=ary._ary; // Already interned and this!=t + // Unrelated constant arrays + return elem().meet(ary.elem()); + } + + Type ymeet( Type t ) { + if( t instanceof TypeInteger ti ) { + // if i can isa *each* element, then maybe can keep. + // no good to i.isa(elem()) because fails the dual + boolean fail = false; + for( int j=0; j<len(); j++ ) + if( !ti.isa(TypeInteger.constant(at8(j))) ) + { fail=true; break; } + Type e = elem(); + if( fail ) + return ti.meet(e); + return this; + } + throw Utils.TODO(); + } + + @Override public boolean isHigh() { return this==TOP; } + @Override boolean _isConstant() { return true; } + @Override Type _glb(boolean mem) { return this; } + @Override boolean _isGLB(boolean mem) { return true; } + + // Meet-over-elements type + public Type elem() { + if( _ary==null ) + return _any ? Type.TOP : BOTTOM; + int len = len(); + long min = Long.MAX_VALUE; + long max = Long.MIN_VALUE; + for( int i=0; i<len; i++ ) { + min = Math.min(min,at8(i)); + max = Math.max(max,at8(i)); + } + return TypeInteger.make(min,max); + } + // Generic element _type + public int elemT() { return TBOT; } + public long at8(int idx) { throw Utils.TODO(); } + public int len() { throw Utils.TODO(); } + @Override public int log_size() { throw Utils.TODO(); } + public void write( BAOS baos ) { throw Utils.TODO(); } + + // Reserve tags for u8 array + @Override int TAGOFF() { return 1; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + assert log_size()==0 && !_any; + baos.write(TAGOFFS[TCONARY]+0); + baos.packed4(len()); + baos.write((byte[])_ary); + } + static TypeConAry packed( int tag, BAOS bais ) { + return TypeConAryB.make(bais.read(new byte[bais.packed4()])); + } + + @Override boolean eq(Type t) { + return t instanceof TypeConAry ary && _any==ary._any && ary._ary==null; + } + + @Override int hash() { + assert _ary==null; + return _any ? 1024 : 0; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java new file mode 100644 index 00000000..bd2827ed --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.BAOS; +import java.util.ArrayList; +import java.util.Arrays; + +public class TypeConAryB extends TypeConAry<byte[]> { + private TypeConAryB(byte[] bs) { super(false,bs); } + public static TypeConAryB make( byte[] bs ) { return new TypeConAryB(bs).intern(); } + public static TypeConAryB make( String s ) { return make(s.getBytes()); } + static final TypeConAryB ABC = make("abc"); + static final TypeConAryB ABCD = make("abcd"); + public static void gather(ArrayList<Type> ts) { ts.add(ABC); ts.add(ABCD); } + @Override public int elemT() { return TINT; } + @Override public long at8(int idx) { return _ary[idx]; } + @Override public int len() { return _ary.length; } + @Override public int log_size() { return 0; } + @Override public String str() { return "[\""+new String(_ary)+"\"]"; } + @Override boolean eq(Type t) { return t instanceof TypeConAryB ary && Arrays.equals(_ary,ary._ary); } + @Override int hash() { return Arrays.hashCode(_ary); } + @Override public void write( BAOS baos ) { baos.write(_ary); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java new file mode 100644 index 00000000..bb2ab83c --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java @@ -0,0 +1,18 @@ +package com.seaofnodes.simple.type; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TypeConAryI extends TypeConAry<int[]> { + private TypeConAryI(int[] bs) { super(false,bs); } + public static TypeConAryI make( int[] bs ) { return new TypeConAryI(bs).intern(); } + private static final TypeConAryI I123 = make(new int[]{1,2,3}); + public static void gather(ArrayList<Type> ts) { ts.add(I123); } + @Override public int elemT() { return TINT; } + @Override public long at8(int idx) { return _ary[idx]; } + @Override public int len() { return _ary.length; } + @Override public int log_size() { return 2; } + @Override public String str() { return "[i32]"; } + @Override boolean eq(Type t) { return t instanceof TypeConAryI ary && Arrays.equals(_ary,ary._ary); } + @Override int hash() { return Arrays.hashCode(_ary); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFloat.java new file mode 100644 index 00000000..f4968f28 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFloat.java @@ -0,0 +1,134 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.AryInt; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Float Type + */ +public class TypeFloat extends TypeScalar { + + // - high -64, high -32, con 0, low +32, low +64 + public byte _sz; + + /** + * The constant value or 0 + */ + public double _con; + + private static final Ary<TypeFloat> FREE = new Ary<>(TypeFloat.class); + private TypeFloat(byte sz, double con) { super(TFLT); init(sz,con); } + private TypeFloat init( byte sz, double con ) { _sz = sz; _con = con; return this; } + public static TypeFloat malloc(byte sz, double con) { return FREE.isEmpty() ? new TypeFloat(sz,con) : FREE.pop().init(sz,con); } + private static TypeFloat make(int sz, double con) { + TypeFloat f = malloc((byte)sz,con); + TypeFloat t2 = f.intern(); + return t2==f ? f : t2.free(f); + } + @Override TypeFloat free(Type t) { + TypeFloat f = (TypeFloat)t; + f._hash = 0; + f._dual=null; + FREE.push(f); + return this; + } + + public static TypeFloat constant(double con) { return make(0, con); } + + public final static TypeFloat ONE = constant(1.0); + public final static TypeFloat FZERO = constant(0.0); + public final static TypeFloat F32 = make(32, 0); + public final static TypeFloat F64 = make(64, 0); + + public static void gather(ArrayList<Type> ts) { ts.add(F64); ts.add(F32); ts.add(constant(3.141592653589793)); } + + @Override public String str() { + return switch( _sz ) { + case -64 -> "~flt"; + case -32 -> "~f32"; + case 0 -> ""+_con+((float)_con==_con ? "f" : ""); + case 32 -> "f32"; + case 64 -> "flt"; + default -> throw Utils.TODO(); + }; + } + private boolean isF32() { return ((float)_con)==_con; } + + @Override public boolean isHigh () { return _sz< 0; } + @Override public int log_size() { return _sz==32 || _sz==-32 ? 2 : 3; } + + public double value() { return _con; } + + @Override + public TypeFloat xmeet(Type other) { + TypeFloat f = (TypeFloat)other; + // Invariant from caller: 'this' != 'other' and same class (TypeFloat). + TypeFloat i = (TypeFloat)other; // Contract + // Larger size in i1, smaller in i0 + TypeFloat i0 = _sz < i._sz ? this : i; + TypeFloat i1 = _sz < i._sz ? i : this; + + if( i1._sz== 64 ) return F64; + if( i0._sz==-64 ) return i1; + if( i1._sz== 32 ) + return i0._sz==0 && !i0.isF32() ? F64 : F32; + if( i1._sz!= 0 ) return i1; + // i1 is a constant + if( i0._sz==-32 ) + return i1.isF32() ? i1 : F64; + // Since both are constants, and are never equals (contract) unequals + // constants fall to bottom + return i0.isF32() && i1.isF32() ? F32 : F64; + } + + @Override + TypeFloat xdual() { + return _isConstant() ? this : new TypeFloat((byte)-_sz,0); // Constants are a self-dual + } + + @Override boolean _isGLB(boolean mem) { return _glb(mem)==this; } + @Override Type _glb(boolean mem) { + if( !mem ) return F64; + if( _isConstant() ) return isF32() ? F32 : F64; + return isHigh() ? dual() : this; + } + + @Override boolean _isConstant() { return _sz==0; } + + @Override public Type makeZero() { return FZERO; } + + // Reserve tags for F64,F32,constant + @Override int TAGOFF() { return 3; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + if( this==F64 ) baos.write(TAGOFFS[_type] + 0); + else if( this==F32 ) baos.write(TAGOFFS[_type] + 1); + else { + assert isConstant(); + baos.write(TAGOFFS[_type] + 2); + baos.packed8(Double.doubleToLongBits(_con)); + } + } + + static Type packed( int tag, BAOS bais ) { + return switch( tag ) { + case 0 -> F64; + case 1 -> F32; + case 2 -> constant(Double.longBitsToDouble(bais.packed8())); + default -> throw Utils.TODO(); + }; + } + + @Override + int hash() { return (int)(Double.hashCode(_con) ^ _sz ^ (1<<17)); } + @Override + public boolean eq( Type t ) { + TypeFloat i = (TypeFloat)t; // Contract + // Allow NaN to check for equality + return _sz==i._sz && Double.doubleToLongBits(_con)==Double.doubleToLongBits(i._con); + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java new file mode 100644 index 00000000..e2ec6263 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -0,0 +1,304 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.*; +import java.util.*; + +/** + * Represents a Pointer to a function. + * <p></p> + * Functions have argument types and a return type, making a function + * signature. Within a signature there can be many instances of functions, and + * each function is labeled with a small integer constant. A TFP can represent + * a single signature and a set of functions; the set might contain only 1 + * function or the zeroth function (the null ptr). + * <p></p> + * Functions with different signatures cannot be mixed, and will result in a + * bottom function type, which can only be null-checked. + */ +public class TypeFunPtr extends TypeNil { + // A TypeFunPtr is Signature and a set of functions. + public Type[] _sig; + public Type _ret; + boolean _open; // Extra args are true:scalar BOT, false:scalar TOP + // Set of functions. Bit zero is used for mutable/immutable safety. Sign + // bit repeats infinitely. + public int[] _fidxs; + + private static final Ary<TypeFunPtr> FREE = new Ary<>(TypeFunPtr.class); + private TypeFunPtr(byte nil) { super(TFUNPTR,nil); } + private TypeFunPtr init(byte nil, boolean open, Type[] sig, Type ret, int[] fidxs ) { + assert sig != null; + _nil = nil; + _open = open; + _sig = sig; + _ret = ret; + assert _fidxs == null; + _fidxs= XInt.intern(fidxs); + return this; + } + static TypeFunPtr malloc( byte nil, boolean open, Type[] sig, Type ret, int[] fidxs ) { + return (FREE.isEmpty() ? new TypeFunPtr(nil) : FREE.pop()).init(nil,open,sig,ret,fidxs); + } + @Override TypeFunPtr free(Type t) { + TypeFunPtr fun = (TypeFunPtr)t; + assert !fun.isFree() && !fun._terned; + fun._sig = null; + fun._ret = null; + fun._dual = null; + fun._hash = 0; + fun._fidxs = null; + FREE.push(fun); + return this; + } + @Override boolean isFree() { return _sig==null; } + + // All fields directly listed + public static TypeFunPtr make( byte nil, boolean open, Type[] sig, Type ret, int[] fidxs ) { + TypeFunPtr fun = malloc(nil,open,sig,ret,fidxs); + TypeFunPtr f2 = fun.intern(); + if( f2==fun ) return fun; + return VISIT.isEmpty() ? f2.free(fun) : f2.delayFree(fun); + } + public static TypeFunPtr make( boolean nil, Type[] sig, Type ret ) { return make((byte)(nil ? 3 : 2),true,sig,ret, XInt.FULL); } + public static TypeFunPtr make1( byte nil, boolean open, Type[] sig, Type ret, int fidx ) { + return make(nil,open,sig,ret,XInt.make(fidx)); + } + + + + @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_open,_sig,_ret, _fidxs); } + public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_open,_sig, ret, _fidxs); } + public TypeFunPtr makeFrom( int fidx ) { return make1((byte)2,_open,_sig,_ret,fidx); } + public TypeFunPtr makeFrom( Type arg, int idx ) { + // Alter named arg + Type[] sig = _sig.clone(); + sig[idx] = arg; + return make(_nil,_open,sig,_ret,_fidxs); + } + @Override public Type nonZero() { return makeFrom((byte)(_nil <= 1 ? 1 : 2)); } + + public static final Type[] TEMPTY = new Type[0]; + static final Type[] TINT = new Type[]{TypeInteger.BOT}; + static final Type[] TINTMEM = new Type[]{TypeInteger.BOT,TypeFloat.F32}; + static final Type[] TINTINT = new Type[]{TypeInteger.BOT,TypeInteger.BOT}; + public static TypeFunPtr BOT = make((byte)3,true,TEMPTY,Type.BOTTOM, XInt.FULL); + public static TypeFunPtr TEST = make((byte)2,true,TINTMEM,TypeInteger.BOT, XInt.SET1); + public static TypeFunPtr TEST0 = make((byte)3,true,TINTMEM,TypeInteger.BOT, XInt.SET3); + public static TypeFunPtr MAIN = make((byte)3,true,TINT ,Type.BOTTOM, XInt.FULL); // Main can return anything + public static TypeFunPtr CALLOC= make((byte)3,true,TINTINT,TypeMemPtr .BOT, XInt.FULL); + public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN);ts.add(CALLOC); } + + private static final Type[] ARG_EMPTY = new Type[0]; + @Override + Type xmeet(Type t) { + TypeFunPtr that = (TypeFunPtr) t; + boolean open = _open | that._open; + Type def0 = this._open ? TypeScalar.BOT : TypeScalar.TOP; + Type def1 = that._open ? TypeScalar.BOT : TypeScalar.TOP; + Type def = open ? TypeScalar.BOT : TypeScalar.TOP; + + int nargs = Math.max(_sig.length,that._sig.length); + Type[] args = new Type[nargs]; + + for( int i=0; i<nargs; i++ ) { + Type t0 = i < this._sig.length ? this._sig[i] : def0; + Type t1 = i < that._sig.length ? that._sig[i] : def1; + args[i] = t0.meet(t1); + } + + while( nargs>0 && args[nargs-1] == def ) + nargs--; + + if( nargs != args.length ) + args = nargs == 0 ? ARG_EMPTY : Arrays.copyOf(args,nargs); + + return make(xmeet0(that), open, args, _ret.meet(that._ret), XInt.meet(_fidxs,that._fidxs)); + } + + static Type[] xmeet( Type[] ts0, Type[] ts1) { + if( ts0==ts1 ) return ts0; + assert ts0.length==ts1.length; + Type[] ts = new Type[ts0.length]; + for( int i=0; i<ts0.length; i++ ) + ts[i] = ts0[i].meet(ts1[i]); + return ts; + } + + @Override TypeFunPtr xdual() { return malloc(dual0(), !_open, xdual(_sig), _ret.dual(), XInt.dual(_fidxs)); } + static Type[] xdual(Type[] ts) { + Type[] dual = new Type[ts.length]; + for( int i=0; i<ts.length; i++ ) + dual[i] = ts[i].dual(); + return dual; + } + + @Override TypeFunPtr rdual() { + if( _dual!=null ) return dual(); + assert !_terned; + Type[] sigs = new Type[_sig.length]; + TypeFunPtr d = malloc(dual0(),!_open,sigs,null, XInt.dual(_fidxs)); + (_dual = d)._dual = this; // Cross link duals + // Return rdual + d._ret = _ret._terned ? _ret.dual() : _ret.rdual(); + // Signature rdual + for( int i=0; i<_sig.length; i++ ) + sigs[i] = _sig[i]._terned ? _sig[i].dual() : _sig[i].rdual(); + return d; + } + + // RHS is NIL; deep-dual when crossing the centerline + @Override public Type meet0() { + if( _nil==3 ) return this; + if( _nil==2 ) return make((byte)3,_open,_sig,_ret,_fidxs); + + return make((byte)3,_open,_sig,_ret,_fidxs); + } + + @Override public boolean isHigh() { return _nil <= 1 || (_nil==2 && _fidxs== XInt.EMPTY); } + + @Override boolean _isConstant() { return (_nil==2 && XInt.isConstant(_fidxs)) || (_nil==3 && _fidxs== XInt.EMPTY); } + + @Override boolean _isFinal() { return true; } + @Override boolean _isGLB(boolean mem) { return true; } + @Override TypeFunPtr _glb(boolean mem) { return this; } + + @Override TypeFunPtr _close( String name, HashMap<String, Type> TYPES ) { + Type[] sig = new Type[_sig.length]; + TypeFunPtr fun = malloc(_nil,true,sig,null,_fidxs); + // Now start the recursion + fun._ret = _ret._close(name, TYPES ); + for( int i=0; i<sig.length; i++ ) + sig[i] = _sig[i]._close(name, TYPES ); + return fun; + } + + // Replace recursively all TypeBuilders with cyclic TypeStructs + @Override Type _upgradeType(HashMap<String,Type> TYPES) { + Type[] sig = new Type[_sig.length]; + TypeFunPtr fun = malloc(_nil,_open,sig,null,_fidxs); + // Now start the recursion + fun._ret = _ret._upgradeType(TYPES); + for( int i=0; i<sig.length; i++ ) + sig[i] = _sig[i]._upgradeType(TYPES); + return fun; + } + + @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers + + public Type arg(int i) { return _sig[i]; } + public int[] fidxs() { return _fidxs; } + public Type ret() { return _ret; } + public int nargs() { return _sig.length; } + public int fidx() { return XInt.onlyBit(_fidxs); } + public int nfcns() { + if( XInt.isHigh(_fidxs) ) return Integer.MAX_VALUE; // Infinite choices + return XInt.bitCount(_fidxs); + } + + + // Used to walk over type structures + @Override public int nkids() { return _sig.length+1; } + @Override public Type at( int idx ) { + return idx == _sig.length ? _ret : _sig[idx]; + } + @Override public void set( int idx, Type t ) { + if( idx == _sig.length ) _ret = t; + else _sig[idx] = t; + } + + // Reserve tags for null/not and open/close, one fidx/bitset, 0-6 args + // Tags 0-5 - not,close,1 fidx, 0-5 args (+fidx) + // Tag 6 - not ,close+fidxs+nargs + // Tag 7 - null,close+fidxs+nargs + @Override int TAGOFF() { return 8; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + assert _open; // Open: Passing extra args is fine, and extras ignored. + if( _nil==2 && nargs()<6 && nfcns() == 1 ) { + baos.write(TAGOFFS[_type] + nargs()); + baos.packed2(fidx()); + } else { + baos.write(TAGOFFS[_type] + 6 + (_nil==2 ? 0 : 1)); + baos.packed1(nargs()); + XInt.packed(baos,_fidxs); + } + } + static TypeFunPtr packed( int tag, BAOS bais ) { + if( tag < 6 ) { + int fidx = bais.read(); + assert fidx < 64; // Need a larger fidx space + return malloc((byte)2,true,new Type[tag],null, XInt.make(fidx)); + } + byte nil = (byte)(tag==6 ? 2 : 3); + int nargs = bais.packed1(); + int[] fidxs = XInt.packed(bais); + return malloc(nil,true,new Type[nargs],null,fidxs); + } + + @Override + int hash() { + long hash = _ret.hashCode() ^ XInt.hash(_fidxs) ^ super.hash(); + for( Type arg : _sig ) + hash = hash*19 ^ arg.hashCode(); + return Utils.fold(hash); + } + + @Override + boolean eq(Type t) { + // Recursive; so use cyclic equals + if( !VISIT.isEmpty() ) { + assert TypeStruct.CEQUALS.isEmpty(); + boolean rez = cycle_eq(t); + TypeStruct.CEQUALS.clear(); + return rez; + } + TypeFunPtr fun = (TypeFunPtr)t; // Invariant + if( !super.eq(fun) || _open != fun._open || _fidxs != fun._fidxs || _sig.length != fun._sig.length ) return false; + if( _sig == fun._sig && _ret == fun._ret ) return true; + + if( !_ret.eq(fun._ret) ) return false; + for( int i = 0; i < _sig.length; i++ ) + if( !_sig[i].eq(fun._sig[i]) ) + return false; + return true; + } + @Override boolean cycle_eq(Type t) { + if( t._type != TFUNPTR ) return false; + TypeFunPtr fun = (TypeFunPtr)t; // Invariant + if( !super.eq(fun) || _open != fun._open || _fidxs != fun._fidxs || _sig.length != fun._sig.length ) return false; + if( _sig == fun._sig && _ret == fun._ret ) return true; + + // Check to see if we've ever compared this pair of types before; + // if so, then assume the cycle is equal here. + int pid = pid(fun); + if( TypeStruct.CEQUALS.find(pid)!= -1 ) return true; + TypeStruct.CEQUALS.push(pid); + if( !_ret.cycle_eq(fun._ret) ) return false; + for( int i = 0; i < _sig.length; i++ ) + if( !_sig[i].cycle_eq(fun._sig[i]) ) + return false; + return true; + } + + @Override public String str() { return "{"+printFIDX()+"}"; } + + @Override + SB _print(SB sb, BitSet visit, boolean html ) { + sb.p(x()).p("{ "); + if( _sig!=null ) + for( Type t : _sig ) + sb.p(t==null ? "---" : t.str()).p(" "); // Short form in signature + if( !_open ) sb.p("^ "); + _ret.print(sb.p(html ? "→ " : "-> "),visit,html).p(" #"); + // Print fidxs + return sb.p(printFIDX()).p("}").p(q()); + } + String printFIDX() { + String tilde = isHigh() ? "~" : ""; + String fidx = XInt.str(_fidxs); + return tilde+fidx; + } + + // Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) ) { int fidx = Long.numberOfTrailingZeros(fidxs); ... } + //public static long nextFIDX(long fidxs) { return fidxs & (fidxs-1); } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java new file mode 100644 index 00000000..4245626a --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java @@ -0,0 +1,217 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.AryInt; +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.Utils; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Integer Type + */ +public class TypeInteger extends TypeScalar { + /** + * Describes an integer *range* - everything from min to max; both min and + * max are inclusive. If min==max, this is a constant. + * <br> + * If min <= max, this is a below center (towards bottom). + * If min > max, this is an above center (towards top). + */ + public long _min, _max; + + public byte _widen; + + private static final Ary<TypeInteger> FREE = new Ary<>(TypeInteger.class); + private TypeInteger(long min, long max, byte widen) { super(TINT); init(min,max,widen); } + private TypeInteger init(long min, long max, byte widen) { + // Constants must always have a widen value of zero. Widening is only + // meaningful for ranges of values - which otherwide can increase in + // range size indefinitely. + assert min!=max || widen==0; // Constants always zero widen + _min = min; _max = max; _widen=widen; + return this; + } + public static TypeInteger malloc(long lo, long hi, byte widen) { return FREE.isEmpty() ? new TypeInteger(lo,hi,widen) : FREE.pop().init(lo,hi,widen); } + public static TypeInteger make(long lo, long hi) { return make(lo,hi,(byte)0); } + public static TypeInteger make(long lo, long hi, byte widen) { + TypeInteger i = malloc(lo,hi,lo==hi ? 0 : widen); + TypeInteger t2 = i.intern(); + return t2==i ? i : t2.free(i); + } + public TypeInteger makeWide(byte widen) { + if( widen == _widen ) return this; + if( _min == Long.MIN_VALUE && _max == Long.MAX_VALUE ) + return TypeInteger.BOT; + if( _min==_max ) return this; // Leave constants always narrow + return make(_min,_max,widen); + } + @Override TypeInteger free(Type t) { + TypeInteger i = (TypeInteger)t; + i._min = i._max = 0; + i._widen = 0; + i._hash = 0; + i._dual = null; + FREE.push(i); + return this; + } + + public Type same_but_slightly_wider_than( Type minType ) { + return _widen < 3 ? make(_min,_max, (byte)(_widen+1)) : minType; + } + // Force max widen + @Override public Type widen() { + return _widen==3 ? this : make(_min,_max,(byte)3); + } + public static TypeInteger constant(long con) { return make(con, con); } + + public final static TypeInteger ZERO= make(0,0); + public final static TypeInteger FALSE=ZERO; + public final static TypeInteger TRUE= make(1,1); + public final static TypeInteger TWO = make(2,2); // Shows up in some Simple tests as the starting argument + + public final static TypeInteger I1 = make(-1,0,(byte)3); + public final static TypeInteger I8 = make(-128,127,(byte)3); + public final static TypeInteger I16 = make(-32768,32767,(byte)3); + public final static TypeInteger I32 = make(-1L<<31,(1L<<31)-1,(byte)3); + public final static TypeInteger BOT = make(Long.MIN_VALUE,Long.MAX_VALUE,(byte)3); + public final static TypeInteger TOP = (TypeInteger)BOT.dual(); + + public final static TypeInteger U1 = make(0,1,(byte)3); + public final static TypeInteger BOOL= U1; + public final static TypeInteger U8 = make(0,255,(byte)3); + public final static TypeInteger U16 = make(0,65535,(byte)3); + public final static TypeInteger U32 = make(0,(1L<<32)-1,(byte)3); + + public final static TypeInteger FATBOOL = make(0,1,(byte)2); + + public static void gather(ArrayList<Type> ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); ts.add(ZERO); ts.add(FATBOOL); } + + @Override public String str() { + if( _isConstant() ) return ""+_min; + long lo = _min, hi = _max; + String x = ""; + if( hi < lo ) { + lo = _max; hi = _min; + x = "~"; + } + return x+_str(lo,hi); + } + private static String _str(long lo, long hi) { + if( lo==Long.MIN_VALUE && hi==Long.MAX_VALUE ) return "i64"; + if( lo== 0 && hi== 1 ) return "bool"; + if( lo== -1 && hi== 0 ) return "i1"; + if( lo== -128 && hi== 127 ) return "i8"; + if( lo== -32768 && hi== 32767 ) return "i16"; + if( lo== -1L<<31 && hi==(1L<<31)-1 ) return "i32"; + if( lo== 0 && hi== 255 ) return "u8"; + if( lo== 0 && hi== 65535 ) return "u16"; + if( lo== 0 && hi==(1L<<32)-1 ) return "u32"; + return "["+lo+"-"+hi+"]"; + } + + @Override public boolean isHigh () { return _min > _max; } + + @Override public int log_size() { + if( isHigh() ) return 0; // High types are dead, and should never hit code emission. + if( this==I8 || this==U8 || this==BOOL ) return 0; // 1<<0 == 1 bytes + if( this==I16 || this==U16 ) return 1; // 1<<1 == 2 bytes + if( this==I32 || this==U32 ) return 2; // 1<<2 == 4 bytes + if( this==BOT ) return 3; // 1<<3 == 8 bytes + if( isConstant() ) return log(_min); // just const here + int lo = log(_min); + int hi = log(_max); + return Math.max(lo,hi); + } + + private static int log(long x) { + if( -0xFFL <= x && x <= 0xFFL) return 0; + if( -0xFFFFL <= x && x <= 0xFFFFL) return 1; + if(-0xFFFFFFFFL <= x && x <= 0xFFFFFFFFL) return 2; + return 3; + } + + public long value() { assert isConstant(); return _min; } + + // AND-mask of forced zeros. e.g. unsigned types will return their mask; + // u8 will return 0xFF. But also a range of 16-18 (0x10-0x12) will return + // 0x13 - no value in the range {16,17,18} will allow bit 0x04 to be set. + public long mask() { + if( isHigh() ) return 0; + if( isConstant() ) return _min; + // Those bit positions which differ min to max + long x = _min ^ _max; + // Highest '1' bit in the differ set. Since the range is from min to + // max, all values below here are possible. + long ff1 = Long.highestOneBit(x); + // Make a all-1's mask from ff1, and set over the same bits (either min + // or max is ok). + long mask = _min | (ff1-1) | ff1; + return mask; + } + + @Override + public Type xmeet(Type other) { + // Invariant from caller: 'this' != 'other' and same class (TypeInteger) + TypeInteger i = (TypeInteger)other; // Contract + return make(Math.min(_min,i._min), Math.max(_max,i._max), (byte)Math.max(_widen,i._widen)); + } + + @Override TypeInteger xdual() { + return _min==_max ? this : malloc(_max,_min,(byte)(-_widen)); + } + + @Override public TypeInteger nonZero() { + if( isHigh() ) return this; + if( this==ZERO ) return null; // No sane answer + if( _min==0 ) return make(1,Math.max(_max,1),_widen); // specifically good on BOOL + if( _max==0 ) return make(_min,-1,_widen); + return this; + } + @Override public Type makeZero() { return ZERO; } + @Override boolean _isConstant() { return _min == _max; } + @Override boolean _isGLB(boolean mem) { return _glb(mem)==this; } + @Override Type _glb(boolean mem) { + if( !mem ) return BOT; + // GLB is not well-defined in memory; depends on the size of the memory field + if( _isConstant() ) return BOT; + return isHigh() ? dual() : this; + } + + // Reserve tags for I64,I32,U32,0,constant,generic + @Override int TAGOFF() { return 7; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + if( this==BOT ) baos.write(TAGOFFS[_type] + 0); + else if( this==I32 ) baos.write(TAGOFFS[_type] + 1); + else if( this==U32 ) baos.write(TAGOFFS[_type] + 2); + else if( this==BOOL) baos.write(TAGOFFS[_type] + 3); + else if( this==ZERO) baos.write(TAGOFFS[_type] + 4); + else if( isConstant() ) { + baos.write(TAGOFFS[_type] + 5); + baos.packed8(_min); + } else { + baos.write(TAGOFFS[_type] + 6); + baos.packed8(_min); + baos.packed8(_max); + baos.packed1(_widen); + } + } + + static Type packed( int tag, BAOS bais ) { + return switch( tag ) { + case 0 -> BOT; + case 1 -> I32; + case 2 -> U32; + case 3 -> BOOL; + case 4 -> ZERO; + case 5 -> constant(bais.packed8()); + case 6 -> make(bais.packed8(),bais.packed8(),(byte)bais.packed1()); + default -> throw Utils.TODO(); + }; + } + + @Override int hash() { return Utils.fold(_min) * Utils.fold(_max) + _widen; } + @Override public boolean eq( Type t ) { + return t instanceof TypeInteger i && _min==i._min && _max==i._max && _widen==i._widen; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMem.java new file mode 100644 index 00000000..3bbdf1df --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMem.java @@ -0,0 +1,252 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.*; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; + + +/** + * Represents a slice of memory corresponding to a set of aliases + */ +public class TypeMem extends Type { + + // Which slice of memory? + // 1 and TOP means no slice. + // 1 and BOT means all memory. + // N means alias slice#N. + public int _alias; + public Type _t; // Memory contents, some scalar type + public boolean _one; // Singleton memory, corresponding to TypeMemPtr._one. + public boolean _clz; // Class-object singleton memory; true singletons stay constant after construction. + public boolean _final;// Memory is only set in the constructor + + // Escaped aliases and functions - escaped into this memory. These are + // reachable from some chain of indirections from `_t`. Bit zero is + // used for mutable/immutable safety. Sign bit repeats infinitely. + public int[] _escFs; + public int[] _escAs; + + static public int CNT; + + private static final Ary<TypeMem> FREE = new Ary<>(TypeMem.class); + + private TypeMem() { super(TMEM); CNT++; } + private TypeMem init(int alias, Type t, boolean one, boolean clz, boolean xfinal, int[] fidxs, int[] aliases) { + _alias = alias; + _t = t; + _one = one; + _clz = clz; + // Public bulk memory is an aggregate of otherwise-unsplit aliases; + // a single final bit cannot describe it. Keep the lattice-dual high + // form distinct, and retain finality for private singleton memory and + // precise alias slices. + _final = t!=null && alias==1 && !one && !t.isHigh() ? false : xfinal; + + assert _escFs == null && _escAs == null; + _escFs = XInt.intern(fidxs); + _escAs = XInt.intern(aliases); + return this; + } + @Override TypeMem free(Type t) { + TypeMem mem = (TypeMem)t; + mem._alias= -99; + mem._dual = null; + mem._hash = 0; + mem._t = null; + mem._one = false; + mem._clz = false; + mem._final = false; + mem._escFs = null; + mem._escAs = null; + FREE.push(mem); + return this; + } + @Override boolean isFree() { return _alias == -99; } + + public static TypeMem malloc(int alias, Type t, boolean one, boolean clz, boolean xfinal, int[] fidxs, int[] aliases) { + return (FREE.isEmpty() ? new TypeMem() : FREE.pop()).init(alias,t,one,clz,xfinal,fidxs,aliases); + } + public static TypeMem make(int alias, Type t, boolean one, boolean clz, boolean xfinal, int[] fidxs, int[] aliases) { + if( fidxs ==null ) fidxs = XInt.EMPTY; + if( aliases==null ) aliases= XInt.EMPTY; + TypeMem f = malloc(alias,t,one,clz,xfinal, fidxs, aliases); + TypeMem f2 = f.intern(); + if( f2==f ) return f; + return VISIT.isEmpty() ? f2.free(f) : f2.delayFree(f); + } + public static TypeMem make(int alias, Type t) { return make(alias,t,false,false,false,null,null); } + // Make a private memory + public static TypeMem makePrivate(Type t) { return make(1,t,true,false,false,escapeFIDX(XInt.EMPTY,t), escapeAlias(XInt.EMPTY,t)); } + + public TypeMem makeFrom(Type t) { return make(_alias,t,_one,_clz,_final,_escFs,_escAs); } + public TypeMem makeFrom(int alias) { return make(alias,_t,_one,_clz,_final,_escFs,_escAs); } + public TypeMem makeFrom(int[] fidxes, int[] aliases) { + return make(_alias,_t,_one,_clz,_final,fidxes,aliases); + } + // Existing escapes plus `t` escapes + public TypeMem escapes(Type t) { + return make(_alias,_t,_one,_clz,_final, escapeFIDX(_escFs,t), escapeAlias(_escAs,t)); + } + // Existing escapes plus `t` escapes + public TypeMem escapesFrom(Type t) { + return make(_alias,_t,_one,_clz,_final, escapeFIDX(_escFs,t), escapeAlias(_escAs,t)); + } + // Existing escapes plus `t` escapes + public TypeMem escapesFrom(int alias, Type t) { + return make(alias,t,_one,_clz,_final, escapeFIDX(_escFs,t), escapeAlias(_escAs,t)); + } + public TypeMem escapesAliases(Type t) { + return make(_alias,_t,_one,_clz,_final, _escFs, escapeAlias(_escAs,t)); + } + + public static final TypeMem TOP = make(1, Type.TOP ,true ,true, true , XInt.EMPTY, XInt.EMPTY); + public static final TypeMem BOT = make(1, Type.BOTTOM,false,false,true , XInt.FULL , XInt.FULL ); + public static final TypeMem START= make(1,Type.BOTTOM,false,false,false, XInt.EMPTY, XInt.EMPTY); + public static final TypeMem SELF_MEM = make(1, TypeStruct.BOT); + + public static void gather(ArrayList<Type> ts) { ts.add(make(2,Type.NIL)); ts.add(make(2,TypeInteger.ZERO)); ts.add(BOT); ts.add(SELF_MEM); ts.add(START); } + + @Override + TypeMem xmeet(Type t) { + TypeMem that = (TypeMem) t; // Invariant: TypeMem and unequal + if( this==TOP ) return that; + if( that==TOP ) return this; + if( this==BOT ) return BOT; + if( that==BOT ) return BOT; + // Treat a alias-1 has either very-high or very-low, according to "_t" + int alias = _alias==that._alias ? _alias // Same-same + : this._alias==1 && this._t.isHigh() ? that._alias // this is high-1 alias, so take that + : that._alias==1 && that._t.isHigh() ? this._alias // that is high-1 alias, so take this + : 1; + Type mt = _t.meet(that._t); + int[] fidxs = XInt.meet( _escFs, that._escFs ); + int[] aliases= XInt.meet( _escAs, that._escAs ); + // Singleton & class facts are kept only when both sides agree. + + // Final is kept if either side is final, merging a final & non-final + // memory can be argued either way here: + + // - Keep it: merging a final & non-final memory means a later Store + // update will be flagged as an error. + // - Lose it: merging a final & non-final memory means a later Load + // cannot be promised the memory is unchanging. + return make(alias, mt, _one & that._one, _clz & that._clz, _final | that._final, fidxs, aliases); + } + + @Override + Type xdual() { return malloc(_alias,_t.dual(),!_one,!_clz,!_final, XInt.dual(_escFs), XInt.dual(_escAs)); } + + @Override TypeMem rdual() { + if( _dual!=null ) return dual(); + assert !_terned; + TypeMem d = malloc(_alias,null,!_one,!_clz,!_final, XInt.dual(_escFs), XInt.dual(_escAs)); + (_dual = d)._dual = this; // Cross link duals + d._t = _t._terned ? _t.dual() : _t.rdual(); + if( d._alias==1 && !d._one && !d._t.isHigh() ) d._final = false; + return d; + } + + @Override public TypeMem makeZero() { return TOP; } + @Override public boolean isHigh() { return _t.isHigh(); } + @Override boolean _isConstant() { return _one && _clz && _alias!= 1 && _t._isConstant(); } + @Override public int log_size() { throw Utils.TODO(); } + @Override boolean _isFinal() { return _t._isFinal(); } + @Override boolean _isGLB(boolean mem) { return _t._isGLB(true); } + @Override public Type _glb(boolean mem) { return make(_alias,_t._glb(true),_one,_clz,_final,_escFs,_escAs); } + + @Override TypeMem _close( String name, HashMap<String, Type> TYPES ) { return malloc(_alias,_t._close(name, TYPES ),_one,_clz,_final,_escFs,_escAs); } + + @Override Type _upgradeType(HashMap<String,Type> TYPES) { + return make(_alias,_t._upgradeType(TYPES),_one,_clz,_final,_escFs,_escAs); + } + + @Override int hash() { return 9876543 + _alias + _t.hashCode() + (_one ? 8196 : 0) + (_clz ? 4098 : 0) + (_final ? 16392 : 0) + XInt.hash(_escFs) + XInt.hash(_escAs); } + + @Override boolean eq(Type t) { + TypeMem that = (TypeMem) t; // Invariant + return _alias == that._alias && _t == that._t && _one == that._one && _clz == that._clz && _final == that._final && _escFs == that._escFs && _escAs == that._escAs; + } + + @Override boolean cycle_eq(Type t) { + TypeMem that = (TypeMem) t; // Invariant + return _alias == that._alias && _one == that._one && _clz == that._clz && _final == that._final && _escFs == that._escFs && _escAs == that._escAs && _t.cycle_eq(that._t); + } + + @Override public int nkids() { return 1; } + @Override public Type at( int idx ) { return _t; } + @Override public void set( int idx, Type t ) { _t = t; } + // one tag for alias#1, one tag for generic alias. + // tag bit for _one, another bit for _final. + @Override int TAGOFF() { return 12; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + baos.write(TAGOFFS[_type] + + (_alias==1 ? 0 : 1) + + (!_final ? 0 : 2) + + (!_one ? 0 : (_clz ? 4 : 8))); + if( _alias!=1 ) { + // generic alias + assert _alias <= 255; + baos.packed1( _alias ); + } + XInt.packed(baos,_escFs); + XInt.packed(baos,_escAs); + } + static TypeMem packed( int tag, BAOS bais ) { + int alias = (tag&1)==0 ? 1 : bais.packed1(); + boolean xfinal= (tag&2)==2; + boolean one = (tag&12)!=0; + boolean clz = (tag&4)==4; + int[] escFs = XInt.packed(bais); + int[] escAs = XInt.packed(bais); + return malloc(alias,null,one,clz,xfinal,escFs,escAs); + } + + @Override public SB _print(SB sb, BitSet visit, boolean html) { + sb.p('#'); + if( !_final ) sb.p('!'); // Mutable memory + if( _one ) sb.p(_clz ? 'z' : '-'); + if( _alias==0 ) return sb.p(_t._type==TTOP ? "TOP" : "BOT"); + sb.p(_alias).p(":["); + _t.print(sb,visit,html).p(","); + XInt.str(sb,_escFs).p(","); + return XInt.str(sb,_escAs).p("]"); + } + + @Override public String str() { + SB sb = new SB().p('#'); + if( !_final ) sb.p('!'); // Mutable memory + if( _one ) sb.p(_clz ? 'z' : '-'); + if( _alias==0 ) return sb.p(_t._type==TTOP ? "TOP" : "BOT").toString(); + sb.p(_alias).p(":[").p(_t.str()).p(","); + XInt.str(sb,_escFs).p(","); + return XInt.str(sb,_escAs).p("]").toString(); + } + + + // Tracking bulk escaped aliases and fidxs + + // Escape all FIDXs from t + private static int[] escapeFIDX( int[] xs, Type t ) { + return switch(t) { + case TypeMemPtr tmp -> escapeFIDX(xs,tmp._obj); + case TypeStruct ts -> ts._open + ? XInt.FULL // Open structs can have any fields + : XInt.meet(xs,ts.fidxs()); + case TypeFunPtr tfp -> XInt.meet(xs,tfp.fidxs()); + case TypeMem mem -> XInt.meet(xs,mem._escFs); + case Type tt -> tt == BOTTOM ? XInt.FULL : xs; + }; + } + + private static int[] escapeAlias( int[] xs, Type t ) { + return switch(t) { + case TypeMemPtr tmp -> escapeAlias(xs,tmp._obj); + case TypeStruct ts -> ts._open + ? XInt.FULL // Open structs can have any fields + : XInt.meet(xs,ts.aliases()); + case TypeMem mem -> XInt.meet(xs,mem._escAs); + case Type tt -> tt == BOTTOM ? XInt.FULL : xs; + }; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java new file mode 100644 index 00000000..67d80591 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java @@ -0,0 +1,162 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.AryInt; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.BAOS; +import java.util.*; + +/** + * Represents a Pointer to memory. + * + * Null is generic pointer to non-existent memory. + * *void is a non-Null pointer to all possible refs, both structs and arrays. + * Pointers can be to specific struct and array types, or a union with Null. + * The distinguished *$BOT ptr represents union of *void and Null. + * The distinguished *$TOP ptr represents the dual of *$BOT. + */ +public class TypeMemPtr extends TypeNil { + // A TypeMemPtr is pair (obj,nil) + // where obj is a TypeStruct, possibly TypeStruct.BOT/TOP + // where nil is an explicit null is allowed or not + + // Examples: + // (Person,false) - a not-nil Person + // (Person,true ) - a Person or a nil + // (BOT ,false) - a not-nil void* (unspecified struct) + // (TOP ,true ) - a nil + + public TypeStruct _obj; + public boolean _one; // Singleton pointer value; the pointer itself is constant. + public boolean _pub; // Escaped/public pointer. False means constructor-private. + + private static final Ary<TypeMemPtr> FREE = new Ary<>(TypeMemPtr.class); + private TypeMemPtr(byte nil, TypeStruct obj, boolean one, boolean pub) { super(TMEMPTR,nil); init(nil,obj,one,pub); } + private TypeMemPtr init(byte nil, TypeStruct obj, boolean one, boolean pub) { _nil=nil; _obj=obj; _one=one; _pub=pub; return this; } + // Return a filled-in TypeMemPtr; either from free list or alloc new. + static TypeMemPtr malloc(byte nil, TypeStruct obj, boolean one, boolean pub) { + return FREE.isEmpty() ? new TypeMemPtr(nil,obj,one,pub) : FREE.pop().init(nil,obj,one,pub); + } + + // All fields directly listed + public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one) { + return make(nil,obj,one,true); + } + public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one, boolean pub) { + TypeMemPtr tmp = malloc(nil, obj, one, pub); + TypeMemPtr t2 = tmp.intern(); + if( t2==tmp ) return tmp; + return VISIT.isEmpty() ? t2.free(tmp) : t2.delayFree(tmp); + } + @Override TypeMemPtr free(Type t) { + TypeMemPtr tmp = (TypeMemPtr)t; + tmp._nil = -99; + tmp._obj = null; + tmp._one = false; + tmp._pub = true; + tmp._dual = null; + tmp._hash = 0; + FREE.push(tmp); + return this; + } + @Override boolean isFree() { return _obj==null; } + + static TypeMemPtr make(byte nil, TypeStruct obj) { return make(nil, obj, false); } + public static TypeMemPtr make (TypeStruct obj) { return make((byte)2, obj); } + public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); } + public static TypeMemPtr makePrivate (TypeStruct obj) { return make((byte)2, obj, false, false); } + + public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one, _pub); } + public TypeMemPtr makeNullable() { return makeFrom((byte)3); } + @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one, _pub); } + public TypeMemPtr makeHigh(byte nil) { return make(nil,_obj.makeHigh(),false); } + @Override public Type nonZero() { return makeFrom((byte)(_nil <= 1 ? 1 : 2)); } + + // An abstract pointer, pointing to either a Struct or an Array. + // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not} + public static final TypeMemPtr BOT = make((byte)3, TypeStruct.BOT); + public static final TypeMemPtr TOP = BOT.dual(); + public static final TypeMemPtr NOTBOT = make((byte)2,TypeStruct.BOT); + + public static final TypeMemPtr TEST= make((byte)2, TypeStruct.TEST); + public static void gather(ArrayList<Type> ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); } + + @Override + public TypeNil xmeet(Type t) { + TypeMemPtr that = (TypeMemPtr) t; + assert !isFree() && !that.isFree(); + // Can't keep _one if mixing two unequal singletons; the result is not + // either singleton. Once either side is public, the meet is public. + boolean one = _one && that._one && (_obj==that._obj || !(_obj.isConAry() || that._obj.isConAry())); + return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), one, _pub | that._pub); + } + + @Override + TypeMemPtr xdual() { return malloc( dual0(), _obj.dual(), !_one, !_pub); } + + @Override TypeMemPtr rdual() { + if( _dual!=null ) return dual(); + assert !_terned; + TypeMemPtr d = malloc(dual0(), null, !_one, !_pub); + (_dual = d)._dual = this; // Cross link duals + d._obj = _obj._terned ? _obj.dual() : _obj.rdual(); + return d; + } + + // RHS is NIL; do not deep-dual when crossing the center line + @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj); } + + @Override boolean _isConstant() { return _one; } + @Override boolean _isFinal() { return _obj._isFinal(); } + @Override TypeMemPtr _makeRO() { return makeFrom(_obj._makeRO()); } + @Override boolean _isGLB(boolean mem) { return _obj._isGLB(true); } + @Override TypeMemPtr _glb(boolean mem) { return make((byte)3,_obj.glb2()); } + @Override TypeMemPtr _close( String name, HashMap<String, Type> TYPES ) { return malloc(_nil,_obj._close(name, TYPES ),_one,_pub); } + + @Override Type _upgradeType(HashMap<String,Type> TYPES) { + return makeFrom((TypeStruct)_obj._upgradeType(TYPES)); + } + + @Override public int log_size() { return 3; } // (1<<3)==8-byte pointers + + @Override int hash() { return _obj.hashCode() ^ super.hash() ^ (_one ? 2048 : 0) ^ (_pub ? 4096 : 0); } + + @Override boolean eq(Type t) { + TypeMemPtr ptr = (TypeMemPtr)t; // Invariant + return super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj == ptr._obj; + } + @Override boolean cycle_eq(Type t) { + if( t._type != TMEMPTR ) return false; + TypeMemPtr ptr = (TypeMemPtr)t; // Invariant + return super.eq(ptr) && _one == ptr._one && _pub == ptr._pub && _obj.cycle_eq(ptr._obj); + } + + @Override public int nkids() { return 1; } + @Override public Type at( int idx ) { return _obj; } + @Override public void set( int idx, Type t ) { _obj = (TypeStruct)t; } + + // Reserve tags for null/not, one/general and public/private + @Override int TAGOFF() { return 8; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + assert _nil>=2; + baos.write(TAGOFFS[_type] + + (_nil==2 ? 0 : 1) + + (!_one ? 0 : 2) + + (!_pub ? 0 : 4) ); + } + static TypeMemPtr packed( int tag, BAOS bais ) { + return malloc((byte)((tag&1)+2),null,(tag&2)==2,(tag&4)==4); + } + + @Override public String str() { + if( this== NOTBOT) return "*void"; + if( this== BOT) return "*void?"; + return x()+"*"+(_obj==null?"---":_obj.str())+q(); + } + + @Override SB _print(SB sb, BitSet visit, boolean html ) { + if( this== NOTBOT) return sb.p("*void"); + if( this== BOT) return sb.p("*void?"); + return _obj.print(sb.p(x()).p("*"),visit,html).p(q()); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeNil.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeNil.java new file mode 100644 index 00000000..32505aa2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeNil.java @@ -0,0 +1,62 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.ArrayList; + +/** + * Nil-able Scalar types + */ +public abstract class TypeNil extends TypeScalar { + // 0 = high-subclass choice nil + // 1 = high-subclass no nil + // 2 = low -subclass no nil + // 3 = low -subclass also nil + byte _nil; + + TypeNil(byte t, byte nil ) { super(t); _nil = nil; } + + public static void gather(ArrayList<Type> ts) { } + + abstract TypeNil makeFrom(byte nil); + + byte xmeet0(TypeNil that) { return (byte)Math.max(_nil,that._nil); } + + byte dual0() { return (byte)(3-_nil); } + + // RHS is NIL + abstract Type meet0(); + + // RHS is XNIL + Type meetX() { + return _nil==0 ? XNIL : (_nil<=2 ? TypePtr.NPTR : TypePtr.PTR); + } + + Type nmeet(TypeNil tn) { + // Invariants: both are TypeNil subclasses and unequal classes. + // If this is TypePtr, we went to TypePtr.nmeet and not here. + // If that is TypePtr, this is not (invariant); reverse and go again. + if( tn instanceof TypePtr ts ) return ts.nmeet(this); + + // Two mismatched TypeNil, no Scalar. + if( _nil==0 && tn._nil==0 ) return XNIL; + if( _nil<=2 && tn._nil<=2 ) return TypePtr.NPTR; + return TypePtr.PTR; + } + + @Override public boolean isHigh () { return _nil <= 1; } + @Override public boolean isHighOrConst() { return isHigh() || isConstant(); } + + public boolean notNull() { return _nil==1 || _nil==2; } + public boolean nullable() { return _nil==3; } + + @Override boolean _isConstant() { return false; } + @Override Type _glb(boolean mem) { return Type.NIL; } + + final String q() { return _nil == 1 || _nil == 2 ? "" : "?"; } + final String x() { return isHigh() ? "~" : ""; } + + int hash() { return _nil<<17; } + + @Override boolean eq(Type ptr) { return ptr instanceof TypeNil nil && _nil ==nil._nil; } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypePtr.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypePtr.java new file mode 100644 index 00000000..3ff9765b --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypePtr.java @@ -0,0 +1,66 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.AryInt; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import com.seaofnodes.simple.util.BAOS; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; + +/** + * Represents a Scalar; a single register-sized value. + */ +public class TypePtr extends TypeNil { + private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"}; + + private TypePtr(byte nil) { super(TPTR,nil); } + + // An abstract pointer, pointing to either a Struct or an Array. + // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not} + public static TypePtr XPTR = new TypePtr((byte)0).intern(); + public static TypePtr XNPTR= new TypePtr((byte)1).intern(); + public static TypePtr NPTR = (TypePtr)XNPTR.dual(); + public static TypePtr PTR = (TypePtr)XPTR.dual(); + private static final TypePtr[] PTRS = new TypePtr[]{XPTR,XNPTR,NPTR,PTR}; + + public static void gather(ArrayList<Type> ts) { ts.add(PTR); ts.add(NPTR); } + + TypeNil makeFrom(byte nil) { throw Utils.TODO(); } + + @Override public TypeNil xmeet(Type t) { + TypePtr that = (TypePtr) t; + return PTRS[xmeet0(that)]; + } + + @Override TypePtr xdual() { return new TypePtr((byte)(3-_nil)); } + + // High scalar loses, low scalar wins + @Override TypeNil nmeet(TypeNil tn) { + if( _nil==0 ) return tn; // High scalar loses + if( _nil==1 ) return tn.makeFrom(xmeet0(tn)); // High scalar loses + if( _nil==2 ) return tn._nil==3 ? PTR : NPTR; // Low scalar wins + return PTR; // this + } + + + // RHS is NIL + @Override Type meet0() { return isHigh() ? NIL : PTR; } + // RHS is XNIL + // 0->xscalar, 1->nscalar, 2->nscalar, 3->scalar + @Override Type meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); } + + boolean _isGLB(boolean mem) { return this==PTR; } + @Override TypePtr _glb(boolean mem) { return PTR; } + + // Reserve 2 tags, PTR and NPTR + @Override int TAGOFF() { return 2; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + assert _nil>=2; + baos.write(TAGOFFS[_type] + (_nil==2 ? 0 : 1)); + } + static TypePtr packed( int tag ) { return tag==0 ? NPTR : PTR; } + + @Override public String str() { return STRS[_nil]; } + @Override public SB _print(SB sb, BitSet visit, boolean html) { return sb.p(str()); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeRPC.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeRPC.java new file mode 100644 index 00000000..a39ebc04 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeRPC.java @@ -0,0 +1,106 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.BAOS; +import java.util.*; + +/** + * Return Program Control or Return PC or RPC + */ +public class TypeRPC extends Type { + + // A set of CallEndNode IDs (or StopNode); commonly just one. High-bit + // XInt sets represent complements/infinite sets. + public int[] _rpcs; + + private static final Ary<TypeRPC> FREE = new Ary<>(TypeRPC.class); + private TypeRPC init(int[] rpcs) { _rpcs = XInt.intern(rpcs); return this; } + private TypeRPC() { super(TRPC); } + private static TypeRPC malloc(int[] rpcs) { + return (FREE.isEmpty() ? new TypeRPC() : FREE.pop()).init(rpcs); + } + private static TypeRPC make(int[] rpcs) { + TypeRPC rpc = malloc(rpcs); + TypeRPC t2 = rpc.intern(); + return t2==rpc ? rpc : t2.free(rpc); + } + @Override TypeRPC free(Type t) { + TypeRPC rpc = (TypeRPC)t; + rpc._rpcs = null; + rpc._dual = null; + rpc._hash = 0; + FREE.push(rpc); + return this; + } + @Override boolean isFree() { return _rpcs==null; } + + public static TypeRPC constant(int cend) { + return make(XInt.make(cend)); + } + + public int rpc() { + return XInt.onlyBit(_rpcs); + } + + + public static final TypeRPC BOT = make(XInt.FULL); + private static final TypeRPC TEST2 = constant(2); + private static final TypeRPC TEST3 = constant(2); + public static final TypeRPC EMPTY = make(XInt.EMPTY); + + public static void gather(ArrayList<Type> ts) { ts.add(BOT); ts.add(TEST2); ts.add(TEST3); } + + @Override public TypeRPC makeZero() { return EMPTY; } + + + @Override public String str() { + if( _rpcs == XInt.FULL ) return "$[ALL]"; + if( _rpcs == XInt.EMPTY ) return "$[]"; + return "$"+XInt.str(_rpcs); + } + + @Override + public TypeRPC xmeet(Type other) { + TypeRPC rpc = (TypeRPC)other; + return make(XInt.meet(_rpcs,rpc._rpcs)); + } + + @Override TypeRPC xdual() { return malloc(XInt.dual(_rpcs)); } + @Override Type rdual() { + TypeRPC d = xdual(); + (_dual = d)._dual = this; + return d; + } + + @Override boolean _isConstant() { return XInt.isConstant(_rpcs); } + @Override boolean _isGLB(boolean mem) { return true; } + + // Reserve tags for ALL, singleton, generic + @Override int TAGOFF() { return 3; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + if( this==BOT ) { + baos.write( TAGOFFS[_type] + 0 ); + } else if( XInt.isConstant(_rpcs) ) { // Singleton + baos.write(TAGOFFS[_type] + 1); + baos.packed2(rpc()); + } else { // Generic + assert !XInt.isHigh(_rpcs); // Not serializing above-center RPC + baos.write(TAGOFFS[_type] + 2); + XInt.packed(baos,_rpcs); + } + } + static TypeRPC packed( int tag, BAOS bais ) { + if( tag==0 ) return BOT; + if( tag==1 ) return malloc(XInt.make(bais.packed2())); + return malloc(XInt.packed(bais)); + } + + @Override + int hash() { return XInt.hash(_rpcs); } + @Override + public boolean eq( Type t ) { + TypeRPC rpc = (TypeRPC)t; // Contract + return _rpcs==rpc._rpcs; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeScalar.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeScalar.java new file mode 100644 index 00000000..4fec3c33 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeScalar.java @@ -0,0 +1,56 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.BAOS; +import com.seaofnodes.simple.util.SB; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; + +/** + * The envelope for values which can live in an ordinary register or memory + * field: integers, floats, memory pointers, and function pointers. + * + * `BOT` means any scalar value and `TOP` means no scalar value. They sit + * strictly inside the global {@link Type#BOTTOM}/{@link Type#TOP} pair, which + * also includes non-values such as control and memory. + */ +public class TypeScalar extends Type { + protected TypeScalar(byte type) { super(type); } + private TypeScalar() { this(TSCALAR); } + + public static final TypeScalar BOT = new TypeScalar().intern(); + public static final TypeScalar TOP = BOT.dual(); + + public static void gather(ArrayList<Type> ts) { ts.add(BOT); } + + /** Meet two different scalar families. */ + final Type smeet(TypeScalar scalar) { + if( _type == TSCALAR ) return isHigh() ? scalar : this; + if( scalar._type == TSCALAR ) return scalar.isHigh() ? this : scalar; + return BOT; + } + + @Override Type xmeet(Type t) { + TypeScalar scalar = (TypeScalar)t; + return isHigh() ? scalar : this; + } + + @Override TypeScalar xdual() { return new TypeScalar(); } + @Override public boolean isHigh() { return this == TOP; } + @Override boolean _isGLB(boolean mem) { return this == BOT; } + @Override Type _glb(boolean mem) { return BOT; } + + @Override int TAGOFF() { return 1; } + @Override public void packed(BAOS baos, HashMap<String,Integer> strs) { + assert this == BOT; + baos.write(TAGOFFS[_type]); + } + static TypeScalar packed(int tag) { + assert tag == 0; + return BOT; + } + + @Override public String str() { return isHigh() ? "~scalar" : "scalar"; } + @Override SB _print(SB sb, BitSet visit, boolean html) { return sb.p(str()); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java new file mode 100644 index 00000000..f51ab859 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -0,0 +1,660 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.util.*; +import java.util.*; + +/** + * Represents a struct type. + */ +public class TypeStruct extends Type { + + // A Struct has a name and a set of fields; the fields themselves have + // names, types and aliases. The name is essentially another field, + // immutable and type ignored (BOT). + + /** + * `_open` and `_fref` are independent lattice coordinates. + * + * `_open` says the structural tail is not known yet. On the low side an + * open struct has infinitely many implicit BOT fields; its dual has an + * implicit TOP tail. A real declaration is therefore open while its body + * is being parsed: + * + * ```text + * struct S { int x; /* more declared fields may follow *\/ } + * ``` + * + * `_fref` says no actual declaration has contributed to the type. The + * parser may still invent fields demanded by uses, leaving a useful open + * structural constraint: + * + * ```text + * S? p; p.x; // fref S { BOT x; ... } + * ``` + * + * Meeting that constraint with a real definition ANDs `_fref` off while + * retaining and checking the invented field. Closing recursive types must + * not by itself clear `_fref`; closure discovers shape, not a declaration. + */ + + public String _name; // Struct name + public boolean _open; + public boolean _fref; + public Field[] _fields; + + private TypeStruct() { super(TSTRUCT); } + private static final Ary<TypeStruct> FREE = new Ary<>(TypeStruct.class); + private TypeStruct init(String name, boolean open, boolean fref, Field[] fields) { + // Check for const array element name with mutable field + //assert !name.startsWith("[~]") || fields.length!=2 || fields[1]._t==null || (fields[1]._final != fields[1]._t.isHigh()); + _name = name; + _open = open; + _fref = fref; + _fields = fields; + return this; + } + // Return a filled-in TypeStruct; either from free list or alloc new. + private static TypeStruct malloc(String name, boolean open, boolean fref, Field[] fields) { + return (FREE.isEmpty() ? new TypeStruct() : FREE.pop()).init(name,open,fref,fields); + } + // Free ts; return this. + @Override TypeStruct free(Type t) { + TypeStruct ts = (TypeStruct)t; + assert !ts.isFree() && !ts._terned; + ts._name = null; + ts._fields = null; + ts._offs = null; + ts._dual = null; + ts._hash = 0; + ts._aliases = null; + ts._fidxs = null; + FREE.push(ts); + return this; + } + @Override boolean isFree() { return _fields==null; } + + + // All fields directly listed + public static TypeStruct make(String name, boolean open, Field... fields) { + return make(name,open,false,fields); + } + public static TypeStruct make(String name, boolean open, boolean fref, Field... fields) { + TypeStruct ts = malloc(name, open, fref, fields); + TypeStruct t2 = ts.intern(); + if( t2==ts ) return ts; + return VISIT.isEmpty() ? t2.free(ts) : ts.delayFree(ts); + } + + // Summary aliases, after interning. Skips private + // fields which need to be explicitly loaded. + private int[] _aliases; + public int[] aliases() { + if( _aliases != null ) return _aliases; + if( _fields.length==0 ) return (_aliases=XInt.EMPTY); + assert _terned; + int maxAlias = 0; + for( Field f : _fields ) + maxAlias = Math.max(maxAlias,f._alias); + int len = XInt.idx(maxAlias)+1; + if( (maxAlias&31)==31 ) len++; + int[] xs = XInt.free(len); + for( Field f : _fields ) + if( f._fname.charAt(0) != '_' && // Skip private fields. + !hiddenInit(f) && // Skip private allocation helper. + f._t != Type.TOP ) // Skip uninit fields + xs[XInt.idx(f._alias)] |= XInt.mask(f._alias); + return (_aliases=XInt.intern(xs)); + } + + // Summary fidxs, after interning. Skips private + // fields which need to be explicitly loaded. + private int[] _fidxs; + public int[] fidxs() { + if( _fidxs != null ) return _fidxs; + assert _terned; + int[] xs = XInt.EMPTY; + for( Field f : _fields ) + if( f._fname.charAt(0) != '_' && // Skip private fields. + !hiddenInit(f) && // Skip private allocation helper. + f._t instanceof TypeFunPtr tfp ) // Looking for TFP + xs = XInt.meet(xs,tfp._fidxs); + return (_fidxs = xs); + } + + // With a user constructor, the same-named function field is the hidden + // default <init> allocation helper, not part of the public class surface. + public boolean hiddenInit(Field fld) { + if( field("<ctor>") == null || !_name.startsWith("class:") ) return false; + int idx = _name.lastIndexOf('.'); + String name = _name.substring(idx<0 ? "class:".length() : idx+1); + return fld._fname.equals(name); + } + + // New open struct with no fields + public static TypeStruct open( String name ) { return make(name,true); } + public static TypeStruct forward( String name ) { return make(name,true,true); } + public TypeStruct defined() { return _fref ? make(_name,_open,false,_fields) : this; } + + // Array, variant body + public static TypeStruct makeAry(String name, TypeInteger len, int lenAlias, Type body, int bodyAlias, boolean efinal) { + return make(name,false, + Field.make("#" , len, lenAlias, true ), + Field.make("[]",body,bodyAlias,efinal)); + } + // Array, known constant body + public static TypeStruct makeAry(String name, int lenAlias, int bodyAlias, TypeConAry con) { + Type len = TypeInteger.constant(con.len()); + return make(name,false, + Field.make("#" ,len , lenAlias,true), + Field.make("[]" ,con.elem(),bodyAlias,true), + Field.make("[con]",con ,bodyAlias,true)); + } + + public TypeStruct makeHigh() { + Field[] fs = new Field[_fields.length]; + for( int i=0; i<_fields.length; i++ ) + fs[i] = _fields[i].makeFrom(Type.TOP); + return make(_name,_open,_fref,fs); + } + + public TypeStruct makeInit() { + Field[] fs = new Field[_fields.length]; + for( int i=0; i<_fields.length; i++ ) { + Type zero = _fields[i]._t.makeZero(); + fs[i] = _fields[i].makeFrom(_fields[i]._t != Type.BOTTOM && _fields[i]._t != Type.TOP && zero.isa(_fields[i]._t) ? zero : Type.TOP); + } + return make(_name,_open,_fref,fs); + } + + public TypeStruct add( Field f ) { + assert find(f._fname)==-1; // No double field names + Field[] flds = Arrays.copyOf(_fields,_fields.length+1); + flds[_fields.length] = f; + return make(_name,_open,_fref,flds); + } + public TypeStruct replace( Field f ) { + Field[] flds = Arrays.copyOf(_fields,_fields.length); + flds[find(f._fname)] = f; + return make(_name,_open,_fref,flds); + } + public TypeStruct remove( int i ) { + Field[] flds = new Field[_fields.length-1]; + System.arraycopy(_fields, 0,flds,0,i); + System.arraycopy(_fields,i+1,flds,i,flds.length-i); + return make(_name,_open,_fref,flds); + } + + public TypeStruct addOrUpdate(Field fld) { + int idx = find(fld._fname); + int len = idx == -1 ? (idx=_fields.length)+1 : _fields.length; + Field[] flds = Arrays.copyOf(_fields,len); + flds[idx] = fld; + return make(_name,_open,_fref,flds); + } + + public final TypeStruct close() { + return (TypeStruct)recurOpen()._close(_name, null).recurClose(); + } + @Override TypeStruct _close( String name, HashMap<String, Type> TYPES ) { + assert (name==null) != (TYPES==null); // One or the other + String namePlusFinal = isAry() ? (_name+(_fields[1]._final ? "~" : "!")).intern() : _name; + TypeStruct ts = (TypeStruct)VISIT.get(namePlusFinal); + if( ts!=null ) return ts; + TypeStruct base = name==null ? (TypeStruct)TYPES.get(_name) : this; + if( base == null ) base = this; + boolean open = base._fref ? base._open : name != _name && name != null && _open; + ts = base.recurPre(namePlusFinal, _name, open, base._fref ); + Field[] flds = ts._fields; + // Now start the recursion + for( int i=0; i<flds.length; i++ ) + flds[i].setType(base._fields[i]._t._close(name, TYPES)); + + // Note: this whole complex shenanigans here is because there are + // actually two flavors of any array (non-/final contents) but I + // allow the parser to represent both with the same name string. + if( isAry() && _fields[1]._final && !flds[1]._final ) // This is a final array variant + flds[1] = flds[1].makeFrom(true); // Add final to the base TYPES non-final array + + return ts; + } + + // Needs full recursive treatment; e.g. + // struct LL { LL? next; int !q; } // Q is mutable field. + // Stored into a not-mutable, so need that version also: + // struct LL { LL? next; int q; } // Q is NOT mutable field. + // Now upgrade: + // it is not the default version from TYPES. + // it is fully cyclic + // + // Evil question: has no *open* internal fields, so can I assume it is fully correct and never needs upgrade? + // + // upgrades are for well-formed types with internal ptrs to early open + // versions of other types, as happens in all the CoRecur tests. + // During the co-recur tests, can I make non-default cycles with + // weaker versions of other early types? + // Seems plausible... + @Override Type _upgradeType(HashMap<String,Type> TYPES) { + TypeStruct base = (TypeStruct)TYPES.get(_name); + if( this == base ) return this; // Already the good form + TypeStruct ts = (TypeStruct)VISIT.get(_uid); + if( ts!=null ) return ts; + // Main work: replace open with closed + if( _open ) + return base; + // Build recursive version + ts = recurPre(_uid,_name,false,_fref); + Field[] flds = ts._fields; + // Now start the recursion + for( int i=0; i<flds.length; i++ ) + flds[i].setType(_fields[i]._t._upgradeType(TYPES)); + return ts; + } + + static final AryInt CEQUALS = new AryInt(); + + public static final String TOPNAME = "$TOP", BOTNAME = "$BOT"; + public static final TypeStruct TOP = make("$TOP",false); + public static final TypeStruct BOT = TOP.dual(); + public static final TypeStruct TEST= make("test",false,Field.TEST); + private static final TypeStruct ARY = makeAry("[]i64",TypeInteger.U32,-1,TypeInteger.BOT,-2,false); + private static final TypeStruct STR = makeAry("[]u8" ,TypeInteger.U32,-1,TypeInteger.U8 ,-4,false); + private static final TypeStruct ABC = makeAry("[]u8",-1,-4,TypeConAryB.ABC); + // Representative unresolved forms for the exhaustive lattice tests: + // bare forward ref, forward ref constrained by a field use, and definition. + private static final TypeStruct FREF0 = forward("%FREF"); + private static final TypeStruct FREF1 = FREF0.add(Field.make("x",Type.BOTTOM,-20,false)); + private static final TypeStruct FDEF1 = make("%FREF",false,Field.make("x",TypeInteger.BOT,-20,false)); + + // A pair of self-cyclic types + private static final TypeStruct SINT0 = make("%SINT",false); + private static final TypeStruct SFLT0 = make("%SFLT",false); + public static final TypeStruct SINT1 = SINT0.add(Field.make("a", TypeInteger.U32, -1, false)).add(Field.make("s2",TypeMemPtr.make((byte)2,SFLT0),-2, false)); + public static final TypeStruct SFLT1 = SFLT0.add(Field.make("b", TypeFloat .F32, -3, false)).add(Field.make("s1",TypeMemPtr.make((byte)2,SINT1),-4, false)); + + @Override public boolean isHigh() { return _name==TOPNAME; } + + public static void gather(ArrayList<Type> ts) { + ts.add(BOT); + ts.add(TEST); + ts.add(ARY); + ts.add(STR); + ts.add(ABC); + ts.add(FREF0); + ts.add(FREF1); + ts.add(FDEF1); + ts.add(SINT0); + ts.add(SFLT0); + ts.add(SINT1); + ts.add(SFLT1); + // Break cyclic init: built a struct + Field fcalloc = Field.make("calloc",TypeFunPtr.CALLOC,-2,true); + TypeStruct scalloc = make("calloc",false,fcalloc); + ts.add(scalloc); + + } + + // Find field index by name + public int find(String fname) { + for( int i=0; i<_fields.length; i++ ) + if( _fields[i]._fname==fname ) + return i; + return -1; + } + public Field field(String fname) { + int idx = find(fname); + return idx== -1 ? null : _fields[idx]; + } + + // Find field index by alias + public int findAlias( int alias ) { + for( int i=0; i<_fields.length; i++ ) + if( _fields[i]._alias==alias ) + return i; + return -1; + } + + @Override + TypeStruct xmeet(Type t) { + TypeStruct that = (TypeStruct) t; + assert !isFree() && !that.isFree(); + if( this==TOP ) return that; + if( that==TOP ) return this; + if( this==BOT ) return BOT; + if( that==BOT ) return BOT; + + // if equal, no matters. + // if short is BOT, chop ; recurPre on short. + // if short is TOP, copy long; recurPre on long. + TypeStruct min = _fields.length < that._fields.length ? this : that; + if( _fields.length != that._fields.length && (!min._open ^ min!=this) ) + return that.xmeet(this); + + assert (_name==that._name) == (_name.equals(that._name)); // Strings are interned + String name = + that._name==TOPNAME ? _name : + _name==TOPNAME ? that._name : + that._name==_name ? _name : + BOTNAME; + if( name==BOTNAME ) return BOT; + + // Check all other fields are sanely similar; same struct type but + // different field contents (e.g. field "age" is either 'int' or '18'). + int len = Math.min(_fields.length,that._fields.length); + for( int i=0; i<len; i++ ) { + Field f0 = _fields[i], f1 = that._fields[i]; + if( !f0._fname.equals(f1._fname) || f0._alias != f1._alias ) + return BOT; + } + + // See if we get a mid-recursion hit; breaks cycles + Integer pid = pid(that); + TypeStruct ts = (TypeStruct)VISIT.get(pid); + if( ts!=null ) return ts; + // If not mid-recursion, we are now; flag recursion. + boolean notRecur = VISIT.isEmpty(); + if( notRecur ) recurOpen(); + + // Setup and install the type, prior to recursing, so we can find our + // recursive self again. + ts = recurPre(pid,name,_open | that._open,_fref & that._fref); + + // Recurse all common fields + Field[] flds = ts._fields; + for( int i=0; i<len; i++ ) { + Field f0 = _fields[i], f1 = that._fields[i]; + flds[i].setType(f0._t.meet(f1._t )); + flds[i]._final = f0._final | f1._final; + } + + // Handle extra fields via `_open` + for( int i=len; i<_fields.length; i++ ) + flds[i].setType(_fields[i]._t ); + + // Cleanup post-recursion; intern/install at cycle end + if( notRecur ) ts = (TypeStruct)ts.recurClose(); + return ts; + } + + @Override + TypeStruct xdual() { + Field[] flds = new Field[_fields.length]; + for( int i=0; i<_fields.length; i++ ) + flds[i] = _fields[i].dual(); + return malloc(sdual(),!_open,!_fref,flds); + } + + // Recursive dual + @Override TypeStruct rdual() { + if( _dual!=null ) return dual(); + assert !_terned; + Field[] flds = new Field[_fields.length]; + TypeStruct d = malloc(sdual(),!_open,!_fref,flds); + (_dual = d)._dual = this; // Cross link duals + for( int i=0; i<_fields.length; i++ ) + flds[i] = _fields[i]._terned ? _fields[i].dual() : _fields[i].rdual(); + return d; + } + + private String sdual() { + return + _name==TOPNAME ? BOTNAME : + _name==BOTNAME ? TOPNAME : + _name; + } + + @Override boolean _isConstant() { + if( VISIT.containsKey(_uid) ) return true; // Cycles assume constant + VISIT.put(_uid,this); + if( _open ) return false; // Infinite BOT fields + // Special case for constant arrays + + // Check all fields for being constant + for( Field field : _fields ) + if( !field._isConstant() ) + return false; + return true; + } + + // All fields are final + @Override boolean _isFinal() { + if( _open ) return false; // May have more non-final fields + if( VISIT.containsKey(_uid) ) // Test: been here before? + return true; // Cycles assume final + VISIT.put(_uid,this); // Set: don't do this again + for( Field fld : _fields ) + if( !fld._isFinal() ) + return false; + return true; + } + + private TypeStruct recurPre(Object key, String name, boolean open, boolean fref) { + // Make a clone of original; suitable for hashing so can build + // e.g. TMPs to clone as part of cycles - but will fail the 'eq' check + // until the entire cycle is built. + Field[] flds = new Field[_fields.length]; + for( int i=0; i<flds.length; i++ ) + flds[i] = _fields[i].malloc(); // Blank copy, but can be hashed + TypeStruct ts = malloc(name, open, fref, flds ); + ts._aliases = aliases(); + VISIT.put(key,ts); + return ts; + } + + private interface FieldTransform { Type apply(Type t); } + + private TypeStruct recurTransform(Object key, String name, boolean open, boolean fref, boolean xfinal, FieldTransform transform) { + TypeStruct ts = (TypeStruct)VISIT.get(key); + if( ts!=null ) return ts; // Already visited + ts = recurPre(key,name,open,fref); + Field[] flds = ts._fields; + for( Field fld : flds ) + fld._final |= xfinal; + + // Now start the recursion + for( int i=0; i<flds.length; i++ ) + flds[i].setType(transform.apply(_fields[i]._t)); + + return ts; + } + + // Make a read-only version + @Override TypeStruct _makeRO() { + return recurTransform(_name,_name,_open,_fref,true,Type::_makeRO); + } + + // Keeps the same struct, but lower-bounds all fields. + public TypeStruct glb2() { + return recurTransform(_uid,_name,_open,false,false,t -> t._glb(true)); + } + + @Override boolean _isGLB(boolean mem) { + if( VISIT.containsKey(_uid) ) return true; // Cycles assume GLB + VISIT.put(_uid,this); + for( Field f : _fields ) + if( !f._t._isGLB(mem) ) + return false; + return true; + } + + // log_size for a struct is not defined, unless its exactly some power of + // 2. *Total size* is well-defined, and is available in the offsets. + @Override public int log_size() { throw Utils.TODO(); } + @Override public int size() { return offset(_fields.length); } + @Override public int alignment() { + int align = 0; + for( Field f : _fields ) + align = Math.max(align, f._t.alignment()); + return align; + } + + // If false, always false. + // If true , maybe true, need to check recursive fields. + private boolean static_eq( TypeStruct ts ) { + assert (_name==ts._name) == (_name.equals(ts._name)); // Strings are interned + return _name==ts._name && _open==ts._open && _fref==ts._fref && _fields.length==ts._fields.length; + } + + @Override boolean eq(Type t) { + TypeStruct ts = (TypeStruct)t; // Invariant + + // Recursive; so use cyclic equals + if( !VISIT.isEmpty() ) { + assert CEQUALS.isEmpty(); + boolean rez = cycle_eq(t); + CEQUALS.clear(); + return rez; + } + // Normal equals + if( !static_eq(ts) ) return false; + if( _fields==ts._fields ) return true; + for( int i = 0; i < _fields.length; i++ ) + if( !_fields[i].eq(ts._fields[i]) ) + return false; + return true; + } + + @Override boolean cycle_eq(Type t) { + if( this==t ) return true; + TypeStruct ts = (TypeStruct)t; // Invariant + if( !static_eq(ts) ) return false; + if( _fields==ts._fields ) return true; + // Check to see if we've ever compared this pair of types before; + // if so, then assume the cycle is equal here. + int pid = pid(ts); + if( CEQUALS.find(pid)!= -1 ) return true; + CEQUALS.push(pid); + // Recursively check fields + for( int i = 0; i < _fields.length; i++ ) + if( !_fields[i].cycle_eq(ts._fields[i]) ) + return false; + return true; + } + + @Override + int hash() { + long hash = _name.hashCode() ^ (_open ? 4 : 0) ^ (_fref ? 8 : 0); + for( Field f : _fields ) + hash = Utils.rot(hash,13) ^ ((long)f._fname.hashCode() * f._alias); + return Utils.fold(hash); + } + + @Override public int nkids() { return _fields.length; } + @Override public Field at( int idx ) { return _fields[idx]; } + @Override public void set( int idx, Type t ) { _fields[idx] = (Field)t; } + // Tags 0-4 - closed +#nflds (+name) + // Tags 5 - closed(+ nflds +name) + // Tags 6-10- open +#nflds (+name) + // Tags 11 - open (+ nflds +name) + @Override int TAGOFF() { return 24; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + baos.write(TAGOFFS[_type]+ (_fref ? 12 : 0) + (_open ? 6 : 0) + Math.min(5,_fields.length)); + if( _fields.length >= 5 ) + baos.packed2(_fields.length); + baos.packed2(strs.get(_name)); + // TODO: Write aliases summary + } + static TypeStruct packed( int tag, BAOS bais, String[] strs ) { + boolean fref = tag >= 12; + if( fref ) tag -= 12; + int ntag = tag >= 6 ? tag-6 : tag; + int nflds = ntag < 5 ? ntag : bais.packed2(); + String name = strs[bais.packed2()]; + return malloc(name,ntag!=tag,fref,new Field[nflds]); + } + + @Override + SB _print(SB sb, BitSet visit, boolean html ) { + if( isFree() ) return sb.p("FREE:").p(_name); + if( isAry() && field("[]")._t instanceof TypeConAry con ) + return sb.p(con.str()); + sb.p(_name); + if( html || isAry() ) + return sb; + sb.p(" {"); + for( Field f : _fields ) + (f._t ==null ? sb.p("---") : f._t.print(sb,visit,html)).p(f._final ? " " : " !").p(f._fname).p("; "); + if( _open && !_fref ) sb.p("... "); + return sb.p("}"); + } + + @Override public String str() { return (isFree() ? "FREE:":"")+_name; } + + + public boolean isAry() { return find("[]")!=-1; } + public boolean isConAry() { + return isAry() && _fields[1]._t instanceof TypeConAry; + } + + public int aryBase() { + assert isAry(); + if( _offs==null ) _offs = offsets(); + return _offs[1]; + } + public int aryScale() { + assert isAry(); + return _fields[1]._t.log_size(); + } + + + // Field offsets as packed byte offsets + private int[] _offs; // Lazily computed and not part of the type semantics + public int offset(int idx) { + if( _offs==null ) _offs = offsets(); + return _offs[idx]; + } + private int[] offsets() { // Field byte offsets + assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal(); + // Compute a layout for a collection of fields + assert _fields != null; // No forward refs + + // Compute a layout + _offs = new int[_fields.length+1]; + if( isAry() ) { // Arrays used fixed u32 len, padding to elems, var length + assert _fields[1]._fname=="[]"; + // Pad out to the element alignment + int align = _fields[1]._t.alignment(); + _offs[1] = (4 + ((1<<align)-1)) & -(1<<align); + if( _fields[1]._t instanceof TypeConAry ary ) // Array length is well-defined for constants, not defined for runtime arrays + _offs[2] = _offs[1]+ary.len(); + + } else { + int[] cnts = new int[5]; // Count of fields at log field size + int flen = _fields.length; + for( int i=0; i<flen; i++ ) + cnts[_fields[i]._t.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl) + int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass + // Compute offsets to the start of each power-of-2 aligned fields. + int[] offs = new int[4]; + for( int i=3; i>=0; i-- ) { + offs[i] = off; + off += cnts[i]<<i; + } + // Assign offsets to all fields. + // Really a hidden radix sort. + for( int i=0; i<flen; i++ ) { + int log = _fields[i]._t.log_size(); + _offs[idx++] = offs[log]; // Field offset + offs[log] += 1<<log; // Next field offset at same alignment + cnts[log]--; // Count down, should be all zero at end + } + _offs[flen] = off; // Max struct size, no trailing padding + } + return _offs; + } + + // Field order with increasing offset + public int[] layout() { + offset(0); + int len = _offs.length; + int[] is = new int[len]; + for( int i=0; i<len; i++ ) is[i] = i; + // Stoopid hand rolled bubble sort + for( int i=0; i<len; i++ ) + for( int j=i+1; j<len; j++ ) + if( _offs[is[j]] < _offs[is[i]] ) + { int tmp = is[i]; is[i] = is[j]; is[j] = tmp; } + return is; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeTuple.java new file mode 100644 index 00000000..86f8aef1 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeTuple.java @@ -0,0 +1,172 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.*; +import java.util.*; + +public class TypeTuple extends Type { + + public Type[] _types; + + private static final Ary<TypeTuple> FREE = new Ary<>(TypeTuple.class); + private TypeTuple(Type[] types) { super(TTUPLE); _types = types; } + private TypeTuple init( Type[] types ) { _types = types; return this; } + private static TypeTuple malloc(Type[] types) { + return FREE.isEmpty() ? new TypeTuple(types) : FREE.pop().init(types); + } + // All fields directly listed + public static TypeTuple make(Type... types) { + TypeTuple tt = malloc(types); + TypeTuple t2 = tt.intern(); + return t2==tt ? tt : t2.free(tt); + } + @Override TypeTuple free(Type t) { + TypeTuple tt = (TypeTuple)t; + tt._types = null; + tt._dual = null; + tt._hash = 0; + FREE.push(tt); + return this; + } + @Override boolean isFree() { return _types==null; } + + public TypeTuple makeFrom(int idx, Type tx ) { + if( _types[idx]==tx ) return this; + Type[] types = _types.clone(); + types[idx] = tx; + return make(types); + } + public TypeTuple meetFrom(int idx, Type t ) { return makeFrom(idx,_types[idx].meet(t)); } + + public static final TypeTuple BOT = malloc(new Type[0]).intern(); + public static final TypeTuple TOP = BOT.dual(); + // The default state, a triple of CONTROL, Memory, and Value, with bottoms + // for all. This is the default StopNode type, when new functions are + // being created and we cannot tell what the future holds. This is the + // default ReturnNode type, returning what the worst a function can do. + public static final TypeTuple STATE = make(Type.CONTROL,TypeMem.BOT,Type.BOTTOM); + public static final TypeTuple STOP_HIGH = make(Type.CONTROL,TypeMem.START,Type.TOP); + + public static final TypeTuple TEST = make(TypeInteger.BOT,TypeMemPtr.TEST); + public static final TypeTuple DEAD_NEW = make(TypeMemPtr.TOP,TypeMem.TOP); + + public static final TypeTuple IF_BOTH = make(Type. CONTROL,Type. CONTROL); + public static final TypeTuple IF_NEITHER = make(Type.XCONTROL,Type.XCONTROL); + public static final TypeTuple IF_TRUE = make(Type. CONTROL,Type.XCONTROL); + public static final TypeTuple IF_FALSE = make(Type.XCONTROL,Type. CONTROL); + + public static void gather(ArrayList<Type> ts) { ts.add(BOT); ts.add(TEST); ts.add(STATE); ts.add(IF_TRUE); } + + @Override + Type xmeet(Type other) { + TypeTuple that = (TypeTuple)other; // contract from xmeet + if( this==TOP ) return that; + if( that==TOP ) return this; + if( _types.length != that._types.length ) + return BOT; + return make(TypeFunPtr.xmeet(_types,that._types)); + } + + @Override TypeTuple xdual() { + if( _types.length==0 ) return malloc(null); + boolean isCon=true; + for( Type t : _types ) + if( t._dual!=t ) + // Not a constant + return malloc(TypeFunPtr.xdual(_types)); + return this; + } + @Override TypeTuple rdual() { + if( _dual!=null ) return dual(); + assert !_terned; + assert _types.length>0; + Type[] ts = new Type[_types.length]; + TypeTuple d = malloc(ts); + (_dual = d)._dual = this; // Cross link duals + // Collection rdual + for( int i=0; i<_types.length; i++ ) + ts[i] = _types[i]._terned ? _types[i].dual() : _types[i].rdual(); + return d; + } + + @Override boolean _isConstant() { + for( Type t : _types ) + if( !t._isConstant() ) + return false; + return true; + } + @Override boolean _isFinal() { throw Utils.TODO(); } + + @Override TypeMemPtr _makeRO() { throw Utils.TODO(); } + + @Override Type _upgradeType(HashMap<String,Type> TYPES) { + Type[] ts = new Type[_types.length]; + for( int i=0; i<ts.length; i++ ) + ts[i] = _types[i]._upgradeType(TYPES); + return make(ts); + } + + @Override public int log_size() { throw Utils.TODO(); } + @Override public int alignment() { + assert isConstant(); + int align = 0; + for( Type t : _types ) + align = Math.max(align,t.alignment()); + return align; + } + + public Type ctl() { assert _types.length>=3; return _types[0]; } + public Type ret() { assert _types.length>=3; return _types[2]; } + public TypeMem mem() { assert _types.length>=3; + return _types[1] instanceof TypeMem tmem ? tmem : (_types[1].isHigh() ? TypeMem.START : TypeMem.BOT); + } + + @Override public int nkids() { return _types.length; } + @Override public Type at( int idx ) { return _types[idx]; } + @Override public void set( int idx, Type t ) { _types[idx] = t; } + + // Reserve tags for tuples with 2,3,generic + @Override int TAGOFF() { return 4; } + @Override public void packed( BAOS baos, HashMap<String,Integer> strs ) { + if( _types.length==2 ) baos.write(TAGOFFS[_type] + 0); + else if( _types.length==3 ) baos.write(TAGOFFS[_type] + 1); + else if( _types.length==4 ) baos.write(TAGOFFS[_type] + 2); + else baos.write(TAGOFFS[_type] + 3).packed2(_types.length); + } + static TypeTuple packed( int tag, BAOS bais ) { + if( tag==0 ) return malloc(new Type[2]); + if( tag==1 ) return malloc(new Type[3]); + if( tag==2 ) return malloc(new Type[4]); + return malloc(new Type[bais.packed2()]); + } + + @Override SB _print(SB sb, BitSet visit, boolean html ) { + if( this==TOP ) return sb.p("[TOP]"); + if( this==BOT ) return sb.p("[BOT]"); + sb.p("[ "); + for( Type t : _types ) + t.print(sb,visit,html).p(", "); + return sb.unchar(2).p("]"); + } + + @Override public String str() { return print(new SB(),new BitSet(),false).toString(); } + + @Override + int hash() { + int sum = 0; + if( _types!=null ) for( Type type : _types ) sum ^= type.hashCode(); + return sum; + } + + @Override + boolean eq( Type t ) { + TypeTuple that = (TypeTuple)t; // Contract + if( _types==null && that._types==null ) return true; + if( _types==null || that._types==null ) return false; + if( _types.length != that._types.length ) return false; + for( int i=0; i<_types.length; i++ ) + if( _types[i]!=that._types[i] ) + return false; + return true; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/XInt.java b/chapter25/src/main/java/com/seaofnodes/simple/type/XInt.java new file mode 100644 index 00000000..e3054a73 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/XInt.java @@ -0,0 +1,266 @@ +package com.seaofnodes.simple.type; + +import com.seaofnodes.simple.util.*; +import java.lang.Integer; +import java.util.Arrays; + +// Stupidly overkill interned immutable int[]'s +public abstract class XInt { + private static final Ary<Ary<int[]>> FREES = new Ary(Ary.class); + private static final Ary<Ary<int[]>> INTERNS = new Ary(Ary.class); + private static int PROBES, HITS, NEWS; + + public static final int[] EMPTY = intern(new int[]{0, 0}); + public static final int[] FULL = intern(new int[]{0,-1}); + public static final int[] SET1 = make(1); + public static final int[] SET3 = make(SET1, 2); + + static int idx(int x) { return (x>>5)+1; } + static int mask(int x) { return 1<<(x&31); } + + // No hash set, still mutable + public static boolean isMutable(int[] xs) { return xs[0]==0; } + // Return hash code; must be set hence frozen + public static int hash(int[] xs) { return xs[0]; } + + // High bit set (infinite 1's extension) + public static boolean isHigh(int[] xs) { return xs[xs.length-1] < 0; } + + // Check for a bit + public static boolean bit(int[] xs, int x) { + int idx = idx(x); + if( idx >= xs.length ) + return isHigh(xs); + return (xs[idx] & mask(x))!=0; + } + + // Number of bits set + public static int bitCount(int[] xs) { + assert !isHigh(xs); // Dont ask if infinite + int cnt=0; + for( int i=1; i<xs.length; i++ ) + cnt += Integer.bitCount(xs[i]); + return cnt; + } + + // Largest bit + public static int max(int[] xs) { + assert !isHigh(xs); + return Integer.numberOfTrailingZeros(xs[xs.length-1])+((xs.length-1-1)<<5); + } + + // Has 1 bit set + public static boolean isConstant(int[] xs) { return !isHigh(xs) && bitCount(xs)==1; } + // Return the 1 set bit or assert + public static int onlyBit(int[] xs) { + assert isConstant(xs); + for( int i=1; i<xs.length; i++ ) + if( xs[i] != 0 ) + return Integer.numberOfTrailingZeros(xs[i]) + ((i-1)<<5); + throw Utils.TODO("Should not reach here"); + } + + // Subset check: ys is a subset of xs + public static boolean subset(int[] xs, int[] ys) { + assert !isMutable(ys); + if( xs==ys ) return true; + if( xs==FULL ) return true; + if( ys==EMPTY ) return true; + int min = Math.min(xs.length,ys.length); + for( int i=1; i<min; i++ ) + if( (xs[i] | ys[i])!=xs[i] ) + return false; + if( xs.length == ys.length ) + return true; + if( ys.length < xs.length ) + return !isHigh(ys); // If ys is infinite, it'll hit one of the xs extra zeros + return isHigh(xs); + } + + + // Construct a set with 1 bit set + public static int[] make(int x ) { return make(EMPTY,x); } + + // Add a bit + public static int[] make(int[] xs, int x ) { + assert !isMutable(xs); + int idx = idx(x); + if( idx >= xs.length && isHigh(xs) ) + return xs; // Set in the default infinite + if( idx < xs.length && (mask(x) & xs[idx])!=0 ) + return xs; // Already set + int len = Math.max(xs.length,idx+1); + if( (x&31)==31 && idx+1==len ) + len++; + int[] ys = free(len); + System.arraycopy(xs,1,ys,1,xs.length-1); + ys[idx] |= mask(x); + return intern(ys); + } + + // Interning, and life-cycle management + // Intern the array + public static int[] intern(int[] xs) { + if( !isMutable(xs) ) return xs; // Already interned + PROBES++; + + // Trim trailing 0s or -1s, use the infinite extend bit + int len = xs.length; + while( len > 2 && + ((xs[len-1]== 0 && xs[len-2]>=0) || + (xs[len-1]== -1 && xs[len-2]< 0) ) ) + len--; + // Replace with trimmed + if( len < xs.length ) { + var ys = free(len); + System.arraycopy(xs,1,ys,1,len-1); + xs = _free(xs,ys); + } + + // Compute lame hash code + int hash = 0; + for( int i=1; i<len; i++ ) + hash += xs[i]; + if( hash == 0 ) hash = 0xCAFEBABE; + xs[0] = hash; // Set lame hashcode, and mark immutable + + // Get length-specific INTERN table + var xss = INTERNS.atX(len); + if( xss==null ) + INTERNS.setX( xs.length, xss = new Ary<>(int[].class)); + + // Probe for a hit + for( int[] ys : xss ) + if( Arrays.equals(xs,ys) ) // Hit! + // Free new array and return old array + { HITS++; return _free(xs,ys); } + + // Intern new array and return + return xss.push(xs); + } + + // Return Ary of free int[]s of fixed size + private static Ary<int[]> _xss(int len) { + Ary<int[]> xss = FREES.atX(len); + if( xss==null ) + FREES.setX(len, xss = new Ary<>(int[].class)); + return xss; + } + // Pull free array from free list + static int[] free(int len) { + int[] xs = _xss(len).pop(); + if( xs==null ) + { NEWS++; xs = new int[len]; } + assert isMutable(xs); + return xs; + } + // Return ys. Put xs on free list + static int[] _free(int[] xs, int[] ys) { + Arrays.fill(xs,0); // Mutable again + _xss(xs.length).push(xs); + return ys; + } + + // Meet: Set union + public static int[] meet(int[] xs, int[] ys) { + if( xs==ys ) return xs; + boolean isX=true, isY=true; + int min = Math.min(xs.length,ys.length); + for( int i=1; i<min; i++ ) { + if( (xs[i] | ys[i])!=xs[i] ) isX=false; + if( (xs[i] | ys[i])!=ys[i] ) isY=false; + } + if( !isHigh(xs) && min < ys.length ) + isX = false; // YS has *some* more bits set, and XS does not + if( !isHigh(ys) && min < xs.length ) + isY = false; // XS has *some* more bits set, and YS does not + if( isX ) return xs; + if( isY ) return ys; + // Get a free one + int[] ms = free(Math.max(xs.length,ys.length)); + for( int i=1; i<min; i++ ) + ms[i] = xs[i] | ys[i]; + if( min < xs.length ) System.arraycopy(xs,min,ms,min,xs.length-min); + if( min < ys.length ) System.arraycopy(ys,min,ms,min,ys.length-min); + return intern(ms); + } + + // Dual: flip all the bits + public static int[] dual(int[] xs) { + int[] ds = free(xs.length); + for( int i=1; i<xs.length; i++ ) + ds[i] = ~xs[i]; + return intern(ds); + } + + + public static void packed(BAOS baos, int[] xs) { + baos.packed1(xs.length); + for( int i=1; i<xs.length; i++ ) + baos.packed4(xs[i]); + } + public static int[] packed(BAOS bais) { + int len = bais.packed1(); + int[] xs = free(len); + for( int i=1; i<len; i++ ) + xs[i] = bais.packed4(); + return intern(xs); + } + + // Next set bit, or -1 + // for( int bit = XInt.next(bits,0); bit >=0; bit = XInt.next(bits,bit) ) {...bit...} + public static int next(int[] xs, int x) { + assert !isHigh(xs); // No iterating infinite bitsets + x++; // Skip current + for( ; !bit(xs,x); x++ ) + if( idx(x)>=xs.length ) + return -1; + return x; + } + + // Remap all bits + public static int[] remap( int[] xs, AryInt map ) { + if( xs==FULL ) return xs; + assert !isHigh(xs); + int[] ys = free(xs.length); + for( int bit = next(xs,0); bit >=0; bit = next(xs,bit) ) { + int bat = map.at(bit); + ys[idx(bat)] |= mask(bat); + } + return intern(ys); + } + + + + public static String str(int[] xs) { return str(new SB(),xs).toString(); } + public static SB str(SB sb, int[] xs) { + sb.p("[ "); + // Printing ranges; + // 1,2,3 prints 1-3 + // 1,2,4 prints 1,2,4 + // + int max = (xs.length-1)<<5; + int low = 0; // No low range + for( int i=1; i<max; i++ ) { + if( bit(xs,i) ) { // Set bit, start or extend range + if( low==0 ) low=i; // No range, start one + } else { // Break in sequence, ending a range + range(sb,low,i-1); // Print prior range, and start new one + low=0; // Reset range + } + } + // Print any partial range + range(sb,low,max-1).unchar(); + if( isHigh(xs) ) + sb.p(",..."); + return sb.p("]"); + } + + private static SB range( SB sb, int low, int mid ) { + if( low==0 ) ; + else if( low==mid ) sb.p(low).p(','); + else if( low==mid-1 ) sb.p(low).p(',').p(mid).p(','); + else sb.p(low).p('-').p(mid).p(','); + return sb; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/util/Ary.java b/chapter25/src/main/java/com/seaofnodes/simple/util/Ary.java new file mode 100644 index 00000000..d67801c2 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/util/Ary.java @@ -0,0 +1,229 @@ +package com.seaofnodes.simple.util; + +import java.lang.StringBuilder; +import java.lang.reflect.Array; +import java.util.*; + +// ArrayList with saner syntax +public class Ary<E> extends AbstractList<E> implements List<E> { + public E[] _es; + public int _len; + + public Ary(E[] es) { this(es,es.length); } + public Ary(E[] es, int len) { if( es.length==0 ) es=Arrays.copyOf(es,1); _es=es; _len=len; } + @SuppressWarnings("unchecked") + public Ary(Class<E> clazz) { this((E[]) Array.newInstance(clazz, 1),0); } + + /** @return list is empty */ + public boolean isEmpty() { return _len==0; } + /** @return active list length */ + public int len() { return _len; } + public int size() { return len(); } + + /** @param i element index + * @return element being returned; throws if OOB + * @exception AIOOBE if !(0 <= i < _len) + */ + public E get( int i ) { return at(i); } + public E at( int i ) { + range_check(i); + return _es[i]; + } + public E atX( int i ) { + if( i >= _len ) return null; + return _es[i]; + } + + /** @return last element */ + public E last( ) { return at(_len-1); } + public E last(int x ) { return at(_len-1+x); } + + /** Add element in amortized constant time + * @param e Element to add at end of list + **/ + public E push( E e ) { + if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1)); + return (_es[_len++] = e); + } + /** @return remove and return last element */ + public E pop( ) { + return _len==0 ? null : _es[--_len]; + } + @Override public E removeLast() { return pop(); } + + + /** Add element in amortized constant time + * @param e Element to add at end of list + * @return true */ + public boolean add( E e ) { + if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1)); + _es[_len++] = e; + return true; + } + + /** Fast, constant-time, element removal. Does not preserve order + * @param i element to be removed + * @return element removed */ + public E del( int i ) { + range_check(i); + E tmp = _es[i]; + _es[i]=_es[--_len]; + return tmp; + } + + /** Slow, linear-time, element removal. Preserves order. + * @param i element to be removed + * @return element removed */ + @Override public E remove( int i ) { + range_check(i); + E e = _es[i]; + System.arraycopy(_es,i+1,_es,i,(--_len)-i); + return e; + } + + /** Slow, linear-time, element insertion. Preserves order. + * @param i where to insert + * @return element inserted */ + public E insert( E e, int i ) { + setLen(_len+1); + System.arraycopy(_es,i,_es,i+1,_len-i); + _es[i] = e; + return e; + } + + + /** Remove all elements */ + public void clear( ) { _len=0; } + + /** Set existing element + * @param i element to set + * @param e value to set + * @return old value + * @exception AIOOBE if !(0 <= i < _len) + */ + public E set( int i, E e ) { + range_check(i); + E old = _es[i]; + _es[i] = e; + return old; + } + + /** Set existing element + * @param i element to set + * @param e value to set + * @return old value + * @exception AIOOBE if !(0 <= i < _len) + */ + public E setX( int i, E e ) { + if( i >= _len ) { + while( i >= _es.length ) _es = Arrays.copyOf( _es, _es.length << 1 ); + _len = i+1; + } + E old = _es[i]; + _es[i] = e; + return old; + } + + public Ary<E> setLen( int len ) { + if( len > _len ) + while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); + _len = len; + while( _es.length > (len<<1) ) // Shrink if hugely too large + _es = Arrays.copyOf(_es,_es.length>>1); + Arrays.fill(_es,len,_es.length,null); + return this; + } + + public E swap( int i, int j ) { + range_check(i); + range_check(j); + E tmp = _es[i]; + if( i==j ) return tmp; + _es[i] = _es[j]; + _es[j] = tmp; + return tmp; + } + + + /** @param c Collection to be added */ + public boolean addAll( Collection<? extends E> c ) { + if( c==null ) return false; + for( E e : c ) add(e); + return true; + } + public Ary<E> addAll( Iterable<? extends E> it ) { for( E e : it ) add(e); return this; } + + /** @param es Array to be added */ + public <F extends E> Ary<E> addAll( F[] es ) { + if( es==null || es.length==0 ) return this; + while( _len+es.length > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); + System.arraycopy(es,0,_es,_len,es.length); + _len += es.length; + return this; + } + + /** @param c Collection to be added */ + public Ary<E> addAll( Ary<? extends E> c ) { + if( c==null || c._len==0 ) return this; + while( _len+c._len > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); + System.arraycopy(c._es,0,_es,_len,c._len); + _len += c._len; + return this; + } + + /** @return compact array version */ + public E[] asAry() { return Arrays.copyOf(_es,_len); } + + /** Find the first matching element using ==, or -1 if none. Note that + * most del calls shuffle the list, so the first element might be random. + * @param e Element to find + * @return index of first matching element, or -1 if none */ + public int find( E e ) { + for( int i=0; i<_len; i++ ) if( _es[i]==e ) return i; + return -1; + } + + + /** @return an iterator */ + @Override public Iterator<E> iterator() { return new Iter(); } + private class Iter implements Iterator<E> { + int _i=0; + @Override public boolean hasNext() { return _i<_len; } + @Override public E next() { return _es[_i++]; } + } + + @Override public String toString() { + StringBuilder sb = new StringBuilder().append('{'); + for( int i=0; i<_len; i++ ) { + if( i>0 ) sb.append(','); + if( _es[i] != null ) sb.append(_es[i].toString()); + } + return sb.append('}').toString(); + } + + private void range_check( int i ) { + if( i < 0 || i>=_len ) + throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len); + } + + // Note that the hashCode() and equals() are not invariant to changes in the + // underlying array. If the hashCode() is used (e.g., inserting into a + // HashMap) and the then the array changes, the hashCode() will change also. + @Override public boolean equals( Object o ) { + if( this==o ) return true; + if( !(o instanceof Ary ary) ) return false; + if( _len != ary._len ) return false; + if( _es == ary._es ) return true; + for( int i=0; i<_len; i++ ) + if( !(_es[i]==null ? (ary._es[i] == null) : _es[i].equals(ary._es[i])) ) + return false; + return true; + } + @Override public int hashCode( ) { + int sum=_len; + for( int i=0; i<_len; i++ ) + sum += _es[i]==null ? 0 : _es[i].hashCode(); + return sum; + } + +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/util/AryInt.java b/chapter25/src/main/java/com/seaofnodes/simple/util/AryInt.java new file mode 100644 index 00000000..b498fd78 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/util/AryInt.java @@ -0,0 +1,192 @@ +package com.seaofnodes.simple.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.IntUnaryOperator; + +// ArrayList with saner syntax +public class AryInt { + public int[] _es; + public int _len; + public AryInt(int[] es) { this(es,es.length); } + public AryInt(int[] es, int len) { _es=es; _len=len; } + public AryInt() { this(new int[1],0); } + + /** @return list is empty */ + public boolean isEmpty() { return _len==0; } + /** @return active list length */ + public int len() { return _len; } + /** @param i element index + * @return element being returned; throws if OOB */ + public int at( int i ) { + range_check(i); + return _es[i]; + } + /** @param i element index + * @return element being returned, or 0 if OOB */ + public int atX( int i ) { + return i < _len ? _es[i] : 0; + } + /** @return last element */ + public int last( ) { return at(_len-1); } + public int last(int x ) { return at(_len-1+x); } + + /** @return remove and return last element */ + public int pop( ) { + range_check(0); + return _es[--_len]; + } + + /** Add element in amortized constant time + * @param e element to add at end of list + * @return 'this' for flow-coding */ + public AryInt push( int e ) { + if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1)); + _es[_len++] = e; + return this; + } + + /** Slow, linear-time, element insert. Preserves order. + * @param i index to insert at, between 0 and _len inclusive. + * @param e intlement to insert + */ + public void insert( int i, int e ) { + if( i < 0 || i>_len ) + throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len); + if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1)); + System.arraycopy(_es,i,_es,i+1,(_len++)-i); + _es[i] = e; + } + + /** Fast, constant-time, element removal. Does not preserve order + * @param i element to be removed + * @return element removed */ + public int del( int i ) { + range_check(i); + int tmp = _es[i]; + _es[i]=_es[--_len]; + return tmp; + } + + /** Slow, linear-time, element removal. Preserves order. + * @param i element to be removed + * @return element removed */ + public int remove( int i ) { + range_check(i); + int e = _es[i]; + System.arraycopy(_es,i+1,_es,i,(--_len)-i); + return e; + } + + /** Remove all elements */ + public void clear( ) { Arrays.fill(_es,0,_len,0); _len=0; } + + // Extend and set + public int setX( int i, int e ) { + while( i>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); + if( i >= _len ) _len = i+1; + return (_es[i] = e); + } + + public int set( int i, int e ) { + range_check(i); + return (_es[i] = e); + } + + public AryInt set_as( int e ) { _es[0] = e; _len=1; return this; } + public AryInt set_len( int len ) { + if( len > _len ) + while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); + _len = len; + while( _es.length > (len<<1) ) // Shrink if hugely too large + _es = Arrays.copyOf(_es,_es.length>>1); + return this; + } + + /** @param c Collection to be added */ + public AryInt addAll( Collection<? extends Integer> c ) { for( int e : c ) push(e); return this; } + + /** @param es Array to be added */ + public AryInt addAll( int[] es ) { + if( es.length==0 ) return this; + while( _len+es.length > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); + System.arraycopy(es,0,_es,_len,es.length); + _len += es.length; + return this; + } + + /** @param es Array to be added */ + public AryInt addAll( AryInt ary ) { + if( ary._len==0 ) return this; + while( _len+ary._len > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); + System.arraycopy(ary._es,0,_es,_len,ary._len); + _len += ary._len; + return this; + } + + public AryInt map_update( IntUnaryOperator f ) { for( int i = 0; i<_len; i++ ) _es[i] = f.applyAsInt(_es[i]); return this; } + + /** @return compact array version, using the internal base array where possible. */ + public int[] asAry() { return _len==_es.length ? _es : Arrays.copyOf(_es,_len); } + + /** Sorts in-place */ + public void sort_update() { Arrays.sort(_es, 0, _len); } + /** Find the first matching element using ==, or -1 if none. Note that + * most del calls shuffle the list, so the first element might be random. + * @param e intlement to find + * @return index of first matching element, or -1 if none */ + public int find( int e ) { + for( int i=0; i<_len; i++ ) if( _es[i]==e ) return i; + return -1; + } + + @Override public String toString() { + SB sb = new SB().p('['); + for( int i=0; i<_len; i++ ) + sb.p(_es[i]).p(','); + return sb.unchar().p(']').toString(); + } + + private void range_check( int i ) { + if( i < 0 || i>=_len ) + throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len); + } + + // Binary search sorted _es. Returns insertion point. + // Undefined results if _es is not sorted. + public int binary_search( int e ) { + int lo=0, hi=_len-1; + while( lo <= hi ) { + int mid = (hi + lo) >>> 1; // midpoint, rounded down + int mval = _es[mid]; + if( e==mval ) { + // If dups, get to the first. + while( mid>0 && e==_es[mid-1] ) mid--; + return mid; + } + if( e >mval ) lo = mid+1; + else hi = mid-1; + } + return lo; + } + + // Note that the hashCode() and equals() are not invariant to changes in the + // underlying array. If the hashCode() is used (e.g., inserting into a + // HashMap) and the then the array changes, the hashCode() will change also. + @Override public boolean equals( Object o ) { + if( this==o ) return true; + if( !(o instanceof AryInt ary) ) return false; + if( _len != ary._len ) return false; + if( _es == ary._es ) return true; + for( int i=0; i<_len; i++ ) + if( _es[i] != ary._es[i] ) + return false; + return true; + } + @Override public int hashCode( ) { + int sum=_len; + for( int i=0; i<_len; i++ ) + sum += _es[i]; + return sum; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/util/BAOS.java b/chapter25/src/main/java/com/seaofnodes/simple/util/BAOS.java new file mode 100644 index 00000000..61ce7387 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/util/BAOS.java @@ -0,0 +1,162 @@ +package com.seaofnodes.simple.util; + +import java.util.HashMap; +import java.util.Arrays; + +// "ByteArrayOutputStream", and Input Stream and not synchronized and has +// matching pack/unpack strategies. +public class BAOS { + + private byte[] _buf; + private int _len; + + public BAOS() { _buf = new byte[1]; } + public BAOS(byte[] buf) { _buf = buf; } + public BAOS(byte[] buf, int x) { _buf = buf; _len = x; } + + public byte[] toByteArray() { + return Arrays.copyOf(_buf,_len); + } + + public byte[] buf() { return _buf; } + public int size() { return _len; } + public void set( byte[] buf, int len ) { _buf=buf; _len=len; } + public void clear() { _len=0; } + + // ----------------------------------------------------------------------- + // Basic read, write (expand) + + private void grow(int len) { + while( _len+len >= _buf.length ) + _buf = Arrays.copyOf(_buf,_buf.length*2); + } + + // Write a byte, grow as needed; silent chop + public BAOS write(int b) { + grow(1); + _buf[_len++] = (byte)b; // Silent chop + return this; + } + + // Read an UNSIGNED bytes at INDEX, AIOOBE if off end + public int read1(int idx) { return _buf[idx] & 0xFF; } + + // Read an UNSIGNED byte, AIOOBE if off end + public int read() { return read1(_len++); } + + // write 2 bytes + public BAOS write2( int x ) { + grow(2); + _buf[_len++] = (byte)(x>> 0); + _buf[_len++] = (byte)(x>> 8); + return this; + } + + // Read a u16 in 2 bytes + public int read2(int idx) { return read1(idx) | (read1(idx+1)<<8); } + public int read2() { int rez = read2(_len); _len+=2; return rez; } + + // write 4 bytes + public BAOS write4( int x ) { + grow(4); + _buf[_len++] = (byte)(x>> 0); + _buf[_len++] = (byte)(x>> 8); + _buf[_len++] = (byte)(x>>16); + _buf[_len++] = (byte)(x>>24); + return this; + } + + // Read a i32 in 4 bytes + public int read4(int idx) { return read2(idx) | (read2(idx+2)<<16); } + public int read4() { int rez = read4(_len); _len+=4; return rez; } + + // write 8 bytes + public BAOS write8( long x ) { + write4((int) x ); + write4((int)(x>>32)); + return this; + } + + // Read a i64 in 8 bytes + public long read8(int idx) { return ((long)read4(idx) & 0xFFFFFFFFL) | ((long)read4(idx+4)<<32); } + public long read8() { long rez = read8(_len); _len+=8; return rez; } + + // Write byte array + public void write( byte[] bs ) { + grow(bs.length); + System.arraycopy(bs,0,_buf,_len,bs.length); + _len += bs.length; + } + + // Read/fill byte array + public byte[] read( byte[] bs ) { + System.arraycopy(_buf,_len,bs,0,bs.length); + _len += bs.length; + return bs; + } + + // ----------------------------------------------------------------------- + // Write a packed integer to the BAOS. Track a histogram to make more + // intelligent choices in the future. + private static final HashMap<Long,Integer> PACKED_HISTOGRAM = new HashMap<>(); + private BAOS histo(long x) { + Integer cnt = PACKED_HISTOGRAM.get(x); + PACKED_HISTOGRAM.put(x,cnt==null ? 1 : cnt+1); + return this; + } + + // Write a u8 (checked) + public void packed1(int x) { + assert 0 <= x && x <= 255; + histo(x).write(x); + } + + public int packed1() { return read(); } + + // Write a u16 (checked) + public void packed2(int x) { + assert 0 <= x && x <= 65535; + (( x <= 254 ) ? write(x) : write(0xFF).write2(x)).histo(x); + } + + // Read a packed u16 + public int packed2() { + int x = read(); + return x==0xFF ? read2() : x; + } + + // Write a packed i32 + public void packed4(int x) { + ((-127 <= x && x <= 127 ) ? write(x) : write(-128).write4(x)).histo(x); + } + + // Read a packed i32 + public int packed4() { + int x = _buf[_len++]; // Signed byte read + return x== -128 ? read4() : x; + } + + // Write a packed i64 + public void packed8(long x) { + ((-127 <= x && x <= 127 ) ? write((int)x) : write(-128).write8(x)).histo(x); + } + + // Read a packed i64 + public long packed8() { + int x = _buf[_len++]; // Signed byte read + return x== -128 ? read8() : x; + } + + public void packedS(String s) { + packed4(s.length()); + for( int i=0; i<s.length(); i++ ) + packed4(s.charAt(i)); + } + + public String packedS() { + int len = packed4(); + String s = new String(_buf,_len,len).intern(); + _len += len; + return s; + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/util/IntHashMap.java b/chapter25/src/main/java/com/seaofnodes/simple/util/IntHashMap.java new file mode 100644 index 00000000..55aaa9f9 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/util/IntHashMap.java @@ -0,0 +1,65 @@ +package com.seaofnodes.simple.util; + +import java.util.Arrays; + +// Int-Int Hash Map +public class IntHashMap { + private int[] _es; + private int _size; + private final int _sentinel; + + // Pick a sentinel value; not allowed for keys or values. Sentinel is + // returned on a miss. Writing sentinel counts as a remove. + public IntHashMap(int sentinel) { + _sentinel = sentinel; + _es = new int[4]; + Arrays.fill(_es,sentinel); + } + + public int size() { return _size; } + public void clear() { + _size = 0; + Arrays.fill(_es,_sentinel); + } + + private int cap() { return _es.length>>1; } + + public int put( int key, int val ) { + int slot = probe(key); + int old = _es[(slot<<1)+1]; + if( old == _sentinel && val != _sentinel ) _size++; + if( old != _sentinel && val == _sentinel ) _size--; + // Check cap + if( _size > ((cap()>>1) + (cap()>>2)) ) { + grow(); + slot = probe(key); + } + + _es[ slot<<1 ] = key; + _es[(slot<<1)+1] = val; + return old; + } + + public int get( int key ) { + int slot = probe(key); + return _es[(slot<<1)+1]; + } + + int probe( int key ) { + int mask = cap()-1; + int slot = key & mask; + while( _es[slot<<1] != _sentinel && _es[slot<<1] != key ) + slot = (slot + (key|1)) & mask; + return slot; + } + + // Double underlying array size + private void grow() { + int[] old = _es; + _es = new int[_es.length<<1]; + Arrays.fill(_es,_sentinel); + for( int i=0; i<old.length; i+=2 ) + if( old[i] != _sentinel ) + put(old[i],old[i+1]); + } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/util/SB.java b/chapter25/src/main/java/com/seaofnodes/simple/util/SB.java new file mode 100644 index 00000000..cc59f2a6 --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/util/SB.java @@ -0,0 +1,99 @@ +package com.seaofnodes.simple.util; + +/** Tight/tiny StringBuilder wrapper. + * Short short names on purpose; so they don't obscure the printing. + * Can't believe this wasn't done long, long ago. */ +public final class SB { + private final StringBuilder _sb; + private int _indent = 0; + public SB( ) { _sb = new StringBuilder( ); } + public SB(String s) { _sb = new StringBuilder(s); } + public SB p( String s ) { _sb.append(s); return this; } + public SB p( float s ) { + if( Float.isNaN(s) ) + _sb.append( "Float.NaN"); + else if( Float.isInfinite(s) ) { + _sb.append(s > 0 ? "Float.POSITIVE_INFINITY" : "Float.NEGATIVE_INFINITY"); + } else _sb.append(s); + return this; + } + public SB p( double s ) { + if( Double.isNaN(s) ) + _sb.append("Double.NaN"); + else if( Double.isInfinite(s) ) { + _sb.append(s > 0 ? "Double.POSITIVE_INFINITY" : "Double.NEGATIVE_INFINITY"); + } else _sb.append(s); + return this; + } + public SB p( char s ) { _sb.append(s); return this; } + public SB p( int s ) { _sb.append(s); return this; } + public SB p( long s ) { _sb.append(s); return this; } + public SB p( boolean s) { _sb.append(s); return this; } + // 1 byte, 2 hex digits, 8 bits + public SB hex1(int s) { + int digit = (s>>4) & 0xf; + _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); + digit = s & 0xf; + _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); + return this; + } + // 2 bytes, 4 hex digits, 16 bits, Big Endian + public SB hex2(int s) { return hex1(s>> 8).hex1(s); } + // 4 bytes, 8 hex digits, 32 bits, Big Endian + public SB hex4(int s) { return hex2(s>>16).hex2(s); } + // 8 bytes, 16 hex digits, 64 bits, Big Endian + public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); } + + // Fixed width field + public SB fix( int sz, String s ) { + for( int i=0; i<sz; i++ ) + _sb.append( i < s.length() ? s.charAt(i) : ' '); + return this; + } + public char at(int idx ) { return _sb.charAt(idx); } + + // Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and + // suddenly call the autoboxed version. + public SB pobj( Object s ) { _sb.append(s.toString()); return this; } + public SB i( int d ) { for( int i=0; i<d+_indent; i++ ) p(" "); return this; } + public SB i( ) { return i(0); } + public SB ip(String s) { return i().p(s); } + public SB s() { _sb.append(' '); return this; } + + // Increase indentation + public SB ii( int i) { _indent += i; return this; } + public SB ii() { return ii(1); } + // Decrease indentation + public SB di( int i) { _indent -= i; return this; } + public SB di() { return di(1); } + + public SB nl( ) { return p(System.lineSeparator()); } + // Last printed a nl + public boolean was_nl() { + int len = _sb.length(); + String nl = System.lineSeparator(); + int nlen = nl.length(); + if( len < nlen ) return false; + for( int i=0; i<nlen; i++ ) + if( _sb.charAt(len-nlen+i)!=nl.charAt(i) ) + return false; + return true; + } + + // Delete last char. Useful when doing string-joins and JSON printing and an + // extra separater char needs to be removed: + // + // sb.p('['); + // for( Foo foo : foos ) + // sb.p(foo).p(','); + // sb.unchar().p(']'); // remove extra trailing comma + // + public SB unchar() { return unchar(1); } + public SB unchar(int x) { _sb.setLength(_sb.length()-x); return this; } + public SB setLen(int len) { _sb.setLength(len); return this; } + public String subString(int start, int end ) { return _sb.substring(start,end); } + + public SB clear() { _sb.setLength(0); return this; } + public int len() { return _sb.length(); } + @Override public String toString() { return _sb.toString(); } +} diff --git a/chapter25/src/main/java/com/seaofnodes/simple/util/Utils.java b/chapter25/src/main/java/com/seaofnodes/simple/util/Utils.java new file mode 100644 index 00000000..577a60af --- /dev/null +++ b/chapter25/src/main/java/com/seaofnodes/simple/util/Utils.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple.util; + +import java.util.ArrayList; + +public class Utils { + public static RuntimeException TODO() { return TODO("Not yet implemented"); } + public static RuntimeException TODO(String msg) { return new RuntimeException(msg); } + + /** + * Fast, constant-time, element removal. Does not preserve order + * + * @param array ArrayList to modify + * @param i element to be removed + * @return element removed + */ + public static <E> void del(ArrayList<E> array, int i) { + E last = array.removeLast(); + if (i < array.size()) array.set(i, last); + } + + /** + * Search a list for an element by reference + * + * @param ary List to search in + * @param x Object to be searched + * @return >= 0 on success, -1 on failure + */ + public static <E> int find( ArrayList<E> ary, E x ) { + for( int i=0; i<ary.size(); i++ ) + if( ary.get(i)==x ) + return i; + return -1; + } + /** + * Search a list for an element by reference + * + * @param ary List to search in + * @param x Object to be searched + * @return >= 0 on success, -1 on failure + */ + public static <E> int find( E[] ary, E x ) { + for( int i=0; i<ary.length; i++ ) + if( ary[i]==x ) + return i; + return -1; + } + + // Rotate a long, nice for hashes + public static long rot( long x, int n ) { return (x<<n) | (x>>>n); } + + // Fold a 64bit hash into 32 bits + public static int fold( long x ) { return (int)((x>>32) ^ x); } +} diff --git a/chapter25/src/main/smp/sys.smp b/chapter25/src/main/smp/sys.smp new file mode 100644 index 00000000..815212ab --- /dev/null +++ b/chapter25/src/main/smp/sys.smp @@ -0,0 +1,9 @@ +// -*- mode: java; -*- + +// top-level default standard library import + +var libc = libc; +var io = io; +var aryu8 = aryu8; +var Scan = Scan; +var aryi64 = aryi64; diff --git a/chapter25/src/main/smp/sys/Ary.smp b/chapter25/src/main/smp/sys/Ary.smp new file mode 100644 index 00000000..0f667695 --- /dev/null +++ b/chapter25/src/main/smp/sys/Ary.smp @@ -0,0 +1,10 @@ +// -*- mode: java; -*- + +// Array utilities + +// Copy byte array contents +val copy = { u8[] dst, int doff, u8[~] src, int soff, int len -> + for( int i=0; i<len; i++ ) + dst[i+doff] = src[i+soff]; + return dst; +}; diff --git a/chapter25/src/main/smp/sys/Scan.smp b/chapter25/src/main/smp/sys/Scan.smp new file mode 100644 index 00000000..acf366f7 --- /dev/null +++ b/chapter25/src/main/smp/sys/Scan.smp @@ -0,0 +1,36 @@ +// -*- mode: java; -*- + +// Scanner; a simple string parser. +struct Scan { + int !x; // Cursor + u8[~] buf; // Immutable byte array, must be set in 2nd constructor + + new Scan = { u8[~] b -> + buf = b; + }; + + // Skip whitespace + val skip = { -> + while( x < buf# && buf[x] <= ' ' ) + x++; + return self; + }; + + // True if find required character, and skip it; else False. + val match = { u8 c -> + skip(); + if( buf[x] != c ) return false; + x++; + return true; + }; + + // Parse a simple number + val number = { -> + skip(); + bool neg = match('-'); + i64 n = 0; + while( '0' <= buf[x] & buf[x] <= '9' ) + n = n*10 + (buf[x++] - '0'); + return neg ? -n : n; + }; +}; diff --git a/chapter25/src/main/smp/sys/aryi64.smp b/chapter25/src/main/smp/sys/aryi64.smp new file mode 100644 index 00000000..499431d8 --- /dev/null +++ b/chapter25/src/main/smp/sys/aryi64.smp @@ -0,0 +1,63 @@ +// -*- mode: java; -*- + +// Ary/Vector of i64s +struct aryi64 { + u32 !len = 0; + i64[] !buf; + + new aryi64 = { i64[] b -> + buf = b; + }; + + // Grow as needed + val _grow = { int len2 -> + if( len+len2 < buf# ) return self; + int newlen = buf# * 2; + while( len+len2 >= newlen ) newlen *= 2; + i64[] !buf2 = new i64[newlen]; + for( int i=0; i<buf#; i++ ) + buf2[i] = buf[i]; + buf = buf2; + self; + }; + + // Add a i64 to aryu8 + val add = { i64 c -> + _grow(1).buf[len++] = c; + self; + }; + + // Write to aryu8 + val str = { -> + // TODO: clunky default constructor syntax + var str = new aryu8(new u8[1]).add('['); + for( int i=0; i<len; i++ ) + str.num(buf[i]).add(',').add(' '); + str.undo(2).add(']'); + }; +}; + + +// Parse a comma-separated list of numbers, wrapped with square brackets, +// with optional whitespace e.g. "[ -17, 0,99,12456789 ] ". +// parseAryI64 & aryi64.str are bijections modulo whitespace. +// Returns null if the parse failed, without giving a clue as to why. +val parseAryI64 = { u8[~] str -> + Scan !s = new Scan(str); + if( !s.match('[') ) return null; + // Array of parsed results + var nums = new aryi64(new i64[1]); + + // Parse a list of numbers + if( s.match(']') ) // Handle the empty list + return nums; + + // Parse number commas "123, " until something else + while( s.x < s.buf# ) { + nums.add(s.number()); + if( !s.match(',') ) + break; + }; + // Close the list + return s.match(']') ? nums : null; +}; diff --git a/chapter25/src/main/smp/sys/aryu8.smp b/chapter25/src/main/smp/sys/aryu8.smp new file mode 100644 index 00000000..0ce4bd23 --- /dev/null +++ b/chapter25/src/main/smp/sys/aryu8.smp @@ -0,0 +1,66 @@ +// -*- mode: java; -*- + +// Ary/Vector of u8s - a resizable array of bytes. +struct aryu8 { + u32 !len = 0; + u8[] !buf; + + new aryu8 = { u8[] b -> + buf = b; + }; + + // Grow as needed + val _grow = { int len2 -> + if( len+len2 < buf# ) return self; + int newlen = buf# * 2; + while( len+len2 >= newlen ) newlen *= 2; + u8[] !buf2 = new u8[newlen]; + Ary.copy(buf2,0,buf,0,buf#); + buf = buf2; + self; + }; + + val clear = { -> len = 0; self; }; + + // Add a character to aryu8 + val add = { u8 c -> + _grow(1).buf[len++] = c; + self; + }; + + // Add all of a plain array to the aryu8 + val addAry = { u8[~] ary -> + _grow(ary#); + Ary.copy(buf,len,ary,0,ary#); + len += ary#; + self; + }; + + // Remove x bytes from the tail. + val undo = { int x -> + len = len >= x ? len-x : 0; + self; + }; + + // Print ASCII number into aryu8 + val num = { i64 x -> + if( !x ) { add('0'); return self; } + if( x < 0 ) { add('-'); x = -x; } + i64 ten = 1, log = 0; + while( ten <= x ) { ten *= 10; log++; } + for( int i=0; i<log; i++ ) { + ten /= 10; + i64 q = x/ten; + u8 digit = q+'0'; + add(digit); + x -= q*ten; + } + self; + }; + + // Write to fd, returns the number of bytes written (len). + val write = { i32 fd -> + i64 ptr = buf; // Cast buffer pointer to i64 for native C call + libc.write(fd,ptr,len); + }; +}; diff --git a/chapter25/src/main/smp/sys/io.smp b/chapter25/src/main/smp/sys/io.smp new file mode 100644 index 00000000..b94e89fa --- /dev/null +++ b/chapter25/src/main/smp/sys/io.smp @@ -0,0 +1,32 @@ +// -*- mode: java; -*- + +// Simple I/O Library + +// Write byte array to stdout +val p = { u8[~] str -> + i64 ptr = str; // cast array base to i64 + return libc.write(1,ptr,str#); +}; + +val p_noInline = { u8[~] str -> + i64 ptr = str; // cast array base to i64 + return libc.write(1,ptr,str#); +}; + +// Return stdin as a u8[]. +val stdin = { -> + var buf = new u8[8*1024]; + i64 ptr = buf; + int len = 0; + while( true ) { + int rez = libc.read(0,ptr,buf#); + if( rez < 0 ) libc._exit(libc.errno); + len += rez; + if( rez < buf# ) + break; + libc._exit(-2); // Need to implement for more than 8K input + } + // Compress + var dst = new u8[len]; + return Ary.copy(dst,0,buf,0,len); +}; diff --git a/chapter25/src/main/smp/sys/libc.smp b/chapter25/src/main/smp/sys/libc.smp new file mode 100644 index 00000000..42fc7313 --- /dev/null +++ b/chapter25/src/main/smp/sys/libc.smp @@ -0,0 +1,20 @@ +// -*- mode: java; -*- + +// libc bindings: +// https://www.man7.org/linux/man-pages/man2/<libc call>.2.html + +// fd buf len -> len +{ i32 i64 u32 -> u32 } write = "C"; +// fd buf len -> len +{ i32 i64 u32 -> u32 } read = "C"; +// status +{ i32 -> i32 } _exit = "C"; +// addr len prot flags fd off -> void* +{ i64 i64 i32 i32 i32 i32 -> i64 } mmap = "C"; +// mmap flags +i32 PROT_EXEC = 4; +i32 PROT_READ = 1; +i32 PROT_WRITE= 2; +i32 PROT_NONE = 0; +i32 MAP_PRIVATE = 2; +i32 MAP_ANON = 32; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter25/src/test/java/com/seaofnodes/simple/BrainFuckTest.java new file mode 100644 index 00000000..0ebee2c4 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -0,0 +1,83 @@ +package com.seaofnodes.simple; + +import java.io.IOException; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class BrainFuckTest { + + @Test public void testBrainFuck() throws IOException { + String brain_fuck = "Hello World!\n"; + String src = +""" +// -*- mode: java; -*- +val brain_fuck = { -> + var !program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; + int d = 0; + u8[] !output = new u8[0]; + u8[] !data = new u8[100]; + + for( int pc = 0; pc < program#; pc++ ) { + var command = program[pc]; + if (command == '>') { + d++; + } else if (command == '<') { + d--; + } else if (command == '+') { + data[d]++; + } else if (command == '-') { + data[d]--; + } else if (command == '.') { + // Output a byte; increase the output array size + var old = output; + output = new u8[output# + 1]; + for( int i = 0; i < old#; i++ ) + output[i] = old[i]; + output[old#] = data[d]; // Add the extra byte on the end + } else if (command == ',') { + data[d] = 42; + } else if (command == '[') { + if (data[d] == 0) { + for( int d = 1; d > 0; ) { + command = program[++pc]; + if (command == 91) d++; + if (command == 93) d--; + } + } + } else if (command == ']') { + if (data[d]) { + for( int d = 1; d > 0; ) { + command = program[--pc]; + if (command == 93) d++; + if (command == 91) d--; + } + } + } + } + return output; +}; +"""; + TestC.runC(src,"brain_fuck", brain_fuck, 40); + + EvalRisc5 R5 = TestRisc5.build( src, "brain_fuck", 0, 28, false); + int trap = R5.step(100000); + assertEquals(0,trap); + int ptr = (int)R5.regs[com.seaofnodes.simple.node.cpus.riscv.riscv.A0]; + assertEquals(brain_fuck.length(),R5.ld4s(ptr)); + for( int i=0; i<brain_fuck.length(); i++ ) + assertEquals(brain_fuck.charAt(i), R5.ld1z(ptr+4+i)); + + EvalArm64 A5 = TestArm64.build("brain_fuck", src, 0, 28, false); + int trap_arm = A5.step(100000); + assertEquals(0,trap_arm); + assertEquals(0,trap); + int ptr_arm = (int)A5.regs[com.seaofnodes.simple.node.cpus.arm.arm.X0]; + assertEquals(brain_fuck.length(),A5.ld4s(ptr_arm)); + + for( int i=0; i<brain_fuck.length(); i++ ) + assertEquals(brain_fuck.charAt(i), A5.ld1z(ptr+4+i)); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter01Test.java new file mode 100644 index 00000000..fa7fb68a --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter01Test.java @@ -0,0 +1,95 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter01Test { + + @Test + public void testSimpleProgram() { + CodeGen code = new CodeGen("1;").parse(); + Node expr = code.expr(); + if( expr instanceof ConstantNode con ) { + assertEquals(code._start,con.in(0)); + assertEquals(TypeInteger.constant(1), con._type); + } else { + fail(); + } + } + + @Test + public void testZero() { + CodeGen code = new CodeGen("return 0;"); + code.parse(); + Node use = code.expr(); + if( use instanceof ConstantNode con && con._type instanceof TypeInteger ) + assertEquals(TypeInteger.constant(0),con._type); + } + + @Test + public void testBad1() { + try { + new CodeGen("ret;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'ret'",e.getMessage()); + } + } + + @Test + public void testBad2() { + try { + new CodeGen("return 0123;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error: integer values cannot start with '0'",e.getMessage()); + } + } + + @Test + public void testNotBad3() { + // this test used to fail in chapter 1 + assertEquals("return 12;", new CodeGen("return - -12;").parse().print()); + } + + @Test + public void testBad4() { + try { + new CodeGen("return 100").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error, expected `;` but found `EOF`",e.getMessage()); + } + } + + @Test + public void testNotBad5() { + // this test used to fail in chapter 1 + assertEquals("return -100;", new CodeGen("return -100;").parse().print()); + } + + @Test + public void testBad6() { + try { + new CodeGen("return100;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'return100'",e.getMessage()); + } + } + + @Test + public void testBad7() { + try { + new CodeGen("return 1;}").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error, unexpected but found `}`",e.getMessage()); + } + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter02Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter02Test.java new file mode 100644 index 00000000..631e1487 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter02Test.java @@ -0,0 +1,61 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter02Test { + + @Test + public void testParseGrammar() { + CodeGen code = new CodeGen("return 1+2*3+-5;"); + code.parse(); // No more disable peepholes + assertEquals("return 2;", code.print()); + } + + @Test + public void testAddPeephole() { + CodeGen code = new CodeGen("return 1+2;"); + code.parse(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testSubPeephole() { + CodeGen code = new CodeGen("return 1-2;"); + code.parse(); + assertEquals("return -1;", code.print()); + } + + @Test + public void testMulPeephole() { + CodeGen code = new CodeGen("return 2*3;"); + code.parse(); + assertEquals("return 6;", code.print()); + } + + @Test + public void testDivPeephole() { + CodeGen code = new CodeGen("return 6/3;"); + code.parse(); + assertEquals("return 2;", code.print()); + } + + @Test + public void testMinusPeephole() { + CodeGen code = new CodeGen("return 6/-3;"); + code.parse(); + assertEquals("return -2;", code.print()); + } + + @Test + public void testExample() { + CodeGen code = new CodeGen("return 1+2*3+-5;"); + code.parse(); + assertEquals("return 2;", code.print()); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter03Test.java new file mode 100644 index 00000000..c96acddc --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter03Test.java @@ -0,0 +1,73 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter03Test { + + @Test + public void testVarDecl() { + CodeGen code = new CodeGen("int a=1; return a;"); + code.parse(); assertEquals("return 1;", code.print()); + } + + @Test + public void testVarAdd() { + CodeGen code = new CodeGen("int a=1; int b=2; return a+b;"); + code.parse(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testVarScope() { + CodeGen code = new CodeGen("int a=1; int b=2; int c=0; { int b=3; c=a+b; } return c;"); + code.parse(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testVarScopeNoPeephole() { + CodeGen code = new CodeGen("int a=1; int b=2; int !c=0; { int b=3; c=a+b; } return c; "); + code.parse(); // Peepholes always run now + assertEquals("return 4;", code.print()); + } + + @Test + public void testVarDist() { + CodeGen code = new CodeGen("int x0=1; int y0=2; int x1=3; int y1=4; return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1); "); + code.parse(); + assertEquals("return 8;", code.print()); + } + + @Test + public void testSelfAssign() { + try { + new CodeGen("int a=a; return a;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'a'",e.getMessage()); + } + } + + @Test + public void testRedeclareVar() { + try { + new CodeGen("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Redefining name 'b'",e.getMessage()); + } + } + + @Test + public void testBad1() { + try { + new CodeGen("int a=1; int b=2; int !c=0; { int b=3; c=a+b;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error, expected `}` but found `EOF`",e.getMessage()); + } + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter04Test.java new file mode 100644 index 00000000..eaa959cb --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter04Test.java @@ -0,0 +1,131 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import org.junit.Ignore; + +import static org.junit.Assert.*; + +public class Chapter04Test { + + @Test + public void testPeephole() { + CodeGen code = new CodeGen("return 1+arg+2; "); + code.parse(); + assertEquals("return (arg+3);", code.print()); + } + + @Test + public void testPeephole2() { + CodeGen code = new CodeGen("return (1+arg)+2;"); + code.parse(); + assertEquals("return (arg+3);", code.print()); + } + + @Test + public void testAdd0() { + CodeGen code = new CodeGen("return 0+arg;"); + code.parse(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testAddAddMul() { + CodeGen code = new CodeGen("return arg+0+arg;"); + code.parse(); + assertEquals("return (arg<<1);", code.print()); + } + + @Test + public void testPeephole3() { + CodeGen code = new CodeGen("return 1+arg+2+arg+3; "); + code.parse(); + assertEquals("return ((arg<<1)+6);", code.print()); + } + + @Test + public void testMul1() { + CodeGen code = new CodeGen("return 1*arg;"); + code.parse(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testVarArg() { + CodeGen code = new CodeGen("return arg; "); + code.parse(); + assertTrue(code.ctrl() instanceof FunNode); + assertTrue(code.expr() instanceof ParmNode); + } + + // Adding functions means `main` does not constant fold the incoming arg value + @Ignore @Test + public void testConstantArg() { + CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2)).parse(); + assertEquals("return 2;", code.expr().in(1).toString()); + } + + @Test + public void testCompEq() { + CodeGen code = new CodeGen("return 3==3; "); + code.parse(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testCompEq2() { + CodeGen code = new CodeGen("return 3==4; "); + code.parse(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testCompNEq() { + CodeGen code = new CodeGen("return 3!=3; "); + code.parse(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testCompNEq2() { + CodeGen code = new CodeGen("return 3!=4; "); + code.parse(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testBug1() { + CodeGen code = new CodeGen("int a=arg+1; int !b=a; b=1; return a+2; "); + code.parse(); + assertEquals("return (arg+3);", code.print()); + } + + @Test + public void testBug2() { + CodeGen code = new CodeGen("int !a=arg+1; a=a; return a; "); + code.parse(); + assertEquals("return (arg+1);", code.print()); + } + + @Test + public void testBug3() { + try { new CodeGen("inta=1; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'inta'",e.getMessage()); } + } + + @Test + public void testBug4() { + CodeGen code = new CodeGen("return -arg;"); + code.parse(); + assertEquals("return (-arg);", code.print()); + } + + @Test + public void testBug5() { + CodeGen code = new CodeGen("return arg--2;"); + code.parse(); + assertEquals("return (arg--2);", code.print()); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java new file mode 100644 index 00000000..51ff20ba --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -0,0 +1,186 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter05Test { + + @Test + public void testIfStmt() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg == 1) + a = arg+2; +else { + a = arg-3; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print()); + } + + @Test + public void testTest() { + CodeGen code = new CodeGen( +""" +int c = 3; +int b = 2; +if (arg == 1) { + b = 3; + c = 4; +} +return c; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,4,3);", code.print()); + } + + @Test + public void testReturn2() { + CodeGen code = new CodeGen( +""" +if( arg==1 ) + return 3; +else + return 4; +"""); + code.parse(); + assertEquals("return Phi(Region,3,4);", code.print()); + } + + @Test + public void testIfMergeB() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=0; +if( arg==1 ) + b=a; +else + b=a+1; +return a+b;"""); + code.parse().opto(); + assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print()); + } + + @Test + public void testIfMerge2() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=arg+2; +if( arg==1 ) + b=b+a; +else + a=b+1; +return a+b;"""); + code.parse().opto(); + assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print()); + } + + @Test + public void testMerge3() { + CodeGen code = new CodeGen( +""" +int a=1; +if( arg==1 ) + if( arg==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,3,5,2,4);", code.print()); + } + + @Test + public void testMerge4() { + CodeGen code = new CodeGen( +""" +int a=0; +int b=0; +if( arg ) + a=1; +if( arg==0 ) + b=2; +return arg+a+b; +"""); + code.parse().opto(); + assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print()); + } + + @Test + public void testMerge5() { + CodeGen code = new CodeGen( +""" +int a=arg==2; +if( arg==1 ) +{ + a=arg==3; +} +return a; +"""); + code.parse().opto(); + assertEquals("return (arg==Phi(Region,3,2));", code.print()); + } + + @Test + public void testTrue() { + CodeGen code = new CodeGen("return true;").parse(); + assertEquals("return 1;",code.print()); + } + + @Test + public void testHalfDef() { + try { new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); } + } + + @Test + public void testHalfDef2() { + try { new CodeGen("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'b'",e.getMessage()); } + } + + @Test + public void testRegress1() { + try { new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); } + } + + + @Test + public void testBadNum() { + try { new CodeGen("return 1-;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Syntax error, expected `an identifier or expression` but found `;`",e.getMessage()); } + } + + @Test + public void testKeyword1() { + try { new CodeGen("int true=0; return true;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'true'",e.getMessage()); } + } + + @Test + public void testKeyword2() { + try { new CodeGen("int else=arg; if(else) else=2; else else=1; return else;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'else'",e.getMessage()); } + } + + @Test + public void testKeyword3() { + try { new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); } + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java new file mode 100644 index 00000000..fdf6963f --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java @@ -0,0 +1,305 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter06Test { + + @Test + public void testPeepholeReturn() { + CodeGen code = new CodeGen(""" +if( true ) return 2; +return 1; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertTrue(code.ctrl() instanceof FunNode); + } + + @Test + public void testPeepholeRotate() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg) + a = 2; +return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3 +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testPeepholeCFG() { + CodeGen code = new CodeGen( +""" +int a=1; +if( true ) + a=2; +else + a=3; +return a; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertTrue(code.ctrl() instanceof FunNode); + } + + @Test + public void testIfIf() { + CodeGen code = new CodeGen( +""" +int a=1; +if( arg!=1 ) + a=2; +else + a=3; +int b=4; +if( a==2 ) + b=42; +else + b=5; +return b; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,42,5);", code.print()); + } + + @Test + public void testIfArgIf() { + CodeGen code = new CodeGen( +""" +int a=1; +if( 1==1 ) + a=2; +else + a=3; +int b=4; +if( arg==2 ) + b=a; +else + b=5; +return b;"""); + code.parse().opto(); + assertEquals("return Phi(Region,2,5);", code.print()); + } + + @Test + public void testMerge3With2() { + CodeGen code = new CodeGen( +""" +arg=2; +int a=1; +if( arg==1 ) + if( arg==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(2)).parse().opto(); + assertEquals("return 5;", code.print()); + } + + @Test + public void testMerge3With1() { + CodeGen code = new CodeGen( +""" +arg=1; +int a=1; +if( arg==1 ) + if( arg==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(1)).parse().opto(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testMerge3Peephole() { + CodeGen code = new CodeGen( +""" +int a=1; +if( arg==1 ) + if( 1==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,3,5,4);", code.print()); + } + + @Test + public void testMerge3Peephole1() { + CodeGen code = new CodeGen( +""" +arg=1; +int a=1; +if( arg==1 ) + if( 1==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(1)); + code.parse().opto(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testMerge3Peephole3() { + CodeGen code = new CodeGen( +""" +arg=3; +int a=1; +if( arg==1 ) + if( 1==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(3)); + code.parse().opto(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testDemo1NonConst() { + CodeGen code = new CodeGen(""" +int a = 0; +int b = 1; +if( arg ) { + a = 2; + if( arg ) { b = 2; } + else b = 3; +} +return a+b; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,4,1);", code.print()); + } + + + @Test + public void testDemo1True() { + CodeGen code = new CodeGen( +""" +arg=1; +int a = 0; +int b = 1; +if( arg ) { + a = 2; + if( arg ) { b = 2; } + else b = 3; +} +return a+b; +""", TypeInteger.constant(1)); + code.parse().opto(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testDemo1False() { + CodeGen code = new CodeGen( +""" +arg=0; +int a = 0; +int b = 1; +if( arg ) { + a = 2; + if( arg ) { b = 2; } + else b = 3; +} +return a+b; +""", TypeInteger.constant(0)); + code.parse().opto(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testDemo2NonConst() { + CodeGen code = new CodeGen(""" +int a = 0; +int b = 1; +int c = 0; +if( arg ) { + a = 1; + if( arg==2 ) { c=2; } else { c=3; } + if( arg ) { b = 2; } + else b = 3; +} +return a+b+c; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,6,1,5);", code.print()); + } + + + @Test + public void testDemo2True() { + CodeGen code = new CodeGen( +""" +arg=1; +int a = 0; +int b = 1; +int c = 0; +if( arg ) { + a = 1; + if( arg==2 ) { c=2; } else { c=3; } + if( arg ) { b = 2; } + else b = 3; +} +return a+b+c; +""", TypeInteger.constant(1)); + code.parse().opto(); + assertEquals("return 6;", code.print()); + } + + @Test + public void testDemo2arg2() { + CodeGen code = new CodeGen( +""" +arg=2; +int a = 0; +int b = 1; +int c = 0; +if( arg ) { + a = 1; + if( arg==2 ) { c=2; } else { c=3; } + if( arg ) { b = 2; } + else b = 3; +} +return a+b+c; +""", TypeInteger.constant(2)); + code.parse().opto(); + assertEquals("return 5;", code.print()); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java new file mode 100644 index 00000000..2ca6f8dd --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java @@ -0,0 +1,222 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter07Test { + + @Test + public void testExample() { + CodeGen code = new CodeGen( +""" +while(arg < 10) { + arg = arg + 1; +} +return arg; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testRegression() { + CodeGen code = new CodeGen( +""" +int a = 1; +if(arg){}else{ + while(a < 10) { + a = a + 1; + } +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", code.print()); + } + + @Test + public void testWhileNested() { + CodeGen code = new CodeGen( +""" +int sum = 0; +int i = 0; +while(i < arg) { + i = i + 1; + int j = 0; + while( j < arg ) { + sum = sum + j; + j = j + 1; + } +} +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", code.print()); + } + + @Test + public void testWhileScope() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + if (a == 2) a = 3; + else b = 4; +} +return b; +"""); + code.parse(); + assertEquals("return Phi(Loop,2,Phi(Region,Phi_b,4));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhileNestedIfAndInc() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + if (a == 2) a = 3; + else b = 4; + b = b + 1; + a = a + 1; +} +return b; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + + @Test + public void testWhile() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + a = a + 1; + a = a + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhilePeep() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + a = a + 1; + a = a + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile2() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(arg) a = 2; +return a; +"""); + code.parse(); + assertEquals("return Phi(Loop,1,2);", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile2Peep() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(arg) a = 2; +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,2);", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile3() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile3Peep() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile4() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile4Peep() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java new file mode 100644 index 00000000..09669cc7 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java @@ -0,0 +1,208 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter08Test { + + @Test + public void testEx6() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + break; + if (arg == 6) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Region,(Phi(Loop,arg,Add)+1),Add,Phi_arg);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + Assert.assertEquals( "5", Eval2.eval(code, 1)); + Assert.assertEquals("10", Eval2.eval(code, 6)); + } + + + @Test + public void testEx5() { + CodeGen code = new CodeGen( + """ +int a = 1; +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; + if (arg == 7) + continue; + a = a + 1; +} +return a; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1),Phi_a));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + + @Test + public void testEx4() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; + if (arg == 6) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + } + + @Test + public void testEx3() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 6) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + } + + + @Test + public void testEx2() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; + if (arg == 6) + continue; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + + @Test + public void testEx1() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testRegress1() { + CodeGen code = new CodeGen( + """ +while( arg < 10 ) { + int a = arg+2; + if( a > 4 ) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testRegress2() { + CodeGen code = new CodeGen("if(1) return 0; else while(arg>- -arg) arg=arg+1; return 0;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBreakOutsideLoop() { + try { + new CodeGen(""" +if(arg <= 10) { + break; + arg = arg + 1; +} +return arg; +""").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("No active loop for a break or continue",e.getMessage()); + } + } + + @Test + public void testRegress3() { + CodeGen code = new CodeGen(""" +while(arg < 10) { + break; +} +return arg; +"""); + code.parse().opto(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testRegress4() { + CodeGen code = new CodeGen(""" +int a = 1; +while(arg < 10) { + a = a + 1; + if (arg > 2) { + int a = 17; + break; + } +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + } + + @Test + public void testRegress5() { + CodeGen code = new CodeGen(""" +int a = 1; +while(1) { + a = a + 1; + if (a<10) continue; + break; +} +return a; +"""); + code.parse().opto(); + assertEquals("return (Phi(Loop,1,Add)+1);", code.print()); + assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 ); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java new file mode 100644 index 00000000..d36cdf7f --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -0,0 +1,299 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Assert; +import org.junit.Test; +import org.junit.Ignore; + +import static org.junit.Assert.*; + +public class Chapter09Test { + + @Test + @Ignore + public void testJig() { + CodeGen code = new CodeGen(""" +int v0=0; +arg=0; +while(v0) { + while(1) if(arg*arg*0==0) {} + while(0) {} + arg=1; +} +return 0; + """); + code.parse().opto(); + } + + @Test + public void testGVN1() { + CodeGen code = new CodeGen( + """ +int x = arg + arg; +if(arg < 10) { + return arg + arg; +} +else { + x = x + 1; +} +return x; + """); + code.parse().opto(); + assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print()); + Assert.assertEquals( "2", Eval2.eval(code, 1)); + Assert.assertEquals("23", Eval2.eval(code, 11)); + } + + @Test + public void testGVN2() { + CodeGen code = new CodeGen("return arg*arg-arg*arg;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + Assert.assertEquals("0", Eval2.eval(code, 1, 1)); + } + + @Test + public void testWorklist1() { + CodeGen code = new CodeGen(""" +int step = 1; +while (arg < 10) { + arg = arg + step + 1; +} +return arg; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+2));", code.print()); + Assert.assertEquals("11", Eval2.eval(code, 1)); + } + + @Test + public void testWorklist2() { + CodeGen code = new CodeGen( + """ +int cond = 0; +int one = 1; +while (arg < 10) { + if (cond) one = 2; + arg = arg + one*3 + 1; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+4));", code.print()); + Assert.assertEquals("13", Eval2.eval(code, 1)); + } + + @Test + public void testWorklist3() { + CodeGen code = new CodeGen( + """ +int v1 = 0; +int v2 = 0; +int v3 = 0; +int v4 = 0; +int v5 = 0; +int v6 = 0; +int v7 = 0; +int v8 = 0; +while (arg) { + if (v1) v2 = 1; + if (v2) v3 = 1; + if (v3) v4 = 1; + if (v4) v5 = 1; + if (v5) v6 = 1; + if (v6) v7 = 1; + if (v7) v8 = 1; + arg = arg + v8 + 1; +} +return arg; + """); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testRegionPeepBug() { + CodeGen code = new CodeGen( + """ +int v0=0; +int v1=0; +while(v1+arg) { + arg=0; + int v2=v0; + while(arg+1) {} + v0=1; + v1=v2; +} +return 0; + """); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testWhile0() { + CodeGen code = new CodeGen("while(0) continue; if(0) arg=0; return arg;"); + code.parse().opto(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testWhile1() { + CodeGen code = new CodeGen(""" +if(0) while(0) { + int arg=arg; + while(0) {} +} +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + + @Test + public void testPrecedence() { + CodeGen code = new CodeGen("return 3-1+2;"); + code.parse().opto(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testSwap2() { + CodeGen code = new CodeGen("return 1+(1+1);"); + code.parse().opto(); + assertEquals("return 3;", code.print()); + } + + + @Test + public void testFuzz0() { + CodeGen code = new CodeGen(""" +int one = 1; +int a = 0; +int zero = 0; +while(arg) { + a = -(one + a + 2); + arg = arg + 1; + one = one + zero; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print()); + } + + @Test + public void testFuzz1() { + CodeGen code = new CodeGen(""" +while(1) {} +while(arg) break; +while(arg) arg=0; +arg=0; +int v0=0!=0<-0; +return -0+0+0; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + + @Test + public void testFuzz2() { + CodeGen code = new CodeGen("return 0+-0;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testFuzz3() { + CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0; return 1;"); + code.parse().opto(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testFuzz4() { + CodeGen code = new CodeGen(""" +while(1) { + arg=0<=0; + if(1<0) while(arg==-0) arg=arg-arg; +} +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testFuzz5() { + CodeGen code = new CodeGen(""" +{ + int v0=0; + while(1) + int v1=0--0; + while(v0) + break; + while(-v0) { + while(0+0+v0) continue; + break; + } + if(-0!=-0+0+v0) while(0+0+0+0) + break; +} +return 0!=0; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testFuzz6() { + CodeGen code = new CodeGen( +""" +int v0=0; +while(0==1) while(v0) + v0=1+v0; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testFuzz7() { + CodeGen code = new CodeGen(""" +while(1) {} +int v0=0; +while(v0) + {} +int v1=0; +while(1) + v1=1; +return v1+v0; + """); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testFuzz8() { + CodeGen code = new CodeGen("while(arg) arg = arg - 1; return arg;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testMeet() { + Type t1 = Type.TOP; + Type t2 = TypeInteger.TOP; + Assert.assertEquals(TypeInteger.TOP, t1.meet(t2)); + Assert.assertEquals(TypeInteger.TOP, t2.meet(t1)); + t1 = Type.BOTTOM; + t2 = TypeInteger.BOT; + Assert.assertEquals(Type.BOTTOM, t1.meet(t2)); + Assert.assertEquals(Type.BOTTOM, t2.meet(t1)); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java new file mode 100644 index 00000000..6b237b18 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java @@ -0,0 +1,418 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter10Test { + + @Test + public void testFuzzer() { + CodeGen code = new CodeGen( +""" +int a = arg/3; +int b = arg*5; +int x = arg*7; +int y = arg/11; +int p; int g; int h; +if( (arg/13)==0 ) { + p = x + y; + g = x; + h = y; +} else { + p = a + b; + g = a; + h = b; +} +int r = g+h; +return p-r; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testStruct() { + CodeGen code = new CodeGen(""" +struct _Bar { + int a; + int b; +}; +struct _Foo { + int x; +}; +_Foo? foo = null; +_Bar !bar = new _Bar; +bar.a = 1; +bar.a = 2; +return bar.a; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + } + + @Test + public void testExample() { + CodeGen code = new CodeGen(""" +struct Vector2D { int x; int y; }; +Vector2D !v = new Vector2D; +v.x = 1; +if (arg) + v.y = 2; +else + v.y = 3; +return v; +"""); + code.parse().opto(); + assertEquals("Stop[ return Test.Vector2D; return MEM[ 2:.x=0; 3:.y=0;]; ]", code.print()); + } + + @Test + public void testBug() { + CodeGen code = new CodeGen(""" +struct s0 { + int v0; +}; +s0? v1=null; +int v3=v1.zAicm; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'zAicm'",e.getMessage()); } + } + + @Test + public void testBug2() { + CodeGen code = new CodeGen(""" +struct s0 { int v0; }; +arg=0+new s0.0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); } + } + + @Test + public void testLoop() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar !bar = new Bar; +while (arg) { + bar.a = bar.a + 2; + arg = arg + 1; +} +return bar.a; +"""); + code.parse().opto(); + assertEquals("Stop[ return Phi(Loop,0,(Phi_a+2)); return MEM[ 2:.a=0;]; ]", code.print()); + } + + @Test + public void testIf() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar !bar = new Bar; +if (arg) bar = null; +bar.a = 1; +return bar.a; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar",e.getMessage()); } + } + + @Test + public void testIf2() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar? !bar = null; +if (arg) bar = new Bar; +bar.a = 1; +return bar.a; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); } + } + + @Test + public void testIf3() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar bar = null; +if (arg) bar = null; +bar.a = 1; +return bar.a; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar", e.getMessage()); } + } + + @Test + public void testIfOrNull() { + CodeGen code = new CodeGen(""" +struct _Bar { int a; }; +_Bar? !bar = new _Bar; +if (arg) bar = null; +if( bar ) bar.a = 1; +return bar.a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1,.a);", code.print()); + } + + @Test + public void testIfOrNull2() { + CodeGen code = new CodeGen( +""" +struct _Bar { int a; }; +_Bar? !bar = new _Bar; +if (arg) bar = null; +int rez = 3; +if( !bar ) rez=4; +else bar.a = 1; +return rez; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,4,3);", code.print()); + } + + @Test + public void testWhileWithNullInside() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +s0? !v0 = new s0; +int ret = 0; +while(arg) { + ret = v0.v0; + v0 = null; + arg = arg - 1; +} +return ret; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { + assertEquals("Might be null accessing 'v0'", e.getMessage()); } + } + + @Test + public void testRedeclareStruct() { + CodeGen code = new CodeGen(""" +struct s0 { + int v0; +}; +s0? v1=new s0; +s0? v1; +v1=new s0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Redefining name 'v1'", e.getMessage()); } + } + + @Test + public void testIter() { + // Build and use an iterator + CodeGen code = new CodeGen( +""" +struct Iter { + int x; + int len; +}; +Iter !i = new Iter; +i.len = arg; +int sum=0; +while( i.x < i.len ) { + sum = sum + i.x; + i.x = i.x + 1; +} +return sum; +"""); + code.parse().opto(); + assertEquals("Stop[ return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum)); return MEM[ 2:.x=0; 3:.len=0;]; ]", code.print()); + } + + + @Test + public void test1() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +s0 !ret = new s0; +while(arg) { + s0 !v0 = new s0; + v0.v0 = arg; + arg = arg-1; + if (arg==5) ret=v0; + +} +return ret; +"""); + code.parse().opto(); + assertEquals("Stop[ return Phi(Loop,Test.s0,Phi(Region,Test.s0,Phi_ret)); return MEM[ 2:.v0=0;]; ]", code.print()); + } + + @Test + public void test2() { + CodeGen code = new CodeGen(""" +struct _s0 {int v0;}; +_s0 !ret = new _s0; +_s0 !v0 = new _s0; +while(arg) { + v0.v0 = arg; + arg = arg-1; + if (arg==5) ret=v0; +} +return ret.v0; +"""); + code.parse().opto(); + assertEquals("return .v0;", code.print()); + } + + + @Test + public void test3() { + CodeGen code = new CodeGen(""" +struct _s0 {int v0;}; +_s0 !ret = new _s0; +while(arg < 10) { + _s0 !v1 = new _s0; + if (arg == 5) ret=v1; + arg = arg + 1; +} +return ret.v0; +"""); + code.parse().opto(); + assertEquals("return .v0;", code.print()); + } + + + + @Test + public void testModGlobalInConstructor() { + CodeGen code = new CodeGen( +""" +int cnt = 1; +// cnt is incremented exactly once, it is not part of s0 constructor +// hence s0.x is always 1, and cnt remains at 2 +struct s0 { int x = cnt++; }; +s0 a = new s0(); +s0 b = new s0(); +// a.x==1, b.x==1, cnt==2 +return a.x * 100 + b.x * 10 + cnt; +"""); + code.parse().iter().opto(); + assertEquals("Stop[ return 112; return MEM[ 2:___ 3:.x=1;]; ]", code.print()); + } + + + @Test + public void testBug3() { + CodeGen code = new CodeGen(""" +struct s0 { int f0; }; +return new s0; +int v0=null.f0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `;` but found `.`",e.getMessage()); } + } + + @Test + public void testBug4() { + CodeGen code = new CodeGen(""" +if(0) { + while(0) if(arg) continue; + int v0=0; + while(1) { + int v2=-arg; + v0=v2; + } +} +return 0; + """); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBug5() { + CodeGen code = new CodeGen(""" +struct s0 { + int f0; +}; +if(0) return 0; +else return new s0; +if(new s0.f0) return 0; + """); + code.parse().iter().opto().typeCheck(); + assertEquals("Stop[ return Test.s0; return MEM[ 2:.f0=0;]; ]", code.print()); + } + + @Test + public void testBug6MissedWorklist() { + CodeGen code = new CodeGen(""" +while(0) {} +int v4=0; +while(0<arg) { + v4=v4+1; + while(1) v4=-v4; + while(0) arg=-1; +} +return 0; + """); + code.parse().opto(); + } + + @Test + public void testBug7() { + CodeGen code = new CodeGen(""" +struct s0 { int f0; }; +s0 v0 = new s0; +while(v0.f0) {} +s0 v1 = v0; +return v1; + """); + code.parse().opto(); + assertEquals("Stop[ return (const)Test.s0; return MEM[ 2:.f0=0;]; ]", code.print()); + } + + + @Test + public void testBug8() { + CodeGen code = new CodeGen(""" +int v2=0; +while(0) +while(0) {} +{ + { + { + int v36=0; + { + while(0) { + { + while(-v2) { + { + while(v36) { + while(v2) return 0; + break; + } } + if(-v2) break; + } + } + } + } + } } +} +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBug9() { + CodeGen code = new CodeGen(""" +int v0=arg==0; +while(v0) continue; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java new file mode 100644 index 00000000..ab730b45 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -0,0 +1,281 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class Chapter11Test { + + @Test + public void testPrimes() { + CodeGen code = new CodeGen( +""" +if( arg < 2 ) return 0; +int primeCount = 1; +int prime = 3; +while( prime <= arg ) { + int isPrime = 1; + // Check for even case, so the next loop need only check odds + if( (prime/2)*2 == prime ) + continue; + // Check odds up to sqrt of prime + int j = 3; + while( j*j <= prime ) { + if( (prime/j)*j == prime ) { + isPrime = 0; + break; + } + j = j + 2; + } + if( isPrime ) + primeCount = primeCount + 1; + prime = prime + 2; +} +return primeCount; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print()); + assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below + assertEquals("1", Eval2.eval(code, 2)); // 2 + assertEquals("2", Eval2.eval(code, 3)); // 2, 3 + assertEquals("2", Eval2.eval(code, 4)); // 2, 3 + assertEquals("3", Eval2.eval(code, 5)); // 2, 3, 5 + assertEquals("4", Eval2.eval(code, 10)); // 2, 3, 5, 7 + } + + @Test + public void testClassField() { + CodeGen code = new CodeGen( +""" +if( arg < 2 ) return 0; +int primeCount = 1; +while( arg-- ) + primeCount++; +return primeCount; +"""); + code.driver(CodeGen.Phase.LocalSched); + assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below + assertEquals("3", Eval2.eval(code, 2)); // +} + + + @Test + public void testAntiDeps1() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v=new _S; +v.f = 2; +_S q=new _S; +int i=q.f; +i=v.f; +if (arg) v.f=1; +return i; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertEquals("2", Eval2.eval(code, 0)); + assertEquals("2", Eval2.eval(code, 1)); + } + + @Test + public void testAntiDeps2() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v = new _S; +v.f = arg; +_S !t = new _S; +int i = 0; +if (arg) { + if (arg+1) v = t; + i = v.f; +} else { + v.f = 2; +} +return i; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,.f,0);", code.print()); + } + + @Test + public void testAntiDeps3() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v0 = new _S; +_S? v1; +if (arg) v1 = new _S; +if (v1) { + v0.f = v1.f; +} else { + v0.f = 2; +} +return v0.f; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,.f,2);", code.print()); + } + + + @Test + public void testAntiDeps4() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v = new _S; +v.f = arg; +_S t = new _S; +int i = v.f; +if (arg+1) arg= 0; +while (arg) v.f = 2; +return i; +"""); + code.parse().opto(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testAntiDeps5() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v = new _S; +while(1) { + while(arg+1) { arg=arg-1; } + if (arg) break; + v.f = 2; +} +return v.f; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,2);", code.print()); + } + + @Test + public void testAntiDeps6() { + CodeGen code = new CodeGen( +""" +struct _S { int v; }; +_S !ptr=new _S; +while( -arg ) + ptr = new _S; +while(1) + arg = arg+ptr.v; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testAntiDeps7() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v = new _S; +_S t = new _S; +int i = v.f; +while (arg) { + v.f = arg; + arg = arg-1; +} +return i; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testAntiDeps8() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v = new _S; +_S t = new _S; +while(arg) { + arg=arg-1; + int f = v.f; + v.f = 2; + if (arg) arg = f; + else v.f = 3; +} +return arg; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testAntiDeps9() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v = new _S; +_S t = new _S; +if (arg) { + v.f=2; + int i=t.f; + v.f=i; +} +return v.f; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testExample2() { + CodeGen code = new CodeGen( +""" +struct _S { int f; }; +_S !v = new _S; +int i = arg; +while (arg > 0) { + int j = i/3; + if (arg == 5) + v.f = j; + arg = arg - 1; +} +return v; + """); + code.parse().opto(); + //assertEquals("return new _S;", code.print()); + } + + @Test + public void testScheduleUse() { + CodeGen code = new CodeGen( +""" +int v0=0; +while(0>=0) { + u1 v1=0; + v1=v0; + if(v1*0) + v0=-v1; +} +return 0; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testLoopCarriedDep() { + CodeGen code = new CodeGen( +""" +u32 v0=0; +{ + int v1=0; + while(v1) { + v1=1>>>v0!=0; + v0=v1/0; + } + return v1; +} +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java new file mode 100644 index 00000000..85317644 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -0,0 +1,62 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class Chapter12Test { + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testFloat() { + CodeGen code = new CodeGen( +""" +flt x = 3.14; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testSquareRoot() { + CodeGen code = new CodeGen( +""" +flt guess = arg; +while( 1 ) { + flt next = (arg/guess + guess)/2; + if( next == guess ) break; + guess = next; +} +return guess; +"""); + // TODO: One of the problems with <clinit> returning an OS error code, + // is it chops all results to int. + code.parse().opto(); + assertEquals("return Phi(Loop,(flt)arg,((Phi_guess+(ToFloat/Phi_guess))*0.5f));", code.print()); + assertEquals("3.0", Eval2.eval(code, 9)); + assertEquals("1.414213562373095", Eval2.eval(code, 2)); + } + + @Test + public void testFPOps() { + CodeGen code = new CodeGen( +""" +flt x = arg; +return x+1==x; +"""); + code.parse().opto(); + assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print()); + assertEquals("0", Eval2.eval(code, 1)); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java new file mode 100644 index 00000000..8bbcf21c --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java @@ -0,0 +1,281 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter13Test { + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testLinkedList0() { + CodeGen code = new CodeGen( +""" +struct LLI { LLI? next; int i; }; +LLI? !head = null; +while( arg ) { + LLI !x = new LLI; + x.next = head; + x.i = arg; + head = x; + arg = arg-1; +} +return head.next.i; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); } + } + + @Test + public void testLinkedList1() { + CodeGen code = new CodeGen( +""" +struct _LLI { _LLI? next; int q; }; +_LLI? !head = null; +while( arg ) { + _LLI !x = new _LLI; + x.next = head; + x.q = arg; + head = x; + arg = arg-1; +} +if( !head ) return 0; +_LLI? next = head.next; +if( next==null ) return 1; +return next.q; +"""); + code.parse().opto().typeCheck(); + assertEquals("return Phi(Region,1,.q,0);", code.print()); + assertEquals("2", Eval2.eval(code, 3)); + } + + @Test + public void testCoRecur() { + CodeGen code = new CodeGen( +""" +struct _int0 { int i; Test._flt0? f; }; +struct _flt0 { flt f; _int0? i; }; +_int0 !i0 = new _int0; +i0.i = 17; +_flt0 !f0 = new _flt0; +f0.f = 3.14; +i0.f = f0; +f0.i = i0; +return f0.i.f.i.i; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 17;", code.print()); + } + + @Test + public void testNullRef0() { + CodeGen code = new CodeGen( +""" +struct _N { _N? next; int i; }; +_N n = new _N; +return n.next; +"""); + code.parse().opto(); + assertEquals("return null;", code.print()); + } + + @Test + public void testNullRef1() { + CodeGen code = new CodeGen( +""" +struct M { int m; }; +struct N { M next; int i; new N = { M m -> next = m; }; }; +N n = new N(new M); +return n.next; +"""); + code.parse().opto(); + assertEquals("Stop[ return (const)Test.M; return MEM[ 2:.m=0;]; return MEM[ 2:___ 3:___ 4:___ 5:.next=Parm_m(Test.N.N,*Test.M {i64 !m; }); 6:.i=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:.i=0;]; ]", code.print()); + } + + @Test + public void testNullRef2() { + CodeGen code = new CodeGen( +""" +struct M { int m; }; +struct N { M next; int i; new N = { M m -> next = m; }; }; +N n = new N(null); +return n.next; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Argument #0 isa null, but must be a *Test.M {i64 !m; }",e.getMessage()); } + } + + @Test + public void testNullRef3() { + CodeGen code = new CodeGen( +""" +struct N { N? next; int i; }; +N !n = new N; +n.i = 3.14; +return n.i; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); } + } + + @Test + public void testNullRef4() { + CodeGen code = new CodeGen("return -null-5/null-5;"); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot 5 // null",e.getMessage()); } + } + + @Test public void testNullRef5() { + CodeGen code = new CodeGen("return null+42;"); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); } + } + + @Test + public void testEmpty() { + CodeGen code = new CodeGen( +""" +struct _S{}; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testForwardRef0() { + CodeGen code = new CodeGen( +""" +struct S1 { S2? s; }; +return new S2; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); } + } + + @Test + public void testForwardRef1() { + CodeGen code = new CodeGen( +""" +struct _S1 { _S2? s; }; +struct _S2 { int x; }; +return (new _S1).s=new _S2; +"""); + code.parse().opto(); + assertEquals("return _S2;", code.print()); + } + + @Test + public void testcheckNull() { + CodeGen code = new CodeGen( +""" +struct I { int i; }; +struct P { I? pi; }; +P !p1 = new P; +P !p2 = new P; +p2.pi = new I; +p2.pi.i = 2; +if (arg) p1 = new P; +return p1.pi.i + 1; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } + } + + @Test + public void testCoRecur2() { + CodeGen code = new CodeGen( +""" +struct _A { Test._B? f0; Test._C? f1; }; _A !a = new _A; +struct _B { Test._C? f0; Test._A? f1; }; _B !b = new _B; +struct _C { Test._A? f0; Test._B? f1; }; _C !c = new _C; + +a.f0=b; a.f1=c; +b.f0=c; b.f1=a; +c.f0=a; c.f1=b; + +return a.f0.f1.f0.f1.f0; + +"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("return Test._B;", code.print()); + assertEquals("Test._B{f0=Test._C{f0=Test._A{f0=$cyclic,f1=$cyclic},f1=$cyclic},f1=$cyclic}", Eval2.eval(code, 0)); + } + + @Test + public void testCoRecur3() { + CodeGen code = new CodeGen( +""" +struct _A { Test._L? a; Test._T? b; Test._F? c; }; _A !a = new _A; +struct _B { Test._M? a; Test._U? b; Test._G? c; }; _B !b = new _B; +struct _C { Test._N? a; Test._V? b; Test._H? c; }; _C !c = new _C; +struct _D { Test._O? a; Test._W? b; Test._I? c; }; _D !d = new _D; +struct _E { Test._P? a; Test._X? b; Test._J? c; }; _E !e = new _E; +struct _F { Test._Q? a; Test._Y? b; Test._K? c; }; _F !f = new _F; +struct _G { Test._R? a; Test._Z? b; Test._L? c; }; _G !g = new _G; +struct _H { Test._S? a; Test._A? b; Test._M? c; }; _H !h = new _H; +struct _I { Test._T? a; Test._B? b; Test._N? c; }; _I !i = new _I; +struct _J { Test._U? a; Test._C? b; Test._O? c; }; _J !j = new _J; +struct _K { Test._V? a; Test._D? b; Test._P? c; }; _K !k = new _K; +struct _L { Test._W? a; Test._E? b; Test._Q? c; }; _L !l = new _L; +struct _M { Test._X? a; Test._F? b; Test._R? c; }; _M !m = new _M; +struct _N { Test._Y? a; Test._G? b; Test._S? c; }; _N !n = new _N; +struct _O { Test._Z? a; Test._H? b; Test._T? c; }; _O !o = new _O; +struct _P { Test._A? a; Test._I? b; Test._U? c; }; _P !p = new _P; +struct _Q { Test._B? a; Test._J? b; Test._V? c; }; _Q !q = new _Q; +struct _R { Test._C? a; Test._K? b; Test._W? c; }; _R !r = new _R; +struct _S { Test._D? a; Test._L? b; Test._X? c; }; _S !s = new _S; +struct _T { Test._E? a; Test._M? b; Test._Y? c; }; _T !t = new _T; +struct _U { Test._F? a; Test._N? b; Test._Z? c; }; _U !u = new _U; +struct _V { Test._G? a; Test._O? b; Test._A? c; }; _V !v = new _V; +struct _W { Test._H? a; Test._P? b; Test._B? c; }; _W !w = new _W; +struct _X { Test._I? a; Test._Q? b; Test._C? c; }; _X !x = new _X; +struct _Y { Test._J? a; Test._R? b; Test._D? c; }; _Y !y = new _Y; +struct _Z { Test._K? a; Test._S? b; Test._E? c; }; _Z !z = new _Z; + +a.a=l; a.b=t; a.c=f; +b.a=m; b.b=u; b.c=g; +c.a=n; c.b=v; c.c=h; +d.a=o; d.b=w; d.c=i; +e.a=p; e.b=x; e.c=j; +f.a=q; f.b=y; f.c=k; +g.a=r; g.b=z; g.c=l; +h.a=s; h.b=a; h.c=m; +i.a=t; i.b=b; i.c=n; +j.a=u; j.b=c; j.c=o; +k.a=v; k.b=d; k.c=p; +l.a=w; l.b=e; l.c=q; +m.a=x; m.b=f; m.c=r; +n.a=y; n.b=g; n.c=s; +o.a=z; o.b=h; o.c=t; +p.a=a; p.b=i; p.c=u; +q.a=b; q.b=j; q.c=v; +r.a=c; r.b=k; r.c=w; +s.a=d; s.b=l; s.c=x; +t.a=e; t.b=m; t.c=y; +u.a=f; u.b=n; u.c=z; +v.a=g; v.b=o; v.c=a; +w.a=h; w.b=p; w.c=b; +x.a=i; x.b=q; x.c=c; +y.a=j; y.b=r; y.c=d; +z.a=k; z.b=s; z.c=e; + +return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c; + +"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("return Test._R;", code.print()); + assertEquals("Test._R{a=Test._C{a=Test._N{a=Test._Y{a=Test._J{a=Test._U{a=Test._F{a=Test._Q{a=Test._B{a=Test._M{a=Test._X{a=Test._I{a=Test._T{a=Test._E{a=Test._P{a=Test._A{a=Test._L{a=Test._W{a=Test._H{a=Test._S{a=Test._D{a=Test._O{a=Test._Z{a=Test._K{a=Test._V{a=Test._G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0)); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java new file mode 100644 index 00000000..807905a8 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java @@ -0,0 +1,226 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.StopNode; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter14Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + @Test + public void testRange() { + CodeGen code = new CodeGen( +""" +int b; +if( arg ) b=1; else b=0; +int c = 99; +if( b < 0 ) c = -1; +if( b > 2 ) c = 1; +return c; +"""); + code.parse().opto(); + assertEquals("return 99;", code.print()); + assertEquals("99", Eval2.eval(code, 0)); + } + + @Test + public void testU8() { + CodeGen code = new CodeGen( +""" +u8 b = 123; +b = b + 456;// Truncate +return b; +"""); + code.parse().opto(); + assertEquals("return 67;", code.print()); + assertEquals("67", Eval2.eval(code, 0)); + } + + + @Test + public void testU8While() { + CodeGen code = new CodeGen( +""" +u8 b = 123; +while( b ) b = b + 456;// Truncate +return b; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testU1() { + CodeGen code = new CodeGen( +""" +bool b = 123; +b = b + 456;// Truncate +u1 c = b; // No more truncate needed +return c; +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testAnd() { + CodeGen code = new CodeGen( +""" +int b = 123; +b = b+456 & 31; // Precedence +return b; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testRefLoad() { + CodeGen code = new CodeGen( +""" +struct _Foo { u1 b; }; +_Foo !f = new _Foo; +f.b = 123; +return f.b; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testSigned() { + CodeGen code = new CodeGen( +""" +i8 b = 255; // Chopped +return b; // Sign extend +"""); + code.parse().opto(); + assertEquals("return -1;", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + } + + @Test + public void testI8() { + CodeGen code = new CodeGen( +""" +i8 b = arg; +b = b + 1;// Truncate +return b; +"""); + code.parse().opto(); + assertEquals("return (((((arg<<56)>>56)+1)<<56)>>56);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + assertEquals("-128", Eval2.eval(code, 127)); + } + + @Test + public void testMask() { + CodeGen code = new CodeGen( +""" +u16 mask = (1<<16)-1; // AND mask +int c = 123456789 & mask; +return c; // +"""); + code.parse().opto(); + assertEquals("return 52501;", code.print()); + assertEquals("52501", Eval2.eval(code, 0)); + } + + @Test + public void testOr() { + CodeGen code = new CodeGen( +""" +return (arg | 123 ^ 456) >>> 1; +"""); + code.parse().opto(); + assertEquals("return (((arg|123)^456)>>>1);", code.print()); + assertEquals("217", Eval2.eval(code, 0)); + } + + @Test + public void testMaskFloat() { + CodeGen code = new CodeGen( +""" +flt f = arg; +arg = f & 1; +return arg; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot '&' flt",e.getMessage()); } + } + + @Test + public void testCloneAnd() { + CodeGen code = new CodeGen(""" +int v0=0; +u32 v1 = 1&(1<<arg)&(1<<arg); +while(arg) v1=-v0; +while(v1) break; +return v1; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testAndHigh() { + CodeGen code = new CodeGen(""" +int v0=0; +if(0&0>>>0) { + while(0) { + u8 v1=0; + v0=0>>>0; + v1=arg; + while(v1+0) {} + } +} +return v0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testTypes() { + CodeGen code = new CodeGen( +""" +i8 xi8 = 123456789; if( xi8 != 21 ) return -8; +i16 xi16 = 123456789; if( xi16 != -13035 ) return -16; +i32 xi32 = 123456789; if( xi32 != 123456789 ) return -32; +i64 xi64 = 123456789; if( xi64 != 123456789 ) return -64; +int xint = 123456789; if( xint != 123456789 ) return -64; + +u1 ui1 = 123456789; if( ui1 != 1 ) return 1; +u8 ui8 = 123456789; if( ui8 != 21 ) return 8; +u16 ui16 = 123456789; if( ui16 != 52501 ) return 16; +u32 ui32 = 123456789; if( ui32 != 123456789 ) return 32; + +flt fflt = 3.141592653589793; if( fflt != 3.141592653589793 ) return 3; +f64 ff64 = 3.141592653589793; if( ff64 != 3.141592653589793 ) return 3; +f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5; + +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java new file mode 100644 index 00000000..d4790b3b --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -0,0 +1,358 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Test; +import org.junit.Ignore; +import static org.junit.Assert.*; + +public class Chapter15Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + @Test + public void testCyclic() { + CodeGen code = new CodeGen( +""" +struct _C { _C? l; }; +_C !c = new _C; +c.l = c; +return c; +"""); + code.parse().opto(); + assertEquals("return Test._C;", code.print()); + assertEquals("Test._C{l=$cyclic}", Eval2.eval(code, 0)); + } + + @Test + public void testSafetyCheck() { + CodeGen code = new CodeGen( +""" +u8[] old = new u8[0]; +u8[] !output = new u8[1]; +int i = 0; +while (i < old#) { + output[i] = old[i]; + i = i + 1; +} +output[i] = 1; +return output; +"""); + code.parse().opto(); + assertEquals("return []u8;", code.print()); + assertEquals("\u0001", Eval2.eval(code, 0)); + } + + @Test + public void testBasic1() { + CodeGen code = new CodeGen( +""" +int[] is = new int[2]; +return is[1]; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testBasic2() { + CodeGen code = new CodeGen( +""" +int[] is = new int[2]; +int[] is2 = new int[2]; +return is[1]; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testBasic3() { + CodeGen code = new CodeGen( +""" +int[] !a = new int[2]; +a[0] = 1; +a[1] = 2; +return a[0]; +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testBasic4() { + CodeGen code = new CodeGen( +""" +struct _A { int i; }; +_A?[] !a = new _A?[2]; +return a; +"""); + code.parse().opto(); + assertEquals("return []*Test._A?;", code.print()); + assertEquals("*Test._A {i64 !i; }?[ null,null]", Eval2.eval(code, 0)); + } + + @Test + public void testBasic5() { + CodeGen code = new CodeGen( +""" +struct _S { int x; flt y; }; +// A new _S +_S !s = new _S; s.x=99; s.y = 3.14; + +// Double-d array of _Ss. Fill in one row. +_S?[]?[] !iss = new _S?[]?[2]; +iss[0] = new _S?[7]; +iss[0][2] = s; + +// Now pull out the filled-in value, with null checks +flt rez; +_S?[]? is = iss[arg]; +if( !is ) rez = 1.2; +else { + _S? i = is[2]; + if( !i ) rez = 2.3; + else rez = i.y; +} +return rez; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1.2,.y,2.3);", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + assertEquals("1.2", Eval2.eval(code, 1)); + } + + @Test + public void testBasic6() { + CodeGen code = new CodeGen( +""" +struct _S { int x; flt y; }; +// A new _S +_S !s = new _S; s.x=99; s.y = 3.14; + +// Double-d array of _Ss. Fill in one row. +_S?[]?[] !iss = new _S?[]?[2]; +iss[0] = new _S?[7]; +iss[0][2] = s; + +// Now pull out the filled-in value, with null checks +flt rez = 1.2; +if( iss[arg] ) + if( iss[arg][2] ) + rez = iss[arg][2].y; +return rez;"""); + code.parse().opto().typeCheck(); + assertEquals("return Phi(Region,1.2,1.2,.y);", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks + catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); } + } + + @Test + public void testTree() { + CodeGen code = new CodeGen( +""" +// Can we define a forward-reference array? +struct _Tree { _Tree?[]? _kids; }; +_Tree !root = new _Tree; +root._kids = new _Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES???? +root._kids[0] = new _Tree; +return root; +"""); + code.parse().opto().typeCheck(); + assertEquals("return Test._Tree;", code.print()); + assertEquals("Test._Tree{_kids=*Test._Tree {*[]*Test._Tree?? !_kids; }?[ Test._Tree{_kids=null},null]}", Eval2.eval(code, 0)); + } + + @Test + public void testNestedStructAddMemProj() { + CodeGen code = new CodeGen( +""" +struct _S { int a; int[] b; }; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testRollingSum() { + CodeGen code = new CodeGen( +""" +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +int i=0; +while( i < ary# ) { + ary[i] = i; + i = i+1; +} +// Fill [0,1,3,6,10,...] +i=0; +while( i < ary# - 1 ) { + ary[i+1] = ary[i+1] + ary[i]; + i = i+1; +} +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 +"""); + code.parse().opto().typeCheck(); + assertEquals("return (.[]+(.[]*1000));", code.print()); + assertEquals("1006", Eval2.eval(code, 4)); + } + + @Test @Ignore + public void sieveOEratosthenes() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp")); + CodeGen code = new CodeGen(src+"return sieve(arg);"); + code.parse().opto(); + assertEquals("return []u32;", code.print()); + assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + } + + @Test + public void testNewNodeInit() { + CodeGen code = new CodeGen( +""" +struct _S {int i; flt f;}; +_S !s1 = new _S; +_S !s2 = new _S; +s2.i = 3; +s2.f = 2.0; +if (arg) s1 = new _S; +return s1.i + s1.f; +"""); + code.parse().opto(); + assertEquals("return ((flt).i+.f);", code.print()); + assertEquals("0.0", Eval2.eval(code, 0)); + } + + + @Test + public void testBad0() { + CodeGen code = new CodeGen( +""" +return new flt; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); } + } + + @Test + public void testBad1() { + CodeGen code = new CodeGen( +""" +flt is = new int[2]; +return is; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Type *[]i64 is not of declared type flt",e.getMessage()); } + } + + @Test + public void testBad2() { + CodeGen code = new CodeGen( +""" +int[] is = new int[3.14]; +return is[1]; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot '<<' 3.14",e.getMessage()); } + } + + @Test + public void testBad3() { + CodeGen code = new CodeGen(""" +int[] is = new int[arg]; +return is[1]; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 4)); + try { Eval2.eval(code,-1); fail(); } + catch( NegativeArraySizeException e ) { assertEquals("-1",e.getMessage()); } + // This test will not pass (i.e., failed range-check) until we have range checks. + // Without the range check, the load `is[1]` optimizes against the `new int[]` + // and assumes it loads a zero then folds away, and the Eval2 never sees an OOB load. + //try { Eval2.eval(code,0); fail(); } + //catch( ArrayIndexOutOfBoundsException e ) { assertEquals("Array index 1 out of bounds for array length 0",e.getMessage()); } + } + + @Test + public void testProgress() { + CodeGen code = new CodeGen(""" +i8 v1=0&0; +u8 v2=0; +byte v4=0; +if(0) {} +while(v2<0) { + v4=0-v1; + break; +} +int v5=0&0; +while(v5+(0&0)) { + int v7=0&0; + while(v7) + v4=0>>>v5; + while(v1) + return 0; +} +return v1; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testSharpNot() { + CodeGen code = new CodeGen( +""" +if(0>>0) {} +while(0) {} +u32 v7=0; +int v8=0; +while(0<--1>>>---(v7*0==v8)) {} +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + + @Test + public void testProgress2() { + CodeGen code = new CodeGen( +""" +if(1) {} +else { + while(arg>>>0&0>>>0) {} + byte v3=0>>>0; + while(0) {} + int v7=0>>>0; + while(v7<0>>>0) { + while(0+v7<=0) if(1) arg=-12; + if(arg) { + v3=arg+v3+0; + arg=0; + } + } +} +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java new file mode 100644 index 00000000..80b0a2bf --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java @@ -0,0 +1,258 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter16Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + + @Test + public void testMulti0() { + CodeGen code = new CodeGen( +""" +int x, y; +return x+y; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + @Test + public void testMulti1() { + CodeGen code = new CodeGen( +""" +int x=2, y=x+1; +return x+y; +"""); + code.parse().opto(); + assertEquals("return 5;", code.print()); + assertEquals("5", Eval2.eval(code, 0)); + } + +// @Test +// public void testFinal0() { +// CodeGen code = new CodeGen( +//""" +//int !x=2; +//x=3; +//return x; +//"""); +// try { code.parse().opto(); fail(); } +// catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } +// } + + @Test + public void testFinal1() { + CodeGen code = new CodeGen( +""" +int x=2, y=3; +if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x +return y; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,9,3);", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + assertEquals("9", Eval2.eval(code, 1)); + } + + @Test + public void testConstruct0() { + CodeGen code = new CodeGen(""" +struct _X { int x=3; }; +_X z = new _X; +return z.x; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testConstruct1() { + CodeGen code = new CodeGen(""" +struct _X { int !x; new _X = { int xx -> x=xx; }; }; +_X z = new _X(3); +return z.x; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testConstruct2() { + CodeGen code = new CodeGen(""" +struct _X { int x=3; new _X = { int xx -> x=xx; }; }; +_X z = new _X(4); +return z.x; +"""); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + + @Test + public void testStructFinal0() { + CodeGen code = new CodeGen(""" +struct _Point { int !x, !y; new _Point = { int xx, int yy -> x=xx; y=yy; }; }; +_Point p = new _Point(3,4); +return p; +"""); + code.parse().opto(); + assertEquals("return (const)Test._Point;", code.print()); + assertEquals("Test._Point{x=3,y=4}", Eval2.eval(code, 0)); + } + + @Test + public void testStructFinal1() { + CodeGen code = new CodeGen(""" +struct _Point { int x=3, y=4; new _Point = { int xx, int yy -> x=xx; y=yy; }; }; +val p = new _Point(5,6); +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal2() { + CodeGen code = new CodeGen(""" +struct _Point { int x=3, y=4; }; +val p = new _Point; +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal3() { + CodeGen code = new CodeGen(""" +struct _Point { var x; var y; }; +_Point p = new _Point; +p.x++; +return p; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("'Test._Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); } + } + + @Test + public void testStructFinal4() { + CodeGen code = new CodeGen(""" +struct _Point { val x=2; val y=4; }; +_Point p = new _Point; +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal5() { + CodeGen code = new CodeGen(""" +struct _Point { var x=3; var y=4; }; +_Point !p = new _Point; +p.x++; +return p; +"""); + code.parse().opto(); + assertEquals("return Test._Point;", code.print()); + assertEquals("Test._Point{x=4,y=4}", Eval2.eval(code, 0)); + } + + // Same as the Chapter13 test with the same name, but using the new + // constructor syntax + @Test + public void testLinkedList1() { + CodeGen code = new CodeGen( +""" +struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int ii -> next=n; i=ii; }; }; +_LLI? !head = null; +while( arg ) { + head = new _LLI(head,arg); + arg = arg-1; +} +if( !head ) return 0; +_LLI? next = head.next; +if( !next ) return 1; +return next.i; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1,.i,0);", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + assertEquals("2", Eval2.eval(code, 3)); + } + + @Test + public void testLinkedList2() { + CodeGen code = new CodeGen( +""" +struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int a -> + next=n; + int !tmp=a; + while( a > 10 ) { + tmp = tmp + a; + a = a - 1; + } + i=tmp; +}; }; +_LLI? !head = null; +while( arg ) { + head = new _LLI(head,arg); + arg = arg-1; +} +if( !head ) return 0; +_LLI? next = head.next; +if( !next ) return 1; +return next.i; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1,.i,0);", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + assertEquals("2", Eval2.eval(code, 11)); + } + + @Test + public void testSquare() { + CodeGen code = new CodeGen( +""" +struct _Square { + flt !side = arg; + // Newtons approximation to the square root, computed in a constructor. + // The actual allocation will copy in this result as the initial + // value for 'diag'. + flt !diag = arg*arg/2; + while( 1 ) { + flt next = (side/diag + diag)/2; + if( next == diag ) break; + diag = next; + } +}; +return new _Square; +"""); + code.parse().opto(); + assertEquals("return Test._Square;", code.print()); + assertEquals("Test._Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3)); + assertEquals("Test._Square{side=4.0,diag=2.0}", Eval2.eval(code, 4)); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java new file mode 100644 index 00000000..24daa164 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17Test.java @@ -0,0 +1,558 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter17Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 1; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + + // --------------------------------------------------------------- + @Test + public void testInc0() { + CodeGen code = new CodeGen(""" +return arg++; +"""); + code.parse().opto(); + assertEquals("return arg;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testInc1() { + CodeGen code = new CodeGen(""" +return arg+++arg++; +"""); + code.parse().opto(); + assertEquals("return ((arg<<1)+1);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testInc2() { + CodeGen code = new CodeGen(""" +// -(arg--)-(arg--) +return -arg---arg--; +"""); + code.parse().opto(); + assertEquals("return (-((arg<<1)+-1));", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testInc3() { + CodeGen code = new CodeGen(""" +int[] !xs = new int[arg]; +xs[0]++; +xs[1]++; +return xs[0]; +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 2)); + } + + @Test + public void testInc4() { + CodeGen code = new CodeGen(""" +u8[] !xs = new u8[1]; +xs[0]--; +return xs[0]; +"""); + code.parse().opto(); + assertEquals("return 255;", code.print()); + assertEquals("255", Eval2.eval(code, 2)); + } + + @Test + public void testInc5() { + CodeGen code = new CodeGen(""" +struct _S { u16 x; }; +_S !s = new _S; +s.x--; +return s.x; +"""); + code.parse().opto(); + assertEquals("return 65535;", code.print()); + assertEquals("65535", Eval2.eval(code, 2)); + } + + @Test + public void testInc6() { + CodeGen code = new CodeGen("return --arg;"); + code.parse().opto(); + assertEquals("return (arg+-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + } + + @Test + public void testInc7() { + CodeGen code = new CodeGen("u8 x=0; return --x;"); + code.parse().opto(); + assertEquals("return 255;", code.print()); + assertEquals("255", Eval2.eval(code, 0)); + } + + @Test + public void testInc8() { + CodeGen code = new CodeGen("int x; x+=2; return x+=3;"); + code.parse().opto(); + assertEquals("return 5;", code.print()); + assertEquals("5", Eval2.eval(code, 0)); + } + + @Test + public void testInc9() { + CodeGen code = new CodeGen("flt x = 1.5; x++; return x;"); + code.parse().opto(); + assertEquals("return 2.5f;", code.print()); + assertEquals("2.5", Eval2.eval(code, 0)); + } + + @Test public void testInt10() { + CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;"); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Type 2.5f is not of declared type i64",e.getMessage()); } + } + + + // --------------------------------------------------------------- + @Test public void testVar0() { + CodeGen code = new CodeGen("var d; return d;"); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); } + } + + @Test public void testVar1() { + CodeGen code = new CodeGen("val d; return d;"); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); } + } + + @Test public void testVar2() { + CodeGen code = new CodeGen("int x; x=3; x++; return x; // Ok, no initializer so x is mutable "); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar3() { + CodeGen code = new CodeGen("int x=3; x++; return x; // Ok, primitive so x is mutable despite initializer "); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar4() { + CodeGen code = new CodeGen("struct _S{int x;}; _S? s; s=new _S; s.x++; return s.x; // Ok, no initializer so x is mutable "); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar5() { + CodeGen code = new CodeGen("struct _S{int x; new _S={ int xx -> x=xx; };}; _S? s; s=new _S(3); s.x++; return s.x; // Ok, no initializer so x is mutable "); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar6() { + CodeGen code = new CodeGen("struct S{int x;}; val s = new S; s.x++; return s.x; // Error initializer so x is immutable "); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar7() { + CodeGen code = new CodeGen("struct S{int x; new S={ int xx -> x=xx; };}; val s = new S(3); s.x++; return s.x; // Error initializer so x is immutable "); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar8() { + CodeGen code = new CodeGen("struct _S{int x;}; _S !s=new _S; s.x++; return s.x; // Ok, has '!' so s.x is mutable "); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar9() { + CodeGen code = new CodeGen("struct _S{int x;}; var s=new _S; s.x++; return s.x; // Ok, has var so s.x is mutable "); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar10() { + CodeGen code = new CodeGen("struct S{int x;}; val s=new S; s.x++; return s.x; // Error, has val so x is immutable "); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar11() { + CodeGen code = new CodeGen(""" +struct Bar { int x; }; +Bar !bar = new Bar; +bar.x = 3; // Ok, bar is mutable + +struct Foo { Bar? !bar; int y; new Foo = { Bar? b -> bar = b; }; }; +Foo !foo = new Foo(bar); +foo.bar = bar; // Ok foo is mutable +foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable + +val xfoo = foo; // Throw away mutability +xfoo.bar.x++; // Error, cannot mutate through xfoo +return xfoo.bar.x; + """); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar12() { + CodeGen code = new CodeGen(""" +struct _Bar { int x; }; +_Bar !bar = new _Bar; +bar.x = 3; // Ok, bar is mutable + +struct _Foo { _Bar? !bar; int y; }; +_Foo !foo = new _Foo; +foo.bar = bar; // Ok bar is mutable +foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable + +val xfoo = foo; // Throw away mutability +int x4 = xfoo.bar.x; // Ok to read through xfoo, gets 4 +foo.bar.x++; // Bumps to 5 +int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5 +return x4*10+x5; +"""); + code.parse().opto(); + assertEquals("return 45;", code.print()); + assertEquals("45", Eval2.eval(code, 0)); + } + + @Test + public void testVar13() { + CodeGen code = new CodeGen(""" +int i,i++; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); } + } + + + @Test + public void testVar14() { + CodeGen code = new CodeGen(""" +struct B {}; +struct A { B b; new A = { -> + return b; // read before init + b = new B; +}; }; +A x = new A; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Field 'b' might not be initialized",e.getMessage()); } + } + + @Test + public void testVar15() { + CodeGen code = new CodeGen(""" +struct B {}; +struct A { B b; new A = { -> + if (arg) b = new B; // Constructor ends with partial init of b +}; }; +return (new A).b; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("'Test.A' is not fully initialized, field 'b' is only partially set in the constructor",e.getMessage()); } + } + + @Test + public void testVar16() { + CodeGen code = new CodeGen( +""" +struct S{ new S = { -> x = 2; }; }; +val x = 1; +val s = new S; +return x; +""" +); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Undefined name 'x'",e.getMessage()); } + } + + @Test + public void testVar17() { + CodeGen code = new CodeGen( +""" +struct Person { + bool focused; +}; +val focus = { Person p -> + p.focused = true; +}; +Person me; // Person !me = new Person; +focus(me); +return me; +""" +); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); } + } + + // --------------------------------------------------------------- + @Test + public void testTrinary0() { + CodeGen code = new CodeGen(""" +return arg ? 1 : 2; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1,2);", code.print()); + assertEquals("2", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary1() { + CodeGen code = new CodeGen(""" +return arg ? 0 : arg; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testTrinary2() { + CodeGen code = new CodeGen(""" +struct _Bar { int x; }; +var b = arg ? new _Bar : null; +return b ? b.x++ + b.x++ : -1; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary3() { + CodeGen code = new CodeGen(""" +struct _Bar { int x; }; +var b = arg ? new _Bar; +return b ? b.x++ + b.x++ : -1; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary4() { + // This test case will benefit from an unzipping transformation + CodeGen code = new CodeGen(""" +struct _Bar { _Bar? next; int x; new _Bar = { _Bar? n, int xx -> next=n; x=xx; }; }; +var b = arg ? new _Bar((arg==2) ? new _Bar(null,2), 1); +return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0" +"""); + code.parse().opto(); + assertEquals("return Phi(Region,.x,0,.x);", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + assertEquals("2", Eval2.eval(code, 2)); + } + + @Test + public void testTrinary5() { + CodeGen code = new CodeGen(""" +flt f=arg?1:1.2; +return f; // missing widening +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1.0f,1.2);", code.print()); + assertEquals("1.2", Eval2.eval(code, 0)); + } + + + @Test + public void testTrinary6() { + CodeGen code = new CodeGen(""" +struct S{}; +return arg ? 7 : new S; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("No common type amongst 7 and *Test.S {}",e.getMessage()); } + } + + // --------------------------------------------------------------- + @Test + public void testFor0() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor1() { + CodeGen code = new CodeGen(""" +int sum=0, i=0; +for( ; i<arg; i++ ) + sum += i; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor2() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; ; i++ ) { + if( i>=arg ) break; + sum += i; +} +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor3() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; ) + sum += i++; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor4() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return i; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); } + } + @Test + public void testFor5() { + CodeGen code = new CodeGen(""" +for(;;arg++;) {} +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `Unexpected code after expression` but found `;`",e.getMessage()); } + } + + + + // --------------------------------------------------------------- + @Test + public void testForward0() { + CodeGen code = new CodeGen(""" +struct _A { + B? f1; + B? f2; +}; +return new _A; +"""); + code.parse().opto(); + assertEquals("return Test._A;", code.print()); + assertEquals("Test._A{f1=null,f2=null}", Eval2.eval(code,0)); + } + + @Test + public void testForward1() { + CodeGen code = new CodeGen(""" +struct _A { + B?[]? nil_array_of_b; + B?[] array_of_b; + new _A = { B?[] bs -> array_of_b = bs; }; +}; +return new _A(new B?[0]).array_of_b; +"""); + code.parse().opto(); + assertEquals("return []*B?;", code.print()); + assertEquals("*B {}?[]", Eval2.eval(code,0)); + } + + // --------------------------------------------------------------- + @Test + public void testLinkedList2() { + CodeGen code = new CodeGen(""" +struct _LLI { _LLI? next; int i; new _LLI = { _LLI? n, int ii -> next=n; i=ii; }; }; +_LLI? !head = null; +while( arg-- ) + head = new _LLI(head,arg); +int sum=0; +var ptr = head; // A read-only ptr, to be assigned from read-only next fields +for( ; ptr; ptr = ptr.next ) + sum += ptr.i; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print()); + assertEquals("45", Eval2.eval(code, 10)); + } + + @Test + public void sieveOfEratosthenes() { + CodeGen code = new CodeGen( +""" +var ary = new bool[arg], primes = new int[arg]; +var nprimes=0, p=0; +// Find primes while p^2 < arg +for( p=2; p*p < arg; p++ ) { + // skip marked non-primes + while( ary[p] ) p++; + // p is now a prime + primes[nprimes++] = p; + // Mark out the rest non-primes + for( int i = p + p; i < ary#; i += p ) + ary[i] = true; +} +// Now just collect the remaining primes, no more marking +for( ; p < arg; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; +// Copy/shrink the result array +var !rez = new int[nprimes]; +for( int j=0; j<nprimes; j++ ) + rez[j] = primes[j]; +return rez; +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return []i64;", code.print()); + assertEquals("i64[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java new file mode 100644 index 00000000..de332d03 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -0,0 +1,412 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen.Phase; +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.junit.Assert.fail; + +public class Chapter18Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 0; +"""); + code.driver(Phase.LocalSched); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 2)); + } + + @Test + public void testPhiParalleAssign() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(arg--) { + int t = a; + a = b; + b = t; +} +return a; +"""); + code.parse().opto().typeCheck(); + assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + assertEquals("2", Eval2.eval(code, 1)); + assertEquals("1", Eval2.eval(code, 2)); + assertEquals("2", Eval2.eval(code, 3)); + } + + + // --------------------------------------------------------------- + @Test + public void testType0() { + CodeGen code = new CodeGen( +""" +{int -> int}? x2 = null; // null function ptr +return x2; +"""); + code.parse().opto(); + assertEquals("return null;", code.print()); + assertEquals("null", Eval2.eval(code, 0 ) ); + } + + @Test + public void testFcn0() { + CodeGen code = new CodeGen( +""" +{int -> int}? sq = { int x -> + x*x; +}; +"""); + code.parse().opto(); + assertEquals("Stop[ return { sq}; return (Parm_x(sq,i64)*x); ]", code.print()); + assertEquals("{ ptr i64 -> i64 #[ 3]}", Eval2.eval(code, 3)); + } + + @Test + public void testFcn1() { + CodeGen code = new CodeGen( +""" +var _sq = { int x -> + x*x; +}; +return _sq(arg)+_sq(3); +"""); + code.driver(Phase.LocalSched); + assertEquals("return ((arg*arg)+9);", code.print()); + assertEquals("13", Eval2.eval(code, 2)); + } + + // Function scope test + @Test + public void testFcn2A() { + CodeGen code = new CodeGen( +""" +int cnt=1; // Global scope; exactly one of these +return { -> cnt++; }; // Escape global-scope variable is OK +"""); + code.parse().opto(); + assertEquals("Stop[ return { ptr -> i64 #[ 3]}; return .cnt; ]", code.print()); + assertEquals("{ ptr -> i64 #[ 3]}", Eval2.eval(code, 0)); + } + + // Function scope test + @Test + public void testFcn2B() { + CodeGen code = new CodeGen( +""" +val ctr = { -> + int cnt; // Function-local var + return { -> cnt=cnt+1; cnt; }; // Escape cnt out of scope +}; +val A = ctr(); // Make a counter +val B = ctr(); // Make a counter +A(); // Increment counter A +return A()*10 + B(); // Incr A and B; result: 2*10+1 == 21 +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); } + } + + // Function scope test + @Test + public void testFcn3() { + CodeGen code = new CodeGen( +""" +val cnt=2; +return { -> cnt; }(); +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertEquals("2", Eval2.eval(code, 0)); + } + + // Function variables + @Test + public void testFcn4() { + CodeGen code = new CodeGen( +""" +var fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; +return fcn(3); +"""); + code.parse().opto(); + assertEquals("Stop[ return #2; return (Parm_x($fun3,i64,3)*x); return (Parm_x($fun4,i64,3)<<1); ]", code.print()); + assertEquals("6", Eval2.eval(code, 0)); + assertEquals("9", Eval2.eval(code, 1)); + } + + // Recursive factorial test + @Test + public void testFcn5() { + CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1 : x*_fact(x-1); }; return _fact(arg);"); + code.parse().opto().typeCheck(); + assertEquals("Stop[ return Phi(Region,1,(arg*#2)); return Phi(Region,1,(Parm_x(_fact,(arg-1),(x-1))*#2)); ]", code.print()); + assertEquals( "1", Eval2.eval(code, 0)); + assertEquals( "1", Eval2.eval(code, 1)); + assertEquals( "2", Eval2.eval(code, 2)); + assertEquals( "6", Eval2.eval(code, 3)); + assertEquals("24", Eval2.eval(code, 4)); + } + + // The multiply initially sees one integer input and one unresolved + // The multiply initially sees one integer input and one unresolvedfcn8 + // recursive result, but the floating base case requires a floating mode. + @Test + public void testFcn5Float() { + CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1.0 : x*_fact(x-1); }; return _fact(arg);"); + code.parse().opto().typeCheck(); + assertEquals("1.0", Eval2.eval(code, 0)); + assertEquals("1.0", Eval2.eval(code, 1)); + assertEquals("2.0", Eval2.eval(code, 2)); + assertEquals("6.0", Eval2.eval(code, 3)); + assertEquals("24.0", Eval2.eval(code, 4)); + } + + @Test + public void testFcn5Minus() { + CodeGen code = new CodeGen("val _neg = { int x -> x ? -_neg(x-1) : 1; }; return _neg(arg);"); + code.parse().opto().typeCheck(); + assertEquals( "1", Eval2.eval(code, 0)); + assertEquals("-1", Eval2.eval(code, 1)); + assertEquals( "1", Eval2.eval(code, 2)); + assertEquals("-1", Eval2.eval(code, 3)); + } + + @Test + public void testFcn6() { + CodeGen code = new CodeGen( +""" +struct _S { int i; new _S = { int x -> i=x; }; }; +val _newS = { int x -> return new _S(x); }; +return _newS(1).i; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + // Only forward refs to direct function calls allowed + @Test + public void testFcn7() { + CodeGen code = new CodeGen( +""" +val _f = {->1;}; +val _g = {->2;}; +if( arg ? _f : _g ) return 1; +return 2; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testFcn8() { + CodeGen code = new CodeGen( +""" +{int -> int}? _i2i = null; +var _id = {{int->int} f-> return f;}; +for(;;) { + if (_i2i) return _i2i(arg); + var _x = {int i-> return i;}; + arg = _x(3); + _i2i = _id(_x); +} +"""); + code.driver(Phase.LocalSched); + assertEquals("return Phi(Loop,arg,3);", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testFcn9() { + CodeGen code = new CodeGen( +""" +{int -> int}? i2i = null; +for(;;) { + if (i2i) return i2i(arg); + var x = {int i-> return i;}; + arg = x(3); +} +"""); + code.driver(Phase.LocalSched); + assertEquals("Stop[ return Top; return Parm_i(x,i64); ]", code.print()); + assertNull( Eval2.eval( code, 0 ) ); // Infinite loop, <clinit> never exits + } + + + @Test + public void testFcn10() { + CodeGen code = new CodeGen( +""" +struct _Person { + int age; +}; + +val _fcn = { _Person?[] ps, int x -> + val tmp = ps[x]; + if( ps[x] ) + ps[x].age++; +}; + +var ps = new _Person?[2]; +ps[0] = new _Person; +ps[1] = new _Person; +return _fcn(ps,1); +"""); + code.driver(Phase.LocalSched); + assertEquals("return .age;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + // Function break + @Test + public void testErr1() { + CodeGen code = new CodeGen( +""" +for(;;) { + val f = { -> + break; + }; + f(); + return 2; +} +return 1; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); } + } + + // Calling and inlining a null function + @Test + public void testErr2() { + CodeGen code = new CodeGen( +""" +{int -> int}? !i2i = { int i -> return i; }; +for(;;) { + if (i2i(2) == arg) break; + i2i = null; +} +return 0; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null calling { ptr i64 -> i64 #[ 3]}?",e.getMessage()); } + } + + + @Test + public void testErr3() { + CodeGen code = new CodeGen( +""" +val f = { int i, int j -> return i+j; }; +return f(); +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Expecting 3 arguments, but found 1",e.getMessage()); } + } + + + @Test + public void testErr4() { + CodeGen code = new CodeGen( +""" +struct S { + {int} f = { -> x(); return 0; }; // Do not let fref x be a field +}; +val x = { -> return 1; }; +S? s = new S; +return s.x; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*Test.S'",e.getMessage()); } + } + + @Test @Ignore + public void testErr5() { + CodeGen code = new CodeGen( +""" +val g = { -> + g(); +}; +return g(); +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); } + } + + + // Mutual recursion. Fails without SCCP to lift the recursive return types. + @Ignore @Test + public void testFcnMutRec() { + CodeGen code = new CodeGen( +""" +val is_even = { int x -> x ? is_odd (x-1) : true ; }; +val is_odd = { int x -> x ? is_even(x-1) : false; }; +return is_even(arg); +"""); + code.parse().opto().typeCheck(); + assertEquals("Stop[ return #2; return Phi(Region,0,1,#2); ]", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + assertEquals("0", Eval2.eval(code, 1)); + assertEquals("1", Eval2.eval(code, 2)); + assertEquals("0", Eval2.eval(code, 3)); + } + + // Forward ref to 'x' means that + @Ignore @Test + public void testForwardRef1() { + CodeGen code = new CodeGen( +""" +struct S { + { int } f = { -> return x(); }; +}; +val x = { -> return 1; }; +S? s = null; +for(;;) { + if (s) return s.f; +} +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + // Inline hidden called more than once + @Test + public void testInline() { + CodeGen code = new CodeGen( +""" +{int->int}?! _i2i_noInline = {int i->return i;}; +{{int->int}->{int->int}}! _f2f = {{int->int} f->return f;}; +val _o = _i2i_noInline; +if (arg) _i2i_noInline = null; +if (_i2i_noInline) return _i2i_noInline(arg); +return _f2f(_o)(1); +"""); + code.driver(Phase.LocalSched); + assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(_i2i_noInline,i64); ]", code.print()); + assertEquals("1", Eval2.eval(code, 2)); + } + + @Test + public void testOperField() { + CodeGen code = new CodeGen( +""" +struct _Person { + int coffee_count; +}; +_Person !p = new _Person; +p.coffee_count += 1; +return p.coffee_count; +"""); + code.driver(Phase.LocalSched); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 2)); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java new file mode 100644 index 00000000..eaf76041 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -0,0 +1,435 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen.Phase; +import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter19Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen(""" +return 0; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 2)); + } + + + @Test + public void testString() { + String src = +""" +struct String { + u8[~] cs; + int _hashCode; + new String = { u8[~] s -> cs=s; }; +}; + +// Compare two Strings +val equals = { String self, String s -> + if( self == s ) return true; + if( self.cs# != s.cs# ) return false; + for( int i=0; i< self.cs#; i++ ) + if( self.cs[i] != s.cs[i] ) + return false; + return true; +}; + + +val _hashCodeString = { String self -> + int hash=0; + for( int i=0; i< self.cs#; i++ ) + hash = hash*31 + self.cs[i]; + if( !hash ) hash = 123456789; + return hash; +}; + +// Return the String hashCode (cached, and never 0) +val hashCode = { String self -> + self._hashCode + ? self._hashCode + : (self._hashCode = _hashCodeString(self)); +}; + +hashCode(new String("Hello, World!")); +"""; + CodeGen code = new CodeGen(src).driver(Phase.LocalSched); + assertEquals("Stop[ return Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return MEM[ 2:___ 3:___ 4:.cs=Parm_s(Test.String.String,*[]u8); 5:._hashCode=0;]; return MEM[ 2:___ 3:___ 4:___ 5:._hashCode=0;]; return Phi(Region,0,1,0,1); return Phi(Region,(!=0)._hashCode,Phi(Region,123456789,(!=0)Phi(Loop,0,(((Phi_hash<<5)-Phi_hash)+.[])))); ]", code.print()); + assertEquals("4029215624828139541", Eval2.eval(code, 2)); + } + + @Test + public void testTernaryStoreCall() { + String src = +""" +struct String { + u8[~] cs; + int _hashCode; + new String = { u8[~] s -> cs=s; }; +}; + +val f = { String self -> 7; }; + +val hashCode = { String self -> + self._hashCode + ? 3 + : (self._hashCode = f(self)); +}; + +hashCode(new String("Hello")); +"""; + CodeGen code = new CodeGen(src).driver(Phase.LocalSched); + assertFalse(code.print().contains("return Top")); + assertEquals("7", Eval2.eval(code, 2)); + } + + @Test + public void testBasic0() { + CodeGen code = new CodeGen("return 0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBasic1() { + CodeGen code = new CodeGen("return arg+1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (inc,arg);", code.print()); + } + + @Test + public void testBasic2() { + CodeGen code = new CodeGen("return -17;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return -17;", code.print()); + } + + + @Test + public void testBasic3() { + CodeGen code = new CodeGen("return arg==1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (set==,(cmp,arg));", code.print()); + } + + @Test + public void testBasic4() { + CodeGen code = new CodeGen("return arg<<1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (shli,arg);", code.print()); + } + + @Test + public void testBasic5() { + CodeGen code = new CodeGen("return arg >> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (sari,arg);", code.print()); + } + + @Test + public void testBasic6() { + CodeGen code = new CodeGen("return arg >>> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (shri,arg);", code.print()); + } + + @Test + public void testBasic7() { + CodeGen code = new CodeGen("return arg / 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (div,arg,2);", code.print()); + } + + @Test + public void testBasic8() { + CodeGen code = new CodeGen("return arg * 6;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (muli,arg);", code.print()); + } + + @Test + public void testBasic9() { + CodeGen code = new CodeGen("return arg & 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (andi,arg);", code.print()); + } + + @Test + public void testBasic10() { + CodeGen code = new CodeGen("return arg | 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (ori,arg);", code.print()); + } + + @Test + public void testBasic11() { + CodeGen code = new CodeGen("return arg ^ 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (xori,arg);", code.print()); + } + + @Test + public void testBasic12() { + CodeGen code = new CodeGen("return arg + 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (addf,(cvtf,arg),2.0f);", code.print()); + } + + @Test + public void testBasic13() { + CodeGen code = new CodeGen("return arg - 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (subf,(cvtf,arg),2.0f);", code.print()); + } + + @Test + public void testBasic14() { + CodeGen code = new CodeGen("return arg * 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mulf,(cvtf,arg),2.0f);", code.print()); + } + + @Test + public void testBasic15() { + CodeGen code = new CodeGen("return arg / 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mulf,(cvtf,arg),0.5f);", code.print()); + } + + @Test + public void testBasic16() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 / arg;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (div,(inc,arg),arg);", code.print()); + } + + @Test + public void testBasic17() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 * arg; +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mul,(inc,arg),arg);", code.print()); + } + + @Test + public void testToFloat() { + CodeGen code = new CodeGen(""" +int a = arg; +return a + 2.0; +""" + ).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (addf,(cvtf,arg),2.0f);", code.print()); + } + + @Test + public void testIfStmt() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg == 1) + a = arg+2; +else { + a = arg-3; +} +return a;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print()); + } + + @Test + public void testIfMerge2() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=arg+2; +if( arg==1 ) + b=b+a; +else + a=b+1; +return a+b;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print()); + } + + @Test + public void testLoop() { + CodeGen code = new CodeGen( +""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return sum;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print()); + } + + @Test + public void testAlloc1() { + CodeGen code = new CodeGen( +""" +struct _S { int a; _S? c; }; +return new _S;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Test._S;", code.print()); + } + + @Test + public void testLea1() { + CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (lea,arg,(div,arg,3));", code.print()); + } + + @Test + public void testLea2() { + CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (lea,arg,(div,arg,3));", code.print()); + } + + @Test + public void testLea3() { + CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (lea,arg,(div,arg,3));", code.print()); + } + + @Test + public void testAlloc2() { + CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testAlloc3() { + CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testArray1() { + CodeGen code = new CodeGen( +""" +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<ary#; i++ ) + ary[i] = i; +// Fill [0,1,3,6,10,...] +for( int i=0; i<ary#-1; i++ ) + ary[i+1] += ary[i]; +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testArray2() { + CodeGen code = new CodeGen( +""" +flt[] !A = new flt[arg], !B = new flt[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<A#; i++ ) + A[i] = i; +for( int i=0; i<A#; i++ ) + B[i] += A[i]; +return 0; +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return 0;", code.print()); + } + + @Test + public void testArray3() { + CodeGen code = new CodeGen( +""" +byte[] !A = new byte[arg]; +for( int i=0; i<A#; i++ ) + A[i]++; +return A[1]; +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testNewton() throws IOException { + String src = +""" +val _test_sqrt = { flt x -> + flt epsilon = 1e-15; + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( guess-epsilon <= next & next <= guess+epsilon ) return guess; + //if( guess==next ) return guess; + guess = next; + } +}; +flt farg = arg; +return _test_sqrt(farg); +"""; + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,Phi_guess,(divf,cvtf,Phi_guess)),0.5f));", code.print()); + }; + + + @Test + public void sieveOfEratosthenes() { + String src = +""" +val sieve = { int N -> + // The main Sieve array + bool[] !ary = new bool[N]; + // The primes less than N + u32[] !primes = new u32[N>>1]; + // Number of primes so far, searching at index p + int nprimes = 0, p=2; + // Find primes while p^2 < N + while( p*p < N ) { + // skip marked non-primes + while( ary[p] ) p++; + // p is now a prime + primes[nprimes++] = p; + // Mark out the rest non-primes + for( int i = p + p; i < ary#; i+= p ) + ary[i] = true; + p++; + } + + // Now just collect the remaining primes, no more marking + for( ; p < N; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; + + // Copy/shrink the result array + u32[] !rez = new u32[nprimes]; + for( int j=0; j < nprimes; j++ ) + rez[j] = primes[j]; + return rez; +}; +"""; + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("Stop[ return { sieve}; return []u32; ]", code.print()); + //assertEquals("{ ptr i64 -> *[]u32 #[ 3]}",Eval2.eval(code, 20)); + } + + + @Test + public void testFcn1() { + CodeGen code = new CodeGen( +""" +val _fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; +return _fcn(2)*10 + _fcn(3); +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun3,[2-3]),x); return (shli,Parm_x($fun4,[2-3])); ]", code.print()); + } + + @Test + public void testFcn2() { + CodeGen code = new CodeGen( +""" +val _sq = { int x -> x*x; }; +return _sq(arg) + _sq(3); +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (addi,(mul,arg,arg));", code.print()); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java new file mode 100644 index 00000000..e4754213 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -0,0 +1,198 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter20Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen("return 0;"); + code.parse().opto().typeCheck(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 2)); + } + + static void testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src); + code.driver(CodeGen.Phase.RegAlloc,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code.print()); + } + + private static void testAllCPUs( String src, int spills, String stop ) { + testCPU(src,"x86_64_v2", "SystemV",spills,stop); + testCPU(src,"riscv" , "SystemV",spills,stop); + testCPU(src,"arm" , "SystemV",spills,stop); + } + + @Test public void testAlloc0() { + testCPU("return new u8[arg];","x86_64_v2", "SystemV",3,"return []u8;"); + testCPU("return new u8[arg];","riscv" , "SystemV",5,"return []u8;"); + testCPU("return new u8[arg];","arm" , "SystemV",5,"return []u8;"); + } + + @Test public void testBasic1() { + String src = "return arg | 2;"; + testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));"); + testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );"); + testCPU(src,"arm" , "SystemV",0,"return (ori,arg);"); + } + + @Test + public void testNewtonInteger() { + String src = +""" +// Newtons approximation to the square root +val _sqrt = { int x -> + int guess = x; + while( 1 ) { + int next = (x/guess + guess)>>1; + if( next == guess ) return guess; + guess = next; + } +}; +return _sqrt(arg) + _sqrt(arg+2); +"""; + testCPU(src,"x86_64_v2", "win64" ,38,null); + testCPU(src,"riscv" , "SystemV",19,null); + testCPU(src,"arm" , "SystemV",19,null); + } + + @Test + public void testNewtonFloat() { + String src = +""" +val _test_sqrt_noInline = { flt x -> + flt epsilon = 1e-15; + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( guess-epsilon <= next && next <= guess+epsilon ) + return guess; + guess = next; + } +}; +flt farg = arg; return _test_sqrt_noInline(farg) + _test_sqrt_noInline(farg+2.0); +"""; + testCPU(src,"x86_64_v2", "SystemV",65,null); + testCPU(src,"riscv" , "SystemV",35,null); + testCPU(src,"arm" , "SystemV",35,null); + } + + @Test + public void testAlloc2() { + String src = "int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"; + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 6,"return .[];"); + testCPU(src,"arm" , "SystemV", 6,"return .[];"); + } + + + + @Test + public void testArray1() { + String src = +""" +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<ary#; i++ ) + ary[i] = i; +// Fill [0,1,3,6,10,...] +for( int i=0; i<ary#-1; i++ ) + ary[i+1] += ary[i]; +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 +"""; + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 7,"return (add,.[],(mul,.[],1000));"); + testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));"); + } + + + @Test + public void testString() { + String src = +""" +struct String { + u8[] cs; + int _hashCode; +}; + +// Compare two Strings +val equals = { String self, String s -> + if( self == s ) return true; + if( self.cs# != s.cs# ) return false; + for( int i=0; i< self.cs#; i++ ) + if( self.cs[i] != s.cs[i] ) + return false; + return true; +}; + +// Return the String hashCode (cached, and never 0) +val hashCode = { String self -> + self._hashCode + ? self._hashCode + : (self._hashCode = _hashCodeString(self)); +}; + +val _hashCodeString = { String self -> + int hash=0; + for( int i=0; i< self.cs#; i++ ) + hash = hash*31 + self.cs[i]; + if( !hash ) hash = 123456789; + return hash; +}; +"""; + testCPU(src,"x86_64_v2", "SystemV",18,null); + testCPU(src,"riscv" , "SystemV", 5,null); + testCPU(src,"arm" , "SystemV", 4,null); + } + + @Test + public void testCast() { + String src = "struct Bar { int x; }; var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1;"; + testCPU(src,"x86_64_v2", "SystemV",3,null); + testCPU(src,"riscv" , "SystemV",3,null); + testCPU(src,"arm" , "SystemV",3,null); + } + + @Test + public void testFltArg() { + String src = "return {int i, flt f, int j->return i+f+j;};"; + testAllCPUs(src,0,null); + } + + @Test + public void testFlags1() { + String src = """ +bool _b1 = arg == 1; +bool b2 = arg == 2; +if (b2) if (_b1) return 1; +if (_b1) return 2; +return 0; +"""; + testCPU(src,"x86_64_v2", "SystemV",0,"return Phi(Region,2,0,1);"); + testCPU(src,"riscv" , "SystemV",0,"return Phi(Region,2,0,1);"); + testCPU(src,"arm" , "SystemV",0,"return Phi(Region,2,0,1);"); + } + + @Test + public void testFlags2() { + String src = """ +bool b1 = arg == 1; +while (arg > 0) { + arg--; + if (b1) arg--; +} +return arg; +"""; + testCPU(src,"x86_64_v2", "SystemV",3,null); + testCPU(src,"riscv" , "SystemV",2,null); + testCPU(src,"arm" , "SystemV",2,null); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java new file mode 100644 index 00000000..a4621165 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -0,0 +1,464 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import com.seaofnodes.simple.util.SB; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter21Test { + + @Test + public void testJig() throws IOException { + String src = +""" +struct _s0 { + bool v1; + i16 v2; + int v3; + i8 v4; + byte v5; +}; +while(new _s0.v3) + while(new _s0.v5<<new _s0.v4) {} +if(0) { + if(0) { + flt !P5ZUD4=new _s0.v2; + } + while(0) {} +} +return new _s0.v1; +"""; + testCPU(src,"x86_64_v2", "win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + static void testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code.print()); + } + + + @Test public void testBasic1() { + String src = "return arg | 2;"; + testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));"); + testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );"); + testCPU(src,"arm" , "SystemV",0,"return (ori,arg);"); + } + + @Test public void testInfinite() { + String src = "struct _S { int i; }; _S !s = new _S; while(1) s.i++; return s.i;"; + testCPU(src,"x86_64_v2", "SystemV",0,"return Top;"); + testCPU(src,"riscv" , "SystemV",2,"return Top;"); + testCPU(src,"arm" , "SystemV",2,"return Top;"); + } + + @Test + public void testArray1() { + String src = +""" +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<ary#; i++ ) + ary[i] = i; +// Fill [0,1,3,6,10,...] +for( int i=0; i<ary#-1; i++ ) + ary[i+1] += ary[i]; +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 +"""; + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 7,"return (add,.[],(mul,.[],1000));"); + testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));"); + } + + @Test + public void testAntiDeps1() { + String src = +""" +struct _S { int f; }; +var v0 = new _S; +_S? v1; +if (arg) v1 = new _S; +if (v1) { + v0.f = v1.f; +} else { + v0.f = 2; +} +return v0; +"""; + testCPU(src,"x86_64_v2", "SystemV",10,"return mov(mov(Test._S));"); + testCPU(src,"riscv" , "SystemV",10,"return mov(mov(Test._S));"); + testCPU(src,"arm" , "SystemV",10,"return mov(mov(Test._S));"); + } + + @Test + public void testString() { + String src = +""" +struct String { + u8[] cs; + int _hashCode; +}; + +// Compare two Strings +val equals = { String self, String s -> + if( self == s ) return true; + if( self.cs# != s.cs# ) return false; + for( int i=0; i< self.cs#; i++ ) + if( self.cs[i] != s.cs[i] ) + return false; + return true; +}; + +// Return the String hashCode (cached, and never 0) +val hashCode = { String self -> + self._hashCode + ? self._hashCode + : (self._hashCode = _hashCodeString(self)); +}; + +val _hashCodeString = { String self -> + int hash=0; + for( int i=0; i< self.cs#; i++ ) + hash = hash*31 + self.cs[i]; + if( !hash ) hash = 123456789; + return hash; +}; +"""; + testCPU(src,"x86_64_v2", "SystemV",18,null); + testCPU(src,"riscv" , "SystemV", 5,null); + testCPU(src,"arm" , "SystemV", 4,null); + } + + @Test public void testStringExport() throws IOException { + String src = +""" +struct String { + u8[] cs; + int _hashCode; +}; + +// Compare two Strings +val equals = { String self, String s -> + if( self == s ) return true; + if( self.cs# != s.cs# ) return false; + for( int i=0; i< self.cs#; i++ ) + if( self.cs[i] != s.cs[i] ) + return false; + return true; +}; + +// Return the String hashCode (cached, and never 0) +val hashCode = { String self -> + self._hashCode + ? self._hashCode + : (self._hashCode = _hashCodeString(self)); +}; + +val _hashCodeString = { String self -> + int hash=0; + for( int i=0; i< self.cs#; i++ ) + hash = hash*31 + self.cs[i]; + if( !hash ) hash = 123456789; + return hash; +}; +"""; + TestC.runC(src, "stringHash", "",18); + } + + @Test public void testLoop2() { + String src = +""" +int i = 0; +while(true) { + i += 1; + if( i==0 ) continue; + if( i==1 ) continue; + if( i==2 ) break; + continue; +} +return i; +"""; + testCPU(src,"x86_64_v2", "win64" ,0,"return (!=0)(inc,Phi(Loop,0,Phi(Region,(!=0),(!=0),0)));"); + testCPU(src,"riscv" , "SystemV",0,"return (!=0)( Phi(Loop,0,Phi(Region,(!=0),(!=0),0)) + #1 );"); + testCPU(src,"arm" , "SystemV",0,"return (!=0)(inc,Phi(Loop,0,Phi(Region,(!=0),(!=0),0)));"); + } + + @Test public void testNewtonExport() throws IOException { + String result = """ +0 0.000000 (0) +1 1.000000 (0) +2 1.414214 (2.22045e-16) +3 1.732051 (0) +4 2.000000 (0) +5 2.236068 (0) +6 2.449490 (0) +7 2.645751 (0) +8 2.828427 (4.44089e-16) +9 3.000000 (0) +"""; + String src = +""" +val test_sqrt = { flt x -> + flt epsilon = 1e-15; + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( guess-epsilon <= next & next <= guess+epsilon ) return guess; + //if( guess==next ) return guess; + guess = next; + } +}; +"""; + TestC.runC(src, "newtonFloat", result, 42); + + EvalRisc5 R5 = TestRisc5.build( src, "test_sqrt", 0, 10, false); + R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 3.0; + int trap_r5 = R5.step(1000); + assertEquals(0,trap_r5); + // Return register A0 holds fib(8)==55 + assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("test_sqrt", src,0, 10, false); + A5.fregs[arm.D0 - arm.D_OFFSET] = 3.0; + int trap_arm = A5.step(1000); + assertEquals(0,trap_arm); + assertEquals(1.732051, A5.fregs[arm.D0 - arm.D_OFFSET], 0.00001); + } + + + @Test public void testSieve() throws IOException { + String src = +""" +val sieve = { int N -> + // The main Sieve array + bool[] !ary = new bool[N]; + // The primes less than N + u32[] !primes = new u32[N>>1]; + // Number of primes so far, searching at index p + int nprimes = 0, p=2; + // Find primes while p^2 < N + while( p*p < N ) { + // skip marked non-primes + while( ary[p] ) p++; + // p is now a prime + primes[nprimes++] = p; + // Mark out the rest non-primes + for( int i = p + p; i < ary#; i+= p ) + ary[i] = true; + p++; + } + + // Now just collect the remaining primes, no more marking + for( ; p < N; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; + + // Copy/shrink the result array + u32[] !rez = new u32[nprimes]; + for( int j=0; j < nprimes; j++ ) + rez[j] = primes[j]; + return rez; +}; +"""; + // The primes + int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, }; + SB sb = new SB().p(primes.length).p("["); + for( int prime : primes ) + sb.p(prime).p(", "); + String sprimes = sb.p("]").toString(); + + // Compile, link against native C; expect the above string of primes to be printed out by C + TestC.runC(src, "sieve", sprimes, 186); + + // Evaluate on RISC5 emulator; expect return of an array of primes in + // the simulated heap. + EvalRisc5 R5 = TestRisc5.build( src, "sieve", 100, 90, false); + int trap = R5.step(10000); + assertEquals(0,trap); + // Return register A0 holds sieve(100) + int ary = (int)R5.regs[riscv.A0]; + // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4) + assertEquals(primes.length, R5.ld4s(ary)); + for( int i=0; i<primes.length; i++ ) + assertEquals(primes[i], R5.ld4s(ary + 4 + i*4)); + + // Evaluate on ARM5 emulator; expect return of an array of primes in + // the simulated heap. + EvalArm64 A5 = TestArm64.build("sieve", src, 100, 105, false); + int trap_arm = A5.step(10000); + assertEquals(0, trap_arm); + int ary_arm = (int)A5.regs[arm.X0]; + // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4) + assertEquals(primes.length, A5.ld4s(ary_arm)); + for( int i = 0; i<primes.length; i++ ) + assertEquals(primes[i], A5.ld4s(ary_arm + 4 + i * 4)); + } + + @Test public void testFibExport() throws IOException { + String src = +""" +val fib = { int n -> + int f1=1; + int f2=1; + while( n-- > 1 ){ + int temp = f1+f2; + f1=f2; + f2=temp; + } + return f2; +}; +"""; + String fib = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]"; + TestC.runC(src, "fib", fib, 24); + + EvalRisc5 R5 = TestRisc5.build( src, "fib", 9, 17, false); + int trap = R5.step(100); + assertEquals(0,trap); + // Return register A0 holds fib(8)==55 + assertEquals(55,R5.regs[riscv.A0]); + + // arm + EvalArm64 A5 = TestArm64.build("fib", src, 9, 17, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + // Return register X0 holds fib(8)==55 + assertEquals(55, A5.regs[arm.X0]); + } + + @Test public void testPerson() throws IOException { + String src = + """ + struct Person { + i32 age; + }; + + val fcn = { Person?[] ps, int x -> + if( ps[x] ) + ps[x].age++; + }; + """; + String person = "6\n"; + TestC.run(src, "person1", null, TestC.CALL_CONVENTION, "", TestC.C_DRIVERS_DIR+"person.c", person, 2); + + // Memory layout starting at PS: + int ps = 1<<16; // Person array pointer starts at heap start + // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8 + // P0 = { age } // sizeof=8 + int p0 = ps+4*8+0*8; + // P1 = { age } // sizeof=8 + int p1 = ps+4*8+1*8; + // P2 = { age } // sizeof=8 + int p2 = ps+4*8+2*8; + EvalRisc5 R5 = TestRisc5.build( src, "fcn", ps, 0, false); + R5.regs[riscv.A1] = 1; // Index 1 + R5.st8(ps,3); // Length + R5.st8(ps+1*8,p0); + R5.st8(ps+2*8,p1); + R5.st8(ps+3*8,p2); + R5.st8(p0, 5); // age= 5 + R5.st8(p1,17); // age=17 + R5.st8(p2,60); // age=60 + + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals( 5+0,R5.ld8(p0)); + assertEquals(17+1,R5.ld8(p1)); + assertEquals(60+0,R5.ld8(p2)); + + EvalArm64 A5 = TestArm64.build("fcn", src, ps, 0, false); + A5.regs[arm.X1] = 1; // Index 1 + A5.st8(ps, 3); + A5.st8(ps+1*8,p0); + A5.st8(ps+2*8,p1); + A5.st8(ps+3*8,p2); + A5.st8(p0, 5); // age= 5 + A5.st8(p1,17); // age=17 + A5.st8(p2,60); // age=60 + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + assertEquals( 5+0, A5.ld8(p0)); + assertEquals(17+1, A5.ld8(p1)); + assertEquals(60+0, A5.ld8(p2)); + } + + @Test public void testArgCount() throws IOException { + // Test passes more args than registers in Sys5, which is far, far more + // than what win64 allows - so win64 gets a lot more spills here. + String src = +""" +val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15, int x16, flt f17 int x18, flt f19 -> + return + i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 + + i10+f11+i12+f13+i14+f15+x16+f17+x18+f19 ; +}; +"""; + String arg_count = "191.000000\n"; + + TestC.runC(src, "arg_count", arg_count, TestC.CALL_CONVENTION.equals("win64") ? 31 : 15); + + EvalRisc5 R5 = TestRisc5.build( src, "addAll", 0, 4, false); + + // Todo: handle stack(imaginary stack in emulator) + // pass in float arguments + R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA1 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA2 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA3 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA4 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA5 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA6 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA7 - riscv.F_OFFSET] = 1.1; + + R5.regs[riscv.A0] = 2; + R5.regs[riscv.A1] = 2; + R5.regs[riscv.A2] = 2; + R5.regs[riscv.A3] = 2; + R5.regs[riscv.A4] = 2; + R5.regs[riscv.A5] = 2; + R5.regs[riscv.A6] = 2; + + int trap = R5.step(100); + assertEquals(0,trap); + + double result = R5.fregs[riscv.FA0 - riscv.F_OFFSET]; + + assertEquals(22.8, result, 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("addAll", src, 0, 4, false); + + A5.fregs[arm.D0 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D1 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D2 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D3 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D4 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D5 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D6 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D7 - arm.D_OFFSET] = 1.1; + + A5.regs[arm.X1] = 2; + A5.regs[arm.X2] = 2; + A5.regs[arm.X3] = 2; + A5.regs[arm.X4] = 2; + A5.regs[arm.X5] = 2; + A5.regs[arm.X6] = 2; + A5.regs[arm.X7] = 2; + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + double result1 = A5.fregs[arm.D0 - arm.D_OFFSET]; + assertEquals(22.8, result1, 0.00001); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java new file mode 100644 index 00000000..5a171659 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java @@ -0,0 +1,278 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.util.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter22Test { + + @Test + public void testJig() throws IOException { + String src = """ +struct s0 { + bool v1; + i16 v2; + int v3; + i8 v4; + byte v5; +}; +while(new s0.v3) + while(new s0.v5<<new s0.v4) {} +if(0) { + if(0) { + flt !P5ZUD4=new s0.v2; + } + while(0) {} +} +return new s0.v1; +"""; + testCPU(src,"x86_64_v2", "win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code.print()); + return code; + } + + + static int testCPUSize( String src, String cpu, String os, int spills ) { + return testCPU(src,cpu,os,spills, "return 0;" )._encoding._bits.size(); + } + + // Should not fold away + @Test public void testSextFail() throws IOException { + String src = """ +struct _Person { i32 age;}; +_Person !p = new _Person; +p.age = (arg<<17)>>17; +return 0; +"""; + assertEquals(83, testCPUSize(src, "x86_64_v2","win64", 3 )); + assertEquals(96, testCPUSize(src, "riscv", "SystemV", 4 )); + assertEquals(96, testCPUSize(src, "arm" , "SystemV", 4 )); + + // do assertEquals here + EvalRisc5 R5 = TestRisc5.build( src, "sext_str_not_fold_away", 0, 4, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away", src, 0, 4, false); + trap = A5.step(100); + assertEquals(0,trap); + + } + + // Should not fold away + @Test public void testSextFail2() throws IOException { + String src = """ + struct _Person { i32 age;}; + _Person !p = new _Person; + p.age = (arg<<48)>>48; + return 0; + """; + + EvalRisc5 R5 = TestRisc5.build( src, "sext_str_not_fold_away_2", 0, 4, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away_2", src, 0, 4, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + assertEquals(83, testCPUSize(src, "x86_64_v2","win64",3 )); + assertEquals(96, testCPUSize(src, "riscv", "SystemV",4 )); + assertEquals(96, testCPUSize(src, "arm", "SystemV",4 )); + + } + + // Should fold away + @Test public void testSextSuccess() throws IOException { + String src = """ +// Should fold away sign extend +struct _Person { i8 age;}; +_Person !p = new _Person; +p.age = (arg<<48)>>48; +return 0; +"""; + + EvalRisc5 R5 = TestRisc5.build( src, "sext_str_fold_away", 0, 5, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_fold_away", src, 0, 5, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + assertEquals(71, testCPUSize(src, "x86_64_v2","win64",3 )); + assertEquals(92, testCPUSize(src, "riscv", "SystemV",5 )); + assertEquals(92, testCPUSize(src, "arm", "SystemV",5 )); + // do assertEquals here + } + + + // Int now is changed to 4 bytes. + @Test public void testPerson() throws IOException { + String src = +""" +struct Person { // Exports a field typed singleton+final *class:Person{age:i32} + i32 age; +}; + +val fcn = { Person?[] ps, int x -> // exports a field with a constant fcn ptr; exports the fcn also + if( ps[x] ) + ps[x].age++; +}; +"""; + String person = "6\n"; + TestC.run(src, "person2", null, TestC.CALL_CONVENTION, "", TestC.C_DRIVERS_DIR+"person.c", person, 2); + + // Memory layout starting at PS: + int ps = 1<<16; // Person array pointer starts at heap start + // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8 + // P0 = { age } // sizeof=8 + int p0 = ps+4*8+0*8; + // P1 = { age } // sizeof=8 + int p1 = ps+4*8+1*8; + // P2 = { age } // sizeof=8 + int p2 = ps+4*8+2*8; + EvalRisc5 R5 = TestRisc5.build( src, "fcn", ps, 0, false); + R5.regs[riscv.A1] = 1; // Index 1 + R5.st8(ps,3); // Length + R5.st8(ps+1*8,p0); + R5.st8(ps+2*8,p1); + R5.st8(ps+3*8,p2); + R5.st8(p0, 5); // age= 5 + R5.st8(p1,17); // age=17 + R5.st8(p2,60); // age=60 + + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals( 5+0,R5.ld8(p0)); + assertEquals(17+1,R5.ld8(p1)); + assertEquals(60+0,R5.ld8(p2)); + + EvalArm64 A5 = TestArm64.build("fcn", src, ps, 0, false); + A5.regs[arm.X1] = 1; // Index 1 + A5.st8(ps, 3); + A5.st8(ps+1*8,p0); + A5.st8(ps+2*8,p1); + A5.st8(ps+3*8,p2); + A5.st8(p0, 5); // age= 5 + A5.st8(p1,17); // age=17 + A5.st8(p2,60); // age=60 + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + assertEquals( 5+0, A5.ld8(p0)); + assertEquals(17+1, A5.ld8(p1)); + assertEquals(60+0, A5.ld8(p2)); + } + + @Test + public void testCoRecur() { + String src = """ +val x = 5; // aa.az; // Error to self-define forward ref +struct _A { Test._B? b; Test._C? c; i64 ax; val az = x*2; new _A = { i64 x -> ax=x; }; }; +struct _B { Test._A? a; Test._C? c; f32 bx; val bz = x*3; new _B = { f32 x, Test._A? aa -> bx=x; a=aa; }; }; +struct _C { Test._A? a; Test._B? b; f64 cx; val cz = x*x; new _C = { f64 x, Test._A? aa, Test._B? bb -> cx=x; a=aa; b=bb; }; }; +_A !aa = new _A(17); +_B !bb = new _B(3.14f, aa); +_C !cc = new _C(2.73, aa, bb); +aa.b = bb; +aa.c = cc; +bb.c = cc; +return cc.cz; +"""; + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("return 25;", code.print()); + assertEquals("25", Eval2.eval(code, 0)); + } + + @Test + public void testHelloWorld() throws IOException { + String src = """ +// fd buf len -> len +{ i32 i64 u32 -> u32 } write = "C"; +// Write byte array to stdout +val p = { u8[~] str -> + i64 ptr = str; // cast array base to i64 + return write(1,ptr,str#); +}; +p("Hello, World!"); +return 0; +"""; + TestC.runSF(src,"helloWorld", "Hello, World!",2); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "helloWorld", 0, 5, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + assertEquals("Hello, World!",R5._stdout.toString()); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("helloWorld", src,0, 5, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + assertEquals("Hello, World!",arm._stdout.toString()); + } + + @Test + public void testFinalArray() { + String src = """ +int N=4; +i32[] !is = new i32[N]; +for( int i=0; i<N; i++ ) + is[i] = i*i; +val _sum = { i32[~] is -> // final array + int sum=0; + for( int i=0; i<is#; i++ ) + sum += is[i]; + return sum; +}; +return _sum(is); +"""; + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code.print()); + assertEquals("14", Eval2.eval(code, 0)); + } + + + @Test @Ignore + public void testEcho() throws IOException { + String src = +""" +// Echo stdin to stdout. +return sys.io.p( sys.io.stdin() ); +"""; + TestC.runSF(src,"echo", "",0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "echo", 0, 2, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("echo", src, 0, 2, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java new file mode 100644 index 00000000..7e64f226 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java @@ -0,0 +1,288 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter23Test { + + @Test @Ignore + public void testJig() throws IOException { + //String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); + String src = Files.readString(Path.of("docs/examples/BubbleSort.smp")); + testCPU(src,"x86_64_v2", "win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + + static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code.print()); + return code; + } + + @Test + public void testOr() throws IOException { + String src = """ +int a = 1; +int b = 0; +int rez = 2; + +if( a++ || b++ ) { + if( b == 0 && a == 2 ) + rez = 0; // Expected answer +} else { + rez = 1; +} +return rez; +"""; + + TestC.runSF( src, "or1", "", 0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "or1", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("or1", src,0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + @Test + public void testAnd() throws IOException { + String src = """ +int a = 1; +int b = 1; +return a && b ? 0 : 1; // Expected answer 0 +"""; + TestC.runSF( src, "and1", "", 0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "and1", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("and1", src, 0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + @Test + public void testAndPtr() throws IOException { + // Todo: have one src here + String src = """ +struct S { S? fld; new S = { S? f -> fld=f; }; }; +val ptr = arg == 1 ? null : new S(arg==1 ? null : new S(null)); +return ptr && ptr.fld ? "true" : "false"; +"""; + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("true", Eval2.eval(code, 0)); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "class:Test.<clinit>", 1, 8, false); + int trap = R5.step(100); + assertEquals(0,trap); + int adr = (int)R5.regs[riscv.A0]; // Returns a Simple *u8[~] string + assertEquals(4,R5.ld4s(adr)); // String length + assertEquals(0x65757274,R5.ld4s(adr+4)); // "true" + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("class:Test.<clinit>", src, 1, 8, false); + trap = arm.step(100); + assertEquals(0,trap); + int adr2 = (int)arm.regs[0]; // Returns a Simple *u8[~] string + assertEquals(4,arm.ld4s(adr2)); // String length + assertEquals(0x65757274,arm.ld4s(adr2+4)); // "true" + } + + // conditional side effect + @Test + public void testCondSideEffAnd() throws IOException { + String src = """ +int x=1; +int y=1; +int z=0; + +int g = x++ && y++ && z++; +return x == 2 && y == 2 && z == 1 && g == 0 + ? 0 // Effected, Expected + : 1; // Error, not effected +"""; + + TestC.runSF( src, "and2", "", 0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "and2", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("and2", src, 0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + @Test + public void testCondSideEffOr() throws IOException { + String src = """ +int x = -1; +int y = 1; +int z = -1; +int g = x++ || y++ || z++; +int switch = (x == 4 || y == 4 || z == 4) ? -1 : 1; +return (x == 0 && y == 1 && z==-1) && switch && g + ? 0 // Effected, expected + : 1; // Error +"""; + TestC.runSF( src, "or2", "", 0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "or2", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("or2", src, 0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + // test it with function calls + @Test + public void testFuncCall() throws IOException { + String src = """ +int a = 1; +int b = 1; +var sq_noInline = { int x -> + x*x; +}; +return (a && sq_noInline(0)) + ? 1 // Error + : 0; // Expected +"""; + TestC.runSF( src, "and3", "", 2); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "and3", 0, 5, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("and3", src, 0, 5, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + + @Test + public void testFRefFields() { + String src = """ +// A hypothetical scanner class +struct _Scan { + int !x; + u8[~] buf; + new _Scan = { u8[~] b -> buf=b; }; + // Skip whitespace + val skip = { -> + while( buf[x] <= ' ' ) + x++; + }; + // Peek a character; if matched consume it, else false. + val peek = { u8 c -> + skip(); + if( buf[x] != c ) return false; + x++; + return true; + }; +}; +_Scan !_s = new _Scan(" q"); +return _s.peek('q'); +"""; + + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("return Phi(Region,0,1);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + testCPU(src,"x86_64_v2", "win64",1,null); + }; + + @Test + public void testFRefFields2() { + String src =""" +// A hypothetical scanner class +struct _Scan { + int !x; + u8[~] buf; + new _Scan = { u8[~] b -> buf=b; }; + // Skip whitespace + val skip = { -> + while( buf[x] <= ' ' ) + x++; + return self; + }; +}; +val s = new _Scan(" q"); +return s.skip().x; +"""; + + try { new CodeGen(src).parse().opto().typeCheck(); fail(); } + catch( Exception e ) { + // Bad error message, but basically requires a mutable 'x' field. + assertEquals("Argument #0 isa *Test._Scan {i64 x; *[]u8 buf; { *Test._Scan -> *Test._Scan {i64 !x; *[]u8 buf; {[ 5]} skip; } #[ 5]} skip; }, but must be a *Test._Scan {i64 !x; *[]u8 buf; { *Test._Scan -> *Test._Scan #[ 5]} skip; }",e.getMessage()); + } + }; + + + @Test + public void testMethod() { + String src = """ +// A hypothetical scanner class +struct _Scan { + int !x; + u8[~] buf; + new _Scan = { u8[~] b -> buf=b; }; + // Skip whitespace + val skip = { -> + while( buf[x] <= ' ' ) + x++; + }; + val require = { u8 ch -> + skip(); + buf[x++]==ch; + }; +}; +_Scan !_s = new _Scan(" [1,2]"); +return _s.require('['); +"""; + + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("return (.[]==91);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + testCPU(src,"x86_64_v2", "win64", 0, null); + }; + + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java new file mode 100644 index 00000000..0f2e1bed --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java @@ -0,0 +1,247 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter24Test { + + @Test + public void testJig() throws IOException { + String src = "int v0=0; v0 = arg && 0== 0 !=0;"; + testCPU(src,"x86_64_v2", "win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code.print()); + return code; + } + + // test it with function calls + @Test + public void testStack1() throws IOException { + String src =""" +int something_specific = 11; +return (10 < something_specific < 20) + ? 0 // Expected + : 1; // Error +"""; + TestC.runSF( src, "stacked_r_1", "", 0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "stacked_r_1", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_1", src, 0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + @Test + public void testStack3() throws IOException { + String src =""" +var sq_noInline = { int x -> + x*x; +}; +int score = 4; +return (sq_noInline(2) <= score < sq_noInline(3)) + ? 0 // Expected + : 1; // Error +"""; + TestC.runSF( src, "stacked_r_3", "", 4); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "stacked_r_3", 0, 5, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_3", src, 0, 5, false); + int trap_1 = arm.step(100); + assertEquals(0,trap_1); + assertEquals(0,arm.regs[0]); + } + + @Test + public void testStack4() throws IOException { + String src =""" +int something_specific = 9; +return (10 < something_specific < 20) + ? 1 // Error + : 0; // Expected +"""; + + TestC.runSF( src, "stacked_r_4", "", 0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src, "stacked_r_4", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_4", src, 0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + @Test + public void testStack6() { + try { new CodeGen("return 0 < arg > 1;" ).parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Mixing relational directions in a chained relational test",e.getMessage()); } + } + + @Test + public void testStack9() throws IOException { + String src = "var stack9 = { int x -> (x < 1) == 1; };"; + String src2 = "return (0 < 1) == 1;"; + TestC.runC(src, "stacked_r_9x", "1", 0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_9", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(1,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_9", src2, 0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(1,arm.regs[0]); + } + + @Test + public void testStack10() throws IOException { + String src ="var stack10 = { int x -> (x < 1) == (1>x);};"; + String src2 ="return (0 < 1) == (1>0);"; + TestC.runC(src, "stacked_r_10x", "1",0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_10", 0, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(1,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_10", src2,0, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(1,arm.regs[0]); + } + + @Test + public void testStack11() throws IOException { + String src = "var stack11 = { int x -> 0 < x < x+1 < 4;}; "; + String src2 = "return 0 < arg < arg+1 < 4;"; + TestC.runC(src, "stacked_r_11x", "1", 2); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_11", 1, 0, false); + R5.regs[riscv.A1] = 1; // Arg in A1, Test.class in A0 + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(1,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_11", src2, 1, 0, false); + arm.regs[1] = 1; // Arg in r1, Test.class in r0 + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(1,arm.regs[0]); + } + + // shows that it is parsed as + // ((0 != x) != 2) + @Test + public void testStack12() throws IOException { + String src = "var stack12 = { int x -> 0 != x != 2; };"; + String src2 = "return 0 != arg != 2;"; + TestC.runC(src, "stacked_r_12x", "1",0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_12", 1, 0, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(1,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_12", src2, 1, 0, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(1,arm.regs[0]); + } + + + // shows that it is parsed as + // ((0 == x) == 1) + @Test + public void testStack13() throws IOException { + String src = "var stack13 = { int x -> 0 == x == 1; };"; + String src2 = "return 0 == arg == 1;"; + TestC.runC(src, "stacked_r_13x", "1",0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_13", 1, 0, false); + R5.regs[riscv.A1] = 1; // Arg in A1, Test.class in A0 + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(1,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("stacked_r_13", src2, 1, 0, false); + arm.regs[1] = 1; // Arg in r1, Test.class in r0 + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(1,arm.regs[0]); + } + + @Test + public void testSCCP0() { + String src = +""" +int x = 1; +int cnt = 5; +while (cnt) { + cnt -= 1; + if (x == 0) { + x += 1; + } +} +return x; +"""; + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testSCCP1() { + String src = +""" +int x = 1; +int cnt = 5; +while( cnt-- ) + if( x == 1 ) + x = 2-x; +return x; +"""; + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("return 1;", code.print()); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java new file mode 100644 index 00000000..cf9d764e --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java @@ -0,0 +1,277 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.ElfReader; +import com.seaofnodes.simple.codegen.ParseAll; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.Ary; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter25Test { + + private static final String SYS_MODDIR = "src/main/smp"; + private static final String SYS_BLDDIR = "build/objs/lib_"+TestC.CPU_ABI; + private static final String RELEASE_SYS_BLDDIR = TestC.RELEASE_SYS_DIR; + + @Test + public void testPostfixFieldUpdate() { + String src = """ + struct V { + u32 !len = 0; + i64[] !buf; + new V = { i64[] b -> buf=b; }; + val grow = { i64 n -> self; }; + val add = { i64 x -> grow(1).buf[len++] = x; self; }; + }; + return new V(new i64[1]).add(7).len; + """; + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + assertEquals("1",Eval2.eval(code,0)); + } + + + @Test @Ignore + public void testModule0() throws IOException { + String MODDIR = "src/test/java/com/seaofnodes/simple/test0"; + String BLDDIR = "build/objs/test0"; + String a_obj = BLDDIR+"/A.o"; + String ab_obj = BLDDIR+"/A/B.o"; + // Remove any prior results so the test runs from scratch. + delELFiles(new File(BLDDIR)); + writeB(MODDIR,5); + + // Compile MODDIR/A.smp into MODDIR/A.o + // Since A refers to B also: + // Compile MODDIR/A/B.smp into MODDIR/A/B.o + CodeGen code1 = new CodeGen(MODDIR, BLDDIR,null, + "A",null,123L,TypeInteger.BOT); + code1.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION); + + // Verify produces A.o, A/B.o + File a_file = new File( a_obj); + File ab_file = new File(ab_obj); + assertTrue( a_file.exists() ); + assertTrue( ab_file.exists() ); + long a_msec1 = a_file.lastModified(); + long ab_msec1 = ab_file.lastModified(); + + // Link and execute: arg is true, so compute "5+1" as the exit code + String rez1 = TestC.gcc("A", 1.2, a_obj, ab_obj); + assertEquals("exec exit code: 6",rez1); + + // Compile again A, expecting both A.o and A/B.o to be up-to-date and not compiled + CodeGen code2 = new CodeGen(MODDIR, BLDDIR, null, + "A",null,123L,TypeInteger.BOT); + code2.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION); + + assertTrue( a_file.exists() ); + assertTrue( ab_file.exists() ); + long a_msec2 = a_file.lastModified(); + long ab_msec2 = ab_file.lastModified(); + + assertEquals( a_msec1, a_msec2 ); + assertEquals( ab_msec1, ab_msec2 ); + + // Touch A.smp and recompile. A/B.o should not recompile. + new File(MODDIR+"/A.smp").setLastModified(System.currentTimeMillis()); + CodeGen code3 = new CodeGen(MODDIR, BLDDIR,null, + "A",null,123L,TypeInteger.BOT); + code3.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION); + + long a_msec3 = a_file.lastModified(); + long ab_msec3 = ab_file.lastModified(); + assertTrue ( a_msec1 < a_msec3 ); + assertEquals( ab_msec1, ab_msec3 ); + + // Link and execute: arg is true, so compute "5+1" as the exit code + String rez3 = TestC.gcc("A", 1.2, a_obj, ab_obj); + assertEquals("exec exit code: 6",rez3); + + + // Modify B.smp and recompile A/B.o; it should recompile and A.o should not. + writeB(MODDIR,7); + CodeGen code4 = new CodeGen(MODDIR, BLDDIR,null, + "A/B",null,123L,TypeInteger.BOT); + code4.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION); + + long a_msec4 = a_file.lastModified(); + long ab_msec4 = ab_file.lastModified(); + assertEquals( a_msec3, a_msec4 ); + assertTrue ( ab_msec3 < ab_msec4 ); + + // Link and execute: uses stale A.o, so remains '6' not '7+1' == 8 + String rez4 = TestC.gcc("A", 1.2, a_obj, ab_obj); + assertEquals("exec exit code: 6",rez4); + + // Recompile A.o, it should recompile despite not being touched because + // it depends on A/B.o which recompiled in the prior step. + CodeGen code5 = new CodeGen(MODDIR, BLDDIR,null, + "A",null,123L,TypeInteger.BOT); + code5.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION); + + long a_msec5 = a_file.lastModified(); + long ab_msec5 = ab_file.lastModified(); + assertTrue ( a_msec4 < a_msec5 ); + assertEquals( ab_msec4, ab_msec5 ); + + // Link and execute: updates A.o from B.o inlining, without compiling B.o + String rez5 = TestC.gcc("A", 1.2, a_obj, ab_obj); + assertEquals("exec exit code: 8",rez5); + // Reset for next time + writeB(MODDIR,5); + } + + private void writeB(String MODDIR, int x) throws IOException { + var bsmp = new FileWriter(MODDIR+"/A/B.smp"); + bsmp.write("val x="+x+";\n"); + bsmp.close(); + } + + + // Recursive search (TODO: gzip, archives) and delete all .o files + private void delELFiles( File dir) { + if( dir.isDirectory() ) + for( File f : dir.listFiles() ) + delELFiles(f); + else if( dir.getName().endsWith(".o") ) + dir.delete(); + } + + + private File buildTestSys(boolean clean) { + if( clean ) + delELFiles(new File(SYS_BLDDIR)); + File sys_file = new File(SYS_BLDDIR+"/sys.o" ); + if( sys_file.exists() ) + return sys_file; + + // Compile SYS_MODDIR/sys.smp into SYS_BLDDIR/sys.o. Chapter25 + // compiler tests intentionally depend on this freshly built sys.o. + CodeGen code1 = new CodeGen(SYS_MODDIR, SYS_BLDDIR,null, + "sys",null,123L,TypeInteger.BOT); + code1.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false,0); + assertTrue( sys_file.exists() ); + return sys_file; + } + + @Test + public void testSys() { + delELFiles(new File(SYS_BLDDIR)); + + CodeGen code1 = new CodeGen(SYS_MODDIR, SYS_BLDDIR,null, + "sys",null,123L,TypeInteger.BOT); + code1.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false,0); + + // Verify produces sys.o, sys/io.o + File sys_file = new File(SYS_BLDDIR+"/sys.o" ); + assertTrue( sys_file.exists() ); + + // Can read the ELF files + ElfReader sys_elf = ElfReader.load( sys_file, null); + sys_elf.loadSimple(code1); + + // Elf files are sane + + // Sys depends on io, libc, aryi64, aryu8 + assertEquals(6,sys_elf._deps.length); + assertSame("sys/aryu8" ,sys_elf._deps[0]); + assertSame("sys/io" ,sys_elf._deps[1]); + assertSame("sys/aryi64",sys_elf._deps[2]); + assertSame("sys/Ary" ,sys_elf._deps[3]); + assertSame("sys/Scan" ,sys_elf._deps[4]); + assertSame("sys/libc" ,sys_elf._deps[5]); + assertSame("class:sys" ,sys_elf._clz._name); + } + + @Test + public void testHelloWorld() throws IOException { + File sys_file = buildTestSys(false); + assertTrue("Missing "+sys_file+"; testHelloWorld depends on testSys building it", sys_file.exists()); + + String expected = "Hello, World!"; + String prog = "return sys.io.p(\""+expected+"\") - "+expected.length()+";"; + TestC.run(prog,"helloWorld",new Ary<>(new String[]{SYS_BLDDIR}), + TestC.CALL_CONVENTION, null, null, expected,0); + } + + @Test + public void testHelloWorldDriver() throws Exception { + File sys_file = new File(RELEASE_SYS_BLDDIR+"/sys.o"); + assertTrue("Missing "+sys_file+"; run make release or make tests_sys first", sys_file.exists()); + Simple.main(new String[]{"-L",RELEASE_SYS_BLDDIR,"--norun","docs/examples/A_helloWorld.smp"}); + } + + @Test + public void testHelloWorldDriverLibFile() throws Exception { + File sys_file = new File(RELEASE_SYS_BLDDIR+"/sys.o"); + assertTrue("Missing "+sys_file+"; run make release or make tests_sys first", sys_file.exists()); + Simple.main(new String[]{"-L",sys_file.toString(),"docs/examples/A_helloWorld.smp"}); + } + + @Test + public void testHelloWorldNoInline() throws Exception { + File sys_file = buildTestSys(false); + assertTrue("Missing "+sys_file+"; testHelloWorldNoInline depends on testSys building it", sys_file.exists()); + + String base = "helloWorldNoInline"; + String expected = "Hello, World!"; + String prog = "return sys.io.p_noInline(\""+expected+"\") - "+expected.length()+";"; + CodeGen code = new CodeGen(null,"build/objs",new Ary<>(new String[]{SYS_BLDDIR}), + base,prog,123L,TypeInteger.BOT); + code.driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,true); + + String obj = "build/objs/"+base+".o"; + String exe = "build/objs/"+base+(TestC.OS.startsWith("Windows") ? ".exe" : ""); + String syms = run(new String[]{"nm",obj}); + assertTrue(syms, syms.contains(" U sys.io.p_noInline")); + + String out = run(new String[]{"gcc",obj,sys_file.toString(),"-lm","-g","-o",exe}); + assertEquals("",out); + String rez = run(new String[]{exe}); + assertEquals(expected,rez); + } + + private static String run(String[] cmd) throws Exception { + Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start(); + boolean normal = p.waitFor(5, TimeUnit.SECONDS); + String out = new String(p.getInputStream().readAllBytes()); + assertTrue(out, normal); + assertEquals(out,0,p.exitValue()); + return out; + } + + @Test + public void testRedirectedRead() throws IOException { + File sys_file = buildTestSys(false); + assertTrue("Missing "+sys_file+"; testRedirectedRead depends on testSys building it", sys_file.exists()); + String src = """ +u8[] buf = new u8[10]; +i64 ptr = buf; +int rez = sys.libc.read(0,ptr,buf#); +return rez < buf# ? 0 : sys.libc._exit(-2); +"""; + TestC.run(src,"redirectedRead",new Ary<>(new String[]{SYS_BLDDIR}), + TestC.CALL_CONVENTION, null, null, + "abc", "", -1); + } + + @Test + public void testBubbles() throws IOException { + File sys_file = buildTestSys(false); + assertTrue("Missing "+sys_file+"; testBubbles depends on testSys building it", sys_file.exists()); + String src = Files.readString( Path.of("docs/examples/BubbleSort.smp")); + TestC.run(src,"BubbleSort",new Ary<>(new String[]{SYS_BLDDIR}), + TestC.CALL_CONVENTION, null, null, + "[3, 2,-17, 999 ] ", "[-17, 2, 3, 999]", -1); + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java new file mode 100644 index 00000000..e88e9caa --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java @@ -0,0 +1,525 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.IRPrinter; +import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.util.SB; +import com.seaofnodes.simple.util.Utils; +import java.util.*; + +public abstract class Eval2 { + + // A mapping from Node ID to evaluation data Object. + // Data Objsts are a boxed Long or Double; or an Object[] of fields, which + // themselves are just more Objects. + private static class Frame { + private static int UID=1; + + private final IdentityHashMap<Node,Object> _data; + private Frame _prior; // Prior frame in this sequence, means when making a new frame we can find old stuff + private int _refcnt; // TODO: Use this in a sentence (free/recycle unused stack frames) + final int _uid = UID++; // Debugging aid + Frame(Frame prior) { + _prior = prior; + if( prior!=null ) prior._refcnt++; + _data = new IdentityHashMap<>(); + _refcnt = 1; + } + + // Dec ref count + void dec() { + assert _refcnt>0; + if( --_refcnt == 0 ) { + Frame prior = _prior; + _prior = null; + _data.clear(); + // TODO: FREE LIST? + if( prior!=null ) + prior.dec(); + } + } + + // Put a mapping from Node->Object + Node put(Node n, Object d) { + assert d!=null; + return put0(n,d); + } + // Put a mapping from Node->Object? + Node put0(Node n, Object d) { + assert _refcnt>0; + _data.put(n,d); + return n; + } + // Get a mapped Node, perhaps using the prior stack frame crawl + Object get(Node n) { + assert _refcnt>0; + Object d = _data.get(n); + return d != null ? d + : _prior == null ? null + : _prior.get(n); + } + + // Short debugging print + public String p() { + return "#"+_uid + (_prior==null ? "" : " >> " + _prior.p()); + } + // Long debugging print + @Override public String toString() { + String s = ""; + if( _prior != null ) s+= _prior+" << "; + s += "#"+_uid+"[ "; + for( Node n : _data.keySet() ) + s += n + ":" + _data.get(n) + ","; + s += "]"; + + return s; + } + } + + // Classic Closure; an environment (stack of frames) and an execution point + private static class Closure { + // Where to return too; either a CallEnd or a Function + final CFGNode _cc; + + // Stack frame at the execution point. Includes Closures which + // recursively include other stack frames + private Frame _frame; + + Closure(CFGNode cc, Frame frame) { + _cc = cc; // One of Fun, CallEnd or Stop (null) + _frame = frame; + assert _frame._refcnt >=0; + _frame._refcnt++; + } + + FunNode fun() { return (FunNode)_cc; } + CallEndNode cend() { return (CallEndNode)_cc; } + boolean isStop() { return _cc instanceof StopNode; } // or Stop + + // + void pushFrame() { + _frame = new Frame(_frame); + _frame._refcnt++; + } + + @Override public String toString() { + return _cc.toString(); + } + } + + // CodeGen has the linker tables + public static CodeGen CODE; + + // Current active Frame, to avoid having to pass it everywhere + public static Frame F; + + public static String eval( CodeGen code, long arg ) { return eval(code,arg,1000); } + public static String eval( CodeGen code, long arg, int timeout ) { + if( code._start.uctrl()==null ) return ""; // The empty program + SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on + // Force local scheduling phase + code.driver(CodeGen.Phase.LocalSched); + // Set global, so don't have to pass everywhere + CODE = code; + + // Timer for when we run too long. Ticks once per backedge or function call + int loopCnt = 0; + + // Current value for each Node; boxed Long or Double; or an Object[] of + // fields, which themselves are just more Objects. + F = new Frame(null); + + // Start at the START + CFGNode BB = code._start, prior = null; + // Return from main hits Stop + F.put(BB,new Closure(code._stop,F)); + // For all <clinit> / Clazzes, install their uninitialized memory + for( FunNode fun : code._linker ) + if( fun!=null && fun.isClz() ) { + TypeStruct clz = ((TypeMemPtr)fun.sig().arg(0))._obj; + // Map from the <clinit> self parameter to the + // singleton class structure. + F.put(fun.parm(2),alloc(clz)); + } + + + // ------------ + // Evaluate until exit or timeout. Each outer loop step computes + // all data nodes under some new Control. + while( true ) { + assert BB!=null; + traceCtrl(BB,trace); + if( trace!=null ) System.out.println(F.p()); + + // Parallel assign Phis + int i = BB instanceof RegionNode r + ? parallelAssignPhis(r,prior,arg,trace) + : 0; + + // Compute the data nodes until BB control + for( ; i < BB.nOuts(); i++ ) + if( !(BB.out(i) instanceof CFGNode) ) + step(BB.out(i), trace); + + // Find the next control target + prior = BB; + BB = BB.uctrl(); // Next unique control target + switch( prior ) { + case StartNode start: F = new Frame(F); break; // Frame for the call to main, as-if Called + case CallNode call : BB = call(call); break; + case IfNode iff : Object p = val(iff.pred()); BB = iff.cproj( p==null || (p instanceof Long x && x==0L) ? 1 : 0); break; + case ReturnNode ret : if( clj(ret.rpc()).isStop() ) return exit(ret); else BB = ret(ret); break; + case FunNode fun : if( loopCnt++ > timeout ) return null; break; // Timeout + case LoopNode loop : if( loopCnt++ > timeout ) return null; break; // Timeout + case CallEndNode cend: break; + case CProjNode cproj: break; + case RegionNode r : break; + default: throw Utils.TODO(); + } + assert !BB.isDead(); + } + } + + + // Phi cache; size is limited to number of Phis at a merge point + private static final Object[] PHICACHE = new Object[256]; + static int parallelAssignPhis(RegionNode r, CFGNode prior, long arg, SB trace) { + + // Compute input path on Phis for this Region + int path = r._inputs.find( prior ); + // Parameters read from prior frame, Phis from local frame + Frame frame = (r instanceof FunNode ? F._prior : F); + boolean isClz = r instanceof FunNode fun && fun.isClz(); + + // Parallel assign Phis. First parallel read and cache + int i; + for( i = 0; i < r.nOuts(); i++ ) { + // Due to scheduling, all Phis are first in the outputs; first + // non-Phi is the end of all Phis. + if( !(r.out(i) instanceof PhiNode phi) ) break; + + // Parms read from Call args - + // - except RPC is the actual prior frame + // - except <clinit> takes the singleton class object and external args + Object val; + if( isClz && phi instanceof ParmNode parm && parm._idx==3 ) + PHICACHE[i] = arg; // Reading the initial arguments to <clinit> + else { + Node n = phi instanceof ParmNode parm + // RPC uses the prior Call directly; + // Parms may not be linked, so read call args directly + ? (parm._idx==0 ? prior : prior.in(parm._idx)) + // Phis read from path input + : phi.in(path); + // Read the frame before overwriting + PHICACHE[i] = frame.get(n); + } + } + // Parallel assign; might assign before read, so read from cache + for( int j=0; j < i; j++ ) + traceData(F.put0(r.out(j),PHICACHE[j]),trace); + // Return point in basic block past last Phi; the Phis have been + // evaluated here, and now we need to evaluate the rest of the block + return i; + } + + + // Make a call. + private static CFGNode call( CallNode call ) { + // Set up the Return PC and return frame (which is just the current frame). + // Really making a Continuation, but it's just a Closure + Closure cont = new Closure(call.cend(),F); + // Set the return continuation into the current frame RPC + F.put(call, cont ); + // Next Frame to use + F = new Frame(F); + // Target function and environment + return CODE.link(tfp(call.fptr())); + } + + private static CFGNode ret( ReturnNode ret ) { + // Fetch return expression value and continuation from current frame + Object rez = val(ret.expr()); + Closure cont = clj(ret.rpc()); + // Lower Frame reference count; might delete frame + F.dec(); // Lower ref count + // Install replacement Frame; set return result over the CallEnd + F = cont._frame; + F.put0(cont._cc,rez); // Set return value into CallEnd + return cont._cc; // Return CallEnd as the new control + } + + private static String exit( ReturnNode ret ) { + return prettyPrint(ret.expr()._type,val(ret.expr())); + } + + // Take a worklist step: one data op from the current Control is updated. + private static void step(Node n, SB trace) { + if( n instanceof PhiNode ) return; + // Compute new value + F.put0(n,compute(n)); + traceData(n,trace); + // Also do any following projections, which are not in the local schedule otherwise + if( n instanceof MultiNode ) + for( Node use : n._outputs ) + step(use,trace); + } + + // From here down shamelessly copied from Evaluator, written by @Xmilia + private static Object compute( Node n ) { + return switch( n ) { + case AddNode add -> add.mode()==1 + ? (Object)(x(add.in(1)) + x(add.in(2))) + : (Object)(d(add.in(1)) + d(add.in(2))); + case AndNode and -> x(and.in(1)) & x(and.in(2)); + case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; // Bool EQ supports pointers, nil, and integers + case BoolNode.LE le -> le.mode()==1 + ? (x(le.in(1)) <= x(le.in(2)) ? 1L : 0L) + : (d(le.in(1)) <= d(le.in(2)) ? 1L : 0L); + case BoolNode.LT lt -> lt.mode()==1 + ? (x(lt.in(1)) < x(lt.in(2)) ? 1L : 0L) + : (d(lt.in(1)) < d(lt.in(2)) ? 1L : 0L); + case CheckCastNode cast -> val(cast.in(1)); + case GuardNode guard -> val(guard.in(1)); + case ConstantNode con -> con(con._con); + case DivNode div -> div.mode()==1 + ? (Object)(x(div.in(1)) / x(div.in(2))) + : (Object)((d(div.in(2))==0 ? 0D : d(div.in(1))) / d(div.in(2))); + case EscapeNode esc -> esc(esc); + case FunPtrNode fptr -> con(fptr._con); + case LoadNode ld -> load(ld); + case MemMergeNode merge-> "$mem"; + case MinusNode sub -> sub.mode()==1 ? (Object)(- x(sub.in(1))) : (Object)(- d(sub.in(1))); + case MulNode mul -> mul.mode()==1 + ? (Object)(x(mul.in(1)) * x(mul.in(2))) + : (Object)(d(mul.in(1)) * d(mul.in(2))); + case NewNode alloc-> alloc(alloc); + case NotNode not -> not(not); + case OrNode or -> x(or .in(1)) | x(or .in(2)); + case ProjNode proj -> proj._type instanceof TypeMem ? "$mem" : val(proj.in(0)); + case ReadOnlyNode read -> val(read.in(1)); + case SarNode sar -> x(sar.in(1)) >> x(sar.in(2)); + case ShlNode shl -> x(shl.in(1)) << x(shl.in(2)); + case ShrNode shr -> x(shr.in(1)) >>>x(shr.in(2)); + case StoreNode st -> store(st); + case SubNode sub -> sub.mode()==1 + ? (Object)(x(sub.in(1)) - x(sub.in(2))) + : (Object)(d(sub.in(1)) - d(sub.in(2))); + case ToFloatNode toflt-> (double)x(toflt.in(1)); + case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2)); + default -> throw Utils.TODO(); + }; + } + + // Fetch without unboxing, searching up-Frame + static Object val( Node n ) { return F.get(n); } + // Fetch and unbox as primitive long + static long x( Node n ) { Object d = F.get(n); return d==null ? 0 : (Long)d; } + // Fetch and unbox as primitive double + static double d( Node n ) { Object d = F.get(n); return d==null ? 0 : (Double)d; } + // Fetch and unbox a function constant + static TypeFunPtr tfp(Node n) { + return (TypeFunPtr)F.get(n); + } + // Fetch and unbox a closure + static Closure clj(Node n) { return (Closure)F.get(n); } + + // Java box ints and floats. Other things can just be null or some informative string + private static Object con( Type t ) { + return switch ( t ) { + case TypeInteger i -> i.isConstant() ? (Long )i.value() : "INT"; + case TypeFloat f -> f.isConstant() ? (Double)f.value() : "FLT"; + case TypeFunPtr tfp -> tfp; + case TypeMem mem -> "MEM"; + case TypeMemPtr tmp -> { + if( tmp._obj.isAry() ) { // Constant array ptr + Type elem = tmp._obj.field("[]")._t; + if( elem instanceof TypeConAry con ) { + Object[] xs = new Object[con.len()]; + for( int i=0; i<con.len(); i++ ) + xs[i] = con.at8(i); + yield xs; + } else { + // Generic constant array, used as a default input to a + // function; should never execute. + yield new Object[0]; + } + + } else { + // Constant object with all zeros + yield alloc(tmp._obj); + } + } + default -> null; + }; + } + + // Escapes of constant memory actually needs to set the memory + private static Object esc(EscapeNode esc) { + if( esc.priv() instanceof ConstantNode con ) { + TypeMem mem = (TypeMem)con._type; + Object val = con(mem._t); + TypeMemPtr tmp = (TypeMemPtr)esc.self()._type; + int idx = tmp._obj.findAlias(esc.fld()._alias); + Object[] fs = (Object[])val(esc.self()); + if( tmp._obj.isAry() ) { + assert idx==0; // length field + assert (Long)val==fs.length; + } else { + fs[idx] = val; + } + } + return "$mem"; + } + + + // Convert array size to array element count + private static int offToIdx( long off, TypeStruct t) { + off -= t.aryBase(); + int scale = t.aryScale(); + assert (off & ((1L << scale)-1)) == 0; + return (int)(off>>scale); + } + + // Builds and returns a pointer Object + private static Object[] alloc(NewNode alloc) { + TypeStruct type = alloc._ts; + if( type.isAry() ) { + long sz = (Long)val(alloc.in(1)); + long x = offToIdx(sz, type); + int n = (int)x; + if( n!=x || n<0 ) + throw new NegativeArraySizeException(""+n); + Object[] ary = new Object[n]; // Array body + var elem = type._fields[1]._t; + if (elem instanceof TypeInteger) { + Arrays.fill( ary, 0L ); + } else if (elem instanceof TypeFloat) { + Arrays.fill( ary, 0D ); + } else { + assert elem instanceof TypeMemPtr || elem instanceof TypeFunPtr; + } + return ary; + } + + return alloc(type); + } + private static Object[] alloc(TypeStruct type) { + assert !type.isAry(); + int num = type._fields.length; + Object[] ptr = new Object[num]; + for( int i=0; i<num; i++ ) + ptr[i] = con(type._fields[i]._t.makeZero()); + return ptr; + } + + private static Object not( NotNode not ) { + Object n = val(not.in(1)); + return ( n==null || (n instanceof Double D && D==0.0) || n instanceof Long L && L==0 ) + ? 1L : 0L; + } + + private static Object load( LoadNode ld ) { + Object f = val(ld.ptr()); + // Check for dense constant array + if( f instanceof TypeMemPtr tmp ) { + throw Utils.TODO(); + } + Object[] fs = (Object[])f; + if( ld._name.equals("#") ) + return (long)fs.length; + TypeMemPtr tmp = (TypeMemPtr)ld.ptr()._type; + int idx = tmp._obj.isAry() + ? offToIdx(x(ld.off()),tmp._obj) + : tmp._obj.find(ld._name); + return fs[idx]; + } + + private static Object store( StoreNode st ) { + if( st._name.equals("#") ) + return "$mem"; // Eval stores the length in the java object[], no need to set it now + TypeMemPtr tmp = (TypeMemPtr)st.ptr()._type; + Object[] fs = (Object[])val(st.ptr()); + int idx = tmp._obj.isAry() + ? offToIdx(x(st.off()),tmp._obj) + : tmp._obj.find(st._name); + Object val = val(st.val()); + fs[idx] = val; + return "$mem"; + } + + + // ------------------------------------------------------------------------ + private static final IdentityHashMap<Object,Object> VISIT = new IdentityHashMap<>(); + + + static void traceCtrl( CFGNode BB, SB sb ) { + if( sb==null ) return; + IRPrinter.printLine(BB,sb.p("--- ")); + System.out.print(sb); + sb.clear(); + } + static void traceData( Node n, SB sb ) { + if( sb==null ) return; + IRPrinter.printLine(n,sb).unchar(); + _print( n._type, F.get(n), sb.p('\t'), VISIT ); + System.out.println(sb); + sb.clear(); + VISIT.clear(); + } + + // Use type to print x as a string. Uses a new VISIT table so the debugger + // doesn't try to reuse VISIT if stopping mid-print. The trace code reuses + // VISIT endlessly for some minor speed hope + static String prettyPrint( Type t, Object x ) { return _print(t,x,new SB(), new IdentityHashMap<>()).toString(); } + static SB _print( Type t, Object x, SB sb, IdentityHashMap<Object,Object> visit ) { + if( x instanceof String s ) return sb.p(s); + if( x == null ) return sb.p("null"); + return switch( t ) { + case TypeInteger i -> sb.p( (Long)x); + case TypeFloat f -> sb.p((Double)x); + case TypeMemPtr tmp -> { + if( visit.containsKey(x) ) yield sb.p("$cyclic"); + visit.put(x,x); + + Object[] xs = (Object[])x; // Array of fields + if( tmp._obj.isAry() ) { + Type elem = tmp._obj._fields[1]._t; + if( elem.isa(TypeInteger.U8) ) { + // Shortcut u8[] as a String + for( Object o : xs ) + sb.p((char)(long)(Long)o); + } else { + // Array of elements + elem.print(sb).p("[ "); + if( elem instanceof TypeConAry ) elem = TypeInteger.ZERO; + for( Object o : xs ) + _print( elem, o, sb, visit ).p( "," ); + sb.unchar().p("]"); + } + } else { + sb.p(tmp._obj._name).p("{"); + Field[] flds = tmp._obj._fields; + for( int i=0; i<flds.length; i++ ) + _print( flds[i]._t,xs[i], sb.p(flds[i]._fname).p("="), visit ).p(","); + sb.unchar().p("}"); + } + yield sb; + } + case TypeFunPtr tfp -> sb.p(x.toString()); + case TypeRPC rpc -> sb.p(x.toString()); + case TypeMem mem -> sb.p("$mem"); + case TypeTuple tt -> { + if( tt._types.length>1 && tt._types[1] instanceof TypeMemPtr ) + // Assume a NewNode + yield _print( tt._types[1], x, sb, visit ); + throw Utils.TODO(); + } + case Type tt -> { + if( tt == Type.NIL ) yield sb.p("null"); + throw Utils.TODO(); + } + }; + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/EvalArm64.java b/chapter25/src/test/java/com/seaofnodes/simple/EvalArm64.java new file mode 100644 index 00000000..32a5b152 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/EvalArm64.java @@ -0,0 +1,766 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.util.Utils; +import java.io.ByteArrayOutputStream; + +public class EvalArm64 { + // Memory image, always 0-based + public final byte[] _buf; + + // GPRs + final long[] regs; + // FRs + final double[] fregs; + + int _pc; + + // Start of free memory for allocation + int _heap; + + // Standard out and err streams + ByteArrayOutputStream _stdout, _stderr; + + // Cycle counters + int _cycle; + + // flags + boolean N = false; + boolean Z = false; + boolean C = false; + boolean V = false; + + EvalArm64(byte[] buf, int stackSize) { + _buf = buf; + regs = new long[32]; + fregs = new double[32]; + // Stack grows down, heap grows up + regs[arm.RSP] = _heap = stackSize; + _pc = 0; + } + + // Little endian + public int ld1s(int x) { return _buf[x ] ; } + public int ld1z(int x) { return _buf[x ]&0xFF; } + public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; } + public int ld2z(int x) { return ld1z(x) | ld1z(x+1)<< 8; } + public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; } + public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; } + + + public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); } + public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); } + + public void st1(int x, int val) { _buf[x] = (byte)val; } + public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); } + public void st4(int x, int val) { st2(x,val); st2(x+2,val>>16); } + public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); } + + public int step(int maxops) { + int trap = 0; + long rval = 0; + double frval = 0; + int pc = _pc; + int cycle = _cycle; + outer: + for(int icount = 0; icount < maxops; icount++) { + boolean is_f = false; + int ir = 0; + rval = 0; + frval = 0; + cycle++; + if( pc >= _buf.length ) { + trap = 1 + 1; // Handle access violation on instruction read. + break; + } + if( (pc & 3)!=0 ) { + trap = 1; // Handle PC-misaligned access + break; + } + + // Load instruction from image buffer at PC + ir = ld4s( pc ); + + int rdid = ir & 0x1f; + // [31:24] + int opcode1 = (ir >> 24) & 0xFF; + + // for the opcode we just match for bits[31:24]; + switch( opcode1 ) { + case 0x14, 0x15, 0x16, 0x17: { //b + int imm26 = ir << 6 >> 6; + pc = pc + (imm26 * 4) - 4; + rdid = -1; + break; + } + case 0x1E: { // floats + is_f = true; + int rs1 = (ir >> 5) & 0x1F; + int rs2 = (ir >> 16) & 0x1F; + + int op = (ir >> 10) & 0x3F; + switch(op) { + case 16: frval = fregs[rs1]; break; // fmov + case 10: frval = fregs[rs1] + fregs[rs2]; break; // fadd + + case 6: if (fregs[rs2] == 0) frval = 0; else frval = fregs[rs1] / fregs[rs2]; break; // fdiv + case 8: { + // fcmp + double lhs = fregs[rs1]; + double rhs = fregs[rs2]; + + Z = (lhs == rhs); + N = (lhs < rhs); + C = (lhs > rhs); + V = Double.isNaN(lhs) || Double.isNaN(rhs); + rdid = -1; + break; + } + case 2: frval = fregs[rs1] * fregs[rs2]; break; // fmul + case 14: frval = fregs[rs1] - fregs[rs2]; break; // fsub + default: trap = (2+1); + } + break; + } + + case 0x38: { + // STRB/LDRB(register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch( ir >> 21 & 0x7 ) { + case 0x1 -> { // store + if( opcode1 == arm.OP_STORE_R_8 ) st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + else trap = (2+1); + rdid = -1; + } + case 0x3 -> { // load + if( opcode1 == arm.OP_LOAD_R_8 ) rval = ld1s((int)regs[rn] + (int)regs[rm]); + else trap = (2+1); + } + default -> trap = (2+1); + } + break; + } + case 0x39: { + // STRB/LDRB(immediate) + // str/ldr(immediate) + // rn + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_8) { + st1((int)rval, (int)regs[rdid]); + } else trap = (2+1); + rdid = -1; + + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_8) { + rval = ld1s((int)rval); + } else trap = (2+1); + rdid = -1; + break; + } + default: trap = (2+1); + } + break; + } + case 0x54: { + // b.cond + int imm19 = ir << 8 >> 13; + imm19 = pc + (imm19 * 4) - 4; + rdid = -1; + + switch(ir & 0xF) { + case 0x0: if(Z) pc = imm19; break; // eq + case 0x1: if(!Z) pc = imm19; break; // ne + case 0x2: if(C) pc = imm19; break; // cs + case 0x3: if(!C) pc = imm19; break; + case 0x4: if(N) pc = imm19; break; // mi + case 0x5: if(!N) pc = imm19; break; // pl + case 0x6: if(V) pc = imm19; break; // vs + case 0x7: if(!V) pc = imm19; break; // vc + case 0x8: if(C && !Z) pc = imm19; break; // hi + case 0x9: if(!C || Z) pc = imm19; break; // ls + case 0xA: if(N == V) pc = imm19; break; // ge + case 0xB: if(N != V) pc = imm19; break; // lt + case 0xC: if(!Z && N == V) pc = imm19; break; // gt + case 0xD: if(Z || N != V) pc = imm19; break; // le + case 0xE: pc = imm19; break; // always executed(al) + default: trap = (2+1); + } + break; + } + + case 0x5c: { + // ldr + is_f = true; + // address = delta + int address = ir << 8 >> 13; + address *= 4; + if(address >= _buf.length-3) trap = (5 + 1); + frval = ld8f(pc + address); + break; + } + + case 0x78: { + // STRH/LDRH(register) + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + // store + if(opcode1 == arm.OP_STORE_R_16) { + st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OP_LOAD_R_16) { + rval = ld1s((int)regs[rn] + (int)regs[rm]); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + break; + } + + case 0x79: { + // STRH/LDRH(immediate) + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + imm *= 2; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_16) { + st2((int)rval, (int)regs[rdid]); + } else trap = (2+1); + rdid = -1; + + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_16) { + rval = ld2s((int)rval); + } else trap = (2+1); + rdid = -1; + break; + } + default: trap = (2+1); + } + break; + } + + case 0x8A: { // AND + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + rval = regs[rn] & regs[rm]; + break; + } + + case 0x8B: { + // add(shifted register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + long lhs = regs[rn]; + long rhs = regs[rm]; + rval = lhs + rhs; + break; + } + + case 0x90, 0xB0, 0xD0, 0xF0: { // adrp + int immhi = ( ir << 8 ) >>> (8+5); + int immlo = (ir>>29)& 3; + int imm = ((immhi<<2) | immlo); + imm = (imm << 11) >> 11; + imm <<= 12; + rval = (pc & ~0xFFF) + imm; + break; + } + + case 0x91: { // add(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + // only hit it twice + rval = regs[rn] + immediate; + break; + } + case 0x92: { // and(immediate) + int imm12 = (ir >> 10) & 0x1FFF; + long immediate = arm.decodeImm12(imm12); + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] & immediate; + break; + } + case 0x93: { + // asr(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] >> (imm); + break; + } + + case 0x94, 0x95, 0x96, 0x97: { // bl + rval = pc + 4; + regs[arm.X30] = rval; + int imm26 = (ir & 0x03FFFFFF); + int imm = (imm26 << 6) >> 6; + pc = pc + (imm << 2) ; + if( pc == Encoding.SENTINEL_CALLOC ) { + assert (_heap&7) == 0; // 8-byte aligned + long size = regs[arm.X0]*regs[arm.X1]; + size = (size+7) & -8; // 8-byte aligned + regs[arm.X0] = _heap; + _heap += (int)size; + pc = (int)rval; + } + if( pc == Encoding.SENTINEL_WRITE ) { + ByteArrayOutputStream baos = switch((int)regs[0]) { + case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout; + case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr; + default -> throw new IllegalArgumentException(); + }; + baos.write(_buf,(int)regs[1],(int)regs[2]); + regs[0] = regs[2]; + pc = (int)rval; + } + pc -= 4; + rdid = -1; + break; + } + + case 0x9A: { + int encodedCond = (ir >> 12) & 0xF; + int decodedCond = encodedCond ^ 1; + // conditional select(csel) + boolean cond = switch (decodedCond) { + case 0x0 -> Z ; // eq + case 0x1 -> !Z ; // ne + case 0x2 -> C ; // cs + case 0x3 -> !C ; // cc + case 0x4 -> N ; // mi + case 0x5 -> !N ; // pl + case 0x6 -> V ; // vs + case 0x7 -> !V ; // vc + case 0x8 -> ( C && !Z ) ; // hi + case 0x9 -> (!C || Z ) ; // ls + case 0xA -> ( N == V) ; // ge + case 0xB -> ( N != V) ; // lt + case 0xC -> (!Z && N == V) ; // gt + case 0xD -> ( Z || N != V ); // le + case 0xE -> true ; // always + case 0xF -> false ; // never + default -> throw Utils.TODO(); + }; + if( cond ) rval = 1; + break; + } + case 0x9B: { // mul + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + rval = regs[rn] * regs[rm]; + break; + } + case 0x9E: { + // scvtf + is_f = true; + int rs1 = (ir >> 5) & 0x1F; + frval = (double)regs[rs1]; + break; + } + + case 0xAA: { + // MOV(register) + int rm = (ir >> 16) & 0x1F; + rval = regs[rm]; + break; + } + + case 0xB8: { + // LDR/STR (register + index) 32 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OP_STORE_R_32) { + st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if( opcode1 == arm.OP_LOAD_R_32) { + rval = ld4s((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + + case 0xB9: { + // str/ldr(immediate) 32 bits + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + imm *= 4; + + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_32) { + st4((int)rval, (int)regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_32) { + rval = ld4s((int) rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + + case 0xBC: { + // LDR/STR (register + index) float 32 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + is_f = true; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OPF_STORE_R_32) { + st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OPF_LOAD_R_32) { + frval = ld4f((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + + case 0xBD: { + // LDR/STR(immediate) 32 bits floats + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 4; + is_f = true; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OPF_STORE_IMM_32) { + st4((int)rval, (int)regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OPF_LOAD_IMM_32) { + frval = ld4s((int)rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + + case 0xD2: { + // movz + // get immediate + rval = (ir >> 5) & 0x7FFF; + break; + } + + case 0xEB: { + // subs(shifted register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + long lhs = regs[rn]; + long rhs = regs[rm]; + rval = lhs - rhs; + + // set flags as a side effect + Z = (rval == 0); + N = (rval < 0); + C = lhs >= rhs; + + boolean sign_lhs = (lhs >>> 63) != 0; + boolean sign_rhs = (rhs >>> 63) != 0; + boolean sign_res = (rval >>> 63) != 0; + V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); + rdid = -1; + break; + } + + case 0xD3: { + // lsl(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] << (64 - imm); + break; + } + case 0xD6: { + if( (ir & 0xFFFF) != 0 ) throw Utils.TODO(); + if( ((ir>>16) & 0xFF) != 0x5F ) throw Utils.TODO(); // 0x3F is register call + rdid = -1; + int rn = ir << 22 >> 27; + assert rn == 0; + pc = (int)regs[30]; + // Return to zero breaks simulation + if( pc==0 ) break outer; + pc -= 4; // Undo post-increment + break; + } + + case 0xF1: { + // subs(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + long lhs = regs[rn]; + + rval = lhs - immediate; + + // set flags as a side effect + Z = (rval == 0); + N = (rval < 0); + C = lhs >= immediate; + + boolean sign_lhs = (lhs >>> 63) != 0; + boolean sign_rhs = (immediate >>> 63) != 0; + boolean sign_res = (rval >>> 63) != 0; + V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); + rdid = -1; + break; + } + case 0xF8: { + // LDR/STR (register + index) 64 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OP_STORE_R_64) { + st8((int)regs[rn] + (int)regs[rm], regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OP_LOAD_R_64) { + rval = ld8((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + case 0xF9: { + // str/ldr(immediate) 64 bits + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 8; + + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_64) { + st8((int)rval, regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_64) { + rval = ld8((int)rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + + case 0xFC: { + // LDR/STR (register + index) float 64 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + is_f = true; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OPF_STORE_R_64) { + st8((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OPF_LOAD_R_64) { + frval = ld8f((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + case 0xFD: { + // LDR/STR(immediate) 64 bits floats + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 4; + is_f = true; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OPF_STORE_IMM_64) { + st8((int)rval, regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OPF_LOAD_IMM_64) { + frval = ld8((int)rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + + default: trap = (2+1); + } + + if(trap != 0) { + _pc = pc; + break; + } + if(rdid != -1) { + if(is_f) fregs[rdid] = frval; + else regs[rdid] = rval; + } + + pc += 4; + } + // Handle traps and interrupts. + if(trap != 0) + return trap; + _cycle = cycle; + _pc = pc; + return 0; + } + + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/EvalRisc5.java b/chapter25/src/test/java/com/seaofnodes/simple/EvalRisc5.java new file mode 100644 index 00000000..8c0a8792 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/EvalRisc5.java @@ -0,0 +1,368 @@ + package com.seaofnodes.simple; + +/** Simple RISC5 CPU Emulator + * + * Original works credit Charles Lohr. + * This is a complete rewrite in Java. + * + * Load code into memory and go! + */ + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import com.seaofnodes.simple.util.Utils; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +public class EvalRisc5 { + + // Memory image, always 0-based + public final byte[] _buf; + + // GPRs + final long[] regs; + // FRs + final double[] fregs; + // PC + int _pc; + + // Start of free memory for allocation + int _heap; + + // Standard out and err streams + ByteArrayOutputStream _stdout, _stderr; + + // Cycle counters + int _cycle; + + EvalRisc5( byte[] buf, int stackSize ) { + _buf = buf; + regs = new long[32]; + fregs = new double[32]; + // Stack grows down, heap grows up + regs[riscv.RSP] = _heap = stackSize; + _pc = 0; + } + + // Little endian + public int ld1s(int x) { return _buf[x ] ; } + public int ld1z(int x) { return _buf[x ]&0xFF; } + public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; } + public int ld2z(int x) { return ld1z(x) | ld1z(x+1)<< 8; } + public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; } + public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; } + + public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); } + public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); } + + + public void st1(int x, int val) { _buf[x] = (byte)val; } + public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); } + public void st4(int x, int val) {st2(x,val); st2(x+2,val>>16); } + public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); } + + // Note: only a few bits are used. (Machine = 3, User = 0) + // Bits 0..1 = privilege. + // Bit 2 = WFI (Wait for interrupt) + // Bit 3+ = Load/Store reservation LSBs. + int _extraflags; + + public int step( int maxops ) { + int trap = 0; + long rval = 0; + double frval = 0; + int pc = _pc; + int cycle = _cycle; + boolean is_f = false; + outer: + for( int icount = 0; icount < maxops; icount++ ) { + int ir = 0; + rval = 0; + cycle++; + + if( pc >= _buf.length ) { + trap = 1 + 1; // Handle access violation on instruction read. + break; + } + if( (pc & 3)!=0 ) { + trap = 1; // Handle PC-misaligned access + break; + } + // Load instruction from image buffer at PC + ir = ld4s( pc ); + int rdid = (ir >> 7) & 0x1f; + + switch( ir & 0x7f ) { + case 0x37: // LUI (0b0110111) + rval = ( ir & 0xfffff000 ); + break; + case 0x17: // AUIPC (0b0010111) + rval = pc + ( ir & 0xfffff000 ); + break; + case 0x6F: { // JAL (0b1101111) + int reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000)); + if( (reladdy & 0x00100000)!=0 ) reladdy |= 0xffe00000; // Sign extension. + rval = pc + 4; + pc = pc + reladdy - 4; + if( pc+4 == Encoding.SENTINEL_WRITE ) { + ByteArrayOutputStream baos = switch((int)regs[10]) { + case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout; + case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr; + default -> throw new IllegalArgumentException(); + }; + baos.write(_buf,(int)regs[11],(int)regs[12]); + regs[10] = regs[12]; + rdid = 0; + pc = (int)(rval - 4); // Unwind PC, as-if returned from write + } + break; + } + case 0x67: { // JALR (0b1100111) + int imm_se = ir >> 20; // Sign extend 12 bits + rval = pc + 4; + pc = ((int)regs[ (ir >> 15) & 0x1f ] + imm_se) & ~1; + // Return from top-level ; exit sim + if( pc==0 ) { + if( rdid!=0 ) regs[rdid] = rval; // Write PC into register before exit + break outer; + } + // Inline CALLOC effect + if( pc == Encoding.SENTINEL_CALLOC ) { + assert (_heap&7) == 0; // 8-byte aligned + int size = (int)(regs[10]*regs[11]); + size = (size+7) & -8; // 8-byte aligned + regs[10] = _heap; // Return next free address + _heap += size; + pc = (int)(rval - 4); // Unwind PC, as-if returned from calloc + } else + pc -= 4; + break; + } + case 0x63: { // Branch (0b1100011) + int immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12); + if( (immm4 & 0x1000)!=0 ) immm4 |= 0xffffe000; + long rs1 = regs[(ir >> 15) & 0x1f]; + long rs2 = regs[(ir >> 20) & 0x1f]; + immm4 = pc + immm4 - 4; + rdid = 0; + if( switch( (ir >> 12) & 0x7 ) { + case 0 -> rs1 == rs2; + case 1 -> rs1 != rs2; + case 4 -> rs1 < rs2; + case 5 -> rs1 >= rs2; + case 6 -> Long.compareUnsigned(rs1,rs2) < 0; //BLTU + case 7 -> Long.compareUnsigned(rs1,rs2) >= 0; //BGEU + default -> { trap = (2+1); yield false; } + } ) pc = immm4; + break; + } + case 0x03: { // Load (0b0000011) + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + int imm = ir >> 20; + int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 ); + int rsval = rs1 + imm_se; + + if( rsval >= _buf.length-3 ) { + trap = (5+1); + rval = rsval; + } else { + switch( ( ir >> 12 ) & 0x7 ) { + //LB, LH, LW, LBU, LHU + case 0: rval = ld1s( rsval ); break; + case 1: rval = ld2s( rsval ); break; + case 2: rval = ld4s( rsval ); break; + case 3: rval = ld8 ( rsval ); break; + case 4: rval = ld1z( rsval ); break; + case 5: rval = ld2z( rsval ); break; + case 6: rval = ld4s( rsval ); break; + default: trap = (2+1); + } + } + break; + } + // floats + case 0x7: { + is_f = true; + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + int imm = ir >> 20; + int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 ); + int rsval = rs1 + imm_se; + + if( rsval >= _buf.length-3 ) { + trap = (5+1); + rval = rsval; + } else { + switch( ( ir >> 12 ) & 0x7 ) { + case 2: frval = ld4f( rsval ); break; + case 3: frval = ld8f( rsval ); break; + default: trap = (2+1); + } + } + break; + } + + case 0x23: { // Store 0b0100011 + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + long rs2 = regs[(ir >> 20) & 0x1f]; + int addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 ); + if( (addy & 0x800)!=0 ) addy |= 0xfffff000; + addy += rs1; + rdid = 0; + if( addy >= _buf.length-3 ) { + trap = (7+1); // Store access fault. + rval = addy; + } else { + switch( ( ir >> 12 ) & 0x7 ) { //SB, SH, SW + case 0: st1( addy, (int)rs2 ); break; + case 1: st2( addy, (int)rs2 ); break; + case 2: st4( addy, (int)rs2 ); break; + case 3: st8( addy, rs2 ); break; + default: trap = (2+1); + } + } + break; + } + case 0x13: // Op-immediate 0b0010011 + case 0x33: { // Op 0b0110011 + int imm = ir >> 20; + imm = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0); + long rs1 = regs[(ir >> 15) & 0x1f]; + boolean is_reg = (ir & 0x20)!=0; + long rs2 = is_reg ? regs[imm & 0x1f] : imm; + + // Insert the detection here + int rs1id = (ir >> 15) & 0x1f; + int funct3 = (ir >> 12) & 0x7; + + if( is_reg && ( ir & 0x02000000 )!=0 ) { + switch( (ir>>12)&7 ) { //0x02000000 = RV32M + case 0: rval = rs1 * rs2; break; // MUL + case 1: rval = (int)((long)((int)rs1) * (long)((int)rs2)) >> 32; break; // MULH + case 2: rval = (int)((long)((int)rs1) * (long)rs2) >> 32; break; // MULHSU + case 3: rval = (int)((long)rs1 * (long)rs2) >> 32; break; // MULHU + case 4: if( rs2 == 0 ) rval = -1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? rs1 : ((int)rs1 / (int)rs2); break; // DIV + case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU + case 6: if( rs2 == 0 ) rval = rs1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? 0 : ((int)((int)rs1 % (int)rs2)); break; // REM + case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU + } + } else { + rval = switch( (ir >> 12) & 7 ) { // These could be either op-immediate or op commands. Be careful. + case 0 -> (is_reg && (ir & 0x40000000) != 0) ? (rs1 - rs2) : (rs1 + rs2); + case 1 -> rs1 << (rs2 & 0x3F); + case 2 -> (rs1 < rs2) ? 1 : 0; + case 3 -> (rs1 < rs2) ? 1 : 0; + case 4 -> rs1 ^ rs2; + case 5 -> (ir & 0x40000000) != 0 ? (rs1 >>> (rs2 & 0x3F)) : (rs1 >> (rs2 & 0x3F)); + case 6 -> rs1 | rs2; + case 7 -> rs1 & rs2; + default -> rval; + }; + } + break; + } + case 0x0f: // 0b0001111 + rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl. + break; + case 0x73: // Zifencei+Zicsr (0b1110011) + trap = (2+1); // Note micrrop 0b100 == undefined. + break; + + case 0x2f: { // RV32A (0b00101111) + long rs1 = regs[(ir >> 15) & 0x1f]; + long rs2 = regs[(ir >> 20) & 0x1f]; + int irmid = ( ir>>27 ) & 0x1f; + + // We don't implement load/store from UART or CLNT with RV32A here. + if( rs1 >= _buf.length-3 ) { + trap = (7+1); //Store/AMO access fault + rval = rs1; + } else { + rval = ld4s( (int)rs1 ); + + // Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c + boolean dowrite = true; + switch( irmid ) { + case 2: //LR.W (0b00010) + dowrite = false; + _extraflags = (_extraflags & 0x07) | (int)(rs1<<3); + break; + case 3: { //SC.W (0b00011) (Make sure we have a slot, and, it's valid) + boolean xflag = _extraflags >> 3 != ( rs1 & 0x1fffffff ); + rval = xflag ? 1 : 0; // Validate that our reservation slot is OK. + dowrite = !xflag; // Only write if slot is valid. + break; + } + case 0: rs2 += rval; break; // AMOADD.W (0b00000) + case 1: break; // AMOSWAP.W (0b00001) + case 4: rs2 ^= rval; break; // AMOXOR.W (0b00100) + case 8: rs2 |= rval; break; // AMOOR.W (0b01000) + case 12: rs2 &= rval; break; // AMOAND.W (0b01100) + case 16: rs2 = Math.min( rs2, rval ); break; //AMOMIN.W (0b10000) + case 20: rs2 = Math.max( rs2, rval ); break; //AMOMAX.W (0b10100) + case 24: rs2 = Math.min( rs2, rval ); break; //AMOMINU.W (0b11000) + case 28: rs2 = Math.max( rs2, rval ); break; //AMOMAXU.W (0b11100) + default: trap = (2+1); dowrite = false; break; //Not supported. + } + if( dowrite ) st4( (int)rs1, (int)rs2 ); + } + break; + } + + case 0x53: { + is_f = true; + int rs1 = (ir >> 15) & 0x1F; + int rs2 = (ir >> 20) & 0x1F; + double lhs = fregs[rs1]; + double rhs = fregs[rs2]; + int func5 = (ir >> 27) & 0x1F; + int func2 = (ir >> 25) & 0x3; + if( func2 != 1 ) throw Utils.TODO(); // 1==double + switch( func5 ) { + case 0: frval = lhs + rhs; break; // fadd.d + case 1: frval = lhs - rhs; break; // fsub.d + case 2: frval = lhs * rhs; break; // fmul.d + case 3: frval = lhs / rhs; break; // fdiv.d + case 5: frval = Math.min(lhs,rhs); break; // fmin.d, also used for move + case 20: + is_f = false; // Output into a GPR + rval = switch( (ir >> 12) & 7 ) { + case 0 -> lhs <= rhs ? 1 : 0; + case 1 -> lhs == rhs ? 1 : 0; + case 2 -> lhs < rhs ? 1 : 0; + default -> throw Utils.TODO(); + }; + break; + case 26: frval = (double)regs[rs1]; break; // fcvt.w.d + default: + trap = (2+1); // Fault: Invalid opcode. + throw Utils.TODO(); + } + break; + } + + default: trap = (2+1); // Fault: Invalid opcode. + } + + // If there was a trap, do NOT allow register writeback. + if( trap!=0 ) { + _pc = pc; + break; + } + + if( rdid!=0 || is_f) { + if(is_f) {fregs[rdid] = frval; is_f = false;} // Write back register. + else regs[rdid] = rval; // Write back register. + } + + pc += 4; + } + + // Handle traps and interrupts. + if( trap!=0 ) + return trap; + + _cycle = cycle; + _pc = pc; + return 0; + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java new file mode 100644 index 00000000..d67b2065 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.fuzzer.Fuzzer; +import java.util.Random; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +/** + * To use the fuzzer run the <code>fuzzPeeps</code> methods. + * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line + * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code> + */ +public class FuzzerWrap { + + @Ignore + @Test + public void fuzzPeeps() { + var fuzzer = new Fuzzer(); + for (int i=0; i<1000000; i++) + fuzzer.fuzzPeeps(i); + assertTrue(fuzzer.noExceptions()); + } + + + @Test + public void fuzzPeepsSmall() { + Random R = new Random(System.currentTimeMillis()); + var fuzzer = new Fuzzer(); + for (int i=0; i<100; i++) + fuzzer.fuzzPeeps( R.nextLong()); + assertTrue(fuzzer.noExceptions()); + } + + @Test + @Ignore + public void fuzzPeepTiming() { + var fuzzer = new Fuzzer(); + int max_nid=0; + for (int i=0; i<1000000; i++) + max_nid = fuzzer.fuzzPeepTiming(i, max_nid); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Main.java b/chapter25/src/test/java/com/seaofnodes/simple/Main.java new file mode 100644 index 00000000..589c043b --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Main.java @@ -0,0 +1,18 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import java.nio.file.Files; +import java.nio.file.Path; + +public class Main { + // Compile and run a simple program + public static void main( String[] args ) throws Exception { + // First arg is file, 2nd+ args are program args + String src = Files.readString(Path.of(args[0])); + int arg = Integer.valueOf(args[1]); + CodeGen code = new CodeGen(src); + code.parse().opto().typeCheck().GCM().localSched(); + System.out.println(code._stop); + System.out.println(Eval2.eval(code,arg,100000)); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java new file mode 100644 index 00000000..ca33438a --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java @@ -0,0 +1,81 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import com.seaofnodes.simple.util.SB; +import java.io.IOException; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class MergeSortTest { + + @Test public void testMergeSort0() throws IOException { + // Test merge sort on a small array of primes + String src = +""" +val merge_sort = { int[] a -> + int[] !b = new int[a#]; + _copy_array (a, 0, a#, b); + _split_merge(a, 0, a#, b); +}; + +val _split_merge = { int[] b, int begin, int end, int[] a -> + if (end - begin <= 1) + return 0; + int middle = (end + begin) / 2; + _split_merge(a, begin, middle, b); + _split_merge(a, middle, end, b); + _merge(b, begin, middle, end, a); + return 0; +}; + +val _merge = { int[] b, int begin, int middle, int end, int[] a -> + int i = begin, j = middle; + + for (int k = begin; k < end; k++) { + // && and || + bool cond = false; + if (i < middle) { + if (j >= end) cond = true; + else if (a[i] <= a[j]) cond = true; + } + if (cond) b[k] = a[i++]; + else b[k] = a[j++]; + } +}; + +val _copy_array = { int[] a, int begin, int end, int[] b -> + for (int k = begin; k < end; k++) + b[k] = a[k]; +}; +"""; + int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, }; + SB sb = new SB().p(primes.length).p("["); + for( int prime : primes ) + sb.p(prime).p(", "); + String sprimes = sb.p("]").toString(); + TestC.runC(src, "sort", sprimes, 34); + + + EvalRisc5 R5 = TestRisc5.build( src, "merge_sort", 0, 36, false); + // Allocate an array of primes, filled in reverse order + int ps = R5._heap; + R5._heap += (primes.length+1)*8; // 25 primes, and length + R5.st4(ps,primes.length); + for( int i=0; i<primes.length; i++ ) + R5.st8(ps+(primes.length-1-i+1)*8,primes[i]); + // Pass array base when calling `merge_sort` + R5.regs[riscv.A0] = ps; // Array base to be sorted + + int trap = R5.step(100000); + assertEquals(0,trap); + + assertEquals(primes.length,R5.ld4s(ps)); + for( int i=0; i<primes.length; i++ ) + assertEquals(primes[i], R5.ld8(ps+(i+1)*8)); + + // TODO: replace with ARM Eval + Chapter21Test.testCPU(src,"arm", "SystemV",36,null); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java new file mode 100644 index 00000000..870792c0 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java @@ -0,0 +1,251 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.CompUnit; +import com.seaofnodes.simple.codegen.CodeGen.Phase; +import com.seaofnodes.simple.print.*; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.stream.Collectors; + +public class Simple { + static void usage() { + System.out.println( +""" +simple [options] input-file.smp ...program args... +Options are applied in-order. +Options: + --help - This text + + --root root/ - Project root directory; this must end in a '/' and input + files are relative paths to this. If missing, the one input + file path is treated as its own root directory. + + -o output - Project output directory; similar to --root, if 'output' ends in a + '/', then all result files use relative paths to 'output', using a input file + root-relative path. If --root is missing, the one result file is named 'output' + + -L path - add an external Simple object search path + --eval - (slowly) evaluate the compiled code in emulator + --run - run the compiled code natively; this is the default + --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm) + --abi <abi-name> - use specific ABI variant (SystemV) + --target - print native CPU and ABI + --version + -S - dump generated assembler code + --dump-size - print the size of generated code + --dump-time - print compilation and execution times + --dump-after-parse - dump intermediate representation after parse + --dump-after-iter - dump intermediate representation after pessimistic iter pass + --dump-after-opto - dump intermediate representation after opto pass + --dump-after-type-check - dump intermediate representation after type check pass + --dump-after-select - dump intermediate representation after instruction selection pass + --dump-after-gcm - dump intermediate representation after GCM pass + --dump-after-local-sched - dump intermediate representation after local scheduling pass + --dump-after-reg-alloc - dump intermediate representation after register allocation pass + --dump-after-encode - dump intermediate representation after encoding pass + --dump-after-all - dump intermediate representation after all passes + --dump - dump final intermediate representation only + --dot - dump grapical representation of intermediate code into *.dot file(s) +""" +); + System.exit(0); + } + + static void bad_usage() { throw bad("Invalid usage (use --help)"); } + static RuntimeException bad(String err) { + System.err.println("ERROR: "+err); + System.exit(1); + return new RuntimeException(err); + } + + static void target() { + System.out.print(TestC.CPU_PORT); + System.out.print("-"); + System.out.println(TestC.CALL_CONVENTION); + } + + static void print_compilation_times(CodeGen code) { + long[] times = code._times; + for( int i=0; i<code._times.length; i++ ) + System.out.printf("%20s %.3f sec%n", Phase.values()[i], times[i]); + } + + static String getInput() { + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + return reader.lines().collect(Collectors.joining("\n")); + } + + public static void main(String[] args) throws Exception { + String root = null; + String input_filename = null; + boolean do_eval = false; + boolean do_run = true; + boolean do_codegen = false; + boolean dump_dot = false; + boolean print_time = false; + boolean print_size = false; + boolean print_asm = false; + int dump = 0; + int first_arg = 0; + String src = null; + String cpu = null; + String abi = null; + String out = null; + ArrayList<String> libPaths = new ArrayList<>(); + + // Parse command line + int i; for( i = 0; i < args.length; i++ ) { + String arg = args[i]; + if( arg.charAt(0) != '-' ) { + input_filename = arg; + break; + } + + switch (arg) { + case "-": input_filename=""; break; + case "--dump-after-parse": dump |= 1<<Phase.Parse .ordinal() ; break; + case "--dump-after-iter": dump |= 1<<Phase.Iter .ordinal() ; break; + case "--dump-after-opto": dump |= 1<<Phase.Opto .ordinal() ; break; + case "--dump-after-type-check": dump |= 1<<Phase.TypeCheck .ordinal() ; break; + case "--dump-after-loop-tree": dump |= 1<<Phase.LoopTree .ordinal() ; break; + case "--dump-after-select": dump |= 1<<Phase.Select .ordinal() ; break; + case "--dump-after-gcm": dump |= 1<<Phase.Schedule .ordinal() ; break; + case "--dump-after-local-sched": dump |= 1<<Phase.LocalSched.ordinal() ; break; + case "--dump-after-reg-alloc": dump |= 1<<Phase.RegAlloc .ordinal() ; break; + case "--dump-after-encode": dump |= 1<<Phase.Encoding .ordinal() ; break; + case "--dump-after-export": dump |= 1<<Phase.Export .ordinal() ; break; + case "--dump": dump |= 1<<Phase.LastPhase .ordinal() ; break; + case "--dump-after-all": dump |= (2<<Phase.LastPhase .ordinal())-1; break; + case "--dot": dump_dot = true; break; + case "-S": print_asm = true; break; + case "--eval": do_eval = true ; do_run = false; break; + case "--run": do_run = true ; break; + case "--norun": do_run = false; break; + case "--dump-size": print_size = true; break; + case "--dump-time": print_time = true; break; + case "-o": if (out != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + out = args[++i]; do_codegen=true; + break; + case "-L": if (i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + libPaths.add(args[++i]); + break; + case "--cpu": if (cpu != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + cpu = args[++i]; + break; + case "--abi": if (abi != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + abi = args[++i]; + break; + case "--target": target(); break; + case "--version": throw Utils.TODO(); + case "--help": usage(); + default: bad_usage(); + } + } + + // Break the paths into these parts: + // - A module path, given from "--root" or inferred from the one input file + // - Some sources [modpath/relpath/file.smp]*, all relative to the module path. + // - A build path, given from "-o" or inferred from the one input file + // + // All the sources are compiled, with outputs in [buildpath/relpath/file.o]* + + if (input_filename == null) throw bad("no input file' (use --help)"); + if( !input_filename.isEmpty() && !input_filename.endsWith(".smp") ) + throw bad("File extension must be .smp"); + Path inPath = Path.of(input_filename); + + // Figure out the module path + Path modPath; + if( root == null ) { + modPath = inPath.getParent(); + if( modPath == null ) + modPath = Path.of("."); + } else { + throw Utils.TODO(); + } + + // Compute the module-relative paths for all sources + Path srcRelPath = modPath.relativize(inPath); + int n = srcRelPath.getNameCount(); + Path relPath = n==1 ? null : srcRelPath.subpath(0,n-1); + // Compute source/class name + String srcNameExt = srcRelPath.getName(n-1).toString(); + String srcName = srcNameExt.substring(0,srcNameExt.length()-4); + + // Compute the build path. + Path outPath; + if( out == null ) { + // If no output given, the build dir is the modPath + outPath = modPath; + } else { + // If the output is a directory, that is our out path + outPath = Path.of(out); + if( !out.endsWith("/") ) { + // If the output is a file, the directory is our outPath + Path fullOut = outPath; + outPath = outPath.getParent(); + if( outPath == null ) + outPath = Path.of("."); + Path foo = outPath.relativize(fullOut); + if( !foo.toString().equals(srcName+".o") ) + throw bad("Output path needs to be of the form "+srcName+".o"); + } + } + String outBase = outPath.resolve(srcName).toString(); + + if( do_run || print_asm || print_size ) { + if (cpu == null) cpu = TestC.CPU_PORT; + if (abi == null) abi = TestC.CALL_CONVENTION; + do_codegen = true; + } + if( cpu == null ) cpu = TestC.CPU_PORT; + if( abi == null ) abi = TestC.CALL_CONVENTION; + + String simpleHome = System.getenv("SIMPLE_HOME"); + if( simpleHome != null && !simpleHome.isEmpty() ) + libPaths.add(Path.of(simpleHome,cpu+"_"+abi).toString()); + + // Read input file + try { + src = input_filename.isEmpty() ? getInput() : Files.readString(Path.of(input_filename)); + } catch( IOException e ) { throw bad("Cannot read input file: "+input_filename); } + + // Compilation pipeline + Ary<String> externPaths = libPaths.isEmpty() ? null : new Ary<>(libPaths.toArray(new String[0])); + CodeGen code = new CodeGen(modPath.toString(), outPath.toString(), externPaths, srcName, src, 456, TypeInteger.BOT ); + code.driver(Phase.LastPhase,cpu,abi, false, do_run || do_eval, dump); + + if( do_codegen && print_asm ) + System.out.println(code.asm()); + + if( print_size ) + System.out.printf( "Code Size: %d%n", code._encoding._bits.size()); + + if( print_time ) + print_compilation_times(code); + + if( do_eval ) { + // TODO: Support for evaluation of functions with different argument numbers and types + long t = System.currentTimeMillis(); + long arg = (i+1 < args.length) ? Integer.parseInt(args[i+1]) : 0; + System.out.println(Eval2.eval(code, arg, 100000)); + if( print_time ) + System.out.printf( "EXECUTION TIME: %.3f sec%n", (System.currentTimeMillis() - t) / 1e3); + } + if( do_run ) { + if ( !TestC.CPU_PORT.equals( cpu ) || !TestC.CALL_CONVENTION.equals( abi ) ) + throw bad("cannot run code on not native target"); + String exe = TestC.OS.startsWith("Windows") ? outBase+".exe" : outBase; + String result = TestC.gcc(outBase+".o", null, null, true, exe); + System.out.print(result); + } + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TestArm64.java b/chapter25/src/test/java/com/seaofnodes/simple/TestArm64.java new file mode 100644 index 00000000..7394b578 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/TestArm64.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.util.BAOS; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +// Runs 32-bit ARM code in an emulator + +public class TestArm64 { + + public static EvalArm64 build( String main, String src, int arg, int spills, boolean print ) throws IOException { + // Compile and export Simple + CodeGen code = new CodeGen(src).driver("arm", "SystemV",true,false); + + // Allocation quality not degraded + int delta = spills>>3; + if( delta == 0 ) delta =1; + if( spills != -1 ) assertEquals("Expect spills:", spills, code._regAlloc._spillScaled, delta); + + // Image + byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes) + EvalArm64 ARM = new EvalArm64(image, 1<<16); + int start = 0; // Code at offset 0 + int cpool = fill(code._encoding._bits ,image,start); + int sdata = fill(code._encoding._cpool,image,cpool); + int end = fill(code._encoding._sdata,image,sdata); + + // Initial incoming int arg + ARM.regs[arm.X0] = arg; + // malloc memory starts at 64K and goes up + + // Look up starting PC or zero if not given + if( main!=null ) + for( FunNode fun : code._linker ) + if( fun != null && !fun.isDead() && fun._compunit == code.compunit() && main.equals(fun._name) ) + ARM._pc = code._encoding.opStart(fun); + + return ARM; + } + + // Fill bits into image; return end point rounded up. + private static int fill( BAOS bits, byte[] image, int off) { + System.arraycopy(bits.buf(), 0, image, off, bits.size()); + return (off+bits.size() + 15) & -16; + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TestC.java b/chapter25/src/test/java/com/seaofnodes/simple/TestC.java new file mode 100644 index 00000000..5c49d3a8 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/TestC.java @@ -0,0 +1,209 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.util.Utils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.concurrent.TimeUnit; +import static org.junit.Assert.*; + +// Call a C driver program, which calls a Simple-generated .o, and self-checks. +// Expects GCC from the default CLI, compiles C, compiles Simple, links and runs. +public abstract class TestC { + + public static final String OS = System.getProperty("os.name"); + public static final String CPU = System.getProperty("os.arch"); + + public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "win64" : "SystemV"; + public static final String CPU_PORT = switch( CPU ) { + case "amd64" -> "x86_64_v2"; + default -> throw Utils.TODO("Map Yer CPU Port Here"); + }; + public static final String CPU_ABI = CPU_PORT+"_"+CALL_CONVENTION; + public static final String RELEASE_SYS_DIR = "build/release/"+CPU_ABI; + + public static final String C_DRIVERS_DIR = "src/test/java/com/seaofnodes/simple/progs/"; + + /** + * Compile, link and run + * - WITH a C driver + * - using the default OS/CPU calling convention. + * - no lib sys + */ + public static void runC( String src, String base, String expected, int spills) throws IOException { + String cfile = C_DRIVERS_DIR+base+".c"; + run(src, base, null, CALL_CONVENTION, "", cfile, expected, spills); + } + + /** + * Compile, link and run + * - withOUT a C driver + * - using the default OS/CPU calling convention. + * - no lib sys + */ + public static void runSF( String src, String base, String expected, int spills ) throws IOException { + run(src, base, null, CALL_CONVENTION, null,null, expected,spills); + } + + /** + * Compile, link and run + * - withOUT a C driver + * - using the default OS/CPU calling convention. + * - WITH a test lib sys + */ + public static void runSYS( String src, String base, String expected, int spills ) throws IOException { + Ary<String> externPaths = new Ary<>(new String[]{RELEASE_SYS_DIR}); + run(src, base, externPaths, CALL_CONVENTION, null,null, expected,spills); + } + + + /** + * Compile, link and run - general case + * + * @param src Program source to compile + * @param base Compiled binary final base name + * @param externPaths External symbol search path + * @param simple_conv Argument convention when calling Simple code + * @param c_conv Argument convention when calling C code, or null if not + * linking against a C driver program + * @param cfile Associated C program which will drive the Simple program, + * or null if not linking against a C driver program + * @param expected Expected stdout string + * @param spills Expected limit on spill code, used to validate the + * register allocation is reasonable + */ + public static void run( String src, String base, Ary<String> externPaths, String simple_conv, String c_conv, String cfile, String expected, int spills ) throws IOException { + run(src, base, externPaths, simple_conv, c_conv, cfile, null, expected, spills); + } + + public static void run( String src, String base, Ary<String> externPaths, String simple_conv, String c_conv, String cfile, String stdin, String expected, int spills ) throws IOException { + // Simple file base-name example: + // foo.smp -> + // build/objs/foo.o - object file + // build/objs/foo.exe - linked executable Windows + // build/objs/foo - linked executable Linux + String pathBase = "build/objs/"+base; + String obj = pathBase+".o"; + String exe = pathBase+(OS.startsWith("Windows") ? ".exe" : ""); + // Compile simple, emit ELF + CodeGen code = new CodeGen(null,"build/objs",externPaths,base,src,126L,TypeInteger.BOT); + code.driver( CPU_PORT, simple_conv, false, cfile==null ); + + String result = gcc(obj, c_conv, cfile, stdin, linkObjs(externPaths), exe ); + assertEquals(expected,result); + + // Allocation quality not degraded + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + } + + // Link with gcc, and execute the resulting binary, returning stdout as a + // String. Any errors *assert* instead of returning some error code; this + // utility is meant to execute inside a JUnit test which will catch the + // assert and flag the test as failed. + public static String gcc( String main, double ignore, String... objs ) throws IOException { + String BLDDIR = "build/objs/test0/"; + String exe = BLDDIR+main+(OS.startsWith("Windows") ? ".exe" : ""); + return gcc(objs[0],null,null,false,exe); + } + + // Link with gcc, and execute the resulting binary, returning stdout as a + // String. Any errors *assert* instead of returning some error code; this + // utility is meant to execute inside a JUnit test which will catch the + // assert and flag the test as failed. + public static String gcc( String obj, String c_conv, String cfile, boolean stdin, String... args ) throws IOException { + return gcc(obj,c_conv,cfile,stdin ? "" : null,null,args); + } + + public static String gcc( String obj, String c_conv, String cfile, String stdin, String... args ) throws IOException { + return gcc(obj,c_conv,cfile,stdin,null,args); + } + + public static String gcc( String obj, String c_conv, String cfile, String stdin, Ary<String> linkObjs, String... args ) throws IOException { + + // Compile the C program. Compiling code and constants in the low + // 2Gig. Pointers are 64b BUT since always in the low 2G all the + // high bits are zero - and Simple code can be emitted treating + // pointers as 4bytes. + var params = new Ary<>(String.class); + params.add("gcc"); + if( cfile!=null ) params.add(cfile); // Associated C driver, usually has a `main` + params.add(obj); + if( linkObjs != null ) + params.addAll(linkObjs.asAry()); + params.addAll(new String[] { + "-lm", // Picks up 'sqrt' for newtonFloat tests to compare + "-g", + "-o", + args[0], + }); + // Calling convention for C calls, if any + if( cfile!=null ) { + params.add("-D"); + params.add("CALL_CONV="+c_conv); + } + + // Run GCC to link (optionally compile C driver code) + Process gcc = new ProcessBuilder(params.asAry()).redirectErrorStream(true).start(); + int exit; + try { + boolean normal = gcc.waitFor(2, TimeUnit.SECONDS); + exit = normal ? gcc.exitValue() : -1; // no exit??? + } catch( InterruptedException e ) { + throw new IOException("interrupted"); + } + String result = new String(gcc.getInputStream().readAllBytes()); + if( exit!=0 ) { + System.err.println("gcc error code: "+exit); + System.err.println(result); + } + assertEquals( 0, exit ); + //assertTrue(result.isEmpty()); // No data in error stream + + // Execute results + ProcessBuilder smp = new ProcessBuilder(args); + File stdinFile = null; + File stdoutFile = File.createTempFile("simple-stdout",".txt"); + smp.redirectOutput(stdoutFile); + if( stdin!=null && stdin.isEmpty() ) smp.redirectInput(ProcessBuilder.Redirect.INHERIT); + else if( stdin!=null ) { + stdinFile = File.createTempFile("simple-stdin",".txt"); + Files.writeString(stdinFile.toPath(),stdin); + smp.redirectInput(stdinFile); + } + Process p = smp.start(); + if( stdin==null ) + p.getOutputStream().close(); + try { exit = (byte)p.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + result = Files.readString(stdoutFile.toPath()); + if( stdinFile != null ) + stdinFile.delete(); + stdoutFile.delete(); + if( exit!=0 ) + return "exec exit code: "+exit; + return result; + } + + private static Ary<String> linkObjs(Ary<String> externPaths) { + if( externPaths == null ) + return null; + Ary<String> objs = new Ary<>(String.class); + for( String path : externPaths ) { + File file = new File(path); + if( file.isFile() && file.getName().endsWith(".o") ) + objs.add(file.toString()); + else if( file.isDirectory() ) { + File sys = new File(file,"sys.o"); + if( sys.isFile() ) + objs.add(sys.toString()); + } + } + return objs.isEmpty() ? null : objs; + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TestRisc5.java b/chapter25/src/test/java/com/seaofnodes/simple/TestRisc5.java new file mode 100644 index 00000000..aff95c38 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/TestRisc5.java @@ -0,0 +1,52 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import com.seaofnodes.simple.util.BAOS; +import java.io.IOException; + +import static org.junit.Assert.*; + +// Runs 32-bit R5 code in an emulator +public abstract class TestRisc5 { + + public static EvalRisc5 build( String src, String main, int arg, int spills, boolean print ) throws IOException { + // Compile and export Simple + CodeGen code = new CodeGen(src).driver("riscv", "SystemV",true,false); + + // Allocation quality not degraded + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + + // Image + byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes) + EvalRisc5 R5 = new EvalRisc5(image, 1<<16); + int start = 0; // Code at offset 0 + int cpool = fill(code._encoding._bits ,image,start); + int sdata = fill(code._encoding._cpool,image,cpool); + int end = fill(code._encoding._sdata,image,sdata); + + // Initial incoming int arg + R5.regs[riscv.A0] = arg; + // malloc memory starts at 64K and goes up + + // Look up starting PC or zero if not given + if( main!=null ) + for( FunNode fun : code._linker ) + if( fun != null && !fun.isDead() && fun._compunit == code.compunit() && main.equals(fun._name) ) + R5._pc = code._encoding.opStart(fun); + + return R5; + } + + // Fill bits into image; return end point rounded up. + private static int fill( BAOS bits, byte[] image, int off) { + System.arraycopy(bits.buf(), 0, image, off, bits.size()); + return (off+bits.size() + 15) & -16; + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java new file mode 100644 index 00000000..e881a2bf --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java @@ -0,0 +1,214 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.type.*; +import org.junit.Assert; +import org.junit.Test; +import static org.junit.Assert.assertSame; + +public class TypeTest { + + // Test basic properties and GLB + @Test + public void testTypeAdHoc() { + Assert.assertEquals( TypeScalar.BOT, TypeInteger.TRUE.meet(TypeNil.NIL) ); + Assert.assertEquals( TypeScalar.BOT, TypeInteger.TOP .meet(TypeNil.NIL) ); + + TypeStruct s1 = TypeStruct.make("s1",false, + Field.make("a", TypeInteger.BOT,-1, false), + Field.make("b", TypeInteger.BOT,-2, false) ); + TypeStruct s2 = TypeStruct.make("s2",false, + Field.make("a", TypeInteger.BOT,-3, false), + Field.make("b", TypeInteger.BOT,-4, false) ); + TypeStruct x1ro = (TypeStruct)s1.makeRO(); + TypeMemPtr p1 = TypeMemPtr.make(s1); + // GLB no longer forces RO + //Assert.assertEquals(x1ro, ((TypeMemPtr)p1.glb(false))._obj); + Assert.assertNotEquals(s1, s1.dual()); + TypeStruct s1dglb = ((TypeMemPtr)p1.dual().glb(false))._obj; + Assert.assertTrue(x1ro.isa(s1dglb)); + + TypeMem m1 = TypeMem.make(2,TypeNil.NIL); + TypeMem m2 = TypeMem.make(3,TypeInteger.U16); + TypeMem m3 = TypeMem.make(4,TypeFloat.F64); + TypeMem m4 = TypeMem.make(5,TypeInteger.BOT); + + Assert.assertNotEquals(m1, m2); + Assert.assertNotEquals(m2, m3); + Assert.assertNotEquals(m3, m4); + + Assert.assertEquals(TypeStruct.BOT, s1.meet(s2)); + TypeMem scalarMemBot = TypeMem.make(1,TypeScalar.BOT,false,false,false,XInt.FULL,XInt.FULL); + Assert.assertEquals(scalarMemBot, ((TypeMem)m1.meet(m2)).makeFrom( XInt.FULL, XInt.FULL)); + Assert.assertEquals(TypeMem.make(1,TypeScalar.BOT), m1.meet(m3)); + Assert.assertEquals(scalarMemBot, ((TypeMem)m3.meet(m4)).makeFrom( XInt.FULL, XInt.FULL)); + + Assert.assertEquals(TypeMem.make(2,Type.BOTTOM), m1.glb(false)); + Assert.assertEquals(TypeMem.make(2,Type.XNIL,true,true,true, XInt.FULL, XInt.FULL), m1.dual()); + Assert.assertEquals(m4.dual(), m4.glb(false).dual()); + + TypeMemPtr ptr1 = TypeMemPtr.make(s1); + Assert.assertEquals(s1, ptr1._obj); + TypeMemPtr ptr2 = TypeMemPtr.make(s2); + Assert.assertEquals(s2, ptr2._obj); + + TypeMemPtr ptr1nil = TypeMemPtr.makeNullable(s1); + Assert.assertEquals(s1, ptr1nil._obj); + Assert.assertTrue( ptr1nil.nullable() ); + Assert.assertNotNull( ptr1nil._obj ); + TypeMemPtr ptr2nil = TypeMemPtr.makeNullable( s2); + Assert.assertEquals(s2, ptr2nil._obj); + + Assert.assertNotEquals(ptr1, ptr2); + Type p1glb = ptr1.glb(false); + // GLB no longer forces RO + //Assert.assertNotEquals(ptr1, p1glb ); + Type p1nro = ptr1nil.makeRO(); + // GLB no longer forces RO + //Assert.assertEquals(p1nro, p1glb); + + Assert.assertEquals(ptr1, ptr1.dual().dual()); + Assert.assertTrue(p1glb.makeRO().isa( ptr1.dual().glb(false))); + Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT), ptr1.meet(ptr2nil)); + // GLB no longer forces RO + //Assert.assertEquals(p1glb, ptr1.meet(TypeNil.NIL).makeRO()); + + TypeMemPtr TOP = TypeMemPtr.TOP; + TypeMemPtr BOT = TypeMemPtr.makeNullable(TypeStruct.BOT); + TypeMemPtr PTR = TypeMemPtr.make(TypeStruct.BOT); + Type NULL = TypeNil.NIL; + Type PTR_meet_NULL = NULL.meet(PTR); + Assert.assertEquals(BOT, PTR_meet_NULL); + Type ptr1_meet_ptr2 = ptr1.meet(ptr2); + Assert.assertEquals(PTR, ptr1_meet_ptr2); + Type NULL_join_ptr1 = NULL.join(ptr1); + Assert.assertEquals(TypePtr.XNPTR, NULL_join_ptr1); + Type NULL_join_PTR = PTR.join(NULL); + Assert.assertEquals(TypePtr.XNPTR, NULL_join_PTR); + Type ptr1_dual = ptr1.dual(); + Type nullableptr1_dual = ptr1nil.dual(); + + // Cyclic check + TypeStruct S1 = ((TypeMemPtr)TypeStruct.SFLT1.field("s1")._t)._obj.close(); + Assert.assertFalse(s1.isFinal()); + Type s1ro = S1.makeRO(); + Type s1ro2 = S1.makeRO(); + Assert.assertSame(s1ro,s1ro2); + Assert.assertTrue(s1ro.isFinal()); + + Assert.assertFalse(S1.isConstant()); + Type s1glb = S1.field("s2")._t.glb(false); + Type s1glb2 = S1.field("s2")._t.glb(false); + Assert.assertSame(s1glb,s1glb2); + } + + // Test theoretical properties. + // This is a symmetric complete bounded (ranked) lattice. + // Also, the meet is commutative and associative. + // The lattice has a dual (symmetric), and join is ~(~x meet ~y). + // See https://en.wikipedia.org/wiki/Lattice_(order). + @Test + public void testLatticeTheory() { + Type[] ts = Type.gather(); + + // Confirm commutative & complete + for( Type t0 : ts ) + for( Type t1 : ts ) { + check_commute (t0,t1); + check_symmetric(t0,t1); + } + + // Confirm associative + for( Type t0 : ts ) + for( Type t1 : ts ) + for( Type t2 : ts ) + assoc(t0,t1,t2); + + // Confirm symmetry. If A isa B, then A.join(C) isa B.join(C) + for( Type t0 : ts ) + for( Type t1 : ts ) + if( t0.isa(t1) ) + for( Type t2 : ts ) { + Type t02 = t0.join(t2); + Type t12 = t1.join(t2); + Type mt = t02.meet(t12); + assertSame(t0+" isa "+t1+" joined "+t2, mt,t12); + } + } + + // By design in meet, args are already flipped to order _type, which forces + // symmetry for things with badly ordered _type fields. The question is + // still interesting for other orders. + private static void check_commute( Type t0, Type t1 ) { + if( t0==t1 ) return; + if( t0.is_simple() && !t1.is_simple() ) return; // By design, flipped the only allowed order + Type mta = t0.meet(t1); + Type mtb = t1.meet(t0); // Reverse args and try again + assertSame(mta,mtb); + } + + // A & B = MT + // Expect: ~A & ~MT == ~A + // Expect: ~B & ~MT == ~B + private static void check_symmetric( Type t0, Type t1 ) { + if( t1==t0 ) return; + Type mt = t0.meet(t1); + Type dm = mt.dual(); + Type d0 = t0.dual(); + Type d1 = t1.dual(); + Type ta = dm.meet(d1); + Type tb = dm.meet(d0); + assertSame(t0+" & "+t1+" mt="+mt, ta,d1); + assertSame(t0+" & "+t1+" mt="+mt, tb,d0); + } + + private static void assoc( Type t0, Type t1, Type t2 ) { + Type t01 = t0 .meet(t1 ); + Type t12 = t1 .meet(t2 ); + Type t01_2 = t01.meet(t2 ); + Type t0_12 = t0 .meet(t12); + assertSame(t0+" & "+t1+" & "+t2, t01_2,t0_12); + } + + // Test cyclic types and meets + @Test + public void testCyclic0() { + Type d0 = TypeStruct.SFLT1.dual(); + Type d1 = d0.dual(); + assertSame(TypeStruct.SFLT1,d1); + } + + @Test + public void testGLB() { + Type[] ts = Type.gather(); + for( Type t0 : ts ) + if( !(t0 instanceof Field || t0 instanceof TypeStruct || t0 instanceof TypeTuple || t0 instanceof TypeConAry ) ) + Assert.assertTrue(t0.isa(t0.glb(false))); + } + + @Test + public void testList() { + TypeStruct list = TypeStruct.open("List"); + TypeMemPtr plist = TypeMemPtr.makeNullable(list); + list = list.add(Field.make("next", plist, 2, false) ); + list = list.add(Field.make("x", TypeInteger.BOT, 3, false)); + // Make a cyclic type + list = list.close(); + // Fields are mutable + Assert.assertFalse(list.isFinal()); + TypeStruct flist = (TypeStruct)list.makeRO(); + Assert.assertTrue(flist.isFinal()); + Assert.assertNotSame( list, flist ); + } + + @Test + public void testOpen() { + TypeStruct open = TypeStruct.make("List",false); + TypeMemPtr plist = TypeMemPtr.makeNullable(open); + TypeStruct list = open.add(Field.make("next", plist, 2, false) ); + list = list.add(Field.make("x", TypeInteger.BOT, 3, false)); + // Make a cyclic type + list = list.close(); + Type mt = open.meet(list); + Assert.assertEquals(list,mt); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java new file mode 100644 index 00000000..c1ffa6c1 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -0,0 +1,174 @@ +package com.seaofnodes.simple.fuzzer; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Eval2; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.Node; +import java.util.ArrayList; +import java.util.Objects; +import java.util.Random; +import java.util.function.Consumer; + +/** + * Implementation of the fuzzer. + * Random testcases are generated using the ScriptGenerator. + * This will generate a script similar to how the parser parses it but instead of parsing it will + * randomly choose a variation the parser would parse and generate it. + * These scripts are the parsed by the parser and all exceptions are caught and filtered + * to only show one occurrence of one problem. This includes script which generate different + * results when the compiled graph with and without peeps is executed. + * To aid debugging scripts that cause errors are then reduced by applying rules and checking + * that the same issue persists. + */ +public class Fuzzer { + + private static final int EVAL_TIMEOUT = 10000; + + /** + * List of exceptions already encountered. + * This is used to filter new exceptions and discard them if they are already found. + */ + private final ArrayList<Throwable> exceptions = new ArrayList<>(); + + /** + * Filter and record an exception in a script. + * @param e The exception caused by the script + * @param script The script causing the exception + * @param reproducer Reproducer which should be used to reproduce the test case. + */ + private void recordException(Throwable e, String script, Consumer<String> reproducer, long seed) { + for (var ex : exceptions) { + if (FuzzerUtils.isExceptionFromSameCause(ex, e)) return; + } + exceptions.add(e); + System.out.println("========== Stack =========="); + e.printStackTrace(System.out); + System.out.println("========== Seed =========="); + System.out.println(seed); + //System.out.println("========== Code ==========="); + //System.out.println(script); + System.out.println("========= Reduced ========="); + System.out.println(Reducer.reduce(script, e, reproducer)); + System.out.println("==========================="); + System.out.flush(); + } + + private static boolean neq(Object a, Object b) { + if (Objects.equals(a, b)) return false; + return true; + } + + /** + * Check that two graphs result in the same output when supplied with a value + * @param e1 Evaluator for the first graph + * @param e2 Evaluator for the second graph + * @param in The input value to both graphs to test for an equal output + */ + private static void checkGraphs(CodeGen code1, CodeGen code2, long in) { + var r1 = Eval2.eval(code1, in, EVAL_TIMEOUT); + var r2 = Eval2.eval(code2, in, EVAL_TIMEOUT); + if( r1 == null || r2 == null ) return; // Timeout test invalid + if( neq(r1, r2) ) + throw new RuntimeException("Different calculations values " + r1 + " vs " + r2); + } + + /** + * Run checks for script. Compile the script with peeps enabled and disabled. + * Check that exceptions raised in the parser by both methods are the same and only happens if the script may be invalid. + * If the script was successfully parsed check that both version behave the same. + * @param script The script to test + * @param valid If the script is definitely valid. If not some exceptions may be suppressed. + */ + private static void runCheck(String script, boolean valid) { + CodeGen code1; + try { + code1 = FuzzerUtils.parse(script, 123); + } catch (RuntimeException e1) { + try { + FuzzerUtils.parse(script, 456); + } catch (RuntimeException e2) { + if (FuzzerUtils.isExceptionFromSameCause(e1, e2)) { + if (!valid || e1.getClass() == Parser.ParseException.class) return; + } else { + e1.addSuppressed(e2); + } + } + throw e1; + } + CodeGen code2 = FuzzerUtils.parse(script, 456); + checkGraphs(code1, code2, 0); + checkGraphs(code1, code2, 1); + checkGraphs(code1, code2, 10); + } + + /** + * Check that the script does not result in exceptions. + * @param script The script to test. + * @param valid If the script is valid or if it might contain syntax errors. + */ + private void check(String script, boolean valid, long seed) { + try { + runCheck(script, valid); + } catch (Throwable e) { + recordException(e, script, s->runCheck(s, valid), seed); + } + } + + /** + * Run one test with the given seed, and check for peephole counts. + * @param seed The seed to use for generating this test case + */ + public int fuzzPeepTiming(long seed, int max_nids) { + var rand = new Random(seed); + var sb = new StringBuilder(); + new ScriptGenerator(rand, sb, true).genProgram(); + try { + var code = new CodeGen(sb.toString()); + + code.parse(); + int nids = code.UID(); + if( nids <= max_nids ) return max_nids; // Smaller, looking for trends as we grow + int parse_peeps= code._iter_cnt; + int parse_nops = code._iter_nop_cnt; + double parse_nop_ratio = (double)parse_nops/parse_peeps; + double parse_peeps_per_node = (double)parse_peeps/nids; + + code.opto(); + int iter_peeps= code._iter_cnt; + int iter_nops = code._iter_nop_cnt; + double iter_nop_ratio = (double)iter_nops/iter_peeps; + double iter_peeps_per_node = (double)iter_peeps/nids; + + System.out.printf("%6d | Parsing: peeps: %5d, nops: %5d, ratio: %5.3f, last UID: %5d ps/node: %5.3f | Iter: peeps: %6d, nops: %6d, ratio: %5.3f, ps/node: %5.3f\n", + seed, + parse_peeps,parse_nops,parse_nop_ratio,nids,parse_peeps_per_node, + iter_peeps , iter_nops, iter_nop_ratio, iter_peeps_per_node); + return nids; + } catch (Throwable e) { + // ignore exceptions, let the normal fullPeeps catch and reduce, + // we're interested in valid program optimizations + } + return max_nids; + } + + + /** + * Run one test with the given seed. + * @param seed The seed to use for generating this test case + */ + public void fuzzPeeps(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + check(sb.toString(), valid, seed); + } + + + /** + * Check that no exceptions happened. + * @return true if no exception happened + */ + public boolean noExceptions() { + return exceptions.isEmpty(); + } +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java new file mode 100644 index 00000000..9175e380 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java @@ -0,0 +1,111 @@ +package com.seaofnodes.simple.fuzzer; + +import com.seaofnodes.simple.IterPeeps; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.StopNode; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.util.BitSet; + +/** + * Some utilities for the fuzzer. + */ +class FuzzerUtils { + + /** + * Get the val of a private field + */ + @SuppressWarnings("unchecked") + public static <T> T getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + return (T) (obj instanceof Class<?> clz ? getField(clz, name).get(null) : getField(obj.getClass(), name).get(obj)); + } + + /** + * Get access to a private field + */ + public static Field getField(Class<?> clz, String name) throws NoSuchFieldException { + var field = clz.getDeclaredField(name); + try { + field.setAccessible(true); + } catch (Throwable e) { + // Intentionally left empty + } + return field; + } + + /** + * Try to rethrow an exception or create a runtime exception and throw it. + */ + public static RuntimeException rethrow(Throwable e) { + if (e instanceof RuntimeException re) return re; + if (e instanceof Error er) throw er; + return new RuntimeException(e); + } + + /** + * Some problems might trigger debug output. Suppress this output with a null print stream during fuzzing. + */ + public static final PrintStream NULL_PRINT_STREAM = new PrintStream(OutputStream.nullOutputStream()); + + /** + * Check that the messages are similar. + */ + private static boolean similarMessages(String a, String b) { + if (a == null || b == null) return true; + a = a.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized"); + b = b.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized"); + return a.equals(b); + } + + /** + * Check if both stack traces have the same first entry. Empty stack traces can happen with NPE optimization + * and allow the other to be any stack trace. + */ + private static boolean sameStackTraceOrigin(StackTraceElement[] a, StackTraceElement[] b) { + if (a == null || a.length == 0 || b == null || b.length == 0) return true; // One or both stack traces are missing. + return a[0].equals(b[0]); + } + + /** + * Checks if two exceptions are caused by the same problem. + */ + public static boolean isExceptionFromSameCause(Throwable a, Throwable b) { + if (a == null || b == null) return a == b; // Handle the null case + if (a.getClass() != b.getClass()) return false; // Different classes + if (!similarMessages(a.getMessage(), b.getMessage())) return false; // Different messages. Null is allowed as wildcard + var s1 = a.getStackTrace(); + var s2 = b.getStackTrace(); + if (!sameStackTraceOrigin(s1, s2)) return false; // Different stack trace origins + if (!isExceptionFromSameCause(a.getCause(), b.getCause())) return false; // Different causes + var o1 = a.getSuppressed(); + var o2 = b.getSuppressed(); + if (o1 == null || o2 == null || o1.length != o2.length) return o1 == o2; // Different suppression + for (int i=0; i<o1.length; i++) { + if (!isExceptionFromSameCause(o1[i], o2[i])) return false; // Different suppression + } + return true; + } + + /** + * Parse script with peepholes enabled or disabled + */ + public static CodeGen parse(String script, long workListSeed) { + var err = System.err; + var out = System.out; + try { + System.setErr(NULL_PRINT_STREAM); + System.setOut(NULL_PRINT_STREAM); + var code = new CodeGen(script, workListSeed); + return code.driver(CodeGen.Phase.LocalSched); + } finally { + System.setErr(err); + System.setOut(out); + } + } + +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java new file mode 100644 index 00000000..f6e12e11 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java @@ -0,0 +1,177 @@ +package com.seaofnodes.simple.fuzzer; + +import java.util.Arrays; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class is used to reduce test cases created by the fuzzer. + * It applies rewrite rules to the test case and checks that the + * test still produces the same exception. + * This is done in a fixpoint iteration until every rewrite produces + * no or a different exception. + */ +public class Reducer { + + private interface Replacer extends BiConsumer<StringBuilder, Matcher> {} + + /** + * Holder for the rewrite rules + */ + private record Replace(Pattern pattern, boolean iterate, Replacer... replacements) { + Replace(Pattern p, Object... replacements) { + this(p, replacements[0] == Boolean.TRUE, Arrays.stream(replacements).filter(o->!(o instanceof Boolean)).map(Replace::from).toArray(Replacer[]::new)); + } + private static Replacer from(Object o) { + if (o instanceof Replacer r) return r; + if (o instanceof Integer i) return (s, m)->s.append(m.group(i)); + if (o instanceof String r) return (s, m)->s.append(r); + throw new IllegalArgumentException(); + } + } + + /** + * List of rewrite rules. + */ + private static final Replace[] REPLACEMENTS = { + new Replace(Pattern.compile("\\b(?:[^\\W0]|\\w\\w+)\\b"), "0", "1"), + new Replace(Pattern.compile("(?<=\\n|^)[^\\n]+(?=\\n|$)"), true, ""), + new Replace(Pattern.compile("(?<=[+\\-*/<>=(])--"), ""), + new Replace(Pattern.compile("\\b--"), "+"), + new Replace(Pattern.compile("[-!]"), ""), + new Replace(Pattern.compile("\\b\\+-"), "-"), + new Replace(Pattern.compile("(?<![+\\-*/<>=&|])(?:-|\\*|/|>=|<=|>>>|>>|>|<<|<|!=|==|&&|\\|\\||&|\\||^)(?![+*/<>=&|])"), "+"), + new Replace(Pattern.compile("\\?([^;]+):"), "+"), + new Replace(Pattern.compile("\\{((?:[^{}]|\\{})+)(?:\\n[ \\t]*)?}"), "", "{}", 1), + new Replace(Pattern.compile("(?<=\\n|^)([ \\t]*)\\{\\n(.*?)\\n\\1}", Pattern.DOTALL), 2), + new Replace(Pattern.compile("\\(([^()]+)\\)"), 1), + new Replace(Pattern.compile("\\d+ ?\\+ ?"), ""), + new Replace(Pattern.compile(" ?\\+ ?\\d+"), ""), + new Replace(Pattern.compile("\\n\\n+"), "\n"), + new Replace(Pattern.compile("^\\n+|\\n+$"), ""), + new Replace(Pattern.compile("else *"), ""), + new Replace(Pattern.compile("\\bif\\([^()]+\\) *"), ""), + new Replace(Pattern.compile("\\bwhile\\([^()]+\\) *"), ""), + new Replace(Pattern.compile("\\bstruct \\w+ \\{[^{}]*}"), "") + }; + + /** + * Predicate to test if the reduced script generates the same error. + */ + private final Predicate<String> tester; + + /** + * Create a reducer with a test function. + * @param tester Test function which returns true when the same issue is found in the script. + */ + private Reducer(Predicate<String> tester) { + this.tester = tester; + } + + /** + * Rewrites the script with one rewrite rule. + */ + private String doReplacement(String script, Pattern pattern, Replacer[] replacements) { + var matcher = pattern.matcher(script); + var sb = new StringBuilder(); + var last = 0; + var skip = 0; + outer: + while (matcher.find(last + skip)) { + sb.append(script, last, matcher.start()); + last = matcher.end(); + skip = last == matcher.start() ? 1 : 0; + var pos = sb.length(); + for (var replacement : replacements) { + replacement.accept(sb, matcher); + var tail = sb.length(); + sb.append(script, last, script.length()); + if (tester.test(sb.toString())) { + sb.setLength(tail); + continue outer; + } + sb.setLength(pos); + } + last = matcher.start(); + skip = 1; + } + sb.append(script, last, script.length()); + return sb.toString(); + } + + /** + * Clean up variable names from the random ones generated by the fuzzer. + */ + private String cleanVariables(String script) { + var matcher = Pattern.compile("\\b([0-9a-zA-Z_]+) !?([0-9a-zA-Z_]+)").matcher(script); + int[] num = {0, 0}; + while (matcher.find()) { + var type = matcher.group(1); + var var = matcher.group(2); + if (type.equals("new")) continue; + String prefix = "v"; + int t = 0; + if (type.equals("struct")) { + t = 1; + prefix = "s"; + } + if (var.matches(prefix+"\\d+")) continue; + while (script.contains(prefix+num[t])) num[t]++; + var n = script.replaceAll("\\b"+var+"\\b", prefix + num[t]); + if (tester.test(n)) { + script = n; + } + } + return script; + } + + /** + * Run all rewrite rules once. + */ + private String doAllReplacements(String script) { + for (var replace: REPLACEMENTS) { + String old; + do { + old = script; + script = doReplacement(script, replace.pattern, replace.replacements); + } while (replace.iterate && !old.equals(script)); + } + return script; + } + + /** + * Reduce the script to a minimal version which produces the same problem with a test function. + * @param tester Test function which returns true when the same issue is found in the script. + */ + public static String reduce(String script, Predicate<String> tester) { + var reducer = new Reducer(tester); + String old; + do { + old = script; + script = reducer.doAllReplacements(script); + script = reducer.cleanVariables(script); + } while (!script.equals(old)); + return script; + } + + /** + * Reduce the script to a minimal version which produces the same problem with a reproducer. + * @param ex The exception originally caused by the script + * @param reproducer Test function which throws an exception which will be checked against ex. + */ + public static String reduce(String script, Throwable ex, Consumer<String> reproducer) { + return reduce(script, s->{ + Throwable nex = null; + try { + reproducer.accept(s); + } catch (Throwable e) { + nex = e; + } + return FuzzerUtils.isExceptionFromSameCause(nex, ex); + }); + } + +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java new file mode 100644 index 00000000..dde5a1dc --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -0,0 +1,938 @@ +package com.seaofnodes.simple.fuzzer; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.node.ScopeNode; +import com.seaofnodes.simple.type.TypeInteger; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.function.Predicate; +import static com.seaofnodes.simple.Parser.KEYWORDS; + + +/** + * Generate a pseudo random script. + * This generator will generate the same script for two random number generators initialized with the same seed. + * To generate valid code it implements the parser but instead of parsing it emits the statements and expressions. + * It is guaranteed to terminate. + */ +public class ScriptGenerator { + + private static final int MAX_STATEMENTS_PER_BLOCK = 5; + private static final int MAX_NAME_LENGTH = 10; + private static final int MAX_BLOCK_DEPTH = 4; + private static final int MAX_EXPRESSION_DEPTH = 10; + + /** + * Number of spaces per indentation + */ + private static final int INDENTATION = 4; + /** + * Valid characters for identifiers. The last 10 are not valid for the first character. + */ + private static final String VAR_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"; + + /** + * Flag for a statement that it does not pass on control flow to the next statement. + */ + private static final int FLAG_STOP = 0x01; + /** + * Flag for a statement that it contains a final if statement without an else branch. + */ + private static final int FLAG_IF_WITHOUT_ELSE = 0x02; + + private static class Type { + final String name; + Type(String name) {this.name=name;} + boolean isa(Type other) { return this == other; } + } + + private static class TypeStruct extends Type { + record Field (String name, Type type, TypeStruct struct) {} + Field[] fields; + final TypeNullable nullable = new TypeNullable(this); + TypeStruct(String name) { + super(name); + } + boolean isa(Type other) { return this == other || other == nullable; } + } + + private static class TypeNullable extends Type { + + final Type base; + + TypeNullable(Type base) { + super(base.name+"?"); + this.base = base; + } + + } + + private static final Type TYPE_FLT = new Type("flt"); + + private static class TypeInt extends Type { + + TypeInt(String name) { + super(name); + } + + @Override + boolean isa(Type other) { + return super.isa(other) || other == TYPE_FLT || other instanceof TypeInt; + } + } + + private static final List<TypeInt> INTTYPES = new ArrayList<>(); + + static { + for( var e : Parser.TYPES.entrySet() ) { + if( e.getValue() instanceof TypeInteger ) { + INTTYPES.add(new TypeInt(e.getKey())); + } + } + } + + private static final Type TYPE_INT = INTTYPES.stream().filter(t->t.name.equals("int")).findAny().get(); + private static final Type TYPE_BOOL = INTTYPES.stream().filter(t->t.name.equals("bool")).findAny().get(); + + private static class Variable { + final String name; + final Type declared; + final Variable shadowing; + Type type; + boolean shadowed = false; + + Variable(String name, Type type, Variable var) { + this.name = name; + this.declared = type; + this.type = type; + this.shadowing = var; + } + } + + /** + * The random number generator used for random decisions. + */ + private final Random random; + /** + * Indentation depth. + */ + private int indentation = 0; + /** + * Output string builder + */ + private final StringBuilder sb; + /** + * Number of nested loops + */ + private int loopDepth = 0; + /** + * Current scope start in the variables list. + */ + private int currScopeStart = 0; + /** + * The depth of blocks allowed. + */ + private int depth = MAX_BLOCK_DEPTH; + /** + * Current variables in scope. + */ + private final ArrayList<Variable> variables = new ArrayList<>(); + /** + * All defined structs. + */ + private final ArrayList<TypeStruct> structs = new ArrayList<>(); + /** + * All forward declared structs + */ + private final ArrayList<TypeStruct> forwardStructs = new ArrayList<>(); + /** + * Allow to declare structs. + */ + private boolean allowStructs = true; + /** + * If this needs to generate valid scripts or is also allowed to generate invalid ones. + */ + private boolean generateValid; + /** + * If this script might be invalid. + */ + private boolean maybeInvalid = false; + + + /** + * + * @param random the random number generator. + * @param sb the script will be written to this string builder. + * @param generateValid if the script should be guaranteed valid or if it is allowed to contain bugs. + */ + public ScriptGenerator(Random random, StringBuilder sb, boolean generateValid) { + this.random = random; + this.sb = sb; + this.generateValid = generateValid; + } + + /** + * Random number with log distribution + * @param max The maximum value + * @return A random number in the range [0, max) with higher numbers being more uncommon. + */ + private int randLog(int max) { + max--; + int n = 1<<max; + var num = random.nextInt(n); + for (int i=max-1; i>=0; i--) { + if ((num & (1<<i))!=0) return max-i-1; + } + return max; + } + + /** + * Helper function to print indentation. + * @return sb for chaining + */ + private StringBuilder printIndentation() { + return sb.repeat(' ', indentation); + } + + /** + * Generate a random name for variables. This might be invalid when it is a keyword. + * @return The random name generated + */ + private StringBuilder getRandomName() { + int len = random.nextInt(MAX_NAME_LENGTH) + 1; + StringBuilder sb = new StringBuilder(len); + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()-10))); + for (int i=1; i<len; i++) + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); + return sb; + } + + /** + * Should invalid code be generated? + * @return True if invalid code should be generated + */ + private boolean generateInvalid() { + if (generateValid || random.nextInt(1000)>2) return false; + maybeInvalid = true; + return true; + } + + /** + * Lookup a variable by name + * @param name name of the variable + * @return The index of the variable in variables or -1 + */ + private int lookup(String name) { + for (int i=variables.size()-1; i>=0; i--) { + if (variables.get(i).name.equals(name)) return i; + } + return -1; + } + + /** + * Lookup a variable by name + * @param name name of the variable + * @return THe variable of null if not found + */ + private Variable lookupVar(String name) { + int idx = lookup(name); + return idx == -1 ? null : variables.get(idx); + } + + /** + * Add a variable to the current scope + * @param name The name of the variable + * @param type The declared type of the variable + */ + private void addVariable(String name, Type type) { + var old = lookupVar(name); + variables.add(new Variable(name, type, old)); + if (old != null) old.shadowed = true; + } + + /** + * Pop variables to level to + * @param to Level until which variables should be popped + */ + private void popVariables(int to) { + while (variables.size() > to) { + var v = variables.removeLast(); + if (v.shadowing != null) v.shadowing.shadowed = false; + } + } + + /** + * Get a random visible variable patching pred + * @param pred The predicate the variable needs to match + * @return A random visible variable matching pred or null if no variable mached pred. + */ + private Variable findVisibleVariablesMatching(Predicate<Variable> pred) { + int num=0; + for (var v:variables) { + if (!v.shadowed && pred.test(v)) num++; + } + if (num == 0) return null; + num = random.nextInt(num); + for (var v:variables) { + if (!v.shadowed && pred.test(v) && num-- == 0) return v; + } + throw new AssertionError(); + } + + /** + * Get a structure by name + * @param name Name of the structure + * @return The structure of null + */ + private TypeStruct getStruct(String name) { + for (var s:structs) { + if (s.name.equals(name)) return s; + } + for (var s:forwardStructs) if (s.name.equals(name)) return s; + return null; + } + + /** + * Generate a variable name. This might be new random name or a name from an outer scope. + * If the program is allowed to be invalid this can return keywords and already used variable names in the current scope. + * @return The variable name. + */ + private String getVarName() { + if (currScopeStart > 0 && random.nextInt(10) > 7) { + // Use a variable outside the current scope. + var v = variables.get(random.nextInt(currScopeStart)); + if (lookup(v.name) < currScopeStart) return v.name; + if (!generateValid) { + maybeInvalid = true; + return v.name; + } + } + // Generate a new random variable name + StringBuilder sb = getRandomName(); + var v = sb.toString(); + if (!generateValid && (KEYWORDS.contains(v) || lookup(v) >= currScopeStart)) { + maybeInvalid = true; + return v; + } + while (KEYWORDS.contains(v) || lookup(v) >= currScopeStart) { + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); + v = sb.toString(); + } + return v; + } + + private <T> T randFromList(List<T> list) { + return list.get(random.nextInt(list.size())); + } + + /** + * Get a random structure name + * @return A random structure name + */ + private String getStructName() { + if (!structs.isEmpty() && generateInvalid()) return randFromList(structs).name; + if (!forwardStructs.isEmpty() && generateInvalid()) return randFromList(forwardStructs).name; + + // Generate a new random struct name + StringBuilder sb = getRandomName(); + var v = sb.toString(); + if (!generateValid && (KEYWORDS.contains(v) || getStruct(v) != null)) { + maybeInvalid = true; + return v; + } + while (KEYWORDS.contains(v) || getStruct(v) != null) { + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); + v = sb.toString(); + } + return v; + } + + public TypeInt getIntType() { + return randFromList(INTTYPES); + } + + /** + * Get a random type + * @return A random type + */ + private Type getType(boolean allowForward) { + int t = random.nextInt(10); + if( t<5 || structs.isEmpty() ) return getIntType(); + if( t<7 ) return TYPE_FLT; + if (allowForward && random.nextInt(10)==0) { + int i=random.nextInt(forwardStructs.size()+1); + if (i>0) return forwardStructs.get(i-1).nullable; + var name = getStructName(); + var struct = new TypeStruct(name); + forwardStructs.add(struct); + return struct.nullable; + } + TypeStruct struct = randFromList(structs); + if (random.nextBoolean()) return struct.nullable; + return struct; + } + + private Type getType() { + return getType(false); + } + + /** + * Generates a program and writes it into the string builder supplied in the constructor. + * @return If the program is allowed to be invalid returns if something potentially invalid was generated. + */ + public boolean genProgram() { + var oldGenerateValid = generateValid; + if (!generateValid && random.nextBoolean()) generateValid = true; + this.maybeInvalid = false; + addVariable(ScopeNode.ARG0, TYPE_INT); + currScopeStart = variables.size(); + if ((genStatements() & FLAG_STOP) == 0) { + if (generateValid || random.nextInt(10)<7) { + genReturn(); + } else { + this.maybeInvalid = true; + } + } + generateValid = oldGenerateValid; + return !this.maybeInvalid; + } + + /** + * Generate a list of statements. + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the last statement generated + */ + public int genStatements() { + var num = random.nextInt(MAX_STATEMENTS_PER_BLOCK * (depth+1)); + for (int i=0; i<num; i++) { + printIndentation(); + var stop = genStatement(); + sb.append("\n"); + if ((stop & FLAG_STOP) != 0) return stop; + } + return 0; + } + + /** + * Generate a single statement + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement + */ + public int genStatement() { + return switch (random.nextInt(12)) { + case 0 -> allowStructs || generateInvalid() ? genStruct() : genAssignment(); + case 1 -> genBlock(); + case 2 -> genIf(); + case 3 -> genCountedLoop(); + case 4 -> genWhile(); + case 5, 6 -> genDecl(); + case 7, 8, 9 -> genAssignment(); + case 10 -> genNullCheck(); + default -> genExit(); + }; + } + + /** + * Generate a new structure + * @return 0 + */ + public int genStruct() { + int idx = random.nextInt(forwardStructs.size()+10); + TypeStruct struct; + if (idx < 10) { + var name = getStructName(); + struct = new TypeStruct(name); + forwardStructs.add(struct); + } else { + struct = forwardStructs.get(idx-10); + } + var fields = new TypeStruct.Field[generateInvalid() ? 0 : random.nextInt(10)]; + sb.append("struct ").append(struct.name).append(" {\n"); + indentation += INDENTATION; + for (int i=0; i<fields.length; i++) { + var fieldName = getRandomName(); + var type = getType(true); + printIndentation().append(type.name).append(" ").append(fieldName).append(";\n"); + fields[i] = new TypeStruct.Field(fieldName.toString(), type, struct); + } + indentation -= INDENTATION; + printIndentation().append("};"); + struct.fields = fields; + forwardStructs.remove(struct); + structs.add(struct); + return 0; + } + + /** + * Generate a statement for if, else and while blocks. + * This is special since it disallows declarations and prefers to generate blocks. + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement + */ + public int genStatementBlock() { + var oldAllowStructs = allowStructs; + allowStructs = false; + indentation += INDENTATION; + int res; + if (depth == 0) { + res = random.nextBoolean() ? genAssignment() : genExit(); + } else { + depth--; + res = switch (random.nextInt(12)) { + case 1, 2, 3, 4, 5 -> { + depth++; + indentation -= INDENTATION; + var ret = genBlock(); + indentation += INDENTATION; + depth--; + yield ret; + } + case 6 -> genIf(); + case 7 -> genCountedLoop(); + case 8 -> genWhile(); + case 9 -> genAssignment(); + case 10 -> genNullCheck(); + default -> genExit(); + }; + depth++; + } + indentation -= INDENTATION; + allowStructs = oldAllowStructs; + return res; + } + + /** + * Generate an exit. This can be return or break and continue when in a loop. + * @return FLAG_STOP + */ + public int genExit() { + if (loopDepth == 0 && generateValid) return genReturn(); + return switch (random.nextInt(7)) { + case 0,1,2 -> { + if (loopDepth == 0) maybeInvalid = true; + sb.append("continue;"); + yield FLAG_STOP; + } + case 3,4,5 -> { + if (loopDepth == 0) maybeInvalid = true; + sb.append("break;"); + yield FLAG_STOP; + } + default -> genReturn(); + }; + } + + /** + * Generate a block statement. After a certain depth only empty blocks are generated to ensure termination of the generator. + * @return flag FLAG_STOP for the generated statement + */ + public int genBlock() { + if (depth == 0) { + sb.append("{}"); + return 0; + } + var oldAllowStructs = allowStructs; + var oldCSS = currScopeStart; + allowStructs = false; + currScopeStart = variables.size(); + depth--; + sb.append("{\n"); + indentation += INDENTATION; + var stop = genStatements(); + indentation -= INDENTATION; + printIndentation().append("}"); + popVariables(currScopeStart); + depth++; + currScopeStart = oldCSS; + allowStructs = oldAllowStructs; + return stop & ~FLAG_IF_WITHOUT_ELSE; + } + + /** + * Generate an if statement with an optional else block. + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement + */ + public int genIf() { + sb.append("if("); + genExpression(TYPE_BOOL, true, true); + sb.append(") "); + var stop = genStatementBlock(); + if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) { + sb.append("\n"); + printIndentation().append("else "); + stop &= genStatementBlock(); + } else { + stop = FLAG_IF_WITHOUT_ELSE; + } + return stop; + } + + /** + * Generate a block with a null check + * if (nullable) { handle_nullable_as_non_nullable; } + */ + public int genNullCheck() { + var v = findVisibleVariablesMatching(va->va.declared instanceof TypeNullable); + if (v == null) return genIf(); + var n = (TypeNullable)v.declared; + boolean negate = random.nextBoolean(); + sb.append("if("); + if (negate) sb.append("!"); + sb.append(v.name).append(")"); + if (!negate) v.type = n.base; + var stop = genStatementBlock(); + v.type = n; + if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) { + sb.append("\n"); + printIndentation().append("else "); + if (negate) v.type = n.base; + stop &= genStatementBlock(); + v.type = n; + } else { + stop = FLAG_IF_WITHOUT_ELSE; + } + return stop; + } + + /** + * Generate a while loop. + * @return 0 + */ + public int genWhile() { + sb.append("while("); + genExpression(TYPE_BOOL, true, true); + sb.append(") "); + loopDepth++; + genStatementBlock(); + loopDepth--; + return 0; + } + + /** + * Generate a counted loop of the form + * <code> + * { + * int varname = start_expr; + * while (varname < end_expr) loop_body; + * } + * </code> + * @return 0 + */ + public int genCountedLoop() { + if (depth == 0) return genWhile(); + var oldCSS = currScopeStart; + currScopeStart = variables.size(); + sb.append("{\n"); + indentation += INDENTATION; + String name = getVarName(); + printIndentation().append("int ").append(name).append("="); + genExpression(TYPE_INT, true, false); + sb.append(";\n"); + addVariable(name, TYPE_INT); + printIndentation().append("while(").append(name).append("<"); + genShift(TYPE_INT, true, false, MAX_EXPRESSION_DEPTH); + sb.append(") {\n"); + indentation += INDENTATION; + depth--; + loopDepth++; + printIndentation().append(name).append("=").append(name).append("+"); + genExpression(TYPE_INT, true, false); + sb.append(";\n"); + genStatements(); + loopDepth--; + depth++; + indentation -= INDENTATION; + printIndentation().append("}\n"); + indentation -= INDENTATION; + printIndentation().append("}"); + popVariables(currScopeStart); + currScopeStart = oldCSS; + return 0; + } + + /** + * Generate a declaration statement. + * @return 0 + */ + public int genDecl() { + var type = getType(); + var name = getVarName(); + // Always make them mutable + sb.append(generateInvalid() ? getRandomName() : type.name).append(" !").append(name); + if (!(type instanceof TypeNullable) || random.nextBoolean()) { + sb.append("="); + genExpression(type, true, type==TYPE_BOOL); + } + sb.append(";"); + addVariable(name, type); + return 0; + } + + /** + * Generate an assignment statement. + * @return 0 + */ + public int genAssignment() { + Type type; + Type declared; + if (generateInvalid()) { + sb.append(getRandomName()); + type = getType(); + declared = type; + } else { + var v = findVisibleVariablesMatching(va->true); + if (v == null) return genDecl(); + sb.append(v.name); + type = v.type; + declared = v.declared; + } + if (generateInvalid()) { + var name = getRandomName(); + sb.append(".").append(name); + declared = getType(); + } else if (type instanceof TypeStruct s && s.fields.length > 0 && random.nextBoolean()) { + var field = s.fields[random.nextInt(s.fields.length)]; + sb.append(".").append(field.name); + declared = field.type; + } + sb.append("="); + genExpression(declared, true, declared==TYPE_BOOL); + sb.append(";"); + return 0; + } + + /** + * Generate a return statement. + * @return FLAG_STOP + */ + public int genReturn() { + var type = getType(); + sb.append("return "); + genExpression(type, true, false); + sb.append(";"); + return FLAG_STOP; + } + + public void genExpression(Type type, boolean change, boolean preferLogical) { + genExpression(type, change, preferLogical, MAX_EXPRESSION_DEPTH); + } + + public void genExpression(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + if (d > 0 && random.nextInt(10)==0) { + genLogical(TYPE_INT, true, true, d); + sb.append("?"); + genExpression(type, true, preferLogical, d-1); + sb.append(":"); + genExpression(type, false, preferLogical, d-1); + } else { + genLogical(type, false, preferLogical, d); + } + } + + public void genLogical(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + genBitwise(type, false, preferLogical, d); + while (d-->0 && random.nextInt(preferLogical?5:50) == 0) { + sb.append(random.nextBoolean()?"&&":"||"); + genBitwise(type, true, preferLogical, d); + } + } + + public void genBitwise(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + genComparison(type, false, preferLogical, d); + while (type instanceof TypeInt && d-->0 && random.nextInt(preferLogical?50:5)==0) { + sb.append("&|^".charAt(random.nextInt(3))); + genComparison(type, true, preferLogical, d); + } + } + + public void genComparison(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + if (d > 0 && type instanceof TypeInt && random.nextInt(preferLogical?5:50)==0) { + type = getType(); + genComparison(type, true, preferLogical, d-1); + sb.append(random.nextBoolean()?"==":"!="); + } + genChaining(type, false, preferLogical, d); + } + + public void genChaining(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + if (d>0 && type instanceof TypeInt && random.nextInt(preferLogical?5:50)==0) { + genShift(TYPE_INT, true, false, d); + char c = random.nextBoolean()?'>':'<'; + sb.append(c); + if (random.nextInt(4)==0) sb.append('='); + genShift(TYPE_INT, true, false, --d); + while (d-->0 && random.nextInt(preferLogical?5:50)==0) { + if (generateInvalid()) c = c=='>' ? '<' : '>'; + sb.append(c); + if (random.nextInt(4)==0) sb.append('='); + genShift(TYPE_INT, true, false, d); + } + } else { + genShift(type, false, preferLogical, d); + } + } + + private static final String[] SHIFTS = {">>", ">>>", "<<"}; + + public void genShift(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + genAddition(type, false, preferLogical, d); + while (d-- > 0 && type instanceof TypeInt && random.nextInt(50)==0) { + sb.append(SHIFTS[random.nextInt(3)]); + genAddition(type, true, preferLogical, d); + } + } + + public void genAddition(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + genMultiplication(type, false, preferLogical, d); + while (d-- > 0 && (type instanceof TypeInt || type==TYPE_FLT) && random.nextInt(50)==0) { + sb.append(random.nextBoolean()?" + ":" - "); + genMultiplication(type, true, preferLogical, d); + } + } + + public void genMultiplication(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + genUnary(type, false, preferLogical, d); + while (d-- > 0 && (type instanceof TypeInt || type==TYPE_FLT) && random.nextInt(50)==0) { + sb.append(random.nextBoolean()?'*':'/'); + genUnary(type, true, preferLogical, d); + } + } + + /** + * Generate a unary expression. + */ + public void genUnary(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + if (!(type instanceof TypeInt) && type != TYPE_FLT) { + genSuffix(type, false, preferLogical, d); + return; + } + while (d>0 && random.nextInt(5)==0) { + d--; + boolean what = type instanceof TypeInt && random.nextInt(preferLogical?2:20) == 0; + sb.append(what?'!':'-'); + } + if (random.nextInt(10)==0) { + var t = type; + var v = findVisibleVariablesMatching(va->va.type.isa(t)); + if (v != null) { + sb.append(random.nextBoolean()?"--":"++").append(v.name); + return; + } + } + genSuffix(type, false, preferLogical, d); + } + + public TypeStruct.Field getField(Type type) { + int n=0; + for (var s: structs) { + for (var f: s.fields) { + if (f.type.isa(type)) n++; + } + } + if (n==0) return null; + n = random.nextInt(n); + for (var s: structs) { + for (var f: s.fields) { + if (f.type.isa(type)) if (n--==0) return f; + } + } + throw new AssertionError(); + } + + public void genSuffix(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + if (d > 0 && random.nextBoolean() && genField(type, d-1)) return; + if (d > 0 && random.nextInt(10) == 0) { + if (random.nextBoolean() && genField(type, d-1)) return; + var t = type; + var v = findVisibleVariablesMatching(va -> va.type.isa(t)); + if (v != null) { + sb.append(v.name).append(random.nextBoolean() ? "--" : "++"); + return; + } + } + genPrimary(type, false, preferLogical, d); + } + + public boolean genField(Type type, int d) { + if (generateInvalid()) { + var field = getRandomName(); + var t = getType(); + genSuffix(t, true, false, d); + sb.append(".").append(field); + } else { + var field = getField(type); + if (field == null) return false; + genSuffix(field.struct, true, false, d); + sb.append(".").append(field.name); + } + return true; + } + + /** + * Generate a primary expression. + */ + public void genPrimary(Type type, boolean change, boolean preferLogical, int d) { + if (change && generateInvalid()) type = getType(); + var rand = random.nextInt(10); + if (d > 0 && rand == 0) { + sb.append("("); + genExpression(type, false, preferLogical, d-1); + sb.append(")"); + } else if (rand < 6) { + genVariable(type); + } else { + genConst(type); + } + } + + /** + * Generate a constant. + */ + public void genConst(Type type) { + if (generateInvalid()) type = getType(); + if (type instanceof TypeInt || (type == TYPE_FLT && random.nextBoolean())) { + var rand = random.nextInt(10); + switch (rand) { + case 0 -> sb.append("true"); + case 1 -> sb.append("false"); + default -> sb.append(random.nextInt(1<<(rand-2))); + } + } else if (type == TYPE_FLT) { + sb.append(random.nextFloat()); + } else if (type instanceof TypeNullable n) { + if ((n.base instanceof TypeStruct s && s.fields == null) || random.nextBoolean()) { + sb.append("null"); + } else { + sb.append("new ").append(generateInvalid() ? getRandomName() : n.base.name); + } + } else { + sb.append("new ").append(generateInvalid() ? getRandomName() : type.name); + } + } + + /** + * Generate a variable. If there are none generate a number instead. + * If the program is allowed to be invalid a random name can be generated in some cases. + */ + public void genVariable(Type type) { + if (generateInvalid()) { + sb.append(getRandomName()); + return; + } + if (generateInvalid()) type = getType(); + var t = type; + var v = findVisibleVariablesMatching(va->va.type.isa(t)); + if (v == null) genConst(type); + else sb.append(v.name); + } + +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/arg_count.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/arg_count.c new file mode 100644 index 00000000..e247e28a --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/arg_count.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +__attribute__ ((CALL_CONV)) +extern double addAll( long long i0, double f1, long long i2, double f3, long long i4, double f5, long long i6, double f7, long long i8, double f9, long long i10, double f11, long long i12, double f13, long long i14, double f15, long long i16, double f17, long long i18, double f19 ); + +int main() { + double result = addAll( 0, 1.1, 2, 3.1, 4, 5.1, 6, 7.1, 8, 9.1, 10, 11.1, 12, 13.1, 14, 15.1, 16, 17.1, 18, 19.1 ); + printf("%f\n", result); + return 0; +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c new file mode 100644 index 00000000..127cbe73 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct AryInt { + unsigned int len; + unsigned char data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt* brain_fuck(); + +int main() { + AryInt* ps = brain_fuck(); + + printf("%.*s", ps->len, ps->data); + + return 0; +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/fib.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/fib.c new file mode 100644 index 00000000..263d2f13 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/fib.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdbool.h> + + +__attribute__ ((CALL_CONV)) +extern int fib(int x); + +int main() { + printf("["); + for (int i = 0; i < 10; i++) { + printf("%d", fib(i)); + if (i < 9) printf(", "); + } + printf("]"); + return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/loop2.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/loop2.c new file mode 100644 index 00000000..934f2d89 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/loop2.c @@ -0,0 +1,7 @@ + +__attribute__ ((CALL_CONV)) +extern int loop2(); + +int main() { + return loop2(); +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c new file mode 100644 index 00000000..7e7e796d --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <math.h> +#include <stdlib.h> + +extern double test_sqrt(double); + +int main( int argc, char** argv ) { + for( int i=0; i<10; i++ ) { + double d = test_sqrt((double)i), expect = sqrt((double)i); + double delta = fabs(d-expect); + printf("%d %f (%g)\n",i,d,delta); + if( delta > 1e-15 ) + return 1; + } + return 0; +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/person.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/person.c new file mode 100644 index 00000000..df95e1ca --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/person.c @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +typedef struct Person { + int age; +} Person; + +typedef struct PersonArray { + unsigned int length; + int pad; + Person* data[]; +} PersonArray; + +__attribute__ ((CALL_CONV)) +extern void fcn(PersonArray* people, int index); + +int main() { + Person base[5] = {{25},{1},{18},{45},{60}}; + unsigned char buf[8+8*5]; + PersonArray* people_array = (PersonArray*)buf; + people_array->length=5; + for (int i=0; i<people_array->length; i++) people_array->data[i] = &base[i]; + + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + + printf("%d\n", people_array->data[1]->age); // expected: 6 + + return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/sieve.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/sieve.c new file mode 100644 index 00000000..cac4cfa3 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/sieve.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct AryInt { + unsigned int len; + unsigned int data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt *sieve(int N); + +int main() { + AryInt* ps = sieve(100); + printf("%d[",ps->len); + for( int i=0; i<ps->len; i++ ) + printf("%d, ",ps->data[i]); + printf("]"); + return 0; +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/sort.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/sort.c new file mode 100644 index 00000000..ad76cc7f --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/sort.c @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <stdlib.h> + +// Test wrapper to call `sort.smp` + +typedef struct AryInt { + unsigned int len; + long long data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt* merge_sort(AryInt*); + + +long long primes[] = { 25, 97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 }; + +int main() { + AryInt *as = (AryInt*)primes; + + merge_sort(as); + + printf("%d[",as->len); + for( int i=0; i<as->len; i++ ) + printf("%d, ",as->data[i]); + printf("]"); + + return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_10x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_10x.c new file mode 100644 index 00000000..d44d5387 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_10x.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +__attribute__ ((CALL_CONV)) +extern int stack10(int x); + +int main() { +int result = stack10(0); + +printf("%d", result); +return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_11x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_11x.c new file mode 100644 index 00000000..d71979d1 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_11x.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +__attribute__ ((CALL_CONV)) +extern int stack11(int x); + +int main() { +int result = stack11(1); + +printf("%d", result); +return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_12x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_12x.c new file mode 100644 index 00000000..a4e425cb --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_12x.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +__attribute__ ((CALL_CONV)) +extern int stack12(int x); + +int main() { +int result = stack12(0); + +printf("%d", result); +return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_13x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_13x.c new file mode 100644 index 00000000..a3a51d0e --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_13x.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +__attribute__ ((CALL_CONV)) +extern int stack13(int x); + +int main() { +int result = stack13(0); + +printf("%d", result); +return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_9x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_9x.c new file mode 100644 index 00000000..4f93ba24 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_9x.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +__attribute__ ((CALL_CONV)) +extern int stack9(int x); + +int main() { +int result = stack9(0); + +printf("%d", result); +return 0; +}
\ No newline at end of file diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stringHash.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stringHash.c new file mode 100644 index 00000000..2c6d0fa9 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/stringHash.c @@ -0,0 +1,30 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct ArrayU8 { + unsigned int length; + unsigned char data[]; +} ArrayU8; +typedef struct String { + ArrayU8 * cs; + long long _hashCode; +} String; + + +__attribute__ ((CALL_CONV)) +extern bool equals(String*st1, String*st2); + +int main() { + String test1 = {.cs=(ArrayU8*)"\x04\x00\x00\x00test"}; + String test2 = {.cs=(ArrayU8*)"\x05\x00\x00\x00test1"}; + + if( equals(&test1, &test2) ) { + printf("Expected test1 and test2 unequal"); + return 1; + } + if( !equals(&test2, &test2) ) { + printf("Expected test2 and test2 equal"); + return 1; + } + return 0; +} diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test0/A.smp b/chapter25/src/test/java/com/seaofnodes/simple/test0/A.smp new file mode 100644 index 00000000..b94fa147 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/test0/A.smp @@ -0,0 +1,6 @@ +// -*- mode: java; -*- + +val _fcn = { -> arg ? { int x -> x+1; } : { int x -> x-1; }; }; + +// Refer to child B is OK because last field of A has been initialized +_fcn()(B.x); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test0/A/B.smp b/chapter25/src/test/java/com/seaofnodes/simple/test0/A/B.smp new file mode 100644 index 00000000..82844f74 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/test0/A/B.smp @@ -0,0 +1 @@ +val x=5; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test5/par.smp b/chapter25/src/test/java/com/seaofnodes/simple/test5/par.smp new file mode 100644 index 00000000..f19bd57b --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/test5/par.smp @@ -0,0 +1,20 @@ +// -*- mode: java; -*- + +// Parent module +val par0 = 3; // Some final field + +// Nested class in parent. +// Makes a field: "val child0 = ..." +struct child0 { + val kid0 = 5; +}; + +// Nested class in parent, comes from disk. +// These are defined in-order +val child1 = child1; + +// Nested class in parent, comes from disk +val child2 = child2; + +// Nested class in parent, comes from disk +val child3 = child3; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child1.smp b/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child1.smp new file mode 100644 index 00000000..7bca0cf7 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child1.smp @@ -0,0 +1,12 @@ +// -*- mode: java; -*- + +// Child module + +// Same field name as child2 +val kid1 = 7; + +// Can read parent directly +val kid2 = par0; + +// Error to reference a later sibling +// val kid3 = par.child2.kid1; diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child2.smp b/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child2.smp new file mode 100644 index 00000000..ab5e2ac1 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child2.smp @@ -0,0 +1,13 @@ +// -*- mode: java; -*- + +// Child module + +// Same field name as child2 +val kid1 = 11; + +// Can read parent directly. +// Can read defined-in-parent fields. +val kid2 = child0.kid0; + +// OK to reference a past sibling +val kid3 = child1.kid1; |
