From 7ab9e78b44537c297d9af4ef752389c671046259 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Sat, 10 Apr 2021 18:50:28 +0200 Subject: [PATCH] more work on test rig --- internal/apimodule/media/mediacreate_test.go | 77 ++++++++++-------- internal/gotosocial/actions.go | 5 +- internal/mastotypes/mock_Converter.go | 42 ++++++++++ internal/media/mock_MediaHandler.go | 23 ++++++ internal/storage/inmem.go | 19 +++++ internal/storage/local.go | 51 +++++++++++- internal/storage/mock_Storage.go | 37 +++++++++ internal/storage/storage.go | 2 + testrig/config.go | 18 +++++ testrig/db.go | 18 +++++ testrig/log.go | 28 +++++++ testrig/mastoconverter.go | 29 +++++++ testrig/media/welcome-original.jpeg | Bin 0 -> 62529 bytes testrig/media/welcome-small.jpeg | Bin 0 -> 6872 bytes testrig/mediahandler.go | 31 +++++++ testrig/oauthserver.go | 29 +++++++ testrig/storage.go | 71 ++++++++++++++-- testrig/{models.go => testmodels.go} | 81 ++++++++++++++++++- 18 files changed, 514 insertions(+), 47 deletions(-) create mode 100644 testrig/log.go create mode 100644 testrig/mastoconverter.go create mode 100755 testrig/media/welcome-original.jpeg create mode 100755 testrig/media/welcome-small.jpeg create mode 100644 testrig/mediahandler.go create mode 100644 testrig/oauthserver.go rename testrig/{models.go => testmodels.go} (87%) diff --git a/internal/apimodule/media/mediacreate_test.go b/internal/apimodule/media/mediacreate_test.go index f5982605c..0c5c53340 100644 --- a/internal/apimodule/media/mediacreate_test.go +++ b/internal/apimodule/media/mediacreate_test.go @@ -13,7 +13,6 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -27,48 +26,43 @@ import ( ) type MediaCreateTestSuite struct { + // standard suite interfaces suite.Suite - config *config.Config - mockOauthServer *oauth.MockServer - mockStorage *storage.MockStorage - mediaHandler media.MediaHandler - mastoConverter mastotypes.Converter + config *config.Config + db db.DB + log *logrus.Logger + storage storage.Storage + mastoConverter mastotypes.Converter + mediaHandler media.MediaHandler + oauthServer oauth.Server + + // standard suite models testTokens map[string]*oauth.Token testClients map[string]*oauth.Client testApplications map[string]*gtsmodel.Application testUsers map[string]*gtsmodel.User testAccounts map[string]*gtsmodel.Account - log *logrus.Logger - db db.DB - mediaModule *mediaModule + testAttachments map[string]*gtsmodel.MediaAttachment + + // item being tested + mediaModule *mediaModule } /* TEST INFRASTRUCTURE */ -// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout func (suite *MediaCreateTestSuite) SetupSuite() { - // some of our subsequent entities need a log so create this here - log := logrus.New() - log.SetLevel(logrus.TraceLevel) - suite.log = log - - // Direct config to local postgres instance + // setup standard items suite.config = testrig.NewTestConfig() + suite.db = testrig.NewTestDB() + suite.log = testrig.NewTestLog() + suite.storage = testrig.NewTestStorage() + suite.mastoConverter = testrig.NewTestMastoConverter(suite.db) + suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage) + suite.oauthServer = testrig.NewTestOauthServer(suite.db) - // use an actual database for this, because it's just easier than mocking one out - database, err := db.New(context.Background(), suite.config, log) - if err != nil { - suite.FailNow(err.Error()) - } - suite.db = database - - suite.mockOauthServer = &oauth.MockServer{} - suite.mockStorage = &storage.MockStorage{} - suite.mockStorage.On("StoreFileAt", mock.AnythingOfType("string"), mock.AnythingOfType("[]uint8")).Return(nil) // just pretend to store - suite.mediaHandler = media.New(suite.config, suite.db, suite.mockStorage, log) - suite.mastoConverter = mastotypes.New(suite.config, suite.db) + // setup module being tested suite.mediaModule = New(suite.db, suite.mediaHandler, suite.mastoConverter, suite.config, suite.log).(*mediaModule) } @@ -80,16 +74,18 @@ func (suite *MediaCreateTestSuite) TearDownSuite() { func (suite *MediaCreateTestSuite) SetupTest() { testrig.StandardDBSetup(suite.db) + testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") suite.testTokens = testrig.NewTestTokens() suite.testClients = testrig.NewTestClients() suite.testApplications = testrig.NewTestApplications() suite.testUsers = testrig.NewTestUsers() suite.testAccounts = testrig.NewTestAccounts() + suite.testAttachments = testrig.NewTestAttachments() } -// TearDownTest drops tables to make sure there's no data in the db func (suite *MediaCreateTestSuite) TearDownTest() { testrig.StandardDBTeardown(suite.db) + testrig.StandardStorageTeardown(suite.storage) } /* @@ -98,28 +94,42 @@ func (suite *MediaCreateTestSuite) TearDownTest() { func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful() { + // set up the context for the request t := suite.testTokens["local_account_1"] oauthToken := oauth.PGTokenToOauthToken(t) - - // setup recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) ctx.Set(oauth.SessionAuthorizedToken, oauthToken) ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) - buf, w, err := testrig.CreateMultipartFormData("file", "../../media/test/test-jpeg.jpg", map[string]string{ + + // see what's in storage *before* the request + storageKeysBeforeRequest, err := suite.storage.ListKeys() + if err != nil { + panic(err) + } + + // create the request + buf, w, err := testrig.CreateMultipartFormData("file", "../../../testrig/media/test-jpeg.jpg", map[string]string{ "description": "this is a test image -- a cool background from somewhere", "focus": "-0.5,0.5", }) if err != nil { panic(err) } - ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) + + // do the actual request suite.mediaModule.mediaCreatePOSTHandler(ctx) + // check what's in storage *after* the request + storageKeysAfterRequest, err := suite.storage.ListKeys() + if err != nil { + panic(err) + } + // check response suite.EqualValues(http.StatusAccepted, recorder.Code) @@ -157,6 +167,7 @@ func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful() assert.NotEmpty(suite.T(), attachmentReply.ID) assert.NotEmpty(suite.T(), attachmentReply.URL) assert.NotEmpty(suite.T(), attachmentReply.PreviewURL) + assert.Equal(suite.T(), len(storageKeysBeforeRequest) + 2, len(storageKeysAfterRequest)) // 2 images should be added to storage: the original and the thumbnail } func TestMediaCreateTestSuite(t *testing.T) { diff --git a/internal/gotosocial/actions.go b/internal/gotosocial/actions.go index 1b3dbf69b..2e745e7ca 100644 --- a/internal/gotosocial/actions.go +++ b/internal/gotosocial/actions.go @@ -31,6 +31,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/apimodule/account" "github.com/superseriousbusiness/gotosocial/internal/apimodule/app" "github.com/superseriousbusiness/gotosocial/internal/apimodule/auth" + mediaModule "github.com/superseriousbusiness/gotosocial/internal/apimodule/media" "github.com/superseriousbusiness/gotosocial/internal/cache" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -54,7 +55,7 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr return fmt.Errorf("error creating router: %s", err) } - storageBackend, err := storage.NewInMem(c, log) + storageBackend, err := storage.NewLocal(c, log) if err != nil { return fmt.Errorf("error creating storage backend: %s", err) } @@ -70,11 +71,13 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr authModule := auth.New(oauthServer, dbService, log) accountModule := account.New(c, dbService, oauthServer, mediaHandler, mastoConverter, log) appsModule := app.New(oauthServer, dbService, mastoConverter, log) + mm := mediaModule.New(dbService, mediaHandler, mastoConverter, c, log) apiModules := []apimodule.ClientAPIModule{ authModule, // this one has to go first so the other modules use its middleware accountModule, appsModule, + mm, } for _, m := range apiModules { diff --git a/internal/mastotypes/mock_Converter.go b/internal/mastotypes/mock_Converter.go index 881bc48aa..732d933ae 100644 --- a/internal/mastotypes/mock_Converter.go +++ b/internal/mastotypes/mock_Converter.go @@ -104,3 +104,45 @@ func (_m *MockConverter) AppToMastoSensitive(application *gtsmodel.Application) return r0, r1 } + +// AttachmentToMasto provides a mock function with given fields: attachment +func (_m *MockConverter) AttachmentToMasto(attachment *gtsmodel.MediaAttachment) (mastotypes.Attachment, error) { + ret := _m.Called(attachment) + + var r0 mastotypes.Attachment + if rf, ok := ret.Get(0).(func(*gtsmodel.MediaAttachment) mastotypes.Attachment); ok { + r0 = rf(attachment) + } else { + r0 = ret.Get(0).(mastotypes.Attachment) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*gtsmodel.MediaAttachment) error); ok { + r1 = rf(attachment) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MentionToMasto provides a mock function with given fields: m +func (_m *MockConverter) MentionToMasto(m *gtsmodel.Mention) (mastotypes.Mention, error) { + ret := _m.Called(m) + + var r0 mastotypes.Mention + if rf, ok := ret.Get(0).(func(*gtsmodel.Mention) mastotypes.Mention); ok { + r0 = rf(m) + } else { + r0 = ret.Get(0).(mastotypes.Mention) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*gtsmodel.Mention) error); ok { + r1 = rf(m) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/internal/media/mock_MediaHandler.go b/internal/media/mock_MediaHandler.go index 6f04f1fe7..1f875557a 100644 --- a/internal/media/mock_MediaHandler.go +++ b/internal/media/mock_MediaHandler.go @@ -12,6 +12,29 @@ type MockMediaHandler struct { mock.Mock } +// ProcessAttachment provides a mock function with given fields: img, accountID +func (_m *MockMediaHandler) ProcessAttachment(img []byte, accountID string) (*gtsmodel.MediaAttachment, error) { + ret := _m.Called(img, accountID) + + var r0 *gtsmodel.MediaAttachment + if rf, ok := ret.Get(0).(func([]byte, string) *gtsmodel.MediaAttachment); ok { + r0 = rf(img, accountID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gtsmodel.MediaAttachment) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte, string) error); ok { + r1 = rf(img, accountID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SetHeaderOrAvatarForAccountID provides a mock function with given fields: img, accountID, headerOrAvi func (_m *MockMediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) { ret := _m.Called(img, accountID, headerOrAvi) diff --git a/internal/storage/inmem.go b/internal/storage/inmem.go index 31c2bd99a..976ddd7ff 100644 --- a/internal/storage/inmem.go +++ b/internal/storage/inmem.go @@ -15,22 +15,41 @@ import ( func NewInMem(c *config.Config, log *logrus.Logger) (Storage, error) { return &inMemStorage{ stored: make(map[string][]byte), + log: log, }, nil } type inMemStorage struct { stored map[string][]byte + log *logrus.Logger } func (s *inMemStorage) StoreFileAt(path string, data []byte) error { + l := s.log.WithField("func", "StoreFileAt") + l.Debugf("storing at path %s", path) s.stored[path] = data return nil } func (s *inMemStorage) RetrieveFileFrom(path string) ([]byte, error) { + l := s.log.WithField("func", "RetrieveFileFrom") + l.Debugf("retrieving from path %s", path) d, ok := s.stored[path] if !ok { return nil, fmt.Errorf("no data found at path %s", path) } return d, nil } + +func (s *inMemStorage)ListKeys() ([]string, error) { + keys := []string{} + for k := range s.stored { + keys = append(keys, k) + } + return keys, nil +} + +func (s *inMemStorage) RemoveFileAt(path string) error { + delete(s.stored, path) + return nil +} diff --git a/internal/storage/local.go b/internal/storage/local.go index 620467df1..09a62bef4 100644 --- a/internal/storage/local.go +++ b/internal/storage/local.go @@ -1,6 +1,11 @@ package storage import ( + "fmt" + "os" + "path/filepath" + "strings" + "github.com/sirupsen/logrus" "github.com/superseriousbusiness/gotosocial/internal/config" ) @@ -8,16 +13,58 @@ import ( // NewLocal returns an implementation of the Storage interface that uses // the local filesystem for storing and retrieving files, attachments, etc. func NewLocal(c *config.Config, log *logrus.Logger) (Storage, error) { - return &localStorage{}, nil + return &localStorage{ + config: c, + log: log, + }, nil } type localStorage struct { + config *config.Config + log *logrus.Logger } func (s *localStorage) StoreFileAt(path string, data []byte) error { + l := s.log.WithField("func", "StoreFileAt") + l.Debugf("storing at path %s", path) + components := strings.Split(path, "/") + dir := strings.Join(components[0:len(components) - 1], "/") + if err := os.MkdirAll(dir, 0777); err != nil { + return fmt.Errorf("error writing file at %s: %s", path, err) + } + if err := os.WriteFile(path, data, 0777); err != nil { + return fmt.Errorf("error writing file at %s: %s", path, err) + } return nil } func (s *localStorage) RetrieveFileFrom(path string) ([]byte, error) { - return nil, nil + l := s.log.WithField("func", "RetrieveFileFrom") + l.Debugf("retrieving from path %s", path) + b, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading file at %s: %s", path, err) + } + return b, nil +} + +func (s *localStorage) ListKeys() ([]string, error) { + keys := []string{} + err := filepath.Walk(s.config.StorageConfig.BasePath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + keys = append(keys, path) + } + return nil + }) + if err != nil { + return nil, err + } + return keys, nil +} + +func (s *localStorage) RemoveFileAt(path string) error { + return os.Remove(path) } diff --git a/internal/storage/mock_Storage.go b/internal/storage/mock_Storage.go index 865d52205..2444f030a 100644 --- a/internal/storage/mock_Storage.go +++ b/internal/storage/mock_Storage.go @@ -9,6 +9,43 @@ type MockStorage struct { mock.Mock } +// ListKeys provides a mock function with given fields: +func (_m *MockStorage) ListKeys() ([]string, error) { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveFileAt provides a mock function with given fields: path +func (_m *MockStorage) RemoveFileAt(path string) error { + ret := _m.Called(path) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(path) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // RetrieveFileFrom provides a mock function with given fields: path func (_m *MockStorage) RetrieveFileFrom(path string) ([]byte, error) { ret := _m.Called(path) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 7c85d0a5b..493c364e0 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -25,4 +25,6 @@ package storage type Storage interface { StoreFileAt(path string, data []byte) error RetrieveFileFrom(path string) ([]byte, error) + ListKeys() ([]string, error) + RemoveFileAt(path string) error } diff --git a/testrig/config.go b/testrig/config.go index 1bc0b8b87..f7028b1b5 100644 --- a/testrig/config.go +++ b/testrig/config.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import "github.com/superseriousbusiness/gotosocial/internal/config" diff --git a/testrig/db.go b/testrig/db.go index e930ba2b2..10025c9da 100644 --- a/testrig/db.go +++ b/testrig/db.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import ( diff --git a/testrig/log.go b/testrig/log.go new file mode 100644 index 000000000..0bafc96f7 --- /dev/null +++ b/testrig/log.go @@ -0,0 +1,28 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package testrig + +import "github.com/sirupsen/logrus" + +// NewTestLog returns a trace level logger for testing +func NewTestLog() *logrus.Logger { + log := logrus.New() + log.SetLevel(logrus.TraceLevel) + return log +} diff --git a/testrig/mastoconverter.go b/testrig/mastoconverter.go new file mode 100644 index 000000000..10bdbdc95 --- /dev/null +++ b/testrig/mastoconverter.go @@ -0,0 +1,29 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package testrig + +import ( + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/mastotypes" +) + +// NewTestMastoConverter returned a mastotypes converter with the given db and the default test config +func NewTestMastoConverter(db db.DB) mastotypes.Converter { + return mastotypes.New(NewTestConfig(), db) +} diff --git a/testrig/media/welcome-original.jpeg b/testrig/media/welcome-original.jpeg new file mode 100755 index 0000000000000000000000000000000000000000..1a54437ea5cb921c05e20bf35d49959f7ba4beca GIT binary patch literal 62529 zcmbT)X*?8u{P+9sER0<;YA|+XkbTsQbtG$HkVqw=7$Re=V`=b9H8J*z#1vyIOGWmg zEU7Tfh_ZzdA=xFB&f|Zb>v28K&2!HC?&ovodH412)4x&h9}wW@NAU9rAP@*aK>;BV zq^O9nu!!_tNpYl{jDozJ3>vMds;#D|d`Jb2#vD;Uq^pO;VinZzrZ{~QZ3C=6L{Lyr zL|6nRDvHuqLM!S2f8M`#ARz#agxA6#$^a?>fk{CAbwVBj0O5oDuYvzP2owhA<3|Vx z3JLE4PzVeLg~Ryx;BWvS@en8sz$N(hD(PA9OFCabD3g);iJ1=sRE|FBkaGF3qH5q1 zmLw>&Z$C;}=DqnoxSTxw^QyOr#*fBE?)8vAO%vwBO;@s zW2njhT}!!s<0d^TJ16({ox6GY4~t7!rDf$6l~145)jw}&Y-)b>y0fdh=S}Zh4tMb5 z(D0|vBU977nXj{R-{u!q*VZ>Sw|;K_+JOMr|IL9w{T-@GyZJVeG{vq5=&D{Wb`!B$vO6XF3APP84cchMWncL{BE zbutZrz`WOR%&B`SER&1H?VyNC66MOqmxD`wfTvQgjrxx7!R9GeZgt^oBdg;BAQK*l zC|BEPd|6iGbg{Ri9zlF0`hxAtgaUCG5}lnnL_= z9ezDaQvb4Ow7TTz?bm}-rpdldiY|j?=I>L~N6lehICt{_Fgb_ZG@jwr>!!+tijL6G zJ?S}=iEC=o15>sLlc?$YUvuTcOfeAs6SUo&IapTlB0({7)Zv{rWj%xm$D^im#PF=W z{*~Oef509f)Ub^(7Rtr0pAU!WL9<~o zo2B2EvmH%I0T5(NF_|Y;mNgLgbZ)z6 zZxfM6T0Thq_+AK}3qgx#NpLgsI2SDDi`T+VT z_L>F0xNuMp^@hn;vb&e`3F+;N1pZiwF2$cX>{D{$;si~Gl1V4IoG}Gub60SE2cyuu z|M_^~Y%-f&Ftfq@p{9}G39O`jOw}v-e>fUAq5b6?l+L{t9&Q8Jipxok>LzWEQnT~3 z3%oa(N!G@65V)iQFJSx5#JWBw_jUnq-zp#~9kStzo=qjjq>BCh2DuBL!*Y9jSh#Au zSDi+|3>#9wAs2|%O@p%>-7a9_kKQbQads0sY5UNiP}~!o)mUfba0~_lGd9Y1D5-1+ zL!h`GRCEfOSnUk~2p zKk3>l)YxrTb{S#?j#>*~pQe>W^%+^IK>~v5&Z9L>-82~+w9A|Qy(7j^Ea|=C3H9

V5PB7MWgG)I5Y`R z=uVqqdlO~@pauh;CmO!_f*epHI#8 zjuK1Bs8v&@iM3KW4HDmfsOKp}nNZm=ml-zYOoZ+@H&w{0A5 zN#D$w?dD~kK&_i%Ae|y}NPt7JRPtSi&QrR85=;&Qvb*dQ#%EJhhG^0St28}FmS{u0 z^scfEhlT>^>BQYyv58#FuarqKQMJ2G-Q0X&1Bv)ziC!a_j zpn^*p7Q5vi8m^dr6Z4o9DY77lhzKV#HYUl9JFbL-E_zOD&)R2cvg#s)o;Ef6%wna+ zMfwSa=0QMvj+17Mbk02}eWn95$CXW`o5p{G=)Ro5m+8KlFL0$;3(O7Cq$mXvUFr*> zits{Q*-DHCg&@!l1kgL2jH)lFMM8fV)%BF2)@`|1Hl&T5N_J$aAQ(BrGpR(V-Y?QW zu-1PsdET19!-CIy#7Q)44m;)zu{XD zS%%JRJUPtlDL)H`i(_})_Ny?NFol*rg|$wOe-t9UN_#BSm<@x+q$}S(^q?2Z>$xn0 z{}IE4rDpLd^~#Z6I1gAl!SX?>`hXgCO*jE5EuPnac9~%muwik<$Lnoe`?T#AFiiOG z)=jyJfQK|8qrvC5xnAQoSHSypPV?fG>z=8N>z5dMsq1}!TrAhG{z#_IX@mHpDhLiRNg8RR>LFwLRHCOe*f7ag3lLHxz^l{C8yGSAVh0& zwe&7XBn(U)rK zMfqnFgcHBeuZ+*Q`~&ZtUNPp?yiq2B$I>3J%K=e+*zBU5nV}b%Q}6zUcxyfE zyxQ3r8pIBNc|B6M{K%&QUKtC-oN#{5`y$6@^0@KW!;A8zZL?pjxZkG*`JO7uJ;~2| ztaZ-5zSYC*79ITB$o@zYJgD#FxfSfxVdS=R=cMlO&pF{W*wy_hKjKVA{oeKb(dShG zA8W^nI(aATL@RekEC7n@@lyXBGJOY8EsFEU%Q=8!6Lq&3v-eXQjyhH0!m$u(RR#MC zL>}B9J(w)5g67)7XKDwAXd*MSnV>sV5ZSmV*9) zhp0|j<5(HNve#Lto=_{@YDb#{3~(FElM_{Qy3=O5pY#tzW~Zdfm(pZYfBqqfT!`UM z0toa{DUH&CGur^OXUlt#eU)bGlzA8LT)edD6Z?}iVMa0w98=c1{aqcGLo@QpEZHwj zSG0wPYEoadix_Op6VN{<1^CD=1vR)n#Zd;8t8(BbVucAu^D!o64 zPN+W&nYVQxqM^b%(cz`FowTirSI4h)5oDgcL zvY4RMb%KZ+{+-Cl%GYLfEJqF8uY)xC_p&r2R|3!Uo50Dr>UazuhQk8?;fgX~?Gb7v zS37P@RFNfT_W{I{-(_vak!C07Oda$OeiG;=EsT!>B0xR!mBaTLLrQv73uz)lx3i~4 z|AshkdF1&nE6vk4MJ3yJ5^g_IeK$82rN(568$4ZkQ5E-1sqTtavtIjd#8WeL;QJDT zV?|z9bEzYW&es5DiJpxZMnZV*MPl{00xT0nCa;F`ehnLvgG~!cHE(zxk-O{u0lu0F z+pNCYxht1^=1I23!>{gNvv-IHIubW<#nXZ1DA?y<+WOqxWz=2(6KcbG(O1tGGMsy^Lk}8YNneiC$^}5r z=m;*lvs38YfRHp5V1EDdnLql2MWTmRlzz^(omO7^K!nth(*qN}*n_oyVEfem(VM^B zp6tM!->tmeHIZthJ(&~>`bg-qB4d)&a+@)IAX@%G*^As5t-4Pn$ZKC_WsAevk5^>Y zU7PcE>i(mb9HFw|X+NECRpMfCBY*P~vs)>rXP>t!?YSy>IyL+<8}U5JXos^|(oJ$(eaHNK z)kk|hGm;q?f-ihEdx!?5^e)=QDDFhC##^!zZH+P+ZQmf;URiRt?U5#d57 zPd68YdH?;?&C-nGAsSQ^n(|6)V*xmBvzC*$xs;C0C8zV7gAmTPP|uRgf`Ry^X8z_R zexNG!su*^YJ`yyBZt5|dAVQAY9VZB8E_&%qzvISCYc}*dClC^H7xPo6M;)8)76c!? zigv&Dei^&=w#WDvQvlPgPPUjQKt&ojIjPw#DmFKaI?}U$^6u1emohh$?`M z=()hjfI@gx!UNc~Fbw3sNG~g>xo23Rfg_p?zZe>Q*@fW<>BHYCe0Z1gi3t6$iFS#P zX2DDtIbcj(+I8E-d+@rvhqtb{?@)$c>ObHK(n(+P6G)^I^d*|)3uBp#Zal0?`I*G4 z72}~V*Z<(79`{F9kH$^0O!7zxQdu*not2;5;?bg7M_3;wE)zBtQ<>YRuwP6{QRj4G{Mjtd`v(xj+UFNKy& zv?j9?z61>-t6r{D+w?8Fo5)T{=nId|pb%egw)_qHuvd{TMR1b?ON2cmpSwsxR`4Kn zpdL-5560rFCG>a#VaQM_y?waV+4n)`>3i=RO#~8`&t6v8W2hd1qtW$*8|6J_)(P^( z6Hi*5ORXvco*iAh4bzC%^gKLfu-Io4mHza2ODXqHIc$zA?c&a!zQ^AAeUkGJRA(zp z7)qbb2bX4j-;C1TrfzPQFh%Hq5GrO$ie50{?lWbsJHe3PI({mQbj<=k>H?K&!iwd9 z{+#Ox;>cF=o0;iCDiMC+q&Ar0@M-<5JVDL}eZZyx0nM}>cl360tmCv;@4l`A>ausI zi!XOaOKI*YR?ckDU7~{v-Zrr8p~S>@LMHWSnq3(b9Jqm!69v+}(!Xp1DWk2E8b#e% z-QBUjJ{IKuC!btVmH8b1T!Xehkq#I=j`Tfet{BD($Pa$s3qe!4KNGu0z_gztxv=gU z`58Y;1IW^xI&t zvwH+h{$e0+?USzByFqLmTiHUW-|It3Hi3Xhz;_v_z3<0ul<7gD5!hqhR`baOaTB_= zF~1uLwwLs|2iFI%dnX3t+Bf^uC+Af16GDw82@=;-!(z(*avcq zGI4seg(hAx+x$g%+lY6vz?5^N>bhA=<=`z`Qjeo|Wt=h?YBLj#uHp&q6B=AYX4R|`UT`j{bmI(4TtNcCR zcYfVR>}-gPxhuca?L}==^QKK6)Hh~2joQ$t=VpS;s}@P^FW; z6ErEj$?(HJSnj8~pG)(#ei1)wjNsO<6$^$NqK~piqr*PhFI^&b(~eY-)B`~E)o!Dt z`5pLsYHj;$LvM$C?(9DxvHWvDG^^km-(4c2=mZI%$Yg&yaB5lL_|fEQ8rSN4m>N$509HgZX69{&z!pSQt{T& zw#)y(yMsMLG!wJ1i_Lk@((~`~Nxzf)%j?5sGBA+G1T=^8bxw{HW+ai1PxZy~)07Y& zDcgg^hnVUe8xM2BH<+OMLvNpSiE8>w?sIiAL9DcpOc!|5<eAWI`QEfFp*7cJ2vD zsH+37hgcFnAQm$}%ir`ItP8(&>BS)Pnl(jRoI^8>dVT8QfMxv&@27r$gM1z)nQLI! z;cik|09ltqM5yr=|A8mptdM^==!hjRqN*f`lI=z)zDDPUYoW6wGikQ~Hd-I)UGqBe}*f;#L53ED`F>cDhYn zb?U>e=nc^%znUa+a&%1+=^?1}JJ5S|3_&KJDJh`rG7_Zr2+}X$=Fo%pEPCpdoCyda zOUdI!1{u*zo(ks<2xkhsyityJy(^iVnK^3cBTJf7d2_9x$in@4N<@t9MefYpiwRM1 zQ3}7&6Bw{~rgq!lOpI`RUJ7+)aC!2+pStGdFw<|a`y>0m9?a3Uc^hQo^5IHdWA2Yh zj0d?e@A1c8q7!0-h7=2Zl`(^cb=f*vYuX%RheSMjc~D9TytAhYfq`drg}alMaO=~8 zJUPuK&I`0A=ALtI=70y!e5iR`l2G>{xSg&yEB`pi7i=;`P;u(T?T!2F|AFPOK-SO1 zy4W{&tq_??-%bv@@M9Du-Y~_*hOx)f#Ma;3n@u;@^sT>`ntZ!m?rG4cI8rHV>zV=I z-|yzF{Eg-!+m{@|rVi!_)LCX1Zh$Ys(O&05CLKaq!H9`2>eu?mheL08r>X|AA$CDQ zeD|9zLlRYhh)^L7P)_JyXz%e%(6g)cH(^6F3q}2cp1(6okAR#=%0qDK7Z@ckzuh_H zh&&bm>-bYFyC+sY=jQrh8;g;WJLJ4{h3MJDePdanrsZH!QS)m(vL_;DgMW@0cs4U0?7Q!;)%h4$bjRke#{0Y7@f`GP%6qo1a3~T=Q}^UH@3e9`_dEg%e?`a)+j;;4y~6m zs-4cdB$4TEV;;$DA}sPZm$EM5cuF;cBy??_AgUqmNdze7jK~%XH-H2mP(HYC6vt&- z98-5!(F*W+6l@^HFlq=qjs-GDXp$je6kA=l!NR(dTpoi28EFDU_=R}KDaU2{Nxo4S zh$?;aN@YN26baJqX6Q{8=G=as7Grik1K(vNYj-J|;y1TZ73=ZheX9_t7yG27Q9$NA zu)SsLQMhTK^W&!r6Tk?26BFrIQKi>ZVTE<6Eq1*F92%}uH8l!3t0qUbN0?)HZ7SmT zSm4NyH^@*^5>*d2%Ua~3T|ZLp7o`Dfg}OhdIh zg%oQ9n+PKyuzqTee_V%?>%z+>eMq>B6l z^^3<7=WQGVKs$Om?RCA5Xz%1n@)Evdh6t^*p9xvAfY^5Q881^79RjxgCwQl|mVbUH zN#8inA8mVFInXFcM10e^;VQLn<$8+aBi)vQ;O93O;||pKky9#e02HT2G6iWuy8L`L zst+;mTKFsm^BD_ET0Susy~~SL3qO7>R9`LGDGh#X!T8`P?9bN+!j1lcPSl}-4sUoh zPTyDYlvpV(#Oxpp*JEk~#_5NJZbWrt_Zc}x1<3^vy zi^d~SVFLvz8Edg0FUJyLDMS7E(pUOa--+9bas35R6}WFkM7}nSj+*+Q*bD1|svTw@ ze{6kIzUgcYO@(+}SXt}oADn3-uN~7hm)W#@RX`;|Kb$zjS4giN)O~u?W=+_Tf{kTcCVDoJlcTgq5RkIy*ZlB8 zrYn=8I~i^`XHQnDIkg>yxKsRo`!^>ae6P^6U=kr`zobWyMV={3cvihsb<%#~fhhKA2O;NQS1%6>)9rp6K>iATdcsIGQ%C>AWB z3&6$OMsg_9*tp$KEQJ=cmU76+y{!?Ye7_h!0^(A5hAJY3br6)QKMeaaMSaxFb%07n zG5L?Y)z_@qzUp)FXY3)_p{ zmOMT$d0tple){s{wGnd}uy%FNIXN58re?LMK3e>vBjhVBPClQ_QXY<{x9V*$5g7<9 zZLSLY!dlPqd<#K{pWm3nz!ZlBfOh7eV!HnR#NJ|3hyySPx>QIF@5ihk*+6w+772oN zn`-IyBzTy)OwL9r5$-ozXyp9IqsKGm7NQCqF0?_rnw@vG7gwf9GX7h)ea&d9xUHfY z?3@+OJuyfhNwB=9=rc_%qZWok9>>Ofq~ce%2O6&S=pcH zU<#mE*^Jyr72C0{oKb_(DW_Gj+}wXaODq6#?;aO9X~$AgL1H~F^Z_|tJ&SJcx*v}o zeeUVz^lbr?-<{WNzIfx9;tng?g<2cYbJ0C{n(|Jph;6t~1#48;L4QI-JTq_!97O!W z{zvXID$d(Fw)D+_Ab(|b??3QLw4B|wh5Vu=f>+7{{w-nKR zhlw7ad#*tF(x{MA(Urpu8J|3R$;Lj0OuoWtO^?DeH-~W7SM|EpgR395+`QBfk)`s7 zFr2-8r!y<)+1?2>5&`J7i=~FgGnN@g2Ga1DXv>mC2d{bwx4(C5B8#VpA)C%I~$=gIk@ixph zD3)X>Xf1PrX8NeU@;32rJ_iT7yM%iGu%K8vUJ36E<-CL8gb;W#|OD4l#~sfr9|Mm!O8_IWPr=lQRq!9YbOe z>Gq+eiM>pq?$;AYWQLLgnDF+8juw5on*V|JTJQ0O&_ z`bLrx#F?slSxCTlk1|OIwQjtaU!I`JnSB!b`0M7uuKqrO6_3*#~pt=aAFwVkFC7G|@)b%PN42A9L~BP)L)D zolf;nYwC$(LlPNI@^#~-Cnmia8?)xxL--#!CSpWjo***!(lbGf-8kvIO^=)+243FZheze@s61z|9&U7e}`*`>d;ZS1dRj=-b$LD$B{>S{lN2qBXh8 z40F%3N$-`=NKyik+%iE^TXnxx`tfT}wApb2H7f8c%FmmV{QH#?5yuj447u`_rkrVQ zJrVdLJMgDbi3A3(FG8=W^Ya~Qz6$gFwX2pM`nJ3e#PYQ!IJA=gfe1obVO?>X(jr0d z>~h+2SNipX9luzwAdBbr$GP8+JMjW~JV-aDdm01lw~&p@EI@Pf&K9J!m(eImeAou# zcq$PZ9MXfV6e?(#bLs|C2U_{3r6q zfik4PUyVM1bm4l^a9?f=?^@qPgY+-Y#W}6DYu(MuH{4&v`xtvukahz@O?+xe0U-OU zm(h;Pg<3&FowqTie?MgQ0oglG*S$VWZpWpIdWytP<*!C()KwN2WN1!c%gqK#&m84H zd?Z|0r0s1Tm^Piv+MW1pYwT_~|KBQo_58lEzcroi2ON)P?O$h#9NIUtYF|7% z$5Q829keDB_DW{f5g%rtT@(9&G?F`%{LUBo!Wlo`1ysG0`^?|D9Fa&K$!=&<+1Kt$ z5YAk*9$TNch49}mAVR$(6w*v>dpx}-6{rMq7qCN$9-6G^^-Xnu9`O9(uzkY~rL&6t zL2u)9{2E5S<9h{rrM{Q9_`o6ktQ~(J(5@6y5;y3Y`3_?(DJzlvD z%OhpBKL{8m>I!X-N`D5qPDz!4$c#_IjVp2^GJi zr(53a)<11|HZbqvLWFWI-Ojgm05)WJlwM3&clGtk2I~5i{oAJxAx5i^H&A~*B>r@u zhR(ayeCNG*di(RBROnSBu=4Pz+>5%Q8S`1_hh#)5uboIKpI#o3dNY%^G6Xl4^gvJI+XMqs|ujiro*?nsj(l&Ku;0 zkDG}eerp`CM}SRUi2wHTWwKL4>@9|rjr<1qz4#5gcv{>4vT}NV>!Z$ugN+~O!cTkR zAXDzu8sdARQ+S@u{XDehPhYv+`ui7yx9+|1alR7;%Lkk0d?SCeU)TEW@x#wH+t>JC zem<0WwD>=?TQY|x^1&8?H3;r7#XvP5-3`_%Y0RUg@!J{BOsK7@j&x1AUG~&I+--h~ zQCb%lJiWiE?YC&lG-`L({Kl|s>N;WH#-dZh!l@yq_cLEuQw=!*N z+J@H65pVvx=+s8N2DFfQC6{@0fEEceqIelk_Z?r_*|_@;+`g(<?!vyy{!TITr+R=@fC8(fyab9xhr}Ed1toDzrMca2oEnf>xh zX7gV@o2eq<(Ezq!UevhmOd;ZXp|xxvDkv5kwi*droe#dbK|(yruESLqhPZx$cHxul3<1+ioKwtQjp|FPmJz zy@imL3xG@Z2S5-fqKXlm`!s7xvOPi?fdy~XovN_^)7{Y91q@v-DE@+RN7VPtar*Y`cvH z?c(^qc(ElNZt z+#iv7yoIa+rO6U-89Fu8!}%!m3>(Hcvd6}8<66K&oc^Y$nnv$!4{Jp7y!nNG#^L;A2qhA|7qY^K;Cy1-(bS^HF~p1 zoWnrT&jmGD3-`?@qr8o&M3|VE=7(L`({b5eQge|X^4A|oOn{keuhA)4C12)iX$Aa& z0Ukm2HA@N2;jm%k^-Hb3-R=+9J6%QQ-r;uKYXbKxbDat!+Xem`PCEBKM0@t$qZSH5 zfJs@u^sslY5S4~sc2N15JVBEu^xIIswMzPL4M-dly12(Lf9@<#!7GUULgsYA_&JFl zNC51EjYT)6q#svCgb)W5=_)GWAp6(J`fJlI>lYr3*`i>n;@vcp>@zR+?{so0NwFVA zOP&WD`oD2HDhxnnj6DQ2FzRTaiLv>80NQWNUTK%8*T z-sXQlFwK`!Bca|$9m4kkEu@Pi9aLR48a?fxXC7gvkQ?pxesu2k*;(uUppS8sIIZyD zyw`6-HG6G^mEIuofnGxmwy!B0Uv?e%WNb{+(EClhOx?ksS4;w>&sNg358x!@cRk7| zE|{Z(AG>lMyE{Q`OcIj-!Oc#vOs24 z_U4K8UITQVVBp628=g1P9q5A!`Hmj3?)TuD8BTI9a=;`6S+x!jSTT%TJO;?wC#P2_ zXMLU_TeSO7lZTp%M7OPZE?s?{mo(#6#7{9r&KcXbee@T+0+agjY2GWbczf^LcYDVr zv2CGFQryp{EqtM`p?n}x$`{2Hc=$X8K8m)lGy)n&n|ijn6$U%8M82SV!e<&qqV}ki z8q?RmAh#_ZTeQ@Q_@M9h1a36@J-O+e*>nn}Of%WDXN=MIw<5^3lX`y3@fp@$Pa1!o z`qsBWegh1tiX?6i4O|a*t(quNy?FBLJ+#kL+9k+Izry5+w%AwA_VZMe)90SOX}TH+ zhU%Zv-%c3EMhNc>m-0zuyRe~>bYaLgNAHy+chM_;I(^@L&S%ov+?Bny<{3<}dnZ>< z<&{dJpM~T^S`@g&Yj%5{dZ3eWK>c9!I=_L}3F*7Hee6u_u$6tXA(s7=B75+&J;0L9i9j7mk=3(L3v^zxs0y9OLk|Uim9q>USo8{S zt#Q2n4YDAT)y?!Lcg2QW57ffm3ZV78%igR}Ss8!kwfKDH+M9ldSRc$uAtMMKL?CnO z!1a;4zS8}W%`)z}r0(N~*+%(*F})z`;T3aNLxOq7#%<+rK`{2J>GkCU z_&E;EsM*Mc6TyTT4n#lm*nP_>QJl7c>zD>5Sgi3(qiZx*$G2NQcGU*IlGr99wAZ8s zBUQ5(9Yn~Sf-kXJJ+&3zy^pxB5SaJh3o^x>Rvgkl)!}9|Z>r6$R5LfqgCE!iPp2g; zl*864$|03uRM&KSb#z=CnGpcHxt*Bc^MvsZx32HuUtVKKJPBf^n-!!36-H#fx1&qU z6|2Jx`p0J2kmqejrZ^bxCB?b9?x6nQrP8M6GXXhukE44|xYtIXw>3JPQ`@lfS!`~L zDLV2$z1Y|Kv4w`1k$=5e_DiR3nk2&T-lK(~5yRAH?)M>Culgos(2HsGe!i+!CUWARRfWb)RXSR?D^a2 z|EgEfnU;4)ew_UCXry*btPXJByj@>% z8Z*@;9gCYmyX4|}VT%M&fZpNS|Lw?kF|cC@Y%&qgQRHq7Tz8l;oxDV+CT5KbGT}mG z=Mo{mFLfX5dWH0|C#r9qdonjf!_-}#^rdM8Tz0*)RbkKj3VqDDhhPDX@-+esRS|M= zRHl+Kn29l0pU$OaSu7C*GJ|hjjl^#AjP3eNf4Mm?=AUv#BMR#vwiWAP;+{mY?~*Q% zIV5Z^b|$YRYLz zh3)*f(#qR`KyaiOnZ}Og-u2Ek-Eo=ZT@&48@&~uKGofNm_Rh;48xIDS!4e&u@SAH0 zJR72deZ-Zk_=E&VuN?YAvo`h519J0XKmPMag4BK1y(W>x@1)=KC)5>s{sZA;M-wQ8 zfq~f^duo(Y!}m1x$px>kR|HZN3u!{@3fK4X2V0++hDgvLH7;IvnFv=qe3=WGfG%Qe zia-6fx1+$$T;W=bj1HVpGTU}9O{w$7HUDz{Xwa;uJks#@djeMBQ zp`q+l=#ys3Ug}B4iZPe?UK9*pO-m-52(8xa3skr=d`aa;*;@=uK(Dt%u`aL7H6t~1 zf+pH#FzBqD`Gy5*4pFS_(oNU909EI8(52s6AGsnS-bJ}l^~qD3dj8BatLyeA-Blw% zT~HeQEIWC`K&y4{i9Z_Zc&50)_O*(?#V(8wPQ82Q%r<*8(mwUaFAPYQX=-4Vh>aleIECe zowH9kLvm`gKfqnSF!dmp@DG%RL)>9l-nu-AoSwso|E59*0*I9o*L4|-EOLJ6xmGr? zFx8mI3mK-S?wbrdV@S+0l38dKytviocVg@Y0?!n8YWUA07_Asu$>(5qoWQ5_uyP^l zDAysM@<%fttS!q-2=_%K6+HG0s199uSlV=V?A$v&)#c0U&O|`!vJ?uLG#dH`uGO7i zdNg^?!$M)gtPaAO5*Id{dAPM|9#(c^fVz=$(Sa(N+pUeCG-0>GJY{ zLG6+0$yw{a8c)V&F=Js=BBK6Ve&XQ9-H;n!o9S#H3cYuwujx5k3%RMDeFiQ;>uWSobTdQtR=T3zH2y3;1;k!8}2BSBxw6pt^eMcR4LAXc)J2wkQ$H zyHZmBLBV9)Ecc_xq<enW-ViwE&qoh4Zwo0W9{LN{`$LR10jby`1G#CO-z{q)&^#A&?%iR=#y ztMEZcZ}czA0KDn3VyUl8?@95B8sh4o#t*NkZ#Cch_R3OvaY)GLKc-Ln-Va|3bp?{k zMJqLSu9+LvQLiF3FBV(%dtI4Om_H{6w2;mO!L#mZe~tz}+#P6$v*rE#FH(xT(40hS zzTDvO(Hi`$k$KS?e{}8mb=8&zIGc~s4$Y5Qbc*h$a&>Jy!4u!0tw-gP=quiuqlcLL zo@X9?`HJa&Ct71*DgV#KlvDDyhKn9ZkcVA3L3G4GQyxV>_l)Bwy?-kIEi zuPY50els(Bx%Pefsq6L3bo=!feZoW!hbEiWE_u#B@le8t$2TK)e@JA-FXo>0P0rsa zy3Q7;%PVVt*Wd}32(M_yLKqekS{WFnaEV)X4>$7P$L3e7LHf~QGc~nX2yfk%JAPap zKSr@?D;bu2tf!>ilZ1aXB%?J^7V-5=#muH0s5#Cy(F%H~n0ftP7`5pe23m8?MDuU1 zbYnB58cp8qi)iE_yC*}b!#Y*l&8Ei0hUX1_p-#&$KQqOiW<5-aD8$?sv#g$SB|_W` zk@;t&v&QLf-M<=!WYJ6xZuM`;-n8oVRXKw1ePk4wc(we^UICBvWy4tG&!hGfRf#8? znoe}o)LCmfa|?!qy#H1x`iz^@)_<@F-zzl;7h+<%ja-IKJ*oEmmLaW{v40o!J0CnN z4?MgR(Q=%OAtI)OdgCmojnhJW%9KCat*v$^)b#~F&X6u48m+P z=Hi}iT|5@z(REXBqk>7isJi1y6V4hxQ*ZW#1gGJc{JO6d0xv7p<*W${qNv=ZVSbui zs9C^ZMEMUvp*<1I-dnW3H&QUYtT4(36MhLVWE@e_|DlI^TDLMPv^!ZsNT0e+5O>#1 zm3Okg9~InNS*t{9!uQxWTM(^)!TYgBDVHz{!%NztmN@wc5LS?co6Wg@KbHISq<=;E z3P-M!Ob|n%q2g4pf*luYH@hF?qsdv6d~g)ctEk{Vnl?wj66c)cC~gnT&NX)Q2fRTS z=6EL-ZnW7TK?Uv8F6?;w0owctA-5D8&zQyJ2^vyl&6qn3C--6Fn*ORQzpCJ;L5~z{ z^dO7Y*nc&w6jv<-8FdhOc58*S*+%0Qev`@PT3GidbG`4>m-F#8Q` zoC|zta9PKNn4E0RU1Z8wjC{^g`wSbVx*$7LASM=HflyIl9Vdl^EGsp5)hH@d0cykR z-{(~<)@?g7W}Dwum5o&8rXK#LD@8t44$8~oGmWo?pRCWT8P_Vj9;FxDcmlRekT;!{ zEsn1qa~w<5=GQdJ=kCkM9xNpBk_jT0w{Td!6C#^s61V4AhppZKBEos{9we&9M$z@g zaFqS#6sIx$`eAv;ds-i)*JXt!K8~`XAFPz-3DWuF|G=lZmOKju4YWJmcJ|W5*qW1k zX`AG<=v*#It|WLJ->bj~>i5u;7yIH&5W$0^bMsKHJzhy$Fv;cyWEmJ zirf(KgIK7sg>H9qVq31Iz_nE5^+wu^U%!xS;1(ah$+iol&OKx!3?6PPFA^;bNB$P7 z`~!!2s6Q^p8M5X-@G*uCn^o%vowh*l(OYtyCrI?jXiV*aeAhcxYlZ2g3se|b0q=zb zB)4WT_dlB>m!xK8x-%&y=kEH?$X}NW8V$-?{ZXhLejyYwAnaEZ2-YUWs4gGA*3RVX z6}_Ut+Q*`W`>0oF8($l^UW=qJ#{Re^uiy}o^{y)oQFo_;MmI(;KI2|Uw1!se5%$sA zyR?u*Xn3`2k5SUdw?dD9;I#VuxijH4TB{#_ju?^y%QW}=4S?nPCXxSGXQP;@@&WxA zK-a^*QhHzAj*%qA|50ipy2s5Zl5x!8rr)G{cEwne`OQrx|J}qbXkECe`O;2_ zo_JG1ZKeH-AU^my8{(BWxhz&%TMOe9d7sHU-_QIFOyK)N3e+o2Kp2b}}tj&Ehx9DVq%uMd49+6;&dF z8E5i*G&myE<`DVZm%v#QQHk`|AM_Lq3i9W_K}h0`q(IrGWBVxfj?fA$!zA=06X=PcD11o}+Ya3u)&m$|?OE^($p!bV2NyI4wHQ6!7c z%CK{fdQ6EMh%$T?o!(t0dZjFLKvbanF_D&akFan{tmTIB{gM@7|8clxe+o z{0tFM&OO7TNNms9MR)jW6rFH!;8m&bpV4pHfYOZqQ}Le7vU52w_M`H}W3t!P*i|uW zzF@7b9qfPZI#+7~WpYZ+o)~p9J~>H5kgB&AP*M-L>33&*`_R*^J6l`RyS}EKtWQ4; zMU{RxEfGX6UTQ~{1jAHaUqi>_2b015S2UDB?~JR4Pi2pyPqsIc|Idq*RDXIlOHI{} z2>pdmXOO&a=_>@$8Nd|U&542Jg)B>AbQE)&1s?(Y+ zft8bD8(@*RPrtZ9%A^RJ;^{5#w6bfczM>QW{V#s&78+*rhumWX5I)ai3{r z7=jA^)BgJ?|E@O2iCJFN?J>=2H1_3HOw&{lVNKQj8@XBK4Mr60;esGh`x zb?&ec*aX);C4`EcD6ZNuK*+-M8zdC)st(#N?zpUObJ9x#AZ4Yo92%ix=C>>T2drLc zawaqY()8wAJO=29?zmLXUQQXPS9fu=UnKy@nS%TSinWR|QcjM8%m^eX@kfq@#aGz8 zTehbpk%wq9$uFyr)}K*5_L0L(_y|de@#~L<#R(ru7qH7BjN#_?Vj7J2DLTCSSDSkaD|vJxbk7Te2)gm^su{iyKq-Lvnc z;#HG;7jKqpPS-zTRJ&~5Qj9qq@x_YN0J2-?wyJNIy6N0IzYrRW=5eLWesY(m#6lIu zt>D}{jJ$EnZp_~smE_E$^^7G`2?OSt*?ID5O^1H2P$Rd2 z;N{;0@U+S9C~)~BSATnUjvoGCGc(!VgSJ>ww0s=P<}>=7#JO9OYn_7|7DfP8phdB{ z(nnUjcjJwyq<-qmKVZ}1J{Z{=EBy9vN!!=0{d48| zSpB1t04%Ej0>JS|h+C4PNfOEI1J`i&?;ouN;@@}u9JW(awy>uH*F+R5o_Q82pBH~wU9(|G^u#JE`{Q#?d?G76l@{ylUMpvBoAeN^#I`5@7s zN&2>E*BGhV_;wNMYG)uL85j8>qL0jpq;0E0_y=Bdzj;^QWFcLDdH>ghyNcaKBl`x8 z_Vjp`VnkTif&V?Pon)_)zcmbF(tI6P0KZm& zQnqQWbEm6>P%#FhZ3C7y1@ml|PJ8>FZ(Q2fooK*d*`eDo%KEZK=PxhTw)ZJ10TLVb zQLlbakNjfy?ufay<{E_+eX?lpIP=V7<^MBBKei^tla@5~r^7r%HR%886#A zS=v4wtFm{CH#WnbR`ryv^!QmaysGr)FglF&*}EGf`rgMO#~mzXq65y*Z{(u>0laSp zk9wGu0rr^gIRvmu`91nFT<36bVWZ5d47t(teVVODA4Uc9O%i=4BdEc!62To2v2E~m z3(!tjPhpn~Nj(p@P4e9+zu+%rq-*2hA^s~WjQfG7JF)|DhaP?A4h~!d=wMh~N>xQt z5Q;NzV5jr<0^zvZ0~fv4cbi%%kcCTIYTdcMng!`jdKmey2ge)~D&C{TRa576Y}}k- zYbW4C4P^$^e=lZuo>F=^PyWx;L>EXNPN)yVXU4CV)LYULYy2=C9}=QtlR{u>vs#iL zk9vpBr3l$ZIj_$nk%l*`eSbpglP`%R!vs)x03@qOFc|C z#`AbKY+hfV^a*JTtz({F$+(mHv7}8+4-KP&zQMq{!yAulUPSfsDBXA+{~wE=tPyTI z_ahQQskysb@O4=ioLMxqubAn3&chAlc}OnNAnu{5ivKVIF4&CTqk+O6GyUrD_Q=p4 zZySvVHb4VP5>iTz=!>Iq`T#LQaEWL_HEiXYi87h6E2+4J>ii`aYg3bUn(c&Ae^vz~ zulZ@9C{jG&DhQK?bsXNX13_|@!Hv4`r9=5-}Q4E z$y%wec5yi=UbnIZnc!S%VdM$(*{s@v&|BIfwLj%LJ~0@o-#Twx;^B1%dSpex$+(JF z^+{~XaxPsWdutYSiMP!(j^Ic}p#FRsA-BYO)5`s#%7@%k%Tt1?5QSnf|DhMY30f6D z>6-&psQm8c6frUtBnb>Ujy=GW|68j?ubI}4LZQA?%2}=gIBJ?Ke&M_Mvv1NlKb5W^ z&%0v^@`(_u%$Z7cgmZG@P=t6_g?M|W6laqYS%JryLD%bhdj>m$2z_M$qxxd4(DP(@ zLlXu=b+HDPIiK?1|F5Ph+r%J}Bq9`QG8Z*L1u`ttWWLAU;K1$s2x<+7g#q7XJbD>> ztUftz`^9~?R_+Gmtq({@-Q}n_S;R(7lCK`_FQY_TU@CNv29z{6D3>p9EgKPc6Wp+D zWC0}^)v4so4GMd6eypIPdM|W^(@;VN<9^*Kp^z^sjtEi8lJ$*f*A~M1gwmbZr^&}J zRpa4+9Ga5?86pE^v}WHgtqR9y(&!#{a9tz;H=S~kjeR-=Ii6`6kW2w4F*^czT)_c| zjpS?5zmOn3@wzB04vTSDX#?u#loJ|ypbgb6RgwKyE1wFS@e8k<6BB%UY5(vItBSyx zZ{cbyil5G6Y1=doVonlSqAt^BS$N!EXIeh)tod=Wj-!5&huVxG%g_rMoj$KwKz{Lo@S1f0#g7Sn&*PnbD%&)kwW-~-Gu26l1FPYPZ2am6 z&JstLG)pC0+7z4IAh;x5eqdN!nwob_dQ!?ax+IFsu}Il`An5)Nj0hDZpY*DJ6peX8 z)MpLa1O6^~(8&Y0gQ8 zULgf$m-Yw9B6%YJRXR>)J<6)tp>dq44txE0TK9bSIBS^Jll=UQ{43B%o*ys9h7~5u zc%J79r8YH7?zumB8SG34{BskU1JT{33cd3vEBNxw-RhzXFA5CN_fR678+9*YYM!?~V970>MdPAn#dg{s?A)_ElbJ}t&UX2M1JT?EE{hTK0%; zh#@j(b%D%_LR;CNePs81okbfGcgpYx^IZYN)|bY6`$^E%*FTkvUST=mbOmr`@y3T= z`J!ik#(fp)d((JA))TrWdhb0}>>{E|GI^BhX3Ehn%aFt6Ezp*T5H|lFJZI5zPeCOL z7JErG)YS(@2WN3#f?l>BHAV+doLpaSWqTDw?wHxb>+6^w$WJv^7>n+Vyp6rzt3Oe8 zg>VvZL7ET%DKGZm@ql62@rF~cA`)V!a|YE=s&)EVE(oDBXBk;syg~q?(LkU7!{t}b!k1%#oQ*6!GR_Fq(?^!9t%)1r zkWM6<&kiSee0c3nefH&hN4KOXxn%A1??kWpQy0rV@N28^EcDe3qGxw$+_#Q>e{mQj zC5J!L*%l@+BaWZa61$o7=)T1)7$33Q~bH!UW4T zqHmP)Xe6&Q55TgvA!5@4uw^4Begjp$+6Y;uW-i|z5i0(k!%D>uv-D;oAA0!}c6-gH z+=dwh?~;Yb>Z#Zy{Jwm2KUyJA@&^TMb^S%(j7*gYws~Z@zSGuPXC3P6iZ0U`tk(!E z=mJ?1v4@KOS1k=VS9+w$a*Ml(H}u~rZCcD9E{6*+^@=5vKfAw^JoYK`Z!e2)QO{q* z=Ia`lbPX%wPUkDEbIm3z5Ppjj$De&-sg==FqTMNM04q7WYrhRaY0DLf{D;r8V!F!w zHYPQkCDSZCX-U2VTUfr&&~^;3yP5ddLOecWKoCDemeUO1Bul2uu3+H!0NL0oUn<3J zZr#V7=;(ZE0o4Ysw#J;zxFS-%pgNX)Nwg4Z`8RhW^Z958^HO0O6094sg6qd`UzGjD z#$!Mi5AdhP*}v2l0%aG*BAEYSKA0IuOo~iDIoGsD;|UY~h$qzXN~#jjKZy{fE!S!~ z(ymgP{ciQy^tcuTM6e?vG8`Y;uBUD?H!Dt;@^wQ@8Fvx}3qV`s2|7w*im+ zd>4(|NhT6q{y4;cssI;!9PZ`OtI3)N;tSs$FhAY4I+N@6Zt24t%g**t=aq?uoX;cj z8n~4yfoE*2;5vX&Y~;{)a5=6{z$BRpnl{4A4-@ke3igtG&}GQ;*5J@Uy0YHj^~cw* zaX&VhAtu%(U&wKhWU0j-dVoE5zt;GhyaOjKj|^7pXqh7ZMQz?+t?GCC|ACX$c<@Uf z7AHGHM^u1EbQh1|2abI*>Dn!nevEwF#^G=(y^Z}JNQ>Vmz+a%H_5C}Y3p^b0fCK?;IOZsSzuSSb>QP!!OJ}D(W2NOGd5PeVw7pC433?W!~(sC<+Xl z@?@7Bi2Fpocp&=f;;XRT+NQsnt`Twh4QQ-r`Qmml)&8Ye`W^lXNeEnPzU(#u`qe-e zoLRmhCRz9QRFv<3m(7SJlxZ!A@tVq*L0d>Sj>d~gSWO%iDW@H7Z0DW}g+}nXaFWx# z$fRCaj$Ue&#GlcEp$7d=Bf&peo|n;LJ7cg42P!b> zOG`dW0X7B2GB5J&7|VjM17ho9c&`kRHP6LJ`#g9!{rte0|7%FRPR-jv7h+E6nbhhc zlcwF`G%@jkvN?9;`47wPQ1 z{;+Lv{NiCD6e(ft%t>Pvh11Usez_RbXRN~dOwS;H;%1BU-7WkSmSxkUYv%{wOQ*@l z-_yoJIK#4Sz@uA3^MhWq(srXopLk7{Z=@wxsOT#nZln46=4$eqX}ov()DQ#P-&T)r z#aXvKVm~*h1r7dIs>50o%a|f*C zBSSH6?2;wm?pR2*W#&InmZ)>Ia;N8hn1)x<(v_o)d`54-qCp!Fb32l(J#>2)X7&y) zv88WM1Q=Ne!`|)45}gb0BMVoHtBHp06nOU7c>ORULX7u6+)oE_~l9gCZGF_-A^M2oRbE? z3>iYdp(8_a=7Fr>EFKG(5+N;#RE@#McfGxS#|ZM)@E6^gGPImsIX+41K*G4oKO}6I z_KAa=N@DkxqCS45etWlmA^WcXg+(Lv-Gf5dm%(cRMQNXw93MNnEc7*aiSvHD9BxY- z7Tuk%FddSzV-?sHpz+E?efrSwiCi_RUt1#Y{b-NLZ2fv6%#}{UJ%&WIY5E{xvDNF` zdCq&o%AMN+B_7_#+!DA#H5w-bOO}vO`o|Y;9!L2js{6>-)?$%q6fmplL%ZVM#qm*b?0>)KGgY{F@?W01gzCWqGuckFp;vI<9y&Ts zQfedF@QKm*x8*i~s>P^NBZC5yBdoaYD~-GgM9GLakr z+;##b$uC#@$GS(&-g-EZ3dpe{=6)*-bit&I#MTvYRY2-xKOMrnUCX8%s*Z&yRu3`wA(;5KGF;`C{i!>?T=GJpYEXBe*MHT_b z4ldb|ZVyjx7wi)talTlv#XMhw8pU%k5MaFNj1AmGCpI~=Z$+yD)G}0ijb=9EQUp3? ztK4cC?tnlykW6}jPxp(8O>(P6?b19&oU?5J$HFpR2Y$?8ki4BI@#n>REGK$~s%2-A zCTGY3g`;+9tfPy1N=ByaO{%dzBm(Se=)|C_VfCd;3APaln_QgHM$VE$AL60kLic+z zXS4_%s*t6N>+A|M(faJP|KmvTri^dmspzRt|42PnccH29Ge7tvg>_Nqm5Hz(o;!)p zcVwia|LEJTuHekWRX^HH9;JYOK}`8}9_NkPNuHEbsbRM*9-rA-m5esJ=0wJbPwhNP zq#FcuJxYDc`?mCR;i6^No&08KET|t`0E|*R#m02B5WM7p|a__cvGyTP&(aF?Mp1% z;2-^>GU0~$w4>c?Mc*=n3o3yXYf9#(p{xdpYyP>BwOMBq6X;nybroo4Yw&LV8cGr| zJp4u7j7_NZ^7vMNF(N>ipk0R*nodjby@utmD|lStR>`y+;;nPposHEW$f-c-LBImj3Z?WqJZPc^TfKkH%Z!e(WUI}+N`b4}f= z^Kw~RHtmwP!@E3BbDcdT#;|2szj}De&o=_gH#&OF-$x=k7#C*sCd9R+_h;3;XZ?-m z8P%0m>%%cl{gQvL{^afy_3itBDXc7S;}OyXysn86m0!5UW78c|__<**N)+0LjGcH)b5Bt_UiA*j9ayKqj|hsVmN z8wu;!UV#w?W{+`k6_PsiKt$n}5zN(u8PvFn^un$? zK9bAJHlgxEw{;I#HG==BiR}5ybt4DTxIvt5h_li z(<$P~jK39MSRm#g|Hhn)pZ_og)V=;4xqRb!np1g@$qie5VNzB_ahDihsNmKWljzwXiaVpD(E+c=|C=VXEEAqO7 zSk^XJR?4oOv*=HYW*S6FPBD}z94$c=@Qs5M<_k7{H`?`PNQ-`jU1f(%FM(}`C1POs+&{oE zn7hy!Jr;zX960&=^CaWBqsb19r(bIznp}p?ZnJqqo+jjuyc0J2nB|={ZHl?XhZT#O z){F5}8cy-<@DsjNmw zh+w!Ei8G}6Ddz8hVO_9IO2Qci2^5FB`DvJUM158m8!*XE$$W6y=51e2I~x&N+1#pQ zY-j#8@~oif66|lM|2Oj^-xG>R<+(qO zzzH1eWEWIt(Ke=ybrOMjmlgHY3DB>4J}SeQLK<6Q#84&zlB^?$m{e(|v2P=fJ;t3E0L zd`W)tdF%Em-hr=_ZjI}7s`cm`>(cs`Qtrj?Fy=$d_#5F<<6M2>~AUbwciqwjrXU8qXZgNFfS`1Q3I=e`_n$(_gjQFBHxjW`fS{WPfQT<}fS z`bAH=2xJ*6dJI<>ow))Vs#OSX_p#wnSYcQG2g3Sl4Xs*-_g{^)wg?{aSue~lh3=?= z>eFWSh6~+u-?IF#vn2Q6^#s;%954jfB4b6laY^;cYfnDEU6te7-ldNbVL9(N;tE{A ze+v>no@n+7ML_!PLT1de(Qh~-C4|Ua=p%E@ zQ0z96`ywIeSK+%F1gO`a@{!1Tld}n<(E@h~Fg<^xE|O^xtcv>r&Md0}jGY`x;S%W$ zR^|%KUB*HL(fM1{`w}o!P@|P%_AS#4C+yDz5`qPH>EQm|WRX_sHf2oGX zDe&k0BA#c`*vKQNdOfJXcP~NuzMktZ=vM~p#DGQ`=-{ls1Hvo zH&*M|RE@B`^oYOkJ!EH|teGS%pWkuK%{J4OKbJRVC$wYK=^JL>slic!_H?y1S8MPB z62^2`*UCN98l$=^L+!6#XYu&5wW{^>=Etv@GgL*DOadI^ImtZxgQcbZAE>lhU#tq6 zIX-$d_dBa?4*9ILh4E)_AUlOEFu{Up%4O^t#Z7BQEUy>#v(T{BZErUAK~r&RcWuGw z{lD~m9wN*r8MSOTcBIgyXM`Q0iS)+b|e#SvPL0f1`@Ze679a8c* zuj37W57I-1LI7hCD^{^LwBh=NsEh6oj35um4Y-FUJ{O`xS=IbkcNZ7ye%qv4p2*Vnwu z-^UT5wkC6z;W~?!GvakxJHS4&9pJ%x4SUQk*8U{T=Ifx$D3) zu&B@67Ay=uGIj^yC|LP?ZY>tqhR^JP{E|&Ru0jQI68;Qh8|wMkDiy;gsS*-97&+Y^ z70#JEG?wQ_zr-Hy7XNBg6n&(2z94`WBzuKlX6b&s&r|7fti;(Rhb)v_rDCfnIGoyqfg(o88In;tro1K?x z)ZW&tM9DQaiw_+cFE!`UMRNf{X^B!Vx#_?9^)#y3v3pajUcvs|TW4~Z7Unr>lv zP%zQO^3D4GWIDyTg+TA~Nhlcy#n);X5|0T_)Iujtz*yUf&{XP995>BoX^xTi4Dy6U&L~T6E`IO0lw1ZY3?5xzTQObsQJ>f(!AOAtz9b&upxQ*kif~0*l`%ScHAp z2J~me?$B)3@g(`LuU`xw$e*eX<8HxQxw4RY;m>ba+ua(=BXG~|@-JzLtZxflg>(x| zef%T2O0CTM8AQDr<~arafgYpz%AXKLuutGsefDxNqr^ke?G#(EgTa(us4B#^mAbTe z>bj&xsc1ptHt-O|YD}#q~6A2q@?qCU~mtOmH zbne=>+=`krE+(tAw`bxRmMESciUy91&s^7Hj5i0sONn#4129 zo2(B8nJYO&hA{A^{zDPVMl!LzFeQqTo_+}|D+Nrlshghg$D;)>Mc;c=flDq+xs({I zf%p~bv+xZXh`-zC!`{3l6XhPq(^=KmJ(mu5C#*88uacF%pU# z{}`nf{-cHT@++(7W3fpJ=ukR2kqJ`P<%^@5hWq&DYhr`JP+N7x8)w0n-{UJm=!RwN zERUSEr`Nf^38$H)XN@rxuf5Vf8^89qF|lnqu@ZOAc#z1bL3X&aSNJ(|5{nOYrD zmiG_blB1Kb0tf#V%&_Lo}@ zeoEKdSB()sjFpA=e5UD24#)gZE;a0>I$!}U9tf;BmpF%4PWdm$P$eyE>26cuQ3{Q4?67m9EyNgPvT}T zseaROBmY7dj!JlYWuNCIf!o2K4AM#Y%OXzf>bDW=a8{+sf@8H@1u8&-AddWqU zTcz`X4W&9_1zH0s;ENC`jvF3DA^83uy}GkY-0tt}%o7b= z#BxNBXYSHvpb1>DaVhJ0nFJO*J!EG-JGL(xC6(BQ))%C)9fuFah3&n1^o3h{%~`Y% zk}BiwD2UE$S#2i~petG_&(R`goBC#yOa8sREPnUbN;s9}OldnbE&|K_b&Z`|$DlK_GkW}AaMC!mPp?TGOs_Dr-Oc!0&3>tr)8>;kXjaB96LOb z8f4Wa;pdi11-=>|xXj#|oYJKNpk%=)y$9b5cJhO1EGCskM)!-@1a=se8V%ghf^9(9 zYAEHx2yvb+gQBq|G6kV&$@9&U0thfB1Wj&2Y;>JKUpFn&+}`hHT^5yIBitIZT@@>c zu&FxY7|8Q}(G@FJKmS3SMdf1L=T;H5fGkTRL*RF60uq5!`C|LcvY|0rwa!10ZRTju z3qr90W&C-^FPx1w4?`BEAgAtk?x%$7y|;9Un)(iD6Kd+KM$BDJMv z!#)FkP~FpO8OVscn=H+q=f8yg2ZX>faSU!o*;+jC4@9;q%7raZyJ|M1bNyS4%)v&LW--y@ur$ zStoJI;-u!21tf$35sg9|=IzosRoYm#B8JE(-L~_zgX0<&gMraZti`RBtEm*dlEfb( z1lAZ1mUGXi-xt)T!#n@L8!+#f4WmCb%%-buWY0S?Gzn0flXK8;zp$a4Tn4XZ@hFQ6b_zHU zSd*`CKa24^3m6)1O#0yZWQ{ys=SaakM~>n&y{N+_Xt5h;m}46&-(^PEF#cOjfgzxx|2bKC_vzQPXM-EGDRz;^Zhx+borBv&^+ZGy6f(>E` zEVpClU!3_oYnBp`*^iT&(no+Q%fEByLmBQ_KOI&WYC+ZVPmv z+pUzfJtvaoc6y#EtAS&XfZgq#Ho0}~3(CqQ(>OgMqyesJ`Va7_*6D{6IfEj{e?lHY z(es%!o&t(+R2y=tdf2O60E+PIeYb<@5)lKY zcUk?Dv7Gu-HbO}HbvTW*s_sh?lr8~{!kQjy56a`2zbH^L@+i;^U#vPaK7tLerH*qz%g zEqzrWh-(cND+dWs6G zwgzVU`!bE-cz`GP8lHgqMqR^lOHwTsO*!3`vFxB5$K>v*RFLr|0qZ|f{Z$9w^W)%% z$xs^GYG=Ew>|Z^9LrDZEX7k4zW00ok9(P6dj%6%YLe(!A9*u~allq$QMZFi=Z){>D ziSX$^?c zNG(Ej>s|tpLn+e+u@;-G_ z7;|~P4L|zX41>#iVfL}+@1AMmA{FeQcN(;Q9NzizAcE=AGozp^vf}p-&_j!6XOpu_ zkRl6ZLu8F(*`i$8XU9o9$tCHc%G z=>m9!$;w_42b_KmsNTyJtN7(iA-sw)c2{aAf{yLHJh4MJBFIs_JvpJnmF3NrF40o&;t5P}zcvFqoiCDgfjEjSGd65gX?e53Pk$(QB z{Ko_YJ5?dpqD?bHnamG{O+IY0FxK-vaDM3flZg{#nOI&GjIPmdlf<6N%ejsCWM9*;2|ct5|wH!4^talc=5 zU^5t&{V6Zn0y~C;UOsjS2Er=|vPlI0W;M+|F$U!!bM}Dds_8;zwQ}2y!z>vL8gP;* zwjuH7TL*#_J{YHwk8_FHs!4xM@cnJBB!7}x83}Q+q5~}BgoBxF#FDW)WJ_3eF(rC} zy@w7&*n^TQ23SP)`yPd!UVv#0)Hc!B$nMrwAjwBgF<3+>46u!5h0@>^FLE`)226^@ zI2Ibt_zHFi@}Ixk7!p_C%Vs9Rw}_qG`azd&8KC+sV~^olVokqd+2M$XT>V7_B>)ze z;;dKgc;O@d$j*N82klr-$iFra}v~(YeNx`)hhx5iz8`o@1aXtioeU9eA0XGQ??(R+FO5s7UG;NXfWdJ+F3ZeaH($Q9Y9v1rvjK5%OzDWWlp z?Bi|XOaE_m$|{MY6Ioz+BBNUT8OoC(O+LPp7oR4vZ~h!HCgaQm7t=S|P&0_0^lDWw ziP_RIb%V3zo$qQwKm=zbb}+IU3k?RDn^>kw;|vxo8!_mli>XsDFWyua6cg=JUq!!u zJ=K5|&H1c(NQ8Cy=-asMyENQ7;wwS!l*e!79`U`DuYBXYLF1K{xV=HK_(QTP&gNXa z5L$Jht8#SwQ2r=X2yBkIenhSUG{ek3=ZL2-{@}f8%VdKJoU=tM1v0O{NF^1D?3K>X zG$n2h4RdGf@prq6i3|ix-Yj{le>h2mNsxtA164)#mUV%kY8|$-?VO-0FnsSRMfZG z8GUy4)52WZH*p^&yNQCyhY1;;(j#HAG#*LVFrqKtNQqJ#R$#Zd`NvA>_)KGo6#_KX zh*WAqV$B5n`_jmqvIFGXlf6)r8lGG<9SJ(5`gu*T#~993yI!m)D=L5n0J5osTin3Y zeXY~f8CF!95W=DJ55!Z!^kV^<|4qh$MA1K>t#-|P-L0}bDI|Z)+!abj+i|)9k*xcy z@=D$8Z4lpKD!86JT!L6qY6F)vonAy3iMoBGv5^(sax?pTCiq{yveqqhf8u@SQ!-YN zme}_)+5VY$li?3b|z1Gw$7;{SUAQJx7laVNCYC zW{iBl{zKKCRUy6g@aK9^KCGB?MO0c5gbUcq*VV2o)J4r+|9PM3F!9@%@K*e6=*FIW z8*p@RMu_Q3m1`Oe0V{i%zf0OBtV!q^Eo-mEW~rAR9UK(dV812H(I0K}6aviw+U zHD9Rptlz-0$L&W_62i;*>sc9|q4zIkv1A4?@V~UDMEe>SMzp)hnmM{WI2heJ`XXh! zf9fT2yTU@IMm2jqcCm4*ovb-!umz#9xm`QiMnbQDr0VlI<@^H=Et?qRH%M5Nh^y$L zK$?L3&AQ*0hs7mD{tn94pF_brQ}})oVX3$d`ij6P!Qe<=^f0NNGot zM1Zof-K_Kwzracmug^YP>cvon*tqem)aHL-d|t*&Zh84rlZbyw1lBtuFnqy@6Jgu= zZ=!;f%tCEH`i_u=eVfE8-m`FCK3sZ2A2SN2c|D84E)S6vPK~-H`S{-W*<5`vHn5lE zqA2(F5s^DCpL{`&<1v@R-d^F&po?q|)Ze~2p_x^A?BvTY>b($l!J>0Um}1=Y;kUwh znS4WFwcN3YBN2?g8g?HcAy+&*+uc=U6BhF2%=k73T$4>!TAcgH;=EF6i_7?LiIH(B zC11jH4WJRHe%+ry!W6gXsamFRXQO4q=Cx5e&^Y`2U^>Mg55!4izV^#bPZZhorC*Y@ z9Q*qsDnC|$US7I7B@kRpmwB640Hc75Gjvs09o-DuV&7u6UdU~%Z<*`{L` z^;EE@&aYj#>w41Gpd#+$&VbHL-Sg?z-bba{KK9(AMA-WmjMdLqgPMFN%1iHA6xt`{ z2S2-is+`!gg5_}>@oE-s#NeA(`~JhX~Nvz8W{R8>)!CM1uB~#EHzY%-tRO-`t^S z2`(YvZP77n%8BJME}+W_&>)3vEGJL}+cXxI0J&k730r*$MKn+-gU*8`?$9}X@mlNqz3?((_9kcLKj2qNlSCKW$Q(qhG>(@D z0c1`xipH9g?6Z@r=|Vn5x!%h|gt()fZ#W}oI>}p8{b!=9otW%Z9sQ^VuG$Z1;D0!>ih99rpDZn5MTmDQ16m$ zcxP=Qi}D4m8X+N$Pv%n2yIUtW!AgSmPe+>`muV25*KYY{esp?$qaxORY^vbSAIQ&~ z4|GBfSrBpUL~-E=+_5h&=l_Ap!En|E3erRtA;+B~bI3VK5uS{kNM$6HJ{SElm&0IY z`A1C|dRPNP*=1$FaZi#2eQ=KE`2Qllj z(z7JaHW3OSvUDcM4ixO&c1j4;+6K4dQfgLZIT$vk>|j?F^mk6j3I$aRo|~F}Q#nip~F5r#YhvDpFiC2|Qs0Xg_ZLr7m!GCnhivm3* zfKz?&T8WOr+gHI^ljha$5agW1`hCm5%H)b{ZLMv{_<>gHs+V+-FdGWi*C14*4gDWa zU;Yo}`$v7v3tcx$3gTF|GorJkx+qSGT?L81<@v* zAATPs&bZxSo%#K#%ALloIiPf`So8L3+c#2Cc>TkH#KG0Xw zjHQCvFPBQk1#)gW({5L4g}-<@(I?B!T?sDbzjwBJFko%`yzHx$sd6LE8m{Fb_m%l+ z7nZ9hxpBev#A!cHyV%4h+#)7PXOEri^)mxn9~-sa2ScCDw^qCLnv6;xE~nC$dKSCxnki*sptRPn4Y_ogJIyI$uXg4Y*u|M?L=H@!X^I5Ef(eUz8#Fa_ zjs$#Iw{i&!rUqQ7z=I6lor8h?f;N#3HrR=hOlg>8r;Qyk7uT^oi0RB8tW4ZpR)0 z!GY@EbFc61NI#v%3SSkfY;Ih7oT7y(FWME5MfLv&q&BlpQVQqF-=Z3`p zP-oHH=4v#qPqISY+^f>Auv$3%zPdr>-NK9SPIhu-)#vfg+~@24qej1W7u!8<8frk& z5=$bR0Q|(mpf}Y%$dJivtBm@cHdW{~6O@6r{`?{qS&5+epxPveIMB%PfRW!pn=Ol_ zSe15SSwDNxe&JvGg-y z7||WRv%yZug`*4coaOOc!IaHXLq=)lz=gOVdxR2%NJi1v1uGxnA5*HA^R< z>Kv|hz&Q9yZ-|S7shUzjkB9V4(;kCH8W%>kpi9ZmXftm+SC%8>C*-@L0r#YNb_O0e zi~0^#?aO+~Z26z{d)`r`z%>E*#{Vd^wU;La@ZZuD1i#vJN0MUv1+qvds$Udh4zwWta`rrWX_2aFXw#n~rLh{#*dARZG#bHgc z9`BZRjvGG8h}~Mn^7DM0>#Qv|^K|&fOe76Gb{(@0>$JuSsYVV+H#Z3t{#Ro9NhFlG zm=AI!P#qXVCE^JDN}>FAhsuZCX%1JPc`)!~3UUOb?KVnPUz>EDxj?9yrWQ+laX9cg z^oRV?v$>Jz{NtiUscgjDeVq&I+^rSMn)+dPqt&nNZ)zt%#{2B|!k*o|=aaJ>U$g8W zutS4V4Gg`5<}&nwpYYi}Xxnx^{9=2F%LGL%DyY;d?(A!m>hlJyr5bCTksf>DBdm|s z1xbq9Rv`r`MwoLcg3HrtUG~?qOO*hBUp6=%;~6yIifKL1`rr3%b@qUDaS1B8+sdmM zHCL!-Fj=!jPyALR_KNcUwswd_CjO$ghg`NMj>&4} z7XR|)0a!gh*vz>(<30CSoLw)(p%5CSOvi!V=4#6`L?mw#n;dviHO%TBiN?runbUVhHcy>VZumcfcxEAhKBP zot^%v$osZ`*9_kOEi~bVC<1R8-=zvj^V*Ni0B|3xn_R*cGisE{q0*h#cGXgux3%N1)grgAh)D7zmxVAcd|Bo%v%^#$h6t&!WXF6Jmb8V&rAZ!NLlQNnFAHx+<%fqdkT~lND2_6w=O{5^$ zY!Ve(+bViV-0odam8)2&DaCqq-yB$_-_iQ0^ZR_Mlu}0U^F1oi(+L{IVQ9R>zEnSV zNNB^J6*Q2Yn;Gr95vl}P#LKGdSO2XH&3FH>4zD@_ex|lPVjTCnvC5Uk zSmC=zfYqt=pgrtVUdu$9spc!)%`qnm3yQ|YE&>}<-CG!vU+(fBF?f78CvT;D_jIA) z*PO;~-+y5!g5dKcNB(Z^eS^rf8&)#wN(V(suet*(X7Z<2@fnZm8;5lzy z^O4!rnv_@R79NFycmQXDm9xw1j%g)uf@-1|rNIbKf|67;m0h>ToWu+)xy*1;IALSL}(?MUF^|B8y|!L*5!=&GFit-v2U9kKL>KW(H~da(?U_3f8uA z=C9JauH?9Nq$Q+cqnjxBea-CEi>tB!$xAA~`2J6Vmvn+#W4XvDw3^)&lQ4XXf;aQu z_Gd=+5}ZON3-48V{?5fvR+Seb^AP{$VT#Ycd)sHJuS9UmzKs-2BJtLW2!1^oAw?Gn zd{Xi*e^KSrhG{y|ZCZ?^`3mT3K%a+G5_>L^-7clq_rk&@`t5<@cw&t=T6{OWM+q2q z6Yz(pr>{M-wFmxug_AwITzc-X0r#9`Q!FddE@AQd`l;F@Q1ZyG@I<(iS>bHui|lmG zek`K<0nQ?mp3}HVgE*WITEydJiuP{NO5@AA`E7%cR<5agIQV}}3O(#54g79b62m7= zSF1eOb7r$K9mRVD=4D`Z4zf{Cj2{2Ax)PR7Uj0-%!DZ1*#~h8#0$=Gs4#G zYb(PdFmMFeS}qDstSpa{)WhJZucPxz-q+q^Xa3U6;bv!vJ%$@yyLV<=S^1*Bub1rK zT$1LeV1W|;6QhIH1M|NNtui+x2nW+Pj$C|EYTfS8W=GUk&IPxO=vwn<0o~YD69lZW z1(A;(fIoWZT`W~zT_SVzCtTO1Xq_v1W`RoeO+z@^Utzs(fv^6hv&-KDNJsef$O!SEArsR&sd9s_k<2NRFx!1Tjm3Jm|NzGT`^m zxh*BHwt&5pDr|uVL86;^0_?4NTgt+Ek&DJB1Qd*DED_j`N(|!#^9{hkIX!=z(KA0A`LhJ!qNZ*vYCFPH*qj%jaqx&{@Bkf68pc{eiZ#9jVUXc ziNZKe2I!{KXq5~TxeyctWW6k2B%>fUrWl->rb~74{@H?_igHm71?|M=;9OA0VvSun z-gPOaOs5eAi>~_3!>{O=sMT2Pt|>zQr0f0yvwDroL-5k|`o<7( zG-rs-%)rv5K-`@CpCSK4Nyd#MkWBh92WvuiQL)YStP$Q!nz; zwzS+~_4zY3WZ4hrW&eI*4o+-)UU~KT!C`NRl!EEvb7NV}B8vhVw{^|1(qce!wJ(e^ zfgHLsF5fREAzqQ8>*UiM-yykZOa?3G*4pQnwvRyCO~;@%t5^FJ@om1IfE*}E>DJJ_ z6}1O(iVIip2Chkoum1L{a!f0&Bmir-?omd%IwNvUrmr0V24@Y4c=_4rGX1gaCYFG>qJggrV z40KmbvHZh_LS+Mx(G&-yT#1NXuXjb2MDEWI^R!aSd~eaHIdNn`Ehh+?n9oyk-m^Ud z&nhm`EhN^^>(?_ki>wS3M^gpCGZ_n|=^o)!>jKL_Gr_(8Xxx{74`zJbw6`9(Rr%}K z*>5*__fUlhVCz(^^Q|KIhfWiDpyYaMps>>`(Zk&?;GL((d(zV)vfY3)tU0TCSv#+)76xKWjh@DSX^1`5{q0Vk;GmQhI+!d%K!U~nL|lfOR@o{M83|$DH8gR$$pRW zs0^|CRpsFDt3Q4V%UkY^KU*7=&2_aaKm|oH!$)qcQ7QS*^ij*$MYHz9ZTUr;`418N9TTzGliV0+W_^tf zrJ2Qk&xXIW^tR4<=4U&r)=g|4)%V+oU|Ey>VQvuHj`Fx2^CXW!uAlbrxw>BfUGDdY zNXD}=5wOCpg{|W669_wXSB^hy14x@?B2@YLN6cKl=&pe0LqPKM&mth5e_A`vRz@v~_)OLOkaugpT>B0;Ak_xHWpt9se3qB#|4jhG@I5h#4Tw? z>iUTmAci>9&Z8?VEq{tM|DN7ps0@3WT&)DTVBe;6LH}!fueA${rnj;pulAk&T+;cf zi>W*;o(gqQ#tXK$ZEgr_`FWyT#@mEY5cpV+C-t^AZh|s!nQnadE7ms}A7y)IMS5L$ zJOR8KYq|UehMzpUk(+6!$b$ULxW>Gr*>i6m~( zMQW75kWT${u?ZZaJ43w#>dTc)AAeMvBk@I)u6*31zF{~oZx&!Bc-3t@Ay! z+Z;*ge+(a#3~-7@L3y$!>@&U4yL8E@%}EpAs07g1D8ngmy;w3iUA?dJ!F?JtXOGfh zLRbA(Ub;I(D@fB?&SUou1<8(rTp=}9V+q&1{xEJ48X8MDHK5;cj1CtgK^p&o z%H}HP5oqUPA1=DbiTwFrw8=LbQxW&6vTGtz2dns>Qt4~a`{Bl$o?s(az{OZ{Ew`0x zdL#Ca^-s+x42{ipF6hIcQOBlo==Wx7Vmf!q9OPSV&-7JnYB<@7iyK4Z)?;W|-+NH5 z)|QMG2`_oZL$rG-GpoiKxp?jAlX?T2_Z5p=IGDbj#Snz0HA7l zkf?-Sd2oNQvK)#mSeioD~Q;+jL$JKz^WBx z3R~cp_W3-Ths~BwMHhnraF<^QZi2_kn_}5UYPe60zrGhYX3_qlXQ-|qm#2BfkUeLs zRQf~e=(s8RS62od*gmET#Nm9d%r)4V*8aVf!Ld72syqwarZGLlm3qGW??0IasJVG- zCqM?Q7_e9>9(*1yw%ssIW8F>klj>~m_$Z_$^>qwJ5q?@6Y+HQi&X(6R(x-xnyz9w8(f^gy5__KA-XPd(kvFi7bh31|@4}+>!yL{G6?fQ!k4HcyXLqw?z2*nYr^|7j6j$<6 zKFF=kD^{mAQTA}~jG&iqi@K@{lwThf8+dLP1^xFCP) zi5-8`d=aDDCFLac|A*oOTTWY5-#+_hB-26gi>>&BX|t!#*~G)G%xe-RcGM6P?P6*Z zRNxlJ;u!tGvX%dgjQ$x7!{T$sj_d%l;*m2S&PWK4N{EcdvbwQ9tDn~@Z20}?@uoAE zAOBW~073w0DX5jKexk`F$@lxxI42oov^$i(Cvf4M^+8kg=la>?&+y^K=dN3l zH#EX$M=x_@TS@0FWLMS}EidH>NcdiQ6rUQ3c*fdsj&$~Us4XVnXC^(BgHh2uX-Br@ zho!~@)8K5UoIB<+Uq`W-qlekEnK-OZDR3o#pRZzIu&q3knRwpAigX{5Gs^rLucDnc zfokU|CjvQ6^pZp*nQZ;HF_P;4C<-oI0b1;0W@4+TO}-P9fV|gob=Z9ml8hk&{24AQ zIGf@o!C}ohU3^jU3wuU4iDfMOT}%wigZ|eqLQavNrBZOvf-&=ZDaFdZf-5~HrXCkh z)VPo&o~rGJ2|7@U!VQV(h*k%rO@Kyc*)#%zpR;}DEL=(TJ}2q%(=&MX$C>=9H+~k^ zJwd6$tdUTIm)hhlIDgk&qs+CcER`n7)KCPZl1A;U(zz@;VZz2g5&gU5gmwRQv--Gk ztPl6+5Pjkx;r#{1`|mgAn{^(X(y_z}hp`tVUO56MTvBdLID z3h}b7`N7ofd!J1+WXaA=ogGL?Uaxd`dET-^lvfI$Rf8R3$ml%kg~vu#Ah){Dw`QQP z>5Y;wH!L0Vk^{x2|GZTHx*O=Ln#~TjUWHEV%;n_YO?k*ZPP;My65IcbYxOp^(eDp- z|M_(MT$~89&=-pY6D|oHR1=#;ECg5)#;L~AJ)X$|G?tJs_JDD84q-E|tp^Wo3r8dp z`>Uc&u&PkI2n`%_A!x&qxCp3H8yM0qHOOzYr>NQJGrC)yq^bKv$t#A30TXchM69zED*{rCgfzpW&+5gP_G&4^PzUDrQjW0^XW zKCl?{I;Ud+%j3JpquiP z5buzPlq-@eGLd^Dcisfx$+OM2>+%!K%4gkzqUM+O9DvXDa4b^4RnWkP5O34O3t2EQ z4^7~;-*YXXaeeqWE!RCh^^QEdFX7AaO7p}ueQ@^TgCij9cW~bG$;SVG4w%-?#!zni z=Fi+oxXfLwfuS0Blc=Oyw!1+Bi`WzI*`#{OQCb}W=aOvrw{|k0-)~&NLO^(ANB+(Y z^3MOxv~`^%9T>qsLl%b`;7xMDc+KD*N#&1v>33*>Kc)JWzFrpemru_OrXBN7s7L@E z4tEq(Y09(Jq{^Ap)RStZ=Jj{0taY+Z?Aay1eSIz_elQ9JDQ0Bd7Gk~-$HT4cB7@!fue+g2OVcZS!{d=qTq7%RD#XN=k?f=N0_yaz9B0q-2?MBo>=s}%{7 zPmCFRY=MP1vsjqC%$^Q(wGj7d^}Y0pA4k9*_vg=l_OSq;?fsg@+R^b;oK z;_j;7DP-$BA%%mjoc;9Z1;2Nt?UaqtcA!$636uFj@KnJ{aPICq9COHrhP}Z^7HoUr zG4Hq=q(+3JNo?B+GqBTeiI#9>yq&GLiAThbE9H5wB--b@#wobN=vpX|0J7x}i8r-E zn24(Yzr5kzX6j^s%09&3il19?hqz~v0gC?dU-B8kpSDT!r;AS0fkMLl+==a})DPw% z?hs#7eFW$qO0JZaNSz`H9=lBq`R5WqMvb|*>Ied<%HOw=m@Gohzighwlt@XQe!wd= zsKkFs{Svzd$qabjh0*w8r}PY-5+V#c54;>Vi%O3lf%cR02cN-u{lhVJSH7R2@FNhT zl(98146;inudRmHFmA$2sqV1+q65!fCy)3`SP2;eFTL1dV&WD8GCk=lcio_c*zmOT z2J{HLb-Iy%=IPbfzWZTA4JO}T*uOpK!=C8Bc9DaJRg=eK00*Zo;%{#|mDCxoye_Z& zd2(LGC~P6oZg8`5z*(1Z>2Bj-@8i1x>6!9aVe5jhcB|>bcOvsPZw7bv!z_-#HSUdk zZWD0x9NG{c0+g)F^o7>2EO0u)TD}Vq?>#vJGi-cbT$S;YSs%XkIoq~?v(7>F)5apJ zwg-ezMV&S2mmzo(<^r!N(K79aj_!h03}Px z{&SaaQ-&U&IUNpf0KrU!6^Wak`ipqM<>aqh!STowv8z`{OEEO~1d-kAeh!E6T#kfr z0_dv~VwPRdi>yX+cj$1z<%8fi zm)u-P>OWXUSdtu`fC*D+O416XRIkCh|W1UbFve!tw7)UcsQ*~z3(dqBU++nr9Z?2}e4K>HP zZYd80lq&xo+xfa>-yM{&RBB3N%55){xKqT!Dwbgs%tF#E#Z{|}Q2OK=$4xRk^JE_O zRaJ(H_YiK}=cxK!7~E8_5bLO!j8D2$^jAS$8u(i|N(d-oEWaxkPIkoIy)~NcCOlOm zm10`>Y@iD*UaCAAq#Q8rAWH|tm(u?h)>HN7dj;1!zf{gV)oJz`e8$8>6l_gy!awMg zC_a8R%A5-Y>%l8~JcZ_5xxvaF>Ou@u9*^c&=!~Bc=~4FkVe&TewDhoe?JpGUdB4)J z!d*zLiT>)fh9?;;8Sq3NfmXIQd>zFS%Z$JgaZr`8)|xQh9|vz%KU+Pl`LlS9tM1L& z#Iol`ygJYt^sGR2UQxR%3uIf)=YpZ93D+lakC#{%s z9tY38BZKp&d;V-DAM{&avAe#uDEYSnGE2Da|4O9UJ0f} z+4bV9CBv9L3$hm9_7?+>pGnu<2VAaSq;Bdj z*gFaKhxXh$=X|lOoAciPCIlAq55C(W!nb_T+gr9F+iuaE6-Jek&^5h&K?Tm&9#L}Sw|t{0h_%Ta4XAfo~Sn(?1J1=-7ltqx5X|F%lCW~8*wumi#- z-)YW9h^47|go6G>={>z#b2i#Bvu88M=BI_;NTnpac<;sjYu)4QI2U&H@{@_q)YmAO z!?Lh@s}I`BZLC^3NyhkY9>qWV!j8SXkTM2Glm1l!YK{@RdK_WK*eiMJPObvP;i!1b9YFE)rqJ97!}|HI7J=FVV)qG zB^APw)*sG@NyR}K$ElDmX`l!fp%dSeFM0$lsU!GiLp~sfGyhm^c`KFi{OyR1#0qhH zeqQ4l%W|A9A1c1R=#?h;Gpr0NdCnq+_P)qM11Gmvz3= zxZVy$U>C9T8Tg3po7HcVZeiGSNt*qZ&U-VZ`)B{`FEU*2CRtV~KJE(kNd8jK72J7y ztJGpb)fer3pDY1i3HeC7U>G$2_M{OWxTX`Ht1?YIB-Vs=#h8XJX zJ=YndaiibsEWFLw?%Bvc@AYGByfeJR71s;H&ZVUWbRlTBNL&_oEUPIybQt19)F=)X zygEbeEcT=I4B2Z`N5?I&_kus`pH4IPgF1;YqG0-huc zRBd7|!9S;`62n0T7k3CokS^KZ;prZe#+U^x#|kD-Um$(r5-o1jzu<0hUVXe9s`;4Iasqxn#=iO5oI|w00xcknLBfE8f}Jg^U7ks{2_l+)79%dk#APDOqyx2*qI( zkT`gT(YR||=rR>uRt?nW-b>PrZmsKM)F<&e$Cy0js*iMfO}{R^ z^@zY_#IJ(~AB^=8kgh+a#;UecD`yX8v_e{Zit@a@h9z)6uS`UK?D84ed5RwoNdIR1 zGFo?pq*i$yAF74Uo5g4uT4L{TJs}CJTOEOu9(6U{XkCeEA$JJHNs~u?t0b~X@ZzeJjkOF~1qE{fn%@ zOch;Gja2!k4YVxkHvWR1s~XEaTXvkBh{m%6<~(0FE`ase@b1spgg97 zyFS*f8fe{$f^~VHNg4`1>2_1Cm9^yi$0UZn>xHPI%lq&lThUGWkfwe3<&#e(q8#|z+FNF2n&XJ^*kh-7 z+cGSj@;|I|iEMT5*(H8I0wtC!QUe{|a$#-9_j=(;qblFr;xXDc#77!nP3rG{oish6 za!a@S!Ei=TY1B9?QF=6E%NyC;-4n=)(P3-=) zJna_Oa!H!y3-z6~a%vo&)x@#@9R~fMsHVXTPJ7q{FB%Fhd=tutq}1j}WYA?knmH3% z01CqoO&f`kMKXHfv_IB|*p9<3@1awVb!Ip+R&AF7UvkMpX~EZBR3i@opWrN90cnbRNk5SU#OX-!Ck0tIb|hg0x4olo)s$PMlv7K2iQW{Nab8cY-KrWy9mj^1Ecw z509?`u7v*~%}E=kGQc%Q;x%S+cZ>~px>x$k5H}FT<)XVz{_OH6+D9ES=ci6B^q>OHtwBNYk zu$BE`r_a|IAGXzNMxqol;8x<}sJRils*ixYcraOLnj{nHn&H`;{Bc-lE-g5@t0NkENQmx@YO~WQ4r`G5&&GElE`>q!q&oPySI0Y-}KAZ z>2NGxO zCQ?bls^vQC-MU+d55K8bCyt-kzzS*c*?!)Vy?b&__TAWAuw?awJ--TUvd z=qa=f5_@lMoE0x0dS6Y~PV+ruv1RVZ>6+;@_7k!;pK;OX3c7K@!bR*w{J>y9EU60w zjPvOHR5lKd=y+xnVt@}#|VnevdZ#Y&XE|5qAN=fFYFUvTF$ z06&?f|FIb?`JU>>tbu76Ntl9N2i9a{!#hel)Os&!TVP$ z9iGJYbYvuINy&#>$8iD&3D@!L@7=^$y|(q&grTUinO>+5!)<%nLX^i-&Iq}c<6%PC zrop9{t*K81=3X7U9L(?7Kcj8mxQ~MH5zKw#00yDIdb#7m_jds_Hee$}AU}eBaS-yb zF@^O^S`|Qi(-T1BYvIH*nA3(P9N8Y91{|VB$-tO(93iNZFEJN`8mnut+aOujK5>UI z{_|tDy9H}7l!{U1@?=R&86k<+qkK65=Cpenq@dP9T{^)R{~1?y5r?rarHt2);8+w-R`udq!F-8~D4w_>L{9eQ8* zaQGcQc_;dFhCfAg_&FPg8&7cYUD;+;TSb-&mqS|JVMAR4MM4@aNvG-%`{lxFnBrM~ z-$ZA_G8z+I$o->MK@g2u$m0vT;(rqs1S_fJk}(_J?G8j;UXQ1bspUA%kC6D@v7e%g z_nwCF)8Ljyc?9P*Q!Lwx1)MG`6y;9iatB!nz3MmsS~Q>(G71urV=MaGcQcz`oUFU= z578vP9QD0%D~2*HkpHD$BU;7o&i1+2=6K*uWXS$#^9&Yhq`9uqnBaFteeJ@kO6wca z!#XL>f(1s{7Jxn-wK2>H|Ja^O&-XXJ>z0upTe%qmYc17EkRhBoi9zR;9lZtQ>uzOY!Dt_0a1! z?+TEK*Ul?G$I$4@ur?vg{v#UG2MSX+$iU<>ds00AZa##3*$;00M2NMd^Wqxs=ZNuT za?Q`S2H&EvAWnfflGaUoJ|<+Od8u~)z9Hd8xr(scwa5NLq2m23*xmiNqk`ajPQGhh zgl=M*(cVC2@1}E@Ai$$lWp|#=TPhD2@1z+H^fGO}O!YCGzIuv!@c5NPXG63;OKro& z7S&TANB$0FhsKp*IV@d-i1#r=`+C!k$HR;^z3~Ma6{CwuW z-xN-f5|yyVASv%Fx_K+#Q`$g2oF8vssr_*5QtbB*wDU3H*Cj8yELKbBv0OPh8;H#t zy90#FWzDOx>jhfwu*fyD{%f9Rb$7+57wo_FsCAhIw9fsy8oeg3Y(v4NmbFb|d2f%G zm#jFs{MJu&44E{iPMJOrxFmmyU>>DRaZay*SdENMB>OcJ2R_^u_Cyk>>HNl$aI#-& zySX}!sgS#i?B0I2cEMcWS5TKxHo<=ZE0hr_)9KLR*_JsfRa~mjS1IoL#!z$U<0k#b{Vtvp?kHSmRK&Rb$&qr9&_F zaQMZd#oF1yisAITZ>79NhoROR2#DKo zis$5q%DiJoK+7y}Iphu^qXIoWRKLEvKW13wxWTtnYCYZBo6@IYg$_#H87afk1Vw2FE3@_^Z-P<@r^0EX?J%@M#hBPAP^uZmQ~^=!2VHbfS7YOF$QghidQfHQiYY zd2`bGTtEyb@KdPk_6vg1XuJ*_gv&h=MC)1_*Xk#cb?*4ZYtjT^t|JihF!H@uE{!>h zi~{tmI1)SF#I*~FO}>xQG6UKXK02*sEA@sqyt!8cGd$-ibr7Rt1|47|m!xiHcfGpI z_L8>Scx0u|@F~R~mv7dFvQ36C+l^0MsO?m>wlc_4p4k$hv2Q4_8NtYp>%RpFp&gQx z_IcC=!9#Ma%$LpF_Qrf*1*u=rDzGP^AQjP!DEMct2Mpe0%w51(6v|_G-J~$?tMm}P zCWgeUpL8!J3I$>kk-pZK>LS)Ua>8|j@Ji3~nLz>eA6lZ%kV#tTp0)u@uug7J z$)|N?Tjz4YaVvgk4iE7-_86uABOvHa4+!chxI_H3Jc#`P)cZhEC)va)^?wsFe^vq# z2es5))Cb3Qp6B)Zn$utc)73=B8%A{e>0A_4SzIJgY5&AX3Yzr1z%qV^ur57CIN{u$TIPAl(oiHJXW>up>6&Fp$6Z&N^VyT!3+`p>kSP$bi zrTW-z6a6U3uC{Bm!~=z)0jE~5I6VhU2<9> z+gT)j^@EYHhgJvU7M?GBq|@CYyV+t9+K#=5SSrBFxwHG4cmJ!siOs097=>$j)L`1@ z7YzEbFjs+|Nn_LMp?IeIG$-s#u90Q%Clt7Q@1^Tvumb99XyO2*!+$Zf?woyu4_r8{P%X ztB21MwqNV0yA3Cs35@xoMeyNimZlg{_Ku8Z9`tvfn2zsJD3~Svc&n2wa4C~W_@#Q$ z%SBly&DP!f-dV0y(t}%7(svlB@aXDdaPEinBNO$0Dl(l}GjeXe!N}OX8_-yj+4Jt< z{Vn%vwLE|*g}cP_Vce@}GI;#_f}+LfrB^xP1df1$0Lp`2*r|gX;str};px$)vogJY z1Pk{o8|euU2LWe`>v+GljyvR-WoWj56X_K5RV-8dXFoqboe|~h1UcE7Xvfrnoc){! zS8$)sF2*neW!)Qf4MvJlM8@MbXsRwaF-}sta38&te1~^h%abrAjg>!Go2R>#3V9e7 z;#d+6FAQXZ;6k&=mXQ}K4+mRZ963_A^G42xx6`uhJ0w!hE+(FNt``lhd7{ZPh&oj94|33nD~l+ zJH58+%6SB%D$o~ul3Ms-?+eaN5~d8h2EJqGhe+qv8?Dn7ZRjx(rz%y9)9M(ltTPk6 zOmFDXm&s8fHNb~pec^|UM*r2o5O-(-xuyKHsLiXa(V81kTVj_WyN|UJ!oz#$Ko44l z{iVTh^LK4rHLWQ0{nI~#8$-dqp#Dk7Isdmw0~)hQ&mu9OH4>eOHu+hRank8)xx~x& zI>dQ*8n<)5roKl3s)@<_RhQ=qrd?p)t7)Q^I)YtO0VQws*NXo8&op85DWw;l6&Xvl zZhWI6NUz+#8et<@K2N`fjIg-ui)B|6$a;1-D}#c7E*W&A3mJH$(S$C38cgZ8q7lgB zxE+ZF{F(0<@nlLI5eMxJLJ|zM7CzGmSaws_VkIP!djH% z=w;z#a_fcZ&834jyrPl8gI;J9am`S-&4gPC2h)|T#N6-pAef1ZkCS);F)bKp${c0J zCH`J54a7;nkXC)^=>v5HEGtGSGR{l`AcZRhBz#biO@`63QBLYy4ii@e=!G!Fc4SL3 z64rz0T|kspCh0u_*?pphDAAZlfTq|1>$MhDfnNj*1Q>NCqb=|z!0}=2n?QAXWxmFb zd66co(Q)Z|5f#;Kz8+Iv$DW3SBrQWv5c8ua|D@xR6YYL(lXC>N*)6K_*7#Sw+O{s+ zEdFjoJTS_fFLtt=ZP|LWCfl`aU8V&$0nzX;aKu}Y3u`8Eu#ZCM^8bRdV1f#qlYc0m zQG-=^Hd%Ze{I8@4>h%EM98*I4x>5tR$*3bhLT(*sZu3d8; zvDPfiMx+B*REla&wakpGoDw3Js2u7_hwFRm^ZWj8zrTKeyl?M+-hV%D&&T8ac-|i) zY=-t$9Net=aF#R}D{<_yUsXX&LCbtp4*Gh(z`-Vuk1bWzKOadEooc6sEU8rt(zK1h z0W}NcU6H4)7WT7v-gdP>dAzIj1EgYbg6?%V_}1I6ev`|RKuV{fMhCOtltt(YO*O_X z(rQ2eg1#hGa!!f2Jk5xQB)#?tdlh_q*9KgGv$c!(p`>-Q)zxv8S5rbPhGDzIRr@wPlHZ$$$0{U z4sea{H;cm&Tgo1n57s8adRoA$qw(NHY`ja;M^BuAjaiO0KJH+=d*2MCZ}nm0XK{A| zuEUhR+vTPYG0xGg0eo#nVEtaMSMaES>3?xWfrDdiruml-BCSw`x-l0ehm7`{^HQsxC<;j|C)A(DV8Z6}`tS!-w-8fwqY$BEhasBrmoqV3jXfsA`Vbkg@4_ zTeUT?tl9W1-id~=%k*}QX=?XI!MaQY4Xvl!Ck4(lm97ovcWgVf^mc>&a6h&QmS}C~ zZ8rL9ZU!&>yOD6kiA`5d&_0dP~ zofShJQ;ePoNY5`{QMu4Qth`3yz}zt*$wJLaJ&L3^e4gJwS83BjVFyBq8bb8~ohOBU zvh!9gn+~adRkI5;3VR+6<2<$BUznMm9vitV>D)gVMFeuPu^s(Lb-0#1OBzIQ_}&r? zZ_?IfD*t)PP_Q%?UXGW?Jd9Jq+{WFzB+1(&tG?dPSf1MrEfHZjiySKP!C5BveBf-r z+fWThp`O#Pwix^`2mF6`RlnYqLec>p0p}@}Q)bNL zl7h_@i8n*jan2cI36NfEc?O>`zy?4@!XYz!M8?sur;&M|6vdhMB`^*%t#8Jq<`5T{ za9MmsFB|BYqhLACy&eD+>fb@xHNaX6=U7h@Eh4^%wWnCCEff;jCS8035a)19iS+tW zA;M~)pwblIWr!Y|Vyy;AvlIb_`a`__i*gXeud32Clv)ibZhN>QOF?t%I9^bcp}pga~2P z1i$Z=0Jrc7(y25xI)iH!szqT{MPi+ojtJvy2NsrfocJnyasP2AHpupr4l>!vvcaqU z8N{;~p$a00*?05^>S#^Kc{*7ZNp%j1z`cov-!W_A_G}AD*iAeB_2S0biA@~;@jv0L zRn`ZKQJ-nf^uqjn1^-|)BHD2o1^Q>GF9>%0MXBrl=QL-z3LD-Yl;Z7I|vj zv09WA+o2)^KXAaUp%kN<3%|*R)*7T`$Nqo_O^F;~*HAjzNuIowz zw$<4hnochIE+WW@b~DhbbKt_r)e5o5%e-RMWCVSiR?ae(b+Q8m5czZT>-tSd_665h zt=NFk(Diu4MwaSE-zweV^u42`M1M#bPJ|t^QNg(wu@kcH{yTHKyBC+J z>NVByD(@!>wh_9Byb-f2VR$9Mr+wGeG zsoTnmCr!AjBbM5_uP@gbAT>4SqharZNiTF9Ol|IC1r0gIB@_9+xn`#XI$&EjOl2%5 zYoJ19>O!F9tN%b&%LZ&_G~kaJ#f^hD@!X>B>tKuDDomEZnCB={nT=;}DQ%yRku;kR zR+P*a&Pag=hc9Soc4p0ac-HJ4@c}_$b^Ui}WXeaUfP&q(Odcq=;38)fEc4?}<5REi zi?@E#ndpko-#CM82`c&zoI3K0o~R#ky!iM~0y`VpCfjsj^I8|CT(Ma@MP=J8q<60= z@EmSz-h+>AzG1jYMZY##_?_|6Orwc9iD$01wMH?ZAt2 zb z`)t;+djA824-GvAFaL>ksrD)z@vg^Kn>7hWA$d0~*LMjvf9>vqHFkwAaQd77?hr{} zHhFmK+Zn<5{p-fQil3$*pQD+{LamA{*(QUW#&!BZp;K2O-9rs4VPA-2DMFQsw@YFJ z-cEI_9Ings%Nk;vPt>_A3DwSES9*PoqmL|VpG1KQ5m~);sseT2+f)u4t?aDQk3j~5 zf^snViEfY&p@nLGS?^;015#htXc&^R(-R-VT4guLg0+eJs2i?+Cka9N8=pbj!~`l5 zrLQ4WnNhH9X1q=(3lVA@ql6hLQEekj6W2ldb#XiF73#KGo@>^6;ZSDoR4ESITb z&LaC%AEZ-Jk9zdji%C-_EeR1t6IlP4$!T_+`Wz2Xa4*urQNq;<*6L^%`Blp2@~n;# zAwt~kWRxgDI#w-_gbW4C_&aLkNz7yb!}qU$FfU-Wr5`Mt!q)yO1l;;paQoh7Uo1m=NZuTa-D<5Wx%6lD#@Pe1WM-F7QtpILN&8oe7DuXv$%a|T*P)>S30f}v-9 z_qmcgMO*j@Y>-%mGWp4!<>#HtqH^ZVQ9~DcerdOi#hnx49oZC1Tc5Ift}~KTPeXnz zDoW7$Q#L4?FC= zHM^HP1)cloOjF6$W>9wQtM+ItLuGLrX_zuG-<%p2U0D92^tHXZd_EjD`nMWls`A5&#of>hfo-H1G z%BTY@g9G~sPaGwGM*MK1N0i#^+Px>7G|oa<@lNIFxMUXqsymykS>tGxueb)fpB8_B zbTX|vBwO;oI^NkJVNY3NwjWVrXH@jDo_k?V26aD#hBQyJ46jmU@A2=hEsNj-*} zh6$Xw^;i8C?b?=P`@=d4`x-$I?|oJynh{{(7{(}LrgHI}n3}n*^J`d^tH#2K+0QF; zJcz4+YO2)vAv3%YpnD8m;?JY!$YnM&(t{kmZ3H_=9R>So`Yr zaL}Hqq3-T=(57xxMkrD**J{-l5!`7CaIJ3DC6n`9yscKH-4#d zXwP?&Q0+Xdnk?IPjGn;*9)}1JX_l;pGo7vj&L+#~LorVcc4`5MS`KN|cq8*B5FR(7t?IyLtp=<= zE6mmx{}JoeDVke6DOB4gQ<|6PRIqiJk# za?6FOKZasIQGr2jaOUaEfF<&{wSEKT>GbF_$T=4~tDhx>P#mI%LHpB3;dP9v9wjOir(&kCIN~HT5yihP) zz}U5Mv2%9y8M;7mj4qL!{}`gdFAniPec ze!8Z$cIaCsCoq_0>@r)J`MdqpsX`2i`1r$XUV-CwtRZ5Qg+)m)jxv&`UB_W&{JvvM zzfH%{Qn2?qd~78!)ZNmH67F;F+CG9*0zVPL4@jiA3HvPbY`?7pI6n+Tw+WG?)=8!L z4X+Kg`XMLB^KgC~8Y1&8W0Zeu&153Jmy`FfKN=TcmLgQo91Bg7d~a8o$e8i9%xFbd z5)Rz<0j7>~p3t#;I&SELH?DhyrsDqR>K+O!hZLBt$NimljfuaWF3Ty(R_Vgh}Et8Ujz)s~M*XEKyA;11WGQE?|RTJU$v!ma{=H z^;(C5jf1(H-G#f?$u>#X4n|Z=N98{BIYRSf%8~W&9ujUTTX%l5oJaq?`}R{W?2k>Y z;rZjCol^2UkEBtPPuzjT+U=EHCT?F6ennpVZui$EsqbpDEq2{kzkW;zXC-)`z{f;< z%v#ZwU(9nq4}f9}vIB)`cb}(-fTWGsLPBx>SN1cHB!-*s5N)K^A?koiSOHg#{IyP^ zF4kdw0Vc*0d@2M_h#;Kb%jqo~4!iKJ0r% za+ULcc{TJy?%a7tL;EMEKCo-onhQpv;r=-zctdjFl5D#hcfr?54l~!YHk_kNdnN); z-xgc79Pm{I9;Q$939iDGzTq{6buOj|>lIwQV>vY3q#s3YKeV3> zc+iHfxk2Gzd^$BYN%`t!1;->7$*pVq@G_02nz2fq)nQ&|zuyWYjL!_N zWyKU2A_Qj|Zr}Wk6_{GNKDk+__DI$#d66J>F?lt9wJ{AV2N|a_Mn#d%DgWRl&2jjn zrii;7n#xg5mOz53$84WExBg%;W9+?qDMYvQld&e@ir$rV9N(nPki#L6|pJl*j38u0Z5c0BXuMX5+eWmE|h@<1zn@VQPOn3ROMb zLa$pUdwmE9%m{^bv>XN!^s!Hn`mKWIa2Ec&?|Tlt+ZXhpcE@YD;$Xr{3pzU$wdoq) zXYO=w_^qSxKtTw`y8{KYl)ch9-&OO<7RyW7w0`uMa__4`0uHwED`XnQJ9ac;0xd2+ zmOglHl|>g?sPW)$tO>WBYmni+x%(C3hEt#(DeGM{e3bO1s@VAq_i5PlnUren0~j61 z;Ivqftm*byL#)me{44e>ITQ`dMTVx}?wg$wa}jXb+I*edRs+suUvIN_@e7S}31b<* zg~wuibwM>|L2O9h9~K`ZM|?^6KsQ8cGI=$Xg|Y*SfmGM@Xh1-AyM3IYrFuD5|GDOP z+gYPva48Nc6nmcHowAc!aobrIc57ONkB!OU0ea~yaWT3xp z8Vy<8=R-2!?Mxh|A-sb9eMN0Vm@#Sv2g*ycs6sf~&I6!+&4lg{PC8x*y75%XUL%03 z5TS{{&s)I;!T+xf=ybB24epBmxf>kuX51xepk=kkriGD%!b%(q9Huzo z?2t${IPxN4*0*9q9<8v*jQb^fC5q%ZBD%FAy1yO~KT zvb&2(zu&=?G_eoQ18hC>$*C>{^n{$Em0Q=Bb+1u|77BZ;*buWhI->MKA() z-}0J|#RUE3?P$OD_=prZ#hiZiuLv0wu5`!UY>T)^L}iTC3pTaf*e-*pe|0-r4RMBVJdA z!V;mhzH<;rqp3vu&Na>YpFes`J4|GN#~8ar zyW-$Y7*b`*Wvo)To?>X*WZnYyBc6pm>l@{{cl9s@K@1F{&i)9&+SdozpBbx8n9_{D z7(c{GQ+{g8P_RtNz651_1Vc)xG4xEM-_L$v1U%5=7iRo0-ZO(-6Gx$XPcV7B%ij9v z42Ksp3z<0kf-E1=$Q-0A#y{reCO49>SrBk6=c(b9z@Ndh^TffRHj$7Qw*rz-BTH{& zz{T2N0^5fxXQ49j_&pIPD}`$LJS6HLTC;Hiq>(Xtg`F_g3u#LYaX#JTW9&;DEDh$p z#{*cR8n~dTvKoIM2P3_UC0p@EGT4zJOn(Qu0ULbyzJ0}bD=LaEhN0bV&HIWA2Ok25qiH#8Ye78&Ef zj(#aE0xiJP+(^vD1 zV^u?{DPo_y=C9Cfp-K6b4WIG{3s1=tMz7vaToQutXWB7iOntY8whZM@!!_?$cLalc zXtTGKt3+`8CH2Ks)NZc_9|;f6I{YSqy_(GEYqSVWP0QV-;K5GZm-+ey5#ri=Wx&CX z&sW@%!uHvwwlf15fAznxgLII#-;B~cRZ4^Zpw8bt{v?3i5#19)L*&>%MSqQ*th&_I z;fR!oZJbZxgleJNCLqVExfA1J7#rxJ7F?RVCAo1dX%u)EYJ66+MBlu}QBuLd zq<(j;{_T^MM5q=T(JVRIz8$33#6*O^eUYNbkWs^RUM|Z*t&ML`N1I&#S?$z*S{av4 zIQJ^wdrKiXzd(&iQ@OkLgrm;Pz!ifJzK`S3=;ey4cxSmv#G6}L%MxDX0fz}TciuI{ zq-dS?_m?wL;7J}T3-(xde@s)ob>!t|(RnRxrd;)$+0BN}Syou(C#VL7;1#=PcyYb>}4kF69`g*a;4ISWRd~=Y>EHWE(n6TgM_m@r1vKyFUnHjKb z*jF?`4@FCek?d`rUJ|MnLv^fpLeNIKTM^x-HZl5;UN@7b`U-moI2j!`oeob6xbNs9 zS2(@(X!+8SLX=`%yErxR)bG;DpQNEbYFGsVg49 zjprHTE)uF4uy?{x;yMzH4FY-OPTVgUdGg-=><_j2o2OsUQhS1@tnqei@L6X5bf1&x z9x+ZuTvI4Ju1e~{niMTAH$EM>jm+rgPqUYDbyEg0`<#ED-mt4R95R-ph0~1|iM2Ht zviU9JnXg1Jc&3*fg75&Ij2wI`Vp$AS&CrucLRAFHfEVXO!mYB4WcC+fC;lEQKr92yvAkR4IqBN(2lz@gAMeI~8%CJx35<7)Ta#LWz{ z&?(3KKl?RAxyD4pY&>JWdE!e@py6C(F&iBDHQdfcOgB2`C;@+k{zbOc$MS#sE@r$W z;Imln9rR2SJb%=7Ioc&KBuo_6_w2o1aE{;Ij4mShcYI5~BE8T~MeT zQGv!PFq(qQ^OjAnlDq|y3=mAz(qo~ak_nOhDfGwvWp$3>1`AXacoh>m+zrLKzH<@8 z1Zoy|w-R9+9dtU>uHT9b9c?sEv{~uvXBe%gfas8%7)^Fb=(O?qop#D4A@b=Xhv;y5 zvTJm9^XKYdVtBI;@DFpB`d0EaZ#*enj4~wr)sG|zRk?cBfQmB2RE}L8x*bdQ{Oo)| z6?j=>H|*AdL1t9EtqG18sON+iW)aey}?V7Ih(H}CBIHK-i zY(C9G`ugZ}r+|>+!Bo`8;ZS<0fD67_Ls~<1o@YKg_dbWP%d=klbVgZL7i_ORGw5T7 zP8QDWGH&wsMesm)0aPV;`8~$lQEK+a*I5n@8WU`%T~+abuD@N634#Tr{PN8s9*|Qm zMUYA-xp{OEVTaAGm`g-es)YNO+K5*!&GA6tU-;4v#A2m3^_V{kSqQ03>}^jGxyJm9 z$cMBCQ4xi_Ki4~?1!OPk#`6Duw!Rzb+WvG12 z<*KFggK>P_{@`7Lt1YpR&9g-ObM}Y_*ck-Wd+d)y()fvZZOe8$_Qhu!tuDzZklO0F zX+>_))kRwIw}|M5b<}xUU>A9y;RPl>_SU{hcmglxG*(bV*sJ&P69gN&i<$BFe7u1) ze*Mx5&)9g!Ssu7~d)j5pKY2~(aIC3Krx-U2Y24*!P<&q`$achI{Vk75fz^;hyz`3u zZ?_zagP?T=>-|SrBZp7i06R&r~soL6EK+NXC-j zEaUrqB1(jFg49~lL(!vvm`>-M9h$i@r{2XTW$oA=%f%ZrY4E2_ST^4vC+1u{9tRIu z@#DEGDpeW$1W21embw!r>JA>*pRlFXxaR6`>$EWr&hK5en704zDm3lFq$v{PV;?+k z%}Mzn1>~chV+pkF#Lq|HwZv|DLzO`$XFX8AXuSWAXv9=(WaEHw%u+WFxJHXm{{SS# z7cZ_eeg~scQ2f3-zdj>-5kbTI1VsT}v+uUXETkl%qpqAkbowsml4cKGJ({li{m}6THJ)~|6n(%LZD^?1KQv>j zcK1Z#Js$XC?TxWo5W^V@eD>s$9=c89soJhAG}e$NZ6YNsq;fVg7~neB zXOmb@P$O4TjU}*dYsU{h=7JJb+Dc!>I18xZ>)Q-Xr$t_w;n-v$oRi_Hg972$73JF7 zU-v~!S66q88kS06o3D6(5vmIv&nZGvvu1p0t8|6Als{{|*dTSfo@h9-T-7Pu?T8V3 zvm`2aM+ebP0|(hiM3F zMSK+(n}ohO4XuFwVxG5>Hzt=2_d&s<)U~6+)meTI2OV<(Qtk zpV!(eHfJgOoG&5raB%add|lVr#AaGmv32rEAudMO*9OwjxXU|Ra=LbMQR)_FZc=2D zY#lO5174TWJwuSO%FlQ0uPL0KoXWx(e~YGIv3Vk+ zRR*XQk^mrFJ*;^{R2ip%o~YX%_s?(lqv21>{%Lg?FBzh*ci45&7+GgchNRn{EmDc# z-#vTqc?DiAds7V8oUX`_k+pW1u7nMU=OueXv=uW|Ut@##2tZ_}mkPZ|RYf5%o= zE?}Bocm$DeI^-ols9@|u%msa<@$cT}c5&jFz27P{IzLE31V}zoqjXPC@UPv4Jz3S& z2WMfgXjZTUVVew2=<`09|$hiZL%>%3|>3^(Wax0TxrlI zR5h4C)?c(v%{A8Ig~LRh4|az=K+pMiXu%p;j^kx0(k%T_s!+ZE^rPakj2U`r%2fVc zT;~`YWS4Age*u>5fK!WBk-_`JpuUGn*bHPrUiIP1~5? z=3&LW&f4ddY;ckEJ%-aXD+{8#WtQDdAmAB`G*9x>=oAYM5&pXfQ*rfQref(vTdj3|C7S8ya92w=k zsMa~N^QO&(Ltc$eEPZ|f^^o%WCyGr*{y+&+4v)fSP>r`6u#=y$5(Ww%H3D;lPn%f# zi%6(qjA#6mA^5$J)=h;<$dC8EjV&HvOb+8?!#Cu#-15%7Ld1R+B2CsBqRmeYFV0Y> zF&oQ+SWSy}hD2bGr_R>F=od^m;&S1hd_Jv0Ewfn$%&|PmZx7ObvD;5U*raRfSb7*& z+X(2HX85$@Ti;yj2tH-^8U@ozz?^w}D}wthK7A8?j{Vqc@4&H8Sm6L`{f0LwRb@qx ztB<4ajDv!_BZV=@GC%s9WrD0b0P~AK>y$(K{&ImDoYuwKfcPX-Z*$IZ3aC^)T<74z z;Q1B1pYEOgS?wo zA1lNA)@NJdFhbQ$mu@>nmZ)|9$?W@hYVJ#AT*@4z>kV(?FH6Gw+560H>bd(Kvc_C}~CY%kj6Q69T{cDO=OTzXA zx&vHC9o1;wVhIHcEKxB%Su=M5eSNK}d+Q9X;~4|e)jB_3d44yWA@?KvXnmAzpW%Lf zOs+yAZjM;BC*Z25H%9oq0q7Ow#;5h7>Q=AL!3l48fK8`gJ1{?2ee=IGBsJYRJedG# zhcf89t!-oMcoCe_x39Mewe&KZk?e~(KL~F58HG~UD_Z&O(J;(kpE&H8W7VeNsP;U; zL3B;w=y`N7V1!7H?Fi<)uHkD6k1kezy~8`gAw$z}*4BnO$D#YtRjHbel0U$av_PsG zoh$kg(Oq_Q3An2gC{!d}Zi;Yie!zkuLk69!s^8vU141l(o>@>A$QZ*1p$GK`kRsQHd~wPREAJGE>9qs|iYMKmJUUP<6XA^ugQmqq*r~pP{cm zXI|w8VU6d+iCr=fv-xRGilJ3!TEAhNUmjzIdO%|gAqDXbzkUMz|2)pb$O1M9>7x=G zxWNC8k4T6#f*6#T>yYlRu(11+$V&5+2{v2g%Y_|oaVs4ygLKS^%f5QV!6K#+$hn-{ zXlV&{oG>&Y#Pu5jhax{Y1GSm^#6b{ z@Oi06`Gv6Ohw(jZ?gw088Kp{f+Dw+1v>*FeYm-c|y}ufte??a6cFN($zTQ(5Or!&%Ik`R!&_GfNeN^YfiZ66{`cRZ#egzyLwBi zUvQ)ky> z2g1yqk(!AM|hzz|J!D8l5~1PM35U&mdRYmMWY71HsX7_|v-seDigSvwMHq z&~wUJ`eP0@-Z!I&+h2=mD$Wm&H;w;6gDrgW`41EqS+wBb`t3G?M$KmAEWPVXEKEsL z?YzR_@ys4M&UH%6zZHe){yGrw-JWPW+Njme!g3~+bw6*HZlkudOiu=}^9UhPWx+=~ zJ}R?>LX8cF^VSFF2V|Wmp*kP+Z=tkA!z%NfmL*LzU&6k3gpJ>-SlFkgGrUWYyB4lC zTD0Kvl7z^1_S1-cMGxF{k-HA`CK*<*^@G;*$3F;6o!@&Jw(s)%zNFW|ATgvs_YKb| zoHKr1yvz4&Go)v`CL~9P=!i9mK78NDH5xWK7I0!Cl9F~(67j78rae!tDc#Z@`+S6O zds(Ql&lF55Ij1y{?%vxdfF09gviw`8xrP7KPtELyb70@e5v9EO7%YPtdo0}p_?p9Z W3@%F^TZbhlRX@6L1zP{#tN#OGwNNMk literal 0 HcmV?d00001 diff --git a/testrig/media/welcome-small.jpeg b/testrig/media/welcome-small.jpeg new file mode 100755 index 0000000000000000000000000000000000000000..b1a58516958bdafcca34a97da390b77f9a92841a GIT binary patch literal 6872 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 U>}#Apo$Iv+f8_x#TvYVNzJ;U zNgB7h_@uxmhsRrwdr&;XYT1Xt>`2P+Wc#vgwp)-M04O7|DY|!GWva$Jd}{c1)WfO) zUjPLDln%(_tnh?@+;`TM@J!u1cZ74z?|e`0G>i|)yXnr{RGNspFINO;_%e_cpEj#piTkqT&I#X3?Ja#sH2mlv` z1{Br`C9Z-Mx@AHcSXRBKGJrY}l+q~p-I59e^;ajCh1V+RmoHO%#uE#~lCwnt5pkJ^ zcEg*nmxd>?Yk7{`E6X?(_ij|13JuCq*jvgEjL!2ea=#(SXCn7WI6={_IXgfRLklzg zijYdJQ=!S-Ptr`Ek{Pc+*s0mYO!pu;(K=iwOfWy5B2aRB{fa#vSJv!y-S&uQlW9Z% z%+KgjUn#T9&49O<-NuDNwWf5^3Jt&o5aiSQq z32S^&=q0^SGa@?zN9budQ}S@xI}HQVH2%vG7GKar. +*/ + +package testrig + +import ( + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/media" + "github.com/superseriousbusiness/gotosocial/internal/storage" +) + +// NewTestMediaHandler returns a media handler with the default test config, the default test logger, +// and the given db and storage. +func NewTestMediaHandler(db db.DB, storage storage.Storage) media.MediaHandler { + return media.New(NewTestConfig(), db, storage, NewTestLog()) +} diff --git a/testrig/oauthserver.go b/testrig/oauthserver.go new file mode 100644 index 000000000..49615cadc --- /dev/null +++ b/testrig/oauthserver.go @@ -0,0 +1,29 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package testrig + +import ( + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +// NewTestOauthServer returns an oauth server with the given db, and the default test logger. +func NewTestOauthServer(db db.DB) oauth.Server { + return oauth.New(db, NewTestLog()) +} diff --git a/testrig/storage.go b/testrig/storage.go index 3786b063c..a17f6203e 100644 --- a/testrig/storage.go +++ b/testrig/storage.go @@ -1,24 +1,79 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import ( - "github.com/sirupsen/logrus" - "github.com/superseriousbusiness/gotosocial/internal/config" + "fmt" + "os" + "strings" + "github.com/superseriousbusiness/gotosocial/internal/storage" ) -// NewTestStorage returns a new in memory storage with the given config -func NewTestStorage(c *config.Config, log *logrus.Logger) storage.Storage { - s, err := storage.NewInMem(c, log) +// NewTestStorage returns a new in memory storage with the default test config +func NewTestStorage() storage.Storage { + s, err := storage.NewInMem(NewTestConfig(), NewTestLog()) if err != nil { panic(err) } return s } -func StandardStorageSetup(s storage.Storage) { - +// StandardStorageSetup populates the storage with standard test entries from the given directory. +func StandardStorageSetup(s storage.Storage, relativePath string) { + stored := NewTestStored() + a := NewTestAttachments() + for k, fileNameTemplate := range stored { + attachmentInfo, ok := a[k] + if !ok { + panic(fmt.Errorf("key %s not found in test attachments", k)) + } + filenameOriginal := strings.Replace(fileNameTemplate, "*", "original", 1) + filenameSmall := strings.Replace(fileNameTemplate, "*", "small", 1) + pathOriginal := attachmentInfo.File.Path + pathSmall := attachmentInfo.Thumbnail.Path + bOriginal, err := os.ReadFile(fmt.Sprintf("%s/%s", relativePath, filenameOriginal)) + if err != nil { + panic(err) + } + if err := s.StoreFileAt(pathOriginal, bOriginal); err != nil { + panic(err) + } + bSmall, err := os.ReadFile(fmt.Sprintf("%s/%s", relativePath, filenameSmall)) + if err != nil { + panic(err) + } + if err := s.StoreFileAt(pathSmall, bSmall); err != nil { + panic(err) + } + } } +// StandardStorageTeardown deletes everything in storage so that it's clean for the next test func StandardStorageTeardown(s storage.Storage) { - + keys, err := s.ListKeys() + if err != nil { + panic(err) + } + for _, k := range keys { + if err := s.RemoveFileAt(k); err != nil { + panic(err) + } + } } diff --git a/testrig/models.go b/testrig/testmodels.go similarity index 87% rename from testrig/models.go rename to testrig/testmodels.go index 960619926..65358eba4 100644 --- a/testrig/models.go +++ b/testrig/testmodels.go @@ -1,3 +1,21 @@ +/* + GoToSocial + Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package testrig import ( @@ -10,6 +28,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/oauth" ) +// NewTestTokens returns a map of tokens keyed according to which account the token belongs to. func NewTestTokens() map[string]*oauth.Token { tokens := map[string]*oauth.Token{ "local_account_1": { @@ -26,6 +45,7 @@ func NewTestTokens() map[string]*oauth.Token { return tokens } +// NewTestClients returns a map of Clients keyed according to which account they are used by. func NewTestClients() map[string]*oauth.Client { clients := map[string]*oauth.Client{ "local_account_1": { @@ -38,6 +58,7 @@ func NewTestClients() map[string]*oauth.Client { return clients } +// NewTestApplications returns a map of applications keyed to which number application they are. func NewTestApplications() map[string]*gtsmodel.Application { apps := map[string]*gtsmodel.Application{ "application_1": { @@ -54,6 +75,7 @@ func NewTestApplications() map[string]*gtsmodel.Application { return apps } +// NewTestUsers returns a map of Users keyed by which account belongs to them. func NewTestUsers() map[string]*gtsmodel.User { users := map[string]*gtsmodel.User{ "unconfirmed_account": { @@ -181,6 +203,7 @@ func NewTestUsers() map[string]*gtsmodel.User { return users } +// NewTestAccounts returns a map of accounts keyed by what type of account they are. func NewTestAccounts() map[string]*gtsmodel.Account { accounts := map[string]*gtsmodel.Account{ "unconfirmed_account": { @@ -440,14 +463,66 @@ func NewTestAccounts() map[string]*gtsmodel.Account { return accounts } +// NewTestAttachments returns a map of attachments keyed according to which account +// and status they belong to, and which attachment number of that status they are. func NewTestAttachments() map[string]*gtsmodel.MediaAttachment { return map[string]*gtsmodel.MediaAttachment{ - // "admin_account_status_1": { - - // }, + "admin_account_status_1_attachment_1": { + ID: "b052241b-f30f-4dc6-92fc-2bad0be1f8d8", + StatusID: "502ccd6f-0edf-48d7-9016-2dfa4d3714cd", + URL: "http://localhost:8080/fileserver/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/original/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + RemoteURL: "", + CreatedAt: time.Now().Add(-71 * time.Hour), + UpdatedAt: time.Now().Add(-71 * time.Hour), + Type: gtsmodel.FileTypeImage, + FileMeta: gtsmodel.FileMeta{ + Original: gtsmodel.Original{ + Width: 1200, + Height: 630, + Size: 756000, + Aspect: 1.9047619047619047, + }, + Small: gtsmodel.Small{ + Width: 256, + Height: 134, + Size: 34304, + Aspect: 1.9104477611940298, + }, + }, + AccountID: "8020dbb4-1e7b-4d99-a872-4cf94e64210f", + Description: "Black and white image of some 50's style text saying: Welcome On Board", + ScheduledStatusID: "", + Blurhash: "LNJRdVM{00Rj%Mayt7j[4nWBofRj", + Processing: 2, + File: gtsmodel.File{ + Path: "/gotosocial/storage/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/original/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + ContentType: "image/jpeg", + FileSize: 62529, + UpdatedAt: time.Now().Add(-71 * time.Hour), + }, + Thumbnail: gtsmodel.Thumbnail{ + Path: "/gotosocial/storage/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/small/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + ContentType: "image/jpeg", + FileSize: 6872, + UpdatedAt: time.Now().Add(-71 * time.Hour), + URL: "http://localhost:8080/fileserver/8020dbb4-1e7b-4d99-a872-4cf94e64210f/attachment/small/b052241b-f30f-4dc6-92fc-2bad0be1f8d8.jpeg", + RemoteURL: "", + }, + Avatar: false, + Header: false, + }, } } +// NewTestStored returns a map of filenames, keyed according to which attachment they pertain to. +func NewTestStored() map[string]string { + return map[string]string { + "admin_account_status_1_attachment_1": "welcome-*.jpeg", + } +} + +// NewTestStatuses returns a map of statuses keyed according to which account +// and status they are. func NewTestStatuses() map[string]*gtsmodel.Status { return map[string]*gtsmodel.Status{ "admin_account_status_1": {