From f0dc0f36214c4dda1eace9d83956e5d7fef4f729 Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Sat, 22 Aug 2020 13:57:33 +0200 Subject: [PATCH 001/399] fix compile issue on Qpace3 --- Grid/lattice/Lattice_transfer.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index e698e40e..91de721f 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -127,6 +127,11 @@ accelerator_inline void convertType(T1 & out, const iScalar & in) { convertType(out,in._internal); } +template::value, T1>::type* = nullptr> +accelerator_inline void convertType(T1 & out, const iScalar & in) { + convertType(out,in._internal); +} + template accelerator_inline void convertType(iScalar & out, const T2 & in) { convertType(out._internal,in); From 1292d595634172e28158f99d31863d63267ca5ac Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Thu, 11 Jun 2020 13:16:00 +0200 Subject: [PATCH 002/399] Add a typedef + broaden interface of CMat --- Grid/algorithms/CoarsenedMatrix.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index 8d184aea..76950baf 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -268,6 +268,21 @@ public: typedef iMatrix Cobj; typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field typedef Lattice FineField; + typedef CoarseVector FermionField; + + // enrich interface + void Meooe(CoarseVector const& in, CoarseVector& out) { assert(0); } + void MeooeDag(CoarseVector const& in, CoarseVector& out) { assert(0); } + void Mooee(CoarseVector const& in, CoarseVector& out) { assert(0); } + void MooeeDag(CoarseVector const& in, CoarseVector& out) { assert(0); } + void MooeeInv(CoarseVector const& in, CoarseVector& out) { assert(0); } + void MooeeInvDag(CoarseVector const& in, CoarseVector& out) { assert(0); } + void Dminus(CoarseVector const& in, CoarseVector& out) { out = in; } + void DminusDag(CoarseVector const& in, CoarseVector& out) { out = in; } + void ImportPhysicalFermionSource(CoarseVector const& input, CoarseVector& imported) { imported = input; } + void ImportUnphysicalFermion(CoarseVector const& input, CoarseVector& imported) { imported = input; } + void ExportPhysicalFermionSolution(CoarseVector const& solution, CoarseVector& exported) { exported = solution; }; + void ExportPhysicalFermionSource(CoarseVector const& solution, CoarseVector& exported) { exported = solution; }; //////////////////// // Data members From dd1ba266b269b093a8bcd9bf815438d9896d7148 Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Fri, 17 Jul 2020 11:58:02 +0200 Subject: [PATCH 003/399] Fix mapping between dir + disp and point in CMat --- Grid/algorithms/CoarsenedMatrix.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index 76950baf..d18fba43 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -432,25 +432,25 @@ public: ////////////// // 4D action like wilson - // 0+ => 0 - // 0- => 1 - // 1+ => 2 - // 1- => 3 + // 0+ => 0 + // 0- => 4 + // 1+ => 1 + // 1- => 5 // etc.. ////////////// // 5D action like DWF - // 1+ => 0 - // 1- => 1 - // 2+ => 2 - // 2- => 3 + // 1+ => 0 + // 1- => 4 + // 2+ => 1 + // 2- => 5 // etc.. auto point = [dir, disp, ndim](){ if(dir == 0 and disp == 0) return 8; else if ( ndim==4 ) { - return (4 * dir + 1 - disp) / 2; + return (1 - disp) / 2 * 4 + dir; } else { - return (4 * (dir-1) + 1 - disp) / 2; + return (1 - disp) / 2 * 4 + dir - 1; } }(); From b2087f14c48e881ab041bbaef7f6c444c88a895d Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Mon, 24 Aug 2020 16:54:36 +0200 Subject: [PATCH 004/399] Fix CoarsenedMatrix regarding illegal memory accesses Need a reference to geom since the lambda copies the this pointer which points to host memory, see - https://docs.nvidia.com/cuda/cuda-c-programming-guide/#star-this-capture - https://devblogs.nvidia.com/new-compiler-features-cuda-8/ --- Grid/algorithms/CoarsenedMatrix.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index d18fba43..ba40535c 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -310,6 +310,8 @@ public: Stencil.HaloExchange(in,compressor); autoView( in_v , in, AcceleratorRead); autoView( out_v , out, AcceleratorWrite); + autoView( Stencil_v , Stencil, AcceleratorRead); + auto& geom_v = geom; typedef LatticeView Aview; Vector AcceleratorViewContainer; @@ -331,14 +333,14 @@ public: int ptype; StencilEntry *SE; - for(int point=0;point_is_local) { nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); } else { - nbr = coalescedRead(Stencil.CommBuf()[SE->_offset]); + nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]); } acceleratorSynchronise(); @@ -382,6 +384,7 @@ public: autoView( out_v , out, AcceleratorWrite); autoView( in_v , in, AcceleratorRead); + autoView( Stencil_v , Stencil, AcceleratorRead); const int Nsimd = CComplex::Nsimd(); typedef decltype(coalescedRead(in_v[0])) calcVector; @@ -395,12 +398,12 @@ public: int ptype; StencilEntry *SE; - SE=Stencil.GetEntry(ptype,point,ss); + SE=Stencil_v.GetEntry(ptype,point,ss); if(SE->_is_local) { nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); } else { - nbr = coalescedRead(Stencil.CommBuf()[SE->_offset]); + nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]); } acceleratorSynchronise(); From 2a75516330925bd05681f4dba639482ca84270d7 Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Wed, 26 Aug 2020 12:34:17 -0400 Subject: [PATCH 005/399] state MPI/SLURM message only on world_rank zero --- Grid/threads/Accelerator.cc | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 864d90a9..f6df4a31 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -21,22 +21,26 @@ void acceleratorInit(void) #define ENV_RANK_SLURM "SLURM_PROCID" #define ENV_LOCAL_RANK_MVAPICH "MV2_COMM_WORLD_LOCAL_RANK" #define ENV_RANK_MVAPICH "MV2_COMM_WORLD_RANK" - // We extract the local rank initialization using an environment variable - if ((localRankStr = getenv(ENV_LOCAL_RANK_OMPI)) != NULL) { - printf("OPENMPI detected\n"); - rank = atoi(localRankStr); - } else if ((localRankStr = getenv(ENV_LOCAL_RANK_MVAPICH)) != NULL) { - printf("MVAPICH detected\n"); - rank = atoi(localRankStr); - } else if ((localRankStr = getenv(ENV_LOCAL_RANK_SLURM)) != NULL) { - printf("SLURM detected\n"); - rank = atoi(localRankStr); - } else { - printf("MPI version is unknown - bad things may happen\n"); - } if ((localRankStr = getenv(ENV_RANK_OMPI )) != NULL) { world_rank = atoi(localRankStr);} if ((localRankStr = getenv(ENV_RANK_MVAPICH)) != NULL) { world_rank = atoi(localRankStr);} if ((localRankStr = getenv(ENV_RANK_SLURM )) != NULL) { world_rank = atoi(localRankStr);} + // We extract the local rank initialization using an environment variable + if ((localRankStr = getenv(ENV_LOCAL_RANK_OMPI)) != NULL) { + if (!world_rank) + printf("OPENMPI detected\n"); + rank = atoi(localRankStr); + } else if ((localRankStr = getenv(ENV_LOCAL_RANK_MVAPICH)) != NULL) { + if (!world_rank) + printf("MVAPICH detected\n"); + rank = atoi(localRankStr); + } else if ((localRankStr = getenv(ENV_LOCAL_RANK_SLURM)) != NULL) { + if (!world_rank) + printf("SLURM detected\n"); + rank = atoi(localRankStr); + } else { + if (!world_rank) + printf("MPI version is unknown - bad things may happen\n"); + } size_t totalDeviceMem=0; for (int i = 0; i < nDevices; i++) { From cf3535d16e412adf80ca1a2f7ead54003860c94b Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Thu, 27 Aug 2020 14:06:48 +0200 Subject: [PATCH 006/399] Expose more functions in CMat --- Grid/algorithms/CoarsenedMatrix.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index ba40535c..cdfc2ac3 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -271,6 +271,9 @@ public: typedef CoarseVector FermionField; // enrich interface + void Dhop(CoarseVector const& in, CoarseVector& out, int dag) { assert(0); } + void DhopEO(CoarseVector const& in, CoarseVector& out, int dag) { assert(0); } + void DhopOE(CoarseVector const& in, CoarseVector& out, int dag) { assert(0); } void Meooe(CoarseVector const& in, CoarseVector& out) { assert(0); } void MeooeDag(CoarseVector const& in, CoarseVector& out) { assert(0); } void Mooee(CoarseVector const& in, CoarseVector& out) { assert(0); } From 4d2dc7ba0304397f536a2bc71260266e1475ae83 Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Mon, 7 Sep 2020 17:57:07 +0200 Subject: [PATCH 007/399] Enable even-odd for CoarsenedMatrix --- Grid/algorithms/CoarsenedMatrix.h | 471 +++++++++++++++++++++++--- tests/solver/Test_coarse_even_odd.cc | 475 +++++++++++++++++++++++++++ 2 files changed, 899 insertions(+), 47 deletions(-) create mode 100644 tests/solver/Test_coarse_even_odd.cc diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index cdfc2ac3..66b9c169 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -31,6 +31,7 @@ Author: paboyle #ifndef GRID_ALGORITHM_COARSENED_MATRIX_H #define GRID_ALGORITHM_COARSENED_MATRIX_H +#include // needed for Dagger(Yes|No), Inverse(Yes|No) NAMESPACE_BEGIN(Grid); @@ -59,12 +60,14 @@ inline void blockMaskedInnerProduct(Lattice &CoarseInner, class Geometry { public: int npoint; + int base; std::vector directions ; std::vector displacements; + std::vector points_dagger; Geometry(int _d) { - int base = (_d==5) ? 1:0; + base = (_d==5) ? 1:0; // make coarse grid stencil for 4d , not 5d if ( _d==5 ) _d=4; @@ -72,16 +75,51 @@ public: npoint = 2*_d+1; directions.resize(npoint); displacements.resize(npoint); + points_dagger.resize(npoint); for(int d=0;d<_d;d++){ directions[d ] = d+base; directions[d+_d] = d+base; displacements[d ] = +1; displacements[d+_d]= -1; + points_dagger[d ] = d+_d; + points_dagger[d+_d] = d; } directions [2*_d]=0; displacements[2*_d]=0; + points_dagger[2*_d]=2*_d; } + int point(int dir, int disp) { + assert(disp == -1 || disp == 0 || disp == 1); + assert(base+0 <= dir && dir < base+4); + + // directions faster index = new indexing + // 4d (base = 0): + // point 0 1 2 3 4 5 6 7 8 + // dir 0 1 2 3 0 1 2 3 0 + // disp +1 +1 +1 +1 -1 -1 -1 -1 0 + // 5d (base = 1): + // point 0 1 2 3 4 5 6 7 8 + // dir 1 2 3 4 1 2 3 4 0 + // disp +1 +1 +1 +1 -1 -1 -1 -1 0 + + // displacements faster index = old indexing + // 4d (base = 0): + // point 0 1 2 3 4 5 6 7 8 + // dir 0 0 1 1 2 2 3 3 0 + // disp +1 -1 +1 -1 +1 -1 +1 -1 0 + // 5d (base = 1): + // point 0 1 2 3 4 5 6 7 8 + // dir 1 1 2 2 3 3 4 4 0 + // disp +1 -1 +1 -1 +1 -1 +1 -1 0 + + if(dir == 0 and disp == 0) + return 8; + else // New indexing + return (1 - disp) / 2 * 4 + dir - base; + // else // Old indexing + // return (4 * (dir - base) + 1 - disp) / 2; + } }; template @@ -258,7 +296,7 @@ public: // Fine Object == (per site) type of fine field // nbasis == number of deflation vectors template -class CoarsenedMatrix : public SparseMatrixBase > > { +class CoarsenedMatrix : public CheckerBoardedSparseMatrixBase > > { public: typedef iVector siteVector; @@ -270,16 +308,7 @@ public: typedef Lattice FineField; typedef CoarseVector FermionField; - // enrich interface - void Dhop(CoarseVector const& in, CoarseVector& out, int dag) { assert(0); } - void DhopEO(CoarseVector const& in, CoarseVector& out, int dag) { assert(0); } - void DhopOE(CoarseVector const& in, CoarseVector& out, int dag) { assert(0); } - void Meooe(CoarseVector const& in, CoarseVector& out) { assert(0); } - void MeooeDag(CoarseVector const& in, CoarseVector& out) { assert(0); } - void Mooee(CoarseVector const& in, CoarseVector& out) { assert(0); } - void MooeeDag(CoarseVector const& in, CoarseVector& out) { assert(0); } - void MooeeInv(CoarseVector const& in, CoarseVector& out) { assert(0); } - void MooeeInvDag(CoarseVector const& in, CoarseVector& out) { assert(0); } + // enrich interface, use default implementation as in FermionOperator /////// void Dminus(CoarseVector const& in, CoarseVector& out) { out = in; } void DminusDag(CoarseVector const& in, CoarseVector& out) { out = in; } void ImportPhysicalFermionSource(CoarseVector const& input, CoarseVector& imported) { imported = input; } @@ -292,21 +321,36 @@ public: //////////////////// Geometry geom; GridBase * _grid; + GridBase* _cbgrid; int hermitian; CartesianStencil Stencil; + CartesianStencil StencilEven; + CartesianStencil StencilOdd; std::vector A; - + std::vector Aeven; + std::vector Aodd; + + CoarseMatrix AselfInv; + CoarseMatrix AselfInvEven; + CoarseMatrix AselfInvOdd; + + Vector dag_factor; + /////////////////////// // Interface /////////////////////// GridBase * Grid(void) { return _grid; }; // this is all the linalg routines need to know + GridBase * RedBlackGrid() { return _cbgrid; }; + + int ConstEE() { return 0; } void M (const CoarseVector &in, CoarseVector &out) { conformable(_grid,in.Grid()); conformable(in.Grid(),out.Grid()); + out.Checkerboard() = in.Checkerboard(); SimpleCompressor compressor; @@ -364,12 +408,72 @@ public: return M(in,out); } else { // corresponds to Galerkin coarsening - CoarseVector tmp(Grid()); - G5C(tmp, in); - M(tmp, out); - G5C(out, out); + return MdagNonHermitian(in, out); } }; + + void MdagNonHermitian(const CoarseVector &in, CoarseVector &out) + { + conformable(_grid,in.Grid()); + conformable(in.Grid(),out.Grid()); + out.Checkerboard() = in.Checkerboard(); + + SimpleCompressor compressor; + + Stencil.HaloExchange(in,compressor); + autoView( in_v , in, AcceleratorRead); + autoView( out_v , out, AcceleratorWrite); + autoView( Stencil_v , Stencil, AcceleratorRead); + auto& geom_v = geom; + typedef LatticeView Aview; + + Vector AcceleratorViewContainer; + + for(int p=0;poSites(); + + Vector points(geom.npoint, 0); + for(int p=0; poSites()*nbasis, Nsimd, { + int ss = sss/nbasis; + int b = sss%nbasis; + calcComplex res = Zero(); + calcVector nbr; + int ptype; + StencilEntry *SE; + + for(int p=0;p_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(Stencil_v.CommBuf()[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bb compressor; @@ -379,6 +483,7 @@ public: { conformable(_grid,in.Grid()); conformable(_grid,out.Grid()); + out.Checkerboard() = in.Checkerboard(); typedef LatticeView Aview; Vector AcceleratorViewContainer; @@ -434,34 +539,7 @@ public: this->MdirComms(in); - int ndim = in.Grid()->Nd(); - - ////////////// - // 4D action like wilson - // 0+ => 0 - // 0- => 4 - // 1+ => 1 - // 1- => 5 - // etc.. - ////////////// - // 5D action like DWF - // 1+ => 0 - // 1- => 4 - // 2+ => 1 - // 2- => 5 - // etc.. - auto point = [dir, disp, ndim](){ - if(dir == 0 and disp == 0) - return 8; - else if ( ndim==4 ) { - return (1 - disp) / 2 * 4 + dir; - } else { - return (1 - disp) / 2 * 4 + dir - 1; - } - }(); - - MdirCalc(in,out,point); - + MdirCalc(in,out,geom.point(dir,disp)); }; void Mdiag(const CoarseVector &in, CoarseVector &out) @@ -470,17 +548,269 @@ public: MdirCalc(in, out, point); // No comms }; + void Mooee(const CoarseVector &in, CoarseVector &out) { + MooeeInternal(in, out, DaggerNo, InverseNo); + } + + void MooeeInv(const CoarseVector &in, CoarseVector &out) { + MooeeInternal(in, out, DaggerNo, InverseYes); + } + + void MooeeDag(const CoarseVector &in, CoarseVector &out) { + MooeeInternal(in, out, DaggerYes, InverseNo); + } + + void MooeeInvDag(const CoarseVector &in, CoarseVector &out) { + MooeeInternal(in, out, DaggerYes, InverseYes); + } + + void Meooe(const CoarseVector &in, CoarseVector &out) { + if(in.Checkerboard() == Odd) { + DhopEO(in, out, DaggerNo); + } else { + DhopOE(in, out, DaggerNo); + } + } + + void MeooeDag(const CoarseVector &in, CoarseVector &out) { + if(in.Checkerboard() == Odd) { + DhopEO(in, out, DaggerYes); + } else { + DhopOE(in, out, DaggerYes); + } + } + + void Dhop(const CoarseVector &in, CoarseVector &out, int dag) { + conformable(in.Grid(), _grid); // verifies full grid + conformable(in.Grid(), out.Grid()); + + out.Checkerboard() = in.Checkerboard(); + + DhopInternal(Stencil, A, in, out, dag); + } + + void DhopOE(const CoarseVector &in, CoarseVector &out, int dag) { + conformable(in.Grid(), _cbgrid); // verifies half grid + conformable(in.Grid(), out.Grid()); // drops the cb check + + assert(in.Checkerboard() == Even); + out.Checkerboard() = Odd; + + DhopInternal(StencilEven, Aodd, in, out, dag); + } + + void DhopEO(const CoarseVector &in, CoarseVector &out, int dag) { + conformable(in.Grid(), _cbgrid); // verifies half grid + conformable(in.Grid(), out.Grid()); // drops the cb check + + assert(in.Checkerboard() == Odd); + out.Checkerboard() = Even; + + DhopInternal(StencilOdd, Aeven, in, out, dag); + } + + void MooeeInternal(const CoarseVector &in, CoarseVector &out, int dag, int inv) { + out.Checkerboard() = in.Checkerboard(); + assert(in.Checkerboard() == Odd || in.Checkerboard() == Even); + + CoarseMatrix *Aself = nullptr; + if(in.Grid()->_isCheckerBoarded) { + if(in.Checkerboard() == Odd) { + Aself = (inv) ? &AselfInvOdd : &Aodd[geom.npoint-1]; + DselfInternal(StencilOdd, *Aself, in, out, dag); + } else { + Aself = (inv) ? &AselfInvEven : &Aeven[geom.npoint-1]; + DselfInternal(StencilEven, *Aself, in, out, dag); + } + } else { + Aself = (inv) ? &AselfInv : &A[geom.npoint-1]; + DselfInternal(Stencil, *Aself, in, out, dag); + } + assert(Aself != nullptr); + } + + void DselfInternal(CartesianStencil &st, CoarseMatrix &a, + const CoarseVector &in, CoarseVector &out, int dag) { + int point = geom.npoint-1; + autoView( out_v, out, AcceleratorWrite); + autoView( in_v, in, AcceleratorRead); + autoView( st_v, st, AcceleratorRead); + autoView( a_v, a, AcceleratorRead); + + const int Nsimd = CComplex::Nsimd(); + typedef decltype(coalescedRead(in_v[0])) calcVector; + typedef decltype(coalescedRead(in_v[0](0))) calcComplex; + + RealD* dag_factor_p = &dag_factor[0]; + + if(dag) { + accelerator_for(sss, in.Grid()->oSites()*nbasis, Nsimd, { + int ss = sss/nbasis; + int b = sss%nbasis; + calcComplex res = Zero(); + calcVector nbr; + int ptype; + StencilEntry *SE; + + SE=st_v.GetEntry(ptype,point,ss); + + if(SE->_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(st_v.CommBuf()[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bboSites()*nbasis, Nsimd, { + int ss = sss/nbasis; + int b = sss%nbasis; + calcComplex res = Zero(); + calcVector nbr; + int ptype; + StencilEntry *SE; + + SE=st_v.GetEntry(ptype,point,ss); + + if(SE->_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(st_v.CommBuf()[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bb &st, std::vector &a, + const CoarseVector &in, CoarseVector &out, int dag) { + SimpleCompressor compressor; + + st.HaloExchange(in,compressor); + autoView( in_v, in, AcceleratorRead); + autoView( out_v, out, AcceleratorWrite); + autoView( st_v , st, AcceleratorRead); + typedef LatticeView Aview; + + // determine in what order we need the points + int npoint = geom.npoint-1; + Vector points(npoint, 0); + for(int p=0; p AcceleratorViewContainer; + for(int p=0;poSites()*nbasis, Nsimd, { + int ss = sss/nbasis; + int b = sss%nbasis; + calcComplex res = Zero(); + calcVector nbr; + int ptype; + StencilEntry *SE; + + for(int p=0;p_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(st_v.CommBuf()[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bboSites()*nbasis, Nsimd, { + int ss = sss/nbasis; + int b = sss%nbasis; + calcComplex res = Zero(); + calcVector nbr; + int ptype; + StencilEntry *SE; + + for(int p=0;p_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(st_v.CommBuf()[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bb > &linop, Aggregation & Subspace) { @@ -629,6 +959,9 @@ public: std::cout << GridLogMessage << " ForceHermitian, new code "<lSites(); + + typedef typename Cobj::scalar_object scalar_object; + + autoView(Aself_v, A[geom.npoint-1], CpuRead); + autoView(AselfInv_v, AselfInv, CpuWrite); + thread_for(site, localVolume, { // NOTE: Not able to bring this to GPU because of Eigen + peek/poke + Eigen::MatrixXcd selfLinkEigen = Eigen::MatrixXcd::Zero(nbasis, nbasis); + Eigen::MatrixXcd selfLinkInvEigen = Eigen::MatrixXcd::Zero(nbasis, nbasis); + + scalar_object selfLink = Zero(); + scalar_object selfLinkInv = Zero(); + + Coordinate lcoor; + + Grid()->LocalIndexToLocalCoor(site, lcoor); + peekLocalSite(selfLink, Aself_v, lcoor); + + for (int i = 0; i < nbasis; ++i) + for (int j = 0; j < nbasis; ++j) + selfLinkEigen(i, j) = static_cast(TensorRemove(selfLink(i, j))); + + selfLinkInvEigen = selfLinkEigen.inverse(); + + for(int i = 0; i < nbasis; ++i) + for(int j = 0; j < nbasis; ++j) + selfLinkInv(i, j) = selfLinkInvEigen(i, j); + + pokeLocalSite(selfLinkInv, AselfInv_v, lcoor); + }); + } + + void FillHalfCbs() { + std::cout << GridLogDebug << "CoarsenedMatrix::FillHalfCbs" << std::endl; + for(int p = 0; p < geom.npoint; ++p) { + pickCheckerboard(Even, Aeven[p], A[p]); + pickCheckerboard(Odd, Aodd[p], A[p]); + } + pickCheckerboard(Even, AselfInvEven, AselfInv); + pickCheckerboard(Odd, AselfInvOdd, AselfInv); + } }; NAMESPACE_END(Grid); diff --git a/tests/solver/Test_coarse_even_odd.cc b/tests/solver/Test_coarse_even_odd.cc new file mode 100644 index 00000000..dfbab747 --- /dev/null +++ b/tests/solver/Test_coarse_even_odd.cc @@ -0,0 +1,475 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/solver/Test_coarse_even_odd.cc + + Copyright (C) 2015-2020 + + Author: Daniel Richtmann + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + +#include + +using namespace Grid; + +#ifndef NBASIS +#define NBASIS 40 +#endif + +// NOTE: The tests in this file are written in analogy to +// - tests/core/Test_wilson_even_odd.cc +// - tests/core/Test_wilson_clover.cc + +std::vector readFromCommandlineIvec(int* argc, + char*** argv, + std::string&& option, + const std::vector& defaultValue) { + std::string arg; + std::vector ret(defaultValue); + if(GridCmdOptionExists(*argv, *argv + *argc, option)) { + arg = GridCmdOptionPayload(*argv, *argv + *argc, option); + GridCmdOptionIntVector(arg, ret); + } + return ret; +} + +int main(int argc, char** argv) { + Grid_init(&argc, &argv); + + ///////////////////////////////////////////////////////////////////////////// + // Read from command line // + ///////////////////////////////////////////////////////////////////////////// + + const int nbasis = NBASIS; static_assert((nbasis & 0x1) == 0, ""); + const int nb = nbasis/2; + Coordinate blockSize = readFromCommandlineIvec(&argc, &argv, "--blocksize", {2, 2, 2, 2}); + + std::cout << GridLogMessage << "Compiled with nbasis = " << nbasis << " -> nb = " << nb << std::endl; + + ///////////////////////////////////////////////////////////////////////////// + // General setup // + ///////////////////////////////////////////////////////////////////////////// + + Coordinate clatt = GridDefaultLatt(); + for(int d=0; dshow_decomposition(); + std::cout << GridLogMessage << "Grid_c:" << std::endl; Grid_c->show_decomposition(); + std::cout << GridLogMessage << "RBGrid_f:" << std::endl; RBGrid_f->show_decomposition(); + std::cout << GridLogMessage << "RBGrid_c:" << std::endl; RBGrid_c->show_decomposition(); + + GridParallelRNG pRNG_f(Grid_f); + GridParallelRNG pRNG_c(Grid_c); + + std::vector seeds({1, 2, 3, 4}); + + pRNG_f.SeedFixedIntegers(seeds); + pRNG_c.SeedFixedIntegers(seeds); + + ///////////////////////////////////////////////////////////////////////////// + // Setup of Dirac Matrix and Operator // + ///////////////////////////////////////////////////////////////////////////// + + LatticeGaugeField Umu(Grid_f); SU3::HotConfiguration(pRNG_f, Umu); + + RealD checkTolerance = (getPrecision::value == 1) ? 1e-7 : 1e-15; + + RealD mass = -0.30; + RealD csw = 1.9192; + + WilsonCloverFermionR Dwc(Umu, *Grid_f, *RBGrid_f, mass, csw, csw); + MdagMLinearOperator MdagMOp_Dwc(Dwc); + + ///////////////////////////////////////////////////////////////////////////// + // Type definitions // + ///////////////////////////////////////////////////////////////////////////// + + typedef Aggregation Aggregates; + typedef CoarsenedMatrix CoarseDiracMatrix; + typedef CoarseDiracMatrix::CoarseVector CoarseVector; + + ///////////////////////////////////////////////////////////////////////////// + // Setup of Aggregation // + ///////////////////////////////////////////////////////////////////////////// + + Aggregates Aggs(Grid_c, Grid_f, 0); + { + LatticeFermion tmp(Aggs.subspace[0].Grid()); + for(int n = 0; n < nb; n++) { + gaussian(pRNG_f, Aggs.subspace[n]); + G5C(tmp, Aggs.subspace[n]); + axpby(Aggs.subspace[n + nb], 0.5, -0.5, Aggs.subspace[n], tmp); + axpby(Aggs.subspace[n], 0.5, 0.5, Aggs.subspace[n], tmp); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // Setup of CoarsenedMatrix and Operator // + ///////////////////////////////////////////////////////////////////////////// + + const int hermitian = 0; + CoarseDiracMatrix Dc(*Grid_c, *RBGrid_c, hermitian); + Dc.CoarsenOperator(Grid_f, MdagMOp_Dwc, Aggs); + MdagMLinearOperator MdagMOp_Dc(Dc); + + ///////////////////////////////////////////////////////////////////////////// + // Setup vectors used in all tests // + ///////////////////////////////////////////////////////////////////////////// + + CoarseVector src(Grid_c); random(pRNG_c, src); + CoarseVector diff(Grid_c); diff = Zero(); + + ///////////////////////////////////////////////////////////////////////////// + // Start of tests // + ///////////////////////////////////////////////////////////////////////////// + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test Dhop + Mdiag = Munprec" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector phi(Grid_c); phi = Zero(); + CoarseVector chi(Grid_c); chi = Zero(); + CoarseVector res(Grid_c); res = Zero(); + CoarseVector ref(Grid_c); ref = Zero(); + + Dc.Mdiag(src, phi); std::cout << GridLogMessage << "Applied Mdiag" << std::endl; + Dc.Dhop(src, chi, DaggerNo); std::cout << GridLogMessage << "Applied Dhop" << std::endl; + Dc.M(src, ref); std::cout << GridLogMessage << "Applied M" << std::endl; + + res = phi + chi; + + diff = ref - res; + auto absDev = norm2(diff); + auto relDev = absDev / norm2(ref); + std::cout << GridLogMessage << "norm2(Munprec), norm2(Dhop + Mdiag), abs. deviation, rel. deviation: " + << norm2(ref) << " " << norm2(res) << " " << absDev << " " << relDev << " -> check " + << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test Meo + Moe = Dhop" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector src_e(RBGrid_c); src_e = Zero(); + CoarseVector src_o(RBGrid_c); src_o = Zero(); + CoarseVector res_e(RBGrid_c); res_e = Zero(); + CoarseVector res_o(RBGrid_c); res_o = Zero(); + CoarseVector res(Grid_c); res = Zero(); + CoarseVector ref(Grid_c); ref = Zero(); + + pickCheckerboard(Even, src_e, src); + pickCheckerboard(Odd, src_o, src); + + Dc.Meooe(src_e, res_o); std::cout << GridLogMessage << "Applied Meo" << std::endl; + Dc.Meooe(src_o, res_e); std::cout << GridLogMessage << "Applied Moe" << std::endl; + Dc.Dhop(src, ref, DaggerNo); std::cout << GridLogMessage << "Applied Dhop" << std::endl; + + setCheckerboard(res, res_o); + setCheckerboard(res, res_e); + + diff = ref - res; + auto absDev = norm2(diff); + auto relDev = absDev / norm2(ref); + std::cout << GridLogMessage << "norm2(Dhop), norm2(Meo + Moe), abs. deviation, rel. deviation: " + << norm2(ref) << " " << norm2(res) << " " << absDev << " " << relDev + << " -> check " << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test |(Im(v^dag M^dag M v)| = 0" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector tmp(Grid_c); tmp = Zero(); + CoarseVector phi(Grid_c); phi = Zero(); + + Dc.M(src, tmp); std::cout << GridLogMessage << "Applied M" << std::endl; + Dc.Mdag(tmp, phi); std::cout << GridLogMessage << "Applied Mdag" << std::endl; + + std::cout << GridLogMessage << "src = " << norm2(src) << " tmp = " << norm2(tmp) << " phi = " << norm2(phi) << std::endl; + + ComplexD dot = innerProduct(src, phi); + + auto relDev = abs(imag(dot)) / abs(real(dot)); + std::cout << GridLogMessage << "Re(v^dag M^dag M v), Im(v^dag M^dag M v), rel.deviation: " + << real(dot) << " " << imag(dot) << " " << relDev + << " -> check " << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test |(Im(v^dag Mooee^dag Mooee v)| = 0 (full grid)" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector tmp(Grid_c); tmp = Zero(); + CoarseVector phi(Grid_c); phi = Zero(); + + Dc.Mooee(src, tmp); std::cout << GridLogMessage << "Applied Mooee" << std::endl; + Dc.MooeeDag(tmp, phi); std::cout << GridLogMessage << "Applied MooeeDag" << std::endl; + + ComplexD dot = innerProduct(src, phi); + + auto relDev = abs(imag(dot)) / abs(real(dot)); + std::cout << GridLogMessage << "Re(v^dag Mooee^dag Mooee v), Im(v^dag Mooee^dag Mooee v), rel.deviation: " + << real(dot) << " " << imag(dot) << " " << relDev + << " -> check " << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test MooeeInv Mooee = 1 (full grid)" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector tmp(Grid_c); tmp = Zero(); + CoarseVector phi(Grid_c); phi = Zero(); + + Dc.Mooee(src, tmp); std::cout << GridLogMessage << "Applied Mooee" << std::endl; + Dc.MooeeInv(tmp, phi); std::cout << GridLogMessage << "Applied MooeeInv" << std::endl; + + diff = src - phi; + auto absDev = norm2(diff); + auto relDev = absDev / norm2(src); + std::cout << GridLogMessage << "norm2(src), norm2(MooeeInv Mooee src), abs. deviation, rel. deviation: " + << norm2(src) << " " << norm2(phi) << " " << absDev << " " << relDev + << " -> check " << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test MeooeDagger is the dagger of Meooe by requiring" << std::endl; + std::cout << GridLogMessage << "= < phi | Meooe | chi > * = < chi | Meooe^dag| phi>" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + // clang-format off + CoarseVector phi(Grid_c); random(pRNG_c, phi); + CoarseVector chi(Grid_c); random(pRNG_c, chi); + CoarseVector chi_e(RBGrid_c); chi_e = Zero(); + CoarseVector chi_o(RBGrid_c); chi_o = Zero(); + CoarseVector dchi_e(RBGrid_c); dchi_e = Zero(); + CoarseVector dchi_o(RBGrid_c); dchi_o = Zero(); + CoarseVector phi_e(RBGrid_c); phi_e = Zero(); + CoarseVector phi_o(RBGrid_c); phi_o = Zero(); + CoarseVector dphi_e(RBGrid_c); dphi_e = Zero(); + CoarseVector dphi_o(RBGrid_c); dphi_o = Zero(); + // clang-format on + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + + Dc.Meooe(chi_e, dchi_o); std::cout << GridLogMessage << "Applied Meo" << std::endl; + Dc.Meooe(chi_o, dchi_e); std::cout << GridLogMessage << "Applied Moe" << std::endl; + Dc.MeooeDag(phi_e, dphi_o); std::cout << GridLogMessage << "Applied MeoDag" << std::endl; + Dc.MeooeDag(phi_o, dphi_e); std::cout << GridLogMessage << "Applied MoeDag" << std::endl; + + ComplexD phiDchi_e = innerProduct(phi_e, dchi_e); + ComplexD phiDchi_o = innerProduct(phi_o, dchi_o); + ComplexD chiDphi_e = innerProduct(chi_e, dphi_e); + ComplexD chiDphi_o = innerProduct(chi_o, dphi_o); + + std::cout << GridLogDebug << "norm dchi_e = " << norm2(dchi_e) << " norm dchi_o = " << norm2(dchi_o) << " norm dphi_e = " << norm2(dphi_e) + << " norm dphi_o = " << norm2(dphi_e) << std::endl; + + std::cout << GridLogMessage << "e " << phiDchi_e << " " << chiDphi_e << std::endl; + std::cout << GridLogMessage << "o " << phiDchi_o << " " << chiDphi_o << std::endl; + + std::cout << GridLogMessage << "phiDchi_e - conj(chiDphi_o) " << phiDchi_e - conj(chiDphi_o) << std::endl; + std::cout << GridLogMessage << "phiDchi_o - conj(chiDphi_e) " << phiDchi_o - conj(chiDphi_e) << std::endl; + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test MooeeInv Mooee = 1 (checkerboards separately)" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector chi(Grid_c); random(pRNG_c, chi); + CoarseVector tmp(Grid_c); tmp = Zero(); + CoarseVector phi(Grid_c); phi = Zero(); + CoarseVector chi_e(RBGrid_c); chi_e = Zero(); + CoarseVector chi_o(RBGrid_c); chi_o = Zero(); + CoarseVector phi_e(RBGrid_c); phi_e = Zero(); + CoarseVector phi_o(RBGrid_c); phi_o = Zero(); + CoarseVector tmp_e(RBGrid_c); tmp_e = Zero(); + CoarseVector tmp_o(RBGrid_c); tmp_o = Zero(); + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, tmp_e, tmp); + pickCheckerboard(Odd, tmp_o, tmp); + + Dc.Mooee(chi_e, tmp_e); std::cout << GridLogMessage << "Applied Mee" << std::endl; + Dc.MooeeInv(tmp_e, phi_e); std::cout << GridLogMessage << "Applied MeeInv" << std::endl; + Dc.Mooee(chi_o, tmp_o); std::cout << GridLogMessage << "Applied Moo" << std::endl; + Dc.MooeeInv(tmp_o, phi_o); std::cout << GridLogMessage << "Applied MooInv" << std::endl; + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + diff = chi - phi; + auto absDev = norm2(diff); + auto relDev = absDev / norm2(chi); + std::cout << GridLogMessage << "norm2(chi), norm2(MeeInv Mee chi), abs. deviation, rel. deviation: " + << norm2(chi) << " " << norm2(phi) << " " << absDev << " " << relDev + << " -> check " << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test MooeeDag MooeeInvDag = 1 (checkerboards separately)" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector chi(Grid_c); random(pRNG_c, chi); + CoarseVector tmp(Grid_c); tmp = Zero(); + CoarseVector phi(Grid_c); phi = Zero(); + CoarseVector chi_e(RBGrid_c); chi_e = Zero(); + CoarseVector chi_o(RBGrid_c); chi_o = Zero(); + CoarseVector phi_e(RBGrid_c); phi_e = Zero(); + CoarseVector phi_o(RBGrid_c); phi_o = Zero(); + CoarseVector tmp_e(RBGrid_c); tmp_e = Zero(); + CoarseVector tmp_o(RBGrid_c); tmp_o = Zero(); + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, tmp_e, tmp); + pickCheckerboard(Odd, tmp_o, tmp); + + Dc.MooeeDag(chi_e, tmp_e); std::cout << GridLogMessage << "Applied MeeDag" << std::endl; + Dc.MooeeInvDag(tmp_e, phi_e); std::cout << GridLogMessage << "Applied MeeInvDag" << std::endl; + Dc.MooeeDag(chi_o, tmp_o); std::cout << GridLogMessage << "Applied MooDag" << std::endl; + Dc.MooeeInvDag(tmp_o, phi_o); std::cout << GridLogMessage << "Applied MooInvDag" << std::endl; + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + diff = chi - phi; + auto absDev = norm2(diff); + auto relDev = absDev / norm2(chi); + std::cout << GridLogMessage << "norm2(chi), norm2(MeeDag MeeInvDag chi), abs. deviation, rel. deviation: " + << norm2(chi) << " " << norm2(phi) << " " << absDev << " " << relDev + << " -> check " << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test Meo + Moe + Moo + Mee = Munprec" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector chi(Grid_c); chi = Zero(); + CoarseVector phi(Grid_c); phi = Zero(); + CoarseVector ref(Grid_c); ref = Zero(); + CoarseVector src_e(RBGrid_c); src_e = Zero(); + CoarseVector src_o(RBGrid_c); src_o = Zero(); + CoarseVector phi_e(RBGrid_c); phi_e = Zero(); + CoarseVector phi_o(RBGrid_c); phi_o = Zero(); + CoarseVector chi_e(RBGrid_c); chi_e = Zero(); + CoarseVector chi_o(RBGrid_c); chi_o = Zero(); + + pickCheckerboard(Even, src_e, src); + pickCheckerboard(Odd, src_o, src); + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + // M phi = (Mooee src_e + Meooe src_o , Mooee src_o + Meooe src_e) + + Dc.M(src, ref); // Reference result from the unpreconditioned operator + + // EO matrix + Dc.Mooee(src_e, chi_e); std::cout << GridLogMessage << "Applied Mee" << std::endl; + Dc.Mooee(src_o, chi_o); std::cout << GridLogMessage << "Applied Moo" << std::endl; + Dc.Meooe(src_o, phi_e); std::cout << GridLogMessage << "Applied Moe" << std::endl; + Dc.Meooe(src_e, phi_o); std::cout << GridLogMessage << "Applied Meo" << std::endl; + + phi_o += chi_o; + phi_e += chi_e; + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + std::cout << GridLogDebug << "norm phi_e = " << norm2(phi_e) << " norm phi_o = " << norm2(phi_o) << " norm phi = " << norm2(phi) << std::endl; + + diff = ref - phi; + auto absDev = norm2(diff); + auto relDev = absDev / norm2(ref); + std::cout << GridLogMessage << "norm2(Dunprec), norm2(Deoprec), abs. deviation, rel. deviation: " + << norm2(ref) << " " << norm2(phi) << " " << absDev << " " << relDev + << " -> check " << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + assert(relDev <= checkTolerance); + } + + { + std::cout << GridLogMessage << "===========================================================================" << std::endl; + std::cout << GridLogMessage << "= Test MpcDagMpc is hermitian" << std::endl; + std::cout << GridLogMessage << "===========================================================================" << std::endl; + + CoarseVector phi(Grid_c); random(pRNG_c, phi); + CoarseVector chi(Grid_c); random(pRNG_c, chi); + CoarseVector chi_e(RBGrid_c); chi_e = Zero(); + CoarseVector chi_o(RBGrid_c); chi_o = Zero(); + CoarseVector dchi_e(RBGrid_c); dchi_e = Zero(); + CoarseVector dchi_o(RBGrid_c); dchi_o = Zero(); + CoarseVector phi_e(RBGrid_c); phi_e = Zero(); + CoarseVector phi_o(RBGrid_c); phi_o = Zero(); + CoarseVector dphi_e(RBGrid_c); dphi_e = Zero(); + CoarseVector dphi_o(RBGrid_c); dphi_o = Zero(); + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + + SchurDiagMooeeOperator HermOpEO(Dc); + + HermOpEO.MpcDagMpc(chi_e, dchi_e); std::cout << GridLogMessage << "Applied MpcDagMpc to chi_e" << std::endl; + HermOpEO.MpcDagMpc(chi_o, dchi_o); std::cout << GridLogMessage << "Applied MpcDagMpc to chi_o" << std::endl; + HermOpEO.MpcDagMpc(phi_e, dphi_e); std::cout << GridLogMessage << "Applied MpcDagMpc to phi_e" << std::endl; + HermOpEO.MpcDagMpc(phi_o, dphi_o); std::cout << GridLogMessage << "Applied MpcDagMpc to phi_o" << std::endl; + + ComplexD phiDchi_e = innerProduct(phi_e, dchi_e); + ComplexD phiDchi_o = innerProduct(phi_o, dchi_o); + ComplexD chiDphi_e = innerProduct(chi_e, dphi_e); + ComplexD chiDphi_o = innerProduct(chi_o, dphi_o); + + std::cout << GridLogMessage << "e " << phiDchi_e << " " << chiDphi_e << std::endl; + std::cout << GridLogMessage << "o " << phiDchi_o << " " << chiDphi_o << std::endl; + + std::cout << GridLogMessage << "phiDchi_e - conj(chiDphi_e) " << phiDchi_e - conj(chiDphi_e) << std::endl; + std::cout << GridLogMessage << "phiDchi_o - conj(chiDphi_o) " << phiDchi_o - conj(chiDphi_o) << std::endl; + } + + Grid_finalize(); +} From 01652d8cfea1ad463b037b20aabfc4f3b0dc7d37 Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Sun, 13 Sep 2020 05:56:02 -0400 Subject: [PATCH 008/399] SlabAllocator --- Grid/threads/Accelerator.h | 13 +-- Grid/threads/SlabAllocator.cc | 161 ++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 Grid/threads/SlabAllocator.cc diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 1a3dfdc2..89d45a17 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -153,18 +153,7 @@ inline void *acceleratorAllocShared(size_t bytes) } return ptr; }; -inline void *acceleratorAllocDevice(size_t bytes) -{ - void *ptr=NULL; - auto err = cudaMalloc((void **)&ptr,bytes); - if( err != cudaSuccess ) { - ptr = (void *) NULL; - printf(" cudaMalloc failed for %d %s \n",bytes,cudaGetErrorString(err)); - } - return ptr; -}; inline void acceleratorFreeShared(void *ptr){ cudaFree(ptr);}; -inline void acceleratorFreeDevice(void *ptr){ cudaFree(ptr);}; inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { cudaMemcpy(to,from,bytes, cudaMemcpyHostToDevice);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ cudaMemcpy(to,from,bytes, cudaMemcpyDeviceToHost);} inline int acceleratorIsCommunicable(void *ptr) @@ -176,6 +165,8 @@ inline int acceleratorIsCommunicable(void *ptr) if(uvm) return 0; else return 1; } +void *acceleratorAllocDevice(size_t bytes); +void acceleratorFreeDevice(void* ptr); #endif diff --git a/Grid/threads/SlabAllocator.cc b/Grid/threads/SlabAllocator.cc new file mode 100644 index 00000000..da863687 --- /dev/null +++ b/Grid/threads/SlabAllocator.cc @@ -0,0 +1,161 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./Grid/threads/SlabAllocator.cc + + Copyright (C) 2020 + +Author: Christoph Lehner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ +#include + +#include + +NAMESPACE_BEGIN(Grid); + +#ifdef GRID_CUDA + +#define GRID_DEVICE_HEAP_SLAB_THRESHOLD (1024*1024) +#define GRID_DEVICE_HEAP_SLAB_SIZE (2*1024*1024) + +void *acceleratorAllocDeviceCUDA(size_t bytes) { + void *ptr=NULL; + auto err = cudaMalloc((void **)&ptr,bytes); + if( err != cudaSuccess ) { + ptr = (void *) NULL; + printf(" cudaMalloc failed for %d %s \n",bytes,cudaGetErrorString(err)); + } + return ptr; +} + +void acceleratorFreeDeviceCUDA(void *ptr) { + cudaFree(ptr); +} + +struct grid_device_heap_slab_t { + void* Ptr; + size_t ElementSize; + size_t Elements; + std::unordered_set Allocated; + std::unordered_set Available; +}; + +std::unordered_map DeviceHeapPtrTable; +std::unordered_map > DeviceHeapSlabTable; + +void* SlabAllocateElement(grid_device_heap_slab_t* slab) { + assert(!slab->Available.empty()); + auto available = slab->Available.begin(); + auto slot = *available; + slab->Allocated.insert(slot); + slab->Available.erase(available); + + void* Ptr = (void*)((char*)slab->Ptr + slot * slab->ElementSize); + DeviceHeapPtrTable[Ptr] = slab; + + //std::cout << "Allocate element " << slot << " of slab " << slab << " of size " << slab->ElementSize << " with elements " << slab->Elements << + // " (allocated = " << slab->Allocated.size() << ", available = " << slab->Available.size() << ")" << std::endl; + + return Ptr; +} + +void SlabRemove(grid_device_heap_slab_t* slab) { + auto & t = DeviceHeapSlabTable[slab->ElementSize]; + assert(slab->Ptr); + DeviceHeapPtrTable.erase(slab->Ptr); + acceleratorFreeDeviceCUDA(slab->Ptr); + assert(t.count(slab) == 1); + t.erase(slab); + delete slab; + //std::cout << "Remove slab " << slab << std::endl; +} + +void SlabFreeElement(grid_device_heap_slab_t* slab, void* ElementPtr) { + size_t Offset = (size_t)ElementPtr - (size_t)slab->Ptr; + //std::cout << "SlabFreeElement offset " << Offset << std::endl; + assert(Offset < GRID_DEVICE_HEAP_SLAB_SIZE); + assert(Offset % slab->ElementSize == 0); + size_t slot = Offset / slab->ElementSize; + assert(slot >= 0); + assert(slab->Allocated.count(slot) == 1 && slab->Available.count(slot) == 0); + slab->Allocated.erase(slot); + slab->Available.insert(slot); + + //std::cout << "Free element " << slot << " of slab" << slab << std::endl; + + if (slab->Allocated.empty()) { + SlabRemove(slab); + } +} + +grid_device_heap_slab_t* SlabFind(size_t bytes) { + + grid_device_heap_slab_t* slab = 0; + std::unordered_set* slab_set = 0; + + decltype(DeviceHeapSlabTable.begin()) slabs = DeviceHeapSlabTable.find(bytes); + if (slabs == DeviceHeapSlabTable.end()) { + slab_set = &DeviceHeapSlabTable[bytes]; + } else { + slab_set = &slabs->second; + } + + for (auto& s : *slab_set) { + if (!s->Available.empty()) { + slab = &(*s); + break; + } + } + + if (!slab) { + slab = new grid_device_heap_slab_t; + slab_set->insert(slab); + slab->Ptr = acceleratorAllocDeviceCUDA(GRID_DEVICE_HEAP_SLAB_SIZE); + slab->ElementSize = bytes; + slab->Elements = GRID_DEVICE_HEAP_SLAB_SIZE / bytes; + for (size_t i=0;iElements;i++) + slab->Available.insert(i); + //std::cout << "New slab" << slab << std::endl; + } + + return slab; +} + +void *acceleratorAllocDevice(size_t bytes) { + if (bytes >= GRID_DEVICE_HEAP_SLAB_THRESHOLD) { + return acceleratorAllocDeviceCUDA(bytes); + } + + return SlabAllocateElement(SlabFind(bytes)); +} + +void acceleratorFreeDevice(void *ptr) { + auto p = DeviceHeapPtrTable.find(ptr); + if (p == DeviceHeapPtrTable.end()) { + acceleratorFreeDeviceCUDA(ptr); + } else { + SlabFreeElement(p->second,ptr); + } +} + +#endif + +NAMESPACE_END(Grid); From 32ff766dbd8416b45ae042463c8f65145b75806f Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Sun, 13 Sep 2020 14:02:53 -0400 Subject: [PATCH 009/399] fix evict scheme, slab alloc --- Grid/allocator/MemoryManagerCache.cc | 6 ++++-- Grid/threads/SlabAllocator.cc | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index 5dd7575e..7d4581d7 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -227,12 +227,13 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod // Find if present, otherwise get or force an empty //////////////////////////////////////////////////////////////////////////// if ( EntryPresent(CpuPtr)==0 ){ - EvictVictims(bytes); EntryCreate(CpuPtr,bytes,mode,hint); } auto AccCacheIterator = EntryLookup(CpuPtr); auto & AccCache = AccCacheIterator->second; + if (!AccCache.AccPtr) + EvictVictims(bytes); assert((mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard)); @@ -361,12 +362,13 @@ uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,V // Find if present, otherwise get or force an empty //////////////////////////////////////////////////////////////////////////// if ( EntryPresent(CpuPtr)==0 ){ - EvictVictims(bytes); EntryCreate(CpuPtr,bytes,mode,transient); } auto AccCacheIterator = EntryLookup(CpuPtr); auto & AccCache = AccCacheIterator->second; + if (!AccCache.AccPtr) + EvictVictims(bytes); assert((mode==CpuRead)||(mode==CpuWrite)); assert(AccCache.accLock==0); // Programming error diff --git a/Grid/threads/SlabAllocator.cc b/Grid/threads/SlabAllocator.cc index da863687..5590f835 100644 --- a/Grid/threads/SlabAllocator.cc +++ b/Grid/threads/SlabAllocator.cc @@ -36,6 +36,9 @@ NAMESPACE_BEGIN(Grid); #define GRID_DEVICE_HEAP_SLAB_THRESHOLD (1024*1024) #define GRID_DEVICE_HEAP_SLAB_SIZE (2*1024*1024) +size_t currentDeviceAlloc = 0; +std::unordered_map ptr_size; + void *acceleratorAllocDeviceCUDA(size_t bytes) { void *ptr=NULL; auto err = cudaMalloc((void **)&ptr,bytes); @@ -43,11 +46,16 @@ void *acceleratorAllocDeviceCUDA(size_t bytes) { ptr = (void *) NULL; printf(" cudaMalloc failed for %d %s \n",bytes,cudaGetErrorString(err)); } + currentDeviceAlloc += bytes; + ptr_size[ptr] = bytes; + std::cout << "Current device alloc: " << currentDeviceAlloc << std::endl; return ptr; } void acceleratorFreeDeviceCUDA(void *ptr) { cudaFree(ptr); + currentDeviceAlloc -= ptr_size[ptr]; + std::cout << "Current device alloc: " << currentDeviceAlloc << std::endl; } struct grid_device_heap_slab_t { From d50a2164d73d2c01048cd1f25f765a720472b2c6 Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Sun, 13 Sep 2020 14:06:06 -0400 Subject: [PATCH 010/399] remove slab allocator --- Grid/threads/Accelerator.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 89d45a17..1a3dfdc2 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -153,7 +153,18 @@ inline void *acceleratorAllocShared(size_t bytes) } return ptr; }; +inline void *acceleratorAllocDevice(size_t bytes) +{ + void *ptr=NULL; + auto err = cudaMalloc((void **)&ptr,bytes); + if( err != cudaSuccess ) { + ptr = (void *) NULL; + printf(" cudaMalloc failed for %d %s \n",bytes,cudaGetErrorString(err)); + } + return ptr; +}; inline void acceleratorFreeShared(void *ptr){ cudaFree(ptr);}; +inline void acceleratorFreeDevice(void *ptr){ cudaFree(ptr);}; inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { cudaMemcpy(to,from,bytes, cudaMemcpyHostToDevice);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ cudaMemcpy(to,from,bytes, cudaMemcpyDeviceToHost);} inline int acceleratorIsCommunicable(void *ptr) @@ -165,8 +176,6 @@ inline int acceleratorIsCommunicable(void *ptr) if(uvm) return 0; else return 1; } -void *acceleratorAllocDevice(size_t bytes); -void acceleratorFreeDevice(void* ptr); #endif From 5cffa05c7e2965216200e7fff3183fce3f15c8bb Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Sun, 13 Sep 2020 14:06:25 -0400 Subject: [PATCH 011/399] remove slab allocator file --- Grid/threads/SlabAllocator.cc | 169 ---------------------------------- 1 file changed, 169 deletions(-) delete mode 100644 Grid/threads/SlabAllocator.cc diff --git a/Grid/threads/SlabAllocator.cc b/Grid/threads/SlabAllocator.cc deleted file mode 100644 index 5590f835..00000000 --- a/Grid/threads/SlabAllocator.cc +++ /dev/null @@ -1,169 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: ./Grid/threads/SlabAllocator.cc - - Copyright (C) 2020 - -Author: Christoph Lehner - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - See the full license in the file "LICENSE" in the top level distribution directory -*************************************************************************************/ -/* END LEGAL */ -#include - -#include - -NAMESPACE_BEGIN(Grid); - -#ifdef GRID_CUDA - -#define GRID_DEVICE_HEAP_SLAB_THRESHOLD (1024*1024) -#define GRID_DEVICE_HEAP_SLAB_SIZE (2*1024*1024) - -size_t currentDeviceAlloc = 0; -std::unordered_map ptr_size; - -void *acceleratorAllocDeviceCUDA(size_t bytes) { - void *ptr=NULL; - auto err = cudaMalloc((void **)&ptr,bytes); - if( err != cudaSuccess ) { - ptr = (void *) NULL; - printf(" cudaMalloc failed for %d %s \n",bytes,cudaGetErrorString(err)); - } - currentDeviceAlloc += bytes; - ptr_size[ptr] = bytes; - std::cout << "Current device alloc: " << currentDeviceAlloc << std::endl; - return ptr; -} - -void acceleratorFreeDeviceCUDA(void *ptr) { - cudaFree(ptr); - currentDeviceAlloc -= ptr_size[ptr]; - std::cout << "Current device alloc: " << currentDeviceAlloc << std::endl; -} - -struct grid_device_heap_slab_t { - void* Ptr; - size_t ElementSize; - size_t Elements; - std::unordered_set Allocated; - std::unordered_set Available; -}; - -std::unordered_map DeviceHeapPtrTable; -std::unordered_map > DeviceHeapSlabTable; - -void* SlabAllocateElement(grid_device_heap_slab_t* slab) { - assert(!slab->Available.empty()); - auto available = slab->Available.begin(); - auto slot = *available; - slab->Allocated.insert(slot); - slab->Available.erase(available); - - void* Ptr = (void*)((char*)slab->Ptr + slot * slab->ElementSize); - DeviceHeapPtrTable[Ptr] = slab; - - //std::cout << "Allocate element " << slot << " of slab " << slab << " of size " << slab->ElementSize << " with elements " << slab->Elements << - // " (allocated = " << slab->Allocated.size() << ", available = " << slab->Available.size() << ")" << std::endl; - - return Ptr; -} - -void SlabRemove(grid_device_heap_slab_t* slab) { - auto & t = DeviceHeapSlabTable[slab->ElementSize]; - assert(slab->Ptr); - DeviceHeapPtrTable.erase(slab->Ptr); - acceleratorFreeDeviceCUDA(slab->Ptr); - assert(t.count(slab) == 1); - t.erase(slab); - delete slab; - //std::cout << "Remove slab " << slab << std::endl; -} - -void SlabFreeElement(grid_device_heap_slab_t* slab, void* ElementPtr) { - size_t Offset = (size_t)ElementPtr - (size_t)slab->Ptr; - //std::cout << "SlabFreeElement offset " << Offset << std::endl; - assert(Offset < GRID_DEVICE_HEAP_SLAB_SIZE); - assert(Offset % slab->ElementSize == 0); - size_t slot = Offset / slab->ElementSize; - assert(slot >= 0); - assert(slab->Allocated.count(slot) == 1 && slab->Available.count(slot) == 0); - slab->Allocated.erase(slot); - slab->Available.insert(slot); - - //std::cout << "Free element " << slot << " of slab" << slab << std::endl; - - if (slab->Allocated.empty()) { - SlabRemove(slab); - } -} - -grid_device_heap_slab_t* SlabFind(size_t bytes) { - - grid_device_heap_slab_t* slab = 0; - std::unordered_set* slab_set = 0; - - decltype(DeviceHeapSlabTable.begin()) slabs = DeviceHeapSlabTable.find(bytes); - if (slabs == DeviceHeapSlabTable.end()) { - slab_set = &DeviceHeapSlabTable[bytes]; - } else { - slab_set = &slabs->second; - } - - for (auto& s : *slab_set) { - if (!s->Available.empty()) { - slab = &(*s); - break; - } - } - - if (!slab) { - slab = new grid_device_heap_slab_t; - slab_set->insert(slab); - slab->Ptr = acceleratorAllocDeviceCUDA(GRID_DEVICE_HEAP_SLAB_SIZE); - slab->ElementSize = bytes; - slab->Elements = GRID_DEVICE_HEAP_SLAB_SIZE / bytes; - for (size_t i=0;iElements;i++) - slab->Available.insert(i); - //std::cout << "New slab" << slab << std::endl; - } - - return slab; -} - -void *acceleratorAllocDevice(size_t bytes) { - if (bytes >= GRID_DEVICE_HEAP_SLAB_THRESHOLD) { - return acceleratorAllocDeviceCUDA(bytes); - } - - return SlabAllocateElement(SlabFind(bytes)); -} - -void acceleratorFreeDevice(void *ptr) { - auto p = DeviceHeapPtrTable.find(ptr); - if (p == DeviceHeapPtrTable.end()) { - acceleratorFreeDeviceCUDA(ptr); - } else { - SlabFreeElement(p->second,ptr); - } -} - -#endif - -NAMESPACE_END(Grid); From 3f0620972061a62fe8802e25ca43d896d9172f09 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 13 Oct 2020 22:18:51 -0400 Subject: [PATCH 012/399] Pretty print --- benchmarks/Benchmark_ITT.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index eb275728..54fe1ab0 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -62,7 +62,7 @@ struct time_statistics{ void comms_header(){ std::cout < Date: Tue, 13 Oct 2020 22:23:57 -0400 Subject: [PATCH 013/399] Reality forced included --- Grid/lattice/Lattice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/lattice/Lattice.h b/Grid/lattice/Lattice.h index 28ea0294..9f5f1da7 100644 --- a/Grid/lattice/Lattice.h +++ b/Grid/lattice/Lattice.h @@ -36,7 +36,7 @@ Author: Peter Boyle #include #include #include -//#include +#include #include #include #include From 9945399e609945bae2c01492cbb4ab56a9246ec8 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 13 Oct 2020 22:24:32 -0400 Subject: [PATCH 014/399] Reaality issues fix by drop from ET --- Grid/lattice/Lattice_ET.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Grid/lattice/Lattice_ET.h b/Grid/lattice/Lattice_ET.h index c43844f8..8d9f4744 100644 --- a/Grid/lattice/Lattice_ET.h +++ b/Grid/lattice/Lattice_ET.h @@ -342,14 +342,10 @@ inline void ExpressionViewClose(LatticeTrinaryExpression &expr) GridUnopClass(UnarySub, -a); GridUnopClass(UnaryNot, Not(a)); -GridUnopClass(UnaryAdj, adj(a)); -GridUnopClass(UnaryConj, conjugate(a)); GridUnopClass(UnaryTrace, trace(a)); GridUnopClass(UnaryTranspose, transpose(a)); GridUnopClass(UnaryTa, Ta(a)); GridUnopClass(UnaryProjectOnGroup, ProjectOnGroup(a)); -GridUnopClass(UnaryToReal, toReal(a)); -GridUnopClass(UnaryToComplex, toComplex(a)); GridUnopClass(UnaryTimesI, timesI(a)); GridUnopClass(UnaryTimesMinusI, timesMinusI(a)); GridUnopClass(UnaryAbs, abs(a)); @@ -456,14 +452,12 @@ GridTrinOpClass(TrinaryWhere, GRID_DEF_UNOP(operator-, UnarySub); GRID_DEF_UNOP(Not, UnaryNot); GRID_DEF_UNOP(operator!, UnaryNot); -GRID_DEF_UNOP(adj, UnaryAdj); -GRID_DEF_UNOP(conjugate, UnaryConj); +//GRID_DEF_UNOP(adj, UnaryAdj); +//GRID_DEF_UNOP(conjugate, UnaryConj); GRID_DEF_UNOP(trace, UnaryTrace); GRID_DEF_UNOP(transpose, UnaryTranspose); GRID_DEF_UNOP(Ta, UnaryTa); GRID_DEF_UNOP(ProjectOnGroup, UnaryProjectOnGroup); -GRID_DEF_UNOP(toReal, UnaryToReal); -GRID_DEF_UNOP(toComplex, UnaryToComplex); GRID_DEF_UNOP(timesI, UnaryTimesI); GRID_DEF_UNOP(timesMinusI, UnaryTimesMinusI); GRID_DEF_UNOP(abs, UnaryAbs); // abs overloaded in cmath C++98; DON'T do the From aa135412f554b5712a62164fdd3136f7e38e16c5 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 13 Oct 2020 22:25:01 -0400 Subject: [PATCH 015/399] toComplex, toReal --- Grid/lattice/Lattice_reality.h | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Grid/lattice/Lattice_reality.h b/Grid/lattice/Lattice_reality.h index 61491d6b..e07dd545 100644 --- a/Grid/lattice/Lattice_reality.h +++ b/Grid/lattice/Lattice_reality.h @@ -64,6 +64,43 @@ template inline Lattice conjugate(const Lattice &lhs){ return ret; }; +template inline Lattice toComplex(const Lattice &lhs){ + Lattice ret(lhs.Grid()); + + autoView( lhs_v, lhs, AcceleratorRead); + autoView( ret_v, ret, AcceleratorWrite); + + ret.Checkerboard() = lhs.Checkerboard(); + accelerator_for( ss, lhs_v.size(), 1, { + ret_v[ss] = toComplex(lhs_v[ss]); + }); + return ret; +}; +template inline Lattice toReal(const Lattice &lhs){ + Lattice ret(lhs.Grid()); + + autoView( lhs_v, lhs, AcceleratorRead); + autoView( ret_v, ret, AcceleratorWrite); + + ret.Checkerboard() = lhs.Checkerboard(); + accelerator_for( ss, lhs_v.size(), 1, { + ret_v[ss] = toReal(lhs_v[ss]); + }); + return ret; +}; + + +template::value,void>::type * = nullptr> +auto toComplex(const Expression &expr) -> decltype(closure(expr)) +{ + return toComplex(closure(expr)); +} +template::value,void>::type * = nullptr> +auto toReal(const Expression &expr) -> decltype(closure(expr)) +{ + return toReal(closure(expr)); +} + NAMESPACE_END(Grid); #endif From a88b3ceca57b616b21e88896a02d5bf224de7242 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 14 Oct 2020 21:33:51 -0400 Subject: [PATCH 016/399] Closure cases --- Grid/lattice/Lattice_reality.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Grid/lattice/Lattice_reality.h b/Grid/lattice/Lattice_reality.h index e07dd545..2e80ce4a 100644 --- a/Grid/lattice/Lattice_reality.h +++ b/Grid/lattice/Lattice_reality.h @@ -100,6 +100,16 @@ auto toReal(const Expression &expr) -> decltype(closure(expr)) { return toReal(closure(expr)); } +template::value,void>::type * = nullptr> +auto adj(const Expression &expr) -> decltype(closure(expr)) +{ + return adj(closure(expr)); +} +template::value,void>::type * = nullptr> +auto conjugate(const Expression &expr) -> decltype(closure(expr)) +{ + return conjugate(closure(expr)); +} NAMESPACE_END(Grid); From bf3c9857e0409036ddb9922775e3c7c8d7e331af Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 14 Oct 2020 21:37:14 -0400 Subject: [PATCH 017/399] Closure changes --- .../implementation/WilsonCloverFermionImplementation.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h index df1bce7c..e721c20d 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h @@ -133,14 +133,14 @@ void WilsonCloverFermion::ImportGauge(const GaugeField &_Umu) pickCheckerboard(Even, CloverTermEven, CloverTerm); pickCheckerboard(Odd, CloverTermOdd, CloverTerm); - pickCheckerboard(Even, CloverTermDagEven, closure(adj(CloverTerm))); - pickCheckerboard(Odd, CloverTermDagOdd, closure(adj(CloverTerm))); + pickCheckerboard(Even, CloverTermDagEven, adj(CloverTerm)); + pickCheckerboard(Odd, CloverTermDagOdd, adj(CloverTerm)); pickCheckerboard(Even, CloverTermInvEven, CloverTermInv); pickCheckerboard(Odd, CloverTermInvOdd, CloverTermInv); - pickCheckerboard(Even, CloverTermInvDagEven, closure(adj(CloverTermInv))); - pickCheckerboard(Odd, CloverTermInvDagOdd, closure(adj(CloverTermInv))); + pickCheckerboard(Even, CloverTermInvDagEven, adj(CloverTermInv)); + pickCheckerboard(Odd, CloverTermInvDagOdd, adj(CloverTermInv)); } template From 3362f8dfa0b9e1122a5923d9d13becdab534e54a Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 14 Oct 2020 22:59:41 -0400 Subject: [PATCH 018/399] happy compile --- Grid/lattice/Lattice_ET.h | 16 ++--- Grid/lattice/Lattice_reality.h | 4 +- Grid/qcd/utils/SUn.h | 3 +- tests/core/Test_lie_generators.cc | 112 +++++++++++++++--------------- 4 files changed, 68 insertions(+), 67 deletions(-) diff --git a/Grid/lattice/Lattice_ET.h b/Grid/lattice/Lattice_ET.h index 8d9f4744..f828ef30 100644 --- a/Grid/lattice/Lattice_ET.h +++ b/Grid/lattice/Lattice_ET.h @@ -488,27 +488,27 @@ GRID_DEF_TRINOP(where, TrinaryWhere); ///////////////////////////////////////////////////////////// template auto closure(const LatticeUnaryExpression &expr) - -> Lattice + -> Lattice::type > { - Lattice ret(expr); + Lattice::type > ret(expr); return ret; } template auto closure(const LatticeBinaryExpression &expr) - -> Lattice + -> Lattice::type > { - Lattice ret(expr); + Lattice::type > ret(expr); return ret; } template auto closure(const LatticeTrinaryExpression &expr) - -> Lattice Lattice + vecEval(0, expr.arg3)))>::type > { - Lattice ret(expr); + vecEval(0, expr.arg3)))>::type > ret(expr); return ret; } #define EXPRESSION_CLOSURE(function) \ diff --git a/Grid/lattice/Lattice_reality.h b/Grid/lattice/Lattice_reality.h index 2e80ce4a..51deeb01 100644 --- a/Grid/lattice/Lattice_reality.h +++ b/Grid/lattice/Lattice_reality.h @@ -45,8 +45,8 @@ template inline Lattice adj(const Lattice &lhs){ autoView( ret_v, ret, AcceleratorWrite); ret.Checkerboard()=lhs.Checkerboard(); - accelerator_for( ss, lhs_v.size(), vobj::Nsimd(), { - coalescedWrite(ret_v[ss], adj(lhs_v(ss))); + accelerator_for( ss, lhs_v.size(), 1, { + ret_v[ss] = adj(lhs_v[ss]); }); return ret; }; diff --git a/Grid/qcd/utils/SUn.h b/Grid/qcd/utils/SUn.h index 0cc0cc1a..7ac53246 100644 --- a/Grid/qcd/utils/SUn.h +++ b/Grid/qcd/utils/SUn.h @@ -449,7 +449,8 @@ public: LatticeReal alpha(grid); // std::cout<::printGenerators(); - std::cout << "Dimension of adjoint representation: "<< SUAdjoint::Dimension << std::endl; - SUAdjoint::printGenerators(); - SU::testGenerators(); - SUAdjoint::testGenerators(); + SU3::printGenerators(); + std::cout << "Dimension of adjoint representation: "<< SU3Adjoint::Dimension << std::endl; + SU3Adjoint::printGenerators(); + SU3::testGenerators(); + SU3Adjoint::testGenerators(); std::cout<({45,12,81,9})); - SUAdjoint::LatticeAdjMatrix Gauss(grid); - SU::LatticeAlgebraVector ha(grid); - SU::LatticeAlgebraVector hb(grid); + SU3Adjoint::LatticeAdjMatrix Gauss(grid); + SU3::LatticeAlgebraVector ha(grid); + SU3::LatticeAlgebraVector hb(grid); random(gridRNG,Gauss); std::cout << GridLogMessage << "Start projectOnAlgebra" << std::endl; - SUAdjoint::projectOnAlgebra(ha, Gauss); + SU3Adjoint::projectOnAlgebra(ha, Gauss); std::cout << GridLogMessage << "end projectOnAlgebra" << std::endl; std::cout << GridLogMessage << "Start projector" << std::endl; - SUAdjoint::projector(hb, Gauss); + SU3Adjoint::projector(hb, Gauss); std::cout << GridLogMessage << "end projector" << std::endl; std::cout << GridLogMessage << "ReStart projector" << std::endl; - SUAdjoint::projector(hb, Gauss); + SU3Adjoint::projector(hb, Gauss); std::cout << GridLogMessage << "end projector" << std::endl; - SU::LatticeAlgebraVector diff = ha -hb; + SU3::LatticeAlgebraVector diff = ha -hb; std::cout << GridLogMessage << "Difference: " << norm2(diff) << std::endl; @@ -114,8 +114,8 @@ int main(int argc, char** argv) { LatticeGaugeField U(grid), V(grid); - SU::HotConfiguration(gridRNG, U); - SU::HotConfiguration(gridRNG, V); + SU3::HotConfiguration(gridRNG, U); + SU3::HotConfiguration(gridRNG, V); // Adjoint representation // Test group structure @@ -123,8 +123,8 @@ int main(int argc, char** argv) { LatticeGaugeField UV(grid); UV = Zero(); for (int mu = 0; mu < Nd; mu++) { - SU::LatticeMatrix Umu = peekLorentz(U,mu); - SU::LatticeMatrix Vmu = peekLorentz(V,mu); + SU3::LatticeMatrix Umu = peekLorentz(U,mu); + SU3::LatticeMatrix Vmu = peekLorentz(V,mu); pokeLorentz(UV,Umu*Vmu, mu); } @@ -151,16 +151,16 @@ int main(int argc, char** argv) { // Check correspondence of algebra and group transformations // Create a random vector - SU::LatticeAlgebraVector h_adj(grid); + SU3::LatticeAlgebraVector h_adj(grid); typename AdjointRep::LatticeMatrix Ar(grid); random(gridRNG,h_adj); h_adj = real(h_adj); SU_Adjoint::AdjointLieAlgebraMatrix(h_adj,Ar); // Re-extract h_adj - SU::LatticeAlgebraVector h_adj2(grid); + SU3::LatticeAlgebraVector h_adj2(grid); SU_Adjoint::projectOnAlgebra(h_adj2, Ar); - SU::LatticeAlgebraVector h_diff = h_adj - h_adj2; + SU3::LatticeAlgebraVector h_diff = h_adj - h_adj2; std::cout << GridLogMessage << "Projections structure check vector difference (Adjoint representation) : " << norm2(h_diff) << std::endl; // Exponentiate @@ -183,14 +183,14 @@ int main(int argc, char** argv) { // Construct the fundamental matrix in the group - SU::LatticeMatrix Af(grid); - SU::FundamentalLieAlgebraMatrix(h_adj,Af); - SU::LatticeMatrix Ufund(grid); + SU3::LatticeMatrix Af(grid); + SU3::FundamentalLieAlgebraMatrix(h_adj,Af); + SU3::LatticeMatrix Ufund(grid); Ufund = expMat(Af, 1.0, 16); // Check unitarity - SU::LatticeMatrix uno_f(grid); + SU3::LatticeMatrix uno_f(grid); uno_f = 1.0; - SU::LatticeMatrix UnitCheck(grid); + SU3::LatticeMatrix UnitCheck(grid); UnitCheck = Ufund * adj(Ufund) - uno_f; std::cout << GridLogMessage << "unitarity check 1: " << norm2(UnitCheck) << std::endl; @@ -260,20 +260,20 @@ int main(int argc, char** argv) { std::cout << GridLogMessage << "Test for the Two Index Symmetric projectors" << std::endl; // Projectors - SUTwoIndexSymm::LatticeTwoIndexMatrix Gauss2(grid); + SU3TwoIndexSymm::LatticeTwoIndexMatrix Gauss2(grid); random(gridRNG,Gauss2); std::cout << GridLogMessage << "Start projectOnAlgebra" << std::endl; - SUTwoIndexSymm::projectOnAlgebra(ha, Gauss2); + SU3TwoIndexSymm::projectOnAlgebra(ha, Gauss2); std::cout << GridLogMessage << "end projectOnAlgebra" << std::endl; std::cout << GridLogMessage << "Start projector" << std::endl; - SUTwoIndexSymm::projector(hb, Gauss2); + SU3TwoIndexSymm::projector(hb, Gauss2); std::cout << GridLogMessage << "end projector" << std::endl; std::cout << GridLogMessage << "ReStart projector" << std::endl; - SUTwoIndexSymm::projector(hb, Gauss2); + SU3TwoIndexSymm::projector(hb, Gauss2); std::cout << GridLogMessage << "end projector" << std::endl; - SU::LatticeAlgebraVector diff2 = ha - hb; + SU3::LatticeAlgebraVector diff2 = ha - hb; std::cout << GridLogMessage << "Difference: " << norm2(diff) << std::endl; std::cout << GridLogMessage << "*********************************************" << std::endl; @@ -284,20 +284,20 @@ int main(int argc, char** argv) { std::cout << GridLogMessage << "Test for the Two index anti-Symmetric projectors" << std::endl; // Projectors - SUTwoIndexAntiSymm::LatticeTwoIndexMatrix Gauss2a(grid); + SU3TwoIndexAntiSymm::LatticeTwoIndexMatrix Gauss2a(grid); random(gridRNG,Gauss2a); std::cout << GridLogMessage << "Start projectOnAlgebra" << std::endl; - SUTwoIndexAntiSymm::projectOnAlgebra(ha, Gauss2a); + SU3TwoIndexAntiSymm::projectOnAlgebra(ha, Gauss2a); std::cout << GridLogMessage << "end projectOnAlgebra" << std::endl; std::cout << GridLogMessage << "Start projector" << std::endl; - SUTwoIndexAntiSymm::projector(hb, Gauss2a); + SU3TwoIndexAntiSymm::projector(hb, Gauss2a); std::cout << GridLogMessage << "end projector" << std::endl; std::cout << GridLogMessage << "ReStart projector" << std::endl; - SUTwoIndexAntiSymm::projector(hb, Gauss2a); + SU3TwoIndexAntiSymm::projector(hb, Gauss2a); std::cout << GridLogMessage << "end projector" << std::endl; - SU::LatticeAlgebraVector diff2a = ha - hb; + SU3::LatticeAlgebraVector diff2a = ha - hb; std::cout << GridLogMessage << "Difference: " << norm2(diff2a) << std::endl; std::cout << GridLogMessage << "*********************************************" << std::endl; @@ -311,14 +311,14 @@ int main(int argc, char** argv) { // Test group structure // (U_f * V_f)_r = U_r * V_r LatticeGaugeField U2(grid), V2(grid); - SU::HotConfiguration(gridRNG, U2); - SU::HotConfiguration(gridRNG, V2); + SU3::HotConfiguration(gridRNG, U2); + SU3::HotConfiguration(gridRNG, V2); LatticeGaugeField UV2(grid); UV2 = Zero(); for (int mu = 0; mu < Nd; mu++) { - SU::LatticeMatrix Umu2 = peekLorentz(U2,mu); - SU::LatticeMatrix Vmu2 = peekLorentz(V2,mu); + SU3::LatticeMatrix Umu2 = peekLorentz(U2,mu); + SU3::LatticeMatrix Vmu2 = peekLorentz(V2,mu); pokeLorentz(UV2,Umu2*Vmu2, mu); } @@ -345,16 +345,16 @@ int main(int argc, char** argv) { // Check correspondence of algebra and group transformations // Create a random vector - SU::LatticeAlgebraVector h_sym(grid); + SU3::LatticeAlgebraVector h_sym(grid); typename TwoIndexRep< Nc, Symmetric>::LatticeMatrix Ar_sym(grid); random(gridRNG,h_sym); h_sym = real(h_sym); SU_TwoIndex::TwoIndexLieAlgebraMatrix(h_sym,Ar_sym); // Re-extract h_sym - SU::LatticeAlgebraVector h_sym2(grid); + SU3::LatticeAlgebraVector h_sym2(grid); SU_TwoIndex< Nc, Symmetric>::projectOnAlgebra(h_sym2, Ar_sym); - SU::LatticeAlgebraVector h_diff_sym = h_sym - h_sym2; + SU3::LatticeAlgebraVector h_diff_sym = h_sym - h_sym2; std::cout << GridLogMessage << "Projections structure check vector difference (Two Index Symmetric): " << norm2(h_diff_sym) << std::endl; @@ -379,11 +379,11 @@ int main(int argc, char** argv) { // Construct the fundamental matrix in the group - SU::LatticeMatrix Af_sym(grid); - SU::FundamentalLieAlgebraMatrix(h_sym,Af_sym); - SU::LatticeMatrix Ufund2(grid); + SU3::LatticeMatrix Af_sym(grid); + SU3::FundamentalLieAlgebraMatrix(h_sym,Af_sym); + SU3::LatticeMatrix Ufund2(grid); Ufund2 = expMat(Af_sym, 1.0, 16); - SU::LatticeMatrix UnitCheck2(grid); + SU3::LatticeMatrix UnitCheck2(grid); UnitCheck2 = Ufund2 * adj(Ufund2) - uno_f; std::cout << GridLogMessage << "unitarity check 1: " << norm2(UnitCheck2) << std::endl; @@ -421,14 +421,14 @@ int main(int argc, char** argv) { // Test group structure // (U_f * V_f)_r = U_r * V_r LatticeGaugeField U2A(grid), V2A(grid); - SU::HotConfiguration(gridRNG, U2A); - SU::HotConfiguration(gridRNG, V2A); + SU3::HotConfiguration(gridRNG, U2A); + SU3::HotConfiguration(gridRNG, V2A); LatticeGaugeField UV2A(grid); UV2A = Zero(); for (int mu = 0; mu < Nd; mu++) { - SU::LatticeMatrix Umu2A = peekLorentz(U2,mu); - SU::LatticeMatrix Vmu2A = peekLorentz(V2,mu); + SU3::LatticeMatrix Umu2A = peekLorentz(U2,mu); + SU3::LatticeMatrix Vmu2A = peekLorentz(V2,mu); pokeLorentz(UV2A,Umu2A*Vmu2A, mu); } @@ -455,16 +455,16 @@ int main(int argc, char** argv) { // Check correspondence of algebra and group transformations // Create a random vector - SU::LatticeAlgebraVector h_Asym(grid); + SU3::LatticeAlgebraVector h_Asym(grid); typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Ar_Asym(grid); random(gridRNG,h_Asym); h_Asym = real(h_Asym); SU_TwoIndex< Nc, AntiSymmetric>::TwoIndexLieAlgebraMatrix(h_Asym,Ar_Asym); // Re-extract h_sym - SU::LatticeAlgebraVector h_Asym2(grid); + SU3::LatticeAlgebraVector h_Asym2(grid); SU_TwoIndex< Nc, AntiSymmetric>::projectOnAlgebra(h_Asym2, Ar_Asym); - SU::LatticeAlgebraVector h_diff_Asym = h_Asym - h_Asym2; + SU3::LatticeAlgebraVector h_diff_Asym = h_Asym - h_Asym2; std::cout << GridLogMessage << "Projections structure check vector difference (Two Index anti-Symmetric): " << norm2(h_diff_Asym) << std::endl; @@ -489,11 +489,11 @@ int main(int argc, char** argv) { // Construct the fundamental matrix in the group - SU::LatticeMatrix Af_Asym(grid); - SU::FundamentalLieAlgebraMatrix(h_Asym,Af_Asym); - SU::LatticeMatrix Ufund2A(grid); + SU3::LatticeMatrix Af_Asym(grid); + SU3::FundamentalLieAlgebraMatrix(h_Asym,Af_Asym); + SU3::LatticeMatrix Ufund2A(grid); Ufund2A = expMat(Af_Asym, 1.0, 16); - SU::LatticeMatrix UnitCheck2A(grid); + SU3::LatticeMatrix UnitCheck2A(grid); UnitCheck2A = Ufund2A * adj(Ufund2A) - uno_f; std::cout << GridLogMessage << "unitarity check 1: " << norm2(UnitCheck2A) << std::endl; From d060341168ab9082649f813fc3727fb65f08d3cd Mon Sep 17 00:00:00 2001 From: KANAMORI Issaku Date: Fri, 16 Oct 2020 21:39:17 +0900 Subject: [PATCH 019/399] add an error check for Parameters.StartingType --- Grid/qcd/hmc/GenericHMCrunner.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Grid/qcd/hmc/GenericHMCrunner.h b/Grid/qcd/hmc/GenericHMCrunner.h index c2443dd0..98e8175a 100644 --- a/Grid/qcd/hmc/GenericHMCrunner.h +++ b/Grid/qcd/hmc/GenericHMCrunner.h @@ -159,6 +159,13 @@ private: Resources.GetCheckPointer()->CheckpointRestore(Parameters.StartTrajectory, U, Resources.GetSerialRNG(), Resources.GetParallelRNG()); + } else { + // others + std::cout << GridLogError << "Unrecognized StartingType\n"; + std::cout + << GridLogError + << "Valid [HotStart, ColdStart, TepidStart, CheckpointStart]\n"; + exit(1); } Smearing.set_Field(U); From 463d72d322581a256369e9c42cdb30ce0e5595fc Mon Sep 17 00:00:00 2001 From: Raoul Hodgson Date: Mon, 19 Oct 2020 16:13:28 +0100 Subject: [PATCH 020/399] Added untraced baryon contraction code --- Grid/qcd/utils/BaryonUtils.h | 231 +++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index b268b684..beab3436 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -61,6 +61,16 @@ public: const int parity, const bool * wick_contractions, robj &result); + template + static void baryon_site_matrix(const mobj &D1, + const mobj &D2, + const mobj &D3, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool * wick_contractions, + robj &result); public: static void Wick_Contractions(std::string qi, std::string qf, @@ -75,6 +85,15 @@ public: const bool* wick_contractions, const int parity, ComplexField &baryon_corr); + static void ContractBaryons_matrix(const PropagatorField &q1_left, + const PropagatorField &q2_left, + const PropagatorField &q3_left, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + SpinMatrixField &baryon_corr); template static void ContractBaryons_Sliced(const mobj &D1, const mobj &D2, @@ -87,6 +106,17 @@ public: const int parity, const int nt, robj &result); + template + static void ContractBaryons_Sliced_matrix(const mobj &D1, + const mobj &D2, + const mobj &D3, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + const int nt, + robj &result); private: template static void Baryon_Gamma_3pt_Group1_Site( @@ -329,6 +359,126 @@ void BaryonUtils::baryon_site(const mobj &D1, }} } +//New version without parity projection or trace +template +template +void BaryonUtils::baryon_site_matrix(const mobj &D1, + const mobj &D2, + const mobj &D3, + const Gamma GammaA_i, + const Gamma GammaB_i, + const Gamma GammaA_f, + const Gamma GammaB_f, + const bool * wick_contraction, + robj &result) +{ + + auto D1_GAi = D1 * GammaA_i; + auto GAf_D1_GAi = GammaA_f * D1_GAi; + auto GBf_D1_GAi = GammaB_f * D1_GAi; + + auto D2_GBi = D2 * GammaB_i; + auto GBf_D2_GBi = GammaB_f * D2_GBi; + auto GAf_D2_GBi = GammaA_f * D2_GBi; + + auto GBf_D3 = GammaB_f * D3; + auto GAf_D3 = GammaA_f * D3; + + for (int ie_f=0; ie_f < 6 ; ie_f++){ + int a_f = epsilon[ie_f][0]; //a + int b_f = epsilon[ie_f][1]; //b + int c_f = epsilon[ie_f][2]; //c + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = epsilon[ie_i][0]; //a' + int b_i = epsilon[ie_i][1]; //b' + int c_i = epsilon[ie_i][2]; //c' + + Real ee = epsilon_sgn[ie_f] * epsilon_sgn[ie_i]; + //This is the \delta_{456}^{123} part + if (wick_contraction[0]){ + for (int rho_i=0; rho_i::ContractBaryons(const PropagatorField &q1_left, t += usecond(); std::cout << std::setw(10) << bytes/t*1.0e6/1024/1024/1024 << " GB/s " << std::endl; +} + +template +void BaryonUtils::ContractBaryons_matrix(const PropagatorField &q1_left, + const PropagatorField &q2_left, + const PropagatorField &q3_left, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + SpinMatrixField &baryon_corr) +{ + + assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); + assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); + + std::cout << "GammaA (left) " << (GammaA_left.g) << std::endl; + std::cout << "GammaB (left) " << (GammaB_left.g) << std::endl; + std::cout << "GammaA (right) " << (GammaA_right.g) << std::endl; + std::cout << "GammaB (right) " << (GammaB_right.g) << std::endl; + + GridBase *grid = q1_left.Grid(); + + autoView(vbaryon_corr, baryon_corr,CpuWrite); + autoView( v1 , q1_left, CpuRead); + autoView( v2 , q2_left, CpuRead); + autoView( v3 , q3_left, CpuRead); + + // Real bytes =0.; + // bytes += grid->oSites() * (432.*sizeof(vComplex) + 126.*sizeof(int) + 36.*sizeof(Real)); + // for (int ie=0; ie < 6 ; ie++){ + // if(ie==0 or ie==3){ + // bytes += grid->oSites() * (4.*sizeof(int) + 4752.*sizeof(vComplex)) * wick_contractions[ie]; + // } + // else{ + // bytes += grid->oSites() * (64.*sizeof(int) + 5184.*sizeof(vComplex)) * wick_contractions[ie]; + // } + // } + // Real t=0.; + // t =-usecond(); + + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto D1 = v1[ss]; + auto D2 = v2[ss]; + auto D3 = v3[ss]; + sobj result=Zero(); + baryon_site_matrix(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,wick_contractions,result); + vbaryon_corr[ss] = result; + } );//end loop over lattice sites + + // t += usecond(); + + // std::cout << std::setw(10) << bytes/t*1.0e6/1024/1024/1024 << " GB/s " << std::endl; } @@ -442,6 +646,33 @@ void BaryonUtils::ContractBaryons_Sliced(const mobj &D1, } } +template +template +void BaryonUtils::ContractBaryons_Sliced_matrix(const mobj &D1, + const mobj &D2, + const mobj &D3, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + const int nt, + robj &result) +{ + + assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); + assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); + + std::cout << "GammaA (left) " << (GammaA_left.g) << std::endl; + std::cout << "GammaB (left) " << (GammaB_left.g) << std::endl; + std::cout << "GammaA (right) " << (GammaA_right.g) << std::endl; + std::cout << "GammaB (right) " << (GammaB_right.g) << std::endl; + + for (int t=0; t Date: Wed, 21 Oct 2020 11:58:53 +0100 Subject: [PATCH 021/399] BaryonUtils function naming change --- Grid/qcd/utils/BaryonUtils.h | 86 ++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index beab3436..1259225a 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -51,7 +51,7 @@ public: private: template - static void baryon_site(const mobj &D1, + static void BaryonSite(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_left, @@ -62,7 +62,7 @@ public: const bool * wick_contractions, robj &result); template - static void baryon_site_matrix(const mobj &D1, + static void BaryonSiteMatrix(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_left, @@ -72,7 +72,7 @@ public: const bool * wick_contractions, robj &result); public: - static void Wick_Contractions(std::string qi, + static void WickContractions(std::string qi, std::string qf, bool* wick_contractions); static void ContractBaryons(const PropagatorField &q1_left, @@ -85,7 +85,7 @@ public: const bool* wick_contractions, const int parity, ComplexField &baryon_corr); - static void ContractBaryons_matrix(const PropagatorField &q1_left, + static void ContractBaryonsMatrix(const PropagatorField &q1_left, const PropagatorField &q2_left, const PropagatorField &q3_left, const Gamma GammaA_left, @@ -95,7 +95,7 @@ public: const bool* wick_contractions, SpinMatrixField &baryon_corr); template - static void ContractBaryons_Sliced(const mobj &D1, + static void ContractBaryonsSliced(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_left, @@ -107,7 +107,7 @@ public: const int nt, robj &result); template - static void ContractBaryons_Sliced_matrix(const mobj &D1, + static void ContractBaryonsSlicedMatrix(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_left, @@ -119,7 +119,7 @@ public: robj &result); private: template - static void Baryon_Gamma_3pt_Group1_Site( + static void BaryonGamma3ptGroup1Site( const mobj &Dq1_ti, const mobj2 &Dq2_spec, const mobj2 &Dq3_spec, @@ -131,7 +131,7 @@ public: robj &result); template - static void Baryon_Gamma_3pt_Group2_Site( + static void BaryonGamma3ptGroup2Site( const mobj2 &Dq1_spec, const mobj &Dq2_ti, const mobj2 &Dq3_spec, @@ -143,7 +143,7 @@ public: robj &result); template - static void Baryon_Gamma_3pt_Group3_Site( + static void BaryonGamma3ptGroup3Site( const mobj2 &Dq1_spec, const mobj2 &Dq2_spec, const mobj &Dq3_ti, @@ -155,7 +155,7 @@ public: robj &result); public: template - static void Baryon_Gamma_3pt( + static void BaryonGamma3pt( const PropagatorField &q_ti, const mobj &Dq_spec1, const mobj &Dq_spec2, @@ -168,7 +168,7 @@ public: SpinMatrixField &stn_corr); private: template - static void Sigma_to_Nucleon_Q1_Eye_site(const mobj &Dq_loop, + static void SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, @@ -177,7 +177,7 @@ public: const Gamma GammaB_nucl, robj &result); template - static void Sigma_to_Nucleon_Q1_NonEye_site(const mobj &Du_ti, + static void SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, const mobj &Du_tf, const mobj2 &Du_spec, const mobj &Dd_tf, @@ -189,7 +189,7 @@ public: template - static void Sigma_to_Nucleon_Q2_Eye_site(const mobj &Dq_loop, + static void SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, @@ -198,7 +198,7 @@ public: const Gamma GammaB_nucl, robj &result); template - static void Sigma_to_Nucleon_Q2_NonEye_site(const mobj &Du_ti, + static void SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, const mobj &Du_tf, const mobj2 &Du_spec, const mobj &Dd_tf, @@ -209,7 +209,7 @@ public: robj &result); public: template - static void Sigma_to_Nucleon_Eye(const PropagatorField &qq_loop, + static void SigmaToNucleonEye(const PropagatorField &qq_loop, const mobj &Du_spec, const PropagatorField &qd_tf, const PropagatorField &qs_ti, @@ -219,7 +219,7 @@ public: const std::string op, SpinMatrixField &stn_corr); template - static void Sigma_to_Nucleon_NonEye(const PropagatorField &qq_ti, + static void SigmaToNucleonNonEye(const PropagatorField &qq_ti, const PropagatorField &qq_tf, const mobj &Du_spec, const PropagatorField &qd_tf, @@ -247,7 +247,7 @@ const Real BaryonUtils::epsilon_sgn[6] = {1.,1.,1.,-1.,-1.,-1.}; //This is the old version template template -void BaryonUtils::baryon_site(const mobj &D1, +void BaryonUtils::BaryonSite(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_i, @@ -362,7 +362,7 @@ void BaryonUtils::baryon_site(const mobj &D1, //New version without parity projection or trace template template -void BaryonUtils::baryon_site_matrix(const mobj &D1, +void BaryonUtils::BaryonSiteMatrix(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_i, @@ -484,7 +484,7 @@ void BaryonUtils::baryon_site_matrix(const mobj &D1, * flavours. * * The array wick_contractions must be of length 6 */ template -void BaryonUtils::Wick_Contractions(std::string qi, std::string qf, bool* wick_contractions) { +void BaryonUtils::WickContractions(std::string qi, std::string qf, bool* wick_contractions) { const int epsilon[6][3] = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; for (int ie=0; ie < 6 ; ie++) { wick_contractions[ie] = (qi.size() == 3 && qf.size() == 3 @@ -547,7 +547,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, auto D2 = v2[ss]; auto D3 = v3[ss]; vobj result=Zero(); - baryon_site(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,parity,wick_contractions,result); + BaryonSite(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,parity,wick_contractions,result); vbaryon_corr[ss] = result; } );//end loop over lattice sites @@ -557,7 +557,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, } template -void BaryonUtils::ContractBaryons_matrix(const PropagatorField &q1_left, +void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, const PropagatorField &q2_left, const PropagatorField &q3_left, const Gamma GammaA_left, @@ -601,7 +601,7 @@ void BaryonUtils::ContractBaryons_matrix(const PropagatorField &q1_left, auto D2 = v2[ss]; auto D3 = v3[ss]; sobj result=Zero(); - baryon_site_matrix(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,wick_contractions,result); + BaryonSiteMatrix(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,wick_contractions,result); vbaryon_corr[ss] = result; } );//end loop over lattice sites @@ -618,7 +618,7 @@ void BaryonUtils::ContractBaryons_matrix(const PropagatorField &q1_left, * Wick_Contractions function above */ template template -void BaryonUtils::ContractBaryons_Sliced(const mobj &D1, +void BaryonUtils::ContractBaryonsSliced(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_left, @@ -642,13 +642,13 @@ void BaryonUtils::ContractBaryons_Sliced(const mobj &D1, assert(parity==1 || parity == -1 && "Parity must be +1 or -1"); for (int t=0; t template -void BaryonUtils::ContractBaryons_Sliced_matrix(const mobj &D1, +void BaryonUtils::ContractBaryonsSlicedMatrix(const mobj &D1, const mobj &D2, const mobj &D3, const Gamma GammaA_left, @@ -669,7 +669,7 @@ void BaryonUtils::ContractBaryons_Sliced_matrix(const mobj &D1, std::cout << "GammaB (right) " << (GammaB_right.g) << std::endl; for (int t=0; t::ContractBaryons_Sliced_matrix(const mobj &D1, * Dq4_tf is a quark line from t_f to t_J */ template template -void BaryonUtils::Baryon_Gamma_3pt_Group1_Site( +void BaryonUtils::BaryonGamma3ptGroup1Site( const mobj &Dq1_ti, const mobj2 &Dq2_spec, const mobj2 &Dq3_spec, @@ -777,7 +777,7 @@ void BaryonUtils::Baryon_Gamma_3pt_Group1_Site( * Dq4_tf is a quark line from t_f to t_J */ template template -void BaryonUtils::Baryon_Gamma_3pt_Group2_Site( +void BaryonUtils::BaryonGamma3ptGroup2Site( const mobj2 &Dq1_spec, const mobj &Dq2_ti, const mobj2 &Dq3_spec, @@ -867,7 +867,7 @@ void BaryonUtils::Baryon_Gamma_3pt_Group2_Site( * Dq4_tf is a quark line from t_f to t_J */ template template -void BaryonUtils::Baryon_Gamma_3pt_Group3_Site( +void BaryonUtils::BaryonGamma3ptGroup3Site( const mobj2 &Dq1_spec, const mobj2 &Dq2_spec, const mobj &Dq3_ti, @@ -959,7 +959,7 @@ void BaryonUtils::Baryon_Gamma_3pt_Group3_Site( * https://aportelli.github.io/Hadrons-doc/#/mcontraction */ template template -void BaryonUtils::Baryon_Gamma_3pt( +void BaryonUtils::BaryonGamma3pt( const PropagatorField &q_ti, const mobj &Dq_spec1, const mobj &Dq_spec2, @@ -982,7 +982,7 @@ void BaryonUtils::Baryon_Gamma_3pt( auto Dq_ti = vq_ti[ss]; auto Dq_tf = vq_tf[ss]; sobj result=Zero(); - Baryon_Gamma_3pt_Group1_Site(Dq_ti,Dq_spec1,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); vcorr[ss] += result; });//end loop over lattice sites } else if (group == 2) { @@ -990,7 +990,7 @@ void BaryonUtils::Baryon_Gamma_3pt( auto Dq_ti = vq_ti[ss]; auto Dq_tf = vq_tf[ss]; sobj result=Zero(); - Baryon_Gamma_3pt_Group2_Site(Dq_spec1,Dq_ti,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); vcorr[ss] += result; });//end loop over lattice sites } else if (group == 3) { @@ -998,7 +998,7 @@ void BaryonUtils::Baryon_Gamma_3pt( auto Dq_ti = vq_ti[ss]; auto Dq_tf = vq_tf[ss]; sobj result=Zero(); - Baryon_Gamma_3pt_Group3_Site(Dq_spec1,Dq_spec2,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + BaryonGamma3ptGroup3Site(Dq_spec1,Dq_spec2,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); vcorr[ss] += result; });//end loop over lattice sites @@ -1018,7 +1018,7 @@ void BaryonUtils::Baryon_Gamma_3pt( * Ds_ti is a quark line from t_i to t_H */ template template -void BaryonUtils::Sigma_to_Nucleon_Q1_Eye_site(const mobj &Dq_loop, +void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, @@ -1069,7 +1069,7 @@ void BaryonUtils::Sigma_to_Nucleon_Q1_Eye_site(const mobj &Dq_loop, * Ds_ti is a quark line from t_i to t_H */ template template -void BaryonUtils::Sigma_to_Nucleon_Q1_NonEye_site(const mobj &Du_ti, +void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, const mobj &Du_tf, const mobj2 &Du_spec, const mobj &Dd_tf, @@ -1128,7 +1128,7 @@ void BaryonUtils::Sigma_to_Nucleon_Q1_NonEye_site(const mobj &Du_ti, * Ds_ti is a quark line from t_i to t_H */ template template -void BaryonUtils::Sigma_to_Nucleon_Q2_Eye_site(const mobj &Dq_loop, +void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, @@ -1179,7 +1179,7 @@ void BaryonUtils::Sigma_to_Nucleon_Q2_Eye_site(const mobj &Dq_loop, * Ds_ti is a quark line from t_i to t_H */ template template -void BaryonUtils::Sigma_to_Nucleon_Q2_NonEye_site(const mobj &Du_ti, +void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, const mobj &Du_tf, const mobj2 &Du_spec, const mobj &Dd_tf, @@ -1233,7 +1233,7 @@ void BaryonUtils::Sigma_to_Nucleon_Q2_NonEye_site(const mobj &Du_ti, template template -void BaryonUtils::Sigma_to_Nucleon_Eye(const PropagatorField &qq_loop, +void BaryonUtils::SigmaToNucleonEye(const PropagatorField &qq_loop, const mobj &Du_spec, const PropagatorField &qd_tf, const PropagatorField &qs_ti, @@ -1260,9 +1260,9 @@ void BaryonUtils::Sigma_to_Nucleon_Eye(const PropagatorField &qq_loop, auto Ds_ti = vs_ti[ss]; sobj result=Zero(); if(op == "Q1"){ - Sigma_to_Nucleon_Q1_Eye_site(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ1EyeSite(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else if(op == "Q2"){ - Sigma_to_Nucleon_Q2_Eye_site(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ2EyeSite(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else { assert(0 && "Weak Operator not correctly specified"); } @@ -1272,7 +1272,7 @@ void BaryonUtils::Sigma_to_Nucleon_Eye(const PropagatorField &qq_loop, template template -void BaryonUtils::Sigma_to_Nucleon_NonEye(const PropagatorField &qq_ti, +void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, const PropagatorField &qq_tf, const mobj &Du_spec, const PropagatorField &qd_tf, @@ -1302,9 +1302,9 @@ void BaryonUtils::Sigma_to_Nucleon_NonEye(const PropagatorField &qq_ti, auto Ds_ti = vs_ti[ss]; sobj result=Zero(); if(op == "Q1"){ - Sigma_to_Nucleon_Q1_NonEye_site(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ1NonEyeSite(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else if(op == "Q2"){ - Sigma_to_Nucleon_Q2_NonEye_site(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ2NonEyeSite(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else { assert(0 && "Weak Operator not correctly specified"); } From 52d17987dc3e09f537fa8d7d3a9403712e3d62c5 Mon Sep 17 00:00:00 2001 From: Raoul Hodgson Date: Fri, 23 Oct 2020 11:41:08 +0100 Subject: [PATCH 022/399] BaryonUtils.h updated debug output --- Grid/qcd/utils/BaryonUtils.h | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 1259225a..15516b56 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -514,11 +514,6 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); - - std::cout << "GammaA (left) " << (GammaA_left.g) << std::endl; - std::cout << "GammaB (left) " << (GammaB_left.g) << std::endl; - std::cout << "GammaA (right) " << (GammaA_right.g) << std::endl; - std::cout << "GammaB (right) " << (GammaB_right.g) << std::endl; assert(parity==1 || parity == -1 && "Parity must be +1 or -1"); @@ -553,7 +548,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, t += usecond(); - std::cout << std::setw(10) << bytes/t*1.0e6/1024/1024/1024 << " GB/s " << std::endl; + std::cout << GridLogDebug << std::setw(10) << bytes/t*1.0e6/1024/1024/1024 << " GB/s " << std::endl; } template @@ -570,11 +565,6 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); - - std::cout << "GammaA (left) " << (GammaA_left.g) << std::endl; - std::cout << "GammaB (left) " << (GammaB_left.g) << std::endl; - std::cout << "GammaA (right) " << (GammaA_right.g) << std::endl; - std::cout << "GammaB (right) " << (GammaB_right.g) << std::endl; GridBase *grid = q1_left.Grid(); @@ -607,7 +597,7 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, // t += usecond(); - // std::cout << std::setw(10) << bytes/t*1.0e6/1024/1024/1024 << " GB/s " << std::endl; + // std::cout << GridLogDebug << std::setw(10) << bytes/t*1.0e6/1024/1024/1024 << " GB/s " << std::endl; } @@ -633,11 +623,6 @@ void BaryonUtils::ContractBaryonsSliced(const mobj &D1, assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); - - std::cout << "GammaA (left) " << (GammaA_left.g) << std::endl; - std::cout << "GammaB (left) " << (GammaB_left.g) << std::endl; - std::cout << "GammaA (right) " << (GammaA_right.g) << std::endl; - std::cout << "GammaB (right) " << (GammaB_right.g) << std::endl; assert(parity==1 || parity == -1 && "Parity must be +1 or -1"); @@ -663,11 +648,6 @@ void BaryonUtils::ContractBaryonsSlicedMatrix(const mobj &D1, assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); - std::cout << "GammaA (left) " << (GammaA_left.g) << std::endl; - std::cout << "GammaB (left) " << (GammaB_left.g) << std::endl; - std::cout << "GammaA (right) " << (GammaA_right.g) << std::endl; - std::cout << "GammaB (right) " << (GammaB_right.g) << std::endl; - for (int t=0; t Date: Tue, 20 Oct 2020 10:11:43 +0200 Subject: [PATCH 023/399] Thread inversion of clover term --- .../WilsonCloverFermionImplementation.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h index e721c20d..3032a80c 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h @@ -92,20 +92,16 @@ void WilsonCloverFermion::ImportGauge(const GaugeField &_Umu) int lvol = _Umu.Grid()->lSites(); int DimRep = Impl::Dimension; - Eigen::MatrixXcd EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); - Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); - - Coordinate lcoor; - typename SiteCloverType::scalar_object Qx = Zero(), Qxinv = Zero(); - { autoView(CTv,CloverTerm,CpuRead); autoView(CTIv,CloverTermInv,CpuWrite); - for (int site = 0; site < lvol; site++) { + thread_for(site, lvol, { + Coordinate lcoor; grid->LocalIndexToLocalCoor(site, lcoor); - EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); + Eigen::MatrixXcd EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); + Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); + typename SiteCloverType::scalar_object Qx = Zero(), Qxinv = Zero(); peekLocalSite(Qx, CTv, lcoor); - Qxinv = Zero(); //if (csw!=0){ for (int j = 0; j < Ns; j++) for (int k = 0; k < Ns; k++) @@ -126,7 +122,7 @@ void WilsonCloverFermion::ImportGauge(const GaugeField &_Umu) // if (site==0) std::cout << "site =" << site << "\n" << EigenInvCloverOp << std::endl; // } pokeLocalSite(Qxinv, CTIv, lcoor); - } + }); } // Separate the even and odd parts From f313565a3cdc9e9487921f7c4d33c14c1292082b Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sat, 31 Oct 2020 12:12:40 +0000 Subject: [PATCH 024/399] HiP compile --- Grid/serialisation/JSON_IO.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/serialisation/JSON_IO.cc b/Grid/serialisation/JSON_IO.cc index aca8bab3..f2282099 100644 --- a/Grid/serialisation/JSON_IO.cc +++ b/Grid/serialisation/JSON_IO.cc @@ -26,7 +26,7 @@ *************************************************************************************/ /* END LEGAL */ #include -#ifndef __NVCC__ +#if (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) NAMESPACE_BEGIN(Grid); From d10422ded88d34a6e72c16c31cfc74dd4805ca5b Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sat, 31 Oct 2020 18:12:30 -0400 Subject: [PATCH 025/399] Test project on group --- tests/core/Test_unary.cc | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 tests/core/Test_unary.cc diff --git a/tests/core/Test_unary.cc b/tests/core/Test_unary.cc new file mode 100644 index 00000000..2ad6ba7b --- /dev/null +++ b/tests/core/Test_unary.cc @@ -0,0 +1,106 @@ + /************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/Test_quenched_update.cc + + Copyright (C) 2015 + +Author: Azusa Yamaguchi +Author: Peter Boyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include + +using namespace std; +using namespace Grid; + ; + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + std::vector latt({8,8,8,8}); + GridCartesian * grid = SpaceTimeGrid::makeFourDimGrid(latt, + GridDefaultSimd(Nd,vComplexD::Nsimd()), + GridDefaultMpi()); + + GridCartesian * gridF = SpaceTimeGrid::makeFourDimGrid(latt, + GridDefaultSimd(Nd,vComplexF::Nsimd()), + GridDefaultMpi()); + + + /////////////////////////////// + // Configuration of known size + /////////////////////////////// + LatticeColourMatrixD ident(grid); + LatticeColourMatrixD U(grid); + LatticeColourMatrixD tmp(grid); + LatticeColourMatrixD org(grid); + LatticeColourMatrixF UF(gridF); + + LatticeGaugeField Umu(grid); + + ident =1.0; + + // RNG set up for test + std::vector pseeds({1,2,3,4,5}); // once I caught a fish alive + std::vector sseeds({6,7,8,9,10});// then i let it go again + GridParallelRNG pRNG(grid); pRNG.SeedFixedIntegers(pseeds); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(sseeds); + + SU::HotConfiguration(pRNG,Umu); + + U = PeekIndex(Umu,0); + org=U; + + + tmp= U*adj(U) - ident ; + RealD Def1 = norm2( tmp ); + std::cout << " Defect1 "< Date: Sat, 31 Oct 2020 18:12:47 -0400 Subject: [PATCH 026/399] Project on group fix on GPU tracked to reciprocal sqrt collision between CUDA and Grid rsqrt --- Grid/lattice/Lattice_ET.h | 2 -- Grid/simd/Grid_vector_unops.h | 8 -------- Grid/tensors/Tensor_Ta.h | 7 ++++++- Grid/tensors/Tensor_unary.h | 1 - 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Grid/lattice/Lattice_ET.h b/Grid/lattice/Lattice_ET.h index f828ef30..4a8a7423 100644 --- a/Grid/lattice/Lattice_ET.h +++ b/Grid/lattice/Lattice_ET.h @@ -350,7 +350,6 @@ GridUnopClass(UnaryTimesI, timesI(a)); GridUnopClass(UnaryTimesMinusI, timesMinusI(a)); GridUnopClass(UnaryAbs, abs(a)); GridUnopClass(UnarySqrt, sqrt(a)); -GridUnopClass(UnaryRsqrt, rsqrt(a)); GridUnopClass(UnarySin, sin(a)); GridUnopClass(UnaryCos, cos(a)); GridUnopClass(UnaryAsin, asin(a)); @@ -463,7 +462,6 @@ GRID_DEF_UNOP(timesMinusI, UnaryTimesMinusI); GRID_DEF_UNOP(abs, UnaryAbs); // abs overloaded in cmath C++98; DON'T do the // abs-fabs-dabs-labs thing GRID_DEF_UNOP(sqrt, UnarySqrt); -GRID_DEF_UNOP(rsqrt, UnaryRsqrt); GRID_DEF_UNOP(sin, UnarySin); GRID_DEF_UNOP(cos, UnaryCos); GRID_DEF_UNOP(asin, UnaryAsin); diff --git a/Grid/simd/Grid_vector_unops.h b/Grid/simd/Grid_vector_unops.h index d225699b..b89bb785 100644 --- a/Grid/simd/Grid_vector_unops.h +++ b/Grid/simd/Grid_vector_unops.h @@ -125,14 +125,6 @@ accelerator_inline Grid_simd sqrt(const Grid_simd &r) { return SimdApply(SqrtRealFunctor(), r); } template -accelerator_inline Grid_simd rsqrt(const Grid_simd &r) { - return SimdApply(RSqrtRealFunctor(), r); -} -template -accelerator_inline Scalar rsqrt(const Scalar &r) { - return (RSqrtRealFunctor(), r); -} -template accelerator_inline Grid_simd cos(const Grid_simd &r) { return SimdApply(CosRealFunctor(), r); } diff --git a/Grid/tensors/Tensor_Ta.h b/Grid/tensors/Tensor_Ta.h index 1ef9fc23..f7af85b7 100644 --- a/Grid/tensors/Tensor_Ta.h +++ b/Grid/tensors/Tensor_Ta.h @@ -92,17 +92,22 @@ accelerator_inline iMatrix ProjectOnGroup(const iMatrix &arg) { // need a check for the group type? iMatrix ret(arg); + vtype rnrm; vtype nrm; vtype inner; for(int c1=0;c1 Date: Sat, 31 Oct 2020 18:14:31 -0400 Subject: [PATCH 027/399] Hip Free managed --- Grid/threads/Accelerator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index da8a85b0..d1a96266 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -328,7 +328,7 @@ inline void *acceleratorAllocDevice(size_t bytes) return ptr; }; -inline void acceleratorFreeShared(void *ptr){ free(ptr);}; +inline void acceleratorFreeShared(void *ptr){ hipFree(ptr);}; inline void acceleratorFreeDevice(void *ptr){ hipFree(ptr);}; inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { hipMemcpy(to,from,bytes, hipMemcpyHostToDevice);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ hipMemcpy(to,from,bytes, hipMemcpyDeviceToHost);} From 5eeabaa2bb3f65f911817a4783fc43a45180baa5 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sun, 1 Nov 2020 01:16:01 +0000 Subject: [PATCH 028/399] HIP fix --- Grid/lattice/Lattice_basis.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Grid/lattice/Lattice_basis.h b/Grid/lattice/Lattice_basis.h index af9d7280..95f55d10 100644 --- a/Grid/lattice/Lattice_basis.h +++ b/Grid/lattice/Lattice_basis.h @@ -62,7 +62,7 @@ void basisRotate(VField &basis,Matrix& Qt,int j0, int j1, int k0,int k1,int Nm) basis_v.push_back(basis[k].View(AcceleratorWrite)); } -#if ( (!defined(GRID_SYCL)) && (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) ) +#if ( (!defined(GRID_SYCL)) && (!defined(GRID_CUDA)) ) int max_threads = thread_max(); Vector < vobj > Bt(Nm * max_threads); thread_region @@ -161,11 +161,12 @@ void basisRotateJ(Field &result,std::vector &basis,Eigen::MatrixXd& Qt,in double * Qt_j = & Qt_jv[0]; for(int k=0;koSites(),vobj::Nsimd(),{ auto B=coalescedRead(zz); for(int k=k0; k Date: Tue, 3 Nov 2020 12:41:35 +0000 Subject: [PATCH 029/399] added Xi-to-Sigma rare decays --- Grid/qcd/utils/BaryonUtils.h | 169 +++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 15516b56..e6b52a43 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -1292,4 +1292,173 @@ void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, } );//end loop over lattice sites } + +/*********************************************************************** + * The following code is for Xi -> Sigma rare hypeon decays * + **********************************************************************/ + +/* Dq_loop is a quark line from t_H to t_H + * Dd_spec is a quark line from t_i to t_f + * Ds_spec is a quark line from t_i to t_f + * Dd_tf is a quark line from t_f to t_H + * Ds_ti is a quark line from t_i to t_H */ +template +template +void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, + const mobj2 &Dd_spec, + const mobj2 &Ds_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_xi, + const Gamma GammaB_sigma, + robj &result) +{ + + Gamma g5(Gamma::Algebra::Gamma5); + + auto DdG = Dd_spec * GammaB_sigma; + auto GDs = GammaB_xi * Ds_spec; + // Ds * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) + auto DsGDd = Ds_ti * Gamma_H * g5 * adj(Dd_tf) * g5; + // DsGDd * GammaB + auto DsGDdG = DsGDd * GammaB_sigma; + // GammaB * DsGDd + auto GDsGDd = GammaB_xi * DsGDd; + // GammaB * DsGDd * GammaB + auto GDsGDdG = GDsGDd * GammaB_sigma; + // \gamma_\mu^L * Dq_loop + auto trGDq = trace(GammaH * Dq_loop); + + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = epsilon[ie_s][0]; //a + int b_s = epsilon[ie_s][1]; //b + int c_s = epsilon[ie_s][2]; //c + for (int ie_x=0; ie_x < 6 ; ie_x++){ + int a_x = epsilon[ie_x][0]; //a' + int b_x = epsilon[ie_x][1]; //b' + int c_x = epsilon[ie_x][2]; //c' + auto ee_GD = epsilon_sgn[ie_s] * epsilon_sgn[ie_x] * trGDq; + for (int alpha_x=0; alpha_x +template +void BaryonUtils::XiToSigmaQ2EyeSite(const mobj &Dq_loop, + const mobj2 &Dd_spec, + const mobj2 &Ds_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_xi, + const Gamma GammaB_sigma, + robj &result) +{ + + Gamma g5(Gamma::Algebra::Gamma5); + + auto DdG = Dd_spec * GammaB_sigma; + auto GDs = GammaB_xi * Ds_spec; + // Ds * \gamma_\mu^L * Dq_loop * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) + auto DsGDqGDd = Ds_ti * Gamma_H * Dq_loop * Gamma_H * g5 * adj(Dd_tf) * g5; + // DsGDd * GammaB + auto DsGDqGDdG = DsGDqGDd * GammaB_sigma; + // GammaB * DsGDd + auto GDsGDqGDd = GammaB_xi * DsGDqGDd; + // GammaB * DsGDd * GammaB + auto GDsGDqGDdG = GDsGDqGDd * GammaB_sigma; + + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = epsilon[ie_s][0]; //a + int b_s = epsilon[ie_s][1]; //b + int c_s = epsilon[ie_s][2]; //c + for (int ie_x=0; ie_x < 6 ; ie_x++){ + int a_x = epsilon[ie_x][0]; //a' + int b_x = epsilon[ie_x][1]; //b' + int c_x = epsilon[ie_x][2]; //c' + auto ee = epsilon_sgn[ie_s] * epsilon_sgn[ie_x]; + for (int alpha_x=0; alpha_x +template +void BaryonUtils::XiToSigmaEye(const PropagatorField &qq_loop, + const mobj &Dd_spec, + const mobj &Ds_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_xi, + const Gamma GammaB_sigma, + const std::string op, + SpinMatrixField &stn_corr) +{ + + assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); + assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); + + GridBase *grid = qs_ti.Grid(); + + autoView( vcorr, stn_corr, CpuWrite); + autoView( vq_loop , qq_loop, CpuRead); + autoView( vd_tf , qd_tf, CpuRead); + autoView( vs_ti , qs_ti, CpuRead); + + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_loop = vq_loop[ss]; + auto Dd_tf = vd_tf[ss]; + auto Ds_ti = vs_ti[ss]; + sobj result=Zero(); + if(op == "Q1"){ + XiToSigmaQ1EyeSite(Dq_loop,Dd_spec,Ds_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); + } else if(op == "Q2"){ + XiToSigmaQ2EyeSite(Dq_loop,Dd_spec,Ds_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); + } else { + assert(0 && "Weak Operator not correctly specified"); + } + vcorr[ss] = result; + } );//end loop over lattice sites +} + + NAMESPACE_END(Grid); From a3de7026c8d091b4d42eba5e5cb8e57419e0963e Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 3 Nov 2020 12:51:50 +0000 Subject: [PATCH 030/399] bugfix --- Grid/qcd/utils/BaryonUtils.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index e6b52a43..8e6bf722 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -207,6 +207,26 @@ public: const Gamma GammaB_sigma, const Gamma GammaB_nucl, robj &result); + template + static void XiToSigmaQ1EyeSite(const mobj &Dq_loop, + const mobj2 &Dd_spec, + const mobj2 &Ds_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); + template + static void XiToSigmaQ2EyeSite(const mobj &Dq_loop, + const mobj2 &Dd_spec, + const mobj2 &Ds_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); public: template static void SigmaToNucleonEye(const PropagatorField &qq_loop, @@ -229,6 +249,17 @@ public: const Gamma GammaB_nucl, const std::string op, SpinMatrixField &stn_corr); + template + static void XiToSigmaEye(const PropagatorField &qq_loop, + const mobj &Dd_spec, + const mobj &Ds_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + const std::string op, + SpinMatrixField &stn_corr); }; template From 67023c334b16938c36af9f41647ba42891008e03 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 3 Nov 2020 13:07:37 +0000 Subject: [PATCH 031/399] bugfix --- Grid/qcd/utils/BaryonUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 8e6bf722..a281a669 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -1359,7 +1359,7 @@ void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, // GammaB * DsGDd * GammaB auto GDsGDdG = GDsGDd * GammaB_sigma; // \gamma_\mu^L * Dq_loop - auto trGDq = trace(GammaH * Dq_loop); + auto trGDq = trace(Gamma_H * Dq_loop); for (int ie_s=0; ie_s < 6 ; ie_s++){ int a_s = epsilon[ie_s][0]; //a From 4014dfd5b9a79223023ab2bee3789a7efde45945 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 3 Nov 2020 16:13:08 +0000 Subject: [PATCH 032/399] first tested version --- Grid/qcd/utils/BaryonUtils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index a281a669..f5bc1480 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -1359,7 +1359,7 @@ void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, // GammaB * DsGDd * GammaB auto GDsGDdG = GDsGDd * GammaB_sigma; // \gamma_\mu^L * Dq_loop - auto trGDq = trace(Gamma_H * Dq_loop); + auto trGDq = TensorRemove(trace(Gamma_H * Dq_loop)); for (int ie_s=0; ie_s < 6 ; ie_s++){ int a_s = epsilon[ie_s][0]; //a @@ -1369,7 +1369,7 @@ void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, int a_x = epsilon[ie_x][0]; //a' int b_x = epsilon[ie_x][1]; //b' int c_x = epsilon[ie_x][2]; //c' - auto ee_GD = epsilon_sgn[ie_s] * epsilon_sgn[ie_x] * trGDq; + auto ee_GD = epsilon_sgn[ie_s] * epsilon_sgn[ie_x] * trGDq; for (int alpha_x=0; alpha_x Date: Tue, 3 Nov 2020 20:03:09 +0000 Subject: [PATCH 033/399] speedup in Sigma-to-nucleon --- Grid/qcd/utils/BaryonUtils.h | 193 ++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index f5bc1480..c0999a4a 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -1027,7 +1027,7 @@ void BaryonUtils::BaryonGamma3pt( * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -template +/*template template void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, @@ -1071,6 +1071,50 @@ void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, }} } } +}*/ +template +template +void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) +{ + + Gamma g5(Gamma::Algebra::Gamma5); + + auto DuG = Du_spec * GammaB_nucl; + // Gamma^B * Ds * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) + auto GDsGDd = GammaB_sigma * Ds_ti * Gamma_H * g5 * adj(Dd_tf) * g5; + // Dq_loop * \gamma_\mu^L + auto trDqG = TensorRemove(trace(Dq_loop * Gamma_H)); + + for (int ie_n=0; ie_n < 6 ; ie_n++){ + int a_n = epsilon[ie_n][0]; //a + int b_n = epsilon[ie_n][1]; //b + int c_n = epsilon[ie_n][2]; //c + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = epsilon[ie_s][0]; //a' + int b_s = epsilon[ie_s][1]; //b' + int c_s = epsilon[ie_s][2]; //c' + for (int alpha_s=0; alpha_s::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -template +/*template template void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, const mobj &Du_tf, @@ -1130,6 +1174,55 @@ void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, }} } } +}*/ +template +template +void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, + const mobj &Du_tf, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) +{ + + Gamma g5(Gamma::Algebra::Gamma5); + + auto DuG = Du_spec * GammaB_nucl; + // Gamma^B * Ds * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) + auto GDsGDd = GammaB_sigma * Ds_ti * Gamma_H * g5 * adj(Dd_tf) * g5; + // Du_ti * \gamma_\mu^L * adj(Du_tf) + auto DuGHDu = Du_ti * Gamma_H * g5 * adj(Du_tf) * g5; + auto DuGHDuG = DuGHDu * GammaB_nucl; + + for (int ie_n=0; ie_n < 6 ; ie_n++){ + int a_n = epsilon[ie_n][0]; //a + int b_n = epsilon[ie_n][1]; //b + int c_n = epsilon[ie_n][2]; //c + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = epsilon[ie_s][0]; //a' + int b_s = epsilon[ie_s][1]; //b' + int c_s = epsilon[ie_s][2]; //c' + for (int alpha_s=0; alpha_s::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -template +/*template template void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, @@ -1181,6 +1274,47 @@ void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, }}} } } +}*/ +template +template +void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) +{ + + Gamma g5(Gamma::Algebra::Gamma5); + + auto DuG = Du_spec * GammaB_nucl; + // Gamma^B * Ds * \gamma_\mu^L * Dq_loop * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) + auto GDsGDqGDd = GammaB_sigma * Ds_ti * Gamma_H * Dq_loop * Gamma_H * g5 * adj(Dd_tf) * g5; + + for (int ie_n=0; ie_n < 6 ; ie_n++){ + int a_n = epsilon[ie_n][0]; //a + int b_n = epsilon[ie_n][1]; //b + int c_n = epsilon[ie_n][2]; //c + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = epsilon[ie_s][0]; //a' + int b_s = epsilon[ie_s][1]; //b' + int c_s = epsilon[ie_s][2]; //c' + for (int alpha_s=0; alpha_s::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -template +/*template template void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, const mobj &Du_tf, @@ -1240,6 +1374,57 @@ void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, }}} } } +}*/ +template +template +void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, + const mobj &Du_tf, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) +{ + + Gamma g5(Gamma::Algebra::Gamma5); + + auto DuG = Du_spec * GammaB_nucl; + // Gamma^B * Ds * \gamma_\mu^L * adj(Du) + auto GDsGDu = GammaB_sigma * Ds_ti * Gamma_H * g5 * adj(Du_tf) * g5; + // GDsGDu * GammaB + auto GDsGDuG = GDsGDu * GammaB_nucl; + // Du * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) + auto DuGDd = Du_ti * Gamma_H * g5 * adj(Dd_tf) * g5; + + for (int ie_n=0; ie_n < 6 ; ie_n++){ + int a_n = epsilon[ie_n][0]; //a + int b_n = epsilon[ie_n][1]; //b + int c_n = epsilon[ie_n][2]; //c + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = epsilon[ie_s][0]; //a' + int b_s = epsilon[ie_s][1]; //b' + int c_s = epsilon[ie_s][2]; //c' + auto ee = epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + for (int alpha_s=0; alpha_s From 3594ce877beb22161dcce972f14bd89598972fd6 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 3 Nov 2020 20:04:30 +0000 Subject: [PATCH 034/399] speedup in Sigma-to-nucleon --- Grid/qcd/utils/BaryonUtils.h | 196 ----------------------------------- 1 file changed, 196 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index c0999a4a..8a4ff6ac 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -1027,51 +1027,6 @@ void BaryonUtils::BaryonGamma3pt( * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -/*template -template -void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) -{ - - Gamma g5(Gamma::Algebra::Gamma5); - - auto DuG = Du_spec * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto GDsGDd = GammaB_sigma * Ds_ti * Gamma_H * g5 * adj(Dd_tf) * g5; - // Dq_loop * \gamma_\mu^L - auto DqG = Dq_loop * Gamma_H; - - for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s template void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, @@ -1122,59 +1077,6 @@ void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -/*template -template -void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, - const mobj &Du_tf, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) -{ - - Gamma g5(Gamma::Algebra::Gamma5); - - auto DuG = Du_spec * GammaB_nucl; - auto adjDu = g5 * adj(Du_tf) * g5; - auto adjDuG = adjDu * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto GDsGDd = GammaB_sigma * Ds_ti * Gamma_H * g5 * adj(Dd_tf) * g5; - // Dq_loop * \gamma_\mu^L - auto DuGH = Du_ti * Gamma_H; - - for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s template void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, @@ -1230,51 +1132,6 @@ void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -/*template -template -void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) -{ - - Gamma g5(Gamma::Algebra::Gamma5); - - auto DuG = Du_spec * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L - auto GDsG = GammaB_sigma * Ds_ti * Gamma_H; - // Dq_loop * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto DqGDd = Dq_loop * Gamma_H * g5 * adj(Dd_tf) * g5; - - for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s template void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, @@ -1322,59 +1179,6 @@ void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, * Du_spec is a quark line from t_i to t_f * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ -/*template -template -void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, - const mobj &Du_tf, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) -{ - - Gamma g5(Gamma::Algebra::Gamma5); - - auto DuG = Du_spec * GammaB_nucl; - auto adjDu = g5 * adj(Du_tf) * g5; - auto adjDuG = adjDu * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L - auto GDsG = GammaB_sigma * Ds_ti * Gamma_H; - // Du * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto DuGDd = Du_ti * Gamma_H * g5 * adj(Dd_tf) * g5; - - for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s template void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, From 3aab983760b188bbaf4c0523d8f1faa39ed84919 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 16 Nov 2020 17:13:58 +0100 Subject: [PATCH 035/399] Flop count set as in DiRAC-ITT-2020 (mistaken 20% low, but must maintain consistency) --- benchmarks/Benchmark_ITT.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index 8ab26fc1..5d602ce9 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -445,7 +445,7 @@ public: // 1344= 3*(2*8+6)*2*8 + 8*3*2*2 + 3*4*2*8 // 1344 = Nc* (6+(Nc-1)*8)*2*Nd + Nd*Nc*2*2 + Nd*Nc*Ns*2 // double flops=(1344.0*volume)/2; -#if 1 +#if 0 double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + Nd*Nc*Ns + Nd*Nc*Ns*2; #else double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + 2*Nd*Nc*Ns + 2*Nd*Nc*Ns*2; @@ -512,7 +512,6 @@ public: NN_global=NN; uint64_t SHM=NP/NN; - Coordinate latt4({local[0]*mpi[0],local[1]*mpi[1],local[2]*mpi[2],local[3]*mpi[3]}); ///////// Welcome message //////////// std::cout< Date: Mon, 16 Nov 2020 18:07:15 -0500 Subject: [PATCH 036/399] Switch off SHM paths with --disable-shm --- Grid/communicator/SharedMemoryMPI.cc | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 5200b65c..4b440fc0 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -772,7 +772,7 @@ void SharedMemory::SetCommunicator(Grid_MPI_Comm comm) std::vector ranks(size); for(int r=0;r Date: Mon, 16 Nov 2020 20:15:50 -0500 Subject: [PATCH 037/399] --shm-force-mpi --- Grid/communicator/SharedMemoryMPI.cc | 7 ++++--- configure.ac | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 4b440fc0..6089093b 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -666,7 +666,6 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) #endif void * ptr = mmap(NULL,size, PROT_READ | PROT_WRITE, mmap_flag, fd, 0); - // std::cout << "Set WorldShmCommBufs["< ranks(size); for(int r=0;r Date: Tue, 17 Nov 2020 04:41:15 -0800 Subject: [PATCH 038/399] Build without LIME --- benchmarks/Benchmark_IO.cc | 5 ++++- benchmarks/Benchmark_IO.hpp | 2 +- benchmarks/Benchmark_IO_vs_dir.cc | 5 ++++- scripts/filelist | 7 +++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/benchmarks/Benchmark_IO.cc b/benchmarks/Benchmark_IO.cc index 0d80d425..87e7224d 100644 --- a/benchmarks/Benchmark_IO.cc +++ b/benchmarks/Benchmark_IO.cc @@ -1,4 +1,3 @@ - #include "Benchmark_IO.hpp" #ifndef BENCH_IO_LMIN @@ -13,6 +12,7 @@ #define BENCH_IO_NPASS 10 #endif +#ifdef HAVE_LIME using namespace Grid; std::string filestem(const int l) @@ -196,3 +196,6 @@ int main (int argc, char ** argv) return EXIT_SUCCESS; } +#else +int main(int argc,char ** argv){} +#endif diff --git a/benchmarks/Benchmark_IO.hpp b/benchmarks/Benchmark_IO.hpp index c4a6ca58..2ff42d52 100644 --- a/benchmarks/Benchmark_IO.hpp +++ b/benchmarks/Benchmark_IO.hpp @@ -2,12 +2,12 @@ #define Benchmark_IO_hpp_ #include -#ifdef HAVE_LIME #define MSG std::cout << GridLogMessage #define SEP \ "-----------------------------------------------------------------------------" #define BIGSEP \ "=============================================================================" +#ifdef HAVE_LIME namespace Grid { diff --git a/benchmarks/Benchmark_IO_vs_dir.cc b/benchmarks/Benchmark_IO_vs_dir.cc index e030bc39..8252547b 100644 --- a/benchmarks/Benchmark_IO_vs_dir.cc +++ b/benchmarks/Benchmark_IO_vs_dir.cc @@ -1,5 +1,5 @@ #include "Benchmark_IO.hpp" - +#ifdef HAVE_LIME using namespace Grid; int main (int argc, char ** argv) @@ -97,3 +97,6 @@ int main (int argc, char ** argv) return EXIT_SUCCESS; } +#else +int main(int argc,char ** argv){} +#endif diff --git a/scripts/filelist b/scripts/filelist index 78747315..27425a3e 100755 --- a/scripts/filelist +++ b/scripts/filelist @@ -26,11 +26,10 @@ for subdir in $dirs; do echo "tests-local: ${TESTLIST} " > Make.inc echo ${PREF}_PROGRAMS = ${TESTLIST} >> Make.inc echo >> Make.inc - HADLINK=`[ $subdir = './hadrons' ] && echo '-lHadrons '` for f in $TESTS; do BNAME=`basename $f .cc` echo ${BNAME}_SOURCES=$f >> Make.inc - echo ${BNAME}_LDADD=${HADLINK}-lGrid >> Make.inc + echo ${BNAME}_LDADD='$(top_builddir)/Grid/libGrid.a' >> Make.inc echo >> Make.inc done if [ $subdir != '.' ]; then @@ -49,7 +48,7 @@ echo >> Make.inc for f in $TESTS; do BNAME=`basename $f .cc` echo ${BNAME}_SOURCES=$f >> Make.inc - echo ${BNAME}_LDADD=-lGrid>> Make.inc + echo ${BNAME}_LDADD='$(top_builddir)/Grid/libGrid.a' >> Make.inc echo >> Make.inc done cd .. @@ -65,7 +64,7 @@ echo >> Make.inc for f in $TESTS; do BNAME=`basename $f .cc` echo ${BNAME}_SOURCES=$f >> Make.inc - echo ${BNAME}_LDADD=-lGrid>> Make.inc + echo ${BNAME}_LDADD='$(top_builddir)/Grid/libGrid.a'>> Make.inc echo >> Make.inc done cd .. From 804a810d68df3dcd285798e4dd325224e1ef2470 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 18 Nov 2020 03:06:53 +0000 Subject: [PATCH 039/399] Wildcard mismatch --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a387be8a..5b22309d 100644 --- a/configure.ac +++ b/configure.ac @@ -498,7 +498,7 @@ AC_ARG_ENABLE([setdevice],[AC_HELP_STRING([--enable-setdevice | --disable-setdev [Set GPU to rank in node with cudaSetDevice or similar])],[ac_SETDEVICE=${enable_SETDEVICE}],[ac_SETDEVICE=no]) case ${ac_SETDEVICE} in yes);; - *) + no) AC_DEFINE([GRID_DEFAULT_GPU],[1],[GRID_DEFAULT_GPU] ) ;; esac From 5adae5d6ff223fb52b6d69d87d0446f6c865a499 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 19 Nov 2020 19:22:12 +0100 Subject: [PATCH 040/399] Unused variable remove --- Grid/tensors/Tensor_Ta.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Grid/tensors/Tensor_Ta.h b/Grid/tensors/Tensor_Ta.h index f7af85b7..bbaa4a00 100644 --- a/Grid/tensors/Tensor_Ta.h +++ b/Grid/tensors/Tensor_Ta.h @@ -92,7 +92,6 @@ accelerator_inline iMatrix ProjectOnGroup(const iMatrix &arg) { // need a check for the group type? iMatrix ret(arg); - vtype rnrm; vtype nrm; vtype inner; for(int c1=0;c1 Date: Thu, 19 Nov 2020 19:23:03 +0100 Subject: [PATCH 041/399] Warning remove --- Grid/allocator/MemoryManagerShared.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/Grid/allocator/MemoryManagerShared.cc b/Grid/allocator/MemoryManagerShared.cc index 537f7c32..3f165007 100644 --- a/Grid/allocator/MemoryManagerShared.cc +++ b/Grid/allocator/MemoryManagerShared.cc @@ -1,7 +1,6 @@ #include #ifdef GRID_UVM -#warning "Grid is assuming unified virtual memory address space" NAMESPACE_BEGIN(Grid); ///////////////////////////////////////////////////////////////////////////////// // View management is 1:1 address space mapping From d5049949a45cee89cb6ff2de004fe719c36db8b6 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 19 Nov 2020 19:23:41 +0100 Subject: [PATCH 042/399] Starting to fix reunitarise --- Grid/qcd/utils/SUn.h | 84 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/utils/SUn.h b/Grid/qcd/utils/SUn.h index 7ac53246..69ab4ebb 100644 --- a/Grid/qcd/utils/SUn.h +++ b/Grid/qcd/utils/SUn.h @@ -735,7 +735,6 @@ public: } } - template static void HotConfiguration(GridParallelRNG &pRNG, GaugeField &out) { typedef typename GaugeField::vector_type vector_type; @@ -800,6 +799,89 @@ public: } }; +template +LatticeComplexD Determinant(const Lattice > > > &Umu) +{ + GridBase *grid=Umu.Grid(); + auto lvol = grid->lSites(); + LatticeComplexD ret(grid); + + autoView(Umu_v,Umu,CpuRead); + autoView(ret_v,ret,CpuWrite); + thread_for(site,lvol,{ + Eigen::MatrixXcd EigenU = Eigen::MatrixXcd::Zero(N,N); + Coordinate lcoor; + grid->LocalIndexToLocalCoor(site, lcoor); + iScalar > > Us; + peekLocalSite(Us, Umu_v, lcoor); + for(int i=0;i +static void ProjectSUn(Lattice > > > &Umu) +{ + Umu = ProjectOnGroup(Umu); + auto det = Determinant(Umu); + + det = pow(det,-1); + + for(int i=0;i(Umu,N-1,i); + element = element * det; + PokeIndex(Umu,element,Nc-1,i); + } +} +template +static void ProjectSUn(Lattice >,Nd> > &U) +{ + GridBase *grid=U.Grid(); + // Reunitarise + for(int mu=0;mu(U,mu); + Umu = ProjectOnGroup(Umu); + ProjectSUn(Umu); + PokeIndex(U,Umu,mu); + } +} +// Explicit specialisation for SU(3). +// Explicit specialisation for SU(3). +static void +ProjectSU3 (Lattice > > > &Umu) +{ + GridBase *grid=Umu.Grid(); + const int x=0; + const int y=1; + const int z=2; + // Reunitarise + Umu = ProjectOnGroup(Umu); + autoView(Umu_v,Umu,CpuWrite); + thread_for(ss,grid->oSites(),{ + auto cm = Umu_v[ss]; + cm()()(2,x) = adj(cm()()(0,y)*cm()()(1,z)-cm()()(0,z)*cm()()(1,y)); //x= yz-zy + cm()()(2,y) = adj(cm()()(0,z)*cm()()(1,x)-cm()()(0,x)*cm()()(1,z)); //y= zx-xz + cm()()(2,z) = adj(cm()()(0,x)*cm()()(1,y)-cm()()(0,y)*cm()()(1,x)); //z= xy-yx + Umu_v[ss]=cm; + }); +} +static void ProjectSU3(Lattice >,Nd> > &U) +{ + GridBase *grid=U.Grid(); + // Reunitarise + for(int mu=0;mu(U,mu); + Umu = ProjectOnGroup(Umu); + ProjectSU3(Umu); + PokeIndex(U,Umu,mu); + } +} + typedef SU<2> SU2; typedef SU<3> SU3; typedef SU<4> SU4; From aace3d47b993a6269d6b53ae3705aa9cf299bd16 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 19 Nov 2020 19:24:14 +0100 Subject: [PATCH 043/399] partial work in progress --- tests/core/Test_reunitarise.cc | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/core/Test_reunitarise.cc diff --git a/tests/core/Test_reunitarise.cc b/tests/core/Test_reunitarise.cc new file mode 100644 index 00000000..3e78b961 --- /dev/null +++ b/tests/core/Test_reunitarise.cc @@ -0,0 +1,137 @@ + /************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/Test_quenched_update.cc + + Copyright (C) 2015 + +Author: Azusa Yamaguchi +Author: Peter Boyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include + +using namespace std; +using namespace Grid; + ; + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + std::vector latt({8,8,8,8}); + GridCartesian * grid = SpaceTimeGrid::makeFourDimGrid(latt, + GridDefaultSimd(Nd,vComplexD::Nsimd()), + GridDefaultMpi()); + + GridCartesian * gridF = SpaceTimeGrid::makeFourDimGrid(latt, + GridDefaultSimd(Nd,vComplexF::Nsimd()), + GridDefaultMpi()); + + + /////////////////////////////// + // Configuration of known size + /////////////////////////////// + LatticeColourMatrixD ident(grid); + LatticeColourMatrixD U(grid); + LatticeColourMatrixD UU(grid); + LatticeColourMatrixD tmp(grid); + LatticeColourMatrixD org(grid); + LatticeColourMatrixF UF(gridF); + + LatticeGaugeField Umu(grid); + + ident =1.0; + + // RNG set up for test + std::vector pseeds({1,2,3,4,5}); // once I caught a fish alive + std::vector sseeds({6,7,8,9,10});// then i let it go again + GridParallelRNG pRNG(grid); pRNG.SeedFixedIntegers(pseeds); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(sseeds); + + SU::HotConfiguration(pRNG,Umu); + + U = PeekIndex(Umu,0); + org=U; + + + tmp= U*adj(U) - ident ; + RealD Def1 = norm2( tmp ); + std::cout << " Defect1 "<(U,Nc-1,i); + element = element * phase; + PokeIndex(U,element,Nc-1,i); + } + UU=U; + + detU= Determinant(U) ; + std::cout << "Determinant after screw up " <(UU); + detUU= Determinant(UU); + std::cout << "Determinant ProjectSUn " < Date: Fri, 20 Nov 2020 16:48:28 +0100 Subject: [PATCH 044/399] Configurable ALLOC_ALIGN and ALLOC_CACHE --- Grid/allocator/MemoryManager.h | 2 -- configure.ac | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index aac13aee..25c5b5f5 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -34,8 +34,6 @@ NAMESPACE_BEGIN(Grid); // Move control to configure.ac and Config.h? -#define ALLOCATION_CACHE -#define GRID_ALLOC_ALIGN (2*1024*1024) #define GRID_ALLOC_SMALL_LIMIT (4096) /*Pinning pages is costly*/ diff --git a/configure.ac b/configure.ac index 5b22309d..4d16d776 100644 --- a/configure.ac +++ b/configure.ac @@ -491,6 +491,28 @@ AM_CFLAGS="$SIMD_FLAGS $AM_CFLAGS" ###### PRECISION ALWAYS DOUBLE AC_DEFINE([GRID_DEFAULT_PRECISION_DOUBLE],[1],[GRID_DEFAULT_PRECISION is DOUBLE] ) +######################################################### +###################### GRID ALLOCATOR ALIGNMENT ## +######################################################### +AC_ARG_ENABLE([alloc-align],[AC_HELP_STRING([--enable-alloc-align=2MB|4k], + [Alignment in bytes of GRID Allocator ])],[ac_ALLOC_ALIGN=${enable_alloc_align}],[ac_ALLOC_ALIGN=2MB]) +case ${ac_ALLOC_ALIGN} in + 4k) + AC_DEFINE([GRID_ALLOC_ALIGN],[(4096)],[GRID_ALLOC_ALIGN]);; + 2MB) + AC_DEFINE([GRID_ALLOC_ALIGN],[(2*1024*1024)],[GRID_ALLOC_ALIGN]);; + *);; +esac + +AC_ARG_ENABLE([alloc-cache],[AC_HELP_STRING([--enable-alloc-cache ], + [Cache a pool of recent "frees" to reuse])],[ac_ALLOC_CACHE=${enable_alloc_cache}],[ac_ALLOC_CACHE=yes]) +case ${ac_ALLOC_CACHE} in + yes) + AC_DEFINE([ALLOCATION_CACHE],[1],[ALLOCATION_CACHE]);; + *);; +esac + + ######################################################### ###################### set GPU device to rank in node ## ######################################################### From 86e8b9fe387a922e13d4cfe67a2dbd5554f6ed46 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 20 Nov 2020 17:07:16 +0100 Subject: [PATCH 045/399] ALLOC_ALIGN removed --- Grid/threads/Accelerator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 5f5cd5fe..6232aea8 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -361,7 +361,7 @@ inline void acceleratorMemSet(void *base,int value,size_t bytes) { hipMemset(bas ////////////////////////////////////////////// // CPU Target - No accelerator just thread instead ////////////////////////////////////////////// -#define GRID_ALLOC_ALIGN (2*1024*1024) // 2MB aligned + #if ( (!defined(GRID_SYCL)) && (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) ) #undef GRID_SIMT From 147dc15d26da963d5c033701ad9e9211dcba50d3 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 20 Nov 2020 13:13:59 -0500 Subject: [PATCH 046/399] Update --- benchmarks/Benchmark_ITT.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index 5d602ce9..032535b3 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -445,7 +445,7 @@ public: // 1344= 3*(2*8+6)*2*8 + 8*3*2*2 + 3*4*2*8 // 1344 = Nc* (6+(Nc-1)*8)*2*Nd + Nd*Nc*2*2 + Nd*Nc*Ns*2 // double flops=(1344.0*volume)/2; -#if 0 +#if 1 double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + Nd*Nc*Ns + Nd*Nc*Ns*2; #else double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + 2*Nd*Nc*Ns + 2*Nd*Nc*Ns*2; From d4861a362ccaca6bbf300da450f46c10ea40ed29 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 23 Nov 2020 15:38:49 +0000 Subject: [PATCH 047/399] Stencil use non-UVM memory for look up table on enable-shared=no --- Grid/stencil/Stencil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index 1e198972..23fc8203 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -269,7 +269,7 @@ public: std::vector > > face_table ; Vector surface_list; - Vector _entries; // Resident in managed memory + stencilVector _entries; // Resident in managed memory std::vector Packets; std::vector Mergers; std::vector MergersSHM; From 683a5e5bf556f97824fd242f76d0a81ab8dd7bd8 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 23 Nov 2020 15:39:51 +0000 Subject: [PATCH 048/399] Stencil use host vector for integera table on enable-shared=no and mirror it on device --- Grid/allocator/AlignedAllocator.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Grid/allocator/AlignedAllocator.h b/Grid/allocator/AlignedAllocator.h index 4b357523..91622789 100644 --- a/Grid/allocator/AlignedAllocator.h +++ b/Grid/allocator/AlignedAllocator.h @@ -173,7 +173,8 @@ template using cshiftAllocator = devAllocator; template using cshiftAllocator = std::allocator; #endif -template using Vector = std::vector >; +template using Vector = std::vector >; +template using stencilVector = std::vector >; template using commVector = std::vector >; template using cshiftVector = std::vector >; From 97e264d0ff6562008be60ee4f3ba355198c7f247 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 23 Nov 2020 15:46:11 +0000 Subject: [PATCH 049/399] Christoph's changes --- Grid/allocator/MemoryManagerCache.cc | 36 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index 5dd7575e..50076eba 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -1,11 +1,12 @@ #include - #ifndef GRID_UVM #warning "Using explicit device memory copies" NAMESPACE_BEGIN(Grid); +//define dprintf(...) printf ( __VA_ARGS__ ); fflush(stdout); #define dprintf(...) + //////////////////////////////////////////////////////////// // For caching copies of data on device //////////////////////////////////////////////////////////// @@ -103,7 +104,7 @@ void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache) /////////////////////////////////////////////////////////// assert(AccCache.state!=Empty); - // dprintf("MemoryManager: Discard(%llx) %llx\n",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); + dprintf("MemoryManager: Discard(%llx) %llx\n",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); assert(AccCache.accLock==0); assert(AccCache.cpuLock==0); assert(AccCache.CpuPtr!=(uint64_t)NULL); @@ -111,7 +112,7 @@ void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache) AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes); DeviceBytes -=AccCache.bytes; LRUremove(AccCache); - // dprintf("MemoryManager: Free(%llx) LRU %lld Total %lld\n",(uint64_t)AccCache.AccPtr,DeviceLRUBytes,DeviceBytes); + dprintf("MemoryManager: Free(%llx) LRU %lld Total %lld\n",(uint64_t)AccCache.AccPtr,DeviceLRUBytes,DeviceBytes); } uint64_t CpuPtr = AccCache.CpuPtr; EntryErase(CpuPtr); @@ -125,7 +126,7 @@ void MemoryManager::Evict(AcceleratorViewEntry &AccCache) /////////////////////////////////////////////////////////////////////////// assert(AccCache.state!=Empty); - // dprintf("MemoryManager: Evict(%llx) %llx\n",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); + dprintf("MemoryManager: Evict(%llx) %llx\n",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); assert(AccCache.accLock==0); assert(AccCache.cpuLock==0); if(AccCache.state==AccDirty) { @@ -136,7 +137,7 @@ void MemoryManager::Evict(AcceleratorViewEntry &AccCache) AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes); DeviceBytes -=AccCache.bytes; LRUremove(AccCache); - // dprintf("MemoryManager: Free(%llx) footprint now %lld \n",(uint64_t)AccCache.AccPtr,DeviceBytes); + dprintf("MemoryManager: Free(%llx) footprint now %lld \n",(uint64_t)AccCache.AccPtr,DeviceBytes); } uint64_t CpuPtr = AccCache.CpuPtr; EntryErase(CpuPtr); @@ -149,7 +150,7 @@ void MemoryManager::Flush(AcceleratorViewEntry &AccCache) assert(AccCache.AccPtr!=(uint64_t)NULL); assert(AccCache.CpuPtr!=(uint64_t)NULL); acceleratorCopyFromDevice((void *)AccCache.AccPtr,(void *)AccCache.CpuPtr,AccCache.bytes); - // dprintf("MemoryManager: Flush %llx -> %llx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); + dprintf("MemoryManager: Flush %llx -> %llx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); DeviceToHostBytes+=AccCache.bytes; DeviceToHostXfer++; AccCache.state=Consistent; @@ -164,7 +165,7 @@ void MemoryManager::Clone(AcceleratorViewEntry &AccCache) AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes); DeviceBytes+=AccCache.bytes; } - // dprintf("MemoryManager: Clone %llx <- %llx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); + dprintf("MemoryManager: Clone %llx <- %llx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); acceleratorCopyToDevice((void *)AccCache.CpuPtr,(void *)AccCache.AccPtr,AccCache.bytes); HostToDeviceBytes+=AccCache.bytes; HostToDeviceXfer++; @@ -227,18 +228,24 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod // Find if present, otherwise get or force an empty //////////////////////////////////////////////////////////////////////////// if ( EntryPresent(CpuPtr)==0 ){ - EvictVictims(bytes); EntryCreate(CpuPtr,bytes,mode,hint); } auto AccCacheIterator = EntryLookup(CpuPtr); auto & AccCache = AccCacheIterator->second; - + if (!AccCache.AccPtr) + EvictVictims(bytes); + assert((mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard)); assert(AccCache.cpuLock==0); // Programming error if(AccCache.state!=Empty) { + dprintf("ViewOpen found entry %llx %llx : %lld %lld\n", + (uint64_t)AccCache.CpuPtr, + (uint64_t)CpuPtr, + (uint64_t)AccCache.bytes, + (uint64_t)bytes); assert(AccCache.CpuPtr == CpuPtr); assert(AccCache.bytes ==bytes); } @@ -285,21 +292,21 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod AccCache.state = Consistent; // CpuDirty + AccRead => Consistent } AccCache.accLock++; - // printf("Copied CpuDirty entry into device accLock %d\n",AccCache.accLock); + dprintf("Copied CpuDirty entry into device accLock %d\n",AccCache.accLock); } else if(AccCache.state==Consistent) { if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard)) AccCache.state = AccDirty; // Consistent + AcceleratorWrite=> AccDirty else AccCache.state = Consistent; // Consistent + AccRead => Consistent AccCache.accLock++; - // printf("Consistent entry into device accLock %d\n",AccCache.accLock); + dprintf("Consistent entry into device accLock %d\n",AccCache.accLock); } else if(AccCache.state==AccDirty) { if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard)) AccCache.state = AccDirty; // AccDirty + AcceleratorWrite=> AccDirty else AccCache.state = AccDirty; // AccDirty + AccRead => AccDirty AccCache.accLock++; - // printf("AccDirty entry into device accLock %d\n",AccCache.accLock); + dprintf("AccDirty entry into device accLock %d\n",AccCache.accLock); } else { assert(0); } @@ -361,13 +368,14 @@ uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,V // Find if present, otherwise get or force an empty //////////////////////////////////////////////////////////////////////////// if ( EntryPresent(CpuPtr)==0 ){ - EvictVictims(bytes); EntryCreate(CpuPtr,bytes,mode,transient); } auto AccCacheIterator = EntryLookup(CpuPtr); auto & AccCache = AccCacheIterator->second; - + if (!AccCache.AccPtr) + EvictVictims(bytes); + assert((mode==CpuRead)||(mode==CpuWrite)); assert(AccCache.accLock==0); // Programming error From 321f0f51b59109f9cb2b17d0e0f6a1883076be54 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 24 Nov 2020 21:46:10 -0500 Subject: [PATCH 050/399] Project to SU(N) --- Grid/qcd/action/gauge/GaugeImplTypes.h | 4 ++++ Grid/qcd/action/scalar/ScalarImpl.h | 8 ++++++++ Grid/qcd/hmc/HMC.h | 2 +- Grid/qcd/hmc/integrators/Integrator.h | 2 ++ Grid/qcd/utils/SUn.h | 5 ++--- tests/core/Test_reunitarise.cc | 19 +++++++++++++------ 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Grid/qcd/action/gauge/GaugeImplTypes.h b/Grid/qcd/action/gauge/GaugeImplTypes.h index 9b7d5a60..55a20eca 100644 --- a/Grid/qcd/action/gauge/GaugeImplTypes.h +++ b/Grid/qcd/action/gauge/GaugeImplTypes.h @@ -154,6 +154,10 @@ public: return Hsum.real(); } + static inline void Project(Field &U) { + ProjectSUn(U); + } + static inline void HotConfiguration(GridParallelRNG &pRNG, Field &U) { SU::HotConfiguration(pRNG, U); } diff --git a/Grid/qcd/action/scalar/ScalarImpl.h b/Grid/qcd/action/scalar/ScalarImpl.h index 14675b11..403ea573 100644 --- a/Grid/qcd/action/scalar/ScalarImpl.h +++ b/Grid/qcd/action/scalar/ScalarImpl.h @@ -54,6 +54,10 @@ public: static inline void ColdConfiguration(GridParallelRNG &pRNG, Field &U) { U = 1.0; } + + static inline void Project(Field &U) { + return; + } static void MomentumSpacePropagator(Field &out, RealD m) { @@ -234,6 +238,10 @@ public: #endif //USE_FFT_ACCELERATION } + static inline void Project(Field &U) { + return; + } + static inline void HotConfiguration(GridParallelRNG &pRNG, Field &U) { Group::GaussianFundamentalLieAlgebraMatrix(pRNG, U); } diff --git a/Grid/qcd/hmc/HMC.h b/Grid/qcd/hmc/HMC.h index 0f933204..f168b69a 100644 --- a/Grid/qcd/hmc/HMC.h +++ b/Grid/qcd/hmc/HMC.h @@ -95,7 +95,7 @@ private: typedef typename IntegratorType::Field Field; typedef std::vector< HmcObservable * > ObsListType; - + //pass these from the resource manager GridSerialRNG &sRNG; GridParallelRNG &pRNG; diff --git a/Grid/qcd/hmc/integrators/Integrator.h b/Grid/qcd/hmc/integrators/Integrator.h index d5475704..70055754 100644 --- a/Grid/qcd/hmc/integrators/Integrator.h +++ b/Grid/qcd/hmc/integrators/Integrator.h @@ -313,6 +313,8 @@ public: std::cout << GridLogIntegrator << " times[" << level << "]= " << t_P[level] << " " << t_U << std::endl; } + FieldImplementation::Project(U); + // and that we indeed got to the end of the trajectory assert(fabs(t_U - Params.trajL) < 1.0e-6); diff --git a/Grid/qcd/utils/SUn.h b/Grid/qcd/utils/SUn.h index 69ab4ebb..675493b3 100644 --- a/Grid/qcd/utils/SUn.h +++ b/Grid/qcd/utils/SUn.h @@ -820,7 +820,6 @@ LatticeComplexD Determinant(const Lattice }} ComplexD det = EigenU.determinant(); pokeLocalSite(det,ret_v,lcoor); - std::cout << " site " < > > > &Umu) Umu = ProjectOnGroup(Umu); auto det = Determinant(Umu); - det = pow(det,-1); - + det = conjugate(det); + for(int i=0;i(Umu,N-1,i); element = element * det; diff --git a/tests/core/Test_reunitarise.cc b/tests/core/Test_reunitarise.cc index 3e78b961..9a6781f1 100644 --- a/tests/core/Test_reunitarise.cc +++ b/tests/core/Test_reunitarise.cc @@ -102,7 +102,8 @@ int main (int argc, char ** argv) LatticeComplexD detUU(grid); detU= Determinant(U) ; - std::cout << "Determinant before screw up " <(UU); + ProjectSUn(UU); detUU= Determinant(UU); - std::cout << "Determinant ProjectSUn " < Date: Wed, 2 Dec 2020 17:55:30 -0800 Subject: [PATCH 051/399] Duplicate code --- Grid/allocator/AlignedAllocator.cc | 67 ------------------------------ 1 file changed, 67 deletions(-) delete mode 100644 Grid/allocator/AlignedAllocator.cc diff --git a/Grid/allocator/AlignedAllocator.cc b/Grid/allocator/AlignedAllocator.cc deleted file mode 100644 index 0d1707d9..00000000 --- a/Grid/allocator/AlignedAllocator.cc +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include - -NAMESPACE_BEGIN(Grid); - -MemoryStats *MemoryProfiler::stats = nullptr; -bool MemoryProfiler::debug = false; - -void check_huge_pages(void *Buf,uint64_t BYTES) -{ -#ifdef __linux__ - int fd = open("/proc/self/pagemap", O_RDONLY); - assert(fd >= 0); - const int page_size = 4096; - uint64_t virt_pfn = (uint64_t)Buf / page_size; - off_t offset = sizeof(uint64_t) * virt_pfn; - uint64_t npages = (BYTES + page_size-1) / page_size; - uint64_t pagedata[npages]; - uint64_t ret = lseek(fd, offset, SEEK_SET); - assert(ret == offset); - ret = ::read(fd, pagedata, sizeof(uint64_t)*npages); - assert(ret == sizeof(uint64_t) * npages); - int nhugepages = npages / 512; - int n4ktotal, nnothuge; - n4ktotal = 0; - nnothuge = 0; - for (int i = 0; i < nhugepages; ++i) { - uint64_t baseaddr = (pagedata[i*512] & 0x7fffffffffffffULL) * page_size; - for (int j = 0; j < 512; ++j) { - uint64_t pageaddr = (pagedata[i*512+j] & 0x7fffffffffffffULL) * page_size; - ++n4ktotal; - if (pageaddr != baseaddr + j * page_size) - ++nnothuge; - } - } - int rank = CartesianCommunicator::RankWorld(); - printf("rank %d Allocated %d 4k pages, %d not in huge pages\n", rank, n4ktotal, nnothuge); -#endif -} - -std::string sizeString(const size_t bytes) -{ - constexpr unsigned int bufSize = 256; - const char *suffixes[7] = {"", "K", "M", "G", "T", "P", "E"}; - char buf[256]; - size_t s = 0; - double count = bytes; - - while (count >= 1024 && s < 7) - { - s++; - count /= 1024; - } - if (count - floor(count) == 0.0) - { - snprintf(buf, bufSize, "%d %sB", (int)count, suffixes[s]); - } - else - { - snprintf(buf, bufSize, "%.1f %sB", count, suffixes[s]); - } - - return std::string(buf); -} - -NAMESPACE_END(Grid); - From cf76741ec651c41f1d1fa38d22e0474899691e72 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 3 Dec 2020 03:47:11 -0800 Subject: [PATCH 052/399] Intel DPCPP Gold happy now (compiles all, runs Benchmark_dwf_fp32 ) --- Grid/DisableWarnings.h | 2 ++ Grid/Makefile.am | 12 ++++++++ Grid/communicator/SharedMemory.h | 2 +- Grid/communicator/SharedMemoryMPI.cc | 2 +- Grid/communicator/SharedMemoryNone.cc | 43 ++++++++++++++++++++++++++- Grid/lattice/Lattice_basis.h | 5 ++-- benchmarks/Benchmark_gparity.cc | 4 +-- configure.ac | 18 +++++++++++ scripts/filelist | 18 +++++++++-- 9 files changed, 97 insertions(+), 9 deletions(-) diff --git a/Grid/DisableWarnings.h b/Grid/DisableWarnings.h index 8ea219fb..4bd1edd0 100644 --- a/Grid/DisableWarnings.h +++ b/Grid/DisableWarnings.h @@ -37,7 +37,9 @@ directory #endif //disables and intel compiler specific warning (in json.hpp) +#ifdef __ICC #pragma warning disable 488 +#endif #ifdef __NVCC__ //disables nvcc specific warning in json.hpp diff --git a/Grid/Makefile.am b/Grid/Makefile.am index f1fa462e..ded6d146 100644 --- a/Grid/Makefile.am +++ b/Grid/Makefile.am @@ -21,6 +21,7 @@ if BUILD_HDF5 extra_headers+=serialisation/Hdf5Type.h endif + all: version-cache Version.h version-cache: @@ -53,6 +54,17 @@ Version.h: version-cache include Make.inc include Eigen.inc +extra_sources+=$(ZWILS_FERMION_FILES) +extra_sources+=$(WILS_FERMION_FILES) +extra_sources+=$(STAG_FERMION_FILES) +if BUILD_GPARITY + extra_sources+=$(GP_FERMION_FILES) +endif +if BUILD_FERMION_REPS + extra_sources+=$(ADJ_FERMION_FILES) + extra_sources+=$(TWOIND_FERMION_FILES) +endif + lib_LIBRARIES = libGrid.a CCFILES += $(extra_sources) diff --git a/Grid/communicator/SharedMemory.h b/Grid/communicator/SharedMemory.h index 6c6e3953..f2d20a24 100644 --- a/Grid/communicator/SharedMemory.h +++ b/Grid/communicator/SharedMemory.h @@ -102,7 +102,7 @@ public: /////////////////////////////////////////////////// static void SharedMemoryAllocate(uint64_t bytes, int flags); static void SharedMemoryFree(void); - static void SharedMemoryCopy(void *dest,const void *src,size_t bytes); + static void SharedMemoryCopy(void *dest,void *src,size_t bytes); static void SharedMemoryZero(void *dest,size_t bytes); }; diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 6089093b..a12418e6 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -715,7 +715,7 @@ void GlobalSharedMemory::SharedMemoryZero(void *dest,size_t bytes) bzero(dest,bytes); #endif } -void GlobalSharedMemory::SharedMemoryCopy(void *dest,const void *src,size_t bytes) +void GlobalSharedMemory::SharedMemoryCopy(void *dest,void *src,size_t bytes) { #ifdef GRID_CUDA cudaMemcpy(dest,src,bytes,cudaMemcpyDefault); diff --git a/Grid/communicator/SharedMemoryNone.cc b/Grid/communicator/SharedMemoryNone.cc index ed37ab47..35663632 100644 --- a/Grid/communicator/SharedMemoryNone.cc +++ b/Grid/communicator/SharedMemoryNone.cc @@ -29,6 +29,7 @@ Author: Peter Boyle #include NAMESPACE_BEGIN(Grid); +#define header "SharedMemoryNone: " /*Construct from an MPI communicator*/ void GlobalSharedMemory::Init(Grid_MPI_Comm comm) @@ -55,6 +56,38 @@ void GlobalSharedMemory::OptimalCommunicator(const Coordinate &processors,Grid_M //////////////////////////////////////////////////////////////////////////////////////////// // Hugetlbfs mapping intended, use anonymous mmap //////////////////////////////////////////////////////////////////////////////////////////// +#if 1 +void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) +{ + std::cout << header "SharedMemoryAllocate "<< bytes<< " GPU implementation "< Bt(Nm * max_threads); thread_region @@ -164,7 +164,8 @@ void basisRotateJ(Field &result,std::vector &basis,Eigen::MatrixXd& Qt,in auto basis_vp=& basis_v[0]; autoView(result_v,result,AcceleratorWrite); accelerator_for(ss, grid->oSites(),vobj::Nsimd(),{ - auto B=coalescedRead(zz); + vobj zzz=Zero(); + auto B=coalescedRead(zzz); for(int k=k0; k Make.inc echo >> Make.inc echo CCFILES=$CCFILES >> Make.inc - +echo ZWILS_FERMION_FILES=$ZWILS_FERMION_FILES >> Make.inc +echo WILS_FERMION_FILES=$WILS_FERMION_FILES >> Make.inc +echo STAG_FERMION_FILES=$STAG_FERMION_FILES >> Make.inc +echo GP_FERMION_FILES=$GP_FERMION_FILES >> Make.inc +echo ADJ_FERMION_FILES=$ADJ_FERMION_FILES >> Make.inc +echo TWOIND_FERMION_FILES=$TWOIND_FERMION_FILES >> Make.inc # tests Make.inc cd $home/tests From 2ef1fa66a8afe2066b8c1ef191a608bd64bdb3bd Mon Sep 17 00:00:00 2001 From: Christopher Kelly Date: Mon, 7 Dec 2020 11:53:35 -0500 Subject: [PATCH 053/399] Improved performance of G-parity kernel for GPUs by simplifying multLink implementation --- Grid/qcd/action/fermion/GparityWilsonImpl.h | 42 ++++++++------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/Grid/qcd/action/fermion/GparityWilsonImpl.h b/Grid/qcd/action/fermion/GparityWilsonImpl.h index 0b726db9..9dca403b 100644 --- a/Grid/qcd/action/fermion/GparityWilsonImpl.h +++ b/Grid/qcd/action/fermion/GparityWilsonImpl.h @@ -97,42 +97,30 @@ public: Coordinate icoor; #ifdef GRID_SIMT - _Spinor tmp; - const int Nsimd =SiteDoubledGaugeField::Nsimd(); int s = acceleratorSIMTlane(Nsimd); St.iCoorFromIindex(icoor,s); int mmu = mu % Nd; - if ( SE->_around_the_world && St.parameters.twists[mmu] ) { - - int permute_lane = (sl==1) - || ((distance== 1)&&(icoor[direction]==1)) - || ((distance==-1)&&(icoor[direction]==0)); - if ( permute_lane ) { - tmp(0) = chi(1); - tmp(1) = chi(0); - } else { - tmp(0) = chi(0); - tmp(1) = chi(1); - } + auto UU0=coalescedRead(U(0)(mu)); + auto UU1=coalescedRead(U(1)(mu)); + + //Decide whether we do a G-parity flavor twist + //Note: this assumes (but does not check) that sl==1 || sl==2 i.e. max 2 SIMD lanes in G-parity dir + //It also assumes (but does not check) that abs(distance) == 1 + int permute_lane = (sl==1) + || ((distance== 1)&&(icoor[direction]==1)) + || ((distance==-1)&&(icoor[direction]==0)); - auto UU0=coalescedRead(U(0)(mu)); - auto UU1=coalescedRead(U(1)(mu)); + permute_lane = permute_lane && SE->_around_the_world && St.parameters.twists[mmu]; //only if we are going around the world - mult(&phi(0),&UU0,&tmp(0)); - mult(&phi(1),&UU1,&tmp(1)); + //Apply the links + int f_upper = permute_lane ? 1 : 0; + int f_lower = !f_upper; - } else { - - auto UU0=coalescedRead(U(0)(mu)); - auto UU1=coalescedRead(U(1)(mu)); - - mult(&phi(0),&UU0,&chi(0)); - mult(&phi(1),&UU1,&chi(1)); - - } + mult(&phi(0),&UU0,&chi(f_upper)); + mult(&phi(1),&UU1,&chi(f_lower)); #else typedef _Spinor vobj; From c438118fd719b5fd908dc72dcd6ff3d6db83923c Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Tue, 8 Dec 2020 14:42:11 +0100 Subject: [PATCH 054/399] Change access specifier of clover fields in order to allow deriving classes to access these --- Grid/qcd/action/fermion/WilsonCloverFermion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/WilsonCloverFermion.h b/Grid/qcd/action/fermion/WilsonCloverFermion.h index 91ad6d6d..92af7111 100644 --- a/Grid/qcd/action/fermion/WilsonCloverFermion.h +++ b/Grid/qcd/action/fermion/WilsonCloverFermion.h @@ -245,7 +245,7 @@ public: return out; } -private: +protected: // here fixing the 4 dimensions, make it more general? RealD csw_r; // Clover coefficient - spatial From 9aec4a3c2620dd459bac5b89fc0d661aaf50e6cd Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 10 Dec 2020 02:11:17 -0800 Subject: [PATCH 055/399] SYCL --- Grid/simd/Grid_gpu_vec.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Grid/simd/Grid_gpu_vec.h b/Grid/simd/Grid_gpu_vec.h index 8b17f75a..8e55ce2f 100644 --- a/Grid/simd/Grid_gpu_vec.h +++ b/Grid/simd/Grid_gpu_vec.h @@ -38,12 +38,20 @@ Author: Peter Boyle #ifdef GRID_HIP #include #endif +#ifdef GRID_SYCL +namespace Grid { + typedef struct { uint16_t x;} half; + typedef struct { half x; half y;} half2; + typedef struct { float x; float y;} float2; + typedef struct { double x; double y;} double2; +} +#endif + namespace Grid { -#if (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) -typedef struct { uint16_t x;} half; -#endif + + typedef struct Half2_t { half x; half y; } Half2; #define COALESCE_GRANULARITY ( GEN_SIMD_WIDTH ) @@ -156,7 +164,7 @@ accelerator_inline float half2float(half h) f = __half2float(h); #else Grid_half hh; - hh.x = hr.x; + hh.x = h.x; f= sfw_half_to_float(hh); #endif return f; From 873519e96046acfd0844a7d07d540d989a7a6204 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 14 Dec 2020 16:06:10 +0000 Subject: [PATCH 056/399] Enable existing conserved current code for CUDA (compiles OK for CUDA 10.1). Add option to Test_cayley_mres to load a configuration --- .../implementation/CayleyFermion5DImplementation.h | 4 ++-- tests/debug/Test_cayley_mres.cc | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index b3fbe096..f11e9c44 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -642,7 +642,7 @@ void CayleyFermion5D::ContractConservedCurrent( PropagatorField &q_in_1, Current curr_type, unsigned int mu) { -#if (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) +#if (!defined(GRID_HIP)) Gamma::Algebra Gmu [] = { Gamma::Algebra::GammaX, Gamma::Algebra::GammaY, @@ -826,7 +826,7 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, } #endif -#if (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) +#if (!defined(GRID_HIP)) int tshift = (mu == Nd-1) ? 1 : 0; //////////////////////////////////////////////// // GENERAL CAYLEY CASE diff --git a/tests/debug/Test_cayley_mres.cc b/tests/debug/Test_cayley_mres.cc index 2e56fa81..5282c756 100644 --- a/tests/debug/Test_cayley_mres.cc +++ b/tests/debug/Test_cayley_mres.cc @@ -108,8 +108,18 @@ int main (int argc, char ** argv) GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); LatticeGaugeField Umu(UGrid); - SU::ColdConfiguration(Umu); - // SU::HotConfiguration(RNG4,Umu); + if( argc > 1 && argv[1][0] != '-' ) + { + std::cout<::ColdConfiguration(Umu); + // SU::HotConfiguration(RNG4,Umu); + } RealD mass=0.3; RealD M5 =1.0; From 808f1e0e8c199204c7369fe2a033bc6041cbaa91 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 15 Dec 2020 16:33:29 +0000 Subject: [PATCH 057/399] merge develop --- tests/solver/Test_zMADWF_prec.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/solver/Test_zMADWF_prec.cc b/tests/solver/Test_zMADWF_prec.cc index d1168764..f18e1d86 100644 --- a/tests/solver/Test_zMADWF_prec.cc +++ b/tests/solver/Test_zMADWF_prec.cc @@ -52,7 +52,7 @@ struct TestParams{ bool zmobius_inner; double lambda_max; //upper bound of H_T eigenvalue range required to generate zMobius approximation - TestParams(): load_config(true), config_file("ckpoint_lat.1000"), mass(0.01), + TestParams(): load_config(false), config_file("ckpoint_lat.1000"), mass(0.01), Ls_outer(24), b_plus_c_outer(2.0), resid_outer(1e-8), Ls_inner(12), b_plus_c_inner(1.0), resid_inner(1e-8), zmobius_inner(true), lambda_max(1.42), outer_precon("Standard"), inner_precon("Standard") {} @@ -246,7 +246,7 @@ void run(const TestParams ¶ms){ typename RunParamsInner::SchurSolverType SchurSolver_inner(CG_inner); ZeroGuesser Guess; - MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 100, &update); + MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 10000, &update); LatticeFermionD result_MADWF(FGrid_outer); result_MADWF = Zero(); From f36d6f3923b7632e169b6740c94cc39ecc6bc8a9 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 17 Dec 2020 17:04:08 +0000 Subject: [PATCH 058/399] compiles on GPU. 3pt still wrong!!!! --- Grid/qcd/utils/A2Autils.h | 2 +- Grid/qcd/utils/BaryonUtils.h | 991 +++++++++++++++++++---------------- 2 files changed, 544 insertions(+), 449 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index b63d8571..497927dd 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -1047,7 +1047,7 @@ A2Autils::ContractWWVV(std::vector &WWVV, { GridBase *grid = vs[0].Grid(); - int nd = grid->_ndimension; + //int nd = grid->_ndimension; int Nsimd = grid->Nsimd(); int N_t = WW_sd.dimensions()[0]; int N_s = WW_sd.dimensions()[1]; diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 15516b56..25c71e3a 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -44,24 +44,24 @@ public: typedef typename ComplexField::vector_object vobj; typedef Lattice> SpinMatrixField; - typedef typename SpinMatrixField::vector_object sobj; + //typedef typename SpinMatrixField::vector_object sobj; - static const int epsilon[6][3] ; - static const Real epsilon_sgn[6]; + //static const int epsilon[6][3] ; + //static const Real epsilon_sgn[6]; private: - template + template accelerator_inline static void BaryonSite(const mobj &D1, - const mobj &D2, - const mobj &D3, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, - const int parity, - const bool * wick_contractions, - robj &result); - template + const mobj &D2, + const mobj &D3, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const int parity, + const bool * wick_contractions, + robj &result); + template accelerator_inline static void BaryonSiteMatrix(const mobj &D1, const mobj &D2, const mobj &D3, @@ -76,15 +76,15 @@ public: std::string qf, bool* wick_contractions); static void ContractBaryons(const PropagatorField &q1_left, - const PropagatorField &q2_left, - const PropagatorField &q3_left, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, - const bool* wick_contractions, - const int parity, - ComplexField &baryon_corr); + const PropagatorField &q2_left, + const PropagatorField &q3_left, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + const int parity, + ComplexField &baryon_corr); static void ContractBaryonsMatrix(const PropagatorField &q1_left, const PropagatorField &q2_left, const PropagatorField &q3_left, @@ -96,16 +96,16 @@ public: SpinMatrixField &baryon_corr); template static void ContractBaryonsSliced(const mobj &D1, - const mobj &D2, - const mobj &D3, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, - const bool* wick_contractions, - const int parity, - const int nt, - robj &result); + const mobj &D2, + const mobj &D3, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + const int parity, + const int nt, + robj &result); template static void ContractBaryonsSlicedMatrix(const mobj &D1, const mobj &D2, @@ -118,11 +118,11 @@ public: const int nt, robj &result); private: - template + template accelerator_inline static void BaryonGamma3ptGroup1Site( const mobj &Dq1_ti, const mobj2 &Dq2_spec, - const mobj2 &Dq3_spec, + // const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -130,11 +130,11 @@ public: int wick_contraction, robj &result); - template + template accelerator_inline static void BaryonGamma3ptGroup2Site( const mobj2 &Dq1_spec, const mobj &Dq2_ti, - const mobj2 &Dq3_spec, + //const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -142,10 +142,10 @@ public: int wick_contraction, robj &result); - template + template accelerator_inline static void BaryonGamma3ptGroup3Site( const mobj2 &Dq1_spec, - const mobj2 &Dq2_spec, + //const mobj2 &Dq2_spec, const mobj &Dq3_ti, const mobj &Dq4_tf, const Gamma GammaJ, @@ -167,86 +167,78 @@ public: const Gamma GammaBf, SpinMatrixField &stn_corr); private: - template + template accelerator_inline static void SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result); - template + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); + template accelerator_inline static void SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, - const mobj &Du_tf, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result); + const mobj &Du_tf, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); - template + template accelerator_inline static void SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result); - template + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); + template accelerator_inline static void SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, - const mobj &Du_tf, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result); + const mobj &Du_tf, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); public: template static void SigmaToNucleonEye(const PropagatorField &qq_loop, - const mobj &Du_spec, - const PropagatorField &qd_tf, - const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - const std::string op, - SpinMatrixField &stn_corr); + const mobj &Du_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + const std::string op, + SpinMatrixField &stn_corr); template static void SigmaToNucleonNonEye(const PropagatorField &qq_ti, - const PropagatorField &qq_tf, - const mobj &Du_spec, - const PropagatorField &qd_tf, - const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - const std::string op, - SpinMatrixField &stn_corr); + const PropagatorField &qq_tf, + const mobj &Du_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + const std::string op, + SpinMatrixField &stn_corr); }; - +/* template const int BaryonUtils::epsilon[6][3] = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; -/*template -const Complex BaryonUtils::epsilon_sgn[6] = {Complex(1), - Complex(1), - Complex(1), - Complex(-1), - Complex(-1), - Complex(-1)}; -*/ template const Real BaryonUtils::epsilon_sgn[6] = {1.,1.,1.,-1.,-1.,-1.}; - +*/ //This is the old version template -template +template accelerator_inline void BaryonUtils::BaryonSite(const mobj &D1, const mobj &D2, const mobj &D3, @@ -274,16 +266,20 @@ void BaryonUtils::BaryonSite(const mobj &D1, auto GBf_D3 = GammaB_f * D3; auto GAf_D3 = GammaA_f * D3; - for (int ie_f=0; ie_f < 6 ; ie_f++){ - int a_f = epsilon[ie_f][0]; //a - int b_f = epsilon[ie_f][1]; //b - int c_f = epsilon[ie_f][2]; //c - for (int ie_i=0; ie_i < 6 ; ie_i++){ - int a_i = epsilon[ie_i][0]; //a' - int b_i = epsilon[ie_i][1]; //b' - int c_i = epsilon[ie_i][2]; //c' + Real ee; - Real ee = epsilon_sgn[ie_f] * epsilon_sgn[ie_i]; + for (int ie_f=0; ie_f < 6 ; ie_f++){ + int a_f = (ie_f < 3 ? ie_f : (6-ie_f)%3 ); //epsilon[ie_n][0]; //a + int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b + int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c + int eSgn_f = (ie_f < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); + + ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; //This is the \delta_{456}^{123} part if (wick_contraction[0]){ for (int rho=0; rho::BaryonSite(const mobj &D1, //New version without parity projection or trace template -template +template accelerator_inline void BaryonUtils::BaryonSiteMatrix(const mobj &D1, const mobj &D2, const mobj &D3, @@ -384,16 +380,21 @@ void BaryonUtils::BaryonSiteMatrix(const mobj &D1, auto GBf_D3 = GammaB_f * D3; auto GAf_D3 = GammaA_f * D3; - for (int ie_f=0; ie_f < 6 ; ie_f++){ - int a_f = epsilon[ie_f][0]; //a - int b_f = epsilon[ie_f][1]; //b - int c_f = epsilon[ie_f][2]; //c - for (int ie_i=0; ie_i < 6 ; ie_i++){ - int a_i = epsilon[ie_i][0]; //a' - int b_i = epsilon[ie_i][1]; //b' - int c_i = epsilon[ie_i][2]; //c' - Real ee = epsilon_sgn[ie_f] * epsilon_sgn[ie_i]; + Real ee; + + for (int ie_f=0; ie_f < 6 ; ie_f++){ + int a_f = (ie_f < 3 ? ie_f : (6-ie_f)%3 ); //epsilon[ie_n][0]; //a + int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b + int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c + int eSgn_f = (ie_f < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); + + ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; //This is the \delta_{456}^{123} part if (wick_contraction[0]){ for (int rho_i=0; rho_i::WickContractions(std::string qi, std::string qf, bool* * Wick_Contractions function above */ template void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, - const PropagatorField &q2_left, - const PropagatorField &q3_left, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, - const bool* wick_contractions, - const int parity, - ComplexField &baryon_corr) + const PropagatorField &q2_left, + const PropagatorField &q3_left, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + const int parity, + ComplexField &baryon_corr) { assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); @@ -519,10 +520,10 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, GridBase *grid = q1_left.Grid(); - autoView(vbaryon_corr, baryon_corr,CpuWrite); - autoView( v1 , q1_left, CpuRead); - autoView( v2 , q2_left, CpuRead); - autoView( v3 , q3_left, CpuRead); + autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( v1 , q1_left , AcceleratorRead); + autoView( v2 , q2_left , AcceleratorRead); + autoView( v3 , q3_left , AcceleratorRead); Real bytes =0.; bytes += grid->oSites() * (432.*sizeof(vComplex) + 126.*sizeof(int) + 36.*sizeof(Real)); @@ -538,12 +539,13 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, t =-usecond(); accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto D1 = v1[ss]; - auto D2 = v2[ss]; - auto D3 = v3[ss]; - vobj result=Zero(); + auto D1 = v1(ss); + auto D2 = v2(ss); + auto D3 = v3(ss); + typedef decltype(coalescedRead(vbaryon_corr[0])) cVec; + cVec result=Zero(); BaryonSite(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,parity,wick_contractions,result); - vbaryon_corr[ss] = result; + coalescedWrite(vbaryon_corr[ss],result); } );//end loop over lattice sites t += usecond(); @@ -567,38 +569,21 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); GridBase *grid = q1_left.Grid(); - - autoView(vbaryon_corr, baryon_corr,CpuWrite); - autoView( v1 , q1_left, CpuRead); - autoView( v2 , q2_left, CpuRead); - autoView( v3 , q3_left, CpuRead); - // Real bytes =0.; - // bytes += grid->oSites() * (432.*sizeof(vComplex) + 126.*sizeof(int) + 36.*sizeof(Real)); - // for (int ie=0; ie < 6 ; ie++){ - // if(ie==0 or ie==3){ - // bytes += grid->oSites() * (4.*sizeof(int) + 4752.*sizeof(vComplex)) * wick_contractions[ie]; - // } - // else{ - // bytes += grid->oSites() * (64.*sizeof(int) + 5184.*sizeof(vComplex)) * wick_contractions[ie]; - // } - // } - // Real t=0.; - // t =-usecond(); + autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( v1 , q1_left , AcceleratorRead); + autoView( v2 , q2_left , AcceleratorRead); + autoView( v3 , q3_left , AcceleratorRead); accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto D1 = v1[ss]; - auto D2 = v2[ss]; - auto D3 = v3[ss]; - sobj result=Zero(); + auto D1 = v1(ss); + auto D2 = v2(ss); + auto D3 = v3(ss); + typedef decltype(coalescedRead(vbaryon_corr[0])) spinor; + spinor result=Zero(); BaryonSiteMatrix(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,wick_contractions,result); - vbaryon_corr[ss] = result; + coalescedWrite(vbaryon_corr[ss],result); } );//end loop over lattice sites - - // t += usecond(); - - // std::cout << GridLogDebug << std::setw(10) << bytes/t*1.0e6/1024/1024/1024 << " GB/s " << std::endl; - } /* The array wick_contractions must be of length 6. The order * @@ -609,16 +594,16 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, template template void BaryonUtils::ContractBaryonsSliced(const mobj &D1, - const mobj &D2, - const mobj &D3, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, - const bool* wick_contractions, - const int parity, - const int nt, - robj &result) + const mobj &D2, + const mobj &D3, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, + const bool* wick_contractions, + const int parity, + const int nt, + robj &result) { assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); @@ -664,11 +649,11 @@ void BaryonUtils::ContractBaryonsSlicedMatrix(const mobj &D1, * Dq3_spec is a quark line from t_i to t_f * Dq4_tf is a quark line from t_f to t_J */ template -template +template accelerator_inline void BaryonUtils::BaryonGamma3ptGroup1Site( const mobj &Dq1_ti, const mobj2 &Dq2_spec, - const mobj2 &Dq3_spec, + // const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -678,41 +663,47 @@ void BaryonUtils::BaryonGamma3ptGroup1Site( { Gamma g5(Gamma::Algebra::Gamma5); - auto adjD4_g_D1 = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq1_ti; +// auto adjD4_g_D1 = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq1_ti; + auto adjD4 = g5 * adj(Dq4_tf) * g5 ; + auto adjD4_g_D1 = adjD4 * GammaJ * Dq1_ti; auto Gf_adjD4_g_D1 = GammaBf * adjD4_g_D1; auto D2_Gi = Dq2_spec * GammaBi; auto Gf_D2_Gi = GammaBf * D2_Gi; - auto Gf_D3 = GammaBf * Dq3_spec; - int a_f, b_f, c_f; - int a_i, b_i, c_i; +// auto Gf_D3 = GammaBf * Dq3_spec; // including a second mobj2 parameter leads to compilation error + auto Gf_D3 = GammaBf * Dq2_spec; //WRONG!!!!! - Real ee; - for (int ie_f=0; ie_f < 6 ; ie_f++){ - a_f = epsilon[ie_f][0]; //a - b_f = epsilon[ie_f][1]; //b - c_f = epsilon[ie_f][2]; //c - for (int ie_i=0; ie_i < 6 ; ie_i++){ - a_i = epsilon[ie_i][0]; //a' - b_i = epsilon[ie_i][1]; //b' - c_i = epsilon[ie_i][2]; //c' + Real ee; - ee = epsilon_sgn[ie_f] * epsilon_sgn[ie_i]; + for (int ie_f=0; ie_f < 6 ; ie_f++){ + int a_f = (ie_f < 3 ? ie_f : (6-ie_f)%3 ); //epsilon[ie_n][0]; //a + int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b + int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c + int eSgn_f = (ie_f < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); + + ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; for (int alpha_f=0; alpha_f::BaryonGamma3ptGroup1Site( * Dq3_spec is a quark line from t_i to t_f * Dq4_tf is a quark line from t_f to t_J */ template -template +template accelerator_inline void BaryonUtils::BaryonGamma3ptGroup2Site( const mobj2 &Dq1_spec, const mobj &Dq2_ti, - const mobj2 &Dq3_spec, + // const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -773,37 +764,40 @@ void BaryonUtils::BaryonGamma3ptGroup2Site( auto adjD4_g_D2_Gi = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq2_ti * GammaBi; auto Gf_adjD4_g_D2_Gi = GammaBf * adjD4_g_D2_Gi; auto Gf_D1 = GammaBf * Dq1_spec; - auto Gf_D3 = GammaBf * Dq3_spec; + //auto Gf_D3 = GammaBf * Dq3_spec; + auto Gf_D3 = GammaBf * Dq1_spec; // WRONG!!!!! - int a_f, b_f, c_f; - int a_i, b_i, c_i; - Real ee; + Real ee; - for (int ie_f=0; ie_f < 6 ; ie_f++){ - a_f = epsilon[ie_f][0]; //a - b_f = epsilon[ie_f][1]; //b - c_f = epsilon[ie_f][2]; //c - for (int ie_i=0; ie_i < 6 ; ie_i++){ - a_i = epsilon[ie_i][0]; //a' - b_i = epsilon[ie_i][1]; //b' - c_i = epsilon[ie_i][2]; //c' + for (int ie_f=0; ie_f < 6 ; ie_f++){ + int a_f = (ie_f < 3 ? ie_f : (6-ie_f)%3 ); //epsilon[ie_n][0]; //a + int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b + int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c + int eSgn_f = (ie_f < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); - ee = epsilon_sgn[ie_f] * epsilon_sgn[ie_i]; + ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; for (int alpha_f=0; alpha_f::BaryonGamma3ptGroup2Site( * Dq3_ti is a quark line from t_i to t_J * Dq4_tf is a quark line from t_f to t_J */ template -template +template accelerator_inline void BaryonUtils::BaryonGamma3ptGroup3Site( const mobj2 &Dq1_spec, - const mobj2 &Dq2_spec, + // const mobj2 &Dq2_spec, const mobj &Dq3_ti, const mobj &Dq4_tf, const Gamma GammaJ, @@ -863,24 +857,25 @@ void BaryonUtils::BaryonGamma3ptGroup3Site( auto adjD4_g_D3 = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq3_ti; auto Gf_adjD4_g_D3 = GammaBf * adjD4_g_D3; auto Gf_D1 = GammaBf * Dq1_spec; - auto D2_Gi = Dq2_spec * GammaBi; + //auto D2_Gi = Dq2_spec * GammaBi; + auto D2_Gi = Dq1_spec * GammaBi; //WRONG!!!!!!!!!!!!!!!!! auto Gf_D2_Gi = GammaBf * D2_Gi; - int a_f, b_f, c_f; - int a_i, b_i, c_i; - Real ee; + Real ee; - for (int ie_f=0; ie_f < 6 ; ie_f++){ - a_f = epsilon[ie_f][0]; //a - b_f = epsilon[ie_f][1]; //b - c_f = epsilon[ie_f][2]; //c - for (int ie_i=0; ie_i < 6 ; ie_i++){ - a_i = epsilon[ie_i][0]; //a' - b_i = epsilon[ie_i][1]; //b' - c_i = epsilon[ie_i][2]; //c' + for (int ie_f=0; ie_f < 6 ; ie_f++){ + int a_f = (ie_f < 3 ? ie_f : (6-ie_f)%3 ); //epsilon[ie_n][0]; //a + int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b + int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c + int eSgn_f = (ie_f < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); - ee = epsilon_sgn[ie_f] * epsilon_sgn[ie_i]; + ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; for (int alpha_f=0; alpha_f::BaryonGamma3pt( const Gamma GammaBf, SpinMatrixField &stn_corr) { + assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); + assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); + GridBase *grid = q_tf.Grid(); - autoView( vcorr, stn_corr, CpuWrite); - autoView( vq_ti , q_ti, CpuRead); - autoView( vq_tf , q_tf, CpuRead); + // autoView( vcorr, stn_corr, CpuWrite); + // autoView( vq_ti , q_ti, CpuRead); + // autoView( vq_tf , q_tf, CpuRead); + + // if (group == 1) { + // accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + // auto Dq_ti = vq_ti[ss]; + // auto Dq_tf = vq_tf[ss]; + // sobj result=Zero(); + // BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + // vcorr[ss] += result; + // });//end loop over lattice sites + // } else if (group == 2) { + // accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + // auto Dq_ti = vq_ti[ss]; + // auto Dq_tf = vq_tf[ss]; + // sobj result=Zero(); + // BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + // vcorr[ss] += result; + // });//end loop over lattice sites + // } else if (group == 3) { + // accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + // auto Dq_ti = vq_ti[ss]; + // auto Dq_tf = vq_tf[ss]; + // sobj result=Zero(); + // BaryonGamma3ptGroup3Site(Dq_spec1,Dq_spec2,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + + // vcorr[ss] += result; + // });//end loop over lattice sites + // } + + autoView( vcorr , stn_corr , AcceleratorWrite); + autoView( vq_ti , q_ti , AcceleratorRead); + autoView( vq_tf , q_tf , AcceleratorRead); if (group == 1) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_ti = vq_ti[ss]; - auto Dq_tf = vq_tf[ss]; - sobj result=Zero(); - BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - vcorr[ss] += result; + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + //sobj result=Zero(); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + //BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); //WRONG + // vcorr[ss] += result; + coalescedWrite(vcorr[ss],result); });//end loop over lattice sites + } else if (group == 2) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_ti = vq_ti[ss]; - auto Dq_tf = vq_tf[ss]; - sobj result=Zero(); - BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - vcorr[ss] += result; + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + //sobj result=Zero(); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + // BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); //WRONG + // vcorr[ss] += result; + coalescedWrite(vcorr[ss],result); });//end loop over lattice sites } else if (group == 3) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_ti = vq_ti[ss]; - auto Dq_tf = vq_tf[ss]; - sobj result=Zero(); - BaryonGamma3ptGroup3Site(Dq_spec1,Dq_spec2,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - - vcorr[ss] += result; + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + //sobj result=Zero(); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + //BaryonGamma3ptGroup3Site(Dq_spec1,Dq_spec2,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + BaryonGamma3ptGroup3Site(Dq_spec1,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); //WRONG + // vcorr[ss] += result; + coalescedWrite(vcorr[ss],result); });//end loop over lattice sites } + } /*********************************************************************** * End of BaryonGamma3pt-function code. * - * * + * * * The following code is for Sigma -> N rare hypeon decays * **********************************************************************/ @@ -997,49 +1039,60 @@ void BaryonUtils::BaryonGamma3pt( * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ template -template +template accelerator_inline void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) { Gamma g5(Gamma::Algebra::Gamma5); - auto DuG = Du_spec * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto GDsGDd = GammaB_sigma * Ds_ti * Gamma_H * g5 * adj(Dd_tf) * g5; - // Dq_loop * \gamma_\mu^L - auto DqG = Dq_loop * Gamma_H; + //auto Gn_adjDd_GH_Ds = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; + auto adjDd_GH_Ds = g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; + auto Gn_adjDd_GH_Ds = GammaB_nucl * adjDd_GH_Ds; + auto Du_Gs = Du_spec * GammaB_sigma; + auto Dq_GH = Dq_loop * Gamma_H; + auto Tr_Dq_GH = trace(Dq_GH)()()(); + + Real ee; for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ template -template +template accelerator_inline void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, - const mobj &Du_tf, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) + const mobj &Du_tf, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) { Gamma g5(Gamma::Algebra::Gamma5); - auto DuG = Du_spec * GammaB_nucl; - auto adjDu = g5 * adj(Du_tf) * g5; - auto adjDuG = adjDu * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto GDsGDd = GammaB_sigma * Ds_ti * Gamma_H * g5 * adj(Dd_tf) * g5; - // Dq_loop * \gamma_\mu^L - auto DuGH = Du_ti * Gamma_H; + auto Du_Gs = Du_spec * GammaB_sigma; + //auto Gn_adjDd_GH_Ds = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; + auto adjDd_GH_Ds = g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; + auto Gn_adjDd_GH_Ds = GammaB_nucl * adjDd_GH_Ds; + auto adjDu_GH_Du = g5 * adj(Du_tf) * g5 * Gamma_H * Du_ti; + auto adjDu_GH_Du_Gs = adjDu_GH_Du * GammaB_sigma; + + Real ee; for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ template -template +template accelerator_inline void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) { Gamma g5(Gamma::Algebra::Gamma5); - auto DuG = Du_spec * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L - auto GDsG = GammaB_sigma * Ds_ti * Gamma_H; - // Dq_loop * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto DqGDd = Dq_loop * Gamma_H * g5 * adj(Dd_tf) * g5; + //auto Gn_adjDd_GH_Duloop_GH_Ds = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Dq_loop * Gamma_H * Ds_ti; + auto adjDd_GH_Duloop_GH_Ds = g5 * adj(Dd_tf) * g5 * Gamma_H * Dq_loop * Gamma_H * Ds_ti; + auto Gn_adjDd_GH_Duloop_GH_Ds = GammaB_nucl * adjDd_GH_Duloop_GH_Ds; + auto Du_Gs = Du_spec * GammaB_sigma; + + Real ee; for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ template -template +template accelerator_inline void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, - const mobj &Du_tf, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result) + const mobj &Du_tf, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result) { Gamma g5(Gamma::Algebra::Gamma5); - auto DuG = Du_spec * GammaB_nucl; - auto adjDu = g5 * adj(Du_tf) * g5; - auto adjDuG = adjDu * GammaB_nucl; - // Gamma^B * Ds * \gamma_\mu^L - auto GDsG = GammaB_sigma * Ds_ti * Gamma_H; - // Du * \gamma_\mu^L * (\gamma_5 * Dd^\dagger * \gamma_5) - auto DuGDd = Du_ti * Gamma_H * g5 * adj(Dd_tf) * g5; + auto Du_Gs = Du_spec * GammaB_sigma; + auto adjDu_GH_Ds = g5 * adj(Du_tf) * g5 * Gamma_H * Ds_ti; + //auto Gn_adjDd_GH_Du = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Du_ti; + auto adjDd_GH_Du = g5 * adj(Dd_tf) * g5 * Gamma_H * Du_ti; + auto Gn_adjDd_GH_Du = GammaB_nucl * adjDd_GH_Du; // for some reason I needed to split this into two lines to avoid the compilation error 'error: identifier "Grid::Gamma::mul" is undefined in device code' + + auto Gn_adjDd_GH_Du_Gs = Gn_adjDd_GH_Du * GammaB_sigma; + + Real ee; for (int ie_n=0; ie_n < 6 ; ie_n++){ - int a_n = epsilon[ie_n][0]; //a - int b_n = epsilon[ie_n][1]; //b - int c_n = epsilon[ie_n][2]; //c - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a' - int b_s = epsilon[ie_s][1]; //b' - int c_s = epsilon[ie_s][2]; //c' - for (int alpha_s=0; alpha_s template void BaryonUtils::SigmaToNucleonEye(const PropagatorField &qq_loop, - const mobj &Du_spec, - const PropagatorField &qd_tf, - const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - const std::string op, - SpinMatrixField &stn_corr) + const mobj &Du_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + const std::string op, + SpinMatrixField &stn_corr) { assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); @@ -1229,39 +1316,43 @@ void BaryonUtils::SigmaToNucleonEye(const PropagatorField &qq_loop, GridBase *grid = qs_ti.Grid(); - autoView( vcorr, stn_corr, CpuWrite); - autoView( vq_loop , qq_loop, CpuRead); - autoView( vd_tf , qd_tf, CpuRead); - autoView( vs_ti , qs_ti, CpuRead); + autoView( vcorr , stn_corr , AcceleratorWrite); + autoView( vq_loop , qq_loop , AcceleratorRead); + autoView( vd_tf , qd_tf , AcceleratorRead); + autoView( vs_ti , qs_ti , AcceleratorRead); + + bool doQ1 = (op == "Q1"); + bool doQ2 = (op == "Q2"); accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_loop = vq_loop[ss]; - auto Dd_tf = vd_tf[ss]; - auto Ds_ti = vs_ti[ss]; - sobj result=Zero(); - if(op == "Q1"){ + auto Dq_loop = vq_loop(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + if(doQ1){ SigmaToNucleonQ1EyeSite(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); - } else if(op == "Q2"){ + } else if(doQ2){ SigmaToNucleonQ2EyeSite(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else { assert(0 && "Weak Operator not correctly specified"); } - vcorr[ss] = result; - } );//end loop over lattice sites + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites } template template void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, - const PropagatorField &qq_tf, - const mobj &Du_spec, - const PropagatorField &qd_tf, - const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - const std::string op, - SpinMatrixField &stn_corr) + const PropagatorField &qq_tf, + const mobj &Du_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + const std::string op, + SpinMatrixField &stn_corr) { assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); @@ -1269,27 +1360,31 @@ void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, GridBase *grid = qs_ti.Grid(); - autoView( vcorr , stn_corr, CpuWrite); - autoView( vq_ti , qq_ti, CpuRead); - autoView( vq_tf , qq_tf, CpuRead); - autoView( vd_tf , qd_tf, CpuRead); - autoView( vs_ti , qs_ti, CpuRead); - // accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - thread_for(ss,grid->oSites(),{ - auto Dq_ti = vq_ti[ss]; - auto Dq_tf = vq_tf[ss]; - auto Dd_tf = vd_tf[ss]; - auto Ds_ti = vs_ti[ss]; - sobj result=Zero(); - if(op == "Q1"){ + autoView( vcorr , stn_corr , AcceleratorWrite ); + autoView( vq_ti , qq_ti , AcceleratorRead ); + autoView( vq_tf , qq_tf , AcceleratorRead ); + autoView( vd_tf , qd_tf , AcceleratorRead ); + autoView( vs_ti , qs_ti , AcceleratorRead ); + + bool doQ1 = (op == "Q1"); + bool doQ2 = (op == "Q2"); + + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + if(doQ1){ SigmaToNucleonQ1NonEyeSite(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); - } else if(op == "Q2"){ + } else if(doQ2){ SigmaToNucleonQ2NonEyeSite(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else { assert(0 && "Weak Operator not correctly specified"); } - vcorr[ss] = result; - } );//end loop over lattice sites + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites } NAMESPACE_END(Grid); From 4dd9e39e0d465e7cad3aef001dc0edf5e65b0ea6 Mon Sep 17 00:00:00 2001 From: Nils Meyer Date: Sat, 19 Dec 2020 00:54:31 +0100 Subject: [PATCH 059/399] up to +36% performance gain for dslash/dwf on QPACE 4 using GCC 10.1.1 --- .../implementation/WilsonKernelsAsmA64FX.h | 268 +- .../WilsonKernelsAsmBodyA64FX.h | 105 +- Grid/simd/Fujitsu_A64FX_asm_double.h | 148 +- Grid/simd/Fujitsu_A64FX_asm_single.h | 148 +- Grid/simd/Fujitsu_A64FX_intrin_double.h | 160 +- Grid/simd/Fujitsu_A64FX_intrin_single.h | 160 +- Grid/simd/Fujitsu_A64FX_undef.h | 1 + Grid/simd/gridverter.py | 2377 ----------------- 8 files changed, 447 insertions(+), 2920 deletions(-) delete mode 100755 Grid/simd/gridverter.py diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h index 2e587dfa..ffec05a0 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h @@ -38,9 +38,6 @@ Author: Nils Meyer Regensburg University // undefine everything related to kernels #include -// enable A64FX body -#define WILSONKERNELSASMBODYA64FX -//#pragma message("A64FX Dslash: WilsonKernelsAsmBodyA64FX.h") /////////////////////////////////////////////////////////// // If we are A64FX specialise the single precision routine @@ -63,119 +60,89 @@ Author: Nils Meyer Regensburg University #define INTERIOR_AND_EXTERIOR #undef INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #define INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #undef INTERIOR #define EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + ///////////////////////////////////////////////////////////////// @@ -185,119 +152,89 @@ WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldV #define INTERIOR_AND_EXTERIOR #undef INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #define INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #undef INTERIOR #define EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + // undefine @@ -330,119 +267,89 @@ WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFie #define INTERIOR_AND_EXTERIOR #undef INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #define INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #undef INTERIOR #define EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + ///////////////////////////////////////////////////////////////// // XYZT vectorised, dag Kernel, double @@ -451,124 +358,93 @@ WilsonKernels::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldV #define INTERIOR_AND_EXTERIOR #undef INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #define INTERIOR #undef EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + #undef INTERIOR_AND_EXTERIOR #undef INTERIOR #define EXTERIOR + +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif +#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") template<> void WilsonKernels::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#if defined (WILSONKERNELSASMBODYA64FX) #include -#else -#include -#endif + // undefs -#undef WILSONKERNELSASMBODYA64FX #include #endif //A64FXASM diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h index 406e5c25..83588a7d 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h @@ -25,6 +25,11 @@ Author: Nils Meyer Regensburg University See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ + +// GCC 10 messes up SVE instruction scheduling using -O3 only, +// using -O3 -fno-schedule-insns -fno-schedule-insns2 does wonders +// performance is better than armclang 20.2 + #ifdef KERNEL_DAG #define DIR0_PROJ XP_PROJ #define DIR1_PROJ YP_PROJ @@ -97,7 +102,7 @@ Author: Nils Meyer Regensburg University PROJ; \ MAYBEPERM(PERMUTE_DIR,perm); \ } else { \ - LOAD_CHI(base); \ + LOAD_CHI(base); \ } \ base = st.GetInfo(ptype,local,perm,NxtDir,ent,plocal); ent++; \ MULT_2SPIN_1(Dir); \ @@ -110,6 +115,15 @@ Author: Nils Meyer Regensburg University } \ RECON; \ +/* +NB: picking PREFETCH_GAUGE_L2(Dir+4); here results in performance penalty + though I expected that it would improve on performance + + if (s == 0) { \ + if ((Dir == 0) || (Dir == 4)) { PREFETCH_GAUGE_L2(Dir); } \ + } \ +*/ + #define ASM_LEG_XP(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) \ base = st.GetInfo(ptype,local,perm,Dir,ent,plocal); ent++; \ PREFETCH1_CHIMU(base); \ @@ -126,73 +140,63 @@ Author: Nils Meyer Regensburg University #define ASM_LEG(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) \ basep = st.GetPFInfo(nent,plocal); nent++; \ - if ( local ) { \ - LOAD_CHIMU(base); \ - LOAD_TABLE(PERMUTE_DIR); \ - PROJ; \ - MAYBEPERM(PERMUTE_DIR,perm); \ - }else if ( st.same_node[Dir] ) {LOAD_CHI(base);} \ - base = st.GetInfo(ptype,local,perm,NxtDir,ent,plocal); ent++; \ - if ( local || st.same_node[Dir] ) { \ - MULT_2SPIN_1(Dir); \ - PREFETCH_CHIMU(base); \ - /* PREFETCH_GAUGE_L1(NxtDir); */ \ - MULT_2SPIN_2; \ - if (s == 0) { \ - if ((Dir == 0) || (Dir == 4)) { PREFETCH_GAUGE_L2(Dir); } \ - } \ - RECON; \ - PREFETCH_CHIMU_L2(basep); \ - } else { PREFETCH_CHIMU(base); } \ + if ( local ) { \ + LOAD_CHIMU(base); \ + LOAD_TABLE(PERMUTE_DIR); \ + PROJ; \ + MAYBEPERM(PERMUTE_DIR,perm); \ + }else if ( st.same_node[Dir] ) {LOAD_CHI(base);} \ + if ( local || st.same_node[Dir] ) { \ + MULT_2SPIN_1(Dir); \ + MULT_2SPIN_2; \ + RECON; \ + } \ + base = st.GetInfo(ptype,local,perm,NxtDir,ent,plocal); ent++; \ + PREFETCH_CHIMU(base); \ + PREFETCH_CHIMU_L2(basep); \ #define ASM_LEG_XP(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) \ base = st.GetInfo(ptype,local,perm,Dir,ent,plocal); ent++; \ PREFETCH1_CHIMU(base); \ + { ZERO_PSI; } \ ASM_LEG(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) #define RESULT(base,basep) SAVE_RESULT(base,basep); #endif + //////////////////////////////////////////////////////////////////////////////// // Post comms kernel //////////////////////////////////////////////////////////////////////////////// #ifdef EXTERIOR - #define ASM_LEG(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) \ - base = st.GetInfo(ptype,local,perm,Dir,ent,plocal); ent++; \ - if((!local)&&(!st.same_node[Dir]) ) { \ - LOAD_CHI(base); \ + base = st.GetInfo(ptype,local,perm,Dir,ent,plocal); ent++; \ + if((!local)&&(!st.same_node[Dir]) ) { \ + LOAD_CHI(base); \ MULT_2SPIN_1(Dir); \ - PREFETCH_CHIMU(base); \ - /* PREFETCH_GAUGE_L1(NxtDir); */ \ MULT_2SPIN_2; \ - if (s == 0) { \ - if ((Dir == 0) || (Dir == 4)) { PREFETCH_GAUGE_L2(Dir); } \ - } \ - RECON; \ - nmu++; \ + RECON; \ + nmu++; \ } -#define ASM_LEG_XP(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) \ - nmu=0; \ - base = st.GetInfo(ptype,local,perm,Dir,ent,plocal); ent++;\ - if((!local)&&(!st.same_node[Dir]) ) { \ - LOAD_CHI(base); \ +#define ASM_LEG_XP(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) \ + nmu=0; \ + { ZERO_PSI;} \ + base = st.GetInfo(ptype,local,perm,Dir,ent,plocal); ent++; \ + if((!local)&&(!st.same_node[Dir]) ) { \ + LOAD_CHI(base); \ MULT_2SPIN_1(Dir); \ - PREFETCH_CHIMU(base); \ - /* PREFETCH_GAUGE_L1(NxtDir); */ \ MULT_2SPIN_2; \ - if (s == 0) { \ - if ((Dir == 0) || (Dir == 4)) { PREFETCH_GAUGE_L2(Dir); } \ - } \ - RECON; \ - nmu++; \ + RECON; \ + nmu++; \ } #define RESULT(base,basep) if (nmu){ ADD_RESULT(base,base);} #endif + + { int nmu; int local,perm, ptype; @@ -209,7 +213,6 @@ Author: Nils Meyer Regensburg University int ssn=ssU+1; if(ssn>=nmax) ssn=0; // int sUn=lo.Reorder(ssn); int sUn=ssn; - LOCK_GAUGE(0); #else int sU =ssU; int ssn=ssU+1; if(ssn>=nmax) ssn=0; @@ -295,6 +298,11 @@ Author: Nils Meyer Regensburg University std::cout << "----------------------------------------------------" << std::endl; #endif + // DC ZVA test + // { uint64_t basestore = (uint64_t)&out[ss]; + // PREFETCH_RESULT_L2_STORE(basestore); } + + ASM_LEG(Ym,Zm,PERMUTE_DIR2,DIR5_PROJ,DIR5_RECON); #ifdef SHOW @@ -308,6 +316,11 @@ Author: Nils Meyer Regensburg University std::cout << "----------------------------------------------------" << std::endl; #endif + // DC ZVA test + //{ uint64_t basestore = (uint64_t)&out[ss]; + // PREFETCH_RESULT_L2_STORE(basestore); } + + ASM_LEG(Zm,Tm,PERMUTE_DIR1,DIR6_PROJ,DIR6_RECON); #ifdef SHOW @@ -321,6 +334,11 @@ Author: Nils Meyer Regensburg University std::cout << "----------------------------------------------------" << std::endl; #endif + // DC ZVA test + //{ uint64_t basestore = (uint64_t)&out[ss]; + // PREFETCH_RESULT_L2_STORE(basestore); + //} + ASM_LEG(Tm,Xp,PERMUTE_DIR0,DIR7_PROJ,DIR7_RECON); #ifdef SHOW @@ -341,6 +359,7 @@ Author: Nils Meyer Regensburg University base = (uint64_t) &out[ss]; basep= st.GetPFInfo(nent,plocal); ent++; basep = (uint64_t) &out[ssn]; + //PREFETCH_RESULT_L1_STORE(base); RESULT(base,basep); #ifdef SHOW diff --git a/Grid/simd/Fujitsu_A64FX_asm_double.h b/Grid/simd/Fujitsu_A64FX_asm_double.h index 76c556d7..bbc4efe7 100644 --- a/Grid/simd/Fujitsu_A64FX_asm_double.h +++ b/Grid/simd/Fujitsu_A64FX_asm_double.h @@ -38,10 +38,11 @@ Author: Nils Meyer #define LOCK_GAUGE(A) #define UNLOCK_GAUGE(A) #define MASK_REGS DECLARATIONS_A64FXd -#define SAVE_RESULT(A,B) RESULT_A64FXd(A); PREFETCH_RESULT_L2_STORE(B) +#define SAVE_RESULT(A,B) RESULT_A64FXd(A); #define MULT_2SPIN_1(Dir) MULT_2SPIN_1_A64FXd(Dir) #define MULT_2SPIN_2 MULT_2SPIN_2_A64FXd #define LOAD_CHI(base) LOAD_CHI_A64FXd(base) +#define ZERO_PSI ZERO_PSI_A64FXd #define ADD_RESULT(base,basep) LOAD_CHIMU(base); ADD_RESULT_INTERNAL_A64FXd; RESULT_A64FXd(base) #define XP_PROJ XP_PROJ_A64FXd #define YP_PROJ YP_PROJ_A64FXd @@ -70,11 +71,18 @@ Author: Nils Meyer #define MAYBEPERM(Dir,perm) if (Dir != 3) { if (perm) { PERMUTE; } } // DECLARATIONS #define DECLARATIONS_A64FXd \ + uint64_t baseU; \ const uint64_t lut[4][8] = { \ {4, 5, 6, 7, 0, 1, 2, 3}, \ {2, 3, 0, 1, 6, 7, 4, 5}, \ {1, 0, 3, 2, 5, 4, 7, 6}, \ {0, 1, 2, 4, 5, 6, 7, 8} };\ +asm ( \ + "ptrue p5.d \n\t" \ + : \ + : \ + : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ +); \ asm ( \ "fmov z31.d , 0 \n\t" \ : \ @@ -130,7 +138,7 @@ asm ( \ // PREFETCH_GAUGE_L2 (prefetch to L2) #define PREFETCH_GAUGE_L2_INTERNAL_A64FXd(A) \ { \ - const auto & ref(U[sUn](A)); uint64_t baseU = (uint64_t)&ref + 3 * 3 * 64; \ + const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ asm ( \ "prfd PLDL2STRM, p5, [%[fetchptr], -4, mul vl] \n\t" \ "prfd PLDL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ @@ -149,7 +157,7 @@ asm ( \ // PREFETCH_GAUGE_L1 (prefetch to L1) #define PREFETCH_GAUGE_L1_INTERNAL_A64FXd(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ asm ( \ "prfd PLDL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ "prfd PLDL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ @@ -163,12 +171,12 @@ asm ( \ #define LOAD_CHI_A64FXd(base) \ { \ asm ( \ - "ldr z12, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], 2, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1d { z12.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1d { z13.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1d { z14.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1d { z15.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1d { z16.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1d { z17.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ : \ : [fetchptr] "r" (base) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -178,19 +186,18 @@ asm ( \ #define LOAD_CHIMU_INTERLEAVED_A64FXd(base) \ { \ asm ( \ - "ptrue p5.d \n\t" \ - "ldr z12, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z21, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z18, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z22, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z19, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z23, [%[fetchptr], 5, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z20, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1d { z12.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1d { z21.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1d { z15.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1d { z18.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1d { z13.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1d { z22.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1d { z16.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1d { z19.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1d { z14.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1d { z23.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1d { z17.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1d { z20.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ : \ : [fetchptr] "r" (base + 2 * 3 * 64) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -201,19 +208,18 @@ asm ( \ { \ const SiteSpinor & ref(in[offset]); \ asm ( \ - "ptrue p5.d \n\t" \ - "ldr z12, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z18, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z19, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z20, [%[fetchptr], 2, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z21, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z22, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z23, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1d { z12.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1d { z18.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1d { z13.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1d { z19.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1d { z14.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1d { z20.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1d { z15.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1d { z21.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1d { z16.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1d { z22.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1d { z17.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1d { z23.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ : \ : [fetchptr] "r" (&ref[2][0]) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -224,19 +230,18 @@ asm ( \ { \ const SiteSpinor & ref(in[offset]); \ asm ( \ - "ptrue p5.d \n\t" \ - "ldr z12, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z21, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z22, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z23, [%[fetchptr], 5, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z18, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z19, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z20, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1d { z12.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1d { z21.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1d { z13.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1d { z22.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1d { z14.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1d { z23.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1d { z15.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1d { z18.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1d { z16.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1d { z19.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1d { z17.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1d { z20.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ : \ : [fetchptr] "r" (&ref[2][0]) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -293,17 +298,16 @@ asm ( \ ); // LOAD_GAUGE -#define LOAD_GAUGE \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ +#define LOAD_GAUGE(A) \ { \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ asm ( \ - "ptrue p5.d \n\t" \ - "ldr z24, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z25, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z26, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z27, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z28, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z29, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1d { z24.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1d { z25.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1d { z26.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1d { z27.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1d { z28.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1d { z29.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ : \ : [fetchptr] "r" (baseU + 2 * 3 * 64) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -312,14 +316,14 @@ asm ( \ // MULT_2SPIN #define MULT_2SPIN_1_A64FXd(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ asm ( \ - "ldr z24, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z25, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z26, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z27, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z28, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z29, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1d { z24.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1d { z25.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1d { z26.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1d { z27.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1d { z28.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1d { z29.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ "movprfx z18.d, p5/m, z31.d \n\t" \ "fcmla z18.d, p5/m, z24.d, z12.d, 0 \n\t" \ "movprfx z21.d, p5/m, z31.d \n\t" \ @@ -338,9 +342,9 @@ asm ( \ "fcmla z22.d, p5/m, z25.d, z15.d, 90 \n\t" \ "fcmla z20.d, p5/m, z26.d, z12.d, 90 \n\t" \ "fcmla z23.d, p5/m, z26.d, z15.d, 90 \n\t" \ - "ldr z24, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z25, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z26, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1d { z24.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1d { z25.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1d { z26.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ : \ : [fetchptr] "r" (baseU + 2 * 3 * 64) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -560,7 +564,6 @@ asm ( \ #define TM_PROJ_A64FXd \ { \ asm ( \ - "ptrue p5.d \n\t" \ "fsub z12.d, p5/m, z12.d, z18.d \n\t" \ "fsub z13.d, p5/m, z13.d, z19.d \n\t" \ "fsub z14.d, p5/m, z14.d, z20.d \n\t" \ @@ -715,7 +718,6 @@ asm ( \ // ZERO_PSI #define ZERO_PSI_A64FXd \ asm ( \ - "ptrue p5.d \n\t" \ "fmov z0.d , 0 \n\t" \ "fmov z1.d , 0 \n\t" \ "fmov z2.d , 0 \n\t" \ @@ -733,13 +735,13 @@ asm ( \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ ); -// PREFETCH_RESULT_L2_STORE (prefetch store to L2) +// PREFETCH_RESULT_L2_STORE (uses DC ZVA for cache line zeroing) #define PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXd(base) \ { \ asm ( \ - "prfd PSTL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PSTL2STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PSTL2STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ + "dc zva, %[fetchptr]\n\t" \ + "dc zva, %[fetchptr]\n\t" \ + "dc zva, %[fetchptr]\n\t" \ : \ : [fetchptr] "r" (base) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ diff --git a/Grid/simd/Fujitsu_A64FX_asm_single.h b/Grid/simd/Fujitsu_A64FX_asm_single.h index d809f83b..e629f617 100644 --- a/Grid/simd/Fujitsu_A64FX_asm_single.h +++ b/Grid/simd/Fujitsu_A64FX_asm_single.h @@ -38,10 +38,11 @@ Author: Nils Meyer #define LOCK_GAUGE(A) #define UNLOCK_GAUGE(A) #define MASK_REGS DECLARATIONS_A64FXf -#define SAVE_RESULT(A,B) RESULT_A64FXf(A); PREFETCH_RESULT_L2_STORE(B) +#define SAVE_RESULT(A,B) RESULT_A64FXf(A); #define MULT_2SPIN_1(Dir) MULT_2SPIN_1_A64FXf(Dir) #define MULT_2SPIN_2 MULT_2SPIN_2_A64FXf #define LOAD_CHI(base) LOAD_CHI_A64FXf(base) +#define ZERO_PSI ZERO_PSI_A64FXf #define ADD_RESULT(base,basep) LOAD_CHIMU(base); ADD_RESULT_INTERNAL_A64FXf; RESULT_A64FXf(base) #define XP_PROJ XP_PROJ_A64FXf #define YP_PROJ YP_PROJ_A64FXf @@ -70,11 +71,18 @@ Author: Nils Meyer #define MAYBEPERM(A,perm) if (perm) { PERMUTE; } // DECLARATIONS #define DECLARATIONS_A64FXf \ + uint64_t baseU; \ const uint32_t lut[4][16] = { \ {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}, \ {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}, \ {2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}, \ {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14} }; \ +asm ( \ + "ptrue p5.s \n\t" \ + : \ + : \ + : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ +); \ asm ( \ "fmov z31.s , 0 \n\t" \ : \ @@ -130,7 +138,7 @@ asm ( \ // PREFETCH_GAUGE_L2 (prefetch to L2) #define PREFETCH_GAUGE_L2_INTERNAL_A64FXf(A) \ { \ - const auto & ref(U[sUn](A)); uint64_t baseU = (uint64_t)&ref + 3 * 3 * 64; \ + const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ asm ( \ "prfd PLDL2STRM, p5, [%[fetchptr], -4, mul vl] \n\t" \ "prfd PLDL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ @@ -149,7 +157,7 @@ asm ( \ // PREFETCH_GAUGE_L1 (prefetch to L1) #define PREFETCH_GAUGE_L1_INTERNAL_A64FXf(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ asm ( \ "prfd PLDL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ "prfd PLDL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ @@ -163,12 +171,12 @@ asm ( \ #define LOAD_CHI_A64FXf(base) \ { \ asm ( \ - "ldr z12, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], 2, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1w { z12.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1w { z13.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1w { z14.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1w { z15.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1w { z16.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1w { z17.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ : \ : [fetchptr] "r" (base) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -178,19 +186,18 @@ asm ( \ #define LOAD_CHIMU_INTERLEAVED_A64FXf(base) \ { \ asm ( \ - "ptrue p5.s \n\t" \ - "ldr z12, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z21, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z18, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z22, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z19, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z23, [%[fetchptr], 5, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z20, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1w { z12.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1w { z21.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1w { z15.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1w { z18.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1w { z13.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1w { z22.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1w { z16.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1w { z19.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1w { z14.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1w { z23.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1w { z17.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1w { z20.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ : \ : [fetchptr] "r" (base + 2 * 3 * 64) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -201,19 +208,18 @@ asm ( \ { \ const SiteSpinor & ref(in[offset]); \ asm ( \ - "ptrue p5.s \n\t" \ - "ldr z12, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z18, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z19, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z20, [%[fetchptr], 2, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z21, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z22, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z23, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1w { z12.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1w { z18.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1w { z13.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1w { z19.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1w { z14.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1w { z20.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1w { z15.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1w { z21.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1w { z16.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1w { z22.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1w { z17.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1w { z23.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ : \ : [fetchptr] "r" (&ref[2][0]) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -224,19 +230,18 @@ asm ( \ { \ const SiteSpinor & ref(in[offset]); \ asm ( \ - "ptrue p5.s \n\t" \ - "ldr z12, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z21, [%[fetchptr], 3, mul vl] \n\t" \ - "ldr z13, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z22, [%[fetchptr], 4, mul vl] \n\t" \ - "ldr z14, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z23, [%[fetchptr], 5, mul vl] \n\t" \ - "ldr z15, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z18, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z16, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z19, [%[fetchptr], 1, mul vl] \n\t" \ - "ldr z17, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z20, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1w { z12.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1w { z21.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ + "ld1w { z13.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1w { z22.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ + "ld1w { z14.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1w { z23.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ + "ld1w { z15.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1w { z18.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1w { z16.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1w { z19.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1w { z17.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1w { z20.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ : \ : [fetchptr] "r" (&ref[2][0]) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -293,17 +298,16 @@ asm ( \ ); // LOAD_GAUGE -#define LOAD_GAUGE \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ +#define LOAD_GAUGE(A) \ { \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ asm ( \ - "ptrue p5.s \n\t" \ - "ldr z24, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z25, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z26, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z27, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z28, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z29, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1w { z24.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1w { z25.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1w { z26.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1w { z27.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1w { z28.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1w { z29.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ : \ : [fetchptr] "r" (baseU + 2 * 3 * 64) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -312,14 +316,14 @@ asm ( \ // MULT_2SPIN #define MULT_2SPIN_1_A64FXf(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ asm ( \ - "ldr z24, [%[fetchptr], -6, mul vl] \n\t" \ - "ldr z25, [%[fetchptr], -3, mul vl] \n\t" \ - "ldr z26, [%[fetchptr], 0, mul vl] \n\t" \ - "ldr z27, [%[fetchptr], -5, mul vl] \n\t" \ - "ldr z28, [%[fetchptr], -2, mul vl] \n\t" \ - "ldr z29, [%[fetchptr], 1, mul vl] \n\t" \ + "ld1w { z24.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ + "ld1w { z25.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ + "ld1w { z26.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ + "ld1w { z27.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ + "ld1w { z28.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ + "ld1w { z29.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ "movprfx z18.s, p5/m, z31.s \n\t" \ "fcmla z18.s, p5/m, z24.s, z12.s, 0 \n\t" \ "movprfx z21.s, p5/m, z31.s \n\t" \ @@ -338,9 +342,9 @@ asm ( \ "fcmla z22.s, p5/m, z25.s, z15.s, 90 \n\t" \ "fcmla z20.s, p5/m, z26.s, z12.s, 90 \n\t" \ "fcmla z23.s, p5/m, z26.s, z15.s, 90 \n\t" \ - "ldr z24, [%[fetchptr], -4, mul vl] \n\t" \ - "ldr z25, [%[fetchptr], -1, mul vl] \n\t" \ - "ldr z26, [%[fetchptr], 2, mul vl] \n\t" \ + "ld1w { z24.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ + "ld1w { z25.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ + "ld1w { z26.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ : \ : [fetchptr] "r" (baseU + 2 * 3 * 64) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ @@ -560,7 +564,6 @@ asm ( \ #define TM_PROJ_A64FXf \ { \ asm ( \ - "ptrue p5.s \n\t" \ "fsub z12.s, p5/m, z12.s, z18.s \n\t" \ "fsub z13.s, p5/m, z13.s, z19.s \n\t" \ "fsub z14.s, p5/m, z14.s, z20.s \n\t" \ @@ -715,7 +718,6 @@ asm ( \ // ZERO_PSI #define ZERO_PSI_A64FXf \ asm ( \ - "ptrue p5.s \n\t" \ "fmov z0.s , 0 \n\t" \ "fmov z1.s , 0 \n\t" \ "fmov z2.s , 0 \n\t" \ @@ -733,13 +735,13 @@ asm ( \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ ); -// PREFETCH_RESULT_L2_STORE (prefetch store to L2) +// PREFETCH_RESULT_L2_STORE (uses DC ZVA for cache line zeroing) #define PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXf(base) \ { \ asm ( \ - "prfd PSTL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PSTL2STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PSTL2STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ + "dc zva, %[fetchptr]\n\t" \ + "dc zva, %[fetchptr]\n\t" \ + "dc zva, %[fetchptr]\n\t" \ : \ : [fetchptr] "r" (base) \ : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ diff --git a/Grid/simd/Fujitsu_A64FX_intrin_double.h b/Grid/simd/Fujitsu_A64FX_intrin_double.h index 232610f2..361246fc 100644 --- a/Grid/simd/Fujitsu_A64FX_intrin_double.h +++ b/Grid/simd/Fujitsu_A64FX_intrin_double.h @@ -38,10 +38,11 @@ Author: Nils Meyer #define LOCK_GAUGE(A) #define UNLOCK_GAUGE(A) #define MASK_REGS DECLARATIONS_A64FXd -#define SAVE_RESULT(A,B) RESULT_A64FXd(A); PREFETCH_RESULT_L2_STORE(B) +#define SAVE_RESULT(A,B) RESULT_A64FXd(A); #define MULT_2SPIN_1(Dir) MULT_2SPIN_1_A64FXd(Dir) #define MULT_2SPIN_2 MULT_2SPIN_2_A64FXd #define LOAD_CHI(base) LOAD_CHI_A64FXd(base) +#define ZERO_PSI ZERO_PSI_A64FXd #define ADD_RESULT(base,basep) LOAD_CHIMU(base); ADD_RESULT_INTERNAL_A64FXd; RESULT_A64FXd(base) #define XP_PROJ XP_PROJ_A64FXd #define YP_PROJ YP_PROJ_A64FXd @@ -70,6 +71,7 @@ Author: Nils Meyer #define MAYBEPERM(Dir,perm) if (Dir != 3) { if (perm) { PERMUTE; } } // DECLARATIONS #define DECLARATIONS_A64FXd \ + uint64_t baseU; \ const uint64_t lut[4][8] = { \ {4, 5, 6, 7, 0, 1, 2, 3}, \ {2, 3, 0, 1, 6, 7, 4, 5}, \ @@ -126,18 +128,18 @@ Author: Nils Meyer // RESULT #define RESULT_A64FXd(base) \ { \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + -6 * 64), result_00); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + -5 * 64), result_01); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + -4 * 64), result_02); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + -3 * 64), result_10); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + -2 * 64), result_11); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + -1 * 64), result_12); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + 0 * 64), result_20); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + 1 * 64), result_21); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + 2 * 64), result_22); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + 3 * 64), result_30); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + 4 * 64), result_31); \ - svst1(pg1, (float64_t*)(base + 2 * 3 * 64 + 5 * 64), result_32); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(-6), result_00); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(-5), result_01); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(-4), result_02); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(-3), result_10); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(-2), result_11); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(-1), result_12); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(0), result_20); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(1), result_21); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(2), result_22); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(3), result_30); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(4), result_31); \ + svst1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64),(int64_t)(5), result_32); \ } // PREFETCH_CHIMU_L2 (prefetch to L2) #define PREFETCH_CHIMU_L2_INTERNAL_A64FXd(base) \ @@ -156,7 +158,7 @@ Author: Nils Meyer // PREFETCH_GAUGE_L2 (prefetch to L2) #define PREFETCH_GAUGE_L2_INTERNAL_A64FXd(A) \ { \ - const auto & ref(U[sUn](A)); uint64_t baseU = (uint64_t)&ref + 3 * 3 * 64; \ + const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ svprfd(pg1, (int64_t*)(baseU + -256), SV_PLDL2STRM); \ svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL2STRM); \ svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL2STRM); \ @@ -170,7 +172,7 @@ Author: Nils Meyer // PREFETCH_GAUGE_L1 (prefetch to L1) #define PREFETCH_GAUGE_L1_INTERNAL_A64FXd(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL1STRM); \ svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL1STRM); \ svprfd(pg1, (int64_t*)(baseU + 512), SV_PLDL1STRM); \ @@ -178,62 +180,62 @@ Author: Nils Meyer // LOAD_CHI #define LOAD_CHI_A64FXd(base) \ { \ - Chi_00 = svld1(pg1, (float64_t*)(base + 0 * 64)); \ - Chi_01 = svld1(pg1, (float64_t*)(base + 1 * 64)); \ - Chi_02 = svld1(pg1, (float64_t*)(base + 2 * 64)); \ - Chi_10 = svld1(pg1, (float64_t*)(base + 3 * 64)); \ - Chi_11 = svld1(pg1, (float64_t*)(base + 4 * 64)); \ - Chi_12 = svld1(pg1, (float64_t*)(base + 5 * 64)); \ + Chi_00 = svld1_vnum(pg1, (float64_t*)(base), (int64_t)(0)); \ + Chi_01 = svld1_vnum(pg1, (float64_t*)(base), (int64_t)(1)); \ + Chi_02 = svld1_vnum(pg1, (float64_t*)(base), (int64_t)(2)); \ + Chi_10 = svld1_vnum(pg1, (float64_t*)(base), (int64_t)(3)); \ + Chi_11 = svld1_vnum(pg1, (float64_t*)(base), (int64_t)(4)); \ + Chi_12 = svld1_vnum(pg1, (float64_t*)(base), (int64_t)(5)); \ } // LOAD_CHIMU #define LOAD_CHIMU_INTERLEAVED_A64FXd(base) \ { \ - Chimu_00 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -6 * 64)); \ - Chimu_30 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 3 * 64)); \ - Chimu_10 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -3 * 64)); \ - Chimu_20 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 0 * 64)); \ - Chimu_01 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -5 * 64)); \ - Chimu_31 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 4 * 64)); \ - Chimu_11 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -2 * 64)); \ - Chimu_21 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 1 * 64)); \ - Chimu_02 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -4 * 64)); \ - Chimu_32 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 5 * 64)); \ - Chimu_12 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -1 * 64)); \ - Chimu_22 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 2 * 64)); \ + Chimu_00 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-6)); \ + Chimu_30 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(3)); \ + Chimu_10 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-3)); \ + Chimu_20 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(0)); \ + Chimu_01 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-5)); \ + Chimu_31 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(4)); \ + Chimu_11 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-2)); \ + Chimu_21 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(1)); \ + Chimu_02 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-4)); \ + Chimu_32 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(5)); \ + Chimu_12 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-1)); \ + Chimu_22 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(2)); \ } // LOAD_CHIMU_0213 #define LOAD_CHIMU_0213_A64FXd \ { \ const SiteSpinor & ref(in[offset]); \ - Chimu_00 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -6 * 64)); \ - Chimu_20 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 0 * 64)); \ - Chimu_01 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -5 * 64)); \ - Chimu_21 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 1 * 64)); \ - Chimu_02 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -4 * 64)); \ - Chimu_22 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 2 * 64)); \ - Chimu_10 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -3 * 64)); \ - Chimu_30 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 3 * 64)); \ - Chimu_11 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -2 * 64)); \ - Chimu_31 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 4 * 64)); \ - Chimu_12 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -1 * 64)); \ - Chimu_32 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 5 * 64)); \ + Chimu_00 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-6)); \ + Chimu_20 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(0)); \ + Chimu_01 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-5)); \ + Chimu_21 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(1)); \ + Chimu_02 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-4)); \ + Chimu_22 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(2)); \ + Chimu_10 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-3)); \ + Chimu_30 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(3)); \ + Chimu_11 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-2)); \ + Chimu_31 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(4)); \ + Chimu_12 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-1)); \ + Chimu_32 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(5)); \ } // LOAD_CHIMU_0312 #define LOAD_CHIMU_0312_A64FXd \ { \ const SiteSpinor & ref(in[offset]); \ - Chimu_00 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -6 * 64)); \ - Chimu_30 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 3 * 64)); \ - Chimu_01 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -5 * 64)); \ - Chimu_31 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 4 * 64)); \ - Chimu_02 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -4 * 64)); \ - Chimu_32 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 5 * 64)); \ - Chimu_10 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -3 * 64)); \ - Chimu_20 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 0 * 64)); \ - Chimu_11 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -2 * 64)); \ - Chimu_21 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 1 * 64)); \ - Chimu_12 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + -1 * 64)); \ - Chimu_22 = svld1(pg1, (float64_t*)(base + 2 * 3 * 64 + 2 * 64)); \ + Chimu_00 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-6)); \ + Chimu_30 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(3)); \ + Chimu_01 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-5)); \ + Chimu_31 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(4)); \ + Chimu_02 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-4)); \ + Chimu_32 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(5)); \ + Chimu_10 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-3)); \ + Chimu_20 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(0)); \ + Chimu_11 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-2)); \ + Chimu_21 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(1)); \ + Chimu_12 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(-1)); \ + Chimu_22 = svld1_vnum(pg1, (float64_t*)(base + 2 * 3 * 64), (int64_t)(2)); \ } // LOAD_TABLE0 #define LOAD_TABLE0 \ @@ -261,26 +263,26 @@ Author: Nils Meyer Chi_12 = svtbl(Chi_12, table0); // LOAD_GAUGE -#define LOAD_GAUGE \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ +#define LOAD_GAUGE(A) \ { \ - U_00 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -6 * 64)); \ - U_10 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -3 * 64)); \ - U_20 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + 0 * 64)); \ - U_01 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -5 * 64)); \ - U_11 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -2 * 64)); \ - U_21 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + 1 * 64)); \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ + U_00 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-6)); \ + U_10 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-3)); \ + U_20 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(0)); \ + U_01 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-5)); \ + U_11 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-2)); \ + U_21 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(1)); \ } // MULT_2SPIN #define MULT_2SPIN_1_A64FXd(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ - U_00 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -6 * 64)); \ - U_10 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -3 * 64)); \ - U_20 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + 0 * 64)); \ - U_01 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -5 * 64)); \ - U_11 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -2 * 64)); \ - U_21 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + 1 * 64)); \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ + U_00 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-6)); \ + U_10 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-3)); \ + U_20 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(0)); \ + U_01 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-5)); \ + U_11 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-2)); \ + U_21 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(1)); \ UChi_00 = svcmla_x(pg1, zero0, U_00, Chi_00, 0); \ UChi_10 = svcmla_x(pg1, zero0, U_00, Chi_10, 0); \ UChi_01 = svcmla_x(pg1, zero0, U_10, Chi_00, 0); \ @@ -293,9 +295,9 @@ Author: Nils Meyer UChi_11 = svcmla_x(pg1, UChi_11, U_10, Chi_10, 90); \ UChi_02 = svcmla_x(pg1, UChi_02, U_20, Chi_00, 90); \ UChi_12 = svcmla_x(pg1, UChi_12, U_20, Chi_10, 90); \ - U_00 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -4 * 64)); \ - U_10 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + -1 * 64)); \ - U_20 = svld1(pg1, (float64_t*)(baseU + 2 * 3 * 64 + 2 * 64)); \ + U_00 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-4)); \ + U_10 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(-1)); \ + U_20 = svld1_vnum(pg1, (float64_t*)(baseU + 2 * 3 * 64), (int64_t)(2)); \ } // MULT_2SPIN_BACKEND #define MULT_2SPIN_2_A64FXd \ @@ -570,12 +572,12 @@ Author: Nils Meyer result_31 = svdup_f64(0.); \ result_32 = svdup_f64(0.); -// PREFETCH_RESULT_L2_STORE (prefetch store to L2) +// PREFETCH_RESULT_L2_STORE (uses DC ZVA for cache line zeroing) #define PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXd(base) \ { \ - svprfd(pg1, (int64_t*)(base + 0), SV_PSTL2STRM); \ - svprfd(pg1, (int64_t*)(base + 256), SV_PSTL2STRM); \ - svprfd(pg1, (int64_t*)(base + 512), SV_PSTL2STRM); \ + asm( "dc zva, %[fetchptr] \n\t" : : [fetchptr] "r" (base + 256 * 0) : "memory" ); \ + asm( "dc zva, %[fetchptr] \n\t" : : [fetchptr] "r" (base + 256 * 1) : "memory" ); \ + asm( "dc zva, %[fetchptr] \n\t" : : [fetchptr] "r" (base + 256 * 2) : "memory" ); \ } // PREFETCH_RESULT_L1_STORE (prefetch store to L1) #define PREFETCH_RESULT_L1_STORE_INTERNAL_A64FXd(base) \ diff --git a/Grid/simd/Fujitsu_A64FX_intrin_single.h b/Grid/simd/Fujitsu_A64FX_intrin_single.h index 180e5f4f..30273b6e 100644 --- a/Grid/simd/Fujitsu_A64FX_intrin_single.h +++ b/Grid/simd/Fujitsu_A64FX_intrin_single.h @@ -38,10 +38,11 @@ Author: Nils Meyer #define LOCK_GAUGE(A) #define UNLOCK_GAUGE(A) #define MASK_REGS DECLARATIONS_A64FXf -#define SAVE_RESULT(A,B) RESULT_A64FXf(A); PREFETCH_RESULT_L2_STORE(B) +#define SAVE_RESULT(A,B) RESULT_A64FXf(A); #define MULT_2SPIN_1(Dir) MULT_2SPIN_1_A64FXf(Dir) #define MULT_2SPIN_2 MULT_2SPIN_2_A64FXf #define LOAD_CHI(base) LOAD_CHI_A64FXf(base) +#define ZERO_PSI ZERO_PSI_A64FXf #define ADD_RESULT(base,basep) LOAD_CHIMU(base); ADD_RESULT_INTERNAL_A64FXf; RESULT_A64FXf(base) #define XP_PROJ XP_PROJ_A64FXf #define YP_PROJ YP_PROJ_A64FXf @@ -70,6 +71,7 @@ Author: Nils Meyer #define MAYBEPERM(A,perm) if (perm) { PERMUTE; } // DECLARATIONS #define DECLARATIONS_A64FXf \ + uint64_t baseU; \ const uint32_t lut[4][16] = { \ {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}, \ {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}, \ @@ -126,18 +128,18 @@ Author: Nils Meyer // RESULT #define RESULT_A64FXf(base) \ { \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + -6 * 64), result_00); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + -5 * 64), result_01); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + -4 * 64), result_02); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + -3 * 64), result_10); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + -2 * 64), result_11); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + -1 * 64), result_12); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + 0 * 64), result_20); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + 1 * 64), result_21); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + 2 * 64), result_22); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + 3 * 64), result_30); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + 4 * 64), result_31); \ - svst1(pg1, (float32_t*)(base + 2 * 3 * 64 + 5 * 64), result_32); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(-6), result_00); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(-5), result_01); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(-4), result_02); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(-3), result_10); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(-2), result_11); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(-1), result_12); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(0), result_20); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(1), result_21); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(2), result_22); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(3), result_30); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(4), result_31); \ + svst1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64),(int64_t)(5), result_32); \ } // PREFETCH_CHIMU_L2 (prefetch to L2) #define PREFETCH_CHIMU_L2_INTERNAL_A64FXf(base) \ @@ -156,7 +158,7 @@ Author: Nils Meyer // PREFETCH_GAUGE_L2 (prefetch to L2) #define PREFETCH_GAUGE_L2_INTERNAL_A64FXf(A) \ { \ - const auto & ref(U[sUn](A)); uint64_t baseU = (uint64_t)&ref + 3 * 3 * 64; \ + const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ svprfd(pg1, (int64_t*)(baseU + -256), SV_PLDL2STRM); \ svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL2STRM); \ svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL2STRM); \ @@ -170,7 +172,7 @@ Author: Nils Meyer // PREFETCH_GAUGE_L1 (prefetch to L1) #define PREFETCH_GAUGE_L1_INTERNAL_A64FXf(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL1STRM); \ svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL1STRM); \ svprfd(pg1, (int64_t*)(baseU + 512), SV_PLDL1STRM); \ @@ -178,62 +180,62 @@ Author: Nils Meyer // LOAD_CHI #define LOAD_CHI_A64FXf(base) \ { \ - Chi_00 = svld1(pg1, (float32_t*)(base + 0 * 64)); \ - Chi_01 = svld1(pg1, (float32_t*)(base + 1 * 64)); \ - Chi_02 = svld1(pg1, (float32_t*)(base + 2 * 64)); \ - Chi_10 = svld1(pg1, (float32_t*)(base + 3 * 64)); \ - Chi_11 = svld1(pg1, (float32_t*)(base + 4 * 64)); \ - Chi_12 = svld1(pg1, (float32_t*)(base + 5 * 64)); \ + Chi_00 = svld1_vnum(pg1, (float32_t*)(base), (int64_t)(0)); \ + Chi_01 = svld1_vnum(pg1, (float32_t*)(base), (int64_t)(1)); \ + Chi_02 = svld1_vnum(pg1, (float32_t*)(base), (int64_t)(2)); \ + Chi_10 = svld1_vnum(pg1, (float32_t*)(base), (int64_t)(3)); \ + Chi_11 = svld1_vnum(pg1, (float32_t*)(base), (int64_t)(4)); \ + Chi_12 = svld1_vnum(pg1, (float32_t*)(base), (int64_t)(5)); \ } // LOAD_CHIMU #define LOAD_CHIMU_INTERLEAVED_A64FXf(base) \ { \ - Chimu_00 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -6 * 64)); \ - Chimu_30 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 3 * 64)); \ - Chimu_10 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -3 * 64)); \ - Chimu_20 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 0 * 64)); \ - Chimu_01 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -5 * 64)); \ - Chimu_31 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 4 * 64)); \ - Chimu_11 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -2 * 64)); \ - Chimu_21 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 1 * 64)); \ - Chimu_02 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -4 * 64)); \ - Chimu_32 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 5 * 64)); \ - Chimu_12 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -1 * 64)); \ - Chimu_22 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 2 * 64)); \ + Chimu_00 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-6)); \ + Chimu_30 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(3)); \ + Chimu_10 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-3)); \ + Chimu_20 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(0)); \ + Chimu_01 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-5)); \ + Chimu_31 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(4)); \ + Chimu_11 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-2)); \ + Chimu_21 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(1)); \ + Chimu_02 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-4)); \ + Chimu_32 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(5)); \ + Chimu_12 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-1)); \ + Chimu_22 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(2)); \ } // LOAD_CHIMU_0213 #define LOAD_CHIMU_0213_A64FXf \ { \ const SiteSpinor & ref(in[offset]); \ - Chimu_00 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -6 * 64)); \ - Chimu_20 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 0 * 64)); \ - Chimu_01 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -5 * 64)); \ - Chimu_21 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 1 * 64)); \ - Chimu_02 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -4 * 64)); \ - Chimu_22 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 2 * 64)); \ - Chimu_10 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -3 * 64)); \ - Chimu_30 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 3 * 64)); \ - Chimu_11 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -2 * 64)); \ - Chimu_31 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 4 * 64)); \ - Chimu_12 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -1 * 64)); \ - Chimu_32 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 5 * 64)); \ + Chimu_00 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-6)); \ + Chimu_20 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(0)); \ + Chimu_01 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-5)); \ + Chimu_21 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(1)); \ + Chimu_02 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-4)); \ + Chimu_22 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(2)); \ + Chimu_10 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-3)); \ + Chimu_30 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(3)); \ + Chimu_11 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-2)); \ + Chimu_31 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(4)); \ + Chimu_12 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-1)); \ + Chimu_32 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(5)); \ } // LOAD_CHIMU_0312 #define LOAD_CHIMU_0312_A64FXf \ { \ const SiteSpinor & ref(in[offset]); \ - Chimu_00 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -6 * 64)); \ - Chimu_30 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 3 * 64)); \ - Chimu_01 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -5 * 64)); \ - Chimu_31 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 4 * 64)); \ - Chimu_02 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -4 * 64)); \ - Chimu_32 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 5 * 64)); \ - Chimu_10 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -3 * 64)); \ - Chimu_20 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 0 * 64)); \ - Chimu_11 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -2 * 64)); \ - Chimu_21 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 1 * 64)); \ - Chimu_12 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + -1 * 64)); \ - Chimu_22 = svld1(pg1, (float32_t*)(base + 2 * 3 * 64 + 2 * 64)); \ + Chimu_00 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-6)); \ + Chimu_30 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(3)); \ + Chimu_01 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-5)); \ + Chimu_31 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(4)); \ + Chimu_02 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-4)); \ + Chimu_32 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(5)); \ + Chimu_10 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-3)); \ + Chimu_20 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(0)); \ + Chimu_11 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-2)); \ + Chimu_21 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(1)); \ + Chimu_12 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(-1)); \ + Chimu_22 = svld1_vnum(pg1, (float32_t*)(base + 2 * 3 * 64), (int64_t)(2)); \ } // LOAD_TABLE0 #define LOAD_TABLE0 \ @@ -261,26 +263,26 @@ Author: Nils Meyer Chi_12 = svtbl(Chi_12, table0); // LOAD_GAUGE -#define LOAD_GAUGE \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ +#define LOAD_GAUGE(A) \ { \ - U_00 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -6 * 64)); \ - U_10 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -3 * 64)); \ - U_20 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + 0 * 64)); \ - U_01 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -5 * 64)); \ - U_11 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -2 * 64)); \ - U_21 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + 1 * 64)); \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ + U_00 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-6)); \ + U_10 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-3)); \ + U_20 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(0)); \ + U_01 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-5)); \ + U_11 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-2)); \ + U_21 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(1)); \ } // MULT_2SPIN #define MULT_2SPIN_1_A64FXf(A) \ { \ - const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \ - U_00 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -6 * 64)); \ - U_10 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -3 * 64)); \ - U_20 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + 0 * 64)); \ - U_01 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -5 * 64)); \ - U_11 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -2 * 64)); \ - U_21 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + 1 * 64)); \ + const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ + U_00 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-6)); \ + U_10 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-3)); \ + U_20 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(0)); \ + U_01 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-5)); \ + U_11 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-2)); \ + U_21 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(1)); \ UChi_00 = svcmla_x(pg1, zero0, U_00, Chi_00, 0); \ UChi_10 = svcmla_x(pg1, zero0, U_00, Chi_10, 0); \ UChi_01 = svcmla_x(pg1, zero0, U_10, Chi_00, 0); \ @@ -293,9 +295,9 @@ Author: Nils Meyer UChi_11 = svcmla_x(pg1, UChi_11, U_10, Chi_10, 90); \ UChi_02 = svcmla_x(pg1, UChi_02, U_20, Chi_00, 90); \ UChi_12 = svcmla_x(pg1, UChi_12, U_20, Chi_10, 90); \ - U_00 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -4 * 64)); \ - U_10 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + -1 * 64)); \ - U_20 = svld1(pg1, (float32_t*)(baseU + 2 * 3 * 64 + 2 * 64)); \ + U_00 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-4)); \ + U_10 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(-1)); \ + U_20 = svld1_vnum(pg1, (float32_t*)(baseU + 2 * 3 * 64), (int64_t)(2)); \ } // MULT_2SPIN_BACKEND #define MULT_2SPIN_2_A64FXf \ @@ -570,12 +572,12 @@ Author: Nils Meyer result_31 = svdup_f32(0.); \ result_32 = svdup_f32(0.); -// PREFETCH_RESULT_L2_STORE (prefetch store to L2) +// PREFETCH_RESULT_L2_STORE (uses DC ZVA for cache line zeroing) #define PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXf(base) \ { \ - svprfd(pg1, (int64_t*)(base + 0), SV_PSTL2STRM); \ - svprfd(pg1, (int64_t*)(base + 256), SV_PSTL2STRM); \ - svprfd(pg1, (int64_t*)(base + 512), SV_PSTL2STRM); \ + asm( "dc zva, %[fetchptr] \n\t" : : [fetchptr] "r" (base + 256 * 0) : "memory" ); \ + asm( "dc zva, %[fetchptr] \n\t" : : [fetchptr] "r" (base + 256 * 1) : "memory" ); \ + asm( "dc zva, %[fetchptr] \n\t" : : [fetchptr] "r" (base + 256 * 2) : "memory" ); \ } // PREFETCH_RESULT_L1_STORE (prefetch store to L1) #define PREFETCH_RESULT_L1_STORE_INTERNAL_A64FXf(base) \ diff --git a/Grid/simd/Fujitsu_A64FX_undef.h b/Grid/simd/Fujitsu_A64FX_undef.h index 81eec37a..51762a60 100644 --- a/Grid/simd/Fujitsu_A64FX_undef.h +++ b/Grid/simd/Fujitsu_A64FX_undef.h @@ -46,6 +46,7 @@ Author: Nils Meyer #undef MULT_2SPIN_2 #undef MAYBEPERM #undef LOAD_CHI +#undef ZERO_PSI #undef XP_PROJ #undef YP_PROJ #undef ZP_PROJ diff --git a/Grid/simd/gridverter.py b/Grid/simd/gridverter.py deleted file mode 100755 index f00a5019..00000000 --- a/Grid/simd/gridverter.py +++ /dev/null @@ -1,2377 +0,0 @@ -#!/usr/bin/python3 - -import re -import argparse -import sys - -# Grid for A64FX -# -# * should align std::vector to (multiples of) cache block size = 256 bytes - -# place benchmark runtime in cycles here ! -measured_cycles = 690 #1500 #775 #1500 - - -# command line parser -parser = argparse.ArgumentParser(description="Dslash generator.") -parser.add_argument("--single", action="store_true", default="False") -parser.add_argument("--double", action="store_true", default="True") -parser.add_argument("--debug", action="store_true", default="False") -parser.add_argument("--gridbench", action="store_true", default="False") -args = parser.parse_args() - -print(args) - -ASM_LOAD_CHIMU = True # load chimu -ASM_LOAD_GAUGE = True # load gauge -ASM_LOAD_TABLE = True # load table -ASM_STORE = True # store result - -# Disable all loads and stores in asm for benchmarking purposes -#DISABLE_ASM_LOAD_STORE = True -DISABLE_ASM_LOAD_STORE = False - -if DISABLE_ASM_LOAD_STORE: - ASM_LOAD_CHIMU = True # load chimu - ASM_LOAD_GAUGE = True # load gauge - ASM_LOAD_TABLE = True # load table - ASM_STORE = False # store result - -# Alternative implementation using PROJ specific loads works, -# but be careful with predication - -ALTERNATIVE_LOADS = False -#ALTERNATIVE_LOADS = not ALTERNATIVE_LOADS # True - -# Alternative register mapping, -# must use with my_wilson4.h and my_wilson4pf.h - -ALTERNATIVE_REGISTER_MAPPING = False -#ALTERNATIVE_REGISTER_MAPPING = not ALTERNATIVE_REGISTER_MAPPING - -if ALTERNATIVE_REGISTER_MAPPING == True: - ALTERNATIVE_LOADS = False - -# use movprfx -MOVPRFX = False -MOVPRFX = not MOVPRFX - - -PREFETCH = False -PREFETCH = not PREFETCH # True - -PRECISION = 'double' # DP by default -PRECSUFFIX = 'A64FXd' -if args.single == True: - PRECISION = 'single' - PRECSUFFIX = 'A64FXf' - -_DEBUG = False #True # insert debugging output -if args.debug == True: - _DEBUG = True - -GRIDBENCH = False -if args.gridbench == True: - GRIDBENCH = True - -print("PRECISION = ", PRECISION) -print("DEBUG = ", _DEBUG) -print("ALTERNATIVE_LOADS = ", ALTERNATIVE_LOADS) -print("ALTERNATIVE_REGISTER_MAPPING = ", ALTERNATIVE_REGISTER_MAPPING) -print("MOVPRFX = ", MOVPRFX) -print("DISABLE_ASM_LOAD_STORE = ", DISABLE_ASM_LOAD_STORE) -print("GRIDBENCH = ", GRIDBENCH) - -print("") - -#sys.exit(0) - - -#_DEBUG = True # insert debugging output - -FETCH_BASE_PTR_COLOR_OFFSET = 2 # offset for scalar plus signed immediate addressing -STORE_BASE_PTR_COLOR_OFFSET = 2 - -# 64-bit gp register usage !!! armclang 20.0 complains about the register choice !!! -# table address: x30 -# data address: x29 -# store address: x28 -# debug address: r8 - -# Max performance of complex FMA using FCMLA instruction -# is 25% peak. -# -# Issue latency of FCMLA is 2 cycles. -# Need 2 FCMLA instructions for complex FMA. -# Complete complex FMA takes 4 cycles. -# Peak throughput is 4 * 8 Flops DP = 32 Flops DP in 4 cycles. -# A64FX FMA throughput is 4 * 8 * 2 * 2 = 132 Flops DP in 4 cycles. -# -> 25% peak FMA -# -# In: 3x 512 bits = 192 bytes -# Out: 1x 512 bits = 64 bytes -# Tot: 4x 512 bits = 256 bytes -# -# 256 bytes * 2.2 GHz = 563.2 GB/s (base 10), 524 GB/s (base 2) - -OPT = """ -* interleave prefetching and compute in MULT_2SPIN -* could test storing U's in MULT_2SPIN to L1d for cache line update -* structure reordering: MAYBEPERM after MULT_2SPIN ? -""" - -filename = 'XXX' -LEGAL = """/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: {} - - Copyright (C) 2020 - -Author: Nils Meyer - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - See the full license in the file "LICENSE" in the top level distribution directory -*************************************************************************************/ -/* END LEGAL */ -""" - -class Register: - - def __init__(self, variable, asmreg='X', predication=False): - global d - x = 'Y' - if predication == False: - x = asmreg # + d['asmsuffix'] - else: - x = asmreg - self.asmreg = x - self.asmregwithsuffix = asmreg + d['asmsuffix'] - self.asmregbyte = asmreg + '.b' - self.name = variable - self.asmname = variable - self.asmnamebyte = variable + '.b' - self.predication = predication - - d['registers'] += 1 - - def define(self, statement): - global d - d['C'] += F'#define {self.name} {statement}' - #d['A'] += F'#define {self.name} {statement}' - - def declare(self, predication=False): - global d - - if self.predication == False: - d['C'] += F' Simd {self.name}; \\\n' - - predtype = 'svfloat64_t' - if PRECISION == 'single': - predtype = 'svfloat32_t' - - d['I'] += F' {predtype} {self.name}; \\\n' - else: - d['I'] += F' svbool_t {self.name}; \\\n' - #d['A'] += F'#define {self.name} {self.asmreg} \n' - - def loadpredication(self, target='A'): - global d - if (target == 'A'): - d['A'] += F' "ptrue {self.asmregwithsuffix} \\n\\t" \\\n' - d['asmclobber'].append(F'"{self.asmreg}"') - - def loadtable(self, t): - global d - d['load'] += d['factor'] - gpr = d['asmtableptr'] - - cast = 'uint64_t' - #asm_opcode = 'ld1d' - #if PRECISION == 'single': - # asm_opcode = 'ld1w' - # cast = 'uint32_t' - asm_opcode = 'ldr' - if PRECISION == 'single': - asm_opcode = 'ldr' - cast = 'uint32_t' - - d['I'] += F' {self.name} = svld1(pg1, ({cast}*)&lut[{t}]); \\\n' - - # using immediate index break-out works - if asm_opcode == 'ldr': - # ldr version - d['A'] += F' "{asm_opcode} {self.asmreg}, [%[tableptr], %[index], mul vl] \\n\\t" \\\n' - else: - # ld1 version - d['A'] += F' "{asm_opcode} {{ {self.asmregwithsuffix} }}, {pg1.asmreg}/z, [%[tableptr], %[index], mul vl] \\n\\t" \\\n' - - d['asminput'].append(F'[tableptr] "r" (&lut[0])') - d['asminput'].append(F'[index] "i" ({t})') - d['asmclobber'].append(F'"memory"') - d['asmclobber'].append(F'"cc"') - - def load(self, address, target='ALL', cast='float64_t', colors=3, offset=FETCH_BASE_PTR_COLOR_OFFSET): - global d - d['load'] += d['factor'] - indices = re.findall(r'\d+', address) - index = (int(indices[0]) - offset) * colors + int(indices[1]) - - #asm_opcode = 'ld1d' - #if PRECISION == 'single': - #asm_opcode = 'ld1w' - # cast = 'float32_t' - - asm_opcode = 'ldr' - if PRECISION == 'single': - asm_opcode = 'ldr' - cast = 'float32_t' - - gpr = d['asmfetchbaseptr'] - intrinfetchbase = d['intrinfetchbase'] - if (target in ['ALL', 'C']): - d['C'] += F' {self.name} = {address}; \\\n' - if (target in ['ALL', 'I']): -# d['I'] += F' {self.name} = svldnt1(pg1, ({cast}*)({intrinfetchbase} + {index} * 64)); \\\n' - d['I'] += F' {self.name} = svld1(pg1, ({cast}*)({intrinfetchbase} + {index} * 64)); \\\n' - if (target in ['ALL', 'A']): - if asm_opcode == 'ldr': - d['A'] += F' "{asm_opcode} {self.asmreg}, [%[fetchptr], {index}, mul vl] \\n\\t" \\\n' - else: - d['A'] += F' "{asm_opcode} {{ {self.asmregwithsuffix} }}, {pg1.asmreg}/z, [%[fetchptr], {index}, mul vl] \\n\\t" \\\n' - - def store(self, address, cast='float64_t', colors=3, offset=STORE_BASE_PTR_COLOR_OFFSET): - global d - d['store'] += d['factor'] - indices = re.findall(r'\d+', address) - index = (int(indices[0]) - offset) * colors + int(indices[1]) - - #asm_opcode = 'stnt1d' - #if PRECISION == 'single': - # asm_opcode = 'stnt1w' - # cast = 'float32_t' - asm_opcode = 'str' - if PRECISION == 'single': - asm_opcode = 'str' - cast = 'float32_t' - - intrinstorebase = d['intrinstorebase'] - - d['C'] += F' {address} = {self.name}; \\\n' - #d['I'] += F' svstnt1(pg1, ({cast}*)({intrinstorebase} + {index} * 64), {self.name}); \\\n' - d['I'] += F' svst1(pg1, ({cast}*)({intrinstorebase} + {index} * 64), {self.name}); \\\n' - if asm_opcode == 'str': - d['A'] += F' "{asm_opcode} {self.asmreg}, [%[storeptr], {index}, mul vl] \\n\\t" \\\n' - else: - d['A'] += F' "{asm_opcode} {{ {self.asmregwithsuffix} }}, {pg1.asmreg}, [%[storeptr], {index}, mul vl] \\n\\t" \\\n' - - def movestr(self, str): - global d - #d['move'] += d['factor'] - d['I'] += F' {self.name} = {str}; \\\n' - - def move(self, op1): - global d - d['move'] += d['factor'] - d['C'] += F' {self.name} = {op1.name}; \\\n' - d['I'] += F' {self.name} = {op1.name}; \\\n' - d['A'] += F' "mov {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix} \\n\\t" \\\n' - - # a = a + b , a = b + c - def add(self, op1, op2=None): - global d - d['add'] += d['factor'] - if op2 is None: - d['C'] += F' {self.name} = {self.name} + {op1.name}; \\\n' - d['I'] += F' {self.name} = svadd_x(pg1, {self.name}, {op1.name}); \\\n' - d['A'] += F' "fadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {self.asmregwithsuffix}, {op1.asmregwithsuffix} \\n\\t" \\\n' - else: - d['C'] += F' {self.name} = {op1.name} + {op2.name}; \\\n' - d['I'] += F' {self.name} = svadd_x(pg1, {op1.name}, {op2.name}); \\\n' - d['A'] += F' "fadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix} \\n\\t" \\\n' - - # a = a -b , a = b - c - def sub(self, op1, op2=None): - global d - d['sub'] += d['factor'] - if op2 is None: - d['C'] += F' {self.name} = {self.name} - {op1.name}; \\\n' - d['I'] += F' {self.name} = svsub_x(pg1, {self.name}, {op1.name}); \\\n' - d['A'] += F' "fsub {self.asmregwithsuffix}, {pg1.asmreg}/m, {self.asmregwithsuffix}, {op1.asmregwithsuffix} \\n\\t" \\\n' - else: - d['C'] += F' {self.name} = {op1.name} - {op2.name}; \\\n' - d['I'] += F' {self.name} = svsub_x(pg1, {op1.name}, {op2.name}); \\\n' - d['A'] += F' "fsub {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix} \\n\\t" \\\n' - - # a = a * b , a = b * c - def mul(self, op1, op2): - global d - d['mul'] += 2 * d['factor'] - d['C'] += F' {self.name} = {op1.name} * {op2.name}; \\\n' - d['I'] += F' {self.name} = __svzero({self.name}); \\\n' - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 0); \\\n' - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 90); \\\n' - d['A'] += F' "mov {self.asmregwithsuffix} , 0 \\n\\t" \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 0 \\n\\t" \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 90 \\n\\t" \\\n' - - def mul0(self, op1, op2, op3=None, constructive=False): - global d - d['mul'] += d['factor'] - - # no movprfx intrinsics support - if constructive == True: - d['movprfx'] += d['factor'] - d['I'] += F' {self.name} = svcmla_x(pg1, {op1.name}, {op2.name}, {op3.name}, 0); \\\n' - d['A'] += F' "movprfx {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix} \\n\\t" \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op2.asmregwithsuffix}, {op3.asmregwithsuffix}, 0 \\n\\t" \\\n' - else: - d['C'] += F' {self.name} = {op1.name} * {op2.name}; \\\n' - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 0); \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 0 \\n\\t" \\\n' - - def mul1(self, op1, op2): - global d - d['mul'] += d['factor'] - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 90); \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 90 \\n\\t" \\\n' - - def mac(self, op1, op2): - global d - d['mac'] += 2 * d['factor'] - d['C'] += F' {self.name} = {self.name} + {op1.name} * {op2.name}; \\\n' - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 0); \\\n' - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 90); \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 0 \\n\\t" \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 90 \\n\\t" \\\n' - - def mac0(self, op1, op2): - global d - d['mac'] += d['factor'] - d['C'] += F' {self.name} = {self.name} + {op1.name} * {op2.name}; \\\n' - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 0); \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 0 \\n\\t" \\\n' - - def mac1(self, op1, op2): - global d - d['mac'] += d['factor'] - d['I'] += F' {self.name} = svcmla_x(pg1, {self.name}, {op1.name}, {op2.name}, 90); \\\n' - d['A'] += F' "fcmla {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 90 \\n\\t" \\\n' - - def zero(self, zeroreg=False): - d['zero'] += d['factor'] - d['C'] += F' {self.name} = 0; \\\n' - #d['I'] += F' {self.name} = __svzero({self.name}); \\\n' only armclang - - if PRECISION == 'double': - d['I'] += F' {self.name} = svdup_f64(0.); \\\n' - else: - d['I'] += F' {self.name} = svdup_f32(0.); \\\n' - - if zeroreg == True: - d['A'] += F' "fmov {self.asmregwithsuffix} , 0 \\n\\t" \\\n' - else: - #using mov z, zero0 issue 1c, FLA, latency 6c - #d['A'] += F' "mov {self.asmregwithsuffix} , {zero0.asmregwithsuffix} \\n\\t" \\\n' - - #using mov z, 0 issue 1c, FLA, latency 6c - d['A'] += F' "fmov {self.asmregwithsuffix} , 0 \\n\\t" \\\n' - - #using xor z, z, z issue 0.5c, FL*, latency 4c - #d['A'] += F' "eor {self.asmregwithsuffix}, {pg1.asmreg}/m, {self.asmregwithsuffix}, {self.asmregwithsuffix} \\n\\t" \\\n' - - #using and z, z, zero0 issue 0.5c, FL*, latency 4c - #d['A'] += F' "and {self.asmregwithsuffix}, {self.asmregwithsuffix} , {zero0.asmregwithsuffix} \\n\\t" \\\n' - - #using sub z, z, z issue 0.5c, FL*, latency 9c - #d['A'] += F' "sub {self.asmregwithsuffix}, {self.asmregwithsuffix}, {self.asmregwithsuffix} \\n\\t" \\\n' - - # without table - def timesI(self, op1, tempreg=None, tablereg=None): - global d - d['timesI'] += d['factor'] - d['C'] += F' {self.name} = timesI({op1.name}); \\\n' - # correct if DEBUG enabled, wrong if DEBUG disabled; no idea what's causing this - #table.load('table2', target='I', cast='uint64_t') - #d['I'] += F' {self.name} = svtbl({op1.name}, {tablereg.name}); \\\n' - #d['I'] += F' {self.name} = svneg_x(pg2, {self.name}); \\\n' - # timesI using trn tested, works but tbl should be faster - d['I'] += F' {tempreg.name} = svtrn2({op1.name}, {op1.name}); \\\n' - d['I'] += F' {tempreg.name} = svneg_x(pg1, {tempreg.name}); \\\n' - d['I'] += F' {self.name} = svtrn1({tempreg.name}, {op1.name}); \\\n' - d['A'] += F' "trn2 {tempreg.asmregwithsuffix}, {op1.asmregwithsuffix}, {op1.asmregwithsuffix} \\n\\t" \\\n' - d['A'] += F' "fneg {tempreg.asmregwithsuffix}, {pg1.asmreg}/m, {tempreg.asmregwithsuffix} \\n\\t" \\\n' - d['A'] += F' "trn1 {self.asmregwithsuffix}, {tempreg.asmregwithsuffix}, {op1.asmregwithsuffix} \\n\\t" \\\n' - - def addTimesI(self, op1, op2=None, constructive=False): - global d - d['addTimesI'] += d['factor'] - - if op2 is None: - d['C'] += F' {self.name} = {self.name} + timesI({op1.name}); \\\n' - else: - d['C'] += F' {self.name} = {op1.name} + timesI({op2.name}); \\\n' - - # no movprfx intrinsics support - if constructive == True: - d['movprfx'] += d['factor'] - d['I'] += F' {self.name} = svcadd_x(pg1, {op1.name}, {op2.name}, 90); \\\n' - d['A'] += F' "movprfx {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix} \\n\\t" \\\n' - d['A'] += F' "fcadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {self.asmregwithsuffix}, {op2.asmregwithsuffix}, 90 \\n\\t" \\\n' - else: - if op2 is None: - d['C'] += F' {self.name} = {self.name} + timesI({op1.name}); \\\n' - d['I'] += F' {self.name} = svcadd_x(pg1, {self.name}, {op1.name}, 90); \\\n' - d['A'] += F' "fcadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {self.asmregwithsuffix}, {op1.asmregwithsuffix}, 90 \\n\\t" \\\n' - else: - d['C'] += F' {self.name} = {op1.name} + timesI({op2.name}); \\\n' - d['I'] += F' {self.name} = svcadd_x(pg1, {op1.name}, {op2.name}, 90); \\\n' - d['A'] += F' "fcadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 90 \\n\\t" \\\n' - - def subTimesI(self, op1, op2=None, constructive=False): - global d - d['subTimesI'] += d['factor'] - - # no movprfx intrinsics support - if constructive == True: - d['movprfx'] += d['factor'] - d['I'] += F' {self.name} = svcadd_x(pg1, {op1.name}, {op2.name}, 270); \\\n' - d['A'] += F' "movprfx {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix} \\n\\t" \\\n' - d['A'] += F' "fcadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {self.asmregwithsuffix}, {op2.asmregwithsuffix}, 270 \\n\\t" \\\n' - else: - if op2 is None: - d['C'] += F' {self.name} = {self.name} - timesI({op1.name}); \\\n' - d['I'] += F' {self.name} = svcadd_x(pg1, {self.name}, {op1.name}, 270); \\\n' - d['A'] += F' "fcadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {self.asmregwithsuffix}, {op1.asmregwithsuffix}, 270 \\n\\t" \\\n' - else: - d['C'] += F' {self.name} = {op1.name} - timesI({op2.name}); \\\n' - d['I'] += F' {self.name} = svcadd_x(pg1, {op1.name}, {op2.name}, 270); \\\n' - d['A'] += F' "fcadd {self.asmregwithsuffix}, {pg1.asmreg}/m, {op1.asmregwithsuffix}, {op2.asmregwithsuffix}, 270 \\n\\t" \\\n' - - # timesMinusI is not used, def is probably wrong !!!! OPTIMIZATION with table - def timesMinusI(self, op1): - global d - d['timesMinusI'] += d['factor'] - d['C'] += F' {self.name} = timesMinusI({self.name}); \\\n' - d['I'] += F' {self.name} = svtrn1({op1.name}, {op1.name}); \\\n' - d['I'] += F' {self.name} = svneg_x(pg1, {self.name}); \\\n' - d['I'] += F' {self.name} = svtrn1({op1.name}, {self.name}); \\\n' - - def permute(self, dir, tablereg=None): - global d - d['permutes'] += d['factor'] - - d['C'] += F' permute{dir}({self.name}, {self.name}); \\\n' - - d['I'] += F' {self.name} = svtbl({self.name}, {tablereg.name}); \\\n' - d['A'] += F' "tbl {self.asmregwithsuffix}, {{ {self.asmregwithsuffix} }}, {tablereg.asmregwithsuffix} \\n\\t" \\\n' - - # if dir == 0: - # d['I'] += F' {self.name} = svext({self.name}, {self.name}, 4); \\\n' - # # this might not work, see intrinsics assembly - # # d['A'] += F' ext {self.name}, {self.name}, {self.name}, #4 \\\n' - # # use registers directly - # d['A'] += F' "ext {self.asmregbyte}, {self.asmregbyte}, {self.asmregbyte}, 32 \\n\\t" \\\n' - # - # elif dir in [1, 2]: - # d['I'] += F' {self.name} = svtbl({self.name}, {tablereg.name}); \\\n' - # d['A'] += F' "tbl {self.asmregwithsuffix}, {{ {self.asmregwithsuffix} }}, {tablereg.asmregwithsuffix} \\n\\t" \\\n' - - def debug(self): - global d - typecast = d['cfloat'] - gpr = d['asmdebugptr'] - vregs = d['asmclobberlist'] - if (d['debug'] == True): - d['C'] += F'std::cout << "{self.name} -- " << {self.name} << std::endl; \\\n' - - d['I'] += F'svst1(pg1, ({typecast}*)&debugreg.v, {self.name}); \\\n' - d['I'] += F'std::cout << "{self.name} -- " << debugreg << std::endl; \\\n' - #d['I'] += F'std::cout << "{self.name} -- " << {self.name} << std::endl; \\\n' - - d['A'] += F'asm ( \\\n' - d['A'] += F' " DMB SY \\n\\t " " DSB SY \\n\\t " " ISB SY \\n\\t " \\\n' # memory barrier - d['A'] += F' "str {self.asmreg}, [%[ptr]] \\n\\t" \\\n' - d['A'] += F' " DMB SY \\n\\t " " DSB SY \\n\\t " " ISB SY \\n\\t " \\\n' # memory barrier - d['A'] += F' : "=m" (debugreg.v) \\\n' - d['A'] += F' : [ptr] "r" (&debugreg.v) \\\n' - d['A'] += F' : "p5", "cc", "memory" \\\n' - d['A'] += F'); \\\n' - d['A'] += F'std::cout << "{self.name} -- " << debugreg << std::endl; \\\n' - # this form of addressing is not valid! - #d['A'] += F' "str {self.asmreg}, %[ptr] \\n\\t" \\\n' -# end Register - -def define(s, target='ALL'): - x = F'#define {s} \n' - global d - if (target in ['ALL', 'C']): - d['C'] += x - if (target in ['ALL', 'I']): - d['I'] += x - if (target in ['ALL', 'A']): - d['A'] += x - -def definemultiline(s): - x = F'#define {s} \\\n' - global d - d['C'] += x - d['I'] += x - d['A'] += x - -def write(s, target='ALL'): - x = F'{s}\n' - global d - if (target in ['ALL', 'C']): - d['C'] += x - if (target in ['ALL', 'I']): - d['I'] += x - if (target in ['ALL', 'A']): - d['A'] += x - -def curlyopen(): - write(F'{{ \\') - -def curlyclose(): - write(F'}}') - -def newline(target='ALL'): - global d - - if target == 'A': - if d['A'][-2:] == '\\\n': - d['A'] = d['A'][:-2] + '\n\n' - else: - if d['C'][-2:] == '\\\n': - d['C'] = d['C'][:-2] + '\n\n' - if d['I'][-2:] == '\\\n': - d['I'] = d['I'][:-2] + '\n\n' - if d['A'][-2:] == '\\\n': - d['A'] = d['A'][:-2] + '\n\n' - -# load the base pointer for fetches -def fetch_base_ptr(address, target='A'): - global d - #d['load'] += d['factor'] - - # DEBUG - #colors=3 - #indices = re.findall(r'\d+', address) - #index = (int(indices[0]) - FETCH_BASE_PTR_COLOR_OFFSET) * colors + int(indices[1]) - #print(F'{address} (base)') - - vregs = d['asmclobberlist'] - if target == 'A': - d['asminput'].append(F'[fetchptr] "r" ({address})') - d['asmclobber'].extend(vregs) - d['asmclobber'].append(F'"memory"') - d['asmclobber'].append(F'"cc"') - if target == 'I': - #print("intrinfetchbase = ", address) - d['intrinfetchbase'] = address - -# load the base pointer for stores -def store_base_ptr(address, target='A'): - global d - #d['load'] += d['factor'] - gpr = d['asmstorebaseptr'] - vregs = d['asmclobberlist'] - if target == 'A': - d['asminput'].append(F'[storeptr] "r" ({address})') - d['asmclobber'].extend(vregs) - d['asmclobber'].append(F'"memory"') - d['asmclobber'].append(F'"cc"') - if target == 'I': - d['intrinstorebase'] = address - -def prefetch_L1(address, offset): - global d - multiplier = 4 # offset in CL, have to multiply by 4 - policy = "PLDL1STRM" # weak - #policy = "PLDL1KEEP" # strong - - d['I'] += F' svprfd(pg1, (int64_t*)({address} + {offset * multiplier * 64}), SV_{policy}); \\\n' - d['A'] += F' "prfd {policy}, {pg1.asmreg}, [%[fetchptr], {offset * multiplier}, mul vl] \\n\\t" \\\n' - -def prefetch_L2(address, offset): - global d - multiplier = 4 # offset in CL, have to multiply by 4 - policy = "PLDL2STRM" # weak - #policy = "PLDL2KEEP" # strong - - d['I'] += F' svprfd(pg1, (int64_t*)({address} + {offset * multiplier * 64}), SV_{policy}); \\\n' - d['A'] += F' "prfd {policy}, {pg1.asmreg}, [%[fetchptr], {offset * multiplier}, mul vl] \\n\\t" \\\n' - #d['A'] += - -def prefetch_L2_store(address, offset): - global d - multiplier = 4 # offset in CL, have to multiply by 4 - policy = "PSTL2STRM" # weak - #policy = "PSTL2KEEP" # strong - - d['I'] += F' svprfd(pg1, (int64_t*)({address} + {offset * multiplier * 64}), SV_{policy}); \\\n' - d['A'] += F' "prfd {policy}, {pg1.asmreg}, [%[fetchptr], {offset * multiplier}, mul vl] \\n\\t" \\\n' - -def prefetch_L1_store(address, offset): - global d - multiplier = 4 # offset in CL, have to multiply by 4 - policy = "PSTL1STRM" # weak - #policy = "PSTL2KEEP" # strong - - d['I'] += F' svprfd(pg1, (int64_t*)({address} + {offset * multiplier * 64}), SV_{policy}); \\\n' - d['A'] += F' "prfd {policy}, {pg1.asmreg}, [%[fetchptr], {offset * multiplier}, mul vl] \\n\\t" \\\n' - - -def asmopen(): - #write('asm volatile ( \\', target='A') - write('asm ( \\', target='A') - - # DEBUG - #write(F' " DMB SY \\n\\t " " DSB SY \\n\\t " " ISB SY \\n\\t " \\', target='A') # memory barrier - #write('asm volatile ( \\', target='A') - -def asmclose(): - global d - - #print(d['asminput']) - - asmin = d['asminput'] - asmin_s = '' - if len(asmin) > 0: - asmin = list(dict.fromkeys(asmin)) # remove duplicates - #print(asmin) - for el in asmin: - asmin_s += el + ',' - asmin_s = asmin_s[:-1] - #print("-> ", asmin_s) - - d['asminput'] = [] - - asmout = d['asmoutput'] - asmout_s = '' - if len(asmout) > 0: - asmout = list(dict.fromkeys(asmout)) # remove duplicates - for el in asmout: - asmout_s += el + ',' - asmout_s = asmout_s[:-1] - - d['asmoutput'] = [] - - # DEBUG put all regs into clobber by default - d['asmclobber'].extend(d['asmclobberlist']) - - asmclobber = d['asmclobber'] - asmclobber_s = '' - #print(asmclobber) - if len(asmclobber) > 0: - asmclobber = list(dict.fromkeys(asmclobber)) # remove duplicates - for el in asmclobber: - asmclobber_s += el + ',' - asmclobber_s = asmclobber_s[:-1] - - d['asmclobber'] = [] - - # DEBUG - #write(F' " DMB SY \\n\\t " " DSB SY \\n\\t " " ISB SY \\n\\t " \\', target='A') # memory barrier - - - write(F' : {asmout_s} \\', target='A') - write(F' : {asmin_s} \\', target='A') - write(F' : {asmclobber_s} \\', target='A') - write('); \\', target='A') - -# -------------------------------------------------------------------------------- - -# string of vector registers to be used in clobber list -#clobberlist = ['"p0"'] -clobberlist = ['"p5"'] -clobberlist.append('"cc"') -for i in range(0, 32): - clobberlist.append(F'"z{i}"') - -d = { -'debug': _DEBUG, -'C': '', -'I': '', -'A': '', -'asmsuffix': '.d', # double precision by default -'cfloat': 'float64_t', -'registers': 0, -'load': 0, -'store': 0, -'move': 0, -'movprfx': 0, -'zero': 0, -'add': 0, -'sub': 0, -'mul': 0, -'mac': 0, -'permutes': 0, -'neg': 0, -'addTimesI': 0, -'subTimesI': 0, -'timesI': 0, -'timesMinusI': 0, -'flops': 0, -'factor': 1, # multiplicity -'asmtableptr': 'x30', -'asmfetchbaseptr': 'x29', -'asmstorebaseptr': 'x28', -'asmdebugptr': 'r12', -'asminput': [], -'asmoutput': [], -'asmclobber': [], -'asmclobberlist': clobberlist, -'intrinfetchbase': '', -'intrinstorebase': '', -'cycles_LOAD_CHIMU': 0, -'cycles_PROJ': 0, -'cycles_PERM': 0, -'cycles_MULT_2SPIN': 0, -'cycles_RECON': 0, -'cycles_RESULT': 0, -'cycles_ZERO_PSI': 0, -'cycles_PREFETCH_L1': 0, -'cycles_PREFETCH_L2': 0 -} - -if PRECISION == 'single': - d['asmsuffix'] = '.s' - d['cfloat'] = 'float32_t' - -# -------------------------------------------------------------------------------- -# Grid -# -------------------------------------------------------------------------------- - -# Variables / Registers -result_00 = Register('result_00', asmreg='z0') -result_01 = Register('result_01', asmreg='z1') -result_02 = Register('result_02', asmreg='z2') -result_10 = Register('result_10', asmreg='z3') -result_11 = Register('result_11', asmreg='z4') -result_12 = Register('result_12', asmreg='z5') -result_20 = Register('result_20', asmreg='z6') -result_21 = Register('result_21', asmreg='z7') -result_22 = Register('result_22', asmreg='z8') -result_30 = Register('result_30', asmreg='z9') -result_31 = Register('result_31', asmreg='z10') -result_32 = Register('result_32', asmreg='z11') # 12 Regs -Chi_00 = Register('Chi_00', asmreg='z12') -Chi_01 = Register('Chi_01', asmreg='z13') -Chi_02 = Register('Chi_02', asmreg='z14') -Chi_10 = Register('Chi_10', asmreg='z15') -Chi_11 = Register('Chi_11', asmreg='z16') -Chi_12 = Register('Chi_12', asmreg='z17') # 6 -UChi_00 = Register('UChi_00', asmreg='z18') -UChi_01 = Register('UChi_01', asmreg='z19') -UChi_02 = Register('UChi_02', asmreg='z20') -UChi_10 = Register('UChi_10', asmreg='z21') -UChi_11 = Register('UChi_11', asmreg='z22') -UChi_12 = Register('UChi_12', asmreg='z23') # 6 -U_00 = Register('U_00', asmreg='z24') -U_10 = Register('U_10', asmreg='z25') -U_20 = Register('U_20', asmreg='z26') -U_01 = Register('U_01', asmreg='z27') -U_11 = Register('U_11', asmreg='z28') -U_21 = Register('U_21', asmreg='z29') # 6 -> 30 Registers - -table0 = Register('table0', asmreg='z30') -zero0 = Register('zero0', asmreg='z31') # 2 -> 32 Registers -# can't overload temp1 / table due to type mismatch using intrinsics :( -# typecasting SVE intrinsics variables is not allowed - -pg1 = Register('pg1', predication=True, asmreg='p5') -#pg2 = Register('pg2', predication=True, asmreg='p1') - -# Overloaded with Chi_* and UChi_* -Chimu_00 = Register('Chimu_00', asmreg=Chi_00.asmreg) -Chimu_01 = Register('Chimu_01', asmreg=Chi_01.asmreg) -Chimu_02 = Register('Chimu_02', asmreg=Chi_02.asmreg) -Chimu_10 = Register('Chimu_10', asmreg=Chi_10.asmreg) -Chimu_11 = Register('Chimu_11', asmreg=Chi_11.asmreg) -Chimu_12 = Register('Chimu_12', asmreg=Chi_12.asmreg) -if ALTERNATIVE_REGISTER_MAPPING == False: - Chimu_20 = Register('Chimu_20', asmreg=UChi_00.asmreg) - Chimu_21 = Register('Chimu_21', asmreg=UChi_01.asmreg) - Chimu_22 = Register('Chimu_22', asmreg=UChi_02.asmreg) - Chimu_30 = Register('Chimu_30', asmreg=UChi_10.asmreg) - Chimu_31 = Register('Chimu_31', asmreg=UChi_11.asmreg) - Chimu_32 = Register('Chimu_32', asmreg=UChi_12.asmreg) # 12 Registers -else: # wilson4.h - Chimu_20 = Register('Chimu_20', asmreg=U_00.asmreg) - Chimu_21 = Register('Chimu_21', asmreg=U_10.asmreg) - Chimu_22 = Register('Chimu_22', asmreg=U_20.asmreg) - Chimu_30 = Register('Chimu_30', asmreg=U_01.asmreg) - Chimu_31 = Register('Chimu_31', asmreg=U_11.asmreg) - Chimu_32 = Register('Chimu_32', asmreg=U_21.asmreg) - -# debugging output -def debugall(msg=None, group='ALL'): - global d - if (d['debug'] == False): - return - write(F'std::cout << std::endl << "DEBUG -- {msg}" << std::endl; \\') - if (group in ['ALL', 'result']): - result_00.debug() - result_01.debug() - result_02.debug() - result_10.debug() - result_11.debug() - result_12.debug() - result_20.debug() - result_21.debug() - result_22.debug() - result_30.debug() - result_31.debug() - result_32.debug() - if (group in ['ALL', 'Chi']): - Chi_00.debug() - Chi_01.debug() - Chi_02.debug() - Chi_10.debug() - Chi_11.debug() - Chi_12.debug() - if (group in ['ALL', 'UChi']): - UChi_00.debug() - UChi_01.debug() - UChi_02.debug() - UChi_10.debug() - UChi_11.debug() - UChi_12.debug() - if (group in ['ALL', 'U']): - U_00.debug() - U_10.debug() - U_20.debug() - U_01.debug() - U_11.debug() - U_21.debug() - if (group in ['ALL', 'Chimu']): - Chimu_00.debug() - Chimu_01.debug() - Chimu_02.debug() - Chimu_10.debug() - Chimu_11.debug() - Chimu_12.debug() - Chimu_20.debug() - Chimu_21.debug() - Chimu_22.debug() - Chimu_30.debug() - Chimu_31.debug() - Chimu_32.debug() - -# -------------------------------------------------------------------------------- -# Output -# -------------------------------------------------------------------------------- - -if ALTERNATIVE_LOADS == True: - define(F'LOAD_CHIMU_0213_PLUG LOAD_CHIMU_0213_{PRECSUFFIX}') - define(F'LOAD_CHIMU_0312_PLUG LOAD_CHIMU_0312_{PRECSUFFIX}') - define(F'LOAD_CHIMU(x)') -else: - #define(F'LOAD_CHIMU_{PRECSUFFIX}(x) LOAD_CHIMU_INTERLEAVED_{PRECSUFFIX}(x)') - define(F'LOAD_CHIMU(base) LOAD_CHIMU_INTERLEAVED_{PRECSUFFIX}(base)') - -if PREFETCH: - define(F'PREFETCH_CHIMU_L1(A) PREFETCH_CHIMU_L1_INTERNAL_{PRECSUFFIX}(A)') - define(F'PREFETCH_GAUGE_L1(A) PREFETCH_GAUGE_L1_INTERNAL_{PRECSUFFIX}(A)') - define(F'PREFETCH_CHIMU_L2(A) PREFETCH_CHIMU_L2_INTERNAL_{PRECSUFFIX}(A)') - define(F'PREFETCH_GAUGE_L2(A) PREFETCH_GAUGE_L2_INTERNAL_{PRECSUFFIX}(A)') - define(F'PF_GAUGE(A)') - define(F'PREFETCH_RESULT_L2_STORE(A) PREFETCH_RESULT_L2_STORE_INTERNAL_{PRECSUFFIX}(A)') - define(F'PREFETCH_RESULT_L1_STORE(A) PREFETCH_RESULT_L1_STORE_INTERNAL_{PRECSUFFIX}(A)') - define(F'PREFETCH1_CHIMU(A) PREFETCH_CHIMU_L1(A)') -# define(F'PREFETCH1_CHIMU(A)') - define(F'PREFETCH_CHIMU(A) PREFETCH_CHIMU_L1(A)') -# define(F'PREFETCH_CHIMU(A)') -else: - define(F'PREFETCH_CHIMU_L1(A)') - define(F'PREFETCH_GAUGE_L1(A)') - define(F'PREFETCH_CHIMU_L2(A)') - define(F'PREFETCH_GAUGE_L2(A)') - define(F'PF_GAUGE(A)') - define(F'PREFETCH1_CHIMU(A)') - define(F'PREFETCH_CHIMU(A)') - define(F'PREFETCH_RESULT_L2_STORE(A)') - -# standard defines -define(F'LOCK_GAUGE(A)') -define(F'UNLOCK_GAUGE(A)') -define(F'MASK_REGS DECLARATIONS_{PRECSUFFIX}') -define(F'SAVE_RESULT(A,B) RESULT_{PRECSUFFIX}(A); PREFETCH_RESULT_L2_STORE(B)') -define(F'MULT_2SPIN_1(Dir) MULT_2SPIN_1_{PRECSUFFIX}(Dir)') -define(F'MULT_2SPIN_2 MULT_2SPIN_2_{PRECSUFFIX}') -define(F'LOAD_CHI(base) LOAD_CHI_{PRECSUFFIX}(base)') -# don't need zero psi, everything is done in recons -#define(F'ZERO_PSI ZERO_PSI_{PRECSUFFIX}') -define(F'ADD_RESULT(base,basep) LOAD_CHIMU(base); ADD_RESULT_INTERNAL_{PRECSUFFIX}; RESULT_{PRECSUFFIX}(base)') -# loads projections -define(F'XP_PROJ XP_PROJ_{PRECSUFFIX}') -define(F'YP_PROJ YP_PROJ_{PRECSUFFIX}') -define(F'ZP_PROJ ZP_PROJ_{PRECSUFFIX}') -define(F'TP_PROJ TP_PROJ_{PRECSUFFIX}') -define(F'XM_PROJ XM_PROJ_{PRECSUFFIX}') -define(F'YM_PROJ YM_PROJ_{PRECSUFFIX}') -define(F'ZM_PROJ ZM_PROJ_{PRECSUFFIX}') -define(F'TM_PROJ TM_PROJ_{PRECSUFFIX}') -# recons -define(F'XP_RECON XP_RECON_{PRECSUFFIX}') -define(F'XM_RECON XM_RECON_{PRECSUFFIX}') -define(F'XM_RECON_ACCUM XM_RECON_ACCUM_{PRECSUFFIX}') -define(F'YM_RECON_ACCUM YM_RECON_ACCUM_{PRECSUFFIX}') -define(F'ZM_RECON_ACCUM ZM_RECON_ACCUM_{PRECSUFFIX}') -define(F'TM_RECON_ACCUM TM_RECON_ACCUM_{PRECSUFFIX}') -define(F'XP_RECON_ACCUM XP_RECON_ACCUM_{PRECSUFFIX}') -define(F'YP_RECON_ACCUM YP_RECON_ACCUM_{PRECSUFFIX}') -define(F'ZP_RECON_ACCUM ZP_RECON_ACCUM_{PRECSUFFIX}') -define(F'TP_RECON_ACCUM TP_RECON_ACCUM_{PRECSUFFIX}') -# new permutes -define(F'PERMUTE_DIR0 0') -define(F'PERMUTE_DIR1 1') -define(F'PERMUTE_DIR2 2') -define(F'PERMUTE_DIR3 3') -define(F'PERMUTE PERMUTE_{PRECSUFFIX};') -# load table -#define(F'MAYBEPERM(A,perm) if (perm) {{ A ; }}') -if PRECISION == 'double': - define(F'LOAD_TABLE(Dir) if (Dir == 0) {{ LOAD_TABLE0; }} else if (Dir == 1) {{ LOAD_TABLE1; }} else if (Dir == 2) {{ LOAD_TABLE2; }}') - define(F'MAYBEPERM(Dir,perm) if (Dir != 3) {{ if (perm) {{ PERMUTE; }} }}') -else: - define(F'LOAD_TABLE(Dir) if (Dir == 0) {{ LOAD_TABLE0; }} else if (Dir == 1) {{ LOAD_TABLE1 }} else if (Dir == 2) {{ LOAD_TABLE2; }} else if (Dir == 3) {{ LOAD_TABLE3; }}') - define(F'MAYBEPERM(A,perm) if (perm) {{ PERMUTE; }}') - - - -write('// DECLARATIONS') -definemultiline(F'DECLARATIONS_{PRECSUFFIX}') -# debugging register -if d['debug'] == True: - write(' Simd debugreg; \\') -# perm tables -if PRECISION == 'double': - write(' const uint64_t lut[4][8] = { \\') - write(' {4, 5, 6, 7, 0, 1, 2, 3}, \\') #0 = swap register halves - write(' {2, 3, 0, 1, 6, 7, 4, 5}, \\') #1 = swap halves of halves - write(' {1, 0, 3, 2, 5, 4, 7, 6}, \\') #2 = swap re/im - write(' {0, 1, 2, 4, 5, 6, 7, 8} };\\') #3 = identity -else: - write(' const uint32_t lut[4][16] = { \\') - write(' {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}, \\') #0 = swap register halves - write(' {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}, \\') #1 = swap halves of halves - write(' {2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}, \\') #2 = swap halves of halves of halves - write(' {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14} }; \\') #3 = swap re/im - -#newline(target='A') -result_00.declare() -result_01.declare() -result_02.declare() -result_10.declare() -result_11.declare() -result_12.declare() -result_20.declare() -result_21.declare() -result_22.declare() -result_30.declare() -result_31.declare() -result_32.declare() # 12 -Chi_00.declare() -Chi_01.declare() -Chi_02.declare() -Chi_10.declare() -Chi_11.declare() -Chi_12.declare() # 6 -UChi_00.declare() -UChi_01.declare() -UChi_02.declare() -UChi_10.declare() -UChi_11.declare() -UChi_12.declare() # 6 -U_00.declare() -U_10.declare() -U_20.declare() -U_01.declare() -U_11.declare() -U_21.declare() # 6 -> 30 regs - -# all predications true -pg1.declare() -if PRECISION == 'double': - pg1.movestr('svptrue_b64()') -else: - pg1.movestr('svptrue_b32()') - -# tables -if PRECISION == 'double': - write(' svuint64_t table0; \\', target='I') # -> 31 regs -else: - write(' svuint32_t table0; \\', target='I') # -> 31 regs - -zero0.declare() - -# zero register -asmopen() -zero0.zero(zeroreg=True) -asmclose() -newline() - -define('Chimu_00 Chi_00', target='I') -define('Chimu_01 Chi_01', target='I') -define('Chimu_02 Chi_02', target='I') -define('Chimu_10 Chi_10', target='I') -define('Chimu_11 Chi_11', target='I') -define('Chimu_12 Chi_12', target='I') -if ALTERNATIVE_REGISTER_MAPPING == False: - define('Chimu_20 UChi_00', target='I') - define('Chimu_21 UChi_01', target='I') - define('Chimu_22 UChi_02', target='I') - define('Chimu_30 UChi_10', target='I') - define('Chimu_31 UChi_11', target='I') - define('Chimu_32 UChi_12', target='I') -else: # wilson4.h - define('Chimu_20 U_00', target='I') - define('Chimu_21 U_10', target='I') - define('Chimu_22 U_20', target='I') - define('Chimu_30 U_01', target='I') - define('Chimu_31 U_11', target='I') - define('Chimu_32 U_21', target='I') -newline() - - -d['cycles_RESULT'] += 12 -write('// RESULT') -definemultiline(F'RESULT_{PRECSUFFIX}(base)') -if ASM_STORE: - curlyopen() - #write(' SiteSpinor & ref(out[ss]); \\') - asmopen() - #pg1.loadpredication() - #store_base_ptr("&ref[0][0]") - #store_base_ptr(F"&ref[{STORE_BASE_PTR_COLOR_OFFSET}][0]") - store_base_ptr(F"base + {STORE_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='I') - store_base_ptr(F"base + {STORE_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='A') - result_00.store("ref[0][0]") - result_01.store("ref[0][1]") - result_02.store("ref[0][2]") - result_10.store("ref[1][0]") - result_11.store("ref[1][1]") - result_12.store("ref[1][2]") - result_20.store("ref[2][0]") - result_21.store("ref[2][1]") - result_22.store("ref[2][2]") - result_30.store("ref[3][0]") - result_31.store("ref[3][1]") - result_32.store("ref[3][2]") - asmclose() - debugall('RESULT', group='result') - curlyclose() -newline() - -# prefetch spinors from memory into L2 cache -d['factor'] = 0 -d['cycles_PREFETCH_L2'] += 0 * d['factor'] -write('// PREFETCH_CHIMU_L2 (prefetch to L2)') -definemultiline(F'PREFETCH_CHIMU_L2_INTERNAL_{PRECSUFFIX}(base)') -curlyopen() -fetch_base_ptr(F"base") -asmopen() -#pg1.loadpredication() -#fetch_base_ptr(F"&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]") -fetch_base_ptr(F"base", target='A') -prefetch_L2(F"base", 0) -prefetch_L2(F"base", 1) -prefetch_L2(F"base", 2) -asmclose() -curlyclose() -newline() - -# prefetch spinors from memory into L1 cache -d['factor'] = 0 -d['cycles_PREFETCH_L1'] += 0 * d['factor'] -write('// PREFETCH_CHIMU_L1 (prefetch to L1)') -definemultiline(F'PREFETCH_CHIMU_L1_INTERNAL_{PRECSUFFIX}(base)') -curlyopen() -fetch_base_ptr(F"base") -asmopen() -#pg1.loadpredication() -fetch_base_ptr(F"base", target='A') -prefetch_L1(F"base", 0) -prefetch_L1(F"base", 1) -prefetch_L1(F"base", 2) -asmclose() -curlyclose() -newline() - -# prefetch gauge from memory into L2 cache -d['factor'] = 0 -d['cycles_PREFETCH_L2'] += 0 * d['factor'] -write('// PREFETCH_GAUGE_L2 (prefetch to L2)') -definemultiline(F'PREFETCH_GAUGE_L2_INTERNAL_{PRECSUFFIX}(A)') -curlyopen() -if GRIDBENCH: # referencing differs in Grid and GridBench - write(' const auto & ref(U[sUn][A]); uint64_t baseU = (uint64_t)&ref + 3 * 3 * 64; \\') -else: - write(' const auto & ref(U[sUn](A)); uint64_t baseU = (uint64_t)&ref + 3 * 3 * 64; \\') -asmopen() -#pg1.loadpredication() -#fetch_base_ptr(F"&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]") -fetch_base_ptr(F"baseU", target='A') -prefetch_L2(F"baseU", -1) -prefetch_L2(F"baseU", 0) -prefetch_L2(F"baseU", 1) -prefetch_L2(F"baseU", 2) -prefetch_L2(F"baseU", 3) -prefetch_L2(F"baseU", 4) -prefetch_L2(F"baseU", 5) -prefetch_L2(F"baseU", 6) -prefetch_L2(F"baseU", 7) -#prefetch_L2(F"baseU", 8) -asmclose() -curlyclose() -newline() - -# prefetch gauge from memory into L1 cache -d['factor'] = 0 -d['cycles_PREFETCH_L1'] += 0 * d['factor'] -write('// PREFETCH_GAUGE_L1 (prefetch to L1)') -definemultiline(F'PREFETCH_GAUGE_L1_INTERNAL_{PRECSUFFIX}(A)') -curlyopen() -if GRIDBENCH: # referencing differs in Grid and GridBench - write(' const auto & ref(U[sU][A]); uint64_t baseU = (uint64_t)&ref; \\') -else: - write(' const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \\') -asmopen() -#pg1.loadpredication() -#fetch_base_ptr(F"&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]") -fetch_base_ptr(F"baseU", target='A') -prefetch_L1(F"baseU", 0) -prefetch_L1(F"baseU", 1) -prefetch_L1(F"baseU", 2) -asmclose() -curlyclose() -newline() - -d['factor'] = 0 -write('// LOAD_CHI') -definemultiline(F'LOAD_CHI_{PRECSUFFIX}(base)') -if ASM_LOAD_CHIMU: - curlyopen() - #write(' const SiteSpinor & ref(in[offset]); \\') - asmopen() - #fetch_base_ptr(F"base + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='I') - #fetch_base_ptr(F"base + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='A') - fetch_base_ptr(F"base", target='I') - fetch_base_ptr(F"base", target='A') - - Chi_00.load("ref[0][0]", offset=0) - Chi_01.load("ref[0][1]", offset=0) - Chi_02.load("ref[0][2]", offset=0) - Chi_10.load("ref[1][0]", offset=0) - Chi_11.load("ref[1][1]", offset=0) - Chi_12.load("ref[1][2]", offset=0) - asmclose() - debugall('LOAD_CHI', group='Chi') - curlyclose() -newline() - - - -d['factor'] = 8 -# 12 loads = 12 issues, load latency = 8+1 cycles -# (not perfectly clear to me from docs) -d['cycles_LOAD_CHIMU'] += 11 * d['factor'] -write('// LOAD_CHIMU') -definemultiline(F'LOAD_CHIMU_INTERLEAVED_{PRECSUFFIX}(base)') -if ASM_LOAD_CHIMU: - curlyopen() - #write(' const SiteSpinor & ref(in[offset]); \\') - asmopen() - pg1.loadpredication() - #fetch_base_ptr("&ref[0][0]") - #fetch_base_ptr(F"&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]") - fetch_base_ptr(F"base + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='I') - fetch_base_ptr(F"base + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='A') - # Chimu_00.load("ref[0][0]") - # Chimu_01.load("ref[0][1]") - # Chimu_02.load("ref[0][2]") - # Chimu_10.load("ref[1][0]") - # Chimu_11.load("ref[1][1]") - # Chimu_12.load("ref[1][2]") - # Chimu_20.load("ref[2][0]") - # Chimu_21.load("ref[2][1]") - # Chimu_22.load("ref[2][2]") - # Chimu_30.load("ref[3][0]") - # Chimu_31.load("ref[3][1]") - # Chimu_32.load("ref[3][2]") - - Chimu_00.load("ref[0][0]") # minimum penalty for all directions - Chimu_30.load("ref[3][0]") - Chimu_10.load("ref[1][0]") - Chimu_20.load("ref[2][0]") - - Chimu_01.load("ref[0][1]") - Chimu_31.load("ref[3][1]") - Chimu_11.load("ref[1][1]") - Chimu_21.load("ref[2][1]") - - Chimu_02.load("ref[0][2]") - Chimu_32.load("ref[3][2]") - Chimu_12.load("ref[1][2]") - Chimu_22.load("ref[2][2]") - asmclose() - debugall('LOAD_CHIMU', group='Chimu') - curlyclose() -newline() - -# alternative load chimu: dirac order 0213 -# placed into asm (...) -d['factor'] = 0 -d['cycles_LOAD_CHIMU'] += 11 * d['factor'] -write('// LOAD_CHIMU_0213') -definemultiline(F'LOAD_CHIMU_0213_{PRECSUFFIX}') -if ASM_LOAD_CHIMU: - curlyopen() - write(' const SiteSpinor & ref(in[offset]); \\') - asmopen() - pg1.loadpredication() - fetch_base_ptr(F"&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]") - Chimu_00.load("ref[0][0]") # reordered - Chimu_20.load("ref[2][0]") - - Chimu_01.load("ref[0][1]") - Chimu_21.load("ref[2][1]") - - Chimu_02.load("ref[0][2]") - Chimu_22.load("ref[2][2]") - - Chimu_10.load("ref[1][0]") - Chimu_30.load("ref[3][0]") - - Chimu_11.load("ref[1][1]") - Chimu_31.load("ref[3][1]") - - Chimu_12.load("ref[1][2]") - Chimu_32.load("ref[3][2]") - asmclose() - debugall('LOAD_CHIMU_0213', group='Chimu') - curlyclose() -newline() - -# alternative load chimu: dirac order 0312 -# placed into asm (...) -d['factor'] = 0 -d['cycles_LOAD_CHIMU'] += 11 * d['factor'] -write('// LOAD_CHIMU_0312') -definemultiline(F'LOAD_CHIMU_0312_{PRECSUFFIX}') -if ASM_LOAD_CHIMU: - curlyopen() - write(' const SiteSpinor & ref(in[offset]); \\') - asmopen() - pg1.loadpredication() - fetch_base_ptr(F"&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]") - Chimu_00.load("ref[0][0]") # reordered - Chimu_30.load("ref[3][0]") - - Chimu_01.load("ref[0][1]") - Chimu_31.load("ref[3][1]") - - Chimu_02.load("ref[0][2]") - Chimu_32.load("ref[3][2]") - - Chimu_10.load("ref[1][0]") - Chimu_20.load("ref[2][0]") - - Chimu_11.load("ref[1][1]") - Chimu_21.load("ref[2][1]") - - Chimu_12.load("ref[1][2]") - Chimu_22.load("ref[2][2]") - asmclose() - debugall('LOAD_CHIMU_0312', group='Chimu') - curlyclose() -newline() - -d['factor'] = 2 -d['cycles_PERM'] += 1 * d['factor'] -write('// LOAD_TABLE0') -definemultiline(F'LOAD_TABLE0') -asmopen() -table0.loadtable(0) -asmclose() -newline() - -d['factor'] = 2 -d['cycles_PERM'] += 1 * d['factor'] -write('// LOAD_TABLE1') -definemultiline(F'LOAD_TABLE1') -asmopen() -table0.loadtable(1) -asmclose() -newline() - -d['factor'] = 2 -d['cycles_PERM'] += 1 * d['factor'] -write('// LOAD_TABLE2') -definemultiline(F'LOAD_TABLE2') -asmopen() -table0.loadtable(2) -asmclose() -newline() - -d['factor'] = 0 -d['cycles_PERM'] += 1 * d['factor'] -write('// LOAD_TABLE3') -definemultiline(F'LOAD_TABLE3') -asmopen() -table0.loadtable(3) -asmclose() -newline() - -d['factor'] = 2 # factor is 2 -d['cycles_PERM'] += 6 * d['factor'] -write('// PERMUTE') -definemultiline(F'PERMUTE_{PRECSUFFIX}') -debugall('PERM PRE', group='Chi') -asmopen() -#table0.loadtable(2) -Chi_00.permute(2, table0) -Chi_01.permute(2, table0) -Chi_02.permute(2, table0) -Chi_10.permute(2, table0) -Chi_11.permute(2, table0) -Chi_12.permute(2, table0) -asmclose() -debugall('PERM POST', group='Chi') -newline() - -write('// LOAD_GAUGE') -definemultiline(F'LOAD_GAUGE') -if GRIDBENCH: # referencing differs in Grid and GridBench - write(' const auto & ref(U[sU][A]); uint64_t baseU = (uint64_t)&ref; \\') -else: - write(' const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \\') -curlyopen() -asmopen() -pg1.loadpredication() -fetch_base_ptr(F"baseU + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='I') -if ASM_LOAD_GAUGE: - fetch_base_ptr(F"baseU + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='A') - U_00.load("ref[0][0]") - U_10.load("ref[1][0]") - U_20.load("ref[2][0]") - U_01.load("ref[0][1]") - U_11.load("ref[1][1]") - U_21.load("ref[2][1]") -asmclose() -curlyclose() -newline() - -d['factor'] = 8 # MULT_2SPIN executes 1 time per direction = 8 times total -# assume all U loads are hidden -# FCMLA issue latency = 2 cycles -# measurement: latency = 16 cycles if FULLY pipelined !? -# spec says 6+6+9 cycles -# 6 rounds of FCMLA, each with 6 FCMLA -> 21 - 6*2 = 9 -d['cycles_MULT_2SPIN'] += 6 * 21 * d['factor'] -write('// MULT_2SPIN') -definemultiline(F'MULT_2SPIN_1_{PRECSUFFIX}(A)') -curlyopen() -#write(' const auto & ref(U[sU][A]); \\') -if GRIDBENCH: # referencing differs in Grid and GridBench - write(' const auto & ref(U[sU][A]); uint64_t baseU = (uint64_t)&ref; \\') -else: - write(' const auto & ref(U[sU](A)); uint64_t baseU = (uint64_t)&ref; \\') -asmopen() -#pg1.loadpredication() -#fetch_base_ptr("&ref[0][0]") -fetch_base_ptr(F"baseU + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='I') -fetch_base_ptr(F"baseU + {FETCH_BASE_PTR_COLOR_OFFSET} * 3 * 64", target='A') -#fetch_base_ptr(F"(uint64_t)&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]", target='I') -#fetch_base_ptr(F"(uint64_t)&ref[{FETCH_BASE_PTR_COLOR_OFFSET}][0]", target='A') -#fetch_base_ptr(F"&ref[0][{FETCH_BASE_PTR_COLOR_OFFSET}]") -if ASM_LOAD_GAUGE: - U_00.load("ref[0][0]") - U_10.load("ref[1][0]") - U_20.load("ref[2][0]") - U_01.load("ref[0][1]") - U_11.load("ref[1][1]") - U_21.load("ref[2][1]") - -if MOVPRFX == False: - UChi_00.zero() # implementation specific - UChi_10.zero() - UChi_01.zero() - UChi_11.zero() - UChi_02.zero() - UChi_12.zero() - - # round 1 - UChi_00.mul0(U_00, Chi_00) # FCMLA latency is 6+6+9 cycles - UChi_10.mul0(U_00, Chi_10) - UChi_01.mul0(U_10, Chi_00) - UChi_11.mul0(U_10, Chi_10) - UChi_02.mul0(U_20, Chi_00) - UChi_12.mul0(U_20, Chi_10) -else: - # round 1 - UChi_00.mul0(zero0, U_00, Chi_00, constructive=True) # FCMLA latency is 6+6+9 cycles - UChi_10.mul0(zero0, U_00, Chi_10, constructive=True) - UChi_01.mul0(zero0, U_10, Chi_00, constructive=True) - UChi_11.mul0(zero0, U_10, Chi_10, constructive=True) - UChi_02.mul0(zero0, U_20, Chi_00, constructive=True) - UChi_12.mul0(zero0, U_20, Chi_10, constructive=True) - -# round 2 -UChi_00.mul1(U_00, Chi_00) -UChi_10.mul1(U_00, Chi_10) -UChi_01.mul1(U_10, Chi_00) -UChi_11.mul1(U_10, Chi_10) -UChi_02.mul1(U_20, Chi_00) -UChi_12.mul1(U_20, Chi_10) # Chi_00 and Chi_10 available from here - -if ASM_LOAD_GAUGE: - U_00.load("ref[0][2]") # U_00, U_10, U_20 overloaded - U_10.load("ref[1][2]") # early load - U_20.load("ref[2][2]") # A --> -asmclose() -debugall('MULT_2SPIN_1', group='UChi') -curlyclose() -newline() - -write('// MULT_2SPIN_BACKEND') -definemultiline(F'MULT_2SPIN_2_{PRECSUFFIX}') -curlyopen() -asmopen() -# round 3 -UChi_00.mac0(U_01, Chi_01) # armclang separates fcmla(..., 0) and -UChi_10.mac0(U_01, Chi_11) # fcmla(..., 90) -UChi_01.mac0(U_11, Chi_01) # autonomously using intrinsics -UChi_11.mac0(U_11, Chi_11) -UChi_02.mac0(U_21, Chi_01) -UChi_12.mac0(U_21, Chi_11) -# round 4 -UChi_00.mac1(U_01, Chi_01) -UChi_10.mac1(U_01, Chi_11) -UChi_01.mac1(U_11, Chi_01) -UChi_11.mac1(U_11, Chi_11) -UChi_02.mac1(U_21, Chi_01) -UChi_12.mac1(U_21, Chi_11) -# round 5 -UChi_00.mac0(U_00, Chi_02) # <-- A -UChi_10.mac0(U_00, Chi_12) -UChi_01.mac0(U_10, Chi_02) -UChi_11.mac0(U_10, Chi_12) -UChi_02.mac0(U_20, Chi_02) -UChi_12.mac0(U_20, Chi_12) -# round 6 -UChi_00.mac1(U_00, Chi_02) -UChi_10.mac1(U_00, Chi_12) -UChi_01.mac1(U_10, Chi_02) -UChi_11.mac1(U_10, Chi_12) -UChi_02.mac1(U_20, Chi_02) -UChi_12.mac1(U_20, Chi_12) -asmclose() -debugall('MULT_2SPIN_2', group='UChi') -curlyclose() -newline() - - -#// hspin(0)=fspin(0)+timesI(fspin(3)); -#// hspin(1)=fspin(1)+timesI(fspin(2)); -d['factor'] = 1 -# FCADD issue latency = 1, latency is 6+9 -d['cycles_PROJ'] += 15 * d['factor'] -write('// XP_PROJ') -definemultiline(F'XP_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0312_PLUG \\') -curlyopen() -asmopen() -#pg1.loadpredication() -Chi_00.addTimesI(Chimu_00, Chimu_30) -Chi_01.addTimesI(Chimu_01, Chimu_31) -Chi_02.addTimesI(Chimu_02, Chimu_32) -Chi_10.addTimesI(Chimu_10, Chimu_20) -Chi_11.addTimesI(Chimu_11, Chimu_21) -Chi_12.addTimesI(Chimu_12, Chimu_22) -asmclose() -debugall('XP_PROJ', group='Chi') -curlyclose() -newline() - -#// fspin(0)=hspin(0); -#// fspin(1)=hspin(1); -#// fspin(2)=timesMinusI(hspin(1)); -#// fspin(3)=timesMinusI(hspin(0)); -# does not occur in GridBench -d['factor'] = 0 -d['cycles_RECON'] += 15 * d['factor'] -write('// XP_RECON') -definemultiline(F'XP_RECON_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -if MOVPRFX == False: - result_20.zero() - result_21.zero() - result_22.zero() - result_30.zero() - result_31.zero() - result_32.zero() - - result_20.subTimesI(UChi_10) - result_21.subTimesI(UChi_11) - result_22.subTimesI(UChi_12) - result_30.subTimesI(UChi_00) - result_31.subTimesI(UChi_01) - result_32.subTimesI(UChi_02) -else: - result_20.subTimesI(zero0, UChi_10, constructive=True) - result_21.subTimesI(zero0, UChi_11, constructive=True) - result_22.subTimesI(zero0, UChi_12, constructive=True) - result_30.subTimesI(zero0, UChi_00, constructive=True) - result_31.subTimesI(zero0, UChi_01, constructive=True) - result_32.subTimesI(zero0, UChi_02, constructive=True) - -result_00.move(UChi_00) # don't reorder ! -result_01.move(UChi_01) -result_02.move(UChi_02) -result_10.move(UChi_10) -result_11.move(UChi_11) -result_12.move(UChi_12) - -# result_00.add(UChi_00) # faster than move? -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) -asmclose() -debugall('XP_RECON', group='result') -newline() - - -d['factor'] = 1 -# FCADD issue latency = 1, latency is 6+9 -d['cycles_RECON'] += 15 * d['factor'] -write('// XP_RECON_ACCUM') -definemultiline(F'XP_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -# result_20.subTimesI(UChi_10) -# result_21.subTimesI(UChi_11) -# result_22.subTimesI(UChi_12) -# result_30.subTimesI(UChi_00) -# result_31.subTimesI(UChi_01) -# result_32.subTimesI(UChi_02) -# -# result_00.add(UChi_00) # reordered -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) - -result_30.subTimesI(UChi_00) # reordered -result_00.add(UChi_00) - -result_31.subTimesI(UChi_01) -result_01.add(UChi_01) - -result_32.subTimesI(UChi_02) -result_02.add(UChi_02) - -result_20.subTimesI(UChi_10) -result_10.add(UChi_10) - -result_21.subTimesI(UChi_11) -result_11.add(UChi_11) - -result_22.subTimesI(UChi_12) -result_12.add(UChi_12) -asmclose() -debugall('XP_RECON_ACCUM', group='result') -newline() - -d['factor'] = 1 -# add/sub issue latency = 1, latency is 9 -d['cycles_PROJ'] += 9 * d['factor'] -write('// YP_PROJ') -definemultiline(F'YP_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0312_PLUG \\') -curlyopen() -asmopen() -#pg1.loadpredication() -Chi_00.sub(Chimu_00, Chimu_30) -Chi_01.sub(Chimu_01, Chimu_31) -Chi_02.sub(Chimu_02, Chimu_32) -Chi_10.add(Chimu_10, Chimu_20) -Chi_11.add(Chimu_11, Chimu_21) -Chi_12.add(Chimu_12, Chimu_22) -asmclose() -debugall('YP_PROJ', group='Chi') -curlyclose() -newline() - -d['factor'] = 1 -# FCADD issue latency = 1, latency is 6+9 -d['cycles_PROJ'] += 15 * d['factor'] -write('// ZP_PROJ') -definemultiline(F'ZP_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0213_PLUG \\') -curlyopen() -asmopen() -#pg1.loadpredication() -Chi_00.addTimesI(Chimu_00, Chimu_20) -Chi_01.addTimesI(Chimu_01, Chimu_21) -Chi_02.addTimesI(Chimu_02, Chimu_22) -Chi_10.subTimesI(Chimu_10, Chimu_30) -Chi_11.subTimesI(Chimu_11, Chimu_31) -Chi_12.subTimesI(Chimu_12, Chimu_32) -asmclose() -debugall('ZP_PROJ', group='Chi') -curlyclose() -newline() - -d['factor'] = 1 -# add/sub issue latency = 1, latency is 9 -d['cycles_PROJ'] += 9 * d['factor'] -write('// TP_PROJ') -definemultiline(F'TP_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0213_PLUG \\') -curlyopen() -asmopen() -#pg1.loadpredication() -Chi_00.add(Chimu_00, Chimu_20) -Chi_01.add(Chimu_01, Chimu_21) -Chi_02.add(Chimu_02, Chimu_22) -Chi_10.add(Chimu_10, Chimu_30) -Chi_11.add(Chimu_11, Chimu_31) -Chi_12.add(Chimu_12, Chimu_32) -asmclose() -debugall('TP_PROJ', group='Chi') -curlyclose() -newline() - -#// hspin(0)=fspin(0)-timesI(fspin(3)); -#// hspin(1)=fspin(1)-timesI(fspin(2)); - -d['factor'] = 1 -# FCADD issue latency = 1, latency is 6+9 -d['cycles_PROJ'] += 15 * d['factor'] -write('// XM_PROJ') -definemultiline(F'XM_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0312_PLUG \\') -curlyopen() -asmopen() -#pg1.loadpredication() -Chi_00.subTimesI(Chimu_00, Chimu_30) -Chi_01.subTimesI(Chimu_01, Chimu_31) -Chi_02.subTimesI(Chimu_02, Chimu_32) -Chi_10.subTimesI(Chimu_10, Chimu_20) -Chi_11.subTimesI(Chimu_11, Chimu_21) -Chi_12.subTimesI(Chimu_12, Chimu_22) -asmclose() -debugall('XM_PROJ sub', group='Chi') -curlyclose() -newline() - -d['factor'] = 1 -d['cycles_RECON'] += 15 * d['factor'] -write('// XM_RECON') -definemultiline(F'XM_RECON_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() - -# only necessary if not zeroed before -if MOVPRFX == False: - result_20.zero() - result_21.zero() - result_22.zero() - result_30.zero() - result_31.zero() - result_32.zero() - - result_20.addTimesI(UChi_10) # <-- - result_21.addTimesI(UChi_11) - result_22.addTimesI(UChi_12) - result_30.addTimesI(UChi_00) - result_31.addTimesI(UChi_01) - result_32.addTimesI(UChi_02) -else: - result_20.addTimesI(zero0, UChi_10, constructive=True) # <-- - result_21.addTimesI(zero0, UChi_11, constructive=True) - result_22.addTimesI(zero0, UChi_12, constructive=True) - result_30.addTimesI(zero0, UChi_00, constructive=True) - result_31.addTimesI(zero0, UChi_01, constructive=True) - result_32.addTimesI(zero0, UChi_02, constructive=True) - -result_00.move(UChi_00) -result_01.move(UChi_01) -result_02.move(UChi_02) -result_10.move(UChi_10) -result_11.move(UChi_11) -result_12.move(UChi_12) -asmclose() -debugall('XM_RECON result', group='result') -newline() - -d['factor'] = 1 -# add/sub issue latency = 1, latency is 9 -d['cycles_PROJ'] += 9 * d['factor'] -write('// YM_PROJ') -definemultiline(F'YM_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0312_PLUG \\') -curlyopen() -asmopen() -#pg1.loadpredication() -Chi_00.add(Chimu_00, Chimu_30) -Chi_01.add(Chimu_01, Chimu_31) -Chi_02.add(Chimu_02, Chimu_32) -Chi_10.sub(Chimu_10, Chimu_20) -Chi_11.sub(Chimu_11, Chimu_21) -Chi_12.sub(Chimu_12, Chimu_22) -asmclose() -debugall('YM_PROJ', group='Chi') -curlyclose() -newline() - -d['factor'] = 1 -# FCADD issue latency = 1, latency is 6+9 -d['cycles_PROJ'] += 15 * d['factor'] -write('// ZM_PROJ') -definemultiline(F'ZM_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0213_PLUG \\') -curlyopen() -asmopen() -#pg1.loadpredication() -Chi_00.subTimesI(Chimu_00, Chimu_20) -Chi_01.subTimesI(Chimu_01, Chimu_21) -Chi_02.subTimesI(Chimu_02, Chimu_22) -Chi_10.addTimesI(Chimu_10, Chimu_30) -Chi_11.addTimesI(Chimu_11, Chimu_31) -Chi_12.addTimesI(Chimu_12, Chimu_32) -asmclose() -debugall('ZM_PROJ', group='Chi') -curlyclose() -newline() - -d['factor'] = 1 -# add/sub issue latency = 1, latency is 9 -d['cycles_PROJ'] += 9 * d['factor'] -write('// TM_PROJ') -definemultiline(F'TM_PROJ_{PRECSUFFIX}') -if ALTERNATIVE_LOADS == True: - write(' LOAD_CHIMU_0213_PLUG \\') -curlyopen() -asmopen() -pg1.loadpredication() -Chi_00.sub(Chimu_00, Chimu_20) -Chi_01.sub(Chimu_01, Chimu_21) -Chi_02.sub(Chimu_02, Chimu_22) -Chi_10.sub(Chimu_10, Chimu_30) -Chi_11.sub(Chimu_11, Chimu_31) -Chi_12.sub(Chimu_12, Chimu_32) -asmclose() -debugall('TM_PROJ', group='Chi') -curlyclose() -newline() - -# does not occur in GridBench -d['factor'] = 0 -# add/sub issue latency = 1, latency is 9 -d['cycles_RECON'] += 15 * d['factor'] -write('// XM_RECON_ACCUM') -definemultiline(F'XM_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -# result_20.addTimesI(UChi_10) -# result_21.addTimesI(UChi_11) -# result_22.addTimesI(UChi_12) -# result_30.addTimesI(UChi_00) -# result_31.addTimesI(UChi_01) -# result_32.addTimesI(UChi_02) -# -# # result_00.move(UChi_00) -# # result_01.move(UChi_01) -# # result_02.move(UChi_02) -# # result_10.move(UChi_10) -# # result_11.move(UChi_11) -# # result_12.move(UChi_12) -# -# # faster than move ? -# result_00.add(UChi_00) -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) - -result_30.addTimesI(UChi_00) # reordered -result_31.addTimesI(UChi_01) -result_32.addTimesI(UChi_02) - -result_20.addTimesI(UChi_10) -result_21.addTimesI(UChi_11) -result_22.addTimesI(UChi_12) - -result_00.add(UChi_00) -result_01.add(UChi_01) -result_02.add(UChi_02) -result_10.add(UChi_10) -result_11.add(UChi_11) -result_12.add(UChi_12) -asmclose() -debugall('XM_RECON_ACCUM', group='result') -newline() - - - -d['factor'] = 1 -d['cycles_RECON'] += 9 * d['factor'] -write('// YP_RECON_ACCUM') -definemultiline(F'YP_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -# result_00.add(UChi_00) -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) -# result_20.add(UChi_10) -# result_21.add(UChi_11) -# result_22.add(UChi_12) -# result_30.sub(UChi_00) -# result_31.sub(UChi_01) -# result_32.sub(UChi_02) - -result_00.add(UChi_00) # reordered -result_30.sub(UChi_00) - -result_01.add(UChi_01) -result_31.sub(UChi_01) - -result_02.add(UChi_02) -result_32.sub(UChi_02) - -result_10.add(UChi_10) -result_20.add(UChi_10) - -result_11.add(UChi_11) -result_21.add(UChi_11) - -result_12.add(UChi_12) -result_22.add(UChi_12) -asmclose() -debugall('YP_RECON_ACCUM', group='result') -newline() - -d['factor'] = 1 -d['cycles_RECON'] += 9 * d['factor'] -write('// YM_RECON_ACCUM') -definemultiline(F'YM_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -# result_00.add(UChi_00) -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) -# result_20.sub(UChi_10) -# result_21.sub(UChi_11) -# result_22.sub(UChi_12) -# result_30.add(UChi_00) -# result_31.add(UChi_01) -# result_32.add(UChi_02) - -result_00.add(UChi_00) # reordered -result_30.add(UChi_00) - -result_01.add(UChi_01) -result_31.add(UChi_01) - -result_02.add(UChi_02) -result_32.add(UChi_02) - -result_10.add(UChi_10) -result_20.sub(UChi_10) - -result_11.add(UChi_11) -result_21.sub(UChi_11) - -result_12.add(UChi_12) -result_22.sub(UChi_12) -asmclose() -debugall('YM_RECON_ACCUM', group='result') -newline() - -d['factor'] = 1 -d['cycles_RECON'] += 15 * d['factor'] -write('// ZP_RECON_ACCUM') -definemultiline(F'ZP_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -# result_20.subTimesI(UChi_00) -# result_21.subTimesI(UChi_01) -# result_22.subTimesI(UChi_02) -# result_30.addTimesI(UChi_10) -# result_31.addTimesI(UChi_11) -# result_32.addTimesI(UChi_12) -# -# result_00.add(UChi_00) -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) -result_20.subTimesI(UChi_00) # reordered -result_00.add(UChi_00) - -result_21.subTimesI(UChi_01) -result_01.add(UChi_01) - -result_22.subTimesI(UChi_02) -result_02.add(UChi_02) - -result_30.addTimesI(UChi_10) -result_10.add(UChi_10) - -result_31.addTimesI(UChi_11) -result_11.add(UChi_11) - -result_32.addTimesI(UChi_12) -result_12.add(UChi_12) -asmclose() -debugall('ZP_RECON_ACCUM', group='result') -newline() - -d['factor'] = 1 -d['cycles_RECON'] += 15 * d['factor'] -write('// ZM_RECON_ACCUM') -definemultiline(F'ZM_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -# result_20.addTimesI(UChi_00) -# result_21.addTimesI(UChi_01) -# result_22.addTimesI(UChi_02) -# result_30.subTimesI(UChi_10) -# result_31.subTimesI(UChi_11) -# result_32.subTimesI(UChi_12) -# -# result_00.add(UChi_00) -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) -result_20.addTimesI(UChi_00) # reordered -result_00.add(UChi_00) - -result_21.addTimesI(UChi_01) -result_01.add(UChi_01) - -result_22.addTimesI(UChi_02) -result_02.add(UChi_02) - -result_30.subTimesI(UChi_10) -result_10.add(UChi_10) - -result_31.subTimesI(UChi_11) -result_11.add(UChi_11) - -result_32.subTimesI(UChi_12) -result_12.add(UChi_12) -asmclose() -debugall('ZM_RECON_ACCUM', group='result') -newline() - -d['factor'] = 1 -d['cycles_RECON'] += 9 * d['factor'] -write('// TP_RECON_ACCUM') -definemultiline(F'TP_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -# result_00.add(UChi_00) -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) -# result_20.add(UChi_00) -# result_21.add(UChi_01) -# result_22.add(UChi_02) -# result_30.add(UChi_10) -# result_31.add(UChi_11) -# result_32.add(UChi_12) - -result_00.add(UChi_00) # reordered -result_20.add(UChi_00) - -result_01.add(UChi_01) -result_21.add(UChi_01) - -result_02.add(UChi_02) -result_22.add(UChi_02) - -result_10.add(UChi_10) -result_30.add(UChi_10) - -result_11.add(UChi_11) -result_31.add(UChi_11) - -result_12.add(UChi_12) -result_32.add(UChi_12) -asmclose() -debugall('TP_RECON_ACCUM', group='result') -newline() - -d['factor'] = 1 -d['cycles_RECON'] += 9 * d['factor'] -write('// TM_RECON_ACCUM') -definemultiline(F'TM_RECON_ACCUM_{PRECSUFFIX}') -asmopen() -#pg1.loadpredication() -# result_00.add(UChi_00) -# result_01.add(UChi_01) -# result_02.add(UChi_02) -# result_10.add(UChi_10) -# result_11.add(UChi_11) -# result_12.add(UChi_12) -# result_20.sub(UChi_00) -# result_21.sub(UChi_01) -# result_22.sub(UChi_02) -# result_30.sub(UChi_10) -# result_31.sub(UChi_11) -# result_32.sub(UChi_12) - -result_00.add(UChi_00) # reordered -result_20.sub(UChi_00) - -result_01.add(UChi_01) -result_21.sub(UChi_01) - -result_02.add(UChi_02) -result_22.sub(UChi_02) - -result_10.add(UChi_10) -result_30.sub(UChi_10) - -result_11.add(UChi_11) -result_31.sub(UChi_11) - -result_12.add(UChi_12) -result_32.sub(UChi_12) -asmclose() -debugall('TM_RECON_ACCUM', group='result') -newline() - -d['factor'] = 0 -# have 12 instructions -# picking dual issue versions -d['cycles_ZERO_PSI'] += 6 * d['factor'] -write('// ZERO_PSI') -definemultiline(F'ZERO_PSI_{PRECSUFFIX}') -asmopen() -pg1.loadpredication() -result_00.zero() -result_01.zero() -result_02.zero() -result_10.zero() -result_11.zero() -result_12.zero() -result_20.zero() -result_21.zero() -result_22.zero() -result_30.zero() -result_31.zero() -result_32.zero() -asmclose() -#debugall('ZERO_PSI', group='result') -newline() - -# prefetch store spinors to L2 cache -d['factor'] = 0 -d['cycles_PREFETCH_L2'] += 0 * d['factor'] -write('// PREFETCH_RESULT_L2_STORE (prefetch store to L2)') -definemultiline(F'PREFETCH_RESULT_L2_STORE_INTERNAL_{PRECSUFFIX}(base)') -curlyopen() -fetch_base_ptr(F"base") -asmopen() -fetch_base_ptr(F"base", target='A') -prefetch_L2_store(F"base", 0) -prefetch_L2_store(F"base", 1) -prefetch_L2_store(F"base", 2) -asmclose() -curlyclose() -newline() - -# prefetch store spinors to L1 cache -d['factor'] = 0 -d['cycles_PREFETCH_L1'] += 0 * d['factor'] -write('// PREFETCH_RESULT_L1_STORE (prefetch store to L1)') -definemultiline(F'PREFETCH_RESULT_L1_STORE_INTERNAL_{PRECSUFFIX}(base)') -curlyopen() -fetch_base_ptr(F"base") -asmopen() -fetch_base_ptr(F"base", target='A') -prefetch_L1_store(F"base", 0) -prefetch_L1_store(F"base", 1) -prefetch_L1_store(F"base", 2) -asmclose() -curlyclose() -newline() - - -d['factor'] = 0 -write('// ADD_RESULT_INTERNAL') -definemultiline(F'ADD_RESULT_INTERNAL_{PRECSUFFIX}') -asmopen() -result_00.add(Chimu_00) -result_01.add(Chimu_01) -result_02.add(Chimu_02) -result_10.add(Chimu_10) -result_11.add(Chimu_11) -result_12.add(Chimu_12) -result_20.add(Chimu_20) -result_21.add(Chimu_21) -result_22.add(Chimu_22) -result_30.add(Chimu_30) -result_31.add(Chimu_31) -result_32.add(Chimu_32) -asmclose() -#debugall('ZERO_PSI', group='result') -newline() - -# -------------------------------------------------------------------------------- - -# C -f = open('w.h', 'w') -f.write(d['C']) -f.close() - -# intrin -f = open('wi.h', 'w') -f.write(d['I']) -f.close() - -filename = '' -if PRECISION == 'double': - filename = "Fujitsu_A64FX_intrin_double.h" -else: - filename = "Fujitsu_A64FX_intrin_single.h" -f = open(filename, 'w') -f.write(LEGAL.format(filename)) -f.write(d['I']) -f.close() - - -# asm -f = open('wa.h', 'w') -f.write(d['A']) -f.close() - -filename = '' -if PRECISION == 'double': - filename = "Fujitsu_A64FX_asm_double.h" -else: - filename = "Fujitsu_A64FX_asm_single.h" -f = open(filename, 'w') -f.write(LEGAL.format(filename)) -f.write(d['A']) -f.close() - - -# arithmetics instruction count, mul/mac = 2 instructions each -d['acount'] = d['add'] + d['sub'] + \ - d['mul'] + d['mac'] + d['addTimesI'] + d['subTimesI'] - -# permutations -d['permutes'] += 2*d['timesI'] + 1*d['timesMinusI'] -d['neg'] = 1*d['timesI'] + 1*d['timesMinusI'] - -# instruction count, mul/mac = 2 instructions each, +/- *i = 3 instructions each -d['icount'] = d['load'] + d['store'] + d['move'] + d['add'] + d['sub'] + \ - d['mul'] + d['mac'] + d['permutes'] + d['neg'] + \ - d['addTimesI'] + d['subTimesI'] + d['zero'] + d['movprfx'] - -# flops -d['flops'] = 4*d['mac'] + 3*d['mul'] + d['add'] + d['sub'] + \ - d['addTimesI'] + d['subTimesI'] - - - - - -print('Statistics') -print('') -print('Type Occurences Total / Arith instructions') -print('-------------------------------------------------------------------') -print('Variables {:4d}'.format(d['registers'])) -print('') -print('load {:4d}'.format(d['load'])) -print('store {:4d}'.format(d['store'])) -print('move {:4d}'.format(d['move'])) -print('movprfx {:4d}'.format(d['movprfx'])) -print('zero {:4d}'.format(d['zero'])) -print('negate {:4d}'.format(d['neg'])) - - -print('add {:4d} {:0.2f} / {:0.2f}'.\ - format(d['add'], d['add'] / d['icount'], d['add'] / d['acount'])) -print('sub {:4d} {:0.2f} / {:0.2f}'.\ - format(d['sub'], d['sub'] / d['icount'], d['sub'] / d['acount'])) -print('mul {:4d} {:0.2f} / {:0.2f}'.\ - format(d['mul'], 2*d['mul'] / d['icount'], 2*d['mul'] / d['acount'])) -print('mac {:4d} {:0.2f} / {:0.2f}'.\ - format(d['mac'], 2*d['mac'] / d['icount'], 2*d['mac'] / d['acount'])) -print('addTimesI {:4d} {:0.2f} / {:0.2f}'.\ - format(d['addTimesI'], 2*d['addTimesI'] / d['icount'], 2*d['addTimesI'] / d['acount'])) -print('subTimesI {:4d} {:0.2f} / {:0.2f}'.\ - format(d['subTimesI'], 2*d['subTimesI'] / d['icount'], 2*d['subTimesI'] / d['acount'])) - -print('timesI {:4d}'.format(d['timesI'])) -print('timesMinusI {:4d}'.format(d['timesMinusI'])) -print('permutes {:4d} {:0.2f}'.\ - format(d['permutes'], d['permutes'] / d['icount'])) -print('') -print('flops {:4d}'.format(d['flops'])) -print('instruction count {:4d}'.format(d['icount'])) -print('arith. instruction count {:4d} {:0.2f}'.\ - format(d['acount'], d['acount'] / d['icount'])) - - -# ---- static pipeline resources consumption ---- -FLA = 0 -FLA += 2 * d['mac'] + 2 * d['mul'] -FLA += 1 * d['addTimesI'] + 1 * d['subTimesI'] -FLA += 1 * d['move'] -FLA += 1 * d['permutes'] -FLA += 1 * d['store'] -FLA += 1 * d['zero'] - -FLB = 0 -FLB += 1 * d['addTimesI'] + 1 * d['subTimesI'] - -FLAB = 0 -FLAB += 1 * d['mac'] + 1 * d['mul'] -FLAB += 1 * d['add'] + 1 * d['sub'] -FLAB += 1 * d['neg'] + 1 * d['movprfx'] -#FLAB += 1 * d['zero'] - - -FL_slots = 2 * d['icount'] -FL_micro_ops = FLA + FLB + FLAB - -print('') -print('------------------------------------------------------------------') -print('') -print('Static FL slot usage') -print('') -print(' FLA {:4d}'.format(FLA)) -print(' FLB {:4d}'.format(FLB)) -print(' FLA/B {:4d}'.format(FLAB)) - -print('') -print('Static FL slot efficiency') -print('') -print(' Total FL slots {:4d}'.format(FL_slots)) -print(' FL slots occupied {:4d}'.format(FL_micro_ops)) -print(' FL slot efficiency {:0.2f}'.format(FL_micro_ops / FL_slots)) - -cycles_total = d['cycles_ZERO_PSI'] + d['cycles_LOAD_CHIMU'] + \ - d['cycles_PROJ'] + d['cycles_PERM'] + d['cycles_MULT_2SPIN'] + \ - d['cycles_RECON'] + d['cycles_RESULT'] -cycles_total_hidden = d['cycles_ZERO_PSI'] + \ - d['cycles_PROJ'] + d['cycles_MULT_2SPIN'] + \ - d['cycles_RECON'] - -# ---- dynamic estimate ---- - -print('') -print('Dynamic cycles estimate (incl. latencies)') -print('') -print(' ZERO_PSI {:4d}'.format(d['cycles_ZERO_PSI'])) -print(' LOAD_CHIMU {:4d}'.format(d['cycles_LOAD_CHIMU'])) -print(' PROJ {:4d}'.format(d['cycles_PROJ'])) -print(' PERM {:4d}'.format(d['cycles_PERM'])) -print(' MULT_2SPIN {:4d}'.format(d['cycles_MULT_2SPIN'])) -print(' RECON {:4d}'.format(d['cycles_RECON'])) -print(' STORE {:4d}'.format(d['cycles_RESULT'])) -print('') -print(' Sum {:4d}'.format(cycles_total)) -print('') -print(' Sum* {:4d}'.format(cycles_total_hidden)) -print(' Total FL slots* {:4d}'.format(cycles_total_hidden * 2)) -print(' FL slots occupied* {:4d}'.format(FL_micro_ops)) -print(' FL slot efficiency* {:0.2f}'.format(FL_micro_ops / (2*cycles_total_hidden))) -print('') -print(' *load/store/PERM hidden') - -estimated_cycles = cycles_total_hidden -# Estimate percent peak DP; dual issue, fma -pp = 100 * 4 * d['flops'] / (2*2*8*estimated_cycles) -print('') -print('Model prediction') -print('') -print(' Cycles* {:4d}'.format(estimated_cycles)) -print(' Percent peak* {:4.1f} %'.format(pp)) - -# estimated RF throughput in GB/s @ 2.2 GHz -tp10 = (d['load'] + d['store']) * 64 * 2.2 / estimated_cycles -tp2 = (d['load'] + d['store']) * 64 * 1000.**3 * 2.2 / 1024.**3 / estimated_cycles -print('') -print(' Estimated RF throughput* {:4.1f} GB/s'.\ - format(tp10)) -print(' Estimated RF throughput* {:4.1f} GiB/s'.\ - format(tp2)) - -# ---- dynamic pipeline resources consumption ---- - -runtime = measured_cycles # runtime in cycles -pp_runtime = 100 * 4 * d['flops'] / (2*2*8*runtime) -runtime_FL_slots = 2 * runtime -delta = runtime - estimated_cycles - - -print('') -print('------------------------------------------------------------------') -print('') -print('Dynamic runtime analysis (cycles from measurements)') -print('') -print(' Cycles {:4d}'.format(runtime)) -print(' Percent peak {:4.1f} %'.format(pp_runtime)) -print(' Deviation from estimate {:4d} {:4.2f} %'.\ - format(delta, 100. * abs(delta/runtime))) -print(' Deviation per direction {:4.1f}'.format(delta/8)) - -# estimated RF throughput in GB/s @ 2.2 GHz -tp10_rt = (d['load'] + d['store']) * 64 * 2.2 / runtime -tp2_rt = (d['load'] + d['store']) * 64 * 1000.**3 * 2.2 / 1024.**3 / runtime -print('') -print(' RF throughput {:4.1f} GB/s'.\ - format(tp10_rt)) -print(' RF throughput {:4.1f} GiB/s'.\ - format(tp2_rt)) -print('') -print(' Total FL slots {:4d}'.format(runtime_FL_slots)) -print(' FL slots occupied {:4d}'.format(FL_micro_ops)) -print(' FL slot efficiency {:0.2f}'.format(FL_micro_ops / runtime_FL_slots)) -print('') From 909acd55cd36c4b567cab30d311aab6b8674288d Mon Sep 17 00:00:00 2001 From: Nils Meyer Date: Sat, 19 Dec 2020 02:00:22 +0100 Subject: [PATCH 060/399] vnum variant for prefetches --- Grid/simd/Fujitsu_A64FX_intrin_double.h | 36 ++++++++++++------------- Grid/simd/Fujitsu_A64FX_intrin_single.h | 36 ++++++++++++------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Grid/simd/Fujitsu_A64FX_intrin_double.h b/Grid/simd/Fujitsu_A64FX_intrin_double.h index 361246fc..f195e3c5 100644 --- a/Grid/simd/Fujitsu_A64FX_intrin_double.h +++ b/Grid/simd/Fujitsu_A64FX_intrin_double.h @@ -144,38 +144,38 @@ Author: Nils Meyer // PREFETCH_CHIMU_L2 (prefetch to L2) #define PREFETCH_CHIMU_L2_INTERNAL_A64FXd(base) \ { \ - svprfd(pg1, (int64_t*)(base + 0), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(base + 256), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(base + 512), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)0), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)8), SV_PLDL2STRM); \ } // PREFETCH_CHIMU_L1 (prefetch to L1) #define PREFETCH_CHIMU_L1_INTERNAL_A64FXd(base) \ { \ - svprfd(pg1, (int64_t*)(base + 0), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(base + 256), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(base + 512), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)0), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)4), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)8), SV_PLDL1STRM); \ } // PREFETCH_GAUGE_L2 (prefetch to L2) #define PREFETCH_GAUGE_L2_INTERNAL_A64FXd(A) \ { \ const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ - svprfd(pg1, (int64_t*)(baseU + -256), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 512), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 768), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1024), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1280), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1536), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1792), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)-4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)0), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)8), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)12), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)16), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)20), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)24), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)28), SV_PLDL2STRM); \ } // PREFETCH_GAUGE_L1 (prefetch to L1) #define PREFETCH_GAUGE_L1_INTERNAL_A64FXd(A) \ { \ const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ - svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(baseU + 512), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)0), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)4), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)8), SV_PLDL1STRM); \ } // LOAD_CHI #define LOAD_CHI_A64FXd(base) \ diff --git a/Grid/simd/Fujitsu_A64FX_intrin_single.h b/Grid/simd/Fujitsu_A64FX_intrin_single.h index 30273b6e..0b874f02 100644 --- a/Grid/simd/Fujitsu_A64FX_intrin_single.h +++ b/Grid/simd/Fujitsu_A64FX_intrin_single.h @@ -144,38 +144,38 @@ Author: Nils Meyer // PREFETCH_CHIMU_L2 (prefetch to L2) #define PREFETCH_CHIMU_L2_INTERNAL_A64FXf(base) \ { \ - svprfd(pg1, (int64_t*)(base + 0), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(base + 256), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(base + 512), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(0), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(8), SV_PLDL2STRM); \ } // PREFETCH_CHIMU_L1 (prefetch to L1) #define PREFETCH_CHIMU_L1_INTERNAL_A64FXf(base) \ { \ - svprfd(pg1, (int64_t*)(base + 0), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(base + 256), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(base + 512), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(0), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(4), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(8), SV_PLDL1STRM); \ } // PREFETCH_GAUGE_L2 (prefetch to L2) #define PREFETCH_GAUGE_L2_INTERNAL_A64FXf(A) \ { \ const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ - svprfd(pg1, (int64_t*)(baseU + -256), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 512), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 768), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1024), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1280), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1536), SV_PLDL2STRM); \ - svprfd(pg1, (int64_t*)(baseU + 1792), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(-4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(0), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(8), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(12), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(16), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(20), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(24), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(28), SV_PLDL2STRM); \ } // PREFETCH_GAUGE_L1 (prefetch to L1) #define PREFETCH_GAUGE_L1_INTERNAL_A64FXf(A) \ { \ const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ - svprfd(pg1, (int64_t*)(baseU + 0), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(baseU + 256), SV_PLDL1STRM); \ - svprfd(pg1, (int64_t*)(baseU + 512), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(0), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(4), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(8), SV_PLDL1STRM); \ } // LOAD_CHI #define LOAD_CHI_A64FXf(base) \ From 4b882e8056b2c9dd6dceab2729104e5e615835ae Mon Sep 17 00:00:00 2001 From: Nils Meyer Date: Sat, 19 Dec 2020 03:09:20 +0100 Subject: [PATCH 061/399] fixed lost bracket --- Grid/simd/Fujitsu_A64FX_intrin_double.h | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Grid/simd/Fujitsu_A64FX_intrin_double.h b/Grid/simd/Fujitsu_A64FX_intrin_double.h index f195e3c5..b645c365 100644 --- a/Grid/simd/Fujitsu_A64FX_intrin_double.h +++ b/Grid/simd/Fujitsu_A64FX_intrin_double.h @@ -144,38 +144,38 @@ Author: Nils Meyer // PREFETCH_CHIMU_L2 (prefetch to L2) #define PREFETCH_CHIMU_L2_INTERNAL_A64FXd(base) \ { \ - svprfd_vnum(pg1, (void*)(base), (int64_t)0), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(base), (int64_t)4), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(base), (int64_t)8), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(0), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(8), SV_PLDL2STRM); \ } // PREFETCH_CHIMU_L1 (prefetch to L1) #define PREFETCH_CHIMU_L1_INTERNAL_A64FXd(base) \ { \ - svprfd_vnum(pg1, (void*)(base), (int64_t)0), SV_PLDL1STRM); \ - svprfd_vnum(pg1, (void*)(base), (int64_t)4), SV_PLDL1STRM); \ - svprfd_vnum(pg1, (void*)(base), (int64_t)8), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(0), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(4), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(base), (int64_t)(8), SV_PLDL1STRM); \ } // PREFETCH_GAUGE_L2 (prefetch to L2) #define PREFETCH_GAUGE_L2_INTERNAL_A64FXd(A) \ { \ const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)-4), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)0), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)4), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)8), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)12), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)16), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)20), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)24), SV_PLDL2STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)28), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(-4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(0), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(4), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(8), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(12), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(16), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(20), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(24), SV_PLDL2STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(28), SV_PLDL2STRM); \ } // PREFETCH_GAUGE_L1 (prefetch to L1) #define PREFETCH_GAUGE_L1_INTERNAL_A64FXd(A) \ { \ const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)0), SV_PLDL1STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)4), SV_PLDL1STRM); \ - svprfd_vnum(pg1, (void*)(baseU), (int64_t)8), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(0), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(4), SV_PLDL1STRM); \ + svprfd_vnum(pg1, (void*)(baseU), (int64_t)(8), SV_PLDL1STRM); \ } // LOAD_CHI #define LOAD_CHI_A64FXd(base) \ From 6013183361d88fe7179b4fcf6b8321c0621b09ba Mon Sep 17 00:00:00 2001 From: Nils Meyer Date: Sat, 19 Dec 2020 03:25:01 +0100 Subject: [PATCH 062/399] removed Asm impls --- Grid/simd/Fujitsu_A64FX_asm_double.h | 781 --------------------------- Grid/simd/Fujitsu_A64FX_asm_single.h | 781 --------------------------- 2 files changed, 1562 deletions(-) delete mode 100644 Grid/simd/Fujitsu_A64FX_asm_double.h delete mode 100644 Grid/simd/Fujitsu_A64FX_asm_single.h diff --git a/Grid/simd/Fujitsu_A64FX_asm_double.h b/Grid/simd/Fujitsu_A64FX_asm_double.h deleted file mode 100644 index bbc4efe7..00000000 --- a/Grid/simd/Fujitsu_A64FX_asm_double.h +++ /dev/null @@ -1,781 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Fujitsu_A64FX_asm_double.h - - Copyright (C) 2020 - -Author: Nils Meyer - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - See the full license in the file "LICENSE" in the top level distribution directory -*************************************************************************************/ -/* END LEGAL */ -#define LOAD_CHIMU(base) LOAD_CHIMU_INTERLEAVED_A64FXd(base) -#define PREFETCH_CHIMU_L1(A) PREFETCH_CHIMU_L1_INTERNAL_A64FXd(A) -#define PREFETCH_GAUGE_L1(A) PREFETCH_GAUGE_L1_INTERNAL_A64FXd(A) -#define PREFETCH_CHIMU_L2(A) PREFETCH_CHIMU_L2_INTERNAL_A64FXd(A) -#define PREFETCH_GAUGE_L2(A) PREFETCH_GAUGE_L2_INTERNAL_A64FXd(A) -#define PF_GAUGE(A) -#define PREFETCH_RESULT_L2_STORE(A) PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXd(A) -#define PREFETCH_RESULT_L1_STORE(A) PREFETCH_RESULT_L1_STORE_INTERNAL_A64FXd(A) -#define PREFETCH1_CHIMU(A) PREFETCH_CHIMU_L1(A) -#define PREFETCH_CHIMU(A) PREFETCH_CHIMU_L1(A) -#define LOCK_GAUGE(A) -#define UNLOCK_GAUGE(A) -#define MASK_REGS DECLARATIONS_A64FXd -#define SAVE_RESULT(A,B) RESULT_A64FXd(A); -#define MULT_2SPIN_1(Dir) MULT_2SPIN_1_A64FXd(Dir) -#define MULT_2SPIN_2 MULT_2SPIN_2_A64FXd -#define LOAD_CHI(base) LOAD_CHI_A64FXd(base) -#define ZERO_PSI ZERO_PSI_A64FXd -#define ADD_RESULT(base,basep) LOAD_CHIMU(base); ADD_RESULT_INTERNAL_A64FXd; RESULT_A64FXd(base) -#define XP_PROJ XP_PROJ_A64FXd -#define YP_PROJ YP_PROJ_A64FXd -#define ZP_PROJ ZP_PROJ_A64FXd -#define TP_PROJ TP_PROJ_A64FXd -#define XM_PROJ XM_PROJ_A64FXd -#define YM_PROJ YM_PROJ_A64FXd -#define ZM_PROJ ZM_PROJ_A64FXd -#define TM_PROJ TM_PROJ_A64FXd -#define XP_RECON XP_RECON_A64FXd -#define XM_RECON XM_RECON_A64FXd -#define XM_RECON_ACCUM XM_RECON_ACCUM_A64FXd -#define YM_RECON_ACCUM YM_RECON_ACCUM_A64FXd -#define ZM_RECON_ACCUM ZM_RECON_ACCUM_A64FXd -#define TM_RECON_ACCUM TM_RECON_ACCUM_A64FXd -#define XP_RECON_ACCUM XP_RECON_ACCUM_A64FXd -#define YP_RECON_ACCUM YP_RECON_ACCUM_A64FXd -#define ZP_RECON_ACCUM ZP_RECON_ACCUM_A64FXd -#define TP_RECON_ACCUM TP_RECON_ACCUM_A64FXd -#define PERMUTE_DIR0 0 -#define PERMUTE_DIR1 1 -#define PERMUTE_DIR2 2 -#define PERMUTE_DIR3 3 -#define PERMUTE PERMUTE_A64FXd; -#define LOAD_TABLE(Dir) if (Dir == 0) { LOAD_TABLE0; } else if (Dir == 1) { LOAD_TABLE1; } else if (Dir == 2) { LOAD_TABLE2; } -#define MAYBEPERM(Dir,perm) if (Dir != 3) { if (perm) { PERMUTE; } } -// DECLARATIONS -#define DECLARATIONS_A64FXd \ - uint64_t baseU; \ - const uint64_t lut[4][8] = { \ - {4, 5, 6, 7, 0, 1, 2, 3}, \ - {2, 3, 0, 1, 6, 7, 4, 5}, \ - {1, 0, 3, 2, 5, 4, 7, 6}, \ - {0, 1, 2, 4, 5, 6, 7, 8} };\ -asm ( \ - "ptrue p5.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -asm ( \ - "fmov z31.d , 0 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// RESULT -#define RESULT_A64FXd(base) \ -{ \ -asm ( \ - "str z0, [%[storeptr], -6, mul vl] \n\t" \ - "str z1, [%[storeptr], -5, mul vl] \n\t" \ - "str z2, [%[storeptr], -4, mul vl] \n\t" \ - "str z3, [%[storeptr], -3, mul vl] \n\t" \ - "str z4, [%[storeptr], -2, mul vl] \n\t" \ - "str z5, [%[storeptr], -1, mul vl] \n\t" \ - "str z6, [%[storeptr], 0, mul vl] \n\t" \ - "str z7, [%[storeptr], 1, mul vl] \n\t" \ - "str z8, [%[storeptr], 2, mul vl] \n\t" \ - "str z9, [%[storeptr], 3, mul vl] \n\t" \ - "str z10, [%[storeptr], 4, mul vl] \n\t" \ - "str z11, [%[storeptr], 5, mul vl] \n\t" \ - : \ - : [storeptr] "r" (base + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_CHIMU_L2 (prefetch to L2) -#define PREFETCH_CHIMU_L2_INTERNAL_A64FXd(base) \ -{ \ -asm ( \ - "prfd PLDL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_CHIMU_L1 (prefetch to L1) -#define PREFETCH_CHIMU_L1_INTERNAL_A64FXd(base) \ -{ \ -asm ( \ - "prfd PLDL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_GAUGE_L2 (prefetch to L2) -#define PREFETCH_GAUGE_L2_INTERNAL_A64FXd(A) \ -{ \ - const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ -asm ( \ - "prfd PLDL2STRM, p5, [%[fetchptr], -4, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 12, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 16, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 20, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 24, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 28, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_GAUGE_L1 (prefetch to L1) -#define PREFETCH_GAUGE_L1_INTERNAL_A64FXd(A) \ -{ \ - const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ -asm ( \ - "prfd PLDL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHI -#define LOAD_CHI_A64FXd(base) \ -{ \ -asm ( \ - "ld1d { z12.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1d { z13.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1d { z14.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - "ld1d { z15.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1d { z16.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1d { z17.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHIMU -#define LOAD_CHIMU_INTERLEAVED_A64FXd(base) \ -{ \ -asm ( \ - "ld1d { z12.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1d { z21.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1d { z15.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1d { z18.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1d { z13.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1d { z22.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1d { z16.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1d { z19.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1d { z14.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1d { z23.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - "ld1d { z17.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1d { z20.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHIMU_0213 -#define LOAD_CHIMU_0213_A64FXd \ -{ \ - const SiteSpinor & ref(in[offset]); \ -asm ( \ - "ld1d { z12.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1d { z18.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1d { z13.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1d { z19.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1d { z14.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1d { z20.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - "ld1d { z15.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1d { z21.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1d { z16.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1d { z22.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1d { z17.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1d { z23.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (&ref[2][0]) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHIMU_0312 -#define LOAD_CHIMU_0312_A64FXd \ -{ \ - const SiteSpinor & ref(in[offset]); \ -asm ( \ - "ld1d { z12.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1d { z21.d }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1d { z13.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1d { z22.d }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1d { z14.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1d { z23.d }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - "ld1d { z15.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1d { z18.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1d { z16.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1d { z19.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1d { z17.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1d { z20.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (&ref[2][0]) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_TABLE0 -#define LOAD_TABLE0 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (0) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_TABLE1 -#define LOAD_TABLE1 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (1) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_TABLE2 -#define LOAD_TABLE2 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (2) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_TABLE3 -#define LOAD_TABLE3 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (3) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// PERMUTE -#define PERMUTE_A64FXd \ -asm ( \ - "tbl z12.d, { z12.d }, z30.d \n\t" \ - "tbl z13.d, { z13.d }, z30.d \n\t" \ - "tbl z14.d, { z14.d }, z30.d \n\t" \ - "tbl z15.d, { z15.d }, z30.d \n\t" \ - "tbl z16.d, { z16.d }, z30.d \n\t" \ - "tbl z17.d, { z17.d }, z30.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_GAUGE -#define LOAD_GAUGE(A) \ -{ \ - const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ -asm ( \ - "ld1d { z24.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1d { z25.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1d { z26.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1d { z27.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1d { z28.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1d { z29.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// MULT_2SPIN -#define MULT_2SPIN_1_A64FXd(A) \ -{ \ - const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ -asm ( \ - "ld1d { z24.d }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1d { z25.d }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1d { z26.d }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1d { z27.d }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1d { z28.d }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1d { z29.d }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "movprfx z18.d, p5/m, z31.d \n\t" \ - "fcmla z18.d, p5/m, z24.d, z12.d, 0 \n\t" \ - "movprfx z21.d, p5/m, z31.d \n\t" \ - "fcmla z21.d, p5/m, z24.d, z15.d, 0 \n\t" \ - "movprfx z19.d, p5/m, z31.d \n\t" \ - "fcmla z19.d, p5/m, z25.d, z12.d, 0 \n\t" \ - "movprfx z22.d, p5/m, z31.d \n\t" \ - "fcmla z22.d, p5/m, z25.d, z15.d, 0 \n\t" \ - "movprfx z20.d, p5/m, z31.d \n\t" \ - "fcmla z20.d, p5/m, z26.d, z12.d, 0 \n\t" \ - "movprfx z23.d, p5/m, z31.d \n\t" \ - "fcmla z23.d, p5/m, z26.d, z15.d, 0 \n\t" \ - "fcmla z18.d, p5/m, z24.d, z12.d, 90 \n\t" \ - "fcmla z21.d, p5/m, z24.d, z15.d, 90 \n\t" \ - "fcmla z19.d, p5/m, z25.d, z12.d, 90 \n\t" \ - "fcmla z22.d, p5/m, z25.d, z15.d, 90 \n\t" \ - "fcmla z20.d, p5/m, z26.d, z12.d, 90 \n\t" \ - "fcmla z23.d, p5/m, z26.d, z15.d, 90 \n\t" \ - "ld1d { z24.d }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1d { z25.d }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1d { z26.d }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// MULT_2SPIN_BACKEND -#define MULT_2SPIN_2_A64FXd \ -{ \ -asm ( \ - "fcmla z18.d, p5/m, z27.d, z13.d, 0 \n\t" \ - "fcmla z21.d, p5/m, z27.d, z16.d, 0 \n\t" \ - "fcmla z19.d, p5/m, z28.d, z13.d, 0 \n\t" \ - "fcmla z22.d, p5/m, z28.d, z16.d, 0 \n\t" \ - "fcmla z20.d, p5/m, z29.d, z13.d, 0 \n\t" \ - "fcmla z23.d, p5/m, z29.d, z16.d, 0 \n\t" \ - "fcmla z18.d, p5/m, z27.d, z13.d, 90 \n\t" \ - "fcmla z21.d, p5/m, z27.d, z16.d, 90 \n\t" \ - "fcmla z19.d, p5/m, z28.d, z13.d, 90 \n\t" \ - "fcmla z22.d, p5/m, z28.d, z16.d, 90 \n\t" \ - "fcmla z20.d, p5/m, z29.d, z13.d, 90 \n\t" \ - "fcmla z23.d, p5/m, z29.d, z16.d, 90 \n\t" \ - "fcmla z18.d, p5/m, z24.d, z14.d, 0 \n\t" \ - "fcmla z21.d, p5/m, z24.d, z17.d, 0 \n\t" \ - "fcmla z19.d, p5/m, z25.d, z14.d, 0 \n\t" \ - "fcmla z22.d, p5/m, z25.d, z17.d, 0 \n\t" \ - "fcmla z20.d, p5/m, z26.d, z14.d, 0 \n\t" \ - "fcmla z23.d, p5/m, z26.d, z17.d, 0 \n\t" \ - "fcmla z18.d, p5/m, z24.d, z14.d, 90 \n\t" \ - "fcmla z21.d, p5/m, z24.d, z17.d, 90 \n\t" \ - "fcmla z19.d, p5/m, z25.d, z14.d, 90 \n\t" \ - "fcmla z22.d, p5/m, z25.d, z17.d, 90 \n\t" \ - "fcmla z20.d, p5/m, z26.d, z14.d, 90 \n\t" \ - "fcmla z23.d, p5/m, z26.d, z17.d, 90 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XP_PROJ -#define XP_PROJ_A64FXd \ -{ \ -asm ( \ - "fcadd z12.d, p5/m, z12.d, z21.d, 90 \n\t" \ - "fcadd z13.d, p5/m, z13.d, z22.d, 90 \n\t" \ - "fcadd z14.d, p5/m, z14.d, z23.d, 90 \n\t" \ - "fcadd z15.d, p5/m, z15.d, z18.d, 90 \n\t" \ - "fcadd z16.d, p5/m, z16.d, z19.d, 90 \n\t" \ - "fcadd z17.d, p5/m, z17.d, z20.d, 90 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XP_RECON -#define XP_RECON_A64FXd \ -asm ( \ - "movprfx z6.d, p5/m, z31.d \n\t" \ - "fcadd z6.d, p5/m, z6.d, z21.d, 270 \n\t" \ - "movprfx z7.d, p5/m, z31.d \n\t" \ - "fcadd z7.d, p5/m, z7.d, z22.d, 270 \n\t" \ - "movprfx z8.d, p5/m, z31.d \n\t" \ - "fcadd z8.d, p5/m, z8.d, z23.d, 270 \n\t" \ - "movprfx z9.d, p5/m, z31.d \n\t" \ - "fcadd z9.d, p5/m, z9.d, z18.d, 270 \n\t" \ - "movprfx z10.d, p5/m, z31.d \n\t" \ - "fcadd z10.d, p5/m, z10.d, z19.d, 270 \n\t" \ - "movprfx z11.d, p5/m, z31.d \n\t" \ - "fcadd z11.d, p5/m, z11.d, z20.d, 270 \n\t" \ - "mov z0.d, p5/m, z18.d \n\t" \ - "mov z1.d, p5/m, z19.d \n\t" \ - "mov z2.d, p5/m, z20.d \n\t" \ - "mov z3.d, p5/m, z21.d \n\t" \ - "mov z4.d, p5/m, z22.d \n\t" \ - "mov z5.d, p5/m, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// XP_RECON_ACCUM -#define XP_RECON_ACCUM_A64FXd \ -asm ( \ - "fcadd z9.d, p5/m, z9.d, z18.d, 270 \n\t" \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fcadd z10.d, p5/m, z10.d, z19.d, 270 \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fcadd z11.d, p5/m, z11.d, z20.d, 270 \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fcadd z6.d, p5/m, z6.d, z21.d, 270 \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fcadd z7.d, p5/m, z7.d, z22.d, 270 \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fcadd z8.d, p5/m, z8.d, z23.d, 270 \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YP_PROJ -#define YP_PROJ_A64FXd \ -{ \ -asm ( \ - "fsub z12.d, p5/m, z12.d, z21.d \n\t" \ - "fsub z13.d, p5/m, z13.d, z22.d \n\t" \ - "fsub z14.d, p5/m, z14.d, z23.d \n\t" \ - "fadd z15.d, p5/m, z15.d, z18.d \n\t" \ - "fadd z16.d, p5/m, z16.d, z19.d \n\t" \ - "fadd z17.d, p5/m, z17.d, z20.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// ZP_PROJ -#define ZP_PROJ_A64FXd \ -{ \ -asm ( \ - "fcadd z12.d, p5/m, z12.d, z18.d, 90 \n\t" \ - "fcadd z13.d, p5/m, z13.d, z19.d, 90 \n\t" \ - "fcadd z14.d, p5/m, z14.d, z20.d, 90 \n\t" \ - "fcadd z15.d, p5/m, z15.d, z21.d, 270 \n\t" \ - "fcadd z16.d, p5/m, z16.d, z22.d, 270 \n\t" \ - "fcadd z17.d, p5/m, z17.d, z23.d, 270 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// TP_PROJ -#define TP_PROJ_A64FXd \ -{ \ -asm ( \ - "fadd z12.d, p5/m, z12.d, z18.d \n\t" \ - "fadd z13.d, p5/m, z13.d, z19.d \n\t" \ - "fadd z14.d, p5/m, z14.d, z20.d \n\t" \ - "fadd z15.d, p5/m, z15.d, z21.d \n\t" \ - "fadd z16.d, p5/m, z16.d, z22.d \n\t" \ - "fadd z17.d, p5/m, z17.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XM_PROJ -#define XM_PROJ_A64FXd \ -{ \ -asm ( \ - "fcadd z12.d, p5/m, z12.d, z21.d, 270 \n\t" \ - "fcadd z13.d, p5/m, z13.d, z22.d, 270 \n\t" \ - "fcadd z14.d, p5/m, z14.d, z23.d, 270 \n\t" \ - "fcadd z15.d, p5/m, z15.d, z18.d, 270 \n\t" \ - "fcadd z16.d, p5/m, z16.d, z19.d, 270 \n\t" \ - "fcadd z17.d, p5/m, z17.d, z20.d, 270 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XM_RECON -#define XM_RECON_A64FXd \ -asm ( \ - "movprfx z6.d, p5/m, z31.d \n\t" \ - "fcadd z6.d, p5/m, z6.d, z21.d, 90 \n\t" \ - "movprfx z7.d, p5/m, z31.d \n\t" \ - "fcadd z7.d, p5/m, z7.d, z22.d, 90 \n\t" \ - "movprfx z8.d, p5/m, z31.d \n\t" \ - "fcadd z8.d, p5/m, z8.d, z23.d, 90 \n\t" \ - "movprfx z9.d, p5/m, z31.d \n\t" \ - "fcadd z9.d, p5/m, z9.d, z18.d, 90 \n\t" \ - "movprfx z10.d, p5/m, z31.d \n\t" \ - "fcadd z10.d, p5/m, z10.d, z19.d, 90 \n\t" \ - "movprfx z11.d, p5/m, z31.d \n\t" \ - "fcadd z11.d, p5/m, z11.d, z20.d, 90 \n\t" \ - "mov z0.d, p5/m, z18.d \n\t" \ - "mov z1.d, p5/m, z19.d \n\t" \ - "mov z2.d, p5/m, z20.d \n\t" \ - "mov z3.d, p5/m, z21.d \n\t" \ - "mov z4.d, p5/m, z22.d \n\t" \ - "mov z5.d, p5/m, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YM_PROJ -#define YM_PROJ_A64FXd \ -{ \ -asm ( \ - "fadd z12.d, p5/m, z12.d, z21.d \n\t" \ - "fadd z13.d, p5/m, z13.d, z22.d \n\t" \ - "fadd z14.d, p5/m, z14.d, z23.d \n\t" \ - "fsub z15.d, p5/m, z15.d, z18.d \n\t" \ - "fsub z16.d, p5/m, z16.d, z19.d \n\t" \ - "fsub z17.d, p5/m, z17.d, z20.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// ZM_PROJ -#define ZM_PROJ_A64FXd \ -{ \ -asm ( \ - "fcadd z12.d, p5/m, z12.d, z18.d, 270 \n\t" \ - "fcadd z13.d, p5/m, z13.d, z19.d, 270 \n\t" \ - "fcadd z14.d, p5/m, z14.d, z20.d, 270 \n\t" \ - "fcadd z15.d, p5/m, z15.d, z21.d, 90 \n\t" \ - "fcadd z16.d, p5/m, z16.d, z22.d, 90 \n\t" \ - "fcadd z17.d, p5/m, z17.d, z23.d, 90 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// TM_PROJ -#define TM_PROJ_A64FXd \ -{ \ -asm ( \ - "fsub z12.d, p5/m, z12.d, z18.d \n\t" \ - "fsub z13.d, p5/m, z13.d, z19.d \n\t" \ - "fsub z14.d, p5/m, z14.d, z20.d \n\t" \ - "fsub z15.d, p5/m, z15.d, z21.d \n\t" \ - "fsub z16.d, p5/m, z16.d, z22.d \n\t" \ - "fsub z17.d, p5/m, z17.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XM_RECON_ACCUM -#define XM_RECON_ACCUM_A64FXd \ -asm ( \ - "fcadd z9.d, p5/m, z9.d, z18.d, 90 \n\t" \ - "fcadd z10.d, p5/m, z10.d, z19.d, 90 \n\t" \ - "fcadd z11.d, p5/m, z11.d, z20.d, 90 \n\t" \ - "fcadd z6.d, p5/m, z6.d, z21.d, 90 \n\t" \ - "fcadd z7.d, p5/m, z7.d, z22.d, 90 \n\t" \ - "fcadd z8.d, p5/m, z8.d, z23.d, 90 \n\t" \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YP_RECON_ACCUM -#define YP_RECON_ACCUM_A64FXd \ -asm ( \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fsub z9.d, p5/m, z9.d, z18.d \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fsub z10.d, p5/m, z10.d, z19.d \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fsub z11.d, p5/m, z11.d, z20.d \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fadd z6.d, p5/m, z6.d, z21.d \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fadd z7.d, p5/m, z7.d, z22.d \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - "fadd z8.d, p5/m, z8.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YM_RECON_ACCUM -#define YM_RECON_ACCUM_A64FXd \ -asm ( \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fadd z9.d, p5/m, z9.d, z18.d \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fadd z10.d, p5/m, z10.d, z19.d \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fadd z11.d, p5/m, z11.d, z20.d \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fsub z6.d, p5/m, z6.d, z21.d \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fsub z7.d, p5/m, z7.d, z22.d \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - "fsub z8.d, p5/m, z8.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// ZP_RECON_ACCUM -#define ZP_RECON_ACCUM_A64FXd \ -asm ( \ - "fcadd z6.d, p5/m, z6.d, z18.d, 270 \n\t" \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fcadd z7.d, p5/m, z7.d, z19.d, 270 \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fcadd z8.d, p5/m, z8.d, z20.d, 270 \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fcadd z9.d, p5/m, z9.d, z21.d, 90 \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fcadd z10.d, p5/m, z10.d, z22.d, 90 \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fcadd z11.d, p5/m, z11.d, z23.d, 90 \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// ZM_RECON_ACCUM -#define ZM_RECON_ACCUM_A64FXd \ -asm ( \ - "fcadd z6.d, p5/m, z6.d, z18.d, 90 \n\t" \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fcadd z7.d, p5/m, z7.d, z19.d, 90 \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fcadd z8.d, p5/m, z8.d, z20.d, 90 \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fcadd z9.d, p5/m, z9.d, z21.d, 270 \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fcadd z10.d, p5/m, z10.d, z22.d, 270 \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fcadd z11.d, p5/m, z11.d, z23.d, 270 \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// TP_RECON_ACCUM -#define TP_RECON_ACCUM_A64FXd \ -asm ( \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fadd z6.d, p5/m, z6.d, z18.d \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fadd z7.d, p5/m, z7.d, z19.d \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fadd z8.d, p5/m, z8.d, z20.d \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fadd z9.d, p5/m, z9.d, z21.d \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fadd z10.d, p5/m, z10.d, z22.d \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - "fadd z11.d, p5/m, z11.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// TM_RECON_ACCUM -#define TM_RECON_ACCUM_A64FXd \ -asm ( \ - "fadd z0.d, p5/m, z0.d, z18.d \n\t" \ - "fsub z6.d, p5/m, z6.d, z18.d \n\t" \ - "fadd z1.d, p5/m, z1.d, z19.d \n\t" \ - "fsub z7.d, p5/m, z7.d, z19.d \n\t" \ - "fadd z2.d, p5/m, z2.d, z20.d \n\t" \ - "fsub z8.d, p5/m, z8.d, z20.d \n\t" \ - "fadd z3.d, p5/m, z3.d, z21.d \n\t" \ - "fsub z9.d, p5/m, z9.d, z21.d \n\t" \ - "fadd z4.d, p5/m, z4.d, z22.d \n\t" \ - "fsub z10.d, p5/m, z10.d, z22.d \n\t" \ - "fadd z5.d, p5/m, z5.d, z23.d \n\t" \ - "fsub z11.d, p5/m, z11.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// ZERO_PSI -#define ZERO_PSI_A64FXd \ -asm ( \ - "fmov z0.d , 0 \n\t" \ - "fmov z1.d , 0 \n\t" \ - "fmov z2.d , 0 \n\t" \ - "fmov z3.d , 0 \n\t" \ - "fmov z4.d , 0 \n\t" \ - "fmov z5.d , 0 \n\t" \ - "fmov z6.d , 0 \n\t" \ - "fmov z7.d , 0 \n\t" \ - "fmov z8.d , 0 \n\t" \ - "fmov z9.d , 0 \n\t" \ - "fmov z10.d , 0 \n\t" \ - "fmov z11.d , 0 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// PREFETCH_RESULT_L2_STORE (uses DC ZVA for cache line zeroing) -#define PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXd(base) \ -{ \ -asm ( \ - "dc zva, %[fetchptr]\n\t" \ - "dc zva, %[fetchptr]\n\t" \ - "dc zva, %[fetchptr]\n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_RESULT_L1_STORE (prefetch store to L1) -#define PREFETCH_RESULT_L1_STORE_INTERNAL_A64FXd(base) \ -{ \ -asm ( \ - "prfd PSTL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PSTL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PSTL1STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// ADD_RESULT_INTERNAL -#define ADD_RESULT_INTERNAL_A64FXd \ -asm ( \ - "fadd z0.d, p5/m, z0.d, z12.d \n\t" \ - "fadd z1.d, p5/m, z1.d, z13.d \n\t" \ - "fadd z2.d, p5/m, z2.d, z14.d \n\t" \ - "fadd z3.d, p5/m, z3.d, z15.d \n\t" \ - "fadd z4.d, p5/m, z4.d, z16.d \n\t" \ - "fadd z5.d, p5/m, z5.d, z17.d \n\t" \ - "fadd z6.d, p5/m, z6.d, z18.d \n\t" \ - "fadd z7.d, p5/m, z7.d, z19.d \n\t" \ - "fadd z8.d, p5/m, z8.d, z20.d \n\t" \ - "fadd z9.d, p5/m, z9.d, z21.d \n\t" \ - "fadd z10.d, p5/m, z10.d, z22.d \n\t" \ - "fadd z11.d, p5/m, z11.d, z23.d \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - diff --git a/Grid/simd/Fujitsu_A64FX_asm_single.h b/Grid/simd/Fujitsu_A64FX_asm_single.h deleted file mode 100644 index e629f617..00000000 --- a/Grid/simd/Fujitsu_A64FX_asm_single.h +++ /dev/null @@ -1,781 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Fujitsu_A64FX_asm_single.h - - Copyright (C) 2020 - -Author: Nils Meyer - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - See the full license in the file "LICENSE" in the top level distribution directory -*************************************************************************************/ -/* END LEGAL */ -#define LOAD_CHIMU(base) LOAD_CHIMU_INTERLEAVED_A64FXf(base) -#define PREFETCH_CHIMU_L1(A) PREFETCH_CHIMU_L1_INTERNAL_A64FXf(A) -#define PREFETCH_GAUGE_L1(A) PREFETCH_GAUGE_L1_INTERNAL_A64FXf(A) -#define PREFETCH_CHIMU_L2(A) PREFETCH_CHIMU_L2_INTERNAL_A64FXf(A) -#define PREFETCH_GAUGE_L2(A) PREFETCH_GAUGE_L2_INTERNAL_A64FXf(A) -#define PF_GAUGE(A) -#define PREFETCH_RESULT_L2_STORE(A) PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXf(A) -#define PREFETCH_RESULT_L1_STORE(A) PREFETCH_RESULT_L1_STORE_INTERNAL_A64FXf(A) -#define PREFETCH1_CHIMU(A) PREFETCH_CHIMU_L1(A) -#define PREFETCH_CHIMU(A) PREFETCH_CHIMU_L1(A) -#define LOCK_GAUGE(A) -#define UNLOCK_GAUGE(A) -#define MASK_REGS DECLARATIONS_A64FXf -#define SAVE_RESULT(A,B) RESULT_A64FXf(A); -#define MULT_2SPIN_1(Dir) MULT_2SPIN_1_A64FXf(Dir) -#define MULT_2SPIN_2 MULT_2SPIN_2_A64FXf -#define LOAD_CHI(base) LOAD_CHI_A64FXf(base) -#define ZERO_PSI ZERO_PSI_A64FXf -#define ADD_RESULT(base,basep) LOAD_CHIMU(base); ADD_RESULT_INTERNAL_A64FXf; RESULT_A64FXf(base) -#define XP_PROJ XP_PROJ_A64FXf -#define YP_PROJ YP_PROJ_A64FXf -#define ZP_PROJ ZP_PROJ_A64FXf -#define TP_PROJ TP_PROJ_A64FXf -#define XM_PROJ XM_PROJ_A64FXf -#define YM_PROJ YM_PROJ_A64FXf -#define ZM_PROJ ZM_PROJ_A64FXf -#define TM_PROJ TM_PROJ_A64FXf -#define XP_RECON XP_RECON_A64FXf -#define XM_RECON XM_RECON_A64FXf -#define XM_RECON_ACCUM XM_RECON_ACCUM_A64FXf -#define YM_RECON_ACCUM YM_RECON_ACCUM_A64FXf -#define ZM_RECON_ACCUM ZM_RECON_ACCUM_A64FXf -#define TM_RECON_ACCUM TM_RECON_ACCUM_A64FXf -#define XP_RECON_ACCUM XP_RECON_ACCUM_A64FXf -#define YP_RECON_ACCUM YP_RECON_ACCUM_A64FXf -#define ZP_RECON_ACCUM ZP_RECON_ACCUM_A64FXf -#define TP_RECON_ACCUM TP_RECON_ACCUM_A64FXf -#define PERMUTE_DIR0 0 -#define PERMUTE_DIR1 1 -#define PERMUTE_DIR2 2 -#define PERMUTE_DIR3 3 -#define PERMUTE PERMUTE_A64FXf; -#define LOAD_TABLE(Dir) if (Dir == 0) { LOAD_TABLE0; } else if (Dir == 1) { LOAD_TABLE1 } else if (Dir == 2) { LOAD_TABLE2; } else if (Dir == 3) { LOAD_TABLE3; } -#define MAYBEPERM(A,perm) if (perm) { PERMUTE; } -// DECLARATIONS -#define DECLARATIONS_A64FXf \ - uint64_t baseU; \ - const uint32_t lut[4][16] = { \ - {8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}, \ - {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}, \ - {2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}, \ - {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14} }; \ -asm ( \ - "ptrue p5.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -asm ( \ - "fmov z31.s , 0 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// RESULT -#define RESULT_A64FXf(base) \ -{ \ -asm ( \ - "str z0, [%[storeptr], -6, mul vl] \n\t" \ - "str z1, [%[storeptr], -5, mul vl] \n\t" \ - "str z2, [%[storeptr], -4, mul vl] \n\t" \ - "str z3, [%[storeptr], -3, mul vl] \n\t" \ - "str z4, [%[storeptr], -2, mul vl] \n\t" \ - "str z5, [%[storeptr], -1, mul vl] \n\t" \ - "str z6, [%[storeptr], 0, mul vl] \n\t" \ - "str z7, [%[storeptr], 1, mul vl] \n\t" \ - "str z8, [%[storeptr], 2, mul vl] \n\t" \ - "str z9, [%[storeptr], 3, mul vl] \n\t" \ - "str z10, [%[storeptr], 4, mul vl] \n\t" \ - "str z11, [%[storeptr], 5, mul vl] \n\t" \ - : \ - : [storeptr] "r" (base + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_CHIMU_L2 (prefetch to L2) -#define PREFETCH_CHIMU_L2_INTERNAL_A64FXf(base) \ -{ \ -asm ( \ - "prfd PLDL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_CHIMU_L1 (prefetch to L1) -#define PREFETCH_CHIMU_L1_INTERNAL_A64FXf(base) \ -{ \ -asm ( \ - "prfd PLDL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_GAUGE_L2 (prefetch to L2) -#define PREFETCH_GAUGE_L2_INTERNAL_A64FXf(A) \ -{ \ - const auto & ref(U[sUn](A)); baseU = (uint64_t)&ref + 3 * 3 * 64; \ -asm ( \ - "prfd PLDL2STRM, p5, [%[fetchptr], -4, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 12, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 16, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 20, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 24, mul vl] \n\t" \ - "prfd PLDL2STRM, p5, [%[fetchptr], 28, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_GAUGE_L1 (prefetch to L1) -#define PREFETCH_GAUGE_L1_INTERNAL_A64FXf(A) \ -{ \ - const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ -asm ( \ - "prfd PLDL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PLDL1STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHI -#define LOAD_CHI_A64FXf(base) \ -{ \ -asm ( \ - "ld1w { z12.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1w { z13.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1w { z14.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - "ld1w { z15.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1w { z16.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1w { z17.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHIMU -#define LOAD_CHIMU_INTERLEAVED_A64FXf(base) \ -{ \ -asm ( \ - "ld1w { z12.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1w { z21.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1w { z15.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1w { z18.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1w { z13.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1w { z22.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1w { z16.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1w { z19.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1w { z14.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1w { z23.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - "ld1w { z17.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1w { z20.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHIMU_0213 -#define LOAD_CHIMU_0213_A64FXf \ -{ \ - const SiteSpinor & ref(in[offset]); \ -asm ( \ - "ld1w { z12.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1w { z18.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1w { z13.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1w { z19.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1w { z14.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1w { z20.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - "ld1w { z15.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1w { z21.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1w { z16.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1w { z22.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1w { z17.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1w { z23.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (&ref[2][0]) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_CHIMU_0312 -#define LOAD_CHIMU_0312_A64FXf \ -{ \ - const SiteSpinor & ref(in[offset]); \ -asm ( \ - "ld1w { z12.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1w { z21.s }, p5/z, [%[fetchptr], 3, mul vl] \n\t" \ - "ld1w { z13.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1w { z22.s }, p5/z, [%[fetchptr], 4, mul vl] \n\t" \ - "ld1w { z14.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1w { z23.s }, p5/z, [%[fetchptr], 5, mul vl] \n\t" \ - "ld1w { z15.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1w { z18.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1w { z16.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1w { z19.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "ld1w { z17.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1w { z20.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (&ref[2][0]) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// LOAD_TABLE0 -#define LOAD_TABLE0 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (0) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_TABLE1 -#define LOAD_TABLE1 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (1) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_TABLE2 -#define LOAD_TABLE2 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (2) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_TABLE3 -#define LOAD_TABLE3 \ -asm ( \ - "ldr z30, [%[tableptr], %[index], mul vl] \n\t" \ - : \ - : [tableptr] "r" (&lut[0]),[index] "i" (3) \ - : "memory","cc","p5","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// PERMUTE -#define PERMUTE_A64FXf \ -asm ( \ - "tbl z12.s, { z12.s }, z30.s \n\t" \ - "tbl z13.s, { z13.s }, z30.s \n\t" \ - "tbl z14.s, { z14.s }, z30.s \n\t" \ - "tbl z15.s, { z15.s }, z30.s \n\t" \ - "tbl z16.s, { z16.s }, z30.s \n\t" \ - "tbl z17.s, { z17.s }, z30.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// LOAD_GAUGE -#define LOAD_GAUGE(A) \ -{ \ - const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ -asm ( \ - "ld1w { z24.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1w { z25.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1w { z26.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1w { z27.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1w { z28.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1w { z29.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// MULT_2SPIN -#define MULT_2SPIN_1_A64FXf(A) \ -{ \ - const auto & ref(U[sU](A)); baseU = (uint64_t)&ref; \ -asm ( \ - "ld1w { z24.s }, p5/z, [%[fetchptr], -6, mul vl] \n\t" \ - "ld1w { z25.s }, p5/z, [%[fetchptr], -3, mul vl] \n\t" \ - "ld1w { z26.s }, p5/z, [%[fetchptr], 0, mul vl] \n\t" \ - "ld1w { z27.s }, p5/z, [%[fetchptr], -5, mul vl] \n\t" \ - "ld1w { z28.s }, p5/z, [%[fetchptr], -2, mul vl] \n\t" \ - "ld1w { z29.s }, p5/z, [%[fetchptr], 1, mul vl] \n\t" \ - "movprfx z18.s, p5/m, z31.s \n\t" \ - "fcmla z18.s, p5/m, z24.s, z12.s, 0 \n\t" \ - "movprfx z21.s, p5/m, z31.s \n\t" \ - "fcmla z21.s, p5/m, z24.s, z15.s, 0 \n\t" \ - "movprfx z19.s, p5/m, z31.s \n\t" \ - "fcmla z19.s, p5/m, z25.s, z12.s, 0 \n\t" \ - "movprfx z22.s, p5/m, z31.s \n\t" \ - "fcmla z22.s, p5/m, z25.s, z15.s, 0 \n\t" \ - "movprfx z20.s, p5/m, z31.s \n\t" \ - "fcmla z20.s, p5/m, z26.s, z12.s, 0 \n\t" \ - "movprfx z23.s, p5/m, z31.s \n\t" \ - "fcmla z23.s, p5/m, z26.s, z15.s, 0 \n\t" \ - "fcmla z18.s, p5/m, z24.s, z12.s, 90 \n\t" \ - "fcmla z21.s, p5/m, z24.s, z15.s, 90 \n\t" \ - "fcmla z19.s, p5/m, z25.s, z12.s, 90 \n\t" \ - "fcmla z22.s, p5/m, z25.s, z15.s, 90 \n\t" \ - "fcmla z20.s, p5/m, z26.s, z12.s, 90 \n\t" \ - "fcmla z23.s, p5/m, z26.s, z15.s, 90 \n\t" \ - "ld1w { z24.s }, p5/z, [%[fetchptr], -4, mul vl] \n\t" \ - "ld1w { z25.s }, p5/z, [%[fetchptr], -1, mul vl] \n\t" \ - "ld1w { z26.s }, p5/z, [%[fetchptr], 2, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (baseU + 2 * 3 * 64) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// MULT_2SPIN_BACKEND -#define MULT_2SPIN_2_A64FXf \ -{ \ -asm ( \ - "fcmla z18.s, p5/m, z27.s, z13.s, 0 \n\t" \ - "fcmla z21.s, p5/m, z27.s, z16.s, 0 \n\t" \ - "fcmla z19.s, p5/m, z28.s, z13.s, 0 \n\t" \ - "fcmla z22.s, p5/m, z28.s, z16.s, 0 \n\t" \ - "fcmla z20.s, p5/m, z29.s, z13.s, 0 \n\t" \ - "fcmla z23.s, p5/m, z29.s, z16.s, 0 \n\t" \ - "fcmla z18.s, p5/m, z27.s, z13.s, 90 \n\t" \ - "fcmla z21.s, p5/m, z27.s, z16.s, 90 \n\t" \ - "fcmla z19.s, p5/m, z28.s, z13.s, 90 \n\t" \ - "fcmla z22.s, p5/m, z28.s, z16.s, 90 \n\t" \ - "fcmla z20.s, p5/m, z29.s, z13.s, 90 \n\t" \ - "fcmla z23.s, p5/m, z29.s, z16.s, 90 \n\t" \ - "fcmla z18.s, p5/m, z24.s, z14.s, 0 \n\t" \ - "fcmla z21.s, p5/m, z24.s, z17.s, 0 \n\t" \ - "fcmla z19.s, p5/m, z25.s, z14.s, 0 \n\t" \ - "fcmla z22.s, p5/m, z25.s, z17.s, 0 \n\t" \ - "fcmla z20.s, p5/m, z26.s, z14.s, 0 \n\t" \ - "fcmla z23.s, p5/m, z26.s, z17.s, 0 \n\t" \ - "fcmla z18.s, p5/m, z24.s, z14.s, 90 \n\t" \ - "fcmla z21.s, p5/m, z24.s, z17.s, 90 \n\t" \ - "fcmla z19.s, p5/m, z25.s, z14.s, 90 \n\t" \ - "fcmla z22.s, p5/m, z25.s, z17.s, 90 \n\t" \ - "fcmla z20.s, p5/m, z26.s, z14.s, 90 \n\t" \ - "fcmla z23.s, p5/m, z26.s, z17.s, 90 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XP_PROJ -#define XP_PROJ_A64FXf \ -{ \ -asm ( \ - "fcadd z12.s, p5/m, z12.s, z21.s, 90 \n\t" \ - "fcadd z13.s, p5/m, z13.s, z22.s, 90 \n\t" \ - "fcadd z14.s, p5/m, z14.s, z23.s, 90 \n\t" \ - "fcadd z15.s, p5/m, z15.s, z18.s, 90 \n\t" \ - "fcadd z16.s, p5/m, z16.s, z19.s, 90 \n\t" \ - "fcadd z17.s, p5/m, z17.s, z20.s, 90 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XP_RECON -#define XP_RECON_A64FXf \ -asm ( \ - "movprfx z6.s, p5/m, z31.s \n\t" \ - "fcadd z6.s, p5/m, z6.s, z21.s, 270 \n\t" \ - "movprfx z7.s, p5/m, z31.s \n\t" \ - "fcadd z7.s, p5/m, z7.s, z22.s, 270 \n\t" \ - "movprfx z8.s, p5/m, z31.s \n\t" \ - "fcadd z8.s, p5/m, z8.s, z23.s, 270 \n\t" \ - "movprfx z9.s, p5/m, z31.s \n\t" \ - "fcadd z9.s, p5/m, z9.s, z18.s, 270 \n\t" \ - "movprfx z10.s, p5/m, z31.s \n\t" \ - "fcadd z10.s, p5/m, z10.s, z19.s, 270 \n\t" \ - "movprfx z11.s, p5/m, z31.s \n\t" \ - "fcadd z11.s, p5/m, z11.s, z20.s, 270 \n\t" \ - "mov z0.s, p5/m, z18.s \n\t" \ - "mov z1.s, p5/m, z19.s \n\t" \ - "mov z2.s, p5/m, z20.s \n\t" \ - "mov z3.s, p5/m, z21.s \n\t" \ - "mov z4.s, p5/m, z22.s \n\t" \ - "mov z5.s, p5/m, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// XP_RECON_ACCUM -#define XP_RECON_ACCUM_A64FXf \ -asm ( \ - "fcadd z9.s, p5/m, z9.s, z18.s, 270 \n\t" \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fcadd z10.s, p5/m, z10.s, z19.s, 270 \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fcadd z11.s, p5/m, z11.s, z20.s, 270 \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fcadd z6.s, p5/m, z6.s, z21.s, 270 \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fcadd z7.s, p5/m, z7.s, z22.s, 270 \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fcadd z8.s, p5/m, z8.s, z23.s, 270 \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YP_PROJ -#define YP_PROJ_A64FXf \ -{ \ -asm ( \ - "fsub z12.s, p5/m, z12.s, z21.s \n\t" \ - "fsub z13.s, p5/m, z13.s, z22.s \n\t" \ - "fsub z14.s, p5/m, z14.s, z23.s \n\t" \ - "fadd z15.s, p5/m, z15.s, z18.s \n\t" \ - "fadd z16.s, p5/m, z16.s, z19.s \n\t" \ - "fadd z17.s, p5/m, z17.s, z20.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// ZP_PROJ -#define ZP_PROJ_A64FXf \ -{ \ -asm ( \ - "fcadd z12.s, p5/m, z12.s, z18.s, 90 \n\t" \ - "fcadd z13.s, p5/m, z13.s, z19.s, 90 \n\t" \ - "fcadd z14.s, p5/m, z14.s, z20.s, 90 \n\t" \ - "fcadd z15.s, p5/m, z15.s, z21.s, 270 \n\t" \ - "fcadd z16.s, p5/m, z16.s, z22.s, 270 \n\t" \ - "fcadd z17.s, p5/m, z17.s, z23.s, 270 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// TP_PROJ -#define TP_PROJ_A64FXf \ -{ \ -asm ( \ - "fadd z12.s, p5/m, z12.s, z18.s \n\t" \ - "fadd z13.s, p5/m, z13.s, z19.s \n\t" \ - "fadd z14.s, p5/m, z14.s, z20.s \n\t" \ - "fadd z15.s, p5/m, z15.s, z21.s \n\t" \ - "fadd z16.s, p5/m, z16.s, z22.s \n\t" \ - "fadd z17.s, p5/m, z17.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XM_PROJ -#define XM_PROJ_A64FXf \ -{ \ -asm ( \ - "fcadd z12.s, p5/m, z12.s, z21.s, 270 \n\t" \ - "fcadd z13.s, p5/m, z13.s, z22.s, 270 \n\t" \ - "fcadd z14.s, p5/m, z14.s, z23.s, 270 \n\t" \ - "fcadd z15.s, p5/m, z15.s, z18.s, 270 \n\t" \ - "fcadd z16.s, p5/m, z16.s, z19.s, 270 \n\t" \ - "fcadd z17.s, p5/m, z17.s, z20.s, 270 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XM_RECON -#define XM_RECON_A64FXf \ -asm ( \ - "movprfx z6.s, p5/m, z31.s \n\t" \ - "fcadd z6.s, p5/m, z6.s, z21.s, 90 \n\t" \ - "movprfx z7.s, p5/m, z31.s \n\t" \ - "fcadd z7.s, p5/m, z7.s, z22.s, 90 \n\t" \ - "movprfx z8.s, p5/m, z31.s \n\t" \ - "fcadd z8.s, p5/m, z8.s, z23.s, 90 \n\t" \ - "movprfx z9.s, p5/m, z31.s \n\t" \ - "fcadd z9.s, p5/m, z9.s, z18.s, 90 \n\t" \ - "movprfx z10.s, p5/m, z31.s \n\t" \ - "fcadd z10.s, p5/m, z10.s, z19.s, 90 \n\t" \ - "movprfx z11.s, p5/m, z31.s \n\t" \ - "fcadd z11.s, p5/m, z11.s, z20.s, 90 \n\t" \ - "mov z0.s, p5/m, z18.s \n\t" \ - "mov z1.s, p5/m, z19.s \n\t" \ - "mov z2.s, p5/m, z20.s \n\t" \ - "mov z3.s, p5/m, z21.s \n\t" \ - "mov z4.s, p5/m, z22.s \n\t" \ - "mov z5.s, p5/m, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YM_PROJ -#define YM_PROJ_A64FXf \ -{ \ -asm ( \ - "fadd z12.s, p5/m, z12.s, z21.s \n\t" \ - "fadd z13.s, p5/m, z13.s, z22.s \n\t" \ - "fadd z14.s, p5/m, z14.s, z23.s \n\t" \ - "fsub z15.s, p5/m, z15.s, z18.s \n\t" \ - "fsub z16.s, p5/m, z16.s, z19.s \n\t" \ - "fsub z17.s, p5/m, z17.s, z20.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// ZM_PROJ -#define ZM_PROJ_A64FXf \ -{ \ -asm ( \ - "fcadd z12.s, p5/m, z12.s, z18.s, 270 \n\t" \ - "fcadd z13.s, p5/m, z13.s, z19.s, 270 \n\t" \ - "fcadd z14.s, p5/m, z14.s, z20.s, 270 \n\t" \ - "fcadd z15.s, p5/m, z15.s, z21.s, 90 \n\t" \ - "fcadd z16.s, p5/m, z16.s, z22.s, 90 \n\t" \ - "fcadd z17.s, p5/m, z17.s, z23.s, 90 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// TM_PROJ -#define TM_PROJ_A64FXf \ -{ \ -asm ( \ - "fsub z12.s, p5/m, z12.s, z18.s \n\t" \ - "fsub z13.s, p5/m, z13.s, z19.s \n\t" \ - "fsub z14.s, p5/m, z14.s, z20.s \n\t" \ - "fsub z15.s, p5/m, z15.s, z21.s \n\t" \ - "fsub z16.s, p5/m, z16.s, z22.s \n\t" \ - "fsub z17.s, p5/m, z17.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); \ -} -// XM_RECON_ACCUM -#define XM_RECON_ACCUM_A64FXf \ -asm ( \ - "fcadd z9.s, p5/m, z9.s, z18.s, 90 \n\t" \ - "fcadd z10.s, p5/m, z10.s, z19.s, 90 \n\t" \ - "fcadd z11.s, p5/m, z11.s, z20.s, 90 \n\t" \ - "fcadd z6.s, p5/m, z6.s, z21.s, 90 \n\t" \ - "fcadd z7.s, p5/m, z7.s, z22.s, 90 \n\t" \ - "fcadd z8.s, p5/m, z8.s, z23.s, 90 \n\t" \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YP_RECON_ACCUM -#define YP_RECON_ACCUM_A64FXf \ -asm ( \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fsub z9.s, p5/m, z9.s, z18.s \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fsub z10.s, p5/m, z10.s, z19.s \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fsub z11.s, p5/m, z11.s, z20.s \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fadd z6.s, p5/m, z6.s, z21.s \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fadd z7.s, p5/m, z7.s, z22.s \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - "fadd z8.s, p5/m, z8.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// YM_RECON_ACCUM -#define YM_RECON_ACCUM_A64FXf \ -asm ( \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fadd z9.s, p5/m, z9.s, z18.s \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fadd z10.s, p5/m, z10.s, z19.s \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fadd z11.s, p5/m, z11.s, z20.s \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fsub z6.s, p5/m, z6.s, z21.s \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fsub z7.s, p5/m, z7.s, z22.s \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - "fsub z8.s, p5/m, z8.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// ZP_RECON_ACCUM -#define ZP_RECON_ACCUM_A64FXf \ -asm ( \ - "fcadd z6.s, p5/m, z6.s, z18.s, 270 \n\t" \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fcadd z7.s, p5/m, z7.s, z19.s, 270 \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fcadd z8.s, p5/m, z8.s, z20.s, 270 \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fcadd z9.s, p5/m, z9.s, z21.s, 90 \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fcadd z10.s, p5/m, z10.s, z22.s, 90 \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fcadd z11.s, p5/m, z11.s, z23.s, 90 \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// ZM_RECON_ACCUM -#define ZM_RECON_ACCUM_A64FXf \ -asm ( \ - "fcadd z6.s, p5/m, z6.s, z18.s, 90 \n\t" \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fcadd z7.s, p5/m, z7.s, z19.s, 90 \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fcadd z8.s, p5/m, z8.s, z20.s, 90 \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fcadd z9.s, p5/m, z9.s, z21.s, 270 \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fcadd z10.s, p5/m, z10.s, z22.s, 270 \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fcadd z11.s, p5/m, z11.s, z23.s, 270 \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// TP_RECON_ACCUM -#define TP_RECON_ACCUM_A64FXf \ -asm ( \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fadd z6.s, p5/m, z6.s, z18.s \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fadd z7.s, p5/m, z7.s, z19.s \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fadd z8.s, p5/m, z8.s, z20.s \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fadd z9.s, p5/m, z9.s, z21.s \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fadd z10.s, p5/m, z10.s, z22.s \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - "fadd z11.s, p5/m, z11.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// TM_RECON_ACCUM -#define TM_RECON_ACCUM_A64FXf \ -asm ( \ - "fadd z0.s, p5/m, z0.s, z18.s \n\t" \ - "fsub z6.s, p5/m, z6.s, z18.s \n\t" \ - "fadd z1.s, p5/m, z1.s, z19.s \n\t" \ - "fsub z7.s, p5/m, z7.s, z19.s \n\t" \ - "fadd z2.s, p5/m, z2.s, z20.s \n\t" \ - "fsub z8.s, p5/m, z8.s, z20.s \n\t" \ - "fadd z3.s, p5/m, z3.s, z21.s \n\t" \ - "fsub z9.s, p5/m, z9.s, z21.s \n\t" \ - "fadd z4.s, p5/m, z4.s, z22.s \n\t" \ - "fsub z10.s, p5/m, z10.s, z22.s \n\t" \ - "fadd z5.s, p5/m, z5.s, z23.s \n\t" \ - "fsub z11.s, p5/m, z11.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// ZERO_PSI -#define ZERO_PSI_A64FXf \ -asm ( \ - "fmov z0.s , 0 \n\t" \ - "fmov z1.s , 0 \n\t" \ - "fmov z2.s , 0 \n\t" \ - "fmov z3.s , 0 \n\t" \ - "fmov z4.s , 0 \n\t" \ - "fmov z5.s , 0 \n\t" \ - "fmov z6.s , 0 \n\t" \ - "fmov z7.s , 0 \n\t" \ - "fmov z8.s , 0 \n\t" \ - "fmov z9.s , 0 \n\t" \ - "fmov z10.s , 0 \n\t" \ - "fmov z11.s , 0 \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - -// PREFETCH_RESULT_L2_STORE (uses DC ZVA for cache line zeroing) -#define PREFETCH_RESULT_L2_STORE_INTERNAL_A64FXf(base) \ -{ \ -asm ( \ - "dc zva, %[fetchptr]\n\t" \ - "dc zva, %[fetchptr]\n\t" \ - "dc zva, %[fetchptr]\n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// PREFETCH_RESULT_L1_STORE (prefetch store to L1) -#define PREFETCH_RESULT_L1_STORE_INTERNAL_A64FXf(base) \ -{ \ -asm ( \ - "prfd PSTL1STRM, p5, [%[fetchptr], 0, mul vl] \n\t" \ - "prfd PSTL1STRM, p5, [%[fetchptr], 4, mul vl] \n\t" \ - "prfd PSTL1STRM, p5, [%[fetchptr], 8, mul vl] \n\t" \ - : \ - : [fetchptr] "r" (base) \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31","memory" \ -); \ -} -// ADD_RESULT_INTERNAL -#define ADD_RESULT_INTERNAL_A64FXf \ -asm ( \ - "fadd z0.s, p5/m, z0.s, z12.s \n\t" \ - "fadd z1.s, p5/m, z1.s, z13.s \n\t" \ - "fadd z2.s, p5/m, z2.s, z14.s \n\t" \ - "fadd z3.s, p5/m, z3.s, z15.s \n\t" \ - "fadd z4.s, p5/m, z4.s, z16.s \n\t" \ - "fadd z5.s, p5/m, z5.s, z17.s \n\t" \ - "fadd z6.s, p5/m, z6.s, z18.s \n\t" \ - "fadd z7.s, p5/m, z7.s, z19.s \n\t" \ - "fadd z8.s, p5/m, z8.s, z20.s \n\t" \ - "fadd z9.s, p5/m, z9.s, z21.s \n\t" \ - "fadd z10.s, p5/m, z10.s, z22.s \n\t" \ - "fadd z11.s, p5/m, z11.s, z23.s \n\t" \ - : \ - : \ - : "p5","cc","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z10","z11","z12","z13","z14","z15","z16","z17","z18","z19","z20","z21","z22","z23","z24","z25","z26","z27","z28","z29","z30","z31" \ -); - From 45d49d86487427ea1e0b34c0d530d475f8e3e31a Mon Sep 17 00:00:00 2001 From: Nils Meyer Date: Sat, 19 Dec 2020 03:35:18 +0100 Subject: [PATCH 063/399] clean up --- .../implementation/WilsonKernelsAsmBodyA64FX.h | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h index 83588a7d..4e463438 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h @@ -26,9 +26,9 @@ Author: Nils Meyer Regensburg University *************************************************************************************/ /* END LEGAL */ -// GCC 10 messes up SVE instruction scheduling using -O3 only, -// using -O3 -fno-schedule-insns -fno-schedule-insns2 does wonders -// performance is better than armclang 20.2 +// GCC 10 messes up SVE instruction scheduling using -O3, but +// -O3 -fno-schedule-insns -fno-schedule-insns2 does wonders +// performance now is better than armclang 20.2 #ifdef KERNEL_DAG #define DIR0_PROJ XP_PROJ @@ -118,10 +118,6 @@ Author: Nils Meyer Regensburg University /* NB: picking PREFETCH_GAUGE_L2(Dir+4); here results in performance penalty though I expected that it would improve on performance - - if (s == 0) { \ - if ((Dir == 0) || (Dir == 4)) { PREFETCH_GAUGE_L2(Dir); } \ - } \ */ #define ASM_LEG_XP(Dir,NxtDir,PERMUTE_DIR,PROJ,RECON) \ @@ -149,7 +145,7 @@ NB: picking PREFETCH_GAUGE_L2(Dir+4); here results in performance penalty if ( local || st.same_node[Dir] ) { \ MULT_2SPIN_1(Dir); \ MULT_2SPIN_2; \ - RECON; \ + RECON; \ } \ base = st.GetInfo(ptype,local,perm,NxtDir,ent,plocal); ent++; \ PREFETCH_CHIMU(base); \ @@ -300,7 +296,7 @@ NB: picking PREFETCH_GAUGE_L2(Dir+4); here results in performance penalty // DC ZVA test // { uint64_t basestore = (uint64_t)&out[ss]; - // PREFETCH_RESULT_L2_STORE(basestore); } + // PREFETCH_RESULT_L2_STORE(basestore); } ASM_LEG(Ym,Zm,PERMUTE_DIR2,DIR5_PROJ,DIR5_RECON); @@ -336,8 +332,8 @@ NB: picking PREFETCH_GAUGE_L2(Dir+4); here results in performance penalty // DC ZVA test //{ uint64_t basestore = (uint64_t)&out[ss]; - // PREFETCH_RESULT_L2_STORE(basestore); - //} + // PREFETCH_RESULT_L2_STORE(basestore); } + ASM_LEG(Tm,Xp,PERMUTE_DIR0,DIR7_PROJ,DIR7_RECON); From e759367d42c14c2de4eac0349b9800b780b63988 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 8 Jan 2021 18:04:50 +0000 Subject: [PATCH 064/399] tested and working --- Grid/qcd/utils/BaryonUtils.h | 906 +++++++++++++++++------------------ 1 file changed, 427 insertions(+), 479 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 25c71e3a..35358d05 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -40,14 +40,7 @@ public: typedef typename FImpl::FermionField FermionField; typedef typename FImpl::PropagatorField PropagatorField; - typedef typename FImpl::SitePropagator pobj; - typedef typename ComplexField::vector_object vobj; - typedef Lattice> SpinMatrixField; - //typedef typename SpinMatrixField::vector_object sobj; - - //static const int epsilon[6][3] ; - //static const Real epsilon_sgn[6]; private: template accelerator_inline @@ -122,7 +115,7 @@ public: static void BaryonGamma3ptGroup1Site( const mobj &Dq1_ti, const mobj2 &Dq2_spec, - // const mobj2 &Dq3_spec, + const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -134,7 +127,7 @@ public: static void BaryonGamma3ptGroup2Site( const mobj2 &Dq1_spec, const mobj &Dq2_ti, - //const mobj2 &Dq3_spec, + const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -145,7 +138,7 @@ public: template accelerator_inline static void BaryonGamma3ptGroup3Site( const mobj2 &Dq1_spec, - //const mobj2 &Dq2_spec, + const mobj2 &Dq2_spec, const mobj &Dq3_ti, const mobj &Dq4_tf, const Gamma GammaJ, @@ -230,13 +223,7 @@ public: const std::string op, SpinMatrixField &stn_corr); }; -/* -template -const int BaryonUtils::epsilon[6][3] = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; -template -const Real BaryonUtils::epsilon_sgn[6] = {1.,1.,1.,-1.,-1.,-1.}; -*/ -//This is the old version +//This computes a baryon contraction on a lattice site, including the spin-trace of the correlation matrix template template accelerator_inline void BaryonUtils::BaryonSite(const mobj &D1, @@ -251,20 +238,20 @@ void BaryonUtils::BaryonSite(const mobj &D1, robj &result) { - Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) + Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) - auto D1_GAi = D1 * GammaA_i; - auto D1_GAi_g4 = D1_GAi * g4; - auto D1_GAi_P = 0.5*(D1_GAi + (Real)parity * D1_GAi_g4); - auto GAf_D1_GAi_P = GammaA_f * D1_GAi_P; - auto GBf_D1_GAi_P = GammaB_f * D1_GAi_P; + auto D1_GAi = D1 * GammaA_i; + auto D1_GAi_g4 = D1_GAi * g4; + auto D1_GAi_P = 0.5*(D1_GAi + (Real)parity * D1_GAi_g4); + auto GAf_D1_GAi_P = GammaA_f * D1_GAi_P; + auto GBf_D1_GAi_P = GammaB_f * D1_GAi_P; - auto D2_GBi = D2 * GammaB_i; - auto GBf_D2_GBi = GammaB_f * D2_GBi; - auto GAf_D2_GBi = GammaA_f * D2_GBi; + auto D2_GBi = D2 * GammaB_i; + auto GBf_D2_GBi = GammaB_f * D2_GBi; + auto GAf_D2_GBi = GammaA_f * D2_GBi; - auto GBf_D3 = GammaB_f * D3; - auto GAf_D3 = GammaA_f * D3; + auto GBf_D3 = GammaB_f * D3; + auto GAf_D3 = GammaA_f * D3; Real ee; @@ -273,86 +260,87 @@ void BaryonUtils::BaryonSite(const mobj &D1, int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c int eSgn_f = (ie_f < 3 ? 1 : -1); - for (int ie_i=0; ie_i < 6 ; ie_i++){ - int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' - int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' - int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_i = (ie_i < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); - ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; - //This is the \delta_{456}^{123} part - if (wick_contraction[0]){ - for (int rho=0; rho::BaryonSiteMatrix(const mobj &D1, robj &result) { - auto D1_GAi = D1 * GammaA_i; - auto GAf_D1_GAi = GammaA_f * D1_GAi; - auto GBf_D1_GAi = GammaB_f * D1_GAi; + auto D1_GAi = D1 * GammaA_i; + auto GAf_D1_GAi = GammaA_f * D1_GAi; + auto GBf_D1_GAi = GammaB_f * D1_GAi; - auto D2_GBi = D2 * GammaB_i; - auto GBf_D2_GBi = GammaB_f * D2_GBi; - auto GAf_D2_GBi = GammaA_f * D2_GBi; - - auto GBf_D3 = GammaB_f * D3; - auto GAf_D3 = GammaA_f * D3; + auto D2_GBi = D2 * GammaB_i; + auto GBf_D2_GBi = GammaB_f * D2_GBi; + auto GAf_D2_GBi = GammaA_f * D2_GBi; + auto GBf_D3 = GammaB_f * D3; + auto GAf_D3 = GammaA_f * D3; Real ee; @@ -388,96 +375,101 @@ void BaryonUtils::BaryonSiteMatrix(const mobj &D1, int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c int eSgn_f = (ie_f < 3 ? 1 : -1); - for (int ie_i=0; ie_i < 6 ; ie_i++){ - int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' - int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' - int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_i = (ie_i < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); - ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; - //This is the \delta_{456}^{123} part - if (wick_contraction[0]){ - for (int rho_i=0; rho_i::ContractBaryons(const PropagatorField &q1_left, for (int ie=0; ie < 6 ; ie++){ if(ie==0 or ie==3){ bytes += grid->oSites() * (4.*sizeof(int) + 4752.*sizeof(vComplex)) * wick_contractions[ie]; - } - else{ + } else{ bytes += grid->oSites() * (64.*sizeof(int) + 5184.*sizeof(vComplex)) * wick_contractions[ie]; } } @@ -653,7 +644,7 @@ template accelerator_inline void BaryonUtils::BaryonGamma3ptGroup1Site( const mobj &Dq1_ti, const mobj2 &Dq2_spec, - // const mobj2 &Dq3_spec, + const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -661,18 +652,14 @@ void BaryonUtils::BaryonGamma3ptGroup1Site( int wick_contraction, robj &result) { - Gamma g5(Gamma::Algebra::Gamma5); - -// auto adjD4_g_D1 = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq1_ti; - auto adjD4 = g5 * adj(Dq4_tf) * g5 ; - auto adjD4_g_D1 = adjD4 * GammaJ * Dq1_ti; - auto Gf_adjD4_g_D1 = GammaBf * adjD4_g_D1; - auto D2_Gi = Dq2_spec * GammaBi; - auto Gf_D2_Gi = GammaBf * D2_Gi; - -// auto Gf_D3 = GammaBf * Dq3_spec; // including a second mobj2 parameter leads to compilation error - auto Gf_D3 = GammaBf * Dq2_spec; //WRONG!!!!! + Gamma g5(Gamma::Algebra::Gamma5); + auto adjD4 = g5 * adj(Dq4_tf) * g5 ; + auto adjD4_g_D1 = adjD4 * GammaJ * Dq1_ti; + auto Gf_adjD4_g_D1 = GammaBf * adjD4_g_D1; + auto D2_Gi = Dq2_spec * GammaBi; + auto Gf_D2_Gi = GammaBf * D2_Gi; + auto Gf_D3 = GammaBf * Dq3_spec; Real ee; @@ -681,65 +668,65 @@ void BaryonUtils::BaryonGamma3ptGroup1Site( int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c int eSgn_f = (ie_f < 3 ? 1 : -1); - for (int ie_i=0; ie_i < 6 ; ie_i++){ - int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' - int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' - int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_i = (ie_i < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); - ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + ee = Real(eSgn_f * eSgn_i); - for (int alpha_f=0; alpha_f accelerator_inline void BaryonUtils::BaryonGamma3ptGroup2Site( const mobj2 &Dq1_spec, const mobj &Dq2_ti, - // const mobj2 &Dq3_spec, + const mobj2 &Dq3_spec, const mobj &Dq4_tf, const Gamma GammaJ, const Gamma GammaBi, @@ -759,14 +746,12 @@ void BaryonUtils::BaryonGamma3ptGroup2Site( int wick_contraction, robj &result) { - Gamma g5(Gamma::Algebra::Gamma5); - - auto adjD4_g_D2_Gi = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq2_ti * GammaBi; - auto Gf_adjD4_g_D2_Gi = GammaBf * adjD4_g_D2_Gi; - auto Gf_D1 = GammaBf * Dq1_spec; - //auto Gf_D3 = GammaBf * Dq3_spec; - auto Gf_D3 = GammaBf * Dq1_spec; // WRONG!!!!! + Gamma g5(Gamma::Algebra::Gamma5); + auto adjD4_g_D2_Gi = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq2_ti * GammaBi; + auto Gf_adjD4_g_D2_Gi = GammaBf * adjD4_g_D2_Gi; + auto Gf_D1 = GammaBf * Dq1_spec; + auto Gf_D3 = GammaBf * Dq3_spec; Real ee; @@ -775,64 +760,64 @@ void BaryonUtils::BaryonGamma3ptGroup2Site( int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c int eSgn_f = (ie_f < 3 ? 1 : -1); - for (int ie_i=0; ie_i < 6 ; ie_i++){ - int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' - int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' - int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_i = (ie_i < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); - ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; - for (int alpha_f=0; alpha_f template accelerator_inline void BaryonUtils::BaryonGamma3ptGroup3Site( const mobj2 &Dq1_spec, - // const mobj2 &Dq2_spec, + const mobj2 &Dq2_spec, const mobj &Dq3_ti, const mobj &Dq4_tf, const Gamma GammaJ, @@ -852,15 +837,13 @@ void BaryonUtils::BaryonGamma3ptGroup3Site( int wick_contraction, robj &result) { - Gamma g5(Gamma::Algebra::Gamma5); - - auto adjD4_g_D3 = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq3_ti; - auto Gf_adjD4_g_D3 = GammaBf * adjD4_g_D3; - auto Gf_D1 = GammaBf * Dq1_spec; - //auto D2_Gi = Dq2_spec * GammaBi; - auto D2_Gi = Dq1_spec * GammaBi; //WRONG!!!!!!!!!!!!!!!!! - auto Gf_D2_Gi = GammaBf * D2_Gi; + Gamma g5(Gamma::Algebra::Gamma5); + auto adjD4_g_D3 = g5 * adj(Dq4_tf) * g5 * GammaJ * Dq3_ti; + auto Gf_adjD4_g_D3 = GammaBf * adjD4_g_D3; + auto Gf_D1 = GammaBf * Dq1_spec; + auto D2_Gi = Dq2_spec * GammaBi; + auto Gf_D2_Gi = GammaBf * D2_Gi; Real ee; @@ -869,62 +852,64 @@ void BaryonUtils::BaryonGamma3ptGroup3Site( int b_f = (ie_f < 3 ? (ie_f+1)%3 : (8-ie_f)%3 ); //epsilon[ie_n][1]; //b int c_f = (ie_f < 3 ? (ie_f+2)%3 : (7-ie_f)%3 ); //epsilon[ie_n][2]; //c int eSgn_f = (ie_f < 3 ? 1 : -1); - for (int ie_i=0; ie_i < 6 ; ie_i++){ - int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' - int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' - int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_i = (ie_i < 3 ? 1 : -1); + for (int ie_i=0; ie_i < 6 ; ie_i++){ + int a_i = (ie_i < 3 ? ie_i : (6-ie_i)%3 ); //epsilon[ie_s][0]; //a' + int b_i = (ie_i < 3 ? (ie_i+1)%3 : (8-ie_i)%3 ); //epsilon[ie_s][1]; //b' + int c_i = (ie_i < 3 ? (ie_i+2)%3 : (7-ie_i)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_i = (ie_i < 3 ? 1 : -1); - ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; - for (int alpha_f=0; alpha_f::BaryonGamma3pt( assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); - GridBase *grid = q_tf.Grid(); + GridBase *grid = q_tf.Grid(); - // autoView( vcorr, stn_corr, CpuWrite); - // autoView( vq_ti , q_ti, CpuRead); - // autoView( vq_tf , q_tf, CpuRead); + autoView( vcorr , stn_corr , AcceleratorWrite); + autoView( vq_ti , q_ti , AcceleratorRead); + autoView( vq_tf , q_tf , AcceleratorRead); - // if (group == 1) { - // accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - // auto Dq_ti = vq_ti[ss]; - // auto Dq_tf = vq_tf[ss]; - // sobj result=Zero(); - // BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - // vcorr[ss] += result; - // });//end loop over lattice sites - // } else if (group == 2) { - // accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - // auto Dq_ti = vq_ti[ss]; - // auto Dq_tf = vq_tf[ss]; - // sobj result=Zero(); - // BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - // vcorr[ss] += result; - // });//end loop over lattice sites - // } else if (group == 3) { - // accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - // auto Dq_ti = vq_ti[ss]; - // auto Dq_tf = vq_tf[ss]; - // sobj result=Zero(); - // BaryonGamma3ptGroup3Site(Dq_spec1,Dq_spec2,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + Vector my_Dq_spec{Dq_spec1,Dq_spec2}; + mobj * Dq_spec_p = &my_Dq_spec[0]; - // vcorr[ss] += result; - // });//end loop over lattice sites - // } + if (group == 1) { + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec_p[0],Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites - autoView( vcorr , stn_corr , AcceleratorWrite); - autoView( vq_ti , q_ti , AcceleratorRead); - autoView( vq_tf , q_tf , AcceleratorRead); - - if (group == 1) { - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_ti = vq_ti(ss); - auto Dq_tf = vq_tf(ss); - //sobj result=Zero(); - typedef decltype(coalescedRead(vcorr[0])) spinor; - spinor result=Zero(); - //BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec1,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); //WRONG - // vcorr[ss] += result; - coalescedWrite(vcorr[ss],result); - });//end loop over lattice sites - - } else if (group == 2) { - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_ti = vq_ti(ss); - auto Dq_tf = vq_tf(ss); - //sobj result=Zero(); - typedef decltype(coalescedRead(vcorr[0])) spinor; - spinor result=Zero(); - // BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_spec2,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - BaryonGamma3ptGroup2Site(Dq_spec1,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); //WRONG - // vcorr[ss] += result; - coalescedWrite(vcorr[ss],result); - });//end loop over lattice sites - } else if (group == 3) { - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_ti = vq_ti(ss); - auto Dq_tf = vq_tf(ss); - //sobj result=Zero(); - typedef decltype(coalescedRead(vcorr[0])) spinor; - spinor result=Zero(); - //BaryonGamma3ptGroup3Site(Dq_spec1,Dq_spec2,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - BaryonGamma3ptGroup3Site(Dq_spec1,Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); //WRONG - // vcorr[ss] += result; - coalescedWrite(vcorr[ss],result); - });//end loop over lattice sites - } + } else if (group == 2) { + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + BaryonGamma3ptGroup2Site(Dq_spec_p[0],Dq_ti,Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites + } else if (group == 3) { + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + BaryonGamma3ptGroup3Site(Dq_spec_p[0],Dq_spec_p[1],Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites + } } @@ -1052,7 +1000,6 @@ void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, Gamma g5(Gamma::Algebra::Gamma5); - //auto Gn_adjDd_GH_Ds = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; auto adjDd_GH_Ds = g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; auto Gn_adjDd_GH_Ds = GammaB_nucl * adjDd_GH_Ds; auto Du_Gs = Du_spec * GammaB_sigma; @@ -1066,33 +1013,33 @@ void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, int b_n = (ie_n < 3 ? (ie_n+1)%3 : (8-ie_n)%3 ); //epsilon[ie_n][1]; //b int c_n = (ie_n < 3 ? (ie_n+2)%3 : (7-ie_n)%3 ); //epsilon[ie_n][2]; //c int eSgn_n = (ie_n < 3 ? 1 : -1); - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' - int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' - int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_s = (ie_s < 3 ? 1 : -1); + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' + int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' + int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_s = (ie_s < 3 ? 1 : -1); - ee = Real(eSgn_n * eSgn_s); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + ee = Real(eSgn_n * eSgn_s); + for (int alpha_n=0; alpha_n::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, Gamma g5(Gamma::Algebra::Gamma5); auto Du_Gs = Du_spec * GammaB_sigma; - //auto Gn_adjDd_GH_Ds = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; auto adjDd_GH_Ds = g5 * adj(Dd_tf) * g5 * Gamma_H * Ds_ti; auto Gn_adjDd_GH_Ds = GammaB_nucl * adjDd_GH_Ds; auto adjDu_GH_Du = g5 * adj(Du_tf) * g5 * Gamma_H * Du_ti; @@ -1129,40 +1075,41 @@ void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, int b_n = (ie_n < 3 ? (ie_n+1)%3 : (8-ie_n)%3 ); //epsilon[ie_n][1]; //b int c_n = (ie_n < 3 ? (ie_n+2)%3 : (7-ie_n)%3 ); //epsilon[ie_n][2]; //c int eSgn_n = (ie_n < 3 ? 1 : -1); - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' - int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' - int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_s = (ie_s < 3 ? 1 : -1); + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' + int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' + int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_s = (ie_s < 3 ? 1 : -1); - ee = Real(eSgn_n * eSgn_s); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + ee = Real(eSgn_n * eSgn_s); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; - for (int alpha_n=0; alpha_n::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, Gamma g5(Gamma::Algebra::Gamma5); - //auto Gn_adjDd_GH_Duloop_GH_Ds = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Dq_loop * Gamma_H * Ds_ti; auto adjDd_GH_Duloop_GH_Ds = g5 * adj(Dd_tf) * g5 * Gamma_H * Dq_loop * Gamma_H * Ds_ti; auto Gn_adjDd_GH_Duloop_GH_Ds = GammaB_nucl * adjDd_GH_Duloop_GH_Ds; auto Du_Gs = Du_spec * GammaB_sigma; @@ -1196,32 +1142,33 @@ void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, int b_n = (ie_n < 3 ? (ie_n+1)%3 : (8-ie_n)%3 ); //epsilon[ie_n][1]; //b int c_n = (ie_n < 3 ? (ie_n+2)%3 : (7-ie_n)%3 ); //epsilon[ie_n][2]; //c int eSgn_n = (ie_n < 3 ? 1 : -1); - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' - int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' - int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_s = (ie_s < 3 ? 1 : -1); + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' + int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' + int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_s = (ie_s < 3 ? 1 : -1); - ee = Real(eSgn_n * eSgn_s); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + ee = Real(eSgn_n * eSgn_s); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; - for (int alpha_n=0; alpha_n::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, auto Du_Gs = Du_spec * GammaB_sigma; auto adjDu_GH_Ds = g5 * adj(Du_tf) * g5 * Gamma_H * Ds_ti; - //auto Gn_adjDd_GH_Du = GammaB_nucl * g5 * adj(Dd_tf) * g5 * Gamma_H * Du_ti; auto adjDd_GH_Du = g5 * adj(Dd_tf) * g5 * Gamma_H * Du_ti; auto Gn_adjDd_GH_Du = GammaB_nucl * adjDd_GH_Du; // for some reason I needed to split this into two lines to avoid the compilation error 'error: identifier "Grid::Gamma::mul" is undefined in device code' @@ -1259,43 +1205,45 @@ void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, int b_n = (ie_n < 3 ? (ie_n+1)%3 : (8-ie_n)%3 ); //epsilon[ie_n][1]; //b int c_n = (ie_n < 3 ? (ie_n+2)%3 : (7-ie_n)%3 ); //epsilon[ie_n][2]; //c int eSgn_n = (ie_n < 3 ? 1 : -1); - for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' - int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' - int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' - int eSgn_s = (ie_s < 3 ? 1 : -1); + for (int ie_s=0; ie_s < 6 ; ie_s++){ + int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' + int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' + int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_s = (ie_s < 3 ? 1 : -1); - ee = Real(eSgn_n * eSgn_s); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; + ee = Real(eSgn_n * eSgn_s); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; - for (int alpha_n=0; alpha_n From 74de2d9742de7a85850cf40cbbe0215edc3405da Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 8 Jan 2021 18:28:36 +0000 Subject: [PATCH 065/399] whitespace changes --- Grid/qcd/utils/BaryonUtils.h | 156 +++++++++++++++++------------------ 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 35358d05..80a80a76 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -53,7 +53,7 @@ public: const Gamma GammaB_right, const int parity, const bool * wick_contractions, - robj &result); + robj &result); template accelerator_inline static void BaryonSiteMatrix(const mobj &D1, const mobj &D2, @@ -63,7 +63,7 @@ public: const Gamma GammaA_right, const Gamma GammaB_right, const bool * wick_contractions, - robj &result); + robj &result); public: static void WickContractions(std::string qi, std::string qf, @@ -117,9 +117,9 @@ public: const mobj2 &Dq2_spec, const mobj2 &Dq3_spec, const mobj &Dq4_tf, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, int wick_contraction, robj &result); @@ -129,9 +129,9 @@ public: const mobj &Dq2_ti, const mobj2 &Dq3_spec, const mobj &Dq4_tf, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, int wick_contraction, robj &result); @@ -141,9 +141,9 @@ public: const mobj2 &Dq2_spec, const mobj &Dq3_ti, const mobj &Dq4_tf, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, int wick_contraction, robj &result); public: @@ -155,9 +155,9 @@ public: const PropagatorField &q_tf, int group, int wick_contraction, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, SpinMatrixField &stn_corr); private: template accelerator_inline @@ -165,9 +165,9 @@ public: const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result); template accelerator_inline static void SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, @@ -175,9 +175,9 @@ public: const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result); @@ -186,9 +186,9 @@ public: const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result); template accelerator_inline static void SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, @@ -196,9 +196,9 @@ public: const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result); public: template @@ -209,7 +209,7 @@ public: const Gamma Gamma_H, const Gamma GammaB_sigma, const Gamma GammaB_nucl, - const std::string op, + const std::string op, SpinMatrixField &stn_corr); template static void SigmaToNucleonNonEye(const PropagatorField &qq_ti, @@ -220,7 +220,7 @@ public: const Gamma Gamma_H, const Gamma GammaB_sigma, const Gamma GammaB_nucl, - const std::string op, + const std::string op, SpinMatrixField &stn_corr); }; //This computes a baryon contraction on a lattice site, including the spin-trace of the correlation matrix @@ -229,10 +229,10 @@ template accelerator_inline void BaryonUtils::BaryonSite(const mobj &D1, const mobj &D2, const mobj &D3, - const Gamma GammaA_i, - const Gamma GammaB_i, - const Gamma GammaA_f, - const Gamma GammaB_f, + const Gamma GammaA_i, + const Gamma GammaB_i, + const Gamma GammaA_f, + const Gamma GammaB_f, const int parity, const bool * wick_contraction, robj &result) @@ -349,10 +349,10 @@ template accelerator_inline void BaryonUtils::BaryonSiteMatrix(const mobj &D1, const mobj &D2, const mobj &D3, - const Gamma GammaA_i, - const Gamma GammaB_i, - const Gamma GammaA_f, - const Gamma GammaB_f, + const Gamma GammaA_i, + const Gamma GammaB_i, + const Gamma GammaA_f, + const Gamma GammaB_f, const bool * wick_contraction, robj &result) { @@ -496,10 +496,10 @@ template void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, const PropagatorField &q2_left, const PropagatorField &q3_left, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, const bool* wick_contractions, const int parity, ComplexField &baryon_corr) @@ -548,10 +548,10 @@ template void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, const PropagatorField &q2_left, const PropagatorField &q3_left, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, const bool* wick_contractions, SpinMatrixField &baryon_corr) { @@ -612,10 +612,10 @@ template void BaryonUtils::ContractBaryonsSlicedMatrix(const mobj &D1, const mobj &D2, const mobj &D3, - const Gamma GammaA_left, - const Gamma GammaB_left, - const Gamma GammaA_right, - const Gamma GammaB_right, + const Gamma GammaA_left, + const Gamma GammaB_left, + const Gamma GammaA_right, + const Gamma GammaB_right, const bool* wick_contractions, const int nt, robj &result) @@ -646,9 +646,9 @@ void BaryonUtils::BaryonGamma3ptGroup1Site( const mobj2 &Dq2_spec, const mobj2 &Dq3_spec, const mobj &Dq4_tf, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, int wick_contraction, robj &result) { @@ -740,9 +740,9 @@ void BaryonUtils::BaryonGamma3ptGroup2Site( const mobj &Dq2_ti, const mobj2 &Dq3_spec, const mobj &Dq4_tf, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, int wick_contraction, robj &result) { @@ -831,9 +831,9 @@ void BaryonUtils::BaryonGamma3ptGroup3Site( const mobj2 &Dq2_spec, const mobj &Dq3_ti, const mobj &Dq4_tf, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, int wick_contraction, robj &result) { @@ -926,9 +926,9 @@ void BaryonUtils::BaryonGamma3pt( const PropagatorField &q_tf, int group, int wick_contraction, - const Gamma GammaJ, - const Gamma GammaBi, - const Gamma GammaBf, + const Gamma GammaJ, + const Gamma GammaBi, + const Gamma GammaBf, SpinMatrixField &stn_corr) { assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); @@ -992,9 +992,9 @@ void BaryonUtils::SigmaToNucleonQ1EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result) { @@ -1054,9 +1054,9 @@ void BaryonUtils::SigmaToNucleonQ1NonEyeSite(const mobj &Du_ti, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result) { @@ -1123,9 +1123,9 @@ void BaryonUtils::SigmaToNucleonQ2EyeSite(const mobj &Dq_loop, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result) { @@ -1183,9 +1183,9 @@ void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, const mobj2 &Du_spec, const mobj &Dd_tf, const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, robj &result) { @@ -1252,9 +1252,9 @@ void BaryonUtils::SigmaToNucleonEye(const PropagatorField &qq_loop, const mobj &Du_spec, const PropagatorField &qd_tf, const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, const std::string op, SpinMatrixField &stn_corr) { @@ -1296,9 +1296,9 @@ void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, const mobj &Du_spec, const PropagatorField &qd_tf, const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, const std::string op, SpinMatrixField &stn_corr) { From 45fc7ded3a18b8fc905127fb4d987a5bb16654e1 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 12 Jan 2021 09:10:37 +0000 Subject: [PATCH 066/399] test for sum --- Grid/qcd/utils/BaryonUtils.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 80a80a76..828b4085 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -950,7 +950,8 @@ void BaryonUtils::BaryonGamma3pt( typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec_p[0],Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],result); + //coalescedWrite(vcorr[ss],vcorr[ss]+result); //diff by factor 10??? + coalescedWrite(vcorr[ss],vcorr[ss]+result); });//end loop over lattice sites } else if (group == 2) { @@ -1271,6 +1272,9 @@ void BaryonUtils::SigmaToNucleonEye(const PropagatorField &qq_loop, bool doQ1 = (op == "Q1"); bool doQ2 = (op == "Q2"); + + Vector my_Dq_spec{Du_spec}; + mobj * Dq_spec_p = &my_Dq_spec[0]; accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_loop = vq_loop(ss); @@ -1279,9 +1283,9 @@ void BaryonUtils::SigmaToNucleonEye(const PropagatorField &qq_loop, typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); if(doQ1){ - SigmaToNucleonQ1EyeSite(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ1EyeSite(Dq_loop,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else if(doQ2){ - SigmaToNucleonQ2EyeSite(Dq_loop,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ2EyeSite(Dq_loop,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else { assert(0 && "Weak Operator not correctly specified"); } @@ -1316,6 +1320,9 @@ void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, bool doQ1 = (op == "Q1"); bool doQ2 = (op == "Q2"); + + Vector my_Dq_spec{Du_spec}; + mobj * Dq_spec_p = &my_Dq_spec[0]; accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_ti = vq_ti(ss); @@ -1325,9 +1332,9 @@ void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); if(doQ1){ - SigmaToNucleonQ1NonEyeSite(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ1NonEyeSite(Dq_ti,Dq_tf,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else if(doQ2){ - SigmaToNucleonQ2NonEyeSite(Dq_ti,Dq_tf,Du_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); + SigmaToNucleonQ2NonEyeSite(Dq_ti,Dq_tf,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); } else { assert(0 && "Weak Operator not correctly specified"); } From fa12b9a3295f1fdf392b0367de4136e576b5401e Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Wed, 13 Jan 2021 10:04:17 +0000 Subject: [PATCH 067/399] bugfix --- Grid/qcd/utils/BaryonUtils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 828b4085..69bf8959 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -961,7 +961,7 @@ void BaryonUtils::BaryonGamma3pt( typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup2Site(Dq_spec_p[0],Dq_ti,Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],result); + coalescedWrite(vcorr[ss],vcorr[ss]+result); });//end loop over lattice sites } else if (group == 3) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { @@ -970,7 +970,7 @@ void BaryonUtils::BaryonGamma3pt( typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup3Site(Dq_spec_p[0],Dq_spec_p[1],Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],result); + coalescedWrite(vcorr[ss],vcorr[ss]+result); });//end loop over lattice sites } From a4afc3ea2aeb23a5a5a4dece03087e6344c9986b Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 14 Jan 2021 20:44:16 -0500 Subject: [PATCH 068/399] Red black coarse space --- tests/solver/Test_dwf_hdcr.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/solver/Test_dwf_hdcr.cc b/tests/solver/Test_dwf_hdcr.cc index 8e083231..f68e99ab 100644 --- a/tests/solver/Test_dwf_hdcr.cc +++ b/tests/solver/Test_dwf_hdcr.cc @@ -222,9 +222,16 @@ int main (int argc, char ** argv) GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());; GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d); - GridCartesian *CoarseCoarse4d = SpaceTimeGrid::makeFourDimGrid(cclatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());; + + + GridCartesian *CoarseCoarse4d = SpaceTimeGrid::makeFourDimGrid(cclatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi()); GridCartesian *CoarseCoarse5d = SpaceTimeGrid::makeFiveDimGrid(1,CoarseCoarse4d); + GridRedBlackCartesian * Coarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(Coarse4d); + GridRedBlackCartesian * Coarse5dRB = SpaceTimeGrid::makeFiveDimRedBlackGrid(1,Coarse4d); + GridRedBlackCartesian *CoarseCoarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(CoarseCoarse4d); + GridRedBlackCartesian *CoarseCoarse5dRB = SpaceTimeGrid::makeFiveDimRedBlackGrid(1,CoarseCoarse4d); + std::vector seeds4({1,2,3,4}); std::vector seeds5({5,6,7,8}); std::vector cseeds({5,6,7,8}); @@ -282,8 +289,7 @@ int main (int argc, char ** argv) Gamma5R5HermitianLinearOperator HermIndefOp(Ddwf); - Level1Op LDOp(*Coarse5d,1); LDOp.CoarsenOperator(FGrid,HermIndefOp,Aggregates); - + Level1Op LDOp(*Coarse5d,*Coarse5dRB,1); LDOp.CoarsenOperator(FGrid,HermIndefOp,Aggregates); ////////////////////////////////////////////////// // Deflate the course space. Recursive multigrid? @@ -311,12 +317,11 @@ int main (int argc, char ** argv) } } - Level2Op L2Op(*CoarseCoarse5d,1); // Hermitian matrix + Level2Op L2Op(*CoarseCoarse5d,*CoarseCoarse5dRB,1); // Hermitian matrix typedef Level2Op::CoarseVector CoarseCoarseVector; HermitianLinearOperator L1LinOp(LDOp); L2Op.CoarsenOperator(Coarse5d,L1LinOp,CoarseAggregates); - std::cout< Date: Thu, 14 Jan 2021 20:46:21 -0500 Subject: [PATCH 069/399] Coarsened vector test --- Grid/qcd/QCD.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Grid/qcd/QCD.h b/Grid/qcd/QCD.h index 76d7def4..858aead7 100644 --- a/Grid/qcd/QCD.h +++ b/Grid/qcd/QCD.h @@ -80,6 +80,13 @@ template struct isSpinor { template using IfSpinor = Invoke::value,int> > ; template using IfNotSpinor = Invoke::value,int> > ; +const int CoarseIndex = 4; +template struct isCoarsened { + static constexpr bool value = (CoarseIndex<=T::TensorLevel); +}; +template using IfCoarsened = Invoke::value,int> > ; +template using IfNotCoarsened = Invoke::value,int> > ; + // ChrisK very keen to add extra space for Gparity doubling. // // Also add domain wall index, in a way where Wilson operator From eaff0f3aeb05635d49e17cb6e271621040f5b7f1 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 14 Jan 2021 20:46:58 -0500 Subject: [PATCH 070/399] Gamma5 on coaree spaces --- Grid/qcd/spin/TwoSpinor.h | 179 ++++++++------------------------------ 1 file changed, 35 insertions(+), 144 deletions(-) diff --git a/Grid/qcd/spin/TwoSpinor.h b/Grid/qcd/spin/TwoSpinor.h index 924594ab..8dad0cd0 100644 --- a/Grid/qcd/spin/TwoSpinor.h +++ b/Grid/qcd/spin/TwoSpinor.h @@ -128,7 +128,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void spProjTm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; hspin(0)=fspin(0)-fspin(2); hspin(1)=fspin(1)-fspin(3); } @@ -138,40 +137,50 @@ template > = 0> accelerator_inline void s * 0 0 -1 0 * 0 0 0 -1 */ - template > = 0> accelerator_inline void spProj5p (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; hspin(0)=fspin(0); hspin(1)=fspin(1); } template > = 0> accelerator_inline void spProj5m (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; hspin(0)=fspin(2); hspin(1)=fspin(3); } -// template accelerator_inline void fspProj5p (iVector &rfspin,const iVector &fspin) template > = 0> accelerator_inline void spProj5p (iVector &rfspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; rfspin(0)=fspin(0); rfspin(1)=fspin(1); rfspin(2)=Zero(); rfspin(3)=Zero(); } -// template accelerator_inline void fspProj5m (iVector &rfspin,const iVector &fspin) template > = 0> accelerator_inline void spProj5m (iVector &rfspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; rfspin(0)=Zero(); rfspin(1)=Zero(); rfspin(2)=fspin(2); rfspin(3)=fspin(3); } +template > = 0> accelerator_inline void spProj5p (iVector &rfspin,const iVector &fspin) +{ + const int hN = N>>1; + for(int s=0;s > = 0> accelerator_inline void spProj5m (iVector &rfspin,const iVector &fspin) +{ + const int hN = N>>1; + for(int s=0;s > = 0> accelerator_inline void s */ template > = 0> accelerator_inline void spReconXp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)=timesMinusI(hspin(1)); @@ -191,7 +199,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void spReconXm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)=timesI(hspin(1)); @@ -199,7 +206,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void accumReconXp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)-=timesI(hspin(1)); @@ -207,7 +213,6 @@ template > = 0> accelerator_inline void a } template > = 0> accelerator_inline void accumReconXm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)+=timesI(hspin(1)); @@ -221,7 +226,6 @@ template > = 0> accelerator_inline void a template > = 0> accelerator_inline void spReconYp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)= hspin(1); @@ -229,7 +233,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void spReconYm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)=-hspin(1); @@ -237,7 +240,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void accumReconYp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)+=hspin(1); @@ -245,7 +247,6 @@ template > = 0> accelerator_inline void a } template > = 0> accelerator_inline void accumReconYm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)-=hspin(1); @@ -260,7 +261,6 @@ template > = 0> accelerator_inline void a */ template > = 0> accelerator_inline void spReconZp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)=timesMinusI(hspin(0)); @@ -268,7 +268,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void spReconZm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)= timesI(hspin(0)); @@ -276,7 +275,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void accumReconZp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)-=timesI(hspin(0)); @@ -284,7 +282,6 @@ template > = 0> accelerator_inline void a } template > = 0> accelerator_inline void accumReconZm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)+=timesI(hspin(0)); @@ -298,7 +295,6 @@ template > = 0> accelerator_inline void a */ template > = 0> accelerator_inline void spReconTp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)=hspin(0); @@ -306,7 +302,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void spReconTm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0); fspin(1)=hspin(1); fspin(2)=-hspin(0); @@ -314,7 +309,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void accumReconTp (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)+=hspin(0); @@ -322,7 +316,6 @@ template > = 0> accelerator_inline void a } template > = 0> accelerator_inline void accumReconTm (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0); fspin(1)+=hspin(1); fspin(2)-=hspin(0); @@ -336,7 +329,6 @@ template > = 0> accelerator_inline void a */ template > = 0> accelerator_inline void spRecon5p (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=hspin(0)+hspin(0); // add is lower latency than mul fspin(1)=hspin(1)+hspin(1); // probably no measurable diffence though fspin(2)=Zero(); @@ -344,7 +336,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void spRecon5m (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)=Zero(); fspin(1)=Zero(); fspin(2)=hspin(0)+hspin(0); @@ -352,7 +343,6 @@ template > = 0> accelerator_inline void s } template > = 0> accelerator_inline void accumRecon5p (iVector &fspin,const iVector &hspin) { - //typename std::enable_if,SpinorIndex>::value,iVector >::type *SFINAE; fspin(0)+=hspin(0)+hspin(0); fspin(1)+=hspin(1)+hspin(1); } @@ -372,7 +362,6 @@ template > = 0> accelerator_inline void a ////////// template > = 0> accelerator_inline void spProjXp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconXp (iM }} } - - //////// // Xm //////// template accelerator_inline void spProjXm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProjXm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProjXm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProjXm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProjXm (iMatri template accelerator_inline void spReconXm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spReconXm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spReconXm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spReconXm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconXm (iMatr template accelerator_inline void accumReconXm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumReconXm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumReconXm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconXm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProjYp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProjYp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProjYp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProjYp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProjYp (iMatri template accelerator_inline void spReconYp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spReconYp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spReconYp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spReconYp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconYp (iMatr template accelerator_inline void accumReconYp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumReconYp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumReconYp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconYp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProjYm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProjYm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProjYm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProjYm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconYm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spReconYm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spReconYm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,const iVector >::type *temp; for(int i=0;i accelerator_inline void spReconYm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconYm (iMatr template accelerator_inline void accumReconYm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumReconYm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumReconYm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconYm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumReconYm (iM //////// template accelerator_inline void spProjZp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProjZp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProjZp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProjZp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconZp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spReconZp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spReconZp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spReconZp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumReconZp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumReconZp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumReconZp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconZp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumReconZp (iM //////// template accelerator_inline void spProjZm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProjZm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProjZm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProjZm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconZm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spReconZm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spReconZm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spReconZm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumReconZm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumReconZm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumReconZm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconZm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumReconZm (iM //////// template accelerator_inline void spProjTp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProjTp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProjTp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProjTp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconTp (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spReconTp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spReconTp (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spReconTp (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconTp (iMatr template accelerator_inline void accumReconTp (iScalar &hspin, iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumReconTp(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumReconTp (iVector &hspin, const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconTp (iMatrix &hspin, const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProjTm (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProjTm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProjTm (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProjTm (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProjTm (iMatri template accelerator_inline void spReconTm (iScalar &hspin, const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spReconTm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spReconTm (iVector &hspin, const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spReconTm (iMatrix &hspin, const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spReconTm (iMatr template accelerator_inline void accumReconTm (iScalar &hspin, const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumReconTm(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumReconTm (iVector &hspin, const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumReconTm (iMatrix &hspin, const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProj5p (iScalar &hspin,const iScalar &fspin) +template > = 0> accelerator_inline void spProj5p (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProj5p(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spProj5p (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spProj5p (iMatrix &hspin,const iMatrix &fspin) +template > = 0> accelerator_inline void spProj5p (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProj5p (iMatri template accelerator_inline void spRecon5p (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spRecon5p(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spRecon5p (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spRecon5p (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spRecon5p (iMatr template accelerator_inline void accumRecon5p (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumRecon5p(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumRecon5p (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumRecon5p (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumRecon5p (iM } // four spinor projectors for chiral proj -// template accelerator_inline void fspProj5p (iScalar &hspin,const iScalar &fspin) -template accelerator_inline void spProj5p (iScalar &hspin,const iScalar &fspin) +template > = 0> accelerator_inline void spProj5p (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProj5p(hspin._internal,fspin._internal); } -// template accelerator_inline void fspProj5p (iVector &hspin,iVector &fspin) -template > = 0> accelerator_inline void spProj5p (iVector &hspin,const iVector &fspin) +template > = 0,IfNotCoarsened > = 0> accelerator_inline void spProj5p (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void fspProj5p (iMatrix &hspin,iMatrix &fspin) -template accelerator_inline void spProj5p (iMatrix &hspin,const iMatrix &fspin) +template > = 0> accelerator_inline void spProj5p (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void spProj5p (iMatrix & // 5m //////// -template accelerator_inline void spProj5m (iScalar &hspin,const iScalar &fspin) +template > = 0> accelerator_inline void spProj5m (iScalar &hspin,const iScalar &fspin) { spProj5m(hspin._internal,fspin._internal); } -template > = 0> accelerator_inline void spProj5m (iVector &hspin,const iVector &fspin) +template > = 0,IfNotCoarsened > = 0> accelerator_inline void spProj5m (iVector &hspin,const iVector &fspin) { for(int i=0;i accelerator_inline void spProj5m (iMatrix &hspin,const iMatrix &fspin) +template > = 0> accelerator_inline void spProj5m (iMatrix &hspin,const iMatrix &fspin) { for(int i=0;i accelerator_inline void spProj5m (iMatri template accelerator_inline void spRecon5m (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spRecon5m(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void spRecon5m (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void spRecon5m (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumRecon5m (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; accumRecon5m(hspin._internal,fspin._internal); } template > = 0> accelerator_inline void accumRecon5m (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void accumRecon5m (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i accelerator_inline void accumRecon5m (iM // four spinor projectors for chiral proj -// template accelerator_inline void fspProj5m (iScalar &hspin,const iScalar &fspin) -template accelerator_inline void spProj5m (iScalar &hspin,const iScalar &fspin) +template > = 0> accelerator_inline void spProj5m (iScalar &hspin,const iScalar &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iScalar >::type *temp; spProj5m(hspin._internal,fspin._internal); } -// template accelerator_inline void fspProj5m (iVector &hspin,iVector &fspin) -template > = 0> accelerator_inline void spProj5m (iVector &hspin,const iVector &fspin) +template > = 0,IfNotCoarsened > = 0> accelerator_inline void spProj5m (iVector &hspin,const iVector &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iVector >::type *temp; for(int i=0;i accelerator_inline void fspProj5m (iMatrix &hspin,iMatrix &fspin) -template accelerator_inline void spProj5m (iMatrix &hspin,const iMatrix &fspin) +template > = 0> accelerator_inline void spProj5m (iMatrix &hspin,const iMatrix &fspin) { - //typename std::enable_if,SpinorIndex>::notvalue,iMatrix >::type *temp; for(int i=0;i Date: Thu, 14 Jan 2021 20:47:28 -0500 Subject: [PATCH 071/399] G5 on coarse spaces --- Grid/qcd/utils/LinalgUtils.h | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/utils/LinalgUtils.h b/Grid/qcd/utils/LinalgUtils.h index 1e016e4e..964b83d5 100644 --- a/Grid/qcd/utils/LinalgUtils.h +++ b/Grid/qcd/utils/LinalgUtils.h @@ -154,8 +154,8 @@ void axpby_ssp_pminus(Lattice &z,Coeff a,const Lattice &x,Coeff b,co accelerator_for(sss,nloop,vobj::Nsimd(),{ uint64_t ss = sss*Ls; decltype(coalescedRead(y_v[ss+sp])) tmp; - spProj5m(tmp,y_v(ss+sp)); - tmp = a*x_v(ss+s)+b*tmp; + spProj5m(tmp,y_v(ss+sp)); + tmp = a*x_v(ss+s)+b*tmp; coalescedWrite(z_v[ss+s],tmp); }); } @@ -188,7 +188,6 @@ void G5R5(Lattice &z,const Lattice &x) z.Checkerboard() = x.Checkerboard(); conformable(x,z); int Ls = grid->_rdimensions[0]; - Gamma G5(Gamma::Algebra::Gamma5); autoView( x_v, x, AcceleratorRead); autoView( z_v, z, AcceleratorWrite); uint64_t nloop = grid->oSites()/Ls; @@ -196,7 +195,13 @@ void G5R5(Lattice &z,const Lattice &x) uint64_t ss = sss*Ls; for(int s=0;s &z, const Lattice &x) z.Checkerboard() = x.Checkerboard(); conformable(x, z); - Gamma G5(Gamma::Algebra::Gamma5); - z = G5 * x; + autoView( x_v, x, AcceleratorRead); + autoView( z_v, z, AcceleratorWrite); + uint64_t nloop = grid->oSites(); + accelerator_for(ss,nloop,vobj::Nsimd(),{ + auto tmp = x_v(ss); + decltype(tmp) tmp_p; + decltype(tmp) tmp_m; + spProj5p(tmp_p,tmp); + spProj5m(tmp_m,tmp); + coalescedWrite(z_v[ss],tmp_p - tmp_m); + }); } +/* template void G5C(Lattice> &z, const Lattice> &x) { @@ -234,6 +249,7 @@ void G5C(Lattice> &z, const Lattice Date: Thu, 14 Jan 2021 20:48:08 -0500 Subject: [PATCH 072/399] Red black support on coars --- Grid/algorithms/CoarsenedMatrix.h | 60 +++++++++++++++++++------------ 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index 66b9c169..b9594678 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -775,7 +775,26 @@ public: for(int p=0;p FineComplexField; typedef typename Fobj::scalar_type scalar_type; + std::cout << GridLogMessage<< "CoarsenMatrix "<< std::endl; + FineComplexField one(FineGrid); one=scalar_type(1.0,0.0); FineComplexField zero(FineGrid); zero=scalar_type(0.0,0.0); @@ -847,11 +868,13 @@ public: CoarseScalar InnerProd(Grid()); + std::cout << GridLogMessage<< "CoarsenMatrix Orthog "<< std::endl; // Orthogonalise the subblocks over the basis blockOrthogonalise(InnerProd,Subspace.subspace); // Compute the matrix elements of linop between this orthonormal // set of vectors. + std::cout << GridLogMessage<< "CoarsenMatrix masks "<< std::endl; int self_stencil=-1; for(int p=0;poSites(), Fobj::Nsimd(),{ coalescedWrite(A_p[ss](j,i),oZProj_v(ss)); }); + if ( hermitian && (disp==-1) ) { + for(int pp=0;pp = * + int dirp = geom.directions[pp]; + int dispp = geom.displacements[pp]; + if ( (dirp==dir) && (dispp==1) ){ + auto sft = conjugate(Cshift(oZProj,dir,1)); + autoView( sft_v , sft , AcceleratorWrite); + autoView( A_pp , A[pp], AcceleratorWrite); + accelerator_for(ss, Grid()->oSites(), Fobj::Nsimd(),{ coalescedWrite(A_pp[ss](i,j),sft_v(ss)); }); + } + } + } } } @@ -957,33 +992,12 @@ public: } if(hermitian) { std::cout << GridLogMessage << " ForceHermitian, new code "<lSites(); From 579595f547bd36775ba42ecd07f9a881e4f12e85 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 14 Jan 2021 20:48:35 -0500 Subject: [PATCH 073/399] Red black on coarse space --- tests/solver/Test_dwf_hdcr_2level.cc | 8 ++++++-- tests/solver/Test_dwf_multigrid.cc | 9 +++++++-- tests/solver/Test_hw_multigrid.cc | 4 +++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/solver/Test_dwf_hdcr_2level.cc b/tests/solver/Test_dwf_hdcr_2level.cc index df24c9d2..4fa1e302 100644 --- a/tests/solver/Test_dwf_hdcr_2level.cc +++ b/tests/solver/Test_dwf_hdcr_2level.cc @@ -262,6 +262,8 @@ int main (int argc, char ** argv) GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());; GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d); + GridRedBlackCartesian * Coarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(Coarse4d); + GridRedBlackCartesian * Coarse5dRB = SpaceTimeGrid::makeFiveDimRedBlackGrid(1,Coarse4d); std::vector seeds4({1,2,3,4}); std::vector seeds5({5,6,7,8}); @@ -328,7 +330,7 @@ int main (int argc, char ** argv) Gamma5R5HermitianLinearOperator HermIndefOp(Ddwf); - Level1Op LDOp(*Coarse5d,1); LDOp.CoarsenOperator(FGrid,HermIndefOp,Aggregates); + Level1Op LDOp(*Coarse5d,*Coarse5dRB,1); LDOp.CoarsenOperator(FGrid,HermIndefOp,Aggregates); std::cout< CoarseCG(0.01,1000); - ConjugateGradient CoarseCG(0.02,1000);// 14.7s + ConjugateGradient CoarseCG(0.01,2000);// 14.7s + eval.resize(0); + evec.resize(0,Coarse5d); DeflatedGuesser DeflCoarseGuesser(evec,eval); NormalEquations DeflCoarseCGNE(LDOp,CoarseCG,DeflCoarseGuesser); diff --git a/tests/solver/Test_dwf_multigrid.cc b/tests/solver/Test_dwf_multigrid.cc index 9e11c160..351e10fd 100644 --- a/tests/solver/Test_dwf_multigrid.cc +++ b/tests/solver/Test_dwf_multigrid.cc @@ -370,6 +370,11 @@ int main (int argc, char ** argv) GridCartesian *CoarseCoarse4d = SpaceTimeGrid::makeFourDimGrid(cclatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());; GridCartesian *CoarseCoarse5d = SpaceTimeGrid::makeFiveDimGrid(1,CoarseCoarse4d); + GridRedBlackCartesian * Coarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(Coarse4d); + GridRedBlackCartesian * Coarse5dRB = SpaceTimeGrid::makeFiveDimRedBlackGrid(1,Coarse4d); + GridRedBlackCartesian *CoarseCoarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(CoarseCoarse4d); + GridRedBlackCartesian *CoarseCoarse5dRB = SpaceTimeGrid::makeFiveDimRedBlackGrid(1,CoarseCoarse4d); + std::vector seeds4({1,2,3,4}); std::vector seeds5({5,6,7,8}); std::vector cseeds({5,6,7,8}); @@ -434,8 +439,8 @@ int main (int argc, char ** argv) std::cout< seeds({1,2,3,4}); GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds); @@ -335,7 +337,7 @@ int main (int argc, char ** argv) NonHermitianLinearOperator LinOpDwf(Ddwf); - Level1Op LDOp (*Coarse5d,0); + Level1Op LDOp (*Coarse5d,*Coarse5dRB,0); std::cout< Date: Thu, 14 Jan 2021 20:49:13 -0500 Subject: [PATCH 074/399] Red black coarse space --- tests/solver/Test_dwf_hdcr_16_rb.cc | 397 +++++ tests/solver/Test_dwf_hdcr_24_regression.cc | 477 ++++++ tests/solver/Test_dwf_hdcr_48_rb.cc | 397 +++++ tests/solver/Test_dwf_hdcr_48_regression.cc | 473 ++++++ tests/solver/Test_hw_multigrid_mixed_48.cc | 1287 ++++++++++++++++ tests/solver/Test_hw_multigrid_mixed_48_rb.cc | 1326 +++++++++++++++++ 6 files changed, 4357 insertions(+) create mode 100644 tests/solver/Test_dwf_hdcr_16_rb.cc create mode 100644 tests/solver/Test_dwf_hdcr_24_regression.cc create mode 100644 tests/solver/Test_dwf_hdcr_48_rb.cc create mode 100644 tests/solver/Test_dwf_hdcr_48_regression.cc create mode 100644 tests/solver/Test_hw_multigrid_mixed_48.cc create mode 100644 tests/solver/Test_hw_multigrid_mixed_48_rb.cc diff --git a/tests/solver/Test_dwf_hdcr_16_rb.cc b/tests/solver/Test_dwf_hdcr_16_rb.cc new file mode 100644 index 00000000..b7900b04 --- /dev/null +++ b/tests/solver/Test_dwf_hdcr_16_rb.cc @@ -0,0 +1,397 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/Test_dwf_hdcr.cc + + Copyright (C) 2015 + +Author: Antonin Portelli +Author: Peter Boyle +Author: paboyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include +#include + +using namespace std; +using namespace Grid; +/* Params + * Grid: + * block1(4) + * block2(4) + * + * Subspace + * * Fine : Subspace(nbasis,hi,lo,order,first,step) -- 32, 60,0.02,500,100,100 + * * Coarse: Subspace(nbasis,hi,lo,order,first,step) -- 32, 18,0.02,500,100,100 + + * Smoother: + * * Fine: Cheby(hi, lo, order) -- 60,0.5,10 + * * Coarse: Cheby(hi, lo, order) -- 12,0.1,4 + + * Lanczos: + * CoarseCoarse IRL( Nk, Nm, Nstop, poly(lo,hi,order)) 24,36,24,0.002,4.0,61 + */ +RealD InverseApproximation(RealD x){ + return 1.0/x; +} + +template class SolverWrapper : public LinearFunction { +private: + CheckerBoardedSparseMatrixBase & _Matrix; + SchurRedBlackBase & _Solver; +public: + + ///////////////////////////////////////////////////// + // Wrap the usual normal equations trick + ///////////////////////////////////////////////////// + SolverWrapper(CheckerBoardedSparseMatrixBase &Matrix, + SchurRedBlackBase &Solver) + : _Matrix(Matrix), _Solver(Solver) {}; + + void operator() (const Field &in, Field &out){ + + _Solver(_Matrix,in,out); // Mdag M out = Mdag in + + } +}; + +template class ChebyshevSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & _SmootherMatrix; + FineOperator & _SmootherOperator; + + Chebyshev Cheby; + + ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator,Matrix &SmootherMatrix) : + _SmootherOperator(SmootherOperator), + _SmootherMatrix(SmootherMatrix), + Cheby(_lo,_hi,_ord,InverseApproximation) + {}; + + void operator() (const Field &in, Field &out) + { + Field tmp(in.Grid()); + MdagMLinearOperator MdagMOp(_SmootherMatrix); + _SmootherOperator.AdjOp(in,tmp); + Cheby(MdagMOp,tmp,out); + } +}; +template class MirsSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & SmootherMatrix; + FineOperator & SmootherOperator; + RealD tol; + RealD shift; + int maxit; + + MirsSmoother(RealD _shift,RealD _tol,int _maxit,FineOperator &_SmootherOperator,Matrix &_SmootherMatrix) : + shift(_shift),tol(_tol),maxit(_maxit), + SmootherOperator(_SmootherOperator), + SmootherMatrix(_SmootherMatrix) + {}; + + void operator() (const Field &in, Field &out) + { + ZeroGuesser Guess; + ConjugateGradient CG(tol,maxit,false); + + Field src(in.Grid()); + + ShiftedMdagMLinearOperator,Field> MdagMOp(SmootherMatrix,shift); + SmootherOperator.AdjOp(in,src); + Guess(src,out); + CG(MdagMOp,src,out); + } +}; + +template +class MultiGridPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef CoarsenedMatrix CoarseOperator; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + + Aggregates & _Aggregates; + CoarseOperator & _CoarseOperator; + Matrix & _FineMatrix; + FineOperator & _FineOperator; + Guesser & _Guess; + FineSmoother & _Smoother; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + +#define GridLogLevel std::cout << GridLogMessage < block ({2,2,2,2}); + std::vector blockc ({2,2,2,2}); + const int nbasis= 32; + const int nbasisc= 32; + auto clatt = GridDefaultLatt(); + for(int d=0;d seeds4({1,2,3,4}); + std::vector seeds5({5,6,7,8}); + std::vector cseeds({5,6,7,8}); + GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + GridParallelRNG CRNG(Coarse5d);CRNG.SeedFixedIntegers(cseeds); + LatticeFermion src(FGrid); gaussian(RNG5,src);// src=src+g5*src; + LatticeFermion result(FGrid); + LatticeGaugeField Umu(UGrid); + + FieldMetaData header; + std::string file("./ckpoint_lat.4000"); + //std::string file("./ckpoint_lat.1000"); + NerscIO::readConfiguration(Umu,header,file); + + std::cout< Subspace; + typedef CoarsenedMatrix CoarseOperator; + typedef CoarseOperator::CoarseVector CoarseVector; + typedef CoarseOperator::siteVector siteVector; + std::cout< HermDefOp(Ddwf); + + Subspace Aggregates(Coarse5d,FGrid,0); + + assert ( (nbasis & 0x1)==0); + { + int nb=nbasis/2; + LatticeFermion A(FGrid); + LatticeFermion B(FGrid); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.002,1000,800,100,0.0); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.02,1000,800,100,0.0); + Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.05,500,200,150,0.0);// + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.01,1000,100,100,0.0); // Slightly faster + + for(int n=0;n Level1Op; + typedef CoarsenedMatrix,nbasisc> Level2Op; + + Gamma5R5HermitianLinearOperator HermIndefOp(Ddwf); + + + GridRedBlackCartesian * Coarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(Coarse4d); + std::cout << " Making 5D coarse RB grid " <,nbasisc> CoarseSubspace; + // CoarseSubspace CoarseAggregates(CoarseCoarse5d,Coarse5d,0); + + std::cout< PosdefLdop(LDOp); + typedef Level2Op::CoarseVector CoarseCoarseVector; + CoarseVector c_src(Coarse5d); c_src=1.0; + + std::cout< , SolverWrapper > TwoLevelMG; + typedef MultiGridPreconditioner,nbasisc,Level1Op, DeflatedGuesser, NormalEquations > CoarseMG; + typedef MultiGridPreconditioner, LinearFunction > ThreeLevelMG; + + ChebyshevSmoother FineSmoother(0.5,60.0,12,HermIndefOp,Ddwf); + std::cout< CoarseZeroGuesser; + ConjugateGradient CoarseCG(0.005,1000); + // SchurDiagMooeeOperator CoarseMpcDagMpc(LDOp); + SchurRedBlackDiagMooeeSolve CoarseRBCG(CoarseCG); + SolverWrapper CoarseSolver(LDOp,CoarseRBCG); + + // NormalEquations CoarseCGNE(LDOp,CoarseCG,CoarseZeroGuesser); + TwoLevelMG TwoLevelPrecon(Aggregates, LDOp, + HermIndefOp,Ddwf, + FineSmoother, + CoarseZeroGuesser, + CoarseSolver); + TwoLevelPrecon.Level(1); + PrecGeneralisedConjugateResidual l1PGCR(1.0e-8,20,HermIndefOp,TwoLevelPrecon,16,16); + l1PGCR.Level(1); + l1PGCR(src,result); + + std::cout< pCG(1.0e-8,60000); + result=Zero(); + // pCG(HermDefOp,src,result); + + std::cout< HermOpEO(Ddwf); + // pCG(HermOpEO,src_o,result_o); + + std::cout< PM; PM(HermDefOp,src); + std::cout< cPM; cPM(PosdefLdop,c_src); + // std::cout< ccPM; ccPM(IRLHermOpL2,cc_src); + + std::cout< +Author: Peter Boyle +Author: paboyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include +#include + +using namespace std; +using namespace Grid; +/* Params + * Grid: + * block1(4) + * block2(4) + * + * Subspace + * * Fine : Subspace(nbasis,hi,lo,order,first,step) -- 32, 60,0.02,500,100,100 + * * Coarse: Subspace(nbasis,hi,lo,order,first,step) -- 32, 18,0.02,500,100,100 + + * Smoother: + * * Fine: Cheby(hi, lo, order) -- 60,0.5,10 + * * Coarse: Cheby(hi, lo, order) -- 12,0.1,4 + + * Lanczos: + * CoarseCoarse IRL( Nk, Nm, Nstop, poly(lo,hi,order)) 24,36,24,0.002,4.0,61 + */ +RealD InverseApproximation(RealD x){ + return 1.0/x; +} + +template class ChebyshevSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & _SmootherMatrix; + FineOperator & _SmootherOperator; + + Chebyshev Cheby; + + ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator,Matrix &SmootherMatrix) : + _SmootherOperator(SmootherOperator), + _SmootherMatrix(SmootherMatrix), + Cheby(_lo,_hi,_ord,InverseApproximation) + {}; + + void operator() (const Field &in, Field &out) + { + Field tmp(in.Grid()); + MdagMLinearOperator MdagMOp(_SmootherMatrix); + _SmootherOperator.AdjOp(in,tmp); + Cheby(MdagMOp,tmp,out); + } +}; +template class MirsSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & SmootherMatrix; + FineOperator & SmootherOperator; + RealD tol; + RealD shift; + int maxit; + + MirsSmoother(RealD _shift,RealD _tol,int _maxit,FineOperator &_SmootherOperator,Matrix &_SmootherMatrix) : + shift(_shift),tol(_tol),maxit(_maxit), + SmootherOperator(_SmootherOperator), + SmootherMatrix(_SmootherMatrix) + {}; + + void operator() (const Field &in, Field &out) + { + ZeroGuesser Guess; + ConjugateGradient CG(tol,maxit,false); + + Field src(in.Grid()); + + ShiftedMdagMLinearOperator,Field> MdagMOp(SmootherMatrix,shift); + SmootherOperator.AdjOp(in,src); + Guess(src,out); + CG(MdagMOp,src,out); + } +}; + +template +class MultiGridPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef CoarsenedMatrix CoarseOperator; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + + Aggregates & _Aggregates; + CoarseOperator & _CoarseOperator; + Matrix & _FineMatrix; + FineOperator & _FineOperator; + Guesser & _Guess; + FineSmoother & _Smoother; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + +#define GridLogLevel std::cout << GridLogMessage < block ({2,2,2,2}); + std::vector blockc ({2,2,2,2}); + const int nbasis= 40; + const int nbasisc= 40; + auto clatt = GridDefaultLatt(); + for(int d=0;d seeds4({1,2,3,4}); + std::vector seeds5({5,6,7,8}); + std::vector cseeds({5,6,7,8}); + GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + GridParallelRNG CRNG(Coarse5d);CRNG.SeedFixedIntegers(cseeds); + LatticeFermion src(FGrid); gaussian(RNG5,src);// src=src+g5*src; + LatticeFermion result(FGrid); + LatticeGaugeField Umu(UGrid); + + FieldMetaData header; + // std::string file("./ckpoint_lat.4000"); + // std::string file("./ckpoint_lat.1000"); + // NerscIO::readConfiguration(Umu,header,file); + SU::HotConfiguration(RNG4,Umu); + + std::cout< Subspace; + typedef CoarsenedMatrix CoarseOperator; + typedef CoarseOperator::CoarseVector CoarseVector; + typedef CoarseOperator::siteVector siteVector; + std::cout< HermDefOp(Ddwf); + + Subspace Aggregates(Coarse5d,FGrid,0); + + assert ( (nbasis & 0x1)==0); + { + int nb=nbasis/2; + LatticeFermion A(FGrid); + LatticeFermion B(FGrid); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.002,1000,800,100,0.0); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.02,1000,800,100,0.0); + Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.01,400,50,50,0.0); // Slightly faster + + for(int n=0;n Level1Op; + typedef CoarsenedMatrix,nbasisc> Level2Op; + + Gamma5R5HermitianLinearOperator HermIndefOp(Ddwf); + + + GridRedBlackCartesian * Coarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(Coarse4d); + std::cout << " Making 5D coarse RB grid " <,nbasisc> CoarseSubspace; + // CoarseSubspace CoarseAggregates(CoarseCoarse5d,Coarse5d,0); + + std::cout< PosdefLdop(LDOp); + /* + { + int nb=nbasisc/2; + CoarseAggregates.CreateSubspaceChebyshev(CRNG,PosdefLdop,nb,15.0,0.02,1000,800,100,0.0); + for(int n=0;noSites();site++){ + subspace_g5[site](nn) = subspace[site](nn); + subspace_g5[site](nn+nb)=-subspace[site](nn+nb); + } + } + } + } + */ + typedef Level2Op::CoarseVector CoarseCoarseVector; + /* + Level2Op L2Op(*CoarseCoarse5d,1); // Hermitian matrix + HermitianLinearOperator L1LinOp(LDOp); + L2Op.CoarsenOperator(Coarse5d,L1LinOp,CoarseAggregates); + + + std::cout< IRLHermOpL2(L2Op); + CoarseCoarseVector cc_src(CoarseCoarse5d); cc_src=1.0; + */ + /* + Chebyshev IRLChebyL2(0.001,15.0,301); + FunctionHermOp IRLOpChebyL2(IRLChebyL2,IRLHermOpL2); + PlainHermOp IRLOpL2 (IRLHermOpL2); + int cNk=24; + int cNm=36; + int cNstop=24; + ImplicitlyRestartedLanczos IRLL2(IRLOpChebyL2,IRLOpL2,cNstop,cNk,cNm,1.0e-3,20); + + int cNconv; + std::vector eval2(cNm); + std::vector evec2(cNm,CoarseCoarse5d); + IRLL2.calc(eval2,evec2,cc_src,cNconv); + + ConjugateGradient CoarseCoarseCG(0.1,1000); + DeflatedGuesser DeflCoarseCoarseGuesser(evec2,eval2); + NormalEquations DeflCoarseCoarseCGNE(L2Op,CoarseCoarseCG,DeflCoarseCoarseGuesser); + */ + + /* + std::cout< IRLHermOp(LDOp); + // Chebyshev IRLCheby(0.001,15.0,301); + Chebyshev IRLCheby(0.03,12.0,101); + FunctionHermOp IRLOpCheby(IRLCheby,IRLHermOp); + PlainHermOp IRLOp (IRLHermOp); + int Nk=64; + int Nm=128; + int Nstop=Nk; + ImplicitlyRestartedLanczos IRL(IRLOpCheby,IRLOp,Nstop,Nk,Nm,1.0e-3,20); + + int Nconv; + std::vector eval(Nm); + std::vector evec(Nm,Coarse5d); + IRL.calc(eval,evec,c_src,Nconv); + */ + CoarseVector c_src(Coarse5d); c_src=1.0; + // DeflatedGuesser DeflCoarseGuesser(evec,eval); + // NormalEquations DeflCoarseCGNE(LDOp,CoarseCG,DeflCoarseGuesser); + + std::cout< , NormalEquations > TwoLevelMG; + typedef MultiGridPreconditioner , NormalEquations > TwoLevelMG; + typedef MultiGridPreconditioner,nbasisc,Level1Op, DeflatedGuesser, NormalEquations > CoarseMG; + typedef MultiGridPreconditioner, LinearFunction > ThreeLevelMG; + + ChebyshevSmoother FineSmoother(0.25,60.0,12,HermIndefOp,Ddwf); + /* + // MultiGrid preconditioner acting on the coarse space <-> coarsecoarse space + ChebyshevSmoother CoarseSmoother(0.1,15.0,3,L1LinOp,LDOp); + + // MirsSmoother CoarseCGSmoother(0.1,0.1,4,L1LinOp,LDOp); + // MirsSmoother FineCGSmoother(0.0,0.01,8,HermIndefOp,Ddwf); + + CoarseMG Level2Precon (CoarseAggregates, L2Op, + L1LinOp,LDOp, + CoarseSmoother, + DeflCoarseCoarseGuesser, + DeflCoarseCoarseCGNE); + Level2Precon.Level(2); + + // PGCR Applying this solver to solve the coarse space problem + PrecGeneralisedConjugateResidual l2PGCR(0.1, 100, L1LinOp,Level2Precon,16,16); + l2PGCR.Level(2); + + // Wrap the 2nd level solver in a MultiGrid preconditioner acting on the fine space + ZeroGuesser CoarseZeroGuesser; + ThreeLevelMG ThreeLevelPrecon(Aggregates, LDOp, + HermIndefOp,Ddwf, + FineSmoother, + CoarseZeroGuesser, + l2PGCR); + ThreeLevelPrecon.Level(1); + + // Apply the fine-coarse-coarsecoarse 2 deep MG preconditioner in an outer PGCR on the fine fgrid + PrecGeneralisedConjugateResidual l1PGCR(1.0e-8,1000,HermIndefOp,ThreeLevelPrecon,16,16); + l1PGCR.Level(1); + */ + std::cout< CoarseZeroGuesser; + ConjugateGradient CoarseCG(0.01,1000); + NormalEquations CoarseCGNE(LDOp,CoarseCG,CoarseZeroGuesser); + TwoLevelMG TwoLevelPrecon(Aggregates, LDOp, + HermIndefOp,Ddwf, + FineSmoother, + CoarseZeroGuesser, + CoarseCGNE); + TwoLevelPrecon.Level(1); + PrecGeneralisedConjugateResidual l1PGCR(1.0e-8,20,HermIndefOp,TwoLevelPrecon,16,16); + l1PGCR.Level(1); + l1PGCR(src,result); + + std::cout< pCG(1.0e-8,60000); + result=Zero(); + // pCG(HermDefOp,src,result); + + std::cout< HermOpEO(Ddwf); + // pCG(HermOpEO,src_o,result_o); + + std::cout< PM; PM(HermDefOp,src); + std::cout< cPM; cPM(PosdefLdop,c_src); + // std::cout< ccPM; ccPM(IRLHermOpL2,cc_src); + + std::cout< +Author: Peter Boyle +Author: paboyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include +#include + +using namespace std; +using namespace Grid; +/* Params + * Grid: + * block1(4) + * block2(4) + * + * Subspace + * * Fine : Subspace(nbasis,hi,lo,order,first,step) -- 32, 60,0.02,500,100,100 + * * Coarse: Subspace(nbasis,hi,lo,order,first,step) -- 32, 18,0.02,500,100,100 + + * Smoother: + * * Fine: Cheby(hi, lo, order) -- 60,0.5,10 + * * Coarse: Cheby(hi, lo, order) -- 12,0.1,4 + + * Lanczos: + * CoarseCoarse IRL( Nk, Nm, Nstop, poly(lo,hi,order)) 24,36,24,0.002,4.0,61 + */ +RealD InverseApproximation(RealD x){ + return 1.0/x; +} + +template class SolverWrapper : public LinearFunction { +private: + CheckerBoardedSparseMatrixBase & _Matrix; + SchurRedBlackBase & _Solver; +public: + + ///////////////////////////////////////////////////// + // Wrap the usual normal equations trick + ///////////////////////////////////////////////////// + SolverWrapper(CheckerBoardedSparseMatrixBase &Matrix, + SchurRedBlackBase &Solver) + : _Matrix(Matrix), _Solver(Solver) {}; + + void operator() (const Field &in, Field &out){ + + _Solver(_Matrix,in,out); // Mdag M out = Mdag in + + } +}; + +template class ChebyshevSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & _SmootherMatrix; + FineOperator & _SmootherOperator; + + Chebyshev Cheby; + + ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator,Matrix &SmootherMatrix) : + _SmootherOperator(SmootherOperator), + _SmootherMatrix(SmootherMatrix), + Cheby(_lo,_hi,_ord,InverseApproximation) + {}; + + void operator() (const Field &in, Field &out) + { + Field tmp(in.Grid()); + MdagMLinearOperator MdagMOp(_SmootherMatrix); + _SmootherOperator.AdjOp(in,tmp); + Cheby(MdagMOp,tmp,out); + } +}; +template class MirsSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & SmootherMatrix; + FineOperator & SmootherOperator; + RealD tol; + RealD shift; + int maxit; + + MirsSmoother(RealD _shift,RealD _tol,int _maxit,FineOperator &_SmootherOperator,Matrix &_SmootherMatrix) : + shift(_shift),tol(_tol),maxit(_maxit), + SmootherOperator(_SmootherOperator), + SmootherMatrix(_SmootherMatrix) + {}; + + void operator() (const Field &in, Field &out) + { + ZeroGuesser Guess; + ConjugateGradient CG(tol,maxit,false); + + Field src(in.Grid()); + + ShiftedMdagMLinearOperator,Field> MdagMOp(SmootherMatrix,shift); + SmootherOperator.AdjOp(in,src); + Guess(src,out); + CG(MdagMOp,src,out); + } +}; + +template +class MultiGridPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef CoarsenedMatrix CoarseOperator; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + + Aggregates & _Aggregates; + CoarseOperator & _CoarseOperator; + Matrix & _FineMatrix; + FineOperator & _FineOperator; + Guesser & _Guess; + FineSmoother & _Smoother; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + +#define GridLogLevel std::cout << GridLogMessage < block ({2,2,2,2}); + //std::vector block ({2,2,2,2}); + const int nbasis= 40; + const int nbasisc= 40; + auto clatt = GridDefaultLatt(); + for(int d=0;d seeds4({1,2,3,4}); + std::vector seeds5({5,6,7,8}); + std::vector cseeds({5,6,7,8}); + GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + GridParallelRNG CRNG(Coarse5d);CRNG.SeedFixedIntegers(cseeds); + LatticeFermion src(FGrid); gaussian(RNG5,src);// src=src+g5*src; + LatticeFermion result(FGrid); + LatticeGaugeField Umu(UGrid); + + FieldMetaData header; + //std::string file("./ckpoint_lat.4000"); + std::string file("./ckpoint_lat.1000"); + NerscIO::readConfiguration(Umu,header,file); + + std::cout< Subspace; + typedef CoarsenedMatrix CoarseOperator; + typedef CoarseOperator::CoarseVector CoarseVector; + typedef CoarseOperator::siteVector siteVector; + std::cout< HermDefOp(Ddwf); + + Subspace Aggregates(Coarse5d,FGrid,0); + + assert ( (nbasis & 0x1)==0); + { + int nb=nbasis/2; + LatticeFermion A(FGrid); + LatticeFermion B(FGrid); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.002,1000,800,100,0.0); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.02,1000,800,100,0.0); + Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.01,1000,100,100,0.0); // Slightly faster + + for(int n=0;n Level1Op; + typedef CoarsenedMatrix,nbasisc> Level2Op; + + Gamma5R5HermitianLinearOperator HermIndefOp(Ddwf); + + + GridRedBlackCartesian * Coarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(Coarse4d); + GridRedBlackCartesian * Coarse5dRB = SpaceTimeGrid::makeFiveDimRedBlackGrid(1,Coarse4d); + + Level1Op LDOp(*Coarse5d,*Coarse5dRB,1); LDOp.CoarsenOperator(FGrid,HermIndefOp,Aggregates); + + ////////////////////////////////////////////////// + // Deflate the course space. Recursive multigrid? + ////////////////////////////////////////////////// + typedef Aggregation,nbasisc> CoarseSubspace; + // CoarseSubspace CoarseAggregates(CoarseCoarse5d,Coarse5d,0); + + std::cout< PosdefLdop(LDOp); + typedef Level2Op::CoarseVector CoarseCoarseVector; + CoarseVector c_src(Coarse5d); c_src=1.0; + + std::cout< , SolverWrapper > TwoLevelMG; + typedef MultiGridPreconditioner,nbasisc,Level1Op, DeflatedGuesser, NormalEquations > CoarseMG; + typedef MultiGridPreconditioner, LinearFunction > ThreeLevelMG; + + std::cout< tols({0.015}); + std::vector ords({12}); + std::vector los({0.8}); + for(int l=0;l FineSmoother(los[l],60.0,ords[o],HermIndefOp,Ddwf); + ZeroGuesser CoarseZeroGuesser; + ConjugateGradient CoarseCG(tols[t],10000); + SchurRedBlackDiagMooeeSolve CoarseRBCG(CoarseCG); + SolverWrapper CoarseSolver(LDOp,CoarseRBCG); + + TwoLevelMG TwoLevelPrecon(Aggregates, LDOp, + HermIndefOp,Ddwf, + FineSmoother, + CoarseZeroGuesser, + CoarseSolver); + TwoLevelPrecon.Level(1); + PrecGeneralisedConjugateResidual l1PGCR(1.0e-8,20,HermIndefOp,TwoLevelPrecon,16,16); + l1PGCR.Level(1); + l1PGCR(src,result); + }}} + + ConjugateGradient pCG(1.0e-8,60000); + std::cout< HermOpEO(Ddwf); + pCG(HermOpEO,src_o,result_o); + + std::cout< PM; PM(HermDefOp,src); + std::cout< cPM; cPM(PosdefLdop,c_src); + // std::cout< ccPM; ccPM(IRLHermOpL2,cc_src); + + std::cout< +Author: Peter Boyle +Author: paboyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include +#include + +using namespace std; +using namespace Grid; +/* Params + * Grid: + * block1(4) + * block2(4) + * + * Subspace + * * Fine : Subspace(nbasis,hi,lo,order,first,step) -- 32, 60,0.02,500,100,100 + * * Coarse: Subspace(nbasis,hi,lo,order,first,step) -- 32, 18,0.02,500,100,100 + + * Smoother: + * * Fine: Cheby(hi, lo, order) -- 60,0.5,10 + * * Coarse: Cheby(hi, lo, order) -- 12,0.1,4 + + * Lanczos: + * CoarseCoarse IRL( Nk, Nm, Nstop, poly(lo,hi,order)) 24,36,24,0.002,4.0,61 + */ +RealD InverseApproximation(RealD x){ + return 1.0/x; +} + +template class ChebyshevSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & _SmootherMatrix; + FineOperator & _SmootherOperator; + + Chebyshev Cheby; + + ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator,Matrix &SmootherMatrix) : + _SmootherOperator(SmootherOperator), + _SmootherMatrix(SmootherMatrix), + Cheby(_lo,_hi,_ord,InverseApproximation) + {}; + + void operator() (const Field &in, Field &out) + { + Field tmp(in.Grid()); + MdagMLinearOperator MdagMOp(_SmootherMatrix); + _SmootherOperator.AdjOp(in,tmp); + Cheby(MdagMOp,tmp,out); + } +}; +template class MirsSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & SmootherMatrix; + FineOperator & SmootherOperator; + RealD tol; + RealD shift; + int maxit; + + MirsSmoother(RealD _shift,RealD _tol,int _maxit,FineOperator &_SmootherOperator,Matrix &_SmootherMatrix) : + shift(_shift),tol(_tol),maxit(_maxit), + SmootherOperator(_SmootherOperator), + SmootherMatrix(_SmootherMatrix) + {}; + + void operator() (const Field &in, Field &out) + { + ZeroGuesser Guess; + ConjugateGradient CG(tol,maxit,false); + + Field src(in.Grid()); + + ShiftedMdagMLinearOperator,Field> MdagMOp(SmootherMatrix,shift); + SmootherOperator.AdjOp(in,src); + Guess(src,out); + CG(MdagMOp,src,out); + } +}; + +template +class MultiGridPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef CoarsenedMatrix CoarseOperator; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + + Aggregates & _Aggregates; + CoarseOperator & _CoarseOperator; + Matrix & _FineMatrix; + FineOperator & _FineOperator; + Guesser & _Guess; + FineSmoother & _Smoother; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + +#define GridLogLevel std::cout << GridLogMessage < block ({2,2,2,2}); + std::vector blockc ({2,2,2,2}); + const int nbasis= 40; + const int nbasisc= 40; + auto clatt = GridDefaultLatt(); + for(int d=0;d seeds4({1,2,3,4}); + std::vector seeds5({5,6,7,8}); + std::vector cseeds({5,6,7,8}); + GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + GridParallelRNG CRNG(Coarse5d);CRNG.SeedFixedIntegers(cseeds); + LatticeFermion src(FGrid); gaussian(RNG5,src);// src=src+g5*src; + LatticeFermion result(FGrid); + LatticeGaugeField Umu(UGrid); + + FieldMetaData header; + // std::string file("./ckpoint_lat.4000"); + std::string file("./ckpoint_lat.1000"); + NerscIO::readConfiguration(Umu,header,file); + + std::cout< Subspace; + typedef CoarsenedMatrix CoarseOperator; + typedef CoarseOperator::CoarseVector CoarseVector; + typedef CoarseOperator::siteVector siteVector; + std::cout< HermDefOp(Ddwf); + + Subspace Aggregates(Coarse5d,FGrid,0); + + assert ( (nbasis & 0x1)==0); + { + int nb=nbasis/2; + LatticeFermion A(FGrid); + LatticeFermion B(FGrid); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.002,1000,800,100,0.0); + // Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.02,1000,800,100,0.0); + Aggregates.CreateSubspaceChebyshev(RNG5,HermDefOp,nb,60.0,0.01,1000,100,100,0.0); // Slightly faster + + for(int n=0;n Level1Op; + typedef CoarsenedMatrix,nbasisc> Level2Op; + + Gamma5R5HermitianLinearOperator HermIndefOp(Ddwf); + + + GridRedBlackCartesian * Coarse4dRB = SpaceTimeGrid::makeFourDimRedBlackGrid(Coarse4d); + std::cout << " Making 5D coarse RB grid " <,nbasisc> CoarseSubspace; + // CoarseSubspace CoarseAggregates(CoarseCoarse5d,Coarse5d,0); + + std::cout< PosdefLdop(LDOp); + /* + { + int nb=nbasisc/2; + CoarseAggregates.CreateSubspaceChebyshev(CRNG,PosdefLdop,nb,15.0,0.02,1000,800,100,0.0); + for(int n=0;noSites();site++){ + subspace_g5[site](nn) = subspace[site](nn); + subspace_g5[site](nn+nb)=-subspace[site](nn+nb); + } + } + } + } + */ + typedef Level2Op::CoarseVector CoarseCoarseVector; + /* + Level2Op L2Op(*CoarseCoarse5d,1); // Hermitian matrix + HermitianLinearOperator L1LinOp(LDOp); + L2Op.CoarsenOperator(Coarse5d,L1LinOp,CoarseAggregates); + + + std::cout< IRLHermOpL2(L2Op); + CoarseCoarseVector cc_src(CoarseCoarse5d); cc_src=1.0; + */ + /* + Chebyshev IRLChebyL2(0.001,15.0,301); + FunctionHermOp IRLOpChebyL2(IRLChebyL2,IRLHermOpL2); + PlainHermOp IRLOpL2 (IRLHermOpL2); + int cNk=24; + int cNm=36; + int cNstop=24; + ImplicitlyRestartedLanczos IRLL2(IRLOpChebyL2,IRLOpL2,cNstop,cNk,cNm,1.0e-3,20); + + int cNconv; + std::vector eval2(cNm); + std::vector evec2(cNm,CoarseCoarse5d); + IRLL2.calc(eval2,evec2,cc_src,cNconv); + + ConjugateGradient CoarseCoarseCG(0.1,1000); + DeflatedGuesser DeflCoarseCoarseGuesser(evec2,eval2); + NormalEquations DeflCoarseCoarseCGNE(L2Op,CoarseCoarseCG,DeflCoarseCoarseGuesser); + */ + + /* + std::cout< IRLHermOp(LDOp); + // Chebyshev IRLCheby(0.001,15.0,301); + Chebyshev IRLCheby(0.03,12.0,101); + FunctionHermOp IRLOpCheby(IRLCheby,IRLHermOp); + PlainHermOp IRLOp (IRLHermOp); + int Nk=64; + int Nm=128; + int Nstop=Nk; + ImplicitlyRestartedLanczos IRL(IRLOpCheby,IRLOp,Nstop,Nk,Nm,1.0e-3,20); + + int Nconv; + std::vector eval(Nm); + std::vector evec(Nm,Coarse5d); + IRL.calc(eval,evec,c_src,Nconv); + */ + CoarseVector c_src(Coarse5d); c_src=1.0; + // DeflatedGuesser DeflCoarseGuesser(evec,eval); + // NormalEquations DeflCoarseCGNE(LDOp,CoarseCG,DeflCoarseGuesser); + + std::cout< , NormalEquations > TwoLevelMG; + typedef MultiGridPreconditioner , NormalEquations > TwoLevelMG; + typedef MultiGridPreconditioner,nbasisc,Level1Op, DeflatedGuesser, NormalEquations > CoarseMG; + typedef MultiGridPreconditioner, LinearFunction > ThreeLevelMG; + + ChebyshevSmoother FineSmoother(0.25,60.0,12,HermIndefOp,Ddwf); + /* + // MultiGrid preconditioner acting on the coarse space <-> coarsecoarse space + ChebyshevSmoother CoarseSmoother(0.1,15.0,3,L1LinOp,LDOp); + + // MirsSmoother CoarseCGSmoother(0.1,0.1,4,L1LinOp,LDOp); + // MirsSmoother FineCGSmoother(0.0,0.01,8,HermIndefOp,Ddwf); + + CoarseMG Level2Precon (CoarseAggregates, L2Op, + L1LinOp,LDOp, + CoarseSmoother, + DeflCoarseCoarseGuesser, + DeflCoarseCoarseCGNE); + Level2Precon.Level(2); + + // PGCR Applying this solver to solve the coarse space problem + PrecGeneralisedConjugateResidual l2PGCR(0.1, 100, L1LinOp,Level2Precon,16,16); + l2PGCR.Level(2); + + // Wrap the 2nd level solver in a MultiGrid preconditioner acting on the fine space + ZeroGuesser CoarseZeroGuesser; + ThreeLevelMG ThreeLevelPrecon(Aggregates, LDOp, + HermIndefOp,Ddwf, + FineSmoother, + CoarseZeroGuesser, + l2PGCR); + ThreeLevelPrecon.Level(1); + + // Apply the fine-coarse-coarsecoarse 2 deep MG preconditioner in an outer PGCR on the fine fgrid + PrecGeneralisedConjugateResidual l1PGCR(1.0e-8,1000,HermIndefOp,ThreeLevelPrecon,16,16); + l1PGCR.Level(1); + */ + std::cout< CoarseZeroGuesser; + ConjugateGradient CoarseCG(0.01,1000); + NormalEquations CoarseCGNE(LDOp,CoarseCG,CoarseZeroGuesser); + TwoLevelMG TwoLevelPrecon(Aggregates, LDOp, + HermIndefOp,Ddwf, + FineSmoother, + CoarseZeroGuesser, + CoarseCGNE); + TwoLevelPrecon.Level(1); + PrecGeneralisedConjugateResidual l1PGCR(1.0e-8,20,HermIndefOp,TwoLevelPrecon,16,16); + l1PGCR.Level(1); + l1PGCR(src,result); + + std::cout< pCG(1.0e-8,60000); + result=Zero(); + // pCG(HermDefOp,src,result); + + std::cout< HermOpEO(Ddwf); + pCG(HermOpEO,src_o,result_o); + + std::cout< PM; PM(HermDefOp,src); + std::cout< cPM; cPM(PosdefLdop,c_src); + // std::cout< ccPM; ccPM(IRLHermOpL2,cc_src); + + std::cout< + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include +#include +#include +#include + +using namespace std; +using namespace Grid; + +// TODO +// +// Coarse Grid axpby_ssp_pminus // Inherit from spProj5pm +// Coarse Grid axpby_ssp_pplus + +template +class CayleyBase : public SparseMatrixBase +{ +public: + int Ls; + // protected: + RealD mass; + RealD M5; + // Save arguments to SetCoefficientsInternal + Vector _gamma; + RealD _zolo_hi; + RealD _b; + RealD _c; + + // Cayley form Moebius (tanh and zolotarev) + Vector omega; + Vector bs; // S dependent coeffs + Vector cs; + Vector as; + // For preconditioning Cayley form + Vector bee; + Vector cee; + Vector aee; + Vector beo; + Vector ceo; + Vector aeo; + // LDU factorisation of the eeoo matrix + Vector lee; + Vector leem; + Vector uee; + Vector ueem; + Vector dee; +public: + CayleyBase(RealD _M5, RealD _mass, int _Ls, RealD b_, RealD c_) : + M5(_M5), + mass(_mass), + Ls(_Ls), + _b(b_), + _c(c_) + { + RealD eps = 1.0; + Approx::zolotarev_data *zdata = Approx::higham(eps,this->Ls);// eps is ignored for higham + this->SetCoefficientsTanh(zdata,1.0,0.0); + Approx::zolotarev_free(zdata); + } + ///////////////////////////////////////////////////////// + // Replicates functionality + // Use a common base class approach + ///////////////////////////////////////////////////////// + // Tanh + void SetCoefficientsTanh(Approx::zolotarev_data *zdata,RealD b,RealD c) + { + Vector gamma(this->Ls); + for(int s=0;sLs;s++) gamma[s] = zdata->gamma[s]; + SetCoefficientsInternal(1.0,gamma,b,c); + } + //Zolo + void SetCoefficientsZolotarev(RealD zolo_hi,Approx::zolotarev_data *zdata,RealD b,RealD c) + { + Vector gamma(this->Ls); + for(int s=0;sLs;s++) gamma[s] = zdata->gamma[s]; + SetCoefficientsInternal(zolo_hi,gamma,b,c); + } + //Zolo + void SetCoefficientsInternal(RealD zolo_hi,Vector & gamma,RealD b,RealD c) + { + int Ls=this->Ls; + + /////////////////////////////////////////////////////////// + // The Cayley coeffs (unprec) + /////////////////////////////////////////////////////////// + assert(gamma.size()==Ls); + + omega.resize(Ls); + bs.resize(Ls); + cs.resize(Ls); + as.resize(Ls); + + double bpc = b+c; + double bmc = b-c; + _b = b; + _c = c; + _gamma = gamma; // Save the parameters so we can change mass later. + _zolo_hi= zolo_hi; + for(int i=0; i < Ls; i++){ + as[i] = 1.0; + omega[i] = _gamma[i]*_zolo_hi; //NB reciprocal relative to Chroma NEF code + assert(omega[i]!=Coeff_t(0.0)); + bs[i] = 0.5*(bpc/omega[i] + bmc); + cs[i] = 0.5*(bpc/omega[i] - bmc); + } + + //////////////////////////////////////////////////////// + // Constants for the preconditioned matrix Cayley form + //////////////////////////////////////////////////////// + bee.resize(Ls); + cee.resize(Ls); + beo.resize(Ls); + ceo.resize(Ls); + + for(int i=0;iM5) +1.0); + assert(bee[i]!=Coeff_t(0.0)); + cee[i]=as[i]*(1.0-cs[i]*(4.0-this->M5)); + beo[i]=as[i]*bs[i]; + ceo[i]=-as[i]*cs[i]; + } + aee.resize(Ls); + aeo.resize(Ls); + for(int i=0;i &out){assert(0);}; + virtual void DW (const Field &psi, Field &chi)=0; + virtual void DWDag (const Field &psi, Field &chi)=0; + + void M (const Field &psi, Field &chi) + { + Field Din(psi.Grid()); + Meooe5D(psi,Din); + DW(Din,chi); + axpby(chi,1.0,1.0,chi,psi); + M5D(psi,chi); + } + void Mdag (const Field &psi, Field &chi) + { + Field Din(psi.Grid()); + DWDag(psi,Din); + MeooeDag5D(Din,chi); + M5Ddag(psi,chi); + axpby (chi,1.0,1.0,chi,psi); + } + ///////////////////////////////// + // P and Pdag - might be needed + ///////////////////////////////// + void P(const Field &psi, Field &chi) + { + int Ls= this->Ls; + chi=Zero(); + for(int s=0;sLs; + chi=Zero(); + for(int s=0;sLs; + Vector diag (Ls,1.0); + Vector upper(Ls,-1.0); upper[Ls-1]=mass; + Vector lower(Ls,-1.0); lower[0] =mass; + M5D(psi,chi,chi,lower,diag,upper); + } + void M5Ddag (const Field &psi, Field &chi) + { + int Ls=this->Ls; + Vector diag(Ls,1.0); + Vector upper(Ls,-1.0); + Vector lower(Ls,-1.0); + upper[Ls-1]=-mass*upper[Ls-1]; + lower[0] =-mass*lower[0]; + M5Ddag(psi,chi,chi,lower,diag,upper); + } + void Meooe5D (const Field &psi, Field &Din) + { + int Ls=this->Ls; + Vector diag = bs; + Vector upper= cs; + Vector lower= cs; + upper[Ls-1]=-mass*upper[Ls-1]; + lower[0] =-mass*lower[0]; + M5D(psi,psi,Din,lower,diag,upper); + } + void MeooeDag5D (const Field &psi, Field &Din) + { + int Ls=this->Ls; + Vector diag =bs; + Vector upper=cs; + Vector lower=cs; + + for (int s=0;s &lower, + Vector &diag, + Vector &upper) + { + chi_i.Checkerboard()=psi_i.Checkerboard(); + GridBase *grid=psi_i.Grid(); + autoView(psi , psi_i,AcceleratorRead); + autoView(phi , phi_i,AcceleratorRead); + autoView(chi , chi_i,AcceleratorWrite); + assert(phi.Checkerboard() == psi.Checkerboard()); + + auto pdiag = &diag[0]; + auto pupper = &upper[0]; + auto plower = &lower[0]; + + int Ls =this->Ls; + + // 10 = 3 complex mult + 2 complex add + // Flops = 10.0*(Nc*Ns) *Ls*vol (/2 for red black counting) + uint64_t nloop = grid->oSites()/Ls; + + const int Nsimd = Field::vector_type::Nsimd(); + accelerator_for(sss,nloop,Nsimd,{ + uint64_t ss= sss*Ls; + typedef decltype(coalescedRead(psi[0])) spinor; + spinor tmp1, tmp2; + for(int s=0;s &lower, + Vector &diag, + Vector &upper) + { + chi_i.Checkerboard()=psi_i.Checkerboard(); + GridBase *grid=psi_i.Grid(); + autoView(psi , psi_i,AcceleratorRead); + autoView(phi , phi_i,AcceleratorRead); + autoView(chi , chi_i,AcceleratorWrite); + assert(phi.Checkerboard() == psi.Checkerboard()); + + auto pdiag = &diag[0]; + auto pupper = &upper[0]; + auto plower = &lower[0]; + + int Ls=this->Ls; + + uint64_t nloop = grid->oSites()/Ls; + const int Nsimd = Field::vector_type::Nsimd(); + accelerator_for(sss,nloop,Nsimd,{ + uint64_t ss=sss*Ls; + typedef decltype(coalescedRead(psi[0])) spinor; + spinor tmp1,tmp2; + for(int s=0;s +class CoarseCayleyFermion : public CayleyBase< Lattice > , ComplexD > +{ +public: + typedef iVector siteVector; + typedef Lattice CoarseComplexField; + typedef Lattice CoarseVector; + typedef Lattice > CoarseMatrix; + typedef iMatrix Cobj; + typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field + typedef Lattice FineField; + + // Similar to the CoarseOperator but add 5D support. + Geometry geom; + GridBase *Coarse5D; + GridBase *Coarse4D; + CartesianStencil Stencil; + CoarsenedMatrix &Dw; + + GridBase * Grid(void) { return Coarse5D; }; // this is all the linalg routines need to know + + CoarseCayleyFermion(GridCartesian &CoarseGrid4, + GridCartesian &CoarseGrid5, + CoarsenedMatrix &_Dw, + RealD M5, RealD mass, int Ls, RealD b, RealD c) : + CayleyBase(M5,mass,Ls,b,c), + Coarse4D(&CoarseGrid4), + Coarse5D(&CoarseGrid5), + Dw(_Dw), + geom(CoarseGrid5._ndimension), + Stencil( &CoarseGrid5,geom.npoint,Even,geom.directions,geom.displacements,0) + { + }; + +public: + void Project( CoarseVector &C ) + { + const int Nsimd = CComplex::Nsimd(); + autoView(Cv,C, AcceleratorWrite); + int Ls = this->Ls; + for(int s=0;soSites(), Nsimd, { + int sF= sU*Ls+s; + auto tmp = coalescedRead(Cv[sF]); + coalescedWrite(Cv[sF],tmp); + }); + } + } + //////////////////////////////////////////////// + // This is specific to Coarse Grid Cayley + //////////////////////////////////////////////// + virtual void Mdiag (const CoarseVector &in, CoarseVector &out) + { + std::vector allout(9,in.Grid()); + this->MdirAll(in,allout); + out = allout[8]; + } + virtual void Mdir (const CoarseVector &in, CoarseVector &out,int dir, int disp) + { + assert(0); + } + virtual void MdirAll (const CoarseVector &in, std::vector &out) + { + conformable(Coarse5D,in.Grid()); + + SimpleCompressor compressor; + + Stencil.HaloExchange(in,compressor); + typedef LatticeView Aview; + + const int Nsimd = CComplex::Nsimd(); + + // Ls loop for2D + int Ls=this->Ls; + + siteVector *CBp=Stencil.CommBuf(); + + int ptype; + int nb2=nbasis/2; + + autoView(in_v , in, AcceleratorRead); + autoView(st, Stencil, AcceleratorRead); + for(int point=0;pointoSites(), b, nbasis, Nsimd, { + + typedef decltype(coalescedRead(in_v[0])) calcVector; + typedef decltype(coalescedRead(in_v[0](0))) calcComplex; + int sU = sF/Ls; + int s = sF%Ls; + + calcComplex res = Zero(); + calcVector nbr; + int ptype; + + StencilEntry *SE=st.GetEntry(ptype,point,sF); + + if(SE->_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(CBp[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bb compressor; + + Stencil.HaloExchange(in,compressor); + typedef LatticeView Aview; + + const int Nsimd = CComplex::Nsimd(); + + // Ls loop for2D + int Ls=this->Ls; + + Vector AcceleratorViewContainer; + for(int p=0;poSites(), b, nbasis, Nsimd, { + + typedef decltype(coalescedRead(in_v[0])) calcVector; + typedef decltype(coalescedRead(in_v[0](0))) calcComplex; + int sU = sF/Ls; + int s = sF%Ls; + + calcComplex res = Zero(); + + { + calcVector nbr; + int ptype; + + for(int point=0;point_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(CBp[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bb Aggregates; + + void PromoteFromSubspace(Aggregates &_Aggregates,CoarseVector &C,FineField &F) + { + auto FineGrid4 = _Aggregates.FineGrid; + FineField F4(FineGrid4); + CoarseVector C4(Coarse4D); + for(int s=0;sLs;s++){ + ExtractSlice(C4,C,s,0); + _Aggregates.PromoteFromSubspace(C4,F4); + InsertSlice(F4,F,s,0); + } + } + void ProjectToSubspace(Aggregates &_Aggregates,CoarseVector &C,FineField &F) + { + auto FineGrid4 = _Aggregates.FineGrid; + FineField F4(FineGrid4); + CoarseVector C4(Coarse4D); + for(int s=0;sLs;s++){ + ExtractSlice(F4,F,s,0); + _Aggregates.ProjectToSubspace (C4,F4); + InsertSlice(C4,C,s,0); + } + Project(C); + } + template + void Test(Aggregates &_Aggregates,GridBase *FineGrid, Ddwf &_Ddwf) + { + typedef Lattice FineField; + CoarseVector Cin(Coarse5D); + CoarseVector Cout(Coarse5D); + CoarseVector CFout(Coarse5D); + + FineField Fin(FineGrid); + FineField Fout(FineGrid); + + + std::vector seeds({1,2,3,4,5}); + GridParallelRNG RNG(Coarse5D); RNG.SeedFixedIntegers(seeds); + + gaussian(RNG,Cin); + PromoteFromSubspace(_Aggregates,Cin,Fin); + ProjectToSubspace(_Aggregates,Cin,Fin); + + std::cout << GridLogMessage<< "************ "<M(Cin,Cout); + this->Project(Cout); + std::cout << GridLogMessage<< " Cout "<Mdag(Cin,Cout); + this->Project(Cout); + std::cout << GridLogMessage<< " Cout "< Directions(void) { return geom.directions;}; + virtual std::vector Displacements(void){ return geom.displacements;}; +}; + + +template class SolverWrapper : public LinearFunction { +private: + LinearOperatorBase & _Matrix; + OperatorFunction & _Solver; + LinearFunction & _Guess; +public: + + ///////////////////////////////////////////////////// + // Wrap the usual normal equations trick + ///////////////////////////////////////////////////// + SolverWrapper(LinearOperatorBase &Matrix, + OperatorFunction &Solver, + LinearFunction &Guess) + : _Matrix(Matrix), _Solver(Solver), _Guess(Guess) {}; + + void operator() (const Field &in, Field &out){ + + _Guess(in,out); + _Solver(_Matrix,in,out); // Mdag M out = Mdag in + + } +}; + +// Must use a non-hermitian solver +template +class PVdagMLinearOperator : public LinearOperatorBase { + Matrix &_Mat; + Matrix &_PV; +public: + PVdagMLinearOperator(Matrix &Mat,Matrix &PV): _Mat(Mat),_PV(PV){}; + + virtual std::vector Directions(void) { return _Mat.Directions();}; + virtual std::vector Displacements(void){ return _Mat.Displacements();}; + + void OpDiag (const Field &in, Field &out) { + assert(0); + } + void OpDir (const Field &in, Field &out,int dir,int disp) { + assert(0); + } + void OpDirAll (const Field &in, std::vector &out){ + assert(0); + }; + void Op (const Field &in, Field &out){ + Field tmp(in.Grid()); + _Mat.M(in,tmp); + _PV.Mdag(tmp,out); + } + void AdjOp (const Field &in, Field &out){ + Field tmp(in.Grid()); + _PV.M(tmp,out); + _Mat.Mdag(in,tmp); + } + void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ + assert(0); + } + void HermOp(const Field &in, Field &out){ + assert(0); + } +}; + +RealD InverseApproximation(RealD x){ + return 1.0/x; +} + +template class ChebyshevSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & _SmootherMatrix; + FineOperator & _SmootherOperator; + + Chebyshev Cheby; + + ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator,Matrix &SmootherMatrix) : + _SmootherOperator(SmootherOperator), + _SmootherMatrix(SmootherMatrix), + Cheby(_lo,_hi,_ord,InverseApproximation) + {}; + + void operator() (const Field &in, Field &out) + { + Field tmp(in.Grid()); + MdagMLinearOperator MdagMOp(_SmootherMatrix); + _SmootherOperator.AdjOp(in,tmp); + Cheby(MdagMOp,tmp,out); + } +}; +template +class MGPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + typedef CoarseCayleyFermion CoarseOperator; + // typedef SparseMatrixBase CoarseOperator; + + Aggregates & _Aggregates; + FineOperator & _FineOperator; + FineSmoother & _PreSmoother; + FineSmoother & _PostSmoother; + CoarseOperator & _CoarseOperator; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + + MGPreconditioner(Aggregates &Agg, + FineOperator &Fine, + FineSmoother &PreSmoother, + FineSmoother &PostSmoother, + CoarseOperator &CoarseOperator_, + CoarseSolver &CoarseSolve_) + : _Aggregates(Agg), + _FineOperator(Fine), + _PreSmoother(PreSmoother), + _PostSmoother(PostSmoother), + _CoarseOperator(CoarseOperator_), + _CoarseSolve(CoarseSolve_), + level(1) { } + + virtual void operator()(const FineField &in, FineField & out) + { + auto CoarseGrid = _CoarseOperator.Grid(); + CoarseVector Csrc(CoarseGrid); + CoarseVector Csol(CoarseGrid); + FineField vec1(in.Grid()); + FineField vec2(in.Grid()); + + std::cout< +class HDCRPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + //typedef CoarseCayleyFermion CoarseOperator; + typedef SparseMatrixBase CoarseOperator; + + Aggregates & _Aggregates; + FineOperator & _FineOperator; + FineSmoother & _PreSmoother; + FineSmoother & _PostSmoother; + CoarseOperator & _CoarseOperator; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + + HDCRPreconditioner(Aggregates &Agg, + FineOperator &Fine, + FineSmoother &PreSmoother, + FineSmoother &PostSmoother, + CoarseOperator &CoarseOperator_, + CoarseSolver &CoarseSolve_) + : _Aggregates(Agg), + _FineOperator(Fine), + _PreSmoother(PreSmoother), + _PostSmoother(PostSmoother), + _CoarseOperator(CoarseOperator_), + _CoarseSolve(CoarseSolve_), + level(1) { } + + virtual void operator()(const FineField &in, FineField & out) + { + auto CoarseGrid = _CoarseOperator.Grid(); + CoarseVector Csrc(CoarseGrid); + CoarseVector g5Csrc(CoarseGrid); + CoarseVector Csol(CoarseGrid); + FineField vec1(in.Grid()); + FineField vec2(in.Grid()); + + std::cout< block ({2,2,2,2}); // 4,2,2,2 gets worse + std::vector blockc ({1,1,1,1}); + const int nbasis= 24; + const int nbasisc= 32; // decrease, not improvement + + auto clatt = GridDefaultLatt(); + for(int d=0;d seeds({1,2,3,4}); + GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds); + GridParallelRNG CRNG(Coarse4d);CRNG.SeedFixedIntegers(seeds); + + LatticeGaugeField Umu(UGrid); +#if 0 + SU3::TepidConfiguration(RNG4,Umu); + RealD M5=1.0; +#else + std::string file("./ckpoint_lat.1000"); + FieldMetaData header; + NerscIO::readConfiguration(Umu,header,file); + RealD M5=1.8; +#endif + + std::cout< Subspace; + typedef CoarsenedMatrix CoarseOperator; + typedef CoarseOperator::CoarseVector CoarseVector; + typedef CoarseOperator::siteVector siteVector; + + std::cout< MdagM_Dw(Dw_null); + + std::cout< WilsonCG(1.0e-10,40000); + LatticeFermion w_src(UGrid); w_src=1.0; + LatticeFermion w_res(UGrid); + WilsonCG(MdagM_Dw,w_src,w_res); + exit(0); + */ + std::cout< Level1Op4; + typedef CoarseCayleyFermion Level1Op5; + Level1Op4 c_Dw (*Coarse4d,0); + NonHermitianLinearOperator LinOpDw(Dw); + c_Dw.CoarsenOperator(UGrid,LinOpDw,Aggregates4D); // contains the M5 from Dw(-M5) + // c_Dw.Test(Aggregates4D,UGrid,LinOpDw); + + std::cout< MdagM_cDwf(c_Dwf); + + std::cout<,nbasisc> Level2Op; + typedef Aggregation,nbasisc> CoarseSubspace; + CoarseSubspace CoarseAggregates(CoarseCoarse5d,Coarse5d,0); + + std::cout< L1Hdwf(c_Dwf); + GridRedBlackCartesian * CoarseCoarse5dRB = SpaceTimeGrid::makeFiveDimRedBlackGrid(1,CoarseCoarse4d); + Level2Op cc_Dwf (*CoarseCoarse5d,*CoarseCoarse5dRB,1); // say it is hermitian + cc_Dwf.CoarsenOperator(Coarse5d,L1Hdwf,CoarseAggregates); + // cc_Dwf.Test(CoarseAggregates,Coarse5d,L1Hdwf); + + typedef Level2Op::CoarseVector CoarseCoarseVector; + + std::cout< CoarseCG(tol,MaxIt); + ConjugateGradient FineCG(tol,MaxIt); + + NonHermitianLinearOperator FineM(Ddwf); + MdagMLinearOperator FineMdagM(Ddwf); // M^\dag M + + NonHermitianLinearOperator CoarseM(c_Dwf); + MdagMLinearOperator CoarseMdagM(c_Dwf); + + NonHermitianLinearOperator CoarseCoarseM(cc_Dwf); + MdagMLinearOperator CoarseCoarseMdagM(cc_Dwf); + + + std::cout< PM; PM(MdagM_Dw,w_src); + std::cout< cPM; cPM(CoarseMdagM,c_src); + + cc_src=1.0; + PowerMethod ccPM; ccPM(CoarseCoarseMdagM,cc_src); + + std::cout< IRLHermOpL2(cc_Dwf); + Chebyshev IRLChebyL2(IRL_lo,IRL_hi,IRL_ord); + FunctionHermOp IRLOpChebyL2(IRLChebyL2,IRLHermOpL2); + PlainHermOp IRLOpL2 (IRLHermOpL2); + ImplicitlyRestartedLanczos IRLL2(IRLOpChebyL2,IRLOpL2,cNstop,cNk,cNm,1.0e-3,20); + + int cNconv; + cNm=0; + std::vector eval2(cNm); + std::vector evec2(cNm,CoarseCoarse5d); + cc_src=1.0; + // IRLL2.calc(eval2,evec2,cc_src,cNconv); + + ConjugateGradient CoarseCoarseCG(0.02,10000); + DeflatedGuesser DeflCoarseCoarseGuesser(evec2,eval2); + NormalEquations DeflCoarseCoarseCGNE(cc_Dwf,CoarseCoarseCG,DeflCoarseCoarseGuesser); + + ZeroGuesser CoarseZeroGuesser; + ZeroGuesser CoarseCoarseZeroGuesser; + + std::cout< CoarseCoarseCGNE(cc_Dwf,CoarseCoarseCG,CoarseCoarseZeroGuesser); + { +typedef HDCRPreconditioner,nbasisc,NormalEquations > CoarseMG; + typedef MGPreconditioner > ThreeLevelMG; + + // MultiGrid preconditioner acting on the coarse space <-> coarsecoarse space + ChebyshevSmoother CoarseSmoother1(0.5,22.0,12,CoarseM,c_Dwf); // 37s, 26 iter + ChebyshevSmoother CoarseSmoother2(0.5,22.0,12,CoarseM,c_Dwf); + + // ChebyshevSmoother CoarseSmoother1(0.5,22.0,7,CoarseM,c_Dwf); // 38s, 26 iter + // ChebyshevSmoother CoarseSmoother2(0.5,22.0,7,CoarseM,c_Dwf); + // ChebyshevSmoother CoarseSmoother1(0.4,22.0,7,CoarseM,c_Dwf); // 41s, 27 iter + // ChebyshevSmoother CoarseSmoother2(0.4,22.0,7,CoarseM,c_Dwf); + // ChebyshevSmoother CoarseSmoother1(0.6,22.0,6,CoarseM,c_Dwf); // 26 iter + // ChebyshevSmoother CoarseSmoother2(0.6,22.0,6,CoarseM,c_Dwf); + // ChebyshevSmoother CoarseSmoother1(0.5,22.0,5,CoarseM,c_Dwf); // 33 iter, 55s + // ChebyshevSmoother CoarseSmoother2(0.5,22.0,5,CoarseM,c_Dwf); + + + CoarseMG Level2Precon (CoarseAggregates, + CoarseM, + CoarseSmoother1, + CoarseSmoother2, + cc_Dwf, + DeflCoarseCoarseCGNE); + Level2Precon.Level(2); + + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.5, 100, CoarseM,Level2Precon,16,16); // 26 iter, 37s + // PGCR Applying this solver to solve the coarse space problem + // COULD BE FIXED??? + PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.0, 1, CoarseM,Level2Precon,2,2); + + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(1.0, 100, CoarseM,Level2Precon,16,16); // 35 iter, 45s + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.6, 100, CoarseM,Level2Precon,16,16); // 26,38 (diifferene is measurement noise) + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.2, 100, CoarseM,Level2Precon,16,16); // 26 iter, 47s + L2PGCR.Level(2); + + // Wrap the 2nd level solver in a MultiGrid preconditioner acting on the fine space + + // ChebyshevSmoother FineSmoother1(0.5,60.0,14,FineM,Ddwf); // 26 iter, 39s + // ChebyshevSmoother FineSmoother2(0.5,60.0,14,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 25 iter, 38s + // ChebyshevSmoother FineSmoother2(0.5,60.0,16,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 23 iter, 39s + // ChebyshevSmoother FineSmoother2(0.5,60.0,20,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,10,FineM,Ddwf);24 iter, 44s + // ChebyshevSmoother FineSmoother2(0.5,60.0,24,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // odd convergence tail at 10^-9 ish + // ChebyshevSmoother FineSmoother2(0.1,60.0,24,FineM,Ddwf); // 33 iter, waas O(10-9 by 26) + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 25 iter, 39s + // ChebyshevSmoother FineSmoother2(0.5,60.0,18,FineM,Ddwf); // + + ChebyshevSmoother FineSmoother1(0.5,60.0,16,FineM,Ddwf); + ChebyshevSmoother FineSmoother2(0.5,60.0,16,FineM,Ddwf); // + + // ChebyshevSmoother FineSmoother1(0.5,60.0,11,FineM,Ddwf); // 33 iter, 49s + // ChebyshevSmoother FineSmoother2(0.5,60.0,11,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 26 iter, 37s + // ChebyshevSmoother FineSmoother2(0.5,60.0,12,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.4,60.0,12,FineM,Ddwf); // iter 26 no change in final residual + // ChebyshevSmoother FineSmoother2(0.4,60.0,12,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.3,60.0,12,FineM,Ddwf); // 27 iter 39s. + // ChebyshevSmoother FineSmoother2(0.3,60.0,12,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.3,60.0,13,FineM,Ddwf); // 26 iter, but slower + // ChebyshevSmoother FineSmoother2(0.3,60.0,13,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(1.0,60.0,12,FineM,Ddwf); // 34 iter, slower + // ChebyshevSmoother FineSmoother2(1.0,60.0,12,FineM,Ddwf); + + ThreeLevelMG ThreeLevelPrecon(Aggregates4D, + FineM, + FineSmoother1, + FineSmoother2, + c_Dwf, + L2PGCR); + ThreeLevelPrecon.Level(1); + + PrecGeneralisedConjugateResidualNonHermitian L1PGCR(1.0e-8,1000,FineM,ThreeLevelPrecon,16,16); + L1PGCR.Level(1); + + f_res=Zero(); + L1PGCR(f_src,f_res); + } + + std::cout< + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include +#include +#include +#include + +using namespace std; +using namespace Grid; + +// TODO +// +// Coarse Grid axpby_ssp_pminus // Inherit from spProj5pm +// Coarse Grid axpby_ssp_pplus + +template +class CayleyBase : public SparseMatrixBase +{ +public: + int Ls; + // protected: + RealD mass; + RealD M5; + // Save arguments to SetCoefficientsInternal + Vector _gamma; + RealD _zolo_hi; + RealD _b; + RealD _c; + + // Cayley form Moebius (tanh and zolotarev) + Vector omega; + Vector bs; // S dependent coeffs + Vector cs; + Vector as; + // For preconditioning Cayley form + Vector bee; + Vector cee; + Vector aee; + Vector beo; + Vector ceo; + Vector aeo; + // LDU factorisation of the eeoo matrix + Vector lee; + Vector leem; + Vector uee; + Vector ueem; + Vector dee; +public: + CayleyBase(RealD _M5, RealD _mass, int _Ls, RealD b_, RealD c_) : + M5(_M5), + mass(_mass), + Ls(_Ls), + _b(b_), + _c(c_) + { + RealD eps = 1.0; + Approx::zolotarev_data *zdata = Approx::higham(eps,this->Ls);// eps is ignored for higham + this->SetCoefficientsTanh(zdata,1.0,0.0); + Approx::zolotarev_free(zdata); + } + ///////////////////////////////////////////////////////// + // Replicates functionality + // Use a common base class approach + ///////////////////////////////////////////////////////// + // Tanh + void SetCoefficientsTanh(Approx::zolotarev_data *zdata,RealD b,RealD c) + { + Vector gamma(this->Ls); + for(int s=0;sLs;s++) gamma[s] = zdata->gamma[s]; + SetCoefficientsInternal(1.0,gamma,b,c); + } + //Zolo + void SetCoefficientsZolotarev(RealD zolo_hi,Approx::zolotarev_data *zdata,RealD b,RealD c) + { + Vector gamma(this->Ls); + for(int s=0;sLs;s++) gamma[s] = zdata->gamma[s]; + SetCoefficientsInternal(zolo_hi,gamma,b,c); + } + //Zolo + void SetCoefficientsInternal(RealD zolo_hi,Vector & gamma,RealD b,RealD c) + { + int Ls=this->Ls; + + /////////////////////////////////////////////////////////// + // The Cayley coeffs (unprec) + /////////////////////////////////////////////////////////// + assert(gamma.size()==Ls); + + omega.resize(Ls); + bs.resize(Ls); + cs.resize(Ls); + as.resize(Ls); + + double bpc = b+c; + double bmc = b-c; + _b = b; + _c = c; + _gamma = gamma; // Save the parameters so we can change mass later. + _zolo_hi= zolo_hi; + for(int i=0; i < Ls; i++){ + as[i] = 1.0; + omega[i] = _gamma[i]*_zolo_hi; //NB reciprocal relative to Chroma NEF code + assert(omega[i]!=Coeff_t(0.0)); + bs[i] = 0.5*(bpc/omega[i] + bmc); + cs[i] = 0.5*(bpc/omega[i] - bmc); + } + + //////////////////////////////////////////////////////// + // Constants for the preconditioned matrix Cayley form + //////////////////////////////////////////////////////// + bee.resize(Ls); + cee.resize(Ls); + beo.resize(Ls); + ceo.resize(Ls); + + for(int i=0;iM5) +1.0); + assert(bee[i]!=Coeff_t(0.0)); + cee[i]=as[i]*(1.0-cs[i]*(4.0-this->M5)); + beo[i]=as[i]*bs[i]; + ceo[i]=-as[i]*cs[i]; + } + aee.resize(Ls); + aeo.resize(Ls); + for(int i=0;i &out){assert(0);}; + virtual void DW (const Field &psi, Field &chi)=0; + virtual void DWDag (const Field &psi, Field &chi)=0; + + void M (const Field &psi, Field &chi) + { + Field Din(psi.Grid()); + Meooe5D(psi,Din); + DW(Din,chi); + axpby(chi,1.0,1.0,chi,psi); + M5D(psi,chi); + } + void Mdag (const Field &psi, Field &chi) + { + Field Din(psi.Grid()); + DWDag(psi,Din); + MeooeDag5D(Din,chi); + M5Ddag(psi,chi); + axpby (chi,1.0,1.0,chi,psi); + } + ///////////////////////////////// + // P and Pdag - might be needed + ///////////////////////////////// + void P(const Field &psi, Field &chi) + { + int Ls= this->Ls; + chi=Zero(); + for(int s=0;sLs; + chi=Zero(); + for(int s=0;sLs; + Vector diag (Ls,1.0); + Vector upper(Ls,-1.0); upper[Ls-1]=mass; + Vector lower(Ls,-1.0); lower[0] =mass; + M5D(psi,chi,chi,lower,diag,upper); + } + void M5Ddag (const Field &psi, Field &chi) + { + int Ls=this->Ls; + Vector diag(Ls,1.0); + Vector upper(Ls,-1.0); + Vector lower(Ls,-1.0); + upper[Ls-1]=-mass*upper[Ls-1]; + lower[0] =-mass*lower[0]; + M5Ddag(psi,chi,chi,lower,diag,upper); + } + void Meooe5D (const Field &psi, Field &Din) + { + int Ls=this->Ls; + Vector diag = bs; + Vector upper= cs; + Vector lower= cs; + upper[Ls-1]=-mass*upper[Ls-1]; + lower[0] =-mass*lower[0]; + M5D(psi,psi,Din,lower,diag,upper); + } + void MeooeDag5D (const Field &psi, Field &Din) + { + int Ls=this->Ls; + Vector diag =bs; + Vector upper=cs; + Vector lower=cs; + + for (int s=0;s &lower, + Vector &diag, + Vector &upper) + { + chi_i.Checkerboard()=psi_i.Checkerboard(); + GridBase *grid=psi_i.Grid(); + autoView(psi , psi_i,AcceleratorRead); + autoView(phi , phi_i,AcceleratorRead); + autoView(chi , chi_i,AcceleratorWrite); + assert(phi.Checkerboard() == psi.Checkerboard()); + + auto pdiag = &diag[0]; + auto pupper = &upper[0]; + auto plower = &lower[0]; + + int Ls =this->Ls; + + // 10 = 3 complex mult + 2 complex add + // Flops = 10.0*(Nc*Ns) *Ls*vol (/2 for red black counting) + uint64_t nloop = grid->oSites()/Ls; + + const int Nsimd = Field::vector_type::Nsimd(); + accelerator_for(sss,nloop,Nsimd,{ + uint64_t ss= sss*Ls; + typedef decltype(coalescedRead(psi[0])) spinor; + spinor tmp1, tmp2; + for(int s=0;s &lower, + Vector &diag, + Vector &upper) + { + chi_i.Checkerboard()=psi_i.Checkerboard(); + GridBase *grid=psi_i.Grid(); + autoView(psi , psi_i,AcceleratorRead); + autoView(phi , phi_i,AcceleratorRead); + autoView(chi , chi_i,AcceleratorWrite); + assert(phi.Checkerboard() == psi.Checkerboard()); + + auto pdiag = &diag[0]; + auto pupper = &upper[0]; + auto plower = &lower[0]; + + int Ls=this->Ls; + + uint64_t nloop = grid->oSites()/Ls; + const int Nsimd = Field::vector_type::Nsimd(); + accelerator_for(sss,nloop,Nsimd,{ + uint64_t ss=sss*Ls; + typedef decltype(coalescedRead(psi[0])) spinor; + spinor tmp1,tmp2; + for(int s=0;s +class CoarseCayleyFermion : public CayleyBase< Lattice > , ComplexD > +{ +public: + typedef iVector siteVector; + typedef Lattice CoarseComplexField; + typedef Lattice CoarseVector; + typedef Lattice > CoarseMatrix; + typedef iMatrix Cobj; + typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field + typedef Lattice FineField; + + // Similar to the CoarseOperator but add 5D support. + Geometry geom; + GridBase *Coarse5D; + GridBase *Coarse4D; + CartesianStencil Stencil; + CoarsenedMatrix &Dw; + + GridBase * Grid(void) { return Coarse5D; }; // this is all the linalg routines need to know + + CoarseCayleyFermion(GridCartesian &CoarseGrid4, + GridCartesian &CoarseGrid5, + CoarsenedMatrix &_Dw, + RealD M5, RealD mass, int Ls, RealD b, RealD c) : + CayleyBase(M5,mass,Ls,b,c), + Coarse4D(&CoarseGrid4), + Coarse5D(&CoarseGrid5), + Dw(_Dw), + geom(CoarseGrid5._ndimension), + Stencil( &CoarseGrid5,geom.npoint,Even,geom.directions,geom.displacements,0) + { + }; + +public: + void Project( CoarseVector &C ) + { + const int Nsimd = CComplex::Nsimd(); + autoView(Cv,C, AcceleratorWrite); + int Ls = this->Ls; + for(int s=0;soSites(), Nsimd, { + int sF= sU*Ls+s; + auto tmp = coalescedRead(Cv[sF]); + coalescedWrite(Cv[sF],tmp); + }); + } + } + //////////////////////////////////////////////// + // This is specific to Coarse Grid Cayley + //////////////////////////////////////////////// + virtual void Mdiag (const CoarseVector &in, CoarseVector &out) + { + std::vector allout(9,in.Grid()); + this->MdirAll(in,allout); + out = allout[8]; + } + virtual void Mdir (const CoarseVector &in, CoarseVector &out,int dir, int disp) + { + assert(0); + } + virtual void MdirAll (const CoarseVector &in, std::vector &out) + { + conformable(Coarse5D,in.Grid()); + + SimpleCompressor compressor; + + Stencil.HaloExchange(in,compressor); + typedef LatticeView Aview; + + const int Nsimd = CComplex::Nsimd(); + + // Ls loop for2D + int Ls=this->Ls; + + siteVector *CBp=Stencil.CommBuf(); + + int ptype; + int nb2=nbasis/2; + + autoView(in_v , in, AcceleratorRead); + autoView(st, Stencil, AcceleratorRead); + for(int point=0;pointoSites(), b, nbasis, Nsimd, { + + typedef decltype(coalescedRead(in_v[0])) calcVector; + typedef decltype(coalescedRead(in_v[0](0))) calcComplex; + int sU = sF/Ls; + int s = sF%Ls; + + calcComplex res = Zero(); + calcVector nbr; + int ptype; + + StencilEntry *SE=st.GetEntry(ptype,point,sF); + + if(SE->_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(CBp[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bb compressor; + + Stencil.HaloExchange(in,compressor); + typedef LatticeView Aview; + + const int Nsimd = CComplex::Nsimd(); + + // Ls loop for2D + int Ls=this->Ls; + + Vector AcceleratorViewContainer; + for(int p=0;poSites(), b, nbasis, Nsimd, { + + typedef decltype(coalescedRead(in_v[0])) calcVector; + typedef decltype(coalescedRead(in_v[0](0))) calcComplex; + int sU = sF/Ls; + int s = sF%Ls; + + calcComplex res = Zero(); + + { + calcVector nbr; + int ptype; + + for(int point=0;point_is_local) { + nbr = coalescedReadPermute(in_v[SE->_offset],ptype,SE->_permute); + } else { + nbr = coalescedRead(CBp[SE->_offset]); + } + acceleratorSynchronise(); + + for(int bb=0;bb Aggregates; + + void PromoteFromSubspace(Aggregates &_Aggregates,CoarseVector &C,FineField &F) + { + auto FineGrid4 = _Aggregates.FineGrid; + FineField F4(FineGrid4); + CoarseVector C4(Coarse4D); + for(int s=0;sLs;s++){ + ExtractSlice(C4,C,s,0); + _Aggregates.PromoteFromSubspace(C4,F4); + InsertSlice(F4,F,s,0); + } + } + void ProjectToSubspace(Aggregates &_Aggregates,CoarseVector &C,FineField &F) + { + auto FineGrid4 = _Aggregates.FineGrid; + FineField F4(FineGrid4); + CoarseVector C4(Coarse4D); + for(int s=0;sLs;s++){ + ExtractSlice(F4,F,s,0); + _Aggregates.ProjectToSubspace (C4,F4); + InsertSlice(C4,C,s,0); + } + Project(C); + } + template + void Test(Aggregates &_Aggregates,GridBase *FineGrid, Ddwf &_Ddwf) + { + typedef Lattice FineField; + CoarseVector Cin(Coarse5D); + CoarseVector Cout(Coarse5D); + CoarseVector CFout(Coarse5D); + + FineField Fin(FineGrid); + FineField Fout(FineGrid); + + + std::vector seeds({1,2,3,4,5}); + GridParallelRNG RNG(Coarse5D); RNG.SeedFixedIntegers(seeds); + + gaussian(RNG,Cin); + PromoteFromSubspace(_Aggregates,Cin,Fin); + ProjectToSubspace(_Aggregates,Cin,Fin); + + std::cout << GridLogMessage<< "************ "<M(Cin,Cout); + this->Project(Cout); + std::cout << GridLogMessage<< " Cout "<Mdag(Cin,Cout); + this->Project(Cout); + std::cout << GridLogMessage<< " Cout "< Directions(void) { return geom.directions;}; + virtual std::vector Displacements(void){ return geom.displacements;}; +}; + +template class SchurSolverWrapper : public LinearFunction { +private: + CheckerBoardedSparseMatrixBase & _Matrix; + SchurRedBlackBase & _Solver; +public: + + ///////////////////////////////////////////////////// + // Wrap the usual normal equations trick + ///////////////////////////////////////////////////// + SchurSolverWrapper(CheckerBoardedSparseMatrixBase &Matrix, + SchurRedBlackBase &Solver) + : _Matrix(Matrix), _Solver(Solver) {}; + + void operator() (const Field &in, Field &out){ + + _Solver(_Matrix,in,out); // Mdag M out = Mdag in + + } +}; + +template class SolverWrapper : public LinearFunction { +private: + LinearOperatorBase & _Matrix; + OperatorFunction & _Solver; + LinearFunction & _Guess; +public: + + ///////////////////////////////////////////////////// + // Wrap the usual normal equations trick + ///////////////////////////////////////////////////// + SolverWrapper(LinearOperatorBase &Matrix, + OperatorFunction &Solver, + LinearFunction &Guess) + : _Matrix(Matrix), _Solver(Solver), _Guess(Guess) {}; + + void operator() (const Field &in, Field &out){ + + _Guess(in,out); + _Solver(_Matrix,in,out); // Mdag M out = Mdag in + + } +}; + +// Must use a non-hermitian solver +template +class PVdagMLinearOperator : public LinearOperatorBase { + Matrix &_Mat; + Matrix &_PV; +public: + PVdagMLinearOperator(Matrix &Mat,Matrix &PV): _Mat(Mat),_PV(PV){}; + + virtual std::vector Directions(void) { return _Mat.Directions();}; + virtual std::vector Displacements(void){ return _Mat.Displacements();}; + + void OpDiag (const Field &in, Field &out) { + assert(0); + } + void OpDir (const Field &in, Field &out,int dir,int disp) { + assert(0); + } + void OpDirAll (const Field &in, std::vector &out){ + assert(0); + }; + void Op (const Field &in, Field &out){ + Field tmp(in.Grid()); + _Mat.M(in,tmp); + _PV.Mdag(tmp,out); + } + void AdjOp (const Field &in, Field &out){ + Field tmp(in.Grid()); + _PV.M(tmp,out); + _Mat.Mdag(in,tmp); + } + void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ + assert(0); + } + void HermOp(const Field &in, Field &out){ + assert(0); + } +}; + +RealD InverseApproximation(RealD x){ + return 1.0/x; +} + +template class ChebyshevSmoother : public LinearFunction +{ +public: + typedef LinearOperatorBase FineOperator; + Matrix & _SmootherMatrix; + FineOperator & _SmootherOperator; + + Chebyshev Cheby; + + ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator,Matrix &SmootherMatrix) : + _SmootherOperator(SmootherOperator), + _SmootherMatrix(SmootherMatrix), + Cheby(_lo,_hi,_ord,InverseApproximation) + {}; + + void operator() (const Field &in, Field &out) + { + Field tmp(in.Grid()); + MdagMLinearOperator MdagMOp(_SmootherMatrix); + _SmootherOperator.AdjOp(in,tmp); + Cheby(MdagMOp,tmp,out); + } +}; +template +class MGPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + typedef CoarseCayleyFermion CoarseOperator; + // typedef SparseMatrixBase CoarseOperator; + + Aggregates & _Aggregates; + FineOperator & _FineOperator; + FineSmoother & _PreSmoother; + FineSmoother & _PostSmoother; + CoarseOperator & _CoarseOperator; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + + MGPreconditioner(Aggregates &Agg, + FineOperator &Fine, + FineSmoother &PreSmoother, + FineSmoother &PostSmoother, + CoarseOperator &CoarseOperator_, + CoarseSolver &CoarseSolve_) + : _Aggregates(Agg), + _FineOperator(Fine), + _PreSmoother(PreSmoother), + _PostSmoother(PostSmoother), + _CoarseOperator(CoarseOperator_), + _CoarseSolve(CoarseSolve_), + level(1) { } + + virtual void operator()(const FineField &in, FineField & out) + { + auto CoarseGrid = _CoarseOperator.Grid(); + CoarseVector Csrc(CoarseGrid); + CoarseVector Csol(CoarseGrid); + FineField vec1(in.Grid()); + FineField vec2(in.Grid()); + + std::cout< +class HDCRPreconditioner : public LinearFunction< Lattice > { +public: + + typedef Aggregation Aggregates; + typedef typename Aggregation::CoarseVector CoarseVector; + typedef typename Aggregation::CoarseMatrix CoarseMatrix; + typedef typename Aggregation::FineField FineField; + typedef LinearOperatorBase FineOperator; + typedef LinearFunction FineSmoother; + //typedef CoarseCayleyFermion CoarseOperator; + typedef SparseMatrixBase CoarseOperator; + + Aggregates & _Aggregates; + FineOperator & _FineOperator; + FineSmoother & _PreSmoother; + FineSmoother & _PostSmoother; + CoarseOperator & _CoarseOperator; + CoarseSolver & _CoarseSolve; + + int level; void Level(int lv) {level = lv; }; + + HDCRPreconditioner(Aggregates &Agg, + FineOperator &Fine, + FineSmoother &PreSmoother, + FineSmoother &PostSmoother, + CoarseOperator &CoarseOperator_, + CoarseSolver &CoarseSolve_) + : _Aggregates(Agg), + _FineOperator(Fine), + _PreSmoother(PreSmoother), + _PostSmoother(PostSmoother), + _CoarseOperator(CoarseOperator_), + _CoarseSolve(CoarseSolve_), + level(1) { } + + virtual void operator()(const FineField &in, FineField & out) + { + auto CoarseGrid = _CoarseOperator.Grid(); + CoarseVector Csrc(CoarseGrid); + CoarseVector g5Csrc(CoarseGrid); + CoarseVector Csol(CoarseGrid); + FineField vec1(in.Grid()); + FineField vec2(in.Grid()); + + std::cout< block ({2,2,2,2}); // 4,2,2,2 gets worse + std::vector blockc ({1,1,1,1}); + const int nbasis= 24; + const int nbasisc= 40; // decrease, not improvement + + auto clatt = GridDefaultLatt(); + for(int d=0;d seeds({1,2,3,4}); + GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds); + GridParallelRNG CRNG(Coarse4d);CRNG.SeedFixedIntegers(seeds); + + LatticeGaugeField Umu(UGrid); +#if 0 + SU3::TepidConfiguration(RNG4,Umu); + RealD M5=1.0; +#else + std::string file("./ckpoint_lat.1000"); + FieldMetaData header; + NerscIO::readConfiguration(Umu,header,file); + RealD M5=1.8; +#endif + + std::cout< Subspace; + typedef CoarsenedMatrix CoarseOperator; + typedef CoarseOperator::CoarseVector CoarseVector; + typedef CoarseOperator::siteVector siteVector; + + std::cout< MdagM_Dw(Dw_null); + + std::cout< WilsonCG(1.0e-10,40000); + LatticeFermion w_src(UGrid); w_src=1.0; + LatticeFermion w_res(UGrid); + WilsonCG(MdagM_Dw,w_src,w_res); + exit(0); + */ + std::cout< Level1Op4; + typedef CoarseCayleyFermion Level1Op5; + Level1Op4 c_Dw (*Coarse4d,0); + NonHermitianLinearOperator LinOpDw(Dw); + c_Dw.CoarsenOperator(UGrid,LinOpDw,Aggregates4D); // contains the M5 from Dw(-M5) + // c_Dw.Test(Aggregates4D,UGrid,LinOpDw); + + std::cout< MdagM_cDwf(c_Dwf); + + std::cout<,nbasisc> Level2Op; + typedef Aggregation,nbasisc> CoarseSubspace; + CoarseSubspace CoarseAggregates(CoarseCoarse5d,Coarse5d,0); + + std::cout< L1Hdwf(c_Dwf); + Level2Op cc_Dwf (*CoarseCoarse5d,*CoarseCoarse5dRB,1); // say it is hermitian + cc_Dwf.CoarsenOperator(Coarse5d,L1Hdwf,CoarseAggregates); + // cc_Dwf.Test(CoarseAggregates,Coarse5d,L1Hdwf); + + typedef Level2Op::CoarseVector CoarseCoarseVector; + + std::cout< CoarseCG(tol,MaxIt); + ConjugateGradient FineCG(tol,MaxIt); + + NonHermitianLinearOperator FineM(Ddwf); + MdagMLinearOperator FineMdagM(Ddwf); // M^\dag M + + NonHermitianLinearOperator CoarseM(c_Dwf); + MdagMLinearOperator CoarseMdagM(c_Dwf); + + NonHermitianLinearOperator CoarseCoarseM(cc_Dwf); + MdagMLinearOperator CoarseCoarseMdagM(cc_Dwf); + + + std::cout< PM; PM(MdagM_Dw,w_src); + std::cout< cPM; cPM(CoarseMdagM,c_src); + + cc_src=1.0; + PowerMethod ccPM; ccPM(CoarseCoarseMdagM,cc_src); + + std::cout< IRLHermOpL2(cc_Dwf); + Chebyshev IRLChebyL2(IRL_lo,IRL_hi,IRL_ord); + FunctionHermOp IRLOpChebyL2(IRLChebyL2,IRLHermOpL2); + PlainHermOp IRLOpL2 (IRLHermOpL2); + ImplicitlyRestartedLanczos IRLL2(IRLOpChebyL2,IRLOpL2,cNstop,cNk,cNm,1.0e-3,20); + + int cNconv; + cNm=0; + std::vector eval2(cNm); + std::vector evec2(cNm,CoarseCoarse5d); + cc_src=1.0; + // IRLL2.calc(eval2,evec2,cc_src,cNconv); + + std::vector tols ({0.005,0.001}); + std::vector c_los ({0.1,0.05}); + std::vector c_his ({22.0}); + std::vector f_los ({0.5,0.2}); + std::vector f_his ({60.0}); + std::vector ws ({2,3}); + std::vector c_ords ({32,24}); + std::vector f_ords ({20,16}); + + for(auto w : ws ) { + for(auto tol : tols ) { + for(auto f_ord : f_ords ) { + for(auto c_ord : c_ords ) { + for(auto c_lo : c_los ) { + for(auto c_hi : c_his ) { + for(auto f_lo : f_los ) { + for(auto f_hi : f_his ) { + ZeroGuesser CoarseZeroGuesser; + ZeroGuesser CoarseCoarseZeroGuesser; + ConjugateGradient CoarseCoarseCG(tol,10000); + ZeroGuesser CoarseCoarseGuesser; + SchurRedBlackDiagMooeeSolve CoarseCoarseRBCG(CoarseCoarseCG); + SchurSolverWrapper CoarseCoarseSolver(cc_Dwf,CoarseCoarseRBCG); + + std::cout< CoarseCoarseCGNE(cc_Dwf,CoarseCoarseCG,CoarseCoarseZeroGuesser); + { +typedef HDCRPreconditioner,nbasisc,LinearFunction > CoarseMG; + typedef MGPreconditioner > ThreeLevelMG; + + // MultiGrid preconditioner acting on the coarse space <-> coarsecoarse space + // ChebyshevSmoother CoarseSmoother1(0.5,22.0,c_ord,CoarseM,c_Dwf); // 37s, 26 iter + // ChebyshevSmoother CoarseSmoother2(0.5,22.0,c_ord,CoarseM,c_Dwf); + ChebyshevSmoother CoarseSmoother(c_lo,c_hi,c_ord,CoarseM,c_Dwf); // 37s, 26 iter + + // ChebyshevSmoother CoarseSmoother1(0.5,22.0,7,CoarseM,c_Dwf); // 38s, 26 iter + // ChebyshevSmoother CoarseSmoother2(0.5,22.0,7,CoarseM,c_Dwf); + // ChebyshevSmoother CoarseSmoother1(0.4,22.0,7,CoarseM,c_Dwf); // 41s, 27 iter + // ChebyshevSmoother CoarseSmoother2(0.4,22.0,7,CoarseM,c_Dwf); + // ChebyshevSmoother CoarseSmoother1(0.6,22.0,6,CoarseM,c_Dwf); // 26 iter + // ChebyshevSmoother CoarseSmoother2(0.6,22.0,6,CoarseM,c_Dwf); + // ChebyshevSmoother CoarseSmoother1(0.5,22.0,5,CoarseM,c_Dwf); // 33 iter, 55s + // ChebyshevSmoother CoarseSmoother2(0.5,22.0,5,CoarseM,c_Dwf); + + + CoarseMG Level2Precon (CoarseAggregates, + CoarseM, + CoarseSmoother, + CoarseSmoother, + cc_Dwf, + CoarseCoarseSolver); + Level2Precon.Level(2); + + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.5, 100, CoarseM,Level2Precon,16,16); // 26 iter, 37s + // PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.0, 1, CoarseM,Level2Precon,2,2); // 296 s, 50 iter + // PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.0, 1, CoarseM,Level2Precon,2,2); // 250 s, 37 iter + PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.0, 1, CoarseM,Level2Precon,2,2); + + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(1.0, 100, CoarseM,Level2Precon,16,16); // 35 iter, 45s + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.6, 100, CoarseM,Level2Precon,16,16); // 26,38 (diifferene is measurement noise) + //PrecGeneralisedConjugateResidualNonHermitian L2PGCR(0.2, 100, CoarseM,Level2Precon,16,16); // 26 iter, 47s + L2PGCR.Level(2); + + // Wrap the 2nd level solver in a MultiGrid preconditioner acting on the fine space + + // ChebyshevSmoother FineSmoother1(0.5,60.0,14,FineM,Ddwf); // 26 iter, 39s + // ChebyshevSmoother FineSmoother2(0.5,60.0,14,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 25 iter, 38s + // ChebyshevSmoother FineSmoother2(0.5,60.0,16,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 23 iter, 39s + // ChebyshevSmoother FineSmoother2(0.5,60.0,20,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,10,FineM,Ddwf);24 iter, 44s + // ChebyshevSmoother FineSmoother2(0.5,60.0,24,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // odd convergence tail at 10^-9 ish + // ChebyshevSmoother FineSmoother2(0.1,60.0,24,FineM,Ddwf); // 33 iter, waas O(10-9 by 26) + + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 25 iter, 39s + // ChebyshevSmoother FineSmoother2(0.5,60.0,18,FineM,Ddwf); // + + ChebyshevSmoother FineSmoother(f_lo,f_hi,f_ord,FineM,Ddwf); + + // ChebyshevSmoother FineSmoother1(0.5,60.0,11,FineM,Ddwf); // 33 iter, 49s + // ChebyshevSmoother FineSmoother2(0.5,60.0,11,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.5,60.0,12,FineM,Ddwf); // 26 iter, 37s + // ChebyshevSmoother FineSmoother2(0.5,60.0,12,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.4,60.0,12,FineM,Ddwf); // iter 26 no change in final residual + // ChebyshevSmoother FineSmoother2(0.4,60.0,12,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.3,60.0,12,FineM,Ddwf); // 27 iter 39s. + // ChebyshevSmoother FineSmoother2(0.3,60.0,12,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(0.3,60.0,13,FineM,Ddwf); // 26 iter, but slower + // ChebyshevSmoother FineSmoother2(0.3,60.0,13,FineM,Ddwf); + // ChebyshevSmoother FineSmoother1(1.0,60.0,12,FineM,Ddwf); // 34 iter, slower + // ChebyshevSmoother FineSmoother2(1.0,60.0,12,FineM,Ddwf); + + ThreeLevelMG ThreeLevelPrecon(Aggregates4D, + FineM, + FineSmoother, + FineSmoother, + c_Dwf, + L2PGCR); + ThreeLevelPrecon.Level(1); + + PrecGeneralisedConjugateResidualNonHermitian L1PGCR(1.0e-8,1000,FineM,ThreeLevelPrecon,16,16); + L1PGCR.Level(1); + + f_res=Zero(); + L1PGCR(f_src,f_res); + } + }}}} + }}} + } + std::cout< Date: Thu, 14 Jan 2021 21:00:36 -0500 Subject: [PATCH 075/399] Gparity fix, and plaquette IO --- Grid/parallelIO/IldgIO.h | 22 +++--- Grid/parallelIO/MetaData.h | 34 +++----- Grid/parallelIO/NerscIO.h | 46 +++++------ Grid/parallelIO/OpenQcdIO.h | 2 +- Grid/parallelIO/OpenQcdIOChromaReference.h | 2 +- Grid/qcd/action/gauge/Gauge.cc | 38 +++++++++ Grid/qcd/action/gauge/GaugeImplementations.h | 79 +++++++++++-------- Grid/qcd/hmc/checkpointers/BaseCheckpointer.h | 3 +- Grid/qcd/hmc/checkpointers/ILDGCheckpointer.h | 5 +- .../qcd/hmc/checkpointers/NerscCheckpointer.h | 7 +- Grid/qcd/modules/Modules.h | 2 +- Grid/qcd/utils/CovariantCshift.h | 51 ++++++++++++ Grid/tensors/Tensor_Ta.h | 14 +++- tests/core/Test_reunitarise.cc | 3 +- tests/hmc/Test_hmc_EODWFRatio_Gparity.cc | 7 +- tests/hmc/Test_hmc_GparityIwasakiGauge.cc | 4 + tests/hmc/Test_hmc_GparityWilsonGauge.cc | 3 + 17 files changed, 220 insertions(+), 102 deletions(-) create mode 100644 Grid/qcd/action/gauge/Gauge.cc diff --git a/Grid/parallelIO/IldgIO.h b/Grid/parallelIO/IldgIO.h index b564371b..ef42c159 100644 --- a/Grid/parallelIO/IldgIO.h +++ b/Grid/parallelIO/IldgIO.h @@ -123,7 +123,7 @@ assert(GRID_FIELD_NORM_CALC(FieldNormMetaData_, n2ck) < 1.0e-5); //////////////////////////////////////////////////////////// // Helper to fill out metadata //////////////////////////////////////////////////////////// - template void ScidacMetaData(Lattice & field, +template void ScidacMetaData(Lattice & field, FieldMetaData &header, scidacRecord & _scidacRecord, scidacFile & _scidacFile) @@ -619,12 +619,12 @@ class IldgWriter : public ScidacWriter { // Don't require scidac records EXCEPT checksum // Use Grid MetaData object if present. //////////////////////////////////////////////////////////////// - template - void writeConfiguration(Lattice > &Umu,int sequence,std::string LFN,std::string description) + template + void writeConfiguration(Lattice &Umu,int sequence,std::string LFN,std::string description) { GridBase * grid = Umu.Grid(); - typedef Lattice > GaugeField; - typedef iLorentzColourMatrix vobj; + typedef Lattice GaugeField; + typedef vLorentzColourMatrixD vobj; typedef typename vobj::scalar_object sobj; //////////////////////////////////////// @@ -636,6 +636,9 @@ class IldgWriter : public ScidacWriter { ScidacMetaData(Umu,header,_scidacRecord,_scidacFile); + stats Stats; + Stats(Umu,header); + std::string format = header.floating_point; header.ensemble_id = description; header.ensemble_label = description; @@ -705,10 +708,10 @@ class IldgReader : public GridLimeReader { // Else use ILDG MetaData object if present. // Else use SciDAC MetaData object if present. //////////////////////////////////////////////////////////////// - template - void readConfiguration(Lattice > &Umu, FieldMetaData &FieldMetaData_) { + template + void readConfiguration(Lattice &Umu, FieldMetaData &FieldMetaData_) { - typedef Lattice > GaugeField; + typedef Lattice GaugeField; typedef typename GaugeField::vector_object vobj; typedef typename vobj::scalar_object sobj; @@ -921,7 +924,8 @@ class IldgReader : public GridLimeReader { if ( found_FieldMetaData || found_usqcdInfo ) { FieldMetaData checker; - GaugeStatistics(Umu,checker); + stats Stats; + Stats(Umu,checker); assert(fabs(checker.plaquette - FieldMetaData_.plaquette )<1.0e-5); assert(fabs(checker.link_trace - FieldMetaData_.link_trace)<1.0e-5); std::cout << GridLogMessage<<"Plaquette and link trace match " << std::endl; diff --git a/Grid/parallelIO/MetaData.h b/Grid/parallelIO/MetaData.h index 4c1cfbdb..d30ba523 100644 --- a/Grid/parallelIO/MetaData.h +++ b/Grid/parallelIO/MetaData.h @@ -176,29 +176,18 @@ template inline void PrepareMetaData(Lattice & field, FieldMet GridMetaData(grid,header); MachineCharacteristics(header); } -inline void GaugeStatistics(Lattice & data,FieldMetaData &header) +template +class GaugeStatistics { - // How to convert data precision etc... - header.link_trace=WilsonLoops::linkTrace(data); - header.plaquette =WilsonLoops::avgPlaquette(data); -} -inline void GaugeStatistics(Lattice & data,FieldMetaData &header) -{ - // How to convert data precision etc... - header.link_trace=WilsonLoops::linkTrace(data); - header.plaquette =WilsonLoops::avgPlaquette(data); -} -template<> inline void PrepareMetaData(Lattice & field, FieldMetaData &header) -{ - - GridBase *grid = field.Grid(); - std::string format = getFormatString(); - header.floating_point = format; - header.checksum = 0x0; // Nersc checksum unused in ILDG, Scidac - GridMetaData(grid,header); - GaugeStatistics(field,header); - MachineCharacteristics(header); -} +public: + void operator()(Lattice & data,FieldMetaData &header) + { + header.link_trace=WilsonLoops::linkTrace(data); + header.plaquette =WilsonLoops::avgPlaquette(data); + } +}; +typedef GaugeStatistics PeriodicGaugeStatistics; +typedef GaugeStatistics ConjugateGaugeStatistics; template<> inline void PrepareMetaData(Lattice & field, FieldMetaData &header) { GridBase *grid = field.Grid(); @@ -206,7 +195,6 @@ template<> inline void PrepareMetaData(Lattice GaugeField; + static inline void truncate(std::string file){ std::ofstream fout(file,std::ios::out); } @@ -129,12 +131,12 @@ public: // Now the meat: the object readers ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template - static inline void readConfiguration(Lattice > &Umu, + template + static inline void readConfiguration(GaugeField &Umu, FieldMetaData& header, - std::string file) + std::string file, + GaugeStats GaugeStatisticsCalculator=GaugeStats()) { - typedef Lattice > GaugeField; GridBase *grid = Umu.Grid(); uint64_t offset = readHeader(file,Umu.Grid(),header); @@ -153,23 +155,23 @@ public: // munger is a function of if ( header.data_type == std::string("4D_SU3_GAUGE") ) { if ( ieee32 || ieee32big ) { - BinaryIO::readLatticeObject, LorentzColour2x3F> + BinaryIO::readLatticeObject (Umu,file,Gauge3x2munger(), offset,format, nersc_csum,scidac_csuma,scidac_csumb); } if ( ieee64 || ieee64big ) { - BinaryIO::readLatticeObject, LorentzColour2x3D> + BinaryIO::readLatticeObject (Umu,file,Gauge3x2munger(),offset,format, nersc_csum,scidac_csuma,scidac_csumb); } } else if ( header.data_type == std::string("4D_SU3_GAUGE_3x3") ) { if ( ieee32 || ieee32big ) { - BinaryIO::readLatticeObject,LorentzColourMatrixF> + BinaryIO::readLatticeObject (Umu,file,GaugeSimpleMunger(),offset,format, nersc_csum,scidac_csuma,scidac_csumb); } if ( ieee64 || ieee64big ) { - BinaryIO::readLatticeObject,LorentzColourMatrixD> + BinaryIO::readLatticeObject (Umu,file,GaugeSimpleMunger(),offset,format, nersc_csum,scidac_csuma,scidac_csumb); } @@ -177,7 +179,7 @@ public: assert(0); } - GaugeStatistics(Umu,clone); + GaugeStats Stats; Stats(Umu,clone); std::cout< - static inline void writeConfiguration(Lattice > &Umu, + template + static inline void writeConfiguration(Lattice &Umu, std::string file, int two_row, int bits32) { - typedef Lattice > GaugeField; - - typedef iLorentzColourMatrix vobj; + typedef vLorentzColourMatrixD vobj; typedef typename vobj::scalar_object sobj; FieldMetaData header; @@ -229,7 +229,7 @@ public: GridMetaData(grid,header); assert(header.nd==4); - GaugeStatistics(Umu,header); + GaugeStats Stats; Stats(Umu,header); MachineCharacteristics(header); uint64_t offset; @@ -238,19 +238,19 @@ public: header.floating_point = std::string("IEEE64BIG"); header.data_type = std::string("4D_SU3_GAUGE_3x3"); GaugeSimpleUnmunger munge; - if ( grid->IsBoss() ) { - truncate(file); - offset = writeHeader(header,file); - } - grid->Broadcast(0,(void *)&offset,sizeof(offset)); + if ( grid->IsBoss() ) { + truncate(file); + offset = writeHeader(header,file); + } + grid->Broadcast(0,(void *)&offset,sizeof(offset)); uint32_t nersc_csum,scidac_csuma,scidac_csumb; BinaryIO::writeLatticeObject(Umu,file,munge,offset,header.floating_point, nersc_csum,scidac_csuma,scidac_csumb); header.checksum = nersc_csum; - if ( grid->IsBoss() ) { - writeHeader(header,file); - } + if ( grid->IsBoss() ) { + writeHeader(header,file); + } std::cout<Barrier(); timer.Stop(); std::cout << Grid::GridLogMessage << "OpenQcdIO::readConfiguration: redistribute overhead " << timer.Elapsed() << std::endl; - GaugeStatistics(Umu, clone); + PeriodicGaugeStatistics Stats; Stats(Umu, clone); RealD plaq_diff = fabs(clone.plaquette - header.plaquette); diff --git a/Grid/parallelIO/OpenQcdIOChromaReference.h b/Grid/parallelIO/OpenQcdIOChromaReference.h index bab54fe8..886536ad 100644 --- a/Grid/parallelIO/OpenQcdIOChromaReference.h +++ b/Grid/parallelIO/OpenQcdIOChromaReference.h @@ -208,7 +208,7 @@ public: FieldMetaData clone(header); - GaugeStatistics(Umu, clone); + PeriodicGaugeStatistics Stats; Stats(Umu, clone); RealD plaq_diff = fabs(clone.plaquette - header.plaquette); diff --git a/Grid/qcd/action/gauge/Gauge.cc b/Grid/qcd/action/gauge/Gauge.cc new file mode 100644 index 00000000..2b5e2691 --- /dev/null +++ b/Grid/qcd/action/gauge/Gauge.cc @@ -0,0 +1,38 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: ./lib/qcd/action/gauge/Gauge.cc + +Copyright (C) 2020 + +Author: Peter Boyle +Author: Peter Boyle +Author: paboyle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +See the full license in the file "LICENSE" in the top level distribution +directory +*************************************************************************************/ +/* END LEGAL */ +#include + +NAMESPACE_BEGIN(Grid); + +std::vector ConjugateGaugeImplBase::_conjDirs; + +NAMESPACE_END(Grid); + diff --git a/Grid/qcd/action/gauge/GaugeImplementations.h b/Grid/qcd/action/gauge/GaugeImplementations.h index a14aec1b..16147c77 100644 --- a/Grid/qcd/action/gauge/GaugeImplementations.h +++ b/Grid/qcd/action/gauge/GaugeImplementations.h @@ -59,14 +59,14 @@ public: } static inline GaugeLinkField CovShiftIdentityBackward(const GaugeLinkField &Link, int mu) { - return Cshift(adj(Link), mu, -1); + return PeriodicBC::CovShiftIdentityBackward(Link, mu); } static inline GaugeLinkField CovShiftIdentityForward(const GaugeLinkField &Link, int mu) { - return Link; + return PeriodicBC::CovShiftIdentityForward(Link,mu); } static inline GaugeLinkField ShiftStaple(const GaugeLinkField &Link, int mu) { - return Cshift(Link, mu, 1); + return PeriodicBC::ShiftStaple(Link,mu); } static inline bool isPeriodicGaugeField(void) { return true; } @@ -74,7 +74,13 @@ public: // Composition with smeared link, bc's etc.. probably need multiple inheritance // Variable precision "S" and variable Nc -template class ConjugateGaugeImpl : public GimplTypes { +class ConjugateGaugeImplBase { +protected: + static std::vector _conjDirs; +}; + + template class ConjugateGaugeImpl : public GimplTypes, ConjugateGaugeImplBase { +private: public: INHERIT_GIMPL_TYPES(GimplTypes); @@ -84,47 +90,56 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////////////// template static Lattice CovShiftForward(const GaugeLinkField &Link, int mu, - const Lattice &field) { - return ConjugateBC::CovShiftForward(Link, mu, field); + const Lattice &field) + { + assert(_conjDirs.size() == Nd); + if(_conjDirs[mu]) + return ConjugateBC::CovShiftForward(Link, mu, field); + else + return PeriodicBC::CovShiftForward(Link, mu, field); } template static Lattice CovShiftBackward(const GaugeLinkField &Link, int mu, - const Lattice &field) { - return ConjugateBC::CovShiftBackward(Link, mu, field); + const Lattice &field) + { + assert(_conjDirs.size() == Nd); + if(_conjDirs[mu]) + return ConjugateBC::CovShiftBackward(Link, mu, field); + else + return PeriodicBC::CovShiftBackward(Link, mu, field); } static inline GaugeLinkField - CovShiftIdentityBackward(const GaugeLinkField &Link, int mu) { - GridBase *grid = Link.Grid(); - int Lmu = grid->GlobalDimensions()[mu] - 1; - - Lattice> coor(grid); - LatticeCoordinate(coor, mu); - - GaugeLinkField tmp(grid); - tmp = adj(Link); - tmp = where(coor == Lmu, conjugate(tmp), tmp); - return Cshift(tmp, mu, -1); // moves towards positive mu + CovShiftIdentityBackward(const GaugeLinkField &Link, int mu) + { + assert(_conjDirs.size() == Nd); + if(_conjDirs[mu]) + return ConjugateBC::CovShiftIdentityBackward(Link, mu); + else + return PeriodicBC::CovShiftIdentityBackward(Link, mu); } static inline GaugeLinkField - CovShiftIdentityForward(const GaugeLinkField &Link, int mu) { - return Link; + CovShiftIdentityForward(const GaugeLinkField &Link, int mu) + { + assert(_conjDirs.size() == Nd); + if(_conjDirs[mu]) + return ConjugateBC::CovShiftIdentityForward(Link,mu); + else + return PeriodicBC::CovShiftIdentityForward(Link,mu); } - static inline GaugeLinkField ShiftStaple(const GaugeLinkField &Link, int mu) { - GridBase *grid = Link.Grid(); - int Lmu = grid->GlobalDimensions()[mu] - 1; - - Lattice> coor(grid); - LatticeCoordinate(coor, mu); - - GaugeLinkField tmp(grid); - tmp = Cshift(Link, mu, 1); - tmp = where(coor == Lmu, conjugate(tmp), tmp); - return tmp; + static inline GaugeLinkField ShiftStaple(const GaugeLinkField &Link, int mu) + { + assert(_conjDirs.size() == Nd); + if(_conjDirs[mu]) + return ConjugateBC::ShiftStaple(Link,mu); + else + return PeriodicBC::ShiftStaple(Link,mu); } + static inline void setDirections(std::vector &conjDirs) { _conjDirs=conjDirs; } + static inline std::vector getDirections(void) { return _conjDirs; } static inline bool isPeriodicGaugeField(void) { return false; } }; diff --git a/Grid/qcd/hmc/checkpointers/BaseCheckpointer.h b/Grid/qcd/hmc/checkpointers/BaseCheckpointer.h index 3cd05ebc..c09fdeeb 100644 --- a/Grid/qcd/hmc/checkpointers/BaseCheckpointer.h +++ b/Grid/qcd/hmc/checkpointers/BaseCheckpointer.h @@ -74,7 +74,7 @@ public: conf_file = os.str(); } } - + virtual ~BaseHmcCheckpointer(){}; void check_filename(const std::string &filename){ std::ifstream f(filename.c_str()); if(!f.good()){ @@ -82,7 +82,6 @@ public: abort(); }; } - virtual void initialize(const CheckpointerParameters &Params) = 0; virtual void CheckpointRestore(int traj, typename Impl::Field &U, diff --git a/Grid/qcd/hmc/checkpointers/ILDGCheckpointer.h b/Grid/qcd/hmc/checkpointers/ILDGCheckpointer.h index 269caa6e..1bb8aa1a 100644 --- a/Grid/qcd/hmc/checkpointers/ILDGCheckpointer.h +++ b/Grid/qcd/hmc/checkpointers/ILDGCheckpointer.h @@ -45,6 +45,7 @@ private: public: INHERIT_GIMPL_TYPES(Implementation); + typedef GaugeStatistics GaugeStats; ILDGHmcCheckpointer(const CheckpointerParameters &Params_) { initialize(Params_); } @@ -78,7 +79,7 @@ public: BinaryIO::writeRNG(sRNG, pRNG, rng, 0,nersc_csum,scidac_csuma,scidac_csumb); IldgWriter _IldgWriter(grid->IsBoss()); _IldgWriter.open(config); - _IldgWriter.writeConfiguration(U, traj, config, config); + _IldgWriter.writeConfiguration(U, traj, config, config); _IldgWriter.close(); std::cout << GridLogMessage << "Written ILDG Configuration on " << config @@ -105,7 +106,7 @@ public: FieldMetaData header; IldgReader _IldgReader; _IldgReader.open(config); - _IldgReader.readConfiguration(U,header); // format from the header + _IldgReader.readConfiguration(U,header); // format from the header _IldgReader.close(); std::cout << GridLogMessage << "Read ILDG Configuration from " << config diff --git a/Grid/qcd/hmc/checkpointers/NerscCheckpointer.h b/Grid/qcd/hmc/checkpointers/NerscCheckpointer.h index cfcc44d8..4534e4c4 100644 --- a/Grid/qcd/hmc/checkpointers/NerscCheckpointer.h +++ b/Grid/qcd/hmc/checkpointers/NerscCheckpointer.h @@ -43,7 +43,8 @@ private: public: INHERIT_GIMPL_TYPES(Gimpl); // only for gauge configurations - + typedef GaugeStatistics GaugeStats; + NerscHmcCheckpointer(const CheckpointerParameters &Params_) { initialize(Params_); } void initialize(const CheckpointerParameters &Params_) { @@ -60,7 +61,7 @@ public: int precision32 = 1; int tworow = 0; NerscIO::writeRNGState(sRNG, pRNG, rng); - NerscIO::writeConfiguration(U, config, tworow, precision32); + NerscIO::writeConfiguration(U, config, tworow, precision32); } }; @@ -74,7 +75,7 @@ public: FieldMetaData header; NerscIO::readRNGState(sRNG, pRNG, header, rng); - NerscIO::readConfiguration(U, header, config); + NerscIO::readConfiguration(U, header, config); }; }; diff --git a/Grid/qcd/modules/Modules.h b/Grid/qcd/modules/Modules.h index 1c1c8889..7aa3f0ac 100644 --- a/Grid/qcd/modules/Modules.h +++ b/Grid/qcd/modules/Modules.h @@ -99,7 +99,7 @@ public: virtual Prod* getPtr() = 0; // add a getReference? - + virtual ~HMCModuleBase(){}; virtual void print_parameters(){}; // default to nothing }; diff --git a/Grid/qcd/utils/CovariantCshift.h b/Grid/qcd/utils/CovariantCshift.h index cee1fa12..6c70706f 100644 --- a/Grid/qcd/utils/CovariantCshift.h +++ b/Grid/qcd/utils/CovariantCshift.h @@ -53,6 +53,24 @@ namespace PeriodicBC { return Cshift(tmp,mu,-1);// moves towards positive mu } + template Lattice + CovShiftIdentityBackward(const Lattice &Link, int mu) + { + return Cshift(adj(Link), mu, -1); + } + + template Lattice + CovShiftIdentityForward(const Lattice &Link, int mu) + { + return Link; + } + + template Lattice + ShiftStaple(const Lattice &Link, int mu) + { + return Cshift(Link, mu, 1); + } + template::value,void>::type * = nullptr> auto CovShiftForward(const Lattice &Link, int mu, @@ -70,6 +88,7 @@ namespace PeriodicBC { return CovShiftBackward(Link,mu,arg); } + } @@ -139,6 +158,38 @@ namespace ConjugateBC { // std::cout<<"Gparity::CovCshiftBackward mu="< Lattice + CovShiftIdentityBackward(const Lattice &Link, int mu) { + GridBase *grid = Link.Grid(); + int Lmu = grid->GlobalDimensions()[mu] - 1; + + Lattice> coor(grid); + LatticeCoordinate(coor, mu); + + Lattice tmp(grid); + tmp = adj(Link); + tmp = where(coor == Lmu, conjugate(tmp), tmp); + return Cshift(tmp, mu, -1); // moves towards positive mu + } + template Lattice + CovShiftIdentityForward(const Lattice &Link, int mu) { + return Link; + } + + template Lattice + ShiftStaple(const Lattice &Link, int mu) + { + GridBase *grid = Link.Grid(); + int Lmu = grid->GlobalDimensions()[mu] - 1; + + Lattice> coor(grid); + LatticeCoordinate(coor, mu); + + Lattice tmp(grid); + tmp = Cshift(Link, mu, 1); + tmp = where(coor == Lmu, conjugate(tmp), tmp); + return tmp; + } template::value,void>::type * = nullptr> auto CovShiftForward(const Lattice &Link, diff --git a/Grid/tensors/Tensor_Ta.h b/Grid/tensors/Tensor_Ta.h index bbaa4a00..90e57b2b 100644 --- a/Grid/tensors/Tensor_Ta.h +++ b/Grid/tensors/Tensor_Ta.h @@ -117,7 +117,19 @@ accelerator_inline iMatrix ProjectOnGroup(const iMatrix &arg) ret._internal[b][c] -= pr * ret._internal[c1][c]; } } - + } + + // Normalise last row + { + int c1 = N-1; + zeroit(inner); + for(int c2=0;c2(U,Nc-1,i); element = element * phase; PokeIndex(U,element,Nc-1,i); - } + } + U=U*0.1; UU=U; detU= Determinant(U) ; diff --git a/tests/hmc/Test_hmc_EODWFRatio_Gparity.cc b/tests/hmc/Test_hmc_EODWFRatio_Gparity.cc index 3434fccc..9ca0b0a0 100644 --- a/tests/hmc/Test_hmc_EODWFRatio_Gparity.cc +++ b/tests/hmc/Test_hmc_EODWFRatio_Gparity.cc @@ -81,6 +81,10 @@ int main(int argc, char **argv) { // that have a complex construction // standard RealD beta = 5.6 ; + const int nu = 3; + std::vector twists(Nd,0); + twists[nu] = 1; + ConjugateGimplD::setDirections(twists); ConjugateWilsonGaugeActionR Waction(beta); const int Ls = 8; @@ -93,9 +97,6 @@ int main(int argc, char **argv) { // temporarily need a gauge field LatticeGaugeField U(GridPtr); - const int nu = 3; - std::vector twists(Nd,0); - twists[nu] = 1; FermionAction::ImplParams params; params.twists = twists; Real mass=0.04; diff --git a/tests/hmc/Test_hmc_GparityIwasakiGauge.cc b/tests/hmc/Test_hmc_GparityIwasakiGauge.cc index bc47b6c2..7f74d5d8 100644 --- a/tests/hmc/Test_hmc_GparityIwasakiGauge.cc +++ b/tests/hmc/Test_hmc_GparityIwasakiGauge.cc @@ -79,6 +79,10 @@ int main(int argc, char **argv) { // that have a complex construction // standard RealD beta = 2.6 ; + const int nu = 3; + std::vector twists(Nd,0); + twists[nu] = 1; + ConjugateGimplD::setDirections(twists); ConjugateIwasakiGaugeActionR Waction(beta); diff --git a/tests/hmc/Test_hmc_GparityWilsonGauge.cc b/tests/hmc/Test_hmc_GparityWilsonGauge.cc index eb057181..b8c078fe 100644 --- a/tests/hmc/Test_hmc_GparityWilsonGauge.cc +++ b/tests/hmc/Test_hmc_GparityWilsonGauge.cc @@ -80,6 +80,9 @@ int main(int argc, char **argv) { // that have a complex construction // standard RealD beta = 5.6 ; + std::vector twists(Nd,0); + twists[3] = 1; + ConjugateGimplD::setDirections(twists); ConjugateWilsonGaugeActionR Waction(beta); From 3c23a947cc4e22b6c01afd9eac5d5a4add9035c7 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 15 Jan 2021 09:16:02 -0500 Subject: [PATCH 076/399] Fixed test for very much non-unit det --- tests/core/Test_reunitarise.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/Test_reunitarise.cc b/tests/core/Test_reunitarise.cc index af164a75..6644be1a 100644 --- a/tests/core/Test_reunitarise.cc +++ b/tests/core/Test_reunitarise.cc @@ -103,7 +103,7 @@ int main (int argc, char ** argv) detU= Determinant(U) ; detU=detU-1.0; - std::cout << "Determinant before screw up " << norm2(detU)< Date: Mon, 18 Jan 2021 18:57:05 +0000 Subject: [PATCH 077/399] bugfix --- Grid/qcd/utils/BaryonUtils.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 69bf8959..edc5c8d5 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -951,7 +951,7 @@ void BaryonUtils::BaryonGamma3pt( spinor result=Zero(); BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec_p[0],Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); //coalescedWrite(vcorr[ss],vcorr[ss]+result); //diff by factor 10??? - coalescedWrite(vcorr[ss],vcorr[ss]+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); });//end loop over lattice sites } else if (group == 2) { @@ -961,7 +961,7 @@ void BaryonUtils::BaryonGamma3pt( typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup2Site(Dq_spec_p[0],Dq_ti,Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],vcorr[ss]+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); });//end loop over lattice sites } else if (group == 3) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { @@ -970,7 +970,7 @@ void BaryonUtils::BaryonGamma3pt( typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup3Site(Dq_spec_p[0],Dq_spec_p[1],Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],vcorr[ss]+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); });//end loop over lattice sites } From 8bfa0e74f837c914efaf95929149f9ce5b0a5487 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 19 Jan 2021 12:27:57 +0000 Subject: [PATCH 078/399] final version, tested on CPU and GPU --- Grid/qcd/utils/BaryonUtils.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index edc5c8d5..ca8b66ef 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -950,7 +950,6 @@ void BaryonUtils::BaryonGamma3pt( typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec_p[0],Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - //coalescedWrite(vcorr[ss],vcorr[ss]+result); //diff by factor 10??? coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); });//end loop over lattice sites From fc6d07897fe4a9f7f1a80bd9a7849fe373648b42 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 19 Jan 2021 12:32:48 +0000 Subject: [PATCH 079/399] revert changes --- tests/solver/Test_zMADWF_prec.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/solver/Test_zMADWF_prec.cc b/tests/solver/Test_zMADWF_prec.cc index f18e1d86..d1168764 100644 --- a/tests/solver/Test_zMADWF_prec.cc +++ b/tests/solver/Test_zMADWF_prec.cc @@ -52,7 +52,7 @@ struct TestParams{ bool zmobius_inner; double lambda_max; //upper bound of H_T eigenvalue range required to generate zMobius approximation - TestParams(): load_config(false), config_file("ckpoint_lat.1000"), mass(0.01), + TestParams(): load_config(true), config_file("ckpoint_lat.1000"), mass(0.01), Ls_outer(24), b_plus_c_outer(2.0), resid_outer(1e-8), Ls_inner(12), b_plus_c_inner(1.0), resid_inner(1e-8), zmobius_inner(true), lambda_max(1.42), outer_precon("Standard"), inner_precon("Standard") {} @@ -246,7 +246,7 @@ void run(const TestParams ¶ms){ typename RunParamsInner::SchurSolverType SchurSolver_inner(CG_inner); ZeroGuesser Guess; - MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 10000, &update); + MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 100, &update); LatticeFermionD result_MADWF(FGrid_outer); result_MADWF = Zero(); From df16202865e5ff8277b805336b12a054b316ea08 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 19 Jan 2021 19:25:27 +0000 Subject: [PATCH 080/399] weird bug in 2pt function... --- Grid/qcd/utils/BaryonUtils.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index ca8b66ef..7393c232 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -513,6 +513,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, GridBase *grid = q1_left.Grid(); autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( vcorr_read , baryon_corr , AcceleratorRead); autoView( v1 , q1_left , AcceleratorRead); autoView( v2 , q2_left , AcceleratorRead); autoView( v3 , q3_left , AcceleratorRead); @@ -533,7 +534,8 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, auto D1 = v1(ss); auto D2 = v2(ss); auto D3 = v3(ss); - typedef decltype(coalescedRead(vbaryon_corr[0])) cVec; + //typedef decltype(coalescedRead(vbaryon_corr[0])) cVec; + typedef decltype(coalescedRead(vcorr_read[0])) cVec; cVec result=Zero(); BaryonSite(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,parity,wick_contractions,result); coalescedWrite(vbaryon_corr[ss],result); @@ -562,6 +564,7 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, GridBase *grid = q1_left.Grid(); autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( vcorr_read , baryon_corr , AcceleratorRead); autoView( v1 , q1_left , AcceleratorRead); autoView( v2 , q2_left , AcceleratorRead); autoView( v3 , q3_left , AcceleratorRead); @@ -570,7 +573,8 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, auto D1 = v1(ss); auto D2 = v2(ss); auto D3 = v3(ss); - typedef decltype(coalescedRead(vbaryon_corr[0])) spinor; + //typedef decltype(coalescedRead(vbaryon_corr[0])) spinor; + typedef decltype(coalescedRead(vcorr_read[0])) spinor; spinor result=Zero(); BaryonSiteMatrix(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,wick_contractions,result); coalescedWrite(vbaryon_corr[ss],result); @@ -937,6 +941,7 @@ void BaryonUtils::BaryonGamma3pt( GridBase *grid = q_tf.Grid(); autoView( vcorr , stn_corr , AcceleratorWrite); + autoView( vcorr_read , stn_corr , AcceleratorRead); autoView( vq_ti , q_ti , AcceleratorRead); autoView( vq_tf , q_tf , AcceleratorRead); @@ -947,29 +952,29 @@ void BaryonUtils::BaryonGamma3pt( accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_ti = vq_ti(ss); auto Dq_tf = vq_tf(ss); - typedef decltype(coalescedRead(vcorr[0])) spinor; + typedef decltype(coalescedRead(vcorr_read[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec_p[0],Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr_read[ss])+result); });//end loop over lattice sites } else if (group == 2) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_ti = vq_ti(ss); auto Dq_tf = vq_tf(ss); - typedef decltype(coalescedRead(vcorr[0])) spinor; + typedef decltype(coalescedRead(vcorr_read[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup2Site(Dq_spec_p[0],Dq_ti,Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr_read[ss])+result); });//end loop over lattice sites } else if (group == 3) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_ti = vq_ti(ss); auto Dq_tf = vq_tf(ss); - typedef decltype(coalescedRead(vcorr[0])) spinor; + typedef decltype(coalescedRead(vcorr_read[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup3Site(Dq_spec_p[0],Dq_spec_p[1],Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr_read[ss])+result); });//end loop over lattice sites } From ff1fa988085ae868ae603fe9562968b3dcd57da2 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 21 Jan 2021 21:38:23 -0500 Subject: [PATCH 081/399] Fix for GPU conserveed current --- Grid/qcd/action/fermion/WilsonImpl.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonImpl.h b/Grid/qcd/action/fermion/WilsonImpl.h index 52e1ee00..d7941d1f 100644 --- a/Grid/qcd/action/fermion/WilsonImpl.h +++ b/Grid/qcd/action/fermion/WilsonImpl.h @@ -106,11 +106,15 @@ public: const _SpinorField & phi, int mu) { + const int Nsimd = SiteHalfSpinor::Nsimd(); autoView( out_v, out, AcceleratorWrite); autoView( phi_v, phi, AcceleratorRead); autoView( Umu_v, Umu, AcceleratorRead); - accelerator_for(sss,out.Grid()->oSites(),1,{ - multLink(out_v[sss],Umu_v[sss],phi_v[sss],mu); + typedef decltype(coalescedRead(out_v[0])) calcSpinor; + accelerator_for(sss,out.Grid()->oSites(),Nsimd,{ + calcSpinor tmp; + multLink(tmp,Umu_v[sss],phi_v(sss),mu); + coalescedWrite(out_v[sss],tmp); }); } From 11a5fd09d65427aaaa68d9dc28318bfe92b08097 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 21 Jan 2021 21:39:41 -0500 Subject: [PATCH 082/399] Hot config --- tests/debug/Test_cayley_mres.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/debug/Test_cayley_mres.cc b/tests/debug/Test_cayley_mres.cc index 5282c756..ea88885e 100644 --- a/tests/debug/Test_cayley_mres.cc +++ b/tests/debug/Test_cayley_mres.cc @@ -117,8 +117,8 @@ int main (int argc, char ** argv) else { std::cout<::ColdConfiguration(Umu); - // SU::HotConfiguration(RNG4,Umu); + //SU::ColdConfiguration(Umu); + SU::HotConfiguration(RNG4,Umu); } RealD mass=0.3; From 2983b6fdf6485569c7d621bb22e4dcec23633e22 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 23 Jan 2021 12:41:48 +0000 Subject: [PATCH 083/399] Optional (superficial) changes to make comparison with Hadrons WardIdentity module easier: use Schur solver; example of Hadrons random gauge init; logging updates; only solve reverse propagator if provided --- tests/debug/Test_cayley_mres.cc | 86 +++++++++++++++------------------ 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/tests/debug/Test_cayley_mres.cc b/tests/debug/Test_cayley_mres.cc index ea88885e..bfbc3cf7 100644 --- a/tests/debug/Test_cayley_mres.cc +++ b/tests/debug/Test_cayley_mres.cc @@ -33,13 +33,14 @@ using namespace Grid; template -void TestConserved(What & Ddwf, What & Ddwfrev, +void TestConserved(What & Ddwf, LatticeGaugeField &Umu, GridCartesian * FGrid, GridRedBlackCartesian * FrbGrid, GridCartesian * UGrid, GridRedBlackCartesian * UrbGrid, RealD mass, RealD M5, GridParallelRNG *RNG4, - GridParallelRNG *RNG5); + GridParallelRNG *RNG5, + What *Ddwfrev=nullptr); Gamma::Algebra Gmu [] = { Gamma::Algebra::GammaX, @@ -102,10 +103,11 @@ int main (int argc, char ** argv) GridRedBlackCartesian * FrbGridF = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGridF); - std::vector seeds4({1,2,3,4}); std::vector seeds5({5,6,7,8}); GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); - GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + GridParallelRNG RNG4(UGrid); + std::vector seeds4({1,2,3,4}); RNG4.SeedFixedIntegers(seeds4); + //const std::string seeds4{ "test-gauge-3000" }; RNG4.SeedUniqueString( seeds4 ); LatticeGaugeField Umu(UGrid); if( argc > 1 && argv[1][0] != '-' ) @@ -116,8 +118,8 @@ int main (int argc, char ** argv) } else { - std::cout<::ColdConfiguration(Umu); + std::cout<::ColdConfiguration(Umu); SU::HotConfiguration(RNG4,Umu); } @@ -127,7 +129,7 @@ int main (int argc, char ** argv) std::cout<(Ddwf,Ddwf,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + TestConserved(Ddwf,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); RealD b=1.5;// Scale factor b+c=2, b-c=1 RealD c=0.5; @@ -137,13 +139,13 @@ int main (int argc, char ** argv) std::cout<(Dmob,Dmob,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + TestConserved(Dmob,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); std::cout<(Dsham,Dsham,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + TestConserved(Dsham,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); std::cout<(ZDmob,ZDmobrev,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + TestConserved(ZDmob,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5,&ZDmobrev); Grid_finalize(); } @@ -161,22 +162,17 @@ int main (int argc, char ** argv) template -void TestConserved(Action & Ddwf, - Action & Ddwfrev, +void TestConserved(Action & Ddwf, LatticeGaugeField &Umu, GridCartesian * FGrid, GridRedBlackCartesian * FrbGrid, GridCartesian * UGrid, GridRedBlackCartesian * UrbGrid, RealD mass, RealD M5, GridParallelRNG *RNG4, - GridParallelRNG *RNG5) + GridParallelRNG *RNG5, + Action * Ddwfrev) { - int Ls=Ddwf.Ls; - - LatticePropagator phys_src(UGrid); - - std::vector U(4,UGrid); - - LatticePropagator seqsrc(FGrid); + LatticePropagator phys_src(UGrid); + LatticePropagator seqsrc(FGrid); LatticePropagator prop5(FGrid); LatticePropagator prop5rev(FGrid); LatticePropagator prop4(UGrid); @@ -194,9 +190,9 @@ void TestConserved(Action & Ddwf, phys_src=Zero(); pokeSite(kronecker,phys_src,coor); - MdagMLinearOperator HermOp(Ddwf); - MdagMLinearOperator HermOprev(Ddwfrev); ConjugateGradient CG(1.0e-16,100000); + SchurRedBlackDiagTwoSolve schur(CG); + ZeroGuesser zpg; for(int s=0;s(prop5,result5,s,c); LatticeFermion result4(UGrid); Ddwf.ExportPhysicalFermionSolution(result5,result4); FermToProp(prop4,result4,s,c); - Ddwfrev.ImportPhysicalFermionSource(src4,src5); - Ddwfrev.Mdag(src5,Mdagsrc5); - CG(HermOprev,Mdagsrc5,result5); + if( Ddwfrev ) { + Ddwfrev->ImportPhysicalFermionSource(src4,src5); + result5 = Zero(); + schur(*Ddwfrev,src5,result5,zpg); + } FermToProp(prop5rev,result5,s,c); } } @@ -251,11 +247,7 @@ void TestConserved(Action & Ddwf, PropToFerm(src5,seqsrc,s,c); LatticeFermion result5(FGrid); result5=Zero(); - - // CGNE - LatticeFermion Mdagsrc5 (FGrid); - Ddwf.Mdag(src5,Mdagsrc5); - CG(HermOp,Mdagsrc5,result5); + schur(Ddwf,src5,result5,zpg); LatticeFermion result4(UGrid); Ddwf.ExportPhysicalFermionSolution(result5,result4); @@ -276,10 +268,10 @@ void TestConserved(Action & Ddwf, Ddwf.ContractConservedCurrent(prop5rev,prop5,Vector_mu,phys_src,Current::Vector,Tdir); Ddwf.ContractJ5q(prop5,PJ5q); - PA = trace(g5*Axial_mu); - SV = trace(Vector_mu); - VV = trace(gT*Vector_mu); - PP = trace(adj(prop4)*prop4); + PA = trace(g5*Axial_mu); // Pseudoscalar-Axial conserved current + SV = trace(Vector_mu); // Scalar-Vector conserved current + VV = trace(gT*Vector_mu); // (local) Vector-Vector conserved current + PP = trace(adj(prop4)*prop4); // Pseudoscalar density // Spatial sum sliceSum(PA,sumPA,Tdir); @@ -288,15 +280,17 @@ void TestConserved(Action & Ddwf, sliceSum(PP,sumPP,Tdir); sliceSum(PJ5q,sumPJ5q,Tdir); - int Nt=sumPA.size(); + const int Nt{static_cast(sumPA.size())}; + std::cout< Date: Mon, 25 Jan 2021 15:09:36 +0000 Subject: [PATCH 084/399] Fix issue for GPU by ensuring accelerator_inline version of convertType is available for Grid::complex. This removes many warnings in Hadrons Simplify the SFINAE syntax and correct convertType for iScalar --- Grid/lattice/Lattice_transfer.h | 39 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 91de721f..c91fa4d1 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -97,6 +97,21 @@ accelerator_inline void convertType(ComplexF & out, const std::complex & out = in; } +template +accelerator_inline typename std::enable_if::value>::type +convertType(T & out, const T & in) { + out = in; +} + +// This would allow for conversions between GridFundamental types, but is not strictly needed as yet +/*template +accelerator_inline typename std::enable_if::value && isGridFundamental::value>::type +// Or to make this very broad, conversions between anything that's not a GridTensor could be allowed +//accelerator_inline typename std::enable_if::value && !isGridTensor::value>::type +convertType(T1 & out, const T2 & in) { + out = in; +}*/ + #ifdef GRID_SIMT accelerator_inline void convertType(vComplexF & out, const ComplexF & in) { ((ComplexF*)&out)[acceleratorSIMTlane(vComplexF::Nsimd())] = in; @@ -117,23 +132,20 @@ accelerator_inline void convertType(vComplexD2 & out, const vComplexF & in) { Optimization::PrecisionChange::StoD(in.v,out._internal[0].v,out._internal[1].v); } -template - accelerator_inline void convertType(iMatrix & out, const iMatrix & in); -template - accelerator_inline void convertType(iVector & out, const iVector & in); - -template::value, T1>::type* = nullptr> -accelerator_inline void convertType(T1 & out, const iScalar & in) { - convertType(out,in._internal); +template +accelerator_inline void convertType(iScalar & out, const iScalar & in) { + convertType(out._internal,in._internal); } -template::value, T1>::type* = nullptr> -accelerator_inline void convertType(T1 & out, const iScalar & in) { +template +accelerator_inline typename std::enable_if::value>::type +convertType(T1 & out, const iScalar & in) { convertType(out,in._internal); } template -accelerator_inline void convertType(iScalar & out, const T2 & in) { +accelerator_inline typename std::enable_if::value>::type +convertType(iScalar & out, const T2 & in) { convertType(out._internal,in); } @@ -150,11 +162,6 @@ accelerator_inline void convertType(iVector & out, const iVector & i convertType(out._internal[i],in._internal[i]); } -template::value, T>::type* = nullptr> -accelerator_inline void convertType(T & out, const T & in) { - out = in; -} - template accelerator_inline void convertType(Lattice & out, const Lattice & in) { autoView( out_v , out,AcceleratorWrite); From 81d88d9f4df1c9944c8b33db6dd123c36560757d Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Wed, 27 Jan 2021 21:09:51 +0000 Subject: [PATCH 085/399] fixes --- Grid/qcd/utils/BaryonUtils.h | 63 ++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 7393c232..d6b48ba0 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -52,7 +52,7 @@ public: const Gamma GammaA_right, const Gamma GammaB_right, const int parity, - const bool * wick_contractions, + const int wick_contractions, robj &result); template accelerator_inline static void BaryonSiteMatrix(const mobj &D1, @@ -62,12 +62,12 @@ public: const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool * wick_contractions, + const int wick_contractions, robj &result); public: static void WickContractions(std::string qi, std::string qf, - bool* wick_contractions); + int &wick_contractions); static void ContractBaryons(const PropagatorField &q1_left, const PropagatorField &q2_left, const PropagatorField &q3_left, @@ -75,7 +75,7 @@ public: const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, const int parity, ComplexField &baryon_corr); static void ContractBaryonsMatrix(const PropagatorField &q1_left, @@ -85,7 +85,7 @@ public: const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, SpinMatrixField &baryon_corr); template static void ContractBaryonsSliced(const mobj &D1, @@ -95,7 +95,7 @@ public: const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, const int parity, const int nt, robj &result); @@ -107,7 +107,7 @@ public: const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, const int nt, robj &result); private: @@ -234,7 +234,7 @@ void BaryonUtils::BaryonSite(const mobj &D1, const Gamma GammaA_f, const Gamma GammaB_f, const int parity, - const bool * wick_contraction, + const int wick_contraction, robj &result) { @@ -268,7 +268,7 @@ void BaryonUtils::BaryonSite(const mobj &D1, ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; //This is the \delta_{456}^{123} part - if (wick_contraction[0]){ + if (wick_contraction & 1){ for (int rho=0; rho::BaryonSite(const mobj &D1, } } //This is the \delta_{456}^{231} part - if (wick_contraction[1]){ + if (wick_contraction & 2){ for (int rho=0; rho::BaryonSite(const mobj &D1, }} } //This is the \delta_{456}^{312} part - if (wick_contraction[2]){ + if (wick_contraction & 4){ for (int rho=0; rho::BaryonSite(const mobj &D1, }} } //This is the \delta_{456}^{132} part - if (wick_contraction[3]){ + if (wick_contraction & 8){ for (int rho=0; rho::BaryonSite(const mobj &D1, } } //This is the \delta_{456}^{321} part - if (wick_contraction[4]){ + if (wick_contraction & 16){ for (int rho=0; rho::BaryonSite(const mobj &D1, }} } //This is the \delta_{456}^{213} part - if (wick_contraction[5]){ + if (wick_contraction & 32){ for (int rho=0; rho::BaryonSiteMatrix(const mobj &D1, const Gamma GammaB_i, const Gamma GammaA_f, const Gamma GammaB_f, - const bool * wick_contraction, + const int wick_contraction, robj &result) { @@ -383,7 +383,7 @@ void BaryonUtils::BaryonSiteMatrix(const mobj &D1, ee = Real(eSgn_f * eSgn_i); //epsilon_sgn[ie_n] * epsilon_sgn[ie_s]; //This is the \delta_{456}^{123} part - if (wick_contraction[0]){ + if (wick_contraction & 1){ for (int rho_i=0; rho_i::BaryonSiteMatrix(const mobj &D1, }} } //This is the \delta_{456}^{231} part - if (wick_contraction[1]){ + if (wick_contraction & 2){ for (int rho_i=0; rho_i::BaryonSiteMatrix(const mobj &D1, }} } //This is the \delta_{456}^{312} part - if (wick_contraction[2]){ + if (wick_contraction & 4){ for (int rho_i=0; rho_i::BaryonSiteMatrix(const mobj &D1, }} } //This is the \delta_{456}^{132} part - if (wick_contraction[3]){ + if (wick_contraction & 8){ for (int rho_i=0; rho_i::BaryonSiteMatrix(const mobj &D1, }} } //This is the \delta_{456}^{321} part - if (wick_contraction[4]){ + if (wick_contraction & 16){ for (int rho_i=0; rho_i::BaryonSiteMatrix(const mobj &D1, }} } //This is the \delta_{456}^{213} part - if (wick_contraction[5]){ + if (wick_contraction & 32){ for (int rho_i=0; rho_i::BaryonSiteMatrix(const mobj &D1, * flavours. * * The array wick_contractions must be of length 6 */ template -void BaryonUtils::WickContractions(std::string qi, std::string qf, bool* wick_contractions) { +void BaryonUtils::WickContractions(std::string qi, std::string qf, int &wick_contractions) { + assert(qi.size() == 3 && qf.size() == 3 && "Only sets of 3 quarks accepted."); const int epsilon[6][3] = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + wick_contractions=0; for (int ie=0; ie < 6 ; ie++) { - wick_contractions[ie] = (qi.size() == 3 && qf.size() == 3 - && qi[0] == qf[epsilon[ie][0]] + wick_contractions += ( ( qi[0] == qf[epsilon[ie][0]] && qi[1] == qf[epsilon[ie][1]] - && qi[2] == qf[epsilon[ie][2]]); + && qi[2] == qf[epsilon[ie][2]]) ? 1 : 0) << ie; } } @@ -500,7 +501,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, const int parity, ComplexField &baryon_corr) { @@ -522,9 +523,9 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, bytes += grid->oSites() * (432.*sizeof(vComplex) + 126.*sizeof(int) + 36.*sizeof(Real)); for (int ie=0; ie < 6 ; ie++){ if(ie==0 or ie==3){ - bytes += grid->oSites() * (4.*sizeof(int) + 4752.*sizeof(vComplex)) * wick_contractions[ie]; + //bytes += grid->oSites() * (4.*sizeof(int) + 4752.*sizeof(vComplex)) * wick_contractions[ie]; } else{ - bytes += grid->oSites() * (64.*sizeof(int) + 5184.*sizeof(vComplex)) * wick_contractions[ie]; + //bytes += grid->oSites() * (64.*sizeof(int) + 5184.*sizeof(vComplex)) * wick_contractions[ie]; } } Real t=0.; @@ -554,7 +555,7 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, SpinMatrixField &baryon_corr) { @@ -595,7 +596,7 @@ void BaryonUtils::ContractBaryonsSliced(const mobj &D1, const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, const int parity, const int nt, robj &result) @@ -620,7 +621,7 @@ void BaryonUtils::ContractBaryonsSlicedMatrix(const mobj &D1, const Gamma GammaB_left, const Gamma GammaA_right, const Gamma GammaB_right, - const bool* wick_contractions, + const int wick_contractions, const int nt, robj &result) { From 712bb406502922fb0ceb733e7a74f0ce2b902e2c Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 15 Dec 2020 16:33:29 +0000 Subject: [PATCH 086/399] merge develop --- tests/solver/Test_zMADWF_prec.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/solver/Test_zMADWF_prec.cc b/tests/solver/Test_zMADWF_prec.cc index d1168764..f18e1d86 100644 --- a/tests/solver/Test_zMADWF_prec.cc +++ b/tests/solver/Test_zMADWF_prec.cc @@ -52,7 +52,7 @@ struct TestParams{ bool zmobius_inner; double lambda_max; //upper bound of H_T eigenvalue range required to generate zMobius approximation - TestParams(): load_config(true), config_file("ckpoint_lat.1000"), mass(0.01), + TestParams(): load_config(false), config_file("ckpoint_lat.1000"), mass(0.01), Ls_outer(24), b_plus_c_outer(2.0), resid_outer(1e-8), Ls_inner(12), b_plus_c_inner(1.0), resid_inner(1e-8), zmobius_inner(true), lambda_max(1.42), outer_precon("Standard"), inner_precon("Standard") {} @@ -246,7 +246,7 @@ void run(const TestParams ¶ms){ typename RunParamsInner::SchurSolverType SchurSolver_inner(CG_inner); ZeroGuesser Guess; - MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 100, &update); + MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 10000, &update); LatticeFermionD result_MADWF(FGrid_outer); result_MADWF = Zero(); From 7905afa9f5b19b147b59eb809b87d1a27e4fc950 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 19 Jan 2021 12:32:48 +0000 Subject: [PATCH 087/399] revert changes --- tests/solver/Test_zMADWF_prec.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/solver/Test_zMADWF_prec.cc b/tests/solver/Test_zMADWF_prec.cc index f18e1d86..d1168764 100644 --- a/tests/solver/Test_zMADWF_prec.cc +++ b/tests/solver/Test_zMADWF_prec.cc @@ -52,7 +52,7 @@ struct TestParams{ bool zmobius_inner; double lambda_max; //upper bound of H_T eigenvalue range required to generate zMobius approximation - TestParams(): load_config(false), config_file("ckpoint_lat.1000"), mass(0.01), + TestParams(): load_config(true), config_file("ckpoint_lat.1000"), mass(0.01), Ls_outer(24), b_plus_c_outer(2.0), resid_outer(1e-8), Ls_inner(12), b_plus_c_inner(1.0), resid_inner(1e-8), zmobius_inner(true), lambda_max(1.42), outer_precon("Standard"), inner_precon("Standard") {} @@ -246,7 +246,7 @@ void run(const TestParams ¶ms){ typename RunParamsInner::SchurSolverType SchurSolver_inner(CG_inner); ZeroGuesser Guess; - MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 10000, &update); + MADWF > madwf(D_outer, D_inner, PV_outer, SchurSolver_inner, Guess, params.resid_outer, 100, &update); LatticeFermionD result_MADWF(FGrid_outer); result_MADWF = Zero(); From 96dd7a8fbd66d438618962ca930e7bd1eef34d08 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 16 Nov 2020 17:15:34 +0100 Subject: [PATCH 088/399] Flop cout matches DiRAC-ITT-2020 --- benchmarks/Benchmark_ITT.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index 032535b3..5d602ce9 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -445,7 +445,7 @@ public: // 1344= 3*(2*8+6)*2*8 + 8*3*2*2 + 3*4*2*8 // 1344 = Nc* (6+(Nc-1)*8)*2*Nd + Nd*Nc*2*2 + Nd*Nc*Ns*2 // double flops=(1344.0*volume)/2; -#if 1 +#if 0 double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + Nd*Nc*Ns + Nd*Nc*Ns*2; #else double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + 2*Nd*Nc*Ns + 2*Nd*Nc*Ns*2; From a673b6a54da17b319d8f6707893a9a3f4005be32 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 28 Jan 2021 14:15:09 +0000 Subject: [PATCH 089/399] prettify --- Grid/qcd/utils/BaryonUtils.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index d6b48ba0..56c5781d 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -513,11 +513,11 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, GridBase *grid = q1_left.Grid(); - autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); - autoView( vcorr_read , baryon_corr , AcceleratorRead); - autoView( v1 , q1_left , AcceleratorRead); - autoView( v2 , q2_left , AcceleratorRead); - autoView( v3 , q3_left , AcceleratorRead); + autoView( vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( vcorr_read , baryon_corr , AcceleratorRead); + autoView( v1 , q1_left , AcceleratorRead); + autoView( v2 , q2_left , AcceleratorRead); + autoView( v3 , q3_left , AcceleratorRead); Real bytes =0.; bytes += grid->oSites() * (432.*sizeof(vComplex) + 126.*sizeof(int) + 36.*sizeof(Real)); @@ -564,11 +564,11 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, GridBase *grid = q1_left.Grid(); - autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); - autoView( vcorr_read , baryon_corr , AcceleratorRead); - autoView( v1 , q1_left , AcceleratorRead); - autoView( v2 , q2_left , AcceleratorRead); - autoView( v3 , q3_left , AcceleratorRead); + autoView( vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( vcorr_read , baryon_corr , AcceleratorRead); + autoView( v1 , q1_left , AcceleratorRead); + autoView( v2 , q2_left , AcceleratorRead); + autoView( v3 , q3_left , AcceleratorRead); accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto D1 = v1(ss); @@ -941,10 +941,10 @@ void BaryonUtils::BaryonGamma3pt( GridBase *grid = q_tf.Grid(); - autoView( vcorr , stn_corr , AcceleratorWrite); + autoView( vcorr , stn_corr , AcceleratorWrite); autoView( vcorr_read , stn_corr , AcceleratorRead); - autoView( vq_ti , q_ti , AcceleratorRead); - autoView( vq_tf , q_tf , AcceleratorRead); + autoView( vq_ti , q_ti , AcceleratorRead); + autoView( vq_tf , q_tf , AcceleratorRead); Vector my_Dq_spec{Dq_spec1,Dq_spec2}; mobj * Dq_spec_p = &my_Dq_spec[0]; From bc496dd8440449a2e9d1bc5dce074192640cd094 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 28 Jan 2021 14:29:56 +0000 Subject: [PATCH 090/399] change back benchmark_ITT --- benchmarks/Benchmark_ITT.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index 5d602ce9..032535b3 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -445,7 +445,7 @@ public: // 1344= 3*(2*8+6)*2*8 + 8*3*2*2 + 3*4*2*8 // 1344 = Nc* (6+(Nc-1)*8)*2*Nd + Nd*Nc*2*2 + Nd*Nc*Ns*2 // double flops=(1344.0*volume)/2; -#if 0 +#if 1 double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + Nd*Nc*Ns + Nd*Nc*Ns*2; #else double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + 2*Nd*Nc*Ns + 2*Nd*Nc*Ns*2; From 019ffe17d4f1ba9d167cb45f62ea7a0df0c19adc Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Tue, 2 Feb 2021 11:32:23 +0100 Subject: [PATCH 091/399] Allow for GPU vector width beyond 64 --- Grid/util/Coordinate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/util/Coordinate.h b/Grid/util/Coordinate.h index 004fbc72..89f73264 100644 --- a/Grid/util/Coordinate.h +++ b/Grid/util/Coordinate.h @@ -88,7 +88,7 @@ public: // Coordinate class, maxdims = 8 for now. //////////////////////////////////////////////////////////////// #define GRID_MAX_LATTICE_DIMENSION (8) -#define GRID_MAX_SIMD (16) +#define GRID_MAX_SIMD (sizeof(vInteger)/sizeof(Integer)) static constexpr int MaxDims = GRID_MAX_LATTICE_DIMENSION; From 9b9a53f87066b5c67b607f56cb73c56efa5d4243 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 2 Feb 2021 13:06:43 +0000 Subject: [PATCH 092/399] ... --- Grid/qcd/utils/BaryonUtils.h | 53 ++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 8a4ff6ac..94cc07b1 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -1350,15 +1350,19 @@ void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, // \gamma_\mu^L * Dq_loop auto trGDq = TensorRemove(trace(Gamma_H * Dq_loop)); + Real ee; + for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a - int b_s = epsilon[ie_s][1]; //b - int c_s = epsilon[ie_s][2]; //c + int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' + int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' + int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_s = (ie_s < 3 ? 1 : -1); for (int ie_x=0; ie_x < 6 ; ie_x++){ - int a_x = epsilon[ie_x][0]; //a' - int b_x = epsilon[ie_x][1]; //b' - int c_x = epsilon[ie_x][2]; //c' - auto ee_GD = epsilon_sgn[ie_s] * epsilon_sgn[ie_x] * trGDq; + int a_x = (ie_x < 3 ? ie_x : (6-ie_x)%3 ); //epsilon[ie_x][0]; //a' + int b_x = (ie_x < 3 ? (ie_x+1)%3 : (8-ie_x)%3 ); //epsilon[ie_x][1]; //b' + int c_x = (ie_x < 3 ? (ie_x+2)%3 : (7-ie_x)%3 ); //epsilon[ie_x][2]; //c' + int eSgn_x = (ie_x < 3 ? 1 : -1); + ee = Real(eSgn_s * eSgn_x); for (int alpha_x=0; alpha_x::XiToSigmaEye(const PropagatorField &qq_loop, const Gamma GammaB_xi, const Gamma GammaB_sigma, const std::string op, - SpinMatrixField &stn_corr) + SpinMatrixField &xts_corr) { assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); @@ -1459,24 +1463,31 @@ void BaryonUtils::XiToSigmaEye(const PropagatorField &qq_loop, GridBase *grid = qs_ti.Grid(); - autoView( vcorr, stn_corr, CpuWrite); - autoView( vq_loop , qq_loop, CpuRead); - autoView( vd_tf , qd_tf, CpuRead); - autoView( vs_ti , qs_ti, CpuRead); + autoView( vcorr , xts_corr , AcceleratorWrite); + autoView( vq_loop , qq_loop , AcceleratorRead); + autoView( vd_tf , qd_tf , AcceleratorRead); + autoView( vs_ti , qs_ti , AcceleratorRead); + + bool doQ1 = (op == "Q1"); + bool doQ2 = (op == "Q2"); + + Vector my_Dq_spec{Dd_spec,Ds_spec}; + mobj * Dq_spec_p = &my_Dq_spec[0]; accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_loop = vq_loop[ss]; - auto Dd_tf = vd_tf[ss]; - auto Ds_ti = vs_ti[ss]; - sobj result=Zero(); - if(op == "Q1"){ - XiToSigmaQ1EyeSite(Dq_loop,Dd_spec,Ds_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); - } else if(op == "Q2"){ - XiToSigmaQ2EyeSite(Dq_loop,Dd_spec,Ds_spec,Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); + auto Dq_loop = vq_loop(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + if(doQ1){ + XiToSigmaQ1EyeSite(Dq_loop,Dq_spec_p[0],Dq_spec_p[1],Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); + } else if(doQ2){ + XiToSigmaQ2EyeSite(Dq_loop,Dq_spec_p[0],Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); } else { assert(0 && "Weak Operator not correctly specified"); } - vcorr[ss] = result; + coalescedWrite(vcorr[ss],result); } );//end loop over lattice sites } From 3215d88a91214bc6bbed75cf21cf1a4a28b0bbfb Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 3 Feb 2021 15:17:03 +0000 Subject: [PATCH 093/399] Simplify syntax with Grid::EnableIf post code review. Updated EnableIf so that ReturnType defaults to void in same way as std::enable_if see https://en.cppreference.com/w/cpp/types/enable_if --- Grid/lattice/Lattice_transfer.h | 9 +++------ Grid/simd/Grid_vector_types.h | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index c91fa4d1..5a26cce9 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -98,8 +98,7 @@ accelerator_inline void convertType(ComplexF & out, const std::complex & } template -accelerator_inline typename std::enable_if::value>::type -convertType(T & out, const T & in) { +accelerator_inline EnableIf> convertType(T & out, const T & in) { out = in; } @@ -138,14 +137,12 @@ accelerator_inline void convertType(iScalar & out, const iScalar & in) { } template -accelerator_inline typename std::enable_if::value>::type -convertType(T1 & out, const iScalar & in) { +accelerator_inline NotEnableIf> convertType(T1 & out, const iScalar & in) { convertType(out,in._internal); } template -accelerator_inline typename std::enable_if::value>::type -convertType(iScalar & out, const T2 & in) { +accelerator_inline NotEnableIf> convertType(iScalar & out, const T2 & in) { convertType(out._internal,in); } diff --git a/Grid/simd/Grid_vector_types.h b/Grid/simd/Grid_vector_types.h index c07077a3..4f952bb2 100644 --- a/Grid/simd/Grid_vector_types.h +++ b/Grid/simd/Grid_vector_types.h @@ -208,8 +208,8 @@ struct RealPart > { ////////////////////////////////////// // type alias used to simplify the syntax of std::enable_if template using Invoke = typename T::type; -template using EnableIf = Invoke >; -template using NotEnableIf = Invoke >; +template using EnableIf = Invoke >; +template using NotEnableIf = Invoke >; //////////////////////////////////////////////////////// // Check for complexity with type traits From 4705aa541d62e16b070452e4a3f329d9f9565afa Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Thu, 4 Feb 2021 14:25:55 +0100 Subject: [PATCH 094/399] Allow user to configure ShmDims via environment variables --- Grid/communicator/SharedMemoryMPI.cc | 18 ++++++++++++++++++ Grid/util/Init.cc | 2 +- Grid/util/Init.h | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index a12418e6..466f6a1e 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -7,6 +7,7 @@ Copyright (C) 2015 Author: Peter Boyle +Author: Christoph Lehner This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -169,6 +170,23 @@ static inline int divides(int a,int b) } void GlobalSharedMemory::GetShmDims(const Coordinate &WorldDims,Coordinate &ShmDims) { + //////////////////////////////////////////////////////////////// + // Allow user to configure through environment variable + //////////////////////////////////////////////////////////////// + char* str = getenv(("GRID_SHM_DIMS_" + std::to_string(ShmDims.size())).c_str()); + if ( str ) { + std::vector IntShmDims; + GridCmdOptionIntVector(std::string(str),IntShmDims); + assert(IntShmDims.size() == WorldDims.size()); + long ShmSize = 1; + for (int dim=0;dim & vec) } template -void GridCmdOptionIntVector(std::string &str,VectorInt & vec) +void GridCmdOptionIntVector(const std::string &str,VectorInt & vec) { vec.resize(0); std::stringstream ss(str); diff --git a/Grid/util/Init.h b/Grid/util/Init.h index dad963a0..4eb8f06c 100644 --- a/Grid/util/Init.h +++ b/Grid/util/Init.h @@ -55,7 +55,7 @@ template std::string GridCmdVectorIntToString(const VectorInt & vec); void GridCmdOptionCSL(std::string str,std::vector & vec); template -void GridCmdOptionIntVector(std::string &str,VectorInt & vec); +void GridCmdOptionIntVector(const std::string &str,VectorInt & vec); void GridCmdOptionInt(std::string &str,int & val); From cd99edcc5f0e3b12106c07652f073eeb5be985c7 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 4 Feb 2021 18:25:49 -0500 Subject: [PATCH 095/399] maxLocalNorm2() --- Grid/communicator/Communicator_base.h | 3 +- Grid/communicator/Communicator_mpi3.cc | 10 +++++ Grid/communicator/Communicator_none.cc | 2 + Grid/lattice/Lattice_reduction.h | 53 +++++++++++++++++++++++++- tests/core/Test_main.cc | 16 +++++++- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/Grid/communicator/Communicator_base.h b/Grid/communicator/Communicator_base.h index bb06d43f..a15f9789 100644 --- a/Grid/communicator/Communicator_base.h +++ b/Grid/communicator/Communicator_base.h @@ -1,4 +1,3 @@ - /************************************************************************************* Grid physics library, www.github.com/paboyle/Grid @@ -108,6 +107,8 @@ public: //////////////////////////////////////////////////////////// // Reduction //////////////////////////////////////////////////////////// + void GlobalMax(RealD &); + void GlobalMax(RealF &); void GlobalSum(RealF &); void GlobalSumVector(RealF *,int N); void GlobalSum(RealD &); diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index c6543851..5713fe35 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -275,6 +275,16 @@ void CartesianCommunicator::GlobalXOR(uint64_t &u){ int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT64_T,MPI_BXOR,communicator); assert(ierr==0); } +void CartesianCommunicator::GlobalMax(float &f) +{ + int ierr=MPI_Allreduce(MPI_IN_PLACE,&f,1,MPI_FLOAT,MPI_MAX,communicator); + assert(ierr==0); +} +void CartesianCommunicator::GlobalMax(double &d) +{ + int ierr = MPI_Allreduce(MPI_IN_PLACE,&d,1,MPI_DOUBLE,MPI_MAX,communicator); + assert(ierr==0); +} void CartesianCommunicator::GlobalSum(float &f){ int ierr=MPI_Allreduce(MPI_IN_PLACE,&f,1,MPI_FLOAT,MPI_SUM,communicator); assert(ierr==0); diff --git a/Grid/communicator/Communicator_none.cc b/Grid/communicator/Communicator_none.cc index 6cb431a2..beb2cc97 100644 --- a/Grid/communicator/Communicator_none.cc +++ b/Grid/communicator/Communicator_none.cc @@ -67,6 +67,8 @@ CartesianCommunicator::CartesianCommunicator(const Coordinate &processors) CartesianCommunicator::~CartesianCommunicator(){} +void CartesianCommunicator::GlobalMax(float &){} +void CartesianCommunicator::GlobalMax(double &){} void CartesianCommunicator::GlobalSum(float &){} void CartesianCommunicator::GlobalSumVector(float *,int N){} void CartesianCommunicator::GlobalSum(double &){} diff --git a/Grid/lattice/Lattice_reduction.h b/Grid/lattice/Lattice_reduction.h index c2955485..7338fd41 100644 --- a/Grid/lattice/Lattice_reduction.h +++ b/Grid/lattice/Lattice_reduction.h @@ -96,8 +96,34 @@ inline typename vobj::scalar_objectD sumD_cpu(const vobj *arg, Integer osites) ssobj ret = ssum; return ret; } +/* +Threaded max, don't use for now +template +inline Double max(const Double *arg, Integer osites) +{ + // const int Nsimd = vobj::Nsimd(); + const int nthread = GridThread::GetThreads(); - + std::vector maxarray(nthread); + + thread_for(thr,nthread, { + int nwork, mywork, myoff; + nwork = osites; + GridThread::GetWork(nwork,thr,mywork,myoff); + Double max=arg[0]; + for(int ss=myoff;ss max ) max = arg[ss]; + } + maxarray[thr]=max; + }); + + Double tmax=maxarray[0]; + for(int i=0;itmax) tmax = maxarray[i]; + } + return tmax; +} +*/ template inline typename vobj::scalar_object sum(const vobj *arg, Integer osites) { @@ -140,6 +166,31 @@ template inline RealD norm2(const Lattice &arg){ ComplexD nrm = innerProduct(arg,arg); return real(nrm); } +template inline RealD maxLocalNorm2(const Lattice &arg) +{ + typedef typename vobj::tensor_reduced vscalar; + typedef typename vobj::scalar_object scalar; + typedef typename getPrecision::real_scalar_type rscalar; + + Lattice inner = localNorm2(arg); + + auto grid = arg.Grid(); + + RealD max; + for(int l=0;llSites();l++){ + Coordinate coor; + scalar val; + RealD r; + grid->LocalIndexToLocalCoor(l,coor); + peekLocalSite(val,inner,coor); + r=real(TensorRemove(val)); + if( (l==0) || (r>max)){ + max=r; + } + } + grid->GlobalMax(max); + return max; +} // Double inner product template diff --git a/tests/core/Test_main.cc b/tests/core/Test_main.cc index d7ed04ba..d3e6bfbd 100644 --- a/tests/core/Test_main.cc +++ b/tests/core/Test_main.cc @@ -231,6 +231,19 @@ int main(int argc, char **argv) { scalar = localInnerProduct(cVec, cVec); scalar = localNorm2(cVec); + std::cout << "Testing maxLocalNorm2" < shiftcoor = coor; shiftcoor[dir] = (shiftcoor[dir] + shift + latt_size[dir]) % - (latt_size[dir] / mpi_layout[dir]); + (latt_size[dir]); + // (latt_size[dir] / mpi_layout[dir]); std::vector rl(4); for (int dd = 0; dd < 4; dd++) { From eda9ab487babbf2409fef80fd69f3b60ed532480 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 8 Feb 2021 10:47:22 -0500 Subject: [PATCH 096/399] MADWF 5d source option for hadrons - look at Grid of source Abort on GPU error --- Grid/qcd/action/fermion/MADWF.h | 14 +++++++++++--- Grid/threads/Accelerator.cc | 1 + Grid/threads/Accelerator.h | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/action/fermion/MADWF.h b/Grid/qcd/action/fermion/MADWF.h index 6b3c6e71..5d17e865 100644 --- a/Grid/qcd/action/fermion/MADWF.h +++ b/Grid/qcd/action/fermion/MADWF.h @@ -85,7 +85,7 @@ class MADWF maxiter =_maxiter; }; - void operator() (const FermionFieldo &src4,FermionFieldo &sol5) + void operator() (const FermionFieldo &src,FermionFieldo &sol5) { std::cout << GridLogMessage<< " ************************************************" << std::endl; std::cout << GridLogMessage<< " MADWF-like algorithm " << std::endl; @@ -114,8 +114,16 @@ class MADWF /////////////////////////////////////// //Import source, include Dminus factors /////////////////////////////////////// - Mato.ImportPhysicalFermionSource(src4,b); - std::cout << GridLogMessage << " src4 " < NAMESPACE_BEGIN(Grid); +int acceleratorAbortOnGpuError=1; uint32_t accelerator_threads=2; uint32_t acceleratorThreads(void) {return accelerator_threads;}; void acceleratorThreads(uint32_t t) {accelerator_threads = t;}; diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 6232aea8..59645546 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -100,6 +100,8 @@ void acceleratorInit(void); #define accelerator __host__ __device__ #define accelerator_inline __host__ __device__ inline +extern int acceleratorAbortOnGpuError; + accelerator_inline int acceleratorSIMTlane(int Nsimd) { #ifdef GRID_SIMT return threadIdx.z; @@ -140,6 +142,7 @@ void LambdaApply(uint64_t num1, uint64_t num2, uint64_t num3, lambda Lambda) printf("Cuda error %s \n", cudaGetErrorString( err )); \ puts(__FILE__); \ printf("Line %d\n",__LINE__); \ + if (acceleratorAbortOnGpuError) assert(err==cudaSuccess); \ } \ } From 55de69a56953f12380e3a276262ef0f547b5e28c Mon Sep 17 00:00:00 2001 From: Christopher Kelly Date: Mon, 8 Feb 2021 12:03:16 -0500 Subject: [PATCH 097/399] Fixed compile issues with maxLocalNorm2 for non-scalar lattices maxLocalNorm2 test now reuses the random field --- Grid/lattice/Lattice_reduction.h | 7 ++++--- tests/core/Test_main.cc | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Grid/lattice/Lattice_reduction.h b/Grid/lattice/Lattice_reduction.h index 7338fd41..0a5fbcb6 100644 --- a/Grid/lattice/Lattice_reduction.h +++ b/Grid/lattice/Lattice_reduction.h @@ -166,11 +166,12 @@ template inline RealD norm2(const Lattice &arg){ ComplexD nrm = innerProduct(arg,arg); return real(nrm); } + +//The global maximum of the site norm2 template inline RealD maxLocalNorm2(const Lattice &arg) { - typedef typename vobj::tensor_reduced vscalar; - typedef typename vobj::scalar_object scalar; - typedef typename getPrecision::real_scalar_type rscalar; + typedef typename vobj::tensor_reduced vscalar; //iScalar > > + typedef typename vscalar::scalar_object scalar; //iScalar > > Lattice inner = localNorm2(arg); diff --git a/tests/core/Test_main.cc b/tests/core/Test_main.cc index d3e6bfbd..6e316aa6 100644 --- a/tests/core/Test_main.cc +++ b/tests/core/Test_main.cc @@ -232,12 +232,13 @@ int main(int argc, char **argv) { scalar = localNorm2(cVec); std::cout << "Testing maxLocalNorm2" < Date: Sun, 14 Feb 2021 21:27:54 +0000 Subject: [PATCH 098/399] Seems the intention with AutoConf produced Grid/Config.h was to use sed to translate standard PACKAGE_ #defines into GRID_ however due to missing '' after -i this hasn't been working. Perhaps it is too late to fix this, since we don't know who/what is relying on this downstream? ... but if they are, and AutoConf is being used, then likely these #defines have just been redefined anyway. Seems reasonable to redefine PACKAGE and VERSION as well, as none of these macros are used throughout Grid or Hadrons. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f077ca93..7d95e4e2 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ AM_INIT_AUTOMAKE([subdir-objects 1.13]) AM_EXTRA_RECURSIVE_TARGETS([tests bench]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([Grid/Grid.h]) -AC_CONFIG_HEADERS([Grid/Config.h],[sed -i 's|PACKAGE_|GRID_|' Grid/Config.h]) +AC_CONFIG_HEADERS([Grid/Config.h],[[sed -i '' -e 's|PACKAGE_|GRID_|' -e 's|[[:space:]]PACKAGE[[:space:]]| GRID_PACKAGE |' -e 's|[[:space:]]VERSION[[:space:]]| GRID_PACKAGE_VERSION |' Grid/Config.h]]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) ################ Get git info From 35114c9e629c53546c8e95edd6def7b3e1692c7a Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 17 Feb 2021 13:24:15 +0000 Subject: [PATCH 099/399] Mac OS (Darwin) sed -i flag for in-place editing differs from posix / gnu --- configure.ac | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7d95e4e2..702ce826 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,12 @@ AM_INIT_AUTOMAKE([subdir-objects 1.13]) AM_EXTRA_RECURSIVE_TARGETS([tests bench]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([Grid/Grid.h]) -AC_CONFIG_HEADERS([Grid/Config.h],[[sed -i '' -e 's|PACKAGE_|GRID_|' -e 's|[[:space:]]PACKAGE[[:space:]]| GRID_PACKAGE |' -e 's|[[:space:]]VERSION[[:space:]]| GRID_PACKAGE_VERSION |' Grid/Config.h]]) +AC_CONFIG_HEADERS([Grid/Config.h],[[$SED_INPLACE -e 's|PACKAGE_|GRID_|' -e 's|[[:space:]]PACKAGE[[:space:]]| GRID_PACKAGE |' -e 's|[[:space:]]VERSION[[:space:]]| GRID_PACKAGE_VERSION |' Grid/Config.h]], + [if test x"$host_os" == x"${host_os#darwin}" ; then] + [SED_INPLACE="sed -i"] + [else] + [SED_INPLACE="sed -i .bak"] + [fi]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) ################ Get git info From 86b58d5aff2adac0b41b4fc7be22e77dfbd583e2 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 18 Feb 2021 12:04:32 +0000 Subject: [PATCH 100/399] changed if and accelerator_for - no runtime errors any more --- Grid/qcd/utils/BaryonUtils.h | 129 ++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 61 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 56c5781d..4ac5f685 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -513,19 +513,18 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, GridBase *grid = q1_left.Grid(); - autoView( vbaryon_corr , baryon_corr , AcceleratorWrite); - autoView( vcorr_read , baryon_corr , AcceleratorRead); - autoView( v1 , q1_left , AcceleratorRead); - autoView( v2 , q2_left , AcceleratorRead); - autoView( v3 , q3_left , AcceleratorRead); + autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( v1 , q1_left , AcceleratorRead); + autoView( v2 , q2_left , AcceleratorRead); + autoView( v3 , q3_left , AcceleratorRead); Real bytes =0.; bytes += grid->oSites() * (432.*sizeof(vComplex) + 126.*sizeof(int) + 36.*sizeof(Real)); for (int ie=0; ie < 6 ; ie++){ if(ie==0 or ie==3){ - //bytes += grid->oSites() * (4.*sizeof(int) + 4752.*sizeof(vComplex)) * wick_contractions[ie]; + bytes += ( wick_contractions & (1 << ie) ) ? grid->oSites() * (4.*sizeof(int) + 4752.*sizeof(vComplex)) : 0.; } else{ - //bytes += grid->oSites() * (64.*sizeof(int) + 5184.*sizeof(vComplex)) * wick_contractions[ie]; + bytes += ( wick_contractions & (1 << ie) ) ? grid->oSites() * (64.*sizeof(int) + 5184.*sizeof(vComplex)) : 0.; } } Real t=0.; @@ -535,8 +534,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1_left, auto D1 = v1(ss); auto D2 = v2(ss); auto D3 = v3(ss); - //typedef decltype(coalescedRead(vbaryon_corr[0])) cVec; - typedef decltype(coalescedRead(vcorr_read[0])) cVec; + typedef decltype(coalescedRead(vbaryon_corr[0])) cVec; cVec result=Zero(); BaryonSite(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,parity,wick_contractions,result); coalescedWrite(vbaryon_corr[ss],result); @@ -561,21 +559,19 @@ void BaryonUtils::ContractBaryonsMatrix(const PropagatorField &q1_left, assert(Ns==4 && "Baryon code only implemented for N_spin = 4"); assert(Nc==3 && "Baryon code only implemented for N_colour = 3"); - + GridBase *grid = q1_left.Grid(); - autoView( vbaryon_corr , baryon_corr , AcceleratorWrite); - autoView( vcorr_read , baryon_corr , AcceleratorRead); - autoView( v1 , q1_left , AcceleratorRead); - autoView( v2 , q2_left , AcceleratorRead); - autoView( v3 , q3_left , AcceleratorRead); + autoView(vbaryon_corr , baryon_corr , AcceleratorWrite); + autoView( v1 , q1_left , AcceleratorRead); + autoView( v2 , q2_left , AcceleratorRead); + autoView( v3 , q3_left , AcceleratorRead); accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto D1 = v1(ss); auto D2 = v2(ss); auto D3 = v3(ss); - //typedef decltype(coalescedRead(vbaryon_corr[0])) spinor; - typedef decltype(coalescedRead(vcorr_read[0])) spinor; + typedef decltype(coalescedRead(vbaryon_corr[0])) spinor; spinor result=Zero(); BaryonSiteMatrix(D1,D2,D3,GammaA_left,GammaB_left,GammaA_right,GammaB_right,wick_contractions,result); coalescedWrite(vbaryon_corr[ss],result); @@ -941,10 +937,9 @@ void BaryonUtils::BaryonGamma3pt( GridBase *grid = q_tf.Grid(); - autoView( vcorr , stn_corr , AcceleratorWrite); - autoView( vcorr_read , stn_corr , AcceleratorRead); - autoView( vq_ti , q_ti , AcceleratorRead); - autoView( vq_tf , q_tf , AcceleratorRead); + autoView( vcorr , stn_corr , AcceleratorWrite); + autoView( vq_ti , q_ti , AcceleratorRead); + autoView( vq_tf , q_tf , AcceleratorRead); Vector my_Dq_spec{Dq_spec1,Dq_spec2}; mobj * Dq_spec_p = &my_Dq_spec[0]; @@ -953,29 +948,28 @@ void BaryonUtils::BaryonGamma3pt( accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_ti = vq_ti(ss); auto Dq_tf = vq_tf(ss); - typedef decltype(coalescedRead(vcorr_read[0])) spinor; + typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup1Site(Dq_ti,Dq_spec_p[0],Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],coalescedRead(vcorr_read[ss])+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); });//end loop over lattice sites - } else if (group == 2) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_ti = vq_ti(ss); auto Dq_tf = vq_tf(ss); - typedef decltype(coalescedRead(vcorr_read[0])) spinor; + typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup2Site(Dq_spec_p[0],Dq_ti,Dq_spec_p[1],Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],coalescedRead(vcorr_read[ss])+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); });//end loop over lattice sites } else if (group == 3) { accelerator_for(ss, grid->oSites(), grid->Nsimd(), { auto Dq_ti = vq_ti(ss); auto Dq_tf = vq_tf(ss); - typedef decltype(coalescedRead(vcorr_read[0])) spinor; + typedef decltype(coalescedRead(vcorr[0])) spinor; spinor result=Zero(); BaryonGamma3ptGroup3Site(Dq_spec_p[0],Dq_spec_p[1],Dq_ti,Dq_tf,GammaJ,GammaBi,GammaBf,wick_contraction,result); - coalescedWrite(vcorr[ss],coalescedRead(vcorr_read[ss])+result); + coalescedWrite(vcorr[ss],coalescedRead(vcorr[ss])+result); });//end loop over lattice sites } @@ -1206,6 +1200,7 @@ void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, Real ee; + for (int ie_n=0; ie_n < 6 ; ie_n++){ int a_n = (ie_n < 3 ? ie_n : (6-ie_n)%3 ); //epsilon[ie_n][0]; //a int b_n = (ie_n < 3 ? (ie_n+1)%3 : (8-ie_n)%3 ); //epsilon[ie_n][1]; //b @@ -1250,6 +1245,7 @@ void BaryonUtils::SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, }} } } + } template @@ -1275,27 +1271,32 @@ void BaryonUtils::SigmaToNucleonEye(const PropagatorField &qq_loop, autoView( vd_tf , qd_tf , AcceleratorRead); autoView( vs_ti , qs_ti , AcceleratorRead); - bool doQ1 = (op == "Q1"); - bool doQ2 = (op == "Q2"); - Vector my_Dq_spec{Du_spec}; mobj * Dq_spec_p = &my_Dq_spec[0]; - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_loop = vq_loop(ss); - auto Dd_tf = vd_tf(ss); - auto Ds_ti = vs_ti(ss); - typedef decltype(coalescedRead(vcorr[0])) spinor; - spinor result=Zero(); - if(doQ1){ + if(op == "Q1"){ + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_loop = vq_loop(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); SigmaToNucleonQ1EyeSite(Dq_loop,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); - } else if(doQ2){ + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites + } else if(op == "Q2"){ + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_loop = vq_loop(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); SigmaToNucleonQ2EyeSite(Dq_loop,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); - } else { - assert(0 && "Weak Operator not correctly specified"); - } - coalescedWrite(vcorr[ss],result); - });//end loop over lattice sites + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites + } else { + assert(0 && "Weak Operator not correctly specified"); + } } template @@ -1322,29 +1323,35 @@ void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, autoView( vq_tf , qq_tf , AcceleratorRead ); autoView( vd_tf , qd_tf , AcceleratorRead ); autoView( vs_ti , qs_ti , AcceleratorRead ); - - bool doQ1 = (op == "Q1"); - bool doQ2 = (op == "Q2"); Vector my_Dq_spec{Du_spec}; mobj * Dq_spec_p = &my_Dq_spec[0]; - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_ti = vq_ti(ss); - auto Dq_tf = vq_tf(ss); - auto Dd_tf = vd_tf(ss); - auto Ds_ti = vs_ti(ss); - typedef decltype(coalescedRead(vcorr[0])) spinor; - spinor result=Zero(); - if(doQ1){ + if(op == "Q1"){ + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); SigmaToNucleonQ1NonEyeSite(Dq_ti,Dq_tf,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); - } else if(doQ2){ + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites + } else if(op == "Q2"){ + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_ti = vq_ti(ss); + auto Dq_tf = vq_tf(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); SigmaToNucleonQ2NonEyeSite(Dq_ti,Dq_tf,Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_sigma,GammaB_nucl,result); - } else { - assert(0 && "Weak Operator not correctly specified"); - } - coalescedWrite(vcorr[ss],result); - });//end loop over lattice sites + coalescedWrite(vcorr[ss],result); + });//end loop over lattice sites + } else { + assert(0 && "Weak Operator not correctly specified"); + } } NAMESPACE_END(Grid); From 7ae030f5851e8a9d74bedb79a28ef598b4006c26 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 18 Feb 2021 13:24:50 +0000 Subject: [PATCH 101/399] changed back A2AUtils warning --- Grid/qcd/utils/A2Autils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 497927dd..b63d8571 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -1047,7 +1047,7 @@ A2Autils::ContractWWVV(std::vector &WWVV, { GridBase *grid = vs[0].Grid(); - //int nd = grid->_ndimension; + int nd = grid->_ndimension; int Nsimd = grid->Nsimd(); int N_t = WW_sd.dimensions()[0]; int N_s = WW_sd.dimensions()[1]; From e3d019bc2f46c8c338bc3306a1954180a8b94dd7 Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Mon, 22 Feb 2021 14:56:52 +0100 Subject: [PATCH 102/399] Enable performance counting in WilsonFermion like in others --- .../fermion/implementation/WilsonFermionImplementation.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 4977ea68..84ac25c1 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -397,6 +397,7 @@ void WilsonFermion::DhopDerivEO(GaugeField &mat, const FermionField &U, co template void WilsonFermion::Dhop(const FermionField &in, FermionField &out, int dag) { + DhopCalls+=2; conformable(in.Grid(), _grid); // verifies full grid conformable(in.Grid(), out.Grid()); @@ -408,6 +409,7 @@ void WilsonFermion::Dhop(const FermionField &in, FermionField &out, int da template void WilsonFermion::DhopOE(const FermionField &in, FermionField &out, int dag) { + DhopCalls++; conformable(in.Grid(), _cbgrid); // verifies half grid conformable(in.Grid(), out.Grid()); // drops the cb check @@ -420,6 +422,7 @@ void WilsonFermion::DhopOE(const FermionField &in, FermionField &out, int template void WilsonFermion::DhopEO(const FermionField &in, FermionField &out,int dag) { + DhopCalls++; conformable(in.Grid(), _cbgrid); // verifies half grid conformable(in.Grid(), out.Grid()); // drops the cb check From c073e62e0be2f2e421588ad76f90dce33e7154c0 Mon Sep 17 00:00:00 2001 From: Daniel Richtmann Date: Mon, 22 Feb 2021 15:17:07 +0100 Subject: [PATCH 103/399] Correct misleading ac help string --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 702ce826..fb0c78fc 100644 --- a/configure.ac +++ b/configure.ac @@ -130,7 +130,7 @@ esac ############### fermions AC_ARG_ENABLE([fermion-reps], - [AC_HELP_STRING([--fermion-reps=yes|no], [enable extra fermion representation support])], + [AC_HELP_STRING([--enable-fermion-reps=yes|no], [enable extra fermion representation support])], [ac_FERMION_REPS=${enable_fermion_reps}], [ac_FERMION_REPS=yes]) AM_CONDITIONAL(BUILD_FERMION_REPS, [ test "${ac_FERMION_REPS}X" == "yesX" ]) From d5ab571a894682e36bc24488958f01a18983c7f7 Mon Sep 17 00:00:00 2001 From: Christopher Kelly Date: Tue, 23 Feb 2021 11:49:56 -0500 Subject: [PATCH 104/399] Added the ability to apply a custom "filter" to the conjugate momentum in the Integrator classes, applied both after refresh and after applying the forces Added a conjugate momentum "filter" that applies a phase to each site. With sites set to 1.0 or 0.0 this acts as a mask and enables, for example, the freezing of inactive gauge links in DDHMC Added tests/forces/Test_momentum_filter demonstrating the use of the filter to freeze boundary links --- Grid/qcd/hmc/integrators/Integrator.h | 28 ++++ Grid/qcd/hmc/integrators/MomentumFilter.h | 94 +++++++++++++ tests/forces/Test_momentum_filter.cc | 154 ++++++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 Grid/qcd/hmc/integrators/MomentumFilter.h create mode 100644 tests/forces/Test_momentum_filter.cc diff --git a/Grid/qcd/hmc/integrators/Integrator.h b/Grid/qcd/hmc/integrators/Integrator.h index 70055754..77b7de52 100644 --- a/Grid/qcd/hmc/integrators/Integrator.h +++ b/Grid/qcd/hmc/integrators/Integrator.h @@ -33,6 +33,7 @@ directory #define INTEGRATOR_INCLUDED #include +#include "MomentumFilter.h" NAMESPACE_BEGIN(Grid); @@ -78,8 +79,19 @@ protected: RepresentationPolicy Representations; IntegratorParameters Params; + //Filters allow the user to manipulate the conjugate momentum, for example to freeze links in DDHMC + //It is applied whenever the momentum is updated / refreshed + //The default filter does nothing + MomentumFilterBase const* MomFilter; + const ActionSet as; + //Get a pointer to a shared static instance of the "do-nothing" momentum filter to serve as a default + static MomentumFilterBase const* getDefaultMomFilter(){ + static MomentumFilterNone filter; + return &filter; + } + void update_P(Field& U, int level, double ep) { t_P[level] += ep; @@ -135,6 +147,8 @@ protected: // Force from the other representations as[level].apply(update_P_hireps, Representations, Mom, U, ep); + + MomFilter->applyFilter(Mom); } void update_U(Field& U, double ep) @@ -174,11 +188,23 @@ public: t_P.resize(levels, 0.0); t_U = 0.0; // initialization of smearer delegated outside of Integrator + + //Default the momentum filter to "do-nothing" + MomFilter = getDefaultMomFilter(); }; virtual ~Integrator() {} virtual std::string integrator_name() = 0; + + //Set the momentum filter allowing for manipulation of the conjugate momentum + void setMomentumFilter(const MomentumFilterBase &filter){ + MomFilter = &filter; + } + + //Access the conjugate momentum + const MomentaField & getMomentum() const{ return P; } + void print_parameters() { @@ -249,6 +275,8 @@ public: // Refresh the higher representation actions as[level].apply(refresh_hireps, Representations, pRNG); } + + MomFilter->applyFilter(P); } // to be used by the actionlevel class to iterate diff --git a/Grid/qcd/hmc/integrators/MomentumFilter.h b/Grid/qcd/hmc/integrators/MomentumFilter.h new file mode 100644 index 00000000..2a15d80c --- /dev/null +++ b/Grid/qcd/hmc/integrators/MomentumFilter.h @@ -0,0 +1,94 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: ./lib/qcd/hmc/integrators/MomentumFilter.h + +Copyright (C) 2015 + +Author: Christopher Kelly +Author: Peter Boyle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +See the full license in the file "LICENSE" in the top level distribution +directory +*************************************************************************************/ +/* END LEGAL */ +//-------------------------------------------------------------------- +#ifndef MOMENTUM_FILTER +#define MOMENTUM_FILTER + +NAMESPACE_BEGIN(Grid); + +//These filter objects allow the user to manipulate the conjugate momentum as part of the update / refresh + +template +struct MomentumFilterBase{ + virtual void applyFilter(MomentaField &P) const; +}; + +//Do nothing +template +struct MomentumFilterNone: public MomentumFilterBase{ + void applyFilter(MomentaField &P) const override{} +}; + +//Multiply each site/direction by a Lorentz vector complex number field +//Can be used to implement a mask, zeroing out sites +template +struct MomentumFilterApplyPhase: public MomentumFilterBase{ + typedef typename MomentaField::vector_type vector_type; //SIMD-vectorized complex type + typedef typename MomentaField::scalar_type scalar_type; //scalar complex type + typedef iVector >, Nd > LorentzScalarType; //complex phase for each site/direction + typedef Lattice LatticeLorentzScalarType; + + LatticeLorentzScalarType phase; + + MomentumFilterApplyPhase(const LatticeLorentzScalarType _phase): phase(_phase){} + + //Default to uniform field of (1,0) + MomentumFilterApplyPhase(GridBase* _grid): phase(_grid){ + LorentzScalarType one; + for(int mu=0;mu + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include + +using namespace std; +using namespace Grid; + +//Get the mu-directected links on the upper boundary and the bulk remainder +template +void getLinksBoundaryBulk(Field &bound, Field &bulk, Field &from, const Coordinate &latt_size){ + bound = Zero(); bulk = Zero(); + for(int mu=0;mu seeds({1,2,3,4}); + + GridParallelRNG pRNG(&Grid); + pRNG.SeedFixedIntegers(seeds); + + typedef PeriodicGimplR Gimpl; + typedef WilsonGaugeAction GaugeAction; + typedef NoHirep Representation; //fundamental + typedef NoSmearing Smearing; + typedef MinimumNorm2 Omelyan; + typedef Gimpl::Field Field; + typedef MomentumFilterApplyPhase Filter; + Filter filter(&Grid); + + //Setup a filter that disables link update on links passing through the global lattice boundary + typedef Filter::LatticeLorentzScalarType MaskType; + typedef Filter::LorentzScalarType MaskSiteType; + + MaskSiteType zero, one; + for(int mu=0;mu::HotConfiguration(pRNG,U); + + //Get the original links on the bulk and boundary for later use + Field Ubnd_orig(&Grid), Ubulk_orig(&Grid); + getLinksBoundaryBulk(Ubnd_orig, Ubulk_orig, U, latt_size); + + ActionSet actions(1); + double beta=6; + GaugeAction gauge_action(beta); + actions[0].push_back(&gauge_action); + + Smearing smear; + IntegratorParameters params(1,1.); //1 MD step + Omelyan integrator(&Grid, params, actions, smear); + + integrator.setMomentumFilter(filter); + + integrator.refresh(U, pRNG); //doesn't actually change the gauge field + + //Check the momentum is zero on the boundary + const auto &P = integrator.getMomentum(); + Field Pbnd(&Grid), Pbulk(&Grid); + getLinksBoundaryBulk(Pbnd, Pbulk, const_cast(P), latt_size); + + RealD Pbnd_nrm = norm2(Pbnd); //expect zero + std::cout << GridLogMessage << "After refresh, norm2 of mu-directed conjugate momentum on boundary is: " << Pbnd_nrm << " (expect 0)" << std::endl; + RealD Pbulk_nrm = norm2(Pbulk); //expect non-zero + std::cout << GridLogMessage << "After refresh, norm2 of bulk conjugate momentum is: " << Pbulk_nrm << " (expect non-zero)" << std::endl; + + //Evolve the gauge field + integrator.integrate(U); + + //Check momentum is still zero on boundary + getLinksBoundaryBulk(Pbnd, Pbulk, const_cast(P), latt_size); + + Pbnd_nrm = norm2(Pbnd); //expect zero + std::cout << GridLogMessage << "After integrate, norm2 of mu-directed conjugate momentum on boundary is: " << Pbnd_nrm << " (expect 0)" << std::endl; + Pbulk_nrm = norm2(Pbulk); //expect non-zero + std::cout << GridLogMessage << "After integrate, norm2 of bulk conjugate momentum is: " << Pbulk_nrm << " (expect non-zero)" << std::endl; + + //Get the new bulk and bound links + Field Ubnd_new(&Grid), Ubulk_new(&Grid); + getLinksBoundaryBulk(Ubnd_new, Ubulk_new, U, latt_size); + + Field Ubnd_diff = Ubnd_new - Ubnd_orig; + Field Ubulk_diff = Ubulk_new - Ubulk_orig; + + RealD Ubnd_change = norm2( Ubnd_diff ); + RealD Ubulk_change = norm2( Ubulk_diff ); + std::cout << GridLogMessage << "After integrate, norm2 of change in mu-directed boundary links is : " << Ubnd_change << " (expect 0)" << std::endl; + std::cout << GridLogMessage << "After integrate, norm2 of change in bulk links is : " << Ubulk_change << " (expect non-zero)" << std::endl; + + Grid_finalize(); +} From f9b1f240f6af103ff437b459bce2937026c0f4d3 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 26 Feb 2021 17:51:41 +0100 Subject: [PATCH 105/399] Better SIMD usage/coalescence --- Grid/lattice/Lattice_view.h | 7 ++++- Grid/simd/Grid_gpu_vec.h | 23 ++++++++++++-- Grid/simd/Simd.h | 12 ++++--- Grid/tensors/Tensor_SIMT.h | 62 +++++++++++++++++++++++++++++++++++++ Grid/threads/Accelerator.h | 53 ++++++++++++++++++++++++++----- 5 files changed, 143 insertions(+), 14 deletions(-) diff --git a/Grid/lattice/Lattice_view.h b/Grid/lattice/Lattice_view.h index 3b76b921..cb568abd 100644 --- a/Grid/lattice/Lattice_view.h +++ b/Grid/lattice/Lattice_view.h @@ -67,9 +67,14 @@ public: accelerator_inline const vobj & operator()(size_t i) const { return this->_odata[i]; } #endif +#if 1 + // accelerator_inline const vobj & operator[](size_t i) const { return this->_odata[i]; }; + accelerator_inline vobj & operator[](size_t i) const { return this->_odata[i]; }; +#else accelerator_inline const vobj & operator[](size_t i) const { return this->_odata[i]; }; accelerator_inline vobj & operator[](size_t i) { return this->_odata[i]; }; - +#endif + accelerator_inline uint64_t begin(void) const { return 0;}; accelerator_inline uint64_t end(void) const { return this->_odata_size; }; accelerator_inline uint64_t size(void) const { return this->_odata_size; }; diff --git a/Grid/simd/Grid_gpu_vec.h b/Grid/simd/Grid_gpu_vec.h index 8e55ce2f..2c1a38e7 100644 --- a/Grid/simd/Grid_gpu_vec.h +++ b/Grid/simd/Grid_gpu_vec.h @@ -60,11 +60,25 @@ template class GpuComplex { public: pair z; - typedef decltype(z.x) real; + typedef decltype(z.x) Real; public: accelerator_inline GpuComplex() = default; - accelerator_inline GpuComplex(real re,real im) { z.x=re; z.y=im; }; + accelerator_inline GpuComplex(Real re,Real im) { z.x=re; z.y=im; }; accelerator_inline GpuComplex(const GpuComplex &zz) { z = zz.z;}; + accelerator_inline Real real(void) const { return z.x; }; + accelerator_inline Real imag(void) const { return z.y; }; + accelerator_inline GpuComplex &operator*=(const GpuComplex &r) { + *this = (*this) * r; + return *this; + } + accelerator_inline GpuComplex &operator+=(const GpuComplex &r) { + *this = (*this) + r; + return *this; + } + accelerator_inline GpuComplex &operator-=(const GpuComplex &r) { + *this = (*this) - r; + return *this; + } friend accelerator_inline GpuComplex operator+(const GpuComplex &lhs,const GpuComplex &rhs) { GpuComplex r ; r.z.x = lhs.z.x + rhs.z.x; @@ -157,6 +171,11 @@ typedef GpuVector GpuVectorRD; typedef GpuVector GpuVectorCD; typedef GpuVector GpuVectorI; +accelerator_inline GpuComplexF timesI(const GpuComplexF &r) { return(GpuComplexF(-r.imag(),r.real()));} +accelerator_inline GpuComplexD timesI(const GpuComplexD &r) { return(GpuComplexD(-r.imag(),r.real()));} +accelerator_inline GpuComplexF timesMinusI(const GpuComplexF &r){ return(GpuComplexF(r.imag(),-r.real()));} +accelerator_inline GpuComplexD timesMinusI(const GpuComplexD &r){ return(GpuComplexD(r.imag(),-r.real()));} + accelerator_inline float half2float(half h) { float f; diff --git a/Grid/simd/Simd.h b/Grid/simd/Simd.h index 1dc86c1b..76ca3bef 100644 --- a/Grid/simd/Simd.h +++ b/Grid/simd/Simd.h @@ -148,10 +148,14 @@ accelerator_inline void sub (ComplexF * __restrict__ y,const ComplexF * __restri accelerator_inline void add (ComplexF * __restrict__ y,const ComplexF * __restrict__ l,const ComplexF *__restrict__ r){ *y = (*l) + (*r); } //conjugate already supported for complex -accelerator_inline ComplexF timesI(const ComplexF &r) { return(r*ComplexF(0.0,1.0));} -accelerator_inline ComplexD timesI(const ComplexD &r) { return(r*ComplexD(0.0,1.0));} -accelerator_inline ComplexF timesMinusI(const ComplexF &r){ return(r*ComplexF(0.0,-1.0));} -accelerator_inline ComplexD timesMinusI(const ComplexD &r){ return(r*ComplexD(0.0,-1.0));} +accelerator_inline ComplexF timesI(const ComplexF &r) { return(ComplexF(-r.imag(),r.real()));} +accelerator_inline ComplexD timesI(const ComplexD &r) { return(ComplexD(-r.imag(),r.real()));} +accelerator_inline ComplexF timesMinusI(const ComplexF &r){ return(ComplexF(r.imag(),-r.real()));} +accelerator_inline ComplexD timesMinusI(const ComplexD &r){ return(ComplexD(r.imag(),-r.real()));} +//accelerator_inline ComplexF timesI(const ComplexF &r) { return(r*ComplexF(0.0,1.0));} +//accelerator_inline ComplexD timesI(const ComplexD &r) { return(r*ComplexD(0.0,1.0));} +//accelerator_inline ComplexF timesMinusI(const ComplexF &r){ return(r*ComplexF(0.0,-1.0));} +//accelerator_inline ComplexD timesMinusI(const ComplexD &r){ return(r*ComplexD(0.0,-1.0));} // define projections to real and imaginay parts accelerator_inline ComplexF projReal(const ComplexF &r){return( ComplexF(r.real(), 0.0));} diff --git a/Grid/tensors/Tensor_SIMT.h b/Grid/tensors/Tensor_SIMT.h index ec57a679..ede24fbe 100644 --- a/Grid/tensors/Tensor_SIMT.h +++ b/Grid/tensors/Tensor_SIMT.h @@ -64,6 +64,68 @@ void coalescedWriteNonTemporal(vobj & __restrict__ vec,const vobj & __restrict__ } #else + +#ifndef GRID_SYCL +// Use the scalar as our own complex on GPU +template = 0> accelerator_inline +typename vsimd::scalar_type +coalescedRead(const vsimd & __restrict__ vec,int lane=acceleratorSIMTlane(vsimd::Nsimd())) +{ + typedef typename vsimd::scalar_type S; + S * __restrict__ p=(S *)&vec; + return p[lane]; +} +template = 0> accelerator_inline +typename vsimd::scalar_type +coalescedReadPermute(const vsimd & __restrict__ vec,int doperm,int lane=acceleratorSIMTlane(vsimd::Nsimd())) +{ + typedef typename vsimd::scalar_type S; + + S * __restrict__ p=(S *)&vec; + int mask = vsimd::Nsimd() >> (ptype + 1); + int plane= doperm ? lane ^ mask : lane; + return p[plane]; +} +template = 0> accelerator_inline +void coalescedWrite(vsimd & __restrict__ vec, + const typename vsimd::scalar_type & __restrict__ extracted, + int lane=acceleratorSIMTlane(vsimd::Nsimd())) +{ + typedef typename vsimd::scalar_type S; + S * __restrict__ p=(S *)&vec; + p[lane]=extracted; +} +#else +template = 0> accelerator_inline +typename vsimd::vector_type::datum +coalescedRead(const vsimd & __restrict__ vec,int lane=acceleratorSIMTlane(vsimd::Nsimd())) +{ + typedef typename vsimd::vector_type::datum S; + S * __restrict__ p=(S *)&vec; + return p[lane]; +} +template = 0> accelerator_inline +typename vsimd::vector_type::datum +coalescedReadPermute(const vsimd & __restrict__ vec,int doperm,int lane=acceleratorSIMTlane(vsimd::Nsimd())) +{ + typedef typename vsimd::vector_type::datum S; + + S * __restrict__ p=(S *)&vec; + int mask = vsimd::Nsimd() >> (ptype + 1); + int plane= doperm ? lane ^ mask : lane; + return p[plane]; +} +template = 0> accelerator_inline +void coalescedWrite(vsimd & __restrict__ vec, + const typename vsimd::vector_type::datum & __restrict__ extracted, + int lane=acceleratorSIMTlane(vsimd::Nsimd())) +{ + typedef typename vsimd::vector_type::datum S; + S * __restrict__ p=(S *)&vec; + p[lane]=extracted; +} +#endif + ////////////////////////////////////////// // Extract and insert slices on the GPU ////////////////////////////////////////// diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 59645546..2b7bf53a 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -104,7 +104,7 @@ extern int acceleratorAbortOnGpuError; accelerator_inline int acceleratorSIMTlane(int Nsimd) { #ifdef GRID_SIMT - return threadIdx.z; + return threadIdx.x; #else return 0; #endif @@ -112,28 +112,67 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { #define accelerator_for2dNB( iter1, num1, iter2, num2, nsimd, ... ) \ { \ + int nt=acceleratorThreads(); \ typedef uint64_t Iterator; \ auto lambda = [=] accelerator \ (Iterator iter1,Iterator iter2,Iterator lane) mutable { \ __VA_ARGS__; \ }; \ - int nt=acceleratorThreads(); \ - dim3 cu_threads(acceleratorThreads(),1,nsimd); \ + dim3 cu_threads(nsimd,acceleratorThreads(),1); \ dim3 cu_blocks ((num1+nt-1)/nt,num2,1); \ LambdaApply<<>>(num1,num2,nsimd,lambda); \ } +#define accelerator_for6dNB(iter1, num1, \ + iter2, num2, \ + iter3, num3, \ + iter4, num4, \ + iter5, num5, \ + iter6, num6, ... ) \ + { \ + typedef uint64_t Iterator; \ + auto lambda = [=] accelerator \ + (Iterator iter1,Iterator iter2, \ + Iterator iter3,Iterator iter4, \ + Iterator iter5,Iterator iter6) mutable { \ + __VA_ARGS__; \ + }; \ + dim3 cu_blocks (num1,num2,num3); \ + dim3 cu_threads(num4,num5,num6); \ + Lambda6Apply<<>>(num1,num2,num3,num4,num5,num6,lambda); \ + } + template __global__ void LambdaApply(uint64_t num1, uint64_t num2, uint64_t num3, lambda Lambda) { - uint64_t x = threadIdx.x + blockDim.x*blockIdx.x; - uint64_t y = threadIdx.y + blockDim.y*blockIdx.y; - uint64_t z = threadIdx.z; + // Weird permute is to make lane coalesce for large blocks + uint64_t x = threadIdx.y + blockDim.y*blockIdx.x; + uint64_t y = threadIdx.z + blockDim.z*blockIdx.y; + uint64_t z = threadIdx.x; if ( (x < num1) && (y __global__ +void Lambda6Apply(uint64_t num1, uint64_t num2, uint64_t num3, + uint64_t num4, uint64_t num5, uint64_t num6, + lambda Lambda) +{ + uint64_t iter1 = blockIdx.x; + uint64_t iter2 = blockIdx.y; + uint64_t iter3 = blockIdx.z; + uint64_t iter4 = threadIdx.x; + uint64_t iter5 = threadIdx.y; + uint64_t iter6 = threadIdx.z; + + if ( (iter1 < num1) && (iter2 global{unum1,unum2,nsimd}; \ cgh.parallel_for( \ cl::sycl::nd_range<3>(global,local), \ - [=] (cl::sycl::nd_item<3> item) mutable { \ + [=] (cl::sycl::nd_item<3> item) /*mutable*/ { \ auto iter1 = item.get_global_id(0); \ auto iter2 = item.get_global_id(1); \ auto lane = item.get_global_id(2); \ From 442336bd96b9c0062a9c42704d2013331b3b10e1 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 2 Mar 2021 14:50:51 +0100 Subject: [PATCH 106/399] Hand unrolled to use optimised code paths on GPU for coalesced reads in Wilson case. Other cases to do. This now includes comms code path. --- .../WilsonKernelsHandImplementation.h | 217 ++++++++++-------- .../WilsonKernelsImplementation.h | 12 +- 2 files changed, 123 insertions(+), 106 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h index 89ae5668..b867369f 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h @@ -76,7 +76,24 @@ Author: paboyle #define REGISTER -#define LOAD_CHIMU \ +#ifdef GRID_SIMT +#define LOAD_CHIMU(ptype) \ + {const SiteSpinor & ref (in[offset]); \ + Chimu_00=coalescedReadPermute(ref()(0)(0),perm); \ + Chimu_01=coalescedReadPermute(ref()(0)(1),perm); \ + Chimu_02=coalescedReadPermute(ref()(0)(2),perm); \ + Chimu_10=coalescedReadPermute(ref()(1)(0),perm); \ + Chimu_11=coalescedReadPermute(ref()(1)(1),perm); \ + Chimu_12=coalescedReadPermute(ref()(1)(2),perm); \ + Chimu_20=coalescedReadPermute(ref()(2)(0),perm); \ + Chimu_21=coalescedReadPermute(ref()(2)(1),perm); \ + Chimu_22=coalescedReadPermute(ref()(2)(2),perm); \ + Chimu_30=coalescedReadPermute(ref()(3)(0),perm); \ + Chimu_31=coalescedReadPermute(ref()(3)(1),perm); \ + Chimu_32=coalescedReadPermute(ref()(3)(2),perm); } +#define PERMUTE_DIR(dir) ; +#else +#define LOAD_CHIMU \ {const SiteSpinor & ref (in[offset]); \ Chimu_00=ref()(0)(0);\ Chimu_01=ref()(0)(1);\ @@ -91,55 +108,55 @@ Author: paboyle Chimu_31=ref()(3)(1);\ Chimu_32=ref()(3)(2);} -#define LOAD_CHI\ - {const SiteHalfSpinor &ref(buf[offset]); \ - Chi_00 = ref()(0)(0);\ - Chi_01 = ref()(0)(1);\ - Chi_02 = ref()(0)(2);\ - Chi_10 = ref()(1)(0);\ - Chi_11 = ref()(1)(1);\ - Chi_12 = ref()(1)(2);} - -// To splat or not to splat depends on the implementation -#define MULT_2SPIN(A)\ - {auto & ref(U[sU](A)); \ - Impl::loadLinkElement(U_00,ref()(0,0)); \ - Impl::loadLinkElement(U_10,ref()(1,0)); \ - Impl::loadLinkElement(U_20,ref()(2,0)); \ - Impl::loadLinkElement(U_01,ref()(0,1)); \ - Impl::loadLinkElement(U_11,ref()(1,1)); \ - Impl::loadLinkElement(U_21,ref()(2,1)); \ - UChi_00 = U_00*Chi_00;\ - UChi_10 = U_00*Chi_10;\ - UChi_01 = U_10*Chi_00;\ - UChi_11 = U_10*Chi_10;\ - UChi_02 = U_20*Chi_00;\ - UChi_12 = U_20*Chi_10;\ - UChi_00+= U_01*Chi_01;\ - UChi_10+= U_01*Chi_11;\ - UChi_01+= U_11*Chi_01;\ - UChi_11+= U_11*Chi_11;\ - UChi_02+= U_21*Chi_01;\ - UChi_12+= U_21*Chi_11;\ - Impl::loadLinkElement(U_00,ref()(0,2)); \ - Impl::loadLinkElement(U_10,ref()(1,2)); \ - Impl::loadLinkElement(U_20,ref()(2,2)); \ - UChi_00+= U_00*Chi_02;\ - UChi_10+= U_00*Chi_12;\ - UChi_01+= U_10*Chi_02;\ - UChi_11+= U_10*Chi_12;\ - UChi_02+= U_20*Chi_02;\ - UChi_12+= U_20*Chi_12;} - - #define PERMUTE_DIR(dir) \ - permute##dir(Chi_00,Chi_00);\ + permute##dir(Chi_00,Chi_00); \ permute##dir(Chi_01,Chi_01);\ permute##dir(Chi_02,Chi_02);\ - permute##dir(Chi_10,Chi_10);\ + permute##dir(Chi_10,Chi_10); \ permute##dir(Chi_11,Chi_11);\ permute##dir(Chi_12,Chi_12); +#endif + +#define LOAD_CHI \ + {const SiteHalfSpinor &ref(buf[offset]); \ + Chi_00 = coalescedRead(ref()(0)(0)); \ + Chi_01 = coalescedRead(ref()(0)(1)); \ + Chi_02 = coalescedRead(ref()(0)(2)); \ + Chi_10 = coalescedRead(ref()(1)(0)); \ + Chi_11 = coalescedRead(ref()(1)(1)); \ + Chi_12 = coalescedRead(ref()(1)(2));} + +#define MULT_2SPIN(A)\ + {auto & ref(U[sU](A)); \ + U_00=coalescedRead(ref()(0,0)); \ + U_10=coalescedRead(ref()(1,0)); \ + U_20=coalescedRead(ref()(2,0)); \ + U_01=coalescedRead(ref()(0,1)); \ + U_11=coalescedRead(ref()(1,1)); \ + U_21=coalescedRead(ref()(2,1)); \ + UChi_00 = U_00*Chi_00; \ + UChi_10 = U_00*Chi_10; \ + UChi_01 = U_10*Chi_00; \ + UChi_11 = U_10*Chi_10; \ + UChi_02 = U_20*Chi_00; \ + UChi_12 = U_20*Chi_10; \ + UChi_00+= U_01*Chi_01; \ + UChi_10+= U_01*Chi_11; \ + UChi_01+= U_11*Chi_01; \ + UChi_11+= U_11*Chi_11; \ + UChi_02+= U_21*Chi_01; \ + UChi_12+= U_21*Chi_11; \ + U_00=coalescedRead(ref()(0,2)); \ + U_10=coalescedRead(ref()(1,2)); \ + U_20=coalescedRead(ref()(2,2)); \ + UChi_00+= U_00*Chi_02; \ + UChi_10+= U_00*Chi_12; \ + UChi_01+= U_10*Chi_02; \ + UChi_11+= U_10*Chi_12; \ + UChi_02+= U_20*Chi_02; \ + UChi_12+= U_20*Chi_12;} + // hspin(0)=fspin(0)+timesI(fspin(3)); // hspin(1)=fspin(1)+timesI(fspin(2)); #define XP_PROJ \ @@ -359,7 +376,7 @@ Author: paboyle local = SE->_is_local; \ perm = SE->_permute; \ if ( local ) { \ - LOAD_CHIMU; \ + LOAD_CHIMU(PERM); \ PROJ; \ if ( perm) { \ PERMUTE_DIR(PERM); \ @@ -376,7 +393,7 @@ Author: paboyle local = SE->_is_local; \ perm = SE->_permute; \ if ( local ) { \ - LOAD_CHIMU; \ + LOAD_CHIMU(PERM); \ PROJ; \ if ( perm) { \ PERMUTE_DIR(PERM); \ @@ -401,40 +418,39 @@ Author: paboyle #define HAND_RESULT(ss) \ { \ - SiteSpinor & ref (out[ss]); \ - vstream(ref()(0)(0),result_00); \ - vstream(ref()(0)(1),result_01); \ - vstream(ref()(0)(2),result_02); \ - vstream(ref()(1)(0),result_10); \ - vstream(ref()(1)(1),result_11); \ - vstream(ref()(1)(2),result_12); \ - vstream(ref()(2)(0),result_20); \ - vstream(ref()(2)(1),result_21); \ - vstream(ref()(2)(2),result_22); \ - vstream(ref()(3)(0),result_30); \ - vstream(ref()(3)(1),result_31); \ - vstream(ref()(3)(2),result_32); \ + SiteSpinor & ref (out[ss]); \ + coalescedWrite(ref()(0)(0),result_00); \ + coalescedWrite(ref()(0)(1),result_01); \ + coalescedWrite(ref()(0)(2),result_02); \ + coalescedWrite(ref()(1)(0),result_10); \ + coalescedWrite(ref()(1)(1),result_11); \ + coalescedWrite(ref()(1)(2),result_12); \ + coalescedWrite(ref()(2)(0),result_20); \ + coalescedWrite(ref()(2)(1),result_21); \ + coalescedWrite(ref()(2)(2),result_22); \ + coalescedWrite(ref()(3)(0),result_30); \ + coalescedWrite(ref()(3)(1),result_31); \ + coalescedWrite(ref()(3)(2),result_32); \ } -#define HAND_RESULT_EXT(ss) \ - if (nmu){ \ - SiteSpinor & ref (out[ss]); \ - ref()(0)(0)+=result_00; \ - ref()(0)(1)+=result_01; \ - ref()(0)(2)+=result_02; \ - ref()(1)(0)+=result_10; \ - ref()(1)(1)+=result_11; \ - ref()(1)(2)+=result_12; \ - ref()(2)(0)+=result_20; \ - ref()(2)(1)+=result_21; \ - ref()(2)(2)+=result_22; \ - ref()(3)(0)+=result_30; \ - ref()(3)(1)+=result_31; \ - ref()(3)(2)+=result_32; \ +#define HAND_RESULT_EXT(ss) \ + { \ + SiteSpinor & ref (out[ss]); \ + coalescedWrite(ref()(0)(0),coalescedRead(ref()(0)(0))+result_00); \ + coalescedWrite(ref()(0)(1),coalescedRead(ref()(0)(1))+result_01); \ + coalescedWrite(ref()(0)(2),coalescedRead(ref()(0)(2))+result_02); \ + coalescedWrite(ref()(1)(0),coalescedRead(ref()(1)(0))+result_10); \ + coalescedWrite(ref()(1)(1),coalescedRead(ref()(1)(1))+result_11); \ + coalescedWrite(ref()(1)(2),coalescedRead(ref()(1)(2))+result_12); \ + coalescedWrite(ref()(2)(0),coalescedRead(ref()(2)(0))+result_20); \ + coalescedWrite(ref()(2)(1),coalescedRead(ref()(2)(1))+result_21); \ + coalescedWrite(ref()(2)(2),coalescedRead(ref()(2)(2))+result_22); \ + coalescedWrite(ref()(3)(0),coalescedRead(ref()(3)(0))+result_30); \ + coalescedWrite(ref()(3)(1),coalescedRead(ref()(3)(1))+result_31); \ + coalescedWrite(ref()(3)(2),coalescedRead(ref()(3)(2))+result_32); \ } - -#define HAND_DECLARATIONS(a) \ +#define HAND_DECLARATIONS(Simd) \ Simd result_00; \ Simd result_01; \ Simd result_02; \ @@ -467,18 +483,18 @@ Author: paboyle Simd U_21; #define ZERO_RESULT \ - result_00=Zero(); \ - result_01=Zero(); \ - result_02=Zero(); \ - result_10=Zero(); \ - result_11=Zero(); \ - result_12=Zero(); \ - result_20=Zero(); \ - result_21=Zero(); \ - result_22=Zero(); \ - result_30=Zero(); \ - result_31=Zero(); \ - result_32=Zero(); + result_00=S(0.0,0.0); \ + result_01=S(0.0,0.0); \ + result_02=S(0.0,0.0); \ + result_10=S(0.0,0.0); \ + result_11=S(0.0,0.0); \ + result_12=S(0.0,0.0); \ + result_20=S(0.0,0.0); \ + result_21=S(0.0,0.0); \ + result_22=S(0.0,0.0); \ + result_30=S(0.0,0.0); \ + result_31=S(0.0,0.0); \ + result_32=S(0.0,0.0); #define Chimu_00 Chi_00 #define Chimu_01 Chi_01 @@ -502,8 +518,8 @@ WilsonKernels::HandDhopSite(StencilView &st, DoubledGaugeFieldView &U,Site // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - - HAND_DECLARATIONS(ignore); + typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + HAND_DECLARATIONS(Simt); int offset,local,perm, ptype; StencilEntry *SE; @@ -525,8 +541,8 @@ void WilsonKernels::HandDhopSiteDag(StencilView &st,DoubledGaugeFieldView { typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - - HAND_DECLARATIONS(ignore); + typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + HAND_DECLARATIONS(Simt); StencilEntry *SE; int offset,local,perm, ptype; @@ -549,8 +565,8 @@ WilsonKernels::HandDhopSiteInt(StencilView &st,DoubledGaugeFieldView &U,Si // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - - HAND_DECLARATIONS(ignore); + typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + HAND_DECLARATIONS(Simt); int offset,local,perm, ptype; StencilEntry *SE; @@ -572,8 +588,8 @@ void WilsonKernels::HandDhopSiteDagInt(StencilView &st,DoubledGaugeFieldVi { typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - - HAND_DECLARATIONS(ignore); + typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + HAND_DECLARATIONS(Simt); StencilEntry *SE; int offset,local,perm, ptype; @@ -596,8 +612,8 @@ WilsonKernels::HandDhopSiteExt(StencilView &st,DoubledGaugeFieldView &U,Si // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - - HAND_DECLARATIONS(ignore); + typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + HAND_DECLARATIONS(Simt); int offset, ptype; StencilEntry *SE; @@ -620,8 +636,8 @@ void WilsonKernels::HandDhopSiteDagExt(StencilView &st,DoubledGaugeFieldVi { typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - - HAND_DECLARATIONS(ignore); + typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + HAND_DECLARATIONS(Simt); StencilEntry *SE; int offset, ptype; @@ -682,3 +698,4 @@ NAMESPACE_END(Grid); #undef HAND_RESULT #undef HAND_RESULT_INT #undef HAND_RESULT_EXT +#undef HAND_DECLARATIONS diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h index c5f50bbb..937d13af 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h @@ -445,20 +445,20 @@ void WilsonKernels::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField if( interior && exterior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSite); return;} -#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSite); return;} +#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSite); return;} #endif } else if( interior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALLNB(GenericDhopSiteInt); return;} -#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALLNB(HandDhopSiteInt); return;} +#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteInt); return;} #endif } else if( exterior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteExt); return;} -#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteExt); return;} +#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteExt); return;} #endif } @@ -476,20 +476,20 @@ void WilsonKernels::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField if( interior && exterior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteDag); return;} -#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteDag); return;} +#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDag); return;} #endif } else if( interior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteDagInt); return;} -#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteDagInt); return;} +#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDagInt); return;} #endif } else if( exterior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteDagExt); return;} -#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteDagExt); return;} +#ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDagExt); return;} #endif } From 679d1d22f7e920163acf7b88515c30ceb297387b Mon Sep 17 00:00:00 2001 From: u61464 Date: Wed, 3 Mar 2021 11:21:43 -0800 Subject: [PATCH 107/399] Sycl happier --- Grid/Makefile.am | 4 ++- Grid/cartesian/Cartesian_red_black.h | 2 +- Grid/qcd/action/fermion/FermionOperatorImpl.h | 4 +-- Grid/qcd/action/fermion/WilsonCompressor.h | 24 ++++++++-------- Grid/qcd/action/fermion/WilsonImpl.h | 2 +- .../WilsonKernelsHandImplementation.h | 28 +++++++++---------- Grid/simd/Grid_gpu_vec.h | 1 + Grid/stencil/SimpleCompressor.h | 14 +++++----- Grid/stencil/Stencil.h | 14 +++++----- Grid/tensors/Tensor_SIMT.h | 4 ++- Grid/threads/Accelerator.h | 2 +- benchmarks/Benchmark_dwf_fp32.cc | 2 +- configure.ac | 11 ++++++++ 13 files changed, 64 insertions(+), 48 deletions(-) diff --git a/Grid/Makefile.am b/Grid/Makefile.am index ded6d146..7c3c151b 100644 --- a/Grid/Makefile.am +++ b/Grid/Makefile.am @@ -54,9 +54,11 @@ Version.h: version-cache include Make.inc include Eigen.inc -extra_sources+=$(ZWILS_FERMION_FILES) extra_sources+=$(WILS_FERMION_FILES) extra_sources+=$(STAG_FERMION_FILES) +if BUILD_ZMOBIUS + extra_sources+=$(ZWILS_FERMION_FILES) +endif if BUILD_GPARITY extra_sources+=$(GP_FERMION_FILES) endif diff --git a/Grid/cartesian/Cartesian_red_black.h b/Grid/cartesian/Cartesian_red_black.h index b71981f5..092d4910 100644 --- a/Grid/cartesian/Cartesian_red_black.h +++ b/Grid/cartesian/Cartesian_red_black.h @@ -36,7 +36,7 @@ static const int CbBlack=1; static const int Even =CbRed; static const int Odd =CbBlack; -accelerator_inline int RedBlackCheckerBoardFromOindex (int oindex, Coordinate &rdim, Coordinate &chk_dim_msk) +accelerator_inline int RedBlackCheckerBoardFromOindex (int oindex,const Coordinate &rdim,const Coordinate &chk_dim_msk) { int nd=rdim.size(); Coordinate coor(nd); diff --git a/Grid/qcd/action/fermion/FermionOperatorImpl.h b/Grid/qcd/action/fermion/FermionOperatorImpl.h index b444f6dc..9345c0e6 100644 --- a/Grid/qcd/action/fermion/FermionOperatorImpl.h +++ b/Grid/qcd/action/fermion/FermionOperatorImpl.h @@ -153,8 +153,8 @@ public: typedef typename Impl::StencilImpl StencilImpl; \ typedef typename Impl::ImplParams ImplParams; \ typedef typename Impl::StencilImpl::View_type StencilView; \ - typedef typename ViewMap::Type FermionFieldView; \ - typedef typename ViewMap::Type DoubledGaugeFieldView; + typedef const typename ViewMap::Type FermionFieldView; \ + typedef const typename ViewMap::Type DoubledGaugeFieldView; #define INHERIT_IMPL_TYPES(Base) \ INHERIT_GIMPL_TYPES(Base) \ diff --git a/Grid/qcd/action/fermion/WilsonCompressor.h b/Grid/qcd/action/fermion/WilsonCompressor.h index 10e98f33..0760bcba 100644 --- a/Grid/qcd/action/fermion/WilsonCompressor.h +++ b/Grid/qcd/action/fermion/WilsonCompressor.h @@ -61,7 +61,7 @@ public: typedef typename SiteHalfSpinor::vector_type vComplexHigh; constexpr static int Nw=sizeof(SiteHalfSpinor)/sizeof(vComplexHigh); - accelerator_inline int CommDatumSize(void) { + accelerator_inline int CommDatumSize(void) const { return sizeof(SiteHalfCommSpinor); } @@ -69,7 +69,7 @@ public: /* Compress includes precision change if mpi data is not same */ /*****************************************************/ template - accelerator_inline void Compress(_SiteHalfSpinor *buf,Integer o,const _SiteSpinor &in) { + accelerator_inline void Compress(_SiteHalfSpinor *buf,Integer o,const _SiteSpinor &in) const { _SiteHalfSpinor tmp; projector::Proj(tmp,in,mu,dag); vstream(buf[o],tmp); @@ -81,7 +81,7 @@ public: accelerator_inline void Exchange(SiteHalfSpinor *mp, const SiteHalfSpinor * __restrict__ vp0, const SiteHalfSpinor * __restrict__ vp1, - Integer type,Integer o){ + Integer type,Integer o) const { SiteHalfSpinor tmp1; SiteHalfSpinor tmp2; exchange(tmp1,tmp2,vp0[o],vp1[o],type); @@ -93,7 +93,7 @@ public: /* Have a decompression step if mpi data is not same */ /*****************************************************/ accelerator_inline void Decompress(SiteHalfSpinor * __restrict__ out, - SiteHalfSpinor * __restrict__ in, Integer o) { + SiteHalfSpinor * __restrict__ in, Integer o) const { assert(0); } @@ -103,7 +103,7 @@ public: accelerator_inline void CompressExchange(SiteHalfSpinor * __restrict__ out0, SiteHalfSpinor * __restrict__ out1, const SiteSpinor * __restrict__ in, - Integer j,Integer k, Integer m,Integer type) + Integer j,Integer k, Integer m,Integer type) const { SiteHalfSpinor temp1, temp2; SiteHalfSpinor temp3, temp4; @@ -117,7 +117,7 @@ public: /*****************************************************/ /* Pass the info to the stencil */ /*****************************************************/ - accelerator_inline bool DecompressionStep(void) { return false; } + accelerator_inline bool DecompressionStep(void) const { return false; } }; @@ -142,7 +142,7 @@ public: typedef typename SiteHalfSpinor::vector_type vComplexHigh; constexpr static int Nw=sizeof(SiteHalfSpinor)/sizeof(vComplexHigh); - accelerator_inline int CommDatumSize(void) { + accelerator_inline int CommDatumSize(void) const { return sizeof(SiteHalfCommSpinor); } @@ -150,7 +150,7 @@ public: /* Compress includes precision change if mpi data is not same */ /*****************************************************/ template - accelerator_inline void Compress(_SiteHalfSpinor *buf,Integer o,const _SiteSpinor &in) { + accelerator_inline void Compress(_SiteHalfSpinor *buf,Integer o,const _SiteSpinor &in) const { _SiteHalfSpinor hsp; SiteHalfCommSpinor *hbuf = (SiteHalfCommSpinor *)buf; projector::Proj(hsp,in,mu,dag); @@ -163,7 +163,7 @@ public: accelerator_inline void Exchange(SiteHalfSpinor *mp, SiteHalfSpinor *vp0, SiteHalfSpinor *vp1, - Integer type,Integer o){ + Integer type,Integer o) const { SiteHalfSpinor vt0,vt1; SiteHalfCommSpinor *vpp0 = (SiteHalfCommSpinor *)vp0; SiteHalfCommSpinor *vpp1 = (SiteHalfCommSpinor *)vp1; @@ -175,7 +175,7 @@ public: /*****************************************************/ /* Have a decompression step if mpi data is not same */ /*****************************************************/ - accelerator_inline void Decompress(SiteHalfSpinor *out, SiteHalfSpinor *in, Integer o){ + accelerator_inline void Decompress(SiteHalfSpinor *out, SiteHalfSpinor *in, Integer o) const { SiteHalfCommSpinor *hin=(SiteHalfCommSpinor *)in; precisionChange((vComplexHigh *)&out[o],(vComplexLow *)&hin[o],Nw); } @@ -186,7 +186,7 @@ public: accelerator_inline void CompressExchange(SiteHalfSpinor *out0, SiteHalfSpinor *out1, const SiteSpinor *in, - Integer j,Integer k, Integer m,Integer type){ + Integer j,Integer k, Integer m,Integer type) const { SiteHalfSpinor temp1, temp2,temp3,temp4; SiteHalfCommSpinor *hout0 = (SiteHalfCommSpinor *)out0; SiteHalfCommSpinor *hout1 = (SiteHalfCommSpinor *)out1; @@ -200,7 +200,7 @@ public: /*****************************************************/ /* Pass the info to the stencil */ /*****************************************************/ - accelerator_inline bool DecompressionStep(void) { return true; } + accelerator_inline bool DecompressionStep(void) const { return true; } }; diff --git a/Grid/qcd/action/fermion/WilsonImpl.h b/Grid/qcd/action/fermion/WilsonImpl.h index d7941d1f..94676b6b 100644 --- a/Grid/qcd/action/fermion/WilsonImpl.h +++ b/Grid/qcd/action/fermion/WilsonImpl.h @@ -72,7 +72,7 @@ public: typedef WilsonCompressor Compressor; typedef WilsonImplParams ImplParams; typedef WilsonStencil StencilImpl; - typedef typename StencilImpl::View_type StencilView; + typedef const typename StencilImpl::View_type StencilView; ImplParams Params; diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h index b867369f..688cb75a 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h @@ -93,7 +93,7 @@ Author: paboyle Chimu_32=coalescedReadPermute(ref()(3)(2),perm); } #define PERMUTE_DIR(dir) ; #else -#define LOAD_CHIMU \ +#define LOAD_CHIMU(ptype) \ {const SiteSpinor & ref (in[offset]); \ Chimu_00=ref()(0)(0);\ Chimu_01=ref()(0)(1);\ @@ -482,19 +482,19 @@ Author: paboyle Simd U_11; \ Simd U_21; -#define ZERO_RESULT \ - result_00=S(0.0,0.0); \ - result_01=S(0.0,0.0); \ - result_02=S(0.0,0.0); \ - result_10=S(0.0,0.0); \ - result_11=S(0.0,0.0); \ - result_12=S(0.0,0.0); \ - result_20=S(0.0,0.0); \ - result_21=S(0.0,0.0); \ - result_22=S(0.0,0.0); \ - result_30=S(0.0,0.0); \ - result_31=S(0.0,0.0); \ - result_32=S(0.0,0.0); +#define ZERO_RESULT \ + zeroit(result_00); \ + zeroit(result_01); \ + zeroit(result_02); \ + zeroit(result_10); \ + zeroit(result_11); \ + zeroit(result_12); \ + zeroit(result_20); \ + zeroit(result_21); \ + zeroit(result_22); \ + zeroit(result_30); \ + zeroit(result_31); \ + zeroit(result_32); #define Chimu_00 Chi_00 #define Chimu_01 Chi_01 diff --git a/Grid/simd/Grid_gpu_vec.h b/Grid/simd/Grid_gpu_vec.h index 2c1a38e7..b2c7588f 100644 --- a/Grid/simd/Grid_gpu_vec.h +++ b/Grid/simd/Grid_gpu_vec.h @@ -67,6 +67,7 @@ public: accelerator_inline GpuComplex(const GpuComplex &zz) { z = zz.z;}; accelerator_inline Real real(void) const { return z.x; }; accelerator_inline Real imag(void) const { return z.y; }; + accelerator_inline GpuComplex &operator=(const Zero &zz) { z.x = 0; z.y=0; return *this; }; accelerator_inline GpuComplex &operator*=(const GpuComplex &r) { *this = (*this) * r; return *this; diff --git a/Grid/stencil/SimpleCompressor.h b/Grid/stencil/SimpleCompressor.h index be7c89c0..2ce48369 100644 --- a/Grid/stencil/SimpleCompressor.h +++ b/Grid/stencil/SimpleCompressor.h @@ -7,20 +7,20 @@ template class SimpleCompressor { public: void Point(int) {}; - accelerator_inline int CommDatumSize(void) { return sizeof(vobj); } - accelerator_inline bool DecompressionStep(void) { return false; } - template accelerator_inline void Compress(cobj *buf,int o,const cobj &in) { buf[o]=in; } - accelerator_inline void Exchange(vobj *mp,vobj *vp0,vobj *vp1,Integer type,Integer o){ + accelerator_inline int CommDatumSize(void) const { return sizeof(vobj); } + accelerator_inline bool DecompressionStep(void) const { return false; } + template accelerator_inline void Compress(cobj *buf,int o,const cobj &in) const { buf[o]=in; } + accelerator_inline void Exchange(vobj *mp,vobj *vp0,vobj *vp1,Integer type,Integer o) const { exchange(mp[2*o],mp[2*o+1],vp0[o],vp1[o],type); } - accelerator_inline void Decompress(vobj *out,vobj *in, int o){ assert(0); } + accelerator_inline void Decompress(vobj *out,vobj *in, int o) const { assert(0); } accelerator_inline void CompressExchange(vobj *out0,vobj *out1,const vobj *in, - int j,int k, int m,int type){ + int j,int k, int m,int type) const { exchange(out0[j],out1[j],in[k],in[m],type); } // For cshift. Cshift should drop compressor coupling altogether // because I had to decouple the code from the Stencil anyway - accelerator_inline vobj operator() (const vobj &arg) { + accelerator_inline vobj operator() (const vobj &arg) const { return arg; } }; diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index 23fc8203..58cebed3 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -147,16 +147,16 @@ class CartesianStencilAccelerator { cobj* u_recv_buf_p; cobj* u_send_buf_p; - accelerator_inline cobj *CommBuf(void) { return u_recv_buf_p; } + accelerator_inline cobj *CommBuf(void) const { return u_recv_buf_p; } - accelerator_inline int GetNodeLocal(int osite,int point) { + accelerator_inline int GetNodeLocal(int osite,int point) const { return this->_entries_p[point+this->_npoints*osite]._is_local; } - accelerator_inline StencilEntry * GetEntry(int &ptype,int point,int osite) { + accelerator_inline StencilEntry * GetEntry(int &ptype,int point,int osite) const { ptype = this->_permute_type[point]; return & this->_entries_p[point+this->_npoints*osite]; } - accelerator_inline uint64_t GetInfo(int &ptype,int &local,int &perm,int point,int ent,uint64_t base) { + accelerator_inline uint64_t GetInfo(int &ptype,int &local,int &perm,int point,int ent,uint64_t base) const { uint64_t cbase = (uint64_t)&u_recv_buf_p[0]; local = this->_entries_p[ent]._is_local; perm = this->_entries_p[ent]._permute; @@ -168,14 +168,14 @@ class CartesianStencilAccelerator { } } - accelerator_inline uint64_t GetPFInfo(int ent,uint64_t base) { + accelerator_inline uint64_t GetPFInfo(int ent,uint64_t base) const { uint64_t cbase = (uint64_t)&u_recv_buf_p[0]; int local = this->_entries_p[ent]._is_local; if (local) return base + this->_entries_p[ent]._byte_offset; else return cbase + this->_entries_p[ent]._byte_offset; } - accelerator_inline void iCoorFromIindex(Coordinate &coor,int lane) + accelerator_inline void iCoorFromIindex(Coordinate &coor,int lane) const { Lexicographic::CoorFromIndex(coor,lane,this->_simd_layout); } @@ -221,7 +221,7 @@ public: typedef typename cobj::vector_type vector_type; typedef typename cobj::scalar_type scalar_type; typedef typename cobj::scalar_object scalar_object; - typedef CartesianStencilView View_type; + typedef const CartesianStencilView View_type; typedef typename View_type::StencilVector StencilVector; /////////////////////////////////////////// // Helper structs diff --git a/Grid/tensors/Tensor_SIMT.h b/Grid/tensors/Tensor_SIMT.h index ede24fbe..672f385f 100644 --- a/Grid/tensors/Tensor_SIMT.h +++ b/Grid/tensors/Tensor_SIMT.h @@ -66,7 +66,7 @@ void coalescedWriteNonTemporal(vobj & __restrict__ vec,const vobj & __restrict__ #ifndef GRID_SYCL -// Use the scalar as our own complex on GPU +// Use the scalar as our own complex on GPU ... thrust::complex or std::complex template = 0> accelerator_inline typename vsimd::scalar_type coalescedRead(const vsimd & __restrict__ vec,int lane=acceleratorSIMTlane(vsimd::Nsimd())) @@ -96,6 +96,8 @@ void coalescedWrite(vsimd & __restrict__ vec, p[lane]=extracted; } #else +// For SyCL have option to use GpuComplex from inside the vector type in SIMT loops +// Faster for some reason template = 0> accelerator_inline typename vsimd::vector_type::datum coalescedRead(const vsimd & __restrict__ vec,int lane=acceleratorSIMTlane(vsimd::Nsimd())) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 2b7bf53a..f1a694fb 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -456,7 +456,7 @@ accelerator_inline void acceleratorSynchronise(void) __syncwarp(); #endif #ifdef GRID_SYCL - // No barrier call on SYCL?? // Option get __spir:: stuff to do warp barrier + cl::sycl::detail::workGroupBarrier(); #endif #ifdef GRID_HIP __syncthreads(); diff --git a/benchmarks/Benchmark_dwf_fp32.cc b/benchmarks/Benchmark_dwf_fp32.cc index cb86177e..03f3ee61 100644 --- a/benchmarks/Benchmark_dwf_fp32.cc +++ b/benchmarks/Benchmark_dwf_fp32.cc @@ -53,7 +53,7 @@ int main (int argc, char ** argv) int threads = GridThread::GetThreads(); Coordinate latt4 = GridDefaultLatt(); - int Ls=8; + int Ls=16; for(int i=0;i> Ls; diff --git a/configure.ac b/configure.ac index fb0c78fc..5f165412 100644 --- a/configure.ac +++ b/configure.ac @@ -140,12 +140,23 @@ AC_ARG_ENABLE([gparity], [ac_GPARITY=${enable_gparity}], [ac_GPARITY=yes]) AM_CONDITIONAL(BUILD_GPARITY, [ test "${ac_GPARITY}X" == "yesX" ]) + +AC_ARG_ENABLE([zmobius], + [AC_HELP_STRING([--enable-zmobius=yes|no], [enable Zmobius support])], + [ac_ZMOBIUS=${enable_zmobius}], [ac_ZMOBIUS=yes]) + +AM_CONDITIONAL(BUILD_ZMOBIUS, [ test "${ac_ZMOBIUS}X" == "yesX" ]) + + case ${ac_FERMION_REPS} in yes) AC_DEFINE([ENABLE_FERMION_REPS],[1],[non QCD fermion reps]);; esac case ${ac_GPARITY} in yes) AC_DEFINE([ENABLE_GPARITY],[1],[fermion actions with GPARITY BCs]);; esac +case ${ac_ZMOBIUS} in + yes) AC_DEFINE([ENABLE_ZMOBIUS],[1],[Zmobius fermion actions]);; +esac ############### Nc AC_ARG_ENABLE([Nc], [AC_HELP_STRING([--enable-Nc=2|3|4], [enable number of colours])], From 1eea9d73b984252c69869f65991f2f33d6e93193 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 3 Mar 2021 23:50:01 +0100 Subject: [PATCH 108/399] Pass serial RNG around --- Grid/qcd/action/ActionBase.h | 2 +- Grid/qcd/action/gauge/GaugeImplTypes.h | 2 +- Grid/qcd/action/gauge/PlaqPlusRectangleAction.h | 2 +- Grid/qcd/action/gauge/WilsonGaugeAction.h | 3 +-- .../qcd/action/pseudofermion/ExactOneFlavourRatio.h | 2 +- .../pseudofermion/OneFlavourEvenOddRational.h | 11 ++++++----- .../pseudofermion/OneFlavourEvenOddRationalRatio.h | 7 +++++-- Grid/qcd/action/pseudofermion/OneFlavourRational.h | 7 +++++-- .../action/pseudofermion/OneFlavourRationalRatio.h | 7 +++++-- Grid/qcd/action/pseudofermion/TwoFlavour.h | 2 +- Grid/qcd/action/pseudofermion/TwoFlavourEvenOdd.h | 2 +- .../action/pseudofermion/TwoFlavourEvenOddRatio.h | 2 +- Grid/qcd/action/pseudofermion/TwoFlavourRatio.h | 2 +- Grid/qcd/action/scalar/ScalarAction.h | 2 +- Grid/qcd/action/scalar/ScalarImpl.h | 4 ++-- Grid/qcd/action/scalar/ScalarInteractionAction.h | 2 +- Grid/qcd/hmc/HMC.h | 2 +- Grid/qcd/hmc/integrators/Integrator.h | 13 ++++++------- benchmarks/Benchmark_dwf_fp32.cc | 2 +- 19 files changed, 42 insertions(+), 34 deletions(-) diff --git a/Grid/qcd/action/ActionBase.h b/Grid/qcd/action/ActionBase.h index bff21d1d..17980ee0 100644 --- a/Grid/qcd/action/ActionBase.h +++ b/Grid/qcd/action/ActionBase.h @@ -41,7 +41,7 @@ class Action public: bool is_smeared = false; // Heatbath? - virtual void refresh(const GaugeField& U, GridParallelRNG& pRNG) = 0; // refresh pseudofermions + virtual void refresh(const GaugeField& U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) = 0; // refresh pseudofermions virtual RealD S(const GaugeField& U) = 0; // evaluate the action virtual void deriv(const GaugeField& U, GaugeField& dSdU) = 0; // evaluate the action derivative virtual std::string action_name() = 0; // return the action name diff --git a/Grid/qcd/action/gauge/GaugeImplTypes.h b/Grid/qcd/action/gauge/GaugeImplTypes.h index 55a20eca..2499e0e9 100644 --- a/Grid/qcd/action/gauge/GaugeImplTypes.h +++ b/Grid/qcd/action/gauge/GaugeImplTypes.h @@ -96,7 +96,7 @@ public: /////////////////////////////////////////////////////////// // Move these to another class // HMC auxiliary functions - static inline void generate_momenta(Field &P, GridParallelRNG &pRNG) + static inline void generate_momenta(Field &P, GridSerialRNG & sRNG, GridParallelRNG &pRNG) { // Zbigniew Srocinsky thesis: // diff --git a/Grid/qcd/action/gauge/PlaqPlusRectangleAction.h b/Grid/qcd/action/gauge/PlaqPlusRectangleAction.h index 639aca19..7690092d 100644 --- a/Grid/qcd/action/gauge/PlaqPlusRectangleAction.h +++ b/Grid/qcd/action/gauge/PlaqPlusRectangleAction.h @@ -49,7 +49,7 @@ public: virtual std::string action_name(){return "PlaqPlusRectangleAction";} - virtual void refresh(const GaugeField &U, GridParallelRNG& pRNG) {}; // noop as no pseudoferms + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) {}; // noop as no pseudoferms virtual std::string LogParameters(){ std::stringstream sstream; diff --git a/Grid/qcd/action/gauge/WilsonGaugeAction.h b/Grid/qcd/action/gauge/WilsonGaugeAction.h index 40d600d2..f535b54f 100644 --- a/Grid/qcd/action/gauge/WilsonGaugeAction.h +++ b/Grid/qcd/action/gauge/WilsonGaugeAction.h @@ -54,8 +54,7 @@ public: return sstream.str(); } - virtual void refresh(const GaugeField &U, - GridParallelRNG &pRNG){}; // noop as no pseudoferms + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG &pRNG){}; // noop as no pseudoferms virtual RealD S(const GaugeField &U) { RealD plaq = WilsonLoops::avgPlaquette(U); diff --git a/Grid/qcd/action/pseudofermion/ExactOneFlavourRatio.h b/Grid/qcd/action/pseudofermion/ExactOneFlavourRatio.h index 9fc0a3b0..576a8cf6 100644 --- a/Grid/qcd/action/pseudofermion/ExactOneFlavourRatio.h +++ b/Grid/qcd/action/pseudofermion/ExactOneFlavourRatio.h @@ -124,7 +124,7 @@ NAMESPACE_BEGIN(Grid); // // As a check of rational require \Phi^dag M_{EOFA} \Phi == eta^dag M^-1/2^dag M M^-1/2 eta = eta^dag eta // - virtual void refresh(const GaugeField& U, GridParallelRNG& pRNG) + virtual void refresh(const GaugeField& U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) { Lop.ImportGauge(U); Rop.ImportGauge(U); diff --git a/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRational.h b/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRational.h index 56dff94d..656e9b2f 100644 --- a/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRational.h +++ b/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRational.h @@ -1,4 +1,3 @@ - /************************************************************************************* Grid physics library, www.github.com/paboyle/Grid @@ -43,8 +42,7 @@ NAMESPACE_BEGIN(Grid); // template -class OneFlavourEvenOddRationalPseudoFermionAction - : public Action { +class OneFlavourEvenOddRationalPseudoFermionAction : public Action { public: INHERIT_IMPL_TYPES(Impl); @@ -103,7 +101,7 @@ public: return sstream.str(); } - virtual void refresh(const GaugeField &U, GridParallelRNG &pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG &pRNG) { // P(phi) = e^{- phi^dag (MpcdagMpc)^-1/2 phi} // = e^{- phi^dag (MpcdagMpc)^-1/4 (MpcdagMpc)^-1/4 phi} // Phi = MpcdagMpc^{1/4} eta @@ -156,7 +154,10 @@ public: msCG(Mpc, PhiOdd, Y); - if ( (rand()%param.BoundsCheckFreq)==0 ) { + auto grid = FermOp.FermionGrid(); + auto r=rand(); + grid->Broadcast(0,r); + if ( (r%param.BoundsCheckFreq)==0 ) { FermionField gauss(FermOp.FermionRedBlackGrid()); gauss = PhiOdd; HighBoundCheck(Mpc,gauss,param.hi); diff --git a/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRationalRatio.h b/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRationalRatio.h index e5f0b602..e968b8e4 100644 --- a/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRationalRatio.h +++ b/Grid/qcd/action/pseudofermion/OneFlavourEvenOddRationalRatio.h @@ -101,7 +101,7 @@ NAMESPACE_BEGIN(Grid); } - virtual void refresh(const GaugeField &U, GridParallelRNG& pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) { // S_f = chi^dag* P(V^dag*V)/Q(V^dag*V)* N(M^dag*M)/D(M^dag*M)* P(V^dag*V)/Q(V^dag*V)* chi // @@ -170,7 +170,10 @@ NAMESPACE_BEGIN(Grid); msCG_M(MdagM,X,Y); // Randomly apply rational bounds checks. - if ( (rand()%param.BoundsCheckFreq)==0 ) { + auto grid = NumOp.FermionGrid(); + auto r=rand(); + grid->Broadcast(0,r); + if ( (r%param.BoundsCheckFreq)==0 ) { FermionField gauss(NumOp.FermionRedBlackGrid()); gauss = PhiOdd; HighBoundCheck(MdagM,gauss,param.hi); diff --git a/Grid/qcd/action/pseudofermion/OneFlavourRational.h b/Grid/qcd/action/pseudofermion/OneFlavourRational.h index f6c823c9..aa647445 100644 --- a/Grid/qcd/action/pseudofermion/OneFlavourRational.h +++ b/Grid/qcd/action/pseudofermion/OneFlavourRational.h @@ -98,7 +98,7 @@ NAMESPACE_BEGIN(Grid); - virtual void refresh(const GaugeField &U, GridParallelRNG& pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) { // P(phi) = e^{- phi^dag (MdagM)^-1/2 phi} @@ -142,7 +142,10 @@ NAMESPACE_BEGIN(Grid); msCG(MdagMOp,Phi,Y); - if ( (rand()%param.BoundsCheckFreq)==0 ) { + auto grid = FermOp.FermionGrid(); + auto r=rand(); + grid->Broadcast(0,r); + if ( (r%param.BoundsCheckFreq)==0 ) { FermionField gauss(FermOp.FermionGrid()); gauss = Phi; HighBoundCheck(MdagMOp,gauss,param.hi); diff --git a/Grid/qcd/action/pseudofermion/OneFlavourRationalRatio.h b/Grid/qcd/action/pseudofermion/OneFlavourRationalRatio.h index 5fae2fe9..128c869a 100644 --- a/Grid/qcd/action/pseudofermion/OneFlavourRationalRatio.h +++ b/Grid/qcd/action/pseudofermion/OneFlavourRationalRatio.h @@ -95,7 +95,7 @@ NAMESPACE_BEGIN(Grid); } - virtual void refresh(const GaugeField &U, GridParallelRNG& pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) { // S_f = chi^dag* P(V^dag*V)/Q(V^dag*V)* N(M^dag*M)/D(M^dag*M)* P(V^dag*V)/Q(V^dag*V)* chi // @@ -156,7 +156,10 @@ NAMESPACE_BEGIN(Grid); msCG_M(MdagM,X,Y); // Randomly apply rational bounds checks. - if ( (rand()%param.BoundsCheckFreq)==0 ) { + auto grid = NumOp.FermionGrid(); + auto r=rand(); + grid->Broadcast(0,r); + if ( (r%param.BoundsCheckFreq)==0 ) { FermionField gauss(NumOp.FermionGrid()); gauss = Phi; HighBoundCheck(MdagM,gauss,param.hi); diff --git a/Grid/qcd/action/pseudofermion/TwoFlavour.h b/Grid/qcd/action/pseudofermion/TwoFlavour.h index f905a675..2ac97ddd 100644 --- a/Grid/qcd/action/pseudofermion/TwoFlavour.h +++ b/Grid/qcd/action/pseudofermion/TwoFlavour.h @@ -73,7 +73,7 @@ public: ////////////////////////////////////////////////////////////////////////////////////// // Push the gauge field in to the dops. Assume any BC's and smearing already applied ////////////////////////////////////////////////////////////////////////////////////// - virtual void refresh(const GaugeField &U, GridParallelRNG &pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG &pRNG) { // P(phi) = e^{- phi^dag (MdagM)^-1 phi} // Phi = Mdag eta // P(eta) = e^{- eta^dag eta} diff --git a/Grid/qcd/action/pseudofermion/TwoFlavourEvenOdd.h b/Grid/qcd/action/pseudofermion/TwoFlavourEvenOdd.h index a3cf8f08..2e5208a8 100644 --- a/Grid/qcd/action/pseudofermion/TwoFlavourEvenOdd.h +++ b/Grid/qcd/action/pseudofermion/TwoFlavourEvenOdd.h @@ -77,7 +77,7 @@ public: ////////////////////////////////////////////////////////////////////////////////////// // Push the gauge field in to the dops. Assume any BC's and smearing already applied ////////////////////////////////////////////////////////////////////////////////////// - virtual void refresh(const GaugeField &U, GridParallelRNG& pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) { // P(phi) = e^{- phi^dag (MpcdagMpc)^-1 phi} // Phi = McpDag eta diff --git a/Grid/qcd/action/pseudofermion/TwoFlavourEvenOddRatio.h b/Grid/qcd/action/pseudofermion/TwoFlavourEvenOddRatio.h index d1d6f336..da628c75 100644 --- a/Grid/qcd/action/pseudofermion/TwoFlavourEvenOddRatio.h +++ b/Grid/qcd/action/pseudofermion/TwoFlavourEvenOddRatio.h @@ -84,7 +84,7 @@ NAMESPACE_BEGIN(Grid); } - virtual void refresh(const GaugeField &U, GridParallelRNG& pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) { // P(phi) = e^{- phi^dag Vpc (MpcdagMpc)^-1 Vpcdag phi} // diff --git a/Grid/qcd/action/pseudofermion/TwoFlavourRatio.h b/Grid/qcd/action/pseudofermion/TwoFlavourRatio.h index 4d72faba..f584706d 100644 --- a/Grid/qcd/action/pseudofermion/TwoFlavourRatio.h +++ b/Grid/qcd/action/pseudofermion/TwoFlavourRatio.h @@ -64,7 +64,7 @@ public: return sstream.str(); } - virtual void refresh(const GaugeField &U, GridParallelRNG& pRNG) { + virtual void refresh(const GaugeField &U, GridSerialRNG &sRNG, GridParallelRNG& pRNG) { // P(phi) = e^{- phi^dag V (MdagM)^-1 Vdag phi} // diff --git a/Grid/qcd/action/scalar/ScalarAction.h b/Grid/qcd/action/scalar/ScalarAction.h index 34fc4fac..8b4f4f79 100644 --- a/Grid/qcd/action/scalar/ScalarAction.h +++ b/Grid/qcd/action/scalar/ScalarAction.h @@ -55,7 +55,7 @@ public: } virtual std::string action_name() {return "ScalarAction";} - virtual void refresh(const Field &U, GridParallelRNG &pRNG) {} // noop as no pseudoferms + virtual void refresh(const Field &U, GridSerialRNG &sRNG, GridParallelRNG &pRNG) {} // noop as no pseudoferms virtual RealD S(const Field &p) { return (mass_square * 0.5 + Nd) * ScalarObs::sumphisquared(p) + diff --git a/Grid/qcd/action/scalar/ScalarImpl.h b/Grid/qcd/action/scalar/ScalarImpl.h index 403ea573..13bd6c90 100644 --- a/Grid/qcd/action/scalar/ScalarImpl.h +++ b/Grid/qcd/action/scalar/ScalarImpl.h @@ -27,7 +27,7 @@ public: typedef Field FermionField; typedef Field PropagatorField; - static inline void generate_momenta(Field& P, GridParallelRNG& pRNG){ + static inline void generate_momenta(Field& P, GridSerialRNG &sRNG, GridParallelRNG& pRNG){ RealD scale = ::sqrt(HMC_MOMENTUM_DENOMINATOR); // CPS/UKQCD momentum rescaling gaussian(pRNG, P); P *= scale; @@ -151,7 +151,7 @@ public: out = one / out; } - static inline void generate_momenta(Field &P, GridParallelRNG &pRNG) + static inline void generate_momenta(Field &P, GridSerialRNG & sRNG, GridParallelRNG &pRNG) { RealD scale = ::sqrt(HMC_MOMENTUM_DENOMINATOR); // CPS/UKQCD momentum rescaling #ifndef USE_FFT_ACCELERATION diff --git a/Grid/qcd/action/scalar/ScalarInteractionAction.h b/Grid/qcd/action/scalar/ScalarInteractionAction.h index 5a5f9251..e04dd486 100644 --- a/Grid/qcd/action/scalar/ScalarInteractionAction.h +++ b/Grid/qcd/action/scalar/ScalarInteractionAction.h @@ -77,7 +77,7 @@ public: virtual std::string action_name() { return "ScalarAction"; } - virtual void refresh(const Field &U, GridParallelRNG &pRNG) {} + virtual void refresh(const Field &U, GridSerialRNG & sRNG, GridParallelRNG &pRNG) {} virtual RealD S(const Field &p) { diff --git a/Grid/qcd/hmc/HMC.h b/Grid/qcd/hmc/HMC.h index f168b69a..44674ea5 100644 --- a/Grid/qcd/hmc/HMC.h +++ b/Grid/qcd/hmc/HMC.h @@ -139,7 +139,7 @@ private: // Evolution ///////////////////////////////////////////////////////// RealD evolve_hmc_step(Field &U) { - TheIntegrator.refresh(U, pRNG); // set U and initialize P and phi's + TheIntegrator.refresh(U, sRNG, pRNG); // set U and initialize P and phi's RealD H0 = TheIntegrator.S(U); // initial state action diff --git a/Grid/qcd/hmc/integrators/Integrator.h b/Grid/qcd/hmc/integrators/Integrator.h index 77b7de52..aa28c6c8 100644 --- a/Grid/qcd/hmc/integrators/Integrator.h +++ b/Grid/qcd/hmc/integrators/Integrator.h @@ -236,10 +236,9 @@ public: // over the representations struct _refresh { template - void operator()(std::vector*> repr_set, Repr& Rep, - GridParallelRNG& pRNG) { + void operator()(std::vector*> repr_set, Repr& Rep, GridSerialRNG & sRNG, GridParallelRNG& pRNG) { for (int a = 0; a < repr_set.size(); ++a){ - repr_set.at(a)->refresh(Rep.U, pRNG); + repr_set.at(a)->refresh(Rep.U, sRNG, pRNG); std::cout << GridLogDebug << "Hirep refreshing pseudofermions" << std::endl; } @@ -247,12 +246,12 @@ public: } refresh_hireps{}; // Initialization of momenta and actions - void refresh(Field& U, GridParallelRNG& pRNG) + void refresh(Field& U, GridSerialRNG & sRNG, GridParallelRNG& pRNG) { assert(P.Grid() == U.Grid()); std::cout << GridLogIntegrator << "Integrator refresh\n"; - FieldImplementation::generate_momenta(P, pRNG); + FieldImplementation::generate_momenta(P, sRNG, pRNG); // Update the smeared fields, can be implemented as observer // necessary to keep the fields updated even after a reject @@ -269,11 +268,11 @@ public: // get gauge field from the SmearingPolicy and // based on the boolean is_smeared in actionID Field& Us = Smearer.get_U(as[level].actions.at(actionID)->is_smeared); - as[level].actions.at(actionID)->refresh(Us, pRNG); + as[level].actions.at(actionID)->refresh(Us, sRNG, pRNG); } // Refresh the higher representation actions - as[level].apply(refresh_hireps, Representations, pRNG); + as[level].apply(refresh_hireps, Representations, sRNG, pRNG); } MomFilter->applyFilter(P); diff --git a/benchmarks/Benchmark_dwf_fp32.cc b/benchmarks/Benchmark_dwf_fp32.cc index cb86177e..03f3ee61 100644 --- a/benchmarks/Benchmark_dwf_fp32.cc +++ b/benchmarks/Benchmark_dwf_fp32.cc @@ -53,7 +53,7 @@ int main (int argc, char ** argv) int threads = GridThread::GetThreads(); Coordinate latt4 = GridDefaultLatt(); - int Ls=8; + int Ls=16; for(int i=0;i> Ls; From d4b4de8f428c625757c31c6d265c288671a0ef5b Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 4 Mar 2021 20:01:24 +0000 Subject: [PATCH 109/399] changes --- Grid/qcd/utils/BaryonUtils.h | 60 +++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index b74a5b20..1a7a4d38 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -252,7 +252,7 @@ public: const Gamma GammaB_sigma, const Gamma GammaB_nucl, const std::string op, - SpinMatrixField &stn_corr); + SpinMatrixField &xts_corr); }; //This computes a baryon contraction on a lattice site, including the spin-trace of the correlation matrix template @@ -1435,6 +1435,7 @@ void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, int c_x = (ie_x < 3 ? (ie_x+2)%3 : (7-ie_x)%3 ); //epsilon[ie_x][2]; //c' int eSgn_x = (ie_x < 3 ? 1 : -1); ee = Real(eSgn_s * eSgn_x); + auto ee_GD = ee * trGDq; for (int alpha_x=0; alpha_x::XiToSigmaQ2EyeSite(const mobj &Dq_loop, // GammaB * DsGDd * GammaB auto GDsGDqGDdG = GDsGDqGDd * GammaB_sigma; + Real ee; + for (int ie_s=0; ie_s < 6 ; ie_s++){ - int a_s = epsilon[ie_s][0]; //a - int b_s = epsilon[ie_s][1]; //b - int c_s = epsilon[ie_s][2]; //c + int a_s = (ie_s < 3 ? ie_s : (6-ie_s)%3 ); //epsilon[ie_s][0]; //a' + int b_s = (ie_s < 3 ? (ie_s+1)%3 : (8-ie_s)%3 ); //epsilon[ie_s][1]; //b' + int c_s = (ie_s < 3 ? (ie_s+2)%3 : (7-ie_s)%3 ); //epsilon[ie_s][2]; //c' + int eSgn_s = (ie_s < 3 ? 1 : -1); for (int ie_x=0; ie_x < 6 ; ie_x++){ - int a_x = epsilon[ie_x][0]; //a' - int b_x = epsilon[ie_x][1]; //b' - int c_x = epsilon[ie_x][2]; //c' - auto ee = epsilon_sgn[ie_s] * epsilon_sgn[ie_x]; + int a_x = (ie_x < 3 ? ie_x : (6-ie_x)%3 ); //epsilon[ie_x][0]; //a' + int b_x = (ie_x < 3 ? (ie_x+1)%3 : (8-ie_x)%3 ); //epsilon[ie_x][1]; //b' + int c_x = (ie_x < 3 ? (ie_x+2)%3 : (7-ie_x)%3 ); //epsilon[ie_x][2]; //c' + int eSgn_x = (ie_x < 3 ? 1 : -1); + ee = Real(eSgn_s * eSgn_x); for (int alpha_x=0; alpha_x::XiToSigmaEye(const PropagatorField &qq_loop, autoView( vd_tf , qd_tf , AcceleratorRead); autoView( vs_ti , qs_ti , AcceleratorRead); - bool doQ1 = (op == "Q1"); - bool doQ2 = (op == "Q2"); - Vector my_Dq_spec{Dd_spec,Ds_spec}; mobj * Dq_spec_p = &my_Dq_spec[0]; - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - auto Dq_loop = vq_loop(ss); - auto Dd_tf = vd_tf(ss); - auto Ds_ti = vs_ti(ss); - typedef decltype(coalescedRead(vcorr[0])) spinor; - spinor result=Zero(); - if(doQ1){ + if(op == "Q1"){ + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_loop = vq_loop(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); XiToSigmaQ1EyeSite(Dq_loop,Dq_spec_p[0],Dq_spec_p[1],Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); - } else if(doQ2){ - XiToSigmaQ2EyeSite(Dq_loop,Dq_spec_p[0],Dq_spec_p[0],Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); - } else { - assert(0 && "Weak Operator not correctly specified"); - } - coalescedWrite(vcorr[ss],result); - } );//end loop over lattice sites + coalescedWrite(vcorr[ss],result); + } );//end loop over lattice sites + } else if(op == "Q2"){ + accelerator_for(ss, grid->oSites(), grid->Nsimd(), { + auto Dq_loop = vq_loop(ss); + auto Dd_tf = vd_tf(ss); + auto Ds_ti = vs_ti(ss); + typedef decltype(coalescedRead(vcorr[0])) spinor; + spinor result=Zero(); + XiToSigmaQ2EyeSite(Dq_loop,Dq_spec_p[0],Dq_spec_p[1],Dd_tf,Ds_ti,Gamma_H,GammaB_xi,GammaB_sigma,result); + coalescedWrite(vcorr[ss],result); + } );//end loop over lattice sites + } else { + assert(0 && "Weak Operator not correctly specified"); + } } From 9b15704290048b936e71c3ddd652a6eec9bf5fff Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 5 Mar 2021 10:42:32 +0000 Subject: [PATCH 110/399] tested and consitent --- Grid/qcd/utils/BaryonUtils.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 1a7a4d38..b69865e8 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -27,7 +27,6 @@ *************************************************************************************/ /* END LEGAL */ #pragma once -//#include #include NAMESPACE_BEGIN(Grid); @@ -200,7 +199,7 @@ public: const Gamma GammaB_sigma, const Gamma GammaB_nucl, robj &result); - template + template accelerator_inline static void XiToSigmaQ1EyeSite(const mobj &Dq_loop, const mobj2 &Dd_spec, const mobj2 &Ds_spec, @@ -210,7 +209,7 @@ public: const Gamma GammaB_sigma, const Gamma GammaB_nucl, robj &result); - template + template accelerator_inline static void XiToSigmaQ2EyeSite(const mobj &Dq_loop, const mobj2 &Dd_spec, const mobj2 &Ds_spec, @@ -1395,7 +1394,7 @@ void BaryonUtils::SigmaToNucleonNonEye(const PropagatorField &qq_ti, * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ template -template +template accelerator_inline void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, const mobj2 &Dd_spec, const mobj2 &Ds_spec, @@ -1464,7 +1463,7 @@ void BaryonUtils::XiToSigmaQ1EyeSite(const mobj &Dq_loop, * Dd_tf is a quark line from t_f to t_H * Ds_ti is a quark line from t_i to t_H */ template -template +template accelerator_inline void BaryonUtils::XiToSigmaQ2EyeSite(const mobj &Dq_loop, const mobj2 &Dd_spec, const mobj2 &Ds_spec, From 7a19432e0b77fff8943ad6701f448ab366dab27f Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 5 Mar 2021 10:57:09 +0000 Subject: [PATCH 111/399] whitespace --- Grid/qcd/utils/BaryonUtils.h | 84 ++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index b69865e8..9d9cb508 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -191,34 +191,34 @@ public: robj &result); template accelerator_inline static void SigmaToNucleonQ2NonEyeSite(const mobj &Du_ti, - const mobj &Du_tf, - const mobj2 &Du_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result); + const mobj &Du_tf, + const mobj2 &Du_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); template accelerator_inline static void XiToSigmaQ1EyeSite(const mobj &Dq_loop, - const mobj2 &Dd_spec, - const mobj2 &Ds_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result); + const mobj2 &Dd_spec, + const mobj2 &Ds_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); template accelerator_inline static void XiToSigmaQ2EyeSite(const mobj &Dq_loop, - const mobj2 &Dd_spec, - const mobj2 &Ds_spec, - const mobj &Dd_tf, - const mobj &Ds_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - robj &result); + const mobj2 &Dd_spec, + const mobj2 &Ds_spec, + const mobj &Dd_tf, + const mobj &Ds_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + robj &result); public: template static void SigmaToNucleonEye(const PropagatorField &qq_loop, @@ -232,26 +232,26 @@ public: SpinMatrixField &stn_corr); template static void SigmaToNucleonNonEye(const PropagatorField &qq_ti, - const PropagatorField &qq_tf, - const mobj &Du_spec, - const PropagatorField &qd_tf, - const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - const std::string op, - SpinMatrixField &stn_corr); + const PropagatorField &qq_tf, + const mobj &Du_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + const std::string op, + SpinMatrixField &stn_corr); template static void XiToSigmaEye(const PropagatorField &qq_loop, - const mobj &Dd_spec, - const mobj &Ds_spec, - const PropagatorField &qd_tf, - const PropagatorField &qs_ti, - const Gamma Gamma_H, - const Gamma GammaB_sigma, - const Gamma GammaB_nucl, - const std::string op, - SpinMatrixField &xts_corr); + const mobj &Dd_spec, + const mobj &Ds_spec, + const PropagatorField &qd_tf, + const PropagatorField &qs_ti, + const Gamma Gamma_H, + const Gamma GammaB_sigma, + const Gamma GammaB_nucl, + const std::string op, + SpinMatrixField &xts_corr); }; //This computes a baryon contraction on a lattice site, including the spin-trace of the correlation matrix template From aa173e29989b93445cc28655dd7d0feadb746961 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 5 Mar 2021 10:25:33 -0500 Subject: [PATCH 112/399] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4cbae720..fff68dc6 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,6 @@ If you want to build all the tests at once just use `make tests`. - `--enable-numa`: enable NUMA first touch optimisation - `--enable-simd=`: setup Grid for the SIMD target `` (default: `GEN`). A list of possible SIMD targets is detailed in a section below. - `--enable-gen-simd-width=`: select the size (in bytes) of the generic SIMD vector type (default: 32 bytes). -- `--enable-precision={single|double}`: set the default precision (default: `double`). **Deprecated option** - `--enable-comms=`: Use `` for message passing (default: `none`). A list of possible SIMD targets is detailed in a section below. - `--enable-rng={sitmo|ranlux48|mt19937}`: choose the RNG (default: `sitmo `). - `--disable-timers`: disable system dependent high-resolution timers. From b24181aa4f21a59e601237947c5ea465d0e1ecaf Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Fri, 5 Mar 2021 16:56:58 +0100 Subject: [PATCH 113/399] Update Coordinate.h Revert GRID_MAX_SIMD change --- Grid/util/Coordinate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/util/Coordinate.h b/Grid/util/Coordinate.h index 89f73264..004fbc72 100644 --- a/Grid/util/Coordinate.h +++ b/Grid/util/Coordinate.h @@ -88,7 +88,7 @@ public: // Coordinate class, maxdims = 8 for now. //////////////////////////////////////////////////////////////// #define GRID_MAX_LATTICE_DIMENSION (8) -#define GRID_MAX_SIMD (sizeof(vInteger)/sizeof(Integer)) +#define GRID_MAX_SIMD (16) static constexpr int MaxDims = GRID_MAX_LATTICE_DIMENSION; From 9e5fb52eb922e5e90086d3d55f022945ec43fb12 Mon Sep 17 00:00:00 2001 From: Thomas Wurm Date: Mon, 8 Mar 2021 13:53:34 +0100 Subject: [PATCH 114/399] Put GlobalSum outside the slice loop --- Grid/lattice/Lattice_reduction.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Grid/lattice/Lattice_reduction.h b/Grid/lattice/Lattice_reduction.h index 0a5fbcb6..326b9ea3 100644 --- a/Grid/lattice/Lattice_reduction.h +++ b/Grid/lattice/Lattice_reduction.h @@ -361,6 +361,7 @@ template inline void sliceSum(const Lattice &Data,std::vector< // But easily avoided by using double precision fields /////////////////////////////////////////////////////// typedef typename vobj::scalar_object sobj; + typedef typename vobj::scalar_object::scalar_type scalar_type; GridBase *grid = Data.Grid(); assert(grid!=NULL); @@ -419,20 +420,19 @@ template inline void sliceSum(const Lattice &Data,std::vector< } // sum over nodes. - sobj gsum; for(int t=0;t_processor_coor[orthogdim] ) { - gsum=lsSum[lt]; + result[t]=lsSum[lt]; } else { - gsum=Zero(); + result[t]=Zero(); } - grid->GlobalSum(gsum); - - result[t]=gsum; } + scalar_type * ptr = (scalar_type *) &result[0]; + int words = fd*sizeof(sobj)/sizeof(scalar_type); + grid->GlobalSumVector(ptr, words); } template From a76cb005e007bf887e0573175f9e9806fecc61b1 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 8 Mar 2021 13:37:57 -0500 Subject: [PATCH 115/399] Update Tensor_exp.h --- Grid/tensors/Tensor_exp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/tensors/Tensor_exp.h b/Grid/tensors/Tensor_exp.h index 0a1d6389..1f637d5f 100644 --- a/Grid/tensors/Tensor_exp.h +++ b/Grid/tensors/Tensor_exp.h @@ -28,7 +28,7 @@ Author: neo #ifndef GRID_MATH_EXP_H #define GRID_MATH_EXP_H -#define DEFAULT_MAT_EXP 12 +#define DEFAULT_MAT_EXP 20 NAMESPACE_BEGIN(Grid); From 4d1ea15c79d30c961272d5020404395db44365dc Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 9 Mar 2021 04:29:37 +0100 Subject: [PATCH 116/399] More verbosity. The 16bit limit on Grid.y, Grid.z is annoying --- Grid/threads/Accelerator.cc | 3 +-- Grid/threads/Accelerator.h | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 4bf7f395..9d9d851c 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -53,7 +53,6 @@ void acceleratorInit(void) prop = gpu_props[i]; totalDeviceMem = prop.totalGlobalMem; if ( world_rank == 0) { -#ifndef GRID_DEFAULT_GPU if ( i==rank ) { printf("AcceleratorCudaInit[%d]: ========================\n",rank); printf("AcceleratorCudaInit[%d]: Device Number : %d\n", rank,i); @@ -67,8 +66,8 @@ void acceleratorInit(void) GPU_PROP(warpSize); GPU_PROP(pciBusID); GPU_PROP(pciDeviceID); + printf("AcceleratorCudaInit[%d]: maxGridSize (%d,%d,%d)\n",rank,prop.maxGridSize[0],prop.maxGridSize[1],prop.maxGridSize[2]); } -#endif // GPU_PROP(unifiedAddressing); // GPU_PROP(l2CacheSize); // GPU_PROP(singleToDoublePrecisionPerfRatio); diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index f1a694fb..56b85c72 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -178,9 +178,10 @@ void Lambda6Apply(uint64_t num1, uint64_t num2, uint64_t num3, cudaDeviceSynchronize(); \ cudaError err = cudaGetLastError(); \ if ( cudaSuccess != err ) { \ - printf("Cuda error %s \n", cudaGetErrorString( err )); \ - puts(__FILE__); \ - printf("Line %d\n",__LINE__); \ + printf("accelerator_barrier(): Cuda error %s \n", \ + cudaGetErrorString( err )); \ + printf("File %s Line %d\n",__FILE__,__LINE__); \ + fflush(stdout); \ if (acceleratorAbortOnGpuError) assert(err==cudaSuccess); \ } \ } From 6a429ee6d3eee71689f650782decb8948bf6fa77 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 9 Mar 2021 04:31:10 +0100 Subject: [PATCH 117/399] 2d loop hits Nvidia 16bit limit on large local vols --- Grid/cshift/Cshift_common.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Grid/cshift/Cshift_common.h b/Grid/cshift/Cshift_common.h index f2f39815..cf902b58 100644 --- a/Grid/cshift/Cshift_common.h +++ b/Grid/cshift/Cshift_common.h @@ -110,9 +110,11 @@ Gather_plane_extract(const Lattice &rhs, int n1=rhs.Grid()->_slice_stride[dimension]; if ( cbmask ==0x3){ -#ifdef ACCELERATOR_CSHIFT +#ifdef ACCELERATOR_CSHIFT autoView(rhs_v , rhs, AcceleratorRead); - accelerator_for2d(n,e1,b,e2,1,{ + accelerator_for(nn,e1*e2,1,{ + int n = nn%e1; + int b = nn/e1; int o = n*n1; int offset = b+n*e2; @@ -135,7 +137,9 @@ Gather_plane_extract(const Lattice &rhs, std::cout << " Dense packed buffer WARNING " < void Scatter_plane_merge(Lattice &rhs,ExtractPointerA int _slice_block = rhs.Grid()->_slice_block[dimension]; #ifdef ACCELERATOR_CSHIFT autoView( rhs_v , rhs, AcceleratorWrite); - accelerator_for2d(n,e1,b,e2,1,{ + accelerator_for(nn,e1*e2,1,{ + int n = nn%e1; + int b = nn/e1; int o = n*_slice_stride; int offset = b+n*_slice_block; merge(rhs_v[so+o+b],pointers,offset); @@ -274,7 +280,7 @@ template void Scatter_plane_merge(Lattice &rhs,ExtractPointerA // Case of SIMD split AND checker dim cannot currently be hit, except in // Test_cshift_red_black code. - // std::cout << "Scatter_plane merge assert(0); think this is buggy FIXME "<< std::endl;// think this is buggy FIXME + std::cout << "Scatter_plane merge assert(0); think this is buggy FIXME "<< std::endl;// think this is buggy FIXME std::cout<<" Unthreaded warning -- buffer is not densely packed ??"< Date: Wed, 10 Mar 2021 02:45:22 +0100 Subject: [PATCH 118/399] Clean up test --- tests/core/Test_where.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/core/Test_where.cc b/tests/core/Test_where.cc index 050b711b..deb29865 100644 --- a/tests/core/Test_where.cc +++ b/tests/core/Test_where.cc @@ -40,9 +40,9 @@ int main (int argc, char ** argv) int N=16; - std::vector latt_size ({N,4,4}); - std::vector simd_layout({vComplexD::Nsimd(),1,1}); - std::vector mpi_layout ({1,1,1}); + std::vector latt_size ({N,N,N,N}); + std::vector simd_layout({vComplexD::Nsimd(),1,1,1}); + std::vector mpi_layout ({1,1,1,1}); int vol = 1; int nd = latt_size.size(); @@ -69,7 +69,7 @@ int main (int argc, char ** argv) for(int t=0;t Date: Wed, 10 Mar 2021 05:40:51 -0800 Subject: [PATCH 119/399] Gives 200GF/s on SyCL/DG1 8^4, doesn't uglify develop for other platforms too badly. Easy to revert to clean more C++ stylistic code. Theres a SYCL_HACK macro I will clean up later once dpcpp evolves a central nervous systems. --- Grid/qcd/action/fermion/WilsonKernels.h | 10 +- .../WilsonKernelsHandImplementation.h | 99 ++++++++++++++++--- .../WilsonKernelsImplementation.h | 20 +++- 3 files changed, 115 insertions(+), 14 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonKernels.h b/Grid/qcd/action/fermion/WilsonKernels.h index 1bac9211..68422f28 100644 --- a/Grid/qcd/action/fermion/WilsonKernels.h +++ b/Grid/qcd/action/fermion/WilsonKernels.h @@ -49,9 +49,17 @@ public: INHERIT_IMPL_TYPES(Impl); typedef FermionOperator Base; - + typedef AcceleratorVector StencilVector; public: +#ifdef GRID_SYCL +#define SYCL_HACK +#endif +#ifdef SYCL_HACK + static void HandDhopSiteSycl(StencilVector st_perm,StencilEntry *st_p, SiteDoubledGaugeField *U,SiteHalfSpinor *buf, + int ss,int sU,const SiteSpinor *in, SiteSpinor *out); +#endif + static void DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField &U, SiteHalfSpinor * buf, int Ls, int Nsite, const FermionField &in, FermionField &out, int interior=1,int exterior=1) ; diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h index 688cb75a..fb42fe88 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h @@ -118,15 +118,6 @@ Author: paboyle #endif -#define LOAD_CHI \ - {const SiteHalfSpinor &ref(buf[offset]); \ - Chi_00 = coalescedRead(ref()(0)(0)); \ - Chi_01 = coalescedRead(ref()(0)(1)); \ - Chi_02 = coalescedRead(ref()(0)(2)); \ - Chi_10 = coalescedRead(ref()(1)(0)); \ - Chi_11 = coalescedRead(ref()(1)(1)); \ - Chi_12 = coalescedRead(ref()(1)(2));} - #define MULT_2SPIN(A)\ {auto & ref(U[sU](A)); \ U_00=coalescedRead(ref()(0,0)); \ @@ -157,6 +148,15 @@ Author: paboyle UChi_02+= U_20*Chi_02; \ UChi_12+= U_20*Chi_12;} +#define LOAD_CHI \ + {const SiteHalfSpinor &ref(buf[offset]); \ + Chi_00 = coalescedRead(ref()(0)(0)); \ + Chi_01 = coalescedRead(ref()(0)(1)); \ + Chi_02 = coalescedRead(ref()(0)(2)); \ + Chi_10 = coalescedRead(ref()(1)(0)); \ + Chi_11 = coalescedRead(ref()(1)(1)); \ + Chi_12 = coalescedRead(ref()(1)(2));} + // hspin(0)=fspin(0)+timesI(fspin(3)); // hspin(1)=fspin(1)+timesI(fspin(2)); #define XP_PROJ \ @@ -370,7 +370,7 @@ Author: paboyle result_31-= UChi_11; \ result_32-= UChi_12; -#define HAND_STENCIL_LEG(PROJ,PERM,DIR,RECON) \ +#define HAND_STENCIL_LEGB(PROJ,PERM,DIR,RECON) \ SE=st.GetEntry(ptype,DIR,ss); \ offset = SE->_offset; \ local = SE->_is_local; \ @@ -384,6 +384,37 @@ Author: paboyle } else { \ LOAD_CHI; \ } \ + acceleratorSynchronise(); \ + MULT_2SPIN(DIR); \ + RECON; + +#define HAND_STENCIL_LEG(PROJ,PERM,DIR,RECON) \ + SE=&st_p[DIR+8*ss]; \ + ptype=st_perm[DIR]; \ + offset = SE->_offset; \ + local = SE->_is_local; \ + perm = SE->_permute; \ + if ( local ) { \ + LOAD_CHIMU(PERM); \ + PROJ; \ + if ( perm) { \ + PERMUTE_DIR(PERM); \ + } \ + } else { \ + LOAD_CHI; \ + } \ + acceleratorSynchronise(); \ + MULT_2SPIN(DIR); \ + RECON; + +#define HAND_STENCIL_LEGA(PROJ,PERM,DIR,RECON) \ + SE=&st_p[DIR+8*ss]; \ + ptype=st_perm[DIR]; \ + /*SE=st.GetEntry(ptype,DIR,ss);*/ \ + offset = SE->_offset; \ + perm = SE->_permute; \ + LOAD_CHIMU(PERM); \ + PROJ; \ MULT_2SPIN(DIR); \ RECON; @@ -401,10 +432,12 @@ Author: paboyle } else if ( st.same_node[DIR] ) { \ LOAD_CHI; \ } \ + acceleratorSynchronise(); \ if (local || st.same_node[DIR] ) { \ MULT_2SPIN(DIR); \ RECON; \ - } + } \ + acceleratorSynchronise(); #define HAND_STENCIL_LEG_EXT(PROJ,PERM,DIR,RECON) \ SE=st.GetEntry(ptype,DIR,ss); \ @@ -414,7 +447,8 @@ Author: paboyle MULT_2SPIN(DIR); \ RECON; \ nmu++; \ - } + } \ + acceleratorSynchronise(); #define HAND_RESULT(ss) \ { \ @@ -511,10 +545,41 @@ Author: paboyle NAMESPACE_BEGIN(Grid); + +#ifdef SYCL_HACK +template accelerator_inline void +WilsonKernels::HandDhopSiteSycl(StencilVector st_perm,StencilEntry *st_p, SiteDoubledGaugeField *U,SiteHalfSpinor *buf, + int ss,int sU,const SiteSpinor *in, SiteSpinor *out) +{ +// T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... + typedef typename Simd::scalar_type S; + typedef typename Simd::vector_type V; + typedef iSinglet vCplx; + // typedef decltype( coalescedRead( vCplx()()() )) Simt; + typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + + HAND_DECLARATIONS(Simt); + + int offset,local,perm, ptype; + StencilEntry *SE; + HAND_STENCIL_LEG(XM_PROJ,3,Xp,XM_RECON); + HAND_STENCIL_LEG(YM_PROJ,2,Yp,YM_RECON_ACCUM); + HAND_STENCIL_LEG(ZM_PROJ,1,Zp,ZM_RECON_ACCUM); + HAND_STENCIL_LEG(TM_PROJ,0,Tp,TM_RECON_ACCUM); + HAND_STENCIL_LEG(XP_PROJ,3,Xm,XP_RECON_ACCUM); + HAND_STENCIL_LEG(YP_PROJ,2,Ym,YP_RECON_ACCUM); + HAND_STENCIL_LEG(ZP_PROJ,1,Zm,ZP_RECON_ACCUM); + HAND_STENCIL_LEG(TP_PROJ,0,Tm,TP_RECON_ACCUM); + HAND_RESULT(ss); +} +#endif + template accelerator_inline void WilsonKernels::HandDhopSite(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { + auto st_p = st._entries_p; + auto st_perm = st._permute_type; // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; @@ -539,6 +604,8 @@ template accelerator_inline void WilsonKernels::HandDhopSiteDag(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { + auto st_p = st._entries_p; + auto st_perm = st._permute_type; typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; @@ -562,6 +629,8 @@ template accelerator_inline void WilsonKernels::HandDhopSiteInt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { + auto st_p = st._entries_p; + auto st_perm = st._permute_type; // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; @@ -586,6 +655,8 @@ template accelerator_inline void WilsonKernels::HandDhopSiteDagInt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { + auto st_p = st._entries_p; + auto st_perm = st._permute_type; typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; @@ -609,6 +680,8 @@ template accelerator_inline void WilsonKernels::HandDhopSiteExt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { + auto st_p = st._entries_p; + auto st_perm = st._permute_type; // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; @@ -634,6 +707,8 @@ template accelerator_inline void WilsonKernels::HandDhopSiteDagExt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { + auto st_p = st._entries_p; + auto st_perm = st._permute_type; typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h index 937d13af..9228b84c 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h @@ -416,7 +416,21 @@ void WilsonKernels::DhopDirKernel( StencilImpl &st, DoubledGaugeField &U,S #undef LoopBody } -#define KERNEL_CALLNB(A) \ +#define KERNEL_CALL_TMP(A) \ + const uint64_t NN = Nsite*Ls; \ + auto U_p = & U_v[0]; \ + auto in_p = & in_v[0]; \ + auto out_p = & out_v[0]; \ + auto st_p = st_v._entries_p; \ + auto st_perm = st_v._permute_type; \ + accelerator_forNB( ss, NN, Simd::Nsimd(), { \ + int sF = ss; \ + int sU = ss/Ls; \ + WilsonKernels::A(st_perm,st_p,U_p,buf,sF,sU,in_p,out_p); \ + }); \ + accelerator_barrier(); + +#define KERNEL_CALLNB(A) \ const uint64_t NN = Nsite*Ls; \ accelerator_forNB( ss, NN, Simd::Nsimd(), { \ int sF = ss; \ @@ -445,7 +459,11 @@ void WilsonKernels::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField if( interior && exterior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSite); return;} +#ifdef SYCL_HACK + if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL_TMP(HandDhopSiteSycl); return; } +#else if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSite); return;} +#endif #ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSite); return;} #endif From f786ff8d69534e7885a55a7090a26b4d28619209 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 10 Mar 2021 14:32:06 -0500 Subject: [PATCH 120/399] Extend test from Fionn, fails on A100 apparently --- tests/core/Test_where_extended.cc | 137 ++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/core/Test_where_extended.cc diff --git a/tests/core/Test_where_extended.cc b/tests/core/Test_where_extended.cc new file mode 100644 index 00000000..706fd7ee --- /dev/null +++ b/tests/core/Test_where_extended.cc @@ -0,0 +1,137 @@ + /************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/Test_poisson_fft.cc + + Copyright (C) 2015 + +Author: Azusa Yamaguchi +Author: Peter Boyle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include + +using namespace Grid; + ; + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + int threads = GridThread::GetThreads(); + std::cout< latt_size ({N,4,4}); + std::vector simd_layout({vComplexD::Nsimd(),1,1}); + std::vector mpi_layout ({1,1,1}); + + int vol = 1; + int nd = latt_size.size(); + for(int d=0;d({45,12,81,9})); + gaussian(RNG,rn); + + RealD nn=norm2(rn); + for(int mu=0;mu seeds4({1,2,3,4}); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + LatticeInteger lcoor(UGrid); LatticeCoordinate(lcoor,Nd-1); + + + std::cout<=tmin),tmpF,ZZF); + nA = nA + norm2(tmp2F); + InsertSlice(tmp2F, q_outF, s , 0); + } + + RealD nQO=norm2(q_outF); + std::cout <=tmin),tmpP,ZZP); + nA = nA + norm2(tmp2P); + InsertSlice(tmp2P, q_outP, s , 0); + } + + nQO=norm2(q_outP); + std::cout < Date: Thu, 11 Mar 2021 12:58:49 +0100 Subject: [PATCH 121/399] Fix inconsistent configure option AVX512 Before this change AVX512 enabled different instruction sets depending on the compiler: For Intel C++ Compiler Classic (ICC): AVX512F, AVX512CD, AVX512DQ, AVX512BW, AVX512VL i.e. Intel Xeon Skylake and newer For Intel ICX, gcc, clang: AVX512F, AVX512CD, AVX512ER, AVX512PF i.e. Intel Xeon Phi x200/x205 (KNL/KNM) With this commit AVX512 now only enables the common instruction sets supported by all CPUs supporting any AVX-512 instructions set: AVX512F and AVX512CD (called COMMON-AVX512 by icc) --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 5f165412..afd5cbad 100644 --- a/configure.ac +++ b/configure.ac @@ -444,7 +444,7 @@ case ${ax_cv_cxx_compiler_vendor} in SIMD_FLAGS='-mavx2 -mfma -mf16c';; AVX512) AC_DEFINE([AVX512],[1],[AVX512 intrinsics]) - SIMD_FLAGS='-mavx512f -mavx512pf -mavx512er -mavx512cd';; + SIMD_FLAGS='-mavx512f -mavx512cd';; SKL) AC_DEFINE([AVX512],[1],[AVX512 intrinsics for SkyLake Xeon]) SIMD_FLAGS='-march=skylake-avx512';; @@ -498,7 +498,7 @@ case ${ax_cv_cxx_compiler_vendor} in SIMD_FLAGS='-march=core-avx2 -xcore-avx2';; AVX512) AC_DEFINE([AVX512],[1],[AVX512 intrinsics]) - SIMD_FLAGS='-xcore-avx512';; + SIMD_FLAGS='-xcommon-avx512';; KNC) AC_DEFINE([IMCI],[1],[IMCI Intrinsics for Knights Corner]) SIMD_FLAGS='';; From 82402c6a7cc3057dd0e4c2f6389ba2b347972cfc Mon Sep 17 00:00:00 2001 From: Peter Georg Date: Thu, 11 Mar 2021 13:08:40 +0100 Subject: [PATCH 122/399] Add simd option SKL for ICC --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index afd5cbad..4e5e33c8 100644 --- a/configure.ac +++ b/configure.ac @@ -499,6 +499,9 @@ case ${ax_cv_cxx_compiler_vendor} in AVX512) AC_DEFINE([AVX512],[1],[AVX512 intrinsics]) SIMD_FLAGS='-xcommon-avx512';; + SKL) + AC_DEFINE([AVX512],[1],[AVX512 intrinsics]) + SIMD_FLAGS='-xcore-avx512';; KNC) AC_DEFINE([IMCI],[1],[IMCI Intrinsics for Knights Corner]) SIMD_FLAGS='';; From ce1fc1f48aac5a48512b6ff70c1d85d0810d7623 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 11 Mar 2021 22:20:53 +0100 Subject: [PATCH 123/399] Possible fallback plan for Fionn's compiler bbug in nvcc --- Grid/lattice/Lattice_where.h | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Grid/lattice/Lattice_where.h b/Grid/lattice/Lattice_where.h index 6686d1b3..e9a3f823 100644 --- a/Grid/lattice/Lattice_where.h +++ b/Grid/lattice/Lattice_where.h @@ -43,7 +43,7 @@ inline void whereWolf(Lattice &ret,const Lattice &predicate,Lattice< conformable(iftrue,predicate); conformable(iftrue,ret); - GridBase *grid=iftrue._grid; + GridBase *grid=iftrue.Grid(); typedef typename vobj::scalar_object scalar_object; typedef typename vobj::scalar_type scalar_type; @@ -52,22 +52,24 @@ inline void whereWolf(Lattice &ret,const Lattice &predicate,Lattice< const int Nsimd = grid->Nsimd(); - std::vector mask(Nsimd); - std::vector truevals (Nsimd); - std::vector falsevals(Nsimd); + Integer mask; + scalar_object trueval; + scalar_object falseval; - parallel_for(int ss=0;ssoSites(); ss++){ - - extract(iftrue._odata[ss] ,truevals); - extract(iffalse._odata[ss] ,falsevals); - extract(TensorRemove(predicate._odata[ss]),mask); - - for(int s=0;soSites(); + thread_for(ss,NN,{ + for(int l=0;l @@ -76,9 +78,9 @@ inline Lattice whereWolf(const Lattice &predicate,Lattice &ift conformable(iftrue,iffalse); conformable(iftrue,predicate); - Lattice ret(iftrue._grid); + Lattice ret(iftrue.Grid()); - where(ret,predicate,iftrue,iffalse); + whereWolf(ret,predicate,iftrue,iffalse); return ret; } From cd5891eecd45b53db3a02498d97d0bd5e29c67eb Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 11 Mar 2021 22:34:28 +0100 Subject: [PATCH 124/399] Test that fails on Cuda 11.0 --- tests/core/Test_where_extended.cc | 124 ++++++++++++++++-------------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/tests/core/Test_where_extended.cc b/tests/core/Test_where_extended.cc index 706fd7ee..9862b3ed 100644 --- a/tests/core/Test_where_extended.cc +++ b/tests/core/Test_where_extended.cc @@ -51,87 +51,93 @@ int main (int argc, char ** argv) } GridCartesian GRID(latt_size,simd_layout,mpi_layout); - - LatticeComplexD zz(&GRID); - LatticeInteger coor(&GRID); - LatticeComplexD rn(&GRID); - LatticeComplexD sl(&GRID); - - zz = ComplexD(0.0,0.0); - GridParallelRNG RNG(&GRID); RNG.SeedFixedIntegers(std::vector({45,12,81,9})); - gaussian(RNG,rn); - RealD nn=norm2(rn); - for(int mu=0;mu seeds4({1,2,3,4}); - GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); - LatticeInteger lcoor(UGrid); LatticeCoordinate(lcoor,Nd-1); - std::cout<=tmin),tmpF,ZZF); - nA = nA + norm2(tmp2F); - InsertSlice(tmp2F, q_outF, s , 0); + gaussian(RNG,rn); + + RealD nn=norm2(rn); + for(int mu=0;mu=tmin),tmpP,ZZP); - nA = nA + norm2(tmp2P); - InsertSlice(tmp2P, q_outP, s , 0); + zz = ComplexD(0.0,0.0); + + gaussian(RNG,rn); + + RealD nn=norm2(rn); + for(int mu=0;mu Date: Thu, 11 Mar 2021 23:54:53 +0100 Subject: [PATCH 125/399] NVCC versions found buggy added as guard --- Grid/util/CompilerCompatible.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Grid/util/CompilerCompatible.h b/Grid/util/CompilerCompatible.h index 37331668..7c4a056d 100644 --- a/Grid/util/CompilerCompatible.h +++ b/Grid/util/CompilerCompatible.h @@ -1,5 +1,16 @@ #pragma once +#if defined(__NVCC__) + +#if (__CUDACC_VER_MAJOR__ == 11) && (__CUDACC_VER_MINOR__ == 0) +#error "NVCC version 11.0 breaks on Ampere, see Github issue 346" +#endif +#if (__CUDACC_VER_MAJOR__ == 11) && (__CUDACC_VER_MINOR__ == 1) +#error "NVCC version 11.1 breaks on Ampere, see Github issue 346" +#endif + +#endif + #if defined(__clang__) #if __clang_major__ < 3 From db3ac67506bcbdbe0799632dcc1561a96d0e320f Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 12 Mar 2021 14:55:07 +0100 Subject: [PATCH 126/399] Update thread issue --- Grid/lattice/Lattice_where.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Grid/lattice/Lattice_where.h b/Grid/lattice/Lattice_where.h index e9a3f823..777f4015 100644 --- a/Grid/lattice/Lattice_where.h +++ b/Grid/lattice/Lattice_where.h @@ -52,16 +52,15 @@ inline void whereWolf(Lattice &ret,const Lattice &predicate,Lattice< const int Nsimd = grid->Nsimd(); - Integer mask; - scalar_object trueval; - scalar_object falseval; - autoView(iftrue_v,iftrue,CpuRead); autoView(iffalse_v,iffalse,CpuRead); autoView(predicate_v,predicate,CpuRead); autoView(ret_v,ret,CpuWrite); Integer NN= grid->oSites(); thread_for(ss,NN,{ + Integer mask; + scalar_object trueval; + scalar_object falseval; for(int l=0;l Date: Fri, 12 Mar 2021 09:31:17 -0500 Subject: [PATCH 127/399] updated to do list. Start adding DDHMC work items --- TODO | 75 ++++++++++++++++++++++++++++-------------------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/TODO b/TODO index f1175560..e23e040d 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,6 @@ +-- comms threads issue?? +-- Part done: Staggered kernel performance on GPU + ========================================================= General ========================================================= @@ -5,28 +8,18 @@ General - Make representations code take Gimpl - Simplify the HMCand remove modules - Lattice_arith - are the mult, mac etc.. still needed after ET engine? -- Lattice_rng -- Lattice_transfer.h -- accelerate A2Autils -- off critical path for HMC +- Lattice_rng - faster local only loop in init +- Audit: accelerate A2Autils -- off critical path for HMC ========================================================= -GPU branch code item work list +GPU work list ========================================================= -* sum_cpu promote to double during summation for increased precisoin. +* sum_cpu promote to double during summation for increased precision. * Introduce sumD & ReduceD * GPU sum is probably better currently. - * Accelerate the cshift & benchmark -* 0) Single GPU -- 128 bit integer table load in GPU code. - - ImprovedStaggered accelerate & measure perf - - Gianluca's changes to Cayley into gpu-port - - Mobius kernel fusion. -- Gianluca? - - Lebesque order reintroduction. StencilView should have pointer to it - - Lebesgue reorder in all kernels - * 3) Comms/NVlink - OpenMP tasks to run comms threads. Experiment with it - Remove explicit openMP in staggered. @@ -35,14 +28,6 @@ GPU branch code item work list - Stencil gather ?? - SIMD dirs in stencil -* 4) ET enhancements -- eval -> scalar ops in ET engine -- coalescedRead, coalescedWrite in expressions. - -* 5) Misc -- Conserved current clean up. -- multLinkProp eliminate - 8) Merge develop and test HMC 9) Gamma tables on GPU; check this. Appear to work, but no idea why. Are these done on CPU? @@ -52,7 +37,7 @@ GPU branch code item work list - Audit NAMESPACE CHANGES - Audit changes ------ +--------- Gianluca's changes - Performance impact of construct in aligned allocator??? --------- @@ -62,6 +47,33 @@ Gianluca's changes ----------------------------- DONE: ----------------------------- +===== +-- Done: Remez X^-1/2 X^-1/2 X = 1 test. + Feed in MdagM^2 as a test and take its sqrt. + Automated test that MdagM invsqrt(MdagM)invsqrt(MdagM) = 1 in HMC for bounds satisfaction. + +-- Done: Sycl Kernels into develop. Compare to existing unroll and just use. +-- Done: sRNG into refresh functions +-- Done: Tuned decomposition on CUDA into develop +-- Done: Sycl friend accessor. Const view attempt via typedef?? + + +* Done 5) Misc +- Conserved current clean up. +- multLinkProp eliminate + +* Done 0) Single GPU +- 128 bit integer table load in GPU code. + - ImprovedStaggered accelerate & measure perf + - Gianluca's changes to Cayley into gpu-port + - Mobius kernel fusion. -- Gianluca? + - Lebesque order reintroduction. StencilView should have pointer to it + - Lebesgue reorder in all kernels + +* 4) ET enhancements +- Done eval -> scalar ops in ET engine +- Done coalescedRead, coalescedWrite in expressions. + ============================================================================================= AUDIT ContractWWVV with respect to develop -- DONE - GPU accelerate EOFA -- DONE @@ -125,23 +137,6 @@ AUDIT ContractWWVV with respect to develop -- DONE - - (4) omp parallel for collapse(n) - - Only (1) has a natural mirror in accelerator_loop - - Nested loop macros get cumbersome made a generic interface for N deep -- - Don't like thread_region and thread_loop_in_region -- - Could replace with - - thread_nested(1, - for { - - } - ); - thread_nested(2, - for (){ - for (){ - - } - } - ); - - and same "in_region". ----------------------------- From 51f506553c59923fc60f3a68333b14757b8853a5 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Fri, 12 Mar 2021 15:33:04 +0100 Subject: [PATCH 128/399] Read out the local ID once, and store --- .../WilsonKernelsHandImplementation.h | 165 ++++++++++-------- 1 file changed, 96 insertions(+), 69 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h index fb42fe88..0703b613 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h @@ -79,18 +79,18 @@ Author: paboyle #ifdef GRID_SIMT #define LOAD_CHIMU(ptype) \ {const SiteSpinor & ref (in[offset]); \ - Chimu_00=coalescedReadPermute(ref()(0)(0),perm); \ - Chimu_01=coalescedReadPermute(ref()(0)(1),perm); \ - Chimu_02=coalescedReadPermute(ref()(0)(2),perm); \ - Chimu_10=coalescedReadPermute(ref()(1)(0),perm); \ - Chimu_11=coalescedReadPermute(ref()(1)(1),perm); \ - Chimu_12=coalescedReadPermute(ref()(1)(2),perm); \ - Chimu_20=coalescedReadPermute(ref()(2)(0),perm); \ - Chimu_21=coalescedReadPermute(ref()(2)(1),perm); \ - Chimu_22=coalescedReadPermute(ref()(2)(2),perm); \ - Chimu_30=coalescedReadPermute(ref()(3)(0),perm); \ - Chimu_31=coalescedReadPermute(ref()(3)(1),perm); \ - Chimu_32=coalescedReadPermute(ref()(3)(2),perm); } + Chimu_00=coalescedReadPermute(ref()(0)(0),perm,lane); \ + Chimu_01=coalescedReadPermute(ref()(0)(1),perm,lane); \ + Chimu_02=coalescedReadPermute(ref()(0)(2),perm,lane); \ + Chimu_10=coalescedReadPermute(ref()(1)(0),perm,lane); \ + Chimu_11=coalescedReadPermute(ref()(1)(1),perm,lane); \ + Chimu_12=coalescedReadPermute(ref()(1)(2),perm,lane); \ + Chimu_20=coalescedReadPermute(ref()(2)(0),perm,lane); \ + Chimu_21=coalescedReadPermute(ref()(2)(1),perm,lane); \ + Chimu_22=coalescedReadPermute(ref()(2)(2),perm,lane); \ + Chimu_30=coalescedReadPermute(ref()(3)(0),perm,lane); \ + Chimu_31=coalescedReadPermute(ref()(3)(1),perm,lane); \ + Chimu_32=coalescedReadPermute(ref()(3)(2),perm,lane); } #define PERMUTE_DIR(dir) ; #else #define LOAD_CHIMU(ptype) \ @@ -119,43 +119,43 @@ Author: paboyle #endif #define MULT_2SPIN(A)\ - {auto & ref(U[sU](A)); \ - U_00=coalescedRead(ref()(0,0)); \ - U_10=coalescedRead(ref()(1,0)); \ - U_20=coalescedRead(ref()(2,0)); \ - U_01=coalescedRead(ref()(0,1)); \ - U_11=coalescedRead(ref()(1,1)); \ - U_21=coalescedRead(ref()(2,1)); \ - UChi_00 = U_00*Chi_00; \ - UChi_10 = U_00*Chi_10; \ - UChi_01 = U_10*Chi_00; \ - UChi_11 = U_10*Chi_10; \ - UChi_02 = U_20*Chi_00; \ - UChi_12 = U_20*Chi_10; \ - UChi_00+= U_01*Chi_01; \ - UChi_10+= U_01*Chi_11; \ - UChi_01+= U_11*Chi_01; \ - UChi_11+= U_11*Chi_11; \ - UChi_02+= U_21*Chi_01; \ - UChi_12+= U_21*Chi_11; \ - U_00=coalescedRead(ref()(0,2)); \ - U_10=coalescedRead(ref()(1,2)); \ - U_20=coalescedRead(ref()(2,2)); \ - UChi_00+= U_00*Chi_02; \ - UChi_10+= U_00*Chi_12; \ - UChi_01+= U_10*Chi_02; \ - UChi_11+= U_10*Chi_12; \ - UChi_02+= U_20*Chi_02; \ + {auto & ref(U[sU](A)); \ + U_00=coalescedRead(ref()(0,0),lane); \ + U_10=coalescedRead(ref()(1,0),lane); \ + U_20=coalescedRead(ref()(2,0),lane); \ + U_01=coalescedRead(ref()(0,1),lane); \ + U_11=coalescedRead(ref()(1,1),lane); \ + U_21=coalescedRead(ref()(2,1),lane); \ + UChi_00 = U_00*Chi_00; \ + UChi_10 = U_00*Chi_10; \ + UChi_01 = U_10*Chi_00; \ + UChi_11 = U_10*Chi_10; \ + UChi_02 = U_20*Chi_00; \ + UChi_12 = U_20*Chi_10; \ + UChi_00+= U_01*Chi_01; \ + UChi_10+= U_01*Chi_11; \ + UChi_01+= U_11*Chi_01; \ + UChi_11+= U_11*Chi_11; \ + UChi_02+= U_21*Chi_01; \ + UChi_12+= U_21*Chi_11; \ + U_00=coalescedRead(ref()(0,2),lane); \ + U_10=coalescedRead(ref()(1,2),lane); \ + U_20=coalescedRead(ref()(2,2),lane); \ + UChi_00+= U_00*Chi_02; \ + UChi_10+= U_00*Chi_12; \ + UChi_01+= U_10*Chi_02; \ + UChi_11+= U_10*Chi_12; \ + UChi_02+= U_20*Chi_02; \ UChi_12+= U_20*Chi_12;} #define LOAD_CHI \ {const SiteHalfSpinor &ref(buf[offset]); \ - Chi_00 = coalescedRead(ref()(0)(0)); \ - Chi_01 = coalescedRead(ref()(0)(1)); \ - Chi_02 = coalescedRead(ref()(0)(2)); \ - Chi_10 = coalescedRead(ref()(1)(0)); \ - Chi_11 = coalescedRead(ref()(1)(1)); \ - Chi_12 = coalescedRead(ref()(1)(2));} + Chi_00 = coalescedRead(ref()(0)(0),lane); \ + Chi_01 = coalescedRead(ref()(0)(1),lane); \ + Chi_02 = coalescedRead(ref()(0)(2),lane); \ + Chi_10 = coalescedRead(ref()(1)(0),lane); \ + Chi_11 = coalescedRead(ref()(1)(1),lane); \ + Chi_12 = coalescedRead(ref()(1)(2),lane);} // hspin(0)=fspin(0)+timesI(fspin(3)); // hspin(1)=fspin(1)+timesI(fspin(2)); @@ -453,35 +453,35 @@ Author: paboyle #define HAND_RESULT(ss) \ { \ SiteSpinor & ref (out[ss]); \ - coalescedWrite(ref()(0)(0),result_00); \ - coalescedWrite(ref()(0)(1),result_01); \ - coalescedWrite(ref()(0)(2),result_02); \ - coalescedWrite(ref()(1)(0),result_10); \ - coalescedWrite(ref()(1)(1),result_11); \ - coalescedWrite(ref()(1)(2),result_12); \ - coalescedWrite(ref()(2)(0),result_20); \ - coalescedWrite(ref()(2)(1),result_21); \ - coalescedWrite(ref()(2)(2),result_22); \ - coalescedWrite(ref()(3)(0),result_30); \ - coalescedWrite(ref()(3)(1),result_31); \ - coalescedWrite(ref()(3)(2),result_32); \ + coalescedWrite(ref()(0)(0),result_00,lane); \ + coalescedWrite(ref()(0)(1),result_01,lane); \ + coalescedWrite(ref()(0)(2),result_02,lane); \ + coalescedWrite(ref()(1)(0),result_10,lane); \ + coalescedWrite(ref()(1)(1),result_11,lane); \ + coalescedWrite(ref()(1)(2),result_12,lane); \ + coalescedWrite(ref()(2)(0),result_20,lane); \ + coalescedWrite(ref()(2)(1),result_21,lane); \ + coalescedWrite(ref()(2)(2),result_22,lane); \ + coalescedWrite(ref()(3)(0),result_30,lane); \ + coalescedWrite(ref()(3)(1),result_31,lane); \ + coalescedWrite(ref()(3)(2),result_32,lane); \ } #define HAND_RESULT_EXT(ss) \ { \ SiteSpinor & ref (out[ss]); \ - coalescedWrite(ref()(0)(0),coalescedRead(ref()(0)(0))+result_00); \ - coalescedWrite(ref()(0)(1),coalescedRead(ref()(0)(1))+result_01); \ - coalescedWrite(ref()(0)(2),coalescedRead(ref()(0)(2))+result_02); \ - coalescedWrite(ref()(1)(0),coalescedRead(ref()(1)(0))+result_10); \ - coalescedWrite(ref()(1)(1),coalescedRead(ref()(1)(1))+result_11); \ - coalescedWrite(ref()(1)(2),coalescedRead(ref()(1)(2))+result_12); \ - coalescedWrite(ref()(2)(0),coalescedRead(ref()(2)(0))+result_20); \ - coalescedWrite(ref()(2)(1),coalescedRead(ref()(2)(1))+result_21); \ - coalescedWrite(ref()(2)(2),coalescedRead(ref()(2)(2))+result_22); \ - coalescedWrite(ref()(3)(0),coalescedRead(ref()(3)(0))+result_30); \ - coalescedWrite(ref()(3)(1),coalescedRead(ref()(3)(1))+result_31); \ - coalescedWrite(ref()(3)(2),coalescedRead(ref()(3)(2))+result_32); \ + coalescedWrite(ref()(0)(0),coalescedRead(ref()(0)(0))+result_00,lane); \ + coalescedWrite(ref()(0)(1),coalescedRead(ref()(0)(1))+result_01,lane); \ + coalescedWrite(ref()(0)(2),coalescedRead(ref()(0)(2))+result_02,lane); \ + coalescedWrite(ref()(1)(0),coalescedRead(ref()(1)(0))+result_10,lane); \ + coalescedWrite(ref()(1)(1),coalescedRead(ref()(1)(1))+result_11,lane); \ + coalescedWrite(ref()(1)(2),coalescedRead(ref()(1)(2))+result_12,lane); \ + coalescedWrite(ref()(2)(0),coalescedRead(ref()(2)(0))+result_20,lane); \ + coalescedWrite(ref()(2)(1),coalescedRead(ref()(2)(1))+result_21,lane); \ + coalescedWrite(ref()(2)(2),coalescedRead(ref()(2)(2))+result_22,lane); \ + coalescedWrite(ref()(3)(0),coalescedRead(ref()(3)(0))+result_30,lane); \ + coalescedWrite(ref()(3)(1),coalescedRead(ref()(3)(1))+result_31,lane); \ + coalescedWrite(ref()(3)(2),coalescedRead(ref()(3)(2))+result_32,lane); \ } #define HAND_DECLARATIONS(Simd) \ @@ -558,6 +558,9 @@ WilsonKernels::HandDhopSiteSycl(StencilVector st_perm,StencilEntry *st_p, // typedef decltype( coalescedRead( vCplx()()() )) Simt; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + HAND_DECLARATIONS(Simt); int offset,local,perm, ptype; @@ -584,6 +587,10 @@ WilsonKernels::HandDhopSite(StencilView &st, DoubledGaugeFieldView &U,Site typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + HAND_DECLARATIONS(Simt); int offset,local,perm, ptype; @@ -609,6 +616,10 @@ void WilsonKernels::HandDhopSiteDag(StencilView &st,DoubledGaugeFieldView typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + HAND_DECLARATIONS(Simt); StencilEntry *SE; @@ -635,6 +646,10 @@ WilsonKernels::HandDhopSiteInt(StencilView &st,DoubledGaugeFieldView &U,Si typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + HAND_DECLARATIONS(Simt); int offset,local,perm, ptype; @@ -660,6 +675,10 @@ void WilsonKernels::HandDhopSiteDagInt(StencilView &st,DoubledGaugeFieldVi typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + HAND_DECLARATIONS(Simt); StencilEntry *SE; @@ -686,6 +705,10 @@ WilsonKernels::HandDhopSiteExt(StencilView &st,DoubledGaugeFieldView &U,Si typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + HAND_DECLARATIONS(Simt); int offset, ptype; @@ -712,6 +735,10 @@ void WilsonKernels::HandDhopSiteDagExt(StencilView &st,DoubledGaugeFieldVi typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; + + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + HAND_DECLARATIONS(Simt); StencilEntry *SE; From 9c2b37218a8849ece7198fc7a25b56bde10f6b13 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 18 Mar 2021 06:24:11 -0400 Subject: [PATCH 129/399] sRNG parameter added --- Grid/qcd/utils/Metric.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/utils/Metric.h b/Grid/qcd/utils/Metric.h index 10d06de8..d8ae27dc 100644 --- a/Grid/qcd/utils/Metric.h +++ b/Grid/qcd/utils/Metric.h @@ -93,13 +93,13 @@ public: GeneralisedMomenta(GridBase* grid, Metric& M): M(M), Mom(grid), AuxMom(grid), AuxField(grid){} // Correct - void MomentaDistribution(GridParallelRNG& pRNG){ + void MomentaDistribution(GridSerialRNG & sRNG, GridParallelRNG& pRNG){ // Generate a distribution for // P^dag G P // where G = M^-1 // Generate gaussian momenta - Implementation::generate_momenta(Mom, pRNG); + Implementation::generate_momenta(Mom, sRNG, pRNG); // Modify the distribution with the metric M.MSquareRoot(Mom); @@ -107,8 +107,8 @@ public: // Auxiliary momenta // do nothing if trivial, so hide in the metric MomentaField AuxMomTemp(Mom.Grid()); - Implementation::generate_momenta(AuxMom, pRNG); - Implementation::generate_momenta(AuxField, pRNG); + Implementation::generate_momenta(AuxMom, sRNG, pRNG); + Implementation::generate_momenta(AuxField, sRNG, pRNG); // Modify the distribution with the metric // Aux^dag M Aux M.MInvSquareRoot(AuxMom); // AuxMom = M^{-1/2} AuxMomTemp From 49b0af2c95bd078b6b0b40b8b28b2ea8309daf45 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 18 Mar 2021 09:10:02 -0400 Subject: [PATCH 130/399] Update of tests to compile with the sRNG addition. Audited the code conventions (again) with the CPS momentum denominator and added anti periodic in time to the Test_mobius_force.cc and tested the Test_dwf_gpforce. Promoted thesee to test full HMC hamiltonian, tr P^2/2 + phidag MdagM phi with the same pdot and Udot as audited in the Integrator.h etc... With full comments and sources for factors. --- tests/forces/Test_dwf_force_eofa.cc | 4 +- tests/forces/Test_dwf_gpforce.cc | 123 +++++++++++++++-------- tests/forces/Test_dwf_gpforce_eofa.cc | 3 +- tests/forces/Test_laplacian_force.cc | 3 +- tests/forces/Test_mobius_force.cc | 109 +++++++++++++++++--- tests/forces/Test_mobius_force_eofa.cc | 3 +- tests/forces/Test_mobius_gpforce_eofa.cc | 3 +- 7 files changed, 188 insertions(+), 60 deletions(-) diff --git a/tests/forces/Test_dwf_force_eofa.cc b/tests/forces/Test_dwf_force_eofa.cc index 80d36934..525178d0 100644 --- a/tests/forces/Test_dwf_force_eofa.cc +++ b/tests/forces/Test_dwf_force_eofa.cc @@ -86,7 +86,9 @@ int main (int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, CG, CG, CG, CG, Params, true); - Meofa.refresh(U, RNG5); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(seeds4); + Meofa.refresh(U, sRNG, RNG5 ); + RealD S = Meofa.S(U); // pdag M p // get the deriv of phidag M phi with respect to "U" diff --git a/tests/forces/Test_dwf_gpforce.cc b/tests/forces/Test_dwf_gpforce.cc index 28133cc6..1fa1c6e4 100644 --- a/tests/forces/Test_dwf_gpforce.cc +++ b/tests/forces/Test_dwf_gpforce.cc @@ -84,6 +84,13 @@ int main (int argc, char ** argv) GparityDomainWallFermionR::ImplParams params; params.twists = twists; + /* + params.boundary_phases[0] = 1.0; + params.boundary_phases[1] = 1.0; + params.boundary_phases[2] = 1.0; + params.boundary_phases[3] =- 1.0; + */ + GparityDomainWallFermionR Dw(U,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,params); Dw.M (phi,Mphi); @@ -96,6 +103,16 @@ int main (int argc, char ** argv) Dw.MDeriv(tmp , Mphi, phi,DaggerNo ); UdSdU=tmp; Dw.MDeriv(tmp , phi, Mphi,DaggerYes ); UdSdU=(UdSdU+tmp); + + // ***************************************************************************************** + // *** There is a funny negative sign in all derivatives. This is - UdSdU. *** + // *** *** + // *** Deriv in both Wilson gauge action and the TwoFlavour.h seems to miss a minus sign *** + // *** UdSdU is negated relative to what I think - call what is returned mUdSdU, *** + // *** and insert minus sign *** + // ***************************************************************************************** + + UdSdU = - UdSdU ; // Follow sign convention of actions in Grid. Seems crazy. FermionField Ftmp (FGrid); @@ -106,7 +123,7 @@ int main (int argc, char ** argv) RealD Hmom = 0.0; RealD Hmomprime = 0.0; LatticeColourMatrix mommu(UGrid); - LatticeColourMatrix forcemu(UGrid); + LatticeColourMatrix mUdSdUmu(UGrid); LatticeGaugeField mom(UGrid); LatticeGaugeField Uprime(UGrid); @@ -114,10 +131,20 @@ int main (int argc, char ** argv) SU::GaussianFundamentalLieAlgebraMatrix(RNG4, mommu); // Traceless antihermitian momentum; gaussian in lie alg - Hmom -= real(sum(trace(mommu*mommu))); + // Momentum Hamiltonian is - trace(p^2)/HMC_MOM_DENOMINATOR + // + // Integrator.h: RealD H = - FieldImplementation::FieldSquareNorm(P)/HMC_MOMENTUM_DENOMINATOR; // - trace (P*P)/denom // GaugeImplTypes.h: Hloc += trace(Pmu * Pmu); + // Sign comes from a sneaky multiply by "i" in GaussianFundemantalLie algebra + // P is i P^a_\mu T^a, not Pa Ta + // + // Integrator.h: H = Hmom + sum S(action) + Hmom -= real(sum(trace(mommu*mommu)))/ HMC_MOMENTUM_DENOMINATOR; PokeIndex(mom,mommu,mu); + // -- Drops factor of "i" in the U update: U' = e^{P dt} U [ _not_ e^{iPdt}U ]. P is anti hermitian already + // -- Udot = p U + // fourth order exponential approx autoView( mom_v, mom, CpuRead); autoView( U_v , U, CpuRead); @@ -134,8 +161,8 @@ int main (int argc, char ** argv) ; }); } - std::cout << GridLogMessage <<"Initial mom hamiltonian is "<< Hmom <(mom,mu); - std::cout << GridLogMessage<< " Mommu " << norm2(mommu)<(UdSdU,mu); - std::cout << GridLogMessage<< " dsdumu " << norm2(mommu)<(UdSdU,mu); - mommu=Ta(mommu)*2.0; + mommu=Ta(mommu); // projectForce , GaugeImplTypes.h PokeIndex(UdSdU,mommu,mu); } for(int mu=0;mu(mom,mu); - std::cout << GridLogMessage<< " Mommu " << norm2(mommu)<(UdSdU,mu); - std::cout << GridLogMessage<< " dsdumu " << norm2(mommu)<(UdSdU,mu); + mUdSdUmu= PeekIndex(UdSdU,mu); mommu = PeekIndex(mom,mu); - // Update PF action density - dS = dS+trace(mommu*forcemu)*dt; + // + // Derive HMC eom: + // + // Sdot = - 2 trace( p p^dot ) / D - trace( p [ mUdSdU - h.c. ] ) = 0 + // + // + // Sdot = 0 = - 2 trace( p p^dot ) / D - 2 trace( p Ta( mUdSdU ) = 0 + // + // EOM: + // + // pdot = - D Ta( mUdSdU ) -- source of sign is the "funny sign" above + // + // dSqcd_dt = - 2.0*trace(mommu* Ta(mUdSdU) )*dt -- i.e. mUdSdU with adjoint term -> force has a 2x implicit + // + // dH_mom/dt = - 2 trace (p pdot)/Denom + // + // dH_tot / dt = 0 <= pdot = - Denom * mUdSdU + // + // dH_mom/dt = 2 trace (p mUdSdU ) + // + // True Momentum delta H has a dt^2 piece + // + // dSmom = [ trace mom*mom - trace ( (mom-Denom*f*dt)(mom-Denom*f*dt) ) ] / Denom + // = 2*trace(mom*f) dt - Denom*dt*dt * trace(f*f). + // = dSmom + dSmom2 + // - dSmom = dSmom - trace(mommu*forcemu) * dt; - dSmom2 = dSmom2 - trace(forcemu*forcemu) *(0.25* dt*dt); + dS = dS - 2.0*trace(mommu*mUdSdUmu)*dt; // U and Udagger derivs hence 2x. - // Update mom action density - mommu = mommu + forcemu*(dt*0.5); + dSmom = dSmom + 2.0*trace(mommu*mUdSdUmu) * dt; // this 2.0 coms from derivative of p^2 + + dSmom2 = dSmom2 - trace(mUdSdUmu*mUdSdUmu) * dt*dt* HMC_MOMENTUM_DENOMINATOR; // Remnant - Hmomprime -= real(sum(trace(mommu*mommu))); + // Update mom action density . Verbatim update_P in Integrator.h + mommu = mommu - mUdSdUmu * dt* HMC_MOMENTUM_DENOMINATOR;; + + Hmomprime -= real(sum(trace(mommu*mommu))) / HMC_MOMENTUM_DENOMINATOR; } @@ -199,20 +233,25 @@ int main (int argc, char ** argv) ComplexD dSm = sum(dSmom); ComplexD dSm2 = sum(dSmom2); + std::cout << GridLogMessage <<"dSm "<< dSm< CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, CG, CG, CG, CG, Params, true); - Meofa.refresh(U, RNG5); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(seeds4); + Meofa.refresh(U, sRNG, RNG5); RealD S = Meofa.S(U); // pdag M p // get the deriv of phidag M phi with respect to "U" diff --git a/tests/forces/Test_laplacian_force.cc b/tests/forces/Test_laplacian_force.cc index 18508860..dbaf1cbd 100644 --- a/tests/forces/Test_laplacian_force.cc +++ b/tests/forces/Test_laplacian_force.cc @@ -46,6 +46,7 @@ int main (int argc, char ** argv) std::vector seeds({1,2,3,4}); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers({4,5,6,7}); GridParallelRNG pRNG(&Grid); pRNG.SeedFixedIntegers(std::vector({15,91,21,3})); @@ -67,7 +68,7 @@ int main (int argc, char ** argv) LaplacianAdjointField Laplacian(&Grid, CG, LapPar, Kappa); GeneralisedMomenta LaplacianMomenta(&Grid, Laplacian); LaplacianMomenta.M.ImportGauge(U); - LaplacianMomenta.MomentaDistribution(pRNG);// fills the Momenta with the correct distr + LaplacianMomenta.MomentaDistribution(sRNG,pRNG);// fills the Momenta with the correct distr std::cout << std::setprecision(15); diff --git a/tests/forces/Test_mobius_force.cc b/tests/forces/Test_mobius_force.cc index ba7bc363..d2326a81 100644 --- a/tests/forces/Test_mobius_force.cc +++ b/tests/forces/Test_mobius_force.cc @@ -69,7 +69,14 @@ int main (int argc, char ** argv) RealD M5=1.8; RealD b=0.5; RealD c=0.5; - MobiusFermionR Ddwf(U,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,b,c); + + WilsonImplParams p; + p.boundary_phases[0] = 1.0; + p.boundary_phases[1] = 1.0; + p.boundary_phases[2] = 1.0; + p.boundary_phases[3] =- 1.0; + + MobiusFermionR Ddwf(U,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,b,c,p); Ddwf.M (phi,Mphi); ComplexD S = innerProduct(Mphi,Mphi); // pdag MdagM p @@ -82,24 +89,44 @@ int main (int argc, char ** argv) Ddwf.MDeriv(tmp , Mphi, phi,DaggerNo ); UdSdU=tmp; Ddwf.MDeriv(tmp , phi, Mphi,DaggerYes ); UdSdU=(UdSdU+tmp); + // ***************************************************************************************** + // *** There is a funny negative sign in all derivatives. This is - UdSdU. *** + // *** *** + // *** Deriv in both Wilson gauge action and the TwoFlavour.h seems to miss a minus sign *** + // *** UdSdU is negated relative to what I think - call what is returned mUdSdU, *** + // *** and insert minus sign *** + // ***************************************************************************************** + + UdSdU = - UdSdU ; // Follow sign convention of actions in Grid. Seems crazy. + LatticeFermion Ftmp (FGrid); //////////////////////////////////// // Modify the gauge field a little //////////////////////////////////// - RealD dt = 0.0001; + RealD dt = 0.001; + RealD Hmom = 0.0; + RealD Hmomprime = 0.0; LatticeColourMatrix mommu(UGrid); - LatticeColourMatrix forcemu(UGrid); + LatticeColourMatrix mUdSdUmu(UGrid); LatticeGaugeField mom(UGrid); LatticeGaugeField Uprime(UGrid); for(int mu=0;mu::GaussianFundamentalLieAlgebraMatrix(RNG4, mommu); // Traceless antihermitian momentum; gaussian in lie alg - PokeIndex(mom,mommu,mu); + // Momentum Hamiltonian is - trace(p^2)/HMC_MOM_DENOMINATOR + // + // Integrator.h: RealD H = - FieldImplementation::FieldSquareNorm(P)/HMC_MOMENTUM_DENOMINATOR; // - trace (P*P)/denom // GaugeImplTypes.h: Hloc += trace(Pmu * Pmu); + // Sign comes from a sneaky multiply by "i" in GaussianFundemantalLie algebra + // P is i P^a_\mu T^a, not Pa Ta + // + // Integrator.h: H = Hmom + sum S(action) + Hmom -= real(sum(trace(mommu*mommu)))/ HMC_MOMENTUM_DENOMINATOR; + // fourth order exponential approx autoView( U_v , U, CpuRead); autoView( mom_v, mom, CpuRead); @@ -115,6 +142,7 @@ int main (int argc, char ** argv) ; }); } + std::cout << GridLogMessage <<"Initial mom hamiltonian is "<< Hmom <(UdSdU,mu); - mommu=Ta(mommu)*2.0; + mommu=Ta(mommu); PokeIndex(UdSdU,mommu,mu); } for(int mu=0;mu(UdSdU,mu); + + mUdSdUmu= PeekIndex(UdSdU,mu); mommu = PeekIndex(mom,mu); - // Update PF action density - dS = dS+trace(mommu*forcemu)*dt; + // + // Derive HMC eom: + // + // Sdot = - 2 trace( p p^dot ) / D - trace( p [ mUdSdU - h.c. ] ) = 0 + // + // + // Sdot = 0 = - 2 trace( p p^dot ) / D - 2 trace( p Ta( mUdSdU ) = 0 + // + // EOM: + // + // pdot = - D Ta( mUdSdU ) -- source of sign is the "funny sign" above + // + // dSqcd_dt = - 2.0*trace(mommu* Ta(mUdSdU) )*dt -- i.e. mUdSdU with adjoint term -> force has a 2x implicit + // + // dH_mom/dt = - 2 trace (p pdot)/Denom + // + // dH_tot / dt = 0 <= pdot = - Denom * mUdSdU + // + // dH_mom/dt = 2 trace (p mUdSdU ) + // + // True Momentum delta H has a dt^2 piece + // + // dSmom = [ trace mom*mom - trace ( (mom-Denom*f*dt)(mom-Denom*f*dt) ) ] / Denom + // = 2*trace(mom*f) dt - Denom*dt*dt * trace(f*f). + // = dSmom + dSmom2 + // + + dS = dS - 2.0*trace(mommu*mUdSdUmu)*dt; // U and Udagger derivs hence 2x. + + dSmom = dSmom + 2.0*trace(mommu*mUdSdUmu) * dt; // this 2.0 coms from derivative of p^2 + + dSmom2 = dSmom2 - trace(mUdSdUmu*mUdSdUmu) * dt*dt* HMC_MOMENTUM_DENOMINATOR; // Remnant + + mommu = mommu - mUdSdUmu * dt* HMC_MOMENTUM_DENOMINATOR;; + + Hmomprime -= real(sum(trace(mommu*mommu))) / HMC_MOMENTUM_DENOMINATOR; } ComplexD dSpred = sum(dS); + ComplexD dSm = sum(dSmom); + ComplexD dSm2 = sum(dSmom2); - std::cout << GridLogMessage << " -- S "< CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, CG, CG, CG, CG, Params, false); - Meofa.refresh(U, RNG5); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(seeds4); + Meofa.refresh(U, sRNG, RNG5 ); RealD S = Meofa.S(U); // pdag M p // get the deriv of phidag M phi with respect to "U" diff --git a/tests/forces/Test_mobius_gpforce_eofa.cc b/tests/forces/Test_mobius_gpforce_eofa.cc index 9c80b2aa..7f114615 100644 --- a/tests/forces/Test_mobius_gpforce_eofa.cc +++ b/tests/forces/Test_mobius_gpforce_eofa.cc @@ -93,7 +93,8 @@ int main (int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, CG, CG, CG, CG, Params, false); - Meofa.refresh(U, RNG5); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(seeds4); + Meofa.refresh(U, sRNG, RNG5 ); RealD S = Meofa.S(U); // pdag M p // get the deriv of phidag M phi with respect to "U" From 15c50a7442527a7962e53cdc4a6bb3369f41501c Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 18 Mar 2021 15:40:42 -0400 Subject: [PATCH 131/399] Explicit instantiate the template function --- Grid/util/Init.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Grid/util/Init.cc b/Grid/util/Init.cc index 55d8c5bf..bfbc464d 100644 --- a/Grid/util/Init.cc +++ b/Grid/util/Init.cc @@ -153,6 +153,9 @@ void GridCmdOptionIntVector(const std::string &str,VectorInt & vec) return; } +template void GridCmdOptionIntVector(const std::string &str,std::vector & vec); +template void GridCmdOptionIntVector(const std::string &str,Coordinate & vec); + void GridCmdOptionInt(std::string &str,int & val) { std::stringstream ss(str); From 8bdadbadaca9cffd8d7616cc9deed07187b262f1 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 18 Mar 2021 15:41:14 -0400 Subject: [PATCH 132/399] Cold start --- HMC/Mobius2p1fRHMC.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HMC/Mobius2p1fRHMC.cc b/HMC/Mobius2p1fRHMC.cc index 82ca4d37..b958d548 100644 --- a/HMC/Mobius2p1fRHMC.cc +++ b/HMC/Mobius2p1fRHMC.cc @@ -56,12 +56,12 @@ int main(int argc, char **argv) { MD.trajL = 1.0; HMCparameters HMCparams; - HMCparams.StartTrajectory = 30; + HMCparams.StartTrajectory = 0; HMCparams.Trajectories = 200; HMCparams.NoMetropolisUntil= 0; // "[HotStart, ColdStart, TepidStart, CheckpointStart]\n"; - // HMCparams.StartingType =std::string("ColdStart"); - HMCparams.StartingType =std::string("CheckpointStart"); + HMCparams.StartingType =std::string("ColdStart"); + // HMCparams.StartingType =std::string("CheckpointStart"); HMCparams.MD = MD; HMCWrapper TheHMC(HMCparams); From 2bb374daea0f412ab131d274c4fd2e60e2a92c3b Mon Sep 17 00:00:00 2001 From: Christoph Lehner Date: Fri, 19 Mar 2021 11:33:23 +0100 Subject: [PATCH 133/399] hip-friendly --- Grid/algorithms/CoarsenedMatrix.h | 10 +++++++--- Grid/lattice/Lattice_transfer.h | 12 ++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index b9594678..2fd187ff 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -442,6 +442,8 @@ public: for(int p=0; poSites()*nbasis, Nsimd, { @@ -453,7 +455,7 @@ public: StencilEntry *SE; for(int p=0;p AcceleratorViewContainer; for(int p=0;p_is_local) { @@ -754,7 +758,7 @@ public: StencilEntry *SE; for(int p=0;p_is_local) { diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 91de721f..2da78398 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -360,16 +360,22 @@ inline void blockSum(Lattice &coarseData,const Lattice &fineData) autoView( coarseData_ , coarseData, AcceleratorWrite); autoView( fineData_ , fineData, AcceleratorRead); + auto coarseData_p = &coarseData_[0]; + auto fineData_p = &fineData_[0]; + Coordinate fine_rdimensions = fine->_rdimensions; Coordinate coarse_rdimensions = coarse->_rdimensions; + + vobj zz = Zero(); accelerator_for(sc,coarse->oSites(),1,{ // One thread per sub block Coordinate coor_c(_ndimension); Lexicographic::CoorFromIndex(coor_c,sc,coarse_rdimensions); // Block coordinate - coarseData_[sc]=Zero(); + vobj cd = zz; + for(int sb=0;sb &coarseData,const Lattice &fineData) for(int d=0;d<_ndimension;d++) coor_f[d]=coor_c[d]*block_r[d] + coor_b[d]; Lexicographic::IndexFromCoor(coor_f,sf,fine_rdimensions); - coarseData_[sc]=coarseData_[sc]+fineData_[sf]; + cd=cd+fineData_p[sf]; } + coarseData_p[sc] = cd; + }); return; } From bb89a82a07be478d50060efd5c76cf71622af870 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 29 Mar 2021 20:01:15 +0200 Subject: [PATCH 134/399] Staggered coalseced read --- Grid/qcd/action/fermion/StaggeredImpl.h | 16 ++-- Grid/qcd/action/fermion/WilsonImpl.h | 35 ++++++++- .../StaggeredKernelsImplementation.h | 76 ++++++++++--------- 3 files changed, 82 insertions(+), 45 deletions(-) diff --git a/Grid/qcd/action/fermion/StaggeredImpl.h b/Grid/qcd/action/fermion/StaggeredImpl.h index 8adf45a4..f44d12f4 100644 --- a/Grid/qcd/action/fermion/StaggeredImpl.h +++ b/Grid/qcd/action/fermion/StaggeredImpl.h @@ -72,19 +72,23 @@ public: StaggeredImpl(const ImplParams &p = ImplParams()) : Params(p){}; - static accelerator_inline void multLink(SiteSpinor &phi, + template + static accelerator_inline void multLink(_Spinor &phi, const SiteDoubledGaugeField &U, - const SiteSpinor &chi, + const _Spinor &chi, int mu) { - mult(&phi(), &U(mu), &chi()); + auto UU = coalescedRead(U(mu)); + mult(&phi(), &UU, &chi()); } - static accelerator_inline void multLinkAdd(SiteSpinor &phi, + template + static accelerator_inline void multLinkAdd(_Spinor &phi, const SiteDoubledGaugeField &U, - const SiteSpinor &chi, + const _Spinor &chi, int mu) { - mac(&phi(), &U(mu), &chi()); + auto UU = coalescedRead(U(mu)); + mac(&phi(), &UU, &chi()); } template diff --git a/Grid/qcd/action/fermion/WilsonImpl.h b/Grid/qcd/action/fermion/WilsonImpl.h index 94676b6b..2ff6feba 100644 --- a/Grid/qcd/action/fermion/WilsonImpl.h +++ b/Grid/qcd/action/fermion/WilsonImpl.h @@ -184,18 +184,22 @@ public: mat = TraceIndex(P); } - inline void extractLinkField(std::vector &mat, DoubledGaugeField &Uds){ + inline void extractLinkField(std::vector &mat, DoubledGaugeField &Uds) + { for (int mu = 0; mu < Nd; mu++) mat[mu] = PeekIndex(Uds, mu); } - - inline void InsertForce5D(GaugeField &mat, FermionField &Btilde, FermionField Ã,int mu){ - + inline void InsertForce5D(GaugeField &mat, FermionField &Btilde, FermionField Ã,int mu) + { +#undef USE_OLD_INSERT_FORCE int Ls=Btilde.Grid()->_fdimensions[0]; + autoView( mat_v , mat, AcceleratorWrite); +#ifdef USE_OLD_INSERT_FORCE GaugeLinkField tmp(mat.Grid()); tmp = Zero(); { + const int Nsimd = SiteSpinor::Nsimd(); autoView( tmp_v , tmp, AcceleratorWrite); autoView( Btilde_v , Btilde, AcceleratorRead); autoView( Atilde_v , Atilde, AcceleratorRead); @@ -208,6 +212,29 @@ public: }); } PokeIndex(mat,tmp,mu); +#else + { + const int Nsimd = SiteSpinor::Nsimd(); + autoView( Btilde_v , Btilde, AcceleratorRead); + autoView( Atilde_v , Atilde, AcceleratorRead); + accelerator_for(sss,mat.Grid()->oSites(),Nsimd,{ + int sU=sss; + typedef decltype(coalescedRead(mat_v[sU](mu)() )) ColorMatrixType; + ColorMatrixType sum; + zeroit(sum); + for(int s=0;s_is_local ) { \ - if (SE->_permute) { \ - chi_p = χ \ - permute(chi, in[SE->_offset], ptype); \ - } else { \ - chi_p = &in[SE->_offset]; \ - } \ + int perm= SE->_permute; \ + chi = coalescedReadPermute(in[SE->_offset],ptype,perm,lane);\ } else { \ - chi_p = &buf[SE->_offset]; \ + chi = coalescedRead(buf[SE->_offset],lane); \ } \ - multLink(Uchi, U[sU], *chi_p, Dir); + acceleratorSynchronise(); \ + multLink(Uchi, U[sU], chi, Dir); #define GENERIC_STENCIL_LEG_INT(U,Dir,skew,multLink) \ SE = st.GetEntry(ptype, Dir+skew, sF); \ if (SE->_is_local ) { \ - if (SE->_permute) { \ - chi_p = χ \ - permute(chi, in[SE->_offset], ptype); \ - } else { \ - chi_p = &in[SE->_offset]; \ - } \ + int perm= SE->_permute; \ + chi = coalescedReadPermute(in[SE->_offset],ptype,perm,lane);\ } else if ( st.same_node[Dir] ) { \ - chi_p = &buf[SE->_offset]; \ + chi = coalescedRead(buf[SE->_offset],lane); \ } \ if (SE->_is_local || st.same_node[Dir] ) { \ - multLink(Uchi, U[sU], *chi_p, Dir); \ + multLink(Uchi, U[sU], chi, Dir); \ } #define GENERIC_STENCIL_LEG_EXT(U,Dir,skew,multLink) \ SE = st.GetEntry(ptype, Dir+skew, sF); \ if ((!SE->_is_local) && (!st.same_node[Dir]) ) { \ nmu++; \ - chi_p = &buf[SE->_offset]; \ - multLink(Uchi, U[sU], *chi_p, Dir); \ + chi = coalescedRead(buf[SE->_offset],lane); \ + multLink(Uchi, U[sU], chi, Dir); \ } template @@ -84,12 +77,14 @@ void StaggeredKernels::DhopSiteGeneric(StencilView &st, SiteSpinor *buf, int sF, int sU, const FermionFieldView &in, FermionFieldView &out, int dag) { - const SiteSpinor *chi_p; - SiteSpinor chi; - SiteSpinor Uchi; + typedef decltype(coalescedRead(in[0])) calcSpinor; + calcSpinor chi; + calcSpinor Uchi; StencilEntry *SE; int ptype; int skew; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); // for(int s=0;s::DhopSiteGeneric(StencilView &st, if ( dag ) { Uchi = - Uchi; } - vstream(out[sF], Uchi); + coalescedWrite(out[sF], Uchi,lane); } }; @@ -130,13 +125,16 @@ template accelerator_inline void StaggeredKernels::DhopSiteGenericInt(StencilView &st, DoubledGaugeFieldView &U, DoubledGaugeFieldView &UUU, SiteSpinor *buf, int sF, int sU, - const FermionFieldView &in, FermionFieldView &out,int dag) { - const SiteSpinor *chi_p; - SiteSpinor chi; - SiteSpinor Uchi; + const FermionFieldView &in, FermionFieldView &out,int dag) +{ + typedef decltype(coalescedRead(in[0])) calcSpinor; + calcSpinor chi; + calcSpinor Uchi; StencilEntry *SE; int ptype; int skew ; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); // for(int s=0;s::DhopSiteGenericInt(StencilView &st, if ( dag ) { Uchi = - Uchi; } - vstream(out[sF], Uchi); + coalescedWrite(out[sF], Uchi,lane); } }; @@ -178,14 +176,17 @@ template accelerator_inline void StaggeredKernels::DhopSiteGenericExt(StencilView &st, DoubledGaugeFieldView &U, DoubledGaugeFieldView &UUU, SiteSpinor *buf, int sF, int sU, - const FermionFieldView &in, FermionFieldView &out,int dag) { - const SiteSpinor *chi_p; - // SiteSpinor chi; - SiteSpinor Uchi; + const FermionFieldView &in, FermionFieldView &out,int dag) +{ + typedef decltype(coalescedRead(in[0])) calcSpinor; + calcSpinor chi; + calcSpinor Uchi; StencilEntry *SE; int ptype; int nmu=0; int skew ; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); // for(int s=0;s::DhopSiteGenericExt(StencilView &st, GENERIC_STENCIL_LEG_EXT(UUU,Zm,skew,Impl::multLinkAdd); GENERIC_STENCIL_LEG_EXT(UUU,Tm,skew,Impl::multLinkAdd); } - if ( nmu ) { - if ( dag ) { - out[sF] = out[sF] - Uchi; + if ( nmu ) { + auto _out = coalescedRead(out[sF],lane); + if ( dag ) { + coalescedWrite(out[sF], _out-Uchi,lane); } else { - out[sF] = out[sF] + Uchi; + coalescedWrite(out[sF], _out+Uchi,lane); } } } @@ -261,6 +263,8 @@ void StaggeredKernels::DhopImproved(StencilImpl &st, LebesgueOrder &lo, GridBase *FGrid=in.Grid(); GridBase *UGrid=U.Grid(); typedef StaggeredKernels ThisKernel; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); autoView( UUU_v , UUU, AcceleratorRead); autoView( U_v , U, AcceleratorRead); autoView( in_v , in, AcceleratorRead); @@ -301,6 +305,8 @@ void StaggeredKernels::DhopNaive(StencilImpl &st, LebesgueOrder &lo, GridBase *FGrid=in.Grid(); GridBase *UGrid=U.Grid(); typedef StaggeredKernels ThisKernel; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); autoView( UUU_v , U, AcceleratorRead); autoView( U_v , U, AcceleratorRead); autoView( in_v , in, AcceleratorRead); From e9479929570e3b6e000f60b6368d7981666fac7c Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 29 Mar 2021 20:04:06 +0200 Subject: [PATCH 135/399] Improved force terms --- Grid/tensors/Tensor_outer.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Grid/tensors/Tensor_outer.h b/Grid/tensors/Tensor_outer.h index 4902c22f..a32a2a91 100644 --- a/Grid/tensors/Tensor_outer.h +++ b/Grid/tensors/Tensor_outer.h @@ -34,6 +34,16 @@ NAMESPACE_BEGIN(Grid); // outerProduct Scalar x Scalar -> Scalar // Vector x Vector -> Matrix /////////////////////////////////////////////////////////////////////////////////////// +template = 0> +accelerator_inline CC outerProduct(const CC &l, const CC& r) +{ + return l*conj(r); +} +template = 0> +accelerator_inline RR outerProduct(const RR &l, const RR& r) +{ + return l*r; +} template accelerator_inline auto outerProduct (const iVector& lhs,const iVector& rhs) -> iMatrix @@ -57,17 +67,6 @@ auto outerProduct (const iScalar& lhs,const iScalar& rhs) -> iScalar = 0> -accelerator_inline CC outerProduct(const CC &l, const CC& r) -{ - return l*conj(r); -} -template = 0> -accelerator_inline RR outerProduct(const RR &l, const RR& r) -{ - return l*r; -} - NAMESPACE_END(Grid); #endif From a7fb25adf66996f5de6861228774c803ef87bd4a Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 29 Mar 2021 21:44:14 +0200 Subject: [PATCH 136/399] Make Cshift fields static to avoid repeated reallocaate overhead --- Grid/cshift/Cshift_mpi.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Grid/cshift/Cshift_mpi.h b/Grid/cshift/Cshift_mpi.h index 375d004e..7e93e260 100644 --- a/Grid/cshift/Cshift_mpi.h +++ b/Grid/cshift/Cshift_mpi.h @@ -122,8 +122,8 @@ template void Cshift_comms(Lattice &ret,const Lattice &r assert(shift_slice_nblock[dimension]*rhs.Grid()->_slice_block[dimension]; - cshiftVector send_buf(buffer_size); - cshiftVector recv_buf(buffer_size); + static cshiftVector send_buf; send_buf.resize(buffer_size); + static cshiftVector recv_buf; recv_buf.resize(buffer_size); int cb= (cbmask==0x2)? Odd : Even; int sshift= rhs.Grid()->CheckerBoardShiftForCB(rhs.Checkerboard(),dimension,shift,cb); @@ -198,8 +198,8 @@ template void Cshift_comms_simd(Lattice &ret,const Lattice_slice_nblock[dimension]*grid->_slice_block[dimension]; // int words = sizeof(vobj)/sizeof(vector_type); - std::vector > send_buf_extract(Nsimd); - std::vector > recv_buf_extract(Nsimd); + static std::vector > send_buf_extract; send_buf_extract.resize(Nsimd); + static std::vector > recv_buf_extract; recv_buf_extract.resize(Nsimd); scalar_object * recv_buf_extract_mpi; scalar_object * send_buf_extract_mpi; @@ -294,8 +294,8 @@ template void Cshift_comms(Lattice &ret,const Lattice &r assert(shift_slice_nblock[dimension]*rhs.Grid()->_slice_block[dimension]; - cshiftVector send_buf_v(buffer_size); - cshiftVector recv_buf_v(buffer_size); + static cshiftVector send_buf_v; send_buf_v.resize(buffer_size); + static cshiftVector recv_buf_v; recv_buf_v.resize(buffer_size); vobj *send_buf; vobj *recv_buf; { @@ -381,8 +381,8 @@ template void Cshift_comms_simd(Lattice &ret,const Lattice_slice_nblock[dimension]*grid->_slice_block[dimension]; // int words = sizeof(vobj)/sizeof(vector_type); - std::vector > send_buf_extract(Nsimd); - std::vector > recv_buf_extract(Nsimd); + static std::vector > send_buf_extract; send_buf_extract.resize(Nsimd); + static std::vector > recv_buf_extract; recv_buf_extract.resize(Nsimd); scalar_object * recv_buf_extract_mpi; scalar_object * send_buf_extract_mpi; { From 92e2c517d8097d7ea1ed4e02ec4b5554d61eec6e Mon Sep 17 00:00:00 2001 From: "Henrique B.R" Date: Thu, 1 Apr 2021 18:21:19 +0100 Subject: [PATCH 137/399] Changed pick- and setCheckerboard to use accelerator_for --- Grid/lattice/Lattice_transfer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 91de721f..202103d1 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -51,9 +51,9 @@ template inline void pickCheckerboard(int cb,Lattice &half,con { half.Checkerboard() = cb; - autoView( half_v, half, CpuWrite); - autoView( full_v, full, CpuRead); - thread_for(ss, full.Grid()->oSites(),{ + autoView( half_v, half, AcceleratorWrite); + autoView( full_v, full, AcceleratorRead); + accelerator_for(ss, full.Grid()->oSites(),full.Grid()->Nsimd(),{ int cbos; Coordinate coor; full.Grid()->oCoorFromOindex(coor,ss); @@ -68,9 +68,9 @@ template inline void pickCheckerboard(int cb,Lattice &half,con template inline void setCheckerboard(Lattice &full,const Lattice &half) { int cb = half.Checkerboard(); - autoView( half_v , half, CpuRead); - autoView( full_v , full, CpuWrite); - thread_for(ss,full.Grid()->oSites(),{ + autoView( half_v , half, AcceleratorRead); + autoView( full_v , full, AcceleratorWrite); + accelerator_for(ss, full.Grid()->oSites(),full.Grid()->Nsimd(),{ Coordinate coor; int cbos; From 3b7fce1e7660517ffb866f77f51747cfbd8afb87 Mon Sep 17 00:00:00 2001 From: "Henrique B.R" Date: Fri, 2 Apr 2021 14:38:41 +0100 Subject: [PATCH 138/399] Reverted checkerboard changes --- Grid/lattice/Lattice_transfer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index e7ab77a8..5a26cce9 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -51,9 +51,9 @@ template inline void pickCheckerboard(int cb,Lattice &half,con { half.Checkerboard() = cb; - autoView( half_v, half, AcceleratorWrite); - autoView( full_v, full, AcceleratorRead); - accelerator_for(ss, full.Grid()->oSites(),full.Grid()->Nsimd(),{ + autoView( half_v, half, CpuWrite); + autoView( full_v, full, CpuRead); + thread_for(ss, full.Grid()->oSites(),{ int cbos; Coordinate coor; full.Grid()->oCoorFromOindex(coor,ss); @@ -68,9 +68,9 @@ template inline void pickCheckerboard(int cb,Lattice &half,con template inline void setCheckerboard(Lattice &full,const Lattice &half) { int cb = half.Checkerboard(); - autoView( half_v , half, AcceleratorRead); - autoView( full_v , full, AcceleratorWrite); - accelerator_for(ss, full.Grid()->oSites(),full.Grid()->Nsimd(),{ + autoView( half_v , half, CpuRead); + autoView( full_v , full, CpuWrite); + thread_for(ss,full.Grid()->oSites(),{ Coordinate coor; int cbos; From addeb621a7015cb690a08d4650e8f2b10568fbf7 Mon Sep 17 00:00:00 2001 From: Andrew Zhen Ning Yong Date: Tue, 6 Apr 2021 13:45:37 +0100 Subject: [PATCH 139/399] Implemented tadpole operator for Shamir action. --- .../implementation/CayleyFermion5DImplementation.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index f11e9c44..c3e0f821 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -880,11 +880,23 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, } std::vector G_s(Ls,1.0); + Integer sign = 1; // sign flip for vector/tadpole if ( curr_type == Current::Axial ) { for(int s=0;s_b; + auto c=this->_c; + if ( b == 1 && c == 0 ) { + sign = -1; + } + else { + std::cerr << "Error: Tadpole implementation currently unavailable for non-Shamir actions." << std::endl; + assert(b==1 && c==0); + } + } for(int s=0;s::SeqConservedCurrent(PropagatorField &q_in, tmp = Cshift(tmp,mu,1); Impl::multLinkField(Utmp,this->Umu,tmp,mu); - tmp = G_s[s]*( Utmp*ph - gmu*Utmp*ph ); // Forward hop + tmp = sign*G_s[s]*( Utmp*ph - gmu*Utmp*ph ); // Forward hop tmp = where((lcoor>=tmin),tmp,zz); // Mask the time L_Q = where((lcoor<=tmax),tmp,zz); // Position of current complicated From 3e2ae1e9afac8aba445b4a8a7611fe847d22b60c Mon Sep 17 00:00:00 2001 From: "Henrique B.R" Date: Thu, 8 Apr 2021 16:58:47 +0100 Subject: [PATCH 140/399] Added profiling messages to pick and set checkerboard functions --- Grid/lattice/Lattice_transfer.h | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 5a26cce9..1a17220f 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -49,10 +49,15 @@ inline void subdivides(GridBase *coarse,GridBase *fine) //////////////////////////////////////////////////////////////////////////////////////////// template inline void pickCheckerboard(int cb,Lattice &half,const Lattice &full) { + static double time_autoview = 0; + static double time_loop = 0; half.Checkerboard() = cb; - + + double start = usecond(); autoView( half_v, half, CpuWrite); autoView( full_v, full, CpuRead); + time_autoview += usecond()-start; + start = usecond(); thread_for(ss, full.Grid()->oSites(),{ int cbos; Coordinate coor; @@ -64,12 +69,24 @@ template inline void pickCheckerboard(int cb,Lattice &half,con half_v[ssh] = full_v[ss]; } }); + + time_loop += usecond()-start; + std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; + std::cout << "pickCheckerboard" << std::endl; + std::cout << "AutoView cumulative time (s) = " << time_autoview/1000000. << "(" << 100*time_autoview/(time_autoview+time_loop) << "% of pickCheckerboard)" << std::endl; + std::cout << "Loop cumulative time (s) = " << time_loop/1000000. << "(" << 100*time_loop/(time_autoview+time_loop) << "% of pickCheckerboard)" << std::endl; } template inline void setCheckerboard(Lattice &full,const Lattice &half) { + static double time_autoview = 0; + static double time_loop = 0; int cb = half.Checkerboard(); + + double start = usecond(); autoView( half_v , half, CpuRead); autoView( full_v , full, CpuWrite); + time_autoview += usecond()-start; + start = usecond(); thread_for(ss,full.Grid()->oSites(),{ Coordinate coor; @@ -83,6 +100,13 @@ template inline void setCheckerboard(Lattice &full,const Latti full_v[ss]=half_v[ssh]; } }); + + time_loop += usecond()-start; + std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; + std::cout << "setCheckerboard" << std::endl; + std::cout << "AutoView cumulative time (s) = " << time_autoview/1000000. << "(" << 100*time_autoview/(time_autoview+time_loop) << "% of setCheckerboard)" << std::endl; + std::cout << "Loop cumulative time (s) = " << time_loop/1000000. << "(" << 100*time_loop/(time_autoview+time_loop) << "% of setCheckerboard)" << std::endl; + } //////////////////////////////////////////////////////////////////////////////////////////// From 364793154b2b715d27d494cd9990ed11959b66da Mon Sep 17 00:00:00 2001 From: "Henrique B.R" Date: Fri, 9 Apr 2021 15:47:17 +0100 Subject: [PATCH 141/399] Reverted checkerboard changes --- Grid/lattice/Lattice_transfer.h | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 1a17220f..5a26cce9 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -49,15 +49,10 @@ inline void subdivides(GridBase *coarse,GridBase *fine) //////////////////////////////////////////////////////////////////////////////////////////// template inline void pickCheckerboard(int cb,Lattice &half,const Lattice &full) { - static double time_autoview = 0; - static double time_loop = 0; half.Checkerboard() = cb; - - double start = usecond(); + autoView( half_v, half, CpuWrite); autoView( full_v, full, CpuRead); - time_autoview += usecond()-start; - start = usecond(); thread_for(ss, full.Grid()->oSites(),{ int cbos; Coordinate coor; @@ -69,24 +64,12 @@ template inline void pickCheckerboard(int cb,Lattice &half,con half_v[ssh] = full_v[ss]; } }); - - time_loop += usecond()-start; - std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; - std::cout << "pickCheckerboard" << std::endl; - std::cout << "AutoView cumulative time (s) = " << time_autoview/1000000. << "(" << 100*time_autoview/(time_autoview+time_loop) << "% of pickCheckerboard)" << std::endl; - std::cout << "Loop cumulative time (s) = " << time_loop/1000000. << "(" << 100*time_loop/(time_autoview+time_loop) << "% of pickCheckerboard)" << std::endl; } template inline void setCheckerboard(Lattice &full,const Lattice &half) { - static double time_autoview = 0; - static double time_loop = 0; int cb = half.Checkerboard(); - - double start = usecond(); autoView( half_v , half, CpuRead); autoView( full_v , full, CpuWrite); - time_autoview += usecond()-start; - start = usecond(); thread_for(ss,full.Grid()->oSites(),{ Coordinate coor; @@ -100,13 +83,6 @@ template inline void setCheckerboard(Lattice &full,const Latti full_v[ss]=half_v[ssh]; } }); - - time_loop += usecond()-start; - std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; - std::cout << "setCheckerboard" << std::endl; - std::cout << "AutoView cumulative time (s) = " << time_autoview/1000000. << "(" << 100*time_autoview/(time_autoview+time_loop) << "% of setCheckerboard)" << std::endl; - std::cout << "Loop cumulative time (s) = " << time_loop/1000000. << "(" << 100*time_loop/(time_autoview+time_loop) << "% of setCheckerboard)" << std::endl; - } //////////////////////////////////////////////////////////////////////////////////////////// From 980e721f6e25864cadfea3611a9c1a052d8d05c4 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 13 Apr 2021 09:33:01 -0400 Subject: [PATCH 142/399] Update MetaData.h --- Grid/parallelIO/MetaData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/parallelIO/MetaData.h b/Grid/parallelIO/MetaData.h index d30ba523..af8b3f76 100644 --- a/Grid/parallelIO/MetaData.h +++ b/Grid/parallelIO/MetaData.h @@ -128,7 +128,7 @@ inline void MachineCharacteristics(FieldMetaData &header) std::time_t t = std::time(nullptr); std::tm tm_ = *std::localtime(&t); std::ostringstream oss; - // oss << std::put_time(&tm_, "%c %Z"); + oss << std::put_time(&tm_, "%c %Z"); header.creation_date = oss.str(); header.archive_date = header.creation_date; From 8083e3f7e8f2e7d800c26bbd7a9e057258271f75 Mon Sep 17 00:00:00 2001 From: Andrew Zhen Ning Yong Date: Thu, 15 Apr 2021 11:14:31 +0100 Subject: [PATCH 143/399] Sign factor for tadpole implementation corrected. --- .../implementation/CayleyFermion5DImplementation.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index c3e0f821..323a6e01 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -880,7 +880,7 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, } std::vector G_s(Ls,1.0); - Integer sign = 1; // sign flip for vector/tadpole + RealD sign = 1.0; // sign flip for vector/tadpole if ( curr_type == Current::Axial ) { for(int s=0;s::SeqConservedCurrent(PropagatorField &q_in, auto b=this->_b; auto c=this->_c; if ( b == 1 && c == 0 ) { - sign = -1; + sign = -1.0; } else { std::cerr << "Error: Tadpole implementation currently unavailable for non-Shamir actions." << std::endl; @@ -919,7 +919,7 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, tmp = Cshift(tmp,mu,1); Impl::multLinkField(Utmp,this->Umu,tmp,mu); - tmp = sign*G_s[s]*( Utmp*ph - gmu*Utmp*ph ); // Forward hop + tmp = G_s[s]*( Utmp*ph - gmu*Utmp*ph ); // Forward hop tmp = where((lcoor>=tmin),tmp,zz); // Mask the time L_Q = where((lcoor<=tmax),tmp,zz); // Position of current complicated @@ -933,7 +933,7 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, tmp = tmp *ph; tmp = Cshift(tmp,mu,-1); Impl::multLinkField(Utmp,this->Umu,tmp,mu+Nd); // Adjoint link - tmp = -G_s[s]*( Utmp + gmu*Utmp ); + tmp = -1*sign*G_s[s]*( Utmp + gmu*Utmp ); tmp = where((lcoor>=tmin+tshift),tmp,zz); // Mask the time L_Q += where((lcoor<=tmax+tshift),tmp,zz); // Position of current complicated From f3f11b586f5b2abae0288bc45c6540fd25501267 Mon Sep 17 00:00:00 2001 From: Andrew Zhen Ning Yong Date: Sat, 17 Apr 2021 12:44:27 +0100 Subject: [PATCH 144/399] Tadpole sign now in front of forward hopping term to be consistent with previous implementation and analytic form. --- .../fermion/implementation/CayleyFermion5DImplementation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index 323a6e01..e426356a 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -919,7 +919,7 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, tmp = Cshift(tmp,mu,1); Impl::multLinkField(Utmp,this->Umu,tmp,mu); - tmp = G_s[s]*( Utmp*ph - gmu*Utmp*ph ); // Forward hop + tmp = sign*G_s[s]*( Utmp*ph - gmu*Utmp*ph ); // Forward hop tmp = where((lcoor>=tmin),tmp,zz); // Mask the time L_Q = where((lcoor<=tmax),tmp,zz); // Position of current complicated @@ -933,7 +933,7 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, tmp = tmp *ph; tmp = Cshift(tmp,mu,-1); Impl::multLinkField(Utmp,this->Umu,tmp,mu+Nd); // Adjoint link - tmp = -1*sign*G_s[s]*( Utmp + gmu*Utmp ); + tmp = -1*G_s[s]*( Utmp + gmu*Utmp ); tmp = where((lcoor>=tmin+tshift),tmp,zz); // Mask the time L_Q += where((lcoor<=tmax+tshift),tmp,zz); // Position of current complicated From 86e11743ca3bb52ebfbedce8584b95b7be6f7fe0 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Tue, 20 Apr 2021 10:19:11 -0400 Subject: [PATCH 145/399] set twists --- tests/forces/Test_gp_rect_force.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/forces/Test_gp_rect_force.cc b/tests/forces/Test_gp_rect_force.cc index 98ebb2fa..e277ea6b 100644 --- a/tests/forces/Test_gp_rect_force.cc +++ b/tests/forces/Test_gp_rect_force.cc @@ -29,7 +29,6 @@ Author: paboyle using namespace std; using namespace Grid; - ; @@ -59,6 +58,10 @@ int main (int argc, char ** argv) double beta = 1.0; double c1 = 0.331; + const int nu = 1; + std::vector twists(Nd,0); + twists[nu] = 1; + ConjugateGimplD::setDirections(twists); ConjugatePlaqPlusRectangleActionR Action(beta,c1); //ConjugateWilsonGaugeActionR Action(beta); //WilsonGaugeActionR Action(beta); From 54c6b1376d9ee23a6b3cef6315d75afa212b8c39 Mon Sep 17 00:00:00 2001 From: Andrew Zhen Ning Yong Date: Wed, 21 Apr 2021 16:56:46 +0100 Subject: [PATCH 146/399] Quick fix of conserved current implementation in CayleyFermion5D. Now function treats current insertion with appropriate periodic boundary conditions in the mu=3 direction. --- .../implementation/CayleyFermion5DImplementation.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index e426356a..1b363846 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -828,6 +828,7 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, #if (!defined(GRID_HIP)) int tshift = (mu == Nd-1) ? 1 : 0; + unsigned int LLt = GridDefaultLatt()[Tp]; //////////////////////////////////////////////// // GENERAL CAYLEY CASE //////////////////////////////////////////////// @@ -933,8 +934,14 @@ void CayleyFermion5D::SeqConservedCurrent(PropagatorField &q_in, tmp = tmp *ph; tmp = Cshift(tmp,mu,-1); Impl::multLinkField(Utmp,this->Umu,tmp,mu+Nd); // Adjoint link - tmp = -1*G_s[s]*( Utmp + gmu*Utmp ); - tmp = where((lcoor>=tmin+tshift),tmp,zz); // Mask the time + tmp = -G_s[s]*( Utmp + gmu*Utmp ); + // Mask the time + if (tmax == LLt - 1 && tshift == 1){ // quick fix to include timeslice 0 if tmax + tshift is over the last timeslice + unsigned int t0 = 0; + tmp = where(((lcoor==t0) || (lcoor>=tmin+tshift)),tmp,zz); + } else { + tmp = where((lcoor>=tmin+tshift),tmp,zz); + } L_Q += where((lcoor<=tmax+tshift),tmp,zz); // Position of current complicated InsertSlice(L_Q, q_out, s , 0); From dbe210dd53405303cf57374c9b658902cbf8072a Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sun, 25 Apr 2021 10:25:59 -0400 Subject: [PATCH 147/399] Open the ens_id --- Grid/parallelIO/NerscIO.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Grid/parallelIO/NerscIO.h b/Grid/parallelIO/NerscIO.h index 3ebdf0cc..1ffda074 100644 --- a/Grid/parallelIO/NerscIO.h +++ b/Grid/parallelIO/NerscIO.h @@ -205,11 +205,22 @@ public: std::cout< + static inline void writeConfiguration(Lattice &Umu, + std::string file, + std::string ens_id = std::string("UKQCD"), + std::string ens_label = std::string("DWF")) + { + writeConfiguration(Umu,file,0,1,ens_id,ens_label); + } template static inline void writeConfiguration(Lattice &Umu, std::string file, int two_row, - int bits32) + int bits32, + std::string ens_id = std::string("UKQCD"), + std::string ens_label = std::string("DWF")) { typedef vLorentzColourMatrixD vobj; typedef typename vobj::scalar_object sobj; @@ -219,8 +230,8 @@ public: // Following should become arguments /////////////////////////////////////////// header.sequence_number = 1; - header.ensemble_id = "UKQCD"; - header.ensemble_label = "DWF"; + header.ensemble_id = ens_id; + header.ensemble_label = ens_label; typedef LorentzColourMatrixD fobj3D; typedef LorentzColour2x3D fobj2D; @@ -232,7 +243,7 @@ public: GaugeStats Stats; Stats(Umu,header); MachineCharacteristics(header); - uint64_t offset; + uint64_t offset; // Sod it -- always write 3x3 double header.floating_point = std::string("IEEE64BIG"); From 955a8113ded53fc55c204fc107832933ad120c2c Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sun, 25 Apr 2021 10:36:38 -0400 Subject: [PATCH 148/399] Expose label only to reduce number of parameters --- Grid/parallelIO/NerscIO.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Grid/parallelIO/NerscIO.h b/Grid/parallelIO/NerscIO.h index 1ffda074..04aae5d8 100644 --- a/Grid/parallelIO/NerscIO.h +++ b/Grid/parallelIO/NerscIO.h @@ -209,7 +209,6 @@ public: template static inline void writeConfiguration(Lattice &Umu, std::string file, - std::string ens_id = std::string("UKQCD"), std::string ens_label = std::string("DWF")) { writeConfiguration(Umu,file,0,1,ens_id,ens_label); @@ -219,7 +218,6 @@ public: std::string file, int two_row, int bits32, - std::string ens_id = std::string("UKQCD"), std::string ens_label = std::string("DWF")) { typedef vLorentzColourMatrixD vobj; @@ -230,7 +228,7 @@ public: // Following should become arguments /////////////////////////////////////////// header.sequence_number = 1; - header.ensemble_id = ens_id; + header.ensemble_id = std::string("UKQCD"); header.ensemble_label = ens_label; typedef LorentzColourMatrixD fobj3D; From d45c868656b4e28452946f90a6e71cdf12c21cf2 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sun, 25 Apr 2021 10:53:34 -0400 Subject: [PATCH 149/399] Change interface --- Grid/parallelIO/NerscIO.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/parallelIO/NerscIO.h b/Grid/parallelIO/NerscIO.h index 04aae5d8..99011e25 100644 --- a/Grid/parallelIO/NerscIO.h +++ b/Grid/parallelIO/NerscIO.h @@ -211,7 +211,7 @@ public: std::string file, std::string ens_label = std::string("DWF")) { - writeConfiguration(Umu,file,0,1,ens_id,ens_label); + writeConfiguration(Umu,file,0,1,ens_label); } template static inline void writeConfiguration(Lattice &Umu, From 8cd4263974060f9af3b002604d9036e2552cc307 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sun, 25 Apr 2021 22:20:37 -0400 Subject: [PATCH 150/399] Tests compile --- tests/debug/Test_heatbath_dwf_eofa.cc | 6 ++++-- tests/debug/Test_heatbath_dwf_eofa_gparity.cc | 7 +++++-- tests/debug/Test_heatbath_mobius_eofa.cc | 6 ++++-- tests/debug/Test_heatbath_mobius_eofa_gparity.cc | 6 ++++-- tests/forces/Test_momentum_filter.cc | 4 +++- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/debug/Test_heatbath_dwf_eofa.cc b/tests/debug/Test_heatbath_dwf_eofa.cc index 9d453a96..e1c18021 100644 --- a/tests/debug/Test_heatbath_dwf_eofa.cc +++ b/tests/debug/Test_heatbath_dwf_eofa.cc @@ -66,7 +66,9 @@ int main(int argc, char** argv) // Set up RNGs std::vector seeds4({1, 2, 3, 4}); std::vector seeds5({5, 6, 7, 8}); + GridSerialRNG sRNG; GridParallelRNG RNG5(FGrid); + sRNG.SeedFixedIntegers(seeds5); RNG5.SeedFixedIntegers(seeds5); GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); @@ -84,7 +86,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, false); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu,sRNG, RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } @@ -94,7 +96,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, true); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu,sRNG, RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } diff --git a/tests/debug/Test_heatbath_dwf_eofa_gparity.cc b/tests/debug/Test_heatbath_dwf_eofa_gparity.cc index 22cc1e90..7eabfc65 100644 --- a/tests/debug/Test_heatbath_dwf_eofa_gparity.cc +++ b/tests/debug/Test_heatbath_dwf_eofa_gparity.cc @@ -74,6 +74,9 @@ int main(int argc, char** argv) RNG5.SeedFixedIntegers(seeds5); GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + GridSerialRNG sRNG; + RNG4.SeedFixedIntegers(seeds4); + sRNG.SeedFixedIntegers(seeds5); // Random gauge field LatticeGaugeField Umu(UGrid); @@ -90,7 +93,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, false); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu,sRNG, RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } @@ -100,7 +103,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, true); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu,sRNG, RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } diff --git a/tests/debug/Test_heatbath_mobius_eofa.cc b/tests/debug/Test_heatbath_mobius_eofa.cc index 4cf4bf53..48806642 100644 --- a/tests/debug/Test_heatbath_mobius_eofa.cc +++ b/tests/debug/Test_heatbath_mobius_eofa.cc @@ -68,8 +68,10 @@ int main(int argc, char** argv) // Set up RNGs std::vector seeds4({1, 2, 3, 4}); std::vector seeds5({5, 6, 7, 8}); + GridSerialRNG sRNG; GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); + sRNG.SeedFixedIntegers(seeds5); GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); @@ -86,7 +88,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, false); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu, sRNG,RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } @@ -96,7 +98,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, true); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu, sRNG,RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } diff --git a/tests/debug/Test_heatbath_mobius_eofa_gparity.cc b/tests/debug/Test_heatbath_mobius_eofa_gparity.cc index 2fcb4b9f..52447e5e 100644 --- a/tests/debug/Test_heatbath_mobius_eofa_gparity.cc +++ b/tests/debug/Test_heatbath_mobius_eofa_gparity.cc @@ -73,7 +73,9 @@ int main(int argc, char** argv) std::vector seeds4({1, 2, 3, 4}); std::vector seeds5({5, 6, 7, 8}); GridParallelRNG RNG5(FGrid); + GridSerialRNG sRNG; RNG5.SeedFixedIntegers(seeds5); + sRNG.SeedFixedIntegers(seeds5); GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); @@ -91,7 +93,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, false); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu, sRNG, RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } @@ -101,7 +103,7 @@ int main(int argc, char** argv) ConjugateGradient CG(1.0e-12, 5000); ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, true); - Meofa.refresh(Umu, RNG5); + Meofa.refresh(Umu, sRNG, RNG5); printf(" = %1.15e\n", Meofa.S(Umu)); } diff --git a/tests/forces/Test_momentum_filter.cc b/tests/forces/Test_momentum_filter.cc index 856ea0f2..794b5fa0 100644 --- a/tests/forces/Test_momentum_filter.cc +++ b/tests/forces/Test_momentum_filter.cc @@ -61,7 +61,9 @@ int main (int argc, char ** argv) std::vector seeds({1,2,3,4}); GridParallelRNG pRNG(&Grid); + GridSerialRNG sRNG; pRNG.SeedFixedIntegers(seeds); + sRNG.SeedFixedIntegers(seeds); typedef PeriodicGimplR Gimpl; typedef WilsonGaugeAction GaugeAction; @@ -115,7 +117,7 @@ int main (int argc, char ** argv) integrator.setMomentumFilter(filter); - integrator.refresh(U, pRNG); //doesn't actually change the gauge field + integrator.refresh(U, sRNG, pRNG); //doesn't actually change the gauge field //Check the momentum is zero on the boundary const auto &P = integrator.getMomentum(); From 009ccd581ede8faf0ba748fa49a1757419106e23 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 26 Apr 2021 10:36:33 +0100 Subject: [PATCH 151/399] bugfix 3D stout smearing --- Grid/qcd/smearing/StoutSmearing.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index ed2ccdb6..629f81e2 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -89,11 +89,12 @@ public: SmearBase->smear(C, U); for (int mu = 0; mu < Nd; mu++) { - if( mu == OrthogDim ) + Umu = peekLorentz(U, mu); + if( mu == OrthogDim ){ tmp = 1.0; // Don't smear in the orthogonal direction + } else { tmp = peekLorentz(C, mu); - Umu = peekLorentz(U, mu); iq_mu = Ta( tmp * adj(Umu)); // iq_mu = Ta(Omega_mu) to match the signs with the paper From cf2923d5ddb9c190ebb78efadab281e5a06ba247 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 27 Apr 2021 16:53:37 +0100 Subject: [PATCH 152/399] Jamie's fix --- Grid/qcd/smearing/StoutSmearing.h | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 629f81e2..6ee78e8c 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -85,22 +85,18 @@ public: std::cout << GridLogDebug << "Stout smearing started\n"; - // Smear the configurations + // C contains the staples multiplied by some rho + u_smr = U ; // set the smeared field to the current gauge field SmearBase->smear(C, U); for (int mu = 0; mu < Nd; mu++) { + if( mu == OrthogDim ) continue ; + // u_smr = exp(iQ_mu)*U_mu apart from Orthogdim Umu = peekLorentz(U, mu); - if( mu == OrthogDim ){ - tmp = 1.0; // Don't smear in the orthogonal direction - } - else { - tmp = peekLorentz(C, mu); - iq_mu = Ta( - tmp * - adj(Umu)); // iq_mu = Ta(Omega_mu) to match the signs with the paper - exponentiate_iQ(tmp, iq_mu); - } - pokeLorentz(u_smr, tmp * Umu, mu); // u_smr = exp(iQ_mu)*U_mu + tmp = peekLorentz(C, mu); + iq_mu = Ta( tmp * adj(Umu)); + exponentiate_iQ(tmp, iq_mu); + pokeLorentz(u_smr, tmp * Umu, mu); } std::cout << GridLogDebug << "Stout smearing completed\n"; }; From 834f536b5f426aa0b3a334a89b37d8da39fb4238 Mon Sep 17 00:00:00 2001 From: u61464 Date: Tue, 4 May 2021 08:40:18 -0700 Subject: [PATCH 153/399] Fastest option on SyCL is now std::complex --- Grid/tensors/Tensor_SIMT.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Grid/tensors/Tensor_SIMT.h b/Grid/tensors/Tensor_SIMT.h index 672f385f..0a7d3382 100644 --- a/Grid/tensors/Tensor_SIMT.h +++ b/Grid/tensors/Tensor_SIMT.h @@ -65,7 +65,8 @@ void coalescedWriteNonTemporal(vobj & __restrict__ vec,const vobj & __restrict__ #else -#ifndef GRID_SYCL +//#ifndef GRID_SYCL +#if 1 // Use the scalar as our own complex on GPU ... thrust::complex or std::complex template = 0> accelerator_inline typename vsimd::scalar_type From 8cfc7342cde4b93d1de8f41a6f909ddd6d5d351f Mon Sep 17 00:00:00 2001 From: u61464 Date: Wed, 5 May 2021 14:17:18 -0700 Subject: [PATCH 154/399] staggered hand unroll read coalesce --- Grid/qcd/action/fermion/Fermion.h | 6 - Grid/qcd/action/fermion/FermionOperatorImpl.h | 5 +- .../implementation/StaggeredKernelsAsm.h | 5 +- .../implementation/StaggeredKernelsHand.h | 203 +++++++++--------- 4 files changed, 106 insertions(+), 113 deletions(-) diff --git a/Grid/qcd/action/fermion/Fermion.h b/Grid/qcd/action/fermion/Fermion.h index 16252340..09777204 100644 --- a/Grid/qcd/action/fermion/Fermion.h +++ b/Grid/qcd/action/fermion/Fermion.h @@ -291,12 +291,6 @@ typedef ImprovedStaggeredFermion5D ImprovedStaggeredFermion5DR; typedef ImprovedStaggeredFermion5D ImprovedStaggeredFermion5DF; typedef ImprovedStaggeredFermion5D ImprovedStaggeredFermion5DD; -#ifndef GRID_CUDA -typedef ImprovedStaggeredFermion5D ImprovedStaggeredFermionVec5dR; -typedef ImprovedStaggeredFermion5D ImprovedStaggeredFermionVec5dF; -typedef ImprovedStaggeredFermion5D ImprovedStaggeredFermionVec5dD; -#endif - NAMESPACE_END(Grid); //////////////////// diff --git a/Grid/qcd/action/fermion/FermionOperatorImpl.h b/Grid/qcd/action/fermion/FermionOperatorImpl.h index 9345c0e6..56aaca12 100644 --- a/Grid/qcd/action/fermion/FermionOperatorImpl.h +++ b/Grid/qcd/action/fermion/FermionOperatorImpl.h @@ -183,7 +183,8 @@ NAMESPACE_CHECK(ImplStaggered); ///////////////////////////////////////////////////////////////////////////// // Single flavour one component spinors with colour index. 5d vec ///////////////////////////////////////////////////////////////////////////// -#include -NAMESPACE_CHECK(ImplStaggered5dVec); +// Deprecate Vec5d +//#include +//NAMESPACE_CHECK(ImplStaggered5dVec); diff --git a/Grid/qcd/action/fermion/implementation/StaggeredKernelsAsm.h b/Grid/qcd/action/fermion/implementation/StaggeredKernelsAsm.h index 63fd2a2f..e9cacbcf 100644 --- a/Grid/qcd/action/fermion/implementation/StaggeredKernelsAsm.h +++ b/Grid/qcd/action/fermion/implementation/StaggeredKernelsAsm.h @@ -680,7 +680,8 @@ void StaggeredKernels::DhopSiteAsm(StencilView &st, gauge2 =(uint64_t)&UU[sU]( Z ); \ gauge3 =(uint64_t)&UU[sU]( T ); - +#undef STAG_VEC5D +#ifdef STAG_VEC5D // This is the single precision 5th direction vectorised kernel #include template <> void StaggeredKernels::DhopSiteAsm(StencilView &st, @@ -790,7 +791,7 @@ template <> void StaggeredKernels::DhopSiteAsm(StencilView #endif } - +#endif #define PERMUTE_DIR3 __asm__ ( \ diff --git a/Grid/qcd/action/fermion/implementation/StaggeredKernelsHand.h b/Grid/qcd/action/fermion/implementation/StaggeredKernelsHand.h index 6bcb22b4..2b6087bc 100644 --- a/Grid/qcd/action/fermion/implementation/StaggeredKernelsHand.h +++ b/Grid/qcd/action/fermion/implementation/StaggeredKernelsHand.h @@ -32,25 +32,50 @@ Author: paboyle NAMESPACE_BEGIN(Grid); -#define LOAD_CHI(b) \ +#ifdef GRID_SIMT + +#define LOAD_CHI(ptype,b) \ + const SiteSpinor & ref (b[offset]); \ + Chi_0=coalescedReadPermute(ref()()(0),perm,lane); \ + Chi_1=coalescedReadPermute(ref()()(1),perm,lane); \ + Chi_2=coalescedReadPermute(ref()()(2),perm,lane); + +#define LOAD_CHI_COMMS(b) \ const SiteSpinor & ref (b[offset]); \ - Chi_0=ref()()(0);\ - Chi_1=ref()()(1);\ - Chi_2=ref()()(2); + Chi_0=coalescedRead(ref()()(0),lane); \ + Chi_1=coalescedRead(ref()()(1),lane); \ + Chi_2=coalescedRead(ref()()(2),lane); + +#define PERMUTE_DIR(dir) ; +#else +#define LOAD_CHI(ptype,b) LOAD_CHI_COMMS(b) + +#define LOAD_CHI_COMMS(b) \ + const SiteSpinor & ref (b[offset]); \ + Chi_0=ref()()(0); \ + Chi_1=ref()()(1); \ + Chi_2=ref()()(2); + +#define PERMUTE_DIR(dir) \ + permute##dir(Chi_0,Chi_0); \ + permute##dir(Chi_1,Chi_1); \ + permute##dir(Chi_2,Chi_2); + +#endif // To splat or not to splat depends on the implementation #define MULT(A,UChi) \ auto & ref(U[sU](A)); \ - Impl::loadLinkElement(U_00,ref()(0,0)); \ - Impl::loadLinkElement(U_10,ref()(1,0)); \ - Impl::loadLinkElement(U_20,ref()(2,0)); \ - Impl::loadLinkElement(U_01,ref()(0,1)); \ - Impl::loadLinkElement(U_11,ref()(1,1)); \ - Impl::loadLinkElement(U_21,ref()(2,1)); \ - Impl::loadLinkElement(U_02,ref()(0,2)); \ - Impl::loadLinkElement(U_12,ref()(1,2)); \ - Impl::loadLinkElement(U_22,ref()(2,2)); \ + U_00=coalescedRead(ref()(0,0),lane); \ + U_10=coalescedRead(ref()(1,0),lane); \ + U_20=coalescedRead(ref()(2,0),lane); \ + U_01=coalescedRead(ref()(0,1),lane); \ + U_11=coalescedRead(ref()(1,1),lane); \ + U_21=coalescedRead(ref()(2,1),lane); \ + U_02=coalescedRead(ref()(0,2),lane); \ + U_12=coalescedRead(ref()(1,2),lane); \ + U_22=coalescedRead(ref()(2,2),lane); \ UChi ## _0 = U_00*Chi_0; \ UChi ## _1 = U_10*Chi_0;\ UChi ## _2 = U_20*Chi_0;\ @@ -63,15 +88,15 @@ NAMESPACE_BEGIN(Grid); #define MULT_ADD(U,A,UChi) \ auto & ref(U[sU](A)); \ - Impl::loadLinkElement(U_00,ref()(0,0)); \ - Impl::loadLinkElement(U_10,ref()(1,0)); \ - Impl::loadLinkElement(U_20,ref()(2,0)); \ - Impl::loadLinkElement(U_01,ref()(0,1)); \ - Impl::loadLinkElement(U_11,ref()(1,1)); \ - Impl::loadLinkElement(U_21,ref()(2,1)); \ - Impl::loadLinkElement(U_02,ref()(0,2)); \ - Impl::loadLinkElement(U_12,ref()(1,2)); \ - Impl::loadLinkElement(U_22,ref()(2,2)); \ + U_00=coalescedRead(ref()(0,0),lane); \ + U_10=coalescedRead(ref()(1,0),lane); \ + U_20=coalescedRead(ref()(2,0),lane); \ + U_01=coalescedRead(ref()(0,1),lane); \ + U_11=coalescedRead(ref()(1,1),lane); \ + U_21=coalescedRead(ref()(2,1),lane); \ + U_02=coalescedRead(ref()(0,2),lane); \ + U_12=coalescedRead(ref()(1,2),lane); \ + U_22=coalescedRead(ref()(2,2),lane); \ UChi ## _0 += U_00*Chi_0; \ UChi ## _1 += U_10*Chi_0;\ UChi ## _2 += U_20*Chi_0;\ @@ -83,24 +108,18 @@ NAMESPACE_BEGIN(Grid); UChi ## _2 += U_22*Chi_2; -#define PERMUTE_DIR(dir) \ - permute##dir(Chi_0,Chi_0); \ - permute##dir(Chi_1,Chi_1); \ - permute##dir(Chi_2,Chi_2); - - #define HAND_STENCIL_LEG_BASE(Dir,Perm,skew) \ SE=st.GetEntry(ptype,Dir+skew,sF); \ offset = SE->_offset; \ local = SE->_is_local; \ perm = SE->_permute; \ if ( local ) { \ - LOAD_CHI(in); \ + LOAD_CHI(Perm,in); \ if ( perm) { \ PERMUTE_DIR(Perm); \ } \ } else { \ - LOAD_CHI(buf); \ + LOAD_CHI_COMMS(buf); \ } #define HAND_STENCIL_LEG_BEGIN(Dir,Perm,skew,even) \ @@ -116,19 +135,18 @@ NAMESPACE_BEGIN(Grid); } - #define HAND_STENCIL_LEG_INT(U,Dir,Perm,skew,even) \ SE=st.GetEntry(ptype,Dir+skew,sF); \ offset = SE->_offset; \ local = SE->_is_local; \ perm = SE->_permute; \ if ( local ) { \ - LOAD_CHI(in); \ + LOAD_CHI(Perm,in); \ if ( perm) { \ PERMUTE_DIR(Perm); \ } \ } else if ( st.same_node[Dir] ) { \ - LOAD_CHI(buf); \ + LOAD_CHI_COMMS(buf); \ } \ if (local || st.same_node[Dir] ) { \ MULT_ADD(U,Dir,even); \ @@ -140,10 +158,32 @@ NAMESPACE_BEGIN(Grid); local = SE->_is_local; \ if ((!local) && (!st.same_node[Dir]) ) { \ nmu++; \ - { LOAD_CHI(buf); } \ + { LOAD_CHI_COMMS(buf); } \ { MULT_ADD(U,Dir,even); } \ } +#define HAND_DECLARATIONS(Simd) \ + Simd even_0; \ + Simd even_1; \ + Simd even_2; \ + Simd odd_0; \ + Simd odd_1; \ + Simd odd_2; \ + \ + Simd Chi_0; \ + Simd Chi_1; \ + Simd Chi_2; \ + \ + Simd U_00; \ + Simd U_10; \ + Simd U_20; \ + Simd U_01; \ + Simd U_11; \ + Simd U_21; \ + Simd U_02; \ + Simd U_12; \ + Simd U_22; + template template accelerator_inline @@ -155,28 +195,14 @@ void StaggeredKernels::DhopSiteHand(StencilView &st, typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - Simd even_0; // 12 regs on knc - Simd even_1; - Simd even_2; - Simd odd_0; // 12 regs on knc - Simd odd_1; - Simd odd_2; - Simd Chi_0; // two spinor; 6 regs - Simd Chi_1; - Simd Chi_2; - - Simd U_00; // two rows of U matrix - Simd U_10; - Simd U_20; - Simd U_01; - Simd U_11; - Simd U_21; // 2 reg left. - Simd U_02; - Simd U_12; - Simd U_22; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + typedef decltype( coalescedRead( in[0]()()(0) )) Simt; + HAND_DECLARATIONS(Simt); - SiteSpinor result; + typedef decltype( coalescedRead( in[0] )) calcSiteSpinor; + calcSiteSpinor result; int offset,local,perm, ptype; StencilEntry *SE; @@ -215,7 +241,7 @@ void StaggeredKernels::DhopSiteHand(StencilView &st, result()()(1) = even_1 + odd_1; result()()(2) = even_2 + odd_2; } - vstream(out[sF],result); + coalescedWrite(out[sF],result); } } @@ -230,28 +256,13 @@ void StaggeredKernels::DhopSiteHandInt(StencilView &st, typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - Simd even_0; // 12 regs on knc - Simd even_1; - Simd even_2; - Simd odd_0; // 12 regs on knc - Simd odd_1; - Simd odd_2; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + typedef decltype( coalescedRead( in[0]()()(0) )) Simt; + HAND_DECLARATIONS(Simt); - Simd Chi_0; // two spinor; 6 regs - Simd Chi_1; - Simd Chi_2; - - Simd U_00; // two rows of U matrix - Simd U_10; - Simd U_20; - Simd U_01; - Simd U_11; - Simd U_21; // 2 reg left. - Simd U_02; - Simd U_12; - Simd U_22; - - SiteSpinor result; + typedef decltype( coalescedRead( in[0] )) calcSiteSpinor; + calcSiteSpinor result; int offset, ptype, local, perm; StencilEntry *SE; @@ -261,8 +272,8 @@ void StaggeredKernels::DhopSiteHandInt(StencilView &st, // int sF=s+LLs*sU; { - even_0 = Zero(); even_1 = Zero(); even_2 = Zero(); - odd_0 = Zero(); odd_1 = Zero(); odd_2 = Zero(); + zeroit(even_0); zeroit(even_1); zeroit(even_2); + zeroit(odd_0); zeroit(odd_1); zeroit(odd_2); skew = 0; HAND_STENCIL_LEG_INT(U,Xp,3,skew,even); @@ -294,7 +305,7 @@ void StaggeredKernels::DhopSiteHandInt(StencilView &st, result()()(1) = even_1 + odd_1; result()()(2) = even_2 + odd_2; } - vstream(out[sF],result); + coalescedWrite(out[sF],result); } } @@ -309,28 +320,13 @@ void StaggeredKernels::DhopSiteHandExt(StencilView &st, typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; - Simd even_0; // 12 regs on knc - Simd even_1; - Simd even_2; - Simd odd_0; // 12 regs on knc - Simd odd_1; - Simd odd_2; + const int Nsimd = SiteHalfSpinor::Nsimd(); + const int lane=acceleratorSIMTlane(Nsimd); + typedef decltype( coalescedRead( in[0]()()(0) )) Simt; + HAND_DECLARATIONS(Simt); - Simd Chi_0; // two spinor; 6 regs - Simd Chi_1; - Simd Chi_2; - - Simd U_00; // two rows of U matrix - Simd U_10; - Simd U_20; - Simd U_01; - Simd U_11; - Simd U_21; // 2 reg left. - Simd U_02; - Simd U_12; - Simd U_22; - - SiteSpinor result; + typedef decltype( coalescedRead( in[0] )) calcSiteSpinor; + calcSiteSpinor result; int offset, ptype, local; StencilEntry *SE; @@ -340,8 +336,8 @@ void StaggeredKernels::DhopSiteHandExt(StencilView &st, // int sF=s+LLs*sU; { - even_0 = Zero(); even_1 = Zero(); even_2 = Zero(); - odd_0 = Zero(); odd_1 = Zero(); odd_2 = Zero(); + zeroit(even_0); zeroit(even_1); zeroit(even_2); + zeroit(odd_0); zeroit(odd_1); zeroit(odd_2); int nmu=0; skew = 0; HAND_STENCIL_LEG_EXT(U,Xp,3,skew,even); @@ -374,7 +370,7 @@ void StaggeredKernels::DhopSiteHandExt(StencilView &st, result()()(1) = even_1 + odd_1; result()()(2) = even_2 + odd_2; } - out[sF] = out[sF] + result; + coalescedWrite(out[sF] , out(sF)+ result); } } } @@ -397,6 +393,7 @@ void StaggeredKernels::DhopSiteHandExt(StencilView &st, const FermionFieldView &in, FermionFieldView &out, int dag); \ */ #undef LOAD_CHI +#undef HAND_DECLARATIONS NAMESPACE_END(Grid); From 244b4aa07fe80867bab2ec7b59c406b7e2db2b71 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 21 May 2021 20:08:56 +0100 Subject: [PATCH 155/399] Serialise std::vector of numeric types as multidimensional object if size is regular ... or individually if ragged --- Grid/serialisation/Hdf5IO.h | 77 +++++++++++++++++++++----------- Grid/serialisation/VectorUtils.h | 19 ++++++++ 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 19537599..ed09bc5c 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -34,11 +34,13 @@ namespace Grid template void writeDefault(const std::string &s, const U &x); template + void writeRagged(const std::string &s, const std::vector &x); + template typename std::enable_if>::is_number, void>::type writeDefault(const std::string &s, const std::vector &x); template typename std::enable_if>::is_number, void>::type - writeDefault(const std::string &s, const std::vector &x); + writeDefault(const std::string &s, const std::vector &x) { writeRagged(s, x); } template void writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements); H5NS::Group & getGroup(void); @@ -64,11 +66,13 @@ namespace Grid template void readDefault(const std::string &s, U &output); template + void readRagged(const std::string &s, std::vector &x); + template typename std::enable_if>::is_number, void>::type readDefault(const std::string &s, std::vector &x); template typename std::enable_if>::is_number, void>::type - readDefault(const std::string &s, std::vector &x); + readDefault(const std::string &s, std::vector &x) { readRagged(s, x); } template void readMultiDim(const std::string &s, std::vector &buf, std::vector &dim); H5NS::Group & getGroup(void); @@ -179,21 +183,27 @@ namespace Grid typename std::enable_if>::is_number, void>::type Hdf5Writer::writeDefault(const std::string &s, const std::vector &x) { - // alias to element type - typedef typename element>::type Element; - - // flatten the vector and getting dimensions - Flatten> flat(x); - std::vector dim; - const auto &flatx = flat.getFlatVector(); - for (auto &d: flat.getDim()) - dim.push_back(d); - writeMultiDim(s, dim, &flatx[0], flatx.size()); + if (isFlat(x)) + { + // alias to element type + typedef typename element>::type Element; + + // flatten the vector and getting dimensions + Flatten> flat(x); + std::vector dim; + const auto &flatx = flat.getFlatVector(); + for (auto &d: flat.getDim()) + dim.push_back(d); + writeMultiDim(s, dim, &flatx[0], flatx.size()); + } + else + { + writeRagged(s, x); + } } template - typename std::enable_if>::is_number, void>::type - Hdf5Writer::writeDefault(const std::string &s, const std::vector &x) + void Hdf5Writer::writeRagged(const std::string &s, const std::vector &x) { push(s); writeSingleAttribute(x.size(), HDF5_GRID_GUARD "vector_size", @@ -275,22 +285,39 @@ namespace Grid typename std::enable_if>::is_number, void>::type Hdf5Reader::readDefault(const std::string &s, std::vector &x) { - // alias to element type - typedef typename element>::type Element; + bool bRagged{ false }; + H5E_auto2_t h5at; + void * f5at_p; + ::H5::Exception::getAutoPrint(h5at, &f5at_p); + ::H5::Exception::dontPrint(); + try { + push(s); + bRagged = group_.attrExists(HDF5_GRID_GUARD "vector_size"); + pop(); + } catch(...) {} + ::H5::Exception::setAutoPrint(h5at, f5at_p); + if (bRagged) + { + readRagged(s, x); + } + else + { + // alias to element type + typedef typename element>::type Element; - std::vector dim; - std::vector buf; - readMultiDim( s, buf, dim ); + std::vector dim; + std::vector buf; + readMultiDim( s, buf, dim ); - // reconstruct the multidimensional vector - Reconstruct> r(buf, dim); - - x = r.getVector(); + // reconstruct the multidimensional vector + Reconstruct> r(buf, dim); + + x = r.getVector(); + } } template - typename std::enable_if>::is_number, void>::type - Hdf5Reader::readDefault(const std::string &s, std::vector &x) + void Hdf5Reader::readRagged(const std::string &s, std::vector &x) { uint64_t size; diff --git a/Grid/serialisation/VectorUtils.h b/Grid/serialisation/VectorUtils.h index dd5ff0b8..3327b4f0 100644 --- a/Grid/serialisation/VectorUtils.h +++ b/Grid/serialisation/VectorUtils.h @@ -459,6 +459,25 @@ namespace Grid { return os; } + + // In general, scalar types are considered "flat" (regularly shaped) + template + bool isFlat(const T &t) { return true; } + + // Return non-zero if all dimensions of this std::vector> are regularly shaped + template + bool isFlat(const std::vector> &v) + { + // Make sure all of my rows are the same size + for (std::size_t i = 1; i < v.size(); ++i) + { + if (v[i].size() != v[0].size() || !isFlat(v[i])) + { + return false; + } + } + return true; + } } // helper function to read space-separated values From 9b73dacf50a1c1528b0b05d06fabf92d00df9c80 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 22 May 2021 04:34:32 +0100 Subject: [PATCH 156/399] First row might still be ragged if multi dimensional. attrExists() doesn't throw, but easier to wrap in try ... catch than to explain in comment. --- Grid/serialisation/Hdf5IO.h | 6 ++++-- Grid/serialisation/VectorUtils.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index ed09bc5c..8897af57 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -291,8 +291,10 @@ namespace Grid ::H5::Exception::getAutoPrint(h5at, &f5at_p); ::H5::Exception::dontPrint(); try { - push(s); - bRagged = group_.attrExists(HDF5_GRID_GUARD "vector_size"); + push(s); // This is what might throw + try { + bRagged = group_.attrExists(HDF5_GRID_GUARD "vector_size"); + } catch(...) {} pop(); } catch(...) {} ::H5::Exception::setAutoPrint(h5at, f5at_p); diff --git a/Grid/serialisation/VectorUtils.h b/Grid/serialisation/VectorUtils.h index 3327b4f0..8982ba9a 100644 --- a/Grid/serialisation/VectorUtils.h +++ b/Grid/serialisation/VectorUtils.h @@ -469,7 +469,7 @@ namespace Grid { bool isFlat(const std::vector> &v) { // Make sure all of my rows are the same size - for (std::size_t i = 1; i < v.size(); ++i) + for (std::size_t i = 0; i < v.size(); ++i) { if (v[i].size() != v[0].size() || !isFlat(v[i])) { From ef0ddd5d04304d882a246cafdbcf975cea6f1010 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 24 May 2021 18:43:55 +0100 Subject: [PATCH 157/399] std::vector serialisation in hdf5 uses a different format if the vector is ragged. When reading back std::vector we need to check which format we're reading (since we don't know a priori) and this involves looking for attributes that may not exist. The c++ API: a) throws; and b) prints voluminous logging. Switched to non-throwing, non-logging, C version of the API after code review. --- Grid/serialisation/Hdf5IO.h | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 8897af57..6f61ad76 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -285,20 +285,8 @@ namespace Grid typename std::enable_if>::is_number, void>::type Hdf5Reader::readDefault(const std::string &s, std::vector &x) { - bool bRagged{ false }; - H5E_auto2_t h5at; - void * f5at_p; - ::H5::Exception::getAutoPrint(h5at, &f5at_p); - ::H5::Exception::dontPrint(); - try { - push(s); // This is what might throw - try { - bRagged = group_.attrExists(HDF5_GRID_GUARD "vector_size"); - } catch(...) {} - pop(); - } catch(...) {} - ::H5::Exception::setAutoPrint(h5at, f5at_p); - if (bRagged) + if (H5Lexists (group_.getId(), s.c_str(), H5P_DEFAULT) > 0 + && H5Aexists_by_name(group_.getId(), s.c_str(), HDF5_GRID_GUARD "vector_size", H5P_DEFAULT ) > 0) { readRagged(s, x); } From b5aeae526fb9857f170feba58dfab14d16e4c955 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Mon, 29 Mar 2021 21:44:14 +0200 Subject: [PATCH 158/399] Make Cshift fields static to avoid repeated reallocaate overhead --- Grid/cshift/Cshift_mpi.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Grid/cshift/Cshift_mpi.h b/Grid/cshift/Cshift_mpi.h index 375d004e..7e93e260 100644 --- a/Grid/cshift/Cshift_mpi.h +++ b/Grid/cshift/Cshift_mpi.h @@ -122,8 +122,8 @@ template void Cshift_comms(Lattice &ret,const Lattice &r assert(shift_slice_nblock[dimension]*rhs.Grid()->_slice_block[dimension]; - cshiftVector send_buf(buffer_size); - cshiftVector recv_buf(buffer_size); + static cshiftVector send_buf; send_buf.resize(buffer_size); + static cshiftVector recv_buf; recv_buf.resize(buffer_size); int cb= (cbmask==0x2)? Odd : Even; int sshift= rhs.Grid()->CheckerBoardShiftForCB(rhs.Checkerboard(),dimension,shift,cb); @@ -198,8 +198,8 @@ template void Cshift_comms_simd(Lattice &ret,const Lattice_slice_nblock[dimension]*grid->_slice_block[dimension]; // int words = sizeof(vobj)/sizeof(vector_type); - std::vector > send_buf_extract(Nsimd); - std::vector > recv_buf_extract(Nsimd); + static std::vector > send_buf_extract; send_buf_extract.resize(Nsimd); + static std::vector > recv_buf_extract; recv_buf_extract.resize(Nsimd); scalar_object * recv_buf_extract_mpi; scalar_object * send_buf_extract_mpi; @@ -294,8 +294,8 @@ template void Cshift_comms(Lattice &ret,const Lattice &r assert(shift_slice_nblock[dimension]*rhs.Grid()->_slice_block[dimension]; - cshiftVector send_buf_v(buffer_size); - cshiftVector recv_buf_v(buffer_size); + static cshiftVector send_buf_v; send_buf_v.resize(buffer_size); + static cshiftVector recv_buf_v; recv_buf_v.resize(buffer_size); vobj *send_buf; vobj *recv_buf; { @@ -381,8 +381,8 @@ template void Cshift_comms_simd(Lattice &ret,const Lattice_slice_nblock[dimension]*grid->_slice_block[dimension]; // int words = sizeof(vobj)/sizeof(vector_type); - std::vector > send_buf_extract(Nsimd); - std::vector > recv_buf_extract(Nsimd); + static std::vector > send_buf_extract; send_buf_extract.resize(Nsimd); + static std::vector > recv_buf_extract; recv_buf_extract.resize(Nsimd); scalar_object * recv_buf_extract_mpi; scalar_object * send_buf_extract_mpi; { From 7b8923225141b0cf3fcf6b842df5b4ee1ba01b1b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 30 May 2021 20:27:53 +0100 Subject: [PATCH 159/399] Extended HDF5 serialisation of std::vector where T now also includes Grid scalar/vector/matrix Changed VectorUtils element traits to is_flattenable, because: a) contract changed on what it does; and b) no other Grid dependencies on element. Needs review. Initial tests work ... needs proper regression testing. --- Grid/serialisation/Hdf5IO.h | 28 ++-- Grid/serialisation/VectorUtils.h | 235 ++++++++++++++++++------------- 2 files changed, 151 insertions(+), 112 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 6f61ad76..6b77ba3c 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -36,10 +36,10 @@ namespace Grid template void writeRagged(const std::string &s, const std::vector &x); template - typename std::enable_if>::is_number, void>::type + typename std::enable_if>::value>::type writeDefault(const std::string &s, const std::vector &x); template - typename std::enable_if>::is_number, void>::type + typename std::enable_if>::value>::type writeDefault(const std::string &s, const std::vector &x) { writeRagged(s, x); } template void writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements); @@ -68,10 +68,10 @@ namespace Grid template void readRagged(const std::string &s, std::vector &x); template - typename std::enable_if>::is_number, void>::type + typename std::enable_if>::value>::type readDefault(const std::string &s, std::vector &x); template - typename std::enable_if>::is_number, void>::type + typename std::enable_if>::value>::type readDefault(const std::string &s, std::vector &x) { readRagged(s, x); } template void readMultiDim(const std::string &s, std::vector &buf, std::vector &dim); @@ -180,13 +180,13 @@ namespace Grid } template - typename std::enable_if>::is_number, void>::type + typename std::enable_if>::value>::type Hdf5Writer::writeDefault(const std::string &s, const std::vector &x) { - if (isFlat(x)) + if (isRegularShape(x)) { // alias to element type - typedef typename element>::type Element; + using Scalar = typename is_flattenable>::type; // flatten the vector and getting dimensions Flatten> flat(x); @@ -194,7 +194,7 @@ namespace Grid const auto &flatx = flat.getFlatVector(); for (auto &d: flat.getDim()) dim.push_back(d); - writeMultiDim(s, dim, &flatx[0], flatx.size()); + writeMultiDim(s, dim, &flatx[0], flatx.size()); } else { @@ -239,7 +239,7 @@ namespace Grid void Hdf5Reader::readMultiDim(const std::string &s, std::vector &buf, std::vector &dim) { // alias to element type - typedef typename element>::type Element; + using Scalar = typename is_flattenable>::type; // read the dimensions H5NS::DataSpace dataSpace; @@ -270,19 +270,19 @@ namespace Grid H5NS::DataSet dataSet; dataSet = group_.openDataSet(s); - dataSet.read(buf.data(), Hdf5Type::type()); + dataSet.read(buf.data(), Hdf5Type::type()); } else { H5NS::Attribute attribute; attribute = group_.openAttribute(s); - attribute.read(Hdf5Type::type(), buf.data()); + attribute.read(Hdf5Type::type(), buf.data()); } } template - typename std::enable_if>::is_number, void>::type + typename std::enable_if>::value>::type Hdf5Reader::readDefault(const std::string &s, std::vector &x) { if (H5Lexists (group_.getId(), s.c_str(), H5P_DEFAULT) > 0 @@ -293,10 +293,10 @@ namespace Grid else { // alias to element type - typedef typename element>::type Element; + using Scalar = typename is_flattenable>::type; std::vector dim; - std::vector buf; + std::vector buf; readMultiDim( s, buf, dim ); // reconstruct the multidimensional vector diff --git a/Grid/serialisation/VectorUtils.h b/Grid/serialisation/VectorUtils.h index 8982ba9a..10471113 100644 --- a/Grid/serialisation/VectorUtils.h +++ b/Grid/serialisation/VectorUtils.h @@ -236,21 +236,36 @@ namespace Grid { } } - // Vector element trait ////////////////////////////////////////////////////// - template - struct element + // is_flattenable::value is true if T is a std::vector<> which can be flattened ////////////////////// + template + struct is_flattenable : std::false_type { - typedef T type; - static constexpr bool is_number = false; + using type = T; + using grid_type = T; + static constexpr int vecRank = 0; + static constexpr bool isGridTensor = false; + static constexpr bool children_flattenable = std::is_arithmetic::value or is_complex::value; }; - + template - struct element> + struct is_flattenable::value>::type> : std::false_type { - typedef typename element::type type; - static constexpr bool is_number = std::is_arithmetic::value - or is_complex::value - or element::is_number; + using type = typename GridTypeMapper::scalar_type; + using grid_type = T; + static constexpr int vecRank = 0; + static constexpr bool isGridTensor = true; + static constexpr bool children_flattenable = true; + }; + + template + struct is_flattenable, typename std::enable_if::children_flattenable>::type> + : std::true_type + { + using type = typename is_flattenable::type; + using grid_type = typename is_flattenable::grid_type; + static constexpr bool isGridTensor = is_flattenable::isGridTensor; + static constexpr int vecRank = is_flattenable::vecRank + 1; + static constexpr bool children_flattenable = true; }; // Vector flattening utility class //////////////////////////////////////////// @@ -259,23 +274,30 @@ namespace Grid { class Flatten { public: - typedef typename element::type Element; + using Scalar = typename is_flattenable::type; + static constexpr bool isGridTensor = is_flattenable::isGridTensor; public: - explicit Flatten(const V &vector); - const V & getVector(void); - const std::vector & getFlatVector(void); - const std::vector & getDim(void); + explicit Flatten(const V &vector); + const V & getVector(void) const { return vector_; } + const std::vector & getFlatVector(void) const { return flatVector_; } + const std::vector & getDim(void) const { return dim_; } private: - void accumulate(const Element &e); - template - void accumulate(const W &v); - void accumulateDim(const Element &e); - template - void accumulateDim(const W &v); + template typename std::enable_if::value && !is_flattenable::isGridTensor>::type + accumulate(const W &e); + template typename std::enable_if::value && is_flattenable::isGridTensor>::type + accumulate(const W &e); + template typename std::enable_if< is_flattenable::value>::type + accumulate(const W &v); + template typename std::enable_if::value && !is_flattenable::isGridTensor>::type + accumulateDim(const W &e) {} // Innermost is a scalar - do nothing + template typename std::enable_if::value && is_flattenable::isGridTensor>::type + accumulateDim(const W &e); + template typename std::enable_if< is_flattenable::value>::type + accumulateDim(const W &v); private: - const V &vector_; - std::vector flatVector_; - std::vector dim_; + const V &vector_; + std::vector flatVector_; + std::vector dim_; }; // Class to reconstruct a multidimensional std::vector @@ -283,38 +305,57 @@ namespace Grid { class Reconstruct { public: - typedef typename element::type Element; + using Scalar = typename is_flattenable::type; + static constexpr bool isGridTensor = is_flattenable::isGridTensor; public: - Reconstruct(const std::vector &flatVector, + Reconstruct(const std::vector &flatVector, const std::vector &dim); - const V & getVector(void); - const std::vector & getFlatVector(void); - const std::vector & getDim(void); + const V & getVector(void) const { return vector_; } + const std::vector & getFlatVector(void) const { return flatVector_; } + const std::vector & getDim(void) const { return dim_; } private: - void fill(std::vector &v); - template - void fill(W &v); - void resize(std::vector &v, const unsigned int dim); - template - void resize(W &v, const unsigned int dim); + template typename std::enable_if::value && !is_flattenable::isGridTensor>::type + fill(W &v); + template typename std::enable_if::value && is_flattenable::isGridTensor>::type + fill(W &v); + template typename std::enable_if< is_flattenable::value>::type + fill(W &v); + template typename std::enable_if< is_flattenable::value && is_flattenable::vecRank==1>::type + resize(W &v, const unsigned int dim); + template typename std::enable_if< is_flattenable::value && (is_flattenable::vecRank>1)>::type + resize(W &v, const unsigned int dim); + template typename std::enable_if::isGridTensor>::type + checkInnermost(const W &e) {} // Innermost is a scalar - do nothing + template typename std::enable_if< is_flattenable::isGridTensor>::type + checkInnermost(const W &e); private: - V vector_; - const std::vector &flatVector_; - std::vector dim_; - size_t ind_{0}; - unsigned int dimInd_{0}; + V vector_; + const std::vector &flatVector_; + std::vector dim_; + size_t ind_{0}; + unsigned int dimInd_{0}; }; // Flatten class template implementation template - void Flatten::accumulate(const Element &e) + template typename std::enable_if::value && !is_flattenable::isGridTensor>::type + Flatten::accumulate(const W &e) { flatVector_.push_back(e); } template - template - void Flatten::accumulate(const W &v) + template typename std::enable_if::value && is_flattenable::isGridTensor>::type + Flatten::accumulate(const W &e) + { + for (const Scalar &x: e) { + flatVector_.push_back(x); + } + } + + template + template typename std::enable_if::value>::type + Flatten::accumulate(const W &v) { for (auto &e: v) { @@ -323,11 +364,17 @@ namespace Grid { } template - void Flatten::accumulateDim(const Element &e) {}; + template typename std::enable_if::value && is_flattenable::isGridTensor>::type + Flatten::accumulateDim(const W &e) + { + using Traits = GridTypeMapper::grid_type>; + for (int rank=0; rank < Traits::Rank; ++rank) + dim_.push_back(Traits::Dimension(rank)); + } template - template - void Flatten::accumulateDim(const W &v) + template typename std::enable_if::value>::type + Flatten::accumulateDim(const W &v) { dim_.push_back(v.size()); accumulateDim(v[0]); @@ -337,42 +384,36 @@ namespace Grid { Flatten::Flatten(const V &vector) : vector_(vector) { - accumulate(vector_); accumulateDim(vector_); - } - - template - const V & Flatten::getVector(void) - { - return vector_; - } - - template - const std::vector::Element> & - Flatten::getFlatVector(void) - { - return flatVector_; - } - - template - const std::vector & Flatten::getDim(void) - { - return dim_; + std::size_t TotalSize{ dim_[0] }; + for (int i = 1; i < dim_.size(); ++i) { + TotalSize *= dim_[i]; + } + flatVector_.reserve(TotalSize); + accumulate(vector_); } // Reconstruct class template implementation template - void Reconstruct::fill(std::vector &v) + template typename std::enable_if::value && !is_flattenable::isGridTensor>::type + Reconstruct::fill(W &v) + { + v = flatVector_[ind_++]; + } + + template + template typename std::enable_if::value && is_flattenable::isGridTensor>::type + Reconstruct::fill(W &v) { for (auto &e: v) { e = flatVector_[ind_++]; } } - + template - template - void Reconstruct::fill(W &v) + template typename std::enable_if::value>::type + Reconstruct::fill(W &v) { for (auto &e: v) { @@ -381,14 +422,15 @@ namespace Grid { } template - void Reconstruct::resize(std::vector &v, const unsigned int dim) + template typename std::enable_if::value && is_flattenable::vecRank==1>::type + Reconstruct::resize(W &v, const unsigned int dim) { v.resize(dim_[dim]); } template - template - void Reconstruct::resize(W &v, const unsigned int dim) + template typename std::enable_if::value && (is_flattenable::vecRank>1)>::type + Reconstruct::resize(W &v, const unsigned int dim) { v.resize(dim_[dim]); for (auto &e: v) @@ -398,34 +440,31 @@ namespace Grid { } template - Reconstruct::Reconstruct(const std::vector &flatVector, + template typename std::enable_if::isGridTensor>::type + Reconstruct::checkInnermost(const W &) + { + using Traits = GridTypeMapper::grid_type>; + const int gridRank{Traits::Rank}; + const int dimRank{static_cast(dim_.size())}; + assert(dimRank >= gridRank && "Tensor rank too low for Grid tensor"); + for (int i=0; i + Reconstruct::Reconstruct(const std::vector &flatVector, const std::vector &dim) : flatVector_(flatVector) , dim_(dim) { + checkInnermost(vector_); + assert(dim_.size() == is_flattenable::vecRank && "Tensor rank doesn't match nested std::vector rank"); resize(vector_, 0); fill(vector_); } - template - const V & Reconstruct::getVector(void) - { - return vector_; - } - - template - const std::vector::Element> & - Reconstruct::getFlatVector(void) - { - return flatVector_; - } - - template - const std::vector & Reconstruct::getDim(void) - { - return dim_; - } - // Vector IO utilities /////////////////////////////////////////////////////// // helper function to read space-separated values template @@ -460,18 +499,18 @@ namespace Grid { return os; } - // In general, scalar types are considered "flat" (regularly shaped) + // In general, scalar types are considered "flattenable" (regularly shaped) template - bool isFlat(const T &t) { return true; } + bool isRegularShape(const T &t) { return true; } // Return non-zero if all dimensions of this std::vector> are regularly shaped template - bool isFlat(const std::vector> &v) + bool isRegularShape(const std::vector> &v) { // Make sure all of my rows are the same size for (std::size_t i = 0; i < v.size(); ++i) { - if (v[i].size() != v[0].size() || !isFlat(v[i])) + if (v[i].size() != v[0].size() || !isRegularShape(v[i])) { return false; } From 76af169f050482fe78894bd69ee4566d89d9a6ea Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 31 May 2021 08:43:02 +0100 Subject: [PATCH 160/399] Add global namespace to Writer and Reader inside GRID_SERIALIZABLE_CLASS_MEMBERS (so that "using Grid" not necessary). Fix issue with output of Grid::iMatrix so that M<3>{{148,149,150,} {151,152,153,} {154155156}} becomes M<3>{{148,149,150} {151,152,153} {154,155,156}} --- Grid/serialisation/MacroMagic.h | 4 ++-- Grid/tensors/Tensor_class.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/serialisation/MacroMagic.h b/Grid/serialisation/MacroMagic.h index 0495b91e..de456305 100644 --- a/Grid/serialisation/MacroMagic.h +++ b/Grid/serialisation/MacroMagic.h @@ -118,13 +118,13 @@ static inline std::string SerialisableClassName(void) {return std::string(#cname static constexpr bool isEnum = false; \ GRID_MACRO_EVAL(GRID_MACRO_MAP(GRID_MACRO_MEMBER,__VA_ARGS__))\ template \ -static inline void write(Writer &WR,const std::string &s, const cname &obj){ \ +static inline void write(::Grid::Writer &WR,const std::string &s, const cname &obj){ \ push(WR,s);\ GRID_MACRO_EVAL(GRID_MACRO_MAP(GRID_MACRO_WRITE_MEMBER,__VA_ARGS__)) \ pop(WR);\ }\ template \ -static inline void read(Reader &RD,const std::string &s, cname &obj){ \ +static inline void read(::Grid::Reader &RD,const std::string &s, cname &obj){ \ if (!push(RD,s))\ {\ std::cout << ::Grid::GridLogWarning << "IO: Cannot open node '" << s << "'" << std::endl; \ diff --git a/Grid/tensors/Tensor_class.h b/Grid/tensors/Tensor_class.h index 36becc49..be045ede 100644 --- a/Grid/tensors/Tensor_class.h +++ b/Grid/tensors/Tensor_class.h @@ -417,7 +417,7 @@ public: stream << "{"; for (int j = 0; j < N; j++) { stream << o._internal[i][j]; - if (i < N - 1) stream << ","; + if (j < N - 1) stream << ","; } stream << "}"; if (i != N - 1) stream << "\n\t\t"; From 0a4e0b49a062de05e2d49091595f49135eb49dec Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 31 May 2021 12:49:56 +0100 Subject: [PATCH 161/399] BaseIO: Added "EigenResizeCounter" to keep track of any allocations/deallocations to Eigen tensors during readback. On read, if the tensor is resized, EigenResizeCounter += delta memory (in bytes) --- Grid/serialisation/BaseIO.h | 8 ++++++++ Grid/serialisation/BinaryIO.cc | 3 +++ 2 files changed, 11 insertions(+) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 49406201..8890908e 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -30,6 +30,7 @@ Author: Guido Cossu #ifndef GRID_SERIALISATION_ABSTRACT_READER_H #define GRID_SERIALISATION_ABSTRACT_READER_H +#include #include #include #include @@ -110,6 +111,10 @@ namespace Grid { template inline typename std::enable_if::value, typename Traits::scalar_type *>::type getFirstScalar(ET &eigenTensor) { return eigenTensor.data()->begin(); } + + // Counter for resized EigenTensors (poor man's substitute for allocator) + // Defined in BinaryIO.cc + extern std::atomic_uint64_t EigenResizeCounter; } // Abstract writer/reader classes //////////////////////////////////////////// @@ -497,8 +502,11 @@ namespace Grid { typename std::enable_if::value, void>::type Reader::Reshape(ETensor &t, const std::array &dims ) { + typename ETensor::Index before = t.size(); //t.reshape( dims ); t.resize( dims ); + uint64_t diff = static_cast(( t.size() - before ) * sizeof(typename ETensor::Scalar)); + EigenIO::EigenResizeCounter.fetch_add(diff, std::memory_order_relaxed); } template diff --git a/Grid/serialisation/BinaryIO.cc b/Grid/serialisation/BinaryIO.cc index 8ddf3c6c..e01d872c 100644 --- a/Grid/serialisation/BinaryIO.cc +++ b/Grid/serialisation/BinaryIO.cc @@ -30,6 +30,9 @@ Author: paboyle NAMESPACE_BEGIN(Grid); +// Declared in BaseIO.h +std::atomic_uint64_t EigenIO::EigenResizeCounter(0); + // Writer implementation /////////////////////////////////////////////////////// BinaryWriter::BinaryWriter(const std::string &fileName) : file_(fileName, std::ios::binary|std::ios::out) From 2b1fcd78c340ee1b1d80c081ff2ef5e59ac427ee Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 31 May 2021 22:24:54 +0100 Subject: [PATCH 162/399] Fixes post review with Peter: a) Correct bug in isRegularShape - detect 3d matrix where 1st slice is 2x2 and second slice is 2x1; b) Synchronisation of EigenResizeCounter done by checking we're the OMP primary thread; c) Move definition of EigenResizeCounter to new file, BaseIO.cc --- Grid/serialisation/BaseIO.cc | 35 ++++++++++++++++++++++++ Grid/serialisation/BaseIO.h | 12 ++++++--- Grid/serialisation/BinaryIO.cc | 3 --- Grid/serialisation/Hdf5IO.cc | 31 +++++++++++++++++++++ Grid/serialisation/Hdf5IO.h | 31 +++++++++++++++++++++ Grid/serialisation/VectorUtils.h | 46 +++++++++++++++++++++++++++++--- 6 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 Grid/serialisation/BaseIO.cc diff --git a/Grid/serialisation/BaseIO.cc b/Grid/serialisation/BaseIO.cc new file mode 100644 index 00000000..9afc20b3 --- /dev/null +++ b/Grid/serialisation/BaseIO.cc @@ -0,0 +1,35 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: ./lib/serialisation/BaseIO.h + +Copyright (C) 2015 + +Author: Michael Marshall + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ + +#include + +NAMESPACE_BEGIN(Grid) + +std::uint64_t EigenIO::EigenResizeCounter(0); + +NAMESPACE_END(Grid) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 8890908e..25481301 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -9,6 +9,7 @@ Author: Antonin Portelli Author: Peter Boyle Author: Guido Cossu +Author: Michael Marshall This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -114,7 +115,7 @@ namespace Grid { // Counter for resized EigenTensors (poor man's substitute for allocator) // Defined in BinaryIO.cc - extern std::atomic_uint64_t EigenResizeCounter; + extern std::uint64_t EigenResizeCounter; } // Abstract writer/reader classes //////////////////////////////////////////// @@ -502,11 +503,14 @@ namespace Grid { typename std::enable_if::value, void>::type Reader::Reshape(ETensor &t, const std::array &dims ) { - typename ETensor::Index before = t.size(); +#ifdef GRID_OMP + // The memory counter is the reason this must be done from the primary thread + assert(omp_in_parallel()==0 && "Deserialisation which resizes Eigen tensor must happen from primary thread"); +#endif + EigenIO::EigenResizeCounter -= static_cast(t.size()) * sizeof(typename ETensor::Scalar); //t.reshape( dims ); t.resize( dims ); - uint64_t diff = static_cast(( t.size() - before ) * sizeof(typename ETensor::Scalar)); - EigenIO::EigenResizeCounter.fetch_add(diff, std::memory_order_relaxed); + EigenIO::EigenResizeCounter += static_cast(t.size()) * sizeof(typename ETensor::Scalar); } template diff --git a/Grid/serialisation/BinaryIO.cc b/Grid/serialisation/BinaryIO.cc index e01d872c..8ddf3c6c 100644 --- a/Grid/serialisation/BinaryIO.cc +++ b/Grid/serialisation/BinaryIO.cc @@ -30,9 +30,6 @@ Author: paboyle NAMESPACE_BEGIN(Grid); -// Declared in BaseIO.h -std::atomic_uint64_t EigenIO::EigenResizeCounter(0); - // Writer implementation /////////////////////////////////////////////////////// BinaryWriter::BinaryWriter(const std::string &fileName) : file_(fileName, std::ios::binary|std::ios::out) diff --git a/Grid/serialisation/Hdf5IO.cc b/Grid/serialisation/Hdf5IO.cc index 77396809..d3c7061e 100644 --- a/Grid/serialisation/Hdf5IO.cc +++ b/Grid/serialisation/Hdf5IO.cc @@ -1,3 +1,34 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./Grid/serialisation/VectorUtils.h + + Copyright (C) 2015 + + Author: Antonin Portelli + Author: Peter Boyle + Author: Guido Cossu + Author: Michael Marshall + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + #include using namespace Grid; diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 6b77ba3c..46cb07e1 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -1,3 +1,34 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./Grid/serialisation/VectorUtils.h + + Copyright (C) 2015 + + Author: Peter Boyle + Author: Antonin Portelli + Author: Guido Cossu + Author: Michael Marshall + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + #ifndef GRID_SERIALISATION_HDF5_H #define GRID_SERIALISATION_HDF5_H diff --git a/Grid/serialisation/VectorUtils.h b/Grid/serialisation/VectorUtils.h index 10471113..8f490c64 100644 --- a/Grid/serialisation/VectorUtils.h +++ b/Grid/serialisation/VectorUtils.h @@ -9,7 +9,8 @@ Author: Antonin Portelli Author: Peter Boyle Author: paboyle - + Author: Michael Marshall + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -500,17 +501,56 @@ namespace Grid { } // In general, scalar types are considered "flattenable" (regularly shaped) + template + bool isRegularShapeHelper(const std::vector &, std::vector &, int, bool) + { + return true; + } + + template + bool isRegularShapeHelper(const std::vector> &v, std::vector &Dims, int Depth, bool bFirst) + { + if( bFirst) + { + assert( Dims.size() == Depth && "Bug: Delete this message after testing" ); + Dims.push_back(v[0].size()); + if (!Dims[Depth]) + return false; + } + else + { + assert( Dims.size() >= Depth + 1 && "Bug: Delete this message after testing" ); + } + for (std::size_t i = 0; i < v.size(); ++i) + { + if (v[i].size() != Dims[Depth] || !isRegularShapeHelper(v[i], Dims, Depth + 1, bFirst && i==0)) + { + return false; + } + } + return true; + } + template bool isRegularShape(const T &t) { return true; } + template + bool isRegularShape(const std::vector &v) { return !v.empty(); } + // Return non-zero if all dimensions of this std::vector> are regularly shaped template bool isRegularShape(const std::vector> &v) { + if (v.empty() || v[0].empty()) + return false; // Make sure all of my rows are the same size - for (std::size_t i = 0; i < v.size(); ++i) + std::vector Dims; + Dims.reserve(is_flattenable::vecRank); + Dims.push_back(v.size()); + Dims.push_back(v[0].size()); + for (std::size_t i = 0; i < Dims[0]; ++i) { - if (v[i].size() != v[0].size() || !isRegularShape(v[i])) + if (v[i].size() != Dims[1] || !isRegularShapeHelper(v[i], Dims, 2, i==0)) { return false; } From 393727b93be2e50a1197490e28520425f0a50fab Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 1 Jun 2021 15:49:37 +0100 Subject: [PATCH 163/399] Documentation update (briefly) covering serialisation changes. For review --- .gitignore | 1 + documentation/Grid.pdf | Bin 1353181 -> 635548 bytes documentation/manual.rst | 87 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5338acb9..40156f9d 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ Thumbs.db # build directory # ################### build*/* +Documentation/_build # IDE related files # ##################### diff --git a/documentation/Grid.pdf b/documentation/Grid.pdf index 868c6db491062ed2cd48bad71d7a28e67377f287..df3304ebb9a6d1e10a17eb140ee5e07ca2eef4da 100644 GIT binary patch literal 635548 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

`@a#P+7)_SB`VU}*gq4cvjM^+Ch5 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 ?mi7lE(^b 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

Go;}}D=^UVc7CCF~G71#T-+lGkHNN7|nbTV1 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 z zeU3o=jG-dL72pnj>f0UPQ?SX)s=sEtD3T{<*`^G_c;YpOm0_8h$jQRo-CAV&Jz@Rv zVYAnhfE`T|vpaF*xaWBfp-NovRP99({Y8Q7n?#-^%3r{A|MS;1vAb-}x{=qWcSIcE z#uq^WZ(%SJ{bKS_Kc>kP{r_AX!H#~#URG@DT*(KcDfl@zKouI9BO||5q3Ref5cXZn zzmHpQlN1W}jDbvpqr~mp51^@Nk7M+zxHAP`h()sGl=*^5!CjyK=RoVxhvs(;K0-wQ zAVeC*fZ|!Glzn@)HNc(oX@P(&2xoQKeGCyyO>im%6@X4!ELYW2#}ddm92i-n=lD)7 z&#}@>|CGP}dr|WgF6kgQy~O_j<)5OTxxXu%ve?dRMGxX=qnqV!>DdiV41WmXGp(%&gsq}?mvHXb>|Y2YXOkQ%-%VpSW%?!_)#G2mZB@9$ z9*J6Mig(uJ^>?$$6wyZH6aC?WLSSnGzJtDoSU(d~H{a`d^?k6f7YC`x2OCKw5Ng4R z5O-DsAPH(`WB#Ek%&utX$i>ZbS)>A(!K;G>8KS9!{#q8!+Ken-Leb72Z_u6q{z7vU2h?yAD*5^f6R-9 zR-O~}VGH-lm*Y*|h%;Wnvb(^BShT6G$ilDbQ8X?uL^Pj$!u88=|K{H(JU{0NR99=1 zk85_C^=#IZ`Ttptr>MWT%IS0_|#xz(csbriQNT; zTqMggZQWGUh_C4;8K(c7w56@1TX?^&l*B=)9{C9=#GJkHL~Ou6eD62#Bj5?mqV68T ze!Mw-4$v-x2s2W5jFjl~gq>Z01kB11c$aP;M9IVek_a;qdriFT4@bNa`75|FP5_QY zCBMLh73eX6I-MM*^s|In8u-Lp@vZcpR`BAY0|SfB>GRGwk2@~Xeik){@*%TN(9SGS zVFJJY*$DioiHdOsXudVZr%fvjx6kim8U9-u^ECABGJ%n&>pYX@0wqi z(z$#Y`;x<<^DVk)zJ1O&pB*tH}+$`=J@Pj9F_}zF09})pM~M6qS;PT^n3n#$zbi zG-^OKe-ZzH!HV+E$mMhdu-sIb6n1c3>ZWVkRlHR=%MV$Nsq@JEi{oq8T#}n9U>{vE zhregQP}==xm<60-h~G?*Aq(7?ElR^1>Cp$C@;PBG1Rw)5$_mQ-H}Hy1nguseksPuU zAaI3Ufh!3tM%o-;pFhg$TX$v|D(^~uGQ;VG#!K=)GZ}`AI5ul_+nbMk4LouIIxi|SqhlgVCV%a zag~i!q5CG7p#)a5PCA8$c9<=471%L>Ix+9gEz#PCm|+-B78i+0RL*Shc3p_zQKDk0 zq8(XhsvFeI>T@*B4!O0hlB_#T*w#315L6<+aj#zjMv|r)#zz&CF!uarj%q1+RAjgo zGb^xc{6aJvL07Cs@&sgVN!XdqC2>pS++5}RCG$7R1FR;ozn26z8e}P} zWRL9yej8$FI6n5r)sm3f2+%KqHfv~e={8FP2V44Z62ndQ3`RJ41SB}dt7p6XnDRa2zl{CZd(M0wzh`{QKj9~87`u`~W3 zH)$KiK&F>W?%+&#)z9qjz-l@98Hw^>bJl68@b7jzGvjG}OKYv{yKjdiv9Q+V41`uM zaIx6qhLgw#-Kl^1>8O7)!A%|*W1Xs7?x0BJKV0&!7Jlqk`(oB&YoC1?e+H*_G3%G* zYx_qpa2ank2vSpAO*0aEfqgTSbku6UcDG3GrZ2pwKxOni%##A0>9Sp;e!uJqx_c-O zVsVuL8;B1*EwwjNLOzXm=D*~yNkM%1k}fT^En`dNDlf8fj?abpSGaxE63C(V>Raks zt)}WV-?=Ezaogg)BnU$NEQ#igC-&aHKQWH8hcLll#RGi!{y5Q67H(QU#o~C<4BI|@ znwh7OuQtZUJ~PXZiq0~<)Do%;sS-K;`1%G9^Bp_=O5I62VX882d`|dSGH?<e+=} zhTb<3JjOG1T>BS%ed({hDgFISiFFVR^Smm^>HF|k`GN9>Js>wBiV_ec@Od=3V z9>XQM#V0Jc*?}O?`!cfVzJu2#T=(JntJI>m!7UMJ-(C1yP~BV}F8lw0>)XRoQ;5Zl zX?iEqyz{CXYG+?X!V<6tWoDAKgjC#i%if#2#EPt>cR5xc$$XP|Pw7NhWE(jt zArd?I1bIQG_yIRR3%O4M7(tM09}3Hk`8sj&kTvTpeH9lOoNMB$Sz3QeAG!MesPQvy z;xq`zNT)xqXn(YZy+7Je0}{>BdTol#cu(TD-mNFx{}&H{sHZh|m694*WT@g4c~5bz zI)bPm(oXtR;ebOZbfnN7%GcNSK_yUT*TOFp`u0rs`uoVjRY_F2t*F|gF~dq{=Foc~ z(_(y^HD%0Mo~@-{mtQqhr+heKY_Y>=PFAQ+O}CRSNK&-HjO?Do@*F(~R`_xGHusHK zK;0~>K6c$7h788=Gh+iLUFno>=0VKfd>IGqX)#jE_^$8yNth~s#Nqn8Q)Jc&Nx|Q< zeu@5}+;sWc-3Dst^^O%+LrONrvpLUl`%lu8&(vbk{)JSevd+g>#|cSlj62H@HJ@cL z=xe@OdEOGyvv5qWn2^cfMAv!Zx{yn4Dh zHRh6I7&V7VE~G28V^M}wF5J<&NPh3iFp&p_&7k!v=$QUB(QVUbZK0XhIi_Z~J@8$f0~a!;Wxov~$}RN-U1s@Ox_w>Q+zictWTI&pP( zow-LfR|?teU3<=6w8U>91Sg^oz|4n!i1ODA?#X+kKF*0G*cr+-f}Y}xv~q^(L(#>& zfv?kD@p^g6+9sj0aPp^c=}wGr4R7E0i~fZ7Kja>s;1f}+3_wEqM2JkGEzw_Q+fk92 zoIH|f$l7g|6qxWbGAds6xBW!iLuzDHVkH~fLT2tbq{kvdF13dz-h@{DpFHhm(dBi@K`5;J)lXeOyZRLM|KF_Z?L=b_UFZO-kxz8 zt{uX%<2USQc|fr2Vx=orT-0MX6PXp+L#`*hR0kb<|qX%*=LlV68lODqo9%7%o)G z3Q1TO0vR2`634B8C=`(h+FIr%ds^EU8eQ=*t7kfhvH5q7_^lJ!YnbsiYl-}qyHgCm z2O-~?`ga*AaP@1-hm+ZvE;{TZNXt_)R z2;V)ey%-ok$Ug{^2>rwhhk?mDHNtr;1sx=C<4)K)WOF|pC=vsY{`+Um{;T(y#JbFj zbjfoXLHz?de%;xmsxyOSfRomDh(wR-J6DMOr67}l8#nc*AJ1No9Ub5Rnb}li7Xpiy zs(Q7LLNr|PaxNU=sn3KO#&dbE#Nv=qO!&F$)`Xh)$tY1nM`qk)qMVYw%Icj~_YI+H!;^&xdUm@$P`&x`~Bwbc+Fq}@4uY$|%m6vgZGHapvFghp<~HTuUx?%h~ulb zYdGm3s(s7ZGfw>S>H@iy9hZ+-)&|Yy1B5y%8WnvYS~6_A_$$iw(bFQ07&&Ivf4VtX zErv|xv-;hl0m_?!QeEcpT}2hu3R1U=A{>rikDckNV;-RS^7wNX!A;f74SN#~;+$55 zYI%-VT&G|PjIl87C#BgxnG#^aCsCQ6nEc-e*d$1KN<9;*ATk>QAm{f^;Q;>VbEeXk zjRS;E_Dxr&(8?>|>K(3&0xqgCiI4G4A;l;1b+!;ebw-o}U_QG#X+f3FwJm*zKlt6{ z1zZ8TSknQm+JNVbiBry-U+)CA#lvn5d8{ZQS$u`*q9;GFm(xT3yQhmam0$O#*1|^>j z*Q`Y9Y+?;v2G5V$9#{o~Yq+|U(w~>LiK08*8S$~Hybo$Ien_4^CkH)JjyvAMGdVG4 zKd4qWdN&AQX#yTHkc)~@mRL@H0Xh(mH>eljiY#7p+Bm`LZ`b)8*i)u&r5;C`r z!-8->YgZp{x87wg2 zFBRdxBp7TQB!?lxV`Qdr6vRh`TB_iaydoZ6-rp6AWiL**vT;)|u9^h_ny1qDfhcpw}LB6tMpd4?kF z%Z*(WT$mWB4U1@hWpaJpR$=q(qQhIRsh(ax0$%pBIA_4fun7_bEKR#D?W|vuj0E3A zOrB^yyXYc$048onAOQdoVe0mIz{5WTu&}9=JWtC3B0N_ns=YVWrM8x_Qv8EURfT-2dshU< zq2mz{_wD&U8;a}J#H`Vm2T08P%P{)7Sr%c(C-HYoLN`L#^Ln&Tn1prQMnv}}EhV8z zGvnOrD4;WwrM{kr+C=wcwHs4g-@^0jkXfliv}Fy(zHS-_BU_FQ?m8{lY)Zw)2=x$Ss0j+Npyg1cT_6BL{*P8z1WvGHeA{m@05OhtDkPw@dZaQ7 zs?-YC=xE+~Mmf~CS}av`bl0cZBGuxXr_lw;ihA^zEj9$>Myvlq_T8tEshU|A`dit% zUH!MP3Kl2b6G|&tMLUak7F9&TR5)J;vhUyd-X6N_a5*FdLXq79BK*1so5mX&yU&^~ zSijm_Pw%I7GA-h+&=XZvoI)+PrnB9ntiFB1txrj1@Yn{L#W-) z4|HnO-`-XJ_YFX5U6T7e2)B^i@kRgVlM~uR))LkxKaI)_hKm>MXe92f++0y~9iPjh zOCWvnRn)KuHdNfU-k|2X`{4VM9IX@edFF6Fo%<8%b?Tl|?7jHAEw1TGuI-$cm(Dw{ zq#;0i*J|GM@?6Urz?f7ZQ?M#&u4DFM2_ab1O)54y&EI%SEeW-rVdV zC<#+4e^)UhG>u1^;~0hnw(QfGJ9Xeu81kYSQIUCm7bHs12{UCGY^Y^=8OA9GHd+1T zG3LJKpNlkeSV@;NY{!S&a(GOyfU}>`__%atZ0k$om`Fj)$;WRKqJS*KREV9NFeE;%P4y63d2v`%nbEq&6V}cSG#}>(8rn3~_^9q1ztrs=FAD|F zf%YwU#6XVdNnRtB=YCMN469dG$V5tol18NJ*xUq+aQr7l8N_DmX;nfJsv1bN zGmN0Csa<`ede{;|RN@(VI?K%fl5K&$taNO;-|~Up)SHz%r}roL^?7V>#Bs&CO5+-( zM`4lcBOopB!#@NPHxG7Z&UPq zcY>%uvKHLLwZ3+vQZ=sAlpb?+Z}{1)6>NZ2!TYeuG%fYz?cvmCR}A~cK~*~9stn4e z0i#3JY#=NZJAIxDVe&HCiF+u$-m|i{R=$R*xrn=*Syfp#C&+(j)46ljxlf|Dj<=b4 zyjJqiL>6$Ntr7i=^R9JR=Lr~X#FWfE2 zW@X;Pq+;TkHoApl!QW33@7hueO^F#z8a=mGs>0_EZ}P@@={$=ItF!HR)BFQg z`5`iH{)Re#o(hCAWN+)zE3=+6QVB1CHRt_$qh)}E;kJ^_xV_Y`JOSHr`&#%~RdJyi zZ#<{7CK;su{rCf&8TQW0GL$ix^$w2{s$#xNsze%$WE9&?eQS0tyi@5Ironh&79M(2 z3muJp;5=|%nYq&m363T3d~SMmm(>8sQ;^XKLN~Q?@`N=fQjs4}madTtiYuMLMtD8mG9%Uza5E3>VHH0|l5rBlSU@8y}ES|$IJ z68?^lz1(wHHOaiIF8ykEQu2eFEI(X<9bdy+)M>X#{`&dK+oice2Mz3+QSILNBJFSb zf}xNQigM!%i;iElz-I)~(f6~vS4A7HesyOhk(lMQ=f*dt%9j0i`qHs)(_`OpMK1bK zxYC^6|Jy3@^1;&siamQQsX~Pj^;qt68 zP33gQ8Mmno%iD%kP3~7Y`@z2(;v#a!GMWzC1=#?h9W zwY{(kVM%*vY>b@q$k`)z&>U_^D(R5L9p zG^y8oMdL=s2+m{j`EtQlGcJFo*Lcc6a9(q?m z9BwgkcF)K2Bww)Bm$BIOb!CEP1%Wbk;LUgV@z2qNe+QUL!r0;-J@f+PJmDBZH7-lP z*9LsG*VW2)7g|{uzjfSLuZg=U^EH2ZkW7C>{~OMt2jl!ul1YFDsm99IH=qI`MIgh0 zD4Rksiu}f*aB01d%$Y^%VN6X!*4OH}ng8EV-${wYG3ot+ys0ckQ<(x-?jI5LY7JfI z;#}dTuE80Zni&HOPJb@B&7xQmHT@!Tkmow@wJvu?EVW$0zvWtte(Bz3e}su(@#RCcv2a>43!RA^;dUXcbBwp zh4wFrrp|R&p3gAnZd-3~kU^ArS^^EAmaV2W(>~UrS+Oa!@ zo?+5{T1GRX22rXOIenFoxj;3om=@S@mJ;Y-cHU>-=k07vnK_O1BiP4ks~&l-3Qa4B zOCs#fe*st_?Aev%jz$nj&J;7sy4zJ}s1_? z{qtexO1KFB>99Xl>UqakQyulZu-Uk4BLh(wFC>5Wss$~3;ILMtd`+r76x{9$2LTdnxcd+PYK5VS0hqHvOb z2Ms_r6;9tQi0|uECAl#JA+{#L=IXOoXOpqLU<}VJmpn*H2#600cmJ36W%wD!dy#Xu zdTKEYePCoY9pj)|NMrw`}QpMT_Dswh!X82u$atym>*-yUJV`9An z`RPn=Lx9whXA&rJ*N#0TrCTw;VAiScV2*{GG@;=Onp^Lae$?6^L(j`J-_J9I!|wLc zNfK_)9}t{<9ZAx$wvX0Q{DAfrLvx2G-UJ!8-tgoXIvCib(xbA|(A_Xy=Eh3@=YvHL zlhu%Ptd`!V6WcauonU#n;Ni4$nyU`dFAJ7_D_UTj-U-KL9`k-1q5C^+@crj20*yh7 z*HvZbs0FjoLP^$+m~`SNW=BC>7Y|rF{5Pu`*++5Mnf1KrZ^+)njcp{m3_fC#Q{3`R z(JOu8{-qB~j2xdE#f`}#l=>C9f`eX0hmgaUz09EvL4#K2*Qi@5A8bG2LSVA)Jeh?I?tc3^du%!l>6f)k-wuaQl0`#@ zCy7n0bhS&~>e;8z;r@OsdVl>5CHspM)F#Quk25v&^;@mjg=%I}5v*Y;6yGj9)lJkr zxC;sk%2e(PII$TeDa1;LbN)u`Ibd@#6W@=-t=`mhBbw<>-B!!@nn^icd3qs(Xj5y5 zYMkkqB~)_dFA;9im|;|8;6m4eU90OsnYO<*(|*WZi;|2vc|d>A{7}S@WH%CfUFk~W zi2@|#^*0zbGF%dlZOqHxfv8iL)qTd~2f<2knqePVgDt;#Q4fc2SsR@)o)bwXAms_OP^aS%j?#67Ia^3tJoP#mLjr=p* zcYI!dmm|?A?pYMgEs}2aQf zM>c0UZYd0*y|AvLO^Zz>X+kJPcL56THa))FJ9t3a=2uwX5IKT7O*xOr5-(NO;*+S# znG_pFg5b#<`dHKxG)X?$UPdw5s(LkRf}H6mKPgb)6i*wOw?E$d>VLm{^Rqir^QK<; zyx;dKFR6S-BO3kU-QhR(pL!VYab;{@w7uLC<|)y9L$1*Y~+1uMXVh9ux-D8imKIci`gGv$7^$=Ydk#( zcw@f~5hA~`iElj*9~bg0BG#n(^p2Ea*c1eg3I(*aKONi+wg)g=SceF5T?mzBa8Z!Bf_y#L+*hUE=Y_>cIrJZN8!IwOQ3J(xoyQ6OIx2sWVkY1OW z4wrnjkI3d;<_&3PhIz@+ZV2c8A(6vJ7+&HX{t#H(q-8WbDNUx|PpA(M_#4CNbg+YI zMAML4TJWAY5hn3P@cOm}H~jU^N&q|JxiGU+L;IIpZdJ{U%PU{mk3UfR>DRO~h&hpN zgD>4k(TH%-`MVKwg;J>yWvPb~M611e-7EU#6KfO(#03hewzu&d>V=YH>E!oqZuyp* zxiBi(#*z#f7sMLXRsUoTl}c8aO~xpp-@f~F-`fsU;KFRd$gy|hf)GW)Z%yFyPxPy@X4SmEkRy| zXr1qL=v<)7uY^pO-8$EPvT>7fimuj4CuDGBjQ%uMT=lis1UloKsMi2B#rs_W9>$N# zqiTYj@ZwN+52;?z#x!E}&7>&Hux{VyV)_mnfufRz2buUPye2n-p4EsXj6oyJ6=}GR ze=lMQR(7b|dOSZZax64eYahUYT-(|7cf{xSoKv{UF31H*z{j32cu0i6?2KgsE!A@e zRh+t5iJgjM^V6K+pHxYABade^JvDeq-7l%(j{acpAqLA7`Wx}18{uDx3x8fDc&$n` z=4ru)bUqLfRox=eboU!i9e;tsRZyyHCU75Op+fr}|E222h8tm_dGr2}$&_yPu8mRC zN;F3w(>k(fhAsyaxBRL7W&p)oA|iISyEp#^>mnmaikJVv`9Y>~k?LhcZ zoDjjz?KX!?v4<%t)2n=rhT;;Htfz{rC)0tG;iO1t&le1mlnh-f4vE}N6?)YZYiX;q z)8_j=6}+zOVTKqBk2!s?7vo@P;byqck>R}X*)H?m6|gHnWO@;!KCtt}?=TEE!>6q? zM52UkDP-NKT;ys)nq$-?jGr*W#7y&d*$m6&^r4w{7&W_iRF3x$t7V#TX^e?GDOZDj zYjs|YN44G?2TB2^WzhV(rCG%=MMDBqLyi$aEt0!aB88LrWPdPMG6v=vwqQ78Qi7H3 zW4Zc(BUPK|wkwwF)S(?i9Xl_kK?S4^Kud;Bqcz_|U?LYp_82R^&}Rg6EMesSx4y$QRPediDRuO8pOpfiR_;=I4N zy;PTJn^&C*%ZvHCw?pIdynXj5m!oLiIEH88G84&zncX^=@bG|G#4zqMB*>|-Nr&FgB-@#R<^t5@RW#DCKSe4 z-XWg6KW}=Dd<*V_1uJqv*yMy9z1;l14%!HGh&rTqSKSeQ=6d=W<2JB%F=tQXhN0=% z5&%Q2i1Llhg-`Hff;ECS<73w*C*$S&Xh$$`F9WQG4f(gdwF*7l9LB6}l7mu-6Nq09 z>h+_=JLvZ=Yq_}JvQ=QTv+=U@0*i?~OFX1D{e;qQx38@ZUB9y3`Rykmw> zfy;$4pT>d=s~WmO1Lqzk?n(wp$7!?9M0ej>lAi`;tLahWK) z2uC*_nJ@I=1gp^(B11UG<=rN&En6j2S}L&9%%>Ds+e|Q`{e*wAcuJVQ3(T zZggp;@ioEh#H9_9rUy557??K4oM@I9>2|L^-2*$xwz}R_)ycArDUZoyiEyrE+>Q~C z7tY;ZOz1%u$=RQ_*8AN0HLekho>-}87fpE9I08XXq`eWNH6Ul z{4k`MpxAT{@+I(B>E9t(^ev8+ZWKZv-vn z{gvH$FH8J?Y>vNQ4+LBDP}cKH<(h?;Pg z5wxn9jCD))VLGP2?085W;qBoxPaB3N(|?NF*Q6Udc!ce%tO%_1AhJ$*%e?vH>-MD` z-|I+f)G0q7H&59Zy}Ku1cejUEz$#3`>lnE%a7r(fB@S`j9a1%kME@3)D|tQkwsx%V ztXZgDU}+=LPcPrD+BOor50O!1aAC5wg}Q* z8Xh;-ww0Ye5Ru+nxLF= zGhWt}IZ7m8jM3N6ke@J_J5g`kQ1V>@^v|o0lm^@$B(cXMWfM}j!p@ghfv~wQ6#Qj| zW#JUK^|{UWygDWcFd6%<;8bk-Q7R_4XM}0JG|U70q_A4~c?wPP;LogH^2qs)+nD^N z!t?-?V_FL4|aRnwTyEt#AX)rPm; z)OJ11U3y*+slsfA_sYtxrY@5Ol<628%rVDnou)>@XEsS6VpM7NYSd4_@zaX+Bj<|r z=IEfh({nHRly0-yi;+UEG97QT&czamUArE>mQ)&0VvZNbR71#+DeEA_G0w0zx8)fkKU z9zSM`nS3=DJ$JFaUiQ+Da~B6v*Olbx|I*?&5z2pWfApi?iGW9lio(lk8>ZB z>GzBQ5)omWQN0>pz!e&slOT(cznx#*;J#=^=ZU2CNl#+5iqm1(nMzf^oc>$yJYKGI z0&$xaJ+Av%$Q?;EU(9MA^BjkHg|hW;^oVGv6~kDMUcU0z_evx5A&lFT{pL5v9_{4% zHMoVcY3~iGho&C+u+8?he#?`g`cUHkly!#rEA9+c^&brmn@=+t&0qe z>OJ2iF2E0EC%v#M_%d@~Us3!n>maN+b*nXXsjE!n1+nCi#US0Ms(cawUc7vP9m0H@ zis~OE!w!B*JU#4jdutPx((eP6SmwVt4a!m z_Mjer2Qh*z%-h(~iSm84ZhnriKHTBCgmfCYeSpu=8d`pl2a6prlAnWBr4Mjxw(2yV-Nw?5>o4s zNL%!xLhDB-g=9y|PnZg6%-A3e(&!)nLnCZwHCXgdhbPVRtR$rT*=ZKt1 zj-(v2${tEXKfS&st;(y{9%JV2M;qL@#3n7wR!qfTLoJQDk7iAf&qC-_6khD=Ab;}? z?0A=U^zLP$wwvsH&Y^+J&%mLiJ=uv+jecFI~oZ1i}UX+OFj%bJPH7@* zS4B5P%|Cjy=$Ranh{HDF*pFa&yI`U2KCjMS`lR#X@~bu7;I*HZ&!2)11>1r#LkkY2 zTZ?%8kU!tob-JhmvaVh0ZAHte3v?zJ6^~CBMM;zt1R}pD*Pi%Xm8dG1eax%Xq5H}z z)U~-6w&?9qy|zAIsOibK!nggV*GccgQ);*;+i$4*n-+fJyB%sV4A$H_qAFqd<(-bs z^jcUNCBxhJv3hWV879MaHDz*z!7Na|DSv#UsSFO974!~~L6^Z!9+Ub8QEy}AzQ2W) zBZ-aq)&Xxs*o@<6#(c8AQr&!$9;(%{DPt+wRA?h~{L(gh^^@1s8gFrZ`Tt|BnCtTq`N~Ha_CNx5|A$GuAzJAMp}C4?(S~B2mkN8_dfVe@Wcy! zYOTB0`q^5S8?JZ;diiC$@3A z(XD;Xy^w@yW83hMyH!1jV$U1h?+k(sT2InQwwDrk@{1bs<)(AlZalX4PT?%ft>7x>J}w6Ob5CnLl zL1lUX&qmyD)4rLU<+}aCLEM%G@B3k&Jn_enAJd$WWaJnYoS4@n26B!2DVAq1iHoT60y5evjRejReiT@W!ZxBe|3sTK*$DQDuD-`!vbS z(P?voT|nu&FIQ2t9v1S|t%r%=sx1%!8-Vw}ub-tfx=&`$e=>M)JM(HI{vSymG?;mu zjmC?bL717*g)&=5TS-`6;Xjr1Tn^^ZPMLIc!5(+ELsaP`!YFjo3m#5F0BGRBB(+4% zhV>Kupr+f2NJT4WbNZ^jZ?0ar zCb&C^!NCpn zIfPWf5r1O-$)53{h#nYSKGcX&Zqk{fT&TZ!(KF!*%nNvZg0o#7V91_SYM{Muyp$X+ zsUeq-(EA_Xlsir-d40XI6~Qqj8FC45Cc&h|<#%eA$B~&RzN1zb1j$_75qnX~Vp$R2 zaLn2K(unYSDY+wvF1;PK#lK@9z;D#oyK*9K;1p=gx|v!QJJQLLnATyZM5wMm%j;}= zFAf{$YS;3ANK7d8<_EDsOJtIqD?Zi; zo2DHKbEJLbVS$`OI*R>eZG&}9Lp-m_4s`w`0J5K>!$9O-^%=4P&=4F#ys!c|m;X9~ z1ggLGG#VSnkOQpqwrScPk$@GB_3d2tf#!p~`F9y*VR8N7Y?*9iuJ*y!CjTz0Eq5DB z4(2nE&B~i{jLIb>xA5OVsiA5GLi2qofWmRqo|n)}|A;;?7c2iaZ-@EtQwZi6vJY=I zOA3*c5n;Yc&W(&KR+3-Wm;r#>A3+)%_IQUQq1#?$G!(r9p?6-r&T@kgbp)cDP}@dI zeND+`;IuMLMre)!?rV&tbaI+}kX8AygfIRJ(9se+lPX(UDwcKJRoXmS2lP@tLJUk$6eTkYbkinUc%e`_I)9$6rV+h<(AM5`|JT&<>DR)0T3uz*lr4#u8%tFTFw_ z`M-%T<&ffXX48+ErcR5SivlL>a|2EU`8BcQd%_F1inkTEUf+M&`iL7}O&0U!b>v`m zkn#)WsH3@ysz@9%WcySdg2%g^t2*KS#W$P#Ig@xFh@u;_7>&~9>394Bno))?5zH{U zDlS?#f=P$p(|r&FG3cp@868gi)KuCj$Np69Wi7NA+k7mkk_owqU#K9;NYx$QcGG0& zV*Q%Kz_%Wf*eox58gBA8*K!j8cQPl2@2$7`gJZsn;;8%HfgenwD zvW+JH1gGaED48gVM{9+{TlR!s0;WQP<*@r-Wee5tEeGb5r&-FJ@_lpTE{vI^v*M5; z<1~@|pyyD{a&l4;3>i89%+zwCN!4vX12Fey_w*y+bcRc0JL_Yk2K2Q@!jmQfBUaY` z*nqCPuCA<+CLqp8qe(i->CJGy*RbAaR>#+^63U9vV#VsInsPu=#oK(z#_aUYf)xP1 zs=JYpg8DUj91wtyT1fib=ULCG!#f}g5^2^_m(A+9D#Hr%)N$r#wbsb_zXRNOGveaj zt;zzmzX~NQ^Cd?M?+cIMuleSf1rEd+~ zjt-o*otze_991Vsd07lrp@Suut5n0TE_;@-`d|Qb9d6b0X_aopebqnp3-<{x9-u6W zu(DPjdZ+yB>)e~|fl+fe3O#jR6*dI5nh_QAIIXUfOwP7n#wB#cjeTn~N^{7KII zfBZR86nW!m(Vj}c*`0`nd}ETJIh2pNCtYy+!+H=X_~ER99%in4lNkxSnG6QO4GjE7 zI?$p|e9GdInIe=fe=(|1fTy?lOD9~ZHzHiQ*!n<86xZRocFC2M^Th5$etO{(iCG@G z)1kk+8a)_wf`s?3y`8LZ4-t5wex8@ESWMyg-A_-+Z1t{%K^Nn%`*(v4%dR~G=@0Pw zwz*nBFf)RpJMqEW=V}cEcvzmnqXsaExZ(8~2@`ZFMm}M?%jX282WuW4nN)1vWV&N2 z^Th}{$%97rP=ET4s-OWM(ah1RAZ4Iz0|GQq;G5@C1^@6|_pX=QPqKRo#uLT%nW&)6 zbed7oe8O(tsu2RVT2mr#{t^c5S7qcSRXKu!Iw}tKUsZiO=y5R< z-Fqix@C{1sQOU?jRn6zSILSP2dVa5N`&*~Tw9k`F$|L4C7torA zL+R-jA5QDb&G-#8&S#Mn!9;E$S?mG0J{fb_`g+pY>@+UyWZIi~sS|Oa-e4n-4(}HU zIG^RCyt$yAZto0zUK#g@S^E@2JH3yV+QcM&LH7;NdZ}RX1tFlH=l-vE zQCIF>+tZ*F3Jni;O^b-_w?TiqbvHJCMu3yDG|qn%4}mOX808O-w_y{x>#N_5o?L$9 zZyOY%5VEGCjW(@`KT$8v7<2l9(|u1c0rI+|snBL3bW5kQ`T(6))T_WdZ6+6QYIIt! zcdejt<9HS(n~wQ=_+)sa)AH+`(2|8sGzOZ3HW?W`DwKyt8I_@aExlBXBH^Zu9H~UM zb(=Lk|7^C_KDsS##>!ROHg3{yblUsOM70Z4Mt&0d>E;fp@y}k@oR~t^Cwf)%uO|DU z+#(?6F`&~o4JP0+I&lPB%zoLCZnj~~S{_<`LFR8`>_XeJlX3?L+AT*FKzp9!%d+|g zoiMPppigz0WZz$WF1WA+rys|LNrs=!sEJN~w=KGgA>qX+l4|Mg{u)CAB{}J8YZEeu zlEl01SwK5w4`^1+`sHQYii7C{{DcOe$9KxUA;T)%8p$>c@Nr|$7?ylw2d?hX#8_h1_>CBcMe8^+&9@*Ebc6Hh6`=LtW(0NICe~vu0Cpiv zq)^h^J6#-FyrZg3w|Ca4h<3I1#&a-K7y7ic-oXL zIeCU*Mxi*Hx>lFtytmP>`pCKUF3kNtUF+R>%5aTSPo-5}oo!gKCCS*vgp9~iNr1f-<8Q7s5?(Cs^t0(1elwoXvM(Ip;Ig_i}4u`sU~j$X>y*9NVWpRC%CUx;C7> zZ8%OUDJ`lLbYP0aBo({ zDpEbmFS_nPir@NAh4D=eVy%Ze$tj@QXY@Ljcj_4Ji?IvUy}F>|V4eUWe@D4DQK=DV z&UEI?I*vQVu|6E~tR3Rb?iUY{$qGMBh()c}3$Sa{Ck$r)LN43|%g$pP zjGwRzP@f|5fI7Y$ET^6Gy?f=C*VjRA*{j1X5BFt%lQ03c1;I7(5bmR1Mh?nefCa1H zuZEV*9S;32kysPQc8p7$3O8M8&EqUqiFAOTtwZy>Gl8bHo5irXKlr1U^Hm7>p`{)( zpK2L%26s+oE&%p^g%R02X+QszUUz4yY!S9U%v(93f%U)n48r_RNP&;m(;6%UW7|pn z#v)Yy;i92z&$q~bPK-Uu*+TCv?Jv@hC+&_LUwXRR5TF7Hp(~a}&{DZr!RGg;$b@L@ zKZwuz3F5P!*{^y?VJ3qkWKjH%jTP)B{Htxt`0RHM;wADMPDZVpYt%8xamRS@j94kt*mleM>e3xeO}49XgIutKJkSi*=9Y zncWM}Bb^uH+(bMB;d3^lu;^=UxwH9uU}$&4tb8|+jnkV<&@ZQ%r_+e(b#XPCgqmVB zfOG*JW@63?A6L6{uw%-Lb!-L)oRo^66$kTYH`KJKi@M#3+i@I!S=Wixmea-lxi=g?CK;xXWBOsq4n+~aa^&XGr1Q-t8+8W zs*$J-@2_Xsfl=X3$@d3~fIM2Dg{d%iZ}Me4M1BF=^NraByW}#m2S$neqVIIW=>#o? zF9bt)5ec}Ak7)h&blm3Jru}%;>)$=A@4h;@JJw{1L&r6{jBVf1h2X*yi+3WhC>~$D zDA|uq>oe80`S6$!bFB0OwZ(P8Z}PjMY6?qGkEJypQ4BJla_H;lI@C|z(Ih^+T>xCd zSlo1Fa!yw~LGav;*=6E^G1pSZVIA-k762}4<=EcBQ~oHRTL#MKhSnCsbQst*D&B(p z`6I;pOBm!!u}_}qu=`Z0^|@D9CgO@IH`%8zmCGa`SkY>76cceRA{DrH(A|(mG@!q} zkNQ{&I>2_BeX_-T*}Gvb%_u*`$#9_5G$Q=*xI;dz$ncpHQ0w&jZL4lFINz50r+D#@ z-;jCJ9v|Xgu7#|d8IIaEI`@FRY+y#|Qo&*xLlLVwrMHpC$y{<-Bx!E?E*~&%fe*}_ zBJ^Uzev%{CmAa`Wvgt_uAuPN8=jcHJOI_aw%n$+Vuy+A2@#6j4;hdASPdC9pAROPy zj*&r2L!z!VaYIlA4B^=?jWSW|TI3Z1uS{<0+Q?lk9#|YDFEA*3i`x~A1!+QY_)mUe z8okR>ulgaKQ*V@bhCG663fMf1;qj3W zjp)YsE(j%(FDpr9`b3t=^y`q$9_KSw0$k6Espr$LaQR=^^qA>l$Vv>$2PN>!b@6_| z(U))KY$SW9(hUafFliKb0Q(KJ(YZf_zL7V)IMC*V&&1g4>xE5aAijfh^z*~=@LbDn z8CTj`>y*DT0BtpsabU-X%9=70%`T7SIe#03zj2qqq>b3Gv9ET>%>^$T^P>#UM|B!^ z_5+z`m+gD6@+Rgk&&&$YEBVj4mcp3~45NUPQ4{x-#= ztU*mrzTI)7lXiv7Lyynh!Jx2Ran+t8;fMe}d<3094+n)R*mnNt$$qtP1URi+2G?Z( z6;d19DSxW~7%-ng;O!rabuhr{pOJJ3TGS#HM=Aq!Tkaf2fPq;a#BEkLsvlLHe|A4_ zvs>A_rId;0jqU{K%3?}b1~y}G%9@{28SLf^<44pPSe=WEN%;^a-7N5;C zt>%^^{g)>2m>pQ6V7_E2yC~c1`-xda`c0cNm~uR>dEu|3NrD^3hBcEDK3sn_L4xF0 zWlp0ALHN`*?_^j&@^bDZI}l&(PCzzuAJ~BmV9{i$#NnYK?5`#PBuWLB=f!iiRWL+Lg_~UYpj`Z}nPh+b7*#{v zOjD9wSTFS^b(cg*a7x7j#mTBF@VctvQwj6o@9vmO|C}nOusk>SZx1cp#?mJr293m^ zC2|}xXD;M)^OAsrlv_1H`bA{X_juF&NKE}8@ws4O!KCiKFeKQ}gW|CX>4nl=`eB>P z38>Z?-FX3^Z*G{X?p_b-<`_{|zYd?(wppj6yJXM3O!f4wwZB+e+(aJ6^$o?GO z@1L9=92X8^>-3qUs54ID8&v-aM&zatE?1TvgRgLVtK^LFA#HUj72G1ey8`-aBR|`| z>Nfy2c4h1!BSLesb8`k^!)Z?aSMcU$Mjp22Fpce(@Diz|mIktLNQTfp{zWdez9t8V zIS68XX6Uny`xNwn8y^m4>EyroFiWFKS-nsj+0Wh%yN`WfOQ+q7H3Kx*2Bqu8pT@E} z1kSsccw~2!c7udn^?7|`L8M2M89dl7JJXT>V=g{%|9$4l@sbbF?D{2MP)?=^S=J8# z|Av1a)pd}&YT-I7m7$nj-5#iu0S}+QuXH0}41_1dHY)N(gRGs5D zu_kqgn7vQ{yoB8HXDEL0BKnW0Zh2L=-Hf_pd%bSX*qRwva>{<&xrcLQ$+V{5emZ*x}I?;(zc8h)E5?rfA*Pz{ro5 z@A5}?ZzjZ4ecl`a`upXh(>0(qJWfYGQ^#}gVx}>8Zg>;lgvFr-^qQWF9le8-MAA+` z)lVCYX~OMm;PUH75X@!s4tmzf7HA3WJRno@xBUZ)9j{r+ z1RjXPXF-@R#I}^o4%+FW`np({i-#I2YAKi+KapuM2%dRF``gqT;Nh${gQiva0EEzp zD(M>}m?DV2^1Je{EI<6dq1F7fUjD{X{esE!#{37p^>dB~0O9ag36{VbigYFzCn+G< zUz5?b*~?~#+k2e1zPz?yT2s1al&Ji70}(th{14qf9qV@S5z&3Pxidayw>kxgIN*zu z>SCXFj4}eKe&m56kollsE5cEV3&AT8%TuYYG^HIz#ovv((0b`FXardN;uiaO-BM2M zM3vW{y(MIy@Spx7QXCr~^43;=$EdjWK&c%FreyHEDZKlxuh#Sl!o3SMMPvg@2k);$ ze2-KF&X@Txjh?AQFn|`mAyg%B0Yrx%?>IN^|3zf7(D-M*FbtRTHz^KZ>V%oxy&J*W`fm>lW<(>atY5n0Nt)Doy9!=m?Yi&0FyW+nmsz8Pv_7_G&jHHk zNy_TtjO6ZbeZtAO(!7vLiml+poy8lxIQjgBFLXhhK9_C_=oTtl?g&rb5FIi{{=jre$24v0ovNUHNnD%<|L$#lcR+_#qYHH%rwXpE;=`00KNy%X>vpC^=j%K ziS0Nf2E>f`qL)g$+lq0=O*08qi&1(!X~W;k@2izKG`H&FghE~b%gsHx;sbT%pTBu&U_0El*c?55bviY-#0(1WCkkOJq`HoWIYfT%`XFl$CQlziatT$@Ym|hN9 zSnWhgVQac)RRM5dW-f*oQN-37u$p)5J1m|b^jBK2!99C6e@$P2@Vki1**D$2c*H|& zx`>yF%Qf`t9*!Mf-!?T$YEqfM+gX^FTXAots-{doc=}uY{)gVhykK0Dz`09=Qi0Xh zOD~JCsV4KfQ`y)x?x`M1u~FVyQ*4UE6pOx^2rdr}^z>;FddHUizdtx59>z|l;vj)! zmWJ+W^g%rq8{@Au-EhEu6M8mOZ~{PA1OfIa@V@7|X?AlzCmYsRt&w`xR>Bwg2zj|F z8r~6S8d!Qw#VQ!(?xQlqw2L^$7-0tE^IiA^N%adpgE<+sW@P6A0>dg`%di!H(`IgJ zw}LWT;g$xLy1RP||9)fleeRkRr%ah1DT{!^Tx zY(N1WI+94O0p#QYiu^pJyHBBsGhRuF`hao#KmW@fSjp_G!PgbSyF|ln)tB^twL0P| zv?g?!?!i(sS;1LpnzlxXYAdC;6@OL=ISbu$BvU4SyS;X`^}iLfj#sgdhwSr78AH#5 z^P79ak^Vu38TP|H(gPnY>qQo=dD2%w$TOOzb`crX>25`A&n`q7HhtXOME^$AOIGlUSLkKT|&JuB0HA zkW-1}S6!miSG>pDR&Xh(b@t%ag?4aU>PGv6xL@-J(;+ZMGDnB?b^NPLZ>Uk1ApG-_ zW7WzFwh4=`bdoiynfmG@nT6L|b*{sqo1$k89>CB~;Pg;BxTiGR>AQ0FiTB2nTsfH~ zYFJ0^|M>#R>;~ED8F3-h-dg2!V}lp`5>6;CXGjH@+9km87a(Ab5*&!QqZnG@a;DsIbw76%y_n$b*oCjX7J z<&df{)v5j{OJ)z4=}FVTe6(dV={&v@$#*QOU>W8`Ga?Kf^dgeB34x^iDEY;$M(9C4 zZoX=E_!2PA|A?_OUm@DBhHcN)0`gHjk#qlAmt&>j4B+AdeExWMx}u$?JY&&64o`uV z{iy*fw5_AmDvxKxJsXc24HQ9Dusx3m23mEE`L^Usd|z&Bms(1hhE5lzR^lHycg zQ|-9}XIvq@<}`*{pUAmaARNSU2X>$`6OxvVmX_z|s+glw_&M2gJVv4u)LfU(&u#bM z^LZmT56|lj`mrb5>rb$xuFm+@*4JFNC8HVeY$Dd4BOiRpcAVR;xhaEM4Qk>lC@j!a zT`zc!ox`RsRrx$xq;m{n9WBEOu7EGaK&oV}h5Y6?r+o3Ce+oXN34WpWJ~o+9KDNX=(^MdT}Rl^es4; z@l8K@8%Wgy`M&RV*`{V+H|D(||7W5KNKQY4axK~#LxBL8^sx~LC;526`))mQ+{9&n zKkrTh5F*Nhzm`~T#a8}pkYfwAZV-I6(YO(P(&*kUo-`#DzCj{cysCQPm8z6`*zHF7 z-{UjVgIXobyU7ht8&Rg z&O6I(dqL=TjIv#Kqr@PVYXIF z9Ki#`%fjR~XYfe&;nHoQ;NA6Yw3$*cf8h+3V$PdDfsgD2^^En-Ho0C3 ziqHul!7_yBP$E(Ac%i)AuhSSXVGEePnOS01Ku{eY?)EKTg4y{e1PN@uIFp2u;Izk9 zJWCFzsF_7i4MFj#Wwjo<9y$gOF;>yK>?feFu;fcrFM2xeg;;$d!EPPX4Xn=-#*cwl zP}d{A-D?p3hpXI~S99kzh*)Ao4Y}jEf{hBEed{P+s@Ua&lB7{`oPoTaYhjwAaq*d>B3&an{oS2L-Hk9cB`Gk)D?g+ zWyygg_Y*5$X@2B*-1417wulUubiKzTeV+>*d|lU!vdU`lk=$XQS$Hz;#Bxcvq{-H& z?qC_ESz0)P;e53GsV&>?w2E!box|}lHM{i{b!j-AROt?6C$f;qpmOb1cW-X{KM5D$ z3+ad6G7$~3-!b;UnuVnJ-=C>#jZlOQn9mZc9M3-N0eGm?jk>AM|3O53!(Yk>k2CS) z+GF2ClHu8GlOYme>tCQb>Lhz_dV5WNCg9d6V=vE<#JO8qeRcc)87CopJN$;D zSOSEmxdt#j<5=X%gMp`42UF_-sb!uZP#!V@Jy31oSiF-po4KAkK#!0BAl6>-Rmh2 zm`A2EZfPfJKelihmG1X7IWu31S;r472P4g>G&^iw)@GiRUokyl}b zM9oF`tm!+)&os#;}vsz4EC#oXG+JPta42n<9nT z63a1Ir8X(Njg<8NAnpr7IGm4(Dw@Huiewa?>~HOCC{V+&yg}A}cn>BTNT|%fMMcC-n>u{SbCExY=foUbgiOhbkU@kjQq|Q7$FBn>()bIcr9|Dm zv>hWL;&Bj^1qYWmKTT*y8-Ai1ceYt?~kn)B%jNOiU~NQb}wqYM`aPkH}| z90j!DKa{7G(sgzWc%*9{W!~p=1y{f)1~h1GG6kTdl&9;`DVSmkBZ{_^M#8Gvy7W63 zbd2Hz$`#9!W-HF@&Viw+)T!Us7}l}272H@>h>!IcKU0gMDe5?7&?v}1c!(S9c>Ri$ zs09Mf{o%|T=jm@kvqBBA$VeCqs2D=7FQ?jpqn3qJsrxFNxCi|^muXzsXx>QE5b7pR zu41^nW6tt0O#-b)sh2<&c8B-KMQKH#)lS9$G4AruG_kNX#M4{WxgypStthd#X zN8Td3mg+|lxZo=|VTGRe)Q9l8pLn*Y*M*K_>&z(4yx)z5yvANw4Uxn%8JEfbaI>K+ z>Si>wJjNHU^x~U^Y|^_3zIn#&RlO{S3q48ZEEt-=d|~qPB>F0_w+LAzuTmIO4`QyCe0q|8)L!|xw|myuW8_SX!=&7)&58&0xD|(^!NrB= zuq&?5qCp{UydIvV+JBdWCuqh!zh`f?k+ZbU5SbPQ(m+$#zE)4@N%%oVmUbMVReV2# z<74xDuoHkEKxFJHcSaV<(&dh>Ld{@~9Q)lVqe)gs)GpP#~o~bnq-i+^RZ+iI( zDAHV?5G$hvG}UE=RG(hv2CppF`)XTo6gr z`Su9-aH>9QdVlFvJ>D)5GZY~)P+&P9;vM~`#!WEGVM@zH|e2qSZvVv zSt|aWUVp}0Vhz&B@#OBq-N-x@!X}k8?yxSJ{~}xeru81uS4)=ozEGJoYPKEx+_83s z3e}RIlq|9=Uhl4lb5~62GRd&)m>xb&VTW4Ljqtj1q+l9oTJ%pa?Z2aUTu(VMnBHGZ zqxW<)0Ujn0^^J~*XGZ^T=HYc^rMCp6wXrvtt26wxeIgmKm=E-L0C3@MsA-S*Lfgbw z!9>&E`KPq+el$cf=?lQo9|V&5f%kA(Q~`rIByl+fcBJ=4T?1~VOn3)s#18tWP(G3L z;j~yojI{=8A=tPZgUY9&ZsB?ds(dS8hGmp0sMm7dH3wLlKVA7<$=HRu2C0~%%_;JXHsSIKAd7cN} z$@xw{fdXl%J1}SWz2QUT7@si&uSE&Dp0${L?)x}#M%9i;vXvqqSOn)R7g*5d1Khbw zgd5V2m%2`VhtEWzT#voPD{R3Q>rZIckv$8t9obkjF-)e5f8Ue21x)M4k_>?UKVpe-TS%oPWn%#J3Cd(0i z9*2ZUK0ndN^4+W+*v3(jWL6v4FLA+=J?5L`x?l_6LFyulP?NyHpU|SE9y>7z zt&3($5XYjJ9J)>4^Y4I;2k4}L5c~9^i$_2LC-hcH5ma%^JUrro-xy~w6e#Bj?9L04E9oQ%YHsqNHkO zaz``p9ntUJig{zpk|qJKl-~&Wz7sVaSbUZ^;q>(#8ov)Env=yT5)7t(w9RbW&ZgDU>HL@|1wmGvABYc0c+KHD0hq_XOnm^#S z?r$cAz6(&2;scZgpvT*`w=uwh3GwoDt5_SKNX?RwT%KP}IaPNqC3nCoa*R^BmVq?T zbg)sy%Fid!K+{-rt@!!J=xBk2nwtu;mn+R>%qRkjFW^Mcg|~cEg!4R+D293ho!7>NV6YC@$$5iR^mZhrr*^l2^>*UBz$yn@4@R z3X(&hbVZ?xJ!7*u`#P2*lljx<;%VoGP{gVKw;e#H{dOsJ?PPLOe>9qxJke!UhsEhT zWo&hZsi(Oo{}TCQadV3nl$~!{^%Vf^9-PX-ZghvuRoeE+PUOb?@#3#I_D$IV73HHJ z1O=y(CKUa1^#^2xnYfzG?|@YTIDL|?)_~Kelc{R@+B8YEFaWNwcT*;c7(SY=#Mf!1 z?)Kpj{&o<^*>3B00f(i^VJGX2SN4UJRDMoCPT9hprxgCDuRIaP8S_-gfM+Kk5kf69 zu#RH+!-FfGvn;~>HjeF3QHVRwuksU@hwC%wTz&I~?u)1}6oM}{qSZiGQ&;~yG=p2{ z@ZI~w;>Tv+Rw9s90}&)U4iRScTp-IS^-sL}_v?Q`WF*=`q}`)2cEb<`Teyw9-rWMJ zkhcD6A65EVOot6U8gBN?mmOeDR96v(0%!CA{AO%n$i9@ul3qC_*(NgnxP6HVqTIjD7(Kd_oNzKX!s@ z>&)?kF*ZpJj$Eq{6C_Q_zE5wS?e?+zzOZlVuYzxd^8KXAV5rfsV`+g4r9K||`eBqx zwR8rAW)q2E`4SpbvxUcJxpw$>!bZ3%|D5QNvIm_0LMIqXToCr!^Z~e~iQjrjNC9t~ z`R0+|oo}CsR5i1e9s_pXsAM!BeqDb_$+ZA=xzW9$J8Mpbb*-7VODh7ri&GB=+j z%JmJoS5$w?&yI6Yh5Z6Bi=SqASj0!$bmA}w{z0L1N(Na3bEPW&XY0Gg?a@i+^#^;8 zE^MI-^sXcFzTk!5rQTy{vA=b?6NHMXQL);uP1%aar)f~^h#p`rVP@QS5DA2sB?Z6X zG~A@piB}J>mwj)t@iN%0&%pP80?kQ@bMzua3yO3(>gE=%QPPrd+LrzuUUI#f z^Tx-$^>rK_5I@tU>bGizezsbXzm=iIFAJz@amodkH{Jzb6V#8FS(+WsHzJSEeeCY6 zeq(083vK%yl=p&p7Ee3vm&9c2`c7Ldu()8D??A0P*moO!BnK3ub0QmvtbL@Z#0LMQ zr?wL0%*cJKXhHhCqo5EV2w}5ZwgpEicrw8f#Ke0Mj;DW~6!S#V;xqz%$$YcB-PPw7;ErTVbgRPa&|Gm3~1tja8U=;TFG$vDQe^;R0kT8DSj&%mXTeP9F!v&;5BX>}knDD1Q_%D^r;>8|gRk+xc~C!Iun69J&hg=bNL1Y9vEx z%Q3IJQUGnN8L#8+RQ^Cimapk#ha49F1g8 zz<}bLSRPzM-gqLfs`aA~RxU@ay8X7QA!Bi7-O|EuW7}r9l_ObvhAqC#z2TvT&8cGAG^GRWS7P;M18_{4r|9_xasr zB1GrEk4yM-wob^+e%9+6DMN<*fBQeCu%zI`-fnfB(LY#xLbEcxbt4o$LPwG8#)E5? zAvS+j9qdOQIdsTjVakp4oPpC%tlf%T9r!y&+aE!JAv<|_Z=?q>^w zQw(krCgB}hfiki^fT}jbwq_PZtm8%SFp{zJ&%2v&1WY%6_GsqM^m=TFS<-3J@G(j` zhP{`8OpikoFciEmpbEO1K%;WN@308XmoD9jOyJmgkkP|4o~$Dby8wp4HiXH>&?3C# zGYYpA*vxLL*^&f#HSY|+PJ}KnNd;4Ft&47~b6JXqB|!E^N!NIS1TqWSvYz+XdCfXm z#nR|(aGoCBggC#!Mo5$?!20dn_rk82SR&AeHicxFr*dsnqf9Z2^Mli00G0{J;WgKU zUHA9ZdNMa~3~)joQukK-Z~Au@Hgx^eGjgJ0W*112%rMgbvm^muvQ!1cEXr0*81t4K zfbuYQH(4?%X$j#7VQ7|pm#{8wIQvX>l_?9+v zGE1^JL#aT*`tAOZsSc*f>2gNqX3fSNW`t8T29dsIjWC>w*r^Ja_AO`e_)iZYF-pz- zM=u_QJsDUvqv`(UZ8XCxU{Y5xIB2Qn4X%8E?91vp_I*(o`NsOUZ2PU$0c^~n?9;)n z?g%IXHceHfdcrQ_Z=CGy9KSWF1NA}jmtBXOLMD++G1srPnIKhLMTPF>>=~Ty)5TZd z#f;x-5B>3LCi5a8A#Q9(bySs83Wdlse}9K z*t*yK8%6v`A2;ZEePdq^U$(l1O~Ll3|FZICg4j4L4abXg1h(Au#%rw(d|8NlLPVW! zu)j@2wmq(wR<*>+TuGh!Y7cPr#%wpkwzie)D>@6C|`u zG7WrCw-DFScZt`fRn;P>zwm5WqlR82`ObRF;{VUX{SSOgHD!tPRObo1yl;Z+ca8!A zSpS8bhn3153eglA2BD*ExvQnofEcY-$Ioubbbeq(tQLY-l@ZB_ROh^T^p598EpA4p z%QPYmt23}{COaln6!(dxqzuB4s*wRPY{b_#nrHW+Y7S_Zcb8uR<+SIw8^e55A-P|& zhy?y{A6^3Ct3ux$8H^Sgke#91Hh_?=t8;x#77_$|0pic{F`^O#<;cnNU8EPGeL*B! z{(ngW;LEyqzvN8!5jCKgy&&9f*=e6nui6z86q zUfetOH+a^4za5GL0y6jWgJ(LnD!f&Q-pa*aez&@mYtUY({SW#759!2^PQq6Wl|Gz<88Gw+FJZF^y{{2lEJTb#urHesM;(lvq)zY3&ZkE-Ko ze};Byzs83;o~Acs#5-l8tO9vDrM84a{G7^|Sq+(2I88A+b_tr+TeKp5BVf%vir)~x z+Y5ZL?~Y{_OWf<>39^rs>vI^&F0HX`8p2Ki6L}Ojd|{|OF4EaR{(rBo_>aNQ&?6d# zE<|J`BYUQ)5F_;u(r4}yf5~%v)#^5)W#XFG>>BBWq6xX;4%~s!H z<$TU`a3z)6-=nPV|M!BKflW6za!x_YqUi0XG?Dul`Wn{7`i(m=uOD=5ylS}WIcO%g z3>j5TBk4jbEi80+mDm6Sxqo%h*5_ON-77JO24Gg)2@|h*S&TG4 zR02TiBR{MSR%$V%L8w5Ww)YZVFRnWDy3=4LLs7pcf)Bp(NZKHRN}Ft`OFJBcWT6CW#+y$-!F==7$1XId}GLd4DdHMBL(&6^`b zv5cbN2AH|JELtbDY(}1sjiBF|1R|e*fz=A)%DVJBz`jm3of1Thmo@nIEZWWwFr<>o zudT6NcL5l;o%_>5sfokbPOm7*)2mMQ_ZILN%>{e) z;&06uCwHZnZH+ygWULE6d4E*II?ti(g4LQcnT>g>WRD%Y$Iy@acx*E>hRRm^$swC8 zto*I__WxxeQ|XROVT}(eMk!wy4Rq6NzhdJ8F)xq2I)|}Z|J5bdT}FU!aJ^L1DM&2m zL`nW+4gyx8?Ty1{YBE4>niVmINlBLI5sc57YOXlQUP`)~e|u;@>Lc0PU|a{Sr`TnD z&Yd90YwX0AX#Eq5GFOLM)m$x-C_hY!`>kos=AfHsdZ;+a6G*%4hn;z~NXk|cRTQ`b zA8VZ?Pyf#UV;2KApbKVd${qlY*d7A=#i}vB3}bp^`gwnpxkZ0Ud2oM!(w(H9VXHD1 zWMwcz-qbc6P8LO|0~HlS{_DkjrItBryDJJ54`A-(yCLY3``tt$NK}S>cDM7gm0H?8 z^oBG(6mUcEqXh_}oAAS}mYPYsw4#-V^7hSf@&`ox^`|a<7~>}-%~(_jP5gCxEp=>} zJY_%aSPF^Fu|JLq;dS3t@Z7Rm8Mp>d3A|kK|KACl(_g&be`q_q1yqX6oz$LjkP9VO z${vo}0y7WG_I?ipPB!e&^l|)mAJ)cD2XlEWYn2}i)SsRyV6QBG8`5;~s-Z5RU}4ad9fnoQ~2M;H>vixO4(a?M$=;7xo8DY;gmao`vb-^P?s833flIy$7!}>^6~|?`PZGG9FlxS z(zdm%ey$xoQljgn7Ym4N;Zy3ynz@z7>c%q)dvwV^So!@R^cVO}jwgekouhsr^V4cV zN5`GM4&`t?G}02i_PM;nz23@j7)e+!^&Oz zm2@%w_|J63JwfqO`OAbC#*tQ~X*;d^JHl`VfkMtR#;Ps{_ZI^z_xX3%?v-ikgznvU zQcGe#Xn$=#s@Z-PZunXv+9*?^+a#Ha-5*%P2^@Egaa{6-nzvAk6pzZul5;=aHABb> zhO(-5!PZ{zDyZ9Wg^|~t8dp_lVk-o)2#AXL)F~)Y=4zxvk00Xi_xi4Q?vz^Qy3U*B z-9l!Q;pAA1p>CgZ8(-MxE4~Oh(e!AM&;S1@d&{Ugwq|QMf#AX2-QC@S1$PMUZowTw zaCf&5+}+(B0vicVaCg_YbIy}B+pJU@9vj*FeCK^@mkw>uGk2N>TLu+-wPX@?SizW-rZ8% z$gFM6d&PAwj==NjIRt@X6lXy{>DAM-Bqj2B6g-()dSgo0?xGpG-oNg}{H-$_?8r*8 zbOW&rBHh(pQ)B063S&yIYK!;+W!#y9dLI3>`+~gFu7wwu>3u_`pCUWfn6TP#ls6Mp zyD(IsBJPe2*@?7(-g$!(VK4R6n+U!LL`#yU>hFNsrA&*#=F_)e)#7I0I8ARp#kZ#_ zKP}us0VR~#og^hYh6d`bqBbrZOk4-n>pWexK=n10Ostand~OKi{vwjjHPDO^*x!70 zLIQx>U7Oey;O;9YV}+Pp-9 zcP*(mAVrEFHCodge6shQ0Q#!s)tmriR4Jhwx-^TwHCgWAPmQIWFXh^~5~2;5us};a zpt~X;eujH6p&5w{l{(t@(tr-A`I6JOJfv6`-eH7~=aKtBvGP@9B`?6}&4u5!OMNaT znzX9uWg5JRkS&5TZzxfIQI|{C6TNRV(0FYOI!B?m2XtQ3x$Xw%voH|HCgUUk?qe}& z7F$D8AN5AZte17&`q0zhv@+uAK5@xxoQ5!2%lZ=hG5i|-9t zt?^abCI21j1HtRc@OlJ%+e*e1!ky3S&Z4;rXF7@Up%^_Qs>zEpW?vZ z04S>B=8G|+KGMwoqi6oX0fL2;#%*j}K|dwz&b#SCPx8)<)T{m*r`t}cmk|qEn|N4s zo@M@sv+E#F$RDs;RYi4>e$2TQ85*?9$*T`$qT3n#&B|U2@%*X&@oT{az0s}(-WFOn zJ#gF1f%rEpHn#1b#YDGDH?uA1b<865Y_E)0e@(iZNK7#9jy6`Oq0o3rFPf~dZ04rs z5UwHF1w@vA%*gMS$UViRsXM~y2FfsdjPC6!jXKt|?ojB@J9Dt#Ceuvp)@{$gr9;4m zPIKXp@xi^HpT+(=1Nb+@YgbFfXr*NZ8=iSU2z^lUfjX|7mT$riRAN9u>c^CMCesub=${Jt2b>VCB`xU5VXf_(mVnskb$O@E6C7lFZjfVb zJ;)F0XkxeO4!kfAPH}M;-M+$~2Lq+)m;t8c6yc*M50~S~>Jxc#iQKL;P^-S}-z`Ii zw0H{kJa(3f(d;Cw=xLl5hJf8(;|f$a043kL1?2oSOt5_VKKNq2e?JI?L5d+^_b5Nh|wp9|IOoM0`HOJLK;k=>8G)k``T zQ_tHx`LI;iv`6o*6;5vYcx-J5*LB$`Oxh9;lXXmTKar+W&XtG{jhci?tr?;$h$lHg zr4&R1IRN&l1fUJ&nO=Cx@z6cNC%N*T)N*l)sGmT;VNC5Jq2B^ZUEOB*9uwq5&>IlS z0|C<+pg?2oAM!>llcPpo10Un7$w6f~TO)R6vmxrIr+R|dErXiN2ST?l{sPD!)~2~Y z&kAalJ*OmrH$UM9cDBtU>Azptxq6Y(e>#cBeU{ufmbBQju)Hexxq< zry|$0t_v(k>4^F0U@J55L_O2sy;YEqBVDOT*$#V{3&p&5C#RVl(I(L6tMUhm;PdNI zCX^HE;M|vL=7247cgFVMY$p^Y^Bf}&uy-ia(2`U+$wFQ2n4fbojbthdaBo*VPup*G zjWv?UH5l&AT*)=mfaYk!9LwB}UUQ`@3#kj9W}WSxyHVa-!qIIK#r4IBSC%-f{vg>t z;e*&0>-^6{B!We-xE&#VJ-Hr`m;>MUj8()_jQBU&1L4-MXN-;HC`jRRAY zo?6LDC^P%%txjZU>{?M&&nL&P`k{JP7j^{B3A68$h&}D{^dc+9%4{W0)3vp~<+BI# zUAzS$z<>~lgRX_WG(Y}6;zk}41e;9vi5OE@pB5F45KZH!14on7Zel_zg&(SmyA02I z>$1S~y6s$hU)uDJ+*Jnp0AwhKR&&Jk2v!FfPFUyz5!SlXEeA^rpcVta(8~B;I^Vh_ z!-Mx4XYmd>pYXC4e?5}Pa85gA>l+m)sqUmBnfHDVTFdb*9Cx^xa7d8Z`wx_h{V$)n zdj;~ADI2+KlN2QewaxnVn$QS@sIbZMy)*5MKTntL?e>n$WlPUAyua6P+{NVa2{DB~ zFaoURyDS)(=Am$f2%LkTaFT}0u&4Yf7gM*18 z+s^b)?XPMNEK6sT&R&8mr~X-OON@ou5mbn@<_y;Q9L`S8lA#e~cDu^`b+1yI6e&fj zjuPr}I)n9ni{7>nql|g-b0mOnIbAvWi!Yj?jLOn! z7UYDCCM#MfGNpdt^wkkyBb!$udQ32{KRCo6tuLaW+@lm#?YEuC!uGcF<#WihR0r!w z7?}H{F+6NUk1{ntC$Va-V~u-=gLm>b9=<+ZW@*A*c7>aCD-PbZIF?I4p(pa)OXC9g z^%o6#>efAl?#q2yfFwgmHy(2eDaEl9_GS4yhT`M9WoFTaXeCSd6I1%e=sW%B`8)iR z`0MBSY-uN0>PUK9ld4wFKy6G95Aw0EIe;?Tkh41Ylw-Mjze1S4#AI2nob!w!>onyF zwny_>!Oyp#;4b*i;K;K|qng{0bmaEUWyODx(8kF^i#&2= zV1wivIs%AC#VAd50hI&C)#Z8y#Qm}mNrC7vhZp@~-8mWTSw)@N$vcn@LxV76OJ{4$ z!QAq5nV^DiJhrHx31|Teyim13i08K&-f1XF6A_HE$Wr59(*vs3Ix8ouG&rN2rQO|y zXRd<*3#Q$K$Jbf5XiYaNW%XJNJu6#b4Vqr=6!wDeU=JmKDMV|k{QS9}Ja3JvaJIFD$8W9CCv z`qMnk30+?fn4;bIMZZWjT?L@C=|z_Oupj=UkIpmO^?C^|u(l{>-5TKi_^~{1zR@uV zCK5Qm``chli6qM00EDmRSs`#U@j{&0m#6JT7SlH&ncJhYIEbLJw13ZVe{Xzb?EUK6 z&@8*W+rFotGVGtRzFbhVBWDkyxCNz=xFvy@$lD=3%yFS z4m(`d2_mODxlRKGeOxcP-4Iy;4zKwEaxi7{n@U@}iwJ|C(x%9?jQOaj-C^lGxY#;- z6k&AGAMW?^x`-AiIa0$vD^C`i5T&iSlRJkAceU~^>#c0@tg&2Wr|&l!mO30V$?JFH zn4y^YDY9JooxZ&TeHEm?11V8ed#}sGz0{GVNxJI|SD;H?!g;JWFtsYt>?M=XI@y|4 z@Zhoca-GF{uZ~*xATCP@VrpWeLG80M85d^>akF z9rjlh(*nsKla!lf%a?r9hnELDP5jS?3bz2)#j!W^=A~|3ehKQOE}9X9fC|TV2P(Pp z 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 z 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%0A_{z4o*pQ;suIp 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 ab97;Hba--QW(qblGC4Um3MC~)Peux5lG|bc literal 1353181 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

fa*cTjG%gqOD(eq2zM| zia|@=AZ8PIS18v;r1J5wQK@)2pWuHl(Jq+s1t~ziV+_`*eY~wAJ+OrMVJiTLTXL~p zoVC>&5e=v}LbpyC`8IQaAUc;$WJ7@LxG4shQaYmfRb2?}=ICiMvG2p!r1sbAUpRGJ z)yN*bCa%REl=-r4N!gxkcjx^ICp}h9uoBvQ*s+WP#I5#Y(hSf2uhckd#?Kg*IbaTq z>JXy{wCw8nbrfRQUB3&hn|f57ctSL*HLZ>!?vhb{eZ*T17!pBrcnjHi8h)r*sr^9q zIz{m9b%JtxP$__C7^BdDfiqHCZCAZ_2`#1cyRy)btW=v%s$97p(EfXV2P%H-;W|h4 z%(sZSgR76~3MmXjLqSWKB+lSEOs+^_Ro9nH=+_r&rie@xE)xe(mW}qnRo&fi@77id zZVDoOCD1p=&eg~$^vkb(h|=P<97k^J>REzvx&JvJ?{6Z0fbC{T*NhEY{af3wXPhD> zw1S}aO9<F?6#A)RwZ(<+MW+3n@osf{bh@RnXqxO<(^AB@OyqFZfuT65l0C7)^ztuN zo{&^tpd<R3FoF%<DU}xs`<Sf(Pxfh5Mf3MbqpKuxh~IrGSO*<d5m#%Z2(iTqx{;TL znk<KHdN|ysrR>9`2Z(sI5$2q=b|Qoau_U7QWYV;7$uxK`aMc#HhN41bTq*qacIZR6 zUTh@l&e(%&v^CUu{lgDuD!)^aQgZ`EHD${n=DB)!4dbl};%Ksxj^=lPSQZ3Iv~(nt z3@BVvf;-hJT*W<w5Lt0&F<yS~(Tdu0>&?jNh}j3eJ0}ev_$=oNKXA7E5l-#a{y2O9 zlC^%;Phd2pO=|6m)C~kjSi^rmoFuq#vGrLD0-_v;2A_SF)FE&R=3R|z>n$A%NWx*4 zm!&25DhVQdH5hrwQ?Gm9%f#cUzz?^YcHRQByokz2nnG;QPPuOOi!}zLYydqgz51-o z#qLY?_G>%k-2WG2?-(6tz_yFVwr$%@(m0LL*q*2{8ry7a+qO9|o5pNx+h_WI-&$wm z?7hz4$-l`x&vV_E?ihocwKW=(%-YzjuV>DJl0LnLe>`HRNS0eX*AuEITF-{NSV$6A zDATRLX3JWGou}xe8S=htvn$k#0My8ERN!eeOgR9qau7;cR!OpwMJ5jzHmlC(lXTUQ zyo0YNy$kCLNV~`i2b$G%fZCMn{nDk)U9;Jo<7S7oep0GD#>Pz~bu87|+r{&)X5-^p zJFCIR#!EiH=<AFx!P-m#s<+dLj?LW^0E>5}psT4!KMc!4RMmPq!wlizIxHSechm%# zA!XX6%x6Xd(7t2mhZOjbp#t0QaAfh`o1GaQ%?@+-jV9XIAx!>Lof|?9srl()Z~2ra zo5KM`h;;Bc3(oq7x{YZF3RL=yQ_{$){v-^5R`-nRb1Jj6C*R^vfIi{pyI$_sT+yCL zmqPDRxEW5fi?=6|8w$3Y>0YWJ>Ibx5`1(0IJ&aSA@{vI59aA$k>`2yRxT!zU(ypTk zDygN?@EkgNo84D`p~kA5*cZZs(7sne0K8+Ci$GmSao^?kGAIScg)G$&^)onfHdU>; zGw<+J&k35^G#}D7@BGSs<lneecu8%s8}@o2pqvwM0VsQVzIE=dGZoTUC+Ms-FJPK8 z)Z`tK8r>;A0pb5NhO>RX?FC-0^WSgaHg#PKv|7O-nkWxyj~S;S_6BWnA?_Hs1fq4) z?C$@KEEPL5eNDN|6n0;E`s}c^+KQLEnuhMAa2UXO(BgzL5)MJehn%FTb+kgp!-igd z3mx%zBBk90?ck85{2w0=GP$RGCs$j7j(emzFDkhmET}W?CN~63Y9b|l(5Qs0V6WgU z!r}~lYC~#psQ{+1*dZK3oIe(9pa#i!qoz?HALGl9ihfy%|C$@{`I4Ol(kcy0W80Oj zT|Es7FTI-{sye;SN~+ksX2wv$pFU3vt~)n}B<XDjbEOCGH=!C?`TjHgJ-(vv^pG_7 zge?wxXTO%wVEXgNzPJvmL11Ota)8fYsk+{s9F;jr&t|#I?253Q@p~NrZtD<n6w!B{ zqAC@R(Nr^*x9iG`#TmL}U`FOg@j_i#Bc=RckU}?N^;ceQy(|oF&gP^_t4D%oBC9*u zZJ}E$`_;|xfkZ8%A&f=jRm6Puh%o_uiY<n{KgN|)qesdukX947dq_qksqzUzeIo+J z448gimy=V--kh3H{R+erK%?ZU-^vme6debcTFj$2zRqU_N~@MYaYRJdcW@OR=p$?J zQfDm=Zx&MMN!TN3bc<n1Wmc^AixC;slgruYBryh=CSYKhI59y9S~q7RN{CEY;&g5j z`+lSqzBcdt4EjTo9<@--TVP$D3HGh}=Q*#ce)SK?8HB8UyqxI8xUiX(=wBJ%{cJW5 zYX(h%*t25qk@*&0WZ7-663ka#sZ<+e1TBR7D(yM!CTbx&=6f42)9g1mpe@t^tL=8% z8Y~S%f9TIA^_W?q{Ts027P3rl=)s5l7vP@KaGe_kNi%=VcwIl^ZT9#aCJg_%%!-cn z%S~@Kept<RKMo4$bLSKZU@u}Kqf1re1n>pal5OtbKpM~kEJdW6H1XIkZcXouM;g=V zqhb)b;EC&Jt2lnZ{QOo09})16I4&#Gf@6EYLd64KX!|~HNlkA$d|*2F_<7uZRV2Jy zRL$w5Z>7DrAHqjqj%um81s#Lap8aA)R~+fIt#__wbypNoix1HW9mj~XQMdv_kQUj7 ze&}Evc;|*X^9+*O*n@s5#Y#?|w3Vkhk$wwHZNF(y*_v|-N8*y1H*Tfw^P$Jw40bIa zp;%jTvG1E?i;?U5Eur=7-*|AHO|U-QrR7M5=*Y9c=~%H(AXaAwg{iG)6434X{u{8n zek0Z!h2EGIeC?a`-ArMC&`UFOjP;1SZSHZtxQmu`R=uqd>Cz)RSmZ35s8U*a7&@NO zFrFf15ynMKRfS+oa|i3|P(|t-M9>%SQBxGPA1F93{V*m_s10fu&2ZXu5DXZEWsGLd z2?TKzRCqLLNh+~3MhUDGJ`y*CK~RC7S$t7LFw1MtG4R&ItX?rA0r{S~ZUN&Fk&(IT ze*<-F)ib67uJKB|6GPV{-(_aw9Ghy^@}uNJG34|X#;;KukGn->?-PQYvyNA5x0a!p z;b7y4=Ni#1O#Ke{nHsXaKKERBZPAn|8UZkrnHf?=9GNNBgO(47mPz$GX(kIic+rpF zv@{>ivVIAYX5UXm5jbc}uYD&BX2$n(9CY`l4j3e@M+t~bLTB?e7vT%~m8b8Iakk3h z6WWIdZkoXFtNWjoW#m`6g4jQ7x;lS8sHg7H(1&_r+6SGqK;r@wYh=(^MX#^t8pMiX zt{b5OV<VSD1@pn`WUAuoyI?6iumRRI@+w~w;`02Rny2ND?F)KqN1bZ3I76^;4`=;q zBTYfs$vdhn(O&nXb^aelU1dRpOCn*L64wh$w#U;2t@WL?lufTdb<DcS89%h_$tfpv z8&}K0<&l8c{HW-ke2``wp2OP#pD&q}H|sxB4F8VAqZ^;M+Gl&l7+UaJ`94*wc3H(p z80@U0r#nXNuexs>0$96(&$c#NU})byj;4>bOkWp!0BD+>(wXrx7`f6&WWO`aaRz-x z^E`(!!{}lhGA6YG!~VoDADZq$D4Cdwm|$Q?^p9$FmVOUm0m2mzv56v@sh~F=M+ub~ zaifus7e^8qgmVWXYe%V-q4w9bf@{cm<Cs*cxsSCh8}-LgqcIv7jmecC)NQvxBA%e0 zuLPS3fT(W{6sDuEPRE0)OO$hindsjdb(pog1NvEOy1%`AbNk)m-zo+dOIx)p<Z$9S z`iR_){F_Mw`l)?I&5T8mvD}X<C}Te%_3viUe3gc~?2>PD{m+W8dDicQUX-4>{xfo{ zp03A$KN}Rf-p&YLSHy?;%zBuX+JANUOX63C(DUr7Xu(<6kK$Zz)DSm6MR%5XU(V4G zW#6WgP=0SS7C7K?$eCUq(yiA3@1}Dq9x`?%Frpd4lN*PYwqMfAfGA>zpd+hfE;moN z!rop*I^zA$Iuld5IYOnjKNi?k`G2CNjljZxf6d=D+d>pz^LM1f+mzhC|MNQI@xojx zaQ%V_<6HmOxIA_h-?E_f!$15B=L!T%qBgt;WpV>isCzeoCJRP+p9O~mmB#wEQXQfs z%kzsuq9exvxgY^bpEn9>tBdHyk)N}lkN{jQ89oD@?_F<pl(5Ilh20-KX|jZZKrIBB zh?hrasZS}3$HxrC!p9LQt__Fvx3pM&_K)b110fC<K^{sm$u#Y$81gApm_7b)-2n+M zTQBFn@B^?j>sMj42fIkKUmoq8@P*x)8b`W$QTkmSq(`e-v+DH4VUF1^!;(-V@=*&b zU)Xlp6H!If_xes|f_amqtMns=R@ID)L!f?*(^(^l;T++!Dsf^vfp^WPXc<MzL=^KA z7-`y@%J>#gB%{~!sYCWN!jz87oyo7*cq%C8eM2^e9>z8%K}3X|=y@!t-|BWbF)K{X zZ{I!#3#9f*9;_+JBe3>q$v|a!-M&Ygo*qPRb-#@L(}OZ3m%?&?(H-=OSZUeN-V#mA z64Hs0DVS#m??GQZ93ZX&{CHUBKXN)>`s{9DoVK)l>oePpA!I|YjL=CH4g+)<2{%Aa z826e9B)Uy#vKVdV5zWyx=x9-cX`liYpf-(-0m1Ncts9$7XBJDpygpaHUvI2`VX->8 zKB^ji&a983luaDQV+DJg$FSN~zaFY{GK0e5<WOH>%&(0sf$=h_v^4&268y1(^U<ip z0A2atlSl0T$d!P_A^F6=ZzE&DATni3aCvPf-uf>1LEuc0ydUOob+m!Yfs)NVSLMg2 z=%;~5J{gYa{#@9bEJPOh|Hdfe3Qs0_HFTK}oi}EMK*;SD8K6NC`Fg;tT8D6vcX1z1 zg+3G_EPS27X=4V<5p&7W7Xl}{1725DfJnZGMj?M6ta^}*pS>=6t9AU6ZoR7LX@rwP z;SkT<b1SBnmu|uX8XO>ssLC*H+YlEbjg4d3ifZE*lL$&2e&6?lvCHnVeIt48-R=rt z6HC<s+C*1;qz7+L&|DztkmUpPv~;*&3OU=tKG>|a3X@WHZeoVHrU|wjuURHgjJ^wW z5|sf5n@y!Q5UoFHKM;Z3npRaz8zU`hvMsPx?3nq^Ik`9ur}6#C1|e2jxZ6UqwG?;b zqLsVv#lc3(XkqSzja|bYK&JYmKU@fQ#-yyN@SwA}kr!*y2hr6SabWbF&GE!!V(!kS zg2t4CFjYB%%&PqyturkppnGbaWVI0_@x+GMF0XBuq^K`fArWTw!Ph{`SyYm+eeOY= z5K$_bHak@D1Mu{I)w!OGpFhSLE}UMkJcaI9`<x0d*itQ08R_hHC`X0uNH=qTttTMo zL<1&Zt43s}^jqQ6>#Z;<Yhoh@Yf9Jyt<-IwIh2W^{+XHFlrDQl43cRP8)_9{^hC66 zRiv1>Dou)s(Fx@^<?ELryyya;jz)N0{B?KgYo~!`Yc=@wN4mIQcD~guz29t8&JYFG z;aG(1zI~hI%Yzl?HTC0hHVFl%Ynh@0H2ixNS&3nh1(+d#`I7ZzazxXkDri5hg&lgI zWV8%UTMsj6OdkeoXR{StE7;w9=xm{sZVJ47soPFwF_d%$9%k+bXLBSo_3bZjI83@! zFQeerD%Im3ys{F0X0Mg(G$~c#ebb8SB9lr-;)p$fO+CE(o*13FAH#@%W$0%_yT(Y6 z0>By`;fAZ&IGS`B%DSIJ-&#+&iFoPvqP#B<xRx0v?3Zu|ud7To!KNQyPW&OkC-GOC zj)ykcT#HZgX5j+_U1W*U3WBwMTcfBEz(#e%IsO-Nxn(qMsvz!-XKW9R4v#ZRGg6Ti z%o!Q&P&Iphxl+6cq9<~UDsEhM1w%Et<>~ULHS}dLOP<OoT9m1*#xGh>-whP;)(d5p zop$#oaSadQUy5TFJT<r609|%;iMxs2fG{d?dK<Ly3>29`kVi7AM2(0`DpNWtrb%25 z8hQpP`r)XH4w8_HNg}TxUR8ji7e5RP;6WSk94um2K~Wi^-g6D{L+A_QwW7syihfXO z)%n{j5|hB!GtJnZk5tHd@@ISdZ1?XIk=VMf-ErOoHeF1t(wt-J;8wcGVoBW$-N|T6 zMyNv|;~S%L>le%a7~hZqANgh#&R8`(s^f@lIu8(@vMgkD(j1jZ!*O1ViWTA2>Mk&q zg5%+KBOh!RWG9382wIXZsvC2{8^Y(kwM4h|(=*=Xk4L%sUr|aK*7?k#Q<RN!S*BaH zwnBVoI#`>fjJe5zX4V4}(#C6PvY1jRb;8lnXd3~i_pdbVQw;U3U@&@B#2}2^LNra| z`c@T#98T0#(9goXI02T*sU(&Z9UZ`qkx4=%H&{TLZA>+ANy&=;*bS~_`js^7BqpmD z-%!1<&c+T3zm;P4MxC@GbzWjPd@z$O9Y$({+gRZ-KPtxU>x!Z~;8JDp5=rhuHeXX< z5muOOH`;rgufLO`4KB%((I8U2h_2WlBByJNyA;k68?htWmby6J+e&67Z_zWLUIlJF z%tYrigz_W^kqmuy$GR%Wi)V*X&IZQo;V-m1YG5n5JMF-)W37><MeV8R%1YxTl5C`s zFW1W<gGy-lCglt`N4_tsEv<%ZFsME?<Y8g8b?u@UfmWh&N(2>UMmtjQ%%ZT2jw;Pc zuz{eNu<0_7HT-i*VSBa|);MRx$uu%&=$#r#Jjq7eH~q&6q1Ey$H!FG_6<A8qsFg7$ zM4vl&rXW<fXjl*`qoh&%`RI_vCO5QK0_mFb{Gq2%6kya-kcW`=yoF^W^p_*USkf^A z*gOySjFz-fD?Ztvfqd5X)cSB2jevNB?*ElWaVCZD)?gfWuQc<1KCaev_fWIwpj<!r z@Nczn=qqz~$1r?dkK)BTLv{Q9Zu6)NoBIU!5yJ}@9cu;U*d4ifim366@c(-wKY*Z` zP@tIG!^;DPa`mYM{FVogMZxKW{hO34m!lv0`{CSYr0r@!vTyF<6eG;x$_YIjih<n! zw<9xxixbSa=_q1>JdMB8*(QcPIqbLT70=95lp_tlvwO@>UdxX-ZNlqq_yHFzqJ|&% zuzA8A((F_k9J?W>X^|QJY0o(=&AgQGrHX&kyoTShrVg;lf6WBCNsnm?1@VuE(Xp5^ zIF`Tk>p=w!)J(IY&`x4FJJkrLoPgO249%^oF-+ApC^`}R0ZTL~)hd@8->Jgru3rhz z`Y&$2H1&X9Wz?y%06(!|#bOcO6d&|U7bMB0lQ`~thq93in0A4^x9R)s-rHtRlhaO9 zvmV0`d;%Q*q0rDqVs1rKL2U@B-TY0S+v5H9OH0+uvrpru*BI~QI7f{+WdA}lx{jr$ zi#uCR$OK0L>M{7WsZpxzX;vXH;?Jag|MtLu;_wV%r-IrWa|Q;=4M?cqbll<TA>QSr z?=rPz`@`lq(HnjGFmFpeQ2iu=q^eApmN29%KqUAOG%?b(y%c-?*%*exZ~WJd^+(r9 zI`lGFX@b1EnI9kJ#5Q2N2-w_WSW}=!wWn(eL8eh!4#W80ZYx-&Oa!%1pIuGe2Tr|{ znN@_}RHjtD2=)`|AKj)53uZMXj6oySUdLUx_8^JDVeLdH=pbj=4<bfcME><WiqP@l zF?kRdCB4ON^X0If06bfF5=FjQ7=iw8WHViT2?lDkYnO=AG(II6O;jCV0Y7$XkREb5 z>OPL-mxNb+D7CZiRf92mv<5zvotYLD`%0n@(;!(ID`#vrL^8?R{F>suomSNv>Y9-O z+d<<}OZLytvmVOzU{p!TuBpAwjb+pWQY!4X`Qj_8qWm_Cs+EXQ{3sj0&!aBZE#Bj9 z+}NR|FTTI?42jWGnD`OpN)^jUe<m%-Clh71aTU=Ri$^Yh<<me9u7leNl%^^@Q!c72 zny3HOFQ&KXj^QPX5IsbJTh6blSoK$YI0D^!3`qh(yQ&+Qz)qQ{fR3RBG5r0vGY*&h zQXx-I3t|rh;bZbg;`LL|t?hNXumzcN$j$`>cO17ur3nl`JsyY_$eNsDx495}n>|wf zF_DNE)9LdP#%eEErP+vz)&5iCz`Rji;vK-JAP`3pL#HYPyjd;Fsq}+z)CjlkoeOrn zfLtW^KDyBmFmwhn``O|bbuzWjKmjl#`%jK(O_!9S2QbM9qtU*b#%sA2lX%-p+5?9| zr^yu)+BMG09H;wmi9rjyK73?e%_XP`G;s~MG-~`8rza$QI|Z*SHFz3LWtO?qhGnYK zH`yN$<;dR{%~|^kgh!@jlP(CsnVppX*>#ou7B#|Y^7Zv=c}3gDdsC4bA1eKiAuzHr z!U*-Q`B5ic9d9&0f6L8sZ+BfTWwTw?845q_-=2C33t0fGkdIQYjHaDn1&wZ0jBK^7 zQ{|XrBe)kP>*$FIB2de(;<;Us`U-jgH9x-ob3tVjvb9ld-Ms$5L{TA%zRMtZKZgP* zMiV5`GA)W?N0Ts>Ea^<%W<^RRDHIC~vD0Gu_4f&?M0Mk|gPX*0dl~aGcfk*&el!fR zi_pm)WY_qop5eaECSZZ#Q(d95ArLp(F1x~SwHVPu^ptwtdQ8=x2U7R2bd=EYnO+X1 zX>=<uhV97A_1VMll2VX?D+K=Zud}qGgDajidxXmW*>%-dMolLRX(pMpthC5eN41#2 zXtI+;-6YjidX(PWWW}A6Bl8?w0BSt02zw$p&AULh=dXR%ZIvGy`#E@EGXR?wE?iw( z7nxKJ(BX_vD$mk@hp&T)$!dKrP?oEgfbYow-ppTpKz#S`wT_uzw;7dF3CxI3BGppa z>@Gi}jV4pP9)Oo+GNq3#CO!>*60Vt*KCt=msXIe@du&ZJ;4(@)paT_om1dS(*I{Sh zhksZZ$vb8P=}(G4z<CJb36xGNw$|4}r+V0QUl+FoKLQ%Va-1)lwmBC8`4IfnKc%pe zbxG9#e7r5eN&8pzL@GNvYpkFl!MI`Oa9oM;1(|zQ{$F#i2=vnuDhb_Tw^epxyehL7 zxF9ty8WvT$x-38%EOLv|!KS)`GYwgZkrgJ2I@5JwW-S`f^XV|tbg91>oUA<Z;Q1h> z*=&@X(y6Nvt-VKscIBGcZ7m7$E4G=jhj*cYG%<YzpNw){o&)TiHf4He;jkNX=>e|= z?vk$l^LgzBbQRv_<~lsr<mM7Qkt}R5&L9yy>e2v&ZkB23b0E$5kgmoR8yuK#YZC9u zub1feS0Nv#mp(y)@K-;R=N&)fG|z`TXpl!>`>23A^ST%&G;Xddxc|y@c>83{DQ1Wl zF<_$Q%+8<-<bBzmBi-UA{)*LI?uI$p2h?{jKbc~zW`xteVnJs0gP$POb&+sDC!)9v z!PcNX<fQ2eU{uCh$FqK%j#X8U@86eIT7hIAlGW$%8b`Nwg*XeLD||t7a!SwwNYpMC zXi3mWS!JHwOi@+kJwzenMR%9TZRo!8_??`<YyEKe2&dL6<S-xOnHRjnE@RW!2fMPs zMit?c4@GRX(>?peD!}JV`EWeoL6?RwEY!NJY%KiAhnI@_yG3P4Tv6NrDewE)Vi)c% zWou<B5fMB*G<v%}F^gWm`L~u2He8#6*A^k>++N?E2-L}el0f%E4%>T0rX;Z$B-V0q z1Oa$i7yD+mC`whjc4@}1+|jBO%V4mWjl7m;v5$)HzP%-=Ih<Ik6vDmJ%njWZXR=-3 zdxI#zKuxK{=*{SBE119X6)Tv#5o2|@rwp{;B<O#^YZhUos#-)Nl*Ktk%Qi~=b%NPL zsEy>+91x0LSCuGHgz176T>NJMT+WFJ|5c1ngOeGx&B;;g&K!Pdah4`%GaQNtGxNR* zeZTB-r9dkjQ{AE1R*$X!r;7WlgDGD4HGmZuLziM>ZVINNAx)a79?rhT%NaqAO$iaI zS*89uCde7n7pf+h$s5xq3jCg6g!WU^+-f!=l!`DELYr%O$lZi4>#T%gaT%pkD-C^L z1EO?^{Y7w-ox=nu^)p~ll5qt>O^();I}$_sDa8QVpEOSImw$EQU)`+l*P*u=D0kqJ z?pk%Ai@3f<l3kDE{Gvh2MS3bg)5q=L3NoU95LO6YU&S;MtDYW2)jIi788XY%t5(gI zVNnN184Spx87br1kV#(U$y5KfAum)fW|Hn9s-0)&BqNi6(+qanu)%fnhnQ}apbF)L zu3PK-5j)*oX*)IW3zT1QDwVCD&MOpYHeTwtN<7Bj(beou@C{W*5hdRII;qup3vv(D zHTFx@*bF^;A{lN#W~0LWuJK`1d<Cu#MDJFhXxi^RNqvKMy-p}(tqK;+tD7AlW4KD` zpk&&WX)+3mk2P5FoC|7n7$6wXsY)FA`32X$FLD3H;01YUxi}dhA3jdrkcC#HGh&?$ z8Lnx+w$Wt-x`ya1?6=t{mBYO^n@I9LY8d+dn9K|^CmB@|otaKZk!Zv^rK=QMWoDFe zLDx0l#x10%1qrGZX`t6k`Nue`7wb=5w4M0Ki>c=@jdt?mJAj)t%lxDIBa>af*)UD9 zm4IBO;ApWzBOm_Y(NYH<r#XJ6I|Nlh{MW;awGZXOC2OOskL9mhUuglvSgzUGJyx&< zxILph24FO~2_EXd5H!UAclBjXXQ4BAp;HXk{U4mpUkn%OcKk@Q!yxx#J4}f{+98~x zn09Z)R5xt&<`_nyCUGdy*+@AP2P$cBaO*E)v}kS`KziyK?AIYWpPL<ybV{d`U=PmL zn1ODu`&xg(t(DgO<x|@1C;zV%@Qf_x)jlMK;Q+1R5sn^+VF6Ts7Ima;B+6hT=|Fj= z24a2-%oMuMkKgSeY86Asy`KS2mp$Bg^*w}X*8Za+JyKA+;BiwLJyGy{E&)neSxgd0 zAMleGAK;=x1cO*_MV^JBXzfERw_OgWiCtWGEc+)IL1bLd^B<rQG;4QCKj|Z3NUQvD zAv0@NzsB&8^jZu42UK<)PHE{lK)hI4cuWqpzz?&0f@hs-&|Cs;A6+EM6y}4WlWIRA z7r^qJ6-fS@wATs(Vs**IT6(dAxALl#nIb@tqjsza4x)C(CX)9?W;|?cH7Mbv2|0il z2V+691${~W=`e%s<|%(-#P71@$1)e;*Of))<v>-GZRBC!wwqXk@h2tw$`I!$n#BZk zL<pQL@U|g-D5#p{m%rQal{2JVu&~D_Ef3;;BoF6u1mB6-jx*w;a?(R2S~`_<(H0pf z8Ih84^f-B%yebP;scTZe1}e?SyLBYmc=(79D?k>u4abGowBUcnCAa?%GESZnOdNuY zBpbF=$YE%mX;D&9=YuFkI6v|EsC{tD`6cm^&u3JVwM_W%<Hr+PG$Zn8ly7KvnF}vX zSC%9t+j)_iHw#f>vZ|<nt#}Mu%FYxoOn<ty8%l0t`{|!3A-{L3v-I82I)|!Qg_(}+ z!CKqIG!1s51eHQ94)cAAZUj>Di8SnBxZ%-p<Pcz3eey07C4~;7rW8JCv(=H2kE$0k ztyX;}^yK#0so?1<8=6D3x%Zz6R64p2j!h9edy}tNgrW(6Ufb9Ot^DFRBwiaukNN@^ z$by+DXN#loy13BLv5wxU^8362j&O+3pq<)*;o<6uOjfPTVHQ^TXC9Vkq_P@acO-;f z3q<%;76w+ayo;u2$<7+ks5B$Jz}7%Q<s!eQc(TxI0i%|pSULm~2b(3RAOuyxcZw$x z!z>beo_7JIQbEYo?DYF(`bw=GpR1SnT_DWpOCC3r3#F=`R{vQ=`7oTad$?;9XY0kV zhW-*ywZO+kI%$`Uz(&Xk(0@_W1@}*i$vkf+IV-$^JlgVtea3t6?{;c^tzt1O0@_~% z(pj!mm}1-bI6^NWBPs`onoM+_URx=bsA-FCKnwv6BR*LEFVian5>G)RCa9Hil_Ax7 zYE1D%1<}6|(807ELb4Mz1>B@=Z$RF&hY#n_p7VK8chvBikq+dn=lLd=Cs!nAn)C?E zL=7;kHO$hAGT!DOJh)ng!2h3vq{A}YR0yaSCi@*=<k|8gK$ky?`up-vXy?Xdme<my znDghOGp~_q8|@~Sl@xvVt)&Ym-aooo#Enq?^+}KnSjBu0h%lm)=*Cdr-WF|-3O;+B zU)YygjE~Pm1qnA0g;1tG_J6o5lmoO+n#sb*yJ;Ym$ZloE3)~}_!@zUIN#IqMI4=3Q zi{N>@yohQoWZ>R{u}f{=(N@|?2wh12OZN4BU8J`V6he)%A;huOzifTuxJ0jmuxeF` zR-x{@)iRATBSBR(Sn@gAAzf#GhkPIIl$h`pre<kYh!n9=G&9@`g~bh+qg(*UnCs(V zHm2fXZjzCc#~=#A<*PFtIqhe*>7OeTs_|~CflHVWBUj>6dl9PTtxEDr+3;JJyPaay zEKwoiGp7@Uq>CG-4s>V_+{3!s1BD0;+l+TUg?Jt_O0d7rY;w8WqjV9CG-Bj<G|OVO zny7JBN`pnCv+d=riN1x>U7QSK(+Ue5T7UWOu~odg>23g8)Cm6)m3fSrLdoPSA)$rA zTF4Tsns};g)C>^Ya3oT|0Z$xIOPQDk_Z6CHCM9kxg^qgaZ16x^q{r5z5M>HdMm;*} z*-`p7^~bymIQBc^QW>DDL5CsUH4~hJ$yIKU7c)?B^03B>1haAZ@a^tvsgcK8Y3DEC z)-~uq%kO|g;{@7^4M~Hbg(uL#F)ckK>emp!6Cv}&QpU%uQp!Q^G`-`T;(vbHZi}g9 z*dle1!1Pw}zX?oE>}mly0mO(eqP@U7``abqSpSEF(gR{QkJ}M`WWU$)Afd(MbW($r zK#)qSh$cm<La;m*&ghk?$1I8T;sn(cyyV0c%1Q&MFhgkBKF2nx0OcPQwCaiFEWUd> z@E9m{$cVwH`L^rBRXV|>y8j3MbU}0iN%)yg$fRXa0_h2&E#rr^Y$ElKI12)Y)dUX0 zyo8CTTtbC}`n457AqSj%8kB{hRsin=yfbCmpXnr4Z|mYX@Ex(=6l9cQ=+P~3ne|&s ztQWcW`HVVSjsd!ZZ>u$RwB8e;(0wdetfx@$Y(&MI98PhQFugM0(1D%=3L#r6tvhr6 zxb^!sjC{fgh8ny>G|wS>SkxfuCOBEdmXgRO54?*;TaFr?-gHup9uZy>Ez<>#6jHz3 zrF@Xq=+&)yWJxRR5#RZ4znb^B@b(WPET~YQ^JzA>F2~Z$vbB~B(Hg5}IO+v5U^xOM z$_ez>W$Pysq7*mFS{jm2jE7!@7+N_J*f^;vxm8+ela%xS2@yRGsIGQXi>f_~vEmn| zWR_oClaEzh0@s79y$%BhL+UZPh584FY1|J3J#1Lu0gNSUQ<Vs&t4{s4EV%LeZBE)E zHbV%vSCB9vop!HLK_yk^91b+Dp+K3WOMaiLk*~teFT{zkTr7hOAq#5;-(UmfjmsiA z&0w(B0H*kPS=smvt9I%A1nvtJt2gRFb9yNiO_eL{b06vP|MVs0|5e$hnc9P7qXJtm zklQBxF)IP03UJFoHxh?x8keX3I(O#?o}86lkG`$xuGNs#qoMxhTsjjGiOLyq2Vagg zajc9)qDkK{2g6i}(LbI*hs<S1YIRSH!52GYaZ%X4*%IJ-1^w=X7@m=GcOhDhNBU1c zLwnJ9o_@=Q<zVH6EuC`X=Oi&9#Sk6vzKQxuLWj^H{~b*rYa!~K{d2I4WF+PqD2cmr zgV;coCQxLti;fG3KL_S+ek}w=c6p_LKJZW0gj~A)l_CB%jtv_XS{~f|jSWMTOj1{# z+8E}t!(6^$YS`v^`w$Rn%k`;5H_f(rU=F-7DX$+Hf6fQ9pUd)g{L$xiSc#gP;~?X% zrXs6HAzq!gtVCB6&5@)%_9IY)=_JshKdmomvrLR*f{!alg$SeK*H^)QTx|>7*&W6@ zR73U;kaHiHugSZGbE;Lx0EU`iw{J5y16D_B72XfELtqvy+3vC=rxpJ?+H|iU#sUlr z%k3~9=wt!nP~Sh{O=)KS{zOTf2bj3-ERS+dHt4IK&9SXrqpqc@Wk!oILW8C3ff>`6 zuDU}O0*vW{8;;JVafLv#_x9lz03h*lgXg}A_F#j<ZXtNzZVWaO428==hrVo(F|3*i z3=cQnb&VxY3@p=@4Lb;t=XHl-4YU|NUoSA{p78Aa*^u<UEeKV$cZ;&A??ma*kgQ3H z$Ee_@1eEF|%bYwh2QSflt3EM;vF7Y+`=>Kx2K#~EiGU!sL&pCCv8!Y_pTWYpkUIEX zq2V~MuA(>*N~>3WFPD>-8LkL%xw`g~XwhbUYnI9yFEc~Mk?I@!4Gk-}y4cSVuq;NZ zIKC!-fgAyb*2|L%jc`HpybP0t*XBC{&mZk`Us8mop6-P3;Uqt$a352V0<Zt>uoFqy zXgP{wTze|S$x8Ban-A^&$cZGv;b7<tTS!RaFP1MLqefxSLo&iFvMcFm{91oEYw*k6 zEn3{=GQG`p^@R8-4P^l`2o9j*nTBG~o_32odqa`HJ>~8g<q!Ln)tDI0Jf+8DVvmy5 z&O`Nb7&7q*xKm7pCF6wNa104~>pBjI?w^CAmK};pRT>YFB(l~+A=_-E-9(i3QlwYM zj+d9jR;S>s_gDa9Qu;QWCIMX$DS}TJr?%7py0ic^xT&k<?AbnBDn{9NanC(?G~~Ic z{L;>e1=CbBT!}V$Rp?#4u5BA0!6@0z59zsxBLth@@|Td#4Z(Sh;LOIq_G8Q2m-8S@ zT=(HCa~?-(4*qdi!piZDIxUoJijA10q!}vSBB}B>Wxl%~0cPBut(vAD6iU@j?B3<@ zubOOysK+Z;xv4Q<KXULjxtU0X+%U~2uC<|J>RS$@^m46#WAzOq_Kk)&t+@su3EEY+ zr9-LKLsZzVv{Xtm3FLnU&)gH;e>CQPfFdKi-P^_AXF%?y6>B%A2rRnhz<_T3^L@Co z4A9Gkd)JyrzIs)zMLn(j-*X6i{PU2!ay${*PMH>xC9jI7oDwHPy?*ZB5)UpOa3pvZ zi&td^3WT(NVHQ4*8GAaoI}%175-X`SRNC6#PKR96h}G}H-U>8+CGNRkHvoG|r-<BH zh(iF8O148>3{yr8rd1FaFaQoTUEUq;qu7g#>#<5&qjXZ7SI0)#))TVyvPmyL;%<t0 z-0*bK*$s0XBnMM0@&NrSAe8d4Thg}@t%Ur=U(w#g$Lv-S2uP50bRo(SK?tcQnnxk~ zKnFyPqiux?-8Z>CP%I=Av)_)emw;B#SjLo3Q&$^AA{roxdnf30m_4?c&1`%bt9BU1 zM^1C~@<IKa-F9to>11<cEu>nS?DY70EqPYIv($5v^0)OHN1_VE!nXJ4M4zO$wOHO# zzkH-|tPK2P60qnB-mET+n`%zFyMo13j~<hE^ssvMfW03^or<e?;h>o9=6-{4^S2w4 z27%EaxWGpfC|F4ie&I~=`7}rq;q;}i0+umI9~rgp1Rhdnea9GqeC5e(W;=ZC4{2r? zNClPazeHRDmHqV=B~bB&h>y3c`4ksR_O|b++SlbJHaKuB2=9z(=|`m5B~@ZzYE%YN z7w{)>oRZYYGng%gHTa4K`W*%)3xwTSc|J-299@xkpoWKOmK$-uL%9!sdDO`V1AJIi zY#cxojVYPiIpasg$L0C(4im^PcQCsx3JZH)&}3aYFY2rIEPHXeN<7KO#(z5BYn_FE z909qXyphsXB>V6I1KpoXHOP{B^4j7IR7-u(IGkhtt9w7QqF~hMMyjBd$R~tRwf{Yq zqWC>#UfTaNRQtr}x{3rD0<efZ`~6}NKMAzd9Hzo0dV4$|JfjOgxy~@NrQUX4u9tbx z+=DxhbS>fWR|0@7+LB<v)qn`?q$+(^YMwN`6#Bbhyzlk<XFCd=b(Wa+TUYtxv^92v zNhmQ5H=;IpQ0!P8+hZ_@V8%sgrtSUIBnc43*C$4$kx-<=s=@;C8_^NW@G|?$W=|90 z2!LXX`7=80z%V}!(E;N_Zc>jiUMBA$MZM)%#3&nEJD|6LUX_Lll*r@@03DGHPN$6* z3Rd|2Y%@K#y9QZ~tPTxAoD;$(4NGW+^An5?`E>J;J{Dx64{?Y<H`OrEk*pU<h*(bQ zXhP%t6LAk@Fx&>I<P8MUd<mN}nB8VWksV46KsXWzRT(Q<^*wb7&b5&yI>aQn9E7dq zHFYE=RZ)*^?=_19TS@qxDL90s$TaBli}&x=Vu5nh_zCKWw)hk6%3JF=)FA4^p!aRW zZRY9k{;uItcRX~MPX2=Wdd*6N9>HszU413LfcxZYWN`@|Hukbac46#^I-N#`PR)S@ zV~O5o5)QqdkmD759H??L@`~(ppOALf1{hXaGcriI+5Xp<G+TI`5Em^<M2;b}It@(A ziaJ_S55*rX1Bl?y=%@9`<ux@ewYvCr{BgY1UUUq~qK@#i7z+8_rMoG#yH$NXf_?QN zf7uVVZI@R!F`hmAOy+Db7bsdBt^{Fg1;vr}{}?Rv?uBUbL0ObN{r@S8VvR=Fh&Gr~ z697c~KnjU({9**?%QY1K2j&;S{CYcn3?LWc?8e~&JMfgy?DJ~rmXy`ZoNY22IGfXk zk0#6z7EP*Y4x(!8^gh<YEc~vABcu%G0_n3MJB*{qyYl5y@Pmz`SkWB!$wpKCiRhs) zDdO;QyCz*~A=m{Z&iG6wx*P5ab@3=T3nrx@EhBoTJa%O`DJ0Npfc-iM*Q<Y4fQ<S~ zN<jYLAWt5n3oKfiw>JYf2&`#N{p6ghdfM!^GZQ4a*+|r`C%|1`J8u;RKD+&``mYJU z`@d&}ufGNv)v6MUsP&#Fi(6{C<`xflg~gR_st=J{cc^C>IZC+}G*9-L;>e`pUUbqK zR9cj)aMK28x$B7J>NFU{BRP2d8T?zbAkZ3=1dnq4AEzJXe#**OoT8j)gCtv95E;Fj zmVA%cYV@0%AK^7?Rt%ld2f}lT%akf0Dj8gJvw)crY_PzZX;$zf=Qy{k<31ZVhOY>B zvym*xB{{JYW!xexIhb0VX5xgMUl@zj9n%dQ&QwQor&X<@YywWLRW|{0lmbG$BAjD0 zi)tnfC%pM6zxyZ2`^$x0{Q2+qVZZ$n89ySf%V~IPnwsU90kPU?jQ-CyuU3diaHngl z@^sxnO%xG`2nC_)0qhc!bIiqAMeTNC=f<@B_ut9Pyq-vv=nj$8I4hfKy-=)}?8bx^ z2xrM_<`v*-b{-)~+!Jb*aGGU~v5EgE$Iac8w?ag<LpnX%o+Mm{<}>KKBs~$OI8zP! zU*wg|oB(*cY6~#`3q)u7nR8EgYxr4s0bXBh9<aCJ5air54u~}Z??;dev5+nA$I2wA ztwu0&+w9Ot(i{u?d$hr?G?B>yDClUK^*vLLkQyXn0)rX_o%Z+oV?qZjsuqJ00%4?{ zi%fJyc1I=iL())k;$s^J^r9H7;hi|5jXQ**Pt^vZG$vVm<38+AzWU$-6?8c6gB2rl zSQeF4?m5dlh$p^tl|6qmFkVw+saWINH&~k5-TsoaD^ZsQcv2jM@!yf$ERM`(8qz4` zK*(WlL>yC-y`f=~WrH(u&FGQzii>zEzKqCtq)BHKR<l`)$kv1?4ABUVsj?@mPB<hk z0s-j?9TSIk47D`~_8|>5I=&U<wRv9+Y}HiR8(&13f-mz{hq-@EeZ`IhW2FYf-~S*B zO}u=Z|LH*04;EahTO>F~aq%CrsGgHuA*eEj!`sxV;t(M?!8Z5N1-bMx7LUtA{)OSc zzVKY;0_*J-TD9@K)8?VZ#4KYSMfZ%Vek6ehKF(_@GpqPjv~o(sB~rQ2D^d^FR7l*~ z=?IX0T%+FbnitCn#*hrYFbXi6fHc&!p|dZ!->Tr0xPrm8Fk`@(W#tx3xJ}jl>A9cH z@xb03B!pu}T|vo!R8(Dm54^0&j~-!x`xQ{_k?%XN&F>)4M;}4*LzU=s`TAb+%@F5K z`xr`rH0kmZ7%fy_L*<g?rr#JqDr$+~(scA(ML1_y?T7A^{uJ8*0%xz41in{uG^?;+ zrZci9XLOrHmXR^!0BYArdZX*%1~X&b>6zxs{PvGWK?|h*xs_?uslG5HRZY8*RFfmj zy2bdeHjSr3t8R@^EqMH)1dKX!1U8P4DF)K)mi)CpQ6Hx*Kcoy7Ulwmih=Pk7-JO3u z4?BFId`U@tpR8~7a8Er9nD;#fI(#~>ND0~$C@MRt^Z5gmsmKf-)neJ9&y4pl*20y^ z`^6?oNWE2|;z>=`Fc914Q(!B%uWG+!FL}>X@Am#2+!GdJ|D4uvqoWm=X^)rB*OCFR z6;3H6_dtYzpp#V-@J0@X=NHIX7yfa+5`^R3{E0;**kYIp!yW21{n|#&@O6r*F{Q(5 zeaA!m0aGiAH;i4wqDGO*KZ>VldF&`c6~_vEh?|)*ayY6WPYl%Uqh=GDBV#2~K{GhR z<Y?d#RdUzwyIWt(xfu5MP7q7bAlzo_Ty~4ZY9}y;U1NvoCy%LK#0a)(IOtNtx4vlp zhx^PDk>l;dSiU;zLF@G>7mGAv+#LdY6=iX8l$hi*Aas1twNq7{351JN8Hp)+Ya-1M zA}0nSc$zn?^s!|jirCx(9RQ6EABicDTp`eH<oi<)+r#O)gJo=q+XL}!y7ijx&%>Wp zc>O6pmX;cl4nk3oBK@({>LRoKMS9Oo$lS<?+jT~nx(Yk;514kAf-Q$-k6G7yBb@@b z*>ah6`ZHxKgzNwSPh33qWLz>B7v$2auj$Oy0fi_aK7#OPVQvXQ73f&qznLNyv1KrL z@3#uUSxk8soHnCw7@_X<glbsOS|j(z`NfH~X#05zYQ+kjT6Q>>X3Dl5+-}iI3%-=8 zFL%l9ch1e}At8pQk*R9Qh^uhYB-HF=pwp-w#i|-YJZ(SC^>h8e$?EcW0j1;Z$TNI? z34<@X9r()9@+r+p5pWn7fz5$abCW!?hvN;60A%UaVcIY_@Ou-;X+~hAm^1=7!Zzfg z$y$#8;;kLf2~t82FY&&LO4o%MQxy^bF`mRN9UUGeZGxUFVcE7DO_sB!!bPs0@eBp6 z!SUDawi85-`P_{)p=`)v-<|w?FRNzszg%zN#t1f9V7g+vYCMZ?t$VAD6Yiu(%gLCd zz~e7b+;)*Eu_m`+e8<2<koL|jkPn73)rsPaMu*F05)0Hi7%0tPr&p$63&9<hZ!f)( z`}O1bj<&|Pafc0ewfqBwtG8B5pFuG=-_sNaDk)IS1Mel?&@Yun3P`2rSm1b6OSBfW zNqw~OUe}aXm(Td1v#^W$nz3M}qegS3FWnKaC6%$HIB{Vieyi!ZY_M8su3Bb5C-P>2 zOr1*^9!8l2l@~4vPUBw37F=X_R=#K~OV>>)HnjB~KL|4FTAjv~xW9hjNf9%aHfv<o zX^dGFV~^%s&~%vL?#+E|v~c9(rtc0Ul;0g+P=4OVe)sD&<fO5<urY{Hn8&sC2Itc1 zFX;0B-1fz3#k-O<3jifvyh6}-g}zIYx(Z~bHypKzB+h~~RE9)z5oDA8K=UYe2){*N zaC?sr`}<oSeLnB)*jkb()c#H1)xU+G0%x>K(L;=x9qpMoC0~{;XqAFowG8^y<4mKA zBIpC^z1gwT2Phc5k$+h}?1h<=I^PFVve@)TRPv@`QvUTbl1ShBJKsHmsxRmdg#rm~ zNV79eK8$&UwtK|MM5GX&TDhB`nK+O4ja~=`&$v&bo8Fa^8&Ew`nGOD65(GsEKJ+(F z%$BnOip>?5f7Q6K@bOa5O&ZnvYBO4*MYzZM1^a%Yt_<V5JRLeCLEjSqx#^_}j2?h= zO5$#-2s6V}h--7K*?QKtw&95(kJoW?6V#j;e3)r@uVrQWNX>{ZlVcM8-6Get9p$Ht zto&o-Ehoc#4i;cXmouSUwER1=c^pP06n;}UHK>{GPs=yyI#IkJ@G1-z6VpT)Jp#I| zO}9y0`?RM++C#vEww-Ha2soUtUJ;o@^juQ`lV*c#+_r?{ZZi9#$Bq)4cWEqbNh^qf zl#Z2bGPCJCiPU;8M^VPBQTk#96O`(mUrD#qD0bxFDI$O)%iu`ASEj`*H3&;c$5s(3 zYF|Vb^4%FaAb(zOLs}bqr6gU@jcYx;2mNbZcZ)UgwB8^-<MI!vC~p!_LB&igoVB(q z6R~x#P8;2~Ia66aXKS<WQZUu=5AbYAE3!@6g0+5%HPOVSpB|PlDHKK{Ih2ZiH;g_f z-+Rm`HYH3_V5DN>TW{pgi@QW@Y&Yw=2$eFkKyvFoej|>^i25Y}W1bH|GgD!IqtUly z&+44KTf6uyv}#?kp>FIpQV|}t;$Q5OuLb0ZUOA6X|8#blVAq#2IcDNH<KL~J|FXn7 zWNfU(Pbi!(4&i5|&Q654XBgU}m3IY=b{5%G^?V6!kX7gX3i!iwN8Apz#p2Vp^%T^x zba}Zc_wr2CS;Fs449DAHHjMb)Xcyg|EpRk0i9nT(ZHGe=TS9AO-3f0bhxX6!2{<{q zpH%AB;#LX$Y8X=~^3ZZ~&(Mh1A1CJDFl%3A8h_Uwa}i()@g`x)rSfeTM%h%?jjI>6 zaiC#xn!uofqXuuyH=LY_-u615s+}?TX@R<7V-+FTaBth{Xs5$XMH>?#b$x?scPNw& z<rt1RBgd1C&{%3><k+S|R#JYebf~XsHIM*KWRqm-L#YnGq*QsqK&_A2Zp8Y-#-eHo zy?2Hk>hXK7fV__NEwI0t#-$O%>$p`~+&Y<I)1ZQ>-Ti_K@vlrHiyNOH(5(q$I)1L_ z3ld-^jte)Ir82p#_9P&6ESf@r?G%s~|2DNl&OMPo+5@IbCZdzr&`B{-n_++@ag)i+ z*(4}q(|4{WHf`-WV7kde(%1;QfSmm>)BWpDAiMrfHsUG)0<>r$HYV7|&P_9k0X))* zBUz1@6*lsqgFh!h=(LvKcAN+87XLM<LrLh54^_*>^VF1W+MtKYxDHe|uR8>03${5! zVN;795QV+IUgZC3LR|xPf-gi?q}S_65SLm^RLi1hp#g8UGt?o(VQ7p%r=veWHDG?A z8ac`tPlTB|EgOdue;H6&7@t{)^|AXrG(P^AZn6sX^c~NcLsA}$7ALgpf%Wmh2Jhi} z*S1`b$L%~Qyx4QybpR5emr<>U*T0UNw(Gm!pTTC&BYy3<QrNscuIy&Rp{jl6uzwlo z>^dV{GX=w785#Y}@Qcg4?iY_k7Di=sG=ZvSOui`<A}5`gn7M+HJQ<Kd04XX*nVBo& z8;8NiOS*&n6Bp5JGdQgiiN|$Y?E{ETy-o^v@3}I`piZXm*+J-gDdaLe%tFGs*xBGJ zxw*(~Ldi@b@mYELk{wn!vfk;`Iyv{9vUHJr&%A#>L<s9UM^N6EzUyIXI;M<_N@@7X z05IvQnixPr5<b2h5C)r&L%qiY2pkI}>X;TT4Q4775&r3MDuX)NV|I2@GPG7V1+BsB zY`5r%$4HZ+W4EG>5uu%t1cqb<w?s&0G|Y)kqQ2>}tp%KWN`hw;{v9{S@$?s&9>t|? zAP*_V3^}#ZT$m+8k9-`%=<-b|u!nBpyhsK<3|wPJ=3F+yq@;jjYPm24DnNNr1;5m` z3Q3I|8#2CHH$7T-$>HI0&VQd;t3DdzbKj@2rpzz~^gU$L7+e(kH;sbzz6n=~!K5LZ z6TZM|b^1KYr8QRPwm!coNBO#Lw7GrwvVB}Z<8R7QEZU~^;$ya`n=vd}RxpB(=HyRG z1P)VRg5jsg|BuapjTps5xDNr=)akaVlJDBO<Qc)PF5Np&`wV_H##AkgISs8nR~R5? zM#R|yC62Ms!znJ8Q6dYG<G&h%Gyd065X&Twq}mp4PCkoQsY0Ne5FfM_^Tsh3M=y^# zrTr*SutGRewev`Md9>z)R`T=hL{^xq)1QZ_6>&EZMVtt(Y4AHLSNSy^H$q!IN3=Fk zk*Rz{yGVKzS67U7ky>=TGT?+^(2yZ0fqO>|;uxG7O)I35n%dP-nPTPZ3aiB5xkbf$ z5JGkRNSiTOa|MA2HYSrGH*fLa;%PXYr@W$K?HY|ZN!4EyaZdtJuRg}sQ6L2}>*r_H zjG-_(<`Ar_H4yVx)cBi@lH#>lDqX6RENLWddhj~_U#F99wH>hvkUz1f*5si~5?Yd( zY}>?Y5u-l8%C>5ofoDv|$Wg|I{t6qQ)3w{jaqMO|d-HrsOWtw}>X)KXEPV>%LF1hL z)F1*oOO5(gv4d?cA2ot0@cLJ`F>~ZU4J(E{ju<#{fZ`@y^S>nD#l&4DL&YKlFs~+U z68e?G1nUP*7@0dE{>@Rxy!Cv8>UfevT0Sr$KE^;+>J(c7dRsQPPc+Yztqx9dWg#bc zNf<sftbf+@ZQ#cw>f7I#<K^EwAcOZ*RI726y?lWA0Z;Q$HOiTwh&77wpJJI%AQN&7 zNoThQ-*nI0Ha0+L!37G3>TmI0JL+^BsFwEqZ3^+7VojFA>_)*|U1V72=71vJG#=hx zD+&ae|9cJl2bmuNEK{`0m|yW(?k#LXEHO9|3`1J?8G8wt?!t7tXiWCN1c+i_=i05^ zHhDeAu0c46Bk0TLL;Yh8ACxYU9+&Aw8Q(>w(&W@mL~;;fEkk3Lm3jtZqH=A!X)+o4 zH)Qb=v{{zbCd94P-9JS$dYtH|=GXW_2hMN2N4xls<_eN=v-PKJKs!Si(OHLYvyS8; z`64rTmQbp8vrxq-)F5<)j9%ieAJ(e|BN5KuU!L-tlM3FEZ!PdD+Ar<#FE}p0Qw*24 zx_;~sn7ehf3)xc<Vl78$&1L`^)_!;{Hl4lmssAF;fTa1EJHo9qhaK_86<=ok55j{- zl+y)ouxYcaEw|JI8|OwN6y@)8@c$k?o~-45NfN5g*z^Gi<m{@Dg*b6^$VvEe0`&+; zb$^|KAb~X;|I17JY+VtQ*<%f&&J{Uc&*<Pb!ILH2HPV4O--|WC^v7^WazD@v7J|!E zkKB1UBm0T}xJl~3WJ`@{Wyr(y31@PK$mzano@=%00}1~wT?H?xzrJeQ2^pU@ikA=W z@g!Q(1%D2liN0B3o#b1&L!WF_v4}ENOh^&PH~@;@l)090u$<8o5Qw|9bsR!ZFYZLf zV0|wMyR*5m;F2GJ;Q$qO_FaTq+A+yu>6H-F(P)WYBKs#Zq*S?cA|2%aKWx1NcVrFJ ztsC2R(jD7&(y?vZwr$(CZL4G3wma6X_dDlu+#gUiM%7q*FU|QZzkVE0XKt*M8^Sh} zFedNPvGO1}Pqfo+Bi2FA7a1?(gNqSdy**<DD<j)d3cZ$LHZ&tg52u89qyIvv`u`n5 zdyoG=ig~qfq{YPkTKcc;784d@yvo3q`?VCDGDySKb^7cV8b0umRvq?e-M|<6wT=)% z8s^DSgnr*Y8#c%Ol2tO|q9EK9Yg5OrK4R^F#1HmC+cQSeBwZ`#sswLPkseJ{z<j!v z;CzCRadS5bPQ@j391HtrV>y(^1TH8cS|fnZCP)|tLYqXYVN?7Fg7^hM*%yp;&=r^8 zXN*;Z7AFIAUnXJ(FmnGE_Omgc>HxZj@}WZrGH>5Aa_{<S>&1hb^t*vzT1jT5-i*o= ziIJKw7HDw<UG@ElqJ#Rs&v8K~et!MUh)@5+pt6lV;R5T`Lgb4cjEHyeO!<~JZ$yy3 zYp7dl!xrHrO*eNSNPSTJHGvNEo3ffH4<c%vvb+3m_*2_Bm`*zEh$en;quu%TC#CnL z+vz{2JZ*abbJ3m>4<L2?%yZ!TtV?&amxjo2N3qaq_)4g_q{r&`2f%9&OU?v&u^7J9 zMl}p&_chTz*-yZY=0dyc(FUPTHAE~1=Izr_4+oQljH3!chElKhY_@1SD)w9u;nk;2 zRauf4{Z&>j4dA0QNg~5mR!G$YQpQxSHzd)HSZ9-%+`~{KTwgh)Jfvoq<+It-_HKMx zx{_HNcO9DP8@)~wv5RrG13Ffw(6u76@vm^NW4D@~8Bki)dHCmT{SZW%6X%BW4X~uk z_O@I%7Ch#qcVl#^8XntsQwK^a(Jr6RrLki|k7H!C(=q*juoZ^8Q056y8;yDUivOdm z|3&<!;;vQukX7McHp#EQXYh}M+w})uY~7WR;}KvnL<D#|ev$J&%q&72wr>K*OwRds z9Mj}UZ2B<O)C?||_GGr8gP0Now@bg(NZ!lvmL39J1{eE1S)XD0F5dH3JFHz5LI^Am z+e&9^Z|HHpl*BBfDeo7Oe4mUUFA3mdlvD@p0t${U|L)zma!EtTqhO*LkV^Di!CYa~ z&~p<i-lxU=GbBOHoqn^4<b%Y}0H1+%RT?c~bUH;1>fy6}46cBsdw2nssCC;c&~;3C znJvi7<P<Wl;OV79gcBQ;8pkdW$*K}WU`gWf(QRu`qp|=y4cXV0*+Z`(L*sGRmAnKE zF)2}w&w<jn1m0O<GKZtDM5&p?$JirE-YXjiGcs=S`;tBcWr29SV3azF5lBy2!|z8g z|2;h3pRxA&mLm3l*8h>%{oK9zT0utnG_$i<5v{NbpqtW}Y&x{pH6(!h5k=zKp@6Sb zo+kTzCX<Wku)|!MI79==J~}|ZmjX`kfCg|C^MoNr@<yv{o4Y|Rz<?A*lBk0b$^iYn zMVjfbr4);cgDB0ohN7qfOrE=cbS5P!towwro(Ka)3mN%y|Ig<EiW*Dds%wSUqDPFm zJoy$stXwHT1~R6I_S;vrv7F%9h_!x`7hIWI)yBxU%X9PQKC0~*pXDVBlMU~_PmbWd zN^I9{1&Kzgy~1|9_;}U;-Ih{mJRauPv(g%1F4U2F`uo>3ph9Y}mtj>O4_BVj4V2;b z-%y_0{OE6Ky}EUvqfX$Z^N53`(R&jn5daBp$pY}~y^fuP$?68!bIZ0Fq099BYR4$> zY8(e+YEJnX&Ga`d!J60Bfq;Rkeqm3Ji;2P{QjMF!UyTX@>5%d%_9Mu0gg(8Wps!h; zWVK0u&q|FJW{tExzp`wCp#K{(pjc|4asIxVG7+D^p0r*&m?Qh@gMl^*gQFA7DWi`- z*BKYzhg2G&rjcZ(l4P<0Vp0O0Lm8m(@h2A9GfdQVah_a@p8%0bK~`dYcOUoTyY0<) z8!$Elptt+-iRlJ49+PD8IZh`+kAuu?_kt0H$x>asZQ5e7VJVTRjC3ZBQnJ6a92Y>F zVtafKxbL{Sv_J_x)Fp&?$4w=>k48Nmtf_(m$z{Y-epAF(%OYICnf|e+KpXi`-fKz> zz-$t()EDARWf*t6fio8?GH2k9v<IJLn{kJ8a+tgy;Esfdp}Qg7phZBbmj}{1N9q@b z%q_*V@dSoc1IY05An0FjY?O9`d_uRG+*x+?6Wii<^lORvkBl|Wr0A%QxiaA5a9n7p z`c9ORp-7C5c8dLZOiDM7q-1I2B2Yvqy@je-$xA{!*V#N$XigPJ3GVPKTF@Y~xdDzB zgPu%2N0G~Wx7m7;)~Y-&WJEsiwT8x4$GZAfWm(gwN==L=c(MMv?Qg@w!09$ty32oi zaMfcU04Lwt)bneVR(;a7jm+fbiLG|;c?f=&uHiAeDA4P@X2)}npoK#eu$QnDDU*c& zY4Wi-pf+-iGY<GZZ!eF|<EEP<aRxv!CW?U>ib2fvva`SiaDbdt`CbGb8+V`Wd^RhB z5V#!n{P2GI8+t?y_UDuqR}5fa>+~rC0>ElJ`m~oLtMN)dwcoXu>6E@_nL>KOsowzS z68r#2Lq7MncJ}Pm*G#&b;kN}Sb8-=!{2rEs;bMfOr<}e9ARw78Fxgl_Ri<b`VzJ)~ ze;$-l?_OmsrDGq8;TvFJ5OJo(LH{IYCMaT3Qk^qpp$3%13&6o{ph`!Gt{X{h0ovRK z7rz{_4~_OtVK27d?O#tpP4&SjS>Ofv_rmb=g|judno;8VSX+rX3-(cW(2$h?Tk0FV zFoySYXY0_cohx{lT`YrB7GBlGOsY&usrxwU$B-E>lL}a-jHJE>If8S`-^JwMDB~I< z4JP}DP84p|#z<2Va+(Sd6c<79{@1II6V#bdf|#JtUEhK9!`qAi_DS{SpbDzmjOGLV z;Y5da4?avH#@dxx0UQdqzJGokaiEB0r*OGHA-H4&OHBhgotMNEdK5)p^UDfD5no6j z3@Km3I+CKljwck>K}|YsldUu?UP2win(^M7`x|ju=p-~w)pMbwNgKY^6%C=6!K=J2 z%Pp`$$TC2;X<@>Z-g$mwlRnMBj_DgE?ab1Q)<V4^M1@$5*w`?%**b%1sF1RV>@`kU zxBhC+X)zu1F;Y8_*SP|CI3qw2`QPr1ym&Xyzb&4cR_fLx^Vzz^9lL#JQoG4ogMg|l zWk7QBSN~^8&!$Xqx~y*>Qri1z{GSMo=l{}G8w99G3r#NY6Y06%-&(a^;c(j>=D0+4 z@iSbLtFieVf6fE6DX$J`?8KSZdTn6jhDMlm?#RUMo70)=i*zP*!0S#);UvIL#YN=R zB6blvX7@N~Ij8^W2{SIqdI3(H&^@Fik|gM#l@7iSOi|K4>*xxDGXuTrb5K$D0BBGd zk3b^qWDC(DNT5niuK4O~-GQnc?N3uG$3GbrrV(T1*i`k&^ajcY`GG>l9r^3=z>-zK zK5Ry?BCmZ$HpN;5;{)^8ZjtrRMry6!uQ%rac~`g5Ada`nw9k<|cC#(0ZQHl{^2A*s zoxvn!HTcL3RyNA8goKz5+jqRK%*P=ORc5k!$N(3mClHMfwch(5DWwUP*MJHSA%Q(G zCdyFW(mAj7Oi_h`&_6iBj1gRPj?V+#h~_%VabnS5ohwFhP*-U8Sz)G!!u4UL)p_-X z+@;%B!VJ{m6H!1Wf`*5r{;R$2`J=FxV`kRvuj?g!zV^?jwRx|#uv*V!3mTY<@2qz_ zLZ{_;cH+XZ%vcQnY)+`Y6JRnNEh^2`@v48uSvhzZ;T+2up^9w^DN?AGN9V_|6JX*S z8B_vu75-?XPO9*ui*T5vtXU@q5H-%B*57MZ2@-Q16gF^_;N(3S7@=DCg+}<)noTML z?U>fCm{x&PlsVEBk>@X?Alka?aXLLA(3JLAp9LyGNWVXi#N=IZfe4?De~sxq3}20D zsmbbeK5<&VJt?A_9jTaT9S=)pIFQ-0J{<>7_oPs$G*K7)loeJ06h*oJx1tC`eT0NX znvd-v(qhU*otVW>sc)cZo*)k3U%r&}1{(u0980Xq)d-jAWXSCIz2h1+hG}+$ozAt) z5qNh+Pw;R3MGt2Mtba*9g`>0D{qWHy=XE<`gm|^{m*9!to-X}oNPcYx>(`eP6-2zl zuqY%@zQnd@khmonvLP;}q`+vac`!^Qn4ENcJX@%EOAWqj1CQ}mb;^KRQ;~7xtb*h% zDK?Fmwj(%8BI$6mcG;=u_>2pbb+7v&$*{6f8edq9oCdJElm_ZX4ZkL>pmmD&I`UQ_ zoz8AIiWh3n=|KbJi}eO#-g49Ld+o}te$mHVC9s{O<3_?p>tif*XN`TdODP6b7?)qe zg-4Et;J3u0d=Lj0r&E`RXDj~oM^@}IoNj-Gz({x|+lc{^S)o!Wc^C>MZ-h|VRa-k9 zpNFBQ-P6<=wm}q#wHbyN<Nd%*5-UY7rHmG?Yz{%6bVPEdU*xs%0V3Wa(iDrup;hqG zFviIiBH67}wOBJiq^VcsHXR|hWG0On3%{`?pwUWz361(MuOn!s|GYuqzUwyQqhZ4! zXes&~RJJM+WO(fsyR4xD!%yST12>PonzfW@`WI52PRt#uen4&sp)ve-b;o+t(_6wv z`X#}R<E;VIm-8;@l;^7ocXXT2^ZvkvyRC1pm1sEcPzFdcwoYYTm9s(7^-smbIft|q zRdSK3+TRDnKcyXB;QMOcoRTUYVnL1k3;==4%IB0`)8TlliW*#o*9I+Wub0D+*f-Oa zmCuE*lhLI}(5d11)$sA`ul!^7$x+z+N<!=^LxlrF*QQ9CGb9HhVzzS}wlVw%z!l8L zUYE(G7ab;yu3jTs+Tg}<I4vxqIcT$YkF68u?W@ruTRjivLJAV$y?1YXCr+edF=?^A z<rJ_=T}`1Ta5<5N<5c_>5&%tOL`YNdfHbNPg4m*4z2p_eXk#{jY}~X#j$(K*7@E*G zV4>s&AZ1+B%!5>ZssiZlVgPjV*`^Z((rQnj@}shUFo#a2Q=oF19ft`Pm;W)5>OOh) z0hnpml!?9%({PhT`C8FmL76WcK^5|1c)15moC*>pf0J`WhJ+M&tZ-@-U4vDW{dI=1 z8V`*S{VwC}&KE!DsmVW&ax-{CX-CKS04f+*FOH#7f~vXw3?WA7VdDM)I#PE{?77a) zMJ3pukJ@v>u1a+78UrIg%!&8<73|SYw1-4vp7HQJz!fab0<`z0Bcw$1PL^kbQroJb zpb8q;%*xgX6~XZ2{3;<yYY$+i@fP3`8g~q4iF<z~P30~av=TwzXkdC~QVC$<!o*%o zmC0+0gSJmkt2{=4$7t|pi??WUG3q9>9-;~vX{(UDjVieUJ%dYWnN~z)q;S|Y0wlag z;U~>{1K<M!%}jOiG#_>N%4`sv1IY4U2TEj4w^-j+6kxMXt<_uiJ$P~gkois6-~Mb| z?|{<SGV0%+Q>99SMp{HF{R9-Q7%D|IX&6+Zmjk0S;Q%lZWcwzdbGom=;nbQKt*mRE zk9DxCH~k+8i*{M=*0Ty>d0b~UeSQx^egvVL<Yd*EZ7Z}&e<=Hf#<FsAZC0(><a$~s zQBWzm21TKZ`IeltK(m=+uMKCgyZ2z(r9GAGsssKIUY@>Kd#=1x{kkwWi;n;1lo#FZ zjYS5$c;07tLheC0>5A_bt}n|KP|7_=DzopC3zH%2jV0#j3?ra7LijF!H@;FQE$tB% zOS36ykj3bT7->kQv#MpjIyJTYs&{bWTZ4OCT$Sayp6@`E)d=JAH)C9ajE+_nme=|> zhJ$XDJVk{C)C{i8xN;ByeY*}2?UUEpa(B<Bueyw#K0l@R{2Ohzpom^Aa4mVjHDN%% z7_NwjmxnA&q@vCiLG?b4<P(7gJd+Mz(8Jl_PfeD5cl}!5pj}3`t@r6xo3a46L7?;% zLc=lmOA5QUd2C=CFy?2xW^8|Oe$XHx$VLmtUyJ<}05Zn!w6#J#09uVX&}Cm9Wl{34 zBqk}g9Mf;G4IYW=bZw3VXt5C)VHM+Syv)$LoeO&#8Hb_ygSk<W{n%U(VkH1x_4rDG zXD=D!UP3aT0{#^XRM$?$x*$@rSxb8IFGd@Hr5%3rF6K7Vf12kmFSI<U4O1y-`$1bS z0`3xr-lF|f70g(qW+N=@-@=h&ztCu-hNyMdWHm<Ma1uWx>-!ng=XKD9$*#*~{#<@; z)pqrHS=-mCM6TUi2)!A77tPyI(IK8E6D8xQX+$YQq$1V~V4TvtJ6%=(q1@`V`=lDT z?qd00%!THv%VQD&-ZsL^Ze2#+E{BgOfV9f(D^V6;sa5DZ0brOvrP~t5_9Gr<#@k}G zU$3HFqE@cA9zJ)Ir3_GYfhc(QK@J=xj)RBY|InX-He}!SN?0-a1WObHtn@<#`e)?Z zQWAte`I|?27U76r9xMXwwYkFSuG_&yw*2QM)G$Vh#NlVlwt6Z-e1Fw?3{wySi<MoQ zV#K>>fq%1&+%yp~R3bdbkLa;-2d9)&E9O=7Uxsk2v!u2JhoIEbHM%%OK#>heO|?DQ zysfFA`Z}+|O`RLSG?-Lq8C#+9Y1&jzP@>d-vp~CV8A1iIcz-hb4Z*4#drh`4`*}JJ zu-j!k<fTOq{kE}-t}=qN(it%lW#NvL2;owA22JDSr=mnpc_4@q^-d|2mSjj)ldNt* zzEyc$Vb73N@7Z!bA~ix*|J?_mZg`vSm`aBhjqFD$JS7fZNQ6kB8u_0h4)N*=(C>7^ zW|9A^`uVx)fovxjapB7aYs=f5Mc?W=)IGo<m)$z#_}+9tN#lqe7*6466+{t-LQ?TR z-}bkuWbBb7($G=saRZ$8a|tE^LlMzG*nRxm&3Ku5+gV@#x^cT*9`P?Z<*Rzk;XUOw znVp)snVH$~>9<Ss{oLYo=p6%#t{UoD=fDgM>$UaH1!*fUk!M9T85)09-O$-gPL)sD zm_cYJoKt>uB{Ek)2n!H52ri<zxL|-4Xj|VYSE<U<P`IRwK`$~D+OjWdR@Za@Mmk8r zA9H7!H@^D|y8V<TmmUgD3#r=!H>-VD5qp<XyB+7%;;g7or!AFytVXTMWQD#=ICn;9 zPqxwc*wEp&8}}hZzTN!K->KB^wSHb5YyUPD>oLzvhWkBJgneesUrVCB;D`)uID>S4 zG=R&1Fss;=RH(*BXx#Uo%YL5Ydqn9kTQ9SNbH;oTv>w>L!jiWu9aa2-z|XO-iV>&{ zyV--CtDr8z-R{GLbL!SS*U20`ew&Fbgw7{Y$gR7~+uhF&BXT^q?Zo-V+m4@t`cDLA zJm<qa&_Mf6pXlTz8?U#Go=VwF!~8E0Y#*rHkG<(+jZ3#&PLHoXy@uBsa#^pHf_+q> zGiDFe{!%?jH%pzWr&Hf=l)}<WQ+8eF4;a|~tFr94zq2z$zpJ-<Gi`w@bvO%_vgJIO z6tU+*$WO}pGs7I|l=5T<_>@R(S@9Ms7tC28Y&+*?1uzLT34-hJlI&tEhLGb9C8V=Q zt%LnKjDPL$4nX2CR39q`+uM=UnI$Wgj5Z^*9QD5>f)c`m8JSeaMRtV`iPn5C&|~WV zeJLzJHP|>^=W`ir-pBbhlXc7hI{Uus@eu9T>CUG+liB?4%Y!M(me~m<S7EAnE~(lu zi3$r6WW1$6=KoeYqUGm(=-3Sv%Dvwpb1yLV2BB&pg70Z!+CmbtQE5PLWD&Hkds>xJ zXps(MqCFzTh3qxfyyM6^UI7V(-y$Q^ChN6pkaUl9w3nr(-N#V98rJe;zesT1)(UOq z;v$uHyjlYTzCmqR_e^V!&#_#)`F_W$w6foEx{H9{v>U+Q`~1P)_eD;O&}shsbid6A zXXP=wA~y$Q@mRMKph+g+Z46R4ywoFGg_SXJPQFnSbe02M?0L&?oKNY+D<cbDR(4@? z6tN|mKTUVmxYV*o`1M72(`mL^{+8u%AMVo0=diTahvPgZ78d<|VWBgo)AyQLacR2U z;`7xS_6wu*v$&xO6K9&$L*-$-!g}*ndtF80XjW4GHG*C4o(PLt#Sr<I_QqlCx|Uq_ zdtpJ+a|HVhM|F%F=wQ)>b@O;Ft4pOzm-~0587cY-qp|Bnv>6MKX-;eO7+SHYf^6E# z&j&Q%4$w}slji_CYRKtc8pgC-!1U6{;}jmi^k_>P5F&D%RlvhMP%@FHZv8Jf)EGAh zx(#)4n_%Z|fLq&+&wvU8cH^hAWN2(BrX-I67dQ@lQs@EZl&)q729m!8#9;_yR`gzV zquPbLE~D@GmC>r#VdS7Kj|V@=dS4hz=L22~o_ta?VaVEUN}c*`Btdci>J>$`iDVoy zAE7~!_c4_w3?qRc3i1m})B9*@-CU!ctDneeCR8UK&R<G=Hs};AxJ>p@wDSl_Fbvpy zvi)B_JK9}2xRr+Yxf<+_+eP$Os6j8V-Zh+&W*55^r-2Yjm~IB^diY2wwp4%_JR7S1 z!jh!0q*S@2jgmkHOEK-X^kO!iXAJ3_2@x8`uJQ<+gjUtCxvq!{{IEB1H5RMyc);mF ztENJE^zK9CF(yP?!#<DO9hmlHK9GG27TNmDR<IVvCc!%|9f^XJt}jV{#ZvA{*6r_k zQ#BFA<BF0{szEgQF?&0xx~l5V(*bCyn;!S8N@4mj;`s=>uX`Ip>}at<RNwbY&eJ$e zPM5dV>n?&3PRxtBm@?fcSsUbRt(Hq~lT_QbT`gF}4JyHBs4+m;VbqHaZWr=SgFP?( zS|o9El;@H&(&GuP)q6KUN7jZrV1}7wzaBo`pR2;qXNgLI*L^7haI4qKL1%Je&o5sG zK{N@K^RYBneul2AS4@)Lw~ka$^E$VpYJ^(Kqy+2>?e-n9%J{1nLrmZq@<&2Gk=yFN zm);&3Qv1F8+*hL1z8dWQkYmq+Ts3$kCf4McG?%sHc3e%Z$6QL3`Ge;RvF2(^iexU@ zT-?FSUoW6;p!g;MqAfSMKBLq9FHHtd^n&4*rKgJY8|{dsqoTje;BkdKCx!xy-e@y` zxk7cK5j5KK`By^S+H$Xkx`FHdU>dQ&lmTg^=W7B!Z^N>hR;Wf8jpSPplMu6?y@_FS zpWm+ut*KqlMSArIpX(m*@yA@+nbiU+O9a7$Attq&>QZs33@Q$Tl2m1If39K4AuNuF z?tR1f7t{Gndym(M$CK}WrRb}4?U?fh90N4JjPavF(OB4RZjM!xu57R&LH??{5Z(sv zqjxuv)s20)-Ev^ZX%3>9u$Un<4J8M0l)}$w$)+QX7pR%0TB6Uv6n8pWTC^<0FC-7C zZnh=LqRRrCJcO(BiZODF9;A$0N&Q=cRfDe_7+Ppf`(r9n1B+&o>Fv5mmcRdpUk507 z6Fk8SJ+#F_`cejBH`HrveDUXcLB!0>reFCtO?6lW6a|(lQR@yw4@b{Y^G-IM)V$j= zO6*F+Z{zOzWyb2H^%m>PJKR+7lXQn$F8ASHNZp?uVB$Uw??K8onyhYX>Kj&-oApY% zZc8S3-<sKNCg}1_Ta|Z-e9C4`Cpm~PD|{~|2Cw9CHUz6g!M22}^V(d`v)p{nJ48IB zPl~k?)D=AM^DX*3p7Ws~u^zjAd_TV<b#3zR12;E=i>n-5w?aV!O`+r83H$b&HIL0Z zB%?j}LX=SGyuagMF|X9uP9r&A_E5g5`w*1O^jw_NxuR~dN7Cl5YjtquR(#@fwk!O2 zbiC9yM-b2cx@}I{m2R$j6g!1R3oXNd;rT9$2anI!9Drq(VJR>M1lkcDaFR$x$y~4$ zKVY<Hr6%FKEo)kvu?S{(I;+qwum>$`i!72++Jb5ABZ_VP+&wXRSF&zd^H`$nRDK_f zz#?>bknU8D;*_N``%nH>PD{mzia9QG9;ci{k(;`AM@zCM-)cDok#6j97$Kj{`dBDP zT5!HOL+9(l>+Q}qL=493V)~S3!mPci#M2vBgpT?{+Qq20j~q%4e+jLBakVL%{au$v z!}AcktTw$Jm^Qa+z1wrRe<7B@BSPX_C8K^Wx$}9tf!{@5+ev73gKxjAcyvuWRMzW$ z_NUdvV&-k%)+BCV6r{dgJF!9)*fChov~j|L#TCjTD?&lEUt^6nL#W?1r58PM9XZ%V zl&Y19f-M!LSc3Wi)U-f6)<%T)k-QUFwz9nXr7BpDsufI;!vj^+q!3P*#~J0M%E%ze z?#Gm}H;Bja+!YNDK+kA?J(Wp6R$jq<B~{|QwitYG?O}NCB!PN?pX{^i@O$}W<+jag zx*x_(`#xP?{B}LN-TB;Y`+nN*`?#=BitX|ppKf9^G>*b;Z68(=A==Bsf$uxcySd25 z03zmI-EK-`+5J`8xm_@k7poq}v`$8wX*_2`;?-FtM$n0C{l^^^uFZ^5apd3^YFa2H zp*qMrFQ{nmlb@~E*sSRH;XNv(4j*9*td|39TBrO!#u|u1=PV?s1(JS1jB!G>1R6Oc z$BOl|^Nd+hw&w*UfZjyx!@WMEmL00B+l%tKZ|MbFecsXWoXtI2g;i`<+>q+5D<)8- z=)8{aY`*dgM05}l`B|>&7u7#7Q-C~t{^podNm&3Bocgn`g6qPIjSV&11Y$_wnO@=# z#r{@G&>8(Z4LC9Bexw;6iV?m!I64tZZz*Py_%41Q7yg#V;+>sN%iDGpLOO5+-**Xx zs?u>NRb^u1Q4vgG(!k+;`uAUd(4&OO*2DDyvzfe?+9WgmoWfIlHiNuWep>RT1piQt zyecdUm^tDW!Pw)-G4V#_K?wUG7>!67(>!2<WhfR>ny>eHX0t=vM^2qf^15C^B`uFc z6Z7z;pr<1S=FTS_N%{UP=2}Vyntgy}8^eb8#?8%de-QkGfb*@xeQ6M#b}t=&Af54% zGt^XKj)Byk+TzNjk`e=)rGXM!kmeAT3ZE=8MT-{+Mx+-6;xp7J&4sMF3fOXw663ep zU68_fs0mRB65ijS!lW%6qRoWYj1Za&oT$rGjO9NkS?(oF5a`bjFx0X_;a?u*3l6hb z48DEDU3d4#L$+EjLxXn#5VAo79s#To3lDxY-4k*qmL{!b_`!d?47Oq4<gXnZKrar+ zWv89gZrhDNFK%@hQ0q+l_Vcy$zJjv&ou*#%beLW$Yg=G%di@T4;`TBqWp{b-H~V=n zP_yNGRibhhYj6IXc6zBMA~=Vi?*yL7AIphVi)C0RVaPn>PCJnkbh@x$d?z%0H2jQY z{xZVYae9!MWa!#6683wk4HD~p+(X{wFruP^<`l%YflOMPch$!HLoRC7G_N~v&s`;q zS!(2qFhYDt+eLue<v2!(x?&Ju%d<a%+o<lsR^6Lhifz1j-8^JmrpbphX{=~wWjHb( zGPrK^CjoFU1t!tKoRq3lQh+Sk_VQB!oz<ZCJgK+*rj!+Q1}>P-25omL?<k0TLWL=y zlczZl1&Q@KT~u6_qLh=8vvaiojDN@Xh?9}0b+pOa+v*#BOy=!Oqp9yHi<CS@D=O`U z{n{HXNxd1`omv2YC6y~(w~&aclR}M06q2Si*HDnt8yZB)zWb~AJUy%)v9^^#JW9!C z$Y|vbGf6L0E;b}PmiRCQ=$f&%GV3=Wd4f?PU>)sPySMXW)V20JoYl?_o4~gp(X5;g z`X?cJU>8ANX3X<Y)Oa}clEFxCt?0H~CtPA|d98)3{CJ)hzT~l?);;aFvXw>!8?c$- z+$9cYn|X+j(s*fl(VU>QA4@7X4x_}&kdb0jQK9;hHNzO}EgUe(4pPR&9^jTjvA2i? zLVBRbu4r{Rs%3pD@SRNwPe&ArG)%=)2Z|?>5e;TU`d1Z;9<ktx(*Vix4VbJtDU=L# z0wT=&1#DKkK9?Z3Lm#2FO{0#nu9Hkis+m_u(42m%r}BU<pdScbEu4e3Zs%cdnDj-L z@BSw5)AOb*mf)o~z^(gnFCmZD^fQhw(`EdX)k$?X>_Y)gav8b`!M})~yKH#~rPV3B zSL*q1XXm!tq8xKDfk~sx)&Kco(?Z;4nasTgPjgpa0opwrvRNap)eK*T+hM<ZUi~>U z?S_4hVJ^*f`hcl+_YdjT#?<^Yg1x9nqTAikVn2P50V8QaHwyL_bi*RyxjWn95a+O5 zx|r4B_>m!(K8D5z1w}Alln)?h<&?9Cn~qg(TL>{l?I9{E!@Uk@N|M!~&)Hvv1}h@v zcl^9U3U{~cUjd4mlk9zyDF2*N)Vvba;VC93L-bqt3mnX;p|0#JHE%?TDDTEOE>y(n znZBUt!UWWM^Xfpm_;(<AeiaWlwPeuzB2+|+F$^`zbZ_b}ZYxdSGy0`hPCXlzol#=M zN4$F)3^NcTB2t<@A(s&b-)Ddzt7x1>p!s9ED)UMtwMeB`&P=Rb@{*<_qpZe}V&^@o zta{ac%b1J7xJI>m95P2QRO?Caw)+^K4B`(GCe&xZo!^doj?ynl;1ft}QB8)-ktU-p zu+c7kNS!{sB8}?)Q9(YYpalN0W<^t)tip>4E@xR8wuciWlm)iDn&=80S{~XHkckU^ z9B>(}36frX;wPDW>0B3rX>bp848ni*X8THv9w^!4KTOn?I)N%hC9}30fmA%sz(|(c z(ox+`ioWjHYnDu5(Xfj~2zjqNN@l(<ncTDI8~!02^;fv5t3xA{uq}$UDF;V#^z*h6 zk)CZ2Naf{wWlxBXvlw#XjJW%$>$q{<p7UvPxt}+}wqrPdZt?9u+-9;IPQs6kFl`1r z^)}ts2cZhEL)En5)&R4VGOs_$1kd6=h%k#u2*AtN2rA#s!=8Y$%wnD-$d-70Q3w49 zhQqfp!#$_y#9`6pJQT7vqtkWn_>qs(da4VN;0-W!_!nVd)aNw-*A2Dea;nOLs!ei0 zS9{5&FmRjGCZkCZ1rx8a-%rRJc0dxI3!`LqA2_>*?DjPj^2o^efG*~?U-Rr-Mm?R^ zT&wtDd_KBfVd-Y+W13j8K0?=XX7{A5K3FKHTCN!|XE*6F!86Yso^A__QIPR#-%S31 z^R;Bec4<U7*Ib$a7*O&%Vh;T8n!hlNOyjg*ppsl9_0zuGMKk)MTWb>c)q5uP@$fNJ zdQY;oojD@Kx-PTL<a-p76EVPKeTl9+F-IR*tvNq-VdNMqce`%u9jcUT@8tBQPUpE` z(X98gd|bEl+?K>jeQ+td<$wyonXL4Jhb57^i`Ke|kpZ{;{;7Ll9~7qD%4cKP#civ@ z$G|%`YWbJ5`>ZmNfK8>O7nS7TA{5Ba+`YXCX!YgC&}Hh+^m-Gc3r1D)-ZKp(?L7+X zj_?Xj6GK#`685BG;&^4w`rj<!{$hLGq|61JE)B_&5={A;%2J*C0v7?LX=<?^CM3?r zU$so}Sb=P*G~PEcnk}6f=+GpfXhoy+oO9?}nGzuruOVtD(p(e@^P5sMh}uulLy9pJ zS?swCPj<5zK^{!%{~$2qKIzydsuf-Kl8-C1pPebR-&S?oE$D%l7^Q{!Be2->F=~5$ zR@QOv8`t`BnBIIqMGqpVYz2J2{!zY)4!w?Ov03|J)pZK!xNPIIi^cw2rP_m$cdDF| z2M~DN0CFO^Uv6hT?}Tv{1M9dK+bp_qAh!}V9_r+Mx#38uj(ZS_(JG-f2P)`>+RxtT z3m(&h($J`(cT@xy*5v~&15tS`Xlq16lFqP2Vm08O2b|?~kdERO!9vvkBGnE(EWwS2 z<a1rI*3LaFxl+Y6W3F>AG$5=xMc@Q(P->w|JB5<$lwR+Gv0lHX4r>lFGb7!Q)L}86 zxZw?Q^b;U45DT?M)w%zrZXlFsG3}24A!)Zfw9vLEK<aYgv;`g@-SD?YWZzqGuXZ6A zBG*HF8_eW_(GoqXZmR6w#(jWLC779*E|$-IlxFL0U|YG`I12KnymP`DtWGYuZBf>I znmZ|FDlh-Ol#OitpwoJ;X8*GGwVtMSvR)IpkH{Yqo{x}dvc?qJc^cU`JzD31X#Fvs zlVA^Q;}kLXF47=r_v;ZmJtv%0pg|tRRC4c`ay0v<DHNzQRHJ~`lDzy<tJz!@ZxHXV z!sP9c*g&u6`fu-p)J3>JBc??lgfMZ^;O>)>XXVk#9@+U}#Sj5StBS;<_|sQ0-&e|! zie}h$=}`#|4Phh-7+j(d(mQMVH-O<fTLaC0kjXws5tS13m`-I$KjxONKNYC67=-OD z_5M<T<tJm?I@gBc3fL%*-xy>iWOQO&EKbvD`qPUM9hg{4J9*#1KaCLT+i06gr{ZWM zPj{JDg=Rin-T`hk?uD-&_YwXM?Wu1j?|a<9=>B99@HV-BI~*l^+YITu*5rPyy}oSa z9gYa>*1CiO+9DR@ls$LybMj19fq4-x>HG3~*t@N0Hcu419RQL-9Jn_`kkeS;Zw}9( zZp-6!<fOa#e!rVfxA1on;`5pMFB|$}L#2oLM+r8E+uxG%_1&={0C7mC^Lg$Nka$@B zRMFOJ3YMhz4`iLc{^izH9vI}qn(n=St;IylLFyHl5KB6%o!?&HgfaTQif4>raSiFY zkK-+#A4JR!h7cM_&ZAV!{p;NjNGGzU&+-+FVR4*(iUL01f_oG}W(ZO8$wdef>kdx) z+w202GM@#Iv1Ep8eldK4Wp&i})1^YGk<W({3c|5nRGwnsn4mb4>i*UGdl47nkNW#& zy)ac?Asz9q)2Qt8Nu#OP^}KMAyf(}A1vEW!5B+jvT(zh~Yhei_56op=(IIri!}W1S z4Aa&VNosZ<`G^10aK|d{+H*%gyL4<WqmRQF?)$Duvo^OOlb9mJAj7y8?r0Vf!qj__ zsuicY)4$R`g`JQe9YJ3s2zO{HDhCTfe+iy^US!S;u`jxT7cx>{&!oal!9OHI!OfPC z&vlyr$I?K%#P|U=Oh52Gy${N-ov}*c^4^dGr8G6bLL_5lzO2(<o^%osxFlEW0=g#A zRb>|84{*teCJ|imKUzmP?N3ILo9VZUh(m6J0JKmh-+vv2i7l*8k<iG5EZ(aJ4fa!- zLg>uE(5OtubdHL92KVvuYX$DPoc32vh`4RH3aGoDgG=<<9E4KO1|{ZG)&qw?D-2Qz zDm5jOJrfp%J+#iXh4PiMe4GUt!zZD%H<H)huP;_@e8F*E-<DgJG!p+BhQ#{L^R@1B zJf#~)W~s?)1}dS_)uCuVi|Cf6v;g0+OuZnJ_%(f-;NsHNhwnA9|3Z;uQ@HrQljOxO z3pQz6#_7F+?YKi;`tCm-+y9=%^!i>e%iMYn&4}$IUf6}j2<>m0QSGz?-G&)lRMpt> zYkv<_P0Z%LOT0Z3ZCz@qj0e8&GPq!r@o%vXP7(tir5Ny#`p8MxQ0e~jII|yn4}zbE z+F36P*OPB%WSMP?3v#Qu?yk^i*UAmAw0b&gDn~kbqzzIr(rNR8l0=nGsE&VlR3%lR zO66vt(O6#RH6Q&tEf_%xxf4ntI+M^!^fOs#<pD7}IMz2F7L!644as12zsc*V2Cd@G z2SIMjxoGr55(rXP&HaYTtSM7e{qr<;UL$Y|{72vocxXo|@$)$BX0$@XBwE{zI^tq1 zYP$E;6_QZIJJ@0Q*;0eX?4<z+ilGX>PpP={e`(~hLv9qkUein*46esM>7}t)QvB2b z$qA`=mFC@07{VrS45f8GK$AE71W%^`b@#*_Jh=BEeQ*S01iK}4t4+Gb-tTpoJn40l z7UfjKUr7O8-y+efis&tm9mjEpODM?QJ@zBo?1OMh-nMZ3Z;NQj_v>t=Ug&=Qh!WT6 zmy1vh2%RlJC4Q^{^||Yg8sf$GXZuBFGoJ%3lXTgw$a?z&gP!i=4d;?Ik1%D$c9Ro! zvkoFibP}a(7>@R`daVYh@XRZSy^aHEa+$%1D@LKTvS+)TcJBaOM!V6DDUaO<jkX>- zFw`-B+d}ABjw)`y?rkACer44re&y$R_!A&r778UGwnZD=y257<PF=7$u__L-^oUPv z2P?JpI1COpeI0Qzx`xtSf1JvG9IvRn+l{wd!#XZ#POjy*b*^*BuUnvYNR-wm^DvUT zeFM#U)35z56K%qFTvsvabp1<O@3D!qfrp4Scgz<~Rb?-mDl^Ay9^5BYvg^ss--bAD zT54G<al=^h>?Vvj#6W@jugtFUm}P_bHRNV=a}p8CU6<qUEXx1!aHzT)>atcud8Zn@ z3a7NUfJF|S2n(m1Ca+5YQ;Cxas6YMTz)*PTyD)~)7`aG9%JT_Bgx7a@NyF!at|R<D z6nRAK^*Dxyy&ZW)#njNPZ9grNsi@dUsq2&<!<Uk-+!sfT1FUWWSI1Hhnwr#6FfQ#w z4G8*>aN6u?mK3RL(JD&+o}=M3-$`T0J=Tnq@SPwUU0lW0>c#GpN*_)jC&6=sU<ood zZ@6k2SUMN;j=~`5CJbpKY_Wgj;i~N=QP^Sg!S6b_KNswCqwO>MG49Bx5`aKEr4LyC z9c;*EaZ+4D=80@sP+!fb;xp!7j!f2zi6$;95-nP59DkkdkMR4K<PBQPc|$5&@=eU# zR4WfpkXnjA;5r|viAnKP|8JdR4!sSZFc{YV?`tB(atP-KWkZz!h{2&r)xc)j>)iNd z?0>%w;q_XPW<AV|j-yOpvn!RNa_J{!MB7n2{SW0_u9AWp((Z^ZxYj>J^>GYDUB)E) zqb(URy{6YZK>1$!)BD8A-+qqIkN@_xSeEO%`5+<HW_SiGQQ1{Hw!A}l<FY90T)0fg z<eV>T{DSc(jLMJ8@W1j3kP=_qbAijzqZn!F<`@)5O7MD9{OQT`N#pyt9pU&KJ_?3J z_ZtzcwshDW;Q3%eZ)udBw`bWJ{+VFjXCZmhL$~sUhL%R4Qmew~v>q4CPu_C7O@s#7 zs(-VeP65*5S<yc*e7aF3HPuDN4Z7>3lK<uom0oIkO;I|X*@D$ZYN#$cwL&Sb(}oq| zPMtE}>>;WT;|<K=?g+l;66_ZT1>m`|EOK3pw6oE!r)GQVO9XsSfJ*p=$8Q3nn=?cW z&0z@A4c~!a{yE&xmdgDYpv^gbH98Ig#j&$AqKtC~eXOPbvE2WV%ek*CA}>1O+&PTi zmH(+ZlG<NycqB%NrN<RlZf3l0f{YpZGUGXaU0XMJW2(ju%s@;piml7_kg`TaHfdiD zPaf435zx+tj=PODNZ4T}s~O_mN6h8BJQkB3HD_ZxWyowfv$R@Bhf@{CkX=C`?i(DB zR}o+;UF|dB)52?^o^B!qlAeI#_`1K|VWf?NYRzPyMTC0DG2$=5R15$cjC|-ET;5`a zh!HO7c|2vli@rUZx2ms)?rn{x=FraeEbyEUoHMK~(fH}DNwFAm79my6{fxA0M-7pS zi_vNZw<6jjqXiU<t;=DExKAm0as-~sgH>hKLp0#se4*H|wP#>J=zXcGX5y=nyuecg z8ftBMK)bn`WdC(=1s4Ae)~%bWKo0m7O7@kpW{c&=DA6&^qgHwSF$WNM{6;?@w__*U z)Vyy)<=}9ck0#z$WwgQo3z^K3&17@rxrdKV%gnurbz*1eO9g*>R15{Kj-a@Fd&e=@ z)poOxs7u6LMkl~J^zH;<XIFy!<yB_tiX?@rKT}s$mTrd^qVNn?Wz}nGNhK6vh06eG z91r;EQ)RHTejn2nTCs?tOp0|Ighq(jE~HL$TFb5Xt{B%Pp-w;pqEH83Y$Vw(G?Dy! z(bzB)loq2&AlNP=0xwB!zv>Cc#gkh8MwH~>Y+g0tpv%ZzGFF7`xerE<aLwOt`Y=2E zG`(EC&Y*dE!%z2Gvc0(K@ELh0d$8MKYihV#RT}(r>CNeW8FXv(pOZ7zw}U!vwsgWt zZjtJ?2TR79W%aoJlPR2epR2}eEd_?_Xa>Fwd|Esdi8IX`!zS-l>Rme#G!lz}H8SRR zz^FfV-9<KFd(K{`{n@KzwzoN~t^<5YUvh%iV*+)I49U-3jl*O?Al5ozDjQl&r5UWO zCQDnR+I*+n?q9j|{J&yGvnP|3I${ZzMMZpl3YADx(4B|To`wpZUcdJ!Ji?CKf{X7a z+T*Y&)voWOcU!k@2STHHrjC?qx<IuQUTK*d`7i=kt9vm9y4igva&?Rj$7$6soAXIH zY`bCC&CdHV&TeE7%Kho%*(0ldhL+KQ6pWO%A%^x!u}-`#?Y6Inx3SjtB~MCD!B{4C zffruPMes#A=K&q*CxXhPz_1jUZsP9u8mrPOzSs7FA9o9Eo<jd%lb_!bmN(H=aaQUM zqa7+lvpKkRu|R(9kFzvN&tp@JDzsL^-@F_28K2ikq}*#+y)M+-pu-w$?|IxhAtT#g zcI+bvk!5`+j5V$iE2|?(Qsb*m+0T)ziRa2DIrbMuI!xTW_jqH>*2=-^7e-p3hta|U z*xJ~Q*>u#UlQ%h3fZQBVY%1_IpP(2+Zt@BhFOj;oZ15;K$g=iVVQ~Vn`Dj>xgq(CN zx)RrAoL&81!$+JYETp{W6~UK+r(5v3d*E=_X9CWfWxxD2MV)8j&&WXnSD3x-R8BLK z9RfP7eh4M|{i0D>v|dz-wFAcJSoa;Te0FA$+P%vyLnG}2YEGOL*{}z!fiFNT^VktI z)_1Z-SB)FW7NSwzm)ikt<me4C_yslqToWg<-6IS257fj-6wTq{YG!w;$84>9*&Xu= zpIl~0=U^`T@SVifE|u7xH}f3HxNgBJ#$<4zUnR7~cqDsbG+tz6cuko!51XHUsd@^9 zDgA~m{9z8MzNN(Q&y01-=UGjNw8s;wGqB-caH7qLL6EAwZnZg)#-0Kh^HC&Gq5>+? zzBFlMLQx`4!eZqbNw#nZ)8PZzITa$NlDmMv)W}bV1LsXfw1xv=E#ZBNnf8iG=C6y6 z^D9*g<Pt1!Nj^^WCo%)fDvnU6cTmJ1-|ubiJHU0`K8?G&GU@NEf<CXyIJ==PBS;;V z%j$$lhl{Z!4W6E5Qm(A(_9R<b=v~-CEim*O*zF?$TGO~-E9BfM>6qG876MY$?<cg0 zWv|Ne2QUZOK{z-(5N&Up-6sm&SFZ1O(mY<*Pb)w7$EfGN8`>WyYSC8pB?MTXyC{9_ z$|yAt&xZxJ1MjwFKd@^QLYCPAXCT3SS!4{EPM>wx>Vbzh*wen}HOkjPZZZ}=0PSV} zIKkPA%Gse}2rk&Q_zjZq`Y=vm%^dmU13`JU=UY&eR63eojD(s_!0)Q;FGo99g{uzH z*~3WWnE`}|!1|?m<$*NP5$dosnr=rr70&d(NOFvP|MW0MbipVIRp5f<;P?N%rOEUJ zQ?rw4bQX@~I8u@JalJ(UJrQPLL&Lwc&leUwqIRL{{94RTz<!)`I)m>zhNDcQDk8x6 zsCWH0K4n(XZn3vp0SF8g_pTO${(dIA`FpZY`!#K-%)ogfaBQ5bJ$T^P&g+a1Le~}j z4X^u_6U06b-+9vMG_T<i)z-I8%<&naFWze0K1(jU@T;iWCdWAwx<pu@Kbu+cnG@#J z(55Rr4%7WQ=soXZBj+V!;s)$k6u4+9o_1(9zD!`abOml~dnBp1xT*hrSp}x^U1W=n zHGDReX5lnZr6G&3{O)ftQrbJHsQj}^u57{iB$VI|Qt@6^0qA&tjlII!?{>P)w9cG) zxTe2}6Ykm*%IcW*LZsu;4-Mn_U!n9De7E)Y^!$bkVYBH^*Sj=5#wVuIjzht&XtY*Z za?&|0!HT5|ERw*d@+h0K-yvieecTu~KRnNU>8ADGi!<}EY=pkcAksqahk|Y0NB);P z{CBR=j(&`u$AEpXq57quiYQu12g~T+Z?y(nWaW1HX3sfpygy(6URS&BUfNR$6L@2z zC422wi>TT8IXnk=0fLBo{=V$5k$qHt`!jsPXeBd9Z7#$pUum6Q_#}9Dua!-yFd5T0 zZf#&?zvP|gx!ZeDs!~crgde}%`p?DFS+6e#rTZF>EsUt42o72Xp9k(-pEZ8Wo@Q5b z+D<{~kNwp8TmYzxfN7<~%1l;MI=TK#llij{w+*4=Xp*xe7(U%lO(yws!3mLEskn;N z`luYywfAnWT!`@`enzU+TwKsnT4}Y-#>w#FBC9>1M}>UgSAKZQ3;E5{M<kX&sKL=M zgBUj&oVyF7PCl2N?x0#N-{&KSaSSt2?PLR*%9iO;G1(H%rEh1Qn~@q>>8+dw*V8&P z+flLWzK2VZTYeWZQ{rGblwbqb6_!1$pL*(QA%6QUsW92W#HLqkip=Hpq}9XPrHL9u z&_%I5bf*?NRz@96u<tjyso*g&(t26Uo1g)@9q^$+^Yk&RY0j9TbITQ$LEz(p`_6^H z<f76rJ|$#q?)O3HL=g2<F$k4=K|dI>%d*l)3Svzarx1Z6yAWej1X6sd?WK|5LyY_X zZIxNG6%+6_BReG`E$llbOwlONCn=D0Mz17@1m};8dP1U<P~3r0*{$cfdVSXow?qcS zT<*P*Fw4yGyH_4PZ7coi-q=9Aywki!L;*qucSgLSjYZp@$1u#9_bJ9hJ|=fG)q<Z5 zz#h5NbKV41)s1$u6GMhinh!3|!u)Y|j8?W8`DRteIiH>^mjHC5S8M?9h7ynew-1<d z-5uB3LkyxeyU*bZUHa2aUd-A{Kd!FtUX1$I;YVM(%h$grcY2gK5^ovA9$qOzG|3*z zgDPqIGZYNG=0ANfu5}o@k4Su*!R%sp<|wy@hb36jMA3CI0G}_9XOozew=)(uwa?lS z%ZC6una*=e?LqJ)$5_M?Zzx}?G&2c-;;4#1dwm6(ZPrNQ?2dX{g%`VbOI2v_|0<yB z)dx*4e(ME1karWY;Grw^VoT<}$|Xq{tN`wZE4-UNP<prM!dyC8y5$Qs16{8>xBrc# zXSrr(==KWqYUN<=rSDboIo|TR!$Bh(Zme%vqP7X(cd5FL@>Ub`8X$(tw!AmQ(X|_h z1h(V1c%O_tDjO35E#L<{DyVl6n}UaoG6y;2azAxesX9DHsC+j5_iP092j0^V;k0s5 z3}q|aWd&!lC@`J+2L?BQUFrzA!p_s<x%JHRuLzDNWa#K2I9fHzpoj3o@a9;MIGR@F z`@D+(S+j)Vp-(<A7~GdBBC@(h1x^`*RVr?(`s!XtG?MYdF~eYqvq%aH_S!JP%PUJa zFxcdB=TTYmI!Yqq0_NX<KJCw`uE!2s<u4@g*|RA>7I^+9eKN!qHjf(v)H?`@+7E_J zcs;Wd6H2NM#ZBv!4X(cfsu?+RwQI0Cd&1PO7`w6i7;W`;n()02egkJ+y`GY*y}KRz zBOiJ{3!t{ISB!_D+8ujtfOc0&XX~_(t|ra5QcZ@bMAgsU(^zNw2GJ~cA^pGBOq_K* ze^cDGG0NQUb%-#vZr%OzpUP03)@x0ZneS(K!!4)#(P=FH&-SBPuJ>YNmYL14KZe}z zyWdzx3U}XPE;x#sGVJ#%rI^iwfv#!d`Y;E)T?9i+Dd|>WX?1<RZ-(8+F%?Y~CP5r_ zbEQHOWyU!wNA+q`o%H<l=H&Dj|Ena2K4v0ylv$M#wk-TlNcBH$8WyK%vNOvnh;kSF zFD?1Po0;U?HM;+Fd&2Km@_dd!1^eac4_c8aN5w!{!K}C?c7X0*uO@Aftvo(ovQE>V zhjr|u@VptT^a7s=0TE5tb;+0p<6p}p=HWJKv^C>6U?4fh;N*WOipFzEsAUroko9fx z@q2bt3I)3Aw|+q2W~%MS+iS~i*NH4({<f)W;27H@h%ws6A#-Rm!Jfwe?}gayd4AX6 zW~2eyofp#9%)sC(jS^eE!d+0H9xxb>E2m{zw;M8l)ljtR{TN9~$l-Gm6<2?PM@}-v zd3Qhhu)}E6Jrkp1=|UH?N~O)Q(ykt0O{CZ?s2fruh^>s)D!I}VT)8-{sb?u;j*p#B zfzx*+ptiO0Tq2LnA87t3_jgd@T2?<g!z4lE+zbt$1c3~e9Pga*o%h`sJIxro&)pRG zs!4i`Kg1R72(%rMj|E@PN{GvaC#Z9|*Uc`k*|JZXoOY1Im73i3pmi`Jq375xKH~`_ z<}|m}aE{L7?MCBM?{=44S2v{4;G3*6SZ;kDps#njp$-3kZscCLW??|I;&`9eT#8EZ z>@V<dzt3Z4-&Wx3K6hjY`3+fm3;fAp)jJR{t*Y(&T!&^OEg)%lPiqtV#QqPm-hwTz zCED7>H3WAjxVyWA;1Jw{ySo!ScyMpr-CY`&Kxo|E-5qXcpYvUL?q687s%q98W4x0W zCS2r@^mQIBo-KEf;+`h9i!f@UXX~+y7uV%Ve%)St3R6JBy#!Q7-*Z~j#W2Inb~01g zZ~MK*H7^$NKmRW?NlGmQqZs?=F8UX1KK!yHoZRIinhc)0ievkB*Ex4YM!sTZ5_9}8 z^74Tys`$w$%`p#B`jAmp^2Nx(uS$`OPv|xL=@2L>>p0Z2MJcB8=GQbi_&Y)%R><Cz zd~uYX`&K%w(fda>50g4FM#I1$>9PXE+;_1Z)Q?W>I;+Piq`t1jc4mZEBg;tLKFssw zpUpQ<h7&&1@ra(4q?p*<8i6$BS{CmB=8OQn&LbGo+dbT8i1<jW7TEl4IR6x2ZyBe> z>Kf2)7yQtde$;Zq;UL{cG3^MC?hB|8PwT(_dw4Y`0nrW~lqr3pSI*1<BULuxda6S# z7lS%L<C$62@q58w-yKrQrMsx&9W%r{&=(quHZe6H#fc3RvT3hXJ}9#R3(P%s<pAru zxwEDC7_>YJQS`P%Kdmp4U&<njBAwa!A2!@W85n&U`>2Twiw2ORFNDnCNJ909MTEkd zhqf2EOYI6loMX+Z<9Ql!u5w1sK_*9GOqQ=kIZV`~#-jxv-JbJ?C`3;aHHLSuZw}rI zQ}%pU2`VnQOdEAO{;dPyFW4Ki6(BvSR>Oe*oB}llLfCy=e6kOmICHRTs%~Qp2)uE^ z&xcQ%Ywy3rG<!~Hc)EQy4}+O={6;<Hv)3YvP;1E=kQ>U|s?yx??}pQ{jgpVJ6dc_U zRZ;3*(Tj<l(`75~pN?)0jKypjqln=Ujf#yAEj;Pg`0pOkisn=Y?IYLBqv-U#=R>Ul z#X?@EYI1k7xaCk>`aB&@Tq?a#wJ@W+x8-U{_J#^A5OdX8%45-G?V0xSxxL>PX9xQk z`w`;looo;bZ?==K*TRvE*yRzLjq9cpDdb4}=SE>HYj2xb^O&0`cf{o19p%Sg8jZpX zo_)5qFgFB!mrGdE9tuv*2gEB|hGm-Zf{s54*|{pFR!w+#AydHb$mDLV)s=aO1TN^p z#kGQgj9q9RzpipgTfX*yUBq9`wfvS&ecI<&d*0urjrdm?V?T)CVLsYaUWh+js3VX7 zBE;|`TAnx#Sz9M22TE&~GnpDcM58Y2geB7I@kr|n_bc~Qu)Zn)Z8D<+(;^*DwRl`W zbvAB{W?r6qrE*-2I=`>SDe1oaRI|=(PLgqZPJg0Sre={M+$~BjLxr2$G_qQD`=7tZ z@~9!RITs1_KdYvS_a4#z96-u(R^vDTf`Jr5!xqWGFuKFwOO-VlQel^Qu!G-JE6X(M zLUTJ^ZVw-xFo*}uj=fMncXdx^xm9#QmcIdpa+t7y$6ByruomZJZU-8LW}^}7#IMVw z_i*9kGEB^3jn({mf=TxrmYU>c+i;K;l0nx40>*=`1YmiWr;90sagK8~N}W4>m$6S3 zZSRA$*Dj>4<^?O3L+Z8r+hnTaSpoj_+7Eu?)uBX*w80TM5f!^j-UTM5{lk`mAIs*u z!xTL41+r5V0PpX%U{pz>Nb9{KBg4QH<>+0#&|)%k>{^*?`MkE&XP?2!7g-CmGlvFY zg_ntZhglK~1giSGevn8e`Bm+0Ig~mBnj=!cgq{<L6*z-%B`eXKi;&OmhQnm~->rqb z+8~+Sy2tEd0OkWsRN+Tn|1$&Z7vBDHPJ8`#3aL+6qEgc6B>V;->ef2FnzvZ<h4h<3 z$Rj@XT5<e~2>5a!A4o<sEzNgIZPI-F-k42tWwF!@7xac;jt)Gk-)J*m0(X6`gHJ97 zLRQ=!F8m)Kj1*Z+^<X740~&!<k?Oqm*}j!ofh!H3%25ulZ*N{Mmk_uf<!qg(Jk>jX z%e<9l!cBt5vePHz<Bt3|RRx>8RE!LNV1G~@n2xxg21RFnhM8lSxGvE#;stSjstvSO zZ{jVMrw1vy<P^h>?GEb2)BM;f5##dI%D7iE)tGG;Et>{e@YsoAle9x1v>@l-j`6`! z)$#mf$R(LcVM3wr<T95cJQ&Iit4lkxiOs&=Gh`X?iS^tMN_Q^xdhMUr{n?y^q;JKW z-1VH80Hf*8a=5A6X(7k?7c7*yu4Ki0xPoTiK6l>x3>$i#*_kXjEF$cF#C)(0DKiM~ zD(7P<D~e1je0H^uEhy+(DUQD5<iO6#DOX(=d&D$ZHko`oBkb|Jl7?Gr{%C8s%+p24 zwREb02^|GLH*n_OXd;lvJor@I)GL13e}~z4Gl~*J4UE#unrCynzh25jC2c#dNM(Ai zm@)5kUM%#3i|#vE`!L?bTL;ypN>?Hq1331l;5Or2>-HW7Y#!?S8}*^`iLDab$CE~$ zVO`F-B&;d?WA)s~6W=N^OS!D~AN_7#84i%ryg>0SAQww6GqPPlr4H}!Ce;{ZO1|5* z<yu3|*hny`^Z{novRYee6!dm;FyGT0Emlbn)GnoZt$F=}aBAX&I3^)`55&9OpYros zYJequj{V^$ef9ck88R<n#Z~IqSN0>5YjG@#wKls6RV|F29<v}jhI2oJtl|~fUZZh! zm*o^OP4_ZB*`VO1wkRHv5VO8m(uVyNO{S-l2J<bes2+qNc;;^rNnL75#6h099O<-% z=Dd_`XKWM^#5P>J)D#UcbmIk;ub}>3y2_+Y`T`Q~&{X-IZ$gO$x~i!mY7jOdOU06d zpMGCxC+w)}(5cg!yaEquH5tnt;i%a04LjK_5`2&<=nHL{y=z4=rPKRTu&aa^h+Zls zhPy5=5gmk_jz#o%Q8kM#z;l0i2x=FvC)cX_s(50Gxr-EJj%1@4=G0q-s@e;;u;Jix z#eNuU7ZyGE@68^)_#G@?037$+z6?3ZcRUE&y;KU@-CKLzQqgWdHhU;zyG2{Y3DZPd zi3pqSqOxQ}Sv8{ds=`$|2ZHuQxMl|+4F|ua0*mBBC6d3R6ZJ&H{v_uyAnC(LbNG`b z?6juIqW@TKLj3{gcho1^e-DMUBbW)RE<Sx2y9sjFHapn&Y0zKxtGh_Hn;(>tIW|7A zR9s{}b6BFmr3P=(z+*)+GGxZNKUtoHrj~4Xm-e(fQEi{-c~lCvtApYv4TV%hV=A^~ ziZP7dR$+jl43ctR^SSYZ;#q#C<&BI|P|&m_dtrO1t69G}nbXOKCqlqEbH~lvKSukl z$6u8`)fhCRDIKusLh$&q_NlKRtT+_;JxwiB!<qBC3`S`qsSUI)z!ITIN`d%dRngsP zpQESQ?LV7f!$yPZ>icxVl)U=3wDLn5F#GSUS|ynp3f+vu1Eu5dG}LrPXgo&6lw_(| zNI2*Nt0ZP1Kp2-4=y&e>k`wgJw#gI{w22erpnCG-n5W;)O+Q9Q!M3gm3WW&Gm>e3> zCSbn`bg9888%hEJmWNl4je;fbwp}l}Tj<<?b)RJy9g6<Ak^~7TjXR}r^pwYzy?Xgl zMZ~xCt=cZ`QH`P*q1lj=)Q<Pe%E3kmR6a<VbdH3AlQS6LY3F->Ol&A%^7Ue<5$Qz| zWTtFasoE+{Z}&IHyyIR?hjTR7CzT35kAJAKf3o`+<;C!|MdIuGW`0{@svRupm#?pG zmibVn22QEY-jb@s3T4;fGdw9}0|lcq)YUKVO<>c%VGi%vlLU3h3*Xkw*CP<GEBjSh zx|`lJjKXv8wbS<)U(g?SbV+$fhZwLTKFZK!6M0SaM-&MYo9X1+V0dI;$LxSZy7|N8 zx6D%{Kp@jy62;{ouh9Jba_QwY=)AzHR!No6KQLmCKUCguEk4bY3O{SJXcscUYAUe{ zXUiyKF@$0(n<d#lk?J=A64?Bp2(j(#?fCh&!={iw11?i(z&91k{@Z2yUWt-k9LE=j z$J{N;#4@|QN!K8|hWrZN+&j|UI~psy&F+QtU0Q11R^GuG9K9^#^8P?wP5+}*<P9HL zt)<PS@a2;q{w4f#>ikILOoco1v1)?+RnIc)RiAL_k&K5+tD=cBC}+gob#H%4C+Z?G zl%y!8IgSG9D%u0DEe?;h%It~$#EH(0r~Z^|QBP~6UVWt#inBCg@!*27@tKQ%JT3Mr zWHgm2Mh>hkkfGJVoe@hIF|JS>9g|>B2|aC=6um*~lAYPHn>+*i<UHrt^wUhESY%C% zFc}5-Jo=d@3i?mTlT)@8$|yUX)Rzmc9}rMxEZZ7`nuO{wfSPQ@e-21oJpt}<k2A<< z<~0&h3U2>zZSp@&YFvzV$7Hz&U-0?`cR39w)ufZcCU~cVK5!DgPk-zP9mS24!l2PO z%7}ZNs85_$%L74)29>bxxWM9%|C350AQU?snEAzN-w3?t?Ky7wP_otM6=VXw`JV>V zzyB5BKZ9-%jv+2~&qR{rNr*1s6N^H!cU_?++d&|cZ9)9rA4lAQPo;mQa5kiG{_Sx$ zDQCeO6vM9s=1C;NhvV#M^w+&c$l{wLQ%YGNG_@sUDYK2E3dw`Tr|+#R^g)qc$f99n z_^fUUK1nhOSE17D%`r9kpYR^W3OX*2^83DMY^|{|;)Kjl6c=8PVWA8U=Py5ZF7a>q zRIL@Y<SY@+-Xm6@gailDriT#`a;@%kX?G@bTiw023cmpdk=I?tA1P&RCy%;aTh%jx zEsZ4wXpe<E&COW4?HNY!v;E63<Is6w&WakIw}pc4NI8`q4&b<w<OaJVJr39!h%=ld z^P2IBkTfcn66A~j?Y1wMK#)<vComnmiCNjY@pef;S2;AC83K3~MBC*YD<*TQu!&^Z zl!{^8!AZ+yy-75`3`EGHidEV~A$m9mD1F2+q`n$2f31{^krUKA%yWBeo64+hp`)?Z zCAy~%i2K_(;vU0xcG_jKER--Wf)TMWMVfkvKk4TrAr`L~Yi0NWz<oR0ocGho>Tw*h znu&eh77cH&Jr7V8gC4est0mbtYv4x3uRAtm(T?CTyB9#ZK{>^2;kG;4WDD@WOcU7w zi0FU}CJ!HB%EP9ro8AmA{0Rpp+M}p~0L@l@@HAIjVNZ-3`Sll@5x+2+!NsAn&Fn;x z<d%gA{lF;Mmv`mC+yNKV61WCsnp|)DpPHvb0377CNhY`g*pltQ5(+eQNmy&BU;k@_ zfsm@?{GC4D#S|)snSY*m`}kw~DVXNeV|N#<4c{0tYxBD9mNOizayax?zmR%m@%VNU z-<3*Lq|6X=8$_y4@F5_$vVe_e$tbn7=c7CG=K*sGaR81N6s^+T%U?&BEk?zV{kGs) zlr)`;Yw=1dskigmSflfK<f`uIg;bkndLy%$m~25wp(@XMCX;9M=zS~5niFncCJ1l< zg#AkPoUmrE2&G>c-XTbR#y=&EGGg19CTKQS(ENnKPKH<DG_odZyOVtx48JsR-z{v( z=Dl<2Nqc#-Nim|*Wi0b}@M{e<u`sd|Sce<E1-vPg@=XZ!`sqvxJ)S#h>babxxt;Lz z{`|awe&V2+dCB)^wY-A(5yuI&c4IDp*sP0BD$hS%B`cM?5E)rO_Qq2@_pEtB3{pOz zqM!8RWifddOyNqa-~WkRV)yzmj@xrnc*KIo{?iYEKfI?GW|=pJ+m-!vtWx5}zZm3E zbY5CF_1k~+g#t6SBg69Ec{#gTP-TOl-5$7{63onIkyob~hyJtRt$ev?g^QlEYVxo0 z*e|-sWKc*bz!yP7jik|pzp?{K9E;0@z%xt~fLRGEg&9JX8$|_-%Z5uXlO6=Yt443) zWc=NyahsVZ(g@&0l$p@CPGlQ~-@1lHSBlEwCplOlA}U_}F`E03&HkpV$^2EWkN!_9 zY@|<fnnjfR09<IwUC5iN?s~pc#!#o29>92R>0icWcRxA6V=^n1gl$^7_VKIZBO%&w zV+xY_TnighH-l-8v>qliJP>J-?{_Jm0eXlRb?)BkSvz@ZlD>D~1Su{wc{g1vNHjOC zydod`3>-q2fD93l+rZ<6MMATK4RI@tOib>YyVW2Xu~`(4nx9kJ!R@ws%pqz>`_iFF zsm8W4Nck7?FA^}?%B@UfRqk$NY84_E3HclKR~K{IoKCGXTcAk}hvjM%D&R6SYJca3 zqJ?QG%!p|nzmEgAF{A!1U%?8qqS5Bu?u+v8npeiY<e6bMCOC5ISQCuBTg<ayKqPc_ z=E_a*xCn;v0eQ>sS>O9}GJbJFhPCtW@$+f-E4W9#%iS9#bI%v^RJge*_D;|Jsr6Up zv?K~7o_F;<A`a)^LdI_t9TVVo_{hmKngVDliC>luwU9XKLwnpa`GVvz@KEyYH0iOI zLD(ErVwLX3rSM;=rULglE&uXQ3kXJm=5p+Y#nOtG>-}$t&$i_C&tr2TZ6_`02|on+ zEl=KknNMN>^a@1iLc&l7vHApl4_=IBO>CKicbL>yCA3+IyTWOSI}YNG?^=G`m~E_S zoVb(%-|B-Ju_Vm5^=29;u}O?S6MTTaP<OdKs>!nt)8tKMb^4!<5x0^In8dzvZrj<T zVkU=O*rl7ZHV)m<QKM`V256O>$o1$vzR#E&Z^Ps?5AbXPZ}V8M$e6_mKX;KPF%~f* zk|_UX+Zrk9+*6RHb=)nXp0N}nqDZ$Y`?V`-9@VWP4=KZ7Xi4VTi4E*UroGl}BA%vi zf3z~z>H0m-Sf(MD6WErxSOF~LpmhpX?&i`|1qC@In1<RtK^Q^0;$vE#WUj!fAd43- zf7ep4gE(S}0eOkgZkxj8_kA@QO;#768AZj5l0nd_yl@*<>{Fuvw^faW7nnM6PpWt1 z0@jro?8ct3^CjvcQ$8v=O)ry8OqMzNeU}YZOR8MI5+$}Cy?^n&RCUUAi3%u0Kg{7- z@fxwC<N|aFM8y6bl5Oe0+8yO99)zWv^z%fw9j_j~B;m|a%(n_S2YNlayK5DL)_>Qz z3G|ONpQh@pReJCq1}p+BYbdf4&dgE=KO630Ry7ye$P$}RSCy%pkYlPfN4=4BjM4rG z8<0Sjoh4TdGR=Yuc<ua4L3<Qvt=S2Rw;DlHgL74dg5_4lePZmt4FA_8vK>(`n8r&w zDrS{4LY5TDBVY1<RHsoLfs=E9l|_w&BY`-|L6WOI!%DP&WtPxu<%&hz8F0?RH~cl_ zuipuQ+*tr?gv8(d8an)Dsm;rCw;>{Q)rS<2FSTR9_O+qaLC|Dv@}|W86)b6V9VFp< zN{0b9ny82`M|;3|+$x#44w^m?$&HCTXaX-z%^;FV|BvGYpZn3(bughCR!ZQXtjd%_ znIq`AME!?y*;4E<)pmVRb?eMYhD41@;2h6Sb}u&EU3IN2o>Dkexfv4ke>vSG0wzEA z8635(Jmf&f4l>!AEE8ogx2hG&y06jJ3VeS<XqMh?TYxFS#1^)O?z4p+0QNWO1(qJy zdGk)OUp@ksVxHEc)T?$3<})W6m1PXM?dR%~>uhH^q-MfJXicn1=T>Ot$n7P^NginL zw;ltx!c673E!F~@Dv*_0M`)5wf6dt>{K27!009SN1DKa>7TnH{MtrSi1dCqQrW}W# zMHdXA1{KAqrtk^RoZvG%{E)9=cVVRDUC++UPsjNS0;j8CGPBy*cd5RZd6N3TP<g}z zr$hC!|5S$SjoCT-RWN>lP6)n-4mzVwUSDvQ5(eKJqh(cHM@`shylS+NRSp$JLx`?^ z_gynvo}7@e3%iscpcOaaip>1|+keZM^y`6pCNtrxD)E4Xo1~&6;4)hK!<|l}Uf*vm z$OqTnA1%>#y1P@|d&ez^sSa0Hnzql#Ng_)i<Yfp<^teqtnDRUMHDlmAP-yX)fK*tY zcs<E#%~A0WA9Ww5y|yu(Gj5dOz$wCaY6k-4DfX=U<Et@Sx=Ml=Ux8;CY6*>>rc}I2 zMtid$M9ga(MQtfqwW4M#x%3nvV{3nyf87~J(a=GoJ0@h2v83W7QC_6i0_-NmjaD#i zq!g?y*z)<ak}Kl_im~aPK1CW>rBPHWe?53#V8!&0Xot}3ZC%h2OY_8h&v99N#6`)D z2vrbv^VeG3J*`Gb^9yHCov1g{y%8&J(Xb0vd|4+!SJT8k2<%PcuH3`Em^tRb<=1<F zKxi5nJ4MYP_ar-0xc$~DG&|$q5YBhX?Wn+hXuoFzOBCYsVkIwkISE}tslzbY=IH_h zw)XrX0%!6$*u(0z3Ztg9+~ijXCas1u-7MEHlHU+Jz8obrN?Xr-lrjk^X&e4gtmLFw zW1(!~`C?};hie@+&=0CWQmYc}rF&&_`R%|d<gqbZ?O8<5<Mr9!YFZp;5xmxoy$m^$ zQs>{pQ9>q>Bhp>sv9if$y-NA${2C$>KGbP2b0hJYgiAG<Mrq>_<!a0G4d#d<0)vNf z@?r|*M9`*$U0=KVsZF|aKe~;cs2aFgmS54rn=JFzv1wIStXzSNgg%dH=6WT_HO^qq zC|EMudA<dwzJvn4y&5-l5DYgu%?_&#1e-w?hv=TRNBt^-;282Q{`4@$J?^;O5*xIW zRBN3CF0R8Wp+;pLa~{Xaiq=r<GJljiLe7<2JISZ(@M#|*fotn8NKQ=()X-DFIRBL5 zwU3ENodwUq(sWurI+~*JY@a=UYyVYsG^*C~ITNIUX1QQl1W0L&%%7|#tAsVDpfsEk zLGH){BAk@de#vZ1Fzq6)ho$>~JlfW2B^)empLcql{|-B~#0C=bMhM4RF^niJDxv0K zM!jWBC=wlN;>VoFeLHFVY0!~J_DzRq=AF9QhZVR`a`xi8oYNjIr`Y)Svg(NoHH*vY z?Y&3)`Qc#F?*?(kASja$de|fdy;|4q7=1mo&(`-MR+Nb7V`SO`MVtp{Ky{B-p8sYb z?R%D;#cZ_0{2bqx0O%-(+HZyv|L0#o=mfX#8%KBuB()mADPfqpia@HKTcYc`w5HnW z__32|TQ4xttb~4k$Py`wzS%Al6It8OM7*_#5HSUa6BJ>k3`a}P^fWRNV#T9Fhn*>l z>m+5wjoE45G)114!$ZQbwzX1klyHWq+T(q+EoZUT;CT~_Dstarnb$cLY!~n<u~C2X zdw5~J<>lB*7wk++4QSgG5&`^kf{Hj?dNx}6v(D<cItozXG!ko%nsOmhR4$D;)0T0& z&0Tj{OTi{78EwsaMs!h_L)pWI1Tpl8S73L$s9{+ns&g%vGF)?Pf#(#H%eOEAHBg|X zp{!<Q;ak)whntOODY3JJ4{+xAZ&c~c+0oJb%_rCiw`Hhhq>?=XLelrd9%GbMsJl-` z>D-;7lDmg`97iPOyIv~5F}^)b<T(7U{ry4Rt7p8Px}dOm(zBL<+ZEuVnG5PR53V;G zpcy8>n3w4Fc2J`ymILtZvYXNf_4wGaaiJC|i0B=CxN-Tb`<XwH5{)$Dn*Zt{7w=ow zc^aMN;E~*tRX1cuP<;cay5CzW2jTb`|3qoAKj}>#vj_B&?3aO%M3Qk(Cdcx^>7v`N zj4Uh2XvE;>{BM&gMwv5Tk9YM6)n$S|C5o(IxUY~olP!#zmrw?i5-m>E>_5l>F#*L& zvz;QN>0QiE7dajb#!x00^qJgG%4#~-zta(C>~lakL1~t3y1-;MrWBt&#H%jwa#S&- zZZhc0J-f)C#1Qz6H^Y<mKYBf;b%^*+W@(bHVV48fL&c&#2e@*@GT>pU_Gu9SKaoe( zPXA8aQ~qwJ{heu}LPJgT2@5ASxzsU;kV2!%=rq4}#f}0dacdSvT~z%KHdG84NRZCg z_*+B22!czVJOBR>2}=9QrzZfG1X=b8_^`54azUmfVeIBgP>7NAs2P^PSyA?_Nd550 zb~aJsnfjt1QD*1&Le8Ln;DvvBj>7nnjmLK3sLXX2394)Lp#mX2m;t1;`*<T`8D%!( zF-*U=ARr2#_e|<^4k;+c(I?_r?|stmnYc&8Q|0+0Gh@JJ%U%q%oo;2;o-MR$bdVe8 zu$UfiqAz_)1Z*Jmt5+57Nj#8*!{aML<SA(eHRSym<mWt^*BeAkb&ADY22%(B9i$8+ zyigH3KLh)1BWlx49>(pE`XK)2;$)*uE^AnfY4^1J+0k+vG90Q1w&w?5{)w-k397BC z(wcxm_fMx1+5T4urC5W_vPIy-^f2$Oum!_bKq{i>P_&mS-RH-_0OZbP?#CI}v5_i= zV}!5eYxxP&=klJaWY$|VSMtIe+ma4}@NQl|j%o3h%$~syOv~{!VwyF|;<0J?0!#|N z#4U&*Pi$UI_~JW+2UIJLU<=>NV_R(RpXNu3EqIqSKT-;vnXXd>SmnE)*h@MvQLw7F z2Ih-&oB6-eApC)Nf};hCU)o6XRn^Y$*Vf>JwE0uHB2Pp9JePq=<ENyoq(qQ}5&^;= z-J@g|PLcm3pVD!(XM<DYQen_+#X`sJA7zt#c?cX+b<s>32wqDni5u*`1j6M}GR-_p z>&@0H{b^glI3*^E+`R@Zdpc6wj?K4SE~wV3w<Z0iY)LCQ=D9(C!Lq0TLcsk@+I&@@ z<uzspZ!v70`^y&-jn(iw6k+fEN*-bnY`Gc{+$mc4m>JD)^*P!vA<Li1T`eC?NEwKm z?gfiW-=c5nt)Uv-6$%?%!tY0otJis?<%;R{{cOn^QwszYB$sz&-wYxeKMntwBMqg< z?Jw8TT?VGE(w7_~qK4S3*KJEg=3_?@p`#4Lv}y)Z5VuSu?2SDcPs1Q#7E>goAHgJV zMb%<|-7Sp<F?~=Grms=Jtl11_Yoj1+<<JZ_LJw`PhP$EG?h+So`8@}DdAdD!(h-_h zuKiLsdTYpB7EiXfu3EdshT+^Ljd0niv&983=@S@f6mT4^Njh`aNxpNXZdkoKnCM0{ z-wY+QQ~H`~O1H;M0xQ!&{AFJOf<u3oUOFtrd5p3pHfiNM<}WLFO~1r1mSlqSW4Hte zIO4!t0GvTmq!*!>PM36Wd!7pK7^@>RdKHdzyd3GO5E_0`Muc!jHD6hCWWU&A3Mv|o zD33LiQ|eBz<Il}xtr}SJncOr?zOUuRkYiVW#3!>kdx3U0;YZ&k6435Qr?^hVx%@R) znD0Z~7_a>Iz|52Tstv|A_r5L#6CH=RwdPeF$nq2BgTTX`G@Som@ssFS^L4>yne<s; zTD8=Gp`096temfSZD#|$LXV>y8apGQ6|{PFxubgFznO1Eh}vpBDxjDZX0}*&&c6Y% zhtrZH9$<!=T!>k_*VIPft|L!4QM=d_Va6GHhn^-of0UuG&S=S8Q#J0l@RE`r5tnYP zT#vwqViuW%i!TTJHjtA33yOeRjMD_k-bX*mG6Xb1+cOy+Sa6+7jA?mZh<R*hS`JXJ z86aIVqn_n4mub#G`fGMdt^62&Rgh1t4NuPT{B?@}+`XW%y5<Wiv#D(hY*Xv2yPW3S z_CNw!;9e4US^OZ+{X+ddSH^K^fQJJGBusT8&%M-UtMvNowNlABm-D_WV662U6>u{6 zsdvj{-uG%7wuZ0x<SF>-ZsjtDwbABN&yL#DHm^D2fx(7#N+B6nxQ3g2Dnk$i&3h%E zLBeHaj?|j$2$fkXNp3mbZV?`qo)ww0{G9r4YHQx}IMX3^QB*2aIOg}XgAAN8X)TYh z(40z^E#HD*i;5WnooDNz6{5B?;Z8<JKii8}x0XaMG5;6oLtz?E4$;OW^JUIncr67a z;0H(gF!k=Lzfzm?LtkT3n&j~PI7JSZ_Z*g)>Gpcs=Afwpbonp%p}vuzpp-asDsi8U zHJAKJxi(|T%MzXx>Lrux+D?<?dy{<GEZG)4*v|<mm|1U|{r^8<If_G99L-lGdWK;- z8O$9>JjcgsUI}>Ii+74JR-I%oBCeADcnlOq)t)oBW34lVywkgV)*hCULTZ>or%kM! zne(sw)%@OenrIa()b`+SytZmyGr}Y`pp;w}o9b`Opq~$?TR^edywF8OkT<|16g&&T zPMZvWmL}>#ldbIVMb)Un;Fn1IxIC8dN;=iU@HJ*Sl5rco@5p0F{wH)$v%Fh6L$Zp_ zl-Z^(F9GA(*I0TaV(GAZNbP8=Y57abYqw!nzr)*f!w%PJ^e>wFq`0;LKRj&u+&MXg zc-&U>PthUe-LwKe&a3Ah0771G57pZDOUV1(Z-r%^b|{8IatZ=D^*gpJu6~7x2P*ef z<}zbSE~W4k6{3e2=~S}vkfz=Z1v~C}37>@`$mlwDTd|DYhFgY|F;p3}m2XuEHMjVF z{CNVkjIdooQOY3~$SK36rIA=A=0yW+%G|Iygqb282!yS^=`6dLK8#^7kS#G&lb^%> zXd9p9au=5oo^z21lt1#_o%NI}J?s(t&0Pabw(8^^yrUa#eEEcTlLgiwtrtywsV?Pu z^c*k$FfiwIJ6#{YI8{Zih_xyYF3n_e6Sg||EQE|3i1UGO=`KPUVXsDSO>Ar?aGO#E z{qWwR1{+&jEPl)1TU!3au&Slx3RaX+C5Wcb!_Eh9+-iILE)6wKx)g7|K0O?c#6f1u zqC%kFpXH)bJRnpAecTef(sX9Wu1%yi`Qta@T@D~45JRMPO{rhOV;OsO5pnTSf}^z> z%<6spa9E0Dm5|+#MGb=&gF<i11Sas9+uM@;lk$%W1C`d%GoZyEBlm${@xBZ~oE<6? zSVx3>;vI;C3yvdG@*`q(UDmW-MIH4L%B8~%2EOq|7D9}0LFU)jUugWUwx5Qhb%czM znz>d;m)@0GL@JlMvzwQ6tsZ$-u2C;6cNn^OVW*aAkxO<i3_DK!N+x@8s{#1J^?@9o z_rEt-1#6438baiC&{M)YN{Iw$DYWPI@=TYa(x~;U)M<K}QjeiPt}sn_I>*t9pP2_| zBk2ivg*2;VnXW93ba(MQA&n8I!1-LN=0;t-dWfh-2(hY)Gy+h(96^G+<Uk_9#<i#p z*qFVFe2F6xUUel6RGUJ942)JhosXEd7>yp46b@F{S-k!8z}oFM)LZB2_%!Nr@Tt`c zmRYTicYQxQE??}425*ddG$q3B;$5A9T{Nk|L;htKKl^?%aa~<XY8*Zh?22oDTe6Xo z(uk*7NlWq1KigEz!JCgY=zdGOvgx^5DFOf~&cINs!^>Y=)rtf&&PjxDE%O(9+3JTi zmkYBOciuJhDXzO|BFlb=BH)OlRC0a5%(_6Zg5Qr8D6yymUN-Cf@58ZxzM@0ZBi$hu zQ?MK?RxG9>)p9GzOYh)ngkWhdw}w;4J|WA9rHpp>_mSWm438EYmY_<VD`Q}88^w=7 z_z!I{+E50l>JcB_r?h)BX+`gqODn+3pkOri)RB(Jdo{5Z4ytB^V8NAXVF@K?<CxMJ z#k08PkEcXT5(qHQ>JbO@8q-k?MjJ+~Xl|VVNcmiFx?!GI`)fam+c?Hv@qKLr`3u#J z&C-J;lrK@n+!!n$Ws-}l#%AJ4-;8ndtYe9(GUa&<2N1sFd`(G@6p4k-2WPA&c-Tz0 zTxvdz_SPUu%9|=frCmv_LpJ0NFwh?x6yc+rj(yTXFlJE2>Mn+5PtOk#^~XVYD3=c{ z#x^oPE<zn+Tf5w)ina2}3twqqA>l`7N%P(A<V#C<xN)*^&<#ojlJwE+ym3v*M%@g_ z2{(7WXaeaiF4CGkzKqhuX<U@&=kEo}(9lDzy`MJaQ!soZm@&9}b}%dis|+vCjXEFJ zU4`lxjT?)7*t7<JhjgeK<}U^g!og@q<Ic;V7CBZ()H5MJc@8x9XF}Hfsml_5jm;5c z1pZL~5I_9po{rSX`T=9ZT`H<@&OS!|T=$plah-*#=p9F}g1<WhS|f}`-=9(sAc~5G zJJs%gD!;gt`jiV4HtcNc$S}7{+BycM4oko5VD3pkv}UlnQa&E`A0ljZVKK8uSlbks zGG=I8iM}o(5l82rrP$qq7$tf2?;pOsc&|VGFUmOHZ+PUGv%1kY64o0z7%ze=#2sXl zzYgP+oK;iQ4hL9w47K4E*!lMO&)rL(x4OKbdsIG$BOjdc*n)(i(dD#U)~V6L06#G+ zC`ibXB-5#?Fog`pXx2aqmUn@Bzqj-9a8|NO3Cf6EnkY6c^Q#lobp|Spqktpgm@Woe z1C6#3-X{JC2_R%PiBc9|?SdU_KDo%y!>7^;Fwd5FFsZ9(iYW@J)QWQhh7wu26J>L{ z8j-V93+QFJ3vu)lBI$_?>i8GI_rYCVyn(Q0A`-2@&#@?YH(&=y_uH<OVVmjJlK#n+ z*J_i1`%cz~PlN56rMALHXF3mJ<3Z|Mu(Pc^Vb7QOs5>h*1eFt_L|U+2jPzBF=x}v4 z(L#AyT8SUE&5-IX;&w1H=yoHmd4a@Q4kqT_XQRJoSdKaM&$^)Jfc}T`D43+cjhSf6 z=J0ni&`6%}qtUSbob!%DI{+xG;Z1T9HDV#6?P|}QD29me)NH5hfiLYsangPi>}F$> ztH_2d`-Vlp-UQY3^^e}Is)EF#zH<=5y{b61^qej|3V!L)#2`F$QbRQKaWyD^SnDL9 zn6NL{cgqNc(XT+38L$^JT=9ugvn*yXo$FogrZ?!pI^aDX@$m<zFj|<>28q}3amBuU zdWR7%sdW9Fl`8$pmxq*B|Ls-|O4j;8#xJY#?`x4SjwVmJgy*lTp)MQlVD2KxFNey3 zB?PC|$#`8Z?P7g~a>1Y+12nU+%(QZBQ%|zOYh8Ou^IxkNmvB_a*srH05$J_53-M(( z3Fq|4>~8}Gn2Fw#(dC_3RsGyZGa;o(?`p1@LC(60cxqcNXlPMMnharN><R{R%k+6k ziPK;M569G4l7#XzopPDVN*a!wwlT&uydvO=C6%#P8x`=8*~ZMMlW*fL)OUuv7Nm2! zlhzF2Pq#PXdpy#pj1eW3PEaCrD=xj*Qv1I4?~Us%Qne=q?hw{|+osWp->?LZfI&uj zbzGf7Z;6DOzNeK-kKK3+R*2(}S8?O|-aN1rt^|54RQF8Hu}GR8TL!<U)io+V@=Uwu znhYyd^AVX>D56?S_np%-^iSXN`5!X=x%rUTk^b5FA}>crn@0nn)jGD5!)*O=a)J$Y z7>zjGu2{!ep_0A~>hCvu7Wr}{Sx~pCJz+-0<gjx7Z?_-E^R|r*kr9p`Lgr1TrHmPJ z*;{z4pyh~Ua#52+=*h%libT*D8>dfVrg!HXC#D0trSRi{zGRW#PbI}Rx8xiQJ9%C# z(?)$0cbNSpV<VX8A(osv#3|$TryKmoV{KloP7486;fN~}Yby(xZ`I&n6VOo#ttnh0 zE|qjOR|?acEQ1ffzWlGmohn~bqs}x?)o2W%<oikr3Z3VE9<>OR85!d^0?BRzh3y?T z*yWk`To6u11pEG9nw(4L`Lvj+6exiUkvA{*ui1ZF{;e{|)b+gQ?0fMraz2ET@)Z^i zsnX`Vyf0eAg-Rmpif?ZfgM)Ux+fqvS<zlD;p=OY`&wM6!gwSF<wuY6%CK+pSc8B-y z!nWV?1#!S$Q%j6~kI|RMfQS##q)4>`jR)c1u5pzj_(c8`i8Ehx!O;XlQ*c+9JHgJK z55X*_(M0EQ6j|b*o+sbg(JT~{YN!ZkJ%?Xd`xVNw;R>An5OAfiKMgrZE|aKD^gsOj z9iTA-80Zq(^tl-5P9>%jYn20&oVq7M(vXRy<=$xi(NW3~GZblvh0rr)fsz2L+T%F! zvMHG~p(^oY&Nnrgy;gms!^_-T4WY)%!HPg-ne5Zh>a)4rZ^Ko5y?T6x|Af2{-#t^; zps`DA4POoXB1F7brEm<QA`|Xbelwi{XQGti0a;<KjO!cRKQC7?<4)n24}1$7AG8@? zi@IvleMFR>5QX{n=GDDr8`-v$lcwEKK8H1FU7D^B>Gx!}?17Wn-ClzUaXEZGJw}Oj z2}}p%Ast#y5t(ps@+@&dg=?|ssD4RxA*#Z%aIvqp#GxSt;i6Q@&DJHq^!{V&utO(( z=M?VxGsqYpavG{4P8Y#N3K5wP`>kVlrsdm=ZfMmQ?$0D(F$E%zk|jlGN*g_i`7d?< z;3<-9s03!e@kfl-;Ke*FU8GFdgwH%g+)OBZYz;6tIDY`SmI{R9vZNT3hFZ|OU@O9A zqC^^Yz~8%Zr`Y}pBKhn&DY?$AMk|}u&?=sGA*ogsG;k^GLtmk!Lc|g#{H#Q@asRn0 zf6@(gJ@Bb9So-yUO;?P|idv+F4elzCYBjADLedW!mZk7Y?}naD11u_Hn{=P3akJU1 z)^M3KKEig21ug#aPE@pLmRv!(Wto$%(;&r_7A8&{wl3^nrn0-W^KbS1+LvJS6Y8_f zxJk_fjpO#_X<~h;w0o)#`&TMUMZIIP6PU*!DRj{MLeRlzg8lZ8q~sMK4JX+LiIu*O zIOj%aq(FU*J#dI{QfH~i4tW=oH#$EjlSFo$_Z)xqC6`zm5<X*FRcPFZBnT;Zl06zg zVe22pbz<vm)#($QM?C3WBh-wXi1Lr-5%=Y(9AV|#3n6cEN+@96mO+wkiM*t&PM~`` zP}_;{eWxAUu&&2%>AtUJ?e6D{`KizBfFC&b{$jjUg+Ix`h}x0_*jGjknXYD_Dlzt! zOwzb>wFGV<!zZQ1X36H$Yk2r-qo(Ik79VRXqxXbij*lWwu!6;VPb_9JJh{-C9NunB z1u)5G?AMSS1ATGWY<NOgAqkg9v3RL*bCk@Fr9h2`G&bsVZIGT5%UrtS?{GOB@#%P* z&b=l2W_t}!+vOTwYY^Du=xMM3H__D33EjPO^aln8TpO!|&O8|;pT>XQFMwVgVmtE# zyY7bkH`?4U{FXmsKDuH=HN~r&T;zX=b$CG#zoYS&*KPh&EQ&|ByHqXnBP!i9lf)Bi zL#o~P-h6`c_1P?{mh50WHY=nl+V(B6RmaWOL%xHts7c_v@X@`Gez#a<nZTXDz{$5S z1nRWCU0<VqVc`5bu>Z5z#De=`CB#hcX#f}_8UWa5#x^0|C75W9L7$_==!QeS^mSTK zfnDsoqEK^7$o~P&{PWl3ZK<J2@~);d*6Hz{Y!!W5?eBvxsFUpFsnPPiEuCux{0hIz z^big^5pckw9IGzgt!?w-`A(ieVAApmQYJJ#cRtX^;K{nWGngpmZLxV%a?p8?!~HXS zeVt}7V!@V@o(52>0~>72Eq^*=y9x1gHGXT3B~Gs&x2ALW{8M{mHf?0kQVOYndiSeK z8I`$4J}T3}i7EYlJGyzAl%-u`Mi6^<qcU=pZHZS(Jp@(0ELoE=YsQri#y4d(ufLL* z=K3U^VM~oFi=IL-3NIqndO^j0bE`i4l^eer2$`?`@;9c(&zoyGj}=#4`xUMcMS#6g z!qhiq)#rnMheacHR@U>>X8|K^@T@WJkpjJY3O^oX5mC^5yNsgrcQ?<DZ1y*zE~vV8 zc8^#H`04yoMoMU~QImI%;)FtH4^*GY7<Wo&ET1dseVl1fj~3gKkMAiN#L5Ei!qG!s zpNDllEvb&<NBxHsxpqeSYoZO0CayT19E_i}6u?S4owoO9e^oewxj`4?Rfz%`CmThO z*OlxOK{1HrM`A5(e$L-`+qHlo)5lOP6AWX^W**|dltk1EgEED}a<A#ZLm~kq1E^WT zCl)+41E3_lF=GX2ZcGPs|K}O%nVk8sVppFkZXd*aC?wrH2ID;4hyb!$WKL?zAAOke z)x8QwsidJE;xq$o15Vez+@l)f!QrAv1%jeukZa(+aX;_|&`bG)gNC?c5wAc?o5X+* z8@?QjXwgXGfk#V1k4<*H#pHxdl3}>zxB>GOg$e9NTNH{RBO~dkm4Y~)48an*T<x*Z zQ3Ij6uJs_SD0mh!gA29Sx54jHeE8Wf4joB!I9_=KIvyve3NLNJH2)y*A{|zPF;UIi zPhZAJ?&5}k`+;^X3LT*HOOD3k##jJ%qcF%GVPL%t-cNpCRevoiFYYJWa7&j&MX3m+ ziB3x?%rfyuuPrG;6B#tH>09ooi3-_7{_xG%Ka%&zJvb-qH-1?*6}v$LB<ge|8F=9; z6Zy>?D-?Gtviv1EJJZ8#dE<C=uwz&hD_d|*e`q=@H!_Hj37ofzcY;A{f`Tl$9G<{F z_zezR#RxUvV+ixX6@jx39V)n}Opd5AI6EmFvQc9^64f~fiNE~ebQsNlBJ@lokUHg; zF=020g^HFHW*{eM{~C(itDs0T<hcS*dU#n4y$NuhiF7Hk#p`+%_BF+cdg5HmnY*7e zYA7#tt(Yq=!;pog-)yfC?<j1s5%e4c=c6rKz28%IhF4>hR!l;}HHp`nl!$IxGTgH) zq5wu@nmA#_U4q+-Hu7a`s_3!ACt&D>ypnNxnVWZ-(|0p-`KE33pJl)#bj6}(Cc^&- zZ-PrBa0;*c6J<WWT5$NW(tcZq<bUa@fB6^cdL%;W+*xmM`(Le2O+^u`kgqVCg|*cm zv8<Zc@qt_3TldJ|Yu_XSu->>0?sl^><kWaQ`$BVuEC(cq?TC<{(nxC#7!>KZdcfpG zw0X#{bPx&KHFn}X<NaQ+FoYlunG`fK(P`w>6WC^%j7FJ~f5LokRFcDI<n_i!c3W)& zs*WR8xDyHR`>OhH6i^%OZ*j2I6LSjd;kxh}D|k@8#od29<>v7Ezw?zrLhj$4i2ms~ z;Jgz`*)J%Zz*va<MjISCsiXTjS7hZg?hyHz^8atZk{d4~k{~vcuBS*#Mll*lY2!lm zzjC;B7Z!N^VAQ?mpc>hj%99=(@9}wYie9oLXf{n?44>zKTP7Ddb@nvf2w^wG^dj6@ z${%ExQ!9#(zdvsDn<SZ6p4Ayv5fjnqm!M*j&aLx>+VN~6KD&fn_+8eW;DlMQX<fqX zydDs7zvBlFotZpzobLVYGSTsx&5w~Ha8OWHxQl*fBvgsVb*nN8rq76Of|VX$`7!qo zgY;kFI;WB7fZuaDCa$Q6(e{5Y(Q+WSunS^hgmPmmPgMBlR*knYswl<m+Rup*e;ZZK zXx$ny<^wW|*P6FNW^DE-<!H%x=-ekQT#5nU!X^9Q%!=*^d`uqx?=kt}BOk>bD$@FV zc=1vD^uET>BG~A;Q{A*e+9!eI3G7-0v7wajKCVYH4~;?$AaADWgWow1L>%6CJj{l- zIjY4M0=C~hP59SKnnKY}hhi%!N(-n7V+QzCs7Q%U9u}PtBOV%mS}J>_V`!I{r=w1% zR7+_Cp_KEiVXb2__!Qp~5Px9zP9##8t}q;*%OCwZONcf*PGI9dRhHFa43&zjn-Abf zip>r(-K45?q(?_D{HhO)u0u5lP|k81jcxtw{Wt*ewSiWra`)y!lTq0GmOwsxu;WLo z$uGX$<dp_XsC6QG$;_~wge1Ba=D(`>oH~#fF!A)uOq<TwHuzi2^NYz3`mAm{<lysm zjfv6o8axBV<Q`ap56Yl%?zF}*@ZhBnZ<>V~j%@-Ho)4wX5y6oF9QxxXjc7V!7`nbD zrQ8&m_KN9Jc7;km|59dc=Ma$51~!5U-CNmVhOsX880V8-T`4gAu9=#B)HON-sx0L1 za~V9c>%2tR_U?fS2ob@i+~iG2K3izBMb0TNa=dZr#KV|efYZ((KmYV9Ze^Vdha12o zK$_if|8ws_uyyaJG^zKklj!daL~}xXc*r{N-lJ9tK<fCn_ZXdgR|Fa(pE0?M*Pcc$ za4R>whCz0klWi8W*(+=)BzE{MIoozC883<v)qd-(xIz&8F!zX?qR3HzO|C>ab9?ZB z5UN8_$8!mbpt8r-x54yCLG0#kv%kj$83H`1qt9KPN;}LO=K6X`mFKeiWL6#9R-uu* zhfm|YZ5rTKpWF}Z)##zg9_M1#gMzAw>X0n4J!)&U($Zru#oYWrf>eP-z6lo`vHUxB zc}<wUE)w&jyq|JpW23v6I$=|spbB;e=h;&1H!-?;>MQgKi_b(+a}}()Qqy4D<9EFb zu)ViCuQyD&%q7KJ74>iTmU0yPUNd5YuD?|y)H+iTvNC#1MmDtZFf;4L(j8tHBu$** z%g38_pMYY{eNaD*;LG2-DZG#s!z=qLlA22YLXi0Nj*>k?xH@X-47CIki8f0Dk~9Ap zR}t-Tte!+fntG*L(rDBtKI-(;A~T6u!khlwF{nw%xt0GD`{-pnQ>lYf?lK1Ie-GZS zPV(YiG8NjvMB>fQ@sc_*)*LSuMEm<EJ<n>m$Kum~Ku)vq=<rl_u$QEkncv=g_!XCs zhFe@lbIb2YW<q2Qxy7~@r%oZi_t}^h#&(aRV*M3&p|3KWq;qGme>7wo!KID?<>gV~ zHh2%`{y%&8#3AI&^RUjU!aYmCiFkeOeR6KR_MK!OetV`RjWptGRludPL_xm-(RnEU zJ30)HEnDRvrEaHCOquMVgs|JYUZ-y41@quEqcr;Sp28985)1h1ywg0~E)<x%p?+%r z)6RP#uC9NYVZ7A$b&$FySN(ajI*8T?Teb_u&qsITlg8^P*aqH`S=L%XhUW=JZ6K98 zrTbp9s*-<etS+I^bOKIV@<p7fD^a3xNL~u^6;(|z<e<4=KZGbD*~Yf4mH4f1UOwMg z1hAp*dB4x1yha0K!%2sAPLhj%@-H+i%QdLXo;y>Uja4DvCeg-d^sYdcvr_d<FN*Di zG3}FYIx!iP@;R-pZFF0Y!cV1Lfxh(HhWu@;i!gkQnlh_@{=GvOEB-pl6qiAip_V1J zqSiY<T|-7wl_I)b>Mh`M%Ve!Rn4(U}9>P94ixiEI%_UlO00LzETWN5>M+mEiKu9U@ zIGGq>16Ev|+3)ivJsP%+FJ?NU-2Wte)Un60p_5p-!vC6eq+D#|VFB12SCPY`1I91u z44?Oc`t3VT7C3km^qQbs^ez#}1p`Zyc#zgtBDmo}$@=BUkgH@s?2X;Ihn=K{9dP$& zANVx9UT1y24DVQh6Nwfm_|Er@B_fv;y>?BY_)H24ph#bb^Q@*g0SDgi9a!Lcx0da! zSwAke$7N-!rP_A~QPsdp9(O2R;%g<a9)#z+ebV9!9`K>Z)yVFnK*XurWrU38OXl;c zFA&>qa%aJk7LIDsvm4<!#jQAmY)C%fRlAP`XTH?dX4)+G%yaE#Ll44yHjs7!x+fFc z8UfWsMJqrxaIrAzK<An(lZW*Lx=AQ{+uH58^Ep-M$AJm8RrLo}=UGf}6ZjKNpE*6u zFRzNnO&z!sc8sRcQW}*BHCma$|M?GMWt_3EhA*}@sn1;C<D|8_&j!i>Z9mTIdUS(Z zq#X0Ykt{HO$Y_@9=t?u`bH{&=2JiT!(|A>as=$_E6{BRBo&*?2)>%X6DG5)%Po#5i z;B;Tt)+62H{aAxjKR2Vvc&gJj7$N7d>6l97%EzgFL%tEI4WTRD0||}<#Hh@!q=O>? zX>P+#AE71*WxzQ*^%e}X(5SqxPejUVlZ^F~u(i`SFr@}pj<XO$5ORRv!bV2=`A2GY z9I~pY7DyU)E@-MG9Qk<`tC3tUKY~d{PshN9PwPjE7p0?VP!Ip6hq!>A>1;@PTcnh` zXdP_5>TilsKEHeIf&H&;sVxYK?fpVTzA=_SeE~(H4jh23CwToH_*`G#H5oIyz2Wy? zris+BHm3P$4nIxb$65(Yt;~D}flK9S^#3iDr%jf1H9D^znFUS!?xqv#N>4TYn;Z{E z)E6c;>VM1xd*TG+AU*tjemtlS`uk&4^Rrc|2h1wWdgINs_KCYOI^`$4%d0<Nx9bjU zxY9tS)OthA%40(8<Yh}NbC*Ew1*~a@r_L|q-4hMS21RTg*m}A#LVWX5*1Y!kIub44 zQiN7Hh1V1-_lw)A(dtXY#qX)PBb-rS?cEsoKlv}csFFfon_qh?q{ltHCbjQj5oD#| z3OS~}{E4~pLd*1nBQosoV=j7C43&dFeRx~@{$sSCT@Mbw+5h=RyF6AWWP@d1&8l($ z&s;aOLe7rxGATpDE}aZW`RWr=3B)c~9n5m14CIpomt6-=t9QVz8VP&tU#JybBmhG` zS3#Vw2o4!53LM;Lp0hgP1*NnM&9099<TeZ@ENoQiIL_Z^Te2F5zO&8VLZoX+PSVSs zXup}B6$Pmc%ew7I8ll7&pX||9&ReMvpkt#bzm?g<-Rt0Z6v2pVRuSeBU;y+%tIYp^ z^BI>Q*LW;9`V@NYYR;#xD<FQSY-XJG2GqgOW6DmD=MP_sUk--MtG8t51>5s6Ja%aB zsIV8Dh;~rj75Py!R1j=2kcs$;crFh;hTV@Q7L)#GY>9?}GzrkdX8}*ZqYCg3!1X(2 zH+t^1f97+(_OX(yl6Fy-TnKgBf|onm^QAQ249NmE^%N=lemeBfJ3wN1D_Cr!$9-vV zhKFhOm8^u~$2G*1Ub4o8j1A!bV(KlU;_8A$TL|v%t^pc%cXxMpXfy;1?(P!Y-5U?? z?(Q0bI{|{e{?55~y!)$v_2@Br?Nw{nthuUGF2X1=GHBk0HW%?(_p<L!EQP<1)U1+; zz}!3J`_X+}4~K!8XBkFi_0Ry{`y|PI4C31Mx}eBjs?{hh^i{xt;nDAy15=AD{Qrb1 zjU2<NoILrHUr<%s?F#oAgj%yep;PdLf*R3Ngs{%3|5D+z>;>AC0f&4nhIG9<Dr0xd zhGfO)BU;|nqY2nqK8G*Dt}!gZ-A_vdyWM|gOcXaG?TYBs>Kp=#tp1u$ez|J6^P$Eq zI0wf=6@=hQZ5U(Ev}VMgKK(%ugzPhi&lc8wVk0-{(-lXXw+sKZPu>?$$@_>lGXh_f zna3530#fNN&Y(qb5nD8Fl>V8l0uGKRQpx_83hSqEHG7G#A?HkMyvOFqI~x4+t}K^! z3|w$o0zl2D!|R7(p>ApK%A+mk%g6qoCh(=R%yPdM{(rpuKQ4LMMOYaL?MOrm@AKLb zcoGo!dS#%2_5q2?RP-@H9L!daoeyA+c29BjC?>_r{rmhMxZCD0NWt&hd~U{^mIE9v z^brY4<Tv0{VcN7PbNrz?`%~zWyaVHXadW#^u~jbNe;1nYNAPH~#DL!hz3oT4T-a0V z%~K=Q*X~R3Fc7)tZs7IMln1zFjX?FMh>yt^Cq1=XqQk)c^#c5_oV02dMQpCJIYqQt z8ImRsQu*F0@M`(DLMdx=DBCEU8XRcT)a(fC69w#m4L_fDq1k)UMbSkpiAC~|g4y4b zPajpi_PM=Pk2{|EZZF1!?Z=(FJ-~W>;Bu9gR`p7qQRhVJ5Z#D~`B|*R11vrjMEjRm zKd<YM`f@mK;n1;1B9X0|<6ZsWdQ~>F$-wkg)^3UVjQ0AISuDIN;q}-3J!+)g3Q|nY z(%*X#%b8d!jEJDj3iV5VKXDJ^0_$yziht4C^KqWh1sB?N>M4oT<{?Uw5rOj;rhvxh zYTSs1#wF`B6jHBl7r37zj9eIs1qm;}D;$`@R>L)doi9VMf1FG_ABd;+P2H+ua2<{p z^f;AcHDXs9VRyIg06vmLxaaf)s`L#gjZ@8lae2d^B(}{aP=7RW1V`>16Ly-dkLeeA zJ`az8JANDDj8d#2aO5>jN<Jp)wV-cCVVKrJjbbYn$*m=fS$OL>8VEq<g}1elffn?% z7DtBqv63kJPfWES<CieNPt2GB#zteUL3W^m(zp4o(uOR%G4Z7aMTtUcdipYKNqVgP zJUo0W>KexADApgKZX_2J<R(u4{JOS0-fL%OL;tmeEwcb!2ba~lub}U^#R(pc%?57_ zM0`F$G`HHDf~@Vs3Wzlfy$<8?&c?mB8hAB*lcTG|TtuCX5mXTco9zvcrmqskK(wix z;`?S2jRyoPlolJiREyP}-<B6DVv0UnIVNPsMS7tvD{vrE2&nhtp3bvof~Pmz_YhtZ z<YGZ>sy?Cy85?^WtF|R%2$p<D$h`=FrTJw2xsFJng!|`X5e|5^u&)<X&SIz|)PCx~ z2>kc#c|Z399@8Zn`#>4rcHFDfqS&%j!~KraYNWRMF7%8o+<z;l7Kh?+_*J$PRPzxT z>TY{cn%m)~c+-_`5kL7}s&Qq;v3Jd;VzH2PWwzL0?(Vvx5on@^SGN=n&^1FdR@wk< zEHpF7KmI0#tYu;5Umk{F_@0ECB2QYZ**apvLin!?WZJhm268i&(APWu)>%l+v>pKb zLarPNXM{}F>kY46YuTr3s6u!4x*a+G^bLTr9&gyi+xawSnQKU4Dr-Prv7V-?T+#g% zAKuU#-cdISP2u1?QN)yZ5nUO|g||zOxt|<{OU-$@HXvi6Pl&S055REtKx#V(OBD^1 z%wVe}V`&H;>-O+I1i9_FpAE1W{rdC!G#jMB?~yyq3~gIyWdPIYur}1-4N&wj#K4gt zr?eazZ@Tium$@ME`n%DV&ztW|Q-Jm-dK<Uhf5eNkthTu!t1VeHiTWy9YW>MYSK$eJ z0RH;<Tqc5U5G99Iz+&UQ=X*cg=<Z7kQaWm#4}@&js|?zK>AZ=tKM$#h>>&{-$X#G` zcW-4;7*642G#rip4eP2IkN99`qicU{J;S1ZCP(J|x3Xlbp@A$+0rDrqUyvpYyEL^J zMk*4EH$|q2N~cabUj5Lqh3)}Se&=mm)Mp?+<9Lc1Y={)TdE}Baw0SZWov=!znDPX3 za*7Itps;0$9=3VzVtOyT2_ixf;zVL49x#<bbs5!b{%Z+h=WTixazC>apye{MG%1Ey z$s0mC2U7mfbUZ1wpHMgkGi6>Q%Nk=XM0YhEb>K<f^?WI$y!-nBvM)*0NO+nvc<GVG zKnt*w`xCSI^Qtm?qj~IG1ZnTCo<|kQki{kz1nknjzMAp%y=-CbH%dezzB8EZ*w47y z5!FecD~UX1s_`nV6l7F)CZ=AES5iI1Q*>S90jFdD1VwS8y4PK1@=r3lbpiAEXiMqB zD0w6un28I;;A9vzHWnET4;r~?uSr7&=;-j$8NGPyieJs+l#`rWE7;VCZIE9Aon_7L zbHH2i`Dzi@_wR<guH-`s3R+$7r|ZQ`&|g??3Jb@U>Jeu{h#QIfkp`~Ux(HYGK}en2 z_c^|pfY)|DmTca8n5#F8dNh*wO)s+zw-FR#mtO`dlivEWeHqGpMyCZsOA+|KRIQea zb+amZ4c9@d{zq$*^}p=SgbZ3KPetBXCdb85p)@aK2Q&-viJxv{Te^R|<M(^jila1? zq>IkRGMI!Rn<!2`CIChV+xQ=QKL9ay?|2hthq<f*XGU-%B*+qEf<CwH**piEY&iv? zc3L?Ye|~F*wWO;dl$0vdIwV2k1>Lt$kVTyNA7#1cto2hG__j)H^S;qgk#i~%x1);g zkinwW^dxb9*jLWW{jsQiIZjkd3cCxIhHLM`gyUM&9!}ooJcZDkN2H)Ing5DGGnjak z-H=fQLAPbWk0|L&2XiWgf|cE%8adm|XgxNDgg|8k{w!URM8sq-a;aU+FrqXe3=AL? z7mW!I4Lrk%m>tTYH~?G-g^g&})rk#SZ?519Y_Yyhj{RORFlPECnofXDw!VEomm;Fy zdq1{$^%kL^)BXAYQ!ireaxHP|L>Ed4S{-6o{K2^2DCVJ`)of;UfvyWxXFofEX-a{_ z#O>%6uV_2AY`1B3j^p7tiz8mdC7K?E(xInk{;I->U5nY}BBm^rY@--GSJ70g`Gx*l zQR|#0c<h;NsL?QkLIVz!Zd)y6I4q<7rq5d+Fg`2HFZp)ksDQ|6)EvsUIkKGLv9eLm zV|g`}B#14~*HiEgNf73cW5L4z+i^LwVSJphF(;&oz0=jO2(LgbL`3g4#$tN63Y*_Z zGraf`VXPcBhhhVaNamNzCLkH-)qd(_%$8{7<3%l|lDGu31Z`!avSQ~a3|UTtz_H!> zhih%5-8+x@tdz||w%kH>`)aVo&3;BU)%NS$X|MM)<Au}1yfafjNbD!mPLQ9^Ts;M1 zX^0}K*#|TBY9mL%465ZQ&cY2{9Fr&;hy%DdsJs3gN9ORHvWRzJXasKP!56C3bgMi{ z&o}Zpf4$sK;eJ-{TG(?)c|pQx<{7sDF;^K*-TyUq$VZI=SoM3jL-CNQ9Ueeyrly81 zi$W2!KsS*BqZVDi&@XSP2Y1w6L7py-sAdl`O;-fWN0|TBKO+vU_|Oc28Er|uZb9_2 zqRzt)VnPD21hQ$|x`z{)ym_ei2&rrXFPeX<<V3v-_4sf9$O#JLOIPw=8WfOhHg44d zR;+x7jO}BY^vn2mQLege5C20SM`Kq|4KM$AtK+I)!^UBxGT2M#p)rcXu$vVb{zHbS zM6=t2z_CyJFNEP`icx5T+;7V|?{*O-CNtA!3|NW*d|mli+pEx<)}?8SvmtvGOKDmL z_}F(k1*W(}y2NjGSMs<wn#PD3Ytx5f1m6^CvbCX8+2o2a=onUH$qR<N)-=gZH-w#t zIWB1qZMr^A`g^-w_>?Vrord45Jq1VFqMc@w*H8@}=^c-MTXE0|kj<e_D|h{kS21aR zyZk!kGo2K|LYSZtx}|$MqI&JU_6@#MnzR?=+&fRZrHD2ff$fq2>GA#8_>Wy~-0#s0 zN%TP&{hJi}IpJO*L3PJ^<5x;FK<Toq$ghgT{TRDS3VkOj#h4a2-A9d)W~X*nvl24T ztq$@Fxw{?u7}U3&4L57&VeTgU!iF*2AUXjZZ$~>^3(g-?P|oe2$2UZW&c6<ue%1_W zBj4x<`c3)G5o!gS%MIxs>z{8`{u?($74-hTI%5%|vCJqBk|x%*WK^Y?(&0c^ml};X zP;v|&zKmc*oDO2qLSaYpz*Rj}a8x3rg*oDLk#YN_h!o3)34c+-=%|G|1SA?N%Th3Q zs3TUwUo1K`pYy&<3b__`@82~(NuM$9Jn;7|j1@(4ob7<!EaV!%fR(28JO^sum@$=E z+5FmLVr{+aH2r9;H*UIO>Fs`~1^<(OOZ2YX9ZyzsHoghlR4|jH6llCk$|turOInMi zl;~fHj~F%sp>Yc<r)QuU_6P(ffa;-57vbdA3Fp%J9pW&)7sKr+Qh-<b<Y;0A3u_p9 zgg}s?xIHuOBI6Ycs-i82E#I2bi)Va(j7qGl@=>bZlW8H+4s~O<-u-!13joh-s2QIJ z<S6jRpuu3<6YsLmcM@(^|1C?dDJLJ{CG;eVkl*d!9yj^}$)Y|Qdtk?H>ukn~Wjt%& zUU|OaCS2UCgZxIrC%d)#e6}c-_+$BRA~r#(5w_viy7n7Vg%?m8b#LXqP06$WKa|m# ze!D_lOCNBsaJ&8>SlKban0xvsLskR}spJ8~x8SiEh$3-73~eAVQBnCgHCn?vX^S5a z?%@3~<yYavASU#bsjHe%zx-C~=d>5trFxr9^%^-yElJL+g+a=`0DjLWQL2!@5i1p@ z5KyVt^MtnVDZ()sm-8Ez6}5xF%Sy$ilIB9l(9|58KbOd^7?@Dcw$l^SBGk4mdNhkF z(yNGSjeEWb`AXjP)AyaNNyNSXZZM$vbc9O)W7-@jpY!cJ?>hQa4;Z&$h(jA>9v-^c zpxZ;hT5`i5%)VGh4RAHW?E_>EShB^sJ5FWqN8u=7G_ZX!hr0L?swMqQ`v4PR`rfr0 zJYFC7Tb$~1Cf6t#hpD#CAtGFkFt2fF=ThI!fzJWdqBE_NWg>18iTFO?NxT{+6|tt8 zi^L!hZ;ulyJL~?SXA?`aFq4eEiv_Kw)g{NQo+w$s3hNsMS^vBAQB4hD2WcKNGfylv zHP>jfC9#1GRUc_%O5y9wTMB*5$5YEy%jDbi+Dac2cc15|gY(6<L+A!M(FR90=bAe? zRBR=Y@2%^25s68s)BEe}(8Bt!dR|}LRud05MzaY1%A<+^ub%L`;#X2l0;B3O2BH2; z4#-66*d0lctzMd?F`>=%P}O`P)-o&naal|q-`3cW?fFU&U|{}3YBsqx;Z91W6+U0e z;urg?Z88RgodubhBuQdyDg1L4w!r$wu=k|@^^o_gA|Z0Va;K0%d$E9D=ZmFlMrjI~ z@YjWk^0=$91m3d_1R!qI<lhiYVsp(%L=|at3hlP~V%QU?&OZSCQ}T+;Gg(OATp0WH zQrpamK1x(W98`WU6d=dnFvK!)tF&;j8vBENXtHU{Y%yY!Eo))!h>+5zXKI3SkwaD1 z3kTI6JyYORo%=_SrF&)CGdH8&m50IqBy>xb>2jr_G|U<(#g>RfkPrv!EeORXRPDn_ zVQaahHuY~$*LC7KfoRLOQE_OP?K3NW!mtsCoe%(heWP_$loU=uE6@%!d20^2NecFg zZ|Of8!?21PM-CRLa3}Gg9wh4F2Sn(Xns)%`q%xP1z9+isK<QgjF!h%(wmk)8FB+DX zglPF2V|W}@W7C0JKxdq=6U1GB%ZC^Iz|w@ztnS0p&zdZkb6N+4PTx%nU7SFVFj(sl zc!;-&vI_jLbzZG6mInYdhuzy^ceFfCzx&UQZzwaC{Y!`*O1Cf1au#IH5okim2ch&< zI-&C!+9)Jugn~OLx4ZUmedr~#J-_Q1*G);ezRh}YQ%u>a#MoKkHgkwYN3qmEi#q&k zP5EQ-)Em|2)ZsYD+;uC~9H?F(>B7W~V4ZFzM|2@)oP5hhpp(6(*LtaTVflIlT-om@ zW`@a|?joinIOh4DgV>Yhq%jgEUtT5aT0UMQ>!KaDsbhRU!az;s@(s~%>GHf{9-O^T z1QtoHD2B9-pP&yOMt*;w<Tk@Y;ZO@2=3tk@K@}&Q20Iep=(1BgV2Jp+43`fbg9A1W z2IB#zVv8==e*{$YZba&JLuT{eS5j3>J?S~W0`-aSYW%p{6crp7W8*SJiEtNER~0p1 zH`x~zy_)D~oC-7>*=4LO861>f2SrRTN9*!G5vyYxa261tz$0jH<+~(305NZ5y_!m$ zt<4>cB!slhD6MW*b`Jj>EdDJwX?(LiUf}w|W+&-&f(LShd-RIYWKSpgirtT8dwe$M zL-8xQuH`sDzW9ZX)Fn9vn;L^IGo7D^hDM{{k2y31X4jDuY*YsM#BivuJj!fUPxjzH zs)F}yBpqV!O(Vf660@&R8#Tb6(<<53WnZK*iOQu1yr4%OuH~SzLDr{6<;-4xlbX<# z4<RsK2r7=JND+@}%ZHzUm|L2cwu;&7{5#w}v9dTHJWEE>{}{V2Cz4Sf)u^DH3LIqF zDN5D{E%foEUmPv?!h*MHU0)VtX7qwRBadfy!=bQUV!=f-_HTi1d--XuvH$|Bk(g!p z8~x`cY?CJQubZ>Rjfh|cMhq_-vZGQTZ>}RFUH@MqZHLUU<<h<6CxQ(2+b`3pqhuOa z)jVN>?}(rR9t36o|0jT-xkxBw{M^fIY0PFXrKPBhH{#Tx;$?1p2Bb_7<2dtMiWY$M z6N%*rh7gU}3MG|MbpHA`NK6j7gy^OEC4Bdpdw(AWfJk2#hQXr|hya#zwDTP`Mvx6A z5>JdNV_FA9td6>hnaH;2n>m`+8)=p_PGhp;6U1rrbWIF>+2)e1y0B5Jqn|H>DBy8g z1l*N7HkclbM^65^btD_cwuw>#&FKUi;olScinde!H+MhHle1u!v~tXjrf0X?=i26* zv$-^hlG=~1E390ZgeLfIis|;>hNf8zWMSL;JD{LZP1j-&CRUX9aBNsk_y+*%(sL;+ zgWNNn5BI>b>c|UxT(Kh}6Q8mJpfq}fJ!-GN)sW~ju#L>5IqQxDrn&2qa_pxo>9#(+ z6x&!liNv!|^m#bR*vkO<R_c*#!T5mVJLKFQOb4ME4qQRjfnMx?>+7LT_2giKH4yvw zJ0smVX1+LI%Jm%mCL9mTJd$$9bHu!zkQdTdA^tgq4XV!9<IiQHb`jUqpLX!+xz=xu zq{|#8)e4`7vF`t{RqD)ZjcSuzsKpK{M&-B_JXnsC2KRN95vJ5yRcd#6oj?j2ul*tD z`dlh}_6W7*dTm?=4$@(+hId*X8f8;#)KR+TnkiuXmWZME#)UtJm+~Kgt162eW{d)G znn`~{!TRBpoSwj)G1%V$>>3-9{r+ABx3crZ>#@*R5uwHQ4A>=UD7C7c$ugmt!*TWb zhizaCaJLO%{5uuOgQRu<D^CM(tFfS_5rnc24e%Y0(NgrX9bVrG_`VUmD`=kX?7&=R zwJI&S&stcDb2+DPy^x+N)gec&tw>Yz%XU!1d;y;JA!e;r>#`(UwcwEj11HogOL<q* zd|K_hLiDwrCRubNFgfN=9e7RW0RI!EQ_u=7#^f{choY=xJY1=M4#}wT!os6feSTlo zXS6r!I#MG|2!3vIa$3>1cB6vMl!HJv=N!Hid4*9n{r3l@E#Pfa;VG9h5{sC1N@zCM z_4H5NJN(#y{G}~Z#FzLwp^7HIzNYwa^5}8x!kV&3()Vk|f)TZLgL69P{|U{08Rq(v zFxj~4AI*!a*WYapNqNfmiP)rL39H!3TE1!vM^m<M+boc4!|Z8YM?qx=G~7Wlx(xhV zec+2!xU64ST}mk<cZh{}9+Xpg9)UwU_0k;mE(31?zI4%coY#nh6VQoUmoxI9$?0#7 za>8xv@(i51Y^SdOR{Jw&t7;T0!sM*j$)xEDe#zHLls>}RipOe#SRy%+2K)ksKaE;S z9@Bdks2i(<v$#}-7T5<AwF3}nA#EQ(M`mY^(%lEUE}97X)2`?TTx?lAoBmEEc-m_H z5c4gGEi=Cy&8w}UDCDzKXxc>9i~6cG3D|VxV6&S5a_C6dXWG$r1>wobD^94JN^3W! z>d~Pv3^2fW^*uKeD`52W-hEgE{J>KVeCb<vN~$&KIT=8>c0>Q#-cVjdqCcsq-^r=z z=x;SDr(R111hNoXno`7pbW`FX?iXP5n{8CIqY)n#SPXxq>bhZXCm;A^u>%1P;E*l( z9%xN?QxvWiE-t68u1FuLPCMWydcNDqRyGV~|5hdeQ#m3+Znq-Fpg$FxR~<jr{hmj# zZ05}^#j-&=N)Y(KZyO89E1DK&0z(`ut+)N$X>B!r=$qOWCh<@CQ940QA=R$c<O4o^ z<<w<X7>WT9;gErTXz4ru=8oTujZJcWhg*15HXGfEZaz-~AmpVRH(RJxr&#;beag(N zJd7}O<kwP?^gHs*HYFzmouvLwRS;C7ujQ~^I*G_kz4T;P4eTZ5{F*aTJRfY`d*8y2 z4#)~e8UUIQ%SRrv><W5c9W(2J2VPz~u{K55s4@SpIQ*$!fXbb30sUP#HDsJ;YTbPB zO#%1nc_9txIzazu*&v&3g4GV1eaE@tbgSxG28NxN)iZDT56~1N-&pmyzpS;-L00@i zSn{nzNP$*SlAi`}A=s~iVuruZxf)vD>M+G8(+gW#!bFJ{L=D$8q#-Y2W5$K3(YErV zazgo#@gKIxOn(emRkPunoib_@n%2^OwdO-P$@aCCS;2!isqf;Fry-)v@J2~-!2;4u zAOfLLL={l7$K_!H%@Rg%(!%s5w#{%+$sYDK`&Dv!iLtJ|46Q`ZN%eDQU6JDWmDrlQ zBjq-dIhRW4lX+W5sO6H>Xd7duXCV>%C>Vg7#cZ{i%9mx@lzD*2t>&(Lq`&#z=BF3= z0l-s`NcZbkB9DD~oRkWFo*3?s$Hg~5Ht|XAGGyNkG#|dI_LUcMzQdly?_B}29buVv zIZR(Z%(9qm%;3hNVHJC(hy(2VX1^S&5p`QR9F8A<2J(pOTj!O4tu|}Ha!?&O%N)hl zCE}`&LUd(Z`Ni>ceTAN#Usu{KBje#&0x(Rw{)YbJowC}R_B8Br5%6H@t1Q;FoD{az z;S#5*@k@s$U?Ik6?HrP+^zs^g`jSEU(bR-%k;J6^PuQ8!LoZ6@-FT1{Ic$Fx%|Kfe zrbysUB16g$Lv<8~xJ5D}vAC5|EVz*PHk9K0`_T8;bSf<-U3sB%*1n%v(6vJfI<}bZ zvZ=R>`W|5+sOpM`FzD>HoITjA@EF;WiZ(MtB64O>9jl~V&obrALlO6f&g<V#4}4rv zNp?`lImI>l(R4VI9rl{EB?*B(N408%(>IV)z~|o@jcIfMhiqR1`!z*vh64P)w|(H^ zG@4M9RO@q(^Arlv=T#9-fY-#A3(07UJ;pM3HRew4O<j0C^CpD2xM?l@Zw?B%bDRwd zO~_rK!%$pOn!^=%2v)9sSr27i0vNdiL|>(Vi#a@Tez_v!Ix`eJFTKZtsGq77b15`l z7SQPTDA3|L4UsOT4~Q;TBs$v6f2585*BE{G{+-5X7d+19b~^qkivWERebEP2Y&an6 zA<$VNSggPpocB2!{(9(<<2}CWtln2?z<RyM(0N@6%UIb`E}yoYhyN-8b%{m5hUvN+ zGM~X+OM&Dc+^ejyxz&PzmygToh$uiEw~=y_$Vd<*H+EyRU6okQST@XanDL4T1)Y;{ zoooT?j}p*-Y6bC*Ki;m>ibQ^zl;<<ocaNIav)X=IFY;sNwTy9KLi$1jB_VX7pZ$ix zDnrc<wD5^fK(mH(ENTXW?a??4TSCU`Ha_{)6z}P@EI|Ne8mE>H8!5vGDw9Ps{ddhi z66uY&#lW)RR-0hIR|<nLhE!00Uer+<4TkQSs;Q{SDCr1Q3o;U&_!2zxd9rBa`S)UU zC3pSPe7qv)B|iK3v%#^0KZ@oOQlhVKA#ihjU`!ScOy8Y7rhOu$<YtAi6@RgaQI8!+ z8=7XE*!e2RPQdna=gxk7Sy8*}i-kyEjqsO@q(SZhUF>%f2d_IJ9-nXyl>GC2twBj4 zA_-fg@A4jhYxz$bdb&Yz!yZRJGP#w*j4(GnT$zaMgOg$vBQc>fjOZBxYg$B-rT<DY zEKjC>Ph9{2LQ7sMXvGS!Rqs)T@jRACy(WpdVkCLA<-fVd^MCwv624hL;Mo1PC76>K zk0!`w73A&H3=JgG>xCr^aP((@d_Z=;QHFE&P|oZ5$I{d7&}(tr1Ak8e_iAJ_y<9Nn zE~N^DCahG-L*GY@!uOh4JB2fBwf_LWb+4qrh}3!bya+KJEOX00YQAjGT5>HU*y02? zqAdzCaY+ukh6v|~w>}oImnpreAB17<61{S5Cm{jn!$#ZD@f3(nIP7jDY?%7JzE3<% zNAl@hdl84Ez6L64udQ$$$4ZYgMjeR<?LBb|-`v{~dG5zH1-$NG2Ke>gfvZ2CM_4xC zyHsk48VCWoy7RE`Qkd3--!I@<-?Q;Bk@Da*)zYAq<H^M2Clc+F&gf`NtQc>_B9SEu zSlp1s<w2yy5B8z)wA95=$AKAt)>7?izR07l7A?&aei)Lj<mNx&b6i?sS@)c-@sSC5 z(hJk_JB|qHeun2N^WEqP*dXm+E*|i+BN~iCHrRY9N>sq%gGmYCi$`5wWw35^ph~E) z%L?WYsadCwh|zUdoExvhl!p~ZN;u(0*B+LlLps7j2`Fh42ou+!<4HgPe^Kf*rA*iI zwJ7kHWpN3eN0GYhJde_6$rFObh2M@lyiZZWEJ?-!Ql>9lg;SZ!%?P)BpIWc!#rGP6 z*PC^P+sDsPyX@OE-1HgqxM~>C26u;j_lT^Tt)`cAq;n=H5AtxB$KORjctW&zATSKi z*SNHyV|;qf1Cj303=#|2kP%gzbG@O&=BQ_k8~rT2N}4(#13L)jV_h4eF|)9QU_O@o zfBBf%i*)QjlFsLD<i97Um-wltqnnSg^MbcfCKa71C=_FogW`nApIb3$-@L-0{c_k{ z^=UbR?SNh|8S)4xtJq&4MEaf?;U|k`2)ADTDd-F;3GFh8yMPj3PXZat^1k}~!%4gD zQrBdu9om1l29fsJYGQ{`<99p^nKLii(m)p7qg~j$<ZB*8(R1<q3zAxeh+GRb;w*W( zx=9{385}!`N>jWgk)GyBXI7O)q5}m0(f?R4wuW9FqR&wd^p6Yc`l_#EHYe#GvRT3= zl1r>?dq^o#fRm_k<O&T7S>H@Mo#eE33KsAyx8_Lr5<3F^<wjx%oSB0}G>S^Y*ze+9 z{pjKQ+w9zif%G<~B*%s8l0y6Qh4`GkMQy?{I%@gwx6t8ohO)(~buFogl5A`enHC0t zB*4!{KQ*q07eW**vSqCQn<ImvR}?orm!=w<=O=}<SAB#X`qsC3LRtk-H1h7J`5Ba3 zQ-x<UGzo396beF<k)A{_ao^s+1nxzd|9J;o@9p5`E{7Gy1}0$1ZrCjN97UI;tak+! zX`n=vuJS13Ox*l!eIrDGY=ik4P`HyQWlL^)Dhg3B>pVpGjbMcuAf9a~yM?1b1e!(8 z+P1>wV&B_|wGbcs&>U)R%IEOda6`*`Ifv^kuHKGJ^#0b(w9kOMo3vf@gJqE<wEMb5 z4}4s`&H8!LpxU-O+{VLXlZm;+99xYPwcpj(WMLM2NLSr{30xgmi{Q1nO_!<D^C#=g z^WP;v0^0|B#u0sPcZ18%FaWOEiODZ#2!9|YCKcaXLT+WAjuNPJLM)5usHrNZI4wu- zrx!J5(u96jyHme}YMSxvfSm`;QD&*)0b2PW)pSZ302M^3j;<XsBj1a?_~dN>F?QL$ zPn-^^@S#!p%;>`UpfA<{UL>y&!K};%L~{#WjbmBprbciZ!2Hj0co^qV%Tg;%*%T+% z4x>yI5nWo%lo~;{hFB)Xo;Enb$8UF^kDq!9e(!&Cv~b>dKD}lPIFGt-IKtB|oZEkU zS*x6NRi64xi&J8&!y5%Z2TiyD%few=lw}_0!a^_``#Ba!&mV8EZf#?pR)d}AK~rAH z_GRpUYomh*dGc&_$EBjvG}ilPQbI1iwtmYPO*^i4Z@s;iM}<7QuJgf{x{`7F(rtgL zyns-)Xd5}~yg)$*<q-u4b`S`bG_>Bx^^;A+b=Sl+<$je^$kv2B>xV1x1J`-<)E&6p zeq7LyeWtR8?@W6T6tf9m0B4y}b!|-xr_e7dR~<%#PmULB&rGQ}<aVCyO!UTUZ#Je5 z1|hTg;^kKw<yfKZI2JbcTgdx<jeViVOwRNFiWFj{_g5bTe>4Gj@|83ZHH?^jz*|>R z2>-;u;ZNNmpTjJ;hNM@x8U0Yutltp7N7Z}(sCA(xqus;8S^l6bg)}@q0d3$ss4=;Z zV)z<{NLpzO)7DlxseY(`uPC9+T8}y_BN<lq-2+>+Y7}mVlB$FOl+5;|7*XKDs2m|Z zL27F_*)NeEFf*{Y!l_AIb?x|dM(D-|SZDZy<#AIibWmD-rIRa<!ax}<)zprq74Tm^ ztt8tfT4_VurB{H-qspL{S<KE|9^6PxURGo7;1$drC}DgP8|wle;{ZCv`jULy=*dyD z{HQ&TUA@uNI8?TV;3pBo<YPrqwb)u(4zww}38YY>FFC=ZZCzSSCKg4JGL>cGdeZaM zT|(1MM!<F!4SH}V@R5z)Ca%K7BIDee!O0MJZK7LL1^UEghyRKX=raEshSX|i0G0DF znx3e|uOHl6ipflrAm29!kpQ{Bw)ytvz4_St4Q0jJVp4DBaukDN%8R%6CpNmrJ^tUz z!V7zKXBRHvm?>F*&N8O+nyN`1H5lUR_L>vebQ_h0EA|*d0vBgkP6|sjw#yGZf0wsf zlTOpqy$-lQ&uy&jMkCxz(PczA93UDk&-D?^_0Uhl?;0?~FFv=FQv$|30hphAqFYF1 zw;X#a9|xNXy&vx=Hpou>@j{-LKQNuM6dkc~1Kuj{Xlgh^$0q?rR?Jxglm^eB5T~u@ zN+caq4&yGnU>Wy>-)d}KMA25UqBn!XfL7Kluf~aBG&ywYY%Qr&8a@cU#UynaHh&V~ zo}0VaBEB0OMr=CsN<R8D;XY~lSos>uainS6g{iS1+Ov_48)phNE~mYF3DR^W^HNCE z)jn5Jo2iX&lN-6Z{4q2h>%^!;u`P*nKi)(nxFI$Y3CA*)z%&JSS#O`>ZzUDoUrkWp zIlk7hy6i~WxC8oT2pv}4J0WWi!f?5euKD-DGq@Y~*`M{sPyJ2l7aVpgJBRP-CU5Di zjGTAVExr_W@0`HQaYrR!eL<4Pj@{-(_!5P*{0Y4!+O~|XCg9SL<OimLaV~Yzb>m)s zxx-0<T(WCvQH5M`(uu<l*kJL&6YFilhPnRjx+Y#`xa{wKmm7a|F%wjnRI?-b)S7C5 znnD6CEp$NS@bN54CpimD7bN_7Mp~X53Vi7xu)Ty)zSVPB@^4K73d7wnZp2o*hA{d9 zWr4mRBlVBgBy~m~emK5?4|*>~u{jD1eT=rbu5LPijZj9T=`K|LrCGp+`tz7+4ZLIH zf7{-1NSMRc<EP%zSfCF_JOagQ9^Rt7O8M$Hozrc(UR=DaXHh)S4pAB>LHK~+;!&3! ztaQpfqI@MZ;eKad-qmOTiGGvZ<PT`rSlS^57&UYcAfMkK+#Jk!C_?lV^G<x<w7nkk zd#6?@MSzoG%nC>@jKj0vQhE;-14Y{o^bP%XL6mrHc`qwzPYZymmF}KtLI1xDyztDg zN3qC-Mx*OA5i*DtcM;9kj4gZj9KVSUY<yt(<ORHKm9AO9#q2iZBri5qXjhxJVpZF6 z<8N)$|3aYmBa)v}{ibm6c&D_Fe8NCQ!~FL`z7ex}4Otruz-gS6VZd|v26b0qO_N>2 z>50cEcy@2|=#kzbQ5NUFvBE`anBKtUAC_-7Pd2FFkbDK^@26X0Q|59~{6|CuBn4KM zEXtV$ln->|>V+^|^b!f-iU_NOqv42?Gr-jQWQ0<otaL_NZoD-7Vxt1Xgr0?WDA)LX zb9DOs`PZZiF12<t`JSYwby#Dx&()GGE4Y-6rP711y~ooaiZ?6#?DDJy2B$ZP1kZ?! z7ugu3zUFZmuDTVfuAIRw&=jbaLk&sPt%PxQwfWj#BwnD8)SU~SZD4CkeGtvI8w+f> zG~w+d*e?BFO>X)Ta=rBx>%N&+9UG2CexUN@+-?6mVS!z_Bn}w7bQbnNCCvvs-l?l( z3?7^(JB#=wtR%Q)p4In+bj}*>tL*I>bNY_}<8$7@<?9}o(=#gPPAs1(a~<d|t)VxD z=CG=_^Qy6tG)P@=m$A`?WW?QtgP4u?dc)Tb4&1~0f894})w@MO`MY;#Z@#JqBIY!L zn*GbF?&+0%qt}%ZJ!8Y+L|>AflQs7grfYc-EOK>ORJoQmzGG%kjdv+AxH(L4;>P7y zM*!EHQM3Z#P$ZiITo&GDjl1q5$+p2wx_rR4pTY&JpqBFNN=qW?hDvS)Mqff~>jsN- zfvS+DT6y6C&d6`l4Os5}kscM@#3&M_UH^lw-9D#(W)b$E{Nav_cArvUNM?bhq~G~{ zA+#M`Yuie(400hVxKACsjh!6H^j5YMwQw_C{H%Q~z}1R*0H2>LhVde3A$rd{Bn|r1 zGb2hlO^fvs#saPZW11|dt54vqvrhj9S$e)1l5kgv#M?N(R0w1rAm~v^)&Qn`jg=KD zHbD;UZKne*xBIu!=+yQrx`W_k?c~3_nK+rSCYbQD8y##t=QLpuY!sG3+(QrC2QjFg z*ykS?k5aeIq&EIO*je&z!Bn!Nh=63XD*4n1^8e;%4c$y9B_hpgT&)VlSV=R-Q9PkT zF^IRQA5b4}hau}yF7sEp<zum<>j_q_mzMT)l})(B2A-|f2Ms58P=R*ptylw(__@2~ zC-Vl#;k+1Ln9t^1GY+e@8DF7rWpXbTqAgLBqJhZ?gSEc<$u);Q;k{?a?`(jFROp!I z(8#iyi24s3(<OQhMSKj54gfMf><nIxb%)&^M4n?2VVH*0&}ge3hC0l;)Mu==%~$}B zK&YRa4lQn2o~=J_YYuMUDT2a;-idf92j@nAEoim>vrgCTaSzwwAQUtn=j{qx=76I| z)Ua8@hTnq<(fu*DQ;VK9hXc;9|7ujE6LP0;l@FM^6EaYlHp4eVzd=f`jz9|=8yvo~ z<5u|I-Yxg~ylv^I5K0;h%=~kbo^cL{F4w`sYW!yxArA%uBwrsIv#u(Of^QfoRxShr z`LP<k6eA#H^X>W|9xbaHXbM^24%B6<R4DVmP_a<6rWFlntx4b>9Fr&{6(`sYdK|$O zo1k@BzQwMRa1o6X5W6gI#Nb`^J$yk^-Lq9@x%BZq;nd%@5@FD2ku!yba-&*<yBJv( z==FZT4Dj6$=Y+*GO}Hi6zuPuUo7u5zYUq))hQ<W0FU!l}%;oi0f$3{4BYWaoSa;Wt zI6EVjWHJj(F=Ra3ew8vXhnVxvBj|mU0$2BZ9<`%r3n_k5q<}-f=<K5L>9L(N=x_7; z>|&$L-kJE9Yk@bxODMBE4`^`vjkp_WTH#5m<n`l7y$qv&zr81WZbMLb`QY<=%&))r z3&TQX-L2e5+O;~4gc-z#puNB8cwYE^x$bhQxa#L|cmsc7m))E6aeYJ$W9HP1J{an) zCzwAt)HSq+xt&oYat==VYkn*qKdIE0uQE3jNCX<3%OzTrN%FpPaeAt$wpNz3wNPWG zso1Ka&?rg;KYS7x-3xq&tHXtt!|{1ku0FOnQD@qQ`>(*~dhME%(JT3%OOP#er$xa* zGdCo#x&jDmS*gHuiw*Reo=+Tv`(Ewnob~X3ABQ4UcS38gZW!b?SkItQGfDQ0ED6gP zA?009@CZa=z7x`XKcXhTruBZKT?CZn4ergRYZ*Brr;9v?;}RB_FB+K8=I*phVlNix zM@`%>T^w0e3xG-6+7rLRJ!PH`IY=&(%YkS#m)~)G{DUJR{OVn9>%c7t9rXUT0G}&v z2ZR{3mIl(mE1_zcE$&ZQXU-92WRL$;1%+1OXt`Gjrw%9un_c&bU>y=+BpHJah)o|a zdtHKv1X;ybXld^XlyYuMv)T-Z#$cO_`HaU?(sg^ICu9RwJJGhHAJ$8;nqy7WWnm)w z{LsQ<KG(-R2;!ReWfp&YACt9oNm&o$BW^D7N!!@o4-Rg4x8)bpNDP(q<s=Z=58wo- z)Prv-+pe?T7CS{eZ>x6soOXI-rbZ~(;TyW2?&}BKp%D&)Q6(^+GgH2*z65}4)|NR8 zqu5GZc4@LDD=7S8o4mi5L8JW@Bbjbf$<>Q8(zBA@D%OZL9ab;TK?zMZS7X0GizeP{ zQ*DZQF}bs79cEO*Z~OgM6||b^_n&r`z<2IgBu7F7sm#8MbWFlV)feG?r<O}50e~7> zn0=)ghn^b)qEv8l4P?H%qa>GZQIyW+r~muq+9>`mevkP<%`WdyKH3qka9G63d`9cy zc?gkOFSS~ohw)$PmG@#Iv36T#k_fz;T%;cO5%Co{kshpBLS4H+)pR919Q{Wid3J!_ zU_(`e$S7VvuL=R=W!oQz_t8n6&;4;ER;7LAL}DyY)_AsBw9}YTxR*Lr-M`;TVqxix z4EqwLx~wwrU7e|qh(X|A))>TngE%qm49dGItU29PDg@ds=Y<00fS8KnN<RS2h+>d~ zf|oy;pZA`VvdVJ>HyuOV=9Wx6eG-N~kjUFv`faQFX)?`66yLdC-ClF37Y;&eH%D*- zW(B<VN)K)JA}d4$+}Ls!Ts5wH9N$ASG{84kl;k4Pf3;|9E04;{_D#ySi{(^wn!j4C z*8hOm-T9~otaRP~hRI;_(2oN*W_uarwNZ$;=5aQIl=u}{E}<Zghgc_t8cQD|5uFAP zz%%8lq}4hRRZ9-Dc#^e3ZeT&(PlN%}Px;(j(RVEKY05%`7u`4?s!lk#uGFpT48G4x z0YLooJnaw#(zQ1<WR&9_w`E*N!PTmxz_=tzrn^y3_kQPcYrf%+$!Z#%|9>Hi83Qr6 z3g7oY@RJQ79BvPekMsMVpO3$P+1k~y+^5hy^FQ2{9E~!^dH|<&Uv+F}rF0ks@aP4^ z#SgC|QRL1P?QPljH_8=zASp~vmlP)+*nU8Wna+oCQCaEuQ7WyWuQ~IG-5<C`{Z!1j zozcL38}!ss0;^ZMCf=V#KBVh+Hz~T#eMgk~Rwzurk{KlqaM@kMzm$p15O=|So!*y? zZzJY8=0VGbc?uSbJ0v8D2h`*ChE5I?jZrT)>X=4vJcPh!ue*()61%L+2XJ4xj5Pc2 zhLEl-Qo_&cpD+=tBQTjE`Lo05H-=1^50nec^^BpU+RyGV%u{pzX17d6T&J?wSSxnt zk#dJQgHFzy5mt5{59*VoiHF&tb+8~>eK3dn>M-=4<d&}3Zk6)salvL_X&}6TX15<3 z$c*u=hRlNM3gzkZ?I3LP^JwGmO$2-uc!$N}#S@9fUUpNiQs`npxG<W|Te5!JNeCb& z8>b8_)zOThDWaQe*G&DbZk+_r*eqU3y|b3eDcbT^cA2BE3@2*&OO;2V8=dLg?y5W3 z6W=}Q{-{{L7V_H{@%20+Xu8VuJ07gCs%F&2ss~>Qs2-Xq315=`A=RoWs`nBU!DuHk z5Z9J3-m=t?S;L3(W5rIFUE93b<nWjK=Bz&(Sk_h{Tzd-xi^IncVf8}a&_TGk>$C|s za!>Q|xB~I5`y~Lo(Rrslg#B7~9tdh{ll#h2U5|U@`m72p)c7{qVpY-8>=7d9szudW z#BEKofh#r&;)GyTT$AJO16I%PfAjNx!OQ663)leSd*@jKrh|%K!r!#GVafF-D@+)3 zhuOn7$)5kkRg>}l`Re(xEj4K(WQY6Rh2h`)kh#9iy}m%C)t9&>#%O}6YT-ofs($Fn z=VRK>6HdZK?Ufc75eNo^a7p29iS_N$fZW24wEXeY^xOy9&^9xcdMM^4KQu!YX&$F1 z&18hBZ$ZS-13xxS&pnmfZ#^8=qe<aBfA?<Q`RRFDiFu+5<cuk^jOTF-VB$yZ@6eK# zskj(6-YcS?vpTiuJ}qaw4u&hQjo|t?cE5~?IaGXj?&<|S&GTyFnH@{0yhR(w3&*ie zR*&!?Sq5eWcXgQ$;O_32o5Sty8#mg*J;DrpsOdt5d)gTK9+^~l{~fqu_AQ?+@?M)* z9%1h%<P3|(H2C$T$7x{(!kqnSQWipmUK^;BUO)lVI7{UmZ<(96Qq!z%)Lf6?{;nfP z^*p`hg-V+)U~V(d0oS(Ir*Xj_145umMe)}6tZ2VjsUq?UL8+kP4n}7EF-*ih8SGU` zjOjF3v`#PhKMJ7+4pMu3k{6A(;z+3_qK##{qw+L-s;(K1!9mcxf843Sh#>IVhj@|~ zG=_V5nV@X&96e~WUrlrD_$KUM=ofyavFzF$hDGaYn&e+4J^(mfd)16&hh;~sJWU|^ z;W4f$S5kFTP#8_AQ&?`1N^MnxP8UB`Pg2x*Wr#-VwZnTsD+K2Q6L23M=TgGKE!2D) z^p({j5YI7E<UGeiFF|`UtAq;)Uxe4OYj{wd#sU7{F9mI|+8zd0+qq#;+wq>v$ni3t z*=XVgtjzohG0TnSEXz!dDu@`_l4$VZvSPFS`;Mw#_+(<sSSNJ@DCUmW+tH2b^^yi5 zn^djl8Sc-b+tmj+@Yff@CG<t(kiBGI<iB7IESW{y(Gz>SV=OjS51fVxR|oP>4QMT> zz<T6w+@>GkV}Y<wC=Eh|)^srk<7PcKgj1~dNmfKmx*rE%HgC=FC{-NV;#XrHQ2`v} z^7a|Xs;^WC@oX^#dtMY|qoRAihh~i$?}W-(f@4M=_gs=)zit*H)`cOK;&z_GtE)?^ zoXAx0Zb=F}n<lT^%hBXzkV}G9Qg=@1&N-kXf1RpEuA|5#@uE3<>d{WqTGpLb3hb`` zg0DlH|4kfGz2Ofs;j?}Y82hH0q?(=@ZX6Q?BAP(?U}U)$WIfMK9Ja7#GYTc|+W$r8 zV7?ao|ME5LQhc0J7h2pl+_z_L982<#eg>-Mwt&u$$6i6_^-Yzmw(BHbZ1?cpL2g0& zWdZ)(KP^7tS6IH9<<8;4_yW!+0+x7|<i*ZQbyZhk4;xsJ01!WJbaJ&l{)+{U^MV$a zCq`-J$c$4$#g#y$D-`d<rMt|m_i-hX>-WF=A|3aCS_DANH~>-G_k~69;IL&2xjy@_ z;=ub+W4gqI7Mh>TJ$Th+{~>e_fdJAxjjgT7Y9$^ij@Am27KvJg2SW<3T2B<e7TA!= z(sYY6r&zmX^3Z8>h?iJ9g?*|J5M}dt{E!i-8RK=TVBN?Xtejm<^U{W17^=LMK{bdt z+6dL&BkYK^jZ<P(Fv@O#J+xR<v%8^AT8GT|!|x!2)9kOa{p(xL=>#$fW;)QZs;y%y z2kN@?0Ebd~WV^s$hP;Y3&jaa5U}Z_&*kLh_qaP!%+v^KzZvQJ|TbO`KeBfYc?>CS+ zw+D5b7W7Pgx)+HUyqjc`nL-2+Nj5YYiQQLf?*FtEb&^io3aYF_%9!48HgRlJ{+oLn zp8Hu<Zs9`Y3m)&Q(cW%{f!gc_HCAv135Phm+yNDCIVfM-s;yz!L4mKtIa5JrD0TgR zSb3>7joyEbiltgCCO|mkKUy9IrsaCk>ao#PA!4)C7Lo=BWM3$JB|=L#V%h|M{Jncc z?LLa{?J!+;CgJj_mniG(A?}Jpitr+OZE5v5Em>*e7aHiWzxahK2AW)B3Y=4ljxSbA z@u)-Xetf5ctGw9zL8D-borZml|8Mkzc}G+#i8k`Ce!-O4neDh5E$i{yb$+kQ`k98! zR){U`%wl@<d`lx@jR)b`hP^g$PEauM2ImHUvQ5aN|LyYg364lOxU^|>c5tXOKoDUj zQ@@n!#1RLvS_+q_$|`uU<Y&y^x)!Wj3W>n>p$vVT#g4ztbnp(r`Dtx%eYZXc?JpS~ ztN$r0#mA9<O{dBQVw{ouxW4KKe!JMn)?<I1&%o^&<14*6-oh4y%Imx}Kd=2d^EovP zNceYZ#H(e$-YNsWvZ9~}(|0&;v!I}Rlw}5KyG*xZ>YgnsPS^UT-|#Q#nsF_j!xtmG z`+ewNeI93Inm(@%53=t^E$&0?%wp%n?{q3D`Mo#$s061bj?}qZ!g^F5FiR_@@|&>O zxM5#qO7UnFU%Od|;=q{x7*Cb*lJ3Jj@xq8rgIx>Dc&x7xu5ENOtJa2a^pe{R$K*7^ z(6Ig=Wi(ws&C;qY^Cnq}O}^&&|6ykGYz$SXu-&vtMh1AMf=GvQXENtxf*3IlECz8f ztj<}2Pe&2c!9Hfa){5XF?<?LQ&S}ZUG(bFe&0B&~)<bN?L+f;g0s_lqKPTkT&ka>H z+6m{XbZp~61$^yOtH1&cHFI*XVG3N)d;l_{`D{kr3%l+#)*Cimz5Uy4IPUXvmFvnR zfe5f#4u$?FLr9)V!b*IX()*$^#a;93>2{O*VjwEfq{|njER1avT?Ub7zt3AgA_zmA zpheX@VnN;-3xZ<#9@FEC6m}FA1@)ue&=S}V26Tr`hN~^$OJ~U~Ovml5BY(FVhn1*I zg5*)<rp`oP5S&~k{q21F#&6KeJ3-<RWd-3i3a#@ve*cpU?5*NwG-&qumUZ-}FPm9v zBVh-l<5`f=LbHNGPKN;LJ24!DHyv+UDXfn$Ps+8)80b)vVHVhy?7?)#GUx;I@327! z$|<f&H}grtE#S#`sqOh_I^<}kxG={UR9QkcGuk!heSBlOLcxDwGrkqJOh2zEAzR>B z$~gu)t;_97v)gu1mQ8UpRy7SUwJ?lNC~7h8{oiF{A0P<Y2G=)g!0|U_5q7Ri5uT9f zg{z2)v2nO(bd3fFn0d1kF^@s}Ld}oZ)DoFwPMz|odie*ZQ2C;(uv)&kIF|=n0TZUO zVnK)Z2aVHr@+vcQt>Nt7lrz*PxV}jNQb%7wgF0wXyk@vxV=c0<$plO}bf;KN*&wV3 z=Ii-H0;}J%suck-(}7%4e2QW9qM|WWPAZxHvm%BagfT^+B}kPCr&EU<{e-lm=`Zi+ zf;Q@wATx6|6Bq4>OmSeukm5pYb1?5|5WxJRXLT{1o3j%V-^fRG<5({YG%{yXNk3ZG zEjPKAADrRQC_;p%VZUSYE0XT}+DT?h6Yvwo^5Wxwc)@==Q^G}<f}eO&3Wk<B_{)2- zXAVZ`>r0%3P1oIx9=Nc-o<YQI+-xeKtwqroCLo19fiFi@`bR$KHZ9QIOxz^~1HBsK zhUUxTEB|A;XTW}=*0qS+JW)bs$PxPN{Uq8zPD#F&2bD6NvJ#tuXB9rKh@r+|wH3n@ zpg0Ze`%XB?!ky5K5c5Tcff!>8#l}^s;uYL(8#W->n95vP%fTg7G2*bO+*!~cln6L8 zF{&~fGvl|VF-{*n3>srQLR0ou8)ua$#ZJnyWJg_D4P&C{b+F}|h2T7Bj5yxTp4mPP zf6)r3#<2Cf8FHd45JWqQ3-1}^tPH}2yf)vHyUBQiTg(51_k76^s`!@2)an6zoKFA& z(sCzX6c#!y5@6K{(%S1pJWh}(a;eqO7%<4xf$fP|wkI@^eXP?bz3|1?VT<<wlt1R3 z!vW7%_U)34+<mK^x*PrExUE)<@>HH$dNo|Nw4seI7a2OE01o1hNJp1F#YX`TW=eY_ zc#_<N9~`M+rHkTv-jMJ;1KPrAoa<Z^+7!Z`wbSz%9u;RSuXa#yQJv;?iI2z%8~^7B zl?NZ8R3wW#F0a`MxlsA6<~D~vbR-h~Kg!-QI?uOT`;FbOu^ZdA8?&)(+qUhrjcx0W zoiw)F#%9C!>i>DxdY`@a*ki2yHJ@^i>$=W!&UqZiZz_9c)+NXUq393EgYD@s5Uj)r zofgKY*a<mL&jBaJ+x5_c@e=fW1v}*{tsfbS-j_J|Zv+abhSq3WJF(%aB@S<s5_AC< zk6u2z_)w*e+<r(M?fcOSrD;R;dgDprGDC?yKOxL0<}Qs2935Io$OXW^#S7pq(Wut1 zdWyqPXfgGivt#!*bE7CZN?wxf=<k=91dO8OHA9vDF<jQ_V(EHFGT1u`G%=W{IQn+3 zIcsV=vz3p2-T6t~FB|{cah2m=cV~HNH1q~2hWxK3pS9m@aH?D@{%mN;Ok+=;@|k0g zqDiu9a^&QiLWiguNV4tQckI8aV)dR=AE3;VIW||n?A`md5T`ztHMm#>G$|aLh3gX= zx>FpPtr1J7z<F$y++@I|>T8h>x=`FIzmqVhBd!klZYo0?@zDoBU=K%zz&g@TuPbCe zgvoBsY56V($~V<jgJYrfN$;V|(%aeyRn9js${@8JVyVI9^KA&~*|_h4MiL4-Ppd~} zA~d{qxjlSgQu_=pTl;pl;J#&=(-|}=Q8$O6OW6d<NkB76jaz;v*={s#hMcg0_8K{C z6<=b*&M(rkESp0<V`*Y5j+>ruX<cVap2HI>C~llyPXHoUW|Agr2(853P55Rnm3Clo zBu;WjzrVa)rb0g(wBIirJ#4i74oX`m9)KIVI6NZt3QJmFZeILM30_N2N-*Egy?eDC z;Q4Hj*Lq4w#X}rJ79FfESn|Pssa?vc^jHnkAf6`M`mGV&N()Me-d>$W(tM6Lkoz9A z$1=%yi1o^EPiO*cKaanbi*}@5Svg|LW5#%14>&xf;2zj}Ece)t)QOcTLwj;uS+X`A zl`)cNs^u<h!XscsY;a9e))+kdRXGi?C_A8K1b`#hJV*uzlbVK}MCPdWeTi0)%Uh|r zr2O$4eU+XCH&PX`UWWLf%W&TRR5FY+7G8(H^;3-r;z4tT>awfLiJ4m-mn+4Y!5r~e z{$&Yi%9x^$S?(8ehhoJ`E%I9yj$0>#(9Dd7nzzSlrTd_6l&t!TqSe1M^U`Sp!QW5o zf83D_2)-K37crB`N9$ChmC%-i%0mc1)}Y3~Bb;7D6w6r5Y(3qbaK5bK#u)ewlJHj( zy4XSnxyT%Min2_ptMCQInlZ`AOJZVfkkAxHEyT_m@8J}(Dk#|U;8YJQ2<<d{d7BLT zJ9&dzV@D~Z1eC<#=UCW(3yF<6@T8pyR<#7}ekl3sH%&+$Vx}kN${V>1)7M)Emm>WA zCOU3%-;EQr?5{rfWS55ZR=;SaSyhmNgE@#YqXk6~%5fUAY|T|C7(ZPZ1k~;3Q8D>0 z?sBW<H}d8suKQ>MkCS;3V=gTFhFw^I%Ft}KNce)#YBab<gZabhl@Td1wwmpaA5h_$ zSYp1JId>^MI5v(kDPe&5mr-BFcoo0Iscta76yK$501$_m*jN*zNp)4y+0!x;Bi=Qn z6G=PA%jk^F7yg#H9IDN)c7MxUt<?Y7&@5u=(u96KS@ZnapGpD*y4`vG$l?pvt^qTX z$&fj>cNLq-(f5zbxD$ezhE<KsMf92wES<?zOG>n8#a)4E+{4B3g10iYIT0A7<$`7^ zhZlk{`&}Z33xW?Ukd988wt<-9+XhbW0BXY+8f}Aw%Dh>1ph7?`ZZX))N_@hT#{I3! z4)`#BcdKHxLzAw!VYxJJ9SqF0jkeH-d9#6zwWSGSUlxX&fQXyx8eFH*K@VER>1Z6m z%y)5)`2NjoVJ~ox2?cMA{&k=+Wypx`?rO_VSD<%7JZ59l+%OMI!N$hxHgj!pdkck- zuyxeJ^yG@$dB>4PAO5u7n$P@~yAjW!k-^a8F|8-m>65WelEjE@KmRuQ5voBOjCBCu zojjAO4bZXz)rLY#CdC^_OBQv)2oIdaU5f5KHy=5GAlWtd8b+Be?cTM7nnBfMj6|ja z>~xWpZs;jCRS*}BKvvi6z*t+zj`hR5)-w?cK5UgqGd?!Yz}sU9na-}9Y{ly0Lg<~g zigm@6`lsrW@db2MY1_|W<Dy;vWEw5q5m0El;=fL+4MEhHkn2ZbrZ~}L_2c5&=cuwl zP^fDTq1R^0v;}pau7P&CYzclw|Jt@3`#a&>b~_FRG=fM5&4FWjg&yb&wPo1(t>q1` zl(#RgE@x3#r4;Qk$U$u%laIu(0@6TC_crf_{y*61*+9|(npyX|jTuCqi@dfNK^$N? z?t($MYA^-GceH0HrdrU0Wl)~hZ86m-@BMZyEvJH23$HK5l^Zd215TP=u8*$VSDa4O z?J1#XgRG%B@_CwRoVOa991A4i&H3KW=rQG$2Ub3@RufD1IADI~zo6l@Jt+T<9@q-g zSKTD|3D%G+<rws4>hdQf+jNvEzv)O8YmhSIEfbx#3f-4>-`QZ(69S7n6XC>Xf9-;M zzhyl*qAYJw>=OLLz2M3v!xF@bG+;kmd;F;bvz@__HceU=Bj%{9kEa*f02S0QDpcgW z>z5R!c<0mNiE^4?wQ`ael%bG&^<?sU$42-3Ic|3hy7?{Q{Y90HHg|Q2zJxtcKAvHC z;L|z=4Cj7VqlRI%#*6owpGx1%c~j=GIYyTz4L<lHa_C~0HX3Q#_pbeBdRin#VqaWb zu)8f0L#fnv=G+`%=LC+92P@!IhCER91w0dv@qm895_j|S-DMrGZTL4s3FqW#tWI{v zU~3=y=W}&i%IMQg1gk<9im6f41;uaLfZ=*67hz%nm{F!@$_y-#rV-ZN*)-{NJr6&5 zRY|s4XYmz+7lfS)5ZOGUY5h4ndO>#EAn)UA>bIeoMdQWG!{L)H<r?at#5tQTu66WN zjT&OJX=UK+JTA=Jyz_Ybqi3=Idwr``!DsgINHg#%>!J!&bvG%G;t=o?J2Pid;X_QH zy`4Y1o0a1u><f4TOMES;4g)$p!1sxd&cNr^);G3gncJg;Tu8C7)dRso;&4Mo0t5$M z`xyx7AG1&pe$#ZC<*_+a^86G!C>WE8M}%>S#kBbVCNWGMYx7*7ouC^nBG+lEx>9gY zH=^%9C9it1+JI^Lz8j#W(8dr@0k4V)5DzM5b+88TEaHnUx1tN2$_0EX#ZwZc&yUr^ zK;eZfcDEAft`s$e`}2ptHdK9AzzClGc4NbFFC$hw5J|DRt`e36B9}G!y<-(XHD#0_ zHz6`rJh5cNR>$j=3&e}ndI7SnDbj|a<@uGCS-*6A6YbK{_eQiquZ4KJc;xgUq~$~Z z6MmPdg!qL{8>ec}G7j_V1BnrK1k*qnlSHJIku@;2<+0J^^#OgcsXw8QR|cVT%k3L_ zez%*_wH}z;A*Z{tWB!obiJ24i$){Cx?NC>m?LReORyVx#Sc;OlI=9beOl%TWVShdo z5IfFi=TXUT`|d2-yuaw!CovFh>-OG{un+n(D61Zd*WZNqkj=*5mi(Z@EL6t0T5}=H zy|*#9AmndDKI$qK9Nr7!hQ>42cD3%h?Dvnl;MvTBb3X(^2@Q_0oNR+X0~zxeroLF1 z?xppFUQHw7FB=vx5)@ueop1qOgI~QaFZ=<nWX;h!`%iazf72G(=|6$IB46^=f#XlJ z#WfVez9R5VnUgGKHWkvm%zIgEU&oio$$4V|h9#m)<~~~{rkjXz{M)|FHmAFeVTf(~ zoySB()`NP19#i;fT}#mtB2^KG>yVU@$9}OMRJlEr?ahmQA!5EupA%-U$a;7K&%UlY zeq9kBud=tGX4NPCVir==uw4{o0$_4Hh^)XfVq~!*vMkT!>EoPCD#ww!pNba>JSuMb z3POnbc$=`HRDsLj9-QAY^&)Lc*mSZCw)i=UK5uQ{P))?IODf`xZ0DYEOdqa}6B`+e zy_)+n>hN0R?1lqd&zGxL<UX!;Vycu*CO#&OmHoF?pk6UwXG+f^hS&Z9e9##N9gIvz zPWe*}pV8W;=GtwziFxN3*Lx&hr~Arr?@cORN4uL5Z@p*E^YD*egkBl%qxKqXI;Y#9 z!}f2>*g&;p9X;-qH-i-5FEZO#qiT{f=uyyv+W8k`@D_*iLg0h9Q=cc$ak^Xup{w&| zsgsROANv9I$6g;eoj~2iwgVv#ehuH`)G$xbMqQtW^cAFJE-8{q&_}+74sl+A>^w~D z>Iy}IzB=+kN|gx~j)Kz7+aLE48(hZI$O9~jd}e(;u=;$wiWBa4w;e5~A_MXzyOuLv z<){mynWdYXEDVcc8o?O~OGNx3#@eUtQz15+kuHgdHuYGD+sAwB^?wCQscxpW12p<; zaYq{yETmwe7o<9G0(3Emh7d$F!t{%pjVqiII<()f^AAhL5Ey)`d%2O*aEF*7g)Sya zj_rG*Q7HfS(?W4Pdi?U68_Gv&fH?&N)`|WSq<?b3@78C*s2|jwz?j=h=&}H3d8N<F zmZvp*=_YJ-%sXClIY8b*N4SOo_-7`~Os}mC+>19b*wvoFBy}S`D4K8IT@E3(5<A}z z1_ZERtuhXF;&h>38`(u#%}$AxZta_Bdz0(p&KoalRk3^0DM0ZBtiJ#03#V!9KVj%{ z5xKgmto%4&^Fh5|QhGav=TcAT9kZo8sFmMn&AjW->pd$5O~E7`2N_n~4bS<gM+;L} z@3Vn-V|>WnH%j<q@1wT1K}=Ygl5-IjJF73*sgKNx>o#&a2*kKZAqD=|-rmqP1Emah zE4?pOjNTZQqNCH9_wum1WhYj%-nfx1{~Emp6U11R)^79c9(Cm}{rNsP5!N^KfqhGl zjcCA^#(1K)-EoM9?ObFM-T=X8@1P3rz|^v7QeC*EFOvYFPj#TB;1z;w2TiEwhU|d8 zPZaJ+&eQ6=eDsgbhP$s7`joea^*L7}W`6r1Mj>gv*{qG6O(9Wfk>hi66x)3c|3N{m ze#}5fohuf`g4=Kr$7w}6`-%!U&Ky|Dnw`&AEnAw>%Gi!i=K6M;Y8GL#b~?uY40FtR z{$HI0>6P1`rCl1-@}*kvx}<$49K<Z<pg%wi-6#r;===xSsd9Fb<_L{j2eVk=VO3!5 zS%n1Ga{kHAtro}0&bJHf*=(!5M)y2CqPh23G2_+ao)j1%pD`Ig<-xT&UA38rI3#sy zF2L(T*Fw*v!2J;nHp|-bIFN5x&}q5|Y{YyI7Ap~>OJ#htYfea;#o$JyrVLv24wNqc zTn@ou`)SZ6Ol`(S{VX8=ewtZvVkxY0twJYr7#17sJ#Iy;K**%8=iT>AgUZ0^Y-+y6 zb`$0$;K?8z4fk%%ZmBaSpSLNsg^GGZWszvK$yJfGO0`O$@|&n30x@7_=|$@_<MJny zyxY;4^IC%)?(|w)b)1+Husp6MX6KM2)n>AXBqv_?_Nd5YQ4N@SL(bN|il;XXD#};& zD^XBDMJKvELwsdXHQ+QFz90~KNI7759!BBJW%D^{itBV_$5D^$!!yT4)=^Jrn_^Bk z4V*>Y!TiKRZFuYqgrXR;dp}_CJ$mC2U-n#IW-$#0Ls<@02ldmWG-YZ={kAsoqH!Ln z>sxyv;nFTqH@9+c3M!w$-G6_<X8D0-;JXi*^%|dd6V!+W%=N4c*~~T6&peTQvYisY ze_MbHol=?-))<`n&hC~uXpPm0MucmZ1;CgVbDZu`KYWAnuRt>F3VX1q<&Pg|RLF5c zQ9lJDrNs{X=JzopiV39%PZ^I!4xua0*3S(We#Z>5<5dU9B9r<CuE%94geIOPf|h@M z{P$~Qu<wseVFq1D4h@`o?YH^>n4`E6&a&Dd2im(#ejhrJIF~_?aOj(%XJd$^`-VLJ z75EzP&?8!{ehePL2z1ZKZ&iIKaeB{{S*H~+oP%pv^)9~R`T7+>MtP9D8Q?F`A4)h$ zeEXMB-YZnS{s0EkH;Fzq7h*IfdV_xMB49VedpkC|Q%X!KAfDhuDf`#7hDiB8Sz9&| zaz0f$r0Qt%;pbZ0g<`!515_kpiWbBfmUg3MUkF7V$-)d+eWe6UNvHYy89Bwi`q#xQ z;?|HcuRkXSq2#t(zA$(gs`bNjU)DDv`EqZzeTmbW1e1j@{0w$qh&paG(l?0Q;xg!g z1<Z>%TJU`SK;;HvC?4!uvslZ<94%tb<0t<55Jp>trdfm_gG7#sD<@U&eP;|QA8FM+ ztIHk}UcbCz#U$wZBsHTI>>znLMc8u$+6P034Ax1Sf=f%UUyuo;Pm>n=wD^YW)N0J8 zY<i*oxkT)F{Xeh4-~YM>&#DE;DMHs#>RMb{eEENTu=TaPR=g1H6*g^q_{P0Tdmf#5 z8guwe`Q&goJy$L^Dh_c$C5Dn_pMSeSO?w4HZjOBqWhO~layN}SkNt)s!mUv2|NgO6 zZ>)LZV*i>HYihhwlG#QL;%k!-8FS$*MZXnbh4?!37V$Smg){EWbf)LZQvRt0!C%FW zf`}eR{Lm;A^eMcW1Vc3=pYu^Ave-*Pt*jGI&a`PP=*>hsd)XFI_+KO~l`%$IWgwr6 z^>Uj_II?O1a%<JpJd<wIssDHHQI^fDc(OEqKJBI55}-?Gyw>q*HDK?5IPGgTaBcwV zQy#U+3;afmDs;*()>o6MqPF*w0@2ZJK@4jHGm!`3wBTuTU#LX#wKBlA<MFA`{QW2^ z#&LXcwskWMl4W`8nLC`S5{)hO-s)2vOkgqxDDw)0pf6{&RrdtmwfsE3cp~V0pA6XY zp1F{nEciOoOg5a7LXcy&S%Pu8LPF6Hy3RXsLPt5Hrqu~f7al*Ni??ee8tu63I|q&+ zbzRqKISBY~l`?4uSz^gr)EA=&9_|S0X_YT>n->7er%rZje8XVK|Ekb|g;<m_0-E7) zD()|ra}s>Woo*Og-%OIuCMc&}`P7~jK12*~Js-*soqoR+ci#$?RGrVqkDT8+(=EFg zjY2(J+nV`-OT>P89o1Nub#IW@OYq#}3s4mbY*)gdF}WT3-BVh$nZHgin4_{)8O1__ zZRr?sIS|l97HT@C@*uO1Aft2BjnuL$ML_0hxz~fSXkRvD_JN0g@*#Cnf*aH~6ir88 zUnye$ImE$Idsxz+M#(B!S^=nA+=@2$lX6q*g3Ey0g{2<M8!<vnRCXn}KrSf#l*9){ zypdk?`4g8?#Xrr&kHm>YX<E|+KqVc+p+_b?T0Z85^C=*(1WCa61JCy(p+uYZxfvt+ z2uvyYAWMlGV=Bu}5l9@rE!WA7fVVAP0@xOS`GWIpXRa{{y0ViuK(tFQE&u!EmmOsY zrn^h8i$NVuze9Si`kTEgHm75Ce%ulHt?>HVemE_q?2l6;m8`=_0&Jqiau$D-&;(1| zhDC%=N}OKePq;e|P!zN;x0$ru?swE}=fYZREDNZ~5Yp175$wV2j9dSnz**Fdj^QJA z|MkI^OUh3ENB?Nute)K5*Tn9oVXD>B);yeW4u*4KeEB2VudHqeAw3-M^dk42;CS34 zyc^5WZE`w!_uN=hW2y-F&ABirq`c)v8iVC}Y6x-<%<PcjA;ZGHiH<4i)$IY$OQ#O- z9zUD&1$uvTD7LS~wBh%=n3Wutchm~jpi}UB70(AN#?yeUl_P~pOJXqY4vJ~nva})+ zNkd^{3Ow<{-Bkz*J5iI0%~VZR3T#4?%i^M2=RoOe%-tXgtiiFU6i6IbUW@(s;ie@` z(aQ~;TL~J=?m{8WuO{;!kC(V<JQ5O+C_~#(=DCd1WwEy-wGJ3Eew<i)-J2DYlCj8r zV0YT|9}^AI+ZjT1yZxE`#yZAB@y;QY$<z0M_Gpf~bw^0W0BSXHY{5zPyv3BHUKy8U z^fU*(SluxD&>BJsXSY&;@vLBi=lJa|uK&ez8Cevxd;a26(7#NEXL}8;*7<CHvH|A7 zUz*JV>IsalWp4Ws*wA0M3yDBqjJzEiYE^9I8S_*@W^P3LJfTrL0E!q0LA}`@;MJ5w z{IWI}jUqfX2aH_D721MkBSlQ=z^9DkCcz51kif*uwNPtQ#ro|u+4;gEKI%&;nFH&; zms!wj2cyT@Qss7Iky@w9af8t5s*IyF<S2Bdme325cGZ2rpE$OLb`p~oA37vjfZWTU zj1x}7ffV2xpBSBjb<m~6%S||0GduX8*Y@&t3|5WM+sR(sJ(<7w;>aGHs-y9hFw_i& z?Xu?G&w_p!8Iqd>=43a+)(xBiLV*F<@MR&E-rtKNZkHz1i9YNS6H|BOm+X0-=LzMh z`!iK(#MGyat<7_=m}!HN&9oZ&k4gwN?Ah4~GN#Iq6{vkw=^q(8rcSd_D-)e>;U_}$ zB#JB;LH5Pvi>DNFs&re-U{5cSNa(Z_bk5v{SbYd{xe|ZNi+rzg20L!kzQxhNh@x6@ zW>PuAlFW1<$cfET-Hv!R1g^dfh<3zr-mR!Be%6uYpkjkG$?fw*AdtgR2&%b7Q+Ua# z!yvAS3MUP{QNZl^SaZh!Xy{LjgASf4qZn?xM=XuwLc9f4{?$vYU0+z>L9#*#bcRL7 zdl0pMupGRg2Dab3jHlKWvU_L9TX9fq{LtpFYd@99Z!~9WTv@LV(tlN?N9~1kH-9j& z>m(hvJN6m9{p;x~F*(?=eQ?kZ;QOxSn}bAFr=*UBHz)%H`I^wjWO&>jlUWClvK%P< z!mB#ZL@NJLMBS{BqL|pe)CcBJP&+{uC!^Jn0nrZ)8W|Qh<A}lKU+WB8te*<qV1Xg- z;ri7mlY`#F&NnWH{+w4J*pDBPY-s&4r>8>zW`M2GbqkdqUb55WsFR=@U5Yp|o>&~( z;)E#CZv%(#PHu38`{=>|P#+#Nwi41{ZY8Lb(t`6kn&4S}Emnbpm8XhDz+*=&>puuS zP5j9rZg=dve|O4yP6dk(OX9>dkz0p13%R&17Q<akLsN$sBCmT20Hqd=Qg3I~Pg8i; zBp!QBwP^E+5MzoO7f8stN&LQ%*k-B5%dPu@6)0%(KO#Nnr0m)qrKn40kpEQ9`om4n zahGpRnl{<K%U}I^(MxV_rN`D;jDKP#boV$p*j-9lA<&fw<m;|Bn?68=g&Rd1#`+wD zVV`H#h0Tndj=yQtNSOLetijgPL8!;|OAKh76f8PZ%&A~1$$%2094G*mp%0re4Ma=u z%Q45vw$IZi`UXEDNdZ4S%mbFLS?g%5m+BQ#9Nh{qPDKV1Hu78oB2X^6x$~CuMi+o< zga%T~a@SSzC}dqD2ZjU<jVe3tTyNCATnI&FM(CaEcLdUQ%|sF9B!1C;9hQ;C6%~Oj zY*AJ-05yT<;=Dkw{^u4DOZgi~>`&5Mxf~qfbfO8qSk6-*)OT-0#NOmTYQOUHzWhFS zg3C8VaWxsAhllVo1#M4f<y#)zs9Z<%M%tTYRKo8t(NRebIp>cH4&fu-_~?CJ)mQCr z5NY;Lbbx@<g=ui*{EOP_MwN@I_9sv=hYX&XvNuQ$-L$bBqcVBIKp7fq$vvEziNBJt z$QtW-lFtUko8P4m$v#kBtKh^+{x=-tYZdR;3ZKv7YrS}#L)Tbxct5IF@^LxHFj;+B zs=bH?)mntkKplDhOtn0j9aWK5%|4W`0(YF!z2=J8GXo34kOxYSxI?Mmk8|PM)Vjgu zp{371))<pz#GLpF<EyX4<ATuJG88kzLh6xwoc13yHWCu>JcS{@^XAwzN3;1rSpI>e z_>~ClcBW3o|M(L{UGUODsOuFVI0AY9-(ALga=t_1JLxe~IKI4vR%pRPgXP1bZq%5n zbz=0tOtf)s0BY%THkn25Yv`YXZcj`>Q{WVyNtspnN*bMBE{~M>Qc36IVm-Ww=`@yO zblRd*|8<!dnErf6?+BKnEc8cMR2eXD_^qZ{r7)0OKj><9Nfe3i>f+B)qbfSmXARZm z{=Sr3ZP4a=2cRC-9wJ4iSB<}+?ekv14;hrh((!qM1`}ycHW?S0^sjHwrAq~!1ofW^ z`0H>*C`GlzH>ZFvE(%M_NCm>A2W^(V0hE&m*xY%KTRNFl<6L06VO2Lp<?vx4kYQ)D z3U#&bcV3PTLrduW!|o>4!S>oxn7#L@R;48;v-Owj8%xAL;$pPQ;Vm{g3)m&FFvQhj z7G(;WXhV^fn_{O?n`as#`g>0!pZ&?YT4%sg_d!2buaZ7YS3ddzXH7LZe;yE&-yTT) zQLwQ<efU1XhG@Qn4clDEg1!gInXh{XeEZIz$CT8zb$3cuu35)Sc^GUWo(@(~o;(K% zmW0MWD|zRS=m_H2Wl26b?O-A@s{0;IbPB%xJ?M3Jo%&SZZLJV?J4~#hr_uNnXwm+x zM4u%X)g<4UagrORS$YmEfJ;h)(GgrqFqeoOIJ~X?l65C|E0izjfrA7O7xER`g@vHz zTw*{eE6Y%V%GR){1_jPJ*!<3zcCHV~i29U}_cT6W(Pc5-|5~dZ^2*8L5PN;@35>A| ze}jDxxMFKQ8UM03rm{!JqK9UIUS<^j^S=wGVoJM2vJ_>Z|MKx8eJM2>(==XNX(z^8 zl;4%JBPRt7gpl@Bi8DuisPFaPd_u`@#-Y=$_rrQ}K3(wN=D$-r-h=45)RVlxMh;Ur z%frco;r}BjHR*eS8{?R|d&DMIIt*K}5wEid!~aw9D2`jKUxP$j(x|?3Lb-J_iuwt- zZM_HT8x8ou52~Oq#`7@0*;!;|VHQCxv_r3Sau?G}%)Wbgj;S~Ze^w>M#GTF9(~dm; zi4NNwJsWYyx41a_uZXlzemfXs|3FK9fVoR2G)S9-a<J*I?Ahp45U*UbWIIy_odV-Z zS79MJ^*QWRi>2Ca{l}Hx3a;T5%;`lm3JdvwnhRpTenT0bigG9i^^{5q`IP8&_1B}r z-}iN4`*8u6LNoXgi*RiI??qk)-R7HrZd#0z7&sk#e~uYAB~`mf9nn+x$@;)bz~^q) zr3Z?@;u;}6ol?+k@tN(O#OHq?%ulfRP^-y{_4pl{?{m_kdRCZ;+(`@%=C@M^g6NP< ziyf!m0|)eP%f$8|V@M?FOs-)v6gAcl%}NM4Yi?kX>+>EGxxZEtW&f+#Ez$q~=uS8B zi;qj{q#=`E@d96&-J%RHe&lfZpKee%2sv$4#%KQd4nMEgt5AY=%-i64kE-S7TZ@?f zV!}i;R=-W3{)^1^?QG0N@Tr;tNO6Quq@RK&DET>_1vift+P>V$&B&0@QQ|rkqXNc~ zR#1Hu0PYPx?eQ8o(~M3iZUnNE@fs(CP4@O8-5%7)h?dB!QVGZ7GU?2kKa8!pg7~~> zR7B?o#Sr92v#mY5@CAQz9-2slX_7_5*GXIhl7{?A!=)6j9EH_~>m!^V^<!Ptp7S4s zHj-t0{s;a8wSYjDz^a?)ZoIbHoxh~3DyVoejg}Ka_^sW@GZ!Vc^0t17aI;<4me{+V zu~bouAmhD|3r>(U8lklx9V)pJr13*4n_PM&YS~65TD)}{w%?O&w6=KI2zw+8xH<Ug zB_5BPPi6=<f&(##13?jm*@tsSi$_13MRSZQBauKFm@AYXXMPF$v23OhdIT)XyHc<o z*5t;@LjGYUKWu1+*2zDv_DbDIxEJrUBZEC~orOgx1kC^BA%4GC0mPEep(eid<H)67 zj2ng!B<?h^!g6FS%Ju49C2X}a?Qw_gJ24TxUz56ZNNK;(d782wmnt!E*izakgbf|D z)8op@X~0|rwJC5#&#8r7$x{<WV{hJ!qNr~P9gTSGsr7C1IT7`YgihQM&iBSAq||KL zSxcF-o8h20lJQw!`YwyucP6(IMCe&$7&Yov{SNExuvx|!?KoZdQQb<6vnV7Cm#(?f z8Ti-97-i{e{(PVEyVrZ}>-zUZKKviMj~WUr^s)Zyar7@H;8Ua?1R4yoteTNCGm^!F zyYqp`CYvCUnnY{usASrfkIAggzc&`qdePQDP7k}K6U2M1(IDOnT&~S}t&M+mLXo;( zqF`eLCP|}k;az80$`qA}0<0yNOdmVgnD?ZU{TXV+^2AC@Q-lecJK!)YA6XISL*tdB z;HeOc$4|u-l_aek1$P^Dg*FUXaDc&34P3xO9wwFCdTh6Moe2sjs4qr>zQIiqc5~9R zZjNU~3v!3q$QjHKsoy<gTgRQ)9Y;+3^_hwZU{r==u3V{B`G2gWbEa-%s`}9;`ez7} zTkTZ>!@TSKkmo|sZ})&n$8S>c;_2wDlX>kscM1MSW-~SE!_`G5I&(G$pbag98S41A z$i;fMwjD1g`TDV<IDE=<s+?IPFPy?8%F5wviyGh)u`j|hccRz4d^0e?#86=1F<D9n zPVMJ>nwYx2$Y)Q412d`CkOTQBD+;g^*E7`lIe90Y5J0j0GYs7M9?^VKzXyY+xRZ0} zKFC#$D-bRm|0ezgx--IsBm!{>JVH&|fW{F{w}aU}>N`EsV|O`K`^Q@4*%Uu8`n7c& zDhXrp@c!h*Lf6m*X23L3Ny<GF9p*}K+02VK*>Bo>@L8th@z`3i!%b6X6T!q_Q>kv{ zKAuHu`rRE6?qM3hwHZw~`0q&(OG)?Kg%7jwLF808Q#CjK@{=<g>e3in>b1tupen@X zRn<Lusi>h)dAeI1>ami!jDS;G(N5U#rTC`Rj1W$N(v;_!a>xb0SDyuwe$=9|XI$h> z2r6v<zzL2;o=TGTY$!)_C@ID_))F~1YsOm)zLha77bcuH+fRhx)=G1KCclsN?Y!MU zxm5IU;|O1UV1*$8KCpN(O<P0aKC^7$FUB&jI>sHw7t!3RU|ntiW8C~ZkV8%6H47h1 z_>bpPr}EG9!JQlRLti*jr%{grFSH@6lAw+(hV#UB+3K=0=<#My$2|*CLM0x3uE}<{ zdjci|i*DAvKEhu|yhHrrGw*;5lP|C=0C4IACp6ZrfN5QR<5LCM>Xb7inJNdXz=fK4 zV@YrWa#`f}zXRQW4}wQV;4q(ZY^q)QCBaDChIaWPV@XPtFRG_enNy&oBWfWc0tIUE z+Yx|SZP=&!RO88oBtd)+*ZdYg)b<Ev>(Ue47Q?y!!s;ag94~K_JGH<eCJtbmZNuPA zq-jT6zjVhHMH^W_R|c_t9n&D7ojo4-f5X#1GwLL=cQb2j&bncef>rmHR^N;2i_pg5 zW5#fv1DVGXW=4pZ#$cl1@UfTN8QGO8m9*o(z0mTV+Fl-$nfKg1wy$XK1{icAGa=M( z^1}468XTJU^1jA>#XXGfrrV=8SZtTG{yY`wl!K;9r!Tv=8IPM4`$VJm7loIts)blm zgx3xziZVh3bu=(vUjZXb@D3JAHJsqC<nU$_TIa#*Tht8zqkQ~+l_y_O9nx72_W0={ z*wGFAOvL`^6cHs0+2b~%lpc()CAr7>sN1vM#GvsL*@W5`iUtuEG9`DA@6zy`F7QTd zt#LPi6F2o27<ywpIWCq#H!TWwO~Y3T&FDGp&P2e;q!BT9e<z7Byzrb2#m>(79x09R z3mA+8{<iRv-zoHu;LIQG+@6TAvhHgX+^#wXD+J&@Xu$l*UOLRwo~Q>FtzxZe*Up~j zvlEW}1?+q=QoBxOv3a*JEv=v4D0o99(=WB9EQ5u;o(r{bs=SUmN%sbte<Yqtn#>`= zDNOdt++56F`v1rqav7wr3VXifk^PGZauS>dyhM8(xo%RP{x7Qxw5jHbm3*X@Uq$4N z{xAm5#G&v9X(hQF*bmXk#u{_`&NE2d*epI;;p_jolE`6T+fmnRa~q+S%Z*YZfZQ&Q zxsYIwd1bq^^`N%xRnd_>#yXTy1I{hVo$6;dI!+9Ud;72j8a8tO|GaY2{<(4{6D7}6 zzACh)8;`{kMZoL$CPZOEIFfKff%C2U@4ftbme22e@TAWl8Taai7-zsmP^@hr8TTUR z{_dBM8wZJ+@OYKCT-`-HOD`%9m5XYFmo39kotiY8mc<1LW&kA>N@joc8*cV=T`~8a zb`V(F0;;ufR&M28JWP^YCi=lzcw$v8LTlKUVACe>>W%X0fq6P#ruR9kO7aP<S~Q%g zq&VD?8z>xfEhua%7a(D0CN12#f<YRcLcIz|WXKB8qN$M-<?pU(>$I>J&zDBaA#Z-f zBVpGSQz7M3I7Qj0pfc9c5f%MY1;-z8{{s3sUi%_h`y><u;o=Jk>`nAjFQc&Fd~iw} z#E;JlnP+}~Oa3zQ^0NkinLjWiA19NU|Eg3AYC-k3wrR{>*)((T9BWgCl&rA&3vZyn zP3wWpO5<jeWs}g>kgeAv_6pil>QT<FqTqEk1>iQ=$ROs7F-TmUq%zi~&y%niX@TJu z+a0iA>Wj&l@4D$DqkVxa&hlMM{5+)AFKI3cx06M9U_9VvNWr`$yQ9OLD}MuwnDd2} z{5qqBRMG1a3`3aEx#(^Rj;-uu`h>oDc=ELl=|X9#eTJAwg`=bpP5v}=;Ij0|C}TAz zk(P~C;cMh46TqXQ^1|JDj<oo|Wo%Zhl}1`V^ZHL*#^`chQQt??^eMGm4FA5sPaV4( z9^lgu3*d7-ee%Y+qD_0Fx^mugi9}iM=YmQ2sHaPlH7yhNJRiW!6fN(WFElOhzqh&V zqhJs@g~QTDzjh7hE@Akke{qFgYIW|I8JDOvb%4%T`7|p8(wmve<?Q;Ia?5!HpgluC z>qol5oqx!v%8#q0^nYk;FX*uxx|%;9j*Kd9jEt^t4)*yTH4&IaR+DzdIbWJ={<+9( zHdj_p-|g}3W{UhDpXX%R2SfnTT4Mb_soJ)Gp(1|+5_D~h+xAy_W)*|l2>naVv&3ji z##3OT^L>28f*#XLOa|TO4~p3duyY4Sl^C{tBj=~f_+5q@U|6+80Dpl*r|yqMj(Z12 zU;ay)&dQ8}9q!_<8X$x*L^9<+YJ9XL#RSOrottl~S}|8Fc<Dl<U)>_4F2(A0Xm@=8 zN-`3A9E;sX0oHE`d=OJ*SdG1wlp%Ovru$JIir?0S;`8E|hOW;|6N64G_<O-yhMBlz zw|m-!J$VT-1N1pOLaUnF&#?t=1dqz7Yl!oIr5y)8&TBJX22R7jEeD+W-eLnN-rSvO z-Lln*iT-}ppYv2pKed`2ZGK;YaWkT)ij*iaNU#(l_##QkOM?Zq$t9nn7pSUu-+eVz z;v>CuY4NAh;rHl7^}s_}92z_-T0ieHKI(p~Ks3PsHrmPx$Fn->W?dLdSgO`njDcOR zrIq7=v!nx!k)WeH2H!zj6k%DAL?e!|e&ixr#7sX?CSc${dVlSMAutIILZ7EsiaMo? z(*`fkrk3*_a)nOOt4%wdg0gc3lc#CdvW4uXT!gnOOdAK^2$yJ7<;GZiYx(OBw8ttQ zETV^os5M2%=+9Bb7hhySH!x2%Iv(v9vcNNMXWyuJ3vI@4pz7BHT;yi!L_^~%PF&kT z9(0}<q4E{=o^Qn%<~t;rLRk%{31yxaC=gItnjK(NkXE$gtSV+m@PVGjvEi+>ut}Wx z0as8T`~V-o%n%?w{vCxTz?)8Dc4i#CfeYz(d$^r<JH8#C$r9Jp@P(u)57FXY{T=(> zZiU^Th(Z}eBfJS=$va>eWA>BEMZ^MYRW0&c5;8d}In)Y_z;1EpMT47R)2c>y1GS*& zKD%J)p>f5mL8>;p3Jl!OZU5KZj+(hP(nQvE(bsrotNS24Rfyh5CqR_(WFg)5>!irB ztY#h4sJ8xGT+cS~ZtNy~U+E5i`E<}60tZcV2&dxFf@8XVz#jb2GPzF@hX0pgTi?2{ zuSu{*`ijQ=q=;=%<2?hg5@dD|cZQJ5uRyvHFjV%}>b1U&n>~<%hFeSso^8FCppdzg zuo_86Y9iTq{`a>=&7irpPOoGvLNEA55ccbG?H145l6-*In=|mQ{zdGpv}O>#?eo>& zY-RfpkC4&ouYQ$kR1N7~Hz)zKFNdrNyb7Uw5WJqy${`xgrZ_E$QEd#kVa^Q<Pg9ux zlZ5wO>#6|j(TcmYjtY)gkw)L}ripaN6rI9n1rA9`=9Ab@TrbFC0!#eSPeFxL^ct9$ zl0llF1~~Zep#`I%UO|@}Z6tn+(_2VJ9ggiY#lI{Ei<>s%BC|WQW*Eky+xB}fk;sa^ zOmUV0D4l;c$#e=Z^Pb5U|4)F&Xg=nA`fGQHvIChy2-Gp}Bm>ytBWl><K+{MJcEv(P zNN5TTyPCkaYV-*5ndFaye3f<L<1_P5z*0HWAuwdFcTkq>UplG7%%=PLlitb2JCFW@ z4YM?MQGBGG*cGmAoYa-@9#%52)1rSW0r0XtXP$8R?#`tJxNS8s>D51#5j%9)5BWV- z;LQt?kk!}cfOcUrO(t|ZK24i8#NsBmZbnau|7|SOvYSNyAN`5izxorC1Aoh7*Ap4@ z5>9(u6$lW@D;`QUH{`DoATwsNBw)~KzPSQybLx04uwMz|#z-jp%ECd9a3&9)A_n%2 zUXZ=h<N!mn+I9Bj%*0_M(xGq5_cRxqkawTQ<k8$3bmqJh(iA^~6i-;wIiAE@^l<E^ zpxN%Mp^F52oS4P}l5u!~V>QC|t)^C-r!nkH{un(-K-oppMs5piiF3#Uzzt=k)-p#7 zfg6zR*$At=@_=Z~VMD?WzsXA5jS$6h_T~93pbwU6<G(k>@z3`y-)WyQa=92VjtA4- z+MxX9C~eo>1MaHB^>nc32l^n;gIV#nm>+5#&7{jjhZAH|)O90a;BH<tK6@nyA42SB z;VVIj<ma+cDfAna#h238E8w}7br0Xz^j#KgbCUsz5~EfVq>TZK9O$}CMAz-7F=U<0 z7+8sR^(q5PY6zXXE=VQxxqj6Bxlnak$n)18VVk|vI^N51MlXV_g$T^<{d_DE4*?jP zHBCF+o?J<K&Huh=Hf((-neX6y3D*&NA7xtFet68|3y2qtx5TPh_i|7yL1>z-D5^h| z`<VWF?`(!+;^O`H2YeyNr9D5Y4;?N@BfF8(t&orec|H*e8oEcj7(z5XymcQHH1(RU zA!-#e>(BwC(7ZAYISnhXZ-(2qglL3vT?J5mptED#G5F`u#1#vXY-aF1_fWX)$f0J~ z=|?@}!zvLww`lmACVzpmLmp!v!%^NM8SO^E7X%pWtjkb8=AKx2_{hoL{Xhh&>v&bx z(h7Upj>73GXV4F=*+#Ec$qeX#U+=jyLY9FA2}TSX?`^&uTN_3vIYT<Y5zby^S&Y~~ zXAM>Nid6eq_6tej;N?Rj)!X)xo?_MJ+hMw%B1tv}e39+WVo@n7<J|_sI8w^W=64+$ zQ`06hMzRzQtzphg0fPa~;=?#fy64&r>(F<Q3!T_1J;%g%v%Fv=Ax!99&C5FPk$rCY z-wU`81fF*tLIU&9O-_oKwk~H>R!4`Tf!X5r(w0)C6+|3$A6bgp4Zw~~_*yyWY59XN z4hUYmK6S5=Zi}`>uiY50RdrOOM_sU$FoY!_h4(26d40g86O@Is9C$;_S(E@)0>iDg z=ikd`mJ)9BNFgysrc~NBj6%^OCX<Z(wi@6vPO<4hV-qUO6964kBOC~qq5&%3h(xI6 zkM=%sdgy3P;C+=^E{)CBV2xVq_k%uAr%AW2JfWn=E72xx_+<+dtOEejO`v?c!^l8& zbp0<=35GdZ>Y<eoSyuIH_1a{Ho{+5k_N%m--`b3!enKL$5OlHL(_?SoiNKhynw2v+ zaCFL~n+NB+Sz_r}&wY{9Lu0G`N2@DtVPG*Up~Z4+B>2Cfs2y4fni7T6#K#Wb&LNbn zS$onz^`+X?Ft3=JI3bW*d+HD=<pAuT>n9kLp~picsrtXDCr7Q7;B|NO7@wI6e0oBY z+snRPL?@7V@Z>?xGxXQ4Je?WMlQS}n2okqxz+7FcGun!}l?2IJ;;rAW;Dsi!CoC!d znSO}rz-r%HdkLb(t$7v3fddf~K-O|{1CY38{ZQ64FK{IXhL!X?8O(v%h5K9%>K3x9 zoJ~9PPM@8{c1TC&(v$xYL6Zm7>)OM4pfsWACA`d61?kAeR^~!h#2=a7jA{AeT?ksY z*`B0h(<>pUv&^P_T?-TNl+IT}x!bIpKn~kK`Oy%UD&X}FdSYaw?g|LN!f*<@0vk>* zk!Poho>;<6(!az(nCTc-v}CD78o>n){Va0~iz9(#@1!rEsy3e`kr|Vz{}3yQgctI` z63fOE2B+Hy4Cns`u0giTn0A-9!Tu^MJk9Gaq{-NtiE=JqCuu?74kHsh6-6ra4EA8_ zwQ&M#o9}8#zM$I_kn^)qGVX%KF865s>xc^*ray{bmK}q2n?MP2Yjzl4ofTuKkH61s zbqeb_>-`F{9~)jlW)`FLBndoa&NLF`mnKQzm<~l6K<$?cH#N_#%G!a!(yuaAH<z#M zWEfk)Av3~XUxDd0hR08&hepi3!iepB@1<~)wH`~ptE<Ymv+pimdS=LKHedeZe)gQ# zc#`X*u-M)DZoeIH*U@cL;1J+b;8NMs`TlgGer0K)${qfR`PbqM$=u7WYE++++cmS- z$3tf6uriCC5Sl-7N9hzBWWhB8nLp;A9ya-n<xZ`_f;{w0%nw*?$t&cRq4Xu2Wq<u# zjcYkecr$NOi9`M=F83*>WSQtCIJZ=9?5?SxeRr|+3>%H_ZBSrj><snK<09hDjUo(` zrcrl8lxhJ^qy=>NLD~#I)V{9gQ+26|+}G&KN92sUD-8I&>cZQ}TP+GToNH%y&<Zj1 zCqA6uxHYKx>RqDT?ZYLPk^Fs+^Vjp%+=q_s=kfTNjmzQ_qSOf3F%RLNap{U!oxan8 z6`mv1E01c%!XsGIpk<{$jwRZWo2lR7b5k`k9q8;t!!Fg6i@Khs7Rj?&%f}HW>aNS^ zHEKkO4+ZWj1(35U?Fe+xIX>GfKqllgi&F#;B_n=k?0!{l7d$2oxNIQyeSX9CAO_#; z-FJ`nb!+Wfss%LA<r$KF%4CiiMvLUnW29@{c+aG1>IRB%w@<Z5qLHYKf}SuH(3q6a zNPmZ}^N(Rv*=qbr%%-i_TP-#w&siE_&qVyyd&JYWE9<<=mlu3Kw`(De`R;xKXDT@} z6iG9i;OdY&?yjzqlu5s|RTN{ce^P@AO3hTK=iRr_=x&?5FT)4g4aInIKtziIaI#Fu zNa{Ii#a;`vlZoQvCWh$J1<DiVelFgJR%Uyz7r!_6-JYI{Ck9q~-_M@kcQ5p5JyQJ+ zwsd}_CunTlwQWr1Dy}pL$E{}^;~ppN(^|!8s|d(`x69L`@aI_kG-2)hMS(xa?iC>= zIHQB6lI2$fwyJIWq_90UWvl5QV7a7k?}pm$#>v}Q1mk;9>&3f*7iDeam*pSs?;VXX zYpA?Q1X$g%mkl*f!bD+&x2pJ~)BBcxQidX=Jfp%EFvfBnpDcGY%X7JS9N5bfBUKOg z9Xi7Dw&tHU!5kQ0FnqKXd+<=Q3uxNY7mSG9nluNF%&0DgxJAiCxV_gj?1Vxtd`Nt1 zbHgx9<)cFr#Y`=C)<QPeuul^NpO&$nTvTpHvPqz0uL6XEkzJK<GyIU1`!5ghFP0q{ zzqaydsA0Z^iU)j-wp5l(z{Lsffd-T0K>Ko7i7uxYCX3cF1IKSi<qIsu>EtrFnU47> zEnLv{lhMnw!?$O&6TwPm`uk;P3+noP^x$X`WIBHJtxHW`BCDb62QzI0Y1QwP%+8CU zj#IYJ)^q}bH1pAVzl|Kh$Cy`m9KF=O^JDrrH@UpFcU8Z&M`0n4exZ(f`SBID0wM_w z>^$I^N@MfE;r;fh!1KM>+3#b|dziX9Q8Er=UNEO~$O!9USiO`aFOO<}4=GZeJ`rDD z==X-e9I0|vQaSY1dhQccKAv(ii;7iPbs@g1%L7EvtduaA_`K6H(d~mk;)AHEfq%&< zo1U*>;<Dpe-xH6qYWfY_NI2KcXzwM>((EF06#YpOw_@j8JPr+W%olPcRCo2yrP09& zVfD`+x_jw@*;}3GoxP@)ZMI#M&P^E=5uXMrJSLV$kOf|1bA|feLf){Pxx5py@gwP7 zjOYMpV?M<!qm`DrDnmmR<#Jot+7o7c&N6MG?FWkTmAU!xybnKP3=eeuFLd5teh0Yy zUVb}&W_;~wy}0n-MBcVNGp$girLsR<{e&x?aL}n6CBY^qmXwZMPLV0kIX@)_XJczR zatHkE1Xsm%Jw?9jHOSugOXV&>MW0Zexv`_yXTE-usUqBJfQHY5mO@`!%8`cOac{Ue zKipjwxf_@%R;fiAL7u_U^3I^Yrkz{8*twV6_VA0!&i}&U^u3*}#&_GxBX?V%=f2%y zVBa<6hPJEVr>&0#23Z8QP>c~cr&$?|1O|7xjIoPQbrU>hzmBx?(Bxe$<!q|-7j@0> zG%2$*XUr$zi>%I34Dj|f%DGScTzP{9woeNX%(!S`p%OjF?yKKM1l?!z-j}Irlc<P# z_EP<-K8vK1m?><)m@<>VcIZawJv23NFU;+8LFX>5bhp3K^Gn&9DxPe6+ykd~G5jte zya2pUx~a}h%15OFduJxESk#~DRc_w!iZ<MrlEG7s`S+(9b{Q6o^w;|O=@i}f!ZoD= z95=SQ&(&Ta60>=_$U_Z7e_cdL3k_Uw$z2&_#qNT+sqvd#@ZWUeaL1|3xMB`At1n78 ze({=?9&Er5{k1p}H@5HDad7O8viM3zRq${;&-gNiV(>PssQ0inwf+mCb-iPRVpI&T z;{m~Ys3L8>^E6LB7OEh2%^adp9?Z6Jk=dUO2G>>%2?bR4&{&G9iGfT@f<fzo{L~)a z&RI6jtT5uvQ^9<k!dkr&rip9JoR|+p0#Fr{i5QfALBO8u#@4yisRyWhQ)HjrKMA6` zqRLUnq=R7>uVGK@1<&)CA0S=}INT0+3J-Xyr}Mwn!;U%iT|b|C%Q#Vp|7q}EBr&Im zDXgnF!5UeMdCN;PPV?E`uRGm1b<k1RK|Zn2v0?-)YS{b=J?2wXQBL_6g}2DiLI`tC z5o~mM1VfpmfcVU=D9?oBhpdg~ctR3gAIHbL-7G}1^IsG{b)#m63K5%<%2@R<JcA^N zFxAp*xIGxVMvZ5NP#|KV3T2P2L>Sw{ebz9zIvl#fx9Q3)3J2H(j*kxG<4;K7=m~MY z#ERD&XLdz`i#P_CTT35RB~v5HKu6K#(A_FuuFyR;p9-6a8C&LE`t%*(!Vk-rURVvN zz+T43Y4o_gZwI^=^9i{WHZ=oo;?SmMXA>yCd59zYZjV()i%u<L91<2V+1%=!TpQ*V zBpf}ltR-MkdKrAIE(fVD+j`HMOoHNglW~R@j;=5(k~^|4Q5v=4P(Hz#ZUCc0Wq|$n zFi2-i$|x|5Z-JJh278Y7_vr(Q7wx;3<pw=>R6?$Q9^Qw8GEUb99hP42U@C<4a!Q@0 zyoZS3NDvFxx{)emMvg5+^A_FvbB3qCo2Hs>gPAj{(2;iy(k4@3A}k5Rbjq}uYawjK zUZb}g*HLz`Zn}g<<j2uDXxS*{bR9KVbw=R)>yk*2|3*m&k|*V8(t#Ri<O^=e<0(*q zOc}lYUMlddGh_Lbzq*OLZTCJ^9Gz}N+4rpPwfLFHQg(@WmfQTbGNK-K8)@+(Vh#JV zK<V?GWh!;7WIhRH`I_nH$Mte$I|2CIS=96SJ+MdX>OJ_7wZRz)XMM;?f3M{W?B2bi zsrT#UnfI%0o<}A@|6e)1mvJtPy$6*ML@6%x6P=^#?vT{SgPT^$yRsD)(oHehZvBnf z)=6d@?aA=!_V%v`bW{y^nhm<nbZH~`Ub|1v`7gI$9`f#g>AcM}_E5I!{5k2Zf9F%! zqT<3_@WGp$*30bqAdOr3od(|~8~i&y@*tk2_h`^>b`!~=UGirb<^kiOKsH)2bEtq! zJP^YE$7ypU>vW9|R2ASTeowdMXG>hJ+aaYtRrR+x{>Wu4(+GK<^f6zph0vJEMM>PS zg&D=T7R^s&Ho#%o0EZ^0npG^asXwKbzM{?C^49#llxq<VIW#@QY*M@lfi*k_GcAG? z+(Y!Sk=$3AmuzgUU|$JxxISvDBpJFtz>e2w<I9ggftRdpjZ0dmD)e%~#iuw!1M~Sd z<H7NOAeSg(eI>u`fP(L#=YA9WeUE2(G|l0<Y@CnSOEUk@^zv#p%vz@wvnR2vtvJQ> zkUeLyB?Oh5jO}@88s|4rPde$E%=WkcsPL*({;BX>u)RK!XsnBChaQxq+{mUPtrEKy z<)j&eUU=QER$lnOB=mY;^ghp@0DrWgc74Ktl5*gmaQ+w`{3KEwQdJ*a0p|Tm?svkT zBF?niD!-_RE=Gt@Pa9jW&1$C*-L5?xBcQ}D{{!I!se)J;N4_GuMA!^Pfjkg<1t?wu z1%S8GO4O5BQn5duVs+_-9D1QNXG@L<AE_PFCQq+_9uK)I$q?f~K=ZM?i(c0Itk1&1 z)9qYe|DFr)9a`o6!)nllWS9d%ovW+zBaw&_Co(wc|Do+I{Gx8(Hh%;OVd$=*yHk;r z27!?dk(350$swgfQj{D-V(1R(md>FYrCVCc{os9n_jUjF_dL7%-F@~CaO6|hb)3iX zKF_O7+wa;AUzXn<nB14{DPNE{7hUqZG8s6;x&%8goqk5%sEYqqu=BR1!K&bg(Nh=t z^;XZ_Uy{eeQGQq1#z|53l0GY=fiJ=F!ynI>XRY=e&g3f}dn1WG8WbMLMjIfPSfoub z1;5Y?GI0qMS`Dw^Pwr1wiu5)xFM724)}B(SX!U*twva|c*?!bL!dolu<y9?}gcZEZ zqAVt&S0q`$+sE&Vm_~`cU`Yy>=uN@=1>0t-le|Lzv~c_T&j0bv0=e$Z43yh%eV#iG zz99V?Ah_!5pS?9FQRgD`(r)_fNN-ka6RKpmY5HKV9g|i(S{fMymZxZ?Ai{w>%xfyF zX6!9E=TB~F{YG8`9()>P+B8GeK}-(?9XPU9RA4XTr#bQPv{EK3L<6Gc66*B`<*@DB z;IPGvqE<@jqo!!vI)1Blis1}op2`{5pFv0r?OO@n264|zLt#^0va#P=mUo5qAM%^! zt~P&nvmM2fW<>qm&1x!Wa|BwsHv3!(nrgxW4kC>NtriZ7YYPTf$~++)&iKj_A%=4H zcKI2OKS@_n@8<>LRjA)Ba{cmjqwt%=D0@foo6}X6%pSSgpvD*P5$;u8z1_(F_iEAJ zLY4&Vx8oHbep@GY6L@lX`8cyl{vhDCGEvyLsd3kG-<{)9LvK2&k}6_ov4HgP{-_>W zGw(Sj?uKF<U6uIfYmCpxDt;yGC#>2Sr*wM}_E(q5>t^sFU68`C)>Gw732Hj^2UPJ$ zR3($hP=i*F*#o+7U9hRdvTx>^`ASyTO=Los*;xNvYa2gLBU<Ph4%-RJ;9TkO$<B{j z*6tICF>2~*yo#-_u-pFD0~_sXO%d&Ftm%5-ClX#!@`&0j<KArqW_m8?{=OP4Yu<nU z*#B-9xA(Ec8ceJ*Ol5W!Z@|AgbyzsVROyT}!u`qzo&0YZxgxmvw9YTY6<$sepcrWA z=rq3yzgRpyf}Stlg;tgQI+XOB#l4*hSw7o1k(FxLWZRqDQXB7YC2aJcYZ(?yuTFjW z<X~z=!G4WK!xMG%1yMqz(Xtw$1Y6}zAM){*(4E#eo%?yzD3i+>FELLzH?rqik^~+@ z0#f+2Z9e?f##!}h3pxYrePgQll-0A7AGc?)>>_6_t(TJP$m6L>0+=i9S!02N-x&GV zna--umEw&cjR!VzA$oZxMKL1H>&BA|&vqqglWp;MS>dUr*He1_wYAq|PyIH(UEF>{ z{k?+nsUL-6Eux`L?D9i%tSk0X4*_LGglk2bDqCusQU*cVwN0*SKHidY0*7U(zfeS; zN?~OOf3X;Q(TAc@bnlS`IT%Loq;NdRYW+5&-#ihM|5o2-%iF~u$KS^#k|z}$vaD-| zqSjxYVsAs|)D}jqof|Fqb7icLzTRTN9*_rT0%b;_%Xpl(cR;zm5ucCt#;O!e2dp<{ zcbd-KW&DLUks8fI_C>l{(oNJ1IQ1-I?)r@=CG^yLu0p&-kWQWh&70tyv#)l8h3&J% ztE}f@4WT=5L#bo$7%1WUtT>AUyu?B8Il1Kvr0dqEJn@o+%=7Bo<A>zGV#b|W+#z|e z44sndUthd~%X_%1NV6M>Bzq(+CY}f|j@8_v;VhG`Xw(aHQ7*G#&_(7D!E|_GjK{AN zY#V^4r%QtePhZw|oUa3?+1NRX93D0u5{TfQtlQCe);11(+KFw32g#fEptK+*OHAB5 z1{2H`^_e<z!I-NhjP+(4SGtMT{W<I$oSpsImCE&)zMn4!U#&$17&;Kz;KR~zu7^l| zjKN{88wWj*tA+ReyW;4r7Y?N1FM3~<b*duEeG_HMjPO!hmrdmrWVLuYVgA`XNbxR) zcz}9dB2FPmYzhK-nXK3&^<C_`m~J!uh!F~5O|mPH%|%7jZN_D2o|-K+lQs(1W_nF_ zFS>SpoJF+!Ug7ZHUcleH*ZIDlhN4}dq*@_Kr3Xo?$f6At)6z9(n%uNr;yuj~-(K|c zdxR_1>OhLZtnR{v=?HSbXYDV3VPYadHzMMzSOW^e(y`Cx2)tBmL`G)|1#-lyl;3F% z?9STLFVUbP<DwPn^A;CTNNZXesGvs``*9W6*)<Z@Lf0~9j(5L#G<hw@wjL(9wzy0u zj`Z^|`LkU|?CD+x3^bFtiv<%QA;MBM1OpT27=<qo%4sj?nvxiG2MvGQ5~!wVS%g^V zd1>je>1N@c!s?^M@Z*Xc=(##v5vHxPW@1M(RisNdqrdMWI2tu8Mb{<rT{MtAWQ2$b zP~L&wzsp=xlxMi5OTKz=boFS?2uc5xAgNZBC-f;%A^!tGAFCJbC}$=_t6KGA#}g~T zVK&$cB3>oH(KoWYND0;&`LaS(bzj>bMY=)KM5T&<;WLD>U1PoaJ|?6Evs`B_W7+GM z3L+2kk|$HCJg^acubpAa&@1=dDzJan$V)4P;FizfFtPAkf+Qwm!SqBkTBV76<TT1t z79uim9=-UUTGiXlm$H=WH@}QUES8sVGoBB(oR+e{yq=}bxk2pE4W40@Jm7VjRlNUT zHPqC}Ua5XzODrrd2XVyqU#+p91}<spEro#AZC}#X!@t|4a(%mv6?a|93%jd}H6|}j z-uE`BZ}uOB<TKe=t{pPPynOOebW&xDpdj&kkbDK#8mD)J@_Zht40UAg%u&W_UO)sZ zv8t=dyXHZcr3Yj>olMf?4V?TGCMvl_vPJDM38zKG)oqIS)J=H%o%i*Yts&Or6r?_2 z?JW2e5-tvT=ZbZ1qSBQ3egk|GI+L1nA-=!tci0Xwa+&@Ckpx!qU%l+sSwcwBJTrgl zUCf*HtbfQbPW3-BY@HY;ky_;Nv`TeB^QYhtiH!H$?Ji75RO!vewXl$Fz}N?GJ=_CN zG8ED3;_=U=z(!-t4V3D!WIrx3?;nlen=2(E;L$wf^+1qq&|@Jn>t9p@Kk(anG82yN zud#3vsVV$>{sJtvo6rb`Uj5oM-Wo=HN0b~HEUY&Sn-)m27V`s@`6{PM&fj`b!{~P7 zAMfHvjz90>)g(A<a8fviE23XD?A9{Mc{zK;@aTQm-Fi<MXc;;2O^KvHgfPUo9+F)Z zZ#}-2L@6opzQ7M@%bqc_Xrd}U!7O}M10)!n#f}=Rm%G!b03LVZAQq7Oc9+Carw5}* z1ZiP`jW}j@VZyUYq)j&WmzW=0`mN^0{?~05^(I(3VcUYLd2$pdQu6EP=helMGPOI8 zuq%4Hl%v&DFi2)iKcL)NuWd8os9_JreGbJS@0Zd#F67PE&+O(Mm04trj_)){=#>d3 z8Lv3|K1;dCq<2X=i~!1?la&nMKNb0aX<&^lMUXJB@@L0j$lNvl#|y~5e*Xdrc*L_H zyVR)W$a%hby7%~HG?d0`<AxG<6KbGgutkg&Ka0wS$TF(bDto6PH^&swKZjG1D>d*Y zbe$h!g;(-m(1bBPn)GG-2VK)@eih_2zGe#C4ER~%7f7bW+0|Uoah54?-4?IkRF5)& zU&+)z68_~SZu&R`Ylh4Kg?xn37XlrAw0LXNdRYWF_G_=Xh;qHUwV?|=rdYmM2~1pX zL5G94-cW{wRqybD54<YPf5;t=9F!4|$Dy{p+xp3hzB7g$EgKluF`Mn(p%i#}`64X| zPL2{?6_jq{MdW_mPuCY`UwA+#IswxuKpB-BE~+Nl60g?zn)~{q!Mx1p?8iFGA#@+g z{VO}jqyJOJG!T?@hSkwiUi+-EzhSJj4O98f;}T6{U*c|<_uoI>oIlPEc^&hadi~rE zIcD#|+Zmo(9}<iRvJ0`}!4^uK-%-n-PO8%~_L9BJL#uk*)Qq&hUwzY76bu$?D>vRZ z8uIzZiuIbD<^f}D^ca7FdV()X@@i)`KjSOoC3}tb>_Ayqw(>`!H2h?i5F7e}Jd(46 zyNKw+xdp$CNkD@@2GE}_DR`Cq#&>#WHF9NSq@atwTU4@4_%U(~u#)MS<5Rwim|-2- zsKcmGcVX^R`DtR65BfBO%md$nE1jO5uj1Nris^UnK6u5gA+3sCIBRDC5q(o;&PUZ# zK9kQhS;aWlJ-j&cm+tzcTGBUjMJ%jpRbir^?gl9uvdyVx9Nr=2!dBy{MceXT(W|f^ zRJV>j(?nWy<Qb;wSfdyhc$i^q0@?Lol!#<GuZH;ufdwTsU4CsGy+i~FrAu6_HIpe= zGh!H@RXGDUUHi(L>)q>u)HAhC*$&hsEVB2J#QYjfsHfFd;Ph9J`3BtiHy_FBGisCk z<HB6uBSn{*zFEpi&XZL&VJv*KnmVhdT(W$sl%li8PaK4$7}`D@IFL=SUNDoGov}Nz z?r=7uTJ022HabVfw>B4a^2RU|&h#qeiQPzjF4VxwYMuH+CfjxNKG>`{^S!7AQ*5B# zJUR0)s@VC{7_LXgZV-+Ue9`@Y1#HWeIh4B)!J3~%9H)%c?x0a)Hyf=+(d*{q4%K&B z@gTwu;56OXv9(Er2DTyHo<2PFNHOi{L>VT`!nNFDoiiwayTF()C#@L=1_!*oq_AXa zO|yw$B;z0=6+*IfA-cB9>Hb2oK^E0sUkoVO;9Zwx@<{K>aXzg$F*LcbZy30g$4sv< zW^}(Uz^0<+13)-7r0A7Mh19QSqhJc|$OFJIGMNen6&%M*QdxqKME?N1>{V^{5nqs8 zB62dsIT#B!v{XFK!+jCa&-eY}s3Q=?NmDU!pN!IAqVkA7O8iYy^utw!&+yw4l;aPC zUaz%1e!QV-y6KV<S@LpS2@G1*vjj0BgLQ%q+ABRni?PP6hR0n&BJpMY<Ibg9?KNt< zoL!(q{*&Y^3v_}ff4S??eBpc;Tb>K2La&a{p;L%Z_<GCD2j4Q^Lnf<nUtEI=)A=C+ zhB`#;G48QbDSp71(BM!S5#rvLlKtaLDHWWh{c+Uu%>GkH{fHRnwAy7k=vA8cF3QJv z#c)N??6M8f-Lv@guWY7mw7k1~4lGVY-{nRsFDeVVXDpc+Q#$MgN{w&#a-x%aIcp)9 zu`!`~8T^3gkHde|Cg^(&I~;!R(D=A6)9tML@qlYr!>-MG2JqPQ(DftxQP9JJBw5)z zV;yF`d<-Gg-*b+#WW-qZ7B8Xhev<Djn+Q}vrf*;1b=7}>eRLbim10s4X$#h+v`8^i z4f<BeVw)!7Ci$~Ipgm_}@BJuZrv!J+<GmYX{;kt2pSG74vT@3nN1)+UJd+zc3vKX5 zBJ-L=usF!l-%Mc8>NSuk#K9uPRVH6=9@k!_C2&97<FMbh(FctUh%^&eP;S|nJM0m& zJ*Cd-AY;&GDJU>se~2p<t<|co?#V4HL_*lLI|@(OphtoT)3bo4qh+P~IqXDno#}J2 zSKJ_?hrCBnPden4yg1}}KH=(oi&Z%i=mI<WqmEcvNTdIJX>-pHrD7k&9n(g*<i~E_ zd7PuqiB&(SdtAO2DWQKG(!0{dy3j|`8KZS6dUi0x|0MFdZVOL4|H5rsbEG3M!tP{{ zA_nr{ae<-sD-vClM5saD_*m(N6GPd7h}kg&7I%HDDQtYb6l7(@-}p&)SHd2!F4x@W zbnc#khlP-N8$#>763tQxgaYI9nS<>tMcHcn5G)o36$4rzZd!6QQFQKD<qvQ(NQ`@_ z<fpnx$;V?I4_Z31VCRvf`w1~>AC%vs*iy;z5YV6Z>`;W5V9bT%k&>|`)5YUIC6}fj zb7f@r-b2@xMAeal<Z1?ue@|ks)5B&2?$#BEK04H#q}>r~;5jB`QG3Khb|Bu7!!i2k zBIW!@gRL*H@yg$TVMv~YD|vW0lC}GCTv&kp@PlMX5wGRUdrlAK?2^ff5l3xG3q{qE zIsmmjv6D~<J@?}ntEf_9Kk0xam376tQ?D9(R?6u?HpRZ0;pF~LL)y+7M!6;<2loZ# zd@P4(9(%0v3df0HV7opca<qR*aet@Jc<@44d06`%#6I*_G>AJEb6m3`tRQvIa7o8s zb37!#H-t-^%SK(z-IC%7qXm@(y$EMCQB1z#0zduWO<pDy3;|(|-P22V3(vUS49NxN zDn<@b==$K{5hG_~c3K<$Zh&*qVEEzktUP#w$yQEnE)!)yV)VMArz9bs_D1ovdUx06 zC!E7ud+di~aC_(hYED0?LOhp$9L2-8{=eTz6X+p^7^jUP*L6#-afkn9i+UV%?%?kJ z%Rf7-zJmM5BlOaLB_Pds?>~Ft!qXuDPTZ2R{yuo4+^dY^M0==kHqmwEJvUgfdT|6Q zcSTts(l8PI7D`gX33&9eFPRvgUE*)qOkaqHS96Dtw~_k&4AH612wt68gEo%Unc)*C zqTN)9f>%m0s$jxmuB#R-`_~m7G&ey}2R@g5n=t5VT4ho7L1%3mll&XF!#d5{sv!eX zwq0`CW41gtbwaWTTj{TswTT+Lk9{@rh#OV9SCA7-%UyvrUe=5Ke9e0E%GK-Ttt%lH z)e6TN?ED}sbE#?dXe6`Zs};BPiTrnA+MwNQaPV2+o`N!1g5q)t({rWcss;TQO6TFK zmwgT{>P+3n3xqPsM+~uZ11XZwgd=T0L0==JL-vxo`_nC@&fN&#vo1G<tf_g@cMqIa z<4+b>QSaGEI*(<A!SpVwLxPmT_L2$AB^nO%Y~$WN@M0n>PsJ-u+0T;&odbKUJsLEv z-ETq+6yzWip$;Z42R^h8oz|3r`JJ+nudF#`yxTU9OZ}bV#kqEq+^dYf=fj2ezgs5z z_vlg%fl<8&RZR4ye?3GVMjPcwq!nYA8GoSSMI1^5Xr4;B7qcwv!AFsDG}x(6F9uP8 zy}1O~n;RqV_vS-jI@$k!Z??ODpIsbRGGzQ_&33=h1&gGU2esWD^4vZqjimGT(ikPr z>%Y>!@_yNVn>UGDGv?jFIxj$mYeB@L{EUoY?Yl_XgNC<)szW%1v%AG`?2BXX*}iMP zJ#qADaffv}%bpKpz_vV1i;8%NW~GY3dskD*j(eg+=t9#0MyrynAm0$+|0>3=2<Zmr z*;d{^J5?o8<n#O8=-7TiZgGHPJU4}8yBS}reZ}hiemtkM5n4)me8i1eqU(B_I;Nt> zF@6xej28;y+J#oW`;ic`fKo45Rl(7aq0ZlUdBE-{{0MqU3sMV6lg6~~nHl|iw<&tf zh#`qAzJGOB&8b=Fhi9v3@%2c%TR2{Ic5T3{kHlZ+oBpF`F>kA;0r?Wvx%FcrrU39Z z?q5N4xBKgGOD_bK8c7>jTK;QKoxnT~c13X|UmXdlyBg5{$hX5`_y!VXJj3wdlt208 zRJm8R0IobS{!~it3)a52AUyM5=`-|>+c8;@Xlaue4(y>b$NjrOZQ&1B;=a(lGKRdA z;JyJHwfzU5+Sl`H$0tABSp0wLLRxR$*#Y5-B02cvRU$pz8Xh?T_=Wh<j6mA7xRbI6 ztk#EzNA#lr8qJ+I1))ozxtI@=PAd)jGAJYwiJj<xBX@^|;5?byf`#DnEUD$AU7#6R zXhwNh>k>z|6Q%-3unMs+$4BOvCUAk0=-gThI*+K=Nzan&&nq#m!$H(}oCkcEyp+gG z!UOVzWLkfOm<wNsdZr*9Pgg(Qy{7yXO{E7`<)1>hL_QO}-&7P^wBTh<`S78|fvD=< zU~YWYcv@<T1pb)HHdE6aCt11gp`5|(tS2ns#lc`+uh%`_i)Hqzf1S${0TVq}FT}sv z^o<yL{%+U38?Sd1+fUr|^E;#tJNR(BnYO9`T2ikt{>c$rIxdf$L|!M|o|Ib(o~lj= zIrfb2=BCAxkw+e!+O?4*bSh7LQceU4C#z;%DS)IcE=`3wmG!Qb`?^{uTZgl+HK8Pb z6*d=;=?prR6Z%R-LLVyoQ}#>KmQ82><+k0^X!o)#c*bHTAP7lfXeD3wXsxS1YT6_} z1-6zK_gf2fan`qOKz&#cppnLbPRK>d|AUvk_O!ZK%X*b|V4~hFVLj~gdtpX2)IyCA zb%$RBNTI9)QkdbUQtToL%Ji6<P3#?HLMcKh+vSg3Dw-4dUFb<1GV?itP6!;dOnbo) zmgY~>4e<B4pw_USA7dmy#;1NWwX`xz{p_0OI+IQ4lxmUbN9E3z4aBo^yG+!=9i}r} z+;XJ)K&=najTv+_<2Co*j^>H}Fk-m=TnfkZMjfrEvg*t;^CFBg(9H$kL_+o~j5qx) z(lR_Ml$FZQ&qA7nyM61$2wNHj*)3gxt8GS#{shto5jET4C5&nCAdF$73|<4N!@utO zFoVCn5&DO27|s}e(q>yI_i=J9wGs#L9-U7N6hClcO}sQZ^uF9~o|e39wJd{M@PVG9 zIVe@D{l0=^y#DgOnO<(Zgp@3ph%G>nupUH<W_3{@${4~h8YW9aA~+<QE)<89&UY`$ z(m7F2PViVX=A@GSW5|GVFXOngC)`J2uQCm%^$rEy_}9SZU4Ma<?~W_<P3w=ixK=#B zgsFA|9D%$Kd`u*$DyB1Y?`>t=G)dac_OXRJL)qU0=JW{Tz^+Zj?*Tg~<ax%Dijm?& zdS|Js&9^r0IaUqNlCZu|<3(mE>!IkoYPB4jao>cd3>85F-=!Ej6goM5R}lhq{L!QF z%^mhfVDAj5cu8}XNYF(^m%QS4!#zvQNM<Juc-MxXfej&ha9zS$WXk{2bRcbPfLnO= z*=5+$UR2Y?Ra<`^i4Lxq=2oP^Er6qASTV+yvUBB?Q3+k*oy)p&4nOTu>)+~2QC5)Q zwD(#cAq`+42#|^N6Z8=0`co3GF18)={z>D$5}Y#b=_fc0K{TX%8$wHeCWmeSaK!k( z;0PvPg~7^jMrmTJX~eJyWS#>=bL{h0p-1SH_i3pW_DfQ^cOp_4i!0H1n2OxjP|<`J zn2){oqE)_{eo@YsX4Xy9d}7efn>12H?rU;{L}Qc)JD-OvT|8bEcUne)zHE-)dk;o} zAXg0?=eN6rLLP^|PDY=YpiMS3QGH@o0?OmJO85esnz5CBLOm}(ZhM)d!r`70*DmEP z{Zk;?0b=wAcUZlMAb{XZ?LgR7Dpi(G&)Pcu?CjZx5eXy(dvTm;Dsqu)U8=+p$W`Eo z5ElYHamF_vbwS2SUQb5D%2_IZTmfP(eIkNa(=_!m8Nn6=&tz`H1aJYQywTVVZv!vL znB3H%a1$kMKL7+aefq@f#Pq+FpaDgKohad>vs98tMAfOp;{{(<#Ff?p^RJ{5Za*gm ziSPIDJbmlw>p$0?&FVh)r=X#6eO;H@W>WCz!M9e+-Q>qB_lpYh|71~N7T<^MVornO zNK!_bkeSAFWL_|`yqOz*b~RclUC;z~JeMMi07txBtK0J}&|xuA?+M@g@Tp)TW|zhZ zI3sE}_$dX38@|*Mgm5MNp-CaH|9)UoRf%}Xa<=^ip1B!hw@>#+1=~ap%2&3WQ1>Vk zPj`jUT7gYXTN48PrBMXTw}U7Bt|7721VE_d--=8lmi#NlT%R34%UutDvK)wT`v}>% z8%)NmS7u#gFt3`8#EA1$`ny-$BAH%NbOg{;ud6@L2+Tr)z!o&p9vBF`sieMi)A-PV z!2Fgd3zO>I4N`weFTiQ5aM9v(t(5kA$kF)bnsn*s%8wC=Yo7}$zU#M?6^E)3)bVfL z^d{`+6YMW+N23(y8NAk4EoIv+PvW3@ToxkRSGiJVf9wsMt#;d8gL`v=DBJRjC8^)a zLGhNO$UT;RYT3~t6QqGA4Au4-b!Em!Y9Z@ZbG#-(N!F7TFHz$SoMna>+F$mGuq=2E zVpM3<!{4!_617!Nh@RVbO4_{faIlv=DU5173>QZ|`{?mvX^2hICwi*}9WbH*#b2kG zHmCLNgLu1HNs8cqUt}sV0MO-rneh>KsDNvu_R@@b8zR)HsJEMGSK1CgwCAe`4kFE{ zuXoAbR2`e~SxIWeCn@ab)WTHFyf8nH>xPR=7My%Zb&I(b6e)vi<7%M0FjYgaxd_Df zV_SB=2;eOIY%^Df*f|zGz9$6&2sh0hr`@LzPnrI`6k=uy0e*Tj@u|gV%O=RXy!Vns zU0#2|q{wnEjx0}6rdAf4pP{CiKSD%QMw`3;3({lMQk_J4F)hj@Iw5mR!=|ujN3ZCK zOx_Kq8NL)O@T51j4ix7)MDq(vnL#KMc3kuRGZNEW_5=l0m?2n_i*N3s#>>JoaXm@O zY4AWVL`_RPM}cq0dQ=KK&=L)FXUpt&eUd^(8zFWz*!8AY`l;}Xu_C$s8R$X;wDmT} zSMrKm1On{^4xjihsy3>>$v=I$WAj*)j}Ln+FREIxCQ3}ei%!n0nZI@zgKV<sv%2j? zg+%sG4>Z9<<m|B*#2USJg2ABzXdwC;>5k_bavDY<bXY-bJesrgYk6%Pd1S>ArGr<q zU)O^=4p&jdfUO+0*4#dvG;Dv$#-(_sWVMa*k?^MpgaHY@%uSx;b<B(n`r4{*&wzkl z=b7ZQcUxq!vfpkPF~*#!kvOTDvCQ5oh?C7&LYkM^O*_NF)Q#!-UyPZDE=^VH51MLX zLO6n(3;~Oct*=;zKrB@d%ynLUUhB;;ODT>7duW-qt{$`w%6x;@$BUa^@Qk;<E!C;Z zfLz65m{Cr=qyk_!pVq$5nLPSqBhm#0o(6xnBAbUbi5YzEN9{NyWP(u@PiAT*zfNh{ z-VF@99oV-SOTuO)7%(YTLnfL0E=3|$gVF}raLiZRHd!+gafCzm`FtG0s$Gc+)xvCp zpM=RtR51}bM55>1<?!`IB+)dlu4GT~65b;m`@ucK(D$-Q+o^2>`R)bjVvS?MnHOM> z%0)8yY1%-5!w!YHuU^1I$olPk_MZ*i+FWB@{vx<K^Tl<-PQzf}iRyh-O|Hd#95*QM zH>EN&1wIsivuRX+*dBX#-fczo<W$NB*sM#ISoac<NdHe0yP548bgN$1F5$BTgMEZc zB_U&og~FabI+;IuWI@0)+PZh~a9wFM7NoL|sm-5;-U<ldkK#0$VO|RB8$(XjEl!9# z;{~!}(4;f?d*9cN@TURGOz*G5@KxUr(^oW0*f%)Y)8Oc4Ngzu$?EC5-PR{ON=a+!Y z-icZ}A>o>|cBW~>KE4#srl0)UOB`tW7WY_049^YAN;l_KNEBgPoV-JWcnX1e(o)Cz z3i>t%d4F-8RsV1o<B9#@NkUwc<NZ;#F<+lWtf+uQUnh|x#bkYBs;u0FejuRldogq_ zB_M6Q5I^c903my}wQF;G(53M6F9ITH!%DeSyEfyc2<eoQcSs+UFJ=_*b8_r(rMT<S zO2ze-GleI=8U3u)gEY3lYLD@%%$1{4@mB2O-q|1rRWUy#`)#0~6!t6UQ9Z6HZqRJz z44|aR4Z*}Ya5G#@q1k&ZI4=k5!8K;ddwlXwyEcv2;NlU)@WgfgHsCoX)oB3yE=w&m zobPXHtA=eN4hirsWT5sYp3XCGGN^kZ7ymFuu1ex17B*Wp`#E2JhdjOtQrjptcj??Y za1=b|aScsu7kE$L6n@MihHAd<mTjupPKK#Hqy}X!h?wfg#vxh#C<js@yr+wVc_vKK z2)YsE`C$cLHzcwRHy4bdD^P>~cf#iGBTSez2@^j8G!Q=%A6eN|LDCKx-_KUr(vuSs zRd+srMu4N+CuhV_Ln^OrK(M9-T`cyMFJY<D;heT~#n8>qln|V~sX;%6)t*;3QJPo@ z#NNY#rYqDl$+Pt!$_RFZP~a!^eGL&W_iTsj>{jv;tv`5h@0abf>iZPxYa3vbfYybY z&p$i)8#E&sy-|Pn<HH+IGxV=ynAzs~hfd!Q9L5*zLQLYZ%UfwBGre_;{O3uUxj>39 zxF!SMcGjF#;i-kvg|vK!@oKT;S4ZQqf?G19H+Doqyl5X5pw!3A4OvxRo;5dJdMerb z2r{bKn`P2aQM_f=!f<3Fe_iTa`VWFQ>rc*9pufqMZ0Ml*giH7_^?6i=5j3t@lB*h{ zp({brWrB@h7q=ZPr|i>Lwe?;S#wc*;O%bU~cYeR`zyhH3e|_k=W-U9D1QcGRI6$4v zT;Fd>NuOCvM-uGszpWhvSIvkalh^GBEJdRcXU8vmM3sZoW(g&e2=7-Er%syaNYF15 zC8V7Xz#1zX>YCdiB{enGI$wD%N_yM}EbY@a1}C$mqkW>cCth4XX!#9_U~vD94*%QI zC^bo^o?F0p`lg`8`E88M&QCDzuP{7e6?SO!k1Ue^uPo9(2~G!Ok>-<IfAX}mY7Z*E zoss7k@O@NSqDPgT<F9Mk$KyU!=%pB8+s@^{gi8?sjR)ThiOBH;sEP!URjr7R_+}RU zO$F{q3Plfi{a=G5Tup%`0*BPjuPfQ>WijvxKOdUp-{$-{P3TUNO3oWP=$VwJ6!QWC zm4dmr+zcN$E*$&p99*j2Nq42)d?K9*qC3_FgSAyg_GB(`mDHc!*z{>zklnr1KCNT< zU02wW!#JfZ)Jm2g*EhM*s!?A^z%Hp82f9Q_Vh(XQ^Kk5c4w<`&_jq+vaqF>gu_Z7f z_81yNCUJ;nj3r8QgxYqRl}Dtyk|P{3bXz_C=OySP7MN~LmDOa{);A^B0ViqqYj2@6 zTOH6t9<JG@DMlUreRJw0FDFRL>VNykp8)P2IR9<Wcd->g^?hQPP^LqY_0=gpu73*> z6`M#Kotz~5_~Pw+eTBot#(7mK&Ita?i|VS*?o=8+%Ow)K0^bexzL(4y-N)V?!_opM z-{Q#2Tm<?W;2E!6i!fyhJ5tSs>oST(=ESAD3{MJD5zo^rQnL1$u!sPU`jvqtof)up z`hD8O{l4_+w)IhZIZ<`_zY)Egl|kpWK41+v=KQ9gcoNYbxrpU=u^sp8h17?bad@m} zvgA8;`a7}XX@vX8Hz0kfqF(;{4^MK~)Nrj)c^b8T7Mu}ci55*KV6<+T&;l2~4nFXn z<Kg`6Z6HSJ;R~C}?8r^bWj9u6f#2d@AAY?Uvx4~@pw2>$lyp70_I$$Gk@9eb?m5#M zrGJMr?ewcCfj935vaE~k{|1^5ikge8pVXC^X%_WSl)^=0v>?|+6EA_X^zp90=i?*M zLBM&=6;9`%En6nURJ3CrVhunKlSk*LOza&GISem>8rbn~KVDC9pSAuCy?F9V!<j+& zc?Hu50^lShoJ>e1NN?Z)xK@z+MkfMh`^tYpOJ;wdB|)xB45!jsd@Tva`^BR9z~<U= za`(IeE`Hf>{PaRsO?+1~bKdouI_xzKl<r0`w*5M-hRWU(3Fso&#LS1(y?f&-#P%1o z)xiz?b<w6y)#H7PRj_kxc<aH3q;&Z09(I}0a^{qQzCi8k`|WOuMzg{|;l~0<7A58V zAE1i-;Mc)F(Gbl)Xvm4K7X`iUyr9neVSu=}f9%;`;m!KM?z!PLcjnrqg9zZJ8+a$! zHK!zGV8hiS6!%kwf7eDAShnsQ8qdM4t|QK(7pKekojgsqA{w`8I3ly1pTO-S;M)V+ zOSP?G@vBWbG}a{0S++`PFa`}f?H78$3=|@KlWZveuX3Gu1MKCbgmSkhXJV9lI^S}f zx97F3L@9Y(8Z{-(Mf0S5T~p$sb4UuO^X|{3sLZ_nXH!S_pC#GG;0S-k(19V}M-jj# zNaxS0jeW<sMa<a}=v^Lqk&iP`(+>r5PX;4pUrK?!;u>@v!G#cfx@&sZt4Bv|cAUeQ zCM;87M(B#t?iq67H6i;FS!>3|Ff#QJBy<-OwY{9iYZl{{rRIv;;-U_Zdm60taJ?oN zcr+i3@Z>#<Qv|b<UcaSfF-zbA7(Dg87;ABtZPtU>^#hYI<dyts^H%tA!{$1&7Cv1> z#Cvb;)i_qsj_VG_?DrID<WFTa5wg`7J6VNYorL5M->Q8X(w;Z1?r?()^gK5kfcqST zr3F%j-u{7nlFR%4xl%{|S*a1#D-3P*bMh~K#oaGP?!doPCVtp65Fd_P%}Rk8xZ@GX zuH0UR$>aWlJ&7Mw!Tyv$-j*}r+gxuo)fWmoGY&L6XgQO6n^KZ2J&0YmN^P{zOs_l} zb2b}zQO~YLpa82CbbEGgOEFxM$y8@nlEfnRSFKPt>e_V_<|%T1#&i5+&6${RmL2D< z8{q)zUJ6T_Pxub*n9KMBpG<CwFHV=gk{;>Pp=Qg7!|iQpTS&hNH4;(Hs}G^*DJgya zWmS;X)(NBri_BJxP;;iiF9CH!I-PLJ*Czip9?h;}ZdHZ{4-W~w&mqzxbAchtTd~*2 zx&4R%TGZhQB~Rp~V61oFjBj*tE9AaxYItb1_XSEYUX_2gVV)}re7Ms0#XdGdgN2Zh z9VVK{T7s5$alBJ1zI6blJ!DJ$dbJqpfg+ot5Dk1aJG#fovU9_{&h9G})6VuvmjL2C z!CWs3`k8iz5-VQiwTJ>QE`t+iX{yu9gruU92=^J}MA8Xo0jXSrawa}!7I4<-1G-m8 zQM^~z2(!9^<#qeV&!eeiIDXUOZc+eely)vODe<XBq_!z)5q1bwWqdGb-Tp&{v)`LH zK^Z8Y6qx!oD_9gnr!#>JC^OA^70KB2Ib^`r(o9-S8w|RS;0x$cRdTeJ7%H04aQasQ zw(1L?*1HaV9*RG)c7Q~2ss*Pg7fnu8>)IR8l!PfE#q8YEBNyEB;$6uz2=C)bPkT(E zZKOAt1zF<4ynpA8Tru&qR2Sl&ljmEaX7m=;vUVDb!Bq4z_ryy(Gr^7!7lB?lao^l$ zWoSZgV!Y<<p48(gUE1{;QOLCei$lxCD^lOnck_o{vWM#-9x2NUl@3rjv$Zb<ztFv& zOk=cdQEY6eM%pM(Je2?PCjiTtxKhphVAIX5$@ewjHQ62&5d5eEIlRbR{T-09{$7y9 zLcheUhbKY3eDCZoN)FeB5GUE$@cmMFl1J^CSjloDU_CLMR)-nWv@o21Lr8cXY+fum zZ>jzC7XYxlRg5)V@T=VS6zl?TLf&yae<faQ!-Tc|lRoMd5adNaOm>4jr(kNmL^$>@ zP^AtzSmoGE5xP5=UW`Z&KJoUR`_yD}61MC$rojF676=Z%-P*kTo2{_=zho<1AkbYD zh~L4^wcmmH?q>LG-{xg-(2kfWhXfH`E)DtVHp2HEp7ukT2S{N=w_BrL00Jp0`r-yG zLFsyL;m7<)cH^|w2eJ!ross;zBL=`<t2J&(_PU9H#_YD_^uaNA?SJDroUcU8Tp8}& znOz5RuD(P=@tb%7u6Zg@zE!Y*c0d!vJ$cn+APlHk;oPI@N0YQjQ3Bx;IX|nWJ>KGC z`}ZBZr}66vp%L+4MCeezc305`C$;ISUzF$q2_}W#zmZ@9nexj6|KloMC1A+VL7kg= zxte&o{Il5!N!GV?ImKF365(CCxlNIkJ?O{zljI^w_SkW(RwO(X$vp8#vR~S=lD3iP zbl5Mnj${*&$sC{2g2uUa7Ts#^315-ML%@gJJ*jp{z7%hA+Pg2~{>KQq5IP4mK<{W? z3_N>0iYP>0i?tRn-DZu6IW@v_TJh*N(K3=ja@P{!!~b2}q&GP0_~ms&4*Q3RsVXH- z$OEmbNS(x;2`}}br#*hDsYRQ6wm6IibZ>DBR^ij7ATF0hL@ikV@7rVfZx*O8z2`oI z3oS=kkK302E<SL*QOOx!#=T(wK_%Ek#;&5RR>K#W`wo4lowcLDC40S~{n3b24R`!^ z=(7#gAEYn<&CT%$%__|T)ZDM2_J76J>Q94npL{QKXNhD>;*Dsc&Vq}xh*4fdVfhGi zvlA)#1hz2LI`5;fJO^-Ih^S`3XA=|+B#R&z5(@6y5$Ye*DDCu=CJNoi1gj_-?6TTb z8cagFl#>BY+H}VrFjxsg4qLe-=0ost$Xj2cvV_pn^bSz0PB>m3vZ?2O|0sP4{PyTq z-QpQA(o0+vDtz#jdPIV}Cxl1L_D>(eDgu4=uegVz?#0X2zvCX97CpMMlVJqf*$bf{ z`FQj@wRk;$3}IT|=B|ivHV2*nPVaB&WJVz3&+McZVWTSSo}a<d^y%!64PX@MM<A_T z0=5>?*Gqzas}am0%HCFF6g&5|{|aD;ut`4AXyg4k`&F|)nILMz3bRvvuKlVK&iSs} zK)@yxTbm*qB_&ho<21tT_a`9aoqwdh%q_2)#|~EVP*c?BN@OJ&RQyTBHNY8nGxxqw zk;MDHc5lDdGeb##LfSa{hk|GO_bB*oql;=^me~Q%>6hT^|J3HpaKwq#fc%dYr9Na{ zoYEtH)5cl0*KQ6$a<HCjoR#o=d7Hx1G5fKmzf>Xm^x%)j2S;<S%tw$$3F_hOJ_>{V zyI-6vBwpp>virT)V5u4NKqs>_rr!mnNoT6{#W!S<`o~JiSf`?#(Yn!Yst=GIKm!iL zeJ^k36Ho5FzwGiK4-Ao1Xl#|o9;(?U&MZLw9ur{5=XzVDBxs?LMEucoU0pxdFhpV$ zy1{)cD^*$>QH?QjFHsv|!WYyPw9bW=jQmG4Maop$xX-yc#shdYKG2GM`WdyVMAny` zK|Nq2Juq1~p0EL*+qE28?JaV%oMJEG8vG<9rbNYmN?ie5v=<=%qtB~%t$u)WsrQ>W z%Gim&0WBl!DA>YJN6ETO?hEeJ<1KZSX8l5N^Jo3={IIzk=qs(|>%Iz_F=_3ixgPg` zm57`mg)D=OET<$M7Q6Hb-o4<G@h)VnG|lp^5DNP#m5#O4G4yeE=N+8o#R0oT#4%lG zt)lNik3Z&G+!4#Y$G`?>jsB;{;QL3=vT+_thq}~o#oa^O-K7UMs>E4=w;cCA484f6 zIhUELjMiHGj1h_jkvlS8ZLi}jw28z#&4@(?BT?4fuob5wk>AW#-7`}+{V9c+8-5!@ z85j+>P0KXyptNN1UxjDOSN{#<ze74C^*sRWR&Wq#Ozv<M)!=~st3?iIDYCsL_kodw z1<>B_=Sn2;viaZlQn29EaX|-{gco4an0BWpCl!*`+2~j@R~|I)_GdifZ-S(@Gq-=% z(M7z=>|lU2w{AWr-viNrO<JU7v(z|}3aag481~AmHiU)Gq&^W9J<}Y0+nyPHFw^MJ zv-D|i>1Nf#aP1Ika#-^?;Z--vijqmKK3GU^_-Gp~9&{3rO7SoetN4*ZaZ<b1hKj}n zg5sqr2HbpJ(E$68ARy!?Q-rYkg;wnM4P>Jh8q(fKsDJNkH@H;_QTn)@)&lmNade2b zzT7i15`8TCN(lQWKSq|huK=Y?p@`bTheQh}#YP|bABWLWX}tyyw~~0r?i)tJnKUew zFb!Zt_ZBh}U16DFyDX}cAX@;NT-+P=LkE^O_VYFMw}VAIEjG7Q6<bn2p`L<Nj)H+d zM$P@Bw9WeMy?_l@!2;lHH6U0neKiL*&=RcgiG+O*_`>w!i{;*toC=Js0XM{X%k`PS zxkJe#`(?zP?%`J;_rAw%E~Ki=3y<?3`f-;~%O+LJ>viHr`wJ!LbOM0tK=3_lw;|wS z@*lv6Yp~kiF*C$G1{c*WzWWWM=7sPx=m!Ae>Ghf$aP+gj9Yk$aUZw}%W45G!W43@R zhB=M+9?SU!bTds-^D``&H+UHs-OD%TBpA5r=DYu3z`ok7T6z0X@WEUVX)B<0mYm1J zh8u5(!%kb!ORmfJEhFORZC?K0%D3iUcPtXFlfJ+A%#D*=0~U?zto$v4W)j{M?FEBl z0moZ0KE1GtM9y1HD{}ic>&SPS5G^eWHy2v_vlm9^Mby)hE7iXuS9eUMsj-8r9uY{k z0X(VJ>QN2)t`+wn4pD2^$hUM<C(ZC5Pb{hb#n63<r}Y8G6L1L@VA{pZ#s?>CB#XJr z%eR2c?Je_TDx}&sh+=dw-ai9n>Iz)7t&HWA%QU=bI;*A;JFInsymbBWH#XYInas@l z#7;WkXIYZAS283u?i%l$H~BVVh&MC7eO>*TH~TrSc47vg)E$)@@XGILt>1{G3&3JX zSkOz?j-0jm@|1BHWt`ef_2aLXj7SQYxK4^=vENDGUwZ#hbx-~?Rp-=vzJhJlbg(w- zi!0mjgm3@%jE>8>pjVVVhYQe!;e!t*(-CqC9t-eoJgn{);cgzx7}O3UdsX>{c%g*Y zE7ygiO#EcE?y-1$x~A{Oj$oR?HTEQ|Gg?_?{gx#+++{lJx9!<VrrF1(1P3;_;-{yH zBX2L@-Hn5eZ)R(o>G@pLOKyV1((N7sV>w(;n7aj;Y>^q%RWYUq-8F1)^Zs<V7m)p@ z-R(XhQ;7b3yEhGo5fEeN!;!nNGcN<gK5$<qf%`JIZF{BElZgzhajGBT7uAHNz+3$Z zc&mXl^icAT!}1>Pd%Q4m*!*KO<NI&ROZ=ct{N6IL24zYiEz1Q=RNapa-ef#D^Cd}6 z|IEKWp2Kuv?84C^P-qs7sL)WKr9TH~-c&=(#B6TS5qesO50ju;98~PrBMT8Ds32yc z&A~10x*5dW{LcH=j;BbT1j!B`6|g@F_%u9Y>=gT)ht|G^Y}bKrXTh5!l1Ow`{Y{6k zV|zyU{>#0`WP?Fy<Ajl!ruNX|W9vY6QjAu}2Tzy#p$eu67FT&?=-~6Z(`wGpt#v*2 zYYE;fE=sxz;Z*tx5Iv^BCuBu|Y`jm4@Pm9-szF+H_Gpca;yU~1#c0S>8w2DFE*(xo z+YiY}>>*#k)T$#0wJl)tYa6x%^D&=SceII?^HsT$3lnJP*&*{}&*i!t@5#@*6LJX{ z6v(5y0IdshGLXij&G2XUKD;CsjG@-<@TcenRb3U7-|cdr7@Y@aP~2OlB*QFuC?E^3 z9#{D23iQs&-yR^2UDgWOG2l7u#tErm`O?Fp0gn_r$^JMVngpLZq`UAy5Luagdq|Yi z8RAt}Bh&bh1IvNe&op7bEo##K8gzHq)Y54By+mSQbmn%i`ZMA~^WPcq0eEijC1-#k z?gMrEb2C%75hc2gNz^)ppUrnzM0OYlaGy~+D1yfIh;w-hF*A{PW%bcCg2#Gea}vTk zM9Ov;*R^wV0Sh21u9v(co9(k2bJpD3cRIwK#U^~=vUXErM|peOOBsJy<9<NuT&NrU z<t#=o4R8R~T$1L<)x_rcf?p3^U^AXyPS1+$luP;Q0|0}`;P@=DsBt~0oiGjYDo}(f z<h?dcSU;=If8o9lh{w8&IlfSNqA#*#Mi+X;S|&<6Lxf6Oc%OgJE!Ij6S!+L%-<Jkj zM0!q+pTt<HtN-VN4M549!+>@C=xe0If_Cbcj_`-SSP%Z!Sz75dm=i!5X+IgF00XC{ z?i_#ui9NvgWzj98Q09>=rpQF)FzQ<|Sv$-Hk8)oQQ8?UTG0XulDi+k{`z3oj>*bN; zr+FVcYN*@~;vx7mh=(P2%JJ$CRTJ5c%GhbL*|x48ajdo1H#U5B&&_W#8$4;#wRRZ1 z5_4FX9Z%8FoZy;4oR1jBdFU)D|4OMGnK_^fHG<*+FJTMJy}uBgTQNg^W4HU3Aau%U z<^97n0CM0Okb(gA_pcwlZ8hjuby+P#&l@D>skTP*t$yf~>|P+J;XdPdvVy+k7TaOI zp3=^^u?NBo%Nz)gyM0|_|A|Q%-<nYR<;PO#{#^li@Qa^CxgRCja15a)j@HA9=iii* z<sLYul>qoqZ`*%OY|SO^z+;htHzwH|)4Vep_rdwYOrdUY`h$0x59k0*+s=k|gGMV; zK()_lZX8P=FL=}KPlDB9OOW?HRg~OAtBUhB%;0qF^Cd9H>d}1(Ff%*I05pp0ka?mD zTe@?1%75v9tGZ&L)u^QgTeP0P+j5lWnB0<5>td|521>f?;Ku+}quFxwuZ}NG3iV&z zRIRhl_RZLQAEtrL;u`XC*9Ia^i~E$^Ge+;g>)~S3`4h`d;%gKLUN|h5KyrmS*y)LJ z-C+Td(l*huB;SKH1KWUKl_?(;Oj*j5!>wpe^+7@2ZxeZSLX%j~Hie`}pN|-Z=U<_7 zW3+j82#^{zK~KW`cE8{rB<|W|cKG7%VGogq_@dDL9|tn-fJv>;+TgHhd682{OO#RT z=@!zPoKwDfrjQg_9o~svGhnqwRG7l(J;o{_>z(^PRPnrK7|fg&J%Ra%M@cC7n4R;2 zTsFyRs5VE;<8`bUT_dwx#v%hP&<xxF_4@w=<iSATvupEd&WAgq$q%B(o-jW)7LhFb z(GEy{sZsaEOQ;Hs9x94|`9V>MMBdH^w(kaM;Mc=7YI5E(TNIm%q~)?{WaS96B;_-b z<j-z~T=CW5*{vDymZ2~!f?<}j-F8f#TpC*V;`@jDjr%d3>i4Z-|2yA(%vtsF*T)M3 z9P@|-%Sy3Szrxox7rCw0QG#cc0w=QGs1C7{(Q-Vihr&ws1KF?6+ltHHU>1K&C0fHX z?HI6rDBli>YNt-?H2L0_>g3SGFE%9M6bSHI8JDcEzf8c<8<oDA9GsK<>~0D*fZ*l% zhEMURV+<%6pl@MQImOI!(!F&2AB>=*8Sq7(I?*Thlj??AM6j{~e7gO;Oqo`*lW#@O zn-uR>c@E31z^oDe*q%w@gRF48m+e$r_NC8?sEbRi2-QN*#)({<X^+}Fh31P>9tuOh z06qa7ff7^lFw6n?0ndwX%TM+@mKU4O&e3cBOZCUM-hv3qL*E-ntM|x=->Hjl_zVty zBrWqOwlDTOl3&mWO*ymMET`bDBDMsibS|>tZkHZm^vYJ<`f&XR&yY-UCxX<=ekFgN zP|`F{2(*1g-yPQHUKnGMQ|~%UW`WB*-^{R@xe)6-{q0=98R(h-j&eeeT<)|oA4dMl z*5yo974vt?%G7`&o*$$4+;I)M%ab1-R@Na^bkvdUWeub9TS&p(V%+I{;z|9ea5NM( zp78gar&ykEHQw1u(W^eY8%9(`wDX=T1h$#mDpDh(W}{xDWxzLyP#Bd_L7G1e`tnKG zXta9{=RD7BNc~JsrslyuF1Ldr_2{Y8nn?;wN3J#xtdXa~fN5&FI_st_YI(%-|LSdN zS3?3yGvZ{pS?tehihFWmstNJ4Mf$+T@$2SK$zMC-zu)@8bl&oh`)oNJo|+XAV3!=U zXCywXpI*8fj8Jy2EATctJ$;%f3gI{`Zb~{9uwbcJl!<l{-(Q_BYh@6&cow^H-~XqE z{8wQD^mK8>T&)RFjj<zV5h55eEFK77*i_IuVc3OV6k&D?<;OV7Z-InTTBK=+g-U%f ziJ1h`PYc~#7?ihzBcrTDCY4!7q25f4>qI^ta#Ia(ho@hG{)%j7yi1B4lhn4A;(H`g z2e?iN8|ed?nNE{)7U51Gd9+UosM`fIom#5y!0@G3wr%OQmW~{__{Fg_<heoDmmXxM z+CrKlL82rvs^L`2il8g8=WFwlg<J{kT_c(NI<}63mqRk1Y<>NSsUlHm{K>~VuRHS} zRrdSoC9?8B@y1CJKVO#t&h-D#4CqjV{c|to^U$Y`815jY*BLTElWs?5eZcYBlgKi- z@8`cTtukGdk#99YpEB3@kDNX_aSEUX-bj&HOJBTF7aQpgIr30TVc%sTPMsug0okPq z1dYqpU?;o?5eNkroS6uWG6x$r4LA6|js>x06`}WTxuG4Df@Yh4?kqPP-BQ*qU9}}b z#7>&_SkAYF&3e)ZMClh7;{G(I)+5N#^;BzmD^m?Mr%X9?pS&@`ib$}i0Gji}9HtT6 zN3&mxb&aC*8W>E2L8-OqtH=DkYswY`o1oz&6a}_8qQ^44570|UUv6G#jh$XU!chtA z&5lJ=jR%7He^J&(5Yh7#tb60_KHoIhWgD=rm{LhKl}Y-xCADF7ubXCmue*JPFA7Z{ zQj?G9%G|&Fac#n3bBq{^>W=Ra^Z0aQmCI2PG15=33||ndj^Vl;4*&xjkpV$kX!D>m zSRGTru4*cduFXU|HD;<XXf;!ej&=TC(*K$JvC>sPGxP@zQ2RIGfO7)q{o&#K81KzY z_p~|2l|IgbY3-TI%?24u(bM3?_gx#OKlui_)zl~oBRj77yCNdYpS{IzOH>I|LPpX= za_}tnnw~mXn5l)jUhC_YWiouEVgtqo#Dlco7(gz$-mHBra?>Y({jGr-&f6+Mz6zY_ zaB=5D=T$OLm;lXnknn4E&`;xRC)wRhM{6xU0#sqlSh2G^FKsri$u{uJRZjsO`)!ft zr3^uk_A-C9bg#Y(hrr5a>*>$ss#9FOdOO_zX9J6HuS;5!Gzq<7!cd?g4j+pj3y?s8 zqf`mT#8O*-a6W33FKbEs^6p5UV^O>>CV~TZ9(?7E$7%B{3QJdi^2?WLswa{!?K4&c zGqJo#w{)5K%KwDs<8aj}U0q<i@2(}4#NZ`}<}QGv)~`_<0BvS)ZHGfSVlxXhfzNIY z+o-L$x~S(@vAFoh3R<)|>Z<@YC+ayvl<}8WPs(N$q-qij<jOycts*G7?weCi?vp5> zM>z05sO+8U#koAJ*#lnWs6g!Ih=md>&<u+D)>^{SA903h;@ThyBtIO^2Nj4h)KVy} ziLk$Xm~^X`!o;2m6pX$*XC7Vs=<k+0u{{5D9=&u3xAJ)CAnGcSi^d%92K1gZT5<b0 z2aXs@h0sR;qJ6Ob6KTxtO%Ztdp$WuaH}USk=%MDLcVUiw-}&B_5l2N<N;&W|`cRV7 zpvE>A`mAzu$G!EQq^xa|AtMy$fPjYW0tE3j&+200rG7l}2omv|HmcwA@)dVL1X^NC zX->o=m;Hr4J9E^Z{f$0<&zT0lK3tb$@Bd=eLBIykK`9!}Pk<q5Xa_NJvqI{M@vrNK z$cfPt_9)vxQN$>~)ha2BW0rS338^9v>Pw|KSxAawk;|0u-N}LiNMYu&C(XKBKW+>W zFJON)PI^yF{WsR$f~&5!&DO*vKnN0C5-h>pH8=_G!QI{6CBfY#Kwt;Ajk~+MySqE| zBJWpqzCNc%^{DFp2kgOG>$#tK&v{L^yhFtQ9g7zFq^-5!u69r=t_yb=(D^0OlVC&k z8^MF9h)ymP$Denx7bfc}XKMn+7rfADd>ZPdA?<w(@f6ddUm>YzNnt{Q9hK~_(%O06 z32t(kasAX-B7AHA*PXfQNmC9foff)e@v2wv$Ne|;YLy%CD9Nt;JKO|{3Hf-AKP>;t z8MgVMGFQiD{g6#*3vL*K;Yfft=>xEwk^0C?VmgJ#LnGPwNuDQxJfrmNo8s@&GvW48 z00Q;u$Yd@D#Zv^_=>B__VtU$kVEy3#B>QM{JUM=%Kj)#1KyW48HQiafX)Ouq5jn4o z%CI2V@F;I`vl-$MCT(;|Tnx4$Zdci0R+_f<&W7@yRb&YIOb@~@8JfK>MTF?~jtzn0 zgq6R!5JagnrJ_}a{wUeS8mi5{r4H9~wCplvirs9zF|B@_xpX7IxAgj<0N6gY|5LV4 zS9QkqeFO2!<kk&9GX*VG<wt7nzR!t`07B#jB#wdJ6s(m3d45AFSWkD&DzvaL(!=eF z(U}D)x}SOakr)DZVa_Q1KM&R?>e0oRr*7@@N7|WpiB~$l>HYTgExJFfzV8k2fq0Fj zoVbsAMvbDnq1(|6fd5jGRuYcn)R77^?I8OOIFF@Rf&>%lge2`c-R1#nuc}3tvZ<e< z`~_}4F_K!OE>V;)(e&m!zcE!rA*T-wUx6iR*|BMGM6PNWX}}BSkDw*dzt*50wFA|E z^VjM9b!{j#OzYd$&3B)R*w*cpX<HndzHF!*58r@k=qV^R6}NOnzPhzHe;2XGp;Y=2 z!sF@Py9)Dx3O@2)Q)xc%T4eh&R)U1PL_2s^0+*LlVbA18AZ;nlGlUqR5K>njo91XP zTB=@<2H_XkfvY3bv9wtiTt8^Z%wCX;+v<zMt5qleJ~18P%WBBDMnY=Lz-s?odgeO+ zAoC2^iJk%2z-F`W!^FPsf2MF8Au5ZKKm4J9Sgrp;PWCkB@S92w(o-pox}ZR?86DBk zi#8SZ4Vw}Nhph~{t89EKUnx51m})ZP8eWF?l-u3<Rm3IsRs-H*J{jts?_ZG0M`nma z|C(C%C&+vMmp$q8vMhB>VlTWK>PDijx~?A0<0rs6HSm!hijK&&>G-&3_^yBHWkSqu z$J9L#^NBGF!+*<!4MxOny<07ywMa}a>u6NsYv+3RoF~A`(g0HW(S$RlLWi5VHY9_J ze{hLEEAs<RydnlkHnMii`!O)6=`O2*Xdl2@o_v^W@S<N|dR>NSyO$ZJPW89%-vs>M zVPXKw<&jR(t@p1F&m6%#qEFx0U~_<>+bC!sXRne%FVF$(#|X#hp*6G%?JpMF&UTl6 z1wL7FACS7?EkFJTXp+jxkJoE~kl_umh}w~*uK$gPqfGOchm!|O3^_c-=GPy*nb+ZU zY+BlMP+m#j1T2waHoMo0<OiiOrasydMOlIKn>a22#Bg*wG=cnO&OyfX$<AT<wixpD z%tAcj1|#Y~vFbr5#=oY6+LN}z>dZDELc^{xDH+8|iQY~Ydg`<9+mkWZc<ie%y<-|H z3Qo)!jjj-AGYY&c-m`O`-{i6U4hO7Z0MfF!PBmhZWi_4z)*Qh<<{XAzjl3c9uYc^w zPTgbtKokKJ<(j`}QSV-b9x0C4GRtyAkR@I#GCq1ongzoLV|N~RI7`{PEaesk8>I}J z?4Fs8i8=T-Aop2Skyl^NVIJ8({A;Za11SX6V!kL_O4CAS0r;#efB)nB#2353+e)!x zO5bvqHRHVs5V`Ia*)y*D?D<=6B7~g3_Of?G;-n!c_7%dH8B4GM0+a}b3*7=ET<f}I zCgyAIA0ZrO?H}eVKvt4OR$7!q?;BOfQ*Qzl|I26tty4Glm462hnG#xbMmFy%{;y=M z@+w(ZmsXE&{I%K;{TEi7wR*r{lC!$aTv6xw9$(yvs0+9=zZy;c_l(o^Z0mn`FgQg0 zY&^I9G-TY*58c#aD^f5KKL+=X3dm3ch)Qi4eA66pVoeC2k1VZ^JD^=unJ@epO@uHG zJkK!J-c$k?{ds*Shx5?h843Ssnt>PySH9<*(GkZ5@C;1FrN3X3#&v(g?w(cgM1Qwr z7YzS}ZRW^d$1|ha`g>j1*0m!5L}CX_+2_O68UK>l12V2p4_3TS<6Bs^d5S-2hxg>k z#=Zp*6?pUd(3Sr}RIKA5w%5qYm|H51pE#zLyNQ45#(^=T7GF}s4qP5#b^LHSq&00# zON{%5B2wBHxRSUUlt#^~a|y&jDInpbs}N_T8kJ;xr#2rj%m?D%7rusn^ROO<DgV8l z%Io9>7+S??Y{}e;jQrS9F!?PKzEBwdj!9aq)8Mp;gOck5p>w3)+RsU%lQR)I!37J) z0{*u>hkrNfHoE`|A^4z`j*T-DB;c2Reok6e{THTX2*9)eniKx;Dp0K#H}zW_fm1jr z<^pW}d>DT2L&dopk(l}`2-?PpFrUwcK^Y$lX12D9m3V-l_on|C)5jr5=)>ELH(s9M z)ITd$_rkw;&z?|k%FXss2a^d^<U5WlVEc~!`7OA(o|^Vm-0ND{d`c`3*j?9{Zv-gN zd#o()K7%V%PE*yZElb72G%)|aKEqj_?}T?uovp06ZJ{G|kBC@Sr(#&C<TZCyU&2%p zw5LhlMlv)Z--pRz^)mg`V!;cLHY~&Dh1H>B)OnZx9$o9JJRqIpR4^o+GsV;T_4CPP z32LEq&Bizt?q`O(-*k|c{;y39pe%bOH?#8mm2wX;{!PmLHvS6TN8&vS6;h{eQ7mzv zFF8{Nph<6>$TYsJtA6S-s*Avkj86umtx;qc;m82H(pBsIrp62x(UUE5Vw=EB1M9($ z7)h2;|Nlz15;BFrYk31+{83;R(k<AOK8yDY;{u>A#pWBC5%6KbU#9)N!qV~<r%`fA z4^JEeq$nfj3?~Pffd)t?wu>K(<)HU)%f8{s=>ui|z^QSk=;C9I+2^+(#9OU$nS!PT zx|x5{-~n!2APK>5-%$t%u<%R379*+}&<6_o0XP#n==?Jjf-Py4BFdq<GFBfX%5Q-W z-(92xyBLTbD_ZO~;Jq$d-LDtV!(Rgqt9$g{LOturQ3UFaWU|p207N?a@PFOS5D$PB zx55I_vff8F_BS>1xk}W&X2G{a731x{U4q9)oj!#EAkLM{1ZQ`1uX<0n$&tSsjmQ*F zyv_qeZjr$SwjkHmagNxc3J~w(Z6&-F&to;c$40PpgRGkxw+ucVWXyK8^-fV;NZ9Cn zTkOH2iz4H6fn4o4TqH;kHNW)iWKx2eGIi0$u1fMC2XIvWIrKs^%J~zkHW9#(sA5Vv z>Mr|9g&JqybmeIa;0h9bg{9pz`hbEzFa1hcV-nWbQvbKf>+3(0SJfzn0KkBk9To(* zhx|8S?H1r{1?<L!CYOx1WbX5p+?(=I$7b(<tWS0`fY0{WlXzHm8D>pfx$bR>beJLP zb*Hk<ZJd5NwetCV{klT8;tF$jefqcbcdHfxzXXLj=j`g&RpXJELk-BQJ>@wkI#!kc z(}(kb1oXa0_HS2gK>#X-(x1)DR-$O``K9aLhUfL|nZ^r}I*rPPB;|;<^xK-Tr@DX! zR`H(0_>}SA@CWl{r!rF%kB*{@=z+~~?Q<LX!0(tg00^cnPp#J8ciVt;xn2&M9zjbz z+OGJv<E46Gk8}OkdZOD-;r?&TQtE_PW~t1CUnIni8UB|oAv=Hx>G=-$b>_z&hlBpk zPnW^;E7kT2n|M&o!20DHE$0)-?m5J>P6(b3mwGl%TEi}X<D~_n+!;vy5^dVkipZJd zIk5E4;q1fsQtHZ``OeFOeQCV55Ru2>9QieP?R9<KFu83nvH8EbsFkk4GQ1D%0RH5@ z()@VksSf{T4p-%QXr=kqecA2#o0DTAaj%5hC0J8^fpEAPEjMqHJ-hO4^-Mr=k3w{h z!qAE7N50+c^rI>hWyG23o3$m64*u3(*7t9|<Y0!Cf2K_mcev{9m@u#-XW0@iDXRlu zib)^U`OfV57m+s^4rN}So%VqL*Rkw1A@wJCAH9zhaOP-vt1>Hypf~0#SceAxqpJ7x z=MXB5N_aXdteUY>_CCSnN)JXL8R10RKmAxwvGn6{hP#|{B2K@(7rJe)dQ3Q5YH?aN z2TT+)-j{_f_n?`~r}%~w&r94pwt&W~YSO!<4?Am15IEPnuYS&k-x2nA;JTZpK5Kvy zF0}?lMA-W?>2<0{oSE?YPlN|Z0vDcb&got9NALTniz-y!yTzFk*Y&~D#i!66jSJ^x z_fx}9mV$npr*T3R4@)ndyn66E8QAHcUvwr4iS`cXF0u(B;Dsz6@sW+~WV&)B%wU6s zIU5E{=7a((zq)zvAlCQHRT-U&`JVbirp(2Xl75|fSHmPnl9wIB1xM?(5W&0Pbo}JI zviNcgEbnV32A<O%bcaTCXESBzqGN734Sb!<t2X3i{sT@*5&svSTg#ds**~Q`A}g>B z48RW`qAtL-*Q+3YkL1OwyoZMr1l9o^GU3tBCQN<NKf=Bef5WAuigc4=j^5Ppxl^GJ z3i<dGOA=EY)|~uzLP}ggct3*@ssgknn6L5ezpRhN?_A?~(h%nD7l(Pz=>piZ-Fboi zM5${}VChRoq4$X&cS-ZZ*#nWgGM~-e63rp9S1){pvFB0SdBgCQ(do%8+L;YES3Q8l z)B<{9Za=?^{Ox`m*y3@Uzq9q@0YJz%>5tI1xD3y{jKR}C99XZs5P}Xn91+$`j;t!M z3sEc!WDR%<+&QjA-~qnmZ>nhG+w(uRMmn6Ftm&7yRTdstFkYp>09O~lJm5$c?71Pv zOKn64v8_x*EF8ov=@9fm<s>MM#F$`n*ygGIBf;`CxVaywsPZQUlB&C@jgw5#oeypX zHV8u{NhbS7eUS=Hksv9j(F1Z2O6O$Ld#<`9gwPvOf<W4wr*9c!crxh^cvZUmAyS;N zUYlCWc6|GlgzSCtl=ytWUw=RI@|agi->?(2K<v4jzmw_uoO<rt(5mSdx&D*)2&&L5 zTkv86GsY)z!GwtSP&Hxiiq60_9Oi1bsj)iqtX{!r$JFrTJuA=_GX%`HI-k!RHrGPI z!9-r$kEuj_R|7SV&YJ4(0(2E9#t>}vg_izO&g0U@R!b{3sn+VnY=|N^t$ypw*r&qK z&G}V)8y}*CpP#FjZyO@t+A1AetMV;A53@1^R6rvFJu)C0CB&foU+&THN_PUkH&hDw zIh-v$UaW~+ZZgzw0oKCCZ86P4wF;RiUH=Icr$O6D9h|NqU^4FqH^;n^3^)Ih46mn@ z-8^2&P=g)+1sMvA>Nn>)AE`li^K+lf&DdjGSHMYp$U>3z9l(P?d6PFTHqN`sXzBbt z4u(?vBP;9LkpH>ey=fOnRHG%LVnSF6Sp~HcxWiZ7KlLcwI1e^G?U1(Odmj~JKR%&) z81@)jJzg9THQu#>e#dw1eSfi%UepbfMc#Y834H_@B>#ua#r|WIwyQ%to7tbi+V^it zXdJJ~^5SXYdO~TOAiv`<q3bBGOnikTi9f%%Wu_NbcW)a)eR*zcSiZcoeti5?;&$Xt ze>bc!?7o-EHFQW6H2$JKZQt2wQsl1oc^}}85eQoKMo=;?uKGaV`ucuU!N7s}%jn5M zFYo}iCR#>+t9CM@p$BsOE)@@T3n*R@Ju7hzA9b`}??-gCTmN}KqALPTMYcDCJo}D^ zPVTENyl&I6pAYv`9{FGPf=~%LF0p5bo3~CCB6*$~AFn^2kn-tvsrE)dq61+BU<gCf zn9YgqcMAI1<=>;#Q%=72&21aX>;eN@1{AxNi35cZY0*E0;C&hI?sdlRrD+vPB}VP_ zzDlL8kr`ZdnaXWGuxfoap6V!<{EnOqnL6fqn&-FCKe9&51k@O2yY-o53EOKcQ5Zm) z|I?pN0-<zicI6C$sy>>(^;H0HndXrU+<BdUenK__j-lT%(+K9bzGtO>86t?T073*X zJ9vn`rTKp#zyZ++pi^oolfGkx?uzY81Dlp@e=~+O_vr|~*B1N@aSS!e=HWXhhzE-* zg({>uMgcvbabtd>B$Kqd^-7cW{qhj$bN}mNC`iKK{Z%qjXmg@U2@mJ)BR?m^iT7h& z>C#iDF_21Zda&}|0F^xT5Z50DD4=>QSk-wt+?s!LnL7CKDDw5N%}~*eF<x^cAR=0O zDfg?KzX}m+1~QE^B{onx^I2tfvZAIF$mHglaNl?VwBLcIMk`>sCzk0ZeU0(pFwj&9 z%fEaErPC$!*NE~>Ep19Ml%}wc)hesnD{y1GD(Z7{#&rre@hc4XMgN*xkIbB=_vdsz z+`e<V(N~js6F8`*gf}1RqvFr)=!koBzGifC@rp6>$?}F_<6CT|VPqI24!xs}556d0 zI~aGmI@G%R{CK^DU+Qsi_j2k?{}fnR%6T+U<FOy&PT#Om-azbmJ%_!i;JNCrl!o-U zmZMq<B6MHzhgF^ut^!s_csqHH1HJ$ugEV;XP7I#u<7+3;))W6Q-$UgO9h-J#tu3*T zX#I;ZQ05EJvh`R+zkI1cTz6R7yw#|Z)<}8t;AUIv@vTL8J5X?|5F5tAsekmR&^B@F zq>yNDmg>-e?=YL(oBGLIq7vH8t)C`Cn~_j!Ju3Bn;bsjqiUmQVammU;NmO?cIC&Up zS)lk-L~Y_5u!}lC5z>@CTpW&S&9nGhw4wCh3_u>6wm9vM@l#!aX@IlwJgHvrl0?(* zP#{q}U2>f(9x260?ym(N;?uAd#-7_=D1>6!bl)qr+sjX|R|oS@_nmPfg5fTVAxl~x z;tfkHXgSpR$a78tf8}EO!fV|#G3_=P|7ElP!HV}0?6Ib{{FQfY^kr}28fC$CaKZqX zhz0&thNL$d3y3g(8R=JhMW1-)!|y4vqZ%_dmWA~{8zggglASjLgUmnnld@BHS0zjg z&pe)&p_aI6<%sFmT7}@m<);MxhCS`)HYK}H3oZ8v7Z3FA`<A(u%q#3MR>JKf1?`%K z0k|PbHaPurG)XiHhbI9@`|w@<_h0n;N>e2F6wlT_Cpe?D!4MWt`(rl!rUn_H@z@1s zjZQRQd}G&ib_RS34&(TNHz{3Z5UEUmS@eqJ|2-FeG05{du%+>SG{@U!EGzSV6d(XM z`BYwq6c-R(#&)s!eM$2l7sZJ`IL=^R8-`m_IrXCj8EbouZ$~;L4tqK77+Y~XWpm#w zyW9pnWOn9OUK=HUw5GFgC#<Pq$Xd|_gPvLeD%p7*U{@swe>$pdBKcWiG#&;px$PZ3 zFf;d|otPFgaQfK{EAz<5H}YL>UI1-FonrKj7b_m;DJ@US{x5T>BeAJ-{haEq*V@jX z7M{%lUIRgdO#vCoMCZdZL`l+a9m`*>U0g4derEh#b29w*)|{D_;7s6ad>_g$O(Sbw zUz<_guw~)Wxc#lY3-0BvH2sEMy5aQYwxiI?ePbHQ+Ew^CfD&Z9MJm5c@l5^c1if+L z2k&vPGjvM_kY3IykJQu|s!K0m75SGB-Cp8`w~R*5CjGQBxUj-sE@B#ZGq-_WkB50) zna4Rt)aPSZ=f?&9rek0-VXAA3(+D3;|9wQCdeZzZq%iU{oc8fNbf#_vO##Pyg39BQ zc`N_J8zqihTsBrxPgLsuVQm@G`M3HOo@OXyEz>>%(r6YQXp#(LH<Ru34`a@qHflbP zDJhSYlbgsu!fi);ad5|U;5?}j)CgUc-fF>IqmT2;!X^ajeFu;3$I(_l#)UAZ;<iDC zM%PXT;}|F}(cvodSFn_QpFPB@_BIULKB99?CWoxg?b~PM$^yxdWSJM3$?Na*7cNCG z$lCQ6YGT*>O`V#ESH}G;ou7PW;_b=+k{ZkyX_Fo&`oclvjAcCExD4@>1o7574V4L* zTiE8<aKsi%*4Dq;TI#>sbDW!{kE!+PvmA{8_*GHq5wDX4y4$lGO_G&VuaV{je$Ok< zMB;tAbkF-b>wazKBY*Uiv_rT@zL4}VWvg>5pLZ8OKJp&Lxx11p;o2Ko=IH*dbK$A~ zox@UQhq5yq0l;?aZol|M>o`d8()K=eHU)>q$vqxZvXddo&$n&O$k%=Qx#)4SNxyi> zOx&=}deXStutKt9antuC88Kt+A41{4{=BakL0&jPU{{a<gNhV)BX$q%@zRfnW17<E zs4XNM%H%fK`tH6DqV*4j-W9&QY2LYE<sUb)(s+`Gb!J}LmhXo6)?n`;5y<4%9>Dtl zX%FD(djOr+Bp5P0*cp&UV_u*ry{H)d+S}5uKQFB#H3l635Ra&6sPgFL3-{S!b4VnK zD8epR?9P6>1x57aJIe%4FfG;1X}W7Sx^Dozh0O;;JAL*)o`WMbo=^BZpNO?>OBWrl z%WaIIUHvx++k#2!zn^V7LBCjSN5y-QVgeGeda~^rL#iMOGZ52b1^2)i$qlrJ(p=Ye zF`@@-xB;b$ktHBj%0h|gcXkLtgQThLVcvSF+Q>rf5Z(9coY`B_)pipx_N9#{N80sU z+D~Is{P!iHn`SU|m{|`f&hVPyI@mu##-7rDT+&v6!tjiPI<=vFCY^9C6(w{gD(=+2 z-|Wx=<eq=sIoflzB{127fxskt;7UbB_{~_$cD4n4FQnOHn$<gPxUiBe$ph-Ynaq;c zOs1u_Wp2-3PczSdf&LZb+|0QE=bEF6hyLQEkuoYI7!v{EFeNhyJqh*tQY%ur(~X)j z2-zhs;YyFlNd~OF>kbY7Xg7>Fqkg~o)!(MTX~a3B;ttxnWp!t`c6~^@QKDJSZZ#n| zH_P*K4?Bh%|CAe++qDbcBvx-^DV-)C2XHzzdTqBaif}7KRx?Yc=~S#l=rf}S5VvI{ zBnDbiO0sct9=&`YvSS@<TOfLx6>)A}J2Zc8gQtss@C2%IfWUiyPDx~9u=dFzUD=E} z$`8gG(2&`%Y>{7w>~Ij3UE&|M>>@S~xlnFZ<Y@jk0htV=GU+){@SIzXhij(oa5384 zJnsRaK|O+Qfxqj|PiSCP)X)0w%!+_fR2DU!@H4*aYT}HzH$t8lI#=$S?4Ifmo)@N% zMlOt#ALTjQaGII_toph!gxC};nz)+Tw0D5vj?<QE1UC;p(l6J2{AApG5$Cz`P<`S3 z==}Eiv8U8yWZM#Z`Mx(jl1HEWw<li6NB&1A{BGp7S$<Jza2q|@D^KEQsB343r8Q>- zoOhGC1Gyv#QfS6?08kyHW98~Jib7(^!noPyjmmSjcy+^DcNZQk-Euqp@-Q*;vKdSt z%bnKfkhm*!^Jrezs@1|Zzo!JkPxhvfB4WlIfQqmDs7(VhBV8I>nph7TT76J*T9=~H zSLUq$3kdoh|NWn<?FN?F!9VVzuR!v!`Q*z>=*w*e){cHhlW{|aa`VaK_~__!(~b5$ zwfE!IO{VihVktjOjaedoHUF8#jU>ZePlL&tEYqyuvYSkMelDo#kb+l+d%YR{KXCYj zXIc(&u#(?lLaTt|^(C{39kMc~b~s3?)n{mZX*L3zpwyOt-nJ2aL4x4ZStfnN#_X^W zrgh&9`Ev{^i<+p9Dp~nS>X*Yp$eu$Se-`E#Bk;)s+k7+(dQ21qWVhG(qxDoi-_21$ zN&V8zFd>}n3G+z$1ZY@!YRNiXT~jkxg#2gF`#C;*Q%&iCnHHCxr@EIwZT%h$8o~2J zq?Wdi2Ae@sr<Wy{lUuTZwm&wpPz{f@DxeQMJ&*QbiFu8?7o^kO6ph*8EQZ{3u1nJ= zjD)8BTG<?T+F<%>%yBu$mqj+fyQ_^DoxyJCLV}%w&k%N0b$`eXVG);RK?UDO;{K?x zq9f(edD{@MD&)tY<#mncn&hemWa;(hFZ4>i>C`hHFZ$C{(DCRbZ4RGcSfmQa1w)qF zZ_4=S$KMDWbd$c~)<3zzYYr!BBwg!#C4+6D-FRi%$nU4%gWAjq+Nz}3`7sjLiv)iX zeYE_t-n^s5LLWEBY9UfHUSq6r6dFxoucp#L^MSxEt;oRVC6x5eYVcAZH;&$!yL`G( zjebO0$uU}m)NXefXgyBZ5as`UFtP5I@THE4j>kU(yN<P0Sg=8Nr!tgyA=tEBRSUHS zA2V06WeO8xG+qcTc7fo;i-fm(0=E&xZDpKrR&fh|hW@^nlX=jm`0bQm6gk=(QFGjs z`3<lM3X323C+sN=XsfDm^!aC`bABfoFC#CeD{s-)fa5o`WvZWLDd2iLF$eyHmISrs z#qHI7wpK2Vm(z8YEQ~G?-i;gblg2mDjYdZC8_|;t?w-g+RA3ZY3y$ECmMo%L2_3x{ zf7Hc^v`LCo*W!YzJZ{@=I;v`9iYiK&=>JpXAc~XP_0gzbx4F%|=LY!y2BxE4Z}6fG z<}Wnva7vaSU8wTWe<ZxlaJezqujfUoIa~^;t9{U(M9Lp$?MU?fOa&j1?q2q=5Plqv zghUXgW@JQdVyif=Nxr5m%pGT$V9`;pv?>axsnRSf5H4;SYNy8dR@<%zsE#!DuF{!M zaHe;Aj4@5MN@*A>kvxC&YGC^5jBP%PPDP9tHuNm8;SZ@!a~Bx+K$l*9r+uP!b{D$S z=q<Q{-+kF=WRUp8X|cVwL7(J1I&A7l5)dXB*Kc~IF-g9;7`VbH5*-$wuvyt47!MZc zVEjE46s>5dD52^RFSnMI{5FTS4>7&s`=}{5O<I%zW0rpVp1et5UJ?egdLr?Kdc5|` zm=96_8eSvpYQLIR>WvnAQ_~KVGeLq=I+E-pMSWF|mYs=B72AZ)tpSbs={+NFPsBl2 zgpR7)(_%?@lErD~1pP14*y}{Tw=1_B3Ww>0oSik|>vf-Kbh_L7KV{Sz_eD$)YkEH{ zTFz4F?=)lUbWRvnZke(|W%s33H%|D*Pg2D(Qn$w|_9yW&7yw;Owb5VLr#Z}oC)Lv@ zpwMC8JM%sXo}_5*@YEku^CtsgV4BY3n%b+=P;}GcfX-xE6{)ZB{z8jg=O|D&Ov{KO zYN1!nc_j<s&LS@xJJkG+Yczv3-T?PbwfX8pgICp^W14ykl1Lu-Rz{Qt>6^WXf$;6) z(WFSXm$|9Q-21sj^{;{Yew~@&6Skj?Z1&&a&@v6=b^dx7wo~IgObY8Vpm?nCzMJ;E zW%TRD-khwIO^m9<^6J49VNzC$899EVG!BK*6UBxvD&HqZu6w2=8mMo?6;@65$)Vrq z3+SlKMlhx;AOaEeE$e-Xp7fxFrq`ggl9#3!G~GMV9}Qae^CGDk^-lK*{B<73UyNWy zT)o?%LO+PU{gEW&0(&}!{7%hJBs&S;sjN=#ZGMrSR1ECzAB~u`?;bb*(AcH)N-Qan zF>Dp&>7+(#Y;5k&9>eKV8g&;nHl}g9b@VCl*|rUCSZh0ptRzSjI<GCgBg+;S>*#ZF z^Xj;Oir{3uMup+q6^+K}SjCMjdbl3?#%H%&gmzPuQt35v%{$T-(Yv8;$tHlqJ`L5p zz-bAM;_@MX);ueJKtfIEaMxL1YBL{IO}fE;<zk82*=f1vzG=gK#pxbQuf=x5f1>4h z0Xzn9*r{vl$w*dai@#&IZ9^H4s{9cO-x%#*pDeyybjdjI)neh7LU1j+ik_H@ITLe1 z=~q*Lrdl+pcFTTbg)vM}5B=(cs`y0!P0>Mt>rneC7m_#^3S_Z3c1ZH$4HJPDa_n4D zXlII@bl|Wah7mWY*v@&67dAUg%SGZ7m<sU%C8q2rNksA<SUH;|EEf2#?7E$ik@2P? zR+4=0!^D^QoENI+Rd_z3r6z|bieR#pcAe5JEd5QD%m<@A0+Q>F+K*`(DqV=GaEs%Y z1n;GMx5yBtIz=VT2ZuIiqO^1$-TH{`EpROA-fsKkoPM|T!>%iHx%AA>o8q?Fff!2} z{m*bsL@7%Ly{&5(g*ze$@xfG+hFJsOQ?HlnPHi1LyzRCc^?Pmzc`D3jX+w>#PnM^L zP4g+M$bVv!S5<ux4%EtrjaOqD6JP8_sj0+66JrB46+0eG>~j=vg7uHJpOz6lgCq94 z@ib0S8J;c@2cOPHRF;}oZ5LN7c;B>7;HMq@(G8Y_Ze+vlK6JaQt9ROYen>pxX)xbd z(P+U@k0hL$(>MBHpdR^zrCc=xiAkStSw6W&cEQY5^QH!(ly=>)TYsM_wzyhT9er`F zOfyIQYdWdZ$J101D8}Bh{L#`294fU4dnzy;sue1Cz1PEK^EHR%y3uBKduS{ykJ=`$ z(P3Fk84hJmJ0mk8QVt#e-*<!dzkD}T<0iVV(MW0Je_cR){V)*sh6Hw18Nxq6`Ylc% zv?kQ@JGdMj@v?l|imfzCbfe~+i!)<M2fAIo!OXdh@kc_wik);P)WBF`t>FAhBw}|w zZa>}c&^)?v8Q+@go&t^DO4nS6MfJc3@3L%Z?hOaumy7vZi93w`IM%J9Nv<o&<+OD7 zoQG_I)n<dcyEmvegH^?L_~-PT%}!Hpa^Wn-1*MM~XK2kOi}bRUKds!HsM?NA4#my@ z>RM+dj(Jx}t=mNbAw})_Y9Rx{la4ZU!fnSzIxqlkX=^k=xU)1G9)g9$>Wh0Wox#aw zsnFC&3Ll9CMImJoCBm-CL=p7hqtr1zn6Ia|rFGfS3A%v1I)%l((!bjZdl+j-p49{y zBuA$yr?QjX{`Pqb8?f<nPq$vNOHX*WL^h^N(3go(fFe&-RMUYKnJrgxM8vhytT^mV zgT7k0BDo;Q0?T3k15&7g$vY1$eJ+*^I;o@($~XKDo3?s-N_ScT8HwAd9WDVK9v{pY zq`7T4X*c4b1-_7lipc2<%7%WF-c1X6KXCSb9m?-LAz}2IJUaskM`R5fnLlf(wYaqV zECTY9<Wp<1>a@=DjaU8S;&FIn$#Rv~j6K&<ggf0{BhV*MC#$v<jl9uk^b-33x;|+$ zJ5rrE|CTu7J<+F7`6))T)Qk(XH6V~J{}zG!=GR#o78W?~!z2^4`j^n0m}4r$8j|^h z`OSG&$qO#@@M4NAEW8NF5DS=a3eF?<X701vG1UEf_*TZ`<%u%VriZ~WsE0!I4X`G) zQ&=;$J?g#9Knos{6zWO4P%^354FNM2#`ezRlhn7;LwAd=jS(GKH5j;=(OCYv6(oN& zF?kp@C7YRzEMzRv*8R^jBg-KTL`V=soOtSyj?f;5ET0-4l3e}6)~KN+{mTbW+@=t( zWs)MQ+XtO<&y|amcI2~~td-9~Y(C4L+@6nr;X{5qUYznxw~|EcSj)I~S+6x~K015N zDeZdg*)Vcydt#ku-xTF%du7fJ>^bd|I1jm|BKoxj1(p0%k@45bLcFtfYHtpXh?IQA zK7<LqJ$V)tns!;n%{c3$ZZiFHR->Vb5T)R|c{H6R|G?A?CVN!6ROiuCt1tmQT_uZE z6Rj}2JrfA-H<XeLB%F?I673|>wl3s!!lDATy`&8`NfIG-@@yiE__*b1*fx+m<eC9w zkvv#SA$QC}etcW2Ynr78Y3EtY#?J#s1B_rlN+%qnFC=EuGZmGGqe@x%R~^9E^HH|* z=>wNo(Vc(9dG+0bx5G~Vh&Ip766mIFCW}yWwi(jf##2`8rYtp4=eTWp>EWX_jq2q! z&8N2=E=m4r(?v4oGrT4`o@x`(qhfaGdf<@B%^sv*jr=UzzGsx$TM1wHaZS6C@Z08q z8W?J{^?s>MaP7k&i%P0eGw}6tzdo_jVA36j<4OO1I@??<!yDPCC~sbudYdthb(%y) z2_@STsV8aCO13aDm{<Reaba!NcB0%g^qaX9#4%#@&;cgHmrdKE)hNdB0Nldwh3|+& z1QhLb=<2BJ5QYT#jQM?RcNjO-BkWV?-y4yEri@C3U3UJ&SWyS4W6FdMR%gTN*xO{k zr}y6|EDyrFM`Keig&Z@plujM^h#P%t>`s&p;SBLv*#N=;{2pP9Dem%=nPtF-pg{eu z=%KYi$EX5QIlZeN?u4i5L;-vFZu{z+=yJ<-g6qPL<y^2Yo`jzMjQSdHd%J8;p@9tF z1@&R?Y3q6VthvSP(Ez{G@gX}<^zxlsR{z|rk})~?V$?wzUyGkWH<r`V#KVRo=8<LN zpjF%?wKNdCCNMX~Jf8GLrb)yem5l8cifSEI_=u2^`v&5M?^#3dr|tsUQgHa87cf!y z!g;%Q#8lR^=>_sW;&owuPikKZIGp_5bG<{TqEUch_X|q*u0uxORo;K8r<=Eat+Yh* z^4QiB-nmw#dvWm-b01r(G4*QqNO2sNfqgJfX2`@hq=JI%fb49$I314ndG?)jP?b?~ zNFUnE(tBjb?9vpnSiRhFlHFU6((<mDE`B5_yAbKeAo4v>VSYnmArP9kz>y#qix22h z!F>-L^9=JbX`_^>u<6wu?T8Xs7+e%-bb-nyA)Jhrj(3bLN_UQgD76AOSzqKr>u^9_ z_3+n(<-V!zQQl6Oh6Sy}p(1bL5Yz3_EQWEA+@V_8*EpI5HQ-X;5?zhnzfnqx&5=S~ zuSdOrs@VIY#hZQL_(|$+B9jK^O6R5NoAYYAp+O}N8?~nD>?9F<uvTgCF)GFd1E0O= zNrHF1Bd!*E2l|_&e0TBSRM|w7cXQ$ub?{8m)YGR+gRD7dekOLuUzl}Satf6`>k9`^ z=f^R+>-5@eFlbge6(Q9Z)b|#maazbp7i#TqgYT~s8ao2HFQJWxZbN?}iSd+O=hZEo z^%_ka??f%G!|m`FH*=jMU}51wS0^Q}Otd@{;6^^p@D`pu@13M`UlfC+c|8;(Tce+< ze_ACGP{nP1h{KSE8ceE(&oaXZ_(qRA7-)no5`~Y~XG>NfE9ds+8B4-L-6&z=d=Z*O ze|<wUoKZqX5>-SCwGch4K@f=s>rLEGNG8TErv2<f-7-o;H@f~*C38c?S;7gk-&AN^ zhLyi2;J&Mp3Hh&9;!I?nIheL9O3K}4kw#)*6!zbcTsE?J<8+e}Y;Po3?||z%IfPV> zXrd<Y?mPRed0eU7O86Vh+9HpcN`rYC&n3vfy++ZY*%Ed9<4)VLe}?D+`i1a2DwAp$ zj4zfKcKE&K>1RAQg2-E(k7*2sw~OCBM9Jxwq4L55v!x4*_?C|e6DSat6-khmx$-@j zgKDoN#)dlEGGD4hvjfa`sghlg4~Pk9_+N77(yp&>2>8!xhv)h9S{WCA?hyo827#Aj zjBKWsC!P3?hR5c(^_av%-ABpfuCcAI{cDh{?Fh%_E=p`YGaC%(v@p9G-6TWfFOb9- zu)6b(<3^)eeM26<$Pum3EMK!=so%DxQ}fySLc)Kc6^Q-rJVw)GVk~TM8s7m{k(cIv zUVF|xx&ynBe}_s`I3%6XDq`7m&l7Gch#s|&7+&Qt%abQ3l?=`OHl|#TAk-D5P?3+1 z71}jj_C-1D?-?+;J)!mdl^9etSEM=J>V(C?LfH8;*83(cIf)yXjzX*XnjB9qTqeHn z3}xSZDNJ}2kawf*&6(-=sD+$7-U45m<Y~DsSd{7W7D;pSqw2452S8)ez!H*^HlGHu zvQazj%C-@ECv9vKY498*ovch{$WZt74IHG7Sag6XV!tY~PL+{x>ydIG34H=N@^c;u z5p33|s$3^2fY!ptUM^M&=?<12dkZvGvyhx?EP`oo5|<4<{z&FmslZUs=;*d?Me_Sg zR2xezpVISdK6ef5=(DxU>E{^;tl6mrtGs128JsYzc6v7z-h7?}`ZfuBU@=Y?LzUy7 zeb=MK6=jaVh+bb-kNd;$V1i2{o*1EuOe4&IpTL4g11TI{cv~*uh~ubM`%?cC-je+n z*)ek&-c9qtoF?Y=__w{f<k;~6O{c%`9~4;+lF_dUMD^Mxw@@Q;jHr~D+QF*zn8lc( z42sB{j`(#FzN!{dDetdT9g|MBz!(o~FSab8D)IM%xi**F!ryAZNczzN#JXyKV3YK; z&4`5&+-Ul8gN8fujT4^cIVxf82PeP(juUgbZae}nk7)4RaI@3mKAhI8?^tFwxSkvv z4~_FK_AdVZY2O3F2h;F`T3pY6SC;{lB5i4nTkZ`|ixFnUoE5HD`3AVUu%Bvb*c4+Z zltju);5WH`E`4byE1G4IU;OQ2hM<akaGBblg5GlrvH??T{6$ApH(O>RkM-XxOsR0_ zadY3FW<_Nw=JdoY4qN@C%FAPpjOj8iteuNHoApzVn)MOz6MsiEn;qd`#O1>jB80Du zC54Yx8Qmnuvg_FLAEpKFJz-&mnIq<4Wx4OQmJAT)7&4wNUjL}LC^Fe|0$&OSfPMb_ z_<l1KLPEcPJ|1AnZc^uoQ6bc^MuDO<8`n*$hPi1|899rKTCCaVdP3BI9B5j8T@TL& zJ5JqCx_N$)^i!z!LJSy$bdlnB+iWK#bH9ovVSP)??$Sd8%u677fA$Xqp~a92Szn8| z26xh&+PZmFq!uo>zU`z0ytxqfT}Jlj{D2;Y?m)r;#ll!yUcZ6C#mK;fB5~bi)5GG= zaDki63pS{PJxijsGE^wDu=SVd2}=*D`Ygs*hK28<n+GZS%|IN>Qq|M~%UDcN7>SU8 zBgvYf%KK&)e#Yy1o%4jlaz2Yx1C;D22{R)<hhZanNUYUxyo={lIcJ~MbPTC|m-6VN z<K@Z~P2SuX(yzk#vG0h|L9n(=oE9@7!Jq;y4K`VZ`?sCi@967l#{~oZp{ANlE|gK< zrSftrC^(ff8+hnqqxTDsWa@m#>N#bXbJ$+~clAkCp)2h<M5+%p>@?4vca>$tH@c*Y z59VxLtSy0rc--P-qeccst(YGiNB7rcjD}V#pX`4LKc;1$cEBjPp?JiiWhHNrVB)d? zk59+I@3)_P1MPgL#z1-_ifdOA>(G+Q;nj>cLy83k0jA{+lvMBkKK|iV60XIn7F)Dr zWcpLOUfdwqL*t*>1E$gHM7;Go9l#+q-wFHTI8rd+UZdl**u6Be21{pY$!pvH6OD=z zhKp6Qt&8iZMfGeGmKWdKrRRKuE<N=4<i{9}MONGt4c6urCEQ@8ETBQyx|^Tt%LfPX z{qsNzRo%qb@&;a$?-LdVy7}eXu`$8et3BXrWPmo|zC~}5)OF2QcUY-=Efp0f1^dJ| z|IXmj8JMA*c!$wB>eQBH;wwP9iqBu}OU6F5bnVZr{5X8uOe&_2B6_3R^F+73$STS_ zY@^)g5nCjG+^;uC48;k2EWkacS24CQbn&zBB0tAv2QaXJFN0tuIj8@|pX9IcVGOhh zbRz0qB*^cDr2F!$RB5o(JK(-EDidzoOLKd7WMnp;cbhU|g`)x3CqnBX|1Jq}`W08T z6ZTSVkEt-)B-0N|Q4S>YAQNtT{Wr0kzq0&lcs5bcaM})tlIl+@MPyd%Hb0GIl1DNf zeTYP9b$r8wK0a((l{|nBJX4<wV-q_#&AdmU?v(Qhl`PXz@^T9bLL2d-g_33556@-@ z*z2z&lImt`Dz~(y!|E=O-L!PXB&Vi<qGEShc&u~uxUw2!tFP~`2NLqX&$2F9rqPyK z=~hrH)89Q^7<q2oce#-GKVn4r^+Eel`MtkCf-U{>CKFZ4khV-`EIxN3v(9?%?W4f+ zOoOt=UWn`LIkI_WZ-MsrkOR@kq0wENhLsw!+?cBKn!-2{qEDu#q@=$(9TQ2`L-V1q zU1`yJ?_#DaSLle8(XjD;qOVf(?kN!(9s4FuWM`ohpPc`YaMN@9eD0<ikFfM!O{ZM; zmn3r3Ps#*2Y)uq@Dar|Zx#c1?>YVoDBxO>ipMOU|w%&FUTs)Y}uB5l&pl=v?dLiwo zgsy2xs!Gk+Sv$FR9SlTk3@bW0M+m18vwq<+no|vT1BpYd4FV}Ou|5GX6r3OQ_}b8P zLX-lAzi9mGMNrP6mFFC-q!MHn3($YWn~rwNYrjyWJ(Ll&cHHglJK^x0-=RysSXxMk z@OEgT$!@l!j~Si!fVOPyti!-P0>ufO_&e4gs~IS>-9DrydY${tS#vuUB=4^-3o|!m z`J1Rv?s+ohp=ecDVZ8h((XHuSguaNtNqgg#*kW<Z^p1ARr|f{}Fo74_i{*K`fheib zbwGdhrQ1uOj8yKjSqO3){9_wuzy1%I#>_k<#fR_-kK9+G)krq{huc4D^?Ts#6t7B~ zrU*B(r3I``OP=eSGZFh<JGf0R2lXN@eGbzU%%8M<V0Wp#*PaTiuIEvL&zW&UMg?bk zqso(Li9V%YhCY{SH9>p_rzco5Eg<(v<ym4_hf?7=p?pHLBb?kh>&eHM=U#fJ1C3pT zRIH)W?nTOJNuxH=@{UK;ZvH%xfSqH45s3tYnzpa>;4|&04YFya?rA)M5;~T)p(2M- zUWdsyyMMHbw@9DJzDw>V+0<jIWl?T-4o+|NJSuO^II^rl=?3_8Vr_%p_QT-QUfOo2 z3Q<9d#*_WB|H-JIpG1Od9CD}khDk4ZbiDC*+7*Wr+Le%~q}R}&684Nm|A6bx2j!QW z^-pC*ko@GEzRJ9m<>nH%dAgPSe>(8c)xyS+8$9P0WYR;LSchgU;L+#Rq|JY6ec+n0 zl9y6Rha5DmBKf+z1qrCz%9d}LvXnk=sQjJWxrGgCo#;OnlkN<4wu+86(yHAEkaVVp z;w$%<HYgvmxNk&+Hj*ksvp^9KbfT1aCuK%TK}7B!x~>H5d0)rE&R$-hFAr&&JFR_U zrs!!WeTg^yoB&VDoWgd<6l-CfR(Z6ked&D>)J4o`ysa*sY^Kk!0fyDDPJ0(5^W(ST z?X44QyxF|9iop(<cs?@;hbU8#x#ScEgHtaJzO(dEky=MhUV!Ne+C?`&8b~meml0K) z)o%_6rVRm!7^zRn`ePKk;1XzuvrdjimX*7q{J^quh2Ti(4WE!+;E6i<!TA|uHfv86 z>Pwhn2AO{bH3<!^?52E|WEZbO!R%WSf9J}9Be^6l_iG0lqh;HdltN=3HrJt&DejCe zx4m78+kQyR?GCD_p2;~vnHRI^?xrLu_Vs60Z^xnb$PDg{Ub&?t!%rI>Y+|hCTdkZk zc=AZ_OClGh_!<M$MG-Lcj}$ZKZ^BXEip69ad7wmxf$tbnOkt*`4CE1MsU%p6davii z5A4*MTtn^EDg(98fNS3Ndj#}TT@EH^c009;c~dw;myee+rD^Pd<YIbot&{!COkdm8 z?9!T0Sjw`J)eaL}HOk?FVL62{BAs?O%fDQ4iAt9gt^)m*c+L@lLRuV$sUu`(+D<LW zUpbgFsoVMmq4j$aZ>F-0j0g!&Dk5}z05A7wxMxAimE=!1CDL-8{d4#pIp%mOTwOI9 z%@E}f5ge+ZwEFPH-vTt%6hk3yz@TDnDLv5p9l{gquHz9aW@rceHW+fC#QRII9z6A+ z3+%LXPxDc5<SyK*w@&+|^-?Vk{+M1yYGYE^v=GMLXKIrtn+Zw<#Od|}d~uWV<NGuz zclwNaT%-}bAKUMc(H3qR-}(2M)*b7Pw5JtuK_^WFIqFKsa?-}Uv&4EA6UzV+0aNg6 zL2NCtW=ay_VCm5+S2<;*7wnUiif)b$@!~k4oYd(Y^DTm}h_JEnE|bbg^Gx_GiIx4& zt~GjT)AzCP<9Z}2{LI6vSM_VYmUFWb%yDjM8ee*?jn}<+EK~rp0S|ax;xPhFuUCq+ zCd<QpD%vT^gwsg5jmf{^rlr>6fw@Ja>Ht`sF;YBkQE#u&PLhhrFc$9hlTtMd)ojnc ziJ3e9iN$P>+R_>=qUWEBmQ1g6fGMK2%3ivaAvC5Iv6qYI+SP?TVmZigu)6ea?j9Uk zGdX=8Los#GLt0Dz#XZ+;_vc;~!mh58;1RW)$(L_m0-)sa&XZ$hX>#cDgvXh42L<_h zWD~IzOc|Ye9Mq`g@LsR381<j^Ue}RsdRcE8#1knbyD*V9bRz|OIt$L55QM~}?Al-D z(#3PXWtL2~>K#FpY-SIPT?~kkW}(Nkaz>$#HGgP)iJ&n}v>e$`+-UZ2%O7m!wzLdQ zCNF7bbDxet*IxJ8oGTA|A@=ob%S{G7kF*a&D>qP1*>Q5_m*MlEUv-zlHWP>#1V^&M zNGnOEJ|dK#O6S5gt-M`!`uUsnp367x%M5a@u;@M0pbb=57aGw$di$u94hYQMH>&OU zikIxOfX9>GN98S=)_(YuCa}PX6%}q0;Vi}x`vc2)6u{SLC>u3FbzbYv=KsaM*T)pj zl<-Y*&HF)7s^ExSQM-r0f$cTKw_12zKC~2bP+Sff#hdzuYRHWEWHsJJuj;O9!0D!T z(L*dXw#Tl;VHK~^_M9H<p}!?EZj5oCt}2q$ccc)6`dr0;i}&oGd+MY52;tHZZa!og z1O;I6kXsJ>MQ(x(@nmJ1wEjqT()^Gt0+Y3r<os*V*{1S8Sag!XU_Q&R-Ly86Vhxxg zhGOpW_&~vIy`v1?rwe5MWzYpvN+24ThK%@~1B4DO#AayXK)fsLvli^|i+GRAZ7}X8 zEHk3X%gduCnXAF5$7_&9Kp_VWtQ~cTnN|<-sT##y3ukI9@|_|r4;;;J(TJp7U2*7F ztPJ+^Jm1yAZ$l`aOliw3qyRtAwoaAv<{S~A0<tzrVNm@lsnpu`1Y62PP&a&V4sdQr z|A*p(tXOG7>TgwKJNe_!LfUn;emVQ`F8qt-`lBhelTWFva(AF{l#;As)I@|e8z_GA zt!eS2?zcq$;8a4#@?0e4#4fl%)%R<|fsv(iXNM)mLn*$0BA&^!r+Srws>%wHh~DcS zV=TMy9z|cR)Vc17jids;MWWmIMz!lNl`D{GT*4K%80$P=k$$PS0@ONqlWO=gNlr3k z?!*s7JHzLFRWQ~N)j0=*v-WG)=Gc~AW;e|Az2v^rqZ9?-Oz_==CFMHxa>?%w3uVdw zb*7X356<-3Emlx^jqb>M1$NKAB6pp4sQ&15S0U|hbJauCV*I$`Ar=urD|A)cJ~M3@ zqzsu#t$g$cZ~si~GmShxY;)Pu)~>0&$nt8G@V%j&=B@KSv40+pcZ=?v*M|N5Fe<d! z-5W)$+I$fvHuJJkJizDPr(vOWmm<e&b3}pvOQKFGb;Pui7yBKwU)v`vd)#J4n3xY1 z(hY@cj;Q^$=7C|#Sz)$lCBNmn4}Cu}%MP`NBiEOA%aLY3pAVVBUP6c&r3yakmQA)! zZnCHITy+?awCZWp46Q^cp`8vQ?0xnkO-*iMtE2ViLjJu2Z(;j`vVnoyc6<6{sr*p^ zd#F3|ekPOUiXm*;DB7T<g0jZLbsG%g2Rp6hIZ3O(@J+ZEY-)ageiLx=wH@6Y_kA>C z%fSpA2^!Z`nAjyjY9K}o4VIfhmjfl4kOEL97r&ddQt<dy8eW(qEXsbq?%+m)Z_B6O z)R18_D#9vksQC%3)yU|}>r9tcfjf72?YSM?=+UnAftvD}e&~<dt}}xv<cXiIp+7e^ z-<a*oyo@#vd`D%ello#x#y5Ml3*m|cu)^?(+dHE4vRocXOL+I8A29b{a6y(k;5!9O zt+l)J#b^W-`My2kQF!Pnn#JB<_~y*4B>VI|uL5Xc<kHfJCAS5V4TpyT`!<E=ZV_(M z!@c*3R`{m<bzYOWIb$D|Bm87<CK$RVs~Q<$4GI4I_6L@o+AO5N+7e?Ubm&aaA=$!R zlJ}Jvvi9uCvyod1L@0{H#$ZMRY#rTcfsW65F7fMM<KaqXQYXF1{V`8ui#$tSLRBM9 z4lVgq-5>C!Nb_!(`oG#H%1245^S*t=P0@RsS|I#7XR6q7eQ4JWfZ!zr@j}?C5hYbY ze=r)KmrGY*Fu3>UR$0}gl|*bs-;&{Zf}+pzH(H|;QlmVhZ9fkWz#?s$9om_8T_~3! zhx6rkTP|XcOn0h99_i-@+haZaRg)?mm0L9wIZ@KFT#GUcLMi>+#3KOSAqqIU2wC6l z8Z{_xdicX=4mc!GebSV7wC5PrYtWG@gn4@Idh05iJul>alveIRWmh=cOlwv2xW%yB zE;}OXa4<E+ryoc3t2sJLtp~%}@nJwbjmM=W-!B`CM?4ntn_N5(r_LL=I|`ko@y%z; zqLU{eG-u@01iq@T%~XDWbu-v1ii#MtThmXT2WKgaxXfd|4&gMHy&-$@;z4;y9eGq^ zuY!7f+S?yCI9ZUqzcSKg+;uj=_p&EHhBMeRnT~{vhaT4rFJv06hmMeldKy|?U^|p& z$jbe$?LN~gY~zPyhi0K3CWNwc8(FyQ2hA-sd-vE`<oJAYtLh0oXA-x9P!7^s;5_ot z7={&Xr*0JZtbevQ>;gaRU9$8|{NO9*>u_xoWN_T(&X8GM$(D)5K|$}rwt-Mz+c1RT z9g3uS^UJ&|V(g~4ds{=QnRGjR!b}>eHj22BNj`+~rj)x+^0ub{;l<&AiAKZcs=4e0 zCp#u`HBzNVkYXSdVn5&x8KRBWJ)=jxyw_qRt#QrTTJ=2I-l#8R&WJ$3SXdP=2Zq3m zc80KE0}SE?J3$WdMqTW(sw8&^$0o=T+azOkO^{SVYk4wqaEyf{ww+C>3ZXn9;1|yL zn3#(BIHE>PXt5b?3|R3Mua@0)H|Wf<=an^|4B~p*UbQo#UTvi7{Z1F>i}p)drgD!0 zVT&gJU#z`_TUBqPu1iZvGXY6S>5`IeNm07HB&EAM1q5jXlx`*=&7?af-QC^o8Tk9w z+UuON&$_N}uk#n^HGw(a_j&H;9!VELrfx1<-Oba33wN!)wWyZoFNV{fx)bsZB!jgC zYL)Gq{RHx@`gjk0F3ylK^Kq7kyne<QW%t!IrK02#>B{(|R@%2w*9^T4*!#>;7z9eB zex+t|Z~fDmuXJfWFddq@s@Z5!o02`Tnk3!UVyxk}Z&#)-a5VPvnt48)PBdA-4_2G{ zmsgf7qvZw#s_+W@HNu?)OzV*$7AFai<DQkfmOkVTY&{O&?7A2t@c$9M7xaPI059;8 z>CUJ@@cM#8&LtCdfHu=8(Dp$2Uv*|UqVjj)|59gyKmDyUCu}xo5sfOjEY&7}ROtye z0XoN5tQ#78;J%nl-xHJe^n$=Ba(+`IA5Csn;C8V5q*Gx|!Y`w;*+f|`SM+0&Z>L8* zaD~d6Zr&xg)4W=(Hea(rV=6LuM>|hYuP$s7RJ*wjez^tqA_^jqPv9%vb={@nLt)pF z{Cu{KsI;WdpTAkVc4d7)U4+Q3ooGg-spxs{*%IlDNjC0`iPvBBO5Oc8)k(&P<)M(# zI@NvHWD-EpvLbr2Bx7sQB?<muPhZeum~BC_?O>&@Yr`#R3|=G>vQ?KP1X5jTHNo&x zReufyO`QIppR}a0AflwEaiAu;$ZIs)<mSuPBfZPn$Zd)^AFiz_BO;sX%j8*OIA3x1 z5yWbnXx$59DEFvNzF>Vfaem?*$j7nT&1uFqQ{+7K|LntUaY3+6uQgx)&XQq8CTwU7 z7l;pvUc^8~nC26Lt;wmIr}CSS?D9s-)RtUw6{$(@X)-0+121n`E~0?!fOl1vAbX1X zJUpZ%Bl(fGlj+u=p{*P2G5u(?x6hTQ$}RnQ4D*`2B%0H4U76s*`tCXNxXR%>e%E}x zqeu-llOlP-O5TO=`b?{-XsRf8)*t2Itk);?jS^2D1BvPRTFMq_CDJwa`T#|ffsUf* zSc!3q>ojK1qQmxM0=LZ)T&4azrUUvk$yHV!iF^>;pd~gbP!%@<56hO){`Y%C^i(7X zhE$~kmR<ZThp2ouhd!9LUh335Efm?kSa>J8cE?|4HI&*B+yXXVEW3Lg6R@W7ocVKU z;KQy2)|8v7BHY@r@HjlhlyLq0HWkCX$!_>k$^CeolumHsPbu+tuRVz7u~6=H*q9p; z?0nX2DO5h9cqM5P;*X;`)J|}Xhj5AN!RN+=1Kht$7~aLIA%a)a7Wgxu9?q=pJ6s=S zN~gkW-eiJOY(Da%D}vWd?Y+3Bl*EN0-FsXVTBSplq($0cj0w<pK|JqG|8Sf*xh!P= zDBc3QHNuYUF1hT1fjjegdAY9BT63{oN}bIn?udah+uM)E=2<l&R5VgXQJOXyvoM@Q zKqE_5uKpvJ&p`!q+$YNH{RfE(@<{zuxaCg-pn(VyHS=f+w#-X!jOG`Vz@vz~u;wIf z&{BLoup@3GpGg0OyyCox<j<FBZ{eJeb>2S5Ne!OT2RBx3=^%C1QS^9I9XBLRM9Jri zA$jFeU_~=iH}lYtt)1R=K>py@tZ#=EFk+UPlR<f-o(%Pc(G|en$!5~DOE_T`V%cMY zK4YW@y&pcfhh_~DpSGp)gM?z*RR!3@5*5I=%YgT!G#5uNZsGVQgr<ozvJlzrAxL3p zcz5Z<m50S=y53US#mLYG-qx%QBs@RVgj|N#{yk0Al<!j^30=9WdRv?BN2*4j*H(;F zR5Mns{Isp>7nb>g--z=i55ZH;C^m0m!{{S4fICN)H-6Di#8)w|ZMdU;#^=gH){3K0 zdj_2dZDbVxqN($St3lL}D5SWG-^Cc;cBLoJHat8Zu^M8KbSR>*>Y9GkMM2H1+zoH( z;a4!45oH`iOkjIXQOf;{(@Xha@D`>h;U%_WMMqvI%U1cRI_04k^izZkP=|i-Ioz=o zna$FzS((r;6NmhEnupF`31NnH2eB(RMiles3`xp)ppM?GxRcYe3DkRS_hDZ9KHf=^ z^IF|$cc+rNZ<rby_%Oq7GnlhaPaVf|zdidzudT1PnpB7qnS4sA8?}2T<@B=g7q?<0 z1<iz684+;Wpvcc?wRHu6C$qV8(^BA-n+Y{o{CA7VTpzr5Ho@-b8^rn+uB{1#jwF7_ zJ46QG(x)DzYAs9oIs>a?WbED{-p33*)@7A8d2AUAJ~rM1LRK4lQ)S(Sr?Y%IMPdaM zTQ5ToE#6Mn>s{i^yS|H_3XLQ6ay>s)$K`h}41O4eH5*_Y^cK>nZ3dh+EsRm?%hw}^ z%5NyR(P(~`mL!wP6l{X?h3d_oy07lQUPY0X%?u)VavQ%Xv$iE;8Bih<IT$E$eWce5 z)UkjAaQ@4`V3}BKNis{Y2~hl77vYdTqpy9te#oKNaGh`SGpc=fmay;FemS1#G``8F zVfr+9YS06d@qi)(R5{(*&546wIKWPD-9O;ykvG6Im~bOqOZJ~I$K^_Pcn_{Nw=b<b z5vE@@4(<f2Km+$1UcS+~0ZH~*nwr87@^pOCrCVsclT#n;IO!7Yy|Q5qe9x5gxwNn) zYJQHX=IvzB$q0h&%lu*@AG6vF=oEkCDt_kZC?^Kx>#KgIqKWu$+BGTL`i#ji-gsxs z7}6x|Ymsaw>?n{sQX(dtB;q=_ROgzaL%tnh;h<_ciKHPI`9R{aFEr+$s+jV^du#3Y zjMF`Ui4fR%zG_{j@qxl!FzO|LQZA!_&SB()*`XsEFu>*UFwNavymYL6y!S1?TcB~Q z{335#W8)q;C@N_8{d~L}aE7*X%?MabLl3<>Zv+TNJ@!QL@LrT$1aE|^<A4_be&n7@ z1LPsk$7bdK_Sig!{jk@yJg8T9AD<cR>(uvkUV4zpqU~5OgO_L>NyBUFdN}2_nO{w3 zsSXhyO=GH!Y2$`5A)B)zx1T00#uK4H1e>JfQ_Q1dCEm<)56O=^xW!MHDG51Vf`bOz zqOaclSa9IEOHIxKTm4CNHoT1)d{tR!?EyR~g-YF}<#U+zXCd7F4H|8Ar1^`NhgVDW z!OWMa26I-~E5)BD1#wj;CH10v4S`_%^3&^lXXGEQmCfdhx*wd7F@pVI@@@t9x52ZY z7+ii%3|AmxJwCZt$FzFi{$3<LeL!~1VVjg4#Vnxl)gq;VbWc-G!uexLA)VNx%DKZo zD*BoT(#}#=dtVcIyTKD!SnJhSlmmCri&-OmE_86sXc9XYFpAyVQLjcy8|NtV{z=~% z+$GOaLuRVd?ls^1r6XN!pAbhcFbEEE+IhjITV%ve>7@Rq&nR02c%3?~G7wrdyh2L* zl((JJq*<dT6}-q^DpiD&t;~(~&gN^dB*$B1p-6VpS0DaHR>9=&&Uo1?i@aFHM&!;z z4Fw>ELRj+MmU0{507=+hi@B{Iylvz>a6}Y1;;tAl89&7x8e0L(6lmtDkdk}v3en7c zf%<1=eP!zQFNgHP-mO%8bOzpBB}qGezs$?9h%#^3$W85P;WyZo6V!0~@XHZMwcFQ_ zTz<3hbEj!H#T9L7QH`tdi421^g5#PB7D@a%8V{(ZBov9cgB?WPra&P9+^onogM(_L zYXv_WWg(?uwZFK3yS#+nIV9nzDsYwb#>^nUQgF~}JZcvy`%sMh)mPfOXU9{$)wN<u zuwclDp$+vs=&??*bG~s)T2PO&EvA8--)^eTPRn6zLLstFDEjteg#Xg9N8u};!qM0I zcw=xk;#z3bo%_ZbRIZSpFZCNeRy}(zQ|juziWo%`+}{zdweuq$*{m&W_}<5Vx4#Ky zJ#T>#Omk!)BFvdWWUyxN6dj!LPs(r>*(ZzgHW<=*l`namm(N8FLHejT!=d$7N#ZX^ zfSmnrf&{?4p-OZETz%)NyU@HE%aX#0HzioXO1m?mc^yW+KW|_fSpH!01a!_q71ng) z!x=6aEKv+@gfXC%&!0U(9GV1zdDP$;snsky-MigLGvjwez>}nrP&XXW-s9u6A(tq4 zKRj%GmCu+L8gR}YbAs5gNxCuD@S;>_pt$)+RPKCO?bWo^(=QrcQX5_z?(v3nH3+<f zO0Ojv#|POF@HbJB<iWlp+k{~vk~dWb$s8F=**OnQU2VHBMp92N_d(I#_2lQd6-!Kc zFAYMN*%T^bT05>VPnCGl!_V$!zCA4_o80SPmpl!Gqn&aerF}@bOO4aLN>oi4ZP3(s zO|}IyG=;{Y(=G;Esr)9ziqW_qEY6Yt(n>c3w^p90bgB>h|9NUsb<l!PhU#{HOm?uB z2+zW!m#1I7L5x46{Wh#7$q<Q+BEnOp+4S2_G(1#YD7>(mPzosJTt<}#Z9ZM6sxV`* z1%{hw+*q|6-=EaKoZo-wV3reh7>)ws97IP~n`?iJF6<f~x-pD73O>k{X5Myu9$G^M z2_%xO)ROZDA9G#e=z`IqGp1IGT?@MP0L_@!k0sNoWKkgXI3leyoF<vp)Xth(=mNd$ zuRlSrLXG%a!3w(VZb7HW`Wr2;&K&&HVw`)!Y~`y2J>)T`5+!T-e8xlitX!zj5ZQ7* z1z20)W8Jt-PDo)v7*%zXAz38@FWowe0CRb83Z_Ox`&#ns0n&%j6FTjs<msM4eOn~S zQji<<cBXt6;=xZiV}Wmj?S<~N7F%M-9MW54_RZtqX$lWQDC#^jZOTe90amb)rGJR= z`?Qx-g&PI&6*mlj0d6Vnc-+4*mx~7P>pml~o*?M2QB(zXI<s09;jKCbAh3lcK&G0A z`O&!m5255y^)r*9zd2zFz*-=oj5EqK-Xb>9-t*&5b}=F#yEDeTI9_PF0N}Kt#U{Ib zWwX^UM3FE$j7GUG3r3$bAFm?tDgin~$}H38X`0Qa{hPvagXOL#oxHFIIyd6}JCmS& zek@g_il~`d%HIsijFxH1S~JvYuR2vV4+>_nR!jW|T@_Zlixn6rx5$z&Ek6!b%&#$g z&;C`~nW}yLLolmWzJoN3!(-h|VzPm|kmYD1jHLEWzAEdaqgHd|W*!KOVs7yX;rTgW zMoH{szvrL@4`)lRUA;tLM%Co%MV{o_uC&KhU2CKoq|lf(e@_DPEqvTC4;$@gXg3E9 zDq$;y@4lQFljoj%T>9|cclXSFW@RgEOfWR2Qya-l)iH3%wyzv(ltXcbHAsobihZ@w zF0hy-8xgzDvL@hlyDAbXDsm4lw8N<|qAP4vH=8;#7|ztlPhP$<s$u6dp|xyEX5^XK zq!tmrOAq)}ArNEDGVOpxL7=`-iW{bGCkp}HR5pBNJfEH3_0GR#g&^w?dB`bp=q(Hj zQQVt2D94g43!VEb*czFo`u8dat{9Q;*~RJZ_xwFk;AX;Fsor@mxo91mvt~vvzU+Q- zShP7O?;+bvw)RthtrU#q(Ed$o{WHUmWJ%-&;fMVS_ag<zIz!nGeci^Lj_pCW!yU4F zQ1Y%2lOe~Un<8UinF>A9Arom$2gYq)Z}`VJaknkrIoKPK4e&UJR|@?L380T+!uz|p zDhyDgKFo=?pPRs9+5|xFeQ{r!qSVhCaio>lZ)?&%czW3`!mkkeq^wQZ-sD!m@V`@> zuQBS`xmRm-h5RV{V8x*gEA;~V>I(C_Ox&dU0fV;2{K+I~!YL;S8nMxSWgKQnMw1!A zQo5vLwx|-9$<I8!mo=E+8%#SA1NQkcuEyb`xsz>m=C-*NH^(U^L}T@;u2kb2$J`o@ zUlCTY80tTjMCc6MA}Dq4r5O9X(C=-g^DmdMJKTXcs^NsoNe1D<4H2k1e3Y4qeA!U> zLA5%Q{$u%PYLL9{(|^VGWdFhT>f+My!n&q@YJQCUi|uV}K~p7>kUYJ$s4L+@u6kND zH}7ro74ADC&0K|M{zT$`FrtxyDKx0cVPyDpvx@2g_XfxLga)(y9zqD^bv(NhZgC)M z2&8()&TZ8#c(8_~VVT}G3tM8qL6eSQOp_mdAygI`zxf+rP@@jTBI#W=cojdInI<2J zPvs9SVRK#<&18p`c9QU1x5Yqfud#^(=&csNHZlJB^=`unyh=EO%&-JZAs`*PE8F0^ z7>)YvfO{DhSMLXYd8HI(xp6q_)t|$n2<yNrIxWs+DB!2|ea>eJHM*}}xR&v@nnU3S zTv>b{h?qub_MHa=hh05V5^}V#K(8#Kf`}qX^!Tk9&&-=D@59f{4MPZB1mUBIX9mqa z!&W)m@-Yl2_P@?#9;a26>4{6xday}_yp!Don=%mgtJvg>LfRa(o)K&e>&3~r6$wT? zq@M3;r8w*tR3$1I{`h6_P#jQjMJ`)n2YG<L8unaKJvDQho&5}Q^?L%^Dj%C1_ppC0 zrb?G<guTK-pjx{f-(l~V`~=K4AiO#cH`~Q*%GbK<9%G~iRj_x6Y3jMHB4ToUz&LG> zxKM=*Y(rI17T^V&som)kP48~4=ndmIS8;j6VhcRG{-d>?9KzRs4L2S#q&~$@5KW{) zm9v!d`-Krni(kYIJWvf$=95mFn??6)=N#$PV*UhXOZZO<9rUqZ`PDm3)C5o}a$1aF zAcO?$dEx6@Ckch<!Cw~*rHa!gA}%pQfgeNDEPy>dK@nq(TauWJtv*Y)?$;P5zE7v< zg`tGW<fx^{JDIQ^ih1N2p1tbeSM#|kNhv1{pSHBgbQhbpdyQmSsbk9L0M`3yn#{Tf zS+$#2+Jeo}b%W((U;3iOIwfdP{vnMwT<fPfNe~BaP%j-qJ@AZplk^;?r|GKai05aX zg1JD7@2fO+;_#urzM?CzhbpM%7z0}?Mm|!kRW>n$Z$SF(ISTyMkS@IxK=5nC*COmd zv?IzD5Woc3kzNm`v%`0AZE=2?9jmwvF@%$jgY?iAM<|YbMwdK%%G<~aK_=$qSD#Fk z74Bcaktcf}6mG|jK20wc|2cv1*$G%OtV|;qdm`XQ6;rq}hAo7U29#RCf@l}alu>)f ze_BUqhrfK6KU^bjCow-Jc(6sod0#1Hb9hQwq}F08CS%EXBKnr^n&PbxFG;Bwkt?N+ z%&(}~*OVPKo+y@NMmD_Y+yPsO=~segN1^~N|LDF{LGTf;U@X1Xs!OpkNuB%?t%=o{ z)wD^bt>Q<Q{Ki``0c<%W*eaQyMUaRX&`!nj*0r@nu&?y?f~GX+lSzE3m+|-nyRoOR z%hG|}GT7;$WB*9wgm>u#k`8O~vmo~Y7C-pSvPBUZ+@6M`*>p9lk^jPd(qU%>n;v-w zEw4sDaU_eD3eRn6pesM@jL88>6NO<6l>$TQYXwn~IM?M6yD#&Z-($+^vZg%y;jOkx zmd`g@2$ZPOCHP`Dt@7hl(<Je=CH|hMyxqA12ui_yO13H4<k{0d1KkTq{hueXIb|<< z44KO@I9K(3Y#V!|+P=cgR4R3Nji$%4>zQhpkmD0kz@4NP7HQai;nlO>@!8(>&T2I| zI;~?S|Fd()Pw?tyvE>+1L4sH)+{+Zshg|^errC8jrkd6+h*f^d`@XOluGMy_1g^YY zuUwtdB2lZcAIZx$p+dAO&)y~tmzLU9H<_Cr*?lww*5nT<y9(~#h`YWNm>r8#bJ_4l znU(#&IgsB^r#9WMVFg{%r#8T50J5Mb%Ft_x|Kf(l1rVr7V(m>7Z-CKd58l9QD_G!< z`q>e-J>+YDH<Z>rz@Q!I`jJ|Y6ICC;^6);eGXf#~^d);b-k?p}0EVEIYu4FGgK(=y zyY`0+mziE$OLhrL->}lSbZ%>!7unsZ-=Udae^NE~U91!bP?}UiThWSX2PGD9wGt!# zj>m&x|IP8Z*RUn95o|Cx^V<TWw`x+-0ZTOOnt@iB3jj><cG~_X++e+Qaz9Nm|Lx@$ znD_$34l{V0fYb=Y*O~6C({2QW#Ii;m7qOW<XvceGQ5#qt)d;_loUMFs_4-azQsj88 za(TGWQn;qqH5N<g9r&sXc-hBr4EGR`-U4eZAbm|8w2TWnxs6W`;$zO?x;=$^b6mDc z;!r@JRPG=aKVMy$PyoWw+XGc%gB>FQxG1LWdf;6?{>|DThzd#CS+RYb{m(L2WChJy zXG-j9;*5=_zKBDys#?HEFk1?|R!R{if)qMiGb~J`Os(7U)kf&FAuLgeNhE$(<w|m= zam%5F)JtQ>F!L|5FJKAZ<8xa{@aJDDDxSVpR658t=cHXZ6;#gAn+!9repvmlPg$LR zp0X`9#0gPdQ$V!E_z#U~d{}l)S5DshWsWJoMro^?@UVkF)Ou$16zHyQimgDa%z@#Y zbuQ(L%`V$Mn)#91NKU^n_r<V7V~;&y;qf#QNbJ(R&%0@|>J6^6yA|QbQzzyy22nSr zz5;3q$q<>sJUP*MzEVEUR0p>9eEFOIh^DiXQn^V!+%nKn{}v?X5C8mn^_ML+vE?@S zs?Pq(coqix@skUosjR={(X>aBjM2));`&{S$0eLR&W`pUye)l0?h;!KDylLs7KHaH z@b63la>ZX@wV^<2xfscOw?g=4YR!nQyu5(P4kr1eF{q*|>m^girxiNkl&Zwf+hD!O z`HqU&$e$7J|1P6MK9~-gWqM*`^L#ITolVscdyszCxIOr=^)j+D6mAQ?w>zdy9AN}` zRz&$nK!smB=wUUSaEo|ww&}WIR-0Yn8UTVIthvpxl@s(E-5>p%%X+UU#J;C^u23U3 z6kf9<^{FVzzhlC-jfJY!BfPzZ{U{nOK|K5#lTGq-Jc(vaD&MBmwr4T+=~9ks=LDJ( zwl6P9swd00K}(v_nst;b1%Hf^o$=^Jq9x9jV<3eC`aJ^0*@uC|Mz*SmLC_@r%(o@~ zV>0mX+9(_^B4lNJQG-Av`Km4tv>B4^amzGdMqd21(T}K+JZ4Z%nxDvyKs?{J4X&O} z`~4yeE3VglqCzmh>Z682-1WZ#83-CuXfV*Y0|S`sQHMF}!8mgUGl`q*IE4J?#pOFg zZe6_R8`D5RHFlorbai1U!;c`4zY;Aj6IQOc`xf4~5d%|Ee<)%M!`m5Zx+prQd{@CO zXsywazoxTn2-=3}3@)89O$YE=!~2xTZJ$3e_}fl)P3C<V@)?q1k*k{Rs_jx3nU<Cb zU#YZ8&;r>aGx{rkt@|!j-xaH~b}E`kJ6{=8z;z&Es2n<#Fli>r$G6gNlg#lkzd~^C z$$#L&t<46Km)n?Rf^aPsay{ete}b3juJf}=hS?Bj#$Et#DD%qpdlfeh+5|tJQ6%Eg znim|kzEAbfm%AiSz*V(x0KImIfa68r<9n;=V-F0f;wFup7qoLv;hbTV7O)+gA5_0` z3O9HVfC&Gb;hzh;T}4gDrbOH}_m-K?fRx7`=(QDMp$>|XUs1~o#PYfiu!A86|9`~s zqd&0NkmX>(O!<l102~0?cOqS!bv&qV0aohd`%a71PCa|1BXIA3rIL|3EOu0gV-y=( zgOtrHRR~;!dm0UVeI~^Y(9Ceu`N>t+V2iXdq61LruDvO?!3z*EtKtA)N%Z$nSFF^X z0YDD)##e1$i3-eCrH~di)(fyd*6|(M^k57e{{Hb_(Vb&SZa20mKd#7!`L`wGb=s>z zcqvr;MvdN;)ss4C2Lt0JCe(BZ3^jJ_y*LPFyLJMs9WxOA566!el8z@Adl%AE%XLmY zfO4TdNaHQl9~tp4_iUv@$Z%eQCTMQ*wWcHI)!avqvpIAtJM%m&w=+OyNw^ptJ%W{J z`N8@&qnX~|Ok0-(2|@j<cK8(*0af?#N#6{`&Bnd@NbPahYciBTctz-|R_RW#1k<zX zpyhw4IuOc&mI)uN@tK)S!nxHZsrx0N_oDNI;to8N`F@Itc~0zPUVlh_y0jvQ_AxkA zaI7`F!7Cm<n*Bp*=Ex2cg8i8)ND}hVnIxdw3tq#=WRhi&sKdGpm<ik;ao*|iAdNJ^ z?7CRmMrL6!<KnYlu0;fbqG-%WF9m3eMPVE)WO;)ohxr>}ZU)GUM#s1gwz^!H%O5Q$ z3;A1We!rslDcA)tM6TYA%{;d7hi)PT&)|hkXN(Vz*^n~%!Sfn*9u0=R*K+ZYs5N)d z+eVqoby{1)-n9V{H`^*5iwD`A5NpN&ql5#k=7jdOIs(Uh5GaK&UM4Vf#^b=**yHI0 zGE2AAcz1O>&#ny<>D@>Cl4Ai?qKnRD>Y~YwgBQ=4U(aX)c7U)Yx!Ur$6WHpl#B7oa z`_kt5!zN+&Y^|HsAvY2d7*nV#TiDV}s}<z=I~iwZK)V$`1(EZ+Vt@Ir&?PI_LU^6L z+rhy=!U?q;2p|6ozmG~{|0g^>ajvmsJ(zL!d<dUh`GF0f(<S44Hpv6n(qpP$p#kV> z_1>2!Wx4Mfm%q$`W3zbnwRu>6TlD7@K6+McpgrVFg-K_#zRaXVceiCENxeBFcQ7`f zr6s1w?pi}JCeyi3WZWj_V<3K6k&GfxXIYBq6`H`o1k?7t!DM-XpuQ+YvHdBWjfj3n zp};hqKCZ5zn}D>)d#2Ek3!wyi)nxfFJ&D!tD@OT=k`y0s+?I64bTj&q8ca;&WPF$> zCE;D+s8zB_vko5f?yL}u5P)V!w~<Jjb<N)k_Efs^9Fx7dZd{d6iMYZZcrdeXXA$A7 z)tEb^GWMN@$HXKP+O5D&Sa#_D_6!&UWocNVc?A=?-Z5m2vrY9!@soSLh++`iZ90SW zW*1xuY)%|oxyPC&6S~h(Nso`$brGvB`?0RMyg2#{7(2J*tdt6q*3B06MKxUMnayV# zz!$<wny~DXN}B)c7JM?i`pYtUmdZ#=Yc^Sg2pFq=`|y80R();jE}YK=$a)Yb`#ski zQnbv1Z8+gz(%YfGcrpbx9lsCWK9LSQqrTb;Yr1SD?~2Q*kr#GlM7>j3N?K6E%Cy>~ zj5Rn{k7)Q6JqrE+KhviT#A-m)U>R!kU7HA&4eFtwLi=%W^=F&VCF}2)K#w=M1CWVh z-in%EWgiSB$0!%YlouK+vCVbZbQ~PczYWb36AT69wdGfg$#VR%*<=C+F>>5<{tS6Q zcZiSoPj~o8(`!k0L1yxL?1^QIU)P>O9RGLZ;$zw`6JER>gO-J9ujy&tAm$PrP8JTu zjPxSgvR=`IzSlMk7FH&Hn=k1uy*3t7{+vNKpXc_M_1wqo3D$98Mq#RkAi98&AL-xU zN;{cuh;FcV7|t(kv!B_Iv);wM_dO}uok4o+9nKV&PW7RS%rA)hMOkCl3wG3k7I>k` zjyteyG)Bp~%V-o+Gc64igvsCN(msi$j24<VDaoZUl?;A0006Lo{QoTgMlx(!UDNcc z&+;V+kpze3hLyDQYMt;B^36KqHfF2urOkO=x!0Pez&M-$CDQuDlYd)iF)g--JU)y* z)+eU6R}|N~+rH5?pvo%Pribmt;!_p0vdV2<DQYH*c@lo4`?7NE^liJ{S0qnqZwW>A z?vTx7?PyTw0@?a_r$2qZMM~KHI)ti2V4qIeKz2SoY<Zd5i|g>X;SNX*XN;z`RnGC1 zlnQBZ58I;`?D>nazg&FU0PhMCzz~4dWZpO4XDkVCGO6#plFqwBllU4Di0Gf^N8T{1 z4(}kz<($#Q5c^cA$O}^UT2A47+BtLAXut2@)y}hXL*0qcB^Y}`ZE|3_iQ1n%MUKPH zD++|M$YpJ#)@~ww06Z?~i#6DcvqmpT+<3~58rV(HMU!1;H$E(d^h)7d4<yT)mcQ?9 z3tp5y_<65xJc8J?@Zxywo45j0?*WLMar`wbWm-I7KNiG;EO<k}vuyFQF+j6h{`afQ z5KP1OPoWk1Og7Rw$@A)>mX6{%8kz0?{VLmk_sZU%O^h0R#z}G<X6QY4PFwm1ZVL)J z1{N0Li%e05J*y+(x*BWukLHSh<~oDvgco8-+bddl^rj)a3_s8dnF5K|#@@+V@7nFi zaxl3VV6jsC-R6o3tS)is-}VJyW^kRqq*H+9l?$+09V-dm_edwHmFszZ940e)qpeck z|3f@am7tn${pTC4vWYX*CFf5j+;8L6@!EJ41amxRnB~l#0l()VFSaT%`ATG|S&qUi z=xeuS3;Ta8bM8h5EH^<{Ehq>M*ERSFb@xOhpIsY}(G3&_>>v&*E*bW9boCEcJaWB8 zAA$dn;5lG82ynK{yEtyQM*wY!fr>^CGcW`%kcFl{QNzU8-Tv5jU)v?9cyX5qCBKiG zRdE&Kp7f>yRg=yJs<X-GWiTt@j#w(-Q`7aVOCCh?Z?#Oi4gAP}I;<0ndsU$#3gq>A z4$c_T7_xu*t2F{i>H7S|AG8hrJNyA4On0qZG61m|O68&hBM7HDvAI%eYRouqCAdVm z-eB0(PTR%yx;yh}(T}M_^^FhyC?2#vfHL7JDG2mFtgt%ybh6b^c!>+1%;&mx6DY5M zxIEsP_*+7-RTc04CRl}wbb$?FUDyKKIBx^|ZFXy-&$_f@TokD?*R0gO?6?~4wgvFF zopIrgui}&SzX@gKHmyam^p|B$v3P51GK$hC5%&t4C=I&gb5LOwv+uRDOgo%aYxLp< zNe>2V6F^vIxt{A7QWz`uS!=dbD1!lN1+ukKA~VG;|Hw{9L}ahE|Ayb}kZ0z!lrl9z z%(MqXcTle%yzXPiE=1sM6MfTB-{TBCoLtzSR?lg#)p7TpsKc)MAQbw2oxyUq_U?tc zLiJ?mumu35%2GPHxljhgFrQfxF_%&^Y5iS3P1>na{<Y-1torW^QoVdO%c*p6P@kn3 zDBRS>cZb6-nC4a>8K&gqgO5@cmF?HZz%xA5wXD;lLgOpezSC+n3^kZg0Us;S6^jHO z`bjf3P#jT&3y!+8(ZG$x#dH}o_;d#`J+Dw9$kaCUB7FT@_<odu_`|TnfKK-tKI&X= zTD4;O{PI*U4&^G%TZ|sn-XD(4mY<WR9EO<`9kC<WBrUS~Q6`|ghmIESEZ=zXI1X8? z2U?VGPRh%X!8CZVzttqD%w3MwJf8?vQrv(1TqgSB$QvmFhUBbO6~!!@W)!a=ter#x z8G==`0cYUUZ(&Z&KXHSoy`kw_n|X+xA!tX+e`tkEka0I~>B{T@XD8kp(~ukugQaHD zJv#<1GViAnfSp8qO&$fGJ5cXCJFHBmcRfU$WI&HJOTInXFrS-~1@^n;t^q!L3lW>8 zGlZ-k99oq6QaBudL&EH@Ln8iTu+iTy92G+hG|uH*O?itCBAC!$SZE6D(uXQE&%UVQ zI~zTrsEW^ujJw4f(KG&Nmein8pPt=jazVQ(06r(gg|W^0!<(sk=yN4`gX)haUnWTy z>kx#67LY}liFjl<guujmJ{m#yjFrywFt;C+PseaKvZU%?Y}I)kmM4wE-9c<~khX}L z99=1X`IJ+~PfTw_VF|xg?J#8Q4&^iC7UL^*G}%s`U5#{693Y4v_jr6mCmLo?lNeM3 zUt;mEb5rS0DT&2RaF4#@!cP0EeA49?LG5BJE+|aDgPC8Yk6to7Y}$)pYRXTYjZ+r? zifBTTODZzeWGO_psCQO@YYD{L^3<bNAyy^iP7{RPw{j-6UVW^HIjTz+I1Dr&zPYQ3 zlolS1O?TW^9x=YO<D(KWgEc=!=(GGpi+tQ<g^i^bWv~NpcB+F}%(7y$Rt<B^!vbvF zh}`a6*pwa-9#Xnpi@$yZ)8;3hb2RNA=BNICari~6yao;P5--S{?zO%K9!Hm3?+5$- z0{cOEy1~52zk@)c%h9m>%~|<mn>IY+>xU%asN2$h*5fZfs5_7!)33%Zvs#h&p)ORy z2!7_k?BBk&HB4a-xs#K^(TD)%MOwgWr~nAs9Y5F0eJV~i8l;PfP;zp}=iJjSTt<b; zm8Lq_xdOxerstCZF0D;ll|hz(FSpu^6wHj}bdU+|oYpqwkJ4tTB;2`72&4t|O*?4< zo8uya2hA>3fv1Dfc&riui1Sy5dWk1xiAzRmh&i&O)FAm>Yp&<akleu?xRfNn*<y-) zxp7~5-L84xzb~y$Amsdv_#SpOgZ0?UVb@?BM@lA^#8D<uJx1eKlKfjV9*&(LlbrX_ zVS`xxmbZ%6mT*A}-U>HV45(-n=p3CV60({hG~zAaJeIL5y@F6Lxl`U+{27Mc3;Fix z93(nI7Y<#Vzf?28bra!bAy(ev?Q0s-W|?AtO;<Z;L$($IB3XIK`p<X9f9}nTTRbKq z%YvS?W>h3|t3JxfS@=ZgIi>Oi!09}#hD7)Jz|Blz$+#PX5hXJ2?*qD$ZSPF{uTu){ zKd&19yPc+~gI3CC)3Z1C^8F7!<V%lv33)y%0PO75^{{TI9pl~MId!yDf6q5$fVh0| zl@XHfXPF4AJSn<z%|GSSjufd;eqVbVn{_eTP_9?ABDj7BbOOw?8MI(z)_n{PICp~> zq7`=*`pcj%y=Olp{gU~d*S6EEk`liNe(zmRd)V>-VJD5@GeUuL6GJ!_@NB?-djJ_= zef=kb;hDQi0<P>CU*+)%vEzuu95EN*e;mkQ2|J!<otY${svz>T<l>_q<Pw>I2&swG z88Dqn;?kSu>UP>8Vw`8lQ*jMK@wk3~RAppeG1cMVW;d(ZgvG2GF5*-GV;vpt>?UbK z1>;u5gCIUrwwX{wM2@))BShjvk)4gaJl3#zfqc37l^pwX78RN>{9X?=;S2ga<1E2K zm1`%EZ7)2rzZ1Y_msySU(vp&6MEPDf!p>RhQ;cJn{*TM+zuU+9D5eeNcQI))^(Df8 zwO(Rcits)x$o!zkcK^b)q-{czDJR(U<w+W;viW6QEd}v{41TBGL0T~i5h71q&%S}i zIEZ-HMQGyBwtAYbZRHj-w+oX8ds4hOCV0^h=00FAXrt`=7m__8zgtB4GeRfSl%_*X z9r{b5Zi-pfFr}bE;c+5U!f$1zo}>L==B|W{yqsI&cQ{<FQm#%8zI{Y7oe0CLdG6d{ zI08-ooC}wKb?rPD*@Gd5<+xh?j&AxsB67CW%^ThF@L96f2Oe^>6H}9fk(JY9SRRje zs7gkXCMH5at9WPOWx@@2@W-n!=1Z*Y^RU{e&~X+IhmZM=w$Ji3x~%^~4OqLSOS|-g z5q>P|bb?Xcf_mKs&q5C<4I{e5)=?z!L!!oIe-0HT<1mzyVD(?<AZO9+kN=g}I7jy& z{Qd3$q%}-~Bp)KXlCL`LlUXhiG?Fd38NGlQ?=M*e5z`-H`|D0yICGnVM{n2C3qB6h z++k}Ch}&FCgl$5<FO1xJ7;=u}CqL)`K76*Y6eJk(%D(zey&1dra-@Q2Y`^DQFe9;7 z_RVY_W3$Td);7=Czf8+#)D&j4r4q~Aqr5gvCd;QV{-%<^Ar;r>tvu}Bwh8tMxWV(2 z`j~JW)()rNkj=_MDxyV_=%g;=1`|j^qO8*xVkr`MUHDmcvY4StfLL@NFx%ArW43wW zya*r6p;jWn=#?V9r}?)=w>Y5Ue%&l&!%dkg68p*zp_fY7ZFBx%cvHXKT5}~#r0;h% zN&fH|QO5nI;$9yRpQ8tUr7f*t>`QhHm3AVZ<wGns^WKBZ0^^D*D{~uN@TFymuHSjU zloat<P!Pu<>(Frg%8%RA7s`YT{$HAeLSkTm`$PD9CcJQT5at;5Z1)0ZlwI=c7;IbS za-zq^6JrFV%lJ{wee8pY+>QbP|1C_$XZ>y$(!blFEL^hAZA-^PGtYP4X<oy$1?#rK zkg_;;#;(P%{QTzYG8q6&FN`sS0g~+$b`TW{0sg6t$#+Js7gG+g^CTK`1sl#y6NYu3 zGVO69*l|g)?6`nkxk4w|e~O8LlgE!AJH%p|)kM>_B&|2wdkS&Fn&YXM)y(WFIwQb8 zM$4@RV<6(xa45sP^W7S9WRK<09oT{;UDoUSpP+m{zFy1i%j3IR()`EOdhMeo-|@U5 zZG(sDt9AGBZ-FQ=68}*k@c&DL7DQcVZcO))nYw9$8F1|Ii@(WtPF6<1c)+7Ycp(Lo zZAF46ksU!;(x&c&Gguiq9EOJts+`rLR&2wdN*+|}u^tmOpgF<J5?lv#GhGx6dU<UE zu!DhJb2>ZN2r`5paPoQajGaese&qUpIj{|(3;c7OIoWGn*^QxA1$>LxI(<rq0_IPT zyEzxdG%GvH4iZLwWMW}pI(GcmmlL7zeFfvc_;L>a@#WB@{-%!ud^toz-+*a{&tm#j z1-6;*JZ2`>I-~kf&lU%P!aq)+VxXZS5tzsO^E&iaYmW$)+UQkB)!_)>fa*K<<X%OY zCfDQLzBJCGOzZl}grjxOJE#y5wgDznLW_T~7_=XSJg-aMnL?dk&Ncus`pUaNztK=F zB1L^UzX@KOMk06MFeJ+5j#dJCXy9tkxU_Ik^C4P0_#GRL)<ll%uiUDdslkE*i=PzG z;xk7^wl1m9e21=dWIP^9-Im9?k4vY`Gfl?^*0(m?XH-F7a964`U>9pksVN|eQ8#q* zKxHqXxAQ8rbLKm4lB<e1(cF+y&F9tjj5k{Bd3;C&Hip(}i;BD7KL(43d=LT~pN-{u zE(sgnE$g-3q?bjzZNeHCxmkxgA{cx(M8&!@)Jj1Si*CakG+HE{(XTLFCPgFrbDdTC z07#gZz54MEio?zW3@S!2mR^y09~ZT7HM$(w#6qX$xW*3N$bOrWH~B`#w5J+v(#`OP zoJ9IRB`#XbAj1Me2L>Jw$zTm@{2{zZz)}qqjh^bl?pIX%LcU|xKjpW2Se4c~`=q0f zZh?R9=``ur^stBZRChjG4Zd*7>1PpZw_0AjwJO)q0MDE57w?gKwHh7&sB+dCWR!hK z7v8@#UYO7%Wq0YwZqu7al&H{cL<x2k$2veOYP5r#==6x5t!xLCF6&IU#eL05z;@q9 zuH3xC_y}hBEsIT8JS<8l&*4j0arit3F={*#Wpo7iPf<@`cd#{ZB!0ahC*$yL&Yr{? zfesQ4#c)z&T!sCHUa$EcLS@Z)O%Vs(S_#Ic_`r!=@0s-pdT=ycwa{ccgqSTGT?pA7 z_b}qy_-OdO=he=DK>x_+^9RL>ouEGHn7v!SMP7#qcq>4-P-^!`*2Ee$hVnN1hoG-| zGm@C@L2dP3xcRYW6+GIC*Kz(ptJ7`M^@kn)&rU!wR+;oY3)=5qvWIuJRtMV!D*Ru? zx{X_(I~?on>S*i<jdczr9h8t41E?RSccDVQrwhLI?z=Og_jYNmqzQQyp>S2267%v2 zRb``oLdjiKp^iMqT_lT8pKz`^0^gmIUAuSgMQizr{z?|n+hDF6(>Jvdum_>u3P_9o z0#02VM1{GEaTP2Lm_jD8zcahu84;A>5+$c|H|=dWAukUJTD<*VNJ{g8!!!ty$!4bM z%fHqg9+6Afxa%i~D&KT=@SkFmQ{Md=sdkI_=IK4yVs3~e(r|J_xK!H~p{;?k4VIo- z)g)UAfyw+Kqu^;~OaK|P5>YHBi>Pod`-K<r83Dn^$zTvv{KK_$Hri(RX}TuzdAc&~ za%GUj>f?7&%C!zw!V@vRx75=0l#pkP5(Y5D>T<El|AFWluEF#UcMGbh{kWkC7>*Yt z9WiR=-*}x?xzCJmwr}!RmcaTrt&d^mqOQPYHDHI3wSV-3gGEDzxiYr~Qm+f1X;z;B zdgWM>#lYF7<SJA1kvVsrsDgBats7W4Pd}!JY*a@FC!ZsKMk88x^yq4Oby-~WwpDsm z%5+Xj$dsfSgN%<4@N2plIA=<zXIE3@hqEfn;2#a2)E?d8G>KZxvh$B#p#nIVcmVD1 z1*Hhmmnbt$MXS}`vX?P~z|gt^d-3>P?(v7R^gqYK`p)jlppu9RpJ|^#8u6aj$*j#G zdPw6dUNSrMN<{Rk&NVP_q%WrKAtCWVZ2Io5y+*&)=QQid1hy8^>7_MnIh!Y4Z8fS_ zNcL>efdec$O82sOTBDz^vi-h=cx_QXE7|GeF68N}zmBj^{OVHCidtmPn3DY#@(dOR zu|#M*EBrDw3Lq@wzW^zF7sPEQcl4i=&*;a5^k<b$mg5|em8<St^=V@r(__CX+Mn~x z$>iu*{q~vg``zuuGIl@gCuOT}-r4V?*04#ivNKY!tRon$c0>B|Iyddr<|OYRgI+LN zX4U3^@#*SJ>y+Q<d8YSy1D)Z81n^;Ysy<e|iLajBsQatxYyU^pw=Sm$C_@Vlsq&P% zBN8owX{qHZkaYR44a_|k;W@Q{MfV|AnLub2Ys{=Fwh7IAATR-PKbVNk62AYl)O`1y zF{|@yn9aa~lGI&OZ@2-`mLG6i5Y$h`2=y^pD=D4nJp%%EHYzkajOp;W!PbgA2C)jX z*fPeIJ|26GK1muwR>GZbA=M#|yk|R~2AlKkf7L-T{-c9hl(PLq0n$z|lN1C11S@I3 zFLQkRuKL2p11SkpS&PkIj<&qrPzF0F`ApWWi);t6)R+R;*j}W?7zQU*VwT?n^<M?K zCN~>nEST2#ZPnFDB(^FNr=xf(HNA0lNwFV^vf(;kYN&}QS_LO3aVMK1Gh}<BkjnlS z$3^Op3}O8jEod!(B@Yhi-A^=ApdspkZuck<G-r4n=GD%QW($87Lq1FJvUc&>9E*}_ zAUKcPy&PG>cI2^hkv=zke}2gNYw*kY!=X;@YQn-gF~}b2<F`amJ(ja*{)6i<9uaoS zeM~{I{9rDA8=BMBNC{D7WtdDk(d6Zna=y>q8s8tU{c)6psbxTLu?D!Y5#YTe(q(sf z#}`90y|Hp=(nsNt0<H!BSTQk$^I<-V`K#`KKe9e()m*Hhpt!_+1v>Yyxu4A3b8H2S zo{#MRfx;3XrA2h9{hyhi^gkjJjZF!Tz+!n!AGSkDn$SWX65?LG6?&y=AsZQ_1j`La zn>P&SJ9Xf8m5u*FkJl6$d!%#PHLzvGJ%OFe4sB~{{G4T;z1};jEs}`Mg#<xurI66$ zJ7R0I!8QPX8mGdMJx+{7rB{i)h31BSyCj;r&|n!j`8<h_9z5m}L6YOC9LGBI!cOE= z4IztFZd(nD+OBmymR8?0^_(d_FCeSR@EWcAnaZud)fcqg;Gp|8n){zsJWR+9yy&?l zl+OkU865*1@yN$q1dHjDNJS{p1qa~28+3zp`bzaJqyWP3NsA&OHo7=L*%ijrQ7hq` zD!)rVX;Trx*xj?-f_@xhXzuv4W}LKlCB%u{!$B~h9D)9fmyO2dr=5{=64Qrj#5`jV zq`)X11rXT(3k`=a`}fgs1nK7Yg+8HS+3^%&$~r}GSjzkX25yRAptA->#;3kdo}1)) zZ|?#5+v;1s%nWOT_EEpzFfG&$-zNB&YDkjkR~RW0JmkBaLF3uIbxx=f;?vphIO*ec z$R#*P(s(%x->Wd0?dk*#ez}0z0x#W;E7M9OIAV$cE5<@kO$S1c!axI`G$}Nd&wUb+ zU#NH%hCfS?G$ZgG-UwB{LhpXf{Au=n+Ea=u6b>2oaOr|NaiKA5=?u=lLBJDd)PC87 zp<g}ay-G=Pq|(M=-O1^pky6@BwKfEU``AVK=b(W0+i7jZ%)2p1b3m0wlS{VK>VD+H zWE=^L>x8<bf9C;36G3T~FnmE?n^gY_D*1T<XbCAnfCS52KbYZvK-?pQHm@kPi|!~% zZWw!glZ`Wfp*(St$^iP%wzmL`KL_X^+7TU>d_Es%YdC<c$mS-S4w0Fezu@M)G4{j= zLa`M5F-&A=UB^I&Oo1D+-GH8?u0m(#-yk8zK~hb~LyGVxPPXimTK5+cn>pFk;(xL% zExng9&@=bzVftYM{`{Bo+xT5F3oD6pqJUh6Mi>8T;st+xmzLi8o!>`9zEW|RZt`E7 zWObpx@3nY;tFR4V%Fw)!FiL<GZT7=ro*I(n6F_3zLKFECq{9Hd9%9hL#-Pa;gheUs zvm7e-!)?&xui9qLm%pEimOD-DE{XQ6Z2CQDbB?5S8RZ$I2P0eHXFs}FK+jwxTks_| zY!<r`XqABKRYm51P`&aVAcn$W++YL?%6w3ln+)?qZ!v9wy+Uj>ys9N%s$7G|H3Qz+ zfvM)l-Z-u?nvl^5>s`*_i#kTcv3)j<-a!=P4O=PR9D++R!|G?`K=<pnEn=SwC^LK% zm7o11^*RY=T6}U%fFZsEiPb2da~Xh&>6ryf+~r)VNYuwim>a$!cR17&)7s|Y!G$Ll zO_NjPUjaI&AU`1heoTN~tr56-jUfKMUSB?Hp@rR2(aK7~cbqL5)T1DHV`?a^&At_h ztta_aF8HcNvx^auEnpq5zwB}=>i19_>S{Q`da!tIF06TCToN(bXe#^1R`XCNjT9gw zVq9z9WBYo-@yZv3LY(>FtxC%p8{q!h!S>bi+5OwY)tb;_%FX5s-$>7BG;8Gt*M7cP z*Tw8LgVoV+0}YfqE!1rRCHuww_OM4sGQA(~Y?KraHe2pX)YyQCfb>c`q*bLnvk1{l z1AWAA22_y2;J6t_e_aGHr|IpSdw6*TDj&wB5X<|<)qvLR0HS=!YISdECV?)m*!oLL z;zeSfXDk=68Ju!`gHFg+azT-ciu-=zXsN_oXv7^>$4za5BXofg$3l_AIxYln1;szG z5xPk>QfmYYHNXhpw7}k?(`3^8Ilv3b)kXbmjOE%@$u6YbO1bqzMz#yND`gXGb^vLS zKx*^7CTiAiF(Px3Pp-8cxud%tNG`Q><e%!_CIx5W#h!GIhh~<M(SjvtUypiy{JZm- z`8S2J0xhmHQC7+0m6}bF5rx(B*DZcFrez27XVLqh_1i`4+=j#VxwA%}fIE+;A{n(~ zAN4x-7=~N;`iVXk*FG&ov3-?W^sRBdxZ5soeVU(Ww+#v}UV_{EF=raetXKwhMAXI# zN;M5%xKW|!gVd_K`9IpPmao)whKi9E3O-FR{f))ncrZ*?&r}uPJ9u)t;7p1^<Q6Hp z<2S)|vtQ;!=ZU9>^Sk68x!E}_^Gob=F#RBs9~Uk>HRHRMo|@i7EUXL6wseOVRIsH* zoL#mj_{9r4K^`n*Wa%MV)GD1LnL3MoCAEQ;MDCb5TMz~2>a?_6*w|Ol^ZER1pVcb& zX9>qaLFD&fU_bolA+l9eE0R%EDXg;B*hA8pHB;=eL23{6+i6--;YLn;#Wumex2Wh@ z_Y9@c_0)1emdAnfd?Rprfu^x^^^E&nmf_#1J^}h%^2OP}8uP_+XZflsG*|OL94?qa z-dnvub{&doSl06IswNU`Nuo1jl=2L4UFf|rxw`0RXbHT(-4JG|I7oyo)VX$%3(UX7 z+1F7<ip}H^14wxijmu)7+EMiU4lf{CYx(+0E_6mrMk|4w)h~Vzi@4wN^RJnI%&)UE zGAd{&tj>`$y>efn&u>z}y*pv#r~_u0amc}+y^vsA%27ZhpfN$)BM)q36%vv-&27e@ z7%zb+e?ECMcD8cl`_%q-`qV4-gZ=f%!FkA>DtKU5&mD~oo<`M}lz_7A^(U@trbU0| zG1q5X{K${Ad$yqWb&zG}aw3p{WS(YsuH>LG#=$?zW8QyrsR;ZAYmC?A2a6MBOL9j> z8{fGe^+2lgg{z)eX<6L1iM)}19Y(_2S|_pB!Vq!>jjWo4{es<Md<ZaDoHLY=3_@!; zUpYWXmPyXac1_APW3PYT$#p68b^D2ij<$hvP*)0gv5TYo=QH@67>l3vpO$#P1yfxX zcW+(cHKexq>t7cFGUJq&j*8XKwr(%iwJ*6ZOh*t0p9SxS=~Lppa62(u;D89)>Mnk~ zDp7Ii>1?XMjMH-np2own>s9cll|Z=i;ne&}XuI~N{dbaHcVl93-*;(UtwT2nu}WB0 z6szEW%q0t=JXeLEr#NYVD5W!?aNz$IrO^M4QueqV65uLlQr|M}RqnZY9al72vAU70 zt&XLV-jnOyQg6w6Z|BhfcJSfG?n|;+zAiCM?yP<aLzWNK@YSzh5M?MFY9!;fvW4Qw zB&lOCa&T0$os%+K<_E=fb%Ynp@ko4Ss}@yfuq;YM2vrET9}Yv_){;sJVLPN>2S3_+ zYrXZEynX&Nk4f{i|9{Fd0<6}juL4N|%W)O>l93UUp_K1HBJQr@d<vy_+W!E_!HUQP zpu%9tc)UC}AeLpeJ^5fJhTl!{%kd3eh4|>62osz>>X*+7D6ov&M;0!tX`AU&2YBvl z8{+R8mfX%JVl#yAg?(C32WEz6!35<72Jy^D)??XhZ~7*29QO1ib8vj<@Q6?1E7UYp zo-a-#q)x_JMP`4=?e|Q{4G;fS>74kF(%F`ZhaM}kRHizItT?9U8e^a_BuAdREz-cv zET|8u*j2!@C!j}Kc;?Tqfo;RQc0;PhinH2#)nVl^2evmTdMu6uo1sih_Ini*(gSnG z90j~GR<$mKA3uJ(3K!sQ`~>8)3Sa%d)NaX^mUS-=TMp>kY*KUGvU?kfXUV#+D}iha zu3B*F@<&@FnvH9eKCr0{KWBlbxUu^r-+)$Q0X}#X3yOZRd75O%EFsA>Z?R3*3n)en zrFvF#J-$x)+@z=IH+!u5E;YZ>{$)HVUGf+P27M2{P|_3rH8|m%kXTqDZL*2@N6-}! zN1^(kalpSBZLgm;i;p;KFpzl3iJ{#J-s^W#|M{1)s~l*g8Li1VUFHm_8Zg=qG!M{d zk<GTr>N{Cq1Rkby?_-bZZ#2Nj`28MulkPd{BVFP$^!(S>T`Kl+^KH#1YgGo)#LX+b zcMPxSmC)kOK3PBiV(3q;gKH3GuFyD$htOx{#0M)Ln;p#_JD3M5{x8<vvaPPJSr-fh z5AGHqxVyU(Bn#I7!2&ESz{1_#-8Fb{C%6VDxCVE3hn{)fcX#iz`&`}o^yxn^Ka5$U zM%67<^P0DdD|;GFr{2mMHP>$B$3a87qVz`?h<f9C;x+4ea?JFI?jQcwZ_)(heG(G9 zVj!zvcu8&gt>EfUukq2#hS#NW4X>+KZ7r>(_HBA1@1-`I?V>hR5FvST(SjuN7Fm8i zyb0{)+pebKlu?34)2?-V`@&x=m+wa<YP1ELl|x3_?Ff5&;Pgtqm&Eml+i@`@ex)E+ zz*t;`HGP0O>$r*10y2<ZEihlX*se?|@uEMMQMX`HS=&Z2H$N{Hx*na5rO%XtMGVAn z308d!VMC>5YHSvA2+uZGZt!XF!C?t5IHL{u0DU6R6PXr)A8|gYH;O8yZcI+1-A$$R z`?^|N%N(zvJbV-36SH_N>&HTeF6-L;`LhZM8HbJv$WXPJ!t0R~Bxt4Kspb!p>f`N^ z+l`bqCe&Qmm9SxDLVJW(_<n$fIv%2G^}o)R<bOSb<3o-E!-~XHE;yYp+X2^u5y|u} zOX}kbhgqxF$aIg))vp^ir=JWQ6?^Crc1zY023_TEuCVhUdomx*Va28VhDJ)vd^@?} zyR%YAY6Q*KvEmt2Cg4lnqvw6?z~CdbpKq26WpyTS!m7^m?6q~$Vb5-8JLysaeU7j0 zv)K~cf$%R@S^mpo-cufcjO2`k>U`qfZrL~X8wk<t5Ys(+OCokr^)Ya){8CnTtAJ_Y z;>O$q;(TTKyGQx<Z%<bpZH9wQGCT&=l?6*#U8M+G#404H7G~3DIO>|py3;sBoWB2z z<YKZ&`gaA}PiV3Cw}OTH6i$TBslnZewwtIZ7;Yv3m<A$tC4s>KHy_@w)ZpP`>iDSK z9Da`DNnez0*l8z#^FR0185-dGFr{s3PMJzNA&jjmSa}B^t6qT7&)#zp7}8=(c@OT_ zWLNZn#gQZ8{$luVRQ18&G;%3R@m<|k5lH;9(q*6;A;UfH>xX1VAyp$vtWGRif~r2s zC15X6>vP>70C0E&W^hG};J(|pT6`u(n?Km%X)8`^H3!zAAk?2?d}_h6JQ}Y3BAv62 zfADba_t&k>syPKYcyB+V;BK^y*2DYP6HNEljF&ROw(dYzC$2gYnp!>_4X7q656wGa zjWD{E4TpU~1>q+Q;h8diZ9eL!w6B-Q7h1oJeUb+_a9ze3$Ey@NEi0uzusz?b)6I|) z1~(nuO7-9PCz$Qzu)EW;z;R4)Obq#F@Ck3%jj8q91xvj-G-M>Y>RiCFD`0=5{?*Bw zZePHjDO$?tRAYfFN&6QcJ*afA8Y_1vGecS()#b4F+cpvfCZw;Nd|R?xB#rF2u7Ich z_U8g0#>-eFi$Zzp)U?}NTGNOYY)A{E%Rb$Ox7lylVnO-ie^`&gN?5#E_je)P>^S(E zIVoL-T{KU^mKFwv6=etAfIWzpmz+S;f-m<SV8M)SIlE6s2@_yax0GwjBNipVzcQGz zrKJKPrD&@!ATw~x00?XdD1O0fyA;-#<e=m0AEzH-J_|OF-q%DU9#dVoLUsygiTxgh z+HjnBY{$TC*<ZkHSwB5Wg=(!m^4#L?7-gN%I&B<P@$JO7;;C8h)@x1lTK@V%S}M*{ zME~mPqUeya15-D_)%a6c_^49_S#REPjG6|HXNdDtYb2gZY3-E}m;%c#1%)~4KO(@Q zjBvh*FbGZTTgrO5k9%(>SVN*1_{UM2#Y=AZ$}4m0OTrZ97C-*?t8qIR{L47MfJBH6 zTScMxudhB-9k6;qTpQhQ-aDyl2>rm$Zz*L+Df0j+yd4w3G>jdY5YA8xzYL5IRJ?yD z4PqN+_-n^52V5bh&gD6M%CgRh0iQ9f<*n_H2@9`tJO0OM9MSVR0K*SVkpD9=a1tDU z<UubMbVQVn*5XbOFsV%!2AJ(`YgTwB5Qek-7aB{0>B*0XW6v|Xe7?1Yv(V)stT;uc zT6W;x&x?0}GS4-LVVSk&TuNvR7}H%X;hhz{u0xoAWM2Z4?uZ2{ZePt#Kjj=3S8%Me z8Vm4Nzh`wiGN5r9*dN=X93!{8D2jK;n31K)bXb(2;qJgP+jDrCc}MX9k>i)?*{`+K zE;ep|<0@lTm(HCwntiZ1G!!NoWlS$6T;xY_af-o*4oEx1$+HNgzEbMG>W*Ny+EVtX z{qgAe*t1aYQ6rQ8I!Zn{^X7PFzCS75(=K4o0><8DQEou72IlveCB14MBqpwN?TbL_ z9;NODN{geno}s*w+9{ICu7cvrG(O>X6(%7oTuWvth8foshMkP8NWmP@%x`bIRoW`} zsl4{f4i-n>|KM>kdd!N2Z^-Z*)>%e;XpUy$Uq5mk$P#v&=DfvDSzRXUpNy&cs?2#t z@TY(iF(M)lj6P=U9*Fo&XdP>)uYY3_%9;u)sQ}kBvN-HjES5c0itD>rx=wE0R-&jf z)%ipK-45X~tJj&|*nWPIZrgH)jMZI)7MmU;;JLE>sT7}P2BLCFmQ&38as7*o8{6c< zUdJuOS5;r}uwsgPw~p-rY^BVPh>FQCZhLmlI^wBtqd+DKZu-SYolC*=>Bl>S`*m72 zzFT7UA!L$CYm_QNkajq715D!2_if@JOB%c*#L$V`G{}zO_~q+!<YT2)D+vun>jM0v zCm1v=yLvjUC`~I$4MPL<er=fYo<p|kty-O|qeW{$VoCLk+R{Uf|7wrhYIZQDHFNLH zdv{d9y05o>j*8ybH#CK-Po1Y;h6}R0Or64dtrA<`(AL>JZe_*oJijB!7D{o;u}1|e zg?7@$DupBYs!^|-u{D?s>%zwRTb2|S+R|aqWMcQ)KzGlNsZ<_X(0ozXVj!u}_062f z;3oadA0B)NE@FQbM6kAVK|av}r(DCElc{BYuZM>TE<dqE+nQBuN`H$gk;j5`sn>gT zgC!rXrIw%Pn^%R2vQ4cZZB3<r&K<vO#g0Q!9G-6%fnal~f#*rRQnnHFhTK^|%VhbT zyGL`Nby4J6D`fcxUz5mPcy=pF>M@tIV5r$Z6Emu3@Yz>9LpWkn?mr)=bGedd!HIVk zWoo^ZqtKp@6UgcAlY>e$hUKO<ntTVflk`)eW6x?@6Qkzz%VZ@+-wy-Q<#Q7y=!#Gq zXgbDhG`$ohu$W`q5u&uAMyon$eJizUug+#yR-4>=uF^^Fj>nnYpPS!#JxN?viQHh= zQHf{}t_izfp2USDz&3`l?iu6NBjA|il-a5LNO^-$PoiMPhU+0Urk)T#QSzGmG4+pz z%C<Q}SM|iP<EXb#hf<c51+6u~OF~LaJ^bvc^!(Wywzb8#JsC#br4#+kfdVCUzCxTP z)TP>Wv0}G#yl~>zhM@Fbp`chwhwsWCjYIk~(3|jk#Zgnq<vkc+IXzJ9Mur2<q;x1u z;NImU0rAx;XRlLH3+#q{<8pQNJ9(7&aV9*e^>*=5nC*tuS!%w<lJw7`amNt+|L+HA zNY@RmqJ3m_*4eKoqT2@R{^fP$nql=NbtziJ{pr`fvElWP&x52QggLr7o52`#?`&`k zC*)NaV5IGH4)e2d6JdO$qvT3GY^kMxx7?MGA!a_1h9!WnVKMw{H{x;wRVw8PWq>1O zhsv$u!H^H;D@U1=BPU%?G%`_XikEx4_(6kHc{C63>vd?bqnDyV7CC=?RIB#M4+xTs z<ijO-uZ-2MAPr^Ad!a*^^O2*5&CRUpjau=tO;rj*O8_pIsX?-Hmx`lb|2)^V<En(p zrDpTpJCe`my&UPz$317XD2brWQ3rX+_XvFla-T0|Gz#QuMOTG6dv>xM*r#Pv8WxL- z$K`haG;D22<KQ}QL?Q8fEXCjFU*AzN-&Ptb@jo7{tX)c6otg-EH|>fbp%XybdMn&~ zX*rzrkX~8oaTqACFW^qx5gvVkSpE}!OMmj%7%!SIN0jF8Sw$F>Y7P6I0E6lc=2U~i z4^I47vG)b=CjPGH?GWpah=fA#Ijr-q+xn1W>2KKCf<6%SoSQfu%x4{kOENhJ4o_ph zq8d>CUO$ibJl@{=^y9R$&2%ANo@C*rw?XLfISIitf|JOUDi1Zx$gzOw-l9yUKs)Cf zX__HKk)PmzQsJ|;+O$~#<lAR^`+TH#Ed(-wM3m|g4w<$Px;lH>?dh(Yt+|Ecp;NGV zH|&96zz6K+TR)+nuP4{z9+rLm@1#i{hAmH?3lvvw!q7&y5481j9TyThJ`zFZu4Ham zi<rLN-Eiqf#W2vX?QzA&bRNziA_2b;^)kmWV3!vA<a?YyacM@V1+KNp#4}g2uaeMK z-RaMbV~%i7wB;7M4vW&rP|TdtoMI!PW7l`6>D*z%{;ki)6^e_~aK5Bj4_p%&Yfpw1 zm;8?<Bwll>)Z_pO^9u%Bi%abTspGYCbwBY%58GwT8g;xTYZKE9T2OD#GQ%%gwE~}> z!6(EW!Bw+{`9a*WwWu`in@Iv=nk<f&fme@P*GAzwmz61(Jc2UxI>pbE-1Ksinf(9z z0kTCx2;a-`^R{xK>9BS)`lV0v)OC0Ok3xxfyT>JDOP1`yj#08oCBi7SZ<kXtxaqrL z3>*D!N*;C~D^a&jUuqz;_B^9^f#!zoSDdi4!Y;nE#KED96kj}Ebc2$)`n4asc;#t? zDrm=qfqF%H9D|Qj`W{PVtKQGH-iwIYq3|L(QbAC9R8j9AB^XM8|3Jy)@mseDMR|Ha zu>-Yhh3)Fp9G~yzs+z~s;di!|>%Hc;&#x1O0ONc#@bQp8$T}r4q(y8Arvp>Q`uf8} zBbv~0QpUbXaXc$Zy|Qw4B6IWNi=~a4p%{-uiLL+lu-FPL8(>!<YC#77eAW~HPrZ8~ zluu2fC%H_e11w8iL{472!2CStm7(nCJL^hb2#JdJpZ%QzMBOYV=#uR)vII1KY*{23 zDETeo^*&6mF@l{s1)umNx=JDZow|L>K^YgFERTF!nncOhm>2ecX9;c&^wP(Sn?;@s z`}-v`&)rj&FWl|+6nF9NnKw`Cj*EiRk7U^}5IBbCnQ<)Ny(AEUAj_UCC7*vW`6!MI zbzPI1`xfUbpC>ZQ8~)9uGCaX1SQf6Gn9gEzku59W0Qh0TW)d5=%F5*uO?}%v&4WgT z{y75ce~w_R&3bS!lAdwTlo-b*MvZOKv3s;&VkkF36@DrHi&-N>M^P>!-Y+3Z`f!w= ztmn7K-Zn1>qoj{ri>s|B`xsLz2ik)A7GUdBhCJMaf1ab51!~u{*XvA35TA8;#%!C+ zk#+>u6(c`}w|@iy5eeCo+agoXZ3xO_7K^zaYS7bsy$V%8#X$lOx>x3Rz~`y<L#TKd z%(NW%h89Edv|G)@M&X8cAY5>?QsyF5w?f5TNJEqnHJ<gBIE^v`@k@Cb*q7`24Y?82 zkUxBO32jE4MtaikgE^POY*kdn$PeX09uKsCc)2h8XD&$rU5)!M@bARgaz16k$mTbE zRBVrnx)w%`k~S48Dt-38+`^)Tpz26|$5E6(cGE#gcP$Ca{Jfn6?wAV6e*)!gDJvC3 z^h{l0pSZQT4yqIs`?RbSs!B7P_~oL?<m*{wv&a_h7s%rIOgc-`*W*Dv79p!e{082& z?zj5*SA6BsYOcjrVR>A30->4_#e{E+Bh)`z1Rfye#pxWFOWPxMhgQv_#A-m>^WBuP zLIuX(sZMEpU7Z&uDcuN=QZ%x3S-EgrgtFIp4@Yd(55;BxdI$5qGw8?2U=Fyrpc&L1 zzkdAP$l~~+@c1jdv_s&*XJr&R1j0y5G?3frc1zZ*cq+Y9-g#L}_JshRjqY>*<&N>Y z6AEFM!*!cz^VGPas3!#BhE2}fWDTfEF$J7&&#|iaW6+8|J4JwS=FQ$VgqDdZm3jGX zdvn_46`$6v^gG2o&8W?p8UDV6Y+98p1kU$e^@)CAY8r<1*U(>d?F&x0IQOvUxTd75 zN}c*q%52G(s;tpk{saYFTZNkAlO$N7<F^*ahl0APR&zez&fzq`NL^QuIgVXzDMsds z(a%iwm{#wo)Tv|!Hye%ZFZ_CoG=0xa#yE3Wn(r~<CQKD}ShopuQ0~7m896#}7Aej} zVf^64H4wxiIwRzVl5Hhj@wPb=WNA^MCX$0(5`PEwVW(a=jrbeV<~8F#DEz_D#=-Q~ zqW@DWxF^}v{GMIHp{KjX3x#B|q7JS+J&Xob;9BFht9ua)l<4&ycDn99!cv+W^Dm0Q zV!{RhQ&4R>Eb#2Mh?e1g?Z~t8@GC!B?8xVpLAxeF0`WS)V_N}JZ&%bE0u=M@KAnd; zKP++Nc=cxJzL7ogw}R3u?&3&09+pTQ>;tE~Q93efNjD?J*Kga=Y#tbv5(x>tf$g<; zs$OqmhoR|>*S0NTW1>^W%(^Fo)8ye{t*W~;SLeV4h)ga?LB##~D>>U$KNyY&8<njk z3&Ilpe!*&`T_#TD%u8n~B`}H+)W%f`UwucMVJzH9u$i@>9sPUp;q<UefSB%#)2hg$ zPLn6fIlyLkRu2Du*xPa;PqGvAur>h?QlaF#D<xx87dcM;0eIy2k1@OgF*PP}F{lwG zF)_J_n?oq2WJ9ouJIz|jMF1OfEMl>SxM+M%(ciF+CWetLV8#yq2x215c&BGqGe&;z zXsv$p=?T%vl2_TzB~xPn0@|ezOlAYo7*4sxq7zcVmDF&v@4e(82OPu$?KE)%Iu3zg zH6!Hc<x}nQPdlLgr#fa7H=^s*2pYv7WHJpRqdON_H!<X9+odoG2?kA8mO2W|YV6eU z;@lA1$A~#6+v^Y^qxdKF)X;d<`a~Y{OZ>Z1e_RGC^>yw~<?IKpq(OBS&2EUgfe@X* zOO?%HEt}uHqMXTTqNww#%H>-QnQoe1QG&d|!nSejj0~nHA55%-tSd7#jMAtB2%|)U z$#+Z(GA6n)B(i(!1TQ70k#ZFRF4qcYA(avNVa;LonEclw*mP3|@BGx0G*pU}4jd&! zsZpcy;(U~}O0!ShSvwcszQ_M=?c#%Pdn|@3%tc%#P6PqUj!C$iiI~)4)8D{MgD;!- z2s7Ey8BQ;TQ!%_NZg*h2W3jsxLbE!rQE=wVV5Mg1@O$!G>7x+Dau~TkLz!o=l)L`% zT6pLeJ-;7g%F-32VwWS0z^vb?*|mPrVxLM=W$f7&m^3+Sr1Xz_ID{Rh?Nd~y3JG8K ztG8q|Fc;Nh6u<=B)1jyhZAmmnWy^EhPW#GL|9^Z;lAv|yG85O{ipfLJYBQWhfedLU zzy>1Xvrqf%b-HP&u3H-J=(}1ho{A0^zDL=mJFU)JTz6BtWuo^q9g;Qywndd1N;pe@ zEK$mxKIG?V%@gomrw|i9kLgZ8E@EONfsk6svUy%5lqIu}{9mu`R7os0az}3yn~S_T zh5rfKglMtX*>)XwcrC%`LMD7L>lEJr!CQhWa<z)M;PAzX4<BIJUDXivLsyVer+MlV zi)o;S7F0yA=FxlI-tjW|ei|tmuduKYvhLtx0wp(zhL`a_q6+TfD!+cd^QnBB*rUkz z{qU~Y<n2H|cgX+7VQJ8fDS=de#BGafGjNF;{sg8ZX++2iuWg+pbp=<`(ivIRk>iDU z?1g`*2r-~8f!0^1um}uYW)Lr^Rz42mm9^FQ6irfvo#Bs$J<Lcgre$I;QSd--wUG;t zm?4S=(Rts-r^f>ik+rpw_9@185#y+L8mF*k$bdf=6PutL0Y7sX_37*54#Ba(=t=&* zBB+jZNoc<zs3yl{YiHXeXOdy=L+j2WC4oP?Xfgfc=Z}P^VI+E}QL4XPcNc??DJ(d` z1lKsV+mS&jqVvf*3>O+z+W^d;5{@RvE8%m^XAOKGAIEVM>Mddkqtn+f9ger2(IU0? zsci{YMZY)vtTP|5FV}H|v0iv&tFTUJpo;oG>!cEXe&(9)IyLmZW@joR<4frsKjN!$ z+GO@<5DC{N(wNEPJf&7}Eo^tc72)|dQ+V0Yv9pEfA+11R;?g-JLoZDesYWFm8J7Do z&nPf2NoQnwx15%&i`$3QK1|L?XIspS?88~97IpmHDU}3g(0BU&JQc8cnG&FKNm&|x zClp@MYZoK(p5(1C1$PLM{~r520n9pV=0NGCKd0Zm4m&sir=IdbL(KzL>^%)#(e~Xy zwFKz1q)&h7m*iYFXxk_}j!dQdSTQpBn~NqBwj$f{8cEpmx(Cs|ZY)_c!~z^d8;<Xm z26$LN#wWb>V=A!<4D$(A2|pIH*KsItEW^at!~gj6_WgXF@o8TD=SrQ2UTd^(OLrss z8T_}K8G98A8<-VQU17CmD$CFBp1~z^vViq<rKO{yii)<>8MXvcNaJmRjG|JKM8X}; zm7JG&AUJ{p0gX$ODCLYb-c*UM9rbMWDR?-oLWrD>+HOP5Y+B4VnQpChI%cTqMSGt5 z*m!uF`K(ElB@F690t^y=$NbJQJ8zaOHd5&gA<Vbamjs<j=6z+QhvKt)>qCF!&&n5y z@6Vx#OE1BHO<&^I^xgv(6tRVsGPRSh$M<%p?gSXxH6k0prv05tgbIFfX{zoF_ETe} zz5n+HGw)1u1QZQ7Tsj2)zTLVn0{fmWyW;0Ix&iFetU|vJU>F4p`HrcbGjEpfwHo=B zhZ&kZ2>meg^ZKO`bLp|5S&S*7Ffk>1u|NprnBvCB5$Ol$hB+BG3vKY+R;!TxI|^^x z1-;kZ^fF<g)7WOq1{)XC(~7o}@8C_bx5g`L`~Itl4e_>v1beg3j)6>K%54lEGdYCr zRgKMK<m#`gw0|^Xm#gg>R-{1OxG#L59ow%1)TJiwmYmmCSqV;E93H>EoAW*kngLep z0Yj~y);tfL*Vq~5g87)fsoh&?Z<^qSrRHc*mcd(teiCJdvrSy{GX=*)ac&1>+{7*E zKhMpf-CnqTpl)2O^_)slApDI}`V9aQ$$BzosglxQ;L@6nbiUjDh(g5}e_aM$D4=PX z#KrwA#W(DUB~we7AXMNun^Yv;)J#E8M+fEQDV^7dP7u--t<>*!#xFT-R@WGtj=5gS z;5~O0IA=K@N;Fpdv0qgc;5l6m9+`b^iG?3<P(TG)wjPJh0&xWQ-;SfKDy6#NTK5}~ z#_NUW!-gXgciTg=U6F@fP3G5yXlDNX*1*$;ixv4fp-64SIzy1a2w2{qp7eMKXq#Ai z1)ZQhnImx@7iR+K;`T~Gu5Z^h^@}`v=rs2nb?`8ae;k4fV6<wWiN$f;T{9bfemY%B ztuv?C{={6`p<2bJ`VpVJqDSw<`}^y?a6Uwbi+*s(V}Y?C>D$j5m#wNsWTu)B4?gNb z`E~*w&i9zg>+x7m%2-+4wn+1umI~W2wA_K>p^X*NiH}n@;7bxV2Y$$+Hcth=(@ut_ zhxv)(7|CmWc+yieL-Kh0*JGaKZ%y7k%*(f1hzZro6yH&D+u@xMp}sH-l!t9YTmR4` z=joXxpck9*jmh@sv;`m<z5*hH^zeT$jK~&0TI&`ng+~i$X)O!{WBOcZM+fH047oHV zS7l9M^yxBUCBB5|R4Kv^s=04Vmu-pZBHRyWj}>jVJj~i|lQKIUNK=<Rm#dKNmBvbU z!oyHx8lK>ZNWOQ)>o0ZtFK>~%g-h{bHN-6qkYxOAG3z)8<Q8L<aA*oA`TAflOP|3j zjynG|XtQ%dh^TNl9H_v|{oz@g<HwaIDn(@N(NA!nB$!4WF|&}rp_z^(S<lxZE&j@J zAJRm^N00|H92KvX`c_@fGgu-e43Vil2w`V4y}yU@;T~JfF*bzyOBswujn0!T!dlvB zoKs=hO%ap5j%1&Qq#dvCc*acxk!G7oKXEG$7Q@f8s1;HH<CAZmH#e`BmABqs_p#(j z7hWckR+gMjSKY~UtFA@Ttpsr;Z3?K0e2P=wk3utAgXR0u4AtWtXe2TNmNv`&jedFk zKQ^@=uvi;Y2aI$+V63#eziuwwFuGd}pfGTcHQjfuzHZdajgkz6u_%lf7e=-|9G$;j zCvH0ns>MzbGKMhomG_drZ0#z)#oklu{ir^K$V3aqXUtQz_l1Vy2w=w;^H0|qZ)1<5 z-=QCp-J5<@`mRvMf}o+TBbzH;GKaDYy|G41kBAUh@PL&zb-+qy{T9F1htK*y%02FK z(Yjb~!$I@oEE%0-l&j+)zZ|@74kH_(<Afu3R*zk1=#n71$q!LrAM(*(JL+*h(jX*t zy0r}_T#!GXK&X%H4rh^&-ObbFPdnGPHqNu7-PbXp$7~n3#tx7GUZuaYd|^=}3FKk^ z+-&@DA)4u-jfMH~=b@&4yUX)NaHI8NbF_{i>{4}rpz9Z~UH3T+qEq)X<=V81W#+q$ zwI_i)p`!&G4Olty+?BS|W`ViWCZ~n?BF{&tAJ7khz`&9Hr<zn_mrNw;9kwYMMP9Rv zIk4*?3=Q-irp&0}5X~dWvfnc}nS|T=$AOR$W;mnfh_N26k>rry=MZ^9ID&k8kirc@ zF^0PT=k8lFdeV&0EX`io>M|u?&~v3hSRb)2ltc+U=j*i}Tp$)YQh4wT-?7Pw`^2f6 z&U<53S&n;x-i>z*cGhQ3G19Dh^3;)4c}bG1VPTbI@ySy<DN-XQ%mMZA`3yW`#5;8e z-w8)KwYJYSdUP-+1V18{s&pD&Ad@={%0@AB102hq+@S^)YYS^k%9Onj6J)WcD00K! zk^0VUnE<lt(3N+q>p4!-PxA8TIbBn@K0Z2nUE4}@eF!t^M#lDWF=+4mq->Lk-t$N} zp`3j8oepN9rr=ID)>tZ`guhy8LGY;2Xu%OIM1_b?bA*pxgrJTOPW*KmYa&CKtg6*) zxZ=5t=Y;Uf;}wuMZ+O0Zxo5y*_FNd@lUe840q1KSeg*U|X3-swazBbz(st{Rgh#+D z(a(aq@MF0dKxD1WtB%<USxdCs`O!|gO!8O{e#g+u1u7E>qzl2VI-6C0*k74ZoeX79 zr*heL_oV4XQ7;I>BZosW_w^)ME|{|F$Y@&kG`YHC(rn6_Ak@SHSB$Pl(-YWm5{+^! zUBmhWhy7yu0&<T65#RBFpWhQRd_1OYd<vGVP*_q$F{)^RomNY6Mlj8Lp}Pr{Qw>Al zZ(5Ec`;e#%Zm0Q@Ch~wO^E+Z8&m$SLG0&CY-PZ#t`Iij_2H_`vM-kn6Sa}Rvu~2_3 zD6FC#75lQyhSXV?CC1Q604d=7Z@$F`p9lHdcP?S&&G3Zlj`NzMTMx?^ye>JMGaOF3 zNkxqRC^!8W0*APSYr7u&Vw;kV7AdRDm_1hfc~vz<ycXLj=^#FW$fo)>wwD$~Dd+MI zi#)NHe27+Lf-P+umkvD&d00wI-p>S@N?4#hgW4~9$CjQz5ZiSQ`?t^>wZ1+>-ddMp zB1U$(EW<{#Mi~7@?x`-EzOKB;b}3HyoRz1CW%e9cesk0mDfo)D$Wx560tJMMpH^)1 z_dgjv{_~%nw(2~j8OOmr3eiv>5TU(Zf<$vK(+qtxI2i+eb!t%_1mdvUeUFgaxuDLM z;ydP(Wz35>%QOzcMJzQ(MkZ6cHA1x7GG|ang4vobW{b3CzBlWI8wZ<V^D#~8EFbp5 zk_)3DJp3gbZr5R7?=Ocz9rt34HF{MxOD#;}HmjbUxC?;g3f@|gKe`izJ~Kd`ccT39 z5b3jDo||S2znR-bQU3&4qQ$;ptrmn%r(~>0mu>gTY6}W>nx|IZ$SA%d(;ZFl;fjQj z`|crAaeZzh+hgBG5ZVc#)lzKEH@$U0HlVWDAsQXRG{z08P*U}$-_WrbYU^`3BvcHs zqcHMVefXxC3Kn$}HRKqmP}*vkQ(LMebr`V~O5-M&OlSz>GqN}z>2U%-d8JI<q<u?z z^4X*MtoJ8|s=X?#v}`W6>{zyi;!XFryTj7r3yhxY{mYoCmU{S0wDh2#1}9uU5SndX zf&`9q$pYq{V&mIHW1i%2tn&JJaDq@Oo~bpXBt}NU+Gmx@;ay9MqSUcNgDsLBd?KT| z@&>21r%PdDMgk7Rr5WPpBO~`yw6Ez>i9c-KUWA?Y11Z=AEM#4FSZBjhSxr$y;n34K z5gnYH|9fR>dH2^o#lRT8dtWMK)%MTR>iyGu7muSMs_4}YLz=AjJ&rU_ALh64a(;aa zkr4bX@QmvXoV*Lx_FDsar7H{cRk;do@67*_L_PzV^luVzu))ZfJesfGeRJY$U>S?{ z=dRPc?WuxCuL_-Eo~<t`Er>MEMjAp*vZFECQh7(r;R(b3Tp^@oKVSEoqA-$p|IE_> zk(yJ5P9!ofLh3gIDS4~Wnps!^GR6ocm9QpEBXWQ*E!(GJPHt*_HZQkgbYpEX5($&b z_2js<U4?VXb5${Ri7}LVhN{qD5=vX>_bvFILjUT>^#uUTYeZh&OOz6}yrZ=Wa}Ms? z=eZ_XrKNa>cn9^=4{A6K+jPHxKeYJM?lObBml(AEV2nx3NfvXnUXik&>LZ;+OfM-C znkts{n@)&*HgyU1!+}K}Iy!NE$~#JGEv}%SS}YJWNzC&+A450^PNs56CYGRVd|tGc zY@#Wd2O#nj>=!Ydg;7Y8XW<_FDJuCPe=2odmfS1bb~jw3mmN;~x1&WadvDt2LA42j zz<ifo=lAEM1@wq5@wOG8H|FOms$%9ufHq1w0Ce@AwFKe+(h`7B{sdQPs{uoSHg-u* zEZ4N<tzvKe)jzj4**=SSY`!$=wO-ZN4jc!CV6>XE-t(2C@yf=;e*9`kACO<FWEvYL z>4P~a-pMhOwC=Bjh2w#RPA>YjDp0B<GnMv>BAJ0@jDgxRLM}c6f!c|oy#>NWPey}l zk~Sa})UK(h`ylUOh%=*Jh4lqEQ5r`CB5_TrL`~rJ0-d|}1|5RGVqkw`IxkddVk;^T z*4jt;X)7|k+5`RmMB&`Bo!(!mTQNuQ%Zb^iH8-7(31-Lf=Arc5RxEzED^A9m%*!-L zzxPRg_{*-QhKJGB`fvD#4L>HEA|n%{i6my(dpf1|*1>9PGw=5Yjt(^Im>(q=kN%w~ z;@porK>rEP<<$HK&jqh8>QTuz@M74)kb0+(X;nZT^96)?UaN0khljpAY{kuG`S-y= zqEm%;G^!wxzS3~7cQklYLvD-Po^24rrY!FM-56K)p$|#3FiR5{3Ieq;_#1qi9>~8W zlYS9zcy#|(m0$(pZ$<A%(ff9pA1NL{dZ2C6E5_5<SX{IIziBJ{Ep2K}eZ6^3fM?UK z#qL}VqLUPKMitA7XfDj{mv2|E;-f%Aw?3UB-84yZ2wsRTs2-U2@x{Io>f|u;5&|~$ z?0lzqf);7)^6Q{JYBwTUGA@d4+kXo5cIaEtGI5_dR$MEwUIuwSEIhy2g_n*j?29AH zY$r6ODbMPThgq7k@rG92WGYvPEEii`H@H+<8a3al8$Ys>H~Z%9^62Pno#Hv%@@lt@ zbToBcLjZ{~m_5iIx>6k({ee%syzI=uT+tXY9-_Wmw-~YQiI$dyk<SN=Mm7SQ*Q~`; zdTr#wl2gR7{+i$3{-6|UMXe>5;RpUbKE0jng>D=x%dXt3sVu8dCTMOKSGQAEA6B{z z6^@j2#<Yo(s5F|x>&sV;`gW3E1`c?U;Ij!)<Fm!AxJ*J>O3546*VLJ>rM!Irsle`L z=DVKjy8TQ>XO7|3YNKU&8;-%DvakYJ#=ckvl&ywjdu;CYsp3@7>{Pqy4KMdj+N)@z zGqZvBqKA;R?p3<muvCK=B9exHLF8(*r}+ud_?NT8WJbR0{+ZQwXNm^On(8py(mAU` z>GnbY;sh|R0l2`3qn3bs?)Wda_unJ7QlnL*`Zb9z@$aABuInQ?y%sKrXgxkAGe4%j z!)?D9X?EGe6mT#sfD&O&9Np#BeEH?J2-Izwi`lb;k}IO1;Xu&~lInH0Mf`8_mT2=w zi1D%|u=Dd=^jQ4E@<H1$G0Bxk=^4--yeJu>n&m=!mqJGT$R8o-_8G{l%`uJR;bF!A zL9P=#f%|$Lj9V{(AarN1lYYk~DHYU@u(mB>zt<D;4#OnOCn!YJEGTXwHi>%HlTsOr zv0N3cfX+Z^d_x}qaJA@^gCjIPG+N>^P}P^WQi4JnvYa;TbsSYn<Fm~4IE6=#!+fT$ z2D9}ad=E9eEXF8!RvY(E2C^4gA*9@Y>dMr~?vkUuiI&JNRBY2=4-6P`6Bdxm;=T(2 zWcxD77f%iT+XgC{TUCG~-7Nl}aipUj38nJ<i`5QJ!xf%oPm>GdEH22=>g2h7V{pvr znLC=L!AqpAh?qs6`ETV9!89L=BAqZ|Ri?lZMqqVXRi=qgzRJ@ji2+@>)X**t>ehfp zLDoFfG^7BKtzfK{CKV6x`w>{IJL+lOWQ0v?(vx5s$hj5YrT1Mso=40BjD4Zi3Na&P zK%OlP&Trw~!GCk}^=ZCJ&-cH;cuh2Bn=c@Mv@D!$vdsU&;M_Q-t`Pp4tT!NpxShVH zH@=tH|LxnrA(=FuMEcf^XR>C7gibfdgS0Yw^yQm_EqIBm_=8kUs^ZqPE~AP)zAjp@ z*<yLoHBl|8u;&$5?e2K1o#U4|@a`JbyKS4%&x~puE*q2JTR|?ke%d71jrS-`B@bqr z6?e;i11GNbG?O~Wn#g)TUHV353w<IjD1LlIWPr`}m}DHi7vdYOYIggli`A6;e?NIg zkGBCnDBpDBU`O-x+_Qh4mSoN`yB|CQZNe-dwt2`nPi`rfS{`ogw*1u18T}z7G@e}% zQYqYpy-Cw9V|9OgnAQJPYrY_Nxu^874xnWFAYvbtjBUpxGMvOnGW<Q%f<^b5@H23} z<QEX;g=!K0^Nb%Qlxg01q6P7lk5;j6ho44b7fA@y5hm(024c-H0<gfNrSRL_2iBrb zs(Xf@Lw)Y6)~&h|bs3ja{JED*?uQ|c-L`w+3Aq|}9)BA1e{J15ceTvcZ~V%`Jby7` z%)7}^zAae=5LnwsRVo&VCR#d14fl)4=@kEcb<5`g8ZtS)@2R63A!;{+@7=2%ZUA*l z+OxmoY5POgm#67C%}lP-y{XuojSDLUbG;hE+J%^P)(}JpV8u9`(m;|mFzL^zdXm@W zkRGBOxbl;rRWa>#<`q``G|j}*pu+d46;?)%7;?q_b>kcE?c>*!DUDClJA7J`IRLkh z+5O?kF)vLDn3?O%WJ;z+x>E-rbpxOAys`%l>jcy9%4EGXVZ29i@$#eiJh9UkUjE=E zf2eKSeqm}d2p?@{b%=el);g{TX0{E`9782~qQXJqq1DJEs3n-~joAB};Xc*<RWESU z{KRZl4uoAnF78H&qnB<0H8o@F@FmzE7`R^?9?`pij46K(fHzsV(224a8n0rH@gfAT zWHgtrgD!DNJX`cq`re~BtM+_KiWxCK2~d!&$xKPSPvvR_5fPP6+py69?J-Kv5*&93 zkmZ7c+}N-e4DhRJ3O>%00+-7(WeOFt=+9K-m~7!XI{RM6*6`r_CGOMg?#W%J<E^*- z5r&{}!v<pGuUY#yKMlgGBb8TJSFFxdYdHN0vE}0mDo3lR3_bq)8C$a}a;IKfV1ook zAE{r@Ohn-IxA()G`s;SM^UKKnyDwh-x~5fL`nHX`hM?h}H^QI3s+5+1?PXOdjpI(c zg~7z?Y`#OC^=fl)O~a+V=OVPwIVr!Mm5Y^zNGW>i;e}|lv!NX!wU}enbRlxScpSB= z^4y#W-1e)-R%D!<BVc33xR7adZ%#^TwsNUE`oIanLOPcmE^$+509f#+Bz8+==&u?~ zYsruI_)|I6a?mSnpmWF~+Z@jul02GmIkn5wyJ`HT?HN&!-CvJCb@albTyN7N#fJNi zp-9o+sON{IGKW>g(qGx&qD6k5Fhi#MQd~TdI-nZl{hz1?0l7@%Z%YY0owZF880PVf z0)yAvpqUP*-*Lu@q&*;KT?>+=u@=i(Of237zOP5TtsqOoa&r6!1XYX1Gp^`(@*hgR zNl`w1NT$HT4oh}hSmXkdDqH<Cx(tAe6eO+?+T-F|8u`?d5@T*)4o{R@?@j_<(P{X` zgAH>$l5z9>R`2CTd-eHoI61O%8KF>!aH0{1p4|LrI2d91_2o3(>4-qD8Eks$ylGE9 zN9y<xyf<pw#dng8j3V%+*Gw6b_!K*7TZoRnjh`txBeYSfSR^L}XfbmMFdE1v!eTVA z2&@q4r*+OojS5Uyhj1Y~%L}zHLPkdelQ9Mwp=?+;MdkCu@=t^5dY%TB6K0B!j{YBV z9r3J+qT4qlAl~<?;jd3aKh2h(){>J2+s|^X=PR(nt*l$SNWJ8|4(76;>6f#zDCcib z9pW+wzk$D6SUHxqBZ{z)q$#%m`b&QI-G}#IeqIhjY_uP<%1&LEs7?wDWSV8Dp9OlG zWj`X|VPZK9&6|v;?vqfAp7z%#LHW{ktiIYW|9owBE#uDg9!`sAsaP78FKl*IK@zZK z#8wYvpJDGi=%hWEMq=9ZG&jzhmRv9EEcNlDsdIpNLY5k;dypnq9@k;C`uc{a)kCjN zO+x4S!4r+Ob0U!!^<qFwJ^%o8;Xg+$&SZ;8{}-l7KtTmGd>(Clo6$heYfd|%tsuOq zkAd!CZZAeHzD@7i^ijd2YgEpkDTsm#BT}VPTa$oAn|U%t+A)ZI94&|)YX=d*tRUH^ z2Sckgod$M&qAFKI0u)|RXQK|CU&dC%MH3m5L`N(NcI<PFoz<(lKCKLn2ei-+EojlN z$pC=ZJKu9TYvXA#ps!n7FRx$lu7UqfcIrN3HR$bshcc+ZeiId`KB4uk%Ka4r=mEVJ zHm*-wEcz@(1Yh!NcbdLHPT&iPE^X!yf6UR57Di;RW$f@C2yQ$b#7$Xn0lX+-$eyA) zHVVihR+Dr0{fB}N!Y-m3t&E*{T9(so-7s(8g(HCSbENc+Eqa3B%wP-B7BMCY<hrrL zEk1s=2ijG)#05FQs{&uFy%qowk2y^F3!t%TjQQ}D3&j2+g<u*g;DQGg2YqjDS_-4! z%RL_Hc|KaW-^_&uO&AoI#rpMT+~{~`-Uwj1WF~Er4saH&>{U*2pKb@<Q4GJa=O1xy zKCW2zbLcuZYL*KTi3osBl*Heb;rGRKtpEHWUu=dRpG|onCu{km{m5ETvVs9cQIV)9 zD3Y+2ECWq2(tgz+%WOQ`uBQ}`7V%gu`HKa{%<~s99dCcFXZ6Fm{qhbzFL(?4+TLj0 z&zg5QdhKDxu675&x%~cO3*L|wIg<vUdY*bq<h(v({0p5I)(Xbr?uW@u20ML@`4If4 zGtAm`OGFFXRYI$6DZRE)j73|x5;b|W^u9EC<v@NkNMFWOA*_Nlif{ia>Z?lCXU%W- zBToY_`=85J?H2UD-QVSHj-~j+Zr#BK51MD=V}9KT=={aIwkhp;424B+D&uu-4b!Wy zKaef-a>8(`-(tf+%_Z0s`bOq@;$G7;v%!XiWz6Q9CIys%2C506e<`*jlYApyjf)&n zLiyRpN7hn_sx~X#Dd-|*SUM>zK#TGZ9AryUgdyc9d6`rhxjv_{0I4(uyW~S;QnmNW z<e|~3zuE8Ry#>HueQq^~h9J@@K#ICZTwi9KX{le31E4~3rJ-gBpTygSfSk5gIYpwz zS00eS>e>~%P>#U@6Trxf!yyDmY>UMC7JEKSRCznJzq6Mihy~Fol&=NjD}5OLZ5{dd zzMi2l20)+*Nr6%=l>pg0>^KM=_X0~gG(SgVm7)1kM6U5#L1oWAXnObGQo*uW-Cs9^ zC%<g+XnF#Q)I`PO<%c6LM!C8}12RsxZL?$jsRG>!^8$9i<P?-I@=E@Xvjz+-2M5+s z6G0pw3v}G(8E19F=%@ejEpRg#uq*9WNfL(6(XMG%!E+0T2i!=kettYmUVd3b8^2Zb zwNrfSEGbUpeW20LoT~BR^Cbr~XHf+EGb&=z2SpI*^JX+0?L-%Y?0WZ)<RKO6b$Nv< zQ$%Q$gyJo+@gFjp<)Lb^4)2?iEI9o&b3alvXnkJ_b#r(-0!UZFk&v))003V88W2H$ zY9Q>6BSq&T7n;{o9q`v^ml0aRv2>5KDboB%Al`KVqWd@k?r@rPXYqa<3Ds2`zKb?? z%XGqOIQQOkeTUu_-O0rx0{`qV+x$hAW8*&WN$Ssf{i2FaD{Q)XUWFK!BwBq+phmbm zjwC9B@Ycn;-mV4nqyuls$D6UMp|G+<IjG>&wgthIa2E6d&FD@0MYO1<id1g54L(o3 zqBg@BHjW))KI;(z^;PK1#+2RS;6(&bI6Zccz~OQIb=J$;p1kRZZ`ohLwf958&!W|< z&P4evt*0}mqcESkF1TEbl_GiVw@QxQbt;-^m-0$GCZX93AcUf_#%F@}|EHa#;NL_# zx3cZTT~F0=gS*I|s9P3iVaGA1!$0>k?lBWaZ(jFCnuCp#(K@z#2F+3$__+?Km9G;* ztNSdQ+ay8n*LPFE`2*QpFDEQ4Us@UfuKTO%+JIqA*3F|OsNH<uuxQ=7HD9Lt^T;Qj z(R&QYSMQoUB|2qF8e)QnR*e|70Q&IUue3X`_I;8;YtV;seWXY{YTp^r^s4o9vwY!Q zXTq{9CseDyt6+l`=^fY}+!p8QG|RPGO%FFeuL$6=-cSSbuD-^$hvzx(wdJzPpZl{i zB%a6;dp@s_Sj`v4Z6)!co^93t(6zx^1J6<Qjk-<D^bO?-nXa}8LSg|^Dw4hJcHJi^ zNoaBQN>5QGsU$c8b8s&iQwDJ<bIKc<>h6zmQaEX<^Sa^00;OSWkvSsk45~^fMmiK{ zm&=JZ??imb4V3u{_?UzcalX@BX+%}n&HtkBROJAbc+q^;^}py<E{bfwe*!4v10lRo zPi%o1ks3#yljDuF^6=f4c(}!+sRG8?H-33Rlk73$g$c5Ns4#3IH0wWMrsmcE%4)S; zxkio1F4i~a*1!*XxwMYXzNmlv<F+1`UliG#4Ys5FnSC#Oseq|7VkwOniGw(xNU*tO z*SYG)w+C#jj(6adbHpT&h9gDS)%uwK<y@Uyx=SH2{7sn$xqB2Mey$SXPiRso`WE=? zL1ZJ&4{MG}vj~J&nCs*k1Dzl9y8!6lIx7yqrOcB6@m?A~N$CaFj4)NkOWLq#@mM|l zH(uCB66`~;9<4OvN&Pb_hMW%-wJk!RCtCl>DrcGD>RXTr@#gW4g`W|9K5<1Vi^kaS z3bFb+y{G#0qzRqZRdBbP8v1rwOWH>h{0-z~k>(fxDU}TRBi_DX4a<o$K$$?2Dbgfr zG9J=qdUH2Iq&ezsaM^$CK2gL?`QvAzPiKV?d=?PP8jqf6e&4>5Zp>h=3Tp_If3*Em z$$cen-Ao8I?Bo3u(fqB&-~;Xgx#8W`d=KEg*zWo?-tuD>o|X7mm2jz`#THqUL>}-` zRO-iRVmXp+m6<5EM24lQw$Qj_ivCsC&e6qHb`Q~kOHwAO*gUM6nGlPsCXwXZ?dicG z&4ZXC#P%*hcFzq$kXfte?(Tt?b)wsj%}D{Ezq_m5H7sN1ajTvr6Yk&MHAP0{R<|bi znK+A6u$YnCM;J2;sXFaK+#PWrPjuF7C(<knofuWWdb|+{F1W^1IBJK_n08c0@IZ8v zLO)Ybr!3C^m49!*L<jdT2M8Z*k0|qAmfH%T%;5xSEzlP)_iRn0D`*N)?95KtD{N@- z88M{tBhwlXqrYUn`~1gs(7N*F@bLAf)v}_)b?f)Qtp97q>)YHEHOBW4DB$9h3tw^m zmTjV0{6O8=PtdCJw`+dOiC(Ix0+P3#KLEh*I+WiM*$)ZkIk6LJ=qlaBja6g^&0#_L z2B1$Lb?FnSb5%ZttRNAv$hJe&t-v_b<8R_pmGt0e<U7~zLNrw<z3c)MYEH$nrZ$c; z(D+bk&JH0ukVyoMRc|KZPR(%|nZvJHWwzJ?BSQFNG`$i@?ne>Tnpu3e&K*@p!297} zE*v`x=wMiG9&6e#lrWyadRp(w!g1?!f0jD`b(#EW_}hWnsIJPTE*E$ERZO!NA`x-W zs6{w1OMoDBCjYi#yzRBudCKbU?2$|1O3!ZlI<o`uZaV4kcI9*8?nM7e5>6pvXZCgs z0~Ek0gM=k!(w&fNB6uK(G!-!Xdj)2oh_7e{$!QSW#{idM&X4GfBc;!FR!bjDcY6P! z+dt_}7h{<{=6hnXd_LGpO-miIIs_-d!Xm<tSsk;`B@Qs}OwQ#38zRG8`OlU3e6u<; zG=ibP=}Kg9BVfo`$+{NKV#;4NK6h1c(1;{g_+P;(;eco{z@i#68s!y$6K%y?<$A!F z?H<J@BZCx*7(eipIks~4j13Tq5)!JCI;BM*q$BF+d_VkxRoqSjFffTKrbsgQm}=bI z@A~x2vbgQHu9UZse-V4#cQ3sh4Q&^#Ty=5{XSD)YO}f)b&zcO=k!sqz=TEfe1gIh0 zWC`CWz-vr_bhB^(^41dn0cQ+Q=$hk<fpNy<BlC^EmlsQNCX4e1tAG$k+ckz_z?(=g zuUQL_H{(oyT*murziIn>ws<|q6}V9R88(eH4AG$<W~gIB+V{BqO_qJlzun_dXd<`+ zX&rXdDWeMk@EK-=064R`kMLlboOA%hRCTjWnHn1T&BmR`sb>yWemchZsRK<SXOyW^ zle+=LDG?=67;*r6PX>2C`5*_w7)3QFUEG~?J4rnHE>$@vwv>Vgij**1O+p5xxamEs zi5n6xv9J1>!4+)wRR2#euj2sSJMo6I(%PMM>eDaY&+QDjBEq{}<!Ikl+P=;SGuCYT z!3+yMWPQqz<Sx?LP--`fvrwinyp|EO*8w20WUIl!e~fhhwIwFd=#n*NG;D*4YSNB| z3aVd@7HTuw&tK}Oxk7BkKC%Yj<Cml6`S0NbR!*+7;=V!HgCTJtAJ`4~RoO`{`G8;k zn;O}lP*c8|4O{z*WFopT)4X3!gaeENLyBb88_QpL_yDduskHtd(L|zORG!|jby#Pe zBneJ8pTOEu!r7aSLy61*tY2a^xxYkdHzA9aZR?(zd+qSd!e|XyNo!T=)i-Y*Nt$jq zn;!d`s&>2)ihibTrRN3MuJ_TacTTUNn{@tAM)aA+JVe4=o>2L5qZurf=^;d2e-@{a zMiET@$|<%1F%@^rohkf^ps&JdhBPHiBWNA)_!mj{uu@@r^<`?v!p$cq)g%V34KMnK zu4XsnK_m+1tNK*&y(B0biD*`-+>RpnL~e4MMLb`+<3&6lA78(2(+rRhxZ5%$lTSd@ z!m=k@tDum*^bauy9F`Cyk}s%YS}HRG5GSPr+3%~zBD0>B%@JD(#QvJE7#oYjMhjv_ zauf2E967hG5qvvIy_gMsR$lg^SeSKRqhDJNZduxZDOU~n9p$#CYrpoj?AIE@ws&;+ zsoq+1r0pvSQpaIp{hAvH5w%1Qhpdi<H_c()T=1`FoZsp;x7rJpYjFPr{k+)<4}P4= zdv;Zci4qmiWDN(LFB=F#zox?Ak)cx8=&X)DeUlF`jt*v){sQq{Y&wMsqJl4Sve8V- zf3HLKwCG=cdi6+hRh`vPwjUbTNM8r_{eN=#|Ehw)sM;8`oMl(t$iO0??&ZJ4J1k)o zKF@R>9MZOojy2UiWlu6p^_r_%ucpAY8ut0t_E&MKkKw_tOw0H3!imsh=6{ShivU@J zh_}!>U%5*$6K*M;FeMHTQ^T9bzDC5d{P!4y+-mV)CWuZUx;d3q!WG9Z%R|rbNjIFF zi&#(1fYTbH!!n^XT=%eL#C<`D&VvrmrWMGyI9}OHUaQmr)20$Yh|0a3@*pCAU4Ci* z)#b+}9LQ#M$BPe#sSh8dxoDU;?5t_?GMypwS77ny;=FGllP#G5p<;r>m!{hTl&<hO zv)j<`BlZx@w?4{^)({z_0NM3ag<Kk*qIyK4l?eh1M}<NQ+-3Sg?oEl}K+8t5w|d{$ z8jYaIMk-Vjq7VE?+k@T!4#PF1;`hq)lX9U_Zi&S*KMjHazvLnml3HLe_6@bsK5J3^ zzP9XD1Y+q79bxfCxbF#YWl;XQCI5A0I7YzX2s`Bdk~nZ-lWd{(m=I2b*+7<fI>Qai z5M#hFSr%FvQ>N6sV!)=kl`0-RwwxO~Wf8mxtS70PhrXII8-M+)KpMG;IIU<tsVF*G zt$OE*V1sL{qGdlh0E?2Y%49~hO+;OS>xfxsM1(W6CBwB6Iu@DKnf_a?Ner+6g_q9j zA`KGD@r^3sQxdZhGb#1f8bp6vg<N{3pOMW2ppwu$>%AkX_SpqT)t^qi^gg4v9S5S5 z_?gYHqxa0pwAs@*B~j5hQZBhItUzNFUVUNcPekJ}y7^s`&aG(L6A-W)4Pl4lJky%z zavtA?MQNZFB<H{OWM~vwgKmM7QNcq|+mVvD+@xKp7gNJ?&~zLkLg)agfGjome+bb^ zrBKG1=S;XB_dw%p{vT|e1w$Q8)~yK+3GNagxCDZ`ySuvv4jLSSySo$IU4py2yKB(k z?r@uTX1+UDegREapQ^prUhApW$OnU5Nvl(81u|RyT%|k+@n`c_i+f4;5i%q7TuC2f zGBh-40S|>DOXY>;(v=VLn$jFyW*=74X6!gZZ^}@yrEV;cW+hJ0=VRtS68=_-x)$Gd zUq-B;gxD%xC5)NLak|H6u{s`@NWQzBSdFAjpwp@KTwg9!vstY(NpgF_M#CT|cVZo} zlhuX9F=9ndknjKM6TRV`hy|p}>?QZjXgU6osKax+<3~SqsWIf+pYKgWQ>v8Za)_TB z!SV&ypDw=v_P&qvR!__6avtX^NY8*C-u6oJ@mv)gkwZ7R&ba4gbDO@ND!h`_G>(c& zsRuHxd7mOJI7Kentn_6WLN(=N$>hXmUhr9gFa7G-SVfBZ)TAg`&OGiDIB{ChwI!K7 z`OgXjyysD4)Lh&#{m5=Zwht)00_elk31}fz0Ngb+=z_EL=*VY*;DCNppB^#z!vX=A z07cms8hb`FTys!G+_|v}S8=jtOFL-oEXhHQQs@BilEC|l8p*oJw~BpT%GNBTHsede zA$Gtz?Y!WUwE#u?B=)B8f20VK6qNky*NkO$mT);2#9wOD%;%J+5+MIGkI#qX>@)oj zRQT8*W+G01WwT(_<D1i*hSy0%<piPf%_VM(7J}9W^Em}{9w}okVyDsD%}-en#E8my ztd0{1Q=j_nv7Y-2ZM-(>?hMvQ)~CEp2P{(;%^_RhgNR35WXgo&W49DRuPR*%=J#Zt z73^QqD%NjJ9sc85uFYPupBUPbgqXaCh+v~yt@9k1bLM`Kdux7d;B_1Dx<AOtWVcwC zlFR&~3%kvS?Lr3+Dw@hJtqlo?(ay;Dl1ilvCMV`b_wGo<8;-;@4*vtoGeSQ*s6ZxE z8N%%V@f}4rZsD5J<#YT^i)f#)dZ%Bw3-$(D&=)rMD0P$8-#akE`TTNwd3z~JaO*V% zR*<zk5)D&|I70H@)iebVE7Dk{*N26bbHoU$AqGa&)xoyD^Vk#QC(LBXl{24`8HCEe zCc7kGyqlduHtW-Vfy2(Vse|Kj{1wXoM>v~--H-JWwcNJX1bw(+oorX;L8?{Jo*mO7 zL6;Z!jp<~&-lo>zB1EZs`f#aXzv14M<Lv1Ob&BN$=rUX;G35o!;a>g4ziiQqXvQA= zS=zUf?#bz^h1G1HYIy%giIJRhRo`Aw+pFceBUrp2ZO4$~wjX)1K0JPVs(bR>?am(< zOLe<;SpC?k)$t;3M6QA7yS*ycZ$4&L8I5wIXjsNXBa6)UN^!H+$ZBAeh#^C<MJMrW zPF;$o0Rs0`3ejec!4gN$FZZ>Mr>fNki&qL%ldw~(`RMCSt=Cs3KY@|(=bst<9~=BS zf)X6`pfeN;M5TXslj*~?_KH^jKRbx-Q6+FY8%m9qY{_Csy%N~z*hL1*ItgpD9SK1` zAj!PDqhMlIRf~e(=)(O2gj!R4tt197p5n|D8d3D}x9b(*o>F=}s)czPQ0q-4WPX(! zkHjmJQz@dYR*6JP&?;_>Mx*$v@cCtr`WJh9hpUoa(6Sa7&DUb@_Dp3TzSdHV698f+ zbt|`7Pd(4UPdwE58qq9t2+)4UiyU_wUL^s*7FzL~c`A4@tMEl`f8^)o(EL{V8@?2q zCRuF;T>za?ge5AK_i;MKh$4;Yj?dF@%Ij%b$JEmXK@t7<E<mIn|D-5aH_0Lc+%oz* z4sAf|tA<<RJvEb$1F(QhJpX$E>n@NPv7y<Q33cJ?53;Q?vEA<FI^6HxO+g|1qg<!q z9N4mA_vJg(-I}{D^A|W~>1X)0ym_Rt#6q47^(F;J#zVK4-87@?9=m{<-D=m+ZggX~ zptEel3(Pd!YvbSZfQ@Sch6`#S)oVQ?AJ%H92t|cZr{5;62$eT1VG%41l_?K=4#<k6 zn?Z^|WCyhioL$nM{OE{BLFhqW9+0PtE4#@FG%<3Fc)eRrS|`^4XKmBZ2g8OlAGfGv zyv_^9P}aa-a772+4O;@VWju{WW0<FI=RWq8?WYA=gs#^N=%3HMta}GD^YJCcUBxZq znIs(2Ax_4!8IU46{$KgVcm2Ww39%$iRZuc{oQeVtv0a?w3p8U^AA2r1E;02*m$aam zpCdHdg-_t$Ss@U-IHB(fu6|8bVh^}{sXUcDh&kD)idL|qrPpD({B*znIwoWUG$#S7 z&f7lz29hXGnp$qng9x^v*-Z{bnMX93OL>yO$Xvy79SW~r@G{2#HrJ<`0~ltG{?eAR z9{&XFGL1E(#(^lj2y?q|VkwLbuWoQ-(((5<U=RroH}!l*|6`{uQuYhdgv9mxfM-&+ z_WU(*+;Cw@V?*caeen5cKcL5J5gsCZfg2E-ogCqgmjd_2(4cpC78Y2|$Z)G9#NE;2 zK~S!ax|)~oM0IV2Vu>K<Y)-JO;x`Z)hAcH5BOG5?HNU)8o{9(u9PMYHVBFC76xIM7 zN&eOw`Hz)HzD()plqIAn8Qq$n?<{~JDXTCw;C5pUW<mdq3rA}{B|mU0vYe5(a-=wo z+)Q24>dP(1VRiLdYunde5rHJoi52ibXV{8`mbfZ~Z41N}B$QE#kU6m_;|W1^2`+tF z;&vod7n#<=DWSy8Cs4i<c{n!38Lm*K=p_{<)c4bpUvF-@5hN{fM_)j4X*?YpN45Dc zz>6=IEdp9%<<;7v?I=rkTlQNUqZ%4Ze(zQ-GlLdl{WvnqWmmwFS;^~$|IO`j<>>5f zpDc=`yl?#BlP7y3-J-8jXc&jszK*OK;Cvf0(6TG&M=rlMEzMVbw|y>$f;VBaAKT(z z1j&c^V=kO`RJ%hYj=!E_o%W98R^MDO6h;w*00f#9r$rZ4=#p_6Kt<c==YacsvNQ`D z8jsRvs4O)pPfnQlsbf!+eilG=v;t4=_%t(ruT)YdYhV>eU#wv=-wmNEeY^Nom^@2U zp~!!NGJ7)yEsNW(A}7rsm}O4M=ZyxRrHyuCP{;q&xzbw+>IPi)b)38f4^`w7!3e;P zhcX3-)KCo{Sw=y1b-;0B0m55GUWQ#5q8t=RnNfv;NnaakO>7jy`3OsdF9t>W7n(n2 zDmeaE)qNa#Nb_=e^hGSWk(sD}4k>>V{bJ2F4;1zLfJS@vW&uROU0A1y-4!Xm(yOA% z71q6(tje8)!8?$%BV63})gd~B0xM&~wZa)rg8efQhwEI*6`%@`oi#nKU)gvZJhrf{ zz8*NWB2#zhYo$OrhQ^Z_fbUf~qPg<_AH*WOMDE$jEL#f#ohWC@uU^|Yt=qTVO;MZa z4#4bpdSN`n2J7MB6#K5K>FJ&!g+h~4&T3u_*;ZVL7iXU_*1pKllF54#Mj$yfEiuE5 z4{y&%QCUetRXbO}Y@$|IfZy<YPV8=U6UbD2cFr1{&XYW%QU!X9SFr%5@<1#`ArsE} zvZ=gR2nm(l$Ejlm;Cgr%*8=Bwo3hdnec{~v)GcXwzLlAf8HqMUE|G}rQ~SsUuZ86N zjHlZT-UMMqp^0>yk@}}j_EE4tH&eSOs-_Ec69<rLzPAy)CG0N(Zfr7y6vOO8{|X*` zjO)SBtMwF@oOV!~prtcGA|HB94HzsM9ZEpBHzZCO|CJQVsx4&F+WBzdyLMH`%lIbO z?yxW*N=V?Le{{R{6tk}1uCJK@e7(6L@@uUl5BXKggyM)Ru|oE=0Z*8C|D)TtNrF3B zVY)l8vAXp9(ZAQZN_mKS-DSCJJ@gI0#s;RGPVpOdx91@B_U-Qxc!`|rmpE!187gFm zA{~MmH7jx}_}oF%{q>^`>0gzqZ@tl4m+a>$WsS@26rn&9@*n5JP)JlJesS`{!6J~k zf8-S|SYe^g?$Qt_E^rcN02Ah?CHD*se-!0cGby&2mi(IyWAG&0ce(6W7uTBcpA6W- zOgl2#TD#C=FSR-6GTU!oX<b>VyC1rgd?c(u*K|Muhb<J>&x4@)<rKspCeVnkCbh{? zCK#ZIbR>N@2zn^f18ru9T?F}DgZ~sQ6A%CM0M22>{<#sO&)UtMd(Y|k(B)>)m#_6$ z_UrGq(Qm)|G$p_OHo@!-#T%>4@8?MFn7$giJ>Rq;yqPpJu2%<n>zEEK@J8g<Fm+rJ zWk5a2WvXBQ);N6-(?z&i0IgMK3w<q|`@-NZI+~yKrJoUH&O!Nr_?&vCumA&yp$hUR zlFko}EMVuR;rW+ry71E#^i5@;;1Lzh8Srb8h`NZDZKSdO$7I-sJhL!#o?^w$Zdd&6 z@m(st!l`E4<UASO=A;$<hL+4qF6mw3OT38;|3nbJ)&at_Ti$Ai`9k@5mV-9&qam8# z1Cr%>qX*Js{0AAXBxX0l+E)85{f#@K2~gM-6qf#d;lf4PYRt^)b`RPF&Rd@x-RoO> z-QNN?HlD_za=9Kt>eGUdH}6iu2X4(8%9(>bMKlWt6`%_jB)l{t)5rU+Ta4<$6#U+W zt6M8z{F0JEHJpz+ka^CAO{1tkdwG5N^_~-3;qyy?{_`!J<L9hHgJ_>em3rUXIoy(L zEa-fI+#WS$^KpeK^A$?$_h)ie4i`iW(i8!aT;=e;bQ2*Q6F`_omfdvSt)lhm)=_Sy z9j3@}-4J#Sncw1g<qf}{ze&IGOknzcStD!da@aU(QN(YN1uods9QCN#(%0@?4}swr zsE>HA@JB#;7pe_5{5p8fQjcs1T(7HdHu?77Bm;|z1}X|DBr|wxg}Jcttq=dshJk=S zbPjC}YE32m61uw*NaLp_{7swZ362Lw&kWp1h$5qMe^?*BSU>5<3SVNI3I<n2gZp-W z`aEpvf1w~A;mdj_$w09+kU1R<x*VI#Iozy2A5!2`>F;FWhE51RbRX{qr30~Y?ZPp8 z%~#gy3>9!A;0QLY9Rc~-OdB@Ylogc1@=+fj=6OheMJ>=J$`a@I^^U}+6Th-yMBwmV z!Puf0PX#8PUtQ1Y?lSIr9D%Z7z=(_WWHo3Zd)uLsuvP+;h!gaz2qLS=>vmEd%5ZpZ z{0A~N9l@KXP+iiAAE?XHViQBDbWnR1fR&v*?U`O0<GNeBn;o~=Xu0U-h<ZM`p{d3Q zlrl5bkE7F#ML4Q>DG`xo#^IRrqm>m+|AjvqueKbah&ayftwS~7+RKo_X>U*<+*h>D zTWHc!%>&3N2#!he_-RSh6bOb9x!~>P)S5!Z>Lr^WFd1_QR)zykiKKny*X0n{cNa^$ z+dELh3@H_qwbrBUN&HH%L5#PCj>8s6(1RdH>>u>lFhE|67CBf$P<79EvdmW{Z%LS9 z>wA%3m9oVGn-$wj!~y(VLk_MYGP9#gRI--b(^^M|`_lbzguW)FQlD&XI@NR?%&*ng z+WqkXoULG_^`DI+IL4wuC%H2?<=}aaNr#<HH)668Q(*ocp_@di;8{r%*|p4*3JpJy z$nSZvph6HkDL<QC(w=4@;A4<jvYY2q${DBLhN@aXDKrQv6@?0lsC`BR^N@Y&($ZOw z#_Q@E^1S6~1m>`XQ3etvXDIWsn&iFf2dxj4p3tdt6yP5H3i#ydV5#b)wN(5TJ<yI9 z^xkgOS%c7Bn2%eySix+W9p_P%jZXlDX#H+Sw)N+yIH)V|+|Nn(E+SHeaHc+X@uJ1A zOYu|}D&N5mzi*Ha|0k3pL=`HTwB&r^VZE!wRjG5k<5t-Ov<sFOBc@{)Nyb*?H|2{n zyphB>9GtKmcEg5~u@|F?37`JZh88zdLu2@qKyP8syY4GpC~b8-;WJt7+}AuVXZ~v# z3r7&pGfyeDZOBsAGbitndNqbhO_`SCt4o$*p<7lltm!c`jJJ9oAunRY>APo0Iutfo zd>%B1VuQv8Mya9{Ei4@rQIByrTbc*I?R2ieo^2xd)=a29BP!Pv`ZRMY8g3`#!FXGE z;(YQU7CC}`R062lYyU||{}M7ne47M!q@Ay`{SQxMIPDhUUofh)L?y6TE9rPJDOLJ} zbD!`s9`((f9JiIouv*?<mlHvlHab%SB21i+(wD`9lhj{`2fgEpX()o=UxU>Ics)2| zx2UDBcJ-7|2#eHRx`PLdg#9W)QK<3j6yvdUW+ZW=sZr1vYb)lI&+Uqj5Z{RrpxjCh zQ(jN+Q7`?lavps?O=lV(*`%Y|_pH~RoYV6CH18fae%4zTT6w{oNUHK@@^m%5mFU3Z z<OLpd6LNn-y|JD$eaLTf9y1SNuSX`%ZyPq{uid^kBuJJzHHRA7Ree3e$t;i6w^I_v z(x{Fi9C{o)hxpuTRIY}4_Ctwjt3?KTA{+(qHMNRTeeu>vMIJlgYaK6KiW+6i$sM<c z-@duJ_m5e;9*@v?zWxdG+8%XPcS23XxfkE{O+cKbzum@8WI)r4`#Zq&V9@x8(`Ac& zPYfZ;+pWS7WDX{=+Jn2fjqr0o8jok|_+PU0i4QARfqV6>KE^yj8P7vbeo#8AQ!gmj z1wn5VR1U>&1~ruQV~2V$s?K<Jj3z^E-q>MY=_|+2(bhHJ@OVLTHBzx2aqrNIW`j;$ z5(-g{@>Jiv&s(&iIf4n)$;w@&v$i>gpHz39|K!Lq%w8nP#c@U=`c1nF$8^ZHB%Em< z7@Y_V#jUEQt3h1;i~R$^dF!iWvlPg4ln)51h3@&@K2;8}cACCDs&{Fu5k9!Vf>~9T z6q12en;rd2C-&66KG;9B^+PTr2Zj74AE$+RMU5Ea_<QfkH*sB2H;7?VBvAa^$&sH) z^^8Xm92HMey=#bRF{RNYBX!qL<g_wph^hBTLVMtzAJbGd$^D%t;O3GdVcR80*emUP z%03I41Q2%4)pj;3ev|GK)_Lfv<x;vg9hhXl--K!rbN_K|!nF51zMu33>T!K|Ozvbc ztTh<x<ybw{jFp7?!L<i>Ky#6uCxn%HPc-BJPM0DY@5H~X+mXCet$H<wdM|%R9fl#h z?<a)A27o^pD2e53y_U6{-UWyH`8-=?efz`Ge|O#PS40neGS9{oz9o8GS3xe#WIJ<V z*H+bN6gna`r?4Y6P<2FEwdXIW!QYsVgX$0fC)reUX@IgkPXlpzmMZnRh|Uq}woq|b zpLJCq-Jqrf51c%i4zXJS9Cl1;&<B3xWTU=5I*z{yzR`$`+e{^8eljYxEWXl~RzYa% z9>;-4wbe=$np+zrdlfT=JI-Uk1|D4x2W8m#PjwTik{!VT@2m)daEl&%5Bx4yq#<ql z`DVHHEc@MU<5X{RmIwS6_#}XJRb+V5<;rrVT{VQq`dGRY8)L6wQdETZ0)_L{eA_dG zSF$LyqRPDQn+V?<i}#L*A>9RSQn+JY&pfy&DLM9$S?W#bU&pym>?wR*_#Zy1C(X=v z*msdxEoBBsF5P7T&X3uK<-fVU&5RKU>g@PWY=6+S-R4u3PsqWiahEfy%cJMc{YYUl za|@$hW}80BzFwbrIi9o;zIHX+thQ}c+;COlqDA<#PDRygeAY852rQt8vpRAuv(Xi> zhpy{)bbS8QHNAAU+He?WPw?95Nb`2JpY(F=`Z0>jsAA>&{F}e6h5TBsN}BBw`_5o* z3<+sCS*R_)VvN#USb%N5d$s~Dj8+QlCQdv!CW$sV;5|4?$AkIdzTbVBe;JOwOlrd* z{cyb2h;}y)Wz~?kJx%U&#=&Xdwa~nOlDkQ*Q5&ld?Nb(|%+4Ty<e`Vv3|R(&;)$4` zoVkV-1&F}LB(%4cc%3r?vQ5nZA*|*=5+CMaX3HndDcboD;&wk#v$Q9)0J}Kjbk`bo z9*XortOQ9}5!qBj&Nj{9&TcLK8@tmWNa%<7RV{t4)D#Icvy#9$oWmz5x&x7F?)IgJ zLE~BZfYvRh)Qi&8XjqLxYZS)C(Bykb40!+0$>`Kc?>)!zd4YV%PJ=yn5E;W-+RP<g z-c1?GJL@CeUh_tSXIAtFqvZ!L;}~{)uJnjZl{j+WFT;xrXoywaYO?2(WE|?rC>3i4 zaAUPogy}4IGFV^VfsW79R#6)-_e?9!go`zA9JL8}53lpAxWlhyQ{vc!{+Lu=u?MR@ zL-fq=f|xpm>h>R+7KoSMXPq5ZD{X{YjxQtWat68e4LZNC^O*)ByUBHqSF7&Ijx639 zqY0TF*qLRq@ler*jTQqLOmHJPmrY{-iX%Efk&cG3*Uz0Cz@a(5Qd$5%tTl4B!nsgV z`v`<n8ti)x7zB|IRPbr3PBW^CI_D}eweXY_*-IKz6F?8YNHSgvnFvW*B1Gh%eGRnn zfY&}L>gA|}KKng@BnrLz2{7uzO_}wi33J2=Yh5bYWwkYR#SQsEW_u*yk=F4%9SIAW zRZZ833xooKdIpL;AS3|Xz!_H2WaDj%tU+HP&PpTC#Uj-1fI&$6Pv|Ld_M%px(Jy@X z7PB(_li@|MYvFU-2B?$Ra5MEK=s!f6ynHz(kZPIOMnQ4)!&*akjdDotT1`8q=o7td zz)`~Y7SabqymZ8)+p9JiEX^MeX56X<6XRm`!Ffoq^-L|&lZT~=(w~@oHlbPmksgUu ze~$w;<1j0ls-xrUUxd$BG)Wyd-DH3+%J=B-%x(MR&@}FtnLL>@z+uH=7|%ZRYzVfs zGj~XZw)EWtN`S$4@LHObiSpyuzsZam#mQHn?3a^!AFTz2s$Jte!#~VR#?fVk0`qG> zZ$^^#qp+{AH2Rb`&3T`*_*GoQ4RAn!M-pt$QL};X<Q#_~OtjHj{uyJHZdM>R2Juuh zNpfKrvmB>bb1K!hDCxPsIHuf$W{|${#+Ia?8H<gy#B7uy)im^6#SrmfOC2DZ0xu2L zRvH2w#1hj5K8eil-$^;j>GjnT)Bm4$#`+bP;yad8qJh>!8YZZG!cOe`XL07^pf6wJ z^>|by?{&|d*1VOg(?AA<dWD8-7v>cjRpM&`Dm`uLr_?=i2n`09cP}bluYBIZ_X}L8 z<IX@md_DEX*L1bDtLCV$?AG)6&WF=F83=?9`I$;-&f(CUXr=5!tx6wKLRjq_x>YC5 z*&;bNDPrSMeti5&Du{e#4I_)41HeizutIHkEZA6WrlW|XncJCLZb4IG(_DdP!C!yU zSpL^5LzMlAhbe|&uZbW4fL9>|A5IQ|Z%zmzyP~gozQTXzU;WV*4zS-(wzaM{fl>H5 zMP(-dHJG<h5nx8$a@36Yj$5`3H<;cHPHR9L6&kHjB<VB!!`uL{_==2y^q5*UJdnQE zYsvjm)&_59^^L1uM8skJIB|nJ8c>Ol<1m>|8fjQPkA@qYn{L9L%H@0?r58c(dzViv z_VYz-FZ1~@-^+EZjqstX*-NM0;HF=vaV(-XamF7GJ1YOd<f<rk1N0lML_=hZYOQQe zKJ`6ZfY3~-Xdt=gO;*i`0YFXfd!jySa}02y^YRAf*WA7FW$N8BVSVk+iOGE#hi;|D zs~oeFHOZwT4C<HYS4;%?3s9nI1ev6$ws4+16qw5ASIZ2<r|1;&Y^9w7Mh9m)pfcJH z(UP;Mfs2rbB%WQCQ>>TX+{b73wE^^0W%r|)+*oQGnY@4KXqueho`J+BKO6iKMhOGp zg$9o_mCT;x*@K6WWlEHO%^1S}AZDHrpxxam*~y~hCfU2Y#65M(D!(SqzS^47>kBa& z_#jZkE5SkAwe^?2Frr#^`<Eyaf~thKA@%}niipS{jWX&5>89E_Pdt>I7`y!#2W^fa z?~bJ<))PUinVlwcJAfiuQ_L<F({&|ZpQ^eh>}Z0gVNK$xhq!C8jX|CuyZHSj@<fhd zzT5TBg6(pVJUQRI!R^kzsMO{d>^~_DN6e{uGlS<ye!sKTql&Q#m+h2aXDj|{tJ^8O z+8MiL0viG4&0o13{YgLV1ZfPV43-J)e@~MvDMHhD;M6Jw)uF~P6rman53Uw+aFwh~ zp|Q#TX#<t<m<@vJE>PO0!p$b`391cbX~qZw5lG5!Vy+`^f=S$M$X_Vs8LS&K&+~K& zKfe2N^>CjSBBB>NDk^8{V3Mg$AR-aL@~xVEu;~j`7K`)B;jO~ofyH=>LPFpF9aoAl zB2xAuu+wbC!$Mlv2TF~mRcZehY2#BGic{W`yXS$5S>nV=&z`^jxv^vU<QgQ2#|c*5 zo>5qiD&;T@O-SKtaIuEOK>^Z;u|Lei&1vAij{kH)f!=63Pqn|dUN!W3r@jm>Ez$vh zLhP&SzAq=$9GvIR$a`exCB<^_8X4cKX;uFc3oVQ2tZ~*Mwu=40eR!#jK?xo?t0qey zyk)@JYw)=n&UEZWj!^G9QtGT%%JG|CtLK)`3NS~GDvX;nly0VG(z1}V_?u=V;wb!2 zqt7||Uw)ON-cHlf?O>$W$ypcOv+*0Q_McB^OxB+7-Q`gdU)}wjY1G?+bjLaoDcETp z?hxtkDMfN)F!VOL0`xh=KIww+2?07ogU0U=EE_!?=M>}dpIWhV06B#sZEd5)wwlih zD5ZKG{P-Lb;N8b%Qv7gn%B-$12mk(R-68%)o7H=QU!P5kD8M-~<&XLu$&rP&xyTL? z1Ao426QMZdUKU|t0RSqqbN#SP*5C<S&AWtU2Xa;!V~%DY@2lx2e;7W!GnH91B|J*( zm$%${wc{(`)`pR~w861BK*&<t=JGM}8rgy~qZ>O1Ld@f5XwfqSf@di9V|cjQT*OX# z>^Jw&Q5+{*r}-1s7D~oV_m18chHqQBVe2<@7{Khy<utV%$#ONIA!_S_LAR<7@>7lC zext0FtorVC-b#@=J|zlI*Ab*$ZhEvz<2dAfQ?l)19{cI~-RjK5sb0&&k_eJj4~g4Z z+igaJuFFbw;h&N+H{0wpqBm3_hm-=cq0!h{>Oo_OPPr8J4+<q$bdkvVv6c2U^T@UA zWFk5k_C=ivHn|neV~{|@=n}OEhn6|>cq{RYg80Dx`&3ir5$7KLJOVk35jS?(B2b(7 z(&;1ku~@zRvIV@>7E-B@BP%<{u_4&r_`adS;?gB^9eDcB1Z5wmd=|kL5sYv6UK#*L z_%Cv|x`IxkEp3(eAiUmjxGe3`r26upM?;Oz<fQU`?cyES)68Q7&2XFMdi#a*HGNAD z0&d$CF>_(ZdM_OQdYHlFxtQ}A`8qbWzUzRPKSpSSn;lv-99wMgSqJpi0c+iHax$j9 zZ0F?mNnsY5yB)Rkq(d1N6si`=%tKkZ07fdZr@`_Xr&!@;Us8}9;n>0q@rMzZ!U(G- zSuIVuqa%W6($DE?clhZKdw*5Tn`f_@Z>O~EvTb2?=g6DjtR{ZP?G@J6FxRz+Z)c{O zes|lst|ok%TxCmZIA`Ove`={{-hb92>@FFHVdELmYBu93sB5h?i(4iyMsXcs`%yGU z<V`F@hcc##kl!>VJ4n5%FA`Q{h#(G*(Biq0Q4Q0e%lLXgrPp%S02pLzK31bc-XUs7 zHNrf)!+;w8f=yP)-E$(os2fs6`sWzRS9f8;gbW{SDr<@=FxqG1b((CG;xp`n<w}V< zXvme)t^)Q9S*yoCYw~<LQ2CU#A*^LCvVHOa(jT9RHp6B^=a!9?6{SAY{{1TH-&f(- zaAa!^N+78LCoNU>Hna&1`E8@ipe&<FwJ$7!<*xmVQ^7N-pgrjtPCd+65)~Y8fdBiS z6?}(M{o9{7AtcKMgw!GPUD|sVkmIqb$>zzX<b<^26(|u3caCsKlfq*rwVVq8j;N5B z|9o7Wg4v1-=8Td7-B%8)MU1W5sie_l`{$)U9ZvyVrtYR=fN8T=w%)}YF3hqzg*KK! zE2z`eDsDD75GNf=fo7j(AX>_Q*dfRMpPxm9&ajqXSOT<}beUHBen>i7*&_QB%+c#k zX42bE5LVXG64Y~uK(jbXoOML{5obIz8r=BsPg%w@WfAg5S>yB&6V39$bE#fRl1+nD zH$H)xM8-nm!tx7>3+|uE%q?nw*MDhqc!I+W5?)+kbGBA%nMihxy+oYz2jdUgIo^Q% zZ1JB1=bvLlYy#l(b=jD~Z9l1M3L#8Ea!8xKk8JJ-jg;FgFH1WFj{7ubi^=d(#d~-U zpoap9A+kr`&&@%Yh##CqQGB>UvB~5pzv<jHf@jPydt%5EL1pu>DXvib0mCQIVNE&v z>gNvrS6<08<b@e|9*cq1T#Hq%e}d?K7&?tXx}x)n$q;fm_2YB@x=M`t_S!RY*8Rt5 zk=PMX;w~W33i5Wp6CGC?V6tJMWJiQAG3IELW;3&yC%Y;{=61Ul+e(jWhU1ggnvL4f zZFo0eybUEKzD*Y<w%+z!>HQ^C>vdz$rpEhbpdy3gWO1y$P~Hvap)&!OUh#o+A^@E` zy`D1^wS`fF#u$=R`U=}u5IIawffF!#dM`^_%Kh=LO#7qJLmu%hU*cKp?YB7RGVsQH zXv%sch~Of()m#ps%!m`0i3oW~zM}|*u=)({(9U|(??vl>G<$buc_=rWIk6&ZX1+74 zKi%;JSIhdAh}K|7BEF{0mwzaK=uxMhCumK+<%u4j`{ZBv>j!2}Ie$SNUCrfEA26AH zwI|fNZnrplIer%FaDLl+Vmn?wAJr`bcTBR}ZELo)j6mfOlZvn=QJsl<5j$Ho8}e^b zlPIExlJtHaPRlI!H-T=s`v<KD0Qu{NlV~-g^x$n;Tn^z(|9plwvNW%6&83NhA0vxJ z?`DK|$jNNVRz?e<j3X-LmwAnod-FwOFo>*zb4g?gIQUWxwLY!V{MnwM9?U*t&jYD; zsill3dbpq<j04<82&E93<_M$i_mpWN_WiN1vf_~}=a0NY)OL-iiqy=K<Wit@Q+gAM zVeB&$;S*)c!j^2SU=d*?%0C5enJ9%D1tgDnKqmsI5uadjA?>s@IenNB>X;WKT)@-Q za4E}mHfm4rq?&2szc{EUyFv*ZJpefLxK+2__N%+3Z+Lo5DAFHI{;{brjdP6#RPmi0 z3`Z~lMi(J;N$_0AQc`|~a0~yP5dK*ydDlf!eJH(dPuT47G~ItFXgF+TxO#1_|Lrm{ z+7r37*3P8wRZ6(cO%LJs&&xUZwx`C<*w<Y;P!)F~=jrk~;bnL6*1We4G$-7borw_> z$3lVq8vO(5i`|@Oc9DrrITXkxT@?tZ$U1M51ec|B)17i)%*!WH)^DTFVjFKaPXK4C zArr{oy>q4*W(HiW<~@bcmtvf74(M47lbRy`Jfq4cC)}ig5r=Zdpz)`)lF6Ym*wEl8 zl!(juqZKqENR<p!QjP(#5zc5!Wvkm^b9wN@0oJG<{?&N*|K%<L(xyN()Yd;sC7)!q z%ruHpshJ3eblQ0qKL7YV!q10-Cd4^ivOvHcHZFpcLt|_CHqe!IIV>RSHjqjS_+n=~ z|10a$4fCuw6bl$d@&+s3JM0CH$#s0(tI2x{y<KPtE4gWe?gWuQd5z`RVxoie;&D** zSr^bm^1y(q6g^RQvGH(@xUDpC?=Gt(A}{?yuvtHT=mZAvH<>fzE(&}kd()&&p{!hS zp)L+hm|AYju&i%h118R6kK?J88xQEWPd{CbtdIEvEF*^SCP<uE8FF@wZIFNQ7iFkE z)|hCL9tZb#^`ig=Z<xwPz<tT>6r{G=zH1bwJD%iwQJCU3QtqYRU)PieZ@oRZFe%nu zouYCo76TXJzYj)xXcj8L7zg%fNm^veObVAk2p1~^6`5?mIYmZNKwM~IA)arh!vm8( zklD(m%%JQO3Tmr8HSaS=Al_2*A@$h0z))?=&+O7d!eB3Bbb#XffYRW{$6}SAfmcp# zyq89)?U?SAJcD^L^e>-*qZw&!k;r>DZSEFZfl*FH@1J_9Y6%s>akB92xO^u2>bNY! z6y)aGB%{`N^wIg}1xnOPJtd#z4zboi(I-D__Mum%<{2qyJZ0ma6fiSQYYUQ<k)vMw z{--8cw;pP3f9bpTXfE<b>}L?;hGAJGZK9<%S_2XE*AMwBjzMYA(egW=M}D#0_gm*k zn|DVG$hS3d<#5aL1YOxL4@#MaxBVS;wc+*JvhljT1+npXk2=i->bT@qba?L3rTc6S z6tLBBJuMS5d?DhInUQqUYT{-u<Ar@Sh=I<4YB^k&G4=8~@oL$oV_pNwa<`@M>fEaG z576{%iZhY0%t3^Kp$-k9hsg?>bB=+{>!bx=o@@`G{oEH14O|BK5!}B?EK}L}hc#30 z$<Gn>O1s-HMIP@uK5JY(HpxD{u-rV&NRs%^qsb}=sLGV!H0KroBNYq$6BeL$0ivF+ zmEh<HizboCL3J<?=u*WMw9=5X;w7Ss%T9xzpdPR{u4xV@C3T7Rd;iX!?Cz3JopYAA zpea1xgzF@L1BTmQ`DSC{AP^qs-@eUtQHnT)=zWU9lZ$bv(K~FiRpI&MKZnSa{*??A z<EP2z!Rp{-3S?VK?b*s<cFXp@eDGxM@q3tlE1wc{8N7o-AX$e=ZPiJ1(SK-yOU-m? zf6Bi9dyf`Blep2;NqsQ&gvov9Uapx%B{$?!_Cu@p(mZiF1som~vNs6Q|M~g3FHY@R z@>eaeIDEWa{E=G?)i+p{Q5me4p-g+5rw=d{S`UX8Vm3`)S8Z~qy%CP5p;;*zQ%j?# z{Y0{C>8I0sw85-i)RoLL%fRH)z)!Di<{nV~{>Weg6^u#c+u(i7j35(nw&T9TM#fNo z4d=8r){oHxhLhn%8QYBSv3*2Cq22m}-_sDo8QV1}vi1*t$S4aJ%xf2?zJ~Mxm<I&F zz6zJHI~vPg5aVM7JH#51Lb*pPoy)!_(hc`{40?gYWRPJw81`4uN6Ydz0^|&f5z2IB zYIv>(05iS3M`SdF6#RXtdD=j>ls5N7LQ`sGGH_C~QC3tSBM3M!3)8(8nJs-oX{b*j zclK=TwD#xkG5n{9m5O~OPW=BI@8PrEXa{tgs~u+1iIy`{p&|_}7|Dl37TW}V7nYBo zhXoSs1>oR}nlV|#*85e;NI-ln2$0+}oJ!wu=D-Rbah^n!My7B$?KrFk`q^CfrZEfN z{|$L8xPfPHZ>K(!zDIg$$VsZBa$n)ZDX#m8eXlFc1GlMLdM@0HYJftLRU8kC6t=P* zL_&5o^Py?U)^yZ$?D(QVw{}NIt^EGkcOa4b_h%%2hEPM!6)3HUfyDc{u{%y)j3Y^k zP@B`@iG)ouuk$&&i5#eN3@OB_56dbId>76OmBMK6g1W9}f0~ggFpMd;>>-Vk=|_SI zag@PljRu{PoM=FO71e~}h-?rfvQutjMvbk!9<xwE<fiz_*d>Iw!uWN@_Ego-KWTM~ zhlpe#b8P{T^)j5`))gFkYf)a~&zG?9R9j1KR-bFzgXiF=(W9r0Re#rg$pu&3&0`TR z@Rg$+3~uf&ku>|EDqm9k`GZagsdRB9u42k+w7s9MNj)l(2WS(L^F1v3ctE8Fblgrr zSk&46@l)jt@v%C99%F>RF=oUi1Ruy?u_AeoR!1ZbWBW3;OvC$D07eX@hFg7LSDcy{ zcMjI)1-Dq8(Z|@<CCJCQ{;?xbCE2+pzN$K;KPwG<DYfObN`lV4bQac%R>sp0QzZ8+ zneI^D7VhqS6CYZ0x5fx{f?0A*J-le0e)o-30jG5&k97<!8XuS8Da{%cx_ot<Bw_a+ zLGT`5g7VM(b%^)55=>Wn9k`U~!pj`X9T9LpvfK5cb@o@(<(~7IMcQ;nj$fH#JXarn zvBXEsP@LuF5f=A_d`BB*k}`tOd^jJRNjA4nNdPQ9Iq!;$Li$NhnrZS(|3<gUmCh5{ zH3^qUR5%1+yRau0^F<tyCU;2uq`6>nsbXMc^HXd6=O*M&089wanD}}fD#pSe#LqfH zJv&>8*8*%ljx$MmI64?AytvLDxc19b>v#G9Js?IV@{`P$c-MC5Qsg^#%THhEh7aKA zLR=I&8F0SX+kdL(7W|$(Q(%>!u4ulpM_ztZnRhlVYw+hP^`sVs)N&Df3t^w`{zWQj z?0(#@p=|kiaDGia$Qv#U6!tOMnhT}lC`tB2miek*mf8c>!f9EwO_^mD3xH(uwVwFK zI08KE-wp1TTT$r@`()~?i~vWucUOb7mX~ixtmT|a>6$1^5O;H0|0F35F%(?=+7m(i zi+-H!tUVf(STC$%qYe@V=}R!U3^TPTaBmo1ch+q|t_f~`J$1hjrAgsIOhj2a6sPHL zv9F!|6;lFuoMF~3o!vBD-=YloQ|SBB5=zpXfNStjT>gCxo-{0ykJ0~ogbFN3-?YQD zRn9E;R(@fIC;QMf!bBHv;6Ef=09nC(2^pC@;`eKabe?Q4nlO?#I3*N?+z6k<cV*l3 zUl$)>g9oo#)u>b9gkyfek`f>{IPn4GsO<f}LYv|wW+&QE4^;TFk4gDt7*8cKTL-NS z_Uc>lJN8-_7(>#@OsZIa&fRyCh<i&TPcNz$GP&Lw_?!pc`F>&hJeTQDtZP?0<K->4 zBuyrQ7C1(wDVgw}0la{UKO{j+kK^H-=ixCHRs|n`G&^fNmSu(l@{lC@W|>usol}fC zf>^WRnTklicWF}QsRRPO%?G7jz9YsWA>GvYXS$D-?QC)=gDgu|rs9SsKqjfBd=e+_ zEltyw=0%d%OJmlO<Y|Mm<|PSWb7q2$UkKahizr<LElhpLBN@xvW5j23b?Akl+A2za z?<+gh{uDO=cv`^qZh?)@zsUHAE~i=3gou8bU@R-w7lzkU9A;3HeGsC&;POz3-lr<m z*;F;&>fN!%vXgu~k+*z{t5OT07ox1vQKApXXWob+>lCeCl|<Eo41I=wpv<jDt!#{u zs!x|Xr9h6CNX5k9o(y->CA%wu1c1dqE=6zC^y6!<+#C6<rd1`Lst}Bv&E&{<C=Fd} zEINXwFOhG!mLAhim`7u#NM2S3fIV_m?|#SX?Co)w#@Y=(g<w6UFFKqgR;x{aBrSSi zuWC}X04G}pgy%)nqq!N(2T4}O)~Twqu+g^V<#qMCaX)$j<enC+3Vd`-J$~Jf)C_*- zh*~6h#eN)_5U?^o$wKWofa)1CfHMO>*p82LVCEU}$oe_ADyOYTla`FHP?9V#MaUIM zwC52FF&9yKe!U92-bBBeIS;j!)8~Fg{vs~B9>wzc!`fujKNEyjLa28u>NKK!7Hq~l zS10`C2-rh`z<uTU5hw__4~^dhI2V0a0&w?gmo))ja9u5e6|9y2kumyC)+mTUg=5K4 z^kX`ZJ0}SIIBv^>B3z_R=Ks-0B^Tq8;gwV*N4A{+gFek9$0nQ7aN9y&8a&>}Ts=4Z zZ#@d^U^P{d^{DR;-ut6k7k8VT^TXViGdV5KsN}hB*u#x(pMx(PB7HJ6^}0FRQaw$6 zJ^Y$1AexPOCp$P*O_$hT_Ag{S32enq4Ubt43NmLx*Fc)7qlEQm<Ay3gt=dKaujI5K zSigJ3($lcryPMzSpjwR_Fo(2$$al@1c0}=Zmt-CJq`SNsh3O+I*a8d`d76yAIi<Iq zRv_#f93I!*z5eZ!I-k^*He9RJ*DbtB`kjAP+a<K{V=-9Ta*Mr?-H!dcad-ePs_c|f zOiFTak0Qp^mO{8`v$AtIIgwmTz)FE2&J(T}NyK(?t!OfOT;@!uT34JtXTSZ~k9SvV zxt$rLEZaAmv2JJzecl>$VKRt~EMww@V*hqDo4QH}cF83Y^bzV`-eKXzLRqu6T@{^s zL_U8U0g0s2`+Z$>f6wg#W#Wp+N?dJXeKtaN#E<lFfb-%MdRzFp#cQT)-2$n^dfQBS zN(0{)>}+r`0`CpO`NwM8FZv}iKI4OEMJPEiqi6-6or)B1e>dZ)+M0^M*1RFgEKL9$ zE0h8^=cxJ<jvYTkJCAcuLV*7}z_YX}on&vn5!d>|##Ve=TpS;aDhNl|{T#BT7C~Ix zbE)92%V~cM7~bvx_51mLu?NV)_uWY6`6l<h@wuISFkMhsi-MtN&F(`sg75+R5|2z2 z&rj(yDM*A29ufjaiw;qMZHysRZ`m~5_Gi6?)<`PRWF*!}tyJe`9T08RMqc0eI?hI& zPn$NH>^ElIw2YV5Us@;DuEx_?_oX>(PFI@_j=|2SvQs6Dc5R7>D}?7|xveHk;qtm_ zaa6#(*{F!J+kVatqn)Kb%-d_dK3`XX<})q5dl0mr9N2P6<j7C>*j3MuNjScRJ+p{O zo(1o;cmko?PHra*{~btj)y0pOxs`&VU&t`w5nLh2@XQNh!g1X8&v=kasWme+k~MaP z4smj+#p8u&xs8HzMS2(F!~gb3E|YL4B;MR9D`NvN+T!n>GoLmOUBAdj#H0)6qKpDO zGqt|Ndl_8QhjqUSdnC)jL(V%Qxgq;P?#k$bTq{=L00+QLdqtmg*ka34*r!0x&P2W9 z79G)Z&~Z?|90pMad3+Wc^5~fCmzE~2%$z|=A{PRWqx>=dut*Sm`vw;q6xg8&5+H96 zBre8f2rxhs-*wb`xJZEi8e%&AH;%EH6EB)cc}ebw)t1}~mf>|WdS|Us$JA8?Rt%jh zq>jZqxpVF7?TVP~uI4U{8|yY)aYvK>Cx8R&;cEHY!|t>|<k#)?;I|Px6huFqbW$@! zfz;_7S-}z&cq5v=*fF<Xm{xuBOMIU+&u_E}Ue@}I^j2WXDV6!SR<mdd%jum0R)-oW zJ1s?)Tb4q9rxiXn!Qgs0JpAh!mWc49+On^xV^J<@B$#18%D`fZK}0$G#8px!GSGHG ziXv`WmzmvZ=q`VPJqn&0cm7k*jf509B<JR^E3qYomxHu&<|p`dnS9beI^EQ@?P{RG zvtI6%fT32GoW8Dz5>^=#T>w!8z{+pfY2thRbdYoIHc*qrljFx9W+sW9`i48nm?}F0 zvYlwS%X%pP*kHL@O~|n8UC>1~dH}Ir^S0!;a(8pcnTcrtf2F*uCrT8&$L=l@Yy@6V zmt5ZX@w48>V#C8rQuxDI?$x;{ttF5lr|5x9)<a2WHpXQ?YK2zL;Uitw^i{wFZVJOw z{~eXm{C(gx`-=7MbMt+injH7(u|nlen4hiq@6X(LxE%I=qiviur9DXLNL6wV7JqMv zfQ{^eWimSKW&9BY^~Lts-3|FxStJsL5uU1A-fR$?@{g!UfB4!9du-b6y=QK_^ZvDj zR6|q1YgtSlO7A2BxbWW;zlpl9i>q&kWIFpZds1Wp)ohRoIk_m|ss58@kW`Za;Rw=0 z+-V|AHVPlmB&&(HFvDWg5_KlPt$1qbI#Xf5c50OpEniOO{>a@QddGchwCO}cpiwJ_ ze&u*mlNT0ko6m262muL2fJZ_4Suo$6$Ia~yQSTMUeNf9TQ#2M7@3BW(f5_G1U2zL; z!&{I6*vTp}fjPeXWTGghbVRnf4y)Vc`#g0AHcwGqdlhQNXV8ve3a^Kx+Apjk4yF*p zo9A|z)Atsg_qN_YA|*tq`fDH^=PthXsc0ywTLzndaaqI!UB~(@MR?8AD#fdP$wf*I z2NO)A^b`(;M>2d+%}rJaOT&+Ue5oyDX`H+^bC>E+Z5?cM+}dllU!D;on9Chn5smns z_W>@WfASJct1X<y%`Hh&x9#ySeCK)jQ*avpri$_aj7W+Kdj2IzjY!rsW!j9Jjeq5K zim=GMOn1HF*TwDpX2O<ktKU3VLu7Z-nnBLYn=)FBeiNPpwV0nLU*SDw&fzI#bbb^g z3)$gsVu)L&uhl-Kq*yrfFnaVr^(4I<ye_!}Eo)7HJZ=t2^lUZ%jI4QYYLf72kR&B; zc7JI%bWkKLU`AAmR=Q6OmN^tMqh=zn9*I|UNhlpOcFd71wj$0elJ$rDv-T&`#pW~L zz_l6o>v1CRFFx=Vw%KiuG`9rZME;GWx7Am0TrU~qGo(nH0Qfxyv%}^I7~gfgho}?k zeAEMCIEDpL+Hu_&sS6BqLt%|(D~h@uVn#`%79$o9Fo1j>d}vXuB$OeR(k*&x(&R)* z<?+;Y$@*A_`;Xh}1TNNl47h9dNC?N918{~X#%M5i*_PcFwFtf~3LOrsF1c?ZmGjuk z8d}5E7Atuei%43sE%s+!DWy)@YqiI@YSJz;nK5E_=eapv2R%F={{(9DZ6|~a9^6c~ z<SNJpA$F%$drOIZ>h_foISf{-5+0lBlDsfhsMeHh{GpNvJa}NXbR5G)EJ<|KUj)Md zhQ4UsMr5e)(9h{Jp#ci`8Zq>YPs(xg!*fe%sPw(YSX<egP)=mg#Kn&12HO$hjv+Y2 zI8M+$mPLk4YZ<!sKfj5cz$KDmeByf%ls&?-^g}p1<h#XdkR6bn1(iB)@vCuj1!6Hb zTJinX4~wiM35<Y%;s^cpRf%Lk4S9%9+=MTUp0r-FM=n_B0j(jO{(A9|>i<Kp%lEBi zU^{w}vC#3AO-&dv&kaW=cgQ<f(WrOQ82h$vk|@q)cS-<7!=HTD(Jo$E6Zb;Fo$R+j zyhg$tu`>5lNrIw+KqD+9Ihp>*;c6OdsV@^zJq2S%zDAqAd6BPvyv~tetI?Jia|wJR zwUP4QCf_1oNE8c5=ptld5+?l)Wm;v)7ou*Okeo5qu3C7M{*T9ddooTqpVscPeL`-& zjhlHMx#qedXjkd4(S0S$X3D{H+O55Y&^E|ba?EnzFCD*}rpmWp*3)yeJISUxVENMP ze7g>z<h;3Q|I6t!c7wwT3oVW3<9V8#4%3Czem(x$%(ub&6Wy%>YEQd8=j|QDtS=m~ zi)vt@TW!hDk<web*>F2!xD%y0uwo4^jEVHVCV$UaR;f{?vtphGW2~t1;r2b|{D~C5 zFn5iIx5X}**Hp8UiX6@ri|QIYfw-*Cd~m6pXJ*y9GHM@jDpiF;awS#1!)TCx(DPUu zBJj^U#R#WnQ6M`eW>rC%a*O+v+e=R9je_0Dl46-z-copc_vJ_2$q>Yz?oumw!zGs0 za?M%FzKdx}X>KcKKf+N4G<slIy$1`Z1ebn3I>zqLA**Hj%S$H|aBtW$4Bcnv)ur6X z!vHOaQ;ddJ%qEMVzVW`lGfePewlOX#EuZ9(AaK&kE@Ff8LUS_A;txL?5dJ1w9R!=r z8q^?D5vMiQQC9AB5h<w!tEqE1U55abmZW1YtI&uvDq?{1I*RX+nJ_<qDT~0Z!$QL5 z#A2(Ih*xQgF`X;wj|uuJGLQ&&NGn@rpWVD7{Iu>xJgUWRmSe|bv+nnLYhS`*{fftH zGgHqqp^scOzF6OGsU7cE3D$f|kgTPf>dg}9q`HA?>IRvLjBCGWr+E<24=|VdaD<~K z*6)LSKDN8`>~wH8+|;#+Z4Y6!V+_ieW4rY5lah#b4!WZlu!j#mqXv5DQ&;^6RQv*8 zSQD5aDq?8Bi|=Z5&QcZKuNTpuhl3pYd@oAMU~bxZuxik)A%i2UB9852B+5jt_L+xd z-j<^r2LwhN;sMSy`@P&{N-8Dj^T&X}R{dTmqEYrN<=fd<QpiJPsG&I>$vI_Z`kr=7 z(!5?MU;mgZ&Gtj|Qf3}JJ?H*GuO?5s2?|Wa)H>UxL=P9IPv6WsQ_E@Ne&a3k8|V6b zP@N_4o{sKK)lK$UpzO^1Zpn|^zGKlK?Xluo{w2gwt}|e*W)zg}mJ6-#BdggM8!*V| zYb>#Sw3;jd5#7A@Y{{R6;^S(@&4>|;HVLL=u1E7BBvH9hHAPf0T?c}(^fM`bWLiDu zNKGV~N+@qFF&L^zcUPupG32L)d!~B<GvaS3athu7Iycl|tjU89nkJpK0x@Or3q_&C zUovP)vNXtY24gjn?>stuZVz-3Di3GwlRSP^D_)O$us=&_by}MD;JW)P48;6BWfbC< z5@za6UAR$Q$_=<Xya_ZWze_2@x{tKMoOOCG^FDZLx-n;sX%3o636aCh&=l6s=dYY* z)ZUI#V0j(g9-cM-xUM#3wfF6b8nY~zAxQtEVF0$}EstHm9ti99US&pw!VBR*g(~I_ ziDK~i7KciMu>BSCLb*+<#dm^o9K2<elgYq3^Lt1F&|&cvdw(+*Dg8>X1u9%(J*aQ4 zXBh5UYx#eT5rVp*;GiC|z9M7W_}^Ku@EUd5A$Vc~hozUJ^~gXauGh8>)8i+N>(1F5 zCAG6&a;<CO=W4!!w&`Y5u@5ES7Jcd&>2*$bohd+uul?HV?AJ}o5qbL`U)8*ggTv=d zNB!47Zo)-bwa=4-HGAe2KfI$st>|*>cU3p3iaygc#gz0V#7FW3DN^Gux!5RF$1yKB zQvC2Z0eRiIC9b#pI1M<A-;ZAhe2+Ho9n#&9QzPsn15mwfkZ{xGhqDe?La1Cy#pE$m zLiN|>K?%%(Wx*D_o}oLOC&3H1)`B*V!!k;;c(t1a)up&t)+rqZNaPx;*e6A&(j3dh zF*CBdtpO9kFVD|zgaq7%eZwk5Zu4(Vh48x*iii+-=T*MUSv=HH{tmT5mS$0C;S1pA z-WYSRwJ8d_rP1RfprzM3tRjR@!+|<5<Q0pyG9$xhs~mrF)nq$!8<Y2R4+&R{GjB-b z3`U~{tPn{IzWy(&{=u#C?|%b@<D|)$Y}+;2HCa1PwlUdF#$-&ky|ZoGb~{b>vp?V8 zxt?>K|DfG^ueI)V>-CQUVwaG@qzgskv^2jcP*4Rqy;Nq1a8XwbbG^6?9?lKLtl=-T zZN8XiZxMFHU1XdkFTjVpgkMUt@$5TO0w!`rZ#W~=nrn)tB>^+-o@q>KUr27!fFi!Q z<j{;jTpel2edm|vE9s8>;qc{0E3Yd_6Tg?4%#7hG;%;NOEwca$VC^)H=)oFY-?EB- zi2a`$^*~>sH;O>B-*MpgbN78X&=qk;Y$CG$I^3gKX8eS?=iqV&5N;ZtI>~$z(235L z5a1`b0-nw}V&GDUs!jR59|j*iZMZrpE|U~9-z~)&VMIbMB1BA_xg2OCVtyGEY?}3; zU=hoAI(3SfYJYf%b!Wi&<$QLDyS8N({4=<R<}S{B0y`6ZhcycwBBKo#D1)UaMx^#m z7@CZDBwpz-Xf}5tEGj2I-l<HwJzqIDUU3^e7`D2?0<1aLH-?e2*3#i%n>b2bF(cmC zXwW4r0#n|wzLx$ZQA@41p^9BR{~MI)wfe(B?5Qyke4gS!SkrW^jYKSF#I$GkLZLK$ z{^YVg97Uaez2i!}mVGqw;(nIyXjK>y7QzX&vA=p$!jxDCMcnKM*a1=bFP^a=hH-q! z1Jc%RhAJ58Ky0Uqerpa*WjDK4P(tKn(^xQM`Q#WGt@$|9N$lxto@Fi`|Dv`0QohCZ z&5Ynjqa9m#x@LLj?kbv8p}Qw()1rrqOlVNXkYV&u=V{VF#zsEr{~oT?^NX@rkG+_G z>xvrkeI6?7CJacl)!Zxo9J719P=`4W<?hrHHq*K|Cxg6LphgA*)vN~RVWxeJ&!gGr zPrvg!RDHgWC!n+ulJA2)$dGjcCDFqXmZ2J(F{E=_?DY}GQ|=Eey+U<`I(1ycN_bYJ zEECqy^;dMZAk;o=gMdML4VrRwSQSZEa!TB*pFe%1Y^6@M_3?e%jZ$+C7hNv{U99lK z*-h_g@qQ<;(+|y)?x`dqapZc3C!h;d-JE1A48J^@?W2!J;iniCpxX~ut~&*=7}ZtB zC==J;YLL&Ei{SS|SRQZv4l<%{O<*GmDaprQT$J2U!Rb;ySUBky*O1Qo`V@Cp1VbHM zl$A8E>Y?LgIJ5t&2;QKM%;@rElXtP?H8&)u)$N}*3x1S%<VKm02mG4<1_uRqLgDWY zl+#%W2xcNScqD3UY*cEzc8iFHk-}n+a<%^#gwACLDBbE;q07qxeH}{nC{(c0T#~cA z<Bl7`)z|VF2Rs$mD;!f&@yndlNn{rn5T?FohXBH(Y1-jqEfZA3RY%VfX<=G<$QP;1 z!>wsDqbqY)<abZGN*<jTi2EA2%_<ipM6XC3n!|G6$%H+m`$ycfg^vsz(nDWAct>&M zil4|L4%OC8qiS`~7+lvmFC*&|C%dAnfV@kF&r81j#}2JS-B_TSX`=TRp~<(rGd26q zwe3>tFl$^orv=;<KTbENUxe+YJ<B^=h&N*e&`^>8l2j~%mk~7U<-1TL86x-2<$jP% zj8r23E8-i&)k?5VK}2|f8J&7=JZ!*U^>SXTM69k??OlnkM3seYRE_uU6sZy#gi?by z6NQyau9Jz=h7VLhr&6n#DO?2iREDW?Ol#AM1tC)5bkaw1>%s!*TZ~qxfAhwF=>}}C zxMLB+53ah?2n#$`N7$59u0`mw=Al|#VGev9_p6$<rLir532-}VxM(Px1ZjlmP^QU2 zE{J{h<<G7O#@1G4HV=Qn4x$Bn$X~5)-I-UEmxv{cI!-J@P`ya(Q*F21(Wd{z{A}{= zxkI)PXnopt$O<*FeFmkq*NnmC_Y-jn^9Jj>c8oHlITj3WySp?yV+8e_Tzxp+si_2J zgWR^2gM)XI@Fm0dti;Rt@^{fv%Vu8HWN|K~;?sCA8|(4BselwJI!KXo6`1EsR!+zG zk0IXN9nYC5^KetcqUQunSJJezl-EB@tSKzI9(V5gyayI!T2FCo8P?(yv{V%q6dCfn zRB5C+*}PStWIOfi;m8^!Z~-bX77t0aQ7h__5-gDcn#NV(^=Rf+bXydgUVmO&H2d=Q zcY4`2E{7}3g`Edm9Jap`@q_#G+la3$SL&`vU$We!keK5i?&AUwAgPKqHz0`EE#^J@ zQ;gA1F*Ox@hMx;=2x(I+%-n<dw@L3NAt;e4te^4fmIOV1<QKEikHrc`tD4a`(F^p^ zWNjv6(1~iM$mGsQJk^_{*%rk2;RcuBRw%;eW_QBj+y8n_e<U_#3yQZEwrjGX{RfbF zk7Ku%zg4{nKt%W`J7`|1)zdNe1kVNf0b4+l)K@4`Q!W!Cv4mK|LGjDzGTYTV@0~I{ zX2#)^=Zt|zVetInGOf1QY?Ql0+;KeHqP7IyghOZv6M4q633JIP4?Op4&FV+S>fg79 zzeLe2l>?2DjTcTqH{b!e|DOxC@qKu$|4N$lNB@)ze&x08x_7P9?vh+a%ID69c3Q~i z%iFBKlld)MCw}YI6jk%-4%!g;Ujyf8k{VSUqCd7BTq1Bh;-O?Rp=#J-IGtal6qoAk zVE;bh<GzpJ(2?`o<dt*CTmME>8Z38gTxu<~5w_!IlhjW*9rDR&qIE5<Cy;}rl_71P z!-B7*?X$TZDK-~!NIq<M6CBC%^Y_%?aBDDTfJ$+fV%co^6_tTjcfOdQ>((!urO994 zODgqksQl`WFLTBq7+w6W%NyPSpIP>E>(dUd$Qc~<+vuVu`J2EzeJ4p}X}cB3hl_2b zBp)rjA3chc9`(n>=JDM}GPn9r|J|@*x!@bNrx2k%q#g$C|Cl9OGUfU_bNtb?y7rC{ zcSpSnVT$6}8k!R9|Az}5G(R;a0`v~EsWU~C6{tgn@>g)R&|Ae`Z|TrTuK+6fmeLJS zl(RJO93u<abV7ou0S8si<4CJUq)*x}FC6LDz`Op`n0|`IV`PJuflLK0C^7md!`)aH zS{I#*G<@0qW%$C5NGl6P0??Gakz#`H`M~G@-MwbXj5kyO^-+_1zC6+reYd#`G(Ti- zRKH*67T(9_4VImHNfnZkqK2YKvH<vq<WRv7&57tVqZ`ml2eNnQ<hD<L8bH|75`~zA zAI!eJRh{`&xAm^R+aC_cP#Q7T8%3Igqwumj7EvhmoRrd<nCEfgKdMU>C&9ExMH;Uo zvfpqyMr4VqfKgOdia0v&ck&dFXx19Yc4v=x*=ioxA-inFlnW+%w?bTXIOZVmRu(ZD z70%578peH;NI-z9f(cCAyi65zAP*z_H?l~CvwA7j%_1yaLlu(=V)5dEvPe=5Ms=1z zAsM8_SxMq;ZK;kiL3Q>s!S~^}Ti45>L*xR@-6!<7NQrJBFGdfb-42mKaHUQlipW=s zzYovXpv`G)`zQND3sAbVjfZ=vz2JExOisz#D4>W94U>LqDZfa2hn_tl;N!=CP>a@M z)U{J4x0MYVssMgMTuoF)F%Dr?2B4N|IXYR*7<mT5-$-j6*PIFKadq^Go7eE<sg}eM zl}<gr%JLDu0AT7b^%Hf5yVn}iK5ULL%prb}3+Dj}DyvG6s3sv&`eLZjh*GNMwlzpd zmajn_bCnOPs^nRJje(&pXTPp2Lo1~M6Xlrd^U;n;fpV5VYn%*jIMCitey7F=-dy5o zoq2*O=u?{wZ$PapPM#65jZ3MU?vM(b6-#x{thoq#KLk8@v%e761p%mABL#_6B_z4Z zE23J&C%hpMRE}x&9I%tVId*~ydC1<p(p=US%-2jhF(5u12WQiH6A!jV&)bllEr8vh z33a$(v+Xy0PkRdTS-;?qaq%X9)$TcB#c&YD%dnUfJ0*Wr_Opz<CJYP_Y?g?Gl4H-L z7c#y}%ClzEG}42O!TXvUEGdaCf0mos)bbbr-J(A@+AF4$uww37c4CSQxq4^~n!<jF z*MWmVL?;N>ypnXJrccNTr;3`RmG=ho5v^r&)Go3$f<8r>r_<5AX=fLselKh#gJY=0 zasv7H<@zyK?UrAsUfT?x=1Pw2hV0!4KYr&Vo@9f3TZ(@!vQ|gE2ydiqwO*|bf*Jd* zF+hRF+fJ7@NvE-Z{@VE#Z_c(baxIdmwOb@Y_UuS~`roioj1Eebd0eY*_$)zQ$~D%w zwx1zzY0=iPAptUpnE=)pL#D`F#fn_)LaKPdzs$pcmpqFgJ85G@IiM@8Vg=#%V6L$a ztd8At;^(lfQY<W|C`@LMRW!MGy%hZK#eV{{5&CADl54!sG5d!sDE_YBnPRy-t_Dqn z{ofDNJ~*$2*eV?5XO;xvz#s2a2?sTe0%=uPJq0k4zTS=Zkn3web!L;B67k=pXmkFW zw#&_?LAS&iV(>G!WAn$W)#|==9Z+PHB)cX5c(3H-?BccV<2O-&Z;sAeGC@h##m3Td z=GFj@Ui(DKc+;lT8=eqhJa}3EaGWmAhbcT^%bGPf{njGY7=qCt7|GV%i}`wFHurBB zQj7j6Jl-RZZFj-apjT3hO-G@x_bc1z>}%#j1A5K%@L)%)fs=66W}af08fRf`j@!<g zi5C~qBD9iL8r7CuuVUZUt_rwB&pKnVD1Vh+x%1e_1a(W9$15hV2MNI|Io`t<24!eq zQmO)Tp)4{D7X}~e5fH8*6kupXMOxi;1_%^|_XoD<yQn{gF5laG*>tDG>T#oZFSFR_ z2zg&rGqvX!g$<r%P9_xnOhbkc-L88i#dRR*KJvfJe!gL#>3H8rF?roV5dEx}&&k_8 zFJd;68~{ylZ~OaNT}Sg}-nn73$LOe=0gR#{dd|DQ7?aJ1(^e{^K6NyZ{%sr&%1BHe z<bh#uQ&cA!iw{NDQk55l@0Mz6&B5K2x^|I|4duyNECzOjGJh;2gE#+XmR=xrFo&uh zhsLKwGR8KBlGibgLC6k?k`%9AQ4qe`J|aGcLxv-4lFALf?@XcOKoVH?Rv-TjvjXbO z^O75}1bR@<v~2sb4EIEduIPp+zGxmdyv+kJ9rvM;_*dwH!<e1qVCVB=`ip$$<G5uB zikSmIWBF;-op93T#X9W({MBRhrC$w`=A~#JxrxqeZfU8XP(^5W#<p)ZS}lhOLx;j0 z{tv<i{Lh=;qI-W(Kj(?;24nItnifz=5B9|sndC;(&(@|yf2S&<E(t;-T1pc9>NUg+ zp<RCi&aj<&q(#CiP#PRT?J1bhnJK5cIO$b^y>*v@JtASkoP`1<gM4;L^X2hn!vazj zIP}$ec!cHq$cm$?(R{;t<zh*s$f9W<K8%R-mgNlUqV%Q7s*=G-7^-_vqV#zIke$Y= zfge0k<90q?{Rnr9og4PSB-_H?Pj8dsJXdD1{HBU>P%yrm4`9%4clq68x1EJ>KgHs` zF#43&GEfk<VxbRSqdWP#0Qpiny~$Prg(i%C-b7I{IiRao>~C183|;;~W^T0LvVGB) za9TvVD3k0@O?3+>=$_o=g!y`8ExGV)^oo@E$f1NYi=t$D?CaH*u$nyUAM!urJ<1X| z<la(7IdSGOSR=G_%<d#bP9fF0NKtz)PpV2Fdc?D&A8kca-_CEP_P_1@c$^UMJ~8fa zaTz`8T&esMu9ZHfqDkS3yMr=eh5Ea(h(7Ht31>09QqaoBNp0@uMU6@Cg&n4)*a-0h z@!QkV^uy`Mdjpof_h8K2gg9!SHIvYjcBDAh4J%UHLn+mIwVn3!KHQPd&fHB6f59_t zrfEjj<Gphw%6*TDAG>2~yI9PnIOVQmGQT@rSMb)jQ~nzbh8VVXJjFG)gt19_0(m-9 z-*56Y?L3@Z6_ql5M7rLnRIM|*_dK@Ct-En=?ykekrLEsM4a}9Fj5S*n=--ZE8ku@j ziHw8XZnR2U01D(qiz@7cskAX!1`#a-2?KJwI2JfIi`aGE84)V@A>vj?Md4|nIovTk z4*isTeJayPtU5n9sHU5pu-)0t^e)f!J8$ahtWI+p@0_B_B8sn;A_1rYMFfu<97f15 z`v~tk2CMTINMie<sS`|28WH*~xpTZL^Jt?Gu+cjz_}D5)2&Lqw66I9?9L<f0VG|Q0 z%}Apmbd2RG|E3hyQFs??B1ai6l26tIOG(TUx0m<-E{lPWEK(Z2vlZwv7=OHE6wcB3 z{fE{Cp!_he=%bAaVzN50_Q5^hfoz;%gv|Bm$s5sRLTpp#2Cm-BHB+U@7vQL98s;Ig zk(Q>+;7Z{kz(u1m2=!phZ$L*jyqIH0gtUx~E9vf9kX~kIvSD0Beb(?>>?|R0!y1jP zgVinHkNc%eG^UJ2Ry<oE4TQKGmdI}07gCU5i`N^1Fp=Xw*hgfKe51qi#U5}65aQZs z>GB`;GX3ujc~aFBy*u~{f^Vj0gdyjR5}uID<BDFGeKp_*dwL4~1*M=oyA+rqi*gz~ ziF9FcihLDJTyi7nxE8k0xrt|>?zrF3Xch_PoanZgAF@l`Xk(j?wIV#szX>i9E9h58 z@a5)~lI&CL&<7Z%nbkY-*Gn)i+<d2%Ri|aKob${RF{B3c!9nqy^;zNmWH@Y)H(?Y( zBy|L{Eo3EJ6v(+8@0uLbi;{Zr%i}HVSCWiaeXSIK2lMII-^iVzL;-TDt~+xwWvi4+ zjups=$4NRi_tDGwjODaXVN-c9|Mut?BUUI3$i`BFKF$@?9Prw`*2XTP>7ZgRhi=F^ zgTBDnevMkX>N1<4w-o?eg`~))14Rn?oD}7dc3m`RUeBUAe{*Dr!>Wx%Gq|VMSxBPc zk{5y0!7NeDscIoSf(8P$sw^`g{uD6O;2G>;l`OX_;b7l1n>f_vWm2k=McA=BDK4h# zqBzdo(?T)Eb=+VKV5Kho14V=lD6Q1^z92?ue;=t6<wX$%Pb;qMV^zKuk#CAY`6W!X zjw7-_WAztZLLS}PK2LLdj^BjUs?#4~?**_3^4O?bysMQ!FkDI8?DLvVbcrVUkte(c zm)4+(kr*%i$$+jotQI_n`=!UtlgFOGSx1I8xpHJGKpVC~#ueKYUGtkOOp5iz+Shdj zf9NypMRO@Yq1cXf&ivNr6v_6#zgyydPPYdstptG$dGdJYL93AG#$<K(dJqqAX*NwB z(Xbh|&rOrk7Ht&2!KwvT2MHSLZfJ?bO|Ip`4eIYF^w54o+&{cWj{U;B2V>s4=)O{z z*b#A#{LwtwZ(OzKiLlyM^x9X&b)5hA9pn!qtJm=GVpsHu(^d8L5C~GFWGjuYDnLu% z8i{3$PM2KpJ&I&i-l(?BP>Z(!&A*I61>Q(`*bxt%$Cve-7!fWjY(RdyTs;f*@3b1L zGSOr!k(5WbEUsLF#3qHb43JRtK2HO*{D{oN5vHF-Cc}gR=IB(b41VX18UTqfrYVc| z221I$`35sKSX}X4e9PNg;vd<oq0-0Lx6;j3kyM8qLlnygYeCNWBV}?8Nm{g=y?vF9 zrCU2O%IAF?#y_uyT&vxf>y@EuaS}qPts{T6^BEXkYgmtxp^2hH8HN42vE(Q0|483b z`22N0uq||ivYOFp|CoO9`b=#z<<*iue={Abcac!UN*7a?RE)?DFvW#fcOyw_vAm7Y zjOpjQn4a!_-0h{%sMG_u&AkX8;L@+EqQI&`4gSK=8x2@nBnn)$T9+^H90SlU<^)%u zrQ-Yzo}0sur-#|9&3pj*hQ9qvp-Z1P){j~@kHIJW+xW@nAwY+R1cIeJQUgh8b$GJ; zaqG4{`1k2;IMuBu{OrI?cpKUUHX2SkbL1Sp%>Dp>cAm1=g-Ty|9OJcWjHKkEf`0;N zYkG||{xODf{-+lPG7xwJ;EgCoBqJF5Smgp+wYn8`Z?cf=Cao4_vS3e|^O56os+cv9 z;10e>KdVaYhY;`w7?*v+Krb|gEs#}EM`-^)kWLn=Vw{ND1OJtll|I|i@S7Y|jJuyk z`S%M=`%LHY6@Z#%i>uCEW1F*%ZVA&-gQyC~6W9d*KicFkNyDYK<i80C6ZNDVE`ojr z{`j(M_?j)?lBDV08CVH4zDULr&C(!iWL;4MWR-KL4I2a_FJ}D`vcsX!M0%bvZ?`Uj z50gcZ3w}%n1ft-e(mGMfT9W(Z!(%`N)?#s)bJ9mDa2^*OM2c+)N|3;BsSe!VTSN~K z{J55?O}zXY;+`bH?b2V_*#01ROX$zK##s)`>svh5$l$C<1kfvBlacHw{lqfLa+}*@ z0_*ha|3)bSD^xem`*hi@M1F!u?KukQRmmo-rduxq-QQ1^rn7Di=$aL+vf9xHPSiX& zb6Fd;Ngy3Zx*Mbk6R4<ULLf|pf8Ky_{U-KKPMyZx^_J_;_}!;`J_q;zxGB-J!XZ0y zL)jPXCkOdZl#rO;Q)iL*_Yr@<kbL1Bw5X9i*epE>3GJfYSoz0D&#jD+gMi?mf+bZ? zD+I{nG?$|4l-fDC&Le0P%^r+XZO*~rXR3qFlitsGZN%B42z}@Y$Et=W#7_{QSh~00 zkgbx_QFr90o#*a9k-43rl?GNJPVCG?Ql126x!5azgSz0(<n((ErQ`Xd&t>-PCe<`^ zgG7YMN!kPV6mssqxzhG;pxu1WH|xYkdN;y@Wd=a+Fkv0BqO3}{HWV3!N(Rn&T8CEW zMItgj;a5<%^QrNPEtw@zH4XnAP;FPG7%hBh5WrxLM>aQIvX;bJI;$up)q{ab#CS@j zK5uv0{-rVg!(Be7#gt(`{3FWJy4!pvzMpbBT){`%9dz$pH#H@g3s+Ci3vKL8zOOxo z!+bM0M&G^^;$euGr~Zs$4lm&mPYOhXDj+YDnE?c{Yth~*TOWN#>O2s^IU=2sUQIfP z^S}X}^C)#2KGmy4HQ$J`QZ{h+k`cb7N=eJ9t(?r~>#&+?%5q|Je<5|h^ff=5tJ=Ol zo_DL7Cr0>g-N|ifnj@_>Kg*qwC$<AuIiKy{FYf=ExGv;zW@2!@?PW1PXGY*=0>|5y z;))2nR@q#DJBf=pKpOD#j<@A%3c<!3Rr@WA-`ocji7<RmbbqF=kTMVD*c~qy%0iUH z+l8LEhg6V*K_khBQlUyfgN-L8jPj5DTVcOBJq{;*c~Z~lk5G5xyR$>}X{$}kj5jjE zCvov|{u1OAZ_SF3ygd!B2>7U=)jfv$JibB*IJYS^fUl}?Mq1!pe*BRs*}N<^qZ5WD z#EGv0g)s{y06jPy5gX~{#70$g66QK8R#<;6Ykq5{{$KYM<G7W0ry(|Su_t0hZxAYW zjj{rbMmRKxEglu%fxTE>6tuDf$YZQSE6OjTbfXt&oMbH@l&&pQjVR4;3(n2X)M)2z z^PxFvz?atG(DVI7yr8if%2gTKadRPsP*UFGDY{{O!>Ob3W8iB-6!g%h7i;6{`bxrx z^)xw=8LPi&bKqpr`1Ve<-|aVkw~eY7Pz9WfYOY%Mgm5Rev!=)m<bA7MHPpys+i?L~ zEd)l+n?eT-c-<Eegz>JUkgmK<WhipcE1a%+cWY3%*q^OpAH39tzcC2<u6(Rv8_#ls zpK74jhl}=;Bk8TrTYkl-Zn&+R44U>$F4vO<u8QUsHD8&YlxT*vCNgc)eE7l#^}doM z0BaeVu_0;#<D17o@~f@WF9AiNUVeNbpH&aSo)d?=7C5;Z-e~`D`D}_E<N`;9YeMY% zXQ<!b9Ybn5$cj)&M{3O(zpOl2;X?mPqGqR6#rA99#DU0-=t=?8XLQ-nHTqec!f!a) z=<6L$!+sKB6uS7-QHqBaU>5wp^57LT))24JpWQnm_3(f97ReCd@TXm6atfPZNz5PA zbSMoN_C{5kOYW*+>)$;dL`2-m48NNU5m1)q^j1J=QyO0wiMVq#+>G-mwi?}yjgB|h z#R7pPb<|(bkJBIwd)t04H&^zUtl4KR36qBY`4Z>}3@+%K)YceY&xL`+@wsMV3xYFK zr%XnqPYBJ6lqEFQmvsdrtg{<fc}i`B0aPmSm=yd<uSQ8cNQgQ3(1#TO5wSdzuaxae zVbQv67EW(tmu;SOu4sV9tm~!dR^hD_RiFq#xNf~S%!CC{Ykr8Pvf`J6z{7+JNt^AP z61ZL;EQWhf;AVRb5$t}CatcZDoe@+1)&pPcR4E4^K?gHb3*1Ni8tHb$-{P}>xsH$X zzLU;&b@9OYF#l2h`5r5@+F*T;j|SGWXTE55qiORM#^miKnJhQ8=J$L|a7+tq@qV~3 z^Z4amsa-w4&(vft-;HMs8GJl#h+=9^>bLriS3^Jj3=Op-sfvnFEz|)Z)NqCctC1C_ zYOHD!{)o}U7yRyidq|<Vb2`hi&3P#JEymaNDxSA?<X!4P$+SIh2w8~T{lG*$G?tp~ zFTcp&NpwG$c{U~nXBaEK-H?MsMaKCEh2s5O833iRXu(j4*YBaE__Mn+(AAh#!AeMO zcq(HS{4Dw>gOdzBMsq3*Lk42O%5!cB{RlB_kr7M>F0?#loFsV@LkW{paa&TYhrDz& zT6#3Kb7Q@wgiK|cDsLi!D;5DlD({>hlJCiB^*3o@@_LN}HO8%K>yfE{XJb~K?U}=G zG_I~kot9~nf-G#X4n6#o9T&v}IrGNx56Ob*`s>~Y>(e&x8zd!f+rM3y3F|;g9NZJy z-QKtMZyJuR`3f}<PwpdDbxA$NVh}|zjihuE3SweWhcC$)577oK&s!?vQ(lfk5vh$n zdv|%C3(cn)?-|u3GXkzlZ+7b~1lvp@IN&Mh!8U9K9F}Ku)#mP@Y%G?4T|Mh%6&6PW zDwH+oRLMxik@V`!P5KwGH?K0EZnlseggj@?{hzL16@B*x#-+D6JAPf36SY4O!Qi#b zs6Qvhyek5fi;dwm!TZ*32&TW@x2YD{rU@*<hkaY!H_O^?Gx?G|;>V>g80uZ=hMOyA zZ+1B{Oq+p1Z$U9jTVkTaEx;Fq2s#+#nxpM6Lqx4Dml?p3DDt8!gdv$u`_r>(kTUO< zrNq#MI7uDVI25mlQvIMJx0yZ1A2<gA1-aUZV;1q>8u@Hg%Qi%<P?j{lH=HP+9Gyrf zw^1Ur*#CZSu)l(Q{O@XM6A4c&SfRQB3&d`e1!#62QgLO<4>OZh+L6eN|AY_TKNwP% z%)i?$KQ|n)U2Qdtn{F}S)iEu7h4B!VV4ZEb$IP5D)3t~_UH#R!%9QiDe{dVdLQrQo zzD5Mc`XeuDx4%(4XGKb|+D?t8^=wZeA`9S`0r|>11PP(FW}V%+n?HQybN?1!^V&;M z^RXp9{C+U|+2I2+`)#h23G|uX@?CLNHXBgxhtSLi+4|_L(&H~4(VMbNlDq!tyY~|R zzc(p1)YrD9KR>-wYPQ*I*UrvEnT78E&~0~=K9eZ<zwCEr3*AJ{c3x%pdGBKoh6Zec z>1u(frpfH^ig!_XqsC!bNZPJ`qY<7&OYG{FWT5r>2LglJ28ZY7UnQpA=f5kauSYIA zH_Q;x+<v`FF#_7=857@5cg#6t)CUk`hQw1+W(E?IesGYo%Hm@NQ_@GnLRd16?`ej` zpTX4mIadawaw_5<@iSVs`{J8j>FuDE#B<>qRdqb}L~{n<ywsG13>@6EGymO-ky8ur zp<RqE&zv+ELLx;|Z=}pGVf$Mu)0OtEQg#Epz_M1t@q+qM^%6-9lt4JiG=GW*^>!MR zXC<5y+bhwM!-l_EWP&4II)jTx%A!k>bR4|FX$D2uW&ISAVwfI*qlJ*+FdEndPJ=i{ z2^_n<U#!0oh?#*M0hsILyVw#TX<ii&(p&J8xwctN0wrc8M<OxI2De+#H~O_Sc7cCG z%GM)hZ7jb=$MbGj<}Z=kb3#F!U60T^R23AOPAd*2Ns5!~P@?%w4t!r#VwkY16YvJ% z*_ZS-+vof-p>e=##=rWi(_}{Sr{Ey7C^5B-Xo|(B(G!S~4a3N!7t;(uix&y1m?~L! z69!stR+ki4cY2MysoDR1N_NTkD4V{Hy~BOkPHntvb$Hn<(1_0JcA6Zzm^SQuZaqf) zcTw>}qeoVu(U=dR+Ek>htu0)Rif~k>>Su;g;qQ=cFG)P_&gihhlH2%3MZJc@mpr5{ zzugg@PoJHf&#M4TKkjR}cl=jDP8ZOdLWVo>&MlPXE$;afq~42#Z&O19bbR>;UW#un zRC3>t_ZzLB<JpUWAo~^A3B+5hxvwI)S!+TABWqp(jK~~T#htms{kx~loXwCq^jqAI zHZBZn^sg`}?a07#&!Bd|K9e13yRk998iBe8eM*tJDGD!v<5wNty)p}N@J>@vKB2v} zOZ(W<O5<1@9a4or#6bKfsz=KD0ADlNA00nru7^UJod6Vh-8Xd=X5x!q-yBXZsQ)7i zHIoZwmAJo6|I4>tV1SrwYMlLov>%!K2OU`s_<^mZWne}LiH#tno=G%D=^eCi5${+- zJ`>&}0P|n^f^#luZCO?%zi2=#Pi0%R%q8nHj?Kg!`sZ=>A(aNK+{EtRAFP#y;s4p{ z`f#nu*jzD^0wfm!Gi2H9*Q#^}@a>U+;n3m{L|LxJ2;+l0miOL?J`PHQ*#gK%xNX<o z6Ov8g*CnS4r!&TBPLUeXWzomoSlDwTzIaIOC!L6iYdR5x7L6AMu;t;GqCvgiGG^lP z+tJdGD*?0XI$@uKQcRLJ$HzS3Hn)x6RpWINA+BHL>*J;;3hT+o^_)c_Du4`T{JVO- z*xl-jPLw5OuB#f2DUCSg!s|E^iOLmY>}~4d!!3%z1mrf&ml+)%<Iq*()1DqnkJZGt z&p-m9HJ6j)Y5beS^I-!4$IG1`6&6)1e>UMWXB)n05kMN|u)k*o6-b41FTyBOWIl#u z$tg{4`hX2#+X8%_<6uGjM-wk@+-l{t!zRi)h@v3p!;ptJI*_V+9=MxML32PG6CMJS zXbv%$!ieu7l^|IT41xXA34jke&?+NyfjGyW<zq@|Tz5`c&V3%n+Kw5gKLH3N62xiC z$m_EhsT)&8X&BN9w0^<YoP5K4y8;w?43_daS`{ul%i`<|U=+D8-oc==UBqKk&PgQg zr3COusuG!fo#%{Ush(fsFh1cu*}Ok!V5ktHNLj@bNbzlqghPrbVbF3anlN;A7&t-D z!l%nuDqL_fn}d*miujhY!}h})in^(`c%{s-oVJs?NwRN|+h}^d2^XhBbpQS7h@bb7 zxZ@O*l6aZPrjm`$@HLyTc=l_)3u?ChTUiBtCxIqCE(oE#V3(P`Tp66lDwWjkT!9d4 zStQWb>LtLBU{L_@?<UCy4eTh>yM+#kyLPOmJ})VekJ#Rzgt+Um9&s!ye5{JQuvqDR ztW3ms;Mne0Td?AhWh~;AkLk(OC*@*vVNjh^d<OQYYGYjSUxSK?UhCRim2T_1P^WlL z$Jla&D|S1(3H~kKX4I;CA7b9dA5Lkv`htx~t3d9s86&zdC~NGS$WzLe1jxpU3I+#( zgb14&XKdMfi}fH&jB?BGrYiI>xHgNX!&xu_>mL5&{|vVIPN6qLJ1)bZKi*!Uuc46* zu57eJ`@7PX5~4VA?};EDz8A6)Ns7eKBjciwisV~C4y_Ll)vN>(DSWz(H@N@gO}A-! z;+ouq_Y;Sbq7n8v%`Gn=Hza~~^+AHBMo6er2{TZ}G3Bcx408>jn%Hgl7gv$53j7U- z5ELa!J}P$=wi`Zd&@m`eyp90F@;#Wacm&A`aV_n+GazUFO&^g1AO{Agj%mi?q2eLw zDXt2hv9*V@MPII54BKiQ$>Sb4s#zG`s6f5C^&6bK0m6r#nW7X<J_D}Ni*`f_vFofU zuQl85(EQZG(o!X4Sbwm_tJ42V(Hf6lOTZ{)MpT!ltMn>*KKuivNgj!_#>Lq^@w0~p z?EfY!Nj^)X(s$+D2qAwEDw7E(s*r#53bc}sNf62txsOGM2)~XO*Nb1plb9_pB&Vkg z5?4GoN9zv5@m|+=^_nMm<@Ub`EfdVq3J$@N=hMm_gc*2-pO2fA<<l*4<SU3T#^_3b znbG`&hBS9n+-osD+phK9zB5~|_C{>B$=LQji(B^b?^*YMexmV4-M?7l(tXJ1i$jpo zBx)wQLE{6@E*7aG*JsOu(PYncbCq^Z_u{pyIH>coqa7(r&AOu=SP2xZFzu%dXo~q3 zGNIdPvU2C@PuRBO-mLU7*Q)U8@lval#rC$I+Vrd2=8Pyt?%KMeQTH*S%TWQ4R*eEl zYap3yyg+{^1TyXug6YVG3@gp#upNE8%(_QSjTHtv9ft3$_sFZ1?B7`jRoL*m>I0ea z#a!i6(Cab0jo<;#M&KT}%-V8V0%tWc^iYkW=uG~*Gg__^-|_ENop4N+KhuOMLU+f> zbs^BA#$K&BmcREG@-=X%>oG<P*fM6b@y40cbd?`G<c3$*R#;YAM!baG{e73DAkzpJ zSB)>tB?lbScKVa=7uAnpsF<hVLdvt_iu{M-xu>kUfAuE`H&tggR5Dq3zB?^MUj52C zgmr*Bj!NVrv>J~E1H`WkA(m0>P!Qk(+>>wG>nM{fuV)0I1bbiu=#wAQc017IabBag zM0wKCdLu)mU^9%<^s9$KDm*leM~ti<>Bw*LBw3Q^QN|Kx#4<_&hI_RyOX92s!LB-l z7GWlCU9Q7-TjDsILPp3<Q_T0e78g#e6VeYQiK%^3A|g>>G;L$WB=*Z0CVJ6>aoV_~ zS50kMkIVFf#UVZ1qW9wxRmrdED*Q^Ht?de$!*QcG?Ky*W-{iZTXT`c&0iY57$Isa> z(OyS!AXjt})wQVT+yb#;#V;Fy`s|~Y)lKF|&rge?`yJQOTHRq5dz$O~ik@!@!rmae zZrrB}Ii0w3s904>juZ5^ykq~y??yzHy7ZAt#ZORQOsn};;aCwI>H8{O+plu!FH2A- zpI4GG<2pKvr$;m&Y@L<LUj$Wf-!^eGkPXmt#q$-J!2mxOs(rRFVKl!Fn(<ci|4*@i z5Y7aIdhC}<v***H7pHNez+=#IQo)PY(n+bM%@JjM@Vh!DFOzNG?39c^Jn2lr8x>5m zC!OnP>?v}JMGHR|$JF;x?w}HaN-oB%_CEl0Zp1ZCjA1{d$mRqo_9U<}B7YW$gxS_U zWNMhi(3%)ZSM*5C)El~dmZYk0d6SyNYpYr&mP*nZFULhnP^i|fBHOd`h15;ao?;@Z zf8s(0z&osQs7+3h(x29rUCWccMT|l<KfkSkMwTD(o>5bko*uF|K6$;c-SBs~L>wu+ z5cP%;!Ug4gbrIVyY5a>g0dajgc8Fi_$j4FlqL^SjaK5^m*OleIZmM;ADE4Rgz2N@g z@>q8G^0~8C=ON>>Hdj@f<MlH}z#)bUDpg0aMHZW{v6T73+O0(AUJOe=m8L>nIqPc+ zqFF{1DURKu(A#t9Ix=tCW$*0x^yj=Sr!%N_ungXSuldG;Cwh37SV>VEXnjlEp<CEv zv(&VjCL5ik*NMeR%1Tfv1##W)3AVBwIjnH_^rjFDRr0y+1l4SC-rF6=4E<=rEqLq4 zvbV5$g3LxXUJa+;x6@S2@WidY9J2yGr*gQa#9^()`W{Z4r^n?O^=nQw>5COTb6QMz z{0_D)W=2m7k6E^gpV#NNmLzaqd$w`^$zwg(e3f<+TJ6ppJfabqH4K-|tBRgrij}~X zCuU#VuNdBFYGcX$^E=b4Os_*qFW<N4SfjpW@q-3#3R98(oSdy-AbFkZ!?*usW-z1+ zrnSy7*81dn%ayeF;#Y$K{BqcC@O7$)3!Z4WrX{-WL_NCpH!#Rkye~E5I>LT0^Cah7 zcS!H}{!a4)HraZtrjhG>QOlQDxRV0V@+ydNQZ;mwyS)p>Roc#!xVA%tU>Z8R(zt^U z&k=j<0ZRpGjn%LBMw-vnh^H?TuzqE!^RHVow%5qOLFl>8{nPE5#!GnfHoL6Fh|T4h zyLvSNDGL_kX_?qW*0UlCS;yoP0>K+Z!!ZB_t+otW5&pGxbQ{X@el|J3<$bfVKSg|h zw&ju0synzRr2Ex?O85a<^@xsum?$nfrbxV07Ug#><pfsg|8`GV9DM&Ma~(}-;*a@? zbDfeH<5aKFKJ<Kw56!(ebxQkKv3^xfGQ}F%i6kj}@&8d%;cPvHRa9Eo=@PsRlUF5n zN{ZfgE}DwEav;;n8A}|aggQu%y5zmmXxkXK7$_p0c)5P-b?;np=1?a^$e-s8CneWp zG_V`9-ixOcv4X(o&G%uSm$e^)Us&)3yyyF3><j2Ve^vKNB<7*MZ&R~Cg_(!bA%e>B zYk^}db>zib8G{79OAhN{7QObwT~Eh6@=rY^*#aNng7rlfxDUDR(CkNf0$w9**~iz( zo-rh{@<e>>#J<8=8(9|*)<!Lx6heeA8Eix-|MkYNxbeD|BU?5+?ZgKBob7N5GyX`= zis4w^=Cvi`UiG`^i}!!!PhI-({xrV};?GD?)USt)6*q<`3>jK4`|>SV&5CX=5}tH$ zkXkp>l;7#_kF>dMi{U{1)fNY4hV9MNx30a!-)R{rZ^K0-D(n=uNeZ+mGv5{2kqc&@ z!yQM$`(P*|)g{SspaAOSJeKl!W^GuD>GQ?Y;YI?C9@qxEKi=|&FSh(0FPeNi<)#Fc zDT?@11_#B~GMA$mKg$18^pB&<d64vS8KYFLf9oW8oKnTjljo-WlQo$<Nrd2higX26 z<8xnL%_$cYr!~BlF07R|xmo$4JCV~nEE0+ggjQy>pvyb2(NBl7jdkSXE%;mp0?00H z;tw?j^r^9Dw^CO@VWND2_y6dl6B3en4e6g0Itx2`7$|n5A;j}zK4mN+U4W1{BDJm$ z(KO%oPorn$npw8n9K1VblhnS}oXz3ya&r{_Q<Gy&-Y+sNII`_oT1|9F4^Xpk+P^mu z%=uUvWU>(EOw_fwq2!%<IABRv7b`D99Qua?U5@5%jgD*?8;LT_l*1DEKNGA7a&10- zAUDSnZH{TkR&WjO>NTX{hxG5^HPBzwiKk<rRLA22&7arP0*|1tVSa{*7r~MlsrD-| zpl?KB(FaxIp<WS<^VlGkDy(m|PG&{dL48@TIazI9#|kEzIRb{4!!?-!kGLO$e<z>V z>?;~^Cn*;XSW8Xnk(VKOjV;8BkQ2B|@rMFfmCt7sw7?Xg9VbdtGCFS&-Ij^1n`3~Q zreKM}FprBXIH{dUs5e<i?$jPaAfh<xRy1I00WG78PFG|Ud@`IQ+C?r2ZY+cgw*sGq zf?-s|NWvCltnn4eq)uJj0nRJHun-NB6B%CE1gYTl9<kg|*ERa@Rj$_2nYeB3&N_;a zEqhjb%Q>@en#X@WcFgv>-LF6ux*B_;tn!3uLaY#c?xR&bbDm)%I@S2FMK!^i_#vVW zKvs8*y-a3CdE~zx#h(ShZa1S_Z@<TZc0a+V0`OYoFbwN`JazntY7T%%s*JCLF8ycm zH)0$kL70h4t1Mhyo~E~$NMv1I3-L}2ZcXDID)j|uC(4NcJDT-~9p8-FfEyh~;&j0N zN65qWt=xq;J3@NMacOw_&Fk7?n($8+!U;Zwd1XI~sJZA^sY1jY-+%|lM8KJGOf&=t z9G~j&_3j0a;zDZP;Ip4Bi)DHOxh8J=eOPXo?_K@+kZ~7Ye`j(3oY$vLwbYq?E3`fP zQNl5$XC(WNkxwwBkP00MA3IsmzqdR|rC3g7GkM;;j9NU-qs%622^H#u<taDH6-?ZL zc+T8^ey{pu!ja1M2<S7!j*;Q>Gq?XWm1N+;&L>$>))S_p67HHA9YjuHO(_7B#sjuH z+ph>uc5id{?}YWx)MVeyhaKrJx1E)FW`Il82^T{me$Qu4(IppmoB<P3RMY=sFg5cJ z+K^kXC&u?&+llczp=uyrYvgo4${uUGuO1dkrg8nom=ddfoqK1>Y%FRvx;&5l2m=hc zg0$vkAeQf@*)bs&<ziGv|L=d^xiF=Z)HS1~4eU-CBTkTdUu_~y4;DW=qlL1W#tN@_ zn;ous;tHerJv6cYYclRxxWjD&u3hlHT8-F$xaQ0Iuw((4&iyj?)0V}c*^cDl!jK0_ z(khV!-XV69dLsb8C_bAghjAsQ)_L6z6nl1iu=m+vGW5!V7uJ65;R8C+|LAP<%SEzj zO4&Uy;ez`iEaZ~8U{mC@Rp%m;dh<QHbF|g~4cAxU?n?|^Hkb=ncRy@U@xurr*RKyh zpBO=-WgDH-Zlu)W=CX<QnNh<oA7Nzso6CbM65)ov-l(X%Cx2?w+qqXqpEj~MmXEHo z8_Cl1KUP&XB^+^cBbK-tFHZ13%sf5wR!~#IMK}+v1Fxzp&aLhO?mx-sRFe|XSn97I zj?3E*BvH~4O5Q%ZzG_QG=b%5nm+wta{mJ9NH|tvz3mko9ZGPp5It-y?d0EqvwBj35 zFl_2E(({OcnmX-!wcCDdr@4MM?jG9S;`Z!G!*<cU=;6Z-35ajjfIK0A#fig<I;3Wc zWtkd>iKJ@(&nv+AvR*pn+?UF$Oeb>RRs?o+KhWPLWny4mKUpIaK)fQ^0~qz%fksC! z>-Z^ed&LMjXB?pZ&73~C4BJn~<D@WO|J^e9q+*N!m7<UuokG^bLfpNJa+qX84508V z>)Yj!k%3Lq)i7qI2kgXP%(zmA_fk7Ep$q750Vvg53%J<P7b*>B=|`RzKkEp;$e=BQ z_C-thK#CrtE>|f;0{724U4eXRzK+XU^{o|<1b>eKDNKrffPt)H;cL<s!kuVzI2FyT zrBsqxL=1*B)T*L{?O`U4U=Rm*0-%Yo?ZpN(;Z%g8lX|R_y~K1zkxhR7GGS>kpH?}h zk6iSQC=T|Iq9DZ30HUxWXC^0jT0yy)>NW}Q!i9aLG?{vgaJMSMx=T7d@B>KJ3P#); zgx?Y0isVtvIp+7@-}4i5Qt;?j>t$FN{wUO|+vg^Ii-PTGpG(jJ`DaiS5K{xxv!o5; z>EX<i%gq#R7+>Lp5xH43I;02laUrzw@<#Tske~rf<|-GS52us9oNeCSc?%T(&#SPG zt4HxJ*Bjc(r9DOpTW+noQ6k;KbH&O(cp{Q+3KfQuL;0LK){e5dX^KG#-ep;^MXu=I zJPhs(PBR}g)w1;Dl&zPw+CvIWw|6(Bzw)89eR&zNa#wgBlAI(t`=I1Q;a=TaG-;03 z5^!D$9Xxlp8>C+UJ$=&^${drP#idc+*2q|;f(1x~O;)W4&}+W!IRNlG-~wNUQ74}J zkkz&}{Xqr~M6SQ4sRgKa=5-W({G97{XCX6b&E|F)%{fmCrEoB4>l8!Ev}P(wRfAN$ zgZTb(cAkN=j&COM>;cvN%hkY+e}uf0*x2c!;Vg2q!7F}AEvov3NplaCz3L8MRm#-_ zavsu~vJVF>2HL=(CK`>%pBOC28Yqd;VujvEFqY5}w0{V|5bc7a*~?kyd<}xN&!t7G z?{-fpC*^!a;PT7sq@yN^uEn;~M;bT^e+u_F-BoDidAn!lk&b(-X;e>J=zk{I!!3Yw zo!phMRh>K%uDKihoyCQGz4LSRp8sK5uhn;@q4Ap#-)lFE!@E{Z$@WLZu+#gLS=Bex zOb(wHK3h_W@X>LSa_*9fGyDq`oRFAJ2qwWyR^(EZ0&S#RsgzU{Z7_1)!*O*UerZL< zNoNB|<KA7TQNF<IS3HiiBp-iTE%7kphcrAuZ)U$u{H5^o*=%75n;_}-7?<jXjOYM) znbwR4oDG<3VJoHJ;87^_xo-C!Uvr*=>tAke-<ED{{2o`{ruF!5Ryd^gI@@@M9u5Vi zo?`toCafqvDz6dLro)BLvwfpUu}<G4-Ns!YJmhx{38m#HTxFnWqz6Ku|9}rX&Mr^` zI?I*N9`>emM~6kf?vzz8*_Rh$HeSm6j6pGHorAZuA1XHK>45FGT#ws`S<Hfac6YkE z1gl7Ua8SG?UME$HybJM6fVy-%)8K!J3cAMOI69|t@^EaVMY&wmxsHCdKuQ$95vWaH z7@dFLs&jl?ZzFHyHb&2$_B&FB(s3~y=?C~OxF*NDU0?5b?(Bxp@1%FJyjWO)16<`0 z(u+oktiK`Qsg)7Ifz?2clJ6oTT8FZrR&lT`>$_@9#I8s|BqNy5mPjt*G>Qeqx=Jq6 z*?5G|t6?p!a*=Yl_xiaT6L1r^p!}I;6D0U^#dGHEu9(yCi$GNaW`W#>l`Y&kVP4S# zPh;P=G9L1!ZjhmT)Ol`S7Uw1!?+SvBYC^S~7Q^FjrSkzYx@u5!PwP9bk2h4P&~eVC zM~oQ7mcwh__|WHsUDnUl7RT{?1@G$ySjDWWE>h?#Y{E2SdRGQy$X3MsB$1GS32Md% zgL;;&mgCXWbMUN$CCzD@m#|Cm?dS7et-|-MMU!e`_HL^sFI(=OGGJOgy0Kq5E!Sds zv)c+Kfil0tu<Bd@zFB%Bgte*XY21H>FZL=l?v**~=6S3ALW4CY_a1y-YjE2^H=f(w z=8Py(Ps-pisY@;=&CGVKhBF(&3OXE~gAai9husgM;V?1(N>yKma{DT6vpX)RA^4t3 zsAv@ucgLg3?U*Wfu%|y7?`>aG!2=xCuMvi>Ar{vG+Xtt<8HqgHhUZLvPhADezYRtm z_fM?9Uh{;$f=o4|<%1N*!80&3f31}8b+(nk08YlhZl$^0U`H(#Kq@`|?wt<2tf<fl z5)3t2pUoY|df&p;ZyP<7>jBed5sU8^$rP#0OeLM=R?q$$+*J%E$abM9vBm=}GKKlM zS_>HUjH>oB5n323f$a7iwkq1~U{P5-3=zdi=#BzBkLfuI1n9)S(Is95@z`f<+06vK zQrBcOdC{bB@p7#cLIn8p0b$fLKMPIA68fkMF$(q&DgatiTj2a(f`a!j)25sE_`mtf ztdB*c^qXKL@YssYk4Ja;)<&b7Br4rHSDXyn-s+B|P#bG(Hb5kO9c}>vjdE7kt?c<+ zHMe{A4;JOo|CD5+|0&5!y6QC{uwFirSG5K{9*!6mp1U12v$xzoW9<ZZqK|)~GOqhI z`n%+WSqSr)G0m;@`4V<s8Ek$8JwUO^IWg^^J_80Z+>-ZZr9QVmyKO#!1a3q<!0P^^ zc5us$W1ssYiLbg+&P<y-XNxWC+uZa~JUF>=0y){kb*~ybT8OE1$b8Q1moLCH%E5&> za_oza0q>K57952(2vbr2l~O@xfjg3^wv;X4f4=y@I%$s|4gsi~szm$F5I5>s?;bbb zFaC4?MaGX0F9H>#ssmBD94@99GE#hj3?-6J3yUhz38nzLC`UqNnvy;TOtV71_WE#e zdl&dkJz`~mBW2r+F#Ns58yaJ6f7<@zY*G#ENDAH`yZN|VTz@oKIbPD1hk07rCbPiA zILTB+iW;q%8c{Vb<a5}2PXjK4#JFGg326MP4fgq!JP99v2)hnBc6>XXy<xcVg58iA zTmpAgHszY)8)f%GsM^TI(`iSl)CH@hgB{p%?diTA;4qR^*?1!0tVr>TUP%2`z84eY z%q@e9&M(dna6z?hhdZhTuY0@A*)ogm5zxedujVv~zj$M=qr++I2$sNYUm@sH8Rk1@ zG)B34UT&ud-oo-#jyfYN;n;-0!7i)EL4H*W&HcFak>76UC^#kra<Yi&^B8emdn@OV zWAh(Z#g`YCH3ud@^QKCAhD&?t@8_GoA~N?y=B)(eS&yFgZ5(sDP4HcuHeLwAdo3mm za97A(M`M(HkhSZ@5Q$vsf>-RsbWf1#rqv=KNfi@&RB0eV(h6%S7oz6BB9(fyKe+o? zv}lwmFk~vah)DmkQs+Z;dT^b(bTD=(50S8=D<?6W4H}gdBF)S&wO&IWgoZ126=BM+ zArhr7Zq5+%Whz1VWt?5IRvlE6@{C^@>7)cQz1I`XykZA3n<!^6q^oR8v20-QZvyzV zf0lEyZG~EPBL;4=Be#rSS0hRLU$*@Kr#zO-C{p()6ULiBCiGEs5Wl-8p`LWo?92TX zz$WaIQ9yZ=WG4A{2BU|K#|al3cTi4~^>9zwIkUI*ZmfM;fOi4ODI3-*qkZy&x5p7? zqUQb)U9m~+x}FqSc}Sn9!iD-+Lj$7JpazD1NGEjE|4bXX$Sf!-|82G8|7WW$km00z zo-j^0+<3>a?DY4#do1h6dKdd1b`-mRoZU2M30(IJhJI`-U^);q66&JQP-zqCF~ksq z9}%*y0{=g{-YGl|@Lv~A(xkDC#<p!Wwrx9^*mly`Xd2tL8r!yQ@9BT7wa>H9+2?L9 z^2|j(%=^9ikqVKC?~d59zMonp`7kc-5MuH&F#5hcdXZQ1z3j9SbQ|^IzQyiKoXraR zu77U|O$FfIm_IxClbu(=zl0e(QKIlN1^jYni0l{=1N+YsM`);^LCgwsC{I7D_k7~R z+n+2gel&DuT)($hH;yax%4H@Se1GtiQTYf|)swhRL!<I4sqmcY2X<eT#tX(TBkfQ{ zvXYbj(l?6ZO7RV^#z8aBM1tf&Men6fF8Cb3?oB6FC>LqTv@WEdray-Nx*Z;Qxa1Rz z6C~*6wq5peFk@M4k3HQ4Nk=-yF}%EJAU1N1u>GSRmbh63@lryyV418M!UP6V{iTJ% z0JN9K)?kW|h~*PoWx}AXskHP>TIR?!TJ+~dqC`_V2|u&d#TX-pl6c3S%s9NS*lx&s z9f0%7h43QP`f>r1U2NHUc!E<hZM7zlV#jUFJ6@>+gpgQ=5@aDj(G$?QDDozZLsIAn zI`h2xX}nl{uV)><oS$T9m7}%0e#pxM1HeeiC~C|A<~~t_MBxiTLv}7wl9P+Fd(l-H za1cMj9%k6bSEGQEE#tc`pTXm&?AHMvdF=aG<?lXEqfAS1x|~Lbo+kC2W3bhNeJ&Me za?~9dX{T2CwA0g0ofuFEYwD{C1#SNmZ2<0?NRLyK9#zUSx>F?B1>Sp3Uw=`7K0ENN zI1ZE3Rq33xuL4|h-_I=aEXv!HuKsSnjpsYaiMP4^#>5nR{pLLoyL&8>w+if{K6p(n z*#cTRK2m0MIhkI@Xm{-=ysUQLClTePTt1f(U^wS}e*tyV)o#q%i=%Tk=FzU9!GknI ze8Yg|TT5(DB%(3%k^bzMx48~?M{z$V6B_QnZL?Z21Ec`*p%e2syG;&bidAA##UTF@ zQj>pI3fk?`LaX}?Ce(^|NT<hnEQuOO5B9AZ1^8^ek9!^P;(u1yo*v{S3rYwJJtr1j zajvU-rZ$CA5HHedFa%E_tt*3K-LW>wj1gm4$y%gnpf3jG+L7padCuLKv-#aeLT`20 zdQSDiUZvHT&5r)~Ylb-AJ<~9nl`u)J8m?%Ps2+f<4*(Oci-CsHHpjGGpJVmAyQrjT z+Y-1NJBk|+5+OL@y5C~3<T|J-<=~*pZZSDqt$d@DT7m5=&on9AW^SSPKfTw&^nyfL zQoR8#2||U}aAX<ql2$4IPjg*!2_Hs=YFU!RJ;^8Iz(Y^(k?SSUaCf&^8`Z4GV>IHY zxA~esm&3b5;n#OH%8<1*e^uLB8P#NvNEhtC#AHU$^DG@gBW_qmUqdMbNINfNrBv#7 zm>a$r*>ZE3De43&_%6NIh5D_IFte-b4>m4mM4wR8HH1ja!~)o@lGK@QAjiCg&%k0x z87bE(Yn|+zIlG~~+UxJsvGS)F&g#CGppW=2KsIKcc!UqD5s4?lI6xyapf0H=K^A>Y zsZ2K_it7Shc5H~QYH$4;duSD4)n)>zYvQ#os>nNhX{cw`pTYY3XjOD+i4#%Mh(lPO zo1h$zA5e|K^g9h{mBSS`Zl2u!F>5=Z{RXtKjY9u5%T24dTwLAI$m=m4!<ud7jXJa? z(dIs0h%Hr@n9dk?s~N_K;lkN-B|=$!8>)Q0(4FI7Jv(Xs^xnqxyZ<Wy)GwReWIN5z zM_<m|?3X=2hH+YcpskF2zo<a7Dev5svP@<clt!eG`DQh5u?SZ3>!46X4Jfj-h-I(W z`r5^Xm?dl30hi+q<m9p7($lQ3?xMM|;`n7-#-u6tWX+##ty-VAY|UrR-a?Dp`?AOb zSUIC${jSG8By3(`S6Qm&WffKqWv*z2KIz^ov<@`Ze%1mO><O~J9x{vnRlsZkFRRU^ zpoP^MgR@7XmblC-PSlfRmeiE*oHCNJg(j|1V#z29azd60!=zmDzUzOdv}b`y@5iGC zt3N6fDgrW0RjvaisPkfz-`=Os2NG4WRO?_AUwSF1Q<{)bwR28kCNb;$XHH>t;^R>; zq2}k6u<j$}A%+C}g!Ae<A%En~&87vaS{Z_uZ;j&NBU{TRGue;)q5-Q`IGmOGMZIN^ zKQ1Ej2TjpQPGKm=x>@O)C|Zb09B|qgIU`6mu!Hu0hxqvKM|0E&3Re?oxaL^ew9E;j z`+-qCZ{6HqertZ?z=zLm6sR+Ut<LBC`UF;6Z7Q6x7kAA!&9?zXDNR%MQepYd{Xtdu ze(*@#jAib(<#_-mqULo05CQG17a`_6&YMqKah|L?9>HfkEzdXBFP`siLQ65aqH2`A z#!J)?Borm}+~X)nxnjk$=821$(fQ+O>T#R=yYIB;M}C|gJ!)8}sn+3vAFH_|M7zmc zi(tJ_*DZ1Z%MiIeF0QW9W6fE%ZtH>5#(Mj6d5xFj?6VxUmQ~A}F8lexp~l@es#TzN za;eLLLG?zg!juCFI|%QRokH`)*Gu9E-he$)9k%Z{cs9>DQA1sq(PnsC6Y8_uKj7Wx zi1{xM*k+(lo#OfBWz8-9U%+FgDeZ;O!k)ka689*QpmJrIUZS_*&ODEV_siG0(%YYF zpm|Gw{Web4$dy!Vrojsgd@kyJ5Rz9FO`2r{<F`})4+;H_&y?jg-SyGHncH;6XXMhz zso8H!M<HzG60Ex>U&vX8$By3q{jnk$>?^_hDDW*LR&mMiS*ZqCBIUc*TJZH&mF|bv zSq3<1qVqzy{i|6uv5Hpk%{OK)ppH_9$16bT2op!Vmgn)uzzgXTg8q99kOn<mitE1l z`E3(0npds29F3bz2ish6eMerTUURL?ZCMu5*w0w%YE)>la9>s!hFS#AX2pvFS2Z)= zFaPgQr4|1-6#dUyC`p02(ECQ?uLU?<_HLh?y$LXV6?4eHdUuQOH!LIEcQ3^ZUrj(# zRHV0f^8lT#NAfY*+4C-iDVg2{rLYnZDRqf5m0;1e$i>9l68)fa96<8{m7d_;i8aN? z1F%U%4QI^q7YFlL4D(t%@{8qPB{U9f$<u^MJKv_!gCvxe^eUR-<d+SueFc7KqQ6Qj zNI!<amSnV&)S+q*Nu>%LpdwQb!L6b@h*wK#gPn(>Ikxw}sZc0il+=7^wRa>5+)(qq zjNc}2`(7G=#Pr~ArE^~X7~u3IW|$d_!d_XRyHKm1)awzwkfAVZ5yLEf?zYgtp1=5E zPhF1prb@FjjJC^bwoQ`8t!kx~KvUlkOCItIO_}?LityrORgyozthXUh#q}k{XPMjU ztmJ?XKkX))M%?fG-jIOj<K(NQ;Q{ff?JbkCZw69gCK<WrL$$8Pl~;5@t|G}S2vp0H z(-23YK)fD9Fou@lM_n7Q<{;hae(Zy@7cFbK(wK5>PhP=_yaY%~dLROi)8}E-cZTQJ zLAq-GlYI5u`|Vl*yz2>RZ3gT*gY$&ScPjXup}(~f3+eDMltLE{R`oYUVnwlRyXYd* zm*1v<1dROD*6h*9pniwpsrB14#99qf2Ez}uIYn7xFreU{QTV)4Mk#782}5O=bdpe* zQ)ivcK1vs4KA`@HN+SA`8J^5H|DfTYp>_->)v$nvLdd4)o54gENZtf)?>ZY#wZ*`{ ze>2^y<!!%^*YCzGD5{H}D~%t2vN_Pv+dNVRzCWJRNa-_k(fQ`>m}mH0qhB%k0c$<K z+!qD};B=m^8{lc0TS?4Y8Ld&J@*9iyyz^n_KO6NF{~Yo5O+x9mK(`o;b?rE_6Z?FK z<{DDG-_LNoJM_HC{kZKs8`@$qJCNx>H@Xs<KE@N(lPKSq7g1``#Gxr%0<!s}U_gX* z{eMGPz*++{)iPFeX*ka;05`~S98L2jZl^m9kc51hu-Dy;Ozh$Z9ESUK8tj)o$-EgC z0Q-|Zt|fd%O3sm>I?aN>O8FWMDD#t;>6if!{R$+1cBPxo{{_rLnaX_y9D2LAxZx>Q zK25h2?XW?=**8&u)?~ty)xsqjr;U{zHPB8R&bhq88Uf%H79^Vvi^Q6rXlb_|2Hsq> zyb!gfi`@2N2Y7d_lH}%QT+U<U>EId-77XYDf;C3^vT{^duzIN1&=?xkU}-c_c38?w zm=dau2DJ(mIh*x~QC9Zg)iC<F3OvZm4F+;NgVbgaHS<2WZM~dU$8b}9_7<tRY|Tur zH69m27BVZ@!(i%mrP6y9H%4;Mg$%-z>R@HJA;=9CWlZ9VqmxhysF^!Q)7azkgv)hc zD4Gt;aQQ6HX}(dbpTYp`xgdMdbOuJbUpF6Gz_Q_J_D)>4-7(MYkBAs|1T<r@LuZyX zZPh{nU@I}1)A;!`mIf&T14<3$`n!{eb;{gpWxa8?_uM?tzWOzKn7qs#Wo%Wm>fejK z=V~~x2rBU7sIh^*#Z~c-N_kBP$jIq@GS4Tg>4lgGMvHd(5vy;@n?jbozdCi+*Yi7Q zVhzBR0^zo=a=byjvAn8Q97%)3G$1<VxKYqyxohzt5x5Qd57Rx-|It%@|MNV<hn*-> zDMd%GAjAJy^3_T(m<ljovfJ`oI!0giy>{y+xV^RCdV9SRcz5%|a$5D7%T4RbDJE8k zViJ}py#`}$tV9`#DBoVA7K<NJDo?@=y)!(tUG|w<R^93{eto0I*~52mhzRxq)pW0} z;)}sOWDuCY<AUPj<~ML5zCP;V!7+`1#=|^nMALWg8w6IK`3%rF_!>`r?;xoIwou!S zT3UM@2@e)rb*gIJ#3SZmSk=U@utN5P!wSlg2!p``2ZiW)qt8346#@r?15%Z2a2VK1 z35m$)|5YrSf4f!#%~cz2!RxUKJw4{ZbXa{aj2RoEUF{RM9;+$z0l!#>I;`0A(Ck-w z*W<xBntQb1EQ5Sxi56U&q`e{9Taq$^+-)JQq7Q_2z~D@-Ho;*SM4jZ58Mzn7Ta<88 zi|V0#+y|5rl2ZH)7JbBGhCrcfT@j~cJ=LZLRNdtQqo=(jz;I>n+C5RG>Fkm)9WVdD z3<vdgecPKV7`9x$h#|?QryQ)FqnG2J6W(kHrp=2Cd$)9WHM?kP(;b44S_ZRHhb&$L z>S}FHht1t)W=4wd(`l{WTW0Uik0l?!n|@G&lZX5CZ)S*@osQW8u|N-$fEkOW772A< z<Hv$!JyLzI|0C`kJ2Vh^=>7j)xHJ)L7+}W{@7t+V+{|`Ga9K^4vlN)0z_J5<-YLHM zmqF)DUx$xpXUI0$ra86Co)PHfS`a;B6aA_`^lTL{MUnB;V&w6xR#+G|H_A9uvA_;z zk*`ow0bc{4rjavUjqWzEz8j%mKe!sGF9vOm@G@$P_1OB_8=$*nUiTus#M<Zit0owm z-K+td5kz65QErS1T~D?H(_13!jnQzh^mlo>3h9m1iLSSpIw4^@Yp9brjxowOeqHgZ zSOwkRH^AzYE=Q{^7u_Si&f@l>CC~#h%VL!N%kYHx%?f>q8dAOH-w6+)<b17w65LFH zm(O+2=&=@yL?imehi(A_7$Gz7`v%$=1uxXH3tOXsQl?B4OAYM$k;)x7XBY)&=~F#z z(?Kff#GNes3s-OgFs>Ai-{yC{D1g&rY~Fwg3kNyON8IBYn;<UGH!Bj4Fc4~9RnTZ) z;L)U5LtLDi$7&}fGc)yTXA^s?<qyPEns?h3lppJKG;U@JP*a?p@M2QLVE}mxS_$SE z@I?o!eQk7IwqeP0-Ha`)7-IoK##pvJF~UlEd9~7N?pEWE^?&Df{#MEe*4<mIdOuFK zCqAgUy!isR(cta`aiTBX%MWlXNW0v1)GY6PG|JS>&XCz<HLTXr(B|xlgl;n@rAnp5 z+@nSUoCaLzKWrClc-MT7)p2aBF6VvLS_!O+Z9Z`%iF+EW$z~HkC+f-Cdmq4*%^ny) zHKGbgOq7Y@-hx;zy<C;YeW89(`kUvSw!KVQU6VyEE&VTiCTD=7!yttFU-q7)m4axZ zh5VbD&7jF~OT%Qf;r?H$tlHO#W0<@xzllx%z=ZZB7E(*~BgfDGZUaZ9DkLuZYe76B zm4{4JYb3QJL!KpG`^%xSu4Yd7UyZ{J{~0W78hm#BQ(honh#)u#7jcAqLtXkrl{I+B zz&vFu;Wj&NFa;W8N>}YwNt`~{&Jp62kNfKD3+^Lf@_|M4$l&8NMyR7N9U<(VM`m}~ zwMxi@Cu1DrqLoABTR0c)6fWnJ^ihoW1aOuv+3*)I2h-*>uG)`bOaO*(0>*T%s3)NY zt1PCqYSxlh=*rdbv%2xlmDW@@hYJPna{1IPuK@^Y#H#<}&GL*|HO|*ermobbrJ9j{ z5o#VZ)L*vo&n<Urs5<|+u0XBEe;Z*5{St}Gf8Ka)Z!gkassYcm5%O16K!6&86?~x1 z(n>Qbn^va^#7j8^wr)iGK)ENn&;73Cyn;=?;IPmA{FELt?8Ue;zxUGARl{Dy>+8vZ zd*lA;PyRZZE*uZEfj_$sxy%J;vH>-_sw@on<tB_jok9O@zGr||_fy_1H=S0teZQsG zVv#hC5wVoz7iCQ3a*m`Ry3InuR|(9aaHJ#Him9jsA(G6Wq{tHDf(TNYfP2nLgWk|% zYjV_kV76?DI?6%ILLK1F_$ih=7aG&6lfwtkaB4ds(qb>*cdu@j@wzsQu^oS1d}J#w z9QE<N<)%2O24Nd!?O1npdo768f=R4YwFU#Se$PcS>m}$Loq(HbKgAo|+wf+;uX}$m z?04fcpwuhs`!V^gF$*k-+EyMt8q)~A3O0|=vofmlTg2Jz_%8zo$i+@$v46jCW&g)7 zoMoqxP#kzN!M!Jp=wc+t(I%_YiJv~-p}j93mc6vc+DM$w$$Jq$;*?cZGc;oGEd>0~ zUo_0$e>cD5MmH-Ioc`9x&{ITC3swc&d=HfI7AqVAiUhW2WvEv<4;+}Wz4gY`rwjL^ z__9rh@s%r1v3Gas-0g;av(UgmoUBz=OGsMU@oCWs&^dLhx=$eM>n){x`o3*N&K|KQ z9SAYX+G1k9OR5<4pRAY+Hm%~H@Qb`oNFaD0TP9Hl`hM4e9~)=AMHATZ9@s2jKnV={ zlnwZB9K+7ia%BA+#qZkR;>3i#cEU|#V4I0=>VIvKL%{Vt&)sBwYczz>hu@MTM2Cel z2VV`94B)vmt61eS*v-)2;D6cZ{_%d8%l8^e0N2yLC*K8NuG3tv8%QLt5yjRdkx+&$ zr3b=9+fdjxtJDHDK!PnBj&iCiTf6uR10i$e#vRli2gO&;Ph4(QwpYv7UIt}2WtOFL zZ|~RFu^g@E#7qi9FFFMAo*ZzS9D%bLE}#%r4I^(6;Bo%EcyPv+_I-X#HM>iJD%z|B z&DBFzyYs9z9T!;bbmIq9wdWU5x4d1?6gPCW&YYx8(l$W;@~>TcX4B^P%}>i3JS8(2 zppZhXQw)y1dH$I{vvm&<D1V>$(2^(M>^AK^gkO6qaMj3ghTR<@_=xW6@cQa)Tzh=L z=ln|huQ3PR^s=;$sM!26AZS-iMnN2$M1%!uVGc-VC#vDQ@CfH@&!jaawwkBAwE5s{ zvsttz3m4FZdKsuCOt-?bFlDNx6hZ`0)E+@iA+xEuhu<!6Gz!+drjc@xMFcl=SL-xb zo~7ciiusJuRxcDcaA({J8Jh<*$vjCk{-P03%h4aOd5Xbk?u(GH3%0^YAS=y`2rC;A zNm0!uLV(SI`JYEpg{VfkC;=>~%Sx(5F$4Rauy#FV;)Z~YQ%<wlEadbXp&BF=$K5SX zX|wdPW)9N~fZd71f<yZ@2g=PDo*UDR%o^56jRNQwyPOi5RT4H+#m(p5GwBFu^{sBS zD?ht->KY7R72Y=UxwBVR;PC>|qTKe&N?CJkzAv#rEppG~n;L1S)j|%YhHN0{vP6yJ zQUFs_vr#IoUcn3S1qOT|TtDT!g8Sn?O|`TFK~%>CEXQT<eCUg?2WYqdXI%r?*Oxc; z*5~~RL{g&~N27>v{7=@PQ}oQ6P7oYc9)l0`aV|p8emed|KPEN0t-7b32<z#OT13!l ze6M~)g5J*;{dM-2rj!Gq=+uW>RT~iLW}bvBY+W11>N!z)LI~9dn;h8d&b1e$y!2(b z9q8BPdJClIKE-kmjXhag_c7Axa(~Q42p+-qaIX|Pw=suu1-_)o0E?WdNd9p1;ml>F zoQ<Cn^j*s)%{`U@XDg!Lp#GjT?KR6>QUb%V2Tw;;B09zEq1Lq0WiJ0Nt~eq3J5Trk zG9X7RB3XPVOYhJHr3Awix~61WX`a4DIy_m>Ip5A5+KJ+*p|v^vWttZ!H$5Yh!kxJc zvC=AF6BY91fR5`FbJP?^HR_aD2>-`1%2>fPu+D-yowZ3KuNb-v6)Q=|cR>nbt)!|h z-oTZxoUgc1F2;AGm8(*Xw5;Vx@0>$`wN?MsqIntdeRY#$%Tw+wj2`zZ3iQs7pxbOH zLw7kMG9UfhubA3rE5E{mlw^E-L3rmkvnPS5pPk|it4=S<uwRvacaWcq`z-_lx1Y*k z+qK7^K}UP0hMZvGmeAuQ3{{O^n#tDYic)nn-yNuC_vkpNn3xz)CCE@Kf!-D|zliKc z%E}zdE*B}9mohy>wEg)py^x%QawHWh=JKSWcn~!GA8JNDi$PF?#eJML?sNDyb)DAt zQoQ_+jh_>_$9(7itbxs+1->3{^JloJyFcKUO<+Agf6eTdxT5??>pA(dq}omrgDzvl zVX&O1HrXzj2g>)EnD(8{eXE=Dw4bX$W3uzk@>0&G%~^xtM3Z)qzU2(4{vlf?5`ccr zLtq>^%XW-HcEWONw%VQFHo1{U#bN5xe93le$$a|V82CfT`V**q3o_1)DHSEW=z{&I z)kaCa(K3N|a4hz=B{DI=Ks#u7W&%j|Gs{$;b3(C#*sj48z~h2W-nO|6)cJLruGacy zFIHZ!aobvWK}ZjTCP>Y&tF0@Cs{FJDO%uzZ9SxbKrZKMICQT8ItlAcdLWrakwNTPC zPe=g%-M}6jUO={cen4;ourPQ<9FQCW?ggevRLu8z9(go(N*N}2mr9%ScaSdgQ=Z}E zFF>nOOXX%eaoz0cY;&vm^w4S5ElU8$zKiFf7J?&Amk(%?_rQJJHo)d*wh<{`2ox+V zXR|5bkm&>=rHry<O<Zo>mbc;UcRUL!syzD|0L#*PY4wG7e}BoH!oMjv8z#y8!3o$% zO6IBIFMypSmsR~!^NT7zVIeRdypT0LlGH{Fq%0MuFaf#r|Ltz#p*A_)*9uf>H(vV( zbjqybG$dF?u;6VxP~G<fum-MULt!DOhU<+9j@kErbmIh10i}Y-tqu`$+a4$SruDt% zn(UaK8&SFn6K%4Nl>%Wjc56_|oSI083N~Us>PEZa0H6U_f4%0t9XOfzx)3#1p!|TX zGc)!w;;q#%0LWHY8agL?0uCCS2-I$KIUD~n=hAxB>XexH#?@!*#9~f%(Eo&Slpvo2 zkBc4;jO4l)Bw5Xw^Zhui%6c43oU<YjKylI{t(CB-o!egM6vtm`KF9`!4BFGCyq#_m zojL560U$3#aP&VE=K|E3xCxcPh7D&McC-wNy>+;pcOsEDJN2E0K|3fx-EjT<uI?Ff zIBqKi{LU}B@xm)zD7M(K86f(9@k@A8QD>7NSSC{y28+xtiA|fOTcPi?-MbPx>aH%F zBQ)#`5|#bK_jUgVZnTZlNdC@synVjc@_Wgc0)>5LQlr!P`u>4)f8V@W=GFbGxE66o z*&I#)$Uysiu^!cyo?Sm<>G7F$Bq<DPSydQ0Clg<1n1EP$Q^^=*4AA&>8z<$ox-OyC zbL<Rq2zVIKyGC`MR47)TZ|*PJe0=QM<k~jzqnffw;v^PvM9wrKGZepa)1Awl8S$Tt zTI#oOU6@yA&jb0|4*hRLL%DSQMLXZcq0Oo{J#MZGTk5A9<uX6CcBZhuP)W4nlDn{X z4u+v#^IRoEtWOTb3%cW&O}Y+Gfbk^F;)Of`ec&iNu>-`DzB++R`Rv7Tk+#hxKRn-a z{rMU=NFRC4MpSL{U(>{j`_B<(Ba5(t<?(%lH3BqbLtRafd6Zg(O@`^FK;Z)jrk^+X z<{XWYkE)FOfUyf-w<lJ@pY+So+VOcpQso^LDlv~5ognjnm3TBWP0N5i1L~`_NdQar zdY;8-%z%G@y|C-Q#mE1m#v1sHU}p(oI@f&f>WaUeY|b{?J#`wZ@3>uJPTG1tRTB(* z4PPtSbf{ng>$0`m;I)yTxot>e@My}g;0jf3s+3Hk7c9%vN8#3LuU1yhT(<-~KJ4+c zzYcJV4QRkHDD@VAe*RX2vz%lHXG-#6o5@>!(@j<|jsTdPyHdGSrPbs)@h+IBmJ_~1 zYJ3EKS^)o&aEQjYKMug^9&D+YUlxifAW<-MjYD}vYGAsnF;8>E#hnh%vne5&E0sX^ z4FgWo{gzz@@N3NYe&s*;BnB8j{Ye#*7ovuzoQo|5>RkR2VS!15IaRlBsn^lVzjEi= zej<3W_Re!;&Ai9n{j1ka^z;780v4DJ$$tVX{C5;ASpsDtLqMDE%i-%mqWvY_R@r+< zoqzWxk1+rRR-HOa#Ja`uFuEZwU7(|JUjNH!8d%H#cBEwV0x`zCZ0X+~X=1NkXi<X= zl4q-0(zo!RBS5Y7|HOzopqBo1lwUab;@i4+sjN{df?RK7VHn@+Cf`UmZ}6F!RzsTm z?9F1}y*%XO%liN=DAiY4kw2vr2kyEoIwUD8VWp*`xbZAg&_GnI-nxQ-q~wooqqok) zrJT=!-_;w;7jz%uov-)jrA$w+6n><_oFw5CP-G0RaF)@z=KZC~<KcBn(T5DvF7AHf zcJ3UPnNyaQW37<O@?M@h8!f!GVd@^0<~6c(`L<4ttCnKFmrUz+`+g@4Aqd_e$oYs# z0~6B81}dcyk^9nONxGJ3MM(W6eE%7)qhO%IyVLXewfu&_$P;O7y|(<F0?)DUrnf%E zkrXHA8v^Ir6sMuFjqx|~pt@jUAuu8&t|Nm$g~?fQNt#MEE^D$2>Z7a>|EJF@0hR^j zCz+@5^YL}B!QaI%kC~OFRotq;6QJ8%T)Z}7U}&w#@Yca`>9@gB`SffSVd+Ton?zg# z*R8p}C$O>I4twi8G0c2qvt_etw2g!QNZ9gWkvt2RWZzZ+A<J#DNP6vAE~XwPRh#z0 zX6IBAW{%40+vU1+?RJJx*MT>niz(XkHy3+lgC%{(MD>lEOW5e1rwr%U2-jSS{ZHM3 z-!tXAw3SFpJa$3TZIH&9T>zkgu~V#}!zca*qWnvP?3IN7EImrsB9W?_?JhIV;ALgh z4N%5k<weJCH}q5=H(#CI;0=iZD%c;M>Y^4j!rv^>zG9<DRtVxnMt!U6ys%s8BhLDH zviVxyuIgL4ajeW8`=R?a;`5Upo7b7INl~MH4}D)EOb3m$aAuLA$T14VK51%KkZEx% z{<$7cJc<f@VF5-}I8h0u6<v8ZRiR@4A`~KA8oM4MAF^mBR=ee;$S_9WisvTt$1uL% zh=I3I=nsvW3A=+`I@rarJ}5n!SOGOOaHd{!G5Q=N4cK9$-2|V0vcqrHtCzNXp}J#! zZ~N<WS2oZ0vWZ9ndGa2aOT{W3s-CKI3@zEPfM2VX{*1^Of6Ahlw$c@fMGA4}+A6DF z<H9B@4noqJ>e1(<TKddSda3ZYW7npM3GN@Vgj_cxy{Dy}*wYt3`Tksb?hX*G!_pAC z4OiKZE22l$Ahm!;b9?l)5s6WgUB_Ud)6#^FQkPf=fYV9KSUZ85dk^ofo8^imB(XUs zLN=Ex=C`p#dQy;#C^96~kiHy&DN9cgMZ<w&+@gF_LQ-H=LVT$P6OK>w7j>6l)-e+2 z2SFPY-1vgdqApqp!--K%NlWR7e9$r4&ii0G*rv%KnAgjSws`5xE*J3VAFzM4e;zET zuN}My57ADs*$f*}Se3tt|7ciU*dJSur0bJv4y>2S9;6ZWz6dF;=lf3g?s%nDVd*~z z&o~=+KzUVZk<&W5wx7*HcIv>eBXQLsL0(|WhGwoyf{H{>UQ9h<)Mp}>ZqbN9oEza3 zoN6yFL)E#<*KC#VE+|GhB=R6NlSKJlO=uUYkp>Y52fQLG<fSBBmj=`JgvYA1`(c?= zv?<))&tvURW+)y0g&#k;2&zIDal<}hstx7rokWq9q*a~FA4VGL$}Y(w47+9RRuk>7 zRaH#h_HEn`oSIoqy>Y`u?9J&D0`eS>EBXTfX?Kzde|H)Ne_@K*rO@bu<%G6+cP2~D zH11j-kES}|;X(&&pBU)szAjXA1$fGfiB_BC82DbM(Y>lhnrRyS-{y=~Na@&dKEVe^ zfQ0QRDQX+`^WuWUtV&h*%mfA)nuEZ?b;S4Kbpscgj5kqlgRL}F2%~o<Blm_62AR&2 zQm!L>))e23Z=A*%44O;@vcMq*o#n>xX4)T@r5+q{`b<tc7!`9Ng&nox<*f6wtIjNA z4eT}0!A-S<Dzr4=l#wY73!G<UX6<wv4VYWrx^LrIxtq+OiSUebAVCc+-Zp)s5z@_U z_A7|knttO-<|Y6JGDF_$uR;|`hVS$DeY7Kb&=aL$36mz-8~jOfT~s!XJB-;29*Yp! zs(eCv@u=V^3G*~5ie{D{(X2$3S?pBL2T{7}j9c6J8R)5eOn2vY=oGpfF!jj3Gj76R zM@NQ-bmXVi_W}g{QA9Ih)}^PrHP2Irf<Kv6#g+C<X!&7(&Sp?K!U?=H`53*P>S{#a zv=K>nfAv%_JY{?8aL`68>^-QNRpQ?9i4j$>DdG)Uj_)AMUx6kXuLeKOPzLvL82vO_ zC1I+>%sC<0@FfTP<31j&hsNhLU}2{yowGMIaLs_W^a~nLjr8j~=WiooNj2mGVsfTG ze@->C(3mE?wz<NJ$b)=Q=5o{na4~wZ4Jr33pHWR|6l4hIoTNj3>TOQCTYDt47A=5i z8*7QK5}dH_!1@$<KW(O`b~HSHwy~jRx*7Q3e!xU$irNCSZBl@VUpsrYWamm5G9C1t z8tG%v=?4Qu2ph+!rI55c*BOzysdMJf@@I^SJuJ8~7jH*?a$Hus^{53z9f0K#U6g03 zsHeb!`ND)~ODGBm8+5RBh;p(SlOc1lOoJBW%<l-9KDytH?4Xmnb{WH+A(JqLP@3q7 zogKXAVarumVkFT`5L>a8o4z1p3tfzW3sw1vPqpToXIHo}5Z|lNGHHU$7o86rxTDcB zC<m0tOF}`*Z2MY4(H?ZvSQ8lMk;O~zLc@is0A~&WRu|k@r0?qxcZ2~MUNXSG<1>nf z=Mr4@&yGuWSBL+=1qpd4?px{RZoouGoQBD<(K@U!*c1&#3gd=P3qcs^U8Mq%3J%z1 zs{getb#lZ`;t3Q-AX4DMo)Ia#$(Xc_$ftw2ju?Ay>EAk`*pWO%)<cK#W2d^7UoIQp z=BcQ;cb;Hj4K{*Ew!$Hl8w=mJ@R%x_<CamKi`jmjlN2Gc;Rt$N3Q;{G7m(?UTMg-X zEfn2~=vvpN?wWqXFiyW58FQoGay3}%T4Frk-iYyigBzA|a`>(c770TGGZC9V*K6Q( zkx=Ott@&8&RcxdgG^NY`K2RQ-yX5Co-5YgHwEP?1t40lRvt39DbOH1`<Xl@cs&bcf z3%O1@xZIy_zhwL^Om<Djxj0oVQ&7mAM@7p4NG~!CP1CH+0xxy?PIQ&O5=aQx{copF z;G=3~D~{PL4qvOjVSeshQZ3720$;Y-!-fR56(H<Ro8&gFQTmG-sNXN(a44le6fmOF zVjD{IB)j6NDvVj2cGNmD+po7^r#h@(kL)s{ZHbwtdx5)tF$3w>E*a{h!n-)Ky^{<s z_EEUBhmjU)oZ~v>PRdo2B*ZXgJd62oj20D1$gfkd*c5S@zvje}^fZq#>mX(o02JxR z;XlHg;OVUPqUTx%gha|Mf)(o_^jsvrX}YkT)nuXoh$eIlDi)STT8RvR<(q&`_`$UV zN9vJy6Vkk-v`kZ`41<hHg_mnx^NPejr9Qjvw<d#~e4Ih9t~wMx`{B3=GK}H2a(Kqe z2I?!o)eKHc$;?X_ZtxYvrIkLG&8`&g2fV`riq~fxHc_41W2`(aABToy-=+r~w<+KW zdDP&tab?k7Xqo7S#2En?_TM2VHR1qpz8m%^7%99vWjk9Zst-$$#T#s0rEQL9ZP}aq z(8s$5;1l7Dd-Ta)W~8ikin}qRA)E@)59>_(hDDU78s0?}ZIt*5$dNY+ol2}^kpZem zsSbw81(On}0inpwDNW}-=x<F1%(mT%(tbW(8xiV=!z#hmjktI1JQT)TDQ!iz8d4DU zEuxr_DC@gQvNW_TY^vd`%w@!Vp3r><7G<QR#tBq#M%#C_kz{L^*$szrQ{!LWS0h*5 zU5jrfSS&Wk>#u%#8fPCbK057X&ByvBWAyj_dNONOw<WI0_GbzY5qgjOMh=3yO2QlT z<9x?DcaI1QIf`uRg!|u8u;fK$yqi?L#U>G=n7Z>M)OuK%rASjEZbYR*ClAY0K!R-@ zf8D0?(DpZ!CgmlK7e33Ro1*8#Mg~;vXuklMEjmz+I^!HWA|JcLtNXO{IO15&@biR` zM?@DJRk&ddvYHSVZ-M!VxAw)|6y+Y_g@WakDvZ2r27$2*<b}HZLKG1h#Uzx(j9V6q zy`U3}aAxQMN)!qc6}X59LGs{nFdY)&@hl1M9#MP<c5r%Xge3P8b1H&P@BrDCYs?C2 z|4vhZRTftQCZ7hOZMk@fiki4(U|Z#C*J}yTxie->10&}Y5GH#zvCSrXI90LYBvWj~ zs-rPq=r#y*o?qjzr`NE+oIurI--7s{Fx;ca4VmvBO)_r6`d5Oa;zzIpBfxbaE9Qq@ znwwg4B!dpi?j9!MEAS&MfrWdE*<b@YPYpTZX7$LtrW#t*$dQTPx{A27a@0NuF9Dbr znG1i`ZX>g%d2<wpPa2U4^+<4sGf|LS{CI{+bl7RKbpw_LrYskI#o#aL27y&lVuXPN zs>IBuE$!BK{kW-j$J|wAp4h<*YJqluaBAwIF>+1BSck`(y9cRl3pET{?gD(21W5rk zaYZVjPHZMM*RD5EYDXH26WFY|TqTmO>6~1$&wahoON{M}UPM?3XKKLwX6T7Ax1nHe z(k55!7p8%2q;*=vg|kE7zAb`e1~q&}>GJ?(2(3uE5XU4;hyqYpRya1Ny29(Jf!<-E z5RKvj3*&{YnHmz+&^X|e6p)-x#|YB}kc<>kLj^>{Q-IF(LnosTH<)j+d=xoOpgQ@G zEPCnO#SuhCT3+zTS`9d^nJ7F9iRX=(hU49sa+2gHJl#%)lD;K4wi@D@;Ocm9w~ytE zkiV-S2HQfog{LBaN7LeUv5@fm{P}_H06FTP$Th_QxuY=9AO|0b+ZCoKdhsPG<0UhE zis)MT;k5PYOppN>%o_q4!iVNH$IJTOm|25nyEJ!l7qD(k)wt{OCs*H%9_T8#qfWs0 zFl}3#>t04F%s-9~S5d3*e9e|_u-H#{4SHFW7soa0aeqWcr?kj&v?KpaCAHQR+TGtV z(NSXFn$WHDeo-1sOnN?dl}i5NF-n(W&ev#(7ow0blcfg2%~3WjxQeKvtfU$w5$V7t z#n0&`11Ekn*8I}m;o?*Qe$=%V@B7~YxgG9*S2k#>y<8D6bbHa(gBz>aiV5ej$r6-R zWuTDF%<zT}w9|!u`iuRoJ$ZqxXKx^ALc1A(tg0T2ZPOE6E84Zy$dT@%N}(U>n67Y_ z&b-b|J*_@iXxgPLX{de|or=f~W0%uv1Kd@qZx=U=oYy4(>f@waEVhkX8rIN|d|fnv zF6!GA=GVtVDsmJz6OA|iF7(W)j8Yy}SJWprb$OW0XF?$CO)&i|xh<&tBq;j4=QYMG z6U8#hbgIN`A_nmrTB}p)g9+QQvcAU6*d6^s)$>5pO}5=oM%i``W1czoey`zf>AcOu zX08=+a)1YnK>`7!Gl43pNj~hILLM%cv&h6dHW1AY|GG74KLyD-%M`2u0HGNyIlfGG z9dG};@Figt695MNB985LmP!^w^w-&ST_(alOLl3kr{AHfkWDbizq?>f-M3*lSb&eq z{PniaTZ?znP8k)a_@N8g`c>`gyJ#_1ydTf^mmPK(I-3%OhfsT8s=dc+pKCKmzw6hG zM7#7`2EWtu2G#ayZ^?5C<9eJ4Cu@?flk~*8Sr{;307<$8dKY`^t-pU9d^bkLTFg|u zpW9NBGEba{Kwumjqud&xC$RR8gHER#63im~7YV+DnzJK#d++7oh_<&{b3$bI_)Z*_ zX>~CDX1X_2dPV)(%lMBWt$*7F@Bg}Oh>MsiPpZG3z6|Ry`n1lO7atBnG_KC>>GzP= zeXG?M1n_e<{mz+X4s*LFGsmmHH7O(sOi)1q#3S}q&{2hh)3Jvm_WKEJUx+|(E{=9( zy=w2_A7kIw?qIv!zM@e*<@uYOW4s}QH;2y@Iw(`Qr6`^Q{GkC-*mF`>4CbdFvQR|% zfosXqZrH~-%_}c8{w~!-wSvt^jN#k?774|2{8(Mj<e}keG*zC{ac_L>s9wSQ5bO@K zF&;86;9wQFk|KJ~Lwrlzn4d&ZmW2)9wut$j($2M20@@?9WQ%kq`-xDu!=-|Cw^S2H z?RYr+A!*VKO(4vXntcapQf|;r$dgFbSgGYERtc5-&pUYE^aQn=(o)7>KGVJr+q)u7 z@4C?+@_IiGaeG5O7MmXe2sT&^9?5NBLYL(;II|iHAp-hs@l_N4tzRGcZrVAD-eZ3H zg8<)VKy|b|(AV(Ze%8I}3+EY6u&xm!A6JXSt;X8}D-?VTf~IR%qV1BrVeq7-f(EK4 zUz(st8zxYtzH8`NP?$V!(peFrx$6DHvq6R|eH$N>`51hpLUPc(M6XpmDgZE(ln7^C zI7*g*32%p7;fV{l@S9jGFzhuO=X;)+V9V_=x&Hadj&uhMW~+Y$EsPacE<<A<_fby% zK5J??O$d*mo7p8z#gIH4c0`*CMCU((MJS*fJTgvHKkumYJn)d8d|Qu6+?uIcg|&b^ z6Wsj<e}x-kfJI06zEt`ivrOT6m@U6n``!BJ^&96RjiL|D>D`M6?w<VhMKbA51%r|Z z*lztDw5r6ABt%l5XYl>oe!il4*On}<<Ztw02G+YeV3u?TCOteQb(XMwD6xdrA|N~h z$-v-m%TIrCncoP;2yP)&UJi@8bM{zsI%{bjK3aZRO)u+^<}Z5<sxZgN5h)2&*p6}D z21i^xzyF#-#nOzgu#z2`rK9&3t-!3Ih~%%5PKl_J_<Nc$jru}Or!p1&z)8qLF5-F{ z;$Xr=?4$k^rgT3&U^sehkY=usw`R!RzyP&nV^{dR5vliz)#Ue_Q5$FNp;gvf1hKW* z-(gQsll}{4Y0>oiwr^e<Y1<+-zzhTLYA%|uT8(yf_Mi|Vt@Cjt3exYKAc!Auf+X4$ zH(${+&~))8oEVUwvu;|$mA}F<T_wmIwvb5X{O9ocXGM*=@KKE(O0vx2%0+^PK$akL zvWf6e>;LzDH_@O`-4MC^?Pow-H<(4~Z59Hp_@u&WjH#=MhkF%Q<7k))3Y(MB;W=A( z@jMgbsH>X9KFUlKTS|m<YK6B$Jm9!%kUZ5DAl@G(bl7&lQ=34y2sGCXANh7}&Br~Z z0jqoK6X?LyFE(I&O18eGkQd3e@>iZXf5iK@TVCGqVaH%2)k>6XhtW4_ARz{~-zw0W z%tHr<kO)HoLAk#g5R~u9)Dq>(Qty+|?zqdGzpCko(Id%)Nass`Pa?gUNTczRvA%_H zHsKJh?<X}LRpT6B*pE#oP0`t&DU#$P?eV7P%O%($dL}*G#3yo?Y!OS`_=m&Mr20E? zAxMMA<$W}bwYK7)`njI*Do#l@D11*AUvv4Gk4A-Jp5(QZ8(rRRE8oa6lEr1T8Exm% zw#ijq#>b^-pr97e$?1hk1PGB*e;4=6uY}+}MrU-NTt*&_<)%G!>MazzVY`W^IzEqL zl~3+E!vP<?YU+X-x`qo1%tW`Iau}}4z^d|0Em8^&av;+Bm9wul1*94#ZEqY@$5j%b zLwZ8sxP_Rlm6-6$)T?HU40Kr!Nc+nKWNNA!RBmzcF3=BGjJT>>tzG$)+IKs6$c^9n zj2YZdlvCljK3;F0`C|F>Yvf?(1BN2=94RZHO2iI-&JvGEI^qqwK&$>lc?+%BVZ&BC z9wc1@b!Io*TrK^;jqU7G<5$1w=6~#hU}0G=X?_3_3P3z8I9|+wW*|W|vLq>y94<Vd zvoj4T`{~9<J93-z=WQqMt2M@&m{c-%AIo<+-Z3p5yqw_M>luC);OQQB2!lxu_a#Q7 zm!_^9g0eJ(#dO$2hjSpzfl{(g+{lF@Q;BX{WT{&A5Lh)ribNuq6|b0U?<oA2b{nnB zly5iTV)g<v0Sy}r)9>EKt~;-#M*t1@2-XkqI3sfR({qUGc2xyxQV;S|^zMIre_)*S z&1*I^TL21Q2C?B&n~Vujw7BF<lPg#=6?9Ge2wEucrP^V_YU_HH0p;|*E<>1A*dFKl zdY(@&3|Who>y7eL_>8vyzGjW7p*e$uL)ofOOn3w7x0r{t|3c-TT?Wt`#Nf`+)M{^6 z-Ads3vc)|8;tYcgJE<7#Ys;%IqxcK!6euX1;5o=VN?Vz0)c24wAvSx8t2ASEL%@l( zHW148T={WTYV8HDd4J?poZ~FQ=1S0?1x}kVZ9hJn@62vQrd=TdB`J-0dY-JI?kuI@ zeTHJxH{xREutdW;h_Z;FUDK-nFe?3>mj5UqJ-5??IZ#{ES<+Z*-21Ko!hTj)SSAWl ze0M#FBE){+ys@Js6Cs3k_KoL<8|Ap5o6Kp#z>&fe-3|1^10J+R9Dx$%j;Orw?XC<G zUGOy*3HV=T8Q6-W?yI0y6F+a73X}vic`(2D{V;ldtt`M!)SZx6Dqj)&d(!REDx4`@ z7BT}v05K|XXft(k^53+Kgx`dMat{qqT;Rtg!DyOMvfvMspK0TfVdoUi#FkAcg#9Be zbtqkFb1dz)%xQ*n40aj9Tj3+?L1~R0o2MHRn{5u8`fFuRu2ml<)e+B?F1^mMb~xQ& z-4(zoa<rCY&+B@YJQyo*;zi6XG!rpt$ASHyP%+K!?vi1$6ik4oH$r=lyQ}+>zEn(s zmeRw7VY799vy~E$%o<ay0VI(2bf?NcH;Ijc?bO8Ew`?RB!lV*e+;_Snk>f^H8~gJT zNJ}ZON+_{Fa*5c0d<SDjD--01o}&^Lf#w%Fb+eJ0$l#cCy#lYC6nL|kQ~?y-ha=tX zwn1DdFmB!W_O#BR%W@lpiNJZ<aU^HQavJBhwCDDkaRGUqh{r(@tcpzS9ZoTh%pF^u zK-^<43x+&wj$KGZdkGDhn)!&A)!;o`sY(N+{Kn<CSWkXjAa}i%<297KXMP<aYG6+) zZo8DcyH`hKhqB7))B)xu)%^=PalMFHl@!U%2x{|Ve!5lpdO^Cpq6=Mv^t~p=u9@Ge zvllTucDFE5&kvF$hlF4-p^iT@`TMsV1;Y6(84oO@f-xPEHXv1l`N@<Z>1cI9SKP## zEYraDqBnu7a15jdKFIppn^5wU9U%8{tO#{_p>I@y<^R=mlb>_cnlI&$KzEt*RHo_$ zGusFXGkZOoGwRj;hUE^X&4|-t?s5Ezk?^PD<YO-dWs4%gYIInU;aDockDjsd9<T2& z5^(mEksu6Mr1)umbh$qgDLElBega%-+Al;9F(!p4FuAW^!!O4O$-@t64xfq6MRZ&A zH)#!)?-tU4J$CnrEy4F~>o1mkM``;CF9)C^Q;X#2<$ZVXK}+3$dr_+q>Biu(u+en8 z$48Ols4h~BENz|9WbB(tEZrVF)*?cNYO?F1e5;GP?chbf11Zi}#A-WSWB_HvWdE2Z z!24Ay{r#)OjV^_C_!yGkR%8ZR#iU4whH3+qNzqMp_t`Nae#%W0C(;B=p9Hm9G%E<m zOj7+fGx=*OcpdlWq+?-fqX}(R#VPb{U>0F~{2#sAXl5bCZqkM`x=iIZG41%Iu%s!A z(Tt|^{-g3V6R(B2`l$=uD2RsIw`KdwlK!&QI9Z(#$)@Lf^n$$Ei~1^4E&4(DqTX8i z<N_$@;KL}>Br1h-bPHNz`mI<#Hd~*inTc*6r%R42i=G7iXB4hy-W3v)_(j~om{n&Y zqRD_6lD&pQF?QjoJo6{cd}MZ<j6|q`#QXF^C2&&4_+CLJXvyztFi_M~r@kG?b#gUA zx?aW)7iEk&i`FvAvQ>>8b~tnIB;F(#?MK4f#n^3d^b59o`PYS%eFb?+X-@QLIQyj) zoNOlHCNN|&{J|h2VHyQpGJk^y0ET|i{)<ph-_5(kqS7>=<9GgO$BiR9dm<!<w!**f zDEmDGH6@RD(L2oj={6yG(h(FvZouCpyx`$c+xan_`nveFJHurgM(<-WswI;xCfI%^ zc<cqv`ZFD&+|>AqlZtO*u{9f<z>vD@hu!9`?R3ybca^)*;*@=Mxas&*i|_l()4Xvu zp0^28s|Qcfq>($uMR(yOe+~K}#aso%Dmn&eDg&N0pC%_laK46kZ~91|<Dtqg=<gBA zCg};&jtHWG<|whEoYCopWz6&@(`2-@M!YK3I)0J=g_$$A<A^Eq`(Al|;QiUX1#V02 zt&Y7%CTTy%wSlM47qNFazo}fko}(%bMC+^?leNol)q5<E4VGn4!Y*_UohHKPMLXf7 zxvDe?5h@4eUWCDMrKnNtz*#M?uhM#2!Gm2$i$*a&{HOytATBBW4n|ti)CA|-kc?nm zyk*8Zo961J6%@lc-rc`<I_Hla>gg#JRu{)l-5(ack?Z!&;0wrc_MyU1M~4M`fhjql zOvW=pqSN-ffKI#3>O}T;p)UoOV4@LWOd1@|(!mD;j8=0FA}x1_R$|~Uys;C7N;P+o z2uh1&(j<+FV2+0@@&yC6So8*axj%!~p-wlJZ-t0YV|z;%mqp@S>rs{CDku-{$06|D z6Zne`AUg)rFuEi55i>go^FG!m68)a$c}43r02pI>`Hmnsld}@PK}Q^G1j;7Iy&D8e zjb~Mel^(!WJu<7jtuTtPz4xmby1%9vvRy9zcCQOO42CvJvtNi6k^Uq`1Wc&>!8Iad zrb;Nq9xtP`sKAfVheO?dnO`aQei=1iS{MZ)D%bqYbG*q7T#2*4FSy4dXB;u&;%n*m zn~^CwGa5wi{qJ9vG;aJ`+p=|oS1#wfK0^<CH3=621nItKf5}J=?QGUtFuTuGr6cZL zRh3*GW{J_TMMwLUeo4cL8XR5+!h8LsU3OXuUPy)4c<+2O^ZqR2uILK!Uxc+@GhzeS zTW`cYWtmLyh2s^Y`(68IwnB>F#PQc%27s%N<nA;V0T63UES?l)7os}(Ft5WhN$aW( zhKvI|%H1{?+fE=6$9bDzA9bLr56EVcnsI_eeSA%@euwN`lc=E^NKVt|O-a&U$ESb+ z*|e2hPLVoN2<rgyLACD7UdIiJoUeg(%ZU&^OodjZxAl%~cBlzHtROzjqRz%2?5d@{ zkXq1wT%0ynfghTaUFJhux%%P20cb!{z0-B-Rd>^F56mJPd#g}$S(XlcxGr!8+S_xw z#%Q?y!I}{(D&1`BR4ZU&&TqB&v2NIWldH<|j{vZ641QgvmrN+?o{or0NiV$>9X<a6 z38$IzM*M{w7$N$Yz#lF98bk{`wF;rmm!yhVkEFVhrgRM_kYpM@TK51Ae|I0ohP({$ zfz;osIZHM#uJ|18pF>*g`U*^qZMWD?U5oB`OkZJ;*AJ_~SWC=T4er2%dp-r3At_X+ zC2t`1-&Q%Kn8pZCrt&@yHS^ZlYqBfIsA)b4YnSaY|8aG`4N%pyM@OF{b~_K9y(W^E z8Ox8-Y@*aimPl}T+yxhKf`TTkIw))t*A;wcp=iB{W9pSQ_yv?}ps;;$1S6ewP|44c z%?A{aU~6k`M8S${(CLskgy-HfY@EcwMZH-TJ?N@guCf+w_v^~UJd9TL3NpI<{8VJL zDQzEvf#WT=&%=MKZdWmy&n02uM$$C<PpK`ZU@<TBZoaydSdpqusr(J;DCfGJNPA37 z3$>htn#=Op)Am2N46#R7@6pU<Cn`E{B_S&EF@K|e?<`h+_~|9nv!3kQpF*55SXOTV zU8o2ayMV>)q|;wm2@}rz;}mPXzXw9APqrO#Qos4_@<vu;@Fr?0+Wjh5?|nc3($INC zF9`F0G4_=Wacx_+0SYI$yB4m2;O<aBg1bv_3-0d0g1ZEFcXtm2cPF?8hgY0)Z@+u{ z_3Q3GP#@Obwa1*pR+L^7UNJg+7m2m_BL4;V^m1Unl$*%_?ZAb~XZYAY?sYVCnmBmR zC{P=5&wo;RzJTsbsV_~|r?)Q0)WX8`$r^h`o6<T#72(_h`z-kaACSM)O^0e;gwcbG z!POQcb-wycoAszIMLtxXV(s6@TajedA@nwXd6?qssrZpOjUkJe<Y`BDrIjy^W*GSn zKZ;wou6)H?|MD^;Ief~|>VqzR=1SvBlTH1?CT8untF3lXo{KEejHbu7MInm6vo;%l z<z(1`fV82nkXqn)${$U(>KJD1IQUY>sUA`?CmdBz44T~8m{Q`A?AmoDDS=}$Tbv(v zr0P^Dss?$dn)^K{Clt<m8SlM8tr|9QpzXSL3C72l=MUdb6u0FDzAX^UPZm62r$PT= zEa}n_p8(EO<Zs5n+lf&r>nT$J6GqpYA^6n0ktpc70-A2<!JuC##hF7{o%`9|Vh$!J zGBkWqv<l4gga4rEuYKv}<K~@cS9>$lYfZ^j*M3ZEFAahouFfHwV0p5o!F}<MiPj$@ zT3dDoRizn2f_})d(I-$HBKmN**Ea=fIvm@6DTcY<51VovUuGvyRqxZ5k6Jp~Hd1Iy ztD_+Ipz<!voN|;N;-!;Ww=uWx%INE)w$jf+*)fL!9TGs@RbE)I`9Sginh!WVXi8u9 zM>~G>@1|6uV|9zz1SQ#X`lM)MIeJbQ4Bg-n-WwpZuOKyPw{Cakn=HE{X$Hce5_tL? z=lX%=?#oWwO3|_i7}WJ|1_pq8PY`BK+$HLfr)fWzz^K@C?vV6GmF*DDS~25d+u0_q z<?H3j)64^>XOrbgE3cZ*VX(AL@IM$|h9hEEN0hSU#78eMpJ#UBD5BH+^=R{3+M^2v z10)hIfKDPA3O&nN7!-YWhq@&jNXlk_A>6ego|#UxN$_nuSQ=}AG$Cl#2i1z)AK5JH zgf08C@un#DOm1oq_f0yP0=XxK8<Sn^e61cs&eFAth=wUf2^@KqO`vq-vvDevnvt@j zF&XTQNrZ6bsTqtJen%K?X>IOz6@cV(PMkrbEY8f!yw1$G-^u~=Jk+-tmxG1-tE%-i z-n;T3Gm$)M#{4=)uDpz4`yHEug+#>MC}KiUng|*!+hUe_tA*qDtFTOBMlU&?9iA)e zdIFmxn?B8*n;j#&wQ3$$eq`n*;pO=`{qgK^iJ){t!ga;LAOC!C*Vg7%=mwQB5||!T zR(s#WfRAY21+3wZn?ru+q4cF?K}MU#Dhe?;I{?2x%A0VL@9_TjR5?1fi|NxE*2Uy- zKe4`jME-(uk>>TxWc#xRVrG7o>+1#dEob_o^^@9C)cuPb2M#}9`&rSCWVawjm+A@M zy2BDg5d#ROq2nC8>2#G*=%18SxSx`H3<i<!u28WJN!^DV9uMcV`I|1v9t*O*YI!xH z$x`20x(TCQ>LwZ@!}a-4NMyC2Q;PtT;d|bl#jUN$lz4MskR8(p3xuH2xKk566TR19 zhrHB@?v4IX!t5`4S0?WZR_%sKDW92x5$=OtzoV(bOFI-Jh@9`DwGt`fRD#GndP{1~ zBw9;8oTbiL|Ei($5n!~y#fmzd(;6aI*I!H3X_w$M@Z<6%3B0F6I4~Khkjv^_7i#WE zL5j=Y>sPMRV?#@j4`OzXBBRA9<vEzf<h74Pu^IvkIFK_yUhSbcv_|g~1$I>SPHclN zBCs6-CAaa%kBCtQ52hgsri2PxGh@a|tpSeLog5A7o3w{p9Go?7gW0M3G_&&6DDiic zPusvC;j%~SQ$^*K52KcJAA(FH=mwbl5_=!B^`A3w@aGJP($?2id<Jf?3Z3w7%WV?W z!=Fws#wqr!Q#)-Y6Wei9{s7}xA%E$#4ii=<GxQb@^ly+*!)GAj9t6F#lA3c>kU=}m za=?o@l*rU1v!o~iHOnO*%<;x4t$q{#NqU=Bci0o+I`kvJ0<Xh37SvHLEj8Z9ZW3`R zQf)iCsit9)FF(N?5MHUXp?M`(7QAzRWF6Gwxf86w{S!b?T3vN=&P{+*u^vzi2j|;9 z-_%i2S7gCC9qu2IJk+!gK$WWrm2$KT^9n#>O0r#!mTs^PVSb+7c04Q7{JdW0RVtVP zT5?9jP6{U^nSrTJoPT}b?^qU=Wga8?DlDPQr5r=!?+<~Tg%EBve|R0R;TOb>&XNh( z3<nm!0!zp+=%9K4I_QY8k(Lwj*U)0CaGy*ys3>Vw;Ru<k?chGe!F_By71l7++-3yD z>o5vA&*stc7FY#|dWHtY(I^cBIvQ0aghKvY?y4964`zv!xF%~XEPs({)jJ)HiGDLe z(EZ~NG)$a3@b7pJa1@9Ks;$?Z_=S}+5mX&8zv`Ypl|_wf-5wdP+Q{~h(waBs%ruCj zDNAt6h#{03eF<TWi!4S1m!_5JbwEW#>0KT~VpQV4ERF_y@9By9xH^t=Opf8PS@P@l z<`}>Nr<8F55i6YI!^*-36wjIQR^;Aax4qg}#HOz#A16$SyT@tq$2&RmHukHQ4mEo~ znG<pQKzV(6F8B#Br~lrorKZC*hu68)c+9=fWT4RRFNdP?QRn60+R?Ub;A>f>3*qC{ zGO)qJd#x6%0jf7+j0>bH+4qW@-cIKOM_Du@>Jo_HZS2>)HaM&pS&Pjdr<&976VKG^ zodo?=_>$iRhI_zaK-Qi5aoqa0>@_7#Jn_uB6VO8hq>uAzymC~~?>VUB)P2VBUxOyF z!<qA`wbK7hRWHKYF$@`IU!XpnT)T$T`Vt7O4TyjeOu;bV-&tN>Qa!j<`;g^qFTFow z@o}+KJ=H5d5`$V5Q3F^V%HWt1V+_F(H>@cWZ@ejw0i=R=5GIvr*Vj=>Af<r_8^Rp! zOH~pQbwwO*fS)XkT6FZwvbVNFU8z`W@mUHd!is2&)N6BE-1GWEQ^N$=HDu4;C~MDV zeus_RLDWSm3ih=;4>I+-9^cxQb<cO`HM#e>Jp*qDSzZ@gRFqO1%Eh!?rqZ|znpMf8 z`lV7<;F_^0R$Yx!{+?MWz%y(6>_CwQ5q$xwvKqZIyqt{mjw&cjPo!G(N$PFH8A2@8 zs>}6b-Xwt|<k~J@DJg%1k<*#kZRQp{R&m=(=sh1lF3uQy-roFu8)ZAdwI9y-=zQvf z6;)!$mdd6h(%%<@z|twMS*H%N7aD>PG1A=JFlyw7)fH_F@>K;U_)Ug`C}=Tfoa+oV zPj8EPKW5ZVqNVcMm(sh$t7mvPQ{Z8twvYl_>^$KvX!{K9rX<Fw$)D_de#Lk$h)ytT z86?1^j(#|o%*LIfsj>S7zW89EE@&Eg|CeJhU)cFL;~NBGHZhyB$<`s_W@=cJrCpLK z^cj3MOPxI_`J5yK*hT5Vh6=HPfiYWB$|q5U3B-)DWg2WOLdG47vHfkN=~nlHDCR?^ z4eBFGM~CBjsfV5$Q4~}vBH@ya?^LhKd_H?%Q4#<kpL-&b+~RiI4Q=TXozeJvFDIfJ zZ9#20>gD=}sQd}b-h88claZKFZ;kbl3*o2rUl6;PqB|2(Z}8_-GZgtow)E6V07-NC zHouD2CRZgZ0aKsB79Pt3*QSxd1l_qOO)iaDj2&ohVNx>bc0)W9e|>N1FY{U-3%{yr zl^mcW1W{Vk3z5o;*M^ae%xe9S(`vZcr7g9^WqDO=1aXzmG{rAR4!msOtSI?Bh7Q0- ze5_n+Q6O=Vu(PfoGB6`8ONtH5wnyJaBq4N$XJ-p?qP>6>k*mrThbm;b?b6Lkd~lyR zd9W34=Hy&$ERzu<^Sk9qfWnnAbaFewhlH=RF$R!d<bgu@(o8kgEZ7X?o&WMm-c25g zX(6&>ecc|J0UP6JVZ$6D3ksRS2gFu1b&E6t8Jb}4G7VCps|3nBPldrQ72yQD*Ts9T z_59PG8=G`$CnZFPjy)b@DNsyeqG--jfO0SE?lzWD;Ad7zVr=7~#5FR~zAV(Hq6o5F zGFrpaMdNTU#2Ef^nizkhAvkCFGIvV?q*-?#$;QfuFVSH>?hX^zyFoYRpZlLaIYuEy z6}(&SmPy-KB^6eu2OQCz_*2>`kCLnR%hWJ!hP{x%BU2#TYBzcaJs;XIPdu8ehvB9@ zuk7}A%(QRC!4jO}FRF9fZufBTvNj@E<Ip3NEtsV8OG}%n;JER(s>VU6#KPlHz8~bs zPp)VI`bo*FNMmFCWg)wf5@gIv31nRDeW_7a83;-*P@KlG`b!UK{)Zj{yaN+qVr*QP z;_s|UTfFa~qM)D}z7Q8OEIJIH_xrS)jo@Vrc>hjCqt+I<R`as3ad&&^dYDC2J=5;= z5=_-NyMofw!OS7}9gXWSNyi}Vo)V`or1BZaTTl!i_<%|>qquMRpm93>Jk<SlOZcpe z%x^xS6_QX5wF$8&*4OzNW(C#&0+|vi3nbYjT)=M!@n(OOW6_BL>yJKM6lI2A*t&~@ z&ZWa+--(6~q+`uEzn?1<4>Aom_i(p#qw1^EWK-1M@@zWbyA8D^XV1zL@8;&{mtABx zv`Jh<O?ii}Wq(xqiFb;8a+ajB4*Jt11NH_EU6AL80x!1${z^~Q+p$H?j<9A|RMlbh z86e-WRL=>S9KZ(MntdzOn1-I?y;OSS`EEx)p4`Q^cGx7_6{Sw&Q4E#wB>rD^^0(oW zIv=ki)KCN;AMEG(1h(&`>G#XoF8IE3NKTX18u^c<gTAUd#m+kdwNO>XQ8k>jzE4Jh zjj^k_<!tcAH4`>T!&#iDiH^50(>x9@Fb`7?a(Oy&1+-bEzh}i2CvmY|cU8cq5C)x2 z0*ZGi(R$AP6f+7(?0Y*ODLR*&e=*w@TVL&rB>^ino88VTyefM*RRBM?Vx_t8%Ef6e zsH4>45YsQLQt#_T38GK61ESd4i3P6>BCdWQg&+X=vTZaK`2qI_!BFVAqU%Gbo!P{m zVHl@dazZ`C4xxtn9NN{PU6blgmnkO6*p9lVo+#r~KW(r@;yS`mvlEoo&9LFTcy_GK zOGYu%?dR)`B$-OJzq<*XtjxB$M`}7F8!jz4p?p$RX5JDVqPI`0F=CSk3&O*#dY7A# zfICe|a*U!PMkWK6@n_N>VhxdGd$pfAnf-K(q_Qyu?E=aj$JtKYu(CA*A#Q)-j=0M1 z@d-Jwbv6DO;IA0$yUI%EjT$iD)v&oJo*&2&AKp==VZy3<*dJ+<9zhdvsJ5OI7OO>x z(sKUkb+dk|>Sx>CF=unFU{mt@ZtNH$UX6ocIegwCS(1#kD#&*fo%m2ib!;VE<2@0i zTa?y!E`S7uTqsnmG;NBlKBlJT4DD4KLjhKaSWR16s@BgSI?jDm!8pTjliX}Whbd2u zm<IUofopDL{_TIl@C4749upe!Si_F$<c*j;jF&KG$5pO4$xr*UnP9BY;9CzXoDzq> z*BjGts&ubX{_!&}+6gn4a{*;=xl9GgYv&RnV-<~>Vl`GW`>JPnPo0GTv=u^)y82Vz zF{<jc-OOqP|Ax#aC<2di;s^fZef}}<fZU|1AC?*Vy+_2P1t)BV=KVcmtS9LzysgpN zfB27^HwNkkv|PoVYX|BQ+rp+ih2k}G`U8I@DPay^GzwjRB#nVnhoDS}Auz-&R(#nC zI|?WYIT|Wri7y8X6<x&SLs7jPq#&*|X(k91IFURBeiV+E5m$d70bN?WLC**PiXZcz zFBN3K?o}>vJM6V+!7#UNoDQqU?jg=c^3~5QT%Dz_n4R~q*3OH*qd-YyV%kORFsVx( zUGcRKxA&Zv{x^nX;y?RfzkjUqg!=jarAYb?X`dVW>}Wfm(aLiDbQ-M%_XygwJ6i@L zuDqR&0u}T=?(~(0c;)pTbgrvKFKWjVG#;<L;#2mk(2Qj5x*=EMc3+}OQ$ZM`>RW&! ztnt77R!p-<S8Xv5n*ycpxtt0!6JBqxj6UZ0f6l4uzTW#R903Ehv^@wf=_(=|bio2g zZAHC_1fP~ozDOedz6UEHix5ZpzQ{1!QYKC`uZv{7L-K9{cCm55Ji4zg=cW(DAV-zh zC9UEjpj^b$b7K-G20FeZ>hD4R?qnN6x5Nij1wH=E{Nt%vYBKe)&svDRm@sg*sxQSi zxh|1(M<~C=aAxhok@KsE!9=A^x%h|2e#?-oY5TFPvsBNs1F8+qishgaBhp|4fGiH2 z$%v`V0#`p*ht?<ZFvkvx=}(k1%=}Su<i}Xkp@x-#<LPWajRkuq*(KJyXzJ<;W{m%! z4t<Ftgw(Rt#ju~$86iv%BO>JjSj;AR;U=GOTKyz>n9o>sdh*fcJ|1J*_Fi>?>;Mu> zSDCucG=TH;MzIj=wHBIi5C)AZz9|~!&}!uLkfRate|ziO_x%CpaE)AjY_VjLUqqDB zO}&{veJ#w<rwPO2hRY}61v3bkZZOEW1(p$9WO;^CAOIVgF*aP8t4%12Bl+)l7?+R) zkT_VdGJL;2;56B<5f{pZn&1BxxUTysGhNk$45!yOWomfHP~gD0T;;KqJMIcScd@uA z*p`Dn(=Pv;>!Gl{w9?(`AumDB5#0U%oh7hFD54jq!?Hq3-ezGXT%1Q6*$Mmspb1G} zPu>#3ALdG#hDo&5Wv9*BdTtzP{`WkIHu>8sh%>TF=}S#X`%P0ak1`Je7P+It=&W>{ z;8vd)M*@k(#?F1;e(>4x9H$b`O@6Y(2E4nm8WBn0k}|ZE&=88l7;x+B)EGmS6SKvV zW~Ju~9B>-4$sd&yQPQ_NYH$8IwizV4?vWfYKlu$%i~|mZAy_pPZ5xtd+%H_2JEdb| zg%hA~8fdrIcEvWs>Q>>?S#;_ido-!^)#0WLk`RyO@hac5o~#Txp^zWxkT}XYai;>v zxf7@ou(58(Ag4cWF~GtB>SX6myvu6t<3jMkbH`<1sl?M@LhozovMZpG`B!;*y(h20 zXg~djzzzqsQH@{FDU2oMifC3e?}5BfJW&XvRXkU+BKzl($2DpqNqt#_q;DoSZY!cS zI&ChH**M3M|3pb9Df19KB)s$?)+kMFop(6=+hoc97$2-EPQ}2usRcSxbo#kAl4Zv- z`0g`_gh^JJjm_<KS=NfSqYNy=1OH`VyJe-f=YZ&TlFH<I=z!Z}X22J36`Vj~7Rox! zI;MolK>9piP?ZeZxTcIK^bxn!+OteFnylcIHMw(OVKR0%W0Zhfo#0B}&$687P3Aq1 zICM%tEL5TY$Ko=Yy2$+Dro?0=0pS$VDOR+joCBCo605_=k7H~gS&6b~xJ9R$u42QU z^Z=w9-|j+m7TvI`-DvZG4Q^c9@5ca@0z6I=@+VS?DULOmg#M&Ai1W)2qlRb6KO)6B zB`5J4Wyv4nl;&d2$;D=+HAagEv2tC71)KXhOcs16d?QB;5%?g*ymc%<j}`K>Sslfv zXOk0I^$vyea{!$%fr*iBN1|b6Z*46yP5a_l&ySeWmZhOhnB%nmvy1c%+bU|E1`*6n z9wT@<175Yg3$-oZUT1XTJ>DmC8V+cB*7^d~vX#}~SY}}xevB!iL1q$1Wf)Lnk>O1# zDqyRQN%hRC*s1Bp3sDx9P)5MVbDQMOy60yl{si?T+8lN6tn3yzZRmuReQL(4#i!HN zP5ZW$Gfk>4ht1?jNCjbk0*M{PvgWyoLII=Y+|K5*iyS%L;9)VRF(3UpSt{vys3)p$ z#`lN$79`xnYrBi=sRvX5WM|X(+&cgO!<Zf_#vdtWH(7usCkwZSj7Dh!PDZPbEx+_< z$bXBj&moKTYq@Q@|E_WS`8cI`Rdej=YT#Def<{%{0`YZDxW*6)rw{I~bfy-CtfQ=l z5=>-9l58q)Au=E>l?`Ev8A_~nvU`WG3Uw)RCe>7UXH$XV(dKy&L;h%UZBM`u6^cKP z1d4W;C7VLnraOZ#*IMP=USQ%VvRO?Yix@}vabg<C7d8g>bvO8POz5K|Xoyj`4)M#8 zjEc;C47K6tcd|zYwp^-UFi#Y@n#~lsFMQK{Ltp?`l}=Q>`-?(UJRsC#fRoRCPa5!Z zZdvBIJ);DCdBk^mScX1%fa>_+Ou6Ugfk`vG)4M=I(mFf<tlWD6*j{5s&_Pd#{+;}0 zZ4%1kLcgehOaJ-$`e&D;81W4wYdj=m!mZ-kSgQEe>n$)dUE%bv7%J4X2ab~bV-NT% zO7f!5@gwBE#@5{4ZLd^JyedX((`02_52)5pnB|CTc!U06+ug(Ji67i=j5_xRukJke zvCz9yZI+WalDmF;0r4zl+G7f`EYVj3p(c(o7g3F0X>KJ_-M4A%t5}JwWI)c+rX!~g zhwJbpamsVQK)m3JwSTAP_b~IHjWDt0*N>RWfh`Wd=X!OT2R=%$#jtLMV@TO~=5_}o z8pcs-qk-P5`AQog&8RjRZXwvKL&8sFE0m(hs1rM6=iZSj3#95h`mx}AmWl`hW2lYS z=h+Yo$&qovWff6r?OzSJG5zT)xc0%2tg2?>tZ3b2LPK|lmrcVOhD(i&rF;#JH(~8Z z-sfGBr7#=<GgUT3Cs{!fz9iPx7=gt}yTAq5qyeyKmG)<jVIfe(?4sewtlhG@bsVgf z^;lNY6K8yD)Jax}NW>6k{%X(S@6H5jXP}Q=R(;#7$AkXKE58W>1uMNkZt%gQ{YQo; zKN44~Q@!I@OH(`Zp6)Xm!;Y#ep37CzunJhOBMeq=CwzfYf+0jL9{JQ8OayCzW7R{s zyndZ=cRw29^^_H$&}4Ww=`sJu)1DaLjDa^m&B~Q024@yJ_P5;!azcUqcaZ_BHTxSq zQ640jHGg{c_p-p+&dEusag3^jWzaw!z=tr}9|0p++U5AF{M+*N<#%HMm;8rzHx?1x zK39CJCQ_hu4;IA!xo=N$FHZq~yN~GprrZ1_51ag`VA)u^T%+NEji4AS<4439>It8N zwqhW7&wUzuAMgG8YlYWBRbIE+@hUT*vg4(5y3GzzpMPnNN;WYeSmzcC%hb%Erh7J8 zPVl}Z7EFrni29JYO6=RK2mVP}+KreyUTWI*J^s?W-LjRx<3DNQq}%P7qzgV(Jx02m z6cu?OIe1#ZsKQV2wJWzPnPvcjz;wOp5$R>hm*;K^8I&uN&K*rn3Mxr~^mp5tt>4`k z6#2WuNw4r?!8XdCf|h8Nj|Oy*<jb`!k%PV`(KbZ`Jq@?f8a&<;!Tpvl2HI*8#Oj>g zpB&Hw;4&Dp1h5n^+=uxq+XNiPPxsAi4g?DBZxzOQpC^J9{-oYzPJojo1cm)1Eo#hV zyV=)Lpix#nl$n&G<*>>3f`rqKcYVHSJvsK8eRV`3jCh@-lMi(|U@rHxeI|O0A1<n> zm1Ag@thk`&j#K&kQxy-r!*eQ+lfSWN+HYH5f+z1XSINf_u-1K7@2&Lu2)@%HgwZS4 zV0CAgm-F;b;mM;OWPwi?N>u)=5E-c7vwU&15hb(bVU7$Dt=qfPY&T2|`B67|q;rLT zcOYrshyc1wI${wHd@X258S!sBbAom$jFB;G`o`Q;8~(rnMwzzH9br)Y>I!(Q#D5GG zgZs^9N+Px(uySxwX6eP1a&gt6?Zb>006y9@N%8M8V*rHpi2epC%<N0_NV6kl;^{vz z^Mq}ds$h9P>OL<-RyLA&9qYjodThQs_6oqUc+G5r#K2<V1Uap4B64&)Z-2ouC70)- z*Gn}NYj{L{nXF-a9AsKs^1hq4UFAP9;s*|udiK>N$M_149bNTket3~K%I|4xahMq; z!7HryU!Lx(E4;D&BKUQ9;Xdt?7nfatUuda2c5=IgS(1Tlhu7rSvFrhb1d1zjIpa~` zVuH{_#)2Dxu|6C87ZPH2x2Cn(VuTxyVc7`X<a&eOdtS?b+QSDi#=r5sXpb^UDy>o` z<@J!pDQPI0f9{SNJRwl5ro|PR^N?WdUeeZUaqjbYx!l@kB6%ohGOEVj%I{BOb;3p* z&4!<?)@Tm#D;UBi3uZtmmzAZuHBnXn^Zu)9YgwjBqxe1H3*4JMHKJjJGG>^kRR`#^ z)>ie`NQCyk8j0GK8Bs?7-JUu1tBD&BAXGR@oQgGW&j(G<JG-CHH|xK3?PWq(*H3l* z>N{DmFAP^l@Z^?RiqcHG=IT!!7%J6CQyMUD6zlA0y$~i^k=r^9mmvb{Z$a8#yV(P8 zVHv}$7Nb_>&RQrSrE5mz>%KzGE?lCACvhYsQMgqvly<5`@i~<wzSU=A5W{$tx3d0I z&H?LmA_OJteBe=}gZ5^i!zV@{DM`!sETl1{`{2nS_P2;R{e-%1bqE=lsHP*8mx^-) zr1q__A<@Pe`;iRSfrhB0u}dV9zf%rDLj*Y(BM1~B!J%Wsgo0v`QV@x0Y*Qg#&gr+6 zI6)5+?H`!j4Gh*x5=8fHb(;*%n2=06h~1AyP+A2>Nxzc}i<O+KjFHDFQ3U-oJRs5J zX3+30u2s4ED4<x4<gv6xgsqw(PV*Qcz+s))$6H1P{*3=Y)m!aJq(g|c(otl@JDR>^ z`TLpGS#th03psbEY#-8bMZlL!gWm|1xc<Ol@Q!4`KdmtO1!y4Kc%$cV>M+A|ULmGh z^u`!qF;7~{UVLFBSGUIhYZwZvE!|*v2Sj1sK(_^4<d)l9V1tVJZwFGhUs&(>!ETEK zL73OvNdTxdqvdNU(!y3O#J2+xS(NJBB+@Oe9v3AI&Nx%?0u@{ep$AytmogUtr`Zay zJot2Y!JXA%KHbVYpWGkRw%DNi8gaT1G3aWL+p{HJLxaH_OUe3<(Z6q00t@pwDEhjl zYC4v|Qw%#w$B(Lmz-eepK86!fA{HJSxg0{A?JzQgG?;zfc)mypCJzfrM><Fr&2S7U zfh>IW)qhe;BH0M+P;3*PLa2v>8?t3x30&hkIsdWV;WF^}?KsL3Aqyd;RP-zzHQrM4 zi(bYViDOn({t#**Bk<40XEq;oo-^wc`smIa<Qs>pJ1nPBmx|?^aDh0HZ1inpw<3<& z7G-aE_~yJij$SXS@r{MWqh&tUt7RSfZ|&#1x6iw~(a%QbS!d}_U&kGeb9}SaY(!?E zb0ljfhl>#u(a9Uj-*(!g@_z+iQf<fd683-0t~{ZhKuw>j)ahQ&Mds$jIjP}T6zZ;~ zeI^s)2xSFx@&3Mad~q&6HS+}xtDc^5FjN@%ZZ$pw4OaU<v_`&~WGz{eWZ7p44r6=7 z8&uoRnP_$k)6}qOOv|ZMuLMT;pw0xlw6l5LobH?29I!UrgJYS3CllsiujSpuw^{rd z_dF2zIuZG%p?1EYJVKRJ*Mwe?!waf#7B*q7!(fD*>|bhT-(pdE;2Uhty^a`6zYJm| z-sa-eP+i9+WA|J7cV&0U(MqRJf^wc~iz|25AvX^<9@wzOeGL|ia1k6uu*M;+WZ$J= zNZ0Cg<f%8kql!j?)C@C^KK=CE!Bnr;|7($Y&zZ3L_@ej-$hcnZyicFJRUs$LO?<Cd zkM-rbY7<9fPFQLFnz-)V_I7Uj(SL^b+p{w`*?3l*R9vfmf=V(O4!)~W`u9K%gWpPn z1APHQpeXHIb4JKC&oVk}a(sgP-`HUT#typI{{cICg5)+r$?;i<ictaJA=9Kct3v1H zarsET#raq3>l65<5!~z9hP-6~4nzWWeex6zMY5zsCkV5np6UsPPw4n}Lufp-wo}Y^ za*?;0$x&~TVsf}|CQg9I8C<YM`v&$`k4hKhv#kF1wyd3CCZH}q`i$ZZ20~rnS(5Q5 zRr~F_BCh7xEgPs}4P_?4x5ydyYL}v-;+Sz}Ou8r35W3Q?{Z@V<7;oWsn&jKoF7(ln zkyFI86^9E<^|+bs;J9>VYJ>M_3;*RxPHJ02|0|RqMULsVB7c2V?x=w}rwW(npSH{S zb?i!^Z7NKGFt4@}yr0KrOeD_NS-LzAPdEx^?AYs*$Y~AOh;9b1*sAZy!yWS23Z{<3 zWNX_B3O*}uUW@$&9PXmz!-szn#hcMb39bu&GsU`dJq$`PGrbO{YeEn2UsJAN_W0Y) z>td#*3e*gS$!GEC=)rgwiLKiaeSSNl@pU-h{r;M|*K|Dv>+B!@R_bkxuzl+C<i$23 zs0KDNi0~E3J`FWbsx8Wh{Ze=`)b&%o^QGJ<4@u@F_uIZ%H6$y({g;$i`>P!nr?Ni+ zbBUY4^kvuBa(Ej#z|rlQb;;V+^KhSL)<?8dlA~1VNc3BYj3^$zrb9AR){zfZRAZ=t zRq%OI_EK<k3`nieZi2Lw;M_3EdpRH%Rx_eb`0^N1dI;{EyG4XTOWTK$=AvfUHBQ41 zEPSCuGT^|Bt8VNnD$#zLUYqt=I&-u$gqM5~5}x*#xoKsC%akCEAmbeg1h-ISMRrIF zXn-i|dhTNQ%6^Xfbrxbu=;=6n7nR5&c<z5uu5772OSy=~!C6&pcNl6LznVK+tlKBu zQa^(DKDw24v;w!~I}6$z$JFJM)2zGSyck^);JA9zvRaQ^85W;s*4BP4!VWT)r#GT? zo{|N_^o@K*RzNl(+i6J9VwLp!t|h*WO*LZ8imXQ%Dl6BkK*HYOFwCA}{aJ!uISg9` z{Lcx;o*--D>x<4$-gf-PSK~hXtbhV1O%x==wDVKF)@i+o`>2(Vb3ctp!e?tsc<|}I zAFQE0Sa*i0n-W@nG(=$Pc)F1g3wS^J4&5b`+Vfj{<W4R5Ol(h2U@{VhG<5`_!N;D& z_R6`BCkLzr_l418FW}fGoFzCS_CNzdM0!D8e6aUlAC+w)&w-pFJYf+eFM@;o3={Tg zAJrcu3o=Zxo8_KBGe|U8%I^uxlEXHo2a7HU9$d4SNv821M5yroiU^G?Mr5^j#$2hA zhR6<o$As>$)5J_yQo@N-B3TKo&Jn{9`rrbG1$;FBayilt(Nw{&$?!lI)COx4*hLkP zzF3887J@Y%E#PAU_2e(azTEM!nqeW}`W$$+!dv@%ZgPie1@2bRg~zVn3z5$f^EMT3 z4S}Os2SbFrKbnvw0TsYI+9<`w6EWDSW#YGn-@wVUkJo)5eTY#&m3J?;!KnB#91J62 zE+S(y=**W2{)QZlp(}87rQxs580DYDC&nFqirCzHuwzU2Z-V15kZ{XSU2FGTY^-bI zyHd#|S05@rXFJ_vjxT+q_mQf>Ps|o=s(ylSOti~503MOk;B`fQazY{?I%B-kb^M%U zUD<1qV&?oFmj@bdDD`6*oF+;{WfEOX@F&Iysk!<<y+;z;>dIMO-R>nYp|jvSgiXJg z`BnBgq*Y!>{U~at;lf`FFX0lBcxKcQSJclh5Q7YlTBR2k!<Op;1r|N$PK=UD6-}Wp ziWJ%4h;TnJFP3x<z7AyZmxGd$4l$E)9m5fMiqd5O%-0T+mJ<5gFNtUmW`<tvG~5wh z%CQkM*--$)<HW#0yn$eJIVt;YQ6^9$!UBDcs7fyu#L`Wa1q)!pGnc<|2gY$?Pvd;h z^m*6p;*00T1N`fv*~lbG#*F=4jpWh%CXV4u;xnx7GYOye+;*^;64z5q3!!xV-5eDK z<y1wY-E}0%2Jau@zJsIe;!<1Urj4^(xqIQtk;+B_CucQVaCb=|m;Ok}Z@J}<XYSCw zwdjK-HvX&Z(iOJmb(h0C4OoS^gD$HW(&<-3vBBgiUGqbItyljk8zA<OU7aAi_y6<Q zh0e;J`b79z>152azR%yn>!u3kz)!Sk_fXzGJ4+5^jY833KM(LB0hiHQxVLekWWgKR z6M)3K)Oy)W5#63>@zeA*p{G&HhQ8wK3<-$`M+<ET6*wRJSa%1vN6%J1&U`?WZ#Kg3 zS#ohH>poJp9h>uL#s6mACME6uWXJL;6xOo?pbQ*?>q!&&j^$hRE(9Z3p@3oqPG5Wh zz=*kzE3Xb>#NP3Oz{cibL(Jq!`NWPIlO!82$JvD;Zge`tRjy~KoNR-YoSW!>LnW#r z+LB2b!Odv^w^GbdE*U^VG~-^&+m-^OGWfM<;MZdOqdfoFuv_DNSr0aK+O4Zz<d%8Q zhyKZ=+sOM6zLM^Q+x1<klKw^}JTgD$`h3P7JUNUWWpFTlUKi>}?e%EWJlWLl=|_N| z+wcasY@m6kyI6l~6~ma4*B4y}m5N(?T?Uc>KJIU>qn;^_YoC|=y*Qg+Wh^}0JmJ3; zFTt<GOCo<4(*AA2NQg*X>h9l0N)1OzS**s82SOcVhCgv1-TOXaA&Zqzg&JtfdZSn` zM56>RJKqwBTWU!MXD%^S5+LN_TS-|3O{qKlKBHmpn?b~jw;sQ92p<+A#aCh%@7ETa z$w~3(D*YRIPX#j4+zPY#@FhjH_^|JA70F!)de0Dku~cPH(L_$*<<fbwk0Ysb<!<%S z)Vpap(8Vc^s<VpNA4Ga>>irNK(1$L-xIWS3xp|(%f&a0}PRdsV6G}FHNQzBWM*#B@ zEV7wHTzA}FbYih3iM49LNvEO&A?Tq9ufctytrov;pcxYIp>Vc%4Q>!kjFbio8_Fbf zU*|@MGJ+Yo2|SpZEZy)#?ShX+?U!ApJmc-N@><gC?Ia*Q%Nbf&xgNE9Heh!*iV1{D zm?;<p;Pe+riXy;BVXVlCm&5aZSLDvX7MhOhD6ZDj3regO{IQ(&>Y;UEe>%Aj@+xgt zF*B=N4e-Ub<wqSkG};yT^_x2}Ut1HT+&;SLEUGw5Le|wcX+1_&q}6sG8(wgfnS61z z#-ScWkT&{sm<SGlco{#jwb@#L_xEueU2b97C4N*ZWcK9|83KmatV6aAwg~E)o*LV1 z9oRSs#v5Y;oOJGr3jf;VsW<6Bg-QdIJVuLAK1cCf7io&#@D=58R8a*F$01clTCeJ_ zF!bKP1Uo2E;Wyx=-zm3}qMuU@2N^oiV<oXElI2x40pPF+2Km^J`|ZHOcIR^Y)}3sy z=ixM&o;B~`h-9I`t)nMz@Vs-(Pq&6?-q4B`>R^U~0&G#58uUn*f_Z~`A5u#AvAyy~ zNv4%06XO}5Ro?AB0VRP0@rGH+`rEnXKE0CU#9rJ2IV2<K55j~8U}uXRWKSP)U#v35 z0DzRt6W!T}GSS#hVLHq$(w%GRGhj3xGV2nu0L=&(qP14j)gr)Xn12Mr`7JXETr#4Z z@(bT^>6>U$x|56YTW%J1sAB5S(eK<uIfz1nkvu<@CN}VWc5)1z6cZ;CDkBv{WB`^> z9DFm{uh!uR*R6-H4FeK{Ak^;(w5(@^geAkJ#eY;JUCO+D=9eZ{`R<QB3iSl+E+T3< zx6C#md=43iNxW0f2`_3BGZQ5_6xZY&xXWmYGu2*7$<tX+!_}ndKc1B9)$BIpL{x?J zBvlez#74lp?5j|)_#wo<r`xygs&t}(gf%P5Ztf)!3M{^x`*Ep+PONQL6wY#8qb#Pn z(8YWuSW560RpI8_NP|J!r3+6)>kOCtCf|f~HF73}fphLL9N@?fe4Ix7Tg#0@6!yQG zAkn)p;@%ZG3sY{$#vvw*#VWI(J%s3EX`Qg!t^~X%XB}=hx^k>$?gjhR1=_FFuhG;5 zc!=m1<;_?8ZA8?>Kk1BOGiiP0D)mjan+y-2ype>r;%~5c^rNMB?qK&&YGfVnboV?> zdXRTB8>9=YUz53f30dpjzzTW~)V;}z_!KDA9T0m_&$onzjeM12bSG?Vn2SGvVn`qb z!y0BrHAiAkipp{ac|g$w1dLpYU656>hoWXf9l^@}2sL+c#@*_#JL_+OJ-IMlLF{5F zQa|sz3a{OsVbK4wl@^H@a;IDHB#1GnSkTlJg>+8}Bu5aF9sU<DWS0OODL9$=S{|qt z49xCd{ifK@8=<hSd>b!m+DO!J9Tx0_k?O*3z1r#ZY8k6!c8@lC{bh8f<Pg*(-4nJw ziosZ%jh1S7czmF7RDF1S|4f~=TE7)B|E;g2*0uhZ*||vB5qTPAuJBBO5WH^@wS_V_ zHypHmtYbD+X=u4Ip1jHW0)L`(wg$g2gPtO?Pao=A?F?-A{9AN~Y3<q|IHs5q<8tD^ z3Pb;G{(*EAL4P;P6bkT^cF37_%&#Bh3NG3}pZS8;k=xnD#<hOHvgfFVBx(0Js?~-P zGB~+9)ntRC$~MRqhIA!@M&};7&&ZRmB6Pv%Z%--BTS+SVio$=h7Eo!_e8)>&*0IqH zO}(z_^YlJ`&Bg0|;&(~)BXPMG1ZCYoqaLg<=X93YKG}}sKC+-%3WiwtLFzHqCsH=n z&vF~SI5tG!g|@G*i9`l<W<(2qW#86T?`Ca+gXE0+Vh7xQa;tL|j%7s9SiT;FbGHXE z;N#_aEj%L$;&NXkk0m_=7IUNemk~Nid%J8qDQ>r{f`!cEyW?Y$>yno{u%6;^&%&;x zo_@Z0A(t#sziykFjVx}SA#~7fBgv*g`o5f$R9QFKU4xFCp5bxe^v89`tWrrn&gf>e zZ>^e0A*ZJ1@LNH^bc;Q1wh6e~=gHy!?e-b2f`6eAfsB1)P=)Fd$szROw2QdDA<`3` za7`iVdqRD9e?Hz{lyda&Uh63uw!ph|^p07Z{}L>$pVMkoChfh?Y=N<To^tt;lyg|I zik+cA(Pi^tyk!iwoQdquz$uct{*2bJ^M$HaiO0_f#LmUe%{+eKhz#5duZE8bz~`uX zDjo36D1}L^mKDN|h9-d}x#t%JtMw;GO(p#?&E~72m4NdY*NNnh$*f0{)fa>2Xfx#5 z&zhaZKCbR-;52Or<AQ&2?Lst)lG$!l8z1unY7S2_atVE}&*x}mDJjL)ho=&PJK89T z-^HoBYJ5D;`(75EOE&BcVtwbw913Hexh;4EgZk&#GnQ_7TE0b$RKKXiRppUIgoTP) zEM?=v$=-10SXYKbo&Sf7do`FZ<pRIKe|K$w-{5kB5D3kJIM5LDe^s^d790Fe=n}r& z*X<wuPUS@zru(F9cR8zA=b|#CU=w=XN0)yKY)85Co*c0x&boNYPnb!x?p@leA&Lex zIt05%Ae&)*N@93>i@BS=+kBSDqQvrG-{7F1du%ZsiUX6SULt3-cM>r1R_j04NNW2# zj9vl(ujO?guH7jMcF{M;`liSExzJ}4=?ZQ*p|1>=jCfRhvMo>cZ7X{7zxjakZ5uoF z>WlAC{E;TXb178rWT=1u>9vNDsr!;Uu|{=2<z(pwuPVq{ggru`y-E$z8Pv6;oos#{ zWGRvPb+t%uou_)H)-hN?FG5ZBOBq1bNqFN^YsKfysz`)FYRo|Uz!YN2AI4xBA!>Eh zn5p#V^#&~!BP+cU_oWHMUdNa++p)7vL`bP2_&jL<|I1H0)}VoUganTMvq2x!Ht|ac zu2xn?RqlBFhw8EUqzb(Iez3S(=&@^pqTo?h125}$(%ndPfce{mgrf1l6CVP`m`6j6 z@`q)7nSl$;n5cU5a~HT~Co`>j^C4DF(A{qCq8-={TS|Vn9Ccrd_Qa~C%QQ@!a5HEc zp*K1jxeG(jQH^Dk5$qiQOgj`|+jgCZ66e9ld_mXKa%Vbrp{XnGNzqVCu{X2E@jN78 zXj)OZLWbrf88q4Wo|*6H=i2ciSh{f?(3z>gYXc~mS*A4@%`Toi>h=b7_10cSQVPmx zDDD1^_~j6PowEmo1GORb5PH9*EB`RNC~)V^G*xPIB9Qn$SHJFbh+-QYG5`rkvx9K) z>3;?#8p8&vtJ&?UKS@z5u~BpfrJ$3wtN)K24ho(X`1sGm5%IqI8_(tcm4eOd?`run zA{{JuE{TR73OEm>d{?4_3V#>3Bhqh=`_XSN6PmqXrF^XhWt(h41Sj6AbItkMjG4e$ zv(O&w(L?|)s*bHz+1<z=3k2t&<LSf-h)*!`WJ!=t%wD&oQuy_dVv#SG{8)ZeF1y>F zxteHmTr#sM;%$%}=WSDne@d`2VRZND4AxJgNijuDciWu%G(^ox$%tT)i@cSlE^a(R zI1_G*W*=)TmP;WZn_RN^^+QiMIpGpPBLi!>%93{e!%Q~ns<g9zq!6d_M*5;}!?|1z z+3au`Ya!6HXXPCPzVTh6os#Iw5M*us(T_NtZGuYCqwR>uti#D%_y!zvwVMg#**jDu z!MP6S<zMT30*zO2wn|mITDNC^U_abw#84||hO$R10#g9$^zcc?=u4=5%L_O#*2vy$ zM%==lH4nW6B^^J#*>e}qzLwry=_s8K(+L3L6W#;l&=~CQ7}5dW1)dr`u^SbPvh?f! zkZAJ$8;J&7%|w=_J(P_>SqsG4&T-$xXM~v(SCVJ5@)rz`5qk6cKzus!m+DHQ#G(7v zTU8N>A4mBW<3KMzLbxrQ*!ZO+!^pH;lF&o9Q@YR@A1uXjy*|vrbGGw!nsx)N_zT+e z4-S8j?^9?uVx)(_bE-H3lFmcqF+RXIo_f{W&vd-C{S|54CxHdsuSeg(g$@IS>}yC` z4<44rAo8(~Shha}F;0}*ew4FO{fV&?zw04e+6WZI?*@Fk-KWNrS07XV1xF`kV$ctp zbNLgHh8W1<LMo!kpncKMfF)UfGeOg$bef#%!B~kbia*(6iErv{_)-Yz?u__jT%1lm zj(R|JD>V0ezJId6Qy<8*KV2`3DZBhAAQ{r-o%rfq7dZaAH!98M?A2kP#%42?qP&tt z!*n*;Y5p1d>_{<Sa}oRy3tjpz$*w*c0q=u#VC{R244ZHDAEJG4B8PtTkKNXVCnEj; z;$nj)sX8+t{Giwcgmv1S=N&QB>bxxGxcTWj!HMPVSB@RtRr;!Th>5w@>J3=*b;m2W zbOlsG;xAiW3Cw{eOC~i!_^B5v9j@v@hHpR(lbSO<RhB6s5D!B30c5qK^Te;BBbdE; zZKpzdp~By)Mw*RurvBaNo;at#y7{)Bn)*ynl@ta3pz@3;PAY*GNs&=+nWO4c=TA`u z0mZ~05l=W+^dp+BarwHrhIgYuufoi>=R1gM(RXfVnvp;Bsg{dn8x1QURa>q*3Xogm zQ5~9qTb@SJQ?&ic^b>^pQpQuA+bUMQB|5GjJZ7`|_XXImM!k*%my94ks$G%;G~`FL zaJwH7p`GwaQ-t6?BZvbDiGuFXQ$C#s{Q5LT;TH~2rW`Pc+zpG^KxhSOqhge&NYRq> zk|o6063%#E{VASDwjkuN&HKeGf{Cihf$gUmBLhKlG}Az`a#uc-tO}ygBO@v567~E8 zPA*8#*7|aN%3-7I8cLosWh)Rb)j{=E%&|zP)Dp^x6m-zqI(`P6t9|TxI>&ef34l3a zA0O|{_ITC#za<4lG|gX%8%;YcF@wSrV+zu(;YeS8b}4`3qA=?#a$ODhf*t>Us%PWq z*V#>3-@@_ya@0T>_3_s8C8jMRZi*=>TuFuRaTQxaD9FTARWnw&sDkC@Z7+F4FeRVA zSI_=PN^Kozm)-w5qt);t3vRE)e-3mq4pO`+3lGuNCD;@-(p;9Na#&E&p>pu;D~2=G zrn|FHhR0R~hJJ^oL`%=+6s`)NotU>;RqHf9a>P*8e!N3E<_-4V52O!%%{Ig80D3ey zL-%M#=EBv`go`&&r8j+$)vQA_ffv&)A(O*GHq=DYJ>fZDN+qh?SWejo-(4}*YrD=1 zr4>ykb&mZ<OuclO{Vr(@K{g03d<*s7szj$>>wNGc=RSR;kF28td`;RrIc8zI$i72x zM(-|<@bkPZYN9VN=&oZw4$u*sb6k`t38xoBzcyE~Rph*e*MAeWd{jx?<hrvGrOR@J z61F|@)36Z^?#mRfWB)hj0D{T3(#>`qi%)w9xUO3`5iS2L4+-1Xl&lU{){_t)>7zpx zZb;U2M>Z%4exg+tMv@X1`uS->_w!Z8{U787oU(^syaY_%=08Q1<|fo@J^CA<?yy0o zt)mMJGFr&Z_vI}#du+&`?%Y)|Mm9a5v{nq2s=xyIeJ_#I`WL}Hr6lC%OQ$?<XGghP zoV~3ZRBZl*x+~wYwImN1QU|zHfBGC%Wks-UxG#kXOJMtNuFot_B!Y6{{+pC&ENH{h zc#DH%qPtk8_WR#KynrFI+#A?ZTK=)5=QvCAsPBaLc94-Cr;2;kQa9gKwX2O=<5C&6 z4Rc90D*&;K*puBYy7R9d6C^g3yzScyN=wn*>w>3g;b6Ac_9v`Ci@z2B0JiY9U3n|v ze27%PR5d&32BC+Iz+f@)Ibh$q!+ym9RMVft@g}lX&(D);#%!ogV$P&yPeh1t^RNjQ zXIc2uhsTyB&%D=+<O%9XL|BHjCTr?R^ev%2R~hI97aHn8vc!AQ7qpjXvs$D6>Te$J zfVZZk4Q6MP&vEla|DduFQ5=qr292&E<JSu<4q+^6Ark(J_R!AsIc=)1&OvmGx{KOf zj_-$0ZzXZojEV9Lxb<;Dx0N7=!N%N8_gv3#i5*4!-uSM2UJ^wx9&AGBjJ^1?|D#2P zUpM`B@Wn~bmJ^%~$OCgJmsu$6E3BTZrpHE3!XT+njAXx5j;6N;f0U@RifME+4mvE4 zlz-u#!e`=mtF^t9A*2~_9|^sv!ii^N5J*$i!9*#7d*gCwQ_=go=t5;Fdx3Y`BF*V< z^T|e$4l1~})IMj63(0a=J?SuD2_5V!IH0$eFbmX$5v5O-i1_?nAOQQxWgR}N$(7~f z>~l1I$ZL*|&N`T^pvN0fL7hWL73=3JB|tb$b&OY*g-<ll1SIFxB?QW{-%bCf&<h2y zU>OYWc_qj;8sJT4@$>8l3M{ynX*L*j$>_bHOJ6=CXHhYPEwgXF&OOwvH+hu|)Y0fF zbIPcXu9N3KiR%x96IsJ@ZWZ>$diD92&pgGbcR<JY$Bz@yAWo(}p4<@}PPpj<a~HvP zSTQYB;nen3H-+5B1dD3Wx*feVmgEH{Rge{1ngc#<bLk_Wd7W#*ruo<VxMT0J8Ps=8 z>Tx3l)Zwfty}GJg?F=xgv!F72?G#r3C|#3SLN9Pq#X?Q(8!M~x_x+?}aA;B-T2^&T z(3&(&^3O^^$O&EejI}T3R3E?vv9d=42EGG9y<g|bs+kV{z41o&s4&^C-NaF*R?z&5 zigM31dPn~+*;&XO5EX+CxZA2M6EtW*7m5Hk81XIuLW^`8-3Qk+rOIx`%P3L%sq5EU zu#uC<%o*>scC9`mD=k303&^z8<~gGqc_iaXv6?~yDSWZ0V1m#s4bn1nr)6^=22jH@ zW**>;Jy*U=2Cu?a-z+>kSym9Y?F^!fzc{QHlN|7DCd<20p9a<MeQ;!6IM37g5C>!p z?jr*~649@y1XqWZ45Xr-WEhxy5sA`4=HXc>$XmIj>IkR^;XJuCa0Y&GN0>IySKO?n z7zRGz+~|eg%K3zM&hL#vNdPjqOp<B{F0dIXXCf&$Q8QcfL&HK$a!ll7oJHwIGBlGw zC<{Hh^{#j}$XRUcs(Zx&eWsz5uSInBt!5Z@-sLRK?Ls!fwo)TVu6ypp6Z&VFhZ<tY z<dfL<Ky15U!*5d$p?+PzG#ISg?Zzj-nFOQ5<JsuRu15Vg-to9%+h^VuT{WY>X5o(9 z>Bt7GS`^hXeIc}O2xP`ytt~YXt_F`Baz$<b#H#moM-pt=KQbAP{XYzXU9{L3n*VDX zEq>jWy&}2#Ng;a<t&>U_C26=R8Y8w;jgx$R%m*DAyNFsVt!D4X7jbY;o#xN$d5=Sd z6RtlCh<Y#Aoo!T|OM?6a^wQ*IK{{@H!uo{L10J)6GL>SW`fxG^8tp@0MgjX?mo2c_ ziO1;Xw6L(e^ZJLlUX#fPZ0p>J8h4QG*sd)3tp|B;q3!Ihb^Sf*kWVK=T_!gZn{P+* zxhODws`NY~Hmk2E9K~NwY|4rW^&{RUIF}t3Bzi>bnWy)x!n~iN0M@XOH9ZG1-b>E$ z!_9v0EKAo2h3dpVZ#GN|H{w;xn(|=#2;EDJ5TELzAF&KJ*nLY=4K}l<z9PHj&f>18 ztn(cdCQI;v6GiH@ODbD_-e@ub*V`=n8AL2A=&<b^9}T*Ac%M!Mb0q%?48^f$hSan+ zeiwH`W0}~WvJ+|M#HAtd4?9Tk|H=;X-zt49i#&yUL#ZV+-|Q%BGTJ>K5#v=2byv=8 zD|wF|E~s^QFINaGs|a}a`VHb32XxQPYZ-d-RtfFwXi~LHup&@dBT~+${lmY$GdOv= z@zBexEgnqX6BOhnu^kX}G+O25Rc<GuF!lcG_YTTuEnF8Ua~;5sfj}H)-cG_?bkKw# zl0t!H#-1r@sQK`5>Ty#)kps?fDQ`)Yc&7RRY?CvruUi*jZJY@;QBcr}Mj@z{$H~>8 zHE0iM?n)>I6eb2@8zah$!~5|<$N>uIhWCFgm=K88+>Lv<7gi@b`Ks|f2Q%Swdhj<O zZOe7z_Vw6a)ZngScZ&;w@-dkd8UAsOh5vevx~&_8U|)^w|CLM?B_hl981()yGBQni zCKSP8JJ7d9w%(8Klz>HUWYIERhgU5Cs9~Zwr3SkYt^>I++voof_m0t(z1zBPY_nq9 zwv&pT3MzI|NktWB#;VwM#TDDOZQHi*tpCDZXWxUp_G!1>FSE7z^&MmM-rwi>_1-0t z0wBq&jJ_Wnm}NP=HBRVp-u{6>fq=qz%lGb{og~8(BDv_EA8<3k4v;+(!8{BFCbKU0 z$R}?-$?yqHJvV|H)X$8o(@ttL$VBN+nd5U8y$BGc@W)KU$7Lg-gZEN$Cl!h(Q8SFg zRp(tX$%)2aR~4$`(?4cljcXDvEHv)kPplNrHYS}9W0TBmdm{}R8SH-yFP5&BKjUjX zun|IRHNM_XNcrW#BSqH3D)rt(ifEKxbJ>i$i`M}AkngrS5CI?h`hxG%B;Jk2(-~w~ z#yG;I=syX%b|s?^!NjBRGh6g%z83$)*W2UF_?usLr$lZ){?8$2DCIl~UsC^0lM#pu zqL&G2${!j2Z_wb~_Xe0`>kL&kQwD{Yr)>MpnMz=XO;lFRR(VW>h5#o4{9@+3<tY%+ zetJ8Lt5oO)HYO%}hk5!<4&=c9m_u(x+k)@~NMq5rKnQb(RFtPRoDVb@KgfUm(bw6Z z#m{~c23$V@Q-j)S$c{cU+?mQ{@KS(Zh`5jE`VL*2(%cC?Ww}onPg8J5pt>;{^|N!X zn_?m?x^-~Vt(Dh2?g*^FG*C{;-c1ixRaiX368zKsz|0q?SMF;sB1^fjt)&OY--1;S zyFXfsYr>wtRN&Xy!PhGTJ#+&{PEx(PbMw`nd4`M&mXH}EyQF@sm1G5G_kG^dpAJuC zGnF)-oSWXvURVy+IIDKpYO;P`&DZtg(!jXq;2r$#?B_ulGFK#p3^m`sq-~h`yU!o| zuRecr3yF0saHbsUKWEB`5C5#X3)}lY_{Kx?JRGX?IxSexNxa<PxEYLoD?qoJdFZ}% zg_6MgX!7C1X>;J6XV*i3_UcLV%uim^4y}UhYV>R9aY7(+`-t+E14^dxZ7}}#rI+Jv z`4^O>_xljHk0p=LjvwV*;rE=QpD#aJC)~)xS-KEBy?eJJVk`wbc0#k>msS)xiCfn+ zC|D7a^<o1INq5c+0w+iCl&!VzBD_??k`-56`!g0LOR*@5eKM&6-wc<~uSHrp%(ufx z2NF{`us9z@Car!$ai^OW3_+MI0;ladmET4yvmBJXe8m`k#$$hpIa^uz;$8ZZr!UA0 z{lcxNRfRr++j@;lSl#_SL|ZK?pinFp4FV(Y6WAkvFYOvj!8p=yea|6qL|4<BF5n07 zkOggU)FDe`-AM@dXc$)4T04dE>l3u*1Byx0>fVuAFb~ZUH`UG~V`*YuT6_@tpP5JO z5pu$foBwk#@BbKy&4yF07PgtICqH>CRqlXLE(u;JR`=<hX*yrWBN^MbKl%m%bveY0 z{W24v#KJ1m19Lgn9kpvREUIoeLR3WLI;VYM^?bah2~&HJymufZ@KO7YNOU52wWf*b zYqTt?c@o4zNsO>fijJy0h!N@ssQOvNs<Lh#6d<7sMx|m{5iS(}4bqE57!^JpI+6=I z7iH$pXkI!fdR!K&n@sp*k4ZD7oujv0U<EUO6M7nC;gI=cQ)|gue$AL2{|S16wiLoj zOHSIzS6pE;2covC>6qZGB<e(Pi>_fV>!G(*fh;xCT2sFPK&8$pEKIjkbDoj%>L@;J z?un1Ki$lb2hPsJO2dqVgsgiZQf&TX&d24%)%nP83=xi3VbhpSVH~*|U0ZHB^Q$X%a z6KCYUHO-rD&GlbOi2p_$-(NPRR)CDi#P(kQ87q{A#E$j(6T8!xlj8WR%*qrHTgq?v z*y;n$?eHaFzPd)$j~q-rMhRlvClf{v!1o(9k5kI@wh!t|x?)L|-~-1626KjG_q)yh ziVeX#G672W5dcYPpSylnL9-qL<q2zwa9B5<u7k`k`WgUw**iIHPGF%456yv2G`W+x zK+&b22K`Y54yFUKb@wJ)P30)Y_0LzM6^`_nw~D(o_az^R9y8lZ$^?@oNd%U&I&kbA z=x&cz&)(k{m7AP!p-Jo0U?vj+snvfCJPy|UYm{z5JAeg`NUnpB!7B#~V;~Sy7!xFN zYp}rJrzL2l5kR9bwc@s<1L$V`VR*`bt3p8<@NM|pU`0%Li7UKQFlJz&_1riZp%b^d zN8NhM4{l4JNB4Tx?}HCmobI)C``1`qw?_BR7R7R+3BKYnN0i;Nrt06(w%N!?v&5)0 zN>%+mie>$46vMEgtR60wQEgT>|FhnT@W>oD_it$<b+n$kXOG4)j5g7SRP*&eHI@8F z<`}CRQIdf#(1Y{c#4=`eh39d;cb6;aAzvX{Q;LwTKiv14aWM)uH!}>md#2E?oMnKr zo}+`T;&#_U5)Egqc9_ljm{@o5Sy0yb?!A!|9nAz=9B>;zjzWAJ0D%T}1WQg55rF2- zHnzFm)tH9FY0#Ox?-#cEed)L_(F7A4Q#P?=b&u2~E%w7Z^JC4UJTl76+1&j8JOT5q zDJt_WMRomVd^N%8e02#2rpWSv;0+||nBHcQt=^RlY3^;Tac|KORxvsw;CNiy<fKg6 zBEt<`mU^#Sc9vPNrn=VaOZmjCMs4V|rieuivRJpgefIH)U0_G;u}?Mp+Vbh1)B8@o zZo#}nktH2xEkAqP`-@_NAxIl3&($lzoU1e|9Nkc51pPonfp%a)g!zh1{aO6th^W2T z4ECf(@c<$6!+FFGS4czb{f_64jz^0vOk=u>)Mnh4@$urdKx2%{Hjo1Iy*6kZrs+Yv zIpPsJ|Fkw~Ff^jq{p}|u`V3vAIz;%R3(uxk7qOeFo1s1MnyP58&b%+S`?l1UixK%X zno<?Pas8RXMTK&x$M|C;&f`$HF+U(7p^lu8c0;UclWa-#DK48>eX|SW)D!dZX!0rL zS<Qn{7y?cE7O&QRIq8SQ?%<rsn?OJ|fphxXyt6CkJ6_|G#2`dvcvENdwAAuC$xqPb zXeK5rT^sQXKHl{b36`>VCdKx!ee)I~C92^nTxCC-T`_;P(OjRoISxBcZ8P?N-y6Gd zuL>c8dG(`DD~(c~RHeo>3Z&CJL@kGo^i-<#BFfxoI!@Tr_bfe(3#rE181xVt;#m>z ziYsLZl*7&cDmx$j>+^LntwdFP!1p;R^L`DL$9QK}U2SUEWQ6$fqYBHoHeo|Wj&F1V zQQ-#LJ@T`)2(bvLzSh_7GFhy<!7y9J{giS;SEx3zh4H4Yzf$wlAmXNp&O_ob9NIba z4kafT@#32Z70GOt4&=?I5&Ij`Tb~kEreEoV#}!C&d`98G+JANpYx3jp76L~6cj)Zh zyQyr`?~l?7yxT@jwfo~0XqPox+=QH<H72t^u!t||J_84r`Be3=8p2pYCWWXBVT!X# zRqXD~UPj$t%kP%R?QSpkQf=6$MU&I1$jYah#B2l?%B#FPJ$~rG;L<G^D%<JwAif7! z+c766C)^rRKomzw%r6Z)(zony3u9qGOw-BOIAB1iS7-Oq$~O+#)!+vVxzlh{{1S%C z`Di~3?eNqz^KpW4UBn5q4wkEEyIMk%TQo`=))!0+hL-+3R+^^K^nItC>?O*}#|{Tx z>wp+%IInqKC2kb;HA)m#UM@EWOl#iJz+rB@D{S64=B!VVp{E>XowJ^MuC}ECS43gh z4LxdTIV6i&1^_L;A>L5zhjyo}LIxOAy;J0tGEb2RA~)5-kClTd3yIxQ!>Olj(>z)= z6F7T{N^(j`)$#Bl5kK;~g2i>?o;2vrG3sgW<v**Adbm@kd=Vg<=d}>r^=~Bb1}kyu zyki7UTn|(SoI9n7$BPCvM2b0d2GSHu2h`TpkFQW=(%tS`{!l!Qd8iU}+rZOxAG4x+ z8P|XL{NxM}{Pv9}sH}N6qyK@dKhzY)!CIP$gC07t(mw(OB@iw3F=E~GP(ZFmk$!#F zc6-<=i@HCCZqfDrAZmY8q|$t!V{-vV*1U?PE}TNE8jh4Gj|*kq&<W+9Wiuf469gFF zNz-C{=q4OpleGJ27eY}$S_aOH#-uTh&VxBRL`{OgKfbYty<&1F2X!MpPqy*~Ii%{b zG$WzPVrCO{_hvN%C-(VdLqx!*F5*v}h?F1>dN5D`)*~emT-cIF>aoc@);c8k2ytTQ zSIoFLHoMMm5CB!4Dm$`B!XBcoiH*dox)yW&NzX2^3JnI$s$82Fl2@$(Hm%3p5Ta2? zP!W+tEzJVQ)o@CbpHIhkQlsQz{YS=#W0I8gD?zyfm$kD8P#D`wEc_Q@C1RE;j8C5K z)pp0+9A(t-ZG}}ugy?;G)tzKBu*H@lpVb%q#E2UpynlDPbvhkHr`9{MM?%-juRW)0 zSQT%4#f$S!OXYk59Y|}tIRDP$;ZOlsCEvhrhM;oaq&EZ511Iy_&1|1<^XWI8`JZ1B zd)DfM3cx)ACU$HF{9y3OBYsH#PQCAlt~)Pa$=_(UW!yKr3JW)_U93Y6L$M?MboSj( z^ZBkYibwp5-koC}NfvQQg<iLe7H^Fnt*$AQZ)a8Bu9=P>UFk06k=Aw0RwBv04eBFG zqlrYd=d`OpMz!qn)$}2ZMYRi+(tgSf`e4pZVZZn0W5}xv3%|Tu$QSxm@_5AT?I>{E zOR5UP72nb?e0n;wi+6RyCBX+-b58I1Z#Csz>j|pA2e3I3zApyDYQN?bP8kFY`lH(t z-}w4;TrYxm>>WZc3OXtaU@h4+3FU5uV*hSuu|AUY$VrwiFOwf%8ZK7A5W4ow&7)D~ z&VLL~9wk+cmQ#OH`l7NiOr_O_8mFFr5a&8vJQqrMMW!2*g?ckTGhQ^10wy@|lL9@b z9-}W$fp1B~9gMCk5p3_1XpiUZE+>IyUm<-X<?khOYb)Z^A$zO2=rnw}BjJmd2at@0 zg%mE^toR@8w-!?r6Y(3Irgt-t@-<e@Uu=bHZ?aveKiQDG>5B+%F%1O|Zuac=3rODJ z-t)IvtgY>ocyZ8w#I`4A>IUpxac|$3`60?4yL`763>O1eP8qp$K8ch|Edb5KumsaE zAUg=fFG4wlkuTTQ<W?^IDNMV>C(45hAFKxDYy5<0>MNfpx+^UE`e|+XNh5Z8$SR<H z@N)4f7%a6$wglKd(uyY20Oi{gXKuYI)}gK$lBC+UGvxv#D5+ogkVoG}$7Yxg6P^mG z!m>ubYBX62d18k7BVQh5QdWS(79@E}Ci^Uqu^5ICZZQcu>xr7K*mgB<v{i548p;Z; zb$}$C1-ofLFb>^z(`Yp*Q%Fl-50y#8tC$j06rdqhXUB4+p*gj3qDv4zx%Oo~T7pYG zbLJZ?TP^RDit7;yOV7ngxWpRE;^qWv<Ha4SIrJ8l)|FVs-S)mv@F+osg)C*RQ}z>` zr#wupHx=Ga2%Sy+qSGq^*zKpfBwnmKZ|bfu`Ph)tT0KF@>Snj_cDF^$0zCXNWo!VQ zy)!XZE(Bec_F`spAE#CP^r>&x8Ej04Rh!M`iTn-OpnQaz>5#9HJJ=*RnbcOQ7lG)a zw7T_j7~lCBx-c6CoBY81Z)Nn@htL-+m^Q@Qhs7Do<pI|O4Y1PHq;<1-(pXQPuFK>R z`I)CM#GDz3cQJpZ`G83ss#TK1eMNK@8LA3Yw4wj}ES~*NNYplr2*8GyO|2f}m@M6` zzm9EW(zja+&o>Yyd-QFt;^|X+07Q(<<ZprmBw0{Hg-TsPgWsRUHj^OC^Gk$@6O~Wt zAyL35dM@6=u$GLdwCStgR`5#3KegY^yuk_1?mTKK*AC9V$mq^j2~OY-NhU7yl2wj1 ztXO;-5A`42qkFlDaof95I`aM;E=5}kNb!&vikPQ`kvE7(D~d2o6V?|dqL3f=phIkx z{(5l`@Ab5S|Mj&+BhmPXe!j@yh`xTjvrIH=!KlgC({{~bPzFO#;a6(ehP|uG(!AHq zw+hetT`GTv@sp~{LVt&Ik2)|90)r|M`5D1<hcJ#CegSyF&Kx?}b_8P$+Fj!T+XF&n z7t&i&YG$d5!p)djc}`s5dCO$mn}UfzC;bmam4qa<r12I?*^5j({)B}sDH-)3K_!mk zO<@Rae%t~~h>7dzs~G6mQvO`AmAf8nzP4u<CKpC6|3&Bq)8J!uHQ+208@r;-&3eg& zDDz{k#XQ5=qu*8%<38hSoN}F~{EZ<%D9p4(ZB<2UB!rwuy%`Qp>__7{0;0P}AXne^ zVbyg3I1}TN!<#k-H`U3Af0*E$5K&YV1(E+W{P-uUuV4$RwZ)S~nwS(g8d1ICOtCAo z5#QuJ6i+?eC_H?5<G#)M9P=&}*ArYl$4)Z;Tm|@uF7DpN3z7|{s)PKl3XmJ`ju1wZ zpr0Sxju0$1Kc{-$&$^e|3RFyd-@8j7`@y-55LouI(dX&6Lihj+u~*Ns;aWa#(SdvW z5=d8IUez>^=s6(6b?O0J-M*q<{^*q@0Sx-%ILM<t{AXgerOCQgm{vRXvtX#X8&*0o zN&3%B@M+T|;wRPUardXnyE-DiBJQ3LgMWYLWj249PVdiK%F~z8H{iB;OVe;z#`5q5 zSkM?1Q}KaG)t=KSJMibWg@k7lyL6gOj%88)j#tnlBahvxx<Q6yH&0S8EI)Fih_|T& z)j+wFL`m~J6MVGkx%UsVO4X-Xoi!Dmi-I=OpS7BT6kz*!6t{1XE@75bYr7-nJR=7{ zc2O2D4HT2fII~|FQ6xZU2K$i7pQIrlaMl~doC(aRe>^^2t3L00W0<p(BW<CrEK>fi z^FBzuZyk`mNLAtUcyg{t2g2I9D3^ZV2?CE<O#sgk{~2}t(-xt#E8d%p?1KEJhn)+{ zhV-c0(Y|4H{89Un-Q|HBP6o|3kGXoWvEbGyvrl;K+Kwa!i@%B}8VMEEr)3ofT@8_% z_6A>`tnT@<+TP2r5;Ty;-}D*+lQZ8=W@Mq{D$;c7>#RLxXEMp4Bi*C?b7XHbSqqSg z(4~Su@5@6AGB-t8b|+Q-?H*yO68n!B^XI&`-yU+>@)AssBxH)E<3^WA#AT;Yho>~T z#ZTJNC<fS;(*|MS9H6bC)?O`t$sFx+h?3$E@_i@?|8TZ+I!?{h5IvfQ*#x<IAEnaM zG;Dn9Y?+~Jct}?BoGa?SaL#x*dpXG5V7ocLN?<y$rMF%UlT^Eurxu_(hb*`kCaQ5d zhT$10=>?@QgCvr3p5r1)X|Ha2Xzi!#SIZQr-ktRgB)W(@I18S9zN>A3+1h9-5O8rE z%254gF7exvD7pUm7uDOi&ZA3-rKLYSKUXm3?BYC#m4X*<!S(vlovpfc#j*nm9pWQp z&I<SgR0rUKqW&}1Xr&2-UF^|3<eYBsdJhu$Qi%(4imvxqob32T%+p+p**5sTex}~D z$^KW2<}v6EF_^ZZ?EKWk;^EoTq4`@_R@>BSrBHTXy1yT3LaRkARr!=&dk7#QL`Ns> zahJ~qJ=R3x8}LNwuU{@}rEz5imSBbb`iJYR;3BOWyRxDYk)Y6PVg*YBi0`Tfd9_m_ zO&4_V2mSnn-6e+PBmFvMu;MlIVYzT@68)LAm>@^QS$p-&GG9Na)Ugc;v&R?)ey1YI zl`L`s6ne6<zB(v0JUy1UKaue?MACU1Z7u)mZ!7Y!)PO+R{b1nX^RMYAu4IF6AGf~4 zMCo?l_6KsOj6~e`6=NY8)@<q=$Wr`*_h(xaL^A0`vIoc8GVo0HcgNaKgR#%dYA41z z0|Mx&CaQza#uobnsgF1;ez88%NU%l_EBDr`KZuyxe#j8a)qVn{wFN*&2mOSCc6bU( zd?_qzN8u>*;<T~)HUDMc_E7y~(^YJi7b8>H{kZ*dx4jl;`cKVsP=`p`DnD>kW;r(3 zoE!MiPmu}f?}JQ3bvOL7y}7O5t~U4m9#^eKmg{_@@`1Wtzf%&=tzz1f)b>;RkHtKM z>=Hdtjmg2^N}}j4);;4rKh)3hxpU8trzEiN^a79*9_Lz)0!=3v^@3>|oLjh7!YfD` zStT(#;`2%uhE8yYCan`P>FJsphmcSJ3DaGl%}`)Y6=iJtDH^+FY+?y-Siotw%*^|5 zF>f`Y7T%U1x~xqVk3RqHWASvOmCi^{SX9^uKVgXTx4tP@M7E|yNJ+?K#MC5+;4sq! zC;H3`17+tA>ywV{2bv|vs}}van^sVUjlEc|tn+I>R1;{#d+RnbMH{j8#>aVe#7pMa z+WTh~Qz61+e_-S8Vb$favV_>d?1WCc=hHs8_Cs434}}`MT|r0XC|pP$e!Rxi4;Y9( z@iAiP=@FE3qz4#N5a+vIE#=%B&bJ>%XA;0mpO=22fpNgP+eh5uC*R@aPv4eJ-pstb z^f7$Zn(XD%tQ}wn@{Q9}Q10+9_7W0P@44w>H0H~P{NfHn&XriwZ!z2-8(4;fWkgKN z+*V!LD+Q8VN`oF|*baKy9E6sHKIoz_^)Gj;h2%9k$gO%-m)Fw1MsCGnCjc6UTjFgo z-^QmnuGe>jvHV__M6jtLG@51YAr<K}`jUt-)9w|iZQLKv5<2ixqat!Ayj(#E7f+w( zhZmBp`?rYz>*B%GlMQ@>eVZuU95hWsHY#MMYx-Y3O;a?6r&I1!j%e9nAR;L!xOUKV z3@NyzCt{-z8W5^n!a9RHDd=L);`&dH;&rDZlRi@k|G<?N^WGyC<8*CKRWTeaz0E7` zJhmuh>A`(u_7h?D!IJ~oZAC$NG?~uvV@FfJQclg0HvI-U&pLnlIr&jr8WJQ_SkBJy zg;;L1J-TLV9rjBS(jf0!JI{f$zUy4kFTsmhIfjiOUDQh#tan#sjPhIX2sz13mE4hC z<4blvYTr{ifD(d1#CEyZMa7z@qkYdhc9M0Qo61^KRFbESL5_yiRB`clb+Sp`>3Ny( zW0v-M!I=2cnb@&_AJ^I*yzaut2e8sG-KpIv@CT8RyoxRDd+)`X9tPZ!rDT~+o8NiT z9;L@iTqd421l=^hTpui`M-2B(F9|F<IS+u^9+kfP%66P!L^xnq&3&2hRRMwd@Dtv| z#=<LIbpRRhu<<I!mo@M3hZGWERUW%fQ4uHu;N6Gy_q$WO7xTTo<SVJ9%IIm1;d+$Z znxwq^f;k28mH)RMK%76B=|cIC=3x02l(#4+_b0hgndeGYz07z~bGn%-`lrP~U<gE$ z_+N)W`n0C5VecP9=woCTZ&q{j{ulAKXPowXwx2OJMG}5AGj!^&d+3$8*V&<~KX7mo zbqRvk(t!nwWn*8(RF5+s(Y{Z4W~1I;KKy*6pfCy?7&vyu?++<L9{c)Zj*f1E@XK?c zNFde2aBhaRW%s<tCd^zhJzu_ck>wHm!>)+NuW#}}!E?2{lwrG^zC+b1gl7Yoka}Og zEt4bWk#*0a(u|bDwt01H#0tH@o9m^6bQO-^gLQ?8njb;8jBg+8uDfO7DB1P@%-3jF zS~7JPAcUe{a|CH16>Qy9pW1Mt_Pn4g&hwD_CgnUw2ZNyye`rhwrv|VVKhteH+f2vg z%lz7SpJhC9`F+rB4SUn4<0OlTfZyt_JJk%J>$j~Fg#L3pm_2k|1J))qjY(U=(q>p( zB~iIFNk$_@gxHRrrU@tM?@GtOe?{r=QoU!areC0v)W#TtiQ%c$&VIG6A&8~*_M8mA z422}8wmtMou93A;g41vNQwXT*qZH5fU?TFx5qf)9kz+#d8zC~#uB3cfVG#=(7-+xg zY_)YviMaDh)^cZY5gTg8;@1yCalgXOEhS-x(txPpz%<7RqgPXyRH$Ec@}9dFi%YsV zW%FJGs;r6<S}S}LbQb9P<}(k=kA5v@-Ba_Fn8xc|r?B$X((2EY0?JWGTLa+Q#&IxZ zgMvP|rS+Njkrop_apq?T%_&;qqVE)jIAGPf3JlLc*KutVV4TY@$00}FlqOc0Twj12 zK9i;{-JhIKX9B;>yhM5W_+bGXu_TXh-E8x;3p}E{nWB;O?N0eG*MBYRE)HxmDKb6j zH9#)r!IzlZ;%jqKdaP|P>z+6hEO{P!(#s0pmBcM@%tA_3u&?B##iK5;w=LI5{;dSN zt0Gm@$!!CmKhgS5B!swRSu$CgYPrX#e48f!;S4jS;`nMb7i(C1K0w%aA$2;K0n2>& zFkT%+6Qpf6wKnQD^zD9%#ghY#7E3q<(&xIbDLWwi8)%EE(YeK~Uq9Xe<I7ccI$O(l zN1pz?(?kM7N*(T=jp>Y6d^_-_aW{y7X_C_S#Y}EN_&CtSkC5Y>H&v^{-1WZyWTX7) z>IReB69HPm(35hZR!yWh;p}SqR)~Y)GjKf;aHcE!JG)z?AC{tT#wyi^l~@wsBjws^ zqv*WDU@iR1*y1wSiv8r9a6>R4iENdMC5V~m^>g;(XI~jv_e;oulRkL?%Cc^=nnhQv znVJqft)J+&S8LE%CB~8#`@%4*r-NWFoSr)ynYpMu8oQlHkNXa5-Q6Li(hw>`Hd-vs z?p(M%vn|D~*u~(iqQEqZnOhYNNAG{=hcn+P89dGLC(m*N^DI({g<}zMM8Fq7d_gQ} zQ8BnE`tIp(b2}UuiubSr8p=`J99PYb7mKUqHsc@jJ8EQdb6#iXTINFRAJ}3yEIaWv z?p%aUhWK#3m`wd9C2HNcdB}+5&~WDFSQrASs|2&3k{PW;qVDm>^jG{M<UX#<?iwWN z1)3BIGds6KL^ARC^_H9~Q#lhnkuMw5C)%M2j5Dzy8OFx0CBr|?(mt9*0y~I0VhJI7 zLJTy?BDj=!Py#kQHAI@UxmUkL3A+_eRe*SfRIY=(IOe3bxpZHLz0migHt07p?1Xl? zxiVTC4JFR;wI*}|`f)s&A&_R6VMKuY(ZP)D?(evEDbPWWHE;&iiyaeiAixG<W7F(m z%PV8k^2~=MgS3MdTO>BcHe)e|?xlC`9ETY#2V`^6vxmB3s;w4G=uH;v{DnyH@DV7@ zC_FWBm=tAlY7&k#69LvHNG2j7q;0=;DJ4Y=DBxvErvuUuxJE>hOOyOeNpXx4{Tr3l zqA|T=o}dMj*Lw-U7B8#-%BoRAbKZc~N6ELEad(S%+aPSmtj6p7)`%uVk4g$ub^1yF zgIj0;m{pC775JlKzUaA9Dx%9Aw2W_1?8>M)?A;Fw6~J+WK;*Wf{BiwI1yC2EiyC_q z5OUEHHL-;I?JFxqA>B$jwQ!CrfAl`5eSXS1aP6n7(UfJH?GyU@P+jr*4G>TRFBRzI z=MgA7s7+e%ROLI=)6)bM&5ihuc<Ddx<)yTRLniCRVe>?wsv)Fb%VYZ;-kCGo6v|cm zNg#nMtrf@52Kfk_XK<`EIKh4pU4wax=qqoF(TML7o%5y@60njXGk8pBaumL=n@AMo zY$1Gc*#gmVhm#926;{|9iCZJCEUcL3N*5C?n~pP0ks3bjtc=a9{W{#fnO7@b>GC5m zVU_qKWYkBdaY+k030EMoD<5yj+RvcAy3mm<A1MCppSJTS<SC5HSP$3^uS+DlZD&Ag z6b<^8hp0RheuSzHREckXJZV)T#M|JH_|+CrqZfb%#8ne#Xu)<JN%*pg1B3I?5#7mF zJMnaceRiG-N+86T-LL9CEU}^jclq-EJaqL%udDlTBE-Swqdrsg&{Sr_mXAZ|#}7~# ze0Y&|0J=1^ZUKs}ZSGXf5Bo3&Fyx(6?#K1v@4=KBXT4L#N&Ic6K;foLk&0r%LYU>q z6FC!*9YW}`O@@ai&q{+-ts=nKAh4Wp>-a2ojqp<)FhQQNk{<EWHbv2(&`s07((07N zc<_@!d&A*^&k37fIn)!pm9(h^I&9rdSK<EG-R4&n$_l@ba%pG&tsdgzr+Tyw4hgFy z^bv&Wk3wj#R+bOj%Hx=~txTQ+0cH1{-sPl3o30Q8?Rz&_oy(=r4Z*hz#JR;)=0rhO zY!kCUyXTjm187y=FbZ;=<u&H~+!wPeW}CusLi~E{)(T?wJL`8XQ3e#u$9fRcOxO@l zU<GoZapff5z;I4s#%m&zG@l2YWZO^fP@dgM+!l*1f8EQmJi--I!5R~0g`Nc%_%0e- zD#ZZQFC>887^gI!SJk(n$%2}*&7KP>zTM{sdDL4E^QByj9)_AjoBdW3j+evac3fCY zcmoTpqipj5EHT$)Q!l5R%O=EzHXZxE+@_>kZGhp$C|-X&VwUQM!c+aV{7Y3F!!UBH zN*oN@l3rxP!e}r|b~SKMt7aCDvFXiZAD)u9?!8W*1`*x6Ne5sV)A!p~+a6F1Yn`LI zdE;NWa`E3!H<^BJM^7y`0Ea~9pT7?9DaoIdJxI+~QJa!@|FV6@CSrJfpE31z^Q0Ib zN!D!2br=EtZFcmgvM%`2LanDb*%>wz?c)KPbyT^b+m$L%fys53suE$|by?!O$6I7w zb#fZ!h6g`?+V@$n$czVTj(+Hh)tZR?95Miy5P#T_0_Eo90ufor#1kwm6%kpAM3_83 zga6A|kFQl>9-~xw9e_srC3UM;PHii-P0_TxkLY}2gZHhbL2lsgAqsm7p!g~;N#J+! z04iDqQ<y>xnRp{3Fk&4Dyn7E0%X|2OU77#X+>*f~%f=njt8vks56Ws&3aq{-Yl|I- z$T@sQkzvv*$ec{QPgkaBkdqsq`Nec^h0LtzF%iFf!z(BMvawcO)lXa%%cFJXeKqxq zNT8+kVD&TqfW`*klMAYP&7~aWI_sPK0>`}z7{7nFQ`@ZY{!6eXD4ZG#D4$DTB=ovw zBokA+!^0@kT8zD36l6mLU6xM0(_W(gV}b}r*!g9|K$SxJkk++ww^Lks42jvF%3So8 zj!)R2M6G!k0nFD}dT6W>LPK*%e!6CttcOADbdB_q#BM0m<R$OQo!2y}b|-%db~63* z^rSw|U^HT8d4bv|RyqMo+DcwrtI<j*!Egf<giR=P5FRE7aKV&+<W(~Zm6Wf(b>Vd` zs+UvoRTkS(Odm?<-boE--VG*ig7npW3iaHDgi#L084L>I$ItVj*f*ReW$O_c)+|3L zz<E$FwStIes;r%f>G)RQ5oCKCLnNIl1=PBl6M=UkH`n>p5`A4?^cKOu+<{6u(O(7f z0NHZ}iAN@_8I`iMS2(@Vy<uF<B)3dXoJiYU<%J<Tp5hg=Gm*b)ap_KCSsQi{2KMjn zIX<p7Lx!h`5vf>N*q2M7HQO6FRTz@1>wInRDczJ9wVJ$Pwg*@9mt)|*`qyog`Lo<Z zB3sRwHF2H2O2bC-NcW6`MazQU-c$MX${&w%o3L+3U!S5|)AV(MqFlM1a3}n_poySR zM|@YwsJc%}+1Qg2#(%PE<EX=7{L=UI%$xp_NW;wuW*Er<15CnrCg9Z-(m^l0dB4It zgQum{$qPlv5;O}>W1VTG`Akb^q;NkABe9zJ7naaq293%nroxp*_T#dZ`XZBkf&g#q zf)E~u@A6TkB%qZdGsGmy$4ONyP&Z&OK3l^8<kQ$+1}B>XrxibxX#;QIB<Cvw34g>+ z$?}ZXC2cdnL(Wr?H5+Y%@in?dKxftMf=;)}=VD)aOKQscV|OY<6=pg|Xx4q}s&VJO zd9ezH73Y(9-t?lEp?65NAPPc#nbTw$Q|;>*#w%uxwBXi5tx9oaDy|NdOjKFUp|ZO! z7DXBZbp6R2Hlu@Z#<o6f#go@?O{2{E^H!?i$`p`#Yy;t{HnEjf-ZK+u^wmZ(*Jy?# zn%m0yrTwtx`DQ$3LWtbJ;k6$^>s%itVu?CzhI7f=3_#sSk|wM{c->gUCG6?GvK0|v z%fHbQ#(!jED5GS=fkYS$5cGJ{&(aEIFc-4#nPpn#+^P0miW<|2&^5nmQe*@#@Um~D zkk)S?-iNww=Wc%&$t*2AeNG_wge3^Pzm8zw%l<>}iPp6ov|CtI`hWqzs8*Bmuxq5^ zrvpt#%k7&B_!IWnAe&*4AnQv<j{NRuts`kw-?=GG;~<VpLkNEZ5Uym9K{t|5mqU4C zke1}7+qvvwal^B4rKY8AX@0=Gfh6}BCMUF;NDcP!rRHq1bdQf&(dlIbQ-~-wieZmM zF>s&__#;vOB@LOK$hsyx`n?of#95Wn?;j5+;uJ}_8#55vS)#MXct(Q*d2HErCa;-D zK>NLpqz`E9JHq+6^d>x{d;P-2=rdOld+z1XdaGx+FItZq{~l_BSg$%;8}c17a-lXk zv#$z!!@K$Q!gd}(v+=!bm|!mrc~b{rRl(8aD!~T)seTwwtU+c%iHBNeDt8EQhy<#5 zYOgWb_^{^(tm40Xyk8lWrnOlc%+KY961scr!8hSmk}zbZ_SPFiQ1oD4D#f-VL7}YW zS&<hk(F$l4pu7zup)ArSl(YH^Z)&PIurYRFssr0>>7NGeMo>H&fG4g~O}Zp#*_x@a zL~-V7=?KPN7{h+6@!$O%Y@kS@!Qbo)kL0IMxi4*=`aS*4W_3A<;k8Ktx3+OrBnKCt zF}Ds`@a=3DF}t`>XMD2xqQ5Kze<Lp?&1I@}N7n^MYFF<!tG_vRcF9_Mj60wcoL=D@ zgR8^Y|J0VXi>mQaY^fZeI<;Q!Xq!=(@7at-1l{V{Jo&0Lfq&ZOlTYw4X3<%Iu4(C$ z)1SOUu>r>Zw5GOBlRsor4z>j$VUN{5m*dN3gN9F%p*6#A;LcEBqS)*_E$y8aaHNGV zd2Mw5ocbMliog*f6JM$=F65IA?U^a~58#oSkk)*0ua+YLzQe?oU;Yi}9Ku3@%<`}T z-((O<QA71FrIP)>s8lYu&^)frqM9&C^%GgWcZmsbYn4{oGhr=lE3I`1)L<Zw7<?zG zuk}1EOECV?CQ+Z<+CBV54HBQC#K^!wrGO+MG$HNYFMuM&c*;Z_Mag_CC$p|4YQJ<T zOrjpBzuc@OX#hN2hs-N+bMOkjU7@6CY`FiZUvv0?7aBS<$tT8+hiltT*NLRVk1bI_ zPqkS`|8|>>%bLmdoBb+WbG8mTD(THO>-D3{vSvu4o3wZ!6edn(xLf|2oDaAh42sG= z-xO+H(=c0Uc>A=TPQU1swe0=KuK=>-VfKta{0>S_n$CuN&~@rX6zBmAUu2gM8=BG| zrJ%KJJ$_Bk@d+eaeAt<{-&=ITdhybk76M8y^mx1j+Pq=F`9w9K&0;Qf;?a-ABjQ8Y zl3Yi)I2QFXiitGh9o66v^)mS-qr@oOiSeu9vUb2)pIkszkad-{cbx%@2rQ{os$LPd zqEOXYnokcm17ysS9Pur|A7N?p=$lL+NzHUz0a!_HRX>8&W)BQh7l;irX!MOJ^JbiM zFx<41W}l>G5M@F!rsjyI2Nz^k8n53vyv1cOo_E(#6nCHj;@0mnXnp<Z+o%JXs)#ch zuGSyN3^U%W)X*E#W(#NCF)fb@i9{e&zQdog?f>N|Y4yi`zW-UhlXJIB5S%q^+kBjm zV2+^wjA|=D3%5K!Fc6!cLH}x^RfHL=Go!Kz3*@)hl~QKd^kV@^g=o(v1Ly>Ki{cAK z1$X_y`?boiE)xR9lINEGp<Zv(*COx>*ySq?FPATmP(=O{xeP*UmubH`^qYe+bRl6W zjB_+Jw-_O{N4pegR@_$_Ln~G}Zf3X?-gfUUHa`Wv=;;fZ_aS}N1xKNXR*|~YGyyNQ zndXS~|8IsF2==8Gbn2P&Z|kQ$-g;eRK3meJ0V)W{`<O;72NWesROBKhTB3bmi*Wdl z6;@aoB<#tB-PV|JAnCcckAD4pdc~byFs^<Gcw{;cfFm-Tl0r(e+DhCm7r)3s>0RMw zWkiuRleSSL90~4km7!zMg%r-!ZwI|!OCjG@JG-NR5omPDdF0Lujjf?E6js{_jLlL8 z!z(kLp3;<}crNbyf#n3Dmcz{o2ODk1gbuZK!^8Pd-9cpkd+ihPQgezwwY&gpY>0X& zRSV^9Z<d*jw$)c;e&tIlC~|1NH!mCEEiE$H4^~4==M2wWfXCId=b{nV#P-mUJE2>T zHthxm5K$cBDvs^rOWW!6=(E!9-?QxvW(6Ew?)dU>W1L1QVmu_*!W|r%R;;%<FQ5Wm zP-;IdW=?b@6|yQF5nsGO<&&W@|G`F*3nKHtMx5Z`i?j35WFb^U4q^5z=<!u>AR_m; z;vqS;;9|fTu8bY64)mq}zzWZ#J;nz_a2Df?WeU18H!{>~xNzmc!F~a}b}Fz6G8Rht zldW{@k4{1167@ya9v!MTJ?~msEvC;@Hm=Zjon=f39V(Tm`a4EZWPlFE1O~->R@}nP zJO!|Bf4zWGZA^jG(a?7n(J1u`{%|(j^Ne-7t+<i)R}0kP<v90UPbmzCqjF1I_ZBmE z=yJ>A>^^=@FNO1rhb3Rg1EKa)BHQ_JrZsioG{W}Ep8Z>{K#tpU>I>9TPF$!MKU?Cq zoyK3E`^-}c5{+dS>cZ0-_J_NgHk!Q0smjwF%of}_@Do3JyFXiR1y0yBvit-WwetXB zyH6UAQ=xXx$KgYSjGoTtu>9KP*^*2BJynF<|CX21Z5#J&FKti*7Emq+xKIOXg4Lvd zyZxj5%K!Nn`7za8AwF~!eox8j=eG))Cc@>NTBO4377;BKG%zlNF`2IWHKV!UAVH~_ z149QH84}j%Qa+U0od)MhgYZV<Hh>#R<5(ac3OVSb@?-nrLHD*LCh(!X9Y4(4K9qVG z3Im)M>_8?4bEnb(ochI2_?sW)u^q6O2SwbGsdvt3+F#g7^fo@6j`KL6$|9B(<l7gJ z55mk_hx_8GN-yU+-45-ruX-LJOj4!}@So7O&EWXGn`NtEd{m$zKoLrP;g;uvjsK_= zH0|VZk*f0|FgkNTc0jd8>?#)<Lj03QGs8JH#}z9atO_`sl}p1Yg@t#W?7`UcT1cqq zun>saX4XF+`HcZ<cE7}Nf@pB~l!3%sP+gX$Bj4tJw8Y0vF|16mk_|W4{c8eEUXL00 zJs}VJi&5x<oYKB>;8YL}18D|^j6QnCy=^*|f#|6@j9CKUjb68=@%cv*N#4<1ra)*q z-5)R{3iDqdM(c-QZBlJDeVf!ji3HxipeTzqjbFR2pFPlVL`2Sg#<J6d<ke_0T0cvX z|F2%{M@or!Ku!p6m>2QR1Fi%Z<zXcqm|4U7yY-Mu%lOMS0gisV6&CG^@(nK59H%jr zqV-)!3X7`)v2lNQ=oIlH2(92MS%7L(C9WaRKQdgxs~o%s!cjtD`7pp^TBVDsf4%1X z3{z$qM?sO$UKUthC^s7c%5fH9D^Y5Bk*>3&KSOC*J9eTuLwSGEmkHHG^)Muq?+psI zJAFX=JgU!E=)CX@#DJ37ZBYEy^-Vk5eCnATCfBUlzsD@sqKw-zj*S`FTfas2T+pv9 z@72+kvUA_fBhY6H6t4IItrtJE4W`R1+Q`68rVQk?U$7!Rp?a2|M@)$%j;2%D%Qj^c z$qwM)5T@zerpW&xf}=Mza$XI&tWuH>h0dzPQEKfzY+1&|drN<;L8grdJrDl%!i!pz zqo1Svfp0$j&*2-fuDeU6Xr<uEyxr`)es+3JZqedOpi0L>=M1b-`1H$qub0C-n7V6> zxx#7i;+45_9rzTmC63@BD0C?6%wZ<7lLTPMsN*Ne04xzsIeYTZ1!We5iFH@YT;ImM zjc|apX3|ASeqLD%>7-Zvz=3F;Jlb-p5u%o&$NXUIA;(#sxO_=PA%)F4tv-w5%IEu( zk-O()9jSDFG0?ans=HA>E48BwecdJ7_h#uYd|_OtD~%6}zP;@HQBC;LgnBTi#0e_( z$hUzi@fHp=vXrfbnzBmGU3>%=T@C7b9KR1&&*85!x`AGo!A_(GJl7uQsaC^_)JV)u z;f`{)LqfW746J^z%B_s*LzD)Y2LTgnZk>4)>0ihF<3f}8AIA;!9AF^?GJx;%+JC&y z?`yJJ`$YeB(&;}AI}{vbuNB`*v1$)Hv)HW=a3WB_FkmCK_-Sk9VWO?lg))<9K;sy= zOyh$8alOmsBM?@5_R~*DKfn^38d$FHAa!g4p)a{}?$^4rNm<#3^{eq?B7WWXy{^8( z+n$_ew^{?@ri~<9`IGl>&A+bq4Zv9v1${wKhr|jf@8VgnOafmW_kYF$-3w3Fj$(^9 z!PcooP=Bb15=lbEH_us)(@`=-!N%bv!Xm593hF6x^~RaXB0QpkN~i*ZF+#v*cQ8FZ z+<hM(Tf5suddX)@q|7?pmZ^GtX`5LP>T@LFLy1k9&^(4cZ$AUfwrM$@?=T?wvR2cz zespVLe~T8o5^?<Y5&t_Zyd3sJ2ph<MIB8<w{|^#7tjMu(j8i=;!AN-%cQhoV?`>#o z8a||N0zGw=47v^LDm}DewH{nzh{;bar0oK6t!Df9@i_Tr;qlijPKa3^AR{hOFFRk? zT4nMhi-u!?ziD^0{D$eY?GR{XTzDJvL>l_RwvZZp(?D*RK$;*HR3HvzOReTBvYg6o zlN2nn6^yMjW1?MwW}VmhBNY;A`^ikKv~k*_d{SJeY~b50R<6j5Z$S(goiW<*_pUD& z072JdEP24dEr7#0vAjS?EvzfIw50PFFKit{bj`enkHXNq-L2N9S`g5$HTBA9aC9E% zdSIrnjSUEf2P9LO05`yEI7j<A!o>Q&YDEc~iGqn3wiD>1(EncJu(s)mV4{B+BKuQ$ zKzm|;yfvZWM+8?OS2|+hO>WurdN6>0Pqw>PgODLdHTgFGLTfnJ!$Up`ykI7V<c%UB z!2i*`fU>${Al#kHU$Xk6nJ~rbX{o<#1mVc$bc6|~2($K*YV+UCHvLQ)6)JOz`Wdbr z_mN%Vl~$U2@H99vv;0C?NOei$2%xhRjEdj~x@rJMd=O~RNfn2LnHN(T8a0g|Ppe8- znOJpi11HGkk3xD2NkR?f>Kvw;tu|=2$Y!*Ti_hikHSw2pr_6K_4mFGRgy?Y@jWl2S zYj+b?+edtmk*~X$M9MRI^nKblewEBD5PF;QRDiR=+2z8`>gw%fDOX16)U#A^U7B01 z!O5dUL1~84Ib-b2ZlX2LW6#@Jc#A8T@u@HD%fp0)Ym3o9kiY=5mHbYREf<9Q5KDaZ z65NCdpi>A<W*9Be#C_y2G{45R8@@0A==a-wd#VSW`F2IT=t7trmPLd-+;q10N24<k z;+2~~^L}!MHjR0%9j>;LA^mgV{P}-XT;Gg7!R}68sqRcbc#(FfASkNp8DYY6{iSv% z&BRMwz*s7&8RU!H)%zz?^eEH=<MSV7Vc|UG?X5lR<I&{GxaRnve6`7D0afmyi-@To z_Uz}9uidENw9JxL&(>%##HF}ai_=F7T|HrH`|qc!KD>8+!#S=Xh9{Gc*ZCSpOCg90 zT26cmVC?vH;Q`Y9m_ufB)VLD$FfyQX%cTEx-Q|6`fwm#(JFicE4RFzmd4k~<C9(m# zuRU>Nd%Pw<Mzkulu+5HjP|UWX*BO6N=;<^VTZf0cVny8c$of51AfhF5G6}NCGQn=) zG-JAhpg%yRcH&TC9FwQg^LZ~m5GZe_cN;rWf)#@}2RN`}Ar&Kl4P4_5gQd|24*DiO zvNYG-RFMBkY@RXynPx<UT<8%K5hW0fd*-FC<oA~xr)I9quBx{Dc>E==OR4sjy|K+k znEn6+OyAsr@^N^BY<*`yU8H6e@d<@Oo{y}+NGJ-%uSR?H_v`$CTxo$=o5yPcMJWM| z+NYopjwAv7s|a8T!B&X^3>4-d7up`bE#nf(wB<d-PL{#*Zp<nzah0#F5(JDwqQD<3 z=CnOfYLl?B7ackR>t6_!MBuN1u$Dx~`y7d^*QMN(zJ5i2k9|`FLy=im!1Qw@RQ&7A zu)<pszFL_D8yLjXG`JJuq6lnkV<7WWN5PYw&D*)X8(t+Bb|Oyt+vMwwhl!p*GmJU! zge)Muy8%2NO!+yDuBHZ$qL1Sqo8+VtSWq0lUwxz2{Tf$*se=>o{~0?S<ae@_L!5(@ zBmEs8!>9;{xhG^`i0k|gYe_+?9y2OM|6K}c7~%B!E{x>KC87e*)@a$c51y=T41n#H zq3I+!+*_81lqpDLoz9V_KnW$a9$20PKpb>NGOYUz3^2MC;Gdw_m19K`T6x-yYJdOZ z7yMB>JfSQOK-sl9fd2|T?t_~>G`PK8dZ$k|bU5k&B^o7PXKTfyzAf`n&Zo2ehQM#7 zXfN{_xG~bQ#Y2L%8Is3~4G?(;wx?AUZ1;!MMIC(aRyT9vL%QI-72IDbwh-sVg&3BV z_rjJMl9yoC7Bl1_qe{S4`IWuq@#vZU%CY>Sl8vxp12_O)XHM!biGch-hHey@O-Tb` zZ?U0kb3{9CGz^?}zU4(R#4<aq&V$<D3XZsWM)bLVLcMRg|5|Q@JZuEt#{$u6?0@#F z{S&ROlDy(W<8v?vLwIhhIW-R5O4M@X`k_~y>ggz_9}y4=Ims2KR<DR9K=ws^a%aSf z*WG$EyxA&?p{dbje+ikPG249q`wnfUR;{z4JX<?ce~@g^C*2Yxf#O;5!~|5aIN<I{ zS3@HdwJN&qyx#<E`<(B``FjU~dVf%#tj;dzvZ$z>f;+F}*|>t#`(`e6!-bzP9B1Ec z7t%Q#isj{HS6inWB(I^V{4#ipP^bi7U;BnV3Kyc7R)7mZh3?^^XKJ36CW*xfwJD-6 z6X>Cc?g#k%q~949UBAz~hYz2<70$hTIc^2EVR^l@^8jxpH?n)i4)fR*Yhf>;Fh7Vn z5->h2r(tS)sFg$SaV`g&NTKT$JJPG|*1G*vUBk!4i7`7hkB1aMM2(Z(CF37RorFJ2 z0d*lGb}SWNQxsihAij2})ZG6^7z(}-oixOZ_(zITQ-R=c61MP5Cp+=1`GgkZz3V7L zoU?TSmQd&F5bRw2ebLQT#OY=xC1OEP)vqGRJ`~+-PH7J*MK@|-p#H)edj!*jUZXFz zNg;u~S<kR<Oi8g1RQ?%HQlnDzETU|vp2)SrVWew`qvd&A-(?T}i#}0vKqT5t`%GbT z)g4H6fZ*k5;6)_;lcbe=m`-c04{#i0;})Fh&~jTtyRN?-K0=rD3Zw7kIIu>BNp$^H zW{wo8>;vvqxx0B2g`q20f8CX0wO+oe4D4c49bv(!b*-^joYL}Q0iCbxq?90rmY_)s z-=w3?bYvQO0ZpCbLmDeuoi|pw+l$)G1orv>^M}(D*-}Fkop+rb8E0M}BmQG52p6Hm z)-V&;0VPDrx%g1;09Ol8J!1U}16BR$WeN>ro$&wSr4j)qjix`_v>^l`7`0-ov)J_% z|9Cd7?7X0<K+oox4jQMrYi2I~Pvt-*|L;`}zwRht5Z%G@NTbUqv&%aJzV{WpEiS`b zAp_fXh|sMT`?2sgjWZF$tiu-SvoUfqtcx)WAp%r+GiWUxJyF_}6xV!Jr397SRVSf0 zMoKr>Sj}g~;!eD7b^4BalC}pKte^H0>dH92{~G6K;rPD2#dHDCwoMAk<RQ9G9LJiR zUb5OK#c6dF`+Uck4j&$A;GXCKEz<5vo*!$%N#nN!XHFpVEzjR15XV97_o4u}hm`>( zR<Pe2Kx+NyVXTV<PLp@V<=a@^G(+-zFy0vFh$e9Gvkb2#FA3|_m3&#6V*~=bc0Sbz z&<<My1=G%-{sJUy|HE;K&7>g0vB5FA!16`*l~pHSp^8|Tp=rq@V2V)i*#|l<@BdxL zWnzptz`|2`k{cd^wJPXfxbhj54$t12krSrx=16zrtNl^uX~n9*iT1<9+tH>@`fb24 zRtsr1WllAmMz0EY4_JB(rby22N*;=ST)A304*{kub^2s(V(<DpPXvQ;l6BV=#0H_V zU+zHb#dBN>oT~?XcKvPSfIQ*Mrss>|09Pkfg4^;6<-Ea_@Fahk#xUi+S)1gd-Dl0H z^thK@Ppil25<OiWRu^=ctGP1m62pD1NVeu;U?0P(Cl+9Bk!^4oDLmIiJZ?nU>mjGN z$!Ua(NSNa#sW53<ggM<dEWd`@ifBx;t+E-|-?$}w$0ISNE3O3?`Xtx>ex!<<d`PKF z7LxLZIDwn00J558{}!wH--l(_<+~2dP(*9nsV&kW{n&Gc?qHv?-FDl&=WK-HmFoz? ztUsQuhbZ1^DpMGSm$e5Hglb&ZTEFmB;e8@Ji_s4pc^2P~tsccAkMoZHGIcVZH{}sP zFq&v9z-_p6A8gL?ZvQ%blSR#UHnj~rMq^`$-<Fn$Am3#98xqb_=SvLGg=*opch{eQ zsHN-uVXS_HwwM0r9-x!Rje%fA1@wT|w%P|#)%$%@XI<Rxocv<}ex;|`VlhF~%DOTw z<3>s0)rc>YN#6-rtvDhTxKDDPuphJ>_cnFp9))6-l2tqKtTZll@Vd`hde{;8SH?^} zvHWql6#sv+WDrDu&SOJH)KqpNm5Q{9-1sJMiHp=d48EK=6Ak@dxs&QY-iTuCZptrQ z%k<Ulcz+(9KB%5I?tvI>B7xu*&KCat<5P-UIMe?_-CG9L*=5_pxCD21ceh{xf<tg8 zxCRZb!QC}TaCdhPE*sb2L4r$gzYlMBpRfCz^Hufj+qcep|Ls4!cGcQzO&N2HF|S0} z`CzrM1Sw$QS11wzWw#4r_W)c1z3XMls@uLJR@RCBZJgd>VzzToMbj*IjkM^^Hxze% zEVnX<PX63&k}8@kG%@U7SM?O=7xhw8Z}tjgA1RTcFKV;11p(Y+bw*u5v(ZLt#&1__ zO$dxM($!A_tD{?Xzin+6R^)~Z$sI;3XCX&QY)iUtuVS#+Xn5pN4J25kYMeT<&^Gpc zdTHh~-FsAaXA6O6lR78iD`*w|Cq_OGt3twX_j_RCGCcB1<o}l<I3Xcjn~VH_s0$L$ zEk<omkAac;bV$<q?|jTZgc-ww61(rP6KVf@!L}xKJee6^Xga#-{(#b31@0xdoc%>` zwea5oMQ+uY;1|{t=2cz48cOw1Sh|~r0TR#SLebajboB@IVKc_s)o+W#>M%0j5s`bs zDx8oZ;ym<Iwc@bk1@?naFMrLQ5XaS>?@Bv=jVTfEUYu6A9#xP_rMA+>tS1==(5{)U ze|K@P-Nz28D?CeaLm_wpNa1k_qCeiKgSvwbiz*kjI-XF+A5F)V!F;^m<-^pT!qNI+ zk_%#?uKInW!kewvME1*=VNH$6V5?r-Sc4y}!C67MXY9;B&FvEE$RFH3Xp(Iz|KSnq z!W~;f$xu!kG)^gnqQH(v=)~z6g?yMkt}(pgKfS*JOw_-=_TQ+QEdOP|n-YVk2+zaM zQCO8d$liQ1K@Dfd;3>nSECXof$4^F8qz|^s`&=Fik3O2D^3)piV9`5dTjkC^-T7S2 z=2)Y8y<{&!$z*B1qs&eegY5$VZYt~I@>hg=4t)}yQZ!i+KU^#j6K>O8LzN}0h1afR z+e$pW(x>y%KNCTr!+C(Gb?n*jg;8L!v)N?<P&me6zG!XA)Bu1G1MIrJ25J!4O=vQb z-P#TFxCOA<>l~%3;_)E%CyL>O-~1KWA{KVhp$EZo<3|Mqh&4<qUkJTER1HMvYr&1H zdd;<WzH8v9@QVo^JKni=JuHK2NSe*IdaJh9o<TN9?;?MhnWA@WwGMoZTLKxV$yrSA ziM(4N-ReaiukkK0*aAivSxe4qzid;lOME``j>B}&Xc%8qRy?$S#GfQu5!gd>ep7R) z-0htjLJv=gO)IE@TAr(s_Vz<N{$Ckr5)}V_23ocqc8PTsBYR%Kn3%|Ov4Z{c_Yo#o znk1Wk<-o&Cmi|w}j;)}V{#XHlwddK~21kqt>ly~SQljXcwHkv|K_c5X3}=0na6yMR z9m=g0%ST)Q+4Z+)-slG+)j&WT;j4MX#YAs%+ESO<l0~HHHXvoUe<V0_kj1k0t-<UR z0G~_J3iLP>hy$-l1b4IHZk_5(*V(KGxN4k6De#<qJk9717B4)QZ<mbS0czZN-}iyB zG&4fHU>)C24uOtrW2<*Ds5VgBo@hv9oZ-rWt^GeyU+Kh}7J~@if$7xw&*eNca8IAr znOdWxOIuF%5?2+x%~^0fGJtUtKBXxIoh;(i1B_eq#P;!DDK~`U2qGbBoPU$<s7RyD z&VuK_J4Bb?fQ+QxKgZ1|Wy>8P6qu7CJ^L$a`7?aN@^X#Q(n@50Dw_Yvmkp4&&W8`z z_?jNRcL}NdcIX+1@{G<chZZv8djt2jnFaPupd*m!mV4Q2kC1VVGH5zas#ZU{8O=ol zAX>)Fd%O?k-EX1jMc5QPcZcG;x@z7?R!r%kL?2p~T|HS&5WXrs%Fan~<Bcjx*m(~5 z)rU7_AM+x8tz~#FhEggx)g7`^u;vLc%a=HePSO})e{EL0^dY^DQmrx>CAcCiy_{gP zFxw|MD{c^BWind{`)H)9DDriT4RV3%E#LfqXp$`gH+7$z{TUlBJS@!v8(GAZ+^1Tc zX+)dQ?Ou1j72w95r*A^PDsZX>MUOTD1*K>(c?{!@3p*s0);46#@o*Ehe@|ITm61!@ zON5HTt5IbJiv^|De8RzAT4QfHDKe(-5;B1Y1kEL2pMF^B$7s&1{dTD>d|L0kD-u)= zEU0^ND>GBvy$7KB(R5_e5`h7KO{Ez7Btg@H+^<5h07Ew%k_SnheV9x%D*U54L3#`z zRqoi*h@o$-*4RaPU>S%b)Kq^OhzC&}>bQ`2th<J<#WEzu$TJ4ytFf|9$+t>4aQshK z#{KW*0k&x%OkG+%H##&5*ZRZ8%g@MQwxz`wr{N;oZyvuJ5)|%7`?7?SABG}Z+k;o# z+(>p%yN6Q?ZO9(X%nM+Nb}=G2YH2x!_i%_qM?Wn-SnS@D2x~kSNFvlgY({R%zHl2- zxs*6$6n*^Be6!0_HbJ<V5f!0g%i@+>e>fHl5HL0np_#A3z@mzBRKm!l^%muE@(G4p zQ7ruha$xa-us%HlT--6e&aH6``f+c!XLH+c;aR87I`)GMSar#E`pd2QY!Ig9@!Dpp zauzT(NYjUK`}4Se)W7@x<MppmZebMU9Fv#$dBDd{3~ARiI7`uVZOx}f0=zDO_MU%G zL#VU*^T|jCF9lW%0vqpOyac-;37oQBtTSo=N2&?jupB(Z!C91N3|9FvPp7rc4PR83 zhx^@JRb{ig@^O8*jq1#I*s5$sbg!lRWY4vdhpW+1pNElfW{&E^quqWa&xK>Y@Tpil z(@;F_*#yFEE;z-5CH-3eNIny}4x<IFBLi02yp!M>)2~Qloy9k~>rsqH>^(Xnf>l2N zatocVwsrUBSOWBiBCDb8QuA&X41ZjI-&ejx(Z2`@|DV&k-XBKz4``9nQhy45+a2Yl z*l~tPle0NRHY+?s-?%QAbMUiMi{xoAQN=dR<NnF<>~?Y1REI4|k|V)>R}&6%)TG_z zFe1s!Zv`Y2u7=_Vv${!VaT`Ezz-f)ePwuL6(-L(9D$Z)~3bB&cX|U9f(L%a0<BS|x zPF&qP-jS7~C1O9I$X48;{yd*r2T1`4w8I1J5=6HjMDzR#&=!|AO2!&|yjGUeZnqB$ zy*8s$zoLv2u-VBQZs}>lM?qc@?*}?ZnN-do{9X{8sbKpf5={RYB0*ii_B-})yyu<- zbzYD1FAq85b0A~vs^hmg2Ti@dCypDzJd=%@riH=)cGr3D?qj`wB1FrZ_>ImL|3owh zy7%z!6CNPI`_2OLs${s2Ml07lvI_yPT9I4dU~~)8M(=etLXDbg7aSM(?gJylxcDHj z0Mz`<_~621+t#|OV15K}7R>97rA05d&#TOYxUM%=YF+s1z7@;NBq>h|G~xFOC12_+ zFs^Br+iz%v`iIac>~%q6?`><XDBBL41D&k=amlM*uV&_-;A*B3IU(mOg_FNKUU{+I zxlhgk{I%G4Z**=zcbD=iUCu8n>(R7s)OBb(sPuxTQ@1|uo`O04@un3l40X!prq>}{ z#p#`^e(0)7AzLy-$#|Q=YaKT|FFDG|zo#{ouHZrxOOM`3i>b)+tZFTwlB8u1V0jBT z9R*~-u4SmTR7@lu07IR-z9<!8=NZoSu%<{lZz|7^tMibj)G}JAGwZHFOgshIj-+K} zdPa>ggng(>uM~1hkox{Gd(fVSi*NV;o`Q%kDnF;oA(o6l;G_H>wn)?ej4cv`6`s{; z;f5mz9GSL0Cg(VW2%FN(QMP03bFuyHud_B1&QoFa`DXKxc=;Ilvb$f7RKmouL7>i8 zOPlD=J9)q6Cn{DNZGVv^D$wTgzVVM2{rP|%tPBtDkl-Qso;sfWbFUNSIVGx~uD>ka zW~g|@khh6_bB#45ZHDO^ap2yKjkB^*VY?8yxvI@{O3QLzWwalhP9*BSC4GqRjPPM6 zJ<R;PGcjhBqR~>(YPp^naB4`~j@;0xl^E34OXm-DtwE@JCF91=8GuXP@Ot-h{v>x# z84}MK`Pw#VxydLQJ!o<{h`^3W$K#|Ip*mw<<Mf-m`%C}FL5(rJ#)2=0euA*;{f3Zw zz&blP!d;15c7$|IU`i_g#v{~@oVG+#wTsQ4c$`{a1R23-Ns>Cr<8^+U?D3cLlMCB5 zi)nwT>Ihk5(5W*QJ-JWM`gnf@xF}@Y|0d$_;p1(3AW*wtXgXeTJ<>E{V}<?sq|B@G z_5&IvR7=Z2z}ZX@icx4}tMr1D(Z48QK$|o<{g8bmPfV#t6Du~T5Y;8~50+4>jJ`P^ zZL=72I2XzANxWCGMQXxRJA!Rs>(o!&J}YfKRjEF@SUf|o;^@tCT%b@OD6(|Kbs1`H zWhRTQ9g#eq8-2sPvv)J`yL&IO@$LYVM{8qTnR&iQL6JQ-ZFS9ov&rL^isNBhyYBPo zpaaW($KFxp#mn{kbBa^dF16~<(oZMyZOFaH-gZ_2Tc^<<Qp)#@pGiz2eZ3H-IZ2Q2 zgd~7DA**~q@*C^gPiQas{>d_oN>b1{6p48?_EM&(yz1=*(sA<15EPpCS?`)$o|Ezc zQ@@;3b^({?9N*oe>4RA=!K%RNikuA(H(IG;F^g$M*doji<KMI;L<*_WIMZQC63h9o zi2%zu=gCC1F5GW7QOu4)KAr<NtxWCvchkRcDtqXIKy5LEyuXDi<`NwnL#a&Mck=f! zOk8OrD&axI`0+ADqCL(Ya5**oZ2|kA$3T^94y0Cd(LFPtD!n*=m51zRx%?`R@L*~+ zbDDBQz*o1n?)e1u=m&-jD+=c!zJUQ{T2d5gIYIzcwwF{$buna{$Xp>k^QO{C;GgYb zjR6}NaH&bn78m)R&;J0{pymr<YrTrdkJ2I_wG>gMs%Tq=l~6T@{n5)fH>rC;4ye?J z1qP{7!6`eWwlawY1d{s#OC}V1`(AnZ=_dxeq!qDu3NY}rxvUEd^@ng~rbSFP`0O3m z6L}F|W?2p&iBmO%;vFw8sA49xqn5r$fkHN=718}Ty^>Y{5=~C6JlrqkKt=m~QVi)- zuWzvhb%<a0NgvFVB+#>1&h%z&)`Sb(W_*Xso)g_DI=x@ovxL(C1b1TGPP5%X@_6^D z*iGq%zNQ;xq_Rl885&w*hC+q}V?A;z(Zkhi=8is9NAVMpz)->!dt4#_qaXek82$gY zQN<th@4sP<?-b=`Ir(`%lbWl(=VS|IFlkp*JQn`_Wv8`cf=M$#a=&ZcbE@ei{Q<=6 zSWRtsHk%e%%Y>wZvT?_*s&Z+{14};Ny|jRtPiZTnKAjNrg-a5z+(Q|91r;H|iQ8?2 zk?{K|L^6-@Sxs`b(W)L}M#n|JsW4vb(@vY2&o4jsuh*j<ZlZIM(;&_5a@c84nhQYZ zPC^e0-FhH~(SL&4hZ^Pt<T9_Ze=V0$WDl=pyh}A7S0D|E&cgS}++$JYETs#Ulntl- zV=&36B3rBpCU()N0Dw<Ns4OI=0&GYTBX44Yb6iIB8jM$dHlRdSH6vm)0s&=(;UY~b zXq8$OFqmw185Cixg{HzAM_d*Obw%}uD{62>{LmY!0DD`l*zU1_kQ&t##ngX!3s@+% z6>IFy^&;GV(B*TsRFUpIDO{CQymZJWAJU7J{`5@I)C)J{N-4kp<02#K8-s&E8VnPb zCg9p81<jPCNgn$~^bhl@*bzYZ-<88yJ**r`Xc0z|r^Eju^U4^V#Q85iK8p5&0+^Ju zWSo}80V;0*S`3#_fF2aXua7lf7g~uz*hjcs(-ocPsvjMRGhMI6E|`yM&MI+6^(J(( z^q1Rn?pRCp<rZ_%KLn|vQU!E+Pb(!`v?S85QzzCRK+Z8Hk24ql;3m(zCQ)GFD?5Ay z@LWf|8|VcgElvkBA|c%){zqaVt4ZGt!Q3YJO#t=Esh^?@n7KyzJek@T&kCnL#Q@S& zCV2PPx&OZQU+kl(SknGxjA}}bvp_9$7rh)vsTHRUi0LC9g#>Kk2b2|DAY!v?9FEic zxA1MxXiNeEdiu)qQ5XR74P))NdZF<W7Y=M~HfgM$l0!lsX>~j_mAd73`QY>&Qn`8A z9`-mywo#FlL|R#%=xO)bVTs9^I|*r?)t+ETugiDSio7p$e!fOmXEi8|CxWdGTb=t< z>97iA1JY{iojxZ^D4?$ViJ!{@$%1@Eo!O0e93i^4!}bJ1ohH^0!;)S15I<<I^4~P$ zy?>if1QA+DNH}!)omlsF!}qD#VxJM)b7M*Xk%b$HxXvDwi?ytDnENw{gb+^hCrwSg z52g7&%T+kx_HHR`d48UWbVtd>x%{s(6I&5Zr!!K8`O){vX0UMovIB&-3UYMfM}B5w zvbJD?NBE@;16DSM{oWBbcjY%36>@{M+r|`*qzE#Y`&d*wd~YBav=oh!s8>vywUED? zbYL=K45QW&_Q{9UI&INr(4<=fv_rc@rx_w^-fl@0N~D0%>4>D|YhtEytAS``;3jxk zcJ;5fyE2znl?CG<W@<;RqL<D$b%S5DrZ{iYy=U&=%|jC7U2bM$6W`FS^v|4y*Lr(c zVpuSh-P41W1yLQ-cb^fGJbU^*-*tlL>&!Te8|^8N*aZo${+;&8eJj&LO*iB?)c7d^ z7hS|y*=T%NWW0wsvV-Fsr222A+WfWIyW7GGHTEV_{I_ngG>4|-bajwW^RoX%zp-l! zqxu~*9=Xr3SA$sjPzUe7G?F<Tl?aemQ|lgntH!_OW2hnOqVvSjO1@Z|qF%-$V-Imb z*cKi<PKbNau0bVZ8LUMGr=$&J4Je;<RL0KdU?Ah|pj8Z(qFtpI^fvwDA@=aZB}~%= zCDe&R)`+n9t95II?Y|eWSZ{vXn7;pLjN=g#^KoAIu%6${sA@J#Fn-4FXLzb<x?$4t zVlHkTdNc?9MtKZrADrpX_-aV(2*RcVCuyE$h2;+(+s_o(4!V4_un|b(Ua0&829z2f znAZZ$`>NgXg($Kw3)m3JQTt*q<LfHp=ADK_R*Z(R{-8=5OWS}+TYaUo^Sq}0OV$?3 z|J(EXZ+(fy2U*ma@Ff0uGqL|Z2$lIV$Rw82+G<!^xR)6~elWQOz>^D4#=emWec@!! z7ls`c^NN1mlQf&PE#t-E<TBm?6SB~N4j~CzG39~>7zO<V#6b%Kbb?-g_~Rr3o!vw` zcZczV!%V&30Ya&~+droPp4sy(`ZtwtEnfW8c5FjXv}mF#Qn21$PEnT%A%rqQN%<nH zN|!PmNs-(&mdEU978Aq(y$cgJe_=5BZ|qMfAq{HOOQ-3P+P~@c0p@;nIWB*v&OdKn z_y5tog5UBM*gL2zQ*iH@1{XkJso{-ikCFUjRab4)gbtVdUFAke5EJ8_H+n>JrQ@~b zD66!jUa=k_{qaLzKa-U*&X~2zAPv-rQI@QSXkql0j75r*cmrTdw;a3AwGc_qd(qRP zf5?6aly0lHN6O`L<0WfhpXP5^jl^92ab>7$SzwA6<$*t6I^T7&DP6?2#(q*{`iJQ{ z@&TdI?0e#lSMB?Fs-!Z{lEoipCIbMmm`GP!E7>+bF^zNh8<A)|CQ+AWg9&cM!#~UO zNu6RW3X8$RR<E2S-3oF9S6pJ&?X>9|1181Ek4}p7(1JL6i&mS$S^94U@?1Rj&Aj}g z`J|ht{Gr0x&DmUawYR><rL@Xv9XU$-(B&oe6Qj9ho`AgUzC1Jm8SA70l?3XGAm`1R z#)v*hFT}gmo)%XkCD*OkSteY<OiAv=vhf8V<1CBN{V1}Ea}-Cm&icY<y4e~8U37+< z5%!MWHyO*g$Tg2mt|NWKGwv-ZMLr+}Wy41(>1GdQel-DhqWx$LXhD5@-O2W<!zKQv z(xRvHPkT$sDKZ{+)v1LTHYDuu_D!O+cg`4c93NB<z3*(FK0|4<#W;34vGtN8)Z7o* z2!bk4vwc^Rw3jGP^Vd1i9L1Y^1-&5oV#WEN2(C`Bn}9O|_}3?{ltm`Is;6FLARwyt zG=jemJ)#JD7iQd5-E-t!rK&qgfr0Tuzuj>cup0+jl<15vR%zFEc6{MaRDg2rYwhm2 zp=MBy^^fYSD>Bs8?a~Fb=?ZwG(Dv7k%;R6|$o>x{<t=!Ds1+=4@Xh_QQ;IEJLdVG9 z)L|)+=$Y?xsOi}N8|*_!fztKks2v~tkD=mlI5&Ei5re;pzjPcF^jp=bA9uqe7k^<h zL@-A#8j(LO$>bRd5!g|?sh{1GreZXKMD`r1H1zkl@pIL%wocWlalR~{Tf9dcq}_PW zs#>Qh5wQd7M2ov}whso!o(ygiUt5QX=x2uEw<lETK}5EiMjAX+_GcN5n1u`xj#aZa zm;}+LX}o6(Ho{7%xeX@LA(7LnEg4K#6FzFXy=XEAa7e^*iDu80T6p)v8U%(9vjuOl zHKL)Bdw;OTdF2ZaBks`eZN6y+k8Kmf;s=0xc~hld!hB87K>8ea2S9hb48IJdFV_~x z*k+g&wgb~k0g}QSH2b`<Mo2A-`I9PXN4xZ>(ZoA{%InjE$e<#t9xBQtDI5rfR|o8) z1mmAKiaQ1wR}XO$6tz8@7ywR5jCkC6*y~-!aZJdcyJwUM5&ZPP6lB7DOqf3=XDZJN zGI2qXX8`xdl021Y5}ICSKk?0MnL8Ih5J;AP9b@?re+;Ccug+tMZuYhi-B}#tTEvH# z`GHFtco?veE~S8kWPXF?C1#x%Sm@dMC&Vks9a(!1<PzUoC@V>-R23g8R_9pj{HXSH zlUOB@!2WV|vIMLKc#?G;zhA9}5ZKUHZ9*88M1wZbr3+`!fzYTFA;je2bXJf>5|K8C z*5w)26AOl<&Ye2v+tDF$Yxwyad?~BLbfBck)T?9k+p<1wx@tW<zj*dm@voXiUk~}D zYEhltBxx+I*8SR_*6WnCtLtj7V@QlXRQFeC^73D7=P<JWozNs|Y5<<Pc`juxb@<z7 zk&@sMq!=NrinXASd(ZY7!kHwTEA2oE{TJ5h^hz(XIyS2vWNT<Zp+P9_Puc5$3hQDZ z0z@HHe<w4!{=mZR$Z?~6Xcs2U&Kzi26Up)NHfL)*bxi$fbF}2~wM(zzjS?<iE!>ze zLw6>TVS8voD5<<dDxQA%;6NC9UKtMB6}77FMlp_1GUY>n?S+^2Kh5?6rd>;9?64f= z<AF~FVTr}_7BKN6+(VjNtuBLP-=eN2e#wp>cb+Bi$n#%CN7ezR$lbT$V?a|gPmapV z9-B?s%*+8RJ_)=u4`qpDI7OLK)|@cYnmPFgJ>9?zl4OIo_uGd>UoP)mtA84GZ>udH zB0BR|HM~~;N^clrLHL$IW0zjg{Y#zF7Oh=ndXiF2)S+a*4}in@Q;Y)t?r@Td=Wk(U z5*;6qyJECPQb!vjGdu31aYnxgQliMpRpD-f41b>c2&kyHd|}CC>D|=A45fv;jHk>0 z#%c8;MM9&cFu2%lnA!#d&Va;y{8m@9=@4s7ysL#!Ws;ZW_IqZk1`^BOeHsr=lv;xu zdo=exITW&_$}~lp7DYuox?_Sr@Wy6S70b)kgp74FMG*a}UNf5EpJ3SkmyGr{Ve-BG zyYm6ZcnkEQXz#(_R%s-u%HE$R<QO@h&Kgw4-3-=FB{8Ex)_uCnBihV&wAk{gAAcjp z7jz}a`F4`A4xhZEMw%zsRzL0YYXl&p`=bgxU@FL8w9fxaAul3Rry2$piUfc=4ILPv zMfcOim6XeK6XhM?T4fYy_-L!j*61u5$3+mqD}jUAHu&=#3`Y9<$S~1pANyk4nG*`P zGas+<)-}6|W;4}I1sIcz85?kF?$5+MDM40yUYZ`^;ehi1ILLo|URkVqT8r!*7G3iA z&qe*C<5;Ce)4s9RWqrnnx!86DPg6a>q<_AqpuS0L=k2M50i)F9Fh=K*{}`=lMohhE zGHGi$?=nxo@79B3j)5J#KGfUS3K9QLQX%#rmT`QRfijnC{VDD9m^}cAUs;U!8Mmjo zykSAO?XsTVJj3DQGG>+a*gR(UI=vn}!kX<xng;Dmjs;43n)J{bh&e^s;A+F*WpLHJ z$;i;X0S21HUI#=RJueU5*sC|6pj}vde;Z(K578QzEW`(Ivs@|9-$^l;z~_#J*Lici z-k5*GQz8PFzdnH)ani222{{(7+Cb|*%V!s*3x>UTQ<HKLKX-%?pFPKzV+3vujr`c; zT?<vXfN1$Mfm(Bt#-vj@EP?`F-R1|6)lxFeZh7Z%kg{E#Z)XJaao26I`B!8Q^SSy0 z`MLCK!^Q;9iA~g7alrY8cE~A~Z9iOwV41i5TSFr-tXYU;eiX?PU-ZBcZQ?0a{5FHg zk&#r`H!vg<65rx<=)-v|0C9Zgq8NU?ud%Hg2f19N|3RzS*06_8K*Ah=9u59I&|_uw z8_b||5rZ8|z-P?I){PmFU)MCz=Hy-*8rh<I%D8Bi?}8s?yyLK-mgo0^F*A-D;Ov6* zu>@z=NkkB7PBt^ewZE`d=bDhtY9%0mvXWQ{%p<GCd0_u$;_kTG3v*jj#_vb^a~=%4 zD=O(@JUfFlt80%nGz8zCEjNCU^bv^EwuW+=U+PtHG!Xv-EPkH(!DaFtwX@3h6at`O zK2&w5bWVsG0BqmTEp$`gP4*N3M(Tg{h6aq((E!(Pw0HmXP9`TS+Gb+nH|PhbX`Hh? zueeV<K7jjZ0ImLaF_C-%6R-4`Rmf^SF#6sFhlQK0_BR|^zpgZWEtl7kMtuytsIZr? z7f!#cHx*m=eEY7%6HNW)fO?+_sU#;HQ@x!uK<`QYRFjjC`21z@0ojcq{LqI4gf7q7 zpQw+X$2QCo7DnfP866HyHDG(Ripy|?wU!rvnPevZPPN*X6C50EUkWX*)R$Bl6X803 z{nyLOc&6tBzpI(~$<bPeg4f!O7PI-d*Tl^?LH;(^lcS>}=l~pU`Y$IjO>$Ps><H}C zGe%t6<%`wERpIjJGO0sESx}AOD-G4PckD>=Q6NiY4;|~Uvt<{_8LR&Hy8KeVTSvE> z=x=BnS<-Xl?FtiPZzFGT4ro>3znr8NkA|PqSZO8G0wmH*lWSzM%!sEP{xm7Xa~J)( z_`66~)Kn4UHI#_Pfvh6M;f%+~O{p{*Vjp$UCRZ&2%LTcv_>ppujBT1Z)$p6415O6g z_y{Qj5SO)$kCG)9J^Kv}xUviB=bT=G=H#wt+paLHiM@;OS+*JXb!F6#*M)R6&OXEp zzMJDh*b?1o1}Tv5R2)lmtd_r)dvIt+hqTu2SsK^&6lOeB*D_NRY@k)2>R-<)RHdE$ zTF&h7=$npn<lbUqgMwrwH`jrClw@wUz*u2*W#WZ5`0mBo7#lymY$O}Fz9N!4cd5;W zwtyVpe&Vo){MLWCgWl!ZI+(iykKTN`{P$Gg+c#?dQJ&POat>OfwSj=7^*3dmE8Mv* z<#g5xiPX>IPBW?tyi-#4fPr=rY?~-yg`9Z)JG|aFP6dJI@<7|uTJGt})Gt&?TIoqE zoD(wfSg~`K>hCCV;=0qU7JoeFPJdE0IT8DgYYBlj5oAdGy_D(HQ1^1&d|r>wnr*H3 zs*6Xy=Lg&`q;KmsE1%k<thRI>s@))n*=DSFKCpGE{@7L|9=q!Iv}UvQ>V1-}IXKd{ z%?e(s1Id`LK2AEcV<Qn+GtLDi4Whz0SEL5E+oOrj3qvd$oM>w<d8Gyy4(xK$bucx) zzqM)0+ix%;uOy;$Up4)$ljMoH{1r4=$fF`7<u&kt;EY)cC43_{YubOC(7-5*35d)T z*gZo~C;Fc!iR3AR-P)?OhP?*(L&vh&PD8|XwkA}Hw?BLtu%`rJ#0WFwEVq?M)ms(n zOPkJ{QSpH4;!U7UoYSyShs7zYckvq=yNc`au~HvmEpRcXIP%BI`h)4pHFY-5?{uo# z?C7Y;2pyj&t@qY*d0p19%M*i^Dm0&x)=aZ&{CZ{=T~fQZ@q4?@A~NaK33}Vm&y=ep zU|0n1Tf-K=IBw;0cyvwP#i`9`bO!a9Aa2&?VDs_if(i86_pUTx!n9GGvcoh<%<qcr zc9T**n~z2oE`*1L<e<g3wZ8>1xbK}<$YkX*Nu{rCQ&I|TJq5j9h^*HO;aZz)3J+L^ zZcCe;qW)}=$`@`Cj_q6a2FdJltcjSwM7awy{s2Z>qExQGIf5xHGu<Aw;8)j7ZSp^I zJju}4aWk}7W+qWGke1Dq`${pB`*AH)H#~_K%oPs_z=Uy9;;oL}2;F;KRHFQ<TITRL z*<B{q`>b8nk`m?H6w^SP?tI)KTXWm(e`S~V0#A7yO&Ny|I2dCYxS=9)7*AOpKld@X z^=Ji*bHsEx|2VZv^XkdI^QLJJFIrT${&EBA$jTNYcq=2PI-OAx*TC3jc%mJP+8uaj z!5b_9-?)hW+5Cib>XS!BN~jgAw4N)U6D+WHR}YKdlUpo(4KazsQvJ?ClqiV4XFw1G zj=H7XRO>r1rFD;A;tzCVz79FDAZPn`9R~bGEECFNZs;0?o*=hPKXUsh7)60kyXnD1 z)j;IDrRFKZjIDV`yVX-VNqe0FlbNjgbXy|mqnW;a%_+uQdjHw!Ohl9O7Za*vecqGg z5;qFmU4d`H_RnYg1U0+3)Z)H@6><(FQFt(p!Rn?JroJ(*7{-oiiJ1bs!&Oo0K(Ner z0S$<q{665U+WzsS|JszJexIw079Bn7{Q30=AoW*x;LkO*XejZpN`Qw^dq#p@6f^Vw zK4H$cFS)F^t?>Lkm+MB@61ctUXI1oG893#vP-_W>)f&P%9D>?xwpcwChq#K@p%_S= z0fHK|S{s#crF`Z@DZf(7#0?t37*IF+rn^Itaw1wmIR<sx2NnsM9Q<a@hbY}I0}4t{ zu6QTT?(;P#J={<;G9p)}?LO+Jka_*{uYBiU$lqd?;JUip-(c6-tlJT<$?_Y|=~SKG zPjVZSi4Sd~p20F)hSZ>*6#wGc5~)_-seATXp3SRFU##`ImPhGyT6e5s(tY73#>l(s zvq%GXZ85Ag4fwfm$qu$ADN&o<!<nyD2A(2`kx!;3EnjRMAU<b>7JNS<ZaE-^FHcgs zy=V<?LCKvmD=Vpt+%ILS%9ki0(Jk~PZ+c{rG@W@RFs+5aI$k!)a&#MAf?Y$zet}*2 zuNTSF;v1m+GXORIz)5&(!aghn+b>+x@_tAu^~52EYe{pf(7CS{P<4o$6hn|b1Z`{- z(I<=)#?|9XFqk>hf3&L4Y<y;o%8K9)c6-RJslkEM#==_lU1V#v1WA$e{RW5Z)gx7z zNVt!kz3a1}#}@2fec9yT*7F2xyG-j?%_5wQ6b^)7XLJYKt4)`zJeyAL;4WZUX|Snl ze@TN+(s0bQHXpirojs<lie;|a4+|m?;7kbI2Z!y=G=l$HRKxgcE6L<u6w6CY%cl(& zpJk#|HvGJSs~WF+aj0K^7gc(5lY%*oO?C^SSE1;&Uyz|3H8J|a4hFFxW2%}eO<K)F z*OHQmnxsBJ-Fp!!ndX^x+iTP(w#`l-|CB);5xM;_IKvuu&X-A<n&^Qc0ddJMQaedD zkT7YV6GuBCipy48QebQYm2f+n9<^2<4*5$ddx<r^beUnjk$TZ|gT$fbA<E2ci-mD$ z6AM0pr(d?3(iBoCQ9$~9BxQV_l)-gBnGy>oWa(bHQ9CA)##p{M&2^&^1HMbfU5HMh zLA=f3Z@z_<h^q#@!BV&s6WK=Ad_CGLyReY5>RND*I!NjYe)A2la?7((D;k5(0UQsV zib7ww3f~$WLn9&^`#~?o?8Nd)(b+RWl`NRRQ)ofjPkls4k*4Ar!%(XRjwj}T$F-eF zlr>$gPy%M7GD#|VxbM*f45~6|q?kS7oq>+B0~;L~eMO{v@Gra=&Ku72kp9q+AyZY# z2Yz{Myx}%P{w3B@@N~K8#JrQV`XIu&6+ap?*lwkMC22&4gs24Flqnfk#!)vf6K)fc zI`WRfZE~Gg-|e&tbtt*CHd6wc!-9={skCNO9qY6tn#hVV-j)$b*3}?w=@j_0FmT-T zoEW|(2i*AZq%;H{?O~Wy#1fW~EXCZiuwj^l<c=lNqf(!>J93Zw!FBFq7FW48lOgi< zY;YCKYBLCr{5{A#8esDQ?oklayj=IJR=a1@cFot9DIeki&l+9L0A>zmf?A8S#H+nM zqj$KC_I)3uC%?~xV={RwCne%B8Y-KCA)IJ;Q+88x5Ns7tt5Va~(U)cl{y>z+iQ}YZ zvgGtGjyJAzpOvy^E%_RqPZ%CIQtUwM9_j1-C{d79Bf|Z$8J^3LXOZ$N{!H>PRisI! z|3;;0{$FdHn@L`-BA`e)e%4R)#L-E<NZ67OHw#{5Q%xoFj!>)Nzazr$ZTR1*a)^`q zun{luvBEijhF8*o2p2EsJ1s|DFEgsIzky%GbbdkSzWhM8m7!m!ar!-**;RfRp>WrT z)b)G(aZ{x3RH+wocUQ+}hgIYm7LwZAoTaL+W|xWNRgz&f^UZNIy?J`3F9d5yjtl+? zVANxh#t3wODQrMQl-lxDSQwv=Nrw-aLr$_OQdY{0%qPq&X3}#Us6q53^2K?IREy2# z5IvM%f!YT5?`oTMt*c1ZwG$fKeo4yDy9e(r_)i6H*d!elSg6O2zkXW9P2f<aC_yJf zydqCh+Y<M18g&n+QK&!h4pWsy`<d`ex{=Y663%+L6!jJ5<4W!FxAj%d0^3168~V&s zRu8JR7cQUG<-Nk6&IX>skB@UUrxThD7()akn!#U}e_}OVy#?`>oeGeWxEmtq`9^GN zU5M04ZF(~qjKdf0Si$FQB(f_)hXm-V%6~V02)EJXdcYb+xY5xJHOVARffx#!tQ*uk zHcaeYaaG~RZoyy|V@7jO9UBm3u9q`<^BNxjTf@(>{H5K!U&vDG(+2WuEh#I4r^3?^ zbfzc+2VkNrP_BO3zrS5P8B%<URNw$<G`La7MG>}5i>|rbb2-|;nFp4I8`dwQoASYT zr}6AZ6+;M%<)rg2+E*d>S?7tzj;%`_V!iXeTpL;Lj)1!wSNT=@&5a;MBQ41Go*eO| zBik*FEfLoCpV)^^h8thkHr|Q#glc)*EJ1%a5tm6JII0wPqfVL~x}_NA?ozsn`$i4X zC=TaPH!?efyV8I@%$}N(h3MPepp2(xh7M;fE5l4p^&Ys~!)F3pal4t+&(*l+s4bD< zCO$>O<P`2-4QU7@xC|l^r3ar!D8*bz^VE0H$s|qRf`aBvjHA-$(NH*ep<r9`-UJ@u z_y=T_%F(AwOn<_yNSP~fs+jWlao@}Nh4}~W&tqU`iyo<knyS;^AIVAK{#UDpDb_KG z?skaso2y%Yo^TSMyYlCY1Lw^1Ljszn(iF%3Gpdio-YwRT2iH$*7cK+v8HG)>S<ZLW z{h!>I=h_;N?)GL_W@UTM2EVB$uE|H0?-auYE7T=(MH>2*ar%L!DPyOSsm2aT)q?C) zy(x>rd~MMDSIXJB*kN2N-q*@e@6te$+E=JbB&9+@EXT)3NW&{xnp(39Ot|H@6om;h zN(j7X&Ldx1x2zqKS~Fc74pu^1BsZO|>0%#_B^oML`y<1CaQr{du;Eb2ZFEm*l=(fa zA5&Q05qK1(h_!82csuH^e!RauFe|9@xyhmvZhzd*(&gIpsZ4d-#!`4XDI9)jHAhoF z(QZE{(w_Itq@ZxTv^+O|OC_nD6gZp$Z#`cdyrj87FCQw>{|%Ls`z04Rh>oto{Mbgg z9y|3^e7xW`b#g6gjEe+t)Mj=1yb(DFKO5=c{`|n=dUkHSOml(hm6<*gYzkuXuS#)8 zr<__Ql&9=RCcVWl*6e)B(XCd~*bb_Wu$d`DV3JspRBYvP+#ehaU+Xo-t@F60SZGlA zWm<fM^{a)HMWZ$vtK~_u%{O34LlK4L&t#h#OZ80FM)s|3dXgR|oKIAS)ZH{N2G+qs zyC$QCQuOMdm>(@uL7X*~lW05caI<v%=na}hhb?OSutIewt!T0z=(D_yLiU|c-L6Tz z_4dxr-n8o&?iaeUE`dF1;-}1w@xj1tCkmS;(QzO`%*9KH$)qH5;f9}RBa!wfC)ekH zO;cQgi6~tlN4!dkn2rqLOcPQau33VZ{I_I+UHECn_XXiLxN}M;Zugy|8G{LaE5iy3 zV(<yOVS`yolgA(DM^frW{^<2Q&?U&jHmR&|@&D-mXW|Msja0Qac1Thssp1ghOh9Pi zEBryecQu+l20cFnQ9vuI>}ESeD4m?2ry4>0!h!JvW_XBVgCy=#UVLY@BQqmFfd5Ve zE|(G`A(4)<gsnr~Iri@i@Sy|vbsaETLsJ4S)T>`g=A+J?n1J{`cF^>x-2^EB{EzTX z=u+#C$7FdX?V}lrAh^iXCZyYNx4Khx>}_>QljWVGUM|i=f%lDj`sGIQAX!aq?S-Pe zRPG4pOX-GvY((kO@?xhEfja1m`0wve*x2N$G;oDr4rG@{Z0er4JFGXWv(e%wfn%{i zEr$gxFGpZXNbv_kQw*>v>$R9WtJa63t|(;tYcZF*`(2$AKBvad_)x5amk>>;k)J*B zgILMRe^k1_J9KA#l8It6+q-*6gt`jz=^@d`>9a0r-4Iq6jr9ooiknQ8l2k})NSTg1 z-E8H@R+2v;k*;QUL7<j3r}Ps{C(z#H^<BmMzwoXG`;I@%|1be%?5lE^comcOK(WH- zd}XCe|8}Acn*@rzEToV({-aCzr02krb{;D+6GG3&t%HjnhD3ce9hWG^24oXtsu;BD znDy6yF;s}alVf%QlfJEN{DZwEFm6l@b)_LPk@YXg7%_g)E+@KX-Ret(!>Q4zz}dJc zVB!&+8EsFByU$A-NCvi5FkS9YZK<{qhfks%MwTi}l@v1;jLO-b_eM%fw$_X}0$t7n zfFNWX=4H`IQjrQsDV#>l5YtqotwyL@`Yal__0uFVslw`wM0wuL*TT=pL=`3mPgf-t zwDi^K`O9m4TI{1vc`R({OJPru$Jie)PofM=#80LQOT_E;FE>rK;_?H_Ppq9~4TGOt zY%WgCriCYyrKgikDatfB7vgfQr8P~^osTcKwC)M<&#B*(UzoJ$q%5LNuRoGiPi42< zHdi-;za}$5L?BGRG@VYv4>Jdveao3@c!Wq!q>>T+s?QE_P$vL7TdJ7&4;2)2(PcXG z-@^bcii{@Nv3{a3Z}4dU@}X1+a)rasz~euaUOXKPqvwNvho=0q--Nz_@7fn_UtK6S z5!xbpZ#J2N(EoQo_s8!ZaC8HpMWO3ILV8{^ZMB3x|H?P*v(9vw7i)32nD7<;A#nh! zC&Px!>ZgfDmAq@n5bz=Qxu|fKUP+vj)(zwa(<@BNyuj*71<C0?re&j<asfkY(}7_% zA^&~mS()#0z6yxli-_K1!^yp|s^zn~w=?YNF=0+Lrqp5dDIchqOn7pjFs3O3^}gp@ z$G2G#sd9@=r$=)}Az`aki5I&McUb&K6K?pYY{Fkl1k3v*e7w3uD(ZOa0Xj>vwk66% zzeLt}8v|FSayj~vnklon<wuD*bLT|fZfr8bRG=Lk*!q{-L7jZLx7Qlfc(sst4fJ2( z_~>UWnd_+utXAry-BNnUc!!A8NKLv>2_IEWm6tf<)q5@a%RdxYMa|F5lN6piP7R>i z>2q>lLx_~+<`SUt!|+t#NmEM3bvy9LcPUdCC)f79j|cI-{x#`P{ITc~wMa*mK}kSY z%l)I=BYnO2zC0Qoc9A@g;<bi#`%~7i8lOzYl#^4WYVnHlgO1D+3}697rqOS;p_={Q zoER2K7Pr^Dqu3|xpYe@po0<PpXz*h12J)j<`ES1*FbBgmlGePizF1lG#bgv(1l=9F zKJLqHmhS%uB!Bq;{(@*f0p)K=1<Wq~8lhij7k|a#I{vR?al%Y>cPUb?%4YG!{`;!a zqAnORtYq?{dsK2jEN`0^G3B7_*q54FG*ALqENNd?BFqHxr!I43qCwcf5=H^i9Zr-x zijhF|E;eiob+p9;;2TJ_``_h@Q`>r|TV_)l<(LgA9kHNXnJB0t0WvgAYDdUG311Y{ zymgs3TZtHT!jYrmBS!ikKoIl41qf<MFVruIW0Tb~A|E#U7LQ$<F7Hny1y8XA<FfF5 zBtIUXz(&2}wHZ;D41=^r`yQud`pHD#L_5qRRc=fH=G}&NNhCc7JX9kU6;1+rE&%+Q zxMiM8IVxcIXhyn)>Pm>$cFmZ?#kG%CCm`kFSBsD28~d2XlkvwYl{tjP=CNn&idq3{ ze&i2BL+}Xw16GuvMXwQhdKA>CljJEZxgyoa4pdv`Ed*_z4Dp3=Y!J_{#f^<wQ0wXG zTfu9L?Pt>B-aBBzGrG=PGH|h*(aFkDE=8&Ue@&fqH6)!O<#IPL4M|m1HqHZ;HUYrX z!~yMRUnWsr_A<Gs$ia^caf}IJcf(+Cx(rE5rqq7jjrrR~%A%^;c<G1}9K@C<mOoJ< z&`{z=i>0nz(-_&~UGZFM4OJl<rBa=as^r*BVA(&Zy*A#3GrAV?-1E(rMCYslV)j7a z{ch+B8Sib$$lv;Itqhq3{EDIqVY#hexlv?mofg(d(EjS;;yk1-h_>1s@lw&`emk~l zUkW4!7)`^%DAPa7Xb;(P!@H0I_zl70M+AlhnNQaBT34^#vKA2)a<w>|q*+o)+<CJV zRlF%Xn=l@jpH%^S=@=#IVsfIcoJ^<;gK@&9`v3(<Q2x=7?I)Q79@g;lhXiqn-^{%q zxFbJxW78!>KP5>AAH9>P7@-o49&t)Ts2Co==c%9|HnWFAc96t^TVjEN6;?}C<&Mgq zW7TFWQ`y(fyQ;qmv3%662gU$6QBfU0aZtPm(-++%ZzX{~tVCJ8B0p0#JCstu$QEb9 z@j)742}Iq448Tv;kZo3mrpnS$QQT!~yF8^rWqEzzjc}VouRSjDzux1TIOG#LFXS_A z7w;kqu<?UM(?`n~X{K6#+x{M@-fFBhutiZvhLDE!|7?nOd0`pph+rCZru@mI*MnGN zOpA-nt~m6L3^E82^DiHyO?_WSS0Dbd@Hu+PP@90;I{xxg-RnKNIWy{i{@Spa?*kJf z<>={z1o-Wie7<>1&5vGMj+WEH<jocMRI=ARYGzDE&!lpPaD?D*MpgD`DmfNS5AsxZ z1>UHb$KCMuGxhWe8HeMEKPVz<X$6Nsd8Rj(i@ON=={Xr($OLt4(WHX<AppK4bu<^q zmgtrv=y(cdAJ~5xk^2whu?+doEhPE5f!;|F#Pk?KJ4jaK$pCQ!^4mh`D~RD9b&xJa z3|v&A#J|6&2Zg4+{?#OA-2!QGoQ;CDaj0wRhm*arRyH3H?(NiJZhh1ITPUc5IRoqG zMD2hp*UMT~v0XDfrU1rFO`h_tk@d~JodHa_&HIxGEX*c|?Q=N*ULM>UUZsD1<tLS8 zcw(>h#K*s{C+IXRr(`;)G2pkfSch|_tWDW=o$#dNVDXfus9U@_(X-T$L0Ak0d<?6@ zoQBF6^s+3<$k1ZaxH=U@LS+<xed)KaFMarHOjxAWh*^Cm{tVrW42*80rX2?R8F(mH zA-skq1Q}NWW16ZmMO>W8YhfYv0Rw!Q9IE9t`Xc`SMc<!OBXC5uh|__?Ki-=(hYqSh z*SNz-#Jz`*WIs2lgsIM-&TWW-;z)Q+A9yGlBH7v{pNC$ax?UcBm%V&>-qFbNy50%N z@_K%j%<8=88Ejj7Is-M`*sNdlG_Ia@)jT|b>^hpxK^;}LOC4TM(WZxMx{X4{I-e{! zeJkL4-5O2yxXmLk$7SSWapPCF8MxaU?yo))tb3UsR^C*T$qr$$U(c_3d#*j)cAyBa z-kkrE+8xaDK3~GoC+m0-PtJ3C+Ck%tX(+5A(nT5&|L(|^35I@zc70g({o2_CELCIq zrG)8LC4?eQ*cOGF;O|eV5HbG;XiqvA{#T$y-GHS>OpYH8VYnL`&UoQ_cG8LH4JajZ zSRNQ#7|QKuyu7$(dDM6UgEFq0{@}5uO!D(f^4@FCyX$TMDvQHTH5o!SnJv{N-!5MH zfJY4t3M`#m71hAbSgdGU^D(_RxvR2HueInc#$z}oY|1_isCyfk^7$gxasgP{DBcDC zD=Efq*--V#O5*Wp#L%b9!)?c9uDWYRLWV9qZR|(aN3J6~4P>?5cvJ^!xvCD2X7+rh z*>8BEA$K$4ydYMdRcJqOmHo7xvfvE+xoVh&^@ZQ_alevd0GCknN)k>yEzwFD;<VMn zIClza7EiODdp-tk7R@@FIV*mi<Hfea)tJdH@n6{9Iu+|Zu;RpY$t~CrbOpht16B}_ zoIN51WDVqqS^+Gf6P?*4B3T_TQ|2UYc3>B~b_HZ%A}Z8sV+<|kv0BE9H|ORSswCd` z&|i2qF`q9lScEl@%4=~3VHtQ=4w`F%x8ITc?g?2e-=dc%%`Z$82eK38bYhJP@Rd7Q z5}owaR0fN)w)hWk^PxIJxvjnx-jw4<C4}pH-C?!!8(mw&xcjH#MI6(@V5Ro4Mxt&H zW=$1~9zfmcW{{R_LQge-e$ek(1b;EJ!EFKCV9RX5`y`OLQbCKZK#bQ|G~5JBoj|cu zj2GJ&o0rCa)^`NPTgppM5DZ@EYsC*^eB=H5F7rl66vYLUyo#{U&SpG3a9*b&%xbys zE2+=uV{m=!s?b(A5qKd>B7D~vLa=V{6JT{(;W>BK^^xteS@z`l7&_pKyUPnn=GsH3 zif6E0(bZcBg1OB33%CYgZJbRukCilr3^}|?b$5_7ttFqT3SMqb3RArt7M!s4!Y}ie zy_;nO7!sc)L7z4wsnxRJxh|LYGXx|S-WOuWZIK<d3o|cxym!%UMM-NsbrX9y!NMJG zN0CVwdTv-tdi7?@WjfaeE1tjBP}nG=8JIe2K><`;PUZ4QP2gHorZ5%|hMWZoXu*hL z+qm^vmp^|<W`1Pz`0<-<M#G3e5Q)7pDKAIWqec*+;D>}@n|~9SgohuE&J%}erBq=B zZjgNI5Ez2V54x5Q-pZ<~R6CfwSj2p#D?y3;ZYVA)Y38v|{PBe{J0eTrt8&AHG^tX( zs>$?@v>-^=7&-kgiL1r2uY(|M!7tYg3W8l`KM|_Nb!#o34=Pw}7X9iT<|;h7t$lt) zeLT%8=(y;i*@N7QEzyDb$^Wqtn{Gxa8K2IPz@P`12tqBIm>K)Up0>Z7Z{HnOr}OSf z<$W?M=_R66<PIj41%8WbcL@tcL7Ut8xm81NIV?JMH}0;0lubEHjan=?CzQiu>)JIl zvu$?@ugv~aKEAsIg|v93wub3U!FB8uB0WLQis=%rR2DgpH5R*~%xPi`B8Cp`{>4{; z(&Q{^Im=tQ{EdbQxgSo5UiU>G45c&Cuxd=>!OD%`8aZ<^3oIE11=`R-21k)<z(TyB z<cmH+XMZ_2Lr}dqL8g9<`|ywB=F!6OI3`c-juspq@_j-P9s+NJx<iKLV7gJU2?t6M zE759G7HS~e{x{R_zh?EBRo*+iq;Q)&{1ag}_wsY`>)~JeCvKkW-?^40Cp70i_c<O% zkUaGEyS{AKuQd?3RLo_|Dy@DH_c>L(Y~m09u&1b6Wjqy@5q{vx{I=!-{L`V+yQgtj z&OL{BTR$Pwl87hU<-C6f=l+23mMJt+M<RfbCa_%8r7t*QHGvthz{ixQhf%@uky*ZE zo)I#-NyjYvM#|SyI9<R#Us7y4NSZ(t{_G(+wf+_}V-l^ULaA%ikg1hy*{8#L&yt9w z#d)YsjG8gE;n8_auiKT`+3L>iSXh225=ulgY+}_@_=wl!K5LxB%f7dg>%h5kuGoqR z?y4;tgqg8e04FM=ULj?Z|Jer}eK*|iH=Y+gAE;zX<~O3F-`B>ZUB9o1EZ~jk$^s@y z(II06(!nf-@i{mryqUAlBgI3yW-J9FnO2;8rH%Jeo}Z5;`eAvP?rbk=U-&w1Ui4U- zPq-+wSFg|TJ`OyWEzA_4jN_e;)zav@UG>k|W!H2(Jr8DW@bf=$gr^h2?`}ThbInep zc274fzx=Lh^Kp_vIdTRCf2^77z6wu&zl&DBRNed`Vfrmn>+Iu&O%w~>wCDE4Vbe)T zTE~WO+~xx{vtXfjS8v+adEOew1B1EKQtgIF?m26J<M35!H6^9`Sk<y*kng9N;F0-q zEX0)oWpGp>xPf^_KJH=00GeJ^Yi@e9oN$qDIWatf3<SKIu=@O10}M|lc!eYX3-Si8 zJ%3r1i~%ZM5qsV*UNRGT6DKdfMuRz@CIV<yg$^#qjX&pAZ1*gQ(J<q^Jf<x%a96F= zV?YX>o;*P5f8Dk85AKs}`GlXe9|Tm2|A=$OsI<~z3E8xSr{P7>l(s-Bj3N8xh3CcN zs(*sGuW4op)6c%;x_=yDGB&Slbv$a<fsyB{+ODBqSbu3lfc1H)*9)nyES}1T(g6)D zhFfvd512;{$Y59(h)2nW<Ky6P(JAT7Oy`>Q%(s_It;=w5(VESn!jo~em&}4ybFpdV z#S@G5#ADR<<wtx2hZ};APgqly7Nd@nSW{e<GW^8FBG%~RC6Q%3K@bXl=0Y^6yI^P3 zA5ce<Qg1!te>FOPy7)NX)|{L>h~`o;!rS<JPi(vx0~#nJ{OzaoeaQk`yB?H^HaVW1 zfQ{+gbQM$8&FXd2+VZk-exwAqh}=iKcKOoY%F}SWi2<{w_Wpi+TCpeP%o|eglY~CK zb(hKF;)akC%^4;wB1&wdi`!p~`$8ZBFJ~b7K<i2`CZ^iYm7X|jseYX?kF{>Md{|_D zeK*$v`6cgv(e_qhac$eyb_nhX1ouE7xVr@iZoz}QySrO(cPZT6-JL=U3GN=;{V(=f z`|Q2W^PS88j;mtMG5e6-du#8o`ix}iwFP<1&1)y#kIs&qu5sQ~<~~5*^<fTtsR34d zi{~+(I|Dra?Y_T^)zEG3f{YY$J?!+7nQzRyG@x`Cv|tI}v7wN+gBv~mZK||nb$%Mo zPOx2q1f|(g-M%vH@tL(VON+<PQ#t$j&hv(UE{-RYo^grK59tuW^>p-ts2Z9woJO|$ z3WIWNsEN=tc{q6}ZY6>5q6NNKs*>+!B|{iReZZz!G!WBAXIL=FZ?>q|Nba&O$xu+t z84FG}7?6F!saAC&%M$eOhL>+OJs>F`>2#OBC7sxR9<6;O@ErHir^gN`cHj^l|HHAn zd11?b<y`=pJn$6Z<^NMG+=S}a3VEsR3aM~0Iocm3AmwwvIZETq*@W|L5;v%9+5lY~ z*TYP#fK?i9I7mL4j7=y+{CflR2MI!W{E;6K3E~7y;G-G55zSjhf`dw(?_-aO-lDb8 zh8U+$f5g2}se;6v=c*dvmSI!OzaD^MW>T%21ZlHlt*ktgqdum*Q?H&hZ>r#nELg6* z!>aDoiwPG!tDGy$K0CNYs43!&>e=a#M4w+co;^n~!=NZBhHOp6`p(rL*@4XPok5I2 zoWx;@Laj6w2<Mxi8C1%~(~p5@mhB(A=m)~F3Jd1)cHeR<%$8fN1|eTovj^Y_P%Y%2 zYMCF?=WL}GhB!e<7K=5PnjRF3E%g^owKP$!@i<xV0aNjO!q`kmo{QtRytjL!!GymW zrpn4>O2H(X?xMOZ({pmW1o;A%l6*N7W<DY0;egWkcl2%0CsJxXwMiqNn^7*UV)IxW zxC~PB;1rA{s<8TiI)$X5HoK^4&iGYOJ!`77LPT^b=C~Ey;f=s#myJ_y%0{ggg=5ES zdoP4{?;Zi`p_8EZsrTZorOOs1c6Q6ruyB)VmEoRylk#^nyJaM?p~W17S{B2Ih_HY@ zP^x#UTVIUvbw5s?44suxe4_)KBC|>Hc&oq6VWbr;%sH+mZ_X|0<(##<=;hE;2;BE^ z#jkpigOCbZTsJ-4!S%<!q_G_|L*yS>!o!{5klE4TX6*V37sTL5<D&J%)a8ui6?@zz z=vPA2z*U3Y`8;XU>D2|cbds~Lq`+Si(E-!3O#Z4TkxYdOLfiso>ql9XEtCC+Gg<a| z0yd=b8KlldpzoH>>op!WwXuM@BhAxAclyJ7gVXBEOt+0$r8K9_DN~;B>-+O|%syGH zNU8}+L6*u=UwzU?CVu?H_)2>G^76q3W1B293|!)AivFLBlNOCu>=Wa76pldhclOhr za?(P&Qyz<l1@OjlU*qnzTp?@eal|UO6>P@wagUX4mRF&1K1{8sT%?MUGmi)^-*r^W zjM#X!c!CeFyKQR%$>SDanol20!ptB7$}*8>{P|!wuQBRZ!c;@nGNQ((4iyv+?xM3+ zvGz|Q2ldUKxg7822WKk!b}t#R^VtYTQMmK4;DNd@J8CSKPEc*WuJ9t)LWo6_8=?*T zl?Kmp9Jez^wK@#>?(?%y76)p7qosaSIoRg#BF^(B4KSDnmKRh{nK96X3gs>j=fvYo z__fZVCnoeE+sv0>@6Sl?;8U<>teEJRq$JFAJtwVWN=1lhhT%GvFO+Rp#Zh^!cJLQ; z*R?y&u(ku}ymjV=C`XNHR_ab7z&9*DLQSv_D;qRm5+OIDqe%%CPjzXKAwAT2H=AJ_ zRc*Ni(jh!}mE2mHvK5tsTHZS&haNhbIb5G<>1%b#h-*iu@PG6jN<k35GCfVZm}bO6 z;<}1I;lSJ%h32Kkr^J`AVpfdDz4dLFUA&r>E0PMjQ0Pfcga%hWWG%mvE5fU#W%5c0 z_=zW`N*L_G`!RwKZ3da7-KJ>ES$(`Cdg<h>xENbCPZ?}G{N`)7P2MXjhXt_oRCvyh znhLNyT&FQ9rMnf&enb*3Rk*T-EtL}{7&XVXq{CEwR{7He!>XP;@E_hZXtL)X_NJs@ zeMQ>;=qrkZbD%>|jt2o==NIO!L9!ZlBgeUsJmpxs2?ReH<5F)H^01Q>G|pxjERHTx z%NYiKod#yfmOiM}&?h~FB2_@<QAET`M;z{kj3Kkd;AV!(QyLzu%$RT2d_}f&1F@=C zgY}^rhtL?YQl4s#pGKlz)yn<!rx3Q_n}CV<lU1d-Jg@G9{&*O#ZJK}^O`>-13&~RL zwFoQA{i=8MT;&iZ!s?@B$>np|!6frJvP-((4FQRRLQKr0Owl9B#f-HQ*yDN10jnZ< z@_}Y#+3^dxyMlT@$pyS5><^NprF;U0)l5GdhRk0dj;6Lq-Px9KdFAA1R~M6Gv9P6I zOEV_o0bwea?tRb|OCP6(dCxBMF54$IWFU-I^vSbt495lu{1&{VBKm6KyMeOEKw-fg zS@)-e1W&cY$xx%j7}zJW!D~zJeGsAN%eO;NL6oFOy1-Ma2(_I%aeEo7VX>}|LbM{= zf`$G!393fjB2LEWY$!3c;v_NjUR<!y(2I$Nwg|<tC3kdBjC#4?PY_#f2af-!olUKH z2H#;#_(x3-_qn5Rdnjiv6f=z{{{~+5Wu}^==Lu=ewvYB`XMv>|l#9*8>fG#q%HEgN z=m|nhXJCqd<?|kSD@ENS9vtZ<y5F*nG!=l>=CS=d&3S;~t-&sd=t3SDK|Jn<<)n<N z``nf-^Hv2XwMC|YyAB0y*L)T-$u&=bHJ@?krTnfole|jQLn;;3G)uVmDzRtUlcw#; zlETIDOqxkjQV(uQoY?Qfk$#?(NuP=ZMm6UpC??pIZ21!VZ)hYyusUP~%`jM~7phVk zc|6}d2^6mJm26EO<Q7!8m_A23-+G2pU_LDgy+k+B_}~Kq`7B$WFJlsdwb(G;C)bxT zLdcUEveS~BQ_4cwZf3P_IAlpe1eMH?^~wX8`z(O|>Ph<IaWE=t?&6K-&x7M>EZ!?e z+^hXA`un$zFKS5!1&J_Yv3ZYm(~^Enp89@2#tJjq$|eiPcmyUL3?&np0C3^evl3+f z;^O2N3XtZ)wpxl>q5kL;TQ%YCX32<}q14O+*5~R6>vI^LJdfi+V3Fy4V?L=7AD8{? zX8qFnWT?G_pkFx+qEgF}{Jl4(f1A?Ys%H**39biu?A8crYGTfG!EsrX%=a%qBEoFF zkFh&=PM_9pmu<07`0$Ot61ESn4D5CX{AA@oUoOv0Kwx@sX?mZG1?74&Y91D!pAd`a zxd^0g07kU|Q#qxs8EwK{wlQI4p3#cu4<-xpMPGAYJ7HzJNxgphMgV%pwADvOq<E1% zFdym;NXG|NGC7PO8O&An8xV?PYSP$P(0X~UFO?&9^ehn4Qcoiku+eRgoVz`GNM#Rf zpYm^!(y9!48yXpr66(db;lI%$ZwK;~o0!%>h%Io8;LN+0=i73_V@RoWB|``nDJdi2 zNqC7<UvKx6&9a~wiRAm%=hO)&D3`U-Z0#1L_7m_J#kPq0bgR?U{%F*XXd!rY*jV?z z_P7{suC!WvhR8fE_d9uiD@6=${{K_6Um{?g=tQ!5Vy?^tI7j)+Yyx~#|EN#t(x?Aa zHp_mMF}Z&Dk)Nx6zI(rWlW|Frzt)a{*>f><kB{YxnWX0eG`WL0xl0+0te|eLK)>QG z_VyJELI-SGB23yRVpw%_o@&2EhiZA^xOH@-&;Yi%I8LP)c}S><o@h%$BnuQ@&Jc<3 zu{CI^Bbq-5XC1!($x7A4f5OZH93R%=HC&Z##fMW9@=kut6Wj22C@v;oRqKHoMykj! zCi*0h+1v4nX|UAxu0_{lJqvTC!`&{bQ;Tam)@q9|z0Tp<ytOLyaenE2aiQ--mMr&H zljJGqhZIx!bcwJI^MRL5C&GqxF9@`J!kr(|h{KASG~;ASftbVJ-?Jc>0M)pho`&C{ zOUdjM_EiD>b~SNh|A^!$_xO=x0rBCCBIM=jBW8Yoj3CWU=Bre&SG&t&L#rT)xf+(a zmF_}knVRuT*V(?AoWtPfYQGg{=bbsPD7M#fBRK7Uyy(>K<$Z5vYH}<9aN0**x*lj> zuQE!k9{ru6R^B?>^IYsk;D29dKXzZ|%H@CeC>ah?{=`Zv^4q})Ph~xPXx3GrFo<jU zP~+QDmI5iq!=ayl1RfGQ-gNLBn#hlQF~M#%Aw#t>QhPK@6WVz27&F|>bY<hJTNGph zQ=lji0pukaMbtJn5GvY%IFMC`-}Wg`00J~cxeBhT%EqCev#p0yU9RYVN^Xc2-aI`E z`%j$kG>ldqC}35rwO#UhknR=xex!YirAPzXQ8chCZ#WLPO44U9K%vNb7vA`ongBFz z^B+ROz4TJ*>sm4AUiCB?kt|$cs=>q*wD=ibC}`H$6TX0uK7QySrA-krN(pEtgHoAy z0I<0fWtWi~73h+|ZUuK2WLoruEVtDe)z++2QowZw!RNRTHVdm-`pm+R&^G?q#lcmT zbir0$L=##hGaMh0@-^!n10m6V{7K2ySH=caxmhS};-}fARU|zN!!4#$#vm^1xPe|H zPlZkC-!5}ZQ<DcXP@QcpqJX1bGI+xCSXT@gP}#YT_e1l^7XO^q2VN)H%w2F$L<ObP z#RMhsX_4pzZi`F*0CX`AzMo5~vC+Z*`_}ecAUIRaM=6pZR)UV8(3GlA9Dyk|1L3jC zQAwmMk`bhiU|<iad`o_h-h7X9R=7FbH%crdHtkE2heURqY6GQ?(_qGVFc2534uvfH z+(V^6QQ)@zb$@cxLWzQ-!@xBjUOW*#$&Tx_B#ipnSA)|o?7Rpefet8DG=E<eq-p)< z6<7<t^7MH<E?HZWQ-f}5U}&*YHf^!^aI#P-X+e=2d?5DR%z{eeV(lwH!JH2bDrcCf zdtU`Zs%GV018tPf<e`TUg<JkPre601&VNF}=7V~W2(B47S~_b&E8s?KmonBT{RVOG z7f;B3nYeU2qSwg%ttn_NZ8IL8P%#e=-^`B-<{Pe4@j{mUF*^5a?NKf%Ij#HgSbGBX z((dGoWx<=&NAI7(B8Ti&8}i4G)?IFxHS_P@oLWNPXwjJ<5P4}82YdlS+{<&<)bXRP z$@}MO9emR3A2v3<VqQt6?AkN3<2s8zxN&_hq|Ycydwc^_wx_MSRaKl;HF?TiC;{l) z^Jc%HM<EENTb^1i-91icGS@7ZE`NdCU-y9>{CuUSvWvI5$AjP>XEucSCZ?<7sH`1Q zSth}}pIZCyOxE6Ae5F22^zMh_c8aIBiwK}s_Vjv1!Ts|h@jBs8{k<v$cNjdVPwvwq z$wIOxUOJc4i$9azVU=DfD|RZ#`Hv^y?F6vY?Yg$p3IG18DMm0SgJi^{|3N*U1>0CC z)_)DR`>S*G6EMwfnNu8<X7Wt1ihS=ied@7qG)RvGF&t*yu(jsPH<7d{mb{roGz@qg zwH;*%nbBoPGwU|eocnx{5`PsHolgYAa-&s+Nnx_^-6ek#%uCXQeJkg2p6L?o>!Iyn z(yF9q5v21Gt6$nQu6c#n7)`18_D3AmiV;8WhEo*}4$tNnLCk%L9<_k6tNQ6{CYhkF za=njW#a;y9KPd21z?N+-s<v8VGtwgZB^}|sVXPS!>z|rL=smSQIx;A&UDuud@OdfL z!aZAB?7c;eshC)&ai2UgDur5`ai4*r5g7_H4%0i~r^CJ$*r{DmNvS|pu|n?m`hf6( z^aaJT@}ar++4I=b!sd|V?*OWO_+J&s1>-(Og_B6<ymM#})Q3G2|76!AL1jdof7h4a zr2AVu;4+Y{?JET~5|*}yL*v)GX(`Rz{V=B4oNy4?9qsU~v`JsCDSU^w_wxiq`RlXJ zgxm!G?X<beea2kE7N#D88v1)5^;~3&)DN*>xt+L05L(<X3q<009yE^lY+UMp*^Q#n zfj?*9aOj14gmlvyP6=*iHXGP^1K5>jdpx6c<WzAp^T$i}>4!nJHO67Kgk1IKmzYA4 z-3&&m8Hp<>;37gHx3`xFK)dnucF-Q?={l!%GR@3w>{sIaO4xCH8X?D4gWO&skC!Q+ zkp74n0-$o0;WFkUK98De*Sl`AbnQ>E#vo=nFUk}^YRNnX@;QQ$>1cV)qkfRiGof6U zbmR9FC#ufIwm<+m_XMsCHlFhA)~SnTKApnOID!1{W+x9H9@VtGt;3FH=AIVa(=?fL z4p@6*;zU%COPvp*J2g1&n(Sd-UI36@w^1*H=?kdEk8(>|!GPs0Zc8eZ@9d=5^Z5{3 zUvi0J9}r4;Fc18{M8;$DD(E-3C}wLFt^v5umNy5h{-CWEkjx9M-%#SgQ?vT2#}`bf z1(4{|c&b@OnK=8&K6Jp%O&5b|p3dT0UN5fr2J4R8fhl@IF|6`NNR*ZUBoI`D1htut zvs~?LD2h7bg-J4$l4!;htTjaMayC)=;w7x5`%{&r>=*E#kA+uVPIr{N9nN;n7Qyw! zT|xO<Qxyz+B9`#x=*8||4?oFR7PA$ksy@gF?8J*r4aiGm!v|$gmMcl4l;*PRU-T-k zyfEubU-jdiQyDL`6fue+HzF%UGbpf~=aJ-T1K4KI^lnWp84YF?qLG9cjR4BX%}JXx zcUEbH{M_ZY)yBn_f~-;B*%qF}5&_$SdcTW{<8di#;RAlOE5_1*HTVkj!_0;$Il1He z<uW-SKl?&URF5!EA|bl&1Aka++fDp%=x=_HCT8g*`;pU=Ry&5APx0Av!iC}k!+xhf zJoaEZzl@&y*BSZKb$04ZLho$t(RdS}HV;t{HwE6onF#YSpbM4Dd8H<oO#ML0?6?W` z;T4L2h<nR7c<f$S2XyQs8gF#lGlI!!&Fyxov>T2}t&+BiKRc$WKL;Qk5KO^+Gt)D+ zsYNrcYYD+Yy7)E_3O0tS9uM^M^VILXYO&mHFNtcd?j6wGj^p3mJp~ui0ywN`Wv1`x ztu>n-BESvZzCM|@NspzI+jGLt58rvL+x?G)^lJ~`#p|uYhjF?wecSJE_%QY|ZySqc zF{&JwE+L{_0Q05lPL+#yb2atWdc8G}cN%R*OF1eHv@Z9^WE=fT>bn)c!vl00Hp0K8 zWqNoaC*HO_h6H*NrOeyUEJyYhYOg*XFmv%w?+-k_-hs>RPRiQXT-h^X&@k?;MYq?) zdsmYD9A=yBznXLkxJg*tf(Q{4rM}<7=wG}NavY>!&38O^@Tr-bGr%9(iufY~z!&cd zj4JZFUUi_DDZPMho^e-gURj?hoN(EmBeN$~8OiMZ2iiYs=}ASD&2EdW|4?U$UJV`z zY4#kdB?yu}<*;}_hNRfijbm;Nx3qukrC4J;{8S$?Ft^(ftiHlkbMH-P=CK8NUD=9t z+WtJ6Amr^lrP;~T%HL6gYNo7=I0mn;V4~uZi-0^>%r?i$ORF(vB&CVJZ!FxRnNR=e zu4CN9@yb#57+rHmNG+bgc{S_EjrV%Gh27Tr>#k?tzI3!2?w^upX1(HWr+PGDMk}@F z=aCI8XYZQPUnwToCWOVcy(_EfmSq<fdTel3f>t+HMsd-|BtvR9Um*(qI2CR{GUONa z{&4)#Y`dN(>CQV_F-l4SP4RI&cdn6ddDAGJ`xfA`((c`?_q%!zmFwbab<I-2S1t>e z1B^WBu40hES^gl-D;|>>i02`RTL_;)lTavicvcLEE9ngf06IC1yJkJfd6@KPE82<7 za_gri{7o>It#wIKJ?%^oh_O1b+XtUPWJzRIv_wBJn?@9VS?&)55Rs^Zd01~}5hVzz zAKYQ<t@w?uixz4Z364kN=(f12lfjKy^<<9FVYz*M9f6rn4F{Fcqf`FE%inmgQiqIS z_WDFcdV>L}R@VLD9IuN%WG4CoSVQoW8bjO6C60WKw)-1n$BN0Qp>_!{G1n3C7p!{_ zyxdP$LibQ)MDs0lefr<dJTQ^Su3aOhVnZEK{Jo{ZjOaRV+608#!?D)$mNRJc25=o< zV@kD@v9V<0w<|&LL=#%l!jedR+et@D(y~2p;ThF>30;yRfA6+L`*Sx$tPn)8K;g(J zWzZ)Ex?p2##?~5Ll`<<^uLFJhjy8-=D;w;)rA~xc@s2Z`TG^$hJ9i34{+E&L`88I< z&9EcPiiMla!*UmXI5s%?3NKQNI3+SzavYryMsfUlu(pd4yzE|TdFf|{;;ZK~DHNDU zBbhdg4_fD|G;DZk#bH^wSlk>27gl1k>d)?91#Jp}64&^5zOWQ#qbDvu&*b>Z(){7v zg)WPB1!YktDf8HR4eP(|^W4OziL$kueP59bdOC0O70p<{f@~{^&?V=TepSw`H`9@L za*MT;YWT*gR#bu7{jL(%zlUe_A;WvA<MgZ$tFuG@6ifTL(QFaV{l4MKv^u~gmu^s- zZ#z~?HY&ZtZffz^g^@Y~_Ykx=AYVST@V-8gQd+)nx^32E02fJEWQ0mSR%8T?)Z7s* zjNbIE(3Ffr5%V6Xb^Z_^h)1YIF(r}y-6eQ;$XI1P!wP)LBw*4#&!WIm4tIM=Y%R>` zbrm?+GIqqaACX37Yj?YY$69+%tA%~Kt6L3jIEcK2rf1yKRZoXbz}lDzOqId39hppb z3a8OswKfM<(e_UOwd;=T7YTBWX%>FXXN*oQF4IrF9bRjO)e`?LycGJi_g=-PhlojM zRBLA2ghy_Sz(vpLU6Vu@gm4{-L$wk3=zGO2^19pRYu1O_c|tw-uzg@Obm&g?tP}{s zO!`rVIa{;znm&8`RAcMR$K?&~Qqfy4hA>K{2N8K*aJt#<Gi5smE6CG!zj&|Z5tgO2 z;Iq2PVlRkUi=oH|d@s&CQ+XAhj^+X)f1AI32J2;>S4H>Xb!_ks2pDY-t|2J^HplJ! z=JP7j5K3F7{fBJTgCCJ1Gg>;svwcWqPP}Fg+T9}4Ywfnw_w{qT15OCo$)t&^3a%)7 zqID1W%I8~P;WFJs@8({HP1_e82`?vWsTqXPn+4rvETf;<Yc2-&P!G^UV1vBa<!ZA5 zGEX5v-|@V}r-Dr1xj;NeHu85tQXVyQL<AO39jBc2EIwwc&UtR9OS<!~+^wPFJKki# zZn$GwBKgZDhv7+4!MT;p)F#Zsp2Hz|+rsiL&B&R{Klv-TJ=NY#F)}NAoyjF8ur~#Y zk<@vbNrX_Oyz5@}C!6PGAP^w+hll<hZ!lE<6~-Rb#8a)(5w+@DXex9B{1Y-)#;Wco zuUZQ?d-@FAt2C~0_|3KTd#HeQ>jcE<Er(xNA+Bio;~7^W{*`RuaF#3gs{@wS^lN8x zm!s_boW^5Sg;;BD6Lk#)BCjWY-LP8&urL;k^jMJ1X8l1k$dl`XSv=Bt>0AjjN;14Z zBP1Q}oz;?vW;@n)+A}@=G$)-8FfOzZkIwZdl?8k#A$zAHPS}u8Ph(HZEcwP&`L}zd z{6<(p=A@PKU+P`n2T_v&pCsw6ahKC6dsK7PAm>zlR*Z*OiKwojQ%cPOnUB$jrq7s$ zqV=chuNT=rIa>a7FJLpO*BYXBe6f{=8_;H*SO|M*l@*JV(<k;R*v?@w2K$;VzhKWJ z*ftl<?1>8Ud=F383&%R}v>aUJq;AydDhHk67(2V=oq#)uZf-<vxTLJqv90KA5GzRd zt!z7o)mP};85e-yhsexU!@<_Y37wl!_$jDR35ADGx<ggio@yG$VYp5&!mX>|Nto7V z0@+b~8FyzkDlxpb(pyQzLVL}87*=UB|4tN&UgBr-LHU6s`v!$j{}89jwOh-|aqgiO z+xDLEO+VSa&I|2xRewLpwj`&g5Ji&%l&9K5>cF0F`AF2g@vHY5Cp_JB(ytL~ErbxT zhQbDz?H7{duEy%ArjDd~z1We9ohz%}>ez@I(K=VM27jw9GNBR3{;Aj6qyk~Y1!o;I zu=EQq!vK*@GQ$pr=5mB}hd<-0t-aO(`%S<3qYTe+LvU2*T?HV03f59<1uNGb|1i}e zlPLxF0p}M6w#{Chs%pk=e&>VeE8^$?t;Y_sL%D6k1>=s0@MDiKW|lE$tPVLvtgD~S zQbk}(v~KYGj<FGNr-)4AFPY#7zaVo^hWl+qa#fz^plH?IyMn0_ZUWjC!%o02d*oW< zgOJ#P{`%BJ{F%Bu&m_?kFT1H&aNRS295YPWQ?=YWubKW6sWI`EdWah_ws+U{@fa@= zjhCbQtEjexE6Bf-qm&-T!WOyjXGF^^22b9H<+B{^FBiK>v9sAo%4Cnj#qE*&*6TC( zM+{lPxxr3F&?e?7P_?sMR}9;W4gjYiUl*y&0TyC|<r1N~I>Zv9z};ZOUscI4gjw93 z9y}+tSPylJ=NFXnFVbY53R^S-Nvw<5UKyM0GX=+|JdJ<qeYY^vr_N&lfQ8c!EQA{D zev%N8zf4Tk@X(Rz?@~oZzkA<tk4Y>o+q|YHY4!fF+^_cof5v18$%v4oR@Gy`$KGOJ zQ(;yc!-}>0w&_9JLHw{n5=bUtXii@wY)WC~fdqwTh9`MPTgK2ZfQ15<<yUtTr%P{s z7nih9u4LV?VCikjI7VLzM^8!ZeZM1n8zHE9E}oNHa9;M}vYKUeJJGX+eHWRpZ$_wU zc6@=(O&#`9lk&xT$sp!a2t^^%5@>q~5^NF{RWtZYh5kgkjlF*uREyeJHF3!B;LUiH zm3;_Z%uR~ZPJF+jc;62tHTjXMcScnz%GzZN?g`h^<K_@~X|1~)C@7nFnO^2Y{k0xB z4pT+WzzeK3bBSDU@j`!b>COVDH<Gl%cux;v6YC{Bex}kk9<!{qQpnIOG!?FbgIc6} zWlJrvSO#~~_zP;}-4^vIyhzOrlZCT287?vMw5W+dezO%qTdl1$Tv9Ld#*-8VYg$b9 z7tun|7n>Jipz%PQc8P2$n`^(0s$%{_9d^}W5GRs1m-Tu(dg8h}5%TY=am(IMQdKj_ zF4G?hCJPgqsfMbVyT*XsK7auC)mNAMfyM#gPsc}IkW9;cw*HIjo-T#bBxT$~!)!3Y zzK1wKx|`C!8eIB4zaVu8SJ&`<?Gn{quHJ*>33xvbCxqw&sqd?!afQXQ`<v&5&{8b4 zPr3BK@VA;$Ha1wT>oEM63Hx4jh_MU0<2wTnlheZp>5}65ytYkaDj|K2_2+X|vd%(3 zzF0R9LQ8!_Sar#8^&Zkb?{1(<<Dc$$9*5Y4z)s@Zr1HFJZSu<>U{9UP4I;^m29@+* z)@?cHQ+%Iz=swv##^-&|1Y_Y*fp6Ta$CQrLraUzh^riS8eG@Zc6-|bzLQDBjCHp03 zHz<$;BYT3AYM;?qc!N2UiWN=}(yx`meh@l%VJmEg)wz^Zg1}&g<mjCOGKd5iici+I zSFH8rP8<dsCeAvu7}**lahZB9&U_Jm#<vQY_jc&*4e8`6U9)tS&1f`}WEiL*;<myo zSdU!oiwi32koHsq^18G!+;**~U1*ldq*T6yQ8O3Z^RhVJH|IyaUI05B{7D^c3v9`Z zCoqjMJM3-!A<a)bJEetN)d{_<hRMSrM=LedYl8_N_lq1AQ-*DQF4cr%T)=COd#Z=K zP7Kq5SlC}igaq#Zd`OM;my1wjYexb;$jTIv(@*9Ed0xx~ZdHkVmS;b(TfSju^FHN; zGINFFoc(@Ez;o97NpBvke1avKlkazp_J=;s_3Nq}G|yw;2Q#{ae|s?EpYWp;Qx(~e zGl{=7-psWYJe@W}R-=UDmMd)?9%w&bU?B`&aYJtzB`Ym89TCxCd)VIJ)vG&!PSH_d zP7HYDole8Po7HRs6#&YYCYXk}V`>WMrpBaytDxfyyVR6w`YT@^wWSF9rtR6U(p;W| zopsA)>g{68B#k6<!^96`<rfMqHSD%c+s6-iZl?CM2A*)3d79}{E&TFDAnjuq0{Lin zd_g(zV6U{4aEb?|B*W2v%n(3fhT^<Id_&+yH^?9ktE}_5q8Pc7(DzI3EH$~Ut<^{1 z2m2dS#*3_$dKUsRx*6owmy>je6{6J3=?C_m0P4X=(l=fD$g&|UN2SuIkV3@2M5u2D zin^h{BmMi&1A=GpbqgI)FcIoD-~*-&?2mFxKi!rYsl{;mtdX>k?nVscew~)BkA+uH z$F7iPJ`~Ep-eR?307>AVP;Rvbl<KP=oNqQYqs|XC5C66SgPAT+(aOJOwu50j<$%Us zPUiz*)R`PN>D;yueTK}*N_yiiQjW|sax&KzqaC1<CF_Y3e*JTiCz)<yAE`)MF`NWs z^paqbBScR%i9lH<czrKBrd)(<8EG%@rAa}vduKOLW%JH6DV~$l;0~x-DxVrPJ!Q9s zN!Quza^BS>8-{m>hsm0#b|0D(@m`hC)`l9K8kw5h=X|LuLBL#wmUB6fVpQ#=dFUNn zwgmiH446)^^)FMtOC6J-!T<(FuDGPuN@Ov&H;3~(t(!uv{!*Cu?S9TJkB#Q~!$IN< z4rxgg{(R%DqZ7P`na^Cd>%VRmF_|HA$Oo4Li;%=OEL$;0=zCG1J<<~10zUrlfR_#U zL$DovZdhk@d2%U1^vHYiqclHz7Pae&qns!E?u6vpb>>*u5jIcUHty)O5}VU~wjd*! zzHeK;>KIygxxVZ7JFE6L&yh@tqyF!D_}ae^8B!;hQq&-5mL@wr`1)#QmHPH$U7l`H zA&8VDY5gE^aKT*?ot^GQpwtRgMlDF2m!pY^v*9<e$U;mSRcP+hLtQ)qzyNLFY1=>q z17Aj(Ufv(alY8y{taxv}uI(HGX;yEFWL9--t`OM@zbFL3WtdnC!R9F1)WpI=`&~Ep zW{hfVjMzfMV*urq>B@oQe9*2kx%LPe(CxpyR(hZjVrd*KW8i~&i<A5L8BRn6p;w<c z?+o|8KVH_sN3weiAk&JKq@y{indS=!<gPjI+_OmQZc>ElKGp$5${JC8FiDPsCMxyN z`T9=QC=%1}O;!DOKvSd{1K<|8KleGqDt}47o;DYKt>wquW;9Z}5Qh@DGKIwP1T!2O zWa47eV5MCRFX@gWvzI;971?+fMmWBt75*W1R`gAO792Vp{f_|(txGt$8r>y*kDABL z9;B(jC<zrdnHtPAIa!TjHXBiHRllS-W1{%9QqXUBJJ$C&UZs}|4&Nb{M?oM^bsr>8 z<q>Pj21!IMQ%{+^n~G?fqoh0iksLvbU1ys9Wr3&^qZo&#n>p{0-Oem7>C@CU!^w;W zEs2Zm?fFl;6hd@=(%mE())dBUKbYOmzTn9)0$C`GzFd!+9GWc(Apvjn=FLm_9vjC* zrKIffFB2GQWF6HfTWVxUJFrUoa^Cn#F1P;BvuP)VAEv&R+%aKb5-O3RCu(ppqGdDx zP<GrEnt7?7H3+B)+GK=ai@<3{PO;_{-aHTvIySvH_66JDNNNApitv4`agN?<g_LX! zcXjIDC~ukV*-uvk5bYt36F;ED7Ru+7vmRyHGAeuV9pA`tB=FtcF<YXPhc9C$NXs@b z$qesOa=$GV#&1i-f8n2?Y(5yB1mK1ansimcOTIW9M4#3l+kT5f(?Akt&%GEbh~&OD z_5b420m{R!q;hv#E!Bx0u7<|3*8Y{kN$aV0@_yI|XSq|Ra%aa&B>djePpTN1^g70g z4K2CtF8R&WwG~d5_U2F?E>Oz;Wvm1ixh(F^#U$%jlJDskvF*<VY06VYk!s4Du^ooy ztDNnc@HmS^CTnxe8Vm#=;3kJb-5h}ma0&gsV+m?f969zBsZ}!4m$`3Qj!|AxeEz_x zko`;b+pV<g!Tu#Ip_|0*Ri0rgNfhzH_i>Jl*(lJAnpTBz-|s+j69|*~Y++gwjrjE+ z`A~O5U>3;vfBbu>=O#*DH%rk$FlQ21-U$)Ct;FZCe2(nMV^U)!$SZzAWhVu?ImC0{ zevIxvzqCPQ1j`OmMPld0(gBf7&XeDP=`gVtLc8ijZ%UsvU(c<(H>HpBe}6sioBO0D zUEeQhC<70g5iNSdo^8&%K1M%}RWl{@&i9P=Q4(g%R?3wxV{_aH(zKX2E6x$C<-eR{ zpAq%{Aquf1Vim*#7C1w#7rn=O4AdbtZWlKMiK!vyVx%n7CI^)13eD;4c7^Asji0i; zsJ!k6BN8V*RxBW7AiKshZ|ext(_kss>BPmxSrc&TEPK$1Q<<vH1-LxEN5f`PYLP8% z7Y$z`fiM1i^Im)TX*>{dh8t^cixA(|4u*`MdpbZ&XLnxALilszY|x>n6xbMy^5se{ z_3g{XeSbfBkXoyEN$V_quewCY3hw)@gwBbKn`j=4O4>mAOG-$$ogj499y)UE48ZP| zFNYU5Z-(aVOBa^KtV*ZorhXgBl1;nj)jLR>d*7>fcU(#532ua+%+C}?+z?h@abt0D ztu|+U2@Z?@LiYf-OTr+Iy(MJQ%K#5GklfKkh4mNwi$Z^r!6g&xsJF5GdE<2Bqh#BA zdFdqKiH5#At_q%nnS9x?cQmv;qj!rp79P)@U{T&+5xuK`{O`bB70hQtBmM3*HdQj{ z_2J<u!Lv7q&zn(@6>^$wfd{>ooA3q&yMw!ccF{(2EyQuM&~jPnIp=mC^j@J=_`r8e zg26AC@`PAdM*1w|yB>=@m4r`}MXZUkm6~Y<PEOH+nJ|nPS{iVh9A2ebN6(@z+YM=` z^7ozc0c33?>fE&;0F(c0#Z#>eLm}5x{J7wYV<qmKZ6DI|>?Ss`jZSJ8wltvrD`#nz zEcJ@5#izDcI)+Rf;wm)1;-u8F8d<XGjQHBvrFR7iU?C(+{JS#su*}u<!M)c9ARoBG z?E+2mP)A$Ydb=QuXLBj`B4y+SVr9`d`XD9rUPT(93}Ym+IT=j?`2%Ud6ui<U#7>r( z;cU(9e0@VG37}llF}_<j=1_Jg<gw~L9mo5D=RETCP}dZcyB2mFJ-5@zQ);~cS$me} zbeLHz7K7yTEb(Pn0|U1ftSgC){+pDls`>rBvEkC%yEiySa%Bz}aH)4$6g0eP>oDos z^<JZJnXN`Sm(GQP<AL*TC5cdYW6l3S7s0WY>vw9xjOpz0nS%QbJ}=f9-(s1+fj>>v zmskiEP8x#Cv-qS`O<ts2sXKAMDZJUj*}?6DtkS8w9L#)S=`eFPTE3en-=u4=2e89L zj?9EDB)}l{12gL=3aJskv+0%XBD0(J4?e34K~@>_3oI6;Xb2w4pwf{h)za5rqRn^? z19zNW_^MvKN?JdCx=(4^;+uz+dI71eFi{~p*CjocF2gPCQDA10?IMpMHiQBYpmWl_ zt?^Jtjd(gR24WluOhlnmM()9u109`mliht*R$0Gtt~k%vxIYdzcSLS8!s1<e$e=&D z;mEuzDCTC|+{lE70%^?E(CC71_Td@+Lm<e5)qSr>!1v#fS&eI9o{!#_yr37a{vSxw zn=gSa3m@(HTP`q3L`-HgA_p|Roxn{i_9&0ddsb}rqK7I(!d#<w#(Z9$z@IheSL&iU zdp?hCH<>UMi2?vj*cMgzxrtY>T2bbd14bW=6{72ugFNd-XXf*_T?C)oD2yMmU<M<} zVP(8*R{#@4zvxwY;)=Y;gVzLEAL_2T0XD-cx3X$x?rA^e-A65(CJUvh@}V=AnSD?e zZ&rvLh&T)3Tk7vhER}Pt5~uuJNY+LL8rhPS5R+3|?DYy8*!fu;yB~N@J@G3`2f<8^ z-IJmJU0*#s$Fja<rB0b}fsMgo*`=c1G>NSQ3j^*&hxI$T_Qr9g!x+Dcb|ORo0usP_ zr1Fs&8wvk$m#sruXIb?cC+_yx%8}3X9HAM$QT&g3_dx`Ej-D(oh1fy5Lf)OfY!`KY zy(5V2m8mcR*Ghka(t^gD&if+!jKqL<G*_mUl#3JB98Pz!-DqE3s;9K+@jvCtJ{BUv zT;p8!9L_s$@S@x=sao}dMRYH*8ak1w5G<1E++ZPh&*uqIog&Oy2z6cL0387yV}LR* zCFb%j$N5DzBvD$Y)zQ+qmHiv40H^JrI;`l!|Hm8mV5L4^Tyx`UI-lOk9%t`vvxu%K zMNC2CU0cfY;!m$D!x6@GS(-l)T6kRB@QA)HMz-57DJC47cgs;hHW7gTU8W>;W*2D3 ztg1bbr!O^~uu$oFwMJ+u9^kUWK$BxqQ5A1tSFE~O3;u#^)k*_F`oX8zJoy(-7xS`r z^DDy0E$bCuy?Al><8}gtS=o3<3p(PDoViY(d{>AvYo|MYJAQr?q(xQ)55(ttnrJfc zES2N|J`Pmeo_ZS{$Q?iEaN5>e{JOpp*Q{eiOK9<4tU}z~)Z$-H!O~vEJ*6OdlM%U- z{e#+onAhU=)c@ZMn2}J9R1<{ccK^yu{a06G%T%6}!|kSry5xXja5QplV&2RM<DJoX z0LhgMTh(mh$KO>Xs;|1;rgos4>BojR129WR)-Cm}>4;if#u@|xY#F0HuNyd7h=>K3 z8|w8Z`9f9k5;J_qMBvr#VZAJg?aw!R<lC44Oj|VRY}vH$T!{F?20pTY=23Pc?{iI9 zXlvHcw>{x{(D=&g9smnSZyk6MlvOEuYZYw%Q|IEVs1nur-<)I>)sLd+mK(XU`~V*B z6*=SUZu@U{*-3oPWu9tC4eK&2nSuOSUWMRZm}bMt7%KmL+lU?=#|R_jevID3RVS<r zU!IaHL7NAJbNx)=B7-4f)Pi32=}Zu}>d#s@Nn1UEQ{|oDjG6WqrQY*1LGa^mkrdtR zHdW~mO%e2e#*?;g>qM!bDsJUpjCgm%L7TV$j`uMq!L`mwtjv$haSE0!4iD?4IEWTn zla|Yjdq_XRNVc+J90{io?S;lkQyGi+(h~8V>~7aI7;J_>VOyT8_TX?%QTnI3d=g#> znQ!aAIN2J8!uS$bvQ@;Zxy&lhe`YdWzEl_kFrT%qOc?<;C0epp%QrG@ME*(*Y0#of zkjZ8jyGumNZnt%(88Za*pM)B3x|A_~6Bn^h)qSl5F|~(KwIL(i!Kg%i1@cA@(+p+f ztkWCP>^V*o<f54Q&y!%N&&_o;GCzBaC%gaH2Q8vWHq&023SBDj#yk8v4{_wDBJ}DQ zM!waj6!zHIWK3Ka7>!1ug-NA)tQ(UwZ|qa74&8hN^j?)?E0{HdA(2wW|7*T(|E~^E z1H^Coci57y_7kwT(i(OKU=25N>s*hE=IF)xO49}fN_$=bT(0Zbh#5OwNA@c-I6$LL zQ2nHtmfF(5bo1R9Khym=F?A5Mg-`;Ja!hsD&!`k5MI-jN!M};5-;Ai?n);5v0dB^{ z*UZ(_3U%jG!G1FfAKCiZqP6^ikPLrh!6aN44PK?CeC21lU{*TL^Uc<zwwwvxNe{Ug z-k>?IcyO^-hwm7;MMq#qToA|FEx~Grts0?NBn?Q1_dFX9v~o!bcy;f~<GzvXems4@ zl>+genk3n+c$n((`{xyLPU>c49j=7BXdz@U(5S=MkMDTjzsjvm?c66)?d&3VMgZ6} zv<|l^fL+gL`8@8;W398`Uu$WPieFJrT%Thq=2PMtm5ekH-j_`x1e5jCB`O&RL!qny zhPn!He^52$WJt?~i58CT1e=d#C)C}APYY28W-DzOPf|C~DCj&LsD{xcR*d?EYcRDK zR`RxkxB&}I&jgmdLXjz+)j`k|eq9on;2Efa==&EYMQG^oGa3iAIx8fZ<B&nDCrYR{ z?#jm+?GJjtFiu}_ulB?EE#!i<q`~&AOi#6RR#;Z*cGt=6<JL8HYmvubggo!M7!&xn zq}5i@3TaTk#{zeHR2CLqg%e7=zb1%+H~zyU=y&^VZ|3bd&&!{ePVUmv%B5@G0!^aD zNUQn1eP*{(WWPl>qTB|)F8rm6Q^4Xa|4{*~O8Htgfz=om#)7^{xe&ZiMg5iOP6EDN ziT0;T+0l-?Z{X4Q<44o_>`O^Sgx-@3u<QIhT%Zua6CY7dyqznHcNib9=At%bB*xIm zXIZk5z4XI{y@D1{Hh(c84D$_634A04<36xu{{zPq++ys$+o?IM*92Fu?6JuSS*TD} z1LNCeON}B~mFTxxi>|8k&975@|8SY}gwH_Hj{oMtwG96(ylMLN%>@tB{V!}!RJyk& z<9hT4CrTN|qBRh)M0Tm_$hVx@rWvyn>e}~{n<@8zsd?icC$Y;Kb+BNXf!@)sqoV!m z-8JwX3Ofn^!}ewKsCK9ElVIDf>5pBuvudo_n>!DS9JB7ML9AtFukPOm)Xp8XQ74xS zX@$<-*o<4uUVxi$KCJjCK%0#}o5%(cxZolQvk$lV&~OsLOR@6NIQ`}**`47}@bSZ^ z{MF@vC5VecbWIr2D^vU7%wEip{N>PQ*cC=^G+kJg-EfY6!#1QX_Y%>7>W+tuae7aW zE1*)KGabi88^7Ylw=I~jTCg|kd4{4oB$XnXvvyi$*N_&8kOVA{%NFIcWo1QHg|*y~ zu2u);KcjgTRyMp45r;1xYvta(xS->t%g%0q8S~h<d|5Q#zl4B^i807g`o&)GR*NCp ze*9ey?_c>WFPW}PQOy;mZ1-#Lz2>86@BH}8U~HO(ziU#hJ)1l?G@jdwSX#G5AUB50 zTNxt9r)oj4bzYxP0P?jex-H~-he9cg-jwI6_VwQH-^M|CA4Kl%6a90<61?JD=-mpj z;hTgL>TsTmU(P>Y)n&5IZd1GYaZ7?T4=pox75ZeWM?f$kpx$k8=`lx!Z3lb`WoG^- z+8%Daut#)<0G&2MP9ZM0xtD-(>D?c!cj-)*DxFidzpqFcRIB9}cL~>3GVi`%v&n=J z`AbWlPfo@hKR6Jv-makX8#>ec)(Ae7rjGo9kllciU&pH1#h^GGFrEG{f@aL{mk{X& z+Zru|%*ebDa&*X`xz4+bPC0gmp!%?2k#S*WnF>2Z27_-;e3q?z;3_<zO)LWB7vZ-d zyIV-5N|qACgpEFcJ5USro|+YHTGBvyVeorPoSm{AUY7%vDM>JH4F(iT!tYDd6?0ck z4RZlAVNmotCFa|2F#3O|Ks3O@goX7!v$siN0{fYo7S@QF<=W!C-B8o!CgKCw|6~7~ z@+WU5A=FmmcAgdf(5bmpw>-N3WFc?0{Z^?65`Lpj?~QX^7+WhtJBU!+r#b3v5NoF- zq&|Z$N{CKi_<yfG<*03y+d&X<9ZPLAf`A6`;qG#3FWYe+ZX)d}`u6A~Zx`HYezmd5 z!u_{uSxPMwNh#@nDCZLI;y$F7HDoI@#<PijH!OABH#}3CEJTj@zDx2ci<0!%f{cOl zg7FRBggD!2@~`E(_&0(e-$PxtjCgTiA%<lTRB0+-Ic-px8$zt^S8yaT;dzvSDRpsZ zb&xR$iIA7<@nW&(Z0)=3QBUHJ9WF9#OJwM?2f}~`a`2I>_31j+oeK$2tp`$X&;^#A z2fwe&5M=zM3@wpTT6SXaTNRvLe*-g8VoCoZl<!1FKl?eY4FY(tdrFgTTrL=R*jAgM z{OefNxCTxARf;#ax$+$Q<@QkU@A?F3tdE?MSX~;M!`Y}4;NOr*{}-y5+_Na8b6T2C z^NyG`9I0cn@LIOu_{{X+njS5?&z#NxW7GmVd$Mq+Zu_f^atLg{ARtjeVwh0@3RyoE zjfGwnjL6MAuYw;EANY2}xiUZy<}AfdS1~0e9U(Hv+J1wGI$Lm3RPa!DmufY+6Sarh z0^WMVv{ne0HAnA6NO#{}oI?;(P**7{ybcSWHh}8aaoWyjFq*xUJTBcL^KGN)yXh7M z=#Gp9?rfn(tQJ1cy86TLR>N5rIJMdv_>?T~GA))Vl6Sq~>`}p$o@q1Oe}8B*It$^r z54d>ASJ~t*670<m8~!s4On2b?j6bEJw=jRcQ!D_jvN>9brWONgnXUhW!l=54UcY`g zJ+!mfb>;dMi0%*%M|-d>G0E}-f}07NmZ#igQ(UO3hl6&fJL8MGWs%k>Kh_rxMM?<1 zH?}&@f6(*xefp^uE7vdHC$0@Av%O(e%X$iqkxh?vhtOR6AhthlR}0RWV4m-voitl= zvpPkGfQ=%iV*K?sW#L6c94D>Z2l7}^FwqP4Z=%;ScqArsWVx-zsj*aeY`^Xjvd|Sr z)VD2Pn)Y*m*R0XOeyJwu-eKczfgU9kU)Q|#E`<cBz0XVd9}E9-q=94ft$4nO@|l9U zM2BluBLEFNy@7LDHBRoi=BNGY(PvhMG>M;E>Wg*1r~~F2_$-SQ<jporh0{p75x^B) ze{RW!#UJg7-gFW+Me^@G48Tw?m;a*4{~BajxIYPo9H2LGf9!SngD4wXd`&$=RlmWH zHnpO3<g(>pF7L68<B1DyVJlqDZF0gL#Au4;cLH~5?v}{I^4{@qf|383<ZFaK!J$9> z`F|n+96%G5;EMfNZ=dP~CyP;!<-dUZIXLn~17rc@pHjk@X2N<uZs@zjr-OS{UnVJ1 z0vP%A@!zMt?e{4{{aqhUq9)Xv^T8?&1_Q-?P*B0B!m#R&kV3M1S8?2!{Qn%E3@xH- zHz63L&vGVWK+5R~PbP9Jk*u>#aoT_e)8X4iu%F}0ZFov^sMgi+>H@U+XLyToL9$Dz z?oU1xB%+Aj|J4GI8>{6Oyb1d~v+U1VsiBBn$Qp@mJiIrV4Bz-yFGS?~1_F2;vN1tf z{6UW*8W6Sd!|v@Pefw93Bit6wLWb2YE(M~f$3P7zk=pJ6TBB=2RCqLlIrUMfM3a;j z{2C5Qy{|R<TCEYGmn}<OOM*vuP#@07Aqr+n!R}g{q_(x5#tj-*b~G*B;bIqF?}JJb zLT4Ahe_7*f9+cZdcF{S!nCO7h_F>dwtAmpZAB<~F@K)ZC49aLdG8^~+Zoqja{bkd& z6CLsrPzR?0cBB9J0<;X$)@Xvcx_0zIZnhksJ&-zM!KrXQ)B{-@!>)0-kdA#xOd1gB z8wR1I>uawpMQ78=-6ibC$3NgnD3#P1gf{B(30y-<L0&i$t27Mn=0uPKA7Q<PK-<qz zd>Jh!y{w+(-;Y6?88f1pv(CT!@&O*o_rS32s3hd!^-k?`kLL2vU_N=PX=h+PxP3Jg z^>_OUJo1QYSEfX1mk5}b;7w{a7Wj|n#*8XAK@-%69GnaO>$#o(&*$!T=Oy$$+2POR zez#RuujbTC&D}rRToVp@*;t0iznyS1eMXUjHc*2rP?!@-reu~vhf__Km2{nq?G<*W z?ak96d><FUIL+`cc--7ozfv>YUu1DnqyqT4rS#j2IrY<2wOp%0HR&1Li^%)BV%LVT z5fl?}f}(WqEPKrPlXp<FnJs498N5csB9U=-xt52%U1Kp{)Df}`e(T=?e}R>#oWcC{ zI<O6nwlVz+KuDi_`LAm|7T<%e$Vpgm$#3<Fu!2c@2G6M9x)Fyc=c5kt?WET>-$$+X zxiMJ(Tp*r0(*=lpb!PiNc0n-ABntuc*9L|oxESLNRDHJjo^xXw#qF(AIc2ZyWx4k< zQz*^K(*{taHIG_qz2;(VadQE8Al7Hm+D^%mhlho8ZF7EN7w&kB0L}k>W&ooa?UZ@m z6~K$As#q<mx~&0m9l0NFO#KgF>%k)4nHu$pDSJkDU#-O4^T$4Yy*fDF+tKY|O`#Z< zi=irB=?>m1uuLdV_-~mI0m&nD`94U`b5Re-U%3#PSxm#3#mUB*E-ydRmRAIwxJ=p2 zoDf_`_d4qeiD>RRmCcB51+uwNUH3OnwMh`tA*AFuJ1-$?j<>W3sQ$OS=Oz=fdQl`! zuMJneoj{&d(kz86FZ6(q7PI=`Da@&zz6hJ$b?Tw)^rODE{Mmv4Iz1~2r-+fqTj}5F z`(_1GhJ>0lIaT(P_uc2r4i}K)bLRc9DX*<=@vMABDP<*9G<B3tRiX-|mhaVk*lCOT z)B7uodc$A%jEJWsIb+dT-5kf0L=8upGg9roEG9-wLqKr#e{|8gbha$7NNW)6&$~50 z1krInJoyG^%FE5vVe95T>yNhSsq8k0jMglIjd3Q+^~%qYdr5q3Zo4Jzk9-0$byCf> z2BpZe=D>gUc{X2-Pr19wzScbJ2I+X6;5unKYToiXzqYoNR!HFYe1Q7$3kL{+rMXp- zHw+1kwnzH_9TOg_6iY1jA;j+&S;%LB8lB&_9j}!S4ghy6CFivhZs#C}y`$6dVg-uX z;=@DGECw2M00y;X+-`qeFphxhW}!CQpWkZ-8O_Cw4N}shUsc#q8$(ot(FvnWXIv!e zfiokPi&w872DheLN%E?<f82=q=R^4!T8Z~$*Gbk&P7i?uRldqT#EWs^RG}LFrtwBp zrSBuxSEFC6N%B*SIIy7)|5Q3<B2M4S(nk3KJECA;T!oe|^cNpWySx!o!53H<Tk@&o zPFqFaBzXl6{jMGc2zM0x{^oSnOd`9f<9o{4@^6eal?9k?&3$zVfIsYZ10w|PD5k-T zkDo-Dze&0=qN##^hX2len0g+g@*WTu`x}E2-mi-W>KzC@HZJ6SPiR7e_@&-rZ+KQ; z1+=;o(LSG@7d|7cR;$(#tL4!N<eiluOx6FtZzkOoUuO9KQTCQ`aW~5zCoTbkI{|__ z3>G9<f-`t<cLoja7Tg^MPtf2xxVsbFJwWi_4m+H4&)(<m-u>+EJKhcd>gwvMUwy0l z>3qa*&Zo8rv<5c#@aMaEpyuZ=a`^CZ&0LohpR}c>Tf+$Zdq}pA$!<a=CYf->J2m!X zt19!rl$kJo8Pg$f%44V9B+QF$G&mvsg`Y88FFt`1W%LsahtPZeGzQ8tO;85+!~l8X zPEn5E;`M5mejcF`u=M&|cHTn&Y~U@lae%Q@I)O3ES^YH6yh?As)I?!W#ytdk43L*k zB2#tLMAyQJlS_XWw{!F!ZAXTpL7E=^`UiT(j{W<?;006Q;f~I=N?Zxx-AQn~F%Jpr zz8N^=10r1BB%aWINuLz<<iHP&;ma_Pkknn-UENR<va$=l6h2m@R-m$D7H{GEcyZAd z+nX*oVO{03G}xUfClza>=CFH~lXuhF_*Q*&;M*XrH#;R}tJ4QrJBMz@b3f_Ui}*+i zQZ9{YY77D8tAGC*ec-?89Lb5Af1Hq^gV~5kLJ%_~fb-u+9U2mcHR_XH&i4?$2D5CM zXuDJnfeBOzIReBI$$pVf&xj8JXhZwrXknZ37h$!=$!nCdFC8MCISna>tF)d8YV^bK z0k-F4)eIkM|LM0w4A!l<g`$S7L))Vcvs-y#f3}tA%me_o>*xDT1*Mx0E^KVouGw$3 zU8tgelK>iIBl6ya_2KpBj<SSzaUKI|)z@<XI9wn(jj_&1BsLdbyP31b^F9QzN++W` zwel6TgI$qHqwd|T^_KyGX<Sb&;blWO;PxBvWzc(L=E7q?&~{AKba`4l<hrYUh{sre zJjs1t8)hn5lB%a$1^WW$KOPSI)((m58=?~?hr+UI^z_y!B4oFZxCla;@AA>$5+hhl zd&O;*W2faxZk5ij&6dJmc_iWUA~kC&4oXTBEo$f#+ZdY+wIZ6{kB|GRRQsl#!bwK8 zkF2I`SQ$~EHt!MrH!3`ZJ#8E+Ws-TcZRpc~p+@0O&!yLc+%izz^IgiO(PB-@y4bK1 zQ?#N0CvadmaQwql!Qii0a`^(N19&GAl)(r=)o;yiZlugvS8r|h!skV1w!51(+w<4N zEpczbfb!h3Y!~G=SN!ywQvt`W=g$McHvKs`dmOnaLh4Bx6YcZBj?`91y|bx1oRr?E z)WIqaG}zA8px>ss{#C{Wwo|R(YYu7#=q(}HpWFJqNeqAwpXOx_+ZU~U&r!@`xxuz3 zqPr<HKqQ12=m;OmpyPF1@Ogf$PJSZpp~Y+5viS_m_$W0BiCK^nE9rM`B$SMpwXDl8 zRim52Ly3}67mq3>ppX!!2vuR^dIr}k514zH6tU=@3Pg^UvdDOok&EKv64JFBGd>Nb zm7a_cd6@NlI$AFn*ZNBQBbr37q4345bW+)xg7+xyt3=>8C}ELRWB4w+n<W$D0;CLG zb$0-c!+!^}Q*AU+S>wX^*YAw)MkQAN0=)nCclt+!A&#`T9zn?%Q(%U+AUmXa$?oKi z4R_w=`6Lwgo;&+BPD4KRu}Td`VbaOLoy*q3*JKKrLKk<N;Gc_o*jvZvw8^qMGIELk zK#zLs-q|6au76dur^~^4)LXZtuYb-=x-nSd!OT{(JI%{vlk&lQ_!A^GRS`QuOYEcn zfoPVyvax4p^|pvn?RN?5dd#%e%gINFd6AXFjsu8X_3nVNeopm%T(9Ob{I{w5t;*{v z`H{x6P5sYwJ|W@A9glk=DF+N=E4)C;h;ivxx9|S#{eP~sB*p@Y%qTYwF@gDe7;tJ_ zCJ+xxy{t|?op74m9AQVX<I!UNnc8Q7wU?wisl>KAW-84K%Y~U=kc^3@(A&hy)5-<1 zstYx#eXZPQHSy7O0WOq5ehKzSXGi0wBWPTcz)k>^?=xR3;`}}H_j~xzcAz-XWN5lT zT?h%(q)(UcuM>tO`LlcpO~nK9@_8Ti5@vKji!A&bi|FQDqpl_trBc&;?diKOqB`&p z>YP>*7UQM#gVv~vX(y6LuRj(Bc14N>g;sW?bQ@+goARFBN_JJ?IS}Y|E3e_XR~}%| z#JkeQ8yOXcc|0*^4Ryo4Mll#<je#h`>cUI1cN|8_oQ1PwL?r?X1wWWRO}iJ;efF3d zK%E*2zpCvBE+eex2OKy4UTmx>T#%6IT!)_m;n1;#qoM@HGiHI+b*LpBu!W7J5T&h( zE0_6=$*)^tgqo?6CFW>p(}>2ehB?o0K36?r{M2}3M9K}reM!F~w=i^b`_l1p<F`Oh zFrglljvSkP>-rL3*>I}!xN|*(HizME?@;PXv9jvfYmk1`yHrub<D~MpZ<-|EC8c6; z2x+{8z}hB7-C}!G7hTBcY^3t=_v)<U?f3AUA8QcS)&(}GIXby3{ACo0sNVA4blSQ9 zzel!U<E3X@0uy0d+1V&g2z)uV+_=!M)-g5=HSF{_E*_b2M$i5Kb0bH@MS;dVO<!mN zz~mflQbfj=!&hB_@lHa<+VpHCjDfV)4<z)3Z8gdR$c|F6FTY3f2K>H>2AQ5mX#G^` zWf3XVq@vt}4G*l9&Y&d|kI#?oe0lKW>p`1P1@85jevBHRO494FJ^ePTGvNCu@m$q( zvlnyskk>X*nWk5-lA+Q7VarMhxI?-wguQ1&{>kWzTWvKK19C9duv=X6*qa$@`7U{l zapT(8e@=AOmrGO!t1;4K3-)IgqK3K%mKfAUpf&-|jiljBWY5EBkc6Y7$=!|0Nh{F3 zL19*3LgH&A9z{ucJ(3%gX1iXEpkFe<;r*J3=?U;x`(8K1U3J%+0-1gKvXvFDUm$F# z;-Qkfu<>W?&|EB9IS;lv+z1r`j6m0)96Z?STkw@VV07gC-G@G#Wnlz;t++8QBNOei zO2y(d9Rb!04Y#_=+oSJuf#&0SO*<p@*JX?6S*>$+i?!Dof6#~|aa#L!Rop$Wsf}n@ z0G)CK?3y*WWl!l*`Ydz>nEjJA<|E_?aizS+{h;$kb-OPeBkgOD%hD{lxnpp{K?W#t zJ~pSpwRco4u#n}L|9aZ>^Iq#5G^>8szrJiP4zXCjRh=eJ<z`^epl(4gJT`K36Uc^0 zD(sWE9$7?BOZI&H7+G8knZeFM2~?e~VJk~hZR;b|vLDrKJJac%XsrE(l&MZ|m9rdz zDn3b!nwZTY4vqp1VLt1h54-e{@)%On0H&o)HNn9!hWeizIh92<tEYHD`amx*cGBxA z==~@O)FUaOYW<R9bPLTJ7+d}mK7wtB!0PvJII@d~zyh9+Qtn75s-dw^AspxE-0RN* zsxBSIW}9QU{zx*5b_%0GkF-8(DT7QH&th!WZMY)KmDSo*sXljwI|rw54442`ZU0~q z>Kz>i?G>{IWcMmL`%U&^L!M6RyK#fTG<s>B%<lCbfHsL@6wdNU2CmT2{++LkpmYXf zx3FZZ&;{%QnmFRO{bT4~<MoS`wYS1%B^GsK#uOZq7HvHG<Fr_j$8X&_Fbxibp8D3N z^-lz!`h=&m4*>-psWP!{-jy=DgToV;&xg1^mCYT?l_Ypc1LM_dT(av^H8bU`a;tMC zU;20pN=$#s2U;6TEq?U=^F@cu(BTc7b^~~BO>H3@1X%oTbG+gRaU3sCzF5A0XXll# zF}|VY4%_9(H6sjxwfeNg-hhGoivg1Vy7}e$>wY8qv3u+^Z}Gf1so=Cw(TZWcE`#U) z-(^q$dextLbqb52G?-7HxD~OVUS4OC=MfmXmkfXrSEblHD}AH&fBDROBOk1(IH7d1 z#Ea7PK$T6%bCenjMR4Iy!d6zVkQLy>{~pKdBOcZ>@(Cp_s@^svE1xZ5DuFK8slx2| zQQt)5-G{wMALLjg8)84pPrn7qvs$QlNaksV@>%E$G3rhO?wjRvTF($$&Br|&8Xi_x zv+TWF75j=FG{Tzi)!dhwvJW(1HNI4Qwz0r22=*@VL(~<w;j|)3OX4U_$!$fbQR`(3 z1|I>@I}01i%-E1<cBY@~J!;m+VsF~^tJmgc_xcC!ACKeL78*Ay$2@ht3{uy*Pr@v( zAMcNcO)_rM7Z*qvAll|u5SFqc^+a>PM{C8bv93LL2-RqF+2EW2PZY1O#&$Ir5Gmt7 zE(tO0)k05R%1+JpnbphEcFyLr>MyxkzJC9G$BlLo{U>XmjdH?hkRB;%oH(ej?upG- zH*C_}1tyUpMvXx~<>xRMnlC}4l)XBvkuHdalZ6EzP+1&V8v*xPWWtl_@^F3Y3kM}y z_d34>m%Mca{fzjfmi=QNx{1yL{<(=#+F)S<>y~fGJa6*wU2@*J`|i}I&e^+p^n8;$ zvGkL9a)lDG?-x%J?vMTSFtx_<ht!ffBr95h!}rI&dYGv=pOR$JWT>w_r6mttH1UXw zRawY2>VzWJ2<(XMYjvb$WTsXeS*@3!>W##<)lbV-&$C?n6X(XCbiPZnBpUwCmjaH! zBEe?XsV`1{@<M2st(7e>OQ~2XbcT97U~{?|MqCeHC$+!eJtN5C{rr1>GP$JO)T+-0 zCC1iMxxNLasLQ>dk_bco-qPx+gcWCnQ(ul+;%n8&8GkcsGV|`mw<4-%CHuKz*~a+G zlP##w>KK&`NGcj-HIV_y)AB4=O2jJJhJ0$%+K`1*aPBu1^UMMRYaosj<%u^Lf`X6x zC*%7Q(7P?a!qw~cs7owVNw0;kAi=nA(nd?tX`#$PE5xQQW9^m#S`+Q-Y)nJ-04;3b zHZ{FD0W?MKEo2*Kdh?&e3CSG6jI;2{QRC-<VQNST3j1skiGIonP4~;;V-^dk5_=d( zYV2<7hqqTI!5(;<G?$-S3_QhC`zxn(-70NwcIcreaMdeHCv4)<R3tyE>>G_NyRmlW zr?9+(X&v<5;nWN3J$<Fh)h3sV;rVe!1A2`PtG$%eR2q&m9Frb?S(dKeQhL@qWn(=p zl;S6nxlb|WXl4!()DS6~zk}sjP6}Bokp~T>4+qNzHf!a5b>Ss&gFixo444a_{)7xb z9JJpWM}t#B-eAv_j4WN@#p8$A!j_8lv3%=e=b%f_WRSz6ROm62QHAN@?N3*=0#!Sk zNz#-m%d@k_h~jMB%QZ(Z_0A)Kqfx6TB})LBBq5N(!IDW0>o66JA?+Tpw6?D#t0sW8 zsV&_znISnOegSB6pw{fDbBO<)=i0Yn4P|NT>UaY4N<4JhqnDm5#b%+=M5LLLMN-c2 z0hKMHg=LhY{cIn+UH9I2X@fT?H>k_>0#oEUN@jeX_t;m=hiq;aXkHi*b`P;tC{9)- z@bS`?Op`57H;*L|HbHzlNU8h-T^c8~3Ry`?9q5(+a_c?-66~<gYWl7|Lic+DRfp;Y zmx}l8ds8v8sL@=zBCf4ixi5H>DtT}5)YE(19PgbpBZq<gbv1KX({@iM8&#Mo8ukFJ z*o~U~xj@n2v)c_zq2ZlMAL%yhl)g_035dDUBoZhG^8)+XmQ>ndK-0xf=-%RQLC(H; zNmeO3pEOOxK{V<Pj5Y!U4-A_-hr6@x9t;EoSqFe}H8zqjz1AcK*Bp65Yv58>I*nAW zM<Q63R_$T&(zF?5XYDx}4t2I_EHiODIXct|rXyHIT8`XQR);v1N}9y_#RAF#7BQH5 z4;&MGDWVBBI2bP#t1}x|$dQ~x)qx|GrmPB?br(@~Uwz%)%&<_UD>?7gz1q{;)OD?| zYCYW|y!+ly@T+z{jT%=LNHczCWAtcrrXBoV-K`MKY}buJ2mVFTwQySaJa`(3A#lAZ zS@sOh9K2;NEyc8>DO@MF<}N485DFup524l-=B${HdTS8@uY1JHNW$8&yXN6nqA5Gv z92%U(NmnsMgjI(DwsH%)<Jh}M3S>5V>zJ(`FQStoY)49>L(a=R{1yI!2CCKTh5+S) zQ^|ed{M6MyOBtvVwGcAgRlm#R4aFniT&rwWnc!8MkMp}+ShqjUSv5Fl@9S%X%E7S{ zMP=@c3Zc}Al;b;0GCd3yeYH?h#v5Z_O6}_Ke)#%vLuK+Unwq6}<7wpQAoPTCrZ^&u z7;dgX=NZ_z{$DK#qqBypXw|rBt0cid9bpfZ6TQxM{EGUJ-s;`MZb1QEANzR`Xi;Xb zcv=3qvVl~-=27-^o9d;=OeTCYR-B{wbXnSGU2kIOdfH{OV1mI6(1X`5@u__pY)RG$ zL!b#DWZ5ssHMH)vvHwmmJTt@`sG?!1ca;W<csi=n;8IyQ`2`^=-+}h;CmiZMs_^J< z$nsAsBt^DG?P#4uYCrU^k^^s&YQT6|DO!aphlPuIp%a?>0tzEc+@f5fBQEm8-LjfE zT>4N|11jA8x2z#^u|N(@O;KbmPQ)@Frk_i>vZ4?96-N3wI+uKGQn*Qo&8q9R;PWv6 zkRYlU#$$A9=`YDL^zaJbQ(wi4U+z}@byN6L+ToZe4~^ewxuAu?F_v7v9J(OD_MX|^ zC{CWVXttzh;20`1AyAuqG}b0IhTUaA^GB&`%{d*{f&or^$~RRlOFImF?@AR{HDt_W z?3val%?MkSKUrfsWtvKaikq6M*^jol5X&sI2R()>+dHh2{V@7NP!(N0JB~NfeAW#< z+MT4wYS?tK8R@hO`703!n%@GsHaw9H>nU)l_tSz0M-8IEiQ>d8g^)-xH{ghD51jU5 zJaiKueb$;MdDVF8BElG76Ms4(Tn=iEMvE#QXAzzo%5g5jsz+Fz-_<}QO+5zxMPih` zY*AxQ%I~7}1frrTwV^CY^gb(8S(%z54?2UMMj5!%bT>ju`pSS~JL{7!5#){hBTk-L zXo1dikNq?>`<BMCg5>3Vn;eEZEuMoeI^0M<O@hOsS`*iv$(aB72-puZtwRJ9>RSmE zXyP#-w~CpzI$r5OwCd$Vl~jQ0VuN>e%8(v2Bhh=QZlI|DsIdqPhZ9e~KLSZ=@p!1{ z$R`zTBGn|qmR`94efM9QT0Avh%Z_QI1=bc?U#Jmr&qF8PT~9`Ml-Ap<H2J;!puqCk z{1K%z_i{n0(M*U{D}fy_U)iw$mD>Dn?)RGSd8H*dqDzDPrJ*XI7RrCCfPiBoNu{mG z{+PB<uh=I%i|xWfv!7?K)^y-$!pURCQ_rh-6t17D95U^nN(q~R-Q=ra|1nPDFi;3H zY%SM@tU8=D)QD97yAgFb3(f3d!eT%=0}-Gmu6RrzNdZM~!8dO{^{^<vOZwCDc$Rc1 z(Sy`WV>oQN@g*FTS6<V@j{&03+auRppUV@TjwFU#8u0Ym<?@LsA|`Rn4)H&pxS`41 zLqZukXYQy7{v9ayORDQ_!2EAm9lq)jgPLA!>m^D6Ten2~F&qw3^uSl17`>m#52Yc9 zeja5vdc>#gQ*NHI)j91S?8g!yU{KX*3%u0ikYUiAbxnYuMc<PJkkf-YgJWb8CKsJx z+A(jqUwesjVeQ8!W~dviK<}OAp6yD6`dnQ<D6&S9h5v@K@qs!?TSA=kBW3QM0VWe+ zooQFK=khh#0gt(uH{du@kKPC3!cKxdV%jO;0E1V{h2>wYDLv@#pS!3H^ACyjzm%3> zTWF4MHCB|YkT@snExIs(&yogfdm`3!39T0{RA<}M-M>3I|A4K~!h^9)lE%@0mL(zB znW(6i2BFK5VAKb<_<S3fSM&TD$v$$8>~mC5xjSuKsxm6Xu;`#|D_m}#fnHt4;uXk< zKeK3ES+=79CM+lL7&21_7<u^+uwyyFADO8e4<*y31X)Te-0yt@E_Lb(yUX-c6$g8N z8kyErkK>nH4iha_&2akxyCL~?*Zn8m4>q*$s_Vz*W8dkY8%tg-D}TRE$c`beJa1); zIZAe{Sa`;sMbeEMgyyYOF#wlqBkhG?oTau!sCR@Eq{Al-cO4PAjTLYl98Ur|yq=sE z({R3)BU0-9SEg)FhQw8g`I?tuRK=Q(b87k4882n$VNch~%8%e}*KH{z*{9F_wsL7u zA7S#*IZSPlU#s#FF@Lg5fM4nf0mL!zvvAn?v!D*LjlKPcoC*7&6f$-Wl>k23{L53T z20%sZm1m=!Ny|$~rD>{fyZi;xhnMuTT|Z3u>DA7RD_`45D_;hscFe`d@M6?r1&mTH z^vWsY@OluN#B2Qs(?Rem?5+E=usLhB$he>(t1Hcf?&*~oVAfT#S46|{HbK({&bT^g z;RsHh#gHC9h9N`G6gzFCvW`AopM(){>C>S55Sx+Q7x8!4xGEetunM?HaoQMCOdE!X zc=WhL?Qy*zG%G>Q9YqM4%yD7}U&^@B1{R?^mOu7D^rRZ?_T!R=#>#5bxl<rH=tKMA z;GHI2z324F>N-Uq34n)GY|V=xiky*nI=xNkF8oZIZf{!F{;S~Bt6cb9O&nNlqxn@b zu^<d^+~EaWY(0FSNCcGswHb{NTt@lUpy)S&Lc#nihwn?ptDeT02w?Ss_kTC7prij( z{9N!dSp$Rsfg+tI&0l8Gt>@)o<_cUloDFZv(EE&Wip1VV;&6)>hbJ7>OGXA8-jzDX zn<j)CjC?AL&kd{5ri-pB27F+abwFBI*aN4a!NHIgs1Gr?=s-oHdL|cZ>!;>&7e?J6 zdaxgUn=5ufh;#2!ZrMs0R%9QK+<P4@!bQ<29BOwl8&V2Zk#F?Iky)d31=sB15chaF z>3s~+&j^JKq3=u&ltSuD^xl`1X9pYUls#z#u0hUc23g7MqUQ={>c-VOoe@{~<~oJn zYBgeQH@G?9=6E$fZf~yLSj1TISoTZFeK=WiBf_+H7CS(4CJ5I0esF-t*(}Emh){TZ zjkuIiaN)xI3WulqFDne80^yYSUEFU~KfY+8!0gh~(BuABPw{Tyvnlkf#-H|e<LKop z_gw!XsJ9`3uj2%jbCy1|U>fdXLZih1dJc=ZMQ3QEW-T;YFfIH$T5#@@p@v@h|CTSy zk*P@)DkDTFG6#+j$aa?U>AMffRY`(yC=2`ejq}VaN^Z7(MNT=f*?)2G)5~JB{^BkZ z_)U(Kpae}6L2{~_TimXT7DLp0lFxL*Sd#w3Cz&Q*{h@Hn_ojGqJ>7CEy$NbD3<WW4 zBhm$O>GE*wfj^ND`Q&mlU37~{N_Lc#qMM+-HbeXhr9X#UH!$N#CoV3HG=IMpkXb~7 zd2jq#O)qTxw-}-bQc9B_GFk-I=Da(%@3!?11n%zG60=V~0uJCL-)EJM^zO|{3|h)& z#WNtb{f-!{3$oNo`oc|>`em6&j>S64_ydo2zZ?mp_|}i^l>H<^#0FHlr_$-(J%RT1 z+`nyp1Pk-q9ww)@Lb+$9Jtyuv<<0CE<xh`sbrwi#_u}g!JjV_aiQ|Op%3l?ZTOHjk zjim|TJFdggOM)pI`v?`SQBut>N(Kq&<BPkNhn=54Esv1k>Aq;L6M6!Pg?;Xz;~CY& zaO8_iSz+}?^%OGOOYYDl&xRAn>|g4m!>Jc#IVbB`7Zx=xWf~duMIXZft5^N;7ZX*X zChutEWN!vbqga)#^iBK)2pYzl7xWVZtV(qX$F;_Vs!qgtF5cj-PIJ~Gjjgo3NOwL- z{uG}=s*U1IHIk8(4rinTL*T=Tsqh?tpdyk07QL0KmDALXs$9)an*HLFdHPt>bQ9Pn zepH2h6^!5qH~Z^9A|HRfJ8ZPK2K?RKeX_S&d)eKEk1BnUSA@4W_`pNqKk`FaIy^ii zS#&SrD?kt%7Z%ldp+K`Zy<pMb44>2lp5`yb81nP+Q2YGHU&mV^!9<@`rNktzLOGT4 zXHDM*=u*2LWHtH!m0`W=gDb6zGMKg3_|ezN8))ubbDrU%b7Qy%@IHU0y6;{TxP}bD zl10fkdEAhg+TOoCYMitA<meVO@}3U;t$I&^q4}B0-Y>q~PBR(TAV1jXIWsx+DzlG6 zZkJ1@DYgk~*w*;N)oEsMVRTB}8s2San>(C)=^Y28D{#~n732c8cbzhF5A6+&J%5`j zZ(gdjS{y*^?RR^jUfa?8y6AyWISx+&xyQ%|oTkJ~IH80;R&qmK)b*=YEqZR(xV~I~ zoDg@$f!D5lW#kaja#9f!yAyr<u!i%o5<~*mOx=zvhh!K%#6>9EzbV6NB;-#QQCvmp z3Vm4K+itl0))}naZYSKHK$OLMHLGwMy<8>WO07&U;!1Ki)RjlCOLy;{(H)NplPLQ? zizk1tiKvd-h)M?;zkZEy7O_NKW0@Ifhkv#;T1c_p$IbF&m=#y}&ha4kYMRh5+CX=j zz^}YQVou_(ad3kZtp8O1GK+djpP5;)Ze&&y-p07urtM~2A&t&a8Md)ef{z4h6qp7W z93)|&sP%I}M6<?dbikT3WVB@6@z$Oc-;gbcWX&1VT5$qgZ?(M6{pYLHh><-|D*|)n zrxHuEh^wxy^u2-jk)mosQ=iquS<ctit9S}3LUl)@An(2Qo<{7yvyK-P@CLTap{d`3 zts5q8y>Vk;yqpedzBTL_`Zsi0_dH)qpUR!Gbh7BCTUpyaR}R!!J<bUX%lEZ`BO?`a z)i_m{8`@5u?Jx`|gq;s+*c*<-5J7Ins5FzX2~e?K2X0NFB&-KofBq>;DPK>%>z86| z2#y4e%#OD;iiPTL6K*15;$JvGE7Lly|7l!4E;#Lzfh{LEr+-^Fz$}FWM#B=oI`?Jk zr_$j&j{7#nzK>c34n}0?9kZMhrHMD8$SpTS4Olm5X;};dDOI>YmqsCuuR&ahGf?#1 zv?590og$#Zk=v8KI_PGY_PsjWiLZC#qxaMJb&v3~PVGqnGlVU1eEF|H<(+Go*HT{{ z5#}hPUcuOGG+M@PboTAO!2CO#Q4&RCV^5e5!OhP%FJDWr^>P2!R9%+73oP<|7T*ja zO}}E$DYlhOuGf?gsmvu}l_GpEX<e+WWs3^S8e%&O#mIIJekWI^`51YQi;$mQR8}NH zy9%Fkq~vNyjr4}Vyyo!IIGK2uKP@h+ak*0LdW8~HU?0d0{j@VL<()81-E2iFLH=;a zYN-|39(Jpb%qJ(@U#BcYGEr{22cb?{JM%x{jUwWTm-%C##$%9^Lo|)9qiDcSdHm^d z{U;^g6$`e^0D!INX;!-_3V9Wp+s!1Jo##)B{fID-Q)V8mDLtsOXm|yFtV-G<rb{#i zL@`XkWj5(KKighp-<Rb)GHKF@^iibhHv;tr*gJ+8&BVelUdVFym>sk2yh34bGaz!W z*z?7ul#aQuuZmSTyX4A@$8q?}#Jx+KYlG0D{jdCqzX3coR%PN5vV8$1P~Jh0s&#%~ zC8r)dTC~R=v<)%-m6Us=*wTGvvtrCC$jEDl`Mv*a_4m`=VfGQnS_G&%RpS@<`rG~S z^FrOjul*6-59blfG%-3vVLD&1iHH1iQGSpz-N^~FG*i?yGc=2)Y(N{Jh(hK|QJHPc zT5AgzXFeRPXx3?}Ry$hO-|D0iWJ}ycwI_914_gK9{M@UgVjXTPj5^?JuB$}9TKC8z zwZ(pgp5&KBMqWTpcJ$4i%2=%NbJ%=_5WRhVLIA64_$j~n`?(*f6$?nX??z_o)hiFJ zdw-JzsrvY7b1N_xj<M<$9IHhO@Q#s2HW5G%U1p*8Fc4<OH)pIdtB;-!&kCo$D%E%X za)*$>m-0#NLl|Le5roMcKGxwkc9RFgvqJ-|!I4%wYtK^|LjB&&ryBsfW)-xaOzZz3 z6kD%?#r7m}ZVpQz)fY?KtR{|AnyjY3cM$m&YCGj+q;4V}uQUPMfuSwe{_-E!Jn|zf zs>LqTN0U7kG!wdit+0LFtyq2WCOx!OqsE;T(^%Xei1@9qdYVjBnCcYdM^vsXCyIEO zwT1G|-iaSm>j@oh38FpUyRW-hl;>(l%ndLR;l43+Mfy}t9|uN9VTdb7#QFzcLq9k1 z%APEQfC;wipsl7dy6Xh%SxfS{ggCmzCnI&+H=8U-KoX5jAUkZ~s~iwPgEb64lgUls zee+o}Qdu2+VQc6i)T<>kv|v#^ELd~ojpf<j=(i1JU;<17Z%P<i_2{RwY_?d_*k_w# zS8sZWOmvBK0@>l~vaX8Ds8_HE0s6D+ZO=_dK-8)|p3Z-RCBhf|MjRLSF#l>sKS=Mg zB$`bG<UP^*&NW_zB}J60mLi6M0B-Nx477KX<ppMaY>DQS4BN4O>m($Tt9QwMh8-Oj zWw=JbNrZ((tbxG5{WpB@yZ3%^{nH%Ua<BK2;dWi4*58k^f=>M)d4p=d@h|?n<c69@ zyP=+1+jU?sEa%MY%;q~jDf9;zLju5KAf2heFSLCp_OAp(`S}C-`i{=upv_$WPh&z# z+jSZrPQv!Nxo`ZVH<wKH^kGzGO<KdW+A*Qk%q9*l?%QrE+mx@sO?`wy6~;MyVfWv+ z8$)u|tsK9nklu0Z=ts{PTeCxO;SlS{)&+PhCF;RJ{!I|8A<b<CC|bSLXwu=1aOl}u zhNw>;t>6W~QTuOFgW~vES{8>({r|3Bc^?6^T#p5(p0hk42ILVCOuqZx`xIGg{mcgj zPRhz=F|7P8C##)$KWM{kJNR_Wplx<Zqjv}C4A4U_SIc48<c-juC6R()nre=0%I`J! z3EKN-of`7@Vdycxg9<wD2>{1`_zga0x?N{SzXDqSaUVvg`|x(+b!(x6xhuNU^x=S7 zy$(z1>^Tt@+rv%B(9L$J&Fio9wKJtZ0)~=cwe{k`h=Qf^>@m&>pqJu$qXJ)l;D}^{ zS`}&=FcA6$Sjno#gS*H1QIF4P>d{5YuPQa`6P-;8lXnE=5O5(fo&j1m-F2<Y={Hrl z*S-Pb1GkypQzk%Lc~A(;0w3|UYfU$HQ7u+NNtCfFv}`*puXx>a=FO;S9G)TZaDW{S znvL!U9AjYKcoyGq*V!@`&e|x;P;hzI0L6rR`_iE{`11b$p~{6a7u8Pl9}_4PfnL!< zIlMKeVw!^C!i^Az!5i$8L_~=rS{T4pdT#5#=_L~;S~yio4ruczEefRf)FbVhluDnL z0Fyf(r$40B+qEN?L3?B-32X(?M<%yM(ZI-Ck3h%Y4h4wmlMQc7BFy*axeEI(MbP)u zCxjam)(pr`X3aOGh;l-?=cv>;rVwb-P&1$)%dja>&iC(4f<^`NS{a-w;&h`dN|-5> zW9gge(~^MwH<##b6UT(2e>0XXsMCJ&@X-8|TH(k<KG)*h9ZKfV(>{zXE(A|lYrwo( zqV5wSOKNOqV{Lw=@tb(_0UF_Dlvjt~{~r#)+-lCg{OCAQOlSgdoCUHhcIsqnPIpmY z;QP;iet54Y)Z%SRHapYQgL1UzojD7@8aDJ}1KBcEiyu+1(4)1M5sCua3K{;*Z4QBD z&0u+|p|-+hDvS=Iol`E_o3%E0fEZcVdrO@@EB&^rXP%m6!a$Ymr`aQqWB$gMXgO-& zYOdM&#p(?*k}*%~{Uw+66}(oS-}HB^BB!lPM4lSc3DJ$orNPgO2WnpizIjlm&Yb?0 zJ^^3p+~kHgzqw%yHV^q1^!MKe)V~m+r>$mI!9fUs)|`d^qvq^k`3@Up=X$ne`{G`+ zUU_o#-TY}W=Oa}^>_v&C5epm(6*GKIgsqj3pyl8E0-{|g)bks-uam>XhgLhtCBe?; z#itCnNaGh!MtHf<>9l{J)f8y)tld{fyaV4~$5T_?^V@G#EADF7Ceu!)0X_fj(CmV{ zGe=Il6o4dik8(5g&2$cpvXBZYYyJxuGX)li8@@s4Pflfw#F^Gt>iFE2a%#w2WeOZo zc&p~{Bg_oHRzSm_Sp!}-JdjdekY^}C)Z7ROjyL02*s9Bb;!lc76RV{4><ru~I3*?4 zhzk}{Rp7Xs&boH%Jo`bPxzC#=l^0w^NUaRzZ2!Z=nd5_*!2N2UL}{Q6KbC(q{Ps-X zYyFW<T^2$P1N#gNW*c!8^7@5v@Vg|(_QE4TBU|dt<yU$y<gd?>3e9O?5-`KQ`dU81 zH=nRb9_A!hkZZNJi@+Pq9hn?B*?$nBnk$^Qyvl#C2U0iO|1jCk8e_QVGudHzEh}1d z)pY-~rJv3C-aI@v-1J>RA4&^taA>{!r7Jw^DvxFyruqB2FZrSSvQQJlB;veVx|$?9 zW#)@Jt$aXdBMuj>u{J`%97*-U!?Zw;WyzGP^^We!_wPm-*ao(%BkY{Q&APRZ@l);= zne~bumwVUg)N56P2R*Z`oHtLPjN&IyN4LJd(f}fT=Q>QZFdbkT`3JfjkTVcmo*bVD zTh2KIEMS>N_9x>;_ijmRayA*;3r$iLL`zfacd>xC)LxJzf1pXNm?_-a_M!7Tr(H`b z)|hlobLEuGn@Io`!it;!S1)|*puE5~eM~vaDDDZw!zuwxh-X2NiIPB1*G_r@C3<;O z3x0;tmgxOW<M|vdg9#@S6+@CS9g|*tP_ulYT57h%N3+6c+*i;VOSMWlsB#9bSh(LT zQ;uw3aJ^kXO+odyjpLvWgi#^s5^IbIq<dWq2O=l!5p?KcB>a!X_;@*QyS#k0st7kw zcMUQQ15K1?-<^`Hx_Y}wKPYK>&xItTds1lJnga4fFqTacJd9A-{d|JHreIP^0FGoQ zn(&~^XmAF_!KT;lw*z|6i+6+#_O$-e&%f<ulce>h)S!w8|I3N1Eh@?Jt1u9MCne=n z+BWHjoF-^XWxE3Iykkg#=*6H!>?pk2_o2P;&V;RGo!nNkm2jl!D}s8Z9K>m|ApeWi z#ZBQVvf@3u_1I61$r*=b4%lcFxpZi>d?kFRr{FT1HVW8;KN1hMN<+PSAEUw=<5ks% z+7NFf1ku8j0Oe(W&!GM8S~RCRM?cC5SW*X3>@N;*C2DdbTL5Lw9lo!Zk}hAWml(LI zaymcD@ik9_SujWC;zXe=z$ObS<+J<*N;M}81r$L>Us;~we=@**9-<kpKI>kiMXAQB zg5C_hRy#{zMe8+AWg^I-yQe}^T^S)%gl&GHZ0AjQQgQs65vW6|OQ-r81>giz5n<#m z;DE~dM2zZrlL`9G|3==CWMo1tVKW?z5+6H0d<I5x5w>2Rt0db+|Cme~WKnwF46svl z%MO#Zu=bfrGw<z{1}OlS8$Qlq8TlZv($$a_ku%0;4YjqJO2}FCU$#n$I?_x@aasgq zZ$Jf5kf8hSRqPc%a-bX^9t_3jS^x6uvm{UaV2wTYnc>fbmwR3<1aEU%9Jd=pwA$Xt z(TOhQLHn2gX3D#E$@%8!CUgyNJm6oEa(><a4Jn@`Pb4bU+bv%>*RBNZZ8NKpC}3?* z6C4%(0ZJ%k{opzJZby6NT#@Xhf4rjpB(l<UQ^mVQ18Gdh#$}|%B1<d)qx{R{i9$t{ z3$G?G{HMt!_?n&8H{T};w~*b)eFv)xCCe7=6>b5NQf70fA2M8hV>`h|sR@D0WGhP> zoM}^1jfw;dO)M~)>4b*=2^^PG1)I>oVaY8m%X#U@{NaR+NchHH9&po1UkEu^TC$a4 zy(rFBt1utOh`sS~>6B`@rZB=6J^<74Q5Jag){zlni>|i3^P)G=)|#A3>ninUkcs(y z9#1w@qfOHIRr<+aCTG^;>iDH3BldWG_8|;{@$CYy-g?Z_hX7!k9A$la^61%tN$6dT zA4(z7D-c&F75EP4=7j`6A1<H?wEplBpNj@6XJLB6oXlda@&KYkqeLyCEi2-Gw5%$w zEqkT6G}}JV`MA23V()FdE$IiaF8#0v>QEPXXSJ^L?guHO0riw3Sy?AEU-Eo)$HKy+ zaExe;Yw&PkwQ!UgxdkalK;m?AZaABXzPg+x=mw~OJKL2^_KMCLSsTAl*lOpay-~Ml z@5_Z7>@@_j5HN3nV}IiDK;`P=JK>-#@eg!rp;w*k9gn9G80+<358E9=e?$~Y$+t?H zt#^{Uh_oSPPFA~LJY1DR-FA(dsYw(y+hF%)IDNNcL8LCdlkP*c4)6l4e1kFXUiP05 z56RiVUp?Af8P&MX7P%n=Ifp;o*gy8a9kxC>xcn4b?37}!aP<DF!t6J1tLbVCY0k+j z?ykwc;r&)Adc7(4Qy3+nytFx&+K6NE5q@`C;m2KF_(#%)KLm8NG{4}9*t_f9KYW~O zL2q~dGJ#_stc;P6J%5eNbGa~E!v<1P+DwD(5!Tfwi#3N9!P18DpTnu(KUG`yI)BcB z?a*%8l5-5^#8OvSi88|Xn9O6U{tiF(oJUJq(bU34w@LXO17~{cEC@`1CO|$))bur4 z7;RNf563UU9VYN1r+j(wIy%j-t%H<l((6Rb8JLmc;_GSmq!D`MV~?u;vcITJMoTqx z3r#lqHrEp3!}WKzlXdiuxt@jv1@pfQb@DR8nk<!)x_Q=*<P*z%fCjtT!mp|un75`t zYk|F}E+zc!!@XRd<q{FWTa7EhpR8h3q#kF??>n3rfi#8X=>ZqT8M`t4CRv4uAy?>9 zbTB~uw2>+r>ik|j+|ec2-lFd2objNX|BE8m?Dp$uukv~KUdqd4!ik4;pnViu8*Tf@ zUxT1_o2kCyWh&!zOfRq;bIVqhR(&o%Hw^igj<M>NMKiGZI-AXEA69a;ZK6LqYP!%& zPm}qky-c}BSli?8A5^Q4*gwJpr8#{Q0k&J2+EWQw@odkIUUM619rl`7G=-44upOh! z>zimnV;ig8y$mRKt!2^3_!rrmvydmtsozkk&z<3P$<6)ta%X;Zy1?7ThR-BR4u{`l ziL3}h|JTqTt_Saf(3Ifb*y1gb6g@cf|NB=TkWZ4O*e|GU7yL-&?k)PTRF3vR`a8IO zD)Ff~Ix{v<LHENNWA!&0{NQt#++fs;IbJ)#Ew_c%pH`9^VBU)@0T0$@V0yjfQ#^%l zem}0Py{K_Q)Ve8<^p{ByB{A<8t$JUJ=U<ES+)U{2rd=~WIzdO0T#D6J@NPd#LjO7& zCk}$rp#x3Kyd6tkOSD9mB?$ol<f}Albs!oE{<OMH*7MQ#crJCjmlGxLYVy~`EE?7g zp(TD#TL?CsdrK*d|1Y0C!#CJYc#f*X(fUT$^gCfZYA{;xMr6r%B`5u=GjnKamaAGL zUk6Cqrh_8v`o-336<#lg!P_3HyjGB&Ch3YLq<6c+jDV2k^&?O<2<PoXeGFzJqptRc zwLCj3xk~!ug8_ApE{>|Drn>s{AKd<yP0SznrkZ)yhbWUKrO<58M}S0{Hyv#Kv|HW@ z2u=^1?;q{V{;bkk12dw{KG%ss==O0AVBLN1?)8pC#_s}2iuyvB_z!*~-h42F7-TNx z5O{g0jWsqqY&wO36ubFsN1s4hDFlY>l|lEMR8i#Z(ve_Z3ZeQJ)~E-P6ILDi545a# zzyfvoS}6?Z%>K(JMZ_sVjAmxg);SCkND%#@aj63-E018$T;=7+;o;Yf>LH!%go2ob ze)Uyb`I=U%IjJQ1mddjqSsDxn?co1vQISnuU24=+GFeI{JU6BBjM@?*K?WXH=!M!s z*}@8`F}L(WI`XH**&bRw-y&YTO>RS(t7vGFrf3|7y!^?H8Xu}N>sNK0Dw>KeJY33s zQf9nyT!;bX-7u9~(Mt;l<zuG@J2w;!SYDyV*Kjh-F)DGUkDk4C=~?mS#EUt+#M1;) zeL?Zz<vbk=tt}&A9)7K*+i5t{OBn2fX=p~e_}>UVAU=0OldR9~$IeuciMJ0&BgbwK zlIP|A;Dxm`0gD{AbWe~q^%;vzmYdeob>($>{nax3nS5dNKI^OaH>3hYFcnn}n#M?e zq@^HO#njfr)1#C_F@A52JpcSz1U;+%s68s)tokZoMA)4(B5YJ{AV6@(YDZu9gNVt7 z)N;F^w4kR<wN0$ZhP^ErT!8?^rm$9Arzfm*Kt&bfFdB8A6}R@3T6{P+B4<cAF+5CH z|NQcekm~JP(?axEy5KR(Nnv_HbIc5<W3`P^7Lr=QF%Jfs<b-B$98u3X*0`}2Hhfaw z&;|@q8sBpha<rgul8EbY?iCSh5O+P-ceRA<=rdTj2wK!1VgDlXk4)<QQb0v`XbZ_u zDeq%n$#K9b`}1^L)^?hgVeE9sPr<Q|tov51apEZ}BazJGv&zp+!YtaXl#Jp08m|(K z_#1jtO=ZA-&%Y)Ezf05664#rpRhg{&-w>?-s*}1SIXDbiI_VVF))o$fU2dy`{O&r) zEwy;Qk9muqL(K7Y3rAQu$~pdxmMF--cOa+$Qn&y<LZv||&`Zw}B^^gsejTDR{jWKj zZ<>Qk?;rnQij3<t?X0&ICRa`o09da@27G=CSPk4@!?;wcO)#kyV|?3;P4SL?%F;B) zf^SrM?qx~b{|qbK?Njsp+Z9|`@g&~Pzvu3VRT;6taKt@0M1kzw518*Vs!cx!YOJ&A zHQBzAv^*|f<_mL)1eO14(2YJKO?Sz;S`493c#P`ON3p0b3xgfrGW`k@1<ZsZ@c3ex zLoPpHk4CHfi@C#0ce`~au7yHF<PEM<ADZb*ma4|*|16dUN#P{=A;~{IE)}k_cV50f z(lB9NbrFXAZA0&D&zjUIefKcgpkcMj?REK=7@7g_P$0`%;bSL;{<+hbf|rpQNA&k~ zISnwO7SX<gR{3&=4Fexky@p(&8s!iB5}u>4t3G-;r^1L%4ggo!I~6`_)3&MXjVd}- zTvA<yNyecBf5-o2W>1X8^cXAVc&x$O>-*jx#fkFtV)OjcG<$yXJz1T1bj`m=G^5f| zRj84FOhVylkG6H>k-1Y&jfnW2vJnXQd5+UwlU{nZq80Y4d8rJQTjE~@kyg@Bx0bL! zUSCzd1q}>KU>~7TX8qTZZ675Et3)HUdFK#5p*MNP+9%4@X`_8^&Ogs8yGG_u^C5Nl z--6^^p4}5wdPFHr5&*VeErzvII0U&^4Cl(Ao~lOa^uDl9**MRlqWSsR4yw7%YIMaP zsTqrL5>MCM4DuI7`WvN-!=|Tzp_6Rra%_@tqN0%nMra)i_l6nR8axcWz$LtcwAm?+ zMSfMnW6JV06QU3midCk%ihI)zw?g*y`Un4OmK0gk5J_BO1ZrD4J`@dyUqP>PeQ!+^ ztkb6Wao*}M459I{v|tL!T)!73uttu$j=lShyIa;&BevOOwTuM;NRNUExUJS(t<p5O z55RL<(G7*H%Wl#>$>u}447k*`dAhjNO`1tG7oj|^s&LrusPRX1(fy^gV(d`8k-+Xh za<zD+*^h}{Wa!(=>g&+SpILc<;_&I8Vn@<Pq|;ND-d52^9l}!&Y2&znG<Doanv4OM z$*-KyTs&D6$UIveZ|eP*ibF^arc)Y*PT$zLTZ|?CfQC%9kuae6O9bN4@Sv{y+1*L! zWOZ#v<8#0XY7|JTy;zTHf@i-^jnfG;U#5^&n5cZ_wSsg_E!E(O1vp>_ypGPH_aKE3 zi|$29{k1SSVIaCbduiz)C8($T-nUPFzzF^4v_D@i7VkHO1+bUSpPaP<M%tG)A*~Vw zIcjn=U73iTBB_l3Ih9YWiVC^$s{6eT=*{-GLOi~4!y8HTjS;EGiwGT{A)r`0cy^B| zUbo~M1uRUCcni@)U0M75?f!nkzu!bSH|R@ce}o)4pTULo2%&e98-;g&>kDx?Wfm&b z)&9`Y%7cRSZfv-@{*VxxJqEVVj%cUUZ1&}5S!jTzx2SrH7feF)nsq1ch2gu=_9dM5 z)|EoVDtDLerv8PTYpWwgFUM;k>1=9(QkBHJzFk=7aRcaV;Ew+E^Gw;E2cN=HHgKyM zHl9nt+J_Yq(>Buem>6ki?>4>dBiPPG7`yS)q2MpC6SNiiJEaL-j`Nc&5YScjPSl%8 z;mMHad+<Lqkvmh`4<`$;s*Lv5!Z4))U)!^<)JUqMosbt{B!pc@^V;Q$_6)HU$3)%B zCL&kqC-mBhYn}ElE{B+;RVw#k%QilXMnd5CCoubCWDXz_(fuQ0FeO?gb}hdAg+d*k zb8<q3qxas_OsU2SsuH;zG!z}=09GBMSx#kwh{#`_l?we(Mm)-WCo}wYMmWJ@yMLJ> z!JCb|Gj%LbD#q`5cAVTqxcry*2EXR0ak+tLsATFHitD{l1PDZDJBv>C{o-_OT3ElG zc(Xb(or56;2m|*1pKU{l&akr?`pC*#lGIBJ@8m%o?jd^Srqf@TP#C5A=yIx|URJGz z;-?Hsft6t;O>_8>d}1cex|S8%bkgM!RNjAVwtGJGDPn)J8Zh#*_?Q~YLSO1^QLFEq z;qF24_c<XW=cu%Qv-)R{0Pu@lzHio>odzQ7b?u|AU?*bV<m_dOm2P`i3Whno)}JxC zCQHAc>x#@q3c#)_Hc`{Y!nTfnOTAyHgmm6BH>GMAlX;%!E2&Wt^b~$}(y#$dg0?75 zk6p#GydmNeeRZ1E#;)U!EcN$RmlHfQ{o}=yP?>8g(PurUfdJRHF0IZ1FP<6~hOTAQ zOfvyn#W(`h{1|3S)!_iS6qYrr@)mV(i$tdJHy=W`3%&0>amz;+Pm8st+1g>FA|Gqx za<JMV;dF_Zh`6j8SBcPr<n{M7cO7x8g5wkXP&JtpDo>avQvKx*HtV6Fx-my718=DZ z$1vqAY#8rNoU77-x`PKobvR=e%{Es*>$!`bJ_8EDGh6{o?qn+Moe72WGwbN-bU?Xz z(JISqWb(alw9vqkZq`c9+<ISMju%~jookQWiK5Qq*xu;;_YjNNY|Rea^`O}K)-{WJ z`PduiBihdO<7M!(HzZk3Ux1~at(Tce2U>)4LoGYi!nVlXVFPZcAD#H^j5bVX35b>O zj@^f8UN&IQjVASe%t}lm)&y-3)L?8#G=5W6r5}4aPY@LN$tZM9h;_AOS>BS*mSq^= zKvx}zP9Z-$KiXd9Nf1*)d_4)humDp|PE$kFKN22>g_D>p1<6uZSSvsNc!p>!ADTh+ za1tAWqF|xn4u=AZ&J~OLd*6EpJxO23o*p6Pa>iLTjeWqO=uENNl~o}D62=tgi@W`N zKWk{^>iIj6#iq2ZAT0|ctuP&nnJ4s8Nmi^XT+kr2$MXa#hHGVJkd_#1hH0aodOl?e zIFH-utwYtP-YrRrOZU*8ii{tWHID|TVgSyL)YuqL4jXUIJvqECIbz#dxr=BMC2st+ zn&QI!uRG(nDSYcU3-qV$JnBAVgWc_>ayhORT*!;Jsz=z*)lV&=c?~NAp(tlH%!S+4 za>Jk7=SzvhohgnvIf)G#_)adeHyl1V5kdjI`VvlR@5fwe`7+{P=aCuylPBr?T=cKH zj+AfFaZYnyR(%4!sAO`#`&bB(vMx1+l9%k4nSpeyPf$c5%d2|nnf~K8x89H`zyl^Z z?A__41yy=h_3uN4p1rlZgDQhr>rfXXrtf_PpVXb`%CV=7E9(yy8I!qAY%flrgQ)Mj zxiPM-(!}?Z4+D{@_n-%bqXd>kx*Bd!q@FDtT}xrNDQrj+v_>uU+xlFi5&mw-6j%!B zSN#|sN^$=inob3w`tn7Zb{&-W6tcf)Gz{UVV>TYeyuED?-&8-mNaH!9R0mo||JqHF znPAMxepp6AbZdQ_>-8`=S@T2qkkyLZ1nJ~A?{ShyH>K-$hRCRL64lUrX9KzZ1KLJk z2<5a<|B5D0%F$-AMQ<RSEm(g!R+(CyqV;_34g7?)96qSb32;1I39*7jydEyiPFAVD zQi~Q;w*ge$1W7<AE?vzRmilH`W=5sy0^LRH-~6JM(Ot0hn&>j2U|qeHUB_e+nPtC9 zNii!i|LNk5Vs_<Xl#6sc>Fz}avwk)*H>M~*!le4cWi3w2eYy)zSiNgkhLi?~fP;%w z_zmk{v+4*Abn{_I2+l1s=1dq=X|FchpK1ggsU`TEDjP5TPD2YXL80KU8Ri)DXM_zi zpSQWQn>k|FKjLcJ5<$)mH6fek1Pn*qArbNA;xD#S*ZnD-ozwp=Q=X+mKwURmJ6@dT zGi~Cdjd6>$mmwXEFk&$M<#+lZB@tuF2BmJF7_Mv!cMD$>e-o$Kz5<o^R8bz-OsZ|e zMz|Np9-6cEhk9<k!>YzI$J2Dn<ZlZV2@{89a_zp=*PaW84(RD6Dnx6)ogllN48znI zH)y*mwe@m47Ww8O{wMM53Gsv1d1V;6KcTYO?Lm+*F0uv7i|2e2;^BSNU7f^Ucyf-< zc1eqXaqwH$YNbFJ_tUTKGhT2^muk4GY6bIy?IjLHvWizZ?SD&O2aKt>W2v-BgR<&2 zpmlWO1t{?LC@Iguc!>)X4fc7++`Rzltmp7M{<+*4k|*q0<8s&jfhpe8pBU*2y8#G6 zd(TX6>HhPb@HMq?+~(z9y@wb>iR0#SugUi&E<-_0)($JI*@2jQF~zr|oy-}|DwuNz zTE$@WLiG=t6yulUT5N{ZhE@4{B`^yDb7W3KOdnS}CT=D8B#}#c2<YuR|H+ATsqT*6 zGDqNoQ<;-s$KRmzju9#Ol9~QA*wQ|`su);~{y*4z$LL7AZ+kRG$F^<T?x17awylnB zn;qM>Z6}?MZ6~+-J-?&>J!9Mt_shLwoG(>1c0E<8k!SC<*Pd(6xjJs<blu}ctqa+y zqSrzDXt?X`_%@3;O=PCr<tAuV@?FO1s(hWy`hLhg_;nvG5-Fwn07!H{I=ThypPysH zh7%lYa3758dYz7Ck!*+`2ev~J1wN?#RyQ~Fa6WgQT<JbySEP5A%bgF{%QprK7_How zZHVZW3Xgf~;?Of23?^Xib5)pU&G>hGZcDP?$_(u9!dCtWdRI=6l$cmS*CwJyP^^T3 zJ38E;WCOB-h57k8g2R-)^1E+U<@_vpyV-3}IuSdCQlPeQ?K}~+S3+mxHsbfW_gKT| zgos-V?fT#5<xR#WCP3|xmgV==!DNbfL|5iep!9AuoMr-yIGGWY&TB=jZq_eYQf;%I z$GCY9*Wq&CW^5DBy1CB*;F_K?MU8fHl;W3r*J4$bl!~=T==N_=N_uDQWi$E9<yi`e z2kYhMpTTAM+)fWKmEx{1*lT_sza;KruKZr6@K?ocMA+g*-~V#Kk|><8QjCL=(hLJN zc>JH0XW?8R-iOt^qorS2x}}VvGyW(Mg?G60o!SA=MYvMgT(u4Crhy0|mqXNS268(= z68idWj-@jSh>vUhh`aK6=9!wzugqaQJ1@^(ohk|56$``d-Sm%P25c^)gT>%80o^)q zD^|p0o7Fl3l4$p?t-7b#mbX~_U8F9^2coog|4*=+%#mQ)747~By%c?7Xb~`GwcjSy zLFwC~8JOgzg7fb|3FFguy>t7k`QR{@&Ax8-0Ak%S*Ivbto^9^Si+JZ}57)t+?UnC* z`4wdBQLMDz-jo%=@ngm7#hyKGQZG$^mhN0v$vKmj)e$`!R(S+KMM!g0u<oJp)0F<r z(hT}sl4gUKujvM0O;W5S%fR^;MDdv$z(^PvZY<}Jof%G#rLw^C^C(*3(<t<e(Yx<U zfOALkwfjuHn$eS38GJb-r)Q)m)VaYfCUw%+suY)$3>AkSIgkQ1-fa7f(mlmwSUalm zx=L&s5Kw2j{vi7pVL0WmCBoNj^^ARKJo*K`B|d${{5!GVfWWNlW-fAzoP#0l+Waz& zyh?o0b8pIzcj+n?!Fk{@{D3hTrVwlu6Ss7x)C7>?nIio{(mYLkQR7#qiYZ1ze0B{n zAZA;P)8sq5g$PE=L^x)1-5ZD7`Q4X#oiYrduqn0vADI}0Xu~GO;1Tc`*$0*$Sv?rl zqq6@VSz|n4Q+nUtY)Z~Rc-Ap`$Oq#<B_D@~wWzzHGSqyiDpJic91OLKT2r-EI&D@h zwzWui)T(eQH~DPWeG}kc?GldU$S{ubZhrfdmG8tIo3-P#S<t7%PXJ&$-4E%JIBMM< zxV{_CXn7B6)a`8~<5WcgOL=}(uhznFbh}*Cdvt{tLb0D~*4=1n(-0An!-N(7sJ|C( zs?wPt3t*2%?6%x*X_Z&B?u+Im03V6`>P*%mpVZ<pUa#lBqPygD^j)4^qw|z0+p!!3 z1dR>=We!81Cv`(}pt#u9Mxz{Hs&LF+Vb-^2nbnzM<uhiR_@;PYx6D6axg2oH#bBrY z{1oc-L#q{kXK@yDmQ<?JB!T`NfM6isdGw=^8LbC(pn^C6&~?GpH1^wVP0(zSOMpJR zqg80nF2lL~y;?SvEvDPQu6iQV86-e#;ii5VFTi-x?y|nYI*=S4W{ij#5vVp8u!UF0 z#83u1>myhAl7Pifk}I|2q8l0)kK-#Yf*n%9D4?5y_0~?AqeKRBnj|IIyaeJo(%UTF zfSj|)vo%;3H=EEjEA%9<m#CUYcWp`+z6}Kft7{*JI;`#2RW03BTZ&UzePHkjc6FFI z^%4Q<4@?nCkK@bJe5Iz-!E6mQ0xY}9+Y1p!K`GdD0F-#rl=lCO4ax5hx4P;x=Ks<! z5`fS}o)k-(Q*=Dy<+NYZc<=TuW;-EW?252M+&nvlPn66Oi~pX}WYhRQg&|xS96zvV zsevgbQw5OeLBv>$cKq@U1r(m{1rS_&Uc$RA=pdGSeZOy|hOm3vJoDLz^QLe->Sa^# z?|2aV>K!xR6x$~1U9!I<Un<!HyJHC=fLFi(=><9Xq7^%dDR6@!ks5rDDFo59E~X{$ z|I?%|Eryzt+`S2hj7u~ZC5mF=Ukpy>lRrVKaxHr$uyXQoh&L>d##O#&HE`N>i!KW+ z{dOey{B6H3mE$yQ?#dCsL8FdT(Z>wx`^EVx#|EO$wdsQ>INZ2s!#!8Y2Jc^^d89Jo zOKCRQ!J>Mx>NMKZFpG^$(I&=3P6#Ak0ub9^aqw=J@*c|OIa8Kpi1t0A=u^z~&N5#B zP)L}}gV^-?V;ULT0M@ngV()Q77ufHf;;%q53>KIcmWzxD6KLcL)f!Prr=*i$2Ea;X z{|57L0kFN>Z_bCpmKRy|=DsQ{zB{e*S<w|McoQ5=R)=lmU9FW}ov%9Ut@1X8b{s5g z$zm;yKu!oUV!huJwV^xXG|S=}m&53~nk}ASPLBY@R_KN;h!1(NZjD808nNa)nW4}$ zfQiWRm_SQ)Yuq22ZwTYO*^0=BGM&iHOuH_th34NYA0}?e0?PVfKwVRSu#pWvJ?l_( z%ONU>0Liivc)7+5gEvSa44xhKu+9>AuWuugAeg#Z;I=xQaUQ(JS&G=js!vmJow{;x zc}ZGDPTTP+le$738~mAf!9dUrK@C7c;|0q-h7O*#k+v4sNIsY}SUe|Pr<r1WPt4+y zE{q&_!`!RlPHCtHZN|u~phBJv%Nc+VW0hZ&s=C(~^q^g6Rwd=<JKE&t#>D}kBUN?Q zrxoVdUIZZ$mZ)TxJB9u>2p|fqce%AUAXFHlXkDu?E4$H}WGi1(?Yb~TP1W@R2JA)* zovp_QY9Annk=tQHM!+&Zi+nYNp)4ABXccZHnN6TMBZdu(jVM#27gbpJGU1Fyg{d_g zi7j##Bawg(u-Y|^#8W&!TDlDkx`3jAICZ1B1Vmkn7K6(niOV5GfWz?pR`Pa-A-!O8 zp4Tp~Vhy`lGRQcfEeo>1c9+b<hiO(I8B9_HWzH-m<3OZ_<%8kGWHbY70fsTO7qH-& zQpM`M)$?f;X@;6=#X*ac3kkYWqN>bHRsT9^StuB4qHX>zPbuTxwgk$efzzp5!Z~v< z0IL&0U_LYi(vFyGhf2XDCP7OGnUs%8k;xPsr$|R8G03Mk%rOaXB$BEO6_f;BZKn85 z=A7Ven%^)a#=ws}j1rA+^Gv74v2z+-mjg2xW^^%S%zHS6Z6~*kl31|D+=M87WN>|y zuyOY_n>TQ$LlK?X;W`8Cr6tw~*<b^N0uWdlAu=b1u#W2`mKZZ?u6-oue1mw?az}Rh zYda+Sud`aeCM8_}efr?LleIU8+1g>shd}4e+;-}sRgG%&-h<K)YWWuSOJ|n~&*7G^ zjB_v5F86XbIjb4JA#<HpKc>d8C;77>^qO{e@Zlt)bBK_NN*AQ4N@fK$*K|u@218Oq zXtUHlU6odAE@tUo1y;pEvBFhwrf{hV*{Tc^y=HH6P<?1g(0~jJ>$(k0<YFmo9aqvN zLVuS&0nA2a{K8c5_x^NejY8madxYL_qdxO!8$%*G)jOeck0wWko`&7A$9;$6)fAnb zifk%TrCIC{Kb*Un2a6f~Kbru~SvAyenF1!iwe-KMntT(0-45X`=w9&1p8e;kc{(CZ z^<m(*kE+W$Far%l2|ho2Bxy=*ne-tDJ2aGt@n>5#YxVchoBob+#KFhAiEX4g&lDtU zzR^|`RsV-H63?R>j00~RNgbxCG?jwuVrV&)rSw7yHKT@f0Q3GsWy{-pWRRczN=C2S zbKf?K`LB84<Y9CS%J&*q+utHc(yl*THXVy5pY`iW32XjV{?|m%#}oN&<7FYvMX=au zDud^d=gFATDgu)=0EKqnrkQ)q8MyA-=|Gqp{D>32`!bA{($knqF9FA|+YU28fmWDy zu8f<B?gA~uD_z%R$v>YZE0Qhev_8ajiGgfthda-Ko@E7X;2uZ@WSp!sh>T2{aqRR@ zA?>4>X{InPwn<x39%Yr9FAkQ+2ZI)5;pRA(WeuHXy#zuaWs5X|6Ww=zZbh%=hKv#w znx{@~k1rLDtB_DnHVZDp`^qIuNI^lG4gWRj_l-2aYB?{6e-7m*uC9?Zgf!Cq)q_HD zrf^bq{yM>oYJigJgPiyGN9sBftQpR#Q!?Wt!Ws4WkDr6mY35M-cQj%ieI@GhQt6*j z)#s!Em}CK{G&c$JMFvr%B&+XiLs9}PUExL5t1%W#f}&ka<T$&$!A_P(dz>L>mzR!j zk#_tWHn&N^XH2a@KL%zYe(_z`EKmc^wL2F>mms?mZk~r@^Vy<lW}LaAI3VRAWl&a3 zp^z2Bx|duR0CyJsmLuiK>%Q$$OX^g%xp+Ngs=@LAJY@TZ{TOyTk4q-h05)G5T^Fil z6AJ~PZG)Hk?RXN&vj<fq7GO}(vN^yR*B?B?Jw=9<S+W)*QrU7I$f#Z#9T2L#O+3bo zNK<?+pV!J@tn|?ffm3AIP~%kEBQi={R0{hu7~oIdFDax~E_#QP=Rg~QH@^3e&Xx(H z`(~##GsH|N3_PO_Zvb~%a@`g%nen2|lGoTS)@f|3^MW(xvKyF^fd}9Chgo#&Pr#?N ztkR(Cc!Iup0iV(rTOKY&^F+Kf8YqX97N<aTlbyM4K0AL&<G2mBAe5$aJ;gAqH?z82 z>D!6WLQ?AFH#McMbSAc^s_}3IZ!<47W-%!v$}%IW2v&@wB$?8)WcZUl28K&dBo{Q& z%DyfTrcxGrj}iat4jHjv>NCUXg8(f%&3dA0%5lG{muVvX(68-QTgL%QuFeVwSes8? ze_y$HS>apUk9XaFjnQMZ8!|n2|Cwz!M>m0BwzuJB$jNDIK*;1cAWa0?e<#K%I|H;l zboxC$0?6Sn=R^H3=YuQ)zXg^3cV?G0O0#?U)9q{z-y(_=!%RhmF7=WDWtcTpYZEuR zmu0{=jT7J4?kOA6dME}9<FC8}PZN(i=}uJePK#LaT`3We1BO=P1bwBX!OU{5)Bu0O zVoGQlE?Klypa^Te`ipVu%p4~nfH(!mwX;kkR>Eoc9=8KAlO^l<BsFo{MzJVBFE%Yj zd$7l~48WZzu)PZ!<w7yi>=*-hxwPQR^$-;DsIRdBlN+iPS)nV{qXR`5`^PJW8OOW- zW_DtWA~h0&6-APG)#8Hx*}pFGt(-tL7SIi<9zXR_1m$kr(saM+<G1HUjpgdiD`dNx zNBI{h7Ks-8v;BK#lit-Th3nb8ggJ_1MTRgZGw^QiL{dUj9I}!Oqm^3|OPFHn@%6nZ zIF+>w_Q}jMbv)VNqRT1bt_qTbs=?Q4u%7h=pYNQAhtbzt-{lc;Nmb=G{Zbx5iD!9u zhGic)J=>A$MdI$D+GC638d_R*h+MTA`d%?*qT<AXFSI{Kha|}mb1F^3(6}~LoIYKu z$!o0@E$u@8z)Q-Pw2^eRvzG(|zNexHH>OU_KUO>#OXDAY0E&3!z;iwTQ}|5A&Kdeu zB8{Q?J6dkIu?lkP{VnmMmYJzI#O1J_Ta<YQYpQv;F}oy~84zP|W1?gR)b&b3??!&; zB;l6Q-Hbb-iW{y&dg*VwdsvA_pMJY;t+Du^?(G)cGWqzdmYs3Q<#k#k@6_e$k2n)( zV*Nhiy`GtaWv`Ja%P9GS;mGt*-8?<QSB8!2k0Fvgmp9L^)sj;dP#6jC+ofioArr$! zZO?6x(5_AhxsUfAf>Vz@yo-xElTDI8x-a<==Pe?%BNy>S)G;nx1h#?<OkmO!=AHIo z2Z>>b#9{MR&@EWd%}LDBaJ5W8GU$DjbW!!uSdvLY#B_}YRNB;NjDzF#($NadVs*c4 zm)C{jDRMNVWoa>}mMniXo(9y(fpptxW;1rj=c8q7vAntc;8ydowZD@^@ru$2!$VcG zTaU(Duaka}<Vp?$ApUc3^2NsV1OI;WArW&U7FhgU>bSdqTWQbd-lz~u&ywc8N;VRT zHlH<L_}Fc)PYSWrEhtB$gW8!d@0SnbF|>E#K>$tcs-V{7t}ZRNFJ>^eXV*;tn{JE8 zPHE~HAD@`|q{cTOS*P6qk15O3(VM2aqe!NuMadA7)f$JhRwcKAHLwEl3m5&OL?21- z>;;CotY2K-WOG!k*sreu%9T=JIQ<g|0q*CUr?pi2$}xqT%;Nl5B-hi=g>l%-NKuD0 zuMc*q6}8Ze|2(%taHiA^@kle#ugeWZI!<^hIQJ|JTm=IT1{}hpNZr!aZm>7tVPu~r z;dt3MRxSZhE&DldR;sM*tk5+nYMq0r<D(oh8H8(+tL1{!2e_Tm?>|L<1IPd@mXC0x zSxY_~-vn|l{X7bVWgLV+X(pzkG}V%tMpc~fCh|o(npBobAyMIT-&DqONnC#)^(mzo z^6vF-+%4s^kB648ENxkB%(!=O%#Qu?w+(J;M`|Cc{2aWT&&H8|V1x&>Y+|#bx|JV4 z#SD9FaEfiXsr(K!ct+o+-&n<VP46jWWD%Yq6Looh?0Az_cw4~WNDnopTH#s6kM%~M zqsDRd`}N``uk&)7o`Y|Z<>JKmLD#)O<MCMS&3E>xi;NR|A7J8lDeyiZCxGz=lPx#X z`&ZE^^WQ~>_Tlhya?0LAE3HKW`xT-od6N8e3FacpeU$>f9bb&H=<asg8G(CFS2Or7 zcZog>-F!9Ln&Kx~TIFZWDV#!zUD4%Rr5h<y0}zYpP*zK{+H6^20H3A@I`ND6gv5-P zXp|RW#erWoXmCa0M9@JTPFOP$taUO#y+t?UOF@VvNYX^)fXciED~N;wofUV=fuJKv zbD47Y5ctu?K2X&Xbek$&QrY5Z!Ft=*hu{j27Q93rr{&?eiQYwT!A~P7JlSXqP0a?0 z6_7j4$82Abh0B8*wB2S^lk#RdrDB_Jhnai!lHRC!(s0hdXo?ka5R--|?*()_ZXe%G z)30PTYU!~SEOqJXiqIDDv3QIQ9^BAMHs3v88l&nhY?K(;Rmz%NxRy$(>)5W9-Kbg` zJKJ?X8-h_x>DS~Fm@X37Hf%i`SBf8QRYb}+*^@J|n&>x7Hyr<@3_Pw2eqJP($39## zIzP7CJtjpNIVi;y0011;;}5cx?X44U=OQ%C{F?#mk*@w0`bQ<IYnstt{?k>)Tk@w$ zF|LtRovJ>02Fz50<PFEga0%P0kkHF?w033|T0xU_BPET>8*QY0m*0{kPaWgNF(cIs zt<$d2eU#^VX@cV{3pq2?M+XPsV>`8U-RK{{Wcp67(X?{4NY3VY)<5JHE|YWc-2JAH z{hrddZ5H1ZSk$Jpx;-uquUs#AqTt|a?-Asd61NHd7Kg1@|9hcy=me{`T^&#F24#AP zEx~?_j3ka*y^(w~{8>H_w3ghjy?VzhQtRPs_XK|b%EQM!`v_v^Q9bu8^Rcx=vUG=? zy0cZ6$J)3`HE@>D63#nPA(gwe96%oOf5=&`K^xNO$e@f8Rvfgtf;%R~^AwW=3rt(J zAwor$^6Z6}Up84IY(gnUH6O4}1~>n1FB#PshnjAfE|#KsYJwi=|9<!^|6@ToAD~I- zsMUS@D~I<{D?PhB^Sm4As$e5ny-seyAH)BUnkmZ`_h$BdJQcHQF?xi;kGy#VBb)1< zZLj?h?r1q&bazD@zN~I-7+7+183ZAg0s7SW*}uc)Tnz!PrjAkKYIF~X6g;1YkMB5{ zkM7O;Zvd09ULtcc11rVQJz(CnCwxwBi_7)Kk+=6|+fOz(msg)eSvB@ka}{l1wH-7# zm>Tmi(u|7oj-6#IH-PnJ)vL@%dm6GkK|VfgGlO1F!?d*BXOl+k&h{E`GZoL=uriBM z^Il-QF@<A|<>pBSxlXmEyuHTMIgcLZyw!avPIKz!Cg?3|`z8_rea*FU&sTRTi!B_h z??}tEj}XWApCHCu+szZdNy&<J1T`k1(p!g!zjp(Z08PZY%RndPG#W@Z(KRDJ)fgBj z*{Gs@kAkZh77<paQG}z?M2(ME$*Cn1U3L*g8e6e(if}S16uz%pVLkDl+RCo(>bN7g zI=d}>>)4vDq`>!B%EgcSoa|kv+iu+-;>hgeKF^<=lWW3>HFDa3fBtp$>qVrLVc@4@ zABNV7?F;+|<FsFC19q7yl`hj|sS;G50}Lp|1SfMI<@(shm>!@m`;qr=b=d;-QPnzD z=+!p(i0~WBa10sV5JqrwK%EQ6)w{=jDh5F6bRp<CegJ04Y|52CYC_^*C%Atr>0+>4 zeaZ3!H?eF1c%AB%d+L^9zZ5&DjO?|&6k7p$YtJy~RAd9(|L92roa@5`KZ(_J0=K5Y zVf0ee59>9)J36DATPvW~M5@=%Do8PtVf85p!-^@ek|I9BFe?!P=!Xqi5Rm=RWhbkM zl>;ws=%4M903WVpfs3J*t7-g=g15%UD}bo<r%FSL&8OjA4xZPNJdo+{7X)zmEz2Ka zG@77UA$eabs}I&!te8xwVlR*}xCF0#5q_?js#gzgbH3P(cuRRxzj!pGI}4XOmKlJ$ zQ6a$Gn(>oAZ?C-?M$o%EfFjov6vMumv*3`OV4Z*lQCm|QDq;tTt<xG1G<-D36f4r| ztGOQbFAtkr6VyDHXna0nGay*4x+)Je7hakFSQ=>(@9l7FRCQU&n;N}W3)x9zYhHOK zqhUWEZWfzG5-qz=5TB7$vkD*$d;k#tcC21~+V)R7Uu8d3Nv@QqWHKU_(JUNCZIYPU zeJ9*1KCO-$wo0Nix=i141}ejV7+h}l*wVife|yy*8>Zm?VVT8La%n}f7!9-|-j-^e z(sSLM@+X3j7ogE&Oo%j)JQsoROjMi)O<C9I9;UP?5@hHsn$u@Mhm(^GkJ7)iW~o;g z{^eCavX#rxZPD?BPnGs8S~@oK^qP0E3+R<Z>-+XInZ(S8$#r@i0o_i!)^p?ZhZp-5 zcdOG?d>5o&?<2T7LElg0DIAh|ciRk-sKxKbkr6d(&rU+!0s(5(tWo+5<G+XhaZI-z zkmY&#OZMab7cdfGNThX8oCMtov{~wY$7}y5)TRTWa<kTHJK_h5fX3b9{Y*#f3#r%f zB*1#jqkZAIr0t5`_T>>E6EC~Fjnia+mGb+HE~k6lxi!AR!N0gEHQlPVS|@uw_0(CY zUST_P)M0sV*BMnw9YPZWLhmlXvO!P4%?5{Vri@$>s}!4&k}M=8;gkx?r;G>z>-QBp zB)|S*J9Aw?;iF-{Qrm9}xYo)}h1mHVk9)0wZ7aRF9^sDisNQ(1dB_EHavrgh(d0Fq zE{{#A)N8kW3%T%m!sT>?<+G@yYd4Oz`Y~EjO%@r%=9+$hAT7SyY5non4jTT;eytWE zH9D(JScfI0r=l9oJNntYt=*JXf<DgN0s!{C8y}R|bo7iI{*f=my0Y^ZCrC^?`6(Yl zMxH)Tp{v=*>^L9EPrj`@&OAk%GOXdQS@_CsyTju;U7o*?vhHH0f~bb;f84J1xqM$- zXj-M+c$k^(g;3CDi5+h&My?MqhzGdI7OdTU#Ggbi_bu)bc*7_DBC5+a*Nn+74<N!2 z@4nlsCsIn0$>>4X*?bC(wTKT-ZGXNtCcPtmxnt<5+-ZmWpr-BHGFZ3XA~`MTJ?LfB z?Sog`6jJW}RXM6NJVfDo#L<7rIj(A4k=HT*>H?)2B^b@)Mjxrt9lbe+L^OC%D|(JJ z->Q2qEQEUG393*q3LD88*b>!fOdZfHBX+{+^k?z_@Y^w;Zcd?{dfdMf*^UcK=(RbV zPFC*JY&32jWS!fzTrYOo-+C`ULVE<9VR0}B!&J6vq=9H~%6KLpa@2vwf1{XdV&!HP zIK`6zAAJA6d%FLfekRn(hUt@Ny;E-!4DM(jFCzqg?|aYTg(JS4?$4v!R|uD@U3UA; zc4{>{?e+(+2pE;v0{ac|3mEXZ%znpjPH#~e@n6G5D_&6+a|X{WFLnY8zG<BqEnXcS z9R^c{@d<fp`sP8F1bgF*92K-bKQMiaJxvdKJ`6{Y(m%G(xUFAt_Dy^E9JV_a(4#)g zZw9$?XEf?f*VccWvD@s_?{{o@^$6G>j~Z~|RSVqg%L#l0wO?Z*hb`u4Y$2oMM~09_ z2Zc)+{`NPxyFn2szzvPX6>*5c#EA2DpbrE7vVZ6N7UPN;s;e<bc%J_aCnBv8(jcB) znltR9niZhzMz5v8AW=cM&i&feh-v5a{zJJE17ZKL<}c>){tWGmr|f180a_^x%6ivW zBwk5&a{@wl7^Wqih%U9S@JmD@raYvCAFqOnK5k5k?Irw&FiaNcj&O%#jf><4)MI}~ zy*nC938hkvalGLVFU}R|FoB#kGQy^}hLNIsW=`VARfJz4E+6)|aKJx+@h+j2VLyGF zKWFc_eHhB4G9?fN*(qiznxW&bpN%tyM<_~<wT&eOx#rLPY0VHc0QTLjV*l1hrSk87 zmM-<9P9_Q`<<lRSWFJeAUKn-rN+K5T?X$Pf&h2Wg-esh{hNDLJ?V(=9DwS62W9+vH z%OQOZuhC~~nbWXL4xg#iBAe%J<y@lK{`~b&c4N}??VNB(IvDzP?3{6UbULqB|9#uS z5p7n3{z-o^O=`7zgX5H%)w0dA=fbiLkL$%&%h&84$w(}LgxNQ{27o<4HN_c$S-MM= zL7E~cE3+&z7R5ej5xpApCz!*=F2iyoX;OjwU=a;eWTQrZNfR*BKR2WY7Z9_l#-JXF zA7{W*siX`$2Zsl}PT(SA%P9B2s)yXfgqzeHMybEF)h3FGp`{InrT>c?w?8V)gZMy> z55pu-@p%Ar%K5x9AmE4N0K9hHi&en>`6uQBdeKByzrrRW0w7S6)PI9OD=5+HRZ0{I z>axUuY*j>S?<1%spaLL~g)oNgkbLlXT+ff^3PTY9UM=5sAQA9+?RR^h=KS8Wd(53@ zQ5`xI-;4vc6)HYE>ZSj3v_#xrCLCqZrxSVuK(7~wT4DSoV)TDO*+Gx&%Cw@!nAj<y z5svxw+8==Ay&gocRA?EJ76y?;2Z2T;(~{Goz4TJ1s<J9|7;~Umq{$ENvMpJFm@uO< zF+u#Dyz1rf|5h^)Dzc#MOH^$Ali&`QVoY9%WF?u%k18%jA|6h~Z9NWao8#(cKDO)l zyO7jnsJu}<_$^5jJ^D=@$kLeo72!JpiBCr8;-v_>9(vHo6eV1wmaO+W8Adu06Mt>v zTIl)tWWsd3y}I3Z-Clz=pJT~-gaA2oEiwyCtOePEBCWJb=iH4oQj)0mgG*szIUn_u ztqIL&acNJ7m))_KUz|XMMk~OC^yItaWKk~@U;zL463VgPxinNd1`ICM?i#F;gk*2K zfFwwFki1x&0lG5?QCD#|=x6PB7vvbK&B$J5>LHLv!zA};GWU7Cut^inFne+c!TTWS z{xgw%GS%N2g^LwP01+@cfl(vv#pP@QwnKknQMGz9dWAhf=x6~F?VA=fOL3C8%MZ*i z&s1&yuCR}r5@K{KX2+BYZh-y?Rrr7MmSW^khUkxo$YiQ4C|F1=6H6L602gfG?V8p^ zi9IKH38cA!l@8oxtb;6N;5I>w21t>Oab!BfiTo^=ipX>-<WwVqbi$#rL@N=z;wt5- zg#l)aeH8|rSgMue5vv2=3@!`;5)_n^guxqUb0-BbWbf){jaiuteDG7YCz@*=wNJVt z3iq0dDt>_xM>dTbn@Ofn+Un>=0nX&#!jOARRS}~qh}B4o>gd&60Ou(S(EpVElxmt| zxA5Jd{Y$0deG2oZ9yG3|kz6J}I~*ZcGAu60inc)Ik4U}LYeTD5t}|Lo%I<CCSdJkw zPN#N0rS!1oydrbEFj{W7oDa;}VgoQ`z$xX}GA5vQh$Yu2PJDL%C#B+eeLse1hEd$o zRE!~MSv!(Gjtq53xH+PjNglP1c91~-0(NlAHu7OP(jvLXHGP5%4*okqyQ5@aP;a%S z|2?woy>1)ujba&1q=3*S`*S249dt1zD$~igzm^-d7B+%W|M0dMB#__arI-T&H_fTf zBq9Nmp^oWB*K^>v5w#R&Kx(kOv1vM{MXX<Sk#U;(q#ij?Z`=RS`C}GvMvEik)WCg= zv$sI&>jOACGXE7u8%}Y876BT%$axee{^Nj19T9AuT-Hcw_`{+ab9q{D@;IJKCxE!V zV;nZ{eg(iC(XrAKkrJ@dNV=!U(^eDrE9y~C<oFxF>eEVe!~ZVJ`JlibJb+T(=R9d0 zLNTCv5V<OVp<O~5euIpIa#hSh%U@CugyOCNC1#ou9{>1!BqbFRKJ;z^0x0PLntA{f zo*w_DH4*Sd{5h~jVB(Mg0(7b>60Wv|A(aR~j7+f{#UiNrw|Rrwe~bQ+#LG#;n0|Hq zkF{U~y?=$TVD-8{{I^sS|0xA2Q?3e!8^y_zNt}^nfWAo<OB~h&0vOHRQ-p0m_|=t= z<)a1@rwp+^NJRlcasoN3^*R+`4W^QZ`%wO~P(RytnZ+cdQdiTSzjmh4s<_ejYrnqE zm@tECW6^jdy?BZl#NtgP$J$`-YCBXelk^~TFqGlwC}WuieD0Hyd%F|-h#38;5|v<4 zKv!LmN3`@UC$?I38xU$Z)L!eO`E94GaGyl3s3lIc-)y59>~Xq^S{VfD4f3;MWU&iW z;Q8vNYyqg&F(@Q(P#b1Cks}<GAAw0lguzB0^oR+i1mwn{tseTjLnZFxCGuvRAz{?} zFYpb#MjKn^Z`k248C<DYmy>C1Bw1edRR$NshWW<n`%;yq&B;}1W!9<Q$vg+CLMz}Z zA~55HllBCBCS<iWfApYW^~e994h#ahW)I$=r>#43RIi*5oJ}QnTNlP=I?;@L%yx_` zaIbW0?Za$}Gf-Vl#9FIUmKG+4m?w+KSvTNd+R(iA3IQ3=i<^dNgZA%8td$$CvYYoi zV2#i9HMeb~g8Sjpgm!g+aTH-fkqB7!xkB1xW<~{y8+Mycn}qPNm`*_*<yc#(chsp< zY8<vApOx8|(3nzhkm()BzL?Cq2rbP9-cqt`O2}zIpVPcxfPkhka}6;v`Rh<Pbb^;O zZ$_W7y<K}c6*Fuc?;mcCI|$rYJ0?vxT`fO@w9H9Iyxo%EppTy%7ZGV5%%W_ZLx^ug z@Y^E_rbWhM5wG}PKPfbqV>ZKO*lUM4I@j7<KAQ9qjEoyx(f7eoLG+KtYSwNEZ5)yp z=EbMgq&ubWO-E_4^tVxLU@zO!qD!T4RS$YvYOO`2kGqbkwJG^xIqS%?HRkb-!#>R^ zse-&oeFpaemB`34$-wHbNaETBl+n5Edrt9pZfl=Ru)nr)wK#4fbXDLMJ6&|Jb@$rb zC;aYy9}Nk1<mR>rQ{}oJWbX)=Yc4>*1w%YEG!7YMBYgKss-h0D`ALqOnvaWTo!Q}| zaXnJ!xY{9YFA@>Jcw2~W?Ka!oHb!YqdT4d1ZQ9CWv>auBIo2G~gcBK@U=l-9m$R<g zY*<pSbNl~#8q3-5Z+s99e?5O7@Ob++ehMI}KMH8;y{ds^N6OjdIhx;E7mH+uNUwP* zrmzvZE97FV6+G%(=QQWs=2Uh)y}XBbJ-y1cY+rM2zpS)oH+4M+>}2<x>};R$exQ7E zc@_^O_axpiz=YEH=vq2{j<A%6rUGMDbzpV?;gByyc&RGwBYo>NjDtwiGt@K2(mJ4` zA#l*u%b*316(Vs&WkL?cZpHy{&YJtiS4zph=LSB3WowtO$xqxb;#XOwVX(0H+?92o zvOjn%s;xkKbMkBIkm`AwT)j-=*qEtAqU8z%&|GlDa!(q~l33V|dPeP2F}m3&q+=Kq z;jBis-d>F{FqY}sc{c5`=wNudp@T39(=7Y5_P(QKZ8+jyv%*9I_4U07{tlx9B?Rub z<;;j$VXNltV=;%gi5OOCsa;Akpm`en1?srDk3LbKx6sGBWN5bKrtq^$FeI#-Zj+k^ zcLbAMDNbZ<xt86yj;101hSn%+>OomuC35;GuUEOAiU2z#>CWhKUuKs;(z(JCEKzEh z7m~?$k&$aC_7EqSr%g%g(v(xee4w^^Xe}$f6UHfpYk1T@M|g~xBHaT?{MBd%ksCR5 zt43h^)%blhJ9gf0_3!fDm;K-6{lClmpXKd0@bY(g-|zYDz1#b|`8d<_cLiKP=^4}D zd13}VJQWezLhCLbi8W80okMRgEGum%y4=F&ZrTHEa!;0VKJ~X>s#+SFF$AtV?l?X+ zys}IE`UNCvoLg_*96wF=?2Bile@uV5i$7d50~NYp7-8rS6lprlSX%N51FOT%1KmlV zN75Qq@R}TuOrJ?77Vk2di^c%xdh!FVpg;E)+0|cO7r{=Hu5<DdrP**S({t7fRp9On zO-t<fm(;dO_+wvBxdRYJBq}2RNY|Y6?RXSkR9e>kzN4@&{y0<!^?>yxor2Sdb>Ia* zgqLN$BaU(g^+Hnn<a>c8_gX>Fde8A9K!Wdk0gd&-*zAVVh0q`P_Wz0a^Bm+CqkB^8 z_Hm~5OuUlaGj@F~Kp1+q!BFOOa{^~A@l33BGCM$6*+T*DAbnlH&rq+UYbN)v=$q*F z{O8}JFXY{{UDtn)zT-U<J-vfplhX{ypV~8Ud-n5)Rl5-OhA@e+fFvccikW0`Xw*B- zif{vhTuTLZ1L6l~NZ4r_Ty)vs#=B7jl;ekM7K7t)Ve)_3j@(AuChg6JE3x)A6K&wE zIM89pCUDgZ`I@S%hh$7T{#0yI^}x2%|H9Uk$2SRiJEo!u@FDvW)(27|FU2H>s68W% zY3W@|>2T;WCD662zBj?~+04@7x(wY`iJ5PE+``t|W%rbH(DOAO5az<mW)-c#^)$@f z>NVGzgM<fxa$;f@`Gbx4$v>`&Hqb1D5;Gwm1K&Kg)lK`nug-P8Mb=6*B#8N<0MpE4 zzOiYH%8Go)=0wl3na*S-&iQ1lHLxB%1c;g=$?9@8G+InbYxFPFZsQC;t6|DR(32$6 z_xL@>rS$d2dJr*tlg6k*!hz4Z!}sYJn+|NXYt}+nGHHC)#0fZ#RAb)q-I5EPt9@FA zF<PT_6*p4})@tQtB`DzLs6%sBO*t6XRqq3S0`+T0O(M2I1ogz$s|?lJ%m(c<Cl&s* zacW_J`Q%iE_i}@_{mz6a?z<Rx0l&`9fdw8t=slG+0qbTunSnIT2iP-Qjq2p<CmqO^ zl{UszCzYF}yT`L{C-Tk$%k#kx6ii!VbLx>-)ovNUAQ(*?BMc0II%I#l!^qgQ;jCCa z?mZkz={3)dOtmHN`yT5YP-a{0RNa8v7N(*;9Z0a#CN0cKiZqVrQ#7x^MKz=O>=ODj zVvw+iRs-){6<Vt>S>v$owZa}8>u+pcOauzVBoA!s1mY;d2E`LJ>395Wp86p!M8c%U za>P84jn!Zp<RsV3R=K54n?&oR9saP`T#v?*e34M&So+R<+@5D?F65Dfby-+i3HF%$ z4if+@lb&UmhS^)4$hi#y*yDyDGJM>-+U8Sj?j4+L4_XO4)wraN*4?bU12&GyhJ0S) z;b0D&odMI>@i^SZ6|~SsjDQo05HO2j<E;dr-zfhzi|IwUxL4WT-s*Q_jSgzvQg=g8 zK@I*$(5+tP**GOC%zmF-nf%{r{QvuD?DW>vdj;;xaP1Ya1M{xC`vlU1@!Zu52zmnV z!F6KaOl*yvoE=RJZ2rEoGqn81#7M|U`1b`12Nw$?=YI?{|7)0+mykim#MaE&oREnH zFs4YzAWO(X$RK3xY~pBZ;A}$3AZp@jVPv8xAxy|1Zt-^*@N5luN7Tf~&e(*Ipa0vx z{-ir_c8Z&_(i(c`OczJjEHW`6**&%=G~qnYcQi1Ca7ll8kdPp9S5a_rR3s`@W9438 ziBMuAF;tkJfrcy4w}|q?eF~yt2U}5A&{tJ#ciR~sBN{tvM=dLBW>-Ed?LhMHfnfd4 z>cIZ2`Rb^bXMNhpC=-KEAee~2@an+5d#0vf63?O_i|<{0>FK||4>vqs()$~CY|!?| zlRGVc`Nb1+h>$`6eG^+^=k+DWH~>jfY8b*|{sJ4nPo`oHVPZzlhc2ij-XE;7ek?tt z=@?5(CI6F;tuc+&$(j?@A-|nLwpSPhmam;gwrf`C4QbM)ESUjx@I<+=@SG(B#E6=% zH+e*!ZynAohKh9=ehUHwl{CoJ=?EYB!eyQV<X&R-Cju;7(y#<+CC%dCc+~Xura|e& zkclQ~;KyE=yUfgeBWw&KhJTG+iWo4NhE2M8{6!V+#f8T5q4`VC+77<2(g)wD=bOq6 z`T0zaP*je_j`evZLl-*GBdKO4UPE`X3Q(gBgy6wmrQ8)E&@DJn<95~^eu%Yh!r*UM z3Yf_Ab89EyTq}B-V~)4nK)dmt5U!{6iOV-to)Qd!01`<w789|9lF|}s(aA|z_|zHW zk0->h{a!AMsj&gttlVCVo8V&=h2tdXpm6h!VBP@rIC|r6L4HenewgeOkXXLiR;cDK zdO$wGT=K1wkfVTR8X_d1q_zT{AU<bWh)mx`yF1OR;%l5@nLR&`UI}2dBW(9%gou?6 zA7NvS9mxu4u?lyBtouNa;~<^$zOe?-GlJ0S!{Pd~*n@5p0s*?jAcCfV(tiiB5Cp3i z;$H>&Cd9M~?E2Hv9%wPZa~4cCklh|a8%(Vi&K?HVUqbL(NS`z#@OFR&RNyrtT7gh` z9C{H#y6`e0jwY0ch>$!dD(H|%cpPGa;L9xeFHGv6<@qA=eD@^I*zHg{KVS18X9-y$ zK7f6K0CpV<25_rD&Hih(Jh)I2{S&*Q?r2$%7`+>NQmyFx@GboqdpOrXw847)iBN*0 zP@r?->QFd0qQY_Nq`*i-R`J9|AZ211aqvYrZPDN39sYnqgytKhVWJOX?T2ba?HQ^X zEE`HOE~Oczsmfp+vYR5Z1iS<m8!pwAs8du5xI(idWk%Bts_Mt;Yt^RKfvy^y^Wegd z^kV(SwfAks)MmD2s|IU9Sc&TJKO2DCBfoL)q~1oo3Pk90x;FGC>i*`3>xaJ+OEBC+ z5eKaaW)+A)ltM^qO{$ED4Rr+d5rovwsu1u?&Y3s_(PL2Ckg6_bRj5-Ems~axRWgJW zLy?p!y)i&p(vnz*oR7SVC~=J2Si%ugLr_<)nlzVCj|?F}Kt73*992#VS4?<KmP@Kl z&_mEeq*xK|m+hi@d0Kn6rd*e(kCYFCL?TP-UF!Z=RboZFRq|P?7nxkrN~&9`9(^u_ z9+98aXUTgjh(e!WpHe_!t!lpbUSmIBu%uW<L9$_(VTNJ2;m_Y}LkeV_2>}T$$Z}<Z zC$;lr#YEV`3&kGut&Kk#xz|nB8Sx}Iiry9yPloOZj=S!mO~_2N4k!<B53mm!#uXv@ z8TFr86tRk<5~DhzV((ZF+h+D>HrQ({biX>Ol3%E8slAhllTDL>lVi(eRP2^em$J)k z%H<XrEy*nnEY~dGmgp;@XG&+>a%QsiSik<fho089hkLfZT0RV%@|`APXJB_@V_~ac zr?L*SVP)86jAa~T9I=ixnrp%|foUFS9yRGQCZ}IcDw~ouL~`X<RbQ0S&G8p)Rq$0A z{c2D*E0k@RM$t*2*WwUr(QZ+46PA-^P-oDqQMk(ISIDi(E$Wfz(efMboh4k?j}*%6 z`;{M&-(!$tP`GDAQXXG1M_s5{$TQ4o)a(#(!@sXZUK^e*nIIWG_CslPaa&oxeWq~+ z*6PrloV9|jW2B&VQ$wRtd^wM6fOpip&?EDO28J(EJu(}83tf-)f>w#{jFv!yN&{To zrt!4)bzprk!%D}<XC%aO->6}_Wb3f0s*G;OwdcZzCqt)p3%x_OW9AY5k@$`LjR}%3 z)IGEd{%O_Om9itqX9?--c&~N(CTbs&!i%DA0l*l_)|jT6hMO*gfyKDSSfs;roO9S~ z#dVCQH>Qu$bGOf5RM|-0*F1Zj{IL>!<T_|GxV6f+_R;!AgCLC%FA+JCRGN5%Jp-c~ zmRh40uU6BN@ML&pc~#2K%^%Jm&5zRU+Fjwp@NDn_^5Ocrb+>YT^t$pU2gV6j34Q#n z2uvJI6_Ou@85$Yf1#F-XxUVCSfMH&bRC^H}MEG6AHJBHg4OR#3)d`cHmbH@n+@n>+ z-Phd>jLuL@RAjK@d+g}^s6=Emx-`0*_?X0y*pkE#F)y)HG3iuwT8)N3;!&DWge83F z1UP73q^^!-PxD*Sq5J2*zwKYrvuLL@oUc1J9h6PXRLAM{X*f{h;}GEOAby3T^p_pT zRNw2hxgCJ5BzA<pB^(XY97W&gJe$17gQW)52hEA54(fy(NmfguN$!Kl2{rAy)a%^_ z{3%O{ODd#dDVr;svIuH?Zd?#1Gl;0A(P81E=wcXPEo2mFAGcV#R6!{aoQs#CbkZ>G zNQ?QDdH5bhno0Ik1E#^Ue%nmKBzIh466x^%PsShH3B0kpu{U2fQvk`ytlRZ7<h_sW z(h9yRc)418qfORLr>}kQq5CY>hS28w+VzC^M6?Ocj%~#DqC%cRZMVL8!`0!iaGS8g zUKic87QFS4HJbIsmT5zk<wh#8?%adn#P5jTa%7tcXFD1`YA;)pk3kn*#gD~rZo95f zZkt;t?RV<wh8ntSv(>vgRR@!|1-C67YUkam23ijK2o?ZW^hV}i^<7dmmJ&;)r$wg$ z*s&RhU8i0x7uoA08!J6KHhv4h(V#<+{P?K2oqnTF@>W$sHbVxRdzAw-X9cJ2J!XD- zexeuOZ{lYpf+dPa`eR*|cotVUesLtR_eUoT*X=uu*nR9ih<Kq?oXE2jwmpM!5^>%9 z9(g9X%7Nuw^N8>%KcV=@eQci9jL^(q-dqlEE@HRvlJVL(@n6Wso5rIP$o%rYk9thc zXc#w>o{67E%gW@+b@ROy9uB^V495uNR&(ZWZ~QZ#U0qwfJrR&vqkGflShmsi_O6mz zVOGth!{d|f({&N@5xtDxk&~i#?rU+<bY;_ZV&CwQvR@U`4eoRFs`JLM{n+kv5c8Fd z0~!hOdP(2y^`*PXcjB}CF$Y<Oper!x$q8s{Y;?X~K=DnEI;UKq>SO77#k8#J{yc9* zf2JYKEH<Q7gyAdsrABT5e5P!MM!Zq{RH8~^Ch8?>%d_r&=z1b5bzwiahw8KWDS56U z*VpZ>%gk_W@Y-tbEL}iq`&T#D>+++`k@4}L1viVUlG2Fnif)0&fRFq)qx+HglXJOe zx#Zkv0XJ{x_gi;z8`Hy`r=YvH{~=@jn}+_)See<GnExYRW%}2k79oSAh={O(lZi1Q z)88akkx=J9E@J!FMgKLaW&h`z|HsU=N^>n?qZQ5PNNqR1e7qN|m(9+#gJpm4X1QF3 zT<K1*V$Ejydy<U8&9m7Z9Ty>xEy!ZQqN@r{LUDI1<WWwCzuuU{Z8F$14vTC;RAM`! zKw?CvCNjDr(s1X8UITn#+P9Q=&Ob?JQM4R?hHiOCCbjqxzYae-4lX(lw)8NJUwGPd z5->Qt#8thJ<?)N2sS*$c23X18l3C;DXb&B%gcwj{3mEep`uQF@xB`+PZ)tAU0^fTB z2?;+#J%!Yz-VJqZ$Ej;340{NB;tzJ3e$1^quZM|>f^r(Lkxps1zp$2QU?zl>vKoZD z3f{xdE`uZW1?Toy@4=@|*d2J<EQk6Sq)eqE>z2h<PRzLI`sumTQ-ET}AB+c5m@&Z> zlCovaQ;_riq~?2p<V5kihK6$@jNbFEl34AZAnRtw%>mVWP8tj$6!GQ)GNS9Qxg+N| zzPRPqtBHjFQ*RmZ3S15R?hW4ScXz(%P>5Z`DoxV*tJzRq#Q~@qp0I(g=I7F3H^NFy zu?^xpYwet>b8UD0tcnNZ0pILC(#;o4?6q9o_9eU65@m+F6tMW5JBXq<2{};Nx;O$= z|Be^7q9<Mn`%X<CDmQvbR*8jckrADw7T%lkd9#a>YHUbZ4SXyHRr*p=tWif2vg4bB zU;|kHwv;!+03Fu)MNdf380M>LRm;nVUKEN+lO3X+hmtMLov!W{18=cOZXc%iiG1~y zStNVtsn*j$XzE+J3J)xcaq#o^rXj&50W>`X6CM415gpeO0)*FNQr-z+?4VwaHM|?0 z5}r(3*c~mwehGq%;wh90YCK-Z2;zJpYxM=IhP*EJ9czvbN1a8AUzg%^@Klkc*6<=C zVcKV9%7Q$`h7Z%yR_nLVy~DE}4VL&Kg7^yPgZn#g6-O}ZA=;VC{P^Qb1R*|94Vuzb zRo5QhM8t(nZo+QqH$$%U!t<wAj(X#Wv`Lgwv0o)&i4faa-HVGP#IC!|;dwxh2b$TX z(~sL}pJL0s-{<|SZ+UXQ&@I^!FYt=scQ#v>COwde!>IOub&51yImsG=ym$U;Nxw-v zYhuw)GMSru1dpZ<*}C%^f7*%YX8&}XJ4&QsJkpTbN6eNzUcHYy*z$~e<(;*0eZRMy z70Xb-*JO;X>3xSkB?kB|ooQFc{ptuYM*qx7yfeq4l13I<{G@Q?khQ>jch0&n-4ke9 zXfB@t@=K7<S?7#(&mduA_<C8VoVW<pvt7)1W|;iF5ID#gulMQnb_o)`Cn?j0NQ4yg z@rW6#YSe!Q8!r2iH9TWEK0|NDJFb8vraV}}b22ffy0YPT|2Sjlf{C7;dC|3KFEMkD zhd@A#G%H9toKjNKe9)kj2z>pqs4i0Kjzo+kbgA;gW*+56VZk}eqfHZyGUgUy?0f2D zreTBFQ%&Ubf^8d&EInrMVIN;pB*_70_Sc`GAb1r1QQ;L}v#^Na(`FbsLXYLN@Aj>U z!TTb@Tbri1OSz+1{vr0<pzbew(TP~}YNRYl3UE3ju#3&9u9_iOLgFL_8ACZ(l+Zk; zv>2rEf!Y^u+$K%aPhMxw6G|lPrSJ`4AF?UT#7QS4in5sc9$e@q-n+ay;&Dxtm>WxU zP#yTvIb^I-u`EWc$3mar!Yr)vL*E=!FI^f=6CzLunIoXdN!AaY{5cVZ!VP}@Ml-Cy z0yLstv5q?5h|j!e7N^SMP=z=JuEgP8m}ryHpycqeDwhuVDfi%*@e?o=SH0PVuL?|K zC_qH43uVc3_yn+7i3OGqKE(L!N6m>9g&N~EgttM>17xRCbHCq0aA$_{BQ&CV-kE)c zxHD}I<dsS%)axhq^P7`zAO27ppQ}3kt-4V@HgOsiA4qn_vKoyXNQs}hv;xnkAG72_ z&B;`;pDOV4n_+d*;O{Wpuv7ZNtc(Ou?KP<Pwu;CP-I!Zva+`?gbAW3{<SEi<M`9Vv zy9c#7vMAABOm_P<{+8sV6ql+rzXY6z$?IX7c(ITraEBel<v4!hgX<E)9D)7WLNsa% zkH#DD)dyJ%pY|*)9R1uI6H}4;!=k(cHPl^R6VdM7nHdajVF7FX4JKdKWy87^G@IJz z9#XlbbAH*K<(K^8;e*R=#;eAaIeP1gfz{96qe5D?w~S=S-3Z1Hs~!2K)4Xa5#-nw^ z%3{`LZfpLh@BHZUT5E;(&@$AY$JHZiNTRL4=FwJAKk(>eLS2Mz1tUl8_lVyz_o9P4 zxhzF$AC7-C@vsi(U*uzTdXB0cv|DF2cyGFBI)<KL+LVMd$Ww6zA9TjVlYMV#4Z2Fm zpOzZSV`!y0W!B3icKyU-l6FSoVRFr}q)g7a^->(<pCgoot-x`@@HS%$YfsTvh)8eG z526|n46jDJepiyb_6zPTRs_C2{7{md7DkNO)AOo2Sq~=}bFq`Fh}0<HWh#$^aS-cH zG$3beY~HkLcH9hE(?SJ%5p@ogBFcrqq7DXcp|4G<Q;;F4m6hB{4;T`e94Z0lzUjlj zZS@Qje9MQ)eX=p-KM3`DEU=O(iNa5Y-m>UGpT548o!)mYtDcGx8$|d~W{bZ7Y=6XG zV$>hG$CO8k^JPeB8*zQh8kYOIez?S)+M*Ui{|4q+qF_9}?fq`7#o0!`^v%Vz@E2Vy z--AeEttQkFTSQn9Y$5$5e8`IwJ9qkEH5{o37m1J`k1~x44!4jqsm^3|5?ge(WSc>1 zK`jPq%+56@rc$go(mA7W+#s}u&3X%atM`bJw(*b4>&-M0B4N$oPR$~JnU0X70ps;s z=|p&ePXc^X>+b&{llhx_{FBKrF|q$glEV65k`!eRdjP;DZ(#P%qoawfGvVL20U(^B ziIbg+qmhXdArr^HjfmLUIs+~M4E#-V0GIqQF}5%ewsZfc^%vs<Buj)GT+BLvkJ>r^ z#qAgYAfBzQ9pD4Zf0HN0zd+r;gMxp*^6wA(n<&ZuU%b6_P$p}#?u)y-yF=p+jW#si zxVyW%ySuwLPGgO`ySux)I~>+x*33G0?;A7w#yR&d>Wh!~)mv2o`Q(#X`Py3P$r;!a zd?+jFZ^8t0@&?ZK1RoedD|0JbIcps~1A;$(P0)^kfZ-GLam+-(!0`|0ha32@vOiJs zE=&ZBe=O!>b@DC@1nhquG7>O+9`o@0Z5e;A>yKrq5iovAImS;5lJWB<Fn(Sr<EMmU z{Jakzb0+`bGk%_)>GNz%p9^F9lLE!`Nn!e=Fnv;(J}FF}6sAuK(<g=blfwK-Vg6hT z^Pdzb=1&UqCx!Ww!u&~L{-iK}QdmAIET0sXPYTQDTK=g~S^mKNb?g6Oyt4e`fl3iD z{5QtycUv8ofA75hR03Cyo5$jL7wHmPkP}`QV9Y+jOaat~=a<(-62Tw@^Yw0^=!Kg5 z0*8b8B~<peDk25#%vQFg^l^i<IOtEb(uPXcameh6yT*H-@zHmjw|jRZ_v5eYUI2U= z0|_G4HUNB)>{?hz!%~?k>0SA&z)*bXML^%Phjw>G7bYhWt)US1LqQL|Ufx$jYm3-+ z1D>+joV4Fjxbx$|k8y&kA@W8g0R!y80+dIQ$Ls-T8w2j{L0^$V1PPQSpyFc#*iZpL zQRSKCU?lrP^Z*qH_rt{4h9wiOq~p*|FHWXlei@ZSH+c7(HJ*IG9v)02Y@QqkVNGZ$ zYp{NB^uJ5GZ1jO>7IaVUhYiK<je`Cb5`*vz?42fhRoE|p19r)+q6mbo>lS=G{j(Di z;aR|4jRIQ884JK)s*0WmQdcjyyXTrHkKoQ<d0-e?#Ag9)3oFOl?+QduSRG~8Zi_JR zDY$TIMVWaW4PYvb4gv-%ChkzBadQBSiQ=8Mf&gxCmlYivI66ZbeKBDWHY{)fY75=> z1c4d<OpqaZ=X>5)oh&4ZvB+<(uY|vlb_QPqJj9gR=<#+(Fh53RwlU+C2h_=IPYZS8 z1p5)vqKFB`;5><cB^*q=hUxQvlA;O|Y7@ja=>)O?3MJg?mHov`0iugRtx7G@)4&H{ z+sXZq1m2SkPS1~r-~+@l1fU)bjE$_S4X*}~P8ui)Wwhf%Lox2%aKFudl!s~@0TQ|6 zYtPPv{R=ye5fJ|uzDm&DKmhFRM4QKzhfCfQPEH%Zc7+d2By=YL4!Qs{u>CM;z(gC) z0=%~@CeE<8a|@c$0I?CmmwJaDFy_eLH&Ld80n`t+8pOxZNyK%ad}+Pe7?Fg_JDG7! zBw^`LY3P{zPti1utd!|8AUHd=R*mKo$K>NNR<HQAV?lM1_nz)8eY);gebo48>9Ai> zqWZ|;Rj=WeGFY0P<d}D{<}n7$Nn%SB7}7avZNp;DM`RDx5zmK?I^Uyfe=K56pV1Rq z$!xn28bQ5VwRksC%u^~+!c(vTBDZ<v*vmc<G>JEYXK~@5hT!#sqqz0pAihxh@<cLd z`>Sv_g6tAu^H9)v)nChv3Ew>q!(kB4hp`VQp8H}X0%H%A@ny~<6lFnQ$AIU3@#7X+ zvGxwf%xXsbvMt5uCR&Q8?O4w307!p4n>s#RJCL_3%KCFn+JsINy@DZHWN#)B2AU*G zSOyPdm{?2LSJrKga1Ou>zBKbT{dQv-X70<JRhiTrE&G+f@(&!+NaWFP(3614mGc7W zWU0GFnZ^O*_4xCW`VRvfsRPzMY-<#GF;S0s=~&~hHh4qZENEYty=de<hE6ze-%Gpq zGl3bLc-0uY3zIAQ$D9oO!0k@o?8#;@$E1Z}1=dUM>QC8(kKyPJN9>#k$jgB-6DH7` z>UR!Lx6udZJP+2<bJ&za6%WkQli+)UB!thuO6|qHy|3V5BzF#>N;Q5d?mD>8k2J<A zhScNi-=Z=a@Q+IXU<v5}G7duNL(OnxC?g$#w38+kR)AncN+TWL<V^>eO13BBsALoZ zBpW2t5P6pgFG*l?moCRDUW3cW$GIg17Il*E?{foi!Vf_&F@86HeTXB>c9ktJ=9f2n zT(dnd@|lq~rPr3th)(AWzyhlRPfehs@OOd-0J0?&l9dUh&l(J@#{rBZ-s4ze%21KI zI1W6MFopvd*V}O-lqmEgk3$O|5V^?(Aa%}~0(!bAHHB-RgPWjc+W>7(;lF@G0Nvte zOa^{M=#miA(HHMvl!l|~#HvIz2j%L-lLp7?(6NU3?K`~zZVjmpnz2Fql6m7xu8q(F z(v1-~NzYjja;Qg^Olla!p?8){hvqM&U8T&J7-*oaD-oEWou!koz-dlh2VL6T)|*;2 zy#RWG<p_S<`BcSQS<miMx}n;DXc_RMbJd0wFS38T<`Tx8M>9NWv-D!r8CEljZL9E7 z+?jkWg10jWf{y~3)1PvauK^kG8%m5oKC-MXgABhsBFi`6GQiUw*xWP>g0*OQ**N`J zIQp<0eV{?&{kX8fgdIg&{6&#WQmg2%I|PrJ0)-f|Clr||a0X<ge%0QxQpLG5vXQc5 zolvXdE}}nW9ke}HwAW&bQ)U8Y5@t?k{AT24%w~dS2(cmxe$?bWPUIS^tMZhm*99p4 z;1X??I{w+JD6L2@W333GC?bPE9Zw}sd7{LgW390n(^Z+9n)@@iP&b}Pt4hSCb_=f; zt(Q8qHMCpoNNi4wN9<B8MQk{pm70YjL$0atMqNc^QDRY$Ls%uhMc$?6ig5RO=<AS` zJhQyH0+76{f<9HL{9Q@9Y-8S4x>TXs<m(~jjr$XoEAA`IE8r`OVBF@w2K_@gX}GB{ z@iymnaY=5;PbxKsNYg6Qgd@aR>JnhnP*X)yKvVVFUej6AQPcXFz!{d=jG4R{yBVdD zbEWp|wL<>s$|;Dc4YPQYlR5UhOX;`?=6RTj9TU%j#_!kZh81WE!>VC<3B?M<w;#I# ze|U+orzT4wO94x>OZ~OY4iLE1xRFwIW2PD1oEn@~4iWZ?O+c%tO|VV#4qAtjQ`hsz z2MPzcGsiP-rQ<X3Q{z)wrCt*6ZGa})W(azf9oj*i9DY8cs&Or3mt+bOoZN@xf-4hq z^mEoZo0i>h-Gsz?c?iTIlW2KN!oYo;eQ$j!ec0hYFz(ZgQy)@;C0LWwnO#O^469QX zM@C0#M)>6fr={gm4r!-{3%U*G3?U7F8iJ3WrBSCerxm7I(OWU$|E8}>){)V*>DVF{ z7ZP_8&)Fp#b{tkxoKqB494?Pkxm7t<u`3HJhgV*&$yt6_9H}p^YUD`aT=cM=+c}M^ zSlX(itrJ)_U8Z(y$SBPiuxwssaSLq?YF%jSdrR~S1zyhS*z^{R%8S(V*DKy;=r5F` zDae;l;ri(@<dz6cjqSO5#%aPHz>&aN%l^7)emr9L%dw3kfrrKUD7IKqE=#f|jV!rN zZ^;5(G6|D*SYt{3&ECN-p^3HOu34qwXQPE(^X{)*ywkR+*B0<zy{3MoD8(qY%wL(0 znL%H@?rkrIu9nVc_bpDpT&!)sPV|oi9oGyOE+-wP?L8-0)n^ss&sX%F8+qAzwRx4l z6W{4Ok~L?(+H;yU**{GlRseAWqk*PFkpKw-Q3KI|n}CplSirg>n4_g5x)V6l_mJxA z3=~U?89N^=O{n1z!$ryUW5U=$tD!ius*`flx|=<WBi12K1t(%YgjZr78?A)>4oCTp z7X~DgUUVySpI4r@|J@5;Fd`(fIP#HYj<SxV?yD4&rRB<7L(=8+wo?DPWTj{{>2J~! zvI4R>(l#+D3Du;TuXaNtLQHg|*d$@GX|l$YA(XY0f;GN|7U{ETN3p7zODxN-j+-aY zb})PT;~YFn4zx~ZG0bABvP*BjomMC;VV#WejMnO0{8|9k$kveCc)ftzwKj;A$(`fR z;@W6Gss-eF9>VPtKa@#v5I}?LLNg5l4bMj06HpUs*^UJrW$!w10@8n{8&(Cfi@USV zv)9w#r}jw6(jI1>b9imP@S8OJW`!S4%b1U^0DbQ~iC!GwNxDUOK$Ayr(Tr}`5Xo`P zX{gK_s52It%-h=7sz8s0c7a|NlMqi#xS`RdTdKuS<F>L{M!F_75O+-qq*<+(Fj1MS z#ur!lc`T;oo_M1oo+7h2RUMQS<nEcBE<^jJy>`Xyag1(&?wgjnCWAw7BI-Hy11dVT zN`gYdx#E?g=F8OK@TiujazfkDmFfH}*PQfx@VtHbty;5H##7Ul1N{8B@_K_$V~%cj zScDy$W>KnQtzFy{*<!)jC}LV#owB(~&F-q==G@rP;VgJPrlnfd>8#`1-7(jT6Y~l4 zl1k(CsZwr*<vG;itg@bhTy0uYv-W+38`Ax6uhTaNjgS8p2B%qWy>89-y=Po%`-)2S z>(=i_zz=|nK5tJmPo?W1p3^fM6P3r=W1T~S8__2d+g8(Ur<^#R4X44+F@fha?aPkx z*TzD`bILR9&JxaL&6JIYs&>kDYGF#lljpeY>&vD~svhdk4@byi<U>;K>?-cI2k|>I z9f#r5@XLF3t6!O}M@DNHjOenUd7a*ZHt@%O*DU|YyysYV?YtJa7P=4Xr)|_UJ+VGy zf65z<`cBuZY1y*n`17!&e>Tx<ZC*<KzS;9hcq0Pe($-S*DEy>lE@8sqS@Ed+Z24u` z!>Rh5`?+|daI;x_leBHCZRxcHJP_XWjpb1byji0;>K^3I{srg$dNrse=!fUv^R9-~ zV#SSCDP9V9$+6v;*(v;4`;+tB_8NICc{lKGd+~eSY4Pavx##_RtDg0q_BqpG4ZK&G z`>%KR^Dq;-rNG=Eyp|Ccj+c_H+K&P4Rx9hj+a+C}jh#EWmYPSsq~2<FJszIj@NIeA zAK1QVT;TlI6#dWC>yLDdiHVi%pXu=*`P^q-`mbWcpNZSwiVZA(&!PWH<UaGke_w1+ zk+fT8MQFWN+7Q7FMIZo(?eDRWb2^&CERGY51s0!%<BBg5k;lF7zpJ<B2O#11jWwoB zsWS1f_6e%DTBmK#fj_{8+Gr<05rPzI#_{-ueY0oyL%QxeU;#f2Z+CF#4>aiC;}gxO zV%9`O#%;xP#1A}>)(FCDrV(ppniNFFx4}GwSs>6i!0<pj?O(D=G-SLq&e0E`RbN9r zRR^VYkWH`VxXsUw+uFw$fXd(IoKhPPlsEhtNMbR-8D^p3-JttH67lRBZ%!SRJm}_3 zH00Ah{wR|36!s!631xi!NXW=AWGNgvFN(dOqX^cYqBVs95O?1)_NTo2ff&M}+5Dy( zp<KO*a`qOQVF9s=_|NOcoV-_nWFlX@(1JTEaMtpXmOK<vlN|YA9&`5#)4;DiUBS#y z&fqj<$*c$1PrP(f&~oqaE5~@Yh#PSGS31kP7W+?_JpIeo24RqW`31<H3R+>}m@ODj z>VN+VqtSAPTDm{nEbeW5OI2z{_dCj=9m=J>dMq9Qio6j!QNLHQ)psKY>DeJFOtxhm z6PnuHvQ<l$;0H$)fjEFfBw|49=6M}#2m9?k{-s5DbWGwKs>c8fa0Z)}8>~GHBQ3*; z_qUthZ?Uw9NnW0JUhggJq}sGM%D$A40AEoJ?FHhTZ`ZB0gW8;>A;)?w8+wI%*eBYl z0oTf!3@Y;&20&`ycjNYveFX=S3qWY<DfJW4%A1ty=YKSBZHiz?&VxXV5tF1>Jr7NP zuXOra0zn*Lo32+V?WnAQz;0gOZCeM9;L{NfrG}ml9{saJmLftgb<meHVlBQe&^s;E zpfqPZ>d^-osdTUdIZy=zw3>mjHB^`j_Nn0lW9C=xia1yHOkG*Y>5UZMdp?^r4rB-f z7?)5q+)L<;?H(#S&3Q|U?U^&cZs}EMKZ+d@n6!YV&rn)Rb?D48vO9ZVuA?&No;wO1 z=wz)@R+y(iD_jpm(RL3)tb)n(_@kvqAcI59_yMO??#OpZs95C2YXgmdI#FBH4d-ys z22`#X>^mPZik-9j$ITp9_phFP3wEgHJl;|lhC9E^;@Z3mN~bSfe*0-g1_6b!PYv9r z8$!%gPhM)BSr%yYG`urNu59!*9YG-YS=QE+!jZ##F91ILqKIc(vw)wYIGibXhnf8` zd)Q+Bu^_NM4KCl6`J==)`*R;1+gNpGp4G(V+dMfdwMDg=P8<6iNcw)P3!jYy4sD79 zs;NrZG8z^2^y^#lEV$35)C?J_ZM3*i1ByvhIgi*-FHC#cy(tiOpE?|oI1tXYetacg zK^^O={PC@hoWA5TJdU(GV3P>e&&A4s*uB+MLy0+eF~Rf+5@A<VL$}k5oc-&*B*AY+ zfSke3LVS4`<UVl;7CB9#P7ZB!Q*+F%;Md$CrIvbb-QMq4)vwMeZSvqETg5U{%nvag z%rKa`$kcemzchX9&KW%Mj($ncPXLkapbU#{*A09*b_dpHVK>@nI9|=EQiXfqX4y## zq$AKi>H;~OxNdMO#)I+IjA={XB(a%7?-6bc;H{f@Kz(l6)i%SiCtV!kS0de4j!nqa z>)?Pj6QPF|@lRF_1}z1boI#=9J!WMx5-Wo<rruO&7!<&O?7xdJ;2NM`*n$+P@!7kZ zZf0O~m30S;rfLDrPZz-lwUsX0BW=XJfLZ~e>!TQHqmZx*v~w67>RVe8E4_l)HNMuD zeZlNI+2ed)b7DMFfbaVib!$h3E42cn_e`I~5S?__*T|@)j62TFRLEtGMv@G{Bz)ko z(Q?h^bYE}i*9%6LVxscmrq6la>FgfM@J&+JenG|Eq=C7t{~BkEpiF0Ump)JwgsIu) z?s9D7X88P+65|Id7?59_w@m)lSl}fi%4xO|2m@FH`@;4nbFfohCmwoD$<kMpH0TtJ zlI$A7mI*WXE-Zw+c_$;zoNkJq9O$mOT=#}%wnKMH|G)soJ#!E`5*^+>)J<giAVVp* zy15<N)h@f3IslQp6;>Lr-CMiN<xIDea(K7Jiz5}o?ef|Po3Di#GgAnC)zZKCK*`Wf zOWNu@QWWemblefK$GWD?jB7GC`PoeHl#xmHv9DyL{i_|Wr?Ap#xc7ufRlATcov8OG zFb%uta}DT$16wH>2ujqhp`ELX`_S><z)7>_9siqX_eXsCBd{^BGSmMR?O6VDssC_! z{}V%+<<su`Fo^$QNdFlM|0jkt>!(xtFGKoI<R1(BziLST(}n(<IsLyG(tob&Gw`#1 zy11;LE-ve*i_7}y1+#wowXC0BFzcrm%=#JoSwC+M+vgRs{Yimh`=qdaQrJEzY@Zai zPYT;7h3%8V_DNy?q_BUkh5b(o6#FNI{gcA}Nn!t_{L^M;|LaNraYO&1PT2qP-2QD7 zugbFRvM`cIztRY?a-<H-*+7~Ab(L(}09t*u!X%G3!Z#v72n>Mo1HUV^$HcFzOu)YA zqT#~|gAh!s)AQ4--4$Ck4^-+eVfo}+Gsqf*p2*mnoF-Ty;O1g$i9~&E_8AjV-!!l5 zRY7EY6-X_6jE)NfL^7DKh}Ncu2@<4`#1MaG60b_rm|hxQQgqte(49OshaLCsCZ#C9 z5YnOp;plHX@b45;-*Va`lLy$##IrNPmU-Z1@n~W_eGH7Jchu!&r#zNp1JykFHWl>6 zeg7i!m@etOf=At><-yj$MbFZm>HOz_Tan=V6OOQMb3hxsFy#p{$1iN?A6s5iz82@8 z#_qW`hcBXey~V_OTK+!vUF@^0*S|+;19KK&3~C;23?mG=SX*vn`y+CJ$=Y<Unk$dZ zYeq{_oKFLORkn}PU_vz?xYD!Y|5RC7x~{LUNqNXpei8H3E#GH5AyHZxx%VT15&S;X z!--1bMxH?+38EtAN}mf!nG~L&8<b}%fbdj|+?1KKaOo8bgy`1eiB$Ci1OFTC$srl% zgd^+oPwMjlGX0Y3eFcJ(I_l#x>eBBu37x)u^5n17g2os<;P)VTR@HUoL)^sV*qKhf z5)-xSp(Q3}AOIE*csmO)LQYw`3x!-?5`_CNH%KcrjepHB)XJUCwKcR6;ZvWUrCA#4 zK;Ay(Ebn#KQ47!RtFWPXh?<s6Q|qDQjHfjVq<~EsVoT~T!fS~743n@cM)q7QRpi@i zR$_2t=2yi9zQxG>N=BaQu_RPVu_`UgQ$zB17&OfNx=X8SU{EeEsy;l{2Yg-w*i*S> z7A5tnj)poc<wY|mG+pHuVxl$I;FsR*%6Jc}5~f(HD`!ttad2WvHUP6cJK#*9X)yWT z!PwWOIp!Y}w=5GIWUGwrm=d$ET%T(>ukyq7E4G<_BT2qLXY~s0^s~h>EBzYS6%}8@ z<9PXLsWXB5L}WfGD+y~h-D_<Hfd>N=mXwAJze|*^(kq=@=dcmB!%+47qQ<=W(28`e zS>1}B*4`@#JIq>B2h0p+f0+Brd=!RT76rb(&xF$oJ$Dh#6kt~bf@{fDK7cMJ!6>tz z#$&Z~n|E$|xr!e<;+>!hL;95KGPRc;N14Ncjz+?8q@P@uR51A|`*C<E0HQm(cVyEb zs(n%;I!b`2Z@Pehs%tGcL5nCrZbpYlyC343B0W&Jk3N(HO~5TV;E13>=+p%t8LW1l z@8D!5c_jr@`hbvwJQXe|nOb;ns&47jB*n7hXvy#q7U{4%R%!twT1Oq&$dyz8OB^x# zs7aiY0n69QvecI!qlg!L54XCmqQ6Nk?RPL@TXDn~n}w>zKi+0fXZNpkO#v3g&?kQc z_hzuRk2d7IWFN(dGxqx3$E+gN6j|LV^pxGMJcH(1r9qN%B@A<3pO#KQTRNoE@^fWp z7VY9~8(LTtiTnb2lRVQ;WYMYO^`2xuQjj=8+J*QAIMsQ)1dZ{`f|Z`4dJZ$Yj;fE= zP}fEn+rUOxl@o4**1-nC=hY7kU&qVS?e%Cjg_F?UgKZK0*gQiD0XbBHPu|VRxBgy8 z=|^gH_^L0xD=SR|xbMKFo}b&&w2^pe@V!F7Zx6W?<AN0~aX5HHMCa$SvBYhV%l2BV zYROs0P+(?>+eY8%j_X#$k>75dVe4Nk7f>(&+5-bO!w_}0uGOB0Pfu2Z9`mm<Bn(dt z;ky+u`qz_;Q=NZEi80&bxDDP$Sq2aIDp~Pl>^sweO@SN6KP3Scg+!5<C3>N7)QhLj z3Mm2oe5%qYh^n`)X1vnsPJVxXz3^0jcfCH$^V20fWFB#^OqSvz{{oM3+cglfs~r6W z&qSwzGQ4Ezhm*9=>Pqog+}m2tkE+4M8KUAQK2$M3zNA{ywe0J-1Bz--|DD2N&VG42 zt7I81%d-tQx%GJ;Y;Dhv*xy8HbySC8zq_<qL{xWzwk<rZ<319W)D;)LLoOBG_fTop zh*cyDegLIV1y7{&K7G5R@K!R@D+eGZGmYxNwCGBBqaXelXK3kAI~KZXFQkzi8WEoQ zWOO-f$I#xt30U#5XA@m@2mCSP_i?C0LR&TFFU|4ww#<fx-S%I2qn{8Xh`EU_8<E?X z?Xe#so9crmd1j&XdMe2Xh{Fywg*3Ff3o#aqCAbUmFIomfxsSjPEc)<)Q@F(GV!U50 z+V*BdCwfbALU~FL3TBlN*y#_&VLx`AQmsgv3s;{(D9s6Djn5DP<q3Lf9TYd<xXs~T zD$*DhrkUo5oEY`yG#-#(NB$n?{+kt-qdW=cg&X;*On6<MX#l<R*UTg9z{HuSTCsjM zQ^pk)epmjJK!bFLUZhCwwod5~YpF$DZn{f^5~0pIpAyoLlZ~Xx^h>XoPQg5bPdyyA zHlN0b*0cq)n~M!Nc+L5t{2Zl}vJ|!!E9dpc%bMq|#%}V^68e&99;=b@&56kS+8O<h zPJqwp9Jj}Y!*L8udNo&<b%)Qgo5@6lXQ4_QLj#zOc9*tfQT=Y)(gFp~zlu8*2(fRH z?I*ab)eJp4W!_f+fWHrtSYHg;Lr6UFr06p5KO~yyFJgH;983S21O5(C2SHmOtn5lH z-JH*6MIm_I@5H?t+nlVHw(CWN0y7xWGXDZ0CNI?y7_T~FQLMPovH*jTN}27ljm01@ zuOP}0SS<yQt_n)Q)QXbd4C^Y{6a6daVi25!Q6-!Ki{9RtsRTo^LN?tj!O~cKgvB55 zMv@%+OCfEmTR{eIvZ&0jtX@+jgdb6~D7+{aq4<OHkwzR5D#9f;V8#WP`4+t$-;k)Y z^=3;vNf-#dBDFnF20mK(z!>OphYoikVtX&(4VVqypiC_B4D=ch>8yZ<g@Rxbr@+!H zX^N#NTv-_}yAlCKh*BzIz8$GRdQom{2)Z`NMQJX`(%HNJG!+eAYKY`nPmqtpBp#)a zL?#B!%IY%awc;|E2Jk96+BAbx{obNg)u3HRb6{~H^zNFo-PoDwRjefBF{D#~*tlrq z#<LDeo^WDxJ}Jqf3eg|ddTv(2av75i4W=a(>>_8eS!I*ln%UwUqaYHL*6&FH;Icmu zcCWGiqy2}vP&4_L2r6U~UiH>2>|%Lp6aB7hZ0#HV*RoDtD4bUUgpCZ^wbuFe2N3)x zALNZ<>JBU6ht=;N+F?{AHR~(SJH8a5Huis2=znUoKNKh<^S_#3+5c`e{U-{Y<5M^O zOQHWM2mcd={x3=XC*%K$B>(UFLOv8ZGXck^&iy$4XB)}?CeJzkP`-aH=^vt&<5LFz zTgxp$c{p~v4t4dKx>8@sz&K{Zjw8hpsc@^rMpZ#Yzgk<)JVcL2Ki1ZbXP`a7YLEjR zfHH#Fw$ODF0b-@U(ds4FQ?|Vb_HBw@Rt2%RO7UH`_32s>l|M$f*!}mg@;-kZqypML zzq)bl>5S7dT@KXjUGH=K)jKC^Gx#m-knJun+-z~_b7Oj1j+gYi%yN6_dA%v}0g+OL zHWrit0`NLJ*s6pA?tP0?uSujrX(qv%6MliT0<vXbJXx@;B$Y`~L8xw^P4+f3<IG7a zGn)!Re~J@r-c>h&tMu}C{0_EK7!gFbG98E_Y3pg2Z*0`AIwxt=u5z8lYSX$T@BFs7 z%4}&Df>zjr;)<C(p?PXQf4VVs85-Z<alcNgDAy9jJM--Jj%N#NIsI<Xydh`9tIGxz z4Y$@e%F^8@GbXyi^bF@__r3&rnYW5P{BLY8dwv?OZ(}7R*QG;a`D_|O--WUT3zI<^ z?hYu2-oJB<!}6=r*hGX%X$R9hhju|l_&jw&7YizYBdA$bdbS&L<EMgFppE1*k)mJp z#OnaI_UZ5@YAEqK3%}o9&Di(f4VzA-K~0CM_*V{;S0l{2jvOzH*hLUHqm#xf=Eo*1 z`<kS=F5+n>diehsrAU4IR<$!`$u>7VbK~X&k(DN6-g{o&J%@vz$vZ#G4VXErzQ(c? z&lBQDBB^+2LiMl#FRiIx8DoBMcGPITG$=Zo(9!SiqWKLlf37xF<4cGS5El9_6!@WY zJMMDzA?{D$vkBRX$jn<o&7#0n0i1P{C!H6}1@^gy(AMdb-~?*gSAs}>#%XTWS&1;L zkj<&#derBDiv9NYhi*`BHd$i88Ix!to8)lh6oR|<_i_^Nx%t+#Ho1I$pPQsWkqV`U z_ug#ga1^#N#RdOg51>vFO1+edTP7=c>zb*N&3*PjsW_^UN&XQi2O21g7iz?Q*bgfi zOO1#bBY;VG52tbOH<$hWKi~zRMmQf#8?{Ul<{Xr)S^x{W_2R%NV3kyCIB}1wu$s0Z z7tv&oKyLez#mRR>ovN4jyS8zzh`J>rJE6^GjiY-GVFirKqAcVH&km|=qi=`ZONT>6 z<;O4qF@iEV=DN9n-6d-k-xRA0aeiM6JVh){14W}0g}K;(b^yGRVik)k6R3vg4Oi<5 z`JOl-!r8@76DcI&=pNV10wB$<yr?24a*1+=Rue3uM8dew3kp_}9UA4Dg2cO$if{G! z-_Qy)Z?=Osi~tP}LPEteQ^UEHiH(36xW&Fufb_ZYM6@n^_pDG4M;<6>W@qgl?Am$M zYvtmnV<;kIN#83VKWw(<aNCiIK7J6B^lKZF7B0iylVBnZg-j3l*loSRmbQl_MjN32 zpp{^`ry;h5)+ro0Zi@U}s7tGkU#_~qpBHG<mOzhvYd+k`VOBy|r}0%i%jQ6Au+UbN zFP``UU)B~#tR<QT`94j0h?PHbnGD`k?pj6!p-f|%xYF2lPbaRZ!Sn(a&Z2vgq5&r@ zaPF3j7$mOLx3COmqx!e^@RBT!D|;&PlQ8*VYfhYmJsQdqsp0f#b=-_mj!h|~2B)a_ zMm4H|fkFL3(o|TbK><TPa_REsK#3ROlMA{_voCpcZm@=0oA4$Fey${sIMdGKj-i^k z=U3|8*^iwedA6DFV$3r(=a9}gMH%*09alTkEYQb!{E88`6f=^-O}?}mO6a*P--}+d zd1Nu2k4}X);h?Kj^3#!Z4}}g*Bj#Lc8H(rQ-F$tU5?0^v3)U#p@i^~w(XsMTw|^*k zbT#j*wpC8f{~B)A+?8FYsydvese{-v<KB5kBOdBigQ}}P6ovc5Yhp~p(h1Sp4(Y(L zwd92q7u^`98u=)-Pc;^*9oL9CB(VSPxpzOGk)J=r47F;I1eX8X`7J2tSnBIBb##OH zz+iMBKL*pj?VP&7#aaAMg}}6=tW>z$%Q26B&N+S*;iSxK!`sna=td4=D}4IYuHvVV zIgdEhT$129cT*qtQdCQI#W&=>m=gZ2MbEnt3U><;VK_tMkvViuYZB{eBP=G3m+&&7 zh=bvk&TO&%ohkcib)9^MIMr#^nhb{qudl?`=)D6-OvQn22RQt|wO!#oE7grz!=`0P zkN)jSV?l;q)-%!j2=*F_b`Q)%jL{3>2OuoY)Ea5Qv<&)jOpr}vY!P2al(i<<%21k| zytGBNi^D*E;3s>Hza+mAc>G4WK}9!i<bx?5`4#{UuWe3Vpu-4^y7u*iFCj3K7k0|# z+loW4KJT2ck~*DRO4m;8Y`phSUO-LMAeG6^VkRTe`XPy4zOj2BVkQ32n*BY=5XrW8 z=a+Ex^eYolcV=Ly;@8t{@bASqrNCki0fA@mY4pCyW+VkSrNPVs9l=flsY%lyqeliV z`JkCt+i(PMlCb`qPCO*deUtjg#v#~ELl&%b;9u@^vUd&j+0h3VX=AjWnplymaw27o zBWdmuLXy*#)KtZC*je1-f$XsC%IxKCGQU0><&b64!QAh0j9swLK!{T(JJ51`+iyw> z2v*PaBjIsds66-JdhSy?PmHZ)hW<?-CaIP?H_WH&ISqSu1#Op1ITcx4TZ=2H{p&a< z(cJO2>dXoK>va7SV0jz?Y@9eb(5BbiF7wT9O^Ul{Jjx|s&soqm$QvyAHCTTI?YFoH zBf<FQx<a(od7eXv`(NMZ`#8-trCKG3No|QvgTyo6U5n<G!icdTKw=INMc<&-GslQm z?#Nq&i4rxmD!)}CLnCfQw>x|GcK?WPnpbjoe0utVunjSu|BHDk69Z$*BVmv^0^uU+ zeO~bJG%4oWwhjtMcV*4sbnQk=*hAJEc&SWK+JBXFe=3weB^?6`^IxVb#~)taUsC2j zl63#*5&fs>`llHBA4<AE7=NAg|7hFq|946EcWL{Vr2F(h|E;9UP#Uyc5&o#6Qa=p< zp0Ez?VG)4S`JNC2leppF=MrSImt*#=jPzD}zev522kjhL4VDt6)fk(@!{E-G{5+by zE7|jFK*IO;YoECN7Vn%pyOmMoPaH>r;>2$MxPXc{rtuw&3Pokm?dL@|MyWsxqS9Q= zPZ512SHdhwab6n(oCYEZ?n6!=<;1PQtF?fM-SWz;c6ZlCZnsv1Vcy<FSw7!m@!g=c zB3o5(DGEI{BpnQKw#)LbnC*~mkCueo-xtuKFPoL$zQx{22l0~-@8a`YXn2Sgo|7=z zA~_3*df~G$xA8Q$0GP@)x!(>y3UJ@$E>vfI@eeDn-?yq0iF5`11>?&XXvrR;nD<dR z3<$;@{zWL)Y3P<0)>jrZR`$d?y0$;N<X$*CMAO{#&|oJ!SFWj?N8<rK;#0|M)cEUZ zYmIxb*>a-hq`77NLX_s@npNv-F8ojbRTR!sL!ENVqdGRC)VelGdk$z!PCr1|rwG=2 z<%GwBvu4dCFb!CHGEc3W)CzqJA{X|EI!&(e=cH~}5*I+AiGqm*0cfw=wBL-!Qc9(W z82E>bqtIR4nk0qSp(CtaiXBo?&%qm8S+qyO^%>lNrRW0#;@-0YLsnI7%Lka_9$zss zvY>5|O4qjxoMZr;z2E@6%8eRc3-IPpnAY%HM~9c2D6GRa)=!f&EbQ;v2Abc30&W$e z`m1gfXn?>$L0A{An_lmw7I|o1Na90~U4OZ7E1b^3jzfaz6wC;OQzUTjrkHP@mXV!D zEpzmTTvv{jW{&VTJOXm*qA+QBx!m0Z#7L#E9C09x$na1_A_wM2KU2=g<ChVX4m09k zZnEzm#B4DNy^W2ZvAXo}OAl4s1tZ+SH2#=3lO!E7ltk^Tma8$jDm}fz!YXLMF;Z}} zBzYl;u6nP;D#>XfR0j-?UPoXFScoF(p?+)oNTTumA8<4mzTh&lfRWXupi!?HSghTc zUplk)QNfjku(Ys$6U(pc1C1+V3G!KE6<S)KvoP`3Tpp)ew=JG9`T5Mn4Tw2&`ushf zGK9_?q<Ro~Rg-Qbv7ZO@(UznZa`6c|xG<GyuwGCmn!5`$Lf9jY5wqHZ%MfrviF)K4 zBaA*$5jbAD35v&DVC_VKaR@W{Mb;Ejx;87*FG3zKAfB;I{n2QpdX|Y2dc-@ey)CR2 zi@Ep2qN*-;1(V`;Cj9HyoFU%uv_ub!_jIN1!sCEO79U^t375I?+#%-WGJ9e`fwkYp z(q8r+!zA{yrr=P(1N=O>Up4a%s>9gIGAScD@pJiqw~M=@ko3gY9p_kqNu~LTzvIN? ze9ZP8IUI;e;WP#<*^@1-9n9-S$}`y_L`_AK%dzj$=Itx2F5=}`EB*1ubRk`E!?DKL zQK^BWqifd=ZTiMw2+RH$!9pdqwTC7dT0=(bO&zn!y^ymU-o3dEd^ec9%)76#M$mBD z1~L`9CaXhEIN>2PP9`(ySYv~pc=+ewhk*Gkd%%NI@v8vywZ^D~N0;9Uix=WFF;1yD zP&aR!$GM06Jqk}}x;)>aUc@M63yj-)qV2F=p7PvsqZEP{afol79-UoVtaxagJ6AUX zAHxScfBO_}oGt`=Fr8e*22X4$mLEt8ijICIuc+U)UD6vfr+9so{Q(SIVXTZ-Y(ImQ zV|PD;4TPTYhKkC&ZpJcsGBbRjrFxs~IMS=^gw3Rva=aaAkOvwOcdtlXR9~(ncqd71 zbVOb=WtIOnBP_omii(@h!>zWbpjm)S%ve^;bOsafF)4(SE96836p!bD5aS`equ&!8 zuP}`7ajJjgY}<&y(>T&a&l9Yq_Zzy>J8;GqJMRA`vi((LVqoH6`e$VOpOzCj{-5PU zj?Yrqzkk2wZ)U|GjDHu|{&-F0&sSOg<}dueRRS64|L`LIBK{*VG0=b3@&0{eQXY0p z6G2$LMs5AJAHK}I${d|R%)1qmRkf$jkO`VtISK=W8I9~U@iON=EPpGmEt<F;7YD*3 z?=V}we0xA{E14+*;6(y0njV-xIOrrB<6Tfp!n;8#SAiL_`4!r@hcsnub}1bqr9)2C za-jC!ML;%nNV>#nPbNQ{43U@-mJ*tXF(vMG5E}SroZD7ZJC3?!WLw0*j+HGlVYUzC zPyozr;vS~M1&IBCf3>``AZ{GpdSql-pieDhsL4pLuvz^n>QHnjI}kIwN9}Z04{r-j z3<bg4eHhcVy)`=#j-n@aOe?)qu$QY16J`ubBHY**+|BgP&6V!X;XODyK@L7|6olXT z8~azkpX~!XPCeRQD~ya5fL?(jQ_{8%N#epwhpc&oj4>j`JBQTf>;`BlUls}|4J51! zwNVM+fk0@zhO-id;>OD+4Q%H1C1dJ=;~E(klN-i-NpgJ2NuWp*h!}-!1%18T$bN+t z#1{C#q@c2bnd-p9aC5W+;b6)!R8m;(<kk|27{ln#Gpnj>av-%C^a^iYmu~Zf%<qoQ z5y2YcEK`)%X<mA1%m8ZpgF6xfeD4+1^yqpZp}aglYBFmkQ=<tz0z?NU*cFs30sv#U zG`bD;N4)fsH^>+z2!DzzOC=P#JT?fM3^00lYO_2+7n<FbOh~x9r>R(4@njosDtIu_ zT76Q`IMs-PFCTJ^VN+3>QE=m`CR)u)48%=<R#av>G5AKJEQ&kIx{sdS7A%+=?CcHA zc;CYw0bZxMv8STqyVOb##l1`7eTUZ-EHV)vvI*Aq^VG0eQD&LqW%q*JHSR8v9_Jvd zGAaWjC*b2EbjGB{qnKf6+kz4EbC8?E&87Je-wjpyT#=F|ZoKrMH@$vB5exxIdIKuv zYtW)OOqc5hg_vb5&V5gu;v>Pok%6vwbav-{v>Y9T&Q8r`NQDX_wF)ivc!U`Qc1~pb zg3v>2Iz=p*88$+T)kamcK~ZGq%{$hfx%i__8hz!yt{I<Rq|<f-(B@<*d?i#v)j1e9 z@Btu}2*nP)redxKSKE2H6Cm&<b$Jck46~mT-_9@i43#@;y3$FP^%nA1qJ^wCrjGhJ zx)7wiw5DgiuMMJ)$sv`pGU+;Y8H>(|PX`SwHHnr``DFr!c8`}~6|CAhUn1tt=Dh(e zULP}xtw@wpT4{h%n{Q(>PoF^nHFWCp2qg)l#Zyy+D{cw@FKmeT4dzLp%y!8R6x^35 z+Zd&NC1vKdxMG)R*)j9=yRh3?h>AxyMomx<D8K-!dptFP>DpA*?rGeLaxAW~TWXzE zO29kgFv6jle0R1hNuKik>z35rnhXMgFR*&XZW^?3l0lA~{q~s(xF{oRsU-1HL|FV# zHmwPVL{>+QrKo=4rC6{q_8wLlhnuh(5}7F@r;oVyw5c*t(eZ-%S)yx>tGh>Bub#!6 z1oG`5AnK6LG<bMl!WS?{!W3D^6-Ue4oiIxx@r3SaA5V>yz6h?nNaXp$!(;cD=RV`e z&l=vl7^z0L4NI%qQwQMlcSs~ojj56=1lT%D)|8ZJ^tRd^@X*wN5zTU0ODDk^H1&54 z`pt8&*Y?|haLloP>}r`N2;FP4Oq*ee2BN5N9z1qMOD$H|;8c_phPhL6_jQ3EGpPa$ zSkMXiYTtOjvm1l&YdGeeFJ5>G8DPuDq#(Ph06`FiUcCMC+aWi*J6-7t-DJ!lJIem; zL#g4k+aU`25r%FCWslY#9?+0C_&(tN;kxO4z&V14uiZ`(+TO5!OQ`K<q&i94xh7me z5SEp>3EM}%%cz;83BGI4?L11h6)oqMrX^`|S;TzPn%SYs>G#8o;7<pg`2pxc2I__J zyWTnr!0BV^%omt+kqt(EC<kP1yvY!R^kDOPl*XP1>Fh~HfaQr9_nq6yNnn=98Fq1K zoxPb`*!Vk?5v&s;$urNBCeR7>e*5}b8mMB4ZKWZiypf->(5T^0h{}a;L#JrMkg&Ri zGtyw5rO)xN9vz!DF5d}*PUxzepMH+q+rZ^Q;ahO5ka>sk-bi*o2m|6or&Zm9s9umk zSgInWcKDm;?f!Dx$bd_3x;$Kk6$1K&g#;kgKfnA1P^|+OT8BPQgaSi~uSWov%&nA} zUQDzDO{ZD`U=?e^xb;#POK1J$Xoczr-!Df&zR2reqkfSWfm)W_shzt6D2rB1dUWr# zja;I`y<n1cVyHjm=G%U2@Vql;(~(n&v2EjRYyiU~=7hWPm7Q&|I-w(?%XbLHdY7`J z!PP|2(NtBugFEZS>HXJO{~7!K2#5@9ObmYodj|SHDh+>y$^RseWuX6TU-?&U;xp9# z&obFh%3tUG|09)UC19ZcM+z?Q!uIjuv*G4LKd^j!_(u`qU%~?Fe=A%t(Ep{B{??Ae zK>r{1<CLo{M=lQ_evBr$kO3|;xeKV*NA9?Ug*#bfF^t6+*zuXO=2jQar4c)ZiMG0} z-;rN;mXWIm@vIcqS5i_?Qpn<5eM0kuX%Ap{`xzdj(%CJE`bMlC;ME8lRwUw5f|nX4 zyCZ{ELm%ft0*$yv+r3}^gGPVzRPbWl`p!{qT`OSRIWee4Y1F;GOmW#(jjgowLTmF> zt9!m)2PcFGzFkkYfJ6{uF9FH6#=0&4+}D6&8Yc^F6L!Y!t=eF-rT6)!A|uEIu{S)x zBvS~}C}-rGytZ(rkV{PT8$7eHwgkfTGd*b#WwAVRY80~>O&*bG1S+KeI*)&)t`uv& zjrl3zEN~GIDfyRdfbgj`dRxOu1?OjM74zFDVhV&o@3*WOs<3a0J4mcg`aI`@+`3w0 zc{G$8!tMFvk~J3fX|}kvQ6%vva-nT4GMb^?X-TatV%HSk-IbG$9iU8V!{={h9l}yY z5UX%%j)kBiqdHJ1k#+izBob1kJ5U$}&GkcIgR;}Q!?P)o#>l@v2x1;*5k-GPJTO4E ze5^<-jV9kb;S#d-rEoTNyxVU1MsaLaS{l7s?sfcL@@T210s1s~hX`-c;f5U2StL&; z42sM#RZMTC$lG?ps^l}!waB27E}F~`JDV~+#&+v^EbnKdI!HHLvJ@}9^wLVx-i4B^ z_Npvj{(5J5r);sp1)pYULYi=wFm8<=czP5t<Z-p&(OSH>Y<O+&bQ?5=%!+u2lu{FH zph1*3uoD9F6cRq0C4otk-y={kNDmk@FkB%GVoH?cw`e4L5%pBz8CiPHMM@rrqdJ%z zD&!rzAL~vvOgT-iaAqq9%|d7#5Lxr$ed&V7Q1(@QBf+2DwFpr}na}DAXAx-}4V82R znLcj~V=JB>QbKqR6?QL}8S==Awp<Z>@&FRvPoYF9krKt-{a%B+U5a;=BakS?O&nC{ z6H`c_iFPP662a9Hjf)qB4P|92FV9J@>M(@w3j?|!-GD!APpWA;*BFBn<drfOJ!+0Q zrpy(=f&KSkeyWD=EK)eWUH5^1pELx0=j{QfbB@>Nt}zWKI=y;8P|t;9Ab<EcAc}10 zC}6#5xs_L=li?VLhmD~>xxAG3ZCYT@)FbLN4PGek8<;`&dbf}9#H_mkv7WTL+XO%y z7+dI_)0Pp8kLqX)v!3wDMtAXPg0(k{#}H-QoD7kZzK%agTBw~$Cy10Hr)HO?ze<N9 z_sP-CPKFe{Hf-$iJb~fS=mx=H%%IsY>Issl>>L^~VGoVkZE6!~rwP%Q;xm_yHdk$U z;1fp>?HVg$k)z<-vxz^SwN+alT19K<cexN5AYs4?8#MKFnvb);QUw?zhe-+jnaGI! zIIeQ^B>{9pI!5kCHuy51oWpZVISMSiD-%tg64G695SH`Y<Dvd>vPd3D&cnshKGCRI zRwrgrq-MG28Ku4cCd@(d)lAb$iDv(FJnqUqh6As!DrJR(R~-}_C-N*UruMR#C&FPz zT|v!jpelbk8?;8Vy*bF*EV21@oUYj>(kQ&^s&l&r)}8f?_D^oGlg_CU&1ELvy0B~| zxqA};vBl(W@_-PbX^W9Ixf*~T-e3X@(f8=a0-GO<-@*now#Q^QM&#Mf3gtB=vH%43 zX*j5(7?;)3I+ONey3ajDlLX-c_t?-xCW!BF*C#O*RjRo&qHlQlWi#L*2-_2Bmw!wk zS<r)b9lb1A{aSLp-DNZ$-$r?viNAcW0(3<(HUwzwSQk_~?PCNPbp!1XZW?CUX@YaS zAO&QwV$HAsWM>z!ad7mf1b}o*WRAn_WZV*WwOc?G;q{qu5Q`87*><=_^ClT2h8waR z$^1R57z0iKXVl{d666w%v6M#v`_raDNY3VdR<KdcJj8UvgkCK#&1eIyIIFABnl&4( zs`;c;Od<@xfhC>aI36q#0QQ9m@!Hx^u3|6rfZ)vNSp<0^Ke^jOHQw-zZgxxbYhNiT zBGJv-zA1~BdSY4ygxWI#?1KVDXYh-qajrr{eM#0qBf)E}34)#xq~Wfq+PImyV#1c( zZ_b~WK8_`mFl&?X5!Z+y4#ge|xAH;;ST@n%+brsdL!7b~HMSuDUe(2k9n#-iRMg^K zhoFQ)A(I~~m#OMHmFv~%sJ6UkRVj%YUPD$$2dt>S;^oyvEpf*NJfeJLL>-Vr#<9&F zs%AnL!fIdmXeWo_NVu6fw<fDw*q#L1@WQQzdj%e`D}JT9)>_=A4qH2F2xxG7HbHsc z8@j4oyjGj69=HfH!V5Dr%4WyrY#F;2>8-cTyV~mZ{?5D^OwHp_x`@><f67%{%l|=u z4Qv~z3j3t1S*KtRlqU>P2S93EY@=b%oacB12dx;>T}fDM5o5X`WV;IIW?gBp@1wNT zs3lw9?Mmw6q%2gXc3kk}Zy9t8<#-oiw($li@W(fc1#5`M{zW&C9XgdJD)ss_tbC&B z88wZnP-Aq5r^jZ^@LdbjIi6HRDf$$Ps_lp%c5cUzaPpO@+WtyYqoZanF}{i6*I{43 zcM^p#emH@ygw$Ou*#qPwJc7Ig<niYq7lX%q2lsp3VdCloorQVXl8`*#dqHrh<M?#J z<y(wXSWd}z{CDi^X{2e8ZM0J!Nu^L=dgQ>gH`rcbKv@tWaW<0t9i$cvsHm3Uxx%$B zIH(E;bTHFYBaY|W)!#F<c6xydy@a?)A7D@oV1Ourr>?P|FO~Xj9E;MaP;|c6WN`MZ z1YK_Suf^p1T?T!3=HvLmXH{jV&0QtWha*+Y5PsjKb8>(bPJtU3C6guev6(E)4|-tu zUtmcn=)EGUK`T~k9?n8V;+q)2Y5P&PXcG}|tf-;9K&2QI+O(a>4=7}n1;${eN;79d zB!XFn(F?`jpxj1Zy`hc4Ap3|MzW2EG?C2oqgTW)rsMu!!^LvVFBG_Z~RWf%YK1(fh ze;M6KvS)BOjf8)-9p~r2dzhd*6gXOvA@D3ViYGn#5dy^ED*>z{Xfazh;cm=nQ9-i^ z;DoBJoY>{zdU*grGU2UBqN=fVv19KVQ=r`;Ayq7+(q#ejgC(b$B;;9OyW$MR0XN?A zULIbm76gUZ7!(DC>Pu(1vxkPhW4Y<;K?Zz#Tg|#G$eH5p%8I~L#oB1)iIdV@MxUq} zhY9kw@z=9o6TC~F*3}R6ILo0jryQk#aSMpL4x8R*%8TCCutPCRcGedhu*q7m=Q5ox zA3rNc&ArqPTxs7ovMfq&py%UBwB4GFcaxZH<Tq&2&FL)0chQ$|QQ7y_`CzPTQRryR z`if7GriozKIxgPgfMJfwLC+L6y+=mr$A<Y`2&EzoMd0{j+mguXR1i;)Iv~l)anI*? zSewtfq`ya$4cE=~By|&VnjqTpTC*FxW#{48EsNoDE`YZ-t2VR?&8P4cE(U2-6)Bz0 zO$ac<uBwGcN+(dr0C83O!}pUOCH*1_s4<jWkvFTQZeEtVpRFrH&#=Lr%%crA87^9* z1{yJ0HLVjzG|D$0?^$#BZ9Cbt^;2xK3Mv4anqo%88<3lGdk(b{b&S!XvktVR+19=@ zg;Q`*R)r(BJg<OD?jDV|!wBF7XLv_pL}AQk!L<n2Aw8uAQZ#B-S27{6D1(1ax<Y&P z=*pat!GbIe5%(qwM@LpgyVv!nE?vxXZ2*!A3FH|d$#gOAEG2{wb*%tKXyI~fGj~&k zT69um);AnFx3uki_1}HZ_q6NtcR;zL^XDwM3r8*4VM)NWzYk+0;lNVq(rit%)+Q%o zUbjzg*H6ATfmd3j1`xSwcpMr*;kwIWPg;?%<F3h-G7ZFc3h4<MNNFPM3HdPP+lwS@ zo^0+G{>Tq$G7TTMT5ET9+ir7yH+^tIamYM%NH=`1<jll#H0;6D>DM)vnbu*J4+V-U z$j&nfpuD%dlCs3axfgIwJrB?bK#(?~ye&{U)C0xEW{=L6i?x-T*5Ct2)o|$9{SgG4 zH0tr1SK6sTG46C)r!t2ddV;#UhB}X22MMPt)FHENu_f6mYXqCaWkvvUOt;^PsZlwG zU;3zYpTxnNLs8e9vWQ^9#<dyk-7nN*O*tA^+nBoXYxV9N;ACFJJP2pE6VOrZ-6o=; z>x<fIwevA^#16Z!#Db>4gmxOjkg7=aA_pb(Xc@%=zselH)Pm+>-B`9ZXl`yauIB6l zu=eXmb8H8MTphoGcK4Si|7GeGOE@l~!1YONDFX!lWe-EJH-n9i8h2ix_01OPLJ7Jf z6e-#M$UB`RlveuR@NSjGNrcNKv-@o*Q|*KaC&$1vTeC^f=0-(mf<JE!oS$SwofuY! znB#7V^wyzqJXkz`O~r{RL66|EKG+08%v#}rwL5vkcdB3iK7T$Vr;PJV!of5cA2CLr zEL^CBuYFbt#itzM`DoH<qmNpR^rS={0T5CE50wa9{<wSn;f%csF5};7de%Z;9<-jZ z6UzR(#Pu(4n2Cw)pY>M;`o9+*|D(k9&q?YZY3e^Ku%B7o|1fd==REfR<8QkBtp@w| zR=WSi+&c!>wr>Brv2EM7Gh@!!wrA{Q#<p$Swrz99$&78EtaaAfxOZ3m_o+I!>V8QN z-qA--Y9Q~^TKhf!wsHN}37G$8-}+}H{NLN`zbQE5f7x<Z_@(7sXG7|`RlO?Vgb_mW zupTUxd=qy=HqCTZkWkJ57TcY$r45CWFg{E0HiF|c{T%ax%@L3*5O-_#U?BD7=j*b* z`{Cu#?c(j=`S#s!M*4QfO7fB1<<iS(@3&4?K<Jx1pD59-0-x<X#ciHe3p@8?eLB1^ zb_=`96#d1?Y0;Wi>#}}YYq{zDOAGa*_N?;#$JqN~=X3Gv+t594tmy?Y9PIwo3e2G> z;)C?YeN>l&vvq=z>sp%)|E8(zdXILqD>H#b2Locmcchl{v=JB}`sh8m^P+6?>7GZA zST4563rW{L=&E1o-pI73I&g0z;qd`3dUUwEbD+u7V^t+hg*$f-W3ZksP8ChR%bhwu zUq4pNHWT?zfF<$*DR4-`d?Dg^uIeb3iEM+3YE<0?vf3rmeoEl!Hwv{8*HSXxF(E6M zXowmr7p0QV4M;QX1Y&Z;r%j+lmwQVns7t*c3naM{!By-PE{F}w`jL+Wq6cH*m!if? zT=BbG+37~fMu=P(^J`6C4TQq!ulOO;M%_FqsFUeY0S)wk6}VwC({wF7-Y_ighO^Qy zF9#QOlyCBJqHOdh=JzZqniZJg@)QoZQI^Fwq6BhbZdW{5saiIJxiQ8-T6}oPI!oYR zoCM*IYVjJT$;jr$z5oTjV>7gXgacEGMM|uPoF_u{+U*};roWL(@=kSdM6z6VF5s76 z?7Von$k{K8Zf0BVf8$!i#F*~*wYaLW*ZHg2>0M@Y5i{gz@kf#~D1C|23@^a($@TE1 z&d0>GhV&U_cbnai3ALaH`k*xv0r6bVYX-pMU<4}V6z+lPu;8V!$jiVk%Q#(Q9>pCJ z;gmHmrBYKK#dV*pk+4E$d%t$Qk=V&m)(o;Khg-w7=*?Ly=KTqd+C=#>y_cR-=5leq zBlq0lKqM8&1b>0GLHM?CCNa3dgoE#OE>|rFl6-ZJ+bv3q8SREHk-}0Df=&|2{F0l? zGA~`~;qh#?LFUQX_sZMB@bv7Y#Zqaff+@;d5~tm7KBO@)EV$$^IGkkpvZHR*xyZTj zi=mBOg*k3kV3Z@0Qx`nugcVZ^SGzujoyex`mCgroPG$9<Vy8;Mx)xcdKZwlK^Rta| zW{i9Qj0l2|BeR-kKz|2Q4eqi?{iK~dVG-p>1o4MvLD9iGg0NL7^tu4G`&Kt&ulvh- z{M5>hV(HQ=*8SbdBlU{a0y$@|NCYTiSs5kbOiAAMKnohNt=y)2;2<=?=>8xPo6yni z1y+e#c)MzCZ%2+j^DShnkXof0<@Fw))z7j~NPQ#uwQ2Li>j0#-oeJ?~;0a$~+DQt2 zMa`<@Qoj{E`1=hok(==AtA+RU&$)AH5Wfg3C#$~V<A_kqsM`hTce8;jD@7LknoO9< zr9enTYuK=Lq1LJol~u4o8yi)sSc)c_>X+?FD4*4Iv-dmO8*NQK;G@|!ik5}U0f`Gp zKJMmQoV#6`n>tsDpv7z~*LD|8(T$Njn3ILoChpqSG@f+|0laVaWWy>@*k{!`7wz`Y zBEfe%i|kuBdmE@k70Qv(PqIVPH65r`@Cvw<p6yf*Ot1Oy@hyqtRF(xgax^xZI@jpZ zo@ZsAtL_b?3Ou3G&q%`|jwFVPA9{xM(OBepFvLDAcXd2?5<O7j&53>mU3YXo1-Fnf z8LFE$;(U~uiL8J?>f5?$2*V+vl_e^Iee(DI65>bWv@#y_qEmTuy10|rJWY$%w?^7F zdY6axU#{Ono|@SJF4uz4rn4w;e!nqnEg8g6iS_+uNzI$<$#K=`1q;(fQ~<Kx%dP;! zj6r`0L;4_0xYgz4HTtO&2ZNXJ?#(K;18uus&YwM)jGP_aU0xp>eVg(aZjeOCosS_x z$Bii7bFWAa>t%^u87Wnx!XysCZC2D;;&19qrxQ#j1K~i*8Fp`rrsho%8qV#g+URqD zHWg!V&uiZX=v^f=tCR)4ciL%M@1XXrsPpO&8pf$YE3W)h+lwQYE1R987d=7E$1`bN ze&!&!lZR3`fYugKa}0^_^6mH@AcTr}xA(h)47vx^bj)}!AfefBsXw9I%2GUfvFNAk zoQgMpuqo{k%)7(VkA(^hj5PW%0;|@*GzfN}C?(w$6afl_g=HJ375=R)ucI-Q7lY@Z zK7#igtH>r_p0OegU{*gb{rUWCuizbd*V+psJCTn~PY&_@xy!kj8nEhGP7%y5bi&Ib z$QWNpW!dbXk4p#Nj{71I5{?K#zz0|GPHi@MOlQFKhoYNNM@0g$o#qqHb2n`w0396% zcf?FlNjjwBOc3XZ7kgeJpdi<Z8(T4c5Mx)>Z%5q>AgfbozZNo*5yx3@?MMt5_e|n> zD%80LQ;@fq3OUZgc2sF7+54Wpzc;<Llt%9tOz9x9!6##t%vKs0nB^jEi-s?4z}js1 zM=Y8i8Ba#_j==p;z#x*6hefyMi6Os$0$;pJH1V1ef>?+}I<jp6Y=>xr6U{T!qYbGN zBk~}tgk>rRV1=g<7W4|k;!OOhJo~6njhPFP1wt;M1Yf}^&rmbB!aN8FAt-6Ded&B2 zzmfaWQvJyM!IW0`D|LaeUn1Vi&kXZ(s^tk`6icK)Xh<4LDj+>=Z~t09;is1w+fg+6 zkgrKONhh`DB7_{>i5vk)Olk6+n!`+H*$_zxP>Q7|RlF6nTbvmntEt3THN3PfX@1H) z^<<B;j)>4o(a~$c!ssl2MzB=?l0aYiECu#&$M^__Bc!{oxA>Tn;L4`nRoq>EXYZh1 z`}*!+@-yfA;i46r=*#@Gy(*z-$pXXn-z0QcHH4=#;Y+3r6;Bu^zyun63yqTd6!!HU zyYO3{lU=r0d{GvlQHJRZ(|X518{(Rd5d@<w!>sZ4$StRWEY)|fLUHH>EYyVM%0gN2 zKMV)5&;`W@#P2zE5)d>lycmLJJ|`WlU235}9TXeuY!`B$mIx}oHA3e<^v>=*^cNsR zZZ5705ei<u*s9pQ=AMt?daoq}+jk3~r**rQT;NwB-Y;kxtsi|q9ShI%U>gZV3&AI; zZs1@M1S`H(fxUXQ(;X8kaA=k68~9E{H;t8e6gKvfTk0|xAu1yi#Hapk)l3+dcP@l( z_n6|glt{qa!nR(%!~-5z4q?z;;Aunt*k34iQw*!Pjo;!EELAu}P|U*|3RPc;;2ylU zrw(O>1c>CDmLxCgXOY*Z?_7y=r-|5Xym^Iz?Mon-xg$aJrHvAAUXF6l(Zy@zG>Qm~ zGlB`cdC7NGGA@6xoAt%>M8j#0nDD}fUk0=Uw|x|L#OY%rFbZc-4N{bpdBPH;+rn8N zGU1VkjK??z_|mj$QCxZ<!W=8LGq7o3g7~d@$l&3FjA703aL)QXBJ9<odz2#<9U(un zd@r`<93@AD{q+O;cG`I|^Vnu+PGPs<<Qa?4l#*l;2#f(mqW0wqicFOs7T3&DZYH4u zr+-(eCEIS%*x5F*2qU!-g)*4*N0<Qv;Ih|A(j~g9+9<~cFwBEHnBa+)dezO9e6LT4 zp=v2@ym2;VBKJiU^-b2JFcZ&eYr{ppFG*QPdg1^>08H?cFWwQID9#VRj?kUTM)ZLx zv%}g@`heYezZe^8iw_CQmk()DFGMI5P=J=Sov1kw6kI{C!w}doj8Mk(3BySCz}X>^ zXPw!h7AMrQ-~o>fJPzmK(~)E-AOY!<%dH(o=AppS2=#FyDzdvt(Bb--C*+_L?+(Lc z<Ybp8yWl004Uxc0z0R7rcPv@DFcar%BP$R<NS?-%mdh`bc5joTmj@F5jc$ncMJ5=H z=3=zoPb44V7nU;2fjb$WVQU^8wL(-i$4rAFYPc|3IZt)5t?Q<r68Isd+&s>fPO>k8 z^7=}_Dl6~M^1Ob)&mbG^;Zt3Py27Y3ZOAtMrV(M{iMve13rrLBmKr0zeeqCjKqTuX z?{MLyO0LTcrs<5~80AA)dQynT#na?7I+?=qUjgUGHM>6QLaX*f^iOxD5zvzq`o4PM zBBC6mk4@sqjR<+k$EK$Jg`gp?6sqLU5^Qi})#1c$)AAQN%DM3G!i)7(V8IU@pL|Nb z)-0udEWZi36p(YsCe>_=Va1Mb`35^FYc>h-Owaf3K%M9qc~wk9sMM0wt>1++=f`?I z5%Nr8zc{}5h3^u2i{G}Eu{QUJv<dFMZ+<LIH*aUv2>E(BB}wc@EC0{LYwqJ~ArMVO z6JJ(=HIEf^I60>GZ(3|Pyxm*O1>w@?^16NYBnrq|&XaZ<u}M8CRd^E$ly4|XX^KHg zf|l+8Ph*3Y=z-0*={qk&xa|8WLNF|}OR!&2^Ei|~8TBN{=Vf)Px#kmdL)+wh3L*Ox zxM$x7;Atl@j~){gXRo(tj6q-=sp_{$`T>K>wrCOh^E{iqi@`}^LaR_V2}Sqkml;iX z5Mjpf(sOjLg>g|jn&jDtvTZ^+o(qr%9<>#7=8J~tj(C-4W51X2QU!5KbgxQbC%t7R z@6sCY+^i25ao`k?C*oJOLUmHk8+Ymu2^8ACJeHL{vP^d6NW`IQM637pmv*Af66eiS zn^Bb|(wrebg&y`_1f|y^*6gK}u7b%&v`}utimLehMxWr1Bs%+X+ziB0;GR3tC#Vu2 zKa)vvsW>Z}PEukH$9@X>s3xH_9vZA-3Mu^KQ4aqy)|ZOR$UmuVt1UU=0AdO?T8+md za%Rrc*S`KlY&<j^du5`dP!yvZc2`kDoql|ZGEC_00O%_tTjcY7V1`{HO4%Xp$(YsG zmiI$gNTA-Gh$BxV?bYyB87z303>%I2n-!FK@Es4}`&Tt+WTyeQH|#k7LB{CRETy#^ z-xyKlqX1T->SUWfEJ)PoOnRhrhdDBariKWK#nQ)gFe(WL8gKb1zF3yhl|yqpuwL}% zk)uDJaoIDqMBz20NFvr?4*gF%O3cYHUP(r2&^^`$1w)m2s8iQSv#6@T*TXYyG}q`I zScLrRp#Bj1#&Y&y+M9>fA!HvPHak3rr*aIJn8$oAjTv1)3e!5d$&?Cl0As+w0Er?> z=nDIw?MyrM=|tu`IE`B0QpCwcW=Ck9CAN|cILsQIv$et16mou8dPeWJIYWULK-Bik zB|1mv4~a%-y2Mi4LV1K#)7-Jfa6-2TC%mNiehH11c^s%0ZZf5<pw~M~Dl#7@^}?OG z!DE8h7bj-zHa*x9eN0r5)Uqmn&Is!^TfmVVYMi`I69vw8!Ecx=JGc=w;DFQRW(mDH zC6-`pNN=#cC$Ge!EJiGJpE&*&nos^1seJNh3&ch@5g4ro0h9%${wgUUa-uiy-hd;( zasOc$k@3%+{vX?a76!(Drepn?0{u4>_z&oW@h>mOzh+qfomu@)c=ESx?te_N{%4mT z<DX~w`=0-=R{j4Y4EZAt<zG<>$5#OMKg#}p<CcFf^#2)i{r46A1DO1`g?_EC;Ek_B z|FCKf6}_A>r~%kxr6rQ)k9Hj0rKp5674=vC*q8xUiJ1;HyASfuc>0w$NttO>f?xoN z(k3kt#FVUyEanYQ_n_Co&m-?^;<seRi$l`K<iyW<zQ=6cXJa)J9%T5%VG_DN9s{o| z6n%xEkHramEl<O5x7F62vR+pfoS50qhdlIe+r#b+smizaw!Ec>rXSTGi(7B#s$M8V z#yfm^3S@tj`=yaaCf{cxx^7Lp;!vMh?&$;FC)w3eZq&Mu!`b^G%Gx*K)YV_q)dS>v zc`31piLz^Jq!dXv&CPTI?6RI~SSRWfR@I@n-?wGUM=jqDnJ-V+nMSbsTycubt4x)a za5#wfj8ld~v*zNfX!jFr&!ZPD8Uk$8Pr&0v){9jFS?K!x25C_{%v3?Sfi|bdcxb{_ zPM|LzpBlvbGm^=lS*XVtf>7srgrDyg!yj?s66e)H;nfAt<yf=r%`9=;RVT|2jD&5a ziTTtP#_z98G>*Hwv!3@)EE=jt&v!HBd53Owc|(m(h4$|8c+VkjxO8OFznwk0;{hig ze`9ym7N5F-X~+B$jHsR8kB_5|rgozb+js4$thRKr<~ni*LP(Wvqy}htcfeuL-{G^z z-`}T3Vl4d_n&u5#Mcg;-n5apf{J4B{80rhD-#b%5Bp#1r2oGz@=5y<1?x6(A`gjf> zXF%rMpHNxG&H33bu<F{u+6c-ejayzy8dVLPWn!B3mcO;mE7{nYDWcztJ=?UkK0=Dw z*QuWU<%TVhaT}R88ejM7U#H*d^+^25Q(kK4`nZ2VV}?U#x)n<Eic^&rV&8K2fzNs* zAumfVQC{Nf5uN7@B9JxbprQuwNH3T{NI_mq3uGT96H8U2m#x{;&jgYh#d?(0ZVGF5 zL$2vt-~j1<XGssJV?x7g0x8Fv5XrE(33laYczT-Cf6E|!6J_PY-yTj|4>>TQvY45Q zo0zi-Tv=+Bvb6ymsU|yH+==O&$lP<OQg!awkxo3CDmNGXGr><i0pGO!f$O`x-U*Q! zaX03P;?lVbXRC7axvR@QbCk{GChEBaH1?@BKSS4rN{Rf5LQL<t#Jp4IluS6!KE=o6 zjqo>GI_5xE@IGQZv}=_F$j$Dj<9F2r*jFeFR(%Jjb!2D2cGmodYIG0xhesCxdTK=4 zK%j0N@TFq^aMbN)pz$4;2}-FPW(-;Rr}9|tnClYZoRK%Fsb=J{i{;b7NB>?UFU{zl zqOyZj_!Fdn{sS7E#rj@PR_$#{)FP$o59&9o#;5T%9Vt;hm@BOg_A0TRmNYR$*&zQ} z&xGncj)vSRb#{x4LQ*96T*ZnTA=~aO>BcR6hqKtppH@aAV)6kz=O{r&0tZ=i(j^ED zw&!#pKM@w8;O?xI@ZwSCc_7F48wyg0#0##SJKwK?$GSVs66aZG!)TX;lqQ)Pf7jbR z-8%@D>(Lw7pC1?s*4*^#46a!Rp}NfxP_FCuhO!OBvqbjM$hffe)Kl{FiJ|XI90Iib zHlDXJ^$P0lp8s8Wl1gav6(dF1{Q$IUdAn1gar`JM<VakAZ9U<0Z3I~)#&i%rS%&3p z)9kd~mlAB;puH=4{edPtM*LJT+rU9GQ%)0qLlQBI9y4H74q6*dY7_{=T#ETfm+F9s zVOp|b2xBcnsfi%G5oIPIymI;)@qlB6>&_{SRVt8EG+r!`O4!EMbnDDyH{g7(u%@Dk zLhE|_QJD{z`>T{}J-Zbr9@8dl)miWTnAMNd;E2;uZDTt*EY0W+oZ)>3Pfrz|#cQ&k z{dRQY-8~S4QmhMiYB>IRgLqyp!{|smH%$Wfgy>f|35%8&9C<<02v4{%?`FgjbpdAU zAee-F(gD9%i*<wuBFDppwdzu4zt+hZ#on1rfe|s%*@uyT0BW-N@8T*-;j-1Jp1co0 zqn}C7;mc;ibL}{*8z{{0A(L()OBpymc!xVa(lKpy9^9!~U2fm29@copBUgSK<$b#F z9}qWpr&;kC+@|D5->ReGY!wN;1T^O9QQ`4QEj{is$Fm>^;CjN#!;GUlR@PPhre+CN z9oAcxCmxc=LZdYGT9<7zxdru5rO11^3jeJ!1zt0euTZAHicbT;EuXo66rPt7-@9hA z(_K|oD5FKO&9cr3FhR*78hh9k_e&?in!$xAC-MLXP@WiB?>N{#%%t(S6q4pKnTqO# z46`j|70n7NtNUdI@b{8q`^MP?YwT?!Z?NTqM;qhuC#ridFhvgf(C?qfSXfkdYzxR8 z83wZtLtx*LF{XK9K%ao+p%;|rK#rxj7G@HGro>ya2XeGH@?x(H%ADX_g4)8rXUwvu zh)ATEtl~jlp0X~AF@1Fs^^JHY6!N=J(!B-b)`n%7AfI_%;nNPcQb42`C(NoO`n!BJ z5;1qn)dm2^^_tad`8|w`RS4&4S)nbIiFqJSTV)H(YGFB2Yd+k)yy(y{d6pwrC1_5g z#Z%T&Qi4k;c?`s;qNCqv(zl=)_g+vF-HrL45*jgNxNImNX#``Wk}z!7Rg#&5C=k=6 z!5zA|R|Suvu=)gTcqJ18Y*U1hrU4F%i>Nk#)8o;ip;8c7szg>?-?UyzF9d9X{6@5S z(tN7<Jjx92lHh=UK}{^u8r^06Q)6;j4b~lep88}70|L!Li&lrJ@TsB+3vPHO7V2%s zLn(4c%yVMbG0aa-Yk**_?YeOE*cU;`--X~wfsD;JmtzcpQb*pB13mk+)hF-i?fAE~ zaJcy#dZ9skBm5<RZH<54U2_HF_lmOWdwUM3;Q3iAjttrUc8#)UCm*ge_L$^FJ+61I z`M~+f4fJs8wYd!#-l0{2)V&7B@8Av^WEgEV?t+D;5&asSsDl=n<c`1BS2}k$7!wk_ z(w7^I2b05%8|O5!>^t-T$xniRL~s~!4~VP($}nA(Y6l%5>tlg~eN##ykgB@WPwHb? zP~V%as20j&Od$ersAp7``-b9ge+37Iy1zea)JWoFri+0a?7Y`NWQ66`YacRcO4OG} zMi8Ea_?!aOSRPcnbe0Z9BlzmGD-NX2WnHm^%#**-S|yxAQq20cjA{k9F>V*u)~U^n zn;~Ps={g*(%JdfBw3hJO_rl`F2AK$ZN$!ftlyT~*zvfA#*Mqfs%=bkt5w2dD%_-`) z_$+AM{I|+<PLb-6daGIurTo!LXA$8q*Kkj!CS}y|FYjnXaoT+MkJk;bQ0Pb6lD12< zB>()JoyCWc%3iylfXir(?j_CE3>+H?0}G<8))w5Dre&WZhT!B6hx|p`__q#iDRv(W zn8=?;3v36DI8}*O{bT@o(v$^ltrYVm#qHWfxw3IaWm1aS@<oGY2V~}X1#3*w=;jL$ zCQ-ShPJYNS)TBL2Zdnue)9mr3t-AGPOX;Aw{y_9doZv&o8$!A%2EiXQw7}POrV@=f z)VrB@eDzU^fdrB>o`D3G2X3MHLruVdnL}OemBor6PLk*Fkr4Skh1TGuCxHs#A#O)Q zi_E|9urw`g&Sg<6A~PEXv7HqNSSEof6^HLdb!EwaXb{W)G$p0$Vt7JwQJR7LZtId0 z0B%>3dHAb;u|!kysuE5Zkia;UVsTN-9vXw+PbI1%QZW%lFp@@AHUb%lixlCrAlYRQ zl>&YaiF}_fSBJqYr%{X%gf?g?M3dJdamt2#<!a?*{poT!j&y+VR^dk}L9VozHjy#V zYdcvT$?!EZ&N<sG%*7*be=5%fv++-o(U4pxP7_o$0aPT41O>ZnPw*0P*fNyPAI&5Q zV)>AX$Hiu&$9{NK5UnDgIU;$hlGiXB6rHUu%K>{bc>Sr;no31EJXP$wbqRoy@OK<l zz@&B^q|*6&FB(6b7vg;2pl4>Bg0;X2Y3F^0A~`aq_p0y^$ibVP%g|GL!D)&?yt>5Z zh!KM0QEVZIpAbWYVnjgzoinrYD%`?8Ipr0S492+!u^R4QW5oEyQqvaA(!)FiH{2Lb zi3AhKYmWB4khJ6;3TSMm^;J8->Flo*dj=_wDYxgWbU!T^fYFOd#wG}eOb*MDO8G=q zl%2<T!RVFv4@2}@pw863-(kzlMb9@bqa1HhE)IY=9th$<hd;H_o8Mwo)Cdz;htAD9 zv=C<f;8{WEX2v-=2+5>X=bGYWa?V4Lncp3lBmn0dG_jR*51X8^+G0%aC56m2e@K<N zzR8?vAdf0Z2sBJ<SU_LCQ_y)cr~=19T`{@`so^ZdtF2bL4=A{d12H7e2i-`SqJZmu zz0#oYkfcF_QM1%r7Vbr)%6|{0&QU7nASFZ*&zv+d{1_}OSt9<t$+IhjblHS{i{elI z$j9W`zWLoz@!{eX8AEpDy=M}QOE@ssyP1WULL@Xf=pZ-BR2zH?xsl4h*z}4G#_LB1 z=^MQQ9QXk7sLXhU5TsE*09&pS-8DJAIm?)5B>suLX%0lzoOR5;^DV$jf<1B;-ShQK z!MptBaG+18t3wb!hQz)1ye{ilwWNI4!0K|QdJcY5JTC+qe{n(T!`Z$bhps!*&2bAD zMG_c}+wzf(1rv4&Eth7M?WNJeWzVd7(@Y3EvjKcntRy)bn@1UE99o8JK~je%_;=oj zB}ZHK#4KGLp4Jxp;4H|qzSg=|rlmI&BdjV^w4;W<7cLs!wLn@yU&95nmGAm-v|7S- zYf?_8LE1R{cP;_`@E94|u6Z)LqQx0HPNfTi$t<Et7^~>Gy@WE6wBtu%@Hmqo6El?4 zu28tJAQpCw)Az<8!b$7g;uw?Wg5s~wR*QCR50RwP7PrL;^rh1_;_zaFSj{hLFMU`l zB<eNi;=dM}#fL``PFl&gopC3!G#4I6Ubpl`lk!B^djg7$p9y4;O<G(LBNYF!{Eve} zv?n-$Og%w!fmpmrvzmXC7bt9|y`;Or+(gN8pUhcpNv|v*dg)M7L9x&GX3zI>eewEr z#c1+L>(=3Ik$9705(6gT^l9V|6lt^|=u8^k+R;2Po65RSX>gh#NP2}xS_xU-Wb0Qr zx!C~pWD<=?MORYdJfH6u=MKTcH<HZ@#5ty}g^EZKsH)Z=<pr{BBthu#)^w}yhmO6B zV`F)sB29{O5Fl!Cqx46C;n*5awAI^DZ9r@+R_Tyw&;@DS4^d_^0>5H+*ff2A2nS_7 z*o&nRiK$yBtl{l^7-K%}1j!&du_F&h3p>HQ-XaKFPj2{wHM@Vt8)2xX>93?qma_)I zC;!#}oH{-#@;6Xg?$xW;fIFiX=1-$x1~g=4u`^Lm?Ofrjrph4nvWdPOoI=6%L3;(a zlA5&^Yu<P%z5XPyRiKU#KpezSU6;<h1Da}y0nc*J>OJqB4Q^uaIGky+LtgVV`#Cjh z2f{mbe)0U|4d7n9^dH(({`6`6Z`Q*8Pg&TX@~^*n!hgwH{x)#^3v2mnqW*tmEq@!s z82|Ed{R?~f^Q?bg$^TXM@;6EOqAi?XPS<~gK>b_i%0CjJ{+|;Ue*+q*uOIvq(EK}< z`SZ8=<JvX9&i}vXPZ<Alj4}Qt8Dsp*GRF9qW{mMK&luxhqOpG@t1|xS8vFYX@i*f6 z-{!IZS<_Xir5=qvjOdlE%NVWIgC#x98-(1WF&vfGZ|{r?9RlRPJ7s}+t#&)2!N*cP zr8}y+q0I{ZQyfkLA}ILRadmlH`Kk5C@6Y|u-@#AGr6behBf82T^dEiRcBjb#sUoQ# z`JPg{L?c26hZ2Y+#%Eho?}yF03Ds}wKQFUCj)E8OF2=mk4g&dJtT|Vw$_Gt4Qt8lD zKf2%6yPv&V@B0bppg$qwsUl$*CelfICf`Rhx^91e@g3K+XLf?i=X;Ikc-GBg@6*ui zM1hioY*e=xu`$^j4hlxY1aOg5r1!@Z(%es;7d}y+RG<l?C#26a;z?{WfmD;EXq2nw z$B(1cMDJIdyhUCb_}vZ~B)qd!krhmsIzuAvGt!(8KOWlNQqp|?JC+R|2GS0&B>G8R zd|fCh5w}%;stMd8*+Ao%LJ|DA;^#eyeG&5-)Ax^ol&L}0S|}x*7MCe(&wl@CJ-R7B z7Fq%?ILs^2=sjd~K3YG9>DPk2>0xAyK+Gw%rJU)0bwkG2t6blL=wZe@z}RWYjHJ4w zku>o@heZc8H|`cEosO3d933Y_H}<(!BHetpWyut7sqByrZ1WG-ZZ6{u5ldQ2(|h>Q zj>LsDvbi=<q@YocJU^td@W7n<X!3CnG;oQuVVL!uD$3ij?ygxbjyszwY&HUFKcI(T z<+fVjk6+O0<=BdH*pV!uz{)7x!~oXk{4O9xBT8q7RD(*X!-_o~JrDboU=I-ld2<wX zL!-fV7SUI228}=1kAL-A3>Io%uV|zd2U{rD@^`k}JBuZ$r9faW;bgANZp{RZp6u>l zz^U4ykbpXiMmJ*{ng(}fRgG#C=kl%b_`uyh-L5BJK0n(jzh85E*DTCwZXN}*Up~EF z3u1fvx!qp8c8@O)r1@shEu~*us%(HZ5^onIqfsf7NOL!IX-M($T7LXIC@$rja}>K` z(lNX_-bI!UFvh|J^)r1tJ}^+6yu*jGx!dz#WWBM{!P$z$XJ3@GO#^0^RqX;|{w`|O z4YMaF0G%R%HqJdsDjhYb(Z7@Gxx=^H`FdbM7w=KQmJ%<2pOEFdS_KO=qO*AmeM?$N zz%A2xikd8dB^pCB=XSqojW=Z5o;4W8fDTfPuevvA>c!?U0M&quuRHt5sN&LIM0!8r zbKmc(JPUTZa|@+}=>LM;vNuer@{_faPd4fH*_koZ?2#Tot<2|bJW*HA_=g3Rd-WWU z5n?ltM8VwJF&{)_R#9U25(<tMQAq&H=z1{x?q{ur4S(u40O#tV@K7D__l;p{KA^^R zSd)2O8hrNoXXh#W(>-QQnIYz&muSqUjNZ=!)ypz5TgYRx41gopV#D+zx0nSlD>Okq zk*tRw?PWv9izd19YF5K6Jriq{q@GR<!*bY`;3e$tQ)8zL`XY!WEmC$pnd0#WyIwEm zn&th3N>q%NVC{y!1Aw;f>{Px#fs5;c88AIkL=r^>RDP?&D>PzwX9y5U*)oQ71KCMt zLE7Sbi^ZrB0en?TCC&&00;P+7;MTwvGvys2BrRCmf@@@CFz^qc3SvA2h#~?BCX(Tn zOkj1jA2fBwfCE)h1XJVoLva=Ev8ZaKG7O0UfP3x2)f2H;htA8xkW9f{Jn1t%g`6+Q zOJzM0WD?r##}1|fJYKtigkB{Pfv!4LFSv6B+hNjKmI-5E78}D?h_CAd<s&c_N*c4T zRrE=9xW8gq@K6e_03o5VvHN_}hz|Qo*N+L5_!-6J$3;MAaz#2Y80rXVw>rJGvmr-B zCqrV7zKsl%oa~;Q+2X$+2;^{7vk=PSoslK13$l(_Tn7F%M1Z6wAUH~UG~ES*@JrWd zWDXAH$Mm%`@U)7`Rm_EyzPr*~FBd&5VKCs&Ow!}f;9i)n0exE{?OlemLz@wOTS5WW zNj}AZ_{$QqW<34)Kv!Psx~V!{UG{)o*G@995lF6WHFbPAg;T*iJ*OKEz<SKm9rjBd zoo=xN_b{p|R^K0avjB0@VNoo;$dGA0GnP{tnH@E&aHogg$$&Ur&rcMI`tloiOh-6Z zYi)d{_HF^vPe+sx2LiETPIEA06wu>vO5v!n!l-}+5_-d-ccl&w2py>)cS*t>!-BIB z=Zoc$0x(VaC?ccXdnw=)&|0%{jUmS~2CM<_nXV1EGdJXaiJ%5MHLenoT74r3D_W5T zFL<ergUeC<<O)K}^|>oeJH`R@eC`s=?K9w=8p8HXX$Gk*WN=ZW8=&I}V=(Ph3EppZ ziaYS)Oc#I_6ZV_bmHPmZpTURlvz#0tlU>45>*Hjft&19V61E_+eP0pZfv66E)Jgp= z`O9o7Ch@6lFkw@9EyW~|G*oCG8oIPb?K(kxVO?{xN00;*jmWCh%$vlwM9Eoi*0C^1 zVa1b88c3epgc@!`HfbPNCdV3si$X1lVFB{}Y>YXqr!s>R>Kt>OZACodfi?l;Yk_uo zk?MHD_(<c?@Bzj`X!$!bJZn911Xgg4Ob!=NV>#=<_-}}Y@H2UJS(MipfQCR2cuC?+ zEbpcsg=r;hSr|TPLKrb-m_8(<V@aBJ<6YV|k;xTHu4Q-IQx7<*Lp9&&pE1sX(&)m` zh-^>X5Llos(cQ!av|{ckg}P+5{*D$;=9wXN;3`Jv48BXDF&k#c5ZI6Be4An@-lC?1 z*);T&QWiLk<eV7Z3V={(OpN~Yg!D26-$%y1KHzMEk=*QvG7-H`O61XM>w$$pL`!o< zcAVDEj|ehxyiZaP%}vR-^5mn!`1UXws~Uiv4BjA%iK*h@(5Ty(6Uo=ql)kaJ4FA6$ zw6IZp>fnPYMve}8Tv_oI1J{5h*Xab7G7Er*V_Y%_tmt#3z;c{DRiPGq2Uj1$P}HeJ zvKJ(7juZgKpE`Y=TtN3Fw-)R`8_a@_7GDEj5NQc!oDk!HgcQ=T`3iKxPKI(%EK?=a z@*_F9p%X2FQzfU9)MNW6X2;%!Wws|`4s(u)NxP();e}#~;f2NM)!m&txsmRVk6T-L z1riLKM_!G>Ic5Z}2DR*Ml_MRuV*%ca7V?uuH_P+U<&01jc#<snqLnq#$1TE8u;-%M zMY<<cO!6{T8cDNYJ4Nw>2!1bg^Y0H|ueiNAnjZ#|tVW%ZCEN5DL)5rd$aK3S5~Pma z7`$6ms?&U0*QaWLit|M-wJSHjJt|K3?(T=fC^eN9wVhmq#j#G;C_ucLk7Wf%Jmq2h z=F|@7I52yYgkdt~jg#OgxLXVzjIVZxsqHGTkAJ~Yu%7_D6abG!I$F<XV=Ff_I9wP0 z9D}<^`K6fl@k^YXo!@RgJDuBnmXike*PZJ8vZWFC<T_z&@te^qdBc=QHK|UtRV8QR z$kMDEHGb22j2v_a^ax9DhTel!eECjAk<9QRdFU&h6CTV_>bsYqjsrHlM7z~ve63S+ zb~M~_KDthwybJ;DDeKY~Utf}7e{GbvViFSWJb;k<flqDCc-k51o^jqpY_Xi{5^&FI zu&_5*bM%O#AuP{KAPp$n;4&b?Hh%0naMS|@9aUPn$9Qu0!0CauzHS<<iODwUX?9uw z8+YKC=52lP19%|{zM0%jsQq<jWNT|<xJ&q)1?Ko7oET-DlNblP)c1#WJGE|-rZXXy z7o^L6<u~@PTUPeCGn|QORa;yde9PjIa@tFO(4B!N_tbJ3Uvp5WIrwq?b{1ymG}`)7 zD+r7B{E8;_D{b|vgOc(DY=)Q44LfJEvv~1Ip1m`c?;&v~q3&_9<)NJa3h|p_K-3t* z+|;-E?V!38E|MM#?okT2Hv!I@H5?9p*&h@Rv4`@M2SBjKXg_0)6M~r7^@S>s*%Ml0 zr^FF$nLYu$gL%;gS3TGjGN-DcF~rufWDeVN2P5D{51Vs2NS!PMyCa8k2h6S0?(N?p zpu?O?^^t}#J-`0_Q;;1$YLA_Y;g~wY-@b$h-GiybK0b@+!d5Bxdcw!ex)5n|<wuw6 z2%y+^^Ni<EU=}-63+qg5e;)})K1RB!%kJ)9C&cu8l+4VYb=iYM#E^mDilvy8<Tai) z*mx0K3&cfc@NQs2eFw7Plnl}aC_fm4=?duJh@~T>e|^R#qNgKSixiMnWjn@|FOhdN zymE-&v|a$rBbTrtM@lJdVwiUjktK)3gR80P;VQ<6yHFYWAu+?Yr!fJ!*6*%SxdLh} zob6PNYTSkxI<u(Ppt}}s3@)~<%?zZFXDESV(1ym7tNCN+g0)tZZ6k_b&9B@{Jo`v< zItp<LTVMQ8xMeYM=fr~j_$Tw=IcgY+90$H<^I#;3`_v3(V}?i9&5?{^p+D{m_r}w# zg!+q~5Uo3jXN%U+SydF5eS|xblDR-$NVtv|`DT5}VL=rYbH23CJhls}{VdC)D|h_r za~G<-*rf&ww(l%M{Lf`svXFe$^K(+(`c}jOd{%GwC@6GGo+Jg+6_Hc)+yS4D#*3Fl z`I1(^!u)ZSyI&R+K&JzA#&xD_R?+J}kSY)j%Pl;}Q!nrP?SHf_THdU~d1ZX1nuwvZ z9%9Wj^L0NbWnU)UGVWS_lpMdd{Z4@OJ#n<9OaMm`w#IoWg@M&h?Ie~x|28La#)%86 zQGpO1$o*+jyJA@7<*ij!lOEUl!vwH%gC<9UIvJPcK}F{ytnuxBW4sxbpUpCE>C(n^ zPPM8Y&r&{UfeDu3*WG|q_6Hkz{FYyo`SoN1RZ-0n2FGN=_HJR)JdBlrO8d|11Z64S zK8uJ=#0rKS1BE+{F_g@?m8!5@$~?1atK}+AN3s@_i|K%p-t}7|_4IXc8OD1pQY+kd zQP%LmL?#N%hy4*Yg$6$&w}CwIIvG!GajSJ{Ld@S)ik0bB>}3tHT!4D}QrMJH+8d%! zfx`F&;0ZE#o`hIF-+p;~FkyQyjm3UD0aJL@DfU%zoU?<511(f=p`8D{6~r2&5lOU` zz6e{ER3wzdqM$o_1BNbIs+#1=nx=IU=)`OuFW~9eUIr1GomUW4wV``@R1qrV$q!GN zA!fu=iE!hVK%!Kis`SX1MwO#kDS<QwyLLi$uyu7Lu&WtqO=604auoHwYw>7ki7;!D zjthnEG5!Kms@sm}$L0O0>d*4o$cEE8{7HQB++RI?*)c2C4V;h%qGj=N<S0BGkc+-e zpwU52oF6KN*i`j$#bmi_X36H%v@@hBF5He$6O<{nnGRezdKA)yG6dAt<dy}U>PfFw zTJ0}=zYyn!0Hv`<*ub+hy_rff-?B$m^>0YtN%mR8q8Xfz$fZ%85{C4f^Ba^2h>M4) ztn4@hs}Gl1SBh6LZMP3OP|7HbF&9O;{OMp#My{Y01QkpHN{?}p3wLVcp!M+MVX_qc zbU#OSMr;Z5s~SCWEP74aR+fwj9Z&-%h(z#>KD?5-X7`>HMKbAdOpkbH`7GyN<dHFG zwX*U73Zo#gyMHXAXObY&b=G9of4PHTn~k>xc*pZb9G<C{&Q~t0IjTBcUpvrzb`K4R zyp<LvKJL6xr%;;z14Q~0L;S%eSQ!6h*6B}4`rpLjzl2DCIn@7U+UZa3@qak&^ygXr zzUTk1pwZt1=#O@te`7}fxvA&h(a@iE_`k34A2jE$E}#F&lryMl6~Equ_%Ws18{H-m zMfNl$wTAg>P`tdK@tuZU;xtMu$@SY}T*(nB`seCh^_@BZbPS5Cdwc3s<S>c^4O;kM z7f<`!<mc??B>Z)9=wCQz`U~eU-i_x><Vk&7NS4q7aie(Sq4X<=c;ETu&Gts)u>*hE z;r;AyqdGqt=#BYBbDoOuYz`L<nAfCE2LnHCyq{b?4`$%~TWH?ErKm`Z5&AnMfZAI5 zvgUbhF38n=%TqtW&V1u}D9kks6m`k$AmQG$0tCnyI*mw@BA)d2H}tgV+2#kE(zINY z<*{D;+m(6AH;>#~%jn)H?!midPtVtW;d)kVYOASaYgY#H>G)H;R}OorfDw|7*Ab2E z{*t}H@Id_*pimGB-n*gi7Wy2FxZUZ4!u)F>y9=cj0%XY87!8GyIh*P?cryCt6(31y zR;_wWQ8diMqTUsrzY9G{*G2*oVuD@PV~sqmD85Nd93eSR$ZpP;Y#t%BBKnGS_O9mJ zB+>rdxPcBBWx;a)ZcEnmG7ar!g%C$XAIw<%#hH(o`x*SloEj_SH6k1_p&yZ~3x`rd zPRQ*6$_c6|VZ+5nyY>cGfObB;NZ2TmEM@y5dZ*Zhc9gpAf}W?sf*$;s3A^jGLf|Yq zd7-vvY#jGK2ZpDchv)E7csSDeP%)-6F@0P6EHRg-RIe`OZq!P(*K;8uw>df7+BgZk zL2R+t?*jLLlTa3@Iwo0n3W4jQy@Qs)y+v&EAi>W`<^GSpG}M}R%f%rFjK~@C?-elV zZ2e1m#S%CnK*eI~0>+K_*aGxnE5V`Q<5_NezA*M9ezcvLg77(*+YSx7JusPwcx=|o z-+Ob7DBq+~&YuEzc%{;A0NJ-Zr;|T%J&ovd7gkyV&euA0x2{j=ySzVws>Ps>uSQBc z+*#k$Hy$*mObib@bS$an_>TjuMbm$FeaN)tTQruKnA}_z<E!K^9zxUm7eiH?A89+{ zfZ9#ztaA1L=zku5jzeG5^XTf(WF#iOi3san=F@tDTw>kFR&24mH|y-ZnjL#uI<RD^ zg4wzRn{GN?WVSCnaT4ztL+oR-nG(x>&V)yGXeud_V0|8|N|ye)3}0-F1z|?_x<dgU z##9LKdy0FXCN2vigoaW>Wwu#m)*RhFW4nXK_G)1}N}r>z08Kuozk7#-lag0TLZAZp z+e%K5ZI9ck&^^S5uo{@U#+^Ko6<1&<1uR}{Hlwf1&JW$AO-`FDC;wc}4=MK{pq&gM z@Qz-EtSRJxY&3zCcy;HCV_kStIT1<-S{QFUKP%@~6e*l(+)@t;zZQzuh9yd;@*4}= z(+(U|5w%1Y1uo44*u|m>83i|TQe&#<<aT9EktX7IPcB(T9P_fB&^fxOov2wi)czfQ zZpzGxTXh@~N}g!VW|>*Heq9Ir%Bf}|&@i19^pq8%yGCv~i2~f7*>F7l{Mb!IoCsk% zgjAA6#E2EnyO4&2SVNm9!O&D@4~biv5$N1T;h#6X{hlJ5>gL%FW}$L)RGz$A*>l2V zNi~rTohI}dd!R%i1tHuZmL!aeS?ky=ere6s)re5kve5%C(|k|Czg2mkAAToYvI|D1 zudDLaZrKFj&4Us}tX8DqAlz>ofdty^)Q)yoF5I?mrj=T`-rDX!3$x#!%sXDkW+N-) zKH)NIs#0SYOo;8h9`^~0r@zhyZDlX80cb4zZh|Ce9Kisy3*!SzBLw)~kX-N}TNKBX z*L>Aebuj>-Ae`nxu>$L%7MU~-HH!!8tb2o$TyJ3n%u?gQf0c;h<A>F&ibj+WjRH}} zr|6F_N!cC}mqaa)5{{b;bz!&QB2Hr35f)y;?m84~!e^+K7&5*vnI%>-w{f?(gx7D_ zvVh4lRjlDY42eMxx?$@hN@YjmYWv+<tW__Q!-O|sN_-zWr5(q1GVA_)3sj!S^Ly+= z3uW(EMDY(uYP`!ZVu<WG8Fur%(0+gtxG2Mjvj+I=uzmCITm?+9`_d;?*aGXwF`^7F z5-@wG3vj2eQ3{-}tVR1U^>-YRhDYM?--Pxsxa<|V{y41FcLB3R<%+Tw-aAI|e1aGY z;SdXl5nuiS$hUgMWJxKEN2;kj{CpxsMf@d?<GfQD&mw9hfN4T*5rOrFg0)w0hk|wc z^zvX6!tnJ)LC<l#lBoZDY5WBvRsVKF{2a3Xym<<kaa?u@y~UN{QF#2E;?eltDYvMu zR)P#@F|s6MSRV0%L$5jeBC*o6&%BJJ+Swv1O=3mZ464|m7$(&>?<vYC%3TQ5W|FS- zxfP$IN|5L}88|;|_2MvpOlC1QLA|ys&eLDqK$m5R8A4<|+c9yNYF)s-&AuLjtL9Ly ze#3P#HtY`k!dd$vZ5Ysvoc-y?1~iIz33p&ymSRXfq6zG3%X_luc%<@)6D~*3d08bq zAznujQFNm?OVFXn-V&6GQS$IXRJ)u!)joy!YTv83e(C5A-DQyw*UO~In`SyXC;%>g zdDqudQ?+}=FbcO87aWm9`%%K^tCjG?s;fl9WqDF3q82*rJ?*0`khNSZ3Z@n!2ZDuJ z1Rj<uMb?EK+u2G@sQs;zL9MGp(aR_%9036iV8)j2HYVT8u%uyvy1J^Hnf(WVMVG5- zi^D<7ws=Z@moMAcWZm8uWJSmPxRo8<Qgtr$zyVFDQwNX*K!zx=hUtjU4`YVxh`#}Y zT<pmWJ!M2Sb9TT)Li5v_dgsuq`cPY<J}ZQ*PXnxP{w~@(Zyb)+pTB&WD|l&Bt;;@Q z7dwRl{<7<y%kXPLVpUB*gGfBrBQQ+ICW-)s4NRX^WkL(;xAk%z1tR+_CGEjCo^bX; z{qQv6jwO^inVvdf2<b_m_ax9&VNBAb(u<HPbjNf|MBvZprwoi96W8ghIKCQH*ql<Q zee4m0a!OxvOlOj@m<CY0p>!go1=l+9Rb@|D)QE5dPwX^U1>3TBf7Z=-bfIb1IvT`T zp_QrYD@*z&MI134ZVNwk)pXi0k~?4#0hDLkvLEV}`Nh*VT|4Nyn;(*R@^C;}xpZD_ zSEDd*FVo3#hHQK!hO1$@%2Skk93ox8AWc8QgUZk_lT2=E?!b6BH&o(^%Moi4{U$kS z&QN-iwIFy?m*Z*ggSHvHdL8Li#fN?KdN=1XQ|1ufEen4EZs>eP`%BEu&|{DY;BuVs z^%X3{w7ZvAclH{!i6T#{t+{Zbro=3Tc_{(xrHHV-Id<zw?qS6X$<pOBJ%ZU-m<xbr z0H`HT^0}V`qFQHLMI#G0AMwxKuyBpv?qX27&4PvshKhX<(U*2YTnj#qd@uWP49-Fk z3a)XPzTmYUMV3tCiB(Y`dm1(yV8mk|7HR2T@mp)G&(Jb{i)Kf)Am>O7%xZlBTQ3`E zGx(B$A^dtQGQCyzgqENQCA^}r(=TF{Inh?b+#kk3YB70-XJwk4u{udD7d!@w5*BAr z<5(H#r{vY0qc*Kr`K)MIl2?5>G1u6l#FRv_9YVswF>fb--r(9YR+K7MR$R$3g@DZ3 zs;;{f1p5>auZjSi_A0?}#(@r{2t&g`RofzyNvKc=zy+*es1Q*_AG%v5M!e0@;`n-p z`y~?DdXVZ(>H;TcpDo26*s~mUghz!`ZHF{sV(@BjNJPngVp67uuPaeyJbVh}#IFT8 zuISnx6AHUg)#EEkAhxkuk_RgEt6K(>gzwbJDDV&3BB-87(UJiy`pqtV#JqrFWxh38 zlX?v!_B<&ZQBecCvIKo?lk%Mw8>)&0GZ0LNE@OZ*g>fYv<(_~W#tu@;At~$_43`8q z`v4=xtod~ighG%P98~^4=@9}(n+B>3H(`sm5%PHikK59}V+m(rJ#Yl%5y*a=Lg&H| zU6MheA4c5Z^yUfdZzN=a#k$OYLx^4WZ*<tMtHA8gefav0A{TI{p0L;S3!Gl;R|T(- z{A+0e_VvR~h(|gKIYu$jjg<vXM|SXt7$#x=aHBqi=>GjSGfUJTYyUVr^PY0^?_q4& zDRN=Qy2P`tHDwF<k8o2GA_EY~`r0D#g5trGBSJ)_tAR#F`f9GT9;;~YuOv0e#i!Ge zT<VsZzbi{QLYN!ZF2Fe>^-+8e7eLisr|SU5U}>8p+Ryg~`r-_gAtDj|oGE<m{d7C{ z`BV`D*nn(n8W1mu+PmD+yS(jXYjMOLfQX|B1p+^8>2AtPov-$ZlPqF%*-0xL-@v(q z7fc!$Ac2%PgBVkUA>>qo7=3toUX%iN<iV8iaS8dXcM!4UA`#NI+k?Fj`lot{5!4PK zDqs|GSMr;oIf1-l(x#0p>kJVERs~D@1T&fCW?V#ts-l#gzCLisT8%6Yv=A<T1?b1z zz{tO?M(~5)L+1HSBAVqBE`vm%aD4NFhFV~rt2j$Z89vWAmRa!U9{T}y*-T7k;iRZA zI&<wTd=#Yh_1^Qu4BKigdC0ckR+`rTnZg<7!Q0a4Q2dq3MVa|4V+6MKVlg$S<$bLe zwl%4{=zy0NM|h1i3`1A!j<QnmNM-L}C21o1=1gTrJD{K|dv{rVi(9ym{hP!FcfgT@ zkQJ#cM`Rg!5Gec(YqMQ;)2WJ9qd9J`x2L1H^2x>}LR_0y*mf&o^JErWNkmyQ4^FE# z+pnSi`-i@|^+n<e^mE)^85HG968C+@EHp)>>>NtrjJvjtuduO9agH<_KZgD2-x$bT zEr}}d%dZZC)K=*}k$@OP7N|g<)0GV<hrvV7YbtBaZPu8`52U_gJ^iZQ#3fM|C0?5t zpuN4r^j+KS-T91!AtXa$Jtx5y(hOZV06Z}am9b^i<Gy>de%WJ4MIGwiD>*!+J>qHN zi?Y7lb4p-PS<=IbLNDw1*4A6P8(p5CZ;R!p%gg<`q{<yteB5!j-$9~!gkNA#!ImmH zVrlM<GMll7jlrQ7P#~zUxq*1(Nx*VKUndokG*Uf=(M-vqMwMMA+2ufd6b<Pth~$r8 zPfSeh4a-d8>)>XkbgRQim-iMODa*+QNKcqA`J~X}^*m)uST@!GMwC<^S9rB6a$X-5 z%TmsOH9&7LXU}ED@Em9#0VP7Ij0W@<d^%Z6B7*H7Qgk$}4x08n#_A$FhDX?V*(jJG zMvceWOPEN_(<Q?@c;!1GTQ*ArMw(FvsB}p~l*`B%^%<<C@~%vG<yBW94~AreYRm;- zn)ldL?+&;pI5uO&46;$=B_iZ%&X$gr9!tbh`rbRzmY=}nE|WIcB`uo6kJXa7%@oaX z<p?(oEDJXzFgHdn`82n@h~C7nYcXC3BaUr6+_%zKNC3un*ia4=fC(mt9`aqG4q7LO z#CznyKn<1dd8%Qcuo$t_!3(w5qd$Q!4dPe+6IJ;G2mDD@82)MSU}XLy2IX&Z@ZVCE zzoO0m*_!hwfA~M8D*u{Y^Z!q(@^|LWzvi8?{AH#4pQuWu+PVF&Ce-)oVtxhVpmkvc zff$0-Q07_`?mag9L^I|nWK)!|@8;?bqxW9IPT6xdGpfg}00RGuw|9)LtlhRoW7|nZ z72CFrifyBUifyM>Y^P$|wr$(CbMwCU?0xqB+G+Qk^W(e!=3LKe&zNhjx!M||k3M>* zbCHz3aa2tOhTies-ta)U0=y4xFe37kdSa0BY3-k(mo6R0lkzBZ%*2f|L^DS5bmtNt z2ofzjcFPr9f}>iy@4gMJU8?n4_W<50OfOuV8g%yN$=1=FSPeRBk2jZVDQ}x(44onK zg`|TSj^IIteycZRyG;0lQw<auCo$Mgp=gruTnra63Rw_iMGl?x6O|7k?7IQ9FjO{* z3D6QLG6h<t3@`e<iGMVJV2&Ermy=D{9|eu^RwN?fxraAb<6XtMSGTp$m0;^l-b;H9 z$;;hRrP0sKua+*a|9*b}?bFcUE6g%4G(Ba~nT%?jFQJpBKI}E^7@1vO@iwUw>JHI@ zVNU_q3fy_P^xGIv-(J1KwzWXyH@0A#tQ_U?&{A&vaT0>>09xVm7&=fvf$k6k29W$s z7g~An4D+g=cmF3Jee5KiXKr`A?fnMAuZSFg=yIt)vB?E0H?iPvVs(b{zJT2j<sn4q zgoY@LArebIBpgH7ryykE)mtUTrM^4;C=rB(T>+92G9Jw%@C!D9#R4gn#qRTF8T#RI z59gKHVAe;0DoZZI0Jvkv+x)f&*>^})YtMvtH0upmJSJ^y{Igv<rFs`%C%v=j<@k8i zXK9%J%$H5u2jJ8YDfS!l`y=XdVCO7+>us5?pi5y~v-ko@>GpV<hw_uq6$i{TElHGT z>o}W)&$$pYlGf&9b8MaJY7?e@&POr_TNP-jg;|@gH1-|lNx0trMyA<<!CVMnJ!wI7 z5&<7+oFPWLM=NeDN7!qkE?I?c0F5nep6U(~9idPT>y-y-go>qk(e)^ba_x};OPK?A zh5>~K>L=!r2gM-Tg+~ztI>5DvM?jqC$bg~1ZnJk#Sp<uOC;lhr;{yY(QqyG)))0<a z8H8}QF~?aB&fhOgq{hhJ5m$JU6bA2pkck=LAZp`Eq}gM(Bl&u%hndseTN=%7zxrhA zX=muXdEVbW-(u41$>{MvzlEkon?)${BflM^%*&T=zDPbCXu{rWn;%SZN}8qxyimTp zW%32vppX#a32Lp$a1=D%TI*`n4+Lb<Ya*WYP5arl!h|UChkD{ShFW<XOgA=ISc-7B z&e`o%kJjP=z<&Jho`h@Q3?^eFlE&D8Q#p}P!IGfcS<2mBf+^GjMmIl^3G!9ow<yAL z1Vio##5X@)>V{#G3eK&y^a&ERMDzr8jD3|tfmg<<R}k@yCjrw%fvq=MA&8Hu%!L?% z+N<WL(+`1Cf<<%gzAyw%hf1hHNo_&;d7NbvQL|rs>RC%FRwzlWo{Fel0iAFk!K7nz zvj0N;svVG47Tw{FBIBSD?`jJ2miJmzQo1`PLDhH)@1>8SotBh`ZyZv!1{_7GDkL*d z6DzzSJB<YGyVy_-P|GMJT+I96)eu!am%mM~Tp-*CR)TI40aKpb^p>333vsnDuklVx zu$Z6VxWEfM5@@R*y$FX_(0XUQ_QZ?j{SIyo9SO}9xu1m^V$JM`0E0>Bx9>r1nm|9w zQcHt6Yu5Q@c(`$f<Kle{qGU^^XF>29ycpq+19%We9mES~!Ih-nk(AbHkvLFs*EtxB zXzXjNPy3X$eYVj+Ob@MMW(rOPTNifOl-nT1rPd;n?wcGrFZ7#Ea6VhY5*pLh67=%{ zx=fJzRke`#xFbWHkkc-p4f#pYvSrk#<}Vn4sD)(#bKzRJ1aV{o<{#e&Y=!X{R=7&H zF?lU4sn4g7ytT$oGYr<hGE<MXJ3q*Snw^#z`3Jc>ib3INirA2qC=4O9h<!E{-^9R| z0tQz#SjVS^xOR~OA%H^Y#~BGuEz)mjNprZfdwY;1GC${>p1tUSM7+GJrJHMTuR9|1 zm}<y3rCot(X30tcIY?-L+l%fNQe<cr+(Hwakp7H8qvwa`%ZTc>d#m*j%*dEM`|aEv z@?B+5oor-Di$~ajDb=q-Pp;8vwPK}$)tcuS`$e~wkrmTsnbv`3iPZzh@8tqo*S|fr zt^v(V)x{!6O&&7|zF>%JUU_>?&5c2fGLm^w9Oqd@EhTuSO+Saz`ah#$3F3{B4pB#- zujIu)2Abs?<z1oG40y<n4@^Om-@sn;wu+pU*RofCHY1AZNwjQ)2I;JwE&yKd0fLYB z;XrM$;~pZ43gzq;8mVVqZE+ot<Uu$NH{YzxDi55#S=O|ayG8e8@bzu@4Or9+uJAc- z!YKdNn6}dn!-CP)GldP=;P>5?0tNvv>sZYJ4W6&lz`Kx6_U4CyF99giKd$a(9uiwY z*nQw{E?Kfp6M0zjly22>J^Gquv|4ODq${bhXGCbF^ujIWu7x#CK8siB3KQ5A-L1-x z;4?xU(>ztzoV~16^BSEJ68kkbZ!V%qe?Pv^%yJL<(gjh-iK?};Js>unz}rc}1Tw)z zq-Jv#Pip`<Q*OGfiQByt9&X04J4(Kv?cAr}WhMLVey|vzEiHEPc}P|&pOH4{^~3v` zB=UbCKJ-e@{TC_G-~Z)*B|nUu%$$F%|9#B-uQKQV!<2~SKlr}?k`n#Lxb%O^R{j@u z^547sBN_X@l@k41Kk45(P5)#k|9f+Z<-ahjWn}pu%I76$d`*Lkp?pn)Z^e}LLD`d5 z*@n}suGCm_=DO&h9~wXj?OEHw0%`6ej*y?1KB_%9PQpQ;9Y<60nlmJ2W?syyeQbN% zecE9@NxyaJnEI`vUv#do=(uVmHp#2sxh=aSsXt=s;*#0uUTg1*Nro#N^EDBqXQ|m7 zm`FxnrQ(aFT;^9_l4)!`M=a^6T*#MJ@`YC5uP@)HmA=l8(>u7_6Fi#k`}-5P8N`E~ zNbv${#$b2nWMk)UYlk@Vu20?vW4eHACEM;24IE>aFLmp?K;MyipxVta-%loN_|~<3 zwAVX)*j@C%M&~lt+kM<_oi9EPDn5#z`4fTqFb1oWp2AFdl%pWq7c<s;m6Js-WFC0J z8B0QPH_&!Rrjir9HydQxi-;%6Xj=vgN<}g-O(AI`>S+#}jL1SNO?CHlW=p@>B{9r$ zL}XMmD}Ae;<4LV=Km&~-kax=67;zsxU3y+(O;}zv>QI&u*($ZV$-PXo@kHU>E{o2c zl1AC`N6ro06Ocfc2%|+t+zH~g5|wXEnLK@sGEGi;^`>}I+@Q2X$-y~rQ^3f;B2*EV z-Itxe2!YOTSw?Gj0nQRUqYZ%b<PxY+Ij|fAkpPZRtl*xmmYF40zqs-wNPQ==2X}?B zm=%L7gT!gfMK#fg*Qd)vUF3h$Z`haRh0?%W=THy=Nrh|R8#XXYgyPhp&Gu^P@jEIR zs8I;79M9|)Rzu)s1fV}0?Ep(%NS?xYw^bWd*yW*|gYWq5(<E*C;ph}q166?#o@>!X zxKl`S2&Qg}39(1v+g$3VRv+DD;D&oHxs=6YDhL?5xjMXB=09*SBi#W*#f&N<#FF+n ziVW(1-+_$Xe3Kw&mU*m+%_j`Q!2p6;`8@bYNSQmzS#I+ekzgYB8{X+Ko4>N3QCW<} zc#i9H$GC@m66fbdSyYW}0!nEX9-`DQglUA3f>AiJsfqC09yAZhT!F4y&P%{{%y_bd z6Il2L&CKeUfFC;3VjyxI*7~|?A8_PD;1boaKNh*N#F4NsPZzSRGWAFrjGmv9RB#=W z)VA*HJ7z!#mkO0xz8RK?j5C##ZN_MI2QFAuMYNexjt#BQB|(^FjA+qn0J10H$Ldb0 zFqK%GSZ6Y#3s+OjTN4DH{ZutMeCCQ+_F@Rp9X>#QEgnUbIFUo5`J(G=t<?1m(wEhf zzPWyC2*l=06iXm!gc6+bjWvz!2k4}%V1RCKNk7UeKY<=BN=$(XJC@zHzbSl|kHS`+ zNL4l-%%&4<OU@B#?r1!12LiW~Cj&)yBp5aF5^2lRVmubZ8TwYJDDiId*Q`CG|J6($ z&VnF+E5;+xZ6ARc3<aD4x-$9?b0KNqKfjF$Ab>IOSSBXBS(?8YI>UxN1ZT5y5p$o_ z8BPz34P~@K%L$v95nzCC5WxW8M5x`M)l7)x1+wI8^7Olmj22irX;VwExPB@}1IyaL zgonvavI(O?k)Neol;HuB@&?$0u~gy#BdP#$im@97sPl^wFcF!PCsrY<*tM}m4~2U_ zk{j7&N5l0@0o53(hZN5{Ae=$4wtnVp<0NZ(8Q36YbNIe!Ry;tzHycI0Tx?lZoDH?Y zB0LkL>e$#)zEDhQytNpql`Vv{Jlk@|`{}O7D(vZiY8gJivsf1puH9thky<aUy_;{K z@tv{|TfH(If3l_VZ8qDzJwRD*FygGz4MXx;VphZbwL;rW8&t)L6v4Ts>jKJ_-YtKH zx&@1Ffj~|YVOrEj4ROyy?K_7U5(L0hSN;NS7@7uG>KHy~5%(Um%W-02_&Ly8u-p}W zWj<Evyi#dkEl%>iV0!c?ZKvSMpYO!9=F+K#n?;g68mXu%vr!(~G7T9;RC{Td>Nd#` zy3x3pKr6qV(dDm*w&H04WD7dP17L|N*+$WZgMN7a%y|e^2ZGO%=|XnTUnH`;V$7i8 zh>~7d1$?}{>RNvC`XzhXI8xmP2@s!O$s?Q*9>D>L=?WE>#0N+%>Ssw5%RR%K2f+7e zR3h}6xQQ_FMjON$fkq3%q&8lzO<_-_Bf*v4&BSDi636SmOSToyo_THq=4i_Hr+hMR zF%v5YH<3D?^|H`n>_@tlv!PpS5g)V0o+oPw<;Ltqt=9_l<Xkj+stJ%>!-^-FUkDMZ zccWv7Rc5{84h<XW;3yrnmNSY5B%KZRL=}GNhm*7xv!kujk}Qul|55yb9udY0Qc&ZK zze2$-yEHyfY|Fkhza(IOY`i|YcILIV$eFUPVa0TGk4iO%Xtdpbq!NN?EO22SdtMQ- zU0kKXju{fX2&%A5G@JMP8icfp@~Cc1;s?p-r>ymg>00F_|Az~J4j9eNj~TjVE@HMf zDZovcE<Q-zMbukCFG>+VA?JAogMz&LsCa44a##U0O=rPGX!{Q_lLi=Yx2afMsq-EQ zYjaq6)>?#1trJo;Xbuj3bhI)@@o+{@oHwDTeY?2iAZ?V{@tpi(`{e;Im!@|#2Q0@9 z(k@bYFyE&x0aP#wiTO&GJic=p(c~h2?2Arei;B7Gvp1S|2oRc+Nh&heE@KcEzAUhB z1nx*J6~&*r4%|toXYADTxJ*WY@MMv}=OHxQZIHfxU{viH9v7xI`B<j;P-e6euPL9U zQwZP~rVKEgOPCYnA{1zBP$phB%gi38eYVu}5wjz>@Mbv^W|1`<{LPZ+e&P8O16A}Y zh6suwibHrvTOzAmWgwM&N$`p00U$S{SX1%D1a`bNgVnh-#xc!R(pe$A-)T$4-qcd9 zR^(fPIWzPHbKO{PBhx5@5Wiu5OX)8x#mkyE$Umy}65jWS?pSWHYW}J2PrhW|0kGPW zTkd_CtkAl{Jc&QP<x3DX4U4{{H^4uPWj2hv9Q_<-Y<x*WfS0^u;+p_Cdc>R5T3DCn z+6=JB2u?%!&<Jk(y$bGirh%WLmB&F`WwX+)H3=I$FyW3vq7~hh@Ww3EELl?sr0RH4 z(|{o0YW!@FsXjKh{>Z;_N*C^2l@2h=|HL|!Gcp{Tk;4y$Of?-i`hI5)Pe_<n*f2{I zuI<oBUw)+m8{;H2egIulcv6#apCG$OUc_MNwjwz%p5wu0biqAcb?j!vn{D*l^a=|Q z3C!#8OzsoTfTBaT;Xq6PE_J=NRITZEvmDUo2QylV9L0NLdE3zNJ;$=|M~Luu3lwUC z$GJ@wSPUzokhm&do0&=ds*!EKJe8b;Ldlg2a219%yMWNTzOqs(OTi2xucFn(`#qFz z+4AOa2gY9DhGvO;pC%LlB(<c?_ws%90!Cal3@|#7#LKkI;Afp7cEN@BD<AmmQ2zfa zgZ>A6$i&LX@XrjI<-hR-{;#x^SpH9IV8*}m&i^H-`1daVS2E~-CdXg=tbdvH`zLMd z|HzsD72Ny>A&ccd1{nWa=umSNur~0wRZ<M-7w&Z_;|~;0LXKc8TtoH{U;?I%peAjV zh#QAno<NutNAPRoo|aQ(ex1b3)ND|Z5Ng!GMNLgDqs0;3$62qLzZLi+pUSb8$nkaM zedFhP7k&6#`(XBt!M-V$nqQVkvDW%^D>u8G1JBR(#S7}gNvp=I2o@PP8UBHwn_H1) z3*;9niM9KT*Z!9Pkm>v1r@Zf{aY?6#n-y#K@)x)DurO2)e>mVxq~FuOtk*DXQg>{T zxutRRyzkc?g!Q|vQ;oeb`pHwg%u9<T-YKBJM-K5X9YHR7H2m7_y3Z_I1Myk7f$Xg4 zp>b(XqE-WEc{`88q|fYRsLX16v)q3>s`xCBccmeQVv|JSS1HDjC)vrt8?7c0f<C$! z=~%NlJs*kLG}%ld7}KA%NiD%2o2;oE-!0H+HrR2$Op|SAq)&4`+grXK@GxAv|1v!I z#(#Fwt=DXpGU31v_{AQ^#6ND-+eGaC9;GlODI+i-8XbIMSIv8`cCP;T(?Y%k@sYdt zy{gh?+`(c}+sUPs7Eo@tq<6Mfvg-Wmy&t`EHfpd~7u+RA@3y)a_tx@_*)w1<?ZSo* z{!l$g45#FZIa>#*Wd2Bp3SOgZo7X^pDZvQTz#OaX`YN}ia^RrCyOzneJ`_WfyPI5) z)>pK|bC@{0p(BW}A{R;GV)TJ_ln91-YX>59_t7^i1q3K8+#H$=PfFo{8Rpqu$>>98 z7Q|l!j=BQ6m4|KNhc@TP1J?KK&FMEkG>m>%l?E`K%=ZfLAJ9{e<@;89f@8#REMOqA z*i8Ns6|o##Ze7sZg(m2SL0N^<F=b%*oP?}Oa;i<4jKawjzb%>bV#Y%n+N`L8^%3M= zd5NP<W5~LVC1y89mq*TZyOJRRd_0IzWh4dfb!UU_=2B$B(jnEGYP-5LSx0Q=vWuI* zJQ*7!*EkIrsX9*Q>NR{-%(o*%2Rb7#6~;)mg2I>IIC3Oa$=7)7(9N*NxE8N|*N1#c zZwfdpv=eB_AwNSuO_yTEI=5ZC6t>_DV`k(Cc#spOryMb*0M5M8i+g4a&DH1i!EP}3 zW{D;9FB?`1ysXH+n5L1DDycwWC?}?)LP)D~U~M+$b!Bepf<5wkJ;a%d0+Qpbj*+6T zF_GJ4>VJ5y0~okCvSQ97e&KH4)!X5Rc2Zs6YUBM#CNcI8z0$Z}XQ)!~>#Rg>>S8CT z$K42H6!W!N{Rx#Gj(4K*A<1eFAjajaU)1yZZBFH9t<}J)bjTpSwYuQN>C8)s59gb7 z|5GoC&6{(h;naEIdeVPg_fO)^J{tDOdc~!)7cpP33v!%<zE0!1z(v5ODhKw1=beng z79_(?P}*nr^@i)Btkwg;60JBVPcdNg&3c!8Ld0ktAP2ph7jTsp{u?_=z^c_`Y`V|t z!R#&&-FU`2rERAL|ME0-EX_w#<Or|NQzEC?SIZOZR1WpK%p=;SaaaTRinT8)TV;Q= z1RY?{VlY^yMO9)d_ey|HzjfEHS}1kbojGGc7=A<KRH;W%ArZ9DUKN%K_Wj9xRQ-^E z!N}Qb5e;Ud3BVxpMiD8`gn*|G?sv$n9QvD3z)}-8<Lxy;8b5t+h;zrQ_dCm&z8Jes zJ*Eqs0XwZHB*FwVo=F_Y5BC|qti+`v*y8DEX!g_=!b~zlcw;Nw2<yYTss+~^)7I~{ z;%L=Z?yD-7G-{-|U3h^K-D<W&^2r>6zaeEfj!g=7RkSqU%yFQXft$UtR?%im{U(uN zRY11yY0#LCmkaJ3swq3OWy3&usq8{V57YpQi~N^q-cq;LHszo}O%M$@!uv?AI0+M& z@{s$gpcj(iHay;;EZ?vGC^rk=Po4WCM2PUiM1~FV4qc<@jU#<o_$Z@wC^A}~q5&t( zG$y2>%&Z6kq*p3Prb5Zc0zgiYYb!K(Ek+g4{1s3q3Vo{+;R3-TX_?=uwPsq!&w30| zQOZ$_nc@=UuWC13I+=wQ@x$irYi-%DM21wYVR*Vk<IzIgK$y}yp8IJMWMUw(__r#B zC>VQj5<B%IggJkZ9Q!9EVR&c9e>-4I=P@Be{uP1C&@akXaC!~DF6Tvv(Gpb$=dk9f z)Yi@aE-|8-LvRp-;9X3(nl__zO-U%a_tdF>n4Bzxg_yhJYnV&<G3|SZ(xDtW9{rhR z3fMOqer~GX9f&$@%EH#q(x-!duN%V8e!`br6S=zn6D)W#DEdoa9QR&65p6i4C__Q8 zh>2fh?D-RqH4+w0U<Z<dtr9$$OC5J*xq44XSu~c0i_mQ1h2BB+)QXP70ZoL@`q4M9 z8$UMtF&$h7p`D^YF`A3e+%@n!bnzhKH&U3S-7O^YR21!G!hNcIq7i=UB8UBc0{w2D zo6G#;8e#|hL68(;n+~*d(x1#koDAYbqJ{^bQK`HBxiMjk;)m=hyZ-S86RE`cIucwy zah#cXLqg7U^C=^S1gegNTn<09O&9r*%`mdPg{qOruz8L!RUlA>EUx3(>;!)zBhc;B z^+BVTm`W@5WbAq|6Xyxv@21XgKweBsO$v6;q(9BBP5RHpSQgW{=2(`$B8Nlz<v+`X zv_$NieJ!MOo*p)5flE3ZOxp9q`Ueln|KcyC;NA1~emW4}b)9g{j-bKAr=Xz_WpRQb zPH2Wi;r78TB>S$e$YN-K*q1l-8@ZlYVTJ@uS}~aOT42RM8%il3)n5q>@BDzizvx;( zTJzVB5UQ5M=!WZ2-l;F~ol8L0o?l`fitNcPPjo@?@3o?bIG(B4ImLQSU(AvP*X&ue zv`QyBn}nJsbDne{+ToO1??gz=SQfJnIK(;<bJsUCM90_}Y{a`zRDXHOUx&K6SF?+r z(9mm(@@DPajuXG`9>+2U9xg!~%C|8#tbwTxI)u<AD(&#~{Aj0G9sVG}?%FLl3B$zX zuvx7HDn-dvcELlFj_h=!JErluiNeo<L7O-$YxSY4gcC>vj3+P)f$HDXs(pt)ZY9)# zw@9`DG%4PMD$gI&Vs1X|17L8pp$%G0m%HEN2MS&(6{1JU+xgp~nUu^M?3I9i&Mzw~ zixl(7lo@<ab?U<oRnQo1qkTO!Jsa(aDM5yJKrCxcRosI#NfJ0BKdC{tfo!Aw>P+;{ z<Fn4e<G?j^Fx?MX--c1`sy~W^Ym0y+-L9s(BQ;?oZ{8sUQC>`+{|Li&e2;@P-S?Ay zS8x~Q(N9e%FdxaMC7NHH?;wx~Q94>6K>%AWUWfenVZO%;|Ap2YuJBO6<&3#B97!Pa z2=VgL0vH}q%mjZ6v2@p@osdu{!5b~d?oGrl%8InqV@v~=(f=em>5mX1Q8ZBY?<39X z3s}Yy9V29TB-XiUb~VyyLyLTbuW~dsf>PekA;@m3D$9>p2f3Z2@@PG;khO^S24x39 zJUXQkBUpYdwSWe}`a<|61eV2LI<f<)AxCm&fQYGPzkDZY{ot?ifGw}5h!4~n=b-C} zXILhf2V0j{#lEQ?rJ~+0{QL%$RyCj^?aqG7?C^rM%yLwY(#!6Ic!!0c&!n??1VLEP zer@@A4!)&~jl!Ei=j7XTb8^h=%ozBk-(PsR=!0k>-7s8DWI^Sg$;)vghJ$0gQs66l z>-an$4Yau9e4pIiW}V$meL0_~oxHf;`tEGqWKZ9D7*e<#cdnLkjcfjA`9zO>9&Hsx z6FD#XU5|*-xMW22sDSDa^;}ajWUaZjBHCwM`}=32Dgo>L!8FiDwC=Hab_|sJoG>>O zIFEvUUsOW@<z68l0&?5#B%iB@rf^&JIqWpO7}cae&#bCuM~z4pG=X<jL~PQ&Ko#$4 z=8(DqEX;LK*Ni%ztu9|@RY-x*oS4D^H_3fIRur8f@x!>~haKw1p!?_pl+6(k9xG&7 zZ6U${FJy=e=g|mJFcOy=o!49szW^BTK*eE02=;!IUT6nYP|{j9@))2W_^kOm?hV{G z^stIx{~T+p4O6`{C1G&m4mE{f=K)O+Pz9War?J4XPvWG?u(is`4-Y?_D{NI`KKWFE zQ3I^&Rk5R%ZVT&UkJ)oG(?Nn~^GbbwMnaEMtEXP6tR&sF0GN#6OrLj@v*F#<fTB_t z)~cQ3Xr`T=irzs<BK};)trY(0ElRrs6=4nE9O*A#to(0h!@l5>IgZGG5}N`MS;HlF z(}M(%_7sTcZrV#k=wavrAZ?}}z7sRDnPYfL?`&Z|Hp2TD^jQtw;4y?f)2M&{t;53j z-VbdMYi4VC6mObzWfi>f7A*sWzkUSKiHBH}v>)k_d2==%&L~{BfYRs+3ee&a20`cV zL6<J_pm&p7XLFLyf*5j>VNVh1);(Ypo>X9y0JJ{rxT)L5V#e{Yv*Ouivda;u$=b(i zp6k0J(A=PHMGtTL-7>$bAa`bXey;UOu*qntHR`bU<Nj=ktj7CskBl!{lt#bi?u-Z0 z<1?W!FmKhtMOz`;rQr`^o;prPK8qPuSe+8sbP#L~O<Y4kEM#%loGH|m;CVFv33Qw_ zGxA?mum9-uFtafJr&<lmKZr7a(LVmC{h$Bn_WWo6hvh#=GXJao&%Y8*{%`ev{;lQn zZ_S{8*0Nup`k&?Qzt^>_fAzWkQD6U~u4VnJ-2W=n|GT=jUS~CSR|45<QSE3NuiM<Q z5eycYp>~wiSN|BW4-D0_`*0R|)Zp>+v8vashkLVUxW%}5j#2~DMTVy7r;4en>OpZ? z*~kpycH=gpL8OmKZ15=;d{pZd{Wjm*ypFoKQ%d5A_$}jPi8&tmHdkeC+eA<6;MMth z5{XPtYgD&h?|fy{_`B>AnU31_$-BYpThnS5`{6-VC-f)&#yj2H3ti-&vWgxbKZ{(7 z0Ia72&ZCOnb7C({o?thP?O9=0*lQ8VA_Ww2`vx{4W2sOip|$lu72Jvro^u<2b_>6# z$JWpDZz#JWJ=5%IyA$4?T=+IUc~ELuX|EZvnfT^}HQsk2=kKewo9$728kS}}l|C;W z&Cp7g#n24Hx8xs7XOpeF-naT`EUzAKiD=y?;;-|^o}L?-Sy9}H7gy6C5Cadp4|bj0 zqEFP)gq!TC``hOEipst7QE7vPA?xxP9h)88q}vZ*=Tn`tY&547a<NquwG_oA40>h? zzptfgoZ{8oe78R(0+9AOKgfrRpvAKNY2%+DKF7o(1vHGn^x0P6q<BF}ZxM*BC#F8u z`R_wfN#FGds!1pr5}iT}4Jf?(^TdV{^7Ipx@f2xr#|%5&jJLBhBj->bADAgii~Lib zGwO4__^7#OdFxvm+Vejz&+T<YPRq`ys-cFGdgkeEg#NT6e~AdH8>vUEeGAvW_q2T2 z)RH1@!8ufD8K7|Oo0|6(v9u99GjOq)R!U?oXtdB37flq7NOjDX9k9GUq)o*PCZ^q` zC^txF%0F;!bz8#Kx*Hf(ul&<8q@IL_-Dx@9Mred#rjTedd~Y&#!%+e}^XsrMG&xD3 zfs7p)(%28DmMVlAbj)N#$+*nY^_TYDkC_UyHRJHpedrhbMK6?^)3B<f!cz)5s|NmE z`;}PG!^U4mzSao~)>yi`iL-H~5CKK;hZ^w%30tKweBKL83nEJXX#fF7Uma~s{K!BA zF%X$8RQ*0;Q?K@BFUtp?rSz!rD2#<dxJ+S`td{O^4GAJe%`Fg0;;;fwcoJkvR<S7N zbl(^e5}tl(Y)KI**tw%<kvtwzF)@^w8T7BiQHfv6W;ZNhBPDdbin)PB59Fb5BUmw; z1juA7`tISo9DUIo+8`Am3WgfhuK0HKKWs(BBe+J8%vS);6mDJlptbg0DrLxJi3m-~ zLrm-+*P+YNWo|C696SVvt?2=DvYnu?wb{~nbzO(Jj$!eIPq>hRMET%MOpalJg`q=J zq>7sU6}VtuD??u=3Lp#=SHF&`GQtpWQzWeEazJN*MT7c{i@&*erW*VJgkd2C4$K<n zvSO6k2OUY}?Fo*?Cqt>mmi^4^rX#`DBpVRHXQU^)+3GLLC<JN?z|0zq9g_G8?u6H7 zK7t38zJm!zPn1vEVYR4Ev`tCm@2hJR7M~b8bVa0?NkrTmA##kqC2F|4Wg_y{1xpY% zqXLqTGCWi_(xbSuWt=c=twZ=5f@JO|F9`{QgSXfBkZ3h~<phbb;dt+b+&pGn;#(07 z<b3sqMkVLl+pkOUO_@M_|F9F;c-+L#99Pfb;F-1NQfH*rMxkWgJZ~^FRy-UUqUMdc zLHimIMx~_f(nd%U=}{61l(qUg)d8Or`FfI!dEzMAK{6$+=?uU^q+6;XQBX0JoHfW4 zZn}`TztyV#h7<0^!vkJpRc$)PS>}m}#mtb^c*-Brz^n%j!JNkaDWIK5TODQ_g(Yrw zi8x7s@7&lGy8eFuLw1i9c3wnEO_25L0$hxhzaoOp{}ul4L-6^(gDd|PyhPGk>`$tV zC;Zpha}gUmwrYYxYGuqM&c(6G(GW6l-!;y3u7m|!8R?Qx3rbPF1Y@28inriW@2VTf zxdxJ$qdp@r0F?u~vMPamh`s+3tAphE;516OCTNGzQF3Q|6WS^4G3bWT8u~`(`!Suw zU~>U-<|yT3(u^A+Gp3PiJ{L)0Tlk)1EAnr^vU+E7P3YcQs(`p)+_0{!rqBQ-(-TKQ zHp1r<^5W4xt++Nm<Em(!B|E}FC}4Bc%=hM)emE#B9z%qI4zqyLWudWFUn;LoKn#K^ z<_ZlNyX5O<;jaRzO0-YO#H1}5uls!@b}152<G9;FQ3|PgwvjgFs#%iZU`#|xf07by zNna&4R&wE2lTz%Qg(VCt%IA*k0`7Wzr4B^Tln-7pdSytV2drx>CGEMGw_?(xq`lY^ zA!891$~I4@lu7g_2s38pep}S|uZPUn7!)gJMn#AOmY4{Vyfo63Y~E(YU4n!D>#eD2 zmh{nFqe>nUnuC(HO6PoEPm^5zr|Iq0wUjhxZw@G`1BL@reyKo;s)hIfC7(H_BQF!3 z-pue{zpEc)&dz=~oQj%s#KI{QyEpk6G~4O@84|<274o%<E(db~Lpvz0%LyqKXE}>? zyg&m#5N#dXP|MnWSPSX43uFlBHpuzYasJpx-33*_Xm7K#o}f$DImhP1^GpshXnDnx zf=!<cGWxn@K;2kmikc{=OLj6OJfb243^6k($eg&Sl=3k#j54`*Fx$y}Un&yub~3q+ zcoMbbMaJTWZwb4MTTxuqUp#n~K4$nCYiHepJ>ivBEtLeckj6_*Q4@|`%^ap-RnFs{ zn&^>HnH*H~Byi<d)6ppykyd3S)-{oY(Pc7y#atmvyg$3AEc#^6IOGylvli4#xTv$a zUvjbo+wUD1z-VtZF9;@l;4u3oJ(;2othSvE3Fajm;%bHV=$ciEqEZFs=WX4(1$u;u zK~iCvAgE_EWE8=aKQ5yQ!}n)+@;-hvkvIWTjps;)?bVy+xhZ%BEo2#$r;>cFk%ix( zP3LnNx*}RwR3@)p$Dh}%6?O|Ea?^$E8R$o9twYVGv_z7Xu_2~vSd<uQ=L*$zYh-3+ zRN7N)S_}wg$63k^OzMZ2)fFh{83OC~IHFjjD=Aa7EuNj`O2{{oaGB3SW@83{0q>fk zUWV~GXycK_;^pM^#kYuGUe7F`?5P@I(}DYNSLgK5<3EN&dleIH3rjayS|QW@j#dyK zfO7DeT7lE<?fmgTX|su(w~@X5v^BzJ<ZYM+^q1%j&(sIlSPOUwAa;MoaEjJwnJ3DP zDA4@YG6#r7L0GwS)~MY%oUvm+z~sak@JpX!I^0>l8;1T_cD1uK={&mJ^Yzl~M7n=D zTt5J=@6dTQj>6i3dPO6Ldw{Br<HcPjrZb<n2;v2y{h@UjS-y)xE8eIi3V_)`6RoP% z20=ekNKhk&pKIv@8ubQcWe(alC;V9^jRlx}+h_^wtJ%EFk!DqabFA4E_$@7?RO%G= zbssG2m4fCZsrG$bvMrr;PCkg+b}#)pM7owU@1uYp-tihfzCWlN+Fmf&ot4)*E<@s_ zJJFWa{)lM$`HW{~AzE-o6Y6x*lxiyO{4rDtrpC4ea$S8ZUZEj(C(rAH1t4CZoge_k zeLWA&5Ix%4f9|bHFHk$BArkLwn}_G)9>uM%sWngR&=uF9=9UieTGWIjM;(qHQquXk z=HbD%?v(zX*PF6nbe`{L)$Q$%aoXe@z}zE00wNvSHfg@!kNygcvG~M;vg4*WO_LFy z;T?uk9~a(tUD<uYy@gMAqMp)w{BT#Hgxoro{!ue3u|^-PJp>mUUU<LQ5;=68I|}Dg zLk`6B9diMVrAS)4pw~NfG?W)(gd~`!khlwKQZNFJ+5%86=87<Zm*2D*lJ;ck+3gU{ zq^Xv%CQ?aSefaeSOJ(KrR4h0Ib96v$Eb9bwQ#u&COw;J8&zP9i$7qDSQV7>%65nGE zRi;3`OLX7Lg}5z-iX`v26&9Y-``2MApiFY(J^Xo@5Y&`E)H>v}0f9_LqwPQnEX)BA z>hN+z?pr|7?qI1|6n!g+mcZSX87fd~x(QBuO7!YZi9Y(%mdI!9Vv*lHk*C2$>6KVx zp=E}%vveubn-R}h!)%cVn_k@O6)&h^7W=|IoWeEMD#mgq!>g0ECG&UGiMiS53Ba0b z$O+W!e9+bpa|Ofei4De{97V5FT=whoSASs>i@>`Z5uX{{jA%ab<(6`Oc!o2V!fm<{ zxaABq@y#^V4<~dRTn?qFNqhbF{J4?kxqiN29?0=&%1J>G@xT$(wt~*qc=G6<q3vQ@ z6~7)nPQ=NtHJ9l!or6<c65r}xWB-8La63RO-J>8IS1H6RK@c>SxjrB`w)V~k9;k{) zb?+YaM!+I}8)=>z|9!#MH#r_mP)7i0J-@x4TLasH4{d!9qq1bZe!p<E`6-jpk!~kW zSiw|>>ZnJ~v;s+^Xf$2C4Ev(FX>ab3-~QX+kA|WLPBu4{Pc8SwQA;G6%S-tfJ~r9P z{H*;WzZ1DpVo2#H%cn}zmdD!ClC^zCjw1T|gMtgqDCpaPjSC6RB)K@wom}g#*{to$ zJQFB<2XV|^m;5Wd4v-|VFz(%Z)ZH*!H}Pu7xa8(%=(lOrO_l>iNzo+$2`i(TmZ^rX z2mLe@YeWh4)3M(8^DI<3@zjXjqS30Og#Cf?WExA*j$CU9UIk9~&0I3HSF!Cto;*Qx zU2gy>A6{soMV<@wl=O>9t(yOrO&o}<PH@I<A~CNJVW(RUc!vC7FkL(g30@k!MWp#O zZ%91FoaaT(xOhQ!h%|IL6$dT0S3kK|Wh6Ik!2IlitIeHB#@+k*cGAQ4t$i)O!5%?9 z+p*f3eeoj+eJZ1DgM>jHdbr@nm3-M*HFBxa$I_?TJUq1HN=G4ce}{SSx8*b5j3Kuq zx1SZqfiBr+dhcov8!XT)LqF1jXZ1`a7FS{z@@zMS{7zIUZ5dCjjrT;eu}7qD=4@70 zVv#_SU-2%O_VIO_Zo4)>PU3dsRx=|%Sd8k~Pv}O_R_!ztf|$~v#U&K89n#TE5J`Ed zft8v*cTd%-Xj9BM#U^IURKN-PnmjE@%By_zb}dy>VzZS#U4vSJ?B>w5v44KX=QG~! zi*3c!^4fy=nNcSH7BFTWg%Xh(fU?uOM4?8KoU8$fiOrs~JxB$vXzUXN+}DqFkw03R z64s^|K`@aTK@3UJFo34q;W9^4LYrQBH((PUYA8F;XhlL4i(lN2{k0#Qh*VZ3Bl%EE zvI#^@v_IY$TL8#lR7=oVEoeZU?D>79zz)+W1lo5Io%0%JE09rBFa^|F8G(3(D&J>R zp$@=E{ko?H01Ur*kr^aroQ4{SQiEBYgE+L#Y}rrZxK<x3gVNyYtz4tM=}i>(1U!-x z=Hy1<;i+8bvM$#19HYD}oKre&+DWLHH#>ant$(($kEmFhT)9zHPfGBd&`EW`QD#4B z5D^|DbnsvsmR6!#vv<bhQN&98#q+=zYC^A!0z3B?^xf?4L4=nSeaf+P`&gs^)#P=d zS1|;un7`j@i3&CErz1ykqt;OnwU<%i!kv9ok{xbLmupolUe>)(%FuB_ouxe&UW0Rt ztX@%Fh>>Y5{@v(9m5&3ew&q>I(&C^=x-N#FBRKER`)`7dHc+K@MUlGA2EXv`u1`&) z&|*9w2alhxr`yCu_&xlkT*~2MHNV{EMG@Ax`_jppZ1K{b)q%DTYI=V5lc?*N4C7c> zz5|fWLYi>t!;7KiH1f)s*NvDuDRqEBOC;Jv(QUdGVZFVwlbI+Q_3-&|@p1NK@2C~$ zA}Bau7h-b;j-Af(EV0w)RD<=m`YALb*)6)nNU`nQ?rwKO@HQO}IZP>*D5OWH-GcWy z9Di|BD^h42NVI><%1RhnM$YYHXs$8xI4L|nBsF=M*|}*mO4zTgRN^yM_`)kw%A67* zp5aKZ^=u~mp~H!PD|c&#)$Jv6Vy^Ek5l!Z-@Q2!_YB&u~R6LKKIA(|{?xI!aJ>$F4 z63~P@r+9FZzhd3ZH1`u;vXK#kBaV#+x1`sc6?r9$Ngq5k=;IdQBKI8Pwtv#a>0XHM zCFU5@*(flWB)P~i)@Ney#&iRxmlkz>(|vHfUs~IoMa#y_?Rl~YwqfBS+|sQ2mMfZA zhoJ2=P+2YrTR9X)X7`+V9mhJf8=`KKu__a{j5`Q*@)OGfPD_iLCzuwR!M127`$2() z5o(KZ4&|wKm5MZf<~Ns|C|nt>O|p~Nui7E1dlx6`j!%}oU}0Pn7Ey4t4S_D<8EjEE znP?rkSR7GFyto0owc<s6lOn#H<ZTEKMY`F}#>(B1A4ZlBG;?u>@@dROAoukEsJTTb zas8URRJxTv3VL{e$588|cd-|X-%>B^=u2M~hteX^4Y&a1=evTgHyg&Z255@TiZ#7Y zzpf#SfoxNYAckFd8#M9cLQZgMo!;CJ5Cxr)T<kA1oL+p`@hZ&%V!8X(Lvw;1#ku%C zfxZ>8nG6%#RmhzA2?*I@n?+&Ssy>y@OPO`G;?JG}2<MYj)D7>4#&53zfBYpZ^Zv-a zkaR-Iiih5D!v}}WrM`Q+c=Dv{dy(PRRx(%x8Baheuj2ro8v#9A?>1M>ehk51LTMD+ zNfD%NW_~p+tV9+iy7#I=EO>gt9PXF6xus~E(>B;al>s>P(Os2-MWa&lT;wVJL$;rY zojgEZ{{`mxH(2)<g2%$i@t=$x*1x9O{(<ZL4>3>H|5t<hU&NLEMhE|wjGe!C`A6jc zUocO0LPmyv8le3X=lsGv{|yZNhspf^mm)Xozaqu|L__}r`2FwD&~dF*z+cSxMKvTh zbvri3koC4p_$#ijF~M%4)9V9+b|?-UKu80fktB1258Gx<VM}YObqT2=&bL;m*dI8~ z>#Z-!DyLpo(pP+;*Yb46Ka`o4Sh_2_sh(a{eu%2jIoyliCX<;c_YqOdFiDk?G-*O4 zsbz@}`j@UQcLrR&%6d2;^Tdf_2X0mzB95BYm%5z0J0ZW3H@z@ET~LMo$)gHD)Tcr- zK?2=m0beIdrax|7W|Fu?k&oCS?Q8X{M(_5F_;QX8GtNXx&<0!O45JiLHc6SA8)KeE zvqvNEFQYd?GK~rC6SccfH0G>Q@3V;C=Ty?CHOn&rqOGL!=RxQ(t=F+tUv;0oxJVqY z$hc)s3lXi|N384>_3&zDmKI@yZA|iF>MXvWUO$bPP@LjcaR0{pxDHHvD<$G)8ZQ%1 zEYWue;K0tS-<b+=pN%%YbDe$bu#BHQLgitd-MqX=E%&l`JkE49AaFMd2&9ZHLJDG3 zpTRzxP}Jn3aXx>~%}A@78e4cAFW9iKip6R??Kkv79-@Kbr}$=iO%o_Wb62v#YkdOl zbBDS<zX;p9(?Gm2cgwv{qqDw+4$Mc;X<*rY`9z&7+{1I?@@@3_B`##9TV)yy)l@?{ zzS!Q}67L03U4r@Y%SQpP+lGIb=eJ=D7$=)mdRWSaayJu2ToDyhYvmgH7Xv#<Ls_+X zIanPzZ9Ly)XYABPL!UVj9Yak)^Oqb&{oc0th83?3X_G|v8rQS7%?BI_@+CS-Xw)i= zXY(X&7;GN!=y|k_w@sJ?5w@1i0Cm}FC(oKOzR8d`aUxjb@`tk=3O2gN!8|RL&x4&b zELg<8^HePFs)KdctXn^{3q70Z1q}K(2GPWk*)uaEVKj-2gtP+Bjab}of7W;&YO8aa z?Qxi5Txr-?8Ur3~2ikwbBQ|P$or*c#rnj-NwI%Xkd?Q}lAeNrJrUyx5uOgDPzboo> zrDt;|XD|OzjlBDVw)C%^K?{HS)#XiEcm=whf5mY)-Q%XTHMiM~F~1$4V3#LZS-LvH zc-rCHZnFoF{<L;&a-&VL+rXM09|t*+2{X#uoS6P2E~T2NkYXH??(`-xOog4V$d$7# z8;FY&tVO;_nX|pic3Yt1kQaI6{3gM;2I1#Q@-x!m7Ce_z(X+*kb~uO8bohNL`^!cQ z6w$`|i5Gq<d0&918T>cKwYEi5hzQyOJ9~KrQRXm9O3=L&u<Fa~!?t^4@!`6jheXnc z_pHq+f;0+~64<;j3>tzAdf=_Q0yRlifnXEp)ts#k^_@A`6jlnKBmLal!ES#fgE6xj z78|&~ih1l%b(yR2UD4(Y50~K>{K@*-Qxom!dcivs+%pX@33L|oZ8@?JT!xtjN`g=e za)>7I8Qg&Bx^kGn-mzfqzP$3tGKgRGrL0z`M&(IAm}eR(kdLTI!yV8j6|tqKhcdQb zEd8X&7RqT!R;31m$=hyTEUtnUoJ0n-T8ub~>ln4#QzXzDoQavwC1Lk8h=Lf=`6ZsO zY8caEeAh*Ix@sRbiap|*{D>oGtbL}Teg2OUzF(~A89{n_RM5o?7uVFVzJ8I<cq6+X z+T_&wVtcl|lOp)NX7q&Af8d~&%v}Q0=og`Z!uozOe;-(DOF>tSvgpQckU`NW0IS(d z)D=(_g$^%cTz7Anlywx~b2M#Hg6HGJ$2%G)EEyGkxIHwq$R#chml$!hJ8}PVx3R{u z$?%LbobC&bTJ;l>8ypC=X<w4fd$`oNsrGrl!Mnb6e9pV3FPDhpE5W(9rHnS8VSd*t zkmxoyEk4K?PVMmTe^GPbKH8)+v}Y)OPs=3MGkmb+QsFz@Jif_yM+6KnS{QqQ;oC3w zR_}Tstrklm;EZc2t<b6uQJmsfz@WP26gkBhI#b{S%np^A9qj!G9&s-5%@$PR7X$|} zU1bd(H;s>%8V6IHW;7NJ1^o~d)Gk|AC#ms$tx`co_QI^#EfJ}=QnPza(kjA~Wo3Jk zPdq>XCkFG@E-<IX><f#wz~37M=wE2(^XCIopL<PL5n7_N(6FOz)Ed9MXrTOT7c1wQ z;$ITN5ndAR0!i1YOWun$Q%@3B$K5UTD#OUX8^4d26(V=&Y%<liBb2FT_nyBnZ##ny zzL_DVunMN3)B=(7c9((XMF9h^^ug30lKQrRbSHU*dM$&FaZWwU7tFpM5OtBS^O8}m z0d;J$tkRmI8~a#`P{E=t<qzIl3MMM)S!HLcg?}m%JGmdWEPI)!+Jre3Xx;<g0vG6W zE!UZA(W@i~?x`Y<TAkcjvKg=anM$xGwb-aeJpQRJ`OB$wh4ovPLv9)WX5FnRY#l%f z#K)(j%7<t;yWN+CXEX=Bv+XGnb8{~F=*Zr;z&S*}z-8&~jcAP1wh{ge!vC)4!e*92 zryedtrVn1$B~~&)Hu)xSwY~wiy|hOr=E-jHWP{hMy(~fcNTD?_^9NCi>`ZLW9|$m% z3MzaQCv>ny<$^f{((mZP<0bscOHsHg-wc!uFwKYWWQ+pzQ+^PU9=Z1k9GikJQI34b zh)2mP4^a(-p4e15bc(S&k`WSI;Jj6*T|{R|RERpX?jG7m)L3k;IqA*1iY>0y<+AP* zQK{&1RAKHIh3AUmKm$FL?!zgG6jOg*wMO0FWY)xGOrN`qfUhNzt3<2Au!QVwY-hRB z$|HbFlv)_*8Zi0Gk$3OXAS#ZPQaOGp!yo|3@Y!0dm>1f_|FH`_w1cRZ7$<(viOrdd zPXEJ1{49TuaNBv%?_TY_^j22lWc@9FAX2nj1|@DpVv3Nte9YK3D+CCl1ZIrzkz<@8 z08a0orEX@1@-j!+fF}Td6j-XVmpys8qvMHL=?>+4g3{PSbhV>0zKUA`uS>%S9Xa0T zc4-<;*s3D_wB`Wk80@W^g{&IRGkW&qyoO{=A+Zq2%;!n*T?aW8+9o>083znr?#LgX zjEXdv4>;7u@85BH{-oVLI6YZ@VLU-z30G|s9B7AU{Auoj%Y0Fc4qoc@t?swS?AZ}? zDb4^qHBBJx1n3g@-id5dK$g-R-Beu>lApI!>Afjsj@u;QV4<ri8bugAtRWyUA~rpw zl7=|AEUs5hYLAqtuOWYIAaIG5*L_xW%>Ft@Xyn$)mYZFI){z@TNtfjtLIGx2B&M19 z!b|aX6fgE*0}j=91;3OmReBVawKmt{UlzD+p3~Ixlu}dQw)?Cv{rb|=?p2~I=tirm z!C&+*U|4I|mOYz5Xnrk`3AS=l-&W8`+Xqj*S#&O%sv<?OoQ_LDQX>#)Xc#N_TY(Cb zyq<~*(C(=A(s3zVvID_6Qtwsgze2s}H-wzl6gMKScUNp9(YE$dIf?rxWv}vXvOL~f z4nnpI-k#(wrZulqE{UANBjk%Wjz;L@nV9>bn8D??c=^nT;)Yo$sUT(Mj0=!6;u$z1 zi~ow>p1NM&*^c1A1AlCpM&#9(Rs%q=vmce3v|C;hU_3NHd$GG?7m6Q7QgfnUey8XO zw)yPP02ZSxlEuiX<W@_Q3h1af^zCjSkVl8=4{}<5z%P1naT*TYxR4y7QzFg0aZ!_> zDlGwGyqXMN@S1IFZPV)n#cVSawq5PA>0%O46h355OvR=Nx=E*VWQ<2~WQJ2h3CfTD z68kFlxeXYG+7jBSNTg^6P&$Tg(X_M$s&a%@2@_KA$IUdU<$mfXC|7#jE8Q;9bO_y* zMp>2?k<X~}@}z$M{b4Lo9V_4#jsGc_%i$O)tnGJ4N2>PG9CoAwx~6$a^UH)s;FDiO zCKRqwsRUS*kKv=|6_kynOQQIlff8B_<lLsxt_U~<tKnKg3X^g~sxC@LktTtO5r)-+ z;zZcm{}@b>c47rsr(x4mL21jXrfZnW8j1##7q^CBmq$&p;7KCLQrsQ{N#OE(adwfB zu<Dy_;vAR`>577ezhJCzIB6+Ckw_f|?sZ6?HuTRJU|M2~2I!vvf|s-%r>0K(!aisJ zV{HJ)F#R2FdkktW#V{<;1~6?y{%-1pKDLWO_%|q!brdxbve%GfF<C~H;h6xCg*5h8 zMm~dY1t{yn*~)5s53iD|45D=7BiqgfDy!dX6xEE$$+Q*p%P?=-r$0Xf(+-n`Zem6T zJQaJKC~9OUrJE2aF)m2tR|8L@4U74Ndz_AHLgl5Jbi*eqGJkzY$~7Tys=l@TwW>2I z(}X}vynrd$gg}h(G8T9`WvInf!AZUCKAf3g*B+mlZiiQ5-4oZ4Bm1>$gTBXxTeQJm zGEpxV%Oq-LB%@uAVhCEq<AdZOu(xl_$jltg#;yK4U<5QM=kpN=*MfMzf_L>sa!@df zQ0uO3Hd{VSl$#Rf++eD(kI5^zHd{Us+;c`@i})xnEl$p#k9DDi8;XpOfof_hWYpKl z2|6<k<7PM2JA(z?@znX;SJ||Xks>t>_<)VefzeGW7QICpF|?ADpiWYLs3y0^<ok<# zd=^SryLAUA`?3@qAC+Ip0CL=)qdbO2(ci-1ya<k~4ha@<a^Q^=$V}SpQyZVT!($EO zFK#iQ`cnNt`m`2_{jz3k`%1AX!qM>Vs!R?P)#l`GDRC*nVJ7bGOb$aezVec$y8dS5 z%po{dUY1PMQ%wz>QLc(~4isM3$_33{!7`@(7l=xHN<<EUdq7;2J)BXT@k@(`24XGq zK`~NW(LB-|EzxRIh#o3%@SebZL!1x1?#V1W1DAVuBm;F$=NS}l!;a4L+WC*5jG29r zWp*LFEMYOp0RtA7Ft_>@HJu<dQ>w>wmhc!;(Y(-ezQfpb-v@V=VxRsW-rg}tvUS`3 zF59-PF59+k+g6ut+qP}nc6C*C+3sThYwtMc+`S{tiF<Fvd*3g)R>aDDGFIdhnK^!Q zj5(wIRZ4%q8N%Q{*<t9*K-oTZa5r~9*7{o53-Dt+XLR?1h}z}Q705F!e*Hr@o^TAo zu0VybPz9^OP|X)#=jwe&_l}-T`gd&LpQ#llHU{Q@rFmHXneq8gekhiIM0XTToa|g2 zjZB;f7+C%hb@?wp)Sp`Uf6E5`>CgJ(o&T@cz+YWY{}B!Rd(+e(Y~Y`q;18?Vf12FI z`d7O0Pfo!4kCp-d9Ve*P98dU419cxzpZ)9$5=RKe)kPsxloBW0G?bp?HFxiGHWH8P zuAiSChVzZ_mD=4;O-^28*dBDC7cT%@s;sKo(k<AjOg-zqyCt9Zm%Qi4m3)+IR+&Yo zdcGqOpAz!I3{Fpd)ht#x67eWQ!+x~B)u66MbDOc7_}G)Lu31_nm`-}TwDs6X+B9;g za^0kUl%(}h+@(#r4)!SFHB_{T0}SyfglH?*v^~jbR8UCRv#hQm+$kbq7JtjZFHsHa zgUH{y=0(l?=;%_6Y)zX<$Eo_*k{a;#L(mN3+o(TVkJPL*iQfEmuq2iIGpDv$66w-} zQ%M2KlOqMH*w{fhqLcWxn__^EQyIhml*yc-ANRdJHI8STuoDhPRo~F8XA_sxDU`XD zFESxix9T2H(K_CC3ocG0=z4ff66qXIoQ4EnFv5QMX|11|I14ThtgNh8Hs03hj6ma5 z-0a;=)`nBMi(U2rbFLSH?At;lAjX--<0-}P!Mti%SWjL*#7s2{To;L)2+dj{aA_UC zeFR@WG19IY9j(?DzqJ&a%<ow`?&)rf8VV-$N<@X<?*Ix0t_C>pt!>GVc9mjM*dX#^ z?5$FI;dO~Qh#;6-M3xsLudI{DZPP#YN`C1Uph?}<##DkDgt;S=7H|SOQy5Y{AD!)r z;^m4Gv&%pwgX%&7SO>s=GmAyt{{6an^l&!8P+t;`j(%AlgV<Gv_+6b^&1iBkI3_S0 z&hXnbQ`#=c!Z(wbtI^AfcM?zEv^^2syhZssd;|B9Af+MM6i2^@`zt@`+uCnv%>18o z4lwZPLBBg1e=*JA2;Z<Y?1*N5H|ufm-OkEs!u#$78j4t&!;BBb06E1O3m*9?Cq`AR ztDT9mS9y+}y%pw<dDmc!GZSlh@GHTYU8H54IYv;<%$^{#&={o8CIc&`4+XM8@a#=D z1x%KSKr%iN(S{VZ)oBfpStJ04naz;%If0YF2qe-(t-%7_|E^<0`5cJy#v-ig3eW9` zs((52ZelO<Tkppqbr9fZ{kN3sh}E&jU*W4M%4Wh4P53oQG09rCm5!Z1NBg)gWJfKv zR)AD+GUDHWj|vF!D^w&<rPTmnXj3K|YD{uG_2?>hQ{OWVQqB|3aD1Xt-Q4a9#_G8h z4bA2ZH-Feg9OMTWY2K%=s+Ace_KWL{jU5_jRRQ7-sk?&m9h-O$U`<ya9P^-A=kEHX zRDNVq2QT~7RGQp7Ygvoqo2DVIYTHa0YT4*P>(<vqbcis6MrK2L8H=H}7RYqM-{my| zgS{KM1Q8tEdR>fu%A<~gHFPp_0d(r%M_$f%I&yxp)o3s!RS@C`7U~K*EHZIE_wEPU zZ{N;w-#YR^A~d_#x#&}(Jcm_(b3NZKFW(?jr~+`n^O6YQHnBXK6McmS#eiv1!GpB# zu5C&1yUOO3>1IxPCzWb*_Qd0^_rY$zRw35LV+l?5XJ_7Uqxitm`?2LwdxCgogAia^ zH@;2u+O)~6g)RpxTDN|^)6cobQf$3P9f~>8T>wJy5|39Rl3qepO~Kt(JDfvd%0!AM zT^P>t5`O$<fPb#&6ysrUhbp|Qna#ZES!-orL_7w(zp@NY_<c5K)*;By$pSc$&@71$ z_*c;zVyi>&ac8Gd_{lV%<&f0dkb^IUEZI=zYESLN<3tdRbpZ8kAOIBRR@jVs_SkJu z0fvm1S257VJ7iFZo+jGm)|<=nme~IK7*|+JQXEi*d#_MVg{6=UWiH10k5IHLza&3O zirgDt2&Fq~z2q@cnYr8@V@2x>cTK73IWmbv9q9+{E7f8*K>4hEqPz=%<SAyEoD@NG z$0Y)UO)~`?w+u4m@OowrpvjrU8yuOhbW$p|T1yeg5)CKXJrWtGTw*puMDh4{63Q<R z#5hI$5y)gZ*)yR?8-q*_xP-YQzGM;hyzw!SDIv25mU0<rT^YJPgG`Pn6Z1NtQ-@!f zdImRSP@F+{>Aib~wOL)gLemQ=vj-An!zaGQ-A@$S#q5Wu5Hh*bI|XJBfM50*IH2XF z^FI!P2s(V3W$^lV1IXlv6t0A5O_WB_9lFHHbrFt$3$567V|fsZ6HnIaN(lu9vC8A~ zljS=aiErp)>p*CMQXgG_0zwdeQah{6DsW~HiHz9>Va2%j*pHYn0=c9GLKkeVR0A*7 z!2zUSI5L+G&P$8LSsd0>2(>ehC+yg|tLgis56P*ua*@Fu(M{+%XF0fiC@);T8c%@x zOyODxGC^`i^MkenVDw(MsW6R)p=3y;2_rJrE0TD3Yz0c$-gerGT|XV>6YQ7;cM3F0 z=#uA508biPU?BL*oARmI2AfNfSS);d%QK^fuJ1cHF%V})G$>T<c!5vSJJIf(vzfds zdk=Abc&|-~xT`(vDC@tI*XD*T7rs()df-Hp%_OgO#cSR~k}_LSXI8#H$S1;bb2Tt0 zD~k74;7>M$5bRC&^-V^4LJ0biMm0=U){?$kt|(aGD!{B!oF!*z{7QgPj>pd15@aD# zsJ;mJ8NxcG&C{D_gJLO^0Y-R;-9YlG#p59*`Utu$=8WPTQDE;0RgOqEey08%t};u& za|i0}Nx~$<gyxb0Z-SROf7HzsOBD%zlz678O;DM@A%iDxSVSqP>;O9-?AAWRYk&eM z?)$bkfmP-6CQ=vRB}yCL3!)NO$A({N*fY*93K<ZV+~!4W^fwlRlFL4O<Vimpsl^V) z)jX;d)X3$tzHaLKzBv-08k;nI0fcV2FQ<~SjJkvNTw@0;F0Lx6m4F#+cUof%US2Xn z6!2O=V0kX}^Rtjm%5`A*cH8;g)mVIDPI3X9=h~O4PHL77Ih(wr_nW44Cq>71qh)<^ zfRZ|~$kjauGOcQhX66JJVMXU3nz-}8ieZp_oGxw4{X$&8XGl6UiMj6enU>9(sfC9* z&^ZD~Uio}S5%LIW<O_;c-;|(;oDe|gSs=};OKS;?*p}=Xj<^<N6B*FDvYCI39MYR0 zlGBV*1?lQ;xMma~TE^)bDnBs5PoEpS+`92A9kb*W?Mi889XhOXBJeu4nJ8f6=cTUu zpFmkJJ^)-a3k*)Gns;>O4+KmVVs;mim9M|SL^gP&l$7mW$Xd~f)8C{(Lug;lB@hI` zFJCQ%jeLL+!&W9Dz?sw!WMpC3VY?7VbVjnZ-1I@~c(iNqL$JcEtqHlAgJ7bOj*h3G z2iMb&m}&3r+F)c}G~th%gORF5y9-sLod(@+%+=&OW}*gF7&mIExaAUJ@Ae0jAs?n) zWzDm#bZ}J1Cco6pCgLDMxrPQ5rcmbE?iO_HLEe0hvTex?lycFbSKBu?@A~Ie@67{a zSeSG$r$u^7ZABN5e@#0M$$b=WdL{C%$HgK+Z#t|vrvM1GMhu9^4yAiz6>*D_iDx|l zR~=<ccjKxEcO2Epti^efVFd*C^NEkTo^qzx&6Rh?E@Yl-p_CIR)0&gns4=tgiqn^- zryxA8FOVZtt%^9;z*<ZP&QXb<&r!)7NsSTUiZ>5pel?e!M6hPZTz-Fm@-<>Wno)i| zqMeCao1GyKdb%$wDf}j`vP2?aW6>#qKwk<U9b&$2#U3#u_zkjXyu`*AkHrsN@;exQ zCRWu41Ub_jk0rvZm}Lgg0#1d2sy{HxipOc;W1VC+i8&S}5k#RvkhV6Zr8$w@(wmOX ztp@JSRt#EfsG)^W=(_70;M7(BkQ>D<UZGiwa>o#gi;I*+U@7}DzeqS;wc9)|<(&Ce z3zCA&3%da!xGfUC9@(@+aX2s=G7tYo9>lrWzG-NZDK2Py^UKtbibZd)=<;@3eppG> zi{q|XlC0RG$ysh|x_?8d@vA)1R$C%OWo0Upd<onv*f~nM4;^2U704=}Ql8I#dZQ#3 zEieus#*c`w5DFw3Qp7Ct0w%VfI0V9t+lv7B_ysr+iClO7J?azBRiCcXZs9LM;Ak6x ziNU_x`~qc8R-{aj_1Z68#FaF%N#P#xLT1>Tp_g#aaaHWXOM4QBu}AzNgVJ%Hb4cjJ zpNKj$l9k9s7!M;JMCWTn4KM5)>(@-Bc+5C7!)i%*DJOBQ#^zj4b!ay8+PPVqNq!8H zxN~cK+H~1GLy4`OiyVpm%=exFAbNTH8@9Oa1{5xZ6xW|+^fn<f$|r;oP-X=KFiFC} zcu5d!$f%}yBvcQ1!WHRd_WoxXWpjh5h?!-5$2|CGsAPy<WWVyt=DzNN?bx60mWdG= z-vfuGQ3kK&jmE%$3Vb#+7*Bah9tT|-tfju+HN1zN)fiE>#E{RuWfT-V$ahHiqz0U< z<HAM;xtA^+?x;wG8Tc*k2EnKv%j~oS^kO(vVRKv+O(qchIIq2pezYj->H8_?Hea5v zbL5Q5DF*&oVkmR=9Ym>p<4aDWguqdo`gIw`1xf@25{HP)$0xL`f*2q#lN3l%1vEI5 zeynmwfn;IEib!-?p1TzmzjFsRDc>!pElMk-_1znPlu|Z_pF7pLh%1%aXaa=pCwz8$ zHQ>Kdzkdp{KPu6GO9Qd|HIwu2D&zk~{j&Ze5c6N^_fG}*zf`|}uJV6f8UL$%{agL} zZ;JO{Rr0^5fmr`a2L7dM|3L%(J6+qXZIejcjQDw_$FK){DAT_selP0>kAvbusSDJJ z%4=UveekFw?Rq)<qyu+%d~aT~67>^rHfkg&;G+8^J>|i<vUB-tw*bFjw*cffilC?o zZQ&r<E9HiW-j;}qHhP2k=iGZ*v5Gbs5mh7A%o%fsS5(gS2OSaOcDL?34s9>5^|f6= ztcixm<hSSZ*4WH#3zv;6H@5lSg3ewsy=@VRtQD09J#S^^F@Qn0Mwm8}CLiVAR`;kc z{3HiIPdnuG^iEOmi0G^L;iN4lf=e#Qua8JBfoo!dwfgI8s9Bph6^sAsuc)cpTyKA0 zXcmb3a{62%s~$@q<=l}nEMF11;vw`aU9m|sz~7x3TOSpsYtO|6Az5ji_oOr%jb`c% zL=pvumz$-fAA3c<?dmx`z8iD+A5X4MYql{DQq2XPQx^BX9_l#Pdw(@?R^i3wHbH+5 zZwF+%D-=5<tkkgk&J0Om8tJ_rcW<Y2h<)WZpW)wHLpLAK3|!hYylILw(`M<lJ{F(K zexE3PJnLRuv0d|dw*Ed`oz<h$+j+0%vhnGS6dV$a7)G)9Y}d$r0;d*I!}<{WzCaW9 z*qt?&sE*<|wLxQU)1#9OxA|Pf{2KdOM2M@+Ke`VSfe`tSV2NUZI~ml}MPD(Gbak~V zQaA@C@q4q>Oz<b{s`k(pfsZFLA__`<Jk@D3G;<h}f(RW7WNDi55`rnQis=E6GTA=o zW4?HW9qTnKJif~u7BREwsc`zRVp2~Sw2kHpd?oRN`ep=)EEW?f!({`5Esc_~V&4v0 zkolNYqE9I-DB;(bQfVdiy{*8)aNiv$wk}-8$^-6cZi<8|1#8;X8`m+UCvG_&ii8N1 z--ii9g--ePH^5aABDiLCPa!OyKv&E4EUGiiQu?C~pXQJB>LkaLnQ#l{6BP5tsFO7J z!snKmyu6{UtCpF8;y|n$CHWzLoCBjabq6#}`xBK%wKQkad!AOcaI}%^)7Tgx4mMBw z7lwygWCDvg!7j3bM-*+~J@&IIZQ@uX-Rq>aaHx}*wY7vZrl|PH*f=3b^wHZm<(Y4j z+0-uo@#Kzxf4fq46Yi6>Q=WLmr2>~NQ}c8`83XXwBM}FWjhD~nkfMb1TNm;Ss){yY zQqhTYsPjB=mMqkqv-&ESpGV3_K>)rf5GNMULl5CEalqr|AU%z6Ljy5m`|b%Oy5w-& z&Db==VDWt@0i)e8(D&N=HuO;^-N`lzse^K<gASiZpWqwl{!~}IwSGZyVH_4hjKoB& zpw!9G%Wt#FOHVg-9`3gvjp*>tcDmm9zuyjF1K7_K>k#de4YnDv0nA*Y;Z>k<0jNv0 zU!YRIzvI+v0G3LH3JGJKyI|;Lyn8+V7UJ!R6kmEt!Z}rdYr4JFp+J(w#-v`S0N4DP zW~w8Kvu{|wFQ$2}pu_^#6B$xYr+n47k!FyJ-Wj?ivNtIrKB}}zyNnkf-b8m9B#p3N zQMNF4>`|7YiY^r0*v{e+FK3P&dQ6r-t&$%23^xoz828Lh6t&$g5LN4he}4I4`Eg*{ z&9}8nc<ZqFUfvLfA|!YB9FLsfs?n{~W7Ix+;i@@UqU?&dW9cJ4gsB@(<Ijpl@BH3V z75~DH!*}*09T(qqkx5-X93>K38j8uGN5lUmhZc|WF6kvD%`QMXhZ>C#H@Bs;?(|3k zGgahj4@{OVb<9zj7X#9ww6ZaI<ClO1b(%_A!`M4SpggoZ$arzVOcV?1K+wByK2Pun zgOSskUt_&9@7&rjeQyQS$PRcxco|N=!`tW)cVRwK5%6B4cqIF5ef-Qj6jOKjJCX+~ zIlF{T-Im`vD1J^Y#RB^zYA;Sb3=+-M$61r+_3oAIgu}e)k#82ye7PYFkZQrTyAmA$ z&}Rcp6ku8w)m5CI%-B(wJ(WVojjYeF%56_yJEm^xjK$1pxbZ!fkGrOI+)QiIi!;Oa z1`eaQ=jgrWkYpb4CGR~D)iswyDi&;JPA1uVT&Mj5D-Bp4b}3)K=I6{+kYUM+MO+mN zgt26XeX5;n)z}pgY^h4PEg(0XB2hcmQePa_^UM17CHw70hS2aKGIApHD`&`U(a1r? zGaTKJ^H|eu`tzDJomepS1D#N9%m$jn;l>=;PWXntVyW!;r77OIrK;mOCe~0lShW<g z!mH$s{A&CvrH)8mS{4V@VIYB2xXk6jN+YNCn~L!atm}g^pZRtk^Hu9LcB-p^Pjbbl zH_XAe@X34}@~}u@YJFRg9%3HzVeEKV>#XI16aNvD5R+T^9&N8mf>0(MB*)4tLm%zS z&qV<oxh^u&Y=Z;!3yBDSt_jA#&j#_&ZiTSA@+o}`(zF?$fr{~)no>$<vSyIGrZsJg z^$*NTcq++#dugcM9M><)z+PAa9MWZrm%^%}`9sWkSM@$=BwuhU3tjRa@B_j8w7{4w zHZ`<ngCN*^f=CA)?YdmhoFcGJVhY5N$o{cTp!q3*M^h2DmYz#VniLveSPBhN@!Iix zLNHtbX{57`W<xGSelb`F8La~cDWl_My0~LkvvVA`=ECv^j%LFzfhOa43Ur>>6dGp) zT8*Ofg@g}b2wIjo7K?BzD`A+`8){q<sY$J$`U72(SukPUdvy(cO1~iRC7}+%>%{N7 zB*UBRG6t~?>q`+7M{PiUUe;@lw4hK5yDWf^VwMLL_DgaGM?=2r`FHT(S)|B8nrker z<`oA1oSRSb33g9PNu+rtku9hw7OmDFA)3CUpe5BfL6Ee}<BLzIDiTOCjz5xTte#{q zw(Q|JZUMVPyG>TS!0aJGT!>Ft!y6D&ozTc3nU6<h@QbbrQfrE>$|BS@2F(^3uHeV; zM5W^tA4(00V&WE;5=G+)i>6tifnQjN4~E+-1iM3d%Lp<ig*7CJWR6T|o0wd9fC6{@ z!p4$5jeL1Szhmjq8<<0BVtIn&k^uJOkJ{tT#sekE@79zs!o}c-X7BJw8k3l|$51?+ z{qM|mHz=!?RBA-v7id(E!f+?7e)=hbkDDFB$So))e)N%89yz2NqO!!9g_v?Aq@-0v z2@Dy>*GNl0VgGSripZ4M7St|^M&T0YZ8&p7LCdfXAB3O;oQ=q&Eou=yK7k=DjdnUQ zWZaARbz*_WpydAwO$D2Vdc-cy%V6f7nv!+_E*MtsKNE&YXCx)9IznLBAg)G6_7?re z2^_=vriV6piA1zCRyP{SP8rbfQ~UBsC7(`NP*YZ@r6emII>nimgiBEa(fkj@M@5!1 z8_O<t6Mt)eq6!n%N--*>rQS1Ts0Z4#D*3_?YC|3FMY=RF=r#cP#%YC+6TnQC#-ApK z^`wkDKdykexgiIP)=N+-%CYHQcfp__OG`u#8Q`D&CH*Q&{kv!g@+qj-nSPz)>v_LK zFqU!q)!;YkvCYHMI^4TF_E`Ly5JitmrVQUFG)R5qtvBSSG}g$2i`TVF01!^&@VWq! z+cI*i>m4Od8Mgfv>Ab0Y_8G*vZReIb`T>`02FS{PCeHVaKA(GD*H%&l<!wLAA}(VA ze@QYYszq6Dl@B%WxW>>AkU2p?ogexWL!WxD?e#mUvCEUCy%qIQNb0O|G#0^gTQH(B z>RN~MT{6eY7}HYkW|C@zWcLE=P^(L@B^C4|kel>$hbxu<I1k~hE$dPBYSEANqKK;c zo{zxCNHL=w{S&%0w%`DwHauo4J@v}GRxI#h#Nka#U)XPJsmh^udAW_l#wJx5Myjja z8qJmG5-u+{;!HnTq?WjxDm&FT`UjPTT$$;dxZ(EW%^i9gL)<DD^I&>7y_V1mV6e=C z7~|Mp$teSq3$@@mR54${Ln(kGO*Po_U_9%@xHjxG2Nfpzh%^{VD1jqQUZ!NrRlrPn zfRg=13Wg!oz{0P<hBYqv`^NM9=_rMsGcGzZ^16e>H8II31f5J`6BeKYODO_)^3^qH zKs7d+$dekWji=JRMA1f+A%95MF*;ZFRKpZ50l3wdA@?zi8p4+O^Y=T^I*_gPUyU^3 z?eQzrpH2~Y#$_b?!!@~Y_+p>prlS{cuSQ3jjQRfnNVj?On175(f{P=?E(VryN?2hz ztF<7<{b6h#tCvE*!KTWY2kwQoW_vf*H)|~aL#)m|#kY%QMaqaQ;EHt{w96UgL=y7^ z02rB;kDx+&5fE*XSJUJ;Q4{wBb1S8e0=EzNq(=?98m>;f2$(j<wQ}^-uWXJE`ckrk z6QP-to=DD+2n0RMIWHQ)I<P<jFrXCFGde9sr34APwkGQ(4zyX?v~}Wc1tzBwtm70G z&)-UeyA6<qejbz$UP;w1Wlo*PRrUxGX#5ALv?^F3SeW~3uI;%RL>tRbla8(yR@puD zK{z}_yc23MYs${52M97|PW}bRYvNhFQ)#y_m^so=ar81-PPh@Vi3$&7x*{!xt-@!; zyJ{7XZVWuD3)A&`eFDXnh`s?0qy`XGfjp?j?eB!aterAa?x*~7wGI%Y+h|$fBi1pT z@EOPKl86`H9iPcMc&@zFM1Ot)yi;*<#;E=xmZ<q6S^KErUHrqe-HI!%5(2vM(8A;a z2dEYzd6~cNOWzCejs3_qt!LMK#NQ^b!sk{M-K8(5G$K3x*nuMwCs@d65H`><wo}qe zyQwRa=Z}C>lOh)()aB!bniA_46;tGA-WOONmNJ22S&vkO-_B67S#=nk(J){7Mlka& zLVmS(Q^)1ANJWDeJy_TkexueSiz^Zo77WZrJsCP&sq6RXhy$834qtU@v$;*_zX)NW zy0A4a7d}UQ@VEiXl;&PidyMjsi=m2DIC2Pi!sNkF1rXYTo-t9)Kala4g44zF%4jhB zkswr5Hd|Xx5irHnj7)IZnBIY?%qDZl?}E0mgBt6>PWNDw04~DzvtqCR(?x6qaL|e_ zh?_ZsX)(tl2s$PP-ywOw&6P|FN!B+fW+tJnI8Yi6Zye+OM$3#X&zpv3yW&pGldRLA zeV;NHm)qK;Ji91<0B`_Q=*?d1(ettTB5YdTTa>E=<%6~3R(|dD7$6<i-U+DNR)6uX zWH~>_>9%0ewx*jN|AZ~rO*pU@=~$z><TKZll3U2?(Qe9>1^pXAt1dKrI1Vb3X+%6d zczk*exOFL;pzy=xMDk-}=knu;xS-Y7OfKT_AXTY!d_`-#7^MxF6SYTkmlgkl@79zB zxcbyP5MR_pK&@^fr5L$Rr+*T}!uYP~rZHw8c1_6$qYoagOvk%$T(*^US$Kw-y>WVD z&Q9!^oy+Q4l;`I|SPP-{Z-_+%Ix%c(7gCen{GA6k%`sY>eC0(Bn6mkAWp;jy@uZxu zc`_ZA2*y5Ba|K|GqV^|?5fDb6@64@L=O0&1+ub&r-jg`3<uZ}iN2R!HBzNX7x-R3k zKF1c1riFC6$=mX+1<_c{m%p-9z3W`V>PcQBn+II6Duv`*mT@Om^Jqm6e?V5u=zWd8 z5vjTgD2_^TV^aXcEMJsfyupPvC-ISMW>ak>$PD>BIM!3>wzUK}mBS23m!lPt06Pf% z4a~2u_5SbJ&mYj@57xrW$oOBW2-d%LzW<-HpMOLP{=$C#(JKFc(+P(4ugQY{Pn{2c z<2rw7WcnYi4-8BM3~c`xdi2%$l!1VO^-puhALfU@5u<;hq`yNVw!b!m{U;=1`$u-< zzlB6vn~s09LLF6`O0?1-6-dK`p^HhZ5O%dL^$r&-xkD&XoI^??xHc^B9@_^$Y<;KY zh)&1ThOn>ilqlB*6OfjZ+m&^byDxMn=8b^(jZ`k2L0wNf%qL9y=}de_FQ>flzQ8lW z3B4#D!8|j-zPfKptGmrDNjN<H{YI|-m#fycPIxNS^w9qK^j#g@`S<!-*2m;usckQj zzm7zO^JkcDsaca@zh+bSIKXt2tJj?B)S)>f>{*c3^zTrRH;IcH6e*1c*`^`r?;m|{ zrw)fk290#NHOC*`ulAxu3)NY#vmmOcaHX|V;v#K_GBF17(l&9^^uBLb=d0p>I0DAC zcCL@Qqn~fLM@d`t10h4xyW3E!byK8qBar^>D&>tYsu(deWPmnGB<qqUR7M5cxD^+1 z1-fEh1xfUZmuWVqip0j~5*w$GP(eSj)iT2HDfHE&bMo%dw3)-&m*#2n?0$7f!cU2w z9HBa(hGYp|M$VS75hIFpT%Xvj2fyMqbp|n9=Z0;#CIZQtMc~Z!7Ous`>yfL3&0}9U zL{Xq8+M<JakV@B1T!Y>GhA}+v8P)ym*++{epTA}L=_by|{CDQQYKe!rW158_oAXFN z<0sGk>C(egy9rz_eRz7_(#@~$-_?As;;gHJHHv=A)tCR!Sl|ylu=ZKb_mJjcfU7!i z4oGM&X?$5iB#K-@9{Nq6D?@uvFN@eR0`J{PP<XMXP<X$(8oevL|LQ_cPpXkdNYQ{y zw|dhoE+6LTV}787JuY1(l-^I0wYg#cv;}1yKUdC#dc+C;9>6ZbFAdv*b=6N@CxV2H z&QL$BFG7zMLqT*x!o0TdDu&JS``48uM$lk|Pd}iOgael-!iO^{iI3Wfibi6uy!mix zWyM-DMDEk*+jI*4ZkH%`z1_K?&N6|G?*{E|suc|+PH;8(Q{DEO46lltjs2%GYPDNT z4h1lRg}An9{&*IAhY;=Opej=bquc<uBm(8dMh`z)^r}W?4^$51O_wi<04|QXoJk)Y zp8c-9{fE2&_~<hmeI5#@qo&KPTL}PIB(9|XZ_nqtb&jkwV$j4sOQvCMc0s51y2xC~ zen$soq&gd9a=*!~%i1gy+5S^r(3OK!Z+EaTLRoCoV6Zc@tdu(1><4?SnysM|eU#E@ zpkpf6tk}F141wxCQ{|~89QK<smbJO?Gn^%?VvxQ|&$xpgWN%Ez%^#-)w2=+SHrdRf z@o|MVoj{B&w;qPUk=df91_{ID!de@GgLNhQ_r^^u@leS)(+V79ws;d?#J5u%i-z@a zg1q`12s25z$p-kcri!B%HBxp-hb<i^k@k!WuCddkp32+4=IncWbtI~<`m&oinA=`F zKlF1%`~~Hh4Fs-uTe^Co^QXVMz5gyavd_Hm8=3~U+py&rMr|4Q0!k%riWWpWwQr?A zI-Gn?a>!_24f9hh{2`nN`zA4m5`9AWD3S+C0{+tt2@o&a@B)v9P8bR9dPC0Cptv`L z{lrgMZ^+PfBoB-FP!OA>+{598yt{F56pqISj%}PC5*n_QFzv!ATD4&~L$eto5|aG< z4g#Bsq<I`|EL1WM5*Eo@WXLQ^!8Nf2S@c@4p<hK~$~0Cs5X{PT{P2q=A=DaRBpSFk zF>v$5Tk8?SFyahud@c~vv#Z50Oi8QA%-@N)#afM>L_@RAN$JMWLwUAFp|8d;ok;pQ zq^pG>z+uX~ZL&UG=krZsAz^Wxz{?ei^Yd)R;JAS-j;}yS$Am-c{ksuEV^Avd8kR4S z;dE@odRR5sMSjR*x!nQkTpNzkuC0e1zysjt84bL%?A+{g3)KhfFnZeBkrm=q$aDQ< zToEc}<q7}B**=4~C=U@p6?;gLZTNQeR3T*518!_V;q(#IIr3Atfoc>!gg08kW%oj3 zMVG9I3F>x{P*9AXv^vY<DX~Gs<?_dpmpm|r6E0Fn?e~Z&1+!U#ACl3D{CQ&Wa&M@S zK5%+?Af82O9MC{Hjf?#HK}7Pur4ddL2PLo*+h9PrB^;%AZ#@X+h=)DdXIpt7)H4E4 z4ro1MY#~7)4wip|fq52138Ca}XG4NzF{tmO5BOh--{pfBbb=tD++k4xWwC=N$@zmM z=XXkt8(;TXGp70p8V-<%9CFC6W6!uhNYDE&qqx6G+9ECM--3qjV5X%Kh64`8Dhb*^ z*z*I$)&&lLQOr;FWRl&&__;>&?il689QzAWKP<x*cNdC1I&%6bR}(t?ghWmPdwMF` zIBXa|PiYISPaaloK38oTKK}`&b~#cmK$Ix6TEkOPhK(t;#YzDBC=Kfwd7y?1!OY1v z=e4gq|Abas>9SnTe+~bbKo`6bV5{XSNsn>7m`T2Bwn%Bq&nSD}#>dn+_!Etb`)g+8 z3KZmVXrPs^BH>8$kH#pBo0B)28IhyATW4DB{j0%};q38{fEb7TdvBq9iAgn5R9j_Y z@W_H4wQs%Dt>>0{s4QwT@WW^tE!72^8=lv1u%Cl^l^<TEIajdrb>Jn$%)cq%@!Kmi zAeA-8>1Q=Mqegf+?p#iyHZj!*pD7)K9_u~f^s;asB^aDimB$WHW~F0Zzjiq~so3`f z@BJ0=7pS{*=?v<hyp%^*4L{AZjv(u`G=$j<vla+kfuWC9>19lc4j?CXK4T6d8^U7N zHtu0let@<IP&@tvIYj;`9*>V{6?EUBrIw1mgK|t71o4D)Nh`M054pKpehDb(-lvy~ zF?kJyD-}pXClg}Lc!=z5&4nSs*?$1asoD}AFD?JXi8l+LNi}4yJ2k;WjGsiwnG063 z!iFq1-U6lVd~HJP{4vqC-xy!GT5Bu9CX&%s=R%2O`MoI6l5r2xQ(7THSPR2RG@^)+ zWZjZP<n`@xpIkDSmf&|a_tJ9Dnkp9?uaVv^-G<bPP5b)QuHBB!+@xEs!k`JniHS~^ zPBUAD9X?k&T4)nt61DcD6pYJ5KDYUZ>=8d1P=KVptTfcRUtiC8{+1i^k&s8O;kl`_ zrR?hbZ~!l*=~V*nD8_K(8_st__S3`fF)2TXzsK-Wt_+&UUzx*Mvg7BN_K^s^uopuQ zblErpZH!RnA{_A~#VU0WOnFh@mwE@_j5|dOyybkmZ6Cg0y*v_X-}<fX+qsQ{Hxua6 zgXzen9d=$byGTBA<Q_iIKFcKgf)ebMYnHVD3guRkRM${~V}`TZK6|j%0q}KTl0Eg% zHj7J-J+~e9)>b<~(?^=RroS-q7S4g<7Q)&`0t{|fl~$0g#jt^lTRfqdV%Ce!Wg<MI zU^g*O6#%Z<v<j(*R7jto?=L17Vu{1fZr%Ageve!LrPPFZz}z|e+eomfH=b)ZHq+%e zX!I&G!<yB<$%~syv5{QZ1P+rB)1_H5F(_jABwok1-(#U%s6H?or;u&fqO`@roou9h znvXThzh?P$v}KB$<2b4*iY!wm1$w>o9L{5cBz}4#E+ks9fCvfbJj(j1!GQ{K4YI3H zY~aQJydZ$`{Fs}`*%`E|03TD{hn(W+U40=@ol#prYp%P=%(5+@s-it~sEbjCwhY7w zI$zek3CMF6Rz^!c$Z-~erL7ItWx}&)Df?=F{s7jf=UymjA9Q;-Ap%XfRRQWGu!kdw zIaFv-G?uw3Tr&XdW&Y+CSm^1Pi&?^g00nvcDRJA!OZ^I-d1iciQD6p*+vapZV=i>I z(FeO3bFUYPa+-U6KFu^jeDC-Wj4O~jJu(>kqI9))orBm@i-lIkGaU2xo6uKCmT!J2 zP$%^HLHrxvepjq??P^lV?!rywhppXlW*F~pf8g)-YCr6-&Ar1U7To~Hi_>=s<e<2I zNXC8ut|C}5Z~MHjOD?3iGyzAheA)oQNk#4^vxE%UW2Ic7z)3V+5w}Fk0{3jG_DB{X zTVXvm8y1dU>wQRv=8cMuJY@Y?ca1!C38s|(>HD6H9M0NbKv%VYc{k1D8!)aTHLUxz zp|_cu7fR!(w;W9s_U`qL8Dey9qrsX$4i<qc*UQ4nucs-qu-HQLzWGKA7`}Z_v+;3P zkPmjy96+kMCS{PEYjQMOZ5{jcF|O&)r!e6QwjlXjvqjswJlJz~&5u2o#hja)vqy15 zgU9Z4=}epPa=&xI(K476BB)drjnDK+2G$C!NR<lLu?8?(asuFrRD5&Hpw?y>z<!pU z4BP&Ily!hB;}y@*s>-_G0;XXDC+JEIPL2~|JYfHM)LW;!8~1O-;-BK>Pon@M{eRiy z+5XxZ^M5E7+5RC7|4S_Xsh0kiMu9(9`Tx@>@HY+lHx2k7i~?+bm9ziSfd9jwe7ClZ z-Om=p&l<gax#5nsL=nYWnFaXnCK2c+b9>SIe7b;XPuxoN>(?jJRO-3jjopP%qEI9Y zOSOWPX+pQB4NrC)I5RvrmV3%ix=)nSUrO|NsVuS2=fB?Ev43CD)g*TO5P1{&Fd?fb zlGe~RRY_PT>hX%PyY8t=8+MDG=i48~f1VzcdCy5z>H6i`%E?pPI(k^YZezFaJL2|b zx%Wjc4KJ(w#RO(T%h*S6n*!Jxr8axlc)~Fg0i|cNABzB=kR(AHnrwLXjY&Z$pv~Aq zksf9jibzwy5W=xn{jz1$)Jv0ZOZeI&XC!j;<4R+Ktk-9~*3K|zG%`1H`&6G-=d-D& z_f)w1!(RXP3_$+nxGo}+Q-VP~K2^e>(zMu5`&X+h6&Vr5l|n?CG3KYCATQuLwRwFX z`LDv>p`gePaVh~vbjd4`dw1mB4`|H@`ldLc(m(>0U_wi93N`*}Ao@xonZjXse7%fI zy_C`K_mQs<8~r4^ja0#8F?zZyp+~#&;Is(SSod*G7QgMxkh*gqk5QSwQc>J{psSO4 z#p>^xvm>>jbRg0Kyqgt7U+IKdAE8;@6_C)|I&Nz4xQ&<J&ETswl+)6mTuriFGzroW z8QH}7>Dv&H)MT_lAVC>K*4P;pNH>*gNHI-CRB{ywR(oR!E1AG4P0hu>8oa9}^v6WB z)zwM#%!t73x}e7S=>x=v<Dj}XhcLntCKzh$Y>9|xf~yxW!_dPYYHzz5#%Q(lTaVL; zM|>|_-fk3ZA1I>vd`>*t3I^_g1>Rr}4-HSxlg{U2YMjD7$dFW39)z7=AYl~x27U-9 z7OeQ75F3-?Sfv}097^$t{B(H)RxGLFz85}*ON#<sI|A~J3=py0qz9}1sV&P@Qd``@ zE*LV_+*0(r%PR`qV;~{x0x6;d{1v{DS?UgGqzs{H+#JNch0mt-yxV!5ag3&wZ0F0Z zesz!j4i@UQ$&;lj#_oQ)MI?;x_N@!fLD4mg^dlTwxu-XrTEB*};9|9M@pJA(+!1*Y zk!~gPn<6WwG2Y7WZA0V=yDRk3$EUilLF?DHb&|hCUbf+7xY8*`6UV>8KU}Ymi$`nR z-(q>1K3?57>@qLq9;ic?9UHAt3Bj!p3=hTU4cph}YMD1fO`Cb&kD8rQ&n`ll<BugM zp^z8cRmm(Q7-$8`q!XWh8_sD7I(SSYhm=*JsGW)QcCYw09fZ1?!?8_TLREOWt503h zq6DZ@G=pruZ1yxmRT;jmi!B(vWrxQ<jb8goAv|ydPYM`FUZ4v2ycE!D_Vw1Pjb>{r zYhirUr?B&<b;-I*c*70|uSE}L%nxoB^_!1;VhpfHbd_PoiS>`g_oOHHl|~sDwo@Rh z_qJ$6<c>bxPlCP0O54Kd=+nq4$}?ie7o4nuSq5oG8S$%rjZE0GY~;p)(N1W=M#f{O z+@!s17Ef^k30a85p#8xx0;ZG_c$Z9vFnxy`ZZa^EwocSw+77Kjpgf+IZ(4aMB{qZR zt=z%XjRMCp@Tu5{+gQOJtYkokGL3F4AQYWe`ViLza7sJ2@qm(AdJyBc1@V~SysD4P zGBp)2fr|3vygT|`D(9!qEhkviIzd%cBF^1eKaR+LlnZ+ddKPhwDgiMoMtK9_tR;;D z_asR4MDUiUBstvlyJtWcM;bkHoya#u-`uFw8h(<pVir$^Xh@G|+Ha0oEFz-tR+9>n zk!Zac^eiqJ2EgK(rNC>Ug>Q(W(^gqy{l6c666A7kVyLl93kJmrEQXy_9l$^%ili%d z>l_fs)gkx`@!9c|yRk&uF~H67h$^A+JL-0~i$Ff*bZ?k8G0kX&_03ZqKBCeHFR{}o z6Uc9CPCUS^Z+xB-AcHT;2*2{gy}|_PFbFwbl1(Ha<K|N%o1pH`3C1|uqL|Q97<Jer zu_VYuHs}uZJh#~!vnZ^bROd24Y?MV6RH?OSD6B-T?LwlPS{Yu9t?zq7%my7?*?UHn zj%^ck7QxD8LRQ-}W+SyY!wrTMOk<{KSUTxpLgW;44lrZ;M4=kF5u#@PjGEDD>I_g1 zhJ75V5`;gZktL|C>laUk%`U@fWf@qsH$?UM3CR$MTzNxmvlEP|Fy0putl=|?ibZ^P z4`m3fS%leDK;>|D#0b07Dj0#4-z8YoX34)$C6ql#-N0Oqxc2^<Ye3BDxC%UmY%Lsm z>j+?hZ%V8I#i#K@b(HV^o;9JE<4!+&W59;Qi2bQZ+}yR&gVr?O03C%=hbQhjbC8C) zpfP(&G3IvM*{3z1;uU0A^SjS8F&KTMteu(>n$LQGY31y-&k%qqgKG<NTCiRVfvJIz z!U+^K)DX>kn(1I{z2P{Gy(=xFB+kMWlo1-*k`@y*%UW_;FsSI{qCB26T;mN9wwe<M z`Vs^5w*_m{JrNdBXMc^d(7{45wyA+g=1dKYB`bR9+7i<>@ll$tuNx^cLeni;|GMV6 zBg$!-j|bNpAEV(OfAo`PBUQ)#-laP$ShNeOG0txPX3NE%$q}u^en>?yEf@*bvlFke zZO1xX!yOT}G0l-lpE_nBa2>#inD$#D(&|o2bFhde^aQ@eu^x;uq#1j4S-mz_R?1Vc zaWt%GMppcjik;67!@jRc_2Fm6eq06#^}RA9dN5^80Jht-q(~+81iop(y@S<)qVS=e z@-=r5?6{k*i@Li9w)DRY{m414vbdHw3{uP_lCcqS;W-Bdg#js*v`b%B^#g)Ws6z;$ z8EQ;6hWWkL5ljg)1ho4Fg@aRRmC_+@G{>q4B5{O*h089VOmTNn3yi)J8KZ4RPTs9~ z6eM7{=BK}|L@vByq01dRFyde&PcJ<MR;ab+tSQGl5#PA>2CPpF^)IYTadx{Kf^m0g zDI9m1Vy|_jk&#B!8Dg&QMB!I+X0D)!P+6BY6QFojT*QN6MJt(zkFCUm@z>om5KG-$ z2IUtxIe4ib9%^(k=_{}Eu(0nIp@@KIJgpe%55EUK6`O5A|B4s1_?mku3qr@kxx~>V z_~R0)YyMhq9Fw(R7pV+9j(jaFW?zUE$=x#*c|@gG;eZ5tJ{3yTW^ai}=mo4MCdAqp zt{#DOl<V?=k-~&-FdWoaIV`5)YXdrr3)5JaSs_6483NRwJh-wL?xotm)!z_<Aw<?U zX7kg$H-I8SV@C+CavScAG7A_)p-LJ08!1Jg(_3%giPF%Nv5}!!(sTz;_dtJn?Qz*Z z^+hLIiP2Khnyy9`r3wGoQ9}cfmewag_44qc;IOt~!RQvO_4H+{1ZcW4({=GN>hAI6 zYvN)wUE+=5!RU`&(auAHJUF(*xb-(g8bkZYZM0i>pDQk!$}o$S9Se{2$|5E?d4jYb z;=S@#Cx}#CK(R85djpkYNKz}V;|w=~Z}tx8PuZ<G5_v;_<mQ)U9YpJ$<Vrct?CBe8 zBTIvH0{9pero5HDDSA_Kx+`y^+n?>!HN|(t$~>2{e6}!jP&R7Q`LqD4F7oa$<;N|& zXKS!_y^S@{uD+_lDwE$Ex9}3LJ|6om7>&yjGL@wj7n4NBORS}0u{B#zT}-q(R-z_a zL{ZeoQP>W|;m$G)G!9jZlIgJUwDd`DIOQVPPL@%2tDHy9`|>qnxbv!l>q1&5!Ze59 zeW9x4e}^{gDrz+=>8tc74?%lA&+5q(S7vosQhm?I*P+ticTdmC58GA*u9&KMz)zlw zDl+8$b~Dp8e%l1@vg9&Lq2GYW^_mZDJUS)<j*C5kP=g67%{!C_M1~z^%2DYz9#X_~ zoD~=&>I_lIaU$Jb(v)xBLq-s*p>hhusQj9Ex(E~K-W$9$?=u;ALW`x_T|`H@EFXhZ z+GAR!N6}g*9?3+P2Xp!iB=^JVC}e@6#n<oJ45&m;-qVDs<Ny{u>xw*@eH%NM;VflD z?OI860eY$715QfDMtMD<BfE)zFW1<h>H?RsxJITaHF0KV4y`-Q0PAv_1X%2$!Z*O8 zNiC7(wK8q}NoLSJahxsDMDv#kmmSvSIB+t2HoJex{EieOa|pxz88Br6zCQNCw1aWF zj1V$vYXMzZMg>8s2o$RG_(4$3Wq~Itc6`+!%|6DnRX_#pRu-qmciQq`CvM|iGwrsr zLaTAxl<&}9=!z_&kCHy?rl2atd}b{lHueTB&a@m~U%3sTpX*>I@HP}sD)$YcKGKcb zqw4irI#CDwp2-*aU%xBNg5j5I(?j|K*PIspEEv@@QPia|;_8M-4Y$l+ulB(_HiiEv z8wq*09*6^&bYA*dk}RO~G*_SXw1x87f~+IuTBbIJ_sYUDC@TFl_x08H=Yg5c>qLyl zj+o4=41eYL9srwRU5Ibt<$~}lalS5;eStZJcP93_KA11&{3JgXsnqpu2Ihdp(>;Ud z*d<R?`4Ey5R~`S*udi509>NT>L%%waq7z&;uOy&=&-JQZNFJTXlP}7zdJ129Dft9v z-5Arbz6f`DtGr46AWs#yYvD!V5-xDC?rcS6$C#9l`jvaNcTdcZ?VJYtU^n*dn{C)j zm=Viqne4W>dRRl3W!2Z5NC}{-UuX641UOaL_3^P(l{?EcuNOK~eoCI_)3RG>5F`5j zR@m?6L%>Br)kI2I?xXl{J9t;-i}Vd{l&3>C`{^9fLt=ebXvOxdiDb)~GHpT<%O%I^ zWk_Bl{oqj|8M;v4=~olqPT}+qHRq{&P8WK>5HdaU7<0bvYrIuquN#Mu5lB=x8=E&V zCeKEdKs`^a=><tyNTWOC%58(CMRP6n0`v@?hr2m!Emj=t1Z~%XLC7(W4cyHR;5gc! zOOB7Y!J72~s|Bm87oN;>)~RWDeR~3UsvDi~9^-wEA-ddVTS26Xn~pWI!slvpk93bs zS%F~Omt4^6hOi%JK~;uxmM5%6t3AlyW0JPrxcFIB6028y7&ea%T@GXsTdRLnv3l3B zbvZ;|*p%{>&X%nj8Jk;zEmszwZ6#iqkZbAn`=Pb)De_ttbIy$`A8WehC4R`|$E+6W zLl1BiZq?vUJOx3k#uI;B`ND^gEc`p#@UKulJ0s&C*Z)xZ3~c`_SpP3V`D}mIZ3+I; zu<uV@{=cOSf2{I<O&k6O75-M5{u_Jv54eH--{tMu|A%?|Yz>>(tTDvTnc5RMMB??d zBiv&V#}%)5IkR)RoG$V8lcAY9E^Q19;6~l!-#_iP?Z%asxBFn|8^cwp8TeJcMsM;w zD&LB{lk`BK`^G;AXL%Nk5AO<-ezu{NCyMG0!9SY(AeX(6L<A2<F6vRg9?pDZqbySA zQ~!<dl$&ALiB#Loj`;NE>9lpMlIkL&Q$55t{OaY<iB@w^8vZLngw!g8u?ibx4m)x> z=Sss1Q5dpD@)q-`KVeP6LW5CB56lM&T9`(MJV8AN_I4csQ0VGHM#n|OWvZ3>ycxY? zz{mWIXI*qPluQru8_JWC0{-Tw?Ni!rHJH-wR+FN&CPi_tD{;Y+L%sUXP+6RCf+qu2 zAndHQRJ8Xe0)qX-Fsa(X8xXoEAXaTX!&U_>j&Qpuxa4_P4ZfXqx60j`s>g3X%S28| zoRQ2Gv{)Bj?gb<wsTUl66fLyM7B{{<nJgf-;DC`^X+4#8c%?g^a9CjFh(mW){?0Nx zoNU2!k2xPj24#Evls}p^BBtT(%ACFMRP*k^1l3MrhYZlcFO1+2h%c2@4BbY^Gx)*9 zDADBSq5}Xeynr5NPA=&pZ{!cq6MKLFUEcR;n2s_BWUPQec*G8CN@ALrKqNUL`=j_( zIh47L)=5$T`G;O4nnUg4p1KVM)7!}`1^H5=&8HQMWLAfV7Z0T%NhtYS5r8}iL=rD$ zm6Zfy$y$IHmgc|?nw*<!s+!Gq*Q>nCi_uOOfye-fW55sYxu?gc-}9)UvT?#@3~pt; zPaPEAyRlVRKw{t-t}?zAHFG=j1^@sA#)$(U8Aur58v%ad*lZ465PgjZY%AdsC>c#a z04@^xcpr%71}8w?2@-kVr#ed9S#JaR6986^g>(pCp>zP1t+&@T@Esrb<XeUETwH4P zIc&e3(rE%efdT>r^ERF5`A{)BdGrYeed^thY+2ryjCl=4oLx>ZqFR;11nK?=a@Y#( zippAw4T-~F2DEB{3*yc42q_up!QD#PU8jyd9)VbOMBFGVU>_<*A_j((LUO3!TW|pj zt_LNm@~xLQu34pqoVzpjE}2ToW+^S9)aZ_f(<u8)jd~~&CJ8=mH`L#M6<F)Vw0V~l z33iD=4?#vwCW`~)G(K%Fd#hzvyJafS)9_d`sh#=;`|aqMTzLuaQJIAh!mEGg9peVS zBQd-FH|^W=x83wyzn|i4oX_zq51&t+ZoV%DANQkf=ec&jKZzC4Ro>+mvL&-2mw9*n zS*bKJ14Mt#3B|!(frc+yPJ{*IMGDY(`b#tiE<+tLTgEHgEE7R!NG;Pj6OTf}kiak3 zY&w8S**7&(01}DM`XTv&bZnr}>YwB3`{9q)%w@^vqn}481&x=KZ|WX}=V-e2oLYjx zuc+HZMhn8fdYGZhUg^jC6<o|MWpeK}04cTtY8YrMtExYJuo=y+6WgyCz=C~S_!Qa( zSpEF2l=_H>7I#{!lBX#6?S)1>ex!UaYs0K1;Ib1n>eAvM=@Ixjklpd!KH}~Ap3d$` zBpH@Os6AEPRqG;>QHub6Y{p*!oPyexUH%MOo>Q(o5aH>?tO>1L&wIz19uV#*3O`+H z&NJo)LJqiQ1<7`t-DLo!qym-YTbds=gF9@^iX&5=z>V}eW?KvuKw}v?e;j(B_^uo} z3RBxmKVJrA{mxX|8g#o2e?8ZFOT$uwzyy1McEZREIe;--n^>q402U+$zycC*4vnNa zJ7mFH{E6Nh=?s85h@&WwuB1F$3dspTJD_lLF>e|N+Uu(L*-0Eg9l;$+)v1*vT96$L zEU$!)o6sn>$$FM~2B#vJPc45VO*O#$`)YDtm*EBFM&rn9SJLcJeeSF5g$MGd=x}X$ zLjwq!h`y*YYP%9EE>HEOjlgU?b7sSZ(6;mX_i=ZL+Whp3jHpcL!o&N}*z0K);FD9b z2TQb#+nhoRdh%d+7Y(ctRpnSwh3Ct-*1{QMv{xh;D*j;ax?k)ZR&f`<qu`9s=$uc> zvGYsk2RN5eT?bV6k#=>K^wHprhSI~q1G`OcUl`eFc&Vo%2aD0=H)&!7B+f96x%Iqz zbYUW?_ZG8>5ptDBt1Wo5^y41(G2Qcu50H!z=b3EVOvl^l>xbe=(b#Zq#zdszvU;%d zmPp98h(ykHW~S@$hf=zQFckkEbMN4!S(JA9R@%00`%T-nZQDkrD_!YI+jdskwr$(G z)qT6a?w;w0@All7iTMZieow@HPn@;(Z>`6Wtu``FIL}zE2Nt9{fWBd<V*l-V#$<tH zfWJ$HKXpx0*`?ig6AkB{j8>K4mK?-6hjurmJ`x+%?~(Czc!6afF|1t+u|lxZDjv_9 z7*l87pp^@97m2o|P%QF$S$evE;cq*O&jAM9I<c+h^B$ee4Yn`O(j$qb=ua1(9{(%9 z_v=WvwCCPSEVN2-j(M9vhEl4&<-j-Q-#xkn`Wq`I>VlLZUU{x3oxB92726+)e7^85 znt|GBrl`6`x~!kp0XX{tm6oI`W?QEu-T{p}&sxKmviYOfp$nrGeMq-2<b1epUoeO* z-@mF~sRa)p&5UJh|G?-fgjs5*A2aeog41;8mn~gK<O@sojW1>~CA&<3QY%l+rejyv zI+wy`IqZ56$dg<_<w=~pVo(!%Ip%boL8kx>U~_(`X}T*Xe59MicteHJ%-AdQ2{Uz< z>#6unG6-FxLVRkbh4Z_c3wC~mr{AI#3aM|%tu11yXNq=RfRqy7LC|#S0p5!Udp}Mk zVfpwTeikm9-&Qf<UB1<)%fv!rqWwue)NRJ0r@5Vy-}KCmxs6KZarG<}1G(|V4w02F z=6cG(XU&zxKa0gsc<EM!$BPbKwAtMjQo>H&1-(kbsU}_JG+=uhTF3ghPo%{MM`qFa zbK$zTe}J3RgyrpwwV)I{&I6h$jG}a=YJ}--oZODTV|sXw>DZ6LTSgoMXJ;~KxXfn7 zQeB1^DZUdY365R>`3cC{#1$gFJ3P)xL;D|p2ac#w0+U+;@$yrmpSKq4l5_WxzT?Q& zo9-4Jk&X3$A3`X(qMaE*dQ+bI){g~^;S_%8{7TMIMmv3>GWbSS^mXW?4;+s9DpG|t z4nGj=oI&l8J+f!n@EONya@SYegWmd4RADaf=v4k=Z?+CD|KjD;ECsKINWr3lrEbCG zJ!HT9C7o?(-f=YCg97+T{iTAl4Ug-S^6XH=JE0m)M&eUeR+hgumhSa5W)Qk)G81GX zsXoEb5VEhRlIz7IbROY?YtH))+8GY)7q}~b=O!?q9DN}p4T0bG4qvl%{dxj;BP5SB zbqVJO&bjo}Fji=JeMr39&d_t2+h7`+EXnuhNRzAjBP0LyL{F+wW&2FeDmO9v!-2i3 zJ+4sA^h!#M^6hb@dMnkq1;nuV(i~z-ymQ)Vd}easj$OVti~<LRa}BSDlHb(@n21#N zl%5Z`QYSV)5u)QWn5`qEI^Y=>4zP>eIa;DPAP?S85n-dUNDMe!%_M5w!jhEk97`cX zx%k=giJXPjixaK8^)1!CijxfcXJim3t7uSO(IcrK8LEuArhnP5*>3&z-K~0X*VsnL zhfNb>95?Xu%(Gk$wL&vINj1Ke3%>~zD)lL*OBR`WnPER;jxk#QgqGq1gXK{;UOrn= z6l&i(VL&fXDQ!5{zo`xWCLN57pXM-co$UFrd{Nc%16cP}ZufhllVEmAz-VOA4ZWAl zq36cHUce@*euVPeu`yOw*%qI9$UO#vKwBwFE*~Wr2-}`r<q;~Z=)32`{v0*>V8L+I z;P=OS<<I+mqTs7GC2V81Wa1Sj=G^&ko~zMwV|H?3gbIi}=TX}<Y>M}L{pnu=o&QFx z{PiRIFXDh1!2S=8mg9dRLk;+!GSq+ls{aqg%3oLc-z`@DMUwn;o!Ea7vHv3~`*+;} z{jaDj;NRt`0skNK)ScRYrYklf`5viXQx#GT>1;AI-Noq^>&SWfu1UW1oUQIEWFmDY zl#r2C|N0uY{!LblV-a4^9e=pne~?NX7%Z0GKI0qkvi-U3=ivLE2%ejPmb<oaDSx@> z>v@5$7avOHKOgIu<ef&)qM7OKh}61-^EB{45Qx*V)I2$Lxm#YPsn?nvs(DHO+ors) zX!CN+ch0VQU4Q+|zwN8V&6k1N>5m5vhlyo?$q^Hn(Yg@2$%(@u=gd>N!GU-+P|oL2 zj(>5I{o84-_b#|w*6F<&avc!W0ZamJcuE$Bfzm>T$q<9L1w(uqw6dQni+z{+8V~+x zTrHu*8;|<D)O?v|)%@`}+WDyp6@3-5_gpUlWYAlY8fe`F4(lAjnDUm>+dr0*TWb=L ze6C%h9#PmBgk>c6y44y1j>MX1vI2X&#ac9*d_~qEQDIrW8U(nGq?9(BHWG5Knf0og zey-4Q3=ORF06%dStcwg>kcO$?aVZ>EaJetDZ8xo_dwM9XdXGy^SY)9;9Eb#F7{29v z*Oi7jO*PE{>$FdhynMkTw@i7w*j+nVE1!b!usI*~lU(+`;Fu7a!T<|@*=*YQb{XyA z8AAl^4oaHt(;kP0ND$&kY2ISqtC2*SN=QAEIZ#To4uT3AakOR%GHc>!DLK(q4~~(i z8h4a<!Zms^fRMB}^%i(LKMsG0u+Lo&8d%qC0=y3n50*bgz@hA>RA<<PXnszt&m#bM zoCzGn74cz;vrmalqMzvGA<nLme2S1TfAH>yVSpWe3u|IP_H}AKfY96_6GdMR+|+|g zP>tldubY=u7Kub$?0d@@?grXDI>bl==ZqC*+C_#@ZDNF&AeZX-3DW0@!lq_`#u`VZ zhh64OITmTMo1&`OprN?){kI8hUa24d#*~DlnS6$`F<N~B=lqn&4MX*>IbHql#gN!d zktSU7E=IyZNEpMZ=K`wUn09Z}Wi&9`P?NIs3QSgVe0yzbvJlkdJ+<@uS=&#UEYm^S z@$=+>Nl|(hhl5w=7W1NFAAOVb=M4-5M)OBs(zM4$l6D@u>%PK0&PjURz;!yQe5$fd z{b)<g6#9E4=^&TM%z}EBT4`xLoL8#Z3{H@;=COyDaq8g?MtO4RL(!+UHv3QA^7R1Z zePa*dDknn;p{h)+uyAlM{$|^<b0ljEQEXCGHD3}+GL34=3G#X$D}}oJ(brtm+V9Ad zX^2UaE?Km{;Upo*xTh0?6Yw6Rm!uSCe`OO<uEb~IC!PQ07YEN~TEztJX9`Ly)RFMA zQOYV1peK?4a^<8J`v}M}fxreMgQmPJ=O}?a&gQ5F*8#G{-=-?KSN92&t_W25iev%Q z!ARnICf%ITPlD+(N<=Co=e`ds(xl@vp>qjult(}v0m3ii37JOX5yu~D5~)$cnF&q` z9-}0vFkD6-%1$EWfJSOdnhz%0)cA$nYxQ&6RQ0KqukgE?)MH*f^3!vNiq_fN`>;)! zlvzVwYFDZO(t_Fz8Vd(<0%H$tm8h=563>x&N(e3buCAp5*Z3wQ_^b#LSzUR8A<*86 zb`yvPKXG0!cR3!in=~J3v9NPd&sPs}t(x4$-5}#%lU#|xa>FM-G{miD;_5w|Kj0+6 znv5L1m(@tYXae;N0@V2Y9zMGEYpqh)B(cRJ+FKNzh?M(iOXuoTxr&Z9T^Qm=O0Tr8 z?y=J0zTLc4Z9Z0*daZ0i&szAEu_Wc<vmabd9M;5VkAaJ5BCL$L)_?7d$^xD381E0o zFaZncb7TyL=h+04QF-4ZhuW%zIi4OkIvSZuNkgfXYW-Z}&Vv5%irTT$!+o519f()D zQM0cmOvjIU*E1Kt=C}DIN+5_6FRc+5JQrnh!gez8EINP{SP!_uxe1HvH@*yaf*u!4 zM`%X}Qz`(|fl$68joBAxSGb{J{8sN?`blV{ez|*9@bQh7oFyGT3#Q^?qFTd=)<@L3 zyE^&7zuAUyT>}e(e{<oYXwzK-ed9@uhzo`pc)?_2Gs*fz_2&kDgep{-$^&J4)qGQT zjObw*u5|1|V^}*{#Sbvcrt&uH$AhN4hduOm_(y5<SM7S9Q4KZKjl@_=RAxu;<!#%D z0r$x?#mw9ysSszbG8^?r-nZLgBF(9~s?~|A3mi18ZM}Q7b_8A}pWKx@?~p=YWvZ@% zSgs(MHcTwl;*s$7m<|vdNeO2)<?6Tm+DWl3OtMM>a*7zoi9dstNhToO!>pzh<Mdr0 z!i0l$Qc)#j-AvVuM$Tf0ak~tMc8ks0Z4hNWElVz-tthDYrOa(P?<=>6&Z*lWNg(t} zFn;~Mur6t!St6vkFNyX+%{w2*tow$0Nd!h)eX7-XAM)_}R{euus+=5rh~AId`hxb+ z^h<9=fz}!g%){sI;fz6&*<;>FH`06?q?fpJF!31@-@n~ym}f^$s{Gzl*Z%5}2HFp5 z5(}MQx0Q2TaD|nR05HoOcT3U{ysK%r4;4KsHdP{9El-dn@m96|Xduq<uI7n0%>iNb zrh4^TFVWk&BDTCpD^0$=u8hA^z+5qulB`j=;Fk9M&|AdQfa)qPTwZIRgD0x2XE#F( zZUFeR<RQ|0NQ2!{b}Dur4J<XHPUG_S$1SB`j&7SxlgTgaL>W<UGI6HOI+SDFw(hUH zO>us8g@(;u&&C4(b{c+M@VeeVJ@`=jZSNpyU@AWBnK=M~1U9(ta&XkoUpW`9#Ql!J z@uHD833|KdeAH`<+>Vcdg|(j=6wJ^Uz}4VD>DsB72iNpB#+^Q`^yN<!7yaQabF%Fe z!Mo;o^#@0`OLiu`tlMLC)$s;Vz^3KD@PkDG@H7hjqf8p9L=dtL(1*iRm~JM3eO$}H z3&3bD3aOnH(mrOc7D=8pHC*&h`5~e0^|;c}OFyZq5y4bE#|klB4Bg(ypsR;o(lkhF z+FkQrEJD@p+^vr}IAPqO?zju&OL&g6BIFLqM%#R?*?#FB<JcJyM$G}BO<{QVJlcm| zjRX`<AuM{BqSqQzERs{?xc&x>YFz&;el@1C{Ov~j2=oGp0@CZ-11!M^$<qVQ*%))` zQ-x&n5b~h3gBg!}ub5F8M>^%Hm#W9AMq!G%A$TRz&AH&Ix>#P9KeHJfrsN%Yx+%6w zOejPT4PZ}z{HM0BoOv@_-}$A08AQ=J;TnTw^X=KWQPUc5h=n+#5;erA6Fmls_sJ=C z!<%egeV0=6BJYqtbChG>632#zJPDw~YjbL;!s2T?ydO5smaY)X<E!X?zun-N;2;xJ z&c#5-JfcYa8m8@Mmc?TEQAu=q3rZ1(tX==@-7d!-laRP4=Vj}D^Ei?OucAiDyU48G z<3{HzaXIKDJJdMx0w5=W)Fj?Vg5!Aue3tqufvq=V3!x=$!}lJ<LHvOv$@8=H{nL1@ zvi23^`<nPY41)QF|6|A$g!wpMh_`9{HMsB)M-d$H+win5ZWf3rpFFsPtBC1RoN~!s zP+8)z3vjYUjqweyK*zHTaRCjtri}^{)TBR_K^9C=O92Y0BcYL89Bjl4$r(!u34WEr zJ~816YYXV-5VZ>^`Pi2ZbYl>|RIVB+=-vT$qBy@`{_=abF<+vXxWQDVBF%F?tCc7) ziO|0IT4#G7pBRLQGj|uLU;W5yOw5P#`>iDc;&6wyjQ21fewbb+ufbk165?pIM52tL z+ZIA1;>rh=p)jsGq@CSNaG7E>od>t;i>&=w2|{VlklBO`oIm(exs=MQkSKtoR1NL| z?zsmsW5Q56I*j;7X`u?oke)>gNn6lKdJ*rvwb2+R-1-3aLn%-}LvYkOrVmj4t`>7% zDcjH9?{q(^o@KC$`PXT`BLG8?AIk{hx-$)p!iF*}kBcPO=kiII*b0Y;!4jn*D?$+! zlD3f-JRNA=4Bc0V<WVTA%_2;A)$ZSCXd;8_vTw&rGBq1KgcGV8r(sHqIOfmNoB}YD zC(WpU6M4o{*6~{?i_Kh^WJo0Oq^k8Ru^pst!AR`F#W*m<`q;d_c1x@?7j2^0b&}MV zUE~wS1NT`G(QYrQE>pvM6w<>P$=KqV&wPd3I&(ma-hQZ1N~VX7aa!(_c;}>;SzaUR z>?A#Wb`@I!A*<$9(@V_NgmWpbrChCm1Gq`l_aPW10kQG=1wx)`r>Z}?LD@O30dNCV z#3D@1Ipgw~9^>U2^}h+_J1OIR-{j}EUQD3HFiikULtv1FGAjj?;XKNV9QfN2np>FX zY4v<?d?O3^^@XeP(fa&!XE3wNQ6z>3S*Z^{dQ4ujsm|(y{~U~b&ZyL>pATS?A@{`o zSrzuM<avB4V&VzVe?1w@-0bGQ%ZvEgQV_!{3D;>ZT>S}}u}1R|1sLlDa47GB!iYQ7 zL^jt5idK>D#oa$h!9p~+I-biPQ<Zh~9C&&9<<tY>Frg^%`CAD5pctYPr7>SXrCnYE zM#K!y51^cg&P?9$>MGp<++P0rBZQLSBU8TW2Sju4`YpY!@UK&PawWZpiEw$>KxiO8 zwu2Elh;_A?LATdn5Ob_Rc*(mWsiVW$h@}f<suUphUZ<mfXJ&EFtxW{V&BkapA7nvn z1Vs1tF63Kt%)Jxn9w|`Btom1CQPR75aMGK}lN(HK?<a)+wg|*QN0c=^VvUE>57<db zlDi3(n>DQhOcy!V8;j4StJV%&AfJ@Z(t!n~xOhE!D>u69w)VB=0T=K`<lw)@OJxGS zi%hfNxonoxF?%Bxi5kV2PtYz<F;c9osU@~v8fh;{t12lz`t}W=^Ui$7L=Mf;9HHbr zW~^0Fcb<nRB(dsUNc3u@ebV_hYb^qbYRr~`bdS>ywg58>c|twbnzp^0L?HF(-O!w^ z_K+;n_d$n`VJei>3d7|EAzeu>yd@K!GRSpy5ObOZ`*6lVO1vcC8KZoTw$;RV>_s?| zttj;gfzln%j9~$4fEQMt0<7O^0_0CGq;es*WI>uweF-UQ9jEu$!b~_Z@iYES)2V`c z7>=J#uEROCmc=3<D2V|y#3fwM@u13T0e6s(k;hjv2roUIdawJyeqBt11NW5&(^n>? zeP0P%*a|E!x6MLKSAblnl}TYAey~aL0a7{_mMY8wSM6tJx&+~{zBVp_K^hRK@xHQ! z7w;^czxHB_M<c089dw5flrt-970NUG&haN<QWzXFE?ze5=RBgjbEV0*$Jy=>VJ^~c zg2Mpeq3>i)E36?}l=!`JF`;&XQh!?TENp_cLk<~2{hkRhej3yNJ6GiIm$fXM9Dx72 z!Ug;<q>BOnQ@Z%Cp22_Wiu^N<{l70={CAh*FV*P(Y>WKMeeS>9^!_c6{6E?v|9s>8 zcX}M~7f<QWbN}}VVr}QOK{UUiY5{#NmBG-O_Lia)2=0`&#1`2C8T?T-kodTekY>aJ zyr-ep=6h~$vhMGYie)U5)1;4mJu6o_PutDcVb{%!gZ@~1hI+RS+F|@*<<}Fg8e*n{ zEHC+=so}(<#^{HVNMwbO)hRm%4;1Ki*RS-?>VOxwwQ+{tB_4*}5%sebKh0{lg(}Tz z`d<IAC4Ypj-|%}9Nutk49Hz)v1}%T8OE?hBrC;eaLM7F<=ZHIkJoF?jkfQQ6_O&31 zzC#lAGn}oV3x$V#Pdq7TgS?IpOC0cWwg0_obB3Nb;=fzm-nM$t-1y?^Ywq{_nSE7+ z5&!bJF=;SC%@g0&SE<`n+SG2=q_46zKmnov5?SmLwo;`?z^@YmyETstL(C2hZM2Qi z)}%FGo+etUwy>+b)=&+}EZsXY6s8p=l~CO*756Ow`RU23RdfE-Du7N>sf|E{S(~w7 zx`H;ZWjk<HwOAp6F{02%>DD$1?<ch!kx*fQq$przUK_HZA_64HuWwNJ?UwpZb*<tO z<Mza2z^3!nr)x_pMx=$*SQF6d*L^vBb^={!)f-U8wpy274l~btY}eADs}?0nhbELV zwK#U*w=iC|;bmQ@x>}T7tLyS2WMt&p7_>~Gs$zn)H<ao8lu!nxcZUkCO|y`Jx5YQV zkVzWPPoJS$Z?ozpH${g@|K-@Z#a7<-F+1Y;(q!6G0oHud<%Vu%ipqj+CW&QgAe<0Y z64H3XLp_>g^F-f|jbtP%vt(QilgdO-hO<Oq$fKETC|df&r8-D*@l>=yi=8_7z+h4p zd^X2QjbiI<<(ZRwrH=Q_E9m;-XNNDYUMDZ(dqp@j0Pl`=^jWwiK!87{&i{yNRz#$N zl7}W5_?5;XDmsF3?2b}Cs(X`oWXItaRSB*#ASjV`0Ut`}K@Bumj8ua(#PP>ucv6}J z^Yz3cVt!hmngBX{`{s_cPw!QXjEK|A!jV^Qs<8UzVtcGBT)s-z1kRaB)BZeUb#UG> zgFW-F`I$~q9a^|Dko`tyk}Ot);Eq*W8UX9<*=hNN@WNu{{Z9(n@CeG8j=gkueVS@l zfwsNnKFqL_pW(_HSDAn8Kyf*Lu9bz0s!p3F7;C$fB@9FO_Fja@d5))W`57DbKYfgt zX)rKt<bzJ=WR`Zav`ECrInfPFn951rMZ|GuNHo%8q(-ebC24+IfX+p14ctP=+7K3- zYFatstdv}Ws$7Ahiid_1mMk$&U<ZuR<uCVZIrIog3Siwm#e=O}2vg#yS=K2CfiEM* z1!b|CHVFyyLsxoQLq*BR$0!ZV2BfL<{T9V6Si+B!Z<72p=iqOG{`KO`Y)44)a1qX( zl_~Pi#&#DA-`1VeYr|7Y*-?BUrr&Ro(NhKp*$TB#rT6&{W!u--o8NxHcHWj-HR(1# z2DN_#0sne8)ttabG}DR0LM0v$i;!8<5{HCqv^49&#(KoLv((#CeFKvyy%fKc@kH=^ z``*#~TDrQ~jL{P~bUj$6l2pTk;xeV=ZlLMQ#ae_rLPx_@D|_+=PUOw;O9#+!!=85z zuXCQ)chVgw`)L8m6tn=zNXyWT#DJ{ACH!cf?cNbWRo9hSA-eOZ6kIHk+9O=tiYX8j zLQsUU_zJ#)(N~_csIXEZ$7BF(zjSudp{dFZ9TTaz01yBB<qD|KbWxCV8uLlDmn;%? zw~YG{f4!}GPP-2%E-4Kt=on~}WKU<rs7tBn29GpLPK=T$(1xlk1wKI?%C<gpi?&RD znCO@3+k!yrSZyv-KDfI)^=0(E9@}$#H1@jy#GicJ_}s|bOF4r5l1k&dUyNx)5>c}; z<h+`?lAWz)`28q)2LjzJZZ3wZtbMK9iJ7B=*OlmCeiBo`zAh8%toJIxs?(~=1%C%} z&kKbpa^ZJLq6csf4&PSG@hokI(=BIz;;Y9FZ|O?ztE{*9^DOtkIMsM>?&Z5HjtRh_ za5fh5b<B%&PIF29SyOJ?H7tWVaS>4)1!Rq5+JntU)E{^|Flfl3uS5-n<|w!FOGMu1 z8~ZAZvx|^MdcXv9GsU*D8>wtw0+HkciLPy#rtb#rnKKz0Pef!=6p)WGmtPwslDLk8 z!$#Q@XNG|hMr;jtd|v+@h~nGc(rd<p-<xWE!BHkY^9*1|J$BW&S2HR1P-b|dpB#%> z?<Qb*F%1h~99}k6m|$)rNu>M%m?uySPp*JXB#m*hX}>H{q!J3fg8%{7-_eZ1#x)hR zudL#%U0i{P=ftFEgvLL}aS{i=3t!~mW?f0ZMAK4T1Y!5iJz$3r)&+kym2wH)=Q@<Q z_6St!M(r@deKt$vd;!Tg0A|R?%>>-b@=<UMF0rr*DlFg&dv4%91c^~?ws0nkTkN>a z6sC|gW;fBF9dsD^k@yZ_t<>B(>8`Wv#ymLb6=2*uL3bU+(m{3Bu6wptIn1_9-8tbC zUL2$&G(I?OHCCH|VheX;R`zvdn9<OGX-xe&CM(r3;pXS8EFvOSv^wn79Iu%kYbomF zm}_g!eX03*z?Zhynm|iY_dzQ6yRn5v`u;91&T8Ec-&}#Kc4DDBj9(~6VS(bQ@lz1r z37#qA3ZwcDmyhmlB=kmx?DYcTuwTF+PsF`Ag<fyOk#8jekC0~}=5K!J3VHbyj4a1x z#8`tuk8Ykbg`diRS<Le8#7fR~0D%q=PQE*gzGzTfOZ{3Z(SuCyafu;(BfgplTk)Ek zZ^8)Qlim1j{3SU~Rzf-<?*2}Q059iy<ZvJQJHE7F@?<i%*b=4{C4!pjPv-vH=tosB z;2c_$@mKL~2}q^+X&uFZfwsDNhVZkBWw|pQ2B^GBIBcOqn)ymV|6D}*CdsLuqNa^C zlDH@}hTOxrXCS*+gqp29=W1nbRuBQF)i&8GW~&5voLEBzXyHXE1E*iSh!EkE983ly zf1l&iK~l`y={ZkOOqc^`If4JMbu1@fmP!&~G!wt_G**6j5_W;ftIe<USo5L?c7AY` zwewOkCzDzl9UA`a7N7jguekijufnn9;gVI2e$)O5OBnbrS<7<~9NHfXN*6Q+l4d6s zBx*k$Ar4Cz-3;>AcwRB-5!#2-Ct}6rv@zV5PR&`Y#T#fBTJc7W7!-=B7h|6(VE}{T z9y8UX6?+YZjn1~kvFLDp9)bA0W;|_jUodlcoVvr2pbrqExVK5bvgP#C%lk=vnF%dx zNh%|yZ22H-xLf2iqR2x%l7<NDuv3$iOCL^py!m=zbKjx+@M{jwlp_mZM^qzF;P_|n zoR#J@=AU`_Z7PgQXiB{zcvmV+IfeO=UfFD!QufHA&!rwuU9mjG=Fb4`gZqMAg~D0u z1<f&$a+^0XBMM9h7BI`SXYY*Oq}9;%Q>C%67(dCqxP8zuIwcr}MHxON=)Ioa#)>`o zunKxa8D<5;))6Jp&hw!naw`gIWM&3$Z$k=d5#6c3V1*tJR6~5!2JA3PbM%Aa|NK8p z3NZXhN#3B7DA3HAW}{rT@!F$B1~w@Q$H>Ig&T)r0EzL&5>Zu?1b~pRqIhJ~ETDhdu zVk8g7NyXZPE@AJEBP3OuuJG?ogc&UzUC6`(O?eq&9~aoXvGdu$xQmh|hQJd|F`3!E zk`l)?aAY=hKl^@wdCUQZBXIdfXR~tTt<61>_VT79N+gj{F_-%hy(oBt%Z7iYzP_>T zv3_Iw_1>`FWo3WuulY0URGBdyNYv09($;KoV!ZsjT{DZo-WQ$)M?{&p`b6}$O+;T~ zs+hVk3;A+34O@M=9Tfc|eV}!|KI49QnED<n9-)W>C$2wF<m#`ILPzyB2yrO8fm66@ zrAKET1IbfyhOm`8V%}d+4_(?9jOuvP_Z)*WGm=~iO$3m|brup9re|$10+<)z986!h z)2QbacG{fX^bmQLu?w-7k;QgD-`({5^+rnBU#hj!E7Z5t+eZafdR{uuokq3}a+==@ z&L#c&QzP)8I0Vkt^cgb|mc*wD<Of`ASMeYxb`Ch<gC2(2LSc+ymC78ak%Th~^g=<_ z#_w-s1iu<yWZASAf|(0@C%nrS7U4B`C&Zn8<~-SLJ8SuC=Og8=BOSh37#E}@M5bgj z>RqJ@k)`u=;+}WM2Zf)Chn-(ca!4ViI#1^+>$f0>#*8DL3fA0tTqqf&hjCf?#h>r$ z9h?aF0@*{Q974Q)yNriw$tuih<9+dHGCYC!tnVo>R0|j>4JJ}OkrR*?LlDfpn+wWy z8J1!kY)|uNbNWRNny51s8fORJdKnZ=?>e{-l6;0I>HJgM4fuWvVdA`p(I=)f?WZS% z81u*Prn#y$Y+(;GSdLWO{__V2FOC5ZISc08r<|nvs}79#eUA}Rx?BMy$8#J`zkUp- zuT;T6^@|~NEkZ^{F&N|yBvTSFiRkh7XR@$@oKRh(%k9mm<4r>qcX*P^o1X1Rx$%o@ zD4_`h_p0`J*P|r101IPwMb~34g-LDB$W)eC(eE5K*We**VRLs+*D_%6oag=GMFh9B zQyYR(Yzcd}EE`QiOK9P9!5KKkH)oNX@d^EnuT1(j<!Iz>znre?^sW^TNj_8DT@)Ik zg@Frpjk5E}zZ7BMu{df3%=sn683_{sa!*caIf+S%kHOrK{gyOJ_B}K6(lN@|GUN)A zh5WM)g!^zvC(d@gjnYO%_p8{O^IXI1^BIiylNfM|%31f^o}g(iHf3*KUb#tD#diBR zig@J^BMLdrE!-3@;mw2xmZ_e7B2R-mZPUe|Bl_l;wTUBlrI|lMe@mwi<@Yt>uZOIG z6cBtuRA*bs{yY8tcYc9|orUcm`u*=<_J4q2bN;PJ>mPw{z~7+k|D+x4udDnYr5gT4 zvj4NL|Nq+##`(`C$ltX+=ig%S|DCq4)X|LH8;<uos@~3rUN*bov#AbD%fh;%J-lv# zc?|ZRF_5gFUQH+|D&TuGU%ftiNB2jHPvc2SqHJWsY=|6>rr8@EmiX=N@3_^@_bDAb zJrOx=OY_8Vx%lzgL)SqJr|ehiH>h}N>bGs?csU@t&u6p#7B=+<c|ee#)ZZ8&SoLx} z`VC#buIn>L8=GO}O^M)I-|74{<@>Dvv<9*Clt=8&4{X^T2L$V}hv~rL-kNJZIrw7^ zw`=hdCk{f2xtg#+r05k(9E{STzWZtR+i_Z4IFzH{@Y+D#3jXPzqTCJ6>fU~L40+pn zqSfQ4s>;iCMn?>S)58`H`LUzZ3tjQkk2BwG`loMFUl%EMS{{6Hg_f6pM!5}Yg0?wf z2#q8z2X1HSj7M0+$nOby!ebRNDOdRgZ8S)9^31AG2t6422b|-7f)jCK*IyZyz4}Az zWqAv#kh+yj?s7mE{YoXiO~?tKg-RGVF&g6>Qv)JGRUqKDCn!6}wPd5}#~04%(L)5& zi#t#b@E2#qTPz1HgI3H$@<lAvOV}sDRwn!M@@{d~_!K)NeWPlHkUTCFWd|<8xO>8$ zH~kJdI~<kh0zKQVyGC37@!N8XE~Dtf7J<>a_)0L|H}*@Oq7+U!nr0WNF-<sG8r<0* zfkwi2d9ZC$jVxK}lX>_7B+pRQehh@Rr2d4|m3}yDo=?65_bam2s+OSn?bQ>88*L*j zi|Y@g7orhK7?!1FSdnp2pS6Y2V%#Wz1lZ>dC{(}Pa8vEGzu0N?D>u}#B#x<Drpa;Y zjStFs-)<-Pdj4o%nh5<RCzGJN8@tz}m&^!8o1cuY8T|GN_XfG;d)Ib^y<_obGR=iU zKX2PU>A+EVm<yMFdVf5|{<OnE9=RE;V!`&eO^ns^)Eg$i7&GNhr(QhhplGNNpdEo$ zn`zc`!90FALehFrqHcM4oyR^H)>G9algr&D+t$M^NRkN@TmV0Bhl)2=&=8xatzU;V zd^odftDgU&vs6EhX9U|cf_}<H?&^|9@%BT?-sF#3fiF?xyJcOQ(TytdpTMYuYaBSj zYq^{*{kE|gLuCu!1k2J5Dh3riE$+FA<rqE+qH8)7ccb%H<*TEunBff3<J4TiBZgtv zAG4prtoD$EhP`jDfencuXARJCzPK(&_Dy<8BxADEzs1zbFoXD15e5juOpp=LD3n73 zud{WK@<5;E_ngPZy4wldpc$g0vRM}Z<C;f6CZotxvDLVG0_LSzzLu=ucIl<4Wa4E= zwGyKlXi{wJF-D3}&vy6(;r8p6tRsgO+Vaj#<c*|+|I|+LN(r#T&<Y7?CiM=9zk~2e zjpF2>VCJFY2(u48<Zf0@WHhaRcJ@`xgRnGBNDU$`(?Mke1>RZJbEjfSDKi4e9hV>) zjqVhR_#|)=Tani^v$K$*_v_(iL&`9i(v_~3Q-(Br($lIh#7M&AN*2ENj=@<n(J`*+ zU~)y-s`B%*4=pldO4U^phzk6Pj|=wXKIeqJDcR7%euGKKbCskNHGWXt8#uLmE?cQ% zj8IbB3eRdnv&pquU`8py2d}4REk?&?3=`K;VdKBweV-?eH@!r-P)xu{%>d9UNO$FM z-v;LexV^u_o-Xoy!f_(eQOd8@-QL-YGV}7WwwrD6)W@A%HF^107_3+D+;58}hxqZS zp|!KW>XL*KjjPbyTGZ+*<XM*>T~YSItCsuFeaJpmT83g%ubM$oQpE)w3oIY(IN&hA z6>krxVDMkTKNRUa4cxfhJjNt*-FlW<$_it1X^bF3l{x7u9?~W=Tyr(}z3XIROw3G1 z`9<C3dG0soi~*zH1c>ya@c*74!*CK{^;5R;r2sn=CuPKMspc@+d{4bkW&Kj_w8>g? zf}k|=Nct|vc~3glNy?H;fklD*9Xa`?gG?OM1gVwihK6YQ{2oo3L@yBB<uj{KRfg8d z0g9=@8H+VZ7rgHR7AO{uKYDsJcA7a4{#p>PLC7HL11L+2W=6`3Bc~&@B_Q#IM2u33 z(m7)sDS=fqLD53ow0|KSc&2xn9X>K3r{@&qc~U5dw@Z@^+0J6ViD;*6=yfSDD9?); zTSQnLO$ZQ3y)*Y<eY3zr;Arbl$+<RdpNm7+Umb+^LE<R738JJe9ZvfE!E2Engod1G zzIY6zy-MTyRv7TOr<ZD!vL{MqFO}<beX5ulnkjg?75*-X>S>uwMKz0$-ug*=gWUN& zHB}2$53zp+^qa*6LZ`*AKK$AR`daJmqQK`c9q$kXx4y*?##~+>x!-+)Ou2V>ytGNY zQGNTO`TQJY6m%uf-FNXdWPNIvm~DS}S@B)Sd1+$hSTv3?<3%_g0)B`F-MKQx;VX^3 z7mPO$;D)lDJ8Kg_(yGFDK|_YMCX&btjYA-^5z*uCPDJ_xc5+i(KAW}BbC)(ZzhEym zFUuZFEs^Y~_$+GWJku~Uu4%{RZwH&GpCDC2;_sy$C;8#1-Z3O*g5wCB7A2<AoL>W7 zV%T&BxI-U{rA)-HzT3Tm5lrzVhWaC?)_~za$q!vX*Ah_oOA92bAM8lHoQe85Z;eIH zmAqFL)tjb~(9?NAnE`Vu6IN;;kX&m4qH$x0^ixKSm=wNSCg6r%V534wOdZJ2&;?OY z4-5C>0K!{n6ydxWti-RZC+{&=2|aT%k!kx{g}(8tWr)q-b5q~WnDm>Ml=b_A2PVZ? z0D*(pDA;DqqpoTWQbYz_(_SX!-8?n?+WBTdY&b>gO@xJH5vJf62I@mZJ15O@BEN8Y z=2~jRsK=8pK`DsoDLDZvB2esUj2EE<(K@lJkfV~keOc=*mMb?$?k4ul%Q!10bC~fa zD5jTZno3{JAqgZmP4!1>M;Ue?&=$z5)h6!ZUDRk=&TvY`Mnr^RtQNY%R=5Eg=-%{Z zo)tkxoo+3BzsCdN8Wr{eig#rEFS*&V*v4xq`_aAJm7HxWb}QEH=|AT&yia-!H(OuA z>%J=Ln$|6J$P%e@$RRgYI4@6rD(QPZlkJ0w<LVS5!US%gXlHY{+`Rqv$1sus)^cFd zzKn+X@yTbQpO9@z)6sdL2Zkg=^oIQdjc`|dwci|=?_?O3LOt_>(e2Hy3m8Pu`)N3H zX4a<cD^?;ZkppFCM<9Exp88=LtLV8A%skRi#s^17^>hvyEK(x*=rloR4&_z!m!4`@ zUWu8>qO_665rTIP)F{JFijs?$#GB%?G>DaYlf(42cc}Nl^E@k9`IXU)S!<}%#0(gL zs%z5l_b&yvn<#t!n{;<DfHV^oq!0rnOp@Izq6%;zNr3Bz$CqDg+t<MZ$L82t`_^$o z7>r$Vn;$Ah&GP*G!->2~TT4}JKEx`gb9^FAZ!$5NQWYfxl>=*UG`*u6aO2MuipV6v z-ndXi*Z737O`lBW9BbetRQcj?;pwg0L+x<o2W9hDP?DVOK-t7!4G2lo$F670OJfjN zg7F@jA*_q=PfwtUG#?<z7f0B+*}EQ;Sb?)=j{8D!We+rd@jUXXufkjpy@wNW>L1{R zy#&%GA=!VB_E@_~JQ5MVeNhz$EJ1a*>n&bMc%_p$Y|oJSEe2q&*i#zAJi-NgM6{9` z`(#Z#0v)|HN~jK6!3i}%wU{>%EQTm#G;Q1&!uA^BvY)v2xwQg4T;lMyVK%Ub%s)yN zyaglkX}Jp+;*4=k_3#hPD(zE@qq+Q#%?Ch#P_Pe+C$Pw^?ZS4vQO>S<n&Xf*vj|6O z21ROaNlsr@aTDhb_aDjf6O|q7KpU9TnCh5hT^UpO1gi9JC~HePtj8CM<^Jqig!UcS z(M8_wCL1IQ%rgQ#xo@xwy~_gxdBtbP_Cey(s8X77R|yQo@v%<qW4u;=gAg%fBv<9m zpc$tdhGw9WI1p3eQmKsd-KQ`KU23mn?1!Sm+lJhaZ`92lGlZ{JC9J?f_HOGqe*bbm z?5<JX=M1agMGG@AE7NiI?;?1<NEGSM+i0Msk`M#(NeZWgI}Pz9D#C=r4yZ3xfN*L8 zbEh`>?w1I!N#y&?Ocz824uHXqR2kbQXRA|w$yP1^dY4NqfB|`sIM+}i=bFPaWWXud z@t4T}-)<nCpnvj`R`0}QRG`_nW~J(}GN(*mCGtJ4j88T+KTBaLog~wQ>Xb~Yh)ZJh z-pAfz1&kIy0(V}`gndbTEG&v+*R(;kbsF%1jNJ9ZF3=(77G+|*pnZUa5Q~21M5J%% zBjy@X3bNUD{J9%PUEMytMf@1l1dEikf}t58^@`@r-}Gt*P^)bi-6Ft$yKkS#Qnw#^ zxLmbK&31(D{V*#Fu!2U7UBE7exQ7-Wr}{j(NTnKaYGE>&Mitt`(-e>46{f%)PQ%$B zq)9f6(&?su@0OmcNbI@-4^$VBDH@to$`D5p0`EGauwa@s_il}pUe!`%o;`t#fuFVv zfz?-VW1SQn@%>W|u&n|=v~8m`AMvG+qlaJ){2(;8jg=W=MaE;w(ruVXfYQUxHxfh( znLC9f6~-~b2AT)z8^Rs}&z!{;Gb67U>bU(vG`pd_k3E>w6Nk?jOVTP8dQ;Wqcx8g~ z0?^IFTSL`5iiQ>itVPUpm>Z)N<FP$?hId^t4sNrCPgv;{H!uNy7|63n&*!}!-X*^x zJPUwVYI+^b7|Ks;rr({`j4VGa<yNiefzUvOd8wJ>n1u6kRbZlQ+xps_q}#?BB6+p- z&*P<hV40f4bt?QyE{?YBJ6E_1ByvsE6|00xbfkHh`PpP>n42h_Q*jI1JEmr7=*ASG zpjjy7@oU$joG19Yf5|d~r!Uvekne$em7I?FdzN2pFuCHKIafMBSI#!YczwEfY^?NR zw%5k}_ZTU_gbIb!LKrWRQ98dNS8G}S-Q{bc6l6Zs6G*!n2RSeVT==G%aUAyGxnLU# z@qp*7d-5l-Y53hRC>++_yzy_#$GE9S=s{Mnx5h!vA5Ni;tC+uSvVv~K?izAj$Q7|c zejPf625NyxA8igJ4fTwr9&koO5-jc!8YiQZPcUsGGL^NX3IH!*?s4`z{o0+!z%p*A z^V-Cn+QUgB0q;{d3>#s6R6@<N>|Iz4&jbiyJ-Jz*6LoE3|JiU6@MlA|Rm?0_xPsu! z-q1h0S~uim8vdBmnXD1ZFhFVm$D?2zXF+lf`D_cf+)1TZDEWxX3(7yFpg|jy6IY2t z(qB)&;%<c65nASnwKplo#La-woZmnQIbu&Gvg_OaT7zZl=|pt4@RAl=6ZY%Og?_Y# zbHzS@6>enmSy2Ex_z`L)7!f1M*~9~7L~bMJ%-N@48`L?eAbwoUq5?C-%Tg%YRP*(N zPvOLkeo#ytRL4A@{j)LqS=iI3%CDsPm<!(=k$j3*Fa}Pq+H5Xf+LIpq&r5DRwxVI; zLVf?Rh2YoG?mg1Rb^h=v?PoOHSO;AR;Z+G9aq|;c2G3ID#;@+pJ8Lt3FZ_01wmH>2 zg_<c>46b3h{7g3tGjS{9=emo{x^6RhD?!PHM4F&;)<3P2Q7_eCowXYT1<;hPbz|f{ zF)>jmHLkU)CFUrH$jPQS2GJfNe&@ZRV$nTRWiEF=0OK_~GF328tKla`m8~Ha`Il^B zw8Pw1)yzgXk0*#FG?LBN*0f7O2`;a7OS57=F*{U0*~M&n<UI4ixY;;ey$p?RIM0<M zZO%1o5IPrKA9;lM)%Ne+R-g`lk}3Ys9rskA;>_lYJ?4a)m`t;+`hky^-c`N|mtHc3 z)wrF-+HQA4$!yGMXYOL=#Pm~49Fr^MZCcpJf-u!l1qFP*wCGLDuGX?=1L>??cpW`A zy~Re@Z>7L~dr_kSZcqQHh0%6~qsAI`HFDi*dY<WF6V)nOP+!Q-(v$6xcDSoQOMKaI zs_$o$0mnfp)Z$OhF9hr-Tk?;xwWRDPbc@Y#$5}SY^=)E#Z+c~dS`c@Kc)a?wCMacZ z5F_vq=>$8nwE!lQr%NU}Gmk`;g4n4vq3DMhFK%w(?qZe>VE0?EBN>c?JmipXct0Iq z!60Pu>?%T&W*K%DCq;+|sGmj6o#6LW0v0FxN9pU%@1pF@j)*2*#5t^vFRAGHe{guT z-MYnSJz>0&OL%8fSzTZ@jwxbRL({DuXfXAzOl~2s_g1@cs-3Tjahp3kayw*!zb(q+ zwB#9<BueBZt-TqqolEIR^>||W?)0U19^3YO|BgwD$co<lN^ZM(niS^&3T1WFb7I?F ztyZ3H2N?C`#stuV+GoFb@%bkK5r}IowlGNW(%++}oRsi`VQ9`g6YD1k1V{To2SN<1 zqF~GEXFrFJsd$yOsq_M3TE;~k{0`0nSZ@ioK2V;F_5L7<`tQ-XJ|Izg742_@tg7w{ zlwQeBPodzWjojGz76N;4yeZ)RY<ft7Q;rwNmE3L2JX9^1zWFC752TmyLe+eB!^eE1 z>Mpid`Wnvf!V`QJe0gt@ZG~Yb;ZQ9v&ww@(S_TK8u&NfOaog{QdcQwKH>9;n#fG|p zW%+^yN(92&&I@x`wO?SN6-)VUdZu6U5R@r)x5HfOlh6ae-Ny|-4$gqm3EGB9#|?20 zEh71iMjXU8BIXis6Y#}a3N6#qT<5-3sqbh-zXFGO3|Ga&FJ<2F<|bNC>U=0IWiHqU z#oDvDoCxK-CQcjefsH;T(EOZTac~M)MAu<!77j4{>Y%yl1gjY%U_GnibawS$ZsI%6 z=iTrK7{{GK#S;!L0KdEw)V=FVs2-b|h}Rw~L5e<?s4}0?mx?$s7l)t%*fEdFKC<R! zEVs0h>H86uc@m?_`|Tvh`h)~$+mFgW9EQVVdaj%wZ?f<yId0R`gUYC-L-c_u;CyRN zp7;LD!`Y%G8B^vMgG?a?CGA0>9$_N=yw@tGNS;Rq7AnV1_Y1w9iN2$Ncz&cip`q`| zxFVe{rc?38=Q<v|N~)U+soqx>xc3EiLO%x{`RFD)8%vHB^@k98fV50~hlBiGWW$#L zEHSb7+o~~mA0v{7N%2JiSbx?u@2LUvb^+|u=x1CxU%61oPlz`5G2@fP4KLlwG((~9 z<=aWo7EJI)pgO+c4B-!RLMA%q*!6_=9-Qkr$}=))a7kIP5Ud6Fyl7}E*GI#^8hBpl z2CjvY60OB}GsLj2M)zY~K<g$!D*wj9{;Q?>7t5H9mGK`fBAowSXDR33Qg}+H&i1ZO z#-`4MOq_qqqy59e{;P-kA0lXfUFH7{3;SOPw7*C8{xaPHK*+@O|MGVK8q?$aXXF3B zJnO&t`2W3UtsT1tNcLN*p8gH1!yS>=zQ5T+-!{h*rDwuuS-^N#g9WQXPj=8S<}_BN z^E&ispLT_NtB@zNlAsHjFx>Qf6UW2jeyjdv{-w^}pUo5bVTAQ&d-2t`^~%5gI%beI z#Q9Z1FjoK@$#@&p06S~CyT$ifZPz90x4-3g{kf(8Zr}d9>qdW2^n}6BMom8U;ZXr^ zapH0CtJwd||1;bEgHa;6@C)phSTro-eMV6?-TMNG?-B2-rQWXPUKfa`oK%S-D!8GN z1u-&tF9g#3R#pimi@joFqt`t~d)vo!LoA!JhS_J{s$1++b{SguPxq~l`z3+{Pi5P) zk00V5pGFG}wtp}z-8tbthQrTY>CP32eBaLU)zfL$rc(H@2Pu?2y2mr&PJcmr2IaVS z1>;B+gfs8yT0K?pGN2uv2rmV~Oz=XAd7O+aA5CyWECShBHaN{=1`AicLCx?HrTe)Q zW}y2D@_;R-M6s=<wNtx3l012WjL_o5EdDJIB=T}SymYrt^IZ_(o8zEEQ%sQ)CxP1L z=UhEidGAw7>3Nfpy`tAeo|(KG1nYD{KP$I|YI!zUd;Fl7hrKFG1dU!=NfXd_e8z~$ z>i2rPHDE_snks&OiwtqkO|`IM^!ochHmvmA<`NwP^PaEo&BfqU;PY-XMrcKuZMi+T z9203*r*#h(QtCa+BCa!qwuy`A9dmd$z15N{?3Z7y=2qt{?BnSVqXvJxRjc+dg=P*t z@SvcuCyp&w-43tL*!Kl|@6gY4b-&-Ry_C0iq_D}U`wbF7WNwZ+pa;w(Y1cjC8U4Z; zahni7$ohq|Zpjy8my<Y2g_Fjx&8(E^vzoHe7~a*k*zHfN{jIOMiZwmEmI*HHJJ#e$ zu|kR%zm<G)FDl~>31%4>1b^}*h<pN*sHVy>?_qM2jLsSw%T};HT6D8Y?t#Q4g6`7d z;51>g&g@R$>1Tm?#&ZSmVsB-e_@GG8<`Ir7(Ox*1Vp?vk`<@0eDG%?@edWW{L<d)V zE4I*(XDXz9_J=8Q21-DjOD2U+mx!hPB>XWIOAnPBmnTv(tu0*AE$8RhuiLNPZ(za3 z-a^`QK4(``M7uQJ806J+$S&g@DxQ$}IQ@-gPQ$DIahkkZ0NYG3*=;x&EwzW33iX7U z9wyf@;QmDjT{01qMK|3Nw>!%$o>evo5CU4aBv5%yiw*=OsD}OmIqUWIr|#M0^VhnR z5*<|#X5RM+yd8Ry*-v1&n+~CgQ@bLW`c*xWSsj5XqL~=S&N0wcrnoKxPqzpmrrU9$ z1!w<sb#6zq#s%j;ZHz%&Ota&GAboDKp<4L83d+fFuyiE+{cfrGA}EwEE#05{m*8v8 zq3;rr%)|AF%yP$%+i0n_PNmO~MEOw)>{|gHb2NFRt99Z?FH}M^1YTS_(bsbMJQ6|x zS~Ck@!eb0%d|x;3j~KYTY5%9Tqup)?njO6e*TQw&>`Xjc11k9=^%|U3qxfvWcTqCe z^7GmmbPx4L0Sr#6Uf58bju482nWyUl=oU56Q1gu;MHm;wKLN>3Tsps&hZtT#<VYl& z8ima6NnBNOFrLW5<yFT?zCeOt*EF2@*tf7l%RWAk55Hm_C@WC*Bl6e<!-twpzk;cd zrt-zg_eCGVl<40Zed=Z7PphT*1bj4SS#<Jq=a(pa^%SDj-@d=y4ud7yJuZ;&qo|%; z<k~flV(m@BU(7+v3BdUunkIbPZL)5$!goJ|OIwfpWUzKBF|ihEZ8<`UhNp8kS+uuu z6CYs;PU-6p%+YUhz&6sj*Wb@zIO_}Mxtp(@T0FKCMoB~Fi8Nl^WII>rxgg1YbZjnV zR1(y@tC*JOq1h+ysqV2sNp&hYaa_X<!ns<%u44|3NXCPcKZFyoUq-k?*Iz0>G}gYr zU!*yS!K1zFnReC38&}YzWwxygO=S`bF<;W=bz$9y#2&bMIMllV;j4ie(Rg~e(gv9J z8$W+eM(cBGJ0NjXqplnmkh~>j9Q3Tu+o>g+2|{0)j!$r1xEfrUaz1W45uz9dyCj86 zvWOFjSb|<vYqaeQFgU&WDQbaAf(FaRv)BCmeFincLd-F0VNtV`DZp=gbyodnF0Wns z<3}DuIqVVCVyD}e&HH-nGC{iKy08owYEP}@!XKX5qf2c3+qgx?_mM|mWV+ynaPdX0 z3h2jC`Fd&OV!^Z?A2yN4g*!7aYD0NgsBy$+5(3Nh7uoLMv>Yq7e%3mW$zBmbxf}9C zW>HFc6u|Z=CkY*sX+h-%NoCjX7CXsVVC5i2(`0c$)L=te|80KnM32y+IpDXh^V*h1 zVmQ>*E-!;|W~nKfKd<FvG6vb>GjYln(-7Yb(dh#rOdgF!>@Ok%1;LmmEFs>a%FUL5 z;-<~s2Hvaa#GIj2R5%bIhc3VMC!0e0*K%2s`0yh*Jf<HmFjqb>w%9~EKw)VzB<yK1 zG9**Pk<nDA#G$G9)=bzV;|7bOj(J!GEI;fBxWM|-WutK_z2r`3As1cMf~aA5q#lU< zFX(P-e<8&WY8o|{4c3kB%75Ls>_yL`6=ZH`c=^q-Jvv=03DtU&qRtzGG9i&DBn?28 zbu+7GkRwYvhT2e$qY4i8q)I~=a}Vn5UrTqd(AOENxSDXo)<Y6g1+6e!(DI4c_u?)x z<uwRu2q?68g?7%Jy?FE*EeD&5{qXE%MUl;{!33&B%*_;>vg-9}U$KlHM0&=f3Krb6 zJd+_>VWfC;sk%E^i7{@WgUme3G98~=<dnPp=1EPlu({j3X+m%(0V_@;9g8imeFzZV zLp(jqGbNw<e`q`BAjzY2O?TPIvTfV8%`O{VRb94i+qP}nwr!)ky0A5KXYS10osGRW zV)ws{%!rdGGJlzo=gH^&ULd+{lZnV&Lp0nc9=o(LQj$C8WUCS9=!sBepA=dLNjG8U z4dxU#s9`QNgK+LH<_sz*K%_{eQQ#$B0!K45(KpCMjC#>P*@J0NLeM1#J#vgXYof7f zT*+cSt{4U@Qg&{g`{rsNKMy2AxC&mLaX4D%DSBIvJo(X}BYY1i+9xIt+jEm7KYyb^ zOp2&sOj&Q$6KG7RRgY3?j>b6O2X*7E^5}LE(oG=}wmZB`o@Zvvs<pX{O^i=xPFXV1 z-$`%TcU$YPb;sJ+K3!*=-Qn&@vsKNBYP@A!j8SJugqtMXN@KhytIpPf)E(X^K)wqG z_8Fsu6*&}ouYliE10Y8bO$()-4tLD#Q_)Jh_YI9KZRSMpN6?6wH%r~e<!vRW{cAv| z(t*X%1N4XL$Vg}>!VQcGWXa&&$9CC)<UjHREO}MEP9>3fYk_I}DK2JGolOq7-Po!6 zen}WuAR6c&;Sgr7)xpdl0E+9+MYuShzRqLJm<K(k#LDA0{zrgDSI>3B?bc2KgkRNs zo@J_3Spv<eW4sWSyJEE6D{Iy)$M}US&ALk$+wFF@0<yWFD88hEg#Dsmy$j$K?)LCu zCj><ajU4-Jbm_J$Y)kLu`JVCn1X<RmHu2jQZ(PR>MJ9`$LIJ+=bRwYz@?t*xMHxih z;%I)<%ZT8b*I9NC<~WBNBmI_!ZC*B|tHPN)pq4-)<_vWsNIxhfDF8`LcOom-1hd<D zG(tvlp(z)xV8f;W3g1z>i`9qC#~)wVO1xB%6Zn{*4>pCRAAUTR9A7XWDc0|Vm(U+T zt0)Qrp`%>#TYA~V22!|a9zvffgshJt*Ip_*+lUx4ZrvFAJ`z-Ta?Lhg+%oIQ7wl{v zlvF8uwo*d4!$7_S!|)W@_yd=Qi9bYk%@(6J9hR12>9lj`m<Uah$d#b<_?&@q(3UWQ zgtDH1A$#+|(L^R_VHLM1ped_v$#LE+Ql`B1yvSWtJ06k;{fr0ziBCMX(5L1QF0kHv zBAOdSo(X$Ubsg3V)z>8l$^`@<>=NlN)wmwbH5*5#1^U{oVccv3jIR&|x{8|-ePr*n zeg;RlKN1uZT;SFU!tj-zHa*bSFx&`cJ>YzXi7>}3P!hu9z$_A=Kg=tn;$}(arJ!7} zkv4Fz66#U{tSnvCD^da{JiIBOj{Y<HM{>Yiao|X^&@M{u-)TgVuP^5p@KE=i;Yh$e z^1)KBooO9m*`^fmG=_C%pRL@vM2$;DQU?)P%^AOcrW@*^$6g8dq`eU{HbZ>Gu~m9_ z6X+fRD~^%ypM~p=LsjpgUXQYF;_Vh%N-$pSiKtK~BxjkIlQVEr@)%~E<EiV(Eh>-2 zGk<em?7zw{8HEpK&y{(SbRQo^zLP3`#g<a8tv^4<pDyIpSA;O}KUZKcZ(iYU9c!!S z5D@|g%mY<V4@|8J)+tdRLC%{TyW<c(`m*>J+vp2Vs+8D4hXC(6V#}Z%W0?#sGXOu@ z9CD6_|0vu4`kO$>q)2w%9Q3)dUP)3xL<WMUKP=E~9QwmCGItwt{&*dSIEnYDw9T1* z09IEGEI$;gJq~<bznC{OSFML)ABa98QxSsKbPN+lel_iKPj1>V`O=v4K%{Gy;yCqN z>Uuv46t@dHH7E181Oq)Q6bDW&Ase<RVD11HL#|V0U#CU=oFxi1d+<La=@NwY-I<Ik z@Y*96hp=JL<bDZ<u+24TZQYdIMo>tkX7PYRvv}s2Uh41D0(9IbKM2fd5@IXm7~LcN zT$ayy<_rhVMefP9rWv@(LNJ0;^DN5ezUtEpU+w_7RViiDupuyxTL$sFvl<OWw!A)J zJOkOvpl<_v8_}?;A4hdWAPGIkOp;Gr__uM$)^Ei2s8PAri<|^Yq)v~5vs5V8ytz_} z#Lw+uCdFDTevB28!~yaqh^_djrDjwF`5ye2E>D*4+~qGMR9BO*1ljSsgL*hpU|K0l z{)$n`_By<H^WUf#Y{uVTi+mi1;oBe&3r|UZ-f393IB^qF$XVE<Xkbcnhd&SqL&~MC z8)KGG(ea^;6%*!GL#qOVDF0DVW-JObL`E4JJo}uh<v52`_yC=l5aNjsRnC7>jEt6W z)>OzZayQXbnRa+(Nr9$?%L}BU8*C6tfQH-3;nXSczfdTYLsHMMj9Ew~tMVN;sf7Iy ze6zD+Ok9GG+7}o+Yu5#8uN*8}ihUKphC4W3?E!q&0;jR%)8muNqLhh#H;C7-(e-mt zT)3hCpmQ33lXT$Ze`(F+PdEsx$6ShZHdX&>r^sk{kgvqfyT?DI-qC0+a3?x50Dd=% zq{;fq9RzZ3`%@jqPDrsh!JWp+UCm52HkHPQoJ&F^TGsyHA)C%5SgweJcpkx#4wfe= zUPZPv0GIHBzrJ)NRhF}_zemkW%c>1dA}owSa44TwTeOEew(cG&TwoWc&W&S3fNyaZ zQqe8>>Hhenj02+RQY?}j19*NB<p=L{E`lqzW<e1okko_UBngQo;P~JsI}j0H4XxjS zt&xR74vVhM6M>#g=a>VqB`P>o5-&1krYPnUTWJ$2o`upq@raAJevJ{AQQJS9RuT{O z!j>JjW?SaLH3&fM<UJJ%kQ0Av;%fXs{5o?1SyMtBDaocNK3IE<49Q+a+}LW2%eV$u z;pz{LZ>aIYog<W^J3z<H>x0m`6#80W%$+YJ+Lk!OOZs@#0<&lm=@gP(DJOt_9$=#} zFka#mXj7RcI<-+*nmz}xDg5Ci65`(UwcS9`<z&i9YEHqRGiJO1QdCBEX(Vz=Iq3XD zxdirF0zRF*{@8lSsWjlcD=Jh|!g1L2DhBBwO$DLN$}ncCNq?mBY{4MSR-t`Ws>!fb z^luF+sX=51Y=6A-p%*m6R)HPL7&fNzzWqd}Nv*%X)iq2c-!)zo*ncsaIVmbLCxej! z|3p)<xH*EJtEp&=2kM#++k1H!6f5<li^s{>khJkRG7IxgN*Nc^hl6Pq?Wy;jwA3bi zT_U9t3IYq92pQ!*&72=Q27=bNBca00)pZRCX~lB?2pYW5kqBo@!~|BnPGaTR*9OKV z3>M8@6&juQUOIbPcs1k@?9WomeRa&F$OF;CgwJRj6{x%H?J}yRY>)d%j^P{rYY|Cz z#VNZk6Df0;(pE7%gU{7Ph1eu1RL=3*cAiw2w-#5FF@0{ZcAEQqxUB)vg!Y5vjbD@x z>tx#hd`7D!ZF(|eE)p$Kp@`Kegw5^8*ZuvhS_vS&=v*$)cM{B-Y24AK7Gf}8mq7E9 z%#^ub8%x2}rcN=Y;!_&!QEULst4aEte|#T*i>aftVc|B;)Tv0jE;E+_91O=$eKfu> zbNMhxot5O*aG@M?F^E}ab1|4Ds~)!&EzGaQ!XkWvo)#^{f_QArVPiN_au*`mG?`fo z3hI9D$g!z1bH!CJMWU5jJx{O{fRSeC{BBG(%*<_Z;&SbM59f)wRz6cD$x#%bgr^AR z2-@40xU~Q~rzoTO&706WRK&$K#NG)mZi(rXKqt+!ErQNW^obcb>YOGQtZ5iQ!=)hS zMR3HstpCX478n7STof4BVO3UbQpj`fE=^eTgtw;6CnK^`RlfDAVEqV}nMk6FY4&7% z-I*82LYL8}N_ju7<)$#QRJQbvoFY44(0-SgO(i!&#;J4Bptq-nW8qQ~ZOwbP#QRv> zUKr&5`b#`BODz!xk4pGLct}#OU&wvE(=R|i{hVA_#|3P<xchVUsZ8Em(WQr2LW=!| zT$>tlN;1J8cTYu2mm1jMv6r-TDtXSx<!ieY8hnNV<dm*)r5|r9G~pZWr>L+>=xNQY zI$Z0x)R*qUj7IP>W4Qx{oZSrU{cBjDX56M(_69a0E11RcPZ6@7zn_QohBf;C6sK^8 zX!G*X;^xKrOYK+{M&s*!LI$QJu8chOoI|NYlubm~@~5VO4dp*Z?UTCa2gIjNscXp7 zwQZeYheKXkZ(ItPgkf5JD7AQQ$FHA)1UyRUO;A6+ek#0B>}hz`w8nD_FM;4k7a}Nu z09o3#-$<ff%rig7ae7E3E^atYiOX2%-<nOn*M2@OCJj%Q31t!qp|Uwwe-*^glrbW! z^QaxyR-3{cx~Kg<Q+MJ-<;~L$2o{dn>3N3|Mm|0zfqjBV=^TY&S03$}uu{IGHCcu; zF^3P~oIZj#ZHJ(tw+mT4<4#?`aB8V{A{tP<jWAa~bi7ou`UV?Y9W;E2XS;$jhLx^u z<&GKQfMwSY?V_B|wskJSy=_MXy?f#V^35-2`ri@hzo&Xxnc4mkist<1X!5^n(fW5p z`fpL-e<IR<tCsp7N!5Rsy#Jze|33{y{})G^gW#(+>_5WG|D{3sUk1tjokag7J<Z4p z&BXAx`PC|J|LkA?i<tdSb<T_|U(fPyTATm68r#=ue*^pfl+6AY$j!v?&u_%vAU6}k z-(J`MJLI<h@@4;{vMUIEl#-}yg^gl%8&xzs-`YOnS(u)}^T$;cNKIE<mrEOe#?HK% z1U%{Z;`exTL8KaMKCJiwH#gH$y?y+By?vBV6ro`g+Jok>Pvn~e{+(ebCG`4P{PmQy z@^~gxyEIivwTfTQj33X>6``V@9{BI<y6;U}Ha7e0^_BZe9RphrMh*N`@*4vmWFM5> z?^<su8yvDKIzxK53VCrr!6&AewiCau<F+I44k}A;Ezx^ITz9&8!+%|e#`zaY8s|Y4 zA>Q3Z526s2J!oUG{7GjJO)o5ud}SK0;2A@9L*Dt2sgHz3vq(M^?Stf*2sOoApm7O_ zxxk%y7%_eDG&?pY^LDlT&U<FW{$o<`j1wcR;GrmJP?R>Fs3k_(UGdEB17zHF$>$rM zrdY)v;fkTYkRO#zqX$ds?_z)&=ZggkkCoe#9#-xK(Z{XjuJ=VebM400&MlS<&>b}= ztQs5qP4@NUU!B~HlV}MAW+T*|J~0rhV1#!Bet-(i9p2Lw#DnYGUyq*a*Zg;_?*<38 z@(35-?ibpdZp11EyjoHPmkOV#FA7T=8o+qcBo9;NH!jmNBn-Xyb_O4No^<*B`HYT= zR$`y;++sAd;ID2v$Q!8QhZ9~!MY9Tp2HiACL`nB6RV3k#YE+esS#gUzlqN_fBs`K- zSD{bJd%1s&bd&5kK+ddxPo2`sN*^J=#9tnxpoxW){rr*mi6b3~<F5C|w+-xVnou@Y zN`gmPg%2shxkU><4}?ZN9V8asK<T~>HZ)n<N&yr6&GXkUhY)syY?EXO#%~91A8~Qu z5HaYUwa*M@q|I}w;hVUR_!&>vxQ|T|c}RbR|4{H(PA3_RgTttjxJ4JS$k&UFRUH}l z1Y)H(b<)&Plou+cwm}Rle{$!zK$umX9FZnj05xO_yZDdFxzR;X7$jCO;V0Dftauh{ z)SP`9wbLf~{}~+$^|e_sAe?TJ?;i{0ameW1sEQG5eBFA4i`C3Jq@vb{1e$Muf80Ap z;N@rlLYBi>1JWcZa;OTL8@hn?6Kv!fvD{~Kh<~joz0J*h)ir|l5A}La%}4jQ_6r$S zcCxh7{AmHA&rdz1TgVLlbFbHVs%9F`>g7-IySfL#4gVO8$O#ptB`6c;<ETfm)(T?- zV$hRTe|x#-x8VpxHki4@r6e%_>U8aJax&ruGSQS2bu`XQoV02Jq@Nyvt5(UHrQ;%t z(542E*o1~!ZSM&5S~^7Di3~6UeJIzQ4Gad(R6$Nkp^5_%9mgM!aGGF0-H|K(l{7vZ z)-Y+q5X&d+@ycVNFlT#>3sf~EB}86sQZ1&HL^wo`5{)M}wbkNjL}Rm6<s`u+DoId! zXB6XQk|m`C)3aW`J)4k@sidhSE(fky2j7e`C5<-1QcE^kw&dW+O>~Jzk2auz;V1<T zM#7D;8Wf7+RHEhlmYky<iQ_|oagim!6ubN}in3&KvsX~(<`8bD>9EIpFV&x(hy!R< zm>)GJ8asG6APE748f+@1kF4t0NpsGiKw?L0r9o(U2GKIuz}XpNtWnM!p`tovCqiFf zik;s);%NDH62@^(>SHWhyR90GLQCTBOK_*YxC`Qx4QLL=8Ia(LyBi#Cym0-HIe7W$ z0Sa9NLhzFVij_o4MkRbqn0TPTF;W^Hn}kZHx46oTnC4+Vs+So0utro;QWv)k2ot!b zUgVmc%^xqW?=(7!prt)<%P&BHtoJ0c-i|>beM_;^aJ3VWb?{OS((g3+MeA@QF3;W> z;+R#yya^^W`&)>Z7%)7q><^FKuQ7Lj-pedrf+Y#j3_Esw+va3o-ha0)7s7YFm6jp9 z7Zq8u?Zl2gzQheeeCGEYb<5unJv02|$ESr8-Z_gSehJxvr-=oHc`SN~dKT!Eb@23q z0CFmFi-jc-3xZ-o@b9goF<}XT7*n=2LJirmTFZopNVRRniDLll6W42gUI*uqiRe#X zf(J3<MefCGs)W*KrqM~X25E=J1ed+u-6X2SIM)o@5Jj>&LjLS~{q4BR+F{lDW3{jy zPhhQSgm3}k0)Mbn;3sQW{VFjU)z~gU<*d59$dyev5Jtr$D&8C-dM>CJBc@xhc%n#O z%9?F|<l;)^bfYdG4cW+qfI<2cJ}3w}k0E95Q~7?Mx~s@nt6>{120;_T2m-s+&ekIt zj=pt5EJ-oyk0)wGYRxhLg!Iv~{kVnEz`pQZTn8BoJn{3Ak(~KuL0vs(RVtI}x_`a| zD7+sljWbN6L7I$ry}?~BSF#v(@{PwL>_Ut5RfLQvQLLioT1^9eau1I0oxQG+-Xv6@ z7vlv}aY71Q3kx~oGim6eI)TeK@>vk21{wm!{#v;Ne?W^=SPq1!5d=bIf8?cB7>(qQ zU(F2j*L$UySTS0u`nrk0H9^2aA?xxzX3Q+RSetT&)qzjYT!wC_A)VUdOo?hgpn}Od z)1;k9l#!B{s0zSTZ%lCOI>)I`Aw?wap<!iPGE|P(xW5G#-vo5RhQ%{P`|5ND^tZ$N z&mfV=^<Re4m0;l-B-G*<dj^H)dkhjlMeyWNSQm)<I^*Kb9n67~KA#RDc@%@Hg<rKv z_RECv8j*VuLN!3Dq0$2H5BTsABj<YtaY1F@N)f5fsdT9OT&ye(l&O)0+6d!Ra-+7Z zT*zm_K0T18Tq_y|R*r-U_~i1)@$_J#RLMML@U61+TxCoJE=nZ1DJgrJNDt~0S8(Md zVNn)70K1=!9IX_J@eysLik8J89EmebC>0<YHm>L0^{cXVy(1*5TiNUq-wEn|H3mGN z3)`pJNcIi`1&cPOkHf4Ay_A%6HeX`>(dew1_LYNOL6HzEuk8_PJr;J_OXo9g4t2IJ z9A>HZTGl5t<1)~Kj`3p99s5DVsuLu}NE>-s9|9GZUM*CRiY9gsKmLgJP5EL%Odtyy z?1050U!gA`WyFFEI>{ypTtfPQ1vzLu+jC{mqL~I=_k&tR1oakfFgq~LtTfcWA#TwQ zS?oM=B2z(EH8hX(wX#2#z9O_-5JUZmx-^uU<2xfb)xCY{@b>DEf`t{%y?Lss_~Srh z9Dq$`VpcRYALj<oj!~vSJP{HwEJg#jprV#v6MN;#FEg>$rVY73)32HO>^+hd1iA2P z9kzMJ*=X3;El&Dle!c-CYED&bPot*dKB}K;?OOCbFRXu8;8AZ-IlU%Ta4-ZL_FSg1 zW-`N<G0~;-HNSutq($`LT(PBUeuX&pa8b>I0nVVh$K_K-Y?<I%W&_np?pVUW>ZB0u z@wSyW3+udnQ6C#0mxZ74?KJ}{V+Gy=NtX_#CU!(<XUx@!kanGPB}SIsoVT$gWPqH< zM#z;)VRu>)Y5!IwcRm1ZL-USOQ~hinC|bWdOx{c6xjSNC89zLzBe)%bkgnv~i$%ss zV5@)_Qv@`=&9RXi9yX&Ax-nNnc{Jh8Q$W2rsxJiMp2xfyG$Uk?+Nbd0LpP;{n+I7- zMqd*2GFFJ^Oi);YcOmUAO`ztdE;?Uw6m^Y-5o3~pZK9aOj|A5bX6>5AEsfo?Evr8) z(@Nok&kW(UuJj$cX=bNzK+-oP=jL$d4(HB~Lf=jP^aV%!)Qa4xlK-JXEIW?`i=2hm zyF=Lslkw)i^2^n~$Pp8da;1=rHP3;!ab|*mN+x$0wq-i45}tRB9)S$va@iDA(uD|( zBTSHykKN^zLXU|*BQ_GK!BkAyo?8yt3v*37vzw3A!xd?J;G^(te6Ve-cBr6^Q<<?r zx%#Y3A_&jWPfkWphVQVmfS4#Pxwr0UzYn?n)i^4cGu8}*va%U}j-b}&GlbI6EM;>3 z@nBEj&#U1NV)`6&-0c)f_<a7=@!`&2fs*A&aCn?lD+VMEF9TVY=7C#76LMp?&qM<z zz$fXKZ_SSH{kYC9&HWzZn-Pz>8u;3c4V@lxrUbcy<57rK%%K?9ytjb0!8?N@)9eTr z;Z~7T;X4DJeh#}Gb&R^3^y5TGFP7|bWqlTSS!n%GC9(IFlT1fYW=(kx|F191X-Npi zcci_ZCdJ|}bTmVZFsSHSa#0;>BYEeG%klzj8~!G~@h~bzUb!{Q8?e$60Ff7h&9jCx z@Qld_E+L375`OxDdvVuPYzw<u_5=!O!@)qSyJ&K0HmKhah)!B$t)>mi%Mt?TO&b?g zI&S4RgYc*utK=qHuI^~UBB&F+N?ez*^%vYFsu#hf7`fu#`92j{U@h)_8E|*j6+AdU zbqnm*`1NCGW)@ZXy{MBgbg@O<Z-6VEcCFgob(`Q4jCW2d*anH)wYKBZt;?6u^U^K~ zQjp=dE%ER-3&sTt8>u{-;CX!bfE~nc6-n6D-t5lsc+fa>N=d{scT%W+IaMTP*V?9t zM3PX@>W9u$*1ny3QowC$s}Gk#yk=e6lWrZtP{}*0Au|eJ7!Ixmw>TYb=#-}j+S1u( z=IUgKd(<jT<f{4sC47doAs?3flEuy7s?6@Qnx<sMu=ayCO2%)6fYr!XAx^YEwez06 zFmtP=e(VV&2vDmbaFfX}rT%`tM=7;Q+l5o}qSWla>`RFi=*pyRVGIhX^BEn&fQT>w z5DyOFo|+;9sz<r;0TRzxk8cW-+X=V{vuAp?5Hw{3nx#j}3xgbcZ~_BwA3QZ#h(TdE zEdsZHAX*h8GYqQrwvL;aacOoyp87Z%y88R19Mp?U8Ea+%4(da)uD**cG7k9+7wig& zOcxx!U4=xiT$FOHEl{2kW*iz?ZNqj5M6^z|a+0=t#q<1Eb2aQU_O&vuFbLx^s7s7= zPmiN9da=&^VS7E#{)j8C+FpR1HKv(WxV$?;vmhm{=R$K_-fo>NM}_Cd8(KvdEMDO} ztHVL8$W~8L3TZ^q4Se6a8bz4A2tFjx&aoJowS;O&)JcxM%Us+?D+;V#Ff~qC0rtSq zy<==46~~P$EGm$5UDia@vlVr3FCl+3>d@DBI)%EO4WIrjEMex=KfQQ=P%Gd35R+=> zO@(y!ZkFG=p^}lN?Ynj=wGutHC0;$YY~&Frplxm>1l&gwMG&gGRSSFSOU4UD1nr!7 zdT;WbC7TrAhli&VyH|23-@7)vtyZeK4Mcpl`EQ<Tb{$)fM@&u|Xty#;c7TNdi_pW? zE{}KO5c}If498~1j|sofX(wSrbNKS7UzLr$6(#<#dofRUphCd)Y9@nymLt*)KKZCZ zX5ecZDfkLW{0O$H!tsD_%A2<#EZXwv*m+mY(;aGfAMreUuY30)X5Q3}QAff}z&U)o z^_hLkE&tzf%zqs1|03kEF#JOhWMcUH<huV;#{GZCF-#19GvWRd$NUFN_@4=a|FO#d zZ$a>1)W3hBFaOC{{*{U_{+nhoCdU6}vsjb%M*PMwz-vUEfS%5V)SofH!$$R)$}Y3s zh<jZHk47C@TBsvd(jT}_;#Jc}T5bmQ8^kwB&ZZ;Q<Xy*?%<HqW>$5rTPl(Sf{C#qh z%^B$HmZZ<0J~uwUchLpo?o~QfzUK2oXpX+oOb4`vzJ+9c{vHrNX8zoZdMW66Ew5sC z`GCnj`Zzaf;JYgKQg?Q0=y~wD$N#wgJnBOTC>?!&(H{pjK?K?10N<L_yE$e4c`{-V zT~hT4z0XOSCrI<9n<)#42tnctdAgK-?|XDT>DrY%t#LjQj}OPocwgpGcj1$NTk^T_ z4E|KD`j~EC<6ZXXOcHK3Sy{S}LjHlbqjqs!y5l@!Qj(J+tpJKFtLXEgp(-Gtq{aTn zXeE1Dr)dslr*TTI=Q_Jj-RLOOZF1h%Y|Q+1lNk3`g;5)bUq}mRlZPuT+~C0;S{J!e zUjNnX;-6?+NniO|0(6#mG#-6}e3t$q&B~AayiMHZ2c^1N$oWzXMFpiQ?;6uXFK-{? zk5|b4M+fbLKZ2vFIriXMQ|}xR{=ma2!Ghp!`r|)6Bg4#PBz7Q3IJ?t^`=1wyCA04c zCV4Gp!Vow0yogahSIW2g0`)94nh<OG;2lgg2sL067B%A`43dXz@bn%_=|acmDMAxy zlmdP0LxxI%yt>X6kq88-;$`00S)G!P1@TM-X2E*Rc;lt}P3(F#kCUUU&Y!mMS(+qC z3u*LmoM}44vsX1>vRSS(ni!1plc!R?H=F#Zv$R!uRpFJ$CzkFM1&FnGCux_51zx6q z=CCpv=d;-PM37}B@~I-npVs6;5pU6^@4EFKxE6-dUr(#`-G85PizPiIT_BVNd&{AN z=nnM{L{;PxEbB?7dl4bTkqE{WfM)l9A3Ex1OeUZi-3!l5ypF;~!aMv(T7?q&T=JqP zA*>0tmH0``M04kYmrv5xFgiLo%pi#aMni6XBvvTy`)d%gRsz$Lvr$PT$P9eYwh*(~ z1@~P8&mC05cy@mga;EKS0S{el)kxVy%6Y`c={rC1TPSq!?dY)FBh-tUiL_s=s~e4| zV@S-q>dL7+ehM%e9uXBqF2b0eg_;-yN(~{AJ27!x@m5j{3gslY01u@<sNGp?c9wiw z35JS9We~X^Wo>-Uxv$*hYzy<FntVw;E#RJjO_~0R2e&V4HxDWx1hzXgtYv)>(mN&; zn?eaC7OKtE44jk{3}#;Y#xFH7q!gZjmn};{Df6}BXU}K4`Fue-HE;Lpt5zVz{ef%{ z#`7%B4@{4P0a%i9CpRu79O_``Hp<%~R}G|uax%l1BSk|2ABA7r*u-T9-&2c1*^VHH zY*ls=7@3HM(V_g`mjVW}&Ex8W(l4yHB&QLr-?giJmi?Cqx*(O+KryWZf}z!3HeS!# zJoM&%LK`xJ9v~6jxmE%~!*6qR8<#qix73K@OL~fkj?j-f2OHo+0VGu_0#?DXSJHGL z_Y?qRqrjz6az>}S(kZ#IqI*bSN}^{ZlDjF{HNu40K$oTKI!jXV@3|UO_h4Za5|XFr zjMB`m$=Mc){ofYmp`xH)kJ15S^WBVPaBIlhR&=Al@vQ<fWdRly86H*c-}mVA3vbo2 zke*KHmz;%}QE!ar$?$-fBa^{P7iDPVU;lW)K><Z*U_T1FaWqclRZUDYa8^%POO9pb z&U*0(9EEGs(qs(pZ8W0s;pih8F2(CA8K<nfo=F??p&m8bThEGLSx6tE)y32k%sBO1 zF}f{SwcHja26saU=U(^0V^=EOK^!+&SJ)dVEk{X}2YnL~*N}bf826=|y4v-)6)IDi zY%Af!=4Kwh0m&Bog<C{a)c@R98%7EhkNtLPb}uV2A1*|l%)~Ixkw5^Kx}8l9&TMrp znQjSfxyFR))(iUCgbr;Ot4$}*0oR5)cPYVW543C4m3jB;xKS@5V4x$1M^gtQ<mQAF zc9NJ3-=iCT5tsz^aY^YWho$$nL?Kc^&05vb|5i*e08XW}p(UGbiT;mefR$QC1fqH% zLBMT*2_$?hoHAF2Syw%zJR***(j>>XJVFgeicA8oUUnJBTviTs83RrsvRs@JNyMO_ z^*o0%{n8LBDg}^6W~$@W-9U0p#F4pBek#tC+mLs#Q8pA06WrwYiSt%6Ei&#>gaP~b zB!Xx}e<-R4$W;Kiseba;r%NBb7ZxA=DWG<bWs5P@k5h0mB!1lWp&m_Mlq9gzm<$h^ zQ_UFUfyc+?OniW5jha?VPXv|TALBTBr{B+#y9pR0(F2%(hHs$(*Fa$yG8t`|iCzFx z7uneYm04tsS2}<RT8Gjy$tw|1Yqf2uXClf{D&?&<DE-Sqg|vfN<x^~_pqkF21OSf> znB7<~!JwDfk?YmRDU#4u;`Rd!Z`+32V(`E#xejdL1WVLCM(fL3xJ85C13XsaH|>t! zl7hAE@y#{JQ1Z7=;~0Hffu7?eC?pU>unZ~QSi}ZxdBFz&mtA01Z!8D4O>0rbQ7G*6 zj*0^P(XDy3ibZNodenu2-|ZWPbORcVT}?Ny;S`~m4tUVWeg?4zM*(OqNxO&xaf5MD zSKktTwg1LC@zZ7v3wa>*eGxZSWhRhkdNQ46YUG5;He0^z*PzgvV2XMuWSPG8qe&Ap zmYg|=wsqEElA;xyImpyZdI1i)-}KSoo=#xd98Fc^!^AzR#ES*ZTg9H&SO){pg@>X5 zQeoMWHMlvbDp3dW`~hc<M>9;be$ho8Xu~p$-SDF?I#oI1-CJ6OqC$L31+kiy(^J`G z7$KAHVK9qvq!bu(RK|QvVInA;Qp##OEpWJQUM-*H)^ZRB2GdK7GwWSCKY}Et&7dtR z7P_y;o6&!61eRFDQS-9?v8UFZ*`*_G;E1kV^y3i=EKQebb4(eK<m_acmK-EJc9S@) zoJd2BEf&l?o7K81q)<%M3SjTQi~Bvp_(%pX$GRQAlh$HK^G(w%w^Avbcx5QROdhI) zClACvQlhQ`^No(dIdM6``^m9z#cecBM)89i7V$zSY*!|mQ4=$2Bo4MJGKFC=R@&Q) z>^fMx4I8QCnmiR`(?hKk<iT7}h?~5tWj>Um`Nsk9>KypDfh-||zLCMvnb@EggOCI3 z56+(#@kL4ZSi3qnJ|bbw>#X@Qh5PnyQoNy9CWJHVACVE7h_im&{a-Ww+|B{FyhrDy zjQfyp`MSAoP|(9ew?v_9ZnfL2FBI2`igL2=p7FzP5N|aT*TiGqr7l)&r7KXZrQcO| zMl>GF-Z-f97=0-B3bQyCeE}AW4jbsDetRT%*5Jd+Iz`~Yg>16HHu_6@t-GQ1AB6{L zrf0HjW9B2ooY{nWV}{2b4K3~!JL#*YF6-(0Hy5g<!gy5E6L7ieIK?;1A=Wy3i94!I z4C86pfv1ml6cEI=ROU;ZO3~DKaAgs>Em5#Z4ZE2NrIWJ?O!jhxnLjVyE!2d+3e}uO zUHD$@>ViVJ77)jUa;gcHtD0T0;idxE{k4o?4KPsN$|L<wPKgEt2M6!dRuIKw6cZzr zgYE&&oK4OsJsB(q#wW|L#^%lB9eKGhky1lP&!8*r*UGGi(({_6XK)!}B&&jO-UXt2 zs~oPuw@6o<rlX|8W$f_T!~rlPYqxi}M_dW1GmP4yvZy_KM1yjB8gL^n9CfT!Zq~vO zYRn2VP^4IdPNCMh!;sOu3%hLo?u`Vcxx)(>A6+duuz9+*Hkorp1>?8nI=<hT8kyiR zr@8R1cTbwHo4>EDmfIy4vwsWfT8Brg(~F%qIOaedohxGx>YA8D9!yS%9*PRAy9W(( zh>LzcBE|*CVg07zZYf^Mo+NFFu*!5-H1lN3NisCfJFK@5=&vrPx`jN!m84h0T~GBW z2VcWD!R5flvMIkXoi>s7Zdq%L$l53+3^0GpvcptH&XF;Kq6&ty9JdmO2G9i%lfeFV zYG6GT8})lI^rf5TJcd*M%YK*6bSc*DNqhFV>^#iAb2FZUx8t`rg%p2X!_;e+;V?*C zn_wer;;u`ScxD`b1TLx-mk@)&Dojr&BZj-%GueNP-ExWk*wlI3H02HW1BTW^Pypf< zDIne^XxAwI+*t9TVF`60`g0V~wdEZ%03i<a#M9ij!kSsRS#$S9t#MeV9=npB%fw!U zm?S`)P*2Nc#{4YDjvD40hrn<s;>H5XIDAXLeWveV!bYCQGM34s%f14RlgC`7b<DG4 z+BYYC=B@^*TVS&mZ`AGZJB2Qr&M2BGJXy}u-<X>$br_Z<`GfTs(Ua5Bvv>GWqx%&? z*2yNfBkX7p8qtTW*dgofjJxa$d7~sNF!0$Z3(TrM3p`6*t}*$s-afwHw3dO5)v=Ps zPWwYGiEGg<OKU33=!#>lABhM%#ON^q1`8SA;ox%yW;go_bU8ScKCG%VHWPNuhc9b_ zm-s2RQla6|f3>OS=z=WzTG<G#4!KkV7B6R;%emORMWZBYtLGra0$1V5S@#FgHEDzo zK%pVcmd)1dW>1$c;tQN<RH+=ZBKv>iiI7T}QFf6rT2z>07TO3ZKG3pb&k;owip`0* z;dbS;J{q5EkJ5+WYT1^Gr!u^+VoPYWU`9tgf{{G7AJW_pf|1~?=dwcg%wF?4A%xiO z_6mMU9c)j_PN?JAi(v)p?@vy6ub(KCYoEHXr)M23J|qrB(fQKGM1BhNc!=}<RV}A3 zO-g&y=>7`ra6y+plo%dyi%vmYWKnoalh^N6!JVuv1f*u}X56C>bbB8n^k8)cTLFwH zk_*4Z^!_BkY(C4-MTA5s03q@CE-g@=D$&14o%&8Uq#PaKgo>UTV9fbhyDm0YWX-77 z&!JAk3f2^_^&}G2b||o=x(Znzq{DB4${LH4zZULr+w5r9!n+RpGx!Y<?<TgevWc^k zdnoI+#-Zh95%p_I<}7NojJxRt=!smHL!x?1iO9Y{ZvnS|^V(T{esynEYyrqAAO~xO zRdZr*^#gL~gCMJld1xy&A)Tn$AJcR*Cdj05K_$w9b%px^2&%pKK1x1tai6*ZM`VRX zVDX2h1aPIXRJHYKY}3I^<Hd=E8FiJ*Ua)F730#Ype{3H6#@89_u}5jE|CS~1%P!Jp zk%gw2UE<3vDzTt3)|?u4kP=#Lem$<xIf0v)Xq77oRIj8z;LbvN)@Md@>2uPo&MHxE zr@lQ{_uwkpV9k(s$N{;FyxKbcnvT<0=II|C1>%I%Ei^D3z{S|9I4~T<Rm6;y{gNk^ zEANy~Co~-49i?&sCAr*)pVhTlN~DHd6Vi%K=)Zio1nkvfXW6UCoY!7DN;Ejw<v+AP zCB`7Jue;BKC4h;{MRvNAlgmf(B!P}ckQd`!1uR-B=nr11yAag?FW%A|fUvTrQd`5l z%}-Dn3)PqBS^kLG92PK5Z?1nL$cz9lAA6l^l=iwq{~MiJA=R{1jmo6(O;EQtADab{ z;HcH+x02p_|2x{aX!C{?PU~z>slhnzc{CD~Y|h5J+=tH991{urkRgil8>+n3{rkY@ zw*?03<bMNn{z@hO3UnAbIREnaUv4uo{Iliq-}7sj82@V(Bhz0o$o~-N{B@Q8-$3V| zrI~-kH@^&;e}Oyyf_?r@dj7R^fay!1`A5zD-~AWH|7I6sla}>Y=>Xs(TF*}&v$hU^ zvmIA_#nHSVNq$BG$9*QVG$ueoL0BY_R!GC0pqJ+P^7E|0r3C^<S46;G+$aLrE~0Cr zW6LZ@r_-*-Wv3O$TM5vv31|-)f1lfG)1%w!r=Mp?(mVgcJ%2O_Hfg5cTSGUGqW-Mi zci9GYY;t@$clF?%KlOXznYLYYt=C-Jly8;sEdAN)e~;;S%lPAd2JWqLc+HDPRcH)3 z;BbIp&+OtPyps8sI!4d+c76d~^Z4Ijyvd7;_y?uF;5KBy{HU&-DkSmYtqO<zT30S5 z?k?(8=+h4jP})BoR(E1;9)CY8(0<J(>}q-XxHhj&R$m&*SA1$n5#OthMwH?oNy<R; z_6;eLJT6H!%oLr4>4D)#j|&Lehz<9~Ip7D(L7rwCp(<<l#e9T9m583704}nqy1JB| z96D&_m-;n7Dwo4a%6yFa10~)FsJM`jY42c&E&X+g1_wm80Yl?e%}00UgTDoV!z4pL zncCO_m{BE9OGMcy=I)+T_~|z>nPnX+HPtap&uQ4JU(C?JDnyN&^c~+t)qY9FMN7UG z^JJZ)vV|%|xd37Nd&giBqom=JNsc2sFLqm1&W-XE7-1M5<&)sWEq-s;e4YC}Woz^J zadFCS1Sf3uN)kR|+Fre*>)M5ySqm{BqyIw`7i<b|^Cr3^d6hW=11t_-^tjQ<^baVv zxZN(gC_{6!-lPJf+<U0@POT`O#R&!6y9lTeGrKb$Pa|OSdn%f#{d!xu?4@hYWU6-Z z9}8HQpB>z^P&Th$y_{v<&rjDXI;s$@dn~>V-ad7|jCP>pohmgi(QSv%ckj9xqn)BV z3`Q&^FL943-7!Zj!wioSjabvC94Z5lv^q?~SCB3>XbpIj8#IqOmIWo{Ic%eha1(hB zdRe2L6qJ6P;hkte6isUg1Ak3e0@Ln+$AizKUa8pL^47}6J5d!?_+{abTt(s{2a!9E zQ$uCAhYo;mB8_aBK#r_DuveE&<{20z3PB&G=tnsJr9lkQk50r2em;n7bSPMZ1-*Bo zX#~<-4QkoYS<VMD{H>YfKy@waqkyWV=2wiphldW|K@V5-%gw4`^MMnjxW%_u#OpBV z-522OiUucE=y&8GScu)vPMENOL3yNb?PtQ-c-^j#^RL2e&5y^ch$H!$&nH|_=%}P4 z3$Nb+gKK_&1}4<}1+U@8;0BzNj9U{eDGsY2BH2021LuP>2IJ`MX==e=3y5uNu>8ae z1>PVJdpjhIaM~kTz_$q|d=q~IwtyZ3ISK$u3O=B+(wc$_RKdMCd!H00lTkXVB%lL# z136<sff6wyc`d<Gv)c4`%KV3519V3~SA9gG?<8Q=*zvwoPbOW|lqxXci-}r_)h#_$ z@JALg7r>IJNL16$?cg^hiG?(ekVL(|<d9lA!68@1HoQBwDiW`{w{`B0q?L|&KZm{( zNGlddNj)r%I6WDtSMj&7V9Q*`gPyRU%S&Fz`yUT{WDM~97`fm-(zdfVlQs_dRR&-M z(lE+O;JF346vM{mm86#@x_I)e)*Y?bQ|B+%wKyiXGj$VN`7@y16~BoNCH<Sixsl}F z-D}vLH8a#%vlBDQ<FuXTKz`Na3^4s0ux?TWX~|iN!<>82fFn(5Z~qv|f*>yb*!i}b zC!Y@)-R*MRv!=IHVLvg_?Bf3$CO>g;Y}q%!aIK&4r9cU4>>+b|M{NWuB>NbL|7;W{ z9S+$L-=snV$*sb(Uki-R19~P<0=oR5pq=P1`@E3pGHjKY#m2uI-9XZ$Z_o`$l{qmP z+-+L20C!L#L*L*OKOedcwqQFS(MZJ?o@Y^-Ngj27AIJQaB3&{=$CgmPcm~%z&ALbI z21&*N@638Wi_tUZBnvKUA)XM(VdNT#IcvwlrcW#`%pREQCBBCO%LIWkwEJD8Qb!IO zS#-*uFElLiTbBi)Z>YFH{!zcIUvRlFHfA69+GwZ^;UO*kPi*#;O|WcjA@p{oYuGq% zA00q+8d%vr!$U)$MQkwor`_K0YyqFn%Df*@)Po$fsh{xk@=+NH>e;=u8%4{8RQo=K zODC;kY+@U*+baz0`cGF#>Hbsn9WUCN{Xfe~?>5wo#A7`nIHgKbzr+yufp*Wt)kCH} z2-e)&l?hSWxEupeH2uK~GjQcYAlvP9gi+`10#HHT5vX*kiQw8Q<z7@x$h<1$lSn=+ zw5#|~qg`CAs#rXS^-sh5U`_O!I$TjzgIN91c~w9ZAPYht<>=SWqb&)&jV9{IMMRHJ zC_6^J6uZU+ED<X_u?qp#DCOUJP5G9(_eu`jHD*Y@E-Byz*oAdx!rY-!Nm_!BAMLt2 z56X*5bBMLzDoT{}b&QXWH+40?h9|rpG;h9Pss5A;;iicIKebG$pl>`w9F%OJ)fCzh zbpxApB)yPC?sp69q?=0jg<s~D-`507ryzzo%=%{HIw+1}ZWVSJjjx(pC(5|)D&=P? zk$=$D6c*2H)j{Aw%e0bQM0Or~aj~dOMu0H|=_FE|4)B1rE4RQ%E)4*pH`q#N7%_k4 z{eA-!8Wmnh9A*6jk`QH_!bMyHn_eo2!Vfyu+x>|vylYF@V2NfIQ`#0VlssfP=6cV8 z7Y9SttgqXZ#$$-$7bC#Agf{%0&rh5MEYe_%*>z9?m0>b_S<L`<V?*>_9{H@Eqt{d- zqOL5%iy88}QDSEV;$>oXmb0Q%iKd7lje-aCy`u<{D29V2geoJNjB-x7L3*v2l2jn8 zR;}!;tak3Of3vaIExt*gpd>B=Ic6RuL{D8V(#9HwGOhUq$;wq68ct%N|CI<f8C2@v zX9yH+pL|^|sYJ?qj*S52Q{3}~IY~>GBxlE_8Ncce_}$oC_Xa|g#r;+fN^B6^1ZA-5 zelFhbv;;QZ{H)`s$XN@m!~zq7!{toAlC6`FQvjp6Sb+-r9JXjz+-*T?P9seX_F~va z>I+g8vZ>Xc)U@_#{g`$t_;%803)bS=?oP6AN{;szNhD!z!>fAx-EVQEA8=?FcrNM- z{LKZtuMbU8idHp)j+!mDEg(NiAV^s~=t>f3LG~2iR&hGlVOrSO7mVPtRz@=+kE~#? zPoEFSDpDxx!2mpG=K!qM#jm+u3<iSM2uF|}KYOj#zC1k*ou;8+Q%{4;&N#3wlmc7W zRy%#=+(Hq2uD0~y0cEulIPK5wSKASxwc-`rg_?u#r@#fA@icrnmpuY!IeImwKK$cq zXg16)NYr#bg9H}BEN}J{vn!~S&~Fet{Y#z@w{x@6nhv-7mEevo{N?%^VbWTUX=3$| z@b+DfI_vsX-T4X)SguF1B%LclR42mx0M(DyYMY8JtiW&5A|KQalLt*!vMMf1#}~sJ zm+a)Cm<sE#Hu?_aXNYGWBwB(wBxbqBjc;Sg9ixlvQEK8ncf<wMAxSfea#z)x^_tL8 z)w<879c>#`p{A*^<ROf!29M%Tn@P-Vaf~A5<~F(bIoJv013wkFT%Zg0zo*c~;RXeI zDkXBw+#Ba`FE&%CT~oT<$BK;nH~~s1E@w8;!YbzKNQnD`6HNN_JQ@UagaDps(1HyW ze&GxJYJl6q4UrfCJ&la$G!VZ&!AV{vv@1f7TfRMDh!1`Qpt`%=qeN&Hx}{O5PQMZ1 zAqwO1^E;e+oMr}9jD8wXY%m+?XeWCyRp`g`7dVR5QS@A1eiCfnrL<7v$mIg^h|-PB z4?R%e=5!IYjdZsSv=TT0aP0>|L3Xm_018e4ijM-A4vR~M^h$Ag7nb~1E#PL!WcqMw znEWbQlFZ5s=E}KM=CiFn(SkT_I50!YAyQmFyDwa|s?HI@hsf?yz>_|}so&wLe!Gd} zT?SzZ2tvzV@?hbUyldxIZE9QPZ>U-`zowFcXsaZHm)$T3K!oLq3TK`5c$ehlI#2E@ zNzZ~4=}ON|3#*es;-+<RhgJsprtOod0Br%AA=ipAUcIdWHKGX+6AU-ziAP6SHDQ89 zfQ5J3(l-|_ms!XgT3#eVd${osEj7wnP)O^%xc=1VxmUCx!Uv$)B{@@Niki*e8ecV2 zkDbdf3ndG9b;w2k;v8|Q1JFp0Lq@x_ZPh4%*S*Xa#^Rg@DI0RvO&xM0S0NUif{!w( z!%9VHL1)`7maibO9vMhVkWR*3EWk9LEv%NbKwf$(9RS;#s+^nw9L;+MM8fKaD}W2M zWV<$imM+a0IAa(_)@4_1umErq?YG_KE~s7Fi{a9@OasbdB%yJR2y$(H6OX}&z;)41 z?oeVT`%|f)q!e1md0gAp8;U7BDP>m+)z~`RYcUb9$@4Sa`r9_sfkDbQm%Y#d@hmGo z4SE;9$3gs4GTqtuKZ_Mh!*N$to(mfLG|vofLzSv711}?&fC5M4QfsRdB5AjRoPlD} z^pXt^2t=G57au&PJKWL{ARQi6p^SS5=0T;of9^9tFp*+eXv`1ouXUL9G{TDo^iV{A z>L0AG0b5adM%zKV3xM?8o~K8)Yyn|aLnT)DWsP9=6gMS{6J$a_o`@CEK{19B?#@Ej zE(&MmV=d*bZbR_4uF^&y#Pf9x0%qZ;vPMdzgA#AcggCVpkbhmYxb#SC2gMw&E!OUM zGok~XHqwZS?smhU51?j*_Xeq<gPy-<gF2pC+<A9`-}ScZtU!P<*o_21XgwTkkbrbw z@c-0OX8)GQHJ&ws##-_M_i7bzSr=OC$0AbHd!=)I<9u;1V2`UW?@4*4@;i*I+D6Gh z!^@}ACyzAAMcefhrTWD@VONXr+|f&UFRCK~EH&<g5sP~Egy4!Y#PN!f{=6n{22B(` zEG7xOK2N}#1nt-Js{)=0cd&|d4=`1a?$wG|#X?gy)uGNQSt>Xb0=u7a9-M*T9pg>q z6^KSO`DrrdtE$aSuT)y)elZWOWn(a+;GyeMbAF4-REznlf}ZB0nw$AL5j^T?t<ghp z6;ZlA+&Y`9EA_jpD!$ZrJGU*CxAPuOWV<fxz=l_71GEi=89g`SHT9oQiw6{B0*9_c z`JDaF+#LbGK@1xZJMD$sd@6BRi-o&b2A$oDJYq{>!qpR1N_yPrX9r^jw+zZ1yY@03 zy*jABfnx=cxni)&Oa*S<d_u^5UjqAg<l^tr2{Q-Bf7UH9eWhsraUlQil8e8DSiU&L zKU9>z9_#<7<l>*BF#m6pK?HwuW&TSL=$|phzmSW+!wQ1`qNV&jOZ68`=RaY^-(pe! zx3Esr_l@X-FD>On{jnf%YSyaWO?v;^-6|u_p`qE)RKN%WML6+XvIwo@)n)H3KRDF= z`OmSk?zsHYIF^QpK10d6j`nM(_V%6npQ~EOd-VH!5z0ABV1Ljz>Ss)8oEAo74#nRo z{0F0Wrw!v;iQrbP2Kdr=?T6Pcw|_^=c2`aeC2sw)>nF=m|Lv`8wNXi1ah2#bW9nm| zH<@|@ZqbidOOcW&2zvny)Xq4;{+Q8VKqPhBXj;>^O+n5Q=24A4m>wp;)Ym)6zpko? zjGlKQ^DNs;vj~%MsHx0yT-44k?=!XDJQ=Z3UXFgm_T%c8eMZd-%=@{Fz>16}+D86z z{G5>0eB4G=Lly2X1``~;2Fi!K9q!H`D<v6RRtx30;Ad=M=N2OPEjg{6O}5M<xW?fz zq-uPy!oJW3GV&qYJ#eD?B(f)F73871i=w=R7KDsI8y=Ge{4r?q;^MN2Hb@%3>E7wf zY{x(CD(i$GH5EKs$S05XJwKs;yjqqdftX--2*(RZ?Ndz0WJUaf51QE13%{8zWG-@( zuN~{uri&;pa^l^SSw1Rep$pc_)i!~|f+mh_-nlu?w<tuO$6>RGfU_zDjXP+bu!kA( zRSXrN)DeN#HgPAEIoBw4rd6}9{B1GcQBQet)NX|jXNHT3p+~!!xN-Eo9b9&-chO!N zUcW~;Ug%wa@p)|$b0zUshc2I3HVKmTNQ?Jp)j1MUzsCAix~1O9vgm`Q;-mJpN~>tM zt(T1<pDo4l7^!~7_pw8*EikM4szQ}tRbzVy_h9@x>P#%WZ{5}9S+8q^FO1;*DQ<Io zC_#yV;5N?}&6^fOequ(8B60QyB@Aaf=A%)C5^95mc%m|z$&dm}3Gf0YhOif@1)S-e z=?dtzqO@MXdiOLd&m-(-zvkxw61w?Z^!D!};zlzSB{YIcvy?*T)IfbYvV6S53|^#o zgoeO*>Y7~)zeUr0!FxAGFJkyF>$$XQU@+@hVjD@bbf?cDFD+|@NVbzA3<assa4Pzy zqgaUHd?;3uqr;uk7ETxPD<e8}CO8P_D1Qg&dCuWU#zajMKWb0;41!t!+`TlCep{Y9 z0x>9s#8UO6oWYaf7Kh6~2~lEhmj9Y*MjYh$)V3GfG0~m}q4JZNcdNkJWypB}9&3%k zDGjjDu}ZlLH!ierNuUD;m>i!JSG}}FJ_xUC>X^j@H;96dcyFRkZ9M{)Kp#LBE#g!3 z3Wjf=ugc}wOMr{VIm6s_k2!Ob5RRegI?V}vkOfE}&z7pa8m4j}8;-#i*B1xtcaGbz zw62@{d>Q&_p#lmfpfxUgs->q#iV8WOMuU6kUdRL+kLEGIDcG+w6EcM$f=2bfczeen z$rf#0yKLKbSC?(uwr$(CZQHhOySi+2m#d4n){4FM+3S4!i*xsnbAM*$%o#H>Bjz0M z`;PGp(Ek3hbt`R7nbd*1EjAdi#ZI;$nR6(4QBr>l5LpgngxWzXT3^LAK;UFz-f?br zOA;-_AyLe}6uC3h7K_I!UnGE2hKcsfkbE$Jmvl53qEsIqAOM=5<rY>9PeIrs@%eTT zgmKq9NgP#idP--lH<nF$lHFjGqiBS1?)$f5IOT_T-3}{j9fUay>4E}W%6DWt7YL>B z({SyA9txKjt|jFgUmhP)K9uKgX=P`kC)dL^1$VX|Dq<)VQ$dJc_)%jSfL3KHdWoB= zwQNif<o&;#TheJS4`1I|$jdgz<Os;Az8i9A(3xr;0uZZj+ZP(Z=A!wbICgXC6hnv< zmqs{<B@au+HmMOEoL!m@;MT9}D`mbgJth;)L!?J;-<_$FgkOw~J|E;_K8tK16O^C; zBG{-_Mtb^zCgxITQk>lwjRK@2V(}h|b+h{cLA}U#O<kqwUR<wQ{4n8jRBD}}lpXS0 z*k1!R-EglPK^)vjcm`1_oERx=#uCCsJe{oVUCtm7O(95M`$^$^#~IAVed95r#G)*w zc?^S!L7<u>abqXlW$dm~1sU|MiH{d?s?p2Ix#nAFWyRutaGG3D&o?$+hu&He&43Eu zZn;c)5BZ6Fa=u$Zz7jsVDEf8O<Pi_1Ik5Z`FYWXfVcl?P2Uu~fKxe?95a#Go@gTt+ z&m5Ur7R&PW3X4VXv#@)+IJVQ!6cs-IQ}3$tTqE4VT$H)|(#_glX04-T6V-v@c#OQa zMm$O`Re~oN{%I|1{3kSij3I>@Wz1mj<y#|jwyf}+z@ze(y}Q8ZOjIN8wG;(|!7$-? z<a<UTj@+8a=%J99>Pr_XbVl`7cp`)COJLp=AB(zKWceL*Y0H#Ty!O*)5%K39>=&0! zgH90NhKR;=@J%5r;v}J#G(A#fk`Gp6LPAwy`xvF2l`-b(nP&`0FKZq}5SAsIIgOu7 zbW|0jn}fB^H!kpW+pU3QE5$w1B}9A{9?Gh(#lZzCHLQvvV^L77%Z%!6<*Wt?@9wQ6 zQgLu`WSM0YvH-}&)j(t5S{=13md{~Y5XGV<I80tIf)ED@8D?!00ry1*chgJkdXMmZ zafgL^kFhCh6IFS_3~Y+~*5WD|@oE=nZU8{ZlLbuQEa$}Tzr)aSQ(9%060YM7=DT}* zh#z@P<yQ|E&LECCq=Z$OL9UzzNZ!_R7z!C?T9;AeZtnwZcgov!4bOW?Bnl>%t*`=K z>FS^>pdjN6A#z*l?3kW<m;uD<o&+-^Zvi))2At(_Iw3h_)GV@{!uUBD4$@=<EHM_8 zBwpx-v4?_`4FZhzt9ac6{-~x(^F<sGGK9%y2=wP{<7F(LNWb?N^!1doz}^nQ&UP8z z#0o6vbMW9<2ItWz>w%=wJt`hYpO8>I3BQ7QQfLz%fTJ|~P1eiUu_7`=7XC5k6sfR! z;0AUc?lmKX(?+S`n&r_>k#YL^Bk@Q%RYsG8ADRuRp}Pzpa2pt&k!w*KhWUcS=Ti&K z5+txW(MGp`G!T!hnNHXvfd$>Jia<uT%I~zrXx46A_!10fA^OfE^h2h-E%W1t97qoy z>awgM#b}=vX@#M}GB~1gZJuP0{ITcYntPy{{3xY1jRjIW5&hMDEK!AqJ^_{7{j$n~ z=pS$603nlf<awjWFGB&#vu0Wb!6L6l8uq`*Ylse<yOpX1PbxHlk^{r?UlOZ_#;>!B zaD*9`cdp@??~|Ex5EyH<eE=%2Yp9e0?n}NamI93Ok9tS#n&IFvn3ef#6uQ%ZgKQO} z<U#gtz`<3*<ZlR*AMDvINCK@|(7|ktU3%-3^zo1m?!Km;+eV!_E|H(XG(CMBZ43g6 zp!i@s;Knh$Y<4U87AoZy@(G*e^>0ryLwv?4=$@pdvhM;?7V;NWnx4-I{h(O4R4N1D zpL&y*(EFtpNiNBbB#Dz2t!Fk{>rr+kbA3$&e;V6@bqi=d#q;R}a1rCEy5DIQo{xVr za)W^9KjS+6)P3n}Eh3bIOX?NPk*v)F6los;YcM5S=eCIas<j|9p9pRL!b?N_sI;1j zvLi?c@Oba9X+OY8G0}S#&o!788{!^-8*^-pFwdZ+cTmb-e2XYkj7(iF5(~v<8j$p< zfIlAu&cHJ&Izrj)CwxGB5TvVZ17_o8l(o?46;!%b1}0Vj@YX{|&P;}^b3nYY&SVRW z&o9uuB$%)!Z8u?eN@(FDtGsH8SOeh;0gqXH4}?|-usxe*pXbS>35KJi4~$3HVaF&I z$x#QGQNDVDK-10Izyv;}{j+<(jy137d}xs!<X83q1rxvS)f~4EX2JUAs>9Fg`qkEM zY}F#Xtw)Az0OyN{`R#ApFm_ARm91;2bIWuy0*Gxm-Ia@q_r}#WP-x+Gppb46nvw(c zkzp9xL?rT#-4|wH)&4crJuqM`V2n7Ci?Z4gShlkEyLABG$i)my0;dZZm$INE#D2Ir z9LTtZemuIaE4NWJs891D^wN#<WXg(p%T}3vv}THD$2YzqFrQBWjSMk?BC(lHPIj-O z_d_Ug47SHE@XZV7?<<e3orV$DU67$X&cau>*J@O?bJ46#OOU-b3Kcphs*jI3)_wVl z?Zt&mo{Pklcc?YYht=$KVBfS4;I!>6_8w(VBqW!#jEY#?SwUT%ogocYFyPsoIQxP| zMYqilOWM}M4isqFc`V*q4SttUvPR0nvoVMYHvp0hJ`)6;f#5#X7qp?Pp>L%NLD&iT z@PeGep);G$3&$Wh&$jKm29jm+>2AhW(`4Cxz)Lzj{)rQIbaF7(xBh3$O#fHR%t+7p zU*R+Tzk+lBFC`qqf8UtLK>rWT^dG<-82&uV|I6_CZxQp~BpegLpVZ|q4f<ashyRv6 zGyD;_|8=#0$vK8U^zYZV{zp0YHD$2X@`s$;r5u)~j@ZraIqZ+9mYBFHZjz}eaBdfA zY}Da!ywf6Kr}0+lA@5CC1i%tB=)+Cuq)9%!x!kRrH*?DEnff5=LGBI1@P$ebYmgbT zA^f0R*}t=t7j=$DIS)NHPEnD^l$1A7PFgN+(+NLW?Uf)Le$f7<5k%`ceU<E%l9UYb z*^rK}8u_5_V#adu?Iy78BlCG*V@O^@bsrax3YDSn*A_Lf4dwAS4_dYGKhO)5I{aL< zBu!!?@>wY&VM>sM1Vej1{0w7SL5;t?demrK8V60D4jdZSbwHoL@OjIZsvlF=5NG$D zB&F(Ow%u=!o<6nh2du4~!+4yL5U`n$z4Z_m(=V8K6}jzl9dZtr$4}Gx^t?Frx5NSB z35j`eqc!G};jmqnDUv?tJ?hq}2${_5rKtrq1#J>)w1Bv9XTw%Zu{Ukp{uQc^!5hHc z57RF7AFr)%AA3HnY1>ps`n~A6+Qacv7CtT9iM@D!sUztS*d9zepnERtnzhL>3mBT* zR?oz*2QHoK>!_6nw{uphPGwMgHjSE?2#7Y<b`TccuT{SbVXY--ITEhLAIgWqnJ<3d zx@}hZ?&oGoBclaP6{h+6fro+YzuPi)Hcy&ba%eove-HmI3=w|1a}0}%D2@_T>*Ia1 z>~}y_A-1xfKJjDfal`MHEzeqmvH&p69fuSYPG@^o2w(7d_!X1d%pO(iQ;K!ol9!>T zkf>UCo}VuD+uO6mY4fb^WpUb1ea(AQUUXsHVTFh)nF4BAVU-ejSXzQ{c_T{pWKMr! zO0+!#L%<xJ{$7;yTcBf78|Qd|`zDyC({{x7%JsduDRf<yNK&SqctAW-nP2cV&M>7* zk>43Hm(9bg-E-?K`*q?JOpKExE@xt$kUv0J-5ehvi63p0R}kAL9#sN=iMLkd?o!-w zL%aOulhJ%2g59yu;y1oTzyCtjt2`iQRoj$WEk`oBRl37oQ22}rF2mcgbZA7cTf{)f zr=<T9$i7<Aa_J~*n{rxS_@Nnsouw+AG`s+zmWIv6nGo2@`ZRzl2vK>y;csAC_j+Ru z9sY&@Y4Z8Vj-I}&*f3DT3B+)2`h5y|VO&YX=@S-Vh64cBAW!=~*5u}z%*x%b9*f$M zg2NpN)+JAE=qYwN58XKej5)5b|0#A=FJ4$ZnI{~KQ?9E2%8>a~IJ@9$ot>H8s~haG zc@p`0qFEjAFjmlLMwwubx6T?p)lyU*N;^6vP>y9iZOVx{(vk3t%}#<%>e>#1yqC(* z?)nhfGW0X!>q{$KJEy6bGE#EzSbhMJi#U><&QD*n_WJr7M7ULGE|Jm~J0EbwNe+Rw z1a2Z8OY7H!r<(Y{V==n-%<Sekuy1tXh=_fBj&_oVMaL_3<)8JS?)+^+6J=D6DbwvM z=QF!e_$4VLA_&(JQ^u*k-eOx2VJ|Q36QbSX@dyi=5ov$rhTsP_vFr<H7!fzj6e3O0 zfhWgIZp`msVCx$$Hn6h*)p9HeP^?yJDb%dCDltDJVFZBE7Wil5$}}L*E)WY*D;$>o zdft6bfLsZ(q>67+E#pql<`Dg5lJJ;xgNC*^|HJJ7bpF9YHZ(j`8^X*B3{J-(JXZPM zU#z+upF-hvKY3!u@a9~&vPg7;pFO_vi#kVM);0<XsJ-zDYM)>c`|DgCYrF!h4g`Ni z@>d*Cx|lx&-m0)3enK(@*+8(v`!6MPrf*+7idm^<L<9D3K$5Ha#7fFy5h$M}6lJGd zKs+5YjwrZdPInnhU{qx7c;e28D8lj*RE1GjfS3NfJ38MEU=H(t0fzk={^)7vY)bkO zPH@7kfkZN>D2vLM?Vk_y48<%K2Z!<zAeVm*ltu+&)1pKsT%PhAP6K;A6)N1eP3T%E zaJ+e(5~)DMb#2aVk*CDWq^#;;PQpUxI{J`hC(pTmPI&QD<q@QrUdS-9Tv#N8mIhwo z2I37X&eI5@ISRVUj4Hze4~^1everev&3{%Pg!(n1NJFF>B>Q~{pgqdaDNn8Kw?oU0 zW*bEF9EL_8F9BzFyH$>mu(k(c;IFIW=~*j>z?KN?#6E}F7OXr6oR9Q;J1g8xPSa8& zPo!3O;TsouNjv#S*Ksg9F*XMS_JO3pDoU5<GrO}Qj(Mop%L%i%4GnBkJq;muo&4u= zecVxQ0q>TLW6_1Db26(dwb5fjGBsn0v4LSHqb9O2%UF-sxpNpRP&g*nzGsUxAQNaC zx<EhG7z^>#8e%fyShkBIzfF<$R&eukb#l~G_6c6@y2q++bAy1k#8k)tt<?{yqGv%# zL-cF)04&37VwT_WE>BBF4W;Hwxr!8meV4Sog#y^d*_=|&-&qn&xCew5sAJAI&c_MG zT46Z?Jwbxa4OZ;v$wT$7H%aIj)rHKhWQW>2s6>8f)l+c_3EW^K*HS-(1;eQuV9K4` zHuWuJexO#Yx@+`nfzA}i6|TYJ#S?{T<ZHPAZ{9bAps>e&pU>SWUwx`DE)D2X2qT3+ zxs|^6b?udh<#?b}G7bNhx82UKD!drTTCr2X5py~|AA5z}I{iVXIr}~3_zNI*zS|CB z*aAwpevK7zdhEDTAmvJZYH8QswV3dXA6m93)4LRanJ){w!38N`<ONv<+amJ0&eSSl z;K9Zty3PK6?&9br7%lnE;*7ssxoHA!-S;E8r_Mm8`dA8>O1m2M;RPbLKLRDnve6bG zD8-<V)3-|L!*2zt9N+F+iK)olOV|b{0^u`!^PCz<-vKjY!)$G!W9Ar=goA~sF2kZ1 zLz6WLhLZnog2E)8Bgl(!O2A?XC0gJZidMLAxKMN9;OA}efPvdNl+KOEb-iR#fBaX+ z#vz%JFBz(ol;tZNmmmp6;(|Y0HV5^TSv)26JsA8%L_y22xHKN3;}jtZ<dsr-@(m&I za#A8N&;x@7L(_N}B}NsQk_B-QJ~i;5-R>K;%G3ov_x!M5g#wczv+G4!*c8(7M#pWM z5uf1?tyY~YWU83C81txe;YkokjLTYBy&@>v0oKTeHy`HBchAo^N1xxsU}TS(&P=&G zTHcop(yZHM4opFa1yZ#f{F<yHBNrFS;5s$Xtk}ir-(Rp6MIcvUyD<m=N=ti%=S0t( zmusNaEyRj!W^)Jel$)O~)bCPk=s(Ewej`|=`U(LPLC;Q28}HlT#T+tKLy<#WquJSB z$AC3$#x0SNf?+Gro~K#FjSs=Z4U&E|uUV6^Zcg<Wk`O_vTCWT1&lENPgpQhGqF#hU z(&dFW4kgtxFjPYI0}@70EXE!lAu%2no9K|E{_^`aPV{PMls6OyoZX+SW>p<!jNjX= zyjN^X7;o{Iq7KxcyX=t2!UMM5k6w)SZIt)w>|_I#*@zhDiv^c{zukI!b1Dlc>&(28 zG(8CVS`<i3><WocAOqb$2F%g)T#6@PY=2^{T#0tHH(bwu6N|1P7NZz3AX1{`G$43! z3c)d=Yr3hu`=|m}R;Ridp3FS(JVgKbx>|VvZ!?Ffv`@F3RRrR`FHU!=ldLPk1Xh%D z@#~tA&iHH5i0fUfim2?daQj}dXGYQ2*0y;S1}Zy);A6u;r}Q~$^K?{hvFfvMaM)rB zc`y6R!`4Q@z7ZPfnz~MDha|_i_6(>3{fvVVRX4t($l)qfKxc!KO($BIszT2Td+@b- zs^@<2-|+;y?kCt~gO$=`PNWn<=#+`RaN5Yu>-`3!(5b(~v_166&lVy<0CUAs#mXWU zaK|u&724pkzwVI9$m$XKXxdUd=l0aXv&s=PD3PIDYYKjQB*N}0Bym1co>Rc$5x2EY z)?H9If(lI8W1ew5=`HHsm5F$B^f<y#M3@jgb&R_rAKx}uKh-IIaEq%bo?UTbNV0YH z?4Y<Ja@?~!ZGEz}DD?3smve)IpCM_^<ATm5lL)ttzzR;x_S*Om3^KnN1npHIN~dK& zSBv!zzQl9L@bO+7yioCtsmOZINE&IlhUh8ikQ{lNhhC-p;!_E!<s)G6bKsaG+l|3E zozDWH?u)&5XwjIq8Az=pzG+lsUj=IdY%DB8Jn$2Tr4To=3E>JTD5iKHeNLGcUs{<i zpskhT3KW^oSv}*?X{wMtF@})Tl%uAU*ExOM=BZn}|3OT)N$^RU5q`a}_yjV8Z<!*& z76HX>;Y!KUKg%ou2R5&FeCD%6ZxT6<5%p5Ac9?AI@_Hv-w#HGVA8;nINIMKe7vWku z9sAY)F#S>_6zan|x_6QGG<PE}02%4=3qqt+LPyRSNRW|~2Qb%ZYdk2wOvz{hGtHbz zRn8e~#T_Q{!ovf;rVQGIYd$S2Byoo(Zu}6ego#1aan{E@^bEdqx^yoIksWqf$Ut+= z*rCMjS^ObYz#p^o%Anqjqm<nr7T$@h)LyT>Zx&t8tPF_#j+k3rM$oyI&C&h2DXS!M zxI>9PuleSt<1%cl?vgijiJgu267`q416D=pkg`nx+U8MFRtfa5r;nD*;;_*T*%6aA z1)43Ab4u3YGyN4k{GE4)iO_0^BO3KyQr1Z9{aDrrJO^PZJzrSr_hVG5&j%&;Xx)`i z_ReW@ncI2S$_I9-N%J=`K}Rge5{ZLYZJiqu_G!%fw5(&Ro5QaY48eFo(KoS216voQ zH~US_PaQM)6qQbylQ4vGom`{qzCp78`R92^$q*aDfTGtJ;j=OE2t}9=p*wPGAH4UO zZeTX%i{mXqwJ<Vb9MW&=F_uM%RAsC+2Tphlo1eG&2B#m2%d^Y=I&yf0aiu9GMv-PY zKs4Uq9TU{e2U9HC8RaWTPgN%*mcoX7A@Ui}WngQ*wKcmxoU4k3<{T+}8LjB-712#E zk^galh<5+l+L()$M6U>C5#F2e`+CZ=a58qh8$|xFsS~ibAJE$yBRBkGDSNfI3}ACa zt-mqAM-(WQ`d~9<&w(-b`wj5RjFX!ylWuJ3r#UR`BJ1<Fd-8!I4&#}fcl^X6qJw`E z2LCC0|IosWtgKA`^cNWZ^TkCB4F7;G{#O|Mr_TNl!{9&9@|QOM-wK2OW*Gbp@%YaG z_zx=N-x-h0U&O~hNd$lFF8W$r^e>FZzqt|r4440&Av64I%KcB%;r|x+sI3`$$b{xI zTRVnakzyi%b233hVy5y<r7xhQ8T#^4&{^7}B(I~Qw4%d5^u6*`^YQM8t$y>2O&k)o z$z((~=F0IkCU$!CdGy&ZYd<%^*nZjQrat-;_2)sY&6fu|QI+Id<~!w{twb4F*(Gxu zGRpdNQ_AYwkT%TfI^TP+yc4tOs(N!#RdKMq8ajR8-N{=!`-Oa@`jGXydwQ;Awa@rf z65~56nl1!}(UW=7SG_;o{oH-ZGc$?Bd$rGs0H2^FPR#RdXo>f@%OAvkc%jJXxeFyo zlHUO0)}@*Ka(46G@b#L?BOBGctAi8oI@oIeB-GFo)l|64V*?LQ_Skjx=Vq&Fc3G}Y zu38s6pwgS;#*lGh(X*O5v-o_Hy^j5u@~C?9k~E_J-nBv0o`DqA2M<YYeTXp)=_>Dv zx<axaRbocsZZMM9!%pS%v1*mVA#Qju)df-cjV`mUYuYnMWbMw_Z1GLz#j~MMrPhjg zG%nftvc#xCck;7GeWx|0Lrz`Cb#&O!OD`QzE(K<6eZ2VkY4_01>^CbLWxcA6*=uAE zzqoK#V<RI6(K(SKNaU9^BT{q^r3vYNg?g?%_w;e|#aV;;TZZunqH|00t^uHPWwcj8 zUu~&qADfJz`Ydcend!nz&V22i%h@YLC=h9n_ny+*lLHUmj?M_o556~0V~EhuM~Y~G z2oV*6C6e><aYseuTT>iGQrxqo*JG;VIql!|k8WGnJBcwnEhjshXe%X4Z`)`C5X`-n zv2{W(SJR;`ci7_Yo@)3zn8<8fh)chLrG)ykR-TSeo;nAwJVbpPG;?P+rC-yT>t{>~ zW=tZ0Hj<7;OeRn`2(zcN*IGzIqe<j)2rCgM;|<u|%3B+k$Q*5c;uC;@B&87NP1O?8 zjYN<fMF)zl+_!)AenuKU69gu^1#e!ikXL;dPZ@wn0P@ICD(aYh2mZ#8Cd=&i?9}$& z3=ES-6-8@EgC+eC30Hv-5)6ysqO}%KFrH9V(Zru*M?)93yvXn7%{*@Zx!Q-w91W~& zRu+17+-jMhb0tr8yr~SYMumoEr=+mb$GKZ;ZjT$i4kZbZ%wh-w*J_t%_Ri8;+oMc2 zC%9w~^HW*{O<euP3t;PX?>WN+RdN9cZ!(8Id|fqTCKU`K?23k}O^df`9?!OEzYb!C zQ6{iD4Yiu*ta?ps=#a7jE~Or@2>xPwyAm%$;}c->2<IZ!&1N<R2O24C5kz>ecnk=K zdczM?BB4}R;V^IvIf+EHX35BznczT+ls_-%sb<@_sJyp|ggqgOS{SbQNv|h?FQ}+2 zUj29cHJ)o{tEz8$A=$uB<$-*rI2wT-NvsBvdo!V{472(vn~)$OC~g)M$X1g3XT(=1 zBot%k)B1U{3K3cXoQ|~sAjnT)AX#{bfv_YSXNy^vC3U?z^^oUj=x6Sm{CrrrUuWvD zE&f*iCNxU1Dz&}niCi9V=L3#IjInkml3gEMf)gps?&Ps~J|wf`s`7S1$@1|0vp>Mo z#UZ|VF9$-o9HmDoc}$B--XF_2AFAgpbsMiPCV4dQeRm62n@|vfn4Q4x#8-tzHSJ3_ zf2YYu>k6r0ptH)gqpcLqA+o0CKNHVMx5(eeQEP@54RYJ-Zcb2#QK-`}Ko8+B$jXNZ zkz@~FK1ako!NK7tLA|82j^Nk8Ru_Q8(;|qs?dyq>qp1!~cX!8!VJgR}(H4z;@-~wV z39A-{6ufC1MXtbkIl1%q36L$s$Hxy_?q2tDOBzyA;*YuNTy5=a-VMm0ZOI&bAIt}L z<+#l0L*XafJb)BWdG?$?zt*E82zp-RUD4CGUbyM&Z>L}4maxv(GX84W*x|mO(HtyR z4nO{7*(u*lOqwldvr6*t`RGAfu<T)rW&o_SS)HPHCP>|^x;_q_L~}k>r{p%7VXwRn z2{|67*iL<kk`12_GhGCw5g52%YEsTZ<i0gKjv;VZpwu7Ipa@F5PLgsi7}W>CnZj|s zkS2p-Ma?{K#{8|>RH>lRRj_f2y&Vye|9jCOWo@lKTjyh}jt{$lMFWv6R!Bft!rVA= zjx2}Y<2l3?-vW(nVK=iGtyh|E$NSYw6SH$P<!|s*oh7w!MoVOKAjbhM6~XaLYAVq+ zIi;AkZCsY#ce|vV(<=-9``HDgqw|882iL4{*|1LyczJJ?MAx{M&!9z=>-8eTy(5ST z*!M9-Bc%@5qsIxcky{IYyrbO)*mIi?S`6V^lZGxmab*Ilv;Ga$#6e6<>N~bWoSy!A z?Uhqiw_NlWC9+X4Kc%-94{sewU4*^UAq)*Z>%edi-ddxZZvJ=K<-TOvu%W%Nq>T_N zrSSdgU+N$s4;oo0m00qEV=)Y}a_ri~49G5VS$-&1$XS8+UzdHqgu8~QEbnow&T6k^ zQaLecDuUw<c`$(A$w?4@-SWC(7QtqeSxiYP(-@&K8&@!*BrT3PBeR=DO*f$~bK;ZD z=7ucSi*~|Rr%Fq8%W-tsHBxiN#=;Nl615G3C#%Rnbof;){*FmaYxea)uHwU{T3YT@ zfCfsdI2Hix?n4$@3W1uv64o#)vzydeLlk757lL(hGBT<QC(G$y1PW+y=&cy3tQA^V zUZ%4ipj4x)M;ByMJhWwq#y05p5XuFGkD8-yi!<%~O;Fk#j6|*GMN}IPGg;_8YyS&D z5rzCMY-t1EnR-nU^(VY{$y)p_k2n_+BpgX#mx!?-9f$W+t*Qv#T`3~Q*N)<Tm{1wa zVYv|Y-M1oq?}KE*zzJJO3dH%NuNyK)Llfe7eZqS<@{4>s4CXzQX<Pgln?e@Vo^HNq zN{bzeK#@3jDmW}=Q&C~~%w%vFj7A(8dWOA-<yc;H*%|3kD@rPm0CYTm;A-vpcj7>( z1;~rLu{=;=%?5&Sa)#4kW*ycfnEfOKO6K*9ACc;&i|wFC3H;9)D~>d{2JT5o-@Hs^ zk4rSrFs>#7<Z{EwYN4i;JusWnf~0!L@|GzDHPOF0kR0h?rrIeh=kgVtecdb!qnA4R z3A^yat=u71h2O@gls?{d={&Bbb={*I7-gr0N{h*&DM-Fn&|4%|^(QJIs~@3%ck>~E zSAUoZQKPhtLn&$|jukZ}4TOV;?dVw;@-Zrh&k%@RK2jPKW%fG+X|#hIoQIu9nF?OE zIX5PqYaZCbt<uaZOD2MXV2ZH#1z7a8zvD(j+CISbfZ0OZ4xi&JKjUI3A&@MA1*3hx zSV*CeBWK!<zxEo7=q!wcl~5zSA25?oXd%`KkmxLT2c@<Q&OE=yf-yV!R;_|s4@8X$ zQJnV|2qJ9!J@I$41fR83*kt7RrNHmt=+z=%$np2EIfvEG?GPUW48jq+8Di)6OC?qT zqDVLNN!CfC=ZJe;45Hf6%Y&wcEph>(4kS0y%z>f>(ro2-CtSDF2ZMuqW;0@*rWvK9 zn(EJ`z$3QuI+~ftvk%wao}OC1E~pO~?!}2lcA)P(_8~%4g86|PL-E%POfFPF=Il%T zKDIfLj%kQMlzFGAX6+9hv_x?%MCC#Cop}B@Aq(8KEGYBbf&|K~y<~9&?SS01K0>2O z)gxyo#?D^{qDe0uDd<P3Ba30*t_P9RnGnVK2uW~EBigq11}=kf$TlIGZ|FM79b7e1 zXoYpSYGW`IBf}CY>Zd2!+&AZo5t!@sdi`F6=XE`?u$e)mvs^(DC#3;m)14O&mA~-I zlY$Iob`1MA%Z7srBl}v0DK&XAG@lAAP~srsYU9CJ(xgQxq6$wCUiC9o-CDJZFBa>i zpB<yi>|7`5R(`bwR)lgVAW0DQIzHaQ$P~GRB76%GL4!A;ev`el7PY|y4(~h^ZyrV1 zKp$*Hgu*b>X+bF3i6;-^B&4AHc+$87h-M>AX2zY{Lqz#S4Sg8}(hE|U5b_nYd;Nz{ zGGzTU&y9$ZkT#3o*A<wsoS$`q72}INC*-1?iH2<O1-jfuJ|kgYCSVoB)8-NR6b*Ci zhr%i$nQj}6r5}qfMJF*l5<@coR^MuN+EA*hTFV_H;vC=ornm*zrN!AJg0nHLqr#Qu zH(wtC`#>$;)L<aRO7QKGm;;JIVWpsIZKS|zYC0mY=1@L18@AnOAPMyqar<<`kUKC_ zK)pU%Y>{OI!EJ~jW>ww7U^Rgl3_AiRaVnyB{pTie#>@|Hn;C6~OAQG=2dUqMA1iSV z;D;kgyf5)v(SUkx0aC$UyiMzbw57}AP&fh2^o<L6AlX1q_3vV<3_s(s8E2JHr2DnC zk^me!;;p2N4a}92;c@8}&Pd%GBoH$I4=LRnZa<a*5)L{Bl7+W#`)JfJ#1wti)$hH9 zv{qFEvo~;1=YeCIosJOerz49L30C!h1u{(<?a69?bh2ib=D;UpsOdGSpvxoE-cmE) z5PergrnM@|DC<v^L#ACAK%p>n!{*D%gEZjJ89JlRFUWiQsise-7$zuhpa5mWA8+?D zpr;3QDC~qn!UWn-H%4&$+gTf<Y1L*tNE*rrQasG2C_4#AJ&eiP5GJ1zfd<hr%XsrL z3M#PU$;pV=NuRDUuL|Y#b@0ZgdoyRww0=lley8kXA<hDvgimSYj<|i3ff6u*pEdS< z%*6BUcWebF;napCdtgmE(^O=Wx7zKDe}`!-wY2{14wz%tv8clqznw;YjmiG*B>d|w zp55}cf73vxgW^I2)X$T=eg^>Y64XXYMx9&!Pk%SIQ-vD=Vz{g0BsYD*_(n}6+P5%o zP)Pf1p$zM$_L@XxGmpJ{vz3BP36`WIjvcNav<?|Cu%gnz<dyfVA&;(G=I2D;Ib4^A zaEm@nq#V*J_`x;ygT2=^rjNW#K~3GHEcSm9v*XGu(i-ok!_6ljPtB{1Fbz;x{AQmp z`2f{K&{NI@YyjlF1tH6`9aRMub95GVo!wLO38}H0=T<#1>GTaHcXRO;{ZUpmNEu{` zzUq3+`2=J;MAuqX7X(8G9nY1sJ|{l29l$`vRd|u1Gtv-DEF>x_;vjdh@%_xKAacx4 zw~Ku~YRnIRewss&D5Fd*OmmclNP6>mhL53dZbvgOcNfU0FQkEJgE!^`KJ@7e$^-$p zO#iT&6=t;L$<>lRW5S!z$(*>4%}nx|KaKYr)1QqC*;&ezyL6OQn<mh<l|{W=q6vsN zZ9Pcbs*jg@QRFN;hY-Lge`@yzcE~dxvUX1WNHk+7aGWyoY1TF%=4>|@Rh4ts>>{8o zyAVZgg)QibST>G+^dR3HQfE)p!OMY;STWXpFB^gPa*`$Pz#o^UChM$UrU@4_DzyiL zW6|4?{6X(n+27OFZG9@1X(h1S6WsHATDlEBPRC@a$h{_AsK{xjmlhXOIQZfeYu1x_ z&XZn*te$lUSlFG$&p;L!NVCMn<^5}_on|)-woyr@ZEVKdYQ3MHwAY3uFX}t2J6=!B z%of)xzIb@E<Zb@-D^gqon)}w8iy7;l{j4Od{0z~e2H0FSWrDf)wcVNC$MmR+Y3y!7 z;?e171j2{vwOwjrNqFUNgJaBRI=Sd_8!oF2=xMg<a!iuxHfJRQN5lefl1^940!od; zz~GDvk`pafd>n^Yoeg<Xl1ErAIdVFNUUGT`!TZvR8@5p4qAn&}ofd@hbfe$MFL0x7 z%DMN4++`HOv@hj<aVTIeXNssYZik!e$5&8?c^vCxqPq#qF^=b|Pen4hmJ85DcJE_G z-y91UbORO_Ys5Aa<%DVUn`Fr2u1QtzkhW}RGT4P;;4D$zIOZD%UIiVoiswO-><qY_ zW1lB=W#$;tm%U4SyZ@G+F&%kSwH~<5-1N~Q7-C6+xS#M98sje;=p8OrK&f>WW9)ao z*LP*^lfW>-!0{-_&hOLiwoCkU@A0DzOSOy^lL^R=**J21*e~~BBotDeTdJz!SkL;z z_hX$LW8~r%kUa<l3=duX)r9lIQ`YXTcdCIDzO{EI18X+5wxOi+>E_@Qu&-_t;oo^| zfA!@sF*E$vzy-r!V;KJd`;+1S!~Xo!R{BppwtpRG_<!QD{mmfzr^ohpgN*TCJ;%Qc zGRA+fG5)8noN9GV+f5dfueIO1I$)TZYByvGg7u-r!mIk<=7#(5R=C+>vdVsuHfDFc zKfT6?O{5T!H!y;HH(KZ7dir^=*Lz9)nri$!8En)NXQkcHyO!|IU;;|(kiM!LB3g~p zny#n9Spz*h0{v+5CQa8r(s*c_(xa)$O6)>dZFrWse6KH0)0$}AtP!)uYxRch$!UVA z8f?gEMP)IL5aQh&qBU6p?U72IHiMgUd<k{_U?C7GG{R^ch*a@4Js)1_&itLVC}ce0 zzGLfp0tsqPH01{v?3p`rq^&KLSx5Q9J?|?*Z#uPQpx6Sk<oHk>u_W&sfzQU~>$wB* zniKHVsV$?idp{%LuFWKLX<ADJ`mfYPE?OiuUlE#JjRW3%Gr&vNFrgUMXXw~EURpig zfaAb8&(}rk_O@KvmLm#G-^UL!@sAyPeE|%&hEp*n{{0J)C4larA(H`TA>pHvyK$4n zu!D4eA>y0dYVAeKa=Jsz&2RMLy;U{5v1^be8q>w65`pF8ZX*l1PX#bYxj=E;m_U(w zSlf{Osi+kMZTNetC?5v~P@~#_JFP;(JH5I#6Avvjr?O}MQyj%1hv8$Wt6+~ULnn~R z)RLuQo7PRg-Iay)+mzi!(;x?Ya;;j+x`6F<CgZ%4f<}?#sDfMauxUwnN(<3-@W;GH zG7~tGmZSS|#Y4}KzA>tdCPE!!rUr|wd+@ppvA8d0_Afvgg$u|x1?*N!F8q|On>p61 z97Qbq#w>nPxt(coa{vvLAGNyzfvbGA<}N^xR8N85RO%C(jSXRpJ1f0%s3+QA3Y@Ry zf1Je35!}H|jKCr2I2M~^o$y1s)%7UDz#d^!4tACm%^CpGoi*4HzB+|msj4N!(sfD` zTVSG5iAlgdUz)s)o2sCUso(o+JV8}V38=nu68Mc3>9W8^K4p^Y2FDK(nU5yG$PKOT z1X`s3DMn&`fCX0!7>&XvYq+5fz&iX9X)3Q1MUSNk!0UJn>|?b@(45b4`;`$54-h+; z&vycM77Domj_&&P)Rlg!>@%ks3}UAWRvi3>*-|32dG<-y4kS{|16N}lnxXN>k7^s< z=ovsrB4mz}0TG5H;PlJ!Axa4BaUn?qknoZ~7@%4)GK~VPUlrp*h3n=<1ClWIQSP2= zNg;UwVMYa{-wRY0Bm3;Z;vtRsq_s$p0hKKo5Tm?!1c}nk!XtWoU!6O1q=+-!042s? z=#(%3GImNAgEBFAYim&gIH8~JB{^Wkw)w-vVULl`WV>IO{m(=#vxI{P!RB2IC{`8c zG|;yw&J(&5fHX!|=GR2gcm_X;f!hk0TdpF_y@)Cmfy%_b=dG&KiXqDaGC1|<jFT6I zPmr<#fe-nJpOl0<V4dJ)nZ6G5WRve!ZgFH85ALh*@`Nv+P1zMf$JPAca;HqEXxlJ# zEIr5;A=4Vs_Nh|YG};HK;YY>EhVUQI$k2I~HiMY&Yvxm8Ix5^}p8<i@asBnK=YKRK z$GpUeV~9}S7XsKl#6FL50@$tViE<jc%kTG+Qlz$n($qgH8Q+g!fd5?{v9P0xP{uTj z&Djh~EtHr8{}@eLi7p`z*uzNswaiRTD75VrG*tdf*ALdAjtJV~YAz-NmPu|!BMNFC z4YnTon;Ced{KP!!>0PX;`q}^rC>%1|%c^13JIL9#2eX0LvXkm}+e~peue4Fe8hli3 z-$-34k#OH;w=~<zSN52J55{9DpgS_XpBCH(Sg{1#AC=n8-I23TM;#j5N{XNC;;Tp8 z5FXsT*mQCXr|}gC(|qCu6i|W(UFr-$R`I-cJjLNV@!?2nVhXaqLS&ORkPKIrvwP8) z0WS81%D+>s3`mkmcq7f`QZyI$FEx@$gg9Ug&bmO%WKc8<3-5elCRDs7iJ0v?CG-Ph z`>z)=v>Rkg1DSIJZVXC6mNumv#W&_aU!~pT`(h&xyHK1j&e4#{xTkTB44(*g;#)Cm zG=a=NTQ~ejn(jJ|Vk?6=pdr*&U0giKgK&mM`TWEN2bB<Hh2}pO*Oy~cq5Otd?!<u% zKUgMuhD9=6VH}?5$Yy+%4#AK>K<lysrpthqaZxxg)YnzFk8l@L1%Y59C(Bf7-g4@v zv!D>6m(YTMVS%G*f4?!b>y>yEOK0oF+OKsc0M&;_s0CRxP?}Y&?A^%!6QAYiQXg-} zR+TwIJiUl<fcewX5AXf{0%R3|NwXtPq2b;~TgQqXNmJ^0xnxB6WuAm)WA`106NvZh z9X*eBdv?m+AH^4m#^BTvqT;8cSo<be#RdM0?K`oCdu-MO?J&IW`$2-}*-z|ESHQ~d z`8EC4n%(Flif~pn(e)q!A-dqxhVX6e=29TmqX!DY13VjB__5*6$XCcm^Rl6bbA5M4 zb=F+_qR#SjO|mi^)cgbIfOL(}GQ_yRJAJ>l*=WF3bF2+$%v*t_Wd2t35YAf#8F{bX zs>Oh%-mq4Er{&Q40S9EUm|1L(PN3ZKacCPa8l$};lk$njbKf_3l5kUve^ccA`K10M zxT9yM`<Ei;uSVtnpCX5W@gM!i3dWAM&JKpgjsy&h|LDa1uORWy(%?TVa{fHa|8|i0 zx4Pzw2u9EHj{@y0A!H<=XZ`1fxW6Tie-%Lgtu2}HUkk#27C`?PvHc$lAdUB!FC^Ay zu57(8s*?Po77gzvliw9bOXNt*cTmbXN$8v*PWpVI^dc{o2p*Z8QrWp4iLY^U66d7i zL|EeX<pf5~9Y)T<r_1l2vtDnJ?tKZ=deZ$q2Aap7>S&FeT!**xrjR*~JCQ<DDcJ3; z`ubT0%JuA?7hKwPQ<M7yDYN$sQ*EgtS;OkP_Php>u%2DdL;g<ogIlJwxo>rDA2uUs zy<bl@Fc_|TAq?<yvF;eR%VP1deYOWTG!8?YJ5^uiU&I;*6gNnSG7BD;BzvNGv5aE^ zZr~?x(d|ETx;_>&f0|y(#}!R^zaKowhC4l64BxHJ?r^8qPnljDj0n8B-`VgO@-`0V z_HQ)V5zJFWbft}I&<Ko!ns%E_pzg)^nXN7{ju0RFP^b!XIxg9?+C&U=HB_~Jc5kyj zx~ke-cKmQQ?zWz;WOX)7k?zr6&cw?HUB6~s4feijSwoZl@nARn*s_I|a}+b=uKQqq z9!z8$FV@Azys(7R@yWuG2{LU!dw1Z6y&La_)B3#=R>&aw_<5b{4oNMyAuYTy`!(-< zJ+UuNz4;TP#1!}E;In%Ltjqp0WckDBw_U`mn9eh|l<>pG(!r_$B{y@6t!mcFVT}t6 z0qP0^@f7{4{3bFW%kzwm3Mk8f)!Zq~?Eb#3Pn6BK4k|q6%}rjP!m3M6L#fY6TiJE+ zcuY|pv~a{&m=)essFjc(i_C1E%3FQpyEwml&S9(tOWmJdg4aWmu^T+Jr0c6#GYvYa zR0!_l3AAddOFp`{^wdwTG^%l*IBY%FVPwu%euG39-<hv2w@UYDrcQQMR8emli1Sqb zCShnEJ*ZNgLG>U6K@TZE0q=gpiKxJV@g8Aq3Ko9yqa+c0?(8DHwo0p)-!pfAty;&X zq-*`QajnG>{PVZD!!Bfhdl1!Wj<npl--2-uGg9WK#`P2y;<XI6sG;yiZwBkC@#<l; z@?eEkr&niFM4Q7CnrbLp;lA~+CxVDtlVel!SRcouTOVEFAGx@gkEOuGm^`8!Q3Hsg zB(J$9)WL)aW1#Tqowngr{QbI)6s@+8nr9vE$}U48Ui=AE_&+Ofwob2zbQ88Gz#+1r z@Z}`INb*pky>!0zxf~*EG8q2;RZ~U<t#j!>0%ZPjtG~%C+;hWca0pGpgNaQ-Ci$I$ zbt3PX2;W7T-OyjkYy+2sML>r;gPp?^4?X8+A>7zCWGz(%QpzhQ4)L8y`}#60(<Y|o zwX;dLhu~DATQxKF1y8iig|hy<`lr*DreJS?u)>O{&m4BQy)qqv0jhwUQo5FmEr-o~ zRpb^{c@n@H-A_0H$@cG^NG~>y5$kqYiV8^PyP!Azll?<KF0Bnh{VIUy>k`?f^O=b% ziJYZk4kR3SJZolt6N#LKY+Mv{aYDij=qkuD1&t$+`m$dWeAhxM06P3l7n#PRI$GA3 zVo*ue`>nP0_%eeixa0t=;IIr-Ug!+>%6j3WsdfCpd>(QlxSrIInzIi6fH`~sXJzS? z$CT;8*>;DFz2$;2nl!3=?&RP(N)Y{m2U($wmHesmw~cDDHv<z1-?t=Oyv5Ha9^bCC z`XC_9&G*9XzO&RMv96<A7*04j(GhUrq^}2e{+5fdq1tsbBf)~k4D3XP$FW$fmG4gO z$Fut6dPDHSnQrG$@bDe`N?WTC#nYgW8|KF?!6T8Dy`5x7zGMV?2wXut0YXv(r4mNy z?PEbI#Nz-scz(Hqfw9_kgFp^*IMzBnPy+=(gHR4{c+_h?I>|a>l+wpkB$#|a)wDb* z00#0<ns1Uoj*(7K=Z~~0vBcl9MDy~gjjvmTBP8_D4MWh12iIT@LNm#p%&&p`#zC+l z_Ua^OxTP|=aa8q{eg#6OZTJ=)B$Paa{99P|p2XQkqv;Qfv@r~5Q3xV29kDZ!1`l6R zy;^sG@HSfX@~k-}7{w}kY;b>-_Bh1B1apwaakyOC#7PwSue}rq=?snNMGXWS=U-!v z4>(~<l6KK}#Ec*<*{H?EbHukxzN1r|VFI)V`*A{nsCXvd=3iHkhkDSB#*o1`;z~~1 z(4Jw=h}}lQA!77i{4(LRb*syYYO#Nu{*?dF+fYYQ8v?g@AKB#^O$w%sLxq>ev?dCB z7?Xk4ky{b4OJ6x|1zR@4KrAZ-?nef$a*S5UIQSR=$W7@)SnA<>XR7W;`&hCVEd`Uc zRX64IK_V1OqZ%1Im&EZ6np>k}|A^hZeeX#tWGgnZl|E9spCW2XN4N_VA4+}ch9LuI zgMDht;hp-a?}8#k2HCarQ;-A}5pU2!+jZeXX^!vLA_?wVE+~(+FI7O09q{lLAla7e z$b?tk4zvvd{`0$_mXJHQ!c5Z}_%%Hx^{su<VtsB*n&EXX7^TjQvxo?xmHux2D7#c~ zo5ZAr&?-y|7T^G5MZ9=W(z|aSG;R$e{5MgC9b^c2;;IqZ*$NSmP|=K%Tz{^-6A2f! z+=5s~*@zP(?i@FMkONVIqf?b{>KRn~ttX<f1b#FWo_Gt?*zB!wdwpCqVL5;(X&~Uv zgQ-HZhyui$wF)fK20ct(3hN-qSbJS;Ugr(wK~r@;=Fd&WizF)8faJsix-67>hCaG~ z9QlIv^{#JfvNW-hNM>q^G_jFL=1Gcl;(~{mOx@Q<CAD%adl`uiH>kK@KkL|qWs-Da z80~|nHcPY}mSlUA6&4|5mhSmei&e&u$G}hHxx5B(K;Q74#o@?_5^*WrLaU6!pM*39 zlV2|eHHVGjgz^U)Q5sJMTY|o4%9$8Zl5H_m^JGqzM9&`2f)U%oWGO}sOCyiPbbBv? zI~<;R0w4wt98kUpb=W%L3rSCc6R?5YnTVisY$4!6Ba#s&ux<#95}d;L+sx62o#i*$ z%sc484+w3}0h=a%=^k`osd+s)8^P~SrIke~6pv;wbXIVSOrp~8Kv9<4ulAtcx|fDH z1sv{bm1sO@7?XN=#GHvBFSp^Bi3aYAr#;jwC8AMB*ivlAcfA;X6<sZZ1RnCRUUdmK z1?#lS&j8p<CQ9$#O(8ICQOjndh7J1U1PLlqA{yLUpjGJ|juxsHBP97CoVnIziCqFr zi}MXw6Oksuc_^^@_e9#vp`;O~s*1aT{>wh<1}v73VG`SeC8|oPDJmK?{894^5F=~} z$AH{v=b02k7Ib`aoKi_<CJQN&<*@{W_In2)ln}gjTR)TXmRNy73P_*mOVb0h$|3Fp zE8np0iUExkDuZf1S^V?n163h@($&Z>&56AfoeKH2v@kO-9|fK+Q35(j7XeGW5sjl> z+2ILviU_I2Jd^Di)@}*3VzcwdTOr<fC^-Q{tFW6`hNus09rljRERmOsU*6;EAY5uw zn%dr`4aO&;0RO13x-@<juwHc1r9`wfYt|>VUIvhe<f7zTB2S}_#l;0;GKBLourp(A zD!$B78AK1gG??2<s1IWDWK;xeiUY?YwBs}@#Sc}S^<&xNmjkKRdsuzO<A=LFeQHS| z$}6Vt!7ZvbL2atk%2rujkXM*GK+r#Aig8I!os)n{M^pacOe&2b0~O=(kxtq70#64( z1)lp1d{<k^EqzzCT?9<7j>aj@8PFMyBYxc0*<ro6oFMoDBCbi+T|63Cr1cW+?;?tS z%7xSh)_(WyteMwZjFU+xouKc(`p}c0K%g|~T>s{*I?16)xVj>Z(x*MAmYnG5u^J)T zO%bXC93T@3yF(_l-_w4jdBuFso&<TMrXYj|<D+EL#}g2N+sU!9Bx6p^kJW--_5h_> zg@`D|5Fd+>zDZxtfLU-tz%sAGTds6R#teuzm5H;yksv%;w#(Nh8XsDJMNs~fyCy9= zL<}>=5Cq-i%*N{KonbKFN)V7~GPL;#{CFhU|L;W6Urm1u>`ebPr^)!Q?Aw1cr}=M1 z(LXu`|3wu2As7BrQS_G+{f8X;-*SHc*#P;~C;2yl^lwGbe?#ps{;Od8O9V0fLr48j zBB)t&(}tuK(feoZDY+p?RXN$ZDmes#ZB%f8p6eCrF8DXKCJ|z5>_SX;50fk5xY=>? zBj%k1MUu$HZ3D|1Oa1DVGIurQDykeRh2CM_k=|j*9*6S%0)-n3v`-w`{hzf55=!k8 z)3n=$6_WBK3^WT0MaeO>_y?00v<xXX58n5ZT<(n)*EIXK%JL(Lz28eSN7oIsYcr4b zi9Srd-k+XBFW3+}s1A1M%bCUi2D{9mwuo|g7EI?2vyMq~j&H!jTz_{Y-!u)s-Y?4> zbRmT&+2<4&NG-B`Zz`iR%n1Whi~GcDG(yl4!zE<`nn;o(lbuewjz5-Gkl&N86oq@f zcy+sV7uV4Gv>28~0Yuc$_^P9nDJLqhc8%d|m1(b2Vxu6OnU>u7>B@`a<jQDq3%4=8 z_13CBc=2R@-ak$Y8wne8^2;HuykRG*Qj$7kpY7qqH}|mu>+84J7a>)~!&;}a7cKAa zE%Tl~bfnL4q?vH5i@fke=%5Lxa2T@_cPdy#^19NEm(h-^Q^uoyf$#WUy1Kp^{90Q5 zG)jod5lC(7!RmbVr}DrpK;dVW8kOmMkfq_&K&z4Sn*`_{xHdMzi5z_#{ri%P6NVCf z_b5xjsbkt~kMA<a-%z=Ix2Q8-kIrw7_hs)J3S2+BVYMCV!lI`79|GDDwcLZ-gsNh} zRFlGN?4mjRp=)^XI{O5>%{NPQoC@?p$;Xr;B+!TX_`tYiz09MzQejKkh`J~ah_+)0 zvaxiO{ohLCC9gDKyPo`hEkL5IRABLQaCET=3;<n2&}Kt!EDyxoO@H7OE`sDx=Y9{E z{z=n8gsphcKb&JxI0>>#&?W^YXh%p_^aD{V7-<~3KA6C{9<;7$15;C*EhgyXH6{E* zp87}YEkYV>C48f@B7V2IIOPP;rn7GVaexGxfHCHPBZ0ZlChxG4=b=lULz`AS$1Xn` zZrrRgp>TqE5V9nOzgc@QJ5%<n)-^XRR*CZ?SKGAA>}0=8-C|qQbrtVSy2y6$V#nx3 z8QY%T-uq&G33`A*=F0cozGuNt$G2;Wg(PrOHY4|BVrl)NM6S9q;R<G#*2G|HL>mR- zEdj9~kkUk(*$-)saqt~V>{C9qyn60u7lRiv?s0({ib$?jJ1G$Ks8A`tL|Y`*R*=;z zf~n)HMn&2>3^;)l5A9wBOwy=g-%!l`8;Zv0mhIqJ14_mgr<T7XSkMtcsmPN?ki-6V zqG6D-1Qzc!*yFvt>CacDsUQ)(Z||QO-`1-Nm#(90nc({&hx_(KBvO#s^d{d8gxK1< z-NdL^J)e0&sxll^&R>crSGS(KVa{Ev`nLFKa@VP1g{dzmq9U6@^z9W$=5cu@>PquC z)$I0zC}nOqnTL9bUjnPSKwNj69i2kOw2-v$|L}H>QI@pXwoco&ZQHhO+qP|6m9|xB zRywoNw(YER-s<mk_xbwX?s2+*ock|!#29Zx>@i~Rc-EY2t|GVnkrCO>qhmK`?`54K zs%WzOY;OCxNAfQTiub4Bs_3yf_T*t3>tDlj!dufGkfjr#vp279e4XD{Ozn);B!SpX z*bH(5CB9G%rGNgE6h61ls}4E3t-G#!R&E~{?`_O#k)IG<-(b5p`v4VM4V3o#uE}D3 zu_w#z9U8BaG?F~xrO0pNZ)2SS*5TI*Eyl)(VImVG-7qn|_Bi)U6utPmTwd-<tn~o@ ze#`*n567B?8m%^ipH^k|af;abs`f3M@jDSXTc7+R)bQ~S4#r;uL3YsGW)7QOO1}A_ zxQwf8Rxy;a#WbTtM-{ec@Xoz=@b`zbQTOyuZHdfr?qF59xZ-P<4Skw{TVkIwwN=CO zX1*_WO+VPtOj|apvxjL=uH~sAWB^6*q@fK>>cv|2ZW;w~f1E$}>NZYacahBwRnJc^ z2(V43P^kXyz#HrO-aRChnf7C`bK*K%wENZRQO@VJc5p6OmmpT<+iuVXs1g-263%Q( zAWCB!!<Q2R;`m6vm_jKbDn!%NaOlKvy7_zjBSxx#VVkTcy8}|+FFN`LB(wP6vssei zcQIck{GXquTGhl+?vK^4`wYyLouHXl=!!gZW<D)S4?4h38~pa%>%pJfKIW}dlZmqv zC%pZf3|Aq`>orY@#+h8>-dLxg)(Cz=vpl#~XM$WyQ@b$jBL7Yyw49gm41Kb+A1y}m zR8u1k2!rix&7mKX7`PRJ5s^00<OFDr;TLMHzLsfzQQNL1122zGdQlLto5&4@4SrKW zIv819pk+dj53e^w;8gAZH7K!?Rljaf)zASBEU_9zCzseid+d5Xv8JTmvChC7^w#+a z)-mN4w%@V{$;E{v+slAt-p~6;s~}O}X!nu%XuB+dvZefVWlA;QT!`9I4vgDtJb44P zB8J+s?@jbwuN1$lGeMaHCP6dG0G5!DwDml7Geky72sk-nB)Vd>Q(P|sL~w-}9kFEC z>xqHbY4>i!ms{IipiQ-qTTMWcA<gt%M>B4^zKIZ@)+TFMt14b%!p*b%*VLC7psqaO zB@iC6S!-DUigAEJh+Tg-0AP+^PHVRKwWqjcuF>1#U5?c+1o=7chy}Uq&NVB&{L+rF zZL~?enZ`Zdp=1?!B3G5(lKViz@ss4TS><LG%L~@EniuuW!(R~SYlkI5{`7|o=4{6( zID~<rKSfoE{JS|a<cL3(u*C+z2xs5$5rJ2a%onKg$~no<6>H>Vrb!CuM2q&$2%?TU zsC76o``|vHlSHQT))U&S<Bu4bwlvI$l*y62*^w*jI-cGbd31L}FlDQn7`qq@(sx~T ziEv0{u3j~8e)cBw8@z-UUt~DzmMR5j#+@Xt>xvH>7I#Zz@|wgUsz=buE9r2+Nv<NU zaZ#nhnRDo9yT??CCp?juR?~L`Q_4l-OgOwEQF053n|i9Eo%2=B&)!!uP&TPRft|s- z5#8=sw1HPOBV~e7LKE3aW=ivUCo-|At3{*$FAa-?Cvy%Reo*Gblu0R?keVBkr^yU= zgpVTnO57U1K33uga;2s<>~TPWmhk;f<Q?{oAg9C=Ide!Rw#2N8BR;4$adSeVHD3I^ zzvB1)@~v;C-TP2nh`ZD2{IwEB%kNmcNSOFcZUchhlpJ52J2D|h37>S!J?2CONKszQ z9JURX$uq)Wx&3V_r7J%OPOL(pO=UDsoHTxUWzZ=4lTtKoJ~BjRGjhgFzWo|(Plyis zpbpKQNXB-{)fLYLq;OwY4L0uG-TVU1QZNY33r3#sIZeV~@~W8KfeeDH80u5gahm(Z z=yc5FhnBTd&?#xKw?H8!EI@H&>wb)518^C{6MXzg>jZ*H;aDb3KEuks49tVh(KwN` zcSQn+<wRa8J$3=xMH=pDlC>+P8b?%8SGYah_CByoVJuK!Vp$O^(7(B{f$O11c5xN1 zU}A0odAfVmL>&i|>lIgohnIbIgI8hs9S!0efgVBFi5SIkcDGRkeO8D$M+n)<CJZ!M ziYq>s9lb<KgVZd@Aa1H2^2KV&oGsB>ufK&oYajO|4Dd3ah_<&c+!|P-4}?<(Op<;b zeVXRpN>$x;;w<&py9>F1=0ac`eR$nuiv)AV7OYvK%)1yezXv;RI@z1D#baud#5#c6 zM~Lr9J9|2}z<FlX;K<A@@uTzZQ+0I|^G_v7Q~spHGeRdTj=o0+3mT}{;0%VjNS_Vr zxbvj)i|Gmv_*;_UU<q6$FC2XHMN&E=<2SCNGv6U)6?kG-6>xm<<Y}j7^N<`&^Y8@j zIZmn3k?<HqCs1*E_}%@+p&GIq1Nuch_ml)J5;*C*Ao|x`^ZOA*{&z)wzFA#xYY-E? zUu}dgjuX6D^NKopE)aGVnX~TmIyVyeJfP?+JdPu|<SY*nZP&Bq1pN(`2ZM0~L#l9c z4nk|*#`k62ga>k4aQ4FOs%a-kHM0d8y4EMSzpPO?z<JC-D%2%5WVBpR^m%8$)KWgD zx;q(1TEKqcB^eyXOocCRJzM4KezR5N-~w1j*{m+oWgaAj<sba{d&IPfm;ednF*dg0 z+s)ArgX>m;H2lM`@#q?d&0bQd4r{P2CCZ0JR1ctSz;8xLcEHxdx}Er6@D+DBZ2yF` z{y>C3`q&s)8UAwoFWXGae|Eb3o213^N0Zl|Lrwo+lK+sj{#fOIMSl9{6!n|6{-KfW zUqTq;e`TWnU)mGPpVMFej$ME4==-0rt48b8X<ZEIGrlY%-{AM$U1kn;ZEeyCslpU{ zs`CZARviS$)tfsgiE*{oC&#Cjcbd1U$Y5Z+B<jNmlApgAc{e}6J%v9B2tTDMh%Kij zbd%go1P2Iaknb?zD|}=mqmEf%jPer3bUxY06_a-7V`9dzuTNRWf_V%heM<SoIAI$z zl1R(}VU3-5$>nRWDHHUzk-qkh@0XX;?ZppXk-vA5^$8ROh(juqJemgvlT%72(G++d zxH`trfBC)48Jsv|4_PMMjgDO+(Bj<_e1mN655_C;NBTNDJlC%iKZq&8yw(5k+I)T5 zk1!#;oe?J)$d9f%R$o?6-$#5w|NaTu_`L-<^@k%3B-OWeB^Zeh6?J8Wy`Akkv}`ZV zO}X)w_fYT8DX05co(~P*`h0a$-_GjeVI!rik*Y#EvnHof0m8v#^&`dZ4ZBLw@1K4L zxwjl-pIy)HEzbsc_RCO^<ezs-@6UUWi<r#%T<!hkeXuGx2s_`a(F=U|i69mH=?y?) zjSLT|%+5;|6Y586zaU^m86<^TR<U6>(}vBosdI<w!9mhlZW>nX((a9vWhdd!8*8I9 zTYlJ2gr=a`Q+@-n(3LIz{Wf_xRp!*l7?Ay@Dr^XBM+l<VfsIIJQc-p2W_u%>cF$RV zu3n7Gb`G$O9OqugFJ%jLa$D?G`aaYi4fx7M4a){=pNGQQ*TM{26*W%)i;Qc!p{&@t zRjW4Pr7YJU4b@DJZY@P-4Z?}Ki&Y=8|HbpogcmakMuTX)i9puNaJLMQ)dL)eV+L4? z*bb3JvkzI*V1rR^kSLwTDlMlLvBWW<RM!!a8*LYMA<`liF)>-QK~Gw_vM!V%<=w-0 zJ2Oomz2rvjJq{!I_)66y4HR)kB7`N`aUxXC_RHF3@A58>To8{erX!GWfcZq^WhFOC zV(iSeJ7<vpz?}9VKNRJTLW~mwlDLR2C?V)cJBDko&afGt2C3ZyC94KBK8rr2Yqomf z`wJ5-Tj&c9{V}^KZDS@%Nn`8EC_&=c*XiYA)35!BHud9~x=+W?hnK^*=;P!4+c)%d z`aHcJQ8U&0dm|<ML=hsfV4h7`qT@7{t^gvLUX**)eO6SDjVdifR6~>yp5>QF%Qt;2 zktd7wM<A>t!2##sehjhjOZ^z|ObgVq(wLtl@Iq3M(MbW0&kEy&+F?z|aF~vcLEBNl zDEd4i9LtC)yAE<ho{eYm26^TNd06=pEca^;QI8CL-(#Kg0X40dhUl1}SnFBv`#5^- z_d&=Q3co5+_60CX0Lv`l4XZU-)^1Y@`dY;mu*?M`M0V5MTDmiL%WrJ#E=cgejX4|P zDhue?rZkfMR7||gB_kg0^W*R@_a-rzsCVm4B7~JtfyEYp6K^{0HN;;xhhuVb4;iEf zf)G%3(OcM{;mr3-w-~}Qght3_Sofx{Cx+eVrrjwmBjveFL}r;|=z_*}T6Dvfun+XD zA$%ny^%MD+PpDbMV9XeI<*I*Z4z0FMAecIcW{HAnh&L+sf{Nkb-NAs(j53F$8Wn|~ z!i9!XDCOLZd0?c-bKW5A^|m_T88HePViU)0(oh+<{%n!0&lg2S6^{5?8t@DdMH`q^ z2xf16a4@&PneJHAomk!|Wu2b63z)^}SsFNie_Qhttf`}{<|Wp4=iISQBXuNH8u07^ z<r`ks$w>HposKlxrnFI>+PX&yf=1}51WD!`9&#A8b1<oeXv;XL<@fPzG^v%sOjgED z7*Ep4!lS|%u#Qe4)gz}akv#FrTVmUQ?8OwNqCtDOHgLb&DMx69V~2^i_MgnY=j8TP zN%0g%PxPP5Myr<vTWL4Zz&Z5z?nlgO;G;3ZO2@}yu$S5=<Zj-rUiipVE2id4qNhf- z*1~5)NxkFpaJn?i4VUo_@^ez}zn%?+_yEr+p$aH2i*l1ay=r2CB1U`T-L6bd=4jYD zxiXnl0l;oh%HXACZf5j^=5ro4`MC}48dd_dmEu)?vA`SYFhyXW1Agq2B^7lV1mJ)B z!s(8|ppCV!@&AEwW!)gUaj#YNWxSWes3YmHdr^6|>o|?n?C4b0jc3g^om)Gj6lD!A z<=r+wb7R<RWEqqPv8F30b4k+h(v_ojpR5#mqsfup!;o>bv=h49P6Ss&AJ|e>2V84K z2ZOazPf}M3FN3PP%Tj8eNR@M^sm^Po<V)~vb7$z`L}ho#OW=!5m7~QhFdkvL^mjOP z_<64+s@#j*&t5r{i}ka7QAL4$2*T@wnaZEJ^h5`qc!cA*T1|c7k26LjzF35|oQ%9e zUzlrioMGg4FLJZ|Wa^GlM=jX%!%7d(z0FdOMfQ_9#HP+Hftwk&Yp!boI&t?og*eVu zR{#uJoR&0l_}~nuYN2@<$S`T?I8*jIDGnqZ3@yK7xV&Bm%9!@Q_+AYUT3#zVc{5Si zIY!Zhe;L*t9(?@*FS5<K_Y~vvc3_dB`_b5jnRF{^Z55em7&caVnz}Y-YO!A8<h2@v za8H&M*ltzlB_0p}0Tvc=te8;JuC{hdan{Tt`b#BVBKx*D41q<c*&&<&jGQ;d@FXF- z)R`H@yX;3o`;n1EBqI))*NJrzV1=LIkh(JOivnEVL0M;F*|FxZirf^2*C2qHy{_9S zC>7^NlKuK^7S?qjlHR8R-FU}J#7nA=WIV;8`Q{RO-D+ll04MD&s(=wi8K&C4U}S?v z&DkMTa3@#=mY!6OnFl67QnV+HnI&!d+GT4nm+%*%xVla%LhRC2rB@TJ!?qf8Xqzb` z?J$F)965`)QN%-SEyE%U{6WOM+X3Vwn$u2YDW+R3plWpHIwrb(jQU~mD-~JNU5(^y zmY8bvm<{*viR6qzR$v4kFtykku|TGF$%Okx?ktM!R)oUFK4wYmy^=@@1juC+tv@;e zO*j+NGf|e{5;`IgMpglCw|UPD#&{V@eTIp_Ljg*(Vn`lbH}W`=F=`3>Ji6UpfGGqU ziYs_T=YBvS6&HC}2U}I?6*hIHpS*C=$TuCy72&x-zToWQQFlV%NWj1bus%Z@&L}j* zP)e8NQ!O3><zfjNZw~}+R9<Sy!9vF5;Zw=5VHHAHZOc=X!2#O>Gg}Q@#!vhEOvun4 zw;Dmz*{~Tp#kSvDxGKDYj|H;7qR&dLY%9y%nu5R9qmPf;9arr6dFuk}10f^YZDWXm znEOAaQ}WBDa5EsGXf{T;FlYR=Xro^BvK3~!wW_l+R9wQ5g0wT2LZTexGox+<so@Zx z7BpjnEZDoXsjm_};tAnT+DmYg4FLlu&6-SSe$SJg0EvFF$!imcgMzdbSA;YzE!oV` zSrL;;w4c23G9H-A38C$YRYgT7_evgXj*5-r_AI}9wZVYCZZvGVPGE{_YzT%$0oaeO zs^TT%Bk+(`{V^0E!N|-z{DY@oa1<*+*(N3ludQ2e^qtA?aZbaPVHHksvrGoNBFbi@ zbO_xf7L{~0O;{TnE>RUVm|Jj#!&U0dl2)p}C#SGJZRC(IDDS=<L}Z~=oL<J8yc$M1 zOf-y!n^C|eBdU>y^&I7>{oT_G8(*e1>MSzOct9!f(ld@=ozYNQ!+L<Q@r`FB>nH7H z6aGFg8Bc+(I0_Jv2qY6m==46oBHz-q6HT#89Nz(zC8TERv6n33L~KWxdR4+n$m_Y4 z-maa|mU5LpOoiH}DM#s8B@4&?o_cg)mTI<C4b0reAXahz&C%-lbq})s(797K{4XE^ zO~cTJp~m7a%Axk3Z~Vsjk_&P9iHpPfG}v=1_rt@&7im$P(C9Q)av(iRe#ClWACc}i zBS`G&q{A`hC(>%I=Hc{m8m`cqGd16&x=ktT0$1LK8soab#!2OGgEfkj<719)in=tZ z1nb!<)rE%FjYMHQczVAlNjA59Pa3lyR*?h?m^qRn!#|<UWGoX@qZ>-gX-^+(1Buyk zMrBF0C3P)8Z9^SqlKG}4!X0C{4BBZ8mg>=u<0R}Ck>`=|@UCD}wPzRb_%rtoIxo5w zV{oA9@p&n$g}dTM2H62jr^QNRX{l7f8P<`r-N5bC7ofYivi?6qvcJh(%na-te@8Nw zKZkGryGZs|sq4SRuRnO@KSi=X`|JL{i(mhGT;{*91G4;CtNS~W{ncOiKO&j7xBW&t z;+KAY&<TDhnnem}g?SxC0>_Vj@MAjkPKntggi&kGRTunezB>y?{YO4&e-zG?a)r({ z@EDH9<#$Ij4(6P6z6SkOa<(7p<De5z{c5P*gJ>5owA6(}sFok!JY!7pOpT`-IpX(( z8rM0!z20J(r|s~A{F`n@79aeE@*-%=ccTwYxPL>C6SFRz*af@W8**ot>AnOS=Ub4h zXDkfE1@=ha)QU;1ANr(b&XGq}19rl3i3si9z6A}la7cNi!S3O*(<euj*u@iZD|A=% zOM8TprVB!`;B%GcM4nzXXLj|iwGZeyq;#~!Bxgi)5y_Zl&R97>l~5_-ab(g)a3Oz` zvCn>zA>`8x;SZGF{&Y`}TT8&b?-0!(%1w_ZYPHMFyq^Ssazr<@``rdUIO0UqUI@qA zTp3X;A9e9uc;<-*n!o?lrOY9Uc*cqxmqLl`@Y~#*PD55gn@f_O+Vkg$BAGH@A%mYb zRjMpGvp?Iql#jniaN{+fd&Ta%#1o?_fQ`bEWsRJ%hR)*a>h!ugzx((;Uc8>^&*bUr z_jI=VxjElo9Ue{`r`OqUg^6^ae@{>xaHOFWQ6Ry!aHjFc3`sCiC|x-5M4~V@HgNx} z(Xu%X_?e^TK}5VRsQ_a;oVhU#yh3-|vDN2bo!nq0?^XDDy@+ZmDVbNVQslPMTzWtX z!3GRGZLB4Ovy_Dv;&ux#g+P#W%s@Sn<0O;yo#0(ZmTh8D@tu@N3fi<z@AGqMV56*S z4aEaKiEk23=h71l7mz1XW{z*Cw~gUfqQsMF=*k=dMUHCAOt7mgS>n#icURdWyb9~I z;?^*n5^Fw^2+M9wS@i3gdbGnXTDlIEofXHHO6|h!-9%`_n;S{PXVPm<T^l%#wKcij zSR4xD_sgP>wL7AbgmF_6?vO3|-Q!aswJp<pEU0$RAqY$83E|T=WJ=s<i633U=bKKP z+ya$rm-P$%cR#(B3m4C!rX<Nh<4HDFJb$~hd8hTTlH^*@`R6cwn)3P2v%@v8K&j|Z zGPA1s!8uHi_AsX);Gf1I*t#WbCur5H8f}reA8EjmgJ4oexmdv@y2_VYydZxQu%=HK zMqj)O9Pj;|`J+vMc@ah*eTDI1{;t=K<@kHK_d)z9J7q%NbBP%`ya8Q&B5X+`_^HPW zbl&c(G>yYZyygphgEJw*%vgxUg~!+6r4ETfmiDQuM3dW<s&68hVisZGWAql?7QI)Z zV=+Cfb}eE{?$FW1ogWwfs4Br}?0g7;>r)`cakC5IY&ou{rtSTBl_M&hBzZA(7>v<0 z0d8Go&KxMeznbbV{EF`h9QVYy)c)sGaf!XIsN@sd9Z>Xhw|DkBq3ONA2lhCj9Ch!Z zmHDQOGF#Hyb|2^zNw*|n_jgiLbX#>gL}Byk<w`G!atZ#^MLE*Y8x7aO2l+)Oilcll z`ZTJE*Vfc3_&M~_#R8s0uQU@Kx^?hLqc2KvF-!E{d7~56CtyN_wvCR&Li?wsGGshb zNJnvhf4kxQfjK?nZ)^L#xE~BqZfOxrDxZDW11Tmp&~l|Wzuw?UZ0k%Wl~RO~dE94k zA6A%(YgKh=m$IE0D*!k{@JK=`3f|KTRaKr`y2qvANL6@#;GRFbt8%>^`#hPk{(jyR z3n0DjwN&pT&@>Vh$-hz6yK<rj%Gx8+q$<WF8%TC%Pxw5S<+?bffICN8wdtKJVQtZ} z<5#Laxbk6xa%b4)q=s*X0qkQ0eFg<1P^eLXKJHRHViMgoh<wjNvgWE8xqKa5znQWo z8Q7%SXTYQ$mQrYy>ALX_!MQI_D8J7dj!Ox?IkxJqxT?_KWkYxe^+A5>=eD$h_$eZu zmIYYLfh%4ZFz6Ea(_SEv9f#tsT>ur^7id$4AkkkDOK&1)vc?U7`(YGA7KT1=^$M<^ zmku9QLpbTI#={G{1v;{cae?bf;i1#{8J*o#-el5_E~pf{_yRDO;;i8$4nf(M(5mUA zR-uU~(sanV+1N#!45J<-yP#vJk+EFFak(}E5#JQkMz1YTr*Q$QC0r(I3$<pCQ@y51 zqR+m7FMH{O7y>4YbRUHl*JqZQB7#&LPCQPEHD}?L_?j^Ck%kt^CC^AF8LaOa4;VoH zSK&s=LZUZ~`k{^5x4sf?7k?Gz^_tGtVMqT;5%Z6sU%mpOw;b^y1!x7QxrT$;m;3up zp>qewi3MLwqTJHFpu784oJKdazaMR1)$1z0?Va>jdi<)zK_}KS{JOj#qK`2o{6$@n z=urM*`0KLDEiZN>APemkMqdYAJGS{qE0Dzy3bSR!K@yO&L+QdCyX&m5n&WxV@2_k| zqakNp9<eXX9Rlb#7GB|1=ji-v_~W63BoOvw^XSB*(V;UB{^-p@x7v0v>NGU!X=^QM zMy}TiIM0;R3S%On659U4cbDMMWfDcQ@!xDzeb?l$&)D##y!%`y*nc9mH(|>m<ja~* z8Tw?}@gV@V&z;=9o*k0%&Xl@SlkN#bt{8S67bJL%v~zdZ^v-FCb=XN*SIl?4!+(N| zC~A(`FQ*kB0wX@$EiNLj<eS+xh8)VU%_|1b4$gSNcb3~~N>yohev^S8UcydGwz{;4 zAhc7RDurdN)UIXU^dY#n#2)AU#5&s|8pIGr=+URE`NZ~CGE|e!lC_w>l_%@iZKp(D zbW-~q;~88l>3fv^m`(GJ@>m!2T&lcs30g}5V+aQYaP98bUN8f7TXgK}Xx8tj6a_P6 zU0Umw_y_|?4}sZu7N{dkz{HNW^^rNwcz^`i^?e*E<jhPv;uFB%=~$rj5o`;WyI8HF z_VQVwE1;hk#)@3E55)5$IHiVM!u2(AvML04VW30hmJf&yMAw|*5#0Ii42sO@f$kEh zu<NBv_)gD`P~7vlIoI+9v}~GI7%ZU}AiKNq<PV>68vh)54nvk<7;f;ZKA5iFC4$(E zpZ=N;sY=S&ja;#dF3bd%#t6mj45YFhR+cP)p@1FvCutHKNuoI>+!<$v;5;snSOziC zZS;*GqYYDVZKO{fnNT68x=RC27^-P;u`mW3C@j6FpateqF#jONQp+S=VPy63`mnc& zt)A;s_!&}Qi%7r%ME&A|ozoTJ<N!QW@pp;U7<_44R+QaHToX*C@LKYOBpfv}Kvd0Q z%KUVzxbd^?o-oT?Y(u!Q{zB(PI{9IDG<%1d5NqGCSc?-mD{E=FEt!CGDt)Qw*q<!A z;j!)~h~e$vma3`0$kyHtuZZnV+rGhm!X2+JgkPB9=G6di*xM@+rKAj4$%0qx*MQ+i zQGRNCcQg34P@ojpK+;|>rn%<3$=}v&62COY5`71ho{oQte_!MJP4AIG7Mg611!;d^ z7LHX!5DY=X-{fd%fpqCOKvb7)sOCc0_B1D-sCZVA%;8Qisk*f)dE2)of=C#nL~biQ zq+NU_f<UD&K?{M8Kqaic{QZDD?%gOT5i80oHWv6NhnVtor9W0oL`Ne<g5%O)4hUul z6--rb4P7EFKHkz|DX7(qY2UB~ytb}j64uPY6<I_Q9EMo~-6kNh|FVUgb298toUz$s zrQy}rv}O{dN?xDqU5orcz5>mQ(Z&)tVP7yP0zZ<uY~l?q+uDrH$9wmPUE>SYV$dtP zx_4J4YujzBu>eo<D$<j=@t8T-xg=U67s@S7QODrby!Bo_Rf&1QVqxBp(+=x^fnIz| z#hRsL_heVz_*LGVk6^ttM-gwvGQ-&>hAV=+p>}jj-psA^toyrWW^r0LQ5WaT+)!8W zTau-23EHLWtvD_8vYs(;4^fa59^pDsrNNMut=zU3)!aKa^u(1`!-)ie^z5#wP@=uK z$TL~vuJBDcl4kOog088OIE)WHNN}8xNN4X+?l8mgbsZwf5ZA4xp?z<R+7FuF3!IaL zQQA8bMs^ipu-!)D-#&Lu4HK8zCUA@7re^LwN#7)e#5qnUw6O-19oy2OHF%_TO$oRw zg>3f6Os!*etWgH(cJXKIrnJ2zo`Ef5BC|`iy~}9Bx98h!9*#r=;8*O|!bxl_MB_vt zeblR!eGrnBX!~j6A&r+m!_uR?1ZZd++j3`5X<`i{EiRIXs&7TQtXpD(ZkHcl`X5T1 zOhQ*)1Cz$yqhrd}f>vB<RgGG9YRaQ77Na_#;T6bD|5)S9rpof1kx3{wf??Q!g=HUX zl+5-F93ipoBxj*1R<WOS9fBvyEvAK#msy*Xgm!t@x8v}0m<yXFKVaPeBsO=qnJX=F zD)3O0bbK!>nqVHsc{DoqfRkA}t00)r0Y42Xu3=LpvGx0eWAbY5GXA|)oSei~M2aH} z@uKO3&igI&QyDu^<q)Ct92-zF8~^m6c#45fEK)Cr)S2)H?ocwuFQOKvbmL^UDXyzu zJX6z&O_<ZTeaaZi0t`cLExp6;)W&ZiYB&QDKI~289oXjKLJXdT*iM4m2a?-B_drce zfmuX|pjphGYciECM{-*9;@Og@7X*rhO)Rcx<=Kd+;*VCXdqX@wD8=W|jOlK=GU89! z#<#;9cC$~7)>$}az1V0^LA}DPH1?}(ANbR*sZ)=XQX6{|&N1_{qtW^yT4yuRF*>p* zRcBy|xP|qO;y}SPPq>16nXlW8fWW5C$tw{06^m#RCVtiIfhgO7Mm2f;PV_RHQTUNE z_Vwz|9K$B~&p`WcDkn25%b!5|Z{E}Yp!JUBuQLt*1loV~o&G1&K>k?ee?|NJt9bdB z*&hE=;QZ(M=s#u+u>P~G0oH#rYhdcP%XD1==`RBv=Y_z?U^=sxSBC-OE+tu#Ct_+= z4cwa;EHYa;i=u@k$4q|d-_v?m(sxBRTZ{p+2_i%p;j5{ttE=W)`I_`L#!n7zCqSc- z2BBhl&!AllhmhJKMJf1N()nU`CXQ$xjMU~!uw^zKC)4LRvVC{_92TRGet(VNMOK^? zE1Nhq%cEU7<<h5%Tj^arAa`Rk%%e`u@ckkET?h<g>-?=RcEv^RR}Sa>Aci`#8{jR0 z<N?`(7Sg=VD^nl{&0Ze1Q<okjQ7QpUkPn7zdbDfLipjG0G&7=yjf*nPF$uQrHu@?e zG9b3^czU*~Cwuo4AC77vAHSv}n=W9=XKg8sj1D~gy>Az{L<YX(C@L_88!sh}#ueBr z;Ii{#ZvvR$J4P6cFKN&BkZHq%PRKU4nC6qBWBHfk(ad)_wrf<v5THQ&YIBi*$2|UU zNg?<!t{J5)1s(39=B}5gLw(07P~Ha3{*>3<+s?zq>s1$9{xWFhv{<v%{1=ysUI(%J zhb_J)HuhDD&}568W{Nrbr@^})dNn&z9_g;ddL=-+WRg1;>Ca?tw8x`Z7;&es83yS0 zFF#Y>fZ~uay~EAMCF^{&guVl&;Q)GeD{M$_Fhn4uxdHiU8KGuXgu_eU?Sl82oPY^# z3_!&=(BFw_`C#@|?N+b?`bwHV*2x>=k=iAMC}OXDbLbb_#%}=MNFLF^Q^aTm$Nqj{ zU>*<zX(Si$xZIq!SRW9b-|PD#2wE3iJvxuccU4sPfPnkmLSEY7;Ujv{y=^q<tu{S^ zZ=xn8Wm~grI+eg9L;|H=!D(4IVQ!+IN;dE{y;;J%$;RKdgt{a^GuwVeSlgC;HQ2@e z0H?gJZ*NypdV8^>hX~BOKH(?ht>G})838<63QgkbS}^`~KhS9+IG>Z4GE8hpK(`k9 z%N|;$9Oz}XPg=hQJlOagFM^cEN;4Y3NVKb4HY%E<r*qTz601x$R^o}981-8vL8R`k zBXWlA!}>vV3re~K78k}!fza14P&7m^x;b)dBCTz|AueP@G;xA`ykb$c>eBT2MKitE zExo7_ji??EsbTC0aLTR7=B%UTiX|RinMVp2QJQ?EN3cD?N_V0~mEJ-l1#eT<OW>+b zf*V~-zj>;m((>t>cp&DRl5=l2TX$9W>*yS+%BwrM&?K+TA7P8OWDvHCwxz9ARs+-H zxBUg8Qj&pE)Sv#<S3v!`_@bH9&1^lm3Y)ILSL&Or4Hu1`NoTd7ycFzdi@Me~`_9B| zi@NM{=1#ZdRM$3&OJ4JMpztGLOan_D*p>>O<RRGO0iY`Y;BLs4Wsj1T+^Q2qBW!y0 z@wJsHsk8)PTmaqyfi1a;OY-&KA};K<tvcOp%f_1mS`@%v>MBOxpLRyx>HsnkBo^Fg z8R04}tH1AI97H^~4(Ix+qZmrRl0@n)n;58{MYuKMt8RqW(u-~cyI44yqrwZb%e<GB zJ5=w%4h5rJi&Uy|c%!+iJrXz&TUz5}-#k&B)SUf3r1JYv#TMO_932Y(aYJ-67CYt- zyBJgx41V$Z<QNSp;aAMKMkD5mvpvR~>4cC?VH_h}TEM9EkR5M<fPrKf@F@3Z0*E3A zk-a7ENdT`<h$N}XCkU0WbsV}V3Vd!iIJa;8S#f0Unl;56co8DhMj~B540>afRc;(_ zdYYAoj^<}w?8_i<GFe~{J+T!~y`?n`^J*I2gdC}i_VCP^5kiMn{M6bwuxkiwU(;gK z+UYLB>zeL9ry4WL&QaYM1OhGc?;mkz`x(P#EJ4P+p`+S+)VI`d7(r+NJhsEyf95D* zuuZrdB%olOC`jl~8*<kR9oU+DlBF$3f@@UTgpC)Jw)9vUQd%XgS(v!0UQ~F!Ldhdz zXV0MQ$^vA+9SUU^TGmjDo?a#-cY&xv#b30xWaZx8yFd61R}6-DD05Ajzi`c{T>+@X zcM6e_>8X_`bBhj1XBqRhAemulcANR{<eo{JemgRrKH<_u=B=?uKQ(eqMg^6v?nzB1 z&SV^Ra`rCc1M0zH(VafFQ0928IK9iFDI<|68+KBS3n$hp&vKWn=OL(nd;{pzne5S^ zse*ZEj4KSZXD%*Kb1!!OZa`-eQ-ZfnqpW$!HBw2OEU4f3$qdM^N{#u>lf_A_ySoql zC>dSN&so%&vY@0-6A`Aat|iuO)}=)Ks46$v(?bQG<QCVXX+rI>LDfrSPVlM6TUjct zYbjhl@n+;1O<KED;3wnB)Z;8B+A8OK2I|0)5u><RaH`8jN?*<wkb$}9gtRIOwjTHX z31cBNAXPSAbb3Zx>(l-)<mc+=c6T+E-PBh0^ka2<W;z~6q;8gfffnLreHbIAp`WW2 zo&I6(`SNk*3622*Ti&5L@dP*0<)kQWZ{+q|sc@`nP5Jd)eFXh!jFwU#H($ws7?uU6 zn9I}E?e2cq-u{AM^uy-YYl>iy0Xp0AvBmrT^99Q1qM0zqBe*VNp*CQLFzeXPKu%E& z3Ph^HB`YW6(-lu749nw!1vG%&Br4i>bB13&2(jNp86u<874JuQgg1gQD{dnu6W6S9 z(QQQRkgwNwGm8(9ID0Gjfts8o2^nlBghb!Wh5FXGfaWhfU(NYuRM};C&5R)Oi1NW! zzs(^u+O3k7D7MR6OBz<dWne+eu~4H?hGIRgR*GnlEmXYe$F7B#kOazzhJ7AE)u?m7 zKSlKG&QH}yfj=;G6zSWulfCviN~B5&cPlMo$N6Jc)eFJPTv5bn89#+lfjo1pz^O*m zkMd|~kK?Y=6g}0?<?)?QimM+zx^VjWMybWfu`RDIheDlFJtjz2Q=UlT`Yx)s+}h7f z)60#*JVZeDf=vSzB%7!<(%^qL>)w_2rD=(0_|Wr^2>G;6<UIMM1a0GfE<cp=%XZAv z`tX#dWT8+kbw4l)45J3SInis(UJIH?N`t*n3)0>oTX{U96p|xEnSH7Qt(vyKw@u-a zw0U!FDMSXrmDrH<6i@pz{{bpB%MkjjlNVALHhKqdues0UYKJ0-_2T2mDlG4e*bAye zlQmECEA><LWPMg%6yh5%FO>Vd6LZMG#Qp8*;qdCq^Yi0#{s_HZzHVL@Ur&3xkMq+J zXxGDFIio8C+NB0|h(jXy+dx>VLFIjjP{Ei8QTc*E$-RNPMhL`t<aQ9#Q6GsCgEGwR z-fJiE2pz#th;2AU2~r|9u`p&+#*O@itgI^OV!&2o8aznGiHPc@VcGRb@D;wK#WH`! zv~0@s)q`8%!w*#Dt;h$+GQzYKa}{My75>wTD=Yk)ZHVLt?70AD-_x`hw~7R99wNDd z{pg!5@@06mN)}yvoMa?CvIgLDmRu)BWSm-Ok|<O;#QxJTPb~ON4y(pOxU6DWdF3)9 zyhBS;D$%T((Vv%ZmCBNNYH+2<%8NVwBP=piNMY9^OnGYkuvd=ic}pN{hSHu|#*7a$ z<yZUntr1|B@^9AR!trYqTj-hGm-ac;>z{J2Af^$Wg~yYKew`Qc(<NLtEvbA9y(SU$ z86Kn#(!Karv&P~_i5&4{M0GBSWJ<gi`n2rP);Z@Dv=kbB?UMm<J$&&1KM4B}*|g6V zfu&5tahcLRO92-X&C8cMMVM>K@r6%voU`pcf{;Z5OUN&i*5{!oeCA`V;)z}HTd412 zF*hUv<L?HTpj?OyqgwFOR$s0<+TTH}b`blMN6MWhbk~wIYmi;bS08rD7_`ynT36LZ z*EnFjDqfO+)geb3ABt@?*HxXmMIxg<G&Hd;v?w@gcBC2jVVmPZnI_Qvb!wwswD^IW zXU>iasJcg)T)`(c1<n)R{L=h3OwJ$sjlf0hvRBMY?AS@&M!W-q8HWUJxpcx;qEhF` zLu_mt(;Ivjp1zgf<_TPY>4k5!q6!sb4VB6L9K7R(2iFD+LaHeHet5#zIq#3v!r~2V z-}bKFqC2Yhbj*=+w4TJsLOKCvdR#+<`@nR`CP?%%6%6r5HilWSa#fwXcP{N>wu0-U ztZdhKy$0I+g;PVOzNx6~Q->)FW_Hnth-}W$(!)+pxnlOCn5vGev<dZon#8!3_t8+P z6P6`+dzF;Y$6*hiMTaPhn6wajcW|0p_*ylxiviz)@g)fszc+}evKpeU0BbO&{@@XH zHnFDe(aL$c;+|mgFyM)S_XEsT6`VJY6%*`QR(K$pHMEIb!S8Xpxu@#6D5%xYCxFRQ zb5%BKO<G0g(lw@ov`gzlS$ZxHb^4WntxX%;g>~{YNH`!dgP(kkk8tZzV(<VV=9DT` z4Kfu4`{bO2?u5j`fk@v-ON3<>D=Ynylofjx9F!MmSgANU&iGNyWlb(Wp5h%zlx1JV zg7Ivy<+PyQM-8~dwQkGpx85%m$j9QhWpD0GU=q7KlBK&Lh*~4e)j=vPDOZf8hDem7 z`|qw?Vz<Q>mWQ6s1#mkdpPfJC_Ir+Ui7{7@ZkE3Q%{^!W{|UhUL0*5DcbPev|E?6X z{-x{hZy5P+D#fh-Pj<{7sP`W##eb~ww=@55DaHT5ssGR?`R5Uq|6uC<7fLbfpM~we zDaEY+Zg7nDsolnJ^X^R9&ZlM!K=z(5Nhctin-PbDoMj?@RbPP_Mda9|0vV@TS6|OQ zU*Ip*r>d*kqs1Iyq#%-_T^CnZS5;rv+u)C;kAAmX`HL5~q!%~cmEJY&w{vN3=f;)z zVc|zc7$tH?UT#Qb@=2gKC*yp5zdM_mp00*ZQ~Iri!?Q`hQ}T*#4_#XKY@_ufTTR=U zi|vbv>kE52+hxec1;4)1yaE`b6?Paw_J(xq*Xgwt!<@bOwH^TP9TG>TwX_u#{6PX> zaJRkhau<f){SpKi4gI5wMw+(kPMy?h=1bE2PM5>7y0#JQuRi+d=B@av(eb7&)(>qj zwvMh#4PKv>w-=_sg%9#g&HIy%4Vo9>a!_sqBmnvqxvz3LnhEQ%v7~^sj;F^4JfRT# z3JkS;uN;-=p#`YimVG(mN%x}jLE97Ck_ug&=LX%c?r9bmTz+UG#+7KgG2PF5lW8nd zIKzp^<o!6(T`OFBHj&5eiaYVE3E_h+EVjCK_WC<gM(E25wd!rx%k1muxsy{kjgxbu zlHcg=T1zHr2$@}bM#f~6TM8z9kbN#PwHKJ?qsNdshBry^=D=y0kxMEj(_9nVRDGLA zf5*Y|v@Vz<XO_NVB%Th-qh&_URgZ3MA3mND)<qYYq7U{MR!VKAFeXOwNp6y;R@DuS zWS=CE8fvJSZ<l41M=Hs?wX!IFvvb(FR`>Mo+9BE=YqW3~B8Q%qc+}udjeeoaC*t^h z#+^GaPjx>Uxn%7VZo34Ywv8B-_|$BjcN`~X#jNp5GhITXm93uMx^Y`?*QY~38vvXk zKm&3~aiceQwWoxK#ZUpm<sOg4Xx_aANyJ|ag%i(%TN<?>uBd@74N@pzap|SWparyr z0b@Wm2!kI)?cXp0Pl;Uw`jUUYiy7zGfwu&3MCs~_im(wC?m|HeaK_F^VAx%RM0V8C z5xjzfbGo(!dqC^-MvX!&#~R9|bi|>Xb4}hFkwY6r!P_Gwh(N&L=GgGf0WvE>nF+ap zG(b7=siBabb#ekEVAfJhh!ta4_*X`Idpul6v-$rn0Wa?9ZnYjF&E}HPPyYZ!`S53p zxI-KPRb*&d-1cIDufKSs1RA#R@iTihcKz^<U%7<|%R~k>WEtF1mzl>AjznNw;EAPq zt?PRJohql!R16Z`LmB-DML>;sp#2(6FJZkYxoevcb`yQI(J#NlE0M_NVaGu{&`G3^ z%6bT(<O^md85d+LgxzRO-K3$NQsn}@qI{uA*qCh>+W1Z(ozn*^e}k$hlt;k5sV>qG zj$?90W1x6IXOz!WayI!SQrevKT?J<)8cl!v_@%an&0%H2r~8wnXJ~)soR#1HI!X*# z7$))FJ^|_??Xs2*OX{aZRd{3fIqg1vt&~Y3tWZLj%6NP~bj01fuJQs=ATiXsPcTO4 z=&pD+4nWr;f65FBAe7kwl|#1uZY&3Hu5%Tl4ZW;yC}XbvA+e&`s$T~^7I+RQ-e*UO z%v|uHZo_-gs;x_@B#@frY2(_)wN?aL^{hQ8`<=0_0N?u6YzspDN=4Df)4MoXiB9Jh zsL>kB@M}?*K>u35F_2UfLpIL6wq#Ld=F~bfHq*3wn^50g!s65YWi8sNpEFEXy9D>( zgXGO#pf<nCM%K=-Yrr=U;}jwDrrLZ)GlBTcM{KS`&_zb96VBqQZ-)Rd9`zFtFTJ>! zr+mm7n3nud)iEi2JyljJj-MZuTI!vzZ#bCm`E}k*99Z2!KSh;xN|@(X;`Ia%I3+>& zi$yqxa)v1(5Z>S-`cfzM<0uk&docvldajFRuzo;Xzc1{|ihVnUya<DAUS1ReZu^MI zzLg5^0A@i9D5;(V(0B?p1*!I>216JL2yLg7ln0`*K^(wgRUQFrGla5vC=GZ#&%_;Y zPeXDqW=FD1)h!Z&@p6Js2t0pXKrn0I?$p|-yNB;AM3t;{={<LP1LwCt_mON7JZwfZ zSvq8uiYNd-uHm%{9+znV0;$hGT)|40NL3jD_bLR!Qszt)@|f#~2qaQvdtu%?Vi3v* z;>xN<^q{hn!`X&hc`!pGZWXk^V+JkphypTd!tZ%G+(XTcl4@qZ15t$qIle~u{Av9B zoE~B_+**F`x+<~kXZ8rkeiQJ+hXf`uCsaq|G$45gac)wmZ=cIP1cG^E0>1D5a2-Dq z*UK&aoE-N14k`Z%%B5~g68tT9lrjx!8G$!SFHp<d=Hpry7`#16&MccS$AnO{V}?d0 zA+jcSX}+C%_PNjw0dFVjKz|Ffe#l6ayV>S|+<AJg(&RA4xA<45tP1UGkruIL(cv@j zJ}dp_0o>Wer|5Y;sRu$J9XdJhierIfliykI5{o7Wwn|^6kCt<M=spEjv@P+5EwpU) ziD(}5ABOZLjYmpUGtmg`#GyC}CI*WHpqJ)7N0Kkkus@9@qB51KhH?m11;Wm>Ym*^X z1@`8Xi3Ek^@+KADS5mNZON^|4Cb>SZHc2((C+;^{c_f>IvO0nvoiSYj$Ra&|Sh#@O z0QtzJh7{>APN0(Diz`qu_d8PAU0&*X2wsRNaup7XFQieo&zpj#VSo6iNu#j)RI<W+ zPf789l)dMb>L>p(+&xZZ+6q1$&ciId!z*jk2T#E5qiSm}1?SueQA_;`AD0*e&&LNy z1;e&<d~%Hgj{E@dv51I2=bH)^5eM5ITs|CoCV)i9nijjb;1ncGxZqx~-&gCK;8fq8 zoRNZ<t}~1kGK-aB1_7C5GM}=p_kb3TMhsUvK3Q_7N*`~;ucs=53{SZW^4}j*KEJOL zLZ=;lJ(jS#TKORq^kc085|Pr!q<$G7=fc{6Dv$SHGB5Ojz68`xOX%Y)Buz~SS}>Th z+$hYCNqoenn3zPH<jq()?7UV>#uU<shx#m8b_#ah_>w<-A4S8ca9Yg?YCHY*9?@YN zu<{WNZo7vmrBlnSG-v7@f>oI8rCf`c=f?>P@AZ@G+!xZWuO0qtJ>18-0sPK9lJNkZ zQp~u{4MsmN=$)`aBaum%&*2MVR2^j`G#K$fS%v5`H=H(cr|CfqPO&q_cv{LmU@Hg@ zVn=zJ;4`nWvkWo+G|~A`z!K*9EgY3)=a*zdAnfgDaZqj9tsVj%WSs3J4o*cS)J|UJ z**nWOU+`gYo`ka5Es>LNbnAPVvwFr422Bl`IDk)pjiY_|70_2hg_7~dgI=LOOW!y_ zPaC$%a>VqP?g_f9qdcN6B>bCqF@0SkTGg<KdlzXQACIY*@b9%^r-QXw`gvfi<u&jM zN3T014a|%p?#OZoCx_lq3sxW#j$zCpOGnFiSNRT<SK?OsC0u(|!RW{ad7=al_XKxl zqj%yJ9568i`ejpMM#B@z)sqzvpLfAZW|SThiOd6&h`gm-@;T?Yf-F6KxG@rp<X8Dr zuFuZ|#qD$zyn&K4T%dy;Yu+4X;3OE6mrd6}dMw%}GNhd)*NUpZ%*tS%@8(fL7d=RZ z$#>E-clH5nB%<TraUje}yS6o0LMn#i+0kMR&7^>a{9bmaK&8xwQ!!?`D9)Jl_dD~P z@bjC%PF6j7*gI^}jtOQ~@iois1lWWjb$K0Lf{rBp#}$2a(Bn-tQ};$*Um(&{DgCZ) zGK3L)<f?V1YsN8<P6esL$aJ)QMMzRS5rmTpng@#bF_==5v;B9qfp9Q?KkVu!EmF|} zc}rT*-B0yz0~b(9>cfKq*p_DZio_L-C1G<<5tUIMa2ODkD}F&%=N1h~Q1pC8B&uSA z6j~URt`>!4N}Taodt-@m3_*b)8~mmeo>9mwNIu2LI6sY`J<!N4iYJ#%!eXW&$A^Ji zxG9ZJatacWQZMrs!7Bka*zsOb@XNa^66cs-1)7tC7(zKQ*|~nF$%121%K8*AMyaVH zWsB`H9@hn_A7YqEs{qNzQ7T5tMC()%@JGeUb66A9f)@d2&G=mvLaD^ZwhZGWWe$Is zA)8pxmU)B<IWNlV%SrePqw_+X`DZ}jy2aq{L@TcJ`Okut$}zL&xhSL0m|RQ7a_j{i z0^!ASwHNk+$|{CYWSaBMd13u@DHBfYzNKEwE~WKpR%B5wl4I6MHnq`yE$3DABTJo6 z?*p#r5=E{)>0Tnz9;TkePdl%D1#8Jy8ivNl+;Y6t#ZT1~ZMVLwa3sVs7D|_$WSApB zmQ^<Ni_?mFDCgf3_9y<NKeP3Jf&x~2H(q=o!+K$D6JKON_&WOSnqGEtu$IA_=-{MB zl)NJ%s?OLad)8_mk0i~DKw6J#W%AA5M<wtKITLeZw$BpcgRLH}Mda0%+k1+fnJ8$b zWdFhS;`=qfp3e8{cs7K$5mJ6-DAxU3ZY5!S$dAEd2=Y*zctvQwi(!+}l8qqr^F>}` z-bM2Q0rUx^eP))LfJB|w{Hq_oxUTKDyTyF?Z0CwLWto^%SIS<+#04^mf^DT}uR=&u zX>XLIEl^y7R3s-EccfoqHZ6Yw6}gc<t_-)yHl$oTjfuMwl6?*lckP<q0?qN?9^M^d zHF?We>gp{kEe;Ky3$>mO(+#ur>O~8(-@>fRS5&x93|(7V)P`h6uumMAsY5Fx0?3M1 zz7~PTRzOeMPEhzzRUvd6$WRqGA$3}>ztK<6xKD*2o{_c}NhXzcSmOWwxgQeHm*0(f zm0lK{cSU5UQpthKZ67|^hf5PV+euk`K^g=y0#Dk0+0hBkI(opt>n=zq+)&k~d^orJ zJcIEOGFp-J%-OogyTRErnds<3U!we^Ixz*~ESL0;{vO%gdRFu!%>csLV@JVjHuPsy zZ38oJX#AOmdsyTX(cte{>bde0Zgo6(jNfb1s_~6#ohW$`csrJkY1*h|03S<#k1Vak zx<ed#!c+EiiKkMd@Rg)iE*on3OfQGTetz@yH8cF_rs(t8)H%(`3aH6047coG=6Kut zj>b-|eeMj6JPA|4%dY{!w>1igmzL#l=w!j>L>q)KnJy97ll(*lqJ}<EiPk5~^wxD% znzP`+;2Vohi7f+;R<&y)We~kg3s$Ek@FkiYxqV0SjMsC>sR~lvuT7k>r0i9_!7wcn zhoLh<5kjw5)nJsRxd)LVC*sfSF=w7d^*WVQ_YF%F!_bZ&SoC{Y5^?7zfm!d@{TCp# zOWGgp#m+8HriQlvU90$;*v`!H=L*K(9Ps~v1kd{a1U>zM_y0}>gI>_i&fevBf-dWS zxy1SS{`>m=Y$pHzQqlOw{M)}(F8-q2|6@+>-zy(~8LRncQUC8%kpD}%|C`xC`}ckR zmtBF0?Z5J&bpL+4Y=3St_*+GU?XR_=|H(VwqNCfi-iqkETxX!q)qxvM*niD3pSdPC z=aN$zX-@|4IzoIt7pJ6qDmLf3=klt4ukDq#0tyL*Kq-|evxmLTp#u=T85|7i2h{`V z2SxOrJVHf5g?e$e^Jaj|(+yi$Kd!p<PWY8UW~w3Ls8DR0p(g3zjql_0MhLgJ^K^Nk z-~S%Hd3HtiEP-mu;NPRmx3!!-xL3bsXBXrT-sTU_*9lw5P%*w^?!#i202p>%O6d@7 z;3?nB-U&a~H6(w&Lpu5Pyvhfc=Kb#bYTgTTM-{WjAyGvk<NU5gVi-zKl^W`prVPG? zB$H9vL=l5*6||LdBAdsyeSm*DsEDOzQN*1D{PkqZpkIsr_95%=R>a0J4v<C}fI9Un z*MKoWMbQ(%(m=2J>fHW(Shu?~6O!cu^0AkXcpnK3%@dy8Sel<vLDv5^ANeD;iiReM zFU94zgg@ogg2DsiB@~S3DK1Y6S<2*$4W{qWu}0&D-}C-z_N0g>+W+2L13wyLq_V4L z()h((0yX>AHGrdjIGI=WOWTQ^H*FAXL=p8!b8#>!af5FmTmAn3@b=EZvPIjP=eBLz zwr$(CZJe`h+qP}nwr$({oZ9zxz4z|xs)~Bu(ba!tM&yb)BQr9_T;sRC@qKVwht3<( zU?HsIgEZ9MZA?{@*Ji$FeQ;4$S*eEUD7*}ePQGU^>e^#}gnd}{gww&Fkr`*N*EGx| zLy`xb`>aJXSu!(6h{*&#a~}w<;X9X4vey<q#_q5T)uTYuwnfCQV{xbaf5Hp)B9=}` zYIkc^p3NNfdpq8p!IQ|1nwV21F-Vso&0_{6;uq&H{1|sUQDa*GiZ>^p-Spk7aL&A{ zwlNwHMVzam5i8l{4(p`q*l1)lvCV6Cc_0^Iqz4BQb~?<0#A3TnMP!cdl*$+`#8ZWT z|E<%*nL>~%;%i@s3xqmq?SiHt^Px&)q^lS*jH)st9PevKM)C5Z5*)vCl|}A*-DKy- zx*QQl+2lGu)z>PTmnIUoM{T(Mh6yTYwo>}lS~paN%k&fTgC03(*j(l6_VF<t`}}Zc zHPcJ!#%(k^)e5Gn>L*A75qr`&^y5ElHY4ltL_~^+WR+k5$Q9qAJOmaH4+B0bQf!#+ zKiY0++F&-bmrJ4vVU*^9#Z6oZP8l87&p2$yyi;@Wyq&1$lZgn3xj5VawTSUGAH7{v zhS^ry8Hw6v4jSWLz<A%c4=F<&gvB7mjzke}!XX>x_UzQz=}miXLI$($vHNzHtXQKk zU}T;#tcTLeVniR0WzI+Kr>W4;L?2-wH}tz(n|7u0`(V1mC)yz-nD){$3cegCN6+Af z#zAwWL5hL2P*_VTn5uwx0aYj?m{d$sB9X$)2qa3rxpEvkbR*2HXCvBT)Osg!NsNU& zA*X0Ol8-uWO)%bx{H?EGRyA#y5v{xTzD+duNK`}307I^*P_*`Qi{_)`aI~@~n~?ri zdXh(lG9Gx-OYbctf%{NbYuK>HLf({}ja#ZaKO^1alpVO7J_2f&K&q+pJKl(X#X#!w z&j@mE_s}YWNI>c=mykdS0W(6sG*pl?8Zb}-h>^%y(2*ZzIVus33FLrYcvL`dE-|q6 zGXQJ?12r_u;)h@>;5AXaK_>jv)b8!Cz6I<tq3qXsaQaMJE0nufP@m{}c?HrEqGJI_ zRiV)xeu=Yw6pjtEwS~+UoEAWNTXW|{HxID1ZYykz(rCEsC!$TSQgH$B@k1gN)0M!t z-Fvdq^ie@g(m^?R=)irs(J#k_2y8`@<XiX3K^D#7k>vVJlFBuOh4b=;i0F|JU2U~- z0=i;x>|Ewn(=N#7dJ_`*R8<=t%$4wl2v{<)7Yp-3?`ViYnrTyi>}MB@7*VzTk(Gr^ zvdXsj4TdR5+a?<m6IA!VG2wpf${3o%(Jr`}KRJC_W~<7oG(3*U<pmMQS4^_Q0ZFco zCmW`+eE|tI9Vp53SM0$3K#OP7BUX$1N5rIbw3VA4Mh8eJZwl6i*$vyfO<jCiNNQ64 zoF*FmDeXXip}1Ql1E?_oV~G0Px7tdH1Enev0)z;oOU1#G5hW#MUu}wr?Y#7FUnAY0 z6D@^)FWB4}*KhDY4mOclu6e>sk{wr;GR#^0DJ)(EDTnK1^cFH%cKLEz>tLA}fT^g0 zHJ$pjN$@NON*&2hg{6AAR_|S@onF8+G=h^A#)Lm;M?n7D{#N%R)6fH2Y90}vgwsSD zR8+Mup^wM45vg}ixH1qP7tkZ(ZbR&B*uuyboO!fiIllnOCLHV#{u<fn(J`;9A1ccY z*ZqsPQ9cj8aqE^0#(X=nFHZr>(dv(d&=%j32*Ln`b=ahQ#efQ;0$(+?;&*V~GA^0> zOTshFAoT>yNvgEao8qDrTbn6)laAOU$8Jlilpu_cWM~N{sxRCnsl0|fJK-d5l+t|U z3u=Lq_{FH^{@A%l8T*2YQO3O$y+Ix#)L1A(qO_$QiPt1Pi!j^FF+9;riMR$~frf^0 zB8X*Pu2cc4ywt$iJJ1WQRH3LB0e*SqCCi-XI35ZXTV?Bv=aVJzY5jFQ5t(iXFx4XX zebqf91%>5tXfX?TKPo-PI6LH4QN)p`z4^4B`!5QRQTfCcFrDI}t!4%p2L?sFN|ber z=519rKZ+_vcJcWkK(G1rd)kh*psT?76j51~>P(Gi$O5ENWoCmXjSCn8S3BIqk$bIQ zUht2H_zDwlo+WK}FEz$Xt48i>Af+~$Sz(gP9V}NtDEeBoCNLU3Mt+g=HK@)&@KOms zUGYURzq0nJvil;H_<rhGz1No6quO?I2G2sLcLq-~aXy2B)iqZ_d(vjLxk}M0;`;uM zqh^lH!qRiI>|H#Lp5hQDUrKAoFR1wLzW0Ipgjw1#n&3crW3^h)_~=6_F@|y!QG~Yw zI`YK<Z$HDH@BEU4^jMXUfac+uuQuPZ)aiYr-=R(|C-U&x@80yOm8a~25oo^4y*!Eh zmJL&Ov9S4efpMLiJG^w6OynQc>tENh2#c*LmCqM-6^gE9e=jz#<$Qjr+h5>TP?WPp z^nUN>vsRa9Y(#2C!h%45x2EtfP5!;Yzdin|j_tR@ew^ko{l}6yTgRo68kLN=mTNhg zlC4aX=~6|VD8m@$cYc-_=OD$Q$#%^KGxm^k1%V-~REbv@5+Gl)08@kxB}oCspK}&D zdxU7yANSUroQf(yB|TKRq4loLSByj;V-DUk0eD#5Y(>ioL*+rnME4B7qVff`pgi$m zFdiS?3L?ST><^WnY{seL>f~CWB&3)baH^QW1_8!W-pB)lDo^GtcA5eXTDjsk^$jD= zUD}jgwqeXx4Ga2AP_XhAR=cJ%5*Wtj9+J)|IAH8Ui~&Sj21!+O1>$Nip==jo+SW^3 z<fe~vm$nB%Hz1fR^P$2I?E^pj(H^!mnMKa;YKJv_ipAZW$s2q%?1=eOcXrDoz#$3V z?o?NH{|q&g-*u4N%NLXt`8JIp=K#56ypBq-il(S)P?I-UymH7{h)s5DnJLU<8jFrg z!fR%dvA1ZU!%m-VoH8ohl!?O>CyMIj2+1;Lgc0SZ;~)v+G!*N*vI$(IiJXY!=1kDC z9ac>1X$e${vfGaiJNf9RF*{iinp|JLs`zNL`)tNPN)ni`LE)44g!IdOm6}Ng!IrB( zk<;7ZoYt+|NeYTZ9K6CaAQjNL3fE)N)h2wIx%I!EOLoJNr<yw`$5$<XJnz9Q9Eg?< zj*?9!82)BaNt^OfPAIc31Stx+oFB4BU1i0vooXSz#Uh`Fo;)oK9Vh;lRqzIF*Jif9 z5Bwe4F&37+sw^d_h`M&xg_*OUh#kgxby<^5+!-0cP9jm++t@zxj^oZ0)A)L~qff4L zQ4N-x<I8b)f=6;gzs|`EOo~~79pU3SSVS<(zPuI2^~X^V)wzJtMz)2Pt-_;Z9W81j z|2~-=Tj~J|U8UK@c25sF_a2-L3p<3@w>E_4ZdZ-?IblnRt-JA%I#{bzN~wY_IXW1_ z?xlOQ@Pc>M=r>gzqN$23*m+b1zEq_uF9qYwwFGdprw*?XHhCC5_w(AWAf8=H`ML%? zy}as>VG=2?ck!if{2N;c?_HGv08>)xhLy-5?nd>tAM-fwz|a6vAy&6IJ;6owj&P3^ z&(%55fYN9BJ*x(&+Z3djzm;)*g=61o+*hgG7IvLZ!cqLOERuKVXSsY9^GZ$QJX<Lg z>IiAnvAryOl@6++N1PsigT5?G<rR9~{QCU%0&?ZD^qU64h>0uWFt<+SGe}a8ik0yA zhEwedU~+_gN(Z7hic?>Do5%d3V&Veq>zTORsHsh{YcbvtDQ?Oky?f}m>l08iD+}2q z<qC<7O!|If3(M6n6<%n=M=bH|@SnRKO+iQ%je>={Z1(G{7@AUH=Zb)r$ZJS72KJbk z_gk(6<nRIQ3&0CbQ+5E>V{E?j^(_O(P4klR@j7IO$C9KVZ24F{$*2ToCFv}QJUlP^ z#b*&&zKph!M-lXB(MtCXXjdUs4go|*p_6Xj9ZqvhBrGMl_}9q}vS1vu3_Fo*ig(c< zW4R;vXqvdqvW06&nZ=NVJmzG}4GSBMP)n)Q+CCOZ2ii<&vnGJyJLL9BdHDB}hG>dH ztCT0spKFm0w_;dgEISahyG(%=o92rzk<-YQw^P=rbOJ3mG>lMmzdPCHmt3?BbITo0 zxUwynjCq;9+zB!mRs11D5_?&BRhUg_c_dZ|GoZ*1O({`P8t~+3)O;b1y0fAwX7;tJ z$n#j{oAQS^UX5_YS+>Kvvv!Wb;nXNZ7Dp0d^h8)}$E3MvL>8mhWN$yh&T$GXjip9m z12KC)MKP)6>?~frJ!P|_NyS`^7m__ndJ?n2uEEJ4Wg_ehs_cq#lyd}G&P@>IsoD*v zSvbQp%Y9Bp77J7Je?BwLSm(dMQ8Sh)ul+<ew-}s$hNOCSC4O$NWn1R-8&V}?RcT{0 zG_Oqg&|(!+86z>JEis~ia!Aae(vGo*b|yq-x13D)`tkIH<nj$hPn;6}FqKTvRQV9u zm2hNUUk#X`%nFua%m$W_s)tHf&`1J(gjf1rEH~4tdM)uN*Ir**f}K9GHC_DiA@U`> zcD;`K((i4sbVQ~|V(cEJe>*d3E>Xw8VU=`$w#Qyla135>Yh-(Ih05vC;OSw0mTfzG z-|Q5x@Es7=Dnp3xj<d1)aFzDJq3A!h_e6=wF`X=I_@v09XuFYe;ZW5vkU`EDAZ7~8 zRe1y(i+(%)xY>@Mv%4XEV{2ustYIqw+245qw<u8SY@og^vY|M?yMHP5j2)gNInr4c zGajt_u9riT<AGuqqFLCG%rcxG0v(lQ{6yk2C7wu=J5Xjz!f=Pobt3NBt-YDX9cG*o zvP!VwIIon{JWLmjnv9{`>Foq-{Kw4`*#^(1-A)Y&l8y`AEIQ4kU+;=^msyARL$Wd8 zd=D@>4K)11#jsl_I)m@;kcf)WZFOjsgl{6N!l7D5<&s%+#2I?FwJx;-v4*U38t7xJ zc<fb=Wae0GvA$^1%tvnw;OI<V1J&_0jcDLbn0=S81#f{R$Brq8lih88txwo*j%5c~ zoYc=(1yTY9SOaewmO5;ycdgRRzTe-A#@(6)b(NqtX;)<re=ue}OEAQyi@1)ze;yn^ z@|i4HPqG}H5hkX0l^k%hH#?d<cS5UzKa#M_H&iecIaitoSRoxRYKPKI*S41kM|%?v zgfDe*T`&S$D(;xiS?xv6{$>2uCD0`W1#j`@xnT<Qi3Jjzx8vFWd3S=0cOzuwJ%zGY zvqSG*34Y){&6tD0V_rt!7P7Dl<F1$X;2dW`*$b&n>;bZO=YqovT{UmuT&N@~wK(+* zW8j`Et0Hf*dr#!2zze%Btvuq-olupUDd`##Ou~4UcE<Xe*JU8|M8lw`Zfs&_{VJTv zbaFN6EfOZd|2EY0eFjRtlbDV+9i4m<PSFYaE2r*`BdJF_0GpD6?8SbZ3tgiPluvL~ zBTBKG7w3BH?t!>FL>B)5{~nKZ2dqyu1&EJ|JIqb{#VY0+#?Fc7pa6WAIKQ?2`FAwz z3r~~jCn);&^DWOFYlu4g^}9T>?VX$2Z46u&29L<GXs76PPcE!*wzfD*`4Gl0xHqqC z;|~W!#vsRbC67UD(l`?sDeF`C$ZezUn$?HDy@H4Fx%U&!uW=V|&X_r66@F#C<2f?g z>>Z17C_)iE@B9Vi2=xEJPX9L^_)pgK4+Rkm8|!}<zq9@8*vWrU(Zu%OP5Ay}B<G)S z>%YPS|AqhkbCv%iil+Z0WBSi%?|+Q|{~h}MlLtcm8?OBEg8vVM_pikA-x1#bbtV1( z4&i0%oI7o?{X^8$cN0rXWjzn#LB3wSvF50z?FOe!X|GCFj5Vw9U~{5yQdeE{P~n;K zjfE}=GqyiQC%qJA!Gg7|k8km&{sR1&Jp7jF+#exTXFI*q!1{TSp{kf1sq;sPzeExf zmC;;OOUyF4?h@78>$?xre6_V-p61J}GflrvS}}>{S5G>w+Orq`Ha*)V^b2$6ANbFY zjA2Ek<SSyb;{>KK!yOjT?SN^uC(Sw|OT7cj@f|;22PF|yS!tV#QI_&PKxz-1W1K~4 zF{E(_VB$oFpEd~nQkb_Ff=(2z@y@MN6Vd+ZS8p|a+L*?hVLj*7Rq0}<m(zUR$J2UD z<2szk`K{f^162g0X3mLXKXnQk<QtVHp13F>5X4Q2M$`g|zK0NTpEo4s$s368voU1> z5qLch74o20jRySd^Mw!R_9}Y+^WUVlu(Xd=@mO-6gCX%yqY_FVOUY=1XmZaaQ##7Q zEgR15Ynpb7839J*HzkRC5A?}Lq)8f_6O>_suR86jJVx`X=7|C#$Kq7bB7d4Mb788$ zg9Wmo(w6+G0wk(37KmS<B4>jkD4O-4tP&EiAC&iP5f4>Ho}np*R@(BXvuu}}!`p_` z(S}GpSvRkdm@acO1gO(CEWd9zZ2*<`HO)?-P<E%y*h%CWXcwp^5>ULW+uSH<_3T>e zc6M`YQ!)o%mR)*ve|f?5N~#%PbGS`$Pp8smkYmU>A)02r(smb0jkt_TuuuY$;+!3x z&Rry_cQ0aSsSTB5Ry|8xi_cLM`7+EwQ(Tlqw`}${lOn@h=ubRSgjW)Ssw4q~^4Gx0 z`#E{STzS`*r9P@Z`+WTB$khV@OP!6^7?ED%CD_>4(at~CA#7PVVyTd_jA2}i1?>SA zb$;LeK)RM0=uJBocFL*8jC-}H8u@ERlYUco#)9VVF*gFz?rP!+yo*4A=`+s(c{>6m z{~#lxn(X@IAM9&jV*J?O$i6^7bT!?WB1|$A#LQ+V#wjXRqv(lsjgAeUQ7kzmZ4>Gw z(|?YbMl@>IcPi#tyiI2&K^R&9P?bqpS9sL6_qYU*0T^aa?f@=;HL3_KDV(IJl!9d( zq&h|I(b<7YO7Y4Q>PDA>i;1rs+*mngVJK{wVmToT;_!`k=SAt%FZ8Ay-15d~xSu=c z`&fB>;zyC6LI}{^p>cb8?bV^fr?4hqkMEW;qn9{Ty6Bz>4MqXQUuOT3U5j>k{<0s| zMb<J(F}yf>4J4=ccaG=_&Ji-|sqSm|%Sp~mihiNVdmIfTo2W=BiLI3?seOCd`*Fvx zRbw|gQD3b;CAR4G;<Hj(aEWQk>-niug3nlwR_(*1WqoUPqA;DEpYG+>v@4k1Jauwd ziY{JHuZ#j(zBTk2Cd%noi+|CyLZd@F7^jggJtA0qTs_LK?3z~)ql98cXc;Phgkl4y z;JQJ8e5(o;`mXI&X_;!7`O2m{2t^$;Hx|$ivCe=vBCX-@9c~QJIUtzJ*jsj`<f+)t z*EOyWRK3rzRG14NSKKEnH?@Hkr9c8RJ1D*A8`={qJ_;BX>CtxD_R3jHEuQc86fODK z5ObaxIBloL1op48Bua>Aruj|$-BqjRCmZr6s4Il7Ts|81MNX8E6%2xd*9i2qI7u1C z_|%c0yGwcHfuR2GeH{@Um5_n>7dZs6+n3H#^<CcX9)PiW1pXbGl*i7@VX};AGW1+_ zZ3CR#s0UyUYTRSsnu3NY+a!2;d{WNRud|eRW6T&xOaHNihK9MTbcAT9BJ)LB4xKne z_8ozRNTY~SL^{CbiX8$4@Z~P^2z0+Dv}5F~2Po)Cr?QUJ-SDB=%<Y5L4@l8V_Q`W# zg~#y=pL~3B^p(nI&p{ghLPW5?n+0z^9iUq)ldHQvi$<EEgXU0!+~dDfA1n+#!es}H z$;3%4s4OMZ^kLZaxT;l&#41Mj%e#_Hh8o1+BEJ><p=kNMlvf|oBjumJpmwfnc5cY_ z?cf<02#RP4Fi>gGAwU{1T1t-&huG_=%jf*n5fhlA4?yI+Jsd_mJ}Ac1cyIHc@^h}) zwaqKVs_9XACMGLl>m=0$E?olBBZwjpN!``06D;4rjp7iTqV|-BJc!)FF{KkEq2jr! zg~}Tq_@ol=+y_%uptHrrldcQ(=2O#3G*L+@14eJ0WYMzBs;@Z=gDCURAk&wf3Qf3r z1yJ@1$V}15F=-koXsaJM>!uKIG%8Bp0?pAZcf>F1zf=`?<F%KaF=*aBz#26Owr&E$ z@8V(1#?T^g=oD`21`S-nd~qP5QA$D=@Dn2$`9zNtW3Gy|0q2X8?Mp&CwMy(u^p-6i z)AfqEqgMn?mJU-V{k>61M&npoHCLMOE>)s7)kLzlIR?ueE_1q)C^9e_adDD4m1kJm zo=!%M?{i;LlakC)3C%-G&{F&N5H&&(V#rYgZhpd5k3QX2HU{QFIgX@FgSpyCB|U>R zZyoBxMlDGcTIz%;%Nc}=FPH8g9H%vsF(&itRp{&ndv~1+{J`*K7}nyeXCnV}uX7hm zMG)i&Y)S&V$ZlM2LRQyz5jm^$RTq?vkwEMgQfb*EmZeb?#acnxQY0=hI%D>gDHJkZ zA!eAO42ZKr$i5xaZmL6G#G*#b_Via_%49O=xH54ak+TSUY_G^jO6LM9l+B&`xBG#r znQjc`Hf8#NDu+TCSvZIfQm8)Mr1J^y@g`CH^=xnVR=suOu+MkBXftgBg_==p7^NJJ ze>1t>IS5SU?vIo(p@7DLO(BOcV*ujGf*0H<Ql{vBqE&<N*Du7oC4W_K<m0C=ix{SG zC?bsonF;I2xoTAO1&d~q1yS2WOwy?|sc{`60Y2&pE(v>;=30BCUmBaPMWXVct(wEt zHwACquZ&HYDzEOsm4c-~K#5r?V*z&x=X(iJ6a=jd6aE!oDL4_<JPv?rSe&cWokK5A zwqZ$VJE%x1PGbx>+#%1uY#6(oSDZ>ptwNouCs1#3PI^>8Izds$)D1@4Z4xc_;-MIs z5*Rr8X6$+Z)%2vqB_@qw;}B!VZgHMY05Q$=eX^2QY|dZ}hOCvaBwkpYngvMGI~6P_ zO*J&!UU-oE+f1hf-|D!YQl~JZ4H`R4ai;)}-uk4%j)?b;6y3$Z?fHRIO8LUs1vPJr z52K-s2%IcI$Bi6{wg{fd+OU(F6!$L{WxRXCU%A1S)Rns%_PTA{sz6cZw55BMk*Wwd zKU?}@u-q9XTj))_f?W7cF->EFwHwT>YA}%R(!XV%c{Kuc*TpPwEshQ9_gnKRVl?KS zjbJU^Dzpz?u*LqitZl|-=IzYTn|MMlUO@!v<S3_on!ua74R%gvx>+|#f^nO;zj}n& z1#40hfHz|`%z-zzA;qd2{jYBf+_TNW+x47?19epKug;LI0p^JcZ~G}C+?8pyUYOl| z;x3C#0cLNhv71x^agAF7X9cC{i^T^5M5XEH4OJ`xbYcx=go1y5=cJK*Qms^~B8Tl? z<A{^*KYLxQ5w!Z=wN<Sb6vrX}CE;S#oyq7A6Si_#iLK8qb~961AA1IrgcBK-XIJYE zHz0<r?p*B~Gq>rEM&!)+#Fy?q^sx#Bs_<C!0O>K$D1r2)q_S~NMzUoBiqk~!NF{>M zZA)8<z$SiZS59!21l`p-xX2(HMKhB&1F}zx8(V3hbdV(nLeOF%YEiBvD%f3~FD{|5 zMy>5L*(fK5KFSfwKl`l{!)0Sv1|kRD0F!eArBG1~y;$juULAq81{Cj4qzaS+|2Ci? z9%JQb%HdfEN#qDm0Fk~^!e!eh5tC;gn&WRW?kfX%UrY=-bZA84rB*)iRyhyd6UVek zaHOgx8hHx0T?4#?lNs9Ssec@_dd~AA*6VpwD<%DdN+li{B1%uVsxZ$+mYA^DmR4;g zsXFT>OQ*xCkd0U_d+D8st2sjHjbY=-fiKU5Pl1T_QDib2<FLbo%V4#a_A3^PfUb~@ zyO~bDweTWI<`Bo0-a<i5_q9Sffpt4Zz<O3=nX-J;$~&+_dC+Ap3b#D8LbpL$2G;;2 zUlpKsRY4LGwRD*ZXHwlt;{5r*Zcf+XN%dsu^SsnJo#UF$15AOgtl<3EVhNK3E_JSo zI7`<~_}->{y<fF8OMbDn9Gh3R6wy_6pj)x9)RIk!ogf3TWU-MKq!u1_K06b0;Fe`N zb4Q`owJ}{JHjmQ@2j!X78%Y9(y>=gm!k^NVBAlA{w~TRWN#5{AQ&0Lx&9Mvbr){IP zyZbc%&dwAY_14wewUw1pg8i0_^#$<0DZ&mcn@{}p`3U_*J!PGpb9Z3kk%L*)jeFiL zFo%epa^@vtS3Cq$Sl<&%W=!l$F%S!Sg%~|h^B*yKR8+U|bl}1DPc3n+l3i;ZU<mQV zS^<R9(h9FYy!g5cWs{q@<@X9u1$vIN7Bcg)8MWy-L{l<AX*udF7}uj(&=rvRj>t~2 z%(<HoBf4V;8BO>(#*ZU{eT5jIA}=v;2$;)1L5zr&DrQ9YPLYR0X2^0Xs|g@?*%0ba zQa(AR@~Kwq*OPKPzX@eWL@Te(bpE;zX1S+5AVCJ`b*QXNFE|92?c8+w$3bDbzGIXu zpYAHg4$Hz`*@ZSR)_!}lU|Od~Q${}|9liuhPiw3e6CUdl%0r7x-Qnu0O&M&0F^oV< z`l>xCbiA)_Z@2UeM%P)md$e}u&fNK_$vjYm3_3rl=22pW>z~M#PCwxXx>_pkzu?}$ zv<EilXY>=psDfW9)Xu13y;twbx<_u8rvO)*g{?Y8Hb+<g#6JDm7pK^~^lDjOU={HE zHl_CEN^Z1V@JsA+nl&wNdBvE&yn&afm3BiBx}Y{g&~U$d$0)}VpyDU=8_rxe;>$j_ zQHQ?1zH|gaZb%&S@M+An*S~yf0o+<y(Yj_Wy#@xzYZ1-MuIPi60;M^b*s(3$e*`hI z>fI;?@i7G!M9)%raO~JzWO=p7*g(c`SlG_7qdIqo=@^;rD2n26WFDG6kk17!pI`@= zlwVAvd+`&>RIW~{_=tY|#oydEydNH|KoC`<$X`s4*Q$|B9EMk5u43dGV*nv|yy40O zsvLW$7|a8q`t>uxiI~6oea1n~9>~qd261h0?PF6Fp@!zh%lsOrt)B2~DSUynw8ql@ zru%@rXyICX$Z@4gE-tgD<`OO?x_S)^Y@#TYA89kC(k7!SBc3e|;4h(Y*MHq{W72M> zE(WwAF(Lv)R0x6{dD~D^t_x=$OcO&G<p5@Fz!u*JkxTJFGXbrW{G$9I0>uCQ@Q8do ztL1T_iUFI|Ca5iR+@bwAy7%!Uv6Q>@K-%)e(T<y;OQ0>{FFIRO1ReUgyWF6y{rmG{ zk)(>BMe#!7G*E#DEgU&Ox;ir`@BG{gPt|)-{I>%9ElA73o^A&m6QHP7oo2U6tU$Gs z!k>-}JM&uvgO)QUp<b1p(1PArRI|*1@B}Geu{A>aINZG!k<=nbuT!xjGT196W*>VF zqSo?U-@1C?#dv0ysJsnm!_XYsK~<*ev=+*a)?%b}WILNZs4#3nt;owIowJ$R8eSGv ztfT;!_xxIvqEHsojF~vUKi=@CNc48~`)+-6&HC;j)A9QscKVbOlBzpqH(MqlAOO`s z-H6i?D0mLt9CLpgLtFil4!m+@aoUBH06?+W9jot{(Az{P>1z+ojQuBOx_g8|cE|sE z4(Z&9HX-Z%UbS51%%KwoQt~gzP!0^uVV03H&gB2Xe`L-E`k%q5f0InHF#k&mA=|%B zjs4GH6x)BFAp4hScDDb)ABOG!g+I(cSNT5zqyEJo=09_({}jgl|D70({a=CJzd<VY z|EiCS&ZZOgDB|x?^(uWO11ZhxsjFp2rQ%9jbqsB!$_IoKKoSB-Mo0w!i}mA&+7oT3 zlLg?ZhSareQfkCVA_;@pm#3$!^K-tw(<9pl&ha}{sC1QR8MeqH{Bf%Bg%Zj{dJm<v zQc<OS&-mhCs>Dg_$JR>{dTX8zsU32A^4Zx0GFvIljMJ^bE<{V+up%21ss<v?PPv?% zZz=$<7sB+Ox104?^V*$k67#=udfB7`2%Z%WN*mf8qn^qo1r`YnRm$^6i3o?wnfATh z#6f)<4P^^b$5qX`uS(HU()oL^$t^cevnC&{?7qIwFeiT`)m)jr_ea%lbFxWfQ)CPU zV(|I9ynPeCs5w7(qEBl0_l62693mHBg*DXyzSh-%(f!ir10&bnJSBT+)H{by1|^A} zsOiw3PPacyjOah>)^zAssl5)4otPidYwgi8*X_F3`SMG9J%Zj3_^DqfO}Ksn;^|tr z39ilpuBUAppV4(^-7=5qpKxHFXLa<ZTQ+Ou$&Nxx>05}}w~Vqrti8iEB$x@2NJTRU zM;@e#VZSI%{VnNS0el4B#)1B(-#P_EB4D!?R8GlSw1sYDR!BexRl`P1csYMODgMcp zvB}y%HH@BEED`HELp{P%d!x)Kiu2T>5=E3J{F@A|+O3mj4c&Gy(@ZB;;$ka*yTnvI zk%CJLKHqWvM3*9{IWOdZPOO&8H$0V!Npv3_E`=R?0{=K1dTiDHAwmE4U|AwO)`O96 zg-7#~Czm$H)LV_X{Bq0nFrCXsrv|Y`{jofn<GC=hgABB}qvunAKJ!GxME{^rYpJOq zU}l-Kn#YEmM!!RB(jb}A<?GnzDyml6UXdu|*K@%eXc8__DW*AM-Q5FXi#jm3HH2Iw z71<$VgAu{g1wb#dhu`kse5H|Rnr6OV-i&MEb?gG3(Var^{6}EXIu#ZL-2xB|6boX= zA;H|k4fWEtU|{2KulBHKw&6f!nd(-@i_fW>Q<ke;nCOfvjd&#nNzh!52_;0ib&Wte zCq$c=gSL5U43m50WoG3%9E)8kl!5@z_CY7KOc>F$%Aybr&H+1KMX0XOfmnf^V7#N5 z{Jn#vT3-{8CJ<2XW}8q6X-N$J&E@fdRShD!1dm15YfQ6M-Mm33RF}B%cV#G(@v!gf zl)puT3gL$EkH3mt<v4?yA1-!f5=JGJ#?bgv_j<ehbj<hG)skC;pr6$3lx8VB>O(zD zXJzBkJqYXvdj{ghfi1uezXz_LX!eTg;he=5dn>v+*XI3xDX(P@_aObGa`-jV5b<Tt zivpGi@isH5UGlN2l~Xumf=a{X>9Lu6Woto?9I}K-1z5h#oEG1wTVq!1NuP;0%gU!P z!XawdS(nIH7}^6qxQ0aiFAlwj%$B=fUH~Uo!hKliZsL(s6D;-!!J)vfb~^OaUW?`T z_kD>CVhL*jM9{PHH(KVz$9%Vvs->#Q*01N9cAmHNAeN&RO!CXJbWF4HRVTP$W*{rZ zd7Kuaggn)%1g5y&EDQYj0PU6>W0Bmc)sbeZM8+iy`w6Y9t?GQk9bZjtUzYXl8nbLk zLhHi7E%qd;T%JrS>%NTROw&wKe!nr=mXF@9+XBC=p!UWDZ_yenQ<GA?v80&0*&%T2 zJQvSN0kI~ihJ?d->dhAyfmiVjB^8PLE8_m#OH3JE?2GhDYFuiWc!o;~YHg<`eEmW2 zi%0&}J6kcc7fJ}`yCiVaT;qqSs~z&Molge|4FVo@Du%Bit9M^fV(ZO)Iz-uPVIJN! z<LWaY#X7oGav`X;cXuQycGC5j9~$K};)*L)kdjmlwv83X7EoTZi_0q%86w_3F30;} zF;b^-V_JhYEIx~{dlUn-vRA2*k#~kH{Y1Gl<{TF<x}n}B2K{18qJEM=*pn!L81Ife z%5`<XXgN<Z{e-+g$_}mJ#!zVIfN254(PmI+v=NjVMT|23F8q~|pXos6#f6>~(v)?N zk73~HR5SX4O5p%;YSAZQWiA1t*pf(qCmIP5eHLwpk^#ZdM38Xjh!O#vvN)1;5(MGo zxY`=AMc~h_n)yFL@!ZoZ0Id}ja+5^DJ)>IfS|ix$lT_0aSdawJynSOj`3)I`7L!DV zH|o~e8S6Sos4x@6>l0)hf}mFK9n>Tw%{E!50LMhzM%Oj=5HC3IR#vm~r{t&PdtG|& zf<hz{6|M}rwD1d&JL&Cy*z-jdNW%di+xesK)Yc5=uJisb6{8;@#_WSlo^#zbRU%Z( zRD5VRb<J9_*hQeWo^)1mGznyPkCb5VLpG~E@W$X)i(18xC^&qBk6*X$A?w`oY7{7A zZqrrCWUm`vNfM%}sgUp(y|17iLv*E&$8le4s&QH)h#Ffn<gW0gLSIgBI2pK`4f0{4 zC&Rbis264ys9<PF7XTNDtYTCoZxb-AQQe`N&vTBnpm52?)2`;aE&-U%rT5{El!6;; zw;G>3luwn9<qcAoV5&4|HWFGk?uO&=E6fqKW>2S}Nvz2QRQRMWg<<~UIQ=ky1fXnX zfXO30<fpvMbq2)cgncCVBTm8qVuiT!a5Oe3+zt}40`Ob5V5Gw>Xkb1D@h(cg5cug> zxqLp9L-4ioSWXevaSZUvKdF{}Gt0JC{f4-u_;d=Z%HS?j1nbagTkNB4XbIeZnW1|u z@Rxzk=J7cK=47LPY)X(&)ntd;Tk8TUH#c1k@<=dUWaGuc5km3VP$H?`+MRZ7k*%6r z`7|2hqwR+%`Vk^_myBQXGtpaj6atJpmh>?WkLSH4#NS9eAXm#$D=bgvmp`(P6LMC> z?Z+YAHl{S~K{x!wj0!n-i&1x7LeX9tG+Rkob4gLPbASSu9jrrjccp09_N?ud1-vXh znTIXi8Sm>KZoGZ~QB0jUQ^PZ9wO$V2EPCA(+iz|j+qpR&51S~owtw2ZIH0RHPUcEH zyESV1m7Pk{G6T|e^th}PbgNJs!3CS2^+0b|5pHXSJl0o`yStiaXA=*FEm`{92o)EP zdKlbeHn)0>&yc_|s79luplY=&3%pLuXsyN|RRw9#f+L=~(2D3DRg*Sbw9g#luCHIc z96keSvEVwF$hzE9^Eye@jW317{ay)*^QNq&D<sEs#SBR6>+YD^Nj{Q83Ct!Inb1z{ z;H=o}rhWaAKLjV)${RvP<=<~UJRtA0;tV?|E!~q0SjB$27(`pd=vNMt;1*5VqYNsr z^@-XG7k`30Q|MQQ5I2rS;t5vR*Ej@`{~3w-7_h?35uRkwpXxl4|4qVPA(qQ-T^E^& zV5OP{CK>f0bIWZ@;UrY%!=;xpi3(SuR9V6~*b&mTi{$EK;HnzMq2m5*Smx(bR8m2X zvZ;n~k6O*=fbCqZ%Ft;YE=GeY1BW^_3ccDAheo9M_FFjfz)^nLV-?_`C2Qo+hKQt- z?!_r-N0K2NUmL6=qC^w_yGgDGcbNEF?9b1iqP$^|`~=3b*_hW{9mD*0iXSa0F%Hj8 zzL4b{QXMlDmdjT?g3zEAYUJUg)Ic8ZR=zHLej_r)=PB&64y6H+i(6OtE!yKL7c3@W zn=l&Sy~e@aX7+qw#I&RPMG!4@ox=51QySe(7KN-KGR<%x91nTN1DO-x$`By9sHVCH zIbsYIPXa&12dev<UDAxQf217>C`j^Q+tTym)djF=EO-HTZX4FJZq=FGo&vE08;mlp zmbOnUuO2Dl64{x+QvLBh*z=gMKxYNn;^MH^b@M52!LpQWy*C&z*oH`Ny^Rj%oMB6f zvI!>x2r<(A`kKLb_9g0F9pkY8+WG#Z|CMdQgg?*1n-5?tcU;l0LBF3;uN1)r#<QTC zaIwuK@GQKjPu&wvq9f^}Kwl4hZvc!%Q3MHSxttY1Tp&S&!H^$K5)P2K@b-^-Jl1zl z(mx;;4-)zWLDIiX7DJ*@1Wo{n#-2f<(L|A85I+(Jg3_y09!dg<=LjMR;@4yaBiW^y z55e2k8a8!qFa^SqKn^rj&x7a}?|<m|Q`!&yQQAHJQQGC}B;fmyY3x|!>ns#``iY|V zA;b0Z^3V{6U&~Aae(Di2AVD>j^MMDaM7GJUX}AyuG{!jcO|~5&*}J0!pW)Iy1w<X# z$Rh|}cVS=WvgaBh&wQSiW?uU=5igEB!PvcBGB|_AKC_4e%1y?DM^2rR)({c5vaqKR zG$M+M6wd}i&fK$||07Q<O9(lLixjA8B4v;GJxDA^06CDrEb_-3;y5zRo<t4=!+7jJ zzQKIh=pzU;_AoNe0`VP)d#2!f&?)54f#>ma;GIDJ9C+kE2OdwnV9;V5RL+4niu_#| zu?G=Ak*e@%j6{V2qqx$~*;x}+903po#+yT>u|`p`7l-Fh)wVgL2pahWpwfRJrVJX1 zCW=avP#Qs34#W2?>x+_jZK1GrE$W#&pYmeFUfu8fKv(M;m@`Awb>jn(n7Fr24Fsw; ziYg}`+FEhgXpkYtyG_C%W&IMT#v1qOQK*%``x%b$MpKbeg&@S-HLQ0aEBN^{1l>M5 z`2vMQfmOa+9?gdmjv3#VA-|;z>uS662i@E8CrR~xRB!yVgZoE=%KRhW_;*DD`~S|5 z=6}*Kv;Pl%H2-WF{~xM1{<+HkuMP8mvYq)~>p1>H@9~f6t^ZJU{O6G^_J1V}|K274 z7yW6Pbv6^QTM>Wf)~0n8wV_(Gq<tQ#k!m^~sGuZQS3d-1gVS+Z&+jLoj3*!H_$+w7 zwjQzgfq^lnLAOiUmWw!r-H7SSv10-3hUy334r2HQOAIu~4HzT)Am0q6wi@Ctiz<zU zewe1HDPl&}O;u1-F5bNm{_^=Ogb?-knaaYZ`{S`yWj|mpsTmxp{<(Z8+{jHooB4f1 z_NDFdd3p!mYKT+pis}QROh_4s@n(j3ma>20&j*b+P*rwkh20bEp(kk(7goqY+l)|- zLKK8RPaue8zHjtgqxt<!hTzy{5##=O9B+~+&qn@E1&@Ata;j_0r(ZG4=r*p;OB0*< z7%TM$;K93CGJ&V!u!A^ZELkS<7L9%X8lL6Tf8JF^jWQOlNsXEJ1OG74Fag8NxjA1G zJW@Xzo{am%)1AS2q)GEC?-oW<Po&)M8;3hhQr>|sqKtwWZ!7zepVmAU`{z+*@w;CK z`KeC-YC_I{$Bg;pI{Rrw#KMk-@hLX<8o67xNWsl?`;yzA*P*?rfq2xB{8A0@NE0K5 zpFS1-GI+}T=o(0{bF11O2$lFYWz!oD2vssaf&>niyx=ADgo1cmWbXXlVGVy4f=>3% z$M+~-9NWo#lQdwdFOXdT;Yt*iy%y=3oNEm|)VOiub_F{iZTjXjEZLx6v~Mu64Js0< zFL4>ma3GWc)iX(5vyb8sZX7^NR8i8IQehyD1BIvpk&d{H<RNM-M3fR?QD{=z61mq7 z?*_|4P&C;z9imM&E4Cg6ET2KLfub^xXjlnby%7Yj$4ZWRfrrSso>PJa4kNl6LNi{B zli;`2id&GPt)iG~Hn^C)Rx?=NHlb3pC!T70-HL-Mwnukg8Cz{3&H3J+gXb_cRCemT zNn41dW;Na|isfz&oR+>(bIF*ehdLg7_P%q(X1Hk%g_)xZ2`?Hk6kFVB1?!Xn<cUc# ztEG%&MHmsab#YORI*)EWAN~T(VTZ98<P24q?Kr%Js)a|V??InNV3Ak~FClO|QDYG$ zRW~GDQScjt^WO3>k&F#OXPyZPu40fQIqeJ2so+rzFksdMK(O&S#;U5s4ZJTcPlvpx zUp}jOCf1i(yVG|rq!)R}9UDnGIU|bMW@?$tFaUib@s<RqMBn6+Nob<80GW#N_=X(i zzQYb%0HxFIf+a}~4Ivbt+fI{G;;>M;>a#CNNv8oZC?81D1_C=umoUOZpnYv^3rH%Q z5l!~t^(dSp4hu*T`)#B%)A##BC2It5Oaxz{nGF5DpV#0^m^S|IpM&|&ZoSrT<6eOi zBJ{t2n(yj;?3J$9RE|2LQYFZijYJZf0D6q`Rw^Pw5LAv`!8GJUU|4dj9y_9-P&Tf! z5a{&SKY7i#RE8{K0_{#^Z=zKBV!B<RPaq>O*Mh7v%wB)>rcBCZMkqoS<OTQl*vZ9i zKAzGi8v<3mj&)%-pClT(m^Zwob`BPPT{yVaZ;0$;yjlxco3LS8Rg_U_mlv2f<k^~9 zHb6BOS}a$j=4tC4t4O*1G<(?0>lmZuc?&qY<mV^%j2_B>rHa>Grx*pKZA}Q;^g3T# zHii~V@U+9#4V(oJ^`0KY5n@LLEi)U;@KWwf%@DF8sH_j-KBgkckSPd~@|v}VdGxd^ z7VtL7cwHi6qwTS&I_(EHT;i)Lv}1|;t~{6;jpO1Q${XN}%aZD=Yl#`ex3VM{-f}W4 zUVf15Q3w(@B}{3CvpB)@9~fp~*mlSennN0Fsx`c*LOocO<W9p&6X#6X427YV@FcR; zTh-VszheEif^6`r3sF__eJxbh=Dj9kWl{2v)tjuNIidEDxJp%jr?mTY<zZNjH1iFq z)DDa&CmLO3bYSyTx$2Iu`^67L)9O4xuJdS(hE-J$#H?roST^MpyhJxnZp~Qm4p(%G z)vCMweuhGXic@CZBZZxFrwi1V0=lHKdDSGllbF+<aFJnvo++tj&gY1AWf00MWyV0( zGZ!vS8EwctDASa4vPJ{ZSAZ4Pb^U1s-&^=IDxMEVeB8Hj*zLN925(|N!F_)Fkb56g z5NQG3Hv`wUOfEt$qhLS1pOO<CL)!il)CN{mDq32g7xB<Sw19LmV{W1}omi@6EUlU< z<T#bmKXb$Qu+|ZSgQ@mEArc-{MV^xa^xu2QNqRs}a+1EV8|MvW*Fvmd5TR}$Ws_nE zs~)~`Z<HM4@sDecq}scmmet5lO46+BgLWZ^`FuW<DjwFdMI(7SBH4^zl9kN^M#-%F zJ~X7(&V!sd`xz(t6+v%p(>H2`M8i}5shN>6JN=4FeRw~plu|R~(d^4{<(e~sT(#?| z(r9ae9<tG(Br^FNWLnJBl##T&5@&9PITe$g%mxsLxPnb1mB^#9#V&qFq^1(#8-#hc z`!L!947)Hwq?rV!qX0z#+?(Vqqj;LTV@NGV+jCZ(L)^_`B^&9GzcB^*`_IZ$+|D@~ zcu=AH<g%l0f>ypeyatJl0VPuQPNiFmjmG3Fe(WUhlJ7^<L4eHT{4dw$Bu9xt38Bw- zA6va$*3;N%tMa+mo|MV>CJ2*W-NvnJ_t`VR&a1qFx)52Bc}5`u^|CxzTv|iBb774? z^>BS4-6D%+Dz_?Qi>I5gUUv=M^2kN<O<v4xj8zjB!tyCzhLt_7b~nYn#_s;+g`u|{ zMGPs=5?&>>ws?6i5AuKf)(_Ut=c?EyuTP5H5Sn!+d?l-LcX0kp4qEIQDLJosfat|X zg03|;VTbUSzxmYv5mY8QZN5aHMf%+a#3Yp)Pdz<t_gKdnFKt!F!xnmNR?N=RX}Sva zXAWqA@Uf&xF<u2L2q`!ztRCIH^bxV56chQC8|eI=68dR*fy#yQVP(7)(-U(ZGKn;z zFrdVVLr}mdKOxytrEr;y8Uh1zbMUq4CAuLhL7)P#dEi9FU;eICA4oxYsoZjEb0}Es z+Zni!e*Rhr+&{&U!1PZ!$pSf9oi;M@jZQ?@?TfasRfrA|pzxJzdyxhQD5?L2Z60@W zAW;*7u^=CZvVWK2QgNzL-J20HdbNz>h|VOIbVPwLoqAZ?8}FuV46Fz&@R6=kFj=u- zB55Y8`a=W|MoDw&c2e^xSwrfAD5|GNdZOPanOuFes^Bu6DcgOo$HrBSbBR1%nqj@2 z8Whdx{ym)k1c^DPL6)c_B)H32y$e|y!1RLgn016;Xn8_ekYYlDR;gC77dl4#pD#|M zt}w(y`(-q$Ue$D2KFxiW!SZ}wc^`-_s3nSv%rW}$qL*%-gnmiWn3X0Mpy=&_D~z|n z6Az!F5f*M(GfG82=*k{l&>68L*WkN5<*mi|_;xuXq3QhMX@Ko+&aMR8U&R1i?)}WY zL-r!D@ru%Kq$4qpkPJNPM<o3-tXPzEj4wFgxKUBe2#CWvNqY?bxmWb`akYJX#*%AX zd5hCANL^19w6^l4%tx}IZnl#o&yAWo1a)9gi~^Q_J^svO4Yr9CTE?_k$3&f|4js5x zDIMP;4tsKCp)3HMBU>)X)0i0{I6$G-ck3;44D6LYWulRGGi3q`T59HhFXW#O=BaHH z>h@K0dwC!|0SX*SMm?gX1#59LAjBR(y6$`&(d4{Qw%mcdXDYQ)015o!g%|}>!3n_0 z2cb0MkC;MjT8YQIH<=hVf|q?;2};l4g%YbNmk{KrfD*H!1eSo4RVx4@(QTCBP68we z#egmo+Cp07W7TcJ5O5m5&^iowlY=36rvN<SXC<Iy<65l8qeEB&N=QWlfCR8w2v$3D zIS|1ol~Q!G62CAp@`M>c$POmNmEuvpQq?2Z2NfC3AC)K24`H><c9GNsln=cm;4Lh~ z1<BSM)CoJ&j7e5dEQq^T>I7I|&Z3lJrUL9vstJ0k0tqn~zeU0wb(uK=5p<zS6`=h3 zqE)jJc)7bitwwXo$+JvBen*MSUvZ>oaZubV{!rQS=2Z*gOvx=E`Ww8XpuZ+YF@upr zP%F;|NEn6*+lTI$r0QG;tOIv(X;mD?9FOXdIC7cFfu&`9i&CtZWWF^ijF<9VG)&Kd z5U<5!>==R0+JjfAj5@Zs))&+LlecO~X$<0ZUsKSUD#XhvUpxoW=??0Ze~l8+Gm9+s z04Ex~jQ@)g1=30ksu`qwcmVM0q|YjMM$T$H;_ICJ_(XnrAv>3!bs8>7Pjjww$fHJ{ z?(ExyUP7GA*NgJ^$Je>3v#Q{2>C;odj{K|8+v(5C70{D>d)}*Z2hz8z;OznqxNmbw zXG@+qdGbm$9M-<f3<j1g$`dfkZwr>%Ja<T^pb0*E>^L*DCm$<10$r`SPL^Dae{9w9 zvlX{VuELx*<u^Wnq~{4a#aVM7u2e2STo;law1l3KI$%QT0y5xu{<|+|QC7!O7x#Gm z6N$is@-H9)P7qF^zd_&4?4jtL&-$;Uq1c^IIT}mlYCI84SK!TLGb(^GDM?x`O0ewn zmU}0D(u-FZI%^+Gyi%zcw<bFJl>2z9F;j)(ZUv7CQjnwCju^8RbHz{Re&N`W@@O2x z<1~WqH(yC*i+8S#TRm2$t?S>|-C(oe7W#QBFwsXWV)h&K$8tP^V*xjEb6B$PUDox| z5pvyHHDDCi3Dn$hsB#v@vo3yD!3ep_W%Wo;eqW2-0Ef(NUQs80WI+|x-2#`wUod5J zkqaoP>dt)jJkYM^S#hO~6Q3d+`ul}v!rSSQN}&XYg+kkEMKXQWeckg|yx8hIB_}EV zkE_L2zAMG!Qe7Up6)k4EMl^fR7LnM|tP2&nSD<xySVI;do|M12nuZZsbi%|yyrzBp z`@ANh=1+@Veq)9itGGtf3i5A5GYYkahn{!?ik-pMnF!DUBkkM`>v1o`k;2iH{Xya0 z@jc>jofFpmF16DwZ4aw#aq-c}<z&njV)=811s#*^VlR1eb_J!gp-i0hSOrYnSCBW& zyOn^-k!9{-a)yyY>h_2dyP{~y8=#SQ$;RU6RYLhJ!>wFEOzjDG+CT2!R8bY?f>$~a z@3;uSimQwTBnfhnY4YZ_-{=1X;-2V8ozfEFfT^)=@UX-9>;%Xjk&lY#mD_cmHFO4U zoVYc5x7p`@xpp<YiRY+BtQ#|<vE$tWtWingbhU+<wL}%OtYUguGMXg`wPF>V*$mpr z<{tFk<ZpGPMTlIjHazb&gA*fG9Z^W<@KjFgN0M!Cu?t%?iT08dnC#DLZIaeiSM;rF z0OIp|u<f&{vmiJ55GN9o(L6(}9wjF1*Kw`wa0ahBH~C7Xk%eg1;M|!i<JI}|3mJ{$ z5!eO`_?H&>DEZAF(6z)NOh^=U4z7z{&@$gYLvkUoOX)CEM?*Qd20Hk}4sK&|z8gYq zQgjZ-Z(PvF0?$9`JOJRfZB7k94W20M)cWRP68PVTt0O!S+Os_RJR8W@FAj9}`kI&; zm0}_5cJih%&hoX;6lS>*hFsEdO<M6x<~bv`_3fF@HRqhwlH8PPR~Uo++Fme=5&6g# zE^eEZ8ozUr?UkYcUehF8_Gc8aiiQ0VML(Smn_i9Z8L78mN5`*Bo}xN$8P_`A4p#pB z7Nw3+_o_Qsd)z~ZLAX8Rc*ZPkdI#<q_2Oy1b=i6}KD^nW+NE_^`rFjr$j|TS^*1c= z)LN?^PCSU2{A);QRJ$4pX2w7yAj@3Actfd;j}Anxc`VoQVmIbltfwADiPq<M*qZVt z`dk@BO)Umycr9WheQQ#C*Z$qX!~<jUlg-92rvFIM`!}=GmH`)W6y{VG4nIGhJK5t^ z8Dg0@{xR$c<D{I$w$-GDTHfuk&gy^T?H!{l>(+JAR4TS@+qP}nwvCEy+fFLB?WAJc zuGq=R_qDb5TI=rb+`ZSn=iEQD%|6EHqmR+rY;E=j@AEp={#5>6Pzg#$B22k4@oY^4 z)H-%LZwzN~i>>$lF*gDn*vH5Gt4ywXdSA_a9xs3SSZ>Y=B~AUt>z?-)21?Lh(O-WL zGh$+5{xj_859*15^}h-;`YZbDKZP0n$p!tp^w&R^0{x#2FZvf>2t6wv1Kr;UyRVoc zwy)9Oim(2e+xjB9{>59u0P%Gt9p2YFea&J1n)8Pz<j>JR`%M0YQ2Uo&!2je~p=W|% z`0};?{buxd^elf>8pZmD3+L~2;NST)w!h8$uS%mDwXAK|hu}Y^bbEcsWy~h*Qm)J~ z@R~AEgtZCdet4BeY*S@jNERh{xsE~lzdiEQx7Y8-J06agu|Q=x@7FW7GuBT}K|Rxd zuzaAEJZVy3t42jXW<0&Rq4IWzXB3h<n|C?k-d4(#D-C{aKHL)qRb8)-RJ+U7234=} zeTOT$FPd3VuNk!$u1^o%@Ao!LT(xkUAKN3Ic&`_Dce`~4&EuJ!-xxuZLD2QkSf~9o z2bWy$+K*|b#xZhi_9R0epCbaot6^Id<Cwx|7zN0cy;?}<lO)pff1ylzlSn0zn5zhD zqwgtal?rJOsPe{Bk=m%3XCoPpf>jy|X$<|kS(tnwP%-cD_&m*7+c4?$eXm)mj(L1l zksQfHPs@tzFAEGbE{LzQ12i!~0RK4cf8a|<WxgKUz5Tp=XM0%Nd+An*jcUi&;AeQ< zIOzBAUGC7pebm%KtoGTV-K^|dIlr1nU#3LAp9I#e+uYC+dKkah=)VX~o{4aJtJyij zafN-Iynnwmk+kffmy}g7^xy2>jX?V#FhGQpjyNo^h_4&n@M=c9xqW!@`voD@irFca zce>_jrZP~Ga}G<0qFuj%&HC!<VzRiDLF;c!Uo3=TIZ<L2uT<7zc3Nnb&Q@Jl=y$C` zQO1DWi@X`9PV6qTn9@2}xr0jV6VQE02(HWbG&ZWUI;e%sn%Z@5?+C9R@Esv9&3Dl) zL`?#@W{m`h3TrzB$J}2@!jMWiM2u?n{^G^%x0$#GKx`&i*>W||3`7)gxe=eCMK_(n z);FLUj$y47e6+J?^j0$<)cYx3;VlsTykmR~?}DU-C3YTyekAY4;4XFeaJ5yonirGy z)}TRIU9~J$@wfwgjh$azH)4oL#EybMKlNH!+Oi6WggLc)HwA=9Q|u3X5;O7`{T;RS zL8x0IoIA#TOMx=;n|GsqL&~`A-&<R|VBU+;%%Q1`!&@Bj4}##;#f8(XEZoE)b#qWr z0>e-va*w6uOzkzHze}o|7oP#tUm!eh=Zf+po<gXxfx}a<mF_&aW?|>aau0t}GcZh1 zey=Jh-xw#QVpDflAy&@I(z?-b<SNP`HKbT``!RfbG-;<btwD-Z=ai-t{Na?sZ<oYM ziHeM>7PH9wPz*S;_{EE4^12i|4R|;u`#B;17JvsQm|f)E>T>o2`{@t}1MfQ2a9Y=M znD&}>aq{v#Oic0laJPBub0Xhg1d2N-5(LvMip^4x5Of*HSL7-%O|HSgwT=7HwJS=C z(!<`I<%6lqYi6g$IBR};dV-KDCMs-FgJP%DO41{OOHxVu7JKFi8!aCQn4EI81XDDL zUP#A}Q_xH_rN9Ns6%;(lZ~Y11!?nTFDo0N=hiDm?Xtbw07)bLrG78QrWLqVJERA?M z1-I!cIwRwmixa`!MSfc;H!Y*FBR-6Ut5vX}mb_A9>P2d<nLA&)n?Wdp=PRakXW7ZO zr}l&ZB(T6wE#tMfw%7c`aHRIcXV~Fn9Dmrxydl>{rx(E)K7`}Q$AZ&wtp9V=Yoq{~ zj0XmS>5<xyXK*0`N)g($K4NQ%oKnI90Ts2C)5I3UHRilGj+KQWfyQ;((oRJR!_l>- z62phU@lh-iEy$mN9TUu!F}s6N9K7jG42oB3Q&4Er>@6BMn_PyE{Q*ay){Y@7ie$zW z-M&WA7#_P#_NpwpJH)KA9RIh|kjpqq5XIb#XA9C8{1co*E<+Zi34<H@3_=OIDP!?( ze5|uKWL%%(V~8zs1*vZj0B3@E={lw2pvZ^65eynE`cLTe=>x{v_aZP0nuT8{eZnuL zx&+Cxf2=5VmBj+w%&j1vhn0E-NH^KUlGj)+Vh(UVumw0trTNu7X~VRpjvcj0%+~QR zLpqjnu{lVibEzxK9tguWy|IDriS^10;5e^BaZ9_XOy~JPG(OJPvX*joPvwo~TA2GO z#e@pEci&&vUwzd^rYzHqBW0}lJxJ_Mbr4U(uV=wn;`}sK*vuP}rl71ffKE+C<P!4V z#{P(Gu`>Sbg_!0*)0wmgm5lh2v_!`+Rq8Nr3GUv*6$W#=c%Eho0Y7<Z#n_HpD;A2n zJe39$v*~Affh}cI&r~*tu%GObijNJ$&H-<Rk#80*JEvcNe}7W51|hqj;Yc(LAv3~8 zV>vsCZVQ>Ja-t){8Uf&2qIF^4z8Pd6iT^r;f3w9ko(ExirulQ|P4%R--?!E#`zHcp z;pgKMEdzW7v%|~7`2B@Xeb@8C2djpA*z+q7KS|FIJXg^MaWl>z;sSFi`1~w@@(68) zKLtB)%cYcp4w}X|bL@oNg3cQ@o@+?@I-SF!)UP|a<1wL&hvU~}!v!}3tnzQj4ceiU zn6Ak$+#8k`6xsI4o)l70Oxg3%9a^O#HrSPl2LVwD{S?76AM0sziU&zUv&PLsLb3SY zF4==P?G!iz4KDHK3~C0!)veL(#Uk3BvHkivG_dk(%e^(EdaWATdK%aQxkQbpipD~} zfu}Wqa$2DE)lOx5O3fgfq~uW*TTE@)wRz8swKe1jI??|iodPY_F++~=tkZ6vz=*)I z>7eWml9ksg04fjf03EEGIvilWxm-RMm~<Bw2tqAsj<X<xR0=rY?@)bwEX|uX`kgip z`D&Di2D~j&kWW2+H0hw#rBxuOxmQR+D4jQet`;JqLsB?ED)pO+$I7cV)bl${Zhz)R zQk)b^va9@NAPF#})x>gQEtQLe4Ey9$o!PIi)=)|mgwATVq?YU9;Szb|r`a<0%X3n} z1hq8dI(JVe*&5hSPmvf9$b4j}tR;*j<PrnG1JD2~<hs0c>n(>A)v+GJGcv&*^{0H& zKby%^H=ya+0rMt&`E-7%f{R$dfNdAIVfxqp?1h>8IpSuoRY5URYBW;g2;*7Y5HW}l z*E80{>8s>0%g~)cTuuJ~RV%G^%MQ2}8f`|2&%gaD>WJiBY{8LEK+@`ngT?HAAACoz zn(K7~g$0q4`*TMrTqB8uQ`j#bki(^KJ;^?t#hH1W9FWhFZ07VV1K0FfIE~m{ULW<M zK)94@dwzy~`^1NTFsssFLI*4L1eF80l|R=G@WFq}p}U5-3$ey=B9fzn;s%m4An%9l z5h^syIP!B4(}YEI56NpvFD((#LxlPzo+GzGF_OzIM<(J8Ato~DGb~Sl^9`;(yIt%j zT4zDd=ZJE0;j(w}5pNw9IwIL#&}qu|99?i{;#nMg#Gl68KB>aF7BSTyEjH9lb*&t~ zcI-X1x;yW=HLglEr;<ZqhVNe=Gditt2@_>})2)x%dgK#VI|xo6R?lJ0SQszN?yNz< zww-!uEl<@8j;$%=F8B@=VBB|mdHbVtECRpU)s^=aDn31Zr)9bn_Rk$F6@`yVcrX;P z0s>!#GLk=%LS-^;K0eHMyrtXY8>kb4;lx@tq0C1{i00A6hgt`S32`0|05c4E?{Yk| zTgG}W&4j@g(KTJVn6-)9t7Wt$S)Ak=JMOQlSxO)M0*oIDZHrQm=*fjMt_peT1L+DG zl#r)GeN}QwWn8xaGJ2o@x}Q@R@Qpb<-P#4%U~}C9F-D@BKboOT0}Cl73z2Snt@J1? zd)yI4GIl;b4zE`~`w|03Nw(61@lAc!>rb%q469R?aB(_O*K^};E_XhDweb%VZVI56 z?z|m*Bx~>dAT$wWt!<W<AIvBBCJ@W>rI-!?Bp(ZAqs+F*&Lbb4`3=~He)%9k0wy-s zhAAj?81yk=COeUn9KpzskA(SwrwdS1_8!HMzm;kIZl77{pG!EW^<Wa?%zPyKof#ik z$TBvsDhTp2K3UFArB)7ziRF29rE(O?jf3?kT|x#x+Wot~<6=S`e)JKjSY?A`aKm7J zPf5rL@KOoV6kY`JzJP|_ID9}`w-gLRL#onz9xB*Gr|4$ou&m2ZiuwMqq@C(bK+Yg% z2At2UM94KDNHc%rYEeBCBLN0Y&??R6wgt%?u<N6+u;Qv2S7iawTQ+{%w0Cq*Eoqv7 zGk_RX4;79noOkAB<pA?~VGWe?l8cY0mac;=XD|5~pl}T!lF7cHIoqZx8Tspl?+;a` zY2<XXenq%jf{$^YqED&oW3tocHE%a@gVrnluPx^`U_<p3D}`IYBP_Bjg)Zb0=*hAG zPG62J+XN7SR&}9M9CZSlRh)?vBwRpS8;tV=n^ZgGBP?~ku=vRIW@94P*vGWw6MisR z+GB_?CG<yHs~VQGBsoy7De00$()tta0jN<6Rq}!;+E~de4y+?7E8Y>&JEkl+#!T0I zo`rpNS}Ur@DS+q$N8r!wNH$`=iJ8D0Y0)1+F2Ke{Y%k;YuhB1tE;gPgy`(}d#-q@< z<bV^hqTvZ7{TK3eKNJdc8y`Mp)bnGI{35xcu*gg2tgJx13u9y3;s8VBUfqV>;{-5F z)C=L_vhfr`TOtU9LYwdu)G|vVkRXQ7iVq2~HC<^Hw!P>y;N{wK6D*N)__XAZ?9?CO zVx!iEzy!g)fG`*x67&Np7<T*U;_}S`iqqf{NEJwsO8NsQ=^uL34i5DD?riZr;i*{q zr@#?O%wYxa3AnXW`Oe~}_{awIO@hF&pe2C?@Cosy?6%Tb^G^!>v*9H`CA;-BHdelg zQ``HwtQ`mt<is5#fkU8s46PoGL?bb`v_&?w`#Rc#7|L&VlpU_)wNI<ti-~X%!T|s1 zm{2P9dH;QXd8qsN+K!$WRAH638`(yOvI1JL`}K5=HdGGN--7k1wr|vo&M>*r(OrVM zE!Nhd#*-HhP7LBEb@5Qd`zOMM-+mQDI;l|g)rZf|_?dI5j%n?HCGVEOqnnpZg$fgE z44(LD{P{#~JrYGo5kk(cFCA(?cMbbCIxac&262&YB2*9nE1p(V={SKc$oyeu;|6fe z`$4H(VUC8eVexXZNOtZ}b<kX-dZx>H-df+iS@M6#q6Q$f^<dnQM|?d3zgsqCHPcfp zTfyNn)A}IkFW(GTIk$!H<rME7{eYaV(ERv#YtNNKpf+Ifc_<j{xY9!2rWmb{eE^Zp z+BW+?<?zn9hex=T<AA&v2$$C3oVt3Jk$&xh6Q%$)UGN>Dv4_%SU-$?R!UDMOq_8~6 zA=e|v%%H5cTK)#7PXTdU+D7!R{V{&A&iwTCCv~2Q=_OQHQtxQ<8$Kiraf13g2`6gG z+qqu!@W#Aix;tu2%KLr)_(i5t_0^RU!m0q}k=bUDmf1c}s%x#<$^pw%t(HKR;Vw@i zW-eXSlM~7%;dATm3m;8{B^4GizaqSl5N>hd@NIH*%QiRm2phM)6fD3Bz{u*%(ypU4 zA^7;>vTy@q6?YHU()xEgl}9g6RDD$Rj=($L8aMM)b^48rj!+?S|LFG?t%H^%Q*s@r zfCoCJ0&tgTpDLK+P<fMifge73aN#Lm=_I-&xm9luaw`-^;AS{47x?VYHM^{9{Q>UD zb-2vG+S#bC3pztLweqeRy>Q+UVILiI)2;}GO9)-eET|o2A76BoecF~bX1!M?%QV;b zIx$uX5xkRqVs+|Ie_OLMi3Al_D7wmhMlU~vxXNx<`OvWJ!i`<!uUuM%;BJx$#aLWQ zx8v$SG;6fSJau}X5%8SjTy5G&$?B?Xv(LEB*Nd`#oNtOeRd`1c@b4T1J;AV<H3=x< z)s;UeY$1zn*{kNd_vX8k5=NQmVhgr}ysnA9`bbACK!`2f0|VtE!cB%cc~Wjq25%yU zfhNAKAx)l{i-F9EH?tGzSL>r>u;OcX9aDXq0A&Y_GSymJx#}3Xou4>zn?QoS*BJjk z?Z~K0icwWOEfGcfV;F-|_!uy37sOT5tGN!wH|e$chtDf77AKF@U(jZMGBAJ8GE7Vi z|Ku9`d-2u(E(828Xfw9IQAq#D{P~me`8R2^KkxE)n&|&5ZHD(BjMu+XVRU#5Z2v)^ z{WJYV&-6u%{e#(J|GMtaDyctgWArRv=l&f1L81M_MD{iK-(-NZ{gt7N?Z0U#tJ2W4 zC1OYRsn*rkkrSocys4u3;g*Vy5{OpiGKT;QB_LhQxE5Q;coGRv_0WBrRTopCb#YpL zDI{MwH8_0q;ypb4LC}S&D->2OVJ_dS!DT(DI~d^Bj#6bssqO3wg<EBAq)j2eCbGI1 zdf)kE^3F?~sJ4aTi&(+SbHSq>#@c2xaQgmnc=rCV<9^$*#;X<99dVyCh`X4dSZqDu zTT^yQB#8FVh~i*r?<m_%c^OoN;njw?BzKkJ3QBAP^W^)&u^w8URHV??Dq&$9R25}a zTi{UDU~Jk$9TvBUQlu1|+>;KnsMNH^=v5dcNvWv{EJ!h;yA{50`|xpXbKB90aOUft zC62JT>nV|{Ad_5-E$+m>dzD`b>_HwCrNoFZIJU8F4HH5plMGYN4qfYflv!CcX0G0N zA2<M;aa6M5D{kZm(7O@5t}T<X+GFm_96wb2!Evl&x9&xjyB#K1isJ6aw!gO{O~Zy( z6i~yS&=@>AWK4`wVe7iaX+$A;m&c8)W371-i5=z`B%8V=DzEJ(yB6M1p;1UR*0U57 zV^zx$eh$cpjG(r3p=R*w?B#H{TK38{V;cA2_2Qv@_+swR{Szq(n%sNVW+QhQYn--Y z2<vQ1NFMThk<8)XTnwq?es9Tl^TI}L(+lBLWE2D)S&Lnvk}>nbyPbVNAzHk($K|`; zj5B+qXyu8!vGm5s*f+M@Rgx9A=-?)&S$MdkgQ14ZrC;R|=?<x)z_D3-Z-~Rh5j0WB zd;#V3*Z?HxfHJ8gy}Q!N$p-iKgU9_q_R;TM6meNTSMmsN@r~K6x}1FJbv3+{;2b_3 z7-DBnp<}__C2IUjNex5!yY&u+?^M5P%8D%b>D<LX@sLMTaI1Y@o|`KzShNCp8<UCq zVFT2<se<+Rl@pjit*V)RMLGmSatP+vUm74lBZyu{0yl@s1BMmntF=btvc#I{5R}xG zBtE;9^W~Q@(Xf%sEWc4Pu*aHF+z^v6h8+@<)C?xs#Y=t7*j+vhRgttR`uaPW=R4xd z_q(A9r%EVIL7R=nM*Vi)9&(}kF;LVvu!*)OM6-q&;TzW3rldfgYef*Q%$31a7E{eX z7{;V>?mRM=sDMdue2!C_d_*~jMoJkJ%u7o?+83$?S+k;~!6AH?y+BvI2MFCR;&%Xq zWn@dUE90bY79&6rk|cO}SN6z}sqP=Y3SsgpEsEy}V@}ba$NYnr_QYY92_!@aRAbr# z?aYLSuIpex9-sC^YF4RnK57pSxT7_dS^KQ8>@=2IYm`=h63-s#mzuexUT{oy8JWr@ zffaORT8bOFW$|pwyv{&+*+@$2#MhD3?F9ssodQ#eXzOUYMxqWq>5#7e{25>FMJNQ> znnPIBS`&(ti>QU}0UeV`C8Xg8g+-~NT7&BG^k;4Z>S;k;qsfj!B1l?;$&U5ZXh^2J zO7#6dUMkx}dd#r02|n^Y)p{URtX)-K3b^nmG}_!1d*6~H*c_|<_k)m-^YQ1|m^Lk) zZZ*1F_?c`s(mT!=YV0=3H3P22s8IMtf07EqCNI~GNkd8dcN;pmpBPduxk9N|FL^b& zGj?d#C_Z%SF|k?93e1yX#QMqWw7n$AZF3lgYa+_R>jR5u!b_=iGF5nuW&|va`8|2B zZVoc&mKG~aUOM#j#@(I%+6Kaot*op1Bo1zyULPIat3CoRF5N8~JR63g(Ph=2&(ES0 z^>4#4&QBq-YZ>qFdmlBogsz{sp4l(J`t9Et47jY&rx&f~B+`>_t+0=RKngh5p{a1H zI2$CR_VB+O+7x%Ib7{yDGbjj2-38it?Bl*>R62;hPofvxE3vaQU)fG!R+Y>yD_z>s zU{sY5sPo(27#G}U4xmOZD%x`;g$LFJUX_j6b(pU%Z#gMH)Xltz?b<c?OeT#L96A<% z=ZIVkc!RGJTGlaOjagKb$J(oZ&7{+iEwV@GlF8{WxPQZ`Ek0tWj9$Err@3_8HRGmb zEk5W7=F))mpR#tMYKw7U!ccq1LZ}i0i&9I7<D69_2XvE-Gf|sS+};oGNrMT~r0l8! zrjKD`R1Q~5ZmL;eGCj1-ke_T)nMFgiC^(vMp8oN;Hrao<!|1sEk<9Bye6l%Qzg>du zQ_Ww$t)L~rSkJ&NVzIF^_X8{HSR=8JTORtS#HogwX`$uXrfvG6*s*wwCaA`)S(tK5 z63){%>Zm^8hTBPzE@wIFOI5xuwNOZu%Tl|qpn?T6cW}>N0=f3TQ#=cKYNc~9T9$jM z12fimaDfA0!ZtwyF9gY6F1Bo8S?Am2V843I(7t*iSGvqv81?mh7e2{o^8*Eu5M?z4 zc4sEJ17x-AfHPCZO`D%|=Pby8ris4J>|y=Ma}Yj5y*Q(6VQY5RxRj@w;S4ru*(4d+ zCF9hC3F<rf=63=dA*r5htXX-D1(<RjoGrNx9r9_mZ6$qTLdO)1O<FvxGvTu}N)C)d zD0`ExcPxRObDthqN;sr5Pz{of#o7*%whDmPx4S*2CsND762LT?x)aL?&0yOXd4YUS zT{2k;g~JmYsxRjVL(T9SP#BrvWZ0oN#aZ*S{SlN9)PXD{f{}AiLfMeV7CO6Sz?DVA zb(2=58_kWkxh2kNiAy8$?F^^6)W<#!9J>buR`~EeDGU{7t7jh~qP<}+bEjeLr9m>5 zysuBG5lHX+jIs|<td!qq+7Ec$eyO+%4#h6VjOq3_JQb0vU}qCN^xI-*QzXbv;<HOs z0mxyn5?ER;9S|wUfPqpM#;d@{ATlstXspDx6_$z-NuFJs3kG=6wZ$NSV;iCOhzNa3 zRLgC@BfSn*4d1I3l}dmWR4j`hFv=cKJ{?Dk@`%yrO{61aEK3Y<%idC;L_H556_^$& z)3-vVKnV!&(&yZnfQssKxJa%7%r+lU^=ZraGKPT5j-ghLb6+K6Z}%ex@F+2H;`Ceo z+H`*6by`)ls6ei-raVd#H+Z1M)CduBqonY(Q@IKv))BpFy{}EVENdN9-qaeEQ-@kT zdbi|i>Evr&zX+-HgBYb_=poyX*LgCAWb_4jf()7!76X_1O^ko<gOtusZ+36*OGx`8 zz$eLENoDmSD4i;8r`m<s0ZF9QqytC@VkSa^p5v2bRf>v$v@H#A76PG%FYuHNhnaxF zs!=qgvM3Y5kx&Zwjr=eK*5NZbbU_v8H@54nn`O3MPT?L==T?k#1Fm5H1+{X4!QyEi zAy-hJe@UaANj`*?G+z^_@tm+pAZywY6p-880*R6_LASYN&{bk#sczVmu*Pmp8RObS z+(xh)6WNY%K4xZ(JQ-L}9kA%vKVSeu*hmene6oO}!}<EC!0lLQ?H)s2E6$<(6S#Bl z5HQ9p5ZNtoR&bmP1D)wd(aAL=J3Uv9@m`-^prj`jvLX|NXvwI!dNor){8H{x8Z%q^ zOTBI}3YPTm73$)_eqcs6T$%PDl5jIjJ6T-J$(^;kN=`GIylV|nM-NZDUU}?lE~4l$ z<T0oF?}x8wGQSj`n_V$Gyo7nGJ~m`=>+;R-W(}NWKE^B#KdwGuFAtNW{t83<U3zC= zrTd5U&h|f$-r4?>^#13r`QOD5|JS7Ve*q5vq=7U1&oIQl2=D(dh=PIP3wr!5#X`yb zA42@UR;;l9Rjvd3-!RI5sjzFVL}LykdtX%_cc5W?ZI7)x*<^s+@%kAMs273ULvQ$^ zN<+azuwdB{0`a=jZAZ%vP}CO|<{*brtY-Sl<{lR}Qa+e{6F$>Zo)qb`m#C+&RGznP ztaVVVH4k)u6Eo`8cMLsNQYDt&oMGP9XK1NF^H|?BKWChWRCG|UhN~WOKFT*$njwu> zB%@X;JugUq?4MG8KVBI2vVWedUs9d+Z5^rpNaF1t^XoCfHyXSk+%Q!x&p&k5QI_u2 zNDD%v)$Q<-0r*y%R34!*LR9M=gTPFijuK;#ABuDL=yiR1f2*6Vy>FdH`;*2YtXs$L zVe7VS6qWbm{@`FCLX~z)Xa94g+Q;SGi5952G?9Wrg`Fp`6S7B_rWr%}+v9KZ&XVaX z)jRX<uGWpp&*m3CUbmOKGH)#tTiSOvc~oV>jRjoXM}Y#RD);4wtwZ0Q;TwW_-i_+! z&-GMM!J&w=Ne5f9S<s)ZkC=i1ABX*4$x&A@Ibeqi7pFGv%Ny@*-JLv@u9^bcHt)T> z)y)+L*Qi+ZuI-a!JYdVbTUW4wUE1nh6(pKiZ;ZCGBOG2g`?Gr|zgO2QNnB4_)pk$o zzKPpcxm&wA>-o0xu5aiDa1#O)beP{S&C=ur(-BuR4xjnDeCk`L61w{yF_U^aw3<HO z<D}<(o>%Mh2#>05E(%i$(OAki$Z@DA2U%fS*Woq}A}Q?hN*yEC6oD+N?T|k0@%Uq{ z{K!maKd7j@p@yL}TfF)4TAvI?25tIr@!e1gxyX!A=m4(!QI7D~01MVok4!vsjyoSp z2S^8uCckuYgNWTY92FUgJ?d%CqX;<_PV~1%bC5rrAlvS}ERF?KwY54JvS1wE;qNha zH)INeXt+_N4q}d`JVSvqwUr8Lz^N7?Au+~xT!ZvP=SV=7j?!KaZDi=ZI2a$DE$qt_ zO#h%rc<~07N5zo_?<VMhOp*1f7zG?cj0z^X6QcF0XQq6+ClGbW7dTo$t`e5tPgf{c zG~4;*2sED?eH=pZOR(eI6LAJ%-A0y)X)IGb{Z0-60%|OoXZY6l$tTCtFITEHO|i^N zIKyI80uU2oimB$_7&{zryO8qrJP=Xbt+9*F27vZi;6{|@`CTWWq-UL_;iwQc%+Nsg z=QiEk{BtzuDZn6&{cb&z7TptYWP(}@Vu(CUm4|j}A%)v+cwT$o2}UYOBN7Kf^J84q z#S@f-Ytp1A+ESJ)tm{DPjYBkz<8t@UsA*<STNG1qioOV}LHJvV1LYZZSOp8&nn@Mv zr8+arJ>Ic(@5*b|06mJJN+mXDlL`BGP6QSKiQP(pUl*Cq-@N%vozHQ8S4@F}Fu~!r zzXrH~i)8aaqZukxT17~h+h+AlqWBUmR*F(PK_SBi%3{VE+VC3}-#*0u3Qps0J5Hqu zxC0fC5V6myCJT$qtW#|({(N!NIEq@ES!L8q!GKwuCjeTpij<ly;hl;V(Pk^+{YC)) z^bX7)l{;R-`wa=+R1wghvbcyBe839+=|a9k;RUgW9Z`2e(igCFOnqDZBt|&0F1%&z zceX>1m@qj6en+3s<zePN=!(YZY3M|Kd=As@`93MQtH=%FHv9JzBHl11)4&5e6;v(! z7_jUeYEe{+eZ639<P@=+89K1uk^JukGdjaW(}eNZo4LULbcw<DewIMqrO|2;4oqQp zW^pQF8?WwA{@UpwK$LK>8^`9Ab78-ZF_BAs-L)UIPX^RTF%sFm)es4?%wnlh$cE?E zUqTl!PtXD!OH96N$45EQC_AwZNCQLN@&(POY~e4O`=<&Cn0nYWQcmP#-ogqu^56(X zp=kH_iwZF2aOENWN@nK3p0H9Jb@G$~zO#1~?Y>WWoz(CAZ1^bUBsv^_x4Ct9F3DiM zzKS<p)ip{~dx%%<)y~a)hPE-1(#|!`FZVAXzjb>S4HdIsr2GWS>p*3$>UMt?g}9D< zBMjvH;}8zvr(LMLUWvn|*<B<~r1MZAYfMToSQ&(TdFZO+Y%I<|tGO6PbfmYJQVEV! z=+Sv$LNJ#H>=5@N`r8awKh1h7jub{&LbB9lZ=t>DgWhaNyZEFYED5xe4=l;RJx>%v zzd3g*Tx6iR@HW<dB3G~n9sD^ucPdl-NzaoBtB1I*Rh#oO3cDW;Y)2<o+U;mDgERs@ zaGT-!<TL~Kjp1<<1m`6r3?6L~!4jxb^PDWGJx-@OD=ruJ4-~hEhX_xB!>^xTZUxrD zEPUTrBmu9W;xtQLDRsdSekV1y>*5L-gRVErEp-;WG{@R3xV;$nLkMgl3;b8!;AIp4 zR>5E<p@8$MpLtx88N-r(kx+1Clep9J1_U<FOy!Mt5!38Z5>9}*Mjw*%fS51?RDCR^ z%MWQovP%WQs22ro63e?fjKzTY>sUib)?gca2&rT9Tuma?tp_nl0A*p#B`21f&#g?e z0X{<i6?gf$0HcUf^r_+GDP^d$c}oN6LHB^-;M1D3UnENMTDe!V`Gqm_efZES@_^n3 z->MA}t>TsQbwj)Se&z;Ol*a3W?Ec0n3O%Ln5hE92=Yr|{)ekvtHoSA<?o{0$F*U?( zwLUs}a2ITIb|+Bz+aKpnzBG6{AEL+%?sy91U{|)HJ>y>AnuSnm$5jfz8s7|&MHdBU z33|eI%{gGdH|DOwXDGSBP@4EbMu}3(44nNu>0pAkxD0?q9~ncXUI~xOGaCX(Z3j3G zB6EJY6Qbw}BcZpvm>XYW$;)?hup{AV1ybY;9G`$42v=w5m+<>DTzIJDV<mkac5Qa7 z2owQ|8}ppbq@yj6)7PnJw|l>_8wjRCHg7o%5I{vslxZ=OTJyPB93ktqYMU~?hsN3~ zCa5DgnscG2fk#&(DN=^ZgNz6ePkIJ_0`#rT?ybsda9HzozF&uEixO6+fvVzx_qANa z+(;%a+CxB!vIA$2bX9=TwZ+1qOazO<>wM^>^2-K&>kZ*{1)D)jWBL%pC;&+VR&(-b z_|75pgesz1zjt%xorCEWJyDoE55!vxM=&_O@-K?urpu;n7wfkhZTJl1Mz~qV@(hwZ za!q&UaVd;xi3Cnd=Z~|4u*dG@*?!g6oJsx4Cz4n6Kyjfi1~v1U?z3qUs@eA^`5Dz> zE(9ckGa{iUvVh0l&tQ(&&!Ich*{|bE>8_oK{6J1%u10kHv{xB5QcRUzev^*Y8chyV zK<)oD7P<{KI<FtF06wfkkFrSM$>)p^0Z9H7Y7J7{Tmc@1G)O9m2uMyW`3;YJyi#4@ z$G-ThNBb&`tqSQm43I+bdON%rxhy+zpNVDd8Tc*OqIqqMl;I7?z9baKq63D9y3U5( zxM?@x5{p(B5<W+pj&1T;m2`S^OTT!0w}7NcVe>qMfUmLILFzaowl_?8;aPlPP5nFH zR&ekmJ_h&_iVF`VSAH}OqGN|+uEl2T4}z&Zw+FZ)k86#c$zZ0kEH{>>ej>QA-!4M9 zK|1guibQ^yo&Ly=a>t5=Mp2fI_*PT~`kBE6M8Swfk~XD7(Uubefr-F^D><UFP8K}E zG$w}M3x^6bP<E|qm+yo<00{@L4a9YdwI6$yI|UQXDP%+0RD6LO2u-H(tnYkI+ar2Y z`;l~T`10tM;W2jr8k+mhh>Q_cLrj6Q0OF8rb>_iyN9eH+L$Uy3#5@CA5O=4bxjHD& zbRz5Gy%T#72{_tDz^6mGiy>jgsBDbH<_u!iieU4^1`-TotQjBSTzt;qiC4j;hO>!| z5WO<j4zJq#S7fHD-0$U&v(`hG3+PalI39@Hv5`G%q$0S8Z{+hfJ2kmPwgzaP{T6?R zF&e<4BxsVX;S{boun)mX>KifY(41@=D&Sx<2t2F{ISTx)eXTa5DT({)3SYgY!H8eB zJV~>)i<VWqA^|5H=czF88%VLXT4I~IifPs=PE1G+U*4?99v3AH`-V`jnAX8x%%}=N z&QHZ#qJ=C?TZFG4&Im*h(%wWyE0{B6WtWY*UlI^*0#zGpTegNI7n0Emv_%y`R^;hU zg<un6>TZ0{C4JB#=pG<g((8AiJ9e7PHoO}=>o8?Bnj=;-5Yb^BvkB0~AfPLNEST;a zH0*L2YceiDbnJRhvAZ%ZE1Jxv?II@7#-k-NNKV^Bf?MZG_l(FmsDfP9!{O&Gbf@oM zfW$w6$RENEBRw<2ALsx0&cObMyz+Os=f9cf$Nrz<3I0Sr|0YQM^Dcj1`TrN8>0jK` z|6k(?*#AmRV*hWdNsSt7(Q6_I-bbpx&H1V0VcIj6Ht>FM*Jc@IMbQF2^dh2Rh3)0x zhcOh@GGd+|o!dTZJ|MmJY0ky_Q$cweIkdI4(Uiz2p9Or*er|_6CD`qhwEoVR{Y>e; z6-+(~`vj+Te@%ZS-@J_-WVlvcdstbFiHFKz`LO&fdUQKEqLLMw?V7_a$gHP2^ofTY z>R;BidACi5#S%zq_OZL~FF+k9tw#B{>HIW05d@4$jYP<6n$IzuN`qqL6=xW_9l;z^ z<9RMo=l#VTrkFf7Qg*g3BTuBBNmLVLwC9b(?fWoZft$kn_O@zu#6?$$G)yn)mOoA5 zeYsO<`?fv2(f6q_kcW$PRUYu{MW#Aj-c-$#bU4_?RhfP|^wBLx+o9*$-ARslYk6Pw ztYo#hAnn|-rb78zN)FjR*F_F{aA~%w+#X~)Uz<^RI^^TCsAPFNo^e`Co*ojEGvL#M z@=^V9LwZqHHdh}h%y4U%R!79>5{Rvl^Z6KYIF6Vc61Ht(Zz@Ffpf@1;>aF;xVhzc# zx|N%~Pj|ea?S5<55^xIm;?1Oi%IWz-^}^qvK<QDbVX|V%F`2+hxm3HnO`~#8i8lE3 z(+r2@zVFBz8ulJ!b+(|6-D$YY_3CPi+KY=Oy2yU6zF1AQ>icw^Pw2$n%05{3WaG&R z=UF?-9++&o>%r9VIscK2aWg2KV}(ca&k@Zc=b>AJ<`>tfo_SDv!w{GPvIMGeC?~CB z$WFK2h!WWpI$DAdm|utc4?d2Siwh`60Y8z*PTgNKP7B1DY01(vPD>F4I#-c~4IAsq zZd=zY+xMQgw6I&cIRKQ{dkGQ42D%w`Huaf9gaP8oKp;dF+JF5Zp-X*q82R#7NU5iO zXJ`z>H#(&f___TCCKiTEQcx)=X1a2EPKjMIBY+W(gmH*?%kwkFb?8URJ_$PF;>A%5 ze0}-1&_iC~5QPPs##Y^wGaNGLiHrM*su+Kd_~BX_h8IR`5-9alAs9!>0Lx!?Rfle5 zE&Dd;bWah$I=!o4loAgi+MQK!hS{V0^_A3&Q}GR|YG87s8DmsBHPx&lb<sMAbpQyO z2B+RDNW-ftv9hJ;i!~zi?PCV!5i&0Kc@4;gh+XB9iTHh+)0F;XXQjwZ3(Cc+qYtmr z%0J^gI`^8v1?Z<nCB>%%ro734A{++nDb%xfO<|-GaM;GE<$+Ly6BQL+nZLjP_zayq zj`>Yd7fFmVhNvABec%$43EV`%7D$G$^r3vsgFNIuA&?eBG^DZHtTK=8VedByFlBR~ zlCf)7vx{biXolLCR8(c%!-+LE=uhl^ns^K>O9>GF4cbxWh23+J$&?;!TdsFmk@=02 z@-(tO9u~kz?*~VHB5VgAwxT0%;imd96d{SilOCD;*?m6hC+ZXPx3vg;4r0ZCv}?+I zr3j9_O0FOeFRC%1jgJo#!F8!o%#yeH62Q{<qU%V5?qen&kM23B*|CMeLnpBatS9UU zGm{A0=_TdhcyaRm$U=SqmnSa`L2|z>1i1Z&=%|6Y#YFgfrelZocpp|%=VI09p&x|j zL~dVQ&;}u;L!I<pv9Y2p6IxTS5(=6}iQ39$U^r^5;W&~M<wLrR8`KCB)X%e;;8o3I z-0X1f2tAO?F~=X3AZgiiV2oSrsZ!Gmw*C18J?P<w!I(-4RgG((H(PE9pbJ8;+3GN& z^?E?k=e`p}t4$FOEUJ-PX!>{GAz{Q)h)$7)K6(v(v+dZvK$f_;*zxca$O_Cw^1UiA zRb=+pjjKgsn~l++eS%fNh)pc5%}Erj)@cT2y3^IYPxpn8mB7}Hxd6apB5uw4c84-x zR^%S+maUmr4AP0b4CviVtLUe+m%Tv%SRDW0F}1DcM=g~7$nm}d5$?(Q{=tA7hzW2w zWEJk?`EACQB=SV>hW3U(1uEZ4C>#|B-fp1@VOW50c~3Ba%|6~XZiou8%%$S1^O^8| zZ(+ye41Fzvo&x<-I=%{)H!BEdxSAhB4u55AAQfbYFm?g7V5O2(99enB#g=+qUf6<M zKEG-J^d@`YG+ad<nc@%XJ-jE56nis+gXGfFRbnCq#ELvN$HgnD3%0XLc8?LZZx=4? zyD_n1eo_)f=ZVKF_>jBv+e7AC;Y_`BV$nhv1m=5pnYZCogKxG}6f^d|<gM`peMSls zWDlmX6<~64h{=|qQ54}}XW}K?)DZBqioqK*qVXSet@-XHVDy^ymS&clpmgA;<#fZ! z&OJRLz~&u_VEJbNnfWNG9Ci-EzL4eL3B=Xn0h*f=pLk3r1(k9ZzpV-wbQvUJRO5Z@ z&7g_wbzv$Kmyp-<D-Y)VSl&v>aoqp_6s+p6Z6)OZs<~*sp5jS11$?&zZALJOBm{0Q zRJQ%KE4>keAoCVR`kWh>!6AUWzt<=+jVi2YRrU(P+QYT&VGqI$m+GmvyJ`wbI1{eq z9&XpMF|TW$T!g!>tP3rXLeuo4xQU(hFdIioj0u{(uX=$`wDH?nxX40N(MiV0%!dUa zOJbos@|340>sg&&vc#U>%aWg@dxVvT5Ua9|1jcPa%^@Dm%!p5~u6Em8<{UnmfJ;GR z&UHWaOZ(ZN^Dhc(w;a^W1C8thym7(}ruSd^x1SGHDm&A5mvZyV?L$o8CAj!$62c&e zxY|Z$N7;t;LMQ=*>M5B5iYPF`-vB`*H4D+sC-GA}d0^DG#RHc!{FSeE4K;DDPMPTi z2vXj=tS7+P@wiUy`)Ge85{8RoHpqdq&gu1=JJqd7Egl0Lf;r)ZhTc5WC*nM|5Ld9* zQqGu_@<=n$AfgPTPhBV=ndc=;^bFVrV4byv&h>~4@gWuBUw*7&%6_-NwQN&lygQ-7 z1khn{Vij~BYT2M#T73jkxB)}7O)IwXtAcNnYJ<sEZLc$f!uHU@vKyW`Q)pn+Y_Mwm zz#2v^uWocfPtF(x0ATjjL9k<uiu@qtN2HlYd1Fk6^TRx4OLBy0(h}POkRp_n?XFA_ zk&u4<d@e`GS3M%gV8>aPzmgxWY@Q)Q%wylE-O=c*V&I5w2qvM?<S|T-fhV*#ikFhV zg$tk<5-q{L6VhH>4(B*imrflvVD8PqxQ6r(r8Px#B@++5wlHn|0h5}RO42`Nn*i9` zhol-GpvBti9@b}ePymJ1Lk<qH^f*jI>6vMxvBeacq2MpXp}ZwRDYM8L?ZYThFL{dD z1XoHWQbwPWQ)S!3W8sMu?815?dH)WMZfJ2+wrT~iJP$TExO4dJQTif+&>Opl$M)1) zaV7|syRWWSS^c%JbJAikHK~@>pO>(DQh@LzlyZJFdN`zWqdbZZr17|&zPC=%(#z6h zf4#}oF^htPMSeb$S~7VXhH5UUxPEpA3`36TqKea@_u=Zn;L0-25P-SWdeVW`43^j| zsg2djx7PPWx;iv%c97=)0ebtGK^?a4IOHW-BARF4ZOO+yXpw2~5?j4E=^Z4izxbn3 z8%FxLPOe)%C8&XKi!~nb(X&mmFywG7o`=%bS(M&~$C$Yxfhz&A;+}(3A-IdMhJqzy z=f3W4js8SWC_FueyhdzR$G}N9hCqbDStoMTisl$6%RW+7ITMk#AkPg6ozLf)m$ofQ z<gWn5KU62Cf2dCE{{z*D{XeNrf9|gTT|n`FO?CPwq3K_YX8)o_{fBYw{~-$fMcF0e z-%V}*wJ5X}zupA@g%jv#%T_2NcHiUK%HsEzWekLk%`4vEUi~=xH;Ncp9ZeeL6`n`; zH@4#Epqa$%=+bv8l_<oq;@OTt+F&uF4rxCgKWKeIdEJp-g^^#i_dd6)-&2-+kd@ps zZw0;6NQ~tI<8%v+V^zX@J8yDyeM8?yy1VW_E~mRrRtJ+lsc0m89~MkLYAhCCJ+^EX zRX=1u?seaIwO{pPm5M$?We7z;&|ah!w^2U4gdaPSat?`qCVy~-KE5r`+#GQ>M2e;3 zO(6=568f@|(if1G)={aLy`<9>(19iNVU8V7+c84KQ+~dfA{>gHfyLS<@-YSBdw3L< z@OrLQru%{e6-ugcP*3jU@+WtM`*Y3Fg`xk%{rSdlX9^9i9vtCOyK;Fj{IR*dgk7F; zIPTG$kb=|h(rvq_n(g6o%a8NcxS4TSo$UMw>;8NB^6J22;pduiClfDg#KRK%^pXsm zEo&B6wylaLSBH7Z1<w0OAP01i6HR<>C<NI>uSnwT2UOj;OE)GIjx4o0oUEx1us+hW zy9SVX;xjoN1)5?asB(NiQ>SM7A)fRo1_Jj!@j25l_S;O#ymvAX`-Hl*@08)?^xgLL zxo1rb6q^x8>>ZgJQZc`lk|>-m)z$o++gRbUhHj^!xxnW}g<sy0v0UG%5WDd?Pg=*K z?8+-G9pfzVO+6EMejV2a;8i)wF~M+)9-BW^|F551ZLq!++j_ta3p@*+ISoCb!}345 zf6e%J!V0=<dz;4=wm$H9OZVdz@rS~|cF-<5Ud3IpuZrly2P%UVX@hO2@?K@Ym^@`q zW`vqFt9RM9QNlhj7!K!V+^@pPcXq7xNMEYDk%@RFFPZ#at%+m`FoO&R`ra(9&~hQb zh>b-oPIxzo>Q>{Uw!z2X?db?vvI|nBurn5efAdP2LqzKQv?He#6cYTh;!|&ZbY62j zlHojwFv$e=u3CKrV%i<2vbFH)U4hHjVS5_V{!#U6GUwfL(<4(40A0uMV7es4?dDjb zLzu)dCqO1@IS7Q`?-2=Emk;RvwG&XcZI&b}R0%Z|2gXlac2ag_z38gI$8O8V>}DAU zmZtM@^YBlgaPDYXi4U{lc(3F2Ty8w$^jq;=u47o&tY4*>`Fy#Al#+oM%b^c(@TJc7 zJ9P)gWa_q60aVVw0qL97bF;9wrvXvd0h)+taDkxhnj!eA`^}!SJEubrFN%&f*8sLx z(1-|!J@e{ZxyFb;{D`C`EvBp_NtLETyz6JE?KPwlyd1XRGoOC~t~~M|Xom20S*pzV znKh1C;jz{DnW#I_raMv*^LUY&f_G}^r=q&~3)W1mHD%34@cZP_R^wUmrBSTTVmrtl zp^7Om7)`hXD42Sr7{I=-m^{BwK=sitWSvxi-Y`FLxtsVqArXU^-~y2N92lHBx*<SP zB?_kfNu4^wK;*Z(uFD3$05<cVGmZkXcOD_QE!B$-U7t3Zu4C=}V;P;vE4xdl?#(-3 z0b5$!RFBkeKj76-wR)xPzJJzbh?~Y>zqOvY>`RDeKGhHFL}TFRB7_Apo83<KbvVwQ z<6;glymU6N_e%B=e1(JE9(dIIEKYyWe~N{5&L~J%W3YtFZ*>*oh;h-;CYUTOo^i9U z*HeA7d^AK1(268>Oi0Gg#t_7xjXypz0ZmU&n7ig|mTumSRpl)vMJw;E5_Xi_j4j;O zs8Y>zW3sK}?mc5C@>+W+LPfWFwjK$$EsS&-!t+b*r)mTV6EkYIKD6$%at+ULb}Ks$ zBv|<DTKRy*E-O{0ZuHUwZlFcsh0SIxxXma`3{LM4TA3?DAI_^fH1nyq42Jx^b;?Ov z&};R+Uxqy>|9ymzieq<cmle$(5ta)Vx+-Oc0?Y>ClM7<fFU?xBV0jTy1!K6$J`GqL z{l1p^qG&eoLE99x**vm`PWIk|qW40F+Qg0wIh;VISS{_dylPgz5K9={MLfxt(1cEH z^~B3O7i?eX7xthVDTSyU!8+Xy$g+E!D+*ySZ8V<G1_2HoV0HmzBzMIPeCF|s=y&Jn zpU5t`D`=<WZA0`<(lrP6GF8^44Ig0*dtwiE;rt5xVa`(`D2<=o019j8jPs&DazyOm zLJ(%d4%b33ft&!zheSVdnfW@~>#l`3>J<lu0v;{kVETQaDkB339ucKvw8o~o<(i-r zJU_4!lR|{9Vb?B-35O1IEcExspMz0`8a1S!VUEWYtL3$(o86>4_OwFifSc4wv4tc- zDW!HD_S>)4Y(DwoI$LNO_Ni^$l+=Od`Q?*>a$4Y76C>y|6ey?InBv9S)1!(?z8OB6 zyeexdf=zz^y;pf8w$th;f>(~mz;fe4Yry3g*qNH(C>*_qFzRmwM3QV+8xVI5l^p7* z2HS+(6bbmc27Ys({)0&DK%_6V$ub<!jjyr6Kw5EMI-f!easWyeMGaBd3CR4m=JVPj zw@YR$OUE#t3L+fJ9K1Qu7f#Cf>f19ri@~rL*XSOeVDtzy3dAycN2s@&cp3=n>=me( z#)2am8F{QfI%ZKsfX<yhVMY&hLYrETAFF!^PsKA3dL~hdnvmB?ftNsQieO8&?0V>T zp57N}@p`gh8_KF^nIwE<NI9)AGT_m;%?)me8ywI*;;&vh>@%V9P)dclGKzKhM39Og z^6&~d%bdM=!Ld|y@~W+Fur5unXaaTm&I~UE0uK1zAOZ>-$4N3n`u;uK`0R0fVtBuN zW*6{#Pl6Di$ME?3R554Gh;5b-7jpyIjRJ|%X;v>8o+aowTz=;8bs>SGDRTv%0<T-s z7C2d{!M*!&WFa!f%_tV0R&?;;?FnV@otOtNQxw7d3Uk6QDgiEqDN$rO9%kS8&6Y2~ zaj5|AamI!h5r;DY#sXVM@ECmb4eUHVK#98!kqri~eKMeKdY+U&81FY-eGW02O6HHu z9@Y4=1s3RXgfpfr^kd0POeOep(t_{kRuBt*x`m@hth+vHa#+@kSesZ4Ns%QDMS&a7 zd7Z=|+5Tx79LhtNdOJ>)P98f_b-nn-c3o7N>80-Nd4LIflnLMi8=(z$7;K$(vo1T4 zL34mgHfQ)KDf+zF`$8rQP68_x#T>(#H5M3B9J>kd9ZZp`dIbx+C;A<TE`0<@8FZUL zcjzk8s;u?kfR7c$h$o1F*D~v2g|>Zeq{~24s*-e7!ZL-B?7Dq!khgn@)h-UGA=e|b zMqx$<gDHVgPKk!~Gi7v}QBQ$%n}CUU{t2Ce0vxQ7uK6p0Oze@(bjEPB(0kN$@{eCD zLcb>dxg40dGb6!a1lVZU9r)LWCpkWQFAV_^=`14nXy039AQFBJ6EfDWq$~ju>n<YL zC?FE-JIax*Z4>QcM7MQY5|B;_A#u_VQMqI`d48$APR>C)(kUS+iJrcR#yQ!jL?T9W zKVyvZ8?iD0fhs;1nO>dCBYR~%LBV!u)Y%e3k^~tiXGU^`J{v$|pJjRWII`~FO=T&% z&??RK^(-DoQBt{xo`BxPr7-=itPHa<h0b?C`0}JGa@HA`9^tVTF_B~w)J9(V*?thD zhGPqC`lJe6$_hlp5|SHch-^H9Ng8oSVoYoY9Q#j<Edo~(_=eE~<*Vj7!tF{!FSSlT zF=vF^O)n*=`7&HUe>xCoVkC{A$(0X<nGE|K*-9<cRz{HruqD~thgCw8oa)FlqZcV{ zPs((Xwq%Y$&OOINp3KcqFX*c{b?u*;AZ(N;P_We_p4_m}3CGvxC-9M83dR3g*)e>P z!2|cc41&s3q!C1rc^riIoKW6V;+IbHH*ovhaZ$<y(L7S{kXUM!j4E0*O4XP@AZc4v zBPDs&Fx(gz6Zwj)z@kLPECGiCdeG-UN5=F79UPhmC1!|e#1n8#mKhRj4lyqpt))F_ zJq%~1LEIfH(rtx{lZYt1o0`YSdx8=Mj?eaXzrc1;%3H-c*+c_Qo@tRov>`TT(Uq{r zPWDLDc7WbMJ;jj_FEfc^2XrQ$eu93z7xsQ!{dt=ESc*L0nXHJPPE!nk=B(E(MU1Bl z;Zq%DxGa<bR4e17HsO^kEz@se{@q?qa?`TkHZ=e3c*EPX-4>-fOXB*h5UHHx8zC_P z#QY*eQtt7al6{U@V1p&rA!wszf8LCtYc3J&otQX|F}eaXasc_QNkg^S*P2+Pl_e=9 z(7m?R+h(hB)+1x?rqZap;b;Xsl{$reTojf~$Wjs%sPjd-LnO~X<w?lpMajl~7yK{c z-Z{#$HQyG@ux;D6ZQIPSBQk8;ww+<ywr!gkcIT;Er%u)Fy6@fFqhI%5d#t_o`qmnI z>@oH?@tf1-?hULwJGl%4B5YyA4z*xmXH3MNRI~;KFPy=yG$vI)blQRv!rJ|s2R=Jm zdWdx7DN>N)(Oi>2nxS(FQXy(A!|Co(OMJpb0;`K&b-A0nf)rJakRD$spDBvIPXh<# zBjz})Yz&JBiyTkTYg;`pvy3lF@I80}{tKP*9)}_jG@B4ikO}gEt{pkwWQ=!8rIegp zls9)JPq6LA;eb!NTa@6Fgt(VaJ<sLs^{VPLJqR3X@R?##S&-Wl`5bGsT_}P{cVT%< z`jwj+uw6=~5!-aP23f=!vCjbE)$Fo4NI!f+*LsPMZ)0{@t<<q-@7lJ_eRhw?-+LD0 zm=IPCpZaqp#o!@M?gFuKlnn5sTrDdnj#~{-`{f>Fuq$p0Or<iB)!7ANO5mf|c#Mp9 z<<~u!7KtozZ4~#j?)iKC5~P>uzP;dg&@@D1ZL^3zpO{5335+rj^4|FYe^uI=I~wBp zH2Iv}pJxH}=NbyNpf*l`b;r_*%C@=G%nFW*ESAs)(Koj#LHEcahS)swWX!Vs`ve9V zN{O;|ptuE`6N0Eky#(&OBZ9JpM>n-7mzY+>3Yy|0$SIjC6KDH`CCYuezszI6SCN*@ z3uIODNgIn$I-^u#(hljVaxF0sVSr6b3bX*~tnA`F#Xo`w&bBg%^do22AZD6l)7d6O z_1`HDdLM;ZYMjZ=)@N?LMQvqI_9x-nt>^nkCn4NN1dN9<XO2h0rtw@g7qN4#tS)K= zPK~j=2CULz6MW~<ZOHMcciI8qSe|n|b(6`z^$bIc@%bFYfV-tQyx)mw#!-TEO4WO_ zyncS&nreUj-&u$MfX;u&hD_|t|0HAn&*$Zs{)}AuYhI4y?-oxPV<YqLg0`-ZT7Rgc ztPG6!Y#fX__;iZ4j^7=Pf4}ut<NSZD^8cTV`486NkCC{4&87NB5Y+!7Wu577Syg{N z{@+ufnCSn|Hh;4(|66)O(>h_j=|57TPUV>xWZ-u)&L0El!V;P=mRmDML)!LtPhDLi zNfe5S$+BgAWIs)FKx*J6;+7&VTP2_s2MlUgQcPWLw|p+UpNQUz2p{VSAJ@me2K8R( zY~Bf}=hzqd*7D-rQbqcvm2t}iU)iL-PAA27p}vl`-kR|`tj^ohzKEyn`JS87u9Fun zv~12zSnj^|x8FCvu7+X+H4Z*))s@Z0@PnO_05>Oc=uWem?^V(b8*Zw5x5&wuC4|2d zGmQr>NI&cEHM_rwOB2W8QI}sDIyAen-no91sA26+vpuJOvO4h5^ofJ>$%v#zY^bF{ zL*2w=9fc8^Z@i@vnac`qcemWygOjSHdvO7CnFBcE3lSv@)H*CycyIC~uAdoeZ7sr& zWb7Q5X?sGU&XfQ0x2}I4iu~9aUTktN)%4a*?q1e<XFuS9M8cLLCw4f%vnM9PMp>z~ zatp3ps6p9aoisSwewQR;T-ijYNeiSH9kH67tJ8umYw}vJ*s4lzzB75q6H*!HfFvM) z%Sqk>_ad2te=kOYdQUPJWD^uQsJWBS{DCTJPD^HHy{<EO5N)e1FrC1iVQ?{dL5HL1 z+&&x=oJca+Un&k-@+*(vj-dq5INZF%ai%PF!v=tbCLDl=H_b$+@vzB{H_LraFsvq4 zgH_;0=Emfa^x3qtZ0s?K<THQ6k;MI1-f$Qsl;8fvxWAi^H|P8h%%C56!HUL`+sli6 zVM&uYD)FCLibd?xf(j!PuwO&ieF8XZdfG<c<HYT$UrRl_d!arj5@M+GB8|w6FJRsH zAxdT|c8NC-GLYDNl?%iB^6ju;6A^|wR-H$jd3Hv;N#0Gki`dZc{KDhu{a$0vM=%7H z!BIsr<>UnyRvH|RgK64BY@UGCl6y@GtjLv@1|8U^;^hxz^Vyx?YL^fJ`_E-QOorkB zy-SBL7aOBx<9xgp!y0A=G6nr1oU+53cn7tyE~R@mLym+;<qtTfYi?ikgPU{wNEBU< zsl)rQy{BmM7=@pydlmaxE}#u4Q3kVr?nZVVLIRR}&m?>gVq6r!CMDl#jR)pM^5G_x zqcTnve{4X$v%UKGwgw*(-{@h`^es(276J=%=_atmd<Wcnji%}0sJ1I43w>tapQn2# zcbf~(RB{WsNkRk>(kaw>Y*1((aGRP<?wp3>$^B?J49_#w4e(1@4hDz`4-RJ}52KS* zREVEwDe>nZQcIOO`8nKU&zXgS4eNCFcT2@@TGr)<?tUWK)3@HBy`azCemZNuJ9s$* zHJiTP+Y{V(c98<v2gw@OHfRr-dJYS-T(92E=}2v!HcM~8%PVpO(9aeh2Z21t66j<> zJO&r5L@W%u4mHwZmaqtn%(N2q@`55Tdv%4yc0akGRc+^HXlZ4H1z?SRr8*h3Iroyn za6p`WSLKF^aCa54uhDvnh{V%=nCDa?*`1=(L`c0gtSrm!E5_8kbF?^m0z^sijG4=V zH--N=WvBUlfYzTCl6!Fsug$K_DH~~bn!Uu)!W@<$56JCO4?@?1o$`1}GUNc7<0th5 z%r#5@OxdzP%QE6j+gr4sNDC?gn-V?GgOGAXBEiwYkUnmA%3d@*T=a2S8_@!t@@W41 z<ry43B|3oz?2v&Lr|2HV?(~H7xR7XUPf-fID&H_Hr>BS~Qk*`KD5F0VTBwsi*a)4I z!w^!LR3aK$Q#xP_R|!}f3BVTXFrjC0pB*OE2Nqg|JnMjgOBM3AG!+BAjMLZO&k}8b z6P;5u08*C@b<*=_Fm&}AoK)fVQW~36!%#^Bn=%cTzt<f53=3P5yAx~J-8Z)ilMO@w zB^*d%V6P6Inf_bips^evL(pgJz`A61_#Hjy&aNbGxKxm|49Rt(ILt(0A*4z&9HRkf z$3zbWyx_od_*ghk_=S%)Jx36o-%4hl^BURVMn353+a7{qWo#V*G5{GAHsBFL-U^d! z?J>68nq_o2Y`C$PeTN!avh13rU+3P;Yay>`HA4fTzfH8#MX4p3HRo}zF<~FI-%=DV za~+1(5*GmF7&9y!`Rx`*GY1AGArhK7LoZs1Qsy3gh6kO?Z~j-VUp)_(x67$yKi3U< z3AGeJUYAeyXG!d_owzK{u7&F}So&%ND02QvlA~ba11YO;?~9pQ6!o~WWb~A8IU|$R zrVs_bjU&U6RLoYv!;cC@K`CK!Sgzt=Ch-~0!P;QH^rEs-{veZ_LPZVW+5^l+Z6$g7 z=nfjRL=`36%?gTTXAbYxJ*B(xwpFXMbrF=kAX=CW?zU~4@?k_`hA*Mb3r$j0F3ui@ z^(GB!%1w^^AR9i$UBoD&df`Svrm!=dNyzdQ$RVQ_VZk{0p{r}!XISP;g1S$;`x~qK zSPFNX12-$Y6aNzerV82veD@hx&LgJ*WGkI+xiQKLn3wA+T^qhCi5X74FFLqLI*-$` zw8vI2>uuNG#c>jqD(}zT^yBwxC4TN2K-4fLcH|XD=`3K21g2u&^a;a6uze<9Oq#rg zO*0S9nhf*JnTJ@TTr6Kotb-i~N2y>+`)~BQd*2IpWGce(>!%}(8dTWh+kte&CqglF zpy3{Bbj;vtJXY?&-243MNNPB9<}jHnAx!!q{r9Ch*PJcD#2S?I*+D|h*o-JH+3p`z zA|B8C*^0McoX#aB0{v+<Lq(ecWSrKs_!+}7XNKVP^qj(%C7>4|xQYE5O7FK+T>0tT zayyv`z!nN2(oPOAN;wQazt?j4T^EbK#i0a7gA~(&NGF}k+8(SE016X8>h*yePXzHa zm^x{2J|l5q=%2^6Lg1%oSn42;z#S=qpE`DkEu=!D&N*{C#`n<8AfWz)R;knLVH`tv ztB9`g?J$UwFmdKT?})CU?^(F?<d5WJL_JycfJI&yUZ=khRUfxIT1t>U?C!_F=Vs{l zhYIV8j?FPEyZ7Wd?2gw5@Wf(b2_#CeVG$jf<nK}Gq{)3YKB5}9pUQ%+`pmMUAR;A` zW4EG4f1(wD*9&n{Y$-<XWfy@e*O~=JJ#}6SiS;a;v+BD=X4yi>>8!`KEo-!Ty?F8E zQoSCvKN3)QV&`nOespX}q#4Zio3hT}Ny~L);A}peh&+Xr9y^RGAhTM(2q$|om;pJI z=HNDTAjQ>jf1Y4%UleZViEj4oMr&AoYf!hP<prcQjZWw+%;b;^`xQaAVS=Y~9B*Gt z0{n3*TIZ8S+WzB|$5i5!pq=cP8Uvk4o?RR+W(vJHgkjGLu*2TnU0f>HlIOJd6mb2! zXzIRTP9Ps3d$j!!GBnhu{xGX%6;&*hFokbumK91SnM(o;(XayLL78C#XRR8VU?~_> zR;$~>Dj^F)fPJU#0t!jYE5cI1xYNefQ8V0QahA8}%_^%n*TWO%wFVuz`P{Vi$Bp=; z$H$F&6*<`AzKqwhmT{M**2bomr(@!n%L63UzC%S#0hhdqiNiF}L1(ghBW+xJo80HD zw>I-hDw$bjtL@U^hL@e3OH*{?sh|0YJ7UB*yJKiM<#9}67#Ta_NY$KMTOssDXq+e& z^_OsWMzi?V15j5s8&@dWx_`?F6BRtBjgw^ccGILy1eLyYuj1hS*~iqT*o;b`JwO@* zL#F@lG|Wm>RafD3bQ9Rf`wM^3(tG*pt1_pcuUmwfag%8wx0rQ!X`y;|W%m{hWGUIc ziaJ4e7!xEConNIo9{>k&o$G?&Sv4=48@;)2mG^x_098$IfP=0&n``Mk4~!TvNV9wu zl~F)6rk48&I%QuH0YN|W2+<`E)~6PzHS3FuJ-|Jb=3|<bXG^>keRg}UZkC`polCms z$g0F}+9TrhL9AR}#VlRituA0u$lAxYt}e!;nz%lKYPon8(hJ0lT-<BigyYU&UrTZ_ z7iXO?KKs-+8H^DmK(Qqsh#_G;jAxz0J!o#X&WNupVi#v2@3Sw*xof;m@jCT2&J@wS zOqHWVGfd1)q$ZY9sJ?5qX&2v^C?vJXi|S#%%#~(Nmj_kG%zB?@A%I8OX@UkBZnyCO zfr!Zwx|XW09iL1`Rh|<BHf3bEXNHH3TWftyVbzJ5XV!ea6Wfx=A1j~c4LPv{g!^Qe z^lIC11~*jOwlrn9^j*>c;7MR~&N|4(JzQ2B*%ED$t=kkZ?@)LXrp<_u6HzFxIB89u z!)&@VbGi&JVym{uZG&Mwq17w{#xc;n9=Wf;CUkW@Ew|r=jGhz?nwO;ejX#XA$i#D? zM5tgx;%8=W)R@w6T#!WNsq$f7KjrOh&aKAX5#!85N-@^D7lm9srlX1hYyl=+5pAA- z0sU@=O#F8U^G|0d6C*3fALsvuHvg72@+VOGH%3{eKQn;;D!yi-|GORZ9|-e5;MV_4 z*Zz-H{=D=5Cq~)73cCIlPy8>i@?Qk-KhY=Tzi8e}^#6>;{{brh4zB-qpt4fiItHs5 z;j^pTU>z=~DvE8K<f&2|h*>tFSUjDS7awo0Pu{VvQkhJLI18!Sr)xSE`zl^avRq1^ zxwC$7*G@OvZC9S}aQlGokmQp&e{8*L4DS5B-803<KI2JbTRG}e?5!YPXEvG8Rz<B; z_o(^lC8YUUg!b3^^6Jye-d+FE6{(MEqfXbeW4C*U-nH!byh(Ta=vUVEC(+mX?|I6) z>t8vwKW7pJpx-D#+N`_UO1j6d92Om)%`>{cdEoK#g?l;;9G_1ANMiF+fQ)0)(P~H< zQK5>!<x;htePFr$N-ss0+~pHAQTOY2WS0);?!|i}y3{Jmug3C-20gIVJC2xt{ruj} zU1|Gk9S>$)SmKLLLOQgK+!#;3f@UxV`zMBH7-U8mE&kv{Wz+cnY#h}XCh|EYRfOs| zsk@?rn#g=JM0k$%(t5jG8gU^FlXyF^#PClic+GX-h38VdNbtC|x;V-?Vvkgid-&xL zF8kukVO)%3UWbFSzBuEo+|ro$RT<V<+xY&np}Zi-liNH#=R(krVu?mTiv$1)z3%9G zjgX`w>ff{X4o%M%cjK>DdLa)M4uNF6+t=Qno<QfH?=`=rV?GVNrM}Z|5=$ytXk`xW zW07C=$e$PDld*s!$VreAwaB=*J}Wk=(YT&z^g=|vcW8E$_WNklV)%*?UGQrI#srw^ zHKOsEpH$(f^2Wd8_i;3HDdRDWmKpB#{irS8$9C?NhUit)>Pj18A7D|-Nx4#Eu(th1 z9R)2+E@Nmk6ySh?j2^O*@M<Q$O<zCv+_aCZVcHxoF+NImvTC(79`pdGn1fxF0bW_U z)i|n$=-D`*AMVx<Z%XzzH3|jqaIg}dRgr7${HOr{me@JVy<yCmG0Z!oC{LaPZgxye z9W>BGtF+352ITroUF@k1ub`=EE~erHmW~#%vf<~#3MPFl)@Q{jG$=r={<b>+X*Jv9 zt|^=>%N!s=g|4s;_(aBPtu}+oBE4>nP%$DnD+k15U>HCj;cKG3p{@}P|Fs<QyMiVP z{Jx@W+^SaCtR&~xph>e9-gC;wzWMW^_uaS*?U`ro(kOt}v)^tg8ay@4_J+}OVsu9^ zsiCr8jD(+)-nXg=zD^xY++Twh-CWtQG3E20-@xm_KyeteS(WqQL$Nbi@rzC^jOT*o z>1PY`6yT+m-b8^$wR=`mB5aq(Y4$Gaiw7iR-)IDjUS1+L%|AeYjZIWPjq5egvz3gJ zT>yFtN~!x8j8s5NS^$p$IwsZ-py&7-^EXd1&YhHoEB1od<X(ymq2(3?8N@9EuA=#` z1>uNOB<f5ENtB8vWo8Y^GfEW{b}y}6BgxOr+$L4tZOA2yr4ZCrP6FmY?gSvW#L3K# zvgW>v4P}oPlk?|Rg~3Nq2r=6DVLv{fJ>Q(cAljP>gRXabtK%ixq4e`%$=LxOB%D&` z0(OPsjxN?PB>`Xy3I>9a7AuaHRqMwnl8~=5PZsZ5aXAE1Q-L^CC<e0hDY0V6*cDay zb(B)?9l!Mpi;R}>mm&Vh2JD)j6;~`IIx+gXIeXy1F^0g&nqF{MU{>Km=y_03f3Q|a zrqN#K>#vUPb6P}GS!VC^BWoe&hNFTPPO{*jLQLjLLCKmdeQzXCHPh2CkkYVU&RYn~ z=25&ruwqE);fkUYhqf$)n9)j%Z%k=VVblHr9PP104m_g`8Ggz8gQ|kSr<vt7tz#H; zRsrA72|wCow1cdXFl~_8SV841y#p-BA!g(%)m?K-Nww$DeD7{^{Q<x?h8vh;?DP_I zR`aW65rgTge=DDggzPmZr9Xe!V=Si7Y{LD`Z{Os}<h(QriKyONOd{cT#8XV!NG<)# zK~3Y~MGY@+McnZA0gAe}sYA`>TSiCkb)wQ@XjOz(=HOKi@$=LC=TIITH+eeQQt9TX ze!uXKFku9(G^@#x@uM)dRzbjlckEKi!)fbiA<z`7_k}3IC{i6zvh@5E852xCkTvjy zM*;WDUn=bUoBpTiNfRo^?L3XuHa-nr!QtQtEQ@634>hk{a3{6fTt8rq7lP)b$^GgZ ztTXWehsHr#yGD`pXtkTpjX+Wkht%RtwbX8LQC&2^IWw{YTbLP?q(BDL0p+S^DZ|=2 zLJlg+{7-sfCBeG!=OITHnnGa|Bx6r+YuuflEb1KuQd_&wUF#`)r65)WGkTiolZR2P zGN^$iJQ6Ple{_2V(ckbbEzGRe6j6AD2?-s%t||Po7PVgdE?_M+&qU=&<gET3YO+np zR^(odpV^uK05$q2xbQlouKamxX)InYv~5c(#SWem%=7@>XWBbPs~rg6J?0#g--Ad$ zBB1YW_^pLscSWEu1X7*=3SHyWV|zOci?DpJNpYtPNcmL(f}=wB+xqXk^{18%ciz)9 z6Kq}u_6I@ryQ7VL4ktGq?jv+Lulyfi+6wV(%5}J~Q#}A9QSt4tm$XD|!QYm@xmb%Y zf0Ntywf22OUcKVmb2LWfT?K8C3r}O()E{200Ht8-F+{Epa&~D#l)XRsQUVZe@KSh^ z!S7rvR-pZn<)pg)0?S@ssC~SyEi}PN<<dnn_-%7EHR}|Xl&c%TMhcVH<J<U@(Q&5E zl5Y&=qh%!h&Vns~Ua;G0UY^z?01InRwYBaMXGTH3qKnkz2dtZn;$_oYC8AcK({&oO zu#-3+CVz&HqsbDJxbr7Ruttj6GP6@w?m6|mko}pVCh?CN?t$=cFC40=AV9y!(CkXd z5dvqgT(|;zu4{$8<kkf8P7^d4!rm2Ji)i&09|=0#afA%tsgEH>D1~K=j5g@73oeYy zi6bj}0XZW<4J)IqwbNP%T_CK(#dHL(U|~D?8b17ad0coxxb13=9bNLph+65sPcy(0 zWxW!Us1PdIj1Opd`8L{TtEf;d{4|~~VK?%a%D2WQL0B)teMaQbWk!*b7xY&~Xb^@# zC?uq4_T5{pb*3@oW6~acNp55}ed%&A-a=hdiB*AUiPVB;UnXC!zbdT(xiSMS?nC)$ zA@@7k#}_YTaVz5EaZ{!^<6V1vvs%vf$%W~RBPd*p(l@zt1kVGVw)uF$0rF-p*YNn5 zL6?V(h=6eryMkTv^5>#9+6)$a58Y9f*>pCH5(<l~dwdCpD13f3xROeMs9Cx1pn{|v ztWwiu#$N0u=WNzu%4TVeCk@6=lZaBS5^@4kg~Ho~Nda{i6TY2}q=Np6jv+@cpf;Fe zM`RCl8d@;;pFryT5lWL}mi$Rh3I#3ERF9Atlnd%!izMV}du!F@ov$NH?kVF?LBUbv zr)#tp;mdXvrs(j~SxaKeq!A(eN7%_)h4%dCL=L>^s5LAshFbPP?E;2I#jSzSbGYf5 zy{-bG6H51BN_)~4MGk;Gb4uDON>Uq4Ddco8#fqB3Pfu;dHmfwWiwg>vHQ{vh-y1co zWlNJqvS=K0V#CcEsO6-*BBa#{kGfJnKgSPk9=3VX%L<Yw4%al7FuVH>Y*npkesB>` zctn5{VblUkIe~%69)T6<DQUkUWBklk)_yDWj@aWAy;VbA=H(FOM#DP#R$DXJC^rW1 z!!aJVEQ>+agaVYLy#DN|wBq|Py}oHn@yJbPN}UiZ)A1ESKD+16g`Ij{hQ52>=;MRG z<|JXbaFtq$sXb2&#Tc16lphu!m?i+I%vEiEQHro(rE(-*Q$5J`^3VJ<usjpYsDKGs z6e?FkC`Y4eVXq#h9CC7#qVQ61dfqV%YAoK8@CCEHTu*P{Ip+^A#f#|jL+1zkAKa(q zz%vdB<PMasE&%WaSv1-{B$~G?Z=IwuG*cmiDR5?3CX@oFI8{j}j8a$+PRC6B1wZ;U z3Xi0n4J#bR$s;h})}I?cuwj(VMpRkc$yGxIUN63pmjSGMN-;Qzq#IoEVWY8zs$;$J zYE^90ga8m)TNTW(b5)kj^bUvS*Qf5(98XS8`4u{zqUy=oTMNk7PIS(jKuvEL&_ox; zt#H7X#O!#gEeJm9iqS^^6p@+k)eX@{0iW;=ZWsl;VEEA^Iy|3S5a^gBrshBG%&uT* zth)y>^WGF=n8*Hvb0@Vd8Y+$ONV2pbD0-uh9^RG&X=<`lgg(*TZD^{-!c8FpqQxu` z%tUCk(qbbRm~>*=U>@GsCnd14y{hq*<Z-YrwtY7jECln~JR*Oi&toWrAXi7MUur=@ z30-UO4R+Mljmsn*IJAvvCU*|WlR3Pmw`438tnLJEZi;NRU?f)`Z+75TVIB=AUhXwn zS}W;4x{*u$;$jU|K<>;Ox<Nw7q4ijVd;3U+Y8YR!OaL8mrzShP{Wi^~Kl3A1KNUwS z5cUDR4xD_QcSH|e_Q>?t+7UM`P37`<C0pcaQm|4{Dtycs_0;Ojw-)YiQxHFy%yExp zVFgLl3PJOX!q7LD0+4`LpBvhxw03N~Z|e02N0zG2z?C7V`EEuGHV{Un#c~a;b<e;D z%w}lbB2d<b$w9Vw&wk~Ee{yZKZ}VNmydLq8f80V6r;ebx$_pkip;!QFL<b{MLZk|E zIdam;Nm*$6w_EyQrN<#o7WyiT4osFDM)%-bLj86USkhLxan3XKo}RoSRKjAWE{8E4 zTL_(kBtr$ItwD9jlR32U4E38CBsDh!Sgm}{Kl~6eDEfRq>h+0btI4HLtMENGMD;yw z>sv5HLxOT*L_;4C9lHQ#7dm1#VOwGL_39A0Pm~=YVZIDTg(f$p$4{Lq$vDiYetdgy zpG51jO6h0FDhvzoGd}9!v`GXaGmQb_hGOcht#)Oi29-{#>C@s?8+gboJdSiVH1|$~ zcNB3BIS$#piHyJX=w5al!;YAYv=`n_RVX%vO4=Q;z<y=Pd^aDEFk*ObL2CF?k9|aU zrDxT&iWbNej`n*@Kht*#GfXZ0@rmxbH!q(X^TVGE1&B<;$fpN;?s(zThLf*qtZDH# zq5!C)9F8F3BC43Ta|*IC3$v613U##?n#1cH?3=0`#Cc^H34R}};hWdT$>PZ8{00O6 zT-QN}r9_8W4Xdte#bo7dY3N+j(yN9|Jm3`s0PsC^LhAq)-i#{q7Lq^L9d~qh9$#RG zvOVz~-whB#?|Mn^GwYBuGdixvhR1GPU4Qy%lqOw@{BjhAkg|5?{BB2E0bLJ!13w}* z797tSS6*KW+f6{Y98p9es$8zadtj1Uj#FK0A_R@@ht_}=DfiLQpw-9_D#DtE3G57_ zcrGPQ2XOc>!J<h&V79_5yEs0na7VR>CaVyzJP3iMKf;a#n0UUtLOX$t<6ETdWt5hd z>`5J8enb<cr0=i!L;<8Y*960-oR$^w?6|DB=){}plc~~Pu?V74z$fz?%9(Tk%Q^md zQ&0xtO9A*ROzLQUMPYr&m!HDZ8+DVW*`}STH?^kG<j|(P0TL-4&NmLCB$^686_$Q* z$RN<@*7spz$G*(Kvk}ND%ip0s69OUOw75FDLGquj10-Sxa>{ddGm-c^@JrP9aPHpg z`%54+?g_LG72aY5)`2Xe$4|Wy0Re?e%`U1UFX7b(UB`rNGzEVr@_Gv7kZ2W0Kq%=U z2PX9pv2x)S!{JaKz<Qtx*t!}=G%Clfj=Ge}Ldew!5r*5A_@U#?kG3Fw=|6~I8YzMp zS^PM%J1rGKjI)b*nTDzMqN?p_1UlV|w%-!=JZ}KZ)l{FOB<oV#?wSbK8%DH(va6Z* zXhz|Rsi9R>9@=6hiyMOtS&#s!2QOIPi5zNkZBDHpitR1Ss}E%=Zt6#f6|>I8EC{;x zy=hWl9Gbb0UzSR&u`)*V2bjH?FkBmhGG8!}7HcOv&Grt!uh%R4MPg_upyi3a%r*&E zYId?NJdV8u_R<i#S<$}!#(Xtyifd%;C{(cy^*It^>e21h#G%!_r(?8z7TGXW^O{r! zePI9N1Vll4bTc3?Lz~eNpBid9GCxQ|-KcBjlbM;;3Go2|;wwMPiIA0#c^e#tv&1@U zLnHOwNKC4sti+k(f*C&zh_)x?_#{h15==559^9vLm@Y(L-QK+FBs%Y{%&oJWByUkM zFTOiC?wpctv|JUl#*-2kcLdJL&+>Ylu2-YO<WJ8YOO|_%v?{lsC2D7Om3UtId{^Qy z8#@?N1*avx5S|8K>8p8Uw#oI&F<Q`Vsc_iacnkpf5PS3!gsQyQkJG`VYKpQaorbWR zE(Rl~n@3ahBFm?A3}OyX7a^DS4|r?I-FAL>wL858ZBs|rDeG)+K*a7o?{)#zRy`>! zn@0P~{&;71KJ8s^fu$(6dRvj8oC(r|Kl@w!2jyiw-`QvaPWj&w-}{BI5u>|5DH<Ud zp*B2gSu%j}{AcbY*mVLR!S&GcV(zER0BJ-@p+2nc5a-OWd|H1Oh2DmOk?s%VJuC(^ z2U&nMdfxdou6MTmJM!_*aS>*A=6}RNGtvL^Xv=?-d}R20aPmKqkAKi||3mWeZ>#*D zAs_#RR{Ve1qDBw-Ptno;b&uMAmEZP<Y38p~>i<-v_K&#fzmcH-aMS$l=&$hUe`P*@ zTZR6LQ}~nl{O!y6Z`o-oHCJs8T2Q=}e^2`Wq0m<%h$u=iLd0H8S&it^TLZa;2IRI; zud^u8wpyNM3w*M?Bw^F0AgUx<{^+;U(jmds-3ofT(e>8ntB0c=mK%jqWdT`lxWKcT z)YYpewH(W5)@u`^II9@rC^yO)6|FfJdt7|D&ivu*mGYiYv~82z%=B1RYLVP^iTXC| z+?jEmyy-slIauZMJJ8I;jWj*otFLea9|-zR5@I8q2KT{qlqkVCssyFo-&L=3)xK$E z&+DUJr>1Vyeo8~5tYQizA!!72f;^+*NO0VzM1hgw77h(URG)zbRcR0Dy+w@2mbP9r z$p(jHr}|pG%@Qw1^-hP&aJgGOjwSvj3wU4KxYbNFbd13JGEX)(0xdOinTQ<GrZ(NS z8JBh(1zsiYK*QJ(NrysBM3r?Bc#mz|8KnHB5pcZ9Me;|71><5O5xeHO9(SS!)r@|k zL@DU!H=qHSA2q7JKLKl|D;mjbRP#vTt5JU!tF=EqK3H@;KUlDJciXaEUaB;|u1q18 z_-zR%81y@hf$D<EH4*SH-Bt<d%O+_SC2-nRArZ!NcJqqS+GoLs2iBk6$8#*Z>1G{p zG7J#ov`4FEy<};ZJ7mGjlCa*0Rsj_vX_Ey~;w<=`f4fj#{t+r+w$|gcMSJ|j=d81? zxtzB%Yq<>RDr$Af**N1d2)^A8>zB3931D!|3GC#rz2yX0I^zT$?fiQl5h-lTDL*u& zUG|%pW#`226Dbu*&RH$yaN5TuPH#`wzaOY<<kzc%`xJa^B8HUO{CbL%+9CWYN!z_Q z5>Us6b+B2(%y@@M=zV;Udr26;kmr%k2~#k7XDeeSsi~SRXl7YOZ&1y=dU&F`?jUo! zJ@u$!d3GQL1*7gVb00%(0D=S`pi<(>F{M#;ddkN)4Zm@fT$1Q6oX8o$q}Gcd%$T0G zKvK`vyH+9GGByPbV?>`zQkspXuVO7*my8dCW$Pv%VnRj{d=(v25h5{QMh0MeYN15l zWDuj+qy)cA${Otv2AjE@?6pbkIj?#>I%o#x(^R8E;8*PL@Eq#w%6#ctKYPHgZ%M#u z>pLj)AM6NVDMB0TffG4e5Uh;8Z#s*--U^@{mI>ZgD0bxc`q?MkNBAiKk#R7`%A+7M zs3D1OH)aeU+HmM~dssW~O-^3O0plh~bK%f2g^}0FnxZ8Z`t=H#z%#T+<s*2>;?j2X z63y2$Tig)01#ayq6jB7>@gRURx*|L4-w>OSGP*k6B=#1F8{Lr4`So@?9?80E?T{O+ zU|I8o>`8)b>GkmhaFD}LFLUjk5BJNdLT)KX=aNgK{vI$m`sO*d^{t~K)`A+3;eszA zWL?GN^`5*KF1l`4LxVg}zD84U9O5#tkyR!6SW*^03(=z$Fu>M-#W19HS*cw7qSL4z zro?B|1WP16oJVGJ@_Hd|Y&bip)2<%In9(=_ifx^I&gkOh3^v-dWLURm;rqoy#(CVq zjv<{d=74UDqrioT09|w^`I<;Ang}PsqcRcG1x0@Y8XV8CJfH1Jl&PF!9Bul;UJ!52 z!w`R)1;!HntF(T=l?LWfe72a3JQN8W<^gvZ8?0nZ_Q7*)TePHMH@elk*v5)OCb>NE z8LV^9d;x7Lpn&N_E|M-DrC4E6E>vudML?kEGEf$t1FA#An+~SELX8yRRJG#?R)8-Q ztk~;-sA>GXoRy8RPi8vzek+nz7-82)iJVo4DL0SER_Q4d?uf~KY_WWqt>kV6p%n|Z zm;X5!^0Hp9n?~8BO-}Yv#*1OQCEI=P<4+yxul4*jYrW_D8g1?gM7FCuop(+{Zd3Di zx9v%`wp)B;OgKC7DMdD*Pur81b>~?gUMDSj@6D(hbeDSJn4I(dJ^OVz=55Ef2bz;z zDg`Oe?xvMk=Vd5f@G3PkTjHiphMy21DEa-n(dD{GuO|R*NxDJVa^v$<fR>AR)KQR; zrJ~Nvd~+{nxPsTXxaNsJT`|+3a;=}_lln8-cO}|Rmbx#lO`+*hKZAq1w<l~<P8O^# zPvVPQvd~PR08%U?Yp*#$Zn!0yyKNQf3+dXp+%5PnWTuCqtWj(Ni(Q(0wR^!F)yM_Z z%oMHbgllJWes!*)f-OzU)(9w=g4fnUe70!{R0s;p!rfs|-8>Ot3uOfIHkY$jJ6?U0 z)9W?F<J_k@I+tl*w_Q^GeiZPuDf?F8#+#TfpL^K(D6P|wD~oYR7_9>Lrb~HT+LF6{ z_^O+G6h&^mYB2MZPQ9{=T`n&$17NKp06eCa>lLx%idfjC7Al^0k$)rtF4EUo=!PI9 zJ%@MPKKYAHl2f=oXZ*QCw7rH>?5touR_>)lUSq4Y0@0BCOxN?m5%1yNJF%P?7N#5t z_O$9r1Iq2aMfC(zhs3<-yXGLMcf!`hPP=ime(P;Iw#O9(CrZ$Sg^KI%?9-{TC-9L8 zHzYL9kLE_UNA_yqamM@8Gx2#*Wpd{-1bTC{jI&2<!~q5DevT!ML$gjaJRlOt77<;! z9$6Iy!VTs8=IS#GLH-r*)+M*T)GY~*uuT<RqkP)IaS&CvBo*UPve>o{*|##5SAuQ( zj8k9R)emL0Y9uc<|0)Xkpv<Q26L5zNe^K_XBK9)zvov=XKCmcXc@peZIfk;>^F7@} zTM6a{Bd-z0uH}Z_;_Ast0?Z@6_Grd=&&IRZ*VsGW*^FeM)F+$uuU0K=Sm9&$2>s_+ zH_tU+gGyVNUJF}@m3ZJw`Xdslq6wn-@~PTxznjWqUZCx3Ew9erE^ESZ)jNo3c>*rI z=1tn|>NO<BV&Dk!@G(>A#G-T2tRx0wh{<GOT=ILpcm|;XG~<cie_0+9Ly-j0Oh>oW zf-j811wh*(TnvYkxb~?hwCAq@P`yYv`Rg$fVcQly3kdu1w-7RJV_5|F7|+zn^2#S{ z;Mzaxw%Fn?wUhDf;~@N$u|l|VNWce@dSHCvrz`L4>wte!sUgx7ohTpI0Eb=Td?cCx zo{~@tl#wEtfdw<hlVwRENuPFN`^BN@NFyoxJ%ZSL1~MX6lt8cst;RF4S9htEjH_KC z;B0}OO6+N-^?`7Egzs!D4t(+V`ctD|l=J7o4YkejOBy;!9f>piq>X(LW4y0AmI$Z2 z(GgT^A!jO*2eOmGiDRj*+Mlx@{UKSt+oNhuL>!w)C#D5MFIVM{!tn@!tVudE9b3P{ zuB|HW_#1G#ccp!VNMp;R>2&8SpZ}DavT8Ce7uGwv0E5qQHwkS0T#d225Kkabxarzq zF%S#jFbk|A#Q*Zh6D6u>PRl^<rGfNPOxEzT(c`nXV{nQOLcur=s;7Tqfx7twR0&rj zao)N2n8CH9)aC+So?SL}$`U!om<)pkn8V|2Q(lzNyROw^jipI)(xZ8P`O5D5X8dmu z!ryk2f9%K^S=br=VNdui+5ffi{O=-!zxNdXg%JL>ef^&zgnu6R|9{n<@bAa#|Jbwt z>t_64fW*J}6Br=>1M>W*P5Pg~1%H(5{9}{;_Y8vntvx~OH2x2J0-u~8v8+WCB_@n7 zhfWe+cpb@j5q9_qadeWT0&~&KmQI!USp0j9Te2riE(2HwQ(>Z_1}@BMlqeqF>$mN) z<Fm@K+mboa#Y%<t#@F^XCAX!fB=;D&#y9nH%$##>D(fQE=Bv$0$}&(nwzDAj?04_H zwTepVLtdyDk@EWDg)$47;yXneo5lV?F__|rWlFF2#e#0?sxS8<RKC*lAV4r3qo&eG zJw|S{sL?WP?lRJO>o{mPZNB!;^@p6)LH>=hiuyIw{FV|_ChP1&5h$k>tA+&^O0UNg z$;MVmw1|?J>goj`B{oVg{}d8k?>piyO;4u}Q>%zA@AK~D!fonoUXM0c_m4fv4=I|i zVi-Qpj%AC+kUEy1G)zf@+f8#8-JB%&6Y{9ms;kb0;FV017VLQvS<4z*M=%ch4#Zp7 zbXROH69D-dWSa1YD-Fb<KkLf$%Qa0Gfi2u?b-z&8n~aO*me+B3`k+r=B~KNL@RFOK z*f!B%&p*C@*)?uf;?Y(q!zAqvc5m~0NVIiy>X?)#8GakXttRQ#vJP7Z==dOfdziHR zj8uPnl4!f!`W*JbfX=yK-DcXwV{JFqHg~%-`LJldZ|^~Sl4Vf`5Zt_O;;T>#xwOG! zYwZBSYAQnqFQj32Sp>}z(tr^BHs&sv5eOgLarn47`Q5>%UV{cMgC=r!8#lSyW@+^u zQbfzxNds*7<}sH9)n?enuLj}i-J*5-!Yyj@b36?MdZVh8QYBGFHEH%^4Su3qf4TRw zyrPmu0LX5TFC8(14J7njTJ3_d?H6x?_Xz@Cd-_jl)gJ5qv5O{S^%obMAB6DBq?YD~ zporfzxjlD+Twc3YkOoi&SY>9ch(C>+>%Msht5w0LcO3F(6q?Qn+B)e(7JaHT+vVq} zTUnAw%rt03!9`&DLE6F1U?77{kK9oiHx2<s=6p-CH#n$$R>x|>C+dS0a;_Mg3=&jX z!IVwNlb0pV1k1wrm#G8*WUV=k4Z;u2Mb=W!l!#0Hp4bVI9y&Z*3khx6z^t*^6ZGzP z6o#a@4_piKxkT#^Iv`^=m@Ovmv1F{;7ZtucHkVD1d;qh=tWloUJKNQHk+lV~m<H-C z{Hc7P5kN$B(FFk}ZVQC?BLb*%=v8R%7L+`027{xh1L6Q5=G1OP#PM`M;MYO46@=+G z=q&yaI)9$S3sOW6NAR=IlbdsM)14$7&d9h!X-x>6_<OblsEqtkdnm30PA2PqQDi&r zIJ*s#HuEIw^@>r96u{{6`~CYdmF&e4w7xMzxQpGE174N^5ewy1&U$gM&TA3&to~d6 zOd$Ajckzl>b!y@4-6V&I)MF&>bhn{%pjzwmiWz6CW8Bz;Wh560N11se82eJe=Cq(o zbZ5^UhS-2L0FI)Rd0pG{NO-%E%MSV`ku(61;Q{EJ%z$9}>`i$5*(C%C(eKh&b1#bu z*g1I=>&Y75Ic3};KVacuAS9-*a?`-T0~!F5Vy)%j9NnPsGv=DR_GbX|Pvu`1#C1in z+p;XM`$mE26vcasqtyDok?^ZS0w;O@NRq}8kfsLf!@dYi^1_ewdg0!K2k(c^9Hz#8 zX3;P4ZBNXUI(1+u=f7FNr~Tn5oO07zBI%ReORZdGJxZk)u2I7y%c!LlIw|kLY64=A zTEXC4TH|*fmWmP@*T4ocyu6w^IAG^aOGbDJUMGvQkVuilA+4I!!?M**D^$gwc8ouX zgujiMK@f&pf5I3nAt&Z9k2>>V_7Q9Kz{HYbLu`Nli`GsP&6zg9WJgBUWaitJe;{TZ z5+lC^&W&D+XRfi_MFT^UyW)}PIVvXV;ho%@lc^;Fu?^-H7a=Mt>AEDkc$lrm8bb&y z&?pw}fFQ7iG${f7E9#kVe0YvG>eW;a1^7e;^v{}nLcjqf_slUN7>ha7Tt>uQ#HU~+ z<&ivL$5gOi{L}^g*r;FzF|a9s97sr<(BI8bhoxHb6pBG8Fr#~I03#i?bGHTg^R@GL zO=gs=So#&Ed1g=3=-ULsw|aoYs661L=UNw#a08a2M7osCWZjAI9~I>g!hjg&F2Muh zoN3+e8$d^;GwREAC8gm}BO3WL3xUv6`j_yXMrZd#^Z^@Pw@U>Oj>NrxVYgx=ZpADj zee21D9t-xj?^EdUUo6VR<cDP3Lm(qoLK{~us1dbc*bAJ*`N4Swma7$&2`H9$fvL@> z$mZg|(mE8RfQ|}Vg-{r?lojSfnyfAKa4t5Qr@&`%j}Tept<SwO4Q-&GE#kAegKuSd zRg=NpqRfFbz&@8@-|?$eAd(jQOf<kVmmjMygKKC`8y?KyAcsbA6ya7InTf%didA-h z%oI_})tqSza5}<3nVw&US$ABW)d9M|z7W0mte>SCcY%4fC*#L=N6IQ5TVhgUX-1C0 zf-+<U9I6bt18Zwkry&D8Hi)8=d~YuJWp-HXEyXx41zAnbqwhBaNRR9>J|CJMnsT6> znpov2z6D+G-KBuSH|orIld*a!BC^MWBZM%7cH$GcQx9(t1A<L)1##H^X3!c;cEbaT z@Fb_uMj{4i!u#MC2vD&>@v{*$AhX}Df%yah4D3%Tj_zFO)mN*{{Q=pe)TG#I%q^0- z!s$}V7u~z{47mg8b`x?~rM|qNo1Zs7Yu2|qTLrEsm^eK<uECdFu5Fgv*<tg8V6}>Y zp%TbW<?i{CY&Pk~NA0=A><tD<ymhaeJfstGkw>!Kco&$nQI2%RGh8g$FbV4pX>Cj! zJJ)Ty^sdFbTHR7a<4qk=JPVc99=%^4vogL~DH1cXUO1LN_b<z7g7q4B(t|f?Y&(&? znS;OU?(yBc7ByezYORGVK7$=;Z(-j{#HQmuWw5#E7Tj0=E~cvddfTN0NY`H)iP*IA z9`HoWX`h@!`)X<RX!d#ic)hCOV4vh@V)H2RcK5!9XllpP)Ahn=Q|_##jcVN_Z?>q% zHnx{>Lzj`Gj^+ing!L3O1V?&WW`?|))<SYTD=+(2cR<PA2g5=hk$xLM#0m}X^=myU zQ6RJR3z1Kn_h==*WQ8!*xCtF>$n!NdIgm{}9nhMdF#_3KY<;iuzNu(7v`n%vkg-I} z8-BbFd>snNLO}Qo#ayvLZJh!mE*)YZwN9A^Ga=qS{yrYYKs$XNT<&P$WjU~E^fA>h zncUg)s5ks-owrq(WA>3yi~TN;dG?XaEMOaWy?~Sb&^PN+MArWj$wiB>?a_Sd_eV@k zyJia0>ORc(?7aGiFGj(ImWfw%4-P7p<=kS73C2@OJ*2XEq>`_7h||<$8ahT;Ih(Li zuphFW4<ctRX~W#(khFMRcS)iv3l2jvh4f5Jd>GC`2J$$+8-zFTYDPTwCZln1jPJKW zg_~|uCep~!Oe9mybBvP#T+4xq{9Z4aEo4BB-OFG?ZazCNHS{Ta9z1FF7fx^>mf~IX zriSjp#(It!-H^xkfSk}LLNePICZe1w4!el`k@o|R6k$>3FNWnU(31VnRArcHL9iJz zo*E#}u-TbMG#t~cOOhdk)Pu>v+!S*zwZhaK&MCq*smRRHuimgoatOc9p~EuJevxH- zuDX|-r5#Uc;fx3?Q$hdUt@%&B9^HchlP~HTJo_&Ai;#gFpvOP%4*yFj3}rQ^3VbkZ zDDz`F#52x%`co;EPsRh{z(bbk*yPRS$0I~6o&D9NE#j{rm`C{ANSL9XKfVJWF0>a% z7o;a2Q^2nc;8Y;JgR^^k#@n5OPv%qn7m$^CotS-KQvc2o@g{divoT}YWSGpou*lUD zvOt5HoI;)g?3kaOBO@8yfu43&3KHNip!*-0T)Pek(;^=dhc&w*RTWqkR}!FiES+RH zZU)>!)8JsTx5A=U&(nTy1pDJN-r{c<`gDx;q+euYk1Z21P|nw0>5tDrmd6KYgZo?4 zls5&zLm&5j55aqMRP^u=mjv%^gn4-8H`nx%+Iov-K*2Wg(!1VEy~yf-e5Uf4o$hTt zxD?^(SEwnmV;}wn&7!$Ih|J`CBK!!VO4!mUrFlrWP?+<ms(Gi1SXU}eo82iC?xA3g zOKnqFv~3|>TR85v3rTPM%a1xxH{LHMAHT{hSvjvA(y`4Dz6X}aqW=<9H!^F*d}kH8 zGA=+1iP&|Fp`72O=42P1t%J$=f~yT8-~D%3?@!%_iGh{*FRb^cLH)ld2mN<ebcVlc zKmUaF{%%hHpA0+yZI%BsYS3RY3?nl>)9*f?zey&_ZvXvepWjl@Kl(ubRSNoFiA(=Q z>(763wEq^6{({$kLPo~_uCAkFZo46h_%Wr|>j@!>m2u=ny!s8x7G9qZJ)_ZiUr6A$ z>F|~qQ({(ps`f^2bGxoO#-h$6+~|(~H=*_uue?24ZBb|Ph2@?0oh9;CF0JtUV&U#4 z?K^J=Y&S1lMk&R-;-|dNL_KHO9&yDl&an(?ZoWXdsW0(9Vs0O_4p_GBt;_A~pqTkG zST>*TR!{bes_W*bw~X`pn~<A#yj`~A@7TsNIG@2Gq=*>$t~S{~&5(3(dtV?S^mLY< zZIC%a+~SHNQLYv6O26*{iWsPZC}!+GEG585Ar3A;%bLQr{PcDnvbkQr^FwoB_h@r- zQecFX_xD|=`WnF5@E+OV#O{2W`7UXvG!=5Vc}RB;5pwr(_Knq96(U2D%yAdt$*LJg zR6?9ElVp6IJkSipNrMOWmI+*=NT?ksT0TQplVva#F(7ov^Vqg+;6g4nTt6LrRr1A# z5kSxRyh3Gg7GaQwdqFd%K?>DfRnv*53K}d{YQJ--N>ia{sv`c@Ar`#EecE5mpN?c@ z;p%{uic=0$5=qpLH14PZZOY<txUKy~a0XS9p713uJLA7-r~Lg;&|a$}N{6<`%;fIr z5*Iacd$hi&t;D{1f3jeHz{Q}v;<28qenC>ykeQx$b9E}***r`Wx*QF=Y8l*5OV{jU z<03J?n#N^c`*2$6Wx{c0$a^J;$q4!-D18ub(^pORdGnfuiazidI<3r5A>E5Ls6J35 z7Pw<bauaHmyQ6B9=?Y8nHef71!7Wu4TXCI0)r2YVL=-B2Stvh{tmKd24AU!Y-@Ym| z)0dfk7whoP@4`F>CNyNlmHnR28zEe_d<*XyFGrCZ*6p#pA?$_40sm8!%r2&M3%4RI z8C9%hO<kR-%rq|XOb_-o?Z!RTU><pj^Xcan-%foEGneb2d=DP(c`c*p1LSC}rJ@&> zh)HR-$kl``{SFgSCdQ2h@z2+keE>eW0`_uHWj?y!R}DG1RpVD*W2~1+vInX4n8u+m z%E3KT*C}_3^<3j|v+UH1OFmSK0(mFn8&=>9pWL~B`A+vmyMp}GjdH4(O`SxpHjNX1 zhyd)0;%3-c4@d#_M3pG}T;u@p95s{68GogP&EdOLZX_vVR9;Y~DjQ=(eyF5IbFyAn z?G?!uk%*!cLrLj9`No|qOd3{)ff8Mn>6b$!_OlFdT4C|7TAg2kt-wkJXaH!8{Swdb z_iyzD3N>P!yt;v-#NE96#ml3mQ;@}464%*EVOimOUvAm_U~un?2egcQ=$C-V^C4;m zNSUNhK+aD&xZ%xpv3tC0o2!!o+zR8FpwGdh$*b32AKyKm#&1WL?{9~<AJv94&QE^* z+Jl}FTt`q0kdDe>l~GQyj*20~SX~b!?CvoUP^{2BaHzr2#Dad`Sdpb9>DaCxF^$WW z{q=S817o=NXuThZIVfiL=mD<8@+t|8b%maCjBp{wG5=I=^kzs%4U<eEKG7U*&Ick) zNAQ`N6#>#H0K!@GVog=#o`XQHSiZ->?n6*l5x>@Kqn3$K7m|l~rS(VQjCkQ3$DPus z!zJO{Bpif`?maE%ZI&D?o#2n)EG%z-MK3yB1#vt@K8tu}EXpl&!~VjX0Fr10z6eW` z5y`~ZSFmA7w>A`5HF)%gdhx<fR*pjMt%=&^K&CU(ICF0vr*MU@8~qpv%-?4fKj;K# zgL`CMMjM_?NggHDsCE{c!fw1#o9nr?(}d2{46{O+N%H=<Qv4&LV_P))<l}J%V|TJN zM<o8t=kV~u+|SD_hj7a^IOnOprSMaK<qd3D_Lnzm*h2Tnn}$*PcIp!xT=w1vb-ePI z8G;^htGHn1#{NE9fB+DN!&V_e^aS>GVp_1*NI*d;d%Vxs77hbSpw)=vYk+v+?;0TK z^Ht|B0pcmfOGOE~k7eD$vx&4!jiD_X`Hve;0gxH?yVmv2f}n4)d_1M;%y15<i(?m} zbBZ|7Zn=CeE^MMog8U$$+sI;;rMa)qQq!&eCW>SGQaHXwFb2_QZOG%mmW?VAV-*p? z9$#o!ovvA4xqu;6f^~U3E>1S4hVhPX=N^)#kro~$X*~>-*rRLm#JG&l9JP40P2^=` zAd9uzC#sW*n!Hm&Z^&^65(`G9<~Rr}nj5uU2r?%?AS;=bP*L|mrE1E!Gqne^S!u#Y zl;nLZWk_UQziZ}4$wteV&yVnVJh;^QKsl$vc_H|SAT;U}f|Po-#A_G=hGl3_xWHj5 z)-}9Q23gTe`$g0fL`3{Ka$j;IH<{l*?WF4IWv?6thz-SHBJ>rCiq6z|in)Ffp`L74 z!@==Q*ppoF?gL}}QYn415AR$v_?k<*OZBA0;mb0^hgHtT-M-%5)U%3qNr{1lBgL`V z<PZG6XnV`>x|SqM)B=l{nVFecI%3IU28%5Qi<#+&nPoAvWHB={Gg{0{9^E^2yQ*gD z^>od={(HXtW#o>G%s6}HiWPax5&-+a^C-Ne+=VXY%|87l6}B`LZn_{q3s)UU3r`wB z^r5j3_Rlc~={@Ri$1yf%NDm=oA9DZAp8>+NeU7D&{WFn+j=9XR@qMVw?U8auElF3H z?$tje+q@t{58e5ON{H4%xViI33lUP-X}1$%Dqq3&x=6g3ZEH)P#UKX#<)aQwv+OoM z;Vb7u=gUt=bJ{jOtR6P~{_f(3#PwyXzDHBkk7{7VQiP3glU8gW&b*XBmRMZEgmsCu zgEYPj)#~P2(&#Fa2j#jxVv5vg7};i}!M!~jQ>unFi^9Lq+7n-ToNpN*UHw~|?NItV z#EECj*rpk;^w~EN^`BTc(0qk*h6il3wV_j{psNj6cWi_%89rp;`6C|~hU|horeVCg zUHZ3-^ei3|0E+4o`J`~xQic<h5ooIkfSl#Pc>@1K#M!ulrb$#);!)_=r}CLFMx>Oo zIrMYVahGr3AVPVZX@z3T#)eC&)$J7;Oot)XYqLuvQ5sG@T1(B`pG{)orq+zjT9EKp zfVzjn&zg3qH7q*yoffO^(NMr+sesZn<BaFt|9mqfUn-VK=ps=Rhv0sSuxevvi#7Mb zHa!N3B9JA!EbEcQ?#h#sIxmLXicKPS!A_~Bh|k+ISF<rAMmA!UIir(Au~9QMBaXGZ zaln^8)sRG0d5sXmT(Wzs#F6aaHqQigxGr>)iYuM1VH||vBrP#AvAju0bl_22*|q|( zx>T4Qi*-!$?6D?+Du|mE(%`&HWs~I0@(m2=OCQ#<3_wJA*sL%l3^~T|EqUY;Y)7KB zpn7V~ATY*C9m+$DLWtJBP($NK8y67N>sagZ3&_P!aX;E@9LHuWiwu%hq)r*sQICww z<zx&)>!SS3buRWX7f;?lJSb+7Xo(KM#^*vSn#MlQ><e2Co|bx%n4vv^Alb=p;#X)Q zAHM|auLe&Sh)j3uv6q~ocFwO|hWq|iad^k0rNjv`mEc4yVUW$|OVW=GMFje#uXTY3 zl=4%DUoP&eH(YzS@tlu&E0S1XWSN@5>GgMVp&8NDytr1;o<&SakM(J118d$b8{40G zRq;d4u3Rw`wwVC|?qvcl*}Df>Sy~FS{IObjAc6Hv1e&r%9d)2MqjWl}7ZlDd(}WUU zYMIWQ^A><J%S)#D4smz~cd<s{N;7Z*VxGNlp5W2@ht^ETp)ksX!#5U|W`3tjho7VL zpS3Zl$+OR_+3=Va4*j0tD{LN5AcAA|58Ze-&H+{0!2wijlC4i}E+v&CCE4fw?QE}l z)yui|kppWRc+oGMx!dxQsOl$k7y6qzf*mUxn-`4ph*!S9&nh<v0Gm3&k-+hLt+k-| zFBor*Z0C9gqQRU^sE|(t_v!~B7Y~B%Mfz>)>%`FlR#itJ-3R9fAjzlB(n>^lgbd?V zwhgaT9e{YkWsi%M9Q{hBb@a0pFCUBpBx#NdVH7v73(SU?*4C4Cbc8tQG@Y>Xlh4l` zSxYTaHQ)29PROTF;*j0+5ewPf5uiE;lmXuh4`8L&E?l_2tZx`OSnLLXCO)=uY8=Vw z;l`rS#0NYN6#yRzwi&9#QkdiVuN}Gj9n1UzIJ_3V!b;4(+5H|ot8XslXO$32J5jH& zBk<U5i93ru71!ZqZ~bD#Z5@u9SiaRSQZ2CYjymxOIw)n4d}vtr8&OBuF|#Gf)H35@ zerSphiPKzsvToSGNM!@Q)=RSKMr)f+tI9us?C$16y&%tN8?htvK~powY5-k_K%1tl z@kXEw$sEXaf&F?EX5;<>SBnEo%;8PN+--+SUD!^zF!0VwPz;Xb!cCUNO%94ijT-CE zQBi!TK-V0N!0%`i2Wm!r*U}ynnWeI%_3TLElu{||4~xK&T9%ezyvCDMtboyNDhjRe z9BzLmEv<kUtXi4^&ZwLgh6Um+=FCTye}RGn$->qq+B}a+d>C*_*z^=RGFpUubSt52 zOU%)Z5{OR{YzS8|XNI~4S2>8LkgX|cue39GGF5FsiQRuuiJgNVA#F!ZieA@PEarQH z-47X#7(RseGMD8|xf=aqU1W|KL-L$VwoZ0`OcMJoye&d!b_fWjvS#v$KOAPKk^x8V zmX=j9+ekOJk^x2THsEWeT2aT?e9(*t_Ktg4T~JtG|49ou!xLALMivsVHqaHgcb;&K zoAh%ptV7~#aR!5OxBB8|Ilyqa@}wdush*2yT$4VH$Kk<Fr&m)~LnA6eCcL&So<LMn zmu?5$upC2f_%O#esfO&*lhhm<NXEyGlAFRYjajgbrEkzkDpURKSeJbEQ95!y4C7G2 zPWMxXg}Ozb3A?@~7n*pNdZnssx$CGpN3rR>LAveJ2tm1&Cx(8FB<vH+3zeoY?f{Jj z1g@xt`yC~6Qjlcu2c?H>p!SD!`#JEKKIkwZIlQiLERk!}pVX2yczYfwmTw}gS-yV; zN<FWQktCyCwNTYKcauRgT-=s!88xp>ha#ghdqR()O*5LeVF($I*%<kW;0^;=FVb$Z z9S1UA(MZR{Vr5u`T~lwui0dZR-cLfUeKZy9(@u_XHC@PDwqY^IP}XRLr}Y?gw*Y;Z z;C^CU&DTIWnyqAeB{}*?+sz?vljgLz#fWQ<?6a(3d6_J(1MIZos{N_?Y9gxm7^}Ky z{9aq5^UkB6*?D`Tw#7(bj{m+>!~X;%l-1)4>U7QDe>K_tn93d_ZUmeM{JTR8<|Sry zePE~EY25DhM8;cBUxe<EJ_Z8)tJ1rJFy&P;ud9I1A08`{wVyAX%P~WLuSH6}O8(pc z<3rsy|M-V<(g*c1(1Nm4Urh{}UscN|^^e^wFEN4jC_Xhy9k2ZeS^fHSGcd1z`e2_C z_QUYUAhF4RJn~@}0Ah=fI;d}YD)L@y_ia1*IvJKHtaGjV<_QSU8q(+HXvR5y)DP;U z>oa*Z5w7Ubhp}gWuiYA2Irx|bVB3tsM^l5rryYs$A?booqfDPvaZ)>=QzJj|5!ipf z&**$f_ih;b;R@JfxNi(TnqQ5V$}W>5+X6oxA%9KGO;=>2`(nE&`R!2T{<vqVl##qj zdF-o^$edkOFgtzRAdCCF>fCCtL<J5V5_9s3J__%Lx?sm4n=DGb(){^}_}Fmr#%`9w zwF&8rquHIslEMJCNl{L$l~Nr<2v%{@rxm(~v@(IG+U?MSNKIMpr`nv*0yCE60u_3- z)oSL<W^o*j`codlZ>s0*`{q0xu4GBg6pdDwOX8a4i0N7I+WZ#b-2)DWcWR*mlpJ!n zd^6PFpE`{FTR2XSu+3sKgEzu|{DjAiV3Qu3?~%J$-HKgSR@yk0-G%sA)#gi!Rhj}` zu~{B?RCYB=+Z$fakKl5RS&Wz!pKaduJ+?Afx|aJD%<3xq3nE(Vbzgoe&j9*0sOo2l zv^ysv_8ZD`?I-g$vzp`}v0VBiHe-;2_IM}RZ3fs#3ZAmrDUsYT=>rQVo|&Iw{J0ni z7$ZoFWxXqz!N>Nrmr1cN^<iV?G1;=(<q^FdmKvj><R}(1S4GD3>rrR|3pg`|er#q9 z()aRwVRdvG8WVByqOd@v8g?uhkV#GyQ4R4PWzw9z_*$2$3D>KBA?9{93mn~&91iWE z*_R!9tlFxeM2vtVU2|q5QC_K=@_G#-LmFN4%r$y60LZyls->qZbVti`!Da;FG1^<6 zl%zWB_$SXN0Nv*NCacgUovcio6LnEl#jak5EkTaN7IYKL+J>&N4F=1mQ^=rD9}=V& z`UYAlj;0eMjlcbiuFK8<zD?9tc#d1hJG+fl*u%;1sK$_z?DZ_~QC$oHuPAEmP5n;{ zjF%l`ZNRDW?3)75Q*=pB1M#?&1UYib80PNr6-le90`n#}h70}~U7Ee8BFQQ3xh)s& zCOPDI5AO#bmvE-=e*r%J32*%Y_OWqt{vCW|`M)5Vd?0#%qIrJ@AOFPD{!OCEpQHSD zgOC5jIr*oCaerxD{O^(&f2X7Tv(n-RQ-y`-Z@}w6Gf-Im6$6Fkzsf+#)V9TML>}CH zP`1+dCneInctZgP!D*zLg3K#P;E>9MynEzQ`+79g!Znn9W_DyZjD3EZI=>AcTwtX6 zdJ%(nJ-E$a_MwpREBhMd{Z=&-Q8`l(@7m{=%ae=xlZvd@`s8nOfuG|Va3+a}XBzd& z@%rlrRh93>*EF5(*>9AaZ)YDEAK+b;KjP_}aq#WtZ2SD^`RKsaCH8lm{u}@A6IDHo zakhibUO<^9cHdTuHEfG$N2jXbsLjn!!tL3?SCla@nP5YuWL49&kA9DSpPv*G0s7th zW}<1!F?eupt*e9=SvKcch!+gCRmYWME*FGRjUya)Pw(4yv5UGLFP`5$z4Np6e!IJR z7|40uJ=cspL=%<1^PS&#(?~aIY{CN&RjooGPW>;GnbqVV;2-o-HR|a-OK?;{tq60_ zJszQ7nF(`x-sUik0-PUK&evzR@85UtX17UNpXC}8m1cYz`$|h`M@+S-oyD(k;~c8X zO1BN?o8m-v?+L3238gBjcoj}99$Ch$Nf+vmk8`sj6q#4Oyq|Xop1boZdsQ5jJL67R z_Pw9ZwQ1-57E1}JIA4CGwHSU|P->5T_EhmMhX-T)mVjwxInO><dt_YKg-v+z`^xV1 zOxjIf-~XX~ef{;M+J{FVqXp;w=X1x0j?z5Fz6z&nyDV?#nS)txIt;|LLcoVYkPh|u z+s%GDB9l-t^#RDXv56qsF1V4_Lg56;TOsy9?!^cOZ2DD2S;|?529{%?Z5lF88TBG5 zLZSfQ#^#z=bIk1BcPu*Vc}zK}fPOZ7_@v$S-C2?Qh(!9aqV=YEwLSXx_Ngc@#YTKN z#4>{Sf!@9nn&+0)TE&+Uk%_Ski<Q}^H4N0TxXVcvPD02>JwJ0J^$1aG*?=ci#9x9r zSgxT8KLbjVM@T~1KGotU@(a2SwqSk5nc`p)9Cfk%)**`4<oZ5`rppe}rIveQE`9r2 zY6Wyp>AC>`r*7(Q(91@k2arDLs<~J35q>jM5Jh*i^Xji^zOV!N(JoN;dYw8Ot1pZE z%h5prvl68<1#RLeP2V_`u(N*0A|~VpIK15_r%YlupQ$ekQ<N>_%}j5EaaDHnOP!*n zX+ODR!(vJ?nNinM2KtGdChT<tZDc&{7%+m;DR$Oe$D%PgqDtl+7G#h1OQqim^}df$ z@hCz6`x_nv^A+$Y7p(gj`_beVgt|8~v!m5sa^iPAfQGld32SnA2Z@W6%qvQw;p4n0 z)>OmRKq^e|>m$JAm(U0}Qy1f^npxV$CGhBLFIeZ-4rM**+17wx+TIla)}Lb-{F~R7 zx~_V8dA~faWK`lF@17m>^b(%-PXM0F(MVY!wX$=CMq@gFG#)!vg9FM->ISZo>q_fz z3vlmjT9D2%nJ#j7ja4Bi*lh%{n$%}zv6?N5-cNogdWO0}x;a56aLe@!SL>?II6<`U zB9E)}0jWIJYaU*lgPSx1Ue9(4ij@eiRoi)C0mR|F=e9&6`J0|xW4&+NZdIaAx_jG? za3HsSxw{WEddwEl(IKn9z1x~!&;gr-*+)L}Jv)zIKSV=ps*hr=+@Ie_s=B7~lE#VT z(_p>5a4;%JFJW+g5>n#&x#%kPdIutOv!*QKi`YSVr?qg%g`yG$ZMo<C$cMtz{>~P( zC5sG6JfS#3Ik5te!J-Hnm_`wYKbm0$$3(wF8Ia|Wcj`yV7&g%D>~|Zrgzn9+uDn4R zaKp$)$}q>SJ0KquOpO2SiinU-0Wp{u((%<*XMWC_xko_yOA>AP*MKx~Sz~a970FRK zdxAz}uH^!#f-{(iE<k}BK{T*S^KQ!L7G{9%!925}xkLn+3$@p#3q2wcR--#~go}5n zVC!dCy+3lIp&HY^2AdZuhTlo8Db8o8(nEeSP&1yTON*OfdC-YS0g&sW4^+DH7|Y*i zYYcXheboGB*aSMDrh<yCYJJ}O#v<&%7g__v5tR=AYly7vd(I+ihi9Rq5xIIF0o01{ zfzmgfF@)NDteSwaeY-}#LNP*5d+If7C}3A}67%TJNzse3_7H^rf&bNGZ4%{RvK8sw z{ODt?bN`NvTKFJl6+vWTHM}uHuo2hi4&!|!&tYlg$K$W@t=6qXo~gCdk2@c8t$EZc zdET~E<-*giRUqpKuJj$xOn5X5Ujj3DG*n_EBen5$_BVv<NYTOTNKql{NLhgH@RpBe z^y&tGBpIFEm8qXb*Ed#Y>kumde>9xWt3e56r1-E1-66J5<QXQ-{CKSG8e<66o6LY4 z%~%)!PZ-Hqc^t;5#|5gN`(co$X&AQA6;=FW8+#DJ>C1)(8^d*g_hWf7H*<M1mq>at zSD<?877QkLd<FH4-9Ne_UpWkCtfj@#&s75TtT~?6fbPRY?PbmY*Uxo8`j`clXT#;K zHh0jrOmgHP=4^Lx^Z=&%T^}YRo*$zk6^cGQ-`ISC=WD&uf4rj6B{|i=7I3(K8z=?X z0h(uSmC5`&FX$h#@j`uiG%NqF4PJgbnYt|cvh9}~I*W?0{IRsXnDM#9*a3{f%n<sr z3<&zNG${JAC*b;m&w}1Gf>4qn1hUzbU6`6#1o~R;CV8|SLzUk?TnT;IUn*flj-)RO zH)x0A88#yPh>}QhIp33q&^rYI&@Z%N>u5sY@TBWdEA@WVLPr=(rb7#<at_Q>R86+K ziJ>(_1m6bAOvglYCFf)LF*0BVF*2ZsFjhMGS7pcTj_TM2K}1VIXY<wn8BBSk9p$<; zk8z1RSN&xlcH#7OKJ3DH4v!FIiI`Cue|Q?+4x#0w_p)GgVE2#XOkx*U=Dq{OjOt3J zLJO-Rn6T{0Id^>M&?*e&M+#=70En_c=>I<k)0VOO$5-+H$J*Mpd?`SX3}yV!?4ca! zF(XbiKKnyo0+NablfV7v{x!UuXZvW{a#EHFba35&|Fw_`_p`-t@mV3E_e0P2mORvy zgEzrdFEFkE%H%Az>M15mzS{1buDZDHQ?m3$0m6Q3p`|aXK7D>F%}WBJwH?6foyjh2 zpg-erXW(cXAB@5DD_+yb#%lW5SWTdDl^M+^CTZ*-O+(kWTQf7XxVqf{XF!0Aiu5N2 zwRR0VW@fxzsV;IFNIw~sa1eD>bX9EUug&u&+%2F8bl>r<9uJ#<nl3DInSLJukL(gA z0_$0m!D0iMly2`|PD6d+ZVx#1Bl<W-aN>~d|7R4q>|hE&CN-<zbu=aQFWwQvbsCOD zikUN}%NMwxeg8g=j?Z^cP)H9n_s6ZSjmt`QJSWpB9ETRdU-*$=qm(2NPz^ovS$G4q zepFU*rKa7%rk*@sP&G_l{f%`JYMBAlKn`BQT4IjfOK7*B_1-^#TU1WGlE)FufM<Uu zH6f11LrOPfU5&+r^Jy!NT!M0cN1F0x_9>ri7fQ;isAk-Re7D$unx(XmvE^K4)w-w( zKfDm948GU1*7FFid+VlUG-dJ^uh;|+WGJZvQHcKf4X>)bCJk<3=q%GXzW{Ys6PkQ) z!j9B0pZgIdb2Rk;hkKN<g5#aGM5(L3{V6YVm>ZEOo1YCIAWIUUjz}UH5M`Sdakd8x z#M?MCH2|#3le%e##gNiS4cW`9gkh63i`Kla$g4;VA)od5S=FIhFHDh0sPF)yP9`LP zLk2B)+|KS?@$@x-Jtk%=TCu3a?~{gWjy#0*9Tn9>pyqvF_d8PiT~!xM{n(xg$BHxK zr>(8|u=ki~tMv$?EMiG%H23I~44U0b0a3;LlXU4>TF4H4Jp@@6aWh~JV&q~IE&fqM z=Dd<)Dc9#py=d0SIGxub2p=a38tOh&II|)`yl7i6aflSLUhfP`MT-z)ESAGe!B3*S zZ!hY=o!Qv7RE7z0?%cRQyx&w335D+{F0?oz2q(m6OMZ$`#g5^4Z#a-Ca+Ai8<a^b} z)2`4F4k=hsMq(3&KNZzl0Nir0jLOUewM0?V?y+3o6$y6fY&M(dP~q*8L^Mj4WyUe4 zv5B-dBt<YdX~|l#zS;NY^Mm23aeS4C+`*Ol)}0oE-_Sz6lFt}xo|1bufwaIxjl;`a zj*Moc(Yv>!(VI`A*;}8F-{9y*70UjcMy)|gQ(zEDbvA;tQEEzf>NwpbrHs1~Dh;jC zTPm?j9>e}|7}}#*(kwl~dP>RUjXJE*oi{bf9t3m9LZ1M5-_b&Bo%8x5ixWaQbyiWi z+U(%$|L`GBan!b)X7{MI>%#I7KKmcRQt`dL2bUsf)^08;O-<)QJE)_v+`!?mgDuGI zteBZPg8{S`x6owFa68iWJak+)H#jD*s(IzSz@{j+twM~~;t!85dck<0N+e{xSbop$ z3Fb81R^GGvD?A>eaApC7*Isw9=f%v-x7K147C-G_Y>%NK#6mbDFs~w!J*Yc06C;-t zccnEL5hcj?hNLmLfk;#H%G7<a4f^*gD~j>39<DTJ#lzB}8BU+qW58@h)AgTI&fTml zzqPUvjchBi1(M|ANima*Y&(Q567AVh_gP{&G$o<!=Z`-44lysvBJB@Jqd2s=%o&>> zS0%g5sa4=0fBaN5bhWOyJD+!jg0XMMj5ipX(QmcemZw&R$<1z1q9L~ou85%C;~pf& zT^k3OfEi`*O94dL(CTzQ0k~@oLh+K-Ew?(-$^(Zq)u)iT5Bl|G_WA(ampgM$iPKuh zMFv`cM=@?4n4;|zszIv4aEB(m7DhP(0Y!x5o+A<@?iSd`+sRzKO+5H?T9<GKSA7Fz zg+`A*jD7gDf%L=1?Wm<hxWir#SBzH{ZcL(l>@M!Uo$hT;7gY$*AcVw0tu9hKVfOwK z!l}*R6np=&_G2R`_qe-pl}3JS8eqofBwhY_5{WtQWo7FGQjGgc72N&MKi!BQ3s$D$ zig*{t1&nb1QNn?j@o2Sipu+fBGh{*<L2bJgDitL%I|a$i90z8C!2jaep|m&*Hq}jI z`UfU53wf;!f4(W?ed=oJBY_BoNrYj!Um#4_hM45`_*yBRZR;#@TQj@-By7yt@WkB~ zs)d%D2?;&iL?4GtQMb)|PrKPS&d5@;XvqCXvHCN!w^Hs9MgK_{CQZAnC*9@_Mq)M( zb6HqSAXIAq>kh$Uak6aE*V&5FTTmA9)BXxJT@HXwZ$dR<zz&Y$u!!k;G{Tp2?IAns z$);e;s3s3(pfpsf(`S4<borPCRpkhJpM7g%onEwQRr7_baLW{xxF&I0oT?&d{@e3N z^Y)6PQQFE_dRTV?EjD1y0OyOJs7~+mNR%-zr@6ch?e;qEd0cS<MI~~ir(46fU7VWg zL5t~YaTRH#MJC<b^QorbFau7aKi5J>d7+=nFzVp5L&OqrXxqeS2d6ObF8Ark^OMc) z$6QGUTRH@0FM{wCUrfF<*hgwC^(tHIkkSx4ETB7z%7130&$4I>3(>=gFHNUiVPN;( z55Qvx8t5mZk3l0n->~EEqpe8sis>ytsxr_v*BMRekBMin1qtRnNTYquirTxmiYzB1 z9(*~Eq?3EQa|o=nm=A5`tev5D@VwZ#{Nr3}_2})l!y|RTsEhobpd4ve*@(z{XFfs^ z>d=I;Kub0fn@H!Nrs2H8)~%dPYu!m@rpiU~@gov<p?~%kC{snRdec6env)*9^>nmy zM`xx_Wyeol^*QBupE1Upi|^XG0U1*BT!yh@IiW38pD$`IaJfX`C3H%}VAb)15g0i1 z$?7nf$rCi~Zq3SkrN>zO0v6t^8lIpGGz%t(ol*c(<<4Ju+Mn%Q`MPjBPd;#vmlNP? z^6T-%tC)L1-&TYrm_F_heEydZRXc_sXD3fAkD9s?{ODTKk4)<jB?K0H1TYvo7nATd zj-?J%BPD@qb1+=e5sNzSm(CC3okJTMgU-WyJiX9Pk^rLwoC<KKch-f6#oqPpZPV!A zo}$?TXPfQr-Yrj$kH^;`tvg`vJ@9)GO7w|m=Y&z1Q-x3GH{-3nt)Sj(DpS7dgqP~q z>#e=N&k+hgW#9qewUV$3+{iE4EgvX+yr9qApWL^Nrs`vg6!6)8>LOkQvw4HAXNAdq zy0|OtN*&LNIEViRGEX>iNT6jVA!pelfa+CTCZS<m0;gCM-dfqS@%H}v2O+ZFzrrd1 zB1y7waj^aeNs{INf+WfEzmX*W1nT}>obr!R{=0F?e=c+TPwL_R3DEot!~9>^jjVsL zE&rPFU+hNKKY-qkSN}g2#OW+MtcqiN^r-!MU=UtO#&q9*C9Ar_C104Kf;XHPLY3%G zC3z4Cl=ZLSzo~jk;%;Aqz-f~Mjgj6@Q40-N^?E3HX@3~-xcK}y+%q&fI<z{!!MHK- z@;pQP#sXw{61*EEV|e9*YUiVV&5vjwZFso7Rz3{c*grc}w|%#0Y;)W3Lf#MJ{B7FG zRemC;%szT{5cZJn@??9lUx4{&ujxv7Iio-V26rKcvURXwNbk+5?IESMH9OM!$?c2& zsi<wu;6wkU_qIs^VIc84ji)F5uu*3pZ0{K3u564Ct<Wo3w@;62Fl2QSsO9h_?(1Rn zPJ{4HvUy>0EXdpSLOSQ&v$rby9qPrkV7Q?l*>A4y9;i(yAc0{ia(1JFYx)lupyM>U zcuBSq&~+y%J-Dly@uTexs)E${put7}Nht^L%)(>IX{#sIS6L>}hu560+TKlzC+0Uf zlbQcADBu)N?X-`kMvo~<|H4r9?BR=PUl`G64V-PkX0*>qb&4uNLP{9+9G$MKnl&pz zl-9_5EVA(gaVuTRucHVGhwV)<Gw80Ej66M==-wdxGqt6AtEftq{i@-Q<*LDKVQ<w` z6MSDgiF(gJmJic}^(L4sM0$UTp#pG5D-`D{5|e;7MOX6uN6AB9M8l`4v~phr%7TS7 zjbW~Y-`Dsm2S6(LwkP-2kd)HN(YrV2MU|M<)aTC~pAW_aIqqzfD-JDBTHG4jmuCgb zbl=L^alBBOW+{jmL<K`pjlgp8f@%0`LFC`V=Z!Vw62=m!NpY(&O)Z6AgN9;PcyfAv z+}71o;rsg71evfI(vfkYr7TGl8TO@S7b1NIip*@h;LCQGxhr3knzr_KP+J+HWmGGs zyeNi$zG_mxK}e`?6;&ey#Af%KBgQ+|wLS{?`60fv66LMcOy+wauw+x6!S?l5vyl;t z(nW2#D?QsM-8XsnT=O=@({)Bl>R8ZsZryRq6w`OY1JaA>3}{6}=?qeOP!5`nnt?C# z5{CCgWr%0eGEh+sZuKhWw*{SewJO%P)>K-@u4lj<Ec?$1VUT>}zJsyC;6cEV{1CuN z8);^GA+>$VBS;Tjmcm3kFMu&hS;GNb1W)j5jQZ&F%edUz)px9UI$upay|(pN1@>0* zCHboqMZD27*j=ACX6OuMG@^wW+c^eb=K&}W^+8?9plpoh*a8A2brxRm2}x;P3f5r* zHDrnFTWsjJXNLhg1e=N>mc`!RL~=(A2(jeAgeS$gZXB9{B_Nnl+VL^;6}YKvouR23 zkC0y=JkTK}K_~CQaZpUwhz}E%%4x9ca~@yv=i)wakfrVCc^i+W-<6eVRb>a6Mn>3A zvw9F|CB6+$6i=;jPlJGVZMCv&AJ6&jd2iW)OT+K$H0WJXeNa(Lm`%pM#Ow$$)IrE_ zC?Vk3O93KzDI~VfB`d0Bsufg%IhhlE#NFxsvP<-L(%Pp7gMF>BJ5X`t5ojUcI~T2e zzjf5hY?0QaP`~SPq9*}vVT&kh`4dkxW9@OmG~kaRs*;HZIPAYM!Nz{-m$0|Q5p71U z4u^!nBEAu#lZ8e-EVP3u&<XMs#l<CkK5z4zlb<e@g`G(kF*bjkCPbD`CSD3_;-`k8 zcVp_HF?+>@#yzFDu?n{u<|}&wbxq}%9IrE8mxum_{M2JXL*tI;E$xP4*#-pF)X)^- z!$Vv}-lMalfkp|*rHc)!kwl8_|Jl(_4%R48p0@vXJnQgXLIm`;cjreh(`)HsgyJvr zBJ(hFos|cKNj*HpKGr`gRG_0uR8%jAFk=-?<slQ(Bfds02ZhvIivARo2PIZwLZVCB z7Mhf@gCH4x<%-5j=lX3Af|*mnFe)$YP)7t1kzK}0?O9X}crBnXtE#m}xfO;@3}*5# zyV#u+i`-jE;VJ-Vi$Ku>>$Pm&eY=^Kfh_Z2d_>klS`^-C4<t|h&ZH~~0qzWwi^$nQ z`)e|^o7FuO9W(j6^*u)0gH{Cz0(+4#P{)FzPmzah;uGa;C$56ASLOg=zO9HX5hzZv zVG>r(r?=Une4tiAas$;pb=~ih`H7!1F$vAt-AYB|<!V{2aMf=@VMwGO(x^h2(<VD+ zD!j5_8V*<>Z^8E<t9^(pXXePH8Br9e&TpsCvjTx2BTcIs7QX@Z^FO0z2ErY})_(Js z=RK3rnVxt}O8AIJIv8J?ug&HuK1l}JaRS&W8h%LfJ%k)Sylp?r2YN^NW)-2S;&l2h z)@)Os;{(j;B5jufW)cj`I4_XW^+-8B=U8Yr2)}vH&bhE#6xR3ir_->a6?vnxF!~A< z!yh->8bRgPA8$oKQ>YNdndW@do9g-#qK6)uNzFJxSHX)-crenJ1P52*KVMdE%%52N z9!#v9j$oY3w}%dhy$iJW)a*jrckQy#B)XI+;NTz{ek?n#-efErZwwiiBTpwRTO55Z zo7eSf|EPH`#~;Ej%HPL-Ha>%Bmoed0Tll)RzU4_NjD-qma_$;}JbZ%^D;6^d+OM9a zmB~y6v+PSD?n+1;A*0yNI90FUA2~nbyG!*0`y>z**^9`5(vx1hKHW%-X5AQsv7G9R z*cvgC*|rEXT*S)nxl*&(CO6D8d~Cv7F-&OLz*;O}@Akl6@j0xRt>BoSpXImIbgb1x z2K+~pO|){wje}{0V9K_RVlLP=>HC<N%NS#R3N_bRB2>*0(ljKDOJa&XP%_~_=;ks; zmw8j4*7XYAba$PO@E#;h=DuOQrr@A2sQc3UqkFU+x(l>{w#sC*G?%4J!P8&WnM4wE zNgjGpyudJ^#X5BfBVdz^dq3LAf0Q=~!UKvQ6>@js+_g#We#M@PSy!2bLFThk9g<Up zCr2-Z8D6!3uBy))mB2V5j`))(zBR9`SuOtTLb#Kr1S8L^32~R8gLTXB$SPJTcEmTq z1$4rLU)3IK;5VemDFOpaP47oz$|A;6;;}%3`Njqmqb?PlNZ_ZfF!C_yJR}b7adI;V zO*Ca3IEezqmns~XO_&tO6`Wy7Muw>te1wRo($)rjbqlbtp;mnJaW<{fF;~aWuFrk% z%QJkKF}7vr%q-UUE*0K*@NtR>R2a11&uoycNU;b*pjaH>pljx`rmAnELM+RRL4#7B zVs+~8mNgR$V;$E<N&IZY?4zgiq*%~cm|XhgF;fahp4Jx0SonS<=!0?6#MXc$LX90H z-@RR%mV%G!S;3rCU~Izj5lZ;0(tnC4Jmnpbtd`|f2-%O#4>@!V?l2=%cxUCt_#KQ~ zpN0pNh-2Yho+(7uDtZeG4bjm0SihtNP%@)C!`3vqVhee?dlU?8e;Px3d@9@*ey+wS zg*(~r%$a>X(OVvYJQk?~F^U*R2A;{f4c>3B#GYm#ggq{>F^?N6f@eQ}ABnA)z0$;~ zcM46!Reu|!8}$7|J_p9kK>?sP@<5{T`;PQ&UXVd>J&%e>kHm=<A}Mt}5E(^EnGOf) zy#n>J+lzsd#JblCtxz6o=1a(8R4g(!I_8ub;1%iDg;1Ok-67eHWH}G<uu#qZ*r(*7 zZ6u_e{0UyqewKUzBRm0E$z!q*t3g!;YZz@O_ou5YoG7qsk*0NUlhX}%X!zs&JY*sG z^FwU(hU9Y8;+o^Y358HKP1|Oih}hw)<NR@!JxSy>SjU0lcmiJYu!i{({B1)h!3x<Z zWOCI9{uKiLi7k>$V7}@)rryL(S`v7Q|MRX9_zbfSQFw1OhD4ofZU`GkcyK~auX%SY z#*bY9JR@t5fKbESH{&5AmNfRRG%+n~I+pDsWAjWa=*2`k3|sT9MIvW#XZfdh#Up10 z+%yOu^m;X#O8EA&^j;ey_a7HO35$E0D>=SNo-7ofbNheJr*?z@L_A#HJhN%o;7s`y znT_g#y_jWjWi3rQgvui~%Gt+;XJsr!%g9oZWU-0eWg&qDs-Zc0`a)vhic)e}qMEB( z3_dWPpY&fdj<o@)->~_(47=p&683@T8XeF6oSh}VSLQe=gKzD7gtBJC6iuW_v7o#Z zAaxB@qRrj{cqYVye_-2JVUbnOcM4-UDsRS(+^bKju-Z$KgpLy6mCzyt-)1iOp2`z0 z4!`_ZnNCSvGO$i!O1dX=`T;<@gU8;UZdmQ}GbuEHJWc150H-yp0ePV~8|+{GW-)_J zY3L~m#|V8R?8kc55NStWBG9e=dGV%q!7saeS$k9+!;wW_v)&a)U$dOrQ&lJpzCxfc z{wW;kt=LO2vqHB%iD!j%=mNabAtgR_#E)ETlS_C1M`5+f4xT+*>X@RK3*b{wGNk}v zWeLWb(N)6d%rO-V`pf}U3`-bo^eJS0<=#c-6nR2^vIomthzK?rL|L$fOBE705kci_ ze!ds*j<T#N31}nZ0BQ?bV1s0QbTkS9q~H!Zv-l32V!ML4F}>9-3NerPAQmo>JVE31 zXydWMHb$Q2J#{Zkh}+5J(dl-&XuD=x{CD8>kDYYP%_*>u7u=72a7u-&5s6U5`;C?T z<+ZQ%yjD=9EW*?1QE(UOZ?pZ*>VoZ_{<FD5?)T#t7k8uH$x{uTU9z@}dML_chK^+( zmDqKvT$(b|N`Z4s;Xp+*oIJergy|R_YT5&6N!Q6%Pj>pKAB{jU_Ysv*2r}Z7BiZc} zYe^EDxktU?T`t`4;#HsGla~qU5MIcpZWe}Ig0Mhd2SbWEVbm}A-*tk+Y{cfsClD_s z_2u=e`1&)OeFt&mjVbNJO<BH}Vu6z`lox8&PJwWEjng#up;3R?HzVUb-Alr;lRMJ1 z4wMuSILSve16`m(7tP?p&MP@2*5!X9G^lYbJ{qM8GLbx!P_$JXw0FD)0^v3q-7bnH zhp7VYa55LLbh&J$L`XX@V&n;?I=b<}JL%M4&JOOJJrMJFa*!yak6;T&)NgluQgl8K zpBHSKNAqykUqvk0d8eioC{Q32Ki`~FR_`cq%6X9&riq1#nWfWdru9og_S>`VpGi*L zM{-M7a$6vXXW>Ax4LUQK4F8znxMIISQaAYl8s}O>i1DjB^0YOkPuOjY{8|z=VKdCY z-6{9l=0lmPav}BmfiL?&1b>aWl1UcDjh%~=rvW>gMXyaQ*WrV^$Af75rn-w7qS@g* z?(yUMut|8~Ct8)3Z;*ApC}v4v!aDXkWXP>q&9>)L5|9p%Ynx$_eDoI5p9WN>XOy)@ zORhd)%irxo*P+(Z7;FfTy@e|PVU_A#+;L|acP<0>xKOYt8T<-$`AaA8J>1zfQoBxS zUYR{eu-2yGyJ?;V3vf#>AbA}EX&b|-_JYJ4o)tU<-8IrA-LZ0WgCO!VkZ{<U`(j9@ z$rdxN%f$Pbt^4AyXHTj&+PvJ{o@&wg3Js7+kD)V0?Sb&HK}VDk2V93a751mJ6BYa9 z2xTYzw`g5_YjxaXiXz<UnUJR1?vgAJDO;*{^<kR2hzm$VG(iFpW9ezjYkYiPoh-^{ z$|oFx>z-;@*^Y_kdJ0guk|LCQP1H+U@!=eT`A$%p9+mpW%hRHuaN^JwV?srt`IOq5 zHB*_osoFF)Q^o6}2<*t^{L7qW$HgsPirEzU;q;7&i4kHYXJ>@iM}o(dQ?yD8_x+Z9 zroHNNsARQt;_;cZIrFm^@wv`rw}s`lc&oFjY_|N9>(pe(qJ}~pvEVWLNxZo28yslF zLs@_pbB<dU)7{}BIyv2WsptJ}1&i_Q3hqgjC1nK*G|}!9Px1UJDyFx+{Jh2l=Zq8U zBC245rjg*q0SK|+FlBObDhp%BY0{zJ(YUN+pr|OrVsFkM*o$25cB0)~+AEwn6YL_C zAlY{kT-()Uhrm=DK*)L1H6jD$pE_*rI~gi{SR2OTWyI$aa~T%oB2B;BpW4MOjE193 zkOaZ}{B;ts@Q~_(*E@aZg#&f#^fuyivhKWejkI1^{N$J%A|rZy00_xZcWBQJn9Ovn z-LH%o+-tUdNVP>FO7Lb)(4ISS=to6E-AldDz-P9x$>K*{UcUL|jB)eYyma?#ayu-W zUzuT4)&-KpmUp*26#Vh&C!^f^k2^K69Ww{|oX>!Pg{HFm+)NNvnNs$Z6fr$BLu21> zhrMhZXt9j~vNK=yzcLXRR5e#+gTXhI&ij&KSSPf`aYhU0Xyh~2tUwc+7vsrF74x&R zpIdxlOweV0SrC1`G9?N6`1aaniv)`7l55PGPF&t`$gSLbgp8-R+d-TlL6|2nQ38P( zvjuK1h$}1>lZ}1?{#RS6;=r`oFZ+H-uYp+RXIF!D(12B<MMuH0S{G6{x<U~x!oBvm z83F@>>XH>(??_<Cq2&&_=~BqLxt2tjJl3_8^?kx7Ssmpj2}IcKy!a{v<mwT+;~Qus zTBaFvJ4$9ENuHaf(z^t8Krl^P82>$bkunZHF~+f(hqsxSbRHVfu07glRZM*ttgirO z5pRXkq2i6wU5{)slCnk%eqkkqP3M<(nI|D$<$Z6VMh4QgVI(ofn@jtX+VAXT-k`Q! z<%=Z~1tI2M_A<4HLC4Y1HqxKM2y%gIYXtMoj8%3fD5e!0AuDX?{h7n1znX+I6?9Xi zJDVlW6yQ{0wr3lDXsiobu2%<jQ{Q5f^zE90wLZ{@GB06R`Ueo5u~UgtB2|cYAo*k@ zc9XZL-w=j@Gjk33hk;!eaB1}tO(@4Bv^@#_V$+}c7ij9Au-_k8AUhNLKd?Ukk|q8( zO3qpTR*n64*5{u%<G+cf{yEBD*yMkoyx2d;0RKO-KL45N$@-7#X#WAN{ulde7pM<^ zK&wdYyR^?xYKPOO0b%iY<xL6sN?~|2iJ~cB0AUvT*aYFJ<&oc6oS%Y0rG)ZNm85X! zZ=Ua1-dY5waG!<n2-PGV^ps*}FY5EvjjqSE&C3KiBq~@Anuv;63)^$A{3ix}L!q#B zNWK5K%}voqJPdNgF4(LxO$T()743UA;7w(-y@%;Vhq3OnKEsLwg5a2z=5d;$Oo5lJ z#=<M6T<fz?w_SbAgkunC<BGJ15PRTeYF(ZHF;y`rF^VIr2D)FPa^C!sdXl}=?bK?( zF8U0@a8IEbg=hPYmGZ{U%#DNiZqoU>n}#3DI6t<H_(@N&Lzqe7;zN_1VcJ+w^jMBO z{fE|(7)tEILSTPn5r?X&&!y*W@x1abk5$7>EUw9o^S5U+UO)^#dWEWzbzy%>o77Vw zPq9l~3y~ACp?Y%`(jGH<t^cZrk75<WaPxXTxbO7t0$T&4Y>d_MP<uPnDa?oSD_-|k zr?~-v(GmRjky3*EZ1v9;7n_q5H1uXOCjz;fner-Hz2&p3eW%TQQkx7Mz5S*vuFE~; zU^sCEEQ~5XLPpG*bGg&-P1fN@rmFQq(Eyd6jG>`(q5hVPeENKfO^9*g7?0eAlhU=` zh}(nF+RyG0S+Qg8jgaU^6`bmg{T4S<Js#qU!AdM%Q>#VFFC%anYN~>Vo*Qwy#4v${ zyislam!X4k+P7Q*IHzMnql{OYIOA=H0p0_=<%HqKwCU5@^A3-D36hYDi9ILhj&CQS z%b9+%)v{D=P0!czA#yWk`@0odrcuy`I+#cWCy^1<8ef$7wjO7H-P*#F-&{|r%{q0u z&lu6uDdX*|e+c}@-}48%&bTh*C~ENxSpOV9mWLv)3iwS!=+@Lus)D4d*N(*JtB36C z?eH074rLR+8dVm#@>~<?qK=iSc7N(<y!Wm?P-RiD{XNAfrg*Z><>{1xu~Kao6jcI` z!QW?ib_drwkJ<j)L*@Ias^w>=mV2Dq%wpAUawui91{jl6X6!7(-}@8;PMwnOyIRmL zSRg#H;^}3;DH5NdqWv=SC!4rRpO<~ZN2Cs&{e4F)@#yWA41u*@y&5CEy;=zHzco<H zPbeKvbZO++N6j3CEAU2%`uP=l`yIU&Q6|r^Bog<spc6j+7mFV~qUm~GmkQIyC|s?# zCYQCg@f!tVIy=OpHsop^_sGwfu^AY!sMO8!OX~B%Nl8|*Q1o0xGEHI!lZC?MxyKwE z6qoj%hlOU&>8DxY%IZlEq!zF1hkMp#B+nXlY;YuhMMKqSjnCj1z@;jwHD#no?mzYt zp!LyJ`%-y*q7m!fA8~11Z<w~q-}dFp{cl;85Ednr8^gmQ16tmSwi+KS>XeDs?uNl2 z;i;$Mm9MTWJhs+0903!cKbAv|&mwye7R$xPAs(7eKK}wYE@+O+u=^}dr-iT}8sRe@ z428R5VB)Bdz9N48Ew33#)=p<FZK-v7rBZCkA+?XiZ+IfSup4GiRa=vrU=k`BVKxXz zYqkW}>rD>6iokP7diA?^0A~`Ez;3cGbjr9%*!Mh=ps-FMdj45w0(13-e!;@3gttM{ z#F!G7IB|)t!sDZsIdWt1z4uIji;}r@fCADK+7qm93m<ldcK?VCIam}I0=^5ZfTJGQ zf6hwu@xVv6tzYV2Wum`s@R(UR{w{RM`iJPoU-{s_D0IpCH>s0<$V7juNceZePyQU` zFU$YGW}<&cKL2!G_fMJVpKkpAy2vB?U$V_Vrs==34cp(eRQ`__dD_;|s{_dIA2KE0 z)Q&<?gZsS7XaEN%LrC+qy@Y2`WpKW*UwNWoEK<u%{<lT<jkl?(x*d}FUuHfB8-=f> zW@cu(nL0aPPrP?~o3%coQcp%HPgZ_^X=rcIrIaIm0TZBkFF(cFOgstnHm>4$wK0NJ z^e>v}sPn1r&{-*o7uW0CyfuoUp@dGvFUZPUo6@s6P2ZRJTsR8pVgGUsyhvvBZmUhU zJ;Rj2#Z%dO6y6(7c;=Q&KrITx2ZZI#bm3e*HnwYYcwe0*RwP(5zTfSPyhpdc1~)~L zMfWU)y=@6O0Lim6)!pRrM3_5JBR&u2Z+`H#i&E^HJ)MA3Rqb3g7_OLG>u1;Vf}O1= z<f(ITbJ)Q-s3np&_W<gxx91V*E(*$@2F8pFfVvgV$R|y!ab;!eXJNN=^n#}ufn&sl z+@;eI&v@I_a{AJ!dUq}y3&xRhYoO{Ni#9S~_iO%zftyV4oZb*+Z|<S-=vbL)Cv$W_ z$+i2er?ZKHCiFsb1wr(nrz_p_V!GFC){nt1mt0Bn-orZ$E5e(WPhew^UIKH8LxI1n zS~;p*oIamGpY^{O(#yD11Wgmhs+xo$6Nr0K{j`PCtg*hqktVAfRpU-DhO>2UZJ!Ej zcceSWr8~FXs9504ola-e_@!?uch2W+gSUI|Q}Mpk&u=Q*^s|eDPJtC+Vab-A@F}l( z#!}V^Z&tR(<u;@PF(uR`;XW#KzVucW^2S!QHyNoTaiS$09}1~X`A7tu&C`NEWitXF zW)@<jqhXd$T&e@uAaE8ts<xU|MhBnv+8l6RBnCUv6$S=|lY?=_S%E(KZ9hL~ASzqv zOIEyVjWbu>!uop$otiic75r#I=XVQbn`1Ep_pV8(N4H6sjnU_9eN)hgZU}K-sjnqM z>By%YrP948=0Q#(k*?S|;Rng4@n%jP1a`)GEWiREGfcQcIZusIjj1UVyx(QabILzO z`b$s?k?T1zRBqgdMj&ilOH*BK_Q1{aO`G0Kx}>TwqTsX(JEu?G?<xaB{VVO4G)@s8 zJ->q?h3as<kMdbw)ZJg%wm)R&f{t%U#K5uRrSSXc&z%W?MFBxIMkr+^kt_41tRUmz z%0Ly&nws}|p}3b$9i8uJZZoL(x6I&5CJ2w)K<u(xL}{Gx1<7o&!_Ai7+OX{6@JY6Z zehc_%)AYy1hDK=MpjRT*eR?h$`Q--P#fEpovg4I`wz;Q|?zVhXEt-jH*CTomN0pCH z$_;-Zm`zMNQ^a&{uwO2^6&S=O&FT+$zfG+fsyZ=z^HMW2Nlg~?esHo)u5-{HH0b`K zM5$1nXu~tI!Vtr2H@q^F+P*2=m0bm)#f2RWx)~5j#_H+8MS_cB7IJ>jMKAQc)jaIS zo;u$G_LK#s88ZYO-)HsUnv8)dqKH%|8HSi)4+G>GWOIXRR`f^9a(TZ;xCF%hK6j46 z4v{263F<l|Z9p(8lR4E*cd*e{dO;T6Y68I)_xBBdW1$2gq>b*@V>x#D^gT^>IVary z)8#lEf43_HHUlpj7j;JP(VbC*4>|{KR$3v&r06^8^sg_bAu)-vQ^AsvJX-h$-C5r% z01_uWW__PyMQ`HTt1((BHmk}Y^P3Xba8oCsAYr)nJXS2=Qt<l%P-i}cK{ykgI=V71 zyc%UC`f%3X#n>5Y`1&%_*~i_i`<|eB=aOU;i;R4t;MT=LAp>x4na{b}$l4|5AyE{4 z_B2(18csvrE=<ivMJLH1-2J3zpcE~r8(hKjZJhpH*6xnX0pt^f2_KklJDYfm7_(j6 z?DuV_K1Ue+n+MJ2Nv*v?h_5d&qQy!H6hrJJcwI{3+@lT9c)#q>kQczB{E0%h7+JYW zg>ku#r>Etin0oP)vV-$%;3POC1PUAv)(y5P?Hy&y5LfY|w9K{-L)ELkb9<6@|7_t2 zP<%SFJ1#=jS;8YAZ65&R;-_t2z(Yj6AH6wY76qHOoTJ;w1}SvNxIQ(v!Spu=jp%81 z9J^6s0>EqqZG*7Zt(p0omn&ol1@kA(F$qG-lB-K70^rQ>xCXiJw-mm@)kPP4Nk)7{ z`r(^<xF-u_HNGdsgH6DlwA0$GV2l6}P}M{Ev{D~xHt+%V7b5j%VB47WeZrH@KqEE@ zo@F)aa;@Pfxcl{{Z20MD;(?4^fEofx7z?iS7_#|R5pIf=j~3!lDl}qqE^8790$D#9 z4q~L&K0#^do1lY5pg7_!2-RmM_8E^HsTh&jQ^%2;fB0VF6z-Vq3@{IhPDOcVzsoe< zPzLb@Qy)x)3*EnkVj%<-d05M_kFs2@RPd5nb1encx7ysYB;Y%=?D0aURh``XjX%qT z3{nkkf-K{kY}+(<k!BaxFs4~oWPXMoPw!E)9V<)L#;r$F$&IuAY$Cj6nr_cGmlzLs z6>)Htye9mnZaf+|C{HvKJ7-G&A(@X(_U~{PyY7BM>ZXU#1IuSp%|+35gu04{GHrDe zmuYIadp8r8MtZrPumki5Rx0vp>)N;2OkjjPszZbjtfbC9SYxsdXE8g_Dabp_Tsjb} z${A5#4_1%i6WKYrZPwESLBCO8`cO~Fva~{dqu}Pp{3b42<ZB8k_R$s~O$d|IUg5OP zYb^-8ICeyM{<&`$1031rGVD`w&=*20`_ZTu8P;w*&rAr0&>uR&yA%O+6Aka7w{Fzq zF{)M8k#tGEPnE0(dM9REutaMYHf`h0r-ybgw~At$_{;C1Il?Kwv+UvS6q$kcko@CP zHa^3)J}>J`nc!G!%+FMX;Kh>Cp-e6joi6ge1$nr8V3?tZ$IB{UVuZkE%!`Q0v&sP{ zua?OY#aDIcz6JH8L9)X9_1Lc{w}C%u@wZ{7FW@<Z_@ZMwoB)@iFocC!JI~GOyS|1k zK@d1shg?Z%TEQIhLVM~;R^s(AIzd?pm0n<ia%bRrkyUf~3Zx0cT~6<v^bG!YjOUaF z5OIz-yFbW<lk2B{cI&6Ty|onIsA|s>@@}ljcV}ns1Ve-))oyEzkJ)1BJ!E~F<vJ%P z0c;(x346)Of`wO4iGu@#l1P4uqq7m`D?~Uts<4my2^98^Mn5!R?dsRGKS~50z~3Bk zl$CUX?HUe|!xE$Le3M*-EM=a1^AnYLNPh<KAjkz{;7WycgM*E7#uRz2#f*f`KdjFS zkss$I`$M+z%aW~<vZkNq=%<19M51(QqW4#dx+r9Z7tg}b;8-0V7W%i^e<d)*G}b09 zzX(jiK#U~^kc)ny0C33u+><BHb~nN~7tDoSwtQcIz`B}b*u1vSuG<nP@&01j5V+Ie zsETgVdnWVhrfzz1?>5@XwI#avL*N4ag7oH9=p~RD1A#$vec1lXz-YcBr|$xx*Z&~y z9fKrm5Ov+^vTfV8ZQHhO+qP}n>awf4Y*&|U-=2w?+27ol8~f~td(QcpD<f8}jL2M> z?-MV|_tf^mKiFBkG*r4mhl8fzO2Nv>qfk}NS51T5IlDA|Jg=Q{H@ICqDYPjI1O$~e z3iB!Rn1pTa28&Wz)&Rzg&Bh^J>Y>+Hl^=y{<ch;_JLa$rV{zKt)LDm(kUN`msHKiW zzG0%V;1tY94X}(7__S~?b&JVHMfbyu6Q0hm8-Mf*vV;RGHJ4j+*q$lsCGE|*!6`O= zt*-)iUeEQ6bj=^phmmbb@GFo9wf{=^f@8~>Q)%jZ@}hq{{j6NK9-==La`{G`8ygY8 zTl4Txhnt!5pUgF;{8ME*pkLW@mC1=;ks<$kz0FtyZ+zRG*J3)+ZlC_ab8$ped~~5X z*jhT&w^pz_-biTS5UZKTfiElCd#d7k1lkuf3MF%9Zr-DqtCeR!jDfnkm{2OH@CkU- zAKJ@7uVwztOnJzzg>*dhwzt#nZk_+yN22jm!Th@Xd6R7Pn*Is5ANuIJWCac>KO$Te zR@B@dIjgj7e$Ko+v*(JrSnjZmyY3dJo0}f!H>)si>S!~fVDt|PkTL7`&>_|<CNMxq zANe!>I8kN&`iF)oM5RABEgG9Z{6kE)VO30yUn;JG<OfR!qNp^njZDI!ym>gO0{Mb` zYpL9r!VK7yN$4{>`VxJv+{6pJ&n{oKamE1`34VO|#7JE90jA&;7GdLdlIb1IW%Jrq zkmkbXS9|95tEwC4DjF<2c>Elln&Z>yY0c?~1q|t(<xhn05?>CZ?Nde?on+6zr`2d4 zVk28?C<Z6>4$d(-iS~eIrb+|h0l8!4x2EO+4@p72-W0zcO28tVCM+W&iIf)#!`g_^ zcW(!6RgK)xJ&haP&Elg@+nbOE5<$y5oR;h3;or%9B+Q?8`xC{wIc>>Ckg?^+UtE3~ z#?o=N$(ylZ2#u;*!I)IsBGzl3g<Pfb135cYdMDh7*KJ@I?gggC#Dov&zbZYQr--b9 z+rjjVi?1=Cu=CCz559;Ky<7aC+<Jwn#qUTo9u0D-_U8`{>X9&w79RR;^`OQ@uR$ax z{ls*hWX!*(fRdRL3aI*a$TWMDnfb9?M}rU-c21B1W54HA1}q@Z-BI_n;Svg+HwDvY z5NK7*CZ4iN710|cx4(pN6)&!GZY$qv6J_OLrtY9hD~-n#4E%sc^TN!Z_NTvgqsKKn zb-glS?nk6v!<jhZOpG`s^cR5Tg*0hFC?7098WR9BFAg?hLSjD1VALW0h#GUycbvgG zfuQAU!Mmq14ZD8ivJ52>C}v3w-WME_jbhG!(9M6>dw<iZOe_q4(aryK$^L&4T&91R zUlmQ9>|7j;Oq}qUnEzn`{fln?o51`Z65PL^<^P^;{+Cq#+i=9cO-20AbNkGHg%AJI z*Z*J%|DC>W(cXwX7(w`Bl%=N&1+C|>X02;L!sp6j!Djcr#2JLcHTt<^?`keps9I>+ z6MmmG<JC)2*4dSb#v;ZB1~sS|;nUgq=B(nP;;{3+^LG6S@280Hs)6vDHSzsZ@0HH> zlaPF#T!k+)u3;*O8rc{1SU_9++x1j$#}~Qx#tyF=?{h6J>+Ngen|i|0@2N54I(4a% zW}{O>=auiZ?fd52_dG0s+VLBR6a^APKb<W$AnWy>#OIjilYRQuVy`dMT~@+89z00i zR1~}-_=aEzPR$qlt;w@txO>}7A>p-2WT9H3p77D^{&%lp-tA|1u=(4$;|i-3I2RpS zRNRMG7lqnJm}%3ZoJSL!%e%RscQd<P*4A0?=81!4?2=CnS}h2<nj{1Bch}oQ9HQNm z^HJ1Lo<{U?|6YR3=T*az#d6f$XRYPNnUXh=P&q2#beF-BX-ChUdY{s|WE3~}y}6v0 z3KyI|6=M06_swD$`!HL2#85t7jQc}FpXS?pH$&Cd-Qc-nSIw2g`-4|avUDCkOU>&_ z>w5JB2;Ua#Q_^w(pbh{KVn&EZ64c}%|IqOEfGwUCi7dL1|B<JHi*c}e!Gqbkslsdv zzvaQnILEZ){2UMtKmy=-!S!uCDGbjo7s2<jl4g0o-ZX8M8G5nM(AZOh^1Gy5Hwa8V zBD|Tx8pXzM9S|8iaD|`L$*JbFO+OwkOFjFl*mC%@wg3huB4pHib(F>GiKc-RhP))H zAtGH5wo>AJsNAjrO8Y2|r|epWUC+5xem$e^O3f>`<s_AH$_(I3hLp)jxmDT~AP;+l ztov;4Pl6ZTTC6)ad-iW9g5P@vG=bw_jjv$c#F)P*?^xj={?-}}xHnE!z%LpgVTu?H z)9+oim=sFtKGA@Fdw+rUkS4u9K-Ge^s+3vapTN?MQ5Ei}m{jp<@~OttEV6@heSR~g z`glcsQj3=fizQjxSAN;xunC11NFYvV9skV~ZH-C#Bi#n3$L)Q0Rv0;)U;DL<FG(a1 zo!UQKh~No-mugH}$U5#NE~e=Fakw=sw>VYOlIW;cDll3i7gPiLvSOzKoi|lez*{lX zr9~lj0$bQzPf2<$f~R+`ZG?H=A)9TI<dxlq<@Zr5*X%S<o-uDP`g&U)u*oy-JBZs4 z)4<D2j)(ik#orR1O>L2~mTz02;S-!m;M1KoX)J5ntm#Q))Z@F>_ttVcIVrxYEnPB? z$FPy(Lb<vKq%?u@;CwSC@xmc^W{l*!5BKEaP;dxCK)2-KMul0-UTH+x;<qqg>2b|& z5{SeQTGz^4w;EbM)stzgV@lEU!DT<oPCMa9g#+#L(FsUFZ=s-Eg0%?;j0y1s8tm8e zwZ;d4!=3E|qh}L^+Jl*sI1qR)zLQG%@G2NCpMMm=lgSqo^$!(^PvsFP&P5)?aGA0d z(u>Ha(HHNH+Ao2k2COht#gc1J?L!Q?O=PJL4RT?ZNW_~u0*`WitZZk3u^Q=l3~>o( zS1?#rx71Cd?%~O<io1=*(@_U9kN@QT(+z1f-g`oT8!w&24yF+8jk|9(;_z!|^<i)V zsRDXGxcNB#26ziXJx;tGq}w7_H)+FT$(lDg>%B|EZclTL2v#q7fP(<}bkag%wYoJt z@8S!4&Qw|W=VBt=sx<{c0Wz9hYw~VW`rOg3^Qw_IFp5d!pDqWQoGo+q%TFf)gOMWY zV3pH6SR(;G11Qf`_HpV_3x6%gJ#U>lUx9w>=Q(=zf_&g!!t4iBhAS_SY#BV{A$bIe z9?k$%hJhCLP>&t05;m=?nWh|RO^P7qPz2ripFPQ~nAXNJU-0{!33ByHI4(KjXd09m zqxOg44v1t=KOoV-ZTk^;l3_khkz<AG5!`-56o++cZtm##^o|}7L1TO14UK3F8MKr} ztcdWCy$EAHLc^~Wt>N&Dd~cMS0i<Nc4`>@bHt@`UL&bVB@3R-S<DX#wH2QI0A6Axw zk=g-R$kob%Wj9;mJmSYX62e|^jy6AhSG22&@zj?9vFkSRz=YK>-ojSTzz3b5V>cZF zMgj5}?c>I9H>#xig_WEW;*rgZpq51WWUrE)%!%@;CVT?K9OR)LeWXbN;zK$$>;W0j z5Xr#=Fm9e-&cr<(2j;Bc`VT}0q)$yEx$2JGoP#v>*%to{;Glt@2;`<KC2sEGMSvs- zj!3A*!NE9^=w2fm1uK}ysqkNR3{Cv4sjw{_CZr?oCCHu(>L~XuorDBLXigbe=VW`d zMvzeIkc}pQdzTY_Bs6i*ES_slonU_w#IQzN%3$chb~nx}%6u)_Ip!}xjkR~Z_?B1J z2x(HSZv8$B5fNV*fq<9+5*wdQ@N3#C<r?C1g{>zNE^(TpQYM>cUIj1Dl8-XL_>5O4 zggn9o-!W6YB&$0Jh+ZJFOE{6RD1bZGhqrW5Ep!n?E?_tGPC;Dp41qals77(A0RNXe zznR}tQ@L`1f1Im{f{}3t<oGb!M8O)f<xr}zVY5sTCbZE{7|^?N(uC+K0+KG3()bBG z*0S(u(v?tA-Bl-3C8G?3EDlk0;$4QYAxeLnyW`Jjrn_v!%B+eZCcX{}SDPNiQ~5_D zro3x%vkHCX0R<n;lu64j+T)LWw)fKY#c3ZNKZilN^Yuv2k5Y}#ZYGq7R!;{Z3nh@a zW(q!DJJu}A?M7wWgcy9F1Q%VkpB2e8RM*3o!M%joP{zI^AW^=FZt=Te!Q>V0rJcfj z8I~C|$n56^J&@5J^~xydTFdmqWPl&}QF*^PD1F!Orj%@?7(1gyX1~8sQX16hvwT<% zMqL<NG7Vg88P~AGFaU*Hzl>Nb=I2WVLn5u%l_vyV1)~5NTSzF~w1l5J!wszpULw!s zI~yehVNfj*Umkc`olhwrE?OTUEtfV5SU08`({9L;Ng$o3KM^z>G6>kB&e+X{iQHER ze_7oALU1NFfG?fIK5}_6LMp{_67VvP4p~_%7|iGc6h+!EmQ}Y41vx#BVS*K0LMK>E z7I&qlnJhY+KV%UN9>dx<z0$=Jn;~@?Td^z?FV3fi+E1Rv4*S6a@;IT>FxpmKfu&l8 zPgbo%Ox^E0Gevpw+7R}<MJWyxL+GLkuSes)z)5D2O_?;?SwFY%VUz~OYLPao>*5#8 z$1uPyp@A$A449Z>ctHXtm_U?@5%RL&>(baxmmn~vP=t-8Cbkl2tH-|*7<QIXyIG~A z^;J08-$^Q_SM%%V52kwut=Srl!&3OS9;-Kfq$bgE_n1ygEtf~NE8qfzdcB_3(5s9~ z+aBFTYtQbCnuUdYMBD+)PFJOZud(GZ)c4blnS|o_PPO^rQ+rQ)3ZoSACCRAh*O&J? z66o+6TXrz0$_v6_OL%B7t|}iktwp=Ko!RTUy=AnOPuZ-x@p0fdH#J+J2!5#1*<=_Z zV@Iaqmo@(REj|e*OCHWndvXTQ8gWtKUe$iR{NTYbAeW9H4U?>&_CUNWi@%KT`ILVP zEy19cq_;XVibya$hxuC<U!700ZO#q$hs6X@JV!vxb*-f-OTUw`q?v2TIlwCb_J*@( z0PyYAT3NEO-vn*#wIn_B(n**>atNt!KOkKvGDZerQzQ_ePqy1!`sNi1<qY{F*cUx{ zhYuc2qN}}14{^KizIfrj(aRYJ4mk#Pr63puKLm%Tb9Txehf7Ro?Iw6}9_3bFH!3Oj z29K52Gr2s#Y9~32M-OaX?TWjQWdkmz+7pdkQlJy~_-!N5dss%WLr&B7XIInX7(KB| z*zAg$J``Udpjn5>d*{wf;~oKW0G7+sxe@Q|4&gOvQ^+e7(l0}}%{$7RS^=A+M!mmS zy19jLiC_-6d|TN+vsUnEJK!^Zrpdm+9||-s#=&Qpb}&cHHWQ5^udHzY%wn_aq{#l& zC@}DSNA8OM`Db4$ss3eg%LPYu0)Njjscak`0`c={IE#(HKJ|#-V%xvKT61+1Unz7` z7;e1$@<v3g%khGd!4dC?j%NyJK0{HRA)we3!^?bB3f(LKxg)*bXIm=WG84tpSoLCe zP*;%n1Y{<jgfSA6;8tg_)xrDw!0~yZtS9NhI5Qs${H>t~UBN=yXYF%+?3I%W{-mHH zzyanyo!WIxHltmpGO1#+=e_;S)dn04-5|3l-@B`mH&Muhs@yun7LX}KiCZBb1j*FD z+93e=_-3H5jL~I~w-5yQlS4pwI@?yegz*f*Wdd2mTqSAT$*AJIX_m3G_i`t?2%NfT z#<~r;nz8rS_B3ZEPZ?7W(j3N5Cy1Oi``(E;di)Cc-@!cj3`@U3V>*Z%Kez=APQ(z# z>bU}xN2_h?-p+7i`d!`|>NpIs7!4=R<H6?rc=`0_6C??0gHN=v6|Z-sH-4(*CO&z) zHKq9(&rUze^ws`~*#!>5m_LFl4YLm=7(k)SkskEg!ZrnvNyvBmCCMTy;a@P*)!08x zD7!Z7%C?n-Ge7$2YUQ@@f+_Q|<frC6h}UN^cVyZX`q+iBmFIHx)G2kQTh*VYsJ;~i zS@hmK{RSZn`1$Uvn*Rgk2w@>aDudW#-$hgYJk7XmhOO%90P|c3@3X-+XG0;>-kX!7 z!Y*wch<80K^FFQyGknS^+a4)GzGj@sIBipWB6U*`XybY&5h*}XJOr7ovnTrDav)el zwV@salS8#?5p(kumTdcK3F4OYB#`@BTv@IERY!#uu(FL@>VB^wmrqr$5=O@-;NJfN zDlDP%9Q;)c_)ckDEge4~b{KrGmQ`TZ{+d4&!!#VTKg@m#s)|%Hxm?=e_<eSFo8Q>B zm?O$dwm8<S?uqTeo)}x>%Hly`${F^#I{h<$hBBr7`AqN%M%cySda1AgcqLvZIo>N* zM?6COXB`*!VC|O|>HDwf7Hgn(3)erolE_s?@77=~+bMgPmP~Q9HZ84TGQt<Oi+Ro| z_3|j>0yF%~aAnd|tZpJlx~`mmckJ3%N4D<A=68hd4984i8j{4XbGv6gwy~=0Yrfl3 zs_k-tt{;$yJTOg?vicaBk3sB08iO&+^oH>7+IW~^gyIenSTp;D>%~Ydr819Ye*E!b zC7pl%0{F8DJaLZuM`G$ks`_Bv5|)c9h+L(;l5fShwORl;N<4Wehur2yTm~~ZLEt42 zXPSv$&dTC*Bg%NWB*T%biRq=soitI*DU$$0u2tgb_K?XcP_guWMAX0*I*zJ8OytX3 zk6^BVy!=w{4sviNv<Ww9H7*T8!J$2sKdNUE;1a6dF1#|+n}tha*&O515?q%*A~f62 zKB!)}5?<%JG!voTvT&1iT()o{)HqTc3taHET{M(?*GFBk;G?squm_5L(e-T5!pLuc zXsLV=a{ryZkDY0euRvWt+So*hHpy64e;hwWQLR$Fx7yJF<z$Ut6J_Yt5cMj?I!4&# zMdvb+e-C<tOKL@*GRz?HXSYI>$dh*B&=|O9G>Ebfhhb*CyN!1=x*wTwf~#NzXhhBA z0eGQLRM3hU??5@<L*Y#v#`uMe^&zHl@@!K<i+G*dRfj3u*DmXgTD3I_Xhmx&qm_0? zMR|&#&3d~at=G~5r@3h#G78c&I9jT0k?89)5xoaBa7UA^7D0pe(UC`|p2%7AOCRgC zylmm2%^`Vr$|B*NmVPmWR|@f|d*=&I<%HbfKk=o1TH2Ty**O08{NGs<^IsQI{*y1` zKk=o1_~8DFFZ~^`{7?DPKYb#9BU}IHeHs71@}<8*x_|Mde}K9FjxW_{|M73MBYf5B z6~yW87N(RxTPf@|ZfMu_|8N^eeVs!NS+1I<JZ{11BW|O4Q@d_^z(^FzKs1qZg(tpU zUmhgjpqqsbdtdur`o0KzNfH^DmD;nj{O;*{(A#-6kvAnw!aGZo&<6HEfAvrVmcV#- zoBev+k~$Ro+A4aM^Lsid^=QA-i|D>FYpZ(w>d?Ka)M?Vxef_?>{<`{poPi5!r~arz zXHFag7;KjS+}zCLTkzU$$}<MhrTK7!hf6#ZkQhu%e=|W$i|?N;q8}rKrUQv6!RG~j zrO&EM?K17C!>jF9BZT=}J1~8eZv1urSlW9hayHw`T=lu!t>shAr`=MhyJJn{*9$`a zxi<4z4J`<rY9bOp+>v+4a7gJ*Etx=yi+YtH44nx4EzH34V-2d>Fx2fvtZyi|i4iI? z{b(k6vuSIx)3+S2uDeUFSs9(L^mb67m|QZ5o|enLBD70`hVPP3wd-xv^1I>d)T4); zKb0w{nz-(Ri?}AdFn^3mdLxnJ(R|@^EwApQVLp-^PB_yg;A4y^9}f%MJq%^Fm&YHQ z&NLT9B0Ih0F<J6P^WzBMeyA{#5U#LMX9-rZtCIL;gAWJ5%Ai2q0RP)Ghzsp_t{^#0 z5H6uACg@97Pl%!kzVfZL7FD31&&S8|x7=fz=b#;b(s9iQ<>kGsrCx%eR7h)P?Gw&L z8Jp}oHM{PuYff*>(^`;rz7MOs-23(en{#ji`lZqdP7a6Ed$#yxW#Us$hx~grf%f{d zX+x!}&L`*P)*Uf<kviP~!YAhG7Iiz(+mhv#dz(RbmQ%b_cNSovgGYD_waxjKE!_fL zS-RP28q)Z3-3HX-B<4VgUR%}YCtySRS^#th+qH2s^rsse81oN-HUN=dKUb8Lf07O4 z@7I|^(bS12v4=+n=FJ@zJ_oPx3OL|6U`Z2?O+39rlqMY8pY#ws01+dVD^MvtL$DhY zDh7-heqa%$SDIsy2xJ=z%BJZdafL7NCug+c5#k}^6Cr>v<J>@g@ghL^;t1T;Lo=9# z^aIOyND-Rf41?n!079wiAhh899-8Y9gk}8+nc#yf&M278j;uM7>OqtFem;92b}m_{ zYs5T99SN|~$6e!EWpX2YfC%AL{;j?o>V27OX=?fXLLU_#R#Eq<K!@Ag7MwI9Q{^Z= zF;563XfI4^qJWz0z#5TULZZq(%VpruTizOfca=o8W<?If)k;0)f%Xnf2Z@BA(u<`) z1MI{g^(j8+K?;UhOS+)Mw`~6@T;(?h&hfd8R9#OXO*8|l(rt%C5dukFhfrCyuhV#@ zoXH$yVqzj^@w8zo;Yslb_=7!grur#M^bY&=cWo{oP1fwYClu{a<lEbf6I@cV0Bqp) zG-RmD{bx!VKS|cGYfG}V2F5Uh$<n;7+tOXZ`dG7ADQXm?;+3mtRtiD%FMW08L^IJK z5nN;w$=B5)HpUup5K>qaMI^R>Hc%CQ%sI+i3qd4^LlA|%jd1K~WA)@G050~FD`7$y z$3t&cWT>(!DKl;Z`kC%|LU4m}T46a7#0zqcu7d9{#_Vzcou306_Sw7XtDLTIgok4k z$0L@R+f(icDL`>qq+SSE3Q9ZAIwm=cCJ3Se$OaRTnpnhkbjd15&PJuxcZ1D9!y8?q z-gfZ?(K84Jofg^7bL#XaDPZOY9=ZY4s0~{@2156uGU`WRIOMtXM>msa8kigH#Jr-_ zrI?{w%o5nM84lSI_%VV*Ne5mR)CxoPiy-@HB_7>SJ5DEOZy&z+(F%kJ8XUpII+t0X z2wwn>54of_@dm2tGE25~X6<Fe(uUG%{UR>MR8&Zzz|TT0u4|-}p}kM*mCj4N`hqQ< z`}E3i+yz1|nfa*zl?I_Fz`A1PBk;mTp}98=2Q&2<$rA7+7Id6DJS?H573L!aoj!hD zcCIPKca-7Rs%Ko=)_p0`Nrpy2ocV9<`)Gb%9V;wCZv(UB>lt=C%2^?z1g&S|o;VY( zFWZ(4YAPZz%aJRw_^)JeO;v2AFJbP}()SgNax($}Zdsq+ZR^qDOAu>dfO%0ixvFe) zX6qtI<t+vQdC%EzU>X3OA^nY*&U)9CetzwNqKsgcD$tNZ4+$bn8#^RH;du5=__NhX zWO@B=OGuhzNEq}KLWG8a(UnC>uFIknC@={IJ<;-8WTEBSg17zDi{;Efz!z!5zVEHM zL76_<i<IX66Q_dqeAny}M-OPr{w6J=TQWbJB2U5Vm>nO-?5Kh`3Ker}DanjmDJ()O zDe>m`G(VG=qmm&X=qMm<`bi0K=J<wrMd$c*)LRia0Hh()rig9GQLm_VlOYd}%lo*> ztU#s|9_Yd%Q+Bsdv1eT<KY=V@`H3?Etr}gInOQy;!gF*!hiiUMA&*FF4>7<RDemC< zlkvs??*ouY?!hfxa5c)324q&RnNz1Xn&pyrUz!iV7s(>-hfl_ENmLzxmx_`eXWw8D z%OlWY#hO`HJQPC^6&?@oMo*)1ap)r!T8D6^r$(%y1pZJdB8~4SmJVY6k>gCHBM!KL z$kL2WFv8gFB(5L(x)0w7R160mDuK^si?kt|$p1Oyj+pKbY?v6d*CYpM5e~0Wj!=nb zzeP+>i{J|!d&o&v=~g~_O7G5}@={~UML3|K7+^>S#;#MCI9gi7jzoeKLJ?(o0APV> zA0nOeAh7=F=A0aQ)@7*ZWT>!>A2r{Yd32sgE)<b2Tmc5#_TUad3mP`D;A|iCrX@+V z>%9=p>?tNt5NrTK6VmxdX_>Uf0y((Q>e}eSQaY~QQ01K&y(CLIu`iWH-nvbdzvZz# zI@-?l*;YPwZlhP~{5tL0mpF$(=N0g^z_m>Fm=3K^i>jXsGtpHT5Z|DrD;3q~IXKyJ zdSiCKKsU%eyBTC@gq64hJr~U+UJJiy*4Z;gmrUERE<YfK80{XFoXaHcK&S}^tOck( z&;dvwm0-o5Z4pY0#p92HCwt7tVO`wS{sF4$?OYqUu6Sn*aC>>46_iMehUq@=@I43W z6yHX2B#{Nq9-1@CAnP}GV<2=}%p!of^(;^eaJvX<)B(ClAeT*JH3dj)0vrcpbQ)c( zQKN9XaK%NTPzAB~xh0;;E1N)%0<K1oUN*Ai5VbNu{@Vhg;*AQjJQHht$d72NJ)0Vo zCPIj|@+S0}c5K{QIJaUgieY_>BLil5ASb1|zRN*&Wqq3=(rhyZWYmeJi_^4rWJ_H> zZEC3<i)>#`ugd08%jv|iJ+s#5LLm*PR4w%e36)iD6aI+-DQ*EAx(I3*DK9f=7z8mF zo_CZ&eRQ<-=<8R9Mq&UQMDu3{82y>gdOXtjL6bGW_`HbG6j7^aN4>MSU{P`ziL`@s zo*BO1RY&fnB|wyGpzNJbLRAC22Gq1}^e@2!OKS4vfhg@5q+jdX615|N2OXI@I817P zj+aaQw)}Jac5Bk9<wNd%W387I%20l674SGi?t$eb16Za$-hDqwC#F*K&yBX$Zr3t7 z$e%!K{|-R3M-Iy|v5dSuvC4r^qmzAjshA+s3eu59R{QT)MC9yAd}x_1e%y)aOeWC( zfz^qL)zF4!G!>^412e4Y9#Q2$q`AR0ytHc1t=3d!^n0RqB(&4G_QG;Aysd60%4sWF zd=3xqm6d+hGc)U#*byu8tY>3h!QiPk|DWqHwU;2IvFGbgO(o+Uo-P)5WOOcXPoU<> z-ZxDogx>85C1!^7Sf4%-X26M`YtPuWD5*V8FH}7_Pn?<i(doHcKf`+C&s!BOP)@|4 z4Su<@2Apmifti$GVO=0|h`;I4*LB%Lnajep@aA5q?<%jq<rq^#^UKD{*|_QsgGJ^n z?el&N#WrzFiq>yRA$DQb{bYEvCf9+&OqT|g?!!5%SGVRlD)I_#te2}6jCRgrh2nPp z$Y0k3_LSY$-9AFi?RPBQEo4R$u`6mEVqMUw^B<cIhMV>_Lr9I0M4Eh=8i{I@VrjA4 zV!<88bu&(QGUpDGdL>fDf%3ZN><P2PQbPW0&W}<xW95wmcpz~zE|euVWRhsB>m%zx zB?T2)>@ThsK)3F4to<%0SiqxSs<GSTY<E;6!RPs*HkPj<m$v27<g;yx%%h#Z3h}LP zW$}8*09n|uouKpg4CAk+vB<J65-7u5gJdJnZ<<}n7z+zZwmB5iD=yYkV<^8r@w|ES zYN1p1LA;$}CF@;O0O>ywC)kb3BC{D@3bquv!4f8*W!=y@D6u#Ns~kSwlK6K8stCb@ zkxsEzJBek@z0RQRV{4QiGrB`*Dn&VWmX##m?R|?J=o(zkREg^fh%fMU^v@Ygl1$ix zx!0EWkcyA(Cm{F`#E_sT)XShQp@Rfry7=|{R`NM7e7adMqA;6D19Y>|UrXqP;4+bH zy$bZK;U!#(`pLKwIDZ3^iGLo~&)*g>4N)L`bU^K7ImW59CG^8^5??;#$sxi^$`@(~ z4Z2F}OYTY=8;pXl3Um&oAGW9@2W&CQGLv!w!6~Gp^GC_s&)h*Q2O~YR56J+P*$*x+ z%1I(eu|V*j<g^=-cEUk#Ni0Z2@Y}Jj6K+tKW*MpzDgcqh@IQ}4OXrY<XIV<6J1<cW zcyxxh>%^RJ+ukjxH2tZ+{+U&mRzPo{n^k2>3+(p<3e)C%-B;3HR+lWozJv_!7Q2k} z0AJ+Q#19oneNb*?)UMAWgeX)2>2s(38LA7|9nXQ_Q?3<vDzp?&k*80Ls=z1vd~hD5 zr|v)L;QvDJC4^?P<iZT7S$F6IdZyt{K>U_SOGCk+OetcgH4Q_3bOjNE(ufi`hZ>Wh zVYF5E<eVpNYWe_k7zhV*apo_@k=0g&>;^iWLWZ(haM!6IWai{_)w)X?SGqq0ueYT< zdFk1*x3ij$*gx$Xu(Vv$^k{3V)92U*FLF{EhqF@WEAN#OnyUauvjTZZfWld~fr`+Z zR32=eD)(5*U}5)Og&jZRSjwr#^Cw;(>ABx)xiNF}gfGY55xls7=6JWpI&qPE0{cJ} zV8pQ=JG%5+15%Q&YAdx)nO4_u);9O7V^hse)lRq0o~zo|y{(pY<L%h8&BE8L9(3yp zi}<v-Ipr*kfY?&rE=)+xb7|L;w3Q#O$W)ZC`ik$@qhI16K|*RugS7&Swp?&hR4^xF zW+M$ziQearBBHDppO{|rk9nAFP$h#Gb-ISpdG(5gK22|J1F^hCNnBl6et_W^fUafu z_dD5o7d-SDZt+$T*Hsj=?`8P19AdyvA#WR%SFX1Zf<<qbCEpYR0$evKRNn?ZeoA{V zQ21H}Q;QMa$CYm&52PiR|HN7T*(1kH&-7nD9_GJJNBpmHmVdZ*{=!-Qu{H5O$-glF zHC*z)i?jS&%H`kkFaMT|`4@Eg_xuaXU&l%Q$xm4Rkwg9O_{p@+zxatSMYl+0xTl)e zWf-wc0-%6H8pxH95^}6s){E`Rwx^pD2liFnp8(7sfD&;xwmeyS0y4hV^}C3G<KCVf zJN6sL7wd~F`nF77LVa1n-F)DBQ0HZzvAU>J9P(4@ol&Z)JX%HFR3&-I!rcRlx9>wE zgtEIE_6wo*%X3v%C%8{;f>OI@-46GUKW6aFple4b_#2w;8};jmF}kR^b|CLYz9ivC z=&>n={iMB<6d#oBV0HP;8L2neZEjz;+TL&6yfVJ$torgI^$L0lm1Jd$^b`x_g9r@% zdFCQm?E?mhSid^}KPb!KFXa=tgwwM1w1*S+C`<1l=aL~L+Pc9%YpHiTBKEI9F#)F? z2#|7h%9SCFbW$IBOD80qAq9;_)}S2PpT7wg@qcg<H%~Jli`gh`P}!b~hKZJghl-X- z7B-%Y|H8tjn%X@<Rlyv#z;Q2v67l%Lwp1K)X!mULGKXC`ml@=1x2$dJy1XLl`#xQG z3()K90GF=%J*m^=z_+b;54QZVZ`5Q3dyeJn<=4i$*(J7FiwC<wH;aDp<!~Rt*WT+^ z_cDC`+kQi7kD3T&Ir8-mN5%|3>{{2=nKRA+-vR{{#hH1s#xSgSo08IiL$pPUC?8ZL z;RI9z16`>9c!9i5(hM?Zwu(4#KhMiQP`^RNPe7}>G(`d#juLaaW^R!()7G*K&C<GB zY`IB*s@Zcn#E?bH4%!R73!`zbP(DHO##%;f^QkKR=H7xedo*F7dZy8!@A9;P$7~Q+ z)y{u4w17e(HGrNWPAC~*fO?)&((w1!fb6S~lb2hQuYW{F1t4-ff9neuaH+EF;hfME z%|08fWw18o&miOr8Dv|hrrt1}CgTQO6vX~iPacFN$s-Bn#Up_Ia~r3il&iZiemaZN zJhFGqWd+?J8J-^<b$+h$opmqAL)hh*R^+PGc#&iS4@<Pn4#?*!H`01Zz&<ig2n^~y z3!AU1PQRmA8p<)m=kz4c8Sf`=?=mZ?Bli^5C1_;q%lu9OPC>TGcTZ=M07+5US(?l@ zue(O!*fwB<8qadF5vxBSXi{-;rJljSSPUhpgDMbVC19=Kc2OGi^V-6~#55yDVlyQj zsS)N(fL5?r92-2F^g(AaH^y-XTf~u_F+<fV3&TFTAP5^-y?A~X5FzZ;ahj|0pH!<} z7=%dd1NxF6|2pr)8t3syGYVt)**sDiH_X`EcY+^-+aLU~!&ro{sI-OklaHbF)Ks`N z4OR&{nO%{i8wi8OwD~?X)mT4`c@i}KYgM4phh>}LkZTV|t7as-7=kKFhl?BS>hU1P zshKq|b2@H%Z_w9A|J%Y3OfEXrL7x5+kl7@bwX*xqf;9Oh;+gVK%#@>!d!ixHqrnDN z@0Dsh-P?)UEzbQGa?)+(;uZ~_?#QFLrbcM5u>gmVZP(B*Xst>}TU7`<0Z~M1%OU(b zLjwk{;*Oui{ZY7fil#X?Ki%QCGR3vBUEyczVkBgJ%||}3FqZ7d#?<sT)CP;-L4s#& z-DcIPYMQ%gatP~mmK9$asvrQ%DnrrWW>t{gTl~D!N0ls4jjqA(k{rnHlrsZ<NV*d< zt^?;vI$iiW0%IB%8|7ja?1h#%qRXMG@qFg?JiXah?}d_TIyGo>#*lIgV7y)~VPb7J zxUMpWfVaj|a7&YJc1Z69IM}5zg@AMYyg?jAMx0mEhdgDtPZt#S1T2MjFk=b{XN{r| zE+&x<us2A}BZa&~&AC;n5eo>>1HO2vRKkg%#m$077^2n_1k@6T*BLAMm>b^jr7Z)4 za6#oV$L}g=+V=r*R42DR7XjZ-M7p)s-{ZW_Ea8`y1V-Q{IgZGrFSdwyF~uDmPcq)L zIgUyvF+oN{$?gpqOfLbiFjXOm4A<DX9LHd~H5;N`bwVx@3*R~p-#Z4uw}O;b6wX({ zui67W-+PKR*clXx`Fc!vnnlZRSBUeMN-2|=wb-`d+8#Q&teHnZ7|L=&Sfl|ZYSnUx zITfWA7WI_E@d=GRx5aEbK4c%r8-7@q(YJk8?fPt##hlw7n?5TJ7f$~44Uy$tsA|$` z9E#PXWBplgKz4XKdA3qy8|xN@lCh;!?MfkdVWJY;5#6reaSxE8xvgtc=03z~zlaw1 z>o<*igrb6qU>Fa?ajiS)>H?6_k7GIy5<*joR@2;QXs&>Kq-<ps${%JD;&N>#B^=Dz zPl@wJq#i@2aZx=@;mkWkpk~LBsoV%Fw}b8Ic!FvQDhKVtqFMV0StaORW(u@obhL>K zxy1X3VmR1)MxR%gIbt0{vBbthAjEq=8a;!E;Z3l+tZX?1fwjgEa7p9#7Im1Mg26_5 zbY>6;FQ{YV<9h+c#N#qX5eN}GMsPkGeCJ<{B9)bd)4lS!k2ki0!!0AXY^00N1#Y8- zdd$YtD1yIZ<;@_>bM^&(T<;Cw!0o-ph@`G4M9v)p+z6*mLmtAB+-MCOWb-#_KxoEA z6+k!;Q@z=r$f%+zWZYSR%pTNr;^cU-JGP!m?4BTj(F2DQbwP4Pe0ME|R*M%^?q)+3 z8)e+X%6_tE<Struk+=LD#T2(e&!)w`TVb%vEpDnQdMJK85Sy^HBA=BZqxX5q&|5n! z(sw!ecrxvIP2j^fBhbL6fTB?E2qsMnG0vTcU?6J?FXqdLJd1XfZ3eo8nME=iyZNzR z{?JMdBBs*3^ot^R;bGLSOWKdSdK-YDDMmVGJU#^Rz<B8);?-5cGCFR|n~{oTB+PIx zwRmJKd`2cC6Z2ruLC>sY<VD_B@3>?{JpAWb3CE>xz`|A4Z%c9oQ)xy?%{^lJTA}xB zU3o)AMoH9#0vEhUu%EL@9C+j7iq`sw#w(axR}id=Cc4KX3O@ox9DcsX6_p$s0}_rz zuQSG=Q-W)!QeOsa?i6q{BYE8>sDJhvD01;Bs)SV26f$FsuZm>V)ALfAEgX8*vx1ji z4(7J!Nzv0sh?wZ{M?F9_;mB=|c~d`N@FKl)hlt!iT2C>jpBUk^y65DdS|&oIW5`r7 zDvm9-I9r39Us}!1QkBIv+v{}X65>(SI0mzENM)+niSnjW@4HPeK{*Pc9-3r)6wd8y zA?i!MZmzB5;mJyF#N9WC4d>*xWr;f@LsK6?jpZY`A+aK>!sg4Y$`Tg`uy6xv(6;5+ zqq13Q>9=6{sG?BC9CrC??r1liuZ>SS=2pp0WoD(Npn4?R3vXKl=B2Vwm=XgeTDDL< zC3{oD#j)(!H0`iKvkR)p@ERgS>W8C0o_Y&U#S~585`)s`dyz2#iQ=N-#j%T7JMyN+ zP+%=?NAi(r9}b<vA1{bSrAAsMl`3<s;W(fj8ai-mB-;_Pbl5k7vj${b{In%>drD6i zC2uVuA-*Rev8#9=(hq7`2p15&pyotEqE>{PQvgag5VY-zBAqm!EF9-0h1=J5gzxIg z#t~lCc)%y{0r8E;WunTwkb|R|yF((waAOq-|I8UiAe=``98)A7Df8i*@1tOd`Nuc% z4!e&e(Y@(?bB!`*&AEk}0Fd?&0?vs-&Ru`=;E0=GU}hfz;*{zp_#xecn}Cq#hg^7j zlZT*i)-ckaGv<mT*GBP~yh&0L(e6UP#n*^lGwI4q=rg$8?D|CdGkFYb2i!3n-v!TP z%zy%C&myth(Zp{YO^yL^gpi#fvb{Q7P!RIq$fa{Z%i-H+5^=^8Zc{*Z>yH{Z(jd6N zt5{IV%^|o%LZf2XK+4HSYwfiLM>hwP+3)6*U<;UjUWvSwz9KR11LocUX!Nx3F(i=* z_lI!r8QihUZn@F_hxZ`y6?_1JVA0FB=Po#uGmS*JPMr&%fO;dWxZu-u2l0#p6n^h9 z0A^gZ=QGM(8PAZW5E9oBPw7p6^<Y>TZ*VLh68`X@Ou%c3sxl@%Ss^%*BZb1aj$Ij_ zfUN>e0zhJdal}!k*0A65s;ONADOT`ukn%!7Ig#e7Qm#FmyNXiCS$P|DnK1B)sMTIc zbH}s^3`Y@9t+Nl_J|N=!3@-^qm5^y`Wej3tfWIM_*I2;5EDy!0%vXCI5?m0hSm>B7 zdeV)Zr3oa{_aS_gl!#4|#d|`C|GQ-xkHN0?+4)q@uT(~DU_<}WO$SKdFaJ#P?RjIX z?03D7{dFB&Tl=(u@AFMXhn@ZQWQUA5XDBv7_TD~n9+dxHr(}j=Y&Hcz=svW{%wwM@ zaCJpn`@Is`Cy2n<Do2)Z|FE^U@N&8C2<)6JihIMKN91A+pN6aele5U~dxorx`&h|W zd;ZJiL7rNoLt+QDox#Bgr$7*_6f6@xuVoKn9uh{L7Eqn)L)(;gEQu@*ieWaxIC$w? z+!Ra%5e{*nsTV@Gn{N_GL^EVEJc+oNP^`3E5jB%}9uqs<v?oSNQTlA##kW9U&EDWp z5^&{JdqTJ3+#lX67t3u|{Gjs+(=4ms2nc54viunXz^X+P)h68qidGM=&=#DF_%-LL zp<$LaCLFW^g_P47WYLu^qGf#yciqvga+5BNHZ{6I{2$3$lC-=?(X3+5HPBl?Gqg}+ zoB~!O4YI4vIMHRNnJ95C0~(|JMxA8ghzd2*$*`7~U^er!ueBUskD%!#^&@uN6JG_N z;m8anP`Cx!>T<GKN{8%r!P($ti{xkvK#j-Wqo-fZH}a4h6_0=<juXhJyPdN46RFHO zO7^Tm>$I|FYEq)?Vy+>qy5MJ?BV)n8q8{??4qN6xkiazSi-5`?pAogFt~x|>3Zpjx z<Hj6JZ2v@fI}vq=RQ76dD)S&~6KZKHaI#uzLyGmbYyG;xk+DgSIe1$07Feicpp?a0 zWD!vJOO=?GI)$_~>FBQ?=fOo7u9vwcj(_prWW5}1eSP`9?~U~Ac#b`ndsk>#yJcmK zR{hX}dYFIHglwxuI()CM1A#4RED0m!uUSIigw&HlQ5FqVV$tA<I8nGuN;UEl*oO{l zD0sKGKH0IeyL#rTDRG*@rc^9Z7xvs;p4xh29hi!VTfA<Wo(|yqd_BtjeK={ADdCf_ z`*?5#Nrg-Kd)s?u6K5A_W544HiSBv&k!9=14$4Btz|tuVOa$Dkr)iTZV2BZ2M=<5n zxY!j@w+ly|>Lh*M$a$1u6pW@<M```yBlfvessYmWhb&-yf&kJc%*2+gxYHazgz6@+ zJPIp@xqOG=P?=?Ysd7OYr~qF<3I5JKdJ0g@9d?6*Y@tf?uL5uSURftbu+Ej%YqWgW z<8%VH-hjAapszPmIT3f8j>&}Iv6ImnS0A3|)l+P4a90)6TVQ&ISBQ$En|d5ZUwD~a zbPI@1XbGB5el3feFkkr{Z3eJrTNsk_D1#uW3@Ob9{WK1(w#?=7G($CCLb`m~z(#+5 zc{@`b`-1P;&p}mH+jPH)&!0AEn{45tYTlP{Ta<KEFFfjEeSWY7hwEXu^?SNp=5K_r ztf8jkGs!HADTPmxl#6$Dv;_GsR~3m3NtnH_*a=v<&QYjQb-9xd>IBTc({6W#J&wl3 zbgY!|lvybuZ+a*mo`>-0#pN`_R*mv?_HDH4?p#6oW=;exp?y6*1}GxL#698ZSgdw` zb(g&R`<Tr_h}psoPXj7h=A~&<Tf8j1IqG}<ful)EBFaYh`jx+*D|pzquYV>D%A}o@ z6PtytiRp?pJZnO0+HIUXMyPpR#p6|?ucn^)pw<dR+$eE#AbnJ<>$<s4<Cx;@jp2ka z>qJ<e{hE6u_F<7O;O}C)=mu<DL5=s#qaCd-VpE=y0KOB&_T5N$=N^gRZzu}yWg74j zv^aq^T1x$F!vzU**D@r7!!~d!D#8h6u*8JE!~)~>Q#RYVgPQpvwb<<P;?sC@SOCs_ zqlD~;X=%^=CN5GyH-WF%M8||&G3$VYHvUKg`{NDzKG*RZueED*!~xRBCY0eRhcw*& zF{Jm7KkIwz%}B`2#2w)A;-CxMGYpwtbz+N}bBBEH(=w(Yhc=ZvM511RG9JuZK^m{u z2|wmU3n`IUy91mr3P12@jKA{M0m30At=eSg3r(}o%>F+hx4)yJzuB3XSvdZy{EX$V z^9cWo$c^Q1NwGghrvE~2e`izwLu=FD&+@+|I`?lh?JxZ^|AyTDJudez=JvNB-M<zm z{(bWH|ApiJ>dg8Vj{8R$&wq#GTC}a~utrfnd;1LEN%H;OdzhMLOKEn;QnCPPGSx-= z7X|p0s4^<VCx}f({a8P#c-*$TxTs(yutI4v4>vown2vMbF?;0t<aWw9zLcZOTqYN6 z(!SDb7wqa5SgojZ$Kf6)1(`K+nwn`dtIb6`dnEJp{0u3i>g<xfi<{Ioj;2N4qE}=% zy*f0wYdar(d-ZIW(N1SiA9oh|bjv(Q*GWIaB{Gts4bt1l@!Mq4s66R3N<@}+mwetZ z&jgY<tt>PZmHMcKp<w1Sk2Hqqdz6L6MHI>JOj}p0pC=!U+?*1)4@`1rY}>L>PDShR zTDL?dCY_%QIy^Sl+>YJ0vY^V&?k91i(73=$__{<iTQp+FD?6trS|y`2`RhoJT$Y<n z0z<k;P?`$+`K0H-Pqy_@sJesr-birUEvu>#M6V@Jc{cn!Pi#9kq7L8GG%2U(%)h)9 z5sm&>r(N-D7x#6qkKNq1TDx9tAMLLpQs6*XBvfAABo&sC-TN(x`3gQ|&HOsWi%@JD zu1Q#1zIRI!gJIt7I|357?CAPNY?V2lPR<;Bn9{&bzQSO!TCws-ngrfB5Z^c+sSAGw z7`%!D^{Dz`a7!`voQTA3)!aM2P#389y-vGtIb7XIf7Ot-&}gP2JiSe-oQL>*ZJI}s z(Spl3lF;f2fpD7D(eb#abd234=4Q=E9?++jQCIyi3}Flt!|cc6EHW9Pss$^qQZ_bG z68doG&pCoGHWNpVWXR_WnwPnRn^6G@j&FQ3iOGW|tD$HD1^AWvr@-_p$U`51lD>6_ z0Vif@S>JSI-+hSMXRrwqYG<Kn+3Vf${^hBcNy}0igJ)R(R~4z1zFbVnjjAvPR8y!s zH|FGgBRqyxz0`WEbnKiKG}U7NT?Dcz6ObSpRrV^jm99R)Ip*l3Gy^t26Sz7Z*PuOc zM$Qlumk}mDkSYUqtf8^Hp16@_S<~aenN6F@x0Gp?*8SIq?~cvNy^k3GF?(9mqv_|x za=@1NZRJKSvD2<reB0%ke@^q6yO(ULwvW;C%T4{-RaX}DHTVytyV>b&-%dZaoh-Yn zowc@UYGFiT+k#cePrHVW20iUSQs^*{js(<_{`ucFjK)R7rY(f9-lTVgIhSmADdJ{- zUSy`5N=@A73$`^@Rji&fW6K%6bHh%c=$wm5p2z>=>A=^cZ_|%4?_mgi?H#riMJiSV ziQ{*VCL)2k3+nUzRxJ@?@|`VPv&EfN*OkH5v{dC}ND|&e3xUU-O-Jk9BbZ3_{biyt zt~P^05ZVbk$sZ%L-@CDmD->4e5FzTzDQQOCQtke<4jG%>^JvtNv=zAF*seWYF%1*+ zjAXnJtxq072w4*HOg^U_3t%WRuKq|pvD6-qL+F5?_VzWa)%cDqVbV$To*ko5A?@zQ zYbWMjVqX%w7?5fR6X!mG1g@W+PmB=h>-+F@UDlE3&av&}Ra(~7(^HsjtP%Sr^+C$6 z8RPJ8{kv+E)rNfDCW5TNvh3ucpUpB*PvAUj(l!9OAy2z!ScL>VhU-z01<8p&+tKXN zJ(c{G5gm(0tZ@w;bd61PgoHCv2AZ}rjgk;3jQD}_GC=)jt6TDj9b~-)p(x4N<A&}j zksd9ux3N5NL*5IagY>rS##AeGfQ?4j&q=Tg20$@>6pI;(bDjG*fJWoa=e*-xS6~&x z1zSCzqRVaJ^N^1Vb>1(=iFg`LHHJM{$gSf0<i|aXB?8Qskzf>ukvzu3_H8w_F@i@q z;=;^7BCjLShs_df-M6yTPpSg=jqu=c%wFe|l?TWGWfG3?tc<xbnXBCbKX(fYt0$Lr z90xU+8FbN2R$uq?#0k3%8z@Xd;JT?tLk~~pw$H{~_1-wm;UpdwU~$(T+?>Z{T`#k3 z+12sYsBZaoX=58dB)p@9+(SA**;P8%`@dn(6mcCQyTpo@4Fh~o>r<NCnMY$|0idu< zI==KV18`u7IVfd|CnyX?3Ng_~lmP|UXfVRNn@A|E*DZpUB~xTVTR*DX$iP2Td_+N5 z6CsLFmb-Aqw{7ud71_AL#?6SxaBz>a4-O-MrvO5&c$vc}Wiv7cb50h$sK5I+$16l@ z%dgM6qyG>Mofu+lNWY9YuZw!<$w6TcNO&KZ{b?<p7^X2gRy6%FpAKX3yPd;dgE$dO z7dt@X(CfaC1F%VM7Cua4+Z06+nG}O%Xe%jQ#b|c4dWitBNuhR9DZK?r(Uvb*L#Bz; zJ_?kEQ6*et$uKugLvQ^!)LQO|Bm5FV@irH1m0`S>|JS}0l8urHw!}eW*#;8x#$9xg zvC{9cM$}T#d*o?D{&9h(@ILhz<B+{0iA)g&VQ|cwLuiF~8vH6-X#)^}4S&kKz-nAC zR^uGlh4@PwXW%8--*oI8EG6Pw#Y*){1=q6?v_r!RHUbg$w%lI?);J8JiZ*(iW+RvU zFBttWl6Uc3rR&?Fz6701V=$C|+C*Ts02Q=il}khn`R?vdG=$PXZxB`d)lYEK31B-9 zZPZ%Jy|JB_R__a}T-X6S!O)-SoiB@(=2!x1BvNo|xig^Kwgjv@WF04^4fa{Ll5=5U zZY?7_pOguj{0mdKNCy=YvxMXFIe;g$Rcn5RCoJGn^P-TX+(}a(Rf5*#7U&m55u}Kl z+Oa9Cu21ZPTdFJ~**6sf##OuSmY?1w<fpZI3n$d^6AfASmYRZkr|*_{gJ9fakpwcX zJZNyf?_iU_=_L2ZO&3w;&yh(DO{1wlJ3nP%^lD#oRw^h^g!UFr2tG)zsEW%eCC?*$ zY;ReKlpFkLGAOU^<Y&(Tm*fjv8IS^DQ^$wlrk3bwq$0s0OjY)7+rmUZK@OlK1fofY zgaqWJJOTB@A25MSMuc28-V_}YGIcm*o1`|7DD|I5GfNlYD@)T+35=T%aCB$fRg1Tl zGhq>Pl*R)#gFd>93?cCVKF}T<=MAqbvm$Rq8Zg!&rNXNRD+E9nl1tI#CD3Rb#g|VZ zuS9G)L`AIUH^->2&m~5OF5ktP2T-ofIZA(k`Q@a9pJcBe${rXn8kKn_v8P=yk}F5A z3YMoYTBfX$2}`Ui&4+;U<pLX8psY~Dy{N{9JcMC4*K9KE5oC_d-_!bQ5<Xzf9xVB% zwcaMdR3$CinRIx$kLFb9F?%7QAp=(Op}{go$glLv6+}2UV-(OPO*Eg>2FJ!^^oc~v z@JLjUp~~nma+vZgR@_1`)9}D@{$>HtqvYtQKOaWn99TKQ2mci|jZjAT%POqE6a3jg zqdHA8i0eIHqUj0S**V>ka1UkN<xkPjQJ%@mxSLJkgP#OEL-Mo@{||BR6eMeyZtIq9 z+ji9~+qP?#ZCA~*ZQHhO8?$WNHqY$d-D`F4)e&p=KC#Zpi_DC?_#!gmzxc-Ujxk<f zFuaWwDHN7S<Y)h|-BA5}){{|)@w=9#IE6ag-sgEfR@;>Fkc5J?P%ZLSt(&gIl#$b^ zku0nIycDw`zmok-rs-M30J0R9a<#B-e_6u2ryEFTTnRooN&7B=hcO^nnDWKL=OLu3 zumi<C%LA%$H1)S=8(E^fr4Hu!B;pc~^;0XyL`2qy3F+ynbiwfWk1gdF_j=kttJ_Hj zhLdZJgLTPbC#$IUwZW}E><_QH*$wx3LM9IpqUEaL19K2>!AaeQd4>rGs>l9Z1XE9g zm+XHi!g%!U3kD!Xd&twCXNV|!;}||PtGH}Udplt7dPb9TcO%`{&Qg*is5P=^IT}I4 z0u*!i3A2^)x6mLt06}^#xt|VD#?+vkl#9L;nci5Z@dca`hMrN0<e;VNTL{5|V4ii> zfi{-Z`@2`>k)T?W*<3Jc7kO!nA1Q5rDDy<ZcuDg}6VRDM&AVdwUf5qAc-aGvw(nek zj6ONB^vB$cV9b%$qtliagRf%vTuAY&^5oi21*bg{u18grz|{*#;2CAX?=jJ$<(aWg zNX3-$1d=(O4m9Io2O0o~@oX}^3j~&9^1=%uqW7(Gk6kASy}yuHS?X{=<#d;3OBH8e z$=QuD6RxXFA#eBHJ@>U56(B0fX8sxjDZgl?*?H1G(QE5sC1w!u8wRUb&=c0eie@{@ z?`3FtaBqnJLfdQ<_+2;YGnKR+)ct4IBR39bIzGC`s%7O?`u4UIdN?ne%%-bb%L2Od zsIinumQ&IRmu-i~gTg#+cFY0ItY*2hM)!xpBjm)D#(mwd$&-u}4C+TgV#cx(>_v&- zGfpw&TLCp_O!4+1JtaMH^TvG6Gc1IfT7NyICjTXy^<UP`U1Y=hdN|vH@9Mhw4b@;U zh@SoI4iFSCk0O|dMjfpKXw`i?Owc2IO-@36g@ysc<75-FomlaylSUyyCW)nDi)tiq zb2p`*2<*X}Y3<3(s?y<#F%~Ql?#k0{s)XDQ;|d7oXmT8ytX<e19^pzFVAIKH<yD2N zTI=O20UX+VjN}XbI4A`0Wo_6%Hu>^%+I$@B{q@1kBsfNs7?0=^WTm0+<jkNdW>t#7 z>jAAKkfOdgbmTVbX0@%AJ$-N4SBfx~Qo>Xjy9$z($&hIjGr|}oN~^&2aE&rM892<^ zd1)efD^^l}!l#ouQ~1~I6-CmA6JC6zz&wnLYG5(I&cO1@V>AzqAW6aZ4--9VG0h=4 zSB^T<3N<UVlw_=j<og~Yxhf1Kc9vSk^T=>;*J?UBRWc7DLP`QrHKa7&>T`>@i6WPx zepFT=P)Ptsxvx-d3?2GNR}Zy(PHfv-ztO)R@z(sA*A@>G530u?O5J3AjI=kv?}XxO zHGSsctM+8u<!<L(8+$74YQWDK*O(;$N6!kheyCvSx#wXxEA8mm<m8ZeWds!yA8(^U ztNU2jy!XRMkG_3FFGs5CeeZ6D54_6PLpn#%oy6I-6^UiiMN^Mf3S*nA&jmbp-h$^9 zN=3CgmKnpr_2=-UflqhyDh>sbw1nE4*wWEJ(8wC9Z(DIN)wXU`sjv*Ptla@W$4@0c z2d2@gCEpIYx2<2aWQ52uY1cDrGy=Oqr|J><vVplIm!0BN8z=HPLkPyl5}Rw<38>HE zP<eP9<p68gXZR<SQndETwAHG6!0E}=-Qly0JlWhrwOh&_<M6sfKszS$bd}2L9I{15 zTk2v~APnRt1uP<(dn(K?y8rbSVD1SutH3pbZLfu%j8_4_b2>FG3r-&epYXbHO8kKF z{(1xyoy3^U`AFxLX?M2iinujwCT2kOSS{9C3)>SQ(1tgFIJ_)~1|7v}a}Az7&#ob- z1o20ESupsAvYuEnI;j&_maz3PXA=7hYAt|Mz>t0I9GvS$_p(<P<rY`^u=zi+wP<^O zr77_C|8l{AHoC60NDjT_k(OUbF`JC-`och^JjpGkAE$7Ul}Cwpb^r5wuow>y#aTpu zK{hT5N7#NY1mSCl4Hra|0V^h3*N<r3qP!q@_NDCQRGDHUuE}ry#MJ}vjZa;=&U+u< zhO#SA@*kCI^qe<FZ<&IXJg50(P=IsJ`mbUKN)bq4J^j~=kWzO8j_2~iC_52YEo9h6 zp#28cW$8GhC0$|pW3w0#CiTI64JQq!H)<I~mLI{hHBrVg;F(xE*azONR^Vm<lTZ&^ zL_S8Q7^%y?0>RFMwgPEb4AwETSlT~pCWpH5Q;-#EjOp4sU#19vmjqX!juX{%pyAx_ zZ)k^jlse_@gx`xXQ(^eVDRz{Hf5goW**XQbU9kvd3+R)&gKJt4K#ON4MTMF5eDkRa zW={Ppw)A)77Bdq&+keF7SpFWS`!{NtSpH_-`Ukf3w;<iWW9RzoDgPsE=^t>?KVhbS zVoU!-lm3rtnf^Dn^p9?|zq2LQ|HG=|CM`GHKRZjmb8`IOe+z>B40gOd!Q($$vPP$9 zuj-<hc_h*!<y5U0h&w3!s(;sbV#^FTu%p1cBZ45}%6!J#_KM;S<O{~{$M8WC9AGZl z&4clQbJ<OC(Z@T4lqvsSz~`9AXC~7hZwNi6>0A%Cz0wgE@9zlN+1}mD7;tw+^_HEd z(eiB4<+YvIEMu>F?x?uo+uh{ruet7LH*iDq22xZM0AtuW#Ic*VYfI{ZZYGpt@>~mS zA-JA;j0>C~&Q9@(o?sX&NR4I40|7B1l+#EAHk{56JdXbYcMIN?MwmFhrNYdXb`6sE zNmMt)J0zO5B?S1mBGmfm+`@V8IG_45bZe!A*Od3^c`0`F*4?U{m^!0seV6GD$)kz} zQ2o9f5FuSx+$DlR>F3i8LMtS}Qhovg$_ee2;w+>VN9gVFvCiQO0EuT;ool6oE4gc> z!$L|G<Ul5o1eJj8=a<;Hu>d-iE=1HHWa{&Cy2mRA9!DkbL?SlF3*+{CA+$Y-B_)kD zNGN|+YI}1~qVciqY&^6o5)_rc+t$H5rfl?w3K$`D8mAtiF^H_k6W?yuS(OyANKmHi zcb90)992hY&{9e@1J${58A;eJWDRuW6cJAp;7CAYpw%y3kTb4(9WPzzu(szGlpanu zRmhs7nAQn9A*<v~PZ^QBhQmuAQMUm*LP!q@1E1Zq4W3A9+!GhSR&ArG0h-SgB?=OI z1d1qF@x?~Nir7_v2tt0CHNG@Uc11!0yrUi~0lmod&8@>NN+%(}FMdV36U?9_qOY7V zBFVLf^diM<^2J=P?%!G8d~4;yeUhVJe!tt*@6sa9$H#U*bEyzu+o~sts^>n^&!1Vm z>IiT5vRMR&yv;6Vy!30VBzo@XSFA3Btz?Xpr_T28vxi!~%wshxM?GfBDAUv3?gySw zK6`49+qz*Em<$FNm>!JRI%dzCbVAT{L@T8!nGQJsOa^3ugCNnMqU;Am3!@pToA!el zJld)3M_2*I3gFE(Q__bIlM4nQ4qL=m`jFx$ogl_qpNu}iIg}GUu(Qf;0D@FMrkTGT z%}fHq7`0`>Dzb~A#TuqK_}u$CYmhZzC5@k>$%p8mDiMFs{2f(Dt0xHjZu8#wYHCl> zJaDuxp=K_bug*Zx1yn7w%~0~&vokh6^hY2iUm?q==d;lu&yWemu0kYGKm79WxIV33 zqF5S%Jjiv&Qe>h*PtRKf;Zh=+Vg)X-G!y19U@aA9I$C}O*m5z5DdHHDV?JyD$^zzM zST3t(uDG5eB_e;)0ysO36xp|S$09k2f_eO+a2vUv<EA3aqm=&#lYX)H>``#!<)uYa zvWQw;8x(H|P*QKgLWh!@?^&5HO2*U|m2iB=i;?$Ztypy7_wvj1<J`yDzKZXL7a#(F zRbM86Fz#y;b53$dJ$n*UirV-6seP9xGhb%-c8wqsIQ08Vxo2z-B>K=j7>6(Cy~3=j zRkl<_g~0adEZ*`iWZ;>aHrfbNxnmgbR+x0|9y6Hk{S{m;ASLN;e*M?+p~NjIXxPZ( zySBYMFGYU2sgZsWo*GVU49`W5OF0Z#pMf?0jPe9I-h_dQ>P0gDS4SD(*~sl=$l;kx zU7~~MeeU=STX1~~sw{WI#=y@Br6E{I^M>TKo_tgI?mgserqEk}lr+!BUpr5ED;Ft> z64HB%6EwnFi63iROOtblbs&^Y7Y}!V=(7&$_&udXdK;md{f_a%lv37^az?s-4ei13 z)5!z1fP}6jvEb_lwF(28cjZKOiXY@S(f)+RuRGuoJy<C}E+!%56p(Xyc0&s1Xin#B zuoylyl79cGU^*%dW3eNr`sf05Ah@dsX@Vr|1dk_D`tdOAu0<iuHlLvA_lyF`qBx6K z3tA|!cG6N+l)uAO!zD$fBhLA%{mH7!kl4=#t*^ct%PGZlEP+dp12$KVLeGCcZG=!m zfdjhjwy)8DE2x;b<+bM1qM78g(QVaocv`Cyjhs}HovShsQipJwWD<E{fi?UPHV$0I zN<?3X*KgT(x@1tJLZ~2a6>o8Jj;eLYCAPrXq~f-#<`1nr;@BIu&^--Z_W5P&Qqn}+ ziCLv0dKWs84du_Dz_E6yzB^W&PP4!bntmZ|QVR|!{|DerR@Lwu5=9uK1Ce3@jb2!F zEvn@*Isq^RBjhqHq-{k4$;7ftGUaJgrzz)hn3|%SS+iS<F#lRbx;8^ScEgh}f9rqU ze8!4|!<`3e_q9Hzp!32g26dwRWIp7HO^lCm6wTg|kib$Pj76f9W!G6aySLlZ&x*s3 zXB8co-%XwV(TJ0Pl7AW;wbW>&Y9tmHs0Po1bX-+32**Owt1KRO&fl)g=^nce&MU=k zI%;L_9O$cfIwEb@Wt~JTj}AE+VU+CK8Jp<4r4#R#M>Gl<QsjX+i4>0>Po!dx2C~e1 zwSfRV#wZt0v2S8={~e!zK1RdgMri(gYaxMQXfJ$NDeX8C2B2!;+%pttoekkM9N(S0 z#+}>W%+23$tKb39SK3U4egbva;Q#CW<RWzwXkUpFxz$=$mHL5SXPhx<dN%z5^fG#X zHtpag{kJd}e7w2+x+oZZVY6UMeadRzhU{;akW4)%*)oN5M59R7CM(l${MfB=JaR}G zy6|eqV(3O*hQx?*U?|{Zl0A1EMq_nfRwk_P`(V)N)mQh;{M}5079&obHcFF=lZeJ7 zY%2R+|IxE2<xU@6yzwLn8Ph}t=HXt{`OerDg;OLG|KG*!*arYmjFUsbfkn>Eyz>@V za7_ykZx3#g4&_^`ehVX8oRC;okLOzk)O06L2D%N~jl>zMYWR=`#ff!Xn;qz(q%uF8 zHJ`J9k%IJoq<^lYhr3!)6|{qUXm?u8)F)59qR)qlRCrqQ(&I0m+^6T@DU+|VE?&>( z=-ABAe`k*2eOUC+&iF0SYDI#T4}X|A`mp4uNfnhOzea715Tyo&lE9`zYuqq<d9bmE z1zVD67Flb)mlK_tM$*tRFvZt~LcdUfk2nz%J0uUzSU(FX+8cF;=^vmP4o&d8zzhUO zB9aY*fGI9GEcz|%T3Bm(eUb;Wm82rJQ1@cZEc0!2vJjQ4tqqI4!I%rDljIHWBXIY@ z$KzZ97Ow)IDk9on<S}MH@#l~d4QU)lb!Flbl07t-?e)HSf1kg-x`sJT3JV`?;@&6) z8iEl)#TLk<jEjo#)5kTCD0Ni{8AYTKgY?(U_*H3KE;UXl@Y6M6o-d)miJd788f>Pc z&lNe;*-SxuB^!0da%$lc;;~Bx=NYb7ea<1>RVIh2I0fwht)?E4-V44RD(<Fc7vO); z?7l!+-#P^v)k5?$Wq4Q)v<*#Sr9B(gX=i|5YV6Gh6>YEr#{aPuLP_xzN(B{YMtkox z80~xM!>frwgc4Nb_wQ!K-_gHMeJSIN$E1MZVvlx(7Pq$GyyMXbd>sclmBmW`o`v^H zPV97g)8G=6XN;QBa0Dr`I&lU)6k(Q<*AcHFqo9A#@V^thYxt!u&rBW{sw5`Qw?j8x z?5aqwtKv!TGIV91XbigLl$~$gZ#PA_@Mq|i@50&zbRJMh*YX1RIBI!ewKje9IYaFh zl_4{+qM9V+Eq=@a(+2y%Px;F(NIQ<nngV_yx@qbZgVZefcm>3+R+ip`@(o>LJP*wJ zjinIpR3#-*)C#RsMAiz^;v`!2cO5HVA);rsOwD3KoMv9T{p=Y^*_Mdu;#y@;x{g?b zk~03Sb2x^eMx+Xxhe53hiTxmD)tG}^Z*7-HUMjwrI=I@QXn|7W$nelr+aM}yQ_ZuT z{CiL_N1(Cns%ZaR(a*=g5zN`AxK;RJ%I0(8=Z`jw^~b)Kgj()Gm<V44k&gR$H0seF zz#*9tw)J1_6vu0D!e<EI<;{k^&uP=4U1;M6{EgkNzTQ60x48uyCdvG}RFF_a73D$= zAW?Zs3#`Ek$V`%Pxp~(`%zA4&5str}X!2l3t_M4QvLoO`1{-qmPEBo1Ypm2D5ix1C zOx7LBt-G3*wzW5bVLuACGYgkO7u)qix#wgr%dSs)Q6e2+XropXSr?s0j~)PqZxVUv z$yd<^zY;TT6bZDfG+vUIJHKCeHre#06}khrM`iQ$NmG!^{>Do|uv<KAtOkQ$)HcGl z1af9`t6^HZ3UBR9xBPmFg1Q)#CU!{#6F8q_G5A4pkJSCrPvSlR`^$QTB<yz(?G!j> z+X3Bo(X5oV?6e^Id7%P(fZ&b+6CI3;nQi)g<Vkt*v;n>s>$+tslY2A}3rC+MZqxEN zSHsDZABxM)0pPSauARFK=CYwkHC$#NKNT>3E0);Ib4;80{vI^M!lRdCmyGum08o`j zm8^EuylGSZUV5zKufS%NKZ#GQbSc*=?lRroGn5rgBrGq{0m|vgO~slPJ9(!)6^|EW z49Dc}r?f^oRKZFT@r!7YGzX_aRKX^3kdT8Ar!sF-b++IGZK?{u4!MEZUwasr$OkPb z(EO}V?0w_YNeT&uUFgYS!2Y#}SkXKRbSxj-!S-0Pam;$)69;<L$=Qo`&QA`oEzFUN zc50ts3pgE~sVfN>QhIgs2tq4wM`i4lat`r2L9}ie7oq2LXC?ify+Ttvs@ffMe%7?6 z=ZhK}R+`uH)~EP63SMIzo{v5+vTz%5=TY8CV-M-!?d5+i#@9P5<F6+r)%u1xLlXdj zJ!`+flRuj}dRs*0HxjJi<r0&kEaW6wR=b2qa6L2knj3+xL0tFYCYL)iSE=M3eLSTi zQ)Fo`i<L^ZeI9nwRWC6T6Lk!#)G(fI-@s_G1uBoMtIh|Y8O!Bsc%Zam%NMzVa!(yj z=DJTC)5+UYbSXu3UV<amrrKyDI1`B<)Rv9!Luc>L8D2QiUbBQ8q-1;!TIB$-gxqKm zH;YUPguJ0=yE@x7nm8}ABR;^079Uo2>kF-HKlJG{cu}t>-Yb%6>u=uBC<V^(_;jGP zy~AN$Ct{9TPz0j7rZ8mdHjMMjrZM@6^AHm(*&xxF(U#<=bq0k~NXKtm;r9d&h8=u9 z@VSu{qW%Ss_^Vd_YjqDh(?9TtzlYBL4IaVzUuvRR{uVg<cX-5KPx&9=5&x7a`%mWz z!G9@){zoS9Pvn9T>VI|c{4;`J{a0<!tbYMIe{4SgoxP<>QzPNewk7Y88vd<XiNK*9 z(Y@C3CHKq1dew_fb$Er$y?FI7IEfi<k`Be!gy{|+sqXAFJUw5)8kMpoFhFS+x$Ti1 zv2W~eF~0sDtwC>vp|9d6-FNEmd+;8p3hwDcA+IztV|C$v`Z*@)ia{Usw`F)fXx$fh zxh=krorx*WUxz)R5p(c1>8IWrHNO%zJO30xe?Q=V?fKm9i0LaPeSs68A~E>_+NJ~9 z?8>D${nmUl*V(h!-45WER}4hWBV~FtPFK$Dr58py0y-fj3?mr?C$#l9Z&;gBdw2hJ z*cI9^3ZKn76>nDX*QTERN%*bb8J|lVS)EneT1|yIJsVIy?V$2+Em?1FsG|JI<QnP+ z-aknQDnMZM;~?NYJ?`pA@<<W8;QFO%8RSTJ-|A_E;*<fBLX7(ZO~dVOtT@tYbmY(? z*4-OOy4~RU5SCLDDL@!L*@6X7R8m#k=0<ko5<hklsJDB$1uH0&E*M6ltu0sz>6a6x zJfH8+Uk%nPzdE((ZMbqtk5iT3zA@VDDf<;k7>REaY0^k@e(B*6vLTvdsMM{x!pD=j z^zN_o{pm;qqHd_A*~6PeTS2d};WNx@g5k}Aj)Mpqb>MT^;}9~lPaI&Xw*r|PLHvnC zsvqCW(K^;8ZZ;hSAGox8ALc)yU#zi)oT<1L(qS4ZeLyPB4i2A*F^!N$F~@**gKQ&? zFJ{E}p`1u+c%VbVbUaM!02?BQqKX!xlJpqpyv#lQynY-?MLkeDy8A2Z<~Jg0bEZkC ztd0267QIWD8_F=M2qbwMde}ImM1tk*v#>ysJqBrzk~}W5@o#sH4~IQ=F?BL;4ni*y z{j`@uVTZWd%vc(IXV(oVX@}RtE@?KR?=NHWF(Ta{5V<0{?C}QA@n+?xe5EQ@%85A= z6w(E)L(T%|SSWebV3a&zqU4v+J;9YUH@+ZC>TsBvQlsjjTyz9~7|LO%Q$&J7?=IA7 zVH6HSx^RZ53W2i_5@92q8LQYO(iI1Jx#H^s=RNQQO5=i_k{m{dg-xylKUZkE=6CWW zq^~z*`j5jWpR+mWV&uZwvmvo25MQ=Ocg8fLfdx6<Nf#)@>`#Y+C7`=%ec!2lxKmst z#W4H+7SpLOdwmE28_q0S<LSy1A35xPR#vAUgZU_sPIk)#A*AB`Pt7`u*d$SWx_yQ( zeydxaBZX6O2M%Do<1-Y{eV*2H)^uGq&FgJs3{!4=ln7PlQ%Vham7ICt^CoQ@HAhNm zw@Rv6g$N*yL0hq!#YqiqM^8#!0#UsPm0j~5oaUxC=Ur6ff*0HsP<*cPK!s{*?}vu8 zL9(e>z2qP9<s+}<Lde-`7dOE{e?rX_@eIg^{5`D5X0W?)NrMLdQk|ffrJC9CE$-~w zSK=6VBg6~PMI+g))VBP{4@rtbZjN^l)`a#wJ$Ho*uUVjaQ);%arlw=RzC*=2y2=}v ztqgiV#OcqVN&z8}oLWxIRau<EorRX@=K|EXh|xx8_ZNOcDf1dHy}`GJGK?97xQze- z0=2mY`=%AlFyaAD3lb1cBnsVRu3<zl1%liGnJeUx%AaAKdVdR(m0P4PWeu8xjtE7N zbSDdA=X6v@{UCX^)1-^kxvAHv--Ep+BVgg`mSfu@33f56T)TPTRTu=Kw~~sTt5s== zw{Xc;p4s;AWhiMPzr5_!)*qA@lU8E|w#Rz58B7af0INr|Z*M6ZN-NTHFnCV^xN=uc z6tm}`Sq@dyZ<u0p{Yid{kQbLH=$d_Hh8bNjPwF}0O<YU-ZmEXhr!S(MNb3je*C(#b z%?czm5p1!C=!e_LS4fE2LgB;>VH14uqqs!AY2guN#`?_4oz6(^ocj<>d;*rZdO*|h zJRdD(a7jrd%sMwp^k+z|+k%LtMokx~D#}fI@FTf6_4Gg`dqTjt2AP8RCvXKankUym zc3iUXB9cpTEecbH&y9KmmVOG(l2H$T=6>m1{cvb?%*HubP(ZxnrK9hfW3CC?s+jc< zaSBEqK+-gaqA)Sk;|GQ<{2EUE8dJE3LFeb0?;@oUt(Pg}oZmFp5h?ujRqhQ6r>3p1 z?7_e%W(QZ`w6%*mBB3q2n1NYOlA9+Pa?-BCTBK^DwJTc-IE@^k<tdkx0o0~%kX@qC z@=)6tVW3{#Jfo%9>BgowhA}i(|Kt*GYHKj+iB}WOshea?hwx#Nc^I9`LK(B1L4G0z z8TtvbV?r44dW#P(Q>@n^q7gAz6>)~BDfMWtVO_(8RZ`X=;zh{d@dlM0y}a?v$3CzM z(|)%@=<3tFc3o+z7<GKLx_kVEG?Q?q$BCoBMJi+-a4r}~t+uj<vI2kZ8`dI*yUl{t zqBKf78h=!ug&fn)bB84;7Lo#Lk`093(022qx9B0!5N&olgOm}^LfSDq^$@c}Khx#w z1sc<U^5cr|F9K;d);x(sccuy-?J*O<8qN=U&JNXz>g8}%(A)HL&NE07;m?5%cgx-~ zQW(z=7P>;Mq-n$&+<<!RsJdw*Q(sy1gv;iwuRIm3tBW}Cb|EGOy%`l{6G^m^SfRL5 zM$EzJWO<73yV)^>FkGT3`9;d(pA>ZgP0Nk1-2K^nIis`_=WkvzEKQv@CJQNXDY~0o z2U6+6<r(F^$-mW3^rg{YZmNqn$H8NKt#hqPuAh{jCdeDGrr?-GkCdHU&P*wG?GD7= zdpk+wT3hm;1v+MUJ*>19hxDVL(j31fgkf3@)4vAMZT+Y5uFir@(!;IXMFtzsWC-lE z6P<8xmkuKYw+u1P)B;_o!(_VV00~zCJ-5G)gI?$t`u7=a)8|rCG~=n*n$w}ud{=Xw z<CPWElYfN}(3B6JyTJUQ5CZu_)h5UDW!-~BWi;5`5ipCeHTp3sPh>Hi<P>L%H{ijt zF+?S=3+QK~D}@iq&F;}gPZLQ3XMW6VJCU5IXXG*AS008ASdKww>%kdIE171%Es@Y3 zhTwC<M2IDoh4vHedxslKe?`!!SlBO{JwDGED3>EY;|rV;+@Kc<oMCE9h}1HNHU>Y4 z3A*b8xTsnlDwwG=x~0icjYqdeF>An(M{1Lp5vaLwk}XUFm1E@ULJmKNidx6dDq+u` zkM8`YX2*@n)AkK@YF;x!O-|(09@qtB2{fHes2pAttujthve*YTxzw;Q>{G0N20}_X z>yA8wk#$c@71n?|K^aNveQ;gpQAtqjDWfFGW2ElrxkGa{UhPs$QQk_7($aG4bKuPw zV{PmF4t!a%qIej~fxli`giv62Gw^lJB<$G-y<e=<fscLZ+vb|aF$ZDt_yC8~zjy%h z<Qm>NsfD23(~@e9$_(!-!3pXG1b|uGp{EJ;MCym>md%46MkzkfqLr7<gxRNggD}*G z8Xj%i*%fu6Vi1^|b#xIb)G&p<Nfe?|CVI}hRh^o-D6A@{+=kFku|f^(BQ_;QxTZvG zP2($@9rE;_9i^5QB$F3A;^(u*IRb%nUq4JOobp4Ft(I+YniEO1R2bw@P1)RR7)o{0 z|Frq~v=U4JHJ1qe$kKJOtsb2{H-fHdrh7$M^(}18FP);4uHHWqIm&bXDYO7K!s%G- zrWfnSm=1g9b|fGM6OI^qoDsr3uoy6`$@80=$_h1Ueq`MO$FKr}q>Yf*)651v;d#ot zfQs77YX67EbEZB41&I<$JS);s?;#ozx;=#fZ>wEgkmvFbRBN0y<wi`eWybu7Niwnc z3`Y>-sm@U=C*?5O(8;kMQ=%fM*eJ*@VHJm^d%>>VtEHs|Li<%ZCah@7*$pa&tg@k1 ztDaH~XQr#NR5Nv=69ZGhrYmr8o5LGy9ivXsVCYKO`Q9ZLiTB@iABm}0YTSiX%1#Qf zKq{Uueb*~ma*$4I?qdc#LeP(>?Sq+Eud$*#u_O+r@Fj&B&U8%r(VkFyVqu<W6l7OS z$V+j*A#XkZ^5Or)joi%7V_GpBn1!H;`~?JcZ^6p78QP^EoH~3GR-jd6q->6u)nqCf ziN%->nF@qQ@u%?@uyOGsJepS2#Fta#!M_fr4j-Ut76;DPgv2HPac1`(MWVHwTf<`5 z{XzM&uwGI#1~!i46%yV;Hcx7h%C=E}(SF<=#+W==W!zm0p=(F+ye?0_0uT4IhXP<N zw^jx63`wkSX<qQ+ZOHF26BAsUEHN)GfmmLne9)hbY%r&gAMob-%h(j8AV$XF$GS8M zvF>vN_z2ePPNB}6wOkbKEIFaliE2322El1oVB=W+KyrTXGDB9dsx2T(N+vM0pNV{$ zgH73boE>2NK6PXNqp%>$^dWnHI`@quhjOllug#l7o1}L26^$>ic%Qx0djRI!%n2kd zc*moJVP+n^FearUdvp16X#M6=&&g!YgWj6`?grU%Gx$A(z;W)3;$R5JNzY0);|uK~ zO@Xz_XI)fA=Z@t8S9vq<jPbrrK!3Gi$8<HyuQ#C4u@rVuIWj-Nj#T7ceBEsZ&^79| zK9m<D5<k4iE1ygt=eIP<69ssv);&k!>fBy=H*kaN(WSWUd`*^obdqPZx`7|Df_X8K za&*(yR#1_#_wgNG)g&*}IOau4If}?rPQjWn?B*LZQ-Ni>!*OBJvb>0GsfK8Ej!4A{ zUR8GPeMZK?s_a!oC+f0a+Skh;6+b=%x5t?kmSJ%TO_ai7B{OHf221ooO<IZde>SB+ zB1vn1MjeuEd^Q5qHykk~MYG}+g=gfTk#LDyX7DyVpMp>rHbTCUUnqHBD!rgFY#o=F z8WXqQn+w!Go1bt}V(P0MfV^0!oDai|K&p9HAmUy7I>cH03H*(&NXxe1?w`n48iql} zi}mQqL$}+z9wdO{{^8u}#jCE$xWW2*xiye5644A7FTSYS00&UKyhOCpGMmABPr$0_ zhNB2HrptF0px*-EZ9e&c!BD@m<kZ5f<39%+H*Sd8OR$JZEy9|58l8`z*=Z<2eBMgM z)FHZ@-UQ#^T1k%?q4p(r7J_}_ko0W)S6hMIV-2B74UX8p!O5($+y516_`AvB2Rp-$ z|FAx={$Df(v;LRH;J=FSe=}C|*HivSP{aR__2CbxU?*T?{9nnL{|!R?tIA;3|HI1Q zsh@ve`CKRB=SyV}-l5gqWbU_cETu`{Jq9@zqGdfAKV70sp7m(J1+mt%)-z}N%SAk{ zrI14RC(7`vYvM1HUt3RUX<qa?Vb4UvJ<?QhDUw#`d*F3wKG~s6R6ml^;5Q3o!zp5l zMx7B)nGh4W7_&cUf>07NcEk|98?>jz_QJ-Y5p}FgLOO=r`8Mg<&f{J>Qa@z4GeX%V z#|1q>l*}mtFkUQR+uKq%XFGOePbz!oCA9>&#pA_D>qr^@oGvL&;D9NX2I4_1ZUBOa zaKM>qz4U(gR+}2*Pfisj;@Yse5oh(_*`pm9C@J!y-^OuK-ZJkJajrmn_Hrs4z6vLN zx@|*N&lKKAlVo0)OPfU=TY*ro95+4zklxYv#m^Q!DJL31;0-8ZvKD%cS9e@FnNOrY zdoTHLi-BaySlH?A2s}HvkN>LwtHYBgr@JEPAwI<%amWo0FP2%!PbFp_@ymvUj4zJ% z!?FR$VfJT?=kCP$jR!IRjF8|EW@G}BhiR7nMN*<d-2BghZqm2s<$_ykv^-mMDyae* zxoBaCoibpNQDc;n#-$R#P=>QjPe0gE!)X*^V2*(VMdBvg``<MKwpF(tPB|nu{H;kp zc+Poq5m3pcb4cW|a5j2wZS&rt(emVgCHz(*Cz>Ia2IRuziI7qLdfd!;2d8WSig{j> z+W-f9pT-1@<eooA!(Qe>()PfqfA!2PC6b_OkamQy&&p|IB$9TJcFOkslQmQ0sfZXU z2ad7WbLxIeAXYd^-QX_#z)$iQSY&|@kELR9i^ZQU;ISo50_s(`$;VKrhnN$yGhzto z%uD-Yq<8WX{$?vdzp><SK;h>rB3X=$=nWASL7orc!anfGQIC@;-=-*3unyUGGS9Fz z(Fp5ZY_D<+`&O#Ev~i%kkmlQAx>L@IYuO>qi+F^->T{KT^Y-BM?2VkCZ^pxkI!=2& zAE9$BeGld+4%s7oMex~ipqG1GcK3m|KO+$ODWWE^gKVrCBFmTX{~kd{tw`pVJ5Y}k z7Ud_G_eI`15Ht#0?rF8H6@~~D-*1iDY4QTERb_BIVoq+`S)sqWQk&;tHr7Ti49$E! zB~s;(4lp>Q@K%Y#B0yijGl%>(k2TmesW^qR3nGnx9hWD_!1ml@2@1rmM+zH~x~*BU zma*$q-OZM4F1uCzR*u7}R{{AYvD!zya%}adPt*&L%ayWQot{o@9zJ8>iL1ZxsOQn? z(V>NXk}W0T64&l*`A39mRWKj`GLWzRjImo)g7|EnJ_o0%sq=^5VxM??ya@vY&ag4b zk7(R;@u-!ydO*ZLRHjZ-BQs#oTYaR;&xroO{J;SS-D<+ZDD=@qe0oZxdLXD4z$v=Z zFm!oNMEdEm3=|E^D%aDLh<ob<6zJ#PF!5LN{UHNWzM%rwvSx|(O9zCs6osEA`4Ukr z<F`mK5*qcwmx;|1JS$Xr2^cS^-&t$risG;mYe><QOSg9y0|uW4yw?#JA>b9Er^*of zI6Z>WgEMf^#KFG=`MM63(@o>3ejXpi^v%9Lzc^5d+)WstNAdtt7g3jV-esxDay(5{ z+WGUOC`EfA;~^k%)?}88u=ix%S?ZZ_s5tWe`DXi7=}<oO=WV>_wqc?y83IoGVPJMJ zNy!@)hPK2rGbCB*Ybr-Y_H`wP%D95ra9;4wlMKjHnkhM}B#sjD?RJ=`d2aA*f2f?0 zTonqIqC|NXh=WnqK)RqL`-%ZiUxM?Or}DFfbF1o1QxTq`WDaG~aB1u6)PV}LcAgO( zy!y<Fp!VCIBZ&-3L}nW6<(s-hk(5|FvB!X8v2ZlC3=|#^#$As(MM*|6(hje805COd zf|BpgvK@$@0~2LrEL&6qLKc^qm1ckbb9y0Og9=^A2g>J7Ej)31dlG{IzlsW*D9v8! zsIOC%6i1F}6>~*-i5f`u?e*E|th66LI_JYiS&Jj6z&z-3rAtGQJCpz*eT07s2yn8m z1lab#4iIQ_hPH1ASz;($bA~Pm3w(=jVR&7w!R$`~uh)<U^hRZ777^%E!OHAk(l%vv z2{V&=9qPAc1G4PJ=RCYhW`#l3!-q9CSLo}f-VK}jTY5{fGCrf8sPhS%-X5u!jC}Tq z0;&cEcji4S`4&2#+=3xufx2@eGfwQ<9kyVxyI@ttk5&Q+Nvww+P7D_LQqsW}YERhQ zm|O4bL(`Y!5Nx(SKPSz=gJnt43Zu_}nw4}>kW%$l8|ca$jt-t`iThOYL?C|sGF^$w z(()SImOnIkhrcHqxiD)ugfEFzFBUNCiTZ6(m0pK#nUwOgD>Vj&ytX%g)kIcv@SGqM zQR4lr%7e0hleUs^1TTjqbu1`Fb?_$)`F#oH8yj>Bx|yXm;>yg$=#4>h>KN{M44+Q= zE4s|ZRvx)tut9+@O_#6whT{2xT{Ibfx}59)T!O4ikEx!BGY%-%V|I+G*-xNgJyUL> z`6A5pRK&GYqId(pwTN_hvuW6b80_VJj%p1a+HA|_b!c?Zjg%_r-}9?nV{w3r(wvzL znWb9J@>$9yY>B-}`yj~jd`0_)hd6*!+$18uhij_NJa1XySn6$6|13+1AH(2)=wWB7 zyNq`}c1#kpS3QH1w9Ur9(%BdY^9TkkAKXFzgsCDe#)S!3xu65Xx0WJVVGa4+Sd_oX zYV4edXDk(kJRav<0#KnBN;`{ZKK9VoO!JWp(a(l%h6>wc?9Wj_#cmNvLu5p(gSXU- zso9b|$?OeL$+T-EJ!5psG0fZ`O))Js#<6wiP}hH}<{SkMeMT01gDUmKU0?!$1;tT7 znLfXdE{1uj?0H0)%LSI=Es3tC%S3CNUuuACtsVI4vzRS|7E@P}GFM|NC-rrIv5IoZ z`1GLo_Ez5AZ?|ObkApLUDVsp1VH$KcX$zp^zw5c8=8GO+;jy)j&HD%>x@$AU2z9N# z%g`jMS-?NvVKCdj0I$GaCvG27W(nGqi*3IjbO33ya#9H$pD`hds{Dg}sndZys+#E2 z18OE{8~=h{y5yyQ&gs#nLHo+KLQ@JZXs$eBYI8x)ROl)sj^YNXG?O3K>*^ylx9fG? zpWEfpJVaCDW2hzhGS6MX$FZwSXL?h+`i*sw7J(OL`XzR=6!soTE3F?VLIJN@JyAkb zZPHF5{D?fT&VAfvr!&w2d5QU5rRUJ-Tt?gtj})WdQs*K+_8N|6xna@ohV8dP$i|98 zR$yhok($$^^@lerzT8w9OE;V05H^=84&Qs=vh$Et`N2u@?~7tb#X<HimF}?i#qw%u zy>ifEEY_fEAHRr{!roR8vy#1;@DvkT8Dks&BvS4!$Dpv3617W399Cg?*<GhHwY7CG zz(4JnD8{MPl4e710IwKVOnI2(vq%L<luA?(w4-8+LQNSe9+x>=Z=@<Kaf0p^@-C}1 z2^aV^U%&^@;?@7^ivRbyArmX(e@+uw|L%GIcczJdn;rhAyP5U>qO6kjFQ@$fcg6pI zo1AC+M{V?XcQf1HItBi_(PGxm_2`YiE}BJfgD?TANSQiP`2{l?ui8w=(cI{V_s1!p zby#f%A7bbrT90&qA8@d_9j~;hJvHI*i&6+7`JlGq&r2?=e6QvU{;S5@*C!0-lNtWA z`C<Ewix0sq`vw1|V1ZAHZ|Ir{p-%EcrYdLAJJG0h?alTBzq5w!L+d~y^<?|PI8iEY zXin}<ew5&e-s1aWuOM(H0^8^D;hcP1m)oc7iQfAuNB1R`fH*Iogjs3LQkrOw{TBHn z$?YJ@=wm=v+w&7vI1up9T7z$!%kL9iPpLi8a&mhrMbs$e=gPAZvgiP1ib#n<q*>v| z8eJaTQZJX?T2-78qPVa{wKSH5?(6Ypt@oII3@Y>hDeq?5wI=!>KF;Sl>$v9?_1YdL z@{R*Fd!U%_72DzNJ+xfj?i<?s##{q=7jT=a9qx`x8Alb+wmQa1;UxP_(aI+F&hxl0 z?d8uh9u&X8RWI7vQ9cY^%wjSI>}$KLEm>9UDQaGm;1_psb#<$CbgRe8e?=`Dx0qK{ zcq@dsOk)il$+K|bX<TIXTDa7rwOK=dMkg&<ZzVZX2BW3ks;)7oMWDc0W?ESRQd@F+ z99C}Ce5g0re9GdEH(&AYAHNr$msebh>0HhraapO`76_@#e|Br;d-~3kHDJcf|12kX zR{K<$bvE{DT2!Kh)U6yXd@JhLajPI-Td<W}bNp0td~|HHcS)(xWm`5KCNjO!zP)mQ zb;cLL%*42|8Fr^`K|?d?)Sak`(6(PccX*83b_?4j^!(_ue{_JBaz&oyeeis^ITF>N zs~3)9T^<M{kz<rvFrA}S*j@zfg!;a|x^F&<0b*Ti52@?Nt*XoPyScKjxm=x>eanD* z;s0)j?NtO(d{ZD1K@#y!q>3v^t6}0dKLE_N1^CSX*_--B&J4Da2I!R_E>zML$R~!7 zTsBF^;Y{H43(PA4pyR8uv+P%+$lG)#lh<i;gBq%zAb$bBxbt-(qq2<Sc}Q5AlQRXN zBKucl27<`m!?zN3Ea4UaiYRedUD8VsauYv2<}lGzNxpj96xv8%7&3&)pDAvv_+#>9 zx|(1afeiV0-5~nVZ8LYxIPJ{XAe&WVeiWSS8eJlLuQ_+z=me-cyTF=ZvXvM{Fp+{% zT;&12#xD#pqLiD7+3Z?j^eH#X)9bQJSY&nwUeuxU&-mpIqpX8-1*%>>7$@@VU5t_H zwaxn65Ep*1IEZ;q8tUeHFmi)>L@BLRS7@|~#m10;<+o)yc|>jsr5mXh9r(;}Q0I`m z9!xfkeC=QvnFJ#puH2*oGZ%f)BR_e@+!0D)k{DF?&Zo>eJUgGX{ZJ}^hsxaNP%c>Z z*o0<Rd;QP=s{JypjtV#tyr*b+$~=dier17}6B^?0cZZ2511nu8jNUlIy;9MaK>=Qo z37lgBRbX0hIDflkV&q%Is7LlD=?p#*<D^2Oi8zkQ@{8$f8uxN$WC<LA*pQLruA_-Q zUcMa`sA}v|0CsNj6UQ|u_)`E+l#}ni<#UfW(UGxUpL?fR@|nK52u8o<A`ZW(uEbN< zL<W0V6fsHhSRA;;24bi1Az}v5IY_>7ptyswV=!TJ5=8>O5|U3GZXhdy@I3*bA9##8 zK8RGoHyY9$#*M!tqKLOI+9n&hC8<UuU`;XYHas{yIS|^M8?A|~_`^Iz`HnhW%sW*E zXI_4%TAXU$e8|*odTaHTqO1s~0&mb3jH5!R+F&jMK?y9uBtq2fvyMuaMS+%(j%+Gq z#0fur)31D|4DyOui{`TvWnf%0zcX9>AfDBXQlH9iN<U$$!wXRfL6%wy=aC1HL_I3t zxltwC#sRnnt|bh>c}b8f?E4Bl<d@(@#+rha{IUciO=*i7FO)wGM7WMoTeld%qOUJ7 zCMUMPV4@R0&*XP}n3w(mEt391HB>3iLT3?<@W-Q|t+shZf!;xXLQQFu4r?sG5CVe> zZ6E0NGN7+Ayi@ANDL;Y)L0isi1Hb64nN&6GKy~@RXd!|1r$X?UC|sxmyg+&stj}>l zCcIs^S!}dq-$3)KEH=LeItxk(tJJo`K{t04;K!=)S@{Oa$h5H{*6~9|KxTQxME+u6 z;=tv~G33xS5>1CM1+v04|8$1)ODlbi6iOftEtB_k*4_pTngvO8)~M?zKUN%#ZJh0z zm6;CRJi4tP8wo|r5Txu;WiXs4Md@>NEkO!1v&vRd#pYz*%mtddMR*;g159;n-QpJ$ zL+ag?+^s)IN2uYC4))aV_kjC*k1oKXb_}Cg5F*tB=J#a}*Ku+Ogq%#*s`R9@SmP~b zutXv}Zo+Dt8~-e}*o%c!@p?%9>(dBi0LZ(<ii<?OU$PD*A^BkJ#qpSb@*x)a`KvvC zY&2?&QzDtf6&dIszAk4kPykt*-F!b%)N8pD{Q7uINi`roGC`-`fJIIi1gHiMy?;&n zG-!(+BxAIYn(*Mlc@9iT+v*3~|44EKap&&GmdV3p`w7h?DR+0=hm&kSkFwH@W$oW9 zR%1BX;#KL@99%6QI5mr&c4u84kS|Q35MUi$3WnTPTs+il{*)LJq<ml%(~$)Se>9}_ z`=v@qQ+~6_ykbr{ZeT^TiwfSl!B#$`a9O;*8U@=N@=89wm~snVc+G;NYa_oF#*ZR~ zpWfYe{=&P5Cw;sPc@?^c*2P#VaA?w)be3PO^K<q`SBlawq+cH(tEUBy_7w{(B9dHI z+;?{{?f8?A^b<=IDs=gdJ{DAA(mbV}&jV3wFrS&!O<?ngLCN(zie^PgZ#C-v#h|7_ zLY{e9^1C?tjBfJKSEsejBiIa<j!H*4@6tAlHEw&zadGTsW5%6`rj{B3j%Cbguf#y! ziRcwq1C<O%<Ruuik@b6d#cZ~?l=jbWVqEwrkEY)vGog$}rU97e_k!EipD;pM61>Xm zRSAk8{-AI^w2kybel2repjA}CPf^r(?sZQ}SSz^Rf<0#?$36k?)ZDHfU+L`}1`U2? zV~PVa^l<^O6G!@?>B516$uf5&K&<Y`#a7*sRUuHraA{P$gN?GPY(<d@siU8D?4kqc zN6e4OU%>XXAWPz5<Le(NIS9*wLQ)Hm5<+4oDBmbosKrPu&W&(KsLr#OyAY^E3FGo! z&Hi=-U_(`?m9NdTPw#x0)TQcYU)$=x+pKo4v#w-hF?m~E{Q^Phs(i{|fl@VXn}CrX zBn~LV6>yBUafMmVyW2;2Llv~#>WPEn?gt^T7yBWCbZ*euhDfS6qcWLA{DGY<262~| zvC)L=I@BGWy*JdGdL^?gg8k3$TowXfrj6vW*r#X3oTqdN#>-S|=^XA{z0Y+LrvOn( zi6lMi5llT_JVdnfA1Q>LbOC{g`IVY3(>K}7tn&QB@fCfHQ43ACiJ5Je+P@wV+s+FR zrt(pfDLzq~*ltbb*iUL=E#|Y@W~aQ;_cVmM#3Gs~>@~(}AQMhqJidi9dPl`(a!w># z;G0C<4s&x~yayE5ybrlRVCJ&(hs>r&VlJzu#zbpQO!dN#^nDmKkoFky1TQ_<K#dg1 zC`R%njAY$w&=9FAP(f}<8B4$RL%JQ@H0|w`z_T4j(qTQ%MKot+Pl8lCurpQe5&gWJ z+SXLr0+v2x)>lONRC4%3!k1w2!{2^i@?TAC4^FUj!_Lyh?!>sh{z(eIeioMr6qgp~ zHm&5dc(gSu*s8y#it>~hEVL-(bYefE6>b<ApRp33vVsHzB(9R&wqK+Zzetj2Yw70f z-)X4KL(!{cLO%5Is~I^ZU_@mqL6YaiMizK-R#}gIgc(j+`6G(@B7rh7*WA;x=xEfq z0a7~H1&wmk_fFXqAR_rDoX^Zcd_KBKq*$uew+B4_8bKpqGvv9h1KtBL6-vOq2VF9Q z8h0zLMhjUrKj`KHvLSIyWY|F0fo{N~R6-<q_GSx^vt#YKz@9P9h(^fHwW#2OBw%ut z$~A+_5%R_buYL#C%hYJG6|KT{Wx!wpw5#D{h+n5{Xv8WOpbg<9Ay9yMajGp%O9{LP z;QM3lj7E_87jo>cQsFQ1=LZ7|`+rPa*#5gBGq%4uITei^ZJiwqjU5Sou>H-?_@8p@ zuiE6_oVfh;l>blV*gu&*|8?^8p9d*y|EN{}LvsC1dj5Bk>yMk$aa|PYkDD`fjofS^ znDGaDgP019X1G!}5#wRVVKOus;a}XGAZ3F-zkDXV132vL2z(0*81jT=<>Yd7pP9Sk zwny3a_Q~@j2PuiyGd+nc?;Hdb>7&$@y-K&hxNi+|;@QPJ2@Z|wiRj6;Df5gRjz2<L zm)D0r{JBl-1vk`&ta`?mJFHqLme3D+jP9u2cnG^k<^w;%WXwe{2WxE+d~KvnsBhU! zMf@rHI!N0<J?QDABuz-W>AlA>!xVp)Bnn6R_XUD5J}X|NI?zKY_I>02(kJ{v=5!S? zfQ?SrSyzy^F5e2z`s|guMRkkEz?Qy(kiFXjoko1+wWr6wTgA^XsM|VRhZg&?&fcb7 zig@blB}2D*le0$m8$&tHsl<VxV_s)rdrh@&P>J4@9Oxg3deMB1`5EbT9ENoD%I$pM zwg7fjM6@#%{<yjKb#D>@{NXG>rW+*L=<5YPr%=NiH_QBBfM;*8eWIIQtjJG5vA6DZ zkc!_In~!JVGrv$-Vw~tMfkWnAH<!oVuuoOw<^8$4SW@wO!s?lX@yapA5w0R0s4|R$ z0b<#XsnYmrK&%u|UPk;S6dF}BPcsH2AkyIIwVyxG6!*logLT|dR$eF5J<l%fiA>h` z;i|5^fYOsAQjm*<nDJb|@<%;xOBHA4t03-@WCUrQm=YXu{VbCw#T3Wq*O=l_5F`9W zU@!~1LJZT|vAgk8Y7e5o0i&h`G5hp=V{6qrb0he^(8>Vx(Z;o_Ub1K{8+B7{-!8xM z4>}siJ_qbP>%gv%Zt|BC#+MF*4*&CPgA4t=S9=)iW*7FwwSqw@*GK^UQI_B4lam(t z%y8VI`@?I+E<J9aZ_X&4o{m*9=0$xNLL{)eHo<;mhy%S4T&A%wu<#_r4YLqQ`aID7 z(Rp%sce-2J?rd%v7cC8)4_j!fA3XIdm1!HrWau*Vkz$Z+c67EC3l|CN6JYEJir@^h zZ8|_8M0);`&q%}zW<)TpV#tB)VQ`tds4GPN6NAiys&V}UD@49?dl=p%_MuAGd8(nb z2Es}WBBnv&#UbkCg2fW?wI-Ee&bhBb3qh3##S0NggQrxq3<20==p@F`nv)SZKUa2Q zAs*Avfl-<$?S)QL;L!s^qTeNUS`b>n3n}$*zG!^5k4SGazb<VwD<9sOItXB>@#=_3 zY;**AXIwTio|lV4Z0*D4YMvw9@=_-8>L0tli0ob9p>IW^5G^Gl3dnD=7<d$QAVli` zOCKq1>mP9<x$eZzKhL1~vp9FWx~E%Kt4r<FNd2uWr!|jWxpUY8D+Sr|!(IeZRZ5uy zGl@e53g!rcRP4hflPq~*5eIVX%bQ&Q;{;Q$!(YAA@b`o~L44VPaDz9%*{tXM8B{%t zyS0rbe(&W1WSjazpOZ!X=`p@1)HCZ1N=k?Pb%VYm#gwKo)76I&A2`iG^`$fnoObA0 zhr_Uf=d6%Mpj=!iQ|N$wjG;cN2SEsLfKBM1`i|0%sKc)>mInubg-*2?cBa=h*>oo> zk&HFr{KUbi!K9lPk-qU_R0b&0)f{)c0})r<Z{TX9l27_3Q@$<V6HWH3OSDv8Q9W<1 zXdijFH&{DaBoBSaPeCChUcy0@HPA$Xo}{#69pHo@W9x{^>|NFMSh=loG*@ytM5ReG zlM^Qm=MWh+?hUErr^3kk7Ah5NFd@<ZL)$wA+1h2>qG{W<ZQHiF(pYKRwr$(Ct(A7J zv~#76n^m`}&R_f1iL-aaxi9nKdl=u088dnxt@YM=!#kG`3=LPdLgb0JyqpFfqe5sW z_%&7tcaGmf;6eC7(A2h;YC=bKOchXjTeWF>E<#)(@G^cSIP5Yyy+UP#*xJVZjI(Bu z={;>G0kiWE+6aRSD|s9#i70^<Gv@mgiA1rJLQ|pgO9l-Pn)gh+#n^u)*vbHc=F1qG zIIIGs9Ok4hc(*NexMS1$iP`>n!XQFQPfCgF{1MX}p@3z1(L}(URRYgWLl#4++E+6T zY@`Muy?5QCIhS|rDW+3n7F}DSWs3l*PO=F$j`ghq-2e*k&Mgnvun1`B>J7{tON!;? zXP!sCfcHr%%NQ&0(x}O!b?zS*PA1zx)1{dCY^0knK0XUoyFzn?O&7yKer%S|=b*z< zdt;1C1)$r{rfD{8f77X7;*H}X+VpVl@R<U<2_;KH3XlZCA>WZuY*Cs#L<wq?NOR@I zTG{8@Rcw>7!w!sZHGw$+KL+%sD&>(YdXb&NsXJd<`Suu6v%qq43!1L#$|)cd1v#)_ z-T+JS<iQ3%XYbl!Yz8#tIXU*Pw^hnYyYmg7Adpqm=1E2T$bS(kAKQ%CcfAao13Byl zB@D+{<L6_i<dI5ndJL#ZDYq&*EsKUoC39hDD;E>5A9NU1|C}OMSBZA+7Al?~5bG+H z%+~HiHR=x4>;L^!!faD6wVN0^oD&v%QWbHgIuOC73Zy~sK5;81s+UalMEXjzRm3*? z)ryxqfsi`@f+)he&uOAJU|4!K0?&<#aRiC6{7zMhZX%yUtA84FL%Q?r^@2Rd_$p<9 zz#7~+V8ccJsX}g)VTMAgHL<@a2V+;a3o2~SyQk+@QkcE?17(+?jAKbtA#_4M(nPbW zw(-OJ)3JCzb8u{zIo{B|GZ1eW`9=D%nrowM^@dUz%$RAW&p>vxr(|WSMOpo%b+<sQ z|67tT(FdrKkVd^FrW!5wEduPTQw6K6Qvs!Y-RMJ67m}e^k%D^?$XKn@RQ}zGL7T@$ zSzXdvy0kX`Jb+g)n$ZC8r`)c0*@88)tZIZRb;75TEU0f`vAX`i$Qg<zPAU4iS+4N| z&d-6ytem1QZx)TxTgcwz#oiUkszAv_5Z<IP9MOfS5a2T3NdZRhxO38~b8=F&#fFqM z(kYuyf+Q)XEUloJCUGdv377hFVct64QtIjGFyBjY3&yBLt4D2Jt7nGswppoR{OQO< zd|<0)Vk~XQOXvCQ&4kimly}-kyQnA#_epBI9a|qGy2J8Rs>7S|2IEwVa?7SclTWki zR+&>3A;pf`UHOMbwQ|5R0O2YXZ=>6DQp1cO@T{Cg@<+TrE#94aWHQI`qPI4ma{Xac zl$f_lsXcjNwka>I(mH=_i@w~Flr_KF#5(t|Q$GMr9@(oV<p;-GeF8ff!w-IkdZ^^} zx2e5d<>g1C;>Eh$Os`Le^yyExftac;qP#|ueq5)~<Q>z_EyEObw`<FIdfMtJdvgU8 zsXeI0j>$j!$PGPk$xd7n23oR+geEMTB~NXN2aMO-7ugY<B#JC)A<Ak%X4xvt68C5b zWUZ%ROB!uMiNfqxN#c4KdvLRIurYWzgo57f*5syhYPG)y5Y*c`DoYFe(Xy}{*7Nc` za#jz>_@%7WEEk1KcSw5k1&9H)gZS*7-{H&#m4qvyFOO5&!wncU3#sb62dn4Z4HG+^ z(zw^ws}{>#hk`;Q?h!JRm+Mv;+WUy3{bkO^Td7%mO#D48y|IQc9QrSEF=D>48PuZs z%j@6~#GQgNmW!eC(~}vj*1mm8mlkj&pQT{-WHZX=HUXz`x5Isn{j#aQ>TX{mhOq<A zffpF7T;<`4p@LBKf<_YV0DfPA6~g0~!ftb-#)0`Fsd-~gwP_5uN62G?ZbZOvMV?2C zke=rw9Zn)CBL!w&=xWVat=6adsDjDrK7#)Zf4eBTY4=X8+$}$KedEXMC@hye7fsHd zuZum@be1va$k&X?+;eVrKmM+-S!3D9?8F}YO|PRS%cq$U;&{(v-!$?4M-~$!X${T| z!_EFx%bKlImGXBoVK;#!YTcZ7ntMHZDqr(YI8q24UMS@#Yt==r6rWCNw#?yIywrY2 z)(#uHg%jfvQ)Y%+<E?#(xPW*G9S5U;4LR6VI$s|VaHZ(g`*>y!(B=#-J8{kwLZU^L zKJ>Ir#d#iWc3DF#kgmzkjxL|TeE&1e@P&>6Bpi^xu@s|cqH>4AlWaESvA!eP#Sv0% z{tbxCQm2(F<|sxKow|Fw`B8=+&c3;qbIKD(&^#s%5Cr1)=-(mcX0iLVV&yeamq{uW zm*w4|n5slUlu`yxJa1{xO1#0QrB3d=&hUFfjWO;q0yr#DLo(K(v*WqLOH7_aZrA7H z1a~j*VykbXOgpoYfGkL9A@=(08!&$=yEjBLHad=7a=c_hiy1TaaKNbLf8`f(@H*lM z2FWwNEUXEqZzE#CdKIN&m`_VVzMOsFW_F+=A+J?r^p~F%n5C{fw<;YOEL+Cntzmyb zf<@Q^;Qa=Y)F;G|!$(9<{OPfebf=+xK)~D2K5Vtb=WCxzJL%<N@H_}2-=cptr<3@u zmfKo!W=eJ;SsoP$p3F57$j~MF(!M7w6O3!zC&<k_%m5}qi@g}HrhwJfs1hx&k&&Fj z^>|Q8j6ZLQx1LZjHCC0ag=+D=%kgtv3r*S8#VNpI2Bh(K!(@r-1xs!`HG_{<Z0fk@ zuFHzGpSy_L;yC0{S7tZQ-o#_y#yg|FRuk#fj|z2t6^4~hwY}=R>-$E}5>IkSLA2@h z$F3XdLJsQ0l3^>=SWE{tFV+x~tz0Y%djUXG;}t21lA0^j)i7=z)lCqo&UM__SVxc7 zvPnHwFO@a$?2b?}Cey6I(--2(cZ<N2IGBg$c!LHDlIX5AtUJWn*0R&|k&}3=JjL0* zW$62hcpXJ~q?aPw;0%&IiJ$Sgk9^e*93_#o939yq<yUn`Fi7c2Jn%F*_-eMmJnn#a zkbQbqc&ufku_cChMy(ou&M|dwoa%76wF*}ek(+$OgA}7FtQ$N@z%XBBQL1uKEO|+T zP7n)Nu}{gvhO<=mAq^QkIEYaTE|ZyHq!^=&PpOdw^lkv^u<ffKc0S?lT(55a6L9~h zoR67}h2ehz_rEUE{!bgo{}XWkhX(L3!2SQ#57PfnT$$|uoa_E=g8P3p^zYv=ITHcn zKfbPt`~TL3`EQW^A4<z_VJiV2-@kq+j=wS-{{-<I|6n!#k04%s!)Bii!Fxshx;)Mw zmROJU0nDm#3r)1;gbnOg0X<kPAKBV^oO+z{^@+DgM6;*tku4ZGEoE#seRqm;Q2v1T zMTE$UWuHWjvmCaaob<&;PAS8(W^2-iLR4r!EM9|5r6gc#<-p^Uoj7VLwcnQHJ)aKG z^dMps5^_i7#l?Dp?Pp{~R{YNRJNfNLmWWCb<O_5rlNshnU2RewcA}ScF6d7p=YU@k zn}B$6LoCf3T4^5&5hxH6Q$!V*Ev^DVz(N4Vh)1exXot%@QvuX9L`Zb!me%!aHzjP$ z1E`R>k(L`U@{Qt+X_t9=J1kKLuu<#siOAXW5SbQq7C;<KXno0XLCV=+VH*iQ!t11o zxN-ZCgA}B}yYL6Jvn?|q;I*9d2DR_3)S_%!rWf$>8{TI{IiQd5t;cv`!UmMS_)T&w z1dAhvcgg&DiI994i36vZ1=8jGm_j<=>^!CU>0b;VliAr%Eom$!yb9dZ^^e)evj)G$ z#rr8DkwF}GJYp|?!k@hxFyxmjL3;66oQ}G(KRLIorFj_77YFf!z45xEP&(*Z97cod zCcF}Oom%Ha;Yy=M2aj-G5j&0}1r6@q@!dlFGJK`b>Mm{#*335zt!i84ok3O!Ou*aq z_uPrG${A;|(Imy&3o?Z3qvDgJljW$`g&@AADwtr3sGLc(Kd3KJ3UdM~#?j;rWintf zAWE-ul4Q(dQwFi(hv)kG?CsduIUsfRouBtUig-eDB`u8)gWt^N$4o*URv!g28s&y+ zD)Btxb}vd1><2(hbLCGkljR=4N<5p+E<6g`+=>DjN5UVPTks;68j?yhdeuvD0V*lV zqY*T8>OjcS^@C+eK#u_5JrRX}B$e7)_DAoL=is4s&nva@-x3h_@&Mg)rdemxL#!aU zqIN1xsFf~VRzut%r!XXs;lSgcVy19?x*xpmVZ3zNS7qHuFKZ<=A3yIxuNJ;h$<rYm zBFNzin0;qnR3^O^tynPol2$YOxscJ3|7ZJmt;KZ7&DsH>uFy7cC$81X$^v^1JCO$> z^aG6RI!0_)GxVxXi3%iU;zG#`R6_)6a(EAkhjq4hm$&A{BfF=;g&Y<zyqDN@(xq4t z8Mxw!e>QwhipmYJm~iVF@@y@cXIuI5&&45Y#SGE3rPvp0m53MxgXfzIjk`J_q6oPr zWTs;BPRJTmQF;ND(EQrNwx4+ZyRTTuXKX^0>@6Bwu{8l(bE<XN)*)1<C7?Z=F1D$| zP?O>mvk=73qW)*jNko++6PJSxwCKAM^e!fD;X5*GS6DCG);IJ%smZP#nweE}6QkMa z+znUb-KF`A>aH>@<5xLtVi$aRh!@rdr@F<AsPS2*=liI?#mEle7W0X4ZU-eB)M27A zRSJ5MoyRX5n(WkZ<HXz6S_a#y@n9Xmc&|`X+u95+LR_>A9GGRQQc@l+9r!^TV{4UM zgp71j8E8alo#6Febhk4+tm#9#hoNL$merh@Wy~v<?xGlU<~__AGOO__JIr&J0}U3A zP{`7k7@&+k@_Y?#9o2!tG0{wZE)3?+@`N0^G_N0yCAm?BnUOs7c!ac}RYh#-+5rAA z&jq#T9oFODg$i=u_3q{66A!-PI?N(C$MlH5qNz?<*Uoyh{(1e0kE5Ie^&fi9-|OSw z$XP}vR@Q%&Que<pX#Sf$hvOentN*L#{JkLm56R-cpYs1VS^RJ9=ifTZzxvOAX)XUW zG5oC){;Re8!;kQPYAw-flPOtj2wQikTJy(l#=HW4u5z8Y<fG{~Md6KO<3hkwENroC z@Uu6wN7_oHgdQj2^P$lK^Gi$B+Nxf}PY2ir*A#RTNf7g>2lDd>Vu>R0W@!he8Ho@_ zoQ3%+B$d;x86Z?4ilQwC+i#Slkg`b%rZ!k$kfrux;Zh)nG$a<XdZqjvekHL(`o3;& zlw0pTM5pKtl8^+~eiH(ao(C*&D5Xp(BIRkfB-dydzKCBRTlfHk7&i;kM=qkDWT&+d zt$8&AVPZ@g3QuHbsc>kFI1o>YI`cmyabvt&U%Fo0o=#h*tvhaQL{N~wu0A4G6<-bZ zLwYv{P;Ga7Hu2IJu}B!h-aNJ(8@4kxm>P8HDMJk;jEcJ)Er}16n+Zh>2+EZRY7w<G z^8Kwlh{CVm*=%=k+_FG-W#%M;#3dZWw0ePquo0PN<z0huk-pql)b6D9jfL0PY-%VY zx?Jgn&&A;HUaP8~R2fme3ky?_L`$rV>T--OJoT&?>XbyV44UGHt#E+|!VE=1qDW}u z+J*#Wm_D2EFF;T@6(_ZO0ck0@_j-B<0=8;K;%BZ4sji%tjLUnx#}0Kz_(o?2t$wW8 zT4(073EE${XFrzj_XpAAW~MR9r$F-9<Xtf9Jn2Gpx#2=ky4WriOqoM+%2}>q_fGh^ zdA3YCk>>oFvY1@#YQtF_`lx|2W^CaMb6Fvz#t5q2yOQQ+0TDq8sBLGC8}VQbCHGfR z1NQ;KK%NZ%uuz0Zelru-zSwLf3TWK2VgGC$0Rbdsh`jgNY3ABkyF|eMSPE${pVJTf zx*nAM+)T!pC%$*#Pja>>v@U62qu1-_8voI1bAgrzMh?hU_TD{Z1|(11rD9?P!Y33m zm!Ey<-v%mt?|n!prr7*e5Lm|1s6Wpi#vv<*3lo3TSAN-(GL4*Lpky&4{8RAm7e?1F zReAW(bjzd%h1dEMMyjNHjAR9w*9)a^5PA@%63L6M-%{Kt4xjwa*^OZhLBnvnMRm55 z>nBmgtc|EW^S|qg1&+1A=J#+qf;9qO>ojy^CyCky{-o!C7CUV%H=8iCnUvJef45w? z>$1iMs?b}mn7O4gnMF!*ciK_N-K4@1q##~<idQ9U=)9<zOAep$rG{Lu=)^A+)tK>s zeW{Iep_x3*`ui@YlVAV|bA3xmm_zuQp7qHO$EP(RU4U@hW|$`Y4tq;>`uQUyO2l7j z?Njxp4{>U4tkujAAUgK!>QR!-<=!Z|yv;&MNO#)sv>@PjA5_HBBNRrz$|V<<l4u8* z(4Z$0J@esn?OT*lE&bnq#y{4YbUvc-YZ4l#M$EVvy|2WBR8FeM%5e)*<i$aZ={&0* zjD}Yp0c?FipU1dA{ihQA&kH^y12gBpN-)P?7mWOOOYlD~`F|<F|Cr$YPp<tOe;tPX zU%mGKtGfPe`wGFoF8%)k0RN@v{uO@wtLXkiA^LwRy46~mv40!N9oL?e27?T-bY>o3 z0J>${^K?EAJ(|Y@a6<>Ca4^d_r6*l*^fIk_&)TllR=`Ifv1Z5s4YG+gRGdGboml#y z*ba*$?#fdok3o@UNO_T`5J5!}q2}Hw+!|3b@uVuIov6s5d)X51v5YS1=!CcS?~oTj zM5+|OPw9;W!`z8PG%4gqAu(!(l_t_7$&j-f-oGC<vM1iT8Zo{IX32wajBza5S4<^5 za?KPfP{x<IQ$%u{G`iW}7&`d)oNC+;$tKcFIq^`?p+SeBDRTu!a8Bh9w$Wep4xj8H z?e$<p@*ov{(=z+L$LOg$QGw)Clfx&Sk>iu~3@gVw`%9B%wVQt`ZS`e(up^TwcCurK zeTNS4)lP*Q`<EpXg34v8JJvN|h`bgSnfJ;K8MW9e>*K;PyerI9&#zLh4BK_R%CMe( zItv>jX?APWX2`a0wuwhBS#j5{*PgV7_+7eq9OoqG*mh?4GEJ(=p1UPW)61&vs7-E5 zPbr`pHnmfK7@8<^Zo58QZoNY{1|U^Fl;|z;tLN-aPeAkQ(k?sB#W(Qt_PN!=EvVpO z%#QPKRb`Z_qu2I^kZ^_inJ{-(vM)I`T1Vj;1}9hHUW+jpa-RS#uWiNy$+*;SsyMz1 zKK0hBNk9Fb*1fd(x=UdAq2^x?ba%THE0#`wmrx@xBNGna#Z|rTveZNf!3_FC_R7)$ znBGxZj{KBwWm4Nv{C4&nUUtn3u;<!uYUb9rhW&b9vd99{^cUg$1S^RqyK}ois3_E! zf6sJud~24W_ag(-sJT|c71g}qeLhj_{P|%L3L{)GIs3F_zwK_iqv@871iUo2?ljfB z7HZ$=B$PsZ6(2TcABaRQ8!H-N0%7`lV9QoUSza?_S&BNn)#ij;PJ*9UimR#TYge-2 zIuWtdgmu0Y>*tbyIyY}h146HTe5htes*o5HWbve`_<VL$0|QmTru_*ykFnp1EuDn9 z6vsou_TpjJXZz+NNDpp0>m*H7R3IkVOaj9soZ%2zT9Q!pRt=>p!sGMPQBC;-CU=rA zw&GOfl7pD0zi(PqU#cu?H3Bq<2D*0+t{yRfi6WRP0JZR@RjM?wwdoH5D?D1YES_gR z2PnaqT6O7wz;0P0j~3Yndu`&9Y-60R8JR%RNUzj0hS1NFMW=T3v!2{8FW1kD^QYRc z2TPnhoftB7TfJ-}`)WWY5+$Ud+IZV7T@XR{bNs5wmS7){I}*W*?`&LCq)bhgRU7S@ zO|K2l^j}l0QM)g<!ofVJ>8+U!yM7Hc<C(@V*#vnwzEI#$SLC>cF&RYpKzcZNMU&8w zN-3#xd)MIwoeK_pqw`+6t!Ae60KwbAC=Y-M1Q}=sB*kHh@reZp_9cTr&uW@!=#b4M z>>>++PBD7GyQ0;R-6W%g$%kC^^JG2d3Hx*i)PG?8YUBUdmKvw91Y|wmjDMZHrFFk8 zaoB~~Cy1NFZR#T%hTfXXqT*ynCQl4dLdx9Tk*9*o{VXxE9EHPxMB#8!v?E#iT6=h& zjUnxFr+{A*7d6N!mP0baPh#`4c9ab_0pA8&^|zSziQJYl&6i5B@@1`j(*J|1M2T9$ zCHWX$T^7T^Va-zPTo!qdHs7h%C`D9>&gr42_V|KoM2)0A>zeW7K96%E8@R$Yh&Xb| z&vO?y03<tXDt?N#37$gcsY}?Obj~Y+BZP47$|HayGQI{`qPaJuun(zo`fB^FNR>fl z)G8X|ECdiUTiT8JPwRln=;CU|r3x<gLEXZ*5@F%*O-kd=o8`#YexZ^?9M!etbvj)~ zQsL=im~ZJCr-^Z9^n0@QsuSgX1ue%r(bGI#YBuhX{|8J-ydM+`1YO`b3r(E?*2Itg zA3>I|x=!`z`9<TNQVlD_=)ByBm{}7dO8N8X?ld9oPfUoyLqdBBLXaQEDZprf4(m<p zhcLix3++k;dn1x1vDYS$)`us%1_DA*irM4DSrjYE1x>4$kCAK**}&ebq64pX{AW7G z+7WaLu}Z5PON<q`6<PX*^bLVI3Q5rBkY}W^iVE0VIGC%$C0e*De~v4M2d*|DHt#at z$i;Xr2h^mu@9j5fO7AYK8iuj4)h4<&;r|Hh7fWI&?|nZ282{zOaE~i+`ek&8)GmgK z!0td>PP;?wMCrq4ck+h@n$>6n>4}K^B#iJ8^5B4KDgg*NT2M|xBa+qntj$Ct<i7N0 zZkFO)Oi&%*Jx@(p5h5^?rT)gE8pnMTpO+j2P5e^<jbl`vyWegEGq%nVW;$#XVk9Wf z5r{>>Xnj+keZzD`rUUn=+%_{GVSd8Ns6yjw#p5fJ*)L5|&v(^GuDaI5`p(*J5~eIZ z)U33T8ZAy`9u&clwjEwRIKjXynY-}0+oL-l&T!1c{@^9sS^C)iDE3AC(Da=7UVLU= z7hKbKl}O-tS52>n%X4-(>FqgB>rVQl(pv?xK@=m@#;><(3Z@}nkcbctLinu=#NU79 zMHn+sr?^yYagW<Z{U$apV}Y0QtYYOwz7H#FQ*CPR9)@X{yy!NR>F%v`EWrS0fod(j z(g>X^MR-zZgCi6o&n?lFu3sU<xk59C3?^}?62{1xQ67(Gw4SIZKJ4%Mjxf*tV}5UX zxA?=6un@v2xUkhc_9Lcq=tRw^%8~n0hI2|HzqS9O2sx;3A7qzoxM<SAQa7*f0%74s zNIt@^xbI~AFu;JX90nQl593)25=+yF2&ne)FO;ocC2Ml*T}E?dy@jx34Mzsr^ev9x zVk!Bls?|CB*@vJ^+C9*Lnl-~^n<hzQJ0CqCJ76DX+M_iJlagP|8Ks%7SwEny5YV)- z>aeyK7q%){+VPp8uzqSiWzupfO|@(px&^v3$-x}_76e`&S!iTQKVcXon5J70A3s4) z9J_L)@94Zxkl}vjfQ$hmmq-)7Pi{KF_!zj{JOE!;gE17!zllY}^1g*bK5w8zM+MvC zDP2)S`5`AC$iapg;sEXW3<TVAZmIQUIcuJEIu+?RS(4+?q1oL>3Dx?0wIkuiy08Xr zT<-c$7Cy`sT+)CQ5O=ZOh3kW8u>kjqde1=L5S}!EomVKiSP^~qYSJYB#yR76!Wb5y zwG8Vea$k|j!%6du`4J<Wnw#4=PYN|3X~9!f|1(XPPp2$rwr$o(_Kd|@0#xNL-qpL! z>oyE}qBBuZP*1W~*ql>hNeGo=Onx|LC=6s*)s%>T4JUtH$kA#5>X5m&UTnXO2)#=W z*7mp}IjjKQb3_7cZUU(v{ybYn3vQ1g%$1y)Ik6cv&Yv5f>Z8Uj;uyha;tP^TBdV2U zFWg(&sqo8bOTHc6*oMkKH6wlLzxuHNL&K^jPmvt2GOKfuHjw!}e#1d$ydFjJ5)^D) zk;E@<=^pf&LteC_xKMmKssW&Lxl<fJl9<~%hqcsTIf6*Wnhy;am05xjaOYkx#~=H5 zYvwH~rV*k`XBGbVaVlk-huoBk@rDO?7WN%o6IX(ZA?tzE1@(mX!%!<s&{rZm80k8d z(p0K_V<8X|x0u+nmk<L1Wcr0ccB$)-TerhS44qQE7q#Rk8y=Ai4kgZ_nH&bRG_eeT zK?W3g`2~mwkPFXD($?)*RoV-%(bF1HUM=z_IK=jLUfeTBKc)^(-*1lLFvBSRtx#zA zU%#+<!Us`G>pCqGoUGUz8=1kCxfDO4W5mCX;>3Tyd!Q%Q#vRv+^Azzng}9CwfjrD` z5S_>c6UO16+jfgb!DM@9*TaQuY!eH3;Z?@$0pbbHW*xl{^L{UKn0N)=5qCSN(3`3` zmDzZ?aClrIj~Y3V4)h~BEp2R+c5-q%_3)ao7t5UinK3uS`+c6a3pdDZBY+QO@fHkp zJmvmk(!Aw_d_qUpwG$I5fPVrbibP@PwfTE+570;1qRh3o{PdpZ6E!ZDB$y9`cG`v{ zK1ZBrTrDzC!lS!J9$ZOYZo4>Ju6`R|{Gdo#ojS>R)H}SI9PrPp2a=b_Y3YJAr5A?3 z&{=lFHGu1nK5ZVh2s^pdBF$NB6j5$K!4iJ%LSJ&P+t_nUBf5<RI=3LJZD4WZyFq;B z$|JmmCQzp{U*CKKdk%p>^FG{AXD|@V*Dr7pa1YW0y<}5eic2pncAWR{0ftPu&>It+ zv%GLfKmXn+X=PWTo5!!m-z2a93Az1~CSzt~W&2;q?Vn?u|DEyeKMZU*|2Dh$rpo?; z-2MTN{wK)o@2C9#O`iSRa!eKi&VLM8{<@{&8{Yj#R_(tGY&idu(T(%JX>_aB*>L!q z(QQ=yH)XSo2u2{cF}EtBHMxUkiWTD__`)zyDnUD&YwCKW@geoyp1QBJ+^iG4O`vX^ zR6CMjCpRxIuQ#W+|Mm<2^PhKwFB7WUJEmLq=C8!wTm7sz69rSnlHP++61o5$6`vdw zL8Xz;m-(-Y_rlv`U!VPti`mb1O`Y85QboPz#@gk(j|RQViY>!#t{dN*+1`g<zNbL~ zI_NK8Hq{KKFoPWuz^%<(`cr1xQ*urJQqM2i)wc_)%J&yEYE*k<5ltBtrbFA~p%SS8 zj&UO4=-hDAG;qlTPiwr7bg97SA^xvq4NT3-ItH3SA&N0dUasO;<sOeaB{)v~T6S_D zl-I`d;R;u!Yuukb>9hG9{U&N8f(av2CRL=8o?#@=hqHWH&rsYdm0Kx~#j!3wKTuv8 zKUz}Y(Qmui2Uip$k`cNz>(=RO&!!EXsn3_$Z&C3>$80K=Y+Yh4oxS%ASlQ~6J_rvj zJ9Rf}PD+9a5=T0=vQ4gf)-TcSw3LVBNH}O4)~}un>N}B<oLSjt34~-oqk%Y46G_s= z=f({Q##hu4@QrVL>bGtG@aH^Ze%{L1CTYM)8XPZu@Iq6}eyk0edUuwm3^901&l60K zusg8dcWv4}v_Ht(us8G3dkb>estG|H#oTTVw%@o1Y<StW1+4zS)6V+C5+XRQS4$Io zx#i(&%YNut;%)xLFV}@~JU38yIa4uRvrR|UdG6J%b0tq6MhhLtf=Tt;lCjpMXtPui z%H{Z}&Fm%e7vJl%M}4;NN|V=%0Coa~_h_noY}{ejwhgvJ^n6-jqK~f;M3WCmlFhPb zOT)rBD2p|8yz0}ywBIwWC*iug^+9?FLN{WJY3HY-D*yLYoGyeX-Gy2cGf3)169rsS z<yjIBoKyt=P*AZ1Pv-!(9s3#mR*DhZ4bV4z7x{fOwkSmsW3F@r)?}p5Oxq#y4tbWN zpr;fANyz}y*kY|Pg^9H~4wm*YWhnC{_a+gpq8>^`kHouSWMP_s;G9A?vXMY`7q^2P zCC9j)eb+7=YN8E;(3tftibZSj7P2%t?^H2-aHMUM=@47;rOr2b5bv0MVxC|UT<I=3 z^qGP9vNLNCM;nBEN1|$iV4PSwvpXH)K{}sP^kom@IGWhJUa=pq`p-8h>?(BioW8FD z>RvH|@uox3&ke5{JVii-xLZA^X^*YRyUVAV1m-TezEM1l{z{=)u0ib>t1?og#wv7e ztSlQz5G3v5SS;I|&#K9&q6cOH$v<H1nM%1VmS#%f3mT_*<R`(Kb5H9QosF*!tP12_ zoSvva4bd$7P;m3P5t%rv%_sk2X4~8f^U-QXE0BgN2*^M+v*IU|o0osn8wI_y>oT@N zg?~e>0Ez?AdHxIaxmweomg)HtMk`xPz(P8f5BzEhP~;9}7R-~4LRIZYBn1^_IhR@& z$J3?&z09t&!y^~Wy(#|H87xqbbK_t!=~Z@&Y06zQ`G}7nGCl$ZG5{d9K4k)t0`#U! z0cmm(K(@j!xL;sibQJTW02|D(hxZFt`@k5yGA(0)96=E-V@$OW?klsoy|u>xsQNT& zoxS)x&PhOMqjUan<srfYwv5@Cf`z7taS;Nx&b=#nO1>>sE_d94XL}GF;R4}iGp2U6 zpQ_M82r2yCwR|;CLFf0aXpS27R`_Qxd$U;2&v^pBpXQM&(nYp@j&?)tJck2H^}3Pq zJLqE!_`>l1K|t>RbbYDgppZco7fd&9$Z{@2GzG;~>Pv3r(l1m-R)-Equ!obW@ZanY z6w40~BJIWv61;U!rB{c>M$9&gLb8M8_{D2%rNclXcAfRZK@8#fy@u_=UvUPNrP8Z( zh`$)t0S)-$8roP)y)z<KEOe1FQEz=7kl2q*6jAV^wMx?A=V)y&zyOtpTI`^P0`NST zzH49fi&Z_4jEIlIWuC=*=UD}Kt0YN1GT)$%>e;kUZ3_HTvz!~m>sjMDEo=pwHY=46 z0IFwH!~zBvECoRshNwSKYOUhU4Fb?wae6>uoQiB8EKX1vW0_1$lqevGUWt(kyto*K zO2q_5i-87`!t?W(h7bhl1Ua41*kjYq`4FG(ue7LN;6a!fUW7!#XyU_&AkB6xxQIy$ zhl&btSq{QhKt>iQ#t`PZhCrp&p8PckRKf|#rbbD4XYA*f!!jB_RRUwM2vicyaHj2~ zehgvc$FyJcmlTOMD_3F~QaIR4369uE1zEk;#oW>-?C*jw%Gw7M8PAXicIC6UQ5~?5 z{dT1c*K8MY)lrQyxR-!(q?Jv#>!xTAwqGMA-CP@6+B^qYiX_kk^14E1AJfkqRK#U; z2XQKLL>@5UaDg8?|F~<1w#jVb!X<oe=X8{aR1Ef7>_;Y`39@7_rPXH`XNv-i-I^-1 zj>_FI<}(U-PjUoK5sx4Wy^HuHQ$LvTaXL!^=1U*u#kWP5S1?Hw*9?E4(9Oa*5UxTw z1WyYr2M<rMAb+~sLnjjuO&kZDb9=za85ez-RGZ`6+e=!eOK6CkD?R*Ha0)we7jcH= z%UCey3FovnE*A+fL$V;B$tb4f3q>_pq(YK8SpfbfCFIu$nO5c2Bq%Nk@Y4z>3(WIb zrzoeay^g8<Ey66ysZB_+4%)rAc+Ma?ZY9NlUuR_%+*vHV5)CJ%Co<U8m(&HxD#e?V z)2=TNOTz?M+A?r9ZS&I)GNitQR<QS2Zp6drZ2cZ_&?!)Y{rvDpGI=LU@Ge;TMnY4_ z5qMqY>?*52s;LABXlDgX@n{rv2QmT5P;ydP3BmNGniD6P$4DW5JxffDu2eMrbG%Y& zsmhSk@9{~z$bfMxovh!dPLgPR#iK;nR{PLcQA}I<=~xL!>%1~9tC$p*5@24MW^)pk zW2g){!q&Zx*$$9SpgRJ%I}*oxFnoZ;G_Ydw2M%0p5;1~{lj{T%%36b94<z<5T?LmI zVrpSV0+J-+(2F+qi;Qz<i^CY&jAs}LSyV@BeT-QTnHrhaTc=g=2Hd(Ktfq{P=S&Jd zb{#P1$(Ipo$8p@U>~y{MiyQYkA<^wn6N@%Lq~>y!AZHV@1U)&Yr#3*F@f4>bks;n0 zu$Uk)+;;PPhQdl5$HTZZduw4*WA|Z*lz~q9Pa)BCq>G$fN5YRHE~2I)zDlx0(3DXa zl|o>oswMdb>C$OG$d>D#*Q=IYW$iKek&^(3+ITwi%rv&3SVcEMoR66lVU<5nzrzfI z5jF)7p&+cN?CEWtCXqquEOH8r*UqF%pTWTL-{w(tZ&pChEF;f+Zt!am4g5h)1~PS* zBuJda+wZh(9Es_dr;T-SChQPZ3XB@a8ZshIrP{P;@RzYGB*$l+G9pl)vzuKROgK-3 zK;EGa$(h!dA#cp(DOUKY*ip}w5+S=~t?Ou!aSBi6ZRb3`<@m6o8YASUEJBUJ&Dy-0 zf^bp6{-qIEZ)lcdSwJ5lQklYw{uFD@gp6DvoM1FV!8{#?A!`6mcJnr6LEeIMsf$*K z(giiXlTleO2>0}KhWD+*^^xL8hN&B&Z^Ksu2)-X@ONTGLdOaVH50PfQHMM9fvsE|i zw;;hj@)^_j#~PeuVDm82K}4o``}FT?`dX*6h_i#y^HxDf<M~TgSjoTwE)a*8WJX$W z)sC%g8E_6xiqK1ri}T>xZjGY?jEQetko`{g!OUlvZnD3)&*z24q_yWXat5feQrS8J zb|sL=1QBD+aE1B5A8-1ZSXQh=#pjY40$;F{I?dVb!9*0O->GIU2eB*Ms1b2l4O1?< zl|QZI8$ajmvTv~p??+^1J&uEf|304+EWZctNw39MNDkd+(BDNqtHX8jRP?7ZgUGbb z&C#ab1C-G8W!kD;;rDr3LX5D_0i*D6rHtUAZ(}t<zRkyB=nK{KtHIF7`ys&%$w_~| zDFL9`6j&;=$3nrFWngk{P*(>Tg-%}LFPJTdA_gn#GDl*!pS6*&%r{jhsI2bc1X6cG zfP8@24{ixkMFtCOiV<;B5)J?^na>{p=NOyCG87fIYMxy$H=M>IBySXz{kqkI=_k}? zGy=k^aId9%4i?A%rYjAIZ=bhcXI64g5!S3`{iIGq&Bp+nzJ|7Njtchd_Hq>yEa$nl z>K5%@X*TF5v5O%1{U=<wPQ%J53y>7>VkwDHRGk?i#u#=L2SC1aNm}WuPeVnFkwH%D zUTZ+7Au(k&e*qx9zs>(moRoskx*|)Qk7?&uTYRBZWXfxXKHmaR;rH7V6rLlh=7(!c z2s>EZy#4WJI3q}a1#~hCpiu>TtQ2ZcT7wA<I4a{pbEZe@PE||6{D`yA_<EeRtGcF& zD@N9beLzF4s}m7X1ksdKu}>;xy?UUVgnL=nKFcb^uoI|-6J(fjFsO4M5^Hb)Oq|6Y z4Kn~U4;#s1M(c9p6eRLvU(!#w^%*8^%0BI{>oZtQmWRdi3bDqiZDq4`LIFEUb11V? zca97HDllHl`>fT46Y<EGX?ECvl^w(NWUmO_pm^8`#_bt&2pbl@@7+qH>F;A%!e2h& zk_%@oeBOD4%DmTA{61wzy}SEn3RP*Q8Itw)=tJQ==Tm~VpaEsT^r=80<!!b;^42dM z1u;A+cN(Yt8h&+1zgFXnU=F0-lwpPza9Z(NL*6-j^X1u_)02ix5kInZSwF%q1mR~5 zy>OUH?8C0)yV5}bcmWA^^p~EPaBa-;;T!M`s>lOZ_L%M9=2yWE1qycLHVCR}24o;u z&}LulBSSD?uf`;rV#vA|g!qjEG~`bd9z^68VdzgLMfG4Ui1@JHMT0mXi^Q2ztBYj@ zPr>Eo&`T9iD2iMb27nb{@gPePBKqyIF-!A+r?N@YrY@As;s*@nc4+bC7%Su@>Ps@7 z1=&>7^O1K-*{*qKI{Og;K^YgwAFST9ac(YtGP4sU%N-l?CP(#e(Sfe5%IVoP;TjJw zrv8Lgr#42y1PVy6$x(|`FB>P+sh8s`=bvtVrX!UM@BKPEr2b_MZjK@Alw3Y5n1%}$ zd|@=tUw+z28J{V%dyll<17=)VOAkBIsk5WQ=3>wIbDI1&Rj4f6d?3vLgG?{QT+7v{ zL|+(cBCAb<8fVt#7z1qWrC`l*LDKJIiF7|FDk0$^*{~TG$?^5!s`bs8+}*Ej`7$hu z3@lN|N^REX5S{tR2)ze$!)|ux3GuUbn{_R%DBciPGw^(Pnj=YI;u^&u%ky>{DJLOX zdfvDad5g*;<%#cXDE~k^i8@u3bfztvSN1dumxXof$ID2L0y2=V28{L@zcjql)<Rx6 z6ViqLAa5-xUiGRj_Bl3H^r<X>1b+6V!H&7K>_pO^F3V10&i(6oi3spb%_`oOuywx} z$qg_K0Xg{iib;U2btNAL0M@2SE)V=$=os5&c+D89JAZ<&Zzj<st*rwBpKFvxM^ScN zGq@FpN!=uN=JG_&6Oxd#0vma$(7ECDLO~V^FY_!C#Z_yTx(%T*8kZgq<8=8eMb6x7 zRYgTQOFGbRj2JTWaJp+vNm@B+Ozk$CNL5LP?GWu5l#9=Xp0bz!=DKHf+5lx{tS(Vu zjM2ZRK#0rMAy3{H5rjbDjpxT4cDOYoug|hc(Ki&NLC-naGTJ@0U2x9bF|fd`5rz1T zot`u5Eh)*>(m&|q>FOSgl8})agw23o#Hb3L4*w@jC~%-p(qBcR^lE@k(Jg|C{c1~c zxx=*-q*|Q)r|0t_W!Z!%1X9z&rCG@p)v)r`#S%flcI<gC#-g0VX2DS8n!Cs!rKaY3 zbd%}Ky-_oXOp2dnk8~$tBqml_XVf9ZVxf2sLdo68;Q3#Zt8THvE*{IMJX8}dZtp|& z_U!sh)9k~d<Ew3%NU_n0wU&o`u`o>%Rg9JklXSUE8P*UFEKiAKjoDSalyfc=;eoGB z0_P+az}3S!`@7;|4cQK!4oWkE)8n`v!eIF_L4Zs=mn0=Yt3ibmb4_<;@-U5y^kFw4 z4T+pTjb;e)jThH%7u?wJy?=(JAs3U}Y)jIh7$O&uB(BH_#VG}Pl@1CqOQqE(k>E6h zHog8%LNd!GGSHlCX4d3(C&S$Lnh9JBcc}OSZ$wo191s#-<U<a&nMpw&N|jJwh-E}e z&#ib>v{`SIy%@7m-@=hcKu#D55->WY=eSh21-w77yDAf?`OB$n4j5arUzQ;t<D@Hw ztv`erc9n>a$spax5B$psuG?%Xmc=eaEISs-08bLOk&;nAk2tXPOYO*7jhDbWVnxNS z#DV#v61#VUZxuKLyNDh{!aAoyzwW9_Cwj84vsuxsq3a>`QeZX&*0-vEGF@ssj02WE zeHqT7Ic<Fq^GCXjceAHtjW7CWJ1A7YAS6+Zx2Y!G=Y8lGU?qJ%$baA&|IUg1jn`xO zYh2IYnYVwkeg92`2<QLD(fxN4?tduK`}-;XeDnWRg~(r2A^*l8{*`C^mt5qVX8boY z@}KgO|4K9dRnPKI9SP@u)7;&xqx~J#ll*P&?pr|nb_}hxA+E|QHK**0Xs1lJl<bAd zNQuU$VQP>F0vD0|`I@Tqk?yJOgtq|!bT~+wFo6l<^M2j5?W?_ty{f&!`7%80j9he` zocdVXYS-UsFHarusq$?~HHdSIY<EC6*bBD!y=}c!d*!2C<M0PB%eSquH|6DZr27Fq zocpar1GjbUs*dMi_-f>%)bq*rO8;uJ9la?04Kb4n7PGI;76a&NK-b!RcCEu8>$l~8 z51>b%@ow>Eiuw8^gM`0-r-CEB+NvHglDUWufdjubYNv1yx9K_d@pA0Z)kQu{%TV;n zx2e|tV=UWyEYe7IN0-m^i5<2=zq=}_vjQOLt0Eb@bQ>pg*nV%ge*hL8@LEa-fizqQ zJjk=}hDc#b8Bk#u>UWM`gngNPYbu9b)`%Vw#CCnZXUZSA@3KY@Hx|?leC3TNh2<76 z<rzl=ZIS!YwAtNjf8|3seMsO4@3#6iLs$xQSU$W5QYVbp*~pJ52<-?Yh)P8di)N4i zy`B=W;nt;@<{`dzgee$Dl+n!q;uHfgZhr?VCGcL|;(#^)xr^rXbF*N52=ar|ytqK5 zuFjA`BMx*&=*)*AQdLb=^reAf;%$xZEx+u__EY>3b10rEK}0X$%Qg0K{~Uk2CgSqZ zx_Rdy^iA{Q$;id6a|SPE6!vO+rwid4_Kq@1FX)TF?RfPMy&*jeP+f0lr^&hw1UGH2 zPeGRmD8URt5xSU&eiMMD3G&Oq*DN9CGa(Dy>rcU_wl96!G-Mo@fc9f>A3L!5+Kp~9 zwsw=lPnuS={M3K})>fPf>FMl>>1@Ed3~Blub7n@@oi3|2ag9unmNhNh(N&4mlz9xi zpJdp9Sl@6431NG^oS8by6ciuiilJXGON%-RS18M(-ft+&$^hLC{5c4$^fMym`d6Zx zj|?VEURy{j)qMcN<3s&nMghJ^PVPubZBq|PNkmuKFqnIeo?s2C9E1EZN2mXfwfv~( z0md%9tEZPM#jM3+tH$NWT)cB#m*+H-Yt<3|=Ik{G^|xlD<m7uwoqElI=)T-{(=JJS z9)L8rj-H6D4&@w`7d?ijOG#|-#|U!}hQY=H9V6jSe^~h7&273Thju+YzfgC9ncr!{ zm4~K*``cHOd-Xc=UlwUc99RXs#Xa4cpCiNq<hH`k?V4~KGIcXF<ni~+FE08?p`^UY z?FNcK4u4$xV)Pyg-yauy?cF8>xVvvm-8l}ZpACRZCg3%@#}hs^PavYP$+mD3Q0U|& zT4ySS(|eh-IPnkw=gvp3tlFs}r5w3|5=4eS0nRXhtBpW;BzF!FnSFO>KGpGlGA#f0 zyMZNfR117b!_PTbLzVg9+M#u)FuNxKY+_Z3({RM0uFp??xy9eu=f}=>m7_!?pwb+= zHqCyBCb3vfzf)KJY-_v^!y%S=O%>GA*jp?Gn#K~6bs(2Q67k^e7gJe|D`qS!4OQa1 z;14s*hfuU+4T0~rM>f|}!~}{?&={^qCsBmgQV`azQNyCOM#I3jOka*KPyl0}+$Vec zBStga^oxrU{AfxnRPkaEWT`(t0uf|5ssf)5;_L?^(dr@rT78O!5IC&0X51zHP3nYr zUn1Eq=IY9jli-D@V!@2SnZ&s&#)v|IIbxRoTe7*pPDu{f3e<sgz%VDw%1evva13-& zYV+pLc9}_m10pzO(s8Y!@fg~<_GFqmI%ch+oIj(-x~7f6Rbhw{#Sk%04o+t&*^Xcd z6m$iPRI$wvjj{5W)s^9U;Hf2fmJmv5hs!+?f@g?!2z~)cK~S&DqIt{gMhOTYVTfWN z0Qyv)eaG~x<QuA7&Rt9F4wq>!Mc>9)DEPvoVZwv%KtCJ<PaNK!UIo3Ar`a8hMQ1%z zYO>~a;UB4Mt=GVHF`!#bBhI<W@S^RyS53Zm;ZEtam4@Iu5p#g2c{kNZKh{Za(pzko z_;GVm?i(MqxUu*`p|74!<B~fMO$U1CiU>-jj||ii5MU&K`z>M}l>RAoJkp0BHeGn! z>N1nb|7_Q?36JV2(Ig=i$~n7|9F>0shus<wZZaIC`&h7)G(DSiWzBQ0udF?F3X8dj z|L~ZS9<dGrhx&F#*9k6~{9wT6y1GrKWrg^GJsm9tRbHq>54n)YQ6TcFW`Reo@s%6h zOBk}wNK*-Q2>Wt=ZM;R3Glc3^wG{@-Xy@2mbrxFg=f@q6-Y}^miLg1gGaE9(Cq>d2 zQu8W5semuo^<dyN23E_s$3Yoz%|^^0;=wm{H8r>0$D<~8ay@lcP64xrg^){T^-Fbv zDTiwN11Nfqf3Md0aqZi}#_piSZ=zcc)+A0+PwyBc4wOKcss?8l#0Djg?L6SZ1-HaN zL96TpjXZ*U7L*$m{{bQNYPV^1&pJa(UeCWZJ77!ZqChy0!Sy`DW~eQ(m`Y)3tSX0z zU*7;_$|gW&vn#-fyEu=<#vDwp#zLdT_#Wov5vQ!-aC>w{sofna>z8G6*lDOqRGJU5 zSA~uPAxH<5QWgvm>G3GgQud_W^6Am2v&AvxvfT>DF%dN<PLvVCJ9ZRCgWeKjo)*~X z#YQ;<ZnW31u@2ui2YVfB$ZQQTD7rU&Y`+2!z=oK{J3xezuB5@**>Ma}``an3gK%IL zERs+TTnpB@By`|N#>|Ou(ISfg5M~A7OOWba=<zPBP{xpvRG^I4Z36Gjxxo|k4Rfeb zuA<zxAnu91sfO`+{w5Dl9+r27&Zd3KmWfKJ&W{DV6_JT>Yak%aOhhH_I2sQngj9rx zA(qQffU^p2a#S4S@ZnNe&Bl645V(hm&FKu~ReR0UnZYNgx6w=C4;N183zgsyjN`9u zt*T)t0r7*`r``1V)f(P>{K>PCUl^BwLa1H`B`z$bF8wriY55CUDB^pzKg`9ew$P-G z$MRySC9`jS$NQZp!m)!(=--1TNzs(_OdEXNN;=x#ms#c%l)y1zLJSZgGtI=gU@}R{ z`6oetWfW0E5X%z|#3%4X>@yzpW;=5pOP>yHhDI&p!j6B(YK|>zVG><R%sAKZg)gdq za}aQr-Wj!jvKBx_79Zl7;0jgfuZo3)<KiHTPflQfUjb5OC3`;IN<`zbfZxUa$Mqo0 zS=XTrD*S>PLXBz(4PV6;oYdoEUrAwR2$;WU+~OT(%xy(aomlw5!vzO7sJ?tDw6?0- z+uSaZIYF^q1Bz=f^y8!6E=Ka5UBHlV7uib)uW=Dfm47ruz&gVENR|Mw&T+t20z$Qy zWV2L*fmN_V7w9yT5QpL@2l7c>u)5F;h(zPtrzi#j3Y-97vAVgGMp@2*T3M@!!wOVR zF6IGKwd7gp&sFr3Z2e?C0f?lji@}O&#lU982vRxRc+k#d&&i0FWWg3?nDgAbp%KLh zT2u;R7kKe`{CFJ>O$8;6l&8&PsOkrHVU-ZFojGV8bvwTrIph*pM_88jG^<H{jC%9) z0ipC@hSWMm3&$(^Gmca7PAbJ8`|CEjPU7GoGI?9za#uCiPUy6g!eW9?{uCVGgHLJ) z^j%bH2k@cz`-5!CD`g&DX#MJLNAwu>I>(7e`WT$B+3E9vuXpT8vHHW@0NMUVDbOM( zDBlr&m?F)kg$>ar$1$iUwv9x9%(nF;J43taVlIgLj*+^M=z<OALNArY$?3MhBu}5k ztFw=Y0A2J;b?=#t(uy!I8~71;j9QYZZAo5VUot6d>xsc7l(9Fr`Gni*7+j_bzi>X0 zs0RzoV>rzpUtF(CK-L=rX?A^2idHv?DntVUcP1Q7DV0F!#6~kcVeI94gKy)M%6d8n zvOOV18$7G-6LsMde(g2dcw$GYKqs&_tBN921Tg%Un2rmUD?*t?{H#m)G*V`<EDF25 z#-J8VDF#l5p8{gt=o_0^z@N<kHi-ar{3|mt2G+)+)j5mRx=@mF^R2q{PB#244jl8J zXm$MklF{*~9w0TE2A)DVL&5>>qUe-<YZ8iYW$|axmN4q{6nZ{UG{Ruo8LuQ#A2hfG zK&NOwHQW!T4?Fd|#d6q^9k=d(JI?P*DAqKv%5M8C2J-?#8OfID_3x4a2F^p8!KP#u z=3vix`Ycw{HbNk?-%@CaqfvJIGuYI*qUL#H7b48@wxC#*mD}FoLd~l!f*Axk;>+&v zMTIzJP5d~eHV%GV3M6QyhU=Ig{skRPLH)zZ;iNG=C%lKH7)z<#Sz>`Q;Ma&28?l(Y zWRIaKnc!9}KUX`wQ-?p<#Abapuuj1fe9!3`;0HR@eHmZ$Reue4f_-Ofv%IxX%f#47 z1PqdLDdboKmRIMJ@|N2#jzbD<1BPH-P_^cJ-i@Dvu23S~jm*He#AfEJ3InHSY8W+Z zUh`XGAJPjI9v_3$n&aDXyQm$gVwUj;9L;2_Gg+dBs#WFGSet6D>SB=J$x{IdZS1^& zok4`@^DMGJAh?Y=A*XwcP&EOF_~plziv#^Z3ni<(XG+`EU_U}!pR-g4{U+VH_9oxH zRicd8>$EdVS?z|BOUrh1_laz=<qu*fnNvOVPAj7qvtZ;J41P@nbC{U*cJ!F=x0>C# zlQ^P<UQ2x1hq==zGC)p~Fo#)P8_s>(e7?+%1mVRLRuHymCU8g-#3h7<_-tVDsns<| z=<4D!#ER(_jRZf2L#l-V)dP{FB_NFb*$^>qtS75S`tHL)fj7)pdoQq(EGj7q`8>GQ zi0+vYhFzXk@l>@!B`@-JiVT1qQBq1pPy_9pM9E!-QVJ~G?C~P<M?hDY6hZdqa3Qd^ zqzs~xbWu9>XLA;Vrv$MJly(L=8mcaLEk`$4a&n~?-brRE4Rf(M2}|lC6xbcHZj{~1 zNcWG_OLGE_E1+6@B#VE|6)SRi%ppKV!`pIrDNwA0)x$8t*Zx23y>(Qa+m`N)ySq!! z;1Jv`xI^&53kmK7g1cL=puq|57A&{~cXtWy4!215J}tNJ9o?tTH}39loRL2&WTcAs zUA5L+b3XI;tatP^zG>K-iC5h{qv_VK&eQT+zLrO5On)pI8KwyuW`ZRaK(b0v6av$} zAeNVve%0_<ff?A)nKfFJ87zXD-S8<gZ^(`^$0@bAZTd4beH=x=JGjhOny)>Q%Pu-t zju9=|y)=BbY-<{#Rba@e<oGw|VtPff0)Db+#(`h)%@d=NTJI(UbnY=kEr*9v%1B}v z$(O2kOS?w7CDk**EL1U-Q*WO9K8Xh+vSW#S9fnbv9{oWTyJjJd-IMn!#1uFA69*lw z8shZUYdAHA+_5SH#?wH!850+T9FD?tjvH3qN}pNnT<9M2d_H4jy^-cHH8``ph~ei< z*>gFF(C-f{-<?Z8e9lU<`78;i5KQjr#`M<EFbCT7TX4tpB~9|qgtj*eDhq<|>xZB) zA`uofCsU0IisT%sY?f~vaPSYalb|Qv;+cxppW4q<h--jq7EXt!kND$MYdqg?4d)X| z$AxLoP0C)IW-08mAfmQN+$**5Pkla3(seX?OV3x^Nd4aEqsCsCZoE!_(mX+ttfM^6 z0EC?G1>h~c-3;-2cth^6VeYUNH+Ta#Mq`C<Wu0VChQwUV`x@<X+@BJX9jmeshK~Cg zQi3ACWpA3d&4r#K9ysbp-{WYnP33|-5xd&v=bvY5GR8$I;8%;eFX^f77(+Z0Tb^4O zoXq^0Ye$I<+A;$<4Km9Ct(v<8ZO;{fB;q$(IPPfUNjkeC6-nB=pTqR6z66s!1~Xi& zhL+pYTg_V_3cy)RiiYDCMy}-sW@xe+e8Jv17^i1o*Y}k(Ft^NQsvULXz&citnVdD5 za_O0z*Gv~W(=JdFJFlST`<74>Gm=4IS6SiDlOE0W?FJ>QyN>`)8m4E0lC(VEA=m4M zy${y9)i|0Wh}1UplU6OWO9<gl*SB4`Q&gmfteW1}`LLMp0+L)~_AiN-SFFe}DQLHs z=uO~DdeAYK<K+(x5*lA$LViQ)>M%Ggzp2PT*?9YgSi(H_vCT#oC3!k}Kv&aZ*<}5d z>MEI>A=>e0h!9_t^8QTyw!5ReRW45MUNiYBv$1IlXIL!HI-mD!=7MJ!m>+u2j!))F z;^xuwm3e$AfD&;5U$D`oD=G0w`|r4UAz-a$beKfCxUnHsYu}%kfA<wJ1a1$^m^Q`u zga%HkWV#&q*##Pg!Sl)8exZqLq5SM5l)Y#-*R^h!_ydpE%I@&>bXVUi454V-AL&G& zRQbqKn50gTnq<r&>fQBpSB~qEm8z0AnYSrbsW4Y&qKT=?*wDmLt+~9};CtzZSz!-& zNg*1He#WRq!w+~a!7;CtR)d_33QAw^wOXGpL_L!8x9mQPiVWP&qtLGSLWk)@R(>^d z7@JzD#$Fb}ZJM?ifu0lKlsQ3$kvmO|l<P#d#c?u)r<u1k6|O>*iaYkr?u1@ZtI!wQ z&B^bbaT4k@zja!SyuM{!jL%rjCl_hSP_?OmjE{w$4+rjlfqg^4c^TB2o{SXi3pn*_ zy%mJyrRNxH@?{NzF0DD4q^U9re(w2-a|QVJrce(ZBPVz7_A8@1K&>s9-TQSFwW4rp zUrbJKt`OV`EFxYzat?*PSU)wli<!4M_89fWca$*7PM;HN3`+=w0kCPbskOz;dhj7X zY9s#Ub{ueS;tDv)`Cj5rH>}_X?TTHKw3n~s?lOEwnpOdlSagff-zO%xsy>%DO*Xo{ zfgEu)k<%9=+HSoRNV-d<yCXzQKc*KhVR_eH@wEecTVBweWp|{KT3l8{Iv6D*y`N`@ zQAnko&LiU0<di<4FlBfBgEf1*>|-rB6qs2Xr`>+eVj!nO8awaZMjQBR$sDx*NNoKR z-{fH7{uetQ&z~l}{|eOk2g3O;P$$nH2EG3h)cLP%aDR2Nm7V23ID~_p<v)7}ht7h- zLf;G1In?UgkI-Xa2cY?&`Ju&rAoXix%4UZ{9vNC3Sv(YOu9dCI?bKspS_UDUom7m& z0FV%Z=NHhKGR=Hoc7=#|O0$JN7#8BpaZjUDeY{fw7sf8L6@15v^C2cM4QNl&6=-md z?ZNt-@Md`1b=hP4d6pv_zd5Wb?Y4aWr^UN+*R3*(nKxIyKc2sPC0Dbcy&V=bR%Lqa zk9QSmJo-bkHrsVMtlJ{0fXV~*s$CiftA??%h}2lw2Qu-LMT(~`DFUt^4w8b)YpTrk z$6b-$(6?e*_!RG%TKe_K3@(6xvGVr^Mfv-hTHC?<JJadvyLI<Lty*a1yXsO+Z76b1 z*?0yi`z~(GRm5jpk>NntjwRL-^cw2iWB+p^HaX+|MlS~Om=lPaaWdSXb8-74jpxE* z!e`U`C-p;&2e)%cjI=dsBw>YTfjU@4N3>uh$!51)+U;li9)=(AHav=cN<K^c@DAoO z@B|0$WKbgd^~FD`o*Xu8n}2#%-<**bF8d#%ifAMa+LF()2Yl-#<t#pXGQCm>o$p*r zi~H_kfq+h9-^O?vZOwJqduOeaFzEWDEU1c{vn6FmBlDm&eW^?4<St!JqHh^+JV(}# zcxB6OdB`$3FqJ<`XpIr>vx8Pq2VLtI`$*@=t?A3C!h|=(FNHYbB{Xbsr{(87Fn`Z@ z0WnWLjBrReYgqH$(Veazh3VD$K|9xzZu}*^<gCEzSi|<U<MHU$hvZJZ?hx&g1MJV{ z(}-tOw6}vFdwfW}JeKN~hA{EZadp@>ABl;wzE?9yfl>F~w0LN1V2IUFa7`P^I!0tJ ze{10!DR!u~n7-cby&C54ul{O0cx>sQqZxA>#)|~Y@-f9gds8chD$zpmHV`KqRg#5i zds2Z&LWHQ(orvxg(Nb+tjzIo~#`ad#;^F532Y|?OxG7+q#~r{+i7WuYeUVR**c^{U z!;hHFz+oB8xdjW8lNL;+8sUcKxX~jZm+A)Y15Uj$%76Nx$W0w=xJIL?#8^w**=mzy zeR&#YBCO9m!!|w&TU@YJ295x5Mm-z6U|oGa-SgTONr)Mo^3-4=fTGZrm#sU*Scu8q zw;^0Dg;U*bQ!h7YxwCAB1zN?m71&JJSR&Afe~GQx^)Ij<$*GZYPmuzS;nr3iP@lvR zmFER*jGh~oZ_9-{(7=zfvH8XiP*w)P`AHkrd>JL)&H+*g_lOI)*Nsy*Ca#h`(+8ba z+&in<!gEugZ_unlpvV-%2!7F9dJR8TFfX`QX?aBIqJcX5@O}Y}c6E@9Ve8=5xprS; zY5HBWiV7k6t?N>4Gi)Oc<u#*@HL@#pE5pvsxGnD|OHVyUf?Q%9O5!aC?Eq=;6L{l8 zq!u8-zLIXHnmBm!TVYZaHmOl6n#ZuwgwFB{qa5kbGAO9=_fSJl3c|=IqDE6w#Z_8_ zA)SbQ4ijaDO9*(ULBhwBBe7EcSn#9O$x$(nfp7!8$@!CrL<C;Q0lv)0h9{^M2*fr6 zUt8xz^v{~dt&u-<(6`Ww)X_$Jow3_v!9u_4i}@lbL9Ubhfhd1`2Z@=}`Ekq?4RhSD zn5xekgRD-0y|lPCwJ9E-sb7z5f+9LACtR()`RmtZG^J$4p^Xef51MG4w|zCnwC^jP zQ>L*)8PNJtFV`U{Xk2lzsyMDx1iuOQ@UP>-f+JC+Cc=7FT4(Y?k(vibE%d&+HTJlN zQ%u6DkjpOksLxa%;C9CIf$GO+s0)cux-0gWz7a!YHmIae+8JL6a@RfyMZ9y=VXmYC z-ey)-cOgMmK$z9_nO8zF*W1x&!RSfHW5ot*&<T+Z&Ds3?dMhtO`--5xO`$@PfHuaT zDy&y|C|w3qsThji?<t6;W;bh<05&(L?3jZREr-sR42QAZ)>0+h!~+gBDp%3Rdf+OU zkg(VTQAgv_|IIQYWjlPyd$(;8ZTTvRkJ)Njg~EFqQ)mv@1#MG<fl)HtrlDQ$a=fQ( zKI@_W&?d#BJoJ6nz_$l*>iPs*)Q9>}4BAeMdwNMGv4YN(GLgDwc!Pzu?--^g^5&}| zCM&tfTQu^l!l|CJgz49>(qaUN;S!{AsNJ<FlUSdFndei7jDn|m#^EW0DG;>npIl9i zRYN{k%}xL-(fC)tQ~>R<!pJaqIIxC`!b2~mpo)|OUx!M+JffwzULL-tBCwd@hdjC5 zGbv+`aTZxHs)TYo4}!)VJ<Fzdy`;le90jy%wpZd-qU|J~NrQ%05*h{kTOb%?p<es! zpWv$fRE9e}--Poxrkxx{x5ykc{bUd2X&<0yNhBQ5nFvc4Ook(6T(VhcZ9;jSUPJ;q z08M$gXv$B@R;@zO{GC^cJ_UEndv&J)!jzNNug5Zzu(rx<%8Ym8R@)|@tYRcjTJ80N zb{|XiKDIFwN4KyF7Q8X8`SGQ+>q^zhcpJi4?Pm~g&x49_kN47&pw8HC_i2DpVmAL- zk}Ly;u=)vJXm<R#Mry9`d{QSy)Toekyl8-ML0_qzt&a<41z4wqS+6`?_iKFMc~Ouu z0i;ULt;>CBS43+kESgx*Eq;i6<gkc00&%AgZZ4*HBJFXi2>FIc(556;w1H#|&KFoj zqYu15v$~Z4-Pa>03HJ4&{9$qaUa--$%3_w@!6XlRs?d$2Y-t!m+1Gg_V-s|Rln|6a zsdFrqY{#$j(JOd;fvCLG)aGB0$`cq(F#}s!pTd{Om>&GoqshHdG=hRPnL<>)rewLh z0<zC`)7?Zz($)#*Tg2R9x|d60#J#`b9=mKk3h-Km&(K*5lZ+kPxeSe)dsL+Kgihp_ zuHj6xaW}3_GF%P!=z!ISWBgS0?3+QRdb^ArUAPbqwbMg3ng#ChBD*3vVDWIW$s#Qr z#h3v|6dlzjv%zZKf2DX}D{!q(d*?`^eB?vbwAKKHjo|bN(WHY(w6I!@ZxcW9OX-PE zMO}mtUTh$^1HvA0k38xIn<Oroh^%3s@mFc`qNC7s`<)nZb1O3nqfqKA8nO5DyWH+i z_HiURlS?-qH8qmx4^ZaW6k~j0Fhqq|_LC(xogvqS3gqAi934xu=rZHm@)?Ix!7;#9 zFZ#aontHbn%wnZ=%#L$H1;ac8owi%ZL7ee9NB)F<uh=5JHnwf9Pvv?(V!bK7=Po*G zjCM6wYPnC-<7b1wrP>fw5S{l#88bX{Yq{r!M_q*NGSTFtrrQ^URH1H1S~C00x*-T@ zB#{DneX6j`e08*uI|-h8&5$l@be1;pWL&Z=oqXKg`1-jY{R%zBn1<YmV>{aEwYdXw zUXb*aiD{(GO~_Z6EW6`T)dOtl7<e@m8ySxyYaXaGIVy_HOZ%{W163XZvDhUi!X0Wi z)iG=Fe$9TR>NA472->Vy3FJkcd4@J4VUh~_V4MNoy60pWg^p3Lj;pg9&or=>Y(Cc! z@JvV;kzl<NgR^sRo0MQNY2QxR@A_JA@Q#o7OxAfj1_u@CF2YU*T{PDYgQnO+*jex3 zP3wNH3;PwZP=kY^S3pZScf*k}5#P;mBq?8xAy)Q;FeIe*dw;O~TbI)sMoFiQ6v`_f zF&Qb(Jv|+HE+^@V5A%W#`#3**3EMo{Zdm%<nW01BFme0)kTV0(<IrxFZB;#q#Tzs| zNJI}**-kFa)hwx8h6}?>2H?naDKjHcnT+%yQJLJCr+nK`h;;)Px#d!M>WfM!*=uk# zZx<Nzoj#@b>rBYCiHo$zHfh$9?75gZ^*az$D-96ekoj|6Urvb6MvUIekNG%_H>N!H zDmgfOnTJc(hx=}{h<6ZoO;J3hwV~FOoz0X`q|h`z?S#Q@&q<P9){kJ25kC&QZkw!- zj_!Fen-rewI`)9~DFb_#yYaQa_a+538SS$Msa13Nxho$Mh~}QHG1cHP=Fbc#pJK-; zlQZZE#TVaTu-D=riH&WsLV{BxH^F&3i*n?e2p@d_D{&qQVn=qx6|I=;3;AS-it8Xj zk;KiT5&F&{;Ab}rgRRv%#bng_iJ?ds4j%{AD=w<l)VEw8PIgvPB}R>h^!&Q%3V4SP z^!saS)8jKTI?E(Pj}oOa$$4U9^3g>Xe^|aDa^HOG#sIbLGj`~r`dQRuAEf~`tbGIw z|H)RYhK}8@ca5njtA^84BIX@gpLh@-Y|R!!a~b)3FKWS#xnyRl^86!foS4w)Y$BFO z08aXnzDxR(CDC$`Lkk9l(D&sy!U{XZwRx4R0~@ZHBqNA!>7cb)a&KJSFng`W0y$b2 z`%%NA1;M$*ZK~aTOYnvURitETe)P_o&W>V6)yKTN9aLVv0-D~^YaY1DYxbLuZ*pSM zcpM@Hqwy>9g?A?z*Iq@!tu+DlH#2PIzsv1UEDi6<g#A=qbWhRo_UOPe`YZ-`*a+*- zSz)ShRdF}HyTG_bgf|V%@Kq3x*ega1%HMM-Z)fn^de6^}T*jh{8PxD9C;pTHew2U- zFB0Cqk4q#KCS3oU*4u-3h#9#_gKMW9-9+LcI>WR-u97=d9NS<jNu$(MX+k8><s@>B zyU`+JoT=Ea6a5&U3%p`dyfQ_jz6&>>CDGgLFYRa#r}UFf0m|>BP`$@7<nmRFDBG&K zbde(G42(i0vRC09v|}XEgBr%OQK^NVQC)eAVE^~>`FE}N!UnzJVELDa0rLFml>E<^ z&+IJ!{y?$c#pQpERr>uY|5sLtS=!pr3@A$W`&I0ZS7|c9iyhO-)<M<I&=^Sea^Yf* zte_`xzc@7!K!1Nj#`^m(8ql#d%o4UXPQU)ozd70FKg5&BK-|*5JR=a{<tNa`{`uJM ze`lTi55cFGU#O{Q^d4yZ`*%uPvyrj0{4!4dTlVSqKmK|5f3Q#NEPp^?znQT=KZse? z*~saG9q^C)Cu7#IFmW;`<6&bbW0nG1n3+3~v9fUSATUeYH~}5(Y^@BPfMiY%&Y(B^ zt6};*JY>wG7EX>zKnF2fYdc#TAZTp3L1ScL1$rmw_2sSNKx11IAn0-t9Gx71hSmsf z;0G=6vwH77FzE)P6ztTvoV@oa(bln^;+<@Ke^@y`-#<-aIa2BTbs#I`RRGIiA(D|j z4y?G6Ne~p<2qw7+7zDYTG?}k3TucB|9LwDE!}Ybm+P0y9hk!?E?e+cEzH906z@>Y+ zS;Mi1fGrX|-~H0@^S$?FfkM_l|NL_V{y75w9D)CB1PpvIZ=Qp^n-Jkp^_wx{zJ!bQ zD&lIpSGN~9KWNes+V`bnxfLQBSonwJg}r6BR>EX}44oavc!fdF6vg$GTU14#fhJPB zCU$_C_G)!Pn?_4itJ@t#T|=_QEYevXr%sTAv0@}YJY5Dr&8>c5HU=zmqLf%5h~C@o z(Ft^C4&e!ik)I2>+Awpbs;rsSzyV_^_HRt41UpC|OY-#9)jpJ8kiE!Rz|<KZM0kK} zbfMw~)T|6JI9oUC6B_$4TE-1~UB>vP4=k$T20H=YHjip^N_^DrMa3stjp2KZrh?nP zmh|b*OV@c@>m-~yo2rL%;)ridyCB;xf$c*^CEJ!Y>#t~;ZUPU)-nZ5U5RYYWOmk?> z-c8oD3#Kx;&^f29YF1{xP0AaO#k02L&rc<^#s1kswv8sr38a{h&YVi<8aL9{0&6cp z**1y;tI+V%y@GbIsO7R|9*ee7w323~KcU#1Pgr#t^P;bwtUdIyjsUMKk><Uh5`%G} zb5sQojVE9qidxR$omvnJnb`S=9pws30l<RwRTAlN#%e_5E3B}m#=)(PY~8imM}QGu zteC+G-+}pkVtDj3LKQ4^j-&eYuB6_=*;(9UT64z4a9?!-;@+xhwrCRQXWZ?D14xvS zG^!~*O}4T5_O9^SFIfz3?o*_>ke9j!hHSVe5FD(UH#uAyqFoQ(VGg%1Z)&-wMO>&! zppvSkq&+&uQp0cJ?S1Kx%1qsTEi24y74_bM1}gq+(U82ey_AM}4Pq(KxC=CAVmiQ4 z8sj0zoxzE`q_w_kici3BbX2`llZCAbZP^2Y3nj4zRkc7_se!|(R9zs33Eb37OD8I+ zS1#vIO`#uZ83W=2OBLl2532W!3RIHdqp<I_oV4hOL&xfRjGG!y)xRtw8ljMzsB&gM z-e>`&85pk;puvWI8Zc4AgLzQ(0rkm})IBDyFdlcWx)Lv{AcREA0PqU9hxKf-fQ6%( z9z27=&$-q50wx9pY+O^_<N&QPrkcb^^vvl~F&J3xO$CI2NoczpDqxWjx;)_|)S$XD zBk?HpRelomgKiJrz!gS?GBlY~|M<V2@Tvct@c%!b@E3OM!%Lr!pogtpe+JIIwo`G+ zv&1`?DUi~v*N)P>m+r8lglnQCfoe3t1@ngzv8@nKvAMR7h_^?^2y_aMHeU8C>tb`! z%9P6kqo0}08?+J$gPrUmP@T_9;$+A6_Z8=5oKw-RN$a8PhgMeut=)hoO*%&M^(HxE z*&{RJubMesrAla_30pmo!M-xrd6yKiWXpKD^~sSwl<N5#+EN=m1jIw#k(<y#s!ooe z&|^Tqh6!`%RY^62z98!WpiX`bCNv0&!Xm`@X!-6rBf{*(+`Gh&vMtR<51HFENnucp zkrevO>XLFv@#M5IrpzMX(j{-XjkUU7%VdS)LyexQGekDYsm_&@S#{O5B;?j+Dbvy? zQ^md`niaN1TVqS&%!<3x#rV487mNP0sz^N8D<!jg)Ck2oCvtO`zg}>gF{z+Zur8g9 zgn_Wg9SOy0aNUv8RjmZuGNSuXhb+OZw&;^oj*>B&)#Uq66Iw=xB7VGVWOB|vY@uV` zn(|JXb&pxHBh?4;lw$4t2uVOfhGjS!F(u({JwJ#z&2YP(su0yr!nkkXvNlRJ37k{% ztF!=fA=V!B(v~7QO6n}KuZp=Ve3!|-1DULasSX<{)kdHxC?<ibp(u}RLg8P!iRL8; z`_n(zcn;|>DH?lP@rm$ja;jY75$z^+??@dLYSK^Rg)h_#)1AIoeH|<DrH5>`aEi8| zx@+Da%GpFX`6n0<Bb26ROke$f!j<McSA5>Bu)1$?Jtf{t|G<Fm@*pJDd$lfo*Yt58 zJd7$=S@kxXIB-^wjqZ~rH00SEvia%5PuLL=I?DL?rA&@^KTrm*F7<0Hp}aaHHa!3( z?-sccR$L!5j+Q)~H5B<-qi)%5t*1RxJ8Wl;0!((-W6Js@MgtQmac*j&NM(T(FWUsF zGdP1aTBTZLNIvp=KOh;pj<%?{QFr&L1h%Bq&+x5W<#0}wTj*|*kJ|f`vb4pTBOO0~ zhFq0Ymi2$1F35+znscgDEgFRC4SQs0Vn~SUGS}H)U0QT2Ryu}e4MwYaO*rQDP*O&j zh;u&x0S01GapR$US}-=Nm}6v%TP!?6y_`IZWZkuVNy%r*pbEmTbW-xltgkJN*(>&z z4&M&79Ur_!&Cze#Sn1}i!yW0J+0sE(Vjz$FJb-&h+jSMI{6&`@FD4Uwtj?yO#K2@R zY@pBFIi=IwMYFE_IU7K6LTF04vTszn-lKfex_|9-d{o?gXU-jUwf-e(g=T3*i|u0F zEQ@l<``l!1!vwBsJ}<ci3YxMKXDtGVUO?;1WQr!-t5eDTlT992-Bg9XaHSqM?I-c2 z#2`^z-QEDQMI>NQciso|0CefX3`7Zu!ohqD;ya)hj#xNPO(bU=-}&NQa=mUw&)uwx zpIe1h?oU?kQO59**hS?^^%7Q`dRt$$^{IT7SCOKTA{ENT$_i$owXpC%`+QiYzy~PZ zc@(2y;;If|0qz+<+gJDy+sX|L3kIhW-1vSFbXOQyF<sgLZxY|8WkWS3i+~Z^c8?bh zvM2obIUr%Rpd+n~NDx@%r-h-_?TdT?gU;k=0ZC^uLiNR+2@n4ZQ>u*9Zqlt5665wL z=e*R>kmJ7bvcm_0ed7iA^^U68uf40n^$dcN4wBv4l|7n^t<Wl=8mTg&><An%9TKGy zJn2-^B$R~|kl)o{Y2)#bd&l(nKUoXyj-NAlVD_W2M1Kd)!Gv>t??=N@s!YoAp`3n| zR>`oAMK#)u1hMNzpC|#yFS1hC;51rTmKoO`rTv6G6xT<GVwJ@eVwWOIs3h&d1V822 z(Ff-|r<N_&HZ)>n&Kg`dCgEU~p+kC}I5?q6x}boDni#1j00Y|uJmrbwqni%n=vlvF zjFI^^uO^eLT*wN?`JQ87kD>C4>{s076;2Un-sv9`R{0+hBG$O)!-M}PeEqvy{5&vH zMiYZe+U@ZRN0W%vs;jxXGBYfo&7j3#UwsgL=Y3@?nL&&j+GZR83<(3Or2_-lwA9P0 z(8D2xHO?p)^#QR7V{jJe$(8_7qHCPQR1N)_L<!SU&LT-~epC$!24k<rMAl-=fgqj| zS5q03pkn?sDyj(x*=LJnY|L~nG>XKi7ZssYPb5Sof}vYVnvNFtb2E-vHsS>BPAO5A z<dKAyPAs@wakzL2V?RLowNGh)B3{jXccQMO6?4CHia|cu0|UKt%J01-QV?>iWIfAu zi22@q@FGTa`O`&*%S)zR_RmcFpQYtLOUr+jmj7riMIKdhW3hBkZ*|e7KrcBRg66G8 zQ&5MOXIOx9@m5G}Cf1OIUzY5;NQ|rtIExh81&r*cJr~kmZu}WCWvTib?rp|h83yB| z%YHS43WrEQ(-2Oso3MNdYaYPeLQItvPbzmJjxq1;poLtVuEAT7ETmW%o(EEr$_Cl% zHod05gmK6$B0$G#YT5AH;T1aRYYSn(uSbfwhtI)D83B3A0@-a!#yWHtrSuHA-*SP9 ze8@TsLBr7_Tpb7E$`;BQpkE5aNp;C25MweH-0&;c9g=u;nE&!v+hj9+&)tVMASyn! zSY<G}B`AZb6i^7t@(KGZ<ysQYh8$ytb65UM0wYyiu}I+Ow7QAb6>b!1NFw0a9`FcK zMvUL_2VW^DLjZ=ze4^@&EGB`U2TYxT6m^HCl%>#-qFNmcp(X3>Fz)_vNsTq&2{k)$ zN&>hM5lTt&RJ<314-7##j^>2cl&;&2m#nsp{E?THhft>mb5c{rc3C<&nG}sVMx~m@ z3^pqeMP0@6dEx|t1pWp&>N#n{7?!BNhZXXZG-U>!q_@`pge#4B>U^KwS#@~5-0hm{ zx?L$gd-_a9ybw;aw|`a&OPd%YA-@&wf@O5ySEy8mjnp^dW3E%Ls?Ne@^A>1Q(==qr z&d0DY<2_b}euMlb?%26f-F}1i<I1H+lk4>qK;X2CdwX!RUhCQ#rVa30JZ*YKD1xgU zr@1=8>&1`|`64IrPeNDJh;*rRzxvEs2%qRHZe;<F9*T|IOZbk6yofn5UV?F6Y6}<3 z<HVvgp5aT0kpOIQF~xj!PZKqwPUdHvS+j|5g*+xqBKoW9-6}O+j2!F8UMI(mQqdON zE+Wysd@J$$)9R);P3;4EM6l$k4qZ76uq&CE!B%0W(yRPKs=KknP{utVv{!&`Ge#QI zye%-Xscxw}lC?cQILH2I+KJiDrdgGFNWnKgn;y_<&?flyd()Id*SsQvDTT-=)hSsY zRWleKC7ioI)qqoTv~gO5Wejbu&PSqi94D&8%E3DmI%v(t9wvY(q6&W&^>p$$Q%lX4 zB0MC@af|-M@$4;jYU!c(5sN(>jCSor49=B_E(2EKOjp%~lhCYI@XopDhzasrJsnv9 zt?yCj`vuBntWvhDsEPdQ{S7$d$<SaC`~P0v+wG-6ntwEM;Di8)8#*IcKAFHgrueKV zBskTUF*CBy?}#p<p7NFftVdqZ8A#nE5+#-QZ=V>7L5oSf8LRs5uLnf3(EzbRmC8To zP=tfTV`w))B@A~ggia!c^rDo#){qYC7n>TwPgbD9;Iko`Tb7Q^;wqVo$ng4JH-WJ| z9fn<8go*Eqc3@rvZSc-1Q8P(eNfIWY2t|pS^$xW-6lJiE)M@WSVoRtAT(aa^m6d-t z;hG5?LgB2ek}!rP`miRBeHg~B5e&G~H*DD)rU^C9VtT?#Yg;M{MsutI1XF1fY*@}w zLh**7JmkV$fuw++jal!c4*H8@-emi?GSS^`Xv~+e&%2?Wf&xOJIjZoR1qs5jOaFFO z+ZOZgI?OJAAoDm%KdAO<`dd7Zr2D_AGhLV)yUg?pUZZ(CT36I@<OU>xgH^~lwgDg= zDa+{Z*O4ITb1B+|?&n5M5VUX4wIs)D161`UnseDI2bs^S(c)ePy?;sh551;kDeDvT z--4OVqCxaF<Ga%fqsU4(If6^h1PlwT_eg&7>#w!c-=kJa045Ogd4=)Uc7;l)c^MYG zjsYJf-OWg31Bjnd$17Xh;#!$j+<lDTEhg1k%_Yg}q9YKtv^#trkV5L5`GC;VrQ#R# zQMJ)RPd8<NW|yPI+b&GSiomRQ+SRo<#|eHQNUJ;oXd4>Uz7YG=Tz`)iRvSjx>~WMX z(pKQ%s=qXasTg70D(4mNv|@UaTFOU`-^86uUxn`Ug(?(UuymtWWiABN185fmfXNby zGrVKSw($RKfG^dRn1~juX>b2cxf?z1&WOiiGCNYuOQaGubDFmwV}Nejv7(9B$O641 z^On1J9Tu;fyBBnT#=b9X{c`=S?@}}ATk)AJ&4B<MP3AmJzcV^u<xH0hV0+?D%aYk3 z>elKX6IsUE^Ihw4D<2reo>Z%Fts@*PQdBDYqYfd*w{%t>muA!`s^?;rr86I1#*@=i zA3)d5uU{`z<?+~}lh360c!{_}eEs;kH;rd+@%0QttKm`$s+IRcD2=@aM2+UbZWT=X z>xJ+l&>%CTm~G}st{$R=#?EQKgy+5kdfB&sjIm^=eGM+E*0DiJ!#$sDP;%F9=ti3X z#Vsjo>_j<q&gh{ZB4|@B4QOo|?x7^uP}fkMv!Vz^nST*kAb#ivRZ~`lK??0gK5mY~ zsEudHV!VmR4yIKodbsS0fBvxZ$hT9{^nAXveDe9UI<swLQ~$Xu_d28QW-_#1uiA9* z3ZTgUMEOKZa3|DI1J>o0E2n47QR!xsq`z%O&Inz{K!xGBtiiusitE)Z@&y?rdf-|F zo4iDfEaQBSuWs%yvWNB-r8OPZIPD}J6rm!um^V{0DM<Ku_qWaX!XbkQnG(2w+H}D` zxGc^FHI!qAfB!>>toT)dK4O5A$MKU6yr*;ekNMA2Q)-+GGB&@28SS?^h*dAQXK~Tv z<?vgV2~Dwx)M=C?eV)5Yk7m|y-YN*6CUqVA>U}>S=Sm`U{VrI}<FH|`oK$E-UUKN; z4E*`k<I&kfd4uy$=j&znk*HwT4U`1|-b7@0ViRC7D~=Rj66-1`KHACnGRlT_F<fFt zf7lOPu~9PjcdIyfJ5P?kn@GsDo4l7nm!bEfMyc~i|Eciw#KZeg&1f0@Ay|Wildj*o znbo~WQij=Fs#K8A2P`Jzwp&<}cbgZMzA-k(OWyQf%FC*?bgG*`iIGnKZ|%3(+*0I4 zEhCFuo>lxq`el7nY7e)D195w~Ir4N~N#j*_`(>$qx%!cB!uGy>4^iLqB=`C03!>iH z9=t7&(Y#PP=d-^VgZorReCG97xc7!}(l?vMN&6jLkG;6&>80zFVad#{w}3OSUHLuY zJ-1@6*D+VqMj+A1PZz;TOD1<pr`IpJKB{KihHDltI^lA?BM!-FFp{u_#61E_kr^Z` z>x`u3{+V)Z^l#!nKYPnan1yD_pgflUO<rzEqmhJ^{<o=DkJmYB-`Bpq83^*F<>qH~ zQGI|}b)Bg*N5YS~xZi@I`q;GJOKK=g&{CQChhAu8ie^h`qC{YiK-@H8a86NeyLb`# zy8g&xo8y?rewbd6>-JD`#BHVMJX+9utMU9Ox#8f5KFRiBep5&2+<)s>&}KN3i0zWN zcc`oJ`Xp1=dw9`#W~mZ8c&#@R?@klH-yZ-a+gq(b<#G%3;ygJhu2<Q+(yO3Fk>Zjd zeA#909i+r<va%tmQ0vrxbpap&40#ykKp~JA*<r|-Ay06TG5k6gAgu}7AQQa&N3h1m z?1&D##X#S!<?q>E^ZxMCZ`Sq+93PYCQJXAx!Q*;GX193XYI?e)EWc__-Ql;mM>nq> z^U|r1%Hg_-|E=vw4<W5b2EhHatX?4b@Npv%pjy_lGmB{Zuphs@E^}}$<h6%*oqE#o zrtCYq>)s*ik=KE=Zg+R=<q@pwaN}t!g;y0u@4OKAYS?q*2+-SUHP@TRbL`r*tnqFd z_GqbTKXyrR)h)tkEf~#vfHPrsE9rJ__7mO?4gm!(9~{HBPQ4N}an8%Cm!ofy-Grgz zRXQ^$_@#C7k9MojooZiAfL1-x^!i+`$njp9=zKx|sLP<a$8{rze)wDOsvh|NRPQpF zpzL}mPkllqQm8IIcHdp^CXBk<8+i8Uc?Vh)J*g;}PuraZo@acrcG5VwrnZ`GhppCP zxCZtT-O|{uBkLE<n{}|m`VCJv;WiI5<$9MrrxW@feUokcUc;>`T6YQbHW@BU0<`T* zaB5#{25@$9gcYTuY3oGlc8z2Ut5M#SD6fNu0(u4<HhjhCCs~air6@zVVD=cd$;m<! z_CUE|L!|_)qdmn3-!-fy5G#N@D{~Vd(yVXQ@7#42r<(+CiqJk%rSeCLwY>WV?FSu- zd+$S4dA%Ge&z8J2snrWLu+{Y6qXw|CEQQ*+b<XA)h6}mV*DJzC{MK6iAY0oW-oYw< zI4z)FpFhwi;=1=aW^Oh=y^~*X*~q51%F!S=MZlQr6XPS*uXI6V8d=w78m?iUHTzCV z!o9cD`$KS@W@bY(-0{qxk7Vg`cJ#be9Jl<;cqfa(BP{4yvKO*s@T0a6qPA=vg|E#W z8;D9!)Ho<++eUkMp%Q}rI#fbvXYLa;3I80}bt_ZrrLV1A%i+X|{#6F%4rpLVy}iza zGOAXS5pB3#pNKV<9G;0&g9UE`bIOmV@G_}A4DNHdR$4r_2jo}M+!vl!09P6`L!R9` z)GdY&lh+w+$0z4yLf&I{`^Sq2jJiwM=T=F;{o-^yin^pCFexC~q%3HFn%EFuY9_7h z7OY+(zeP~Z<Z<%%)l7*T(q?V$eeDWBo3G(wO;y<db4Ef6P|6mJgklGxW#EbsS5IUI z{w()Ov$c7%<PYST5pNnJSU(+78RQ@c$He97D;;zc)GAa3RE1q~+Q)5fY3Xuzi(Di| zE|@J>?A3&FwOxO@Eyw5DqJq^u4eHEqIi7?r&}}8)!d|QfCYej;5Q#T(RDrL;mN$5~ z9Oeg~f(4ukwH}SMv77^8^-gs%zg;Lsr?rlx%zSj+{%kp8-7PtCV#h*c6DU)*7&i*; zViYmUln+6cAfm*~m=Lu)z(6$dBhoDNN`=S~&LOo5{jg)?r;L$BWjO<wfjX=st2Edw zy$I7iL}Sf-YF*N<WIat?ZW8tly2GK2U6XlRg4WM@mp0BGFr8`2^v9p%kbDV^MNBAr z17R*-YONw(dVXx&?c}a1WVuW}oy@OgvBS;hv#2%e!rt%sa8`1)Fs$yd^(fUP`9#uz ziO3}WEvG#T_b*}CLh6sFM5%E*ng-4+IxhxtJ)Q3%+V39>qI^5Emi+Os=~{LhO>djx z_1IF($sd4OzKAfWH#md^hEmvo)NDM1Nk}sL;m0BBdg~&yu3hJfrv=GTU$-&l@`r;1 zR8MQxqd~yO{l|v>=_UwBuM3l02|Z{2_NF1`tLm&;P{qBQ&-}ukICZzu8KZRAg4h%5 zzX*Hc#aewqMx&U<WB&{$2v_&?Bzbb<caF38VO48z#d*<c@%~OSGyTF$a-^#TRw%`L z=hD@xJ05wKe#ZIGS5;9FrKSl(!>C^HVb*UdRWY^Vu2WzC;qG*T?9?Ok^Uc!)PR7SM zdCa8cNJ4D8XeL_=;c_YAq|Ps}#3qTtpzulu=7rM@B8wpEE`V}eTp4Av8o(j)8|wjy z8f2kj`p~@Y)+#U`EfAPM32smh)p&roIsV>uUN4(;DPuNHPkmLgua>@JFZ)s5w8(Pd zaUKuZ%H7oi{ahN$W2|#GDe*ZU_x@m3kPKiy70x9;g=n~+C@GOQ1%n8)X(n7#a|o&2 zTl}!P7~*~1i#<s?q}{yYn$)_Hti^4%K0VjQW3f!WqkBIQ=(=tSxk%_E#>`Y-r2#3a zkOaO#4Yg?E@q8tDnt<HUk3&kja6OcFb2Dp+l5sXKmj5tOgoq+=&8th*YJ8&(HTu&< z5@@25GceR=^?|)OqJ4oJgW^rVOJ6s3P9qUvgb_;XPyi>~mnk6USI>+|Y`4<2{5f&g zp4mU==S_hKsIl&O+Z1<ciIK<gjya&Ti6UqGz5lPay7oK@*cR9cg)9-!o(HRUF!jhC zjtl$ahxH$%w>?v~{8o3zq;}o=DCu`k%*_0!aqr^Qt-3l*?GY08^Tuf@Uy;WOGu4?i zv#k0ZT3;Wu1J_1Adf%>i+AkLi3gOc%aFKEzR-2XHj4e8!+ssGk*wynt;FXatR6EHT zWu3M^3)j+p*oTsMDWTM9TClJzvAM2{n)NX<bl~o`6wQjw=Jkd<$zoGn%@CKH|51&6 z2`2l8dc|i|L6oqDIWYYwyYFv{PuTsrA~K^=kx%YM3Yg<`%uY)f`R~A7XR|TdoR~dv zW^<5<5>AO&7RACnoS2*5-%u|w4||MxVd_6M_BTXd$FFFGNm8xq!Rd<iRN{z^!x7$e zr;mq1mylO*V8r@?bQ3mqh=MW2ZqPo^IzU#%8a(>K2PFPod;n4fP=0)_oP@1HH6`^Y zh;1k_$JHL}2~pg?A@_$Qa-e}0{RzSS5Lfjl+E84Wh+>ZEF);|?#bk^G^njl&gh0zR z+51!XBbQ}DPlGx2*LaAFrxb(>+Hp#Ma~wr6qH|kM^P|(OR!_)_m!u{zc+|^d{?Lu= z5R?;$<vI1Usz?RI2>MfPu##P_i({@Y!=#m+Dg~mZwZf4~cUE43_JCW^w7DAx7Sk{- zguqg)?uJ9!Z9wbj%VK$Edf_~L?pjawopu8)-etN;50N4A04vbqCH=b=FQk=Lywg>2 zFw%kAcK(Z=K|dcjUB$W%(n9-I!Gag1ePf!yP}*|;lr!e&zUD#BcP(F3`D6~_iOuOj zaDD}YdaZT$H{gS|M@1p}4uifG7TmRX9lZM9(-ps!cMVn0kt)OL1eAP|ejIk*Vf^N} z_ZimSz3UCa4fE4D{f^>1VgQ~kRGsE4{yB)%39|-l!yoy?+!mRvd()t5cB-6E=&=4e z<>bttx@{--LodK-W7G1u1Ax{*tl_?u{KJ!{NyS!R7e^>uM&770f8hHIQ<7-T+|3$H zRu7spB!7`PgM+yG1wDxily9Fyolw<jFs$56N!LH!*Oz_RSerQQn-danfN4FtN<Ozu zuDCc_I9~Gg-bHoaVh_){k{~CJFw|i{|2F<b+|H!io*+~Gel5*8lkZ+T%~YSyX1*9D z<L;VfL}>TG(woJD(WVM;J}F;!kS7*nj;cmJsuh_hED0eOB!Pxi@K@a(DR+Q!;_ac3 zj8AJMile;F-+!Wh1g^;n3Y<dU%ik<L(tLZq2&B>X!W1M6BDAzm!9tz?Uc|PWM-_+3 zl7JZC3(#EGrEB8$>ZEIpaotK^H~|Y7NpD#niAuR0hb;eiT0pZNJ|uXWZs}FEI!wrA zB^FszJ*HQkrwBm@Lw15`Qb{RBQE?d>3xaO>mCJgolEtKELC`JDUjn*qLZjOnrvguG zI11xKw)@=u119)!^RDf2oL@J&d#T#nbA0i!t^QzP!dBnye3Xm0&-3dzt(ok_y5v2+ zw$QnxS#`x|#oI*oXe$0yKgKjgU6<=5rsh546>Yx-$`fZ(TY>jIZQhpfHqG7kuH8+H zH*ka`OJoIF7Bh937B1#*yZ`dZIYFQm8amOA8_^cdi5APEnosXgtIfkC?vc=%hM+?~ zsQx`2vt{7%IO6?y&v+!!Zj5Y)4JztnJm%s3!ACi?G|wxP{T2fknNLrSXpY<sXl!`e z<?9+C*YaQ$?$SMjZK}GtuDnjiQ|*!<qG}7F;OFx+qG~ncw5miX1qNO0&Gk9n;o#6M z#1Ymb`A30zJu{%XW#Rm<P`3~SI-%hea6cku)_<RwSts2ECF6S<i{C)?#=}&oqrTTD z<pYJFO?vlEDbu(20#88w)?(ShSti+U?`e==gh7D%k4_2rad@AH)u<4Ux&v*81^<=Q zMjTR;`(9PtZ-=`n+}Rro&b{f5ORi=mDV`giZo`aq{)9AE%_oT1wGgXj%HMirHogC0 ziRV9f@9IL2!&0zsQ0h;GZTDe*_-@uQoMwN2ub^`h=1(njY@GD43<@6lhta&=Ti;W; zHU_NitP=|)i)|!h_uFEtomFXl=o{S`nmfBqbgKcL*J45$-n|8_Z3oz}@^_csZJQcG z4r8gPnO7;*iGUh60jAk?7F3>#v#K{zHNMFodZUGWrzJ{l>`Mf=Y*Ju}LwN$qKsJlI zoxg(3f*nB`4VLriJC%%X;Ude!$c5PtJ&&#r^g<^IZ@t-U=t#ydm*aQ5kAmr2S-0x6 zxU9ZIE2bfh4NHJMX*%;E;6Qf$)Y%Xn*Oc%&RFYfA?sfhs(zzvj{Is4|EO-}LyyA6% zlxDHwG8uvD?a6BMw!!T*X#aRNjF8SQAIJw!E{T*iMO8)tJ{^ez+r2mV5}N^)<parL zCkvq1O!$|G&196aM6-Ufu?-QS_sDiDdxk=-Pu`w}(;!uHuKeT8I_|+6aDBs*&bfW{ zk|jUOYIBihn=Tc47;QP}#nvh^TupM>GKvc%pOy`GhSoQ@D`R#;xAZHX4+r|Uqi@?D z%5k5zdzl_r3K+YlEIt|r9AK4k(}F#j*T0aFKF%rKBKCJGg;t(6i)=`|7>;lNkRD(? z^7rWhsU%T7jXD!8?Tv>DEi8P{3SnhV$o`LaSnw}co~6+1<VYxyq%ME5qyq?p7_wdD zMB0f%!6<vSGLh^Un^XHCozVo%9;W^HA}oU~f@aX6$5N+0pEZq*_ypik6S-6O^g$17 zI*jeLD**qMHhZow%zOitfS|&OH>&pes<yjV-)RFnJO~twaw<2sp_VM8Kd@@lpo29x zcZFahB>U&pMPgm~rCOanRDR%Ex^79aM7bTSJ>~;G6$=S%l}Zk^zuoiqc4Abh<33&g z%0Y6U<pCU=Hz5ni8^%~*BQ#|7F#)x(dqFCtS_B8@*8*6^(m&8Y2M|Ip*IOIY6UhNG z*AVt2A{Fdzg9_kw_3yvh*Pt({E@`t#jztyBD>0!-?icWWDUs=Gu5LfdcbDYu+Kq=x z;g_jPSGU6%^|9qa(yYtL|GB5%d)r~kgFG~}4y8+DW-eLWc=6|Zx>Re*F_B+-ch>Eo z-Al`36&9rTV`&$SCA2$q$AJzq?|Q5h`rLf9v7&u8KB9YzNc!!h?Q!94!xq>MFaM!T zRMpKJOwLhSuKa8bFRAZRxb>CG7j9vslXegWJ=1pY0glH}$0NGu(FF`QIgm60*U!2q zI++az9KmuOu1~{l7H=|AgteDucV*}ZRWT^BeV!^KIkWd+eT|ph$HJ^>LSP5=a*5-7 zZM2;J#`Q+2{V8=r2`N&8se=gQKN(06PY<HhdD&2mCkEf4x;ZSQyBoS7q+tQ}2wvLX zxK>N=dDe9qIWeG+Fi`z;s}Oxe{Ia+-v*qgVxqJE>U_wQkx_en|MKp}?n@be)ReTaG z55|FYWZjVOjUq!2Rj8kxN6Q-~zzR4%Ml#r>3tTMjKkd~7f)W>MlJP+1<?l1Lo|f~6 zZ)|6s_67J(x}muH_9SjMv~=M=Bw{W>Sh*(QRc40{spPu`AlBW~!Rdy#I`sn_^6r*; zM?7zLsN41%c-&0pq?1aT%uiuV>?w|n6(5G`C`3o%wp1XcZ_DfSBnj;0Sh-Lf4oj7b zWk>HUK3O7->kRo&MZaSM0Z*pjU>pD5l_hX@3h(dImYf#ZeigSk{RVo~Osi<{HwbNe zfgfUu4clVuH8-P^ADniUpNm@eB2qg1w?s;%Xo@sl<F4X|YQ7`t^fvc%jk0fBwyoU9 z_1BFLGlDLA;X<C>LTOd2n}Zq~HY#Z=N;vET7C0g@xqM>@1o3pZaG^)zHSN4WRs?R$ zxunTen}8I0t~GG|azVGB&x>Z?pKng6%89uy{mpa(_h;SkjzQIJ_X;#iN{=3krp70f z18CXAEx%+V8-9&?56>a;<QIVn{QrH8IxkF6!2Be-0g~{0(_u6i7TnH&Trd5Q=d*xD zTmFxCI#Fq$6p@K%Z#<Q$;r-({2kN@&a|5}Ee#TT-dxt3%A(TI`y&b5O*jkglSyLpT zJ?$t6)_@A$JKtD*#AkWcBsS7Q;!5%DvJW3LRs$>E7EiurjJ@)8H%P>-?8r1aL$F8( zRD?dv1@xhWIx2qO4d@*=5)DUVlf`!DLA#m9Utl-WCZJ(zWC@oI=gwbmcbNd70vbPV zn2l(kHi;Fq?y71$TnS<>JKZ>a)Ht{epI-4gM&ZvzJX}qZ;F&h+U*d4LL8^LtA1m`n z@F|bEv@jo9p<vx~D>+O8Y|+$*@J%c4o;pALS|F&Ni}Y^0aJ_0I(id>~-d$|B-VOPK zMXu2_+z4f$DLb-Vufjx&DdhEbkE3(*45$e(2TG<1|DQJjL?*N`#5ZGtG;TW{v$=g3 z8?A4q=SWdK!>4tklAi|rsn-IoZr4z6Hj1ima2Ol)u>cRi{&Z{3e7r^D?gvyv2j5E^ zJCIonz~YMR^M>vT$L@F<;HG}KHM4lR6<REG7TN6<bVztgxsYSlw?TH24ENEqGYTLU z{Sz6_L>f|YtK}JJ&jeR9n?Y!{5#pOHhF1X+I0PU69@ES}QWP7l`7F*7ZNi=0Z*evJ zBYG7_t8p`=S<HK5BdPVEogO6IGw3>WS>Qa!DTp?1_7z7(rdEj@c9<U0(!!z@+eYdr z%P$NNStjk-L(p~>Y;y9Sz_q#`fZ9$$%;uoVpV_;k6?2)zYhJ{xoZEISovUmmDEMx` z6>0;I0TL>dlT7=`a^K4sTGX<-(SDF+S89md=_Tk0{9Qrk->TX@p!8kH5U9>qmlT>k zIFnGKA5U9a<W6oiMU0=IPRIZ<6#3xDdS*fN_?v;ss+#?yZnIk<w;m&Y=POi{hBHuG z{voNHf%kO%T@qs>A}oN&+};$E?tXyL3D$SvC?FEa9WfGGAqIs5p2dcxoe{r*)p+^q z523W`BT$?EnTzI!$07NRdo8{<ms9*<*c-DFtvXXg689jPlYJM)f(<|Cy7d+|@W*_l zzILhBnQrawNJs*0Ty2md+*IMbhYbn9llVo}0D{`}TE9TaQTeU+h(D1GO58lhy-+7t z+5ee7Jm%<tFHSkP)>;*Ll%^ZT43)-;bC;s}(`Bfm_daG)+l{Kmx3jF*Zg7Vw6Sloc zers3Sx*AvxS1Pi7knpC6!qI9`?zjuSVhlhAurvRT^>Q#ob=#h6rPXTVavqbPAklma z)LR_5Z~L~p-?>8Y&1Is~?K4zaBrL8~9>4>!MkxkkZo}B%IfwX)$%u;<1LXF*vY{XY zx%~*a@bGqBwfI|JSXgVvxIf{sNE%Eq*|O@cKN7VUXQJM4e2#DKe}hTU+mTGZrAy#e zUnA`r(Xrdv8P+>REg0(k0R!S}4qtj(L#T<F93JV5yy0dlYjkBqEwe|KJe(>j+2h(4 zgH~PSvFjx`ReO2{4^Ze4h%+^(NznYoIH*zF)}RK${WQkMHG-&GyyG@6=hIM7B4Tvg z(@eTJ=eCzBYBC(TdnaPhva);|xHt}TYGAQ{9|@vTjGX^w%WMYOG8ylGk#m`#o2Lhv z9skwM|E4kiJ2c8LYE?sGhz60;RygF*Yy`8%(!&zI<&}Wr>sI)ERMfPKuo3y5q?UCZ zY^Jttn~%Hjy^Q3WwOU}3+2v~MXjUna+=;o|#er5$xXD88Y1kV#Lrb;$b?fR-9}`X% zqBAbXN94Y9oV0P<T!9)v9{q96cJvTsyX%z}cecosP-vx#A!VdcI2zMB%-XE;AY-aW ztjm9yN%=JeD1p_b9KTI++O=2iA_rX0J`tg5UW{nthb{NER2NguoR*fq2s8S|->@dO zHDlk4;Vd@~xBM<xgl$6tf?v(0<LA9%s@-{SYvbmj<c}`o%<_kWTyH^Z@(i)?^4{+| z*Y*SRm$UIVuoqbZWHX`yM%vb!xl}yaIYF^KcbYK(utuSALMIE+$kSYAerkw%dgj(G z@Q=iuJHy((g#3Cg?bzNQM{}Pq1GU`Z4tdUxG%Jtrx!iVLl*iCJw3j5DT+lS{_tJ%a z*>pYX(a}IQ-E59OooyX>m7iq3B|g4*@PcdII4wp!cne}&s0=}#V)Z*@K~?eJ$ldEy z>(a4bKD^PdwKIrDFOZhpe-6@OY;l_s(i}(-)6M91)_5@HI*M*N_}qL-<lpwV&d=|5 zl>jU6)r)Jo7OUzu4c;mSo@AB>ARy%7cqXd|=}B&+n8PK8FnPq{5`F6q*+_<WVjS8= zu{wX#1tg4aIq7M;ALn1(^9`O`w<I{>LPR2+8QNOtJ*kJ^+xn6o22O)4l{SbD#!Ay7 z)%$-&VgJu70yGzleZ=!;JSjSwN7G}FK<7GO&%rf~#&tn}&KEE)0xefpmDMjyK6%Km z{yf+LiPA_`HbknVfqS`d9ZuS_GL8NHm;_xL+4h%8M3INakOim{*(V>Uc4_!b+3!jJ zi@CR8i|gCdv~dX<2<{L<2<{L(1W1q&g1b8u?(V@8g1ZC=!6~3{D=6IE-Q5c)U@HIa zp1!7g=AAz0Jy)Ok2C#SSwSMc7d-+qrFY$W1>k_KCgKZ}J>_>uVMDHiWo39784$2#k zI%;o3tL=6+nrM1Hz7YZLu~}YlX*9lSXJ(5@M~btJ^xs5Y&3T0Wc)|ojzq3}d)|L}Z z5wthX8td5tqi4Bgx)w$i>*8?rUbY#5rBpNkZ{a@V)9F?y)VePDQ2cBtToEkWcRGsc zQ;O|>f<eATCHYO;Ov2L6{vV+g{KK*XgHof6@)<dsB*X8Z;V0;BB5nsQcq1KUK%Q_w z=0lW;#nagiQ`_~ZmB06Xo}+lnS!V%-eZ6;+E(P{{)ucM(fvij2a~%SmSA5rM{GmVA z@;!dHPcK!r>|V27*n2x%i3;<dcqAkX6ht4bg7OCE6Ha=}$;L9Uk!XkAQ4qvW+gx`x zqGoJw=cD=VGhL6eX#~*iduV*N`)@KESFuPIPbZ`gk}DP(*E;m@#P3j+TXRT`(nPTE ztOROzAi($D%&|f!4S%>OtN&cVuH9?&s>|u|J60lwaf9y-p+?2hU8c~1*yRvo{lseb zy%IEk<kMY~3JIm>nWBcS|4IynyY2krEI1BV^tU`aT7Pa-2ogCC!>$EI6D&!BMBY0O z9C#WiOP$?(7f4uGaSy!}@I76m7*1(3T23pQ#aL({nTvJnB*$?H?2dpdBAmr3@kfU9 z_07$8GlcGTOD!Hp)7iSdHx|PUxIOd9{@4A<eqQSanY=dvH2P6(Hl^N%Ov_?>(hLGb z-=w-_UYALhvI>+7$)fd8zFUG1{OOnfT=m>gbl1yJsJZVuW;hL{%8BRh*wz6}qf2)S zlS2Jg@Jju5jO?Jtt)$Ab<6=f!R>R??l}+J_i}*7Ku>|5)v>J7d^c(m;Fl#@yU(=;% zw{+D-rQ^n-2|DUw;ivNHK00|kcC8RR+FyAZS9|if#*zJ`<-J^+UOrdp`!Kz<;4U6P zm=BoA(+*0xCPy4VAjf+7YXIfIIKon+9oo_7HuNCOq3<_hL{fg&K11BL_m!+d*!C_x zHY4x=w35*RM+gs<9D90P#P&GEv3=@nq-xnDabh>2g_mCA&hXf&oiU{UD20Q6pHlb_ z`}$c~94TEl>9al~uH3GcYn7acpXa47yRPes<Q_=hD|Q82Je|xWX9(TImP`YruXFX3 zR#QUcZW(t<Qb00bj7Trm1yT@O%=5YD0-@xqYByp|9j-T29&>E=VkUbh{%5}pgsmU_ z#f^vkg?)zYDS4g>476d%7W&?!Aq(tHMi)mnCyTeUBVE*u9++?O<IV70L_q4+o$7-) z>-I#@^zPs|kygKfau$u{n{F1s?Y#=g<A{>}bspT0JikzRyh@jE<#rv4cU?I3Tg$=q zw(XjR=G)1Cmi=l{yeNn%i`_-p8SrOu*!w3h4(5#Xa2sZwWcZgbweNY-J$0(*1@cO& zbI*={^SP)&M!1f}irZkR|6PCH8Ofr@bso;lO0Dm}aF65LcTFyREVmRb-9h6-1N;!X zsMt}omNgfzCve<~!w6Uf;CFi2a3Jp2u`iHWb*T}Tav!y^BXaO<ZiHF@s|}$NmT5xL zdK+zaukUi9v18w!i7Bvr&k0bT<%3D<nIh*<)*_IXai?e&4}9U&`^c3fIL%(~J1}Ej z{s6s6YP<j0P$6czpH0noLGww+<FdPCM8^pPGegl>-TKAh{hWXk1<)`l5#sXR;s2p+ ztqk=rUXIe-q3-)ejKli!fH3u<>vZQ8&ExgB0FBTE2%cDuIt#pGu%Px^`;9rX)O5ET ze%A0(WEw%1zQLu~8`+87m-Z7Zjh14ea5*7k`lsUsIIgVTyGMh>7TN<;Z@s>>ark(h zjF*0~=m^Z_!9|}Jwa`Xj_4ixMFc7mnwx<+*nkr(u1<_R8%?CA=FVrKWLvtBcPZ9MY zQ}qY!rj^m2Zv8){NxD8j#X3nJ)q{9oEekZ=zQgr-k7GQ^>#r7ee4)l7=UDz+LhE^I zgcU0;H+4(Z4BvnnuZX{*$xkkP6&#E?@QuNa6|!ASP+8PmX+))~sc8KEHzdgZQAS=J zaV0i7>0{>^8i}6cu$I?O-9^Qm$2buPo-CW8;eQx{S0HxIoW;&swu%&5?e>2U+0e{J z$k)?%=u?N(&=ik7N4FrF5O<d|e_z7_a~6qpa|g_|I4soxlb4;lHu;_$ru&mKcwK;q z?0sSf-*Rn$f<3q!q7_OUcv%n43_ES-lO-!R;KUvUJ@4al?}|nfc=&o4RcHPyB01XE zDcbTs3wI{Yy4T5T@U<hCTX$HvpVyF#Pr}KA1*_kHGLHcdVBa8HghpU){mt<vg}{dW z>v}!9dOJ;zc3K!0p0=p^UJn~Mhrx|QwTAhRi}7!PVF~TYT|@S1g8zwN!x5Yb1B@Km zm2sRhWWXtA>Sc&TrtrKT^7(~9GCv%_#{d5x!KP<Ol)BX|v(|CD^Svi4!}}nZ=~Ml! zy^_}H0Zq&14BT$F_l0p&hCG_gr%Bt?t-fQ7b$-23tHt@D!X%%|EO@;iLdY;4s{wdc z1ZS!kpZf|^r#IhqIBG0EmY?X}z)VOU&!1mpdG9XnM5kXIN}btU108ax1a@{NIatG0 zoE_?Yk8_><)+!s?oF-u_LVjyat<G@E=ZCs@z&-w^pK7n9^b6@&fXPCx5(2SK&VaG% zcmYdaFs&Y9Z{|yzlNBM~scrZqxprib^3eIf?*G`s?z^?a<bO&b?^k1qT)wqlmh5(n z5z`K(D?`gGVF>zvNT{7e4Wv&J{MD-^ji7(#dAq<b&TVq_u#)E76S$-Ql*}}oSmOC~ zNuv=X`~;z4pLe)wwDYJ&jIEZ;XQdFOCH>ITUt`hsDru|GR8}y0Z8hcx({XUic_I3# zN4YOY>+R8s=3^cqiKshFmr^U+wfD4wj?>4^Z6wg`dL#y1A*4ity<{2{BqWIN)M@0b z?=qg}?&sa@N3s&#MB-4b=expCFRbxmOYJ&J0~&pl$@gXZ@Po$N?#5_a3^WKQp>Acj zV%-Fw@hO_jM%?$>)dOSUphw9c{t1XduNZrD8}Amo-Pc~}ES3a-%QDRSMt`w4s=n-v z0XlX`4ANo$N#tbzQ+4uaBHGyCXP;P|ER8tISM?fUStGFB#$;bN0``$dn0R*h1TKKT zoeF~jwC1U<w2KAY-NGXy4@}@IwB@XtO`_{mj1XWwJM+&F8quykBP1z(1pgorKl$a^ z=CFIXgS})$3mV3yGV{l6!_DHmls~0IP+ANAPJVY)8f+m>!L+cZ^i9-au@rtvxfh7b zdaCANd%U^O$Oah`4iApT(|CRRx|H+&S{3g}sT9reFOH+>_dJ%?J;9?5ulsS~y0v`& z6H&Xh6JP7~3cuYvl4IdDj!!AxhXp0cr3;bazPXwr2)#OZZ^-ex;ck0I4Fj4nuGv7Z zHPa_Yla^z<u6LFOu3Uc;e()bnWBVyVFSvF}@~BK*L;_b?VFeU%*$x+NzRsAR_&nAp z46=Cbvrj6Z=Lri;`u;15H)72fRk`RjXh)m&suq=1(UJ)2NFQ`%$+-r4!BmK=(_foF zqFz^-c$vV%Wd^1S-={ei1NXal18|_5$Z!EL%k`88kf|s#v92k6iFE|VZsYaC1yuVU zC0E?;&@4GjCe-e*gB9!W`jQnbnJ&o=gX$1pPQiy6UfkJAm+B8z^6lrrI?ws-XuMWA zTXPUKEYCFSo?I6*#D!euo0Kv@Pk0p%aeFqKuXNxa2Vx4g_E*7eCedU2j`K~KuHVOb zo(K?%;NE$+x*Bshck@rj`lmEJ<nPGPfZ|-Neef4QZs6p<%8!c{M+yFSk2+A?xDj@O zB^k*3AE5;j({a4}#Q%|GRfR(CS%4b}1!CM#ls0?c3s{tSk0fuy^<P52N93I~$wGTk z^J&NArDo#?sT(_gY`$OE<iJHaF4D_5TS<6OpJamc8kN<*(O3maN|H4Z>TTe^Qy<#) zb-@ge`fU`hFFtmK)Xw<Y0MN&-=@W~FO}KveNR9W0%{Rw%Kt~>Mw!d?CviVZOdihbk z4an9wBL8%N94Lt||45BrqPY$TiAu)N%X<PNGd+wywXDvah@Ze~Jxe#}10x1bH@*wu zt+K(1hl%cQXh(eSU0Ob0eX#6veeXa^!fNN+_B{~L;0RAU;LS!Bk-$NB@QU02>^S~W z&^)(ktif_hJ1@qmUzWmIR&s3}7LmVTj@kw$!2V2ye+<lsWB`gThT!KgheNV*+V`+m za`FTzb**>3K2g$vrommfqm<OA^V)8xE^JQ2PP5)T%703^m%=re)^1lY7=gc>fjD3K z<ZC+l-D4@b+gdTuK+j>Oh`nKJX{F)%=94x|JT~P?*Job-IHmjGbZa_W*mRi3??@<9 z?sIGt7`iGXE^sM-EI|qZ_pFM}*xu+iWL`Aq!{2a7$>X}t%=*VVgVZL(o8$E`^u;>s z3frhD!+~l^vJaJ7nDa9^*z$UWrmk|83V50bvBl6wRFP29IAA*uzQYf;R&`|e+4ULm z>?+cc`?nTndh#bemK$}l!7}W%c}WwF9r3k}?_m%~(q+z+-Cy3i!f<@g8?u>q<UoV~ z$N$(%pnmwPKBh1%W9a=6aPYt7iEy<Y9<WVFeezTpM_>4Z4{i9n@S*Xjg5JGbe9h~S z50sW?zsjvj_-TBW`^AObt}mV*#trU!yE%kb<$S=C4H|a!MjLLFUZH_82!WB`1oqK7 zF6DR&U*yYBASWp<`8}~`Fo`?QmD#^FnX8}?^@l#r<|+mDl~p20?Pv-WdRb_@F2-b7 zZQ<%<*oL1}b_YM-7CBhWH5j21ATd~}eCh`2dMx=;gtaLP)FW#_FrGTT8q24K`CYNV z;g5WnT>cvEq}HdcC$kSI)OL=YTLMo*eHUjG8yaFRbjPKSJrg9pz7TC6HwA#C5lO(| zhl6-Zi5^t>X^5=Yx?mODO$__H+{AxH@UiUtRRw|gt3YY`kw8=nST72?|C4F<dUY9f zqbsQ5L%Wn5hi0Sladwvp&awmaGFQjKzjB>rVp_NATukKW<zslb7-g3}PR;J}{419& z1x7RnmEGw~CDw&Xs_dz^Uj6g_LOT!vSkIS5`+yS=RqKr_G!U)+EPw{ldp|6&NbI?} zI71;oe%3kEZG@?Nb2&Iy>04jRGw<>V+>gCgw?g5A5RJHpzC9^{IchO0oGjBOQ~0t4 z@W=iUuH#8!sgLK}Rf(;r-yIjbiW0kkeR@MBvbxSBe=K%<p6>YLC_6ekY=sG`SPfso zg?;DAbqBhM{Co^KEorLQq8aH#VTeajCszSbK0A2ZJzt%MF=dB1sKrbH+eMk?F9x0q z8It#H7y)M@MkEE{tv4&h+LL@F+=!Ts`u`|M2PTe+6;M5=&UPq-M-@-s``&UF$7MDg zZlNoBEz7b)LIp*BzIdwl>Ap%o{m7NDhKFBspkGb*%m2Nytk2?X*1J5LfGXw&XoUY< zk41+=a96)K_2YJDlbFYGjK53&lz3eD5U!~E!CnP2GWFY0+)S%*|D%rm)yM_Rtmn2K z#~mslnjHD>BCaCascukYODT{53<TbJJjRTA9BB|(_cW*nl8g|71;h-SjrC#9y48{` ziiNDFaD>IIvG&&scdY)?1q647(guH~)_c;p@@@eDY(AXBYwVHm$rN@eHPU9(kCSdc z$Lr#ZirYRLWs8nY<!`+Fc3iIMo6c{z^Ybrj_1ua_8^zu)A_{L-z|jBA_%jK3$=|YA zhAQ&EB-;?)E@RaJYAGGdi77CV&?3+!+2>H(X(dM~Sd|MGd?`0fb%Wp2Ev;{)fO1%> zqV=YkRG1|OK8iA&?^HfckXnfQAX#=C={Ou577e!&8&F*pGMG2t_LBV(;o(bmM#*t! z8SsF5HdKlPm#Hpu5FwD5q1A$Wmm7_WU>c>oGGxjX?>Mc={9uM;IaId0b*E{j1(s0a zzmbAELUxA~nri@F$%PNXjnZzr#Xu!0vn}PCc+@1kDA9}C8mX1GoQ0bgECf@ixoZaA z)X%Rk!iT$koBcidjeS1g;B|}oF5m5e09yB}>>0>c`d=GVgpCS$M}O3?@kf0H%xHop z@DPSe>cz=sj?P}{z=m%R8)o3k1=iy!@W*L#&P}%C)aXeDd>l^`FNfuS!EkMQUqi_9 z8yE<to$^iHWilVHvyuNZjKg_(aGrMkf8=S$`T2C_EOETDV5n?)3A$21glehpbN>sl zlEN1)Ppez<B*ozRDrt|dsF`^$_~=hHNB3{7=CESY3u^SD3g{AbwGC`oFNCo*6)o4_ zZnD^eQ#V8J^<#U;VP_pGk0Ub`kRdd|AFGQ&a!X%{KAbW*N$oP1GVoK+Zg986vju*x zWcGmMH^N^gCeJan<U~I_|B!=<-+kA3PX%3DXaYCe@GAA1%z*Iv^1JKq>!uxV;>(+_ zqA^o*u1W>(v!2t?_f!!Ze7w$(a;&BjTaQhTIkAKuZ4kFy9Sp=}RJ(QlzPE2Z`CitP zHT%%T(;BL%Bs^Om{izL&VZGY5H|@O%Hv;XjvW*5*c<eBpMsPnqec5YpFxhXrf5-oj zK%^dO(CWsQWfvtIxHfE*F?E)%=(llpgEx!hw7(Bs?ldk`{v(<T-2GEP+erKiAJ{10 zL9rw$VW;*zBphu!yYhfeaX6rY#Jmjvs{v8**t7xFeOw{)a4i*deoR&Gs{VMJ65|vv z={+bUs>xK9G%cq?`}{~Q+HUT)t1R7JzuD<xOl8{ll&NGHR%-_D)BQGh>$9AtgS$D< z7G{wB)+cs1m3YS0(br=)-OaI{>dty8_kyi8grsMFCcEm=|7jNjW~vZ%zkRjH8h9_U zS&ZQoS8dbm6d#SxNrb?R5#;p|4Lv7I%g(kZUuiuTUH*91ZS34Tw#D`eeOMDVoo~?! z%jr82@nb(W>G)xLw&V<VHDajx7P|DS5iNQ05u-pdOz{48<Jdn%L?@}7lZjY;5qMw% zCGpgC>E3a7NZuP42I)BrL6ja&wROLE?;&bDJ{DE9W1_@abuyD!=;>+_@>@mg!I=yr zONhM0D3S*awY0M@aOL5CLxWY1iTt#^_{qHDy@AsJO}5+6MVy}C<yEbeY)`JYWjG*z z$@wBcH!d+9MdnT+3V1ncho=PJ9)>|m4R=%UA@T0gM$BbdaYd)vag1u=c(M9E7Y<f9 zFI<e9hZ659-<w<lN$U%$pFgyo3ZO-=p)(`Ea4h>8?N-uSj-^%_x8Z?#zYZ}%{hF2c zK27Y8%Pw2}icn)S7+Zup?g15M@b=1qX5ES<`0xImTN+L@Ekvcb3!eNz1#7cMy!g+R zRcTuX_v)_i$O06s+Q6%H;7p&2%%6_cfVjY3cB|9Wcao*cjgpgAucc{Wn#IrofYZmA zBe<0xyzojdg7MacqP5^)L1W3c-3-)vvB0A5w?E~fN+K2>O1N>F``n^8w?frppBSm( zJA%AVmOLPb>d|l}ivs&43)Ax2;b))vS>g|-Bcf<&>b|iG?z!j1Nx371eYe-e8jInl z2R(!SbA3@yFr5K$$F+T)ZTl3@1!55*M2gKMbOAN8LgMigkNk3WGzb|{kG)UQ>9%5= zLff9-qDSQko_h0!rtVXAWRXSl2}=~jz=MH(v%GP~2!_TW^pUhUfN<3T0qv530GS@o zG764BLPpLv>T|~GSaxDWYy1@#wqeOZ-H097;v<EbB8So)69E%ViaC8^l>XF4d?*dN zIc`fFg(sX#%e3_1=f<+le*urH@m5KMIE=S*=u(2i56&X88bD$m=ZT>8z`S~q`ygNT zp497>BDS}#=XPILE=KcBWh!`)1jRq8Pi8LD62Zy7446SqKJ&n0xMkscpQN%=>c0X% zq5JI{QyPbthvF`Yvb33t+AN+1);%b&pli7L_}*3i4!ZWacbQWuGVr|X;8_P3X?c0P z;qxiYy!xXij+{}fHf&h5L4Qj7-vl4TrPjHP@1q0icZ_s{Ac@Wmh6{p(xIlU0m*aqs zwjq(@1^%EM(l|kg?>q^6)6Tc-<<l&VA+QNAoH!E!JzPT+Z#=%gnAxcK1aBw%x2$Kf zS;{6qF;M$3i3R_N8H0y)_VBOc2^s<;aT7#H-~9!1#&h|<r8B($;yEV_)lKjIMHsW3 znm<6TT7e;RzjC^(mH6;~eBKGD+M>koafaX3XCi=lsm0f}+r(YhDBuOCKT_vC7f}4N ztN=<oJ2-W-F;2!_vDT;Y5`}_G;;=_aNK2mdg-J0(wJE+VEi7Lo%QgTnD3kXBSLB#z z#q6|`koaN$`^Dyl#>nzT2BOak8>sd{R*&`BM^pr-P&dKzNhwg)Gx`FSY^_p?f<v-R zxnr`kqL8dHcIvQCt6(K*($+q=#SFNp`v5<M-UMxsv^<I@wcen!#HG|+$0w%SH{Td# zAeg=C;5AK&=`b9oG{oQGmNBLXmTT#13O;~I^~{$wojn+nurGMPwglQp45S4BZys^4 zhBtP6VBQ0cBmBp0-J+boLK*?+0N5B`460txI{+CRr(JXPAcNLuR`vGZ@-f$XaPsrN z#A7-}-~gjUIL7`T05E!uRnv&GJ_)ze&Qe``u3>OFlgep%lnA)8bmQdXfxcoL$zpE7 zw&-vab_6Z5qx34EdWtg0*@`<+*Qk~bSb;x(vd^eT)m;atg;~07t~ZfQ?yEL0a8o^m zTi34z;<Qe&HnxnkykkS13VrXqI}XpjWV5-}6`ed+Q7TQGI8<5rTv%HZz9t17Mehmo z!&!Oo?#(cu6u<G59rhp4NTzYvD_d#t+L&?|tGN!;gF0kCEQDi!Q>ora{J^QEBZG$U zU_wE`!uYx^>L-<~y6#htM%>y865QyVX};Rkst~apFTqO<TF}E!^>`?x;6JW+zt5k2 zj(d0w%Dytp+)Xp4{0o$$$zG-Wub`atzX_CMs=g#ZFxdQeQr2XI(%+%@H}-oXRKVt{ zFU}I(?Vr{<ak60@=kA$;CvjrJd$r-8yn7s-Pe(dP*qSWP=F1L1he@)oxh&9&2IaS0 zACM84*-B@+G57(`Xwo}P5dE5A`f$76AXDgaa&G0VTL(uhO=I2dp(F70U=IuEWDI+n zyT^49j%2Vg;S^kR?J~-Az?~(<o`p0G8_funw#)`}1lV!+2hzUG!^glE+M!Kw_V7|Z zW3~|8%5Z*1d-!z!$fF|aI^r^NnVIUH{jIij<$ilgRU`B55|48cKuHP2nvAc*E9oU` z)my@h!-tuaSl)o|@&qq#1cX=jnNz)zp}|g)FprX>T4=pq(h%@jhhVNWy3aR^44q8W zpxNZnVNS{bV}(I<1I8od^6KsHJGiIy_=y>Lo!I+6hGxt6YB;`OpKES&8{=;l3zMuf zI1)kQnOnE*qSYBRUGTp{8;lI_)03Roz?BpuKmPnIYZUOk#OU|qmpWA~-NiC;iP10W z+lfRdL3fc~+)EfYL(KpR91JvWBfIfSr3f^L7V7jCc-ndcx?`zsu2z(YmJQnDDh2@4 zUh08n=|oOdb-<{cp&suP)JW9lMF)dNyQnn6WIetJ^vmYE0xQz#>MFMZw5?HMTbY$Z zbK=(HLOPHAPHB4iXLLF)Zn-N&$e7#qv%)^d+spaMSvT#fA{P(F>EDZ7$uI83epFD@ zJIrP%Qn<I#Gly*At?MMIk?Y7;2;!IKb?Mb6f0^5o`=}ps)*i)wLepZopci8^?Csa_ z5v1sM!-vyjconkR(E>v)&dHqUADs?S;A1S>%B?II{1L4eteGYk;i$l2Lh#udLh-GA z_0>RPBnnmCFY@VGJDm%Ay#u9Y$Ic4sZ0C!5qK6$@8RIs9#%N1K?B_n}F@Pnnk4Q|0 zX4epV%n4wEa_$TK@=@8(ydKs8nCn@Y&DYgM=0;X=Kjw{JcYeT@uD|R{RZ`H@Yx7vB zF1zQ7Aj1Vc_OA1&oZ-dLW)PY*NSB9EJrvj$SJ&6;@CD=)ln%16V4z$P6&$O}_hJn! zrs&Iv9cGO&HfbS>Aq|(lAEvHpOlrvEIocl=L>hIf{aMty*!xX1$usJW(XZ0QoW%A< z%im{UFX-za+0y{;NQ7UGbRMS#y`gdwoTX+aM@cySxoor1t#d=HK1+&HA4;PNy2=;N z_zD8iFxk$h=B0I-@U$-%nqFls`b`#bP=ux8__@#gY=W>-@!4acP4|}qJ7x!=ACMUt z0!tmVwbgqMh68DB3O;k(8a5;9KlNOyiyo+FUJW)+9NR)VtL;y|#6=)=9?uyf1v-c! zdi}sEW?*4_zQv5OsaEmJfLC*3x*0}<&L#M>bYIw*-)@`rf(~T>x_(uaW+H6-YN7GQ z-KKNj#Dk)I;*>FX$4*i^%Dv}jBGmGni#vw&(WHeDn98C9iD)`CZVBo|>?Yn5m^FTl z1fekOugB#We5lYN7xO#&M#w?tWWB@X?`JtLb;ZKfO&NwWkef_y<u|YzE<yYl5x7XN z0gnAAei$#Or4#8be+Q(C5vaNZ8KLTwn%rM@@nzl*->=176}eHyn7tUKyH1w15cv!u zhzeRur1a1_epX@{Ee%yloC|F}wjD`)_bg=C6gxAs*?PvAhhJaVTBr~Sx;aGneQ2Lm zv-c+G)4XQ&8y;-SPb^JF29|@Y3MwW-c8jQxFXSGMpANX9SQ|K!gVV56zf<u(BT-dp z%;74{6Mf~tIpObH-vhP3Om(|$Pm(Pv?a9E40--FW3!;yLi+eP*iDYBaXPoip1`|bN zwNW?{={UbqtG}k(=r!)If3DdA&>T29$%;-C6Z4!Kwb$jjE^T63;r)p^^4z?9F2<P% zlXKW^$cpG&eoYqZ8o}ApKlxSsLPV)mW~@ePnURXn6A6|<Kb-h6t@}7x%2PoV-pZli z9+!$!?&HP3j0^sy?Gj5TB@xaf{KhGdc8wqkCbxyzVb5G(Y_IlAPStv7SmSPJZqae} z3wEaH86@JQq3(<=b3bk`Ai^Rz@kJ0Lk(?D%yp;ALF%LnP8<;nqFWx%s7H4sMR8J0> zqfcZbCDL5*gOw$7T8bFbW_@wxi(v<h>N!idYVFeKFjR7}c@v~pGWEL!p_=(dka^MS zB*ZjT9fF>>XFohX6R@eJ?#36Ya$r~^Yemqhbt8vT-NS8^SFB61Oc-s)qR)Q}gdT9? zJuTy0dGvn<S+bZTtGvDnZLf?paFu+=)Y<q?UYtH7WAqccJ0IW)y=erVz9hMDF+v>| z7afI_9oTTmoZ0C0JYJW7oWao)wA_}#E|qqAor*AuB6&KIXY)Sh7*r#NDk6^|*cr_d z%8QLTlGL&`I+t{$DT5dl<a?22MRUmixa%OzsYSxtYrb{z^yL%9)kIn&%|z>QS?0b{ zt8f2Fc9yrvP(PwGNI?fkk^g+OA;x@DeJcM%=(W$J?%dn1M?E(Ud1Y=EOU^+AduHS2 z4k?_Hc@bfBme|Z?v%w;Enn<#pc~Jp$o;l-hZ2IhA9*m6rB#uR*W<C_UjIW2p4diZ@ z$<+n@>+66igrKBq?ut@c{7H?3MjbXf4pwIqFS6T8Z<*vnb5tmXAdo=FdvsrWJ94<D zM1BK9Vqhvh>&}Tm>RNM~?RQ9SwNtP<(m`G^1n*!iAA`CB`Is3g2NJs4kxWnWMiE?@ z6jQ6i|2n+Rkmt@^0C?&ufYVWi1=#IXJ!Q(YcL>3mHc6{w-QB)ZJHgCuG@iYGT%;Ls z@ORT8EJW`9Hczw=<e6>yo89AM)&_kOxlv-F(nhJ5qstN@->+mRWF)umJG27crssF! zYtohaDq@o@%%pFlTD7#c_z$z#t~wsG`>gU?*K>rm8;i$idoQ?piI--wTU^iMXtG>Y zhWgVS_PBT%$O&=S?MlM+<G!<;Dm$Cah~>XJ%aLO4q1xdIQ=rA{nT^o0al=b#MN}eg zo%DN2&Fix|g3S49tr@*YjK^x|yzJibH$$FazZyaa?`iZR%$&V`zDc@VCWa0wKljDV z(Gg^wQ(0+brk^VHtKzjQNc)|><~EuT2aemwfzEU*0!@dhI(>4?lpNZh9aeq)VgxI_ zgi&;AQzq=NYEVZNQ_5@$tqf&aS|x^N8Z7%<BnW*`1w|qTt0S00nq4(5%!rKwZf}0Z zwd*jy90HiH9RSFYW~5E~5KW^1EQ988xj>~bDGUDzZzOfgcdt%2bjUe8owi$QS*&&H zj2f9rY~-C05S(;l6QxJ^BdQS^D@&h~C0yc3F`UxRCQLQm56>^!U7oLq@{wR3Ej012 zN>{E{d2<fZ?x#<MKjJ?sZPzH%)YkOX=P$&0>m=Xd-K3o6hnws3d2pFmchfE~>RfNE zwYuV-(pAQ0z9_I`+c<0jyvNB6*B@4ZJ!;pPiJO-miFu4Q;3+(e96;7eA$g5E%PY~T zw)cuFoT3?B*y5hwCbV1odvJ=RJyG!zbT+L8Gujf^)QCykOVIFZ5)wVlPy=&X^Cp+9 zF+Gh^DJBMb!<Tp(O$AumW!qB|d*_1HXSg+73ib5JWf8Q?lP)KdDafjOxb2l0u@e(1 zY6vt*6#4u#@o1z^F{mAso+I$Fqu{l^X!vYJ9#(5xx>PMGRD+s6CeV}`TG7NFq=t0} zzY3_u?wb)F8C7Nejve6Q@Nl9j_F;DgEz`m8>BmAGY(rEfE2EM;Q!k|#%NMx&D0h%Q zvCEoZwod-jiZscrHv9_<<1KdN^s$fGdcWRMPbu{Hfk)i;qHCjRl4hprc=x*Y!z^d) z3rxGp03-__{Zsa!m7;JjH;&PLPVVw#?y;TTzL<mW1ev=c{1KB_0IcVlY4ma|b$A6o zh1DD@ipf}2Q#UmSAryM%s*a!>1F1j`HWkmK&broGcCNff8Xm;XXFA%NeMP2SAV2$| zlHtx20sCKnul*p-t6T;zvN`cl&KUG3y@`ryQbWej?(c7|4wCq6pu7*L4QZ|%jM+2W z1Rs;>Ri_iJGOP~DZ5~RN*rc+&ohWCIVL)KhkoVdjh{2vSNd^5P2<z|1(Z(!|DkK*# z4UePuK*6go!U!NI2|_oB)cwVa1J>rB2F&G9&Ib+crY{Y4&ghXyMya;9Q22jPac`ki zI1#d(hUhaS!?wQftsE)ebn`Q`PPE|8u@blV`t>(999$bZc{i)#8l1(KNm#K;n@h^8 zRW_>{s2k3{*J{Wmc|MZBb#R!HbaWGK5KMC0cGIqKDL|i#W5I(#Q=RVq1l+7S(<l+# z<16N{=sxGxz!-?&(y^u=lJl#Q$No7<ie<|SM2y77dG=yf-ZiAsmI`oaxy%}6n6H<5 zLFeJFa96bu3qB`g(w+J0-t!1O^eH8e{$yV|R>`ajojqFa8tWKS5Rpl$K9p4A!Kd^u z&cfp)CX?r+_4WN_v_+&e(wWzo(y<X;Zo(Ce?$;x#*+yU^u)4+l64Yyci+$50xACp; zCg;!0ZuHNZ*=>>?e`2^>@0o=L;9)zU8&qAHDfC?(EdOX7HSbz@yg|hl?D{chbH_%? z<Bvt$F75aFW0YY3k-iUEwpj$D&U92pp*tH}%)x8&vKRGh$Q;oE?ZS#2gNrW8E1^X< zS$9dFT<zcWuuhM3zR_NLr!6`BiiM>9fFwnMTkaxAPrDi6+L*S4&D6k|uH)QQQ<k+< zUK3U2tkSxW`Da=q&d$9xVd*eFul7P({z9y9q27_>QBS4^7-}HKzf;$!HZgMyiKEJ( zwsaGF=H<5cJb_^gFhyHI`?);1#KPDxGK5HcopWo3a6=>esa=(o{Cs;w{Hfge-oIh# zb?Lc?gG}XRO4f({bWH{UFXEVi&n@t=Js*M(`?mwsXU*A4?utRWf8RrcGm_DocCOfL z3<BZ|xmmssK&%;q|D0!8&Te-?w;)DB_A#?0PWtX~EwtX)BGUblcB)!>Sp=PC18b`0 zVp^uIsvYM9Ed`T$S3x;)M63ojB<6?5$rGGvBkv#*hwI4`^;-H?=EO9I>xHHM^=1PK zS)_08Ehnr8m7fL{m<*+r2RCmG1O(vo<DnSeKo8q|*u7Z1RNxnb=e*_JvTn66f^29D z%Cq>iD2>3SKfl0tO_GjpL)WChb-a;5;jQa!Jl&ZWrj=_n1i<`x6qcYqgKwF{!&Z!A zioL2rh>-Z&H2V0~1b$$=*(YJfC|r5Qi~*sq1dI!e9f&pY&a0J>-gP1bYpGC{eVL=_ z!fmR|12N};pEM@lj&izhlr*DedN_Zu$uQJY)p;pw<rLXXVF%4tzEb|gW75*=6E6Wl zw_^Kbt8ft5?QBmue$IqleWV^`_P%u{A2-<6o_szil;U&~DcFu690ys_;}rogp))Ws zuj(>FE+UBD&0-E|Esr-9xoYtGw}qL$mL%=J@4Jx8ggBZ}ryB|u^Ssr&H}y6Z((o5) z-DjR>s>squF8aNQh&Q=v%Nu&)bo6Y1bj#lN{9UoPevg#BJtJwEVtb=-FAlx^=<k4^ zsB8<gkGr~Q#PrH15VO}H-sd5S<=$HOGt!9PPx@$hq0|z`RZmlGCTPJn_d`$Wpm`I& zhr_C;3z+c8-u=Qd;YpcRtmmF1OSmX&U|%azZy;|7O9JCz<1aYatp^E(OZENv>(AZn z)Z`4gElpNK_SqTU??4a%_s$G7Nn_%nwr4^Lvx6#rUTGs~ySLuo7|BJ;q79k#+Wo^^ zyWnQkK84Pu<ZrUAnkxQ|Mr_SYI@^yJ9V>z^zHMdlM|<vkAc{uQ(Pp;vo7PAgIYE3u z2dCjv$pjM%DoIWAv3iSlGBH?y=Tc5lQ)dv4x#in#PYZn&=g!-Q<-2<lv1?FA+b-YZ z7(^|_Pb&;ZhXlzG=&y^rZnIambO`NcWz;P}vr}^@nGQ865Gvd&tDhy3w~g{8KxTXc z)=W!(*~OChy{q*7P?~x?vmeq3tE6N1!$C0d2b+@S7?ZV4*PB_a>YjF$pQW=hiTIiZ zM00;WjA06SDz!@d_x8-L8ZETHzMu}w%!iUS-EZ+KOaZ@f;Wt6IcmYWoX=ImoW&<lA z;cFV*N~4o+bLpoLfobe+;Z+M4vS8&<i%=;G155pI=?=~h1QGPdkB1l%A#PwTAJgO^ zU1Vn0tJ3cdqm@;?x*XrfcIu4p1LqlqIS&pKgNdJJz(BDT=W`w5aGTdCJQTh3S{<8c z%JfW93XDaGh`&U3c@q@-i>%mC#!3q6!~1eyq_TB*NpPO>Rg(B<;POE-<)jHGWRsB8 zY(?x?eWzSzw#^Hx5~)udS~&)(0-48J{cdW2+370`q^3<)*LN*~{+soy9%mi#;P5g^ z%Oy3OgIm)N3EfL<#-anyh7%m(H#W7JE+q5@__3?)o?weBmswx6p100h@h($=yXtOc z%DRxTNNF1qS&)u1NLdgCl4Rb0PlG4m2E>$$A&0}&sGZVQ+~~%sM`|PgX@h5`5zpB= z7Em92-*KhpFryzu3zeUWA9~*he`$PP^hzw2>?Q*@UsuYe={97Bojz9!*#0$6mH(H= zDY6Y9$>sDAiGZ<7KHGf#O(^rh<N2<O!sP8R@Sgfcd?b6>j5VqLNF3gAYN;^BK@Div zIG&$fH|aoobLUXpuS)Gqg3PRD(q>3zl6-&~wrdT}GtAeiGvKkl-E&v;U2#>^#8ue- z`OI7g$9bO9HQGu1WeDJ$OkQp-{^IRexK6G6g#0iYp&Y}vkPQCVXnd}lZxNQpcNpI` z(kQ18O<S1mV~8-n)&#HNyZD-LFIbj6tGPbYqY!ffhXAuupFA$^_H1qp^;s!NM5Aa| zf31Og=HFK9CP;sBR+?aWg?;&g!1znZCNUK-j<*~(MMe0j*|hh}-{W~nobN^B{IKti z0NY$EU<WbvQ2q_0_#HxbWpjy_#wn*ulr9eP-M^h45G3WbQZ>}^G4@&HmdcX8kt8*6 zwM6^Oiss9^0g-%AGPEKxL@iy}oTV-1rwSZVa<;5LD)}~ir}JjLAXe-AtkodN66iXn zx)FYLs*Do1ig@z^@$<&J(^YwR{jbnSa{w~WMv)+;Q%U9<m1@PhSbc>jXZl<7h3)t` z75oqH9Gx>*5?d25rwG?D@f!#3{kAY)(P%Or3}RzTzc%7Uir04y2~eUZ#8DlTSS0+e zR{;59oQJ5^oXKmy(dq{j*x-XXXQe;Ih)sxj_!6o=<NV?B@y2$xnA9~~1}v*+EvrRu z^Pb<6Vaw^a#fE|jUc5vA&Fbhcw6G~n2x~R3lzxG8is2M%OdVp810<eTjutUw@ExxC zFT2@Z(jN}GJ@f7^WlugMAD<ph2U*Z-bHJ2K+XwD#L32zF&6l%%9}^Ey9F#D}eg;CX zUWYc#8V+=EE~6n^a`n2kN8^C74WgyVTK!E#^(n#K=RMx(-q`vfskZR5VsV9Q>SmcL zxfCNr?V6Nn9%!Fe>Iy>PpQius(yxup39#H}{d1yNcNy>fD;^%6=yLVKF}0JA>P;rc zX%=Ts`{ct~FXDW~edjnAE4bL0!iLwyVjWI#D8b?_vPEO7a*Xp(oS(d7V``CAPRLR& z-*bDg52S{cDsT4oD29CjkI7z&kVwP$dbEMpy<I>|E2FiHB%ZfJFQv>1Gt?J1XkTG5 z7NoC0oaCR8OoaPLFyyvkv)6nyk@#SO@iMLmXX50w`e&OyxHe$RwaQ;<rE{@%tZ1>! zbJRFCK%RV}*?MG_5%>XVYqGQMaBeFLIU=nF*jpjXoNtt@#CC?-H?@FA3$(jm%usn7 z>$l?hz}0&#e<WjAI=r!=VN%VJ;mu9<p-QfR3xx~RJEaLTYEP?%pHOw&CjTZ60oPKu zX-0Ya`EUh8I9Qzb7}d-BO?%zD8>Hvm-833<5By|OJgRC41CXh&mG9R<Cy6sjfO%n$ zZ8W%m_LOV2(w6&(r4a+&uH6`JsJIQIi#j#GnN(}4JM?vZD*50SELH4phHS=*-yRLI zGVhl|F$L~A1G--1|Clz~J@gkBIluya@;c$eNfAxdAIDq9;`x}&CmkTH7-*Ig%OyG9 zjL(z8BJudH0PD3@`E1G;N2YHCf-G9G#*AUkLZIPhbY(@!u=?)bl)L#!;rKABm4aib z8Tc$U315U3d<UJdt22Rq9B*yEqxUOAayK{mk49;bT3p@w*z1jgn#^==Oun(1qgvf8 zF48Pbp4OI|DD83HF@gB??lu(^d<2Z<a20M*z01BssgZ`&Fgu7Up5<UUy{7o-P}&&_ z@HKFc*=Bpwte+4f!8nE&?o#uH@{JYB)fDpYAY!u@&xFmpD)Q#&T3<3!+G9<IE|m<o zvF3=sP2T>UMv<JmI9|P4-h{e-EO%o&DsaEQ0y_3H8*5xbE-Y<1JR|e)xXZ;tUqAU- z$1pZ?dHAzdQwkAGwUve%>mnfe`o1dY?#P*p=^e8G=X?I9`}P%$Bf)@>>upCCzq8rH zxJ%MFW{cp4hIy2VwSnwSjRMm_xhFxIAM^t`33k@U)@Cl>^>awSIqL}&DOQRs?iAsq zjn#Y+WF{Lp>kk`Zhim>ihsNHv;f_ViTwAc69r3Ydzek8~iaCyW&d(9zwuV!SOed#K zRloU{S@IEbEJ?Z;v+v?ckX64g())H3#u(gW1<>uDJY68b9$<B6pcqYVw+>>F4T*AG zpyjDUsm$ZPW^UQi`#Lq%y9PDJ(cDtyHBGBnD=&~)7r}yYc9%qi+^UT^f$&IPyY6o% zRZ<uUqmA~*>0SKD&|h+5o+tr7YxS3ZYYBQTiCzaCa-F_lW~ozVM48r*cAIk&OuT@6 zktJe98PkzlQscn*4L>u~x*0)^{e*B-u2@a6yV(7NV$wZ67W-W7k<g-SVhUfk&NrJb z{J3J77ZKVh5)jB(1Z--&jlOe)&o`{4393McgY}{N)6*A8fHznUAo{@xv+ja7z|=Qt zt6u~td9^v1yV+mub><m@No<>n&9~-8hM&x{t(cYt*P!KB9h}HKKuOLrNlswk6;rXs zsjmKV!84vONkjLasbxK_D=@wneB8$v4M{@3TyuF=u&eya_MPkxHd5q{1c}Zci?=as z0>2WHE`v%l80H7rAd<6hMCbW}$x=R=5{GO^g!yxF1;M>V(8YWlxPALZZvlF%!({X- zQvMDk!s)0tah|D(CB-cvHY(UPEjvuCyq^kwr>)H1Viv6@dgI_4P$WW<Qe`d>MsJwd zazUNqWnE;bj-mi_z4jeiLSygR_ud`rrpG;Gp+y>|(*%1>MnzSts@SsPyD9t-!XL1$ z@cK{7zS1oek<GoW%J|i@@`|9QYi*PcgWpNEujHm{;!DQ}J+&pB$#hy}VSrj(G6AQg z;P@6JmOqA>!++H<GaEu727TgjAr>%Tb!>WC-7mXms<myg@fZ;DedWLQdyOeW93fMl zRK|NLAQZd<kO2|gC3KO1Y(Lr{cCJ;cCb7V8Hn_*BpTt2|{9JL0XUj!SNSMvOSaWil znm&)GcNf!C)B>~tSFL?OCKD(TD$5*78)Q=e6F6%R&nfhiY?aLN%@w{3FJ)>lg~)xx zVkS~RNDjf*#}%WXz(x8rXRbh#L=TQuW5P+gtooU7DOnIg%_5BpB3PQm5SUMLnm0r= zhV{1-AJiN1c2;DiILjZ&m8v&8-3K1s8-Py|etGi<uHTITF@n6kEUmPa^;?i4X^)-I ztd+PUztyrYR6nJDw0VS`@`^yAH&JMQ=i|9dAR5mt9w1k=#a(}9x_evoO}mYU9D%O- zB<c_pDGP$11h<<V5w%p*&2d?Q`TPG}qtnzN4jcKR^OlmEBa>d0r~Oby3<y`-!7cma zlcycdab1eXwLlBgBIhi2X7{Q1X1G&&-Qv<~Ia+0W@3`cZc2`wz##fz5E$Cb?E+UF4 z=CSo*z#en|;+cdSgBj>{Y;6z963|1KIm3gW5=_Pzp5T>AspPEP*VE~k`|8I}>=?d? zSnGi3Pi7e0qX~T5YQ>y@cf~{r24j&7=&K6L&4ENRroJezDUI22b3CzFoiO*~-~Sn; zJZ$Ys*O4e<D-)#%TPkh}uQ!&b8?HA5K4GBG4LA7>o_Ac-8SZfn(ydZ^-CUk#gaDM& zywjGY17xCaZMzH&$xNdPZm22Res5qo`_ID%h$9W*xZAOz7!AX3N3&)sNwy0OCu*?9 z5aIi0<aH5Mn#@Ulir0U#0DYQ4@GQW8*DPhg2Yffc4oDt<?s8h|Z@7C1$wVhEotyGM zPXlla->%`fGU>nFR7u7W${*OhI8I-*7#t@naX<38<#YMkA>a`GNV@@6<+Ie1wCv>{ zo3%F-`d#%CF((p+2sem!S+|2bkq^Yhgr9ieJN*nF&RV`5d5u-IAd}+O`vJL;#g`M@ zpI1_{c-|_$VD3|`kiZw+sadB+M{FghaOcL48K7g!4iOvb4iH|$7DOi!vQ<KLY3LRd zJcgU3eF+$vz6vt4Xl;^k;g|TJDbsOgvILF8&-_k?_3qV$Z?2TSzun&LHxKLL@wZXW z4yMM4@ZWKeHR!+G-KXjx$5}#WyIUK=F|Zam2b193S@(>!9ZyYq&*p{Qrt3_fEZ6N2 z>88vZm9zpd<Tup&rz)fVPH{aPnSAEhHNiFH;-Gi!6pxMUgHaYyBj}5+ks1*ITa8FU zHI=I>Di<ivf9P50xTO|10=;F!DXOEnEvRD{ZuUAPiu>ekc{o1rnNDoK*yOt+8I3s^ zBKaJp%z?+;aZuBy5Jj_DCZq$+gv9<t3$(8^!Xda(Zbj4PxGvntPdQWdY5McG=jpQ$ zpr98l^At)A$qoFt*(Lb?gMS#c%MS>_JIPUrb#}eN-ysr}uP8U((|)D+98a%7%!5|s zP^JAoiE^gEZa$>58;z7xst=+DUQChdhlUV<+6DLJ@xn#J8Em<5*F<Od{mOSXX~cLB zssq{Z6!pXT$Vcfa@{LO`=mt`!<@o!%2r3Inr{%!9wG2CUt!A_9;)^_6HX3o=+&C64 z<QM4+X#jFgf9T~^N!=k>>674k{&86s5TW!7K^Q82f8&R*)#hqkuPI9^P1GzkNZwx% z6a9|Lrc%T(1-Y~R%K*0cn`m?wQH_q{gHca&;2V&5SMtx04ZFZMmEKdg$*7mIJL9@) z&KukbI@-jGuFr9*RTf#e$~vlJylr*>UZrkxs5eSNwcuYIx<ZFPOTwNu3&ZM^hDj%z zi3?H&<n!8}F9EFr%u*`@u{sNq4h53F%mq>JhidemS;L}hy~BoAyoP?6t@t}^_t>Y9 zMjv4tJU%0schKYPvy#WOV2+4CWNBqHOUxBBXC+I+wNrEsAtOrF<sI)rkp5VSS&$pk z$3zzni5_tp)HBQvA(ZACz<85X{ETM#OMMFC9wG$`fjbulsPnffDkPi(UKCV5J=W*M zdFQjd>abVEvB-bxt~7GAROvODXGh8%v%*bkO5tKJV^QiVQ#;O{9Z4CaWH^H+f=3CH zz39)x6DVyw{l2FecLM*qMbFmho_tk&!;36hIsK*viG-(Zm)g9aBtHVkaFk(93i*7d zptovq#%h)OP;5;>^)!>Ttl&u2q*>zomuPaTNCrHF7yv<bwbXwVLf(cL)BTQDqDG{( zDoX}g6My<&L|x3lE4A->b(EjBnyV(2QL*6nKz(ta!U}q$R_Rlwmr~Va@CmFcibaN8 z6=k3fIDaTy!KpBoD|tXa*s(w6GhJ)&eU&+FHMvtx8q4>>Z=t04Ufvm9L5s&?YWQlw zV40+~Jg;?sSW<6TH|;eG3lVo(aSO&O*b{3U87{HAD5VV4T$~n&dl;Gvy439*!7~8X zgw1ks(I38ODJ)!}!Ok{Lx`e6~mWdRl)#i!Z`>}b3wunF)ycn2cq58Y=pXELy;*#-{ z_HqGo3lJce1ie9u3k42nA-wE?@nM7gs*$XHq8W|+Z@;d2n|h3$Q0eV+(Je5$^FAFH zH>8XiLxajvh!7*_vqAI&R$!*|lJDcqULFxq2fbcNq46+$Fl{Q)f<ASd>HWlphQamh zrM;ex^Oy9s>^5uYP2-IIc`vVsRBDUUPL_i@3a#c<kc>?i^tURY)JUsYQ$>AS;(xop zi*9Y8*DO9jB2!=2w>~?bw|S1@U#wl7`dVv3u3AQE(+YLS%=VW0tZC}4>0+-IuH1E4 z*&inxtoC1WvVr&eWs8dvV==eqHV1WWBEGlRbQ9BhH|vW}jm5_$RI-^KeTLZl(3NGv zW6HW;jvd=L!unw?K9^|P(QV;xG~SFw<7leO;lC`BMhY%9>`QvsNIvNrool<&fx@S; z)-vbJ)H#GcrId-JJ9EW%(h)zzyQ{~=XJ)xy7Z@>H(H+h~P&aw`a(R!3A=oTV(K?!D zh|zme5>bg*X1t34qs|ki&z(lHJMNNw*rvVJKLzA2!0)$ZF#L;FXP&(xdvv%tgVId^ zdgFI2Y%^+{NW#9D;Mcp%o(q93xCeX)wx{4S$05=?a4GGH_tuF`UE896|9nYy`a?-p zOpy!eRVqsu{3m%SjmN(tnup3^Z(MCDqVrv{?yzK)ez%<4k?+qC3^xv)X_KZTjFjQV zp%Z=m>x*}ZwIlFk8EtYm2zz$v&2JwN0u5$=>HG2w1+I`k)<~6uY^4U=o2@|>;5Lmj zTh5aseZwMP$ymvrW(}*P7K>bcF0z_Mk6^8fLG8orlsnFgZ8N9YWESjXiNzlUzx-?F z$Hzw6Ui6!r?t_~gin<9Pkh2W&ZP2p+S^!T4wexbzk7Jq^lnZyGO2n|4^-LR2Uj?eQ z<MY0EU=tI;&?kbSu7Ic<%r`_2%t&Zo_$!uUY_BtRg0w8e{lFpadP@!p;o1szk>Al| z8}RDot-f6H>hi*9v&>YUZJa*$9MsJE`DAk_IT=3>P^0q3ukqP?onRa~;ggDpNosn( zY?ChLJ+NuNB=B$^$Tl)OD`>NnWk|hn3Y+0UM|v#1c(xO7z))~3)Ti%hCaD59rF>i` z%8@VBPbm3)^+r0d{KtT2QFb!4K+%?q-Ic2>hk<s~4HCN!rB4dVU(RugU`QBZRw!Nd znpSzqaD9Y-`ft)l{_c!r>54}57KBY_^Vb60N3gB)dn`nUur_RdFBXS4II5!1{R_Y! zFpjd*Ze767L>3bsX$Gj8C}UGATLW~7<*-q-Ov_&oAT(pt`OUBZr;n&O-La_OZj4<p z*{4D6^(cZ|J32o#J1q1ImA%vk%u^2dF{0<w<yQ)zaUb&@zM8V-#DA@}{UQyycn}Sn z9*rvuNq(uBi(Glx@*CAWTE?YqaUkhL*;g(?+{YE9h_d%;Um<g7=x-^lqgFV2+<tk> z&fKYZ3uHfa1_|$Ln7mr>q`0<Qa>uubdL;iJyuAfip23=?8#K7P1b25xfZ*=#_TetU zo#5^s9D=*MyA#|!1b2t?X7Ap!dYzu>={aX+{sD{nYQ0s@ece~ZBcM=WX6t$(UALqF zTttiUeSa{tuvuKQoK`#LdL1@DGZYz$eTh%Y`f&Efte+X(6h}p7yW8>T#B(hIA{wj3 z{D@Mz1|itDChPM5OME4}K1)JBQ?su)5qZ7a++I(OG;`}#<YfNWt0qBxb2fz6dfn`7 zKF5QnWjes+=Kg`L<D<Mq$&`3II><d7?G!x;wQb8c&!2i=T!r?;5FN2-M7QlaX(pmk zm(S>t=~Kpc=c4MBU)rA+2){o*nkX{6H*OT&AzqoGwKIoFd7XUXbx%Q<TTVV%{$b1& z=%05I*JH@k7M#V~6O$;2TIfaD8VFz3rx3gB7lE_Jj9@sB)e%_;B}#z9IVk(UOutB4 zDiJBk&W{UeRDFVjJh4aN5!qg!g^*Us*Q$G(v9kDSv+(WvdU~ga_;nRgwx{+fErMt_ zv34ep2kG`xHLEuN<{p8Q{1|(O)zJ(LjBpqNe=jW#?x$$CA?DnJ!qrcNO$hmxHD^oW zOrKuDq(evtI{sW9xp^a4<)6eH93Xn_Vl$y{X_R9x>;G91CSyDU*roq9vILI{Y0SNB zurv0$)$;^u8MH;Cq-LEz`sXSUg;5*nC+qbHX<0s8a>{Rp!n0CrNYzq<N3AwykZV_F zaES*NE3^@I+<@ZQ%+e0BJe5ONAD_dYSsQPv%Fdei_OBa!mt&`5nX`s_O>Og;zMICp zLj-(Q5Ve{&cip<}-;P<ga{!K1CUM|LlQne5nGo=Su^$>Tmc=0tOS?NxK5+vmsWNDW zw}lQi0N?9%*v>QPX<p_mbnWRKXO?M=#c+5sW+j!(q$UF4CrjUvAW^AM4#fXr{6ry; zgTm6?Y=WQ=PF*5R9o6O(KcXlS-J)y)dO~3~rwGSS;2~XZUM`aLi)`Uo`^_SLH#?oT zSAl^W^=+(+<wX$!_p8mt(~@|b{KH6JU0bxq-LHDYVe1q-Ph2bZEnT6JPx~kF%8HY% z^>gvkw=c#$=f7)I#QEn{!@w?_)X)1V)*hFaQ#-KI7zUT$hB!dL5B!Rc3_B*Z#^)_v zH^Y^1i>N@029egh)e81aX-vZ<DT|`I2CL>jU~QvA$mT6Tpu?tU?~S?Eut*B?rqp1} zrdV5)WE_<>JO`IPwsgh!;@aX8WRfMGx3q^Du8`VC;`HmhTNpjAs}(+^5>7(Pd#v^f zI9=DHgn4i=3bz!(1(4p%N{AySHcPN<e28Hhn%oH0-u7Krc`q3Y@w(X&)DYW7c9nH& zHZ5s~p+tuqSy%AJth<Gj7e&D+e1iRh`4)!8YI>_v@BWTHfzNof8K8MTgH2U8Y1Hn2 zl!Yix2-%W0X-_etbdt-vFM6=;R&0o$Oo0ne-}iIdR6Q$lEdPSJ#V94UFg7f!ARi`i zi5vQNH)UclXQ2nrL&;Wea+$mgxSGxnc8ytlq&$+2Uy5LeM63x?Nu>hp;F|?~hKZ2z zAMg=4;v8UbWTWPBqmLCrwyBBQJC8h($D56&cT!C4^y;oLbv<Mz8At4H%jqbGH5pcQ zU;r?E6f=tGCk{s7Eq1cqN(92)pTk3g^eSXs_KR0+gl-WEw=2i<;sKn$b%3SaUU72= z$N(R>Y9;-c*x@yheok>xDitdY0o??P?70~Bzw%-Ksx(+gE`pTO@qf$Y359A<LVRn} z;|y#5sIN=MNX*B@kayP~GlB@|zD?l_a9j^1DRo6fq!rV^4?s7kY$G*0ZT19Nx#lBd zRe9@PF5R4e!w>2FQRR$#UACGIj5^Vs$IODhBeG|b79ZSVCdR-?@LUujFNCMJ*iLn~ zXxmok28s5_9k0KfS@q`Dv0hqRBUBiRD2DG-%2DQ$*7cK*7RaO<fhojGPB!;a+lHo^ z^cO&Ca0ZbrvD+5rAaz0qpg3VK#rn!H-S#H$gpVtT71;-WH<uvlM|?LTHm01BR;6Ag z)<OjjyMzU#B7K=^vnyPULw=C?j|%ribLZ(EJrVHd);duw5r?)*5oy-$M=X4sdYjk! z`D{cwD>%jxz!mt=y4}fbEc-m;>h-K4)%W~ZqqS8X9ZeSq1D0x?Pa9u<FPexak*K`R zm2UN_390;WCpFmC2qHwcXz!Go&{lElc!2Sj$}=0siE(4I=o>AwAdDCV!^{P6EvXrm zVn{VXogRSd+!boU`^#%_1H2ZzmBqh!tx8N%n-s8lv7`saeSX}NPj0v88W<;+t^3&g z(=Whne$(^K?>_VE*0JtTqp!z;bb=a43&OblcdzIly3VDY{9zP~wb?e#avI{FPyu?L zQ-Kf?XP+;$fElhUXXWGmt`ooWb-}&PTxiM~i#6--48#=0?z)5DE$-yzoWL?zTi0^S zDepe2#%4*<)KZSTE-e)(xhz_ZE6p+oKv5*bwD>8BR8xAaQ5aB2lHDNr&^C9A$wKa> znn0Sw&KXD&)scIL{bN#jfMSlh(;!`5tB4j#BA?EkO<acvOo(%%a;7*ladIz}x#W4D zTOI*)EJD=zatB9`vws<12Wf^VJ1xx7zpleCA~5+_h0&>?0Y@P)EZ@w~?IpGF-Rb!u zVKvHY(f#$S&xnFd<n;dX^6r6c?<@!n{_GDtST;Kn8g&J`BGdo%&&-SeFEm%`V`TYH zeYth3`l;2W&(haEo1QydRV9y7&x)I^E^F@haxKo&Lc9(Ad&zF0G_G8bN|3|h2>xY# z9?7~{I$CN6C<suRnt}q+{wi2?k(~6amrIGagux82NBMPe)14L4uPi+Xx--qozxVHb z0Rjb5HDO-EXZVra?7&beEO6TRA>LA|5@(7NLSuHwX}FrYHU={+K0$t@Oq=iLypPsi z(?eg{^@5x;)opqJkvQwH%S-qVx+bD-b4dPs5f^zCe%*@e%&3Yyqc-s2a*^*5gk3Ub z^qc-iQq#f_Je_a5E9vt3{3qgEzjUuI*l%`_kUuVuR;jy$L?S1k3M|)lmXAmO|G7K9 zwdu6=*1EKp#bTFMgEj<`R-e1N>4ut6%BwUViyEv)&(xsE8Kjn>FaPzauq93WwqgK} zq7$>U;}4qaW#|Z|f0{wOC2BUGzC65n9^MRoIY}PO_(-2xYmBc4Dg4gxWIB&peI*%T z9<F_;Z&Zi^hY1ILFXpNTL)S=z(_#L_bmskOUHR)8s5j%wd{>F^`phgJJ`yL)m4@U7 zxzlb%ksf7BxFf?-n`zz*?=+f>1#fZ<>@_=AU}7=>nSMSM>omCt)l%T>bO#1IJEzyq zFlTf3oBq*HM8^~A1~GM;3+(8&LCrvYF`_e;w{g!RT`o%=toeaqtW1ALj_v-0uN^W9 zdwMhr192{D+NR5{>!H)XDI&)-KaEDFH-O0-GDn?;Fyh_Z)#-n3d8IaPaZ~yp!ivMp z#1TMNN&h0a6@wojJvO?dM-uu~sw_Y|7k)^}NoG#lp@!~X!<OJ0b}q?Kby~78&9HQi z3UcKa$Slo(Pj4fZV!@iU+6%#}bxVROfweVyuHWkux{^XAxi_$2`p;Ak^|7Xjen>m1 zPDEJ=UNWB9{toZSAOo4&wY&a`r~8!cPkvV~2a-GGe&XsFboDY_05yyaF|7pzVtq$k zip#I2SJ9QNq%~$SWvxEc9sa2I{IS){tZMTMHNocyO|dSAd-ittU~)+GlMILWnctfF za&#p*1?)?Xqh{d+`SG*7jfTs{N!5wQN(7ROg(;H$GA^=dKd-`A4D}5$T|mis;w{Cw zsLqaf5Dqcou%Tfo0Ad>Woivy=nVJIhYN23>LKV1F{N)^v2U6vGe7ApS7Y-z<Zf^n0 zUf-W;iZMmqf^5!Ad@#aBjhOs900d?pzFWPYco>t80H`|XWyYm?yVcI`J#&!{`6ztS zd_TF`+{SCMVTo6*U{udmd{NNnNh?nItY-$0<zEeBTG?77fr67E?C)OhQp15j(H>%L zF9->tG8mK#2VrVQtRA7Uo%qEfq2QKY%;=<p)FM~SFp9<j{xE;Er0&IWa_+Go$iU}X z?VkwU-_JEJ+hE^jxyCI6lkJqzM@3Zny*_&`K&eJiiWc1j1zz*-hLqPsq9r%8AJ9`s z_AoQ<qcU5-kO{h$O!qt|@$FxhUx_nU%~(y}G+P~*iZWFxcS6@|iN1!^p1C6iNQwGh zh<3OTJ$%W6`pRpu{?5;Fe%&PVxz%O@e}8J+TER&L07B8!ToJM5IM_tu=<%$j-7$*T zl7ciXz|`KK>YzEn4M77<LiXHbPR-mH5R^FRtB}$a(FTM0%pHEY^)fKjqG*$;f_4WU zg$JbFkhVrl)wu}4rY72s^xNVjMwlehtLm{OP^r~)b#1Vv9IXBJZxVJD)-kqU&H&=E zYsu*f2uOWhp2WsW{aZNfKkZomN?>~qnv+V2fx<`0cb6ju6*HKAZT55<lee>dX>xk* z+Ar@s=lw~PwW71>`e{~+cSx{{k+v|}9yl^|SD<M9L52I`!Ca9CUQQo)()LfdPFKn( z%%(=(C`W|if`iR}D|lBaX;~(*+=4<gUzi#Toa&{E-`u}`VQiKA|I2i;0|ucN?@@2Z zj*8b_<o@JW;cB*(mh-Fh_2d~FZ2pm)<rlPjrgC`S)vciKKP&s%KUVkHOgfv{?|08= zszFGj;7cE!6$uI=8@;$;|B(1i&^N7#VCz-x?j4dV{WryDy2*Umu)+^OPy>{#vEmJ7 z?htP>f6z=Fij*fOBJ$c5S&3L8a6=f&fpaedWSA`h4P2Xj$xO(&svTqnyj0=4a-)(g z7<QJ50~8p`@VNL&2x4G+3DEm1j6?))nTOTYu_fpwNDO5lBIDf6A7piE^iE?Ys$$0a zj-R$VS}F}Ydb148gae(A8hxq&3~KP@TWzkYvQy{WA7-i27k7F4%^}+blb1Te(7B(N z&$O_nHB#cw{y;?&V4g=Cvi|vrua*~r^;&QC?bCPu)$?y<ugAj5=QyqAz<mEfOQ4HU z=R#2&6@!_KtDy<fW${1eUH`{IWG%sUrJ1d0GD6m+@JN7mtP{+Ri(r}EQcgksa#!9- zrNex2GYN{_T$tL7jDsFcBlI6ct+1~>wp|V~8T%2TVJaXU6Nt48{zjamCt-fyes$kk z_{Fwd^Kv)Dth2+3Z#MFRf%#hrl&_!`oh1ArZ%sM9jHy%$E?O$Js4?557dNJ{=3ih_ z2Q)^!92YOVgrA|5hiTEy!|>C$Feh*v8ZFNl+W0S5B&b-$j$NThtiTN+gr`g!LI%!$ zW=v_5dZ1@%=m6V9QOK@1yRc+WJmCW(j$il#qdnoslE5(8jXzkK+HQ$W`P`dE^OO1= zvM+4n@R}rH{W!XdC=k=<+IG!-Yq=;%L**WTiaxojZ8l}ko!6-&b)C#uFv;pfQGIz* zUc7!)XcY6Zs2h*|@6f`mSBCeIjLQ>n7pV586pnW}^9|#eKP=Q!2DkkN8$1b3PI-I@ zc?jNeFMmXK^=8AZYtvL#j}A<*szDm!d;@jfA-CTsvYCOhQ6rdq+*V@jir;%x$vWMg zCzw1r4ON4NfK!-N<JnN2!5=GFTskY3k>5$?2SS>O)pQX7h}zW+X6vQ!&x}tC-fa91 zb0XsjzB13BWqORozNU{4;o6OO#JO8tQR9x#bj~brfQo0kXPUh~;8+?B8k(9w+lbwT z8_@GSqd_)cxQ?``!1^2T0g+cf+FuwI*182kxt&xx+R6ZT4Hh*@Es8s|CU(&nt?fjj zkj1r@#z77OgRceWfItQOGIqUq2Q`~G36ZKg33t1iuhR-*vfFNxi_i>IWgmO`67|fC z2ZqeINA5cHr$6C6mZriA4uo()QEf26DC6w7c#$kkly>+1%sCZ&|0{6N%+}jw*<1ub zlp==J=yq&<8XdB&y;u9+?VY%%$R<|U(W*-<ATk0eHsGSdojAjjjA9_G_h>I6Ue-%u zH-LV6>sK0UexQiua%0e_H1jX`DB{1tN2cZJn2K!*#9Nd>aWxumX!6KkzOp^9E=GG^ zYkIR7p9y0lzV+!QFw5?rBz-A7he|=V|D`U>Mno68Qmn8bz%T}fh2c_IC*5ksvi>^u z@!fwr0^-272V6e;oaYe;0E6xPgV4i+=fX)hVK&v{MMTk3?df>rX|DY>6Dx~z(y}Bo zXV+*3W_5m{$q;KO-B2^Z&GK-Gj04Y43lg(d?}ax5F&HY$4)h&TCm1T5EnjIlJbq3X z*r7jKKoiEKiVk}LGVOjqY?=rcC&H&u#-<Mnv_iy1=>=g>h_}g<!PW@&=@xSEU}0x0 zgqE8tEpKJ6avfe<JBMbry&volt{0&23J%q#e0iqa%S7kV%B5*g)fl}rcD`4E>zKG% zvXz(|J2OqbZrnN(J+E%LzL&g*VIx02GDO`+X|cLrHZrv7_q2hYoWTUW(0kC6Dzr?( zQVVy&*5IiBc}i<r`}Y7-yjy#Viis)3Kd~1Q40y#PuPc}jl2#q<s&)2P|9%=K1yf!X zep0({PkR_u7)`4Ilh>5kTBe7hQWCIlb=n)b{_2QV^hfkVs29|cnn4Za!J6uqXE_b; zTPLS#qyyX^gP9ws5dnq`_Xn#X^Z=R4Q|7X>fngjr$uQD47nP*}KPKZkpy4_P$ufr* z_F>=u`}<PuJ+W!Kuj8L!MzUl>lwBHN^p%RHPpK2`JOwQY1Hh4z*ir2E9LXIS@L^yX zHP%WZlZiXaiB>1U%;h`L;Rv2dB+Y)Nu>!xd6lh0M1LpEwf-!jpqq4e&Zcq&Zq%$oI z1hx@iEE4@9h@8Gf=X@5DD0j?@?*JFKK;Kc$6LdDKnid%PB;W>m2I?t`1gkZ7uj}dd zrf&x$JiwL7-RKW=W(|&U*-w`2rG@<j7g3CbHgy7V3=Q*Cv`&<1!w9I_xCQ>z=GxT= zr+2qS5iftQ!gUGW{m0|)%9l_9oon+zzmAG65=sGeEmS5ezb@0f?;n{<w)O^!Wi|i{ z3#t)dzW61#ZECQfLoKTJ=z1~)Y%!@ybE5x~HVfme$Z3gW*d+65(h7;h=NS_m*{J@9 zXrcLkCR(V-p&nR=bj5lh5ft5|Ul3oaxt~88YXotu!k=ef0dT6%R)GIGpWTNiiz+wk z!JLBz2@^vyXH@YP7M9wt#aiKMK)XzO7z+Ov-IK69_NU#-^~cdf)wXlOF0AE~Ll3#n zm1M040sS;GQ~`@F#d92Z$zfiDdnAcziL*)6@Muv<FV$&&p}?|@!^YI4$k2?Q>43OI z0zkYotct~-Ynt;?ieeM>0FE^j-z|=nGNh2j7Ca+fK2OhQWt_+K2rU#T)4{<_3Lnol z+Os66ka{gSF(A2~gA-Dt)CW#tg!n^ZCYA-mq_E_-m~*^w<L6bX!;LS749V|Ht>?s8 z=CS<RJT(r%yC7Y%ijFb$vcUkQF0l)n51QezYdb^d3C?t}3t8XhL+|yOfA3-=zs6Wu zuJG|V?M2=)90#aN6;7$G6^dm^Iebe_f`y!~`wuhA|8W)9USM~nBf9o3vn!ea>3#a) z>eu-cxHa83CKN@T7l*=Mc73^qG+e|m55&p?bmLL*jca_QI7lJ;{ugj;2P*F-Q)2Bi zkr>!xbOu^lsYl13@Zt<f%noy+p>%#NqNQS@Ip08U<1#KN{9dm<=<S3DBJxQ24c;T& zMfOi~xpKh-zZZBLk+SfVea4W}b@{A}G{gBJe|}uECK#I-9t_+S-yU{%Q;+N$FCAz5 zHrWX&=v9O1I+CUeyoN_KRSc0bU5lY|G4R8L1!NCrrX>k{hx(`@>1vnbajnX!o==Pj zgZ%33K}ZA=LJAgn@ZTlAR8wN9<Zz38`&<Jm!IdSXNaE--05SofDtW4lXJ2VleGlc0 zy`j}++23nZ<_ejQdV^NBqYBa$dDEq<)N@Go%dK<U`JMzRKDA;$CBa5alu&5!`B;B# zW$7qQbBevPf6A<&vssQJkJJsO7%@=oNg`AYyvYE5i1x%Bm7BnY!%lJg)`$X<Hq3{+ zAYXM`L6WSv>XQOyJ$1gq5(}9R<^<9u1<kgbb`Il-p7Wat*y~J7z4g37WFS^kJn=F7 zvnh7fCY#G7aK&eQ@VQ@^(&Zr-gD_NejST3k>rJC`86JoB!xWbcp37rKPo6u0!b*1k z3Rf9DEy0APSJ~67d1K44QA;$p;)>gO@)3EQce{1az<bqRQ=4#<x=pZ`(n8E)Oj9$} ztv_B#99`O)ls#dPdoUgq7dv4-wLno<%5U!sI8N6m%qfD4&tnh>34I$KLX!##Hnxft z20<J19LV8<WQf4h7f|+qS#d_7!oT|jT`ox2qU1P$9O;Aib8`8`$du*EPQ%cZ<m6x+ z+xdTazW_DVV{U7j4a>|rLndHbEDqUU?pQO~Z~wmC!QOjvw4v{4i#Y%Ih^~LppHU!O zRM|IXlnQYH!bWz#e|+r%VvUx2nO5JMcuhJN1Ak})Q30g#=qxEoZn|RWS~OW;JkSsK zUmOppQDpOi3(T(2+Rf8vwx50S6uiT63s1ycEk*-BE+cKT=E)p&2bJW*VFmn$Af`N( zzFV}bf6r4!57J7r3xAAa>qqo^3#d0uebMV6+z=r7CPb0hK^~HkUVU@I-x^P1m}Jf! z>%b{yLHNUAoC;c|tz&n27*VP;)VYGL3z@f`Kzo`1>BkSxKZL-j#=BSh%RC_=f)0Ws zw4@1#4NUyU5n-uSU_>S{wbn0x#r2z~$7!PZmV6>Y>5Fj~LIm`?x*R^cL<!&R9tLpV zr}}2~G)g$})%%;v&u!`K^v1vX1@1nU{S;?d1NACw7$smTVwR{oBqAVP(&k}Esw_I% z>T&FYQ0wi``^QQ@^SMqNuXBOI4dLd8?e63$8|-l$2mKNP#G-m<Now^<ajeGQqA#iN zzc2d!AL#)(+i)}5qRgTPTta9h6aJt|H8?y9#&mOq*j<xr|3-a~_3E+6lEVKPQS<DD zqs@TQ&%@Kvj>Uv7-gIkLj=T!+j*R9g8QNhsR<++D%kThD*+iM^S6=tm&8LP;FVAa$ z2Q!GT+~RQ3#GoIw0Hw`$Icmw4$*4_9(iD~}l@9vH-e4ccysFEMMbT8`enqUmmEvJY zSE<$XN%wPhjycu1Px?}ndA;`XOj$l4(>T`H8L4?>KRH$h+rs%sD%Ea$vqD46+`NB= zC5Wz*aYI?+b!u=2@uK&(yN=!kP}N~6NRRhj2v0{7h+H?>UyWBjj}uUn-I@5Ido?KG zMbjnW{8BBo6Z01?mfo+18~BG3AT76c_#|t9KSdUdKVS;5?qD{B5B_xm&^mIn=KQtp zjQ%fJcgWhBnTCm6NVXJ-7Zkd8l2YT3nUFbspIaTW+l(GkR-^mDQwX!Nf|`=38m$LB z&fLC3Bm)cwZDK{xkbuBi#gJ&kuZSc6(<j}Y2!ng}ibv_qpv^Ad12elUAQ!=7xjdO+ zl~m7g5Z`9X#QSxWgy<B>*<eTgI&xL?%NJk!PUXh1lGNi`N85vpEK{k9iM>^?bZI!R zGL{B%jKXt!(2Lv_CrCGvVEbbt41sFIDQgZ!SUx`E8BRpJMnMwAQs6>@z>?Bh>iCv1 z+0d}1eWezH2ti+kg;KCdno#2OFdri40x_BdQq(?O%~FocHy-uAY*3Gm(}Tqw@aOTn z&-L4_e<4h>IS-!S#j?|{V@vgi#0Ka0;`z;yr!Ko6U_iNJ&JAPvL6e3=aNs)$0<*wE z_`rbJ#G0S~#|Mtrd8B-K+b>s%Ncq0=b+A=2+l%bUu01Kp&r+x4fdRr2a{^PQ?k4^X z-4*NYsTOyCwF7(XOYD4YKbJ8Uu(!x+Qj>;*cvOC;{_VswpT!+!{dGDV{+FE&%VNfz zKMi2OY^mL>K;tKWSz@<^ALwzTJ~vh0yMCEM+EQ~NYWOwrFjFzu^8G1Wr<Y01ujWso zCq8SW4{ep0Xa0tD{mo|O`cJRyGxN7bv(r+2-p|kITyngfPJN1hwF0udR<otdo6PR0 zRG0<GQa=)zNazw?MCc{4?5(9s;=q-%c7r7vksX$nm|<b-A~JLwO0HOLGT|I2vEWlM zLJ`ZU!F*sC{UI03sey4O?$Gd11|xO6OF|I}|0T2;5~Y#(pu-$DRj}!m4ipQgLVvi8 z&Ss7ZEk%7$hKdO7etjgYO2ixvY{kpA_%i-#w5Rk?D^NSWnG-kF=dyeRg1BmdzTow$ z(S8_@j$;$0yMR$!;?%G^JS1@Z&`;$z86xa%a)GXL`kB>#YQ+NvboiYfFQ)W)cJ^HI zs~h35%)7@*A2g$QG-4r+nXC|VAM$2&VJZ<*OcMe3SZi}leKFd<`A&HLH~CHyiki-B z-7=f3clC@b)}JN^gbduCakx+;Erm@z538}hWsGZgz@GyeC6<mbW+Ba9cf`QPHrYN! zX;UbQjTFtM<0OE#-g<k>7&`QMY&<}Aw({`&`2?z=)<~VUG*hRaVbu&Nz@2GKG$8?e z3E$^ZQ?q7ixeCG3B|+R4arB{tyQY7XM$?ym?Df+^o?!^h5K|K=%l-@)j1vm=G|JKV z63A#xUpN>?JEj*LJpW55>w_$&MD_=(W$FIv(O^jp&1hSL<v5|7#QcjMj`_p|5H>8W ze?dc`N)wv&IH{LN7+7!{<u}O%CmdLYw`dFXh<dEU$B~YrmzSAk!eGWDF3;-#*=J)l z$QGsYu|Os|oC{kQ%~<r)b<%~<@<igTW0>#OEHAKCIRJZ!*_r4wQwQ6Z-3d&4xmux> z4L5n(p^zhi8&5sf*$JKb5-YlDc(<s#NwCu9s3B%}5TKnvWED1vTjq=KX3PMJkJ&)+ zk<Jz~>TmH;b<f^d;BWCU>Azfj>==G|jrjt8M)>;%2whf{Z|Oesu|0p}`@`@3Sk2Tx z__%J{kiyG*np@M8XVB~_aZmpjVK-UEVpg!1mIw?$MDP0`^lj>F>3yY?@FQjU((Oy6 ztU8j;3z2t`Z}I9wjvXff$PYM5T2K(duzr-Q=eCJ0AZ6bAO0~axGTf|y*Jly4L+$us zQWNEim?k3r&<RBo3BL~N7~Rr5IUKk@U`@jFfHWWPpFzatgcla%oDXAeNbC`fZa{$I zNXEi&P#6~=_69E(C1a0ff!!@~;Pj`4WG4tJBOa{Dr<tKBhK$ET7g(>=42+>>>9$`C zY~0_HL3*}`TNst}v3_2;*PVNq|B}vhws^Hw;Cw6XO27WZS_zL(JFj2lcsi_Fw8;>r zuBl1!U1Jb{{)3r}YXnUrqL;J2W52a7x|N>-_q&j7?E5J@oBjl{auB!GK9UR@wX)9$ zjJBv>+bjz8dTFO$4l$z-7<*Sl|JABJ4Ime?-i=#c2dR}81Na}%0B01-ajg1_|Ly0- zFbC=|9F=_eui<~AmIlO|cUKckcWyq5wLVjz-Q@Ruf75ccu-v-eS+5849Dor)XO5P< zRb%es6xfVSFP9)4mdhC0q!lsAY-1!e{;vrAKrq#{hReJAzvKnsv?EYu%I>t-@vOA< zk{)`>v0GTXq^JsY3?`P&_4+;sElCRw@s@uQQ5UVDCt`u{lP^4f7_9k&WiXj>wKRez z<Fd4^uvatbiIFi}PoV)tTkt2aL4tLA!#ZY+J&i9S!~S}21ui=h75#cEQssMV99W99 z6l*#fq$qBZBBp2!oJIqGH=nsE0ufjN7F*_WkadCO3H~s!^#U_g$bUJJ;keEu$aaq! z*UcJwep_`IYN&c`VnifrGu+`i%W`>xhd;N}`A)L`x6%)Vx3?2-CEkgfvt5PFvSkH~ z8&Cc!&T=8tX6(JkJ)%(GQsqz*3^qC1_vP<p2KScU-%@&h*9}w$T4iN_pVw2f-UuOD zzvvbBy48;6kyfz&M~`L@)kLqj_8(unIXZj^gnxCAb<}#9|LKX^D2>uw_aOAB2i7uo z=)b6diRvA-fd?K@0PRjf;K(ssKRvaH)AJvmN&c)S3fi`8)_ggOhHaM3fq>jyo_+>% zbq?GTLp?BoJ>*bW2?OT>M@Za$Q~yq3duX%7Rb=vf>RdMEZrh`7n;md#vl*y9(8oxB z)D>?N>zdXcFdM8yOThzeBO90TZr>^t7Uk#UK~@{QbrUvW6fHu$oHZN)CT@sLW>ukz z5=b5Ea2PHTx8%fK+Kf4@GUS|SExV#{#R6`~RMvn#EDof02g#6D2>mjotO<#rbb>?} zHfsP|`xLYbqE!WL3KTWYvOxy?25l+zR1XUzjXxm7_;f<q@dGg1wo{Yb+djz27N;)n z{bT~Ll=K{fn)NP3gqessx*zk#eD=fD^Ev?A(GqIms1*IBFy;t@nqjw_R=HLv(H2z$ z?q%9o2uVf9%gj+sw03Z$>rpTt>#&-4i&!@{wKb0#V7|Z+Pjt^6T{-;g*sQW9W-<7; zVYY)NY~MePJsxG8rq0LCzh{8+Wyq)h+TNRGf4Ys1hOg-fPiVU%Do?As4EDM9S+#mG ztHQav>_U9(rx5nt^z60yj%_5t0KH(epo#D!sS^40(!dD3aJ4q@8jre`f*R>Yi=&}u z99nkFbwjP~&tc5u=i6u+qLYPhUMU{s1%60n7jJsk!JL<0^^jC&p*OTNLug1LK`4x^ zma#l(s2%1K3n-CHXDs}N#EpiU0xF<2n)O<QU#yhFjx}*97!K%#c<@}MZsTaGrdOBL zTHbxJ0;zQ@5J+4l#$k}=JMomz_)h_m78*n2Dx9qF^y?@m(X;l8D$`&X_c&&bTuHNd zil|&UHqf4y^3Z0uFg1T9vVmxEb-^I3m8+r3Tl=sTz0&Gsd;;C~>Z06Aj1iyR75<>1 z4?ofIam%uoD^N!{Z^nutf9ujQV$`z#gLZD99Z5lhn`yn_&=|n*bS?vxiwC(TJYKEO zm+>U}=d4fdo>Y9Cw#$+GtA=Eqf>G3<5LS@ri>Xv3nCOu!y+ZmwoBz4X|2vxhCKlCn z_u%V0$Al#(;S-)2Shj<$3BZNmvG1$_kfBCp^dh$|>VEURh5#jRHNf9&_KNcVvGUip zR@6$i*MsQme*DQzjq2eR9<FIt)6a0Q=jE{yT-lehoy@=#Kp12QeTB`JfQVke%vT-n zPfTS0#ywGuNIitYCPx*SPyUspTrzNn1m`P>xT8@236vTd^RV$arXhA00uu}K8KgM@ zk8i$GO;dLoGByj>E_DS4IBTNG2r(?HleDG%;=q>j8vEp6ijY9x-at165j^dDB!MA8 zBAh9KW)H}Z262+ppG<vduU@>Nq({}SUafJc9{>J)k<0tJ^9U#T`eM&{H`#i~(nCWT z88`o+-qB>gqjKM9o-PrpiMkCs`AL)<+|;~h$4K~RMH|3h)}3bsF4xIhvc1&H<pal3 z28aXX%vvl-rp_VbScd0T!8BtrO#ympD=sn>&APt_6-Q04F~9YSyir&9i?|#>kTp7j zAv_F3N7(SLke+`JDqrmX3mUz=T7?HnaWuMh3~;0tll8L{%F0gQm3w`QuU8;n{1?Dd z^uzsDgC6eAcpWZMSflCHq@5`^w{9o2Ldl4~)Yu=fl$5PmT#Q~uDKHQ#MwyAI8mDg3 zr~Wz6)y1N}9$GxZ0<m{b(DwBpu5u6a=0gh6d25XKgs`EzO2sOQNCa^zO8L*}_>F#Z zk*Nrt*3=r75QCP)@nJUe@G-@uf#G=Bk!0?~PfpT=u*?U)gW*pr1cE<pM+o+k<fs`{ z&H5WqBAr6o_J$ZT;3<jBvoj3((_^0u3d48Q@0!r|GCJ@JT1l7KQskw=a#?CLnKbh? zT(*$Xhtx4<&~zEc+AIdg+@#tA6A$e6=8MY%#JR<j9+Yoh7Fz*uouBjgwRJokdB!+? zchv*C25=19H(}tt$_YP3rD(gJd15j(EljNbGV9;b-&}ZTp$ql~Zcd%{`_<WNzot(I zTB6`Nci|B?P5pwHIwt}O_)Z!JD!*c-wAR`uR3vM)l~yVIRpAmJMopf%dylOEce+$y z8Eb2_Mu-8I+`5$4o4a9)gT|yb*<DcP;yQ?F_!ESDT&)qls#?m=K>NQ1?KF9;zX@pj zzf;z5O=#RaTrGY@An7iz)980us&{Te*8JA<n$YF?+AOd48AEyJ<)D$0-Xnq@8<t_P z)FgA+Pq?g{cMR^25o~h4s^#g<-OW{xuff8^^6@=VM%V4xpaE{soQM3YLDOaGa4~xU zb90gtBPu0Bjc123hT&(!8@bPlQaG9Pq3ESJ^oLaOG%QinfuzDHu9HrYVmX;MBU!`n zIfiIr+bCkmwAX4Vc_@SHvKN_+JB<zC1H*M>O}Wen((5@jC)k`q@;NZfMKQE_xcR+~ zeRwP5fpQ4g%m)!DBB>G0qhh9wd;14{U`VRdzkWm;AY|T>LkJ#Q!5Ye06}`m!@7q|# zoGOF>%ILFsuZ6j5&qz%cgURHQGFQ7b^l{^|%}>o|d%cP9u}rh<QH3eG(y#>T%2+y@ zVILq1%74fFX#Iv?WY%(ZdGxidW&8Mtcs4L-iLouVdjBOu4fNW2$4s7-by2xLkByVa zVtI~@t;k+al>MW1(zgAN0AvZuEb6#y%bKzlOYkD4lp-T69>9vMZbY)wrga9B&$qkA z8tCXj)LpkD@P%jYgvcEoK30V3(@(lTh5VcUlnH^wvSatuOX*Uu&yK-Kw=goZVlk=R z;LZdvy)fx<?C>4lK9BDUcRAb0-lrTy4QXTI-y1yOiY<kwd^536>_^G_#ju)^G(U#M z8*L5C_6xp)UAuZ|Ib2tR{^-Q)@W(HwS=dF+_6E=eqXx4F^GhGuqLO-Q%fMKI=1O`< zSEil42l|wx4obr8Fv!H?1b<H2k(v_{sa``2qjl&Zl+w0{QK+b1dNBBBzpR5<Fex3; z^-KB(^vE!q9kS=t_^b*xF6E%a1-4|EJ+;6O=*=yNjCZYws|A5XK@z*xF(|~nHM^p` zpiGWRXeDV{R!l=%OK3s7OsU&qlH{lGv=Xh~lhK2P&?(Ttqy+1>1i|UMh@-B6HCdbT zb<01ED^l0r3hukLL)f-Z!ZG7%uMAzo&4MkS79$_??K50g|79IvRWO4_-SJ~lk~$y% zD==K)x8Lpt!Vwg^_P5iX12a4|15RcH^;ncY@VQGrIV=+g0SN{B5POHT0DTR^og_kH zF=VGHCCZw;PATiZt=>170U?5aFku6wcJ2<ISYJZJ4M>QB+bpPj=@gUf+Zj+ofnfM6 zszOq--?@7vg?599e&5SP%p&9Y)k*p0oh2YN_~}aUy2&-KfA_n-O}u_9jSveep~}Ny zNG*H9;udpxwirH8hsecv8KT!T)?wz<=dq#X_?~~G{mnN!JtFW9z<rpdP@0am<Q-RB zaD+nx{cZ1KCkVgFAdh$G#lumlRujmw3P=jktHKB(xS502M49m5FqjS5!Wbpt(*}h4 z>2^q)2W?^c!gIMZ{{(STDC!W#$#MfC5G5G3TKc5@KOu#%6;#R8sz$Wjey}YRtD)uE z1O5W0HJs!fHHuL?#od0D<)$dTv$MGl;nonM=+f)5=)>d2jj%(gDZ!ByNrH`Tl@4Bq zPspxBG4*$&f)O=tH^ri>2R3U>-tU6m-^V+6_e<%^<k_4E>#Ts6c{=XeU+}Qz30a+O zVtlSAkJyeffY;A}?(pVs)`J*V*>;ny0H1_~ty>mg<C39Zh@i5<RVe1yPyZW~1pc2w zNu;1bw}%YOvd;a1Q_T}%+JGQ0oU@`IFF-}7_p(ud*rn>TFEVj>!6G;~e>1_fgTJ_= z#>;W(klD*&0|!yFeK)M{86ZTS?6jJlL!_Iwq4Tc0x}EH_typi)+paj?pQ$Dr)6@y1 zrRO^LF}dljAPix69cMs~IrdY*dX?i4Zz95e3A$1|wcJ2?iHdj~h)=jUV=~R;&}3}Y zuFd7`PYkZ%{({D5CDJ(k4IA7E<O&=xABqQiLpE5cHG@S>Q?y|3%D;huY(y)DWOd=B zhOLQ7q)cJbXFUed3Q1TN2dXhwFeWwP2T?+ibV~DaB%Zm?3Zdij?6s9Fk9h)WNV=7D zl?0@q!^Hr$(vlcF4ORkN803Vpf!Al(eb>VzcvR=@BpxYNJQPUc>t?z^ro7N(z1#(Q z7xc>B3Qv$uVp38kD@@*4F$pXk?oO$KvfgjvP5x4z-G!UR;M3K0{_=Lt-Nw{v<Lh9x z`=bv_R-+y&#%+r<$+7FHwtB)yIjI!vUpq_VUpot-tfWO!s9gzHa$M>cfRCl^P+g3p z-CW44v9XRTE!T3cWep(ZktRmnw&EB3T`QS?t<}4TKs2@fQEDd8&@#-!A0Kt@c~vf` z1GixKOjqe?$f%Z<*=9)<yx6PA9~rA*!o}FuTo^`N{%w6Mpc=juww~{!o_4(j(L(J| zqDYsg<(WlC#@Druqh=(H+l{>@LwSs|^%=tK^K-|PLz%urG54Ia(l3yzgKIQ#%pRjS zn&=5+^*BC>4n0aYJH4Fr@I>>~4q=um-W=aioFab;7?~DJEsVnC)_|ob&BPW4un|pK zq-C&BqByH(fywpb29&_exNxv&Tw*@QQhku+$j?)PVS%|FPpdoYPJyMGC*-UN4~=pY z6)j?fG@(UXP+UV7C{DD>?VLS1VZ`I`L$TognS`XB8zp@-&aZ5T*W|d8zMkRoGg?@p z&6=U3VN4%NHSTc?%<sVmm@#XjUWESXVUe!#q6dnB`z~Z%8zu;WoH6n#z!q&m=sKRq zj;0m9@a><qQ?8ipt(hg5yJ0k}>2F-3Vo}ILdq__cu<vsy8U2@K1i&rgSU|il6XB|x zS37s~E-08iLi`A>b8>HLd9*+vNsQ8_{-Y~^j>Wt=8?5bj)&M*za+I!%T=oc+9_!77 z*{%2U@+)(3`E^|#%Q>PLo&-^lKo53+k4Q-yZL`PBg<4b{%*n7Lmi|&`gv{qa++7xu z(@-ewRRk@sgLUQ2g%U=uFZ2YA)WVj1VeJ85IKGB(#I&rNy;@?y)~pdL4X#m$!X_jK z{yfY>CZvdCW+$Z{<U;Ng#-71PG!bO+^pBES1}4|i0pr^{3JB%|%c)vSJ!oqWaa>=- zoYmutOoTiYrMN*KyxWTeX}~D$(VwTq1S8>jEENS*H45z;y`eNC2?Z#Lb_c!c#dhR~ zPbM)B?;?*dX?eYjOiOpw!mSaS>;tOo?@ZZ{a!;FGoh#n=qR;{NiBgyK&<f#4d(Zjf zXeOl=G)=PF!X_8pXG;qdi7sT(!SD@@Ah3ki7rXN?jgw~vvs=FH7qILuBezv5)5sqk zsKRyRP9MPT7{(4`=Osc(bo}M<M%X*>h3F$h<w03kx@-wcd^9`#g|y;9J-+{J86o9r zg!X;aoEWbDPubYEt<oNRy$7E`5C?tH+~tvGS91fb6P|(O4beJ~W6z!Aw&0A>(Jxnj zWW(S3qW`C`Q)_~~w^GkrRUwu>z(Z3ASgT@eV_wm<`#+m0Hs0<sfFKaO-z(#*hy4MS zZDy#eUN6$Bx#P<3J3?t)Zk8<(p(tGv=~9&Mh3tb;eMY94778AH7BYCa^KHOKi9-&< zV9EnKwk%cw&(#d{mwu7eqLD!bd01e<bK0b=%Fgo6HbY9`vC=8i)XIEgqCuC8_bf@z zD<B1<!KF?FB@jf9Lz3qz`6CX_{UJ&wgH4`3U9q3FN$|b<eKPj3ertYghGA9B+SmQw zfAKBMQR~gI`yRrl7bIrOo7QWv0#NbX?;9hV=Xw!^L!GNOYuQgSofh2zo#^UQS~U>o zDcf`^-l7*?gh?D14}h!cpOqpsAO-)Ya`L}hDf*u>%Ey-}oiGz}BwL>NOkV4}_WXl5 z?bmBQLqF#4H{U-$r0`{&;k3diyKT?IvOrT@2F1Sas)?|-PeemY|2-(gn-@uO@8BB% zBsS1&?Wl8@i20NmRDYL><ZNfHkN*Zm27Q?dyg@xaM-jGGd58D_>M&ocsFCD4An5D1 z6hebNK(&YLRWv$#3FR>!PDc3jK?<`MWiXQV*eTKrQHVG8BCG}7T8O5>cncG6Xbtx3 z0b{iydTwaP^a;30v^pOB5h%IfqjkB&L#SXgrrKce5?nUUVev0&*#1~5S^8o~xU^Kt zaN5aeSOQ4qaI465-DrFgZCT6UnbCPv+f^voQ*lq3huyksq93(B2d#WptLnAO_3+A! z0!_bWwc9Gat;|s{Hl3Gt$c_Y=@eF%Rd261GGEk{r+`DJGJh$1@5#ApwqL3HqwE=d! zQ^=4;Wz0wN(<t7*A>=Uu8b;bD2{_b>xQ*=xl(3glCgd!1igiF4FFd=OO?n#GCw%M( zb3l&&4W6p1ZAdSc4?2s~WPr0Yu=}ZW_-~`qaYN%zaX=8U2M~7uff7tY5vkf_zM}Zf zr1y=<pa2YT;Z<z><&gS5HT;!~G)(5#01(*`fQ=nctTJy1930iIL2XV^O$57_jt9Qh zwhDZpYxzEmC{a3iW=&t4JX*N`KR0i?@%-l(N1J|TOUZL8rmvgJn}w^K_u+h$-JoCK z1?4B}3Hl$7RVnSH<8b6b5;9us?#!HudV6>g!MEisPt<G<30Rz*nmWVEuo^<J3Z>nH zncO@U@*}9GDsWOYy3mQF0UX2**v%<_Fy|9lRHIwq%Ly>3%)7&P(s`%D#yt82Vfrol z!;6*HwhC$c^ZU8QMQaB4G%)zE*nerK<FMk3C<eHI>=(3IhoU%+6jPg)U!cBT67RZh zYJZ;3cAM)z-}ri6#l-MOwFwX6*J1f)o5fTaYhy^2cqiIEo=TM7ccZDrl9lTC^;^eA z$i)p-d)H;rU0SZlEZq)g{nr;+U<qyR_r|j~XG23YhwXeloFFJ02hhS(N3i7EE^NZM z%(g3|m1vNDZlS0!{Wn47|5gh__e0*0Ivv_quT7!YMNjPVb&hIx1~st0okpt6&38a( zE-vS}{hIJmM(;ZpG4red^MGQ!ScG50m>^%Ro2GV*w^3ZPRpGY)!r#jq;XBFDFxy0s z4uP^`2d}RO)}6)zV`g?gya2iG?&qDms5ffhS)-KUJXIWSXH=eKEsYYP#0p&Llh$Q* z`XHDq;hOCVQ%V9|R=&k?#9W@-fRBD8^QjJ6di}|A(U4NG59Q=b8r-!s^J6{oP}mwj zZ)G|@$T>8C;WEV1WNs7V&Gyl&k=Kum69i0;55`0n6>^;l5D)sX*8pDk*?znP#A3si z0IZNDMqoK<;x)V-{f`8r2QWSY+EKUH2coaM@Lga-_12p;?;P}OQ4uieu{z3zj_R%D zH%9f93di_^w15lF7S&1vi)kS-Nf+BZ6(1$1hl^yvycytYHg7C@2^xpqYtFw+xx+?$ z2NpQf!D-?qn3n|MWP4zSBGy{f?zFU1=x;RoJ(KJ6|5#%^0Xzj^`JX-oQTrhYqBCJ% zp8X>dbX|nJ_41R%L@kp1)QjpAWjpvC;BohLv6@qrpJ?|znPqX}9L=vcc)T0N1`koQ zSJQ&zZA)$=>SqWN3zMW#S1_RjFwS`;F=dVMvKHeppS7a%?B+Ybrtr0!cr}BPv<_&l zY}aS@{Aw^|&K*g4IFr}(mn0{jAkH9$h5g7lh+s~g?@tkBa(*CW`hm7bi;)1nHfRe^ z8~V@LOqr~oyi8=)=9;T?NfP+;bSju;v_p>TJLm~Ufnc2!de454HB0~;CM``ErO^gK zCzV{4CpSq5<l_36Xs)o1u7eZqR$IuBkcP{GpLM#Osrx%il=Ha=X?J6@;_{}g=xux1 zoo}$x&3155lFqea#o>eU%&5F+N3^}eWzfOdpZU^7%IDU*aA-jtjlSBy_9ZnWHJw6o za7UGKr_d3@CwV=m<D8(w4YtSeGAp@bjB#T+`DSPt%?Ofaw=RUchw8S#=}>yN@Fn`H zOl-9iXKi|&jU3$k51jdhCPnj#J*?cuZ7o`2%R=172=AKUf5(r7&2eR`lYcQ!#_Rt# z=82V~v2(2QS;oGq=Nai)|Mhw0lxa$*CVw$DNMnxRIOBb_<yEfT`nPNA;ySOf;%;6= zYJ;l%RwW<9)x`J%c%?SuC}|lPyhXH8U*R-$lL2DR_jiF5*0*VK@+Ra&p=up&EaN7X zRk$;w58ZrKFCC_rF6Gfc@Neb|y!i7zwc+Zws;ctbbq^^>lV^lI5E8CiV{pD7#_z2G zh6&|`0hM$U7&|R&mm9PpEDb|W0^7pDH8LT4-f{maULauS*?04u>pG)j((ZiOY}08{ z_Cx+*+h(U#m8Q)?eS_Il7Eif48E4)4l<ZRLtMw&zV~t6qVmLc{vcwizRE&*MPoxMX ztq7<9!y6-xlohrgjoU<nbtZzA)unI*MMDWL8_qv|S#BiSfyd7cPyWkD78dxU7{OAI zU+NO{t?Jg5qaV{yp7Cjaeb^5kM$A<uP8fdUwQ$NfXs_t*g5Feje=*49+Vi-YSTui0 zbeDuo2$-}O3t`oBr4V&YxAHIb>SW+6Ol!=SCq38a+3hYfMY_DO*obfy!BH^kZ#!zH zseupg+aqbd=3@w9?$RVu$XIPqC}qXGsx*j2-5T^P{8l|G2HioAV318Q(faOp>&jjB z8^8PH2?M)gUQCuWo8()o9B889#Wf-dVaC;Mi)1+bG^&ag5Y*|CZ(J(Ws#w5_lbdv( z(oITL16Su8QeaAFD^1TSJoN-?(%aNg8ZjrUauBP0=6}DIt5@}`Ak_+!QKTXwm}8gw z`Yhczr+no=PJrcJzWeQz^=6sfs=qPqF%#<^BdhhrVcBVE9!{|8($o)+RVkW+Z25#< z+|CU|4&4N}bhv#+8e1PT#3$C-eP6F_xo&ROLNC?8Q@y|^?R?*T>6$u1l}|p(>BbD- zrQA=vJS0`v@mIl`eZQItYTz`>xUO+W^I{#GMjlblPIz7lPEADFx#sVak8@`bB<>?0 zD6q+)Xf`D;I3f9Rn{0^esPaQm%0=b}M52akEt*%DUL4#|<Kr)a^Ek8;(^Rmy?nIlK zK1n<E1PB3<LNDqE0T50C$PX^kkPKo=S=t1f!UR?ZkLI+IAhvXBX_~m?&^hGJ`sHGO z*Vk?6251;;@EAJ3_Eu=*U9X*`Op85WSjbj2SJXJfK_#AzINVDw4Z0i^VRN2zFVK<@ zQ)<=}cf@Jsq)57mR)-@wZ3iFXAJ}!~ySq1un-aP{rph}LUOs-x_i?kB%RWo@I<AWV z3X8R-t7q8yPhV3lBR}Xh5+yC<RGJRreLJ<miqJ+_TE!b1`3bhKMAy?5p!hpWRaeq< zwXg=9BS$wGL2x1xmxrnEbZm{hM6QZsPjR`d_N#EDp}%kF<;Bt5;16Hz_M@O7%8H|# z=}0@BUw%IjH!h!hh@Qr(l|Ra~*111M&VOMp_xr?w<rukmQngGgHM|6EYy&>Q8JwOf zIPjBNHlj%vn8r*EQ}Bf_`53P}6>7VHo&(mIUWuFn+x^OdvyWI^$}uyWwu4rOs`&S} zm$Ta%PZ>Hgkj^$Pv}E*xc(l8!P{|NGO!G=&`*nZf{9>uUxD=C95o-_Bpe3jtMFvX> zWGq3UlSL2<XN8q2*NP-2Qg99nk42rxF-~y&rKM`4r8HcR#WBU>ua~DzfrM<_4bM7^ z?ax9vtxQiS>e2RelDJ1jJGz{SKUexOK)!!6aiVAu1GkVo?f3)aY9xZ+z5JJO0^9(N zz}gqjsMP}a!wq0KD}PN@nQp%WQ8&F-J^MjlIWH`CNMKipPB?+y+{#>JAzDHn>rU>t z@!k1g<p=Oz;OB7yjm6uz)r!rDDb<YQ;W-D<*K>lLg0b(keYPP}!#j(eM;4^B(%<vx zwE1T(9@-||t{@H(UP&6G-AxtO*JP!+&OK~X;NQ|{b|*sD@tX#4X{I%zYY6zUM5T1B zp7&`-MTI&=DL)uFr(0IXqn4z0KxVjnXee2#^WXnkS?{<}#wm`=Ghd56t?URUC>XC< z$Q}%t^S}o_v{3r_d)d56B(yO(&L=<0?c}KS%7V3<m(8NeHJ{DqBwr7Mr<h91N0CF+ z>?4+|G4DT<ovtgVvSZ>C%b|7kD3B``xhItRNobSg%QlR$hG4Sv)6BH~5^P}~VwV)L zp7o4sj*}3_<h_m0)Su+vZa8d)bmm-M*4aLPZMg6sIW{d{4`W$0PKPv73F=LkTEYP{ zC^A`P#9lC>>74+n|KKy{zv;XVV5vIab`|}-*GVXSmnq*#-W$UEbc@(^?663F2a!Vr zIp8)>uq7fY)fv8T<r<@i7eq7N;eF>4tnJ?yB5|bS*W7P@-Y}A+P+C$NXV?0j$Xwg_ zCylCSFWr@h^hn4CvS7LC*CLTpl(KFEl55gP+!_yjgXDnUh`8#aH<FYYj%Aa-=nuD& zq*ckG&BX!|BnM^OS!Yud95T8M<sn4sd}g>)B6f{C$QUgjuc8tzI`*O%%7Q1B#|ML` zOupf*2(V62&DAENG5drbS6dRc`S5YO6Q-ZS-cNnD?XI7Lp6n<!d<=EZKwZvD8=6pP zC=SP|%$#)_LDP@w{xJRjF!okab%o8cC@kFF-Q6WPgaE;VyD!`&5Zv9}U4y&3yW7Go zxI>UYfV1-NJ;oj9oV)M1&-3x??pa+`-EA2gkC(k2C}aM;<x6Qb*t;n%kLO+RbQ0(4 zc~t3h=I3({-GxLX<hs=`-RX7_x)BJQ)aQlOV-a8-0(X$;SI;pF!0K80(2}M}*GfQ$ zSz|#EbE;5gBuI!{ldH3uZRRR!V%$f*7V+yWi<HmNlPOTU0>x@sY7CzVQ1Yglr<>Hn zJf;T&^2OR@V2v;#8ohoUh?er<`&X~Gbb9Qi>Yk;&U#&!t@p+wi%@W`BG=~1&z2k8I z`uO|z1JRFn94IW9z=SShdo{kMc&XY7fiJc)F#eGfeG|jf<|tH<M<MJ}6k5aPpM%V8 zPk3x^HDAB^K83KnQuR5yDDidwJpCy*n)7$=0usCreBENX1rH;{**;iYKFyjVz1$56 z0^gMxf!uNYNPl)}Ytsd^f4w7eNu{aiE|&)mn(a2fG+6J?NBaTjI_&8Qm7xTvsrXb9 zgffnS#rQ>^V}mJW)Ux5#8ni;zH439mNMqKLC95f8N-0&#H|T>HTe9tB!c&;16(+g= zFu0*SQkKjxVzT$$*Nrnk2BPtzQ@Nv;C0b_1&sV76E{v4fDkcI{f}xY3lT^-b3FKuP zFsXI2<q_575fxi_96LFBTXcm<SJ*$j7Sw@^<`)}36|C!KN3`@Q2ED=BbD@mduUXH< zN=Jl7Z(F@y^%i-=>OO8(A86z~o@}BS&U;$cmacxU@p}pZ_B!2TH!;J`y7vbUcNp~> z(kOtE8*a_37MAqVOGj3nho-RKAkszKERW7e(4q}p*GZBvR!BWoR8PEm7s{@(U(19= zJf^{xcz%o+;(3Y@_(wfS5{ZS1iI8Ah>JLT>YO{gkcg@KSR-b9kWyXQavvoOWbt}!r zr$ph8Hj~ObQ?M0n+#iX)QsFE-#9Y0qiOK|M70Q6CYZNC#4-pwcRAg1Jn6gD>j;!I} zRd2st>N|-0_o^kr@KV=Lel-EKwu`qKcm3%*qz1XX!R_WmbtlFgr7U#RTe$&NE|RHj z-3DTSNd~$&fCssB|MKHEkKlDyTg&+(QvJ{C1Mu7G=5fG1Sh}O6Hg@2Zy282H^(Bf1 zHyd_1EtEqY>uL8AyHwb;ek{Y9;7H?GJKCz-B2XK#4|L8i&vx^9EXR(PIkXuqE#z$p z-b1Cuh_-!Q^wr<wH8k#FTAOObKH@-S&_6giH{_^>iMbp$;#b!DB@7$p_}pu<&dFg{ zTJxgY+StG6lPT(H#)PVLqS=Z(8>gyqDFz>D4X%-({5i)e601f^+Za>`UwF%Tx4MyC z@Ui+{zwfM+AT&MRt2zUP+F=8aob$Qto%z;3s+!I3yRlYVVXOnOh^m^GV&6iJe@Am~ zW~$hf4dW|Jhm=$HFmk9yKvuO=A>-{Sdybdt199!zJI#g-Nz>nhCbLBU9`B_$z8y)% zSnPa4VRE2j-U?Mj5Jo7;K;IncP%A3cfY+Q<^z795IXo7P;ZN~|t<)&r9C?$=S+2wX z?_Dd^n`Rd^561xxKa(Kwlr*xIFp#Dzw5+}kjVvImGPm@RCMiK+BYl!eED)7g4Zp(J z5;@{tvG>Ec?#<2asF}@g-|6@XS#Y83=Y1yEeXe*Rx;J8xCU4kG!Zwziuw_-1knX%1 zjpC+D5zmy`m1_EDpaoWoBp|1m{lRk*q@Ersd~}8o_S^D16YL*-H#+7IBqj(96+lG) ztq)$S_j&60ah%rO(nXAWFG6b{zaxfsiFT+!<C+>#zRWKbgFbfKi-qGl?7d%MN8ax* zGG7gZ9%GJqE)50zr!JL0YSUYOLB_DCezy8m!pWf}qZXONmGlv5bvnX$WJR#(ysAx{ z?<m$9+WOn-U98t>r^9vPG;X@vW`Izl@#n7<%Bny4cN?>r2oRJg5CmE`VWnYn*iniW zwO*vEJ!!E9Zkzt)mc_NB0kl0huazG{mz(pit~G*nx=^^-IWJ?`S-KhuT0JCXng<EE z`k!qfnzIbJXh*bE`1_obA(KU@R_IyEjtEQ%RN)5AZQFLo+jp)%9Z?;l`n_5OiEi2b zl6j-k<1(2~57EW7cZaNG+r_N|<xPCrgIEf!Fv8J7C}OPsT-%^oSi_h}cv0Zp9O<a; z`9$udzP@}RY&qCNQNj2P*Hk+Pxy`>}z4$+6T~_e_D(m@M<R&^o&$Ifi_W>*F`mi$+ zc#*RXaW4mphMoDOauyDDS|hC3Sn&9G;P^RHNk0ntb&^Lw)~KZdi9XImF~yq?aUl88 zLE<<3{hTL;NzvDR2G1coSk(sOQ%?(1s5l;n37_JK1jPFa724H&xb|k8ArIsvR0o<1 zmPq4@a2$c{OiViNz1TPaZFqzUa*d<QWC4LG_4eThxV3bTdg+audD}-xa{LXlF-plR zb3IxMwzCRNZA=h>m4oS4RvQ^psC|UZSeR&#OE2R_468#=7Qvl}!2Hu-0GPop(M-Th zFO^4#9;nV%%QTjy`oo}qCOmG-{AhUJ1iqW<r}Ngt=;`?FhyQ@n5BYN*Zw}m&)@o4f zFl;!r^0><e(mFg*2rsS;?F0b7d$EC~ljCs1Vkj|hlj|N!v)PzMfbjHPXd-wzIPND1 z(8~3QV&krQZO;8z`<NGaYLczB)#Xlf6iE>&nq+7MmAKU6wnr}TQWl*dAqU8%CFm0& z__nyHVMoRb6y8pF`?c<rcm2SeN=dwNzIISc;&r-sf6I#!WvgILs~Y;LZ%G|BzE$TX zlZTMXe<DRgTKiurJBAQG@oE3(&F#lUJNhbwwBxm1Id=gQ)=2^;1==`Fm*F6-OykI5 zTo4cw(I2o!o6$`+z$oEkPv|m;q=3Y`v(k34v_a=n@=dryq3i7B%1j~L0Zu_%(Be|D z3uE#|9N=pWi(nGXL1@Iy5k-RoK}!dvRmx6mG)%IsY?29IX7}i8j(|(l^@N%2Ip>d8 z1bWWllIwD|$3-F`SR}-BkB;U$JGuWP|MFIJkM6?Dwif!wpbd$L%5ea0b<?o^whk*g z8Ny8~%F^TN<(MqDfs{?&scPYCJ!_!@QXn*RtGTsSq;{k&4<nH8sW$!5&;||NL-ubi z*T$YxD5MaTUud~Zv+~Gu!f0-&5J4WCvZxa=C~MYXB!SZKO7u_K(`-kDlgHKo3!m8M ztk4xVJ3?DQjuAr60b_~baP#`_xK5n0Z2f5|4(E#la4HT#x;;_6`zE%$#=Z2y{Y%C& zvw|-ze@|)%_$n?U2#9V>JIp&@y`wA1t560oxxVN&Q7}O!D;poG3+-+e%YQ(q)}Maf zXs~-bPtEo{#_r;Iy)g{!K9+y78uW;p^>rUOvtnumO5w33&SfOwt#0O3RHBz=BSw5q zo!8~3Vo2%M*>*dn=+e*B1Arfwb#2nWO13lejT7`3S5o*dp%wn;gzf`D;uGx3Ya$?b zeDCTW2s8R5)cLIW>D#kyobod@<T;A3_XBAA+mwg(x_~^!O%k-*d%RIM4NWEMKq-b; zc4oo&CZhwahH@r!*WsikJ5O*wQrV)Lhv)ngto$n|1#5e|_3%NM&?o!^_H(rV_*`6n z4zOeT`p=&YQ_~??h);V#k}Yp^A4diJZ@L<>+6^dVnHgk!1{=HS_0#eIbh`Px-C>Yj zlr2Uu(4qbsH66ADzQU@EsOOCg&Yo=NovF0>SwU`bA^|dsdN?FEazAkn^?^bX0l`2G z{(d3?$2(Hx20cr*(2n|M^(SK~ON8tu0<}u8WvMfB8Koz~9-{bAZdbYn8LU3?8i56U z8~AqeCj05s=<yDxJrC}}ZV8lu@Ge2jWgyR{P5=+2$ptP1dqyZSf_r^zch0rFuGb3I zeXsN4FSJLZkE8AB+Vky`^+3PPAeo-;U58El<53QruFt<Qf2WI$xKL7V)POe$WVU0# z3GZg4@hX+CmuslQ=<;&;Bi2((ni~}rsJkCYYDIxa1wD-uSlkNuk9(~0E>9I5gkHXV z>HVR-sQu$x|0gpfUZJ=r%U?X-0K05Og?Ag)Ye7A~3=bovGj7^HmhW2R4F8kmogI&u zpZ{ffx9O~C4d`E%EBwz{4grLN;M{KT`h^n&LrD)^sj6W=ipznAocnrzKH}4G4lh5u zFSlX1v*|WHCRqfwPc3MRJTtMGWx6d(f)eFjAr+<yFAyuy_al@y*a7w)do)jsezz-G zBQ*l|w;rPgq6iqLkmIsYqUfC-IuIq66o}Ns1bC-3*KZiez?I)VX|b=T-NplOQMMdt z#D0$^ZX;{6)3NJl2GCGi%mJ4t^|K&`xGC2t8dj39I>`Q@k*09lBqQ3Ha0w9#8^UTD z4l&{;dZ+LZiDb`cM|e{5c#IHCFP8d9Y%`Mx8QAWAh(K@2ND#Q9_gkVkQukIlMyj(# zs92<&urO&Xg+yafxu-E?mPu0}Ng8RRbKQ{uWIObOgKZIMOO_6}(v!JT`UrM)QEK*J z;MM`8&D)L!i(agxuWRSD@8H3RdeH8?6;YiG-nJn*#3Z-89mWcD_);ixbTdp70D44A z;PnE~x<{HzQ;&F(p9>-y;*sZu&MISxwMxM^r)n4u=mM$Rik(}_)3V)M>)rfC%2MEg z_&YUI0b$PAQ-Ln0Zi-B#JyLhsdHV;1<o+Xc+QQcpeAKBU_F3ZCLS)Ts==wU{+vRNm zqw&*}b}QD~Bk8P~tucSJwqic9Hh)(`T2VOx+FtW*g?jU0C2u!}=D)DeS`y_i1(%0s z+5fRTAVP+l)P@&?Zt<|E<LEWr!c@(ly^mF0>n#kS_18(Ae`;+)vHUJ);WPj^a#-UC zRQ6DC$PRJF-+#~{A4)`oh!l6@Y@r|oFOD940BYp^-nAv}@faFr!S}ee$IklO=ETmk zBG(j8Bu!l;)W$~@jG#O9yQlgZ-+S&YNU-z?zXgA%!e{_4plKZlM2~ORCa<S4bLZ$9 zxYYg89o*U)KIK5v`|<<o*gJp6UE*lGQa!6~8Gpfo&Q#(i5`o|3C$L}r0xlu}ldf2N zdRHo@RH9RWA6l_6upE5U4X~KZzfQ9`AyYzti%!eW^9QjQ#IRx^6!H!F9c#2n<Kt}u z<%vDqANQLUSw|txtE-+yE3z@&cEs*Ei_lI15WJmBKc*#6e%b;HLM02?>ZU22VQ2C@ zjm+<9YF1D1HV3b1w;t7V(ELZaCrycbS8Bc&=fmajx3dA;@9<ysgm4pBM94awrev94 zhX2yxX_&oW*%UxHvuFY)kr=o%OPZ)9ar=tP?Aiv@zVn6a)a%%uL_egV<$NUBjF^xB zdVItjy-YqdY&C*Vz0Z;S2ERkQKdF@>GEY%!!y=ffY;>zXx<ypd_}^G-OV?^Fr`E3j z9?|=U-+{^>zwljS2bMb|Js>qF4aB~U!$8Bxn5Qh@JI}7m;M6x)%osJ7>7<L#m93El z(o{_jAk;nFe>!Na72Od<5hOCQ!Y$O!F7q#Ge2UfOx0y!m>Kgz-yFG@DC<d5I9pSp0 zFt+2u*B9&g4@^_1N7N8C3WP)4#$ui;*IQrtUFAA0nH|)3$7R`=No2}jw1J|(<f2A# zjEp79RQ!@G%c>Tr^Ma!F21E3V&q($J=d|AJTvHCleEH{fHSJ2XrZ=fnfZ`!FoTHw) z=$lOnq=fQs1~JzrI_$??hIpSWD!Ilfj*tl$qOEuu9mR={638W-C}kNC(&!0Z5$dfO zOeDl8dDDfR95Ki6y*bG>U2#V_85-z{hR2^S)QU4uG75twbmXf5%e({pCP}uXiS&7? zC(8#w9BU#K21arkW;EV8(fxWg-tvJmKwjcSythAMoU-21Ry%4Ia{iIu{#nZhA^YXG zAiUiGq;tp3JLeCrG~KY~8LXDfS=|HirjgKSJ4B`9S)wa92jA5YAlhj}=mn{?KGFA3 z{*A=0!OE$?R6H(4c?K%)iV)(z#uHCPnoaM&atZiq2%~K0U3VdDu3}42=8#+T(r?@S z6U-oJTEMX~XvD!*?g4Ug+T5@H6l-v|u|aE+RU4Nrc;O-}<<o1p=n*$f3TpA(Ost-4 zy%_0YB~4nR%CLr*``H@oGEFbdnEz>yL!n*_s78*ff<UWb$_aFn11M!%C(B9|n>=FL zuDe`L(N{<E4ZfcBE^jU)AD}24X;#w%Yg2Cz%T~7!%;s`Wn;kd$$*A+08n=<vu~lpD zS1!AnjZbUstp}ePula#vud}{{8?%LI-p*$_w!f!=*nDe+T}{RMVb~U*Rc&T6%@B`n z!xG?i$4pQdQ)f(m`pilK+xPALkOLnxRp9y^Pb}VgbF!kNIbeTF&_)|OhRJ`Z^8n<K zo1Y(lfzD}jI2}<%$Occd;ccwcZB0W^p)>BBlSkL}r(l6}x);<R(^+Lb1s00|`a~aZ zdU=9*pL-%*i@e9+XSvL-{yvl5QL^97^il`WJfim^)GyHMVV=8|j|S50o0_TNM6#v8 zPcKGGyNxBPq?Jh{aO&u7ci-$nk5@T!EQbf7f2#rjy<<Ve;9W@Z-{gK}QVC6nSgIlE zXL9CltJ?5n{l-_lBy4Kad`#c|?f28F;O<9-*Vi2m!(I<W)JsBcAXQI~MCZXKa}ljg zqRvM1GL|`GP5v>N(Qn1)A%w-oaQ2VCzXGo}1s=BWn!0X<TCL%cOq2(;$BomGdV_Yj zk@gK-1JOHa4-R)D%fv5CUsLb1MBUe(KRWEJcM{}oW?(ug9S3k9wYZ>-L_<&rpr&YR zovcytabQ4yLq|QH$><KiJvdaE!2A27Ox`*F+0Bly(=W2z7nQsYR<v1nlRH9YHL=n@ z7&Ia_h*!uK(JzdBYQt*qX+Y+%xM5r(+(ip^@CL~Qqo%PTex?_zihfzi|B4&PrPBRn zvMLQrW4pDT13e8&FTJ}S>N>s7O6s^@%}k+0-+i7L-S+Q}Ni*7x7Rrv^?m{)P3;bvM zdwf5?F+kBi5Vbh|x`+Xx!}b@9e{vgC17K&_aY8QLsJi_=JNfA(J(uk|dmzSoA>efc zystyTRm9kTj;d5RMOV!P?bZD>{=(QL13S7jh9Bz67Wvf=7CCe)PJiv?-pj(^?qcC< zS@me}Y-DvOhaF67Wxu)^!4PrFSO`-wMHLCZ15#{2pJIz)uhoQdTJ&hS1@d~*UJvQ0 z6m<b%sBc7|xB>I`+j0s@*}HQS>X<-$A#^J4`kic1;m;F5Q;Q{x#@D6nKxx%dXwHb} z`VQ`*BYhMNKAP;6k?kT%JxK>djc#$QuUQrA{o=$%^%QcpI>}5yriqx?CeF;z!ZyuW zNRnccmbjhUB);$IMX$~K--8xOGoqHO`3h~yvmhj@zhCmH>Q`Gq%_3&^<L5@N#D~qU zMaN`*^RwMPt{F55;>eDBK;d70k>#+vNi<)3rB-c_5w;NRt90PBpR9%ISn6%OPIuVi zgt1TuuD9FoX|Og7FEU(C>9MfE_%~q3FK3(HF+dLcFT+2l<GC~nlV!!sdfooP-|q1_ zP8?ah&W?`r%gbmseq7ISKMe}#bLSEZ;3#G$r%zMk0`dpcl5g+hLK!dsEycbzY2tHS z-JAY49&OBEh>AtxMj)x5tKzhR{Vq|35E1Z?IR5!l3!eQE8x4QxO568o=j+V2<2#m1 zkDtfApd!)l71i86hE}=<hhYLlmZ+Ahd+;$h?YU3Z^d*tbdwQ2@*1wA*Y6$?HF!4;d zTSaTIgz1rO7{`vrfxmyC&He#PZ5%=0mExqP&f3b;oyjD^(%SDDRCX4e!;!gVmW*3z z`g|C$wu9ZuM=3W}T^;(S*kk4T<|VcM_%|Ni<`8bqbZI$}BRTOdb2(KU5{lQ^Lt|;{ znFMsZz0Cs;Ht)oHqc9q?gKvG4znLiv5P4~4jkBHbv@JX>m2}au&8fE)Azyps1dCnd z5LZeokH91_875GsuE4sAtEv!=YwlwU4p*ct0D?YwkC~#XS)t;(_QRS$qcx~uHp6Sv z0~j%helnT4Bof9`QsdL6C#%HG8YQw(`bgdp1wjXTX7fi4!-6*c#3I;?uzAIf1{8Se z{s@?eh>Xlr-wo8UQ_q|ZxWzB^P72+Oe3O}rcWSEHD2S2^#gx-qp14J8JnjB0`;Zvq zl6|^fy90t|frpDHS!l$tF!ehUU~b6q`uOF_XNRs#*$9NC%F6s&%$b#HGidpUWSLyA zlWwxiiy!?Yp{4nFksTvUmh&(jMd+w8v+<26n1#U4Y0%x9CSZ`P9yK5`8H3%|T#P>` zCSTtl^J1OVC$tYA(lk-PSNA_H%V<owg7`mdy1sNdsHg7H(1&(r+6R-oOzS!%-pHu2 zj!|FFJ%}B}Qa4Hs!A>EC2H}I<$y~+Vcg0$CWII&T$ftZugvYx(y+kJv*BA7+9c{YJ z;sVjuJ)BL@R+^HklW$B}vc1l#b?F~QU1vptPbOub{-PI_;()IUZtFX1shVDg)UoQO zX8q7}rly@SY~3seL8Ae21yRx8`Jv1>JxBHeK3=ja?>4`u8t#rJU>IMvI^=l98d~sK z`#x8!cUi|u8tiXkWH?0~uDfp?1KGNQFLt(CVCnw8pUj+UnZB;{0MRu&rLz)bF!Q95 z$>%f8aR+_I@;yhe!sufiGpDoy!xm#%j!h2$N+zaaCYYF#{bO34W#2+rhu}+w**_zh zsbDmoMv441;z6gFD2XIC2<Hhz(T-C4iPm4!3aKIIjcZb==04s6GU|_~L1!{B8kZ|S zs@rRWLOMgcTnjc60#o0dsLUsV&ZmQ_t5gevSr`(HIxO1V0sU+>-4ZVnKjvHfTgBny z=&C>>j%S`@Pblpu^UNAB&+Th!W~{<Y<$l~jnTLsKyW7P}RT}QHtG><k-zx<3ZQh8y zs66xhXXV&D-A;jvTa>!qE{K9_Uq<-NdYD(+V><k$2r5Gucn?&x;BD&1aId#&NSdFc zJ4?N<7ifud?lVZKzO@+(9dSG6&VYt>>op*|>77f4jok>1XovCTCSar;R`oIg#T)>7 z@=BI+^9*a8y>;XhzW=l{v6b7S)M|(0fnAmV6D@575k4<iaA3BBB*gCTM328MwfC_2 zI_vSmQYLi!f(7ea|I`Q?ze#9W*0S;s|HQQhU`^6S5Ti<IAP#l!Ce&obEbp`6l%&?! z+*7JUl4AYyqLAdoc|;*hh}!3kiq`7-dF#Z_#ZN>CzLuPTk>2-rZ%&k`$Ly8;BEB?v zVqu^bqD;iglgrnSuNF^FnTkbEqhGnV95?@_$LVvtM~@zfaJmZfQi)5YYfr~gOryd6 z5|HQ)NOawKx%5RCfScXC38OnYK%V>bWbaHM`lG3Fw3`pL-_22atg1D;PX7z+DaUnK zGFn6dT2bW-`vFH1nwa{pzO&h2zGUet{fOapHRF;H=$Hw58)R|Z69P6RE*xjbuBB8h zqiC6k5&=RZO$SpM-$Ke1jCy``sD38cvI)5h`88Wl1?7AR6l0hX9Ai=>B)G|*r^5Q3 zZr3xjqO^kcy-SEd8lRNGn$mni8=sa;G}hO>2lSblL5x=S>$t@p)M2?))`zR^pbw-< z%ZB!rXgb!APRuOfe0u~BhU$@kFDgUtkDCG~&X=nn-7QQrmX?3}%no9S*ikAYbkan_ zfL%tS4N#NDy=Fp5KPEL<jkfcN7w8*wv}hnS(16R(+s4MgU<A3=t?i}@i`5vfkF{^N zTbrL)txs-Gsz&}~)kjguCXL{;LHu39wBA#{9j<dWgU03JR9|B%sEsUz^)jinH2!ZA z{HcQL$*9x-L-}9j5&s{#5{P(YpM<wP6l_=|<{U|Guf3#u-(^1l?lkG!aluYU8>Ad~ zvbpD`{PY~-JP_F@(+R_$8)uu9*rMQH7-inz%S5k-f`~BqV%G>oe%zw~H7Fxrk62Xe z5U=vD9>S?HhC@U}Zxgv}&EPm=uQ~fd;AQtA>xv7JC|1xZ<sX7ok8%idHb38MoxWt) ztSfpN;ighLCb0C}i>u{lnDBxFX9)GP$_QQCFgFsdt<%pn)y7XI5mdMWzHdk4*WEu4 zjpVftx+{j7SgV%NC%Y0NJ@|Tp76QqJEgxZKq{D?%DcF|}A?9pUn3Zz!k}}OTO>pG+ z%(8~W8M?qHQ5kV@*i~u+(fgAR0}&}~=v2kEG1H@_+5%g}Pg#Duq?DxNHoiUEBF0IJ zc3Vibmf>w(wes}6INE+ST3$F~=g@EflB-(vhl{|?n*3}kI_fNG<inowL2@%j8W?+H zcRDkfT=;EUL2JrMl%^a(Zry%~-kF{n&^^6Ly50zuc;Z6rKpT6dsp_CAWTLD-gc=w* zi%L@Vk6(Z@Vk#xmX2&W4AiiEqo!i;O<x`yD^7-xBbLhT}&$;Nb9rX&ek<LMfa#Yy9 zbTf}&Js|}b`cNW{YD7+Izcm4a-Wrp#CJsulrliA=wYuFOPGu74e`cm1O4mK31}St% z4Yi7}dScpkDqoqoD@{sBFo@*1<m*8IFZuxJlTkibf8G81+L<A<jT(ab6J5Md`x14l zZ+ClCv&4aQxE3J?e<h~)^Wnbmnfmd%n1n*ow@lLm8~*)@ti`d(1I!R1e98N=IHMWR z6ttf<!j8SqGFt{`Y(^M0W{!ikbJz=S73}Zcb#_q8wuN3k)$OIQ8cMkg9cLW|=WwPl z_Z@<^9H(5XL8y4OO7#RsuWUpgIUA+>O-fbx5?WDR<X<z8IpdDt(vE+BONvfAjAcT^ zHuN*1+h8J01!9kk^1xSYolLn7XFn`p>})39MZENTQ9Tq2-O7v*^-DU2*HxyO;4n;p zlB^{8C3m&ydFfKjwfLp(mfyk9MV2_dFj#Be233s^E~+Em>A#T6J(Fou1xarLQ+sH1 zc)U@%k&3Kv?&w&Ds@dDijp9`h1F=(7Nh8P&0?p)}x67Z-(3jCHWjeEXMW(Wvpm<sR zAW+O(FO)@g#@(CLEj&bEHJ(HG+}v^teA&@8{w{6{U{vb-chJ@|P;3@a9@(f8Eh0Yo zr_xCYZ4xMG_zzgo4@X;Zl!8)B7JCKrszQ{#1Yr<B54wOq!D99ml$9asJ+}ZWkxxjs ziWYxT^@GZ)F8|IUGYbh`XvX!tr$N<I{Bdx|asM_MiKFY*9q&zO+r`W#%{8tLX|0PQ zp4`pYor1n<gf<K|zA-7cezN?J@eKv|USL+?f?dO_I)T)t^9b;iWhJMV=B!K}iT7Gj ztO&1GcZIDKo(Q)eeP_QSKN}=K)RJ;l-C7Xc620uLCBCnpne{GzI?2-)L@i_7<Tr;& zRW{CJooUtD3GtonU~B$r%tIbDyBV06K2b}X&74Z56OMsS*9bg+c%^NhW~^_8fYqxa z0b}GA;u%`Ezg4j);ly2q{j5AIlMrcKO5(}U(E%KoS){~rgN0-{#?%AXRBQxK-H=+Q zf@EQ5vDv)@hU!Iiw)W5jt(0?j>SPsZOOhkugIVkuuwS=$j1`^=qGEptt|__$uT_3s zBg?(Z7HA5sz=^USM0-#0_jgjZ!6$n%8APfV)0g-Ia=XTP%HS<=kUFC6Xi5^it!38o zS3CpiRp2+nOmsd%sLq0r$T8;jZK{I2c=ws)Y+=0~cVXPo0$VBE=>}p>wMLs(w5Ovh zD~*#$bC63v-GV{}mCy-H${BG_d|%dES`FD@(R^ws!oq6n+CO6kT7Q;PBCPmnwEq=> z<ue?UlS;D^Tp&0mY`e_k3>VKS?Jics8W)VXm`4{3z0)E|rr7EFW)__hTP+27*f8p- zAySLStc|e%eeRH1!qDNL!-CM5q>K_S$A&God0@N}$u?Y;jy*-9fMcG*yhL=DEv#Fi zF;0x*$)}7EOT0X@TGB?X1muGT^4WXSn<HJcLSG_u|37II7czuy4W<eAN;B_2r`5Xd z9%>dHRGUZd{;d{{eLvmZF%4fgqxi5d(0+V-vwc#A%X@}@kL3f7jkkj5*qwNIi)jdo z3I4kyKLSusDo`%`!p|QH<?d4nn3so)L&fcc+fB}s%heB^f4uY=ZM#{P>RY%v#|(44 zamEOTW~A_+cVa<wb%vcV9YZRVr}cNf*v53AfRmV6^UOL&J<;&Hc)<GLvwV-&Cc522 z7;wcVZm=SN%NOmC=AhQ#JP0{YkIeK>|C8I&%twV#rnsB#HS#xm`Ur<2W;XDL^th%- zkibM3J*z3BQ~6839(2G!%?ukV-4v#abB%E78H9t-@WQ$p<8)nvqBG$lM3PCFR=M27 zeideS{aS$5e{u6=X-5nyW6qU@1W65R7Apv*1mH{;Ce5RlJnejgwv`K*afN%Z?VES+ zZF8W_ZKti-jAaZy8=A0EXlNrfx2CP2F@(}?mXPPMc)S19QuXr3r*YeBoNsD^v&I~% zf4Lb$$5PYPojo^XlCu!)6!O;8C{6Y}yJ#q4amt~8Z(u-iWR|E?LG3R~CMN0~Sg7D~ z+UM;dIpAXGGPPr0WOthEjXr-|vZEQOeilPkRi;l*9M%;g7Jdwx9PQd$jl2A43_}$# z-u=O5)peEu1A-_^lvg+N<ENV31MU?MHMbbn6zWm`(lrH;Ym}A4GX1yP3Qj2tQ7zQx zKvVaTOYdxU9dVx8l)4wuVN(6Q+mvzHtfrJHXtdhvwCmmhEHOB4oQVV-<%0YGvC3ld zua{9oPLEG1gLtSJE%w_l$MuAeIl5D*^39@%47-ud^!25fXwhz6V$L%JROGZ#b--nU zxamO#C{WZxJZX%iSA8gri|<W?F-Nop0kyrE7B$COk`MDBc{&?cTn-?GbYp2l@xflJ zY6ES<$bkK*akVAq`^QBO)n+i7lvLOBug)zH+7THw&fley8|vbMHjApYh%tgFTfdK! zF18)M({8-D;nh#R^ZAA(7^%zxNOEP0Kgqr)uga$oXSHz`(;9z?1PStMU<B8}?*~d# zmt80q*A*`@#Po~nt+-=)$s$G%Q{t5is47<Nei@0t@E%8&MAWY89!lh(N>aeU)B=or zn|HzGmR~L6?P)>k0T4Z<yeHj02i@D<W{6slD~Ie~0eIqh6e>+%f$9lE=z(l0srK8; z!S^|%)$fx@NU@zhFJWvB!d04$XxQ!FHIB?1)g|A6{0c(xl(F=xB13oUKXWVn08Sd= zHoXhMPFGMX6y7Iy8bXFHU}it(%T=9B?Vlkb2qT9N&KXVD)Z#}7sR*O7zPrX-xfYWI zyKA~5$0FycH50lGuIpUqhj7V33;R9-6h6&W=nC{N8t~~f1TW6d$OQHZUfF60w3^DS z3+D|W>axGGR)BI82_|#4{zB2w8QJ73B1jfz<$rcvWxo}TaM}WW{aU`y?GwFe$c>Mc z{-*%U9Lz8xy;}ja$ycX4&5wDxIi9_)>(w0gn>s_$$HV(`Pf-zzp(>P<v>T%tXE<S_ zI~5~4ZJRVXmbeI><*7Ob62b_y@|y%6H{`y;9$<}?;6E2s4iS4B_0HX^6&9)rarEy@ z!naFkND_2mVlC6+C=PT<Q>oI<lsz`&G}0pRzz};a=9t}Q=u*|K^9~+Tr#%qXb>6Zc zSp8@i<`AQoJ<6%^PrJZ-olC@qAfUcMV@D)uv;(;z?6ep$0D8*2?meb!F9T_MSUXDT z_)V{e(>1!4SHkvX7Wy1u`N$~AAr%6@``1}o)5DicnLR-leDAvH`$<DD3uPvi3{qO* zt)pJaWHQ-LrfK@xRCbcl++@v@n=A7txDdSYxFYPC@I3zt#X+F<k8Z2{@c8$^BijME z^l;JY+PcW(a-a@ZLUMVw1_DAIY;1PxpF(B1dP#(y%%Qubn|HuB4_}+urA^y0IhDZ7 zgk&-;mF@2G3%Y1>#oGY{S!Pp)xDt}{;AhdAIq4%?s}J2-vinmT+5y)wk^vp)$eVPt zyt)p1hdzSi%1FL(TPS}rL_)4(Fi)U#Ua_;e89Lp=uKT*OBm5rF7?$gD-L%KG0xSRs z(0u=jouW&o2IS{!2~Iw|sV7$1*V$kL2MOjK3#ZdstS{KytMdPtd&OX$SJ6o6kGrjN zk`h!|ydZ^X@X)cTGt^~)(h!k5RF1aQ6<p~kN=$69Q8Za@%d;ENz@87s*`{m#mEaWR z(MQk6ubRz9d8wVc8qwOnXwh%nvbt@g05NgROg((d4P;3fYXsy}oAR6xZ*-|M`^(4O zSgVitE%4X$^&fvWUcgu3ZSQWw^GxopA(P0%2ICEqAfv7gz~}}<$B+wU#*ch6q1fQa za$l45yZm;Q;cy-5X=e2UEC>twnf%%JLrM31%!dJc1oln}X|irhU_;{<x`O*}Oh@+4 zHk@OJ`H%u8TQ2Mky1?F-y#=xz9uh(9?(!d4Q+-4F?&W9GOw~;A+Bd8yY<>unM7pk$ zju^z0*C99>bjMt@T>(tW*qit^Pcw0<>IwaavPx@E9K*8uoL&<cHg1565c;AQbZ6&8 zEuduWN}-k{?N{rpv%6{Ps{F?&6#VG!Qn@YNS6;ufGXyOw$M<j=ts+kIao#22-#9<n zH4Y(eEO5}o_~k>9TJ3c&V%UWEU8o*U2R!K05l2K?LCVIWAN=@fX!9*9!(SA?7$E0= zyIASMJD_T<Od}>nK!Cw$*C%1s>o=EZd1uG7EqrYeVae<D-H$+<8Ym5PKjyT1P-IRP zpG9UX|AHulAnWST%pOIhO5ZNcB*+u3N(q91!)oNSyoh^JeDm!sMa$*FR;3i}ondL{ zwz!b(g8VgzIuxk+H7R;K`qmnDSH5Bm>mXvh4)2_iPC}9)20^nJGfmYZ8u4embF^&Z z*Ij4WUx>Ale3}Cy(VME0rHZg!aKbD941l2A*zlMV0$SXxs68&uT6dQ4V~dM)VcU^V zB-q)vb(jZ`>x}}PXl!+dVp~0q{$dr6pra{%_$`oaD3(6e*4z|AMMIh_Nj;ongO4kM z0*49^s#&G}Ixfr=+ZU=PoW&R0_Ic=Aq7nM{&*s*15uwyXp#W`e(6GA+efC8u<q8P3 zQ!5?gPy<l5%JCw+&B19h^!0ndpcK;@KuwO$jVBUQ`Z?7A#-A)+FUG$*X;(M<+imE5 zChBiUDR-?p@I_o-BdM;Z34zZ+pkh6hA=9V5;0ki$e-Ks#L0`o*61$!OOw~I3QX8_! z)T>r4{lumTjxrdKMK@B$vn7|h$(N^@x1}gjFJYGMA+BBG;36lNgx3sq-m=B};SZQ; zm81^kf~ni+vx=MPuC$vThyl+pIG4%R&*T@0H5;$?TPK|o?CWZFC;En}qkbmY7M#-R zya&67>KgmM*4PgJ@kBP<g33XIpRe&@QhXiS9Ed)sK-F~k_54)=#_cw-h^;F4bAH|2 z06F7LY6lhbflQN8P(qx+n&(1LqvHVKfKFA?==V=}4t+_7F9t6ttDuq;pnUiQMME}v zvCgPX4pg|N!^T#Z5%?OSi>Tjr<JVlCUvo*MZ)1j`Z%-*KU~`gDCGmyn<X2LSIOhzN z66>tYGH#f<2E6#?RJ9;swPFp7nrZ)77xfbT>8rLgA9->0T;{P(0Rl%zvu2rpRDWcO zn}jX%G<zx7RSJn7Co=l(4;lUS$m2ZM&vc)#DoEgVWTp18T(op!jP0rXbw`j6NP_K_ zlhb1jSBTd$#%lmUo0sUJ9)qYU4*XsJ)A=HF7C&^F@wWe+%jJ{da^1ckSxy+#VO)nP zF<3i<R}|Oot(fkHi{2i`EYc(iCB7IfXXZpB3l46LF-DK(p#^57UBC$r)BD`*b7oLE zrv`g)wZ;x~dp*?p6YZ?E9)iy4a-RJKD-f7i&8vM#4a0$2!K0i#fDs`ye^zzmJ!Gn2 zBk4eS<^~c03#?T7kN5d@Ftv&);@-~)ugei`y#5x#Jm>IUkr64ZUHG&ujgcgLxsV90 ztSl}y#1QbEj{xYZLkx>lZ%vVnsc7RvBDYr#uZdGqcPhIWj3_pt=lKuN2%EJ#XPots zGNxBrUCGSZ*KaUBCcoA~E`pa`hf`TP4Unu<7M)T+FAKne&hTy044O+J9iodrGl%(L z>ZCc0$_21~V;iEFC+oEa0M=JsZKPK^_$qJ8SSSO8Icvv@;Q_U?wvl{yG819r>p_WU zO(+3;xR}dQEf}lv&&QeUch3crqkh*dRv>O7!L=0@P@pR69?FPs+g+T&#Iuq^Wr#}@ z?MfmB5&$<FvTaxZ8oCDbvb%>+IZMV32X|`H^7zG%^zl-T@EZyHX=XxHZbpb?OQ(`9 z`U)cz6LJc!9v5$uS7qTkO-(AqK&2T)w~k~RFF(m~1=zy2<+S{o9{kU^^yB}9jFYE= z_yQm&&4DWuaU9-cUXfDNc_&U4ElB!v(muH3@{)AT?=z;!_EYru-Rc=Vnh9kr$~Uz8 zrz;<ASGE)t`(?43H!E>cit1+}yDzcqsr%D>u>Bb}KTz`;+s_xHMEu^UFES27>l~}% z6lOd225aq-(lt1U6IF_|IL!|!yAjDKCev|(;YY?MP(p^n>QfGwsVH@rG`|vnJ6jzY z`KWpk(`wZ>B2ONl{R-Z$pTi4Cwh#W(fl4R0!EveL7k}j|R-kDEU^cc6z%Bn5TvD&C z;wOEfD-_`@)Qgod1YJBBm^dfzH2Fim04I1P7;sPR$oP2kOfIYT({T<?`FlRLXQZ+k zeRm{4uLTf(lZ}a8BJZmCxpaSH$fztcqtMPkQst_kr(~+gYZ<eavP3!r3m1nqs4xUg z!FQTB64NXaXNhkawNgRE&FuW!b;eq)J-?gRm*0V~V=wtU(5_Ufep>w(73CxF%I@KA zQCzK8BO3avywyVQR~ckownAGWXG8ugnyz?@sU}N&Srlvt3i9ZnWrxhS;Q4kMeXSC4 zY(l!2Lg{R`DlG9m0$h=okWrN*Bu!>|Pp_TSYqa#0A48ZzoJRa`0-t8q1|*+@MorKv z<tjs}_0*UXh6|(T5iuaN97A%FG=+YAy}tu{&mP}hLVGTkKD(oZ&yIGWWdB)ea(#9~ zcA?FPuuRf`z}~<rtN6*+9E1;Fs}T7AGmvyxN1BR&^`hkS0Y;uJRsp&KQ8eE`-(g%D zL9DN3tFf1VPA<Ggt8KNLT-Q?d-FH^6ocR`Yvq>7E{p*vV7_m$E0Z6c)r!b76CH}71 zofLlbxV&(zwiusYd=@6!LJ~opem}HwS11Q+pEZ+*QFPNnDUsjHOcc6DvV=kAej!Cr zS>?PI;3-Dn_3|RFwUB}T8;nzC_lCaKPD<oT`d_lI@9PSKg|G-(lr0gio&I&}U(Rcc zI)HVnQnU(9-@TS;lo=_SqQR=q$v)X8#~ak!NT=kauP6;`vqGept)iLXb|@TPzyj4W zP{v#zAFDA9A8VVOf+7}47`{NA`Na7!t4;q>nMjRqPYqJigaoCMfX0hREq`5#@2jnV zP5F;=?3z_-Bm$NU;*g9lhG_#G+5-=8ZVp2tM22m~`yV2_Pno4S-)6VDT^~@ph({YS zb3K}6v0F{lxGJR~qA}Qi<!^lc8%lq5HiAPZDs*h~>6^z+$-%a}0k}~k+9m$!F>VSi zQ=o*59tLM2OQLGxsj^iwKw`_8L<v81=7?6t%shCg&`died1ond(o<)P5AGs8cBVwA zQjs(3G0-kfGWKZRms}xn-k4T@0=pV?7!%yGAUT=c<Ocb$0)?lJYrIIY8bQZ@e+!lw zd2Ez*#tiM;f?rm^5tr5(+!q^?1;L0;VnAY9dPdZ5AVMZV<%_3Ij9aIcga2uIr@1Bn z{IuN`(;&EFb+Ewnx8na2n4CG(0&)XLkX}Cb4*hnxUlon>e@rYpBKhHQKPrIY_gWq# zvT~Y1X0R3rR%sQ{zmlmCg2uy{yt4FIq>x{np__tNow-BVXn_@Gh%I}UI3^X~`9}q< zdJ;K{Z=R0421*?=;;?GI?fUSQ&ai3j|A9YUfX*RO0p>GuX<5`j2Eu5|gb^*<Nc|J8 z!oU$Vp`$P_QIcubP!W-SZAI{q18xB=>hf?akZ%&fg(`h<CYjCKrX(J6UwodDoJt%c zx&=O~erJ{KD(|6yNoU6?KzH!(dQBaj_hcwcA1gN7IWz)0amhBPbNm!+uZ#r7kSC!+ z$c{?u{(?VV{h=)rzi6VN2H!C4pO9bJG+^o`I7Q5kir6+EvWr$*js}ClbV`i@2|*J* z%N3psO26E-e2~uQ^+)ySs#e$&fy?j1YQEE=`$Z%;@IrmA=efMPTuU>{)>?8T8|<2q zs28XJPy}j}Gx#gW=GlZe^@n9GEoms`W3NIiog67#{MTu@bvkL2)XV=35j_p4u6NUX zR{JB)Mo^TRRep6#F<x~&v>9CObsRVtQjf(W(myyt>wX;QVatjDWGda5u0%9lckZ`i z#Y;GBbJiBK9Y(yrfr1U`w115XDy_QYbfk3)9g<1D7Vx<l6%=)OAxVPcW*uY<S>7;^ zfD4p2{u#+-28+D`G$qK-&LL=6x6kM&bYHGmzf%ud&`Ygos$6Sdc+W`qr!Oi0pUO7v z^e-rOYKYB3xjizgIY}^8fL9K_kvLq_2%6q?>CP2CyC}OI`@5mLQA1jfjyBJ=dLbqj zl{@MV35qjus*FUY&Db)Bz*33TKb^#Y%Hu$8bx(>V5Wiq`RXDiY5#oLY=bZ@SALP6P zK&$a+|M^E~FFNn@ylhx5c5c|}IS)Z@GBa{4@e$wO&%RPHA@nG_V~OM~#GP}CM<8S) zakoGzytO-|2I_R7Vv7R|JYd4*P~Ns+5qM;mPx||#z*J4h^^aW{5{U^MxTw(b;ARPS z%+KUfy7Dx}u-6^t@(t4?wtx1HfuVNXA4>Ey?Au4?Lw6?S^`jGi3LqR7vb~+G`n--S z(Nc09W!%+NWc4V&ROf?~=xd@mleNdK0>zlm0v-F)`;xbRigQl#bLXlMV^+in3isn_ zTj0&@Gu5FPa=e3``$Gkqd^@=3T6K&NXo>cRwhOZmb!67z{m}b_X3<jZuB&od39n;K z5Bg!OLt$Zg9p(d_tV6go56}41npyK7s7Xse6SsZP7}r#TzUsvS`^GKWMw(hyv=|c% zMCvaHV}`PIcc>zuF+*^}$;Aww2w3*sJN^U&CS7mwK2*^iZE@N!2Orvx!$m@%a$D#y z{2XKot7eA4$4hYAV9ghY$g*R{2}0t1-KX3DH%5PMms#@8c=s2#q`dD7LscDqMA_DN zqV{M=)g&iiR`5^(%XCs?&YoF<S7{}x&x~Mgxcb`u=?q!HevrS#z!2Lp^Z$U@RWe+E zAi}wkI|STd;JI#YqPP&ts@Hw5K`9`{8zMaJuES(H^f}*})t`;mS)pH$>l^zG4J)|2 zI4%*fEk>(21yepjjShv@%TtJqa>MYxjF5-d7B~%EJ~`yQq>4;G{}v&Dm-_gM_mqYl zc)PpLLHyNL%jpZ|t*1h~tP~%Q`S5{NZX_`-Cu3*Wa$>STiF_eB4JxA^vJqCXeQ8Id zVEykogBbT8(O+DzGuqtN&q$urQJ0~D;DI`x>8KX%>Gvpeca(`d)9y}D{&0e9#w76O zsXZQ(zo^*kJyfqpppu@4_DiU-Wt=e@PNAUg-6nw1{R_}EvcpknN)rK6#5Q`U<lBvO z+ep%0iVW&F3Gz}n>Xcmd9?KBSN)jXKk}wsKVgy9-YO4+4OAA8=cXhQ~J$o0cC8&q4 z?s-Q~hP+pmpW3;wVVi13D$%EIioB~gwe7+qn55bTpgdP_Mc@irc8TbJAi8W2UfBBA zzVCSZavg<z(R~zT$>&VVB{&UBTsyr}r-PPFwH23=GDE{(Aybx6=KpOKV8-Lws%h#$ zsZ{OE;a!gKs>xo2cDi<xmliAdo=c#~!%Qaf1Iv8!RvS9DzU4SdFVAKkyKe-kZ!ElN z!z}<=*uJ_g16s8nP+_;$QYpnORPYfz`#}8g-kA3e9vM04-YfYw3wAH9+5B*hz@~2w z4CvOsJcR$53I4h89N6&6SFg*psHd0zt3x;wAIB7x6G<@k%5+ey`Bl8-RJfVy^$Y)+ zc*rjSC&CwT_*G`$fsocuETWgO<IhLGPef6M#Y<}qm39vIGN3jz;`F<4b^?uGNqVk0 z3?QB}C?ofm;}OB6lHD*j<FrwOX%zqy7RZUN%eT*S5_gq(J6=g=ltG63>eMLPdPbh{ zbIQw)q?>XHFFfP(+?F{mvZE<BMS%Xz5VZ2?k7S8bt;B+rm}qa3Qx5A0L}Vy>`Vi%a zAjGsY&6AMBAx9*QlRbqi-M@0bz+)kySpD`yy@Yha#xkb-n!4Ix643zpi+7?<huKq` z+3eP*@oL8r0+e(&FCVmzxjnZA*G_gPwj%1)sZI~UTd9ls{nehc)ZJDI&LkDU@}Bp{ zWS^9`jd=cQzkH-|oD9NK@=);&f>~V{5A}j{cLl4d9s?HN*m3px5l26)IyHC6@=*!< z-NP2q_WTcI4ML+qNTK&8@L(kk<dqBQ$MYa<g!8Aq3OJ@9eH665GXyA|&3$7;inV96 z*}d?MMY60gunH>Ie~q*XUiQ~poJh?d@@1l3&8MV9s<(Y#)uApwslkzRS#*C~OFtsr zKDiPTOQSN7rcfZ6^PIFsp3!V2tiktlpx<#|icr{ZYtJW1ppzRiFZ9R=EvOOi8?^h# zrzf2P2;jR##nus^cwEWc-i074AwJ)SZ-h`D)WPy&MO4)DiZ=V&Wkp}L2js==CiyHM zm+;~8pmh=celo=K?2VkEBGpF#9O(X7twE8}lh^*jNWIzzgUdDUzy9lcb`-1{{b&`8 z62+t_n)bi46eZv>`_lfOpgJVM&{ZVN6oNzQIqVk)d?(aWbDR#B?CtRYct#g}a9?2M zeEqxsatq=`cMtA7(X~V%SPK|x(UyW3S`UcOPOj2-qv1{0OJ(>SjQ_3v@M2$~v(6IB zVdth`g09AXFc~$r;qJ37J~Rh*$KE(B5`=Lvx@mhq4QV2v<o3*{EE1Y*L{(HMVJkX< z1wrNzWcEB6jtDHVSh}Fs4h-|-{5)WM%tPie&d2OMtf&WyLyEGsvmf#{(5uo=ffk#3 z0b(Gr!|Sy1LBol@U2JFMb=RQCk=LOEzHk9-)3HU?xIQ50QO>uI8REbu`Vhwm3{wpQ z9jSV;#0XGwM-w{VV#LD`qv0M{C2t^<=1<&~!Rj^}j_gos7=kASsLEK=ssB=!<k}c* zqDM-G&qdq;ZKxwNtA6&_^WLyHvXesCpN2<VjZBBRyn6d)BOWM6Ly)MBWJfUBuDr8} zO9Q4p40`{L{+NB<Jv=a6?M{FR(<xY1->g}S&?9_}x38}h5OSY-jVvj}$H4(f<`l)9 zsnct8=+qopFqP_UC*v~ci8$SG#DiCEM&6Kr91_tT*bar2)r<~OZMW|lljVqR65*jo ziODgBR;NQ~S<^&I>7n|gXAU9yGx=$KaC=S9d|hApd-{I5(_VZEo<$wyZ7~$_`<?Ma zq5Vgd;0fHT55>!2uwA>nx{2}J@ka_*gSk-g%19*`TPrAzw*SXqq4yv{TL7L#`KABA z&7#<#6E&g_rq%=uA-yAqBs6|90`}z@iv9!hD-eFY9aaM<MYsp?ctaidO6U&xwe+jX zYGy9BSq)sx=_4nTW{4{$)wD-ZHTHV%8(|iHw<8f>4Hg0!a-celqbRxx<WdQOjicDm zoes&z()@`Tps^_9@$<T-Tx$UwLXsE!CX?L_zl(J7DY*)#q@gS$dZ#@OWVk3L(QAhK zbr5gY7uSZE^qG~01cHM+d5x~H>1hA{HTVI*p5f9@$-Swk%W1nXL6)0~MC*D6{tj&C ztHLDUaJW~Gne=<uy(oH(8DvtcN-Cz&`!iM2Qq#4ta>OV4Md_~k7^QWeW{!!ojC)!0 z>{nAf`PcXtoeV~m7Ue3u^Z`1aI%2sx4aP5#oV@;w{;k;n^ads2lRW>YnJ2mLvT_#Z zsAt+>$<_``Mjxc7JRr3i&GQH#zGlyfV=(yuJg2!$sRN=?AT_rOS*Rce3vHO^gx_;d z^SU}7a`0mLi}AJ_$&+1Ek}6RrEW%QPY1C;a&o~4`vB})Aen7&T>PYRks#W})gjZ|T zO@tbw1bnFo=iJVwo=wLMZ$2sL{!aS#a%G=zIsZ1|cUUUpN6dXagJ45j1Bx9Gubsi{ z|7i1S1w=wR|1ZAYF*wpU`rnL=iEVRYO>EmvI<`HrlZkEHwlT47+s*{r-{1b1wY6_k z)p^l%`nk_@pX+m-JFD_^ok0yGL9hrp{^|jY65}hh<ym>Hb^_<dwEWMxWCkuTgh~{L zND9ogZPi|gpJ>cRc;;}I$s1-BpsIGBAxZ2Ls+BMrWsb3l|6Mt5AEbN~!mFN8>)Q4t zVL3EkLOv$x3M$5#sMGu*scdEiz}i)tgZN*=JJZZudBIx4&cgC?`C@Q@e2j!3=3cUZ zZQ%O604c;mraa5FNuaHUFLTfI#8BJ}9aJG&|4*9WWB~+ZH09=@2}?)~0s*dmjhuG- zXZ;zTgB5v;{wS^hLeF(3iafKUqS*;iC<(!tjRQ(i4Eo4^9RAiLT+z2m1AZF4G>%ap zMksfEaDg%k4Eyn#p&2xz@;dvx#Ut1&_m%RYzbOcp38F--(ZdHcW$i(KN!qQDYXdA1 zCfxX3Bs-%cgQ>bCQuz@0hz~rbiSgm^i1DiarHDrKXnMtUJUMqpWIV#8Gty7fS@X!& zgeX*@2$rd`SM*L81U6h=$qH>_hjvue4KU_mbyaHaHKmP3Uv&(XROx$Pc&P%;MXQtC zT@zp7Q@&V<0g=xIME;5Qud9U)WW8X%wYp{8D<oI{Ve{&F={4LcBN*&$%_<f_+;a@G z-#S1_FJt+vJmkMH{I7S8o7|y#yQNkwY@f76h%sS{SVy5l!z#-p(7=~P4J8I;zlv5? zvA9HXR~mVWk(vsz2Rm(E;;%d8doHtL8NL|8!FM`d24mn1HEsBkGgq_<Mv*NTR1+-* zltEf%$(Y?l&7X$-%?uml!$C|S_OBZ-8IX#s<L`-`HTl&e!27fY)E@bMa@ouU0sr(B zBs*M*LY=SYE!zxs<#dFq7)Y5eD~8%a4l-OWUTz{v3!I{s@U2Wo&sT)AcGZ6AOzBNA z9mBEqT8ZI!H%I>z;LCJI^kR)}6U#C*0v|x`8clC>JK18OtGl?=cwgMJeBm=k=$~Jk zMxN>mGgQ&A8%;GnMXOtm?`l(jEwt)Z|Emd$Gn{~0X9mZ_5;8?gl--iQu@Loj(PAlK zu>8LKFp3{s-00!_=WWE{3yCu&^>ebm)zc&OBw*3^Y{=o;c};@Pra)fFNsZedphQlr z|Dqbp40&mEh`JH3MA9!jQ9|US0ufJSynzbezL)}Cxp!O3nZ4q(NO92XGI%J!&-^{D z?oLg`JJTL7o3AMaS}TxJNa6_(21YHd%Ikv|49mltvngPCwHAcw)BKH&&(~s*3dJ7k zJ^inZg7()GePc?8)#kpZ$TON|6jvCtx_ON}xqlQ#(dyV~gbJn==rB72S>#AmL7p(s z?W1ZFnj>W;RY5s8L+_~X8CCMw?|V>R%(@)5`^bmRryp*!b0xh)Xtf_0!>qnf@4{iC z8!?KZ5)RzdAletr^Zb-qB6zlU63bm@J!rid<!YWrfVGcnudE~@f)tZ{IRqIWbmvr6 zXAI`*R7Plm(way)49|)R51Qr!EqP{9h$KA!Obz%!je|fRNFo>LKC18<#Pocz>0l9C z;{HtVkZ!%<>vG~!h25X>+rmO!+<`v|T(CcuLQQbCzexA336UKUey`3jQ%7!J_8HC2 zg0JPI>?P~&aI};6AzLQ1PH(1c4UZWB;E0RIn2bvXVFO=T_cfWhJth?b#7E${6y}!T zRsomQ{Z~^2g0{5!pZ!)Ln9C`jeAA{B4WksjUJwmSnj0kkn15K&mu<f<fL5%~sa1z7 zN&0Nt!MzsEwBQ@5`f}IYe&^hr9zp_0N~x-rjJOIX4LprbT59#$zvxxN@E7eDxqfb6 zm|0z(@4$4t9ZAOTKLVi3?#I5;RNRX561Xh-h9L8x6zqg=%;DI>qX20dHK;aJ7M$J$ z63S6133~Mamar{ZNaB{WUF?lxYCbZ^krl3gLXvf1M&yOKL#VGJ7LE=t;x<8VmC#Ik zjmE24Q{jTQZ`cNW)}T0>c6$keXWSk}8W1+bv7b(UzBg4fdYpG#STTG}=4fshZt8C$ zJDWbL<9Pe&(K1qINU%67qz_%hia(RvP!&+o;3R!A3uJ>KOthm|qfub8>4gI|j|WOK zm}!(qnL@BeWZO&cW&T*cJyO;9HtsWFt(SiR`|6$b(l=lX&i5k4fm{NpdEmOi9{!`; zND8j_7CST^)e@}<X<Q#|bl5ee+4XyT&{@D$ZNrE!(^0*-(wF*ls3n!Iq&RVDDSoHv zt!%JbalTq=Ks)k&iCB$I2^LC;5Sa@m2}b=%+ZI%CWLCClEKA2-F*daI2`30V>Q0T) zjiA4N;8h+imMUv>)@h7E1@(~fiqB+(_VL4G<8R^U*L~k3a8Ukuc1`yE5Ub$VYrslr zer=;4Be#fU>jTQB*<aA*|Gnpn*@}HDZ5jYfym*J89PlfM6S?tbrZ=3n2`0`0XQ;G^ zW`c;u{efmt%wT@YzM%G=U-nNA9D3Y7d$F~IQONz<zU#Y%-@KPpE78MrnH}w!m?fO6 z=2VJ7ZkqaiYH=pfMG-Us^*+oP=>w#6K8SxTo)5#!h@78-$rx?=BPzL4(a3iF48_uS zb{D%xk@fieA&|g94Jh}=Nk-64e;k~$(&Nj8r&b;$Xe2ITi_-98VjJ~|b<?=9vID9| zE3-kLje{WYK!<npgl$<HAeh{+c-D;y3tw(@-6fHIZnvYwS_FEm-!Yyh>dH_RWT{c; zaQj{Xh)wTRAT$7k3qlWDd8irsLM)pzjn>PyjV&)!S?rGc`=I8`;FC;?CrvAp7YaHY zsT|{Q1@l~!b|e=mY1x;^2Ugm}9CX0G4r@ZWP`Lu4SsZF41Wr>p1<=g4&>|{XCxjgY zT7}AJY?3IYi%Y$;?LLWRpZ0n}bpn{svU7_J0fq6^Eg}|+o^L9k*JzNA+Y@s<NM>I4 z+*f4sDUGEnX$9UuO2&#enc8%oM{0hSBPn6mC~{gs1*Q7rS5ogc3ZFW73G!k}(>l@| zmT59b3_{~kGgU+i*%wiVC^$m~<S*)ONorxNm8A2zvu%d=p!}-qZm}kq)*Zy5TeXCU z@*xBYDrRC~thL<e39Ngy+o(n7O{BS<txda2K~%=SK(oQEh_|T<Hu_07gc4U=JT0J7 zNDW7G$mIR*>3*Lp^q7)viy5atNyNst-pgJUcM00qZr63;DQ0E?$E`;=jhKR?YBvDX zMQ%8aOu3`2M&FV{t1FUjt>Vkjs!jQZy0M2yd06C%|6-rqEkGuE?J7R~+u3!3Sx>_F zjGp6?=b(m$bLHo-k&z}3o<P0`n4gszGd}jAL1>R=-Yw*h%gCmxw;M?PtU8~6fCY|6 zf_8`<#@}r_uR$FvH#gfd?{D~>B|JU^FkBs`Bk&4_2Ppnbfq&zYa8;<8_F2R+#56}Y zov=r9s21iXU}R)m$knVxtP=WFQKwR5A!X*@AmQ)6&do&8YTu<A=W5T`aMAd=lF(#Q zx%UdAY^v+V)r#6!exR`$Lm`7A2k$I4oL>q(^g3UtUefw$0^P8&@?cC@4{dc+)8QsU zjfvnozCpG7q>3jpv}df5<H?3U7;9o=n5IM4Qs$I9)HXC52m$BPNmBKp<R_ddRo+k# zn`5?HvHsAp$eR40ona@sJYH)+*0H_?dX9cv5<a|+UAe`*lL0ynBACL%FSror)+DmH z@f8fk8ZV~9Wiy`-7cFsIps_5K-hI6%0ik2r1OjBgfTVcN#11j{T=w*EC|x`eh0umt zf}X+@6(otBSZdxTK?#GVb3L(X=g<MoT^5|uM!*$F_CrngZ@vO?{rznCbzC?|p+XEa zkgxswW<q^fgf&Ov8euC8#6bstR@~5OO~1W3PpTcBJD@{}f8jTI%k|sTlx^Cer}4Np zL^qcQ7+MR48Es)xi|!B-bA7$w|1X5PG1LjV6j_m8uPsJUYCcgdjimWw=zcFl4NL@z z(h#^h`U|K5^8;$+$Y#9YX6iI;9M1ivfU+=d(-7-3k3~ovoH3nbWs2!Xj!TE6JSa_8 zNVj9_v*RtUlh3X_nI6xFMPPWb=dSA*IDlS7wjSC1HEz<b=kat2oxKSEtLIj3`|hl= zn+cP=_Kn5<eW0`J5^uu<1eJ00?;PzPHlMmb91dBimC@0-DjG5QCgkv})WX7Ma)z?R zL$tgIQ8`KsY#E{~`d{zq4zjOo__NKRR8EARcWt%L!0Xhzq=3(!TjLChWSX9RxW4y7 zHj|Sq1kCIGEw+;T>)a-!%p}6!Yp<NyVTGfcoldQj^9p38%Or<p{YN4E=n5=Bd7OQB zBNWv185xz5u#*8Gl2tV^fP^F*9GM{~3_KRK9^;{*u|WKeX@SyU`a(eg7taeR<jEe> z%kz@qjk+mFbuMSSWiM<x$`oz8H7(Q#t&AiP1S^;oJYvHURun?DZPz``q50P&SUQ2Z zxIvcJT|^or*S3K?gcwuA)Jih}#tdDOaa6;b55>S9>ZPk9DcCSj^?j);=?LSJ0+y-O z!W4)ArDbKDQrjv7RT2#F_-dW>Xn_@n=bL%|BMQy>Xw>hc-;Fh81~I_zA)8YFy3oJr zFG%l)K&3EL8loBAJG5r!?-!Z0#_HVGw|Au|U$?C`_b*?juUkl*Z5h&K+q7OBv=%i} z+GUFhI?%s4`IBOSBcx~`I4QFKcQarkYB4_6bAUBPx^1fXr<M*$MzEV}_ddiCtzV51 zc?(@mLu=11Dv->GxLhK`G~$20z+y8@WW;y;Ph)V&^Y1V4mPr;tr7hfyWEQ(p8CNGE zK4>H6gJnLBMiy;K>xH*q4R5q+|Ap-4bi?UKiOa{iv;bSDKL>p){6Qd+2tG{HpaL>m z`5iSoTw6U$v=)AmiEKo>V0si=SBzGXYIM93;GA~QfHo+BeP0Ib43y%BW=JIkg`1-i z>DsTgpJKc7%koda4%O`|ZAO2?4cJ65(HjT3`-lV=Ps3=x<`ord)u_jbtNfXWd*y}r z_uI%i3OIqxa`~>BF%UpO8-{kX9zy#QHU6QkD1T>~N}cK?O%zF$9=wUO>vZ0&x-VP- z<P&>pji1ZJA;lSp_l&KUQS0-oY^(NYImWaN9i?n&ZZQDbT?c(EXYK~G_iq<eBrRt^ zzZ9i>>1z<j57yalb$pP^)F{!4eGD_%s8KZDf4e%3nWO(>STX2vM8%W=6gTOZ?Gh>o zi@1r03P<pw-A>vh^ecw()eoH0F?53cS4SO-){6})<4F!_`9l%$G5XRH7Z_rYd(ydm zLU|@kbui*<OF6+S0<fWB{j(+?1D2D>AG<MUt8@E6z<VmH)hNndHo)wdqxrNN>5@;- z8p&v(SSoag9x;Znv)hw<y60mL1HixJ3V}(!TYT7#JlzJ=(tdtS!GDr&NVAyU%Xz2? zj%eQ>lg69G!}@DR0bAz(^&0kH%lsT*k)l<`@DGRa$=o)?0+l7fAf)w(t{0E~F-)h6 z()bXB7e5AizTMh=o6BqL4%i2=1aW>p*T3X&L+Ie^vYA|$abIUDPEPGdBnQFQ(l%yU zsb#<?D%G}|BomW}B8rs!m}OjVg5Oy`SSXs&WktC#yTjo>cK+ZxJ-~S}lM|1dt-oLz zvNM1ZnspGJbtDPN7o5SifKaiUg(ya%0Crc1C?)=SVZEwQV&Odf<tcNl<gkw18-cgc zerYeepjbRkF>F3+da=VG9@f#W#IHs0wJfDI+W|k&kHT{?sO_CE{Fm_u#LX@};2xY= z><G4Qxijl6@s3}RE|z>ircG~m+*6NjoEr_1loaM+|9kX!{VexO5>s);pb3B@VO9Yz z#EhdxOu~`jt%pOX+jSlS4p_r+IN#f6>xv*uUuxjBZ%MFw{toWpzFNTCAsm}=zgq)L zEJs3;`-e=S!Prc6Nt{PAvR`@5nk0^mcU0-uhCNMQF(+s6ot~-|+19JR5O5yTm9dlh z>#O#h5OJuYxVT|n&ZEU$appnkX`1CW2}R2t`lPFh1(nESLW+Rk05E=2=2pVOcu9kc zE8^PLaRNELydN2Zu22&8XmfAQCOZJd0u*-kU58uP(MzN2mf+S=Y6{&T`X|$-RC%x> z9OwJ>;exyT!alwxYDEoW@hKTC3sUexKj{Ln4{*Q8d7JE?591l^njl#N>`JH&nuj>h z0ZyLI@o|9vN~r4pSwg!H{~yG>#@CO9gua@epIgnQY$o^>fz5ZT$+)G^#w%+-vtH== zA%|P^IHUCgUw*E(hY-=TPK+S+`HHUF9{EdG%1MfY{i0r-JaYRj(FQ#DA^tIRkCHda z)+o5CAR3jYMHA<<o~*{Z93!P)-%dc%@(3Trz>BUgh4Pvr1jR>d1@PMjiNHbWlFQU@ zNFG9wy#OuyLNSi|l8SrGG0L!#l)$$yQwbwDg~$25EUc$mAoox<cmPG|<9kZwQ#WO^ zuwR{a+aJsz&8pIqUXd&{T>Z%gD~Y79xffBmU-$PpHt5*TudfOD>3<wlcG1T?5Is7` z{LupuagJWeU$PbrNV2!}wTrDdV%!vI7LJ4|_sS&W7;vOCRm8baQEN0^Wq-q;TE`&t z(%^@+34$B!&$d2jd@kHi9-VV_9e~b72O4}J>iCiC$p2BB=Hws?mF|IRsZ;+MUw-iu zyZsTU*B+9d4)SI*eyfS9AI$1)WO#BIM;OV0^)O%vLYu6QSP0DBW274jrVJTF6NV0@ zTkGCv)^$?uJ}1VnOP#E=A_x3cRV@kNXEaTu#8FjBF#u7;Qmr#4*Ns@?keb-V)F4`0 zKA<_E<CN#O-PQGJcv-xZTOD&7obDaDN)@yJ<6;kTq)M%CO>FC5?orEWJvH60vZ8nY z=wovqM3Wusj{60Sq|5g-Up3@E<fe6DcB&g6IdstlN~<s|9W$nKV!@7KrnfP&{J*pn z#yc<;@lopyxqHh0L)Md!y{UQV)ZAy5dz4NH815Q9a{Y4qCXiTjBjS1hMhuaG84nUF zzWeC~sDri*&_5HizU@c!xl$XyS*oiC=FPe@nlZr42!q>X->RkW<oHSsfGLCXz3$A9 zFhf_Lxyx<#&T?TSw)-uW)73YOSYH}4wvpubb7}q$X0Vrd$Wa>F{Wd`*C)dY2cb*)I z5UMD+Xr>><25t~;a2gmn@#XJRlK$z^;1<rL9AbH3f9OF^Av!An3z!|wQ3D199RCKE zA<{g(fkV{VEjHL%mfVbH6jmx~IXB3(l0l;J^$M*cSEwX)DPo93$++m&RhSWZV4Q~X zbJP63$C#<%sPj@$ik^&uIQ#d$%9j-WX+jd0ldn{XxzxYW2h`kG4lY&{yrlO<LnxYj z$vB}XO;i9_cWFK8gSY=KKHv9f+gx)Y=cCPkCU!p$AO04w5q|BgOm<{zoP5~EG#1-- z-8C&K&^~0b*fto@tK_GN-ro~Rg^W01u8myc0j2-i!M_#*j`2bI@sx8#poVitD(#xO zz%3y_l*JP1f{{vr@AsCeW<ypoY_5*t^kZ7e;!1G29{$l8G!*b2<EjQ?Ow`SkRL_0i zAN!~pYy~TB<=zXPe=HQKHU;1nN`M&1s4_aKuX;ln;WL1}Zi5d}l}_CjVAAQeaeWun z`b@y~l8MEEf7h!(_+BZo<G%cZUZ$<wZmj5N#t6fXMrAAxj^tTo6?iYyo^nD;G6n3A z>K)|RHOInLC-np6e)+Gf&TjncYi_x`ab%>6=VSDYg{L=o6CoD_3vSK?`s}@roJA;W z`#Ez;x0qo|4gG3Hsqw3w2L4o^2r!!)ZdgGyt*(K9f>e^=OpHm0!zIv;nIT?|2!rTR z@GJKr$#aE1y&q$&S{-M$%6`qrj1*)Jw?4nJZGd4s4jNG}*3-LuT~3-xj^j+&tnSZJ ze)ht_0>Tg&g|bV3MqucTN$x?b4Aap|v(iel*n+UAfX<@!Q~&lS6WcXT(06s2SdAM8 zlS@WXVSo1+^W(qi$#d^FF$b!*d-KQ`2ecj%<q5b>#zT*S%<XoAkwqv|Tzza?V{qVU zP-p>q<A=#vpE^$S;EgfezWcv!c{+8#i99u>g!#tIq`MAByc}(4g954KB$G+0<ErG5 zE)mQ`ZK%<Q|EcaaCInzLic}a1bEhzkxnCn#NEBKy{fcyeoZy)DKyY@Pxa<EF3H68Z znqr*+3AIiUMCa^Bp9pkL36`xFD6|F;!^?&G`Fd@uvJ><>bc@A<ZQC%RHE!FmhD_kl zMC(+Bk@kou9U&Iim7ccuSS1OD9B{Z@<j-qbvVJHdPp=SxDn{cYT*XdR9OAXc;h9W- zqC7%)OHkg74xRN2n0PVj&fs?vySQ_osS|6d%=Jb==J#2xZ)kC<t!q)0H+!nk#%x5C z=&RlOGCl~LYGr4<c-%#(8vO@M@~uujzgFthC0^OeO<Ww?>Gqt35OnGrA90F<yxwWI zKX(gRI!1wdi^x#0SPD`k9a#W-Bez(SfUmQ*vgq7jjI-n}Kq<y}5hzm;n1w-BCZr%P zh_gEXi{L}U&a?gRjq)HQ9>-lj{O`W{ZgHc%S(SxlBRKe4L+XG4h?@3Z-KEGX{E`pd zcikmMm9H6=kRC|7H{iMiKcLc(-{Y-~Gi&8FgYkOkZ63ygN(?uzn=O8*2r2O?ySE+$ zM6MG;K88r0C0d9~f^=T=UM1!BRo+TA=DrBA9u5u}cS;gmG$|uq8H<MYj3pB-pg2wt z0e&4#HbQ(2AhQX4&8>I!%a-`3)z%^6%|Y7s`4rSx7mS(-nV)wjf;d+&Q;nw`C25Gg znUFp25OoU+T>*@xzWo%z^m*=R8Jw|qgAB8eVRFvIuRNblkxMS|7(@FvXwJu?29Y5r zZKy?s<kI|iAt^Y@q#B^b;t<h+`is3G(u|ynz8nnIRY<b$_43~_+H@%4pP<kk-~P0N zoAdzA3C*RTa@v~orhUVq1jjZ{ek@|<n&lZmTx$2;M?bDuaOBbxgq-gXJW9gF#{TS% z3o>d0>cY>tC8fcL&maGol0S#^q{T^&#+BB<O*`z8thFp(LY=~z@ZX#I8gQE#rL;~o zb6{jC>c2FVjbWD{D}AiWEOEdn)4{hG;KEhjc}a07o~GeP4FQSUGxQ@hFt12aA=bmT zwoI*dE)ZI36l`L<4dXT~ByHKvW~09W8vBZRm%t2X1URz5+a180Z{zIC@~LsTb}cfG zqf64M%Xd1Zi?SsMq^d#=EIV)Ie}?oNs?;Y-h7KVmJs&3jL1?`HCs%D0pe8FkF)u)D z;Bj|j-ExWh%l;tSHL6p9>558&!|&*04#=jwI;L`xWn3AwLQok4uxdR}$UHWtGS(It zO&LMgoD;*zL7j^Vsj9^6BlOJgaM80*{^JSLFDQF}Nu1E#ABp6Nu;0t={Qt1TDSB<9 z%Z*Nr46e?=#XSOG!R0&yiE)xF#Rs85D!6&#s<QO^E3<V!%xIlN)62~wM$2$$>ymyN zsqW_m3Y)a&t;K;#SAzcL0KkjA_5y5+bO^`#=dRo%>z)A`EnlzKXF$BG3owA|qc-*X zP!Xre4&1KwOLJ-bwt&%Sf~E>`cp5tkbx2A|LXYDsPG9cd0X=O-l4eLh4~-WH{ck#h zcTpLYakkfha!+BwT?iJMP`;8`@3jnZrTkD)ToL979!96<{w`z-J=Iu==+BO2KrGB9 z`dwz2*?~x1SV>iGo$;@dtxFLmy72KR5K|%J0}B6@9*?{c_={0<o3_`r;$B~e=aZV; z*BW@8=h1mBtc7>>+il^KGJJbUkr-BNrbpXj+OK%HbSKLS3r+m0?=f~RUS<TRGG>?} z+XCuj+NF`XQJi?VxCSP*z#Jt}t&|Bh0SqxN)8ti~qyXZES+u%4?Mfjsp8bM)u43HW zCnErwO>bz#?;7(7Rgi77nq{*}Na|82#zLyRB~)ZP4+C!JCnWlkZky9U6)4&F=ixuO zmpou1Cu5(Z2KPgkqdFS$dL2*PHg8YL80Lp+raDJM(&>(rcI;0_!BgGI)M|}%`QN1l z<v^k+=l>##Fg1tC*<|@S?jtQHT{X$r{8W1T8|MgPf%)YNc^`;TFyqmL${ek5xelg` zKHpoO5fiv3C-|uxt8Bq{H;j1y7LuO`)}V$LRFk-RD_!^hS{1x+h5^Wz+kXk41RNOC zz6TXox3Nh+ooS)s9EZfALGq-w#DgTQAW)3)u%rb?S}cO$A|VuH<Kj3%C7Y`W-0FEv zHmj2RH5v;|B4?DOZzyo+C3Kx2*%By*nsiG~#K)#xVQhLl4#<a80jd09e-yMpHD$EW z)~f}y8H8+-b=Ocf3mElwx=_8*x=;4&p<is)k#m<CN$+$kHv7aMa#SF;6OS5*8f=cR zF<i9v&@W_|)Ztu7h6)ayj3IBx#QCB2&rc>VkWZKW>kh3s<+$Dd3PX_dPPCB$ky+sq z7)3a06(6Kfh7~(|z2EnPjXNhP(;Ne+P^;5SFD83|8|2o?-YV(MJXu^qztfPZSV*X9 z;sV5c#AK-#ib5+9W#LScEXA@~Xlt;iK`2r#%WT_2>?lne(&tHWq+rpDL5To;7grGs zvZ8NLc<=g61n4+$NIJ@X`xPxJgz4TpMXswDpomkrKS7&DU(K6~wfzfdPsV2t)c-+m z3Zpaqw{^#6#LGwOx9m&2J=a@3xG(o@&<XEnCEmyuzt`Qq>#x?{-4^1Z+yglf>6luz zHFfTKWjE3C@iQ)28QP>mGmXFZ$f70f-jI75KHSo3o)SR~0!%=_W%*;$pz&a=MNI=C z-Fuw@t;gGOP~wZ_()#zguQT96Ea=4e>~iR6h9vKZb7BNOuYw4t(pYKV*sU>={`7|< zF&W1hF2^XrJ#Y)=Lyzmk;)@;&W@it;jv=^V3_%B*cox?D-E;HUW$SXJ(9Xb<wSbyj zWcS^Nz?u6;k%X+o?ou*Dg}%0MBcy^@{ZR@*Gda+vF)XaDyiXC;3q@wxrCI!n3Rs^B zpd2%+SD+qR2!<u{4VW*!2BM6s+PToGPnAIK?hnvTKGS%tL{a4hQg&GS2zTIYHVLkv z-F^^ndGVhUsrG|+52%@TOCIn2XBKX{pjabL5|r`66;!S$fuFO_!mT7#{5L6EY*1K< z*BZA**)3R2)n9K2yWs$U>~|4of41;3M@RK>n3K*I$}lp@52Rq=y}1TU2rFmz(uJ8} zhsgT+87Vxpab`O@7F6KB|J9rkbyi^fsx|`nVU54nE#r)Ipx-B$@Q#J&0=Hmk=cB(j z9{xzc=wN#`DzU2^3@WFG&!}jLP!kGI%BvKXw($gI8EXbDruV>PmAdm+(N^t*LoXKc zjRyV6N+AVGR*=wxr8aRzz2ExbWu40m%oq)PZ}JsREJR&r)In9E{Mam@YNbso$4KW< zSz-`V8!i|!ivSDnR{Bo0SqJ8Tz%x=@y(~r?KQrouW`S7#Yk#ra$tL^TvJ!meiH&B< zo+od10E&Pq=bPyI)iyZ29kb!h8EuLzc%)^d%6CA)vaw23qn1$xMj0pu3ocL-LAhrN zKCAy498RZ=*}}fs@lXrDa^3eYe!)J|!)8V)ESKlhw%6}|(2p>5gNm{$qji}<MU<vb zcr-I7$9BbrL!rB60u_zAb3h!nh=0*p2Rw^4=E`^)r)w9UQ`SqxzA8YJ=;GwX#%uYd zlH}aNJUZ@+TTy(gCk6%h;CY|s4Y>p3W-Pj$zq%+>LM`(euE@GeDoBEIFp-*NG>(8> z58=NcZFr?iT-+rtlI2j*qWq&L2GEj8W7o)db#81XsdIGZUqyIWSdr(wnrlav*9znD zH)mdij*eCrQPg=H#l-+5P10h6G(qYzFYiad-mC$keX3f!UtKe4E3Ts_&rfOHk0WiC z)X^*XZpHU_rc4;;L*)^1iqHiKv~*cwXg)`g{9>@c-=yOw>`)fuQ==9CZJ*8;cqhQF z<u1*7Lmrqm2$a1<sy~8!N#^vihzV>3#rlZTj_C`|3mPB-TW{w2Yq_@!RL1z7w3KTG zz-q7ty6!2WE=d2C#-hMcU?GKA=as5T)8&eXl^B*2Q8UTH&j_vEK6kK{a~z!8pB)j~ zi^%~aQvvE#k1myXcat#hq@?qx5nr*v_3hPc@*}02bYv&~VzvTZ+Tqvl67JJ|C%GPq z!b=0Xa1}y!{}{@|AYEfInsuKlgP998Y(+%;o4K+b<{NA^kacbwtw#wRkK+dAeLw#6 zdhd5)aq9C}JeQqWw_bi+)bw_!Q0ev*z-~m}M)S3ow@c>AMaem718C%k)g+pLj#K(~ z=gTTln#~^j585%CPPYHuxzJR3aYQc2*GhEJrO(XQ>G&@Sh*r6OCddOLwMxCmKn?SU zY-{}JUc~+MSZj>#>t(cS)biEl{l`v{j1ihX2sPgx*uIn0QSgw5=+9H|`mCECDQo86 z!BR!QDE(l*;VIRYj1<v#-o~MUWjOMuC!1hfO^!&K+g5O)oxoW!9UMTJEc|rI&Oj}Q z|F1^3aWYb1k*ZtcAIVMzP*RTJ>qa7`3Z&<_VFUJG!O6u{%DLrz7a_ki*;1N=Lr`lN z8(f_uU?>M=CR?9u-&WPod|g%$CeMuE>P^daOsvuPwQZ}$X;ACF*kC<2jbVb=d_I`{ z2H`bLyeHb0{Jfm{IqlQ$b5o-SNp0<;D**`Bdcy#5wqKD_Av{XY;HlgKv@{sW_k>a6 zKFI~L(o9Jj(pAkUH)^lToayqK-J33l6aW-W(q5o-!^dpfOg6M|crQxnDPiDTDntqm z;D3TV$fqy(bGr*Zlj^bZ`}?vRx{Ywyl|KifHFskMW3%%>e;=1he)E9qYr_#Wl`E!y zD4DB82vrj1hnoM{mcMNUbGJ0HmYznpJ8-q1YcM$ks+i&a&cnw}`pe|o_S)L#wfoJ| zuz&Fhf8|3q-wB`T%;fa-^z`=cKKoSPk4<jJo>7SC%E9h6F08<?9y^~Lu-39-MRsJ< z!Leucb-j(G6vgEAX{08iS=9$OVhbgtumDM;;6nQIb0&Df*0t?2waQE_r3;!r7=>oS zn+}Cdn%a(_KlYOeMm^Z(Om6>zZ#`uyq=mvVK<jrS%;?^h$J{2@Y{hyvyC@qn>dGV? zY0#;&SYs>^&7KlDP_8#T)VI6u#Qqbe+G=|AcP{aJt(()t-n)sxe#kYK`}Lk7#yP#} zuOro#e@KZwluj`>62N0blv(6PAzb}ic+B_FbuZWHJ)(rf&f9$dj5$vXy&Jx_p!n@l zPn{q?@MH9|d>CfkerA9BGN_Yir|Tg8jIJfuZ6e!1z;-+nspE+Pdh<5pX6K_FK!xwV zl`!{k)Bc@b_kqNU?{bg}9_Y~VJ3496*86R}yFxz0IPVh-=Qm8wzul=Mtqb=YZqLtN zgZkHMDtYha{5>?`Q&vy3z7hjzcPqWhrxV{V)Pj-=GfsV%e{k@9m!(;;e`ltNNvpPc zGVDMr^tkgEvlKj8lyPQ5sE$kfGQyk~RdVGB`Bf<F*zxBp<}KKu>^kOV1hELU34?3# z6Yc+44x+>!NXcf6*aZ8vn~-et^+V$_RUN4YJJ^%gnkT6gk2E1QANIW@fD<7?08Fc5 zBRj(f#jC&Oe_|Q_eJRLCGg?1c<98ix+QTK8&OBlQpLyT$ypQ(l@Zi^<&S-k~<;4=` z$moDkC^u6+lUA>vK!XPhGTAg7^?xfF*75T>aO#2y{k2ywcPBXd2BmH(M&M;?)=VC< zUSaeTU>UTge^QxTV3`JIsyi&hgW^5fwC%(`Rt^nA&@3m{D(}5xlz8{!a5qy!x0k7E zC9L_$VS(_fwFTDN)m0|-Xr&qsa-Gh&_L;$gfNQB{<NcOhWqGgtWCw|$aVLPY=lLIJ z?<W-*QisL!)7=&`g0<(&vcfEg<wNaqfHtL|j|o`8&|<fIC3gDw8P$4q&}lYok=KoY zNgj<ipPW2oY3aG`VZ^3*-W20$!(#I;(dQ@8b%*&%*;}ULUASupzvJR+FRsg|L|F9K zxuxEyUhiv0`Gwh5^Y71=FcQp?kD~fYEZiw}Pqq88a+{4;-8D6(!x?GC*9cC9J7R1) zHDeSK-SvZ*H64Yl_k#Sy=LpVguBt!o-~)x{HcewS?5-8AogQD2<`fvq%qDK<(dKL* zX4x&#qv%EAO7f}8-~V8Ndw{l^oW1%v(LzpkQ!%F$0;U#+A13jEp+`H0fDo~x%zR$f z{^IdmO`FHyP!qgt*jBWKEyC^F{$ING{6@5x@ax|d#e<{Ue~NRN@IYfBCxq|eP8h2P z;h+VY!5jxMXT<N+*Q;EA)u#6zy)s+(I1cZ(=JFCG+U$v7>-|I2L8O`xj~}$LpVXv# z8;)1ryL?4eZzLas&O@qK=6gt?55tTnjDr5e*7iA^T(i(><LM)Io(|PZL-3c8oC!LC z2riX>5brqrAruB`G12ze$BBMh25GJ3bEW~m?S3Bp8EVu6YH)=B(C*~4<~9;057SR) zUke{D!I23tN8~^=oL`g{k(Mcwwp9^KXDedZl3mEc_xeLIYf6lcxuZIaAf;0|WT7wS zia6v$R)x*(I~H)V-=eKl7QJ&Hd4vVkTEEBZehZ;Hkq6??j7_;Vy&0^7xk32O$4IUu zqwh=JSH76DoOwe!XQm;hd{kZ>N;`nAIO<>zQ(IZpancVfbKUK6St0Uslx!}-{`1b3 z2q#*i0L}OPg8L*^o7?rR<*Ji#m>cVS_D`vPl)NoUmQM49k7<fs>y8e*@;a^1Gt4Nk z>@e#j2DS?LrXZddNfyZ6ofLT#00z9lH3siS7$~|3`>b#ioL56fd$X08hHO#Ei25&u zKyUSG8TfQ|%-O~30GKwBY96-s^7r6X)v{^g`{tn<T5iW?RJCw(sf?gQf&HEncBw$s zLWn6MQ{HgM2TE)0*W%j)Q%avtug7wf#%H~~C>72O*k!$ELPB+}X;W!)PW$EL+Mf%l zQh&%iVfGwdX|aq2+w)sQ#jAO=byVL(V728W)unfM{H0Iljh;8&wDMAuePbAwc2f42 z8#pRg<i=EjGZ<+CI#+0qwSq>vKmJN-+F0#Y)7A6b?N1@+n=$<u?*1G{%w4ytVi2wt zK_~wb!Xm=z<7{Nw*c0$;L~m>}aFt#8Cg8aPe*7VaVR|K>)(S}|evn0@x~fD{CY_ec zs5nIx(w}EYdJvl{qHE7M?!{~_!@=`4;^Fw~u>@m<u?=g^h^wET#DpL!6rGLJ_WDRY z@zNFt8tkv8E748h9!6IqW$oxc_Zu#pSnUCHQ#Ny?#=)c@t`fv)9r?5$WBD3pDOMP> za77(XR+i24aq~%o>Km;I@)+`<ruX5Rd=kvR#P^fOtYsco;WZE|`UmHmQbo<gs^QU1 zGkn|@DD(D21@u6IHz4D^F+!UyWiRBQc0#>J#}>Y?=Ecn2ZTnP7>8rxZVW_dyiCeaz zy1BX!o3^tUW#-(MP-B)ONKLxxmYAy&*P3lEZt+rlj?)}(csz!Bp!L7ELCJc#d<JM* z=`*`*=&sq-uGcCUyR29ceXD1-SYXRG?9|?+@@Sg0ofV+MtO>kXn7ot5IFPIpgj(Y- z&uVhKPIL0OZ;|nTd{D0zqb=k6oNfNh<vkk=lIXVYBk=P()YPT=+IM$1I={@ub1x7w z(iT1<jo)+FsD5bLCLif05T=2_;3JKN$GX&9J&ELg*+u=L>qSyAH*j@H<B7V#8BU$O zs?o!pUH%=Hy;bhVtLLq;F^qir*L`EczGP#?v&cC#T6hT#0^fH@GI(sRdLJUA6kCZo zAkdz8pPO7JO75Jk=pM5zGbNG0eM#HKoJ}a*%SDZ0o-=4kS8Rcj#tuSf4_RXK`}T?1 zr-FUchSv&pyW(qq7#^wJlVZDK1h+Je)qjFiH8lk@>d#TB%NWfBs>0-*2YR9n)n@Yn zm~2D0<1p1s=D+#;#CezNQw;u2{GP5XW8`4`PL>Z@7Oa}<3VeexW!NZDicV&YJ(N%? z#0yx%^UDqStgqTsdfxk(C5@@Az|`3lo1N~1y>p3pUNLf)N;%Cl>Ftk`b%IW+nhqlC zYXXNQ<-;q6!O|X&Q_&Vv%jvg0JJZ<y5wN;8-Gp*=P^Vx6vxad;Ha8f{%m^j%KCM-T zbm2a?<Q|NKHI!giaoQFZYK|1tA}P9iaI<{L7+W#E2dWNG`HHftm&#xR+7<|9E>ARZ z(*gv2UKiBkN`O(K{kIuSPY|!unHxF+(4NusdLoy0q`HjvN}<AiWjXNP(#`bTK@RhR zIMHk0?)UP+{>v`2@ooq&_3LD9;mhsxX8U8O_3LS`_usjtN=&Ek*i<8ju}KtOOWTl& z81Zf{E@JOd?)7;VCJ5QDm954EwjGj^j;;Lh+!)PRmNiO-43k+~a_^2x3BnFM8&MB< zgjREA<>7r2wA4^&B2BP&K5+4#CqFyy(HZftgF7^6J$|A;@ZOH_sU3=s%+*i@E}1`I z=E?hjHO39o90+hsijnAU;~ll8Y0C|Y2fvQkLwJ2gD?LzKa}ekE*fa>X{<vl2J)M29 z4lCa(zoyVzQ;w%i)_Wb>-gxEhk7y?*_On_sEUdd{r3SnIAmy4>OP+@iniSnr!*k`s z!GW1+1T!Y|N-Oq<;e0D0?1&~!1x-l28*U<iVMc5Uj!u9vSo||Vb{n^chj_zl`Oe9x z<72l1B^x+Q;Jb)QTj4aAqBg$%pbVijVdVHeMf%qt{4jo^<zTJfd^-1~Cehq5yWoU? z!zedJfPtzp-ak|;w-VbDZkDWBDCQ_~RI)*J0Lmc<PAgK*EEm*h35Jb={_}l~)%?Kj zfm`o_s<wwnMaMJ2)FON_=;@G&wc|-oTCp#awT6a?eh(Pg#<b<Tc6ayN8-V;r$o<yt zu{eOiu$xBEpT_*a9cm^u%S7QoXL)H_L4yg&R!;*fM1O!rOF$W!ti$&MPOJwE>LV18 z>Pp#E32L=VgGs7!8>BQAYDygP1OIPOLE<JZ@kab>dI<eFZq!8z=91`frbjUg6vp#C z9G$#y_@`&tyyFZulW#9s=k49mpq-BE;J|GFlzdRXX8?P|{JkH2*SLbIm1#>UL9nQ| z(H8uh;+3N#`1wAS{FJlCO`FNb`HdbETCG{{UY?G@XHceq^W<x;9?MHbO*8y;kKe)X z*xhs*`5j(@jXu6}v@FFQwW#ccnrqRM4sZ1YB$x2B?Z8vTBL#^niFBJpOu74<DQ7Ce z4p%nJulUA)_20u8Bmm59=X<#crp{e}h~G<1kVMbJF3JuUfR+)KTZq6GI&pQ*O&3d) zO5D0}PJhnfSEUG63BVU=nCyU|lMt`dX_N+S*(kt{cW)T4LDQ9^swbxe$7JEEY0#up zn;&<=MA_Whcz7&iU=1J|4_uf6m*8kYLE9lMNSS1J@u7slZq##@*i&|0!VW%-5X^6j zzB8G77(_L$#uCuM+Z2fUgZ(N^TvDF8gqw!DV<jI#VB7bQo0+#|q|wI5`U`PX?(I~o zvG*yHg6fY>RO$=ol@EHNW)rLjogl$-3QwAT0Woa{wFa*^G<`{qu@JWpESQW#*JsgL zT38)&O$(D`l#1;jVEGm+(I8YICL}9{>>wHBin*pDlN5M(LeL^%A8y-tv<YC=wsha0 z*31l<BDNjUFP{zgCn9^|6hdF5&+$@Mdph?}!pUwf>o;G;Utn%}uZF9Adz}@$<Z__Z zKJB$|ltcv^ahT)YCJbendrFSbduw~sAEUP&Nvk#tp~lHkQsB_iqWMxb!5Qt&@3Y7c z(8R{<<CVa0HcJFTdt$^a>vTG4WPT{|pH7NQMHGnDPsY&&N+wVe52Q!>R~CsMvf)Y6 zgUIvuo31!36%Td*E6nEwVn(+vhcKtz5UHhAtCqR8gHl+giBC_+;^$;{#Xe&|9|(p< zI2U{E_WkY<#fv`w-F5DV*L7(O;Y&||d)LEmd@i5aM=WE8>)0#1v-(chKP3d|CD=+N z|3ZOZrAvdTEzVgzGS7cII=0*w6j*}^O&jDc|BnZo4)PY;M9vjts)yz>$j-r_?J8M~ zcK8y)Hs|g0%J->R7yR=d){-pee{fYE{vlnuSlS-|`1A5a#+_{)&XapN2=ZnOK(N1% zJ2pA*?dc|$B$w6V`HUXdw;Yx15i9{XIFiMJVgMC8w}NHtRE%ote25uZH*sMp-c>+j zqP*VE?7bCOh(Zbhr;p1Y;T~3f%OFv+(miidWgnBu+Lz*byhT)$$bR#GL4&!qG*x|N z<^VLvitgNF!iC&kY4ggiEFdk{uZ|20fBTc>R`BssiU%ytL&bEM!_cD4b|?Shwb1uI zV_bM=*KuIm0}>+s#d)N{v4Suo|420?;sIdte*_4zi^p09T0Eqwvo1%{iB))KPsi9N zEowV4%WEAfcif@LYgX<xkGdL-X;pc|qHy)Vv>f+rd5q%AA%FkCg82xz_1pHyRw0oF zJ%+{+*JjEXZUk&XjC2}8>-FLnYE|`(2=TK7#S4r!DVxz}7MzdsxX8<K+#mlyo#)7{ zjxN_@;AJ=l8$TDog_P47C+{I3dy>nO&2c51g7n0|Bzkl(-&0}sM9mueW}&mv3sfsC zp0QgGq~&!1{b98w8`b5k?CXKEYQ+*34Zi?D%6;8dG53AR_%(C3?jOQccZrv>GB`{F z->h7dynm?hbIvXz(yR6UM_Ji!=@YWk45or4Gu~dx8eVLd*IcSX&d0Tg-6*c;4S~Zy z_vuW><M1N@mTkY6!G`<V08BnksD>`WD)26))LS%(@LBRVGTcHU66n$ulG>NckQcBm zvz#LjvLjnt(8D-{<MM4t_sA|hc3f~h3x%#p?{J$vdf?}_ne0R+d;{J(JVqD+dcFG* zx?q-FPt@7abjkM_Yc6<{`fsvZ<+KT-;NrCQ`iS_#_Q}I@;8e`-0%vwn+&>3H9+;W$ zF(lmgs-Im->85g<YLx$(oQ<rNTe(~PHcKd98)oc2wSQ9894JswFVhZ~wV&`D=bd8> zPqPEX%ugrTGgsW_ek~rhTO1b2v5+MM1-AT7SpEO2=PwAO)H=!Uub>i3`EV$6)s8;z z(wV?}^_h-&ICuz^-IZ=>V~t3*sm*9L{ThMhMh-AtTV(7?$TkGkXv&M3A3nm)*{a=q zhbiINJwAD<)qBogFz@*&8`JMNvm>|G99WEQ-ls)!p)5J)WlNyzWU#4ZX2NT`d+O@n z1BYv~{=Gir>b}|j+sG#;YKg?fV@8!&(6&O_n^t;Y0S4@Q_Rhf+yz1iL;6=*!)LJ96 zD`sWV?lV0!!yPL7w#YJWBU4m`3eJRb!dON2+TTo(z9I+x#Ef~|PA%!;Vl2h#iW0rM zd{;r0DLRR6mLJ>=BsDB?*nu1=^gh>rw3|E9F<{BT(F;d@a?fJuWJraKzlLZW%koew z&27lgBkMjz4=Vqm&g9Htda|EM5AtNue1yV^{b1x6uTpm1O**Q`dUm1KeOu9Qv-}CV z$Sf<|7lF;0hgsA8y}X8Z*Ra}~&GPmeTzo%*)=u#E=OfLl_~7fX4u_2&c5R2Cp6eC? zr$o&6Wr_m?RfpOcRRE#qHSkQN^2_O{;~O{0WMUul=9s}S3H+r(hmSUKS7tn1qURBW z3Rof1<w65r*ZAHYdBJD7R~Z~J_KAw%!M?a>U?MK70dI+@Pt+T-OsE2W^T4&dj<Qj| z#ITVKKPhxW4~p@kq50jGZFF-EiZ9i%%voza3XF&<Pms7l>s6W=Q%_)|J7m{7;cV8f z=)#(U%*`p*rS;fM#;^H;ocsjIjU+<t(Dd#|G>wE4ET{aDprq}W2IpIM1u0z5oi`x^ zWb6M{i|zRc?bgf(L*;l%Zb6uyGh1Oq)lQb)*?RO7sRgr=F~;zFjL>i1_HU_HnM6Tf zmvxNWK-4NEwJyk8O#Mnsp3Kd=E8(DA+wZWMt=_w+d99<bnW)o7=_U4uM&u_Vo~Sm1 zb(umjNsHFICtiDq<0jmNSU*9|xs5bR-1&UKNy`qW5Uf{3HIv?brWwh)ZVUw}3DwHy zv!W`y&}p)e#~;A|t2A*lDAC{JwMOc*pR#}u2w+(NK?;+k2<|#AepVf+=$4-wQVtPR zwk}UNj5~Ri@O`BjE^mTwlO2)b(h~VW4Tnb@LUC*J^9>ld&eB467+`S-QbwZzKVno{ zH2ibJ-<JZ?Q3S^EmU4F?$o8GSWs_q|eF+N46)*u?4jCCAlZe%Jp8D`+#sDSL(M{TO z^iL&%`2uXw>Xjd^=jt!<snO4cD>@>q#J=#?;XNSUqCfRc=YEYD0q%|`fKQXVw}TO) zw~dhAEA3woHCGocd_xg|T{;&qAe&@D-12A6e$HM=YH%--#l4??_q#XcO%@44H~k<o z$o+T5ND5l>0!`uRbgj93PTY*wU+=ebX_o%3!u-D{|2qx+abVKI{G)`L!X0iX_<L_T zkU+R()A+r%3CTUJzAG8(w1tY(`ua1^UjP2mSM49*$DZoBd#%Aj&i>IOI4+TRS~It` zwgCtDx{PD~!{!#!eHY7DG&g{p6$~XjoRmwWob%VGK9Est)sXEo7}N47?F1FF-xcpL zg3=hO_=ATCEXD(pfz<pQf+mj*cw)&7Rg*A%KxDR8`!lA%Xi&|C6A2-3oL8J+;+mp5 zQRx5G`FkE4;*Umpy;hK-sFa3$+W{#3c+zU@aXTwmpsLAqdjU_2+{L&U9#bzY)|p=f z%Y|^AQ+5m;_H=ugmcX*}`XMv3hw?4(VZ3b}d*!ulm{l@5o8HT1g79_Qs9lp&pFu_) zVw7%DgK#*53}xoCK-+>_)!|>^pUg=lfPrMF6@)jq7?q6;Ww?k)H77P}j@%nv&j%eT zxNBPOuH+vQq2z8y#P2pG@NH$JTWoR<A7&VMm(~m8*T!6_ba7|Qg<6slU@4ZqJXhM` zuShY03|gF{a}Hab;HEZ%Bnn!5tWAti^vK|Zp!>lrc0KiW9&zx?C;&Z_#rLtjAfcK4 zDH0Zih|OnZzusX|TNr~C6c&x;h|x)T*XS-zaW&r~huh)O85ytjMhR`lYhdxGE*Fu^ zvr)0dq)q<;$TE{myjpefMEAI5K{ta-O@U&CJU@4S`p|JG!?pC4&+ChID}QjT_m|bC z6}{AB{h&neS)R^qwwG+f@C+Sgb$<m+nkEdxM<L^qj1KUBEYZ!&C45fZ#Jjq7_7ZrH z@4Zkb+7>MQuS4>j#F9hSj(KV~e=GKY?`M~&=hoxXs6p@RMX7uDfjOB&#0#g01d+pa z6Pmqlp!*P$tGWh9UJdDB<@ij_yVTn=@#ck&+F0QG4wEZpslX=tzyular}PIhq%Lv- zK2)~vEY|$n!ISX&zGlYT((U+*6-92#@|?<gwyQHV+O1;UJGG9{hSrHu@y9xa1jUp^ zesO|Y2Tc1TK5cPjs7jeRcr>>6S@pj@y=KfHrJQjUFue&_m7mj@=w$(awsCE)JuN4M zG3%2cYDuZ;Xa}rg&j!G5%6RAvL*fZjRxJDmOKoV9)%|m|w_hXh^8JSqjd&S`D+qGA z?5A}?!=zf;0PV4VEUUZrG?fxjCEGdS1vpZIMjd2<3W~vUzYm$%w8vB``9XK;9`7j@ zE+)65-)SW=*fIii0ZH*G_!SmiFqk5y2uvllzd<Ih_XwX(fOPlxEh41P!O!3b<_JzJ z*cRI~tzFVJxLnya(`MBaW0J%G?=P`vb!Cj^hxVh`gGE%7u5O26UCsdn6(2hUfwu+p zq`Nf^3U3TQe`KjEjEe=BdZdnKkYYdffV!MDCoRdMyVJcw^XZTN<_X5E78HZM{sAwK zvHCOV>Ib;eBKwJP`x!^EAB<8Z9GFfH@&+wNCy1=e$URQ|sR|jv$jg9G2Kmz+Zu@tj zE~5>wZN_UqOs{Kz0Sa>@&^jM_nyrr4r+-sGMNm<>K~V8=7XAdRm-zxIs4ejZ_s;N{ z{S#LlZtU{?OaroGyMYQ_11_We4PPfb%+A3yw{PdtZ>LLIpEi@Nmaz77`r|9bExoI3 zs;g$0ZE}^hiCoO2F5f`&p0q2!iv-)SZMPLHMt%R{mOC7>EYLwR?QM(s6LtBE#)^zl z+k1}*wX8ZSi?>0p>*gBv3cN73T>EhVmjpOy-=+CwE~|WyfR@6ves%&<ncM$H)>&}H z*+g3!4-$gAli=>!I3Yme?!n!HJHdkoC%C)2L*pLY-Mw*#>3sLjTC>*tgzkE)>YTHm zy=yKOY*~CG$gA#dq|a3n;hkjoAfDLV02ki7FD{;9marrXP5+HrMDtdH7gy=3>(~TY ztN%C-J<BH)4fVUreKH9jQWe?Tj^s63r^g;D%0>SJ7M_-VW%EIvTuI4tVpXf+Ac-un zbn6>h3`DvKSQt#&uB+3;#658cF(mCmCv0+HoK<G1#4afuI>IKb2Lo{xt}BMW2<@X8 zAD_ll8bohV0e40);!$~n@kQC{mtD0Dt(@}#1IVBBfA{KsTIG4pBGTB5r?n>#Lfvq5 zzsuPYz~18UV_R2DB85S6O6jp00@b8*Iw{X$2!=OIYc6Ed3z-Pdg(nz9MSd>I`<k~{ zJNz)&{mbtr-WxWb@0?1$;O#R<U8N!_X;L9ckLy&pHXiM5_01CRUz{cqvLF=yp@%ry zxnRC$x|%W(7{eXYvYwUXhrh!o(f@wkr-w6T#-&hmX5OOj+HFa+rL*se{klJNQzYpA zW~wM@pl|f+!|U9jX%6F}>9fT<?5@g58Pq*wLCW{S_s&}`;pQU}VUow&nW9YJl`Co4 zCZj_X>C(2!!MSy^bC(%;=iE6mcIRwylY3l=P<lUpqkrWU<Vw7E&x9_<iC|-5{>!S| zUqIZM=+8p^ohr6V;KxTH$LE~huUoaBtXK889Z`jlLoOIqo!2J0Yi>;OF4NGxS&$lq zLP83EVKJy9>oxA>OpRU$xQ&Da+G)P=9E?G(#lyTCI1=Uo6%F+nwq@qC_=10Phf*&M zgSrU4)^yQwV+|}9y-Kk}*KwngZ%(apZ#=>3SHm?NVeX%NkED5yw{ze#<vA7F*qA0G zT@TDRHD^hM5FwTDxuD+&LOWlu2DW4G2R9N&(y6;JBRhKcJqVj~@L;?b2uo;hWlR_2 z4trh6@@}>DtdMb8nn#nj&$qr4xuN)8u`9c^RCE0qGn$3yn`%AVS>1=I-dA(Mqld*M z!$<aVf`C-Cl)UJwOb=NbEDY1;xv+!*eMu4BbflPzXv5#@9Ml!PfGxC4p_4r+`2h=V z?gQ384To0N(<vX-#c}25F+X>KhGW%4*b5iBOoeoa8yLpxsGxu&h`g_tXX|WCF^G+S zcqY*huQH5<3-FYG!hyoCT0wKG90*b31s&H19N@@{!zt_PYNXD_NCsZrbk7{mser#m zl?7VwofV0ez0Q(Us+lk0_RUzq3NcYS_3+l@o77B@g0XQf6piRMF-w8ea}HEiRNl)7 z-!2qFfKqt~2Zhs>q;CFgAshfbMyjRJlm)essZIT_gUhk}H(0l+D}&h<Rw~$1#h=Vq z9Hhg+vxry#_~Z3_67(B*{;(E3+N9xq5u)&sh+}u;aY0Te6e7s{>0e2(!1#Oh+-mh_ zb7bkWGt!BYzXKMo5`RlzOt!<1L6n7N^WcaRv`jW9h#U&u|Fpg#O>_SsH+K3Zk-z(o zzPvp1IshT}kVtL8Yj##G<kLLAA^I>e^zDlpsI__v&lO2IkG4pbYYm1`jKe;->g%|U zTPHZ5$mMsHh!#Sw9;#G-f_+FF&D)HLQ3xy(Zk<Swed;gbc!jO9TS6C42F3HQU$zgY z)PHZc8N16wOLE_JAsdh_`rD81q=y}(6l+!)){igy>7U6o=a=o=VXUWv+8tNN`r6fj z@GrBEPM34As{=Pq&iHSRnndZq-}{+)>T4dHsf$+S!{2YsK3a6SYCTla;<^r`lGvgq z$0B}lW_)B_0h}g*o5_*TISp+v@ZNq7_!Cqer$f|po+{m!P8IXbl|D^9=o6NLec}#N z#664;!UE-l?3ScbjU#6AA?5ViL8=<^Ocm<&VAW>-(%HNJ`DU~`{!lUenkC{?`?fTO zMXoLCF39G<NRP_$y>mD0m%V@`ztDN4$4*|7eb;m6ntszdf>!-R6&3w>j(Q=g%A5ek z&MzXJ_L(T8diVA4g+XRQrv<-szFYCowVDk#d++NQ`+g9j`|b0cM_Tm+6PqCw92HYd z6w|44)wibPi>?kpZKd6PmaKx3iCpwF09DFG^j<aN3J3lD7rkkYQ6V(*2$*t_OJ#w? zYi-+4paCUIsT)-1=QoS*O@5l6mbA`hkA+Zg0dJEpl3n@k48-iXu8UGf(y1BBI>(vt zc?d_(Jd-zQ!@3CEsUYy4BB~NIwxh7;>Hid7)V0r6;reTSp+8=Bc)=<CE}SdwNYymM z;n-M@T>x-NJjh|A8l-t_tOL6fDK0{wOHiB6%uqObo<R?}n}6n;iM&qzR*s@EeL#$r zPFkEZyp{oRE)FTpjG)!;_KAF<p&E=VC2|?&(Y)01`NkO<T-@>S$(L5JUG%QKXJ^A_ z<fAv|R`Elkrr?M~|MqYGPzU|7jCywaPdH59gXwrKXN-#?4I&b49B~H*+ra?E^gnqT zmo68qwM<u788Oz>eI6)=z7SaEx+QS1Yk!fsoG64lSgX7%vjxfchc^P~6|M)mB1Uqp zLmu%4*3{`Mw&U^X#Ku^M`C{>$JKiY?ja;wZ_TTiKr!TJdNuM16e~!>is-~@@OhMD# zY9S3K{plkEv6B6Li>g#v1i~z{<ztxaDd*f_SAWr~8w!jhCTtR44l9Cz9=BM|&_<x3 zINN>0Ky?TGatm^;O(klM-Ei_aB`owUAXWJ9yg0(&Go_1P(#3<>cdn?9=+W?0+(rCl z``=p}`OZ@#wH(PR$u1Mkb?4RoP#m`$ovK@66yU?l2=U?EQXArx@rF1(!J@tUzO4y> zA+PgxX@RSXfNyCzU0x?K_PuS!AM`j+%72eK9uLNA33kj;@u!tH$J@yxZJ^}pz~P*u ztnG?0nZ*QIV`Pp2qbe6TNvYJ|Zm~z^yh@9&;2h;aA3qYq=z803+|uel@V`A#WdU4Y zto_`tBksD+nckfkzBX#kenRm9M|{^UjnMG$yqf0T_HLT<gFC|{<DATKh7#S9$H0|q z^;vQ)@40$JIq14uq<a7fP;&}F(q6Xqemi^7J3E&2ehhNWfBZmpwiBbYXo0c+{7H48 z<4yD{6)=)T>I(z2h~H_^5N|VoiK`y@;Z=Y5p&^2#$kK^L={6937po75t>2PDPdI)P zPJ^56pAyQ3BN`#C4xKX>^z^?f%^y!V4STs-XYpv>T{WPO>j@6!NT{JLqwwsOP-tYo z#<9NhLp~2F&tCk&1c~b)p(>-gqzK!!!P!u3;-s?uOlP|i6dV>2xDuD;aw5I{ZL~}G zA-T86(0L?aaG1Y2sE1<xVZ!HA+bPRAz<t#TVM|cxDE?p^V6;oW`lc7PcSz<-yb!a+ znaLym@Ks}l_lO-wIyAta+dTi!32&@-#g&DS{c;KR5-?NCcfuC8j4~Jj|Fw`<Hzb`z zE+7n8LKNK`PUZd0%>S~e1kV{9-k@iLnoh5sJ5F9|#3`=0K`BMW1V)U=J}l)=7oCd7 z3~HhJ*2yJ;6zi|GnOphR%)FA^nlbgUZYXZVU3WxP6VE}6Y8ZG`Go1Yz!g4HhQGLlG zY&0D@nR0u!!PsH4ZwCC)8{~@3WUZqB%-{@CE}Z6+fj&^gT#<hZrpE0Oz&(E#yz5Fa ztM;Cmm_p$u^PPhMin;HIHns2ipR5amT_b<^v3c%6^k5^+6H&FV*aa<|14C~bHFg-P z%`Ekve+7soUT+>2+Q9eCNo2nP(UCHp_6vCoJi@%5Jpc$0aVgxD{@TBVCG2oWLKdlF z{y~=?Ey5S5*NGaB>h3kaA{#1a79*ewt?HMse)sp{@+*BIozbUvzm4jbnS)8MS4Wl0 z3XfIXh~8hkOsqav0+~LG!gw9^t`<zKqQK{^r0PsaP!}ote1WyOyf!eQ`cRwWC6}n_ zQ_JosUqKLRijjs~!ppQ18jVVR8I{d$F`8>9_;0Zo+kWhXY^B9FQ7fRzLX)kN(eZIw z^Un@7njVVmu!eh@^V{cee31~tT?)e}H%3D6v2m-A%X)iYrH=32F6%I^`B&WpL&nmE z@j@y20>0TdXT9_O3VGmaMvd!1mAT!3)LGZniR6W_3$@wjAO*}IL)UrEP5k$2hH^1s zhgI27c~D&4gAMJUxuy7poyysf3N+XmsZAWG24*fcJv_L#M}@JVK`E+gdA#$$9_DrU zUXVq~p!GOkRPT}1JSPZxFXys#Iv^phFjPndgFxU3gcJv(nIwgvb}8zIOnp*R7*6|H zTg@q0B+owB#OxCliR{{JKV>i5)<0F5D_!|F@k)5BbhxEMtF#$5EzT${y57L4G`Z;1 zu5m|jgbJoRG!~D|6o04hlF_PUkCe-$H#%OC1xfq-wWnRFU)3WwjF)$^SN~TC$l%UK z+`Bwu*Kr++H}N#awj;#uj;)^az6{ZkTRo>tvDDp|R$6i8gn&XHvn(yHCkL72>oLw3 z^qjL<s541nmpi3;h|lSWg(-dDRI9-Ji&s%-x;#ER_sl7`=UGvU_uWMLzMD~+t2@tK zDK4)!x9%*MF<-pp&^iFJWY{ttR@-GjmP1Tj;(CcLWY;R(jcatFl^`A|um$FY(M|zA zkUX*~3gYt>^sJMT^>)S=VDMSo<$M;wq1L;Ls@x75<sJMr3kVTP0&;vIRUS|iX|688 zwoB`en_SmyD)Hj+ZYT>0`kw--S`%b;Olk1*il&{MlNd*(lRzf(K_On+a31o0xYCo^ z6`gm3KJtl^m0Px0J=EEfbMsI*3+EXJYr9u~S0gV+CrhWA&)%vRm=K$~ueQ2jmcjO? zuuIulgtvy2SI_5<=~kCUg!=Y9;n4QNmQSP6yG4UyusOnzt%3#qwIaI1CbykIqwrp3 zounsdj3s32f5}Z+z3n}Y5XPhs!CEvgP?U2hkBQv+ecSLHqNVoJD6QXKAK6UYQT*bq z!$6Ac{m7(27kCx66ILG$6T{f3dYM)7pUe_Mj5B)0YIs?sjK<{}5in)~SE{_C?yG++ zT}v(ekpmh38>fu8Xs0bZs-h~ehSfGxAdB7#@IwZT2r~bM^l1_Y`X1{aODWLdClAN` zI8lY`zEh*kb9<Z{Vu2B8E1y|cPz}uYP3h=clvivLm-&Z!)KfG5RxYCGZHhBI;BG{3 z;Wkx+wNX1A{dx}DIz43;IyYLj`kxKnr(x|LPT6)sbXzvvAnmS--s(Xvb49XmsfHYT zfx4fAr-|O$IhuLqbjrWh>>Jl`_R2Tc+6W83hhDOzswMZ68@1jlorj7#bKjS+nhQSn z-GgY7_qA*DOz)Z6G;`ZNe_RDhFeU!368KHZg-}^rj^|RX5U(B-;F|pHJMuQ5jkK3N zF~vGGxvI<e(Wreds-(`+G?3T+Z=slUkx7QyZnef(D~m9T1r5vb|CHoNd+by{<QC+_ zt#bc?RR6VU_<Uvw&YTM{s%@wgI*PgH6A77%%>TGO@uyQop&w8=Tg53?I^l`C`A}&= zTto%-knZ20A{oS89GfjurR^`sHTax+RF7YHOv3&N4O`!J)`SuFrePFsXALXThHV%! zkQ`)n^56N2EqFqvWBco8+MDGw<>ZPiCQ=#Y_n!ePNp{bV4-Fe_`|^<a+ls#7kLV6j z+<_)uxgFaPo-9^WFSK^gBXEtIu@-E5R&Y~2E2}FIGrD|UASXxjCn%OkLC36WBY5hx zCU3#}I-H7(*JnQ>rh1>4=F1=-_;TQCoz1p=B1+B5g*j@0UYB>iS@Wk2xpKXzesF;( zfhu;R%zQ^s>CCvcft8#E2|+e3Vb`vR#_IfCfg*u$fQ3XRWnkQ5S~m{sC~5fL2}U7l zQaOADz+V%vH+YbTagfIcJO;mDniA!YaLUw=WKZs6Dbz6^>~ic0>s;)0z5y_w^GR0F z4Rk!!P&fnG1fh|64sMXJ?SH@<7qITj(7V1^uD$JCYjbOB{~!i>lvjnztj>b;^-kwZ zVgIipch5hG45JeRxLb6|D<pO}Cb@V!ikf_!N3D8Umm?E4;_S@vr$Nzd`GjX(?$G7h zI~i{Ife~;}8P_Gn3l}PUNcJ+19?O>9PkBcZ-9Z#F*0uFe!i(p030$`mo5T_jcP*MO zq3=2^=wO&(W;>b5@3m2`a?XiH`j7u*CQYh_ViaZn)ItAj#Ya$bgqyuwK=X&ErtH|R z)p^bpiIK06nbeFRgrc;sf+}_*LSw{@j6P_Xm0~fx|Eoec<0D2De<~De(mF2nY(bI< z(CmsPlVArh%?jO}lq-hXb=OR%IehoP=4M<&&S>BtAXSowl>H{UgZAF8Rb%-uiQLn% z*vgFfVrUVr(}Q)s{Il`;(O}GLDi+DToD2)6Q{A7YRMY$|i}{bAZu=1&+07o_6I5)t zWfOevHiB;wsJn#Ie02?Ey9;Ubr5-e0a5>1fQBB%HBYXVH#8P^%{vKY=i9@wQ9?FzD z(Jf`>fRij4b3WB3k&Qy@qj68GX#2gOzwZhy>C{<J_J$Q?=I;%QO&gz_i|WWW9kgku zS=ukX0T0SPcIJTSyV<kF1emluauM`4#6PVr5}!-L3&MY}^WSf{1~V{v)%Q>n8x-`R zL|y<)5lDk|NrV9*jRV^Y+{L!})0`uXDx*2-G0w7vKLU)8LYORG3^SRi$&7~c-aFmr z4N!?6$Epl&UtaA!7bflaF5{G(@R&Agc6^)rLZ5LqX3M5^C7TWW{v!pd^Z__MTzoS3 zoVauFYbq`y42ZljLQjW}8f$OAL^ZljXm~okHV*@tGku2Lff;Mz1!&debtrYEEfp!Q z1h<2!IEINwTyhRBNGhl`FBpX+KT>7N?;ek?4~#@@7$Zm!kPHir4$a-^*7$E9&<o~N z`t8Ek%p&OYJm-V0vI+qnr>e5IGI*shTzWihj$BIJFx7Cwyf>w)33di@O;B@{>53zf zC9P?8!0c}2#o7K|#$Lo&dPi%d{Oj$+tF=&MLv|oyqfyONJf$qD@7yq)MfFW1YYub6 zmmN{yn*(t4x!y2D|H*4>3u{Brd%1`u<v#D^yicsWX;8WmKj8Qyz}8tlxnj)C1BLR# zj&%0cT1|<Yu>XP%LQFFxkg)^Z?bl^CS<}}p2u1w)T+?Ui)T?!Vwd?Io%8-ARG5VeO z1KfLy(lg1s6LlDJmN3bOAx(E&`}D07;{$~?@JyQecaaEijgWXsEk0Ro{(kwM682X) zkab$5e@eK+sV0xpbd9wOqp64cUa>4!z4q^`(JyqLf2vxgH6}>AJf%KTD^jya673cw zmY^ZbZ5moGyZq1BV{ufM)|icq_8+RL<he)uKL?OvjO8e97GYl!kwKG0e+b=SAXsTl znoP)P9zyV&Xr`M)Txe{k%I*<-APV3?w`I@Q%U<2nUTzjykm0X``!YzB$73Z})?bZ# zGPeVZO0!Xqed5z$+<mz44h|7DUt=}98e`Hqho>fe-Zto`g=WxkgMxFTE6TFC&C$UE zV4mZi4O3@N-Tv99inQ}W-fIP@sd~VRW|DdA{x+U$dy@O`a^>@3<Hf#6fvnB}B_0i@ z1Ly>o)cS5i$&YQVY(M#d_X5Q+A`AcTwqQh2ym0fKJR?Kj<d@;wTEJo=bM#t?bLqU6 z<R`EG@@E-y^fUW9A-U(VT>Dv4Ohl^M+umv6G>XgWn^G7x26P8xzcF1$QcH*j;ZjDt zF&i<L-36D)<iA@BWwlNsv3ZZ#Ngt9AFjheje)SIq*w4Qi|B>?Y?-WuSvq1YoqaF7< z3rVNi@x`pkiZ7_w1PX}s&~3@_&m!Q>fpQ=b$+R@zF1bnb{(ECK!I{NEBUI26iaFB% zsCJ{pYzZ>;xe7eF=nGnLxxeszxHpt%G0}yWNb{=)RfMbY+GTi`r~9wexhqE4zr4PB zIDw(?+)CNn(ReC%e3p63O@$f+k7cG#C`KLlaVzpRd8rr~#^JwH9heNco(4pweS(`~ z7`rOcHsqb={8;U8rPjb(2&A7@aLO!%AKC5Kjivd%RV2#guKDLq)kJ-^RitEU+MLH$ z6o<4G3b6?#_hy6-fvSe*Cqp*rOcE0+eLI(#B+<b@c1TUinRRr=)t&)MpI5Z|en9Gv zVvm>Jd7Yn)3CMbuyonu8@o{h(zAT5EI_>7NoPQxgne$3|)VuTa?CYoYJFh_lk2722 z1^Y$B-S?<>)&WHZp<TsXY(;tDDY;M1cF}ox9V>;Ax11a}>6xV}>!J@>#>>VNuV+ME zK9^DmYmM(Mb>JKw#B2-4GPvMjNV|bM_ev9nOzy^~;-Xge%T5_?<JB-i6wN<EH+`PX z<?d=J4UMejxGb6Jscgoq{l{Xy4?<+m!P>jgCjR<#O|n!uicuEF{v^U?jC0N2eV_Gx zZEw9EOfHFKeCue!&=b7VIhVK<rEj#Z3q|~EIaV>3)&7Ie^$WuRa*D@vY}2%p1(zxL zuAoAjr?PP+Cb@$5c6F)dzz-Z`xMX@i(@Gi5EmcZ-+c`Mp6bJKFvIAAHWVaQsZvbvp z41i+-y8A$^)Acbor>P25)Z@?_deT#?r}{_wIi#>c4d>EsXksmfWwF|NH?E?IvE6NU z+Lqzm2Qj^HMW)+uRLyBQNmRqNgij_QaH%DNM>xo|Cz`Boe?^1o@ubdd%QB)1ao_{< zx3Gi`)dZ3N_iT<-S_3oQFD++mlwl;+T)Whib#Qc}dF3zDz1?)>37hnJq@Kaaz@2Y^ z_&gnz<RDcj>!77ViT;njFSO!z)U@f;XpLV$2h|#kr49&G>;wkw?B;P^$Yu2T){UOk zB3M$XJxMqf00!dca`C|qFc`@m;CL*o%ZsK_XbySzhlj9w@oHkNqNjo<s*t-tPWnhD zf?-ar8Bo!hzlDQ<kS+RMe>=b6-gj^I=-KCB`6A0<&*k%gBe3mW$o9Eh$o9_4<A#cM z`=QZI5yvIcGDe6d(o$H+WEYL)PlRPXdbbKf`49i;Jz=idK4^pfZ^@tnV6b?iG6r#1 zB>Ybb9(~dt0(ATFbRowz4HmtJQe*1(tX>B_;{CT^Xj{UWkjlc-`;qGaS1r?neXlyb zWuKahMBDj(N$F#w6ASr8<}>>x8a!&qCiOp-CnrZ?oEuMfCo-{My9L|P?nJbFq~}q} z+pY|VoiG3>iA0rcOBZ4qzOKT7f*B-azvgli1jMrZOvxD<rlh24O7y^SQ&Y8ibu^;` zhQ>o7{NRq6wR?#4S&zLeeyq}OL{~Uq(}CjgX6;d1L0qvf@OhkErbaO1b?T4MLRRf- zUVtY?m5`j~i&nvKrG1K=Vz>Kbi~}DzT~phm6QbbJv!$6EREO1jYuPNpRG05!6zVS( zd#kRhJw)R+EUF+~$wJCO?_VK412v5aro^~)-Io}nZ?s9I6sL_J8=bDDIF5S!{iEUg z@Gu1H8l#j8(}>EX5orMRD!~@(53`}h5n{V}WLnEv@NV07W4Hv*^;z{;bkL#djVg$f zPN#4u)eoQY*sxbFgO!B6i(e~kV;)o~8xb1~ILT~z&n)e&0n?@ZUlPudQE@Z-{oHN6 z&yPtA1dP944Adh(OH7+8+Lo&{OVQi@%`|JfQ`P1i&h|>C`jEpvP~SVzd5rpO@X{py z^=&h^B|g~}p6v727Z;0Mm|}g$<R6|ADkO3xSD`aJNhN)G!!y*C&u<M7=-(id_v}%e zI_Q~i>-y`_G_N!JWl5@w?h~BcQ}>mla+LS<xGRPP(7`?mqKFSOG+2jU5s!-?W8pBJ zeCrPl_ivl+vrjd<pZJz`ik#)obelj49`^{&%`KH$UW3i?uV|J~3Lb|OefX~Qif8_D zo=oUTi$yD+30^~iT_{6Z5t|_xN6|FF?vYHdAuEo}2Zjj8*3OonZ#!fX<x>_onFexG zvFyKsTlb0-bYnO^+dpJ)S;Uvv=1e#T*w*Ei@n+wW?cUN@+HQ6(r0&vE^EUJL&*18& z8<qC@>uC5MC8KP3$!IQZE`=_ieD^KlpHt&UreMn7nU7W#<ga*=X0P~&M~`eYSX>cF z^5@Hpn5)jsPpNnvWQL+7`4op?5M5cT|CRaSv1W-K@pz2L+-UMoi6*s_dg|2|IzWtt zA&VOqoVC|n?89lHM?Ry8bRmj=b)Gb>Hr|YA+>lY3>hOp-ds6UeqlCycdWX!+j_t%5 zgp+fhW7A79iC~d2HpF5S;B)I`9?R=Jp-4>HmMi(v?x?n$cXf}5I%CmNAJ71(!31fr z6^<W}I=g4N#yrfRpqo{ROUk+YzuM$~n$)N$?T+zs7lGi_Gv0CvZnAMZrFGy=8@>O; zhaSC=BMej*PD=fHqX<Lpb>bc|T1_`Z1sXJ>n&Uk4ao<O!_^e=@P*B=u$9+S{p10$; z;X}<(o0pUJ`{sM<SNrx?fd34(PAH0`&@~NNmM1PUk54oL+0J=|mV5`1T&4+0xi^NS zjett;QtoU(&+Oa7ZbIgQ=X4am0whl&{zEA4j(Ts+OPCCS8Oj$)bHs+0xO7FfQ8WM$ zB0ecMuh0jCd!UGfkQ1=F$ay75$6W?Xtv5zh<$lD!AIWP2ALaIZR^M7<W5f-bp)Ab5 z8o@>#9L!yQYG2~t@~T)XXv$n7n!Q7+JP8W)r%eqZCgNJ%>CkFV<hHzhZ5Db3^`opi zi#>div6(pPbZ%Bl12xqb<)J_1?=&`I>$Ltc{4m?Q3^xj!6Y@h|-TfwC&=oneyv-hB zSCZIZcc90Gpn<r93DPfrUJw(8Ws?HD3BKL*WD^Q9%6a*x;xsTTTGihy$>}HthcZKf z&H`vVeng8(pUQ0_TQnqN8ntoKa#?K>&o6@z)2X5rHc^T1&$ATXV;GWOjF!KaOGL>E z>K^8}JhV)vRX5SmSm_Yo(fh^xtsioYVmmwSFkS}4%?o3OEliRng9#>l9K}Ur<)bYP z-m~yt&o<|Mw9~sB1}tZypSDFpTdU9g6h&bN&10%b_f6}#(Fkge4Oq0ocuemEkgris zv6{GTk2cx-e8DNgJ6Xco)A|#K4{)U+la&px`WL=LePgW=Q~_CymOdY7F1JD+88>ok zFE+z|VKzdFLq+S^u>y%Lb7T6xVe-#!iv8JrPH064b&53Eo_0SqP6x7ZQPw7y5c1%Q zw*8AJ(J>_8tzdrrF9-uAS<d-8b+m&iSQabyJpSh4`}SiX&8ge&E<_u?HelA`b>1yy zI9TPd@2z|$^GN6MZYQ}dmMlw}A>q~!R~zF)M093>fM<!Q)wJisJM-s$b8#_Q9M7nl z#k=5NM_5gUh0wh=khdsl+8Nhk6_k>1=C!bg=W{4jTru*gHcfShX4A0P0uq9io^*{T z&ge0ER!}s?Tt1Hx;${)`D%f$to4z0xe`R=sBJ&#km@v$UV{H<r(O5?F6AmZwgB+)! z6?x08%yWMzSl@LwzbS+F)~PGy`PDkfkV=QK#O>avIoR0T&{kj_VfZHNRj!zC4AAYP zJpp()cht~zI!SRk;pzVQX#?ZLULy_6_h7lag7hB43A1)>CV<qaLqG=PpQ@0N%w7l& z&m({3DV%%KI3bx<JfNbV@Zn`KeiKaMN~zudiBe?yazBdKb)A32g3tcb2a!Lts~c{a zH;UVt{dA;U{Mxs0+O6QcxMuRV@9;AvR&?7Ri@P7o8I6KU8-%TPpyi}MW;XMj8jTo? zpLwsP%LOZ3^qdtFe-%f5(S;{Y2L)$&BdV*C)*JJew;_vRa~Tu5hlpfhmBUM71yN;3 zP=R7H5E4tI`lmmrL~i2#`MXc!GBZ!C?#GEFJ*H<B&o=mB>k1x2AtIfh^k9XUxN!CR zaQ1x$`>T!y^H<p(`tfG?aIeM`^9a{Igy5vxpjQ>0^<2k41MQ-^Sw?e9|2!_c`-y&T z6Y0sMY*SLz4__S~h|mY?laS5kn%I~+8B8*zbg`H}fRGpYei!rUV+46nXYZ|^wNfM} z=z02&k>SBobka3Xi)5#imgPd)zyS<#=pbR)4SZgBWOQ5jAeZ9s_{5I68+GC#>qW7M z`8kChyiUu9Oyatf&utoCRM}Sgzx;*%iwugibSV*DmAxIBT!qR;M)^kl)ya%Dvt9Gd z24tMcVX+#4mIV%u*x$LPY+_mpF=SdN=;6St|5N*#D`$ySR&RZ7`&sdK)eB=!;>;i$ z69R=*v@z!14c1v;Ryb@%+RAm{s4%9{0Y%gAS?{}Za(*!)hP89$*!h&Z6}%(w<<5<w zxu=VHD!l9@JIANq<l0L!T2eVu_uJYoVf%APA>%WQftBTQ_`u0CoRrm26uT@HY%YG( zgZ{8*{2AG;@4o2UX~IJ{gOJ&Dk!7k2m)w7)ni9gNlw9z5(=?nM4S3|5#ln)8>+Nrl z*S5sf&to%ymZK)@m=7Xw%bj;$`lDzTMi~-pK2fl}Xl)$78!u*~29ET>8(i{>0{X1@ zZT^(_EeA=PvZfC=RtswiC*Bu<Z?ysS*y3i}x-<0?IHX3O2;ZkaQ+K#Lr~=ssX>umh z+kH<*NSaCejH6#Tw{7jvuo6QqY*Wox>j!S>s8P3x{4|SBWV^H<-e$~<w&5}x`*=1% zH#w}A<ji73pE}4A7z-GYNELsxZ4DK*@5#x~I_wrv&sYG6DN`*=e(j2wMRY0wp`{rN zEXdv4aX{TDv{yO}BvbUQ50*yS9lz%pOVnjE{afM}%Ru=Yw2py_om?6!(*gEzCc(Ck zP=?UX1Xva)X)EwbC}M@n%9?65P)AHr(;mXK+a?IPJzoun6V(K0hSBgNq!BgCFI)!Y zdsHbQV^t%e1*Ueqlgb@gzjZ|h+mT0{T=AOlr1x@86L6xj@iIrR_p<(KQHAqY;`rvH zx6j^S6~}C+2)}%c!%UtPk0IMHTv;6gVbOmFWSZKrcZc~3`{C&(eB3c?Mk@!wq@0=Z zxt4zCAdd%ESIxrd_1`rv0=+|xr^(uD<!-!(ev4TaRg@WVXQs*hpA7b}DjM^xWk}4X zDoT`2D6mu;BVI|{Mrgl>^ogU$%u=WXn4}~4y|n+Oq&@Pt(rBNKwH!iMMQ~Puf#+7i zdt~edhyEK9*$k=WP2ndT6|%}2qDTO8D3&}Q)M!+O5M=G)WzfPANTH50k!7pTu;cAs zn8kIQxuTJF`hKJn82p;_)oX`BY0rZ<MCR{&2_Ag4(BkE}-4F&`_8|M^O77^heXVP@ z7c^d*xGu7Lfk+x12MGl3QXyFz4OArH;Vy_Dw?aCmjizUs^x7CWJ%%5nsvpjzH|{XT z=X!K`6-cCto#a2BUY>L)eFQrfuXkT6Q;ZX$(yAw-W|cO<5U*Yin&bJ&?!kt)tEQRG zQ;dKnJ40&r&!?L}$mHWXgR8oggW~VdMlMs8ZmbB&t!jp`>Z!M}gxudy8pSu;=8%+N z5_1~^*V%lxEcREa1(q)7d9!xWUtR(hqV871)T_1(W-}-1<s}T+t><bJ>uhJ4WTrv| z=nbq1=a%TDD6K`u32x|bH*S45LQJK2O;!S&O3>w+N9Ym_f6ds$eIcd@KLL9beYods z7QFWNdIHTxMDuRehD`gP1s4p#`elV^CLiLSI3Z_t=pkR(?!r*fo35>?kM@txh@8#_ ziOi~J%91@%^Q5)@!9b)q$3wM}|5S#njoCT7RY?2*9pFP39qgYPpq}9D7dQe>%%)W} zZB-$|(Td@GR#`MubpT!M?wdvikb;P@1E-kKuNg1ylHBa=+keZM@avv?CN1u=BL0As zo3yOW4;-oW?n<X#tLL*8;Du-Biym(?)!DA*x#JSRRD-7@McZTOD4s45^gIAb^tg#X znDjaMHKXs{mv8=wkW5IAWIe%h%|U*gkGcoTPRoey2VR6h-zlOpwLPKYBzyYZ@#TmO zT{&Ttx4@G$wYd6E6DnQ>!@b#QB&;i3c`ZqJ)q+M#+0-O}k(Dppzv+yFNN~U5Efb3H zNJ8O}2ru$W9!`V&Ml&RBq!^+s*zozXQYaE+72?o4ehk;QOrb1S{Ce=Vz>4J?)(WN3 z-MpYJn&OW2mg%(kfQOn97Az;^;;Xs1ds>N_;uFfCGFEG<b1hofq;4B1|GZ9$p{jv% z;NP9XUA~8NF>}m=$FF-2h1f7Oa*Fnc!kzp~?&e!FV0OmdK9uj2+d+=~&~DEfo;b+s z*%BxVo`5a-qRlYT;_d_oL3_rDA)b5=_K;f5{D?_S7vKuvgymqGi^cjy;ww_y=c9yr zDXW?HVkUrsmch7uIVa5;%a;b8&$f25cvc~Oz0+mLsud#LbT4d9zwJ2zZX2_e?gbP) z9-n+Ir^Ij<A!}XV&5$V}d9ECa8Z?0tmg*FXok20{QOrl@Qx_KZu115E9ZtX`RII@? zOdE?NTV0x~KSvzqA2^7c6O}J3j6Nyk{L<M=ZQPOl-f8$qRmaV;{DL0ZV3D(qL#wo6 z=?r2d@_I-y(=9@&`T=1^A(GL~(+$M+1@Qa!V${$^I9TsEJE+<hXbN2zq;uLD@v8ue zW5BcU<NXNtsKa(sbihtRwN(P7xDF|Y8J2O#d>APySVOf<`(Ek*Jy&Yw2u#)H(>g+i z)YhMo9UJ7RVJAT`zDb2^?_=TG3-0~JskD4_GzFm<UVFY)zN>2JRL$pe#>jb%vVk&) z&{CLbKUs}eiK<SgQ*es}xx){Lag$DaB{HxeX%{hFES(1wkv5Jip%7{NyxrsccgU#) z4v2_1OeortVMt+70WAkB;`Ps%Jn^9hLDYH7x0Cvx`fWMn-?WKm-l!|RSV0R#XV2ct znXRF+^7Vhg6^~qK>0FktZ(UkX_XiU`*GMz^0cm`&gT_f1l{&V^80*13Hr^M}BE-b+ zLsM?3Vmu&ysyqDB+*f@m@3V|_X2Tukr`VpjthQ2^{YC__fBXVsJ7j!cKg2^Qp;-rU z3B%Qt`BQb>5MSM<G*nN;j+{){ct8@(is<JDERfUb8*S6DP_%rE#hMF<kdi>S0b!Pk z2(<J}k3(ZYmOR>YIB7C?j*^DlSnXyFlN2eLJfsY3TPwAOac4*>U7knVvgT`b?$?26 z!gpO3Iqj2ywtg?-8@1QJ2N%|xo{v3rAY@vyU&|&yIO`t?Dr^sSueb1Joz-@B5TL@X zC(#-<;X<M;UmE&BTf*%!chzAf37?=~xHan@)<J0oV+S7;z|bXDhSTY!ifx6Y#<gI= zaK*6&d8e2H*u(@<M}?Jwv7D9p(4_ihu+eDt3r@Pw0qz|CwF<o%I|jP1*%&*~wluZ0 zWTKm2Q0kuOLzJQtb?50YovUL+V&_1Y!;l29<GBoC<J;3fi6Q9N-|yGCe8TUl2?&`d zJ8SB@SphAYI-zaz;CZrTHNyEB@e;q>^s9G8b7Xx3cYZNMJ3e-(U#Lb7Ab!IbtY7}> zdge>4KqJMt=DT{x#rxWEo<e8Qe<ZtP*$LeiP+K=$+3P8piFo`(aH6o-oA3(6>Y9E| z^!bCBK>FvPM3&{5(@CdQ5k*Fj(U8IC$G=S~A7RFPHQLbws7ZtTm8dd;q22&9CL1_a z55ONL1zOyS*?%DiB!rYJjkfZPCbv;Pon(118H1VNFlMqpDynK<{Z2)ivCEvs4M?$I z(*Y&2F(rBJAzgMrmZO3pd6Pj;_Q?r&5=H1U+W3L2_rc>KrA^p(B3*-Q4W|^e9xNL1 z$<LW9ngJhMrALzx^pPT>dg^!lo}#j?mNL^unYyaTBQ|byVzEO25v6*C;c0I5iY+Bv z{MIa-nuyvs4onmzkRX+>{<peb0TdUIJNN%45)}88PK{+*5T@J35x~nx$_AK_hOnC{ zz#xUwqy4b}&5E#Zh3kcex3Y<l%+wZqk1##I1N@l&7rgLK&ygEFvUc0fAC|uAAVqVo zJd`7%hhzXL>^@veTSS=7xDC?pEeMEw$ax}jJckw(<>(Q1uk}3Xb&ubp;i+(cmY&gP zvtciU*-o`IZOs6z8XjcF*e|BW8tX}&5`*f9d}>vMy5bKc5b*g5ka&t3r|WXQ_w#ce z&Fl6fB|AoAEkjZV{~e?ZL%c9y+CTk!ZNjQkjqgWok$a&2=i+3eO)RNfjB0ha_}SKU z6Eqks55e;T!Q-)IGy&BW6`Er(7`~}g!rT7}p(HB^EL#9MOb>Hj^P4bj1SG=>4n?}D zQoX((^g-`{v)|7k#743#t|5V@x5Y;+FYp~z(X6LNw#0=ejs+be(e1om4AbHbxgCQK zBrV6?kZIO1oyWTFGbkYtjF%Ti5#P8P_t|>@AEa6s#umDl!?xJkJH?M2o%beTcBBwI zGgYIKWtr=GVkcqGM9Hew?4K*#Y3lnzgE$WLh(HSwzqF9&D=L3{xUzyAq>Ud-<$3CI z=ehKi>OUr=C&W*SD-a@%>l`IIaSH!m`IL^MH3Q-rmkfbrD+C<3zL!k!<sfoQ)<iO? zBYG?;#c!~C6AG0^NH_8@tv6b&^rmbD;uaapb9d`E?P*JLJ2c*OIH6go+!XbiuqCWy znq>$4g~*};h<<l7Df1Qn7FSqpyoK;JuFs#1)mKArQH4DB%XvtIab&B6@g`|MM9pY? ztIgDU4qE<1;cW3>OvXUca3@$;{2F;fZv|8DDwkj96nZyoRJqP0C0j_h?_)z=pPVNs zC$YRE^Qs?K|8em999b}Bc5kVs&N3)@mA>d02`$J@t!7(1JQpW|7z1?>u300HlB8)Y zZg1q#XbKJ)tB^7-^$0F;E20|b>uz!6G}Aj3QR*5c+?w@Zh88N~Rwm70J?y~tYN!iZ z^)5-_md{gwhr7#DI~|c(`Pwfv!`HgBWwAs%tBSQN95~KhvM{He8XG*wl0HHrjr@)y zHOOXe+bMP~)eI_E`{P}R=Nn<9cZy%KP3ZQRN#Uj2NIvh&L2>Bq(o2OT{TTVu6rHf5 zjP=X%gN9H1XA5${`4K!qL|ie@O%{TFLbwN!sCI``U~7&N?+B{{EJg*cRIDu7Dgd1z z;ZK-QTP0sfV|cIVViFoUt_Y76jAQanpu^A2M9nIAika*bEWWR$M$jXd<6;vToZTSX z>(C=*@i_EbvPrH}F)m+q7UsKP7sgB9JxJ!sUBw1ti)&AZoUyij%v$5BHgxF;^MU{2 zP73b-SNy~~RDE5rUM71Im{KX$XDFqB7cJ#0T-({eDAVOAg~j<3*F3#?3Eok;@ZHR{ zBtmPp8WxaG4>4V=Ip^Pi+QV(i6!SAhOU%cr-fL(fbkznDja4r;gqd;%-(sZ5%pd*H zQ)9GXuBsUIS$Iy$4U0)NQmjSfLp2Rgz$1`_f9*?3{RKlvEy`((Z0DsHVG%SvM%y(J z>YsO&O@d`{o{x2GYf_q}R@FzgW=cKFV<z4B2l=n*DYfE5>}6gqiPi@Sj;F6%gplb4 zrR5c0K#6sAi+_u1PYrmAbK4CWWR3?W=`jCJk^PzaZLWj^tdEZi10qUxq{s$qv6XxL z^;jwAoXdRM6)@8Ljh1!N|FL_^c;5SR8@`IK@Z>S@@^%Fr#aeIuv1>>5ahul+=|F$O zDk-0wD^%SDn9L9WMe|n9rymC{&y-w~8KN>xCe1Fz-z~t$*0m&81kS1brncfek1-iw z7eS*!LtuVOIrxJ+BBkl}6_!)MqUl=zd_f_D|Bu;PSh<MpG=!7k;ZJsAmCZ%rOU(aS z`cRoh6N9v{$i0~}7G8?8;s^r6y_mXp)n2I0_+hWGz8GineLqDB1-cJP&vbe`ZgbF7 zWOev1_@KR#qM{c4;8fr~8)+;WPr5Q?$w?QQ0CbZ}bZn<c@V!brZx(Hf9PDQX<;|?O z%>Mr^EJtDRii6pTc-J6YJA;`$sr%?i)e9kyYvE1-=BlI2Mc8G+xLaR-MD;m?EA~25 z&>OwWC#^wAN#wdo4BGhenK|F`UyW}qr}382fR=k-qqSADsv#!PK83`Z=wx3b2EAMa zojl6T#)S?l!kj)HK;SGCJ8j~JvlJ00nhZtz&nkvx`oDx*M}gQvE2&iXgI8Fo$VM&n z-a`*TxgRm68-XsV42eqGlct+Gyo8KrU!&=fNu)yVptT|`r+{FKE0;lMpTnC}gEr?W zjL#Z+WOz1y-`%WxTsb)bJT5DGrx?&c7frwS^UAsVEP%)BeWljj63Tw(YkrBlEvf-P zR!$(ZcE@JL*(V?AK<Tc+OnOAYsrUnBnaCk#DwPZn+QhRiZ^tz!?h_!4oUU!R8QaKZ zuxUUMQ-wiG@kWJ6V~g+m_~Ufb5E~fgi!4f>tRg~63aMp$PGpvKi3<*g5L384p^)V_ z?PVvE`w>hA@+D?!igWnyEu*tsu42+cb57#^z$5S7S$Emu!!FU^+*P1N%XZ%WTe`ve z=a2Z;=@1RlYSF}-3M|{D>i`~SV9xAxygGh%tO#EbZC31Gnn~j(YPR=U2pZKF<AdDN z9YoSX9`&9YI5<p@F{Kj5;hlLE4vv;+?3S;m6!65LqN(T-UW8F8fF|F~)(d~sa(nbP z1uaIZ7=OMtH57r=UV6*COrX}6<)U27FIX63)B>{7bf(A74P@52qt{{`_S1&b4B=W; z#XfltCG3?2B!x?H4pypgt9P}7AxRPy0NVlcDh3Y*x$dShEYLBxrv>{*#qVYMO3lM( zAoFoU*S=q|-V6ZFHl;D_BO*SrHl+RqhoMQ}kZ4VZ6|F}>TdlZa@nD_4cdVf~fDs|U z?CR<>jnC!w<6xvV!04!vYlUp-O_4>oe5o^|aY@JWfp_H!?ZRS*p@SEGa;X}nXy?M9 z?bN4eq8qO=i$JK>pTqs`_XewAbs=_L5KtQ<DYUJaSb&yNYi=*cWGNzrTGvvIrmG?O z7-rfTt^r^BI8y!-bN_5OJs~eZqe6!1()>tg7vCM)2x$`H&!uXt*TJuail~PYttd+& zoQ{<xjB}OfiznQ;648c$*{djET;b4)ODT}*Br3Fjr2Of8*p&Hj<gkQLpxn;l&G<cQ zr_Vrljl09+u+zcEW)FB~)f(RQ{fwAg(MKBm5$fTjFx!haH9~fgggQ6i^DaTg-9-Gl znxy3Dhj<7T*ZR6-Eh(uUOS6)a<ePi8sgj95AFbc{ns8~|b-hxM1)@BIgjyYfe{EID z6VCidAVO%Gzu3!AJFEgP%wF7jR?#Op@1_VZ`ydHJERK?iwSF_}0)cWq-<x1WBMx}k ztoD@$qqBMn4o!}923SlWa<FKjsFGyUjRY^fy|W>rg_-OcZVmgG3?sH8`t9Eb!f$Xq znrzsDN;S@m{@E>*-}^tjYl+eZGr&|1dGS7`+?h(rd#->jv!43}BXK5=w1wX)Ni=cM zG{OY)E=}@_zHru$D4bC~iD`U)jK?B{g5+5};7-3pwN*l*4Z~J6wobCh_?&P%;ht7| zt3QfaJ49ddeQlZc1=Nhp(oc&kf>B3Y7%U#76ALRwW@5=+jqq};qDiRIfV>8Mh|0KM zlTyQlqhWI)9_uk4Hj^!<s*l6nRY(#*6GfPmOUZTUy6ip%`eXe90t}OpkD7=^3@X^2 zh4AdDxj`bnxQO?qz~Dk0L$l)ov=O#7@Ge!frAJQaN*xO+KL$&R_jWs9O5FXmqqV(G zKr)E5hi2!MYf>iSdO%jFvEx|-L~nkP(&+Yim?lR3qBJ*mFHo9>9%k+Bv>}(0;Tz$M z{_T^!K|Vxfcz&waez)q#SHrB|SnR=})&D!7O;tC4(RUCEiFPz<2M09CvO=Ss0epZQ z=&nyhtoxJTB0crZArwUZ;VckA==B{PnWNP`=7y_eME;yz6!2W<m(6jFxr)dwN1&Xq zD+78xoO;iAu^R|g$=sD{_dk_iOj2#q2?hs#ws~lf+bLxo^NTi1uXBI)iC?5fpqfH1 zKF&BXj+&6D=>xn?5?l#0ES`8z2eGijkDtXjor0J}Ikj)^-re{wKYcGsINq*#WSP@D zF*f4X>p2)N0?WkgWfH#*;uf7%QdST8S+xzc;OE(TclplUNu4)4J!80)KZT+koblL9 z3&CQ@YC5e`qlaYu#44jCB~Oq@rK-RJ=#S8>P0Lx_`tSYT&dI@D$si*vA$Dq@+_cE8 zj8oI@D>sS&4T)hn>2LMb+k|-<`y$3oqp*n=vw*4>Y~gb$ga__F7MDTtY)SePItnJ4 zB4COwIXB?Gph&f&ZcbGqaTaTWJS=vhj($QVJd(p4{~}cG-_^nI3uz=K)%5=qjf#H_ zAwW7`cP$NCOtzNvPA)xG8w6Z;(uch2Y}PEa<lfs;d64Q4l3xRV*Z_&TKF>$oTCyQ3 zoe;;<LU1v%7ggfJ)zx@&#bqf4ezX<?s@Jfa{_udCjg-a(QY%@ws5`HX-mXDe=H&5p zLH9nr_aDQMBn57)cpEnRzZ3q3K%)10gW7Y>TMjKhkdV43=}E+pxv-YA9do=W65?Z{ zt)d%&loRDi>rtSKwPChA8;Z;;HX(ZhOvBf4-5V7-@kPBK0f=`hV$@P|I`pUn#YbcP zA7B&eB4Lj!r*j82Py7mrdIG(-3{e?<@?@B^_JRh>K7P?CiRw?~dQ-jb4!F1Sdy7SS z_|7SW9-^>8>M?j+wr`i(W{5{7ReNixLcj9)KIz4GyP4w)Yi&Q{=T+d_TKKbr@nbg8 z`O9js(}pV~caij$eR<y!qGR(!tPYn}p&mo2U_homx@kySN-2(sJ9++<j-7<ruT@Mi z0@X3j%V|*<Mn2p^Y>9Q;IXw#dYo9(=yyrw@X*+gBFE{c`P;tVWs&iVv4;^`Y)h#D< z^oRrvh7fXgIsLh1`kaLLDG0#BF*%YTuJ}Z!SfaF&f-9?KggNy=KI@VtnXy|7E$cn4 zg_%)1*V+}(bB4DTpnbZN(wN1cYG=szaHL)yB|;_@r$FRVSbVXis=W5E$MqDh*pq}z z2&=wr(`d(TSb&BgK}NbYT<w6@cp?q&)AFT<PW%N+q*3V0m{C1X9(YP;LR}WBJEq2H zWDU11{ohk+>gDe_CY^H)2IVTbNX#pg5ltrhKT`kboxbMs-~ahX^C7b%|HJvh!NbFi z!+x-;ZQF?<Homx-fd<-)h8!-JtfQ<j37`A*_8Z*`yg3rgshd?Fu_B@}SvmiA+K=mg z)53<th`<je{VLs5!VJCaDYTW>bVNF_s6h&NG&Y|k6*R)Z?Gc~p-ucFfWzTLQ^l+dj zQQ-4aLB7Q$F%#1k$ct@KuV?HEx4&d$2p2iPl39Z^X|(=$P4IB6#jDwFF2E`jc4=&7 zX)gV(5@KuuIY^>6go?)`ldWb;VtJDP;UlOm{VRT}!q-r*Jq1!R96>BnUP(fwbKlRQ z7KSk;XB<T&-L0dvx#fmXo;go>q2$EyZ~wW;xwM~73z>>R;&@Ow^RoYj{WqoG$^%TC z&%4f)3kOkhp%fG^v2n>1Hs65W=yexL@vJM}-4zV>TD2}qNul7yU^ya<08g*^G@LNN zVl0ljrTr#3Yhgy4=itJ&&+-L{-(EvglwOzN=Lf&Acael})dTf=q2JCi<-!ESzLfDZ zpLHPC1R@j2RG2%?)|C&@G_&4V`(YSG{GXl&Y;S7>1SA{CXEmRHSXlcN%(LMPntc~= zrnEZ^I!G)Lua5UU{HvU$K9kkg0oe4q=<7@-p%ZPEg(Nw3js>Nl5KGCv()>$DDMk7t zPeTI0NRt6cWLZ`p$B30oN~ZuS#1c7QRi*cu^^gz2**EHddT@W4zoK-;X>jG)T=uuY z3chY#K7)TkUXb^miF3dR7)RY(ouB{-|3x7bleoZ`yP4lqJMRZ^QemHrkmjGOYrOI2 z%cxPuP^^2tg^hRGKVJ(vs#Cp$6(5m=`1a=2JZ0+HwiFYlTv0!TG-!fN)(7;uGMe@v z&g@Q){<xS-KCdpr_?kGT1B#$FO~<e_1Oy;UOhEoxGzOYaLQRm0kPJffiw#L|P+q7A zRbr!6(J$TqGIiKt6O=iHI>!4MV}nkEmBi@6xX7TwbK$?W?anlPo6!lb7{U9Q04k(J z;!&`m3{GmHCpG(}<{LOkngJ8X>@)g+*&Mi-gRO&{1|Ro{hnSlQm5;3s4i|SkE89Yj zXjFy_bHYFqb{B#oY{rYH;Q0N$9d(TE9U}&2%t^|&Z`E5`uLf7}v;riXmCzxjuor!q zf)X)Hh|rS)@y6Y!irfhov~~Z-`ar3d|Anp?m*q9d^6OlcpjE4y%K%dM>K4Tx6y6Nn z8~Rw3L^tU^QsZT?S+3zR|9KDDDHJsS%lm(*dZ)m;<EC3Uwrw=F&BnHE?bvB-8;xx{ zX>8kOlQg!`v->>nd(QV=<!aw#{xiRsHEXSzs%q0Ly9IO0wjkc2f{QONPM-SHzIuG~ zlhv(*cfU9CSd_(Apx-L<{%6+0Bz9kcCI(rx-D_jmzgAg1`U{;E&mtC1zLP2vMhC40 zQtCNH(aT>FN~|9gBjXr$*$v-Vp7IWJ=nu?!gOvg+=wocb_{y?$3dvc)Tf!|_KA|=! zbmoDoz@#xz5M1yKYfRoZTfZ=lb6aQYF7Mz1!kHg+0<G}Li2vL?;>lhsVK$}S@wrpe zLi2WP>BMN(Ny{o4_<9Zkw4Lz34m&Un8+v`$pZeRj9|IRHF1(k9e1W?6ca!}Z+!;1{ z<hGQ&V<qH}`C2-PGLs)-DH@NiRs;L+&?)J0*)oN+8Xi8{$QcFXrDxhosJ&t6ljHDH z%)sTnCkEqp9NEyiT<#upd7zWcMC6Dp9c^jYQg~8WF%gGHsYtm=YqYe{dVt1rItyin z_JW=h({hGmVz?}p$b15z>u8;})n3EXcB78lW&y}?^fXw7nrazj2k$vNorZt__QuMg zi?0T$7YRtmMc`{AESJ7O*4>ET?l1Ri-wh=67gx0CmIQUv>q4?Phj$o}M=C!#-PY++ zVH}#@>$TEG(HWjuM4lMC;vGIu7E|OOZ|2eUBqx(`*&!`4wx7xEI&MB5a-H-gEqp41 zr%&GcJ>RP<_#XZE&ZWrk)T#TrC88tIu>KY7|4?k=fVfy0A;VXC9)vMf9*}3oG9}W@ zpKOCho2y0dhDEwAF|Q}j`dvjqptUXJe~o7T@oNh9)lkH^x6+y$^tjLWOQg02`=N^( z#QM1Ew0s`Qm)r9q!ymIe1jEkx95BcyYD<6D|MKEeA<e`yZTnbI!Z*8eKG8?x%D#Ox zm@4IN1AHnv=zPUvPmkW+r5lV{vZQ6C=czRS2{sm1fzFt20=yi}Qmt`>8I6-RG!96C z+G9)UW5ZVBa7C2AC0r^fEHny{8BWg4XpcKkEz-rU?3y!!SbLh4;A?ElywVy$C<<jr zT1=QTZ@tl^l+?WD#LzADiMqnpo0Zo*1t8?#g=+PJN`05NypOAQBb)FUZ|8WMGZI!T zw45hOYwmoDw+SME+$cfH`-<AD;ki-ah{MgD0`;Z5v0u>4vF=fPeMj;}&oZ!xC_dfB z(fYslE>8i+yV2Ja-G{$VnecdNeAC9ts4$UJf1kz+gf5+^zLC%$mQh)~RnvMqQz4(O z{YpK1BB%Ra5%3s}8uIZrs_SV*ah5RdH=@9CI5t=pV|Y4s%l7JE@}?yZ+@#ZJ|3LCn zg~D4Nc7fj#EuwM)C@gs0%Dm#0E)W?d*FzTO&L!Ax`v;l5gld_hnOL=Q5zdhlP_7P3 z7YoXMWCV{0`Hu}DXA7QNa@7qjq~J`L$b)mDJD~c#EmAJ#u7s7kde?Az!xn<U=@!tL z6zE3ylhnhrQ<59?qs!Iy$)Elt4)qYB8u~Tlbmzl4t}z)LE(}-1FFXOd4aAKH2k!Iw z$c@;j2s_tsinO$e40tf1D}fd*8Yx`RC@H9Msjd&`?2swabPsIzKz~I+JiGBW`BKoR zC>lz|AhuUS;0j%~{>=Ea313~;W*AZ!cng`%h0;rESVf!%H|O1<Gld4rs{l{O;~ZK3 z{Z}y6KNEPNPV3>==+=W*vI(Nc_>qC*0J}E%&V{QRw&v32xIE5g!3BGmp`BmQzH-N^ z`rFY3@qs9#ZQY_(<wBsQI&I|;8-#!RY>8o7NWceLr1Hm2l}V-wNAD;8IeCvg0d>N@ zlQ$K!al2Fl1YM3qL+>0FLWzuV0`U(*8)T_DSsvyayJzFWouk4SIsD7|BlFq$Q9<|& zK)qFh69h^N7--qe=oIEjB2aJ@E6{|CCddO-0@ONmDq|xvI3mYl9j0~4L{D&u*5<+_ zNBTkO&|3t;^iCxaI~7(iV77h_6|N}Ggiq3r9Em!r`j&3Ubql=o@V*s#@9(@A<x*yg z)BPdnV}=#|%D$bu{IqP`R9Ws?wOm?(CId;k*HI<XS=<KT_Z$Z5qpjM1e5D<ZZpA8Y znudmJ5^lFB65O|Cx@TKP=NXe|Vuh7<^B=4M<SJNHQR4{Dfu<L7iY6HqZa>nURLm{p zS`JVHD+W?f6-rte@c%2`1hz&%6<!Ylc_FS^aQK<x@vly?|EQ<_>0c-t;qWE%mwo`@ zf3*fRWd-1de3Q{U>{sI{)0Ra84-oR+e}V@-`=$|jJIx0`w40eOx6bR$2b?`*!#_3b zP>A$`N>X#kphUmj1EL_}mxtVDCxM_{a~IAVPU5PiAqZi}48O6dPBXV2-vQH14C1WZ zEBaTnqAV^w_YYinx2<0Ts*|u)?gV_iKB|7ZMU=+J`)n+YgzSQP*e<*#@*d=$@lR40 zoNQkIS6>+<;I86C@NbU;)H@+oM1o-j#DNqx1F&SpPoI`u;gv2qL*y1K|NjC@PMnA+ zytpWu-V!Zog_r?yfD6U{tix@(F@W)dQ2((E*380Cney!Tgv*Up@}4b9wPy-p__jQ- zZ+e~EU{BQp6LwEXE5w<t^q1sjc2nUc@$bEUix}hPn>yVVY%&V%I#_JVl}({Q2M!<t z$tCRC_om?-E6kEb>jvWR<Ai|o3paS=()79W;%KhhRL5(nFjgGTL0(n<G3JdPUpWEW zt;RT*HZ!ILQgU+BX!)NF(tjG)WsPJ9+}@iB5d{UbU;j5m8v&exF0jcFO3m$D(cwt# z8lMvs(F!^BNXZd%&B~Y5Zp~;b{#m8lt@|O1fFp8QY7#CQ_Zdr<(mY_}l5=utO>+t? zlYjoFOul=|MRSG<wZHwj{#*b0wN2N?-|Tr<+p<aAFN)<k(7m<5f>`<cWhaVpWE^A& zelJ}gIOjYQu>E-CVl;fnRV}^dvsLjl<=rW32}QjaiL3rrUPOr>JH(?*K}>M|yygTO z@!T9}rR0%;rd?){fjpm9E3Q2Nrc_`9X%m~tqwtvoYlPW1mHf?YlkV(F?lkf;DaQOP ziG}w<Nk)r4R6M?6#h)!DE+@!rkD}U<78SKvLLVGehhjKSDcfm0u6^#u%Mge}6SYqD z@B3>_dO?c^Jh`0VPNR0yNS@!Rn@v_=I|Q_1Sz(7sDKu@2bE<{xI-u7O3A7swd(M~u z+<nHCwbW;QX17C9V7=XDp!d83-as*Z8YshE$fR=avOzQO;HC|4Sppl4YXLeu|B<wS z1xf<2XwO<SVrWbtX!=_e^V6g|s^-gC<*R-D${DqtLl%s8F=3QxJ}drYn&@Iqu)pdx zln-P)wo<Z=yT)XKRfNpFRY1eLu84*m{60}$fQ126ntlq%<p}Jx$vPE8O*XHedzg^& zvD+CG7GB)OZ|-nlapp1b5$7~L1s*-~w;u&c690H`5>DKOwZO-P25kW59;LEBT<5>s zqj&Pz<ZF(4L+32rejU5UuHN+;U9i)f`DMA3v&n);WQW_9dtkSj`K}OA>$}sAEdas` z@dUdk3?H4h$B`^+VGleILh(n~@k-P(sN$vlb2uZ4AG5W`eD16$laK4?_*-|E;vwU% zg}z=&^_9#qiFN0Jb!gP@Kd+PAzcheQpR5t)R?NsuuX8E$Nm0#IZAkX_BT5^!^71n; zh5W(*yq|o@JX0=M-wPjE<uoDsyNN7L3j$?HCdPlG>x9j+FI2HQI4_lBetxHEq`XC) zvP2??Uan%!7oP{x9#!-*f%M*<g1#`N3YWAWs>pLa`%2NQN3F0;x_;KpU^@&!@Jgt$ znK|Glqm0bg>yJ2Lpj7dO?|(mS`uP-cpMnOd_}}LmW^qC`4R7shi0Z2SiWfxg4i)X` z!qt(>7b!&<h_soK;GB6UI7+C`;`Br#($%ZgQpTeZcqlV|mY9n!;olGDPk_yUE^q#? z?4ytVQn>+2soP{gUnTgUHpPp3-Av#R9ge#&*Gv4|M02u~A7ySjqriH!*AiifFSpfX zd~`M^*h@^y-1lfD{FXyN!!16uwJkA<5g%SdcJ0@@Q<s46*HUa7eTT<sss5(BfP^$V z@$x0)w1#vuu+=djzd0>F0OoN1|I6WXhmcFpKMmGZ?%8}!gge_`Gs~0pUqr{y2a9d# z#1RrT{x?dZMT7DLSE0OLs1RJXEY-v0x?KXX6*7NB1>L^%x^$zi8HeZTB~jmw<WG^; znSf8{qvoH3V!q{j%GZwR4(@9ab^VJ>ll6XyVamFE^|!s+AZlYwnQla1Z{1x4jgN64 z4ZJO@qP>a)#}jC^0axyn;d95VN-EV{TSlenG%#bu6LG1oNRGlLcEit8QZq%Di{gU$ z9HIzk8`rj7=DV|Z^LB6P&w_mP<7)}=BW55joOo2{Jhe2CcePbXwn=&E%9+x9q6YpU zg*sNFZxg(dnWBGw?fYRE!!hZe6N5oHkJHxnZja44^lbX=0@<K#$Xs(ngyBo{ta;;G z;vs&V$j3NCd?rDrTDJJ6THnfi9SK!Un(#sS4?dR%1{>|+G<9;;5Z3V}xENea4&j=U zg}lsvkp>EU1TgCG1QY|#QVC&pAw@(OeUbKPQ7{2M=o$2~|5f;?V@_g%r!aGb&zX0o zUGElP<gqwz!H38APu|cNz8wV(+IOC>vT@1lwSc$j-N2Ia2b8C9!R>5Da6&Jn>Q};p zZjlUN?*3kWK1_K&1foC31Fxey4K`OB(2iADQ78fYUp!Jw5&6WZ_1k=<m*QY~3bYMa zZ)yrtP{4fe!~piY^(>dI`tfnSE}Q#pwLXWiss>(i*drOD64e8Z3phRp=WRZ~10Q-E z&8*(?1nj!q#_%XUB;FtTd~qG7kCsg7;mDS~zat!HITc3W4M~T*>W?v?EY^ShGHaE6 z<GOPL&@Q0A8A!SexTg}@8V{%o3s)`FLB&C+4>;G|nm+F&(aeC++SVV$UoESGzYI;O zZK*#qJ1?OFL0|-`ehXTNNUy4wJsqfXR<xG!aw_F1HEQYM|9%H?($1K-qu2X;l$S2h z@sip-mqQhKzl^RLdUb=_#2pL5;VjXOq%|vbbR`+|ITKW3fEk~1k)VoK6VNuQY@7<w zn*`y=ykzJ+E9&WsK)n13RQGl7ztFrqO*A<Ta?+bl{&YG3TF5!<IsPPY<zd&pC*6(G z2GN!51qDh1VwIORGk}tSbhlBbzoDi{6$8t5>TPJ|q0t2ruLMflGxUu!koEKT5akB9 zj!PgTAhLPE#m)4zD=(C+SR^&kZJ<=F9N-iwSaK^&)?@izzIZeAo{j-c2s@{1*X85s zV9)=qhqx@f(Ey10+QgN*sU2*+8t+RHk-of+fc#gtpKUM-9fJY{KCxB<`g{rmomhFc zp1}A$^SFL|Y0_u*{D3}wpC?em*q!I4`hzh46lcvhySb>c0Bn`#QU9k^o;Tgl)#$o? zVdOXUeVk8jD8JD3YjONDroKA0+i0{H?1>eO1@}Dn_Ht4iG-oufiDdoL17Zter}=(f z``q0Gl^g-*=5`v$c0Gg)R~)Jq-)V~7d`XI%xoL}I?B>hAhBWK+)QL3teXar8q=2ad z*+?^ik84rRT+orwK&0hU4%03x|B+_xe*I80UV8((rt&j?j6E8-dp8;Qul`Ffx~$j- z5P7soeAdftTK^RmK~f$rpKIpBo18Bvut7UKCe8Xa;i6YVS3Ml)&E4MrACLCSyW!DK z`{{pfmuKquERc*_*)<Npo9l+w@Hr7)rWGid<um?i65b)z1DHiy!`Y7H0X(w6w(G=c z>v5pFPSjpI61l3ID9@0`l^;t2#vyZ4o{jU;b4e$>sGORv)zxv36hLRn#6ppQ<*c&Q zmfbv}!m{)LlA$FwLo0Kx{b_bt5~MaN<8~-!jF?b*eneBfVy%paiixQ7SpkTD(!ug5 zfe_KG!OzD-%hO-jV*F<~pLw(3nt<U(n?|c$%l;~{xxnj`!-%!hgghL2M&7mHY2@=Q z(!r2%>w)B|=wKz5%MRrW8S<JP)()(vsxW$y0)!<NG#OU`$K|=#u;<0pa>nnHCD|~5 zDhYh_(*Jefv<mnEa3rSvj#>T~Ttq6=K2vm6)Gq0k4WazC>gA5|b|Z<iNVJMcIZMoX zoB=-al_xs7AN+l{*L{6>k&9vLgQV=6(H+RFUaH2mG$2p$BJ>+-I>pD(<{}RBUe?_o zGeOsp>Q!PP$a~v7UuvrLFi7xureP#zcU7S7lQ8EgkYn5Pf;4NXMzy5CM-~&3OQ&NF zh+16X{3BGUX6sF5=gR!~1zx$`E_<(vr#=f1I0arPs1`nj4egxzHwvF+El?&8*yf?r zrfJ`i8MvX<C&@(}QF5moO+d}^*n$Z<MKc9;KQH0#b|23e%56qi7g8(N+6EMwKblN} zUDe-tlVjzd1M8t8Y*2*;q=83jGu%(_{y-2s)|tZ>Q;WXuBR6T&<wu*h3;*ns_xTia zzoLxwfkK&iEdEFUnbzVAaySQpY2!x8@nj`na6Exb>fb1=lg!cVDYAx;J+1l~lPzPX z`|4FuCSez_;J8EwKA#4o6N-+srLil6yqqT!^FJo=C9{muzZd==y!;<7dFe%HDIw)Z zcnkN-+7a*)5KvwjpsR5}s5ljMf*T9D)nn~VH%GZAw|W$v?CIt@e++%w3<e+cxy|EZ zz-~6c=13DBFHdp<92KUG3NywZs<QqHT#|I4elBiq7s<6s$Nx9bct3(hn?(nFH)yQB z+NDFETW_8l$*8(7fyY3kU%CO;LsRZR%No9_ze3)IVD{R|IrxVG{p<NSRP2<>riCm{ zQrU%+ndxGN58`=ViZIG~w*tv)bBNoB?5b?Y)8wqMtP}aHbQ`|j?E<s+!i&O-=%R}x z!TGblC!fD6du?)hE1z~e@?72w@Y+v0cY6SJI)LR$b@i&1TK&$6lp*R7A(OKh(+4OV zGLUw#7+=rp;JPwsPQj3|Cw!r;o0DCgpgJWM<H>-uRpxHdy7czCKeOoAm4fT<`+MZ@ zyX8b^>?ObV!k079SLopyGRjphd3{CP4e~9vQOjSWH0EPHqVg{^YE_aG$W4Of#li#T zFO29KU#hUe>l>FW&Jc+_yB(o_j?i<U%H_wu0j@A13tIKoaCg9lppNYgJst?A_KjRB zqp@sH7PQ$FVpL;R8liT#?&!S5@UhNmaFuB45gVtP>BeRBeiB+W8-gFJVe*gM*~RZP zTb$4=a{WC#0_^y1h|r5O2SbxoH_3Y&s?;<%>xZIQ2sD0QF->YMrq9G)$5e#{FfM$o zjSRG)q_)`6){PZM+WbXR4m5ZRrTd8%JwV@Rpgzb7kd=3y-zurkv>p>#s+SWjAg7@z z#So)G-_OOyF(<F4kBVge(a;U=h=|a{?w42FmdkzZz^Lc97QbceuVw4FT1(a7id7Wv zZr7~)L5t7h9Y}GjvB}TeJ}e7YP1|cb9_L`td#j3F-8VV9O2C2N*%(e1p1;{%|77$o zS_D9z$}YNZCRBZZHAif*v`#Ty-T7^Hq4-_Sdn?<J_@q!fq-6!Vfd~Zr^Q5QqtQq(D z!|F4bn-HOhUxTa<zh2VP#?riP2?2yD&kl4ioX*T-vhG|{C_vQh?^7Wr@V2mzCwTT^ zh#mNT%D@PWYu3E4+X0u+5`|5Gq)$86RZ3w@X^P%{$7vOOTU{4Q`WDvf%Bkt02sBQm z6)D+#c)E(qUZmP~m=X4Lg-iHPp0_G2$#INb<Ea>QcrD2-7RbB1t|(ZFsNvNu*#i`{ zko1){x;7??8H68?iNR}`XnB{1!KgleLW~e5P1mgK(4av4Rt7R`+Ux>2>5FOV?0#!5 zq-0nO(EUQF7z(2YP15cSt66K=r>?I=aqzqyIr;134{kADzl*)|*Mw=VKAEAk9%aR1 znyg|)>sMS@eQ#Jt?I;APt;0kiL&8N=MF<D>E)Cj#QYaQV`{~+%q^S-b;wmp4s)IXx z+d*iGaHv>1OARqoeb899yVqfZ%Z}UG0F(Z&<KL%Q4XV8EIm3()R<-83kd3x$L-k&C za_)Mln4%=#%!bCBu6%GLF9<y!H`?;J^BgGhk&mOcvD*DcJlRWYn(H&$l7th<ucE}) zpB=SipD_ktu3ye2!>I=mvzhr!H$Hn@`=Lj7-&)|)kZQd_q`KZEkq?aK4GsLbh=imL z@f!l&_(pg4Ru%=JW&ezZA@hEqUo~SB9PDg#?XRt;oA%FSOMd=V5Np-bm4eJi_)GiP zpa#h*LH->z1)j-^G{aD_Q!@>_Zs^2R>i|Em^ENi}ZvZd-c(O87usDuM#F7JqNfH^g zpkl;#g$c%_WJOYbL9=3Q43nJ2v|d(2IM_nCiG&JlKnktWGLq*!RWV%WZCWNmKchHZ z%Vk7KVl;ug7l=eQsLY|!cw$OFo?tXu^1N!M1?pO`)@mBkz_X0g`BHFMx9b6d4`Jj; zSgHds^zdU13s95$6SH~qN|Jk{xvX2bsh>_BN99SNMTVxhtP(yxYH@YFETL{U^7ukN zGidD?FIXDkRf!E(qPYrW<CW^k2uN-W485xFMA~qtC|U*s_DOUgq(uoTo_85ZKZ&W= z`Ap)X%p?jTW#Ba-CobfIk|32?m?Tx*DWs=8C-rC{qQXjMwBs<!e>IPPn`GZw!61ig z0|g6kkTSl{2BzZ6)gqS9<A$4-*h4ZRa&7Nlr;C}uM<`BGQ@fU`5eGex8`1lbdXD$n za3_^PP|cdpIi6_$_jVqpEbe>As}IyVWWu;jPvZ@j5kvyVU%HBuUOG~J=?Xmhr};xm z;W$2It!9h0vx?gF*MX~kM{ARHzpT#$bX&ij3Vkq5j*B3Ht6fM9sO9GoJl{yQbpQCo z>G!M=L98!M6P}HsH4H^Cl$(5tryId*<9+J=qKmfv#GWud%wgs`(}x}*L=YwB_r7h< z;yT!5$<7b4R?kK~{;d|;lBNt>T%tf}n+Sm&c;7-w9De3^l<Ahe*8fe{r&V;D`-6gv zgk6rH9Z7hH7z(+%Cz1Wjrea?D*tF{HBtbbb^e#vOy1fq#nqyI8IBA>x6hwO-j+E4J zo(h#>FySbxKD`ozddrj-PRxfI@>CoVJ*!?RVz!&!Vr&c^mP{Y`w`55S4vn?Yv1Tz{ z|C=FRfIpsyaCBHmz!_%v>`*r80o{c_=!iyb?e{^8%@r)ZE#~*hvEK{21`NN1({NFU z*SGKIl7)17@5eT;KEh=+yWbxm>x3*EuSIX|sYAXstPasG{-ED){O+!k*=%fnfuaRo zYco56W<(0lz-i|hCucRbY`tlIj_GbUiz!mbA)FS8*rBa$@~+5^QG?dy_+3Ff$x<$A zuDq#84UERQuysxic=pUHM1Po8wjPsAtE~n!42oW7)BB?j5SJP1n{>Nzl#g#eY69-l z98pI5RMDvIzPuVk7|4?A<H3Ii&kuRXwqWY#d{V}!7Z)pNzz(WtV}CU)#LZU&65hLw zx|r6j$l^QF3?s6H7bA_qCRYzBlmT|x1R!R=+E2NR-V&~Ox~M^u7ZGI?rL0I$kn8*l zNt|8JcVfN%<x~@4{mErAD{lFaCB0D9z8YkDv!9+tw*5YL+UxZ~e_{VH@4(RC@ck#l zPN1*%TpcM~Nw6G}@fRb;Y9m|z43gO>=E4niEQ2sh0~=s*P;31qme}?sc@g_SPakOL z!4o3ibgM8*!!z=C{(8Be)a|U!si5cZ+XW%Lu}ACz$XrDjc|TRmkhd}^pz`-{huk4U zI}9DAv9dCT6e4Nh0`){Pq;gc<LcffeHuO<<IZ2uboU%=WQJNgxe7MP@&KW^S`IlNS z<Y-IEbqk!QIe9KlAOk!dQvi$Vty>tr;fK3QkAUJf;G+4bVs_-GK#$+{kL<uuo-}#C zr9nQiW`kCBK>3O*XiOi|q;I<OMVZpJ4a^T6Ox0a}W$e6@t&XdHRZH8EiXcychsH=Y zy>4a%m@i5EVzq8}T)RGvM-aWsWc`qO>EC9xUhP8i48}&ys8FN>I9f6>R#zc6txMCU zXG1oMW)hUNFfpIhvJ9~a)CtbkS29>PY6fuWYtx6{ah>HTvNRx4SfmS4scBcFNb-lf z*3^hkHw5ho*e)p!ExW${^!Ii<@+g@0+7EwLdGL?4McL0Lts&_?(b%2*HfN*cBc4N< zR_J<+Q#5S;xTKo$o=yyA!i!f8+0r^4QM&e8bB5`ZAnHXu_sZ31DWr^oWx2$KfBHNz zIJVA-{XLp4hB64LbCXOnC)g{%uVPnc@cs>%u4GwC=vR5dezbK3sgAw4TyzVx)|2W; zvwgdhaWS#SRtL$2^xY0kG}6b;hKq&6FlQ4^LH!t3AT^(+mz_11Df^EpaEJE4CpY+q z4!;hYepU}@Alzv3`%d}J;i(6iNDpb9=$vm=ypHQ3@q4+h&X`84F4N02NDye5(JPTo zX|f@%i;u?X%G(7EUxw4eO$RcmBeKG~V=0}=+Q}1BLLTurO1k`#gO6cBgSjZCw^PR& z0^kpoX384a))L6$EEb-c%z0fV244%h_3s+|Nt-d~Jn-`=h!KXjo9%$wEZ`VGg_5B3 zI0tCl7&DZbTmIT(U~awZH2P|-GibVE>g|500sbc+i?y%Z?Eb7~Z#WBDmNSwd=BvJo z$t1Nli&=b6F4nmc8PRJ7KwuSAOwT~j?&0!H08~PnF2YDG;?E`WIz%9SE{59?#p&K@ zlA;J?O)Vg4U;`TTL~Izb7U{2;kmRh`ta#Ss-#p^-q7`GD6pm7~pN$IOcgP#NweQa> zTj;QjhnjJ?8tnLvb?bGvJ+LqPd?ul1bv`nso3is@-$MRm;_<q??s1|#5H9K<vj%kB zw$7%nn8h*o?Um)pZ9>P++RALy|7Epso6izP7kMiCjnBd_KEg8mUfX^{B>UFTM&4Vo zZ(01}_lh_=({Ej%W#$bm7B1IcxGOulsB_P+lEj5jpz`hj98)gKfk;AIxR82WLq)}} zQ~fpUKW%XXf*ssHrhLooX}=3RXJ{#<*DZh4_}cFUbSd3tk-bL@l8cdWtD_RJE&%&{ zLiuu0%<m<Fq<o5X+8z)#J%yNt<I=vvQo`0C*qJF<WD*>(>1t|Y^XH;j<pUEcS=QR$ z)$uf}3ZIN43$@E5TVr1?f~iQle)@c}Gzq!&-wpaVpN??wp-!6sWU`&lbFZULwE?jk zdYF`fCSf6)^;$i+%*8jnL9C0l<aAE@SbcOE17<8SZgx{y`;nNksP!yhCg2x8LewQ* zC?6ohjXt|}gU0J(e~Xa)oypNp!epqawG9uG#>;IS+PT!Rw&k&HXwjV3%rq1+jDY(b z@E}+X6%Sui%7Lfli?hKDk(zaT(6)@BSeQw|*hPm>R_~H#R7nuaXNK~L1g-mB@}#T` zy8}OumXZ5CB_&6Hvn8RP1xW{fV@j54<|COV`s=yns%7$HdTphTfwRwJ)Yjo*+cso_ z1b>4qi+#<F1U#k!-{;n8ypYha)Bf{)c4%S!R~@$xR;!`A3%zkTZ^co$zh_U_UC}#{ z8m@j-DXl<%1{-JsdCZQO&{i+S(wM;JdWce<0CTB1&bSnsrcY~3@b-K~1Kq&<m-uW_ zP5hm>Vk=CZxalv}cdH~+5NlInV==;nm=c(mN({dBuVJrAzw05dcR4(SJcUjH-S#3r z-_AEPr}UC!WI?Kh^0L^gv3TyY4p;zI<m6+p8i9#g1e~G-3aLh0T@lnD@Xlj8ol}zX zj58@vpBzY=^%AR$^1g3KdYDMOo`?Xp$56OZ67$rs@6|R3`w+y_Xj$J047V%<Im3fX znqJ6p%Y+V<m@jOVdbEuIQ?+hifo5(MsV|)L+E?zn{~73(%+jPwgee$R5sS>=@IgTi z)>~kU49VJu6GPW>h%D<qp08^~vICHpZzE%o(b{KLd<CJx4?98VbaeFBkr0#F`OO=4 zAV^xXNeq)QR(wicDfB|itL@mB#KY`G0(;=ehacb|-l{+88vaz|e52`!syvW)P7ERk z3uW1pMew9xYKf1MxiNsjR5CCcsG;kO6|@Jr^LPC6gc(?x@SfFrc>Y<P>3B|Q3)|_l zX{v=8;2sKP5ex(JF;QBM6S~f=4rX>hhitohTjYkE%kF#sx5F86#;kt{&RzcY?M2#@ z*dZKQAn724##}RGK3xNm&=`+@2k~~-2D%TWc(&(vE&aL?5r^}vJ16Osm14BDIaV{< z_ozswY6xN5*Vg1?)92pEHv0~{LB_7z@67=!`C^U?oUj&Y#?tr~(gsPlEV!CkYuc@s z$`@wuM}U?6ega0w-03cYZ@4F1uGw%snf9t9p)zHaQch*#)l!Zcp_`fp_an6AWRA{o zzDt+q<@3PleIlSxd_^v}b^H&?;9-R8!#7T2Y(zHYz+pC4X-p&$ylEgu!kIcNr2`V5 zmqTy)&@RY-<6tn3?)3YjBgQeGqV|nYomTK{-seh+qLBv;I~72O;I7)2vrSIcZZRe{ zT^Jv0A!Sug?R}GVQO>i8n!-L`t&vsI+>F*%;eAlZ=yJ3+?=M_cOg-iTEI9B4?X66g zm^)qc2XU{Od}nKOM<XE~Wiw){i@CM!tF7tla+9jF)yV<}7>l)-=O63_JLo6RXf@U} zLMn`YbgPrIId9TmNwqB}{xU^x)I^R+(HP{Y)EQ~K_!JbX`Nt*@AZT4j_E3@OBoo6S zJ~D{2l|5O5uVne3S@4<!UYq*-Q-sD;;2YI|pVNw2Ri$7OX!vCk1D+5g57*M*Sq&Da z`elrskBLnv3Wp%5Z@A?rQ$%n_HD$xk0JJT&ODnmob>1CL?-(h}FRmqhiC6lr%ZVh! zCuK5l`+Qp|R?_12K~o)Ui8nh_p3tCeN~gC4$r<e+kBF1m-7s(r#~9$!jMX{7WiKz) zNs12Fd?b1q=0@jb3B$0-glcovpb-v8f%%S|1=>-egFV-go~HAfK-nRAVzzWI_7_$X z<Kvgn)KL<JlTxlAt}9$aJ{PQl-~Xq8pE-)kr~ll`Xlcx1Eukc>h|_1+B;#gme4$I8 zAi#9sH51Nn&`BVW#vOt)U?~t&K-7Hns~4Rdatzi^@r`%&p8Nb9rfVR2Ul<0SM!=^t zo1>iXsMZIqHxzxQPaac0z-PA8lFLA_LfOn#v)D*Aqp%;78lNCYou_W1?aML|Yt@2^ zTpj)UHi!s3r-jQ|v15tm-gsp1ms3l)VPKUY&exoVyAkG^&{w#f{NLIAG*{Y`QOw*f zD~g8IdY@yPXU_7{Fj9O!s<xnFWfB6{)d<byUqVwX1~9SgJr2mKR#CU;hJG*1eK^r8 z!#k#fa_qSjltk#6&VznnTD9W_Jgr#c6N^k)(;+sxhdybne^e9d(6Wroq&jGg1f;rY z5wY#3$!oPfycJoRJ`2S$k@mUUOWH^RcvkA*t$^?Wn=9zt9b^ZdF(!0==7Dz1zxcYV zkUiU4q7TG8{Z3Cah@LOX6L&gCxe3EYH;MSR<1u2=j>iq^BOCXU%mQBP<9>XZpi#&% zb=(eoJ=gjz;I$Y-#am%=QP=$*wn`khEs!je3e;H}ijdfD`45(3C4j!Jl7ipVSLNFs z-zVS$$7_D@JH3<$o;`tYIb9o+0*iF0lir=WyK3na3wflLiCQuceoH{r{@}ow!%qGO zz*Uw;4AV!_u^US`BclJXPfCmDOdss;0CbIwNV$GiLa*#RbGt9}m4~adywL3u)|Z&q z%w!r;%waltzhdYb(79O!(?3pyaKS4dK*><hxm25yQ}Bb^g!uc6N2|+uS`Dvn`MYie z?ed$XIoL9mny*TT?K2mYU|!DYSS+Nah<8YnXvk4i|FRlXHCce6e289aRlh9GQp$f~ zLd6X6%v9J_GnrO)m5sW#RwIsT1SCZt*8*cYNB5s9os67+F(#9NGZbkq>Fz}Kb4XH| z8wv)w@~`W%4!w<j*O4+&e9%jiz5R-gg$o%(hBOF*3H$J+&^x4p(d#i{oBzkA>~ju# z1Udopl)!9`)9G>SC(PJ@%%v4WI9Ob*KzWmIUsGHdNz}MTL3L>a(dRXN{)lqB?m4x? ze@e4&x{1ytWER%?SM%cPwX5YJ5m(thK8r*QUL{Lu3zddo)VKC+%LNh*$UXJzNboHG z`a4j1$AQ<?2c8Jo%er-?rQ}i)+Zd3SL21R85oqL7Pqk66QeX=3qzSiUzK0+DY53#P z<$y3~c>0^I3~$?_EFH5p%f9PhwEt#rRgR*E8=e)}8#Z0REcuuV(}Y`?bD2*Nh$cl) z02?rzX`~X8=-#sctr&UC#idf@fIfh*H65-x{PqDvL{`Qq^?i`jq9MN@<%*8~#g_St z(eD)8=dISS?>@ybr6!l7xi!_K1w7WWO`8bXkyP@Nbenc;EY=eMHce5R3~S1+Kx}Cl zxd{~`35~`SZE8f?0a{4UzL#bKS=651yDwA!AJ_^3Z++|bi8Y2je+FQ$T~Mgn>&prW zbtdI>I@#6i{LDwCRcgop046*$BhuIgt>ie6`vs`HW=lnlD7c3OCcR%NS}qvdNeAAU ztN=P&V96Hy46wkvDGXB%6OmR?k)w%Fp&alPKHu$RDIEr4|K^7NQ`tfSF1JDk4aeo1 zR~<jreP2e<E$2<lzGpS;$b;YjoHrH_R@6+5`G(k-T5tO~Q`@S2Q8qP94db5kA~ge> zf~%aWNCv$7%E(L2QRVzW!axIjk<)fw=T1Hi3=DI8hFiE4HyhpXZ~mV8gUCoUZnlss zO)>YUd6ycSyX&KB%B&?P>U889Z;DL@*o!|-mE%^RtYx!Y+6zfez4c^O4(ugn|C-a6 zJ0EP_``p5a^3M!|9{?B<$V416?ecqFoiJ(x4_;o|GdD$5D>MEsKm4hakHnd03gIf4 z5<Jc|wQe%_A&YhOvXBaJ8lZVHtCz|$L~n1H{lvUtcd6`J285oM)-i7R4Nw#z+?e;c zy{)y+ftLS*UGk}bO@>gEmzk#HfVEj|h#q#GbJ8=r)ntfEq7gJRgNzi;j~uS9PeoY7 zK#L7lrflU!Vh8so<~?kYoc<axuVTS5KBd>dGpeDavfx4dljUP2xq=Pyr>=`bh60~5 z-3u|<5gkA=0SACU6qZHI8kd0#FpeL=Obyi$-8RNTB7WFc>sQR~B|yLS)H4@8C(_B8 zb%KxOm1k-0j*#9+VqYqzN#brDA(u{6rfiI!o&|;VC8Y&y7O~W1C|s6me9NVa*lO;| zgMZBPGC94-^9P(Z2z9?x;k)nCU?!LIaz%3nKP`UHWfA<TSqAO9f#AVW(zx<O$g|xu z{k_X)ydx;tE{*2HgH{^7jTY2cIIL*H5PpDh-|U-BHlkuBjmh@ocp#Ucu614>&}z9B zBn{qyxy)8%Q7oeLBtTutkyjK)-B;kzNwv~$77+)-<d169^%(NXJ!QT%?V;D@$mh<` zS5c&8HYsSO$ss~f?VARH%S3?M+Bqay;psX043_@wtEmagG?78$RnURnT{}|o(_oMp zA#{HhSyw|CvXJjiG+kT|RcREHphYY_p{VuS_n-oT+Yr)o*CE%KG%|GtEg6Ax=Dweq z5H&-xnpS9TQYp9eI_{ytNGfuNkSMG*>^&IFFsNB#a+WhgLej?I9jinfFOp@9L*e&_ z4(qO`2i{IdggZzi>>{fD$eQd)wtGz)Vt4@WqZ(zr=^M}~z{_vd##CxL+bkbln>9HN z+I*b8kA1-6G_pXXc<W1#!xSR^->X7Qf6oc93$ZBEJ^E5NWyVg<O)VH6lP1{M*lBeg zXIt5vIre(lCWNkr!w@VYio+Ec5N3{kDR%{KTuA8yI3Icci#co&Ug<)ET4O|PPwl7t z$e&82bIBB*rVuFih!7&#^%0IG4{(lGgqj+R#}Wp9YxF*Qucy%(`A@Ssop#5i;SGQB z!E^xS8@34Au+-+TrYn%T=Y0-(zaDy|xlgV-tM(P^(ckY;HQ!f4(^s|>%BHR6VW>pG zFVS&X(42OI=hHcBNa6j0dKFYRw_0Ga^RU?M;P}X6H<E7>=y3z3$8PkuD-+7-ONY4* z)8FC1A+qDIlT4xf5dHg4%|SkK#@jVp;Ym&tbG_&K?vZkPR@+bOgnrDtm(mYRNL;8Q z#)mBQv)<5}rz=}GEd0g6rC7r}5jKXzaBm!jDkkQ38K0yw!hSw2jpsw0#;l>nfKS&4 z&tOtZ`(3>cPjn+<I<Ty_)yCiNnM^B)DjwLM8+nvUfvR<;WF%}jN;E>&f&fn~vIN6; zo+KP`?plN*@1}E_hg}G<#A6e8HaK>0EN3DrF8uxx3_aHegvr7HX}hy0l+SqIIGI7L zMBYrJRbmEGho<Q#cBuGSaan%u+}VsT%W0H?nF{q)3xcI54ss4?VSEzWdfo|ed55tf z=AGxM4~hxki(2Ws%D6w)@SfK9bT`BfyC40?;8Y0JN85CFV!*cvN{o?<K!Zrvr=bm~ zZV^h7cod^uo=kB~S)ii}DSj)b{GN}YbdNZU?Y=zfIZ40~EykrG<Lnm4`}Jxsc(VY@ zw(GpbpPdkg%+F#T=;hrE0l?Spg(C8|^P>fQKybTJfOc?K$nAM$>gl%aH9hHpxhI8w z*SDNr&L49VR{}ubRmkU}>?1|uc+RYyLhH5Kd;z=em1IbvT6ga^0fvKRPMIgQx9wRo zj)izD%%Dc(MScbju|cO`!EBM%r+n5@`45$YP_$kAcaH5uc))z<XgdnFEP)}L^^K?{ zL%*l%#KUw1kLI-}L2$}@fSktK3dc!|#5jHAkx1a)GpFFqy*0kaeoT}9`~IcBZ~q-| z`}6k*-4dv!Qch5X_0Q3ohk_ADvnX)AfMNd3!bXG7g;7&Zg;0ni{w_0-V4ZkIO<`zG zfBQWGK{TJq1wllnfvD)gCM1rMya@axApLkP#kv|y25Gf$X&&!Ok7y+)?-_^f(j48Q z=X8ySn9qYokdoJKL_q6rSdIeEjW(Yp{Ql+Q0WT|@?kH%z<(HgjIW!IsDWL;%ukEV{ z(uxXD3=wo(K^r16?(`PYzwU~4;kKP}H)l%@!`tZELzlBngIy?YNE`(~#@1_k;F7{z zl-N%xP`6MO2K+KBDyDWXRFRtJlK)%$3~PGfyW<AqT^K)0n7#m?;R9W6U+Q=>!fDf| z+-r2<TCMwjvo3r4^f$yh>oyfDZN?<F3bJ8?v%{u)L`vC8&C?;mA)_G|^l+HV&yi1X zLbzxkAQaojprpQIe0t6uj{3<M6dh2X9$Axpy&=zHr)_{0^&+@RlrkU*H3-DVx;8?h zXQA+b_*l~a;$y~d5-|gbntyjAUZ3sX;-;RDZoWd#^FKlu6g4Bk5e*0risC1KZbhd$ zdxk>zX0tl!P_hMC13V$qWnllTVt|1N^}W!;Ocu`IZN0t9Y7WW^>@tWrHYB|N37|F3 zrSg8oOug<>QDdqZ+W)j@AnLPH!w4nE>3A42VO+GL04=;nzOZr3Q#**H;oy1=6ki32 zSPRi-FMhtdNg6gB96O3kmAfUBnC41jRFZ(FZtw@799t~5hFl(^%zYc^9~ac}QCUZ8 zPSiSNF@s7V5nb7K7ndUi4pHL><*KGqJ{i`UNvZ9mOu&{~Z6s`o6&B}mBOw^t*j6+O zQL%pPchRm+)bRanR!;puTAO{M-NJQofzA0sT=w3g2HqGoxlGtc$Z#2L>0;%&x_EeT z76zeY3oTzF-OndqWsZk8JVbTkW%U1@k%6FBBqt4rnlg*WUs4Irx^QchE$4YWO4)`e zgx$X;XW%YP<sQutgp|$Vh_Fri+M+=OeR~5FSQn*!=N-_!w}YFzZ04vNXmm@~!^T19 zh+2fDy(>uY1I0?z6-Vi3A|@Z}8^L_U8;sX<1v?4iRwPEJ!XWvx4nug(xGUszB3XJ; zTbQ!=4YLTD+vZpttb02#rXpisYD3LUd2H?*F37oW=g^%+Rof8>Ue4VN`?OfQiQ9!g zm=*~`y0441fzQ>)tgi<Jl2yCyZ5(74@ps4Q6Z4V6_Pe_3Otd0*iOO5BfYpJuaBj=n zG|5_RKjPk8zg=8-Ap2m?AiU4zZgBYpl8$3`ViN2O_83%jQtq=Q_*U}yD4t9+*sPG6 zoUD9`-E8!JdQo*IRp57(8~Iy^nlaZ7kn^B6$|znmKq(Wbl=e-MjtrzkQ_Grwp6AU* zWb)RZ0Hbu@J64lO@KC>OW^`eF&<A}0JAzvPcUE!(q`8H<+O9NYQ#GiK&g6JGER_AI zWvP{-bc!8)hh8!gpE|XAN*Omx^?L^Do(8bO$8C3?kDq$*yY_$BnL2E|oL<xWpGV%; zA7N`0%<X@?tyRoADNOxMjg@Dq#U2HI2TfRXW(C8Ph|63K1qDEC?B`ej4R4%{iiM>~ zYBff#J4IOm3)t9yccX)_xiT!)Cndtu6c+nu;sTC7R=&%qO*>9^AHBV1M+IEFPV+&R zT4J#}5^cwoo&azw<c(}rZUDcn!iX#gYeNGRQAnM>(_a=Lr(Hv%<oi`30V_k2%pXn! z4;<%FQ+GhS{n)@En+ye2pPBYRa7IHOfA&)Os+#H+c7b1HPMY+vf7#wFJTk;%5!$)3 zGEf??y;vAJXa$Vti<aLh6k-InW0_c3Z$a<-Rrdv+GT6`mEh)a2++Tg+9;?w|%T!Q= zSJR{Q0dAd$K>QK{hW}~}c^_s%*C)P1&*+3S%=!-Tx>tVok6IL{(OW+(oaGHlkxIbe z;8F(6H#8>okq%!&;!DVnp;=i;B-Rb}?-j<ET4<AJrYAv3eY#@^SB^sOd?PESZAfBy zmJ82!q*n-+m>{y!o9q`&^Pd@5TwzxusJynLni0712Gr{PV0zm89x^DQveL<sORB4Y zoML3n)Jpeno|czt6RxnN?9$Fh<5Hs4&M0E#EDLHRCn>EqvGok%3=lQAiHUK9iMDMx zMF&edY4l*LUVhS;$Eez9Y8)zEgY^}SX7DyAtz2v^DFav*+ys!y(-i-~rfgkWOd=3Q z6gQG$;CR;d&{{&)N`l335Dt8B!}XSm+9s$(Lnmh6n!!vLacZJoR04R%W`+HV^Y1cw z425qs)&<Xg7)?u1=hX>nEkR?%kC*A21BnOSU)%in@Y;Opbw*sVFrCz%xg13$o$}=F z{fU9%evk8bS#V*a;^4?37(FHB$6m^CUR^n<sSHU_)n5GvD$P=H;fggH57*HFik;L9 zndR~e+t2Z%#<0`qbgu(Cz+)SIyHOu2LwFfZ8WVs_$#s1Mc|G(~&s7zY;LZE?+Z3Mx zmp|IyJ>f0*(p$DY#jk@++1{^DL`www{x|`T%O7YCnR0d*SpFXscNEp^A>)&Dh31Tz z1K)IC8iMV&UMk=<jo1vjtb-)o;(sf%bm2#t#|YmH4%4+VXL>eH1R+bKkY}ljr%>>K zXfGzJP_XzB3ijOG#T4?~VA5kyn^f@7qzd*)(8S18n~lRyTP;kD1yY`kblf<QDs$NH z-HQ^X$(xjbBCYm05m`=cI8ScmXz@l<xUUl+;m5Qj%>DQf66FNhNWdFQUjm{jSj*b` zq>mM3)Q@Tn0p~dCCn{1SspGaNo59rRwV!y*J+Q-N0$L`ngJ;k;ZnHn@44(U&(k|Gn zS9T6R(+oe-nCaQ?rdxbSYd_fm8RK^HfV%ud_Z{oai?Ah937J1MX2{!;R%&#YdW1jF zWDRo26R#Wh^2%)g#7ieRl@yjsCnf%|{Q(suGWf@08?Sz@f4jDcn-Mz8)%SAaQ41|z zkwGadf=9Wj8lWb?*U~}_KnNSp{AMq0islH9Gfz*+bwi3H5eQ^2p_Xm+92UQ>iGriL z>BWv%X;kAyT_7&d<fo?`t51@r_u+)$=z62{q86DTLefNQm}qIG@m33@HyZ7N*IgR> zZ>YSC8CApBHU3L`yCFd~EBBw;OJf1vY;mxp@448EGKyuZ&YGv&(!E&NnJ>cF!X3gC z_WUsZK}DmEJLqZO_VDxMkp=r5e7IMm=m>S1q$htsK*dlF(LyStxC3~6kFm1R;=u7x zR!ll^d{Xy%Nbc=h#pUSi^`ciAq(j*}`pqQvpimJttQ(wBZWn|J)|U4&6Zh2Vkkr%M zGE8ayi-8xM`S!>aI#Q^1ohE<=QerJ4`xvui?VjT_Q3Dwt=-#>hA6q4BrqI#5_1Q^_ zP30O@CavgIR-8Cn8+E^6X?*cz=9HXe51#Ji_YwZkl2I@|UdS|}Rjnat00D3cdj&|~ z9lk-WRVbq*r!X3VF;cGG+guv>Pf*0gd1q$m2vwsS=)A+S?dHjPMNC2}K;C|u8OFC9 zcG7={D4!VL%93drBcH;7rgWVEs-t!S9&{mIrC<~^K~g#p_5L&RO}4aTMnZbLB<y0N z9MzD9iF+u=;B#|y`t#-2q$3u&MiR-Mn1@AZW0d#Rk`*&>m5r|0gQKy>RWFP^EBNC0 zq7DR3ZxV1{;OH;1P>Fm@V$+?p%9WhhgIXX+k<5nb6UkfgVy$cPG{8jPAdXZV@?R{W zs!P1#jJF%}EjiR+Y{FSC{oW04`eAdtbmVHCO)5|H#v;Cuc(U)dU;i*cty~iL4_-P5 zx+4+g(LLR%D5eh{oF_R5`NprryJVcz^#pg$>hCM=?HaKAjR4}ZKY{D(9>>!&GKWrd z?<o^ah%WV^58CF?%8&EPv5{0zEuhQTXnhjg?!rOz#%G=0`xhJ5;r(m(O={I{VPM|w z-PwnalCF>m1;1MVvXWa`Mc?Rkg=o*%a2P&Vl0%Z(p6qlDH>_!n7LyXk(uONqI@x%a zJgtlE1Upu2UR5|?%>hw802)!O$=`9|W7eSSE`oR)Xwu~kWc$fpF!QS`%&xQ~5N#;t zl%w{=$Fy!RN#rXDm?@VP9AJ+8Cfb1F>>ue-)JljXl;8C`=-TbGe>D!b`O6z-M{oUC z90-z`qRZ=ax-NvYqo{A2%awvIL<aS#V6?H4z#H94l^_*triq-julYNfGY;VJazxW# z1TIAFc?G9JoO+~3Dx|72UqYHf*P~7oXLt4SeRS69{2)%tGlm!JDi-}1=M@hI?V}5P zl9$qjY+qw$29JrCM*i68K+fs@Ek8Q7{f=VGKUp*RnmZFK8QKIHR(hj}q3w_=*Z>uY zt{eN%1N}wNP)FeX%E2YyZ8@odvk&Af`Lv+PTa$)^vX~eDRSooW_O*a$rWO~HU^b{y z0;jK_m}4uN&?FtiUexigi?c?Rax9a4RA~8H?C5%ilJ2FXJY8iGEVhJUsqsd}%o&uW zocbtI#U^;^Zu!Z$(O`RC1S7~}d9D_V-r9^KTd*>@7X#9kAWT}%V2{dN*Zu62O%wmw zv*UX<z(pi*LUE{XR)tS~%))SqQcW5c4XH^78W(y7Bh9?SYSTcHZ5pnZ3SZx7r4ouX z%)HcRpuA0=4+D#*lamG^qF0usGj3%9H1HHcWI*YJJCufYp}FQa-~U;w<?^(LWqT0P zFdpmW1XXH_sf}O1S<Qmeg9Ot3HMUcOk~)V8oUb2M%hT{UlR3%;Ox*Bj$&8v|njt<w zC00iu1Pyc#KUuNLeQxiTd%ZulG!^m0bO&Z$?ImU${G-Y=vC$h}t;1!2fB+%YLu2Mu zMPbklE$PYyUjQ$9qo-Urh*X|+AIOtgWj#d!6ZC<KRHfp#JTNjQa^}>+0rfRetb-Fm z*~FrF>p}M;=psYpF0+rARYDH@QCtGY<&9|UtG)*?WTibT1*S`HuRrWM+vY;Fsx8t+ zP~a|PYtR=X%Y40FpO^kV8zSsb*hcZU`1^O;dZ{xz)=l+2Vipi+fc0e=Y0SCY{z@SF zT0_s8@DbYG^&{3=pDBsh6zw|#wpG7kDG-O4^UKBUeG&(5_q?ApA}I?<f08BxOTg&t zqQU8jwL`;W^XKehqvYP12-r2>2md9wajrWA(0(KKMuJjsk}PTcBtkn~|Mj=mWY29d zA~z3AUXRH&n4cgNc;?;8eS~$Z-AJfzTrl$Iho;Ac57>2=WBFA-m+c4e2fM6ZL{IA@ z%BVA^#xz0TA3Z_5K_N~dJ&f)2LJ@P&5>$CH*t|sIU{sPfr0}?^?90XK-x6hf=3=## zQmo9)C~LsSj8ZU^LLd<3^M829)4S#S3|EB-E{EZ8DPDbTu_Mj24fkIG-}Ra`d;NEk z<I4ssh)&b|gXaIk*gJ6N`R?7?v28nPY&C4$*!CSeX&T!$8mDP&+qP}Bjm?Jd)&E*+ z@AW==>@oHyNX9tt>%^SLoIh@GoAMGH7}HW&rYkHP_sQA#e%P<&b`BZ0|M@xmP;wzK zckhHoYKCzO7&aDXPs<Q9jS^JYb^}Hr5`8=*$@L+u_o-j))!&9kUR>i|ufLFzCbU1z zu{|hYfre1UfC9PDEQmarp#7@ne(K=JD4Sy#x2!z$$=gxjd6Nd`GCb>xM0NZb^O3KA zNSIHh!+F(a156XGx7p9@oZA*Y3bnC@#Q$8dTxx^+{j&q-kOGqHUsOT1NhnhKS<Jo- zQr2SIWh_vWSO`%{w+(FF%iUTFKP>LE+%uG<M+tH<x2bVu8dzPRMcQoI{SnEkwf+N= zF00k2<{zHs3(;yL^;AV6!n=G>LL*)m2VL-@YBxnDzrF7h)wM`i_hZ8@&+tf^*<Sbd zE_pX)=2M9E<aMOQ;9K@!`6*QbFH4&*GG6A}h25^owm;f$bxBPOk+Z|qbUxfv_qjm9 z?+2oYVLYZMeNcMx13YWfEc#(Ac`mDz&jm}!d?M?-KNo9<drOAWohK5@=cOd4#XS_w z5iHuwo*o0@>MhPkT5IPGJys@~<#M93r%~ICDTQ8kd(TT~)KYKWt<G#-xuX&72;e2s zdrng^2<nucgm&#4&zSfblu<*hON}|So#_!I0uw91b6xDjxwL*nXs*BezMicN<89-0 zf%Yo4c?WV)f8hv)ge}dcHO-#{5h{06DOb4~+)*vP<`asvSTYlb;a+AVcEJsaE=db_ zVNMfhS@|oa%Hv||+}n_4`f2yql!XZo<M#3@;)9<x|F(S{9@l)|9Yti7-&Kew!gOPe zWvfIziW-J}s#4Or`%(}ML#MCT6ED$WmWJo#K=q5L7Wm7m{WvdRhequIIp=v5N89pw zHdc!{!8X!17;>WWo(v5LBH;Z3XWy6~cODZmin9gQ?Sh<V77X3I;s)Lj$y!)?Ez5eT z(?LUWuUya0&skJ+d%>0KLpXlZ{O&u2`xZOlCBpp9Y*}+osux{$uR&?5KypQ1IxMxd zQA0yvSmtxjxJ-*kR!KYP*<`ue6Ks3yt(sw}<K`!H8k?(53{cGW)X!@n8+O6tU<@wS z8eS|WD}#$rC4mx67bO;%0>^-B#8pb8e)vf_F~sEIvl&ti3(9UhG@yRUWM_YR#RN@K z<iS5_#duM+!@_nXZ(O8(d_Uu7h<%);86Zcz@PGo3u)F3qjS2dGzHG-oDvq4)qTkiI z+rHS8tLHghPObUB7c!sGADt`jb*C0?yrveL+m++(<mTJ^{m)iQt16b8B<e@L+pB_K z!_3jHHj`S<nwHZNn)Lj*bo`>C`xoKJ(#LYvmh8K0#d2NX<c3EJa$~n_o?s$IvmsoR zW|}?}@+)X74m=_^d(IKx<kGIDRB>MV-8AI^^lIDC<Nb#h$?ElGl9ofyFA5zqWTw_c zMzK8{cBfE?B9SSg4%iQqyPsp5iFgipP&1()0!3o>35a7Es&Tr5$NPSaP|eqA8bz+% z2Ek>nIuD@`Ij+k1ai2R5HTZ4^ku1$qz|HC$G7+i3GZ`cLvP0|C1x<kZiuq@{Mv#-O zr?=>5sW^YKn<gTxQktx-<h$@lxIiC6CFV>CDL9M<^oUc(LT}O7nh-AEf?z+`4!kBf zr^>aMCB3_zvgw-Y3az19?MC}EqkpL&HK9C5et3V`3t4~vwRU$I23H1rVbQp;grB0% zIw_XPwa~#F8I5L5S-)(>`4N$hQiK$0szp)%pq*(^Oa7^1o&d+#AX-SZwUW&Fsqyaf zB1caVcEln?nQNXit<lW(vJ1e8Zy$EPm8@O}`s@mOyZyqiKTr2L=r1uVXVk!~1|k9F z17iiD3o@P(O=_RCp8~=dt%UnxnsY@P7i!WgK4N<=S!uCrfEo<<TV2+tec1q5TajSp zB?JJ64{k%M1;3!x;^3^(#9c~1%*NvI$2RR2Fjx)G+ULOUR=V(jQJEXwloo2a-XK+H zlwhL7Hq+!QeLBh<B7`cNSE@wVR3q&>XCo(y3sk}}Jm}nGb^Cgko6`y>rJ2ib!4TU$ z%i=c~kpC3=qRtINrafL_$e2CI9=cBY_$Q{El=t@sx3^7+aYI2XoUe}be`W_jIu<uN z{NZL0F$;{5_!H$q@fu~lP~(pWH1CI;1oIk8jnKki^zfnLLYrc%n}vSad2K1VqerRP zw=}`c#w^v4%nLrKdMuJW_77@_@DpDGh$8zu*N#ry6k4uaZC4{nVBLOpuV4FUyP1i& zq3~yoD6ov?aP(o|MeJ_TkQ6C8>eb!Ip`EbWH)}mCrakwEDy$6Qc-eJ6jfmKmyt!>_ z2RzL3s^J<Rh$+5A8pI04u#T4x@gSP|X9RY1fckK@w?H7+?OlU9OW1qpzBgqp$WS*6 zJ?~$JB_4Nu=gi*4(?2{`#ukUzdkHv0A~AGZAGA45jB7EbzZn(<QKD7)Yo>lDw^2P# z<{WLDnKV;YE3Z>q4dec*DM0x+x#5mNlgbaWnB{<N-sw?2<%_C?r%Xon&~YniIbA9v zbPqx<q2vxkV)Yy(WFHT7FC@aS@Bgt%C-4tKsEUo)5}V*otsys5Xo_HA+G3|L374#8 zjIFyDFzXw0#6KipbKyla&RaWzb9NS|p!*ovZ?RiWePH(@<eTRcdak<Y)DVJ6<7AZJ zTPE7aaJ2HQ7S0aCj!=3ON9^f3swQ1fcKJOol0q}D*d&?CtOAWTcBGp4NBg-RDvA3R z?<tKStQWN3O=yf`0SC8W!&SfsRug|*yKv!?ELZI~jq!{EE<`+GUb~LLeidq4xIeA3 z8UWfJ0-){OFeoj!55{D;>5pvGvHWJnK6x0$`ZK0QM*1a$jBE+ic(57KnZ7;0%AY<; zMHjJ-Yx$AS9IUpW8PI7b^n=$cna$GQ97ndO^l{*=&V`EUe2PJG|NJBaf!ViU9BD;I z<l%xoUsv9D6e3ja&o|MhKBoxdn!9$Dx`&5Zi*-n$8!Wh@h0z}~?Y1VAWWGzhBy8Au z-v_;ZWsFOqXxkjS9CeSvz(FQsora|JObH*$7WI9{ot$*|)6UO<Y5lru!D5!csG<8E z$3&+O>v;%OAqa&y?MHAbDw2waQYE|_;{1<BiAy)q)H!Kn;s8qO!U@$rV`IlxrKFl| zCp=Cx4`NULwN<~8ajl*Nv+X<QZQJa78ADjD=SeDb+{*!NU0;z<(N)2XZ3s_D?JpCE zB>jY>?Y5517Sdo$F7HwKFX<f6*8=}rU&AWI!!C5B!D+^MdE~}6C41|or)+5UYk#}% z7I0WySIlU>NZ`eC3El4J7O-CA=iC0>=oNa7>8)1m5GsVn?{LU(ifc-i@32r+b{=xO zh6&D4%ZC%0SZ<B?WP<H5r_SYuUYI^KWgk~^&L8ds$vbxDA~o%CP>SgE^=?<V?dEqQ ze{BOc!zatvxp`o4*rJJak9A1C|IM%gZG2oK^*81&-14Ho61pqDA4!ht#zuI#JdXrN zQwedSSf%W(9ywQ~8?sL$Oi*cIs>!i^wACV6@FWQADcVYQmoli9qG2?4K)<#D{dwa1 zs=hgZoSjed(gdICDLxlL)(G2K2v**}Z;3RIQec)aeqIAOv}hFL>w$I}+w|D|ueG{I znIEQmS2rG0@ulL7HKC%FnnsrPRJ7<AYzyg-to&Q`c;zY{`%)1BWJ$%qc0Puq7u~<p z9Rel0_nEOdgkLe%zdyM9OD%}om8w}CYN|Tbomd3US-jp@HjI!s6N;4B>H`(`Uu;E% zxZSdZGGm`2symcT6bps#^2UPaW?G3`C=cnB$K!msyVJI>GP6dR6>yNSi9(BQQD7GX zay87FYZh%~KNdKo%W4iJul}Qz7iv&z|7BE6m7_5jgo6I6<q?2dt{tfo9a$D6GEHS7 zuDeGHLGCRUT(}n1EZ})}{fyH2E4I7MXw`w3%d1+fsJ)A*BL*?do$$G_$@QpUsh&@; zug&_j6-T6Ye1*w>Mm{n&UpdLO3Z?V@l@_-2bjOog)(9&F>jLl3@Eh~iCy4}_@ayV1 zBW4G-gL2f2`!5%{-HxlrsumkTmN--MsgbjdbqE!%1jlRE8a6Wm0`ZsFmw4mNg08(U zXYUWNghGLZ^~2Ns1MPkS@KfnJg<OYr*a+nkID}<pf&B&FqVB31F)PW%{96Xnbg<{! z?iy&}Yy)#snqhmcyx?1&(p;DSA}jd^;de$8#r#nYh(25&bo{@Zu4QVo-_NGubd7v0 zygb;z5`fHUzXF|9ewccn7-We1bEMC!ZoS$h1w>Y4<)C}^XD#MrwSHw7gIg}rZW*~` zeiEf^deN!*lW@Vf63gL@9@_aj@TWS5Gdx9ySCa?H`>hh^HhOBlee7o{rG(7Ri*-bt zeLY9=%oRa33J;*A<x~2MnXFy1FEb^%Hi@oW&P8ybPri*NOSnt+;2gT6N2kE7grwb9 zR|r+s*&CN@fZ4fAZ--)V>Z7Zg|AUOC>ZF*O6{TM$O0daPJpPwvCc{Qwh62+`lc2AQ zYb1cUFMTX^LMnhBWy_))1I_A?A@J}kY%<WxxZ7L~FnOPG`>~G-)+QN5vsXOCI6u3J zEV-&5O_76P8t!HV-TOGBe2TQkJ}(?uyHx~YpX%RDP*Kvy`)eiuNAuQ(5!Guw;zr16 ztFBtF{`}?7dd)$PkCSvqIx(1^*<vu%A1MN|WMWpL>!j`{#R=|;)`zQg?$f@Ac*72F z<f0I^bu=jip4}b~ov;9OQT#?FP}rP|IVKqS;thr?galRuCOOr;_P_!_2QzdAkB2JH z;Ynsl&rQbctRj6i8-)=oO#tUn;-*SRn-dscCi&@b^}?sy%{xZy8es<JJ`AOKKYH_x z6yR3z(rVOtyo=g;Qx}a*H4w1^(Qr*jX`onZgN_CmQn#Ww2rk=R)RS26p&t|~6VcHi z#Y0T6OxXiz4W!WeW?x|f_7sww<S%Cvgc^a#c*!lfsG4M`MmW$1=#&|P7E>A(Cp{lW zv;+h1VA8(iHBLS*$s?Iyn@ZdH+pkJ*OR`(GQ4~#ZGnUo&F*VYUj>)Mr?)+!j*!%DU zHUWR50@#0(=3!<!<lqPhpSXTdGS&@t4X;pR+oWG^h0UVVJW=r>)HjAFfT)u0mCk-+ z=P8_a<dw@b<Y)6h$znj4mdt7LdQ#heB`Y&VQy<LyNijurh~u5$C-Lh;ZNDZeB(E{f zhiH=wEK+_W4y_4RBQ`MezS-)J!v5v28Rc>eQIq~$5+CJ4sy}^-qO?~`_nj8jYa@vI zQM&-HIA(ujo23(%@@o>}^+doz#kAHK#AfKI5tc4$6E+|>7u^uZdlbL`I_+AXPvz!p z2glR*Qd&FE4yhfQu_&b*uIiK?U&#$jbFC95#8tK4GHeZ}{kn3P-dJz*j%<4RwnsGQ zyO}QLC`8UjG%f*6!yE|ln(vl{UikbJBWBTYeW?u?`>Sb$+y)Is{2J=ybs_u`SYvq7 zl!d=#0<Kd0U5rH?qtMaH(J!eX?w|SYi{1Qo>(noVoo5N-(u01XP2Y^8_GK01s=HDu z&??BY$-0%{;Rx%g?w6a<PcY=C0KD&*{b$%i+99Hkks-B=(Rm`HN|dp(&Q~>S;A~7q zPOQa%g(^Z65|KS!`x`k7)>wqH$imp*WnqNVOB<cqz>2_#y~)BsF+#4LqA1Z$>$9o> zVdN@6`DVa7^cx_Iwy>wSOv0Tu!79^l{A>Up>+r9o8ODKg3vf`V#R9(o?MPpyy}+*I zzQegar3sdN$zf`8wYi^-t7S;Z9)FUZYd495QNd4XsS<WQL?q9qQbwgmCsna&iO;Y+ zqz><4ojmM@%fASjzhOZB4Qe0sdpx&p5ohG?S#H-_>m|c!GGmmXbW_)^;Hsnvu5&z1 z(;Q~tAbJb8bKH@;=XYhMu-1no&W`isNDe8S7uEIvhwJLo5K7@(<s#Q07jmndoK15r zIc9mbf`pA|2U*46BgwA)`v{c*j!;VC`7Oui%(!gGTvm|9zNe;GoU(gbO`J>ss==T< z*q$yE(MpW)X?|>yo$!~bIbf%FyB>T1EkVzhaniif`%tp!tH;3S5h<J+S)*(3M2D&t zIlN6sF!)_OdU)*;LKQpm`XIHn?nf>ZrwrEWk0pr9490i;gfOF?yEM*qbZ91{5(LkS z6(m@qQ>|Tf7l)zNX6ZWT#O-e6MOAW?yrkSQ*e^2i8$r!(gev}Hw5;91*71;FxOe1l zVmMxQly|N*Yic{am4k8JPNCtGL6~=3;rN&CEG>?N-sp*<`itbV_qhyAmTJeI4KA5! z?#a_UbL~+#NLEY?pIlSxl5_$|wtf4y{a00-?sM7$)LBZ$#>$tyd!HuK<j0aaXDh!3 zg=4c&15zVb>Lar?Qt2c(x2>X^Z*a*5+T;Vy)VIp-WUQ%(tApN~%FxFAjD8TfL*YTN zj*L_53TY1^vYT_--pl^-4K<bEIOx67d#JOFwl>1$^L5NJNG*prYH&IH8$$Xvu6sSh zak=fMmBZ6vnjX8nZr(7-y@r>~y*pcQd1>Yhh7C%zjX@YvHvV!F&@578mOse0>rERW z$8DfJh7X&?mpE{93bZXt=1|Vq8aN7LrskWP*EtgB2*h#=>!;S^fXJ1Zq{$jWGihf7 zq1j8h9T*&$lN{1-^|#Ap=x4*$`-P*2jh5d5DeI*DaDx|zN8}zM3G2&^i=S!0s~O3O z=KFYeueSZ%pAEB{Pl-Q#B#oks4Ac-RLbhLOm2xURR>L-orOU8>tH-d?h7xA9*I<)0 zpX2lAz3<s$n_xb~c@?lHHUXxe$6m`tI?}GJ9I<4xVm`0?9iCG2_U}EGy6uPSM$43- zKRK=}S(}c?7)vx%^X4}Y5V0fHeN9o;960+`J_U49wn57X0$Z?ofD8~OH4Q!q&s6PI zk5rM%Ua7jInfZ;e%E*Qvu8LSILwe9*H1B&V8NwY6uPe|@QDuU7&{(Fr{MGry%q5%W zEA^S-9O-DzWf6JOsG^rymb$q^q2i@B)h!#>t&?GJ+P8<Qx5rAQ`+!c=^xBJp)xVl~ z@sy#^@2B-Yu1JPN-wo#rSSjTrbt}<}=!=5oAp{|-(4ycGPA|d=Wh|z*p6*V#Usmy> z41EU31S*N0Z6O1kWsciL*(Nnq_yeNNSmfj-v9UME=<*{LqGv&Sc=_xK3br5dDu)z= zck0yNCPMxyZ%}KTsQEO2lQ{Go4f}5+u|5-?ygkmUnyA$WHAmy70m)6w^u%0wBa3P3 zdh6g)M4-<^*G29JC{Ek{3fU{8IHbGsMLWf+j2s-yL6j9eAc9zq8^pFXR~ZL-x-#^u z+0FjI;=Q=btC~~KmmR<Er2{M{>mue{NX8B4C&3R!X0rvKE{Ls00=qO>kx#FT$w_h5 zY-eVmLep@>ywfu8l0M?uI7TIf0PruffedJsu*j)qAhwv$xuYL2hgdk+<0HxSR8l!p z(&EG3HKpUp+s8^6K;{d7uen^RjjwipuesXE|MNk!h^<E#{P|?f{bye?84&1p<@2G8 z&0o6)U?!76b6(F14wIuFSj+h1LTN@7^{fSqS`cjQi654f=+O&1{8M;`3S)(CWvVm7 zFv&}W%v269gkbhNL=G2(9@roqowRKIvBkFyo!)_}4G=W?2H6MmM%Df@LA990Ko2YN zad$e`w+=ht!}#5)iqi^BzT$%8T)%bDKix9Y#2Dhq(Q~XLO%(mIFw_7<+`g{CwHqIF zp_iSG#1KvY5a){R+sydn0q!=g;ECC{4m?bmzD0I+wB)2JFghU~b8u*Fn1>|c;u3V4 zeQk1i3x<%eb=1ao=ZV>Q$CJhwqF8UvVg1{?5zD3djj79RN?*9$>)Se60yD1t{M!T; zRGkhOdq2>3@=UJQPtOkAHWZrDsNZ_DWzois3BcK0r5N6`a!~w;62E3$!zeSP+<z^i zWl}X6C6g%wJ6&XF7<`IO7Q%-klGQUi0O<(Zu_MoGKa;Q#!d93x65@XBe|s#V)ZLYn zEn8h&2)@%%u`avPpr|YwTfk72w*3q?Cfe~&rqR+B0hO*J_WOj|AVif3l|ck{k`rBe zA3nZ)rYZ*nwT9LpMs=D@OF-x8TF*|0Ez!@&U)z?We-+Mcm*YUdBM4{G>OW>w=z_jb zTZWzATHf$TdQ<=Ed=`OIOx+rV63~K`cqE47m(s&>Z}V>CJHyGy(If4rm43fl|Bb|b zk<S)0fD0_sRVV;o4JNnnj{XeQR2zDr1j^mIC8`qjz0a<x=~Sp<;q|4kd?Tu+-$~2k z>*H76D{iOC)}&zc0rucb`E0Ee?psYQt_3pi#vIQt7*VC=2UcFuR^v<dcwj!~zo6lD z+-Uwv4;=Zat1c1(L~AJJa!mTuH96ywExO7ydAgE?n&hkm%Ot1G!uQ3UcQ&|;#K7Rr zcsTLdU%TL*Z|M(?sLNZ_yF~x=Uhrg5;t1h{8*(15J^s{%+5X0rGDTh!CFZDSK%gI7 z2Nh5^B3$rg*C!!F@y@Hs9rZNMYULz8;G06$)sxBZ9UHy#=a}75=*G9O_ZL+T`mEI@ z#v)FC`B<i*eu{NW81DU!dQGEB%@@x#AC=ze^M<rzbIcAcIzsS8l;Fh<9dz=PA07LR zjPyv%q~7@WV0T+0MpDV|tXY|#zQl30Jy-#|GUS1(H^59h#sdBcTg=VRcjtA2mZ9HF zMcfmo(YhIJ1I@jhpU*XHX(CTI5v=l^sV7HF7Zme!0N{El3t@Z#&?r;WWcruLQ;2Ks zY#MYso`;@1DkNL1)A{qkb3@JrNo*d`wf~$Qy`Z>kQ1$XR^x06)qVo}C<MGQD^9**; z;GInsRy+ErMhtS;G&AwHALnOp-nqT~(YM(Dy}nhg;5GYrq~(8=eo@g=aW^54>frYi zH!X8f0XeGI-p-fP#mey!_60nSEw;L+1`|5f&-;m=!O-j0);qdsnb)m|N?0+!*$u%$ z;&4Moq6ZGV`ZEyHKW3vQ&eL+5{b+Nh<W3PhAQY8GK!SOR!?O7RCNV@CZS$O~6Q>s` zBG+!Jx{`a(Fl^vEDX)65T8C}=zU!y0(8A<b2Cs?@G#-@A>f-bhSi}}wZbjxgl?r;5 zizmfPpC4<4^n~WK+1*NDe5J0+-=9DHwV~?00!H-gvl|_Xe;KyohDeUnaTT{D7{06} z;2EvZQ&mFK<04F{N+6bq*z9<{a)EfUS}RDoHA!AKxIDkoH0zT}XrfbG{9cbf;IR<P z5Q~yJh_rm@dm`W*5f{7AZsSzhvy8{O`aouk8^+RK!XgoFWo!+owjXV@_`G`5ZE8;# zW0iX_c;)tu+<(|j>RI>C?NBjX*|8!gwqs{TP<S<qt{v)0bNsgl%<e*v8ckg^SL5>8 zjD<s@BIM6!B2vfsjO-8c+ul2iHt#RG_6bZR+j`yi!<+-YOv<W<;<Y!SU6ix2w?#7y z*!jwsS8LA1S@$;P7Q_NAC`TQILPL82ywC(7ov+p%mwmnw7aupX;am@aP(qy}EH}r% zPk-iYrb%@R)4i0g;HxP_!eygeW}^Jd$rB!cHTc!-{377@m9jBXcmL^5|8LqNBlRbc zSL9E;I&l1Hwz!6B)LQ_)DRYvp%%MV_opvva>+SdwJ~0mxWLhG*WbL(OVYvw_CA{rT zYjL`38-my-+<A;gWIw1C>@tO)(z6sTB2g7_xDHAhe(V$LLX+D=-QK*|7bfMu^g3bn z2(N`Vbnoq`5zrI)=uz?((5U)kP{>A(7P5<~Oav&$11Jg~hm9>(M3&`Q+`YaelFM-= z?<W%k153rrSVjy{8*39Xm@If1*oF67rdFh7370{Z$(Arv(d(@R9IAozbxB3Mp5xpd zjs^MZIKG~_(4(>UTMa>zoZXOr^Z9b+iX7HzJGM&kMEqmIXvzQb3N*^*YfKs0#0XlE z!3Vy;V1QBT$|+M+@f)vgYOURdnwYnb^1O!=w7aexci$uvw6(ez^VPa%J`c@wBXrAn z9<^5CGC17^9Jb~y;{vxO>&P*W>}e!FUy<3~Dpixr0k_;PwD!M625&K_FGOAhJGI$@ zZKul>5PG_Ambw|ZjM2zwGkd+@41zTm+YZDZ39I-gCWk%-Y}E9+Nnb%qW|1SQ1Yq$m zw2AWxW@KaIR+cFe_0~}3)2NKIape|o-p<^IZSa7mQ2N;v`OSK}U=8^B6vtifZrhqp zMf&B7b}gqpO3@ZX(~37Y*_al^Gy}gWERhHVfpkt=CxdJ@!=2+1ZEA54w~zPMYyTRQ zQk^U<2k4B~;*K^ZI7orQFGw|>L>OWabwP;e#Hklm8&`Nm4CudK=O30p5SaX{ds*RA zaEI7I`OYRvj;(v55vc$B(?WGTdQ|_-3*{x%&zgh@>%@53V{mdI;L>ZsY!J{H$DGwo z?7RSHd1b)Pk*z&+>GH|ym~X7=vY)Dnfp`rQ;AbYxOs}mCT?;obIn|!QB=y3O70tKr zE(Z~tNx$3>`}wiqtTGR@<8@$M8{36j%}$CHZ|$4tcv9)%&x4k=D>&U56rlM1R^R{h zhSJsdoiKGci(Flmmt*zYAZzqVN^eJfywn$d$8IVOXcjPDGw(R`cu$W)S1?J%LxI(B zA^39CrH%bb|FfZIeQeNO9u0h==TS?`05+^l(YXklot1h<@*}I_x{aJJ0x3RHP_FN_ zrzdn(e=(EYO7}|zvnQsd=*U#sy*#X5$%)mhCw_R-zogedqA084>g|uaM;$p!e|`*% zhx862b8hK#ko0@gfyTRA9S7++&P68R4H3Nd4k`!^Of4HGG(I);rV%0Zs`fYKzCuv$ zpbK~1P#!S$io!j~xm%r=j{MQxaP_vrnDlhBKIe(Y&S@RM%qOoko3)X%$tNi;aC}aT z;JD8eILNIwi0Tij`HF+N;4)Oeby}9nxuOD&HwRX<X6N-?+m^1lJi3j-+`vvt%_2nB zP8al#m}CC&|9DA|U%C8Q+NDD)U8)AJN!WM7LriB4_yfeyjU&)W&Sxl3l`|5whUr|| zSjF-WEBvd^$|QJ}b53?{wYg4q@-A>^GpzRNU9$;D=H6$;K&!`HNif1*qcVWYgKu@Z zYBL^oNbb~_OVEL#jgdx;KNAQx%ii?ZpJSBUZn_9e#C#7FD-vT!W`4A5j7yot<VB>V z30U;>moEKW3c+PdG2k4cHf^JE=9hCn#i}^I6jHucrkgediwpK1vm%x&Y|`8H?tP~D z!O-b!a=yuS6XwP5$uJci|8C80sXZ!(uOYeV1MP;&BFRX@S4Hv))e6D#JW(SAQUGS@ zMsGLc@uif!+tHo#Sc4sE_gGtX9G@1nJgz3?<dP%TVX=p#B3<`%D@$Wj^_zS{$<Voq zWi$;a$WiqvQcys{Ah|q4d}UKL<Tf6<AQFB^I$(MpLgmil@H%OTX?Ntr(+KY+FvmyH z)rf1EWKA{opGDijreLErI{pHLqL?$fkumunJqbuJyRI+OSq1{3EC(wC`sk7x(zGIe zTbp>$eHpIlU3($p(J9g}w{mR=D4oXNe}BPco53;k-iJ(ojm^FZsK)_xJu4#)b4`si zcO<V2r}!Uv3vj`cN|T>72PS`Tx}*(Q<Fuob;M=7GWlW2iPWNcYc`&|ZNJbqY4;Iw| zvHkT5nNFx0r$D5%&_TfbK5AGot{CCz+oQ2V@XE9GbKQl{F_Y|A#Q}=Qgn{AL;}TS2 z6Zaw^%fB4|{Te0g`(s0hVF!{!9k+h#tpQNXQCJUWS!tLF?O7tf5A9Ey#Uw;Lm}lf( zA7tseAy0S(z6LCMShLm4z!8jK=X~r|#Sb#4_bi!pdO@Q(xVlx(!mE$pzaz*f4^TA% z{Y#985>66%|1!$Eg)7$|z+ieOFec}Mj7LRpFwUI??WXx|M@M#wNy!Dp<Gg5O|3Yhs zH2<?}%SJ-Zt3sDt1ARX9TzflTtXrY~1DTkj1?e|SyO9!gVo^uR5JPrvDM3^6DS<v_ zZn5vaHBpQBRg|pj&+!4MS*?~YOm0SMeek@OwGBw#yqhiRG1?PgvJgg}!S3_X#*Bx1 z2T+=v2i$M~y_l<s!0QiGmOrNA!LBu%wQSVUBKACC{4eAX`U-Td0t6W(Dl~jKsanrF z5Tty#Rp+c8XH;nI@`@FU(AOuaY3)D<$;(ONuA`oPFof_x-GoWFl+@Y<8GptUX)%h$ zH+-jN5Qnnqg~sP1vFG*wJ_djP>li$%6r`dKUPrBIa&Gb#m_fGnw!Bun5bgeC+VYUc zyGnl^8Gjme_)PQUa5yzrDmEexaq&S6HN`&Xc7vAw3WmxY_a4ejlDz0{3T+<u4ON6! zq1yKyt66`var|QcnjB|xtXz`SMh)V7gD@p){w#H$6@Wr~AAAe@8>7M-^JF>G|H@WM z(S+cuVn<EFh$nt%9Na?@T1kef`Ynh1Q6;?4Lqe^joj}gCVKm^)L?>g}7V*=+nzU3# zndy~*d@jz*Z5HwHszr}WvzFGGbc=57zuz7u8LWyYOY`Sb9y(2adJLeowpXiud*8z; zZ?pb$!=7H{5u0rPJW@2_QvtEwsx%d~y`R*GjxGygI2+jU9}!M-pEmb}izHvm{cPJF zpYqM$kJ6(Y#};RsH$x!VmbadHLqC+Gb0pteQN+ObCvx?qUG*Rs$XRXGJb`yCKaVY* z2z|Lv1hBkk9uy}F{<aj84X1=4l&EdCK)jBiV046z^LD)85$=d74WiS9M=T8SR?T?h z9q0Wo{=-Ne*EQM>g1%eDEII*}II<SCg{VS@J3{)}rHj1gxq$Pjo6#IwHxTsybkTu@ zSd=mb9>bv@c-1fGB=}L<T`;%uOcKt<X{NsNt34|qhxPM3AIcA&{<sx)-3pdeozEc* zpWiyuE4dhnKs#I8nx4TY;k>+#sIN)CH_YxPdT#IrS`~6_S3;n%c^&#(lbUo`zfUcg zqj6LiM?-^c=^FDm5YdI_YdI!=MByAp!Qf>WuI5w<gUr@;tp#J#xvWd;1rMe0B6m`P z8!#{uO~qJWDd7A$$i-HDSk#w7!!B7|2Dn?iiZ=HXa+B*q%K+`dRtx5d7^WsFyAqfy z7m#{N<^>~O&nWs#!J}06&tqaHemq{9-ZTzyNk{P*QOJ*$kNM!da)~T^B;fmi^~EBV zXwf-0W5yVUDW)1=D{^5@X8S1uiRZKRbz;NsZHtcxwh8Eb!OPp3tB-&#Z|Cz9?a)uj z`7xosqYS}vcj<94pv&!Z$jDQBvv<YebgUtOKP<l$T3g)*r>&HMH91_)K9nHHAzCPB z@ka?=sK{kV<kLx!(@X3LZ~FnNg3jeOi?+-Cj)v`ANOP5CE-fWOO3D<1J(wM+`R@*# zPFwF7I&Al!54KcNcH+O@kJgPEiH*GtoGzNC+FdP;LviO|co(3{nMj|Knn8rrP=M)0 z={>=7yGM8j$uVqlJ9&29Skq#w2>Q%9Gbtp!<%EO4vfMR=c>AY!$O%wjVc$eY74>WO zfYM8+Ht;SVoAU)mUvnsq?}hZC_q*8TT$gvW3f4WR;P)!-4_3^lep@R?3gwohV7zVA zQ?ex~1!R&&pFq?E;)lDd5Y%>}CS{w+TI|%g#3q-81-D=PrLVDf1E_HZMkA6S@xJm| z?8go@ENO{eZs^`h&{=lo3u}Ehna4U_;-zzoi$kIbZbhBv0j0{~Zij0hFnyajvG%w( zD<r36lSAfo+VmY24ba~iM0C0RnfS&&`jPsbODgSS?*sayIsVoi@du`!W)sIIyhQg~ zY)RUcF-c~3^PU&08&)rRBdAXq%^$!%u3&>_`s^;Q|E=dTw#aRD|HZFhaG40t@fuvI z``P?t1I$gJID@UH%RjQ3wPnV?uCHbn5`nQ0Wji|9s?f?k>Zy#<+?f7(T(i2rC#*jR z?PkB9PfHR}eQh8T_0!}WAi0hyv;@qCi<s1aPl945!18&Jz{Je8(W;Zh`s}ng`9s36 z45XCIf$`tVY#7x8k)tiia=Xz;&6B100T>Jwpa@MlYCWkXjNF7>4KMH~uC2kH_=LrW zHi;%6_wwi43Aa&y5^#)<k4(Zk=+O{l#ht8~9U$wsynG*pRU`IvvKMzv6ezqnvIqar zR{u&IYzD(|S@rH?!8n8h$x8-vvir@}1)K>&feFRvWg(i;*MmA{moC_eG2{{(TW|Q6 z?0L5P3C*eN^M~TFNs5iF&2zA*DZ}B-lqyCnC4?%@jEp!LQ)S39wB8S?Sj=scrx|GF z@n3G?$Ak1G3M`m=><dd5PpRcp8Mau#o?ax8FzBfnzVI61^dih<N&LND<am@a+3}k8 zE{^zz6;xBPlFJbnrKJKvPF%LiR>ZSGaE)!iv?Go2Y(`u0u?{coDKt!x+&+&70y$jy zJyo~p3NM*8n50z^q2$3g3fNs(Rd-B)hyKJo;NYG#g6Xn*#8y8h%$Hl?Te-yEq0R>1 zBP$%oU{nCwgQ)(4<KO|+zy00;np{`N=$@u(#zVF7L7%^_rYMr%XiU?*vR)rx{I1A| z)(z)s{$Oa=PCjIJ>@{-xm+31qIoPp%a4_)W|Do-ji9}hWq=7>)AOi&X8Zbs>KDs<6 zviBpUJ5c+CR<xgql>g(1x>zGcuyCl?`e#zpIzbjDqE}G@(GN{J88#PC*ue6yb*3$L zihLJXK*T*<zZzk2(0|y;<8kQAd<BC2gkgzB)>xTcZGtfU9Qj|j&=}z*+ntZvi8?W) zNW)`E#i1=uNaB4q@c8fK23B~FE(`(p;X!jNE(PXRf;J%~FuSb*p6%CS1vpq~vRD{A zZrHNHgV59XpG?wL$KLyQr}XD!u-K3UZfp~|b$GL&i|ax${KXV>4TwRinkT=W<oprZ z?ey9yYR{_pV~@!u9exo~Y*A3Igq(}S?;DA2wn~Dm8g(3hA(Q_R={YCo)M+b5TQY<E z=jLoM)bJd0`PQIili|Jm-KQI)=;l^>be#?K6FaW6%gMp+QpyT}p-3=CZ?)0%0V*Wa zIMOKE>mUU8Jgp{VdiZqgO|weE)N6bVww3`xBc@NHU-Kk)@eB2w3bv9=Pke*}HPB_~ z#Q~y&XbOBe<~rH-dZJ*g^C6KG^wGyYVC$H*j>LJXTp`EPD+A+JWFq6B$|54^$-*%I zvh`)71E_0+22#v&*A?=plpVtdMnrY>Dm$(`Z?xV#2n8j^813tKMACMRBw?jwK9N3c zmf@hXGJiIX2&-wJHR0pMd9Hr#&n+O9@;8#$m!P$BIWWxaL>G9moUK4?;98G}yD4zg zdgbGJ`D5+`pMQ}0Y9cn90O4g4+MdD6yEL+1xrXG8ygS{vNWfvdt(*#S&KDmX!b`mV z(eu2bx6;=z-0Yv|01>w{%fQOHy4veTg|n&-MNc7@41t-lXOA3)X?-bXdE&UCGBnPT zYbYy=Ksj-NHO}z_zYVIVfO9XBy}yQb?unKBZ#c-;3cl}UUZ2I+x(V6`uW{rEW-3>5 z@VO|l*}d5+JxB)BnuO1K+OmCFsz0W+RfJnLdeOWJ-f>HJn=9f@_b+^cJWzVXA58vn zoCV*a)(JKbEq#tv1)}^Gb>jVrP-7()zXzk`n_^l>P%TQA)Bc0zMqC_$`zOdBe3>?l zksMwSmVY3rf5k()oM})BJpM%05V~{_?sx?n9D%(5?+(zOocCbpPHNO7o;P2<6?)*{ zK<SXE3oW*4jTqxE6CJ!8ptbZlgVLh=HTX|%r#rTgDX<GqC(SB+Cyz`ml}CzyDQEC< zwjNr<b{fqzK5f#i{k}{Js6Rh2+5)9$@_i8&Rr<~AeyeF!DD)@R4tzDcBnc<{>g>x^ zr7AkyYYo-m`o5G^Y1s1h4ybxqdk7bqS_S2y@AF;24;q%jGVr_i48+r&Y%(vh7+l|A zNEZt^2^l=)3e@0>(1>b_Z%zVVTm+7`u?mDq7y2w?T~B5<fVuM>H?^~>#yG=t!m4hH z$`Qgspuo<i=j-X*@4Orx1{X2<hTKhPfbF#;v3l-*Se2HX$S_#0tuGSC!pCfuBUr5e zB4`)K#uQVHU63YZq60-<YKogeYo2C==<7L!a`q?vYMlv3!wchFqeA*HRryFA&icc| z{JCF1POCpHmQa1J#?XD74as~P2d=rW1!EV|7yiy2@a;RpE>m*b=G{qIxkg<x<sq=~ zSO!={d8$k(STZ{M^u(P%qQi)zmqj_?^aJrIXs&y7kx7L1_dTz>>$InWZ)^Fm+aY3g zUG<<-;EDEUCGsrJxGM3^jGM|R#nQch0bEiVjDhG<g0)EOz~OE6m#iz%TfTg57aSyb zsIa%#E-VBs_YxCoNy#@Qs0>Y;${znY2b<sDroPk$e2bun%YGW`x9G4K>wB%%33}yb zbBMk^cL!wb{NG^6f>#`kCu8b+qbhq0Z2IUH7$wG`KmXT-sgTAlo-#>U_&*#!l6tZ6 zs1|5#rInOmQGQp>j*7geKZv}mLYy@MxwhMP^9ePl5syKq)(7Y5%jtsew!od*@g79i zrM~0^E=q{PSvFoajKCiusR{23{3yrd-6IaM;vv|wjac197=fRPM=`u&eVSxClE$^| z<I2sO5wuUhY3tcvTW=@;eoz5@F_w*;XJ?U~j$Ht?&<efM&RfVRG5hZ3KC0sI>9Z<1 zHva6lJ)Q95pBS)>k+WfU{ELgT|B6Tp=d^-B_VqW_`k6boL-**A(F`>FwLR;da$}XN zmTaeMpp#&}GL%_JPJRwK)n==7S)aM`S;05Df;qj2L}jDuS93<}GpH-!S5Xe;qMcMp zqM8)FuKa#<`1`&lWIx95Qh1s$Y!Qyb_r1Wwu+x0=&rOqY0u#4`_s>y7r-VvpsUt>e zA6YM03HYq-npA%gSbSror&DT%Eq=4zlh_<&;+!}O<Z3NGoW~!~{GSsRHPS;&<W8bK zV&|PY5Jd)UTI@Le?mu9BTPC&d0YM^3r|}F?qN=eYH!2}yuDO7Pug|+lWc^x+m;KLW zx5(iC@;colEIcl%k%mlsCGdY`b%`*#n91bvJ>8&o5O&%sk4^jY1Abn=TcHU3n6K{Z zJ({+QcQs<_iwO(KXzey*>Mu&$x3f`ap{Gh}AjJ_no_g9dP9wneEVOyF(4u}PH!VZP zK!fj8hz5Wq%{{ddKyh#AX_rU;nO0<6VLgzYjMY3DXt1{z>2#w-L9|3!m5MtalSyUO zngLn!1n_&%sff-Gh#|<2WLUd*5DNX|J~WXA)1r)ouaUS0EDiaSx=SfOIch89>m$4_ zjblC4uJajU8_5!W-vi(NYQP|iW7o@e1+8s%<}B%{3MrmUp=SmW=d~KUXQ4)y-_|Y> zZ?@{$l6uxM7b|KLeS6R6ffFK+L}>2AfJ!Xy(VUUWppssRShi7#6mOn_?Q`cCsV*Ec z#vM)vP7VP^iO1u{lWC%jKz~e9|DLe?jKjI3#iO5%qM63!;YdB2*ef({XFhTJ(Hy2> z`b2EYyHc=j)>I&6Vc!sw85{b+bt<gYZmAmy*TQ{H6tD-Lvyd<azxkgZN#E~P0JG$C zu!(o=7)r4^sBREJ;!X=EBvaO+RKNDCgspa(J^qk=J2s-{YeJ_kIei|3yD9r|u@V!P zEsc#r$lx(2BfhMhCd@@ZivmyNoLb10JS|Bi?&i%1s>YV^(XiW|TJJW$6G_)_@c13^ ze0OYIQq`87wUjxh86HMGCBFr>_p*q6dtx(Dn7+j~<9fY{-yz*?Hp`eJZKn$}mCdAh zi^8AaQnhy4{r{4T5tiQO&-Y2cyFKT=um6bWC!E=R)Kp+&jP_lRVSF(GpCor9(qxil z*9xDWmMk3Do%c^P+3XRiinrE@NThGVN@V}?dt(v38+{#XYRDy(DAr?*4)I>_a&6XQ zZS1=fs?_}wH3u`GB#pp@cATXvQ<ujJvKL{qV6}0u?nx*5GF6FXixn3qeIjaXgTpR= zWJjD2j#Z9;|A1IHb}Fu@Bx&U+v|FzyykW$K2M9w|aDETj*dOH9qdProOi;OddZQ#5 z>s%CJHz(X{=02`yL+-E|e*rT>>T~_Jt?NqaiYF%i`uu?f=%@@zTe(uL@Wm=;_(I#l zQZdsa`ezWESM5~-)4b#S@Z*J$&+Y+>uFr(x#naJQJL}pH-XcOQRx>r}!_`F=26GMv z;2By1GuZZTlZ*XsZ97&@@)fJBFm%#%vXoUbJCxca!ph-nix%i7;#`DfZO5p3$ul&; z##CVXXtI<F?Ap&cbWt_E;m_^}2WC>uK?m{?R@7i8U(e9y=j5Gqg8;|&&k%6tyG8O# z{T>LI<W0<EK$a^XQy^YA{!RL;=gt@(lB9=6@DXatre_S{bUTm(%fRW85x2vs(l^>F z+ote=*{7xLQ0WuSN4}qYI2f9GfCfw_m7v@;-e#@@m%+Mtlkuj*51(#I6^pAKJ=8FH zHXcX{Hks^F>g8UrX3*IN;Toa|9Gj8E1OHZv*or#mF1%Pl2jP>UES0>3%THf8(3VCy zlCL!f2UH<8uPW{_ibaitOH*B9(2kYNWdxm43U)$<F2y&kriJlxl_uTKl!Gn=JbEoy z3?ddm?lIvvL1?gj{U>-9*(wP-v%y@A!Q_~E>_u|u*37q<{41k4&MbIuwiLwR)=G1K zCVq_c?!4VVIhS?u;(fY8W``jIKCoCZEn6efUbBo(>L8g{T~Hh7B9d1XtiuK97&rd` z<WS@J%t8m^{%d_|RQ_2X{J9YyjD;f&I*kbMd>hIN3EJ>NICos<tqwcGE>9*6{Ieh> zG}4jhsti}VCqN-sbg}OC`t*I+Gsq`4?GDH=c>}`&08%F~uD)gkO#3pAU$w_px0ETt zR5?%uF4)8qM}ily%ff&B6?FeC1P>3xVLxTsR66%bf|0ol?(&C6lNBppR8FC>CP7Ju zRYQdN3sw`hA^=)#2t{o&XksBjh|uk8PLmH>YnZZi@d<vD(cFJ(_2PjZFHh7vHUB{t zE?}B%-M~$_X<JL5blVkm3#DJjH&Xi=mI1&!dpz*XBQQ8KZYQyKF{^J(zhRMr)$o+o z*o){5)4}6s#r!hYV;)VM7A9gEg^iBK&slV5Y*(yQ)JpjFLeGC{dwEQ0-gWobx}vk| zXV{Lyf>6832h+uFcxc|u_ZssZ|1h$XVUN*pu~p9c^JKVFCb}wvf$ZL9EPi_Q6P?=M zD!dF;ZN#Dif>yv$lo2MXp@aGU3V<+yJ2<G7a6-G1Lz@lg?FX-K5jQ{?<>Qa5Z27Xv zp!QO*M~aI;M;Guj5&NT4MAUQ?x7)B{Mlk-S#I7$#o$jqBhV>Mb<7(>Ebt2A`O0GTL zOG9&dz%OEJjkgY*w4pE8$P?$uaj^utVNs}K3cg%;THk4RIt)%Gg@m>92U(cWh5KwU zZpODC;nE1d0AU>9+d_+eCow*Pvtrq~Jdxm}-&ZNPT(u393BtS4f%#Itw3(?r(e^J| zMO#&`ojuQI#2xzz+IeFpcbv@P@@-*TT0gx}^94($Ug}6$27dB*&ez7P@HlEG-|KIj zi9eMznL~n8nCO<dxtP85oyi__9$>5pdA|H8`?n&<NoWdSiT1d%T%_E6Usjpulg$;& z`N=K6i^v=QVGf**K@|wlPH;Z3A7qe?2Jw2&GfCXoEIwKh8vMDE$YkQ!(a>*k8K#xX ziclhg+%Al|kl>7Z<+!tTqqXf;(Um>MIh0WY_AQ#7%4Zh_ZcK@L`;Y}X4l3XOK5|n2 zIdUfACC`(-D>SEqMq^3B;B~#@BCsJG$#|i_`B#1SUVc5x=d?e#Gv*ABd2~aJG2tU9 zR@ae@c~J5G@QKTcfkcaYyvkm#>>!<G6qSd{LbD;rkYTD!PMA$e=Ya$>gpvxTw7>cd zH+#CSnDtIS01R#EskZr|+|0Llm>{`KGQ*yKVpS<ZZ`2!S(<1oliAwRnI+Y{S{hVGQ z`Gj668p={s7;4E2TpaW)sBOv@AYrE`EL?d4do<gHyA_ZqQ52v>lf$V?-@m4;)5Bgo zUm7n5z4?$1hg_FU29-|Y6=b0Gl(3Hss~DUrIL^fUtI*H!+8fE*D=s$xpHNtEZ@iCo z8I=tW*(rX2Fg81Ap7s4LQGNL3XBFYHfd97~yfjvUt72`a1=ZW?hEaQE)3kweoJ|>W z%KS=oK7YZR<^!9R`ppK*2H~wiTaQQF74)a%qs(1Jq3cR&fHv6pM#>#!7{5C40i?s2 zEnzX-1j8$~+i$_r8<jcVannml{{mT<?!6fQc~GrS(p(g7C!P2J)bC<M&AKGJqsy8l ze*;L&Il@am?UBN&7&UQ5K`a<N3^%#QR(3MI!g+4){LO=UP}*vrA;y#8XsAUKDFzRm zmne+Ct!Bp4bI>b%52r8z7!{Ql-u82(#RndcS*3OgdFk})KXDo3%XviuFD=uj<We!h z`&=Il+)j9)pN3SBkmrfQ6Yq*X<?X}Omp$ij)a5=Nm^dtbJ-YNMnULpsKUS7VdG{RQ zDS6+$&229Q!|+Kwwid><YdBX4BlW(;6-KGmxnov*lIG-o29WY;dJv>1>j&qv>t~v+ zFT+6FGX%6jxC`7l^0$hdm~tB5hnCjdF3Z8I`SYRhh{F2t$lAt0uOAWP{^^ueDQDdC z#fj#h3(RJ-Wc3YP9^Y;zss7<SCrXeJfr{25>;KuRZTq)W<ZnQNp@n(d{z~7hY(NL0 zZ>e#X6nzOa2_`z<%TFrgHnqfJ*m?e-m=Om%cVJwOY1=z|e!5K9VYC5;Q%wT&FOcch z{ISS%ZNu!%c}dY-nO3mFU;I@CgfIrlCVfXhM@v#HfPLS-`L?PZb;U-IDop;}B~0p4 ztY(LP*9&M#M&gWNv)jnU`7J@%!%`knWv?w|1n!^adX$aovvr~Pyf~_<=XKM-q}vSs zp8NLAOkA?lHRZydstAP%`WzmiS<U6==mIZ-TY1Da#QDF{j{R8kI?R{;Q}A!gerMjd zxIh(e*3Oh($?EucUmyF=`43A!wHqC6eqVv{GGio*6e%)Eu;n9oBT30ig9Wt6C7xpB zs;YS2eK%F&C%<%V^8KJI;MR`jMu56FIB-<3e%=8(>U=CiG{FQW+RA>4Wp~s|zW_;C zs@9f`f?cnrl;VN2r2-ElAxBqC{)3hX;*tP~dOVOp_#%4PbRS6?0Pr8ZzxKiqnFI%5 z%rh!QoYKVTfR|>_%6Sfcg-+71PC1=~vik}qPuHq#3)xAt2yd02G6ud8Dp9Y>i@EsL z^p_8`$0-~rV1$OKHbwZ>m#Io9zQ~4QXr63*JkmC3L15m>xl#5O+(=mWp-&&6$j#PC z2FF&Mc(w!F7~C;~<;!Z_-wH9!cgWI&)9cXUO586{A)wN=+Q2?QTG5ZOtC%6d`@4gp zL!0Sg6S#ByuAq>801jY!5U?KqN}+M^rW4rh-;UnEg$+90T+TZk-;U2@Nvo>(gHn_S z>G7}r%D%T-AvdU^P==8RZ^Ah84wxY9K61IRXke_WMNU&(8h1IDTCOqJE&jY{U?Xfw z#mKI|HWb5WXB>TWo~SiQ)yA)a{r7X*|2f;y($<C>C_65C>#uC}9zIRxW7IPUl6-rz zkZ$>PQs7upwT^9EU3)IBZySF%dXu`ZbVs;+I$#cghpsh<SN3SZHC5Yh4}NHw*eeMm z@XM&Bcm0#MNuXxxist=<h;2drJrghzWOe|5nwZBYSGpbmD*LMSo8QLF9>{x!noNkE zZ9SKuP<WJZ>dA+z!Z|+vzn>aa1LoGc-ICD=-QX7ixUb9ATOZ$+<o!I}z5xH~--?}; z=5NGr`~0;xTNz%Y!<6&}tKX&SRf9U$4U53+OChWLuYzbEgs#W6Gf9Rrs85TcRO|h2 zShM^?QxxX^BoO@2zRJaUwBjwU`2a_%NM~Sl(?Gssib3tQ0*9m|Lm~DP-vhFc$dYh` zA|U?*qb4@CWPnyr9UOe<;DYgBw~%wD4w8Vy=`AF)F4y*%;@>U@i<=fuf!Uo|BMkH4 zZR<UlNO)OqnmAiOl<q&1WZDH;`Of4E|7U^6NDlUV>T73^vIC_;5Y#c>1QXcdBU;E} zf5UJTZrMUvP;e3*r<!11B}N$4bRyOOe|e4g*!27pFjUTT5DbOqy(eAvZ#${O^rq|j zlm5xY`$vNZ8&+xDg4l37u`7Jr7^$mIdpL=}OpCtBIDln)PCMc8-knSFbJ?n6(XV|f zA$4f8AM|-FBbXN=qpYpX?Ae7$Ga1)wqnI+Si^flE-i(|S|NF2^$!HMy|GiJt{`EdF zIq<bS{(2%~Uc_yWuL9A7`bvOW$qV^w7|4v7ED0L6n{TcF)12B~bM03`croKjzO!*L zBAm&CCy9aOF$!_En;c+jReqiQ|7d&busFNzOAra}?u7)m5Hv^u!685ecMa}=;O_1c zoFqu$T7lpmTnl#y?!kjjk^B96?$gsf(|4xlU!I4mI`4bV-fOS5&ffP&9~_q=96MKh z4zo$9gnkKrxUsP5IC5SKhQy6w#!*!^4<-r~T<^OoYd1P;>0_eoC#1gaP;k6O<u)Pl zsbB^{S&TcA2%~x^8CzI>(A&YL#5v}`8aEV!Z4~x60+tXPGtuXTlwhJYpDhh9+A=q5 zD|+PXBaOQw|4tO9rT;c5P6XOHd#QWGDd1wnIT*xtVT<$JM`^YC3g)ibT}*?!yJipU zxHd2R8a=Mw^qqDVusif}gt@ve6yDX7#e1U&9V^&j0<Ekgf&TbiWHS3wS>dVt*&KYf zRrU2(9z&NYyX+)b7bQ*|2%@dgYsHSU;<x&)o2H1>3IlLOx)t*ruTz5A-1IwCL+)z& z-R|<$X2m?VHmH6$d|V{I-~Zf>{?76(^7<7t22+p{j>m?jk!?e<D6R5;9W)y^Jkda# z`0vB?#Gd+J&8%ME<Ouu6iN;wGS1x)wf{M@~6Qu<;hl+%0+Z#t?d_yNs+gKE0PBR<6 zOju1Wn0@OJ@(qZHlsVqv%3At+>jYvfZ9>(A%Gw5yGlVMT^lE&VQ3$HSLeIi2<n!6} zYkU-XiN112O@Omg>^{6)NWz@u+e|L_4Yv@~)rj8jD5Ez9*t_L#?OmhL&LMulqg_E$ zZ{vQU0tMYVm@f!su(K(~CComscK23%cR7v$L)UR?+~86N)~5W?a_5e5oryZn2AR)D z{##GkV`5eTdGRJ3OHV(1_*Rxo4zhanU?H5h+^P_xmd)lx^#f*=+&c}L{GQY6I;O|f z83R!H^5brr0f;7(53Rs%ZMp!A%XzuPF^HKwxBN|y#mo$1&iOJ~OXnwlI?$+_zi>B} zk?pQ($tL8f!-Y+9o}F*#n|V$UrWg^@y7p<cSKqHUO50Op*OVTYO=3VO667O@$j;@6 z$$D>BA|O-BLEcIhTngZ;CVXAcXau+aLAZgBomC`|V2ARdg|2mhc2(j>)WW&xLU~gK ze&h*H5l3h|W>_ak%oB@@P4peARre!C*0eNiBrxiHWA2qwI+$uzP!^LjBDu`Iw*Lhy zMiNcmwsp6FX|in_9*<al4zOdN8GT0_j0aQs`lLgw#v41O*pZ^NVf$5f795kQ#T~iO z<%_hV$&zMMcEHF^UZhJ~yJd$Qqz7w|{^5mBYv{|4YPPQXk$B@Q9nFw3gbeGNg&N(L z#vX{=A`bJc+FyT|zSu;>cum>DeZ@|)ME(}pY~H+t!;!C9A<ZHv*VT$x&t_6X#sH6} z>aSK;SkLj=yoi<9sgCmhycD&@D$P=)e3)?4<kQ@XlQH2y8=yH;H6Q93T^TFZ;o6we zOUu}e^5^Up8K?JV7gM(8xz&@C&Ro!<8-BF+SSbZN)rX7I&UK8B9a2HVJ6U%~Us`hX z#<UNPUTTHYxQ@ci)m3`^4Y(_bh}=b9hF!{@c+wl<GD>pny{~qxe|hQ7AXK`3pGS7& z!|3pTS$}X2>%=wTi?g78LMGGw^Sxm+hXstgaGT7+UBOmUv~A>C>$JCA4Q@)Gx%Jzl zgt#+3EL<N4$Wuw2qA7e)mXDZj;L7L57*F@&OwE;QLD#v+^q?IWoeRdD;4<rMnHoZ4 zbpG9&y~@1|yNB(UbYo1$<o|GqG}O0L{Tp@x7MWl46KuliDfYxj!R_nN4`~{)2<CdG zrS%!A5hkbsy_>~Op|LcGyv^(-BNY}CGztR>HCQn+nB>7&R>Zbu$fT_%aHRi1yM}iy zrmX8iwGO{C!csjigCV9iuNWtD^%AEH?FrK1Q*dNMj!<@19_ogOSA~DiDCM=9!Q}i* zoX?j*lBXLie$~|ZwWEYmr^Wkltsh>9zR_+n{e4tOpgH(7w$dza;B2rJ=rHhe4x3Ao z(Ss)7kSpCpLPVY>o^Lb+vm3WdG0e;&yF6nJnb@%0Ow&TCteInAj`XDo#o`=tyD1t) z0y`2$_HTli=BIWB*LOApNte~-pD!I+3uo>)GQL~P{&73H%c(oa_Ew&5ZFq86^}lRt zwas(%_s(-EYioYGJ<$AZWvTWijE-w-dW>fB{z5IX^S$dCmnY#amwafk<ytVyAH}^i zhNYK5mB93I=<W5gh^gYCb!ecw!3T>SZaex?#n~5zGT+~AZO+HmAH_dfKvZL~H>DKm zqKjq$_d(fUgMmv(UgP>S_=p5g_^}7rH*kde=VlsX?Hq*sLffRZHc~B*G{O=oY$tV0 z<i#(2kJ}2cs^XWZ(;Mv1)xQbQaw_sKhA(s&)Je~r-8$x|k?4d;gJKt6Kr7Y(*{i!V zE`7Pb+>alQ8?vvPR__Mm#+FVC4*)6QC<E@|o3Uvi;%1*w(Nd2-=D8bnQ}I6H(T>^o zf1HYRBbHOXp=GCNr8~0OON5?kCKa^Yj!e^Ma+eIE4^^KPvujmKQ11dS%YfJ!W%iVM z_<V8>%82n<-=!G*0ZAC&I9nf78b$Z1{ZDJDeeNDf+^ON0+kd%5`M5T;%v8ZN(AhDX zUyMn7W1N;ras#vrt>kyKshUP0)Qj6HOo<3wPEil!QY0cqB+O^h6?zlOl&LO~$Ys_* zvQlsR;Vwff^bR0Wu|Yj*H@7G>dwL@5eX$Vil<VdTJW|aXV@MoZhSx+~bNlp3hB0wV zR}~a}w#nR)2R2t5g}SZcv0XsCPQ$tzjU|OhI{?!_)OWA2F*UQ)3q7Y;hZCd*KN#c7 z=cx=?__}!QTAOb?oNO=ua=pDP9O|BLe>%E*T0b#lbx-l#S<%}{i`QDY{IN8g4VtSJ zk6rw{Pqv@<i`6<-R~7i~n|+P}gCE~C-H?s5hO$VY{R4V((B~$Wve#STBx-hz!{QDk zj16XgV8bPS+LzSVmku5W!a1J;8%~~-JsGPaG-k)$o|@{S7jT6VDT!NSPHQV~#Q~vI z7itv!qra^FB=?5PdPIg!5e#HI-CC__mt+eFI`WnzM5yila%>9CS%Kaz!#Q%^6L{-_ z+Q@M-^H>_wrc3~?5bf?gbEcEtHxe)3zVTYnvKI?J@uu;v$_^zkQ;G^nkTkPeTL@m_ zAvuf}y`3e#by2<O%cMby`Ry+jg#Ah7;<GQdO4sQQ#mTG_=a&XSEp?*D5GjATC@U42 zcrwzUHY7M1K0J-xGJHi)=sUcoF;o$ICLh>PoMr)|^U>%{dGWj-o1C5=O+Ib?&FIz& zqu)-O>v0#aqI&uh5z{CtE?g=*6S$2(VVUb1$*X;1<Z_-4aT>9^vta{@vOuE@woRPi z2e{@0ojldQi4b`^LtGvjTPhwKBZ;y5HJBss$G@PIA|&F$9sA!gX)RwnK3&}Ac{~+5 z`x3T2g=%U7l1Mpog7}?-`?z;QYh-1F1l79QXfdk|0m3;U-)aN0WJ|bd74he5-W=cx z3zodJES*Qu6che^x`WX%AuA3i1^qY+xVQ!;TuYc4`4t`V82A_`%sL%)-U^zkrJbYp zg$bPZx1X}iOiXh{vL7VA0X09yk+N__YtX;Pb<>msM+L=)*4$yWwzCECHaJ6_Jx6D4 zS6$v8n{k3LZhIKqhi3b*f%h@lVx5n{kHpRbUh$a}5$rA|Y_O({xeODW);gMM9JS?) zvp+%?Zi(WuW?A#?uoz0_CZU5lSewztJNkYndQbP;{;u1zkH>eM4{Z%6Cw|M=t9D0b zrE0894!iSoWK!`v&H9njJc^QuY1kzU=}P?25k*v6JF~t^*s|k4fffxw!e<MJ?N6sF zEutXr5WUHPy@xyDt`8#xWEHS(_=0%J>{VdCREnl6<7Md2*5ZiufON?+9olgE&wTYy z9EJ<J*%i~x8`(dux0vkxP8<)P8hI*xRz2ObSAlI;jh5ZNJ|&;Cw&ZQvd0P^^3?~tb zHi73iFJ_S@cvGTa>LOMFK_lwYlXvbNzN})LNRijj)DBCPHBWUWx)ndkXznL~Z(LxU zq!SUy>B+OZokHLu!;=h=Zo_t)|Jo<&Hj(o*%T$%f1Zdkx@hz8on?hr*yo7AVMT63$ zA8Bw6seLmw`AYyPduFb+@qt}L*3Jxcu<CvVi{6E3>r`k!H19Msz4K(k_jwM^uRLRL zH#N&$z0g3mZ%$=`Mx1i54z=t*TXM3W85*WBv|fo<f`NSJcKUY}o*gviP_~F&En`1@ z3>ix;GI*JF1#D1j-sH&O`8xbIn-uDP$}E|rqwW0tds5#xZ7X*k*h06a`(g)vc{J_p zyWvbfurcLb??XB72XKrYe}W9IS4I}M&>I$;`WX5p$(ydxy?RSi7n=`rlww}w#VlAL z)G5K))lGBx@gS4gsbk`F6kpeYakUAsS!oDZU9fK(!y5UE2f38{+yqOx_LI3Q=0df} zOt}(rVOIihl~n*7@3#<84%TC;U8*(xRlkCGCw?6SGW}-C(j;O-=9Q}CO=ySDaffzO zFZl1S`rn56-`24CT^Nu=ANnjFk34=pP>$O)dMc2f1QChrgNC>xs)#OxSO!_-9DG~T zOjCNC#2u9q@|{Zi;39uo{Kk)_i!8`0(NKPj2+2pV;D1YkuY_)_kmw(m-V*5%zki*v zbQecOqwno>bGe>@@$z_!VN*YHtT!J6l32`bK;RK54IolawSD8x+0t)1){BD>^CJJ< zzV%zq#xU;%0)ZySmatW}63hH<9^n4oZd}{}4JtbosYZ-cjcIyI1pHg4pb{JTz49bx z3<ab}wk);_mD4%4o9~C>=8~pXIj7#8J7j1-mB1&~-Kr?3aj{x$u1~A}Pldu_F8Pq} zFgI}s<h%2S7s614J&IQMc}~k_9aB=8*KqH$E3&d}x#}@V4J0#WV1v?&(PH%ZX!Us( z+ZGh!L4y#}&z_|E;@p^S*!qA}W>9Y~<&nNWr(>DF!?#dGXClT(ID)SMR{cgBP7YUT z-Jp}k_0tlgwo4|lPk*kTeg=L%T<CF}dALL_6*I^JJIi|Y0#Io%@)ue$OBMR|EhTcM z-MX@Vj(#&sF<XVR;8JCyZ|Px8Vj@DH5l3!T_+g=gz7lhW-)LIR*u=f;5)z&p%jT$K z3(9KQtF>+pC;eBFh*$c`NY$Z4%LmbW(OoATRG%Z5r;3=|fA$Rw{M!6^_E4k(^5)0- zRf-foTc3*0QRhS99l+|{4E4kti-)rC8kAMc>67pU5;-9FZqh1+IYuUzhOuP9Ozvi} zM8zJ6mOX)c4Bddcv8mWV3ts3MlXf;l9QN~^Jt1k|C>VJ<n;m=ly()O~O4M&FtNk?A zg|mI9EF6&R!amg8ujz)!yx+5It+M{E)KVT2o$1<DmuZt|&excPrs?4DfX>ELORin3 z@649km+QHHdzX8Ep>dsawWaqsR@cVZp!esXz2-?+d4)-UXv&*>c+?=h4NIP^>>CS( zZ6^FT3hW+oE3f{*ZQc(uz3cRM$Xwl~y#YL|k`^!g)5$x~e-VyaVA`Z<VKJ4$<M`fQ zlpM_nd|C|#Z>kxt@cmKzJj){HanQ;2dm)&`TrpDmoF~*I`cnaPD7_Yy*cKKv`PHps zu+97!bqqnald~((E7?y4r1VJa2os5MA1H0m__$cnWl^uA_DvMODBNd~=mhyl)1!7W zTPG^8{ejJRjWRtYgaN!HT^lmmYBjO@0|8;s7z?u8#pi3M9m;IL=f%0)A3F@f*KJqJ zBu^WHv;C=#XT^iU)SfcAe@18LGm#gXb+|mJ-`PnqjP^S47g`}OxhmKlgH!pxN_eoz zSEe^U{u{z8SN)6dTu40WXtWlkbVGKElF#3zV9ry2D#%JT3OVt-m@hl=yN_@8I%&Uy z9>D%+$^7Y-;DxNC$dEH(SkUd;!r=0ns8Tqu2YTNF-ejp)>kUfNAbd$G^cvQf8eMLC z<)}v8pV7egB1+@v2biVQDx^xKQAOhBILh<^By%wE5&(qlN^1!ZY8g;hF2nrHJw4JC zIBP`_K!MpvWSe8q1trIBNi-(725R55wy?kRI_k7^^l&{kG`tcZe?n4y!kP~}kqLFA ztp4;#<p%KfJwG-)ZHI>6oh_aew;il=U$%!#N%BHW#ZzrMY>0IQR$w;WoQz=|_XBVD zLtB$&$uC18XS%I+kApvAXQR=6xA{h?(RN}!YvX}>fW*<XOU8N2L;FjaS^+OO5wv0c zp?u^aGSOw4Bon}UwP0iCK)&^e@7(Ew+450dx*y8W)<4-%$d|1@M__XaWW+tR-Z8Fv zpr<E9JQX8kl}SNFP`ga5glmA?7dnd=f6bH{A=;mT{wH^jp+W2x<@3_R)1$w@{Sukh z{hT+a-_{~$0(eQn4Cdf!Y<&Ca1F;GRfu}~x7e_kNdh0NG{T&nd!%lP>V2lI;CP0R~ zotzLG^rEPx6hhx$a>bqA*8Y?1J78ES*ra8SvWtk$8}rzaxuynd6)(%_)vI=jblDgf zsJZd>L_<93_&GdkJ}0N1QHj<PW7EKGxkWyjgYc?$&gFM7+{@10Brn~Bca>qeGd)uA z8*Qrx{5mhVO$*msH(YJTF(l}bzVtGi@LHc@uHBh_sl=RV!G#SXjlx_n9R_OfhSW;F zAsEg1!4xTic=>7dEw(>#Pg(z$C8Bk2zXycI)!Al#hvaBGN8tU%I#XVsbbWC1`&g)3 zT~B{6!vA-)XlE`(4Dj3cOpJJF5V;RLKe-XeYmqq)c&JV0H)?4<usCvKyHVAijjpAP z99b?Q5jYyxMsDT0LdV&bOQ5X-ez`;af}rSE!FtZDfqFrE7-@HVlfGpN9?`;-9aR@n z$P=ZaRe`5WgrlsOM)1~ccb`9|{n?W{lU()lgIck?<y{LY-%UQozsK6<FSF1#+NP6E z{3<X<Hh8)_P2I|E3OYeWHH%xb`y=;Zu>IIt1EMCVv4=6+2>Xa61BzF|)~h&=+ktr= ztA$Uu!&R+E?*#lG4{-VgL^pFqYQq)h=W&PpA(^B6IVSR#?2&G_J}6}Wz{oY;9iaxl zP#17Dd4OD?zN6FPI{13|;?(<U`7x}n>d%Ro$2`u%Oz7(6_PLaJ(+<nw!mje<U^_vx z|3cd+Z#E=T@6GYdnylR>wW<fw_<O>nD8p4{0#TOQ`vHWrUA{;4Nm{q7=y3+;OD>{U z5u6Afo2jC>FO%RRW^IbW2HTgA^){53xv|Zez!}T8=f55<bMwnSa;WQxZ6Qo%%JZPF zb$*=)9R5kqy~S_|xsp#b1T`O9ONVL~8JEQgwr&|szkGWjMw4!X%f$@Nth}4i_OGwM zBNg)7`FZ{D6X|IU@$(=e`DSEOgUC%vYrG5AN*_K&O{7armJ&;5hkOox)}3{sQZep| zLK2%rr9WR}kz#3W7k9Y`YgtO!IEvTUl5{R=|1^Ih@p|JPz271sga7WpPTQyDV8^Gk z3bFGVHYw)K6Je`wLRfp=3(8C5Rv(+qxeFz&PJcXL<ieAM<iW~}Qs>D8FRy@V9mCf? z8r$pQ)Lps#aR;-sZqAeMH3*fdU$8FI)|2cY<$$T?p$m5o!W7;YUJEtA3SL?nHe@gS zEB1l<Z6=m4q6S~zi8O`ngZ0JFyyCnGV!tMsALAwudo4(>Uc=qBw-f;@mh!G34`(mP z{=`jwWO4%)<>qKsK>mF53aRentRu;9CKT%vGoO0HLqGBT0U3LhWKFe^pMzqR1(i0c zfG}5+E0_MvEXk$`cIoLx7hXuOvFmCJHqFM?QSjuX<pf_4=X}eS+M~XC<nw-fD>zuj zqz|zTE?so$nK77RtZB^CTnIs5ub^)<-M-aIwi+y8Wn=Fi%&%2wMECu2J#4TU8K7@Z zV2ziXg?%?d{A&W7+rE9=2fAH~^*;cjv|rnkM7-}esOnZikp3ymkQeExyd{;%#mj6i zH1*-jhhVwKIHDn{MbQM=RFN4FNH1NkPkck<uAFu!`;^`r#GGndB2|b4ZP-c3QM)i* zX(egqug~+G?p=22Nt=hZJ*~0%?=9i&JmY*{4}IYt%+z{5F?smZbp+ui@>z-Rmuj3e zp1{7=$e-`~x!oh=>oq}T;g*jP{IvK5fXmMJf6&q4ytgA0>zD&d!n3h17w|n5tp&&D zOL+=J>J)<1h7RWK=vJta5O9!7b-2pQ$R*S)bQMve%l$Y?Y;Btf>b*Df=FSd&y0>_) z#<!m&xwJXYCXWriV(@3Vi#*i22^eZ6b`uF9goB1>s`3UVFVOQ}Llv^#)3&72YYpo^ z+2AW>sGEnHYkR6|vS@w9xyWsd7Qss>v!~<ea)Fw(&zp*z&ef5u+>bv!MzS@l)e3Kk z7CWmVxJ&X8;UflN#s=kW%E`QZpiRGpKfOg;Foe^&z)!7LdKD%_Sjr90>tp%86Y<iR za9zD|;%sUyB-|QnNzk(bHu^^D04~W&wOERevf)Q3T9hj&OIW=858lggmOBiCjR`*W zT#GH{DyG9h@nEuGPZ?6h+GA_sSdAQ$m;KTkmVtxwhMwx7_z$n`Pm)W2CW)cbm&{JJ zBG($rM9m@!F%gmiis*oc%5|T1^rR@Z?*AAGny;=t<h&bgyQpN!^?aMT;0m%u(S3_i z0ng<$FZX<4Rh3mp4V1pIB$t*~W3t5$-fps91g@xQuY_W5+33+Uf;X%)IetFG16|gN z!XF#rjmRp~kGymnTm8pD#SGRKn<or$dT-K%rxkbcOOiK&WokG!*}Wna7K=zEsiF$! zPIK0a0wS4-lw6F1T8Eui;7K*R86?P>*tyA#6${Ix$~tpJotB}u4;jFj`-sj*ue)6v zeT?ZDP-DR6WrzVB4mMf$npI)4{0#7@2|VpRmsxNPJX-ZT=>!=%&;A05!6Nyeepaim zd~h+Z=Kj{Z==WP+{|$z*tNevwTSU2{nPv7ubxKQWzj;T9CA}6N4stbx<sG)~`1x!C zCQ`h#agIGm5rrY;lV2)fiN=T%R;tI7{<=Ybel@%fxAKIrSM!3)9ZI@QhXKc^a}9xg zz?0Q<9vI7Cb?H1xjsI!!8X$6z)C};x{j+1VI||)^R-EcCZ8eRWl!!5xbHgh0b#}#q zfAFHJ;luX7?Zq^<zkBg^8W27_%^$}RIj9u=U=jUsHGfS1G&cNktFH=k6(QN7LX0Pp zA7s=B%CAebn%qpK5EG3p@q^p7qt7dws!L2VjhI)(<PG`CiWH(<xZf-bm~>(z;*tLO zn9A0molBmKsg40KWScw4<)4=)X|aA@VsU)s&+qS#e(W)+wB%M3bSx=-NRMWROE-IW z1t}Mktlvk&s_E-dh*4HVC7w4)L42^<++)C2#u`re5{61PD6W21%2lkB*UL37xlA9E z*lnEDFBw8SS#!BDPqECPeM2$|g_S?&YdNql6-+_bwZam|Pg+#`n=xMI9T@%F24vlO z-he#riA)I2-<1laJsgA%1@vOPsXe#vDR6eYbyanDi7*o9kyxN#4Qth_f>fm!7$OH3 zuxkp%haAGTxIvb<74XBx^w}{adWk7oCJ=5#gsj)C<TyFt%j9pMJkiVBh2XQVCa`sz zn>j$n82|>7jcELTJb@+xp%`<dx`<?B^u8ePQMBa;>-L*6u#sQq_v>hv+Xrjfuru=2 z>$Sk-tu_=eVAp{nG#s-38gT4c`{9@L+1POvK3M`%N6_wXW|aL2tQe`lgs%B~uP*t( zi<|dZsbDh1n7ZI>YfnPAvq9Q{1iR8>TH&c&%@V|MvC%RJ;VuxO`J>S6y6Hoe&*iTz zrW5ZYZ_YpY!R~{fb7o;eNq2Yy9mSo`CaVK|txdS%#w+I-YP$+I{i6SNcnkhAJ7f)v zmnw}5`()_-OP}Um)%y^mMv`8OoDH{8U_V8#em;j}<s7DaRfpB}cPJZbY~23TkmC&z z>8Lh3G92;w$&6t}Mh#CNA2Y$7q>|)|n7-bf&rNS&v|^{)nI9;Xo3D^Yn1z?l6lzUZ zQbc@t{1_Q?vasa0Jq^Pkpdl2Y6<N=UCp@Ph%duNSLs>19gR+WMg0!ej7?n)Nn3%C4 zXo_)St&&TLbdc*dQ=BDI3qPRB_aQI{Q0w&eY8}UhT|}q%0N#L8RYD1?bl%n+B<xW2 z;WcGn^;|K-bRGRd@96T}zi`)QrHX;MTOxiX%Nk>yY*$d(h)n^6esrIN155c;J@T&C zns$vjfr?f9r5fC_<Ev51u1)euo)<Yr#-IatdeJDRD@d*nAAna(&H2yvsU9?#FI)6_ zvz1iViXPSIvhE}7p=;cFaRix_WL_$FOLZZoVvxp05pk=wAYDK#0oewi#U`9Zhcwd0 zocdJ%gm9NwxR^>4hn0f#A}J+f`cky@%z0&niq#AG49!DsqF@ZUu+GuIp?v(UlDXvk zoP)6~`^zyUh*Ln-_yXzc&4u7|2mL%SgF)yU+p)$%Z(UEzEvl3}mb;iEfN6PNtgtyl ze4zFs8RICD$dynW2bz&9h;0l{_$Xir%VKQ-@iCP5`|mQg3wnqf=D3lowR*GgU2A%m zQjmHgCP5c$nr{5W<}_3lOP}^o2b_5-mnAe+g`R8fQf<D)9vr|~V)VKos}(yJ4CD4v z!_%qMOs7KNjDiUj2}rZ~Xgh9Z2TMhUnUwl{QN2lrAA2m)$NJaKifMpE-sz<Sqp+n2 zFc_fDY5iErorzuyz{IvDL8*l+r81k3&XsjT7=kq;ldO5Ei0zn1B84BC>>q%ezplYL z=8GwvjF1lc7=nQlRtZdScUy)IzTUV#?FvM6Qj-fjBBjussy(HP20En1yjYk00)D7K zJWC<)G}CbZ<v`hT-y<%#;_0#$7`(1+fk}@5&<sBAtn~;h$C$7jopiwzOspE5{8+i$ z`Ca*dy$3Uy`#k-tIST%pf3fQ^uOnWg?s_a;@EM$<c+Wt?z*}wiDZW*{Ck&R8zBszq zCW|BZFB_osXE<k0m3RRYe8VGIgwSVRO6o5!C0}xx^_NjEGX0-2>Ze557ZB&wV1q2L z1H`mMxd=JT`BiJe$G3^uKUhpUXt)ku+cP;4Zb*;SUe}iN&RH<hXLQ-|R2n@T7R03Y zv)6;r<Kx1#bGTukKLHP|gV*;i_hdBIzFD9l&-JobV92GX>A-p`2lm>uQFNjP5mCZ} z#h6*U<L&3ZrG=8{9Da<pphsQx0#;D<zA64Fl?<zbOg_EG?P*NOO>-S96lYKg?FiAL zFwZbm3jSHkWRoT6D)zfEptE56Fm@ceUxBmf9_tEP{NyzMTEkNv!6@S!8s=ywuJOIC zxdvc6nQ>Dz1c+(jZ^|=lX$Dgi5^|$}iqmF0XZ5#PNt`bZ+3fbLbucG}1Y7aVDR!+t z*dG$H2vL3QB7LdBR8sO`>jjQTjC#9@iU+3@A2C7C!8kZ+n+^`dPsfBg8zUt?$Yv{y z^O3F)Yt0oTe8P3=?LmvMR+NCS_?jP5Y`!i{?7hTFhSn7?1#0$RtZeQ3C136%w{Ox6 z7E9~pTEss6l3e$Ts?YgHnLNtR5$#(ojB6b@%?TRkvbV=0+;5`p8g_9timzSw)W*63 zBW=%z$>Tuq0wwwy2E<y3$=<p}lM|KOPA{vD1x?RDxe0e?YWzlbE5VkA+|8f04n*x> z)a9nzg688}z$hOAS5sJ{XR>J~K3`yBF=L3Wg)qx^KPZ#AZq1N7CMOLUvM>r~yg~}t z6cp!HDfYQxT1;S~3%;%EYwlGP$x%|Aa*DzyL>o$}SNL>Sef#90#;6MsxFn=3>9mP> zLSzzj6E5_uUWX_eVn~|OphC6a$&FOj25l^Q*webjq>BmrK-}qwG4u|dqO3D=DnAhS z)cy=*e3@c#tjWd~mUw0E0l9D<1ZzdO*kbiZ(i|8t`r#Mxh#YR)rPqQs;^hs4B|WzC zj5?x{1yul2XL2`zJW65O1hcSw@*v5O1*KI@(1quBJ7$X6VHUZ8@1yC1pGP!4su~s= zj~zc3l#B6f!bPm{3TtfVyn&rM1PC$y73G87K9eC!;nm@thakJKKQWk`@#vFkHQ^<h zhx#j;{%Vt<0luLeKn`maWj71*H}vL|=5&JWF@$l&a!cHF!}mpbl(|q4WBj3ZwrfPr z!%k=+tgm9!2)ULIE-n#5K6<y6{!<f}gBsNj=hYg#13F7J)d$IFU1CGCn!bvpM4EfK z3&_EN^=~knm&U{|v5?NNW2AyX64^u!{{-?EpZuSKB=EJNBlNRIpu2_@mxPo5Z9zT3 zd*S%;`Q%?-LT<tSiBRqAKS^+NUPo`=JHP7U0VMB=TWt*AEA%U1JJFoTUQYGgdMyms ztY4pER=XfB5vm#s{|qDk&JN?~<MkL`zP-WQwVu5OMnE_tCOb&{eurv8azfVUHocoC z8cgx<<&f{|gaK=nsCBvgA};IZOh<P$?$r0e(Z@bF13S6i>shsBkmK(9ECv||u>BVG z=DPk%xP066ECH4x78L^0NE?YC7WK)h2LisTMMTYty=w?bCe<#m7%%0?dbMe_bL--% z_uxXnLAl1ZnR|5{p10Dnema&{^TU$U>Re`npC<U=4iIt~cqpp?5GB8vLHAhex@|-G zgV=qt?rB$mgEZH${T`}7oQ5iLr7KPxmUOBCL(q3niO|E;-ob1O@hew?SmxE1&`o6z zy56A+2;Ovg9o3<=*vE;l;knw^kkDZH@WXWc528&cMK%dR@SY5$)tR`J8AnA@m{+hI zYlrNe)#*1Sf*EWt8L0vws)il3F3t9gp~d~Gu^-F@Ra|@40+s$wi9n8nRJS_Ajbbq0 z(S}93f1eh`2mr}zSkYKV;?E0&QRH#9WEv4x$w_!cPogkF80M*!em~E|8gd#XO^uZ) zbUlm&%gq(A+}s@XoSRQz-O2v1x!Lv_e0hCV`!eT=Ip6JG3m}+H7Toc8^6Ei=B#PF{ zQ+1rIXz*6&)=RJRp=cWC`-E2)^CAx^jyd5gg}0<HH#Y>U;F~`2Dve;5&L5P6v98a& z<_GTl4uL4MK>ICPi@p?6SX!Q?L4v+Owp2ppdi-9?igPYc;7r{GK&}(3A=~ER{vpCD z2kHeB+0;J2J0*D%gsbOlbY{0Cy*$J=S(rh**NUg!xn>!Al*s<k5IG||G4kFt*=4Ik z1zpbl3@?~Y(i4$>^IE;w?UWCJN4_7RB<raEvccbIb;$NK;?(<w22(j8O9I`lZ*Kgb zY*S`Nk1B=$Ji2{^u&b5&;o8WV|2Wm?<xf<a-yAX>Aokb%se|@5?o-_?48DYS@BW&K zD}iwv&sz}1^?4nx*`=73h7yJr7XOt~r_irLTo7Hz*2h8{ZijTzUhlK%JAk5%=3c^I zaHpSNDD*3pfEA`Dg~X-5VH|1jg7f}MefECjbWE2cT-hN4!*b}{+2BENN5qS@gm2Ua zMj$;=&U=7i$N%7{{kVcSzWL?K<o{a>)P5gi3lmr5$N=XC$#k@vxMcW%_rTLRo~&7* zlY)D0z0a#x!f8Cn)Q?`|1kQmUM0^-D+o@SsF+-E#SP75W3ip}tuhOZ^necBeQ`^#P z15HW8a;n4ISJ--;&}G>|6p4J<(ir1f044Im3!7~yuY^6%`&L|j-->XY3?nUK!@ox7 zqCi;TACe&;RsRdboL`5k<nhwJ>KVj+#*}|zD767f+%r(;sJFt;Nky(r9bDy93?5k? ziiY%uaN;p1(om5namUqmn3xneNl9;nv4?arpECiLheNnL&H6UVB@ZEgu4D*d9X;2t zfj_JV#`Ha&I<+1r8y!WCl6U<4PN>3<QyzA*)@3nQRBDWVv&C0V%3!6EHAr-(7FGgg zAW5NT9*Mo2G#HXH2*Wc6*3tw{)yZ$F2{9u`A<SzfpseMUnQ*77{>^G%7pruu2-eLO z#PlEhA9y6YgD<4{eh?DVg-QLE`qr{*-931-XDbxrR`nH}vz!M5gv4<)VrG5nTPkQR zJ7gEI)bjp0wNRCR{kaE2ALjVTBng=3WP;WIgUw!l0V!9vTBjMBYIIH73jY$zPmhdL zsv4?d{|6r~jCn{LJ;FqsRWM0`4t=MEwTqN5gAZ}98qK+;HJRI)j@Ul0m@W7m-(KCM z9{^%%{XE+Qj>W-j5AXXmK@8LQRPJY1*5;|+-o3iZW8u4?TxR%HyT5A<^61{H5;k|s z)f_EvJ5_>L9)R|u2cOP)F2veWztI_mjy7J2W1HNoAotbQU3%nQhf@T)I^!9ON?nF? zWq*QO1xJT5Q~LRtOAvE*?!F&m$s#9xpe=E+$;r@}f=dxpwi#VPodpc%zTB<?e+M<- z-Swo*;q7gQ{dG5t=8V4Suql;Jo8HW<#fEW@AJ28=QrI!3^bAkDZuVMd#ctXys=(&2 zF@=!r<sr&Xx1b!eZ?Ua((vuY=q#=YX0lWl_m^8?i*CoRAp)be7rKpK{N2Id(65z65 zKZCMtcBJ!jTqe~8@pS(<Qdqf{bJpD#;Ujxkn}yx}h=^kJXK3efu*A}L-^JUZ{Z~Rl zJFZ{SOs6h3Pf^Ni24W;dlevXh8%b9+;!e{eEWYkA)>s%hJw`utU|sVxWa|xjm$Ra1 zDEETyqj=rUCu_F?%ci%f7~iOHqrNI=BkH)Qx1E`C-iKw3lz{?+GW6|Bot!ol`CvHy z6m4>6pA`+@mE#SpX#FY}d|lHcBe$V{XrUIx=%fnn+4j@5CSVO|NZO3b_<tlFNSd3# z=AHw#IW{!c5ZVNY)t9lDkeXReIcl5|Fbb+AeS9S=M^P0e-wp1SlpFh~P>=H9?m&iu ztR%ag=hhfWz)So9$tXWwcOb{#l6ZZ&<Am$48utVLg8on^$$kW+D(>49R{1+UY#U~d znEWqu1cR^aaBT#=1d-(|bW{+u$c9OM=JQFmkMDv@sL~Qkk3>31P#kr6Ee01|j`Pl2 zIO#pQfahV1;t!K=3dItPT3KpubUV3H$I8fjjZfjI4U==P7C|f50;@o$RYY&S*2KeD z02~ORZse@E(>V+z5Mh>%GBrh$u5YaL#<T*nh}$yhTkg!9jl>(OMVU#Pn*v2Pw~T~N zaW9GAJTdkl!@qWi_3KD{5cbSIh*hyxarNS|qubBcj&&58L|k&1z@DWj9ktn`L==fo z2Z#)H#@7bYJEUo08l`%^84s^!s{M5f19RC^kz8sfnK$WBmSAumV+T6SE&x|F9>49S z>j|2Wo;l%fp`aOr83J2Af97&x_+L`cU`2whFag?SCNUZzB$H^e<l7ohelxK6Ry^t9 zOL8#qsPC1~Cl6o$h0c6tw}rn24Yi9|LuQ9@2^##*c8h~_fwkwLg7AM6R2YFrxd-U8 zfCS=<aRvm2$pXpu^h^#5qi=7=Yb8orz>Zhqq>+F~z0HP0-x5tGW0k&$!<5e@Q*j5> zPOuqKqai}%sIGY8D<Fckh!@7CT>eLaEp-*Ziq(9(Yupc}po0OeUo|XK1&BXb3c}o? zjXm6C$Lo1^)NG9LbymiqxxQVmvLBn0+l^rsDw$7$v(Od)S`nAG$KKU0C%>7F1v!2A ztle~{<F;zQUgt2bn~uc+U)B1%)jYtN+>mz#P(!v<-sJ>-g#+a-sb<0J@;KB|-MFfz zbV1R7CQIdt_a6kS=+W`8+sIzG`P|88J&iaT-QSU{{9gMtCVJ;{P5Jun6GhF5QY2NP zgF}DPz7GD;(q0T=d6BM}j#4GdL3Jt{r9f4v)IjZ8mED;aY_{4%?{}R0b7<ABUp#T+ zZULf~G<o5fgpfs7mt?RiGQYQm&$tUC9$Xt~ud?GEA!53=7_dT_D{!6>q;GdKAjq`j zF^pQH+6WF}$|UT7ObK7vb&FX$xZB%_otH*8pF{wWF4NrKuZ*yW`NZsgM}ZMhFvQ;= zm$jh&GX>abT9Lu~--1jnD$MBe9A<og_BCJ)q<-oNFMX&ACDmRl&05DvN@uYm?=ak) z%2tomechSqYfCZp#8la%g8Ez~Q&03SlUfmi(<SHMGF{^ycm=D#8aS#b&I}L`76(4? zD8B9B8xQu<?~V^DAX~>WfoG>c0Ktx_K-RMgA;j>XsStBBP=L_=)aN$CUF%@4>i!#I z6&al+<1&kd1kxfo$$BX)?w8+Nxg!OIB{et)zrhJ0Rca>FiKtVg((-*k*KY}bdul*O zXdE=0rLV_Z;z4I(6$s=wLG}yJn1d=1blq|NyCkNz>VX+tV+v(TFTZ~_HQtm~0ky>_ zW&uO}AZ2x6f$Zyjt8sCxKnrBdM;k`JyYmcEnn;n`;U0&62_gRX6J^pzbKXml-t7+s zzGAnWf*|jH*zk$}P1OeEr;L!^zO{hx>(^KlMbQws@6jSWp0v`Yt=#pasHD?nUsP<a zYee&ZyCd@^BjiuKCsOUV<qZkrLB^!}F46T)Ra(_BlolhH<(1kz-DXh-TM=n_WaaSf z{Ew~Ru9I~n5m+ims{ddYK@xs6W9?i%SFzqffyV!N3aU$tCwX5ab{98ijk3A!+c(6c z-F+$cHfWbLUh3yPJ?g|qDmZp3Mhw$WvOv-~3sCDSt4ViwxQY?&;QNUWVJkDWI>RPv z=peR`7JV4S#xhW@i7%4L3*fkdT-AHMoTmW7<-V}U+ENML!pw8P9pJ(#E_ut<*pcbf zqf4e}K1weQtf+x`H-+kB3#L#1(ulOdffpegmZXchEh4&K29df>2pDoH%ct|y)6Fv4 z_6`EWABK*sCsMJP@rR7dl@W-iH^hm>zaw_QXgJ2(J?pP?q6q{ecEzvR_?3E+Wg+}5 z!=L%dh?UTx1%$#^oYmm1WjNt1&z^J-U<KDHw%zcdepswj>Rx8YP_bJ{wn*~?f8I5~ zy>^*YW|k%p=EDxlbz8p%hmsE3`W(F-d9c32xcP&Bd+Ceggq4N*@>O)d0fbDQ^DJRl z#&1S(ZU%4ybl5R$Jn4*oyy~^2d~+f0154HwE6j(<a3ue46}!2;?<ki2E<OC0NxDZ+ z=UM{#P;=Qs9TZZ3l&F$`w=@kwiC`@WWG1+(w9MAeBcFJ9@W!!QOwn)nb&Nn4D(2@z z-HAM3<B%nCxcg)4$GEd#VW$7jNyNHu%Iq!m3f4V#{wyG-RSc#j>ks^Jiy&jQxAjXx zVC_b#pAvOR-MmyYWSv||WYI}C^8~^!eFNQ>p`#0<QWC92HKJu$=I24kAP+vktE|k4 zfs%pkVXi+Mmylm>BCq0qc@PtQFYro}YA)7c5-BSo*3nF6%P`*Bo~f#KrW*<v*eLg2 z$Oy=qEX9jH4}eNt?jBe_9QVln{+9zHc-vB;QlmacPmpBB$t!fg`*qwnjL#{s#S!Oh zM6LvCujcVj|1|tvuMKK$%dJ1dt^1%5lZm@#n-FA;5M0Ojg7isOCpG*}0a_!D2~O~Q z-W&``lj#G93c#i~YJBt0w&0=y4EQ_rida0d&j;4ccYyLS=;+j4@g9tGOl?qw`FEM> zy(3=#<J$VpGL--a<1Qq<?TkG>&N<{z^+hfJ^%%Lx11rp}cdd^KzCDEs+y*Oemw#~X z-amHaJ$vO6mfXn`i|-V1#w3FDVaqk&M6Ht)U1LPqo3SKvrYj$tcs)%TQ<31=UBv%t z$~X(Z7fMzfUh-pGG+%#b$;f-nTlfDzVDp%k%g>yOju(kJlsK0dRohcT+zFaI%2(Xg zmgW;y`FQmfA6sic+K}x#iHwFW{-(P3a=EWe1yh|S`>cfvs#bBHDDV9Jca*c-`YT8a zg|Q`1{IglmWQ}SreZCQsB9ax#7x-D_NLA3&E#Lkwzn!c?{jWW^|J&Xr<XMIK(E+1L zFt_+xuf90>>$bufI;aHwN^$ToMfpLBp8rAT#A)N$esbA1)Ho5Vx}8QW&r8$Lf04MA z15@rj$8^A_?(dg%xXNL)p=}$vT<T24kQiKMKwDnS_P$`KC(Vlz#KyRV5lhI;Woyfg zhrFE+FTJvzX&yBt`6otoR7VCfv&xT^|K=e6`d4Qv(b-{1*SA-D!@(~=brqds=$+6i z#sNWX>PeDwo?^j2!0ALTsQUavd8?n8J{k~qUq&L?TRiAHv;;%?e^UDHn5!<uU<fY? z2y>my-90Cz)GtgXV@Y<;XKNqI@qJ96!Sf*nL(XvQqvN;Ngtfy|rb!jkP%i`W3nw)c zIPY(w6(rs80M)f^6}3G~d1Ym#245Kt3OXEkrp{Sw-Sc^p@d4q7b5D+6G~BvnxnTe8 zF8_zhXk{^{z6Tib^ix)y{pSR!tsif~zr^sQ_1qJ~f72ql|D{Fxr-Er=TI7f6-M@O; zCB&W5Z-4CFHTVcgiV&@~d(y0)buuBPMmxh0%Vwb<m%kDUbK`+`LL&=2V6KWJ!FBb> zG(1!D!Ily?IN36IE`KvjQ5O?fh`=VX_3KIZ)GLP_;ddj`+<WY4vjlF$nPgmH!yc(w z@^SA$-r~8=&iA9oj!S1g`^Ps*j}kpu_n%4Tf@#mR000feu|vrl9C;O?d+PxWbJ9mW zjf)1Rr-stD0{R&RzIM{$gn{YpcGbpGd{!}~1k4-6RK`&IOLxb?cc6v)M0bPxng{o# z>s_8P5drTwQqdD+BMf2cQ>2cIuSJAPYX$s~BM*?tznfryh-bDvQ%a3dL&t<n6OgJg zsIiOKYI#ftda-GfB^Q1A&%<dDyO|=XYJBofJcqG+fZ|Vm8zO7GDn~@Qe0lb%R<{>; zIR0&Llq`ZBw9;a%lgkf_jWzby+gEj!*kgEl*O0pI-c0J(7AwTICBECN1A2@(y=Pus zqY^xbKNHBRoOuSCz&QpkW$2QnU6~*F8*<787l0By`sXE?(0AE28DEbWFbH8BwSle$ zttl*a`hDKP*;x7f(2kZ}O$e#}52Sa~D({8e6pSIqf+xDEH<6uD%NTywdkKHui>Jg* zg5y2X#e!7m9!1V(p>AV8Vd_gA)#^V#Jk@?j)#baQQ1sSiNKU8)attkx;g&^G8yI*O za_qJ6iv7t;SA@ddH+Lqlt1!8c)kwAt{D5<J^5c5KGS}}IX&!VcujRpU=o7&TSA@g& z>@)o?|DX6wyZve^z^zAtOj{yHPq51eWvyjaZyKsh)yf9QE5X8X>YzKqDLq(ODsbTM zA#f@@3}c=P`Lo$*s#Xcm<!l*8m;+G4Wl*>&lKUq^Pa-P3b#1x#1hz8V=B=K*uiyMp z{rHmqT@Aw+6y}o<buuOqCAkL&;8=p5FFN5f+SUHMY031jX^EHP2Gyyu9#37A{yA8* z9$Vj8Odnh|fq^%JMnczG%D@A)yhWEgs&F%EZ`ymg_|Chm@04~PaIjkhJLturh9CzQ zK9+x(wz@cBzb?ka2{JiAUzdA@1#Ul1NzDd79AZ@&uIA0?>hRQ?J*T@FYPA|&*<VYT zQivI`e+^Y+$A6Ch-5R3y*BWxJ<w;JbwaBX(I|_4LJiqq*AK+FUSoYlZT)1@U(S!zY z(hddj_AJQr=~{EN@g@9L<UX+0f`zR|`{pY^yUW-|;p>Z4yzW;m4}z)>S=fT}-Jb!S zV}OTan;YfbQQ++kEi!W|=4HNOWe6%YE6q1L7#YY%;E=8_^IzpU(Kf)-Ngna>P}<nA z@M5v;tmx2lPo6^TtTK8A$U*(4dP`08x_d+%yZiC)QWWRR{{2!%@86JYt$T{Mrf>hU z*hda#n;==dgc$iwa0-54NuqOp;Yl{hKt(qa$T=MXmwzJ;@JwjZas-ru@M!PoTyD`$ zJ8aoU(T$mA!VOX6B;0bOBff_oiGJNQGRh@Y357#(Hda0?Xue}IYFlZoc_=UIa({M% zRi12p4}ra!6hmAYkMa!O{8Y0~G)$&R954@_%3+)p(0PyfIDYHcI2>WEc=kg(_^fGX z3qc)^HZn5SOJhBOS-9)2i#|V=Jc~?7N)0LnLEZma+S5%y_TrQBw-Jp+6G)dUXsGX< z=@8iGI6NzmGVI~6$tS&f;NK&4)Za)Ag{-~oXk3ta|0m%&7`egzQ-SD3-%w%%4uqK; zJ#gPWkX50x3jLMad)8EL%o<i9d>I?|q@N4@5Xx`lwx(>zw%*kYZXNQeQgoQCT6N0f zrKZ}|`RMccu#I|oC+H3HY6U-BUfGb3R^%}>m{z1RiTtZpXc%|tISuy^yt;gK_Ga@V z5y3nw_9s_@A*90$rVgKoeVhsBNqC<;PV#R~H-A#mbZC+CC4pc&8=5wfpM1@Pl#41O zh}!b<U;bE@eC_ClsRql8mh|2q7>2(EG>m9=gDK36|Mze-2abhxIqt7s5!3mcz%4VD z=res1F*_?9gbvXljZVpXAgqL71pPF+*TSih{<fp)uHHEiC`x}@{l%Jbp)Byl+Q2uv z_()YI0(#b5;bi6t<f7}d{Yv2Ou{X^LOXiQ;<uG?dsSMc|*s0mqI!l*Y80GrtwpKIy z(N6vbW^tZkY?Q+Mo%M(q53KWCMg*5vfr-AVsnAJ=W+D;u59k&|(ei(Vsd7yUd3fw! zVY5!*X$|0_xo+>ErVS;lTXq8PVkjlqo@j7($YC=|yO&zzUn@mscF3s{bn(^Yr0BNq z{dI=3K9e`WIf$QS83xsAm}G@#^I#fKUY6B5oRP^p(2$LVsf4ly0P|UbFQG$HEKpy0 zS<{M)J-8OI+gK{p=ra6eB=OwJ9u&>49Fn0>Ha!E;veTum2$zS8+kbYCTyri0d(!8i zUT4!DcIbS2a1Q7tUxEBw|LBe!afvjP*FcZyt6gDJI&&*2TUGiHN;=7B$4e(8{yrZE zzIFuBz`_>=@1*|ZM74)Qae-(pnyv4`pgVge`?hTZ65orU#S>4dldVwqjMb%Ddv9se z&2PGYP(0tvqIT?(Z*MC_Su0Gvkoopk04tcfRmw}T?&VZ_{UgAP^pFxJ{HVZmc)^9n zN0`d`v?PUra)aIoPWATmeYCx<IN9QZp6BO-k1D~b?)8@<6|2oK>gnYLB;1I)?d8=^ zsHo@h&h^U2UFF~YJTP0{I_jntz@RXeoR!BRG>Gk;0kGVf0b}boU9<sA$cuTA?h1NG z&d`1XbsSux%p9?|EU=y-aI-hL9+Mb;<K?yRxyAZCeARP8mh;C0OgQ}cV6FF$x5D!O zqqo8t<b8k$@;lzY^E>`<uoE#quyYd<ye}fmCQ67~NKJOJ2lWlY)p#KZ4;N1OusiMv zvp@<9zrP2FQn)-*_;J6|UD<7PVA=(s!B8gX^d*d6t2b&(_q>nvjyq^87yzRm*gfIe zU#&&X-Rd7cn%)Jnuj?Tr`c1tDd>7)W-Yr=|KBf-loW5<*<%gkJ{=!oT+B6MZG*84- z!SA|R_fI%j{sYIc)P8-T)PnxYP)#Z`H$@FVYKNA}b%hp8!6f(l4-`z8rmT1De^#d@ z4{I`X+~BHGtqjbTc{@KP#{7x4pj@3&G@?hVuqCRt59O?QnoLmM4l9A#lK53k`m4lK zsc#)$Q}^I#HCeCKPo<I(NFCqOU`}%EFT2)1JA4J3PhdRc!8yb^{RY_LbogAx{r3`d zsrLfx0`$Jx_0U^^acC*RX1o=!a*sJK?!pk)Y0Z7mSlv((&P`qLHQrNsi}vum<2SRW z0@jqNnL2rP5WM<rlxFh&l&8vwP@i9BX4%f64K}?l?I)a)b?|H@CWrGfv>u@I&*QQ9 z4+~U1uZ1swQj2lsvyRn&iVqwwBr<xvg!in!D0y2*Srs*uzrT(u3_{uOWbP_)&fhBO zL>m+T&Y8I3{dQaF7fCM6np@x=_O<dW49)!s?))#=T16<N@Xbb*8&ecZDpzC+)mJc( zNrd7$8pDU5la)~3C$Q~h{l_CjrgtzqFGyG|;EOS$Dx7(6E*v7xr!lHuREV8)6vnc> z2>9!Ws;p8Pb*c<}2Ncr*P8zgl?zy=YFAG?z#n4{>&O$%=5>_RJU1WD*#%o6479p5; z91V`sRe&E(|1>OL!diL(g}wQYe^8BylJ$kY60!Mv4`CgOvi>i*hrHpvUi&|C4|bb2 zZPocG6#4Q!U$9If%A<0kwm&LAjc;pDWCV--s{nSdpV_2_m_%P#Nv^}k6<IyL0AlE} zSp{@+$+J%})erDl+DOb+c>UHR8ABDkEJ?}tpKJd$7$L$U_C~dX>-YQ*wZU}!=xs~% zZk2`3+gdPtP_HhJbr_Zgc|KxBo_yLY)b!~yOypfeQ(5JdQ7U2u$h#}c>2M@76A#P% zrsNo6Pq<%*EtMnodam6&@3c)3v)_<3&;ND7v;1c+_+G<nh%eLpkjJbZ;O>7)bLKc= z$E$+=`-)Nrv<RedPu#KoDAjMf03|-&Dm40<^lo*J+`}<nK+Rvg6lHe!m%#Dqg29I| zxN-bOFsqO3@ZiBWCv#DQLL64VSXHL)6YeOa7DjXjm{}5e$^(f_c_jYv;*wUGh?g|3 zG&?Foq{rR?ClS6k_lwEr&)i>r^)GmRI7Mn3g^3r+Hpz2Kpnuu~=)ZP(D3j+kS4|~K zGucwnDbx=Y9rxbmJd+ZytdE4Cjy*%v=3M@3sv4S)r55zur*mZrlzTYuIN2rxxKvYU z1V8_dUY92w$j_k~vX&V7KWKXkwz~RtyBjB1AV84dn&3_#xJz&k?oM#GU_pbs23=@y z2=2k%-Q8{B&<W48&)Gd~ci-Li{Q_LqTud4NagXu4Ckw}-w?K3I#)GRpMUKW()FpHS z2}0Zw_{<js<-kR|0`fl$T(xhqPMAw@*ho`K{q_$~8KH(oVgEFO)2aJ>;7&f<k(X-v zQb^eJ^Gi^E;9QQomeTTFZ#mJZpz_IFx6`+k;2b}(O!ciyn>dE|7O4}A2mU2volr<A zie;Sv?<}XJ+BcF%;m4?L_g*hA_FF8DpOJLbh<hG&dn0Z{p1gnd7^pxW!v5J~@O%-p zRJ6x}-|edDBA-LsUB8aZq~B)xJkUJ*Fr=Ke26Xz;LP{G^GuqHbm`+e=H9gj|?u}Rm zDcbLaUd0GIX|}vBk73p~mv_lfP5tyv-|<Z=St%hLYO9i7<k7E^#eW<;n}2(6LH#|X zgOEP}g56?PEDee6_FM`y?*FLB0hJ>2J6w-+tiW%?&);(itSI69wSy!i5J9Bh(KW^u zA$EA14aa#opJ_HclF+R)k?-M*OVqs||8B<aX)Q^xZ$>+rY*WkjN8AHsB0}9}LE%gp znz({0%k)>4lB;GIg#!Eum_-7LBMxmDVMj9!R^3Yp2TS*>&YBy??z+d-AEULppjRYx zYrYV&sSTa%!bKsUe@lJ`6N6NYAXXgLrnDucFfsm4L;4lSU|w7u<wX!+Fyn~Je(?HU ze%RZe4O^&5e9tSt*4w83AQ2!Dznjub=rUt%6=r&UpsmgIkxPpW<&^nVrhz95o=_pD z%tCC8k&SqRhv@oo7=KESI?PTS<LGl?B#1)6SOQT!9rM{jrXVRS)oc@nwPB_DMkNyY zRsPuO{a4HR>bi%4B8FzO2mJCK{!@1sR(xw#pY;&~?@(p^K1)~NgDYkP<ZKmWk?eY_ zkIdXlkk(=_dLfwt)uM_;e6cQAMmK;T#CFGCfCX$-f?&Bk{-}EV8<=}P$8E0oD-8<I z@?pH_O9+JHszlW~P{X|0qE$M8fNH=0bJlK)#ZLEs5=PueuXzfedCQ=FRn_cySU+M= z2)cBS1wuSMu5<m?UZxKNuq~3yq=e5gTkOAMwty>!I6b}=L7RnqKaEv=8korFzYL&z znMQ3`eO4Vk4<Y+4tMscRcM<%L=KQc*0IjoRI~q9D@Gum3(F|X5S9WL|95rvI@qa7d znr<H7^V&~(J{=h7#Mysa)TuS`Hu9T^{#tb4PZ$9>-r`ZIg_T$$ZZb+CyH6j6*N6g? zl#Cqhh%GPWw82FL(|jvcHzBKgdV&Nf{*?&me62v9RB8338h+n|e&97`OW^RIR9G9u zpmh#of`4S_5~7Gb06YO5YXPD~_-vGa^j0Fblc;DjvVo;>et0>5+ZJZH%Bvp&$Qi0E zx2-FqIb}lia*dZ&MBK+Uj<Oo|v46)#J7^Or896KjznzxGDZ3`hCP&_(ff*AYf(IEh zqS`j)1>C5?jLI=-fKs=XtVb(*PHR1l$6WyyL-c}Ls&dGzS=J}=A?Pszed+ic4RQ!E zL{uB`(TFwdAJ=X#s*dA-rRr>&z$+*wjYk`^o~XioHg7EdW^`Q7`Dx*GA1}Bs3?0Sl zP6vyKIWK^A(U7|2f*hSGUlCXhA5`XRqJ76eS-C45p<u?Taf(1=Qq}VvJxNz&ueQYc zFr$=N+GkvHPhYCC`Ouavp_hI33;oCpmFMCjX4v5h)YULx{dKmciImAszU1DIJJkXP zz;f81Q+BaZm_w1tOTU^Pa8fXT$a`tG7vTIa+wC3!Q{#Py7GLX+gJpGWkB1)vFJ0Bg z4}srh68K%_cFk`kx-+1Fcbs%Q=&B0i7jRc60CzPo4L#<2aaewUJkJ(}kDJy<($=1g zuiyA}yy+?Bu9w7PQ!-vagjJ2Ncf(<LJzo-M^Uw3IcyPK(xJ3|LFf`GkHa;AzK<Z~8 z&6})=7?VxUHB3ru6+4Ms^BNY#aCjkj7#5k5ZF^uxxproJZhr5Fq4foX3wokeyckM6 zi$}cxc?b8;Jh-+ED2w(t7DkM5oH3Y}Rew}4+IMHzpP$_4n5;iC+!#hkhMuMS*_o*i zHTJ6(*;p65XQ+Z+w9#!|DLf&#_M(dR`_85s^&KzcEgc?7IeRi`IWj4tdIFR<OEy}< zBIqdp1O5Q9Jaw2tT5+xA=VCZ0{H=amGP`!0!Ce?!EKAueLIUaGahV<9<JUT5OvpqD zu4-@PDr2g&$7M%X&a;4G$ezo!J3A1acfz1!Q7;gMw*#sRB0{naC)+^+&ptdKT{>Bf z#qlZE6@G1{JG0~U;ahly*8@CmLM3VXiG$xV(dtmyPi~QYZM-dk#IfB*A@wUz4z*5n za$vsT5Z5;Z@eZ69$3qc4p<S>O4Ov!LDBn^x)aG(rtFwV(Y|!fck?Uy+yVni@c3(BT zQ~K1>Nb0jhA|tn@cdC3rd?@}qh!4PX`@uH@K-~Ll59em49)e3$t>Xw(3V#~xz31E` z??)BDvl2%hQ+u1sScsSb!6^I%PQib)Cn6_0sGYNPk9<=(Hy5w~LL+-{+q0>D%1~y` zIryr)?I_k|3KF)M8a>H7*j-9{$r|?oQs+uu`!8oPY^k3Hu;$_vhi@mg!3$p9BtA`O zUOC+>!c%r-tB^npCXMDN@1nxpfO7P-tZRX|yI7u~Ui9W=Rlc0lAs`-W)8?4Ii(+O8 zmug?B6)WjV><wZ_?4bQzCt0lFA2d})5IqzGDk9zIXB^=s^78*{umN%+?GRucBm54r zT2M~TY7c_B`Ec~VpQZhpCZq+TjKm3=&;W31>d^`~AW?tw)F^r&XUjMdM&z8R7=m@+ z7PdfKa4z$t;R1oI76YvSqhdj3zK^e`qfQh;bei#_wSv?$hzI=z#PdFP%KG*tRTI(% zOWvWo-MT3se5Q2JJ34gvoSWaKROe__HMJVJ<+fUw9ZOPBoS>T-2M=pTI;$*6-Xv8F z&m56_*LFt(Zo=ktr@lhM-0~URdy9Ps^zTzPD?eaTfRF=SKff$sf8VV4v{u94)@C*j zey-=8$KM&rH(6IH*}sBHK^36kSb<-1jA%F5Oll+FIzUDY%;*n_d@!uGOrVf_V~QcD z5&uiDZ=Xe!Q0}xSH=d8`wWhnSwds&J_>W|w2&8pN2@oIZY5jjQwx;8;VlYZW8|CW> zZ`vD)jJ1uODOB}Oh4fW~Bmp#S8x`C=9I;RV{voY_P6TNb>wT9u7E-%8dfr+x7p}8X zCGA6?`o-waYk*^g@O%WAnH?YlisC9bF7WKePV^n3S$z)DD@IBUN;2t-rt^<G)}l0% zJNz=8<Q1mCk?zj_Bao_5Y(Du%<4f_K{7n~r%dD+s6ADx8H1M&wla1duljR{s{e<fh zu6E=Kvls{FFz$GJ2Q7;hl%9*uw?gS}!=Y1qT!1ODi@D6l1ev04{_UnBDPBzPeW_%S z3DLzDWIs2D7)F)vaqr=_+4!+P4{HYH-@?<sYISL6!PahcKM(ZU&q6(l**D8*_e4EF z8N?0ngeLhv9>_QWCbd{gz16nyRZbx>W?GGlV?a-0PTA_ESX@X|PzQW<zsUw>VG_C9 z=m%b5x7;7!#lh7>gp?^^6Nm^55^Vlw)U;Q)!f`f(H96eQh7sH(4U{5ji)6$=8Mxlv z^}idC`vU`?eY1o)5BlInFIhF}==ssn;6&kg3t;j~0DH(&f?uc&BhHhx{;mQ7w~dL= zvI|+^O&v&~I>&+1sMvrrC6`JeGl%_sTs}Eg{_J+ZEmJj`#fCa#DKw=xVY;z!mj#6j zor2OEp2OqAhQsg<`R8ib|Ci@J{IY8K_s4~Pn)&f);|lI%uR=q!tK1gTP}a)|mUCe@ zSgVN1FcF5;V|EG4{%kF9YjNpU#Nzm5%ndZX_I^_s(Kh7JHiDE6-L>9i8>>cU?m=D~ zA0V%lcKsp!FB5R&UaGe`=k<AhcGo)@Ai>M<=gm8W_EF%-;O>w<l~YV9BG^O19INd< zk_K92sO92#Ce;l#b0TGa^XT$+)uUL=PJG}3H;O;5G8~tg5Pk^uMsbM?8u$Q0(`dur zvHT@aL{MB}f+6$$atzbXmiVNtgKfSz35MN03-Ad@&?P7mhtjQpKj3opXPM)$eR;9* z5)5Dc|5Shc=_v@tJNCSnZE+hO_PVh144QfUCWM%B1jQ4@7wg77SW!g(A=Acxr-&*V zB9#tjsLQUK9loMPwJzujqyU5->V%(s=}qF#2?<4mXdiPe(yqW>r^0ZfoI3ki93xc9 z`6jZ}jD-l>=|5l=Tc9-ota5@6S?(|~7=rpu)oF`g8UA-=WvX8s&5PV^?yMT#j$_@~ z#5ACigdn7?w0=Z%2g1LLn?ALdC$0|`1P3j{5cGrg0?EZe!PlIhR9fJ1XuLAGjS(#7 z(`sNYP5=d)4SSW62HM7iCYOXoRy<Yj&Bvl5Qtm#UbGbCb_R>9{nkV$Idl(3)gHI+< zj1!|cu{VP>g_;foXlkk|n|jSMd2i?c(QRpyfdGzXZxccK5vNt}o|6+(jpL`yf=6c7 zzqe2MZuWSd96Zxi9GJ&EcC3ys^o!6@N{-snVqof~mmUX#C2ea9+_Wz)K4oyp(i|5z z#+|Vky)R!B3bWxkT%9g$A!9cZh}e4W|H(l8bua-sU3B5M8*KQa)KHli<78<@knC4x z<-|6xEP}3z5WCp&!)--(klB)&1&Q9W{qjV7OTn6YQRrw#Cb{b$5^BOZDfwZ<-Hn2L z6Vt<4WU3xy_31azugIoEyMC9Wo7}om{2Yl?0j`tROz=o(rb8F}p1s3E6fQvwcDG=r zLrK~RfG@RB?FzOwx95O(uFeEyKdX0Ubwg3q6cQD&V#aaf4<#Fy``vPX-k9erq>FCr z9L``?F}EJL9u#t+>g|h3<_v{nPCVN)?8rx`=<`yG`M}_gHikWZYFG+5)BmLmXjO!A zdJz6|Frhu1-cQ0XO$aFIwrA9RJKNw0DFu1{{41tastP^)r`j(eV}tp`Cf<gY1<vQ5 zAIntmN=u%5xGUhqStf~k|NYzKN!(^+ixd{WF_CJNXt@BE?}P=Hx-27<{+f+L_1=aN z$W)m{@I5<@a7Vw8XPZv<mg`R*@M@QCTVrIo&l?ZkgLm2WyHn7)NEa6(Uy7-9<GAo@ z($zf`$(o8&dNisWU$v2fqm9aeavrzU^f>*=?C)Y#?NGIPGChCf<Qn+ZGv=NRNh9=a z<e@ldF{((+k3x))@Fmz9+gD1X7k3D+rF?p_BjBW?fWiDfQr6nCT;L?6XXEX8zNx-X z)o)rpC6%lv6!&L`f6L@qH_be4I{pSNvQ3O9C!Ua$I%U1MHbLog<gXUxt$oMm-;j(} zEQbb%3x2xQlzS^Zit2dQ4*(mX06|)42HD{+k0@qQITcCLs>_ocK2_+qn!!!-Vg6at zzi>ZRI_qWzU%~-0|7JJ<jPCxtJe>K}4}HA@WtuUG_*p_?OUg3+0rFq)DWKx#*2cvt zQ-7C?%)7#n_B-az;9vs*hc~S;Qa%z;5Q-31F2%0XQ%4IkHSYFzUsOvo$l~#-0Bk@M zvhr7T*=xG58}UVsU(nP4)<E^()(T`#G1^oRkL|JTDh_fW3(=h)yCF65sZO?y@P3B1 zsS*=9EIVQZ_vNFjG8NrqE8)y-cL53YLy_XO5W1i8GINz+&lfuymX+<6i__)G3ski_ z3)KJLfJLZxB`tA^7;fq8?m!{#4H7dFAc1^F@S}C!^6$LF`3Ru1HsY8?ogiFBLmA~J z{C)Nye=|nCZgnXNOqJ)j@uZmQ4q;09i4;uvR+O_#u#{)zU!?hH>{WiPE>JzUYq9xW zF`|dj7l2^vHr^cpH8YU1)v?H1eIpr`pN{pruq~*nu;AMW)Hi2hO2j$xt3Ygy%Vh>L z?Jut$o65vbTGt<#E3fOA1mn>?ms8H4Cs9C0*yn$8vbU+@p>sB+{w9|rC2KMFmMx|n zPQ9piqd71Yfi_SP)eM3q5oRbKxqzFj=AHNkC$$Dl+=E&Y1$8oTVD#L(bnfhf_g(Hl za(QVUX;=lBIKx<R+4JVYQARle-IE3rdJkKlVNL!3(qKTe57Z@Khd<mG5l+MC%6hBD zJRWJoC?fa<TKBFoIh4K)4XNO_VkY;%!zF@^Xe#trrKgW{aGS)dX%)i3;GvO49<unx z`bP1xHav=d9l_a;(`#D0?!eWP$7<Zi7=>SPA_}VXujsQaP2J_+(dV_CX+p!}O%dw8 zER%M0Dj*${q+rVdK+@oj!bN7;<i(>5YX@=P!bczAwIYYUg$AxxLTn7NteqpE64$Rc z`Q7<KT;zL^3|`N@Om`qsm~rMpwCUJ~8ji^b*k28kZWB{(c?Ssp6^j=7prbwSuD)L? zt_OGF-zgB`iN7xQ8UIdHL^l_TBOfj1+;mO#bd}%aoCo?1uck(6NP9119Oaa#BqTK* z8B9o^qq6;FYCAWY;KnEOPr;gt1h4I1(wQrswB?X8siBLO&wlk@?7!((t6o)&knSj; z;l@*rE5vc+v;Jo?Y=gKmSJ!6kfL(bLZU}<$ke?@!7#Pk-ldzDQO}^u%mFj%2z#UJK zUV8djDd*%=xP1hGKs_fiS;|3il>S$G|J<dRnzHR*+y5WQJ~|u^j_(=Hxaq=Qxf1M{ zZ7*E4{tD?9Ir|-%Zi&C{QQqWcGsrDW*65V55NJcxuDZ^mJZ0^j1?4@X#Q6OK0|@We z;LL3)0>mHhm=GvVScR)|L6kZ(YC08Y5~(h>P@RvP8gSi*OD>aU*v;1KQyNDZi&y-- zi_b3#fbH}9|CH_1Rg(_BZ6JD@*t`O0rl7^@f(Wghx7jh_K#AOt)G@%DlC6?I&v!5x z>*1zZl@1n0W~f~$Dx)x2?*n%q5@Wy)%qf-Mhk^P8efnsNl+8W92s?`|@k&R;p3jo6 z(fwcze6D~O#B(j>*nPw^as<^4-Hx`u>V=ZD9DgXUfmD=X2id#Nc_hvHT`;~*NXo9$ zZ4QX`s#$ianE5IxoZ}V{A*n~`5k?9VPHmw1j;bLDIT1HV0z=fYBeTHpT(vMV|0m3# z?~9@@(V%YieYJne*Xh5cHWVAC3~cM>{+x-}*6o(*SRR=@t*aglT~*OCP*QFvZR&|g zy0tfd5wXXiQVt5?_Vn&qfgz@bkGR!Ro(lkrY+b}ikaGRf37nC@<>6G^H4O@&E2Vve z5Ca@Sn#v=yY^?<=wR5uXc*S;AHQ^dqI&AZ<#M-hm=cHq{2IBDQHAy+gX2ZOh4e4Mc zq{eit_7A0}u5)*?k3gL05r7SBH2d6*@9F(V3&#<nvN(x2pAurFUVwu9VbmdqS{~9< zIhCfcP_P*t!N`j)1s0KAnS;Yt7Tr}YE`_%g9dtxJkq(BJ<vHPUw|+KpiM-W;r<hlk zru&NkQu**SQRqvkWpA9K=Re~~AC}~3q7!=H-B8yP^fdJJY46_y(W!nC1}J($*QTSR z?xCB$#iwyGyKOV~0L%xbObowGQ+5~;yR|>+{;kDg`k99#5|W*3e`Y-aUX~`%(vKpT zE)_c1$h9FIP|C+8`k=xGB=L$FrP#^aF>gn!KutH94TO6D*7BHmqQQ${ZSi>+qT^m> zlrq`ZzIWyS-^0WJmdic8lv~eB4$tD1MR>1)iQz^+;~(SidpNt5l==Y<RlZDcOdi^U zJJ5b&G41R(X_r;Ui|+l>S3KoMe*sNW*!bS{SiVa423SPx$kO0{;^C;!zVLAJV2L1y zCfR-a0ypwHypGIDoA%2q8Jd70QuM~3wPJ+<8H`C1Tf)e%RRv8P=Kx|jsvR0%;UarK zed>7opnOXV89Y57N3hO>+Fzo!--+?JI{1CuR#cPG22^N1YEDQ+vQeS8lZT#s+w<v8 zpKUzyQJmT~ixI6#$R3HR;BPYyxG34RbD!Jbw)z4Gj9~!Mvbb&yLZVg8I|-~={J+9E zj6IrpgA|hacH}4S(Y_!`|M7CIpLD1<&qj|lM@*SjIReP)T`MvkdPk}yBe97)w>zAb z+)buR3!{y4x=mL1^!oTLd>hdFtggtbFXym`=o|XET8DuYf@(QeoF%PgDLW6mR+hfx zJ3sKo%=5HTZksW*++<FBuK+}@n}v_*;9h&amaA|fXUU$A9T7OGuatU=;LA)T*Z~7d z1jB`Xo(ZmXO)3NPx%dC-2h8eU%vXS{B!#TJAcfvLqL`=A1S<JEXalWVH!8`u{SKK5 zT69`2?=tSivQ~YztZPbZ#@1h=HiZ8oYO`7o1WdA5wpc3aJm2DpI}vsPXXfXi$$#xQ zfoEF(k-^{)^|kTb^3{}eKRa+!kEuw;L?8+58R3_u0T7isvUq0M;zU{y-$+<n@3%oa zsImfl7)=B)4&0A0*51_o=Y4s-#|LxJ-s$oG5t@PM|DkfrIjt*>3*Z@;OG<N|o5po7 zWp~f&IKr1H**W9Ch|L`O>3XKuSbwSO+5|fSK%|dADEn-vCjD=V-9H_CvcK$o64%1I z#a;4V=SO#*T+C|#QSolh0J`#TM8z5oLVK;8tc8{G*s)_uxtsX=KR7VvG~$cu*a1t! zY>vbigW6NpbVRs_lo2vMz>&n&urzW`;}d@@lp+#NnkrFxit(@XFEkeYMg>6q``pLq zr4Q?2l>G0_RGyU=5NH*rwIz2eHun99g2`tYFF<LM6P>tFr^#s(3nkwRLgz@kwx5+k zr(h;<f(sOm0rGFV4*wj~ZFT_$Lh#?0JJwH4k$@}x@R+!y_BW<w5Wutmo)g~C3b0x) zb}~o&6{qm`=<_P;$Aced->5lv!V^+{euuVkA}HXsVN}7xf|>bU%|_IZ-*eS>g!#?k zyAbi~bVM)DA2h)gE4x1gyl0MSHsoh|X#&ZGDheFO6tR6q^K%3j)>2X>#l6777L#HD z!0fu#V%=Yn!DD%8=dr3n^&~~3#;R02OcV3}_8!jkL=)aNbN>33%N9C9?~ssnWipzL zT0v_^?I}z(UT2E*bp&G*@@<$rRu6NqHtRco8KW|69#~y^CSA0Gx9HlE3V?NvQ^A;c z#vDiI+s7-PDX5LoH52Vrw3qS2{i=hk^gkyxfV1qC)Xc{HV&xuS{F9aYdF&awkHm8r zDx^WvqV&ss?$@ab08K)4BG(jHQ+wZKTo;ZR5tmf;W|cDC7)KVEm9ALtH8rNYh#qfJ z5ZMG|7+MdIU?f^W{lB$rC1i_$$MULbA*j$U<d0xe+RVG380P?WDW<^K9G@2p{v!3| z2usIPl1lYSW@!8fAVnEFr#m^w_BTK}v7ZMqm4n{GE&2RNO6xEC3r>wYNgo$u@^NnK zPQ3MNF7x*({y!|iwC{j4F3^PFv+pPZ3|RO@V2Tl01NZ}leE^&Z9dz~q>Xj{7wGzsK zh6+|MBuWlH@fR0q!7fI^`-&F(b$GAyR(J5i*^lRf!^$qh=TOgj3KagjL)k2JMgWnH zKJ>45GsFR)#mzAP)XcXLjeSjxysi?zpS$3jqDpc0pD(J$Mx5S<0wB)ijCf~v3$J=l zw~67GiAF@S=ey2*1TK+*dG_zFtz#T9#T6i)`|C>huiW=F3?A!&G7WNW>RhsT^pMe8 zHP+k3bs=FRb8RvE3oc4bQ-$)s$KWDBf~W<hr^gc#EL15AHg?rg``JLEGXKEqjd6B< zjQY4gV}hy~)rh;?dsP~oJ+tM9O@J#%C<#lqVN8sIHz)H<Sz{K~+|>9d<t6!-@~R%e z;0GA+azlau_mJ<ZN~Z<*wgPeEBGU^dTXOd~E3OTN$Rl$!pzD*>4B)dpb|vnXT!z>Z zmcczu5f0OYJ?_-jxs6j#CttsP06&k=zkUjHcYXM$_IIoHD?SNI3(lDp$rY2~=mSm2 z%UzXOCwey3|1pPihXm|>k?Py3*n|LF4CVZdj8?)ZuDL~UPs8Kd)^y`JX`N<eL!wG} zTiSK)=tG@<1DkmFL0s}!4*dRH*@^5V<-MaQ6M8^%Z2Rnb0dO5th5*5|<)PKu`(_KU zF4xLIQ^Rjk4!0^kZ+oeo+v9*=q9=Ool<xn;ETxHmW|qp1`$j-)o8t*=3fTcnNKZ82 z>MV{t4hH<3A1(qJmTT-4H{L-t1LGI)o9y?bJ7*A&x*_j$KWSv)q&DpEHC|XE$e)5V zF5XOeekF7!eGDkgKbW~2TTEHLvDkjPvoDR)5hC<Bn56($tv-*h8z#2wB{u#`idq?5 zCChWy4&YC2D=m(eAL{U)W^q*?2bY_#-Iv@RKRY=l5cNo?UsP#n%o7YXzsb#;_?T7s zx@Ouxsar9sTXFE%jD&Y5EA6n_R0Uyr>S}e-ql2&Yr}ZtOKsIJr`3Jg0afi#Ej&Vaf z3f4{GUuAUwOfiu}gZI>)ZvlCO@j&+Z-f8#$-^#M*hE#sw9(peskj&BcR%1~VL2oQj zv<?mY%T@30%O+4Bk??d>Trp>(>Un_4l^F;}GRBFre+XJjw({k6hP#+_B1*fy6}oP( zzK=g$Y;jt$074Y9-WNqJx1i~ahq#7g&kNiecK^o98nT;3;_cN%2psUvbDpyyC*1y~ zs_v?(*BYRNORqu^67&QoKC60!>2a_BKzM*8aN*f!ozRj<yl*4Vt5JDw7N(C~*9J-# z9zwS@&z+atPmJDM3HolF#0pj1Ek1Sf=)-TPW2b$1(j6}%+&!2*&mw?;7qYy^LpHIK z?aG!ghYb|wZ0I+g6$+@7bo1UusPCSwHa-*cIq`!`o{b?Rlbl4WWtJz+%ZmO4N9Q#k z&NJ_H^x(6+@N@(W?`y~VA5-u228Z=#GUR5XqOUm(eVi<+*A-;{0Vk!5dx7WvW-Ey3 zn^YN=<KF@V@EvjFdDU<53W(1mX`wpr?k@QiTfZ*3@W=;K=H95FFiE1%xKz{;Zqh7K z8=Bv4R2ja9kOX5%VT!|AP~^lX#}@wRV^l^}gtn^UZG8QA^sxlZHI6&=)tvpp5YHLC z|HmwM9$-FE>e?Mp`qWY6eeBEitNHHqj?i6&*XCxC_5j(d2fo6@^RVr#VQAC%<oNo{ zsSVesdH{*34eW`z{(u&d<9-y-;&ENDy%}@|Amp12hUr>dhNhoJ;Ti7st(Tt&KnER; zuU1VDzgA!up;#5l8FCl8bAUzQ0ls7obrjL{S-!2YE+;2j+68X4r3V&_S7{)?)desQ zI8ueXZU}MG>rvm?m&e2B_oEke@q3}N<CTV^O|dy_^VI*cV7VLI-1k-0_!0uhG~Cq3 zNGIvf1~vj3grO3pl6)csq(hS>NXu#UfgXhN8Tr(%s~#x<^t!Yl&^G7pUBVceNX)NV zktx565NE2_q0zP-+j=G;dmld}Jnr+=-%dZ>=T$N^Y)8)%dF~W!XShD5ocT1gYWYU2 z1@jz26`5xVo{wWje@mD*CFD6!i{HJZH+200bGg&hSd(#DuV}n&W_0|P4cHbl2<Thg z52p?rtD#kagkD?sDTKV2{k8YbS{m;B^c5&35bX6uR({gXV=_ly7nf~PtTjs55k#(9 zeb-p9PlTbH3#xh7i6ey{A8VGb8zNrYDj!*^@h&_Lu`&8rKqCNqWI#5`2;U2Sx<|n) z-|*+Gs}}KbI9qu<SrfWkrE6RRtcCUK659D1RdP}KzGG@m!?xi%I6WgEWZqZR9Q{l( z+;|}wf+tnnJf6u=10DZ@3{{2dJL@_Zp-F!gyhrY4;<2U2?<78Gsl<i`@E}kS^Tx!+ zcvhIKoWI1vP>GYUv8@jJo!Q-*b%8`RTf!^Gg_V(2Q7eHneD&=^x8jxaK-0rESu39R zVF~vA1FDBnx5?N0^L@g`n>J8RT-WZGr>`;#dSP<NyU!;f5`aPSKg3+@Ng{Pz9pc!{ z^9O#vMf^qUcv+SgM;qH6O6vsq1&0}3S7muz5|T9T?EIRAL0rSVZ4mY8v8`e0;>P;^ z{{1hvLwAOoA;ls0-IPy*2ZZ0po;0THI~z@l-PJ$r0o*Zs!LL2xR7?vi-=MF3d`MI= za9~~-J(=i*9>CN@+xTVGPDT`Tf41+1(t%zf<ujsZIo5$hSLgYBME`s9Kh8(=#h}TE z_GXZ0@6q7#ZS}d=bsF~L!LI5(-_!1QR056*>}jIr%@f54?uW*EFv&3)uU?m0PdFqx zP(}bm7?S2pc2u9!_uwwSZtd=Jiq+3<TTtfb7}&C)nAHp%D2xcpzR6eK7jf=hr+i*o zUqh+IXuRH5tJXELR8?Q3a9Q-PSf7riILarXQII3kL_bb(=NSKGYebJjjbgT2pGy5= zf8I(I29W0S`_f1ulrPLLok37FhjZ6HiU2OtB7%`CuQNXwvKdGW<wQ@tvbgp+Eq!5# zAh-eu5g_c~A^Mv3e?ou*su93Wsl^P2j%E5wc7X<VZQH(P3>mJIVLq=-_$#7l8kCI# zG$)8V%WB1Hq**3@ePH9p-1x5yvYOUQExNZ$gJh3=&x4`w5{7RtlaNB26I6e3bMD;p zaY7t>-`ABcK6IJ@t;D9guiopRUk}|x^#}fns2=lQ>pUH<Ek3(U?g!nANFKBqDY-Gl zX|4N*N9io)O3M4G60)Tu(>haO150N<sLo7O)OG@$++0(xD=&cd+uzjq6&UV`Ww^<J zG434tn<`=XmQJB`yM$hXD4*3+CzV2Ji+b6<X4ZHGtZ!9EerQeyCvy=!!*HJrz+C#| z7PLLVQw4B)&S}PyrgNrnP)+fO#PyMJXLj^NJ=v1!ou7Ebn0V!Q!m#lyH&QXu4HE{@ zXyO9T%UAcuoGuTvZ$8|E7x79x_HUj}oEaVhDoZ&J`)fV+Lfjb|*2^1+Ji)Wr8;YJQ ze#)sx_p8}zr62<LWj|PzN#SZ>g!FDZ&#~VJAY_mQ4%~>rGm|{;B-(u78{)mI4AQk} zSJB=S3yCr~9|dJR0b922s~MIq6p88%N}D$uRZ|<OuI}7ye|vmx5#9<A+$_R|v2^Mi z2^QKSYMl@g?a5Rd?DrXBmq)Ch$R+$mx3L*)I=B%5wc4#(@B72NfmW&T`$%k(icli; zO*l>-MrtM~E(Jk{=&H&^1E2_L$s8;UMYiTyzAV~M{x=JdM`kTfJEMHmmsPaDxA82o zUhslc%lCjkK|D=r?Nc05va$S21Rmo3&{vFIx7|<(rLw8sXKJ^XuV9ZZ=7AoXNy00m z9T+3l)Lw)u)>hC`sPm!ctOVZj`P8}BnrA}lbrRmwM&I36p8G0~RrMuFp4E}3-ElC= zyz9WYA)tu)|6YcqKN16&FfWYs%iZGl+;cx}sjwp()7O`T4L%qqadncPH3LHCulPyX ziMy*ZCWdDo_tRiYY>i6z)bm<}z=WlTc)o^RoyRt1yZ7@gxAEt94DNeYxmGO8AEUnt zw~G|EYZ>|DhA7+M^v%*H(kdPt`y=haclq5481$AVOYJJ1u6>AiMrnf~D4FuZY|5bl z8NT7R3&<Q9Z$AJ0QOnsG$SF9C;RReJca=e;Fu$<ql_>tb6n+WF^D&^M@pdHJ+h#N~ z<8}lf05^TB1c#Iq5?;h~vHJ?7`i+U=#O)uYv#bumEvlaQ(t%8LyvDX791@1S9Jfur zay(>lT`jp>f4|G<%&i0)Cy`jwTe=g})-q-;>s5gsS^+BASsf5p#sBee_`8WTxWafW z3}ABGI}o$5^u9SZD`DjHwHZ?3R)}llz1TPhwheWPF*Kepdz>Y=JS_P=&87^;q|Ekl zYPf=RoZrtsn)^Q&f(V-Y(^Ux1hNcM<W!yTJB&}UsFA{^(U&fq_|K6B0<Dx19cpKk_ z^2yN3S=9eduW8t{bZOlB+};KEbW@sk^--qb<mtMj$jg0w3d!14_{g6MWU@)7utfPt z^ZppUaXyIWD9{<Yr2}X$XO~B4=?>PV6|#x^okO>mxZ)|J)wfAIsSGTt@RJXp0-nq* zV6VsB9FOe%tRw2<5v=q5JYUlhpiG$S+F~_-jHKlpGNc?gqlFYjocy4>KMS3%TYjU6 z<2_F8@!q19?+#I!BNvyQjm#63rf*0`mTc~|fu*N83VF-aH-4EnmL6}U7)P%r+8ORf zojYyRzuhM%-&am-AOj7zZJmXIZL|Kf#70mfbXi)fB}=UV&QD955U95u+<GJ<t-eh2 zVaz3M1B{KXos1^YP+p=#)fUfSDf?b~h-dF@2)2D#7fddXY{2EyYwXGj$(U%B7m&f@ z=kx-X!XIGkdV!kQ^?cT#A>@&DKTYGKm>z$<+>fLVGfLK^&xt<2A2Dqe$2%rVG$}!} zxkgKEO70f6F*+2!$(p(O_qLY$FZLW~=4qqqy#}m@!vKC&RA$)gc%J_H^h%3#ImK(Z zd7jVn(ldc*k3P-ww$8dwhvm=@JvsFN?w&U!EllO>+1GDq=RqVqhq3Oi6w0{vMpoH+ zFY8>m>vOVM%j{6Lhr$8aZr!!Ow<ui)2_Cwhht8(J&{+BVBdU+&2nus;o6`z)pMw`X zjyD(<E?9^f*4U04cN&&Sw=J)FAEd&kt^GnM9X>wpDTPxMjpN%Dro*5j#a@ZsLVG;* zy~8m}?se1=5)Nf{8)!wl?S*K~r_{g1Q!vXrGpfvYBQK33y<219p=<eKglAplJtzX1 z^t=bK{{M6j;K^G6o!2B7GBnWXpGs>{s3fzX6!pBfrQKjoMpt?i+8-buQPWcAF(?%6 zeT2;>l_I1JyI8h6&2jr4-d*4<8!*niSU0QXuG8qg4(u&#-XGlVwGVm>jL>{M=Jk9a z(y=XFa0Hjzm_WPwZ4k5tlGT4X-Ee|_`nnYv=S7AISi~AhwyTUOf+#FNO^>ar8_rn1 zzde)|{JV<@-GAK;Sh^VT3&ciMBoT#X_v-rqSxS4Dw|<HavQRt3pIZ&itWB93yYXoI z(#GRMo%&6khtWyC+h3s@<}h`bnRh78@LE4~v4cWJAJT#@=qf;A?@YdT>Og;+aKg1x zlF%KmxKaOdwM_?*d;UIiwCCzbV6q1Sg~^Wr%M}$ru0~t7vMk|yAkFVnzoO~Dh5gEu z+NXKxWd3^YWLoK1<#xYhnz{c4`d5&1Bm2B6*8){M^d~2+v~dyss0av$If+^5L8!-v zMv2Oue#D$n$S!FSS7umV%6}E@Pv{R4yCH;WjoTGTKbu0QVdwOU8))m6mF=P5YlAwC z63y~<EAfH3nVuKB*wI{gCtR>xu3hk^G5W)cX|x46K+>_%YpZ=hgi8gohD9n>w_-Wm zfCb&3s4YD{A;5}Cik*w|@afB-9ouNzJmJHPh;#GmfyHARJbm1qC$K69D7^P&|B6Tm z)Hyz&FPqjt31X@R4Vn+h75jF`4SlD!OUP%>DrWbP59Lxrj^ab~&tM#pP0Nmi=iF>O zST$>hi`L=dehU~48sYTwd|mm$(12Dn$ozM-A`ldnLyaT&fakiBFfH!=D$fi3Q!Zjw zcMY-Ux!Jw33)2LN0%sdeGfV!8j~io%P2qy6tGP{kM-|*r>SC?n#y$zdQXR>AljieS z&*i(CbMJfS*N^wzr5?juR@h6oJ!ui#23$Fw??OoU?w#=dAh*r%iON*9F_1s=B!WX- zJ3FkbIV<42n=BkCq)3rM)4~40>KNUxu1+H;q*km<8*SdG+@}kdS3Gq$KLTZ1u7{rP z#;2b)0vTesQX3r-c7(3(E$UjeTRzS0DueKnylJHgSup#d;wnjWXhG&=i=&I<Yhi;c zcgjv{(zFIDob@k&pq#ibe;sYtvCQ}XN*752&BNwnfv3=?>vXJbgN`PXhIEzY<NL9Z zk;kShom(33`^&2g=evYbKH6II1iTu)Q_CwU#+&X2(^Wa<8Nnqt+4h25P}2b=k1W?( zGyFer_ynig4)U;4UtmJ3fzRtxM$<>g%Ix1m-=$l>4X!QDgk$5E+Tt_VHloi<;J-i3 zU<hBI88XJS?!BUTj7DWu7yYJ2UVfY+a8Lx<eSqV~$`Wl{b$rJ@7X^bJ9SH&X$Ls9g zda{7`>ag%v{o>US0i5kI%W&E_Xh>yp(K<~-ODk7|BKZ5;SzbIdEt&r57MJdax~Bjg zgKi93!LtLTmNpW@jqj5u7r!nhHsu0rgEp{GjgGV{p!YpJ5BFe+cufAx%cQv}nS6w^ z9CXhHm!^#y3r+d9vO8|K!SvQz;BrtbiL6)ctTbYD2D+gO33duTLfBE)<&z)4A}r2; z3cn1;=Bu)yBjwS1+u*Y)7DT_v>l({7%~kWy)bA;n?~z9A)VCNb&hIYlxObAVfR8sS zR)hNlL!Q!S#&qw?*9iOlDs9=VZ(^Cp0#4Le=C|{uEViXi<E2?6pReLK)Mif5W+mlz z&~R)I68v%0;nLGu^R_lCL+mV@rAX~qt%>GgXcVQrx@re4F}_=BvEjF;P_mn^0~h?c zu?)^!<x@rK48t<Yj!~*)b~{VJ*5l-LQNEo0@in)2fjVY-ZohQwI<{6}!3MqU%21;D zK(lf+ZPZ#k%v`0GNlc89IH5N&^Z3VJq&$DdaT`(Gmd6NYls56E8E$(xSq8q9yq@%p zq<DiUYJr<Pw_at6!s=TU40}Qg+N^FIc?^zlF6boZVdBAb<tdi*KSHD{Q~MxC3D?t! z*`FW!>w8;X>~7r$Yn75Xc|B*TqNqaQo!CKN89YP1C}b4hVSTB<KjXOwicBJ_f#KXT zQpMEEp(E#G_j))HHi;1$+Ml2*kJ`4H4yzlPBa7q5`|^t&L~&BONR0dRn%msFuYmt= zKpHA|o#)L!!F=O3r_?W`b2VND5(03#%a!3?Jr7dt!J>cN?>n6dq=GTFjs%|%)bRdk z?qzrLKaPGNA>oIq8ynM@+A57{QLL&6bH!T4TXxhduZY5FsW!{;{}49|wNqz$tz*{> ztd2DCuGSq_bY^h7k2XvBn%pq>OX}?2tARP#8QWq8oto%f*x)1ox*wzl?M*<y9erBO zjn1+9=}qW%qqpEP-k*y`W5a|8PRp&;b%sQrks&ijQvWc)*gmsM%?XN)g@9!yk*Kh^ z_>Ia2!MG~^4yK&J?@>y2N)l=waq_E)Nw2f%dJ)npzKoc0(WXWkGG!XH?<$xE<RxOT zXe1DwYsBeHkA6e)fAg*pcBM~UJLO9IV^h;Mlrw(3QyP-o1Z917x3-<BO*Q+t?zJJU z#mOxbPj~o!SGca4+rz@IABmPHo#PBY$zs3>ysww9*A)-a2sk@y#n<ZI)9U_d?|YwK zXVM!!PNe01w_r6xX|Ualt=l<nRJm!!29?#DTGKf06E{H}%S6*2r_`6o!)OTXYWf`| z@NtU6TzEnwZ5#?6_N_C|gWz$p);4$j5e;7wPzI*$JgWVDc@m0lR^s28M5ih(8RsXo z&~=6ab;Z1dAfg_6*_`)l{)e;3)A}|IpW`a+K&>~xy;EzxByRAkzHv;|Xh9OmtGbpI zrA7K|FJdTseSbJ1^2f`<%yjnc?1F}5fPrsk#*cB^55_ioh&Xi2{dt`~?}qHuIS&%U zx(q4rE4*)}Jg=F2|6p%SRLUhpR$_T|V~Q}Vs7DVUAu5kSp>#*G<B2Nt%2Vi_DvJgf z7=H?@A%E}CXDk3ZEVB`e?(z>u0DaDUo2)N0V5#LbV6E(>B?e88CK}YBZ9gZHl3wq0 z8_!qgaU@_2E8^<i1{F#y`Z_34)&=%t6d6t3S0pPD&#A0V|8+sJzH~HfPEaG}Z?yZ3 zd|JEY9*IR|a>mWVJl&KC&Gn7FnIkv@D&s%Jjg6_CZXLafytZuv>()9>BFpg-Mb4{> zXyjSqVjaCMZeAVdP~n_xU{o019nmP9juqU9;ydu*XI{Id;x|{t$(3HiV4mT&@Sb%I zD|UXIk5f?1^PE=DC@#bWGZvWz{SxXz2RqIN(i;V+>M{-X%jb(U&Q43Uw@vHr%TBjd z4BG5he8<|3=fG_Mhn=#zmW1@xd?6>A%Qlqhu-flc5u))P_;}&zyi3-B_cs<^Da5BG zSJ7h&F=wJrPzE)Wpve|Z>YcKn%rM4rn!%sFP!&J%p(#5kaUFiY&xIt)g#uYFj2@5% zAu{7@Bgf1Zhju30$pj4PV;FORO6;6>d0?}`v|S|LS5YIJqePbllZGelR;gsMhQ$Ey zl^wTJa&n#&gi6vcy_k5iAM!%=y^7ApwbkX{iM}%3Oa&)53(I^~Bmaic9uE2GhQ^n9 z3Mx&Ax@eQ*rv%T1!XJ@AObyCPT4IMbXTsF9Z+i9Ne>SUPP<MCQCuR-)$lUF?vXskA z2Va%6&GbiG$r^lsYa&cu-0x{!Juli8c@-B(Jz<pD|0M;yRCi+Q;Nfkz*=W#xMZjHQ zF+&$>0zO`v8Zs-Os-_6Wrl_tK5Dw5TfQ?gU9u;5cL8+~L_eP8z)KucQKfcFNvQcGl zr1P+Z;29Xc_vfAFaSG$ZdBVWM>9Fcz^NQ`lN(B#M`#4_ee!gCy6m%my?w<p<o4R_Z z?Z>->L+%EP^<~W#9E}Kq$yozqVndCH2P~E9K}bx70;}?gRq}I|Pqm1(5T$f$Mt=<U zsAEcMv^3BcR?D=qH6+u>oJdYmM4*^@$_hqG&vB^L!|kc7=uy9-a@Bj?T{MF^tk#S- zvf4vqV7b*dc#IFqqRViovfG(h2#~Vr`Cgt4x_|j>sKt)|0ly)mRrq-hAxYdHi%1H) zq5|RPFY_8F09p%b2@Nh=SG=siwqi4t3f;Il`~1{I%7K1Ie;{Laee9lqw_-cZ2{j;w zNIS5g5{bzD9ha}(7iez1*!0iMbq@i?uVre%VUgYNfjg|5TDwDmx8-7fU&S3pf{yfR zX_M-H<#JlNd(J_&!0NEW-P{`1o5QMMJN)(aoXJX6X>#E#!3Cv_n51jZCW-X0mp?3D z9jn=nP7KCO1MXU9C5}bcuitLxg#?tp*H(%cUp?rmK*wKqoTmW-a8pOK3BsMV(deK` zNUXl3`@*>@$vg#`21${GP*4<74nZR9vP|@qK76DG2C>CjT3c$D9lf9n$g5LW+$-&i zt+0oQrqpTecf+KpRF#yE<kvahUc>sY2k+|DD|P7$@BEUB?h^E2X5y#JQxny4U_)ll zl^Pattu!wQLu@cm|Di-72(rX-m?K6CH8e%@z%uy6nocjB7(#`}=dfX`udjTg?Vp~o zh1%ib-{C=Q&M3oW!%4Ru2hA@)9x5WQJ0KTIBD0ek^0xo<?HZKtTLOZpRfUg?q#O~o z?BsrIrPksy?lZ5D7o{FrlhmekAFsUX?-!1KMEqK+_L{c;v>5JAzuO4x6R49@-;6?D z?=^mkxdVZZ+sqHu#?L+{4116FDptOa)+#ms1lsK9&r*2(itFm<X(|?0RUYvKGmC~m zXm<1wH9{@vT>RX|9GlenC-fgBl$luX!XZN}VSZ3@9=bPko&Fw0-K&RhWlCBaFC%NZ z8wi8CD?(qd(xPz+YsR)my|w9Yd50v8dfYCQL?(8H&w_=qwSE5}{kinO-Lh+aSQl0u z25x#JhOcfJ$?uJr0*tz{&GdREGL~rT-iPVorH}?fBnU#zcN!6n&>jb@?;GwCUH!sV zX`rS2$_I|!CK150iQzTv1J1c;D#gm%3YksTDyJbfALNd&&&C9JkzbFMB!AYcBo#Z- zHtAW?Z_S*G%35_w1+P9DMNDpuuW|30p#-;AW^cou(LIQBQ)nrngDokk6();KBqxgA zown0>b8v(w7bx|<8rR=dU{$4Umt)$9wLbhqZcxr<JUAYr9C$PLMt9LKASIpI9+f`D zdF1447{9)rvgL}Yc9`9+DFhcHl@uc>r(>H$J86`y3kAKfDF5#svId((iI6%4b`d5# z-11ax8^~=6Er0R|Zmh+S8<s&|-p!R&t<wF}vrHC~$Ns~9rYc`5Cmdq|QuC?lipql# z<;;T14nX#h$aOvte-bOc@e4nzxtaHN*zOzF;l5e~UA0YT5@^jdLwehI%86Z-r6lMc zwM{MFkyz8JU4UudzwU5J^h=#8mbIAXG1c`{ACDRlvqRUf3YpmGM*7*v$GYWnN~N<I zFS&<n_6G^CZ5CJqL-S^>PkN*3_uT-iYKn3*@b+>CAAi+k)*FN4PWy5)(_A9U6Va%o zU{RNHoj!(jl1NPjCD$FHFJ;+EK0iK?SO1x5es#unyxc7GvxPLo5kl19J|?5UhHddm zB-0On+@dc<XoMpCN_M*Rbu@La1_gOd_`cb0Gi_*u+b1);H6{m58kY*YZ0ARRrSaFm zlnouI$%56jx5;|T;J02>{{7wU8+Mga$We1EnUsDK+^B04cfvFXXNV6fh7b-_Z(ofv z$6lPUu=an$FVwgxzH4pJHLieEN$cu^JLYaWR>U5<*}D8Jy3_)Wcb(t1nho@MC!udJ zt+C3}-Y(Z&WGKsfPIJ(6(t4IQV_`XS*w5#5bnp>a^zwyE&fv_vk|`<ae8fQpPn!>4 zFNV{~)We1&`kr-tzg65cr8J;wm49}WWh_xZwn@Yum7M(=ih2!I_>h2!>k8tE_fb<n zSa1H#V&IPhFF;ZFz<IlOL|4|c>j&`Mzw5&MlGwiJe=w2L4c;bD)hxuY`w69Y(;;i% zs^GWS{fDQ1we*+P#gVNiyz_6>KMM=*S$f%1OlVgA441@W8QKSOXM~J@Mk*}K^3Te$ zi`C_Ln`hri4^<f{k3`&FmewsdYL}{*$>!ybljPodn3{LVd>)jj;zDQ;jlg?Hjrkdg z6<=u15=VkUEY816756RhnP;4fP9332flaIFXh)F1!uUj)N*|zN8p6p`>3GA`qI}~> zfbyFkCsRN^v<?T<RSyp)DECQmkMwrRFe+>%3Ke+`hmdBMYB_|1<PO#Pah0Q4P!lfY zHR0vREuwN_Otv)YT0QDHRK>1<Hc!^R<9q3w@eEp=OWmiY&(14pMuwH#>@-?xGZTdH zf!d{kN2nO*jJ)<{$MN3vj=0(%JJ1mm3*5y6Q{)m*&}PLe>fo7WXr@jU2iUUT_?p@s z39#s~W)~@cFc9{qDTrlq*X^-cXVj{8Dn_a=tnVp$!)YlmQ>4AORdoxFZ|n%*x_~wr zybcXU65}od=he-h_85;JZAUJw!EN)EG=DmKg@yGFx+XDcdA#MW5I5psny2XWarZcl z>%0Uc!{eb8(Hiwo6Z|z1pE`DvI2J<&Y9O&5KGPh-|1$&bK!7o}NF?67UR&})Ie9n4 zM=S{s4deLnvju2YgSB<7A50RmQm7)@s72_R4T4CtSctK~kjzY7%zIfydSz5bZuEUA z$`(dSGX&%2In-}H8CCuqhx?*NF66gTi8G#g>R{HcBqe{HNfv>DQPg)udeO-0jq`^L ze``J7db_HwlS4@LkT!Ds-EC*DHMc9Z+b_Nb^R|d1=F&j!#xn`>svhI0&@72MzA>k* znEXL{|2`qSj>^PZMw9cU`E5S0IfiM^_3sod&PTL{Lt7<icaic2WvD#xfVOmD72o`3 z$_xsCWkV9AW2t;w#X-IME82!8%c?;7msUp=?}chsVF6$!yulO5o=pXVukiU!e-F*^ z>bEj21n=U1xB6bS6m4uXxisO#dpI;Y%cajO9_l_qE)T~33ihi-vbG}_ojw0$>zUDD zNUx3A)#xS_8h4H)#)$PN?<jU8vehT#UO=93nRW@xiluSgmPW&C>jMcdK*t~R#d(yr z$<##H@FcFIN>xFI>v8ol_wc65jp7Sbg5m+$w01G;hI^iHQ(@GIrNqz*hk2d?1({T6 z?&ndJ@>fD#k%|=scvzubQ)L1wVK3i+iLG(%M@b@3^=z@$RI3vf2P;8maE$j=YEmK> zppHUoc$*xL&t1m9Y!7B#2^7WO^DDT~^kh$WkZ2<(jkUm+CVE<}2^MF3dyS;EL8A6^ z%mMJ2G_i!_Wh|yZZ0s~nJ92GA-ihm5gqqy@iO0*6>9RDvz5V+s!<HRYlrfS@Y?EcA zT>4}jNJ8&Hj(nVlLiiiCYO3HwMbPSx(Wmp}BKrNs`<_BAwM-=ETFXGXtAr&Zk9?_u zYE>9YT3x-?%?Lg}i5e5>r4t4|t;eqZZ3Fgpd4oJd{#85mK-JewrUT<fHBM-gKbp@H zL7yjJ_btchqp7p~vTnMyKSf%+VnVMktH%v8+8_U<8AtT0np`u?kPqLITNCLAyzrL1 z{~^cWZ=DN+_wN?%1>{C8WO+6$2C|!2*5Y3G=uu$D`8S>X#Jf{s+fPEjEELsmo7hAR z&o-u3W^S)itH&(C3}sY8=5)lXlkicqlumwospgn?yjg{D$Npr?3aS==E0}9@!6p2; zwhGB0il0bNJs&nvU&owC_>~)NZ|?V@jslbThdGW)So?vAFFCPdPT<DFs-<B~?kld3 zbhvjX^%~n&84a$-M<#<~JPSPwIl=bbAiOGC?odnc+!qa5z$wy^(Y)qb2elYuR?L3I z^(tSlswwKDo*Xj67z`zq_TtZ>Fvw*n?PPmnRqPvgy^t=bst{PFo?qB=W=Y;}W{tPt zi0Wp`Lg=xUv&@_VhaNlo<zYrtj&fFC%<`bsSGv4B`p|?v{oLBQq_f!|<*?b{6<$J4 zc(eH-4n}MNOwp?!%cF@uMyidkl43sU+VUNw2JAjyVTD;BWMgHz@3#KxC&)HpI#~b* zRh$=_?mAUn2>Mrj%Mbc;H5fw5uy;1*Z}ri%&J&|TsAH89MR_Ln51BgVhD~L}3@&Pk zR-@}NVFPl2Svj~Ko*j0KrjKmn>^w18sOMY^5JGxLaXW3c6H>X7qKR0a6SBJW-vHqy zkb>VwV*Jn&NX1Oa;!gwHsZMQwcvPk5FE&4KC;KCwi~B4g`*9MZhoL)=azL>%{VuOx z$N0p=$c!QZ?y~7-^<zB8&Elyttb{#Hr2A^5SY~PKC(#|27E=8|jJFI6&qXf}QuMQ- zIF^-~nI)Eqn36CO0X|2fHDk5+)eii$7kG{Hn8Ru=lS~to<R}F*tuXuFSbGbmxY~7F z8+Vt+C1`L9?jFJ2-8yJ+cLKpBkj5>zL*wo)!QI{6A*Yjfty8P&JKwI_`<#E!HJQx& ze#W@QcyJwLjwuY9PxpykYZjccTTh`4A5xzEjeI=>;%M7i!iO~Z-wd8GI7>8CzGuNp zN;0d)PJ>TY6#VI^^E2+Y&SlBWbhNcTQviJ;V5O>x5e7l?!T}Ms%mRPunMPLxO*gml zdV#<<=%EQz8?n}vORn`)&&&MU^H5Vsac4}u{AA73%iSJAMlATAX2QbEq63f8W#Vu{ z(PDJH=EW&X>Nzdvyc<s46Wu!jJ3D!c3Xg~f^7?cS|HPmV2yqOU`Bh*tuC{UWVG~wP zBdU($Zd9$<EErhj%)p5M_w|pe{oqliWwXOVLt{E)>?>FRe{A_JXBZf#OD^2B*9{p` zi#_naUB-)tz3Pozm-|-6H{e-qZH2!KeZyvCfD_=B>+BUcY1g{gh8HIBbML?0Voi@Y zJq!CqXp{YYh8eJZ!+<bSqXf|)zIa(*nyQ3;75V3dmaM%?Y!D8)r9C7o3Goapa^&HJ zbJBSt(aR{<M)(xBL)F;3Sl4Z*>$_4~1`730Z2OrZU^G0(FolKNGvV5qZ51F!y-p%p z6+pu~x^f#NsPQ~@-$pHKiY{}f)&Ih}yUZ=aHRhl(=$%liayn!(LIJ}M`B;Gafj9AA z;8+vq5v9LPDh=ZTAYTTF8e0CKt-|DwiIMCqNvzVky;NxUk_v<Qc3R8;-EIVJ4h^zh zCj~*j?u^Wq%RV3nAPO5&`$T3x8r1vYJ=>ax&KYloq4!KA3kXQL66;L0SYRdSWQvl& zpOqb0FSL!0P1t!v4r)5Dkyc!9+@>GTq>bS`p^QQAa6#e3nH&SwCJ*C4-l?}G2|v2| zKl@F<JZKb=soSQd<mVL^N3;;fNhT|L9be3m@;2Q@gBs@?YIY12A{(#JJPnLw<z{9f z7ZrOcAp$OOzE{>;+I_^o9Zo9Jp66b&O=GFBGp=URV0(DEvhdk@>~*IKdd7_n9E1&I z48(stfv*rp$;6a5XQ?#$m6*4b*=WCj@htW_*R0`v5biO5iDq3hP;96jek2n!I<fE2 zyjD+>7hii>U-Dg=oE`|Irq1ed`9ZZAQ3ONa!Gb;T5I<Y9#!9Y%O+fq&XPrs-K%LCu zG~mZnPBsq3*=5)VPZQ6#m!4XQNGtd{Mpa5#a%i#N7?P9;^wERl8K#_+S4(x6a=T7J z8r16F{;q;N16@=^#BiCtAdIMjPq_Ie;a!+y9%&#g^)`a+y}XBRcJdAOHKW`Ur1R+c zz$iuindXP#AEVsOVkz|r{=sl`{FEjlo!Ca<>cL~;dRYTV8o4Yg{1Y{d5?r#urq9H) zah~~IS86QBiW2rN`vZe#d_IeNtjSj^OG(jw&aKQjZMJOj6N}!kwjDi<xI`xf-^I>? zT$)aG%rtoJpHhGLUIs4M3%V31AFi)TakXX#S!psH_;BW<8&uoj{thcQt{+&2y^1DG zL-G94ZgbCx#j@jHc|?AkBuwD@?sc}A9Ms}5Y`Xrt&sVIHTKT$75_%ULwo7=}R7j&Y zw+Kx~88zjd_eW^8P>Krk{70?Ehs@6RwP~|-Q5IiVAgI%d&*t`A^r7z_QS0xcCTaIU z=UF-~dP9HseI~z+my+7sMfA{1E~4-WiTQ!pDi8}fefo98YlT58lr}g$$)0lwZBRbn z7S}$4k=PZT9@&v>dhepY2zOC%1<MGQpcJ)CPqWLHn%|aLXQ~}bNXBvT@&_p|-xLQb z6)qFYV8zi}mI((mV2$y4BB?qKz|dTc&mzCu>XYL?S_K9wJ&m^9KFFa7PbZsUw`XK_ zr~g@FXU^r_I*f6!e-B_6j4=d9!gBqkFIAEeS|*Vu%jp}3X%UEu$TIxF1clQid1A8V zXW9*)EB1|~jGXUiVL5Nca!~MX&y&XQyG{DaQfN`y?Enp7hAQh1_xZ*(qJ`bWI69G& zXw5zgON!~?t=yyYHi$TjIttcV29yGGb}I6k>Chv<TB?uxJJ1kyTf-KEGh6-bmge8h zT~Nxb!IkYz8TH<1PlwDz3yaR37*$Vt1c^pJuvz2yo!3@$L<_YB>^m5W;U4sIKTu|j zJXFl#vBz5Qf!}Qc{QULp<?5)uwd)2w7hQiB_3uRB+ayF5t`weQ&IB9#w3?G`!)w2* zue}ufmb<zN$)8Qxx4`hGb!k{%6~lh2-QT-%Cw^YE*EHLsd0)gu#V5mAYArW|%kDbB zOyZ_+QmWHkpC1fd!@lZ+Gz}yHRTO1(=1tpzLs`NLq%Cx(m4a}~+=)mHqqwIhVk)aV z(Zc|C9#DKK15r~7OG2?{Vf=4#=d%yAV8kQUGH8M_n5dXpl(toR<$8rxi{~+@g4}9` zPn1CdUboK77OP)=r<7O<@pz1u&j@DpdLHc4-48+Q?{+iB_D?U6DgORE+t&(`=iPke z_H!BSipdb%8c<#VnbU7|^T={n?R4<Z5v!mgu1H@2N%V%9N~7V}p6TW;QKB$0WaBd} zywT$#!4K>yK)9J1GZkbO#t-jG2W}VMA35r@dPF$s)PxvbK#qC4_((YC#(bP^ypB3o zi$DZ(_czyy6=}Q>$pv_HYn1cK#n#Z<=H8K1Qt@sry9+L~c7o3x*LDVXTp{gYUUaqk z8j}?ir3s5cvEYJ4r|_PTvpalm)=>u(q!G#w>a+ii)S+D}oT;IxC`~1liVT|=EG#$? z<x`w;Blpcyow~~C@Dj0KnJbZz$XJJ2KU^bRnvn5pT2s{WPch~?y3ue?$f9Czt1vvE z4dnxP=za#okM4mnLZOGt{lr5}5Sd2Y;b#^4+fG7b_7T?ojJmGvR~iUOewk#XwtymM zC2<elGFiPiOfje;&vqS=ytk@4eN2=0V#{bELLD~=+r>h|Ub<_+3K|49o*IvLrIiZ6 zf~LN@7%L?3v&3WB0<hu}*bAh=bfN}OJ1g9oa^!>n6BdnH*NpVyL#hgy?TJxgLRa*& zMx#@aW$+C-0f6`#R7st0C1OjV5p;2@H^7p92q5|1|Dih2I;w8nwEkn2AUEj(|Bk-p zweQAc<8SY!YKUwg1m67c9F1TyAWvPN?d>%a=NfCpZ=uo28Pt5&-eB{@)vi~21hLLI z=$?0&b~jk2sbv*8N)Cpo8JkDzcjr;!7cRd6xSTNCJL05Gg7UDF*^G|xqz%@2D|Rv@ zf9XUY<Pm%H_M(m3j<6rCuV5`af+OmuXD{RFW{&!)8)(J7@;vvy9b_Zz8>>s4FezJ! ze-aOdQ6auePEcabWzCnG<jNb75b0O?0Z;{Uxb{2iFewxNIlAIDy*2q`M|zrMqcp$& zK_}OXhq`4PBhlYeeA$X5DJ$>T^+zsUz6@M_m&sGNCxM>B<&C?K3pL&*`Fv5uA^E1h zkU4-H7SGDSg$Lb}`GBbE$zfr~z}#y3nt49&<)q-&_c4cm?dc%gsflN`)vW)S<%xXl z4#qX-yR!9l)M7za<F%B-6f!R9iINoR8VKkw%|KtV5T$SD=e{>2+Gg@vwe?tOmS+cG z^GSm>)8t-i!SNm#WK=&Qwf5Soa}+6Camt3go^1Y_7}y4fQ8W4w11CX7s#Ti5j8L`^ zp8q6Rq{Un*c8c+`!HXwI+-blchyWx*Nv_8qkzovt-j{KF3K`zrK*lg2?)7J(TYwR8 z&M4b9HP=DoAfc)EEB&MHY6YKf8<svLFcEm~+Z@*ltM4wDz#j+Mq7lVg4jOBr%4?&B zBQ@oz1z+LwzU^;{9VA0YN4lfTHv1Y35Tdmm3yj$Yn-eQ3wi*=5^)QFYm4a#7%X9x_ zbara~4~$M4IJ{Ryp1Y0~YQQiMIRbBA$R7r72a6%}kTrxW%d8i!f>b6X4GmeFuK)*D zlE>W2nPOkce<Rd6i{e1QbL9J7WM*`$udjD~vOu#%zwgL9G1Xjbuwm>mURqOuf9(X( zMiggDX}}D1Rmen9yIu^-`kM2QT1{x6&*i=WNhea-bV_Gt2_5)}rDLW_IQN7U6C!J) zmwatnr<UK?o#IKE`r3yWnhTj53jbDo(9~)isDtcEzf6Y}E~VYpnO5<h?j!!T-FyZz zx%yWqY4n9y#;Pl+#ZE=r^AsdbV*tyZjX(Ve3QZ+*smepu_|c0HqJ_UP77|mjaB*C2 zIhqns82w6{Gc%z6wYIVZl^oyr6nE8K_$2OTt<mE^c03irEt20Sw&>i7*Q`OO2}o7n z<8BIlM9tFefV9>jTGb(5$nn#l3I3oY-y6FetcA0Os>?khn|InEuqLqe{d~v8Hb5IN zJ3&|c$%@2NN=~`QB#-vtxI~HeUo)NNe=yS<cDM`D>y5|pRe5~|OTCP+FoSSdZ^FAU z@^r&>;sXT|p_Y*%s*SZe-*WDlrHlgQ*WLtycfX|$0%J~(JKYZq4eJ}OvVB`V1l%#q z3OD+lIlYc0ddBrE8p8j4nvmS?8;GUQYP*V*o%_92HZ0;bsApsFkfJQ?a6(6t^`TKc zbsShDOn?O&*hvp?B5G5Ei>I_vXfD}s!5nI^4vEyrj{JgM{!^vzIN%MJ(r8x{T2ob@ zGIh@D<tPyT8cNn8RpQyWa=K%Bn>StPrrUD7!$hxsbS+vP`+NlH;H@upYH}-2Ba5E^ z+Rr^in=fGu&Fq3-c4yC4s-9H|M*CtO=Q7{jut&~X#F@2MGt_%~?1BqK!LA$mt_r%p zMW(!pxAnDOQG(AtcHvkP;m0AjAI<SlVGG<u%3hPEhTz6C13b-oof&8(RUwzjCGLaP zil09!AWHE?#yTxF9^L7Q?D!7>&DpnOqwOL`+g`v1EgS*DZmen5L<`5aKD(hU-dzTi zOboAVqlG&AZtOtlvp{3>AVC6=UtfMNDjEtQcGpz6>Mo~bd~(vclB!MutB##{Vv(m; z<_XE!B6^L6!99u-71-{9A5`%SHXbgQ<B&8}1`jAE5MgJSmj|*)thu<U4%zzu2%se~ zD=Xu+f;OlQeBNffyL3MLr9_~oN53<JsBNd){MPr@9D@KC#OZ-dFl>Ex9U9UGD&pmR z7{F0y9$IW;g`)*FVy^#~X6XUsck>y|aDMI8!m}MJ0{z3*NJcY!BkOswk^g2M#h+i} z?LlMJsIV>gCSQ7oHe2O`mfm+|?BsLZAPASDAiM<(`h!hWO;9uCe|kfdVuF!cEcIv4 z)N~Y}>@p695+*AUhH}&)2i3w-T1+k~r>nA?Jq8JCuIsbNA$Q>HC<?v6uw@5XY_Lk| zFkG;7T}A~1s9Wbp_hvnosuXDv0z^GmOL=3`T^rEGhxj57xR0~yQx#(KYDZ(H%DYz^ z(8mhUE8eyWiGlaXgHNu)H}`ud%*tAy3OTGHgaoF4n#P{t0*7ur4r;ZOQ2)a~V@;dS zm5RUm+G9cG4dE{5MlBmr8T_vMp0q9kTz%R3n{m@RzMcyEk;pC*xB{V;(~5HK>t@Sw z@1>$vcc0^#%Vxpu5?2Kh>-oyK<S8iqIb|KOkGdOkHQIk@hA(O|(q>)uY}1#a*{b92 zi+F#QaOUfQ@B@|iU-LoT`Ha8*2<nMx7-0^C+0epS8R?2%M%&;=rL(ZH4Bkx66G_X_ z=S}k~#TNU~6IAm4<_>rGZjCvbs=!;vw=Al=B;h#`*_g*k5xjyHHtvUC7na)m`W<a@ z{pmex`y(zn-`hcHd^K3&Kk?TagO}-IY7u*DdT}u3jyUF5zVhk)(MP;L%eBK-v(rv5 z_RP8(o=gBA9h*DP7E)6~^C+%g1gh3umUVCRue-9oT|N0W>fNZR&kCpwu@o(wD&Y)w z6@r6u_x;64znzabnf3f{+A7ZobK+yxV>J6E=!PSp4uc=ipgLK+GWvC^25iRDS~l$M zwJzhFEC$0DEJ(#HrL>51;YdHT%#jsufeVDeuF#{xu~+-tn#tW#2}#Nn4#|MtDXJP+ z10N1PzF*-#Ixl9lB+*}xNJ{4Xt$?!rE|{@X20Z3l!*(KN>y;0E%|;8n`IT*FBSd~* zZn`)yZ?@7+ex|<{i3?0wW%P<IAdpE;x(+sXciru4of}zxZ1<}}0bU`FrM>ng6c|Z| z=m^!RI<)!=6<81Qo%mi~B4HNbtc`mAj5f(0tZhj_&Lz_S;G0tA&_Po>`Y!PBGiOmS zAd!Yx!|ciCrwf1C>Q+$NRLX{SvsGPk_Vh-QY)6}^mj97`xuMYMFZ4T>rEm1&AR&Kf zU79@a%#TdKZAui;b=W(&M=7ZGQ)4VHQizUwS>aapoIAYxGIoF9Y78g%Tl7iT7it^4 z%tx+2uhE4!7%X-rou~(KGfjf*j#d9<XMTfM{UQ1vb|zTmubnw<yG;jgQpF9_n1-lI zuh0ooi~J?}p)toED<Fpc=+sw40+WcP9jyY?PYXg1BNgX8@{3ab=~b;}s-JSjm6QB> zJmVopsLZ+60}6ZX+l?B_O<PpvVj}^%C4vS`QM2H>oh>l>F4&tWm_ROpzwE&6fSMnf zLr41a<rciss-a-PPTl6M%`r_eJdbXo1+}(<*OOOUgbOD5qzfkAP%*KlM<exl`Z(}J z#AJ*5DQqSQLea9pd$EGBb?B3XvpCY04Hy^N5bV0yXzDv~OPfPhh(zo(r3oRbuB?Xe zw+r<EPB?9xp`R)`vRGZ>Wae>z7W#-gR69`XP5Y_A%|gT;Wt=bf?yL!sZOsj6!4$?% z(qkOH#x_d-nq-P+La#u$p^u~>@e$zbRO9YEZ<ir<75YDWxNWX*c4>8%TR&LSt;t1= zO<{uY0Z}U$NN{ufBG9!t^-I+L)6%^@s2RG_t8QYoY5gtcM2FzDUEm5bz#ei}Whs)E zxbO3GG6=z+tdm^778P~N=#V+9*})-KjykvO=NZ%+iqa@9r!7^2Yn#VEn#XMp|CvDR z-M#`1*g}T%6$^CG#pgSxp{A{-+S72FgR|9;G&n&ra|TgNuQro+$*K_Uu(yUOTa5G+ zyndCMwz<t=_OCeZy(IA1uEJCqE@3*N&5_<_=99_=!;Ap2$sks7Gh|q{l?}Z=Bx0Z@ zO)#b|8wU0YvYw#u+n)Gh+Iefz@U~HA4`AV)>)W4wS<q7IgmVwvL0t3jJR@LB<-H8x z*20Hg52~#&SBHDBW#x5zjV|T>`F%E;WrxEUUCHBYl8jz>`ga-05AVY+?K6?wyRcvG zM9@o_3uRMs;U()yGhG2V>Z6?mcX)6&D4zW8%s7zqcPXPlj0QY-BXwCYeah2?&10Wi z`9s<4x7xQEfMi=`K{N&Mrn!SR_pFknXjk7MH>FP5D3GjJH;gG^N+6i`z4>p>b7$A( z?5vVquzNG~_`#~{AsBLIzN)CuciwC*u}`kI-N79<Qe}UqY-*WVD@IK#V-l%ttF-{d zMFeqV$tyIoa`_$AFeiN@E#50iRZ>J4rogPJ5CDe5Ni{5^DA_Y^d@x!OsUV|>qNw&f zbp$B671SNKT|i`rD5tPwCjApV^&O1MncllUa#E9*?D4&|dm2EKZ2~RcT+bar8(#X) z!H}YAHK?+cxsPRZ)Xv`EF0f!EHuL*QC6okEdnP!4!i%w?D5?^Yce0%^?-fm0=mPee zq0O5ZOg)VqKTTzhkX&@62m(Z+JJp5QB@*Sq4{MO#ld@bKgSh3h`>(VuToFY`?$5#U zqhklF=We{LzH<#gSyvNdTUa}b4uI&=XbVy~UgwWgb#s1|B2xMab<K_reP!xq-#6Aw z)YS9V?SgdeTi3t>;qN2`(kI|q7i8PFF<}hhT97kGrVoDcPk7?!H?};HKjU*hc5RkK zqV$hA6WV@I_=~148E*vBL?DylD*TXOde@r<T4;Lyvtl(xBj`~^V%4`~)kjWQSbG>h zFu<;3w8DRI6f=YFzd)|=H%%+!hr(N)rGk~(jS(Mz1NvC)ukM_WRyaiYb(khp(f4HE zPHZ7lzjl4vuw1gM(RpcV=~e_YtS^{DwK=>*FlSWylPAi={kjJQ9lKD2_ue4po!`r& zGzFjaqi$acna8%dv5_we>>i^f$K32$JkPsJ6$V{Hjg6!t<Ot9Om44*GrHnIra~_XE z1SRdXML7{<+n_8+Z?|)U08bW+SLS7qD>oBru>>DiK-`Kx`#WF{v~3bYE4TIpLMKvx zq<tcz?`gBoGIhW*{+^(k=nwV+@K4dBFZG#aEuOok!Y|EFkU~~l2Xj^Z<=4vsdIb`B zWIJzTPaVD<wz~t8jK{(2{z|P=%?bm5b*D7}S4Mw~qS|dR4q6*o<Q^kVhYrSs&CQ$f z6V<nrJgBsdWu+jp4~0A60+9xb*S_0Fuy=6;u$55^@6&c8Rpy>_3?p(xA}1piuCMG? zp(Yk&0WLuQ3zkX5l!918%pk_UO)(DHAM|zbz#l0T8>Z)7L3)iZ?<)4wW}efzUh~@m zT4t4z3!{Fh^k-xdh?Ucq-I_S^g%j)y)0YLqfV2&s$AlU0U3E~wocvU}&v$&gvv*_D z12>1>JhC66HWhT#g#K3N9w0pkG&hGG;q6w@r(bS<{G>V3ecmfRaBIsJ^qx8Ab6HVa z<kBK@?Yo)c^Km#k^nwy1UyHi*sae5@4g8FW2`&t(H@8E~#nbWM=r(2S8q#ON_~KnK zqsfwV@5Hj1u_FN<2#J_5((qg0GQB&>Zn;jl<>TtLB+{l3q+_X<!O-aA>JqAJpWV&I zdFLkxCPHBEMclqd>pKN=&13*lQ7xyOTExf+vqytBVuZ=(WnO%|Mt7=vdGf1xT&8uZ z`XXmnYwHm-A}(zI<7%=3(iz&xJuhT6H+ACEb1y_N;dv;Ihlf~t9kTsR69=&J_mO+8 z4pV&nvo@>#muvGP_VZ!y+K55@Q+!60pYveslI#exRmYh@Iv>#%f|mF0-B|KNtDuJ7 zYCSwGs@7}=^Y%ScLN-@r?htKiv=>~V7&d9CO7x3U72f<GA5su^WLJ<dL%Qp16=o{L z4sGKxYuS<aF$I(fw*H;yV*C(2LR?j3;|ZCRB4r-3aycx93tc<`O<EoGWCbfXC%3B& zAuKm2MvK<j>m{FOgmKkpqz$45j3L7Kjmnz>7o;q=s#ePteMM&^j1YgQ{0HHqJ@A4G zqwCM<u}XNXmsgLP=yson#ubu_XC$W__L+|pn1!@{I%Kq6eREVZFaek{2<HwcpFCh+ z6#UGD=@zJJeC~*R++hi<Yz!JID<Egk>je`-ZZvT1L=p!#BouqNuUUhTI>}k?^OK<` zq*so$mfT#e(|f7)OLv;a5h0F2P%uo_Mb9<6ez6G$m9ysCL6dAT$aU(tsu0nti5MYO zC4VocMY~o*CS--9Or{toTa^b@z&18Sn)4l!NCXENvEpCJDuhDdl8>XR*qcpa{L@va zu~3(>2$ozQ@Y6OpP#Su`YH>G<uY-aYhKLeJ(hUPD{imd3b2|hxg*fxn2%v$-B2-I1 zi2XCawLbgsmqS8ya4(abn1{7gOVW)$D)%-nrpg~Pao4zAZUnn=0h%73^PC{6cIPI7 zYa_cLPpWQ9T=A|B^`r*B*cezVB(9}!h19>h`IvfEN`ZtY#8K>B@>EwK4;xbL$cV<o zX5r7~k6mS9b$L9EuIN*bj!8J`^4z5Z(ep^e@{T&qr=4QuiX}*~ezG?G`(B#uZk4mb zg`*~n9Vl19FZBvNOU=8o!Uj|w(M=SB_Otc&I*z}l<s<4vq8^mP16I#Gi->uPCf*q0 z{erod)Ip``IWpCvcI*0yZrJR(;njbWTwfn6W)em4^hmhbDF}aRyScpW_Z0ub;XZ`z zstrmw)rpaaFlQE?(T34Wd}KZ#Dcwcvh&<BAXjG55K>94dfSaZZL0Ms*Q|FzM)L)PQ zDf_<(5<ub&)#BUWnnySN<<^ZD)?_ZcS>Z}Hy8UVGyD*BQB_s2oif6M|i03R)X+u9b zmhPI)8p-HR7!6qe{Mie@sZAi9PXnHp*~qlle>{k=FclzzOp<0o{crFNp2~B^+~VM) zZ(&;-{HA<Ufma;S=kQHCWZR2Ph-G@iC9S98pRUF<i07<d^Jw|VZ253_CY#bUx?rW$ z27suXiXX?p-^Im1BZH@Q31dX0Z>x<!oaw9CInOP<9S4ZxDHk_KfGD2^imTkpRpxwj zqpvLN@|Dr;-M5$*N_=SFE+6N=zpjAHo(%6wUx&Y;UhtfzDkeXs#OdEAswYe|X=}Y9 z--Q~TMdj4%lz^^MeVc5}WI6<v<San9*8hrICr4B^I|%;&-I~-Lb-JiR^?Q^-jt)}a zGVvJX7&h+V<1gvHk7-CVMqne0@m6cMH2RBw3)K|)R#ZbM12N@XCsaplRqj&MSg_cG zzL{y=TX&j1oj0H_9X)rmd=hmWi-gEI@J?>FcL5l^*tNd&zc3al`MYkl^Y`NOQJX4B zyCT@jfLvMpEcJ;K%O)o-m^#V!ZD=;bw7<NwR?TNY;t<v2l&s2lj&x32H*<Em7l1z0 zaE?}u68_hM6?Q+^oth;dY6jk3ItHXhy9|Wc$yEz`%3;nXO4srGPJSJ<cBMvzXU|ay zw6Vg+x_6(Mmci;`Qr8D{WtI-3yZ01Bn#)6yF}1=wH$e->2#OQu^t!8{x&9GDI|S)6 zfIH1zhFmZF@lP02q3<J|MILlkyAnv8vb*FCt&`w6N>9QmlqD9r<n<B)tPm4mz*p1v zsp!;2+lBF!_l$o5ZW-Np+`llFs}|pzK@*Ao;HkU`6nPGMi#k@(-FildU<*xvL_Hmy z)w2xSMa8S`Z}EZQ{+u}wYnhZP&LqQhm&8o>(4Pn7YC=H%Xo`7#w%l?Jfzw7;TI`Qh zEjGRoML_8>ndG`Io2Y2N+=k;-LFf<}iwxh_Id<R9@A7L+zymLOIZ;nE9{3{xv*05^ zEOms+$oV>|Mn+X8U@Aywo<@VXM_v24Z~<$h%%9Lrext8Mo@r*69CQOz9<5y3Wc-nx zSJsoFdzU4gStr*`7RKqh<t{bT#8bq2Ivqw@_qITtZN^EbHDV_pfJM2u@(TB74p>l; zI6E9V>cGO-QRvpJ5?D~TxOr0~`E@ECa@W+Es0Yh8XD&UF0{n_zwk^XZh8R070V7J- zO5YC9=YPrZ%)G2B{_s1v^q61Y4f`b=8r`FdV4?05v}!k4fi=OYFwYjOL}bme(QF@7 z!kP__JqWA~eAB6pK!$?Uk2}@vTom3LHld$Q6A=PqZsIQ|R~6Z`|C!L{V{%5srNxX6 z5x#3bn79ZcV=XZ6gT(=W!3oNH=00aDA^j9~d{sQ({ei8Xzh;Fn+bG4?3#8BoC|07l zw{cUPs~=hEJ&2)eKdd&qS37pYh<MK-N&j>h;Drn`AJ$GS;I-<ib7skw5wU{a_v*M} zdqvStz5}xH*L<TCg5}uxU1sYu<EV6L#5JMfQKiSJyi>jL$8JOY=Kb!y5%-gQ@+Sc3 zK!n+t^Tb_&DX3hH0pWz1thO8DA%EbTa-5|5F5e>bz1TK*lG8hx0Z|I#M=|65O<WZP zYS5hI#5*ibV=?bQK<^_-KicAy&suS0RoL%pQx(0u?N(sd34N0{XYKBDE1?7h6qagD z`uCqS+TFUc$`!3Sb)jVtv2SlNe|(6W(L83<)ml2AAxpU6B1I)JIjV}oEKP5*AXrV4 zcFGo4;x_x4Z-8El3BJd)CpF?&D(7w<J6$~A(`4ybTz7YxWrjD^tnN)Qy?4s3<;)AW zhQ`qRsU${k<Q`6?_awtK=#BPdJ6CYCirwu2xls-0+b8K@T$oV;bw}k7^AYGxRf_60 z84Su5pJ@Pc`mg_q?S1?Q+pCXD#S815{i&@S^B3FO-knO3MnLfL(V?k=`Fh*mroHoI zTOj|`9bw@nGXFaQU(tkC2CB%YI){l-<$eRj6Xq?B%Q-D(=TjHq6ra=OqiCBWc~cOz z00)nCpYZV}f)+5XV*$F<h?6!A!<4ol>RO~cG=8TM!k|W;NJKEWZu2Q9TbP4RC1(pp zSFyQnis!RK%X&z8?>eHV>h7>f0vW7VVq2Jg=Lu{(gEt80kr-DYQHZW?{q;TYW3*Po zUf|OQRyUt#K{=&l)lZW!HXA?3#NoCeS9DrkeV~M$J@UPpFVgC}MRY6YYqy+&9d={& zdnRHYr#*5R79Ml+Oiswr!2%FlMFtZ^kQxYDGhJG?R6TvWvNZlm=qd~wNisiT@fo_> z@qwRlEOF>f2FoO!`iK6wWSwW*lrDjfJ79B0!XY)=oQbXuN1Z<iHipg0%;LHflL12i z4~;S$j%(^twe$e|^7ttnKuBdSdtx_5prHo#VsQfvORBxYJW|bj0=jBn+Z>Oue<h~M z)@nt)!@f@G9CZJHeq{C+ve@q8(|f+(D`8i?)8F#^MP^hD{Rp3`ncFTV@rfT2PCFzi zQX>c3QdgD-dP8UE_P9ndc-ScTKshZ|-rTd=L1x##UF(@qe8V>|lV3+QX9WqOh}5Wa zR&yE=O-S4P!|!1s)(}+z*_@?C)R1n@sX-m)Pe^PD|8=>WAtqK(v&T$B2)Qz+%>=58 zkbooq+t%)RLJ?X>?255WN$Pa?4Q43h&(N{}!Jb~H;A2eNl9)}cKg+fs)fy+hPoo@w zqJqlctfS07pSBr|e&HQkxa}6y@Vzfht|0q12ei%bkeIW7g8;13Gv{{%8~ii}u^o18 z*e@yVLTBo`LvwMge$io@6}GBSOy&Eg^V5<vm=ia6fF7;^G9%t5{gKmC^)+)O3o<Ui z+yI65)mr;;_)~eYX!0DP^6ELJkgOFGKN;2ryM)nqi2Cg{0r{^XTSXUw^KZu2A?${? zC(0EP!i2CR1D?#6Cju}XasC<I8@Nr;gfmVf3{!1RQ#f+zy>hV0?;<LNnOW9|Rm@f8 zpAcavKt9JsdvO!5b1Nmk&%5~Tg@BCfb8x0!aJZ2rlx|F6%U{U?%dDXRbjud1C<Bu} zZNhcGp$im@)k@k+E&UQc-lgSwsuHn1xu7c6Xfv1i0AxBBf5(4E`A&q7v`m7?jY{uB zUgW|Xs_t4ZWFWbTEgu?B;BI2tt+2(ZIE0pe@mQ@SP{u3#l~!lntI(XJNufe#W_@Wr zXO>~7pzK=E{2(EO{RsiO`oquSuJCBUe&yQkosCq8pX}bUwk$x!EWXU!baI-*)JxQL z_1Jz5?0npPbgFgEw|d@{25t7UF!vc6KjiJ2RWT~ep_Y@yTn(E^!17blNlzua0Yx_* zpVkmb1gnl3??YLTn;`VO*)eGgrExU1JY(4#d2!M>x3#bKUzRd{M3>iR&Uy{OTJM&w zU2V4!C{d?L@yF~~7sRWlO5^KF{oSa1Jh%f1%D{t4cFEZk*>gXGJPOGI{zPJP%F+9c zSt>BNHVl33n)_usVqxa1l)Akq(qcFa%(cuY@QEm4&Qpqtwd}s|8940w?j87Kwws+^ z)U#0h-hUJ%AiiH|JA;>(A`$uKZ4TqhA%uG0>UI!aLuVh%CO7NzRMZO7Zns(rQ_*Qq zp-E+xsM9=z;BA*sDPEoLV4I3dN8_ds;$cAYnD`29_M41D4fn6a-B1RJ9ZS-1+ww(P zl>dJ@kjB>w+rBr@!meqv+h7X_vS1+2H~_>)yk~WV6sSpK9Zr{QgV8=7zlGJ6x5A(B zw<qj)F3|m9EUSNvK{wo+l~R}!*$~M3{645B94_qwog)oz#I|D?L)h9a^YXk&wB55) zS25jne!vdMAw}gERu-4WV?&FW-IwxXD#QDCiuRGKwZbrRi&|(qY6;zl)C#UnV))<i zcnI`=Gd%7+283h;n~W~~cfn}w+EnzABpP<@a68mB1WfU9-uo`vWV3qyG)KAg9sLVb zd?9?d1*~mgN;v$R43CXDcLG9^k0#yMF&Vt5XNTmG+gRN-aE%Bq)_xBLgBQukpZKhE zdAU)Ox#zaDR!SKh`D+UKIDX+6AHpNNgQT$_>TAjfFfRD~AwDgbpCyO;;R5FES@{O3 zV<AIQg`-6LQcYDtAppnV5Kx5;c8Y+&MbRC1!vY2P_nRXCY6Mvqh0aNi-)r0vm9(2Z z$uS#=^R`}wVvZ&18iC^>?8&e?$;DJ~GHC3r&`=Q{>fG0Ew?pTQp@~Y(BJg{w)<Hd{ zZ6{VT=%!9#mS18Jp$XsP^H@s>7ThQ(T)a_GI?lD^qFcWZR?RV(3A3zu-uTZctM|{8 zZL1|oi0qw(NLx(*(3qwt<yZ6-6a!xtnG0%_cKZlVx&=dR<~J@N-qn4HHDH4!=o?qP zYsE^d>z=Q6L4+=Xa~|fA1a@f5nHMxHo>l^>eVWfzA8lrX(XDQu0_<eU^diOx%Jyt5 zq*_Ay>xZKJPvT4bW&B(zj_jQUa`*q1rn8e$ct{l=80l#mg-HazeSWi%XNOG!+ymd% zJKUNsKw-bAxDuLw3;>?ac_w`@S-)P{dTjH&fsw=6*ZqyRYiP_<YNtg_UGB{a_dXd` zz$`FV5)rEdxvP$wiQI4ZEB}0*1<|dyHzcxyNikyzsO-%|XYN*6rx#7GPW-$FHi%g2 zu3U)t8Se4lWt1pJ(f|w0uk7qz?`7|@sheVs(=MC$MxJ-kBdS7Sc3}tlqB|tv#*r4p zRI>sr{W}5A8{Y_bNk$f0?wS^KIppskKu{N3Zfi`{G{bhEazJbOz%8Z3k7TcP8u+H7 zI}U_FH3c~VW^B8dDUAlWcMs6Y;!#p0V{b6or9a1$YS*Ul@5t<Vm0(}2=D77tqbgzh z@sXx@v3?%`(w5b3A>YaeV3h7pMy(L7a<!d7R5*ygN1(9qJe=6fUL8IHn8Ba_z8Y{w z4sNWA#PKFVQpFcH3PO;p?)5~SC*7L_rb1%mC9m5<@LHf>MipcQi5zewOC5XQnz__Q z#4xP50gvfQ;XrF;E%~^+e+e>Os0g7U5XT)7fXSY4T(lX9vt+c8y3dY-EBKSR6fow| z$NO_)8ZNBC&R3tSDGFuG3I+ry(cv;<<w|<&;!T<`G8Yep!bdZ{o2Q|Vq<1cG6W*Pw zGdT^=cKP@fb=!O%m)?}N8*;7TQ*y+f@9${*J!ktC%RvmebZLo*4ed_#P8p00ATS}8 zTBj5>h&>{Gs48#EZ?)#JM3b#Y!A#cW)~FJu8y-XL#JQANJ5esaoneo3k)MSa&ZWQL znH#q*8=|~CM1u&v>9A55n8yDjyhQb0UCuBre05<OfZz?~-r4@dag%@@@Shz;Djv0U z*-7We?9ft$D`*;~x^o-iYkw7Tx(<4IZ#{SBi9ub`qLqV4xA^*vD~!qtx_d`aJ(f$f z$&&y;`1=z7O4R)}axNx0{GoNA+<YFO`s<lNS3YLSQ6VB0rJ_(Gzwa13q|504g&aST zg~g8a2^!2?khlkd0{}<PWGf3!#|>?elsd(c^Gc0#{~_5p%=^3)a&pI&?n+6F5>p$1 zs%4cLfvaeLvyq?gjKndj1&*d5h59CRu`WhbAPW7R59J<sxl6*jBoLA$`n%MXDD|a7 zAO{B1+m2YGLW>O<gcYr=LTu%F{uA4NjNy~Utp7xJ&ZK$V*=PN^Bc7Mul}^^{ZUo~c zQwy3j`&8A;=%F4DPnMd|&?hj~+H(xxz*!vF3$b<2cL^$<y<AH>onIeb%g(OVJNH8> z7dnHr-ckSlAo=Bqy=)W-##`77)m^U6eEh10=k#SEhkkv3iI??Z9->(iu0%zRV<iF= zZ64BF8H_G<^+^%nG-GwY-C_|?_l=zo&QsoRKWUEForS$2M-GBjm?CbM?Ey<M|EW4? z`@gI@5Xqb>7d_qNw=kQ5aj#3#^iM(?KobPS9eb+s|CEsMn%>X2Qv|)<SQA9~8lA{H z)fwO8m5iM({HC&S;(+>!{h2ygx=Y!GG_cPbR?F9HhINFf+ol{66L>o1dNklg7;k|( za0S{$WMZ)3;&a?=h6e#6X)VZZgy>4dp&YF~@&(U~2{uFB4^tFR{Ng^|?R90Tc(I}? z5^Sq&B&Pf++zVld+<cl_dF^0N+{Ft2fETts(Z0MEqpB3gf6}N+s89?8z?BoC_S_X8 zTUBniIbAIWw<dVpZ0j^EUL+4fta&4hQcl#`bGkR0aGXoQfMovo4?&^xp2sexp0DR! z3-qhakGBs?9J){uK7%CaoXaRuz4WfL*Ddaxe0VN`1}4+c!-Q?18sJ$EB&)X`y+a}D zN0%P}orKxHyJb<2)J#ZVN~x)8Wk)-wQJ5d#Y?_@u)u*5mOd;ro{pE*z??>S_!aLAG zHzy-0*A#I0+vH#PeL@=hf5X$$S6Zt!Bk7lamhhSNENlomT{_8c3mV3j{iPl|6^N$M z;B#|co-5G2_GJ+qlgWFe%gfqmHI!fU;#Ikg`kXTxCY#NMo<W7?VaG(8a(_bMXlg`9 zM?#t1yNPT{uJ@G4v`4|uNP=DoLKdnAmce_6CU7!Cb^K^DTU#b*C{9-BeEr5w#IP@4 zXr9IpSKrh}KvwKCUu4V;SBkx1wsw-1#O5E1QE{#$!w*@vrCl)HO|nu#NQj(GPx57? zd@7xEN_S|tz`uNYDuu&^rm~|tNM$X0m!5?CtK4|c$lu;IZ%C<y-(n9xTR3#GigDFx zEuK)D`pv;&Vv>s-RAMKrISzgQ0~kZh(y&C!N@g^Jv#w394)v^(SC0ZQg<$rFG)CE- zUYJtooH+Ih&rNM+G~dzEeqZmqVm5t_Gkr@rNwj$|c5dlq88s%IyB!K*4NTdk?Pps^ zUkDpn!kTXiSwY?d_<U?54;ZyTZK9*I01_jDgjK&Q{?EgzZ|poo3%DU=J@7L_URzDc zIu;?eTrf~+ol|+d8A3ZwKSmy2$%g-+zPihs`s}3wB^5MC%LgCCeUe#A+fc&Fbvk5B zwYWA;X$2KL3x7lYrmx%ZHISkPV5rFtT_R}qDNlJdI^~g#pB*AMY>m-Do^Nx9yQa_h zDr@t~6^*5TQLRX*E;m<UTk5gvIXYf_A6+6L7!AztD5(7Pku%SBhZz!x`NXprz?cv5 z4)O8+@eW^T2Y~d~<YsSvy|V5K>N`+M;x|UDyrkxt@!{<owJpzi&&}}#vy|d+v2rS; zrxn|k4~QoWzOiMrvNrSIL8rg*-d;}reL1!BCvJb!z;nitU=tT+5~gkppbs3+O8fCn z*4cbpe4C@&cxiQy<I-W0?J@4X-+Af5Ji^PsScar*iZ6XcL1A1TRjvI1*hy!q&>Q9B zq$BHgbL2;l4_YNO%&WtNVRHBSbgvT06GfISN}rOMOGjdjAOP5K!T%8eBOL?Q)V2^0 z0?|o{q&R`w*0L@e^`fgt_ghSRnC*TywpaBP-kaJ&lQ2S52wT&y0Ue<wbl9G9_)vye zD$E_kly?Vveo?i6s%q$#=e?DZ3pLd8sy#j#8fL2{QbB~L@(S#<J^KS<($~~?gkpz} zNERRMX_4uJ*a!LMR6ai-Bpk$!qUaGgq)|1IU(JmHZ&C(u9bdLRAf<-$CUd%KSNKXw zMYOmlol(mUf+g5ru2r_d2f_qU1YnI1@0*{}S4DT2HTQ{S^B+;AV#9;r1M>YTnkF=1 z9i_Ql(t8<WUaJ)N0Gi%w$y~4dmmXT3PeTW~`S$K8`{DWozg|&V99i!pj}|VF;;{3J zgP^Q(**a)+TgaXvJuYc0wb)DxCg`N@ycMTS9A;?ZAh(5W#nrC^GWa&bppWJi?*}?U zR%DNVK53ec!?!FWo^5`Yl%FzqhDgph0b0NhZJyA|h4BC@zOUc~_V|y{5NEgI@2AWd zOw0d|p%wavY@~CR<I_heo4|82u{iqsDLWD%b_igXpaEZUk)DMa`z&73mHmd<odTRe z5*Csx%#p_Z8{^;fwKkt#EERq)_Jq)jF2|5{R<`jP%yscGW}y`^2a#<468LCyV88#7 zliAe>i;ePcHdjJuW0l+RVK5Lgo%;%%ULKn7Q=sj}uhNhs&ot7yPyKJs!amI0>#8*j zWl83%6V&i;{d}uaK7Faa>Y`%C^DbT!uY*@!ILB+A<&(u9!0%6x7h9c#Vm+eFB1e7! z5ZecA<M?+n=W${fxC6LtLxyv_tHn>Ke<C9N?AC;YW~4A|-{q+0n(k0f-|&3P`)R;L z8S;aKuOJNvfiA%O>$6S=IKZw1pm^dW9YgpUNn}oi1}fVAA?wIv^MIfd@iB3V;wf%H z%}s=7#)leEL$(m4$u5_l&Z2}n4wT2Iq3_?4K8_OHZJTi)&Ptzh+#;Ctu10|u${+9= znK!02=1}=tYXp$d_x+1M=o<Z3_ydG6J+O97hZLJ3SFJcQ0dQ&(TdQPdf0^d5hm?vo z7>&8v>$=+A^<})S_%oNPzxBnRz?*6aq)K>A3I@CnE2;rq%(Od+u5!bY``*><K+G$M zTpn*j@;xEMx|;832du_Tw#*K<C2ECjn!gSHzOXwzXj9fbDUMK;Yf<J<epUnX&<5#m zyX3~5+`uOvdK=2dV_t_04EV^BZ1v90Yyx>uDvlVND0OPp_qftJ`pA2KjczQn&IIuu z!2k+t2ZFFHaR0Gm$e^q}7Hrs4rWlP-Dv@kV5?Lr81jqNg!y^W40=E4ZM!hoTWK?Mg zqUSsr`+^7jVfA0S54wcjwJ@}t4!+F8!h8z*-R?E#y)o(0A9>Q-5RA-lq&HIG-r2WY zU!)0|8nc1`sUNAF-Cd~yqggJ2L@Z@A%sPLQPc!!FRDWA?(5wHe2B`sl+qD$>xGCS& z=qZ@l?H`UOUob7L0Uww_pkrkjtE$esGsp}Nbt~`jtkjCdI&$8Kf}#Nv%Hv}NxnYsQ zPN`_e1WCe+al_E`G#k0Ix|**6MqVGg%&#id2r_hyy$Rnmiat#+k|>Tj4(s*3<)_K@ zq0=Z~D5yyB=2WfLe!%EgAINfI0e()JbsS?>aKa8}m$u3rLY|)DJ8`lS0KWC+bsDwW z3bLx$nUVWM4%Otz@lKndDt9eP`)WE=N#W?_bGbO;sSiRp6zOHVI<iGJ?F3$7SSP6z zQWsY7HjI(;kd-Bk;PgGb?zXmHUB(G^y0AT&;E6RZLHgtHjT?(6jJ@PQbQ35F3QNPR zZ{gQevE>l85cCZE8;VHS+~Eelg)vofgS%0ZBqIid1&Y0yrls7ROtAk0Pc7u(TZ!4O zUczN&aq3VR%HVK9IwUOqc1Xl4hnW2R;HVj6pmMF{YRg$EieXOWVWG-%$eyUt{^^S< zd9*bUiLCsrz;sZu9W@_-3Zwx{`1T*PQV846Lf~^kTqwKD-+USBC%(7R_b36Va^=#5 zF^<7lsDYV;8StmZqj1c8R}<m%f3VUeUY5?|in(Z>X4VwL>)m?qlZvDXn8z-=9E4q> z7AH5#Jl_hM`02UrNG#EJ>fOdneWCouJQDn6PG)<cg^dVTg<*pDNza$J^x|O-w28sB zu%%Z2+MCLL%1Ev3fcp)dmiIe}3&_?GgF7WyU6Gj^Ls*E@CT<v?cO1kpwdH0nC#g#E z!dnnNB^8@%vlby)HMpq3v<2bqdKu8Dk*E{$qzXeHS-X(gY`j!PpVp@dodj8q-9Oew z$cj$Hq&Xd_j+@@t^HYmiKwF-{4FXl@kj`4Hv9SyyjrL(JF7z-<Sl4YgYoN|}Ss@uW zBKJpEcBL1%=j1-Ol2~OhT|wd%XUow^K}sXy$rqi9T2#y%ykJZEH-=hxoW1S?iVj1C z4kL2(Bl!`31A#<06JZ5A3vwXa4m^^(=Ood{hq58IvoBdR-AFHKx4&*O+mVi@T&YFj z{4F7|e}}sEF!@8I9xh5J69Op2)WD5UA;45${Ne!5g(Uezus$YS>G=u2OMj<mIW-D* zs`^OJIuy^_eib8JI@^wFqf8+`9*ub!sCnRAuo>>6&K^}(S*uJE?qWt)R~yRPPO=1c zrxiF)+5_rBFGrKf7$pJ#S6;eisTWnLYkJC8OC%?m5sJn3T(9}jPsfkoGSY%pt67e< z<|ElH`_`j?!PI)8uUD5OPtY6bY-iq%2S$@PG9O|{ojxRLMr-9I$-P76<=hW8%XuFa zHi9(-d{DRp!UQk-$lp^lqM%ZuarT@`eboL+E7|tla}B%7I~e7LC;6S#@3E;T5x+sb z<3y*a<r7zzFExxXeMESf@Kq0ZN7|-zndaEBX&T2JNH!u6Nmfp};q#;E?<dQWHqV)^ zHDND03u@BE4PRAICO#2bPMKUGWIIo-CDp$>cDImPHSNP-LXL?0JD{uB4=*MEc1pqg z@1w^5DW_@fsFVEJ{POLS+|aWx#p+9bLcZ@h1a|iBecrOyjrQsGnmt`@c;X*5f?vCi zW$G&M2PQ(R&WNwy3C{X<B1C9aJk>qKWL{4+RT$K+3vWF_JOP%4bUH8++YtsQjE7M) z(YgmK!%gs)fy*pue-OXR=3ZKLQsNik9|K#d&%2%g?4)1#OjD4(iSZj2WHw;GJMJ20 zd-FS-@eg;E1Ua&&$I9UqVaE|kIAJcsW*vXP5_P)ByfjNdQG@4g%f&}I&Ly${5YiB7 zGGaQH#-+6^*6(+ANpM{v&Bir}#N+xyN>#>>6jB_I?+-Ft%~&m3VZtwjFt*TOF7K13 z)i55^JPG16KDH8yiG5<}!1yY4uE4=gQ4wR@x=gXwO8klAj}{f0F!tU6CE*L&64L^~ za<y9zMB83|<q#0U=J>D?;jJU{i3#~f{Wu3#nQsY>aoRs!UjJ1-)>k2Qw4j$+n|Ux1 zHr8g9c{SYUq%cF#fc**4t+ZoWn>i=M9Q{0%Ox5zHzK)V)`2&8B{c&muG7&s)T>p`g z)+B)Bz*S`W_nu~|zFpNW3y&+aCr47eBqn&p80snTFnGKC#~0E=5&s8xxl2N4l;oBZ zZ9Rq?kv_@=wlJmOBGE}AbHeZCWnL3QUlwmgOuSv%;`ce-tdnogkH0G;n@@+~)&B9^ zp*Vxge_x4K#Jcqyj~_yjKy%)1{6I5*A0Dw#=I(<AJo!_yHV7Vdx0le6hWhx)fN^ay z-mw~lAWK37hgvCM<!#0TbNt(TF#1c3-Jf8!Ns-e69u7atBi$hIBC34oS_6`HOOtgS z0>fpk>Ggn7+=B<)M=nE;sf@#WCAN^I@xMk+e*8IF48mcoAjKNG)<epqJ(~PmV&f9k z5BK-m11YUx9wAkX=mp*OIDlAh;Iu$M9wu*yjQ5wU>XI-VW&hiqwtVS63yapNV-Ru{ zroGSJ9vHW?nF!s2_EZ$H`#kCrAxLrD4|(|PVaW(k6jg%_JqGjk?>~_Vqp~+HwP8kJ zuOC_1zQkl!JZ|n>aeSGRORp_T??@q$b3lG$4k9n0H2toYz$p_q=%YI3(Xj*e4!p<n zmr+hQ3+se2Y|3V1B@@#jO?1}$;0_f;N~)^Y{1qq`bXWBG<4g%-wGfH;5v19s?w@8G zM3)uV5KfI!DJJh^*+cEWHoDa@HP4$?5nCRr6tNg$f4Bi^QTLst=dm5bP8;p@FtNeL z8q$KXOQQ6r9fiX|h<uI~6iZiD%QOgb3zc=ISm1{*vG6(UT7ZNrs;w<;^}#p5Qhooc zz*!lR%iv(n6SmQ@`1P!Z3q(~y#(*y^B44ASAm@i~@0szwp#d;|(Jb^Wb4A*N-b7>D zu~ZPfG@qNoA>71Ia2;VEPv>?Q3I*(9GX1IF?M3+SIw;FGY>Ruc@lzRB`vTf`P#q!q z9Z+PfE<G`u(X2ne`?<~pLZTO@7@`o8?E-oP1q%-LwS(Dj{?h>FC-|Q8D9m+i7<X+b zwk4|6m+26vRpIipLJrkRJy5_bGb0zTzaaKkt2s6^ZM)Lcfoz{y_-Px?*Ag}hi`%I5 zz<_8S_gakM@C)P7bju!rO{9o^;OHaRiZ$({_mAJf1^)a4z`dKZ$2zitmyHJ9(-yzU z{83$_=egT0kIC;r$k9^&HV_2=Rf85xQ*UWXugpTzGR*?%*b$U`Ti^mxg~NEpqk}_~ zfy%ZfMU~19CoJvIbjBH}3LOi>!v<6>=+G#1U{HfbH2Q6Ri5t<LV`d6(LF#6DDH#p& zJA|M|f_fMA_OanU5Gvvn@Zp)dOx$PX27EcT`$`}5`z&K-z^1AXL!%n<6|r+xNt=Q+ ze|kPHx+<hv+XK5vnFNtYL?O|!v%h^g;qpJ$G5(`3=j5Nh9NLsdhB!!H4$<g$NVLOu zCGEBn+rn=NGlP4JNprM+my<yLpH83>h(kpxw1oHjP3VKpArUl<32}Gz$vC6~YVgX7 zX9IbT!hmn@#x$QQwKtX-N9T!eL_R!h8%(Z*8jrXVd=!baq)*Y4OjA(7KHOyz%eTz1 z-BcqcLvypV1KylNpm5|gCd%cBQiAx<kfS}*>hf`|Vw7%(06UJ(bk4`T-0Irdk-|c& zpOjN2mrhLVy)vKqPu%Fqc|Dc-fM@zIs~4^FEoVkH54Jp))WKhH*K5+DSL(`WD7zFQ z@9E_rmc5k0K5=Nz{14nDH#JG3#Zjf&&l{cTZ*@5G`4I?gjcqhm6b^nUhe&=^6akxF z{>t@Q6*YcbGibk0EB{}ty#-gFOSk14+})kv?(R;I;2s=;LvVMO;O+r}ySuw<aDuzL zLqFMjcb{|m_PF<b``j-8W7JdsRaI-w`CE~$yC6m-u2vxq;QGl1u*gpaTB$H1kzMFx zhHH3>dR1T73E&9d@3YHa00{HC=Lq^qY3Q}Te$^!6Z(KsJ^O6qsR_7Dz=-K&Y_URKU z>4bS%;{-~&Bh^Ub0h)Ire98X>anYj3G$_Khr{Q!L57My07)Or)DAj<_=%Fs?c1L<F z;4^LYQ+98VQDLiZSTgeL5%_m+mr3~x6Yf^?1BFcW7=o#n7D8684P5A3H7-;B9uEUh zpP+}dS{>e1SsU~-E95f-k8h1u=QIhJoqKb-bQd7Rs&ramf?PzAPvA;gzx7;b4+>vx z><5;wYcF)g#^lDMxE(`R?>-?a*3mReqfnMk2vf?k_+VF^{)vMaww?*oIspDpVGkg8 zus3=pdcP_oWzVy0i*E%^*%LX7=qN|K36cQcr1`0b)Qa_<ICge#BM609o)x;uBU@?q z<ZPm5wasQ6B1by1xaV-j-H>}n(I9yc_h6K7WYXgLMXu@~a9A?x=+SqL%YF{b5&$lg ze|sftWBfi1!<O@d-$%V2QbgyZq4p@u^jxzB3~s~oyl}MB@v-e*{(#rQ5fH|zlajM% zN8HMe(66={LHaR^|3g@}b{%#DWqjP6ioPJX%I(R(j4y}+&<_iTvjRSst3FL`hl?T4 z-_kn?;tQ%mKx;I`R%GLADyDn|Qu=E`95~PW@z-X(!`K_Kd=APEzwx-0Y!%A+DVRm> z*Rel*rD_NVISKLl47ujV=h)AJQ=BIkTgA|VBw!rfoYl``h*g1xlaj&FcC_ONy*|!w z#`YhUl=c(*1xyG!>%}&^f6^W95x-H;wl5%560{F6-l9@cdGaS~UBeSRyv90A4IqVD zF3zxjH*|%6(SX^nlbqkwB>WxxA?t_KXAcu2te$C0A-Ph*@G6&zeDrV&aBy!&{V~ka zA1>ufk=7G$3-u9yqALr|clt?;-o7{G?Avt;=t4%%=2|))VzRW~1pymkZKX){e*<(4 z+Vb@YZEsfY%j=FNKsa6%cR;LPq4K<}aa)|(?cU|BE(7s)+@40tgWUklZb1nq>{j#z z1%ZS9aA#@-biHmk7Z|-qsg<Mg*8-N>Qfj`MPA<D?e=o`a-+KT7_3)(&&w+KYcl18< zqcx;(gO9AGR+mPD@7kn>CCOkV>6sT-qmlCV2Da&1!17mY-EZ|ZzNnkhG=7m_N!^jX zj=x}Q84mu&D^vj&6L&!Uy&(~TwEJ$NDQCGkRDlpR1_Z6EP}a_Q^3LTeGTzSx^_<++ zG0VcMycfL3$VCS~rZBb#>q5?KcuE~mE8tM8I@NywC4icL?g@?q+NL~D-Sv8%-j~^L z#vlzneV$qq=1T>VwU$%5#e{z-I-meWN8wo-U294SImb64*mIBU50afa_C}VvHfEA} zF27$z>-!pW=Dc)5@E>DgAVavuAB10)Mo|yLOg;c*@9%M4%$xfA<};-jpYaE!lkKpK zW9g!^TzlEtNcTFT3ip1U^(!SZTCaOC?D=qit%BJX<xSZ#jB6=*$_gY2M0!yIgmDtF z^V_(dtoB29ttmcF&zR>2Et6VPfcSKGrFF?`_$Q`ku>(&7B?c^JpXzH3Rb1`TPUByw zzV5$KecLi}05Y`bl(ax$AUx45h=NR}3Q~vnUf<MX4UAO_NOYefSMY_@Fh(t@q8O8} z1OO2rx0AW(Y{BRE-|bJywAp<zq1L0T3KCCkLt*+jd%nPHfnP5LF~r+=tE_x+=n81C zGm*kkA})l*j&)WQ(1=vQMOV;n40GCQ42zQ+Fk&AJ2&j&`7yO~~X)rlG{%0H%?cX@4 zH3=IfV$3h`CgS{n0l`wz*KV2n*hNp!Xf!o`K6|a*?rhJ?6=tlLgxh%As>EgtS&h!0 zi3ut_${;AI8maObkpHTPG&z`Pqw6S)*lO-BB2ZKzSsg^v$f%8K%SwIml?}GJ(?X1e z;i_0!@%otL=zI3(ib-wrS#HwCrLddqv}PLsTk_br?(<wbF&u<0=Hn4DxaK04{fgSv z*;4V((jE&jF2;T?>vLfO4REKKZwQmWQ5-nGIZIv}e7Zhm%pbG6emT|7+l*h`#>2FQ zR7^<3Y(jPtE_`vBK*vGpc#bM6l^x5&=z??Hn=HeLs1B8?#QAkQuUzQ;w8j1Ic{~FX zKfewDF17$RHaM79c!u;L*UVZ-mKSm^dBzkdr2jqtyCof67&p=%Fn`T+^DE<vR{hNu z42*MZ44}Ek<SCiB<=XHW{<*UMPb(}IWO{hN+W#H-Nly_1XL_E05(L?O;k0`ezYQ+< zB|a94O#oNbOgbV^0fYmTqF@5lXa2<XE(b$im+R|n^qKZ$|LC3}#~ex?^K4gJt3|eH z&i2sM7okKHcE}#sPGT`#?lYz?YZQIpYg`IO3_8-{l;0%|6`LCP9^z~2%mzt;$`*(z zx^tR}1&Yt4v7BqK2s#pxwgj)$xbC&AeQ{|VG`CF7(sd%XScO)V;xgR!{VKD~rpIry z!$O%qmG{4-_z%GkV8Yktv)tB@(C~=x5NF<|LdbMVLRBG<H!J}EZp;<LF@`iDxTuFl zNsBl>I<hog+2zAm2d(&P(!zedq+Pil+JV9K4*2sZ15<~e^)m!R8^Mmu?)LotmEhD@ zTukK7Kff6|CDF;(LKGNb_7oY$!2kgJf2`qP75~0999**fQ?YkQXigllh_ZGGD6%rI zzrL$n9iUkQA>&J*H;-K+UAAWc{<g`+ot0^&-#z8q{Go%)K4FfVt{y*$dV`iY-d(oe z33DcAsL>HtOmrcKhm|@`n@EfWKb?!k;FAiS$)R@OnBC2Xy}I9zGs+ZV@eW8LK#H;0 zL(?AHy*NPMJ6!@!#bQ`2!j6=4b;3dnza5<W^g*B&7k=O)z0%V2f`<fY2q-kj>F*n| z#MRd9-&df1EqorRQ?~2IG$ZP%pH#|HA{2H`8!s+T4HeQCYrpg$a-2IW{~Y7{!nW{5 zF6(L9!4yEHk>?Q}bh@26(-}p8pgF?+)_ZcFg%g5l7c+n&YDj9rofWrO1yn*3OaOvq zsux7_-*DW6hqP}fbPFFSi0>GACP>GcLMhK(q|so?cO9();m=X(mu?7$-`*DIIU4qu zmW0c{+D@VA>Fv;Rsf;`j17Xbhe@x&QST)j6LKC9}@3+7wsjE<$__c_Mu;ABXa}t2R z$4XZysSViSTQ5temHyq!($YnUn!R$npP-)5=Pg87*~jRYTHQ!o76#xl<T@C4b5Oj6 z{aU))Prix}+~uMl28i-^2^&M2pS8G}RhUMRq{yMf4C6tB+kKJg=f|bF`5<Zc;Dqb~ zwQ1_^$7ys?5XlSqL14;<&BrT!(OPtQYxe02WuNCGWiHbO$|60g+nUF$uOYQ=zk3Ag zevmHmwVhfkqNXpAF0zXbT|%h_R3$)qRgwB{NUvNc5Fwz54~RkhQu69Dze0WCJHB>+ z+(EP&+%*vWuHFJeGXZ9HAgTE>x6Wux#b>sHcvZ6az)s_^>|f2m_u>S4fmDmO2cr=$ zGWr_YQ|33@Kny!$W`#|`@-i!uZQ~=Q$EDQ!8(`SuTTXF1RRC5oU6UZOr`%f=v8K3i zQv)g@`%_&Jt$j{TG%!5jbQw9`4M1}W^c4WikMXm2TUaihlMv1K+v{f?6d-#lTG>e$ z4ohWYy2MyiUkxO`Fz-d6=!(b41l@IL_S5#{@L9#_tvf#o`@WQhxEM?_o~&J)3Tj>$ zm4#2WewBW=(LB{oCjgugQ7-kLP<%W<xnzq%dYrhyEXyleTR{J!gB`0CGW&IhskOmJ zRhldtP)ROov}+XxHCQ}s>mUu8fM{#DVvd$Ot~TxgBKytr{)BsP3bilS(sv0?6s9}` z*l7Q7|BUKykednxCgH+a`g#xv`lLN$L9vStemdZFtY06O-NGuXV7U=*1sFcHZu)ob z`{NXf*Q$F-(((0sMmOD><E`Ow{;}l(G=od_gxNXiYW7*^lG5Rys9Gv<W*V`lwXyTN zb>TWdh-0<HejBX^eFMfXpmlbaaI(P=WL6(BY}X8Bk5ZFP^XDiRW}XhLg%Pq#e>Jnf zmrjzsA5zl&(EX{qbtWf09b%AOKKD57dL4#@&ax>DHj_`3_oFH0<_^5`Bl`q(S?JLh zeKR3h6@(OZViX^zJQe@Sc`g2%z*xQxm&NavDKpiYZ4u$cwJY}>zSdtWPFAiWk1@9& z*HH3WPCw->87cv99uGM}GKXQZZH{R~*RbsiJ!JM{iXOS{O%7o;qb5<;L$0QDU*Q33 z7%ud9*H6<<<RsbAH0ltrtyLsy8a|*?0)H-2n{K9mW4}6L$S4iuBFsK}w|y9CEluD= z{JMFiD)-6WgTop1mq?Gy8VN_-Ze8P&-Lmis-oiv-zsxHKGpl)FnQbnTZ;!a*>WznH zT>tO4HmdN|ZN8<Bfv}<~ru6Wu+wOSZIDW^T7c(ho>K-jJmA=U=?X}^uh5&OMH>BLX zp3n5P>FIeO(YUkMD~0#on>B8KARH$}5y?S7e)#<*qElEaf>u}|w0g+MUEGN=OXRji z;s`e3GCiqyCpWHYAM4*sR19u=gpliaXxT%{qJnx*@m=1)Y5czXWB0qQz<5x71Ms;- zYfAz3rfZc>vNbhuE~Wvf>>tLs9(4nlwTTx%7%P)ijm5f>gcqktWNASA;fJE~bl~CO z;<=h#!52wc@CD7(*$<(MOzmQA8%ZKW77OqIOL-Ex^ICw~S>(zA7XVr7h{2T!Srn1d ziYH?9jXOfd8!@-YU;LZ=y0j>z0tdtB6tOrY6Epi~k_y_>1uaV>5W|dx4zlos1nH=R z!NkX$=C?U>M}bzs$B)(Arwxqq<ooW&EeprYR0;i@Ho~JvwqZLq(v%WZ0L`j`UgzR* zps~Z*sv4E*Uy-x@#(qz?=0`v6@`n~b`6K<ADezNc&$?434yL|%f#yJ-_?Qvm$=}Fh zu75MB2<%{unfk&YQJfrcj)+L3CzrFqp4vjenm0xY2G@NYFUb7U2rwI~B<6+>*sOt* zo5rE}AbYeg{`zan24doYa2?khC*UbkDcLzLNqHvBO`rPMZv|rZ-^eK`T1dupB!Iy# z4sI4#V7XCdKbw?(b0q|k-j)vR-J!Rnb@=Js7XvWkR0IdP+CQ{zPnRvbJgBde5MzG; z@245_qC=nuQG0d%;S{y~yy!K;qLK?aq<?vw{xreUIT;Q;i{3l21gl>zP45Ku8y>ow zlXM4K6N846C3Un;UByJIK^S2ygZ?I$tO|496<f@+k^`-jKL6s;|I$jK{;QR8#9<!~ zTD_RYMtf9!<m!1|)n>`)iodlvoks9Xr29y=C+)RgKn~EsCt3$?375G0MKn3GM~Dp= z<ZHodKSJSTem>PmLF;4+L6=HWN2Fz8sb#t*pf@iJjP36YD_Z6hi(#r2R;Mv9Ndyo1 z9A-Na3car-krd2yO1)k8YU8EF=KbsO&(Az6-NW{Ohh_L%ZZF&g;0IKqedbPqhDezu z;lUJga}nkKT#o+bZ$omB95fbYaZt}poGb?bmZi73kT(&*7{JeWprWi2ojMYt1J#4I zv-k`HLd$Vx=DeA{n=yZa?zXif%G2`O^=d9UQ}9{Py90J~abl?st5RP-jvmr#I){mB z_!p}Ek*;_ysy8J%-bGxMnuf}shtnj1qfvH=$zRUxPjo3QFaH_oocK4=*@l#p8abj| zsy3IfG-~i3akMo!SC*qILf_RSa2T@Gh0kNqe^63z@jZWZ-=Lt|fV8#hs^M96LV4Pr zi3&!S!C`bagpSVksA^7fblHfdh)c?{!5LdoG2t$ZkE>M)=+AzR@w02#lP<3qSf4N- z)wA9uV}E4!G7!y{cH36Kv?;plKy56X>JV!;s#lO_Ql0qI3%o>&KE_V~RE<@zpef{8 z_?z9!Bm;Ueak>?=eZnCCF=`+&xS8i3Gw=P7k*e43zUlM39rsJV(J#re*U&o5<e(b` zUBUdZIj8u<;wnkwU5Iyn7YNkPwSR{L{>|3*_GP>1q>}~>zNd@`+#~<9ULV=}Kgh01 zK#``kB4Tx3HXv<5>^{*vfun#n*(Yr3V}uGg&EPmjnbO;7`5@)nJW7?}G36~;<~;uA z*EUctg0Rb0uVkf4Ly)+8ht5NTORWGGd!=OcXJhEiZ`aj>FW$ki;ElsCE{l#;y|z1; zy!O(MRxPUEFRvY_IiLF|s@2@Olbi;RXbaJtpup%%>WI{A=1Mcrz<7N8_kT%K4g^WC zQ1U@cd=bU9>Gyo=t=^O4*A4G0lWN{KZJHVyD;>Kucs?ubHoJxGh;q0j$%RW|3_HYm zc~Hg>+hE;IMJeMrjV9fj*!BfOjMoI?qBWX)&5EI89d@{Vy^uP^Ka1lABJ9{{6Tgy@ z$f7K-Lzp~)Ty)+=X#f>SrxxHV7ul66Bwh{V&}rw-C~Dfs=j7$ag4QEbGxnR1G78Jl zUYjdFhcY8l(Kj~pJ4R%iDK_{v_+l`I<X=#Qegr+^?Tt+Pj{W^|SZ5qjT-At#P_u_z zVfwaOQ^O3ap*&(6`ZI$_Ez_q0$8PJ|!^MjVQAx+n3Wt$uQ`z?uafjfwhL@V(^vcio zC+>IRny4TP;Wq*Xl?ff+H6jiJ)l{)yl&k+<Tf%?8f@3?J289=jq+GGNTz3Ge2SdWS zea6)1H&)X&@3FaF$(x}&X6IH~)`|n<?{+KJqWazCA8ycd9S$TvnL&t%`;UwjoBDNe zK=ov$5Z3USZKB1~DosHZ6Cme)?L^@wvR`bL4r6k`aYn1o_3E>A)?&$SXg}*#koyu} z-EXrayyw8PTxI!p#=N&Y5FXYA4bkPyqr<X)Vj9?@*`X!}G?sWQLaGzyu`(-JJ#D-u z1<P9tPcVy><#5k39p7GVI@=9~n<P2)t1I(Yvbsy4HSm=P5iLyTE-+Np6}9Iu@YwwR zLUPetB>i)P9l*6X_;Z7W{2YOY%%;ZCg|we2&lh1T3WNr}?~4K8fV+<bYc*Kds9L^C zcgJ7iIMbJ<8umJHAOkM_v_=NGKhA2Jn31ItO$neY^Hn|oomFoj=x6V>3_x0J$sWxI z)LG;`Auy!zIP?tu>8d{IpGU4_$-~v{6v~NQSGo>XLuYuzef^m1#II~fhSr5fg;Ui} zwgSv0YJ08w4Ky5{0S4FiF-*8atL0aGq{X8h&i10THZx!x3UvKB%I6j|%ahSsJ&7Ni z*hf#d{(sWiOzN`^M+Ej`vL1%JNWENtGQre;!gwiD%$ttXbt1}RVX5V#(SU2B^4P2k z!VslL(O}d!j1Owc0E#|iX!}_=rDL;Lroj4b;<F5(fos$Ka=J;O*056e4bl6}I^7gD zVR+lgy+rpzK!WMs4;Bw9Mo88v)~S(z3~qtlx(Ss5yAbgY#|CtGH(g5@cKIyN6hmEH z>Gt_7nL;IW&NUX8VpM<nqeqn<RTJeN#HO(8<JzqD{=0@k03m(t?AwanGErpbZ3Ps? zx8GOT;NC`pS)__P=O#U766%Ij<_1*Y+AMS3Sla^zEf!>D|7AQ16K>IV-JgMU^ONSc z3`yx)EJC@Wwp8HYObC0(`Ygd*TqHQ^7Th^-Km;?k<>E0NA)J?9)l#}C7hebm`$m7( zmWo^sHbql)37(eq7tnzXm5X2U-YtPJB0TE+`rElI+;_?5+2<B-%yYIIliyDEB5}a8 zKogP;i}@5_%MJl-S$`ce*=mgglANNR7)7nHI!z2^k=;Zvk<=`>&01rfmOr_WmWs<1 zp+A1QC~Da3pww;iYV6r8Y{c38tPgOk<0e6q86rFsYKiCK8V98~Ccv;uJ^|MHPtXtu zV{9M3>xU)wuVlSF#u1qE)ey=D{dSUI^p+mI@y^`QOPEF7;lciSGie8oeI4f?nCPIz zTu~r0^wpQV6GA7LeXHjKfwQU_e;ImSO9^dCsi&MQ*aR=CLG0L+K!$w8bx?ee{3D!% z9P=pcpE!0okP0zzDgV*0DCLqE_yxsU#@hZAx8OFX^S{zKq8EPv3O_)Q|2;K$77~Br zNh2J5f|rie;(-%5qe&eOgzfHY);Oo2N3#N!8cRZGNKWu$FEhG*zqLg$Qs+XiIY*{i zc49s*ige0lUaH|kFla2e7E|ierMp=|xyX9ohBB08Ujw8&eBO%tchmFFKTeA(ST~uB zc)6+xn4C}aDV+xoCw9mtNbIf(;~g`8Nl|7xE{jrfbfTFaIKKUYBmD@&I%IM&w2|7) z%n@KzWyIv#wbxF0Xf6T@f=Wyl(?<pw`AI~Cboi;$p~J!1t58mLt;A#91KMuArSx~l z^U3R}SAov6S|-nJluUBw-RZBzfuwXVyTAhraC_Hf=|TA#@aYLl8s%Jvn7FPDJ>Jv< zGVLpb7AGGa0~rOCb6BN)S^2j)Y}}tp^!!$smJH&wzuZ!2_cF2~`F;pxego@KYOmlS z_dYB=S{^4T<8(EA&WeR<$nYA~T7`LPj;80?JaHS$5^$emyGKu1UnL%xiK+Uk$aaDA zJD&~a`}bUP<Ow5>Aee9bn`k5b16wm7*5n{b`Ish=Md9zlu`H?L>_3ImwQ}lq5``4W zFQ)>j_i#^{yf6GG4)Y4N+gCd!tscH>Fze6(nJe2?h4?g6Ida!zY5Cl;+aY2ObmJ>~ zE%#7AWnKB>idl~RI_4*HD@7g{L{uIT+lx!4F)!IWSz;mcrXf0t98&tvWpL1sn^er) z_xLO$@Psqg2vxXpni22~;EAmS?ILoPlvpP)VN>^M4toZZ*YB^9&y^Z&gp{OhOHj{V z=5nJ_>*sUw5>!GI;FKT)8>3{8tWs5ADs@s$7H#>7#nr!5R-S4C)_dL8vqMm=8T#%B zJP`TnzTW#g$@|>iQ5LMfbe($}EJ^LtcM0gUiSB$u+GO^;mlCn_`UxvlAkHDp5*4Hn z)<qSo5CQ9_La}Me++aMa4G|k)SzJ(HOO5_36TQy{v}bWbsq)x@QcqQbmas<KFLNq` zgXjxSM98suA<H`-w6&cp{F#P%$}N-`u}b#OdMNOa@-s`Moq74DbTCBGTt?VyoxYnp zG?@qumAqWP+$vOrT?$zVYjWL7j`&q877Ws&h&;RR&^Fg<SY8xsrCY%tNL+X|j91}2 zJez~83nMq$99GM?n*<*svfB_+PuX4g!b}I77!bWeF1}(JK;oNl{QfkT!=5~Ep7>x< zs?t|E4(jzh1)uIQGps;qP;O$Q&V5unLo*vT@v5RRHEu?;N?dIC^EfbFCMQvpx)8B} zvU9>l-CJH1jUmPZI!Y5{ysC@JuTrD-=3;(ry~(5ZCY|u%bduiVwHeO)MfAE#@D9a} zTu=>nL%<dFEG{$wqA{H5zzC}z8pHTUshv_;$_MCrLRnL0Oi%F%)r9z|;`f|SslU~f zcFkzJtEWz##(nrZ6|%%FsH}0`5>jI7q2|ve7B4<9Z!ClLW*GLAObsvu@fO$l@v|9I zlxWt)3g0hqL5kcO$ffu32gi~*{#5*A6grTB+=SgHf|yDo<4Fs_=80f8HX3*#u0?7L z36}>8?5{Ryd##FEU^eUr*PEN^<Z=AxUlB=d_sh=$%y&#K;)^wwM8BVnI!B=Xe_ucY z>K;%f%@eDOt^plE?RE$cz4x_S+V!{8m1seam!U%=gWIw%!$g9(KeT^rhoDfy*<ct< z$tcl+OW6N7&dbJ31oxGQk}mPIrI46zc_=1^$$TOTPcXlQK=HTTipvR7s+7T%1eTBm zB&UiKMJ9y19AQD4glIF-&{(M{UK(uqqZ*sycrK9F>r`VwE<tiwW*hpXQtg`;7%Uda zjY&wLh}I!10b;~;rG@+B6Kf5#yJ^)2m7-OfsubFmKumM`2C=Sva@GOe%N)1Pn__a; znr%2Z!Y`M7tm!VNy%$sniE`WHjxu5d(EUf!U#@<s<xAHJtqZXA?qxZ$%t@s*EEg3` zO7H(}*x8Z5z;tAdg5~^Ff_=!dxu;~dt1wa=a5`LByOOv*I~52w=LRjR6-d-}FVK8# zIhys9URmjR93-R5>w(`H5q$+y-U_v&J9BD;6^;8ti1N=`eb+D181*{?K=t|y%E1vw zX94TzhrC!*f5!863J-jb1VQdSt_!H!`S`;!z@V!Ic`)iVCvh%>+d2-DaAv_gB8}yS zd{FUc{UX-uWJlXe*?DEV$x^%w;nG`Q1OM}D613NMHarvZT*Pogr+oTHi&CY0%^%;0 z(hMBrd2o)%WWQLe%$eppeEVW=p9c%qf+HD(N2dDSG1Jz8y3U?zcdq+xXJP4d<lNk> z2jYk?@FRNjy+42J``PWJr)B>DoCM+1sO8yfzWmx<IMVp;k*02r(^5j`Cp?FR8_9d7 zLi(={ckJ3xF|;%r2kh}NUB|y*V1ZAFc>Tj5aGwhO>}Om6eo01{1*Wy})GK?kp923* z-FfTQDQkom(rOEJr$tF*80wFbA4P`zr*3eF=^Wvs0d23R74pk-kbXp2PwZ0}8!rYG z*F4X~gx(9v6eNHMGX##V!LIU=$m!OldI0~bm-#wogCbs?sfqp<QgC1ID(w)JO1^LJ z@C(cy-@0kT;xK0EMpPQd-3*QqWftq(;JfF&Tcbdo>)NbqE>0<Oo&1*>4jO5(OrHP# z2H7FRh3aE%y{}wqI<DQ0e(P61cN-nVB2~cO?R5>^ks`jbqZ6xA`feE8zt1KX()80H zhM8tRB^N!239m=1KQ)L!bCJ#`UwzB=D@J%)K{xkB;_%2-iXRpivVQSG{YKe7R(Tq( z64EJdkWQfv>+tieuIEbWy3ebv&oWGQ7?j`-@n8@g@+g95QQBhQJCM<PPHPt;D9#Pa zcOrJLG2fh<VRPTzRC9VcPG@_&J*t2E@;+4nD9$H?pN@Hgty2<1TZC6II#DI9Z$C~o zBJrOlWgHq8#WNw)D=KCuGBhv0S=y)=2y==S+Xnm$kF7wn0cI5<=4bFMX1(yV>OArz zd~Omt%b_nBWL#m#bN1c`_<1gCBiXMH)|Ffigi4yf5BKt6w6o|zi+96`6Oee&rC=pN zWOnd3`%%3oaQ5nCedCj;EBUea>JG_<C0(_$Jo9WR6UE+R-dO&r5*)0^CC?dm%bXeZ zk1M8L`{#^$93A$g5AhzEcQ2bx%Y1Xs#M$5ukhHJACei#3lH_ps7!Rb#xC4qv#xW!* z>Y7wMcG%vzz2F%?@NBP?V(~RWFtYE(bQPHi?pOf}zylusE3sjxv|KvT#IM84EO?y% zuO+blYY8UWt%rvrY3L42@G)#+RG4R+ddBmoMsgCAp;qGcOdDxC3v*zwhWN#3A`n`c zF7Ho$Y~GH>iJrTc*V~K_QD)bUH2HKb%&pI9b1@VCI*wwNDBRNCZ!;Z&xve8I=G!Gt zG{2+W(D9)71boNABP4!tU#9Q94@H>CVl>l141QUxS0WFrI7;9|_RjnX<UCc%_=`r( z=~sh3P@%}5_ox`#$lh@cM(~YSN?wI&S15V#s|nE|#xvdHr;%mA=#`h6`?24CATb0P z34qEjrpky@OHZ0UT5vtiRz{SM{8-NK`9$@bi{pCW*EJE~tMM2zpH7@F=T^jxZ2rJa z&iuTrZDHsnW>cZ0)Nk*{As|#BR~^aYG>#C&Vlph|p&@FSm%Eq15mO=d*G2&=VIp@B zx=@up#BXb^11XV<eOXlqQ>L6x)Vr!O{(4c_EVx6Z=dgVFE1j|F>*+8SBfr%$b_3T& z5120Y4R?98id&IYcrLpgZ<zY`BHRx~-&Ma@1RcTV#%UdyN!Y{mgjFpf#HxYXbKjLP zfds`rD9@^WU0)O+EZGVamp8O@UAuBx2C>&7fW)^N0Abdbg99h%3N9NP{$XB}-z?V? zzj^x8(Bkx|;Pfk$gk#Xr7exeWXxvCkBsur<-IlC*kyIMz+{@CK>?>X@8|~MD>pdg5 zGg1N9<4v1rv(&i4s26B~hHbX{WHpc(VOflCud&LH6QJ_Gdxd~;=EK3R0~I}eD#I#R zM|0ZD4Y$U<1e|=Xderu>Up)N@*;Gnd&};<V^@;xBDryGxx1f63_W5V*YzOEI?6VS8 zCC&o~rM4t1Rn|x?zk>sBt-{Q(2@|Z4vD=E`!{mC%*MEGuU%+Sp7r(6_cAB`^k&ny~ zruj9~Yf`<ZRHu{~(rh?!xHJR?JNL*zO!w=!B+qlqU4T6NxNaMGL3vE0(y?}7ER$Xe zf%`*>s6h*V@A@t?l58vHhPBI<AVq};GL;<cnm8TQkDhwvJQiR;mD`N{B>Njh69d&p zgXVV$-+@?DGXaaJV{cE5Hv-{IMIB^$dN?H_@2%Q>ch9mpa7C~8wA1$R6%be7T6~ia z5f;z~l!9uLQQlYgWuy#`TPM!7r=h%P;S=9?TFsgSQJC96&s|wmoqZt>2jDW_;oEhr zRc47H&872;`Wx{JPn%qN#X}rX=hF(2qkYh<4?<^VEz$OOk<I%KB%3GNl|)=zA7FYd zma_MU*ild#qm5llh?wY<3DcgLkTe-62&?LD_4NgF92k1nq+t93-L)UPRb{lN!;Om8 zV)@~T{zDKNY1fIfKNcl26%y!#acbi#1#W)A{Guz^OR$-@pc?=4!-v#CF9!B>SDZ#g zE=8IQ-j5@6+E>MhpT~VIS2Bcq!A~1gP;yFS+z%zhbgF`<$z?!Bj^`A`I|x;61`~w> zMhq2|9ltr0Ok64yt*FbijYJTLF~`Cbsfh^1|0w*kt)ub5#qt@@Lq5sT6J@~Z*wsvs zJUUsc-hF<7akk`Av~$f=8+4G{C*@0KmZLP7b&o~HC5J4o;b1v<`vD(#6c0S7@f(ma zaQLd}98TUo*RHnO0r!9MQ#uht>Mph5aqMAwlVD=%OTkTJ0}keWQsdANxtYomC*FCr zy*e(8JAC^XVV7ikEj)OHfTZ3UO7B|V$WtECe{Sl}tH4ctljCza%aI#VaGgc7JB)Ua zgI3VB(sq%C&2*m-TXLEZ;-a!*`Ho|zySjH2A6JNgZ5#_7t;yL(V=I2^$_zEbG>Smn zD4`INJ(K*5sU8&3?7lj_Yq2@l9NEC@je>cH%I|sM&EfW_Jhy`A)U!u${;El8N<|7s zPNG5-h*7z5z6u&8+2<ZiUCUqu*gvgZeX(s%g%JhV@k>SU9Dr-b4CLKZOlpzIG{7{_ z+s20el^vbo{AMs4!?ouAU~YFR{IG^=TIW4(p7}Ohsa`TlKyoi}5(-leF8zBX^8$iw zKOkNM3;C*d`Z=aFT~<7HHPX<W=_dt?#t;?yxdeH}fo;BVlZ#r)z@(>R_;K1HX=N(E z!1aJ?OI8CzVLeJdc;F*7g38E_Xk%2i42SKUpLF&A<zbQpszsfdxB(_C<A79~;XDph zNIPC;IXrIrv@hQ0+XkxIB@s@3>qR1|$dC~SWZl|xs$4~NcO^T<IxlmfX;bF5h~gs& z7wON%3ORGfJe+O0ygr+x!UC5uJqhrI^mK#{L{?JFUN;G)$&7^maq7+%$D+e`_BAoP z%9xP`oFPpK6?tFm)^SAC;*2k4LWMBRau4Ew;oOj@RK$fuEKhy>2*Ki}0;3zY2Aev^ zS)W)$2{N*zB#5?%+~*F*Mep}{ta!4*!iL|vlbc>Hxk)IZl;;_dZy!_f{mX-IC0Jsw zJonGzhh}52!2yoY|HfjeQBNqyDV4?TifGcZix{*5lq9w9G6EYrm$2O-)l}4mR&^v; zp`Hg3A1gxjDT+aL6-h0E!d7WT@~ah3gSn(^)jmfPR-tDEAfb=aQ3z`o+l%Hu(O7Ne zK*3}PAvtIfv~%llLODp;T1og8p}PvR);mv<TGM7gT?z|NQIDCwvKsd5>SB&SGK1@g zP2b>DN4h3-+~HJ{U@~_wZ<EkVGV~+$WDyZUU0k)8{Pquk#ZogAI#w&u-K~3w!A2F} z8>5G8oZautAQRO3Y#ojXim0h?&XW>>B+Vu2d&_MN{C#W;r)>~0m|{BTuX>$M_g>M0 zwU4Ro2{(n)TmIIWPw3a17y@W-oKjV2XOtj?17EaKalgE>&vl<0_}sG4mlAWQ^i7^{ zS2=Gpcs2+|XyU2;%4IvJkaa8Q@VFP`{PwHhy0dd{2gXxEmekm_YebSpf-+KtTq-g= z=Toj>P;Qde*xY_O6>&F*FOz+^w4v6nuqpA!ixLfr_=j_HQMTZpGy}Ox=4Pb|fXgLi zWfYD-qN2|(Mv#CIOn{Ul6ffX_<q-#b6C!i4<T`-O|4@s?JP@Ov>`6_<6GE7PlDcsB zVX#_M?u(f3K$u=~4l}526c%fy!egv3vCQ37lQDCl?PQG@#AV%+P=61a6fsOb28<2s zPfL9)G@#<+-uu%RTLp#thA2gx^4n`U<~x<5V(VakYP}~|tTQ^#t^ZP~_0(sL^ljx~ zOgDq)emmo!VrdJtBC0#Q)>LWr74Fr%ctHw?zOJ=&R#cHwmAF6@I}}j*SimDF6(<pL zM6f64CLZyPVK~T5N)Rgim}tDK652TF-RYP0bY6GBb3SRm4>PrCG2N!Wx7O<XMP4t| z`_j+M$=S?pO{gfUUl$svpEw;e{loOKS**xVsV@{a&rVlN?m{g0D-#U}w;h--%}D?g zcNlg6t2`!+D9<fTv40bRC#Kx@9ki6$y*wRmu)Vr70NOPs709gllU$G-YI$Y0?gHX- zW2Jq-&jwSUOfzT%HFr#EXrBK4Iz8S)FV}sMOB?M#778Z*=_7DDz5?!33YW~g)klp+ z?$uG+W=~vy)V$mQ378xjG*Ct((kN6^(LOXe=yFsMBlz#>N5}?0GVYezp*XD89S$Ez zeQcL>-uKf>1^CZnn=Kn`Tusg^+RuKPZwrGNt!*3ztiv?K+YaL#%)dGX(F-fK(|*ci z<$qK*GK-O}zp2tJYep|u**B<2ad79j@&$G3xD8YlpL$qv*;r@7Id^q@{tCC?a}oRt z7_A3DtzS00j$Jlb=%hoq>A$HwT4`<@V}_^xP$MgavX*Pb%Zy;2y5*q{iHTy{4a~TU zThV=8SU|eJa{oxtxLoTsn<R@njZyLqXe1K%X3Uc(rGZ1HH5+REv@453M45bFmAjHf z(lCyTYc0Vx=#3>-NtnWwWxbe@$KO^@fmTJ9%gt4|tPz^Rr7B#j-|vcFao(=3F*2EO zyOzXy?ap_}ayb@ltoUueuFT7Mz8W$%|Jo7@HRvddC}-Jr8a5B?BglVwA7xc3)Q#49 z+zB?`F1;Q%oDh209-Hn9KJBYBye~yF@Eo=Uoj+Zz$t>_kYQoeR$nlPu%LLF6osIy{ zCdNKKXHYMONX+NuUxCzd2PJZDV4Lc?g<ieX>IY6*Sg1y2$L9Iq8a1H!A{ZWSnT@`! z&e!6b3@Ns)s4IKq>*(ZV@yTmCG|pVpUmpeX9JJVJhDSV?=<<`kwbr=qR5ilW*Mxd< zQxwQ_;ApWCpek<0qrE7iWpUWTE~;C~?t)Ws1c`(-R!Ag1&)S$@6EZvUI4o;&R`59Q zWvF|aoykuSzSl=2y+qR{Pj-Ag<xKw8<kQQrdcOmcP_0P%6CtMq${7Ykk9M#;d>7QZ zOr3<YcOHjE_?KTyb^x0#(4yha$J0-b_^XEz+2T)S-9j$=Yym2vfdXwrlLPAH$WWOf zou=TXs4jp!SBkH|oiLXwj@wDT@NMO~JuzL7<LTnLqWzweLDPLka<3D1_PY0a-C=jF zvC;$gI1HYWGh`}~8?I>ct?uv*48BL81S?ie#8MwtGQbwKjul6GIaUFKvS5b0AL6>? z)qKrK>#qme`~p7=A|wVYBEZ}qowqrCUTY$khu0kc4Eb4<e%uK)3;r9D$yk#0VlC|Q z&<~Fhby#d@8KA<Eb6KhG)b+lai>8Fb(02qw?`@|K^pZV2qN_N?hEnL2f_v6zz1Tu; zq>aZp7nI%=(%Wl^^?QoharsSV+<nJWZ#N#mZ{tLxZM}$EBjPnW`{s3b_kLY@@ALH# zO@?UcZ7OMP#rb^QgIK%jRxsU)4^zx0pS;kwD3xFwl+M~*ra#R<HO`SzG&68zyY!## zm-qkcqxK^jQ)B9&q1H!~wGNN>?Ug$^534~0T8@dP$L{s_t(t{#!ohGx*)gMn$hN1G z%lF&FT^Bx;*jZe<PzLVuK9aYceMK<z1BJd%sv|J;ROZ-pxytr_pdhS)EGQEJ=~|QR zEKxLjG$T?6bMFd2WlI^M)ikxFazu(35cWa0HmGP|po8+C(9&j)n24>xu={+ut^enB zkE2|uF4o6j*z7b*QY#tZ<}}#<2UZWOp$-3O!ifiy=RPQCaWM7F$0&1OlJTKE)i_@X zIapQdjV)(Pxd3k7Fkjm}wnBcpyO)_(JGb_Bwu_Vf_X+;zY*+WjPB~tz$^aLcg2G5b zho{9$)5+(hX!@siMuz9sV|CpQ*VnC(M(gF~Xe~a7mFhq~H$8K^o=Zv?=bl%xjX77# zOt`I$7v2Z{lO-E92x*d>wf6I7-i7lf=cV{UuV;`l(5D~(ICA(>lWOFe2}`lZJS!>B zWty>I?sg1L33`AkIc_jQ`AoR#|7xC0$YEV}#BYciL8m@uq(fyWHsb#!R0bCkC(m9^ z_71uTMK$0{&pj~>QASvndY@Evse+%}Yo&g8KfX4IXfYJq`>j7@5E?RUM944hQ{yv_ zsdINN0wZN9)<>MaEjU^`>kH=?2__vGipZ+mB(e4I@JizN<XNp0@iAkDz<Q`WTFwdl zy*lWhxZ`XZyO(OcTBuWepI}Oqx(u%1Nt}nJq8K;;jpac4SdE;ig()Uw)}Ei<VY#<3 zax1`z;=z3t$6?*EJNI7OYm#z+@a^|&y1GDpe021trj^j<2x`=wr0w%^@WIa+sU~Bc z*Rcp(X_=k}Ez|;azP%o_iBw$C0F{#bka5HD{1XTVB|L8RF>V?`oH}kuk@q>YsSE+) zsy5Tniq}%kGhDsr8=!99aq8(k(qb`qEsb$YZgTFK=V=^&1^h3j(VfrIWrb^L`*pAa zW9DnouYB526FC{c&e~X1p0MS&7HxU(r<!q{;j|w9iK3GO+)PAaulRQAY}NxHhBBkN zXiHztrL*lGiP8(B-k=4>jz=UP>j|@5QKeMjku)ADbF?QUm=)FK5EJv=P`aN@&LAQP z)ymPd4eApd4~uB>Nj&of{U!%ne<o)5dd}JS=C4>GFs2HjR8avltyba;!J7}l_7f^+ z8%E5(X*i7?Iz(k~I4>4Akp#|~J>c_uok*Hac&&x(zaNRqylv6a3cLh332N6v$e`E? zhXtU4pcU>Z*_UoNq|Upp(1pzaodULh@hm?2GR)JlcMTz9iX~8Yn%f-Rc3ev9ea-5U z;ds_VBxv+ov8imtJk&Kp)9q-8c~&A?u(UE`{#3s8rfL>{BeqM-QDh8;Ss5(0j|xHI z$MpjmNn#(#2$kRzbJ{K@HF6aEsJMoVzcDDe0B=VIg@5**Ee#GIy4wQ!H2(sHt}bov zMz?$-N_M#v?N+l|IL%hhxi+M(wv6Cz2}Z<%m6xYw_JX<0_P7ab$PH7WmoQlcf*c}t zT9M74?_}`&*LQl^sq>Ven>6p0jRyG$1M2-2ER=JdX5g2>Mi)5Lr9pNSgu!C>^Skuk z6-Ayn_bIm&UGDdbOru~-m=ZI1cw&`%Ll~<aGg?(x@SV9L=15zHN7FvYNpn+lZu%Lm z)#E-05&<OWr$2(j{U*fw<MpUq=c6!PjZT%#N(=p@&AL|?<`NLOg0hz9iS9z6$qba? znks)jhW+B7>#iQdW9oiYI50(=XmMyztKmScRXowF&AfkOwF3e%$62duXc%9S>4C)e zbVEqT@$i%>zqzoL?RjX!2kH#SYAH6C+df)yHgcu0Ba~XWX>?oUVMMAg)1a{^Dw_*G z2+3(<$H8UL`ms$iWi9HaYDiEJK{QoRXLpnd>ri4V6ef*P>5-r(e@S6{qQMCI?42@u zm-a2`#rJ^xi_Y&D@{X$TlG26P(o?Ax(hoh~9*#?ju26b!53gfpTk4^%k<x=(_0QPL zpqp*pf_YE0i31m2V&mI|VqT;%taAG~F@g~)UMbY0M90R$JLVP2q1;Leqg2tuLM)OU zeIui~bBE_N=Sm=A#sZH-BxvInBO?z}G;gU>@jq_gUxl9!043PfoZq_ixXy;LvYNDz z)Umg5Dmo-5Z+dNZb${rPbZ`ROqd(PQ-S+p(`r`|MtLMoGdGvax0c93JuM_3V$Hg72 zA4A_lMfs+AUom}vm3Kwk1=E*NxUoQ9m#*OO$^74n$QPiJ{wX4kHYgc0CyUklAI@C# zEn|^>KXm!Dzf^GQR3XzYGWSQN1rx>DNH|as?y1eRR6gLddV#aNR`6@sFV;<y7DVD7 zUU=!lP_QXc3r6OC7oVmjB55<+Fbz+DM;Rj{7f=Upgb(ziV*Xsj#zCRW?CoBJY@{hn zC~ADYnH;yVFMCOLsVvMQI)PA6TNM^UNM;L4(1Pv7|M!brmlx=HjmgM(3z0#TceYib zE<k?!vd|=@uoCYW@2HykQ3a!6mwE{JLd&go*BKmr_;Q<1MyU8~#4&f96)A_Qz7kpZ zG-85bslr*))ch>-sVfj4k1TSLk@4$O;K(R6*n?X&7#)zrP%m?R4Io9?=*z|E8H2O2 zxsY12@n$8TfSsRUzl`D{fIyTy5BU^8TFK+^yHeYA#iO!)f6Fa;)$x2_H(Kzz@2-7O zt~P-W;JfU*5L}Mu)4;UE+g5zpT3o28idhf@o>7W{ayS3kOAzt5mp~5TcSx0{3IGM# z+9x`*+|ZP<ioFj|{nF87`zq+U{nn_{c2i$Fcp4mv(q_i=$X$-aB^49<>8k-vU|xxW zNo=^7FY2&J7wfO2%>V^73{Ny<5}~hELE^=ksZ@IM#QK&o`YNl?IoQxRDrW}v7SLC{ z84Yepnt)YMv!<f%ql~8k#xK<>G(BKNDV?D4MAXF-)q&GX?$V<#_}Kg_3i>zt%L1h) z=E8gdjYEW1Tfx!wUQmKF*-Og~ngE3!`5%0GXQrPw+_gHV7@Q`XN78fJ(0JT$*yw69 zuhSd`e9rPB-uBhiJPmI)r=c3Q{ON594UG+_k{F~OsTDffhO4bjeF%=694R+ZKZ(+v z{8K36JWf01{!>4fP5rNauKD`14!KMN7m6)7kxvS-Muo$39<KoBd-eVM=!o9aPTWFP zKtH5IbgICfT9rejp9JLl10~k%i2L%cSG$8jQx?a-evBK-$j794@RcbPS>D<h>@DtX zPxv9Rq#^t*&z@;zQ6^yjt?85%5_CwGrFa76fvQQT2up2idBghu;jIX;w5d7w^Wi)L zj!pL#yGv;Yt)$=!@>nJqGXW0&JiB@&Us+1(&AAkbrWwLx^QGwg>cK@{KlD5PE>=Tt zeqd71-cQmOP{GD-|4xdN4nw>Zqr&L+!<Qf*$Nn`9V~<~_@*Bn0t8!jXORpdHp(G*; z`s462J8(@X%d>jo!B=K&d_a}A>C0s!%Y~OW^{*9H#?5x>CQs~SOuu=!K0CQsr+AIF zyxZ-<p3L6X;5bC-&mU!v+$fKYmto_tuDY-?R5X5{3{~B)TmHW5g_M?slE)38kqynw zYBu7@yti^7NJ!(Dh8A~sKPrS-QD{hK_?u5p&g~|9BOAp^u_*SbD@w_h@|oGi)$Ntm zhnMVvM8GCp&~IZTDvbZ&^5ZT?1e@WJgal3`sBB!s_-tV-cH=O{5|W0^4ONC)aUWm6 zDzLwu3D<jDcbG}%!aBNMZMZ69!#X@t8lDe9*B?uZu+xxikIu0<Ta+p{Kigq)$HlRi z_Ab=u!l3W7?8$GfeUt7!D&F7?1FOcXAGsdwWp;))sdsUlOvimY@N2!pg|vaJraIiV zWWnlKqGLFqD8U?4-#p)twHAkC;q;HT_pb?CsnJT}1L}C!*pDw@o4T;h?*&VO8qd$k z49}@>m>pMR&8`Qiyp9I>Ac72u<NI9dZ$s|Oz`ad<IeQ*gY)wdRG)U-*uzJ&d8T*@z zCDP&vOuSSv#Nr}54I0m=Oz<vvOmZb`dIqQ`7ea=RdO82WHNPPb{3i!w`waN?=9tFG zh;So7AlC|+!hF9C!K@dB=6|r)N`GJ%6AvDM-q;njKj;mGLop8b4GtAD4UU_NO`@3h zB2z@8D_2I!r`A`P+|mUaxEj=oA>Y+LHd<oRlGm5Fk;#QNWI1oyYdI;E#Ali6unCNx zhWpOm3}@>;`5miyTTGC0t~VZ@4Q4O3IS_IDt}9g~en^h?!CN7|QnF2hIMS!hNmzm} zjr++9i0!L{dR}Tg_YLHf_sW1qx?S{t(n!ZW6G~)wma83|M=QKaUuKpj8C~I{RY`LC zCm<QpGxyX>LRN^{U{DLc@Z8HBn^S%)jC4kcRhl*bZfLGbrA$Bd*-vq<I5DsrlLFM$ zQPmpoC`g%wnS|y8wiT51lBA+R9)BE*O(z|VyNvKTbs9oceQEcihxGnir_1k)fMQ=@ zwT4ee7MN>Gi7_qEH~fz_U!Ufu@cKApj@3kIy8R{xh?WKOO_q6jv@VSks<IK&#C?IG z_#HGgeer$x0bpQ3Ba&&Hi8O6nuf)x?30>|^M`@)r$g6ipJ5ZuG@kgoZ<V9_1-G&th z+}%{>rpx7pw|KQg0$w-lwfmE8c20T==KCAuaJx3+U+7d=UAJb;@A=pv2dI)Dwg?cK zil0o?D;`$;2hZH>DQC3c)!}toUHix83w$FjNXtIK&_d*R&d`lN@^g<@HM{@yid7f; z|6X|~Pq08=gm2n$5aYQz9@(wu#hD8X9!IahGhrSu+dO4lCbyJ}ua36$Sbpwdi~blI z7SAH@P$|%jzD?O7Y4vz|oHg)OV=+HxwYTKB4v=K~9fUtA7}-vUW;hEIW(0Vuga{op zVP|0I<>lk%hH2pbb&Q`Rl&U{?Aq8`nk5@76Mw~~Y7m5l{<0k6T1)=?-1KI-5mI7co zPfUfMl@AQ$j&(V1+IH&BR3%-{u@~MlIi5yX_uC(VBjje(W%6Yr;Jt0<(#<kkxA7|{ z!{XI1Bd%@M@?EhiK)~8PsZz2?G}h2EY<OITPbdA?-7Q}RDT$@If2NLag{s^Q6L?fR z-U05GwATQq^Ny!1y_dN-^-T8jgW1?0TUS=HW;!*vwM#LZOrbCiKosM6Rt;84-*_OS z>P1GIRbqr}@Wx-1O39?pg-byB^Bg^AgA(_%MtCU=Oy~{E*R5}u_s?HbX4O8=?Qv_& z`~b9l)SizoPPu8~05jKz-h^0%Xs-_F)D3>Y^3EPSuH#F8D3$V72lpAr#LA1}_Cilz zdi#x){IRxu_l>?uKVrOt$uaiBM%$#kIfHGW`UE203poZXCzV<*PA$%S-}i$*74CE0 zANK+WWh-j4Vi3d{d{GZV9F0VaTvIc;7I%XEk-o>(@d=HqoDo^;p!qf<J2GDOQsYhR zDb{zs8%g!m+u&<VLa!E`lzsvP7v<j1Nikz)XMwU(HJK@ikE!f!a(H+pb2iMB!1EZP zcLkCo6sU5+!S2lHOZwPVHTj<wiGUxMSIR7+!*T$9p;NMj+xR?zq^-fx&uh$=`Tetp zE~k4R`x6vCfrc%Z#-Vxp53TwU)sc#8Olwvb$~9~Oxacx*`IX~U<OZJqdc{`nj@+x) z<lQ0!*M%L>F%{%}pZ0lLP<`KxaCsYhgwyjL&^D>^*0pWiH;@}`y%YHSRjH)d++IqV z%qZ@>N5C9kmDz8kt6pURvT3xW?^2Ko^he6jt74JD2oY&-JrqBsW;UpkLoMnA1$C&j z9~Nt^vJ3}X0*C$jsTDCB+nBi#U0moKvJV>>1#`Lh15MBrUjeo24|Wj~7of2aK!)$0 zNIOt9oYs;T@A<oOw&kc#LSO5cQK~tfD>QjL<9c?VzHi$|ul*H<k0n5dCw2VFqFiU& zBE^Q|fwoXSz_7PWOp(>9V&#u*aM>a+SAaItV<j#gPZe+ta{V7%gMeKoa@tZ9OKW4B z5S(FhD_{TpKKNIs^K_h%JW;Qli?#(}(nO18Eh-w<68F~=t~NPKgK`q=Cun7h#tZi7 zc#<*&zoaPNepnM=V234(Ed+c%VU?}!1$73XMe^ZS@E@>quZ(@}O^GqnH-o~<srMi> zU(;&%#)%GoI+k(w^IqrePILYBc{Dk)auvFOA9tz|gNDScHNqTv_5JNU-T4GZr`g=( z+-2LIWP!-(Ddb?>wwwDb8y<o8L!YUlL*h&9jBNoj@-B9!&@cY268S=Dalnh2gM-pQ zJQW_JhK6GWLo=s!F>aV|%rt`Ou(!HY`^Im0GB^{XuNKCPc2`)wI4bipoUY@gZ#iWu z|Lhd-F~<qZsxZ1^OVq*VQ90uMWu(<~^<^VDnXls_$9k~>EyBvWt((YO+WTlB3zTLx zD~oLL4$(0#1NWQxSBw9_*ju*M(XHFM!6mo^4G`QD+=9Dg;O-U}I1C`TySoJm?iMU) zaCdjN;O>D_ymQWV=2?4xIKKd{(W|QWr?tDaZsk<ch9vlrBu%ji;4gXI_a5HsH(w4w zY&0J;OHW;ws7~??BpW5E|MGP=N=qT)VPQKC&YO&-{vn|lIqj`Wg7T&Nz4qd;-2Bq$ zTFRN>J(L#zv0`aRwxH2f37OB15l1zUb%wR)pq=($8kuR!)7&^`T5O}Vy~M|lrq&Vm z2}OLc_CbPNaZHQR%JdCai-&Hlim2A#2T$}*?c<5uXcv9LvH^ggi=Z8~IFltT@xPoV zJ~<`O(0R1+ZF)UDw>j;&rkuc*9wvr|xq~pZ$PT^hFDW^bju9DurXUIq%t)nnO?85g zn#_|a5>7#^W9UJw*t<xGX8FlJU6>jr-)Z1B#;dZ`L_y&dwKl5Ixuq<H95j(JNp!?Q zV5c6}*je4G>(k2Mcz}g|XhM&ENd^SO?)k3ESsPD_K0Te9I$6E^xAnYt(o^^8Ye6vo ze=LIvoHtRCs^c2ARqihkfCuzi*t|Y%vgolC{QR6-v-?XQa)MAmbZIkxST;vTS`d-7 zp1#X{@Oks;Aa2Tn1Mo!&L-yrWaZo`PvFhx5??2>!5O5JvYhi59(XgCu?SzGS8;%Id z{UNS<Y|#}2Zw8m2wum{NFVl$=ZXspr0F0|{i3>8HukwAd_nQDkJmxUvRY3csHtNGu z&KKKH3c)gzdj}qnAMm}oX)1_9DED}z=aRB;znKdS8aK!{i}mYHztQr}xZ%Tg$w=BF z?PD)o*{_`9JlzSrqZoSQz&re{@wj5c&#~j&s8J?FFd_gtQ4D`alGhi@sZRStuJ8;! zK8s@CkIZE$hvD_YWH|$h!a^Y-P$XduSvvaXNQX6lY_qW}`>qlIE#k6T@)r(_nddEJ zI^Hp@`{ajvtN#`uCwLp@+QDeU&zgHEdi`O>zGfHDx%^(W1#ifTj7dFkdY*zI`nEA+ z+>gNxX9a6<S7vgP&PxBod=TNw8CK1PC6a~R8llyWxNhqR=As>Zv5GAE_ntIa#Xw$k zNKg7y0i2u!s&DTa+KW<^wz}>8@KfLOAMMgL`vqOw`@5X2(G-8U?K`;O0rM<;EYr<^ z_I~d5EeY3SC~W$#l3wT5u-$rkeOdg^Ck&^0O*RbF9G^Qv-$-7M->Z9O)Z2XgIBIiE zlLAUd2mK78e=f2o6MG|4{SGCfm{QxvN7_<>swVThbI?W1kVH~gfCl9%JY-u;kRjzL zd6`rZrS3;VK5|J4PVtAxq-yV#$wQ+xf3u0k{RJRjeQq^?jwsm9M~b#cTvuwGVX2q@ z15kxzN<z&LzlgLB0^hXt$|({Rp7MZ%Pp%!Y3+0#}VFMVMaoPCciS3ZtVQ}WdgcNr| zd)vDif<7V{h4M6En$m~i-`0}n^>ht}F#v!jBn3*jL=@!Uxa-J&+znjPp{^Z~S&HsU z5xLH71(h}Xpzhs!O9jVbb$?wSp8UMUrS1t3sfqH(%MV9hj54){24w7RJ7&jvQ~5d- z=J~9C$tkG%vI_o>vjz+w4-TxO#)H_T^0nON8E19E=%@e5EpRd!uqy0Ti4lg*(XMM& zA#e(W2i%CRX+It&FF!A$kKM}q+RMXy6B8lwK2YmrPF4G$eaQyRUKqjp7Y!-tgFFbN zy%h~lJKn({z0p~gJg7vyAuCt)6$x4?p=euptV~k9JX9st@%^tP3wD3?Y$=L*jl88$ zH%FLZ09^@3M#jMf1bEqN0D}HfPuLkpiorq7Kd-CY=dac#$-jha=^kfOs4hhy(s2Ny zlNtv9cAj+q=>0exsv|#i7j5j8;f!5>?!D#u7Na$~or6md;jiOtqrNoT=6%kS`07U8 zqLNk%+;{Vw3Ska0^tzNlwQzS_F*F8&?TZcFJqzYZNA6;&o6)Pmu+l^ssNmGrh0iJB zA2IsWqqiIu(W8D<q;k4#@_6bNwi?c`u<a7_SPv7Zu0dxsr0f+1FCv1%>2bRF4v*`u zGoRP@Wxo#lmc9bl-VgcBg=<&siL#j*PiM|YVLr7T@Y$Fvg|eD33Qpd&O6qBsvI@H< zp;-(d#KO{szXb39Cr*<8ZzG*k(Qf>%t7^I4U2rw(_Tx7Jr%|TE)%zLum~o>wFZ;ud z!N$qxzqfq`%u?!kIS!~5uM<M6dn_AU#X#>j_ENyPeOVmOCm%oRH`N1Px2f`apW)BU zn@3AfoB1EZ!VT+|T**%D;V)bx_m~h<@1Hpebc&QT!~_p5YB4GS^x@g2w7YN)Jz_!Y z(1$WTq{v(<d5q|~)q2^Pz6h=}VVRcW%GG&FIG{y(N0tY-#d$jQa*dX+hg;e!e0ZO3 zr~$mIr=j)X@0|Dga%pArpIJ!~PZZI8pBG51`ZME>g2-Ukj`DxFHUw*6JF31>wTYR& zp<E%;(KJC!%x6kPcCg#2{Q@NhEy7yqDWo8t1W#ZN?j~bOCoW-5c|%j(DHSJ<o2ERk z6Hd%m62=nwLvVvZSpn5Zi{k8ZInm~=pf9<BB5ytq6F(Ae9?g|nRE7O~KYhD08>rZe zMq9`KqFb>rvhDr}u#gY<@kTsx_+|uuI&qyGZ>E)p?>)!EFD6apGtR#8%L$rfjTtLQ zkOrW_u<_8$|I$p&tN-;?tLex#YCv(ZzB#uB7U=oXIy&p3?s3&^BQCcvvM~#6PuZMx zFK{V`r8R6RffR|0)F)4{wQb+N=Et)S+*lp&$Svc9MIZrBilL(+m8*ZQN-ohM7a0Dg z)PvkT3JE`3fv_2x6pFqHA!`7|i2cL5lfo<_;T6^fxmsVlRBi_#{aa_o0lJiV5&-X| z@sgBWV9y9pr9Y<)2^EdjA=q-mJ(A!Yf^}&n7*Fc{qG8JTP*K|<2703Ro~*H#8m_^B zOo+FRcP;#k=yQoHQa`GV4y_QYZqR$GPEVT9d0ho}x~X97ls2VFnc#1tEDJWq07|J? z(5gt=f;AjFZXaa=NrvDrA(OF?)~`2r!$j&M-UgSw$L`~WoRnqFi9YQW{0N!Av#j>$ ziSB1>O1e3NwI-m(S1x6@TFH4OYu!i)HRR*{6wzqgWbonL0=ePc_IwwRz1ZpaGS*Z! zi|~o~SebAszsU|ookSMMQdH{2sbf2lZI_zJH${e}DL2u$WC*>|wR3cFm7Rlh;Np}? zDi#lGW+tSfs!3$oHV1leNaFyOAhCl>kp16!e#orV-_Fjy=MAFU-&>P>z<hUCvu9Y! z%;i=+NhZ*{v-cGRjZ@W{+-LkOPR?RR<`3eiSxD7s2h!fK`&i;P^)@2)($Mh{)vL!F z!QlLBJh`Jbg!E}ARYVUYH*t(JIaSK?bWnL-Jr)MIcR4`dU}sp7`?B1Q4|NVVNMnJ% zXt`^98beN<pJI1*%0X^ZgU5&=l^2E9fEeRB<E{3p>wtCT^WovkO^ap4Z`bXKzFGg5 z^cR@y6cxtz5GdfqDHph6pOF4Vv-p9!y_cXx>2+#`VaF(uR|3h}%^v{bcP;9KXx2l5 z`H$Ff6%3_L;)W`+gT}C+Tm#S-DINMm>TIPiAuGrPAEnzMs#aj!>9IF)sS3IXGqUX) zcOmLZlwS4$az9UnGp9C>($RU)XwD8HT98RZwKZ=h;&$~h8p%V`%u+iXzF~g+QJQYi zB=@6;YV}N>+i$<Cj)48czg!@87T{n$-aP(n#Z<t20_$qLEe(HHm)%_Q?KNexTK_tM z+9<Edq%IeA`c+J`79bO`(Wpc?GK+#BbSD3fV!(7eXgy_iws*;-aD2~dHJ#aoc>g;2 z?d{6r%-N1%N)k>XXm18HiU|r}ltjiBHt9^rHu-$;8F?yTXkrDnuaKv32HAN4+`|B$ zZqAGJ4Od)`<*bH2nC|rcL#Kbzoet(QYfN5Zk!&v5N<~8zsX7EV!NMZKk69I~z$Fd{ z?@Z2R12;s5x$>SX@c3r7r>g}+fxjz|!4HEWXT=*DxQi*TG(KllaL}+ANBApnO4uh< z1h}Zij7B;6;6yu-7MU(ER+~qW$?yP$JZ2fbBHQ+lePaW}!i0pXq;?5G2<fmY22Yt^ zu#($J045eu#S}?857SRK_q!flvrJBh?JLD?lzw8b`_853qrsiRm8*7+q0AOQtNHGH z(zPzhbfld2_U{*3a{{ywPO=1B3h+8pAl)oHpu9E3f506D2wijBQ84bPY-FxcUU`ui zd$I_3uo4Jqq)lxw2E2s~`;xf;c{9ee>N3__W2@=!+2r*%F5iV>b?9rPVTcy}5JN2s z@*j`e39_tf-kmPT0+Y|Xkd`4Qtx~!WK%Zeo41hPAlR^MX{`d}vn96Q;DN}>P6D*vG z?7HS)#iwJ;W-VwE8KVrXpV^zhb4o->6owMO+Lg}POFqEHFiKI)P8WA4(MA%Fu}4+T zjw3GTfhsNl|1%+-QsnFXClfbhZem~6GlMI*tf}5+FR$YOojZ~Gvyz(K4eC>U@4syf z?*s+*I?B;)S6WTy1Q>tr_`weGKV*JM7vn6{+Ei#WjI&UrF}#)(cF+POv1F@(fq#N@ z|9d1RQ0tI3W;AStiu$D)4HZ<k9L?Wq_9u7g_s=V&7Mvq%Kt6sxYMjptC$MsMofYv7 z!WjsO3;Dom$g9jsa>)ZM`2;nJKcTv8H4BcWzE~oLG1I)?j|fLtM}`!!syCMXIrxCC zJE^eopQnjLudw`k{q|w4b&?qPyZJcwwgT?{bR25r4<Py_{4={>h;|FISlPPaslMNa zz$}1XpP95?rCNRS=8@#r?bffyo?lhF-iU?GY1`j(0_-+==vCXN*D*}m%M_7(rm+r@ zv6jb`%5Kzy#WOtksq4<-)Y2$|$zRxoHz8j|oU&&MOcC{z*v*iq1ZX~62Ryzi=^j=} zEHA!H^_lN-$w}3TLF+?{{-JAG^*IpH{Q0ULWqdDD$_65uH7d8G2p++k>_$P)=gxRR z&&S6X(;b>V5(0NShGg<_h)P)2WJ?tkikIFYR-fY%qG<93RZLT5MgY>JL?G*Z^=M?~ z)3P~I3xV)!_=>TiC~PD@W;i<`SHX#W#~RVMoz#oj&}Zd&Kk6g1&P()5)4}b>R-omo z9{+c_-RXM&9}S1~hOnJoEnX@ZYqqpM3ZJRtu(12*`a*;(F~T8hBjLa1ux~DSH`32< zbsAe7_{-Hen?cPt+u^~FQ#pTKm13fV_|!jz1Id?7ME?G%Fa#8+)O9+mqc66y0mjk6 z%o6$#@5NuIP(f4(h0ZqWX}RyU$etGc%TKQ!Nv^6h>r4NH#x>B_LgoEmUjA1q7>uTg zNy}b()rkUJ1k}0wYP`b{M&WZ!=fETHNNQPA-BWfY!&a}ms&uRKU8~@nZ||6jh)abB zyD}}`%L*hykD9L<u@?eZgP=G622Z(55!1U8Isr;tE~ff7k39`YrMd4h2{~2b!AuaX z0t|C1tAs1Iy^jxFLnoc^GA_bhKl_~5ksOx^CEz=UEF<poi?trKxVEf7zD4nhUa}e` zj#xI8fI?L6<(vZ%d`<bK{qK|?hd>~U)g3oJJeD3pkouxw;?OsBo9F3t$ydN)_2Rsz zFM}nS0I_16#FwVi1N2=^JEPOkPYP#{#<quYvn50lIY4?NRW6%`tFR7<Xl0zh!bz^c z;@vWR0q2%zQJ`f58H}ziOM?+K*>Ht&LiB+jX<N`6AYr(UT=ZUXeo`h>+%2(4ve_UA z$V)ClA*%!i<J?di{rOZ__eWFuDgvowhK{gkGu-zCcx6yu(~|#sW!Q$n;fTBB{$jZB zVUsMO4p<O&gW153@pOipmLbMKV6rr{B&JlMam9c|eLGbodUQEEcFH1n5xAbDb{_g_ z#%%26l|UN4i8!rjJgF!=S*v>MifHrBSV_ZSvJVdRyE2m*)eaGL@jEB10wW^a!EH&7 zmC(`1r1tL<D!+t*2vB&*ybkgJu?)|M0zM`2Ct@ar?izz=+cn6g=l3(Rc|cU+pZ|34 zM5=jq!B(}}uKT^m2<G>J&?J6FBizV6vm$NQG;VQJG_JTycGD+dFbc1}F!U#)@fg|4 zQ>Sw)oc06&cB4U@aNNJN=GpAWw_#Cg==sUHFJ0+sh1Q^3;FnRtLsi)om$lrYU8xgR z!E;o18YDve4N?MGs_~Zb(~751#+m;ZcRlWc#@%X(x;M;~u|A<!q_P*vRnCKvda`)7 zyp!=5qB7RVmGQ-(!p4>p_EapgQkiQiU4l~3lH=(xhgwRXa^MNOF2ll?y|zT3mOjRr zjh%f<`cpajN@~+%5v_t2W}|qSG<GV-`Hq;w`lxR#<@RQ5If^NfS+~|}b+J(0cDc?p z+5Hh88<(WqnRCbiqz6l2%!!ex(97r>v*we82c*j&GJEDsJpV}45xJcS!%!XSEcuYL z-AQ;_)v{b3sWW3l!I1is#TNkYdpm3WxR@dDdA5Z91nA+d&lGRZ)S%IL^it|fy8f<j zeyXR7sN^(@r=wHuf=zGQqe%}*m5(tm{ksUGmU_Hkdh9zZ@}wx3artDTDocN2T9hJh z5sv~*o?diiMFpXDt3V}s8aBboB^)t`>NMhl!W0n38Kh6d4y^*pUBiOUd0P$-edkE_ zKdl>lLJNVKBY7iCQ}%%Y!fH-v0j`KYGjZW7PSI*^1FxJWJF3%)?gK0dvY&)8$X%gD zl5rthtB~GYFqwed5$~k^oDXCPj`2<EP8E7f6D%VlbKu{EXMUP^F%v9QYTG2}oTnP7 z;Fc%oOL6-ClN}avoSnJ2^8x4wqDDefy7S;Fg@n8aT)u_$weei=N`F4Du<iqG?0MWI zPMgIE2dX4lIluK$B5B%VF9h$Yx6symt?t%vg<^HW$E?pPZQcU589A7I$mL6!SVG)} z68KrQL($@n%BzC=Lwd#PjhW+rsO8%11<2UIh78R39ZVz_-Ey5*-^{nq_wp}I4-EqD zecpHbIa%D6tFrQ0TY89_g7_}X$l#)h?9$rMz*wEk%nxaFDsSW^-I-sBM54i{cP0^A zi2OsJru!ACL@Pr-JHmX$1jWx?(Ym}(xNer{5z}b*k8r_X!w&wy<q@r6+VXQ7K`dWL zem8G7RT*iurobAumS4JILWw|B;j6lqFj_@Ar`+nGm`aW$Ni|I0kcP&ajj#L=lKjM} zO!;#56Dq?nh36EPl=D~EDRjL){W~<`OsfVo5zm2W{y)Om6yjc-w}jOuL<<ac%{ktt z!jE37sxv*JO@X5z{PUf&{c5XvgNrDw-pT!iCS=W{Bj=l!6Wj@&H=xV#nZ}kEut#|J z7Js+LDPkD858!CqNWP=~R4t}%`&c9JKT3?ujH|}xlKO5f-z~}f-EbSO{Ab9}Uz<Ni zFOPMPUOS!neIse^SB}eX+qHlHl`^K*B=Xx_mhZI~F|UllyjC(QV`Bhe2tLzXul(XP zG)}_(LbJgv{bWI3imeHT_EQekVUNX=!pSf9vq_+<)p?Vk9HcJopkDLV&xc;Wr%YiC zH*xD9kABORxQ?WR04Mkq(-KYj-_vBqU@b(+`u~Q5=p9x9x3gi^XoE`TL+h2_oQ#}j za;#EtHaSp`<O7n-D;<RpyR23e^1>Vu5GdM`>Srz8fBqP6uJ{X6KYz1c3F$Gl+p}7X zzX7Y>O!~|Ba+9G16>2&q?By!)C}}38wc!}d098T%?BRf7AD;*{%5x^p0^`|Q{GG0e z>_5-7G@~Sdm`UIA*}SWsf8UJ=Yqmz>2M!E)FY~Yb=T@Rh5};dX$!qGd;IDawA4c1Q zkN}U?$I_q3rT7df>QgwvIIQ9v(P;t?lc~lu>1?-xUPcq%kCVD)Ubd)8I7hdE;`PMG zMY(#(mYL92F<%Lo0$ZLn-IMO<*?b*=1!Uv@e;2Us9D@}fo_mp054nCn+d2#1{Z9V3 z$L%W=6uLLecM`#a50bnq-=^=>+HsjZCos=AC9W07ql_aL^<t?vEjTn8xVh+L7+&?< z0bq9P9V3U~wZVe+vNi8FW)a?N|Mmmcu1HwU>48+Q&6GlTi;)r>9crCHtDF*C-k`K) zh#Xv&0`NFsC{eBlD*=%mtPW^y83^^^A-|&Ny@3KCPZw8qQj=)m<`@h3G#|H&uK;9i zquaev!>O-(bP7@Xxl<Tt(08PweV>L6VWu+vU&bTvPTbFYA(d?>1=^&pS1dU1&%ABA z`?K<iWu#oCtP<Fy9W!8#N3vPaBY*#&@{Ql>xg|PUNxGVdOv)%74H8<L6we2E)*n6G zxzL2<^yeLNB9i`2@Yv_RK?l>KU}Py$za>J0nyREOXoXS*It4I$ig6W_NJVqE<6`;A zUhh?G=n!~D8eE;XdGrx1U7kF#*pdemV#~0T5{5a8W;m1jD2<!7OyD{YQ9b8vLi};2 zM=b{cW)2USN;!|*09@u*&9F%jrU2^94w7UlYs0fU(uiEb-Sr!oM91rTLF50yX^T|+ zgSB7@{h^3V%T}HklSYl^7Bts%FJFz%2S}g3fMrCe*g0WfSawRJ2T>~02P4Do!D&Qb zHKQV|lhJmDi~GU3x*F=<eq+_O6-p%{ywf=$Af=CBY+MjMGb@r{c(ss%cAly@4-(UR z-w?vEgjCKz0vVx}YlXL!hkk4sIJ6~{n3<hg?{6&ulay189-!UWLpX5W6CyELOepl- zh%aX5EgdRNVl>g0wD^6N=dr$guC?!JtB6DwZpRC}XEtiV!%kY3#kU7y3(_i>MHsyJ zv<al(x<r>AZ7Bx|y7Md>vD7f~reiq2u{;9X;!IaKGn|qNQ~J9}8OH1DPE;8y!r{MQ z`E>r@YlpS@e}Nyq7*qs|#45|RMVrx99`@Wfw#GFKRzf~4+UADM<OcCnR*S9xky$C= zPW;jRVd?PnWsfSFqP%DH9>t40iFw{nIV_w<a!(he4v=pHhT0AVy%^<JW~KRRuh{2e z7<3~(_mMsEd9Xrgz`MD#-_;&4Nu$pvcqiRMxz*R_EQQe|p+E!8lJmTa8hpv<7eGZ@ z>*Yauf4ne_7?yz9W27QGu0Tzigz|e=;?p!x)zJd{=T47Pvv<lRWgtWA_)o=}rn8+e zYH~O8jAGP3v=oa1#%QzGW3hjHcBsfn4*<N(35C4jkkj<xc3k=dH{DD9rQlA0vajP6 zD7dep9t%MQzId1ufJhD3@Rnm3TvrEdH<n<cMT|wnxdGaK;nXQLIE0LqftI9Sk_2xN z$%rN4C=cL;VkbfpzE|DF<AgRXmdBjOQ5%~}1msW(HGZ0}+2n_#e>G@yrmyF~G@lFW zwD3Ekq?Wo>)%e0Y*HhF!C*unA<!p-;w=z1$gwo(;uDMoxLz3oxPsZaqlX?lL0#v7s z53859p8F5YT+7e<&Mg@9zYVlg;he$}s0^WZtDLZ1h5kQ{MMO!Trz?NhTaxHTf1_pe z-Xv(*yy<L=Ue9m@yx+;W(M(&s`}-68+p5OLJC;-$Ena!+S#=D1DNzA}J=Qo#k&zXf z&p3i`N?3YQrn?}~uCbDevZk6&uCV!7t)8%u(bt@~otQ?jnbh=*4K%YCb!4R~{D^>3 z0Yc@zWUOKqlFdb9dABG!7PqhS?<t_y!_%Y|*vDH{lm|W)&dg5SP?qOgn~R!L=+NYn ziGv>7hSmfuWoD<m+^>noNGl3W<>HMsP};eN-}JbfIXu!ep5vH0g4ObUjFGJnzmt5% zr$S9N%Kj5j!Eb<j)gN}bn(C6%25uFx`bLr@h*MMZ1`(SXGcdvjmY_`NQWkUB9yWR9 zY_RZEyDAi5eUWc-oa>VyCGj*kyjgjST{UPk&`JcJ-rP`ym6oCV{HjG#DYT_HQ3zAu z<2$1N(d}Dhpq;JXc{p-$x^!(B-05DX-bX+0aNM^1@e9Pq2V73)gf)koGq6V6=GRD~ zB;M5v0(G8DRVp;`-y)ecOY%#^pM&Xp>xUgP7?rDUe6U*<AhWa}lX3?oIPjRl+u3kT z3e~aiyh2Ebs8k+r1;h%LIOwxGG=+-`oW)q)hzT)Kdxb?jNC>H$7TZqB9AqOHK8p2R zEc(~Qw`Be&12#9+hJn4(Ci>7#Z^66B_0vyI4<vhQr$;MD!3j)F`!q=SqVc`_sA?Zh zz(V1|zi`xL*LliB0+rAY<!<}I_htIv_3ZHTVBaffH;FQ-h^>2Q9&7IRzi@kO+&w<; zIv@RUxt{kEY&ilk{%jro__Ie#hViHAyY4Wek;?pDo|NB{mjgFv>$arV<K`yy8ekt? zv%Wcj$ov|%-<M>WaF6m?8dpCxPwpl4P%r1eE0x)zjD<5FSUe<#^OHaHvSQ9Ss_c`W z(N7f?-~usJ5uqf?*@2-s{M>Y+fRc3=A?AXfiA+o)vcefdAuS3C7m2d9bk6@khOHP= za|36o*4*3<#UCGDrP51+YOZzO<KazSCW(*ODV)?YJ|%wS>lnz7BoQmW!S5UvH`)+B z(7vDMVM=;ygzbNiZngT$6a66p>I+{oyE|!Z3uMD!?Url|9DWIhXK+_IcV4y}JGH#o zg*`^_(j(9O{L)r;P~gtR|7)OJzKfLpq#*Rgr=9fvvsMl5)c&p#wk3=zFolbdUKmpu z5Pj7v#&Tf`d7Z*FY!q?7%YMN!nvLH7@|2C3PE&vS_xbPl*PPgrpnoF#*2fGUw`s=) zi5|}?jh>e?qy<nM_^gi_f|a`du*8=23@7>XJvAqf3mPtEs<3#jO2h&4SSZgJ5T*ff zo2|N6v^?H8$uG4b6gjOLA+BHuSspFD5Z4Pe8vHsHp1fPs{IPH`Xc9dy?my3g6k=w9 zb=YL(=kThBAn^3nM?O^qprXGD)rM>Szxhwo4sA(Xud1)t1@~TLf{KdzDhg<1G6ibI z`0$Bs{v1q)gMm48273l>MK$9BzOxcY<EJGaq|fq)BmmYk%V%^n@nQKb&ifBGj|OpK z7x-o(AyqNZew`@q2h9S`6{RBmIB#WGXg2z?CS$-CBjY)L*6Ys(6a`g#+c`eNCx-m- z80`dS0I_nN!V!qpGiP<CDzq`M1)J3lf&FbKjT&vs3Q7?L>5u;8dCD<j73h)ukP`Cq zi6Ul}y0m3Q<?&g<-Jlsw0~}9A*VDS&%-b#};MmY-%*T1W96Xo3=~zixD-BK}2!2um zQ`O{kI;#z2I=&A6eVOY{(2WyVE*a$a^kwOBNnv!lSY30#%8s9OO)iY^-LBkDkJ_#^ zpLg;^KOJ8)RO1H8nwuHKGwa2n9#;G<5tm~p;F<Af0!d_iCmv2vUyM{j8|C)VrR#I; zW=Z9R7#4{26s-yrnl@MS12PJ#Q?deadNMr?s!?PvbXz&SmZ*tF$vV_K)*O<h!N3zT zIX{I}c~tJ5`O?m|-&o;Bw2CU)t1%D?{}OyK>y44qpd~tdKiCQ1?h`%&kQZaZ2+<T# z+cg<4^Ha@R5Tn`nTI64)VmZfU&Gk2OA9<!BhfoQF-N_|7MO*%H<@axog}cE>11(zR z9#Cxt-DDlY_vPo>z0p2`jS%!zw_ig9CKAENxl;t?(0NYDf7%<bB|(uBZvs5S*2&bM ze<Y7(*Rqc*HrSz4-wEKsg`&07zBj*MI>|&O#-*_0w#cWIH%Yq*Q?rCqY!Fp03KJ1m ze~<RY6ZF`jtve@2)X_8Gb;JJ)@L>z14W&y?F=s(q)ZMH5E%%jP@M$wN(4M`D#MBvY z(ljV*>4Yr1;GNDt`M6jA=!frk_pm{T7s8eG`z*Th*CWtEw0gS@YH>q}hr5K%eV=^i zA}(8qWajITAW{6hkU)2?`W5=m*EQ<F|AbOR>B3}^7rq^P+UzLvRqEbsyH_>><AT-s zklDz2vWa#1b@}|1KomIv4=*B*!=TZ4-1)FlBFYw1SaB0QJg#pE{09E4>z?wt^2YB+ zVm9mTyPAi^tbduYNF;$>v$T?%MjT~bGYXz*mm^s8wCOp1dQ_>FdSw-ZTAovbM9XK9 z3gX7Re!E7L1L6I}XTdX=w%A;6XjPSC#N>h_>v8`~m*ydFI-hBBXPb(=G?D5|Nys;b zJx-lSL^z0gvfdOPe>;YX!-)JeEDfC6YyU||e-|}Idl`pzVw$b9|4*ODVEPT}zsji6 zla$}YS<5B7kyZPYc;*{X#;>uSljFV=6<#aw{bDTmovrRfpEw&YtlUL$|2X{z@_wKA zVg{OE<mV7|VF6Db&<4HS<&M4zCTWp|OJ_)*v6z2F7$!Y&ol*jx?vxB+3_T_`Yi-4h z%9%s)A=+zW1UR>n$4tP>XV}{yyqw=aQ0p6mFDUu2_BHFZEAPB`Hz~0DnfS*`2X=YE zjCh*Ld+H2z{iT?oqm(&*98>B5QvH#xG6UF;GoDlT;m?Pr-(J>i%bz>_t|`#1bZh=- z>Qwb~iKTEnRNqWUo5*1~iSy|5@c$wHtWM`@qz@TLN?$HA+!g03K(47(lI=;bK`-*$ zhF<yomrqHvj6LP|&7Y4ST|Ih7ET4~t7`&dh!o4?#T{WDsk_hgkcKj02ra#?m;wQ0S z>&GATvE3W~dLZbq$G;<o0SR=fvIJYakzVd1T;D`>>ysnm-#Gf0EPd?D$yeY}ePe(- zOH#&vpOYV)!Rg!$&UHc69|o7h37NwUB-?G%_ea;6Oph>Rs?VA@&MGtVxDB_g_(dd$ zP^(i)c1ih!RWup4>rv21@RX<d<-Olv3d<2mq)$=lD4n*?F+x$>`L>lK&oX_UEFaGs zh2}r$Ar|`^)SP&#vu}JXJP^OEmZ1)F@vrV57~UH{W!r@y{=<AgP%ZSx_wlWAjC0WP z>r%T-=Zy3v3=#QJWkn+sRJH!Qci|X9-|dTUr(+O$5!EjmAoDOOCLm$V8ZXp+N4-wy zinT_Lm@19w??H|6M5%8wjOwIxoaR$Q&h$<WTP8|x^;ljzYl@tHmm;hS>FFU|O^Z6< z+ZfVJaui~lGzE91gKyb;5z|1@_L<uDCZ&&ZJz~1|9kqPQcV>O#+;{76&5|B=SEg*c zPosOuAK)HV2S?<O=fhh<@ctfYq?vP4(BHdu5%y`#bMuFC((g)y?jz~Z#1I_^lyy2$ zx2sn#=g{xwZ)+g1WcRqiIIaQp2Yn@Rf-P4dtH~W`IJc+iDw`WStAL$VhwqVHpHleO zCWx(Y;yVg*8OGb#3p+NdhNE%N=y}DQ=z&v5v~^qloI3Kg#VEKA6(Gqbno0vz6!;s+ z%YUfRpNZ=pVr_~Rcl6j)^)UBqN%JEqQ0bDp7a-xsmIgx!VWb%M^f2=rB>Kgmv2L=J zl=**At>y5Ov$77xUiCZ*I;^c$uF%?8qu8yOGTQz&0(`(@>XG1#+W$G-M5$&+@*sax z0>ik+^uGpv7c0`yH~sy7xOOf2-)<6AZ}OH0{uD+L#=9&sI__}gIMt~dAmV%|U5JZ? zXqpxk5&eZDV6@ou3Kftk3ahBH==mrv_`>0{EpEho&XgSCl-D&2EkQ|*e`ua|9d_U} zgTkFE*g*{SRwH?8_BW)1%6cIyP-fxw2S9#IH!L3H`ZcjeqH1sxKXPqh>$uORsT@;7 zPZBO>R+q=jocU8CWaSn{|DA4p3wpjf_I5gMC4KH_vR!W7sJP~<BE*gi;GBrA*L<&U zS`bt~6K{R!T4t*!41urfb#i(_>6lzNU2gaj4<UJOcVc+C+)Ms@<@z?7&$wdg>+DN_ zy`{oRu4=md0{3=*cPs^E1XY;5kW#GjOn9JuzDKqqA%b=);yOVB^g9Y2YJfdB%^*Us z^VsXW$iIldU!=5Uk-I-y`GtKu3TNGrw>e4ed&<KL>6mNUJI-CF*Q|{*fcGtnR^et5 zM)%amYl1C<!Sq5)RLNSwiv~ns6AGrAN}~3uK2T#5&=6L$FGGxQKegeT?i}-N3vIKP ztVzxbUYJ{ob+TgxKMzxGE>4=FtcYr&A!n1Je|x8v_=VfKA1rDoby@o<S9XE|o?Th^ z49O7%j(K0a`g7aDeZR@HLSV}VTiSVPS`4CQp$#VMd|1kz3@&m&*mz9ZxX-Rr`K)lh zOuON(2bhXwC4K6GIq$j*^R??E1EO`U$v-V&$7%)TZ4%2(%$E_Fr5aD|_hE3J1skoZ zQyp|RPQ{~<f?2U*h%{0=L7KsF`vs5jwb1ct(mHzW?;YFHH`4i<7oOTgqWkArPQt<G zvI!}C(tvk#-f{cOz5}1wUj;D@8nw+WhGv+*KTq3#TQ9YeYCHWM%8)nAt#8o%d6m!B z7u89vd$e42TXtyq@+*dv?Vg(*ginNpJ!m{1#9~Sq#k*)4cOZr43`aQ}&RsuqypM$K z^h|3ByjUyLT!k}Xl#oc&69)V{9t2czC_3cyH0LQbCEYXC*ji*-n(PJ5i7{Y?pQjkD zg-wE`ED<Gh)VTu2c!0G}j{bXC@+tc@fFuFGg93p1NE7B=>0&(bV%itV4nJBOJK_iY zVY59Gi74y%9}mSu&8sGBq=dr&K|K=_0tg8}Z{QTCM2g9#<&S<pQQk^pulXXZ&cJ?H z2ntRr5=6opJp4hB*mC+y?|4KJ;!4EKrXkiiKGIZu367lvo3|g&7<w%m*DyG~eo%Yh z_Ln@mhj!z(8O~UDE3lOazJ&Gw5ieb-n6|2Q7AuQ~{VDgV{-pTWJ!pOke0?*^jFdq+ zvW!PI-*tG7f22n;wV$H^W*lzKP<428^_}$Tk|Fu`bte^|iwZtCK7F=-bZi`T%1Rl} z>Ep5HFiPMacrtpku|4yL4twFNC!8>gU;mXHB^&Kq#)A}A&Ek|x6v$#q_k)dyXtis+ zR|M3oOg!_CFu=d|_hF@II}B$;WH6w;ZpwTALrB#{%8&pIFp_U}owOPRkIx8L!X<v$ zC_LdVGfxZ0#iE@^B+JYVzAGmv)|yE3D@uOqEsiZWW$6D@cx_M7%Z|rISz<oSl4cfm zrfP(Czo7v%ngTx>qP-jp4wxgh19}`oD4?BonAe-p>RrG;zZoYZA<b7j=Ojbz`}B9< z@-YX=Gq>Wbhkif7Ust2iQ36+8GupG(uFid#Fd7w_t{v|#vFVbY6R|!qrJ<zlQp0Gn zAiT1uMBNH`b6?L1;f~sa^ojK~em!TXuUs}oGjiL^5`X(Mshf#P>X@IUoc=8Wo)^26 zd!R)bDm9eT!Ld_q+=45Lcbz6K0n_d+Bc%w&r40fIKL;o)J;w{P<+tSGw4ID5k6~|P zZ@vLfNKWzvorW;FF<AZAPlg6^BYG!^3eiFp28vf<MDCCO0MDE#Om;<2(`-e+)W7DV z4FceMKi=B1-1vsZ-#I!v5jcbSh!z1b>V}h6<X6J7O{D&ePG}}Wrs%L3#UdHsK|2dW z0Pz(c0qe6huX&<>t=E?SuA&3o#_1PdJ&#7f`F8A@a5%6MHOFy0pEAm*dKQ~7E;qyU za~j{byJ-E$Pky)g<dSZGC3mx){^WbRj<k~AcQkqHwi#ac>i!yutWBB<K*EpCzc;-s zidzH!#4gbkAE8?*n^8!64Huv`(JC3r?D|ku^Wp-frgvS@Z*_S3_;3USg7Rx_Uj(!C zZ`kk{J9A=l|Bk}9&=XaTIDkxZnMs3trF)f<zyZRv*qXtnscOx<XO0DC3i;Jv`Vvxg z3;8$FPXN&28#8b++6>i}x2!>mRDdO)UX)j=ms{T>X7{rN^i&m(!`R$7dfP8~|FolN z@j`nAk(;_T_$LmN1|p00A8IL^KPqsC3}AdIQT{$<gltF7J|@hxvt6?NgZ4AU?(GHP ziTjW8EAs5ijS2mpP~$!*;UWQP9;S|sgHMH#)u7Gq5^Shy(mqD`bBL+p;{6ONSm%`M z>Sz23aPpGekP#lH93z2kD=WN5k`{9ZE%r8`ifBbCyI4}sm3noe>WcJtBQgVLGJieH zZL@tW#u(N4&ks?@@+`BRu3K~Vi$x05g0qG<+j|nS>m!K&q%@q~P1KtkK1B)npDrI( zj8wR6rhY$N3Q%9(Ocl_{+$j@Y3oLJ9<Z}w3MA;7397ydi6W#loE>lv3t$EL@T?($l zie)LnG92t*F67}WS(?D+QrKz*m+_nTgX_*QTPGsS$L>g~4M7YeBtfVY<=3%SQP&|9 z9=6nfX%$#(er28I=@!0y<#P22ALk-t=6_dIPSqi#&>h1>p}onsZt}(dRH!;%oL7!) z9r0RNOf)Yl`t{#&rAT8k6>ky;trj9Y^tnCYsL{AA7w}ik<b;9dMBw=Lsjp(1JZapk z>mVREZX};tlOpLT(Yn(s8qZ0!9HF5RJwhER&WJoXP!285&LYBs0f}+c%>@&Ft??`k za%Zz_<o(+E(!Vgz4Eza6M%O(*Ub-11FSn>WjBg7{<x(|Y{4OWe0!l2kEn|L+at?4+ z>;>&1%dYiH^UGT|TKN(!0%)({`%WaYk-ze!`d3l1r`@tnAN5<jHbj>IA33@(e%wf| ziJnc{Qr_|)-B{d7?4LoOclf_}mBa3Kv(n8F^yl$u7roQbYrZzOM{G75uh;4FFj=tf zZpJM7Wnb?1DjDUQlRCly%AXTT)Fy9mTICCHX3%=%3L?gYnT-s8eTCsz>-v30Gn#<X zf}aD(DKzOTYt8o6g4V!Ms@u_D&?ynwWAsa^KOsSx_2s+%gYPZd<R6)SbRQGfXOp7| z^A1e}V0}e*;$UhjazMi+p6ysiEe^c{q0Y?#h05G~c2+5x{Nc-a7l_<I&MI@n$^7kI z^`~(=qsP}yWezQA&l1SuhI_Y8LIu*wAbN)mG#(EaLw3_bAyz>%TV!f@ZF^soee@JN zW{O1Q6caK+M5x0@?yS#!eFq=SbG&hqKW1a8Y|?n=<YQ^{vXL9UdOd>+cwa6jY31lv z%YhBi8|N%~Rdui^HA;KGK-M6QovXa1A`N0%O#Y5TSclw<7}Z~+uy>6q_Van%C#$#1 zQ)4Ik&G!r9=+<2n?x(FcnGJd_OVx#2C1vjR*{5VLSfY-p1ylpWakcdQCNS;tsoYSC zC6~-m7zS~bkeXSHT5c+FU0g^}yP|DwMbii@Ffh7A&m&=HO+DO5KVhQXbN@Wi`tm?< zhjSK*@q?8xZqYJGhy23%E%cFOJ!H`mS$hM$RMd%+o9D>r&F<))k>dQq1$!NIhFhYF zuX8?!NV7QZM`CYHphx%sqf<jsH_4u<%BLS$|0n{)v@ou**zeg;<2ydCvR6BQ`{r@# zp@CtrRcp2F{M*&1<}Osi)=P5s!r!aiNW`n*hT~_F-%hDl@#zg*`y>N!!y4Qjuw#(8 z;zCY;!*A?!)*Yp!yn}$+$2X4)e^7Zi(94ZGmf^u+X=6^^mz4|SreSy)F0SxO7OwXs z2g{R=%uSI)4IvapS~r5Uwd4;ENuDU*XQ<y2XWZ`|s9H2lUpC!LXgg%vBkIjiHzHY& z{fyr&tgT_MYnIx~N;CWFzI|0q`Z&JKmELg1B>;JBu4vkO(kAUJ8AagYAJT3z=P#&h zsWp#Zq%OvE9pbVpnj!Nc7iGpA(L&8{oB;LHFB^!57a5^SL8CT%EoD|CH0ZHD@6+iw zpEdx1Y|Yzh9N1elo#<Z(4;~2MhJ!cb74mnyXn!?~=%NC0j1;On-XVkz?ysw8Nhq>H zvWePFHz|o(_TJ>mN;qoDmohB_cm~M&VQWP}P!}$rwl<Wr%mvh=5GeQdiEKT5I&5aq zL`6yVJ<|cBOh8YCQ^TRX1vrtS2AZ-|#mC4tEcB<X9*fEsExJ81aXb&5CxQz8aYdbR z*9iJS){^Lu1ViGV|E%C^Db>IE#4%A29}rT9&39?*n!`xIr>B~wnvfUONl>IkE!;jN zpiGX49oPO=0Q888N(Riv$19pIxxAZFHe_bxv7X1>sGUe2PJuivZ2f)=<TLXy8v&rr zZcx381yZ<Gbt+RFi*|6kt9ATzNDx5=o+85@Ltl(+z@TFe<e%3fQg={WBs>w`T&_&J zZ7(!~t8AY85#jK8J1hBRI~ecB!UEh=sBn`MX1q;g#vyM4J2ukjpc{zwR7ITn7sw<d z)Ksgy|4g=<mSWv7&0SD<Dv7m_ys-S7=KQl;3VX9U!1^z&503Ge!y-#5u20u$FOq>) zxJ#sX?O5%YW&{HFvZdVm&fdq0+Xf=%>v6F|+oPyyiK0%x^2nLLS~hq6#wx8=7p1?2 zk9stxi>Zjxq`Cz5;Rk}qVX}u`_sxEUNITx5XhA~JxD;y4pUfVbAyek>x?-u4z-6<D zsjhHBfrH2J;f*<a8fT6Hm)<E;)P<ROp7TM~eDhVVTfxkBM$RL!t~dgcUr70!dx<}P zzf6k$_}n#g+PP&sPwoULap$nu1$jGP$&M-w-*Mq#W=BRWu;yr%X0vlyq_`?Z<#xIj z+sh4WMG#Zgnh)DDulY3Kz6>NMy-XG+wcK=F>K~A*ce}Ib&=Y+$RQ*EWY<Z+JSKf)_ zsXK<0Q2|9c7KrmXqn<YmtC>}r!3364?h@Zm1S4Eukrx0xeHLY`<hKGU)3-Fcs3Sk- zOFv1z{FLHd1b#7JhO+KRGNh<Yb(eiObMmA`GExDGub85toWA|rOw&G}c4G|Qn!l1+ zo+=He&YT!pS+AYdD7X9})gZqTi5mPUwCD8Ma;WnAE)Duwl9rSk{+Q7jlz_tTcJI2% zg$nAJYc3Xg0B80YLaKe$W_kMe=t=VTx0l^VuA{}XVZAbFr(~<0)+Q^fNGu*n*+?4- zwW;{OlBdh&0|AZd(nX)(WPF|m)3eG0OyQev|Ebmk3i;~>lbN)l^^t9xUH%}MZM{b} zwz8;i$z_N~9-)fE>10KA%*krZR>2OXjVCJ=`tlqv|Kf+uVi;A0<dVb@xc{LVZgo<% z={<y`{tYB^*Au;Vp}CAdX0V_joCn%g6tfVX;Sjg(=Y&}y{@szEiqav7*Dmi5R-0yY zMOs!#N+~e9DZhxuvi2B>3rcYPz?W&Q;1Fje%Rd2bnJ7gX1|*LJKqmrPBTx_tVI8!! zczxMX>)7X{U7$1432Do9*J@90Wt$ihJ~*nXxWWk^-UD^$@yqVrZI`#nABprEG3DNx z{)4Hojq(i#RtX;O4@R;9po=Ju40JARDWwoggk`{XsL-^mg6lk`0i1rP7h?8ky52u6 zXarnkg!-+n|4o?~?}}g8=wyBBR!+Rh%?K3=$jjOPxU0_1+S6IuR~3IO@8$A5=IwC) z(zLq@3@4v2+LI#3jzoicHGBIq<~w;$9ioz)b7(M1Ix0}HFmzvJNG?j5C)?%UurD4* z+q?`vNv^$IKLR~d4Ou|`?zLx%Wvb7`de%z}XCc;^VE+?`QF3F{))N*eCGk4_4S5)E zEVfW;3za+;i!B3zVu_S|0Cqtms%%MLCG7|x8xagQSGKtSX(|sH+s7N$C%zo*{9kkl zkTwOPq4ohkRP!m8%gmx_m7B;2C?~&7Bj+EzM)(CWu|;_&OXf&EhmVS*=P=k?z4Ueb zxEK@$x%Z_p0bJ~q*MDVwIuV}q2jT!wB(J~Xb;6$G8DAwPyd1x?)Zc-Zww9kn?MxI8 zQqWwCD<<2|C>{k@pLPI4BtHVUO34dr2cL-G@UyiR;q670bkv1^C_d*~sCK}Bzs{N( zbx{<g*qx+w4&&sD4|8#Be5dWch{*Zk-Dmo3<Y6?ea_t`H=F!dN(B?=e&?<6(XpF*{ zlO<=z#1`YbP*JAZLyf5x<xxm)M=vG-cq3H)0_e-n&cW)-Z9B%{dZWpH=Y^^6L*?EY zy>*Rw$Tpk(bK{bo)v2l{lCemk0ef$l{+NeJv&O%9u%aw7W2Zz)Bt?o7g^NmotWQu; z6p$C1T1w@c>+&OH^kud1sj#T{hJjmaPb_*YP{}vceJMRR&T-XR^Rqg%(Q&!USRLU6 zp)ec#1v#wqGl|OSO?K01b(}JsQ>NaX5B!TWa5AT?EfRm7rY+p#Dsao`J_V#*s9C{< z@*K}SIW3-oo}CuIum!ujHhxk6b@=w1+c{?RQa!Dp)i$|yUlEEwKKH;gTho**Jduh? zSLz#cEgMUUrJ=)aNbh4K$i0hRCqVA@HJXdD7WW>^x@J@s#gt^FgWW&|&j^*T>J*#~ zAEU7Se&{>bU9U}!oJD7hutIAiU(RO`fAA$_v0v6KqV-_d)mFfJ!`A!e2F%6pGweJQ zr0bGf@!M;cIm36ouYjwD?{Sfo<pUZ2mnj){?Z(gCWdexLhOzLOaLs>KznFP@AA2`% zGPAD$$8z_Di0a&`awzyuTuM_>i0r|nL1B&!VSiE-wPu`xnpP<bK0Mm*!~1*8|1osw z7ee*;D7{GM5D?x(zpF4q)-C4&S%^B?aeC6cd}stc{^huSoRXmkn8gMu3afo7A!y1i z04x<t;$sfr>;gnRT`Qq+_UBEbFoNq4U~puME12Y9r=?0H<`<p&QQ+?J*RB};jLYbe z?R6haAMfl?Pn_|VH)AV4T}S99LIc3<_k8maDKMCb_n>EeRe~m7F=mgZ@c4WbZul0T zYFTVHW$O<HZ9pXp&FD$WS%?NSl_J%Ka$B}axWgjEPY{{PBVqTQ{}M`~9*a*%C@kk7 zrM(7)9*&(BwCq%e4oddj!5wzORMJ{wJN^E|<Gatd9_3m;=;Q}n%IvheFD#N4Q=yTu zV7r511D>9qdg9fuWWLt|iz6t|Ar!UIP<@SO6`jd>5yrN=esYgcp?&}7T++7D`?6L3 zq&w2-B<x3O=ETDANiP|QE8}ExmnnqPo4%5LY7uZQ4gK}YrtSdcZ|loD-~?k@`O<$E zJ0-$Kp6#^fxRyE4Tf;l4gJ(Cq&vHCC|HVG@Yg`Z6Kv<_i|JQW12-Y?&njd@nc3)J) z3TAbR)1E_nfSLzX0AEE)+!>DJE{OHDMjYUbOr_mrlFQ}Z74JlPKLS5TVcO5K7=rk{ z=&e=x3kgQ1<q&O#3OzF4Jy0{fxJza{fFAO7plQ+&RLYclEUhKGG#)fA@e3p&oEZ#k zn1vbM^XyiBVGQ&qu-m(~4%&OOcenu)<jTc<(#HW?N4vya*E)fn7V3X~FiVuP(_x_x z%o!_$MituzeHBwkm_-B<>;=%!tXi=@$ZhtjRM3I=STG>DXF8X@)|mq<c*uJkSsInf z<Gk&-9OQ3%)t$~Ra(58=P;iaR-PT5bDtCwe*pQQ4N9VD`OHf?rhJU9g$B(qGS9&Ja zjb(^Q@uN5a5j}iqGnj(vatf+(!QO1xb>!&0L9cdOSH1l1$*(Wz^UwF_LM&lMyi0J} zk$p*bGb6XW0=S1VG-0+U#bb%<RNiMZ%wsulXSlLxRZxqn4T9(2<|@UoUj=nNuikWH zGr)`~x9Xyd{?dz%5bE@Wm@@`^LUF7K$0(tN#1qvZLgt{-%8nIRc{O6GiuRd?k+nmV zXo;0^%Kk*nC?I)xgP)9|FKcBEko7X1kyaI*x@$3?6V4X!h}2q2u9u%`+d^iL=y75u zP1JtYeaMAY+R5V(EAW%2?GI_{ERiv{Q&T7@eh;NvLMc}qMW~dz90T#!Gp)x$_XMxQ z^1g;eANJ|gfr;B02#bD$JfKugksoOQ)nly4*Cwolq|kkt9M%-C(dx*gL3}^f=1F9~ z3IN1VZn!Z3zKRoLqu)Xd1fVUKrwnj6^+@swuI#p@t7O_Yq?XkN45sCPC#AN$R$1g* zH?yUUlC{a?{RG7wN0tYikEMr4&lpro?#2kIZU{$?nWs0C^Ut24Dqy#c;<t%q!WQH+ zI$>C$!%?V?mm%%kB?;LTOjL2(TZMV`l@L1G>Y!!K=Kjub+>!zJBfDSyvB_q{TI@QT znrF&z;`yE>$$ts;og*Q7ism#okF>Za^egrto2)U6*8N%kREh;8H4#93a$Xe~#f;;w zbhDJH-nCBEOWjARD+)gG=m;19yMRzj`k@WUQ9CA~Xw8{ks2UpEx@oVvt;4zj#e~SL zNzYedk{m+8LYzbN)6<ni&A`XUX)0NtKo?h4fbg3qA!Lzm_0|Ar4~YE|h4SS?f@>Rm zDaNgb70L(Z!F?p=P#48^7J?5D2ul5Dk*_IJ1=jf)N)}7I)a8ejd8d;g!>!A-<62Bg zt9kql)IH|Azp}|AcccCdWs6V!vnv|GK1ku<@V6;8e3-uvlR?KI_RC%ny(glj^P)tn z3db}aP?9Owa_krD1oUD5Z1Aw!h|XZyqtaMr1$vabcQh$$1q25qo6qQ!ugJnh2-hbK zj+4{T!k{&-yilb+7{r67A=q$|-H1xRbkT7s|As(+VW*b>?hPaA{Bcu|Yf9K#Pv0v_ zYg)J;8(EeC$7}XelCizFVnUdRH{8ahy_2EiW3-`A>ZhLc#FBJp;2Jzkmw#V_Cx?jc zYy5w1p~7==*KG)`l~aq|l^@uVsh~QB*q8(N0|r0^uoa&#V53ro{J#%S&Qk5hkVXlF zq=sQq8xvFbEp3|p>*51^=#XXW8Vx#vh<6|GWQC~>k9`3-DtqrhbX|(V{8$I>o{kvw zkepA2`&ja2W50z3qOp;%4bjHM9gs_5Q^VUj^Vm)%?=FoxIj^3};(KWj{MPr{?-#zu zf06OXxpKKZTHbs^(P%1SNnl)>nuYuxs25NTfF+6Ta{4pl_2&o=uR;*0G&}uu1Y-Yx zsQL%)%$l|f6b(BzI=0cVZ98{t+g8U;$F^<TwrzBbj=k^ad(Sv~?0>M<s8v;SO<j<{ zJS3@sMQ+_%-#l}H@Xw{_TxFz&W7_l;YN1ent1(#*GsFZWq{qhKT%YNxqdhKFOPk8g z`NZ*AOVf->0jVpW&h`Zxt1{__^=Uh4imb6EtBPchWeZ_fV#1!4GAa*YYx4k#Kg`uV z@sdl0x{M-_-8GdBe^p(Yyd({QeOkcrZlP@-ndAaQ{}$M@M2Ip?F*a0NN~2pS&vIzU zK}D$UczgkIf2m7#_tZ@GhmP!iI!V7>DcXD|)~QD@icr<*sxXEWv+l-{_lehUNuz2* zM1H`&QWdtK*0#mVv}DU%Qz9oxWng0P&P99ckv~^J01Jy9{)s<j86-8``?LwzE~rVp z*C80YTPTq8Q5ktQTK9%6{6l`^S%1sAVx5YgC;i+U1)h;x`Y%VeH{Wj)w07Qj=>$9J z!*S813EJHTlUZ@2Cv|h;CD{3Lmbkx(2DSFW1t7?)IQrCdS9iPjfB(M!+I^Y20_L7p zZA$`l&3!XpCL71hxMJ5xzp&mWXN7F7uJTZOPay}#4Ph+6P7jh2U0C_Xee=AQw-j`g zX|vMslq%AM<_UR1iB5dO!IxtyZy&axciI`Za#tYt3x<8}DLy6TcVgM_LAU2(|4%{a zB82?Uin@TPm<OHnU$0K|=LPT#1qSYGZ!avvfb-A<O@a4f;ARNUNz;ZF&==g?gkTG8 z8+>7cev~(5$pqk9cNK@r2IkHQLg8i{`A|g5bjX9@0sw^=|CqkW#0unl2rwA4ObhJu zsf-S+6=lJajIA^Zqt9DVpvUT|%j~8C9r#}^nml~$k8aNjKW`MY{bJLW2cXY(2Leus zxx|L$Xj}9O4rB(~g9Zh)*eq#xTil#r)U-SjGMzuk`I0%x-CE!BT$JQ)L>_=?rrrv6 zueMz^U~AO@0$44#HNnpF+fRK>$CKxkJud34KciL<cCW>rg$u4I{yx&|lkj>Qd$E`S z;=-LkL6NWBn87W(`??0<ui@Ec^YhpFu*~h8j;zskt$|+YV`@Y3O;f+fs>52itj#`W zDW?;snMrgAKdSt^N_=`+_@FYz{k~GPd55ZdGzF1DXUJxWF!no+1Zm7cT9bGhdSdQk zq<Vj%0r!aWjl+Mg)@mmU2zd?$`{{unlm`5b=%VD{yLl!gtL4ExX!gyu;GELyr0A10 znf_7H<sx}Yj{P-#Cqw~<u0V>U*8jhC(Q8;f2ExQmvCYJ$l$Lyi{1}JqXkgEaTjWzI ze&_GSsvT>j3cCXf)p<?4K#-fUwHVxQ40ngE?o7sYasiXmIAur$5aT$dfTNmp|6p&E z`KHF2(5|9!syr=VajZx>%(APR7Yrv}j!qHxiIk9_8L-dNwsMZM=TTDokb|TAsk}TX z990;GaNr|iUp<DTeDI%=j~=)4CD8Ep3}nAQUe*SIS@_{c*`mO-;co%&8&LCArR`WK zM)v$+WMl9E5aJ|cq9j48fH`3zWUz<`7<zQ@5-bx8nHHP&iSEOlPI_aR6w}EBH}y*0 z#~mPPt3LVlEzo;2<$m41+wQ!(=&fzCzVq2NyL~^C#r{{8%l>++{qz##c0NBt%J|rk zh@?hzMV{Aot`erGzX=-v;?F@%l;7>OG=X-L@w(!y{q^xsXStGV<3EU?^X|e?NUA__ zCBUh6dr8X05cR<(A$=2m)aeHd)%NkaVFVvRP^hiJ{VS{$7AGdhghlWKC&#raiI2wi z{(HlRTuGyqqnW08EOLfjNF$jfLeFa)UMM!SniPFLD7`_-o1F6atg4CyETb(q>sy51 zJM$z~jET<{E<~9E_L-><r@YAFn7{4>)i@*BjGb{m6Df>4m-5!el@!{ti-x!W-L&_N zsb`&zY^B3WjGQbqo8ECTgQvZxEgMnbRS=gqkr8jMX+c?8vZ|~(WTXlau-K|_#b;&0 zU<Z#lSeBu^T9!f-J)tD!I7}giXp+aS`mcA%uyhgT3+IW<9o)EaEUN1Y7wnD{zoD7F z=HiaF+jPx60nifYJQ2-o{%L*NbWfWSj>lTZwBGCoFy+1N2JpZhU|&z024AP^60yvu zgE59lTolA0>})a%M4^m@0(s#I0IV_XaKf~ACZ_H1%DMo&*6pKq$>;X4vHm7hHI=FW z&sH97X*Hu;$kuo(RiBO6M(28DLsscqI~0zu%j^H1VVwvswmbixCIRKHQHm8BP7WGV z0zB3|AhDJvg^9i&LL70=uFB$BQ*Q$v`XYRO#{IBl00}8{T*2GrSZZGew*YDLCP<iW zgJLc?F5BF*`+l_5uSMaDfT>B3g0Z=b3R)EtT?kPOSS!Elq=om}%SFL`z))QtS3wYO zf|WF3o&jf$IYWNZ(s8!+Iq$U^uGMC%o{;I-zoegh>J)sZ@q68M^ZD_NI~UUs_FnZ^ zUz{lXgwsbZ+!(B+Ijy=44qtzFt@ZUYHTreB@cveu-UgT<ryPVx(?`i>HNoM$==z~r zAV9XG6{v&>oD_zu(GZ*7VK(}ef6xAm-|>>DuE2YJsZ@Iu733({fX|DI!{r<_)y>^l zIf#^vRHyK2ef~rQJjm|Y=HjA0XWlF!i5)L}ypf+&#r~i$!&28PT8v>)9g2&ML~nm` zCS*Om_!aiJ|9y~>X>AYrs){c{8Ja@?6FpB4nr)_A+xk8u*ZsSAB10Zh&jF!SP>2$p z5j<xBK|L1|jUYSDn<d6#uLO!FT~D%)8I_QgqB{#_%h&X)F9QnXs7WQo=JR^_P~qjw zKaru$z7Gw7R=xVi7uUDCqNsTHN^vWAgr!IfSS&=4HS6PT;_~r0%}IG8sCs^tvWc+d zpfl3WYoRv(rgwNZ?y4NHI~fokS`a8mE{<aUi^#FiW$U2&FJJSi{d;WxNsYS6%@5Zo zrLQwGolkZ#7jy85y;~>Dg%|6-7e{}GKT=}UBaINQ%Xf6c08M2Ln{X>)k2OrohlHT@ znBOb(DoN_Zg-B`9Ai`Nxexl*9NJg(}g=tDrS$IirpG~D~ZF3J6J~F*(U1M#o`zIaF z8;cSI%Z1~c;xT4LpfEWjbN@iJyQ68ny;HUHy5G)W`);e>!?OhU0IH)<V(9?%;(w%# zVtETxS&QEG!L<kJqGBs@11-vQYX`+0gq;Jn4Se_G<j=BNVeYJZa@uV{vwovZnD}#F zu)d48uvBuo4&}%qPI!A5k~Z1fP4DUH*6w`FzJrj1sh_7`>mFen+Ow9vk7uO%j#`J4 z+x~l6qyn0xsVRE{#63nX%7i7Xh$?X^FB#!-XCf9fEEM&VNy;9{m18EZ1=8iVBt>QN z!4QYrhq)g1_yVI37QA1VDZp2J<uC1aI+*O}414@@{)f@gK*@EdVobn@GHVvt-($Em zVU>(wrt3dWliVDj5faZeA&k<4<IPM{VpJFjZM;-dHsBIJMJlrvvv!IB%;&*ImL*6- z8vRsxLhnkQo2{t5oPVg;neO#*czQ_YVSmAZdEop5?)rEN#`Mk{2jU~&dEBWUBd|}Y z%VpcIzz|Wpg0-QkJyCDHS%k5Mq%Gg+eAAy^>87*YbXlk_>mipLFY$a^Sn&IFkPq%K zR7c<-Ia>JiaiOzNNj?m5Afw)2Mgo2yP)h79T)j?oda+;n&P1tROS;VgkOF-0(0tiM zrn>~vxY%<9qY$RyIK4Jx$mmG??8V3sB|Ob|M&@_b#FdHV^(<7zA(Nl``P`6h<Z&eB zuD6B<F_Nwk*d*9)KLTvZj99jF^qjpIB(7jm$S~jqK858ke%b^f+?)wK;kL?;$}d?~ zy6+3B^YVoLWNot*XfTNSQ%f2e0}d%@c^(M(!-N{~nv}8!TNyWJx9*HwvcqS&jdY#) z86Y!q$fzg4&^dY#H^*G+`o*Cxidf{0Emt`1AFgaXG-rbK)I3L&=y5zRgrXTtaqQ}p zB&&sUr{qK4Ae5w;yiB6X`(BZ(tR&P14M9O}FnP9>#a<c6LexUZoKvjXZD3UvXpp3P zA>3uWFTq*?n?hr(dfpyb76^f24e_H4nS_+ZpjVY%Rr-@?fHtCFTCKkc7G(tP^5jI$ z4I6&@B|jkIkzvNd@4~at8$qYeVEY#xc|J=4uG?|b1GtW1p^9ssi(uu<zXj@I=M8;* zSEs9d>Qgr2A@`>pa25BxJ?Bg}e5_WNO*VR3zqi{g1znaq+rKS%+lvfiFIRf|B{Y6c zC+-JFh<U_Z32W*gk^9|gAAe-`_3~kkrg3H~3!o)hJ(yD%15M9w*|um<W%J_S$Kq|N zi(&ryE(a4S6SMZuM0Y2wTQ$~ml8K!ymy7EezFYFx-2~uJyDu)O_vbXdVb`jOMifc| z%%C*MK=pmM#|eUqt}&u%*p$dGN!S4>^WKT@g+mmC{wP>|Z0R<+)t#l6&%_RiSL5I( zdh1<at^a=7ZZzJc|Mf6WuPp4s96`9q`GFo9)#A%$S%Jg25*P1uc*bs%{rTAk37i|Y z0rd;teQUk&&sm5z_%%lB7iPP4*znBo`63Ifgk!u%dMCUh5;%5N)m=h(QDi}mMba6* zAz=g2))>fA-k2siK$6}>S5>voL#(0+q_NrMdI#LHvLYLE13)X*ri=l`?<#phZp!)! zqACWn0}TOPkbniKkgU^@V7XN`m=+FHW+D~sl~uLLzj^#Z@Uk01ylBGeP~gPnup9aP z-Lw9aoer1ZezApb)&RL~X04^iMkgt#7Np~eAWd5@!=EkGO>Gy)+#4bT8OM3eN$WIZ z1lYMWj4c{FyYmtj0N3L&c+|_?`q<npaWMX~2V+dm3d>_qkc?EkZ_Ee9kTZJh12xpw zfTqqNRGApIv@tYUT+GOjAJ5bHmaQ&sL_cPv2pc)_<3*f|$;!O%bjxr+Qx01mAc+-V zEY3oqj?YKB;>cBvZ3#jj;S0vHWLE7lFOwdI4;M1lWiSLuG{u>x`m~fl263heIlinb zy{xLrIM{<pRx|_|7#x4E)pLek$;yYT?>;j2yWP)emJ$;&quFsi#n;0Pp24CoqnbXk z0e3^tq%Z(fZHd5tA#Nx`FD+n|s;|h*Mi8gx$huY5chj?)IKow-FJ!xM$}-!#@P|Qw zyjEL6$QW~=iPXWxR+<!eT*vl@jbI*1fTsm7Ge!d19EggQJ}qcOifWr$y0~(-E;w@~ zJ{jJhtQO44#y@C)Nd6`g5LDBF{#@~L2>8|)mKPxl5(X3nCI1lJN17=1w6Rw$)4nF5 z_^PDUvPcr*9NLOJP4a@V1kJQ(-`;?yQ$2**v&ENG-^_aDhe=T9oAfTX^%Y;9XM9m$ z%->H=DQP`<vBlhj7uBQMkhj;LKy%JaMits;vK#8A&u@eO)lbWtHE&vL%tA(l0&0=A zv}L7u^E#*LX^Qga?~A9in+}JEdUJN?z`@vQo03I>Y<NvWkbQqetP;*pXz%|}W^5#^ z2sUJ-a^bi*hCpC>q^u=Yuu@SZuW7yHQCNYCznp3sIY?jeAZZ9XG@kNM18bQ|W_A-0 z;gaY>GI-u#_-JnzpPL|r4M4&`zUI*(W7!8kTeI;S_d6l@VF548->!C(qqR7{4|G}H z;58rmmL65qZ-yweA4EUu1xmUXI?N?NE5NLWHFC0>-Jbi>ff>G@hu=4ukLec_J%@p6 zMZ2eGAA7C_Ux(hJWqC~>b7PGsRy7X(ah6@^3Y^b%j~U7V3$*1_j1?poiUjF0GaiLl zDAbqnpV%^ju-G9*1BDfyPlDL3*vw{^U!#E+doM28-pCm-&VNEs{q2!(vK1%tPT3-; zJt`#>F#wSUJBpUctf5um*8F~vN8DH8t50^q_HPq%D)P8ZdnNUiI6v*udkv8&G`Fy> z%C2R(Hp=4{<@LHkX2U-}KD-GDc#Vc907Tv^-|eNa$Fs_a;6=A}fvkCaG_k=hO(HfH zv1rk&U{?MZ%g{~fO2?IPGn1C<U(G+u5a1_5b)hI~)*R&~CvLX54hz-g`wH9APV~+Q zH%)RMNfivIVn=NeNe$`#k%H{wks9|Ps-2Y;6bJArA!L+Ej^Hh83!`n8bU;J7A(}R& zgWD7cd-fM&L_9>sS#Sb;*h_iD)tk?`=o8QoD*MCfz&2b{)Gcs-Q5~4Xqz;7SB@M~r zh=`5M@y68?l|FX=XuXl>Di{r4eYW(v5i|CCo6E`^ttRL-g4!|lCvB{o#S}W)pdMIN z`W<5Tr&cw<x6vDsx7F`&0M~{4A=Kp!L1t_sg5D<7vuRfRl$q!7YF8uFEHp)u*)*UV zg(<<`Pi74`opVgzxd=&<jB5}A2PJK^CNM6G2(7>^#R_IjR3;)sSmaklqqU&fbzrbb z_LH1>Ece;W8Ct5{@iqE`KI@N*`4#rMwt3)du#YTUo%;lIC;E=67uiQf8!V9pi<6Ft zADq&*7;=cdQ=?LD?}M0EO@Doo8~1v?vu?d(HG9zR^o03av2JdS!ewt{KtZ;!l)9ot zd@xa>h?)nae4xXZ=@P1>)>xCrE?=YvW_hhk*$clkCj!q?Ob{!|o{h1H<;<AQoPIFG zmTz4)tK)H`+4l#w#9QfSV=wksiLQ3JF#&$eP;0xJX9YCT&C7_pLx0;wB%Z5RbdXU@ zUlRYcjk}RbI_gWNGkL!a`<C*%eM>NY65?5O2;u?~)Xeq*%-JO73?|QVXODl;8h$B1 zV+ZEOaH5g_ntQrtd*|)T8&{*aCuvflgbPlok;M?B4p8K)Q-a1uzUT#wR_XXfnQz8k zEq?SwjrhKfl=tHKC)%hVlzdOvzF(<AT!gZBYY3QXTwM@@UM*1|0D)?DlhY{uq1yM! z{Oh;h#RHNa_t#6Kgdv>olisBP;}l||hXW*S4F+9E_pb2!GlZwiA4nRxno3oQxQMmz z>_};P^pRV56sAC=0StZrVH!2c3ROrYF;@~YtQ%cjA8{M;Gfh1l-%i8S+~Z}}>i}m< zoN#872P*8K1ZJ9%MWO?xL^zf_@9+c^-s-#49J$fAXVXKJ$tau@!$K6h(W*^He+I+) znixfbhI=)VIWs|=K@f|-J3)bl6zvI2_#veQILpgoTS}NcibqSQgCc4Y+3+uM55*7^ z!Nu80i^?8ajs|mwF2&IL^~8qPZ`+*9rSEwmx$SQMycuw!L?XA!`8}XFes8gmvL_S; zbs?V3i-P>ZXM%>Kz`#JF!0t4UXc{Xj@u*Pwe?jO%j=#daUNwrWbfd3*=>e${dYW@` zws+j$rf}7b0=gkj`OQj)lvJE@M^!@U<t3OIuAC4+cr;}vbgV^!a=7yNc_I}=I|s=! zxmmasWma@m-kR+G8C&VI(-Ofz6T4~ElBm!Pp?zyu-Y2nuhs5BRdyc?~zI{gM`xoap zrcB8Tam2C8rb$$t7Ba2tChK)%z5H}fbaf-=ioxrO@8Dni_K{xn%i3A|&o}<*kNk5L zyYG$NGOI8vENaIktTjJYH%Aw|&a%GMy&c%Qi9&F&$bU&n7Qw49>J73zNRhOW2NyC@ zgi~Wx2>*(?C$KaUtWscMo*>3&UYn1baMrz?HmYE28kGCjqN|XkA)A%sy}Jdg`G>(& zpiPA!Ws>V9<20cIlu*c3YUhfUfjyOB$}F>*RKkI<<e1$wk?h)#jWlhB>$5?e@jrS2 zyKC;~1kl6l?vw(&&ovR&rBxdd+Kl-~<~L|V@RNSk^EQ+=g%JL3CrwvPMbnpRAzEZ< zGN4Ps-vb5n8+@^K)mg2hKQIERfFAO9%X{}<Yl<rbV#Qsj79mJpgm$SmyY9%de`0i7 zeES{{%z4{icI~r6jcr~p(>iM>pb7@@Sp_(QwOzZ$Y114EM|a(wTb)n?`%Z7Z93E7Z z0&*_hb`^tz_mgnM!VfG(D!2>wkyFd(-c_VAuf^ljIMA9Ku)WC}NtLwVA{Q#rE><iZ z|KdD{c(ZrCW~D4b&5R0N;I`aIP|cIwN*P;`n)f{KJq&mcElIYYVcO7c#K~zW%Pq;% z7W61nO0Y6{D}hOO>oq_TH;G~alpxHXlIo(?RK-LYBK_5ks>2(Q%`B;RNVmQIytk<j z<R9+!Gi_auR+<So4Y%3va^dp;`|~>pZY<X7Z;0Nq-NfO3#Xmj9`NM#cm#A-n;4_;q zdJd)-p`4+q%lVAH7T)1erI?$#2lMO_Jxqg;A&^?VV%M+mc}NwMFwsoJ@<l6~QaaM` z4p3%qC!<mesi#Qh&56D=m?7H~#t&cxmts}QL+0glL*dxDyk<NTm@oy#TM5{<SX2E2 zNPfmKTgl!l-}%GBe3c)yuGQ&inRx=|0!cv@5+)54iPly~hKR1f*0PYg7+&YNdgp(V zg~!a<pK+Yia>xx|JYA>Nm6(pRcZoPm=9t%&LL0O2ub?5!S+t<780G`#er;GvWv-`x zG^OK5GgJ*VM>bzM2HpV&<o<sy*yhjCjlmlUBB{X{Nt~)%n@#UJ$Nd$V%#`oFFU_=& z@3)Wn-;QSYOx-x`H#6j|XM4yaB<cE2(S)_inD~Eey4VDvI7C8;B|}v(gfY8+h|90k z+d`(l;9z}@VN#RuSm#%;$XW%#DhyXRG_SOmSqs>*Gl}UXoQ?Qowotj2G~mjBQb`hZ zE}%nKQ4Lt%kCmA5+b174eejKC`~CJ*V{vOTq6JHF7iZXRaf!-AuD@7L&~_V?&Q|AX z=qD2YIZ|=+$M;v}Fc4j&*XNJ!0naSEh0R%eSA<NKhFugP<AUwRd_6}oMG4zA(5I_i zxFjD9Y^gqZGLMG81ZMHQCzAJiVE^5)VR^tCwyy}LGo%3m`G3q36|rJNz8TJVT775N zn7e~cr2uKkd@W@u#{Z8C9X2~N!Uyz^GO5x>lozUkh4NIgwb59{-tK5oN~{5@xK}dt z5f!u5upJ_cn6yHIDF8>+PLpu!hD0x#Z*MFaw~Y^jsWF43%YPB{--fc}G{A&uq73$9 zovEC)uF`O%2Unqsx*{#j<#8`(WDVsLd@qK+|9AJACez)K160S2AG!01O7+|pGLiid zypjA|F3i3EUNl*B>m*f*i3u49!AS$4Ba%b;Le!_C(+uw}*SZkAL#KCr2Galno)(A% zggij@?W6kKuco7a{nPGvG=|KOuE8+UI2@6a*`b(Jq3^Ve%GfNQ73W!1tRxAdO+3<Q z6PEdo%^@ONNC}9dvQtD*IJrntE`_o;E^YP>2v#i@8@r@eE$Q=sWbaOhs}{=w2=>}C zYO~yhDL~C=fDEn?pe$z$5w|E=4IaQj2mO;c66U-?TzR_~UE4s(c#1%zWT-rnNR3XF zA%I^JZh2meU{_PTYl2Ue`JC@_^yA+3dgK_PP<{Ue<s(wGw~-UI5721~OUt)b&l`pB ztHCpX?W^D6II*kC{M6Pc(cQtpKGIqEx)mm)U}flE%!G_aGqX}qthq<SoZ$Z@^&ixt z@)&pRmdtBs0tYLEo)S?Jl9Z2wnU@5pq*{zm*U&|tU*c?~wNI+gh4r~Q_{1%$d2-Z< zVG2p4{=Lce5x4@N>8%XncZa*z8BslLPtY#F{*VdhXyj8=79~_pf+h1sRiP9jQ_1US z5*4f10Q<{UF{-STZv`I%K~=$gTUCx+Mh+y((Nq_s9g+fMEOcuf_3v1aKTm^FWBBf_ zu{F*;ffV$a^_DlF&J{D?kigox%uRbliOG_oCUD+Nka-XU8o1ft2<ig?6z!3G_{ySU zY!#JJZ6Z_NpfE~*sdVfyl72dLUlwr?e|V)iZ!DQ@7<Z$Bd^rryXK*GS?TlY^AUK&f z_I@YSV};Fk-f_Pi$jN5AK>x+Uo_47_a6pe?!Hbt<FfMUShFA2nh`hxM2;plLjRccn z&Z6NrdPvH*Vp2EMfsDb1&kGh4!;n4C%W7$R_6Kj%8y@c$)=F42b1gqLK>%Gpwz{0b zc#7A80)s`t4OhPrbD*F}$PFitTA-5k2J#W@<qH(f(zSd(#p-9X(VS`LSL1$fOr^sU zNF_471&<YaF*ltS@H6in1}_VxC$=MYZg^ioxrwJam%eQ!y333;QEviUX*=yV>%&0C zerEzusP?hfqe<9p#H+V)vBR0WD}Yc3CuHRo36nEF){v1NHjdgwrnHD<*$bV`$4R!q z7}ud20+kkR6&vC&nV1D&j4_~(%#*Lo!zdz;=S%-J3V6#m544pql9y?8rIN42;|gY* z=tA$>zaV%G+bKgwcZ@>&b!iz*;@uz)9kl$9mnuTfR9$R?6Flbdm;v#(D;Iq%o5#(t zvB2-oBb6`Kn-Qi;2idt5J}BVFJ5$O+K`BR69o9hFm`Ky$#(B&J?_ZPE;-)~5o)m4y zQ`>R9-7@T!I7a~d&F$KjdbeCZw5o3u94E|aEBJd*dU}5KUjG$@@9&$d^^uHQ+H<wF zvYNFs#G%tUl``3~E&hScj~5SI);}y~%Zp)hFBsCMO^!htL~0`tYPe%L+6OW4C#DPk zMnN@bUc%!&@|pIREcE-uG?=vH2KwQd#^>L&o|;f<Z%2o_+Vvd;s<-py!&F#{>T=!o zK8(HC@Rz|AG}6d-WcuX?cJ`HkC3;qw%f$ujG>YAS4ULg@6gj+N0{Y-!yprQRj35w4 zhNi_U(U!_1)36|L(4PU}a{T@VhU7#wJ?DS`A?V+YZF<hC&!MZ2c3#%KDX}{2h~BFV z)>{1DS2gsVxrSlG=ULMU#ky$-AVRzK&qP@EguN%fuXA4SXeqlsw^EGX_h5v+D;IO~ z_b-ZnnTicvPO<O)_O-l?=FWO>!(filQZ)q_MuYTS^!_j+UJR$Il1qK*Y9dN+o(#-P zOdjTdpmmd1#h-`|MbS`}6@u;+Z)wlP+7`ccmW>VN$X+gK><az$wUi9p{M*?&0TjV3 z$~r7+-=fK=yQm6Ye=+q#_6QZkIdzHyaa0araaim#9UzmG?r?nPio}MJfU>vhWDvyK zWp}=p%$P-^2gO|5t}nxAUzE_Ac8L6&`roFHMF5(^Avhe*8g+2kFGm^3#e$f@V&BC$ zc4^#_U;dX91!*<iP!eV(TAcu#^}i@9E=|*BWyl_RiB21CX{p~}#mEmvHXqg+ZN~{C z#{ylyp9J)IUblZn_e)W{<_qozqjAug6p~8}55yH4=S9=Z*QG>rkrz{x1|s9HB=Ny} zjr;=9Y`6nv*v>rCA|d6-^iRMJ<cz6J6*HV2b*h2hx@*2Z!7u^VBHq$rZrh~Aium$T zUhzsyni?H!yoy5v`EliF?opiz;Uppip|md_I@m>v3R+bmnzCeNv0yk<<pVGwntXrI zZX@M@FOH~5TOY4Mn1|)=ExTaCT><ZxkLgK{8`D@G6L}dh2;c1|AZWL}=JMF@WWYO2 zG5;)zKI60q;DfAO8h}>oP3GbyS;?R=-bo-;htSKPDo!Q=^z;j-hlNT~7aV2fMf0uN z75@mQf~Af!&iPhXH3tLl%UexYY(UVE3C}^POj(Q^NjNtzPNu=QU2hAk&9{=0)s6Qk zPhgSxNEv6vTtsDz(9kk{5EDEDRp}u@>c2j%E(Pfm$(DGw5lsEKxEDYCc_8&X#p``) z)aC3ve$u^G^(R~-V?s%t)D>$Famo@YsJWOX?IQ_uIlPL`(#KI{LHDZGxc|x)%|dvL z;ECYlWo7p1Z0xfMUC(<sW?@PMX~2q}|3x!Wgzb(IuH&hUe6z+@^Ysww#Ak2eu9m0p zl`6|5GyD0`sS5G2Psxwjp}kW$=30bo-yxaDow_G@XVS6YgA!F3Lo=TAmR;1yI3s~1 zgMJ{0WJ5C_GfzpWTo0DIKPpw@occ4L>3Zj3(wn{KIBR7m2(#%|)i+)34k^mdzYxvz zeaiSofo^viWo-aClHz40=HXPTm~8!swxNU}nSD%iOzUNgdhg5#C7ciu%ah{pw95so z32YX<lmb0+lSuS>KPa%4yS=de`R<G!&&>yKikj?hGfMB=;;Ld&cnd*)B>!UE=PedP zgttSOPc8lR#Va`BL!s0udPlVgJ?Fdy&b3A4aS+JpJtZ6rB{-Nek~7f?@_!Cyh6Ip_ ziIJv6(I8qzvSjHgMfIfKCF%%K2FoPVwZY<|^8}q0gF)pn(2>OoqYpN`J^GW+*K`88 zYC(UfoB@hYi}F62NSE}MM^-*q7ki-1b9A729)0;^>h!Q3s_c#HcMGlLX|jcwO6msr zuuMc{DRWrj*f3Dh$h7=@sEb?Rkxg%A7!e_D<C6;7`{qQ~Iay4oH&NfUJmz~VFzk?q z6PrMF%lGSXB@3B8bD0s_rjZgv#0_0=Kkf(7C10D@2aEu|!#~Jp1djs4<BH`zU<VNF z#(3H4ALepBuBLqP8q)qf967!ZlXJX~i)K+z(A7zKFSLPL;17F73eFW7pDeRDkRgk5 z96pV7W^jyr=Sy61BkZ~ru*<!Rr={q6+)`^53}&6`HD4UDP2K8XT8y>CJ1)2jE*35v zRE6<nXBQV6knhq17^ImtIPx@z(k<O_QAw*(F<2~k<_j8700yAIIL-$wv2<yVn`DjY z1YwCBfNTqKDH|z59?PdX%j~k4PW<X*8}p4A9r{2!Y5H&h&E^M*6POS{M%i_5L9%?E zY{j7x0rqc_mi1%wY5`pZ)l1k+KE%HRn&pT!Qhnlyl)$eGITd^CPOpuLt7vMlnCp={ z;_kp75H|2pD>pr+Q#3ZbK&y~A@oa!#5x1kf4BWo68s+<WG;0t`rU<0UL^Q2?M!mTh zG8Rekr7Dmmsy$OKf`(Cpp-`6o1&BWbj5K)$dsrsR?2Fplwah1u^mrMUDP`kz?N5sc zYdgzNviG%-PO$xLG6FDCl>Gt2#{iU7sc|g{<Jmn%YDIaGMnO}FDEe4dY(x~8ppyLv zQ?6%;EL2-}p-#xB-Z<oF?acL?vRrrkBjCLR6hWSwwM+Ij5^zVW2wHvKGw`pG#lG?d zHlWg))KL@TCBA7<)JIi<7qEWxxp{Kf;W}waQYBZ7&G>6VR!X{JxT2{4bcIN<y4rx> zl=}^Sj<IYe4k!}dv&voE`JN%%{g=KY;^%mOl+unH(3CHWeG#}0dSOId|EL4<1eIpp z;t>s*Y4h4LEn(h48U$1=(7Onck@iDNMep(~p6-x>UQj{@4Y2-jo;VB&>>o{dYoqvz zqhUnEIq*bt<b1HzT_i$kTGD9Vl+?5S?>i_MMNqBf;KZmL5TULf=)>hBP03N1T$ckE z#WEDm9G@+{;(iv)uDVlho1+kEYc%^Y0Tys4?qN$XauHwNcWQ{YDz^pt^LqU}^mn)A zM76OxQ>mCNibYA)3Md8<sD;0%y!S;KpiL?=3sZn*9)TDQtT9)sMroLfJ!<GukS<M8 zs6SX-Z^Jj3uF3p{`|4-@!3xjVK`pr+>Y=4}o|2d<=me~A0Z<EaDi|x5X-d+dV(lNO zYA)N^i&4B7U@_9Y8F8&~`_-TbR)?7oLSYr@(#frFaI0oDPK+#s0%jQIa%aJVH~5vY zBlqp{IJC=ug1Da9ZTFmU_5Mm>J>%6@uy{8cs&kc4%t#$mpHu?N3^2h0*>odJYqPkI zP>&hpzM7rwectaURjbkgw#~f>9AVL{D<eWGgAKc&>WuquEaL~PTW-o$bWZ@NmUDwE zkyA0#gBKQX;%Oju>aw01eM3L~rBG)q8tFxCn#JJYr8j?bd+<_Y!(Bp>ov2)jYjk-s z{Bi5GIr{hQZ7|cTBk*ef3-3O(2WT{$c4Emrew&v9es-SHw?zs+I2__NY7NC@qJnh+ zRJHwvYX9g$S^v`u188x*0nmn|V`34s1B@~O?HaxE+IQJ-w$ql&lG%`_tpy12xz)ea z;Gm9vNW3bG9){rZ_#2h~L`5kwf-IDlQ-$gLA4n$-RWeFM>Vt--Vx-A&F!(707US-x zR>5_pZkOdWxdu>CZ*$dpXzp;*(k`VxX%bSp^lWT_{y*9zov`UzQ|#ZAsIh8NE*tJ3 zEl+&;Ep+Wp<BFKc!8uR~G`dPg7s^&6Zf0Cl0c2OOr;X|dBP?gT@Y`aNs>8j`nRQwf zLx)Mj$OJzp0|F2+k*FNWq%BB%3ZPNJ0_xD&%vfn6<yij~A4Ljp@re>b?<fyFKAJ}l z;Y!_#*Ck%3hqx#4vO5n}HFrMo-Q)dc++eL}%pX|(tCq=HnFyef!yqQyQ_w{>%ywHi zpa<&ooBzg111gobE(WxjEd_NiiR@SkX_SelEN9!V1KdAPS7x*Cj;LGZEwejOhE7#H zSo0X0H3>l-#(JA1@Dj+$B||`r1$6H&vHYeEPR|@C-E~$Q&UxHte7=Vd|F|hownHH} zu!GqZ9wrC+kd_jfJyK*7{vIIsf*}0CI&5AmeY9P68WP$=wYBz-j)q+kH5UfPUI|^i zfr=lH&uS)4-Yvd&beoUcER-`Gr`(#0$wOZco-c8j`QD7VLmK+j7mi*7jfa!KOS<xC zw<TRIqowM=L$%00cq(~6M<vl%4Lh|r7fE&+knL=z_!I1kJ&V=vHI$m;j~<)ptDAVs z+#MV~8Y@vB)Jw>P`}SJLzoAaE1K;dZYl-~`4~97ajs28W#G0Zq^~OkK6cRBo<7pLI zlOG97_kvSN(aEjCExclyMBXx*KBU~KOgdil*2IhYD;~kjWW`DhedWBej7SFpED`l3 zmExk)ao0s{Qp#O6x6Oq1F#IdZ!m8J7E`E?~HeAj})BW<%seWdLFAu7Lh7;V#o8(Y) z0`u41!URp{N{EL6Y`*F%q8YTPM?6s@GFTx=x#S!mfLVj;LDA}j3$FV}5c7m+Mq)kb zD9!`(@`6L5*Wjf=DXR5Oh>@&`y`LEGEmd4XMrG}Eu|SK_L|vK{gZ&4Q`?asx`9k&X z<KIQM>O}$=E~{>K3zJ+4jm3HP%zWWJsH(-B--9B*-xD|aJ<g5wFLu4m7Z*%%-Hf3) zJ5pR>Avda83$doLV26nObssp}Zf0Pty^%COvU$vWE+b(^AMqd0_2g0(!5n(yWkMP7 zlQ?@&5)a@Cb5SY9xDhLraVasd#RU-mG5;(YG^4>}rKw2j8<z@oH+ncfR-Lunw#fV- z#(NPFso*I^Nby#$49P!GV~c=}3S2*+eaz=B0)cXyQ33cW8)c>iEEL3_m=G^YW6(LG zT7aDT%8?o|kO5GF!(lPt-cGHRg{C2Hqhba0HnJCY<{JL>-q1~2igX)bAe4B*R`v%X zVbm(hQL2T5Uoyob0X#64D~bcx_5k^G^~l8q#bj<Yyv@^$6~hvBMamIn1s%b8Iaz9* zoE<)tCrvmKYAiaw->_GdmLqveBYSSnL?8-^2OPzBj31cwlzz0_EwFqZnl!>4Y(4OV zbm%YBQ(3Wk%hpGZ=FK0U<cGa}lMlN{I{wv-(^0L}o1P%<1h!VBc>$asb?XLd`AmDx zK&u7s*hNd|us)~z5{v-$Z4}&%w}~WaE=r~2P5*u^A{+CoW$cre%IHs8KHs&k4Gg1s zcHpnt<@?iB=jn;W&i6f!{7Wy?&Rr&D=Qf+`=@MIIYnzI%WM4`&?M4f+rbz*G(Ua;x zX%c|39NEYKDS__8V<`FE#?ggWp1+?5hu>%21F!GY{-F&@=8iM^cerd0=^jF%gWN41 z#^Wnk5SK$pZ5MGd646+l8QqVy7fUQ~mm~^iDrF46CRR+4yojC@Abm!i1753_%_{JL znS-*~<v8jm8b+#(LlGr^Yz}0>|0@q(LuL%|8rSXJ6KsG^KUgM)g~FM2mCP+_fh070 zQqdyQr#%=~ZY_PNfo%BncoGzJD>vXW9>FCm%k8fO(<C#xG8A-YX}X)_kZ(79m>8dI zt&eSNEUl;bf$}#E#Gt=JceS;u&v?TwdqsdK^v{m~&&J@wfoV;((ak&vP)wg&dZs`q zQ&qBLSek^;{77j$BRy$XAi_GogOnxHgc(927l}#1sq$(T!v+OefDV0H1K<<L())_r zycHF1+GJz)H}}}&OXLaptIfM!3+)u$i<1Wk;)ZKCctcE?H)<@7P*zpC*z-P3DG_$q zd?*0x^?_oz2PsyL*9h)DSCnH&itn7T;?F+l62~eT=m=_vk-El1Solb{8=f|w!|P2P z%+I|HrkkrL)~Cg<ito=@{`DrSM;v6Jo;~+Ny%$-NyC^3AAjx>OrM;l<Yl>x-cZc)K zeU-y6|3>BJ^>eN^YxQ9=b435^WlIQ6eOj;Gcd`cjMK?6mmasZ1Lb*r_08`5v7OX;C zlB%|@jwcnPj>E_0et%4=zIQgyu*-VP_cO-V^(LOPZtPS1Nx`Hue*}S_+5N~^H8hrj zI-N%_eHz6NVv&iS)(OIrdq3nTQJ!uwLayX6PZB_8Bvd$3>J>DS6o3A3et9!tS-2LG z7oJL&4Ly(YP3tI0gW8%3L7NF%xb~V?N;5`4Rcr{+g#|847AHp1LR(7jSkjSH=OHT* zjhqop;ndt<Au3swrp%cL<BE<8lghc^2j_cwR`XLrfTTg~NQG{v#%gTl-}!`PcW2h< z2c@g)Nw-DXG#>*Kq<tR`S=UtwZtkK{{8KVthTf+4(dMl6=N4h<$8M1GFTDCo1s3)x z&0g<&yAL&o_5!(DkQetc%lf3g5@C>Hh-M;cQ8{7ZsN=Wf%%^Dmw$~k{$r&$)k%-i0 zpM!_|@1@qW%+Jgk!Z}{ol@Hs^Hr!qM5KQ0{^k8eILKchjg&H&WP$mY8bXU&?X}RTb z|4KzQYGq;~5jdTCGvmP}jP0AOm%AMVdw$Qki{CG|@AAF}Lz5D_+g&c#75JS`_z>7_ zbE>b2F`x1P#S$ZEb>P0W9f25Z@NKC>ux<g0@L@mK4^6XoJB)wijQO$Y@r8O<xnbqW z*qL6BjZ$SI(wLLZQ<WNPvGZ~V!d@N?vn@~!mct^|RY(qDiWYlO7eNrtrv2$#*H2k= z%T}Q6ft{v^Y95J~N33~LlG)B#;0ag&0Rvs{#x#xiZ;gDmYNVT@*2qemKbuY!Pft!I zk~_#?+U$Zpo9u2tpZ~jBIt0TL3)jf+fC8}_Ss}8mhj?7M;?vx8wPqv&-9Le&&oA24 z6*Df|)z_vIrkkCnNs}E~?0WhYcnA*>QO5bUN3^UtQ*HCuvvrq&b^6@z!=w8!2Hbjs z$qjrcMydR${lR9<+%<8&8e0|0_VWX|h-`pgCg?lo2q=ighE-1Ye!=L8&*MjY?R!6I z?bnXT=;zV=cbCtlX^@#hR-@1Cj_;bQqUn(0Ac%UwrHzl)It|Y935^NEG>Pk<fk!Wq z|NWC<fWddH{L%GJsoiC=**L!l{l)+IhkCcG?3GaA_uFB24*y-`eD_VJpZ6guUa0>z zkgn#9YMIUnuly8(Hf$c12Bqp5G#ukdw7{roOK!B<{DPr%+hXzDPFJAse@$OAc|UR1 zy88tZ&F<H~62q%$mO1tFY|o5EQgsMMazrF0Wo{@jNs5JtQ5pv=n2aVG62yXT@<2T- z{v4v-&#5XHiB%rwgon<e(-+6|MrRMXG@cF1u)6EHFPhaK^R2c#Wa#LT`B(ZujEqWn zAJuYfMb@<b2pkczYBO0uDN}lxWKY`9D(NlY0!v#8#PjJzHHaoPku^ezrTr#-QthNf zd{w|awYd>0J#I?RCgvOK(HdSpQ50Gcqh{d@PSY>OC?6yh6Grn07%u`1hfu@7bsWY# zNnqLU|6%nLSJ)Kj2teB;*~btKN%N`%k=TKr&a=sG;Vm^SJrRugrGLKzey3MQX&aCp zQob26Z*2h|9nZOCQLsW{#|j2=emh2EUtL&aGAlolBql<*M~3V-J;b%HKs#kw&+C1O zZCBdgYM1-Rn9?4*73T(CtHqS?PvKEkabju*{tSapvu7h}4g?*&PE0EZ6?Wuh<xJ_O zn?R$*c1>wXO}E$Bhl*YLOR{t3SNZI1>;u-@Zff&GyZzgCp;~lyujBN{)vQ7HYx`f= ze^-@KYJJjj%|_fXH70`P9Ub8^<apzf)w-F6ML{9GUSinZ-O*u1rT6j8@;Xh&Z~1UN ze*0q_-#&Y}-#7kfe(bk0pE&P)tj?Doa+&S~d-q@#_gEJ%pgM2nzAa5n;PDk>*eSkw zV95g`-XBzc4(D(Be9SjMClF_e`k@lvcAYULgtS>@V?@@dGS=J!R{DYFuN>N}ksuL2 zs<<%p@pJ(qs<ENfzF|%OLwZ}HP9q~86<k#hnv`NQ6GTp22Y4;cgK~2b;7(IgJf*s~ zP5U~~NMl(ZA5jK@MTPw*q(j8`1YJ8l7#%-irh`b7lh7#merVz<K+he&xjmX(*ziXh zY%UMTDzX2Z{a0YML<_Re(mekI?l3a%4+?@z;}?d8hQ28oC<Y9_Y8L(knRnpQRlGwj z$y|6JFT{W83)Y3Cja6yEg5n|Je5GCGa_8*tI3{CvaNWN-$K+~|GE@8MQjAqa;s4p{ z2C%G%nOsqm{Kb|5bHq8!x60H<(4CQh(a@4HSZTKA2&1D1hR^<~0Twd-`9jcVs9o3o zQ^IY5_Z7!V$8)+_R>4}KRiWqoSjY=Q?s!nm7p;h?TWUd=Hnlfepyk0uUcbR_6)kb~ z<77qZhS&7AUcl$542|%^;W=NR!)+_5da`~h#1&q)A#Qf6sDWfs$4M}vvXR!5XJ5w` zqgQp=k*u`bbzQAFr5UqAU=vd`QL%D@xkEL4v`s!3m&CgDI<w1T61;kH*3)C<xrX5W zwGo$p!};{@EY4lx#i%~7!}Xq2rFr$*pKa)@`KF&5xS$5P%%9nTh2r7t%MgmBS<fNa zG78h%K0rg*E-&}@Bv25SYT?9+Td$h6-$vZLBrVMSGT`8h4j}JcY~0Tvr97gF2@iot zGy@q<p~LYIPY|nU41xU94S)_j(kLf(2D!kP=cZ3--gHV?&3he1-;J51IR)@0;>Kx8 z%IYx~sv40;sTol5w*SCUpZ-AmxB(P-43}{`SQah4N@MN~p%%L@KR}=`UBzRNEr=!^ zr1*1)DdU^MFS5ok)GThW7@cySZa*G1(N^-4rmSQ0rua5TLcv9p(rP#sPZ_w{51qnj z;7}JR6fHTLE`W%FMf^<JW0LX)qiCrsSu1y_pz5Y*5gS-$H=NyU!NTkkI{bV&;o*EH z=sLShNxaTtQp!Q0h0nn&nTIcMM#|CqD6gdH##N`mx`e4H-2X*Wp$N=lm5FI~uYm}+ zE#qox^yA`0FvtOT_LF1-hxQa{+(Ji0T)Wm&Usn`J#%%5oL)>*3PgqvvzSc#Y87y_a z)}~@SFl`QN%o(wXGncU|CUhhllJZb{P{~g#zXSS|HBoPPZZAvl-|IVE7492)kY+f~ zCYW*sD)+m4asMsfXV$5DAEVvJAJ1sE`vQ$g>y7MTbB5GmU{)A+k!NIW382lDm9+M} z2@%${P8iaU=9_^Qs1+7mCQ39ASk}uXquCI=n;yR>|LN~?pMh_Oc3p>oe|@}z--09P z-&kvg4)&z2Bt)^~J>r8raTPJ)iwVZiAYdU92^Ls_j%<#O)UE~K%YC~|Ho5C^W>~kp zuuX46`-y;wQwsQ;<y91t7~q4u`oMuxz$DZwh3P9|nsC?Sg}M5ZPwhATi>oY9ZVUoM z@Co53pH#RC*p41IY3Y~C-$npo`2j>&Jd9YSh=%6Ec_VAV-2lElAQu9&o_@~!sq!i5 zC9WEpuDy@7O;4sm7{hWC&f~FhT)il~S&m|TCkU9kX@m|vH$g0#eg)j36z>V)VboiZ z-KuvyAp5C=rKO5WGD<PVE7SZ-(U^?hNI)(71*<AcUFB8$di)1Wog@--gN?O!O1F;^ z=>H}zO+HT}*K=jv3L$yoFP99*uatfE3b2%oN#M^He2hf_3BQdO(TQKj7M-ssBB3D* z6p_C$L+%a3^xo8S^;*PzXa9W{TF#fN5gdXp%dL?!3^DWyy%;ww&8=PRz+D($g4&Y+ zF{l0w4r=Bgf6!)hzFX(J`(V0R;|<$tow@6M9=GcAyKnRN>kFkf(&5zxoAy%ycN~nk zI({qu9Wpm?cClbJi5^onggSGco2!IVh8L%8<x#ztE!9|gYW4%g&{}{{rO6<fe@o2I zkSXnM<FyCZ-*_E+?yU-63+-~>9&dFD*-RgssVy$O*5~*!GPhP;&DzfiJq~h!v>HTE zDt)nJqa~UL{!60)eu%C-(6F*B7Mt<s>+DCQ)K~$a(_!?%>VTwL!S0iBScwUzry<}M zj<Bn23Q7a2w;?p()ezVNmsM9mh3lk3j1sC=9G%7Ua8AWm>O09*-3>)=p_?W^8oED8 zq78x^HSuoAvdT4B#NEWAszVpeYeSdA#2IHs*;8@!lowu8S7}jY5%CuGkp3w~N~{(x zq8wkAM*{du)%}~~u()9YRmm&`3sjaFOYlDw&pu<>>oS-m&{C7tR7G#@`RTY6dE=6O z3~3MbH!6`0&vG&r2oT|ELoA}0!9aioxTilgHxZ{9-p_GEaSwn7&=)`Y-Ofhi=S8); zQpITltF27I!tF3t6L=5(RA_KYj~HnkqOl;7Bx%Cvak|o9u;paDw2vx3Rzw)}gI%@o z%)^Y`dR#{zc0@3@`3(_TW_~?tn_oFHPDwnKCZ-OE3kpU7(X_QCy|9ZDMD(%;-K<e* zzlzGL4x7mrgMCK0dH>fnl7e5$P56x-Q^yTBi^Eob+G{4`p)r??XXU0!A)pyrN_YN8 zwAV@8r7H@d@<vp2UZHS_{Ew{wJ?3$Xniey-*O%qc!>-$CjovWx1NBWFdCw0y0q;xO zUaXfZ8LhYruvld>mQ$3E{J+1OxeW0wv}qz&N?yQznAC8uLovcQ&<s?$cHZPRT$dtE zzpf>t#dUR;%#Nu)**GawyzwbveQaZ8BIu*!i4@5H0s{Ozu+I6Sgz<s_aJqZ-|3AfY z33JZN-)FZ{ma~`vzC4Q=1ssExn+jaKwr(;FO_nI5qoA6Y{4AzJ(=%dRk)(4`ZzLei zo^+w5b|B9x94+u<6w@$3wugjwS$Z{LdH4mOb|a{DqzjW0CtlzqJrG6DjMOa@46~_w z%2G3qp)xj*sO%G+YcO#BE=^V4@g_2k*HpGhEEA(LT8)boB~`9lN3dh&4ym7^Izxk1 z{l)_Ihqm8fQJJ10qB*N8zm+BWh!_WJef`+D99w<Henm=EczMcZ`R4S(aKqVS6LcW; zg4G#?3FnjX)rRf7ru<#Z3WyufvW5MDO)`me5JeB+feG(!R$rd?zOB;hAwQVu_lEU{ z&12R6$M@bwy@#aF#zJ*nu9t2MuYC*~SgMv-n=}S@bJ?#eE4NauM`3imRLV+K#ccRC zSkufXB23$5{*TwtO$5%g>;C!4+3!UgR;SCl;c{qw?$$eVj_A>S0tI=^Myq>*F72W| z>y?)EH0kIhoo;kiB1YUQagf_VPoS0U#D0y%r#}UEq>9^hFR*rt_0jfk%!pJAR^i7W zx}CY@3uq34(Rw(|p{=@nrYBa-^@L^PYbuLtN*ww|tncyEMMhk%;h_3Vi=J@tE35gG zM^La$$uE?&@R(K0_(eS)D=}QBjaO@TT@I_^)|<4u&>Ht&!DDKX*`rVyoXRK(Cg=%l z`NDQ3gYx0cCe{}0x?J?{lKu86{oFraV+{vZMUI--NlgR?b8~ls8%gS2pML&dW(HNF za8~02bz?xLzd}KSJAOSFz$1g<23@ZVyX1+CWm2l`j^C$kcL#wm!}(S_sU_g|wn%uv z_5k;Z^V@N8$T~-d(Ij${J8JbB9c!8wTviD-PP~?SdcS|ks9MvB49jK&4@g7jR2g+~ zV>@6>JfSPWZ7}-v-$`)08gdL|0XA<ewEp#~$MzfQ+w;HHyMMdgQhEt&-{+LK88SJ) zve&F9z-2?CzN``$OM6yEA!r$YgTQ!SQnCzzfvc=ut_is8oZN>pe4bA)?s(s=9nKIu zp6_^MwrdX`@N2{ClM6h7E1yu~65z*0#}tc{Nh1c;kxikO{on2>kAof@|5Z;}mMFDY zd7)JrW0dMOK7dj{`lWslr%GlQD?F&oN-SS1J(VPmBl3ULR5(*#Q8l>+MusTo|3lYX z2G!L?-I{2y;4Xn6!QI^<1b250ZaKIIcXxMpcPF?@aCdj7ci!*b?z&yw|IQDJI(61w zYtJ>u7|*O9zbLYnlXbUtRFl<`IyEkz`iW%_TLbP=ldxSJZWZAa0r5pMMyk(Z#XVbu zKG0qkT<4_zu;`MQ0(y1Yeg23vim&cz?QO{8dHDz5Ck8xT_u1YEn>?zIUzI(gaXH9u z8{`ZSL8ie}@TaAC)pa8bH6(@VX#@B@3$`mkW<559osS0`(vRIlnY`}+V11qe=3S~Q zF!MngpVI(S^5JE?djx^FG!73lt~Y<$Qqs|du|eH3kpS*f8q*h~f86*bCr;;5#Gmz# zn^8VG)9v;_hVQ8v5iE-voL0o_f4$CnW4vE@k`~_GKTL1^c+wJOb!(xcL=3_50|r-0 zK4JSSnN!V#!hRhXAlFJa=CR+4{BCO1tlwXIvCe{?W_2}z-MJn2J2?&Mb*O+yftmCs zUWO8Bic6LmF>m@Q*zQkoFBDm*vKR>#go<)0$4_ZolU9uR)Y-zxU;|!S7fijaAFnw> zXY1Z}XN{g6QWJdgqy^jx0|Ua#>5E~s@1_3~{e$pQ4g~FN+AxL7-<q*5N8h4nNwSl# zC5^@p<G@%SLqPx<pZ)w|Mn11Fx&F0yZn?D4$=nOoo`l+Vo<Oiausp39RoY>hdNP=4 zq&*jR&g0xyh4|buW>2+WmmG6?J?ZahkPx@;-8FT1Y-|FjKJ}waM}B)Z4e3@mm`HBK zhr~~C$2xE<!D`TbI0g33QTVh}6T?QUt$X`)ywc~Y;~Bgyc9z0xB`L;)odW&5eXH(; zzj3yyK1ycxJ9kEWS?>!2bY=pqaav|qWSkTCy9}wy!lec9gV$J)r6|r8sE9wKLXjrv zvKV~-Gr<ZXR_EgRva>8uW*G;p`&Z%oy#&{P|NeVu`7{N2?D3#Zy!~O0BJ$-Z&&BU^ zke7bknZH<ClFd@YDfXA3@ZE~hK(~;FS<F+03Jh#3dy|4ozut_Otc+H-0~sT=EMEQd zp{jJB2b}kTl<`L<o3aL+akBYc#$uyd#6>VpLo<;A#8~!Xyg?sE`I9Lbb)W@k&5G2R zfXZ1wwQgkPWap!###baW#Nh}6lG^D6+T-~o_H6<9g0jO-1^vcmkP=#`R0Rh9hePo~ zog`uau@ENM95@U4g1!+V2$+p9#*`&|weMv2aagJg3Q#3Fl;DJp758817S0X?EmLz< zfGon(aa!9PG-bh?x6QW~v!-9w4kF(-O?JARFHXy}RJVl~r3sSx8Ns-nhby{gT!a2- zR^mYyRQapn1qj*JF*>7dr_<9)Bc`+$e&p44IT_rzdp-2GdGS5w0c(*(Go=0g*#0A| z$p<XHJf;S+_}VNbWE3q{fR0VQBv@IRqNnhS;EJ+3{H-v|vg#W|(sP})5Gx$?aK-~> zOcQcFPIwTJ{VsDPzl+stsUu-#$oE0Jg`tgCw@b50g2)WGLp)N`@;(M3Q=yS!nUERo zeixQ8pJT&_aIjM#KGp8&-UE!{LTFm$wiz#pqI*0A#cg=K|6DcQzWDVn;ViK7#^C%h zt4p5rQ*-(?-|F~B5zB<Of#fwUH(x;hH&g^X%mi8Qp3-=QLMer{gjv%Pa*=4463dJQ zWQaqC$Luf=(6~G0ICj?Is`#M863=w;>D9-Kkl^+*wfQv>ujk0jEml_29i*TT3`!68 zBO$dQ<E{ISTi51bvm`LywZY!E8PrWtm3ccGw6D9^dR*d~20+yb<^w{0&!$gMB^0(F z*Tp6(rvB?-s^#vsBDP$Pjc$Y52=O`~s=!{Vq_o~k9;&-8?&pdoalA(8;wn8IdZtP& z&8k*AT@SnneDvA;)Mq8Y7H=n+(ZS}WA{2-J=Qr<|o6wB!oKn)L>q;CUj1_<T+xRur zU*!0h62fv4Be?2ydZ_A=Er{ax;MmHq@#rUkcBfUCHom(`B|`6^s!wl2VtGJ2_tT7y z6@y-rHPQW<J_m%Dc^m_*ZPYmVst;a4Oy-v?+NFqUhZV2WsN<X6?T>b&!50SHptegF zkJCfl_l{PtYy`{3#I0Q;Hkcm*{Eq2!mIe0fHI5QVS6ty8!_|5yIG!@MpCYI-fnK<> z^IpAz7n&c5Zf)?%*dG*1rr{Zl1~PSaHp_62DJ9I(KeS9K*&H~6Ax@|(4YFF>(npp( z9ose3$$#cYa#0nwLK(XM>#9m72E(tdMiExxM)UovnJ1@S^Qwy22xjYQfK>%y-Du6> zT#JP#85Iddk$---Tim!O3i}=+=kBrPskC5lavJG*{?_=|moQrQYH@>PhNX+B!6P41 zgT|jAEooeoTzK^rD4V(sv|S<~CXRYvtT!IoC@!B2y9PJb*<HJnF&)*;y16j}d}5kZ z!4HX`v7&Lq_Q;u{7$!!cLcg{B^9s;DuM|%>^d@o2QwiR+767j9d+OWxbTo|1M+-!J zuonay6<Y1KI)nY^6}-gP?LxS$W0uptwX9y4G^-E0gZLm%@2wKp_(C)vg@S-8&3wka ze4OpGQm6!dv^s%l#@F*f13k;eiy`!K7wEBph*7zA_k}ik0>{&oywfCi^}6}?-oWoL zKfOreVy5k2=V>$~P(G;(?21#P)nv;Bh~h-9P~}M{<!U-ESKgS{5#epqAOwk1_tOxU z&3%qvhr1OD5B^3m{Zl;NBqRduJH%gEQLDXlEIvOLU;?0#fYsS5B*8?8ti5uSxQ*y! zT7hM5?jk{PA-8%dx`$NwrVtk9j{-mVK%Xx`1rBufu#|jKQ<bfv?)h^&2+0z)Xu(bu z`ZecNxWENS(i}>}{S>b~rUk*Jl6A!E-}li$ALrY@QGXs1fZY#t>+-ltT_<IF++pK0 zL)7uBs6$sh?OxD0<>+S3k;+aNsvliqg%Z4)H`t>F`f&l2($WSt(BP;2==2qi9Pjpr zy;&QaTeD_J-X9l1?H3OsouDhq@`Y_$QY&`#n&B^6`6sgFk+_0lPBLZsVuQJ?nih7F z*~zkgGVUcA&;=k=Y!|&-y`%JdHKhz~DS3-U^|pX~<BhG=@1MDmT0cGiF?W`E8WbBR zI({ePMq>ZFJ#W+$uFmVQ5IAt+Y&}4}@_X{CGmt(aHG@sHw6%e@LIFc14mv@x%tyQF zrh8X~#}=pVc?fyzsTWacea-t+@BRzu*CaVF`R1&qtcRCF&DJz{I;F|X7Og4kQNB19 z8fA@aK#BTPS+Sy@qPrh=3TwwPkac`DiedJt>|3m?YriJoB*Vl^6$)mMn)YAvif>lb z&5xhCFYi&d{j5-`#G7@W+L*aFVAkIXgqkQ+Lv_#?5LJ=l!iDqQ_o045hEPW010~uS zOOu;}=E*V`W3OYgc<;?Ne^%nj67Tt^mvK8aBrUTI`}brZ3V#IiFxi=J?s~Ir?UIUf zqh?S`neTnf*Uiq0b(zo^yIz^_Cs=LEpNqkfWToTd?;X$mq;`wvQhft9Kle))gYBDo zRnf+K*^vF)gh>T9aypC0Gq=@O(cs}xqEhyvvSYk61+0LGH8489bVkHtg***}Z1Kb- zBn_Z)-o^qs3_Z6XVx=;je#g1JOeTAV)h&DY)0}YdVZOjYj~7sX54o24I+jA}<+0XG z6EsHD<uWSX1s>9V>SkOu>T}$0s*Wj_h=ofk(+k?@Ik;p!0q`$ZH?Ip<mR=7_uanw5 zS4%A7+a0Z(gZF!U;*U|@X=CQ3@8y?pN|V6?Cz+n%Uonne#hgYR!Ca&__XxgAkAWm0 zD8BaxK1Bit9&6`mJu1Tm|2F2@_x5(PKCKD!9^y~WgiM@dcWDE{4w}2K$v+e<Q&Uwo zUb9_pLZ;F4?wFmaYGTbpZGfP7QH*AiI!Pz|F)w-XXu95ii3+NQp=c`mQj%aygn6lK z<eBz9C0{ZmuRjoLo=}?C*p*oxpqtPusny}*$DQ`X!BiYHJL-P!Gq&;3F3`&@$F21s z>h1R}3^xYGx_-7&u<!E*ag4vgVaes;fxxOSOA(jgAN4)S(-slHmc^wQ5wazi7fK8C zvqcjMSPi01qd;QwRF*CQ)Jhohf7!mWIQMut8{)GQHX}t&GV$R@UT_?{JIiL(|H4;P zg_<L=WMm3<h@F*n!BySyERO+yt{I>y9d?-6k;J+R$Grff`ZlImN{Qx@Qtq&eh^pw< z)ZOxi<KYew$bXP^?h+wPx^DZDGdlP&W}Tt)x7ltqSH}Ia9$Ge|qVp@{1tvkVAvK5w z5xfOHH(oHnXN;WoUayv6z4>7H=meORu%I?+`5bgEvhj4%qn`h^K5tY>$lPVV;AX|% zT~e1^i)!dqO35}~+T^rEhA+=!JES<{gJ+W30A^wAdKCQ+_+q0#;ar}zVw&@}kAI-* z@Xm$%a}`e8>6Pn7mnkih_@g{9CUwE_u!-KfMSp5lKt_|rb>LoQ<$mj(e<(=UyIj$e zrqrfF!{n9?Vi2~w93otS$k}ecd?TU)7Vz|k<G${w$+!Sf{VD;-Dnb!Wz&<#EZ6J7m z6Pz{9mGl>of9nm}?;aU{z2xwJJ~dVgm-dq#1!iET|5_^I?r1H6QaKy}+)6Xq{&wmL zDoNBlTeq69l0y842vFq2y_UBuE4_0Uzb&<qF8hs}1kJe45=fJp=!!Z@&7Zti*(+#@ z5Um4|q73_+CGvB#)#uP^X%%fGLe$X|e3@-ntQ0ib08v>a2p-9v|CSV($Mh6-s?vdX zrA7Gnl*1-%-FnLJg}f@Q(TyUJjgxIDAI!&_yADb@{UhIaB(|44A1!Yiu1rN;d>zRD z#mcx3(XBbTk6zE7XS~lNq+a<W0AnlG-XEN$TN(_m;=gItfUwf6dMexF11&8unN&il zYjE=5DC9FbZzNA<D%qVge=x`o|5K8`{HG)@Xen0(K)ZQ}T~zCNxY(hYxo)*rP2aHV zL|OB4gdgZ2)2?_mcsphVnF(;4(9JCOdJ=S8=&il`-9s=*+0$(zKdB6$IVEgQi+^l< zbXk7X;X8fluB+_ZZv!YdcD>FIM4rlWSyQdftj$)8uQQYTF+g(T5PZCw?M^XjI3HbZ zkN$+-E0>pUn1v1f&w(c@8mvbwihmf&090A+XEGVhIrdPx>S89J|9tVjHQ!x+*!m!| zDt_5<0J~DocyqbpI{WAT3ym2any-@$tMo-;vpt)jNlWysqbU-5oSRn&k2O}I3bP}S zrzq;RMK{UkZmSIj=v}~_`j5FDmbg_D+z?lhJ0#lj&ZN!z@wgJ;Nb=tqxq82yUwJTE zI#|$<hI(AuAT~osJ4{zZ2pg`N_@ii=&uzQ?mRz@ZD$M?}gHPdAskg%;=SuMKLjW{r z*N%NOeMNKS2E8gVumCVB8?%k^3^IGb6fLD<s5C+qYW$T_0SC5JTdJoE5JoaD8I4mp zE|C4C9Z<WT>qbXAbwlH*`HS@(090$WJ0qKMI=5LKFEUu|*BR+?R~^Ok6t2#+x7)Ap zL*qN`$oQShLvgW&qm?S>WOoSS&MjVKDbq3%jEwQ_ZZW#-=2kRQ+>L(U_u2{^24X^| z_GS^iE`LDFuca(fOx~l4c+w)0rggE9oJnG?!Qb6<cXEwi;OToqbC!H^EQU{dR}Wa7 z#<<Up8qWA&-R5Ka*~_FZ!_kV~iQBXzz6hRc0V{TByv_IRs>RGFUJ)I0Sgt=-%p7AO z8?5RdNTn7f5@!d45``=gidaDl9^pD8X*N)^8^^v=6K#`h4*@g0d>qX|uR&fW)Wif` z{UzX@f2eFr7OM0TEKdCF>KHy(qWl!Egtb%By#3p_H0@_vDskR)_mwykx2XQ~MzSfi z?-f?X7?x0YS3cYtI;E^k>w!PJ;C<Ja5t~OZD-phXpSS)1QXUH?q)9vDv7?QrM$}<c zV86S@As#i8ZA!gm02B7%uu5r|SUL$8jluou!<eI`^J!M2#ZY(23B9|;R+LS$k9!`` z5fjE=TAPG>cb9$iIJKR9szRgc6>V|i(tuu9nKR{)`g(Zr0aY~JfDXv8|4bX%hzv*y z|F&Af|JrJE#8|1WhqPn1SMJdan|(dbE{nQR?uDLv?S;<o$5%}myqA4^f$tkK=(hL` z1X`$5-!#5x(?k#gmk3D^FPYFc@$F$7*4JZ;cu&TKt*_X;42)h+_wM8sd{0{~L|ulx z_%D&W5+^glUaOxP1Cv1bS7wiPKHp9&5ubvL94OIvnS5hh7(&|z#UTD$;t+LZ4ConQ z4yCC_weEL9MBAgqg}3^S)XUels)jN79@(@6{ZDsp(#mgsD!LMv$rx1b#pQ03eE|Ea zBw8?f31y2al$D$;M$a&cE72>s3J=394F!e=9kYizA@6<cvL}T^zD%S!&9Z=givAGs z<9cZL?wn6BN|30B+j_~}&Xi@LE%JCBECuBd&*1#5p2W~8#5!LsC~mzH`l*;|-aJ7i zfC&Pu>O&Ka0kD@xR%45hisg`4q`_mXC^z>`m}g2i{OrpPMGL2N5PoE<jW9wDB=v|q znznoWYrQ7tz6ZrC8^DWF<HZF^_+`!3&Eub#X017n5;<mN*8WWGD}=&27%Kw}jv0%| zMUg#j6p%<q)RFDpN8`@wbva}I;rJj;s}!!)`9)3+2mqrfp{X*1n0rO_<Al!y4cNKJ zNRQ4+Z$+1-p}@Sqb~7W~KO6cMZy4QldJY^uWIXrt$l=~bDt+>N7-3rctix%z?`B-b zIf_sv*!!#eM3%Z8EBV+Whjwbpp#uvRX+>>WKCktEM;m~rCe-!VxLbwtgzgvxVV?Jv z)5}Lxpw|W}J&MElcv&hl`6CaX-0MA^Je~6TsI#xjd+qTCX8dJ#w;?XkR<CIX%;pw{ z^ralPuoqE7Qznm=j*pBPQ&zf%G2BJ#fgruj>p?_mF^lilFc8jp-JM5Ycd{9^bm!=p ziMY3^uXiO&6<^b*fozWK4n;O%KG2&P^)S=sZZGQNWWvDz_t-3#PXjH09Jsh_&MxEq zh$7{PBr%vjU#ZDIDgNH>)WoRGg%D~X*r(IwJp6&~M-Ks6g$7((Z=;_3-T5EoH>U=8 zzx|F43Opqd{p(m;`$%m9D=%KCS#JQ9NLE`4%erN0oE9O*@Hc&dqMp78lx0Jz>+Uvt zWya=x8w$74ZtXVNgK&{tZ8|gZ<&P=yT-S8{NP6r9xk|8tah#ejsvZbJxHbX~R?7_A zdUcl7`{t~IrgcN$X7nJcUr2=Li0gKP!JKQavV?<!E~DA_WVzynQt~fCZ&{jg!6tJv zz0dKjCbl~a+M>!8up~(3?n9xaz#*+%_8+;fshAHdRi!ju;+FKCvHz~S=fLS4Fx=g& z*Mv3c@)!<#>#jfN%x3a#QF!-Wj4-4x&Rx{BRD?C^$I<!wEHarAbw5f4(1;t9(pOUo zfs#)PSSc0z>}H3~hBut;rVBd&1^-viWr1FcJ^akF+MU&}6XJK+sp_vN%p?N1EkCH! zoWTxx3myS5q_m{dq@{L7=B&-&PR->f>PWd`EJro3bMSk@UqClzj%1h*rvZg0#K>1Y z&9^qbJ5~mBMX^*TB#i3}Q)YCKu5xD;l0C2zv}`qw(mDQI8&>EMyg1lB<HKP2X{0i| zq}YKte%LN3+gVVS#~V;%u)U81T4eFXjhZGlzsy+oYrOy#wh_2Lrdi2#<_pVf>e<~! zqc}4yykYy+q*~mE^N}TL5>u&T&ecO$5nOmXPQ+--FM}1&XF9X|%O^)o?;e}@-nV}Q z0R6JbS*FA6bmZy8*>=elYzVLU3&!8!PiN&AR%IRAlI97_f>Ov7(vTK&KNld1WA+L} zQ~{BtSuA6<#>*xuz%)VA23U?4u%r9Db2ro8+OwvH^24W1Y2(JMqZJ>vl`1{n(iP8H z+n<`;9_NLw0CGm&@>Q38P}r>8rlLgE-6E(A)=a?ybHb%ZXcaKlzSn@}ZHY2I?$V0> zAz(Iu!)kr;_xy6T{>eRYb5z;|C;CwWOH$$|PU#<!1;$QcVhL#SvO?zaLu6cXUaNm5 zwPt{%_x(Y=MZPkHvVb&GrBi<~`kdGV<m=RFf1C=AN-ey?Qx64oVk0WLR^~DM1a_^@ z^f7{VbTk?^?A)9p&TXh1^q_#ZaCTh>%$Ka$nPfi|3j?UKjS&JuR7;rzCfnf{8i*?S z{Tay^>J9yzF%gk`3<U>S`N2%fCPhe5i~!{*VA>ctBUlE&p#8r?eEhc~nW{tu%ds?E zvn;KeW<=qAKvd5|Co9H##d{36_*{p9J2F^nzt63X<FwW!eKvCEu7=cj=~s}{Fkvqd zmh0FZP(kd23dK)d;(l4017Ra;T;>54(AH`pa_0S<*@Ok>(X#ykV(P=vTtnT$>GnFD zB%>3$dg*htL^V-tVSM*3p1h<JPBd$_xR@!O51xiDxADL8PHS%X%gMpL`cGArS^}s; zRTrdiXX#53oG0qq1ug)F$nAP|agh>f#<Fo;2TU95>C56ZT8gqwx7%1!DQi6M<A;JB zb6KlWhTY1dF7*f39kvKk@-1lpdXwM~m?OSg97Xg3aHQI;U-1a69y7xRJI}*S2{y*n zW;VYdy3CUBpYO3v!yP+B^UKMYnfp9JMNCoJ3Sk62K={V(P{hM#$uK>IZ+<>;I}qM2 zTjfe=d#r}zEs4oBN>I=Gq0mG_;OF;P*mEZ&ry?3Z!wA7|qxN4D`jwC=-F>R_t)4Th z@r2LtR|BU;pEVtYu*ENbofWwP&Qbz)%(hSW<p~fUu^tD0F9DGXi{6il)c{1wYo#Uc z<E1jio1mi<bks=aj&%J;qjG#1BM;ImV%E2oQk%!!SMdNFPrQcb{!9N8*&>qOYXr~+ z-CvCAy7~^e4jRd>(p?J2&!9tSD!;rTFI20(RN^)-4QS|NEO9a{FrL3HEeJv{L}atz zMf+SiJ=Z7qZ&M`~{jXB=zqL@5g!o186;8O~Yj@tWd35q3!1PhXA@}UjCB9p~gml}r z7%_A)4nt9%((J(lI9m_oA~G`OeibC9dE}KKNI)mn#z|K|gx8=Jk!(uzfzNUP<^w8S z!J8vXinlv}B%+EpY97PEJQ~5g5{>#~{znmm1NX;q?1YV1<H%k-T60PzO;N(r8rQBo zKOFHN#lI+D1|b%uHRIJ_Yxc<`^Xy<lllGx3!rMufOKANa2g5lwcc7@yD4!HnJ!!SJ zeh@jM=eQfaOkDRq)dP*`fn3E?Uj7JRdJ;4IG*&^64B#%*tRr*3$1GqdNMFD*O_{we z(6{9;y4z8c<-MxZ=m?_ibf0PcLE~Kcw}wbV&j3db<^w~C`-`&h!bD}f56HBq-cQ-- zDbaI@+x?_?kB>0<DuYJc`}Ed;i0AF-qq+VL`JweCjk0$dMq)Yvwfaq^w%UnTbY8YR z-t;%PrW>aLo_wBo9hP7OEyI`ER$h$(y5rr*J4bg~)-uIWrJC;SyuWf1V9hChNIVYj z`(d9L9-jy3s`!s`)Us|jYXk@`$ECCwaBKBXV=G>%5Vr>Z)JV*yAi`4$o!ME`T@{KI zMzU>VicFnDP67=Wxyg;0gW&<ac7tQfmq+N8YLrxlFBr25GDZ-9;GbIX__vf&)a(a5 zl|lSbY(ZwNWd{2Q-EXsgwR?0@(eKQN-@J16>i-#PNAZ#j@^~nOthyof$2-BY$MJhs z*?6jc_W%2t>7LE6`-I$c8`5E^&c6R`c+1b=z{G5IP3-@4e@Y{%$IM0NmAz$_>UoKI z!Q>5KJwIIL`vpF0KVH@&(loV@nzb-mqD$s96zzEAAk4it=qltN@b*r?>NLYO8;*8v zIkJ;@zKLcTP`utwbG+JhzsP>M?l>9TU@_g3ZpSpd5Slt95Y?3^TbmP6Y}CM`DOd!$ z`D744g?0J=Rbc^G15=e!R!phSZs{Oyu)`>trgQub7aE`m`7~~;vmP4P$q(8O_U_Q% zEqRcBG0Fq@NpF`Dp2NkbD6k!-zadKa8uTf1;+g4~LC}5jr1{%YO{f0?=7CIQUIKPK zT^rnp6o21MHWX}d!69uMDZs1K5Wm%Yjx$OgEj_5G9p9h*^^9N$LX@BX(YRkI*7QJ2 zyK&e5@=MblS#zq;c_*@;ciSRfc6R#LX@nfz=LY?GeL7!%_2J(1Ol20FZt4{bh6Ysx z8V$59ma<}|*eb&T%>o6^COs0gzdMMkSiM|%uH<F<{h4mRRi}|PvfsF^-5r)k@sm7v z7O1(bO^q+r@8<*N(<<15;A^)fQ+gEEhBGmR^n>GT5o9)@$qf{wjiZXf<I(b{nL9?3 z*`u<B%e3Js8uv```OHsgAgR@k;Q@Ot*iJZ|zG2qK)te>&8xCjhz<1srb=!OkiEu%} zFcRB$WLeQtDG&gt#NQmoPN#6x$&eV(swr1r9Yib>XP+zTjJiB#=K%Za$H;!d5_g!9 zMfI{z5AKeWLH`1{z?XxDdirK3g?#0*>Hx6e<GBQ$cUF@#F%hh0t&{^+ujUu|bXy-a z>hzDtSMa!MkP`*cb#KL3y?8@eWsNwBI;n|oc;aD$pxsjE!d@Kk81(I@xMBWlPxb!q z>lq=!c&Ty;CT96J!nZ{)%~<_OU$aG<4e!N6%q6c&=Psh_Yuk;N=L>;XXKx&bWzX5H z<j%|@68SJDVTqDU2<C<gw84<F%@t~~=t0G@c-+7ngMI5I&)FrFjZUNI7ka!MLOZ(< ze|K;Vm#Rv>2>g8pfw>zlSUzrk{a+-P2i-h)CLwSH*ar<5dM>>K0P@VUpT^G1X!27# zX)QpZwjMFJbUzRt$h+uJ(Y%UA&c?E+j{eIE(;bW;C`&2~fd~u=(e=QbvsWzu27`l= z6s_<W*h;<<f2040STuvYR0Ypc9cm`%wg@~v<iWOEel3U?9i(0E6}KF%F7SjpTZP>( zUw75$Q+(Cs!8(|||H)YjbI%ekxH3U|MZB{pX$rI5Oj1ej2WN-HnNVen$1s3C!6!X@ zD^9Q=;h-AUO?kfyD)~xA@yB2E9)}qkjjrXdI4$e37B#5yCJTt3_K*VM%AS>5;xv<) zMPWK#{{CqW>dm^=7ZnIx***~i(segk1YLV~`yB^@nE-67Cu#OBso*Mh(WJ&3Bq7yQ zX2o_Hf_n7jn#^{qoAtERM6ZYA8t<31p6_pqp59k|;6z7vw<(aO$Y~w+83K`j2TH({ z#axq=y0_tN-n<T_uE*ydf0i8%s62H4{|;Q55H>7`!;sg_Br0xZo5IiOjps8I*zW-B zK#zBlZ|-TpG0n^F?a>jYRi<%P^}Kr+Zm9-L*T`6}GM}EU9KJ9#np%uJn$-da%j!xA zZ!!{KIE#D)nh1F51DZzWR28Ok|LS&#Ufsa5pPm?;CDPM~HO_s@bC0jiqFL>e)FNxI zTTHb-F1u+xE+d%yT7&E;6{fCCJGO^J&<mr%KuK;{iZa=i<dKetnA+E`ZLEO~;&?`A zV}!Lu%VOnpe_lY#lR6wN)?9S=gxU+63+8|aWQN5sCC1>0`NaZrks3y=`ri%@pyYfm zhZS6pMU=~ON$s{23&kLg;lun14;ubv)(Z*V5QZq!yp2$;j#jEf97zqZek8N{PZ>r) zntD_Zn{+UW+EGXI|H2iVAgl}d!<V^jcM4#7jMWQ}u&|TGzQ;eTw))Kl{$fGu8U#ko zs{$Sl1Rjlx)WyZAc`UXP)6$YYw$^bsn)9J2lRa85V7*zV!tv7*0Znmc+?`1Qj{)rE z_g{aWelJXjn&$?mB`cO}r}fB!@=+F`WQ=6n5&K#}FQ-~k#oc1`w)$`1&Rt6yBDi>n zRqaN}bVvWLDr-8!Z_vLvLLTo;arXvY3(_ui9yG~$9E>nEu`{G~S`4YS*S9)4p<r6g zN~%yPGIy(z0@HvCe1>fN4Q`rlaoP`!)MPzQS}FisY}1iFY1G4Lbq1RNCUJNA&TBum zOh$h{x*?TsT%2?m_XhM*$@#Jz{uA|`;-74f<jp0@s_JxVDXD+qGg*B+Z3ZFy|FQSP z|IG_0p3k|OUjIEYW^Rz6I@A}V!m4$tFpACFoJ(So4<FbaS3oV<hZ;SX+X{tFRX|eu z=O@XqWHu^sjiKb0G<mvsO^jVdZS}11Ka9f_{|N#@GGa#EL$)7ZfFKkp7fFa*eQnBk zr6p8*{~Tq@*G+bOe+mr7#Lk+F;wU|?tpnsqPnYHAC;a=b341@ohX?MbumbJ9=)NNC zxTbZLUMdHSyD>&FE?C%wz65jOPvUbvNF79Yj03ZDzYTo=Iha<5F_k_nBO(Z-BM7Eb z1zibM1SK)eWz**D0w=Ee?^O*qPP8UEnOta}&zFv!v+IG9My%>TZ<brwvQdsk0(FHB zE!DK#lTg!uf!>mpPga?8edX!fWjT5k;ma^f;D<<5&g0s1TU(*dVl`Bnm5`5$JQD06 zg5Vu>x@NLr>699kA3@?VK-~!UgmsB?na!=>yg*1f<FL(of0r6G=)t-$yLH#lQN>*# z=<Uw@{KEa+hx~aYMK~I;fj_zo{+jhqV*@n1%5*HmrADm$j^F=IzDJN|*F*LUH=SmN zZJ)XOLZK9%A&I2<2W3R)QsxgqOso0Wk7D?J;ZS>szb2v*Us0s<<3o!{@<PaHd~Z1` z^m_siEy>Yufo$0#b(o#zPc@JW<GWbGY+yu>b|xPp!?AV0NVBbg_pO>u>hsDF)@Jl& z(SfzNaM;_Y=BuLL)kvH0D~CGEn=8L*e=>=cs8nOY)b0GzNPqebNhjd!)JO61`DJLm z&&#FHAK{bH2_W@~dc93RHl#zuQ(MbnhGQEdRw88cxmAR9K!%)LkHzTQ!7OwbiT!)S zmHr=ZIP(rep(v;XqFXn3(S=ZsgLPJiBX2#veOoU+99t>ZmEkDQqt`-y<VlP4COBmO zYiPv5KN#4#f7U<Yhd0UR9p|d2>MEcn`>Q~#zxqjgh!qS1B7yZuDf&g`9S3$~Pn}WK z@%-%wp-kg`bj4qX$eSBA?lyzo88{#iCu5P`9FUxRcwBe{IH#^vc8O%XJS3HlUpFnt z*+W*O{Gdlzn~lx3$rQr=%Zf?k(kkQ!pJjKz0LA<05~&*C`&|V-Hq3a4#<COKv6(-C z6B+a>>GR>)2c0Bm%6Qw0-?Y6%i3z)J1)D@5G?84@#cYs6f9`#py-J5{Fo4%X+>rf> ziSXGBYB}(SFVDGY`7)ROcB<YQ|I=F6m)HF)zUM%q&)sc1a-AUNT8-7({y6e#QCtmD z2_=LQdZ0|S4n%0RNXk<M#agrBDJ40vwTVA5d}Xd!yMf)|p!mrAj?b;a_H6##!=Ut8 ziDmKB!{hmRG*k02E{(##oeoL7I}^$(Q{ZHp3lPGp;N^b$x}H8R?47VBf0`Rr$>@}) z3O6mraB`K=>Nu%M!3WSzSKffCJvWcO;o)?mu%@GV;vi+5yax7%f929GgEpskZc0Y~ zA%Q_3jSO~`Vqo;eEr0IB+9g1s>~;K2Q;vwU%cN%?apfV;Nj=pOVSAY9Exfbc{iCO0 z<^GP4^CS6x#vF9f%h1}R<MK;`VO%g7{^sB${+g#6WQTHcq!PT1h;+*KNLF2Jt#Q0f zn*+r*lTK?qe+E~en~Gk{bS*psU#e0<Aw&dA?fSbhU?wSRAMy-OJ#WQ*3MCU&L~u=K zxmKO!Q8N0Xh|dUP`AlIAf7<0MV^hDzH#buCKNv*Rvh;gwZes8nyCS6Qf-Ue8sEX6W z!b*n3l2o&C&=9i_KBu8n0V-j?6hY?HrNxz^*nYhaINNU0QG)<+%3&shg`9pZP@S~= zu&dc2d4@jH)NYCav^~C`w{P2ON4Xxsb7iuYR?Ql!o(K12lUYo&Ov+}Wu>RO{A{7Fs zw$X)g;ce4KU5(|X%-d=<d-BW*Y%icK%5A%(m_Ezq^%Mzckvqnas$?A&^O@M{GJfDo z64myLzD!|FhDo%#c~8I%47ea%K4d;a`4B!#Hn#vpRQosr$9d0O;FGW`c$d$6ZT+{8 z4-edp_uC`r_y$#u1`*-t@2tO%G1IO(!0=dk^xrVYxW0n-(eW>MGpW*T)IMy5SWdmw zAcI#Cy7wUy^}Ijnt+GEgChh^EQ!jpHjc=&4Sv;z+Wla#P+jzy%SJ+;J1iu~^uASdX ziysD?e%_r<*FbyjT`X(g$c?pS7b}G>>&tA2-~mE6_um4?R^~u1zo%qr0OU+Xn$OLL zH=CY#GImVVdnpq?dtVC7R>ZtQ&z&&oF-==kgut_fN<mj7J|^g<*09iFF8d^|FfN*# zExZTgn<*BOAikBZyZ;NV7|R5%x_C-)j=owdI6=@c$HoQDf#RUPr77iUiWe^{B{hx0 zg}D^D!oqhQ9p-6|j_VkE!~{<z?3hG|@XI0EXx<cnv!G64ZIs9^f-6PGi5K#kmxNv^ zuB?sLcluh!SJWUI;kDMnRiR2&+Weq<$|1noqWAo>X$kptd7X5_P4*;+9{(c@{Kkf; z%XBbRXDK8!2NN<TqUO=UyC5$yfsjxT(ecIfK_KjVhd9Hs!;=!iM}_wd%sb<5Gm*gc zyHe0*&Efm+gB=qCPKaQ0xG_?O%7zb(1WPjo$y%CEc2qMvbR1MnObqA}-_R=nZ;P0B zNJax?MJ8qEFG-rGQe9+>-MLZSfXvu36lE&rviQMhFbusfs)pSQzhS=?^>S9b%o1AF zc39p@^77v|ypLxc@}2&(1~$FtdAYvKo!}>Je?eR_MsRzNncgjSLd#F?KKd}H+Kd;2 zD`muEFrT9~-YlL2<oh&i+YZOx<#jpQ_hrDC?6|eGn7M9sQg1NcsP$XVd>UMDpDhgq zL_g~)Fov3LJxU=nZoV;7<-%{B(7>Z?H~DV1XuYv$HuY)*ya-vp1KPJB<LsznVeFF* z#FrW^w1g{7V?;aqA`fd~V`D6gz4}Kc(2qW5>8evsSXMCW75F?veDI0u)?fX#-d!fk zHC`DD6_+dA)<4~$rTPP7C8ya{SCs;lzgvPQi)GS|1k6y=7?pFAC5nbtZi<8<g;I+C zRMa(#jRpR$e}|30H^U{z*S{VB2LBZYB?LgZLue2e@x7mh9?Tw71_|CI(PsV`pi6s~ zV>tQ)(yY)_zS@jiHGMc)-)K7CcUX2#7r?Xa<hiSX=7`ea15EO+`1hOoxctmkB4zV_ zf(2!4R(TxK9bjaXVdkuHOD&snR=j=oC%+3TPCoh}(lwu2ybxVppRy(iukucYNYlP> zg4W^_c&hpH5GKfFRPw81sG?)%{c@lRSW`mDti-@dlkf^+QA_^c?j{~;<KtaTKgBkq zm4Ag!sb!S91j{fEqLnMU%dRg@|7Bz#0?g!4oe|L?`|g)6JpV~RDhS<Z7csN$cA#%u z-D#@Mi0Hl&r7Jhq`qut8PZ)#U5}Y!#IuyE`jf9W7!Dh%8FaYbVR=>6ZlZh`2&?5!P z_SiboBF{r!S`2)FZiTslV}cuKuik-3^*W2Q;SX~bt$U4jv9VWFU50if_C!1VcX)dV z@>!^;@G-wouCoEs<;+>HxBbfW`+>Mw3nBqD2TigXiJvvIoAVvwgo{mk89>OOEqT(z z;VRCN!*&S-_C)-d{)@t_uNo8gS0#ucgPHm*P5mMdZ7#>HP}KDfJ%=Iic1mz(d~ffI zTZT-I>k0wy)3Yvu;EG=q8|=6Y(0wud5^hw~8Kg+&2~-9CA~TC(Q>G~vm|Ly4PG9YH zmgi5A>bC}nOaImPwf{%mXdR`V@QLei^K_><chQIfjs5S0dWYlX?H%Xtu33}xv&%(M z4f2+f*=GTu1MT_2dQekxa`}j(%V*jiFF&AZQEup%KysOC3})d$C2g46PvhNX6rb7R zw1{5Eu{FRU;HppW6xMN6u26NlzPn)c_O@-6W!=b+Zo>8hFRqXybh-hRq3D^L?o|53 zkpF1JT(6ny%&aP74(Qjm>p>C^X3_N(ZhaDmGp$;8y}B%DuA6F*O?%Van#BD;C)JEf z=)~RH8-jh#cKQ)ud9*K{*A>NV+_`@Q#FI3N=CcL#fKhg0d&oz<wF2jI84JN8t?P^4 z1YW0lbJd?=JmoYR(6!8BrbrZaA45!s7Z3!?qI<tq3($NU>}-U|rqnEGG)OT4gb$#Y zeq7_5wKqaNs5I&YVi#V{53FCm(=UZ<MQ001mbI5FN8GD-fX)3;<k3hoDFrwKYRfh8 zAak`ko`rC1-+u*rLFfMxAOEK{R?laMF!L3@W5w&Hwg~cQeWt<Yp~FaR%lRC8!rJYj zifG7v=u*+DT^Sp|Woxw}YN0-ITaiT&(3IkQE>N+mR5T8sH!oEi`Mgqd@%QhE(}sZS zn=N6+#~yx>J`DsGrS3f7=Wo$J$xN_wq$D4*nz+_8S!add@P*I3DUnT5Tuzu3?}UG7 zKH}S_CPd<=1@SKm2dG2l+X3hvT*-)-p9+a!VeoVfgW1HY5IV~d53?ji9d?g1i2-SU zD**Qm1773phD|E)Z%p}OavnV6eDz`Tlf>kNs1Ye=BTE3C%O@nrFMc4i^7<v|GJGi} zYqs?}k~?e997p=}Yvj#8yl$Mg$45E<FdLM62N3>S^5!glGB5$)jkjfpbztE>5--c_ z-DHlrJqe=>fWWF<YmQvIP!>ct$fW}~8t3#r9Hsy;1H^%%;S=;I^OCtwTd1+ER>99| zT(E3wmEzv{|F!_N=Km8TYKL9?$0$Fu^CGl%=~P~!Q~<l&#KAJU+D^EVYFgtnHK~Fz z^W2%iB6zyXA(Zn3EGShM>7m~x75Z;Ff42XitU!>G3gad)Ps9LIws`6M4KyXcbQ!*M z#4TpN_vcovF`v=BiFZ8To|Z5@JX3g+33HMLQ^0;>c>Zi2o@Lfok}wuryBNODF!jsD zTinKl<2-HB+<deJW=YQ7ZELNWw<bu<wZg1ghAzk2p<&rvEO*hQcDpw>eh^9U3Q5*e zObSRyC+I7egoN%&iN))fV-%wF74!W!Tt{ACnRl!E{bT6`iIFGN$Z}=r69s{N?^RD- zggqHvCL|K)$|R?Ok(Ci7`R`hP5+Mj;6s`k(KlzCn@gFo5s$7=e&ZrO41AHFdF9cZT zl^&!Y#!kmp-3M}up6=5sN-DWkfE}RIR8+JwtZ!hc!0^(}aqhjwQt|L;8e(owlS?YD zj_=%5*X`HPW`n!&8W&_XyxzQCInv5Oe;{mrw?LkbPr7R@kCg5_Q7E<YC>v1+pQJ^5 zX0>(v1Admu0`h!Ss%A4)sB_N))X5a?mdnLnQEyJ)K3;X@{3~c=$4#2^W0-3;(e}Ge zUhZ_+Hf;sUB9G1Qsa6=Hv`!FUVC)d9Z}*J8f-d_||MpDEf07cW^D~aBi|r;YTmR|r zh%=~^ztWwK-DdEiE^4kSquv7s3tX@-IO&(_?_vHXiMGF13f~IA+`my@>p0GDS9prE zz8|eW*R`p5RjeH<aYw%Cd<=Vjr^n@W<ZDz=Z`;A#l?c+tAS;+&U?{W?L$i&a+!kb7 z*oc0tBM=Xxf|{R)R}qd=L~FrR+D?)$-#rV24wk~LgU*2|oQ~9LekwGG5V+vEO8YWI z=sm3OAr$yUy?WecZ<`KbVYC-kmnKp`6$6T?$4rbq6Ga_i$Z$K>v+vtJWYzMyHD934 zsQ1h6>g<Kp<E=~_ia@rUYuaLwa=VI~$}B^31_CH%xx$ALH8sC9d~qX1zDT41f3~%v z@;NGKqI@qPxv>s&R<gO*?5Kx|a5HjciiGI)E?vlJJ=9}L%7Hy);hiu4+-<v`col)> ztMgE$?U({)ST#yBR5-V5Z!57F^|#9i986l8z!B==p8`;HQqq<V;AS2}+pDHoBC+vo zj&U$eWePd1ETL``q{9jf@zrEc2M|h9lf>bl!LhE<AQe&MSrw6=svv};lYK;8B$%}g z#reT71_akW;L@oJ=Y#MfR1%XD+e7cP4L7sjnD#blQi<kt)5Cv0b!3zYxc2qi-rGJ7 z<ki&-oCODHC0eZq4azUeT}8juFVF9eu7=X}N;dh`NoNeu2z#6bl+^KkqI<Q!&@4Ci z89-#5@!O-ksQ8)LGP1Ip!Se0cj$upUqFsVK&x8%bOotR5g`T{Kdfc$rST^Nn0}@GA zh=YHUt++H*$2MP+MUIQ080DbIo#b>p<tJ64ZP*4HWIQ~m^00uX;$R&bZ0jQ)i;}Lp zB~H=CU|Vn3mHf0oI>Iw=esU3X`5=<|UF0My%9$I9LW>_3wbJ>FG}INHKMJvImb6-o zwLX?ruzA}y@!#;OXE=4o3>I+Lr;dooGhHv}_dsM_@y7gJ$yodaiKgd5BX{QGT54Ts zESXdIE4@4#YG3!~+Yx%jz?XM*V4KRJ5}%AUTg^wIcG3)QRn(JBlIj06rMAFGMUL?a z-r0jBtVhVuTX7#3<|Ss7D}$%Uu^=$)1m-V8J`Jtv|Ds8K5%tjDNJfV?d}T6psefaT z?l>ypIv`|C^jd@DG)iUAV9JvLh8T2|86lc#y`7i1azyDdIc#B-&ju8<*NB&~&dn@4 zvW(WVSHt-?)_hf_r3t1CO{|~iJo#qYMz>avz2TwrGM1jT&I}%h$T$o3yS~}Os&^zr zs)^0^FEXx%_n4xYF^J)t0q^BUf$|TAPjk1uw8Oe^<0V0{6UG^9{PD7#R95y|j2ZK; z3(y%Vd_ua>=ul{}b2Nzxrsi+qti%=R>{O3?VLEDz8=E<)m`Qw0H>Wn36gnL6b*Ns` z&cZ<l2L}6e<i}OF0z`db#M5GyCCA&<kCXd?-<eg!6?cqj`4PU)q*B>`7I<ayG<-hR zQ4harC6?;?=q_h?$Z*r<pbeMbxl=W*z`x-WBQ9rCAQ&(o+d`VV0FN_T_J5eB^zY>` z{BF2R%2a`!c|^44MGlegGUl&~!ROHb(?&rmb7!#sk^y5e1`bq(67z{O*O26gDrz1H zIa7ZAu|_%u)42O4S5P7OZ!fgjOf_G8tR7qg%AJZwbQ2nRX`)#Nsetdg>k}@Pt_iG# z^I%#=nxe}@N9<b&o`oI{>nTa?^^fnZZ0Kpu`kwf2@Zo8q)*vmbL?H2NW6PG{SRwsQ z8*{5#>QHp*P9GWC%06r{Ao<2=T4Z+el=;2v5vzO$=X2>V4|{%cd{&#)uz6%{kof^! zm|Kaco4~x;{J3ayAQ~7OoWEs&Qi3Uy0dtXby(Y|bZisX*U2X$A_=Jv4>QG0(1bhLk z1}1VxJMU@GQYDTUX?P>_Mr6gh7ue{0CnM-gMQ;3Ewdv~7>GLR<*F|8d6j9of_8Z>k zgOO5LJG9XAuY%?owl#vH-I(Z+#_*2A3+EmM2J@A^jvOGI&d;NvUeANvA^P745<s@? z?_oSV=TI{FTfZ_o+kN`aNXa|!UrN@ueaG9Q)Qt}fR}q9ECTS=V8P`0UiGs*(D&&ck z@gUBVd@g0E6GFD)j$k?bQ2ge149VDyN2RPp-tEM-#n^jF{?rOZ4reQ{?%S0eI@C7D z{91#YqoU^CdO$$XU;9nE5e%)=Q1H4zz*Nx`wS?|i#P<D^v=EsMPtg5bi0U3S@0<3R z#h|YHeBrf-j%7{Kwh1JbQOf!7s5AYBlm1HQBID`iT7=ij=OIZ4yH83Gq3|^D<B>VD zJ^Bu3u@%nY8uvx+MTQ!`Cw2H=`^y5e7QG#+dcrP=mvRx^t5uQL+k_Os=fOY0%(jN3 zD|JdWlWV6y$>u}GNc;RW-ZmNI;#4tDL?d?`5iJ9uJW1C#PO&x#Jk{zs&{f35k`l4| zTu&V#hSkWFAF}=2f3AeYe&0H$T9U;EZnhai21M57VC)X-<W?<VdJF34pUytxQA)kZ zV}&J0))(vk=!~W+H)3(vQf*IbyWBvSY`1(qut^QKCSjWD0iODWRFs(QZ|L{(ui~h- z4$}Cz2f<RVhMMS6_N$azi5HDOpog%dS<HqaG^t1fVvZpq6UC+fn34RTr@4<<1v4!N zp-J5j<qNMvrLfwHo@(xWC01${EME<v=OWFe>BMzZl@0?T8`Ci;|1>w$jAH;TUHNt( z4y?r5Qx8WQlV!&zr<pLN>Zex7KV9mWmB;0mcy7B~8xM5wasGC4(x&j-4aSd^W(>BL zB`{jjSNjXBrhjr`T6XMEy_X<9t<<4RMul)6=oJxEv^s6Kj_%kNVc}+eKR77!GSzRt zNrCv4NA+_Cz6{0_Efd|KI3ozl_7lv6dKBoh*P1OFRwD0K>DI=P%H1MN(HdK4Nvr)y zYsUI6+~Kx9)OaxC4t>IhDH*Gc!gj<+0H=KT-73?rK_R7yx<_GoD<z>kYUq`Gha&5@ zP+t|4Bs+tIya@?(-#}Ey#Ku!k%$G)eX6r5mDQ{2rwGcJrA!Yxn2K<{g9txw4#MVM< zbxCO3W>M@=wAF1z85&v^HkDvj=2DVgH@M!tpQU6aMzK_%4L5IULcgt?XVmXUO^$td zTnt}ybuPRZ<FHtvu0DI~s-L_)d1|+nHXZ5}kJ8`z=t{3tUKcwh*q+GWh3MY%8`=r# zC<?F9kMSL9-`pe3XDYC%ecgpj#E}z~_Gnb`5Su^>W9rJ5Q0-=AmLy9Ixe}EOoY*f* z1oOAHkGW3bq3x?LiO-H7D|nPnF~Q712=%Sp(s}|h|7=G)=!mj!554aUuIkm)<%nWA zA<Py=9Tr_^Q07L^Pj5tCxCZhQFKr9kiAvqV^La~uE3vXO7z9RBQRi!S3(!QQ72?t2 zQm<Kl?)*N&3TB4ur$nPLR{k6kB1rB(2BA$#GL|mE-7QK8%??FRjTG-vY(_=Y0p%;x ze2HC7?bBf*u*~8_#N=5ov?&`cQC=Oj1kfs%JD-aI=gz1l4ZN&_Z;;H%_$Hgo{$%-I z2k9aUR&DjU0_Oqv)0}F%9o_nQ=2)t}x@P1%`JrwFZkQaOaMCej);|)Y<zM{m7(q_` z=@DOalbuzYLK(DKws)|RpMj4EL_b}cO$X{Rd8)~g*Q<u-G*mIdh7XLrSCz#bmBMzx zc!?m~zy0D*-)>;mFl!3q@JuE)rXKchcO?ET8$Fh)93FI>U|El&jxEDQUp^2cRWGni zMuOBIOBI*axS`eZsuwl+YM-^N#1lD?N-fX^5>86mH$trr8Etocad9QHZl;FE$eKqC zlOWBbCMi$)svVg|&9&_TNbM-2Q3C7L=gY)W)g2Rywpq_tx^a;`;R{If!A$kokOpp8 zvupBZ#;vkd-a+cPhMLEPTzFgbZJQz}rm#aNl%99625<^w^HEH~U(rDFOY(<iR2Kx@ z)o@!Z6ry2V5J9{M)susw>gs!ZKLmcvrC<f=fJlc6s9}9WqA9><``{8Vhw9BXSl$Zl z$I%@;Nf+F;Z=#4oL(R{4WGwpaSB&K!g~YQ*O@axoOgKq%Vjr$20?A%t?OO~8jPbQS zHrqyXM95#2k^QZqor9B5KVfL{{`x85_Wu11*A8aHCyr~917=IUzg`wG6u&b_SM==T zhqSx&&@r-8#hb&%vm;R|XdrtKFoX}zX^fTjzA&@?p6S%s%9_WyHc{oS&6!w!HN2xM z=MFo9+QGJNX{vo1p)h+p++RkoB=9m_yux8W;??hAQCb+&sKfsf8lKoJ!_kKNJ&DXx zLuh+<%UD~Hd1G9s*5gTWATIv#)JZbogU2vMk~v4cIa-K9!c>MDC^tvgG(VSz6{g2m zqlifPH7dMMHR?O?o3iGV^b8dx3Gkz@G<)3U`ewDe{Q0{^Q|0c2gr(DivFhJY#a8rn z4)<HEl8Q7es;Mc#(4JO`@OK}vKQ%{B2zBiBM2#3%!!VUq1Cgz|f-8mF*6Nv3om7eR zgY8r0E>dZiSxLuLd-ILkl*RQ`ucDJ78A0r_nysLlO0~_R`r*^+gqU7Vx`iU^sKp_5 z^&ig*`f!E4+rs>M1So~};-;d}MxTTpIhD}Lf@%wUWhc+~Gx&^&gguC+9)D~KDm@5_ zKJK`WGE0ZCj4&N5G8>CQ=fY`rNWL-Q+E>(7I~%!Ro~gL)X*kQY8AvNx?_kX_N8aw# z-z=WCx?0V)AW!u3Krl!kfpx@E#W%_Yy;8`1&f+XI_K5Vuupzu`iQi2`aZERXs0Tr7 z`2QGN`gR#@n_KV{yNnHjfO`_hbv{Y@7D4>S(P>pW#5P@KaizP@uCjnl@V8G_-in%6 z{gA%^AD7wlb+3me?}UvKI$qITC#vPM>c=P1BAjS%o=;C(?C^9}#qxK7wq8^__m`fR zruN>K&#7@XDc20%$EWowZBrgUPAQD)@Wvf1Njr~H;%aB$A%sDsDH52S>@C+mK2eBW zSmi4blhNMJi}6Z4Q6d6<QEZH|E1+(_ni~!}?JgLIpD8h<gm$Wq_E2p-=L5r99;!{T zp<QEJQU4cXZ`oE?w`~gscXxMpf(Ca83rKKx4Z+=Q;SRwG9^Bn!;cmg*3GOa;<=y+- z+UL~0Pt_loALbl$^wyj7j&D{4WL;15hEFT6TX`HK8PNWFY>5Al$A+?yqvB81`~AC7 zEiOp=gmeCOFIe;Z_=a@{W7W4xV@`xLYu)dJ6R@A%HIXq^1=aZFhsZbsyo+@Bt||_e zI53T1FnqU{%;u5e1L5hxwt`p94a!~2%gQxUm)m<3mZy?%qf@ju9B_T`_)9x|%BRnC z4*~uNE}sczKcBN%9K9;QQ{)D&B+0uG91=DyJy!cWS5ef6HleeJeG0HlC{h&0?|h&Q z2~%gP^qh))7HY%t0`9`_*v~|J03M+s%ZcP9^dAO<7Czy<QAAq(s)yR36uM76(NPU( zi^x<c)RXI_z}gJ^4Ak3DOB}Hk;Pr=NN;NWtu|RL~?Pp59#QsD3gIb-7QE_~kT-pDm zUGQ07RHrdDdF=5m6?V|h6@6;kjdfqp`)=TqH~d|Z#l8#K8kgZ6?RUhGMWuAU%!Xeu z0lhY)Y6*Y8ABc84^#oJ@E;sE~L})#rD#{M*Yj|lp?ppRm_KYK2)r?k(twH~!F4&DE z20Z+Lt!G=T<D9f+c(1LB4WX`7lAzBVDpIMjZRDB%D{0KMqdZt^*_*_(9zc_}Ns7mL z2t8ab*Y94e-y$6u;4=Lq5!w3J2u(U3iY-RDCy~pk-}p+tQIGkU(8KgNcXqq!h0Gfd z`ZYM1t??Q(H(FG&h(K@%qMyh+Zmd5_2n%PK-lk5$mD?Y5z@81n5k5dd&1dO9FiF%n zX|M3y^H7?2UX4!Nn66w#vP3!--G)LrCyF-2XCZ%CD0zuqr1RYWskBl9Yklwp#W&CN z)raZm`cZ;tN9p1;iF&J?O<4kRyY3o6O?E&I<_E|Vc=NoQ`_-azLxD&x4`+~#>$(=4 zCEY?y3rkL!A#WQ<EM~S02n$CyG#qM{@s|eVp_-t&1y_36&+E<F;m_)>q<Z*h`(-vh zuEJS7?$oOx9wvn+Com8=#CjVZ@C)P(nZd^}jV*D}?3<_I^cF55u3$>!E>llRs8RWQ znsbc!!cV1e6p|1oWMUNZKM(M7;GuyS-^A(NkM`IOp6cZ}%ayFzey-s{*l_cFdApRT z^NP_D_MBE9<LYKs(Vzcd^L=-VCqZ3)2-3>3G4BfWTONJWG9|zqm-u`(N~lVmd3k2< z7ffo$-EbtF-w9ceu*)BG$;Q~<<=uUar_bVv0l8VLX4U+;OT1H+qMV^~i8M|H`xi3h z)f(al)%uu8fcd4<1Wl1lQO+b&@qw2A_x*0F$)vU>ah>O9NLkyTN$+hQjPUhdmCFQA zPm7r7Jhs}wC<Ov3E4|%wruOt<I@&=`?FY{YCq+yN1^Ur9;w{QPhi${8$<6@j-bk_i z);)om1eSTQxo+^lw`1dX?0qVDbZ=b(3#7*B8lq3}22?U_p+bvr#UH2FIR7@Qvr8d@ zXae*a+2YMm)<#WqG+^7+7iQDn2*6+}ad<E&_g4pl@*O}8MXmzlE)DaRJK*F=T~~?~ zT`^cbS045U_2qaflNZ4H3dYHlSF)~`+GIqXua9jvCXG5-cXPT>PKdhOn^h>AY>VQ7 zdVigi!hWJzDsk;E4#)o7+d%|F4IESQ(K6BAh`aCQe;_VDqFST#{WJfREyQ^+A{KqG zq^;86{CrjMOp~4@4bWk?nN8iKRec;A`^*FnKZirhDpo8)j-HZN)ct!YnCLDlz3b0e z#Qtb@>TQSqT#*}rn{<l9!w7!a#I_SMxan2b5Y^N(n)|{@apkFq>#71CRi2?uO~*?M zMq2S%yXuo*s$s(B(q3&$H31>GI~e(s7^k%|2WhEB<+QP(9@idqZ>b2Ny0TvNiir3W zXMf3<zpBOBRY<vQyIp|R<QZhb=Ke<|1)2Z#>GDA+M#!LA5$SipKtzrMeK~xw)V|CN z<*=LsalbQynhfT1Nck2wf%;)T^$LU&kJ0*a$tzJzN2j{5#$}iAT_?=PkE_K^w_rj6 zjE6<Xig>XNWf_JS<Ybe=#QSu&rr;Fb+=Q5iud)iBw_@L0qOB>Z<+6A2eHY^#Qq!Qz z$e^B%Ni$vUZ-@qPITVQ=qcwY&YRh2gOM*X|4Vvom^@ZBg%hgI7JJV$-vusK%R4MF( zM~zUUQ;BBAea*IW5FcXR#O^c`+D<r~ImJuBCV<5AyZLV0mDAiUf(>qh4FWulDct?^ z?IXKf)xet6t<of`dx0+r?jO)^t@<WQmtT+l1SlzuCghmf{EB8t<y;wG^vpnH&2*%n z+Ym!*YI{^%%2<7!2k<HhJWdSsJ?|gcGUu&U8<ZwV*=_zlW`(1^DV>T}<=ZbQ@ip{3 zDGz!7xr%~qHi#@VV8=*GmA9*21^EB6!8>~A3q=b3Q{E41&8aJ;8$xmn6ctbK?B^U| zuE;h9-6oGq%^VRaPk*~4<HKL+3*mmKcs(z%_CnFRIq)jVa+2V7CF{+EW=@#08=J{> z;xVStDVKnklgB$cNz&AC`mE`FjA;xSb~d+PVB+e>oJZBEZZWtWk%wg#J_tz5?(pCY z)X{R1GtnONzRriSo6!>oM50M=uLjWt+x49^v=?Wf26N3k3y`?ckBPbgjuQF~zTC51 zBHZ2*BUr|gDdTNPDv4ih1JGH37yMMvL!1Dl@`JAPpcYd<Z>Dn01Z*Wpzqs8{R$=W- zm#xTaF{u=xLX?*uR|m_;W-OT)Y-|CPSm342j7dpDsp$!M<f4kV_3-@Ahs8i_tw;sv z+ljZ-v53$UIwwl2MojYF;pSS*&eYkDZ8n^#Ml5W$>Eat<!>d854ejekYqIOD_Ui^K zrT4CtuclSu4;9WmPDr+dU65Vn;4N~@Rx}T*`c?wCOUTlNoFAFSqf-w9d*9%ro7~;y zLKWyZTpFLr?L4l}Z;C-Fcp}Xu`w4^QtHS0>#U2^eX83*RAKKC!D+(?X8${a}DL1dU zsc^-qWivlr>q$h68CS0D{+2~wNJdh|i~*BNl!mlhxYODh9}ZY~E09o`;&B+8jMXLj zN9F6j2r7PtZj$<(56|-Iz;d-|7#jkPTQ@x4ud?ZVyb8iY<vVIWP_+Gc6zjII<Mxz( z3U`r6%u5GU!(j9dqZ`Bc6jPNz*=?Z!i7{wF@QZ@^3;`}B<BmA9-g~e@jR{QojVWz# z{UJHU=zJ<8t}k=Xct1eXB$)WR>0JEcUKRc`giBGk)@61=!#}@+$cvIo`75nCSxs)V zj9aCz7o77Gj@ZQq&{JZ}ip7-%Pa)e~S2G9W?|!PJ;9y)1tg#0UfB)u#KxCf<lfFeP zNREB#dh{wtKR_|Mu68Fv`DL8xA`?;%P9wA`Z(mB_t%ARuDZN1H9tMbaNvy*QXRQ)4 zcSy@kY1TpecL}d7j`OUi3PTU1`C3q@`P1>NagPoZzB{B2J7Keh$KexhLP7bT*PUd{ z4Z4JHBZFVr4nHUOv9dSZ5QB!$!Mmr7MG+w*q>pl=%KXu(X~{826OdC<<Iz4yaeTRl zRD6#gd^|)=8oW)le@k>KWZ7W7Os%(iv6Oe&;c*||5PjLSe*c*3Aa7UhWe-+lYS10L zys!6OnHlSePHQwH+}J$k)*7#Ngy^yyG$e{J<gL>iO?*=*<=cRx%@Q<NrrXXcS9(~R z_Fk-8aMJ9BTsDJ+hVaH5c6X^Fg6}2ru<y-oEXiDhhj79+64MB(riHpp3~N{%U)|KU z9~=_mCfy_nBTOL;sIaOeGlRg)B*VX%$@od&Dv`{D!>^PEQ|8R_BZRBKO!Bz60{xmO zPBHc_>iT1r43$<Xowy&NKPD|l(i=~D56V(az2;`?CQtPuVd`t17wyi9drOyN6?B8; z8Xs<O@^fZR>nhE(S^H56dumvd^5GGH`;lfp7`~j~STdWiZo~+2+xRR@k9YYvp7EYr zb|)A-VDdi*E>W4r%@g%UFFR3COax3*?bPo}@rXy}SlsjFV(<{AC&Kq7-lXj-LsPTI z^@u7X$ib>3!ZR`)`L<)!Dprf>d70dvma=EfTLV-SDjV8u31?rZys2>84#YQ$2wIU@ z=WKRzFMiR3@^h3^9a*sncT39oxJ|=M5orLzzz@Trn)#i8Az+`2kzbVmJUl$CMW<9` zswP6*R)J3J7>1K4YEnoG%FC9D-)&H1(y$k+{cJ&(Db>BMs02nm={otTfPnfI$yCbI z{QLGazfCBskL8Fq;HQ*m+p*}O7c`el8fuxD$vq!~(D;1IPiQhD#!eF3^=+G}px3TS zcjNg<yPsiZW0TFkFOT=XO@0!4n{u>x2oz2jyW^gA{hAQ2#@VNvEr(gg!G%a+BbFD^ z;zI@IYD)K{4TBu^RpSw0!&OYv5~dtbB?B!mV<h>a(tee4vYJlOFxMChs#fXxMf?jh zXKcn&(&vJn1W1Srwy(g)QfH%m=Z-_(&tawS{_RofMbU3ETfh6Dk{8W7v)Xj!46179 zBV4^zDZIEdi+zWw_(|bb7<IN9Q$o1vUYQp;Ft!9Mk_Wu2<>^UYUmMupi9T-}P2$HG z$P05u@3%MHoT@JRyA|CS$=Mq)+R->$C;#PZ81IWaY==|s=)S(b@~^6*xXGLS!e?4N z&@`!tA|D6~kufSX;2l!g9&;j&9S(=N*BN5UZALeuw-aXpSd52FfjeP%IKzSx=wiH_ zwHINvMX?l(a_UWxC|07ig-%vd2uPJPE<`*Wu*?+=)c&a7-}C7WxC(!?ws<8*c@)!A zGQTJh>sp7U5?fBce=`O{>YgB6xChtXpNiWRzKfR8PM-6+I-clv|65S9PSXW<R6p0@ z1L4GsY#zk0L$yfh#F%%zXo<;;Dy8x*<g!ObrS~`7LIUsIDz>ioNw%NPr$b$<BDeh^ z4e~svQiaqqlxQyFss%*GG@J|xB?M!o^p@qM;ReW9n~%Sj%Df*(EEeWQz=+B<cm0HT zVvR`lc<7PnP~w<3T3UJ~?Pfh9Icr*z;-&ZH-HORgcw<wcw*TDuWZP%pcBeYwRD>+e z_jrhgYTx$zYBOHfv6_7NjjNin^X&{JHi6_wuX4ORvZUeuMIefo4D+Jn0&p$`Mf0WO z+1&fB@Kbqbu>U-g^@=gKi=Fja?0u%`xKJ2z5su%5e?|+O1YxXj?O6b{#&GslQ=to5 zwW;O(SB1IA4k5gY&<yI@%Dn-T0FN@aHTKp&aA;$KjYxO8@Kt*>(?6OBgCu=?P4V-B zcP^;ZaSY|ASaT-jSTEv|A;E0gQZ}DNEj5gF0Byf|*IAFl8eP_V->TJkun^vt7Uk#F z_Dvr6aUuL5A-uwl1`-~%5??rN1V4Vh@8^N9S`(cX0~^@}Vc-R5U{bxqb@EAX-F64k z@+ZN@FReud7M#J_z-a_;&#7wT!Ma;(cKpaR^Nl0zfbm(s<)XXVL5p?%N~;19@W3&Y zRX`7oSmX^01%tAFS_=+N?kzfD6aA(1BP}>W^g2#DQu^MH9eDIDm@!w5A$&E0;ZmO7 zHH=J-WAI?r10w9jeUJd|EWiiOV59mt$)c$IZLoJ1eZKQOFeRqVax-Nms{Jl)iA~8M zv>JCMF;@-Pj)(kk^kJInmj*L!J*EGq>OS2RZdek7_eqGkx9(1(ZGL)n)1S~bg>DOy z^OH>%HGMlAoLNe@laQGU3MIg3ZlqQty=IbZg8kh#w1^`-0(IryuU2V2(U*^OEtjzz zJ@SU}V7UgKz?V=o!bum4_5{PC?+ZFoP4%TDWKlH^3p%g(>`VH0M`>tD?~n5yEL9)R zGv{q~YfGg(jF<KE(>wkAQ~^5lHg`bqddtm|uspTRN_LCcANWKOOilii>Wj(voO3<v zPtL{PsMRJ_hl1P7_^-xO?-El(tR~=RGktb+{7<Zc?Qk@@wX%6A3in*8C<=Wno*7>{ zid1f8ya3&+Nv^%gl<EDYb(RRfzCy;#;d46b_Wr6s4C5p@!e8y}hS3How4qHHG(DeP zDroj!MlM9TpJ(g8^ohV3ISuOv;YAxX6O>>gbd%XgEeT!n&#V9oWIfCV7zQrYz9Pi; z@o!*Q(I+5yM+4ePLH|SLu>?Gr(_NWu%xqtcX@o}@QZ#`^n=`rql@a{H2kZsHA5ksq zXTr2E!<oQ?!8Mkm^ql=>EI=B|QBPH8I0ui3-^sG;5qn!KpQZ#smA|rPu;h_ayzLpU zbqb^~jH5mfM)T{}SFHLPUS0i6jhJ%!{!yPWYqjaE*|uSE3%hQ|-A=bS-%XxmR@-am zTM=5It1hQd)nxdhu$-~Ks0MI6?M|DsCXNj^0in!k3Peuff~W3{MV~hpS4KLLQ@5ca zD||v>hxhA&T$3hE-6;P|`v8P?O69u$6U7(Mre&J|*s1T3W_f;l{TOqqyrVD>vq-cs zS@?vT0dvPv+N~!wK|5PnuoZ`3FG-_nph`uXG`i6OA*k7dOvS_()O^bXF8W2Xyt&jh zd0!pA&BNwKg+(k$RD*eb@DG~#-j``LZq<cxy+1p%-ke%}6TrIu)+plT?i#uUmM2>q zJ(upxbnZ+T?6{cKRc4Ke`XS3lUjX$;n8Q6jF-mldcy|4=%=3SqwiLK6=O)k8A2U{t zTRYn~(-_KXq9OO8@-Ho1a#fxZ<x)9zuy-EHnd)V?Gta}gu!o^LrD^ro1mM8t1Lgn6 ze8B5PU-ot|+WD)0FRcm_r$^j2IK_e2FHIN6$$P?R=$3%^(Fm1m6}efrZKu1yY{e5< z8wiU|<n4Eo7XX&Kue$82B+4UU(Klj@jG!OA0ob_-SLnyy=KXxaqmnauLvowdc0+jU zB`izr=UWWc?^mlYvroL<&DN)F0vditA#!>l|7Ludk4fE~(8^PjpMAi5p2ew?xL%9% z@m5U6vl|sNBr-lUqjV}1X11#sAolzLeOn$#&S``t*1ai}l}WNi6tfc|hqFkY6g=mL z{+%)q)gt?pGv}-6mIUr>UOI^XHj_e$(i_Wz)jocq&VV_0`9@7#%N(l|p0e5&C>Ql= znhvF5qUvNy0e5Q_DVB9+0c%0n8IE68m-j;rTIMx3!Kg_dZ}x3LZ#L$SYS6+by8EA3 zgGC2x>J7ENdx`)H@q9X#f_fId{GY=PyS9gm$w+z8q{I^Rk@PrrCF~8~7f(>u;8-P1 z-g3J-y;nC3gttbv{93xUI!E^EG`y|@D6GsPDhhJ@6S)$S0hz|c8_I*f{_Dft*jRnX zG^&b|#`dE5zW*bf_8G&skR#%GYbXFSjH#?V*ksF8O(_m<7dik)eH&r+10nE{CRfjH zDRV~4ri2oH8YhMa)e`M8!{?RNZW;tJyRgRRd<k>Uo4I83qOlzP_@=;(CnVT$Ui>T7 zBbddlX2QSzs1!-u2!eI!B-ef>Q*9JxnwkdxODf1{5cT029oLxLbGY&OXkJ&S`Lg`E zFxy$jrwK!z?!nqa4C6{a*%%eR&yPwvyW@gRoHi8!^x-^Vef@K(FE<v&2~&u0C<eVJ z9nmWZiWV2-m0oOb?8j2JK#7NPMSrkrH%w0b$`*q782tVNLmffRu>?{4Vh^K@L>aFN zK;hL}T6-?tR{HTgectAGEu)_>%Qt+S=%aa^Axcfd^>n=sX<nlMK5sJMBO~IW*+`{A zcJGF0OJ^E#LcxB&YP|s`Mv`JMn`<-$16J9m!x?M=hbXk~Ltp_1a+X#=cPIg)$u~`j z3!SSA*QlEWY==P0Yx?yoa+KMNb%=^JsnX8El%+~%fct$nSBvg8<LMp`Z=K(0Zt5|^ zqGByt>I3!54sEbl`7_;_vg+!`QESGJ!RC>S1FQkbz0Wy@uUUA6^Ts3@8yjkV1GhLu zE(G@#wn>^1FK3tIRQopRUAB|S9r$T?v<V!LzxCRNiEC1sdrJoTH_7M_ej?)^2EVnD zTk%y=z_`qDBS<=yey&YrPg9}Qu8?`MA{eLs{)hA~<u6*@<w{EEG>ilbypH2IP{(-; zbOby-WKy!^y7u-{&BJ7t(_jvWpv=|SstPO%K6pNJ3>tiT5UIq!3nD73sXo2nC&H`T z2r7Yx_wQI}?yRgY{>D2K5g3^|)O-Mqu235$>trA96NJo~Vz&}2*Ju;U_ByxYbY8Cg zb)(*=OynnE*%b*lC4!i27PcmN;r&Udb45&^ZH&ZOOj?ysHI6<o5CSzDG2;8e(M`~1 zKrlZhdlvLo1Z~MXZ7Bs7BNPZ)4-+Xq%6cO421arX{)@R54K;&0JTYsHJ^Yshc%Jq% zF)d^59Tq^M9*d~!Tt0(9;rCz(@37zmdX)j7lSy?_807RycZ1abW|qiFYqQ6~3zk^d zd^0gvnYJQDJb8X$VB<Z2pW{2gT_^?A*r-1Z2rpwLsy<|U*S~lvj~>^#KQ>;ol?RbC zST*I&HcDZrO7nk~L@YD04CP3OD!~8`O)EF(go=zdxH^o&sv>+_8V&K?H<0jicN*uO z93$Yg7Siv{HG-p^QpE!zRk|jImq!dJU$7CZDxlo7zuSI`&s<GCNt%-KOwbWZba54E z>Q^rtYVm@yBH{Og^09m^oQAey`q8VSp~p8*(6!xk!oS#Tq|_g%fTqTy_jY*WWLG}m zTwdiy{CvGa+vw%HUI*3yHCwR8h0|3WdZo<oX9{RX*|j6<lSmM39oBs|xxce;lvq7a zwPX+`pKCU_hy<z$rhWiMc)?>q)}IA%-}|@jH>XUze46tlVuA?D92d}f=dNVhchtnI z|B4s54nt&*H}6;X-S7`hgE&X$Fl4wxq2^3#-8x>|8xTepIuc4G4a-PqcV%T+{qRQP zW45b<+`;TOo~1I)bf3g1EIM^0E!vteW~a0`Qwa8iVeQX}rdx_wv@{5gV&tDY4E0o! z$mt=%hp>nH(p5wy+>wSG5hjbGmz)B!9c=8;S1Z?B{gxw0a3Y(c4BB0m_I)hrYgr+? zha9+?<Q+Jz9&k}RNxI1;z`mB(LDpXPlY6`Jo`p_>X3sv4SK3=*_V=Y$HI?+n3P~Ne zsSN(Y7In(#e%Z8D_!b<hHFuM=e_>WCFlOzT8z|NyVJbvd)nHOZQ1~pjs}2Y^5U-JV zk^KvChLFm1>hl8Fw#eX#`F1K+ODmpX74&BJTKGjy)I7G6dN0OLN`4N$?ri<JkG31& zJBVO;c0KdMi7qwfOy|@S@9&F4WbcyFuGa)Ohz>!Bn`m!snluT)=}R;P`>WF?1x!W& zs2H$kUF(gt&+bbEcz!laVx$W=lrgy_YX0<cr6RyVZzZQ~wfBa<Wau-tpOPM<qkM4y z{f_fql$c=CF-n3@AN_bClY>7+Uu*vxy!c?CK6nQ8;I~tVVEDy2%ij`+-OOgrDPNC- zpQZJ!JmZQ&sn6)EMf%)n>DLrd=sh$LE>x&3EUd+{N&%TVED$@|j&-o97!`jk&hC$i zwnzOhlDLmqw&>4jot;h_WnKmzB+*dmNW{yw{!_hb3k6)j;uL6T#k^DT)K-uC9vEx4 z*q=>*_H!d^FcvjdqTg<QNhqGO?=LhdHk(MA^w!!OyAglc_zkg#EwMWx`?vhL)DA`c zC0juC(zN7xf?LiKb*a^<DzxdZU<;4+p?mYlV3Pj)i#DIu9M&!jzZf}%T!%4%S)ie> zoaKTJPm!~_PU#^gVlcH0lPI~OR9!g5$ehldf==V@9z&TOKKr{y6M(N`wmESnYT#`X zZ&k(bIcxwSlBa6DRf)_^+TNyN$jE}cJS85O<AAw?L`Lk2z{MHt!f**Eu27vP1y#g; z->sjW{Ny=%`eY~7!ppnXRQ_3#BH;d05)}StV;7HOLP&%vTT^Jt%X~nXV1~K2#y3u5 zMc044k`I$dk~&CSIL<pGvtVN!16;ThWML6&#DL_gwtle|khvM|A<HNordqh7>r4#n zQV~lccwc(t+bB2#-P&f-xu_sPbnbsLl?B8lCrjkM1gZ9N?Cs!~0Kam`km8ySC9hME z_hq9u7e`X$Q7{;vEty96AjJt)(8mRu48gl1lzUneA<udG$uw0xS;mI@c{)zq>;>Oi zeeHkw;uMV(UHD<8=X1v98o8Jz6ZA3TX&|+|>L{gV|L0oPt?)Mr1XL<iJKZKP(bpqe zwuxu6jd1*o*VVn=&e@Lb1URBI!X-_9yB!d>07ny|4IUF>`J!36ker-_8lHzxn|cC- zT08<CHOe4&L26|yZGfzznj9|HKNhlESrL}}G$6}b?^~^^+CXq-q4Esg_kZ+|mVfIZ zv=3k+Op=oiTk3-id8_XuR5TP+qa|q(^OED>MZaH%#R$RA0pCCA7<9VAHyS?QY&|{R zx}WBd)X#Ohd_>Z<&aa^i^ssY@{>0)tPBJpfd8Q>8imH936(}r006w9U%_<*QKWUwf zzYg^{Z;PFmQv@s|wLubVpf@9d;{9D;VOQadAW*51vjH;AVueEX5P$8j3ha7uVExgL zkE-0*lC!5I_(Cos{)0rsKqk(tE6RM4RIqu3m6xZr2TfnSHm9=gws-TP;C+}KC0BO7 zR1ZIQzx)!Hv2F4adfEp<9f#wxF9K7PlXGNM^)O#1nQ=Gq7=yh(7W#M;3RQV?+>b5s zc80gOqpJ^N&e95=$bwEO6rgP}ZMe3>OzD}pQDiftF7`V62^214I^a_5R#m!8N3qn# zQ-proE8a&;>hat}YM_ZcKRGN23hzA1F&$KJUJ5#M%gm71nFNky0-V)d;uoA~b<owN z(6wB2eoRJ#jj?Na6`TmBwG*}}!`Zy($xipMGoKvaV4tR*6!P^F3K_D?{>(`!PvYac z@2P=JA<TMRL{uNpVhvpTsb-aqx%PK?sJfP2f3w+@*j(?9rO;Msw|Jaa`c#2<)u5-h z<K_4WDx~Ny>7q3fkTNg7r$5$95XGM921Rprkc!+GMPC0x4n?FD%(2y07J_~}41vPT zlh_zS@5&+d4#zs%RuBb|I))h=a_iQFbx&%#T&0<%;yUS{d83WfOxxm$Cv=9R=On3Y zSm46@eA>0KDjmhnbXcfAmSHW^{ox^ex;oe98Kv!tYP`JYg7!sSm2F#Mh{++N)`U|L zEC>&`8C-2eK|g5AP+}Dqv#=VmkH3=tl5C8k*suG_%NC$#BAbIPVjooDG|qYIfs>;J zgt(u^A90u87Zi2m>~6Xn5UL#PyUxxOh#s)o)3UuRSs2Kb8s1f>XT_<0Iv8n|8^I8F ztg)FCldMCF)^VNox!pKZ53uX$oVUGEvMv4dFm?iwsKL#=60z_tRfdA2I@o^{lk`YU zeQY&C3xx#IBU<MNAG9=;LKsxM97CF&A-1;mEW>pMb0JQtWNmv!y3TYkBkuvaNP=<9 zBtPfSQQAurwh<u;?Yaj=V8>lJfylYab5dhINBD8Qq6wRq=`z;bxY{)@<yn6YE9`d+ zgtntfm*n9e4W{(GYCUVzcL7$#yWv&}Zh)VBZd1XEx_Kn1IK`vpI89Y-{+d61r_Mv6 zb(O+Qy8F{Uu&C>`-_B`;{DI6NDh5M2sl!0ZzQ8yH=)9DvU)Dbjdyh%U3r{(Xt@=S@ z9H*IT0&TIncS0vEn*;R&I_^@gbp!Ru?cr12qKR6${lMQTD%b;9O`<nE<Z<wt5Y%aL zM8?=9%5U4@$3exR$3w;Ji51X8#g}mfP&99cX-KQh+DXENE@Ur29<jvFQkp1{Fl8m1 zOf1l}QYS(e%Y{GT_N$ip9rs&xVA<L?&xSSQ_mLK&1RG`-ug^1AEiOPDbqf+7sL+yG zS@+O8&FWJ}SN$C#9K09g{u@Iw@jrdAKX|IWp{D<@6iMF^!)sHYJww+kMtNR<UXzW; zK2f`FSL<NpwXe%DP|1L2x34VJC%^ZwYeOS;NjH(G>16$#kh))uek6O(1GNgj=L%hp z2Er8G@Eah~hA`%ja>lnz^;WC!DM04F+nE>}@y*Wa=yPu1*WBu!oBgk1k+4w9JA()^ z?&4y>m+bI#-|4oH5i)Wpm&oK$KsZ6!#CUR##l|_-pA#hVyUE5oWgaHrmYN2vV*BcI zZ~Fku3N*>xa%yhEs>Po`w`K{FfRihdeh}&p7rRi#WkFhX!1Hw0ows(G*%VKojVM<M zF>tQBFU>!-KAC)1w4l{^cKy<c*V)TxqRO^H>f>|2b!hgC!&vrty7&1Z%_eW<O7N)( zd596TJRZE+h`H?|Uq4@`&KJsXr%tMwFVwSaLeUD8Cpa@<##O-aOwMUj5zr);^hP&* zeSP7q=^grzWi&CQj-5W1!=&B_agrnnIUn@5xnv*w)KlK?(_~KzKi6Dd{B-$G##ncJ z*W4gGX^Cd4%{^xu!FhU<cnGe#Z`$w>Mont|X<Ak=8k9_sqmhaK^45181VUTEH}Umx z##6{ximPOr`?7s;F3L5e569t$FCY^DGYHrou&DTj){%S^`NpyUXif@KT=;T#+c0(~ z%0C~lt{{mZ@o?Zi3;zCy*X*!PTBHzW_4r5lrk>~XOm#CVyg}cTx$zNmp(D#mwbypu zxI4`J<<gQ!doJc|hvFZ;r=pIsD$nmv`AG^+;MxB_*nzd8k-d1G)|Ik~wu`F~QlE5D zUBDLreP|L_>b5B1FkjjXY_gp`7en^;Ytu-}f6tTXQvPiP@kaKj{pqL~Vzg!Qsq-P= zP&+$K&dbJ$?hQ%tq><Te?L7}1249^n@Tv$r6emk<!Mz)Y35hg5Idf|%J+Tz55x=2c zttn&$DQ7%+c4mR_A+Ir~;&BBDHB*O^?$+Ij?I6hwNM^w5^bfRh0_{*Z;`ip_9b*ct z$Hi+amrPuo2qH9IBi)X=?)Vls{c1wSZ(aH)Ud?KK_4sLnWTa#H0;-Q3r>jFQXq3l# zWKIe${OQn?{7E!PxH$J?kTX2n%y96~_44zlzU8%#3892w+;IgglYSXY>U}R;affbV z`(2UQ;4L6L+RyY6*y*S-s`VQtjit0g8RI)W3atRtr)b2{>QC45;s+NpC$$>lDSg?* z<bO?W{NG8|8Fl#p7USH<ffJ=!<ZMF-kO*=|IHUA+^}Z1Ze~~5Eb7F|P6b&=WmJZ-p z*=2ftB-@^S@WWR!X|wEdTig4a^6XVzr_XTAPeNBk9oAL8-UAXlDQc4&VFMn|SwWV* zYIs0WcItZVde)>#AXC0Sp!zfCxb|lWm}CC$>#v_<G2}(2Y$#oUMX9(wEYZRq^&+c% z)8)CZTWtGY37FK-@lZv9JSFAy^-%@G&B>`M!eVLUQydt_xreY{q}PT~pT{@>^3vrq z@JlYW-6h7L%pl}i|DGaDcKz_{y;!TDO@4g4A1Baig#^52lrQ8|Q{3yYN&P8*OI$!f zI2{6e!7(}B86{c3D0{&WuN)s+ZXPZNgDFNLfP?QkJj5!%akB6S@n3Sp7*P;X(pT>r z;JH$9F1xej?0j+}yTP$&VIEp9TzF!n$BATE)mK-CLffGv-kS$o&blnD8GD=|aBhjI zaYs#~%P5kq*=vMAZ@{Oncd@QD=6zN#(F-M&*LXnNyUw3hBS%#Oo_!9k@z<C#24og# z^iLx?914OdWhGqoF<H=@n!Sd8q9}DyDRm@5BEMPQoM%CH@-zUHY<t|bySi8CvZ)td z{-p({j*wAbKO<&4cZOV30hiT@m<Gb(6cRU@ecgKtjS5!BwS&`b4>fAM(d*m1)<W$2 zWSNZjk%5HLx!@h!JwU8kz-|xK+kjRHIwyzT@6iz&dKlYF&6I~~Zi}6^^mOt5h{YsB z*u`Y+x%Ic<Eae}Gjd@hbejSf3&p)*u)6Y}N*R?0!?nWMUtr#>ltq{)hVztIlczy5> zWwUi?6rJTDYA}%*MYg5Hhs2D$Tt0*?X)L|g#pN5ZCfcpcn^Ifpn?nOg#8~)*6ndx2 zw=)4tQY7^{0+i^qNHvGB%X9@V*IMJ<S!Crdwp~jdiyTM%b!rY23?GAc-V6B}7xpXz z7-A8tN3uNrtoHdaj?Q@W2gNfpXC6%mm?w%_%VCW=5W8);B{G7q&LpYX`%NVw6%^(* zz$@svF9$t6zw-H{<7X*&dBhJUIOaY@X!Y@<*$VHiL$ekHmk&T<@_GVjI0Y18xL#8h zz#%Bq@Ii5_E(Ps*v0p;it^Z<U<Ez_ooYba?4FNJL@pegFJWXQT%{G{su5|g&8Y<en z4~~-jyASv$O7dpN{VVjb*3QboW4}yNsya?*%WQSrfL5cQINJ%|_!jfYt_Q^7O&HQ| ziaviwP=As4T;$uOF~=(q#ow^Ah;*Jd<240Yp6suMSew9-hom8-GQS$7>EAr&ELkcq z6O_BW<;1JU?LIt7n)cc+oG7wt6WFEwBiw4b2{yjs<{4WR*y{LazE`hhfJd4$j$<nV zOV-{yuO|e_IDuLh1AwC8FK2{2tKMw9jp(2Wi7=6)REG9hlhiRM?}1!ZINi`GfF19v zY-BLFhT8gkoeTXYGcqo+q9!4y`@0c8u0L}X-ysB&L)}7(1EYsRbm-yes(Dz;c)6*m zOt8`EHoW85_o6$h43=AXw%V5DG&@+@pUlPv3s{n}N4p4@G5{8>GVbP?7lCRPmyO33 z9o98%<6x}}WL?EX`ZK0UFI6ov8B2`K*?~RKlNHdx%oM+({<pIp5B@J+`L7^QxY|q0 z4}S3&|2xA|5QVSPrP+C+qph3u$oLh5c~{+?!0kF^SPiV#5r=4Y5WhjGz!IaEjC|=0 zA%Xjb_Z>vNvT^hC;c+z7=OsHxsoD5p(re+)+kuqOf>|I)<GcH(IJ`NS_&@d^DT##- zK12nr)gEkuqP@tnYVSY?``KV^=k&D9G)`UGI(VQS+K)Ik5D_ax&h6y7B4*|0>W3*b zpW??34|Z|<K6k?J&E&LlARLH;3x7~*@25hc4nK*5Esuq(Pn?RQBIRQp3Qfj`wjz=o zEIdfFbQ6Au?IpC}KKEq|CDHf&_o~1*bw&Ler|Ybss?N8rnRa_5L!sq)8u{d;5WRaG z9CHh!+Mc;s1(C<rcrYotE8$1xE`8vj3A{^N-iw?+S#I9(Ke6oHY27Z^4V*M{(eH6e z(Fea(0~W^IG&Mz9N-$c%swPbHx391-onfX0fa!YmWAdvs%h#T7pV6+(x^}g-sc2+` zGe7KPwf*p5Ru<}sAipMv2iqt?g{`q_JVuO=lq+?uQG@=cv9`qnpvL=HtxvuaA^p~F zM!Fi(q?)`vUmP)m;D0h_3*#tZc@7IzwF^6qpB-4(9tsye-Ybm@yiSBD-K9TdO@Nan zL`D5%tr~3QdpS3<fKd)XwAr-cmGDUv5#kxAhdzG{P_Bb^Up+}E3qcq8<Ws#Kn9Duu zn2jA1f{!k4;~tu$C@HLc;8nf&QvHd^@iqOEOQ5ND#veO>q8HzCcbVrAu-5(1;H&cf z4BqJw%Hoq}w6?n^z<YLAboy)nS?Je|7F{qWN&)Eiu2>pvLd$A>nx{a*=<)5c*bCP{ zeb$d1=~@-q8%Q}YA%ZEFi(G;SuLTV)C;ek@MbsgSH8N(y)RdQQD+C;1`P}}sGaRa4 zQ;C3s^v+m0q~Bt;G;$jPCl?=Wj!8--4__0;A>4Ek+E150CGkU695itQQlL>98`m-u z^4v(dROU2xzL@QDHJkvC{_A2?RTG)di2)q3*Vcy<pCG(%?^(@|SUBvw0GG8bB<>#9 z9ZMW@N<}^<gLGrb#%I*G$y%1@LDuzU--j8yHK9`zA=;rb@4oufIDe6`<7<%i$2U2X z0#H+{<LoFIK~Y2C%1mE<(XE}Oi1YB`W5yRBKKr16urg2F)Q)c!DMs?0K9kO4IRi>b zRM%DtrlVpdL}AG+g}1_EeYS*eWTadk&Fga|h__zD@{#(f4Mu<VeOB%|A_lR>V+7xH zM_FZ5)~HkRLF5T4TFO>md!h$ViIi&?@Wto7q*;2Fbq!iw`@G(+whvgzo+?;PYH+s; z`ZG9OaFIrH5aw#MT0#Q~hj1xEn9(Za<!SEC)HUx=oYmXPv(%cT9*N)J|JqX{8%L<) zhCj9G(fX~oSN{WvF#dl*q7GFS^bzPD@7#v9<V^?wI=nSr<vPFjleYJR{q*bY#vgr$ z&!HR}XZisRUF^7*#%m)43M=fz8Rp&d4QGzbRhr~!jo7!!_4W)th!bt7?VZLe5Wt3e zfUeJ8&cNTWjB$3WNt<d{9TY9K`_HQP1EtzM_+&3{(kMuhi0{2ny6N9aE@)&3zkf9a zFppRJsv5rJ9&*eiLr`-p0FR>_b+>?yUs!0#$y$G8Bab0JhD-)?{f(G2O=#-Zgi?Ts zYDRKJ*#sx(^uAS2WQGLO0J7nFzz~fbZmD$Y59%Qph+s!kMB!p&cucIgFhD$V8WJhJ zT{^_u1=EfSFW_mS<0Gr5k<mtJlEi_XezVazE3$bfsps(sTAT1F`46&T$<hn8G0FrL zs^DqkLo#iCW-b4cI<;#aVdWZRujOqLT=kz)^v{vP+%{Q#0_8N|Yy1w~U=5Vah#3D} zPnm__c;<@zU*D|Wn)e?p<l2+AbHvCOiBKU2J`rjO{lH;xkMvDwMrqWN)=0kT*1+-1 zarV<irKEcCU&a83ZPG^m(vpQz(}wULFce-_w#oc~7L9Ea(+>JFufp~c7f>Q}Kajrj z#_=Eoc3T{Zz`o~ALIc`<wmO#~FK)*}#2kvtqt)c4kZ<#W+*Gu96U?Ow)$plApWtXM z%iV-s<|@JR;LFh^e|D$UOq;+$YJYJ1QltKR<k@26pu15XXj`h59*ZrWn&SgYVBe@T z4)$ws>`iU;Ogyu<ByO}`08J;6%h0xB950e|JOVCi1%wpmQB){-2-kw?La_>LJ`R+g zT(CTb@fdOvMa0^B;G~XpstMSk*e*7O*Z>bdWXG`zT<1Hz__fjLHt-yC5^ar`jhI#@ zah{2uXf0!D@bjF^DZ9F02)&4f_HOelr=RAhbDLA9*sfgETgU4M9G6kI%9Yy);RNv< z%pFvZV(z(CRbK>zmi&6|ULTtA&BdkT6+w>c6+Nbyj*Gqf*S)>iSCfnE^UN3LamSNf z{~Qfl@i~}WncB(W5=3Q8%BG6HJ?(MDe->YIU8l@au76|KPhsA)+J4pPGritRY%R(2 zvcvIcbluGdtY)N<s!CQ;{e78)QhWg#R*PESL7;JPsW9>1ZhECPTI>JV7WHnHy=+aE z?T{@pjO&$XRO2vjrrje(U(2aAqo7u^3XJqapAB*A;PkmYJ21CB<Y;^Z$1+7uC#=9; z%ZG`WIl@`bd;oZzh+^|lhhT6%v0A!&Qm^>YB~1i7rx@Q+2x4x|Zw-r>cr*|~qwR&y zF|)blAXf5y9$qcYO?)bDzja`DPPYPsT;?Po_qDF1YIg&2>uB?d6IaULXsH+<(McR< z9P+#Thcqm?I=#+(&E^j@vB;3x;a0I{UtT*|8w~n?FVXG064#tumK@WvY}B|OFr{u+ zDv0ruJ}NihSiV+o;fc?SsVv-()?e7&&+j}3&I-i5x`LC9=Orm6b(*K>WRnr#U6pcw z26CALRvR6e3Ymct41XtQgwA|g!GudqOj7(;?XU&c4#xNYkJ<qWR@e-qB;+6|L5Bu| zOtaqXO1-z|m1D(L*WYdLFW^lh_&0Nn`76TQNJLzQlxf_`6e-Cr5EjRvnhEAFn1l~Q z7@rvIrq~`7qVBU&qyI{ZsS*BJctKw0@FCwiHgTQ3s@zb|v->;Rvv)&SY4wFLXO(xc z5bJ^GDW+f49d_!A`C8()ZJ|oER9T^8qGmm7+=`1!;>Ovq8DG#t8Oyp4+Jr=5eZ@X# zQ|{QfF~vqjO_9!39xbvq;AeG!<I-8_jlO5CLRYJ~>FtgE?@$3$x#l~{LJie<qehy% zYJA>z?N<vMxK*M%G}yx7KJBFh(<c_JWUe>a`k$U&@RZKEa5pAVGa7M`JdE6N)jv>1 zIOcN}PMw6y*R>ZGepTJNk^HA{cuG(XAN@lVZ^a%bxi9{UDb}9{F{{AN_Bx)ei$0;e zr(MJD3w2o4$IZwVY8VYu&Ji#&f$Loqu6}3i#r=qu^KcN#!Mdi;Oam3i++AQB-40gx z0bNDvQacG$BPRt!#Hvi6mWDUYHg)7d83G0R#+l&7a$dBT4BN8jeczl0@^`_5x3qVM z>s>dO@;l-A<Sp9F75DfG1X~5@<NI@)()I1v;Xdtb9*lId<8-+w%zNpd(VqgEk0{VN zMm~P08AAtFBjn3E$ig!+Be%hLh%iutbHivT3bcGUEl7G1D`Uu+q4@Kj-y)S-+dq!9 z6t}>w^BR9-7YrNv3=Yh=>&LI5lN@9iwCkQ{vc<YW_$U@56X;sb&!`$*r3GsRo9;>@ zdW5Meb3uNCftKLd$Xkk7J;?RE$wo>GI~(WfrV(EP<Ni0*s@A&mw98mLyfyU>$D#J| z>-qDg`UCQ9&0~lkqubfXtMJ?YbAYXJY<)om?fS>9%h6?F?(4r=R@<>V^U~|=`uguB zxIvbR%qEPkGl~${zLBq}N~mTOyNyXY95Mkvbfk80X+~@~Pz{K~<Q00A$haCEhq=;h zzDhHxhU2P%|C|Wi35sUHzSx4)9j6$<TF>EEB{X<Bl3-ER-D%A_myKrr<2FIwgA5XB zzwK?Y!I#H=u!i<z(-p31PHfF%jL6#gaw{zvgfjX8(=Ck7J0>w|w~lf)9s~lWB4f$X zMG_nFfRa0^=6OyJISL<(V#nUVu}^qwa765h9)N`WhQ9RV5V$ca-%ObcIZJ%XE<#xh z5BU`~{L2A)AV3~qoMu1AKY?MCY_wbe0%j}Vnlph#7ep_<IqVek#E;@M1ph>YMwTM8 zJG$bo)yYHUhkszh^w(=+XR4^+C8&_ChSlUsVu^lqgU6wLwz9k$>40di6w+pXVhnDF zvjuk31Z6H&<6DH{jK>Q5nL)h-igK-VKCS)yChYzSJYN;4d%ZAwK>rS&t)P#9+pr(1 zm@VmRF4h(bPrm^!5uSk<qSiz-(7v%IX|~=-Aug>Gf3yMyPG9|e9)V1uCPCG{y|_lB zQYY}REW~+8EG>X@e;R~aN(|<1;OJ`OKb<k!e-WQp4}@uw^C)1)mj1s8j(>`TM?v~} zhxbxbeY4=TS{|k5P{jr3**;rh*<X4exd!~j>@cPpCWyx*x=n)+ka&&WR~08Gq=RCA zj(53_UyyC6`Ych+UOeJ|f`K1O=P8HRMvJUUVT=p;!V)Pv-vBgtCbO%lniJ6PSq2k2 zi~d8nOiNkL@~@$7ilUmw(X)-0fjR_9SCFK$qfYn|0RccP3IaN{UVJP&zDqPX%-jb_ zY8rJ6rM_r#RHI|!17LnU`94BD;M+eAN@_-=ES3!{C)6oww*fF;J4{|i9O$qtt~;0& zcD>v9KzyaZNy=(ZMH`VI2@c{7gkUPjI`l}e0-6vPnQ|r6dT}6@Z==7lLr-|;303XF zI!)|rT@0GP?pa(~dT%}<yf0adOp^Vab$F<iIbPVpGoDR;h0}i}6V#pG39(S&dx>i$ zmTP#Jr=g;rs!X=Oi6Yw+xFhX5JkBX8vm<TZJik|X6ssDkY9exR)vyE4E-B(O94Y;y zu)=fh3DaALIaq2NxW*+{X=hb`HN4x1Q-nY0wuU8_c})@@LYdaRFx1y}{Xen+l217` zN%H&uH;-M|ocx(zr2lulp9>r)1=|EYG$GuC$+jI{sypZBsX&fsG(E11AU`tjFxqdP z?R;q22qq3h0I6<`UQTl)j~52Q3`1?08T9fYXF|OpQK^tv(M{1p*AqXRo{)~%xvJ;c zk4TCwCWN46H@EVhV^zDcd9OCY7@KxkInNh+_Ag;@-ks2@v}5p~4Dla0{?#8su|kvz zsaD|)r52%Cun+JRH32NRyFL)OxSu$YvOcAK;X;o~kxx|M?Z%QaIUC}uFfdk4wZ%!z zOAfrHkx-Lp&7zLv=QV;~En%*Z2_hqz^{f+UPlHt(bS@tFy_EQ@D0DvT(X>zw!llc& zcQ=UIwgP?ZpG>)rdJGjT?Mb@d*pn^oZ(=2&2yksEU;z;*V1ZP@!T1GzsAG-y<1MRH zbNiQHK_VW*o8VysExSD>hTCgcrqlxdn0n|m{JI;TA&C$Y{>61PuqN;w2vDL(aRyYy zBfu{Z|F^+Q2&;)Qs6Rw?|Cq55Bhi(42DX#a!BbP0XfWpk(8t&iPCZBWe@uLn$4RP2 z2ikGmDmRGIt02fPv;y(VtQq00q^C-QMBV(WsH<V9^@mY@Htxq5MZWnO2)Tv|;vi8u zle+o7x7tolO2t+i-YR-4QIO|VS}a5?D{CZ%|A4Pd?M5_sg$RhJDTj(BaRE1%u9JP- zDP5}%YtQDs%_~4Rmjs%wYSKUe`Hi{nQ+!Y#rZCIKM6>tSMG7||Pqn?Qzc@COeCCiW zr@Edn_7zxUvx2zkyua+i;Y^Wi(}I^vM+-(YKoeVs|H4os_1H*1B<x4!`t3cWQ6f1? z4lHb_k}*2ZkC6NfVc{q8Vr{neAds*RIUaRbahLT@bjZ$c&1|rjhV-srZsp*6)&*_C z?QND2iI%caF$=>RE|L{Tf-8loG6zBKCzL($2O~QeM!w^OI&&W=$vW_5IpYIjaN~M8 zeGK*~>rk_>s9Fp1$F&neA2~AF6aM{&Ke<3x8=%@Ty5%aNJV!>+-8X47MpLZQ{um!o zc$}4bdA-i98BCNh`f`*E4uJTWzHqkNeFOLJ<9Pb~V)DyE=-*MfRz^M(F~4UYadvV> z($#`$?Q-<s5+GP^O^xs}`70{}>r$uwN(X8*T7c9s2CRyCs@M7`bIe9(wC7P}H3B@x z^v`kz)&GQ{_x~f<L5+^Ei6HkuwT&F}f@V0_*o6rvg;SX#zp5D;9+zlPkn5!19xQBk zt#oWZ$cK0z%}^NF2po;b6dBz+c?*OrxW-L;G|mWwRkqTFFc%i$O3>G0M!^;?7(M!t zQzMM+S3JwGt}dII&ibth?DPq%2p>u{&dD^~&#&|ul%^*4;uk6)o4|Y&BR&K>TkIi0 zeWZQys#pWi<P_eRt|rvUruIrR;T}<*e9K><M-w5luOJICOlU)O)@!?4g;|UXj$wIY zvQof9MpV;&6B;kaNTg)CxT*fl&B6^;P8~S~T$rf_Q%SQ>7Npb115t8P;}~UGd0Ej} zXdt43a6}UjS}=aM4M)0fKXq>!ks$`7|43rsI4>eD9WE=mQ<HHk_w`#?o?H`rJn<?r z5VpUJtmECb*o5#qVkRZ?O}`+%tV_;Hmf==jS8(Jnr!UFUcq^mK<T#7akY(aItuUzF zYs`(T4h5xD5naYd!oD4-QLzUgCZaGN*mPI9&_lvm6z8<`l8FLK9_D{tsbG@o+80N# z-_)u~sxNl4U5k_v{X<oF_&3pGF?8!AkTAHyr~Z|1Lc5!IQ^LVH_c(5FWCtNZEAj7? z8^>te|1?2jc4H-cD0UU2-jq*3N*aq-<vM=~HN??5<+581LZRdsZaltrs$uH|`_+Xz zt~GBkG=x8qFfA!stp?hPYe;?38^vYSapo)YPqm+n2%^4~LHI7z`0Y7>fyuR#%S)w+ zW4z1L`z+;2(Zgbp5!kT)`RXlny=N0A7=>2<Hb3$UP_!o~{<1-E83PyfI?d!k%+xrK zZ~)DiNEVhO+=6DF%z+%8{Q>fjs+ksg<Vy0AqKYdFJqPL-PX1Szm76R6c7OeOe=FSS zrTHpSH+!+>Mc;Kq-Oentq2+c)6jJDee&LG<)}V4>b9XfIBRMT4qNM!re|RCgh2hD; z$;|f(phgI7PXAhra=$>N(uV4NqKtVHN#jj;hznM_8<)*mm(RO(yo$vm#^{aZ=xpf` zpji$SzB7u&Qj&v_ZhUldsC8U(bn^I0m%Y}o9k~$GS6b)Z@Y~`-y!@ClgE~)awonwo zznJcuDnCCwj6=Lr4oz8Dg(-oe*~X$!vRsaqkQlRpGOAx6`rolLa1jfCqdTnYH%1|G zCDd3~6aS|$^uNqMknZA`ALdxYpuOcBb7!0i8V317iZ?N5EipRty1F^}HV)bMoivbT z?4L(<+R;J>C)cK$ZSmAO2l>L0uf;JKJ;M%IK4q$jUa|x_P|FEakxRUz3Ei#-RhhIr z2+);xZnnVCZK(Ubpd_xl`Jg2KkkLGrQg}mD)d!k@aKc?P+2{J?J5&28g6pW5;}Hhw z#yDQcIXS*6Z2IHbl7Ji9zWQde&*-xwItZ%=c6J80>l55$=lqwu;OQs#dgl>1CPYmY z8^L%F`w#<uKHfL~i?nxstUTKCM5|&O6;y27Mx|mWC$=i7u;LS|V%rtlwr$(CoxD?h z@15zMH?O;A-Y@45*!z3dTKl6VJR>2)<+el;O>{v~#Es%xiti}t>Ad5pw9~u>A~H`O zPfrPNi{BqXe2V)M6Pq$1?Q-i%HtAd4hD}NqlDI{>&|#Og1d9d{Vi^&Uif)pd1`P=< z-An&@-fhsFa&a#9$X29xjk-tyr>5r6-++MWX1gz0CZKGeSNs2Ww$D%{>^qeRSoB{6 zRj3Z0<g;Fkb|F^;JT3ki*Cf0?5S$h9a;&c~`Q#Z<>opQO-?L=okx`rf8bqvLQfpKs z?jvS2L)pAdI-4hEAD6FTrOT6cTC<Kdk3yF*5a0KoBLMVgw1%9n)U1l#r$XU7mO8d_ z_$h{^VBUE(yi_5)PO2u;AR>&C8N_OsKig7K#WN*!&yq6#RB+H#)*sbux*1&cyNq%f zPvS^oKAEVx8n8f}A<25v>?rbbaa#wuX@lq&eT!-qBaxNO_QG5F7@twHd6JNdX?wgB zBUQeCS892FEyjDK4*zykl(MJJ$MbgJ@uOqenyp@}_wtloeiZnp83%7b{}OA)(ltlR zyO5rImRwv-4oO5<sIb{mIyQv(Z=X5Ul_CW2=SjI$f$&l;&=>rVtPRi?T!t6)Su;OI zW{~lJ7q#)%Hu%4Emyn&_F5k#cs_!aLU1z0xE15+)R~13|TaX)Gy8PQ9Im)Bw#IPkn z=GAL%{7k~np5^`OuaOW9_JMApNM@J{iFALzV(!K`YsGJwWSH)3o9wjnFU_WdF(9zi zL*#<`@f%dE)yC91VNGAV(YrsyM_DcFts7bX9@-{x@AMcy7urk$P5vDx<c;C75s#`@ zmgU)jO?gjlgcr!)wz*peSo(zQi!cG2OTqt4{1y-(y45f;bz62L(5UJon<&}jRg-ZN zVGEUSt5gSblIdL5PO`WRu>6+sYpqakgQsey#vxE%FH~LHyc9ysQFv3KrCf2VG7PSO z62srlKbe3m&lm(Fgs+VlGn7o-Zc>xeGt(+_Uz>d1Zy!}*Id!rQ4Jt7NohKE<f3u`x z4j2GJzhUdY8T5kh5WEL{(aKD({4*BIS~a?mSdR152O@V1-1kh7<=soGVWoY}x*Et2 zF^)_KNgED<xSyenc{J3?@+@OZ4V<AyMFCB3ouHVVjMS>F=V)0$H{1EEHi|ar5|YQ2 z@TVHoS7t3;h9QFZy8+Wsy^)cyJt$iCDomsFK&RL@>cLQ(*4qT+7<YQcE1K@+N7K<O zO<i#yX?+dp{>(c2+n|7<X?eveF{-0Pz(fNgBj3r?`sosgx^d~(nJLF<g-|xLOszMX zTRMBu?eXjEsksg#6O`3Z-a87NwU52c-lu?(X+`M9_x$Uwyk~Tl=gygFtkC9!BlLZ% z`q*t3#WFaiqrfA|3iyIcI~9;%4DAn4x7`E0ey3DsA?*rCMk8(m{FfLGGL9AK_%B1? zaenz4&*%P|3pS^(v)O!DGEnwX0u}8y#HBykr(zux*vFV%kv_XG96tLQkZk#@W$V?* zJH(43*s)d}>rS_3jCf9(1$H2fCLG0*+UQ!P?Va4IKwvf+j!v|I_&7aJ<~O48x!dLx zQlD-jOp=x2yp<P~(#M^doAFkMWi#tS-g@aV-d6e8*LW)vdN;3*K>b9jWK)zh*R6Sl zK}u#adbl5dkhW6+;>N@HGa)vpcG1RSe@F$SlZuyqv37@$;4kAf&@q>(E^FsL&t##j zNjmw432~}yrY(8bU&>|^&kdC_7f=AZS3iBmHGWL6RTh081gptC$&1n1!K)BG*$EBH zJf8RiTaRs_em9OZ_lSZZIN$EHGP}Vi&~O7|qg=VC^>A?y&3dO1MX8eUn=Mj_A{hce z3!8X~wv5udvWN}!6Umdsh+7y~{oI3B+&<;W_GjthSIOgzj`HOYjQ~V!JR*cFDxK{k zT^fXEK2W1Odb6Bfns(!#6iv>5LeYStnTS)h2eZ)0YA7&wvfcJ@>7nMumE~Bhd<8?I zg#PM%J}aF0e(y{q!={PosVon}jUoGmeyEolD%=`EVEkU3Ze&^}f$y%{Az9#r3!>t< zKAz`cIotR;OuHzo`19Lx50CRm4oI~d(9?ooIaM7X5-)?~&{<&{&OPevX4?Oz{S|6F zC4vauUng;(K!^SUwsi!pXLn0u8In;BOq(e|^fQ&#yfPN@`zTxS$8O@~O@C3`E{KSS z14<k@z$joAhUUA8K_7JX^%O)Zyg$1$k%%Up_Emj7rbOM{I90Rqc~XiyeFc&z?nJXC zuBoTtdjXi66MWv7IE`EkWxwe5?>}+5zDd50y)veKX?np7S!E}FNnp;O#Md4>LH6JM z5lI#&k9NCM7VFVumDNlthKq>~3q{C_6D7Z`CD0`nIQLx>-*6aFJO%1N+w~Y3Ha+M+ zM|$6d4d(TYKGcRJz~@nX!IGIE?@0gb1I{KOtkdeW;DD}P>+xfro1eA=<XGPMVBh6k zqpkb|pYZ1=pdOR9_H^}uCZAkL{C&GKp3&cA*`!(sH|0vT-31U}__t9*rDRV}mS%ht zh?PO|g2-$`<B45EgERZ9wVe#=0T1~bHPU3HGx;Bd?g{hq%v*oor>0)hlf{MpdE~&* zqQqiIkz^@A^E`PkfInGHhC)pI1^$ekNk6pd=NG;%uA#k1nGaz`o6B8z^~gup3(c@R zee#te=?24cFtz5}_I#vfITZUwify2g<Ro>U3hg-l!FS`yjvZC2o?;yrR`<EAz5@Za zn-Px_!DS;b4)tpi2o1SmtuI|K@Q{wUM9D%hig4l-_+JAa(UKJ|{bm(LNqs^fRLJ@b z!uEngH{n_+v{BH@lD|`v@Dj&I+2GH3-rN^0ApO8+x5=616~RE!WXJN+jFS3HcrsH@ zxcXQ&n4~5{sz*#%*eMFU2RRoc=W4vU6tY>!ItP>HO<D5AOLR~?m9l@Nk!cBKhY8wi zZJ)kDT&i<)zFwlg$OwQiVJ|Pwt+rUTg?~^AN~oH%N}G+l%~1ow<D>GDEg=ZzQ=KXi zT%=~bg)VD;=2)?aliiyqvln-zy^E&{E8+d6l&9No*BCbNUy@CUV2aDVPb*pCf6JJd zs%b_G7nU>K{e74G?O@7Y|9E=#g^_9NKsxXJ?>$=e@6w?3THH5(N8<pcyV8&#U0u8_ zQ6tS2Npkx|RULAB@7^L9Q*D~ZA1bg|Y81cYpvh3vvN(k+L*~X8tk%>!j87cU)wEw8 z5l(pnJrDe813$9NFxx5I>zyFGHN*bER8xhB*OR9;vPx^#!kfT~=@t{qVj>x8BIus+ zTrQ`4t=L>iJ^<~m811py;04!;B>sMhbuR`eSz&ujT!)hmfC<?~d9o_j>C?I#xXOM? z8}21;F9)qj`y|UKY#Y{l4D#qb=HM$XNTVcpQv^J=AH+bkM`xcFCP={OMbWO$mv0w3 zt>g6Fg|D1c5Hz~%u7>L}ogfGAj87Rhz`&SK@;dZIIQijCw3ckO>HJXGhx_8PjUC$j zukeuI15Jsl5EZ>|;={c(D8ltgnr=u2#euKXD#8fgg@vXR#&s2M(x2{;7O_j8XL<1$ zJT0a~mFLF+HST@&;Ez}`rY$3jbW&PKO;2UbRQoJQ3Xg7T=)+s?;99GO%9S93{Hf=w z<A%B5zH%bc+qGkkr;~&11NQ#*9SRozV(pFh=z5|%6p=kliZ5-pnu-!gHr$)cfGM!^ z53J8DM<kSN{3+tQXtd0xrSUd9;dob(R88DJjCg*7W`FLWOQ`ur6W?MiEyCmQpKQUx z+|QNvtG?gGscBakx5T8-?HK+c+$x8NrpKD-YSvx&a33eMuHbFkSyWz*?Aj1KPYnT~ zy*5+O2F<=!{QX$MJGNyl_zOYb`@UDP$=tzpv)~ylCA|3^{A{;dwU?>xOJx5`vQ{lD z5NSqjs!d?bq-2eUig5F=2p47k@TCojE=`(2)C}VZXitD!0sBeZ*qz{AOnIr=-vbIX z)B|IR^`OmfE7oTIi89;QG}aFLlZ-l$jYTfo6^O>Fx*1v&f`$r-rXl6i11bL5SoDWT z$Sn1-t?66pWN)p#=r(01rJF2Yo>xx=LFSB!$_yy=aa^|rFPqNV!d3TD&v2OyS^UZP zv1>sBSuhqPLg<LTx<ANkR^``Cdl-0k)U)9PxdU=QSju%KGTSOMklFOq$Wd74y8=D& ztm?`1_CQ`SfLTnVi+;d<Ww^|odlHv{{co)8wG=*8zuWNdt4i!x7CM1cH60A(LYTi; z4s{AzUnk9PX|f)Q#~qQTw7>JoW}yxWD7Vxudz%ZvatM%k?6-^t(iI%i+I=(g*M$<L zP5Kt97$@L|_3FF<o7w2X#4-04NgMQ$?WMB;0xM{7`c+Zp;Znr<xJvNg&Qly>Rit4P z3^XB<a%$uKrP&^*k4W`?Loi_)4DEZwOE(zcOl0!&9QX??x|M3y8+A(Qy`xE9zaeFk z(*>`vZGFr?*KRa=6!+Is>8fx_0Y)}Ra$m*u`$N9|gy!5X=#BR1^(~uujRLeo#`eXI zeWik*NO?JX#5*2$)u;Hg1lnQ6uvm#*(_7UT^bi#&szL2~^4?IK<DXbgTx4khp|HcH zk96U2sR^CxTj%A5wa;Qu*Eylbjo?>{y{`P>qI$F2PY+;1Vf5H7sLCr@|F(>l@2G}} zlF~a`TI=hrpkr`sQuMpD@)WNnahmX7k%D03y0Gc%<`(3vpnzEElYRs5{(zoe^QBb` zhyOA0M*5^6$+peJL8?a3!dz9Qdm631Z&rE^EL(=0P6w22Rhl6)U_kR54rU<qliz18 zq8&7^FQ&<rwlf|^3EHonvww|^oL|kHa6W3*>(Vn*{lq&d7?xXsGrD0XQZA%x$y8v% zS4;9Ha9xrzT83`aEN(*(l(6&}hd85e74H*)YtU79i*JsW<piy}1IT0V_8Ucnhdf(J zaxRqT0d@PV4vdSJIU1}n6wHCW#Goq?{e^;eb6i13Bnl)($KVT1kPNU0$xKGt{zC+S zLxBt7`9lR`-~)4lVGVi1&0K<R;05xHUg@n~j(cVwZ5BvCAc4Xp$p>Nln-H?c69W@8 zvou-j7o(D*!d_y2lx!wJG6?);qDA|;C!QtaBsO}}wQ7$xQ(wZ@EIRi$W*B<G`6AWz zN;=f0LL)%7YyJ!fIaTVe4j(k}Di-IDWgBQ1G5P%4r*l?=?q{3r*f_|OV03&s7dg?{ zp#K+lJT2exTChP=OYf^*d?ax^u?Ddgg*6Q3_;&UFj96<mB__gEppiqiu=QWP3bFR& z8%tJR2HmMI>i|eai-oTF-<Hwh)^6J=kpRvL*s`e|RZGc;LQGNVvA$P3%GE`&>PXp! z)>vsZdA_`hgL3LLr*0SAkLAy}?ib<p-fufv$vc(>`SEBaNlF8BT=|6c@g@7+=M1GP z#AND1i0P=bkG<&y?0THHL1HH!qr3Bhg0ha=JaN57lVRwV`C)bL0GrW0X_5zblAZ#a zxxJrtPeg-W9dxxB+zc$<?MauS6ls$sm+8@&z1<<mzOrJIRtzW{I9niJc5r~`2>~!i z?@g8QAXy&7VIgS(`_mC6=J{df;yOyxG=773;9fQvriK{ts%K8Rvv5H65WvNzIO~V5 zfCP3CscL~{c9b{758RpDb!4^P1H#1dUNE8v9kz+3D{q^PCZKqm6(57p6?q+&-P4l+ zXLrx@$w2m`S^wYIc8p+}KaD@d+)-I3^d;|xnK^Q42>eqH68PVegZ!sVAJY;~!Tw-M zF;#>uS#^3F@C80r%}{ss!lr`v<oSwHhxd9F&$1GacfZddhQ43-(xQg0J7*2w)|M(o zyBISRg*i0&V%j(4*C&Is*E@H;jGCf>q<uj_UP7CGK?kEXUS5^9ucW4)zkEJ{d##7) zQb^tU@uR~L1Y5KbG8P^-!igl4Vw$mKNEm89b4<Q$=_jzm7%t~5s}ampJ%eO&hIO?Y z0?Z9FzfI)j^&*k+s^qZ$Xiyup1vPcX7eN#x_+uHvOO3($@P3ws$fp@P$XhhQ6Rmz6 zb9XDKN^<m8=X(oez~yx3uSeLC?fTN&ZF5!qWeuxKTu3GtgF%VzpXXTc@6S=UWfLEy ztC9Ynl&R9UurfV5y?c6kh6!MNAq<v1ZEIM|)5vZyh~!2RE!DOE(1MVuA1_L-#wvhm zM=Hqj5>NDpB&#y~d2nEw>G;+-q04dmR}2agD2%uK?C#k~G&muYiRy8Hn*ljM_J{>? zG2|FcyWAq4ymTePCIGswUu#gmF|1BIs>~o0rafhh&t3E)K$OBCGY%h@jf4!|f0sF_ zkUxo>VHmDD?}|=JFw$I=tBy<in1MB_iN7#czk5Hilt0^;bUKVlG_~oC&~K!-a~NJM zT`hmc*LYwhfY@q$y`A{(n+uN=Q4g!odlMn7UV6=GJ?<t}1M(r?ZFL|5KlJnj-lvJZ z8jYsYNwJJ@gi6tWf9=|ph&luli^R`p(WTZb`i-x<$C3UIzU)qc%x?UDgq)$2bIYlw z{8LT#RZIZAOb{S@WbmIzgLmH>V3MrTm03;b<)WXm>^5gAK@OY9%$lw8=x}v@j<4{G z8S@sWAc^+V+gWU-TsO#=nB*1e;X65y4ev0A-io#b;RBS!qHBQ=;tH-PPi;6KXfS$^ z)pY3V?9b$5I|&7?pMa@BZ8cy+pBe5<;naW0$1gzK$8&jyu1#+41fR0lCy1lYzx%4Z zF&g>1bFQ0wA~dRXaMQJw$1L^;EZ-zR=DVG%E~>JSSh@xHm-~SkRmWGZYfnN8nb57J zhhN14RS&xktwlAV&#D#pb++*JiXaYM{}%^|Zr!=rYR^1<`UP|FjG=8}Kh{c;9FyBV zPw8*_C(@Zp>Mu@BZ>BHI2WuQvJFGRC#aHun{kYUHZrOMT#hv}!C_`rQB#<Fy`<FBg zQ~%`ogZ@3wpVUHZ6$8qYL;W9_a$>{3tL{Sg{tMoCXr7y0d0x8(3p$a93mi9{!FL73 zRx=IVx2{lp^*Wk-_;B1Dc<0`A=cl=P0-X8E0&LMLNUugULyqGEklRNTx9m|ejBbPQ ze=fZoZ_B=*EWO_cyM8RWhjciUbB5h>jDEZPXq|8+3uEp=@bK#03Xitnci#!gd|z6T z=OAibQzvIZNYah*Hz3(L(+`*&!Be!-x(oMI4o#9@ap_NAlqkg_FY?Zy40tnGLcbPn zWjEUnB^gLaVaMWl6rQv!hT=*y$sd9+UIeA>I+ou?DKa0Fyl7$!KjX2zM4zp!sCt#Y z<mw6VK)-OwYgD0+;I>}l5>$8p4AxSK^e+&NL4&}^{Q~yL*Gsd;oIj3KtmiQVj_6`? z(*^niJY+)aA9Y9*T6Ge@JsO18wbo9d{P_Y6ctA03THQM`4dSLg;-cJnWGGF@O^pjg z|C@Ql7A_;yxcR>q=KYtI*mO9>a$%dPdh(0=QsvGkiY0*y`RYF1Gl0`|9Fmb;`=d`F z2$w@l-!C%;K`bm%JusJJ-I2S-!y;-1BZP&7E^}HJme0p)0GQf?q`d<{{*T&sM8Xq+ zt2F?okKwWi;3SZlf(T)o1RYgz5F^AFSoOP*MQPnE&|h2!j8e&<B1|yu2c##l5Gs5c zbOa}K4$92mXkJ<<I$UPTn+*6Q_eoQQoujuL&;&Do6LK18ZlCdFU2DNoe$9{-_XT=_ zrWC?bLq^ijM@()r8=|(W>G<neN#u#Z7Hz{^=0k6*9BE316+o{6NU6plBt*MYbDp03 z`b%us%mW{77l)9|6m=7u7Bq_tQzq$nf%xyg@YePmsV7hw(aAJ=>28rlX8u`u0+Ost zDxb`WI@Zu_Ynms`iu1qK5dRPA`2Mm9l^kSv2DaDw?-;>UBsQ#XU)UVS9OcJfrB<du zvZcI+kF7pXZimlTrmJgIy@<h-W0XLKeNrK0e|+Cjvsi@;FT23b#4F|`ab9p-P%)=p zcDvi`uh<Z{Bju-X8vzoR_PObG<u~ghP@J$N3x#&$>DWvCp{oI+m%WqGWCs)&b5kE^ zN0B+2@fTkDs?!}+;9%MlS#@u+)>MvST>pMGTwza(eyg}kbzAZl?=iKxq=+|Ol0aZS zs{_a0f$ny1_2@0esNCd$3rSp`1~Z=EPpSU5z~dmmzeVZhw*#5+2xU45=smNsFa`oJ zg)l!wYz^k?|F-xXVF*+&NU69j=>V}=e>FU1pjDxt8t|?8+F*r^d59{!k}+mrpmkl@ z8K4ukx<_4m%MWf#pGWt4*6)K3m>uu6bo$p=T((B{&lW|qqrPh5F@=}ivZUzU(X?4h zNis((HGZ%9XBEr*_f-tTnxcBRNJ_a`(d_SfD}p04+?;>3jg--Ps-8XSU!gP!-Xwt6 z|HV}DKRd@*-H3t|e7-K6&nA`;iwiur)4iKaNe|fy;hI9YWc}g3=Zv#qkeR7L@ZB@H zR^==`l+_$9WEGd229ih^OSS!M=Euakv-g6c_D`>k#Hc7n*dqU1e=-!JTYm^Nup?M9 z;&6X7H`cMu^{&QLBo6(~q<!De)t^hpeF?^x*qG7@C98WR&Z#jUUKt;2?&T4YrcP#N z_vi7LZ%vUIcgf1@H{+}Ej_0dOI53444`1ItMIO^xFS6FVup-U9jWzBq8o<g&rTZU` zYZ;%ENt&m-qDxclbxY4O3Di{Ada9OB%&OOhTmyv7Ymh~|W$m(#M{EN+YLC6E;n$W= z_Z;7M@^td&#S1NHIcoXX+TLH};|)Hwk#JwVew}lXWPzg{stl(a2+!9F$PYJLv93Rh zTO1Lw6P>}HR4*DJKz=w6-{A~yh`HbKaOik6-@-Jay+~=sZ5bagS_?43xNQ5BZ?@M4 zjl(!Sh&M+xV(XXMCJBZ{_`1LSq(GOhqfmzke{|u|^y(~nQ*|@62VPSZ<=L65YO`-c zWw98ZR|EK75fs~>Ayil(gL;fVM(i{Wg&XYv2?=%Nh_oAQS(|7>qDOw&#Nv}x5UZAu zhew@9A<JSGghC%+(zkfE_Qz2#IA#auOx73zvI(5S&-$HBG4IzkE^#zMWV#ns7I#Z6 zkE84aZ8jhwN#WX%d+_nDmr$UTturyEhxLb-APHd&XTd7l+3bqht2JPK=H@u`IHk?V z?|pCV!mTQp5a!jFF10jLaZ;HI*D!!q_Yk!lI>JMt){`(}qv<$)PtT+DFgCavYh%z| zaEN<FtSh#ZK0pRHPg8n6>d&|9A{z0kIRBq>-_82fl^)}qSah_gV3QEy#*Zp2V%vla z<k^4F@<)d0Z}-T~)*{3pp!!%{yGdoT>;}PX74=id3|*mG#}vewxco`UONEG?COi+0 z#jtPZ$UT&pV8DxOB9JGwUOJF9okr|$NNatHUzvWT6&jZ#&h{RK18e`?H4Nax;VA%) z`0dczxph<8q}?B-=6kh`oND#Q$<ZtWT3iJkq17j|KCp-`X}^Jj%e<?4SPWn+AQOX? zhA_ogzE|w-&0a>{U(4>6$n0(}_fl@yrACp_DoM+x7)Nge704>RJ3fAB!{E{`7%19m z_aMIeTiG%tCB@$wkV6zjiq9_%|DtQz-xk8cfS9J0vbM*7P^-@BrIBqMvaP}QA9ADS zBL5=<m;KRx7}DVZF!gqXaaqI(wF;7{XuDcMlUX!O9o7>_2!fXUHddOd-t=>)ob)Bq z)7usYUgLlWXE?WcT`6`H^)*riR#qk_8%$&Vm%jbncvtAWQS@1#Jbh0&%sNLs*IaE& z1Fo>#uq%4x&~k7llN1nIc0;V8$QSKSONkUXsC*~S_1!F4Jdn&p13yLvrYtySO9iK% zrVVhkYRZ516q)FloTBaSO)Pfgdj*T@$~CFqoo(3D-phAZ9r<vlM)AT=I?rP+u<O_O z)eEe|vGa}rJYhXR4S4RDDi$Xa*bpIV-x)w%B<WvUS3kZ&nL&HIZ{Z++9Q{xw;JSgQ z<2GhV`!cTg^6kk9DDdM4cVJobZhHR%X@7_bjJ=g4BRd^*K&4;!CzJrRl*jOOk3)W$ z8hN_)S)1))%S@{NXxc@W`-8~+P2ozjefG@-9BH#E=DIL)jcPa&!dy<2c>_n3d*)65 zlrIoqyeCbIaUq*<v`vz3qg@Dv{;BCWGwPE@INA?p=nysW{QvroJ?s^cIohik@_MkA zH^?AWkEI$4Ru(ZCtGP9+=sU8_CmA3DzjP6O=|rUXWUmVY1!Or=5XOZqd88Vf%w?%V zf)5uXf_}w}jb*j%`~d+}=B~0OjUebD?3&m}xT<S0)0_0@60K0D2UO))zYxD_46tfE z<^&UtLVgw&PS614|5^>BK>7W2eD{5nOtk;V2ysk;f^H=+XW+7S_5ccFdx@FvLbOEG zLW$wY!>!u(n2WuP3cjtNs*nJ^Pq(_0bOyG_Lin57g0Co11B6#`r)#I<K~ze;BU=P? zz3kd^n!07th9+LDS858!)8~QIwu|$h-0t=jz*Vvh{ALJBw@o@zARQ=~-*#sEe4AIV z>CEr^lE|Z0?=wH#BXDBJdcYS3pDf%#vN+|wBdYE^pE+-%*@j`?^eQyWq;|0mITXc~ z<jdJlU%>NSK_s`Bs_xydT;fckk_z2!DGi<)T^bz|D4))%++9;`U)s`LiX)Bd=&c0e zdu!B3ltyFmYL96b{`6|;<*VsK81rgpN`?L88}z~Koq~R^&Bx$ZDP}%d*I-q;RkAq5 ztnEl}+)K&|gB71rRbE}~*~PoM;gX<(%sI#RytkV2uJw53;sI>-_@9eGuv)L#1ylO| zgMR2XL^nR(9oLKC9ean+ivqtC`LUL)n*?*VLa>Y5nXQf_+_RG;%gbcPmxhbvFa)oC za&oB^x$+*vl153Cqh!>c6jYTqhAB1rP-E5d4q{z~i{?TIu1IyFGf{8mXT}Q$lEDNf zev_kT*JJeM%JD7<yMfVmC4lXH5$W-`-R1ad(N{p%NKw2*W@Sl~GGu2t7nO=Hb0l=p z^6)9WVIi5*CNs{V{nmVnd?Idx!{lxTQntp@N!3QM_9n}j>Wej*tDdmH7UNLR;AYQ$ zKflBc?mb_d`P$k}i6=YVM@)NChK~Q<71#ECnJ=RBvGY$WfiO{U#pIDor;`Yol>E=R z7#3ja`lJUT_=PBkFtX)Z04~MS-$FD?ydvDV@Ifk2K1NT7CO)zWBD+G;uV2=dpVVWf zhb;Zu2QL?&g1}O0q)R~dkybRR1}LANSTn0l(GE30aH4YC&XhAqK}q$(i#+-^IyS?2 z82?m28JanwsorEE=z$sPhkSXEK~eE3CO^?bBFTG!l-VGZV2e?}Nms;V#ipxyqpf=T z)<9Zdt>aVtS&*wb1jEp6H?>BSBDthE_E4F4oRZ1cihMMr>Z};{R5Zs{4s>y%CzrmA zM+@-p&m4LB%a+SKrDD1SLXvZ_;?6Nf(zw|{T6nRCD)zmFrFA71vA4Z1<lG97p}|WT z>lFQj=gALK>rDl>6M|<`e`s|Jfwuc8&IuQ*PMbRGOWxLGG?q_L(mGi!Jl$>Kvp{!0 zOet$%XYWk3rSsP=3p-KMxsTH-KDv~*>vUGe!>Y|@vjo0|tk1jzn`w}*5j)t#I2lxy z$`=9XA~ZVnG8jMk=({i*2Ah1r{BEUm*@n;;%o#Vt+K0s$%4LDqUmIX0t4Zo+b0x8! zJY1H^!t*jtVTd@=5$~e^t<C#SYE!NfAMVSeGfPocprQ@^uXpk6dqS+TVfYnnc-h4A zL59)7%}R4@BZIEpN@%`;Fv-1da}`gI${i?dWGZ_T$S=W+8X{Qg@;T`JS#&cI!Yr>u zh$unvlnxRFe4^*#Efj0XkW!1T`fUZTWc*9}?aUjT!0gVWhGOmD{EL*%e3ifi{*Xk% zG7o9xSi_3>kMR({(LLIin`qa)8-*jUZ(-kQN`cAlQbXbMG%&LIacG6%2B|`NLWJb9 z<L<PGt&*A-2XUTH8~B>9E$RtIM|AUr`bTv2<DF$9nG1$ZJ{~q}=7Ulg0&;&+$~Np= zl$Pc_XMR+8)bCRI*^i%8T^9J+pS#zAxqsEK5|*72NV5-Rzv1JD7wF8Ug>6SLQm5H9 z8n8JaP;@4_C81*aUQw_aJuAzB3p#J9EISi0Vd%vFFjR?4P)QhVp_IKy#o<qw+mMh_ z4Sub}`E^qeOp_P8026HNa{4Ozd2A_fuE^3&7dB7JqYIN0qn7U?WP@?=vAP;~mVu33 z(dKHk<V=|HG1p?2?&RKYBY|<Bt{JOX=OKGz02B;0DN$Kf(ijOQBUEdKgA;XVJV!uu z6As|)+dizi&Ie~?Sh9c9V&|ef8Sx7hm=h$7j3g)Yn}#3%VyOwXpj=xtSqQ)+$5D^$ z6=RH9nGOFT>n?xl?n>_N!xQ^s*87-esi^+z)pN`w)9+QFx5(n|U7P^vV2awO;wpcc z@$PUTG;zB5vF&hyBC~VK=l#t4?^^-#3GaJ%@uUtM+Xw+=FB^RxzAFR|un>Fo%o{G{ z^X476w=V&-`DRs30|_1jQk<vmpw;awdgYIvnc|?LKaPVu+QWAyYEzn|Q-x`{V>b(i znzLc49i6E6%m|-4O)Pd&jUIb{s<^8y>?7>v0WtW`Z(eHC!DM=W-a?kHjIIH<%}bJ+ z-6DpY*WaAlu!xcu?0fAwt)e|&PFrwT7LjwO>Eu`@MRA;*E-6{eR@DtMB%4{HT0!}d zD|wuC-Dh=_O9_-zk28Ts>z;eRP|FlO>eX2jk-11{Q@vTsDM)_Sk4G`P2FVg836-`x zB91dMAY>Os(b7N>sgx7jl_7aN1R%(pRQ4nl`GBKdKl+S+M(yMA>00@D-wVTxjSOiE zZDo<7xX$Y!<-T=5`XWV%*Zs+<A`K+g&Oy2K1&`-{%xnUBjQGu{>7BL+o?Y?WY-Hu< zH9hQHSTv+X-j4PSqvMa-jqEND+;GqX+T7>rMaP0#BTc{HwQKz%)?fTnNZv>wuQn|$ zKj>nB)U-GF@??3>m)Z7Seig5dH2$XB5RjDdb}}OkB~y{AU0-MAAw82p3LW7V>6a~i zo57NgREYjP=-a+5v;b36q(ygP<v-pLCQ32?sxg1hd;9GnyDc}~<VakqNHTVGiC9c} z3Uzo2z$JFljz&Jfx|}))17{Cy1-15S@ki=tmtBMehk*A(LCC?$!tppILtW%(9%l2? z)%z%=F2JDit+Qo@w&5X3#bd6p`@$*x;q2ugV}teP{3@RDz=qChHB>_7QkIIJ@*Fb% zUWl;9@fe1Cq@?#VxhW)}jME$^VRCzQ(?e@NZNEwefA#LHPXOUX?7>;k<nvu^1I*S& zQ$D}*uc371A7<jk7KBOl&wnW2#<d@vODrt>;Q2U%FlQI%KUvCo^5kEyAKlrgSye3C zqtGHgQe>}yKR|T=FUae^VU1QAliS7|%|p)V1g-ZVku8-tBPZ*4jm1ijUqnC6wU};$ z@9SmgJ{#|AVl<CIZ-~OQ6=vn7BoqzLo(|35!m`+;R4atA`OyCJNaI`0V<^j~blZc0 z@xj{KsgJw7*61<D;y*wqN_YKoSu2Sv&A$XI<l8@7X9*W!+1QmCg@^=&UK1lw>Q8i6 zHOQlt9AUDcjX&t?E953VBpcz|DTNgW$b;p?u}<(~(qM!f5o77qHO+Yapj5-wFUT6B zA1F>ik||l_0Lt}bW@_5YH9S3*xIK|_H$>2S8E!5A>2E7^w@`;b+WnyC=Jl)TC#qzH zZy&e1!$j$J+x7$LP8kTf?kmQE)2&$5*pa{U3EZD;krPU#6-pl*Z%e^5+T9&%Jq^Y@ zGpU>yX%FzDrx+^_J{y_u52QTeF#E=MOCrG<Laf|dtvV1gwmC?BovZ!wnZ^bP9ToT+ z3flfDFyW=3tR02D%#*{~^3S~L!0n;h$)=0wEDuJ8klS(l<!*Z|&h+1z=b#Req*Zp{ zsLWz)t~n>*p`Sbh($5>2n(}V=WqWg5uU%#C=RK}UjWp-^M&$!lyI!XRo@>Rl2Z_y> zc8A4Wgsc+X&+3zd#R?+m&Q?9+Jq~K;_*^;X$5Z0ice?(_@sD#YM*$`i47x!y4Nfhb zD`6GHjVuxv9dWs(3qvQkLz7nV8FaKwjYCK%!1(E|Z>A_Pr}9$P{p5|^Qr0m9H_YHP zTc&3Hx0ts8sD-yBh%PG=`J-?D__KJp(nzMK#V^Wjgq_gG`dQuNFCtsfASB0UFkk|{ zhO?UjzzM%G!9dyi!Fs1*`+m-p;ZceD)=eWI#mZJBQ`V{357qQJ{JnJ>nY@k2YUAU) zI{YO=v-bX(*+h^a$q!`QJ*>JsRumUKn4QpW_juX|*Lr9R<tA5wx6SXU9EA(c#g9{; za)5#86B{Ffo*qFtM|yxU`Q&uhtD%^4!|~=|cqR^d^m*zP=o|U3yMDwje(@Pz{_<nl z_|4SQQxC&OrO8e<)yf`rAkQdO3FQv&VlO@+<(`W+T7ABJ$T#*d_*{WG?H0rBv4MF= zNJ`kG%yrd;t&%^{xis)$hV`JQ&0cUx@PjrIQ}1%OT2L0iPG;G&y1bUE8L<_M9S>|A zZi%zOd>fx)zh2)F!t#Ax62_*2P;ZvDgOsOB?@J`YOud(<vUYnsi|@csi44z~@O1f1 zuz31BKfI7=)xS*$To((Xnrz@5?At`)VyA8zvQ{EBS<}<>FiBP)o=(0~I-+5Pfrua{ z=iEWlHX!GeoQR1;Xh5iP4($x;B&Urzi|s!-iqn~nNc=`A<bW$F>a|BC%Hh(SqGT{w zdYfC+d2C+F+=Khb<SWeNjVJSIw-p89(Re!BmkmwrN--r{(&Pu^Jj?v)x1>icNytwj zLNc}nFGMn<?NK#b>#(YcNP|3Y?c4`WdM<N?e*`XOW#~5obx<#zvEE%2G0Jbj!(}8k zm2yUQjV{@EseDdlfC>ou;oIe+7ZqzBzwCO}u@kM@T$R?EA`?BV^|RG2r;3Vxs*z6e zOwUV&9W%Gr3q;42&cuxQJ6vnI^SB8iAHYh&bf<JD!yiOM@W{8c@4XiR-1WI6N=Y-C zHh*%bK1z<4I8Qun2)F`NuMZZ~!iW2&m-rVQod!PJ9F@NNNOzoIgxh0R&8bfKD1CzY z@D<v`#=<LIwf{8YZtYowFKyQ02Pw$UqBwS+ti)di#Jdme?{}keE8=~9$x~2Dk<tZ> z;kuXH8YjQ}fjRx;Bl~}J0kM9hCJW_5fWh)DC@&EXt}im9QqPqvx*2gIX0$U^bWe+e zpc05C{{LM9=~A1zhP@ny(8owG-Yn<l{Vw8c&N%G$Y`$S^3dcJ%(|78vyX%&?)!Cw} zJ+N~Sb_syj(t-tvW?^4NSC2Ct(Y#N2WTD<)KKy<oCpQcj7&vyq?+-3S9@BJ~qoti7 zP<;*%4xoG(&Plhj=$?1qgqbU%<IS@wv^au)*cDd)^FuZ;Xs&jbB6OF-XQ(=v;A{XB zQdjfGG8tknY4<ED^+-8vn`gIrjNl8rnQq#ru7VMKu&z)Mvm@x1@$G}%b=OQB1>64L zdFt&7OD1mo1W<HqzdkjP2()ghO>H<+d0fyI<+{uK`0g}E3xgpScW6WkrvkJRJJV@9 z+f2jc&CqPT&ommj{5j~lhP~<CagxdSmCy37JH-^J<GZaLi2i##h%IDY9o9M|l~GIF z!g^RtDM7I`QA#~on8=onx(O%opGe2Re+TLCRK91Zrdyzt(83sliRP}=%6he_`5Hsx z<uMs{83IX0Wpn7AR3mMv0H@dXcMwq1Ln)f=!9?VZCGhg9BEy8<H$<eTSxHu1VHOP? z7-+xgY_<879De7Sq~XTwEIQPT#itjD;&z3dQ%cMRr4CWUj%kJyN~a<>DObPf=rwmQ z8k=}=%IdWSLRsa-HCA{hXwA{}%w`^zAAMWSx~JwTFpbtZPhn-NCDooO_!T3Mwg$kp zjACKR1_iuvOY1Z4Bg`j$<IK+xn31={M%~E`vBRo#<r|!RUdOeGhjA*u9ETivQ<zv~ zba?@7_)MI-bbE3-oe5B#d5QG!_Qe7ju_TUgU2Sr;^4%l77^9H%Y)|<v*Z(Z*EDmfk z$}>LcHb5@s!k3uY;A?SExUX$5>zp`!UGg~eppzE5D~VlTpM?~!U|Y#fjYC~vYg?|7 z_{Ru#S4ASPozn(Hf1>f3h!1wov|zL_(Qu1a`Y}!B-~=<J^y}4dF2<nte1M?u;``}f zIxN%S!+3Qhb)c5%)Y_=)(2x5mW)F5W8Z4n?Nbl>urY!%kAD>%H4A0GPef#kS7+$Wj z(pX!@J972r9Vg-ulIw8ytW9P-<Jv(ljk`evOylG}Ra2P-q2mB!Ujp`Xo)nD^Gnf1R zla2DHs~b!%4+LmA0}qOYS{31<__M3&TS0dEZ=m%^piEb`cQ)4uUo3f_^i|3aOVLE& zNAk7hM&Wsf{#uyo*y1wSirwU!P(u(fk#v=kIgp9)^;_2BHy<f!w@b)@lRjB~in4Cg znnf3^nVJqfjo;`tS8LE%B}NkF`$90Qr-NY593DFx89Auj>bsptkNfs(-QB??k`PKm z)*8%CZk)J1vn@rf*hSzhBA_;lnNt-7N9T9wi!<LT5j4&Ix6g8c`Yck3`L9BvaDP=` zTz(8mVG+0p`tE75nJo?s`Fm(SHN_}ywhN%+#r$fy&B$SXM}<^o&hzYC!%UFv16%Zl zc_*&Mjg!F903WUwld<2pM5P-y7a5TZ8qUlN3xhvp_3P}HBnC_2$b0-Ty%pbZnU5>e zy9Nn5{w8^XjLz*~;S4-J-6f~W6pnZg<jcmiiFW9(Mj2R;^kZY!5@8=_sUJ<k0Ud-L zF$54j!TJEwa85;T6#oqmb>Sv0u2t1YA=iSbicg-wmFu5ger2b$Id@-&zR>lfHt03d z?}T)@x-eK74kgU-w#Ih?`*A#&AdqI5V1z;Y(ZLLD@9#Kw$<aR_tK$r+75$3GfdCtb ziAlACEw7A8%{3d62-FH(Y!TlS-HgE;x|iI!`!&p9F(93To;B1JU2VBwOlQ1c>nBKz zhmSyEO6~!`VU(B3u8BVaBmk|9k&K0dN!tGGQb-8vlfz4uPWz`KaE=Hkl_vU{kl+|5 z_%$l3L}7YGKS2v5t@jduEnZjx6_q20=DdKdj}mV+<8J2fHi6i`G8?b+TEm+V-7Cpa z)#xVu4sN0OVOG^IR^X3{c%$Y@DG4vL(b9iFu_>Zvvvof#RDj|JL6X~w^2hZ<C172+ z4r<JefAB?1<iry04^0;G0@{^wDxqu_zNmc;yS(Ie(ArNI!zqhYn<w=5p}L~=8<0Q^ zyp+F_kDI^jpf+*ALz(wbR~PVEBq#hQ;-%lXr>DXe4ym*!yY<soWpzQlT5g-4@J<|A zCQvR~PyF$msjWD^*2qWT+=FAKLGgBj=;};cgql1ph9f>pv`(9rNT5lERR1x)>6g%b z-9&-_M+?D=^VTPAH#nJK6Ct^+k=Qk&%7Th%&NNYxvgufp<nP0$os}^ewVK22o4K`O zmCg<U@vB59!K2<%jY}HPiMad;U3qvzR=)c6)djyu^FZLw{%Jd3e6HNMl+}RE@Va<{ z>vlS%df}i?d9cz$!AFSlK$Y0$$CE}SLYy`Jh;MB^6?*<>|JZ7xbPd?9BXJ)VF;H<n zI-)(<YA2cwx68^^LJ5Evv;9-uhb3B=?<QN`pNp=x=y`P?Mu<4reAH)x9+JXj(DHEz z{rK@21|MFy9f&Rot&@+UW0Nyg<6swR4~D#R%JsND{4<C`{j7J&D3P!26a?Iq$y1U~ zm<ur<c_3#1vw{hnw@LBPWLc<@s+IT|8u*vvZ+|_DUL*Wg15S{oucU>)v`vvW$aPcq zue3TQG93J-*V?eZ;C002Qw;F{ZzXAJfeu}F)segZbGNCfL{Z@zTrTOvx79;*{8W$D z!7gr@h(3Z){ZRnz*~<K3Q+XWyww1wsz^~}G)4QCQVBHn0uXXP#t$n!^vLW!6jySit z%9J3$f^BRXVEg>?djPG<3r0?+v%JQPkLzM~#dK3BR*+AZ%}P$xZfE_jB~qW9=~x$H znh_h~2`pdcb8I=W7pR<*oAMY-CC=x9l5G3Q?8~z{iP~bYWv_eLmq$3GD_EjKEzvW9 z13yJ#O25;C@C$JeH^w2!>sj?<XfnU%Y_sR$JMZrEgDmQ;yV+6>Mh|_>q4j>NG5gD5 zQadgzCcM5m)=`$(0G6mrl8L9|&1Dl}Lz}i;UrtlvtrpPWVid1G4lz^tL++{mTK1(X zmVOvHMJW~rZAmwxVPP}~CaW5>rxlRNZDevY*@vefrgN{|r%p)wZrlM}#`O82Y18u= z!%F+8Zr<n*u1ws|(@n<T+fh@?4WN+dyz|!qUIp2cvWM@pRa7R#UVm)fu?gv4-)Bs` zTs_FgN0I<dIrbx;i%pN-l-31aTBvm8Cp$xjqP*Q<Gmk1abh=XbD=<0lQk25Yx-Lt6 z_IL`ds!mQrUGd=OPy4><7MgNn&Cv~Au~-qZokRMA8lr<O2?%aZ$`_V~OgO>9QWBOX zPk_nw)&Kul>+v-z%%T-4ul><zR8zKkWmL9O+T=~j`v}h`HhA7@8e|6U9wM=~fby@h z5?_5U9zKgy!4xD@K_=Wt@sC&qfZpDN!?Nx^U{_|p0b5ddq*=H_x;4&#`M}IJg@EdN z(zcj^@a)596e&iH{EW$z`!q%J1{s<0nLmv8mdH%<?h|p#H#{=3FB@ysRsBR&G29ww zURP6p2>Dw|4_3eN4XAGbzc{0+)m+L@th2nyF0kJ_gYo%wJGRXV?Y{&8KEtUnf97@W zi-2ASL@+Y8+dqslu0`AFMnX1((`IVdJMJa;J;n>Ohn`=C4^+ve4QX5}c00zFM-!X= zoy<jUX?cb8OH`VN5x{(mB!|WtA=CjwvePxYq&@Var)wma#I{2r#xJ>7ZagN5wL5uJ zu#;)urYH5d2cr-(%JWseu+Z{b&{Xo^T8>si354mRAZ$XRf8u6@02fI9S6?+XS4!6O zsSB%fR=%8yt1{n?Wc*M-_eyLy^J*}D6QHZ^ldI<{Ac(X#N~f0-JAR%I!M@=zE?W;z zw_<i62j@n;)CeS+sj_k+qUBwIN09Dq43==L<X7oxP5`}$TwUf<O7wJm&|3roat11C zMgHW=0;SLC#UB|pW|YcOU*U8|_l9u+iLMzOI1x6xiVH)w+(j#<XTtwBi%WJANn5iC z)3g0_%l3A$9x^yh2v5Pv#J*hmT(iA_Q-vY1y3X77p4?4=Q48P+wK=$&zZ?Vi(YtP= z$eZOF65eXgsEO_DRTwsuMY?AgEL;}&@t(q~TmE>I(}aCH`uY^rnyRNA80o_0h&$oi z1x*NrI^wfRO4)r{%F336F#elG3r7tO<By()NA9$00yP&0m_Y<P45$g?8G~0>NCv*} z<o*fk44RfyBP$RgjRzE*#yHVP@|u*+e8>GJgv4U(S5QKY88|8>p8{7J(T~eo>Vr)B z1p>UW3qoidzRO#l;w#N}QUgrFJe(Ajd^LS~qq8*(U>>#IWl)kCD6RORObhe^Cq7>p zi2n<AN|dL+E@_zpAF`hctypOqjIPnm{X45}7qq*Tz7_e<Sx`~bAG=W^DlyUif@ayb zsv39dn-{I1UvWB#<4G%g8G46Q2_z@blR8b3GSRw@X1HQfPYr54)Tk6wq~z>S%0QLo z7%IE#VwR`YN7tLYVKqDmV`%Hsl0SJ30~lu1pSMyDS0;a|$JQ6BY7<>)<vBBkMqh0t zb%~-sqQ0%HU)m3Co^QruB7n#V7+!M_T<82K5lzr$HJD4<rU&6Z64ap$LhDAt&Y@5D zm92<C8@`Q}P`)E;11SYVb|iu*pn&_EUZzF}y_uk0&n)9A$4<4+QskI+xQ^LXlRN`> zzNcLyxujkL(LU65J6HR=a7Jmt>2v(oFIWPg_v;7-zVyHFo+urQLED8zg%21YjB+&z zH=BA2ej12$wAjA6fInf23A7#-4z#-b#h%w4rEw&w>@zn7FbZV9G=T8a|HPT}Y0#DU z%jHn6D5M2h>2?mAXzcJTT&YQETdFUpZy?D$hRO);CQyNWsMefKmhSN~$veJ`U<wk( zMAGju%Lfd!fqum6Ra24K2(4<uqTWlvg`JcseE)Sp;ipK7-IxK;PU4+4Ml<T{$YaYc zGr7%#{95mI#C;%Q{}-IMb8q}Zn&%%}j6O3Z(dS-vjkkLG`@;3u@t+~ai1o^|wZT6T zBNl3tGWx2pH@uo(FKp%!0FCcu!(aDOkvFvwR^@&<U&UL4Kh+Q8i8e@0C~#A0Pvr~& z50O60pW3NUHa_h6f~I)YkM}FX($qF9{rS0^5CS*%J@_WPN@Dtql-_zH2=X4xONE$r zBq)@%TuZY2B^rK>e3Z9gB$P#(_;MCMp-q62Ju5>OrW(j*OZPNrJA&fg06KB)YLX=Z zi`EReCGs;Di(g=D1<`D`>i@TY2Wu$eDDXGCf+N}KQ?5(vr+yDV(^(x3B6uwl;H^z; z74gBvH_WX=W_(+lMa(Wv)EVzA-YC_DpdVzVBsq+=Zs<DTNbPF<ru8?+PR^NYkFf`| z0@EwJV{mmi`(N5JcTv?piYyfUm8aJ0f7zrL<asos5kj|mG*7-NOyHlkdFOq77&Gt8 zM+aDVXZI)VkZ*voJ*}y%Q|Ap?mxFCVh}&Vc&t?0tTBG3;r)$jc>ATVA8_PF4O-p*E z`X6cFOI#bCKd1bJp8EO=A_M<>TWs(bZJIL^a0k$l8k5whx>d^%gT7&+O4a`Z<{ZLY zj@06?0^fKLN?u+0-;heS|0Yto+(P}hI*V$|_`RRd^1VxxpG%{((vA^pX<K2fgTDp? zc|`vwaeb}FVOhM9Lz{SgQfv3HstP1Nd5NLEy;44Ld`Nuiy{|ucveA^W8j6D1R(3{R zOXPm(QmA-62!FX*NmK{Ay9}9C;%4LJ|F}X)R^M=Qs9&@HfEOG(GtMKzj)QC4PScK{ z#g8dbLQk<?NB?n~hRc${TFiD82AHjbj!b;B$$b6jvZxsn?<Of42!V-J8t#^TCgTMy z2ZN%t%QJym2N+~23~!&-)9MwTvXs3a`Q}5GJj|Z)h225vO43@B4Z2Ldh=4eN;ft&i zA_Eh;qhz#}tw+tYZ0`WV#fP1FyS+t6tQSx1X+aS5LWjpQpv4mk$|tJ%+$`!`Cl>Ws zG$J;HEx~z&i(_6dC7(bo)=>=(Q7@HOGD?KPl@PZYCT$Cv^~w091z1*Dde`Z}2*DCd zzt_v-Rum{ZN%HFArhgi<AVYkM_d{6PJo+IOKwL8&n-5l!Q`L`Px!D5))%D4m>2uVL z2-9Y)WDwl+cR-(nMIc3d5hh>+;LZt|nabn44sU)Lgy+?D6v-8!kGNG_2Cb(zeH(co zRTX|l&DrX3Oh4nrLIu4sZMtyQ9o_OMmq7T5(r5TfmfgR*C5`@=Z}-2ece3x6zXoLv z+cY1?$D1MOJ)_$2)4(my4-CZQrPI9{YZPJzY0oHa!h-Z$YzoOUta>p(g#xr^;{o)q zxr<^8h52{=LHo6eug(+vMH1&0ej%Q3)7QfA3)tl=4KJ53k5Ghu6FKyPYnQ2iI`o<Y z({&(W$&Iqr0b2}^TBBWZ)GKZ)jUg4Q>^C!<a&NnL7n@%KUUc;Y%=(Ztb-+=`qm;g1 z0*t{+t*6;z{QfV?3<TR!3p&-z`H%I}9xvT4QtvHE6MrQH<b6y-<^%GQB}y{k5)F|) zuthlh#|lfVbYixof^I8JxKC+0w~xO4yt+l5o-i)H2zaF055OZ*oRR_x)7nbhE@$6} zLCIaAW<^BdHRHBXBph+BAC)0v&;{g9)o%yAnx&9$tDW7Epb9j)<TP^UiN@N{7y_&1 z2*zq5h2fczMn_>nUNjf`^T1+)K*RoKg`JfqeL|Z`tKs2%sO})5|GoB!XsJ2bk4l!G zB_>!cgtCR=wl~w%TFX)snNRVO5{e9(_s!E<XiI}s`h&#)(<$8}2k3tF?6GJFHnBZ) z<VN7yqeZiU0g@;VaTdk&@uu!{y7yUX_3zpA2C)E-E_ZymxiC&6<uM+TYT*tJO)A#g zoEA`lFDSKN7BeO~5(`)qj)*Q^pz=sjnf}T~lJX;R!A2b6;fu2J(4--hgb!i%%<1rz za3CV~IO8BWG~lAa>934_Ssv&~{uL`clk^xJ5W<;{GnC2c%-l#(t>MCz2L<{1^VllE z%1N0k<W08HvOPKmhKbh~TDf;9-}Jm|WVV<*Q(C(~-*uKT#&;-HqU!A!Mv?+M<m2h( z?^$pQHgn~`y8U$hOSLfhQ$|DHokb$m{vYbzDy+^ZOB)OXw*bN2-Q5YnEx5Zw(BSUw z?(PJ4x8UyX9vp&0fcZ#Ob@xBrGf#DO&C@lP=XRg<t-aQ3`<y478S5-#-DWFpq~o_K z((rPe>#m0witS;AnYC-Hu`6VSSxI&u51Z$gvy8hXAMgYIjuU+A`EdF*Wgv~PqpD~B znmvH^`i$Znsf-N^BF5L6pnaz)SESD*r7+Pza-lvvy>WlIyLqG8Yn;3y&DMCqr4uLd zwfBdJ#a6(CRTE?I`_DFR@0f3s#^dBETnjL{;lZP)3s}w6yFFU-C@fP2Nc^t3$XvFu zPWRFVRiND~q}<OH0Zg!l=r7wpVs1h3ALYkXOQq1zW%w-_ldtb8NSXk<S8B05mrF#n zSm3}oKgwjfmU>1@;X#6I3oDA|hY#S;4j0m)6t0w*mnwKyD%buT2r5Us1rQ$sU#niX z&+oLZTVn$5Iy!K|EWU+O3`3y2=K?*Dh(X<{(tS_y<ooHXFWHd|FwBF%??^N_W;E|F z>?C>_{G5(+J0Q;@kmTdu=amjb&0mL6@=&0Wa-QyhwB1)Y^Z!IxuI%nNp<$iD`h7Rc zTE*b7P=%K?l;Yea-y0q0UN&&r!R<U%^NDwK=639We2u_aDmH{5m{T>wF*e5;EgZBO z2xnzi(NAIIS|`3U@c6~gU;MKOklJQ8JRbUv0W-UA;y7;fd)SnL#A^^O#^ytxmVV^K z`%OW#OwiH|7w7$7xT;)kGq8L7ZZzkkkOw(seHB0|2&=9*Ep0|04c*>0wbMZK)Evqz zF0j#S)iyoaB@yNy&Smn3rc=KOL!was<Db#?%2$_ES3}b-_HT*=u0NtE(>0acU1t%u zkC*}iXWnDkY5dZPlo@RzVkG}hrF~B(9PgeJ!WHI8uycnc3`+L1iW=0o@#V#0$f<Sw zX&V<)r^6hLYDMk}3vG_gfLzAnCM1Q?*_Obd|3~N)!QwkAzExs>g{UelJ%B&bTf(jy zyamKj{9y%9z%i{-M>YKZ#U=t(ZWc#Mn$S@mP*Ef`8v(+48euJ5W_F&gxuY{fW>z<L ztU5z>d)}7`(M*0f#9!bA0<k-NK=nAP!(HUK@Ce9&l3A<}eb;r2y4t-P=xrz0ELbdK zmg^A5t?9<b^uJj=NA{f4tSs-<Q<brB+|0w#Wb+oS_yW|6FY*TcMHW?LKo@<+hqPR1 z0q;<4Gm#M^eBs0CRF?8h2^r!87#O%|YL_XJzk%Q%o154!hn!Z)NQOdZ<>ScIcJH># z;^Mu;U)R9X#seP*{}lXZ(~6I0h;QN>jQ?7EBiMa&Asa0lG?~Afo!`$w!^ZKsqzXXk zIH?_h8HHP?y!Y2~m>d0%S_AfQO6+(!_B>neFX+ODuwbN`Wc4Oc6WK|;P{b7Rlf>?f z5e_+f(vXGarg(|<m&@#5$G!A10kvk*c}PKic`MPRXT!k3=Xz=6<uZMEH5s@0!PuXy zr};wCrIkgbR_jzcj4~@Cw<#kxkI9;1={$lUaYN)cqul0-hvzz4OV%$f;!50M>?bQt zcZ)u~EId)oIO2HPP$vWl@(mxZ1LWgPZ7C(m+KN;qWm~$ranHLOm9<&F|6DzTz0CLl za9z4Pkt(q4du%6ajZb1DF*`*&a@n>C=>{>-IzjT+5(;-wD#UKM^h|m6CQ(Fxj@#~B zmEdp34R8+7V7wVXJ+J+HJ-@6;s_o<d{Ys~QKkU%=@AleoJY}l)&@)S1irkNRD`^L; z#1=8OR_-R+tDML(@ds3nfX6h>dG6P{on8Z=HD)n_!F}DAm=!_ud<H3E6YzXU9P`xc zPbVcM8`m$#kMMc4UiP~CimrQdT3qUM37R*Otfh}%!d3sQ_l-c7L}6bb#7}}Rh%Z7} zzv+3^?e~LY0rtX!sk6lNnXheX5yTH7qEr-5=GkMG^<<P-hOcQj39s0Evyx(pM7e3E zsu&x;unMA3cMQ*c^G6U34$N&IH*<%}d3vb`Dne#GR_jy)j=0tAJBl-ePd|k>B|`IQ z_q=>{QQN1bI4z;x6-b&-*ZESfh5e;l><Y*6e2e&hlZ976zw)C4`VR+H6rBG9Vuuzu zGKg_#V8R`#Xy%9p2lu%SjZMP=_er3ksFwI>#k5KTsaK;7lNe$ctcI{%D5Tc%Eq**s zy5-aO?<_X3Sx!JBF5DnFU*A@3XqQFFy1>)CJ6dr?f71RFpfb+A3^*h8e4$&3bib;6 zXq-TpAmIB#5Xzid!(D7PmDeuHS8UA}TW?HHwF1dBulWWQ;%WFwOsurCeM7{MSto1c z-Yk);%#3e^_aB|n-|+KlsNj9~QJb#x4h5?a2J_hL94@t}zQW9m+E1vcZ4BNy^A<J= zMf-ZUMuU8TSEtU%Go#VoaiIH-fu=6jKM2-6ncNU~0ba#1+SeW`*6+7k)F<Q5paOaw zxH^b5-`7|zta>8oX`Y6N-zE=85A@gPW@Ma*pvn(b_Gs9XTUNbpwC^pGZEh98BuJ19 zzs^5V>CN?UlFR}NW@1SGC;}Yp8}0?b>h=LJH%@<G^?Tz_q^l=oev%P*Bb(C^hHL^1 z8cPZ-e<j;=G9~26O-LJN*mqn<b_rJ6C~x1V!3Y``6iI?BiyA}#%u)~%oG-|#E-1l4 zfNmFg90F>7OjT&qG@LY*0(Dhl^{o{QAG;qS(KR?B1%$I>m_oMVp!p)R{yG*8yQAmC zALvew{yZFF7Wo0*?IIdszASfl6I#PtXpoNkM+u>rNA&2+v`_phv2g(8Hroj=Tce}X zxr_Pb^V3qEgzSk&nasL4hhn3HTj>|s88XL=v1gl!wm7#vFUL<?>_K!7ePK#>6Q<6s z`U8Qy0}STUJ3ZFyV6H=q@ij{@6Nc_x{Kyi+$ccuoBR@k6YMp<;7Wo5wzs=W&29TMr zmjsJWc*$W|_#cLwPxsz1I$eG)sR?AS2S-Swn8&)|8gmJvw}mtKe>7ZQjXpr{PF^bP zOn`Y3b;`rZC}``W!m|HCyOU<(h0mePWEFJ_1a2Dql1Y2MGywhi4Ov(?OL>0o2z$LZ zyfmmiI;dD}wpu`vy6eWL?}t7OUh=US<(rmRQtR0o4FbCmQfqa1ZKZC&OYQjnP~C_9 z!lO6G9!UFO`1*Uk*4~UC?3{`X#}w!tx#w>0e%$8}TOBs7MBR-HXkOFnsINP{EH_d$ zCRuWM7t{g|y_h8EZIOM@W%2n&(9{vH>MrrQI<%<WhG|gHy0X_1XOaKmBnVxTlcRD) z$okOYC6za#HF7cuyx1(kX5l1bx)ZnGU%qbQr|>u`XO+j}UVH!mZ>Rq-uqOj8dFSYF z%Yueb0tY(q3wszejV55wC-I)KrT(gt<n3bfi21*(8R5YfdISYN<9?2N<f5qJ@e>`V zV5rKjt}%PP&z069Q+&?e*k;B{zXJrO&#nM`9Ns9|(B)qrsaj2NO!_6?TatGq6cHu2 z>D$MbUj_bo;=Hj|_rGvu#CTQe9s)yHlX!J5BY+Wtxe5_z6ejP^HQc_N#U+$$NV^Fh zFTc;fGOjYilKW+zz^fk;_5QwcPQwkcE(sld(Y7<7;R#Px0QMIk))EeRnIn|+ypVd( z(W&h3`PLjkTWs9zKm8a95&wH;_{(z=j$*kfGbq@@^m_;RMFHs8rT~VA&cX*9tLL+C zF4$G<=!ux=&y&Be+zhpOTcAw1CM4a%e>A?22bFtFqpq#RChcRrMJGJ20tSksm&>md zTIz9ysG67&|DS261AULTatLw|a>OlhQ1pv28G1qnhS<+;(3ZZaHK0bN=)8y_48xr~ z-h>f8I7L*tw>O#f?Y~dfFmQ+dAwk(i_;YVr8eF0<k!d<doD?yX$YNl5(jDxeE0T8I zcVK|dr4Z*B(WU||63^VjW>mxSjW2jZJ3Jsv4?tKnSb<+fZnr@#ZYmsJPQBB|8=9>3 z?xiZF>a%r1QD2w2$>!6Uzk}hlk#>}O4_xW1Sz{wWTMbEL$NCGr0QR)%!tMT$`ltiT zAIioK+z97f*L?dcC8k1LSYX4F(w^vIL(;+w8iIP9#N-KB^10b-ZucJPzgd@`<g?*c ztlSTPb>_I?0w2&1WN1YZTa`BA^_J*4w?uScMMJ@86quctfGx8?Yu+jTW#EXLr~5ef zZ=&93t^YV~gxqZe-NpjaYV?2etNoj_x?1#-8=2eI1QhPEz4pW)bSqKKp50EnCe_1U zN+-fU6nv6BOtC=*jh9$SXmV%7oXgc>GrYw-i?+GRX@BVhZBw?%zU2;8rdpk&o-}g@ zeSe^2F@{zt0&mHzP+|g-U>xxFq_dtrqGC1mkNoer?fYymM+JKaeA;hPpRBHK$nvPD zoWdK=<=MEx)Y}$zWxa)9DArR;>xFby+Y)JM$<?;$M$uo8<h~hP#fap5zkl})yA>@& z(XRjxg7V+OM9<Vd%1#mp;c1XYUnI~#;NK2#`-;ENE;?Jzyo3**J{Qfsc-n6Tw4-@G zb#MY($(7`uf$cnc<yzQND3l!mYXZuv*)&vL4~1OlE#}2wGZAEiOlNwH&DsxN1?TW_ zAp+Dc)uW*=?>-wOyNbr&5jlJk5d&}`eHJu%A0tF920*^{Q?{l54Hyc#`aG$J8u7O) zN>Lt+)!~z=Z#wa@M=b_&jMuKc1VPT$IcP$?vu%)Ljpd??vw*|ZOiIK8pMq~?;J46^ z#&fcJ2q{`o0|O1`Ug#sJhBPXDvCUr+SX#98`o?5s`aq-~u|-v?KA%RE4>jOBSK5wr zFR`{hj_Ww>!7AzCxA;dQUv<nBwN&4{tMTW%7!5d&q<IiEmk!fxtMdlpAe**ejE0um z8#}c8Y;fSZ#aHNjF2;cw87k2^x7-9FQqKFmXVvcJRTPRAU&F8N6!Z0pRXM=LrZB>Y zQs-Q2x;Ulg$p|uE)kP*u04YqF7QRVMk!epq^aPSR$Bi&nygF|nf3p|0n+bS*fcwMY zfq1DgirTB*hL|nCj}GTP^&LB&@YXOr=m8m6%9+qm?*Mx%fF3dZk%1}%dm2GPStR@? zy;P#RVUtmCy9O8^7@b;-MHY*;%o}IZ#=-@X3UD@$)R34p-7|CXZ<7PL^nY)1$h{$j zf_DYYCyK6^%&zG2x9lr?UR;JX{{Yx_@IRU__M>5M8f3zUS%fV#WTWI{Sd^gXf%z+N zWl)*fdmy&U%B=Y)i1Ep~DojFdjFfFKGnvec#T|QIYxW)XByA7UT0HC})R(hb=8p3) zvRZC$(Vx3(SSJN$a^l}6j-yRZFPU$YVYWF7irmnp!-hxdx+c0o3UoM=6vP^`QTi^y znBYo$&G$15z_eBTzR3ID&0LoZEy(xTU2OgEZmgRTMwM&D>FZejG;Q*25cU|`h$;~L zEWu^QMaXn{DP5js9|6arQ9wQd&|ym;pc)0!N`S)l-x-(KOj3MID@^@!G@lPXlFB42 z<Pi%ql&$%=^budY_W{P`<sW5SCdLT-O+Dl$Ibgw<ssj&(s~(Z4vA<c+u|f4+9cpc; ze>?0tsa)kf*0`H^KHStyzxE$SYbDAi%c+4;>6Pc`0ZorV70B6L$w$<Qt58hm#6^{) zNT1A0>|KB1jG#40vgp19+rX3a%^PSte~fE|adv~vZn%ydkj9(Y^mvjPVDBPNa9Lg< zo7cVkG|5x0GEBB_+%CFkBcd9W9{05CVSYbds;$Mz<n&SEa;{vXRBvA`lDVY>@G-1< zpt-LtG7k<Tgy$Iw#f`{$-sSW*JB+a7e`0+~DoWZGU`V$PE2yO~#~)K|uWAAOja#2? zIEAOQgw))JFr+$O4i&JHf0AhthornIPGF`g0j*~Fe@m<RkBep3rMnM|5kJ>;P?)BJ z`?BN=-9SHNyX>}m%~|os%hltCS-d`443R$9R;ADmFKY}W@Yg!8wJC8|V`Jc*#^?l$ zJPPf{){J74#Cb(4O&yQtPr3QyjwV|3a_BAH2AQzFeEU6ol|{jQI<*ZPqk#d~cQZ5i zcV8uW8WYY^=Sy`z^4Gy^@2)=pQcLICpRqa-8lE~LJphx)fdXev4sgJ0TOET)%Kbj6 zvraBI4t}xjzGWxbf-!*<a#|9t<N8VAHSkYlNtU=w=B$xlIF553(C^gj_ck@9?)hVu zk`+3!%~dWmv45Pl_OQV5tc)3AFut)|GXG1I4BY47d35lI+Nv&uGJ$r1E1%>oA%Xh4 z!KY(K{GsnFH)8!q8&PyWnhT27GJUi<Umi!N4{GKOdca1T3Bfo%Wq<l^hmj%`PJbi7 z#0jZ}%1aChy-FMpIJ+I;y85BwsGP2o*PIXSP_xc-?qjqU<Fg$C${T0ds>MIwrGIeY zMs+R)?cmPc#jhmKLJ~wfxTzyXzN!<Og85k>`I`g*{Hi89n-{=6)@GDtR2wYSW_<Pq z*ZJNNggg0&ptg5O9=5K}LJHgw!n;7I<t*fg3GRsZ{wyCT)*l-Ftu)44q-c;bywE!O zV|r=kJk4W7a&HHgeTyt7E@^BHdi!HOJA+KzP?r$ExC{-y3Hkq_4vvFE+3G0W|Jf0q z{r+Q3cek#-@^nzb<*$5{KgNuq0g?SrXz>*PwP9P8ESAuiGdK;|=&)aoONMO;D(7$! zL@D%_Uy*Ya3dohkgh^%RLH&1~5A<D)Lx70q_d-$9ZJP3v@{lo2&06~6kTQhC4>*MG zkaBwj&=^;p6tx&sX`aKt^Xr4TGrXAE%YAW&q^J@ekHu-3+YuS56f$#llsf!=KlSSQ zx=&X}yS+^BHTh?W?}&M?04qEOcBIyw@>o~EaZ$yhTKhBN_-~_e`FCC(pYkDU&Y>uL zP>6U@5!ZauNik>ZR1thKW*AbUG8n5CH`k#@s?k?JJkoUJpXYY+wdW7)9yLlfmMyzR zJF-PrlTepY1dNl2eUM?o#Ia{_i$FL|8&?@x^_@Q41Q_+CxBVMYmHs~wyvb3Rve4|@ z%!QTN158b46J$`v)NT^&@)Ce&e*A1iLHua9tk>~(;fa=E3VV%iH!78Fwt4RC^MlvT zY>ow@`)l?hm_(N9Cz9-VLC9VJ;3l;wE_*|`=a46%Nj@jaVucF&qd;xBs3_8hG;>&& z?3xRwRd{t=`)0z*v^(^(w~W5%z0&Y3b~HII01n3(lvlMaiE024qK4eCQ9%rNcNd%p zZ@qp;H*N|%?REK~vi$c^^!5k637`3!cZ;ZKMaQn#D-BvQu%PQGq~4&~y@)Ds;J3V+ zH+8D(pS;&W5TO_2T=zV3t-I+5l;BmHthCpxEZhPt;Xj2R7#ktC@3izM#Vn2KDoL46 z{}lMNK(Nz;FkbCZpt}Q57+FgW>jzdTw<TU*dd4Bz$yE%lD$1YQw6G>|S9yLSIlxq3 z%XfLC1W`egpi%IuAeQB-q;h>}!}_ZNO@#QLuRzPTMk}$%qG8G_7!?$FDVDK$`7ulj zNuFTYC+~lpN#FN%$gXAVwJ(~7XZ>Y1x8Ck!m_;=;<#*i3z4dC{6kc2_80w4O3aEhN zyLS1O@|6=-K<xU<EpOxtuA)C+j__7JVWp)qJa4YeY|g?}b?z6lK0M)_Im)76Pp>yV z2f*i&)B<g089e`6eBS+RsC)ZbqYXxje%5OH5n@aSFE?Y#qs1#%y89&q7eE?!+54k^ zG}RafGf>0(tF6Bs<LKH$6rv^At{W0OAxo&de@ovs;+vdU)pP*%2f$8UZm;Aafw+0C z&D7|hTwAd)l{hJ2Zq0&V5(0{w&?!|h@I(RoZh&sh6FkIvlic8p!wLi`vHZ=tqacnn zI}4HnZ5vs3H>NKp^b#{8mo0S!TVO&6|KcmF=1ct*)!ivdO)Z}8xoCcyGaC?ZT@D?u zb2dKx=;TxQW!v2!;TD-&2F|C?2?NE|L=Oq$ZwK6TD?Lm#CkPn&8RQ*jm22Oe_2(i1 z5G~_Q2=j|c7Z(_n0HciC{$OlpXElsy`IPpD$YZn8n`g5LoHwUO=_LV1tbTb36Z<i@ z^3ay#@4Rqt3kmkC!Q^rlW!vmz)Ol=_vL$Byvs7xxgRSz{UiddE)jX3%gf+~}-5xRv zrEP+x{0<IMBC`djmt3rpIA6m+Hy4=R@=gAim}~*4smI*xZ)i}VA*rrt2m(e#URA=h z!|FKBkDBu>fHv+jZ43O(fm1o~`DD|dSBxBmT`%Ueuw7JceN)l|6C+;z*OZx9DUqm+ zNbqN9CDP15!GM(Nujpt?>rBmOMFv!ze1_0~p}FMU*JX2^DAk$uUye0}&l?>NMZEI< z1+}lvrN*-Rj{sCZl7>J~!qe}oDi>vwz-v^Ho5U9lD0D;Lv%@Ph4H15h2-PydPKyGp z%I!NUQB*BeD*GRv==)=ERF$9mW5-C3H5~C>H=IJ(qp9Pgq-p%}l^7VNq+3L6ng6#Z z<31s2KyB&|QJY%Fh7A6Jb>nICbvrzeacMEieyGSQ-St;}oXq1$Zx(;z(_nZ@Ti}|r zGyWc8*HB8KCE=5?NdYA8{>L!p8Vcs2pXhkOBVQMvO!pu0`Bh#DL}9Bzx5Bq1U)l6X z9ZPI8inNxS?)KSBCvdhhBEl4`=$&)xjz<Fl0mddAINeRiyNIG3g%Cn<?L}$yeC(l? z4`#kRIgpqEs9#?IE$*mx$IiG4)wqZAi-}cwXx6!dhRpyqYHgym&Pt08BdAeXth%wH zlqp0t{PZ!@;XKBl`S<XDeg4(YEsS_SN9!(p>8JITI`x(ueJPT%wdq`+hr<!@-g6JA z@O4yuI~&g6AV!UXW#kx$6=BlDhmyCBc0lxFPBDZUl7a?3x`=R#LM>Zi@37Fg<BaHZ zb$OVptY~tPKdlS3RGj$%S(#0P?7sAv=(b++bTcyI^)wtx$6R%Mvfl^qws6WBIu(s+ z6pYC>8;7&Y3MG5Aq*LP?&S@ysuD_smqRT*$cNSP}lmtK8QGA!X5kYgp)U6@FTe%E~ zTPQWvExNWw<G{ZZnGf!MH|cWx=!@a&{ie4l`UfH5|GBJ7=s3)`UyXo*>}w!bSA@H4 z`vn|%&ejy+EdLBu!v=58(RPO#{_{Y*f>o;P<Fnnx{o<^V24jLKbDYh-Diq3yVVmP| zSc0+7>KI?B(gz=iwJl21dq3<W77J8vA}58r=7_to;;ef2AaiMrdNZ9UHTXMYmhj<~ z__d#>dy-NVxJ*aH*|K|N-{w<l-xC7{?NC4KxX-&!pYwbQkQSFVOGfLx+*emp?{|+2 z-M1oBl0J-MGg?dQ?P#k)N4&qmJM?#mFszt?{k6b5Q_lFO6HNarbb?yH-A_!Rm@nON z${em`o=-W#b7KZ*m8V=eM~ywdCQcjPxh3j1PV<ET>aNS4{oi%I@u1Bxv6~&MzVS$) zltR!#aZjKiy%&CYl@e?xBNZF%*@Zw>t-!r^AhIbzgU1FVj!JcvBf2AW*O5M4Ol-hA zKg9gZ*ucU>tCrfzKyFwNdX(GErA2q=Z)<co7*2QQN}X8B-sLNF_{q-;<e`r;C7!jW zA8#pE+U_X$`Ua7RZ8XOOg{*4KNm`E_{O!$sF^DSPN;C6cpfpo)?Gf_jLWw?|uD)9B zJtpP=`C5z|cN%wN57*KPoer<78<7;wWVJ|ph*Z4iQ}<pjZoE0Zu}0<e)V1;^Mz=w% z#c3UDKFEr4K|2zIiI`h~>+N^luQ~FGzou2?ZlHo>zn^>(7gUgBUsGE^#81ucNA=*b zKk>_eT+dKyE}w`$0!W>Q-UtPLhZ&Z(kj8LI4^lU+wfXnwWD;tKGaF6;wCn}hb_AuR z+WHMqIK7BVZxV8nG3CP{rhuO+j^17WJqHnIM1D@EZ8RY^kCybGSfuH{Cl(3R9Mk-~ zaMO+%icnn#g=HL6fKhJdB-<|frP${F;Gz|u<(yx6zR6@bRys<$^x?pclpilTV65XU z(kAlTUf#j{MEPoi)d69=3`H&njBo7c?I+|wd1z?cI9FaFvRI~XJ@zD*B#6A4zLJ<* z!NTQ(9)>ne)fVq5GK^q^fw~(VXJ8~nbHsIaQkv-ymt?!iXgfNeh}YyIc#7=^^I{@6 z&iuAFF>0Qy(p=tRwvp*~u1C>^P~V{zAJE!E=?iwNf}?pO;mpnAhe1^TR(m=BBDE*~ z9@7Eg)+%A8Q9ltmU~(k@+ZtEH^{fZBD&tV)Jl)0RwNGn6WmLPN!1LIL7jmOd@4Ytg zoE;R-zQ{cjY?>;-l1jt423r$Rl!z*JGWz0;lj#T`!0IoFk|nsl@wbVte>p#?kbUE* zwx`PWpml1ET9eObk7-$2k2gSyLdN4STxKs$j>ac!r7P;j(^aPvReeSV$n9r&4h60+ zNF-p*%}0J0GesZtgTq_I7sT}cK?4KQq~ZCO>=S7`5^eHm!2y|wPKo~`5=xQAJ4eeZ zizbI<kq`^tV>Mf#Iy9v{&=Rs%`ONv7+|F~Q;%`UO7w|Q7?OEol55%yt^zAX7dTKkF ziGu4V_`fgp(@`F5oDF>*ghVz!9igzRZH~*+%@@hYGUcYOt=qCRx*jOl9k;e=zKjgm z(jT_}JgK;Py%oA7K3D8is`@7Wd?wwB&~xfxZSJ>o9{DA??9ku^-!R<U9d??9;Pin{ z1mFo7r2P_MENZ?Yz2^HSO3=uOf#(p%=T+N?86k2gw-t!TNGF04t3GCZYIJ-_$Ol6G za?Y7}9A9$0_fMveW?6YF{in-wHeH=5#EJz?r)43FP`-?(tBVK}lBTkxLE^`kao^$s zk#P=_@k*T->30!yc6?rL{dX<2ZHEuj2j~^uQ~_hHQ8*mG_{!(v?HYnf4PEx~4?h|@ zQG``MkKtj(N)&zWc5sEtsqSm_JNz~}R<UkNU_KYwJ@d7~o#mh`Xg|yGpe)Rlw#C?f z$_^Gw*}|gxE7)(JcL<Q5p<IPGKY|&R6os1&V}q6cOeiG18nlY1D;J-Esj%nydv{m^ zAO;3RY*Mw^QM&uv{{hw@=JTPcy_v{QQp4V>$s&qXQZx_AA*v4fBA2pkk@bw(B9a{! z=%$DTChrkgNyHamiyrbU84~|IbkECAJJa1KD35-Sfq<sSWms6KJBBhgDx$T-Vrsvg z$P4qFr9b|Sm!iTKYj<@;8a1IFvGgNiENDwy7TJfzJz*6P(d5)fLmkNb%i0_gd=yV{ zPmeCBg-bdlcrum~LC&JT(4Mti=Pz)c@g6FDiFYRM@OW*@;!gz-+=+2()iztv)BWdS zXSqA7>aGvN6-CNT;NT)N#1i-)H^QglU7g%#9;i|@WWVC_4949s#l!<J`tg5)(f_{= zs`w-S{u|c#Nmgo>g`49Wfr+9J3u7>~VVkV%DgQ6ey_WU~T2(*M!_E!2sm8Okr!hLa zDl)x`+0^hFT6m2Qn-5Hi3fD&LkVNxcOA9FZBvt~-({WLrtfH7@uJYikh_G??Y|g_p zI6uxo6WI+esuQ#I*R*Le+OPVI_%WlO_gam;4t!jaZbw|5KhK3vkE!mKK~B4oUjaUM zd@4xrmLtKBeP@Whh#~gCUFJUekKJX&*+c6YpHfW5We9>Iv#`7}f6^<me5VW)l?<i$ zLrl^rNEWNU6TE7W0l=pdM0$K99!B`EVVJ1E9LHhpdV|&N`VZliO>iF@fQGV4eU&N~ zutuf`1WYzL4hYcHfRkd5!>tGeJ0bc)71cYzed!67fxNGhZF60KO^IlXpzXW92O^YO zi&ggLdSD;FXmUE3DTw!+6|RZOUfX694QfY=e|;ft?136|B9T5^zRHM5r?%Big`h=M z1yb9@z-bdyiK1aX{|T>(9tMp6ojEjBL-N5mrXl!w8r-iEZ;H`L^#7FOBWuIUgF-S( z$YNIPrvL-UVp#S4w85wkw5pR9T5yAyhS{9bWF6+Je%s+?I^7Ch(VbLZRG^P&PiSQ6 zthD7kFnrgMTFgcM5}<@g>et~hEthE898a-97GHPtevT$_oUVA8jVSLHUxuEu^!PV` z=h|uCfiLi>vDg~p@@bxM|Hk7ppY-13&25C<0#L7<x+xOBncENFCR2K28K9J>sK-?0 zu|NHl_ditsOMDb5ed^zWQH_Yu7s&V?B3JxL)MC^DGkw@^J|4^1et8*3(CF-H+tXCv z9W1L0azj7A?%uNe57dD8hNfm*xzJ#V6&<oBn;=?S&NeQOpeh!eRN3r@bYR*Zfz-TY zH&YB8<A}g&JcT5C<g`o8kjUiBgNQi$T6f@k_v=s7vK+6JKHmB_7u6pc&UjmFcRCJ9 z(;#I^`^A+uI=s%7K8$tdPi(LBC-QO@b!0bSG6!i|4cTDxbr@QJ4vBU?f-Y0s<ij*! z3US31fd&`i;0#{>z|*|n^nPwKJ)}W%+nnNoqi2K1tF;-+MO~3Q&fQMH$AJ>vCa<pZ zA~89nzX`?J-Tw|*mY-)R-d=KbDSc3CXeGd6e?cHKKO!V=3<>oQCqQ^8AVkJ%anl(P zwgzJVhF;p#WniQ}>=|};k%!4Bml~+qH6XSlfR#WwL?vbCgaM^ilhsckTQzJ_L-=9X zjzaTs2(cEYS30D|eupB1Jk0`-9a_iR&)`~cbcuc-f%hAk4og_M#iK1V?~jxRD#6{X zvu~r#iSB!4X&^dWrh3F0^7r}1E|9C1WQSc!A-Z;sJa|Ell_o|;;Z4m7-^^KPHLl|l zy@ILi?rzj9(5k53$BdxF+4CRyP7~}&Gvg4>6zA-MSNIq@59()+EwoS7UGK-i#?N6{ zDZ@rfM`A<5V_k(2Z0+XWEB}(K$zPv+xG%g?VrnG7;&P5AKQ<zwtbGqQFZrMR8~X+j zia*B2!w;!{R>M_1)k6C&4QEbA!~^2hl-j3Xim_ar)YZ71ly2y1iC61WWGk42OhJyY zyZi&EaWSvz)rf@j12u>sBozJ(er1z(@@V<Y)Px-E6taP06l+wx9!7sY#BTPOxM|9O zxZ2O}s|DzNmAceI4uyD3H=3R|rysQp&|RaVwC4Gb>$r{eD`&HKV`r?lLsN{>^b%GU zb20LeBbli-%c2N+p^OH{*Mg#lVYh5q2=Y|R&6e4%z7eC@YI0IQhQW`!BXVQwlBj&4 zTlY8Vt#ZNQBhJ1qV1y$=?2W#Tt*wZew;vQ()gMCrBb7D~w|pmVo<wQwc1!V>TbnQc zZ=c_PnM*7@K(9=TDf0J+iT&42sMMQUBEF2mO2xv|rBwI*7sGo1Jh^hC=^dWX;ZJmX zrQV}ADeu!fOSN3zF<2Z*Eam7oBn<Xz=M$k2lrMOKkkN^Q8!**H#_sWjK25;Z*pIh% zu^m4;&eZ<p$CtvfyFCr)%wA@Z(-qR2-MPuE83(~AkUlGjL3+5`M_kJU;Yjc$<O{6H zT}v=0gtJwfoiZVrPT&FZE)<OXg@MGssXxK^RLBso?Wc!p{^r{Ul>1d>7~CBie}8!0 zzstP>xi|`JY?bAS*?t-Y7J#BEVGgU0;%_r3D>kTthl>8HbSA-$it@-CIl;ftaNn_$ zlv`3R-v|@e`qJA+Yc7vIYN0Sd4mPZxCF%OPFmgx2G}&Ib9*Ct|i9Tdq2&dw>>TcFK zW;zB&x3&8d`7){TlJ$_U^LGsTf=<2|5@a>>5XFnqz@IOk?=;z%CSX-<Gbu2=Y_x%J zgrh$zgxCJ&eIHK|m1kcv{S#)=4+x8KHPzJ;t@7hjS%%VaKi8q)cB<AJVw6Aqy*i(i zDVm~?C`>fvib;Z<06P%bB|6Ox%id9dDNg)tFFOy;i>|$BzQv!V!^M;5=z3`E?i0x= z-ZbS47RqG8=%lH>^CKp?MM~|&PTY$!FTM{C$vN|E%-!1asS&ub&gv2I!900c?$%X? zb;h)VJX&lhFhr7bor@hLLPd<_q+ZP$UI8)AiU8$uk$H@rFoH#vC#TU?O8|J$1x7~5 zCo1nmRD&X?JVvSZv|+axE&}3wzzWKU1zXa^6ioLP0&GvAWdL|Vy?a~;e^!Nx{LQ6B zMd_Q$MZh939&^*71{X5OZ_5P}FYb{uiV(vI)=lL-+p9zUy~%VOEe+3N$qsC8SuzZ} z(#`nL2|x8U!p-E3Cz>OMd6dx(kS>;;--dOvhui{^89<VrS(6uOF)N>Y5P*iL*xdjk z6nsJ)@F~Qgv#R^Vqf$|GlKA7tWt}#=eIRZeSWzM~Jk3*Yt*tpjpGg7dT2jsarJia) zj>U4-%?%;q+V1xSr0H^K{ouB@jm-5Q+{pfimhxsyf5dWn7%UT??BrrI$KX*yC}l_z zcq+Qb95O0KAO`#RJ<s=z(}+DUEUjQ+DD*q+>#%`ec%JP?1%2kV%BNk>2*sX^dax!4 zMZ?nPC7JAlK|Fg(cXhKr#Yt%l-y^sUSLpe=-uXDGSXiWJR6AUk%`HB{4Nz<fF(}rm ziiGV!+EZYxUL3xIVoC(DjIF6ff%7r`=<}1W!WEZrEtMc}u=HPr8ZizU#2Kw(sy7Uv zNL3MH4Ae)BtG*A!)xeiht0@^sQ{q2qyuWHR0dPpTGLa^?)f#A*<7!yyFS7+)XeyE5 z2tCUTG4A>NLvVXkKeu3-K%!d(QMm!&UfM|RfS<GR1-L%PT>#MCI>RRe-t*QJ0nHet z+^T<i$xoDjll+h)S|7f7F@I7a^<<w4F%oa@U$T6<;iwfPm4iRiit_uv!>k3~qkP9b zuOD+dW>7WAhF#S9VyFuwCDCBA<)LkK8pO~deCwK#$AR_HekUUl;$=vum6$0%&r8b+ zMw9{6j~P)4?j$&s#9=(lY^e(?H_%8{4o*?M@U;AisjBkmBbz)-KkqG$vMyqQ&Mae4 z_#gXirisbG!<*cpx(iyw`xm;k{0rihM0N~4M^f=Zrt)&43YEpjvQ;@28p~B~&LV60 zB50mBXG_4-0MC+6<Bw}qpgfy8ijA-%qDbI|nv|i`8lWl_0yrq_EDkb~@B-o{;F|11 z+Jb@a$#SPIIrlX1o$I%Mfh=XU8}*ko8o9TRq_5~uq$$=xa|>th6dzPCdb>&|RDIUi zPY_2{YdNghw%8z{Sldu~qai=`BD%amlh^;?I`=XAp9xJOrubnho8*$@l7(`;2$Td4 z!$<L<mahi{J-W43<IE(W->CZ&>%1~dr&YKU)-sy!Ay|L|4h<Y(Uy>eML`X+n0iX*Z z`q~>y_4ya>hL7tXf;-ZxcI1Fd8j6mWwK`Z}Dx>I3n;<2QuV1?lZI-Zds9{8f=(*4e z4B3F=fQjWDld|_o2l_)$aY!&ztSVJ@H3%{X6UrY0YA?*x|82Dwz;-PWGC?wzjr%_r zgv1xmn?l46vkj`Uwm1$DrbpaN97v9zc3i}<OLN~uhSvh2$X)lLqrg%#PlU+96rD}d zM8^y%JPCYhuJR&@P_h!=8FE65s^_Giv^D)R@Dp{p9(Rw6Jg*;}s<!pJc9j;7;T*Ut z>))n-IT)JgF)Xuy(Q9|`zNHRv)0WOsZBemCvS7krN5J9yOHhIT;cya)=kFmU;_aRg zI-}HvQ$`xXGus~{(MLRaNj^wQRbuRp>1|(n@hB)ad(vmp_iU-51XDm=$5Q5}vzWh% z;ghS$3@mo(rL;nTP{XsGa%rkI9;1#5cQ)fFOmfiQ|Hw>Hfv5lZn97bGp;Yh86v=i- z1co50Fil*lMqD0?Y!~MXd}FgIvSp>~d<L4C0&qT+Z#SCWzW~|)mm7^MZc@nR(`CP1 ztSRzfq{qN7^Hls4d5>=sQZy{j7xfC`&bsU8q9~DL7QLEu!|HSo6ll^ZTEF083p(SZ zygLXPhR!~b!OvswDxdfIGyss%<4HLtzzT8~ZEzpR<b`EwR6#(3;RA4|o-Gad=fgB% zIr*~OcxhXx76}=0PKwIXbxJduaRE4x3gBRNb^mn^=3|;rc*y5SFPmbknKNSN3orNa zmUZj$CS%1d83@CS8A}i{wr_ad$pPj+-Bn#fLxJQ0kTKu!d3nLAX*I%6sFaE0+lx9U zr_pi^M!lmgD>^h!bJ1<EZbsTbNdJ6wL0zNZ9@n|4E{)ja(8rDw-%$#qjHtTLi3BZW z94qX8zgmt?nfv!JdlB#7CPb`%kqNOIu9W$k1el3b%eJ`J@9h5fSQW)^-!QtH%IX*R zTd(W5O)_k+uA|l%PEDfrZ`10K!z>tI#mSK_r0BuKrwNWNfSyy74WcvzQVLbYk%$1^ z<EN{N=YE9C-2M9Gfwp${72J`b=a(+Z?jVIh$wF-4F8z)C{DT;^A$0CYXsrkHTgChn zJ|$cb>Dx1~VSDxJyP#A4%1xxMi+rZfl!1^}FxAOdv2!OMW3%UIa`Ztgz!82odendw zF2I>>$C0T{k{fo&hlCMBD_btJn=d7j@0WF)2FP3Id3S`-op#;_nj|5(n#|P|NYAC+ z>NUi<O>80F3j@hF6odBBjEA8T*ee`u>Gcf&S+fvM_gf%Kc+nO8b0hnA+4LDW=8S~G z-u^*}px9>nV=tCd9?;VZN7>NZLzP|4nD;A1I$zY9tn|7mc|=SA=u!8d13gw$!Jq`B z3Fz*b0Xbt{R?c*A+?q!5mS>OZ;0UH&QwBwAoLAf*#@mkzYS@1*7|@|B0m&}#S|#ZF z_5!@{bCQ|KPJM+n8n-x<=Bs{wBo%ma?_9H*9r_RNCLT_!+);K_C483CUh*KAoDd0q z$1+iiGdOiyfP-@Wv|_^w5I-3su&O4R<`#Q%9QDU8zl)uxdvcuoMCPEdI|U1Pn2!}* zNF3rK`hnOtWK+$QPm|pRK#=-hv!Q_?btEA58|l;E{U(#WIYkp59t`pkVk*mQ_Z#jL ziv{35D!{7$Lr@?e+t58NYVCa$=R2yN1>1$YtTq_ttb-d>Z?lyR_z^E%cT%(^w1x8@ z%8kVq-RVE%*aOL6j>ry4;Y)HtQIy*V{Is8y&sAA)@Gf5$pAej>Lyx`i$B?C2`r>tv z^B9NdLqaHhuOmajN&Br%)-b4VP}lSPP!f!VKPgsuvw(mg9f~2veD@}hM}fVK-8guC z9nW-|;C3=LIXhWzmvLXeQ=>B(cb~XR=jCp7Iy*Tz0rx{^qw+k9YLqgUXM$xSo6%=g zFI%iKs0@`xmPi@I%>ruxS*@?Cd0>K<ju<nOch#^6xma-&oiXnd(&QHV)iSc%NOeci zK%bT)ZCw~2eII^@eng=N?Rl0`JQ8|IZmyO{0f<O54Q~+$GsB*9xKqX8E**7hV;{nu z5K{yU)<1+b^k)^x4rTlf-x5nD$MaJD+~}l+ZMGoQ89Q7yCSjFoLOKL9c*H_Y5E~|@ zJH~2Z=_O~zO2u?X4kGVJ@GYkYyD72r#i}#JeB$TTk1VT<$J$b|-?xR7<PKhV)ZSa; zd}tzFsk+JUKPlLiXqc}gmANvjM+UXj{4_JD=`PH8s;Z$Q!`?)yI@h_Km8nd<I9SPS zckP{yv18j|WCVN9KxCo;^;?v#$@JqYgA*+WwC)dgmWJrq=@orR|BY3F+_`IYMx+IV z*tRp<p9oyOL+w<Kw-$kH?U+<1(`CP={JCJ1d?Vb*5T$I@Mr!;4OY1L^S|_MWO_J%X zReZ7S(+*?OE6j5OCcpkRd^F1lewmzD?gz}C7#10xmok5=^BT74iWE=M1huq;Rh9{f zSk&k_Gv!Yt=rLWX=8MZOxzk@24bKFBV3>hoP6X)T{rFCMuBUlDZZfaUX~DSObJNK# z-MtKT0H3~Lx%#y&!hA>Lsmd7?k8#Fg?+at6;_|L6-snxAn+2nld(X3E_0frrRaW3q z?U;ng+V4r*HZ*u#3!1rrgaJedhw>EvHXEeR^ZcMIx@YRDOYSLwh5h?1l<l+)Lid)f zd587-L>0IsE^9`=G!ooUR+7dh3)vMU#N7LzU>#7tgYn-<%^LOH$JNtFq5vi{878+N z#EHI_NnB}?K<Cy<wITO@?%>gE#`7RyjhzXF;@vNv{Wc_HAEWrGb5>f*BI?Wwb;OP4 zjY-+ZYGVz-4INTZ5r>3Hs`jxO8aj*XuuxN;qD?VSrkL}`3Ht&m%TzTsFCR22TdgU{ z2yyJ5Ni2SD<Z?K!qm{)6ES0N1C#)M~SNn9&E;^=k?PB$GUW8>*DP#AvB45Z?g+b8s zJhp@^dfM&eGrM+9KEx=^sB{E$8^UeX<e+hK=Dy?UvFTZ@M}cT1K4*ex6q(-_-0vbF zc`+FYFI)%>3Ccl=ZEfQkqjvduW-5`DODmSPzDq*FxAPqERuEZl1>sbaXao&Jhwh3S zpCfKJi{<k-^GEltc#KKxGp`F6LPWUm(<}qj)@M?uU(A8TW|_{v)u7kbjV$xEn4cx6 zYT2lp%`y{6s0m7E%Dlzsh<sQVs_LJG3+9Rkc_2bqNHEt%?)V<vuPQzqRIV_)p6##T zX@66%Y)+2wZj7p@NOL%Cm#n_;^1ZRndxa)BjU<V|0uqc-b)6C6m<^`PPhWbeox9ci z#+jo!9hT3nQ{B6>A3Vt0LW>raEj;hW+Ox6+vAHCq6sI#vV(MvH_0H6z5xe{!OgRF1 zpc@vEznPp7Onr4NPYyPR6xVj*w1)(q-PK0rc4HGvTZc<vHdB5u{frwx)!ong5sIw2 z%t-AAz|y+JE^+%i)7=K0nG!Mnvkn9LDw_7gVs7v{u{JN8WgkM@$UEW!uQsEjiOT-) zc{9~>*cmI6_BQk9H2k(&8CqjW<>}V=F)d>qo9gqAb7_4S=QClA4xWaji8>r-i6zd& z82dcw{5CHahuGEo7-YiU{^e4(_z{>8c7e)9<wo97P9F{IQsXmu_J=AXl!0cM?>N>k zc=qdvrE+)Kv+vf3x$cm)lL8qz>+<F82;S?gvO{00sF9OkqLu(3M$H92a#7Tb&|}=3 zcW+{8ackkF5UbN>$P$Q+;x|R)9tkM<tY8Zf>a}W|IdtrrY{qD91>2bNH&Sd&nHrRA zY^^3D?neH?o<w@JhL&w?_+!7aak}Onaq`*ca*|Q7``&lqW0M2v7MyV6eG+2<X^G{Z zgqggOa?(QeRKvq^HCi7d?h5HPz8&N{93Z4eEkSj5y1<~-T5ee5txIwn%xP4fKTfjg zmI@E<B3?jJUk6nqo)sUk?+8>W@72DzugvCEq%GFC-%5Yzu-~w&q}6<7!~2+b(`%Xv z;?%5HVdS^HaLx2?T~wqdyPG9ntrR3#^kY7ulDKrSg`e=8Ia1)`u&`ObAeJ=#_uWMc z5K|Ji<XK5kd4xVOBSp@59+56S&%Ej3MS?WCRsYmxY>QaQ2(yu0WDzD60h<LT`M(yD zn`t`W{L>w4SVoWIGGrR!gY4t4ZWbDpOF6U6VO>()DRk)V0bCsdXT_j|PXU{oMO1Oa zg)wzlBGkqXR9fbBnGG)t5m{kufzD64)z#=w>Zqt|-iwS)W@BPRoWDTc_h=I+OvF7# z&pz~;Qlas7t-WqBvuV2lv0X;B48~y&`Z7m+kTaSCZB<4~=5CE=4^UT-4CH8JH3#A# zljO`ZEltNx?iasP*90@yY=(GYvC$`Z9s@&u&NP4=EGm6WvJ$0rDT?O6qu^ABip?@q zD;;{-#88aYygJsYdx-dccbAMZjYfDsMx{X9W3wPZGGeIj$@C6%LBdEeMVz3Dma;iH z9x*|=pRDI9Tr|}!^}a{HS8$h!D)u>pEG&FiD=@<XW6qmao(%ViIu35hCtN*2(jO<` zkOf^mE`rrcU6f~Z6A@=Ok_xd#2MWRSJ5z}TmUyXNoxXC>biK&2+3|;&`({&v;6{2Z zY&V~5CAlg1U|hen`EZihJTcwdK0-Nql=t6%mg%>l;Hr$~3zOeA$WdcCW;_IG6zaxW z9{=K8SPi?W*B<x|m24>4z>u#^abq14R9aO7;#&Kjteo3q)4j~>V#J(W_nRN{Q->^{ zCu<>>?kPANf`JeCQq*2FhZv;|Em+BdAvCetdz&dO*ko~1*3pkjRlxB?9kDyL(SBw~ zQz{gJ*sMqpOC0L`ZTJqXB4N0gDejZ5hP*8!B_UOLxOCtFW)#aE%VkhsaL}NUBFPiC zG#chmD;)O{!*`H0smS=ev(&l(oVisWa%0FYxjs2@IO@2FIL+iK2`8EnXLmz3LxEbN z_QG8vjknzG)N*Aosnk{@Z1UrR%|o%&CL;}t)CBVI@==cFVNr&)0Cn+X=!*~#jI^96 z&Lvxn*wBPjSTFS<h!nUI`r$0u+|rOCh`7Y|C8Lw?Uh5A;uK5ESY^U^2QmuxAL~Yq1 z3Mf^^psx8p-{+A7FCSo>1klb)b<L`^xi#)qC%sO2;q|*!YpVLuG1Fn!m|n!+{QRl^ z38TTL_lx-CkC{*uS`YbzcuX2SdE<AmXX;%fU1ZGII|XElWK^|O-!pla;iS=HSjcG2 zSUiej4QgFx#Vi;~k|OhQLSu%DZ7E#By*+-56eLs&uxT|xv)ZvQk|bfxBo2{=8&>#k zRv6{~b;h}y<X|lVhLqD6op?8Njl`?C9qCZxz(q#I6he<MrE2a6T&$k@|IU<ym(Ys_ zca4P_%Ca3=K{<xAcs<`?HsW-hQFWURauwC#iOhEWg>WZBr&i_sM>d_4^bl;}zCMA| zkJ!`3aLuXj?g(9-?cZ$I5N7D{YwmNFDm$ATClc52hm=gV#*wt=sc1d1*Wv9JeB<7c zjfxt;QvPMI8N($}m##$p`0bQn=$J0(ESorawN&3^!q{{sEytE@j0#`6I8T;zvB?Cq zn<NRCZ7}}BY_p+u6V9-HMsC$7O7d<0NXV4?oac^F)J}$;Z1gng>l#KJvm$W`G9laz zQG(Kru&e!uODMTa-I+&-q9oFG+zY{GMsso~!}U@`(g&^8nw9j8HI@Ra0ZdD(%yR}; z()CwXueFt*h1(9gZv4N0&sm;NsMdcR#Ku<*Oj_ASZM@+c<0w7nA;5RhgVpv9+fus{ zs1@7tpw%6RF4{AP&fAP<k_8X)(^QoHVek}csml6<Is|*Cp&D$MNstUW7%*8opn9qo z-?QqZz>U`Ykx7sa$yRZ+|1({kls?Q`e*j_)zeRI@@9Oa+Os-4q&#y5fDG!_qO@-B% zBJuBsh%Enbvuz`Ezj!t%%LQLx`(A%wvyhcIWS0V2b-(+1q@E@3T^2@2pM+-e7w^4> zi{(n{AbPV&hkc|ZK9^aCiQnxz*BW@*m%X``l5FjM57kc6Yc^Y(0kZmP@7ua_gqKdN zc2sr*7}~bcj_vg}lh!vs33dmox!x^-|1cDmNX9;?5OyX@m>s+)9%Ad1yNO9B18oq7 zvaKDS9mH6zM;>BINzQ`v?y8r^R5C_}vXGRZBO?_8ihJmSXD4PqlkBAm;}WqsJk-#u zXo!f|B}tDQTZC0NEM9!zWtc?Jkswcb51CNZh-)lh-q0W-Z64_ZGY1%Ca~_QU3A(Re z)^{nYG?D4A80E=xCHCc0uFH=-ES_}B7~7}7n=RS|rb>$T|M5u93J;Ua>!%n-MY`HR z%kFOOec40ty&lS5u8tfsFORXw8^0&p^<9u^;dwM${64yUX1sFjht4Q$q{wo3sOtOb zvNG4&aPshThJIGE`(hwnF@9Y-qHM1iDp002ku_Y;r<BF#U8+1<3ZY{3pjgeAwW0?} zQHZxClJ9C66Dt#hQ@K!$6xlxcdjgwsMUjLQFwm9Q*f4QuIWr>*CY}lB{N|z%ei{)T zA-X)Gr46&1L9unC#i2ks_(dY4>FQ3Vp=jK}Vx>Pf>=(QL`3)Nime@-9oJx}4-J+Gu z@Cn<sC|R&|tK7p*XHDzz{>ZqX*6S{dlE3ZuVU{NAmRCiJ^De5)^I750Yl{hz@|k+u zC9e9scP25h-L=`J2^XoTdV>E@GPK2fP2iI1CY5xsKwml{3)^cha1iaC1NqSnP~Fzb zDOi|+t;$4dWFN2MKoFahsq%)UKz%I5hx+pUi|d%!Ff&bfrdMZr@zIF!h?3-DPR?1> z3`x$JP7HgBAuO0U<s)0nr_pQ`?I1Igao>reijp#xNwIyg)q9)QXm&1RmZHJOLa$R} z!z|t+q)Z#skr>R*5-rn#M;eNV&9)OQtId=%85)>&cB$~Y?a{xI>XCJkL+Dxr^6eXr z=!sD&f2I3vmNLdtZ8nLt=K?iL*@s-OT6Em3#0|+;d)9&^`GqRWLqF)y;oSKa-$VQ7 z#RW{8hTdVJ6T{NGXH~4^xlv9CsNHyeqXbH3IMBIR5kZOMcvg(iGj(`^Zu!Kz{G?Rb zC5W)^3q)`?31QRWK`g0!ibK^)pp*ZUn_wS$nntJ~)DmM(?#%hIV<clB&S!N<Mn(`i zZa-uoD`E0fYkoMncKFY(=ZY*s6tYEXj)C=O|DTB|+|pN6-`vAb5haa*i!mHS3SH$6 z@P4RP?bhvH=KV0Xs>o!#hx?&}<=a%l7&pIvY`-xkoZtYy%al9kMa}TcFwo$C;DSgc zM~R4}eON-%AnF+XXEylYeyrMd2(`f}9!Ih@&yx9wOM6<NzmFa@er+>^_XGLU-f<jj zd@*UwF2uc5gJF3WX<PX;>mSzkDo;Jk&&jhqa+J%2X^An@*`{CbM2`}cl-6H~%f3sU zpnHDbw22P;zO=H~p^vRR<|+K^$1@rlQ3^R!;X7Nx>k~$0H;g@oyS3R!;WPiyXkeB@ z1s*SlrHxDW1zJ-S@KV;>Fn3X<14UL|$oSV`E_eTjG7EH0wU5EEU^@p65?=#1Q~Vdf zlGT5kbb;^Ch2dEug4X!w!&5xiO^8=FzDiE7MM=vhzw+m3*N`NPMAGDhLIORKG>qvc zb05Z%{C<%%CF?6}rK~x*?ROgfHimECRn-3<zN>-%i@LWAs%z`EMR5t1K#+wK+}$le za0?#X-QC@SySux)Lm;>>+=IKj-o>}~IcI;T?yGur>b|P`{>?v=S*$TeAHBEUTI+o$ zGIL7?V;KjE%myCCggsCU@Ht5glxgXwYOo1o(bt8<()$0ZQp~jM=u<DEMS#InoNVe? z*da({H`6ikLX4o>1erAIQ+3q(d%zehL}1S`I)zEwme*fmY6*xLRr<Kn5FXF)7cz#A zU9`)B`lD|3EzIsrZ-nQ3To7d95tJEeLx^?2K@mU%wpB1)ZdYxpv=M_%pcX=u!cUqI zH5&Ahr9JP1n5ty05nULnlp6?wkg%JRL?uW;Dj+0w7%@UjRS-8HCTr<4X<*Y%75z*a zQm-e-{&lVv9w-q}7#}oU6<^TOSEuDCt!lQ=N0$6t*wUBGlq`*QFkYTO5)hA_NE#9k z==Li&OfhHm1<Q}GonZ)ppIm4zPRXW#Cz7P1luRzl)Vbheb*`l_jMJElEjP933HHmW z|06v=Y0^njM3z>6EUBErWV&svYy^KxWPpf(n|5tDor)b|3@Y|bfz|K`;Veic!`fB0 zb}>+=Aau6$d;Gs;P|!u!X>|XD0b0J(7-UEL3c`E<Q2g~!$_Kf^?5pGU-%>C34(gGM zfq$SW|8L)fzJu>N5NuzaFE`-Z!uw`4nT+uJAD{czcMqAnL7+vU^S?uS9>BI*T<`zM zn>JbJ>U0aWn5?wevj28*fJRS-3>wu><BiC9)(|0JLmse_VlKXuI43O{zUWM^&@OX; zMo-F$PWRC+>jBGobS(_~hm`pI4(Mhieo6VrB6cqzdX5ey_C_m~&uH8OnbM*{9LNpH zLa35uNNEk&bD+?sNdmNf<y*wInd2$42~DR(vP2+ZC{+m;yAE}j{AUra`+sE-{+=S3 zJ|<uT>f%YsVlDb9O^MnTNfvzL8DcGUoN3FYs7h+4jAoagMP`j1<2kx9h;UOt<zWBT z|F|4f&zIu5RUre^LIN77lHfS0fTncyWH{z4^^q>gJwzOXcuIr@T_16u6%FMVSz?uY zO@5cl6qrZM0p|z`FYKrKKU!<Eu-!rkl;!5)eB_2<uK<XXh{kl=u}gQ!6YD3|_I-;T z<9J`1G$`(9RFPVw<I2DUP*uyeRPL6xUi?@di3+>$C4l%nhjsbCGKW<$GZ>XmOctvJ z6y*mV8^!6sf)JTT-_?d{rvJki!vev=_L^q|(}c|{wmwBO-G2)W9!%Y!`_ZHP(l-ap zPIrx<H7}$uS`u|35rqOlV~4Vj?RuNO`#%H8Uk~7Kh&B^Yex{_L-o@V$`n`AYHx}3N z|2Y=NPh0npEaoh46kF_fpg1k)geJj2BrUj4Dg}z=ZF2&K%p@HLV!#C*IS`8_HEAWx zh$nyQI!7XM3?oQH&tJU5fn?`<I4FA;8q$Y4-eL#g8%VVWzod&(+Iq-ZW|AAF=yXZ! z(V?7aiAlpjWN7k~j^O?h&IqVEi!x8f5+Sm<WBc#VXleffg6RH_06{Hjh1w-CjFPH) zUxtj*V=-#er2X*3;E5MuoaTQG=f`5>SSokC7bD7|A&?fIe#NL7ni=q%s)ZP&NR7(E zeBDqh38!L)hiW7x#f(GE1p$8sE}0i%_OfW+Kf+ypYKREeb^#4yV%kTl<B;;OtA&U2 z^}P*aiMXTX%Ird-^O!Pr1<gTYex#3n1_N;Y{8!|l1#c00dSsQz5~PVuS;Lh_4;5SI zOn5C{bg}s{EfFtnh4uC6Kh{%GwSw2^+W=EzzS(_-r*Q^eQ?oJ|QA$dYEQTw9mZna; z8j@0%WVsvE4M|ay*UuX(Z2|#L6NePGKD2@yOl49L;RBk~F*I=@4?|#Z8q^7LhGf3o zjrrSp@`8$LfHcGjW_;6A(@m7HPbe`X#bUS4sWeQn&H$%cT}8-7u@r~nDk&xdSSB;2 z_rkjns9_?_HrH&5cfsH<WCN<ZzYcm4aom>-|3}@enjtZdT~SoQFSWIo8$q<zX<~5< z?WZg(%ueWpXr;y+D;D|1cgHH#t3afm#xNv=B+XVrZP1Dh-iZ)|-{350B2dRkm|4`T z-n>`Ks(7Tx)xvP1MhPV`7tQ9Rv4%{H`~Wat^8%*QQId}fiSZgz5<exV_2d4y_7jis zN<Zr|ZW7r6(1%`S;)F#m>3U_@!p*udDB~hu62ya!zlu~0lL|%-JES614E1BPR}kYH z*}x&&iK4?T(nG=WE2Sv1MdZ&is4<o)9H`~p)ZYY~K5Nv2+5ngl5gnl9pm-mqFS1A4 zTm*GUj-+};8dx<mm|Q@^7-PUJBaW~*M%IH2f}boP+st(h<;5c+*vi&+*-QD#^7_CV z;r<N1*SPrq^BUK{E+5x%KA(2Gco&(6ksB<MDpEr4M~X%I_OEc|R(;j}E#f*NgjDqZ z-(Jy9Z}h_*VYDNTB%8EaJ%}~>6j&Hc-v_@EK?Wjz{_8>9(C58%CG+oz&+%J^(gfVr ziPxsG$2U}Cx{v?;wIL&)Ct4bkk+TUA@cS+4eB-E^rCti=mb1ddKP&JlMDO>gkv<U> zt-=GsF@m2FY1y-(=x8L>n7gtw=!^=vJq&HXl1;A=G25T|jfF)lu3+NG1AC)c*@~cF zUK7Fj3_gx77*vow`NNl_jO2oDOH|WgRDi6J4AY+87r!9@{h;6Me1fmb*jEvps2*Kt zJJE_f2~gaCOrI}(2QgeD4%5W&K^qk>{+}E5u+XsAubRN9n<q7frIEKb=Hr_3(PVG5 zxup!kqqQ>3y-%uN3o%&`OF;dspfzaA^)i%IY}X8p%7W36eMwGlWccuCtpihT`Rz0e z{c{t<_JtG(ULM#QTBUkF<)@Wp0HODMLi3;b1f`1Ultc#^8vK?j!%)tYg(2gv13)|m z79cl8*5b*6nx%w1hE83;NxeG6qAQO^B}p%j3@s#%rCw3QS4RBzNxy$T=|f3TA>pdS zM)jH4KuVwlj7H-RYc!^F@Sm)O@G7PdM63lgsfzN%F);@3iG`R98u;=TsFwHWi}?Q( zeK)6u;qYn^rvrwTz8N(C98iF+afK0y`351%bYV~lQ=LDZ+YkZ89QS^G08mr}veim# z2j5=0-kvVY-n?FSRI)s7cY?D#USCDCI<I;L+SXpq$C~ae*ROgSSI@d?o?gbRJDM)W zI;yM|J3L+@4Ug6|8u|3q%}iK)D&Tru8V&Z@jKi<TC8VRVVpq4R+1eW(Z!~e%J&cbk z@2ZI;2ho{s=hi&k*PiY>Q21BxF80KB2eLdb7BRJnI^Kj6^Bi7wKCwhK6xQHrAoUCX zvS-W$Lp}a<dsO!8*3kegMP>P|g!Wz`m^g;t3WW^kpTANeV*b+DzIYJ)UVuqmzo}bP zjxQ!|s0$<Jc;PoD!imTYC^=(Tb{H!dlI>SOUQDy}$5<SlGS<7_L8DEXUoNh{^xm>O z+;)SYvKWjMgF$42nNp1}+r=v~05a&YfYQlTK^2UQg^IQ{Z^Nt8hboJ-T9e*l0QDJe zQ}&U69ang=?NzktJZNa6a2NbuLX`EguHuWi$n(pvu6LK4%Z}4*br(=Xf-)_2bg4^| z_1IbkS!p-+qaB%4Rfk(MQ$FoXI^bvU15lV_jDdX>+812mAa$oKD8pv98fJcd{&Fs6 zFEI+_5^7vYzyeSetdzh{T|J6%B_^YHH|lxhq-LZ4QD-@8&dq+Z&~~&MHQ6P+hvBJS zvEBnKj8B=^f&oEU5M<bI4gty1BTzup@C8xTpZ@1WXEuRAR>#|vF@cLU*wwCe0a1v6 z0-4e%b&GMds{X>=g|Ueuf#)N%7yF;juh&=f{3=N0wU`62)Ep~^%{4*WUkNUIf)~oS zsH6$=3*&`Bw-d>9e2oI=l?PY?^|X`}YLoM}SQ)N-sLr2k=IMoZ<=7E%q1qk~=<VEk zw-zw2ekp*k6ABpgls<;=kJ|%TQ^kUZP!Adzge8BVry9nVsP;{Qyo@ZdTEI3KGg|;= zJeeyM6sWTJfX1SsCRnmK;+<kZbYpa0D))KcF&IZF2Ng~bc%hFuH;n$B=jB7@9iJeI z(^%pv!hAcU{!sr#oeDpL>4A@^HjTH=?TNE&Tj4~&l_UZGLt`+`x{bHL`B{bg?0J_a z<8`y->FWu!zn81i8$ssU(@zEWAnT$VE(o02%(*ML2GH0z%WQUYaWsi9@Cw!4f#MXV zoQev7+?-_EdMR``evA1%<G200Wds-k??qnkHa)S`vY^>6r*A+U0u#?Gp_8`oj@o(P z8-U{>vaKj#jlFJSA2W!*!{s<U0Zq#VeNn64Xt_-NR%gY1Zw-Z!B$Ar8vv$m%l+~eJ z8mS3fmDCW%1VWdkK=xA*qR=*0eb)8nl4zzTqubIYBTz*ThZl*dF(EHU(XECTp<qdb zx6Q8!OvKHXT>XXFuu`nB0xM9ubr1}}V2QFt##3HVk#y%X2Rc4y=}KTcw+o7sLaK2z z6nAW)#E!sX=&IBZE_sSruVNyVJ;fNLbCi^Jh{(;tXwtx#8n4$awJdL!(I!IGxJIq% z>tO}G<$`bB(`<!1n}zpYgyvaZLB~}O`99=Ubcs65CbwoI1|?7~5u4H;N2dqW5rkSW zFw*ypK5Kuw*nT*wPUF}Y%QG`7>BS@Y&K5){3Hlc6?jkyhtQwo+YpaUZa!6$KZp=dg zA)|bj5}8m?&QE5yty|~J%(mSrK$(qMKDMg}vAA%hnu;N?;5K>+kqReg#c&ZzEb9xq z1v=AriL>|`L^O4*gR3N-(!?w>DbstY{EdbQsU-(QkH;bzUGdCM=rxA1VC8ynjVw8t z1*X&kJZ-3BI>+Hkpn-USiC2BNj($=ux?}aiIGNftu0u;F%_D{5F|_V%9W9vbUk-2u z*l|2{>W-+FgD6LYChSNA%mu3r>B&Ih_J7m;{`*!3u5vs85<)HWuuu72T+1(nZ-@4@ zPhH&Cf3Yr$PW+g)?Xy1#BY5il?fkY~zt(``R56<^DYq&k?0xqAx`{hfX8*flmHt#n zM(Ck49aqg2xY?1z*OzfvmVLXgTbq!n3HX!kQl6JVxl0h95`}uoNH`GUIHn64R0YQj z1~C05*q^1TU=+~3C6=%0fP8v)X`jo|2|0TTrwf?oN{X!p2;=ZVU)@Bf*10}sOnz#q zkn0-JrEMiz_U^FQH^n1paU84@BBM!Zcy=7s>UO4cG=Fe8;g=o^hY}DBnOJq_Kjtua z%o-=~u<5O2J#?&`EjFixyJ^cF`<$^*04FG+Tp?zeZ|jYUx*O_y3Gkre94ncUxJ0z~ zO=?Wo^-YSW2X91`=P^i#3?3~I52DwN&A~k7$eei{E*{h{qAw80G-uf_ZG04Sw>=U0 z4a-jZV0Bgd#@TWArp3^F%1W%ZdV3Dg?0+qr2Ns}=11?5u$+cZ>e$QHG*L1wR4rFa` zbH6Z$rs2Zx{&~e_otggFJ>9VUc3IWt?I3}2>^K&rSu@*x6PosI_fz>|b+b&|G#66q z%=49H1U+Edef#RD>9i!ZW5Xxr&l4FPZ=q*bZ)(yUM~(fV&g@yKT0=P7tc9O`=&HDq zoLqghVp-ytj~Os%c&;2Baiw1#{39P+{~Qe`+YpUEd9R`c8`Y<rP=RhKAplMW0-z?O zK0jIq&7Brr_So<0O9Sh^pQJ)YKPiWR4TqP9#6;f2>D%5&5X;MiKlv)(;q|z_ZC=H8 z&!P}H9pLRbb&;B_YNZ|xlJD&F2}(O@*VHelPqf7hJK-P@lqvo@&Kaf9N`cO2*%F!x zh#)U*fs`FZ_Q?y)i^WnlgLtfIrVr81zGr=W9;Vedu57hGZdZqq=B(PTA)8-+YeRtb zeyi6CuCFYf%7;=P>t6^pXQS#jj_8*_vnUXbkPOAf#AKzEQwL7x8uiSzmy4}SFtbt^ z&3=R@Vr?&(0jp+ZRLzUU7wU;ctL@7VO9zJ=gpZ9|lNT2vixFA-zAUC|#!4^L=<Okr zr9Xl56a3r>Z$M+-TJN`i9YJ!v#jxMa$lTe&^IThVV(!2vr;1^Y#>;)7@nSS+P#WQ9 zGo|fA<nP?|Bv<r@`PBh5F`bRFVye1Xxo%obS~A9$5a%9|?U=(lU))oE8g4h<f5y<p z&zDm*`m~&GL+o1upBa~{L>4O>gqUE?5MdEsd>duV!D@^bjsU!rj^Go8GnJ5#;&0Zp z_!(2>+l)E%b?fD$BIDbK*%nBzJgYB=re0f+C)~Vt;=j?@k<v9Ty35`B>AOG9frc7j zcC>n);J7ou<KFH2%UBKG<t@rcAvM5GFPr(sfTfK}hd~RL@|_q8c{{k#<KCr8OV$*m z0d|7z5+o?ij_UW7VNcGjompBvew@kK&v#uk{(Epdo%D=Le0fX<4{o5N7evv}l;Je8 z-B%cpV?#-VrpX87BfFIbf<+6ETB?%mW+#IiMSaAgSuzmQM`KttDQK~%{FB^mU7Dew zm^&7nY|t<J45(3cBFPr??}3+ZGd&=x818bHzayU5e;KKJ!t)&W(Wl1>C~@Er9Y5yS z-MqAAzxFN!P9AuQ@bVv*2sfknwLxBKyFw~lPLBMJ5|HwF*c_p8=4=Lhox~0*pEf`f z$M!H2D`b_18w!$-CSel_5&zZ*{ZRrR9(VYAM1nXT6DT!<H==p3NB}6*1wQsDXsud{ z?Fe!D^hex(s#FlU^IcUV+%jxR_%{NO%}i=^lOSz&td*5#a@EI_cN)}_=1moRkpwH0 zcUaY(dNJUlXO;7W*=Gl~@HIueQ9QdGlIRPH#&hP#XBZSE#gMG2SixKkk{w74!3<&y z;v^1J6zZfgfq<`qW>6`Ap1=1;vuuCgMLQ6VRai8axBHq`X|~d4H30d#meUW9hhicB zT*v&BK4&YnILHZ1vRJCU()1u-YHhe|uA_-+iw9)G2TaBD31cxKdM=IM@!su?1mpi~ zoGLGuDFZp#^c2@;n_iIFCCC@DlorS#GxG@{4F#0NgVDD`pGv9ys!JOF(t><t6`RlE zz-5q{52s)(QH|L*s#8P^Y`2S==8RthHn65TD?~)6VvJkC9o`B|cH21RrTnSWB6sY3 z>*xjd?%l&<J#-TEKJ#9>vvk>l#L8(s8WL_+tv1|qZ&v<hX19VUHn@~)P{(2z5fK*9 z2Tb*DbL)#SzWEKvm!Y#Vif?jYQ)D(N8E^BKIgGTTg}K1i<juV!zM8Xk7rh#M4uShN zu1KL5IRL4k#dZ6uC%ECnmpHbQW{~U?OL({w91=S!+>Bjc(V`d}aa^>Xn7W*iyy7o+ z3HsF#H4tjBJD)FYI=!~YmQHj|K|K0XB069?mdRiBG?J-EL5N$xY~v`KvUT$J;Y_xD zzJLv}d<L;|G4PwE^G2<QO<inO{gLL`k~{t3gTYzNRi@jYS*0|m%_&o!ZyWpbcFaE6 ztca=!N<o&&QWQSv!xP_spi>Z^yuN<4LEk3H3<E(t&C&l^anhpFihX7rj{*oJe`7z} zsUR+*JL9o<Tm)S#54G-IE0waA9!IQlTft@=pY~YUW_cBw=EKyAD@3X(Ir9nN3S39D z%m|IwN+$SldfK-p5Iyd)Obh6PiI^EgfY~PUj6WU?=QT$BN||a&T8Gv6)S-gnL0xp# zYS!PANI`wG=PoC^1;Lq$zCFuE?0h!DQRMDCEI7b!jLuri<x><Jid9~uI&iUw3PaTX zzslfQj*|}Ns5XZ|-+g`-%923sue8)pDhJyfUWEDH!~q7=qZNfUQ)Ucwp+b2pL%H#Q z3BR^Ew8VryB%AqCto<3u9b9tOj8zl;(v*am?w6zu45<ha%`j}oipBEn>NqN|wNC!R zp85{Q8P<+0I&Yo1LCO(hn$`N#2+)MZC#VVbA!UO`3<9JkG*l_UlBsSD62!-P?-nyG zqv|cUK)Mf)UZr<drfkI(z*Y!nq|if0Gl!dVEq$$S8FB6C6#h@XgDD?`uT9U=E~gnW z5xK78PdPC5MWK1AaVc>nte6$!vG05vXP2(0<%*?(E){+yCqjde4_QkJGDUc`v`k(J z0YCA?R0)F}ct1u^(q@oJ++&KmlHJEUtd~y4ijBTi`<%hH!*9NR*X+HzdRPccPle<B zq^SVQ!*v#uQnp*U;zuCiQjINZ*jhDVf?j)KOFTr?XH_tbH>B#h1OM@zgC=|7VQ)$d z;#Z{oC%>XtI2Squ`D7r=>*CV9El5_wZulfGlBWW5Hv#W^Q(WrpVm?-qg2wqQgT>Kh zY6V08&$Ga6*|JBqTKc5NP{c~eeDa8R>4?MKkTE2-80^eYc}l~B)fw~cS_&jfHz2Ee z4TvAAaR`kbE9I%?_<1<`O|8ODf9k^)eDi1`?qqctHqV>;fIklUTf1h~ttLT-_oZZ+ z_IiYs<$g6-16KvOiLm+zNpi(pPB77YuI#ezH^Z#N0U;)4Vy5Wf<Pyd@39Rva<$yI2 zJ^4U0lAQR(yj?-PA7lbv67~m4(o#MFLu#g93`6E`4o6a3rS5G@xx8`<a%xJ*Fj?5r zZ=@L$aYkXPmLGi36w98bhIr4f@~=83{>XqEt?HBI+!~Gz;Q1|jNk#P4!S{^HB8>_Q z=E}N1CnR{P9ZrTCCC0!$lMLKgdhY}AJzu{b0t=%gMbZVHQ$?ul+zC6%Sq)2cg%qL{ z*%mGIze-Rw=@xS`M(03@sg)#&q4i>eh=yKFG_=LYmaTasdt%fpg~vf`d7S|N5j&eY z@eIDh+VD@B9`18T;r39@T*zh`&;E_P>MKmOM=ul7n(d$LQO^U*GANf?2-UgSkINy- zYxM-dr!z3bDfqmH-=U}ngo7i!ME5(^;pRf%`aG6@mpRX9cw4YbBASp#Mi7ttVFfXx z>OQw+>%3LrX<e~t)_tdfwrc?kiR8Mcz`D=4^KwCVyGec($|04CYMLb+gi7qW_M~Zt zvZQcHJd<XUl+>e}5+@c!IO31fa_KX%z^In|1jPiq(k)*?|34ZD;H(bWK{E^%>P4!Q zMjkJ>&jLm3e5G5H2YH2+E~YP$&Uc=n<QUJ(La))yG(Nalfqa&&FIO=M!CGwS5XlYY zjNtObhU~ON7nHJ4wwu`<e;l$U!GlU?NP6W*nfok8{neB7#p7U9*4@RME?x%4(^$M$ zkGR)<yXf!VIlihT85AbMjK$_Z)lW<MF?s6yeIF~zXfK~E8sia|bTE`mWXggIubGt~ z@fQ~-yHtQQ7q-<>)C%=SBj2hGcQ;E$&<v$!?zg^BKiHT<@8WqH4+4=)ADRk?jrh3i zXEz&`FD66nB?SE{Xb_ZIm*pS4G5p(=_SQUe(MqvB$YQsKiBl7ErwdQYqh!93juPPK z=zWUa!EyS$ez#(aiOh#<Oo87qu-d=d6YztT18t=uFX02zqf0YHGA5Mk>4<q)d_h7i zg6GmGb>nDM`)Des)D5FexXU&Mtjr5)$^5}&VS(sd-dh)}Y!9*558sHa-Z5?U;bAFW zBoB<o`UB$e0hLS+BS;2wRsF^fB{8*WY%Hj~JU3U$5j%PoAJS6KA{4ODY>%9KJbn?& z9@st?+##k_8}v3dF(M{3h;PGxrA68f<f||-tpyib<QN9byH*s~a>JubsdXoV3l=LW zBjQMSiBsQf|0<tlK{XO7@NLMg7fw(vZ>QPXElmB5$72-RD(cgtPE+^2Nk5_$@6F-Q zhWCxf<xoqN)%pu~=2?Z`Da4%=A*lKP@0|To0qaC3qP0_VWu~kP<S)!7qfhFe^eJ8X z^q<RT*{?GuHx57Xb2ZHO?DuRkE-Uia*)cGCE~W1Av5=ZcdM-kfIhd2Vl*7mh>gEaj zR=mU7zGnH*37eJ(llGYqRvnF}#&5}?MxHQk0}U}WfNd_0Qz=Fs5^CaCw51`U1+p(^ zh{U(pTGZ5G&F}cL4&RQmQ#Em)F|xBx4(o6lugkaM!>RFkCqLzj{qc7wDZyh^`vo<G zSXod)@L3?UxAQa8K$-1*tFFgJHpXhFyIoY57T0#H)fRqwy~B-pTXpEu{4zvIk?%yd zEcaHk<QeD36jS+hiLg%d{?|<>{KgG0aMS|)o$t~JLyDR-<0MLf7(?G6SddCb)wrCV zhrrOJWOj=Bsz?2HHL+ulMRJvY`H^9c;=&n4$jdcE%q)D0AkInVt5UF6yU%Astt5!K z9+J6}?nYyon(<86*}k2eL+9t}xD#jRow=wivDb3LJNx}~*`?jf3t?tzaw3rBw2!iU z)8DaCZIoCuvXG%x(Kh?*rNj--|DoP~?4jP3%m4mKG90M<nUz*#!NCbfWg~oW)>WV= zh->9g<Lh#^0x`$qp`U*Q4kA0wbnqOi$oB#<!5%dsL$xttdsIsk+IY}!%y2)`orA4z zQJ6WJ0!5CHMOK<oOl@NWuA&_XfUG|Jx=)UrB|uZ0r{JopY#jO}$9gE$<(mG7<R8(Z z+vgWy|A|wc#*yj+1<cCz_A6cw;=K~zPqgoQDbj#;1Qn#p8;ToUBkHpkAXj9)4{v%( zO&B$9_a8*WzVcG)>s~eIUh^~=mMmIjs>Q$%wD=KTBxu(3D}3=o`uL%Tls0+92xV3a z36#pbLl&D$aZWjzQK2pg>{f73VW!2ekd^j&qq^D+N^-cKAoyGt{1#zVOP^U7BHE_! zy8vueNf#{TB~+m$62tLfDPObRF(4xCryrDTedTOWRhvc9CVraTTE)_{Fx+A~<qYDo zj(^Z<<*BeJ{X1k%XlnCe`fGBmMHB$)r30rtPxU2`0aaZaINvp&ZE-JXec*MH&D;eC zL{yN=TuhJ?pO*+u;kLN+4}h2Rp!r-<O-&B|-?p~r0zs8(0dlbfp%Qcixu#S@;xJ5! z84!n6j!GhBi3Bfo7#(X+<!dqoTFV3Ayl8W%Z-h`tY}%J7ACcrF)dor(&}hba&>t78 z4uvH9@{3A=ywGifVt;beLW!KC)4(+zUOW*#$&Tx-G>rPjSA)|o?4tNX0^O)o@%%$| zkf!zV+h`qV<mt;sT(Y(#rv}|r|KL)UY}!)E;bf6g(xM_aC?WRT%!W$iV(lwL#+VNc zs$iI@hp2`jR<m-ig*HlO^3eMbg<bI_ra|`<&VNF}=A(L$2(}qFYC3B|Th^`EE@iAw z`YpoVPo9wda&hSn1h3(TJ5%6#+GadFzG6NOu9+Vf%vWrulErNM6EyC(x}!W|GFtbO zv5o}l<=x3w%fff6kKVs;7C9uhx{zZ(T6ei2*37?qb7~2FBgN-}qe#nZ0MG!0xYw8N zsgoyN6Nr}@9bDp@?>08QVqQt6?AkN3<2p+|*l~R>#4pH7dwl&=wr6d+)zzF<wfV~3 z$N^~F^JWXsBj9+`t<P<i?jC0|nd_FzS3iO7Z~LR2{Cs6+vP*Y)Cj+2A&TI(tRZLgM zQCT~rs$7D3Keg`hg`}gW<XU}*0PMTtc8aIBi%3?l?Agt#g8P>x!VUZ%`g_$1?l3q| zpWUZLl7(bXy>zaomwqIHVU}GhD|RW!`Hv^y>;$mX@49x-2`^A+is8-4AR000e^f7E z!7>(#^<T&Cp>U3V9!+yw;S@)qnLHP)Cfj>UpL*&W3DP4%2#47)Y^x>xDv~zEl0UPA ziVhD@+fkN~8Ciievu-EOeJBtq^;c2R`HVLtH&R`c6ebJbQ+k|WUYaKCTQv`Op-ZrD zfVP84tCpVqAYFh^^V+U)!z;wbXiCMmKkTShg79TGoT_ACXttmje2z4F!~({y`iHNX zWP-ZN&He`~_Tnu5gF-(AEZMf=8mo0SBQ1iT(h<&oj5Xt8{Zo_hy{9%th6kjz>wD54 zzbwaExaUZVL0Ht9iivd@_sJunkgK&D_ZcV}ksu=hn81Xe5BpkSr*?s*WdhYD3VGie z0>b;#7ZuAZ2InAh=CP)Q%^}IavQ+zUDHO;A<32@&6G`WSIW!9D!=8(Ow)-VPWkgtT z-&fGAyC5EL70A|3L5_upsqNv=L~%bYrJ1)M#x$E74kWp!9lDb?>B}>P@AUS5nE<bN zd(oMYo8Z5jHg|c*m`m8g(EFf<2H~Tghh&lZF&0F(6SoLLjr(bVKp4-1$`PM~P5m#q zQ8e1<j~O@sz0fZq-L%FtyxW=0Mt0r+cBR>0p3ypTs@RzY<E8ubL%_OP<1kx%u7-;% z457##1|!vs#8qSvh)~4s?In_>-E?+0U=Q<rliN0#X682bGjV=3>?A%7pJS_0ZZDC? z%QUNq{)iboOXWJlWz0u>9wpVTcf(})#-DthLCkVqlqrDNl6eg1a|9#P+4`1G{V1Jp zLb)O7#_uUkP?LjY@d4=kD{yt7>5ON$UR^Zv`3!c(3Fv=6J9+r{q^9L<9d<M`_q^nu zrpcUpz}g!VC!&H>=6n#{rNMFEY!CDLng!`~7xg-jzKBxtB)6;;oVBvWZApdvjhz^4 zz5rZ{G>;(m@k1F8#(_U+WIPtHf_|fmVvbhPdKUNj%H}}zG3r_&(Y(;cEhP>-HLI_B zeBp#z0Fge8r<!GyiL;;VV<+6)bP2Hb`8=-m?edy$p#I2xG(}G+hE?7Ok<u~?5eO9_ zL2ahvELS%himZ-sX_5@3B$_b=YYpDJl0%TbbOmea{#<P-OA2~?D!T4=x~JsrbhdN0 z2yQ6p4l3B1s$}33v4l5AD{-ed{48Tx!d957`Y0o?6E8N^FE5b;ACxm$p(Kr5mdCPx z*{i(z%B(Ye{Tt_k%6PH0m{AO=2}vQEL4oZepD15Di*5E?@6Obc(O^~~8c~SRC`%cs zC24c!-YN~BpS$9&#<=84kTvQX+v2lWV%D~x-a<)9JT_$=e8Be(#aJ2;2Va4Hh}keD zH*b8uLM9jJXJ2HA;t}RaAVk-5;16qUyNMeP{nhWu#4Md?KXQ7~YR8cCIX-7jxJbNz z$nWgC#~w@<>BxnDy^%j%SC`Ht^v>2EjW-@j%OE*nbKpIki7+1nnoxzDS88(U)OW<p z&f8!gUZDu^xc7R4$L^K2U&lV8=~lNRBbbcV+-|2@yYZ;ZDru|ai({JlO90{l-W1$d zGd*LQI#lEO)(`;V<=6gDkT6vBr2jWRPs84u7R&wivZ&_T-T~d+IPU%Zb8r!D7Kb&h z%=81jwPy2U1gN3g*C*3H>9KrzcY*)q@f(kIhyRI?e%%4Qc!O2=5TFOcx8vcK4}CB5 zuBk*8z1ng43Ow2+Yrag~scPwduC~EiueTQRUZdS;Iaj5T*5v_-<j-#<_1(&a@Bp30 zKjEZlnI2w9iFfT!A%UI*Df9L-E0MiL+G|e-%v}7_`~6RE_aNBaNm=`bD`!Rw8pge? z`0j>q?^=?d!)%lNXR}TrHxY|l5I#bp)VDhr{mVCej)N4;`OcS4J~eZ52KYl;5r3pC z(BNHx5k+3t>rP}drB~qX3-+4L8|w?X6E@pRWX{AIBZ<9#f5#^+J*kND*=@0n@9GTE zYr(@IEuMpQctO%<92SpAkmOssam;PumiAA*<m-%wpBp0j=XM)|)mOP{AH4C+JhmWj zs@gEmI$lN+guI=nG`o1(_&aM+%#@W8#^Ci8OjKO*J|GR0u+6dZ(rSzuNonHl8w<B; z7SMmb?;JOAympj5LDSq3Qj5oPUdujm<Gq<~Ww*7a*!Aq&myTA$J}!M>)+_08YCsic zv{HL{8UBOm>|HzZGsOhU1iz%NcXch@vi#CQj}6XB(CXI8C@va_Xi)8z0=)3pspt<R zLqT!xcSq6|+l_olci!2`5n^&^@=xP=b4`3Jn?~u}cUdm09o{W^3pINvT$k5t>y`?> za@p7%Aj^}UY6cm=%13ct@tDj&91ltCBKQoNgd(ZK^OCH%(%$f_tS(OD?paSV9wz<S z$_@gvyoRX>e-n%qYh9vLPdk$jgqWRJ9Rn{wk|dI9T7vHw&BF>mE%%4A5D=+@d06je z5hU=bAKhUatoV&?iWlpa@J@!~=(f12lR=GH^<<9FA-R2h9f6rH4F{Exqci@Zs|6fb zsY6DP_xeO-dZPicR`$c;9IuN%WG31oh(qv$8eQAWC5~*4w&yEj=c>twp?2wLVxA+y zPgwULc)1_0`0k-d2<BU8`t%FVJTQ?+uHD0?VuPJg{JmwujA%OV+=LHzhhuFQt>@6@ zjUXK0&y;EzV^itGf-7F|L^EpA;<89X`)Oxu(u)1);tPuPGMXfL!QNe|_Lm;;SRwEd zfui9N%An5-biu~fjBT~LD&<zTUI+U0o$csdRyJ7o%UvI0#XHXdb+XIN_wMA5{IA0~ z^XsgJn_)*7m5aBVhZQdTaBOh&m0rXaaY`hxWB{EIMseH*5VwmF6n3w)y!10ean%c$ z6benG5ltJ%2doQJ8vl4|#bH{xSlk{47gb@g>d)?92W<)g6W957NLh+<&=OZ(W^#RH zX^uH}q06IPf!UNv$~?ASL;7#~Jh$;_qHG;z-&Q4qo-f*cMKcyLA=^tMbjdiS-<0zj z%ycB4-C`}J8o#ot6<4D4fK_4p|KeGD%<x|BJUcJK?CR7%!_<ChGF!rNe`vfmtqE|+ zqZ`oX+m6+ejY{vdn_4<?VWiH$J_IiH%U298LNp{&O3N2bx6hjNV<QTS3{%O+iVUL? zn>(V0(VM;lP00ZA7>H4=i^upt9DF76DT(xNF2TEl#wr^bR-h~sk4g6;n;cU)-0d~7 ztthwGRp4OD*b&QqSQ>?`!|fg(bNwZ)4)*!Jel581Ao2>Do^elCJsmm$^Uq9Rstkth z@MOAEIF0U_wfSf@?eB?E?fN78CA>Uin#G^<86#6mEA&(E$!qP9TH=2YFNJ>YL8$or zB4E-P(VE#d;gK7|bJ24GYnBKD7p_Nks4)VizSrC$Z@cZjW_>7Kr_=+F+XqI2hwfA_ zN`XL(r0?Yzv$e}_>9cpwwYJWDT;8BA6}|OR2%}Vb5P|0<r<?6QQ;u`6f;?@{tM__7 zetAkOE~}d?)}olT7_xl8x01|rl{ew(Xs%JDuk&{=Aim7Yn&>{fjt$NM9;5BS4J3J% z%}EEp`MioWxYCyC??bklflr8$8LeI6IX=WPr(QD$9d427b#`0o`}%o30jGHEB+`V{ zh1Zn7qID1WD&|{Z;WFJs@8@2JOgokw@vkQ9s2POOS_Iu?ETdo8YcB`(P!7;SV1vBa z<?3>>WS&EUzTtR@PX(ERxq!b!Ht}~uQXVySMg*2jour)qT6)S<o%7sImvrY}y<bPc zb-c}h{o{^diRdqv9EKxB1?N^eQ<pFgdjW^&Z41l0JR@f=|Lm{e_FQ*A#mKDebuO2f zz}_4vMpW-<CJ{oO0@kzQPcqNTfG0rg4-dT%Z!p+E0b`G1;;B~Uh*JGEG!;4m{uzlY zV@>z7SDl5MJ$(lDbsE<={O0<`1606<bppclmcviX5LeWK@r>&b|0=d{ILp<CwSG%$ z`t|d<s}Xj7PUEraBFuHSiTXx7k+)O79@wpZSQraNdQ8X`v)@58NRt}_**wzu>0Ajj zN;14ZA|xFioYj&DW;@q+Ix;<uTaqsN85dg#N9KMhl?Qw*B>__r$8St%ps}ZAmVEbB z`49C-#jUV}%xN3tzu3FHkD@01K1tGB<1S}Y_9*76LC&fAtmu!i5>efQXOx<SGM}Ok zO<yn!MH|l4-!5~0aI_xxEMhUL*BPR8lG;kc^=q?EEQY<d$%@6v=@a@CZs)QXgLKW7 zq*(JGY+H(F_Cy7FzJ;gjg<~FgS`MsnQa9;zR{+le#?Ef}r=U)vn;StpHZd!8Y#SOI z_$nf98{5ud%{3Z##${IUV`OHV;XvEcgwE{<{1jBEgu>%z-N9-sPc@B`Fl?t+;kGr< zAxvvGf$S`~in})(kr>)r?X99>p}k=~46Cx42NQ*&m-x|gP;ns1{)b%X_aLXrja%#L zN#3Cr+xDLE?QfC?ombkI>fgVKwk0_|g~*#7pgh$cQ~URPD~6*UjNiQ1IpOK1lYS0c zYkdd-aVTs?bNoV*+|^h;)zlGHZ<adqu<~Tp+Z>y)BiiOl*WvH9MJ6-?*+2JMn^b-n zb^&Z)1eTFvGxQVKBs1)wYpz6CcltA~+1hIzu;2bRf0E%jX$+3)y06SipMtg2TE)zB z$30B7$Ye^vegynP$FkY0S5?i}E$Dg_eM1=Or}fxDa;UIvyky)F5q|n5jFD~38LLA^ z9_#9-vs@Y28m$`)(K$8@>J*Vk{6!NS<`-lR%5cAnNUqNJ91yL(2P>Q^<;J6JHSEgz zX^&KAd=L`b|GObI5qG9O-!n<{)XQ!v7KD2SkYR)=d#YAg=eN**CN?JAQV($>#PaUG zIT_<6pz(5ae-qWVa0U8zag@=+SlA-<{fKCtMd!)?xN@GWO?tVT6g!)Ps7&%SRMHX2 zZ@n?|aKw-uoEPj=3~geb0#!H5b<ME7<dEew=<6bt+0Q~~uu>{iUyo2q5V#v`__I10 z20xp-%Y)~%4)d{o>Ee=7{#Ba9Q(=pyKZ$h-%PV7(eWvi_jHl_i!FLNIed;16D{JxW zBMZI;yPqU@<WCb*H5@b~`ukLok#FAj++z~UD>iTGNm{+%E%zJzK+Bj6E*TM$)TVlp z^{KbS*HoC*#;|hzp?!M5b^tf5hzOEN7@E@;5sOloxj#YSh2dG=(UvhZEXzUx(~`p7 z#OcbL-^C>@lq*>`ELeJ*GLF%g+|g4~d*AQK-bM&&o{Q)74pf)Dx~yee-A(-3!n%(v z&^N<ZH9NUP<E9RKtxX~IUN(sN970}%xD4E0h6IU(Mb!-cVxd11Z)5Er2GyZ7RZko; zJbE)8W#=41mv9pUItU+D6(4>>NlkvD>YY)Qin4YYgL}sI^te4lT3+v|7!{OFyh<<k zp{8g+io;NmGw=d&&0HckTD{O-U3#)X<&7k*FwXO%*u+LDkDsZujmIo&ofHx@3r(e~ z;D8qKUioq>ET+NzH147rS&u~nGB09F<7ClnZH7yXJS|G%D8Jb%zOB|)8aA<)dDCeM zgEcJ%`>SY?=&Q{u;iz$coOY>f8Jp{G9aY7G$9k;l!yryXZ!YVN4z$D#cLJn^>v7B8 z&r;Ph$u83$3nz;bTBrtVn7hYDdwjA2+}GY*9{QX5M}Igz@d9O9A9D0xUH5d!l_n|U z9vf$a@%BB$v!r_{{cAwb@5LptL%6zz_glB9_DanjBu@auJe&}M52U`Yj>a`6%kBct zE54;zXrFS~f#HIhQVte~t?MxKmk9eobda$dy7L<Y50lg5F!8eD#=N#oQz|}vuJxA- zR+6qFKfYKu;D^?Rh_IT{p_)C!ecs(bm8RpKcpit?#lSAYyQGTzX>GEr?;uT`%Pj)Y zj0TnTU(#(k=rdfOc<4UaKE{`Q-~?mQQK4_#o5z%n)TTT&6ZDn%vA&5Jp^7HMRFS28 zsFMA%vl|r9fssAINwv>tEWFX2NyQ2fg!pr{=r<4z6xfPdV0Eq}mB7)NAvt=dMj1o` z48<qwIx5%u@+J<04HIXbS&VFr5xGn~mu5(XUvRBL=Di(ydqcXo%GNENWiy)0BpLcE z3An9r3O6Fx`r?AhJEc9<MtNP@8Sc7Q)h;#5WKybL!>E}HA9z_DA6g2c-Y!Qw9sG$M zZ3}Hlj3+RRF*@yS{UOayJ-eiZ+tl&BtcJ+KAxEk-)a!!rpZ1F#6;p<6eXi7mV_ZP7 z$34}<T_=X=KrHMpAwq(804}7)#_MG$lC>iqA7oXE$k}IeynHX_LbvKfKFjm(Sgl{N za(JKfLz%h40p|<P2{_JrKj_Val}|B6a|`@#P><>3Tq)M%pn0AGKbp}c{D%f3{uwt) zF;$TbDU<MP)9qYa;qzGwWDRmSc7@W`;eqzcCFX~rYi{T*qhzJ!<|6_+EDzg<`v!F< z;29b+%&7s7ywh3OH?!L9QH3n!D-#Su>@hV3G*e^Zzgf_6hTUpPwZE%gpR}d$`ljvK zuhU$fg`IUPWE$*Z%p{E@^TNarW91i%EH&)5O*_U9d2XlnwECX`%seghsTO_(B9Qhm z41s*KJHEhNc#u|FN;t)XQj+25zr+xu!VD$(1Gt8xe|mrhahT;@CzT~gRrtQ7d9&1H zwzgKEMnBr$nlfHyw>G%ok<iT`wY{FEgRc^#UQIu;?*vc}L=wOA(uY?JVL7UlK8F+` z{6#{27f{p>{=L(`{W!pT0gYSejDm?!w*e)XHn86-F#L2|XQY<G>9dE^LVB9ek$&s6 zZhb1cem-%9Joh129_=kr8_FUIJPzenYecTO{?7R>Q#0!NSo`=NGGLIW3ski7&zbFD z7*DxTV=t$R{xIrHj@xu@Tkt+Z=42(kaTh5^<{3Ge8;g-nVCk~;#3{f2g~+o^5224# zB&`@A0ST=%nCJ+>Q%xdJmI+?p%Z@4ULynBJ7iefw(Cps%EmZlu^Gu58<TR)Qs+P*9 zMomxIqhZp0KD&~CJ;{dd-RWVnE~?#!>O{C#EwuGV4Ni?jP3}v9RJCB%T!xl&#VGlR z+H1=o7+j77{CNz>onY%<qI{QnCP9UMbo4xNNv+k$5^iq}XE3eXBCX$LF!9^JIk!Ci zv@{$J5N2>lOCs|Z7;hb&;yliL;j-QMdAo$c44F$duo75|DE`N?4P%(T7a7_kE#ZB` z$N#<IWdn}!wxcf$>y55XuOtYbcu&8V739pKbYF8+@Z{W|65Y7YoCrI@=8N0L9i3HS zak|eIW+c=1ZOd1mK<lnFbT5Eeb-ZhiWJ(?N7aHK}{<6rBI>nHp212tm+v&kK)G(_w zbe!n&^oR<9rzA=12Z@6KcS$sMx>tcRD-;>EAZ=cbW+u+Yh0$UQF=-T`xzCUF@gK4b zQ2U>^4Mfm!Wu)ol{Q;ib>ksE8d-L_}7vPAqdQ(KRs$+9SNLIMTAs<|Z2(>=g97UU& zSa@iI^>A;-sK&;KEjB&{P+ps^9yrbi?JARL52KE{{ZnhD2OK7p24EVGKB~7mxnG<E zA}aB{`owu>xcB{WvJXCyJfM#<ty)PsnxmL$z5;>nn)A-TmT28giZR^BI!6(+hZP@9 zlH;HW$~<%^z+{agG5p?H)qihj@-*WtxJB+Sea^7Tq{%na=Av(P{21GeMrxPhPy*Mc zkN{7RheM-GTx=Sww5#D2-AQE5il@3F8}H&U2WeW-F`=`fZ~BYi;Ni%B37}BBg_CR0 zT+;WbdED%QnhK1PP+^m)!Az5rHOOYO5fxSq%Zf85iqETszYXuk`kuzC^^!r`chKcY zaCB64A1F`d5o^i@NkA>rK$*OoieQ?nq&xkI?1L7&&NTh&B0(8?34o@DIscH|&MYqJ z^VBxO>5K*~k&Er!#Sgm_d^CUJ-6R>-6viAsnB6bFpu;c>StN|M(tw>Dnj;D>0dMrK z%}e<f8^=VYr0nr85g1BjJ=JGhY9vWJ5KH@N-uPNBui?<Mc_)P*rlF3^F`<7FDv|tG z)WA|i>t?~B?6@m5^Kt`g(5NPGlM$RP0?>k#V$CbOc_18gVtRSv3zEMP)BepB;rmqU z9KF>BDcKh8>h$|hMeFRZ{dBb~f<1(B!bjxTBKZO`)}w4&MrAL)lUq5C1it%wW=rIX z@D+>%Y1u|5nW0@u?)Ok({2nU)VgCeW^U>%u06TQRq`Mkk^3~xW`mEu^_G=uf2BI)~ z-sNCnB=?P}KdDbAFdwUm%H3_POecD%1{z?k{WFD=)>G{iV#o-v(j`;1v*RTa{$S}R zRf0r(6XV2&n%sV${4VO+3MWZ>S11n^D&_n%RsxY+mUic2lJ%>|_VkO{_UD2$<*A~G zHRa9N4nqr6&Ua0CoJAs&wYg>u`U5^-Cx=1Z9svun@%_GG3Tl!cIsPhEt74=tci*xc zqr4*j@{v;^=cnq|J89R0{VP~}H;KFJe8W_tD8hqp;~W{YQNS5BtxDs*g+MYBaFd1{ zVOk=M_>J!cQ1^o%FOZA>((j>Mm?%+fmZ1S*&LytB6C!%s2rpv!9NCe^q{d2-R{ey^ zPYZQ(3Fp3kAK8C-Z3oW?mK~sq#LADQ8$~p^NCpGB!^Bz$?Wz;JGkw;5J-6=PnLf_{ zTs`mGhooj*U(z(>{zuJ-R=pw5cIRCmqaP=#nG$*zdq(@n2{UG^70Oq!xo&uATFjf3 z7YH@-q^CLO1iz07LM#bb1#w0TouM|0A#k1ob%>2S#0`OBYDjtLDJ!(e0p+?vb2_`- z;RR{qXKb%3Z~MUrgo#g;iytzOT;rLyb%g0@Fcs`{;$q{h@i=u>JZQwJOjYLsT%I6M zv6z%vWy?B5!<UKROTOGfXs<kv2O`XHW6o{i<J#K6knnR)2Z-tH&Wl+He`%TxI`otR z34@VIujNwTS2pe&#N<J0o!%9#v-E@NGCnJ)@3$H{Co*oLc`zbr1LZF%A>DrZp{wrD zk!z<PcDG_Byrg9_G<RRRs61v(Iz>12>tMEQ+6}MXLE;=luipJh6`d!j5q>&9QxtKF zUvtfk$;GwSl1&;M7Eem|2)9ebAda;qWYWt357nRC*-VA`m-!dD{v?A-CgxFZQ^(7n zvp=6C+actolY}Q4`|i0ac@ky{WXHg0Xn&2|FWp*rym*30c>~4tt^)E4fx9XgFNQ|? zJ?m_$B+wf}LsNq1?+TxHp&%>dG}|H%S{*n3EizUocOmVPjpllY<7AQLiqcE&-98XP zp-uR}cT9r8FPQR_P*+C!Jmi}mi#?TuPn1QhiL#ZNX(b@Hc+pH4MhrD=bejxbrA9~3 zqCUqBaj<IPUikpBE)r$#MlcJ5|9sU`tsGq;&s6-RkkqjXd(O5Gab<QBi_k_VwHr%1 ztAT>EEL)a()z;#3`x_lYCV;RS)vqKewY*lAWI7|hE_NBLPys}QWQhkWR}afv+Zfn; zdmQBhA>1y|M342fRc&{R!Z<cpVy{w0UO-kBjiZlJLJ%s_S;{a*BAb)ZM3CPR`%OWS zE+KZZ+zhZavqSN2p(KEOL&pfVVa%cIj?ZJ&b2g6i9mje2`LVt^C~rONBzkVAi>J(b z5wh+)-{~;3L@Wl;=S6~aNCO?a4#X>ojsDvyRaNsFgt6iBI@r58Msigy$ly}%syJw9 z)7D|qwHrdCXoam-IgieToa2%6el-bS_|Llkf$j&#Uap1Igc;M>lXC_4TU=hub-tx? ze*=G-YSLJ67ET(xtMmAzR83yQJgIwezbTyAqS=A%gY2@Y`&^6yVd*e)Hd?;hXWyh7 zuSbx=M2^ISEhNAo_9HXv2r{t|uCwX2?Gm$__IEz3OF>o{^Gi$?rD$*-%Am60X4SH{ zpQ0@|4*mC>Ubw1Wyh>U>e0t7k+T&Y>lzOvL+hC$Xc5X_4S-K3hvPXeDlWdoG46(o! zMzeHId$u(m>!}gW`p1Bb!-0v&bjnD-u;hS8XWS(BUzAlguAM6{3N#*0!p$9#+Kn)I zmmf3ePj5Lg?+Z(~88`oA!b1Ty=4xqlL6d!WhK}(Bc`$n(6!G}}X)>#EBh2&3`-&I% z>h=3O;`HWgVC&*1JO0*73?dPe*^J13O>ZYq(~3RHWAlL(i@o@<8Ua7g2+Wwz%X9Qc z?ZvgaXzrfRQ~PZuOl6`#76xpqD*W8U8;GqabH)Lq53&`a>ym>!??Gec^S50B<!$7~ zPna+Rkz}wkUbd@Q69hl$Re9oyy~u*s1z8{KZ@9B;hF0%n)y&+}e#pCzST;`<NmCU- zXRa{&ATQmn5;zcW7QweR+?QG^=UOFB`MD6Sj|eoeB`YB$r?%Sb6*aQ+vpDuV@}7C( zR+SBaJUMnx2md?1dU%0peaA|jGT{On17O*uqTV!#tpX7P9z=)qySVnov8BTpDMY(I zL}UddfcQui!!tG#{^KrNhqTVJ>a|YX9kErzU+6hPGkl}?pY$Gr@b(-%SzL;+f^>zv zyMEd(>HGx4i|v)EGy!3yKY(dL<INX+k$pyDqxUq|rk0dT6E_@A_pv>w6fQMW+Vr@e z^JJfj5MXWqSHBMDo&WG6KP;<S^@2!rudy0Bk*VM;lIh$aBKNN^6QVlB7<J(4x<~;! z0zAf9%Dj{qE4v&QmpPCGX<b%F%NJJm?^XqX_J8MLMIZie{^MVmsV|o|+}N7VXLqtE zIlJ2|qU%Z#Q_wg!mh!x~(;Lcg_%Yp<=Fj*R9yc~TqHjx)9k$Dg2`A<~a#WDbc%aW^ zN>XQb19!}-I|6z7Qq%E^lwQ_qg_h&9Ty_{}a!o3$<1Oq;R5$BDE67%()E}fDe1^qS zaQS>WFMB_~DvZ>+QAy#&3*e922^3~!;~_5Wj6ZVbI(_zCCCIFs?p$#E@+3%$qzL*U zzC6%GlYmaCB+uwmf92hox6y&z$)gUZZG*+nn`?2+`u|1STerovZQX--AZQ>+2*KS8 z3lb!_yG!9xxI2Lag1Z(T+}+*Xf(F+B!Gn9~%{}+N_nhwE`}EU)^cSewd#<_Wnsbb? z#s*<0H2W@AqV8;h1=o}D^;Zc`Xvm&rL|&BtaBV=%ZT5WV`@09sa42_*#Y<?Le?^Az zlZUxY%7^6rpN)65(10Q!8+kYJu4csE{$}(ZrB*X-(YB8thbd3gS@pb5X~(fNjtz0k z#48zIw=ue8CTVsbs~67XN+0Qd+IaH~6*d2SL#OUIPoy$ldPd-g1gLg*>!nFtheA&1 z*D(Qj4%l*83aO&Js6<01ehQgQBkxIkPxW3P+Hr<Hp9z-(<`=fNnJ+TaTKfM8D`*rx zQw5v<)VX|C)rji&yHB!))+-4dn~fX=!ORc7D@x{<T~0zbSxEwJr9Rqd_3QE+8D9mz zeJTLBFunTYF-*bR)?p)N?qLx8uNb4di;fra0=a4)?^?ZK-Rfov7FkVEqZW*^PG$o6 zv@~mwWgLt?o~UmJrO&keQR_MVEet&V7FprdPGhA3$>h5}^0#t!tsNNU^hGU#ixDsf z+)RlJK)#Q;3a@p1$Ip1h9;af%;e5AV@&?r!{M}|5w2P(~Mz)oO<U%}&>LfBwkpe0d zNKGVib-Z5FWwjri4%_nKbOO>jS?!;f%fF+jp$lyN%_m#eRE$XaLZOm$HHTf}@o*-? zovPeC6Ymjx0Rd%R6@wLQm#^eoN&MC7QxPTD;GvdhJ4+<Xp4YV}=`-(GUW+tbb*kge zlNP?8tfj4(W@`(fZ$*E3gQO8fJIWtD#5kDsW}U^9ao1()T@Hq&{|_=;ow>Qr2KGnq zaj2K#ujxf>nI@(Sh{(ALf4p<>X^4v;J@KdZA@plwIx+8!O;F<c$B}3ZCZrVl``R%X ztA<|Hs?g2*%$|!f0u{?9z$8+(=>N7~fBxSXpnBMO78n8<52uM2*K)d!CV++;y=AW3 zU2o)UeWh`O2BR%EA34WkY}oQWb9>ekJCLAJ$CzK_*p^yT0eADAm@klDbCNpKh}I$r zqjY0hLto5FaFcao=S}|ONGfE;fZW)7B$RnIF12Q*tzBUFBL%o`X6>g?H(R)t*B_GZ zk1m`<=&sAJwv?y-$Q;DU%yYWglGK_#!T;S`DTY6AjyE0vdkqARMz@&XIg;kbadt^_ zS`uhQs20j)rlI)!9{+0Vo)qxp)tk$ACDV0(0=Jb63&%hP^(-2qzZWH6k>X)!LO0+| zsErmu2Mmqc&A$j7{qnE$Y}G#Xi`4#omNWBlv~h49dGjMdoq)~bxbFu$g9ZN@8z)Sn z@;cJGY>1>^u}2gXtv|dsi%bORo2GjdbQFn3T?H_8{m6%<1>s>$%|eP6i|zm}A1h4g zc!`}9VD`^eIDn2*Hn3@!eVplsaHLnv`oya7z^p5|KLhzP7aAYm+3<@*Ci_$cB9?#Y zl*R*gpf2j?zid)O2KULaxfygg;VE4PO=5j8LVXEWURCR383p5>JP}^}3KO(e3Ixjm z*RwKww9`0Wa5A)ceE)gWvi9Ci{66Squ5WFO1<Lc#>Y!?iHelRsO*l28@B&4H2SKW@ zI*^7x9_#z`eA{(T#`XAzKZk!h_)1PHmM;18^+*;YZGY_kV)raT|FY;wl2b3xNwidc zlDT*-c#!`>qiikfo$VMt?t-yJnFxwVdEEtMJAuftSpRi}!bp3r5D@(@g4kg4&vf)8 zINi`p#}4wLe3gg~@eyUDKXVk`?#IV#yK7IHNwRhb*c5MMEh)OcFK5au{c$!Sh9rcn z2DGGr-3Niff6Xz4w?JJt+tvGZdH{OmL_kf<L65QeHNH)u#4M6ijb*E)@S-Zu>N5HB zA+Obk@EHWA@%aygHsR!An-JRPTkuF-|FZ3gO7qnNtw(S0V3cw%+Kr+XD=bwWd@f_K zZ^G+9xcvOu6T;UIv1&MUl{~N30R+>mEG~}i<!!V#m!mKk@5u!3ex5gt=y#Yu2)FJ) z6nD6OSK-fI-FRDPTXuaLz+Yzn)HT1y;MQIfb$rg6THxkO0NP^zlz9~{fFB=~*=q04 zCB8ud04_2}zmPZY>W?G%X;xmDr(GRDy;u)}j_zLPtu6;F!JZZ1=pj*DK%9m$dhkN> zmP4Cftbly64PR*Ngmd?qwxVr$6-xwEwcn+Wv-o&ljH-QgW9B|<6;$02+CriQzqne@ zHI*<Rs}R?lbp%^p!doZ8zvF;kwl143EiJSysNswBu-&uLjQ+5&vhfKOb?EHAMhWJR zJI<Rlh1m_jW1fIe;2WbTRS4ji7=s>VT=WUWb}>Z%i(scS+>?OKIokyU(@JI1;aAP= zryo&FH(vxSk?b3XMD^&`9xWd08%~|XZR}bj&>KR2*qWlpr+{I(IxdfCGV{RYUDirH zgP}BL&&qQxr#fG_=QybUnx^*hi`E>r0V=+=(X|K{kwrMM0ne$_`H#no+6>OwpA4Q~ z_+)^|18n(Tg9U1PpBY4)S?4*hbe}EHwGDKk?Ck%vwnrW>=$6=ihr<-1q!O3Y)bkFs z1apWFlg4(g(J|>Te?h^jRiy;lAzs(WxS=B8k`E*Cmy<dD{ypaC&Y6VsdIeL^)Qxdo zH)vmuAyV;WRy{^uEvH^5tLjj|RNB8BG-HOSLgeZls=+TaB6CBiao_{zI&RK7l-@fB z)`bO$kBhO(mph`eng~4z*t7@$D12tCWW@BBh%XxoI|cMwl*ut{1UUVK{WVB%44l|g zGA8N^1ES4wj_QtYJDuqvWI=@0xCk5x^XHHiD-RxBtB;mq)6p<$?AJm_#{V9H7)J{d z7S?+$pErrc3vvdg7iR1nm)3BOgN>V;sCU5qAE$XbP5umGgsq03xwb@uCswju$~eZ~ z3;3&?wo1g&h#CxfuH0(FxLR1-rirEenxdYASTiXtOAdS}Av%He|6F^@Fk35r2ExX* zFSRlXXVyy%b(JxEav1mHBhjy9X^T$sbtjw>)E@g@@aq{ZORhm6E1~$W%egd699Bwc zeU>^Xo=Za1w8Z6?>2I~~1?Ul?J7llF(NP>(Q?l}$fu7BqP-i<V{#CAv=3fd6+|_2u zOBH=Bz_kfPC`l10V+t(wd>Ly<3uF@8Tklf9r7jM;7Ct5+5&ryrykzVtSKH3}s0XS0 zc6WKMB}$yxJ+aJsYM|w6dAN-A;zi4>GMd(@HykZJ4T7sp7Y4mnN0d%3DLpos*8-+% zA;2Rgmf|mm^6kjz$1kpH1DX7n-6cs^?q{qYxK<kx{A)S2c?Tf=8bzBwd2?O*ly)(Q zZhD0o?G9X%INckX!nqg{P@XN5{x_>)YM;W8jwv~2y&F=_aJ2UC1(yo>N53I^mn_&> zy;jU7xFgnxS>Fq`Yk$((tB1Vk6V6Ojksbo6AfOw^Vso%)0T#I#r<Ev!QvE^)JS+Y0 z!rWxvGnY@w%0-9|aJF5cV$S9t7v|s9-lW)m-;UbFYzB_rFz^cTvfjvzIK|HMi*pO( z2^*??3$Mi|VhW%SK1$st2dvp!sN>SCGoBBc-m5N&OvB-^uiIOg5vv7{vmX9P{8h+y z`K~QaCVs`sJ8X-ks??p&cJ`P6(lcdA_@9NgWVRNI!y<eOy~v{eBmKU~dBcB(mF)(Y z&jgd}dkXS?bVvqZS2RVdG1lNBEOYf?sf_4|8}%87vmm-jURJDMOf&DleWO3nnwVsB z49mxc$n>GiVpB?_vYVS}yDOba$EFZGB8X4Ltx5+g@a(Pr;lI}NPQAt{)+?8Pe2+cq zk7s+rDwmB^Tp}CqYxfa(e@$~8dR{EJWdMG@huf(R)RwiX&H)>RY(+%tt?FWnsBc`s zId{~t5`d%Ei+?+MEd!DGo~^)VJI;Ww@xg(1hnR!8D59=)`5e;64b-fWfj-$rxt<~O zu8-Yn7@s|I*E>}b5O&Fzh+Y-^bt4VP(P!{{5rdqDz1V<vRyP0}*xsXa;A&T|9IKN) zo#@}Tri_W?&2>eFR63b+^#V49D$14{5V2IUu9pC!>(3`szo^)j=*ui^U#JY{Z338j zx&O^gK6rp*;r2KPzMsXy>%PbR5LF?x=#pWEzHUPhdvZnXz<tZvO4<A88y`Y|!d5w- z+vGvsi_w$J>loc(yjh|S%Z2&C16clNP_Ged0;xYm{y!N2B+x_!fY^`q^lF{)aF})5 z{L7F(`;Bs8KV<;*>*O%DnXsN$8^-SOX#iJ6^_?a;03>Kk1ef}J-Y1Lnbz&VyP3Sb` z0V)kv6V+eSf%zjULt5J+Dp0S^qPQ{T|7o90t)pu;VOiyVXHUdTtEZ_vSSYPTa?Uo! z>1WnMhOQTZ`y5o);mJ**;ESQv1w<=y6zg$e%5#LS*M2l)5~y8&qrl_FYWRe&!scg| z{dp>MRS60>BXP`!cE3YW8vf!!#6>q=0(HpV0%K8;1w$erYD3ZSd69(v#c+f>AY03G zI>sf#7IvFxk4mSsIZv-Kx1y@tTOytKsZ?Og$O+SiPp910SkhMMMi^x&Fw~H}CElwG z2XRXPo>DJ%>?~4RTTbEz%q!X(mu?6N3NC+57bk?yE{amE@iYx6?V`II?4M1vBkTKt zz+AP+O5p=>EeXEr8#00EEeDqUSO5X%ll0d$T}O$*Pak!FF~D);A1lDbDrbi+oMY(7 z66oo`P411>5etmMc?fruajZM$;UWf3#WAU)XhOKeG9FJo3N#&!$2aFMHeMY9n@~2X zBM^JU{WXB0C8IC=maH%h@8Wr>1hlZeBGW$~qXg2MEqXY8s6~&aH`8Y%GG^W8d-F2A z)o(|`enuss53P6TpL#cykpuqZEs&0{bpU-e81--Z3Wz+a_639__53B?IdDkL#*Y5u zx$&aPEU<-*;Roh|{(5e=|Mj`My!eTIkGBOg_+Ykb>$F{a82I`|nySO6|7<M7=3P&C zLLM<>5lys_^Ht^~p>*uBILKO11sRX;u{~mLOg*^<#Bgx|pefdWna9mt^r<x=|K%($ zftHUlx0H5$Hm7rvqMc(~pe;8;counETjWtcHjH5b3@B>1ZVE>{n*0NLO<XZQ-GCYq zi$=-U=}{K?{27ZMh3z3ffw!Lj_?NK~y&K@aUJG1<V`@mF$`moCUjFN|9*c0(7t~}N zgw)qYh4?`f-2=ZFp4o_fjMEWk<u-~-d(jcwUwpWnhi9lKZp;DVv~FDg%P9z$nS6VR z8N7k(0)R2Tqgs#lqS;rFC_Z0<ib*H^Pd2-MW(wpuKeT45GFe3}wOsOYHha1Q48-~@ zcIycxbZAH{$01vjpkUj3c+~1YJp*9X=&1eyCLcvyOT%_i%X4j%(1q{r3gW-_q7D%8 z&Q$A6Oge$QK7$i;PVak-jcSqkuSb3k>50U+pAA;>%eC`Y0y3dov46{i-jUrSmi?MG z@>w(*6|7hY%_w5z`NqS=lcuaZ)0$g|n7B;W#hwsU%ltI!0gr0sF_{Izv7NR*(^~hp zO0iE6F(9Vn{(V|Z*%WVM9Z>ZjdCye_eAS|OoKY*Ga@#v)P8rK&%G}UB0Vcevy@xQ@ zHkLwy_a2jXr6;e9^_71we8gel#NZJ(^L~c@T}3y`+0v!8<*4bi9(-@eGuqv!T^=)T zhamh8hDEc=<t20#^wA7a29=2#bl}eyKf+F$tsdYmaO+Hii9o0)WZ7fU-@3SuzLV4+ z=*`Hsede$*V;dX=(EpJ|x02b?+(PgGa6j+bYJZxU@9yDqP=>P7Of7+7&ZF^2tC7Y| zz4%D=B5*OzV!2NJF>*IafXj2Ixb0rxqkOGwQ;kUp`mELHKY5<bC-W1&&QjXyN5epa zPsfC=dM<j`{BBPz%_Zg1MBP{jRKagXVe$30igSnHN28svu@GazW7T3wC9y)j1XG5P zf2=l`zixl3xO2|*vQ=|iJLYo>blyEU881?ynJwDipPt3VMhw7Zu!-C0s||Yd(POhf zpX+dbZ7;p4sG(j~ZiH6jJ!V6Qh8PZU6lBI-x^8r4*k<wK32R_$s)a1K>ZjtBq<<d5 z7gJlQ?yOpw8kwoVX<-dog}Zo3-Zzz)rt^%xs9G$&)W+H@Yt>0#R3rB6S;AkJOj<~> z^l-G&VG%^+|B}*R5(o_zz-Uu8W6P&{f#g6v3GHxD{hXw%!fo8y%?j&<LDbij#+gCl zIC*qSH(MqIs;<b#^K9y^O~^ca@7O>5&I<z)1iF7M!7e1@3BuL_en#-dYlz_oZpCe8 zT<kn99m<zZMg*8?oY=S!xbDz|dZ}}x#h&nQz2%5Ht|Y$%96u3(n7}RI15TTRWB40e zVWi6cf8R{5F}~FF@odC*&ZnjTvI^CI|LdEXzotkqF?`6lX0}VBPx4~ZjX|j0U0sHc z@lI?7CXqn-J2jRB%Sto<#F=0oDU%^+;zOtHB+Qd;1T;3~iH9ykFD8~0c~lsNm7k0! znU=Iv6Ozg?F+d!*U6AR!aJA9}&cRodEx9_A{$UP29l!vH1N6m`3G`XEs>dG;D>QbC zO(goI97C{2AUU~sA{9qXG%f6CnUr_Y+lOSR+fpPAk~HvFS!k)-c4P-%=S^e}wso$Q zql-cBj=#njaS}4^nL-2KBf{lOzUJR6?vr>mIgq6>d=U)cm$)sxtsQE5q2z)qfr}NU z<*($J&XrFVBP!HlbKT`8psRG647)w$q-<r_9DK)k{BBws*Rrn`dJ~}aa=X}cWqLn- z`@qd;u8?}IfSWKc@xq9*+5l9x^82^pd!Fmgk<9QP4--<<Fzcc52p<jb;QaPbhK598 z4f~{*ay<mDpo|;F+AbACP&^fU*3Tc~h`tj~&xj6zsDk>UsbHIO7htu<iK`VeE*!#~ z*$haAE4BXMRfB_ZK{jVZRkZJG{+_o(v{tP+`9cP*LtCQ`vzs}=zqS;qO?g2!YiD~+ zc_kb7F3ikTt{E8GF60qE2|*3gp*b&u`(F2Fk1~dIu^oYG)YmdW*z6D)jj_%!BxV;b z+nLkGvp$566;6h?YGuo)`#XY@hTS{qYfl5b(>R`50!s#PvRf~q7Xf5O4EaaCkgdqd z>9XXQx~s1CAx<Okc)a@$ZJ4P9aq^yS<yYs}|H*Jz3|l1jFNjW<tny1L5!0Kah;=)C zZwl((=DK{)zd--`QG3~KmUX-3Qf7tPx6OvcPH7}oWPySy2^%@Fi3%lXig}D#ib4TZ zFYCje3i+N%r$D@6%>$E38&+yKq|JK-{8E`Szo(5=xl}xdsts-WHxLw#lx!Nkx*J;Z zJMIhVWGbv_X%}mzHzcjdvJ==atk`}bNl>UgmP{@$%E0SmG19LH0aX~L*VhteOe;6m zyCFXWXSTYVHQRGnMJ;e{prEqs(hL`+Hdow~>l0qbu0J9JvTfiwI6G{aaD0kMN@MLa z|Bj?qN4?XjTkOQ%@T9>?R#e!|)_|WT*?yHqc{Wq6&?{C7TEHbFL!_<Wn^0f&UigR9 zLHmN$r!!>JC=RHNvCvK;r7RM{3~+@HdC>7H`s-P4lum9O&Vl)J-Le*uP5mG-S{FGl z^Rc+!xe;GHbk?Faw?vJ4>NRq>l)7km2_A`<C`phq9s3_>z0!c0hj9U;?g?+$XbGc~ zHxaQAE)G6*yAj>vU~<Xv2!V%bkMQAI-nf?io2&>zy@vcJ(~?OgD-y25XnQgLFMz@# zz1rYiMmJ*``Z+`iSao+0w!?pm*@-r)khD>L%=3H3bfdgcSCzf{>plHKgAqqsTn_<d zjEQWjHXln}^P=tXEi=v!>p#aqICmTw&vm*^xQ|I<I2@B&2JTF{2EICt-vn6PExf-k z?m=%Yx6=mW%E-tC?mZ34jeBQ@TnhNIU{{xw?Xb6Y5ez<Kc)LDW?7_fXy)(^4Z=Lww zY*@H1DM{f~tk%a5e)~e{?n*|UomE?chBe>Btm-k7TQ4Rb9DWEcA9U>3$yDtO7=bgZ z_M&?=m*Brl-ECG}RmzPto^F6esC@!MUUWR{3MTH;jxBS^l7@~;KBs;6Pw)R(X>s&< zWT{aO>^FF3WH8XA=rq~aSn8#<aw+)J#AXQF3LOvTKmJho3^4T)R>c?FR7FlDdttdS z@bD4QQ|5acTY6f$)UD_OA+@WK5m6HzP2pun9^?^Y33GNddOQT`n%JvYP}v^Cl>+wf zQ$OGP_ig(M6HNvt^A!1Y{+cu?a{aXeb@6_TszFJwWx2S$4}0-bJAfez_u4$7Ioq(S z30bkkBv*U-R#iv`zK$ZZm5|YBF=f9s{CwJp@WJbsxxQ_ILSDY5?OW<~Q_2lF&u&HA zO6VK}^1PK-U%OZAV^PMqQpFe=76p4eGNcc6!#!6qm=_uY;RaRt7ewz^4VBpPXNd@k zdFS)8=!K`<^QlEVrUp=^hC(iDI=+_T*Ykjm8h<V{R_D))Np-HlPeHJ$nL|*J{bT6T zq3Svm;tsC_3?&dHEsH9ac#MdzS|a(I$rHrpsHl<&#xIB2PO(KQAJ7XmUK+mTfZ@2H z*_N3fy1sepc)IqTr@@<04@h|tm2u<x6jRY~qVuqQHH12c?r!H$@`+@5#j{sG<+69N zyqeQV`FGzmVXjMJ`QQ-Jcrl)pb)vfY)~GHTzv1af#lg>&S;w1iA(>gLbxf`E%s@Cg zIm-Q{6bQ&MxUM^G-T&V!+gGE-KR9^C0yfgK;cRvAWv^t$`M<Z0F{7)!N{MFYlp3e= z-20!692y-C8S^xGqWlacW^I!oFuEAN?DCIs;y2Q!VJ@cgr?R>yq{(lqRvLKWC=vDa zb0lZL_lr<~$yumYp<*wiV7?|f=>}|we~n}+6_IF6ZdB*fy)SnU>V%5yZjZ@_@B#98 zy$+j`FS9xWpB}{iR5o4jM&91%v`thb>(wi#DmT<Irzd{CMY_s|y<>h+NcRb+%5p3c z;$Wm<yRhi7J2TYsP5cV|+O@C$jNq~_o1hj}W2DIj>c_xO0i+1lM<7Ljm_X-7lCdW; ze!yrDhM=L!+>Xjf%2U5YW>8;5;%<C1ik$d-BsVC|c0C6HTs*<*{oIJ@@$yuCx@w5N z?5;PFW$@|CP*S*hs$)hG4HD;qjX7ljda($l9N4N5Llguk0(F1F*TL3LdG;)yM@P=y zz3;PL5<mcJMUQD28f%wUC=?~@@G_lixYbtN9DbYgHyhV$+8(jHDqT2BZ=JJUsJTl0 zg-Rff-P*UK?C$Z3!jO^?)G33<qFIeo`j`?9W~9!;?4PVQ8zDxBF5x=rhnzL4+p2bq zw6E4(l%&hd9YGuRQz2nLqB0v?dq>s$^BIqLuBKf@c3bCwuKHd7+LD=Qo%!00$~18j z2Q94zMGIQ~k)fL#Z$_QC{2rm}p?So#c+ZCqVMR4{Gp{m{{Z*!`nM;#Z+WOvV*^O$p zo$B;XG}e4aN>j(X%v=gY5uJR45|_a$3Jr%0Vg3Q14ZHNc<ust61WiktXhOfj7=VQx z*_4DdtERXhU|BEdtN7<t(90_J*CQ+@Z&l4SyaD<Kx|aWxkFPdEQ1!c)tQiFaP+rdm z33nu8m7pj92**A=^ZGT9qDzgw(dHNe9!X%-PNdWCkpwf9&`Je!E<~l@geWjxTCPr& zfH}(DS=j_4p?I)rd;1GOc698wm(S`G-6?18HQ9{~c{-`@L=S$Y)JyJUaIg0OwTTrW zvz3L>vImX!Z`;#BQfQ6bf)gx*=3nJeM!&)6A49W`0T(H0ZwAkbE$Bv$$vea^SbOwG zYcahTzj5op)ZgcS>|31%AM-u-2~1}kfbu+&q@vusE2MS?hbJ)q9N_p=G<Pgj5WbEd z7_U-emtLEyo+)FJS(z(V?c>TTHYt?zw=$Ag_~89ZRfovH;U%1Q19WaxZ9W77TKHyt zwCq^tI9`@;zI6A_)+<+Id|k^Ow#$)yMxYMXQh1T20UhT%ZC&p3=9lTO{rSR|#bdj9 zlk>?*8M}p?>Lb?kGI;)fT?Tnsulf_OPJxfe4QA8FZUrnS7guS-Id}%{#RE{p6$zHk zicb;X?>;kMhzG07k4c>@UWe;?ph(B&I7*BKA-M3wzfw{!m*!=|{T9vTBO2T@B8(gz zUT+hap359M6-)iqsoeDF0c<Sz?)`3<&x<HS>o>j@!asS-(p$(m34c%y<ucObqt~8% zzH64tY&}J6H5>P6Xt-ZlNw@QERp=|Y*9dODQ*&Qz%GlR{)le-Lu{M8|_tm@D7g1N# zn$40RIi9sBF}oF^TCJD)EA&tntuw!&)RY;Ca(nvG&ZBy5Eb6*#uWEH}cDH}v?%^nA zb-r=EV$4(5OFwCi<2cyj>f!Ea*f{k%WnrF>wocp3vW~H|Kt0Y3^ubCYeXMKOy^ege zxpZ)jmouE}lg3sR6ci@qH!fah(5r=(u$YmQ;WMk3uI-$`ZP{OZxpYPLZQG4%0j-d! z&sr&VG(hj|+h|cpU+p8ajc)LynF~xDZKN74II(aT3iL}rm9kTZHPnSrvN1B^f+~u_ zYC_?jhfH`PT~78deIbxIt6t})uf-UykiyXKY8hF3z$QBV{P#_i*ai#ZU%PZo<awQg z>yr7-{nK`R(wv=}N6#0TV+&uYM^``rOSW(vdw1lkhp9D=JERuhAzt1pJA8NaNe?p# zTR2`CRf^)uQ&RlEMf3HWA{9nrjavTDRXp1_b~QSZQc_dPj!afdkM)Khx71HcSI*L1 z`{U-uA9cQoGsYSG%$1NGfklE%t5si^7WP7Dn5~h{GfgaC&UXef9<(vt3?r(CtCQ59 zM@9!RC;LI>M<f%Uom45JUu<MGmHjEt1ZAmLI37Rn_YIYva&S?42*t&y1+G@LjL{dv zCR6WTTuXv_CZfV+i#EFNp3DLHmPaVevTudLEhkdza<n|l6yvapx9Wu3wAQ8T%Gvgs zia2Lw{j2L7C(7ckQ~CHF_KwH*CID|sd$@Yt9(A$#O3BrbWh5B)4XOxnYAxhBU_xx_ zGSY6zqcYaM%D^;G|E%>&c8h|>3=e3Ld-Ivc>0kb*aY8afFlEcXbkr!^H%JOBMrN5U zAOt5KQ+7WcJft(eRb&Z<NQ~WXW_f$1;q5}V-e&V~e1uMM)_l(_S+h*uogKRG@n7)@ z*9o4uFcHj6Fa1I(&0?gTCLElzKdpn-JDhZGwF_3PSZQ)OAO0~;r%$8NVY!=_ltjsT zif!D(BhA>=TSCKht7N37g<SMVI42xghHB~%N>L|a{d=%1-AO)uIc&e7<bHoi-+HyI zuQsF@ZZIn_K%XK1@mJtLor5-pQ3NzG@a3zy;*rJ6*D<((Hn1fh`xw9Uv9MCdYSPMJ zk;?a&N~yr~aP_CCSVF3tO~olol;l{LB89Lw?_`=o>3e@5K_gJACdG?EnuLLM`Gdui z8dkx|=tJ5)P)Y4i;!K(#rlz(O&ji|pz?gYi>wUFmN1X%QZ=6@38di}PH!qJSFfT=e zragLT*b}Yi8;u2<Ng2gu4DL~wLt7X}N!m~Mpj)+MMvLoQ0oegvCg+%ff5N54e{dar z`shP6w<Bwo9~yjDXCq&fpn~V)r7fN;U6x`NMJQm5h%rd2l!YdVomBZkQ9@nTEBEQf zeE`DOVVB<YO?`y==LCul`4bK~7Y3QhN22i2Y}*3%%_tew*QClh7_Ze+dfXiEoHWCR zW&3NZ=dh-2ACK27F%vcHKv+@h)q8XPLSIjB)-Cvlw<~-k+pH4%gb`v9vn2_|koSM^ z?qyhzYX{4kEC{1{i(){WKjp+*Ch7=l8jC_G)g9=pdGYRPH?|LUX5Bq#@$l03L1k*p zgsQz(g!)&kIsB{85?5-CB=!daSjJZEVbPN0nHSF5bCj&=%vD&XqOWDBQOfBLVHK!Y zvy+${qLnKs<LVdkNb?v!!qj_U8{<k4OfbX2cqv<+TEo^I%7|6&JCbWkD-&6D5oGk$ z*Y3^?^H;bMb6wu4Jz^xSY1ymwbPICrc|XFh*!nbTT$&@z_@0i@pi-N3@OX8%)@f$A zt`9o!EC{WJP{HTGQ;H2i>rIF<X0YerEwZUdrX5Y-Iyp3V*cgY9=|Fu5HLftHMcfpd z3ka{fN4yNhtQ@<m@4v^Hu)xis!kHg;71aqc>CnPfYyvxurHe36YQ49X!SdleB0kh+ zq&On*tjxnt{x@WxO1*XnRK_=z&=<l(QB_z%OCG0%km|1TO)6(71_Ap@d85+!b(PsT zkIT7L`@@`NgM;=SSR+UVj)fpRZF`g-xmK_Y*I|<Wez3saTv6%u7|UW(SBLk#{fBks zNeomq3(>}tFp&VX*fRQP0`y1@_Ce<v*y#T6EwQ7s1}dmkILRx7UuAU!Jd}_1I@@u} z>jQhMb`HAvcy)d3eh30XnVrH#*~9WWQrW6U>Elh3mjVNk!1Y*Bro!VzNuO1{v4QJJ zm+`zYIs-@#Uc1<*=5eqkK_?i2@-sf;US76=Rj;+(H@xAQAqIbC4GX=?WLU(LQJn^t ziu_4cgz#Jks^7xc6uaah5no=&JuVX#*c7y*b`q#%fiDyMuj8wsuhSE?@|6$r7jl9o zH1~MrN9Z|(*o8)1<c7PYHL=;j0IC51cmIVoq|WDwfz}jR+Wh!q+WqvO687|n10MO2 ze%8)KAL~R8!Z)T>wVUv{=%Bg)@<_TPGzv-81SuML`EN<j;Kg@$%l_Ia+$rr4Oyv8< zFI4QnU~q&b(=UU@2eP?iurrL7ds{GD+%s?lz)T1f#vhEd-x$H}(4zVw*S2P!jBCNj zP6+3ksFWrj_>;MkM^_FR(HnUt_es*hR_0Dtn@pJ`5uo5CC297fZp=q9@b3c0a7BBE zRe~>ie;|s2t7pg2da}=&{s-IRlqe1BE@nfWc78uaJU+7<S@w0$7Y6ktI23!yUk69^ zBcO4jZy59I!id~tN2Gh;v=?H4O?)V#^@H#k<Eae|rh9JusrhkODOelLD><D7Ij>2_ z+3_nMV0C_0$s%d$(fTdCLGH^CGGZhBCRC3nB$QYa#28QGvrL|zrYU%@Gw5lUiZe}p z&94Yn0v*{}9d`-7SkFCV<E#M&bk4h1k3+K<losWLPiI@iFcir#tke-9hTvo|R`V)N z96Nd=o<E1O{V>xy1hV{n%l^DgochF;k<*q(%N>Z8y=*Ar@&GP2cw4Jf*JElZbSKd* zE95t7BnZRm#M$qMK$uiC9wam(tgKC-5|7`~EAtub{#{dxv)aD&h$@12b-wk90ukpd zXyV<~WT;0;z4daF?^6~Dmd{33xZ>Q?IjKf7K30v`tIt0wI_3eW&G-6lxA~4sQj9gC zB*0G+KmoOo|BC{Gjts>WH^cfP+k(8J9$%Yp<>#9gp1N95L#Od4kLXT3FJq9|3sqR9 z+J#H-n`OI+SD=1lY;S@gfef%U?Ca9%aFRd}$^Xwm)M3jvwSx(cgy;-}LYmlPFnz>% z6}<Ugz7+0ZRC*WxtL5P|{y?k;sh84V*kWBZ1d>x$-NS<pp~=}L)?Ay*5tt5p1GhNf z>9xb|6It+v&@m&>?`YzhGHn+LdFYIxqx|bnf0^$RT^OH#{Djrvt{Ty=?tNvoND5-^ z7K=H8!$yi2u;+}_D@?dA2}JbuD81HubJ9NL<{4F$+5X;cEVd2`sXS?cmzW$f2$-{~ z{_Jbs_h>H5=0TClIx-29jfOYvm^0k3y~sAdnkCEtq`@-aJIyiM6$j*8Z9gQeTAY#R znzZqrB3@fel<fm)_O3oAJ$|i8SC!||712JY*+*~CQJ5Z$Pn`=3A=-#Zr<eoG>l$_} zzamY^K|jCj1#OtWL9G8qTE5x<J-X#sL4thToV2&l`~WUvGOW$<$EFKlUN~2oZBKFk z=H#3OTdwsQ#v)!48~jI_5N~^;yh;*+CPRo`|Fy;E%fJscPx~;Ik*gOzhj|q{(?%uA zqx`fB4%#*XWoD^pRi%tx{&cuA3sx1S+wxHSGCYqVQ+1G`mk%BbmJ|G;sk+fn0(D}5 zg{1u5?ibm`PF(?ash-NBuinBV)4J->JTgncLPaX6ZdtJF;`Tf4h16Nhs3DbC56wrP zrVG~>y;_!k+fPW3y;%OUnL6eu-mz@%8Fd;)J+2>=vs_6lyHpcq#}DHyu_Z{c%`Yz* zGHI~mh{$0ikL}=i{JF#H(P<$W+rA8uRPW!NvK<i;dj;lmUxr>8WirmD^{LiqF(c>I zbiK6P$m^}zP5F4cl)0alE)D7<^gcQVNiA}#l|F)Ij~21;i#>s$Xj&ddR$D(tltJb( zjK9H|u=k3AV`l&aAZ-1wOsyP%6tGmBj&{Z`EykB5t6+5b@umzff-_v-Px<QA%#15t z*-9#11SECLMM`m@*I<1fC7<t=QNrf(cw-!+l@+Xm;FaH7`)ht<)^dSvUPD?}k{->| zEA6vsSMhEEB`Zd(rZt>VRlxiqoI0Zc4Q?cDs-DTK<dKS6niMc09pa+!p!*QBp^U2N zyH_~Mtk|&fI7rdj=o0kn28gd|a0uF?dm*Tnd~DkabwpA}ae>^4<BIE8`0iMKuLdF} z)u^@}7CkhUSDMb8{D~p&+YbhBHL2@8r;k_GNcsptoNqs_df|l=)4iEaY2&{QIhCZ| zotCz<=bL(l3%{v}%2rux+Kb2K1%r+{ykr+z5AI3gKxMzJM??9RkiRr2_)Z{`FnrJC zRxN+V)7aubOkVJQ@22H-z>h_L@}4HEW$R>R1v`zKRcFzxe#pVh<+-jq8(f#7^%-Fo ze8dRD<`69ki9M_r5Bq9xTjCsJ5*wmFBAg$S9bBzV9Z^{Xde0#3fV3vR3r$3YgL#{$ zK1A!H1As*J^e$FbkIiK+bh-gFP+#0O*H?Lg&b^D-rOUxsVSSu3WI9^-3qs-86z(6b z-;yv1exWf6OCP1qyJ88cbB~dc+(UmWf{;%e^v+~oF|fW^kF2yT<Ex=g>7$1KYTen) zAQO>o#9aPN?YMfUGvYG$T&DnrRwLF{gPZeBrdRXB*2e0!d88$$MZbj1`{PA70!%CC zkNZf@cwe=??eD*4YnI^vg~~rXS6tF?xDWvz`GXVi(=u%kZwM)F7YByQhbJv$m>n8Q z8k~P~insG3Ccs&ZJN?OxwU@ihb4`^`Z(R&m#|eOQ7T>pE8th;K)nWiGlhMq)GpJFs z2B;SF^M6$fwml-0pi94>a;2G4)k*xN_y`4NvLkrXoh960_aT`|aR@ePejkrfj#+u} z_2&1mDJNz-Rrfx<bY?46cPalbGH>yUQH2o1r@A>rZM&$@h0G?oP1cRXY2FJ<HF1H5 zLM+HkUd!}!%PjZCszuV~MKX^_=E<bU!Lj%kA|Z0iWT(0477-S2D=J1b0bH8_Zn@&G z1NLi}@%UpGmqyCpU-F2|BcNQ@eoQ9k)_$9`;dqH93HPZjysLBGom;nC;C<fPTjsco zlMkSMIB~M{l9ArsS+PM2>GT*{#I~QIgS7z`TJfqJ<VmVa1Tu_P;YRN{wfkiV=|nfP zx)b-}@evzPs2@wFdv|%;*Rp@N`Qpw0*zzzwvEk1?E$KOS-!5xrK`(oJh^{q9TD=op z6XZN{5Q`hfUsJM|KWcS!w=j~#gYUQsK`Z`B+SrG$V1=Ayc3wP)M-x-jwKVMfM|f$3 z@U`xf<{G}I>>B}}Ti|*|)f+hCg~jyXdc%4WsjWqKpvkjl!#4dF`si@tMOwzjblQbQ zfkT>11borQV8HSjfBa5Qp0CL@8aCOR%Gmg^(nj(+<{Sc)@#cAOEU#sWPX4&oIDh4_ zDChZ0oRw*|8l<u1wkOHXNAW_@DWsZkwj@I-amf%mYG@sNa1r@y2U$n~;b%s@<;vxg zr1i>dO<~P`(a9Vz)-?6RD`Q{s{JwHJ=)IfW)i1#h-`^cH+F5~q@9aF<S*||q?7)YY zJjp4*+v&gOB=H-`Qj!b_2}}^$4YddHy~2S-ah}i9EK12+@H54IYYb2Mn`G?8pQEAn zA0NISZ3ccN5K)o%D0UgdrkFcxg7I0G!gW8r$?vxm(`6rANo~0Pter+yUniHpnRoSB zs*BFG!7j-A><{@}_X6)#-4HBMxLlLRHKB>k9mZkfoVBo{TfhhzH5!I`Po9C<sq*f3 z?(9xeDc1mB*oZk(8TCrj4?}JjizbOSv8%7FaEGgsP2qy66}vUO+fX;Q*>+Pp_TMhU zQJ9w#^V-~YO3B=}H#GMAZmPI`s?=(6fUvaR?gn{nMeOOK`2%p=Yxx&FhKA5&MF#u{ zMclFCYl?!d?=@->b2~=$WxT}rI5Q4hwq?sB2X!sS<&ja_5l8o{*dHn&gmBFi?Km<> z1`$K-_|p9wQcQ;Ye$=5wm2X{v56gSo4wv0HLzUWX1=?c?(zz~Y<xe7(DtTQglxPH9 z32%qGa%gm^@7z<nV^Cn?r2jcQ`FTx*cie<lI7s>SYlJX<jMFuengJO8+13btg?b-1 zi{oJ?9D!TM{p`zWeBTIt-Dy1EvU0IGvEN2t8=PSMruvr{)f4+nO^bBH(wkndkDG4T zY((c%>KvB7GBS+y5km=wQp$b}5Yt!CDx4S8tTr4Suwn}wEnaiHv3rYaz#Krh>Wpcn zFafQ%Tw3G!WiK&eXeX-`in&~<$k;6Cs;et`r!RV_pc32EXE|}2Y2SJoLqd+P?r0d` zz1!Z?`0Dqp<9Ru}zRgll(wDDR4HGxsIFT@3PW#ng8g>o*8oI1{{#;3($eb{CGU}#S zTG{-m7^t;;nByIm>uZCCg(+mKu_-e&v>pGkMb{@0aNe(GX*m3d2yrt)p`3(`1;Bb8 zxK;V$;2vQ9{8gG*ww7?)FG1JvH4HK`JKom#F$lbczk!5_d+q>CrnOrC3@$f2oc8g+ zrW2ggU(x++n#c;HVF6;AQ{607Jb1@?*QU_-K`YO}kO-|~mTjUW?mFm2%QZm*)-`H! zI_*GWC6261BR{Ks06XFgAbmF}j|aPxd@gt7@MNh9xE`h=Q)fQ@<lXq-{WyNrBk)J3 z<~Wa`jyZ08={Il1t!uE?VqYx*<|v(B-q>seYU)lz#?2n@k9XFigbGGRo-psfHvhSP zvM+uG#`#@cc~SDtzu?m!(T#w&DVMZ5MK;n2^_p^l71;z#68L1|Rz*r$HYl)6fi|;% zjBI=VXF^4?kD=$dAo0n0MR^>+Du`rC$W*^I)Ek0wnZZk9r@h83G{30AVNbN}<&RZ{ zy(c#C)lR#RbHX%nvk|O-_`$tUODfOsuw8jzI6mgEpE4IrL%!}F1d_C7<{$M&0dd*O z?4eKNA;8HYf>PH}=yRbQ?({hLQSsBVIdj@)kd4qudb<fSaV4tT^(3mTXQBCCXfVVn zEr-g42GUtDybM29DQO<rB@_uE875&jo%H-M+g@PTm+m|=Y21nQL9p^C0>wJiJMs;x zvAJ&yzs1fG3ufC{x%}?N=dj%(&nK4>YKHv2N+yAf;!9Ib$6?ipJC`=s2L1)R@3|Ad zKXX=FmWqZ-_kAt~^bUIDtv?2qGwY$F1-mQ(TXja?<FgMHTDmW-myOu?=(ub#zxAK4 z{CvDU$T(zO4TV%CX?%xXeYrdOGhciEeQ!kf{aNS|Wuy*4u#W1hH$#5e$XRdcZ)F4+ zn@MV$X`6)-*8u_)k>6}FJgu!+Yjyto)Q6P`)hb!Va$C#lORYq#bg`R|_M|S;K`ZaA zuY09Ll*3KAVF!HmRi%)<RgW}MTa-O;l3x@Uddae}plxhdL}87e!RFEi>h1C1gP2@H zPI%17=CYE?=aF#Ujm*@mR~%UN{v-@g@$uE>kY~srW75kzQj6f_8haboga;hD4E$s; zbqsVbPnjZD9z5^=$e(;tuDbP;IjHl0Dx1_kfDy0~M3~IvW*UBJJGnnRJ2cSxHOz8* z_0Lo)f4_J0$vVikSsAq_&FViAip^)hVr%k6b|#}gxvGUtdK2pjWqQ-^+d8=x3R|Tm zq;3LEuVh}EfuT*;{<5qqPPq|Al_Hnv!^s|V$_d@ymajg!TQYrmJ?WvX5<c#%kj&_Q zPrze!+0$gKOkXQ6H==xLF;T$Dpv|9i`cCwSLXZDolMnUJo%@=bd0DoG*xUd;0nSSU zS0v#onrJ8*GHrAjBG%vZH8gV*&)SpuKq%f;EkHGuQeVYd%~}x8#YR&vJQ}Loyxd?! zk|osGkY$0*e})4>DY1s(XVN(Eyst$x!<5v~<~N7#gS=YOg7OyBgTHEyytFv|9f8qM z3dO_J_a=p*Qjd5%&0vl)iTY!G<myc$mWC#lf+szERoYd45&kSJLID1Dt?f_Kp)5+} zE@$Uo!V-b=enZytJD7iw(f5)&jB%zDpK~5*KFu{=2FHh%sgxiFLqKlt-1N0~66APi zeQXHk6b;(3e(J;~5UY1di@=VK3(;O7V8_A2B39SI!2MTz@U!=BVNG}rb*a~T(O|2# zQS0{yX+EcZh@5_v@3^Yp4zYpe;ZBgJ)>bXl3(Gm}Dy{jJTLSGK#sClW;7@JB`yF-9 ziNzjoC^vTi40d$>0+6|W!eji3TeTYRkAwF(IIjI7HWp3vz%VM(#;w6x?U=wcvw@9+ zgV9ZHlV~ry0Y=DIrkleRaQ}I;J|tt+%KCE(=^g7fIAYGoilq(*4zZSKjhE9xtR5QR z*HmXYq`4&zNUIkcjXT^C4m?{+5y3PO@?Icpwf`zLNRIwUN@KID|3A?y??aH5>k;47 zpLCBped5qMOzykhyF_U#aN0dB8)-%JN37gUC(G@6Ux0Ai`g$^^-!{9b(YuXw3hJSe zsbMu}@<!-S7fVDiNiut3!s9h43~>KUQ$v0}v^{3G0H8yL2Ri!2qyHhz?J6VUS)lcw z^q~XNhpY2-w-y?dqr5vA3@5AA>#&%@k{N2gHQe+9*lY*dTz-o7or(RSFr;`Zt>^cK zB#afO57AB_y+qe*W%&AiM<i>M${?H113{{?6-;`ZIJ;~g^tcVD9$X}RE0fZNsjU;~ zy+cWdWaq<TXo0cmwrfoW+*Iyf^HLU9c8i{jG!~%p01=isF5)w5O+9vA^|6?gAa#X* z$!1ti;i~7<n@-axBvtHw|5XTNHliPNgpPUbS#-@&Yr~K~Ypo<j!tPxy`%$2`F9nE! zD%t-s<pP?EDyJVGVo4Nay(0KCxvEb-YVw5$G}bu`UcWkyLlirtf&pEoWVimMUNWYq zf>R-71<0f1aERVxkECmS5>0X}O!kjx@Q`9}*S1V5)uE}lY%7E&ETJ`=5=PEy1i1co z!25<K!Qk3B)NJnudw$=AAlk0_gg}G*sy^}Ytl7E*L1qxg9Jw0n6ar;3N-7{^88(rX z`SklbR->F@wG>VTak^0&IoJfyvGmRKX^Fx9rAzd-j;2QjUyo(*>9n8U-!~Uh$R8TZ zWt*S51Iip4s{65p`L7dJ8Zgh1s1-(JOp5AktjVn~dKqIjKq;_<{G1Rx|40aiRx_5R z2givbd}EN~tSsX~r%s0EbQcLauHO%ldonft7H=D(*_oyu<ilO>v{?|=uz@c##D=y? z^pIki2DP;mUr4qspY|`^<`7ic6qd6Jh!qZfenbG(oKnH=td;(KouO5|x5UXG#h;e- z3{$i8=qTd-l)J>y3}3k8EJpQR%``hzEnmJsGU9B#yI{AvgxAXPo&JVZ;Ix^B$XRVN zA+$cZI4H8PuckWi#e*Vg=H$2JG4xXBIy<EK<u%<`v%r6e{{9<4@VOujRTYCWHo|9M z=FI=c%-O^E4L01?^>oqZ$-R25;`s2J+2cay2l9rf^I{7_MmR=t2Keew8%usZi{H6< z1UrDJ=cnwRP9{A!YRw?K7z?)-w-Vd}rSIob{EPWcr@g!MCV%rk+I_Wew&DA0IjgIB ze)_IxMPKe%r`bw1pyl2kn4WWVX3A)nfRLo_kgtcnn9QM)=92@k=D!4ECbD^=1}_o% z6O!n{u&2R_9U^UsCk70aCbA>)7-|k#!KS!1yc&KC8t}Ry{-knzoI|lfW`;;`T&YI_ zmR<TJzY>+3n8dASXW&ktiHT8$?67r}d5%jdOshA}v+uzSecp^oT+m8<3MD{i`yWl5 z87`C_+OH-oL<tanjDI8ic8%d{{E$vu<^vD>`}Fl^8*$`w`uVYOyTr$KLqZ`Vo9fMF zmwHdc&z~dZpVPp^V}O0mwOoWRKEYv}4DqfI*D7rn-j|r$Qkihle^Z2NF0<WmDgC}0 zNLqLQMQ<}}gzf^Sx5e^WQZVnT?k=>UnN9uHJUlnt^i3WNsD;*9wVr-c=bv_!MbHgX z{(jz<Jixxp*F-lCJ?oaNB8*6!QFW)1``p=x%}!;cjgU7-ST%n?&D&#9Jf&i_t-Ji~ zn_=oJeVdgL7B+!q-I|A(DR=X<dIgV*-K!Le)yl#Bp4nEm>qkgx(Icdz8w^$)K&0th zgNYEJ22H=nLX(kY^M{rt#Kggtu?@-QF;2hmBjP~wZb@!(HXhpzidW%7O;+o7F^9L% zo);&+r%WoJ$=}@aq4qtaT8%H#m~>8dWfRYtiIvTV6*c=e7e2OMmS>YPrj%|N{RrV? z5|fRMVMLG$7eh<Yj(>#2d3jXviNI)!_5P&v{1YLC2`3dENtikvnNqc1y>zZpV!Fvq zxlCu&m)9CazCt>vd<rd}zuPEP3TvNty_rWzMDeqUW~K0lQ6}vA*cj?h{k#|s1Wwu` zXux8`{$nvdT>P+ETDn|OfE%d2f*1uuCdx8yPl#1qz1<}56*ax*0u#_YNi=RuKskcw zOUAJthR7_wJ^}WLn51H$L+Od8*MMg9Ybwe9hS$!QeHzG<cc}HNY4Bp<@AlHkxAi9! zkn&K!i;2rka`ExYU<gkqDd|-5*4y`)O#r2`RStL7F(gm$q+k5;Fr@0!eS7|`F>~=6 zv5j~u{z!p6f_jBaozrAquBzqvb^gi=g*!B>u|kc>8HXiS*a&5r6rfr@D}1M?;L@7b z^Oyxbycueh1hRV%z1#}@8Px}3_<AG|(cA<N`Dt%gzy0<~D6=XP9PT7rTwABmU-a3P zpvjGB9+Eb9KvpjyS+-a&HgH|(batBg$t)RW-VBAE4Vg6W6;VJ5w}mhy$qYXTR8Tki ztmP^Cy9T(=LnzhNXU%K0AjwFD&zrW_a(fY~V71DoL{N9&?kV3?TZ#{Wu+8_R?Od^s z%8vG_{yJ}Usa2k<032TuB8<#AoU9U<fKEMUG8WwYm*gEuN-EF-Hr2r}?xEwoh-?@; ze(Tkla)NC{)@1x3qvD^9&$bF~8Nt%#Rz5SyX1%?V5P8|9h7WUChCT>P)YWebi0NX| zhuT_A#AM9-FIvTg94V(H*vvm?tOEcjgwK8FGU}4&g})5<YZwxdS--NY(|AwZuNu27 zGs7bI7rS09co>;2j#~|aT5T_7sD&1D0RHkXO?lT2G4~wxgs#E02mG_7oJaTnBq^UI zjw2}1+bNqr)2@K*ZZW74%42O!;~nPzk`+@-&*D7%W=nPHT%O<sK3Z0P6kKk)uH@RJ ztZPhpg+oV$MHH6@MgCXF<M<0I=AT2J|M!rKaW^}yZIHzYv=Cj(e1od<CrB6U=5K=H z6KAuh?^9hrMRh_ClVbgsh?W=E*^;Lu8Wr&7n;2m<Q}7M`Q#dZB@-~3NVZk9O&356) z@cx*YK;YU=4s_i~lV7*LxM(BAbY7I9R&F+q9(C>G(kan$MPi66un(o?Ce8EcttG;L zCA8A=&WpxaTWfMExvRvFRx0x6Sq#xowKid+z2u{xROYP5<<V1dYShu%?0s+@`j_+9 z^;Tn^K6oITgm9~q;|I?UOnmPu+#nLMUf$?h3IBK4*H45Xnh;)1S*!OSaM>vVI1AGg z=6Dusg%cDJ6fR@|P*!jLMp>0#S@cS7YPP+n_HlJ9d9}NaQQQw=TFkPO)uAZxPH$b~ z+6z!ZmDN)WV`7@ne9HNxI~E)gj%`R~RQ(zURtsCPk%NzP1R_c;<A%Kv_eqzn7)>7q zbZfJm#!}u{Ep6=^1Y6~NxI5|=;e9cG{c08AV<40(&#^!5XrN-{;hjK0y6AgqwV=yR zmX3#$Q1rEWulubI{$GOfCB&P>%~sn9T?E>7rB0SRsvfS2L2f&S%@l+Rnr*OqQf!~L zq9AWwdMDk7Y8>EsTe$}#-#zU;A|4R4K<z!+T<O%<PZv1q@G=jw+*m&JV+>mz?_UT< z6*(o^$sdwkmYe?MYBgDje4BavEO*yrS@*_Jj96>R77iu_m6bGSQy8)?JizZv%V*uz zhJ1M2@C%Qcit;-=0ZVti`}+@5Eokk|suS3DUzN~fGk#pX;Jlcht!9QODsCjh_6X>L ziK5JaAz0ENMkItBUbxDl*I6VTwnMvVQ^xTt8<x8Ka=0O`$7BwD)i?ODKRHxy%bQx* zsW(W!p<_>Po(4eiP<e?*37YI91W;FG^ss$H-C_LCGs~71t|F3s+dAITPkJ4Hbe2s` zbou0I`=}9g>0^fie%f2mCZZxAx&fMvzKzw`m=N&xR)P-ri2ZSxk1tnssFRBh)_Aex zt(#~4NbVb%_dv0$$+uTo$GkC-wc_0k?^49wI{3fXd&{W0nr>YXCxj5(0>Rz)-Z()L zf)g~jySqCi1ef402^QSl-QC@Sy9Vbjl6?1k@408(?mqpedyG5wkF}SKwPw|ns+w~? zv$npo&FAs+Tq#}f{9t@bM&NQnPugrpD@l=EnCNqso3t6$rI(To?|1P+gbGShGoinP zf;_VW3v*x&x+ABpE^Wv+?LT?s>YPUQw~8M&Z$#YmhV9r0x|;^jRgu^Cy_Imv*XgQ? z9><c71~onxqOMuUQz}kneh)w#QPY)QGpduUyG&&=-GLTfY#i<i4H?fi&`_rT)l?wc z&Zp{fyp3e~2K`&$XHl?c6v$#NS#>NDErRL6)@^Dfq1j3qjUu~pDquq=`SMpNkFL4t z=2jArcdcSnLwk(q4$fvvvFp;7>2#(!nsai#I^URH9M5uhFz3`uk-*^6n<LDrq(0X6 zhH1y*;2q=H8l1ht6QPdx|G(erU7}IKSgRR@^{j7ktQ|QVbA`wpqUrHfV^Q~Yp~>N& zrPMi=XiGm)V1GG<`u+v!Y>M5IXU%D*;fJa4N<9178n+8$on&H_@qGlbXJ!|sxRroz zWXQ6<B*BPY4hcT{N0lm1qlb~%X;!)yug4ve-q-<<B!^su1*|JUVc_4%5WX*v9ok(> z&)z)eHb;qPoEPZ>LcB;ISCmA4g*~olp7OAt9syCbd_0tKFC}_nW=@~FA0<91Wp=#w z)JqaR?B6nbf~~b4`4Cc!q4J5U_Gjc!NKYuwFa9~tg|x(rmgL^CNl2+urkW%{quMh* z$B!0ni?AALH11Y_=e3C7C|dn(WMZ2$RKE%02hAWPCERH%e-Ew6;E;=*J{8xNVn|+B z#1~M7t%I$6zP`F9HHXu2!SM7!N1TCcm7gp@bS%aCbdU*u-Kvd+mumfMZl3Wz!=3$& z$sZ*uOYyYGlMfYdE2(xccA=d;uWvLC{D!VSzsl+KqvP8B3IB`3pi(P&E)B=cMPaa} z&Supvz(l%@(_-M>BqbI{o4MHc222)0)Fc`l&rZx+^~e}<`|6NUjhcg!F+(y-@k6-? z3V_*vTqO=V!U<DO_FX%LLNMWZaa%LjjF6Invt=mvxNmbi@=Jb?N_<%At&wKwMPuQT zN`oPRFwvUagBM`}6bog~f7zq_tLp0HNO6SXBy?C#GDAt_dAvL{Y>YrbZQfKqskpE! zYF@PuN4cpkD(#<w9^Lh>{OL<5D5A$GY<k_iiF9giOOvY>)$EFDbIx2G3O!;c+%X*R zL4|El#cQE+GrNU@N4p!ph^x`u{B<v3#OTB1zUtq(c2p#$L>S`Frm^FX<A`+nMg$hJ zHP1BE_XoImHRP`+V2sb9F!v-N>!@RY!r_2_XaOc!g3br_WR=4&Z}<BToGM>E%y)g6 zSxVqGN@GfNH8CSUVK7f|Qn|k@zD%sTn1?<2kR7_i_~iWSm6L?i7m)QGM~S(kB*s}p zRn@@KAdz^cOKOHV{UFHyBBg4-DI~(6^uni~&lwyPFd)(G!?R(!p{dTnueTyH-^3%z z<0@8a9?rjFWkCp&#ZA1*r}DMM6<XA%sD!o;nY`1KRdq}uB9Ijx>=%d+3)R{+J^zbW z{%WaqCUh{7=YZibJ29&+YyxasYA%z4ppbRIhJq|Sto-FGUi&H9kgf_kY;<Su3KU)f z=R+-GC=XvWzhmF`3p_?&)+$K4LS$;_2{cR)CDONm-x>M)dR1PrCOKHhGckZG?@edk zfzJ{1!+2xLdV-sF__*H>p20VaJEn|Z-^VWW2h$Ht%0ATcF{m<<&<1iTJ-KM!Kd07L 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 diff --git a/documentation/manual.rst b/documentation/manual.rst index d51f07c1..e545bdaf 100644 --- a/documentation/manual.rst +++ b/documentation/manual.rst @@ -1787,7 +1787,7 @@ Hdf5Writer Hdf5Reader HDF5 Write interfaces, similar to the XML facilities in QDP++ are presented. However, the serialisation routines are automatically generated by the macro, and a virtual -reader adn writer interface enables writing to any of a number of formats. +reader and writer interface enables writing to any of a number of formats. **Example**:: @@ -1814,6 +1814,91 @@ reader adn writer interface enables writing to any of a number of formats. } +Eigen tensor support -- added 2019H1 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Serialisation library was expanded in 2019 to support de/serialisation of +Eigen tensors. De/serialisation of existing types was not changed. Data files +without Eigen tensors remain compatible with earlier versions of Grid and other readers. +Conversely, data files containing serialised Eigen tensors is a breaking change. + +Eigen tensor serialisation support was added to BaseIO, which was modified to provide a Traits class +to recognise Eigen tensors with elements that are either: primitive scalars (arithmetic and complex types); +or Grid tensors. + +**Traits determining de/serialisable scalars**:: + + // Is this an Eigen tensor + template<typename T> struct is_tensor : std::integral_constant<bool, + std::is_base_of<Eigen::TensorBase<T, Eigen::ReadOnlyAccessors>, T>::value> {}; + // Is this an Eigen tensor of a supported scalar + template<typename T, typename V = void> struct is_tensor_of_scalar : public std::false_type {}; + template<typename T> struct is_tensor_of_scalar<T, typename std::enable_if<is_tensor<T>::value && is_scalar<typename T::Scalar>::value>::type> : public std::true_type {}; + // Is this an Eigen tensor of a supported container + template<typename T, typename V = void> struct is_tensor_of_container : public std::false_type {}; + template<typename T> struct is_tensor_of_container<T, typename std::enable_if<is_tensor<T>::value && isGridTensor<typename T::Scalar>::value>::type> : public std::true_type {}; + + +Eigen tensors are regular, multidimensional objects, and each Reader/Writer +was extended to support this new datatype. Where the Eigen tensor contains +a Grid tensor, the dimensions of the data written are the dimensions of the +Eigen tensor plus the dimensions of the underlying Grid scalar. Dimensions +of size 1 are preserved. + +**New Reader/Writer methods for multi-dimensional data**:: + + template <typename U> + void readMultiDim(const std::string &s, std::vector<U> &buf, std::vector<size_t> &dim); + template <typename U> + void writeMultiDim(const std::string &s, const std::vector<size_t> & Dimensions, const U * pDataRowMajor, size_t NumElements); + + +On readback, the Eigen tensor rank must match the data being read, but the tensor +dimensions will be resized if necessary. Resizing is not possible for Eigen::TensorMap<T> +because these tensors use a buffer provided at construction, and this buffer cannot be changed. +Deserialisation failures cause Grid to assert. + + +HDF5 Optimisations -- added June 2021 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Grid serialisation is intended to be light, deterministic and provide a layer of abstraction over +multiple file formats. HDF5 excels at handling multi-dimensional data, and the Grid HDF5Reader/HDF5Writer exploits this. +When serialising nested ``std::vector<T>``, where ``T`` is an arithmetic or complex type, +the Hdf5Writer writes the data as an Hdf5 DataSet object. + +However, nested ``std::vector<std::vector<...T>>`` might be "ragged", i.e. not necessarily regular. E.g. a 3d nested +``std::vector`` might contain 2 rows, the first being a 2x2 block and the second row being a 1 x 2 block. +A bug existed whereby this was not checked on write, so nested, ragged vectors +were written as a regular dataset, with a buffer under/overrun and jumbled contents. + +Clearly this was not used in production, as the bug went undetected until now. Fixing this bug +is an opportunity to further optimise the HDF5 file format. + +The goals of this change are to: + +* Make changes to the Hdf5 file format only -- i.e. do not impact other file formats + +* Implement file format changes in such a way that they are transparent to the Grid reader + +* Correct the bug for ragged vectors of numeric / complex types + +* Extend the support of nested std::vector<T> to arbitrarily nested Grid tensors + + +The trait class ``element`` has been redefined to ``is_flattenable``, which is a trait class for +potentially "flattenable" objects. These are (possibly nested) ``std::vector<T>`` where ``T`` is +an arithmetic, complex or Grid tensor type. Flattenable objects are tested on write +(with the function ``isRegularShape``) to see whether they actually are regular. + +Flattenable, regular objects are written to a multidimensional HDF5 DataSet. +Otherwise, an Hdf5 sub group is created with the object "name", and each element of the outer dimension is +recursively written to as object "name_n", where n is a 0-indexed number. + +On readback (by Grid)), the presence of a subgroup containing the attribute ``Grid_vector_size`` triggers a +"ragged read", otherwise a read from a DataSet is attempted. + + Data parallel field IO ----------------------- From 0e27e3847d6252c7a950e59517a8af0bf1e15549 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@arcticus08.ftm.alcf.anl.gov> Date: Thu, 3 Jun 2021 04:24:19 +0000 Subject: [PATCH 164/399] Remove synch --- Grid/threads/Accelerator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 56b85c72..b76d6d1c 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -457,7 +457,7 @@ accelerator_inline void acceleratorSynchronise(void) __syncwarp(); #endif #ifdef GRID_SYCL - cl::sycl::detail::workGroupBarrier(); + //cl::sycl::detail::workGroupBarrier(); #endif #ifdef GRID_HIP __syncthreads(); From ca10bfa1c7f6615bcc13322ee5e050c6e4b37ad8 Mon Sep 17 00:00:00 2001 From: Antonin Portelli <antonin.portelli@me.com> Date: Fri, 4 Jun 2021 11:12:22 +0100 Subject: [PATCH 165/399] removing Travis CI constantly failing due to overtime (no way we can compile Grid on free time anymore) --- .travis.yml | 56 ----------------------------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3a0e1e35..00000000 --- a/.travis.yml +++ /dev/null @@ -1,56 +0,0 @@ -language: cpp - -cache: - directories: - - clang - -matrix: - include: - - os: osx - osx_image: xcode8.3 - compiler: clang - -before_install: - - export GRIDDIR=`pwd` - - if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]] && [ ! -e clang/bin ]; then wget $CLANG_LINK; tar -xf `basename $CLANG_LINK`; mkdir clang; mv clang+*/* clang/; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]]; then export PATH="${GRIDDIR}/clang/bin:${PATH}"; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$CC" == "clang" ]]; then export LD_LIBRARY_PATH="${GRIDDIR}/clang/lib:${LD_LIBRARY_PATH}"; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libmpc openssl; fi - -install: - - export CWD=`pwd` - - echo $CWD - - export CC=$CC$VERSION - - export CXX=$CXX$VERSION - - echo $PATH - - which autoconf - - autoconf --version - - which automake - - automake --version - - which $CC - - $CC --version - - which $CXX - - $CXX --version - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export LDFLAGS='-L/usr/local/lib'; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export EXTRACONF='--with-openssl=/usr/local/opt/openssl'; fi - -script: - - ./bootstrap.sh - - mkdir build - - cd build - - mkdir lime - - cd lime - - mkdir build - - cd build - - wget http://usqcd-software.github.io/downloads/c-lime/lime-1.3.2.tar.gz - - tar xf lime-1.3.2.tar.gz - - cd lime-1.3.2 - - ./configure --prefix=$CWD/build/lime/install - - make -j4 - - make install - - cd $CWD/build - - ../configure --enable-simd=SSE4 --enable-comms=none --with-lime=$CWD/build/lime/install ${EXTRACONF} - - make -j4 - - ./benchmarks/Benchmark_dwf --threads 1 --debug-signals - - make check From 92def28bd3331153da2b8a2414f471e4f7831a4c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@users.noreply.github.com> Date: Sun, 6 Jun 2021 04:52:05 -0400 Subject: [PATCH 166/399] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fff68dc6..88b922a5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Grid [![Teamcity status](http://ci.cliath.ph.ed.ac.uk/app/rest/builds/aggregated/strob:(buildType:(affectedProject(id:GridBasedSoftware_Grid)),branch:name:develop)/statusIcon.svg)](http://ci.cliath.ph.ed.ac.uk/project.html?projectId=GridBasedSoftware_Grid&tab=projectOverview) [![Travis status](https://travis-ci.org/paboyle/Grid.svg?branch=develop)](https://travis-ci.org/paboyle/Grid) +# Grid [![Teamcity status](http://ci.cliath.ph.ed.ac.uk/app/rest/builds/aggregated/strob:(buildType:(affectedProject(id:GridBasedSoftware_Grid)),branch:name:develop)/statusIcon.svg)](http://ci.cliath.ph.ed.ac.uk/project.html?projectId=GridBasedSoftware_Grid&tab=projectOverview) **Data parallel C++ mathematical object library.** From 2df308f649195e9f80c571b993073d556f134b64 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 7 Jun 2021 23:25:07 +0100 Subject: [PATCH 167/399] Add a ragged vector to the serialisation tests. NB: Already had nested (regular) std::vector<std::vector<...>> --- tests/IO/Test_serialisation.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 27fe589e..11db1908 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -48,6 +48,7 @@ public: std::vector<double>, array, std::vector<std::vector<double> >, twodimarray, std::vector<std::vector<std::vector<std::complex<double>> > >, cmplx3darray, + std::vector<std::vector<std::vector<int> > >, ragged, SpinColourMatrix, scm ); myclass() {} @@ -56,6 +57,9 @@ public: , twodimarray(3,std::vector<double>(5, 1.23456)) , cmplx3darray(3,std::vector<std::vector<std::complex<double>>>(5, std::vector<std::complex<double>>(7, std::complex<double>(1.2, 3.4)))) , ve(2, myenum::blue) + , ragged( {{{i+1},{i+2,i+3}}, // ragged + {{i+4,i+5,i+6,i+7},{i+8,i+9,i+10,i+11},{i+12,i+13,i+14,i+15}}, // block + {{i+16,i+17},{i+18,i+19,i+20}}} ) //ragged { e=myenum::red; x=i; From 0c4f5854964dfad6123f31fe190e873839f17a78 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 8 Jun 2021 00:05:35 +0100 Subject: [PATCH 168/399] Test nested std::vector<grid tensor> --- tests/IO/Test_serialisation.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 11db1908..e1596ea6 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -48,8 +48,9 @@ public: std::vector<double>, array, std::vector<std::vector<double> >, twodimarray, std::vector<std::vector<std::vector<std::complex<double>> > >, cmplx3darray, + SpinColourMatrix, scm, std::vector<std::vector<std::vector<int> > >, ragged, - SpinColourMatrix, scm + std::vector<std::vector<SpinColourMatrix> >, vscm ); myclass() {} myclass(int i) @@ -60,6 +61,7 @@ public: , ragged( {{{i+1},{i+2,i+3}}, // ragged {{i+4,i+5,i+6,i+7},{i+8,i+9,i+10,i+11},{i+12,i+13,i+14,i+15}}, // block {{i+16,i+17},{i+18,i+19,i+20}}} ) //ragged + , vscm(3, std::vector<SpinColourMatrix>(5)) { e=myenum::red; x=i; @@ -72,6 +74,13 @@ public: scm()(0, 2)(1, 1) = 6.336; scm()(2, 1)(2, 2) = 7.344; scm()(1, 1)(2, 0) = 8.3534; + int Counter = i; + for( auto & v : vscm ) { + for( auto & j : v ) { + j = std::complex<double>(Counter, -Counter); + Counter++; + } + } } }; From 4c5440fb0678b3a936ebe95f2c891d90b62feaaf Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@arcticus07.ftm.alcf.anl.gov> Date: Tue, 15 Jun 2021 21:45:07 +0000 Subject: [PATCH 169/399] const happy for sycl --- Grid/tensors/Tensor_extract_merge.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/tensors/Tensor_extract_merge.h b/Grid/tensors/Tensor_extract_merge.h index f1ded209..ea619d0f 100644 --- a/Grid/tensors/Tensor_extract_merge.h +++ b/Grid/tensors/Tensor_extract_merge.h @@ -1,5 +1,5 @@ /************************************************************************************* - +n Grid physics library, www.github.com/paboyle/Grid Source file: ./lib/tensors/Tensor_extract_merge.h @@ -153,7 +153,7 @@ void insertLane(int lane, vobj & __restrict__ vec,const typename vobj::scalar_ob // Extract to a bunch of scalar object pointers of different scalar type, with offset. Useful for precision change //////////////////////////////////////////////////////////////////////// template<class vobj, class sobj> accelerator -void extract(const vobj &vec,ExtractPointerArray<sobj> &extracted, int offset) +void extract(const vobj &vec,const ExtractPointerArray<sobj> &extracted, int offset) { typedef typename GridTypeMapper<sobj>::scalar_type sobj_scalar_type; typedef typename GridTypeMapper<vobj>::scalar_type scalar_type; @@ -181,7 +181,7 @@ void extract(const vobj &vec,ExtractPointerArray<sobj> &extracted, int offset) // Merge bunch of scalar object pointers of different scalar type, with offset. Useful for precision change //////////////////////////////////////////////////////////////////////// template<class vobj, class sobj> accelerator -void merge(vobj &vec,ExtractPointerArray<sobj> &extracted, int offset) +void merge(vobj &vec,const ExtractPointerArray<sobj> &extracted, int offset) { typedef typename GridTypeMapper<sobj>::scalar_type sobj_scalar_type; typedef typename GridTypeMapper<vobj>::scalar_type scalar_type; From 6cd9224dd78aca959d3997479287cf943832d79c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@arcticus08.ftm.alcf.anl.gov> Date: Wed, 16 Jun 2021 17:10:55 +0000 Subject: [PATCH 170/399] SYCL comms buffer allocate --- Grid/communicator/SharedMemoryMPI.cc | 54 +++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 466f6a1e..786122fa 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -35,6 +35,9 @@ Author: Christoph Lehner <christoph@lhnr.de> #endif #ifdef GRID_HIP #include <hip/hip_runtime_api.h> +#endif +#ifdef GRID_SYCl + #endif NAMESPACE_BEGIN(Grid); @@ -446,7 +449,46 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) //////////////////////////////////////////////////////////////////////////////////////////// // Hugetlbfs mapping intended //////////////////////////////////////////////////////////////////////////////////////////// -#if defined(GRID_CUDA) ||defined(GRID_HIP) +#if defined(GRID_CUDA) ||defined(GRID_HIP) || defined(GRID_SYCL) + +#if defined(GRID_SYCL) +void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) +{ + void * ShmCommBuf ; + assert(_ShmSetup==1); + assert(_ShmAlloc==0); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // allocate the pointer array for shared windows for our group + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + MPI_Barrier(WorldShmComm); + WorldShmCommBufs.resize(WorldShmSize); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Each MPI rank should allocate our own buffer + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + ShmCommBuf = acceleratorAllocDevice(bytes); + + if (ShmCommBuf == (void *)NULL ) { + std::cerr << " SharedMemoryMPI.cc acceleratorAllocDevice failed NULL pointer for " << bytes<<" bytes " << std::endl; + exit(EXIT_FAILURE); + } + // if ( WorldRank == 0 ){ + if ( 1 ){ + std::cout << WorldRank << header " SharedMemoryMPI.cc acceleratorAllocDevice "<< bytes + << "bytes at "<< std::hex<< ShmCommBuf <<std::dec<<" for comms buffers " <<std::endl; + } + SharedMemoryZero(ShmCommBuf,bytes); + + for(int r=0;r<WorldShmSize;r++){ + WorldShmCommBufs[r] = ShmCommBuf; + } + _ShmAllocBytes=bytes; + _ShmAlloc=1; +} +#endif + +#if defined(GRID_CUDA) ||defined(GRID_HIP) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) { void * ShmCommBuf ; @@ -557,6 +599,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) _ShmAllocBytes=bytes; _ShmAlloc=1; } +#endif + #else #ifdef GRID_MPI3_SHMMMAP void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) @@ -727,16 +771,16 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) ///////////////////////////////////////////////////////////////////////// void GlobalSharedMemory::SharedMemoryZero(void *dest,size_t bytes) { -#ifdef GRID_CUDA - cudaMemset(dest,0,bytes); +#if defined(GRID_CUDA) || defined(GRID_HIP) || defined(GRID_SYCL) + acceleratorMemSet(dest,0,bytes); #else bzero(dest,bytes); #endif } void GlobalSharedMemory::SharedMemoryCopy(void *dest,void *src,size_t bytes) { -#ifdef GRID_CUDA - cudaMemcpy(dest,src,bytes,cudaMemcpyDefault); +#if defined(GRID_CUDA) || defined(GRID_HIP) || defined(GRID_SYCL) + acceleratorCopyToDevice(src,dest,bytes); #else bcopy(src,dest,bytes); #endif From 80afacec5b44e89b6f39e57249a57f8b134e1f2d Mon Sep 17 00:00:00 2001 From: Peter Georg <peter.georg@physik.uni-regensburg.de> Date: Thu, 17 Jun 2021 13:05:13 +0200 Subject: [PATCH 171/399] nvcc: Add -fopenmp to LDFLAGS --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 4e5e33c8..02720d36 100644 --- a/configure.ac +++ b/configure.ac @@ -390,6 +390,7 @@ case ${CXXTEST} in CXXFLAGS="$CXXFLAGS -Xcompiler -fno-strict-aliasing --expt-extended-lambda --expt-relaxed-constexpr" if test $ac_openmp = yes; then CXXFLAGS="$CXXFLAGS -Xcompiler -fopenmp" + LDFLAGS="$LDFLAGS -Xcompiler -fopenmp" fi ;; hipcc) From c50f27e68bd4b3e4fb6a1da00aebe224f0a0bc23 Mon Sep 17 00:00:00 2001 From: Christoph Lehner <christoph@lhnr.de> Date: Sun, 20 Jun 2021 11:34:38 +0200 Subject: [PATCH 172/399] Make FFT play nice with split grid --- Grid/algorithms/FFT.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/algorithms/FFT.h b/Grid/algorithms/FFT.h index ad42f049..29f0ec4b 100644 --- a/Grid/algorithms/FFT.h +++ b/Grid/algorithms/FFT.h @@ -136,7 +136,7 @@ public: flops=0; usec =0; Coordinate layout(Nd,1); - sgrid = new GridCartesian(dimensions,layout,processors); + sgrid = new GridCartesian(dimensions,layout,processors,*grid); }; ~FFT ( void) { @@ -182,7 +182,7 @@ public: pencil_gd[dim] = G*processors[dim]; // Pencil global vol LxLxGxLxL per node - GridCartesian pencil_g(pencil_gd,layout,processors); + GridCartesian pencil_g(pencil_gd,layout,processors,*vgrid); // Construct pencils typedef typename vobj::scalar_object sobj; From 403bff1a47751996d6d7161403c17c7c20f596eb Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@arcticus08.ftm.alcf.anl.gov> Date: Tue, 22 Jun 2021 17:56:10 +0000 Subject: [PATCH 173/399] Force reqd subgroup size fo SYCL --- Grid/threads/Accelerator.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index b76d6d1c..c0af1019 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -257,11 +257,14 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { unsigned long nt=acceleratorThreads(); \ unsigned long unum1 = num1; \ unsigned long unum2 = num2; \ + if(nt < 8)nt=8; \ cl::sycl::range<3> local {nt,1,nsimd}; \ cl::sycl::range<3> global{unum1,unum2,nsimd}; \ cgh.parallel_for<class dslash>( \ cl::sycl::nd_range<3>(global,local), \ - [=] (cl::sycl::nd_item<3> item) /*mutable*/ { \ + [=] (cl::sycl::nd_item<3> item) /*mutable*/ \ + [[intel::reqd_sub_group_size(8)]] \ + { \ auto iter1 = item.get_global_id(0); \ auto iter2 = item.get_global_id(1); \ auto lane = item.get_global_id(2); \ From 29a22ae603a3cf18d2ebeba2eb5aabcf27fe3e5d Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@arcticus08.ftm.alcf.anl.gov> Date: Tue, 22 Jun 2021 17:57:20 +0000 Subject: [PATCH 174/399] Simpler SYCL setup --- Grid/communicator/SharedMemoryMPI.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 786122fa..caa03a60 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -473,13 +473,13 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) std::cerr << " SharedMemoryMPI.cc acceleratorAllocDevice failed NULL pointer for " << bytes<<" bytes " << std::endl; exit(EXIT_FAILURE); } - // if ( WorldRank == 0 ){ - if ( 1 ){ - std::cout << WorldRank << header " SharedMemoryMPI.cc acceleratorAllocDevice "<< bytes - << "bytes at "<< std::hex<< ShmCommBuf <<std::dec<<" for comms buffers " <<std::endl; - } + + std::cout << WorldRank << header " SharedMemoryMPI.cc acceleratorAllocDevice "<< bytes + << "bytes at "<< std::hex<< ShmCommBuf <<std::dec<<" for comms buffers " <<std::endl; + SharedMemoryZero(ShmCommBuf,bytes); + assert(WorldShmSize == 1); for(int r=0;r<WorldShmSize;r++){ WorldShmCommBufs[r] = ShmCommBuf; } From 323cf6c038adeaaef45e8ca4e35dfde81dc3c681 Mon Sep 17 00:00:00 2001 From: Ed Bennett <e.j.bennett@swansea.ac.uk> Date: Wed, 23 Jun 2021 17:00:43 +0100 Subject: [PATCH 175/399] make message consistent with configure script --- Grid/threads/Accelerator.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 9d9d851c..688f1e1d 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -83,11 +83,11 @@ void acceleratorInit(void) printf("AcceleratorCudaInit: using default device \n"); printf("AcceleratorCudaInit: assume user either uses a) IBM jsrun, or \n"); printf("AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding \n"); - printf("AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no \n"); + printf("AcceleratorCudaInit: Configure options --enable-setdevice=no \n"); } #else printf("AcceleratorCudaInit: rank %d setting device to node rank %d\n",world_rank,rank); - printf("AcceleratorCudaInit: Configure options --enable-select-gpu=yes \n"); + printf("AcceleratorCudaInit: Configure options --enable-setdevice=yes \n"); cudaSetDevice(rank); #endif if ( world_rank == 0 ) printf("AcceleratorCudaInit: ================================================\n"); From 25e9be50b541290d85c38936ceee84b31bd76b75 Mon Sep 17 00:00:00 2001 From: Felix Erben <felix.erben@ed.ac.uk> Date: Fri, 2 Jul 2021 15:51:19 +0100 Subject: [PATCH 176/399] created test file --- tests/Test_meson_field.cc | 91 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/Test_meson_field.cc diff --git a/tests/Test_meson_field.cc b/tests/Test_meson_field.cc new file mode 100644 index 00000000..b108ee57 --- /dev/null +++ b/tests/Test_meson_field.cc @@ -0,0 +1,91 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: tests/core/Test_meson_field.cc + +Copyright (C) 2015-2018 + +Author: Felix Erben <felix.erben@ed.ac.uk> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ + +#include <Grid/Grid.h> +#include <Grid/qcd/utils/A2Autils.h> + +using namespace Grid; + +typedef typename DomainWallFermionR::ComplexField ComplexField; +typedef typename DomainWallFermionR::FermionField FermionField; + +int main(int argc, char *argv[]) +{ + // initialization + Grid_init(&argc, &argv); + std::cout << GridLogMessage << "Grid initialized" << std::endl; + + Coordinate latt_size = GridDefaultLatt(); + Coordinate simd_layout = GridDefaultSimd(4, vComplex::Nsimd()); + Coordinate mpi_layout = GridDefaultMpi(); + GridCartesian grid(latt_size,simd_layout,mpi_layout); + + // MesonField lhs and rhs vectors + int mfDim = 10; + std::vector<FermionField> phi(mfDim,&grid); + std::vector<FermionField> rho(mfDim,&grid); + // Gamma matrices used in the contraction + Gamma::Algebra Gmu [] = { + Gamma::Algebra::GammaX, + Gamma::Algebra::GammaY, + Gamma::Algebra::GammaZ, + Gamma::Algebra::GammaT + }; + + // momentum phases e^{ipx} + std::vector<std::vector<double>> momenta = { + {0.,0.,0.}, + {1.,0.,0.}, + {1.,1.,0.}, + {1.,1.,1.}, + {2.,0.,0.} + }; + std::vector<ComplexField> phases(momenta.size(),&grid); + ComplexField coor(&grid); + Complex Ci(0.0,1.0); + for (unsigned int j = 0; j < momenta.size(); ++j) + { + phases[j] = Zero(); + for(unsigned int mu = 0; mu < momenta[j].size(); mu++) + { + LatticeCoordinate(coor, mu); + phases[j] = phases[j] + momenta[j][mu]/GridDefaultLatt()[mu]*coor; + } + phases[j] = exp((Real)(2*M_PI)*Ci*phases[j]); + } + + Eigen::Tensor<ComplexD,5, Eigen::RowMajor> mf;//(momenta.size(),12,GridDefaultLatt()[3],10,10); + + //execute meson field routine + //A2Autils<WilsonImplR>::MesonField(mf,phi,phi,Gmu,phases,3); + + // epilogue + std::cout << GridLogMessage << "Grid is finalizing now" << std::endl; + Grid_finalize(); + + return EXIT_SUCCESS; +} From 67c3c16fe5762d8dce988ad39d8a391457de5fa9 Mon Sep 17 00:00:00 2001 From: Felix Erben <felix.erben@ed.ac.uk> Date: Mon, 5 Jul 2021 14:41:52 +0100 Subject: [PATCH 177/399] working test --- tests/Test_meson_field.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/Test_meson_field.cc b/tests/Test_meson_field.cc index b108ee57..97d3106e 100644 --- a/tests/Test_meson_field.cc +++ b/tests/Test_meson_field.cc @@ -45,11 +45,18 @@ int main(int argc, char *argv[]) GridCartesian grid(latt_size,simd_layout,mpi_layout); // MesonField lhs and rhs vectors - int mfDim = 10; + int mfDim = 3; std::vector<FermionField> phi(mfDim,&grid); std::vector<FermionField> rho(mfDim,&grid); + std::vector<int> seeds({1,2,3,4}); + GridParallelRNG pRNG(&grid); + pRNG.SeedFixedIntegers(seeds); + for (unsigned int i = 0; i < mfDim; ++i){ + random(pRNG,phi[i]); + random(pRNG,rho[i]); //ideally only nonzero on t=0 + } // Gamma matrices used in the contraction - Gamma::Algebra Gmu [] = { + std::vector<Gamma::Algebra> Gmu = { Gamma::Algebra::GammaX, Gamma::Algebra::GammaY, Gamma::Algebra::GammaZ, @@ -78,10 +85,12 @@ int main(int argc, char *argv[]) phases[j] = exp((Real)(2*M_PI)*Ci*phases[j]); } - Eigen::Tensor<ComplexD,5, Eigen::RowMajor> mf;//(momenta.size(),12,GridDefaultLatt()[3],10,10); + Eigen::Tensor<ComplexD,5, Eigen::RowMajor> mf(momenta.size(),Gmu.size(),GridDefaultLatt()[3],mfDim,mfDim); //execute meson field routine - //A2Autils<WilsonImplR>::MesonField(mf,phi,phi,Gmu,phases,3); + A2Autils<WilsonImplR>::MesonField(mf,&phi[0],&phi[0],Gmu,phases,3); + + std::cout << mf << std::endl; // epilogue std::cout << GridLogMessage << "Grid is finalizing now" << std::endl; From fcc4374d7b5fbc6f9933df037b15aa35f15ceffe Mon Sep 17 00:00:00 2001 From: Felix Erben <felix.erben@ed.ac.uk> Date: Mon, 5 Jul 2021 14:52:00 +0100 Subject: [PATCH 178/399] i/o done --- tests/Test_meson_field.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Test_meson_field.cc b/tests/Test_meson_field.cc index 97d3106e..61b5b8be 100644 --- a/tests/Test_meson_field.cc +++ b/tests/Test_meson_field.cc @@ -92,6 +92,21 @@ int main(int argc, char *argv[]) std::cout << mf << std::endl; + std::string FileName = "Meson_Fields"; +#ifdef HAVE_HDF5 + using Default_Reader = Grid::Hdf5Reader; + using Default_Writer = Grid::Hdf5Writer; + FileName.append(".h5"); +#else + using Default_Reader = Grid::BinaryReader; + using Default_Writer = Grid::BinaryWriter; + FileName.append(".bin"); +#endif + + + Default_Writer w(FileName); + write(w,"phi_phi",mf); + // epilogue std::cout << GridLogMessage << "Grid is finalizing now" << std::endl; Grid_finalize(); From d75a66a3e6321fd85497222b1b396fb90bb63480 Mon Sep 17 00:00:00 2001 From: Felix Erben <felix.erben@ed.ac.uk> Date: Tue, 6 Jul 2021 11:42:36 +0100 Subject: [PATCH 179/399] test done --- tests/Test_meson_field.cc | 73 ++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/tests/Test_meson_field.cc b/tests/Test_meson_field.cc index 61b5b8be..25d908d7 100644 --- a/tests/Test_meson_field.cc +++ b/tests/Test_meson_field.cc @@ -30,6 +30,9 @@ See the full license in the file "LICENSE" in the top level distribution directo using namespace Grid; +const int TSRC = 0; //timeslice where rho is nonzero +const int VDIM = 5; //length of each vector + typedef typename DomainWallFermionR::ComplexField ComplexField; typedef typename DomainWallFermionR::FermionField FermionField; @@ -38,23 +41,31 @@ int main(int argc, char *argv[]) // initialization Grid_init(&argc, &argv); std::cout << GridLogMessage << "Grid initialized" << std::endl; - + + // Lattice and rng setup Coordinate latt_size = GridDefaultLatt(); Coordinate simd_layout = GridDefaultSimd(4, vComplex::Nsimd()); Coordinate mpi_layout = GridDefaultMpi(); GridCartesian grid(latt_size,simd_layout,mpi_layout); - - // MesonField lhs and rhs vectors - int mfDim = 3; - std::vector<FermionField> phi(mfDim,&grid); - std::vector<FermionField> rho(mfDim,&grid); + int Nt = GridDefaultLatt()[Tp]; + Lattice<iScalar<vInteger>> t(&grid); + LatticeCoordinate(t, Tp); std::vector<int> seeds({1,2,3,4}); GridParallelRNG pRNG(&grid); pRNG.SeedFixedIntegers(seeds); - for (unsigned int i = 0; i < mfDim; ++i){ + + // MesonField lhs and rhs vectors + std::vector<FermionField> phi(VDIM,&grid); + std::vector<FermionField> rho(VDIM,&grid); + FermionField rho_tmp(&grid); + std::cout << GridLogMessage << "Initialising random meson fields" << std::endl; + for (unsigned int i = 0; i < VDIM; ++i){ random(pRNG,phi[i]); - random(pRNG,rho[i]); //ideally only nonzero on t=0 + random(pRNG,rho_tmp); //ideally only nonzero on t=0 + rho[i] = where((t==TSRC), rho_tmp, 0.*rho_tmp); //ideally only nonzero on t=0 } + std::cout << GridLogMessage << "Meson fields initialised, rho non-zero only for t = " << TSRC << std::endl; + // Gamma matrices used in the contraction std::vector<Gamma::Algebra> Gmu = { Gamma::Algebra::GammaX, @@ -71,6 +82,10 @@ int main(int argc, char *argv[]) {1.,1.,1.}, {2.,0.,0.} }; + + std::cout << GridLogMessage << "Meson fields will be created for " << Gmu.size() << " Gamma matrices and " << momenta.size() << " momenta." << std::endl; + + std::cout << GridLogMessage << "Computing complex phases" << std::endl; std::vector<ComplexField> phases(momenta.size(),&grid); ComplexField coor(&grid); Complex Ci(0.0,1.0); @@ -84,28 +99,46 @@ int main(int argc, char *argv[]) } phases[j] = exp((Real)(2*M_PI)*Ci*phases[j]); } + std::cout << GridLogMessage << "Computing complex phases done." << std::endl; - Eigen::Tensor<ComplexD,5, Eigen::RowMajor> mf(momenta.size(),Gmu.size(),GridDefaultLatt()[3],mfDim,mfDim); + Eigen::Tensor<ComplexD,5, Eigen::RowMajor> Mpp(momenta.size(),Gmu.size(),Nt,VDIM,VDIM); + Eigen::Tensor<ComplexD,5, Eigen::RowMajor> Mpr(momenta.size(),Gmu.size(),Nt,VDIM,VDIM); + Eigen::Tensor<ComplexD,5, Eigen::RowMajor> Mrr(momenta.size(),Gmu.size(),Nt,VDIM,VDIM); + + // timer + double start,stop; //execute meson field routine - A2Autils<WilsonImplR>::MesonField(mf,&phi[0],&phi[0],Gmu,phases,3); - - std::cout << mf << std::endl; + start = usecond(); + A2Autils<WilsonImplR>::MesonField(Mpp,&phi[0],&phi[0],Gmu,phases,Tp); + stop = usecond(); + std::cout << GridLogMessage << "M(phi,phi) created, execution time " << stop-start << " us" << std::endl; + start = usecond(); + /* Ideally, for this meson field we could pass TSRC (even better a list of timeslices) + * to the routine so that all the compnents which are predictably equal to zero are not computed. */ + A2Autils<WilsonImplR>::MesonField(Mpr,&phi[0],&rho[0],Gmu,phases,Tp); + stop = usecond(); + std::cout << GridLogMessage << "M(phi,rho) created, execution time " << stop-start << " us" << std::endl; + start = usecond(); + A2Autils<WilsonImplR>::MesonField(Mrr,&rho[0],&rho[0],Gmu,phases,Tp); + stop = usecond(); + std::cout << GridLogMessage << "M(rho,rho) created, execution time " << stop-start << " us" << std::endl; std::string FileName = "Meson_Fields"; #ifdef HAVE_HDF5 - using Default_Reader = Grid::Hdf5Reader; - using Default_Writer = Grid::Hdf5Writer; - FileName.append(".h5"); + using Default_Reader = Grid::Hdf5Reader; + using Default_Writer = Grid::Hdf5Writer; + FileName.append(".h5"); #else - using Default_Reader = Grid::BinaryReader; - using Default_Writer = Grid::BinaryWriter; - FileName.append(".bin"); + using Default_Reader = Grid::BinaryReader; + using Default_Writer = Grid::BinaryWriter; + FileName.append(".bin"); #endif - Default_Writer w(FileName); - write(w,"phi_phi",mf); + write(w,"phi_phi",Mpp); + write(w,"phi_rho",Mpr); + write(w,"rho_rho",Mrr); // epilogue std::cout << GridLogMessage << "Grid is finalizing now" << std::endl; From 770680669d81ecbc657902ec2d08253dbc8f98d0 Mon Sep 17 00:00:00 2001 From: Andrew Yong <ayzn95@gmail.com> Date: Wed, 4 Aug 2021 09:21:59 +0100 Subject: [PATCH 180/399] Whitespace removal. --- .../fermion/implementation/CayleyFermion5DImplementation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index 1b363846..e98f41b3 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -934,7 +934,7 @@ void CayleyFermion5D<Impl>::SeqConservedCurrent(PropagatorField &q_in, tmp = tmp *ph; tmp = Cshift(tmp,mu,-1); Impl::multLinkField(Utmp,this->Umu,tmp,mu+Nd); // Adjoint link - tmp = -G_s[s]*( Utmp + gmu*Utmp ); + tmp = -G_s[s]*( Utmp + gmu*Utmp ); // Mask the time if (tmax == LLt - 1 && tshift == 1){ // quick fix to include timeslice 0 if tmax + tshift is over the last timeslice unsigned int t0 = 0; From 80ac2a73ca4ae28115a19e8b8345e98ff5f16dd8 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 5 Aug 2021 18:33:20 -0400 Subject: [PATCH 181/399] Check is wrong (HtoD / DtoH) --- Grid/communicator/SharedMemoryMPI.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index caa03a60..d7b41cee 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -844,7 +844,7 @@ void SharedMemory::SetCommunicator(Grid_MPI_Comm comm) } #endif - SharedMemoryTest(); + //SharedMemoryTest(); } ////////////////////////////////////////////////////////////////// // On node barrier From fe5aaf7677c11e917af03bd0cbbc14a29b67bd2d Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 9 Aug 2021 04:06:30 -0700 Subject: [PATCH 182/399] Make comms benchmark same as Benchmark_comms_host_device --- benchmarks/Benchmark_ITT.cc | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index 032535b3..7311dfc4 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -133,15 +133,15 @@ public: std::vector<HalfSpinColourVectorD *> xbuf(8); std::vector<HalfSpinColourVectorD *> rbuf(8); - Grid.ShmBufferFreeAll(); + //Grid.ShmBufferFreeAll(); + uint64_t bytes=lat*lat*lat*Ls*sizeof(HalfSpinColourVectorD); for(int d=0;d<8;d++){ - xbuf[d] = (HalfSpinColourVectorD *)Grid.ShmBufferMalloc(lat*lat*lat*Ls*sizeof(HalfSpinColourVectorD)); - rbuf[d] = (HalfSpinColourVectorD *)Grid.ShmBufferMalloc(lat*lat*lat*Ls*sizeof(HalfSpinColourVectorD)); + xbuf[d] = (HalfSpinColourVectorD *)acceleratorAllocDevice(bytes); + rbuf[d] = (HalfSpinColourVectorD *)acceleratorAllocDevice(bytes); // bzero((void *)xbuf[d],lat*lat*lat*Ls*sizeof(HalfSpinColourVectorD)); // bzero((void *)rbuf[d],lat*lat*lat*Ls*sizeof(HalfSpinColourVectorD)); } - int bytes=lat*lat*lat*Ls*sizeof(HalfSpinColourVectorD); int ncomm; double dbytes; std::vector<double> times(Nloop); @@ -152,7 +152,7 @@ public: dbytes=0; ncomm=0; - thread_for(dir,8,{ + for(int dir=0;dir<8;dir++) { double tbytes; int mu =dir % 4; @@ -168,15 +168,16 @@ public: int comm_proc = mpi_layout[mu]-1; Grid.ShiftedRanks(mu,comm_proc,xmit_to_rank,recv_from_rank); } - tbytes= Grid.StencilSendToRecvFrom((void *)&xbuf[dir][0], xmit_to_rank, - (void *)&rbuf[dir][0], recv_from_rank, - bytes,dir); + Grid.SendToRecvFrom((void *)&xbuf[dir][0], xmit_to_rank, + (void *)&rbuf[dir][0], recv_from_rank, + bytes); + tbytes = bytes; thread_critical { ncomm++; dbytes+=tbytes; } } - }); + }; Grid.Barrier(); double stop=usecond(); t_time[i] = stop-start; // microseconds @@ -196,8 +197,12 @@ public: << "\t\t"<< bidibytes/timestat.mean<< " " << bidibytes*timestat.err/(timestat.mean*timestat.mean) << " " << bidibytes/timestat.max << " " << bidibytes/timestat.min << std::endl; - } - } + for(int d=0;d<8;d++){ + acceleratorFreeDevice(xbuf[d]); + acceleratorFreeDevice(rbuf[d]); + } + } + } return; } @@ -281,7 +286,6 @@ public: uint64_t lmax=32; -#define NLOOP (1000*lmax*lmax*lmax*lmax/lat/lat/lat/lat) GridSerialRNG sRNG; sRNG.SeedFixedIntegers(std::vector<int>({45,12,81,9})); for(int lat=8;lat<=lmax;lat+=8){ From 75030637cca1f60173ca760b09dd37852a88299d Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 10 Aug 2021 05:16:30 -0700 Subject: [PATCH 183/399] Improved comms benchmark, same as benchmark_comms_host_device --- benchmarks/Benchmark_ITT.cc | 73 ++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index 7311dfc4..81d1acd4 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -144,23 +144,19 @@ public: int ncomm; double dbytes; - std::vector<double> times(Nloop); - for(int i=0;i<Nloop;i++){ - double start=usecond(); + for(int dir=0;dir<8;dir++) { + int mu =dir % 4; + if (mpi_layout[mu]>1 ) { - dbytes=0; - ncomm=0; + std::vector<double> times(Nloop); + for(int i=0;i<Nloop;i++){ - for(int dir=0;dir<8;dir++) { - - double tbytes; - int mu =dir % 4; - - if (mpi_layout[mu]>1 ) { - + dbytes=0; + double start=usecond(); int xmit_to_rank; int recv_from_rank; + if ( dir == mu ) { int comm_proc=1; Grid.ShiftedRanks(mu,comm_proc,xmit_to_rank,recv_from_rank); @@ -171,42 +167,35 @@ public: Grid.SendToRecvFrom((void *)&xbuf[dir][0], xmit_to_rank, (void *)&rbuf[dir][0], recv_from_rank, bytes); - tbytes = bytes; - thread_critical { - ncomm++; - dbytes+=tbytes; - } + dbytes+=bytes; + + double stop=usecond(); + t_time[i] = stop-start; // microseconds + } - }; - Grid.Barrier(); - double stop=usecond(); - t_time[i] = stop-start; // microseconds + timestat.statistics(t_time); + + dbytes=dbytes*ppn; + double xbytes = dbytes*0.5; + double bidibytes = dbytes; + + std::cout<<GridLogMessage << lat<<"\t"<<Ls<<"\t " + << bytes << " \t " + <<xbytes/timestat.mean<<" \t "<< xbytes*timestat.err/(timestat.mean*timestat.mean)<< " \t " + <<xbytes/timestat.max <<" "<< xbytes/timestat.min + << "\t\t"<< bidibytes/timestat.mean<< " " << bidibytes*timestat.err/(timestat.mean*timestat.mean) << " " + << bidibytes/timestat.max << " " << bidibytes/timestat.min << std::endl; + } } - - timestat.statistics(t_time); - - dbytes=dbytes*ppn; - double xbytes = dbytes*0.5; - // double rbytes = dbytes*0.5; - double bidibytes = dbytes; - - std::cout<<GridLogMessage << lat<<"\t"<<Ls<<"\t " - << bytes << " \t " - <<xbytes/timestat.mean<<" \t "<< xbytes*timestat.err/(timestat.mean*timestat.mean)<< " \t " - <<xbytes/timestat.max <<" "<< xbytes/timestat.min - << "\t\t"<< bidibytes/timestat.mean<< " " << bidibytes*timestat.err/(timestat.mean*timestat.mean) << " " - << bidibytes/timestat.max << " " << bidibytes/timestat.min << std::endl; - for(int d=0;d<8;d++){ acceleratorFreeDevice(xbuf[d]); acceleratorFreeDevice(rbuf[d]); } } - } - + } return; } - + static void Memory(void) @@ -449,7 +438,7 @@ public: // 1344= 3*(2*8+6)*2*8 + 8*3*2*2 + 3*4*2*8 // 1344 = Nc* (6+(Nc-1)*8)*2*Nd + Nd*Nc*2*2 + Nd*Nc*Ns*2 // double flops=(1344.0*volume)/2; -#if 1 +#if 0 double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + Nd*Nc*Ns + Nd*Nc*Ns*2; #else double fps = Nc* (6+(Nc-1)*8)*Ns*Nd + 2*Nd*Nc*Ns + 2*Nd*Nc*Ns*2; @@ -720,12 +709,12 @@ int main (int argc, char ** argv) if ( do_su4 ) { std::cout<<GridLogMessage << "=================================================================================="<<std::endl; - std::cout<<GridLogMessage << " Memory benchmark " <<std::endl; + std::cout<<GridLogMessage << " SU(4) benchmark " <<std::endl; std::cout<<GridLogMessage << "=================================================================================="<<std::endl; Benchmark::SU4(); } - if ( do_comms && (NN>1) ) { + if ( do_comms ) { std::cout<<GridLogMessage << "=================================================================================="<<std::endl; std::cout<<GridLogMessage << " Communications benchmark " <<std::endl; std::cout<<GridLogMessage << "=================================================================================="<<std::endl; From 50181f16e5cf0da654fda79423e6d9dd06f158c6 Mon Sep 17 00:00:00 2001 From: "peterx.a.boyle" <paboylex@ortce-ats4.jf.intel.com> Date: Tue, 10 Aug 2021 05:35:15 -0700 Subject: [PATCH 184/399] Level 0 IPC set up --- Grid/communicator/SharedMemoryMPI.cc | 56 +++++++++++++++++++++++++--- Grid/threads/Accelerator.cc | 5 ++- Grid/threads/Accelerator.h | 7 ++++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index caa03a60..554f338b 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -73,6 +73,7 @@ void GlobalSharedMemory::Init(Grid_MPI_Comm comm) WorldNodes = WorldSize/WorldShmSize; assert( (WorldNodes * WorldShmSize) == WorldSize ); + // FIXME: Check all WorldShmSize are the same ? ///////////////////////////////////////////////////////////////////// @@ -451,7 +452,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) //////////////////////////////////////////////////////////////////////////////////////////// #if defined(GRID_CUDA) ||defined(GRID_HIP) || defined(GRID_SYCL) -#if defined(GRID_SYCL) +//if defined(GRID_SYCL) +#if 0 void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) { void * ShmCommBuf ; @@ -488,7 +490,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) } #endif -#if defined(GRID_CUDA) ||defined(GRID_HIP) +#if defined(GRID_CUDA) ||defined(GRID_HIP) ||defined(GRID_SYCL) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) { void * ShmCommBuf ; @@ -511,8 +513,16 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Each MPI rank should allocate our own buffer /////////////////////////////////////////////////////////////////////////////////////////////////////////// + auto zeDevice = cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_device()); + auto zeContext= cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_context()); +#ifdef GRID_SYCL_LEVEL_ZERO_IPC + ze_device_mem_alloc_desc_t zeDesc = {}; + zeMemAllocDevice(zeContext,&zeDesc,bytes,2*1024*1024,zeDevice,&ShmCommBuf); + std::cout << WorldRank << header " SharedMemoryMPI.cc zeMemAllocDevice "<< bytes + << "bytes at "<< std::hex<< ShmCommBuf <<std::dec<<" for comms buffers " <<std::endl; +#else ShmCommBuf = acceleratorAllocDevice(bytes); - +#endif if (ShmCommBuf == (void *)NULL ) { std::cerr << " SharedMemoryMPI.cc acceleratorAllocDevice failed NULL pointer for " << bytes<<" bytes " << std::endl; exit(EXIT_FAILURE); @@ -522,8 +532,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) std::cout << WorldRank << header " SharedMemoryMPI.cc acceleratorAllocDevice "<< bytes << "bytes at "<< std::hex<< ShmCommBuf <<std::dec<<" for comms buffers " <<std::endl; } - SharedMemoryZero(ShmCommBuf,bytes); - + // SharedMemoryZero(ShmCommBuf,bytes); + std::cout<< "Setting up IPC"<<std::endl; /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Loop over ranks/gpu's on our node /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -533,6 +543,23 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) ////////////////////////////////////////////////// // If it is me, pass around the IPC access key ////////////////////////////////////////////////// +#ifdef GRID_SYCL_LEVEL_ZERO_IPC + ze_ipc_mem_handle_t handle; + if ( r==WorldShmRank ) { + auto err = zeMemGetIpcHandle(zeContext,ShmCommBuf,&handle); + if ( err != ZE_RESULT_SUCCESS ) { + std::cerr << "SharedMemoryMPI.cc zeMemGetIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + exit(EXIT_FAILURE); + } else { + std::cerr << "SharedMemoryMPI.cc zeMemGetIpcHandle succeeded for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + } + std::cerr<<"Allocated IpcHandle rank "<<r<<" (hex) "; + for(int c=0;c<ZE_MAX_IPC_HANDLE_SIZE;c++){ + std::cerr<<std::hex<<(uint32_t)((uint8_t)handle.data[c])<<std::dec; + } + std::cerr<<std::endl; + } +#endif #ifdef GRID_CUDA cudaIpcMemHandle_t handle; if ( r==WorldShmRank ) { @@ -569,6 +596,25 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) // If I am not the source, overwrite thisBuf with remote buffer /////////////////////////////////////////////////////////////// void * thisBuf = ShmCommBuf; +#ifdef GRID_SYCL_LEVEL_ZERO_IPC + if ( r!=WorldShmRank ) { + thisBuf = nullptr; + std::cerr<<"Using IpcHandle rank "<<r<<" "; + for(int c=0;c<ZE_MAX_IPC_HANDLE_SIZE;c++){ + std::cerr<<std::hex<<(uint32_t)((uint8_t)handle.data[c])<<std::dec; + } + std::cerr<<std::endl; + auto err = zeMemOpenIpcHandle(zeContext,zeDevice,handle,0,&thisBuf); + if ( err != ZE_RESULT_SUCCESS ) { + std::cerr << "SharedMemoryMPI.cc "<<zeContext<<" "<<zeDevice<<std::endl; + std::cerr << "SharedMemoryMPI.cc zeMemOpenIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + exit(EXIT_FAILURE); + } else { + std::cerr << "SharedMemoryMPI.cc zeMemOpenIpcHandle succeeded for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + } + assert(thisBuf!=nullptr); + } +#endif #ifdef GRID_CUDA if ( r!=WorldShmRank ) { auto err = cudaIpcOpenMemHandle(&thisBuf,handle,cudaIpcMemLazyEnablePeerAccess); diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 9d9d851c..9c40f538 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -171,7 +171,6 @@ void acceleratorInit(void) #ifdef GRID_SYCL cl::sycl::queue *theGridAccelerator; - void acceleratorInit(void) { int nDevices = 1; @@ -179,6 +178,10 @@ void acceleratorInit(void) cl::sycl::device selectedDevice { selector }; theGridAccelerator = new sycl::queue (selectedDevice); +#ifdef GRID_SYCL_LEVEL_ZERO_IPC + zeInit(0); +#endif + char * localRankStr = NULL; int rank = 0, world_rank=0; #define ENV_LOCAL_RANK_OMPI "OMPI_COMM_WORLD_LOCAL_RANK" diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index c0af1019..2c9d15ba 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -233,6 +233,13 @@ inline int acceleratorIsCommunicable(void *ptr) NAMESPACE_END(Grid); #include <CL/sycl.hpp> #include <CL/sycl/usm.hpp> + +#define GRID_SYCL_LEVEL_ZERO_IPC + +#ifdef GRID_SYCL_LEVEL_ZERO_IPC +#include <level_zero/ze_api.h> +#include <CL/sycl/backend/level_zero.hpp> +#endif NAMESPACE_BEGIN(Grid); extern cl::sycl::queue *theGridAccelerator; From 417dbfa257aecb6e46d6f63377da1e274e146160 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 10 Aug 2021 08:55:35 -0700 Subject: [PATCH 185/399] Fix --- Grid/communicator/SharedMemoryMPI.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index d230b2a5..11788744 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -513,9 +513,9 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Each MPI rank should allocate our own buffer /////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef GRID_SYCL_LEVEL_ZERO_IPC auto zeDevice = cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_device()); auto zeContext= cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_context()); -#ifdef GRID_SYCL_LEVEL_ZERO_IPC ze_device_mem_alloc_desc_t zeDesc = {}; zeMemAllocDevice(zeContext,&zeDesc,bytes,2*1024*1024,zeDevice,&ShmCommBuf); std::cout << WorldRank << header " SharedMemoryMPI.cc zeMemAllocDevice "<< bytes From 5d29e175d82edc92af07ca9b708ec7f187fc006d Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 10 Aug 2021 18:25:43 +0100 Subject: [PATCH 186/399] Typo fix --- Grid/communicator/SharedMemoryMPI.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index d230b2a5..11788744 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -513,9 +513,9 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Each MPI rank should allocate our own buffer /////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef GRID_SYCL_LEVEL_ZERO_IPC auto zeDevice = cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_device()); auto zeContext= cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_context()); -#ifdef GRID_SYCL_LEVEL_ZERO_IPC ze_device_mem_alloc_desc_t zeDesc = {}; zeMemAllocDevice(zeContext,&zeDesc,bytes,2*1024*1024,zeDevice,&ShmCommBuf); std::cout << WorldRank << header " SharedMemoryMPI.cc zeMemAllocDevice "<< bytes From ffbdd91e0e2770f0e12f9296e5e9991e52799f4d Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Fri, 20 Aug 2021 01:15:00 +0100 Subject: [PATCH 187/399] Apple happiness --- Grid/util/Init.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Grid/util/Init.cc b/Grid/util/Init.cc index bfbc464d..3b661524 100644 --- a/Grid/util/Init.cc +++ b/Grid/util/Init.cc @@ -52,10 +52,12 @@ Author: paboyle <paboyle@ph.ed.ac.uk> #include <fenv.h> -#ifdef __APPLE__ static int +#ifdef __APPLE__ feenableexcept (unsigned int excepts) { +#if 0 + // Fails on Apple M1 static fenv_t fenv; unsigned int new_excepts = excepts & FE_ALL_EXCEPT; unsigned int old_excepts; // previous masks @@ -70,6 +72,7 @@ feenableexcept (unsigned int excepts) iold_excepts = (int) old_excepts; return ( fesetenv (&fenv) ? -1 : iold_excepts ); +#endif } #endif From 7163b31a26bba90241480fe444a3584cd66f5893 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Fri, 20 Aug 2021 01:15:23 +0100 Subject: [PATCH 188/399] Examples --- scripts/filelist | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/filelist b/scripts/filelist index 8e29ba88..c3f0cf3f 100755 --- a/scripts/filelist +++ b/scripts/filelist @@ -82,3 +82,18 @@ for f in $TESTS; do echo >> Make.inc done cd .. + +# examples Make.inc +cd $home/examples/ +echo> Make.inc +TESTS=`ls *.cc` +TESTLIST=`echo ${TESTS} | sed s/.cc//g ` +echo bin_PROGRAMS = ${TESTLIST} > Make.inc +echo >> Make.inc +for f in $TESTS; do + BNAME=`basename $f .cc` + echo ${BNAME}_SOURCES=$f >> Make.inc + echo ${BNAME}_LDADD='$(top_builddir)/Grid/libGrid.a'>> Make.inc + echo >> Make.inc +done +cd .. From 40098424c75cb693cf92ebafa758efac8b25053a Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sun, 22 Aug 2021 14:17:12 +0100 Subject: [PATCH 189/399] Examples --- Makefile.am | 2 +- configure.ac | 1 + examples/Example_Laplacian.cc | 395 ++++++++++++++++++++++++++++++++++ examples/Makefile.am | 6 + 4 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 examples/Example_Laplacian.cc create mode 100644 examples/Makefile.am diff --git a/Makefile.am b/Makefile.am index 33b25026..d2a1a326 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ # additional include paths necessary to compile the C++ library -SUBDIRS = Grid HMC benchmarks tests +SUBDIRS = Grid HMC benchmarks tests examples include $(top_srcdir)/doxygen.inc diff --git a/configure.ac b/configure.ac index 4e5e33c8..721d890e 100644 --- a/configure.ac +++ b/configure.ac @@ -815,6 +815,7 @@ AC_CONFIG_FILES(tests/smearing/Makefile) AC_CONFIG_FILES(tests/qdpxx/Makefile) AC_CONFIG_FILES(tests/testu01/Makefile) AC_CONFIG_FILES(benchmarks/Makefile) +AC_CONFIG_FILES(examples/Makefile) AC_OUTPUT echo "" diff --git a/examples/Example_Laplacian.cc b/examples/Example_Laplacian.cc new file mode 100644 index 00000000..f6bbf7cf --- /dev/null +++ b/examples/Example_Laplacian.cc @@ -0,0 +1,395 @@ +#include <Grid/Grid.h> +using namespace Grid; + +/* +///////////////////////////////////////////////////////////////////////////////////////////// +// Grid/algorithms/SparseMatrix.h: Interface defining what I expect of a general sparse matrix, such as a Fermion action +///////////////////////////////////////////////////////////////////////////////////////////// +template<class Field> class SparseMatrixBase { +public: + virtual GridBase *Grid(void) =0; + + virtual void M (const Field &in, Field &out)=0; + virtual void Mdag (const Field &in, Field &out)=0; + virtual void MdagM(const Field &in, Field &out) { + Field tmp (in.Grid()); + M(in,tmp); + Mdag(tmp,out); + } + virtual void Mdiag (const Field &in, Field &out)=0; + virtual void Mdir (const Field &in, Field &out,int dir, int disp)=0; + virtual void MdirAll (const Field &in, std::vector<Field> &out)=0; +}; +*/ + +const std::vector<int> directions ({Xdir,Ydir,Zdir,Xdir,Ydir,Zdir}); +const std::vector<int> displacements({1,1,1,-1,-1,-1}); + +template<class Field> class FreeLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + GridBase *grid; + FreeLaplacianCshift(GridBase *_grid) + { + grid=_grid; + }; + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out = Zero(); + for(int mu=0;mu<Nd-1;mu++) { + out = out + Cshift(in,mu,1) + Cshift(in,mu,-1) - 2.0 * in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + +template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + GridBase *grid; + GaugeField U; + + CovariantLaplacianCshift(GaugeField &_U) : + grid(_U.Grid()), + U(_U) { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out=Zero(); + for(int mu=0;mu<Nd-1;mu++) { + GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent + out = out + Gimpl::CovShiftForward(Umu,mu,in); + out = out + Gimpl::CovShiftBackward(Umu,mu,in); + out = out - 2.0*in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + + +#define LEG_LOAD(Dir) \ + SE = st.GetEntry(ptype, Dir, ss); \ + if (SE->_is_local ) { \ + int perm= SE->_permute; \ + chi = coalescedReadPermute(in[SE->_offset],ptype,perm,lane); \ + } else { \ + chi = coalescedRead(buf[SE->_offset],lane); \ + } \ + acceleratorSynchronise(); + +template<class Field> class FreeLaplacianStencil : public SparseMatrixBase<Field> +{ +public: + typedef typename Field::vector_object siteObject; + typedef CartesianStencil<siteObject, siteObject, int> StencilImpl; + + GridBase *grid; + StencilImpl Stencil; + SimpleCompressor<siteObject> Compressor; + + FreeLaplacianStencil(GridBase *_grid) + : Stencil (_grid,6,Even,directions,displacements,0), grid(_grid) + { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &_in, Field &_out) + { + + /////////////////////////////////////////////// + // Halo exchange for this geometry of stencil + /////////////////////////////////////////////// + Stencil.HaloExchange(_in, Compressor); + + /////////////////////////////////// + // Arithmetic expressions + /////////////////////////////////// + StencilEntry *SE; + + // Views; device friendly/accessible pointers + auto st = Stencil.View(AcceleratorRead); + auto buf = st.CommBuf(); + autoView( in , _in , AcceleratorRead); + autoView( out , _out , AcceleratorWrite); + + typedef typename Field::vector_object vobj; + typedef decltype(coalescedRead(in[0])) calcObj; + + const int Nsimd = vobj::Nsimd(); + const uint64_t NN = grid->oSites(); + const int lane=acceleratorSIMTlane(Nsimd); + + accelerator_for( ss, NN, Nsimd, { + + const int lane=acceleratorSIMTlane(Nsimd); + + calcObj chi; + calcObj res; + int ptype; + + res = coalescedRead(in[ss])*(-6.0); + LEG_LOAD(0); res = res + chi; + LEG_LOAD(1); res = res + chi; + LEG_LOAD(2); res = res + chi; + LEG_LOAD(3); res = res + chi; + LEG_LOAD(4); res = res + chi; + LEG_LOAD(5); res = res + chi; + + coalescedWrite(out[ss], res,lane); + + }); + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + +template<class Gimpl,class Field> class CovariantLaplacianStencil : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + typedef typename Field::vector_object siteObject; + + template <typename vtype> using iImplDoubledGaugeField = iVector<iScalar<iMatrix<vtype, Nc> >, Nds>; + typedef iImplDoubledGaugeField<Simd> SiteDoubledGaugeField; + typedef Lattice<SiteDoubledGaugeField> DoubledGaugeField; + + typedef CartesianStencil<siteObject, siteObject, int> StencilImpl; + + GridBase *grid; + StencilImpl Stencil; + SimpleCompressor<siteObject> Compressor; + DoubledGaugeField Uds; + CovariantLaplacianStencil(GaugeField &Umu) + : + grid(Umu.Grid()), + Stencil (grid,6,Even,directions,displacements,0), + Uds(grid) + { + for (int mu = 0; mu < Nd; mu++) { + auto U = PeekIndex<LorentzIndex>(Umu, mu); + PokeIndex<LorentzIndex>(Uds, U, mu ); + U = adj(Cshift(U, mu, -1)); + PokeIndex<LorentzIndex>(Uds, U, mu + 4); + } + }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &_in, Field &_out) + { + /////////////////////////////////////////////// + // Halo exchange for this geometry of stencil + /////////////////////////////////////////////// + Stencil.HaloExchange(_in, Compressor); + + /////////////////////////////////// + // Arithmetic expressions + /////////////////////////////////// + auto st = Stencil.View(AcceleratorRead); + auto buf = st.CommBuf(); + StencilEntry *SE; + + autoView( in , _in , AcceleratorRead); + autoView( out , _out , AcceleratorWrite); + autoView( U , Uds , AcceleratorRead); + + typedef typename Field::vector_object vobj; + typedef decltype(coalescedRead(in[0])) calcObj; + typedef decltype(coalescedRead(U[0](0))) calcLink; + + const int Nsimd = vobj::Nsimd(); + const uint64_t NN = grid->oSites(); + const int lane=acceleratorSIMTlane(Nsimd); + accelerator_for( ss, NN, Nsimd, { + + const int lane=acceleratorSIMTlane(Nsimd); + + calcObj chi; + calcObj res; + calcObj Uchi; + calcLink UU; + int ptype; + + res = coalescedRead(in[ss])*(-6.0); + +#define LEG_LOAD_MULT(leg,polarisation) \ + UU = coalescedRead(U[ss](polarisation)); \ + LEG_LOAD(leg); \ + mult(&Uchi(), &UU, &chi()); \ + res = res + Uchi; + + LEG_LOAD_MULT(0,Xp); + LEG_LOAD_MULT(1,Yp); + LEG_LOAD_MULT(2,Zp); + LEG_LOAD_MULT(3,Xm); + LEG_LOAD_MULT(4,Ym); + LEG_LOAD_MULT(5,Zm); + + coalescedWrite(out[ss], res,lane); + }); + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + +#undef LEG_LOAD_MULT +#undef LEG_LOAD + +int main(int argc, char ** argv) +{ + Grid_init(&argc, &argv); + + typedef LatticeColourVector Field; + + auto latt_size = GridDefaultLatt(); + auto simd_layout = GridDefaultSimd(Nd,vComplex::Nsimd()); + auto mpi_layout = GridDefaultMpi(); + + GridCartesian Grid(latt_size,simd_layout,mpi_layout); + GridParallelRNG RNG(&Grid); RNG.SeedFixedIntegers(std::vector<int>({45,12,81,9})); + + FreeLaplacianCshift<Field> FLcs(&Grid); + FreeLaplacianStencil<Field> FLst(&Grid); + + LatticeGaugeField U(&Grid); + + SU<Nc>::ColdConfiguration(RNG,U); + + std::cout << " Gauge field has norm " <<norm2(U)<<std::endl; + + CovariantLaplacianCshift <PeriodicGimplR,Field> CLcs(U); + CovariantLaplacianStencil<PeriodicGimplR,Field> CLst(U); + + Field in(&Grid); gaussian(RNG,in); + Field out_FLcs(&Grid); + Field out_FLst(&Grid); + Field out_CLcs(&Grid); + Field out_CLst(&Grid); + Field diff(&Grid); + + //////////////////////////////////////////////////////// + // First test: in free field these should all agree + //////////////////////////////////////////////////////// + FLcs.M(in,out_FLcs); + FLst.M(in,out_FLst); + CLcs.M(in,out_CLcs); + CLst.M(in,out_CLst); + + std:: cout << "******************************************************************" <<std::endl; + std:: cout << " Test A: consistency of four different Laplacian implementations " <<std::endl; + std:: cout << "******************************************************************" <<std::endl; + std:: cout << " Input test vector " <<norm2(in)<<std::endl; + std:: cout << "--------------------------------------------------------" <<std::endl; + std:: cout << " Free cshift output vector " <<norm2(out_FLcs)<<std::endl; + std:: cout << " Free stencil output vector " <<norm2(out_FLst)<<std::endl; + std:: cout << " Cov cshift output vector " <<norm2(out_CLcs)<<std::endl; + std:: cout << " Cov stencil output vector " <<norm2(out_CLst)<<std::endl; + std:: cout << "--------------------------------------------------------" <<std::endl; + + diff = out_FLcs - out_FLst; + std:: cout << " Difference between free Cshift Laplacian and free Stencil Laplacian = " <<norm2(diff)<<std::endl; + + diff = out_FLcs - out_CLcs; + std:: cout << " Difference between free Cshift Laplacian and covariant Cshift Laplacian = " <<norm2(diff)<<std::endl; + + diff = out_FLcs - out_CLst; + std:: cout << " Difference between free Cshift Laplacian and covariant Stencil Laplacian = " <<norm2(diff)<<std::endl; + std:: cout << "--------------------------------------------------------" <<std::endl; + + + std:: cout << "******************************************************************" <<std::endl; + std:: cout << " Test B: gauge covariance " <<std::endl; + std:: cout << "******************************************************************" <<std::endl; + + LatticeGaugeField U_GT(&Grid); // Gauge transformed field + LatticeColourMatrix g(&Grid); // local Gauge xform matrix + + U_GT = U; + // Make a random xform to teh gauge field + SU<Nc>::RandomGaugeTransform(RNG,U_GT,g); // Unit gauge + + Field in_GT(&Grid); + Field out_GT(&Grid); + + Field out_CLcs_GT(&Grid); + Field out_CLst_GT(&Grid); + + CovariantLaplacianCshift <PeriodicGimplR,Field> CLcs_GT(U_GT); + CovariantLaplacianStencil<PeriodicGimplR,Field> CLst_GT(U_GT); + + in_GT = g*in; + out_GT = g*out_FLcs; + + // Check M^GT_xy in_GT = g(x) M_xy g^dag(y) g(y) in = g(x) out(x) + CLcs_GT.M(in_GT,out_CLcs_GT); + CLst_GT.M(in_GT,out_CLst_GT); + + diff = out_CLcs_GT - out_GT; + std:: cout << " Difference between Gauge xformed result and covariant Cshift Laplacian in xformed gauge = " <<norm2(diff)<<std::endl; + + diff = out_CLst_GT - out_GT; + std:: cout << " Difference between Gauge xformed result and covariant Stencil Laplacian in xformed gauge = " <<norm2(diff)<<std::endl; + std:: cout << "--------------------------------------------------------" <<std::endl; + + + std:: cout << "******************************************************************" <<std::endl; + std:: cout << " Test C: compare in free Field to \"Feynman rule\" " <<std::endl; + std:: cout << "******************************************************************" <<std::endl; + + std::vector<int> dim_mask({1,1,1,0}); // 3d FFT + FFT theFFT(&Grid); + Field out(&Grid); + Field F_out(&Grid); + Field F_in(&Grid); + + // FFT the random input vector + theFFT.FFT_dim_mask(F_in,in,dim_mask,FFT::forward); + + // Convolution theorem: multiply by Fourier representation of (discrete) Laplacian to apply diff op + LatticeComplexD lap(&Grid); lap = Zero(); + LatticeComplexD kmu(&Grid); + ComplexD ci(0.0,1.0); + for(int mu=0;mu<3;mu++) { + + RealD TwoPiL = M_PI * 2.0/ latt_size[mu]; + + LatticeCoordinate(kmu,mu); + kmu = TwoPiL * kmu; + + // (e^ik_mu + e^-ik_mu - 2) = 2( cos kmu - 1) ~ 2 (1 - k_mu^2/2 -1 ) = - k_mu^2 + O(k^4) + lap = lap + 2.0*cos(kmu) - 2.0; + + } + F_out = lap * F_in; + + // Inverse FFT the result + theFFT.FFT_dim_mask(out,F_out,dim_mask,FFT::backward); + + std::cout<<"Fourier xformed (in) "<<norm2(F_in)<<std::endl; + std::cout<<"Fourier xformed Laplacian x (in) "<<norm2(F_out)<<std::endl; + + std::cout<<"Momentum space Laplacian application "<< norm2(out)<<std::endl; + std::cout<<"Stencil Laplacian application "<< norm2(out_CLcs)<<std::endl; + + diff = out_CLcs - out; + std::cout<<"diff "<< norm2(diff)<<std::endl; + + Grid_finalize(); +} diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 00000000..31cbdc86 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = . + +include Make.inc + + + From 6135ad530e4f5940f145ce2f9da98b4bbc24c77e Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sun, 22 Aug 2021 18:25:07 +0100 Subject: [PATCH 190/399] Extra examples / solutions --- examples/Example_Laplacian_inverter.cc | 122 ++++++++++++++++++++++++ examples/Example_Laplacian_smearing.cc | 127 +++++++++++++++++++++++++ examples/Example_Laplacian_solver.cc | 127 +++++++++++++++++++++++++ 3 files changed, 376 insertions(+) create mode 100644 examples/Example_Laplacian_inverter.cc create mode 100644 examples/Example_Laplacian_smearing.cc create mode 100644 examples/Example_Laplacian_solver.cc diff --git a/examples/Example_Laplacian_inverter.cc b/examples/Example_Laplacian_inverter.cc new file mode 100644 index 00000000..1235d2b8 --- /dev/null +++ b/examples/Example_Laplacian_inverter.cc @@ -0,0 +1,122 @@ +#include <Grid/Grid.h> +using namespace Grid; + +// Function used for Chebyshev smearing +// +Real MomentumSmearing(Real p2) +{ + return (1 - 4.0*p2) * exp(-p2/4); +} +Real DistillationSmearing(Real p2) +{ + if ( p2 > 0.5 ) return 0.0; + else return 1.0; +} + +// Flip sign to make prop to p^2, not -p^2 relative to last example +template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + GridBase *grid; + GaugeField U; + + CovariantLaplacianCshift(GaugeField &_U) : + grid(_U.Grid()), + U(_U) { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out=Zero(); + for(int mu=0;mu<Nd-1;mu++) { + GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent + out = out - Gimpl::CovShiftForward(Umu,mu,in); + out = out - Gimpl::CovShiftBackward(Umu,mu,in); + out = out + 2.0*in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + + + +int main(int argc, char ** argv) +{ + Grid_init(&argc, &argv); + + typedef LatticeColourVector Field; + + auto latt_size = GridDefaultLatt(); + auto simd_layout = GridDefaultSimd(Nd,vComplex::Nsimd()); + auto mpi_layout = GridDefaultMpi(); + + GridCartesian Grid(latt_size,simd_layout,mpi_layout); + GridParallelRNG RNG(&Grid); RNG.SeedFixedIntegers(std::vector<int>({45,12,81,9})); + + + LatticeGaugeField U(&Grid); + + SU<Nc>::ColdConfiguration(RNG,U); + + typedef CovariantLaplacianCshift <PeriodicGimplR,Field> Laplacian_t; + Laplacian_t Laplacian(U); + + ColourVector ColourKronecker; + ColourKronecker = Zero(); + ColourKronecker()()(0) = 1.0; + + Coordinate site({latt_size[0]/2, + latt_size[1]/2, + latt_size[2]/2, + 0}); + + Field kronecker(&Grid); + kronecker = Zero(); + pokeSite(ColourKronecker,kronecker,site); + + + Field psi(&Grid), chi(&Grid); + + ////////////////////////////////////// + // Classic Wuppertal smearing + ////////////////////////////////////// + + Integer Iterations = 80; + Real width = 2.0; + Real coeff = (width*width) / Real(4*Iterations); + + chi=kronecker; + // chi = (1-p^2/2N)^N kronecker + for(int n = 0; n < Iterations; ++n) { + Laplacian.M(chi,psi); + chi = chi - coeff*psi; + } + + std::cout << " Wuppertal smeared operator is chi = \n" << chi <<std::endl; + + ///////////////////////////////////// + // Chebyshev smearing + ///////////////////////////////////// + RealD lo = 0.0; + RealD hi = 128; + HermitianLinearOperator<Laplacian_t,Field> HermOp(Laplacian); + + Chebyshev<Field> ChebySmear(lo,hi,20,DistillationSmearing); + // Chebyshev<Field> ChebySmear(lo,hi,20,MomentumSmearing); + { + std::ofstream of("chebysmear"); + ChebySmear.csv(of); + } + + ChebySmear(HermOp,kronecker,chi); + + std::cout << " Chebyshev smeared operator is chi = \n" << chi <<std::endl; + + Grid_finalize(); +} diff --git a/examples/Example_Laplacian_smearing.cc b/examples/Example_Laplacian_smearing.cc new file mode 100644 index 00000000..327c5ca9 --- /dev/null +++ b/examples/Example_Laplacian_smearing.cc @@ -0,0 +1,127 @@ +#include <Grid/Grid.h> +using namespace Grid; + +// Function used for Chebyshev smearing +// +Real MomentumSmearing(Real p2) +{ + return (1 - 4.0*p2) * exp(-p2/4); +} +Real DistillationSmearing(Real p2) +{ + if ( p2 > 0.5 ) return 0.0; + else return 1.0; +} + +// Flip sign to make prop to p^2, not -p^2 relative to last example +template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + GridBase *grid; + GaugeField U; + + CovariantLaplacianCshift(GaugeField &_U) : + grid(_U.Grid()), + U(_U) { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out=Zero(); + for(int mu=0;mu<Nd-1;mu++) { + GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent + out = out - Gimpl::CovShiftForward(Umu,mu,in); + out = out - Gimpl::CovShiftBackward(Umu,mu,in); + out = out + 2.0*in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + + + +int main(int argc, char ** argv) +{ + Grid_init(&argc, &argv); + + typedef LatticeColourVector Field; + + auto latt_size = GridDefaultLatt(); + auto simd_layout = GridDefaultSimd(Nd,vComplex::Nsimd()); + auto mpi_layout = GridDefaultMpi(); + + GridCartesian Grid(latt_size,simd_layout,mpi_layout); + GridParallelRNG RNG(&Grid); RNG.SeedFixedIntegers(std::vector<int>({45,12,81,9})); + + + LatticeGaugeField U(&Grid); + + SU<Nc>::ColdConfiguration(RNG,U); + + typedef CovariantLaplacianCshift <PeriodicGimplR,Field> Laplacian_t; + Laplacian_t Laplacian(U); + + + ColourVector ColourKronecker; + ColourKronecker = Zero(); + ColourKronecker()()(0) = 1.0; + + Coordinate site({latt_size[0]/2, + latt_size[1]/2, + latt_size[2]/2, + 0}); + + Field kronecker(&Grid); + kronecker = Zero(); + pokeSite(ColourKronecker,kronecker,site); + + + Field psi(&Grid), chi(&Grid); + + ////////////////////////////////////// + // Classic Wuppertal smearing + ////////////////////////////////////// + + Integer Iterations = 80; + Real width = 2.0; + Real coeff = (width*width) / Real(4*Iterations); + + chi=kronecker; + // chi = (1-p^2/2N)^N kronecker + for(int n = 0; n < Iterations; ++n) { + Laplacian.M(chi,psi); + chi = chi - coeff*psi; + } + + std::cout << " Wuppertal smeared operator is chi = \n" << chi <<std::endl; + + ///////////////////////////////////// + // Chebyshev smearing + ///////////////////////////////////// + RealD lo = 0.0; + RealD hi = 12.0; // Analytic free field bound + HermitianLinearOperator<Laplacian_t,Field> HermOp(Laplacian); + + std::cout << " Checking spectral range of our POSITIVE definite operator \n"; + PowerMethod<Field> PM; + PM(HermOp,kronecker); + + Chebyshev<Field> ChebySmear(lo,hi,20,DistillationSmearing); + // Chebyshev<Field> ChebySmear(lo,hi,20,MomentumSmearing); + { + std::ofstream of("chebysmear"); + ChebySmear.csv(of); + } + + ChebySmear(HermOp,kronecker,chi); + + std::cout << " Chebyshev smeared operator is chi = \n" << chi <<std::endl; + + Grid_finalize(); +} diff --git a/examples/Example_Laplacian_solver.cc b/examples/Example_Laplacian_solver.cc new file mode 100644 index 00000000..88275df8 --- /dev/null +++ b/examples/Example_Laplacian_solver.cc @@ -0,0 +1,127 @@ +#include <Grid/Grid.h> +using namespace Grid; + +template<class Field> +void SimpleConjugateGradient(LinearOperatorBase<Field> &HPDop,const Field &b, Field &x) +{ + RealD cp, c, alpha, d, beta, ssq, qq; + RealD Tolerance=1.0e-10; + int MaxIterations=10000; + + Field p(b), mmp(b), r(b); + + HPDop.HermOpAndNorm(x, mmp, d, beta); + + r = b - mmp; + p = r; + + alpha = norm2(p); + cp = alpha; + ssq = norm2(b); + + RealD rsq = Tolerance * Tolerance * ssq; + + for (int k = 1; k <= MaxIterations; k++) { + c = cp; + + HPDop.HermOp(p, mmp); + + ComplexD dc = innerProduct(p,mmp); + d = dc.real(); + alpha = c / d; + + cp = axpy_norm(r, -alpha, mmp, r); + beta = cp / c; + + x = x + alpha* p ; + p = r + beta* p ; + + std::cout << "iteration "<<k<<" cp " <<std::sqrt(cp/ssq) << std::endl; + if (cp <= rsq) { + return; + } + } + assert(0); +} + + + +// Flip sign to make prop to p^2, not -p^2 relative to last example +template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + GridBase *grid; + GaugeField U; + RealD m2=1.0e-2; + CovariantLaplacianCshift(GaugeField &_U) : + grid(_U.Grid()), + U(_U) { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out=Zero(); + for(int mu=0;mu<Nd-1;mu++) { + GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent + out = out - Gimpl::CovShiftForward(Umu,mu,in); + out = out - Gimpl::CovShiftBackward(Umu,mu,in); + out = out + 2.0*in + m2*in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + + + +int main(int argc, char ** argv) +{ + Grid_init(&argc, &argv); + + typedef LatticeColourVector Field; + + auto latt_size = GridDefaultLatt(); + auto simd_layout = GridDefaultSimd(Nd,vComplex::Nsimd()); + auto mpi_layout = GridDefaultMpi(); + + GridCartesian Grid(latt_size,simd_layout,mpi_layout); + GridParallelRNG RNG(&Grid); RNG.SeedFixedIntegers(std::vector<int>({45,12,81,9})); + + + LatticeGaugeField U(&Grid); + + SU<Nc>::ColdConfiguration(RNG,U); + + typedef CovariantLaplacianCshift <PeriodicGimplR,Field> Laplacian_t; + Laplacian_t Laplacian(U); + + + ColourVector ColourKronecker; + ColourKronecker = Zero(); + ColourKronecker()()(0) = 1.0; + + Coordinate site({0,0,0,0}); // Point source at origin + + Field kronecker(&Grid); + kronecker = Zero(); + pokeSite(ColourKronecker,kronecker,site); + + Field psi(&Grid); psi=Zero(); + + HermitianLinearOperator<Laplacian_t,Field> HermOp(Laplacian); + SimpleConjugateGradient(HermOp, kronecker,psi); + + Field r(&Grid); + Laplacian.M(psi,r); + r=kronecker-r; + + std::cout << "True residual "<< norm2(r) <<std::endl; + std::cout << psi<<std::endl; + + Grid_finalize(); +} From ec9c3fe77acf94a15f99338dc4fefbd607b04426 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sun, 22 Aug 2021 18:28:39 +0100 Subject: [PATCH 191/399] Remove the file --- examples/Example_Laplacian_inverter.cc | 122 ------------------------- 1 file changed, 122 deletions(-) delete mode 100644 examples/Example_Laplacian_inverter.cc diff --git a/examples/Example_Laplacian_inverter.cc b/examples/Example_Laplacian_inverter.cc deleted file mode 100644 index 1235d2b8..00000000 --- a/examples/Example_Laplacian_inverter.cc +++ /dev/null @@ -1,122 +0,0 @@ -#include <Grid/Grid.h> -using namespace Grid; - -// Function used for Chebyshev smearing -// -Real MomentumSmearing(Real p2) -{ - return (1 - 4.0*p2) * exp(-p2/4); -} -Real DistillationSmearing(Real p2) -{ - if ( p2 > 0.5 ) return 0.0; - else return 1.0; -} - -// Flip sign to make prop to p^2, not -p^2 relative to last example -template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> -{ -public: - INHERIT_GIMPL_TYPES(Gimpl); - - GridBase *grid; - GaugeField U; - - CovariantLaplacianCshift(GaugeField &_U) : - grid(_U.Grid()), - U(_U) { }; - - virtual GridBase *Grid(void) { return grid; }; - - virtual void M (const Field &in, Field &out) - { - out=Zero(); - for(int mu=0;mu<Nd-1;mu++) { - GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent - out = out - Gimpl::CovShiftForward(Umu,mu,in); - out = out - Gimpl::CovShiftBackward(Umu,mu,in); - out = out + 2.0*in; - } - }; - virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian - virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid - virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid - virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid -}; - - - -int main(int argc, char ** argv) -{ - Grid_init(&argc, &argv); - - typedef LatticeColourVector Field; - - auto latt_size = GridDefaultLatt(); - auto simd_layout = GridDefaultSimd(Nd,vComplex::Nsimd()); - auto mpi_layout = GridDefaultMpi(); - - GridCartesian Grid(latt_size,simd_layout,mpi_layout); - GridParallelRNG RNG(&Grid); RNG.SeedFixedIntegers(std::vector<int>({45,12,81,9})); - - - LatticeGaugeField U(&Grid); - - SU<Nc>::ColdConfiguration(RNG,U); - - typedef CovariantLaplacianCshift <PeriodicGimplR,Field> Laplacian_t; - Laplacian_t Laplacian(U); - - ColourVector ColourKronecker; - ColourKronecker = Zero(); - ColourKronecker()()(0) = 1.0; - - Coordinate site({latt_size[0]/2, - latt_size[1]/2, - latt_size[2]/2, - 0}); - - Field kronecker(&Grid); - kronecker = Zero(); - pokeSite(ColourKronecker,kronecker,site); - - - Field psi(&Grid), chi(&Grid); - - ////////////////////////////////////// - // Classic Wuppertal smearing - ////////////////////////////////////// - - Integer Iterations = 80; - Real width = 2.0; - Real coeff = (width*width) / Real(4*Iterations); - - chi=kronecker; - // chi = (1-p^2/2N)^N kronecker - for(int n = 0; n < Iterations; ++n) { - Laplacian.M(chi,psi); - chi = chi - coeff*psi; - } - - std::cout << " Wuppertal smeared operator is chi = \n" << chi <<std::endl; - - ///////////////////////////////////// - // Chebyshev smearing - ///////////////////////////////////// - RealD lo = 0.0; - RealD hi = 128; - HermitianLinearOperator<Laplacian_t,Field> HermOp(Laplacian); - - Chebyshev<Field> ChebySmear(lo,hi,20,DistillationSmearing); - // Chebyshev<Field> ChebySmear(lo,hi,20,MomentumSmearing); - { - std::ofstream of("chebysmear"); - ChebySmear.csv(of); - } - - ChebySmear(HermOp,kronecker,chi); - - std::cout << " Chebyshev smeared operator is chi = \n" << chi <<std::endl; - - Grid_finalize(); -} From c6a5499c8be2b0f90ceebef000899db9c5bd1665 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sun, 22 Aug 2021 18:40:55 +0100 Subject: [PATCH 192/399] Fail on non-apple --- Grid/util/Init.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/util/Init.cc b/Grid/util/Init.cc index 3b661524..c0bfc906 100644 --- a/Grid/util/Init.cc +++ b/Grid/util/Init.cc @@ -52,8 +52,8 @@ Author: paboyle <paboyle@ph.ed.ac.uk> #include <fenv.h> -static int #ifdef __APPLE__ +static int feenableexcept (unsigned int excepts) { #if 0 From 5b3c530aa778b23ae9383a5b7a63fd20f5d9c2cb Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 23 Aug 2021 15:30:45 +0100 Subject: [PATCH 193/399] Return value --- Grid/util/Init.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/Grid/util/Init.cc b/Grid/util/Init.cc index c0bfc906..ab2d2399 100644 --- a/Grid/util/Init.cc +++ b/Grid/util/Init.cc @@ -73,6 +73,7 @@ feenableexcept (unsigned int excepts) iold_excepts = (int) old_excepts; return ( fesetenv (&fenv) ? -1 : iold_excepts ); #endif + return 0; } #endif From 0d588b95f4e1847024c4eea0094f7fc0dc36ad28 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 23 Aug 2021 23:14:26 +0100 Subject: [PATCH 194/399] Bug fix to Example_Laplacian test --- Grid/threads/Accelerator.h | 6 ++++++ examples/Example_Laplacian.cc | 9 +++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 2c9d15ba..a8c91aa8 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -39,6 +39,10 @@ Author: paboyle <paboyle@ph.ed.ac.uk> #ifdef HAVE_MM_MALLOC_H #include <mm_malloc.h> #endif +#ifdef __APPLE__ +// no memalign +inline void *memalign(size_t align, size_t bytes) { return malloc(bytes); } +#endif NAMESPACE_BEGIN(Grid); @@ -419,6 +423,8 @@ inline void acceleratorMemSet(void *base,int value,size_t bytes) { hipMemset(bas #undef GRID_SIMT + + #define accelerator #define accelerator_inline strong_inline #define accelerator_for(iterator,num,nsimd, ... ) thread_for(iterator, num, { __VA_ARGS__ }); diff --git a/examples/Example_Laplacian.cc b/examples/Example_Laplacian.cc index f6bbf7cf..fa8466cf 100644 --- a/examples/Example_Laplacian.cc +++ b/examples/Example_Laplacian.cc @@ -116,7 +116,6 @@ public: /////////////////////////////////// // Arithmetic expressions /////////////////////////////////// - StencilEntry *SE; // Views; device friendly/accessible pointers auto st = Stencil.View(AcceleratorRead); @@ -129,10 +128,11 @@ public: const int Nsimd = vobj::Nsimd(); const uint64_t NN = grid->oSites(); - const int lane=acceleratorSIMTlane(Nsimd); accelerator_for( ss, NN, Nsimd, { + StencilEntry *SE; + const int lane=acceleratorSIMTlane(Nsimd); calcObj chi; @@ -202,7 +202,6 @@ public: /////////////////////////////////// auto st = Stencil.View(AcceleratorRead); auto buf = st.CommBuf(); - StencilEntry *SE; autoView( in , _in , AcceleratorRead); autoView( out , _out , AcceleratorWrite); @@ -214,9 +213,11 @@ public: const int Nsimd = vobj::Nsimd(); const uint64_t NN = grid->oSites(); - const int lane=acceleratorSIMTlane(Nsimd); + accelerator_for( ss, NN, Nsimd, { + StencilEntry *SE; + const int lane=acceleratorSIMTlane(Nsimd); calcObj chi; From 114920b8de4048b250206b4bd3ac469cd7fffd95 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 25 Aug 2021 12:24:17 +0100 Subject: [PATCH 195/399] Some example clean up --- examples/Example_Laplacian_smearing.cc | 4 ++-- examples/Example_Laplacian_solver.cc | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/Example_Laplacian_smearing.cc b/examples/Example_Laplacian_smearing.cc index 327c5ca9..9780e8a0 100644 --- a/examples/Example_Laplacian_smearing.cc +++ b/examples/Example_Laplacian_smearing.cc @@ -112,8 +112,8 @@ int main(int argc, char ** argv) PowerMethod<Field> PM; PM(HermOp,kronecker); - Chebyshev<Field> ChebySmear(lo,hi,20,DistillationSmearing); - // Chebyshev<Field> ChebySmear(lo,hi,20,MomentumSmearing); + // Chebyshev<Field> ChebySmear(lo,hi,20,DistillationSmearing); + Chebyshev<Field> ChebySmear(lo,hi,20,MomentumSmearing); { std::ofstream of("chebysmear"); ChebySmear.csv(of); diff --git a/examples/Example_Laplacian_solver.cc b/examples/Example_Laplacian_solver.cc index 88275df8..4dc00280 100644 --- a/examples/Example_Laplacian_solver.cc +++ b/examples/Example_Laplacian_solver.cc @@ -15,8 +15,7 @@ void SimpleConjugateGradient(LinearOperatorBase<Field> &HPDop,const Field &b, Fi r = b - mmp; p = r; - alpha = norm2(p); - cp = alpha; + cp = alpha = norm2(p); ssq = norm2(b); RealD rsq = Tolerance * Tolerance * ssq; @@ -26,11 +25,12 @@ void SimpleConjugateGradient(LinearOperatorBase<Field> &HPDop,const Field &b, Fi HPDop.HermOp(p, mmp); - ComplexD dc = innerProduct(p,mmp); - d = dc.real(); + d = real(innerProduct(p,mmp)); + alpha = c / d; - cp = axpy_norm(r, -alpha, mmp, r); + r = r - alpha *mmp; + cp = norm2(r); beta = cp / c; x = x + alpha* p ; @@ -121,7 +121,9 @@ int main(int argc, char ** argv) r=kronecker-r; std::cout << "True residual "<< norm2(r) <<std::endl; - std::cout << psi<<std::endl; + + // Optionally print the result vector + // std::cout << psi<<std::endl; Grid_finalize(); } From bcfa9cf06854d991987df08a0df32866070a0d90 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sat, 28 Aug 2021 08:08:15 -0700 Subject: [PATCH 196/399] Improvement of output --- benchmarks/Benchmark_comms_host_device.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/Benchmark_comms_host_device.cc b/benchmarks/Benchmark_comms_host_device.cc index 591b5597..49a2181c 100644 --- a/benchmarks/Benchmark_comms_host_device.cc +++ b/benchmarks/Benchmark_comms_host_device.cc @@ -53,7 +53,7 @@ struct time_statistics{ void header(){ std::cout <<GridLogMessage << " L "<<"\t"<<" Ls "<<"\t" - <<std::setw(11)<<"bytes\t\t"<<"MB/s uni (err/min/max)"<<"\t\t"<<"MB/s bidi (err/min/max)"<<std::endl; + <<std::setw(11)<<"bytes\t\t"<<"MB/s uni"<<"\t"<<"MB/s bidi"<<std::endl; }; int main (int argc, char ** argv) From 3044419111cffcbf43c11e1d4965ba565ff514fc Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 30 Aug 2021 20:32:11 -0400 Subject: [PATCH 197/399] Some sample code --- examples/Example_Mobius_spectrum.cc | 334 ++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 examples/Example_Mobius_spectrum.cc diff --git a/examples/Example_Mobius_spectrum.cc b/examples/Example_Mobius_spectrum.cc new file mode 100644 index 00000000..dd84a336 --- /dev/null +++ b/examples/Example_Mobius_spectrum.cc @@ -0,0 +1,334 @@ +/************************************************************************************* + Grid physics library, www.github.com/paboyle/Grid + + Copyright (C) 2021 + +Author: Peter Boyle <paboyle@ph.ed.ac.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include <Grid/Grid.h> + +using namespace std; +using namespace Grid; + +// Flip sign to make prop to p^2, not -p^2 relative to last example +template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + GridBase *grid; + GaugeField U; + + CovariantLaplacianCshift(GaugeField &_U) : + grid(_U.Grid()), + U(_U) { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out=Zero(); + for(int mu=0;mu<Nd-1;mu++) { + GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent + out = out - Gimpl::CovShiftForward(Umu,mu,in); + out = out - Gimpl::CovShiftBackward(Umu,mu,in); + out = out + 2.0*in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + +void MakePhase(Coordinate mom,LatticeComplex &phase) +{ + GridBase *grid = phase.Grid(); + auto latt_size = grid->GlobalDimensions(); + ComplexD ci(0.0,1.0); + phase=Zero(); + + LatticeComplex coor(phase.Grid()); + for(int mu=0;mu<Nd;mu++){ + RealD TwoPiL = M_PI * 2.0/ latt_size[mu]; + LatticeCoordinate(coor,mu); + phase = phase + (TwoPiL * mom[mu]) * coor; + } + phase = exp(phase*ci); +} +void PointSource(Coordinate &coor,LatticePropagator &source) +{ + // Coordinate coor({0,0,0,0}); + source=Zero(); + SpinColourMatrix kronecker; kronecker=1.0; + pokeSite(kronecker,source,coor); +} +void Z2WallSource(GridParallelRNG &RNG,int tslice,LatticePropagator &source) +{ + GridBase *grid = source.Grid(); + LatticeComplex noise(grid); + LatticeComplex zz(grid); zz=Zero(); + LatticeInteger t(grid); + + RealD nrm=1.0/sqrt(2); + bernoulli(RNG, noise); // 0,1 50:50 + + noise = (2.*noise - Complex(1,1))*nrm; + + LatticeCoordinate(t,Tdir); + noise = where(t==Integer(tslice), noise, zz); + + source = 1.0; + source = source*noise; + std::cout << " Z2 wall " << norm2(source) << std::endl; +} +template<class Field> +void GaussianSmear(LatticeGaugeField &U,Field &unsmeared,Field &smeared) +{ + typedef CovariantLaplacianCshift <PeriodicGimplR,Field> Laplacian_t; + Laplacian_t Laplacian(U); + + Integer Iterations = 40; + Real width = 2.0; + Real coeff = (width*width) / Real(4*Iterations); + + Field tmp(U.Grid()); + smeared=unsmeared; + // chi = (1-p^2/2N)^N kronecker + for(int n = 0; n < Iterations; ++n) { + Laplacian.M(smeared,tmp); + smeared = smeared - coeff*tmp; + std::cout << " smear iter " << n<<" " <<norm2(smeared)<<std::endl; + } +} +void GaussianSource(Coordinate &site,LatticeGaugeField &U,LatticePropagator &source) +{ + LatticePropagator tmp(source.Grid()); + PointSource(site,source); + std::cout << " GaussianSource Kronecker "<< norm2(source)<<std::endl; + tmp = source; + GaussianSmear(U,tmp,source); + std::cout << " GaussianSource Smeared "<< norm2(source)<<std::endl; +} +void GaussianWallSource(GridParallelRNG &RNG,int tslice,LatticeGaugeField &U,LatticePropagator &source) +{ + Z2WallSource(RNG,tslice,source); + auto tmp = source; + GaussianSmear(U,tmp,source); +} +void SequentialSource(int tslice,Coordinate &mom,LatticePropagator &spectator,LatticePropagator &source) +{ + assert(mom.size()==Nd); + assert(mom[Tdir] == 0); + + GridBase * grid = spectator.Grid(); + + + LatticeInteger ts(grid); + LatticeCoordinate(ts,Tdir); + source = Zero(); + source = where(ts==Integer(tslice),spectator,source); // Stick in a slice of the spectator, zero everywhere else + + LatticeComplex phase(grid); + MakePhase(mom,phase); + + source = source *phase; +} +template<class Action> +void Solve(Action &D,LatticePropagator &source,LatticePropagator &propagator) +{ + GridBase *UGrid = D.GaugeGrid(); + GridBase *FGrid = D.FermionGrid(); + + LatticeFermion src4 (UGrid); + LatticeFermion src5 (FGrid); + LatticeFermion result5(FGrid); + LatticeFermion result4(UGrid); + + ConjugateGradient<LatticeFermion> CG(1.0e-8,100000); + SchurRedBlackDiagMooeeSolve<LatticeFermion> schur(CG); + ZeroGuesser<LatticeFermion> ZG; // Could be a DeflatedGuesser if have eigenvectors + for(int s=0;s<Nd;s++){ + for(int c=0;c<Nc;c++){ + PropToFerm<Action>(src4,source,s,c); + + D.ImportPhysicalFermionSource(src4,src5); + + result5=Zero(); + schur(D,src5,result5,ZG); + std::cout<<GridLogMessage + <<"spin "<<s<<" color "<<c + <<" norm2(src5d) " <<norm2(src5) + <<" norm2(result5d) "<<norm2(result5)<<std::endl; + + D.ExportPhysicalFermionSolution(result5,result4); + + FermToProp<Action>(propagator,result4,s,c); + } + } +} + +class MesonFile: Serializable { +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(MesonFile, std::vector<std::vector<Complex> >, data); +}; + +void MesonTrace(std::string file,LatticePropagator &q1,LatticePropagator &q2,LatticeComplex &phase) +{ + const int nchannel=4; + Gamma::Algebra Gammas[nchannel][2] = { + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::GammaTGamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaTGamma5} + }; + + Gamma G5(Gamma::Algebra::Gamma5); + + LatticeComplex meson_CF(q1.Grid()); + MesonFile MF; + + for(int ch=0;ch<nchannel;ch++){ + + Gamma Gsrc(Gammas[ch][0]); + Gamma Gsnk(Gammas[ch][1]); + + meson_CF = trace(G5*adj(q1)*G5*Gsnk*q2*adj(Gsrc)); + + std::vector<TComplex> meson_T; + sliceSum(meson_CF,meson_T, Tdir); + + int nt=meson_T.size(); + + std::vector<Complex> corr(nt); + for(int t=0;t<nt;t++){ + corr[t] = TensorRemove(meson_T[t]); // Yes this is ugly, not figured a work around + std::cout << " channel "<<ch<<" t "<<t<<" " <<corr[t]<<std::endl; + } + MF.data.push_back(corr); + } + + { + XmlWriter WR(file); + write(WR,"MesonFile",MF); + } +} + +int main (int argc, char ** argv) +{ + const int Ls=8; + + Grid_init(&argc,&argv); + + // Double precision grids + GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), + GridDefaultSimd(Nd,vComplex::Nsimd()), + GridDefaultMpi()); + GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); + GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); + GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); + + ////////////////////////////////////////////////////////////////////// + // You can manage seeds however you like. + // Recommend SeedUniqueString. + ////////////////////////////////////////////////////////////////////// + std::vector<int> seeds4({1,2,3,4}); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + + LatticeGaugeField Umu(UGrid); + std::string config; + if( argc > 1 && argv[1][0] != '-' ) + { + std::cout<<GridLogMessage <<"Loading configuration from "<<argv[1]<<std::endl; + FieldMetaData header; + NerscIO::readConfiguration(Umu, header, argv[1]); + config=argv[1]; + } + else + { + std::cout<<GridLogMessage <<"Using hot configuration"<<std::endl; + SU<Nc>::ColdConfiguration(Umu); + // SU<Nc>::HotConfiguration(RNG4,Umu); + config="HotConfig"; + } + + std::vector<RealD> masses({ 0.03,0.04,0.45} ); // u/d, s, c ?? + + int nmass = masses.size(); + + std::vector<MobiusFermionR *> FermActs; + + std::cout<<GridLogMessage <<"======================"<<std::endl; + std::cout<<GridLogMessage <<"MobiusFermion action as Scaled Shamir kernel"<<std::endl; + std::cout<<GridLogMessage <<"======================"<<std::endl; + + for(auto mass: masses) { + + RealD M5=1.0; + RealD b=1.5;// Scale factor b+c=2, b-c=1 + RealD c=0.5; + + FermActs.push_back(new MobiusFermionR(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,b,c)); + + } + + LatticePropagator point_source(UGrid); + LatticePropagator wall_source(UGrid); + LatticePropagator gaussian_source(UGrid); + + Coordinate Origin({0,0,0,0}); + PointSource (Origin,point_source); + Z2WallSource (RNG4,0,wall_source); + GaussianSource(Origin,Umu,gaussian_source); + + std::vector<LatticePropagator> PointProps(nmass,UGrid); + std::vector<LatticePropagator> GaussProps(nmass,UGrid); + std::vector<LatticePropagator> Z2Props (nmass,UGrid); + + for(int m=0;m<nmass;m++) { + + Solve(*FermActs[m],point_source ,PointProps[m]); + Solve(*FermActs[m],gaussian_source,GaussProps[m]); + Solve(*FermActs[m],wall_source ,Z2Props[m]); + + } + + LatticeComplex phase(UGrid); + Coordinate mom({0,0,0,0}); + MakePhase(mom,phase); + + for(int m1=0 ;m1<nmass;m1++) { + for(int m2=m1;m2<nmass;m2++) { + std::stringstream ssp,ssg,ssz; + + ssp<<config<< "_m" << m1 << "_m"<< m2 << "_point_meson.xml"; + ssg<<config<< "_m" << m1 << "_m"<< m2 << "_smeared_meson.xml"; + ssz<<config<< "_m" << m1 << "_m"<< m2 << "_wall_meson.xml"; + + MesonTrace(ssp.str(),PointProps[m1],PointProps[m2],phase); + MesonTrace(ssg.str(),GaussProps[m1],GaussProps[m2],phase); + MesonTrace(ssz.str(),Z2Props[m1],Z2Props[m2],phase); + }} + + Grid_finalize(); +} + + + From b06526bc1e0c85f54a58e22e5992e33d671d3b6c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 30 Aug 2021 21:15:39 -0400 Subject: [PATCH 198/399] Comment update --- examples/Example_Mobius_spectrum.cc | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/examples/Example_Mobius_spectrum.cc b/examples/Example_Mobius_spectrum.cc index dd84a336..f4cd3335 100644 --- a/examples/Example_Mobius_spectrum.cc +++ b/examples/Example_Mobius_spectrum.cc @@ -1,33 +1,13 @@ -/************************************************************************************* - Grid physics library, www.github.com/paboyle/Grid +/* + * Warning: This code illustrative only: not well tested, and not meant for production use + * without regression / tests being applied + */ - Copyright (C) 2021 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - See the full license in the file "LICENSE" in the top level distribution directory - *************************************************************************************/ - /* END LEGAL */ #include <Grid/Grid.h> using namespace std; using namespace Grid; -// Flip sign to make prop to p^2, not -p^2 relative to last example template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> { public: From 381d8797d0225bef118fe390882ea725f5b32cea Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sat, 11 Sep 2021 23:05:02 +0100 Subject: [PATCH 199/399] Drop half prec comms for now --- Grid/qcd/action/fermion/Fermion.h | 72 +++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/Grid/qcd/action/fermion/Fermion.h b/Grid/qcd/action/fermion/Fermion.h index 09777204..59fc17d5 100644 --- a/Grid/qcd/action/fermion/Fermion.h +++ b/Grid/qcd/action/fermion/Fermion.h @@ -115,9 +115,9 @@ typedef WilsonFermion<WilsonImplR> WilsonFermionR; typedef WilsonFermion<WilsonImplF> WilsonFermionF; typedef WilsonFermion<WilsonImplD> WilsonFermionD; -typedef WilsonFermion<WilsonImplRL> WilsonFermionRL; -typedef WilsonFermion<WilsonImplFH> WilsonFermionFH; -typedef WilsonFermion<WilsonImplDF> WilsonFermionDF; +//typedef WilsonFermion<WilsonImplRL> WilsonFermionRL; +//typedef WilsonFermion<WilsonImplFH> WilsonFermionFH; +//typedef WilsonFermion<WilsonImplDF> WilsonFermionDF; typedef WilsonFermion<WilsonAdjImplR> WilsonAdjFermionR; typedef WilsonFermion<WilsonAdjImplF> WilsonAdjFermionF; @@ -158,41 +158,41 @@ typedef DomainWallFermion<WilsonImplR> DomainWallFermionR; typedef DomainWallFermion<WilsonImplF> DomainWallFermionF; typedef DomainWallFermion<WilsonImplD> DomainWallFermionD; -typedef DomainWallFermion<WilsonImplRL> DomainWallFermionRL; -typedef DomainWallFermion<WilsonImplFH> DomainWallFermionFH; -typedef DomainWallFermion<WilsonImplDF> DomainWallFermionDF; +//typedef DomainWallFermion<WilsonImplRL> DomainWallFermionRL; +//typedef DomainWallFermion<WilsonImplFH> DomainWallFermionFH; +//typedef DomainWallFermion<WilsonImplDF> DomainWallFermionDF; typedef DomainWallEOFAFermion<WilsonImplR> DomainWallEOFAFermionR; typedef DomainWallEOFAFermion<WilsonImplF> DomainWallEOFAFermionF; typedef DomainWallEOFAFermion<WilsonImplD> DomainWallEOFAFermionD; -typedef DomainWallEOFAFermion<WilsonImplRL> DomainWallEOFAFermionRL; -typedef DomainWallEOFAFermion<WilsonImplFH> DomainWallEOFAFermionFH; -typedef DomainWallEOFAFermion<WilsonImplDF> DomainWallEOFAFermionDF; +//typedef DomainWallEOFAFermion<WilsonImplRL> DomainWallEOFAFermionRL; +//typedef DomainWallEOFAFermion<WilsonImplFH> DomainWallEOFAFermionFH; +//typedef DomainWallEOFAFermion<WilsonImplDF> DomainWallEOFAFermionDF; typedef MobiusFermion<WilsonImplR> MobiusFermionR; typedef MobiusFermion<WilsonImplF> MobiusFermionF; typedef MobiusFermion<WilsonImplD> MobiusFermionD; -typedef MobiusFermion<WilsonImplRL> MobiusFermionRL; -typedef MobiusFermion<WilsonImplFH> MobiusFermionFH; -typedef MobiusFermion<WilsonImplDF> MobiusFermionDF; +//typedef MobiusFermion<WilsonImplRL> MobiusFermionRL; +//typedef MobiusFermion<WilsonImplFH> MobiusFermionFH; +//typedef MobiusFermion<WilsonImplDF> MobiusFermionDF; typedef MobiusEOFAFermion<WilsonImplR> MobiusEOFAFermionR; typedef MobiusEOFAFermion<WilsonImplF> MobiusEOFAFermionF; typedef MobiusEOFAFermion<WilsonImplD> MobiusEOFAFermionD; -typedef MobiusEOFAFermion<WilsonImplRL> MobiusEOFAFermionRL; -typedef MobiusEOFAFermion<WilsonImplFH> MobiusEOFAFermionFH; -typedef MobiusEOFAFermion<WilsonImplDF> MobiusEOFAFermionDF; +//typedef MobiusEOFAFermion<WilsonImplRL> MobiusEOFAFermionRL; +//typedef MobiusEOFAFermion<WilsonImplFH> MobiusEOFAFermionFH; +//typedef MobiusEOFAFermion<WilsonImplDF> MobiusEOFAFermionDF; typedef ZMobiusFermion<ZWilsonImplR> ZMobiusFermionR; typedef ZMobiusFermion<ZWilsonImplF> ZMobiusFermionF; typedef ZMobiusFermion<ZWilsonImplD> ZMobiusFermionD; -typedef ZMobiusFermion<ZWilsonImplRL> ZMobiusFermionRL; -typedef ZMobiusFermion<ZWilsonImplFH> ZMobiusFermionFH; -typedef ZMobiusFermion<ZWilsonImplDF> ZMobiusFermionDF; +//typedef ZMobiusFermion<ZWilsonImplRL> ZMobiusFermionRL; +//typedef ZMobiusFermion<ZWilsonImplFH> ZMobiusFermionFH; +//typedef ZMobiusFermion<ZWilsonImplDF> ZMobiusFermionDF; // Ls vectorised typedef ScaledShamirFermion<WilsonImplR> ScaledShamirFermionR; @@ -235,49 +235,49 @@ typedef WilsonFermion<GparityWilsonImplR> GparityWilsonFermionR; typedef WilsonFermion<GparityWilsonImplF> GparityWilsonFermionF; typedef WilsonFermion<GparityWilsonImplD> GparityWilsonFermionD; -typedef WilsonFermion<GparityWilsonImplRL> GparityWilsonFermionRL; -typedef WilsonFermion<GparityWilsonImplFH> GparityWilsonFermionFH; -typedef WilsonFermion<GparityWilsonImplDF> GparityWilsonFermionDF; +//typedef WilsonFermion<GparityWilsonImplRL> GparityWilsonFermionRL; +//typedef WilsonFermion<GparityWilsonImplFH> GparityWilsonFermionFH; +//typedef WilsonFermion<GparityWilsonImplDF> GparityWilsonFermionDF; typedef DomainWallFermion<GparityWilsonImplR> GparityDomainWallFermionR; typedef DomainWallFermion<GparityWilsonImplF> GparityDomainWallFermionF; typedef DomainWallFermion<GparityWilsonImplD> GparityDomainWallFermionD; -typedef DomainWallFermion<GparityWilsonImplRL> GparityDomainWallFermionRL; -typedef DomainWallFermion<GparityWilsonImplFH> GparityDomainWallFermionFH; -typedef DomainWallFermion<GparityWilsonImplDF> GparityDomainWallFermionDF; +//typedef DomainWallFermion<GparityWilsonImplRL> GparityDomainWallFermionRL; +//typedef DomainWallFermion<GparityWilsonImplFH> GparityDomainWallFermionFH; +//typedef DomainWallFermion<GparityWilsonImplDF> GparityDomainWallFermionDF; typedef DomainWallEOFAFermion<GparityWilsonImplR> GparityDomainWallEOFAFermionR; typedef DomainWallEOFAFermion<GparityWilsonImplF> GparityDomainWallEOFAFermionF; typedef DomainWallEOFAFermion<GparityWilsonImplD> GparityDomainWallEOFAFermionD; -typedef DomainWallEOFAFermion<GparityWilsonImplRL> GparityDomainWallEOFAFermionRL; -typedef DomainWallEOFAFermion<GparityWilsonImplFH> GparityDomainWallEOFAFermionFH; -typedef DomainWallEOFAFermion<GparityWilsonImplDF> GparityDomainWallEOFAFermionDF; +//typedef DomainWallEOFAFermion<GparityWilsonImplRL> GparityDomainWallEOFAFermionRL; +//typedef DomainWallEOFAFermion<GparityWilsonImplFH> GparityDomainWallEOFAFermionFH; +//typedef DomainWallEOFAFermion<GparityWilsonImplDF> GparityDomainWallEOFAFermionDF; typedef WilsonTMFermion<GparityWilsonImplR> GparityWilsonTMFermionR; typedef WilsonTMFermion<GparityWilsonImplF> GparityWilsonTMFermionF; typedef WilsonTMFermion<GparityWilsonImplD> GparityWilsonTMFermionD; -typedef WilsonTMFermion<GparityWilsonImplRL> GparityWilsonTMFermionRL; -typedef WilsonTMFermion<GparityWilsonImplFH> GparityWilsonTMFermionFH; -typedef WilsonTMFermion<GparityWilsonImplDF> GparityWilsonTMFermionDF; +//typedef WilsonTMFermion<GparityWilsonImplRL> GparityWilsonTMFermionRL; +//typedef WilsonTMFermion<GparityWilsonImplFH> GparityWilsonTMFermionFH; +//typedef WilsonTMFermion<GparityWilsonImplDF> GparityWilsonTMFermionDF; typedef MobiusFermion<GparityWilsonImplR> GparityMobiusFermionR; typedef MobiusFermion<GparityWilsonImplF> GparityMobiusFermionF; typedef MobiusFermion<GparityWilsonImplD> GparityMobiusFermionD; -typedef MobiusFermion<GparityWilsonImplRL> GparityMobiusFermionRL; -typedef MobiusFermion<GparityWilsonImplFH> GparityMobiusFermionFH; -typedef MobiusFermion<GparityWilsonImplDF> GparityMobiusFermionDF; +//typedef MobiusFermion<GparityWilsonImplRL> GparityMobiusFermionRL; +//typedef MobiusFermion<GparityWilsonImplFH> GparityMobiusFermionFH; +//typedef MobiusFermion<GparityWilsonImplDF> GparityMobiusFermionDF; typedef MobiusEOFAFermion<GparityWilsonImplR> GparityMobiusEOFAFermionR; typedef MobiusEOFAFermion<GparityWilsonImplF> GparityMobiusEOFAFermionF; typedef MobiusEOFAFermion<GparityWilsonImplD> GparityMobiusEOFAFermionD; -typedef MobiusEOFAFermion<GparityWilsonImplRL> GparityMobiusEOFAFermionRL; -typedef MobiusEOFAFermion<GparityWilsonImplFH> GparityMobiusEOFAFermionFH; -typedef MobiusEOFAFermion<GparityWilsonImplDF> GparityMobiusEOFAFermionDF; +//typedef MobiusEOFAFermion<GparityWilsonImplRL> GparityMobiusEOFAFermionRL; +//typedef MobiusEOFAFermion<GparityWilsonImplFH> GparityMobiusEOFAFermionFH; +//typedef MobiusEOFAFermion<GparityWilsonImplDF> GparityMobiusEOFAFermionDF; typedef ImprovedStaggeredFermion<StaggeredImplR> ImprovedStaggeredFermionR; typedef ImprovedStaggeredFermion<StaggeredImplF> ImprovedStaggeredFermionF; From d1b0b7f5c64f7d051b278ec7c67eda802906e223 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sat, 11 Sep 2021 23:05:40 +0100 Subject: [PATCH 200/399] Half prec comms dropping --- Grid/qcd/action/fermion/GparityWilsonImpl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/action/fermion/GparityWilsonImpl.h b/Grid/qcd/action/fermion/GparityWilsonImpl.h index 9dca403b..fd627aed 100644 --- a/Grid/qcd/action/fermion/GparityWilsonImpl.h +++ b/Grid/qcd/action/fermion/GparityWilsonImpl.h @@ -327,8 +327,8 @@ typedef GparityWilsonImpl<vComplex , FundamentalRepresentation,CoeffReal> Gparit typedef GparityWilsonImpl<vComplexF, FundamentalRepresentation,CoeffReal> GparityWilsonImplF; // Float typedef GparityWilsonImpl<vComplexD, FundamentalRepresentation,CoeffReal> GparityWilsonImplD; // Double -typedef GparityWilsonImpl<vComplex , FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplRL; // Real.. whichever prec -typedef GparityWilsonImpl<vComplexF, FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplFH; // Float -typedef GparityWilsonImpl<vComplexD, FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplDF; // Double +//typedef GparityWilsonImpl<vComplex , FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplRL; // Real.. whichever prec +//typedef GparityWilsonImpl<vComplexF, FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplFH; // Float +//typedef GparityWilsonImpl<vComplexD, FundamentalRepresentation,CoeffRealHalfComms> GparityWilsonImplDF; // Double NAMESPACE_END(Grid); From 73b944c1526491b5625ca8c19b341f135d62427f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sat, 11 Sep 2021 23:07:18 +0100 Subject: [PATCH 201/399] Drop half prec comms for now. --- Grid/qcd/action/fermion/WilsonImpl.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonImpl.h b/Grid/qcd/action/fermion/WilsonImpl.h index 2ff6feba..2685796d 100644 --- a/Grid/qcd/action/fermion/WilsonImpl.h +++ b/Grid/qcd/action/fermion/WilsonImpl.h @@ -243,17 +243,17 @@ typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffReal > WilsonImplR typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffReal > WilsonImplF; // Float typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffReal > WilsonImplD; // Double -typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffRealHalfComms > WilsonImplRL; // Real.. whichever prec -typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffRealHalfComms > WilsonImplFH; // Float -typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffRealHalfComms > WilsonImplDF; // Double +//typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffRealHalfComms > WilsonImplRL; // Real.. whichever prec +//typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffRealHalfComms > WilsonImplFH; // Float +//typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffRealHalfComms > WilsonImplDF; // Double typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffComplex > ZWilsonImplR; // Real.. whichever prec typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffComplex > ZWilsonImplF; // Float typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffComplex > ZWilsonImplD; // Double -typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffComplexHalfComms > ZWilsonImplRL; // Real.. whichever prec -typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffComplexHalfComms > ZWilsonImplFH; // Float -typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffComplexHalfComms > ZWilsonImplDF; // Double +//typedef WilsonImpl<vComplex, FundamentalRepresentation, CoeffComplexHalfComms > ZWilsonImplRL; // Real.. whichever prec +//typedef WilsonImpl<vComplexF, FundamentalRepresentation, CoeffComplexHalfComms > ZWilsonImplFH; // Float +//typedef WilsonImpl<vComplexD, FundamentalRepresentation, CoeffComplexHalfComms > ZWilsonImplDF; // Double typedef WilsonImpl<vComplex, AdjointRepresentation, CoeffReal > WilsonAdjImplR; // Real.. whichever prec typedef WilsonImpl<vComplexF, AdjointRepresentation, CoeffReal > WilsonAdjImplF; // Float From 4c88104a73871551050484fac521d49aec8a6de9 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sat, 11 Sep 2021 23:08:05 +0100 Subject: [PATCH 202/399] Fix compile warns --- .../fermion/implementation/CayleyFermion5DImplementation.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index c3e0f821..1ed66bda 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -880,7 +880,7 @@ void CayleyFermion5D<Impl>::SeqConservedCurrent(PropagatorField &q_in, } std::vector<RealD> G_s(Ls,1.0); - Integer sign = 1; // sign flip for vector/tadpole + RealD sign = 1; // sign flip for vector/tadpole if ( curr_type == Current::Axial ) { for(int s=0;s<Ls/2;s++){ G_s[s] = -1.0; @@ -901,8 +901,8 @@ void CayleyFermion5D<Impl>::SeqConservedCurrent(PropagatorField &q_in, for(int s=0;s<Ls;s++){ int sp = (s+1)%Ls; - int sr = Ls-1-s; - int srp= (sr+1)%Ls; + // int sr = Ls-1-s; + // int srp= (sr+1)%Ls; // Mobius parameters auto b=this->bs[s]; From 8195890640c37324757b864b472d17bc23408402 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 05:00:17 +0100 Subject: [PATCH 203/399] Force MPI over NVLINK --- Grid/communicator/Communicator_base.h | 6 ++++++ Grid/communicator/Communicator_mpi3.cc | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Grid/communicator/Communicator_base.h b/Grid/communicator/Communicator_base.h index a15f9789..4510b03e 100644 --- a/Grid/communicator/Communicator_base.h +++ b/Grid/communicator/Communicator_base.h @@ -35,6 +35,12 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> NAMESPACE_BEGIN(Grid); +#ifdef GRID_MPI3_SHM_NVLINK +const bool Stencil_force_mpi = true; +#else +const bool Stencil_force_mpi = false; +#endif + class CartesianCommunicator : public SharedMemory { public: diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index 5713fe35..a6eeeebc 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -370,7 +370,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques double off_node_bytes=0.0; int tag; - if ( gfrom ==MPI_UNDEFINED) { + if ( (gfrom ==MPI_UNDEFINED) || Stencil_force_mpi ) { tag= dir+from*32; ierr=MPI_Irecv(recv, bytes, MPI_CHAR,from,tag,communicator_halo[commdir],&rrq); assert(ierr==0); @@ -378,7 +378,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques off_node_bytes+=bytes; } - if ( gdest == MPI_UNDEFINED ) { + if ( (gdest == MPI_UNDEFINED) || Stencil_force_mpi ) { tag= dir+_processor*32; ierr =MPI_Isend(xmit, bytes, MPI_CHAR,dest,tag,communicator_halo[commdir],&xrq); assert(ierr==0); From 0fc662bb2455a4637de3cbf78cb07f7657cdf28f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 05:00:44 +0100 Subject: [PATCH 204/399] Dirac cuda 11.4 happy ; force host for functions accessing mult table ET runs these on host BEFORE lodging result in AST for kernel --- Grid/qcd/spin/Dirac.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/spin/Dirac.h b/Grid/qcd/spin/Dirac.h index d03e0939..2f2a9732 100644 --- a/Grid/qcd/spin/Dirac.h +++ b/Grid/qcd/spin/Dirac.h @@ -40,7 +40,7 @@ See the full license in the file "LICENSE" in the top level distribution directo NAMESPACE_BEGIN(Grid); // Dirac algebra adjoint operator (not in to overload other adj) -accelerator_inline Gamma adj(const Gamma &g) +inline Gamma adj(const Gamma &g) { return Gamma (Gamma::adj[g.g]); } @@ -48,7 +48,7 @@ accelerator_inline Gamma adj(const Gamma &g) // Dirac algebra mutliplication operator -accelerator_inline Gamma operator*(const Gamma &g1, const Gamma &g2) +inline Gamma operator*(const Gamma &g1, const Gamma &g2) { return Gamma (Gamma::mul[g1.g][g2.g]); } From 7440cde92f25f2a9176b725910c15d748dbbd27c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 05:04:56 +0100 Subject: [PATCH 205/399] No half prec comms; coalesced access on GPU --- Grid/qcd/action/fermion/WilsonCompressor.h | 98 +++++++++++----------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonCompressor.h b/Grid/qcd/action/fermion/WilsonCompressor.h index 0760bcba..e0e08c1c 100644 --- a/Grid/qcd/action/fermion/WilsonCompressor.h +++ b/Grid/qcd/action/fermion/WilsonCompressor.h @@ -68,11 +68,12 @@ public: /*****************************************************/ /* Compress includes precision change if mpi data is not same */ /*****************************************************/ - template<class _SiteHalfSpinor, class _SiteSpinor> - accelerator_inline void Compress(_SiteHalfSpinor *buf,Integer o,const _SiteSpinor &in) const { - _SiteHalfSpinor tmp; - projector::Proj(tmp,in,mu,dag); - vstream(buf[o],tmp); + accelerator_inline void Compress(SiteHalfSpinor &buf,const SiteSpinor &in) const { + typedef decltype(coalescedRead(buf)) sobj; + sobj sp; + auto sin = coalescedRead(in); + projector::Proj(sp,sin,mu,dag); + coalescedWrite(buf,sp); } /*****************************************************/ @@ -82,13 +83,18 @@ public: const SiteHalfSpinor * __restrict__ vp0, const SiteHalfSpinor * __restrict__ vp1, Integer type,Integer o) const { +#ifdef GRID_SIMT + exchangeSIMT(mp[2*o],mp[2*o+1],vp0[o],vp1[o],type); +#else SiteHalfSpinor tmp1; SiteHalfSpinor tmp2; exchange(tmp1,tmp2,vp0[o],vp1[o],type); vstream(mp[2*o ],tmp1); vstream(mp[2*o+1],tmp2); +#endif } + /*****************************************************/ /* Have a decompression step if mpi data is not same */ /*****************************************************/ @@ -105,6 +111,28 @@ public: const SiteSpinor * __restrict__ in, Integer j,Integer k, Integer m,Integer type) const { +#ifdef GRID_SIMT + typedef SiteSpinor vobj; + typedef SiteHalfSpinor hvobj; + typedef decltype(coalescedRead(*in)) sobj; + typedef decltype(coalescedRead(*out0)) hsobj; + + unsigned int Nsimd = vobj::Nsimd(); + unsigned int mask = Nsimd >> (type + 1); + int lane = acceleratorSIMTlane(Nsimd); + int j0 = lane &(~mask); // inner coor zero + int j1 = lane |(mask) ; // inner coor one + const vobj *vp0 = &in[k]; + const vobj *vp1 = &in[m]; + const vobj *vp = (lane&mask) ? vp1:vp0; + auto sa = coalescedRead(*vp,j0); + auto sb = coalescedRead(*vp,j1); + hsobj psa, psb; + projector::Proj(psa,sa,mu,dag); + projector::Proj(psb,sb,mu,dag); + coalescedWrite(out0[j],psa); + coalescedWrite(out1[j],psb); +#else SiteHalfSpinor temp1, temp2; SiteHalfSpinor temp3, temp4; projector::Proj(temp1,in[k],mu,dag); @@ -112,6 +140,7 @@ public: exchange(temp3,temp4,temp1,temp2,type); vstream(out0[j],temp3); vstream(out1[j],temp4); +#endif } /*****************************************************/ @@ -121,6 +150,7 @@ public: }; +#if 0 template<class _HCspinor,class _Hspinor,class _Spinor, class projector> class WilsonCompressorTemplate< _HCspinor, _Hspinor, _Spinor, projector, typename std::enable_if<!std::is_same<_HCspinor,_Hspinor>::value>::type > @@ -149,13 +179,23 @@ public: /*****************************************************/ /* Compress includes precision change if mpi data is not same */ /*****************************************************/ - template<class _SiteHalfSpinor, class _SiteSpinor> - accelerator_inline void Compress(_SiteHalfSpinor *buf,Integer o,const _SiteSpinor &in) const { - _SiteHalfSpinor hsp; + accelerator_inline void Compress(SiteHalfSpinor &buf,const SiteSpinor &in) const { + SiteHalfSpinor hsp; SiteHalfCommSpinor *hbuf = (SiteHalfCommSpinor *)buf; projector::Proj(hsp,in,mu,dag); precisionChange((vComplexLow *)&hbuf[o],(vComplexHigh *)&hsp,Nw); } + accelerator_inline void Compress(SiteHalfSpinor &buf,const SiteSpinor &in) const { +#ifdef GRID_SIMT + typedef decltype(coalescedRead(buf)) sobj; + sobj sp; + auto sin = coalescedRead(in); + projector::Proj(sp,sin,mu,dag); + coalescedWrite(buf,sp); +#else + projector::Proj(buf,in,mu,dag); +#endif + } /*****************************************************/ /* Exchange includes precision change if mpi data is not same */ @@ -203,6 +243,7 @@ public: accelerator_inline bool DecompressionStep(void) const { return true; } }; +#endif #define DECLARE_PROJ(Projector,Compressor,spProj) \ class Projector { \ @@ -253,33 +294,8 @@ public: typedef typename Base::View_type View_type; typedef typename Base::StencilVector StencilVector; - double timer0; - double timer1; - double timer2; - double timer3; - double timer4; - double timer5; - double timer6; - uint64_t callsi; - void ZeroCountersi(void) - { - timer0=0; - timer1=0; - timer2=0; - timer3=0; - timer4=0; - timer5=0; - timer6=0; - callsi=0; - } - void Reporti(int calls) - { - if ( timer0 ) std::cout << GridLogMessage << " timer0 (HaloGatherOpt) " <<timer0/calls <<std::endl; - if ( timer1 ) std::cout << GridLogMessage << " timer1 (Communicate) " <<timer1/calls <<std::endl; - if ( timer2 ) std::cout << GridLogMessage << " timer2 (CommsMerge ) " <<timer2/calls <<std::endl; - if ( timer3 ) std::cout << GridLogMessage << " timer3 (commsMergeShm) " <<timer3/calls <<std::endl; - if ( timer4 ) std::cout << GridLogMessage << " timer4 " <<timer4 <<std::endl; - } + void ZeroCountersi(void) { } + void Reporti(int calls) { } std::vector<int> surface_list; @@ -321,26 +337,18 @@ public: { std::vector<std::vector<CommsRequest_t> > reqs; this->HaloExchangeOptGather(source,compress); - double t1=usecond(); // Asynchronous MPI calls multidirectional, Isend etc... // Non-overlapped directions within a thread. Asynchronous calls except MPI3, threaded up to comm threads ways. this->Communicate(); - double t2=usecond(); timer1 += t2-t1; this->CommsMerge(compress); - double t3=usecond(); timer2 += t3-t2; this->CommsMergeSHM(compress); - double t4=usecond(); timer3 += t4-t3; } template <class compressor> void HaloExchangeOptGather(const Lattice<vobj> &source,compressor &compress) { this->Prepare(); - double t0=usecond(); this->HaloGatherOpt(source,compress); - double t1=usecond(); - timer0 += t1-t0; - callsi++; } template <class compressor> @@ -352,12 +360,9 @@ public: typedef typename compressor::SiteHalfSpinor SiteHalfSpinor; typedef typename compressor::SiteHalfCommSpinor SiteHalfCommSpinor; - this->mpi3synctime_g-=usecond(); this->_grid->StencilBarrier(); - this->mpi3synctime_g+=usecond(); assert(source.Grid()==this->_grid); - this->halogtime-=usecond(); this->u_comm_offset=0; @@ -393,7 +398,6 @@ public: } this->face_table_computed=1; assert(this->u_comm_offset==this->_unified_buffer_size); - this->halogtime+=usecond(); accelerator_barrier(); } From a7b943b33e262d528df5074e95e1f45b5a9b059c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 05:05:33 +0100 Subject: [PATCH 206/399] Remove half prec comms --- .../instantiation/generate_instantiations.sh | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh b/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh index 72a9eaf9..d7553cdb 100755 --- a/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh +++ b/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh @@ -9,8 +9,6 @@ STAG5_IMPL_LIST="" WILSON_IMPL_LIST=" \ WilsonImplF \ WilsonImplD \ - WilsonImplFH \ - WilsonImplDF \ WilsonAdjImplF \ WilsonAdjImplD \ WilsonTwoIndexSymmetricImplF \ @@ -18,26 +16,17 @@ WILSON_IMPL_LIST=" \ WilsonTwoIndexAntiSymmetricImplF \ WilsonTwoIndexAntiSymmetricImplD \ GparityWilsonImplF \ - GparityWilsonImplD \ - GparityWilsonImplFH \ - GparityWilsonImplDF" + GparityWilsonImplD " DWF_IMPL_LIST=" \ WilsonImplF \ WilsonImplD \ - WilsonImplFH \ - WilsonImplDF \ ZWilsonImplF \ - ZWilsonImplD \ - ZWilsonImplFH \ - ZWilsonImplDF " + ZWilsonImplD " GDWF_IMPL_LIST=" \ GparityWilsonImplF \ - GparityWilsonImplD \ - GparityWilsonImplFH \ - GparityWilsonImplDF" - + GparityWilsonImplD " IMPL_LIST="$STAG_IMPL_LIST $WILSON_IMPL_LIST $DWF_IMPL_LIST $GDWF_IMPL_LIST" From d5835c0222aca3962bff100c0331b79f30c75254 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 15:04:14 +0100 Subject: [PATCH 207/399] Switch to coalesced stencil face gather --- Grid/stencil/SimpleCompressor.h | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/Grid/stencil/SimpleCompressor.h b/Grid/stencil/SimpleCompressor.h index 2ce48369..1150b234 100644 --- a/Grid/stencil/SimpleCompressor.h +++ b/Grid/stencil/SimpleCompressor.h @@ -3,20 +3,48 @@ NAMESPACE_BEGIN(Grid); +template<class vobj> +accelerator_inline void exchangeSIMT(vobj &mp0,vobj &mp1,const vobj &vp0,const vobj &vp1,Integer type) +{ + typedef decltype(coalescedRead(mp0)) sobj; + unsigned int Nsimd = vobj::Nsimd(); + unsigned int mask = Nsimd >> (type + 1); + int lane = acceleratorSIMTlane(Nsimd); + int j0 = lane &(~mask); // inner coor zero + int j1 = lane |(mask) ; // inner coor one + const vobj *vpa = &vp0; + const vobj *vpb = &vp1; + const vobj *vp = (lane&mask) ? (vpb) : (vpa); + auto sa = coalescedRead(vp[0],j0); + auto sb = coalescedRead(vp[0],j1); + coalescedWrite(mp0,sa); + coalescedWrite(mp1,sb); +} + template<class vobj> class SimpleCompressor { public: void Point(int) {}; accelerator_inline int CommDatumSize(void) const { return sizeof(vobj); } accelerator_inline bool DecompressionStep(void) const { return false; } - template<class cobj> accelerator_inline void Compress(cobj *buf,int o,const cobj &in) const { buf[o]=in; } + accelerator_inline void Compress(vobj &buf,const vobj &in) const { + coalescedWrite(buf,coalescedRead(in)); + } accelerator_inline void Exchange(vobj *mp,vobj *vp0,vobj *vp1,Integer type,Integer o) const { +#ifdef GRID_SIMT + exchangeSIMT(mp[2*o],mp[2*o+1],vp0[o],vp1[o],type); +#else exchange(mp[2*o],mp[2*o+1],vp0[o],vp1[o],type); +#endif } accelerator_inline void Decompress(vobj *out,vobj *in, int o) const { assert(0); } accelerator_inline void CompressExchange(vobj *out0,vobj *out1,const vobj *in, - int j,int k, int m,int type) const { + int j,int k, int m,int type) const { +#ifdef GRID_SIMT + exchangeSIMT(out0[j],out1[j],in[k],in[m],type); +#else exchange(out0[j],out1[j],in[k],in[m],type); +#endif } // For cshift. Cshift should drop compressor coupling altogether // because I had to decouple the code from the Stencil anyway From 65ef4ec29fd628bedcfe3c6399dcc642ad8e18a0 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 15:05:01 +0100 Subject: [PATCH 208/399] Move tables to device memory --- Grid/stencil/Stencil.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/stencil/Stencil.cc b/Grid/stencil/Stencil.cc index 5b1bb2ea..c1b33baa 100644 --- a/Grid/stencil/Stencil.cc +++ b/Grid/stencil/Stencil.cc @@ -30,7 +30,7 @@ NAMESPACE_BEGIN(Grid); void Gather_plane_table_compute (GridBase *grid,int dimension,int plane,int cbmask, - int off,Vector<std::pair<int,int> > & table) + int off,std::vector<std::pair<int,int> > & table) { table.resize(0); From 7efdb3cd2b84b83bd8508d682071d6c7ecf144f9 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 15:06:06 +0100 Subject: [PATCH 209/399] Remove half prec comms --- benchmarks/Benchmark_dwf.cc | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/benchmarks/Benchmark_dwf.cc b/benchmarks/Benchmark_dwf.cc index d7b49122..707f330c 100644 --- a/benchmarks/Benchmark_dwf.cc +++ b/benchmarks/Benchmark_dwf.cc @@ -236,34 +236,6 @@ int main (int argc, char ** argv) Dw.Report(); } - DomainWallFermionRL DwH(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5); - if (0) { - FGrid->Barrier(); - DwH.ZeroCounters(); - DwH.Dhop(src,result,0); - double t0=usecond(); - for(int i=0;i<ncall;i++){ - __SSC_START; - DwH.Dhop(src,result,0); - __SSC_STOP; - } - double t1=usecond(); - FGrid->Barrier(); - - double volume=Ls; for(int mu=0;mu<Nd;mu++) volume=volume*latt4[mu]; - double flops=single_site_flops*volume*ncall; - - std::cout<<GridLogMessage << "Called half prec comms Dw "<<ncall<<" times in "<<t1-t0<<" us"<<std::endl; - std::cout<<GridLogMessage << "mflop/s = "<< flops/(t1-t0)<<std::endl; - std::cout<<GridLogMessage << "mflop/s per rank = "<< flops/(t1-t0)/NP<<std::endl; - std::cout<<GridLogMessage << "mflop/s per node = "<< flops/(t1-t0)/NN<<std::endl; - err = ref-result; - std::cout<<GridLogMessage << "norm diff "<< norm2(err)<<std::endl; - - assert (norm2(err)< 1.0e-3 ); - DwH.Report(); - } - if (1) { // Naive wilson dag implementation ref = Zero(); From 361bb8a1018755423f584316363a3cdc16137b86 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 15:06:29 +0100 Subject: [PATCH 210/399] Remove half prec comms --- benchmarks/Benchmark_gparity.cc | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/benchmarks/Benchmark_gparity.cc b/benchmarks/Benchmark_gparity.cc index 2045d650..ce84ecbc 100644 --- a/benchmarks/Benchmark_gparity.cc +++ b/benchmarks/Benchmark_gparity.cc @@ -2,7 +2,6 @@ #include <sstream> using namespace std; using namespace Grid; - ; template<class d> struct scal { @@ -118,30 +117,6 @@ int main (int argc, char ** argv) Dw.Report(); } - std::cout << GridLogMessage<< "* SINGLE/HALF"<<std::endl; - GparityDomainWallFermionFH DwH(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5); - if (1) { - FGrid->Barrier(); - DwH.ZeroCounters(); - DwH.Dhop(src,result,0); - double t0=usecond(); - for(int i=0;i<ncall;i++){ - __SSC_START; - DwH.Dhop(src,result,0); - __SSC_STOP; - } - double t1=usecond(); - FGrid->Barrier(); - - double volume=Ls; for(int mu=0;mu<Nd;mu++) volume=volume*latt4[mu]; - double flops=2*1320*volume*ncall; - - std::cout<<GridLogMessage << "Called half prec comms Dw "<<ncall<<" times in "<<t1-t0<<" us"<<std::endl; - std::cout<<GridLogMessage << "mflop/s = "<< flops/(t1-t0)<<std::endl; - std::cout<<GridLogMessage << "mflop/s per rank = "<< flops/(t1-t0)/NP<<std::endl; - std::cout<<GridLogMessage << "mflop/s per node = "<< flops/(t1-t0)/NN<<std::endl; - DwH.Report(); - } GridCartesian * UGrid_d = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplexD::Nsimd()),GridDefaultMpi()); GridRedBlackCartesian * UrbGrid_d = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid_d); From 5dae6a6dacc73752f6bd6bc0440db73656da4391 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 15:06:59 +0100 Subject: [PATCH 211/399] Deprecate half prec comms --- tests/Test_dwf_mixedcg_prec_halfcomms.cc | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/Test_dwf_mixedcg_prec_halfcomms.cc b/tests/Test_dwf_mixedcg_prec_halfcomms.cc index 8b0126dc..ff52b0d1 100644 --- a/tests/Test_dwf_mixedcg_prec_halfcomms.cc +++ b/tests/Test_dwf_mixedcg_prec_halfcomms.cc @@ -29,19 +29,12 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> using namespace std; using namespace Grid; - ; -template<class d> -struct scal { - d internal; -}; +#if 1 +int main (int argc, char ** argv) {} + +#else - Gamma::Algebra Gmu [] = { - Gamma::Algebra::GammaX, - Gamma::Algebra::GammaY, - Gamma::Algebra::GammaZ, - Gamma::Algebra::GammaT - }; int main (int argc, char ** argv) { @@ -124,3 +117,4 @@ int main (int argc, char ** argv) Grid_finalize(); } +#endif From 86e33c8ab23fb4e91f3aedaa318bf4b12733157f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 14 Sep 2021 16:14:23 +0100 Subject: [PATCH 212/399] Significant GPU perf speed up finished --- ...rmion5DInstantiationGparityWilsonImplDF.cc | 1 - ...rmion5DInstantiationGparityWilsonImplDF.cc | 1 - ...FermionInstantiationGparityWilsonImplDF.cc | 1 - ...FermionInstantiationGparityWilsonImplDF.cc | 1 - ...rmion5DInstantiationGparityWilsonImplDF.cc | 1 - ...FermionInstantiationGparityWilsonImplDF.cc | 1 - ...rmion5DInstantiationGparityWilsonImplDF.cc | 1 - ...FermionInstantiationGparityWilsonImplDF.cc | 1 - ...KernelsInstantiationGparityWilsonImplDF.cc | 1 - ...FermionInstantiationGparityWilsonImplDF.cc | 1 - .../instantiation/GparityWilsonImplDF/impl.h | 1 - ...rmion5DInstantiationGparityWilsonImplFH.cc | 1 - ...rmion5DInstantiationGparityWilsonImplFH.cc | 1 - ...FermionInstantiationGparityWilsonImplFH.cc | 1 - ...FermionInstantiationGparityWilsonImplFH.cc | 1 - ...rmion5DInstantiationGparityWilsonImplFH.cc | 1 - ...FermionInstantiationGparityWilsonImplFH.cc | 1 - ...rmion5DInstantiationGparityWilsonImplFH.cc | 1 - ...FermionInstantiationGparityWilsonImplFH.cc | 1 - ...KernelsInstantiationGparityWilsonImplFH.cc | 1 - ...FermionInstantiationGparityWilsonImplFH.cc | 1 - .../instantiation/GparityWilsonImplFH/impl.h | 1 - ...ayleyFermion5DInstantiationWilsonImplDF.cc | 1 - ...ctionFermion5DInstantiationWilsonImplDF.cc | 1 - ...allEOFAFermionInstantiationWilsonImplDF.cc | 1 - ...iusEOFAFermionInstantiationWilsonImplDF.cc | 1 - ...ctionFermion5DInstantiationWilsonImplDF.cc | 1 - ...nCloverFermionInstantiationWilsonImplDF.cc | 1 - ...ilsonFermion5DInstantiationWilsonImplDF.cc | 1 - .../WilsonFermionInstantiationWilsonImplDF.cc | 1 - .../WilsonKernelsInstantiationWilsonImplDF.cc | 51 ------------------ ...ilsonTMFermionInstantiationWilsonImplDF.cc | 1 - .../fermion/instantiation/WilsonImplDF/impl.h | 1 - ...ayleyFermion5DInstantiationWilsonImplFH.cc | 1 - ...ctionFermion5DInstantiationWilsonImplFH.cc | 1 - ...allEOFAFermionInstantiationWilsonImplFH.cc | 1 - ...iusEOFAFermionInstantiationWilsonImplFH.cc | 1 - ...ctionFermion5DInstantiationWilsonImplFH.cc | 1 - ...nCloverFermionInstantiationWilsonImplFH.cc | 1 - ...ilsonFermion5DInstantiationWilsonImplFH.cc | 1 - .../WilsonFermionInstantiationWilsonImplFH.cc | 1 - .../WilsonKernelsInstantiationWilsonImplFH.cc | 51 ------------------ ...ilsonTMFermionInstantiationWilsonImplFH.cc | 1 - .../fermion/instantiation/WilsonImplFH/impl.h | 1 - ...yleyFermion5DInstantiationZWilsonImplDF.cc | 1 - ...tionFermion5DInstantiationZWilsonImplDF.cc | 1 - ...llEOFAFermionInstantiationZWilsonImplDF.cc | 1 - ...usEOFAFermionInstantiationZWilsonImplDF.cc | 1 - ...tionFermion5DInstantiationZWilsonImplDF.cc | 1 - ...lsonFermion5DInstantiationZWilsonImplDF.cc | 1 - ...WilsonKernelsInstantiationZWilsonImplDF.cc | 51 ------------------ .../instantiation/ZWilsonImplDF/impl.h | 1 - ...yleyFermion5DInstantiationZWilsonImplFH.cc | 1 - ...tionFermion5DInstantiationZWilsonImplFH.cc | 1 - ...llEOFAFermionInstantiationZWilsonImplFH.cc | 1 - ...usEOFAFermionInstantiationZWilsonImplFH.cc | 1 - ...tionFermion5DInstantiationZWilsonImplFH.cc | 1 - ...lsonFermion5DInstantiationZWilsonImplFH.cc | 1 - ...WilsonKernelsInstantiationZWilsonImplFH.cc | 51 ------------------ .../instantiation/ZWilsonImplFH/impl.h | 1 - Grid/stencil/Stencil.h | 53 +++++++++++-------- 61 files changed, 31 insertions(+), 282 deletions(-) delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/CayleyFermion5DInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/ContinuedFractionFermion5DInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/DomainWallEOFAFermionInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/MobiusEOFAFermionInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/PartialFractionFermion5DInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonCloverFermionInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermion5DInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermionInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonKernelsInstantiationGparityWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonTMFermionInstantiationGparityWilsonImplDF.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/impl.h delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/CayleyFermion5DInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/ContinuedFractionFermion5DInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/DomainWallEOFAFermionInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/MobiusEOFAFermionInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/PartialFractionFermion5DInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonCloverFermionInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermion5DInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermionInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonKernelsInstantiationGparityWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonTMFermionInstantiationGparityWilsonImplFH.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/impl.h delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/CayleyFermion5DInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/ContinuedFractionFermion5DInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/DomainWallEOFAFermionInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/MobiusEOFAFermionInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/PartialFractionFermion5DInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonCloverFermionInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermion5DInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermionInstantiationWilsonImplDF.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonKernelsInstantiationWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonTMFermionInstantiationWilsonImplDF.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/WilsonImplDF/impl.h delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/CayleyFermion5DInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/ContinuedFractionFermion5DInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/DomainWallEOFAFermionInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/MobiusEOFAFermionInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/PartialFractionFermion5DInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonCloverFermionInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermion5DInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermionInstantiationWilsonImplFH.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonKernelsInstantiationWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonTMFermionInstantiationWilsonImplFH.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/WilsonImplFH/impl.h delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/CayleyFermion5DInstantiationZWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/ContinuedFractionFermion5DInstantiationZWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/DomainWallEOFAFermionInstantiationZWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/MobiusEOFAFermionInstantiationZWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/PartialFractionFermion5DInstantiationZWilsonImplDF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonFermion5DInstantiationZWilsonImplDF.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonKernelsInstantiationZWilsonImplDF.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/impl.h delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/CayleyFermion5DInstantiationZWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/ContinuedFractionFermion5DInstantiationZWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/DomainWallEOFAFermionInstantiationZWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/MobiusEOFAFermionInstantiationZWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/PartialFractionFermion5DInstantiationZWilsonImplFH.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonFermion5DInstantiationZWilsonImplFH.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonKernelsInstantiationZWilsonImplFH.cc delete mode 100644 Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/impl.h diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/CayleyFermion5DInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/CayleyFermion5DInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index cb1db625..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/CayleyFermion5DInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../CayleyFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/ContinuedFractionFermion5DInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/ContinuedFractionFermion5DInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index c2d4b8fc..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/ContinuedFractionFermion5DInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../ContinuedFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/DomainWallEOFAFermionInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/DomainWallEOFAFermionInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index 2f550a2b..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/DomainWallEOFAFermionInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../DomainWallEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/MobiusEOFAFermionInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/MobiusEOFAFermionInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index 7a8f1172..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/MobiusEOFAFermionInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../MobiusEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/PartialFractionFermion5DInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/PartialFractionFermion5DInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index 7f4cea71..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/PartialFractionFermion5DInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../PartialFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonCloverFermionInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonCloverFermionInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index 9cc05107..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonCloverFermionInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermion5DInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermion5DInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index 804d0884..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermion5DInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermionInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermionInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index 5f6ab65e..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonFermionInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonKernelsInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonKernelsInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index 87adea48..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonKernelsInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonKernelsInstantiationGparity.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonTMFermionInstantiationGparityWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonTMFermionInstantiationGparityWilsonImplDF.cc deleted file mode 120000 index d5789bcf..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/WilsonTMFermionInstantiationGparityWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonTMFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/impl.h b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/impl.h deleted file mode 100644 index 2f13ce8a..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplDF/impl.h +++ /dev/null @@ -1 +0,0 @@ -#define IMPLEMENTATION GparityWilsonImplDF diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/CayleyFermion5DInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/CayleyFermion5DInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index cb1db625..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/CayleyFermion5DInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../CayleyFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/ContinuedFractionFermion5DInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/ContinuedFractionFermion5DInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index c2d4b8fc..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/ContinuedFractionFermion5DInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../ContinuedFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/DomainWallEOFAFermionInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/DomainWallEOFAFermionInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index 2f550a2b..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/DomainWallEOFAFermionInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../DomainWallEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/MobiusEOFAFermionInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/MobiusEOFAFermionInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index 7a8f1172..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/MobiusEOFAFermionInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../MobiusEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/PartialFractionFermion5DInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/PartialFractionFermion5DInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index 7f4cea71..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/PartialFractionFermion5DInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../PartialFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonCloverFermionInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonCloverFermionInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index 9cc05107..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonCloverFermionInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermion5DInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermion5DInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index 804d0884..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermion5DInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermionInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermionInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index 5f6ab65e..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonFermionInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonKernelsInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonKernelsInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index 87adea48..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonKernelsInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonKernelsInstantiationGparity.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonTMFermionInstantiationGparityWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonTMFermionInstantiationGparityWilsonImplFH.cc deleted file mode 120000 index d5789bcf..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/WilsonTMFermionInstantiationGparityWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonTMFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/impl.h b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/impl.h deleted file mode 100644 index ebcb6e62..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplFH/impl.h +++ /dev/null @@ -1 +0,0 @@ -#define IMPLEMENTATION GparityWilsonImplFH diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/CayleyFermion5DInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/CayleyFermion5DInstantiationWilsonImplDF.cc deleted file mode 120000 index cb1db625..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/CayleyFermion5DInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../CayleyFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/ContinuedFractionFermion5DInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/ContinuedFractionFermion5DInstantiationWilsonImplDF.cc deleted file mode 120000 index c2d4b8fc..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/ContinuedFractionFermion5DInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../ContinuedFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/DomainWallEOFAFermionInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/DomainWallEOFAFermionInstantiationWilsonImplDF.cc deleted file mode 120000 index 2f550a2b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/DomainWallEOFAFermionInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../DomainWallEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/MobiusEOFAFermionInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/MobiusEOFAFermionInstantiationWilsonImplDF.cc deleted file mode 120000 index 7a8f1172..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/MobiusEOFAFermionInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../MobiusEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/PartialFractionFermion5DInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/PartialFractionFermion5DInstantiationWilsonImplDF.cc deleted file mode 120000 index 7f4cea71..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/PartialFractionFermion5DInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../PartialFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonCloverFermionInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonCloverFermionInstantiationWilsonImplDF.cc deleted file mode 120000 index 9cc05107..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonCloverFermionInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermion5DInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermion5DInstantiationWilsonImplDF.cc deleted file mode 120000 index 804d0884..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermion5DInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermionInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermionInstantiationWilsonImplDF.cc deleted file mode 120000 index 5f6ab65e..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonFermionInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonKernelsInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonKernelsInstantiationWilsonImplDF.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonKernelsInstantiationWilsonImplDF.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonTMFermionInstantiationWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonTMFermionInstantiationWilsonImplDF.cc deleted file mode 120000 index d5789bcf..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/WilsonTMFermionInstantiationWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonTMFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/impl.h b/Grid/qcd/action/fermion/instantiation/WilsonImplDF/impl.h deleted file mode 100644 index 2adc6136..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplDF/impl.h +++ /dev/null @@ -1 +0,0 @@ -#define IMPLEMENTATION WilsonImplDF diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/CayleyFermion5DInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/CayleyFermion5DInstantiationWilsonImplFH.cc deleted file mode 120000 index cb1db625..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/CayleyFermion5DInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../CayleyFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/ContinuedFractionFermion5DInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/ContinuedFractionFermion5DInstantiationWilsonImplFH.cc deleted file mode 120000 index c2d4b8fc..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/ContinuedFractionFermion5DInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../ContinuedFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/DomainWallEOFAFermionInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/DomainWallEOFAFermionInstantiationWilsonImplFH.cc deleted file mode 120000 index 2f550a2b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/DomainWallEOFAFermionInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../DomainWallEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/MobiusEOFAFermionInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/MobiusEOFAFermionInstantiationWilsonImplFH.cc deleted file mode 120000 index 7a8f1172..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/MobiusEOFAFermionInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../MobiusEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/PartialFractionFermion5DInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/PartialFractionFermion5DInstantiationWilsonImplFH.cc deleted file mode 120000 index 7f4cea71..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/PartialFractionFermion5DInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../PartialFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonCloverFermionInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonCloverFermionInstantiationWilsonImplFH.cc deleted file mode 120000 index 9cc05107..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonCloverFermionInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermion5DInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermion5DInstantiationWilsonImplFH.cc deleted file mode 120000 index 804d0884..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermion5DInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermionInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermionInstantiationWilsonImplFH.cc deleted file mode 120000 index 5f6ab65e..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonFermionInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonKernelsInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonKernelsInstantiationWilsonImplFH.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonKernelsInstantiationWilsonImplFH.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonTMFermionInstantiationWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonTMFermionInstantiationWilsonImplFH.cc deleted file mode 120000 index d5789bcf..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/WilsonTMFermionInstantiationWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonTMFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/impl.h b/Grid/qcd/action/fermion/instantiation/WilsonImplFH/impl.h deleted file mode 100644 index e442863d..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplFH/impl.h +++ /dev/null @@ -1 +0,0 @@ -#define IMPLEMENTATION WilsonImplFH diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/CayleyFermion5DInstantiationZWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/CayleyFermion5DInstantiationZWilsonImplDF.cc deleted file mode 120000 index cb1db625..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/CayleyFermion5DInstantiationZWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../CayleyFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/ContinuedFractionFermion5DInstantiationZWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/ContinuedFractionFermion5DInstantiationZWilsonImplDF.cc deleted file mode 120000 index c2d4b8fc..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/ContinuedFractionFermion5DInstantiationZWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../ContinuedFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/DomainWallEOFAFermionInstantiationZWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/DomainWallEOFAFermionInstantiationZWilsonImplDF.cc deleted file mode 120000 index 2f550a2b..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/DomainWallEOFAFermionInstantiationZWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../DomainWallEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/MobiusEOFAFermionInstantiationZWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/MobiusEOFAFermionInstantiationZWilsonImplDF.cc deleted file mode 120000 index 7a8f1172..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/MobiusEOFAFermionInstantiationZWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../MobiusEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/PartialFractionFermion5DInstantiationZWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/PartialFractionFermion5DInstantiationZWilsonImplDF.cc deleted file mode 120000 index 7f4cea71..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/PartialFractionFermion5DInstantiationZWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../PartialFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonFermion5DInstantiationZWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonFermion5DInstantiationZWilsonImplDF.cc deleted file mode 120000 index 804d0884..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonFermion5DInstantiationZWilsonImplDF.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonKernelsInstantiationZWilsonImplDF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonKernelsInstantiationZWilsonImplDF.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/WilsonKernelsInstantiationZWilsonImplDF.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/impl.h b/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/impl.h deleted file mode 100644 index 7daf76ef..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplDF/impl.h +++ /dev/null @@ -1 +0,0 @@ -#define IMPLEMENTATION ZWilsonImplDF diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/CayleyFermion5DInstantiationZWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/CayleyFermion5DInstantiationZWilsonImplFH.cc deleted file mode 120000 index cb1db625..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/CayleyFermion5DInstantiationZWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../CayleyFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/ContinuedFractionFermion5DInstantiationZWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/ContinuedFractionFermion5DInstantiationZWilsonImplFH.cc deleted file mode 120000 index c2d4b8fc..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/ContinuedFractionFermion5DInstantiationZWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../ContinuedFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/DomainWallEOFAFermionInstantiationZWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/DomainWallEOFAFermionInstantiationZWilsonImplFH.cc deleted file mode 120000 index 2f550a2b..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/DomainWallEOFAFermionInstantiationZWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../DomainWallEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/MobiusEOFAFermionInstantiationZWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/MobiusEOFAFermionInstantiationZWilsonImplFH.cc deleted file mode 120000 index 7a8f1172..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/MobiusEOFAFermionInstantiationZWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../MobiusEOFAFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/PartialFractionFermion5DInstantiationZWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/PartialFractionFermion5DInstantiationZWilsonImplFH.cc deleted file mode 120000 index 7f4cea71..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/PartialFractionFermion5DInstantiationZWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../PartialFractionFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonFermion5DInstantiationZWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonFermion5DInstantiationZWilsonImplFH.cc deleted file mode 120000 index 804d0884..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonFermion5DInstantiationZWilsonImplFH.cc +++ /dev/null @@ -1 +0,0 @@ -../WilsonFermion5DInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonKernelsInstantiationZWilsonImplFH.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonKernelsInstantiationZWilsonImplFH.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/WilsonKernelsInstantiationZWilsonImplFH.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/impl.h b/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/impl.h deleted file mode 100644 index 7eb490db..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplFH/impl.h +++ /dev/null @@ -1 +0,0 @@ -#define IMPLEMENTATION ZWilsonImplFH diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index 58cebed3..ca581804 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -57,27 +57,22 @@ NAMESPACE_BEGIN(Grid); /////////////////////////////////////////////////////////////////// void Gather_plane_table_compute (GridBase *grid,int dimension,int plane,int cbmask, - int off,Vector<std::pair<int,int> > & table); + int off,std::vector<std::pair<int,int> > & table); template<class vobj,class cobj,class compressor> -void Gather_plane_simple_table (Vector<std::pair<int,int> >& table,const Lattice<vobj> &rhs,cobj *buffer,compressor &compress, int off,int so) __attribute__((noinline)); +void Gather_plane_simple_table (commVector<std::pair<int,int> >& table,const Lattice<vobj> &rhs,cobj *buffer,compressor &compress, int off,int so) __attribute__((noinline)); template<class vobj,class cobj,class compressor> -void Gather_plane_simple_table (Vector<std::pair<int,int> >& table,const Lattice<vobj> &rhs,cobj *buffer,compressor &compress, int off,int so) +void Gather_plane_simple_table (commVector<std::pair<int,int> >& table,const Lattice<vobj> &rhs,cobj *buffer,compressor &compress, int off,int so) { int num=table.size(); std::pair<int,int> *table_v = & table[0]; auto rhs_v = rhs.View(AcceleratorRead); accelerator_forNB( i,num, vobj::Nsimd(), { - typedef decltype(coalescedRead(buffer[0])) compressed_t; - compressed_t tmp_c; - uint64_t o = table_v[i].first; - compress.Compress(&tmp_c,0,rhs_v(so+table_v[i].second)); - coalescedWrite(buffer[off+o],tmp_c); + compress.Compress(buffer[off+table_v[i].first],rhs_v[so+table_v[i].second]); }); rhs_v.ViewClose(); -// Further optimisatoin: i) software prefetch the first element of the next table entry, prefetch the table } /////////////////////////////////////////////////////////////////// @@ -85,10 +80,10 @@ void Gather_plane_simple_table (Vector<std::pair<int,int> >& table,const Lattice /////////////////////////////////////////////////////////////////// template<class cobj,class vobj,class compressor> void Gather_plane_exchange_table(const Lattice<vobj> &rhs, - Vector<cobj *> pointers,int dimension,int plane,int cbmask,compressor &compress,int type) __attribute__((noinline)); + commVector<cobj *> pointers,int dimension,int plane,int cbmask,compressor &compress,int type) __attribute__((noinline)); template<class cobj,class vobj,class compressor> -void Gather_plane_exchange_table(Vector<std::pair<int,int> >& table,const Lattice<vobj> &rhs, +void Gather_plane_exchange_table(commVector<std::pair<int,int> >& table,const Lattice<vobj> &rhs, Vector<cobj *> pointers,int dimension,int plane,int cbmask, compressor &compress,int type) { @@ -100,7 +95,7 @@ void Gather_plane_exchange_table(Vector<std::pair<int,int> >& table,const Lattic auto p0=&pointers[0][0]; auto p1=&pointers[1][0]; auto tp=&table[0]; - accelerator_forNB(j, num, 1, { + accelerator_forNB(j, num, vobj::Nsimd(), { compress.CompressExchange(p0,p1, &rhs_v[0], j, so+tp[2*j ].second, so+tp[2*j+1].second, @@ -266,10 +261,11 @@ public: } int face_table_computed; - std::vector<Vector<std::pair<int,int> > > face_table ; + std::vector<commVector<std::pair<int,int> > > face_table ; Vector<int> surface_list; stencilVector<StencilEntry> _entries; // Resident in managed memory + commVector<StencilEntry> _entries_device; // Resident in managed memory std::vector<Packet> Packets; std::vector<Merge> Mergers; std::vector<Merge> MergersSHM; @@ -342,7 +338,7 @@ public: void *shm = (void *) _grid->ShmBufferTranslate(recv_from_rank,this->u_recv_buf_p); - if ( shm==NULL ) return 0; + if ( (shm==NULL) || Stencil_force_mpi ) return 0; return 1; } @@ -609,13 +605,14 @@ public: template<class decompressor> void CommsMerge(decompressor decompress,std::vector<Merge> &mm,std::vector<Decompress> &dd) { + mergetime-=usecond(); for(int i=0;i<mm.size();i++){ auto mp = &mm[i].mpointer[0]; auto vp0= &mm[i].vpointers[0][0]; auto vp1= &mm[i].vpointers[1][0]; auto type= mm[i].type; - accelerator_forNB(o,mm[i].buffer_size/2,1,{ + accelerator_forNB(o,mm[i].buffer_size/2,vobj::Nsimd(),{ decompress.Exchange(mp,vp0,vp1,type,o); }); } @@ -1039,7 +1036,12 @@ public: int so = sx*rhs.Grid()->_ostride[dimension]; // base offset for start of plane if ( !face_table_computed ) { face_table.resize(face_idx+1); - Gather_plane_table_compute ((GridBase *)_grid,dimension,sx,cbmask,u_comm_offset,face_table[face_idx]); + std::vector<std::pair<int,int> > face_table_host ; + Gather_plane_table_compute ((GridBase *)_grid,dimension,sx,cbmask,u_comm_offset,face_table_host); + face_table[face_idx].resize(face_table_host.size()); + acceleratorCopyToDevice(&face_table_host[0], + &face_table[face_idx][0], + face_table[face_idx].size()*sizeof(face_table_host[0])); } // int rank = _grid->_processor; @@ -1062,20 +1064,20 @@ public: } send_buf = (cobj *)_grid->ShmBufferTranslate(xmit_to_rank,recv_buf); - if ( send_buf==NULL ) { + if ( (send_buf==NULL) || Stencil_force_mpi ) { send_buf = this->u_send_buf_p; } // Find out if we get the direct copy. void *success = (void *) _grid->ShmBufferTranslate(recv_from_rank,this->u_send_buf_p); - if (success==NULL) { + if ((success==NULL)||Stencil_force_mpi) { // we found a packet that comes from MPI and contributes to this leg of stencil shm_receive_only = 0; } gathertime-=usecond(); assert(send_buf!=NULL); - Gather_plane_simple_table(face_table[face_idx],rhs,send_buf,compress,u_comm_offset,so); face_idx++; + Gather_plane_simple_table(face_table[face_idx],rhs,send_buf,compress,u_comm_offset,so); face_idx++; gathertime+=usecond(); if ( compress.DecompressionStep() ) { @@ -1172,11 +1174,18 @@ public: if ( !face_table_computed ) { face_table.resize(face_idx+1); - Gather_plane_table_compute ((GridBase *)_grid,dimension,sx,cbmask,u_comm_offset,face_table[face_idx]); + std::vector<std::pair<int,int> > face_table_host ; + + Gather_plane_table_compute ((GridBase *)_grid,dimension,sx,cbmask,u_comm_offset,face_table_host); + face_table[face_idx].resize(face_table_host.size()); + acceleratorCopyToDevice(&face_table_host[0], + &face_table[face_idx][0], + face_table[face_idx].size()*sizeof(face_table_host[0])); } gathermtime-=usecond(); - Gather_plane_exchange_table(face_table[face_idx],rhs,spointers,dimension,sx,cbmask,compress,permute_type); face_idx++; + Gather_plane_exchange_table(face_table[face_idx],rhs,spointers,dimension,sx,cbmask,compress,permute_type); + face_idx++; gathermtime+=usecond(); //spointers[0] -- low @@ -1208,7 +1217,7 @@ public: // shm == receive pointer if offnode // shm == Translate[send pointer] if on node -- my view of his send pointer cobj *shm = (cobj *) _grid->ShmBufferTranslate(recv_from_rank,sp); - if (shm==NULL) { + if ((shm==NULL)||Stencil_force_mpi) { shm = rp; // we found a packet that comes from MPI and contributes to this shift. // is_same_node is only used in the WilsonStencil, and gets set for this point in the stencil. From dd091d0960c3334c17df2456e3dc3ac48a048a3e Mon Sep 17 00:00:00 2001 From: Christoph Lehner <christoph@lhnr.de> Date: Wed, 15 Sep 2021 16:58:05 +0200 Subject: [PATCH 213/399] consistent pointer offloading instead of views --- Grid/lattice/Lattice_arith.h | 2 +- Grid/lattice/Lattice_basis.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/lattice/Lattice_arith.h b/Grid/lattice/Lattice_arith.h index 3c269c58..b39a475d 100644 --- a/Grid/lattice/Lattice_arith.h +++ b/Grid/lattice/Lattice_arith.h @@ -225,7 +225,7 @@ void axpy(Lattice<vobj> &ret,sobj a,const Lattice<vobj> &x,const Lattice<vobj> & autoView( x_v , x, AcceleratorRead); autoView( y_v , y, AcceleratorRead); accelerator_for(ss,x_v.size(),vobj::Nsimd(),{ - auto tmp = a*x_v(ss)+y_v(ss); + auto tmp = a*coalescedRead(x_v[ss])+coalescedRead(y_v[ss]); coalescedWrite(ret_v[ss],tmp); }); } diff --git a/Grid/lattice/Lattice_basis.h b/Grid/lattice/Lattice_basis.h index 863b2548..0928cbd7 100644 --- a/Grid/lattice/Lattice_basis.h +++ b/Grid/lattice/Lattice_basis.h @@ -125,7 +125,7 @@ void basisRotate(VField &basis,Matrix& Qt,int j0, int j1, int k0,int k1,int Nm) for(int k=k0; k<k1; ++k){ auto tmp = coalescedRead(Bp[ss*nrot+j]); - coalescedWrite(Bp[ss*nrot+j],tmp+ Qt_p[jj*Nm+k] * coalescedRead(basis_v[k][sss])); + coalescedWrite(Bp[ss*nrot+j],tmp+ Qt_p[jj*Nm+k] * coalescedRead(basis_vp[k][sss])); } }); @@ -134,7 +134,7 @@ void basisRotate(VField &basis,Matrix& Qt,int j0, int j1, int k0,int k1,int Nm) int jj =j0+j; int ss =sj/nrot; int sss=ss+s; - coalescedWrite(basis_v[jj][sss],coalescedRead(Bp[ss*nrot+j])); + coalescedWrite(basis_vp[jj][sss],coalescedRead(Bp[ss*nrot+j])); }); } #endif From 3d0f88e70208d87ebd6c318d07361b28ab8ddb7b Mon Sep 17 00:00:00 2001 From: Christoph Lehner <christoph@lhnr.de> Date: Wed, 15 Sep 2021 18:38:32 +0200 Subject: [PATCH 214/399] A64FX drop mixed precision as well --- .../implementation/WilsonKernelsAsmA64FX.h | 240 +++++++++--------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h index ffec05a0..35d1b841 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmA64FX.h @@ -73,17 +73,17 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -102,17 +102,17 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -131,17 +131,17 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> @@ -165,17 +165,17 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -194,17 +194,17 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -223,17 +223,17 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +//#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> @@ -280,17 +280,17 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<WilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<ZWilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -309,17 +309,17 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<WilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<ZWilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -338,17 +338,17 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<WilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<ZWilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> ///////////////////////////////////////////////////////////////// @@ -371,17 +371,17 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<WilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -400,17 +400,17 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<WilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> #undef INTERIOR_AND_EXTERIOR @@ -429,17 +429,17 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<WilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> -#pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> +// #pragma GCC optimize ("-O3", "-fno-schedule-insns", "-fno-schedule-insns2") +// template<> void +// WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +// #include <qcd/action/fermion/implementation/WilsonKernelsAsmBodyA64FX.h> From c15493218d292191380bea9a6c87ff7cd3d3ce61 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 15 Sep 2021 19:24:39 +0100 Subject: [PATCH 215/399] Two extra routines to break out SchurRedBlack on many RHS into stages to allow efficient deflation & split grid Split grid solver still to do. --- Grid/algorithms/iterative/SchurRedBlack.h | 33 ++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Grid/algorithms/iterative/SchurRedBlack.h b/Grid/algorithms/iterative/SchurRedBlack.h index d0b133a3..15ef95c7 100644 --- a/Grid/algorithms/iterative/SchurRedBlack.h +++ b/Grid/algorithms/iterative/SchurRedBlack.h @@ -132,6 +132,31 @@ namespace Grid { (*this)(_Matrix,in,out,guess); } + void RedBlackSource(Matrix &_Matrix, const std::vector<Field> &in, std::vector<Field> &src_o) + { + GridBase *grid = _Matrix.RedBlackGrid(); + Field tmp(grid); + int nblock = in.size(); + for(int b=0;b<nblock;b++){ + RedBlackSource(_Matrix,in[b],tmp,src_o[b]); + } + } + // James can write his own deflated guesser + // with optimised code for the inner products + // RedBlackSolveSplitGrid(); + // RedBlackSolve(_Matrix,src_o,sol_o); + + void RedBlackSolution(Matrix &_Matrix, const std::vector<Field> &in, const std::vector<Field> &sol_o, std::vector<Field> &out) + { + GridBase *grid = _Matrix.RedBlackGrid(); + Field tmp(grid); + int nblock = in.size(); + for(int b=0;b<nblock;b++) { + pickCheckerboard(Even,tmp,in[b]); + RedBlackSolution(_Matrix,sol_o[b],tmp,out[b]); + } + } + template<class Guesser> void operator()(Matrix &_Matrix, const std::vector<Field> &in, std::vector<Field> &out,Guesser &guess) { @@ -150,9 +175,11 @@ namespace Grid { //////////////////////////////////////////////// // Prepare RedBlack source //////////////////////////////////////////////// - for(int b=0;b<nblock;b++){ - RedBlackSource(_Matrix,in[b],tmp,src_o[b]); - } + RedBlackSource(_Matrix,in,src_o); + // for(int b=0;b<nblock;b++){ + // RedBlackSource(_Matrix,in[b],tmp,src_o[b]); + // } + //////////////////////////////////////////////// // Make the guesses //////////////////////////////////////////////// From 4b24800132e53264ac6c2d950fbd8c7889344779 Mon Sep 17 00:00:00 2001 From: Luchang Jin <ljin.luchang@gmail.com> Date: Wed, 15 Sep 2021 16:24:01 -0400 Subject: [PATCH 216/399] AVX512 drop mixed precision as well --- .../implementation/WilsonKernelsAsmAvx512.h | 398 +++++++++--------- 1 file changed, 199 insertions(+), 199 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmAvx512.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmAvx512.h index 4aed13bf..e025ba41 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmAvx512.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsAsmAvx512.h @@ -74,15 +74,15 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> - -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +// +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -97,15 +97,15 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> - -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +// +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR @@ -121,15 +121,15 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> - -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +// +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> ///////////////////////////////////////////////////////////////// // XYZT vectorised, dag Kernel, single @@ -148,15 +148,15 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> - -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +// +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -171,15 +171,15 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> - -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +// +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #undef INTERIOR @@ -194,15 +194,15 @@ WilsonKernels<ZWilsonImplF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> - -template<> void -WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +// +//template<> void +//WilsonKernels<ZWilsonImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef MAYBEPERM #undef MULT_2SPIN @@ -228,14 +228,14 @@ WilsonKernels<ZDomainWallVec5dImplF>::AsmDhopSite(StencilView &st, DoubledGaugeF int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -249,14 +249,14 @@ WilsonKernels<ZDomainWallVec5dImplF>::AsmDhopSiteInt(StencilView &st, DoubledGau int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #undef INTERIOR @@ -273,15 +273,15 @@ WilsonKernels<ZDomainWallVec5dImplF>::AsmDhopSiteExt(StencilView &st, DoubledGau int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> - -template<> void -WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +// +//template<> void +//WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> ///////////////////////////////////////////////////////////////// // Ls vectorised, dag Kernel, single @@ -299,14 +299,14 @@ WilsonKernels<ZDomainWallVec5dImplF>::AsmDhopSiteDag(StencilView &st, DoubledGau int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -320,14 +320,14 @@ WilsonKernels<ZDomainWallVec5dImplF>::AsmDhopSiteDagInt(StencilView &st, Doubled int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #undef INTERIOR @@ -341,14 +341,14 @@ WilsonKernels<ZDomainWallVec5dImplF>::AsmDhopSiteDagExt(StencilView &st, Doubled int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplFH>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #endif // VEC 5D @@ -392,14 +392,14 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZWilsonImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -413,14 +413,14 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZWilsonImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #undef INTERIOR @@ -434,14 +434,14 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZWilsonImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> ///////////////////////////////////////////////////////////////// // XYZT vectorised, dag Kernel, single @@ -459,14 +459,14 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldVi int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -480,14 +480,14 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #undef INTERIOR @@ -501,14 +501,14 @@ WilsonKernels<ZWilsonImplD>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFiel int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<WilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<WilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZWilsonImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef MAYBEPERM #undef MULT_2SPIN @@ -533,14 +533,14 @@ WilsonKernels<ZDomainWallVec5dImplD>::AsmDhopSite(StencilView &st, DoubledGaugeF int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSite(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -554,14 +554,14 @@ WilsonKernels<ZDomainWallVec5dImplD>::AsmDhopSiteInt(StencilView &st, DoubledGau int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteInt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #undef INTERIOR @@ -577,14 +577,14 @@ WilsonKernels<ZDomainWallVec5dImplD>::AsmDhopSiteExt(StencilView &st, DoubledGau int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteExt(StencilView &st, DoubledGaugeFieldView &U, SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> ///////////////////////////////////////////////////////////////// // Ls vectorised, dag Kernel, single @@ -602,14 +602,14 @@ WilsonKernels<ZDomainWallVec5dImplD>::AsmDhopSiteDag(StencilView &st, DoubledGau int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteDag(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #define INTERIOR @@ -623,14 +623,14 @@ WilsonKernels<ZDomainWallVec5dImplD>::AsmDhopSiteDagInt(StencilView &st, Doubled int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteDagInt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #undef INTERIOR_AND_EXTERIOR #undef INTERIOR @@ -645,14 +645,14 @@ WilsonKernels<ZDomainWallVec5dImplD>::AsmDhopSiteDagExt(StencilView &st, Doubled int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) #include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> -template<> void -WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, - int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) -#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<DomainWallVec5dImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> +//template<> void +//WilsonKernels<ZDomainWallVec5dImplDF>::AsmDhopSiteDagExt(StencilView &st, DoubledGaugeFieldView &U,SiteHalfSpinor *buf, +// int ss,int ssU,int Ls,int Ns,const FermionFieldView &in, FermionFieldView &out) +//#include <qcd/action/fermion/implementation/WilsonKernelsAsmBody.h> #endif // VEC 5D From b4690e60911b86db6c81dfa6978f79088ed3e7cd Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 16 Sep 2021 00:00:38 +0100 Subject: [PATCH 217/399] Adding build basics for different systems --- benchmarks/Benchmark_dwf_fp32.cc | 2 +- systems/Tursa/config-command | 12 ++++++++++++ systems/Tursa/dwf.slurm | 31 +++++++++++++++++++++++++++++++ systems/Tursa/mpiwrapper.sh | 17 +++++++++++++++++ systems/Tursa/sourceme.sh | 2 ++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 systems/Tursa/config-command create mode 100644 systems/Tursa/dwf.slurm create mode 100755 systems/Tursa/mpiwrapper.sh create mode 100644 systems/Tursa/sourceme.sh diff --git a/benchmarks/Benchmark_dwf_fp32.cc b/benchmarks/Benchmark_dwf_fp32.cc index 03f3ee61..d48486c0 100644 --- a/benchmarks/Benchmark_dwf_fp32.cc +++ b/benchmarks/Benchmark_dwf_fp32.cc @@ -182,7 +182,7 @@ int main (int argc, char ** argv) std::cout << GridLogMessage<< "*****************************************************************" <<std::endl; DomainWallFermionF Dw(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5); - int ncall =1000; + int ncall =3000; if (1) { FGrid->Barrier(); diff --git a/systems/Tursa/config-command b/systems/Tursa/config-command new file mode 100644 index 00000000..b47c34e5 --- /dev/null +++ b/systems/Tursa/config-command @@ -0,0 +1,12 @@ +../../configure \ + --enable-comms=mpi \ + --enable-simd=GPU \ + --enable-shm=nvlink \ + --enable-gen-simd-width=64 \ + --enable-accelerator=cuda \ + --with-lime=/mnt/lustre/tursafs1/home/tc002/tc002/dc-boyl1/spack/spack/opt/spack/linux-rhel8-zen/gcc-8.4.1/c-lime-2-3-9-e6wxqrid6rqmd45z7n32dxkvkykpvyez \ + --disable-accelerator-cshift \ + --disable-unified \ + CXX=nvcc \ + LDFLAGS="-cudart shared " \ + CXXFLAGS="-ccbin mpicxx -gencode arch=compute_80,code=sm_80 -std=c++14 -cudart shared" diff --git a/systems/Tursa/dwf.slurm b/systems/Tursa/dwf.slurm new file mode 100644 index 00000000..5ed4ca2b --- /dev/null +++ b/systems/Tursa/dwf.slurm @@ -0,0 +1,31 @@ +#!/bin/bash +#SBATCH -J dslash +#SBATCH -A tc002 +#SBATCH -t 2:20:00 +#SBATCH --nodelist=tu-c0r0n[00,03,06,09] +#SBATCH --exclusive +#SBATCH --nodes=4 +#SBATCH --ntasks=16 +#SBATCH --ntasks-per-node=4 +#SBATCH --cpus-per-task=8 +#SBATCH --time=12:00:00 +#SBATCH --partition=gpu +#SBATCH --gres=gpu:4 +#SBATCH --output=%x.%j.out +#SBATCH --error=%x.%j.err + +export OMP_NUM_THREADS=4 +export OMPI_MCA_btl=^uct,openib +export UCX_TLS=gdr_copy,rc,rc_x,sm,cuda_copy,cuda_ipc +export UCX_RNDV_SCHEME=put_zcopy +export UCX_RNDV_THRESH=16384 +export UCX_IB_GPU_DIRECT_RDMA=yes +export UCX_MEMTYPE_CACHE=n +OPT="--comms-overlap --comms-concurrent" + + +mpirun -np $SLURM_NTASKS -x LD_LIBRARY_PATH --bind-to none ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $OPT --mpi 2.2.2.2 --accelerator-threads 8 --grid 64.64.64.64 --shm 2048 + + + + diff --git a/systems/Tursa/mpiwrapper.sh b/systems/Tursa/mpiwrapper.sh new file mode 100755 index 00000000..4d96ac67 --- /dev/null +++ b/systems/Tursa/mpiwrapper.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +lrank=$OMPI_COMM_WORLD_LOCAL_RANK +numa1=$(( 2 * $lrank)) +numa2=$(( 2 * $lrank + 1 )) +netdev=mlx5_${lrank}:1 + +export CUDA_VISIBLE_DEVICES=$OMPI_COMM_WORLD_LOCAL_RANK +export UCX_NET_DEVICES=mlx5_${lrank}:1 +BINDING="--interleave=$numa1,$numa2" + +echo "`hostname` - $lrank device=$CUDA_VISIBLE_DEVICES binding=$BINDING" + +numactl ${BINDING} $* + + + diff --git a/systems/Tursa/sourceme.sh b/systems/Tursa/sourceme.sh new file mode 100644 index 00000000..6286750d --- /dev/null +++ b/systems/Tursa/sourceme.sh @@ -0,0 +1,2 @@ +spack load c-lime +module load cuda/11.4.1 openmpi/4.1.1 ucx/1.10.1 From cc4a27b9e632335d1a15e52d41bad51f0d88ebf2 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 16 Sep 2021 00:15:35 +0100 Subject: [PATCH 218/399] Scripts and performance --- systems/Tursa/dwf.perf | 245 ++++++++++++++++++++++++++++++++++++++++ systems/Tursa/dwf.slurm | 9 +- 2 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 systems/Tursa/dwf.perf diff --git a/systems/Tursa/dwf.perf b/systems/Tursa/dwf.perf new file mode 100644 index 00000000..ee9d20b7 --- /dev/null +++ b/systems/Tursa/dwf.perf @@ -0,0 +1,245 @@ +tu-c0r0n00 - 0 device=0 binding=--interleave=0,1 +tu-c0r0n09 - 0 device=0 binding=--interleave=0,1 +tu-c0r0n00 - 1 device=1 binding=--interleave=2,3 +tu-c0r0n09 - 1 device=1 binding=--interleave=2,3 +tu-c0r0n09 - 2 device=2 binding=--interleave=4,5 +tu-c0r0n03 - 1 device=1 binding=--interleave=2,3 +tu-c0r0n03 - 0 device=0 binding=--interleave=0,1 +tu-c0r0n03 - 3 device=3 binding=--interleave=6,7 +tu-c0r0n03 - 2 device=2 binding=--interleave=4,5 +tu-c0r0n06 - 0 device=0 binding=--interleave=0,1 +tu-c0r0n06 - 1 device=1 binding=--interleave=2,3 +tu-c0r0n00 - 3 device=3 binding=--interleave=6,7 +tu-c0r0n00 - 2 device=2 binding=--interleave=4,5 +tu-c0r0n09 - 3 device=3 binding=--interleave=6,7 +tu-c0r0n06 - 2 device=2 binding=--interleave=4,5 +tu-c0r0n06 - 3 device=3 binding=--interleave=6,7 +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: NVIDIA A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42505273344 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 3 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: NVIDIA A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42505273344 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 3 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7f84e0000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=9d2238148c56e3fbadfa95dcabf2b83d4bde14cd: (HEAD -> develop) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34004218675 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 1.234535 s : Grid Layout +Grid : Message : 1.234543 s : Global lattice size : 64 64 64 64 +Grid : Message : 1.234547 s : OpenMP threads : 4 +Grid : Message : 1.234548 s : MPI tasks : 2 2 2 2 +Grid : Message : 1.272006 s : Making s innermost grids +Grid : Message : 1.316898 s : Initialising 4d RNG +Grid : Message : 1.423692 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 1.423718 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 1.851400 s : Initialising 5d RNG +Grid : Message : 3.495243 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 3.495282 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 11.929748 s : Initialised RNGs +Grid : Message : 15.135728 s : Drawing gauge field +Grid : Message : 16.109867 s : Random gauge initialised +Grid : Message : 17.570374 s : Setting up Cshift based reference +Grid : Message : 26.641932 s : ***************************************************************** +Grid : Message : 26.641961 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 26.641963 s : ***************************************************************** +Grid : Message : 26.641965 s : ***************************************************************** +Grid : Message : 26.641967 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 26.641968 s : * Vectorising space-time by 8 +Grid : Message : 26.641970 s : * VComplexF size is 64 B +Grid : Message : 26.641974 s : * SINGLE precision +Grid : Message : 26.641976 s : * Using Overlapped Comms/Compute +Grid : Message : 26.641977 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 26.641978 s : ***************************************************************** +Grid : Message : 28.676542 s : Called warmup +Grid : Message : 56.574545 s : Called Dw 3000 times in 2.78974e+07 us +Grid : Message : 56.574570 s : mflop/s = 3.8104e+07 +Grid : Message : 56.574573 s : mflop/s per rank = 2.3815e+06 +Grid : Message : 56.574575 s : mflop/s per node = 9.526e+06 +Grid : Message : 56.574577 s : RF GiB/s (base 2) = 77426.5 +Grid : Message : 56.574579 s : mem GiB/s (base 2) = 48391.5 +Grid : Message : 56.578174 s : norm diff 1.03481e-13 +Grid : Message : 56.613049 s : #### Dhop calls report +Grid : Message : 56.613053 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 56.613057 s : WilsonFermion5D TotalTime /Calls : 4692.42 us +Grid : Message : 56.613059 s : WilsonFermion5D CommTime /Calls : 3204.73 us +Grid : Message : 56.613061 s : WilsonFermion5D FaceTime /Calls : 489.82 us +Grid : Message : 56.613063 s : WilsonFermion5D ComputeTime1/Calls : 41.1853 us +Grid : Message : 56.613065 s : WilsonFermion5D ComputeTime2/Calls : 1014.2 us +Grid : Message : 56.613073 s : Average mflops/s per call : 3.56434e+09 +Grid : Message : 56.613079 s : Average mflops/s per call per rank : 2.22771e+08 +Grid : Message : 56.613083 s : Average mflops/s per call per node : 8.91085e+08 +Grid : Message : 56.613087 s : Average mflops/s per call (full) : 3.84425e+07 +Grid : Message : 56.613091 s : Average mflops/s per call per rank (full): 2.40266e+06 +Grid : Message : 56.613104 s : Average mflops/s per call per node (full): 9.61063e+06 +Grid : Message : 56.613106 s : WilsonFermion5D Stencil +Grid : Message : 56.613164 s : Stencil calls 3001 +Grid : Message : 56.613168 s : Stencil halogtime 0 +Grid : Message : 56.613170 s : Stencil gathertime 45.0563 +Grid : Message : 56.613172 s : Stencil gathermtime 19.5342 +Grid : Message : 56.613174 s : Stencil mergetime 18.4625 +Grid : Message : 56.613176 s : Stencil decompresstime 0.0616461 +Grid : Message : 56.613178 s : Stencil comms_bytes 4.02653e+08 +Grid : Message : 56.613180 s : Stencil commtime 6345.15 +Grid : Message : 56.613182 s : Stencil 63.4584 GB/s per rank +Grid : Message : 56.613184 s : Stencil 253.834 GB/s per node +Grid : Message : 56.613186 s : WilsonFermion5D StencilEven +Grid : Message : 56.613193 s : WilsonFermion5D StencilOdd +Grid : Message : 56.613205 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 56.613211 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 56.613214 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 79.610634 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 79.610656 s : Called DwDag +Grid : Message : 79.610657 s : norm dag result 12.0421 +Grid : Message : 79.623325 s : norm dag ref 12.0421 +Grid : Message : 79.639282 s : norm dag diff 7.63236e-14 +Grid : Message : 79.679130 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 80.544440 s : src_e0.499997 +Grid : Message : 80.384797 s : src_o0.500003 +Grid : Message : 80.485472 s : ********************************************************* +Grid : Message : 80.485477 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 80.485478 s : * Vectorising space-time by 8 +Grid : Message : 80.485480 s : * SINGLE precision +Grid : Message : 80.485481 s : * Using Overlapped Comms/Compute +Grid : Message : 80.485482 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 80.485483 s : ********************************************************* +Grid : Message : 94.142389 s : Deo mflop/s = 3.91983e+07 +Grid : Message : 94.142423 s : Deo mflop/s per rank 2.4499e+06 +Grid : Message : 94.142425 s : Deo mflop/s per node 9.79959e+06 +Grid : Message : 94.142427 s : #### Dhop calls report +Grid : Message : 94.142428 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 94.142430 s : WilsonFermion5D TotalTime /Calls : 4550.65 us +Grid : Message : 94.142432 s : WilsonFermion5D CommTime /Calls : 2986.58 us +Grid : Message : 94.142434 s : WilsonFermion5D FaceTime /Calls : 607.616 us +Grid : Message : 94.142436 s : WilsonFermion5D ComputeTime1/Calls : 58.1666 us +Grid : Message : 94.142438 s : WilsonFermion5D ComputeTime2/Calls : 998.146 us +Grid : Message : 94.142460 s : Average mflops/s per call : 3.01763e+09 +Grid : Message : 94.142464 s : Average mflops/s per call per rank : 1.88602e+08 +Grid : Message : 94.142466 s : Average mflops/s per call per node : 7.54407e+08 +Grid : Message : 94.142468 s : Average mflops/s per call (full) : 3.96402e+07 +Grid : Message : 94.142473 s : Average mflops/s per call per rank (full): 2.47751e+06 +Grid : Message : 94.142478 s : Average mflops/s per call per node (full): 9.91005e+06 +Grid : Message : 94.142481 s : WilsonFermion5D Stencil +Grid : Message : 94.142492 s : WilsonFermion5D StencilEven +Grid : Message : 94.142504 s : WilsonFermion5D StencilOdd +Grid : Message : 94.142515 s : Stencil calls 3001 +Grid : Message : 94.142519 s : Stencil halogtime 0 +Grid : Message : 94.142522 s : Stencil gathertime 54.0447 +Grid : Message : 94.142525 s : Stencil gathermtime 19.4485 +Grid : Message : 94.142530 s : Stencil mergetime 18.3146 +Grid : Message : 94.142532 s : Stencil decompresstime 0.0593136 +Grid : Message : 94.142535 s : Stencil comms_bytes 2.01327e+08 +Grid : Message : 94.142539 s : Stencil commtime 2989.1 +Grid : Message : 94.142542 s : Stencil 67.3536 GB/s per rank +Grid : Message : 94.142546 s : Stencil 269.414 GB/s per node +Grid : Message : 94.142548 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 94.142551 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 94.142553 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 94.216038 s : r_e6.02111 +Grid : Message : 94.223150 s : r_o6.02102 +Grid : Message : 94.229148 s : res12.0421 +Grid : Message : 94.830824 s : norm diff 0 +Grid : Message : 95.633245 s : norm diff even 0 +Grid : Message : 95.999452 s : norm diff odd 0 diff --git a/systems/Tursa/dwf.slurm b/systems/Tursa/dwf.slurm index 5ed4ca2b..35675d1c 100644 --- a/systems/Tursa/dwf.slurm +++ b/systems/Tursa/dwf.slurm @@ -24,7 +24,14 @@ export UCX_MEMTYPE_CACHE=n OPT="--comms-overlap --comms-concurrent" -mpirun -np $SLURM_NTASKS -x LD_LIBRARY_PATH --bind-to none ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $OPT --mpi 2.2.2.2 --accelerator-threads 8 --grid 64.64.64.64 --shm 2048 +mpirun -np $SLURM_NTASKS -x LD_LIBRARY_PATH --bind-to none \ + ./mpiwrapper.sh \ + ./benchmarks/Benchmark_dwf_fp32 \ + $OPT \ + --mpi 2.2.2.2 \ + --accelerator-threads 8 \ + --grid 64.64.64.64 \ + --shm 2048 > dwf.perf From 145acf29192ac88695cfc7488e72b16a11c9d5a1 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 16 Sep 2021 01:06:28 +0100 Subject: [PATCH 219/399] Perf results --- systems/Tursa/dwf.16node.perf | 293 +++++++++++++++++++++ systems/Tursa/{dwf.perf => dwf.4node.perf} | 276 +++++++++---------- systems/Tursa/dwf16.slurm | 33 +++ systems/Tursa/{dwf.slurm => dwf4.slurm} | 2 +- 4 files changed, 465 insertions(+), 139 deletions(-) create mode 100644 systems/Tursa/dwf.16node.perf rename systems/Tursa/{dwf.perf => dwf.4node.perf} (50%) create mode 100644 systems/Tursa/dwf16.slurm rename systems/Tursa/{dwf.slurm => dwf4.slurm} (96%) diff --git a/systems/Tursa/dwf.16node.perf b/systems/Tursa/dwf.16node.perf new file mode 100644 index 00000000..a51aae94 --- /dev/null +++ b/systems/Tursa/dwf.16node.perf @@ -0,0 +1,293 @@ +tu-c0r1n00 - 0 device=0 binding=--interleave=0,1 +tu-c0r1n00 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n00 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n00 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n21 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n06 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n12 - 2 device=2 binding=--interleave=4,5 +tu-c0r2n21 - 2 device=2 binding=--interleave=4,5 +tu-c0r2n21 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n21 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n06 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n06 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n12 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n06 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n12 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n12 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n12 - 0 device=0 binding=--interleave=0,1 +tu-c0r1n12 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n12 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n12 - 3 device=3 binding=--interleave=6,7 +tu-c0r1n18 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n18 - 0 device=0 binding=--interleave=0,1 +tu-c0r1n18 - 3 device=3 binding=--interleave=6,7 +tu-c0r1n18 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n06 - 2 device=2 binding=--interleave=4,5 +tu-c0r2n09 - 3 device=3 binding=--interleave=6,7 +tu-c0r1n06 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n15 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n09 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n06 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n15 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n15 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n06 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n15 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n09 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n09 - 0 device=0 binding=--interleave=0,1 +tu-c0r1n09 - 0 device=0 binding=--interleave=0,1 +tu-c0r1n09 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n09 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n09 - 3 device=3 binding=--interleave=6,7 +tu-c0r1n21 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n21 - 0 device=0 binding=--interleave=0,1 +tu-c0r1n21 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n15 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n21 - 3 device=3 binding=--interleave=6,7 +tu-c0r1n15 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n15 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n03 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n15 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n03 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n00 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n03 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n00 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n00 - 2 device=2 binding=--interleave=4,5 +tu-c0r2n18 - 1 device=1 binding=--interleave=2,3 +tu-c0r2n00 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n18 - 2 device=2 binding=--interleave=4,5 +tu-c0r2n18 - 3 device=3 binding=--interleave=6,7 +tu-c0r2n18 - 0 device=0 binding=--interleave=0,1 +tu-c0r2n03 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n03 - 0 device=0 binding=--interleave=0,1 +tu-c0r1n03 - 1 device=1 binding=--interleave=2,3 +tu-c0r1n03 - 2 device=2 binding=--interleave=4,5 +tu-c0r1n03 - 3 device=3 binding=--interleave=6,7 +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: NVIDIA A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42505273344 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 3 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: NVIDIA A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42505273344 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 3 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 64 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7f05c0000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=9d2238148c56e3fbadfa95dcabf2b83d4bde14cd: (HEAD -> develop) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34004218675 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 1.814936 s : Grid Layout +Grid : Message : 1.814947 s : Global lattice size : 64 64 64 256 +Grid : Message : 1.814952 s : OpenMP threads : 4 +Grid : Message : 1.814955 s : MPI tasks : 2 2 2 8 +Grid : Message : 1.859229 s : Making s innermost grids +Grid : Message : 1.907983 s : Initialising 4d RNG +Grid : Message : 1.999619 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 1.999657 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 3.786102 s : Initialising 5d RNG +Grid : Message : 5.361999 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 5.362036 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 38.698345 s : Initialised RNGs +Grid : Message : 42.821728 s : Drawing gauge field +Grid : Message : 43.916364 s : Random gauge initialised +Grid : Message : 46.410003 s : Setting up Cshift based reference +Grid : Message : 54.242661 s : ***************************************************************** +Grid : Message : 54.242686 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 54.242688 s : ***************************************************************** +Grid : Message : 54.242689 s : ***************************************************************** +Grid : Message : 54.242690 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 54.242691 s : * Vectorising space-time by 8 +Grid : Message : 54.242692 s : * VComplexF size is 64 B +Grid : Message : 54.242694 s : * SINGLE precision +Grid : Message : 54.242697 s : * Using Overlapped Comms/Compute +Grid : Message : 54.242698 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 54.242699 s : ***************************************************************** +Grid : Message : 56.314112 s : Called warmup +Grid : Message : 84.246354 s : Called Dw 3000 times in 2.79318e+07 us +Grid : Message : 84.246405 s : mflop/s = 1.52229e+08 +Grid : Message : 84.246408 s : mflop/s per rank = 2.37857e+06 +Grid : Message : 84.246412 s : mflop/s per node = 9.51428e+06 +Grid : Message : 84.246414 s : RF GiB/s (base 2) = 309325 +Grid : Message : 84.246417 s : mem GiB/s (base 2) = 193328 +Grid : Message : 84.250016 s : norm diff 1.03478e-13 +Grid : Message : 84.285132 s : #### Dhop calls report +Grid : Message : 84.285137 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 84.285140 s : WilsonFermion5D TotalTime /Calls : 4703.27 us +Grid : Message : 84.285142 s : WilsonFermion5D CommTime /Calls : 3131.05 us +Grid : Message : 84.285144 s : WilsonFermion5D FaceTime /Calls : 492.972 us +Grid : Message : 84.285146 s : WilsonFermion5D ComputeTime1/Calls : 56.9085 us +Grid : Message : 84.285148 s : WilsonFermion5D ComputeTime2/Calls : 1099.95 us +Grid : Message : 84.285160 s : Average mflops/s per call : 1.43412e+10 +Grid : Message : 84.285165 s : Average mflops/s per call per rank : 2.24082e+08 +Grid : Message : 84.285170 s : Average mflops/s per call per node : 8.96328e+08 +Grid : Message : 84.285173 s : Average mflops/s per call (full) : 1.53416e+08 +Grid : Message : 84.285176 s : Average mflops/s per call per rank (full): 2.39712e+06 +Grid : Message : 84.285194 s : Average mflops/s per call per node (full): 9.58847e+06 +Grid : Message : 84.285197 s : WilsonFermion5D Stencil +Grid : Message : 84.285271 s : Stencil calls 3001 +Grid : Message : 84.285275 s : Stencil halogtime 0 +Grid : Message : 84.285277 s : Stencil gathertime 55.2059 +Grid : Message : 84.285281 s : Stencil gathermtime 20.0923 +Grid : Message : 84.285283 s : Stencil mergetime 18.9057 +Grid : Message : 84.285286 s : Stencil decompresstime 0.0619793 +Grid : Message : 84.285289 s : Stencil comms_bytes 4.02653e+08 +Grid : Message : 84.285292 s : Stencil commtime 6323.57 +Grid : Message : 84.285295 s : Stencil 63.675 GB/s per rank +Grid : Message : 84.285298 s : Stencil 254.7 GB/s per node +Grid : Message : 84.285301 s : WilsonFermion5D StencilEven +Grid : Message : 84.285316 s : WilsonFermion5D StencilOdd +Grid : Message : 84.285333 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 84.285336 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 84.285337 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 106.985790 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 106.985814 s : Called DwDag +Grid : Message : 106.985815 s : norm dag result 12.0421 +Grid : Message : 107.188790 s : norm dag ref 12.0421 +Grid : Message : 107.349010 s : norm dag diff 7.63254e-14 +Grid : Message : 107.762980 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 107.458374 s : src_e0.499998 +Grid : Message : 107.754073 s : src_o0.500002 +Grid : Message : 107.855191 s : ********************************************************* +Grid : Message : 107.855194 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 107.855195 s : * Vectorising space-time by 8 +Grid : Message : 107.855197 s : * SINGLE precision +Grid : Message : 107.855198 s : * Using Overlapped Comms/Compute +Grid : Message : 107.855199 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 107.855200 s : ********************************************************* +Grid : Message : 121.549348 s : Deo mflop/s = 1.56492e+08 +Grid : Message : 121.549382 s : Deo mflop/s per rank 2.44518e+06 +Grid : Message : 121.549384 s : Deo mflop/s per node 9.78074e+06 +Grid : Message : 121.549387 s : #### Dhop calls report +Grid : Message : 121.549388 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 121.549390 s : WilsonFermion5D TotalTime /Calls : 4563.01 us +Grid : Message : 121.549393 s : WilsonFermion5D CommTime /Calls : 2967.77 us +Grid : Message : 121.549395 s : WilsonFermion5D FaceTime /Calls : 601.095 us +Grid : Message : 121.549397 s : WilsonFermion5D ComputeTime1/Calls : 59.9877 us +Grid : Message : 121.549399 s : WilsonFermion5D ComputeTime2/Calls : 1038.46 us +Grid : Message : 121.549423 s : Average mflops/s per call : 1.2726e+10 +Grid : Message : 121.549428 s : Average mflops/s per call per rank : 1.98843e+08 +Grid : Message : 121.549430 s : Average mflops/s per call per node : 7.95373e+08 +Grid : Message : 121.549432 s : Average mflops/s per call (full) : 1.58131e+08 +Grid : Message : 121.549436 s : Average mflops/s per call per rank (full): 2.4708e+06 +Grid : Message : 121.549440 s : Average mflops/s per call per node (full): 9.88321e+06 +Grid : Message : 121.549442 s : WilsonFermion5D Stencil +Grid : Message : 121.549453 s : WilsonFermion5D StencilEven +Grid : Message : 121.549472 s : WilsonFermion5D StencilOdd +Grid : Message : 121.549484 s : Stencil calls 3001 +Grid : Message : 121.549490 s : Stencil halogtime 0 +Grid : Message : 121.549492 s : Stencil gathertime 55.2206 +Grid : Message : 121.549496 s : Stencil gathermtime 19.4562 +Grid : Message : 121.549500 s : Stencil mergetime 18.3469 +Grid : Message : 121.549502 s : Stencil decompresstime 0.0646451 +Grid : Message : 121.549506 s : Stencil comms_bytes 2.01327e+08 +Grid : Message : 121.549510 s : Stencil commtime 2979.17 +Grid : Message : 121.549512 s : Stencil 67.5782 GB/s per rank +Grid : Message : 121.549514 s : Stencil 270.313 GB/s per node +Grid : Message : 121.549517 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 121.549519 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 121.549522 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 121.625928 s : r_e6.02108 +Grid : Message : 121.634489 s : r_o6.02101 +Grid : Message : 121.640496 s : res12.0421 +Grid : Message : 122.275455 s : norm diff 0 +Grid : Message : 123.135840 s : norm diff even 0 +Grid : Message : 123.389190 s : norm diff odd 0 diff --git a/systems/Tursa/dwf.perf b/systems/Tursa/dwf.4node.perf similarity index 50% rename from systems/Tursa/dwf.perf rename to systems/Tursa/dwf.4node.perf index ee9d20b7..9073969e 100644 --- a/systems/Tursa/dwf.perf +++ b/systems/Tursa/dwf.4node.perf @@ -1,19 +1,25 @@ tu-c0r0n00 - 0 device=0 binding=--interleave=0,1 -tu-c0r0n09 - 0 device=0 binding=--interleave=0,1 tu-c0r0n00 - 1 device=1 binding=--interleave=2,3 tu-c0r0n09 - 1 device=1 binding=--interleave=2,3 -tu-c0r0n09 - 2 device=2 binding=--interleave=4,5 -tu-c0r0n03 - 1 device=1 binding=--interleave=2,3 -tu-c0r0n03 - 0 device=0 binding=--interleave=0,1 -tu-c0r0n03 - 3 device=3 binding=--interleave=6,7 -tu-c0r0n03 - 2 device=2 binding=--interleave=4,5 +tu-c0r0n00 - 2 device=2 binding=--interleave=4,5 tu-c0r0n06 - 0 device=0 binding=--interleave=0,1 tu-c0r0n06 - 1 device=1 binding=--interleave=2,3 -tu-c0r0n00 - 3 device=3 binding=--interleave=6,7 -tu-c0r0n00 - 2 device=2 binding=--interleave=4,5 -tu-c0r0n09 - 3 device=3 binding=--interleave=6,7 +tu-c0r0n09 - 0 device=0 binding=--interleave=0,1 +tu-c0r0n09 - 2 device=2 binding=--interleave=4,5 +tu-c0r0n03 - 1 device=1 binding=--interleave=2,3 tu-c0r0n06 - 2 device=2 binding=--interleave=4,5 +tu-c0r0n09 - 3 device=3 binding=--interleave=6,7 +tu-c0r0n00 - 3 device=3 binding=--interleave=6,7 +tu-c0r0n03 - 0 device=0 binding=--interleave=0,1 +tu-c0r0n03 - 2 device=2 binding=--interleave=4,5 tu-c0r0n06 - 3 device=3 binding=--interleave=6,7 +tu-c0r0n03 - 3 device=3 binding=--interleave=6,7 +OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ OPENMPI detected AcceleratorCudaInit[0]: ======================== AcceleratorCudaInit[0]: Device Number : 0 @@ -32,18 +38,6 @@ AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DE AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no AcceleratorCudaInit: ================================================ OPENMPI detected -AcceleratorCudaInit: using default device -AcceleratorCudaInit: assume user either uses a) IBM jsrun, or -AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding -AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no -AcceleratorCudaInit: ================================================ -OPENMPI detected -AcceleratorCudaInit: using default device -AcceleratorCudaInit: assume user either uses a) IBM jsrun, or -AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding -AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no -AcceleratorCudaInit: ================================================ -OPENMPI detected AcceleratorCudaInit[0]: ======================== AcceleratorCudaInit[0]: Device Number : 0 AcceleratorCudaInit[0]: ======================== @@ -73,12 +67,18 @@ AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DE AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no AcceleratorCudaInit: ================================================ OPENMPI detected +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ OPENMPI detected AcceleratorCudaInit: using default device AcceleratorCudaInit: assume user either uses a) IBM jsrun, or AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no AcceleratorCudaInit: ================================================ +OPENMPI detected AcceleratorCudaInit: using default device AcceleratorCudaInit: assume user either uses a) IBM jsrun, or AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding @@ -86,7 +86,7 @@ AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no AcceleratorCudaInit: ================================================ SharedMemoryMpi: World communicator of size 16 SharedMemoryMpi: Node communicator of size 4 -0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7f84e0000000 for comms buffers +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7fcd80000000 for comms buffers Setting up IPC __|__|__|__|__|__|__|__|__|__|__|__|__|__|__ @@ -127,119 +127,119 @@ Grid : Message : MemoryManager::Init() setting up Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory Grid : Message : MemoryManager::Init() Using cudaMalloc -Grid : Message : 1.234535 s : Grid Layout -Grid : Message : 1.234543 s : Global lattice size : 64 64 64 64 -Grid : Message : 1.234547 s : OpenMP threads : 4 -Grid : Message : 1.234548 s : MPI tasks : 2 2 2 2 -Grid : Message : 1.272006 s : Making s innermost grids -Grid : Message : 1.316898 s : Initialising 4d RNG -Grid : Message : 1.423692 s : Intialising parallel RNG with unique string 'The 4D RNG' -Grid : Message : 1.423718 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 -Grid : Message : 1.851400 s : Initialising 5d RNG -Grid : Message : 3.495243 s : Intialising parallel RNG with unique string 'The 5D RNG' -Grid : Message : 3.495282 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a -Grid : Message : 11.929748 s : Initialised RNGs -Grid : Message : 15.135728 s : Drawing gauge field -Grid : Message : 16.109867 s : Random gauge initialised -Grid : Message : 17.570374 s : Setting up Cshift based reference -Grid : Message : 26.641932 s : ***************************************************************** -Grid : Message : 26.641961 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm -Grid : Message : 26.641963 s : ***************************************************************** -Grid : Message : 26.641965 s : ***************************************************************** -Grid : Message : 26.641967 s : * Benchmarking DomainWallFermionR::Dhop -Grid : Message : 26.641968 s : * Vectorising space-time by 8 -Grid : Message : 26.641970 s : * VComplexF size is 64 B -Grid : Message : 26.641974 s : * SINGLE precision -Grid : Message : 26.641976 s : * Using Overlapped Comms/Compute -Grid : Message : 26.641977 s : * Using GENERIC Nc WilsonKernels -Grid : Message : 26.641978 s : ***************************************************************** -Grid : Message : 28.676542 s : Called warmup -Grid : Message : 56.574545 s : Called Dw 3000 times in 2.78974e+07 us -Grid : Message : 56.574570 s : mflop/s = 3.8104e+07 -Grid : Message : 56.574573 s : mflop/s per rank = 2.3815e+06 -Grid : Message : 56.574575 s : mflop/s per node = 9.526e+06 -Grid : Message : 56.574577 s : RF GiB/s (base 2) = 77426.5 -Grid : Message : 56.574579 s : mem GiB/s (base 2) = 48391.5 -Grid : Message : 56.578174 s : norm diff 1.03481e-13 -Grid : Message : 56.613049 s : #### Dhop calls report -Grid : Message : 56.613053 s : WilsonFermion5D Number of DhopEO Calls : 6002 -Grid : Message : 56.613057 s : WilsonFermion5D TotalTime /Calls : 4692.42 us -Grid : Message : 56.613059 s : WilsonFermion5D CommTime /Calls : 3204.73 us -Grid : Message : 56.613061 s : WilsonFermion5D FaceTime /Calls : 489.82 us -Grid : Message : 56.613063 s : WilsonFermion5D ComputeTime1/Calls : 41.1853 us -Grid : Message : 56.613065 s : WilsonFermion5D ComputeTime2/Calls : 1014.2 us -Grid : Message : 56.613073 s : Average mflops/s per call : 3.56434e+09 -Grid : Message : 56.613079 s : Average mflops/s per call per rank : 2.22771e+08 -Grid : Message : 56.613083 s : Average mflops/s per call per node : 8.91085e+08 -Grid : Message : 56.613087 s : Average mflops/s per call (full) : 3.84425e+07 -Grid : Message : 56.613091 s : Average mflops/s per call per rank (full): 2.40266e+06 -Grid : Message : 56.613104 s : Average mflops/s per call per node (full): 9.61063e+06 -Grid : Message : 56.613106 s : WilsonFermion5D Stencil -Grid : Message : 56.613164 s : Stencil calls 3001 -Grid : Message : 56.613168 s : Stencil halogtime 0 -Grid : Message : 56.613170 s : Stencil gathertime 45.0563 -Grid : Message : 56.613172 s : Stencil gathermtime 19.5342 -Grid : Message : 56.613174 s : Stencil mergetime 18.4625 -Grid : Message : 56.613176 s : Stencil decompresstime 0.0616461 -Grid : Message : 56.613178 s : Stencil comms_bytes 4.02653e+08 -Grid : Message : 56.613180 s : Stencil commtime 6345.15 -Grid : Message : 56.613182 s : Stencil 63.4584 GB/s per rank -Grid : Message : 56.613184 s : Stencil 253.834 GB/s per node -Grid : Message : 56.613186 s : WilsonFermion5D StencilEven -Grid : Message : 56.613193 s : WilsonFermion5D StencilOdd -Grid : Message : 56.613205 s : WilsonFermion5D Stencil Reporti() -Grid : Message : 56.613211 s : WilsonFermion5D StencilEven Reporti() -Grid : Message : 56.613214 s : WilsonFermion5D StencilOdd Reporti() -Grid : Message : 79.610634 s : Compare to naive wilson implementation Dag to verify correctness -Grid : Message : 79.610656 s : Called DwDag -Grid : Message : 79.610657 s : norm dag result 12.0421 -Grid : Message : 79.623325 s : norm dag ref 12.0421 -Grid : Message : 79.639282 s : norm dag diff 7.63236e-14 -Grid : Message : 79.679130 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec -Grid : Message : 80.544440 s : src_e0.499997 -Grid : Message : 80.384797 s : src_o0.500003 -Grid : Message : 80.485472 s : ********************************************************* -Grid : Message : 80.485477 s : * Benchmarking DomainWallFermionF::DhopEO -Grid : Message : 80.485478 s : * Vectorising space-time by 8 -Grid : Message : 80.485480 s : * SINGLE precision -Grid : Message : 80.485481 s : * Using Overlapped Comms/Compute -Grid : Message : 80.485482 s : * Using GENERIC Nc WilsonKernels -Grid : Message : 80.485483 s : ********************************************************* -Grid : Message : 94.142389 s : Deo mflop/s = 3.91983e+07 -Grid : Message : 94.142423 s : Deo mflop/s per rank 2.4499e+06 -Grid : Message : 94.142425 s : Deo mflop/s per node 9.79959e+06 -Grid : Message : 94.142427 s : #### Dhop calls report -Grid : Message : 94.142428 s : WilsonFermion5D Number of DhopEO Calls : 3001 -Grid : Message : 94.142430 s : WilsonFermion5D TotalTime /Calls : 4550.65 us -Grid : Message : 94.142432 s : WilsonFermion5D CommTime /Calls : 2986.58 us -Grid : Message : 94.142434 s : WilsonFermion5D FaceTime /Calls : 607.616 us -Grid : Message : 94.142436 s : WilsonFermion5D ComputeTime1/Calls : 58.1666 us -Grid : Message : 94.142438 s : WilsonFermion5D ComputeTime2/Calls : 998.146 us -Grid : Message : 94.142460 s : Average mflops/s per call : 3.01763e+09 -Grid : Message : 94.142464 s : Average mflops/s per call per rank : 1.88602e+08 -Grid : Message : 94.142466 s : Average mflops/s per call per node : 7.54407e+08 -Grid : Message : 94.142468 s : Average mflops/s per call (full) : 3.96402e+07 -Grid : Message : 94.142473 s : Average mflops/s per call per rank (full): 2.47751e+06 -Grid : Message : 94.142478 s : Average mflops/s per call per node (full): 9.91005e+06 -Grid : Message : 94.142481 s : WilsonFermion5D Stencil -Grid : Message : 94.142492 s : WilsonFermion5D StencilEven -Grid : Message : 94.142504 s : WilsonFermion5D StencilOdd -Grid : Message : 94.142515 s : Stencil calls 3001 -Grid : Message : 94.142519 s : Stencil halogtime 0 -Grid : Message : 94.142522 s : Stencil gathertime 54.0447 -Grid : Message : 94.142525 s : Stencil gathermtime 19.4485 -Grid : Message : 94.142530 s : Stencil mergetime 18.3146 -Grid : Message : 94.142532 s : Stencil decompresstime 0.0593136 -Grid : Message : 94.142535 s : Stencil comms_bytes 2.01327e+08 -Grid : Message : 94.142539 s : Stencil commtime 2989.1 -Grid : Message : 94.142542 s : Stencil 67.3536 GB/s per rank -Grid : Message : 94.142546 s : Stencil 269.414 GB/s per node -Grid : Message : 94.142548 s : WilsonFermion5D Stencil Reporti() -Grid : Message : 94.142551 s : WilsonFermion5D StencilEven Reporti() -Grid : Message : 94.142553 s : WilsonFermion5D StencilOdd Reporti() -Grid : Message : 94.216038 s : r_e6.02111 -Grid : Message : 94.223150 s : r_o6.02102 -Grid : Message : 94.229148 s : res12.0421 -Grid : Message : 94.830824 s : norm diff 0 -Grid : Message : 95.633245 s : norm diff even 0 -Grid : Message : 95.999452 s : norm diff odd 0 +Grid : Message : 1.198523 s : Grid Layout +Grid : Message : 1.198530 s : Global lattice size : 64 64 64 64 +Grid : Message : 1.198534 s : OpenMP threads : 4 +Grid : Message : 1.198535 s : MPI tasks : 2 2 2 2 +Grid : Message : 1.397615 s : Making s innermost grids +Grid : Message : 1.441828 s : Initialising 4d RNG +Grid : Message : 1.547973 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 1.547998 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 1.954777 s : Initialising 5d RNG +Grid : Message : 3.633825 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 3.633869 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 12.162710 s : Initialised RNGs +Grid : Message : 15.882520 s : Drawing gauge field +Grid : Message : 15.816362 s : Random gauge initialised +Grid : Message : 17.279671 s : Setting up Cshift based reference +Grid : Message : 26.331426 s : ***************************************************************** +Grid : Message : 26.331452 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 26.331454 s : ***************************************************************** +Grid : Message : 26.331456 s : ***************************************************************** +Grid : Message : 26.331458 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 26.331459 s : * Vectorising space-time by 8 +Grid : Message : 26.331463 s : * VComplexF size is 64 B +Grid : Message : 26.331465 s : * SINGLE precision +Grid : Message : 26.331467 s : * Using Overlapped Comms/Compute +Grid : Message : 26.331468 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 26.331469 s : ***************************************************************** +Grid : Message : 28.413717 s : Called warmup +Grid : Message : 56.418423 s : Called Dw 3000 times in 2.80047e+07 us +Grid : Message : 56.418476 s : mflop/s = 3.79581e+07 +Grid : Message : 56.418479 s : mflop/s per rank = 2.37238e+06 +Grid : Message : 56.418481 s : mflop/s per node = 9.48953e+06 +Grid : Message : 56.418483 s : RF GiB/s (base 2) = 77130 +Grid : Message : 56.418485 s : mem GiB/s (base 2) = 48206.3 +Grid : Message : 56.422076 s : norm diff 1.03481e-13 +Grid : Message : 56.456894 s : #### Dhop calls report +Grid : Message : 56.456899 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 56.456903 s : WilsonFermion5D TotalTime /Calls : 4710.93 us +Grid : Message : 56.456905 s : WilsonFermion5D CommTime /Calls : 3196.15 us +Grid : Message : 56.456908 s : WilsonFermion5D FaceTime /Calls : 494.392 us +Grid : Message : 56.456910 s : WilsonFermion5D ComputeTime1/Calls : 44.4107 us +Grid : Message : 56.456912 s : WilsonFermion5D ComputeTime2/Calls : 1037.75 us +Grid : Message : 56.456921 s : Average mflops/s per call : 3.55691e+09 +Grid : Message : 56.456925 s : Average mflops/s per call per rank : 2.22307e+08 +Grid : Message : 56.456928 s : Average mflops/s per call per node : 8.89228e+08 +Grid : Message : 56.456930 s : Average mflops/s per call (full) : 3.82915e+07 +Grid : Message : 56.456933 s : Average mflops/s per call per rank (full): 2.39322e+06 +Grid : Message : 56.456952 s : Average mflops/s per call per node (full): 9.57287e+06 +Grid : Message : 56.456954 s : WilsonFermion5D Stencil +Grid : Message : 56.457016 s : Stencil calls 3001 +Grid : Message : 56.457022 s : Stencil halogtime 0 +Grid : Message : 56.457024 s : Stencil gathertime 55.9154 +Grid : Message : 56.457026 s : Stencil gathermtime 20.1073 +Grid : Message : 56.457028 s : Stencil mergetime 18.5585 +Grid : Message : 56.457030 s : Stencil decompresstime 0.0639787 +Grid : Message : 56.457032 s : Stencil comms_bytes 4.02653e+08 +Grid : Message : 56.457034 s : Stencil commtime 6379.93 +Grid : Message : 56.457036 s : Stencil 63.1124 GB/s per rank +Grid : Message : 56.457038 s : Stencil 252.45 GB/s per node +Grid : Message : 56.457040 s : WilsonFermion5D StencilEven +Grid : Message : 56.457048 s : WilsonFermion5D StencilOdd +Grid : Message : 56.457062 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 56.457065 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 56.457066 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 79.259261 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 79.259287 s : Called DwDag +Grid : Message : 79.259288 s : norm dag result 12.0421 +Grid : Message : 79.271740 s : norm dag ref 12.0421 +Grid : Message : 79.287759 s : norm dag diff 7.63236e-14 +Grid : Message : 79.328100 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 79.955951 s : src_e0.499997 +Grid : Message : 80.633620 s : src_o0.500003 +Grid : Message : 80.164163 s : ********************************************************* +Grid : Message : 80.164168 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 80.164170 s : * Vectorising space-time by 8 +Grid : Message : 80.164172 s : * SINGLE precision +Grid : Message : 80.164174 s : * Using Overlapped Comms/Compute +Grid : Message : 80.164177 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 80.164178 s : ********************************************************* +Grid : Message : 93.797635 s : Deo mflop/s = 3.93231e+07 +Grid : Message : 93.797670 s : Deo mflop/s per rank 2.45769e+06 +Grid : Message : 93.797672 s : Deo mflop/s per node 9.83077e+06 +Grid : Message : 93.797674 s : #### Dhop calls report +Grid : Message : 93.797675 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 93.797677 s : WilsonFermion5D TotalTime /Calls : 4542.83 us +Grid : Message : 93.797679 s : WilsonFermion5D CommTime /Calls : 2978.97 us +Grid : Message : 93.797681 s : WilsonFermion5D FaceTime /Calls : 602.287 us +Grid : Message : 93.797683 s : WilsonFermion5D ComputeTime1/Calls : 67.1416 us +Grid : Message : 93.797685 s : WilsonFermion5D ComputeTime2/Calls : 1004.07 us +Grid : Message : 93.797713 s : Average mflops/s per call : 3.30731e+09 +Grid : Message : 93.797717 s : Average mflops/s per call per rank : 2.06707e+08 +Grid : Message : 93.797719 s : Average mflops/s per call per node : 8.26827e+08 +Grid : Message : 93.797721 s : Average mflops/s per call (full) : 3.97084e+07 +Grid : Message : 93.797727 s : Average mflops/s per call per rank (full): 2.48178e+06 +Grid : Message : 93.797732 s : Average mflops/s per call per node (full): 9.92711e+06 +Grid : Message : 93.797735 s : WilsonFermion5D Stencil +Grid : Message : 93.797746 s : WilsonFermion5D StencilEven +Grid : Message : 93.797758 s : WilsonFermion5D StencilOdd +Grid : Message : 93.797769 s : Stencil calls 3001 +Grid : Message : 93.797773 s : Stencil halogtime 0 +Grid : Message : 93.797776 s : Stencil gathertime 56.7458 +Grid : Message : 93.797780 s : Stencil gathermtime 22.6504 +Grid : Message : 93.797782 s : Stencil mergetime 21.1913 +Grid : Message : 93.797786 s : Stencil decompresstime 0.0556481 +Grid : Message : 93.797788 s : Stencil comms_bytes 2.01327e+08 +Grid : Message : 93.797791 s : Stencil commtime 2989.33 +Grid : Message : 93.797795 s : Stencil 67.3484 GB/s per rank +Grid : Message : 93.797798 s : Stencil 269.394 GB/s per node +Grid : Message : 93.797801 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 93.797803 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 93.797805 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 93.873429 s : r_e6.02111 +Grid : Message : 93.879931 s : r_o6.02102 +Grid : Message : 93.885912 s : res12.0421 +Grid : Message : 94.876555 s : norm diff 0 +Grid : Message : 95.485643 s : norm diff even 0 +Grid : Message : 95.581236 s : norm diff odd 0 diff --git a/systems/Tursa/dwf16.slurm b/systems/Tursa/dwf16.slurm new file mode 100644 index 00000000..a35e55be --- /dev/null +++ b/systems/Tursa/dwf16.slurm @@ -0,0 +1,33 @@ +#!/bin/bash +#SBATCH -J dslash +#SBATCH -A tc002 +#SBATCH -t 2:20:00 +#SBATCH --exclusive +#SBATCH --nodes=16 +#SBATCH --ntasks=64 +#SBATCH --ntasks-per-node=4 +#SBATCH --cpus-per-task=8 +#SBATCH --time=12:00:00 +#SBATCH --partition=gpu +#SBATCH --gres=gpu:4 +#SBATCH --output=%x.%j.out +#SBATCH --error=%x.%j.err + +export OMP_NUM_THREADS=4 +export OMPI_MCA_btl=^uct,openib +export UCX_TLS=gdr_copy,rc,rc_x,sm,cuda_copy,cuda_ipc +export UCX_RNDV_SCHEME=put_zcopy +export UCX_RNDV_THRESH=16384 +export UCX_IB_GPU_DIRECT_RDMA=yes +export UCX_MEMTYPE_CACHE=n +OPT="--comms-overlap --comms-concurrent" + + +mpirun -np $SLURM_NTASKS -x LD_LIBRARY_PATH --bind-to none ./mpiwrapper.sh \ + ./benchmarks/Benchmark_dwf_fp32 \ + $OPT \ + --mpi 2.2.2.8 \ + --accelerator-threads 8 \ + --grid 64.64.64.256 \ + --shm 2048 > dwf.16node.perf + diff --git a/systems/Tursa/dwf.slurm b/systems/Tursa/dwf4.slurm similarity index 96% rename from systems/Tursa/dwf.slurm rename to systems/Tursa/dwf4.slurm index 35675d1c..65191398 100644 --- a/systems/Tursa/dwf.slurm +++ b/systems/Tursa/dwf4.slurm @@ -31,7 +31,7 @@ mpirun -np $SLURM_NTASKS -x LD_LIBRARY_PATH --bind-to none \ --mpi 2.2.2.2 \ --accelerator-threads 8 \ --grid 64.64.64.64 \ - --shm 2048 > dwf.perf + --shm 2048 > dwf.4node.perf From 7ee66bf4533309bc1be3783fd278a85bfbb1237d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 19 Sep 2021 19:45:20 +0100 Subject: [PATCH 220/399] Make sure H5NS has empty definition if HDF5 built without C++ namespace. Add comment in Hdf5IO.cc indicating likely source of error using H5NS, i.e. lack of --enable-cxx in hdf5 configure. --- Grid/serialisation/Hdf5IO.cc | 2 +- Grid/serialisation/Hdf5IO.h | 4 ---- Grid/serialisation/Hdf5Type.h | 4 +++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.cc b/Grid/serialisation/Hdf5IO.cc index d3c7061e..db78df8e 100644 --- a/Grid/serialisation/Hdf5IO.cc +++ b/Grid/serialisation/Hdf5IO.cc @@ -33,7 +33,7 @@ using namespace Grid; #ifndef H5_NO_NAMESPACE -using namespace H5NS; +using namespace H5NS; // Compile error here? Try adding --enable-cxx to hdf5 configure #endif // Writer implementation /////////////////////////////////////////////////////// diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 46cb07e1..ae5e740b 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -40,10 +40,6 @@ #include <Grid/tensors/Tensors.h> #include "Hdf5Type.h" -#ifndef H5_NO_NAMESPACE -#define H5NS H5 -#endif - // default thresold above which datasets are used instead of attributes #ifndef HDF5_DEF_DATASET_THRES #define HDF5_DEF_DATASET_THRES 6u diff --git a/Grid/serialisation/Hdf5Type.h b/Grid/serialisation/Hdf5Type.h index 64dda349..d8a0dd22 100644 --- a/Grid/serialisation/Hdf5Type.h +++ b/Grid/serialisation/Hdf5Type.h @@ -5,7 +5,9 @@ #include <complex> #include <memory> -#ifndef H5_NO_NAMESPACE +#ifdef H5_NO_NAMESPACE +#define H5NS +#else #define H5NS H5 #endif From 68650b61fe193c8d64c8b86767806697df35f0e6 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 00:51:01 +0200 Subject: [PATCH 221/399] Options controlling behaviour --- Grid/util/Init.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Grid/util/Init.cc b/Grid/util/Init.cc index ab2d2399..697f3ac1 100644 --- a/Grid/util/Init.cc +++ b/Grid/util/Init.cc @@ -301,6 +301,13 @@ void Grid_init(int *argc,char ***argv) GlobalSharedMemory::MAX_MPI_SHM_BYTES = MB64*1024LL*1024LL; } + if( GridCmdOptionExists(*argv,*argv+*argc,"--shm-mpi") ){ + int forcempi; + arg= GridCmdOptionPayload(*argv,*argv+*argc,"--shm-mpi"); + GridCmdOptionInt(arg,forcempi); + Stencil_force_mpi = (bool)forcempi; + } + if( GridCmdOptionExists(*argv,*argv+*argc,"--device-mem") ){ int MB; arg= GridCmdOptionPayload(*argv,*argv+*argc,"--device-mem"); @@ -419,7 +426,9 @@ void Grid_init(int *argc,char ***argv) std::cout<<GridLogMessage<<" --threads n : default number of OMP threads"<<std::endl; std::cout<<GridLogMessage<<" --grid n.n.n.n : default Grid size"<<std::endl; std::cout<<GridLogMessage<<" --shm M : allocate M megabytes of shared memory for comms"<<std::endl; - std::cout<<GridLogMessage<<" --shm-hugepages : use explicit huge pages in mmap call "<<std::endl; + std::cout<<GridLogMessage<<" --shm-mpi 0|1 : Force MPI usage under multi-rank per node "<<std::endl; + std::cout<<GridLogMessage<<" --shm-hugepages : use explicit huge pages in mmap call "<<std::endl; + std::cout<<GridLogMessage<<" --device-mem M : Size of device software cache for lattice fields (MB) "<<std::endl; std::cout<<GridLogMessage<<std::endl; std::cout<<GridLogMessage<<"Verbose and debug:"<<std::endl; std::cout<<GridLogMessage<<std::endl; From 109507888bb524fc4cc3ebcb4e2b2b69ce188a48 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 00:53:25 +0200 Subject: [PATCH 222/399] Option to force use of MPI over Nvlink --- Grid/communicator/Communicator_base.cc | 2 ++ Grid/communicator/Communicator_base.h | 6 +----- Grid/communicator/Communicator_mpi3.cc | 6 ++++++ Grid/communicator/SharedMemoryMPI.cc | 8 ++++++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Grid/communicator/Communicator_base.cc b/Grid/communicator/Communicator_base.cc index dfa4846b..79efb90c 100644 --- a/Grid/communicator/Communicator_base.cc +++ b/Grid/communicator/Communicator_base.cc @@ -33,6 +33,8 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> NAMESPACE_BEGIN(Grid); +bool Stencil_force_mpi = true; + /////////////////////////////////////////////////////////////// // Info that is setup once and indept of cartesian layout /////////////////////////////////////////////////////////////// diff --git a/Grid/communicator/Communicator_base.h b/Grid/communicator/Communicator_base.h index 4510b03e..ffcfe37a 100644 --- a/Grid/communicator/Communicator_base.h +++ b/Grid/communicator/Communicator_base.h @@ -35,11 +35,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> NAMESPACE_BEGIN(Grid); -#ifdef GRID_MPI3_SHM_NVLINK -const bool Stencil_force_mpi = true; -#else -const bool Stencil_force_mpi = false; -#endif +extern bool Stencil_force_mpi ; class CartesianCommunicator : public SharedMemory { diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index a6eeeebc..01335b41 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -384,6 +384,12 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques assert(ierr==0); list.push_back(xrq); off_node_bytes+=bytes; + } else { + // TODO : make a OMP loop on CPU, call threaded bcopy + void *shm = (void *) this->ShmBufferTranslate(dest,recv); + assert(shm!=NULL); + acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); + acceleratorCopySynchronise(); // MPI prob slower } if ( CommunicatorPolicy == CommunicatorPolicySequential ) { diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 11788744..442338a1 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -543,6 +543,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) ////////////////////////////////////////////////// // If it is me, pass around the IPC access key ////////////////////////////////////////////////// + void * thisBuf = ShmCommBuf; + if(!Stencil_force_mpi) { #ifdef GRID_SYCL_LEVEL_ZERO_IPC ze_ipc_mem_handle_t handle; if ( r==WorldShmRank ) { @@ -580,6 +582,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) } } #endif + ////////////////////////////////////////////////// // Share this IPC handle across the Shm Comm ////////////////////////////////////////////////// @@ -595,7 +598,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) /////////////////////////////////////////////////////////////// // If I am not the source, overwrite thisBuf with remote buffer /////////////////////////////////////////////////////////////// - void * thisBuf = ShmCommBuf; + #ifdef GRID_SYCL_LEVEL_ZERO_IPC if ( r!=WorldShmRank ) { thisBuf = nullptr; @@ -636,7 +639,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) /////////////////////////////////////////////////////////////// // Save a copy of the device buffers /////////////////////////////////////////////////////////////// - WorldShmCommBufs[r] = thisBuf; + } + WorldShmCommBufs[r] = thisBuf; #else WorldShmCommBufs[r] = ShmCommBuf; #endif From 894654f7ef60f85222f9c1775f0f8013e55e3fb5 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 01:02:34 +0200 Subject: [PATCH 223/399] Simplificatoin, always gather faces --- Grid/stencil/Stencil.h | 185 ++++++----------------------------------- 1 file changed, 26 insertions(+), 159 deletions(-) diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index ca581804..fb01abbb 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -326,21 +326,8 @@ public: int xmit_to_rank; if ( ! comm_dim ) return 1; - - int nbr_proc; - if (displacement>0) nbr_proc = 1; - else nbr_proc = pd-1; - - // FIXME this logic needs to be sorted for three link term - // assert( (displacement==1) || (displacement==-1)); - // Present hack only works for >= 4^4 subvol per node - _grid->ShiftedRanks(dimension,nbr_proc,xmit_to_rank,recv_from_rank); - - void *shm = (void *) _grid->ShmBufferTranslate(recv_from_rank,this->u_recv_buf_p); - - if ( (shm==NULL) || Stencil_force_mpi ) return 0; - - return 1; + if ( displacement == 0 ) return 1; + return 0; } ////////////////////////////////////////// @@ -1020,7 +1007,6 @@ public: int cb= (cbmask==0x2)? Odd : Even; int sshift= _grid->CheckerBoardShiftForCB(rhs.Checkerboard(),dimension,shift,cb); - int shm_receive_only = 1; for(int x=0;x<rd;x++){ int sx = (x+sshift)%rd; @@ -1052,10 +1038,6 @@ public: assert (xmit_to_rank != _grid->ThisRank()); assert (recv_from_rank != _grid->ThisRank()); - ///////////////////////////////////////////////////////// - // try the direct copy if possible - ///////////////////////////////////////////////////////// - cobj *send_buf; cobj *recv_buf; if ( compress.DecompressionStep() ) { recv_buf=u_simd_recv_buf[0]; @@ -1063,52 +1045,36 @@ public: recv_buf=this->u_recv_buf_p; } - send_buf = (cobj *)_grid->ShmBufferTranslate(xmit_to_rank,recv_buf); - if ( (send_buf==NULL) || Stencil_force_mpi ) { - send_buf = this->u_send_buf_p; - } - - // Find out if we get the direct copy. - void *success = (void *) _grid->ShmBufferTranslate(recv_from_rank,this->u_send_buf_p); - if ((success==NULL)||Stencil_force_mpi) { - // we found a packet that comes from MPI and contributes to this leg of stencil - shm_receive_only = 0; - } + cobj *send_buf; + send_buf = this->u_send_buf_p; // Gather locally, must send + //////////////////////////////////////////////////////// + // Gather locally + //////////////////////////////////////////////////////// gathertime-=usecond(); assert(send_buf!=NULL); Gather_plane_simple_table(face_table[face_idx],rhs,send_buf,compress,u_comm_offset,so); face_idx++; gathertime+=usecond(); + /////////////////////////////////////////////////////////// + // Build a list of things to do after we synchronise GPUs + // Start comms now??? + /////////////////////////////////////////////////////////// + AddPacket((void *)&send_buf[u_comm_offset], + (void *)&recv_buf[u_comm_offset], + xmit_to_rank, + recv_from_rank, + bytes); + if ( compress.DecompressionStep() ) { - - if ( shm_receive_only ) { // Early decompress before MPI is finished is possible - AddDecompress(&this->u_recv_buf_p[u_comm_offset], - &recv_buf[u_comm_offset], - words,DecompressionsSHM); - } else { // Decompress after MPI is finished - AddDecompress(&this->u_recv_buf_p[u_comm_offset], - &recv_buf[u_comm_offset], - words,Decompressions); - } - - AddPacket((void *)&send_buf[u_comm_offset], - (void *)&recv_buf[u_comm_offset], - xmit_to_rank, - recv_from_rank, - bytes); - - } else { - AddPacket((void *)&send_buf[u_comm_offset], - (void *)&this->u_recv_buf_p[u_comm_offset], - xmit_to_rank, - recv_from_rank, - bytes); + AddDecompress(&this->u_recv_buf_p[u_comm_offset], + &recv_buf[u_comm_offset], + words,Decompressions); } u_comm_offset+=words; } } - return shm_receive_only; + return 0; } template<class compressor> @@ -1159,7 +1125,6 @@ public: int sshift= _grid->CheckerBoardShiftForCB(rhs.Checkerboard(),dimension,shift,cb); // loop over outer coord planes orthog to dim - int shm_receive_only = 1; for(int x=0;x<rd;x++){ int any_offnode = ( ((x+sshift)%fd) >= rd ); @@ -1214,20 +1179,7 @@ public: _grid->ShiftedRanks(dimension,nbr_proc,xmit_to_rank,recv_from_rank); - // shm == receive pointer if offnode - // shm == Translate[send pointer] if on node -- my view of his send pointer - cobj *shm = (cobj *) _grid->ShmBufferTranslate(recv_from_rank,sp); - if ((shm==NULL)||Stencil_force_mpi) { - shm = rp; - // we found a packet that comes from MPI and contributes to this shift. - // is_same_node is only used in the WilsonStencil, and gets set for this point in the stencil. - // Kernel will add the exterior_terms except if is_same_node. - shm_receive_only = 0; - // leg of stencil - } - // if Direct, StencilSendToRecvFrom will suppress copy to a peer on node - // assuming above pointer flip - rpointers[i] = shm; + rpointers[i] = rp; AddPacket((void *)sp,(void *)rp,xmit_to_rank,recv_from_rank,bytes); @@ -1239,102 +1191,17 @@ public: } } - if ( shm_receive_only ) { - AddMerge(&this->u_recv_buf_p[u_comm_offset],rpointers,reduced_buffer_size,permute_type,MergersSHM); - } else { - AddMerge(&this->u_recv_buf_p[u_comm_offset],rpointers,reduced_buffer_size,permute_type,Mergers); - } + AddMerge(&this->u_recv_buf_p[u_comm_offset],rpointers,reduced_buffer_size,permute_type,Mergers); u_comm_offset +=buffer_size; } } - return shm_receive_only; + return 0; } - void ZeroCounters(void) { - gathertime = 0.; - commtime = 0.; - mpi3synctime=0.; - mpi3synctime_g=0.; - shmmergetime=0.; - for(int i=0;i<this->_npoints;i++){ - comm_time_thr[i]=0; - comm_bytes_thr[i]=0; - comm_enter_thr[i]=0; - comm_leave_thr[i]=0; - shm_bytes_thr[i]=0; - } - halogtime = 0.; - mergetime = 0.; - decompresstime = 0.; - gathermtime = 0.; - splicetime = 0.; - nosplicetime = 0.; - comms_bytes = 0.; - shm_bytes = 0.; - calls = 0.; - }; + void ZeroCounters(void) { }; - void Report(void) { -#define AVERAGE(A) -#define PRINTIT(A) AVERAGE(A); std::cout << GridLogMessage << " Stencil " << #A << " "<< A/calls<<std::endl; - RealD NP = _grid->_Nprocessors; - RealD NN = _grid->NodeCount(); - double t = 0; - // if comm_time_thr is set they were all done in parallel so take the max - // but add up the bytes - int threaded = 0 ; - for (int i = 0; i < 8; ++i) { - if ( comm_time_thr[i]>0.0 ) { - threaded = 1; - comms_bytes += comm_bytes_thr[i]; - shm_bytes += shm_bytes_thr[i]; - if (t < comm_time_thr[i]) t = comm_time_thr[i]; - } - } - if (threaded) commtime += t; - - _grid->GlobalSum(commtime); commtime/=NP; - if ( calls > 0. ) { - std::cout << GridLogMessage << " Stencil calls "<<calls<<std::endl; - PRINTIT(halogtime); - PRINTIT(gathertime); - PRINTIT(gathermtime); - PRINTIT(mergetime); - PRINTIT(decompresstime); - if(comms_bytes>1.0){ - PRINTIT(comms_bytes); - PRINTIT(commtime); - std::cout << GridLogMessage << " Stencil " << comms_bytes/commtime/1000. << " GB/s per rank"<<std::endl; - std::cout << GridLogMessage << " Stencil " << comms_bytes/commtime/1000.*NP/NN << " GB/s per node"<<std::endl; - } - if(shm_bytes>1.0){ - PRINTIT(shm_bytes); // X bytes + R bytes - // Double this to include spin projection overhead with 2:1 ratio in wilson - auto gatheralltime = gathertime+gathermtime; - std::cout << GridLogMessage << " Stencil SHM " << (shm_bytes)/gatheralltime/1000. << " GB/s per rank"<<std::endl; - std::cout << GridLogMessage << " Stencil SHM " << (shm_bytes)/gatheralltime/1000.*NP/NN << " GB/s per node"<<std::endl; - - auto all_bytes = comms_bytes+shm_bytes; - std::cout << GridLogMessage << " Stencil SHM all " << (all_bytes)/gatheralltime/1000. << " GB/s per rank"<<std::endl; - std::cout << GridLogMessage << " Stencil SHM all " << (all_bytes)/gatheralltime/1000.*NP/NN << " GB/s per node"<<std::endl; - - auto membytes = (shm_bytes + comms_bytes/2) // read/write - + (shm_bytes+comms_bytes)/2 * sizeof(vobj)/sizeof(cobj); - std::cout << GridLogMessage << " Stencil SHM mem " << (membytes)/gatheralltime/1000. << " GB/s per rank"<<std::endl; - std::cout << GridLogMessage << " Stencil SHM mem " << (membytes)/gatheralltime/1000.*NP/NN << " GB/s per node"<<std::endl; - } - /* - PRINTIT(mpi3synctime); - PRINTIT(mpi3synctime_g); - PRINTIT(shmmergetime); - PRINTIT(splicetime); - PRINTIT(nosplicetime); - */ - } -#undef PRINTIT -#undef AVERAGE - }; + void Report(void) { }; }; NAMESPACE_END(Grid); From 1fb6aaf15058cd5dda0bdc6be014a4af2c3754f4 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 01:03:07 +0200 Subject: [PATCH 224/399] Device 2 Device with cudaMemcpy --- Grid/threads/Accelerator.cc | 1 + Grid/threads/Accelerator.h | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 9c40f538..32a6693e 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -8,6 +8,7 @@ void acceleratorThreads(uint32_t t) {accelerator_threads = t;}; #ifdef GRID_CUDA cudaDeviceProp *gpu_props; +cudaStream_t copyStream; void acceleratorInit(void) { int nDevices = 1; diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index a8c91aa8..fa4d1dc0 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -105,6 +105,7 @@ void acceleratorInit(void); #define accelerator_inline __host__ __device__ inline extern int acceleratorAbortOnGpuError; +extern cudaStream_t copyStream; accelerator_inline int acceleratorSIMTlane(int Nsimd) { #ifdef GRID_SIMT @@ -213,9 +214,13 @@ inline void *acceleratorAllocDevice(size_t bytes) inline void acceleratorFreeShared(void *ptr){ cudaFree(ptr);}; inline void acceleratorFreeDevice(void *ptr){ cudaFree(ptr);}; inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { cudaMemcpy(to,from,bytes, cudaMemcpyHostToDevice);} -inline void acceleratorCopyDeviceToDevice(void *from,void *to,size_t bytes) { cudaMemcpy(to,from,bytes, cudaMemcpyDeviceToDevice);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ cudaMemcpy(to,from,bytes, cudaMemcpyDeviceToHost);} inline void acceleratorMemSet(void *base,int value,size_t bytes) { cudaMemset(base,value,bytes);} +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) // Asynch +{ + cudaMemcpyAsync(to,from,bytes, cudaMemcpyDeviceToDevice,copyStream); +} +inline void acceleratorCopySynchronise(void) { cudaStreamSynchronize(copyStream); }; inline int acceleratorIsCommunicable(void *ptr) { // int uvm=0; @@ -289,7 +294,10 @@ inline void *acceleratorAllocShared(size_t bytes){ return malloc_shared(bytes,*t inline void *acceleratorAllocDevice(size_t bytes){ return malloc_device(bytes,*theGridAccelerator);}; inline void acceleratorFreeShared(void *ptr){free(ptr,*theGridAccelerator);}; inline void acceleratorFreeDevice(void *ptr){free(ptr,*theGridAccelerator);}; -inline void acceleratorCopyDeviceToDevice(void *from,void *to,size_t bytes) { theGridAccelerator->memcpy(to,from,bytes); theGridAccelerator->wait();} +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { + theGridAccelerator->memcpy(to,from,bytes); +} +inline void acceleratorCopySynchronise(void) { theGridAccelerator->wait(); } inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { theGridAccelerator->memcpy(to,from,bytes); theGridAccelerator->wait();} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ theGridAccelerator->memcpy(to,from,bytes); theGridAccelerator->wait();} inline void acceleratorMemSet(void *base,int value,size_t bytes) { theGridAccelerator->memset(base,value,bytes); theGridAccelerator->wait();} @@ -394,7 +402,8 @@ inline void acceleratorFreeShared(void *ptr){ hipFree(ptr);}; inline void acceleratorFreeDevice(void *ptr){ hipFree(ptr);}; inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { hipMemcpy(to,from,bytes, hipMemcpyHostToDevice);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ hipMemcpy(to,from,bytes, hipMemcpyDeviceToHost);} -inline void acceleratorCopyDeviceToDevice(void *from,void *to,size_t bytes) { hipMemcpy(to,from,bytes, hipMemcpyDeviceToDevice);} +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { hipMemcpy(to,from,bytes, hipMemcpyDeviceToDevice);} +inline void acceleratorCopySynchronise(void) { } inline void acceleratorMemSet(void *base,int value,size_t bytes) { hipMemset(base,value,bytes);} #endif @@ -435,7 +444,8 @@ inline void acceleratorMemSet(void *base,int value,size_t bytes) { hipMemset(bas accelerator_inline int acceleratorSIMTlane(int Nsimd) { return 0; } // CUDA specific inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { memcpy(to,from,bytes);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ memcpy(to,from,bytes);} -inline void acceleratorCopyDeviceToDevice(void *from,void *to,size_t bytes) { memcpy(to,from,bytes);} +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { memcpy(to,from,bytes);} +inline void acceleratorCopySynchronize(void) {}; inline int acceleratorIsCommunicable(void *ptr){ return 1; } inline void acceleratorMemSet(void *base,int value,size_t bytes) { memset(base,value,bytes);} From e188c0512ebee79bfb15906676af1c9e142aa21a Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 01:04:30 +0200 Subject: [PATCH 225/399] Udpdate --- systems/Booster/comms.4node.perf | 129 +++++++++++++++++++++++++ systems/Booster/config-command | 14 +++ systems/Booster/dwf.16node.perf | 156 +++++++++++++++++++++++++++++++ systems/Booster/dwf.4node.perf | 156 +++++++++++++++++++++++++++++++ systems/Booster/dwf16.slurm | 29 ++++++ systems/Booster/dwf4.slurm | 40 ++++++++ systems/Booster/sourceme.sh | 5 + 7 files changed, 529 insertions(+) create mode 100644 systems/Booster/comms.4node.perf create mode 100644 systems/Booster/config-command create mode 100644 systems/Booster/dwf.16node.perf create mode 100644 systems/Booster/dwf.4node.perf create mode 100644 systems/Booster/dwf16.slurm create mode 100644 systems/Booster/dwf4.slurm create mode 100644 systems/Booster/sourceme.sh diff --git a/systems/Booster/comms.4node.perf b/systems/Booster/comms.4node.perf new file mode 100644 index 00000000..30ad9821 --- /dev/null +++ b/systems/Booster/comms.4node.perf @@ -0,0 +1,129 @@ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: NVIDIA A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42505273344 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 3 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x14e740000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=f660dc67e4b193afc4015bc5e5fe47cfdbb0356e: (HEAD -> develop, origin/develop, origin/HEAD) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34004218675 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.706584 s : Grid is setup to use 4 threads +Grid : Message : 0.706591 s : Number of iterations to average: 250 +Grid : Message : 0.706592 s : ==================================================================================================== +Grid : Message : 0.706593 s : = Benchmarking sequential halo exchange from host memory +Grid : Message : 0.706594 s : ==================================================================================================== +Grid : Message : 0.706595 s : L Ls bytes MB/s uni (err/min/max) MB/s bidi (err/min/max) +Grid : Message : 0.744123 s : 8 8 393216 45765.4 91530.7 +Grid : Message : 0.760079 s : 8 8 393216 49399.0 98798.0 +Grid : Message : 0.776168 s : 8 8 393216 48904.4 97808.8 +Grid : Message : 0.793391 s : 8 8 393216 45680.3 91360.6 +Grid : Message : 0.841483 s : 12 8 1327104 61988.1 123976.3 +Grid : Message : 0.881324 s : 12 8 1327104 66673.6 133347.1 +Grid : Message : 0.923429 s : 12 8 1327104 63049.8 126099.6 +Grid : Message : 0.965199 s : 12 8 1327104 63558.6 127117.2 +Grid : Message : 1.759350 s : 16 8 3145728 63505.2 127010.3 +Grid : Message : 1.162325 s : 16 8 3145728 72851.5 145703.0 +Grid : Message : 1.252374 s : 16 8 3145728 69873.2 139746.5 +Grid : Message : 1.343308 s : 16 8 3145728 69193.9 138387.8 +Grid : Message : 1.537929 s : 20 8 6144000 69283.6 138567.2 +Grid : Message : 1.689674 s : 20 8 6144000 80997.7 161995.4 +Grid : Message : 1.844366 s : 20 8 6144000 79440.4 158880.8 +Grid : Message : 2.585000 s : 20 8 6144000 78662.8 157325.7 +Grid : Message : 2.296310 s : 24 8 10616832 78318.9 156637.8 +Grid : Message : 2.552185 s : 24 8 10616832 82996.5 165993.0 +Grid : Message : 2.810117 s : 24 8 10616832 82325.6 164651.2 +Grid : Message : 3.708760 s : 24 8 10616832 81433.0 162866.1 +Grid : Message : 3.519278 s : 28 8 16859136 81498.1 162996.1 +Grid : Message : 3.919983 s : 28 8 16859136 84154.5 168309.0 +Grid : Message : 4.324104 s : 28 8 16859136 83438.8 166877.5 +Grid : Message : 4.726446 s : 28 8 16859136 83807.5 167615.0 +Grid : Message : 5.379742 s : 32 8 25165824 83183.5 166366.9 +Grid : Message : 5.973767 s : 32 8 25165824 84735.1 169470.3 +Grid : Message : 6.566572 s : 32 8 25165824 84905.7 169811.3 +Grid : Message : 7.161794 s : 32 8 25165824 84561.2 169122.3 +Grid : Message : 7.162522 s : ==================================================================================================== +Grid : Message : 7.162527 s : = Benchmarking sequential halo exchange from GPU memory +Grid : Message : 7.162528 s : ==================================================================================================== +Grid : Message : 7.162529 s : L Ls bytes MB/s uni (err/min/max) MB/s bidi (err/min/max) +Grid : Message : 7.222442 s : 8 8 393216 14172.2 28344.5 +Grid : Message : 7.253802 s : 8 8 393216 25092.0 50183.9 +Grid : Message : 7.278690 s : 8 8 393216 31614.1 63228.2 +Grid : Message : 7.304138 s : 8 8 393216 30915.6 61831.3 +Grid : Message : 7.347792 s : 12 8 1327104 109873.2 219746.5 +Grid : Message : 7.370904 s : 12 8 1327104 114900.8 229801.6 +Grid : Message : 7.418730 s : 12 8 1327104 55509.9 111019.9 +Grid : Message : 7.466445 s : 12 8 1327104 55639.1 111278.2 +Grid : Message : 7.531724 s : 16 8 3145728 168680.8 337361.6 +Grid : Message : 7.567205 s : 16 8 3145728 177379.0 354758.0 +Grid : Message : 7.669544 s : 16 8 3145728 61483.2 122966.5 +Grid : Message : 7.771543 s : 16 8 3145728 61687.6 123375.2 +Grid : Message : 7.852470 s : 20 8 6144000 232313.7 464627.4 +Grid : Message : 7.905813 s : 20 8 6144000 230410.1 460820.2 +Grid : Message : 8.979520 s : 20 8 6144000 63957.7 127915.4 +Grid : Message : 8.287586 s : 20 8 6144000 64802.3 129604.5 +Grid : Message : 8.393759 s : 24 8 10616832 272264.9 544529.7 +Grid : Message : 8.471189 s : 24 8 10616832 274276.5 548553.0 +Grid : Message : 8.794453 s : 24 8 10616832 65688.1 131376.1 +Grid : Message : 9.122233 s : 24 8 10616832 64782.8 129565.6 +Grid : Message : 9.263925 s : 28 8 16859136 300457.8 600915.5 +Grid : Message : 9.375294 s : 28 8 16859136 302794.4 605588.7 +Grid : Message : 9.757443 s : 28 8 16859136 88236.5 176473.1 +Grid : Message : 10.134856 s : 28 8 16859136 89343.4 178686.7 +Grid : Message : 10.318775 s : 32 8 25165824 327347.5 654695.1 +Grid : Message : 10.470617 s : 32 8 25165824 331500.0 663000.0 +Grid : Message : 11.235800 s : 32 8 25165824 91024.0 182048.1 +Grid : Message : 11.576311 s : 32 8 25165824 91062.1 182124.2 +Grid : Message : 11.592542 s : ==================================================================================================== +Grid : Message : 11.592548 s : = All done; Bye Bye +Grid : Message : 11.592549 s : ==================================================================================================== diff --git a/systems/Booster/config-command b/systems/Booster/config-command new file mode 100644 index 00000000..8530c5f9 --- /dev/null +++ b/systems/Booster/config-command @@ -0,0 +1,14 @@ +LIME=/p/home/jusers/boyle2/juwels/gm2dwf/boyle/ +../../configure \ + --enable-comms=mpi \ + --enable-simd=GPU \ + --enable-gen-simd-width=64 \ + --enable-shm=nvlink \ + --enable-accelerator=cuda \ + --with-lime=$LIME \ + --disable-accelerator-cshift \ + --disable-unified \ + CXX=nvcc \ + LDFLAGS="-cudart shared " \ + CXXFLAGS="-ccbin mpicxx -gencode arch=compute_80,code=sm_80 -std=c++14 -cudart shared" + diff --git a/systems/Booster/dwf.16node.perf b/systems/Booster/dwf.16node.perf new file mode 100644 index 00000000..8dcdcaed --- /dev/null +++ b/systems/Booster/dwf.16node.perf @@ -0,0 +1,156 @@ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: NVIDIA A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42505273344 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 3 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 64 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x14ac40000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=f660dc67e4b193afc4015bc5e5fe47cfdbb0356e: (HEAD -> develop, origin/develop, origin/HEAD) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34004218675 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.910318 s : Grid Layout +Grid : Message : 0.910320 s : Global lattice size : 64 64 64 256 +Grid : Message : 0.910325 s : OpenMP threads : 4 +Grid : Message : 0.910326 s : MPI tasks : 2 2 2 8 +Grid : Message : 0.973956 s : Making s innermost grids +Grid : Message : 1.198830 s : Initialising 4d RNG +Grid : Message : 1.119813 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 1.119870 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 2.683307 s : Initialising 5d RNG +Grid : Message : 4.220535 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 4.220563 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 37.198140 s : Initialised RNGs +Grid : Message : 39.952612 s : Drawing gauge field +Grid : Message : 40.488019 s : Random gauge initialised +Grid : Message : 42.659220 s : Setting up Cshift based reference +Grid : Message : 47.622210 s : ***************************************************************** +Grid : Message : 47.622236 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 47.622237 s : ***************************************************************** +Grid : Message : 47.622238 s : ***************************************************************** +Grid : Message : 47.622239 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 47.622240 s : * Vectorising space-time by 8 +Grid : Message : 47.622241 s : * VComplexF size is 64 B +Grid : Message : 47.622242 s : * SINGLE precision +Grid : Message : 47.622243 s : * Using Overlapped Comms/Compute +Grid : Message : 47.622244 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 47.622245 s : ***************************************************************** +Grid : Message : 48.950210 s : Called warmup +Grid : Message : 77.311124 s : Called Dw 3000 times in 2.83592e+07 us +Grid : Message : 77.311181 s : mflop/s = 1.49934e+08 +Grid : Message : 77.311184 s : mflop/s per rank = 2.34273e+06 +Grid : Message : 77.311185 s : mflop/s per node = 9.37091e+06 +Grid : Message : 77.311186 s : RF GiB/s (base 2) = 304663 +Grid : Message : 77.311187 s : mem GiB/s (base 2) = 190415 +Grid : Message : 77.314752 s : norm diff 1.03478e-13 +Grid : Message : 77.349587 s : #### Dhop calls report +Grid : Message : 77.349591 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 77.349613 s : WilsonFermion5D TotalTime /Calls : 4761.53 us +Grid : Message : 77.349615 s : WilsonFermion5D CommTime /Calls : 3363.09 us +Grid : Message : 77.349616 s : WilsonFermion5D FaceTime /Calls : 469.094 us +Grid : Message : 77.349617 s : WilsonFermion5D ComputeTime1/Calls : 26.8794 us +Grid : Message : 77.349618 s : WilsonFermion5D ComputeTime2/Calls : 949.276 us +Grid : Message : 77.349702 s : Average mflops/s per call : 2.68569e+10 +Grid : Message : 77.349710 s : Average mflops/s per call per rank : 4.1964e+08 +Grid : Message : 77.349711 s : Average mflops/s per call per node : 1.67856e+09 +Grid : Message : 77.349712 s : Average mflops/s per call (full) : 1.51538e+08 +Grid : Message : 77.349713 s : Average mflops/s per call per rank (full): 2.36779e+06 +Grid : Message : 77.349714 s : Average mflops/s per call per node (full): 9.47115e+06 +Grid : Message : 77.349715 s : WilsonFermion5D Stencil +Grid : Message : 77.349716 s : WilsonFermion5D StencilEven +Grid : Message : 77.349717 s : WilsonFermion5D StencilOdd +Grid : Message : 77.349718 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 77.349719 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 77.349720 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 104.883719 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 104.883743 s : Called DwDag +Grid : Message : 104.883744 s : norm dag result 12.0421 +Grid : Message : 104.901901 s : norm dag ref 12.0421 +Grid : Message : 104.917822 s : norm dag diff 7.63254e-14 +Grid : Message : 104.957229 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 105.334551 s : src_e0.499998 +Grid : Message : 105.416616 s : src_o0.500002 +Grid : Message : 105.486729 s : ********************************************************* +Grid : Message : 105.486732 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 105.486733 s : * Vectorising space-time by 8 +Grid : Message : 105.486734 s : * SINGLE precision +Grid : Message : 105.486739 s : * Using Overlapped Comms/Compute +Grid : Message : 105.486740 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 105.486741 s : ********************************************************* +Grid : Message : 119.695464 s : Deo mflop/s = 1.5039e+08 +Grid : Message : 119.695494 s : Deo mflop/s per rank 2.34984e+06 +Grid : Message : 119.695496 s : Deo mflop/s per node 9.39937e+06 +Grid : Message : 119.695502 s : #### Dhop calls report +Grid : Message : 119.695503 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 119.695505 s : WilsonFermion5D TotalTime /Calls : 4734.45 us +Grid : Message : 119.695507 s : WilsonFermion5D CommTime /Calls : 3287.23 us +Grid : Message : 119.695508 s : WilsonFermion5D FaceTime /Calls : 537.724 us +Grid : Message : 119.695509 s : WilsonFermion5D ComputeTime1/Calls : 16.0483 us +Grid : Message : 119.695510 s : WilsonFermion5D ComputeTime2/Calls : 939.854 us +Grid : Message : 119.695533 s : Average mflops/s per call : 4.50726e+10 +Grid : Message : 119.695535 s : Average mflops/s per call per rank : 7.04259e+08 +Grid : Message : 119.695536 s : Average mflops/s per call per node : 2.81703e+09 +Grid : Message : 119.695537 s : Average mflops/s per call (full) : 1.52405e+08 +Grid : Message : 119.695538 s : Average mflops/s per call per rank (full): 2.38133e+06 +Grid : Message : 119.695539 s : Average mflops/s per call per node (full): 9.52532e+06 +Grid : Message : 119.695540 s : WilsonFermion5D Stencil +Grid : Message : 119.695541 s : WilsonFermion5D StencilEven +Grid : Message : 119.695542 s : WilsonFermion5D StencilOdd +Grid : Message : 119.695543 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 119.695544 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 119.695545 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 119.752707 s : r_e6.02108 +Grid : Message : 119.759448 s : r_o6.02101 +Grid : Message : 119.765382 s : res12.0421 +Grid : Message : 120.419093 s : norm diff 0 +Grid : Message : 120.829772 s : norm diff even 0 +Grid : Message : 120.909078 s : norm diff odd 0 diff --git a/systems/Booster/dwf.4node.perf b/systems/Booster/dwf.4node.perf new file mode 100644 index 00000000..7c0458e3 --- /dev/null +++ b/systems/Booster/dwf.4node.perf @@ -0,0 +1,156 @@ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: NVIDIA A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42505273344 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 3 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x14b7a0000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=f660dc67e4b193afc4015bc5e5fe47cfdbb0356e: (HEAD -> develop, origin/develop, origin/HEAD) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34004218675 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.751288 s : Grid Layout +Grid : Message : 0.751291 s : Global lattice size : 64 64 64 64 +Grid : Message : 0.751296 s : OpenMP threads : 4 +Grid : Message : 0.751297 s : MPI tasks : 2 2 2 2 +Grid : Message : 0.792527 s : Making s innermost grids +Grid : Message : 0.835495 s : Initialising 4d RNG +Grid : Message : 0.940402 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 0.940421 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 1.336448 s : Initialising 5d RNG +Grid : Message : 2.956230 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 2.956251 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 11.242345 s : Initialised RNGs +Grid : Message : 13.415017 s : Drawing gauge field +Grid : Message : 13.937529 s : Random gauge initialised +Grid : Message : 15.529056 s : Setting up Cshift based reference +Grid : Message : 21.472100 s : ***************************************************************** +Grid : Message : 21.472690 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 21.472700 s : ***************************************************************** +Grid : Message : 21.472710 s : ***************************************************************** +Grid : Message : 21.472720 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 21.472730 s : * Vectorising space-time by 8 +Grid : Message : 21.472740 s : * VComplexF size is 64 B +Grid : Message : 21.472750 s : * SINGLE precision +Grid : Message : 21.472760 s : * Using Overlapped Comms/Compute +Grid : Message : 21.472770 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 21.472780 s : ***************************************************************** +Grid : Message : 22.302206 s : Called warmup +Grid : Message : 53.261410 s : Called Dw 3000 times in 3.07237e+07 us +Grid : Message : 53.261970 s : mflop/s = 3.45988e+07 +Grid : Message : 53.261990 s : mflop/s per rank = 2.16243e+06 +Grid : Message : 53.262000 s : mflop/s per node = 8.64971e+06 +Grid : Message : 53.262010 s : RF GiB/s (base 2) = 70304 +Grid : Message : 53.262020 s : mem GiB/s (base 2) = 43940 +Grid : Message : 53.297930 s : norm diff 1.03481e-13 +Grid : Message : 53.647310 s : #### Dhop calls report +Grid : Message : 53.647360 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 53.647410 s : WilsonFermion5D TotalTime /Calls : 5149.34 us +Grid : Message : 53.647430 s : WilsonFermion5D CommTime /Calls : 3752.8 us +Grid : Message : 53.647440 s : WilsonFermion5D FaceTime /Calls : 474.579 us +Grid : Message : 53.647450 s : WilsonFermion5D ComputeTime1/Calls : 46.5708 us +Grid : Message : 53.647460 s : WilsonFermion5D ComputeTime2/Calls : 926.982 us +Grid : Message : 53.647550 s : Average mflops/s per call : 3.82643e+09 +Grid : Message : 53.647580 s : Average mflops/s per call per rank : 2.39152e+08 +Grid : Message : 53.647590 s : Average mflops/s per call per node : 9.56608e+08 +Grid : Message : 53.647610 s : Average mflops/s per call (full) : 3.50314e+07 +Grid : Message : 53.647620 s : Average mflops/s per call per rank (full): 2.18946e+06 +Grid : Message : 53.647630 s : Average mflops/s per call per node (full): 8.75785e+06 +Grid : Message : 53.647640 s : WilsonFermion5D Stencil +Grid : Message : 53.647650 s : WilsonFermion5D StencilEven +Grid : Message : 53.647660 s : WilsonFermion5D StencilOdd +Grid : Message : 53.647670 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 53.647680 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 53.647690 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 80.460422 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 80.460440 s : Called DwDag +Grid : Message : 80.460441 s : norm dag result 12.0421 +Grid : Message : 80.481170 s : norm dag ref 12.0421 +Grid : Message : 80.497179 s : norm dag diff 7.63236e-14 +Grid : Message : 80.536902 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 80.917100 s : src_e0.499997 +Grid : Message : 80.993190 s : src_o0.500003 +Grid : Message : 81.634480 s : ********************************************************* +Grid : Message : 81.634500 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 81.634510 s : * Vectorising space-time by 8 +Grid : Message : 81.634520 s : * SINGLE precision +Grid : Message : 81.634530 s : * Using Overlapped Comms/Compute +Grid : Message : 81.634540 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 81.634550 s : ********************************************************* +Grid : Message : 96.356262 s : Deo mflop/s = 3.49003e+07 +Grid : Message : 96.356288 s : Deo mflop/s per rank 2.18127e+06 +Grid : Message : 96.356290 s : Deo mflop/s per node 8.72508e+06 +Grid : Message : 96.356297 s : #### Dhop calls report +Grid : Message : 96.356298 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 96.356300 s : WilsonFermion5D TotalTime /Calls : 5095.74 us +Grid : Message : 96.356302 s : WilsonFermion5D CommTime /Calls : 3589.98 us +Grid : Message : 96.356303 s : WilsonFermion5D FaceTime /Calls : 552.086 us +Grid : Message : 96.356304 s : WilsonFermion5D ComputeTime1/Calls : 52.4692 us +Grid : Message : 96.356305 s : WilsonFermion5D ComputeTime2/Calls : 963.892 us +Grid : Message : 96.356324 s : Average mflops/s per call : 3.42222e+09 +Grid : Message : 96.356326 s : Average mflops/s per call per rank : 2.13889e+08 +Grid : Message : 96.356327 s : Average mflops/s per call per node : 8.55556e+08 +Grid : Message : 96.356328 s : Average mflops/s per call (full) : 3.53999e+07 +Grid : Message : 96.356329 s : Average mflops/s per call per rank (full): 2.21249e+06 +Grid : Message : 96.356330 s : Average mflops/s per call per node (full): 8.84997e+06 +Grid : Message : 96.356331 s : WilsonFermion5D Stencil +Grid : Message : 96.356332 s : WilsonFermion5D StencilEven +Grid : Message : 96.356333 s : WilsonFermion5D StencilOdd +Grid : Message : 96.356334 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 96.356335 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 96.356336 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 96.415562 s : r_e6.02111 +Grid : Message : 96.422386 s : r_o6.02102 +Grid : Message : 96.428364 s : res12.0421 +Grid : Message : 97.865450 s : norm diff 0 +Grid : Message : 97.490738 s : norm diff even 0 +Grid : Message : 97.561591 s : norm diff odd 0 diff --git a/systems/Booster/dwf16.slurm b/systems/Booster/dwf16.slurm new file mode 100644 index 00000000..e7729447 --- /dev/null +++ b/systems/Booster/dwf16.slurm @@ -0,0 +1,29 @@ +#!/bin/sh +#SBATCH --account=gm2dwf +#SBATCH --nodes=16 +#SBATCH --ntasks=64 +#SBATCH --ntasks-per-node=4 +#SBATCH --cpus-per-task=12 +#SBATCH --time=0:30:00 +#SBATCH --partition=booster +#SBATCH --gres=gpu:4 + +export OMP_NUM_THREADS=4 +export OMPI_MCA_btl=^uct,openib +export UCX_TLS=gdr_copy,rc,rc_x,sm,cuda_copy,cuda_ipc +export UCX_RNDV_SCHEME=put_zcopy +export UCX_RNDV_THRESH=16384 +export UCX_IB_GPU_DIRECT_RDMA=yes +export UCX_MEMTYPE_CACHE=n +OPT="--comms-overlap --comms-concurrent" + + +srun -N 16 -n $SLURM_NTASKS \ + ./benchmarks/Benchmark_dwf_fp32 \ + $OPT \ + --mpi 2.2.2.8 \ + --accelerator-threads 8 \ + --grid 64.64.64.256 \ + --shm 2048 > dwf.16node.perf + + diff --git a/systems/Booster/dwf4.slurm b/systems/Booster/dwf4.slurm new file mode 100644 index 00000000..7462f81b --- /dev/null +++ b/systems/Booster/dwf4.slurm @@ -0,0 +1,40 @@ +#!/bin/sh +#SBATCH --account=gm2dwf +#SBATCH --nodes=4 +#SBATCH --ntasks=16 +#SBATCH --ntasks-per-node=4 +#SBATCH --cpus-per-task=12 +#SBATCH --time=2:00:00 +#SBATCH --partition=develbooster +#SBATCH --gres=gpu:4 + +export OMP_NUM_THREADS=4 +export OMPI_MCA_btl=^uct,openib +export UCX_TLS=gdr_copy,rc,rc_x,sm,cuda_copy,cuda_ipc +export UCX_RNDV_SCHEME=put_zcopy +export UCX_RNDV_THRESH=16384 +export UCX_IB_GPU_DIRECT_RDMA=yes +export UCX_MEMTYPE_CACHE=n + +OPT="--comms-overlap --comms-concurrent" + +srun -N 4 -n $SLURM_NTASKS \ + ./benchmarks/Benchmark_dwf_fp32 \ + $OPT \ + --mpi 2.2.2.2 \ + --shm-mpi 0 \ + --accelerator-threads 8 \ + --grid 64.64.64.64 \ + --shm 2048 > dwf.4node.perf + + +srun -N 4 -n $SLURM_NTASKS \ + ./benchmarks/Benchmark_comms_host_device \ + --mpi 2.2.2.2 \ + --accelerator-threads 8 \ + --grid 64.64.64.64 \ + --shm 2048 > comms.4node.perf + + + + diff --git a/systems/Booster/sourceme.sh b/systems/Booster/sourceme.sh new file mode 100644 index 00000000..56499be4 --- /dev/null +++ b/systems/Booster/sourceme.sh @@ -0,0 +1,5 @@ +module load GCC/9.3.0 +module load GMP/6.2.0 +module load MPFR/4.1.0 +module load OpenMPI/4.1.0rc1 +module load CUDA/11.3 From a29122e2bf77ad6aa513109aad3a4203df8f2130 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 04:05:04 +0200 Subject: [PATCH 226/399] Rebench --- systems/Booster/comms.4node.perf | 142 +++++++++++----------- systems/Booster/dwf.4node.perf | 196 +++++++++++++++---------------- systems/Booster/dwf4.slurm | 1 - 3 files changed, 169 insertions(+), 170 deletions(-) diff --git a/systems/Booster/comms.4node.perf b/systems/Booster/comms.4node.perf index 30ad9821..4b7c9b20 100644 --- a/systems/Booster/comms.4node.perf +++ b/systems/Booster/comms.4node.perf @@ -17,7 +17,7 @@ AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no AcceleratorCudaInit: ================================================ SharedMemoryMpi: World communicator of size 16 SharedMemoryMpi: Node communicator of size 4 -0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x14e740000000 for comms buffers +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x1463a0000000 for comms buffers Setting up IPC __|__|__|__|__|__|__|__|__|__|__|__|__|__|__ @@ -47,7 +47,7 @@ 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 General Public License for more details. -Current Grid git commit hash=f660dc67e4b193afc4015bc5e5fe47cfdbb0356e: (HEAD -> develop, origin/develop, origin/HEAD) uncommited changes +Current Grid git commit hash=e188c0512ebee79bfb15906676af1c9e142aa21a: (HEAD -> develop) uncommited changes Grid : Message : ================================================ Grid : Message : MPI is initialised and logging filters activated @@ -58,72 +58,72 @@ Grid : Message : MemoryManager::Init() setting up Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory Grid : Message : MemoryManager::Init() Using cudaMalloc -Grid : Message : 0.706584 s : Grid is setup to use 4 threads -Grid : Message : 0.706591 s : Number of iterations to average: 250 -Grid : Message : 0.706592 s : ==================================================================================================== -Grid : Message : 0.706593 s : = Benchmarking sequential halo exchange from host memory -Grid : Message : 0.706594 s : ==================================================================================================== -Grid : Message : 0.706595 s : L Ls bytes MB/s uni (err/min/max) MB/s bidi (err/min/max) -Grid : Message : 0.744123 s : 8 8 393216 45765.4 91530.7 -Grid : Message : 0.760079 s : 8 8 393216 49399.0 98798.0 -Grid : Message : 0.776168 s : 8 8 393216 48904.4 97808.8 -Grid : Message : 0.793391 s : 8 8 393216 45680.3 91360.6 -Grid : Message : 0.841483 s : 12 8 1327104 61988.1 123976.3 -Grid : Message : 0.881324 s : 12 8 1327104 66673.6 133347.1 -Grid : Message : 0.923429 s : 12 8 1327104 63049.8 126099.6 -Grid : Message : 0.965199 s : 12 8 1327104 63558.6 127117.2 -Grid : Message : 1.759350 s : 16 8 3145728 63505.2 127010.3 -Grid : Message : 1.162325 s : 16 8 3145728 72851.5 145703.0 -Grid : Message : 1.252374 s : 16 8 3145728 69873.2 139746.5 -Grid : Message : 1.343308 s : 16 8 3145728 69193.9 138387.8 -Grid : Message : 1.537929 s : 20 8 6144000 69283.6 138567.2 -Grid : Message : 1.689674 s : 20 8 6144000 80997.7 161995.4 -Grid : Message : 1.844366 s : 20 8 6144000 79440.4 158880.8 -Grid : Message : 2.585000 s : 20 8 6144000 78662.8 157325.7 -Grid : Message : 2.296310 s : 24 8 10616832 78318.9 156637.8 -Grid : Message : 2.552185 s : 24 8 10616832 82996.5 165993.0 -Grid : Message : 2.810117 s : 24 8 10616832 82325.6 164651.2 -Grid : Message : 3.708760 s : 24 8 10616832 81433.0 162866.1 -Grid : Message : 3.519278 s : 28 8 16859136 81498.1 162996.1 -Grid : Message : 3.919983 s : 28 8 16859136 84154.5 168309.0 -Grid : Message : 4.324104 s : 28 8 16859136 83438.8 166877.5 -Grid : Message : 4.726446 s : 28 8 16859136 83807.5 167615.0 -Grid : Message : 5.379742 s : 32 8 25165824 83183.5 166366.9 -Grid : Message : 5.973767 s : 32 8 25165824 84735.1 169470.3 -Grid : Message : 6.566572 s : 32 8 25165824 84905.7 169811.3 -Grid : Message : 7.161794 s : 32 8 25165824 84561.2 169122.3 -Grid : Message : 7.162522 s : ==================================================================================================== -Grid : Message : 7.162527 s : = Benchmarking sequential halo exchange from GPU memory -Grid : Message : 7.162528 s : ==================================================================================================== -Grid : Message : 7.162529 s : L Ls bytes MB/s uni (err/min/max) MB/s bidi (err/min/max) -Grid : Message : 7.222442 s : 8 8 393216 14172.2 28344.5 -Grid : Message : 7.253802 s : 8 8 393216 25092.0 50183.9 -Grid : Message : 7.278690 s : 8 8 393216 31614.1 63228.2 -Grid : Message : 7.304138 s : 8 8 393216 30915.6 61831.3 -Grid : Message : 7.347792 s : 12 8 1327104 109873.2 219746.5 -Grid : Message : 7.370904 s : 12 8 1327104 114900.8 229801.6 -Grid : Message : 7.418730 s : 12 8 1327104 55509.9 111019.9 -Grid : Message : 7.466445 s : 12 8 1327104 55639.1 111278.2 -Grid : Message : 7.531724 s : 16 8 3145728 168680.8 337361.6 -Grid : Message : 7.567205 s : 16 8 3145728 177379.0 354758.0 -Grid : Message : 7.669544 s : 16 8 3145728 61483.2 122966.5 -Grid : Message : 7.771543 s : 16 8 3145728 61687.6 123375.2 -Grid : Message : 7.852470 s : 20 8 6144000 232313.7 464627.4 -Grid : Message : 7.905813 s : 20 8 6144000 230410.1 460820.2 -Grid : Message : 8.979520 s : 20 8 6144000 63957.7 127915.4 -Grid : Message : 8.287586 s : 20 8 6144000 64802.3 129604.5 -Grid : Message : 8.393759 s : 24 8 10616832 272264.9 544529.7 -Grid : Message : 8.471189 s : 24 8 10616832 274276.5 548553.0 -Grid : Message : 8.794453 s : 24 8 10616832 65688.1 131376.1 -Grid : Message : 9.122233 s : 24 8 10616832 64782.8 129565.6 -Grid : Message : 9.263925 s : 28 8 16859136 300457.8 600915.5 -Grid : Message : 9.375294 s : 28 8 16859136 302794.4 605588.7 -Grid : Message : 9.757443 s : 28 8 16859136 88236.5 176473.1 -Grid : Message : 10.134856 s : 28 8 16859136 89343.4 178686.7 -Grid : Message : 10.318775 s : 32 8 25165824 327347.5 654695.1 -Grid : Message : 10.470617 s : 32 8 25165824 331500.0 663000.0 -Grid : Message : 11.235800 s : 32 8 25165824 91024.0 182048.1 -Grid : Message : 11.576311 s : 32 8 25165824 91062.1 182124.2 -Grid : Message : 11.592542 s : ==================================================================================================== -Grid : Message : 11.592548 s : = All done; Bye Bye -Grid : Message : 11.592549 s : ==================================================================================================== +Grid : Message : 0.729967 s : Grid is setup to use 4 threads +Grid : Message : 0.729975 s : Number of iterations to average: 250 +Grid : Message : 0.729977 s : ==================================================================================================== +Grid : Message : 0.729978 s : = Benchmarking sequential halo exchange from host memory +Grid : Message : 0.729979 s : ==================================================================================================== +Grid : Message : 0.729980 s : L Ls bytes MB/s uni (err/min/max) MB/s bidi (err/min/max) +Grid : Message : 0.749870 s : 8 8 393216 50783.4 101566.8 +Grid : Message : 0.764282 s : 8 8 393216 54704.5 109409.0 +Grid : Message : 0.780310 s : 8 8 393216 49090.6 98181.3 +Grid : Message : 0.796479 s : 8 8 393216 48662.3 97324.7 +Grid : Message : 0.841551 s : 12 8 1327104 66728.9 133457.8 +Grid : Message : 0.880653 s : 12 8 1327104 67932.9 135865.9 +Grid : Message : 0.920097 s : 12 8 1327104 67304.2 134608.4 +Grid : Message : 0.961444 s : 12 8 1327104 64205.9 128411.8 +Grid : Message : 1.660890 s : 16 8 3145728 67833.1 135666.3 +Grid : Message : 1.153006 s : 16 8 3145728 72416.3 144832.6 +Grid : Message : 1.240962 s : 16 8 3145728 71536.1 143072.2 +Grid : Message : 1.330372 s : 16 8 3145728 70372.7 140745.3 +Grid : Message : 1.519996 s : 20 8 6144000 71017.4 142034.8 +Grid : Message : 1.667745 s : 20 8 6144000 83189.5 166378.9 +Grid : Message : 1.817908 s : 20 8 6144000 81836.5 163673.1 +Grid : Message : 1.969344 s : 20 8 6144000 81148.0 162296.0 +Grid : Message : 2.260249 s : 24 8 10616832 79299.9 158599.8 +Grid : Message : 2.512319 s : 24 8 10616832 84249.2 168498.4 +Grid : Message : 2.763820 s : 24 8 10616832 84430.4 168860.9 +Grid : Message : 3.172850 s : 24 8 10616832 83776.5 167553.1 +Grid : Message : 3.460951 s : 28 8 16859136 82176.6 164353.1 +Grid : Message : 3.859348 s : 28 8 16859136 84642.9 169285.9 +Grid : Message : 4.254351 s : 28 8 16859136 85366.0 170731.9 +Grid : Message : 4.651748 s : 28 8 16859136 84850.2 169700.4 +Grid : Message : 5.302166 s : 32 8 25165824 83402.1 166804.1 +Grid : Message : 5.889123 s : 32 8 25165824 85756.3 171512.6 +Grid : Message : 6.472357 s : 32 8 25165824 86299.1 172598.3 +Grid : Message : 7.572140 s : 32 8 25165824 86059.7 172119.3 +Grid : Message : 7.578700 s : ==================================================================================================== +Grid : Message : 7.578740 s : = Benchmarking sequential halo exchange from GPU memory +Grid : Message : 7.578750 s : ==================================================================================================== +Grid : Message : 7.578760 s : L Ls bytes MB/s uni (err/min/max) MB/s bidi (err/min/max) +Grid : Message : 7.119231 s : 8 8 393216 13844.9 27689.8 +Grid : Message : 7.150661 s : 8 8 393216 25034.4 50068.9 +Grid : Message : 7.173800 s : 8 8 393216 34002.0 68004.0 +Grid : Message : 7.197415 s : 8 8 393216 33317.7 66635.5 +Grid : Message : 7.240696 s : 12 8 1327104 110772.0 221544.0 +Grid : Message : 7.263466 s : 12 8 1327104 116627.5 233254.9 +Grid : Message : 7.310752 s : 12 8 1327104 56142.8 112285.6 +Grid : Message : 7.356881 s : 12 8 1327104 57551.3 115102.6 +Grid : Message : 7.422351 s : 16 8 3145728 167086.0 334172.0 +Grid : Message : 7.458334 s : 16 8 3145728 174903.6 349807.1 +Grid : Message : 7.558746 s : 16 8 3145728 62663.3 125326.6 +Grid : Message : 7.658824 s : 16 8 3145728 62871.8 125743.6 +Grid : Message : 7.741423 s : 20 8 6144000 231840.3 463680.6 +Grid : Message : 7.794862 s : 20 8 6144000 229996.1 459992.1 +Grid : Message : 7.982472 s : 20 8 6144000 65501.1 131002.1 +Grid : Message : 8.170548 s : 20 8 6144000 65338.8 130677.5 +Grid : Message : 8.277182 s : 24 8 10616832 274319.0 548638.0 +Grid : Message : 8.354585 s : 24 8 10616832 274365.1 548730.2 +Grid : Message : 8.675675 s : 24 8 10616832 66132.8 132265.7 +Grid : Message : 8.999237 s : 24 8 10616832 65627.4 131254.7 +Grid : Message : 9.140302 s : 28 8 16859136 300825.0 601650.0 +Grid : Message : 9.251320 s : 28 8 16859136 303749.1 607498.1 +Grid : Message : 9.632241 s : 28 8 16859136 88520.3 177040.6 +Grid : Message : 9.999663 s : 28 8 16859136 91772.9 183545.7 +Grid : Message : 10.183071 s : 32 8 25165824 328325.5 656651.1 +Grid : Message : 10.335093 s : 32 8 25165824 331109.7 662219.3 +Grid : Message : 10.875980 s : 32 8 25165824 93056.0 186111.9 +Grid : Message : 11.418666 s : 32 8 25165824 92747.5 185495.0 +Grid : Message : 11.434792 s : ==================================================================================================== +Grid : Message : 11.434797 s : = All done; Bye Bye +Grid : Message : 11.434798 s : ==================================================================================================== diff --git a/systems/Booster/dwf.4node.perf b/systems/Booster/dwf.4node.perf index 7c0458e3..e897fe13 100644 --- a/systems/Booster/dwf.4node.perf +++ b/systems/Booster/dwf.4node.perf @@ -17,7 +17,7 @@ AcceleratorCudaInit: Configure options --enable-summit, --enable-select-gpu=no AcceleratorCudaInit: ================================================ SharedMemoryMpi: World communicator of size 16 SharedMemoryMpi: Node communicator of size 4 -0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x14b7a0000000 for comms buffers +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x14e9c0000000 for comms buffers Setting up IPC __|__|__|__|__|__|__|__|__|__|__|__|__|__|__ @@ -47,7 +47,7 @@ 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 General Public License for more details. -Current Grid git commit hash=f660dc67e4b193afc4015bc5e5fe47cfdbb0356e: (HEAD -> develop, origin/develop, origin/HEAD) uncommited changes +Current Grid git commit hash=e188c0512ebee79bfb15906676af1c9e142aa21a: (HEAD -> develop) uncommited changes Grid : Message : ================================================ Grid : Message : MPI is initialised and logging filters activated @@ -58,99 +58,99 @@ Grid : Message : MemoryManager::Init() setting up Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory Grid : Message : MemoryManager::Init() Using cudaMalloc -Grid : Message : 0.751288 s : Grid Layout -Grid : Message : 0.751291 s : Global lattice size : 64 64 64 64 -Grid : Message : 0.751296 s : OpenMP threads : 4 -Grid : Message : 0.751297 s : MPI tasks : 2 2 2 2 -Grid : Message : 0.792527 s : Making s innermost grids -Grid : Message : 0.835495 s : Initialising 4d RNG -Grid : Message : 0.940402 s : Intialising parallel RNG with unique string 'The 4D RNG' -Grid : Message : 0.940421 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 -Grid : Message : 1.336448 s : Initialising 5d RNG -Grid : Message : 2.956230 s : Intialising parallel RNG with unique string 'The 5D RNG' -Grid : Message : 2.956251 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a -Grid : Message : 11.242345 s : Initialised RNGs -Grid : Message : 13.415017 s : Drawing gauge field -Grid : Message : 13.937529 s : Random gauge initialised -Grid : Message : 15.529056 s : Setting up Cshift based reference -Grid : Message : 21.472100 s : ***************************************************************** -Grid : Message : 21.472690 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm -Grid : Message : 21.472700 s : ***************************************************************** -Grid : Message : 21.472710 s : ***************************************************************** -Grid : Message : 21.472720 s : * Benchmarking DomainWallFermionR::Dhop -Grid : Message : 21.472730 s : * Vectorising space-time by 8 -Grid : Message : 21.472740 s : * VComplexF size is 64 B -Grid : Message : 21.472750 s : * SINGLE precision -Grid : Message : 21.472760 s : * Using Overlapped Comms/Compute -Grid : Message : 21.472770 s : * Using GENERIC Nc WilsonKernels -Grid : Message : 21.472780 s : ***************************************************************** -Grid : Message : 22.302206 s : Called warmup -Grid : Message : 53.261410 s : Called Dw 3000 times in 3.07237e+07 us -Grid : Message : 53.261970 s : mflop/s = 3.45988e+07 -Grid : Message : 53.261990 s : mflop/s per rank = 2.16243e+06 -Grid : Message : 53.262000 s : mflop/s per node = 8.64971e+06 -Grid : Message : 53.262010 s : RF GiB/s (base 2) = 70304 -Grid : Message : 53.262020 s : mem GiB/s (base 2) = 43940 -Grid : Message : 53.297930 s : norm diff 1.03481e-13 -Grid : Message : 53.647310 s : #### Dhop calls report -Grid : Message : 53.647360 s : WilsonFermion5D Number of DhopEO Calls : 6002 -Grid : Message : 53.647410 s : WilsonFermion5D TotalTime /Calls : 5149.34 us -Grid : Message : 53.647430 s : WilsonFermion5D CommTime /Calls : 3752.8 us -Grid : Message : 53.647440 s : WilsonFermion5D FaceTime /Calls : 474.579 us -Grid : Message : 53.647450 s : WilsonFermion5D ComputeTime1/Calls : 46.5708 us -Grid : Message : 53.647460 s : WilsonFermion5D ComputeTime2/Calls : 926.982 us -Grid : Message : 53.647550 s : Average mflops/s per call : 3.82643e+09 -Grid : Message : 53.647580 s : Average mflops/s per call per rank : 2.39152e+08 -Grid : Message : 53.647590 s : Average mflops/s per call per node : 9.56608e+08 -Grid : Message : 53.647610 s : Average mflops/s per call (full) : 3.50314e+07 -Grid : Message : 53.647620 s : Average mflops/s per call per rank (full): 2.18946e+06 -Grid : Message : 53.647630 s : Average mflops/s per call per node (full): 8.75785e+06 -Grid : Message : 53.647640 s : WilsonFermion5D Stencil -Grid : Message : 53.647650 s : WilsonFermion5D StencilEven -Grid : Message : 53.647660 s : WilsonFermion5D StencilOdd -Grid : Message : 53.647670 s : WilsonFermion5D Stencil Reporti() -Grid : Message : 53.647680 s : WilsonFermion5D StencilEven Reporti() -Grid : Message : 53.647690 s : WilsonFermion5D StencilOdd Reporti() -Grid : Message : 80.460422 s : Compare to naive wilson implementation Dag to verify correctness -Grid : Message : 80.460440 s : Called DwDag -Grid : Message : 80.460441 s : norm dag result 12.0421 -Grid : Message : 80.481170 s : norm dag ref 12.0421 -Grid : Message : 80.497179 s : norm dag diff 7.63236e-14 -Grid : Message : 80.536902 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec -Grid : Message : 80.917100 s : src_e0.499997 -Grid : Message : 80.993190 s : src_o0.500003 -Grid : Message : 81.634480 s : ********************************************************* -Grid : Message : 81.634500 s : * Benchmarking DomainWallFermionF::DhopEO -Grid : Message : 81.634510 s : * Vectorising space-time by 8 -Grid : Message : 81.634520 s : * SINGLE precision -Grid : Message : 81.634530 s : * Using Overlapped Comms/Compute -Grid : Message : 81.634540 s : * Using GENERIC Nc WilsonKernels -Grid : Message : 81.634550 s : ********************************************************* -Grid : Message : 96.356262 s : Deo mflop/s = 3.49003e+07 -Grid : Message : 96.356288 s : Deo mflop/s per rank 2.18127e+06 -Grid : Message : 96.356290 s : Deo mflop/s per node 8.72508e+06 -Grid : Message : 96.356297 s : #### Dhop calls report -Grid : Message : 96.356298 s : WilsonFermion5D Number of DhopEO Calls : 3001 -Grid : Message : 96.356300 s : WilsonFermion5D TotalTime /Calls : 5095.74 us -Grid : Message : 96.356302 s : WilsonFermion5D CommTime /Calls : 3589.98 us -Grid : Message : 96.356303 s : WilsonFermion5D FaceTime /Calls : 552.086 us -Grid : Message : 96.356304 s : WilsonFermion5D ComputeTime1/Calls : 52.4692 us -Grid : Message : 96.356305 s : WilsonFermion5D ComputeTime2/Calls : 963.892 us -Grid : Message : 96.356324 s : Average mflops/s per call : 3.42222e+09 -Grid : Message : 96.356326 s : Average mflops/s per call per rank : 2.13889e+08 -Grid : Message : 96.356327 s : Average mflops/s per call per node : 8.55556e+08 -Grid : Message : 96.356328 s : Average mflops/s per call (full) : 3.53999e+07 -Grid : Message : 96.356329 s : Average mflops/s per call per rank (full): 2.21249e+06 -Grid : Message : 96.356330 s : Average mflops/s per call per node (full): 8.84997e+06 -Grid : Message : 96.356331 s : WilsonFermion5D Stencil -Grid : Message : 96.356332 s : WilsonFermion5D StencilEven -Grid : Message : 96.356333 s : WilsonFermion5D StencilOdd -Grid : Message : 96.356334 s : WilsonFermion5D Stencil Reporti() -Grid : Message : 96.356335 s : WilsonFermion5D StencilEven Reporti() -Grid : Message : 96.356336 s : WilsonFermion5D StencilOdd Reporti() -Grid : Message : 96.415562 s : r_e6.02111 -Grid : Message : 96.422386 s : r_o6.02102 -Grid : Message : 96.428364 s : res12.0421 -Grid : Message : 97.865450 s : norm diff 0 -Grid : Message : 97.490738 s : norm diff even 0 -Grid : Message : 97.561591 s : norm diff odd 0 +Grid : Message : 0.717713 s : Grid Layout +Grid : Message : 0.717716 s : Global lattice size : 64 64 64 64 +Grid : Message : 0.717724 s : OpenMP threads : 4 +Grid : Message : 0.717725 s : MPI tasks : 2 2 2 2 +Grid : Message : 0.801634 s : Making s innermost grids +Grid : Message : 0.844903 s : Initialising 4d RNG +Grid : Message : 0.940001 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 0.940060 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 1.338368 s : Initialising 5d RNG +Grid : Message : 2.859273 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 2.859304 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 11.140924 s : Initialised RNGs +Grid : Message : 13.433456 s : Drawing gauge field +Grid : Message : 13.955847 s : Random gauge initialised +Grid : Message : 15.528535 s : Setting up Cshift based reference +Grid : Message : 21.484340 s : ***************************************************************** +Grid : Message : 21.484840 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 21.484860 s : ***************************************************************** +Grid : Message : 21.484870 s : ***************************************************************** +Grid : Message : 21.484880 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 21.484890 s : * Vectorising space-time by 8 +Grid : Message : 21.484900 s : * VComplexF size is 64 B +Grid : Message : 21.484910 s : * SINGLE precision +Grid : Message : 21.484920 s : * Using Overlapped Comms/Compute +Grid : Message : 21.484930 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 21.484940 s : ***************************************************************** +Grid : Message : 22.344741 s : Called warmup +Grid : Message : 49.832292 s : Called Dw 3000 times in 2.74873e+07 us +Grid : Message : 49.832358 s : mflop/s = 3.86726e+07 +Grid : Message : 49.832360 s : mflop/s per rank = 2.41704e+06 +Grid : Message : 49.832361 s : mflop/s per node = 9.66814e+06 +Grid : Message : 49.832362 s : RF GiB/s (base 2) = 78581.7 +Grid : Message : 49.832363 s : mem GiB/s (base 2) = 49113.6 +Grid : Message : 49.835924 s : norm diff 1.03481e-13 +Grid : Message : 49.870568 s : #### Dhop calls report +Grid : Message : 49.870574 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 49.870598 s : WilsonFermion5D TotalTime /Calls : 4616.79 us +Grid : Message : 49.870600 s : WilsonFermion5D CommTime /Calls : 3241.77 us +Grid : Message : 49.870601 s : WilsonFermion5D FaceTime /Calls : 469.006 us +Grid : Message : 49.870602 s : WilsonFermion5D ComputeTime1/Calls : 27.0492 us +Grid : Message : 49.870603 s : WilsonFermion5D ComputeTime2/Calls : 926.33 us +Grid : Message : 49.870614 s : Average mflops/s per call : 6.71631e+09 +Grid : Message : 49.870619 s : Average mflops/s per call per rank : 4.19769e+08 +Grid : Message : 49.870621 s : Average mflops/s per call per node : 1.67908e+09 +Grid : Message : 49.870626 s : Average mflops/s per call (full) : 3.90723e+07 +Grid : Message : 49.870627 s : Average mflops/s per call per rank (full): 2.44202e+06 +Grid : Message : 49.870628 s : Average mflops/s per call per node (full): 9.76808e+06 +Grid : Message : 49.870629 s : WilsonFermion5D Stencil +Grid : Message : 49.870630 s : WilsonFermion5D StencilEven +Grid : Message : 49.870631 s : WilsonFermion5D StencilOdd +Grid : Message : 49.870632 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 49.870633 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 49.870634 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 77.321890 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 77.321911 s : Called DwDag +Grid : Message : 77.321912 s : norm dag result 12.0421 +Grid : Message : 77.334619 s : norm dag ref 12.0421 +Grid : Message : 77.350515 s : norm dag diff 7.63236e-14 +Grid : Message : 77.389923 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 77.769815 s : src_e0.499997 +Grid : Message : 77.847560 s : src_o0.500003 +Grid : Message : 77.917493 s : ********************************************************* +Grid : Message : 77.917496 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 77.917497 s : * Vectorising space-time by 8 +Grid : Message : 77.917498 s : * SINGLE precision +Grid : Message : 77.917499 s : * Using Overlapped Comms/Compute +Grid : Message : 77.917500 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 77.917501 s : ********************************************************* +Grid : Message : 91.412946 s : Deo mflop/s = 3.95925e+07 +Grid : Message : 91.412978 s : Deo mflop/s per rank 2.47453e+06 +Grid : Message : 91.412980 s : Deo mflop/s per node 9.89813e+06 +Grid : Message : 91.412983 s : #### Dhop calls report +Grid : Message : 91.412984 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 91.412986 s : WilsonFermion5D TotalTime /Calls : 4496.84 us +Grid : Message : 91.412988 s : WilsonFermion5D CommTime /Calls : 3057.28 us +Grid : Message : 91.412989 s : WilsonFermion5D FaceTime /Calls : 528.499 us +Grid : Message : 91.412990 s : WilsonFermion5D ComputeTime1/Calls : 16.1939 us +Grid : Message : 91.412991 s : WilsonFermion5D ComputeTime2/Calls : 942.557 us +Grid : Message : 91.413021 s : Average mflops/s per call : 1.12574e+10 +Grid : Message : 91.413023 s : Average mflops/s per call per rank : 7.03586e+08 +Grid : Message : 91.413024 s : Average mflops/s per call per node : 2.81434e+09 +Grid : Message : 91.413025 s : Average mflops/s per call (full) : 4.01145e+07 +Grid : Message : 91.413026 s : Average mflops/s per call per rank (full): 2.50716e+06 +Grid : Message : 91.413027 s : Average mflops/s per call per node (full): 1.00286e+07 +Grid : Message : 91.413028 s : WilsonFermion5D Stencil +Grid : Message : 91.413029 s : WilsonFermion5D StencilEven +Grid : Message : 91.413030 s : WilsonFermion5D StencilOdd +Grid : Message : 91.413031 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 91.413032 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 91.413033 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 91.470394 s : r_e6.02111 +Grid : Message : 91.476539 s : r_o6.02102 +Grid : Message : 91.482442 s : res12.0421 +Grid : Message : 92.138799 s : norm diff 0 +Grid : Message : 92.545354 s : norm diff even 0 +Grid : Message : 92.619444 s : norm diff odd 0 diff --git a/systems/Booster/dwf4.slurm b/systems/Booster/dwf4.slurm index 7462f81b..e8f4e738 100644 --- a/systems/Booster/dwf4.slurm +++ b/systems/Booster/dwf4.slurm @@ -22,7 +22,6 @@ srun -N 4 -n $SLURM_NTASKS \ ./benchmarks/Benchmark_dwf_fp32 \ $OPT \ --mpi 2.2.2.2 \ - --shm-mpi 0 \ --accelerator-threads 8 \ --grid 64.64.64.64 \ --shm 2048 > dwf.4node.perf From ca9816bfbb2bf310c3a87548038b9851195e9436 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 04:12:04 +0200 Subject: [PATCH 227/399] Typo --- Grid/communicator/Communicator_mpi3.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index 01335b41..df6e1f74 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -389,7 +389,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques void *shm = (void *) this->ShmBufferTranslate(dest,recv); assert(shm!=NULL); acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); - acceleratorCopySynchronise(); // MPI prob slower + acceleratorCopySynchronize(); // MPI prob slower } if ( CommunicatorPolicy == CommunicatorPolicySequential ) { From b3b033d34361d4ff17432f2cae27010ee80889f1 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 09:18:54 -0700 Subject: [PATCH 228/399] Clean --- Grid/threads/Accelerator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 2c9d15ba..e62090e0 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -267,7 +267,7 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { if(nt < 8)nt=8; \ cl::sycl::range<3> local {nt,1,nsimd}; \ cl::sycl::range<3> global{unum1,unum2,nsimd}; \ - cgh.parallel_for<class dslash>( \ + cgh.parallel_for( \ cl::sycl::nd_range<3>(global,local), \ [=] (cl::sycl::nd_item<3> item) /*mutable*/ \ [[intel::reqd_sub_group_size(8)]] \ From c6ce3ad03b44f5889357bd15906d28cb745d4761 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 09:20:21 -0700 Subject: [PATCH 229/399] Some properties --- Grid/communicator/SharedMemoryMPI.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 11788744..26e26bc8 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -516,6 +516,9 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) #ifdef GRID_SYCL_LEVEL_ZERO_IPC auto zeDevice = cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_device()); auto zeContext= cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_context()); + ze_device_properties_t device_properties; + assert(ZE_RESULT_SUCCESS == zeDeviceGetProperties(zeDevice, & device_properties)); + std::cout << " Device " << device_properties.name << std::endl; ze_device_mem_alloc_desc_t zeDesc = {}; zeMemAllocDevice(zeContext,&zeDesc,bytes,2*1024*1024,zeDevice,&ShmCommBuf); std::cout << WorldRank << header " SharedMemoryMPI.cc zeMemAllocDevice "<< bytes From 3206f69478195b5593c29800f7a9b18377918829 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Sep 2021 18:01:35 -0700 Subject: [PATCH 230/399] SYCL happy --- Grid/communicator/Communicator_mpi3.cc | 2 +- Grid/communicator/SharedMemoryMPI.cc | 68 +++++++++++++------------- Grid/threads/Accelerator.h | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index df6e1f74..01335b41 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -389,7 +389,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques void *shm = (void *) this->ShmBufferTranslate(dest,recv); assert(shm!=NULL); acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); - acceleratorCopySynchronize(); // MPI prob slower + acceleratorCopySynchronise(); // MPI prob slower } if ( CommunicatorPolicy == CommunicatorPolicySequential ) { diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index eb1f2d76..795f3928 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -513,29 +513,16 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Each MPI rank should allocate our own buffer /////////////////////////////////////////////////////////////////////////////////////////////////////////// -#ifdef GRID_SYCL_LEVEL_ZERO_IPC - auto zeDevice = cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_device()); - auto zeContext= cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_context()); - ze_device_properties_t device_properties; - assert(ZE_RESULT_SUCCESS == zeDeviceGetProperties(zeDevice, & device_properties)); - std::cout << " Device " << device_properties.name << std::endl; - ze_device_mem_alloc_desc_t zeDesc = {}; - zeMemAllocDevice(zeContext,&zeDesc,bytes,2*1024*1024,zeDevice,&ShmCommBuf); - std::cout << WorldRank << header " SharedMemoryMPI.cc zeMemAllocDevice "<< bytes - << "bytes at "<< std::hex<< ShmCommBuf <<std::dec<<" for comms buffers " <<std::endl; -#else ShmCommBuf = acceleratorAllocDevice(bytes); -#endif if (ShmCommBuf == (void *)NULL ) { std::cerr << " SharedMemoryMPI.cc acceleratorAllocDevice failed NULL pointer for " << bytes<<" bytes " << std::endl; exit(EXIT_FAILURE); } - // if ( WorldRank == 0 ){ - if ( 1 ){ + if ( WorldRank == 0 ){ std::cout << WorldRank << header " SharedMemoryMPI.cc acceleratorAllocDevice "<< bytes << "bytes at "<< std::hex<< ShmCommBuf <<std::dec<<" for comms buffers " <<std::endl; } - // SharedMemoryZero(ShmCommBuf,bytes); + SharedMemoryZero(ShmCommBuf,bytes); std::cout<< "Setting up IPC"<<std::endl; /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Loop over ranks/gpu's on our node @@ -549,20 +536,24 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) void * thisBuf = ShmCommBuf; if(!Stencil_force_mpi) { #ifdef GRID_SYCL_LEVEL_ZERO_IPC - ze_ipc_mem_handle_t handle; + typedef struct { int fd; pid_t pid ; } clone_mem_t; + + auto zeDevice = cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_device()); + auto zeContext = cl::sycl::get_native<cl::sycl::backend::level_zero>(theGridAccelerator->get_context()); + + ze_ipc_mem_handle_t ihandle; + clone_mem_t handle; + if ( r==WorldShmRank ) { - auto err = zeMemGetIpcHandle(zeContext,ShmCommBuf,&handle); + auto err = zeMemGetIpcHandle(zeContext,ShmCommBuf,&ihandle); if ( err != ZE_RESULT_SUCCESS ) { - std::cerr << "SharedMemoryMPI.cc zeMemGetIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + std::cout << "SharedMemoryMPI.cc zeMemGetIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; exit(EXIT_FAILURE); } else { - std::cerr << "SharedMemoryMPI.cc zeMemGetIpcHandle succeeded for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + std::cout << "SharedMemoryMPI.cc zeMemGetIpcHandle succeeded for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; } - std::cerr<<"Allocated IpcHandle rank "<<r<<" (hex) "; - for(int c=0;c<ZE_MAX_IPC_HANDLE_SIZE;c++){ - std::cerr<<std::hex<<(uint32_t)((uint8_t)handle.data[c])<<std::dec; - } - std::cerr<<std::endl; + memcpy((void *)&handle.fd,(void *)&ihandle,sizeof(int)); + handle.pid = getpid(); } #endif #ifdef GRID_CUDA @@ -605,18 +596,27 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) #ifdef GRID_SYCL_LEVEL_ZERO_IPC if ( r!=WorldShmRank ) { thisBuf = nullptr; - std::cerr<<"Using IpcHandle rank "<<r<<" "; - for(int c=0;c<ZE_MAX_IPC_HANDLE_SIZE;c++){ - std::cerr<<std::hex<<(uint32_t)((uint8_t)handle.data[c])<<std::dec; - } - std::cerr<<std::endl; - auto err = zeMemOpenIpcHandle(zeContext,zeDevice,handle,0,&thisBuf); + std::cout<<"mapping seeking remote pid/fd " + <<handle.pid<<"/" + <<handle.fd<<std::endl; + + int pidfd = syscall(SYS_pidfd_open,handle.pid,0); + std::cout<<"Using IpcHandle pidfd "<<pidfd<<"\n"; + // int myfd = syscall(SYS_pidfd_getfd,pidfd,handle.fd,0); + int myfd = syscall(438,pidfd,handle.fd,0); + + std::cout<<"Using IpcHandle myfd "<<myfd<<"\n"; + + memcpy((void *)&ihandle,(void *)&myfd,sizeof(int)); + + auto err = zeMemOpenIpcHandle(zeContext,zeDevice,ihandle,0,&thisBuf); if ( err != ZE_RESULT_SUCCESS ) { - std::cerr << "SharedMemoryMPI.cc "<<zeContext<<" "<<zeDevice<<std::endl; - std::cerr << "SharedMemoryMPI.cc zeMemOpenIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + std::cout << "SharedMemoryMPI.cc "<<zeContext<<" "<<zeDevice<<std::endl; + std::cout << "SharedMemoryMPI.cc zeMemOpenIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; exit(EXIT_FAILURE); } else { - std::cerr << "SharedMemoryMPI.cc zeMemOpenIpcHandle succeeded for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; + std::cout << "SharedMemoryMPI.cc zeMemOpenIpcHandle succeeded for rank "<<r<<std::endl; + std::cout << "SharedMemoryMPI.cc zeMemOpenIpcHandle pointer is "<<std::hex<<thisBuf<<std::dec<<std::endl; } assert(thisBuf!=nullptr); } @@ -643,7 +643,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) // Save a copy of the device buffers /////////////////////////////////////////////////////////////// } - WorldShmCommBufs[r] = thisBuf; + WorldShmCommBufs[r] = thisBuf; #else WorldShmCommBufs[r] = ShmCommBuf; #endif diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 70b09e05..974cc6ce 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -445,7 +445,7 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { return 0; } // CUDA spec inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { memcpy(to,from,bytes);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ memcpy(to,from,bytes);} inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { memcpy(to,from,bytes);} -inline void acceleratorCopySynchronize(void) {}; +inline void acceleratorCopySynchronise(void) {}; inline int acceleratorIsCommunicable(void *ptr){ return 1; } inline void acceleratorMemSet(void *base,int value,size_t bytes) { memset(base,value,bytes);} From c0d56a1c04517a83d3bfb76e0991b56725256e5c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 22 Sep 2021 06:02:34 -0700 Subject: [PATCH 231/399] Perlmutter tune up --- Grid/communicator/Communicator_mpi3.cc | 2 +- systems/Perlmutter/comms.4node | 129 +++++++++++++++ systems/Perlmutter/config-command | 12 ++ systems/Perlmutter/dwf.48.48.48.48.4node.opt0 | 156 ++++++++++++++++++ systems/Perlmutter/dwf.48.48.48.48.4node.opt1 | 156 ++++++++++++++++++ systems/Perlmutter/dwf.64.64.64.64.4node.opt0 | 156 ++++++++++++++++++ systems/Perlmutter/dwf.64.64.64.64.4node.opt1 | 156 ++++++++++++++++++ systems/Perlmutter/dwf4.slurm | 24 +++ systems/Perlmutter/sourceme.sh | 4 + 9 files changed, 794 insertions(+), 1 deletion(-) create mode 100644 systems/Perlmutter/comms.4node create mode 100644 systems/Perlmutter/config-command create mode 100644 systems/Perlmutter/dwf.48.48.48.48.4node.opt0 create mode 100644 systems/Perlmutter/dwf.48.48.48.48.4node.opt1 create mode 100644 systems/Perlmutter/dwf.64.64.64.64.4node.opt0 create mode 100644 systems/Perlmutter/dwf.64.64.64.64.4node.opt1 create mode 100644 systems/Perlmutter/dwf4.slurm create mode 100644 systems/Perlmutter/sourceme.sh diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index df6e1f74..01335b41 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -389,7 +389,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques void *shm = (void *) this->ShmBufferTranslate(dest,recv); assert(shm!=NULL); acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); - acceleratorCopySynchronize(); // MPI prob slower + acceleratorCopySynchronise(); // MPI prob slower } if ( CommunicatorPolicy == CommunicatorPolicySequential ) { diff --git a/systems/Perlmutter/comms.4node b/systems/Perlmutter/comms.4node new file mode 100644 index 00000000..25da2164 --- /dev/null +++ b/systems/Perlmutter/comms.4node @@ -0,0 +1,129 @@ +SLURM detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42506321920 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 2 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-setdevice=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 1073741824bytes at 0x7f8d40000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=b2ccaad761798e93a9314f97d8a4d1f851c6962a: (HEAD -> develop) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 1073741824 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34005057536 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.956704 s : Grid is setup to use 32 threads +Grid : Message : 0.956709 s : Number of iterations to average: 250 +Grid : Message : 0.956712 s : ==================================================================================================== +Grid : Message : 0.956713 s : = Benchmarking sequential halo exchange from host memory +Grid : Message : 0.956714 s : ==================================================================================================== +Grid : Message : 0.956715 s : L Ls bytes MB/s uni MB/s bidi +Grid : Message : 1.108420 s : 8 8 393216 15427.2 30854.4 +Grid : Message : 1.198740 s : 8 8 393216 87332.8 174665.6 +Grid : Message : 1.574400 s : 8 8 393216 20938.0 41876.0 +Grid : Message : 1.956280 s : 8 8 393216 20598.0 41196.0 +Grid : Message : 1.125254 s : 12 8 1327104 105614.9 211229.8 +Grid : Message : 1.149709 s : 12 8 1327104 108578.8 217157.5 +Grid : Message : 1.262612 s : 12 8 1327104 23510.2 47020.4 +Grid : Message : 1.377804 s : 12 8 1327104 23043.0 46086.0 +Grid : Message : 1.445986 s : 16 8 3145728 107931.9 215863.7 +Grid : Message : 1.501495 s : 16 8 3145728 113380.0 226760.0 +Grid : Message : 1.766377 s : 16 8 3145728 23752.8 47505.6 +Grid : Message : 2.301720 s : 16 8 3145728 23850.6 47701.2 +Grid : Message : 2.158035 s : 20 8 6144000 109657.5 219315.0 +Grid : Message : 2.268232 s : 20 8 6144000 111535.7 223071.4 +Grid : Message : 2.779996 s : 20 8 6144000 24011.8 48023.6 +Grid : Message : 3.289081 s : 20 8 6144000 24137.8 48275.7 +Grid : Message : 3.549101 s : 24 8 10616832 89696.1 179392.2 +Grid : Message : 3.779416 s : 24 8 10616832 92205.2 184410.4 +Grid : Message : 4.656539 s : 24 8 10616832 24209.0 48417.9 +Grid : Message : 5.531893 s : 24 8 10616832 24257.5 48515.0 +Grid : Message : 6.800400 s : 28 8 16859136 76106.8 152213.6 +Grid : Message : 6.443946 s : 28 8 16859136 77350.6 154701.1 +Grid : Message : 7.830994 s : 28 8 16859136 24309.8 48619.6 +Grid : Message : 9.215301 s : 28 8 16859136 24357.8 48715.5 +Grid : Message : 9.955615 s : 32 8 25165824 72403.7 144807.4 +Grid : Message : 10.648284 s : 32 8 25165824 72666.2 145332.4 +Grid : Message : 12.713098 s : 32 8 25165824 24376.2 48752.3 +Grid : Message : 14.775577 s : 32 8 25165824 24403.6 48807.3 +Grid : Message : 14.777794 s : ==================================================================================================== +Grid : Message : 14.777799 s : = Benchmarking sequential halo exchange from GPU memory +Grid : Message : 14.777800 s : ==================================================================================================== +Grid : Message : 14.777801 s : L Ls bytes MB/s uni MB/s bidi +Grid : Message : 14.798392 s : 8 8 393216 49210.4 98420.9 +Grid : Message : 14.812519 s : 8 8 393216 55716.0 111432.1 +Grid : Message : 14.861908 s : 8 8 393216 15926.4 31852.9 +Grid : Message : 14.909307 s : 8 8 393216 16594.5 33189.1 +Grid : Message : 14.938366 s : 12 8 1327104 157435.7 314871.3 +Grid : Message : 14.954490 s : 12 8 1327104 164724.6 329449.3 +Grid : Message : 15.921650 s : 12 8 1327104 19280.2 38560.4 +Grid : Message : 15.229618 s : 12 8 1327104 19311.3 38622.7 +Grid : Message : 15.275707 s : 16 8 3145728 221257.5 442514.9 +Grid : Message : 15.303489 s : 16 8 3145728 226547.7 453095.4 +Grid : Message : 15.619610 s : 16 8 3145728 19902.6 39805.2 +Grid : Message : 15.935287 s : 16 8 3145728 19930.6 39861.2 +Grid : Message : 15.999038 s : 20 8 6144000 269586.0 539172.0 +Grid : Message : 16.435890 s : 20 8 6144000 275886.8 551773.7 +Grid : Message : 16.652349 s : 20 8 6144000 20185.6 40371.2 +Grid : Message : 17.262005 s : 20 8 6144000 20156.0 40311.9 +Grid : Message : 17.351417 s : 24 8 10616832 300428.2 600856.4 +Grid : Message : 17.421125 s : 24 8 10616832 304656.8 609313.6 +Grid : Message : 18.477072 s : 24 8 10616832 20108.9 40217.7 +Grid : Message : 19.556481 s : 24 8 10616832 19671.8 39343.6 +Grid : Message : 19.681365 s : 28 8 16859136 318966.5 637933.1 +Grid : Message : 19.786400 s : 28 8 16859136 321056.1 642112.1 +Grid : Message : 21.531557 s : 28 8 16859136 19321.2 38642.4 +Grid : Message : 23.384312 s : 28 8 16859136 18199.2 36398.3 +Grid : Message : 23.556358 s : 32 8 25165824 332397.6 664795.2 +Grid : Message : 23.706392 s : 32 8 25165824 335492.9 670985.8 +Grid : Message : 26.356425 s : 32 8 25165824 18992.9 37985.9 +Grid : Message : 29.126692 s : 32 8 25165824 18168.6 36337.3 +Grid : Message : 29.137480 s : ==================================================================================================== +Grid : Message : 29.137485 s : = All done; Bye Bye +Grid : Message : 29.137486 s : ==================================================================================================== diff --git a/systems/Perlmutter/config-command b/systems/Perlmutter/config-command new file mode 100644 index 00000000..b399c535 --- /dev/null +++ b/systems/Perlmutter/config-command @@ -0,0 +1,12 @@ +../../configure \ + --enable-comms=mpi \ + --enable-simd=GPU \ + --enable-shm=nvlink \ + --enable-gen-simd-width=64 \ + --enable-accelerator=cuda \ + --disable-fermion-reps \ + --disable-unified \ + --disable-gparity \ + CXX=nvcc \ + LDFLAGS="-cudart shared " \ + CXXFLAGS="-ccbin CC -gencode arch=compute_80,code=sm_80 -std=c++14 -cudart shared" diff --git a/systems/Perlmutter/dwf.48.48.48.48.4node.opt0 b/systems/Perlmutter/dwf.48.48.48.48.4node.opt0 new file mode 100644 index 00000000..30e3194a --- /dev/null +++ b/systems/Perlmutter/dwf.48.48.48.48.4node.opt0 @@ -0,0 +1,156 @@ +SLURM detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42506321920 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 2 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-setdevice=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7fc320000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=b2ccaad761798e93a9314f97d8a4d1f851c6962a: (HEAD -> develop) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34005057536 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.762377 s : Grid Layout +Grid : Message : 0.762378 s : Global lattice size : 48 48 48 48 +Grid : Message : 0.762381 s : OpenMP threads : 32 +Grid : Message : 0.762382 s : MPI tasks : 2 2 2 2 +Grid : Message : 0.790912 s : Making s innermost grids +Grid : Message : 0.817408 s : Initialising 4d RNG +Grid : Message : 0.840908 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 0.840921 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 0.911684 s : Initialising 5d RNG +Grid : Message : 1.270530 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 1.270544 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 1.568435 s : Initialised RNGs +Grid : Message : 2.241446 s : Drawing gauge field +Grid : Message : 2.318921 s : Random gauge initialised +Grid : Message : 2.779258 s : Setting up Cshift based reference +Grid : Message : 3.188306 s : ***************************************************************** +Grid : Message : 3.188315 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 3.188316 s : ***************************************************************** +Grid : Message : 3.188316 s : ***************************************************************** +Grid : Message : 3.188316 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 3.188316 s : * Vectorising space-time by 8 +Grid : Message : 3.188317 s : * VComplexF size is 64 B +Grid : Message : 3.188318 s : * SINGLE precision +Grid : Message : 3.188318 s : * Using Overlapped Comms/Compute +Grid : Message : 3.188318 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 3.188318 s : ***************************************************************** +Grid : Message : 3.548355 s : Called warmup +Grid : Message : 37.809000 s : Called Dw 3000 times in 3.42606e+07 us +Grid : Message : 37.809040 s : mflop/s = 9.81714e+06 +Grid : Message : 37.809042 s : mflop/s per rank = 613572 +Grid : Message : 37.809043 s : mflop/s per node = 2.45429e+06 +Grid : Message : 37.809044 s : RF GiB/s (base 2) = 19948.2 +Grid : Message : 37.809045 s : mem GiB/s (base 2) = 12467.6 +Grid : Message : 37.810181 s : norm diff 1.03662e-13 +Grid : Message : 37.824163 s : #### Dhop calls report +Grid : Message : 37.824168 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 37.824172 s : WilsonFermion5D TotalTime /Calls : 5719.36 us +Grid : Message : 37.824173 s : WilsonFermion5D CommTime /Calls : 5085.34 us +Grid : Message : 37.824174 s : WilsonFermion5D FaceTime /Calls : 265.445 us +Grid : Message : 37.824175 s : WilsonFermion5D ComputeTime1/Calls : 23.4602 us +Grid : Message : 37.824176 s : WilsonFermion5D ComputeTime2/Calls : 370.89 us +Grid : Message : 37.824191 s : Average mflops/s per call : 2.36923e+09 +Grid : Message : 37.824194 s : Average mflops/s per call per rank : 1.48077e+08 +Grid : Message : 37.824195 s : Average mflops/s per call per node : 5.92307e+08 +Grid : Message : 37.824196 s : Average mflops/s per call (full) : 9.97945e+06 +Grid : Message : 37.824197 s : Average mflops/s per call per rank (full): 623716 +Grid : Message : 37.824198 s : Average mflops/s per call per node (full): 2.49486e+06 +Grid : Message : 37.824199 s : WilsonFermion5D Stencil +Grid : Message : 37.824199 s : WilsonFermion5D StencilEven +Grid : Message : 37.824199 s : WilsonFermion5D StencilOdd +Grid : Message : 37.824199 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 37.824199 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 37.824199 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 41.538537 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 41.538549 s : Called DwDag +Grid : Message : 41.538550 s : norm dag result 12.0422 +Grid : Message : 41.543416 s : norm dag ref 12.0422 +Grid : Message : 41.548999 s : norm dag diff 7.6086e-14 +Grid : Message : 41.563564 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 41.711516 s : src_e0.499992 +Grid : Message : 41.735103 s : src_o0.500008 +Grid : Message : 41.756142 s : ********************************************************* +Grid : Message : 41.756144 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 41.756145 s : * Vectorising space-time by 8 +Grid : Message : 41.756146 s : * SINGLE precision +Grid : Message : 41.756147 s : * Using Overlapped Comms/Compute +Grid : Message : 41.756148 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 41.756148 s : ********************************************************* +Grid : Message : 59.255023 s : Deo mflop/s = 9.6274e+06 +Grid : Message : 59.255044 s : Deo mflop/s per rank 601712 +Grid : Message : 59.255046 s : Deo mflop/s per node 2.40685e+06 +Grid : Message : 59.255048 s : #### Dhop calls report +Grid : Message : 59.255049 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 59.255050 s : WilsonFermion5D TotalTime /Calls : 5830.89 us +Grid : Message : 59.255051 s : WilsonFermion5D CommTime /Calls : 5143.28 us +Grid : Message : 59.255052 s : WilsonFermion5D FaceTime /Calls : 316.834 us +Grid : Message : 59.255053 s : WilsonFermion5D ComputeTime1/Calls : 37.4065 us +Grid : Message : 59.255054 s : WilsonFermion5D ComputeTime2/Calls : 375.889 us +Grid : Message : 59.255076 s : Average mflops/s per call : 1.4225e+09 +Grid : Message : 59.255077 s : Average mflops/s per call per rank : 8.8906e+07 +Grid : Message : 59.255078 s : Average mflops/s per call per node : 3.55624e+08 +Grid : Message : 59.255079 s : Average mflops/s per call (full) : 9.78858e+06 +Grid : Message : 59.255080 s : Average mflops/s per call per rank (full): 611786 +Grid : Message : 59.255081 s : Average mflops/s per call per node (full): 2.44714e+06 +Grid : Message : 59.255082 s : WilsonFermion5D Stencil +Grid : Message : 59.255082 s : WilsonFermion5D StencilEven +Grid : Message : 59.255082 s : WilsonFermion5D StencilOdd +Grid : Message : 59.255082 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 59.255082 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 59.255082 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 59.286796 s : r_e6.02129 +Grid : Message : 59.290118 s : r_o6.02097 +Grid : Message : 59.292558 s : res12.0423 +Grid : Message : 59.482803 s : norm diff 0 +Grid : Message : 59.604297 s : norm diff even 0 +Grid : Message : 59.626743 s : norm diff odd 0 diff --git a/systems/Perlmutter/dwf.48.48.48.48.4node.opt1 b/systems/Perlmutter/dwf.48.48.48.48.4node.opt1 new file mode 100644 index 00000000..f54e9d14 --- /dev/null +++ b/systems/Perlmutter/dwf.48.48.48.48.4node.opt1 @@ -0,0 +1,156 @@ +SLURM detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42506321920 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 2 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-setdevice=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7fbae0000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=b2ccaad761798e93a9314f97d8a4d1f851c6962a: (HEAD -> develop) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34005057536 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.692368 s : Grid Layout +Grid : Message : 0.692369 s : Global lattice size : 48 48 48 48 +Grid : Message : 0.692372 s : OpenMP threads : 32 +Grid : Message : 0.692372 s : MPI tasks : 2 2 2 2 +Grid : Message : 0.701977 s : Making s innermost grids +Grid : Message : 0.711295 s : Initialising 4d RNG +Grid : Message : 0.734938 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 0.734948 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 0.798281 s : Initialising 5d RNG +Grid : Message : 1.161711 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 1.161728 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 1.522440 s : Initialised RNGs +Grid : Message : 2.260710 s : Drawing gauge field +Grid : Message : 2.102597 s : Random gauge initialised +Grid : Message : 2.562592 s : Setting up Cshift based reference +Grid : Message : 3.121880 s : ***************************************************************** +Grid : Message : 3.121970 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 3.121980 s : ***************************************************************** +Grid : Message : 3.121980 s : ***************************************************************** +Grid : Message : 3.121980 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 3.121980 s : * Vectorising space-time by 8 +Grid : Message : 3.121980 s : * VComplexF size is 64 B +Grid : Message : 3.121990 s : * SINGLE precision +Grid : Message : 3.121990 s : * Using Overlapped Comms/Compute +Grid : Message : 3.121990 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 3.121990 s : ***************************************************************** +Grid : Message : 3.350688 s : Called warmup +Grid : Message : 35.847527 s : Called Dw 3000 times in 3.24968e+07 us +Grid : Message : 35.847576 s : mflop/s = 1.035e+07 +Grid : Message : 35.847578 s : mflop/s per rank = 646874 +Grid : Message : 35.847579 s : mflop/s per node = 2.5875e+06 +Grid : Message : 35.847580 s : RF GiB/s (base 2) = 21030.9 +Grid : Message : 35.847581 s : mem GiB/s (base 2) = 13144.3 +Grid : Message : 35.848697 s : norm diff 1.03662e-13 +Grid : Message : 35.861967 s : #### Dhop calls report +Grid : Message : 35.861973 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 35.861976 s : WilsonFermion5D TotalTime /Calls : 5426 us +Grid : Message : 35.861977 s : WilsonFermion5D CommTime /Calls : 4817.47 us +Grid : Message : 35.861978 s : WilsonFermion5D FaceTime /Calls : 246.175 us +Grid : Message : 35.861979 s : WilsonFermion5D ComputeTime1/Calls : 8.72676 us +Grid : Message : 35.861980 s : WilsonFermion5D ComputeTime2/Calls : 370.494 us +Grid : Message : 35.861995 s : Average mflops/s per call : 6.50606e+09 +Grid : Message : 35.861999 s : Average mflops/s per call per rank : 4.06629e+08 +Grid : Message : 35.862000 s : Average mflops/s per call per node : 1.62652e+09 +Grid : Message : 35.862001 s : Average mflops/s per call (full) : 1.0519e+07 +Grid : Message : 35.862002 s : Average mflops/s per call per rank (full): 657438 +Grid : Message : 35.862003 s : Average mflops/s per call per node (full): 2.62975e+06 +Grid : Message : 35.862004 s : WilsonFermion5D Stencil +Grid : Message : 35.862004 s : WilsonFermion5D StencilEven +Grid : Message : 35.862004 s : WilsonFermion5D StencilOdd +Grid : Message : 35.862004 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 35.862004 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 35.862004 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 39.599406 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 39.599421 s : Called DwDag +Grid : Message : 39.599422 s : norm dag result 12.0422 +Grid : Message : 39.604317 s : norm dag ref 12.0422 +Grid : Message : 39.609961 s : norm dag diff 7.6086e-14 +Grid : Message : 39.624145 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 39.772334 s : src_e0.499992 +Grid : Message : 39.795705 s : src_o0.500008 +Grid : Message : 39.816822 s : ********************************************************* +Grid : Message : 39.816824 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 39.816825 s : * Vectorising space-time by 8 +Grid : Message : 39.816826 s : * SINGLE precision +Grid : Message : 39.816827 s : * Using Overlapped Comms/Compute +Grid : Message : 39.816828 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 39.816828 s : ********************************************************* +Grid : Message : 56.382758 s : Deo mflop/s = 1.017e+07 +Grid : Message : 56.382779 s : Deo mflop/s per rank 635627 +Grid : Message : 56.382781 s : Deo mflop/s per node 2.54251e+06 +Grid : Message : 56.382783 s : #### Dhop calls report +Grid : Message : 56.382784 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 56.382785 s : WilsonFermion5D TotalTime /Calls : 5519.98 us +Grid : Message : 56.382786 s : WilsonFermion5D CommTime /Calls : 4856.39 us +Grid : Message : 56.382787 s : WilsonFermion5D FaceTime /Calls : 303.043 us +Grid : Message : 56.382788 s : WilsonFermion5D ComputeTime1/Calls : 6.77807 us +Grid : Message : 56.382789 s : WilsonFermion5D ComputeTime2/Calls : 376.551 us +Grid : Message : 56.382810 s : Average mflops/s per call : 8.31124e+09 +Grid : Message : 56.382811 s : Average mflops/s per call per rank : 5.19453e+08 +Grid : Message : 56.382812 s : Average mflops/s per call per node : 2.07781e+09 +Grid : Message : 56.382813 s : Average mflops/s per call (full) : 1.03399e+07 +Grid : Message : 56.382814 s : Average mflops/s per call per rank (full): 646244 +Grid : Message : 56.382815 s : Average mflops/s per call per node (full): 2.58498e+06 +Grid : Message : 56.382816 s : WilsonFermion5D Stencil +Grid : Message : 56.382816 s : WilsonFermion5D StencilEven +Grid : Message : 56.382816 s : WilsonFermion5D StencilOdd +Grid : Message : 56.382816 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 56.382816 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 56.382816 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 56.414571 s : r_e6.02129 +Grid : Message : 56.417837 s : r_o6.02097 +Grid : Message : 56.420535 s : res12.0423 +Grid : Message : 56.611957 s : norm diff 0 +Grid : Message : 56.730597 s : norm diff even 0 +Grid : Message : 56.752566 s : norm diff odd 0 diff --git a/systems/Perlmutter/dwf.64.64.64.64.4node.opt0 b/systems/Perlmutter/dwf.64.64.64.64.4node.opt0 new file mode 100644 index 00000000..b16a3219 --- /dev/null +++ b/systems/Perlmutter/dwf.64.64.64.64.4node.opt0 @@ -0,0 +1,156 @@ +SLURM detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42506321920 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 2 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-setdevice=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7fd460000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=b2ccaad761798e93a9314f97d8a4d1f851c6962a: (HEAD -> develop) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34005057536 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.667601 s : Grid Layout +Grid : Message : 0.667602 s : Global lattice size : 64 64 64 64 +Grid : Message : 0.667610 s : OpenMP threads : 32 +Grid : Message : 0.667611 s : MPI tasks : 2 2 2 2 +Grid : Message : 0.702872 s : Making s innermost grids +Grid : Message : 0.742911 s : Initialising 4d RNG +Grid : Message : 0.813463 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 0.813479 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 0.922630 s : Initialising 5d RNG +Grid : Message : 2.306290 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 2.306540 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 3.878430 s : Initialised RNGs +Grid : Message : 4.536926 s : Drawing gauge field +Grid : Message : 4.824391 s : Random gauge initialised +Grid : Message : 6.253195 s : Setting up Cshift based reference +Grid : Message : 7.326402 s : ***************************************************************** +Grid : Message : 7.326411 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 7.326412 s : ***************************************************************** +Grid : Message : 7.326412 s : ***************************************************************** +Grid : Message : 7.326412 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 7.326412 s : * Vectorising space-time by 8 +Grid : Message : 7.326413 s : * VComplexF size is 64 B +Grid : Message : 7.326414 s : * SINGLE precision +Grid : Message : 7.326414 s : * Using Overlapped Comms/Compute +Grid : Message : 7.326414 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 7.326414 s : ***************************************************************** +Grid : Message : 8.283417 s : Called warmup +Grid : Message : 89.658859 s : Called Dw 3000 times in 8.13753e+07 us +Grid : Message : 89.658898 s : mflop/s = 1.3063e+07 +Grid : Message : 89.658900 s : mflop/s per rank = 816437 +Grid : Message : 89.658901 s : mflop/s per node = 3.26575e+06 +Grid : Message : 89.658902 s : RF GiB/s (base 2) = 26543.7 +Grid : Message : 89.658903 s : mem GiB/s (base 2) = 16589.8 +Grid : Message : 89.662424 s : norm diff 1.03481e-13 +Grid : Message : 89.700433 s : #### Dhop calls report +Grid : Message : 89.700452 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 89.700456 s : WilsonFermion5D TotalTime /Calls : 13588.2 us +Grid : Message : 89.700457 s : WilsonFermion5D CommTime /Calls : 12137.3 us +Grid : Message : 89.700458 s : WilsonFermion5D FaceTime /Calls : 548.408 us +Grid : Message : 89.700459 s : WilsonFermion5D ComputeTime1/Calls : 42.6163 us +Grid : Message : 89.700460 s : WilsonFermion5D ComputeTime2/Calls : 910.312 us +Grid : Message : 89.700477 s : Average mflops/s per call : 4.43502e+09 +Grid : Message : 89.700493 s : Average mflops/s per call per rank : 2.77189e+08 +Grid : Message : 89.700494 s : Average mflops/s per call per node : 1.10875e+09 +Grid : Message : 89.700495 s : Average mflops/s per call (full) : 1.32753e+07 +Grid : Message : 89.700496 s : Average mflops/s per call per rank (full): 829709 +Grid : Message : 89.700497 s : Average mflops/s per call per node (full): 3.31884e+06 +Grid : Message : 89.700498 s : WilsonFermion5D Stencil +Grid : Message : 89.700498 s : WilsonFermion5D StencilEven +Grid : Message : 89.700498 s : WilsonFermion5D StencilOdd +Grid : Message : 89.700499 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 89.700499 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 89.700499 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 101.462401 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 101.462412 s : Called DwDag +Grid : Message : 101.462413 s : norm dag result 12.0421 +Grid : Message : 101.474097 s : norm dag ref 12.0421 +Grid : Message : 101.489396 s : norm dag diff 7.63236e-14 +Grid : Message : 101.529094 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 101.996820 s : src_e0.499997 +Grid : Message : 102.626690 s : src_o0.500003 +Grid : Message : 102.125734 s : ********************************************************* +Grid : Message : 102.125736 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 102.125737 s : * Vectorising space-time by 8 +Grid : Message : 102.125738 s : * SINGLE precision +Grid : Message : 102.125739 s : * Using Overlapped Comms/Compute +Grid : Message : 102.125739 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 102.125739 s : ********************************************************* +Grid : Message : 143.296910 s : Deo mflop/s = 1.30119e+07 +Grid : Message : 143.297140 s : Deo mflop/s per rank 813244 +Grid : Message : 143.297160 s : Deo mflop/s per node 3.25297e+06 +Grid : Message : 143.297180 s : #### Dhop calls report +Grid : Message : 143.297190 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 143.297200 s : WilsonFermion5D TotalTime /Calls : 13630 us +Grid : Message : 143.297210 s : WilsonFermion5D CommTime /Calls : 12124.9 us +Grid : Message : 143.297220 s : WilsonFermion5D FaceTime /Calls : 590.958 us +Grid : Message : 143.297230 s : WilsonFermion5D ComputeTime1/Calls : 43.2806 us +Grid : Message : 143.297240 s : WilsonFermion5D ComputeTime2/Calls : 921.187 us +Grid : Message : 143.297460 s : Average mflops/s per call : 4.24329e+09 +Grid : Message : 143.297470 s : Average mflops/s per call per rank : 2.65206e+08 +Grid : Message : 143.297480 s : Average mflops/s per call per node : 1.06082e+09 +Grid : Message : 143.297490 s : Average mflops/s per call (full) : 1.32347e+07 +Grid : Message : 143.297500 s : Average mflops/s per call per rank (full): 827169 +Grid : Message : 143.297510 s : Average mflops/s per call per node (full): 3.30868e+06 +Grid : Message : 143.297520 s : WilsonFermion5D Stencil +Grid : Message : 143.297520 s : WilsonFermion5D StencilEven +Grid : Message : 143.297520 s : WilsonFermion5D StencilOdd +Grid : Message : 143.297520 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 143.297520 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 143.297520 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 143.112368 s : r_e6.02111 +Grid : Message : 143.119760 s : r_o6.02102 +Grid : Message : 143.126239 s : res12.0421 +Grid : Message : 143.720780 s : norm diff 0 +Grid : Message : 144.885380 s : norm diff even 0 +Grid : Message : 144.154396 s : norm diff odd 0 diff --git a/systems/Perlmutter/dwf.64.64.64.64.4node.opt1 b/systems/Perlmutter/dwf.64.64.64.64.4node.opt1 new file mode 100644 index 00000000..d48f8126 --- /dev/null +++ b/systems/Perlmutter/dwf.64.64.64.64.4node.opt1 @@ -0,0 +1,156 @@ +SLURM detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: A100-SXM4-40GB +AcceleratorCudaInit[0]: totalGlobalMem: 42506321920 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 2 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: using default device +AcceleratorCudaInit: assume user either uses a) IBM jsrun, or +AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding +AcceleratorCudaInit: Configure options --enable-setdevice=no +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 16 +SharedMemoryMpi: Node communicator of size 4 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x7f4b80000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=b2ccaad761798e93a9314f97d8a4d1f851c6962a: (HEAD -> develop) uncommited changes + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 34005057536 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 32 LARGE 8 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 0.648397 s : Grid Layout +Grid : Message : 0.648398 s : Global lattice size : 64 64 64 64 +Grid : Message : 0.648401 s : OpenMP threads : 32 +Grid : Message : 0.648402 s : MPI tasks : 2 2 2 2 +Grid : Message : 0.663662 s : Making s innermost grids +Grid : Message : 0.682145 s : Initialising 4d RNG +Grid : Message : 0.754321 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 0.754332 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 0.863265 s : Initialising 5d RNG +Grid : Message : 1.967677 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 1.967691 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 2.921676 s : Initialised RNGs +Grid : Message : 4.382384 s : Drawing gauge field +Grid : Message : 4.672590 s : Random gauge initialised +Grid : Message : 6.102697 s : Setting up Cshift based reference +Grid : Message : 7.185897 s : ***************************************************************** +Grid : Message : 7.185906 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 7.185907 s : ***************************************************************** +Grid : Message : 7.185907 s : ***************************************************************** +Grid : Message : 7.185907 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 7.185907 s : * Vectorising space-time by 8 +Grid : Message : 7.185908 s : * VComplexF size is 64 B +Grid : Message : 7.185909 s : * SINGLE precision +Grid : Message : 7.185909 s : * Using Overlapped Comms/Compute +Grid : Message : 7.185909 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 7.185909 s : ***************************************************************** +Grid : Message : 8.114241 s : Called warmup +Grid : Message : 83.988100 s : Called Dw 3000 times in 7.48954e+07 us +Grid : Message : 83.992400 s : mflop/s = 1.41932e+07 +Grid : Message : 83.992600 s : mflop/s per rank = 887074 +Grid : Message : 83.992700 s : mflop/s per node = 3.5483e+06 +Grid : Message : 83.992800 s : RF GiB/s (base 2) = 28840.2 +Grid : Message : 83.992900 s : mem GiB/s (base 2) = 18025.1 +Grid : Message : 83.134870 s : norm diff 1.03481e-13 +Grid : Message : 83.493960 s : #### Dhop calls report +Grid : Message : 83.494000 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 83.494030 s : WilsonFermion5D TotalTime /Calls : 12506 us +Grid : Message : 83.494040 s : WilsonFermion5D CommTime /Calls : 11071.5 us +Grid : Message : 83.494050 s : WilsonFermion5D FaceTime /Calls : 530.971 us +Grid : Message : 83.494060 s : WilsonFermion5D ComputeTime1/Calls : 23.6428 us +Grid : Message : 83.494070 s : WilsonFermion5D ComputeTime2/Calls : 911.864 us +Grid : Message : 83.494220 s : Average mflops/s per call : 7.6108e+09 +Grid : Message : 83.494250 s : Average mflops/s per call per rank : 4.75675e+08 +Grid : Message : 83.494260 s : Average mflops/s per call per node : 1.9027e+09 +Grid : Message : 83.494270 s : Average mflops/s per call (full) : 1.44242e+07 +Grid : Message : 83.494280 s : Average mflops/s per call per rank (full): 901513 +Grid : Message : 83.494290 s : Average mflops/s per call per node (full): 3.60605e+06 +Grid : Message : 83.494300 s : WilsonFermion5D Stencil +Grid : Message : 83.494300 s : WilsonFermion5D StencilEven +Grid : Message : 83.494300 s : WilsonFermion5D StencilOdd +Grid : Message : 83.494300 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 83.494300 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 83.494300 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 94.600488 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 94.600501 s : Called DwDag +Grid : Message : 94.600502 s : norm dag result 12.0421 +Grid : Message : 94.613445 s : norm dag ref 12.0421 +Grid : Message : 94.628514 s : norm dag diff 7.63236e-14 +Grid : Message : 94.666370 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 95.136361 s : src_e0.499997 +Grid : Message : 95.208108 s : src_o0.500003 +Grid : Message : 95.271511 s : ********************************************************* +Grid : Message : 95.271512 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 95.271513 s : * Vectorising space-time by 8 +Grid : Message : 95.271514 s : * SINGLE precision +Grid : Message : 95.271514 s : * Using Overlapped Comms/Compute +Grid : Message : 95.271515 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 95.271515 s : ********************************************************* +Grid : Message : 132.766274 s : Deo mflop/s = 1.41952e+07 +Grid : Message : 132.766295 s : Deo mflop/s per rank 887201 +Grid : Message : 132.766297 s : Deo mflop/s per node 3.5488e+06 +Grid : Message : 132.766299 s : #### Dhop calls report +Grid : Message : 132.766300 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 132.766301 s : WilsonFermion5D TotalTime /Calls : 12493.9 us +Grid : Message : 132.766302 s : WilsonFermion5D CommTime /Calls : 10990.2 us +Grid : Message : 132.766303 s : WilsonFermion5D FaceTime /Calls : 604.889 us +Grid : Message : 132.766304 s : WilsonFermion5D ComputeTime1/Calls : 13.7158 us +Grid : Message : 132.766305 s : WilsonFermion5D ComputeTime2/Calls : 920.096 us +Grid : Message : 132.766326 s : Average mflops/s per call : 1.31121e+10 +Grid : Message : 132.766328 s : Average mflops/s per call per rank : 8.19504e+08 +Grid : Message : 132.766329 s : Average mflops/s per call per node : 3.27802e+09 +Grid : Message : 132.766330 s : Average mflops/s per call (full) : 1.44381e+07 +Grid : Message : 132.766331 s : Average mflops/s per call per rank (full): 902382 +Grid : Message : 132.766332 s : Average mflops/s per call per node (full): 3.60953e+06 +Grid : Message : 132.766333 s : WilsonFermion5D Stencil +Grid : Message : 132.766333 s : WilsonFermion5D StencilEven +Grid : Message : 132.766333 s : WilsonFermion5D StencilOdd +Grid : Message : 132.766333 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 132.766333 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 132.766333 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 132.847999 s : r_e6.02111 +Grid : Message : 132.854237 s : r_o6.02102 +Grid : Message : 132.860309 s : res12.0421 +Grid : Message : 133.458462 s : norm diff 0 +Grid : Message : 133.832713 s : norm diff even 0 +Grid : Message : 133.909147 s : norm diff odd 0 diff --git a/systems/Perlmutter/dwf4.slurm b/systems/Perlmutter/dwf4.slurm new file mode 100644 index 00000000..ba198595 --- /dev/null +++ b/systems/Perlmutter/dwf4.slurm @@ -0,0 +1,24 @@ +#!/bin/bash +#SBATCH -A mp13 +#SBATCH -C gpu +#SBATCH -q regular +#SBATCH -t 0:20:00 +#SBATCH -n 16 +#SBATCH --ntasks-per-node=4 +#SBATCH -c 32 +#SBATCH --exclusive +#SBATCH --gpus-per-task=1 +#SBATCH --gpu-bind=map_gpu:0,1,2,3 + +export SLURM_CPU_BIND="cores" +export MPICH_RDMA_ENABLED_CUDA=1 +export MPICH_GPU_SUPPORT_ENABLED=1 +srun ./benchmarks/Benchmark_comms_host_device --mpi 2.2.2.2 --accelerator-threads 8 > comms.4node + +OPT="--comms-overlap --comms-concurrent --shm-mpi 0" +srun ./benchmarks/Benchmark_dwf_fp32 --mpi 2.2.2.2 --grid 64.64.64.64 --accelerator-threads 8 --shm 2048 $OPT > dwf.64.64.64.64.4node.opt0 +srun ./benchmarks/Benchmark_dwf_fp32 --mpi 2.2.2.2 --grid 48.48.48.48 --accelerator-threads 8 --shm 2048 $OPT > dwf.48.48.48.48.4node.opt0 + +OPT="--comms-overlap --comms-concurrent --shm-mpi 1" +srun ./benchmarks/Benchmark_dwf_fp32 --mpi 2.2.2.2 --grid 64.64.64.64 --accelerator-threads 8 --shm 2048 $OPT > dwf.64.64.64.64.4node.opt1 +srun ./benchmarks/Benchmark_dwf_fp32 --mpi 2.2.2.2 --grid 48.48.48.48 --accelerator-threads 8 --shm 2048 $OPT > dwf.48.48.48.48.4node.opt1 diff --git a/systems/Perlmutter/sourceme.sh b/systems/Perlmutter/sourceme.sh new file mode 100644 index 00000000..9359dea9 --- /dev/null +++ b/systems/Perlmutter/sourceme.sh @@ -0,0 +1,4 @@ + +export CRAY_ACCEL_TARGET=nvidia80 + +module load PrgEnv-gnu cpe-cuda cuda From 67e08aa952539c92cd03b9449924bad5c96e7f36 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 23 Sep 2021 23:39:55 +0200 Subject: [PATCH 232/399] New file not run yet --- examples/Example_wall_wall_spectrum.cc | 404 +++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 examples/Example_wall_wall_spectrum.cc diff --git a/examples/Example_wall_wall_spectrum.cc b/examples/Example_wall_wall_spectrum.cc new file mode 100644 index 00000000..0d70f351 --- /dev/null +++ b/examples/Example_wall_wall_spectrum.cc @@ -0,0 +1,404 @@ +/* + * Warning: This code illustrative only: not well tested, and not meant for production use + * without regression / tests being applied + */ + +#include <Grid/Grid.h> + +using namespace std; +using namespace Grid; +typedef SpinColourMatrix Propagator; +typedef SpinColourVector Fermion; + +template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + GridBase *grid; + GaugeField U; + + CovariantLaplacianCshift(GaugeField &_U) : + grid(_U.Grid()), + U(_U) { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out=Zero(); + for(int mu=0;mu<Nd-1;mu++) { + GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent + out = out - Gimpl::CovShiftForward(Umu,mu,in); + out = out - Gimpl::CovShiftBackward(Umu,mu,in); + out = out + 2.0*in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + +void MakePhase(Coordinate mom,LatticeComplex &phase) +{ + GridBase *grid = phase.Grid(); + auto latt_size = grid->GlobalDimensions(); + ComplexD ci(0.0,1.0); + phase=Zero(); + + LatticeComplex coor(phase.Grid()); + for(int mu=0;mu<Nd;mu++){ + RealD TwoPiL = M_PI * 2.0/ latt_size[mu]; + LatticeCoordinate(coor,mu); + phase = phase + (TwoPiL * mom[mu]) * coor; + } + phase = exp(phase*ci); +} +void PointSource(Coordinate &coor,LatticePropagator &source) +{ + // Coordinate coor({0,0,0,0}); + source=Zero(); + SpinColourMatrix kronecker; kronecker=1.0; + pokeSite(kronecker,source,coor); +} +void GFWallSource(int tslice,LatticePropagator &source) +{ + GridBase *grid = source.Grid(); + LatticeComplex one(grid); one = ComplexD(1.0,0.0); + LatticeComplex zz(grid); zz=Zero(); + LatticeInteger t(grid); + LatticeCoordinate(t,Tdir); + one = where(t==Integer(tslice), one, zz); + source = 1.0; + source = source * one; +} + +void Z2WallSource(GridParallelRNG &RNG,int tslice,LatticePropagator &source) +{ + GridBase *grid = source.Grid(); + LatticeComplex noise(grid); + LatticeComplex zz(grid); zz=Zero(); + LatticeInteger t(grid); + + RealD nrm=1.0/sqrt(2); + bernoulli(RNG, noise); // 0,1 50:50 + + noise = (2.*noise - Complex(1,1))*nrm; + + LatticeCoordinate(t,Tdir); + noise = where(t==Integer(tslice), noise, zz); + + source = 1.0; + source = source*noise; + std::cout << " Z2 wall " << norm2(source) << std::endl; +} +void GaugeFix(LatticeGaugeField &U,LatticeGaugeField &Ufix) +{ + Real alpha=0.05; + + Real plaq=WilsonLoops<PeriodicGimplR>::avgPlaquette(U); + + std::cout << " Initial plaquette "<<plaq << std::endl; + + LatticeColourMatrix xform(U.Grid()); + Ufix = U; + int orthog=Nd-1; + FourierAcceleratedGaugeFixer<PeriodicGimplR>::SteepestDescentGaugeFix(Ufix,xform,alpha,10000,1.0e-12, 1.0e-12,true,orthog); + + plaq=WilsonLoops<PeriodicGimplR>::avgPlaquette(Ufix); + + std::cout << " Final plaquette "<<plaq << std::endl; +} +template<class Field> +void GaussianSmear(LatticeGaugeField &U,Field &unsmeared,Field &smeared) +{ + typedef CovariantLaplacianCshift <PeriodicGimplR,Field> Laplacian_t; + Laplacian_t Laplacian(U); + + Integer Iterations = 40; + Real width = 2.0; + Real coeff = (width*width) / Real(4*Iterations); + + Field tmp(U.Grid()); + smeared=unsmeared; + // chi = (1-p^2/2N)^N kronecker + for(int n = 0; n < Iterations; ++n) { + Laplacian.M(smeared,tmp); + smeared = smeared - coeff*tmp; + std::cout << " smear iter " << n<<" " <<norm2(smeared)<<std::endl; + } +} +void GaussianSource(Coordinate &site,LatticeGaugeField &U,LatticePropagator &source) +{ + LatticePropagator tmp(source.Grid()); + PointSource(site,source); + std::cout << " GaussianSource Kronecker "<< norm2(source)<<std::endl; + tmp = source; + GaussianSmear(U,tmp,source); + std::cout << " GaussianSource Smeared "<< norm2(source)<<std::endl; +} +void GaussianWallSource(GridParallelRNG &RNG,int tslice,LatticeGaugeField &U,LatticePropagator &source) +{ + Z2WallSource(RNG,tslice,source); + auto tmp = source; + GaussianSmear(U,tmp,source); +} +void SequentialSource(int tslice,Coordinate &mom,LatticePropagator &spectator,LatticePropagator &source) +{ + assert(mom.size()==Nd); + assert(mom[Tdir] == 0); + + GridBase * grid = spectator.Grid(); + + LatticeInteger ts(grid); + LatticeCoordinate(ts,Tdir); + source = Zero(); + source = where(ts==Integer(tslice),spectator,source); // Stick in a slice of the spectator, zero everywhere else + + LatticeComplex phase(grid); + MakePhase(mom,phase); + + source = source *phase; +} +template<class Action> +void Solve(Action &D,LatticePropagator &source,LatticePropagator &propagator) +{ + GridBase *UGrid = D.GaugeGrid(); + GridBase *FGrid = D.FermionGrid(); + + LatticeFermion src4 (UGrid); + LatticeFermion src5 (FGrid); + LatticeFermion result5(FGrid); + LatticeFermion result4(UGrid); + + ConjugateGradient<LatticeFermion> CG(1.0e-8,100000); + SchurRedBlackDiagMooeeSolve<LatticeFermion> schur(CG); + ZeroGuesser<LatticeFermion> ZG; // Could be a DeflatedGuesser if have eigenvectors + for(int s=0;s<Nd;s++){ + for(int c=0;c<Nc;c++){ + PropToFerm<Action>(src4,source,s,c); + + D.ImportPhysicalFermionSource(src4,src5); + + result5=Zero(); + schur(D,src5,result5,ZG); + std::cout<<GridLogMessage + <<"spin "<<s<<" color "<<c + <<" norm2(src5d) " <<norm2(src5) + <<" norm2(result5d) "<<norm2(result5)<<std::endl; + + D.ExportPhysicalFermionSolution(result5,result4); + + FermToProp<Action>(propagator,result4,s,c); + } + } +} + +class MesonFile: Serializable { +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(MesonFile, std::vector<std::vector<Complex> >, data); +}; + +void MesonTrace(std::string file,LatticePropagator &q1,LatticePropagator &q2,LatticeComplex &phase) +{ + const int nchannel=4; + Gamma::Algebra Gammas[nchannel][2] = { + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::GammaTGamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaTGamma5} + }; + + Gamma G5(Gamma::Algebra::Gamma5); + + LatticeComplex meson_CF(q1.Grid()); + MesonFile MF; + + for(int ch=0;ch<nchannel;ch++){ + + Gamma Gsrc(Gammas[ch][0]); + Gamma Gsnk(Gammas[ch][1]); + + meson_CF = trace(G5*adj(q1)*G5*Gsnk*q2*adj(Gsrc)); + + std::vector<TComplex> meson_T; + sliceSum(meson_CF,meson_T, Tdir); + + int nt=meson_T.size(); + + std::vector<Complex> corr(nt); + for(int t=0;t<nt;t++){ + corr[t] = TensorRemove(meson_T[t]); // Yes this is ugly, not figured a work around + std::cout << " channel "<<ch<<" t "<<t<<" " <<corr[t]<<std::endl; + } + MF.data.push_back(corr); + } + + { + XmlWriter WR(file); + write(WR,"MesonFile",MF); + } +} + + +void WallSinkMesonTrace(std::string file,std::vector<Propagator> &q1,std::vector<Propagator> &q2) +{ + const int nchannel=4; + Gamma::Algebra Gammas[nchannel][2] = { + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::GammaTGamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaTGamma5} + }; + + Gamma G5(Gamma::Algebra::Gamma5); + int nt=q1.size(); + std::vector<Complex> meson_CF(nt); + MesonFile MF; + + for(int ch=0;ch<nchannel;ch++){ + + Gamma Gsrc(Gammas[ch][0]); + Gamma Gsnk(Gammas[ch][1]); + + std::vector<Complex> corr(nt); + for(int t=0;t<nt;t++){ + meson_CF[t] = trace(G5*adj(q1[t])*G5*Gsnk*q2[t]*adj(Gsrc)); + corr[t] = TensorRemove(meson_CF[t]); // Yes this is ugly, not figured a work around + std::cout << " channel "<<ch<<" t "<<t<<" " <<corr[t]<<std::endl; + } + MF.data.push_back(corr); + } + + { + XmlWriter WR(file); + write(WR,"MesonFile",MF); + } +} + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + // Double precision grids + GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), + GridDefaultSimd(Nd,vComplex::Nsimd()), + GridDefaultMpi()); + GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); + + ////////////////////////////////////////////////////////////////////// + // You can manage seeds however you like. + // Recommend SeedUniqueString. + ////////////////////////////////////////////////////////////////////// + std::vector<int> seeds4({1,2,3,4}); + GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); + + LatticeGaugeField Umu(UGrid); + LatticeGaugeField Ufixed(UGrid); + std::string config; + if( argc > 1 && argv[1][0] != '-' ) + { + std::cout<<GridLogMessage <<"Loading configuration from "<<argv[1]<<std::endl; + FieldMetaData header; + NerscIO::readConfiguration(Umu, header, argv[1]); + config=argv[1]; + } + else + { + std::cout<<GridLogMessage <<"Using hot configuration"<<std::endl; + SU<Nc>::ColdConfiguration(Umu); + // SU<Nc>::HotConfiguration(RNG4,Umu); + config="HotConfig"; + } + GaugeFix(Umu,Ufixed); + Umu=Ufixed; + + + std::vector<RealD> masses({ 0.004,0.02477,0.447} ); // u/d, s, c ?? + std::vector<RealD> M5s ({ 1.8,1.8,1.0} ); + std::vector<RealD> bs ({ 1.0,1.0,1.5} ); // DDM + std::vector<RealD> cs ({ 0.0,0.0,0.5} ); // DDM + std::vector<int> Ls_s ({ 16,16,12} ); + std::vector<GridCartesian *> FGrids; + std::vector<GridRedBlackCartesian *> FrbGrids; + + int nmass = masses.size(); + + std::vector<MobiusFermionR *> FermActs; + + std::cout<<GridLogMessage <<"======================"<<std::endl; + std::cout<<GridLogMessage <<"MobiusFermion action as Scaled Shamir kernel"<<std::endl; + std::cout<<GridLogMessage <<"======================"<<std::endl; + + for(int m=0;m<masses.size();m++) { + + RealD mass = masses[m]; + RealD M5 = M5s[m]; + RealD b = bs[m]; + RealD c = cs[m]; + int Ls = Ls_s[m]; + + FGrids.push_back(SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid)); + FrbGrids.push_back(SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid)); + + FermActs.push_back(new MobiusFermionR(Umu,*FGrids[m],*FrbGrids[m],*UGrid,*UrbGrid,mass,M5,b,c)); + } + + LatticePropagator point_source(UGrid); + LatticePropagator z2wall_source(UGrid); + LatticePropagator gfwall_source(UGrid); + + Coordinate Origin({0,0,0,0}); + PointSource (Origin,point_source); + Z2WallSource (RNG4,0,z2wall_source); + GFWallSource (0,gfwall_source); + + std::vector<LatticePropagator> PointProps(nmass,UGrid); + std::vector<LatticePropagator> GaussProps(nmass,UGrid); + std::vector<LatticePropagator> Z2Props (nmass,UGrid); + std::vector<LatticePropagator> GFProps (nmass,UGrid); + + for(int m=0;m<nmass;m++) { + + Solve(*FermActs[m],z2wall_source ,Z2Props[m]); + Solve(*FermActs[m],gfwall_source ,GFProps[m]); + + } + + LatticeComplex phase(UGrid); + Coordinate mom({0,0,0,0}); + MakePhase(mom,phase); + + std::vector<std::vector<Propagator> > wsnk_z2Props(nmass); + std::vector<std::vector<Propagator> > wsnk_gfProps(nmass); + for(int m=0;m<nmass;m++){ + sliceSum(Z2Props[m],wsnk_z2Props[m],Tdir); + sliceSum(GFProps[m],wsnk_gfProps[m],Tdir); + } + + for(int m1=0 ;m1<nmass;m1++) { + for(int m2=m1;m2<nmass;m2++) { + std::stringstream ssg,ssz; + std::stringstream wssg,wssz; + + /// Point sinks + ssg<<config<< "_m" << m1 << "_m"<< m2 << "p_gf_meson.xml"; + ssz<<config<< "_m" << m1 << "_m"<< m2 << "p_z2_meson.xml"; + + MesonTrace(ssz.str(),Z2Props[m1],Z2Props[m2],phase); + + /// Wall sinks + wssg<<config<< "_m" << m1 << "_m"<< m2 << "w_gf_meson.xml"; + wssz<<config<< "_m" << m1 << "_m"<< m2 << "w_z2_meson.xml"; + + WallSinkMesonTrace(wssg.str(),wsnk_gfProps[m1],wsnk_gfProps[m2]); + WallSinkMesonTrace(wssz.str(),wsnk_z2Props[m1],wsnk_z2Props[m2]); + + }} + + Grid_finalize(); +} + + + From a822c48565ccd3352b2d7f4682eabf1365249b6e Mon Sep 17 00:00:00 2001 From: "Henrique B.R" <h.b.rocha@ed.ac.uk> Date: Fri, 24 Sep 2021 17:13:25 +0100 Subject: [PATCH 233/399] Added accelerated pick-set checkerboard functions --- Grid/lattice/Lattice_transfer.h | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 2292088c..4b1459c3 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -85,6 +85,77 @@ template<class vobj> inline void setCheckerboard(Lattice<vobj> &full,const Latti }); } +template<class vobj> inline void acceleratorPickCheckerboard(int cb,Lattice<vobj> &half,const Lattice<vobj> &full, int checker_dim_half=0) +{ + half.Checkerboard() = cb; + autoView(half_v, half, AcceleratorWrite); + autoView(full_v, full, AcceleratorRead); + Coordinate rdim_full = full.Grid()->_rdimensions; + Coordinate rdim_half = half.Grid()->_rdimensions; + unsigned long ndim_half = half.Grid()->_ndimension; + Coordinate checker_dim_mask_half = half.Grid()->_checker_dim_mask; + Coordinate ostride_half = half.Grid()->_ostride; + accelerator_for(ss, full.Grid()->oSites(),full.Grid()->Nsimd(),{ + + Coordinate coor; + int cbos; + int linear=0; + + Lexicographic::CoorFromIndex(coor,ss,rdim_full); + assert(coor.size()==ndim_half); + + for(int d=0;d<ndim_half;d++){ + if(checker_dim_mask_half[d]) linear += coor[d]; + } + cbos = (linear&0x1); + + if (cbos==cb) { + int ssh=0; + for(int d=0;d<ndim_half;d++) { + if (d == checker_dim_half) ssh += ostride_half[d] * ((coor[d] / 2) % rdim_half[d]); + else ssh += ostride_half[d] * (coor[d] % rdim_half[d]); + } + coalescedWrite(half_v[ssh],full_v(ss)); + } + }); +} +template<class vobj> inline void acceleratorSetCheckerboard(Lattice<vobj> &full,const Lattice<vobj> &half, int checker_dim_half=0) +{ + int cb = half.Checkerboard(); + autoView(half_v , half, AcceleratorRead); + autoView(full_v , full, AcceleratorWrite); + nvtxRangePop(); + Coordinate rdim_full = full.Grid()->_rdimensions; + Coordinate rdim_half = half.Grid()->_rdimensions; + unsigned long ndim_half = half.Grid()->_ndimension; + Coordinate checker_dim_mask_half = half.Grid()->_checker_dim_mask; + Coordinate ostride_half = half.Grid()->_ostride; + accelerator_for(ss,full.Grid()->oSites(),full.Grid()->Nsimd(),{ + + Coordinate coor; + int cbos; + int linear=0; + + Lexicographic::CoorFromIndex(coor,ss,rdim_full); + assert(coor.size()==ndim_half); + + for(int d=0;d<ndim_half;d++){ + if(checker_dim_mask_half[d]) linear += coor[d]; + } + cbos = (linear&0x1); + + if (cbos==cb) { + int ssh=0; + for(int d=0;d<ndim_half;d++){ + if (d == checker_dim_half) ssh += ostride_half[d] * ((coor[d] / 2) % rdim_half[d]); + else ssh += ostride_half[d] * (coor[d] % rdim_half[d]); + } + coalescedWrite(full_v[ss],half_v(ssh)); + } + + }); +} + //////////////////////////////////////////////////////////////////////////////////////////// // Flexible Type Conversion for internal promotion to double as well as graceful // treatment of scalar-compatible types From 6efdad6f2163928f8cdf415801700dc9ef5995ad Mon Sep 17 00:00:00 2001 From: "Henrique B.R" <h.b.rocha@ed.ac.uk> Date: Fri, 24 Sep 2021 17:18:04 +0100 Subject: [PATCH 234/399] Removed Halo benchmark --- benchmarks/Benchmark_halo.cpp | 99 ----------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 benchmarks/Benchmark_halo.cpp diff --git a/benchmarks/Benchmark_halo.cpp b/benchmarks/Benchmark_halo.cpp deleted file mode 100644 index 5a8e99d9..00000000 --- a/benchmarks/Benchmark_halo.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include <Grid/Grid.h> - -#define NCOL 2 - -using namespace Grid; - -constexpr int Ndim = 3; -typedef ScalarAdjMatrixImplTypes<vComplex,NCOL>::Field SUNField; -typedef typename SUNField::vector_object vobj; -typedef CartesianStencil<vobj, vobj,int> Stencil; - -int main(int argc, char **argv){ - - // Initialise grid ////////////////////////////////////////////// - Grid_init(&argc,&argv); - int threads = GridThread::GetThreads(); - std::cout << GridLogMessage << "Grid is setup to use " << threads << " threads" << std::endl; - - // Module /////////////////////////////////////////////////////// - GridModule GridMod; - if (GridDefaultLatt().size() != Ndim){ - std::cout << GridLogError << "Incorrect dimension of the grid\n. Expected dim=" << Ndim << std::endl; - return EXIT_FAILURE; - } - if (GridDefaultMpi().size() != Ndim){ - std::cout << GridLogError << "Incorrect dimension of the mpi grid\n. Expected dim=" << Ndim << std::endl; - return EXIT_FAILURE; - } - GridMod.set_full(new GridCartesian(GridDefaultLatt(), - GridDefaultSimd(Ndim, vComplex::Nsimd()), - GridDefaultMpi())); - GridMod.set_rb(new GridRedBlackCartesian(GridMod.get_full())); - auto grid = GridMod.get_full(); - - GridParallelRNG pRNG(grid); - pRNG.SeedFixedIntegers({11,84,79,47,90}); - - // Stencil ////////////////////////////////////////////////////// - int npoint = 2 * Ndim; - std::vector<int> directions(npoint); - std::vector<int> displacements(npoint); - - for (int mu = 0; mu < Ndim; mu++){ - directions[mu] = mu; - directions[mu + Ndim] = mu; - displacements[mu] = 1; - displacements[mu + Ndim] = -1; - } - - Stencil Stencil_phi(grid, npoint, 0, directions, displacements,0); - SimpleCompressor<vobj> compressor; - - // Field ///////////////////////////////////////////////////////// - SUNField phi(grid); - - // MPI sublattice surface area /////////////////////////////////// - - int mpi_area = 0; - int mpi_face; - // Calculates the total surface area of an MPI hypercube - for (int mu_ex=0;mu_ex<Ndim;++mu_ex){ - mpi_face = 1; - - for (int mu=0; mu<Ndim; ++mu){ - if (mu != mu_ex) mpi_face *= GridDefaultLatt()[mu]/GridDefaultMpi()[mu]; - } - - mpi_area += 2*mpi_face; - } - - std::cout << GridLogMessage << "Total MPI surface area = " << mpi_area << std::endl; - - // Benchmarking ////////////////////////////////////////////////// - - int nloops = 100; - double start; - double time; - double avgtime = 0; - double bytes = sizeof(Complex)*NCOL*NCOL*mpi_area*4.; - // 4 is for the two reads and writes in receiving and sending data - // I don't know if I am to consider all data being sent and received in all mpi processes across all gpus - double avgbandwidth = 0; - - for (int i=0;i<nloops;++i){ - - start = usecond(); - Stencil_phi.HaloExchange(phi, compressor); - time = usecond(); - std::cout << GridLogMessage << "Exchange " << i << " time (us) = " << time-start << " | " << "Bandwidth (GB/s) = " << (bytes/1e9)/(time/1e6) << std::endl; - avgtime += time/double(nloops); - avgbandwidth += (bytes/1e9)/(time/1e6)/double(nloops); - - } - - std::cout << GridLogMessage << "Average time (us) = " << avgtime << " | Average bandwidth (GB/s) = " << avgbandwidth << std::endl; - - Grid_finalize(); - -} \ No newline at end of file From 7e130076d619ad7756caca7806b31c9bbc4ace2c Mon Sep 17 00:00:00 2001 From: "Henrique B.R" <h.b.rocha@ed.ac.uk> Date: Fri, 24 Sep 2021 17:26:31 +0100 Subject: [PATCH 235/399] Fixed line left behind --- Grid/lattice/Lattice_transfer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 4b1459c3..ef489ea6 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -124,7 +124,6 @@ template<class vobj> inline void acceleratorSetCheckerboard(Lattice<vobj> &full, int cb = half.Checkerboard(); autoView(half_v , half, AcceleratorRead); autoView(full_v , full, AcceleratorWrite); - nvtxRangePop(); Coordinate rdim_full = full.Grid()->_rdimensions; Coordinate rdim_half = half.Grid()->_rdimensions; unsigned long ndim_half = half.Grid()->_ndimension; From 73a95fa96f09b9dd63d713d952630e3cba4de541 Mon Sep 17 00:00:00 2001 From: Antonin Portelli <antonin.portelli@me.com> Date: Tue, 28 Sep 2021 12:44:26 +0100 Subject: [PATCH 236/399] LinearFunction loops over vectors by default, can be overloaded --- Grid/algorithms/LinearOperator.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Grid/algorithms/LinearOperator.h b/Grid/algorithms/LinearOperator.h index 1add212c..815226d2 100644 --- a/Grid/algorithms/LinearOperator.h +++ b/Grid/algorithms/LinearOperator.h @@ -530,6 +530,16 @@ public: template<class Field> class LinearFunction { public: virtual void operator() (const Field &in, Field &out) = 0; + + virtual void operator() (const std::vector<Field> &in, std::vector<Field> &out) + { + assert(in.size() == out.size()); + + for (unsigned int i = 0; i < in.size(); ++i) + { + (*this)(in[i], out[i]); + } + } }; template<class Field> class IdentityLinearFunction : public LinearFunction<Field> { From 9523ad3d7345f443c272e6c148f7c54311b64e68 Mon Sep 17 00:00:00 2001 From: Antonin Portelli <antonin.portelli@me.com> Date: Tue, 28 Sep 2021 12:45:47 +0100 Subject: [PATCH 237/399] vector version of Schur solver use vector guesser --- Grid/algorithms/iterative/SchurRedBlack.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Grid/algorithms/iterative/SchurRedBlack.h b/Grid/algorithms/iterative/SchurRedBlack.h index 15ef95c7..d97e4993 100644 --- a/Grid/algorithms/iterative/SchurRedBlack.h +++ b/Grid/algorithms/iterative/SchurRedBlack.h @@ -185,16 +185,19 @@ namespace Grid { //////////////////////////////////////////////// if ( subGuess ) guess_save.resize(nblock,grid); - for(int b=0;b<nblock;b++){ - if(useSolnAsInitGuess) { + + if(useSolnAsInitGuess) { + for(int b=0;b<nblock;b++){ pickCheckerboard(Odd, sol_o[b], out[b]); - } else { - guess(src_o[b],sol_o[b]); } + } else { + guess(src_o, sol_o); + } - if ( subGuess ) { - guess_save[b] = sol_o[b]; - } + if ( subGuess ) { + for(int b=0;b<nblock;b++){ + guess_save[b] = sol_o[b]; + } } ////////////////////////////////////////////////////////////// // Call the block solver From 6c66b8d99780dbe2b75aa226dcfa8f5ac34e9e75 Mon Sep 17 00:00:00 2001 From: Antonin Portelli <antonin.portelli@me.com> Date: Thu, 30 Sep 2021 19:25:12 +0100 Subject: [PATCH 238/399] deflated guesser can optionally be used with less vectors than provided --- Grid/algorithms/iterative/Deflation.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index 626b60e3..43fe3e35 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -54,15 +54,23 @@ class DeflatedGuesser: public LinearFunction<Field> { private: const std::vector<Field> &evec; const std::vector<RealD> &eval; + const unsigned int N; public: - DeflatedGuesser(const std::vector<Field> & _evec,const std::vector<RealD> & _eval) : evec(_evec), eval(_eval) {}; + DeflatedGuesser(const std::vector<Field> & _evec,const std::vector<RealD> & _eval) + : DeflatedGuesser(_evec, _eval, _evec.size()) + {} + + DeflatedGuesser(const std::vector<Field> & _evec, const std::vector<RealD> & _eval, const unsigned int _N) + : evec(_evec), eval(_eval), N(_N) + { + assert(evec.size()==eval.size()); + assert(N <= evec.size()); + } virtual void operator()(const Field &src,Field &guess) { guess = Zero(); - assert(evec.size()==eval.size()); - auto N = evec.size(); for (int i=0;i<N;i++) { const Field& tmp = evec[i]; axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess); From a976fa6746f463cdf85836df7c1fe834e3b6645a Mon Sep 17 00:00:00 2001 From: Antonin Portelli <antonin.portelli@me.com> Date: Tue, 5 Oct 2021 14:19:47 +0100 Subject: [PATCH 239/399] expose gauge group in GImpl and generic Nc fix --- Grid/qcd/action/gauge/GaugeImplTypes.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/action/gauge/GaugeImplTypes.h b/Grid/qcd/action/gauge/GaugeImplTypes.h index 2499e0e9..5c9405ab 100644 --- a/Grid/qcd/action/gauge/GaugeImplTypes.h +++ b/Grid/qcd/action/gauge/GaugeImplTypes.h @@ -78,6 +78,8 @@ public: typedef Lattice<SiteLink> LinkField; typedef Lattice<SiteField> Field; + typedef SU<Nrepresentation> Group; + // Guido: we can probably separate the types from the HMC functions // this will create 2 kind of implementations // probably confusing the users @@ -118,7 +120,7 @@ public: LinkField Pmu(P.Grid()); Pmu = Zero(); for (int mu = 0; mu < Nd; mu++) { - SU<Nrepresentation>::GaussianFundamentalLieAlgebraMatrix(pRNG, Pmu); + Group::GaussianFundamentalLieAlgebraMatrix(pRNG, Pmu); RealD scale = ::sqrt(HMC_MOMENTUM_DENOMINATOR) ; Pmu = Pmu*scale; PokeIndex<LorentzIndex>(P, Pmu, mu); @@ -159,15 +161,15 @@ public: } static inline void HotConfiguration(GridParallelRNG &pRNG, Field &U) { - SU<Nc>::HotConfiguration(pRNG, U); + Group::HotConfiguration(pRNG, U); } static inline void TepidConfiguration(GridParallelRNG &pRNG, Field &U) { - SU<Nc>::TepidConfiguration(pRNG, U); + Group::TepidConfiguration(pRNG, U); } static inline void ColdConfiguration(GridParallelRNG &pRNG, Field &U) { - SU<Nc>::ColdConfiguration(pRNG, U); + Group::ColdConfiguration(pRNG, U); } }; From 8ed0b57b092159ccc1123f739abc0179a182ca1b Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 5 Oct 2021 11:41:03 -0400 Subject: [PATCH 240/399] Memory verbose and tracking, shrink default cache Print PCI device IDs on node 0 --- Grid/allocator/MemoryManager.cc | 89 +++++++++++++++++++++------- Grid/allocator/MemoryManager.h | 5 +- Grid/allocator/MemoryManagerCache.cc | 3 +- Grid/threads/Accelerator.cc | 14 ++++- Grid/threads/Accelerator.h | 12 ++++ 5 files changed, 98 insertions(+), 25 deletions(-) diff --git a/Grid/allocator/MemoryManager.cc b/Grid/allocator/MemoryManager.cc index c1a4d93a..30be510b 100644 --- a/Grid/allocator/MemoryManager.cc +++ b/Grid/allocator/MemoryManager.cc @@ -9,14 +9,30 @@ NAMESPACE_BEGIN(Grid); #define AccSmall (3) #define Shared (4) #define SharedSmall (5) +#undef GRID_MM_VERBOSE uint64_t total_shared; uint64_t total_device; uint64_t total_host;; void MemoryManager::PrintBytes(void) { - std::cout << " MemoryManager : "<<total_shared<<" shared bytes "<<std::endl; - std::cout << " MemoryManager : "<<total_device<<" accelerator bytes "<<std::endl; - std::cout << " MemoryManager : "<<total_host <<" cpu bytes "<<std::endl; + std::cout << " MemoryManager : ------------------------------------ "<<std::endl; + std::cout << " MemoryManager : PrintBytes "<<std::endl; + std::cout << " MemoryManager : ------------------------------------ "<<std::endl; + std::cout << " MemoryManager : "<<(total_shared>>20)<<" shared Mbytes "<<std::endl; + std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl; + std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl; + uint64_t cacheBytes; + cacheBytes = CacheBytes[Cpu]; + std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu cache Mbytes "<<std::endl; + cacheBytes = CacheBytes[Acc]; + std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc cache Mbytes "<<std::endl; + cacheBytes = CacheBytes[Shared]; + std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" shared cache Mbytes "<<std::endl; + +#ifdef GRID_CUDA + cuda_mem(); +#endif + } ////////////////////////////////////////////////////////////////////// @@ -24,86 +40,114 @@ void MemoryManager::PrintBytes(void) ////////////////////////////////////////////////////////////////////// MemoryManager::AllocationCacheEntry MemoryManager::Entries[MemoryManager::NallocType][MemoryManager::NallocCacheMax]; int MemoryManager::Victim[MemoryManager::NallocType]; -int MemoryManager::Ncache[MemoryManager::NallocType] = { 8, 32, 8, 32, 8, 32 }; - +int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 8, 2, 8, 2, 8 }; +uint64_t MemoryManager::CacheBytes[MemoryManager::NallocType]; ////////////////////////////////////////////////////////////////////// // Actual allocation and deallocation utils ////////////////////////////////////////////////////////////////////// void *MemoryManager::AcceleratorAllocate(size_t bytes) { + total_device+=bytes; void *ptr = (void *) Lookup(bytes,Acc); if ( ptr == (void *) NULL ) { ptr = (void *) acceleratorAllocDevice(bytes); - total_device+=bytes; } +#ifdef GRID_MM_VERBOSE + std::cout <<"AcceleratorAllocate "<<std::endl; + PrintBytes(); +#endif return ptr; } void MemoryManager::AcceleratorFree (void *ptr,size_t bytes) { + total_device-=bytes; void *__freeme = Insert(ptr,bytes,Acc); if ( __freeme ) { acceleratorFreeDevice(__freeme); - total_device-=bytes; - // PrintBytes(); } +#ifdef GRID_MM_VERBOSE + std::cout <<"AcceleratorFree "<<std::endl; + PrintBytes(); +#endif } void *MemoryManager::SharedAllocate(size_t bytes) { + total_shared+=bytes; void *ptr = (void *) Lookup(bytes,Shared); if ( ptr == (void *) NULL ) { ptr = (void *) acceleratorAllocShared(bytes); - total_shared+=bytes; - // std::cout <<"AcceleratorAllocate: allocated Shared pointer "<<std::hex<<ptr<<std::dec<<std::endl; - // PrintBytes(); } +#ifdef GRID_MM_VERBOSE + std::cout <<"SharedAllocate "<<std::endl; + PrintBytes(); +#endif return ptr; } void MemoryManager::SharedFree (void *ptr,size_t bytes) { + total_shared-=bytes; void *__freeme = Insert(ptr,bytes,Shared); if ( __freeme ) { acceleratorFreeShared(__freeme); - total_shared-=bytes; - // PrintBytes(); } +#ifdef GRID_MM_VERBOSE + std::cout <<"SharedFree "<<std::endl; + PrintBytes(); +#endif } #ifdef GRID_UVM void *MemoryManager::CpuAllocate(size_t bytes) { + total_host+=bytes; void *ptr = (void *) Lookup(bytes,Cpu); if ( ptr == (void *) NULL ) { ptr = (void *) acceleratorAllocShared(bytes); - total_host+=bytes; } +#ifdef GRID_MM_VERBOSE + std::cout <<"CpuAllocate "<<std::endl; + PrintBytes(); +#endif return ptr; } void MemoryManager::CpuFree (void *_ptr,size_t bytes) { + total_host-=bytes; NotifyDeletion(_ptr); void *__freeme = Insert(_ptr,bytes,Cpu); if ( __freeme ) { acceleratorFreeShared(__freeme); - total_host-=bytes; } +#ifdef GRID_MM_VERBOSE + std::cout <<"CpuFree "<<std::endl; + PrintBytes(); +#endif } #else void *MemoryManager::CpuAllocate(size_t bytes) { + total_host+=bytes; void *ptr = (void *) Lookup(bytes,Cpu); if ( ptr == (void *) NULL ) { ptr = (void *) acceleratorAllocCpu(bytes); - total_host+=bytes; } +#ifdef GRID_MM_VERBOSE + std::cout <<"CpuAllocate "<<std::endl; + PrintBytes(); +#endif return ptr; } void MemoryManager::CpuFree (void *_ptr,size_t bytes) { + total_host-=bytes; NotifyDeletion(_ptr); void *__freeme = Insert(_ptr,bytes,Cpu); if ( __freeme ) { acceleratorFreeCpu(__freeme); - total_host-=bytes; } +#ifdef GRID_MM_VERBOSE + std::cout <<"CpuFree "<<std::endl; + PrintBytes(); +#endif } #endif @@ -181,13 +225,13 @@ void *MemoryManager::Insert(void *ptr,size_t bytes,int type) #ifdef ALLOCATION_CACHE bool small = (bytes < GRID_ALLOC_SMALL_LIMIT); int cache = type + small; - return Insert(ptr,bytes,Entries[cache],Ncache[cache],Victim[cache]); + return Insert(ptr,bytes,Entries[cache],Ncache[cache],Victim[cache],CacheBytes[cache]); #else return ptr; #endif } -void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim) +void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim, uint64_t &cacheBytes) { assert(ncache>0); #ifdef GRID_OMP @@ -211,6 +255,7 @@ void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries if ( entries[v].valid ) { ret = entries[v].address; + cacheBytes -= entries[v].bytes; entries[v].valid = 0; entries[v].address = NULL; entries[v].bytes = 0; @@ -219,6 +264,7 @@ void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries entries[v].address=ptr; entries[v].bytes =bytes; entries[v].valid =1; + cacheBytes += bytes; return ret; } @@ -228,13 +274,13 @@ void *MemoryManager::Lookup(size_t bytes,int type) #ifdef ALLOCATION_CACHE bool small = (bytes < GRID_ALLOC_SMALL_LIMIT); int cache = type+small; - return Lookup(bytes,Entries[cache],Ncache[cache]); + return Lookup(bytes,Entries[cache],Ncache[cache],CacheBytes[cache]); #else return NULL; #endif } -void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache) +void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t & cacheBytes) { assert(ncache>0); #ifdef GRID_OMP @@ -243,6 +289,7 @@ void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncach for(int e=0;e<ncache;e++){ if ( entries[e].valid && ( entries[e].bytes == bytes ) ) { entries[e].valid = 0; + cacheBytes -= entries[e].bytes; return entries[e].address; } } diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index 25c5b5f5..eafcd83f 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -82,14 +82,15 @@ private: static AllocationCacheEntry Entries[NallocType][NallocCacheMax]; static int Victim[NallocType]; static int Ncache[NallocType]; + static uint64_t CacheBytes[NallocType]; ///////////////////////////////////////////////// // Free pool ///////////////////////////////////////////////// static void *Insert(void *ptr,size_t bytes,int type) ; static void *Lookup(size_t bytes,int type) ; - static void *Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim) ; - static void *Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache) ; + static void *Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim,uint64_t &cbytes) ; + static void *Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t &cbytes) ; static void PrintBytes(void); public: diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index 275ed5e0..72111dbd 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -3,7 +3,7 @@ #warning "Using explicit device memory copies" NAMESPACE_BEGIN(Grid); -//define dprintf(...) printf ( __VA_ARGS__ ); fflush(stdout); +//#define dprintf(...) printf ( __VA_ARGS__ ); fflush(stdout); #define dprintf(...) @@ -429,6 +429,7 @@ void MemoryManager::NotifyDeletion(void *_ptr) } void MemoryManager::Print(void) { + PrintBytes(); std::cout << GridLogDebug << "--------------------------------------------" << std::endl; std::cout << GridLogDebug << "Memory Manager " << std::endl; std::cout << GridLogDebug << "--------------------------------------------" << std::endl; diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 52fadc0b..14e07248 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -74,11 +74,13 @@ void acceleratorInit(void) // GPU_PROP(singleToDoublePrecisionPerfRatio); } } + MemoryManager::DeviceMaxBytes = (8*totalDeviceMem)/10; // Assume 80% ours #undef GPU_PROP_FMT #undef GPU_PROP #ifdef GRID_DEFAULT_GPU + int device = 0; // IBM Jsrun makes cuda Device numbering screwy and not match rank if ( world_rank == 0 ) { printf("AcceleratorCudaInit: using default device \n"); @@ -87,10 +89,20 @@ void acceleratorInit(void) printf("AcceleratorCudaInit: Configure options --enable-setdevice=no \n"); } #else + int device = rank; printf("AcceleratorCudaInit: rank %d setting device to node rank %d\n",world_rank,rank); printf("AcceleratorCudaInit: Configure options --enable-setdevice=yes \n"); - cudaSetDevice(rank); #endif + + cudaSetDevice(device); + + const int len=64; + char busid[len]; + if( rank == world_rank ) { + cudaDeviceGetPCIBusId(busid, len, device); + printf("local rank %d device %d bus id: %s\n", rank, device, busid); + } + if ( world_rank == 0 ) printf("AcceleratorCudaInit: ================================================\n"); } #endif diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 974cc6ce..83b27429 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -115,6 +115,14 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { #endif } // CUDA specific +inline void cuda_mem(void) +{ + size_t free_t,total_t,used_t; + cudaMemGetInfo(&free_t,&total_t); + used_t=total_t-free_t; + std::cout << " MemoryManager : GPU used "<<used_t<<" free "<<free_t<< " total "<<total_t<<std::endl; +} + #define accelerator_for2dNB( iter1, num1, iter2, num2, nsimd, ... ) \ { \ int nt=acceleratorThreads(); \ @@ -125,7 +133,11 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { }; \ dim3 cu_threads(nsimd,acceleratorThreads(),1); \ dim3 cu_blocks ((num1+nt-1)/nt,num2,1); \ + std::cout << "========================== CUDA KERNEL CALL\n"; \ + cuda_mem(); \ LambdaApply<<<cu_blocks,cu_threads>>>(num1,num2,nsimd,lambda); \ + cuda_mem(); \ + std::cout << "========================== CUDA KERNEL DONE\n"; \ } #define accelerator_for6dNB(iter1, num1, \ From 2f4e85e5d6db98f5c3e9edc7c31deb052bc68341 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 5 Oct 2021 14:56:17 -0400 Subject: [PATCH 241/399] Summit set up --- systems/Summit/config-command | 14 ++++++++++++++ systems/Summit/dwf16.lsf | 25 +++++++++++++++++++++++++ systems/Summit/dwf4.lsf | 25 +++++++++++++++++++++++++ systems/Summit/sourceme-cuda10.sh | 8 ++++++++ 4 files changed, 72 insertions(+) create mode 100644 systems/Summit/config-command create mode 100644 systems/Summit/dwf16.lsf create mode 100644 systems/Summit/dwf4.lsf create mode 100644 systems/Summit/sourceme-cuda10.sh diff --git a/systems/Summit/config-command b/systems/Summit/config-command new file mode 100644 index 00000000..b565addc --- /dev/null +++ b/systems/Summit/config-command @@ -0,0 +1,14 @@ +../../configure --enable-comms=mpi \ + --enable-simd=GPU \ + --enable-gen-simd-width=32 \ + --enable-unified=no \ + --enable-shm=nvlink \ + --disable-gparity \ + --enable-setdevice \ + --disable-fermion-reps \ + --enable-accelerator=cuda \ + --prefix /ccs/home/paboyle/prefix \ + CXX=nvcc \ + LDFLAGS=-L/ccs/home/paboyle/prefix/lib/ \ + CXXFLAGS="-ccbin mpicxx -gencode arch=compute_70,code=sm_70 -I/ccs/home/paboyle/prefix/include/ -std=c++14" + diff --git a/systems/Summit/dwf16.lsf b/systems/Summit/dwf16.lsf new file mode 100644 index 00000000..16f4b82d --- /dev/null +++ b/systems/Summit/dwf16.lsf @@ -0,0 +1,25 @@ +#!/bin/bash +#BSUB -P LGT104 +#BSUB -W 2:00 +#BSUB -nnodes 4 +#BSUB -J DWF + +export OMP_NUM_THREADS=6 +export PAMI_IBV_ADAPTER_AFFINITY=1 +export PAMI_ENABLE_STRIPING=1 +export OPT="--comms-concurrent --comms-overlap " + +APP="./benchmarks/Benchmark_comms_host_device --mpi 2.2.2.3 " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP + +APP="./benchmarks/Benchmark_dwf_fp32 --grid 48.48.48.72 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP + +APP="./benchmarks/Benchmark_dwf_fp32 --grid 64.64.64.96 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP + + + + + + diff --git a/systems/Summit/dwf4.lsf b/systems/Summit/dwf4.lsf new file mode 100644 index 00000000..fcd80bcb --- /dev/null +++ b/systems/Summit/dwf4.lsf @@ -0,0 +1,25 @@ +#!/bin/bash +#BSUB -P LGT104 +#BSUB -W 2:00 +#BSUB -nnodes 4 +#BSUB -J DWF + +export OMP_NUM_THREADS=6 +export PAMI_IBV_ADAPTER_AFFINITY=1 +export PAMI_ENABLE_STRIPING=1 +export OPT="--comms-concurrent --comms-overlap " +#export GRID_ALLOC_NCACHE_LARGE=1 +export APP="./benchmarks/Benchmark_comms_host_device --mpi 2.2.2.3 " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP + +APP="./benchmarks/Benchmark_dwf_fp32 --grid 48.48.48.72 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP + +APP="./benchmarks/Benchmark_dwf_fp32 --grid 64.64.64.96 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP + + + + + + diff --git a/systems/Summit/sourceme-cuda10.sh b/systems/Summit/sourceme-cuda10.sh new file mode 100644 index 00000000..58217613 --- /dev/null +++ b/systems/Summit/sourceme-cuda10.sh @@ -0,0 +1,8 @@ +export UCX_GDR_COPY_RCACHE=no +export UCX_MEMTYPE_CACHE=n +export UCX_RNDV_SCHEME=put_zcopy +module load gcc/7.5.0 +module load cuda/10.2.89 +#cuda/11.4.0 +export LD_LIBRARY_PATH=/ccs/home/paboyle/prefix/lib/:$LD_LIBRARY_PATH + From d899ee80fc0d9ed75b3025efa32df1e449125a36 Mon Sep 17 00:00:00 2001 From: Antonin Portelli <antonin.portelli@me.com> Date: Tue, 5 Oct 2021 21:12:47 +0100 Subject: [PATCH 242/399] skip record fixed to include norm metadata --- Grid/parallelIO/IldgIO.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Grid/parallelIO/IldgIO.h b/Grid/parallelIO/IldgIO.h index ef42c159..9ed773dd 100644 --- a/Grid/parallelIO/IldgIO.h +++ b/Grid/parallelIO/IldgIO.h @@ -576,6 +576,7 @@ class ScidacReader : public GridLimeReader { std::string rec_name(ILDG_BINARY_DATA); while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) { if ( !strncmp(limeReaderType(LimeR), rec_name.c_str(),strlen(rec_name.c_str()) ) ) { + skipPastObjectRecord(std::string(GRID_FIELD_NORM)); skipPastObjectRecord(std::string(SCIDAC_CHECKSUM)); return; } From ab6ea29913137fe40a57f762b616135f48a71230 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 5 Oct 2021 20:13:25 -0400 Subject: [PATCH 243/399] Print removal --- Grid/threads/Accelerator.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 83b27429..2c08b76b 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -133,11 +133,7 @@ inline void cuda_mem(void) }; \ dim3 cu_threads(nsimd,acceleratorThreads(),1); \ dim3 cu_blocks ((num1+nt-1)/nt,num2,1); \ - std::cout << "========================== CUDA KERNEL CALL\n"; \ - cuda_mem(); \ LambdaApply<<<cu_blocks,cu_threads>>>(num1,num2,nsimd,lambda); \ - cuda_mem(); \ - std::cout << "========================== CUDA KERNEL DONE\n"; \ } #define accelerator_for6dNB(iter1, num1, \ From 9b1a0653cf64a2342f776aef63f3c8228d627807 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 5 Oct 2021 21:22:01 -0400 Subject: [PATCH 244/399] Summit results --- systems/Summit/comms.4node | 179 +++++++++++++++++++++++++++++++ systems/Summit/dwf.24.4node | 206 ++++++++++++++++++++++++++++++++++++ systems/Summit/dwf.32.4node | 206 ++++++++++++++++++++++++++++++++++++ 3 files changed, 591 insertions(+) create mode 100644 systems/Summit/comms.4node create mode 100644 systems/Summit/dwf.24.4node create mode 100644 systems/Summit/dwf.32.4node diff --git a/systems/Summit/comms.4node b/systems/Summit/comms.4node new file mode 100644 index 00000000..b0df0801 --- /dev/null +++ b/systems/Summit/comms.4node @@ -0,0 +1,179 @@ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: Tesla V100-SXM2-16GB +AcceleratorCudaInit[0]: totalGlobalMem: 16911433728 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 4 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: rank 0 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 0 device 0 bus id: 0004:04:00.0 +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 24 +SharedMemoryMpi: Node communicator of size 6 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 1073741824bytes at 0x200060000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=7cb1ff7395a5833ded6526c43891bd07a0436290: (HEAD -> develop, origin/develop, origin/HEAD) clean + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 1073741824 byte stencil comms buffers +AcceleratorCudaInit: rank 1 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 1 device 1 bus id: 0004:05:00.0 +AcceleratorCudaInit: rank 2 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 2 device 2 bus id: 0004:06:00.0 +AcceleratorCudaInit: rank 5 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 5 device 5 bus id: 0035:05:00.0 +AcceleratorCudaInit: rank 4 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 4 device 4 bus id: 0035:04:00.0 +AcceleratorCudaInit: rank 3 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 3 device 3 bus id: 0035:03:00.0 +Grid : Message : MemoryManager Cache 13529146982 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 8 LARGE 2 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 2.137929 s : Grid is setup to use 6 threads +Grid : Message : 2.137941 s : Number of iterations to average: 250 +Grid : Message : 2.137950 s : ==================================================================================================== +Grid : Message : 2.137958 s : = Benchmarking sequential halo exchange from host memory +Grid : Message : 2.137966 s : ==================================================================================================== +Grid : Message : 2.137974 s : L Ls bytes MB/s uni MB/s bidi +AcceleratorCudaInit: rank 22 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 10 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 15 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 21 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 20 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 7 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 9 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 11 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 8 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 6 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 19 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 23 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 18 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 12 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 16 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 13 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 14 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 17 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +Grid : Message : 2.604949 s : 8 8 393216 89973.9 179947.8 +Grid : Message : 2.668249 s : 8 8 393216 18650.3 37300.5 +Grid : Message : 2.732288 s : 8 8 393216 18428.5 36857.1 +Grid : Message : 2.753565 s : 8 8 393216 55497.2 110994.4 +Grid : Message : 2.808960 s : 12 8 1327104 100181.5 200363.0 +Grid : Message : 3.226900 s : 12 8 1327104 20600.5 41201.0 +Grid : Message : 3.167459 s : 12 8 1327104 24104.6 48209.2 +Grid : Message : 3.227660 s : 12 8 1327104 66156.7 132313.5 +Grid : Message : 3.413570 s : 16 8 3145728 56174.4 112348.8 +Grid : Message : 3.802697 s : 16 8 3145728 24255.9 48511.7 +Grid : Message : 4.190498 s : 16 8 3145728 24336.7 48673.4 +Grid : Message : 4.385171 s : 16 8 3145728 48484.1 96968.2 +Grid : Message : 4.805284 s : 20 8 6144000 46380.5 92761.1 +Grid : Message : 5.562975 s : 20 8 6144000 24328.5 48656.9 +Grid : Message : 6.322562 s : 20 8 6144000 24266.7 48533.4 +Grid : Message : 6.773598 s : 20 8 6144000 40868.5 81736.9 +Grid : Message : 7.600999 s : 24 8 10616832 40198.3 80396.6 +Grid : Message : 8.912917 s : 24 8 10616832 24279.5 48559.1 +Grid : Message : 10.220961 s : 24 8 10616832 24350.2 48700.4 +Grid : Message : 11.728250 s : 24 8 10616832 37390.9 74781.8 +Grid : Message : 12.497258 s : 28 8 16859136 36792.2 73584.5 +Grid : Message : 14.585387 s : 28 8 16859136 24222.2 48444.3 +Grid : Message : 16.664783 s : 28 8 16859136 24323.4 48646.8 +Grid : Message : 17.955238 s : 28 8 16859136 39194.7 78389.4 +Grid : Message : 20.136479 s : 32 8 25165824 35718.3 71436.5 +Grid : Message : 23.241958 s : 32 8 25165824 24311.4 48622.9 +Grid : Message : 26.344810 s : 32 8 25165824 24331.9 48663.7 +Grid : Message : 28.384420 s : 32 8 25165824 37016.3 74032.7 +Grid : Message : 28.388879 s : ==================================================================================================== +Grid : Message : 28.388894 s : = Benchmarking sequential halo exchange from GPU memory +Grid : Message : 28.388909 s : ==================================================================================================== +Grid : Message : 28.388924 s : L Ls bytes MB/s uni MB/s bidi +Grid : Message : 28.553993 s : 8 8 393216 8272.4 16544.7 +Grid : Message : 28.679592 s : 8 8 393216 9395.4 18790.8 +Grid : Message : 28.811112 s : 8 8 393216 8971.0 17942.0 +Grid : Message : 28.843770 s : 8 8 393216 36145.6 72291.2 +Grid : Message : 28.981754 s : 12 8 1327104 49591.6 99183.2 +Grid : Message : 29.299764 s : 12 8 1327104 12520.8 25041.7 +Grid : Message : 29.620288 s : 12 8 1327104 12422.2 24844.4 +Grid : Message : 29.657645 s : 12 8 1327104 106637.5 213275.1 +Grid : Message : 29.952933 s : 16 8 3145728 43939.2 87878.5 +Grid : Message : 30.585411 s : 16 8 3145728 14922.1 29844.2 +Grid : Message : 31.219781 s : 16 8 3145728 14877.2 29754.4 +Grid : Message : 31.285017 s : 16 8 3145728 144724.3 289448.7 +Grid : Message : 31.706443 s : 20 8 6144000 54676.2 109352.4 +Grid : Message : 32.739205 s : 20 8 6144000 17848.0 35696.1 +Grid : Message : 33.771852 s : 20 8 6144000 17849.9 35699.7 +Grid : Message : 33.871981 s : 20 8 6144000 184141.4 368282.8 +Grid : Message : 34.536808 s : 24 8 10616832 55784.3 111568.6 +Grid : Message : 36.275648 s : 24 8 10616832 18317.6 36635.3 +Grid : Message : 37.997181 s : 24 8 10616832 18501.7 37003.4 +Grid : Message : 38.140442 s : 24 8 10616832 222383.9 444767.9 +Grid : Message : 39.177222 s : 28 8 16859136 56609.7 113219.4 +Grid : Message : 41.874755 s : 28 8 16859136 18749.9 37499.8 +Grid : Message : 44.529381 s : 28 8 16859136 19052.9 38105.8 +Grid : Message : 44.742192 s : 28 8 16859136 237717.1 475434.2 +Grid : Message : 46.184000 s : 32 8 25165824 57091.2 114182.4 +Grid : Message : 50.734740 s : 32 8 25165824 19411.0 38821.9 +Grid : Message : 53.931228 s : 32 8 25165824 19570.6 39141.2 +Grid : Message : 54.238467 s : 32 8 25165824 245765.6 491531.2 +Grid : Message : 54.268664 s : ==================================================================================================== +Grid : Message : 54.268680 s : = All done; Bye Bye +Grid : Message : 54.268691 s : ==================================================================================================== diff --git a/systems/Summit/dwf.24.4node b/systems/Summit/dwf.24.4node new file mode 100644 index 00000000..212e471c --- /dev/null +++ b/systems/Summit/dwf.24.4node @@ -0,0 +1,206 @@ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: Tesla V100-SXM2-16GB +AcceleratorCudaInit[0]: totalGlobalMem: 16911433728 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 4 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: rank 0 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 0 device 0 bus id: 0004:04:00.0 +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 24 +SharedMemoryMpi: Node communicator of size 6 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x200080000000 for comms buffers +AcceleratorCudaInit: rank 3 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 3 device 3 bus id: 0035:03:00.0 +AcceleratorCudaInit: rank 5 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 5 device 5 bus id: 0035:05:00.0 +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +AcceleratorCudaInit: rank 4 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 4 device 4 bus id: 0035:04:00.0 +AcceleratorCudaInit: rank 1 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 1 device 1 bus id: 0004:05:00.0 +AcceleratorCudaInit: rank 2 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 2 device 2 bus id: 0004:06:00.0 +the Free Software Foundation; either version 2 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 General Public License for more details. +Current Grid git commit hash=7cb1ff7395a5833ded6526c43891bd07a0436290: (HEAD -> develop, origin/develop, origin/HEAD) clean + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 8388608000 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 8 LARGE 2 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 1.731905 s : Grid Layout +Grid : Message : 1.731915 s : Global lattice size : 48 48 48 72 +Grid : Message : 1.731928 s : OpenMP threads : 6 +Grid : Message : 1.731938 s : MPI tasks : 2 2 2 3 +AcceleratorCudaInit: rank 9 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 23 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 22 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 21 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 18 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 6 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 7 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 10 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 8 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 11 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 20 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 19 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 13 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 12 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 14 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 16 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 15 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 17 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +Grid : Message : 2.683494 s : Making s innermost grids +Grid : Message : 2.780034 s : Initialising 4d RNG +Grid : Message : 2.833099 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 2.833121 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 2.916841 s : Initialising 5d RNG +Grid : Message : 3.762880 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 3.762902 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 5.264345 s : Initialised RNGs +Grid : Message : 6.489904 s : Drawing gauge field +Grid : Message : 6.729262 s : Random gauge initialised +Grid : Message : 7.781273 s : Setting up Cshift based reference +Grid : Message : 8.725313 s : ***************************************************************** +Grid : Message : 8.725332 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 8.725342 s : ***************************************************************** +Grid : Message : 8.725352 s : ***************************************************************** +Grid : Message : 8.725362 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 8.725372 s : * Vectorising space-time by 4 +Grid : Message : 8.725383 s : * VComplexF size is 32 B +Grid : Message : 8.725395 s : * SINGLE precision +Grid : Message : 8.725405 s : * Using Overlapped Comms/Compute +Grid : Message : 8.725415 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 8.725425 s : ***************************************************************** +Grid : Message : 9.465229 s : Called warmup +Grid : Message : 58.646066 s : Called Dw 3000 times in 4.91764e+07 us +Grid : Message : 58.646121 s : mflop/s = 1.02592e+07 +Grid : Message : 58.646134 s : mflop/s per rank = 427468 +Grid : Message : 58.646145 s : mflop/s per node = 2.56481e+06 +Grid : Message : 58.646156 s : RF GiB/s (base 2) = 20846.5 +Grid : Message : 58.646166 s : mem GiB/s (base 2) = 13029.1 +Grid : Message : 58.648008 s : norm diff 1.04778e-13 +Grid : Message : 58.734885 s : #### Dhop calls report +Grid : Message : 58.734897 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 58.734909 s : WilsonFermion5D TotalTime /Calls : 8217.71 us +Grid : Message : 58.734922 s : WilsonFermion5D CommTime /Calls : 7109.5 us +Grid : Message : 58.734933 s : WilsonFermion5D FaceTime /Calls : 446.623 us +Grid : Message : 58.734943 s : WilsonFermion5D ComputeTime1/Calls : 18.0558 us +Grid : Message : 58.734953 s : WilsonFermion5D ComputeTime2/Calls : 731.097 us +Grid : Message : 58.734979 s : Average mflops/s per call : 4.8157e+09 +Grid : Message : 58.734989 s : Average mflops/s per call per rank : 2.00654e+08 +Grid : Message : 58.734999 s : Average mflops/s per call per node : 1.20393e+09 +Grid : Message : 58.735008 s : Average mflops/s per call (full) : 1.04183e+07 +Grid : Message : 58.735017 s : Average mflops/s per call per rank (full): 434094 +Grid : Message : 58.735026 s : Average mflops/s per call per node (full): 2.60456e+06 +Grid : Message : 58.735035 s : WilsonFermion5D Stencil +Grid : Message : 58.735043 s : WilsonFermion5D StencilEven +Grid : Message : 58.735051 s : WilsonFermion5D StencilOdd +Grid : Message : 58.735059 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 58.735067 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 58.735075 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 64.934380 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 64.934740 s : Called DwDag +Grid : Message : 64.934870 s : norm dag result 12.0422 +Grid : Message : 64.120756 s : norm dag ref 12.0422 +Grid : Message : 64.149389 s : norm dag diff 7.6644e-14 +Grid : Message : 64.317786 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 64.465331 s : src_e0.499995 +Grid : Message : 64.524653 s : src_o0.500005 +Grid : Message : 64.558706 s : ********************************************************* +Grid : Message : 64.558717 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 64.558727 s : * Vectorising space-time by 4 +Grid : Message : 64.558737 s : * SINGLE precision +Grid : Message : 64.558745 s : * Using Overlapped Comms/Compute +Grid : Message : 64.558753 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 64.558761 s : ********************************************************* +Grid : Message : 92.702145 s : Deo mflop/s = 8.97692e+06 +Grid : Message : 92.702185 s : Deo mflop/s per rank 374038 +Grid : Message : 92.702198 s : Deo mflop/s per node 2.24423e+06 +Grid : Message : 92.702209 s : #### Dhop calls report +Grid : Message : 92.702223 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 92.702240 s : WilsonFermion5D TotalTime /Calls : 9377.88 us +Grid : Message : 92.702257 s : WilsonFermion5D CommTime /Calls : 8221.84 us +Grid : Message : 92.702277 s : WilsonFermion5D FaceTime /Calls : 543.548 us +Grid : Message : 92.702301 s : WilsonFermion5D ComputeTime1/Calls : 20.936 us +Grid : Message : 92.702322 s : WilsonFermion5D ComputeTime2/Calls : 732.33 us +Grid : Message : 92.702376 s : Average mflops/s per call : 4.13001e+09 +Grid : Message : 92.702387 s : Average mflops/s per call per rank : 1.72084e+08 +Grid : Message : 92.702397 s : Average mflops/s per call per node : 1.0325e+09 +Grid : Message : 92.702407 s : Average mflops/s per call (full) : 9.12937e+06 +Grid : Message : 92.702416 s : Average mflops/s per call per rank (full): 380391 +Grid : Message : 92.702426 s : Average mflops/s per call per node (full): 2.28234e+06 +Grid : Message : 92.702435 s : WilsonFermion5D Stencil +Grid : Message : 92.702443 s : WilsonFermion5D StencilEven +Grid : Message : 92.702451 s : WilsonFermion5D StencilOdd +Grid : Message : 92.702459 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 92.702467 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 92.702475 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 92.772983 s : r_e6.02121 +Grid : Message : 92.786384 s : r_o6.02102 +Grid : Message : 92.799622 s : res12.0422 +Grid : Message : 93.860500 s : norm diff 0 +Grid : Message : 93.162026 s : norm diff even 0 +Grid : Message : 93.197529 s : norm diff odd 0 diff --git a/systems/Summit/dwf.32.4node b/systems/Summit/dwf.32.4node new file mode 100644 index 00000000..eed54f2d --- /dev/null +++ b/systems/Summit/dwf.32.4node @@ -0,0 +1,206 @@ +OPENMPI detected +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device Number : 0 +AcceleratorCudaInit[0]: ======================== +AcceleratorCudaInit[0]: Device identifier: Tesla V100-SXM2-16GB +AcceleratorCudaInit[0]: totalGlobalMem: 16911433728 +AcceleratorCudaInit[0]: managedMemory: 1 +AcceleratorCudaInit[0]: isMultiGpuBoard: 0 +AcceleratorCudaInit[0]: warpSize: 32 +AcceleratorCudaInit[0]: pciBusID: 4 +AcceleratorCudaInit[0]: pciDeviceID: 0 +AcceleratorCudaInit[0]: maxGridSize (2147483647,65535,65535) +AcceleratorCudaInit: rank 0 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 0 device 0 bus id: 0004:04:00.0 +AcceleratorCudaInit: ================================================ +SharedMemoryMpi: World communicator of size 24 +SharedMemoryMpi: Node communicator of size 6 +0SharedMemoryMpi: SharedMemoryMPI.cc acceleratorAllocDevice 2147483648bytes at 0x200080000000 for comms buffers +Setting up IPC + +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|_ | | | | | | | | | | | | _|__ +__|_ _|__ +__|_ GGGG RRRR III DDDD _|__ +__|_ G R R I D D _|__ +__|_ G R R I D D _|__ +__|_ G GG RRRR I D D _|__ +__|_ G G R R I D D _|__ +__|_ GGGG R R III DDDD _|__ +__|_ _|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ +__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ + | | | | | | | | | | | | | | + + +Copyright (C) 2015 Peter Boyle, Azusa Yamaguchi, Guido Cossu, Antonin Portelli and other authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 +AcceleratorCudaInit: rank 2 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 2 device 2 bus id: 0004:06:00.0 +AcceleratorCudaInit: rank 1 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 1 device 1 bus id: 0004:05:00.0 +AcceleratorCudaInit: rank 4 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 4 device 4 bus id: 0035:04:00.0 +AcceleratorCudaInit: rank 3 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 3 device 3 bus id: 0035:03:00.0 +AcceleratorCudaInit: rank 5 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +local rank 5 device 5 bus id: 0035:05:00.0 +GNU General Public License for more details. +Current Grid git commit hash=7cb1ff7395a5833ded6526c43891bd07a0436290: (HEAD -> develop, origin/develop, origin/HEAD) clean + +Grid : Message : ================================================ +Grid : Message : MPI is initialised and logging filters activated +Grid : Message : ================================================ +Grid : Message : Requested 2147483648 byte stencil comms buffers +Grid : Message : MemoryManager Cache 8388608000 bytes +Grid : Message : MemoryManager::Init() setting up +Grid : Message : MemoryManager::Init() cache pool for recent allocations: SMALL 8 LARGE 2 +Grid : Message : MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory +Grid : Message : MemoryManager::Init() Using cudaMalloc +Grid : Message : 1.544984 s : Grid Layout +Grid : Message : 1.544992 s : Global lattice size : 64 64 64 96 +Grid : Message : 1.545003 s : OpenMP threads : 6 +Grid : Message : 1.545011 s : MPI tasks : 2 2 2 3 +AcceleratorCudaInit: rank 8 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 6 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 11 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 16 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 17 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 13 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 12 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 21 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 23 setting device to node rank 5 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 22 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 19 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 18 setting device to node rank 0 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 7 setting device to node rank 1 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 10 setting device to node rank 4 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 9 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 14 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 15 setting device to node rank 3 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +AcceleratorCudaInit: rank 20 setting device to node rank 2 +AcceleratorCudaInit: Configure options --enable-setdevice=yes +Grid : Message : 2.994920 s : Making s innermost grids +Grid : Message : 2.232502 s : Initialising 4d RNG +Grid : Message : 2.397047 s : Intialising parallel RNG with unique string 'The 4D RNG' +Grid : Message : 2.397069 s : Seed SHA256: 49db4542db694e3b1a74bf2592a8c1b83bfebbe18401693c2609a4c3af1 +Grid : Message : 2.653140 s : Initialising 5d RNG +Grid : Message : 5.285347 s : Intialising parallel RNG with unique string 'The 5D RNG' +Grid : Message : 5.285369 s : Seed SHA256: b6316f2fac44ce14111f93e0296389330b077bfd0a7b359f781c58589f8a +Grid : Message : 9.994738 s : Initialised RNGs +Grid : Message : 13.153426 s : Drawing gauge field +Grid : Message : 13.825697 s : Random gauge initialised +Grid : Message : 18.537657 s : Setting up Cshift based reference +Grid : Message : 22.296755 s : ***************************************************************** +Grid : Message : 22.296781 s : * Kernel options --dslash-generic, --dslash-unroll, --dslash-asm +Grid : Message : 22.296791 s : ***************************************************************** +Grid : Message : 22.296800 s : ***************************************************************** +Grid : Message : 22.296809 s : * Benchmarking DomainWallFermionR::Dhop +Grid : Message : 22.296818 s : * Vectorising space-time by 4 +Grid : Message : 22.296828 s : * VComplexF size is 32 B +Grid : Message : 22.296838 s : * SINGLE precision +Grid : Message : 22.296847 s : * Using Overlapped Comms/Compute +Grid : Message : 22.296855 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 22.296863 s : ***************************************************************** +Grid : Message : 24.746452 s : Called warmup +Grid : Message : 137.525756 s : Called Dw 3000 times in 1.12779e+08 us +Grid : Message : 137.525818 s : mflop/s = 1.41383e+07 +Grid : Message : 137.525831 s : mflop/s per rank = 589097 +Grid : Message : 137.525843 s : mflop/s per node = 3.53458e+06 +Grid : Message : 137.525854 s : RF GiB/s (base 2) = 28728.7 +Grid : Message : 137.525864 s : mem GiB/s (base 2) = 17955.5 +Grid : Message : 137.693645 s : norm diff 1.04885e-13 +Grid : Message : 137.965585 s : #### Dhop calls report +Grid : Message : 137.965598 s : WilsonFermion5D Number of DhopEO Calls : 6002 +Grid : Message : 137.965612 s : WilsonFermion5D TotalTime /Calls : 18899.7 us +Grid : Message : 137.965624 s : WilsonFermion5D CommTime /Calls : 16041.4 us +Grid : Message : 137.965634 s : WilsonFermion5D FaceTime /Calls : 859.705 us +Grid : Message : 137.965644 s : WilsonFermion5D ComputeTime1/Calls : 70.5881 us +Grid : Message : 137.965654 s : WilsonFermion5D ComputeTime2/Calls : 2094.8 us +Grid : Message : 137.965682 s : Average mflops/s per call : 3.87638e+09 +Grid : Message : 137.965692 s : Average mflops/s per call per rank : 1.61516e+08 +Grid : Message : 137.965702 s : Average mflops/s per call per node : 9.69095e+08 +Grid : Message : 137.965712 s : Average mflops/s per call (full) : 1.43168e+07 +Grid : Message : 137.965721 s : Average mflops/s per call per rank (full): 596533 +Grid : Message : 137.965730 s : Average mflops/s per call per node (full): 3.5792e+06 +Grid : Message : 137.965740 s : WilsonFermion5D Stencil +Grid : Message : 137.965748 s : WilsonFermion5D StencilEven +Grid : Message : 137.965756 s : WilsonFermion5D StencilOdd +Grid : Message : 137.965764 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 137.965772 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 137.965780 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 156.554605 s : Compare to naive wilson implementation Dag to verify correctness +Grid : Message : 156.554632 s : Called DwDag +Grid : Message : 156.554642 s : norm dag result 12.0421 +Grid : Message : 156.639265 s : norm dag ref 12.0421 +Grid : Message : 156.888281 s : norm dag diff 7.62057e-14 +Grid : Message : 157.609797 s : Calling Deo and Doe and //assert Deo+Doe == Dunprec +Grid : Message : 158.208630 s : src_e0.499996 +Grid : Message : 158.162447 s : src_o0.500004 +Grid : Message : 158.267780 s : ********************************************************* +Grid : Message : 158.267791 s : * Benchmarking DomainWallFermionF::DhopEO +Grid : Message : 158.267801 s : * Vectorising space-time by 4 +Grid : Message : 158.267811 s : * SINGLE precision +Grid : Message : 158.267820 s : * Using Overlapped Comms/Compute +Grid : Message : 158.267828 s : * Using GENERIC Nc WilsonKernels +Grid : Message : 158.267836 s : ********************************************************* +Grid : Message : 216.487829 s : Deo mflop/s = 1.37283e+07 +Grid : Message : 216.487869 s : Deo mflop/s per rank 572011 +Grid : Message : 216.487881 s : Deo mflop/s per node 3.43206e+06 +Grid : Message : 216.487893 s : #### Dhop calls report +Grid : Message : 216.487903 s : WilsonFermion5D Number of DhopEO Calls : 3001 +Grid : Message : 216.487913 s : WilsonFermion5D TotalTime /Calls : 19399.6 us +Grid : Message : 216.487923 s : WilsonFermion5D CommTime /Calls : 16475.4 us +Grid : Message : 216.487933 s : WilsonFermion5D FaceTime /Calls : 972.393 us +Grid : Message : 216.487943 s : WilsonFermion5D ComputeTime1/Calls : 49.8474 us +Grid : Message : 216.487953 s : WilsonFermion5D ComputeTime2/Calls : 2089.93 us +Grid : Message : 216.488001 s : Average mflops/s per call : 5.39682e+09 +Grid : Message : 216.488011 s : Average mflops/s per call per rank : 2.24867e+08 +Grid : Message : 216.488020 s : Average mflops/s per call per node : 1.3492e+09 +Grid : Message : 216.488030 s : Average mflops/s per call (full) : 1.39479e+07 +Grid : Message : 216.488039 s : Average mflops/s per call per rank (full): 581162 +Grid : Message : 216.488048 s : Average mflops/s per call per node (full): 3.48697e+06 +Grid : Message : 216.488057 s : WilsonFermion5D Stencil +Grid : Message : 216.488065 s : WilsonFermion5D StencilEven +Grid : Message : 216.488073 s : WilsonFermion5D StencilOdd +Grid : Message : 216.488081 s : WilsonFermion5D Stencil Reporti() +Grid : Message : 216.488089 s : WilsonFermion5D StencilEven Reporti() +Grid : Message : 216.488097 s : WilsonFermion5D StencilOdd Reporti() +Grid : Message : 217.384495 s : r_e6.02113 +Grid : Message : 217.426121 s : r_o6.02096 +Grid : Message : 217.472636 s : res12.0421 +Grid : Message : 218.200068 s : norm diff 0 +Grid : Message : 218.645673 s : norm diff even 0 +Grid : Message : 218.816561 s : norm diff odd 0 From a8eda8f6da2c33fb06ef95db503fdd7780653ffc Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 5 Oct 2021 21:22:10 -0400 Subject: [PATCH 245/399] Summit scripts --- systems/Summit/dwf16.lsf | 14 +++++++------- systems/Summit/dwf4.lsf | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/systems/Summit/dwf16.lsf b/systems/Summit/dwf16.lsf index 16f4b82d..ef8c21a5 100644 --- a/systems/Summit/dwf16.lsf +++ b/systems/Summit/dwf16.lsf @@ -1,7 +1,7 @@ #!/bin/bash #BSUB -P LGT104 #BSUB -W 2:00 -#BSUB -nnodes 4 +#BSUB -nnodes 16 #BSUB -J DWF export OMP_NUM_THREADS=6 @@ -9,14 +9,14 @@ export PAMI_IBV_ADAPTER_AFFINITY=1 export PAMI_ENABLE_STRIPING=1 export OPT="--comms-concurrent --comms-overlap " -APP="./benchmarks/Benchmark_comms_host_device --mpi 2.2.2.3 " -jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP +APP="./benchmarks/Benchmark_comms_host_device --mpi 4.4.4.3 " +jsrun --nrs 16 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP > comms.16node.log -APP="./benchmarks/Benchmark_dwf_fp32 --grid 48.48.48.72 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " -jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP +APP="./benchmarks/Benchmark_dwf_fp32 --grid 96.96.96.72 --mpi 4.4.4.3 --shm 2048 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 16 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP > dwf.16node.24.log -APP="./benchmarks/Benchmark_dwf_fp32 --grid 64.64.64.96 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " -jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP +APP="./benchmarks/Benchmark_dwf_fp32 --grid 128.128.128.96 --mpi 4.4.4.3 --shm 2048 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 16 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP > dwf.16node.32.log diff --git a/systems/Summit/dwf4.lsf b/systems/Summit/dwf4.lsf index fcd80bcb..7d940338 100644 --- a/systems/Summit/dwf4.lsf +++ b/systems/Summit/dwf4.lsf @@ -10,13 +10,13 @@ export PAMI_ENABLE_STRIPING=1 export OPT="--comms-concurrent --comms-overlap " #export GRID_ALLOC_NCACHE_LARGE=1 export APP="./benchmarks/Benchmark_comms_host_device --mpi 2.2.2.3 " -jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP > comms.4node -APP="./benchmarks/Benchmark_dwf_fp32 --grid 48.48.48.72 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " -jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP +APP="./benchmarks/Benchmark_dwf_fp32 --grid 48.48.48.72 --mpi 2.2.2.3 --shm 2048 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP > dwf.24.4node -APP="./benchmarks/Benchmark_dwf_fp32 --grid 64.64.64.96 --mpi 2.2.2.3 --shm 1024 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " -jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP +APP="./benchmarks/Benchmark_dwf_fp32 --grid 64.64.64.96 --mpi 2.2.2.3 --shm 2048 --shm-force-mpi 1 --device-mem 8000 --shm-force-mpi 1 $OPT " +jsrun --nrs 4 -a6 -g6 -c42 -dpacked -b packed:7 --latency_priority gpu-cpu --smpiargs=-gpu $APP > dwf.32.4node From cda915a3452fe0b8c0613ae3990ce2fc195c12e7 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 7 Oct 2021 20:29:09 +0100 Subject: [PATCH 246/399] Better options --- systems/Tursa/config-command | 2 +- systems/Tursa/sourceme.sh | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/systems/Tursa/config-command b/systems/Tursa/config-command index b47c34e5..58174c83 100644 --- a/systems/Tursa/config-command +++ b/systems/Tursa/config-command @@ -5,7 +5,7 @@ --enable-gen-simd-width=64 \ --enable-accelerator=cuda \ --with-lime=/mnt/lustre/tursafs1/home/tc002/tc002/dc-boyl1/spack/spack/opt/spack/linux-rhel8-zen/gcc-8.4.1/c-lime-2-3-9-e6wxqrid6rqmd45z7n32dxkvkykpvyez \ - --disable-accelerator-cshift \ + --enable-accelerator-cshift \ --disable-unified \ CXX=nvcc \ LDFLAGS="-cudart shared " \ diff --git a/systems/Tursa/sourceme.sh b/systems/Tursa/sourceme.sh index 6286750d..38214052 100644 --- a/systems/Tursa/sourceme.sh +++ b/systems/Tursa/sourceme.sh @@ -1,2 +1,5 @@ -spack load c-lime module load cuda/11.4.1 openmpi/4.1.1 ucx/1.10.1 +module load cuda/11.4.1 openmpi/4.1.1 ucx/1.10.1 +export PREFIX=/home/tc002/tc002/shared/env/prefix/ +export LD_LIBRARY_PATH=$PREFIX/lib/:$LD_LIBRARY_PATH +unset SBATCH_EXPORT From 16c2a99965d6b3dc14d4c947e03dbec5e7df0195 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 11 Oct 2021 13:31:26 -0700 Subject: [PATCH 247/399] Overlap cudamemcpy - didn't set up stream right --- Grid/communicator/Communicator_mpi3.cc | 2 +- Grid/threads/Accelerator.cc | 2 +- Grid/threads/Accelerator.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index 01335b41..305a3a9b 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -389,7 +389,6 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques void *shm = (void *) this->ShmBufferTranslate(dest,recv); assert(shm!=NULL); acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); - acceleratorCopySynchronise(); // MPI prob slower } if ( CommunicatorPolicy == CommunicatorPolicySequential ) { @@ -405,6 +404,7 @@ void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsReque if (nreq==0) return; std::vector<MPI_Status> status(nreq); + acceleratorCopySynchronise(); int ierr = MPI_Waitall(nreq,&list[0],&status[0]); assert(ierr==0); list.resize(0); diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 14e07248..9f27f12b 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -95,7 +95,7 @@ void acceleratorInit(void) #endif cudaSetDevice(device); - + cudaStreamCreate(&copyStream); const int len=64; char busid[len]; if( rank == world_rank ) { diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 2c08b76b..cec0600f 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -95,6 +95,7 @@ void acceleratorInit(void); ////////////////////////////////////////////// #ifdef GRID_CUDA + #include <cuda.h> #ifdef __CUDA_ARCH__ From 1f9688417ae1fc1fcb680d37d05983e6dccbfc9d Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Wed, 13 Oct 2021 20:45:46 +0100 Subject: [PATCH 248/399] Error message added when attempting to sum object which is too large for the shared memory --- Grid/lattice/Lattice_reduction_gpu.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Grid/lattice/Lattice_reduction_gpu.h b/Grid/lattice/Lattice_reduction_gpu.h index d8a47ae1..c2875052 100644 --- a/Grid/lattice/Lattice_reduction_gpu.h +++ b/Grid/lattice/Lattice_reduction_gpu.h @@ -42,7 +42,6 @@ void getNumBlocksAndThreads(const Iterator n, const size_t sizeofsobj, Iterator std::cout << GridLogDebug << "\twarpSize = " << warpSize << std::endl; std::cout << GridLogDebug << "\tsharedMemPerBlock = " << sharedMemPerBlock << std::endl; std::cout << GridLogDebug << "\tmaxThreadsPerBlock = " << maxThreadsPerBlock << std::endl; - std::cout << GridLogDebug << "\tmaxThreadsPerBlock = " << warpSize << std::endl; std::cout << GridLogDebug << "\tmultiProcessorCount = " << multiProcessorCount << std::endl; if (warpSize != WARP_SIZE) { @@ -52,6 +51,10 @@ void getNumBlocksAndThreads(const Iterator n, const size_t sizeofsobj, Iterator // let the number of threads in a block be a multiple of 2, starting from warpSize threads = warpSize; + if ( threads*sizeofsobj > sharedMemPerBlock ) { + std::cout << GridLogError << "The object is too large for the shared memory." << std::endl; + exit(EXIT_FAILURE); + } while( 2*threads*sizeofsobj < sharedMemPerBlock && 2*threads <= maxThreadsPerBlock ) threads *= 2; // keep all the streaming multiprocessors busy blocks = nextPow2(multiProcessorCount); From cfe9e870d39101d7214a093a40e0f9907eea8f23 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Fri, 15 Oct 2021 20:46:44 +0100 Subject: [PATCH 249/399] Stream --- Grid/threads/Accelerator.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 14e07248..9f27f12b 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -95,7 +95,7 @@ void acceleratorInit(void) #endif cudaSetDevice(device); - + cudaStreamCreate(&copyStream); const int len=64; char busid[len]; if( rank == world_rank ) { From 749b8022a46606bc68a0bddba7ba7f9485f209f8 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Fri, 15 Oct 2021 20:47:18 +0100 Subject: [PATCH 250/399] Linear operator and SparseMatrix virtual destructors --- Grid/algorithms/LinearOperator.h | 1 + Grid/algorithms/SparseMatrix.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Grid/algorithms/LinearOperator.h b/Grid/algorithms/LinearOperator.h index 815226d2..e1077850 100644 --- a/Grid/algorithms/LinearOperator.h +++ b/Grid/algorithms/LinearOperator.h @@ -52,6 +52,7 @@ public: virtual void AdjOp (const Field &in, Field &out) = 0; // Abstract base virtual void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)=0; virtual void HermOp(const Field &in, Field &out)=0; + virtual ~LinearOperatorBase(){}; }; diff --git a/Grid/algorithms/SparseMatrix.h b/Grid/algorithms/SparseMatrix.h index 8a265b3f..b4bab20c 100644 --- a/Grid/algorithms/SparseMatrix.h +++ b/Grid/algorithms/SparseMatrix.h @@ -48,6 +48,7 @@ public: virtual void Mdiag (const Field &in, Field &out)=0; virtual void Mdir (const Field &in, Field &out,int dir, int disp)=0; virtual void MdirAll (const Field &in, std::vector<Field> &out)=0; + virtual ~SparseMatrixBase() {}; }; ///////////////////////////////////////////////////////////////////////////////////////////// @@ -72,7 +73,7 @@ public: virtual void MeooeDag (const Field &in, Field &out)=0; virtual void MooeeDag (const Field &in, Field &out)=0; virtual void MooeeInvDag (const Field &in, Field &out)=0; - + virtual ~CheckerBoardedSparseMatrixBase() {}; }; NAMESPACE_END(Grid); From f824d99059b00a4b4c39529eac46b4542e174b79 Mon Sep 17 00:00:00 2001 From: Ed Bennett <e.j.bennett@swansea.ac.uk> Date: Mon, 18 Oct 2021 09:41:01 +0100 Subject: [PATCH 251/399] update documentation for GenericHMCRunner --- Grid/qcd/hmc/UsingHMC.md | 90 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/Grid/qcd/hmc/UsingHMC.md b/Grid/qcd/hmc/UsingHMC.md index da3c3c00..99f93121 100644 --- a/Grid/qcd/hmc/UsingHMC.md +++ b/Grid/qcd/hmc/UsingHMC.md @@ -1,61 +1,63 @@ -Using HMC in Grid version 0.5.1 +# Using HMC in Grid -These are the instructions to use the Generalised HMC on Grid version 0.5.1. -Disclaimer: GRID is still under active development so any information here can be changed in future releases. +These are the instructions to use the Generalised HMC on Grid as of commit `749b802`. +Disclaimer: Grid is still under active development so any information here can be changed in future releases. -Command line options -=================== -(relevant file GenericHMCrunner.h) +## Command line options + +(relevant file `GenericHMCrunner.h`) The initial configuration can be changed at the command line using ---StartType <your choice> -valid choices, one among these -HotStart, ColdStart, TepidStart, CheckpointStart -default: HotStart +`--StartingType STARTING_TYPE`, where `STARTING_TYPE` is one of +`HotStart`, `ColdStart`, `TepidStart`, and `CheckpointStart`. +Default: `--StartingType HotStart` -example -./My_hmc_exec --StartType HotStart +Example: +``` +./My_hmc_exec --StartingType HotStart +``` -The CheckpointStart option uses the prefix for the configurations and rng seed files defined in your executable and the initial configuration is specified by ---StartTrajectory <integer> -default: 0 +The `CheckpointStart` option uses the prefix for the configurations and rng seed files defined in your executable and the initial configuration is specified by +`--StartingTrajectory STARTING_TRAJECTORY`, where `STARTING_TRAJECTORY` is an integer. +Default: `--StartingTrajectory 0` The number of trajectories for a specific run are specified at command line by ---Trajectories <integer> -default: 1 +`--Trajectories TRAJECTORIES`, where `TRAJECTORIES` is an integer. +Default: `--Trajectories 1` The number of thermalization steps (i.e. steps when the Metropolis acceptance check is turned off) is specified by ---Thermalizations <integer> -default: 10 - +`--Thermalizations THERMALIZATIONS`, where `THERMALIZATIONS` is an integer. +Default: `--Thermalizations 10` Any other parameter is defined in the source for the executable. -HMC controls -=========== +## HMC controls The lines +``` std::vector<int> SerSeed({1, 2, 3, 4, 5}); std::vector<int> ParSeed({6, 7, 8, 9, 10}); +``` define the seeds for the serial and the parallel RNG. The line +``` TheHMC.MDparameters.set(20, 1.0);// MDsteps, traj length +``` declares the number of molecular dynamics steps and the total trajectory length. -Actions -====== +## Actions -Action names are defined in the file -lib/qcd/Actions.h +Action names are defined in the directory `Grid/qcd/action`. -Gauge actions list: +Gauge actions list (from `Grid/qcd/action/gauge/Gauge.h`): +``` WilsonGaugeActionR; WilsonGaugeActionF; WilsonGaugeActionD; @@ -68,8 +70,9 @@ IwasakiGaugeActionD; SymanzikGaugeActionR; SymanzikGaugeActionF; SymanzikGaugeActionD; +``` - +``` ConjugateWilsonGaugeActionR; ConjugateWilsonGaugeActionF; ConjugateWilsonGaugeActionD; @@ -82,26 +85,23 @@ ConjugateIwasakiGaugeActionD; ConjugateSymanzikGaugeActionR; ConjugateSymanzikGaugeActionF; ConjugateSymanzikGaugeActionD; +``` +Each of these action accepts one single parameter at creation time (beta). +Example for creating a Symanzik action with beta=4.0 +``` + SymanzikGaugeActionR(4.0) +``` + +Scalar actions list (from `Grid/qcd/action/scalar/Scalar.h`): + +``` ScalarActionR; ScalarActionF; ScalarActionD; +``` - -each of these action accept one single parameter at creation time (beta). -Example for creating a Symanzik action with beta=4.0 - - SymanzikGaugeActionR(4.0) - -The suffixes R,F,D in the action names refer to the Real -(the precision is defined at compile time by the --enable-precision flag in the configure), -Float and Double, that force the precision of the action to be 32, 64 bit respectively. - - - - - - - - +The suffixes `R`, `F`, `D` in the action names refer to the `Real` +(the precision is defined at compile time by the `--enable-precision` flag in the configure), +`Float` and `Double`, that force the precision of the action to be 32, 64 bit respectively. From ba7e371b9097e578b8c10c041c4a75c808241580 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 21 Oct 2021 19:56:52 +0100 Subject: [PATCH 252/399] Warning free compile on Tursa. Hopefully got all reqd virtual dtors --- .../iterative/ConjugateGradientMixedPrec.h | 3 +- Grid/algorithms/iterative/Deflation.h | 5 + Grid/allocator/MemoryManager.cc | 1 - .../WilsonKernelsHandImplementation.h | 218 +++++++++--------- Grid/stencil/Stencil.h | 4 +- benchmarks/Benchmark_IO.cc | 6 +- benchmarks/Benchmark_ITT.cc | 4 +- benchmarks/Benchmark_comms_host_device.cc | 2 +- benchmarks/Benchmark_memory_bandwidth.cc | 6 +- examples/Example_wall_wall_spectrum.cc | 95 +++++--- systems/Tursa/sourceme.sh | 5 +- 11 files changed, 191 insertions(+), 158 deletions(-) diff --git a/Grid/algorithms/iterative/ConjugateGradientMixedPrec.h b/Grid/algorithms/iterative/ConjugateGradientMixedPrec.h index 08942097..cd2e4374 100644 --- a/Grid/algorithms/iterative/ConjugateGradientMixedPrec.h +++ b/Grid/algorithms/iterative/ConjugateGradientMixedPrec.h @@ -35,7 +35,8 @@ NAMESPACE_BEGIN(Grid); typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0, typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0> class MixedPrecisionConjugateGradient : public LinearFunction<FieldD> { - public: + public: + using LinearFunction<FieldD>::operator(); RealD Tolerance; RealD InnerTolerance; //Initial tolerance for inner CG. Defaults to Tolerance but can be changed Integer MaxInnerIterations; diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index 43fe3e35..2eb28bf9 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -33,16 +33,19 @@ namespace Grid { template<class Field> class ZeroGuesser: public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); virtual void operator()(const Field &src, Field &guess) { guess = Zero(); }; }; template<class Field> class DoNothingGuesser: public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); virtual void operator()(const Field &src, Field &guess) { }; }; template<class Field> class SourceGuesser: public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); virtual void operator()(const Field &src, Field &guess) { guess = src; }; }; @@ -57,6 +60,7 @@ private: const unsigned int N; public: + using LinearFunction<Field>::operator(); DeflatedGuesser(const std::vector<Field> & _evec,const std::vector<RealD> & _eval) : DeflatedGuesser(_evec, _eval, _evec.size()) @@ -87,6 +91,7 @@ private: const std::vector<RealD> &eval_coarse; public: + using LinearFunction<FineField>::operator(); LocalCoherenceDeflatedGuesser(const std::vector<FineField> &_subspace, const std::vector<CoarseField> &_evec_coarse, const std::vector<RealD> &_eval_coarse) diff --git a/Grid/allocator/MemoryManager.cc b/Grid/allocator/MemoryManager.cc index 30be510b..ef02f6aa 100644 --- a/Grid/allocator/MemoryManager.cc +++ b/Grid/allocator/MemoryManager.cc @@ -159,7 +159,6 @@ void MemoryManager::Init(void) char * str; int Nc; - int NcS; str= getenv("GRID_ALLOC_NCACHE_LARGE"); if ( str ) { diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h index 0703b613..771aec85 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h @@ -77,23 +77,23 @@ Author: paboyle <paboyle@ph.ed.ac.uk> #define REGISTER #ifdef GRID_SIMT -#define LOAD_CHIMU(ptype) \ +#define LOAD_CHIMU(Ptype) \ {const SiteSpinor & ref (in[offset]); \ - Chimu_00=coalescedReadPermute<ptype>(ref()(0)(0),perm,lane); \ - Chimu_01=coalescedReadPermute<ptype>(ref()(0)(1),perm,lane); \ - Chimu_02=coalescedReadPermute<ptype>(ref()(0)(2),perm,lane); \ - Chimu_10=coalescedReadPermute<ptype>(ref()(1)(0),perm,lane); \ - Chimu_11=coalescedReadPermute<ptype>(ref()(1)(1),perm,lane); \ - Chimu_12=coalescedReadPermute<ptype>(ref()(1)(2),perm,lane); \ - Chimu_20=coalescedReadPermute<ptype>(ref()(2)(0),perm,lane); \ - Chimu_21=coalescedReadPermute<ptype>(ref()(2)(1),perm,lane); \ - Chimu_22=coalescedReadPermute<ptype>(ref()(2)(2),perm,lane); \ - Chimu_30=coalescedReadPermute<ptype>(ref()(3)(0),perm,lane); \ - Chimu_31=coalescedReadPermute<ptype>(ref()(3)(1),perm,lane); \ - Chimu_32=coalescedReadPermute<ptype>(ref()(3)(2),perm,lane); } + Chimu_00=coalescedReadPermute<Ptype>(ref()(0)(0),perm,lane); \ + Chimu_01=coalescedReadPermute<Ptype>(ref()(0)(1),perm,lane); \ + Chimu_02=coalescedReadPermute<Ptype>(ref()(0)(2),perm,lane); \ + Chimu_10=coalescedReadPermute<Ptype>(ref()(1)(0),perm,lane); \ + Chimu_11=coalescedReadPermute<Ptype>(ref()(1)(1),perm,lane); \ + Chimu_12=coalescedReadPermute<Ptype>(ref()(1)(2),perm,lane); \ + Chimu_20=coalescedReadPermute<Ptype>(ref()(2)(0),perm,lane); \ + Chimu_21=coalescedReadPermute<Ptype>(ref()(2)(1),perm,lane); \ + Chimu_22=coalescedReadPermute<Ptype>(ref()(2)(2),perm,lane); \ + Chimu_30=coalescedReadPermute<Ptype>(ref()(3)(0),perm,lane); \ + Chimu_31=coalescedReadPermute<Ptype>(ref()(3)(1),perm,lane); \ + Chimu_32=coalescedReadPermute<Ptype>(ref()(3)(2),perm,lane); } #define PERMUTE_DIR(dir) ; #else -#define LOAD_CHIMU(ptype) \ +#define LOAD_CHIMU(Ptype) \ {const SiteSpinor & ref (in[offset]); \ Chimu_00=ref()(0)(0);\ Chimu_01=ref()(0)(1);\ @@ -109,12 +109,12 @@ Author: paboyle <paboyle@ph.ed.ac.uk> Chimu_32=ref()(3)(2);} #define PERMUTE_DIR(dir) \ - permute##dir(Chi_00,Chi_00); \ - permute##dir(Chi_01,Chi_01);\ - permute##dir(Chi_02,Chi_02);\ - permute##dir(Chi_10,Chi_10); \ - permute##dir(Chi_11,Chi_11);\ - permute##dir(Chi_12,Chi_12); + permute##dir(Chi_00,Chi_00); \ + permute##dir(Chi_01,Chi_01); \ + permute##dir(Chi_02,Chi_02); \ + permute##dir(Chi_10,Chi_10); \ + permute##dir(Chi_11,Chi_11); \ + permute##dir(Chi_12,Chi_12); #endif @@ -371,88 +371,91 @@ Author: paboyle <paboyle@ph.ed.ac.uk> result_32-= UChi_12; #define HAND_STENCIL_LEGB(PROJ,PERM,DIR,RECON) \ - SE=st.GetEntry(ptype,DIR,ss); \ - offset = SE->_offset; \ - local = SE->_is_local; \ - perm = SE->_permute; \ - if ( local ) { \ - LOAD_CHIMU(PERM); \ - PROJ; \ - if ( perm) { \ - PERMUTE_DIR(PERM); \ - } \ - } else { \ - LOAD_CHI; \ - } \ - acceleratorSynchronise(); \ - MULT_2SPIN(DIR); \ - RECON; + {int ptype; \ + SE=st.GetEntry(ptype,DIR,ss); \ + auto offset = SE->_offset; \ + auto local = SE->_is_local; \ + auto perm = SE->_permute; \ + if ( local ) { \ + LOAD_CHIMU(PERM); \ + PROJ; \ + if ( perm) { \ + PERMUTE_DIR(PERM); \ + } \ + } else { \ + LOAD_CHI; \ + } \ + acceleratorSynchronise(); \ + MULT_2SPIN(DIR); \ + RECON; } -#define HAND_STENCIL_LEG(PROJ,PERM,DIR,RECON) \ - SE=&st_p[DIR+8*ss]; \ - ptype=st_perm[DIR]; \ - offset = SE->_offset; \ - local = SE->_is_local; \ - perm = SE->_permute; \ - if ( local ) { \ - LOAD_CHIMU(PERM); \ - PROJ; \ - if ( perm) { \ - PERMUTE_DIR(PERM); \ - } \ - } else { \ - LOAD_CHI; \ - } \ - acceleratorSynchronise(); \ - MULT_2SPIN(DIR); \ - RECON; +#define HAND_STENCIL_LEG(PROJ,PERM,DIR,RECON) \ + { SE=&st_p[DIR+8*ss]; \ + auto ptype=st_perm[DIR]; \ + auto offset = SE->_offset; \ + auto local = SE->_is_local; \ + auto perm = SE->_permute; \ + if ( local ) { \ + LOAD_CHIMU(PERM); \ + PROJ; \ + if ( perm) { \ + PERMUTE_DIR(PERM); \ + } \ + } else { \ + LOAD_CHI; \ + } \ + acceleratorSynchronise(); \ + MULT_2SPIN(DIR); \ + RECON; } #define HAND_STENCIL_LEGA(PROJ,PERM,DIR,RECON) \ - SE=&st_p[DIR+8*ss]; \ - ptype=st_perm[DIR]; \ - /*SE=st.GetEntry(ptype,DIR,ss);*/ \ - offset = SE->_offset; \ - perm = SE->_permute; \ - LOAD_CHIMU(PERM); \ - PROJ; \ - MULT_2SPIN(DIR); \ - RECON; + { SE=&st_p[DIR+8*ss]; \ + auto ptype=st_perm[DIR]; \ + /*SE=st.GetEntry(ptype,DIR,ss);*/ \ + auto offset = SE->_offset; \ + auto perm = SE->_permute; \ + LOAD_CHIMU(PERM); \ + PROJ; \ + MULT_2SPIN(DIR); \ + RECON; } #define HAND_STENCIL_LEG_INT(PROJ,PERM,DIR,RECON) \ - SE=st.GetEntry(ptype,DIR,ss); \ - offset = SE->_offset; \ - local = SE->_is_local; \ - perm = SE->_permute; \ - if ( local ) { \ - LOAD_CHIMU(PERM); \ - PROJ; \ - if ( perm) { \ - PERMUTE_DIR(PERM); \ - } \ - } else if ( st.same_node[DIR] ) { \ - LOAD_CHI; \ - } \ - acceleratorSynchronise(); \ - if (local || st.same_node[DIR] ) { \ - MULT_2SPIN(DIR); \ - RECON; \ - } \ - acceleratorSynchronise(); + { int ptype; \ + SE=st.GetEntry(ptype,DIR,ss); \ + auto offset = SE->_offset; \ + auto local = SE->_is_local; \ + auto perm = SE->_permute; \ + if ( local ) { \ + LOAD_CHIMU(PERM); \ + PROJ; \ + if ( perm) { \ + PERMUTE_DIR(PERM); \ + } \ + } else if ( st.same_node[DIR] ) { \ + LOAD_CHI; \ + } \ + acceleratorSynchronise(); \ + if (local || st.same_node[DIR] ) { \ + MULT_2SPIN(DIR); \ + RECON; \ + } \ + acceleratorSynchronise(); } #define HAND_STENCIL_LEG_EXT(PROJ,PERM,DIR,RECON) \ - SE=st.GetEntry(ptype,DIR,ss); \ - offset = SE->_offset; \ - if((!SE->_is_local)&&(!st.same_node[DIR]) ) { \ - LOAD_CHI; \ - MULT_2SPIN(DIR); \ - RECON; \ - nmu++; \ - } \ - acceleratorSynchronise(); + { int ptype; \ + SE=st.GetEntry(ptype,DIR,ss); \ + auto offset = SE->_offset; \ + if((!SE->_is_local)&&(!st.same_node[DIR]) ) { \ + LOAD_CHI; \ + MULT_2SPIN(DIR); \ + RECON; \ + nmu++; \ + } \ + acceleratorSynchronise(); } -#define HAND_RESULT(ss) \ - { \ - SiteSpinor & ref (out[ss]); \ +#define HAND_RESULT(ss) \ + { \ + SiteSpinor & ref (out[ss]); \ coalescedWrite(ref()(0)(0),result_00,lane); \ coalescedWrite(ref()(0)(1),result_01,lane); \ coalescedWrite(ref()(0)(2),result_02,lane); \ @@ -563,7 +566,6 @@ WilsonKernels<Impl>::HandDhopSiteSycl(StencilVector st_perm,StencilEntry *st_p, HAND_DECLARATIONS(Simt); - int offset,local,perm, ptype; StencilEntry *SE; HAND_STENCIL_LEG(XM_PROJ,3,Xp,XM_RECON); HAND_STENCIL_LEG(YM_PROJ,2,Yp,YM_RECON_ACCUM); @@ -593,9 +595,7 @@ WilsonKernels<Impl>::HandDhopSite(StencilView &st, DoubledGaugeFieldView &U,Site HAND_DECLARATIONS(Simt); - int offset,local,perm, ptype; StencilEntry *SE; - HAND_STENCIL_LEG(XM_PROJ,3,Xp,XM_RECON); HAND_STENCIL_LEG(YM_PROJ,2,Yp,YM_RECON_ACCUM); HAND_STENCIL_LEG(ZM_PROJ,1,Zp,ZM_RECON_ACCUM); @@ -623,8 +623,6 @@ void WilsonKernels<Impl>::HandDhopSiteDag(StencilView &st,DoubledGaugeFieldView HAND_DECLARATIONS(Simt); StencilEntry *SE; - int offset,local,perm, ptype; - HAND_STENCIL_LEG(XP_PROJ,3,Xp,XP_RECON); HAND_STENCIL_LEG(YP_PROJ,2,Yp,YP_RECON_ACCUM); HAND_STENCIL_LEG(ZP_PROJ,1,Zp,ZP_RECON_ACCUM); @@ -640,8 +638,8 @@ template<class Impl> accelerator_inline void WilsonKernels<Impl>::HandDhopSiteInt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { - auto st_p = st._entries_p; - auto st_perm = st._permute_type; + // auto st_p = st._entries_p; + // auto st_perm = st._permute_type; // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; @@ -652,7 +650,6 @@ WilsonKernels<Impl>::HandDhopSiteInt(StencilView &st,DoubledGaugeFieldView &U,Si HAND_DECLARATIONS(Simt); - int offset,local,perm, ptype; StencilEntry *SE; ZERO_RESULT; HAND_STENCIL_LEG_INT(XM_PROJ,3,Xp,XM_RECON_ACCUM); @@ -670,8 +667,8 @@ template<class Impl> accelerator_inline void WilsonKernels<Impl>::HandDhopSiteDagInt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { - auto st_p = st._entries_p; - auto st_perm = st._permute_type; + // auto st_p = st._entries_p; + // auto st_perm = st._permute_type; typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; @@ -682,7 +679,6 @@ void WilsonKernels<Impl>::HandDhopSiteDagInt(StencilView &st,DoubledGaugeFieldVi HAND_DECLARATIONS(Simt); StencilEntry *SE; - int offset,local,perm, ptype; ZERO_RESULT; HAND_STENCIL_LEG_INT(XP_PROJ,3,Xp,XP_RECON_ACCUM); HAND_STENCIL_LEG_INT(YP_PROJ,2,Yp,YP_RECON_ACCUM); @@ -699,8 +695,8 @@ template<class Impl> accelerator_inline void WilsonKernels<Impl>::HandDhopSiteExt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { - auto st_p = st._entries_p; - auto st_perm = st._permute_type; + // auto st_p = st._entries_p; + // auto st_perm = st._permute_type; // T==0, Z==1, Y==2, Z==3 expect 1,2,2,2 simd layout etc... typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; @@ -711,7 +707,7 @@ WilsonKernels<Impl>::HandDhopSiteExt(StencilView &st,DoubledGaugeFieldView &U,Si HAND_DECLARATIONS(Simt); - int offset, ptype; + // int offset, ptype; StencilEntry *SE; int nmu=0; ZERO_RESULT; @@ -730,8 +726,8 @@ template<class Impl> accelerator_inline void WilsonKernels<Impl>::HandDhopSiteDagExt(StencilView &st,DoubledGaugeFieldView &U,SiteHalfSpinor *buf, int ss,int sU,const FermionFieldView &in, FermionFieldView &out) { - auto st_p = st._entries_p; - auto st_perm = st._permute_type; + // auto st_p = st._entries_p; + // auto st_perm = st._permute_type; typedef typename Simd::scalar_type S; typedef typename Simd::vector_type V; typedef decltype( coalescedRead( in[0]()(0)(0) )) Simt; @@ -742,7 +738,7 @@ void WilsonKernels<Impl>::HandDhopSiteDagExt(StencilView &st,DoubledGaugeFieldVi HAND_DECLARATIONS(Simt); StencilEntry *SE; - int offset, ptype; + // int offset, ptype; int nmu=0; ZERO_RESULT; HAND_STENCIL_LEG_EXT(XP_PROJ,3,Xp,XP_RECON_ACCUM); diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index fb01abbb..c2bc8dab 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -322,8 +322,8 @@ public: int simd_layout = _grid->_simd_layout[dimension]; int comm_dim = _grid->_processors[dimension] >1 ; - int recv_from_rank; - int xmit_to_rank; + // int recv_from_rank; + // int xmit_to_rank; if ( ! comm_dim ) return 1; if ( displacement == 0 ) return 1; diff --git a/benchmarks/Benchmark_IO.cc b/benchmarks/Benchmark_IO.cc index 87e7224d..01325c53 100644 --- a/benchmarks/Benchmark_IO.cc +++ b/benchmarks/Benchmark_IO.cc @@ -137,7 +137,7 @@ int main (int argc, char ** argv) Eigen::MatrixXd mean(nVol, 4), stdDev(nVol, 4), rob(nVol, 4); Eigen::VectorXd avMean(4), avStdDev(4), avRob(4); - double n = BENCH_IO_NPASS; + // double n = BENCH_IO_NPASS; stats(mean, stdDev, perf); stats(avMean, avStdDev, avPerf); @@ -164,7 +164,7 @@ int main (int argc, char ** argv) mean(volInd(l), gWrite), stdDev(volInd(l), gWrite)); } MSG << std::endl; - MSG << "Robustness of individual results, in \%. (rob = 100\% - std dev / mean)" << std::endl; + MSG << "Robustness of individual results, in %. (rob = 100% - std dev / mean)" << std::endl; MSG << std::endl; grid_printf("%4s %12s %12s %12s %12s\n", "L", "std read", "std write", "Grid read", "Grid write"); @@ -185,7 +185,7 @@ int main (int argc, char ** argv) avMean(sRead), avStdDev(sRead), avMean(sWrite), avStdDev(sWrite), avMean(gRead), avStdDev(gRead), avMean(gWrite), avStdDev(gWrite)); MSG << std::endl; - MSG << "Robustness of volume-averaged results, in \%. (rob = 100\% - std dev / mean)" << std::endl; + MSG << "Robustness of volume-averaged results, in %. (rob = 100% - std dev / mean)" << std::endl; MSG << std::endl; grid_printf("%12s %12s %12s %12s\n", "std read", "std write", "Grid read", "Grid write"); diff --git a/benchmarks/Benchmark_ITT.cc b/benchmarks/Benchmark_ITT.cc index 81d1acd4..2f7acb56 100644 --- a/benchmarks/Benchmark_ITT.cc +++ b/benchmarks/Benchmark_ITT.cc @@ -142,7 +142,7 @@ public: // bzero((void *)rbuf[d],lat*lat*lat*Ls*sizeof(HalfSpinColourVectorD)); } - int ncomm; + // int ncomm; double dbytes; for(int dir=0;dir<8;dir++) { @@ -290,7 +290,7 @@ public: LatticeSU4 z(&Grid); z=Zero(); LatticeSU4 x(&Grid); x=Zero(); LatticeSU4 y(&Grid); y=Zero(); - double a=2.0; + // double a=2.0; uint64_t Nloop=NLOOP; diff --git a/benchmarks/Benchmark_comms_host_device.cc b/benchmarks/Benchmark_comms_host_device.cc index 49a2181c..53173d9f 100644 --- a/benchmarks/Benchmark_comms_host_device.cc +++ b/benchmarks/Benchmark_comms_host_device.cc @@ -72,7 +72,7 @@ int main (int argc, char ** argv) std::cout << GridLogMessage << "Number of iterations to average: "<< Nloop << std::endl; std::vector<double> t_time(Nloop); - time_statistics timestat; + // time_statistics timestat; std::cout<<GridLogMessage << "===================================================================================================="<<std::endl; std::cout<<GridLogMessage << "= Benchmarking sequential halo exchange from host memory "<<std::endl; diff --git a/benchmarks/Benchmark_memory_bandwidth.cc b/benchmarks/Benchmark_memory_bandwidth.cc index 10fd48a1..3920d5f7 100644 --- a/benchmarks/Benchmark_memory_bandwidth.cc +++ b/benchmarks/Benchmark_memory_bandwidth.cc @@ -184,8 +184,10 @@ int main (int argc, char ** argv) double bytes=1.0*vol*Nvec*sizeof(Real); double flops=vol*Nvec*2;// mul,add - std::cout<<GridLogMessage<<std::setprecision(3) << lat<<"\t\t"<<bytes<<" \t\t"<<bytes/time<<"\t\t"<<flops/time<< "\t\t"<<(stop-start)/1000./1000.<< "\t\t " <<std::endl; - + std::cout<<GridLogMessage<<std::setprecision(3) << lat<<"\t\t" + <<bytes<<" \t\t"<<bytes/time<<"\t\t"<<flops/time<< "\t\t" + <<(stop-start)/1000./1000.<< "\t\t " <<std::endl; + assert(nn==nn); } Grid_finalize(); diff --git a/examples/Example_wall_wall_spectrum.cc b/examples/Example_wall_wall_spectrum.cc index 0d70f351..3e16473e 100644 --- a/examples/Example_wall_wall_spectrum.cc +++ b/examples/Example_wall_wall_spectrum.cc @@ -9,6 +9,7 @@ using namespace std; using namespace Grid; typedef SpinColourMatrix Propagator; typedef SpinColourVector Fermion; +typedef PeriodicGimplR GimplR; template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> { @@ -55,6 +56,16 @@ void MakePhase(Coordinate mom,LatticeComplex &phase) } phase = exp(phase*ci); } +void LinkSmear(int nstep, RealD rho,LatticeGaugeField &Uin,LatticeGaugeField &Usmr) +{ + Smear_Stout<GimplR> Stout(rho); + LatticeGaugeField Utmp(Uin.Grid()); + Utmp = Uin; + for(int i=0;i<nstep;i++){ + Stout.smear(Usmr,Utmp); + Utmp = Usmr; + } +} void PointSource(Coordinate &coor,LatticePropagator &source) { // Coordinate coor({0,0,0,0}); @@ -97,23 +108,23 @@ void GaugeFix(LatticeGaugeField &U,LatticeGaugeField &Ufix) { Real alpha=0.05; - Real plaq=WilsonLoops<PeriodicGimplR>::avgPlaquette(U); + Real plaq=WilsonLoops<GimplR>::avgPlaquette(U); std::cout << " Initial plaquette "<<plaq << std::endl; LatticeColourMatrix xform(U.Grid()); Ufix = U; int orthog=Nd-1; - FourierAcceleratedGaugeFixer<PeriodicGimplR>::SteepestDescentGaugeFix(Ufix,xform,alpha,10000,1.0e-12, 1.0e-12,true,orthog); + FourierAcceleratedGaugeFixer<GimplR>::SteepestDescentGaugeFix(Ufix,xform,alpha,100000,1.0e-14, 1.0e-14,true,orthog); - plaq=WilsonLoops<PeriodicGimplR>::avgPlaquette(Ufix); + plaq=WilsonLoops<GimplR>::avgPlaquette(Ufix); std::cout << " Final plaquette "<<plaq << std::endl; } template<class Field> void GaussianSmear(LatticeGaugeField &U,Field &unsmeared,Field &smeared) { - typedef CovariantLaplacianCshift <PeriodicGimplR,Field> Laplacian_t; + typedef CovariantLaplacianCshift <GimplR,Field> Laplacian_t; Laplacian_t Laplacian(U); Integer Iterations = 40; @@ -167,19 +178,21 @@ void Solve(Action &D,LatticePropagator &source,LatticePropagator &propagator) GridBase *UGrid = D.GaugeGrid(); GridBase *FGrid = D.FermionGrid(); - LatticeFermion src4 (UGrid); + LatticeFermion src4 (UGrid); src4 = Zero(); LatticeFermion src5 (FGrid); LatticeFermion result5(FGrid); LatticeFermion result4(UGrid); - ConjugateGradient<LatticeFermion> CG(1.0e-8,100000); - SchurRedBlackDiagMooeeSolve<LatticeFermion> schur(CG); + ConjugateGradient<LatticeFermion> CG(1.0e-12,100000); + SchurRedBlackDiagTwoSolve<LatticeFermion> schur(CG); ZeroGuesser<LatticeFermion> ZG; // Could be a DeflatedGuesser if have eigenvectors + std::cout<<GridLogMessage<< " source4 "<<norm2(source)<<std::endl; for(int s=0;s<Nd;s++){ for(int c=0;c<Nc;c++){ PropToFerm<Action>(src4,source,s,c); - + std::cout<<GridLogMessage<< s<<c<<" src4 "<<norm2(src4)<<std::endl; D.ImportPhysicalFermionSource(src4,src5); + std::cout<<GridLogMessage<< s<<c<<" src5 "<<norm2(src5)<<std::endl; result5=Zero(); schur(D,src5,result5,ZG); @@ -287,15 +300,10 @@ int main (int argc, char ** argv) GridDefaultMpi()); GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); - ////////////////////////////////////////////////////////////////////// - // You can manage seeds however you like. - // Recommend SeedUniqueString. - ////////////////////////////////////////////////////////////////////// - std::vector<int> seeds4({1,2,3,4}); - GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4); LatticeGaugeField Umu(UGrid); - LatticeGaugeField Ufixed(UGrid); + LatticeGaugeField Utmp(UGrid); + LatticeGaugeField Usmr(UGrid); std::string config; if( argc > 1 && argv[1][0] != '-' ) { @@ -308,13 +316,20 @@ int main (int argc, char ** argv) { std::cout<<GridLogMessage <<"Using hot configuration"<<std::endl; SU<Nc>::ColdConfiguration(Umu); - // SU<Nc>::HotConfiguration(RNG4,Umu); - config="HotConfig"; + config="ColdConfig"; } - GaugeFix(Umu,Ufixed); - Umu=Ufixed; - + // GaugeFix(Umu,Utmp); + // Umu=Utmp; + int nsmr=3; + RealD rho=0.1; + RealD plaq_gf =WilsonLoops<GimplR>::avgPlaquette(Umu); + LinkSmear(nsmr,rho,Umu,Usmr); + RealD plaq_smr=WilsonLoops<GimplR>::avgPlaquette(Usmr); + std::cout << GridLogMessage << " GF Plaquette " <<plaq_gf<<std::endl; + std::cout << GridLogMessage << " SM Plaquette " <<plaq_smr<<std::endl; + + std::vector<int> smeared_link({ 0,0,1} ); std::vector<RealD> masses({ 0.004,0.02477,0.447} ); // u/d, s, c ?? std::vector<RealD> M5s ({ 1.8,1.8,1.0} ); std::vector<RealD> bs ({ 1.0,1.0,1.5} ); // DDM @@ -330,6 +345,9 @@ int main (int argc, char ** argv) std::cout<<GridLogMessage <<"======================"<<std::endl; std::cout<<GridLogMessage <<"MobiusFermion action as Scaled Shamir kernel"<<std::endl; std::cout<<GridLogMessage <<"======================"<<std::endl; + std::vector<Complex> boundary = {1,1,1,-1}; + typedef MobiusFermionR FermionAction; + FermionAction::ImplParams Params(boundary); for(int m=0;m<masses.size();m++) { @@ -339,30 +357,40 @@ int main (int argc, char ** argv) RealD c = cs[m]; int Ls = Ls_s[m]; + if ( smeared_link[m] ) Utmp = Usmr; + else Utmp = Umu; + FGrids.push_back(SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid)); FrbGrids.push_back(SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid)); - FermActs.push_back(new MobiusFermionR(Umu,*FGrids[m],*FrbGrids[m],*UGrid,*UrbGrid,mass,M5,b,c)); + FermActs.push_back(new MobiusFermionR(Utmp,*FGrids[m],*FrbGrids[m],*UGrid,*UrbGrid,mass,M5,b,c,Params)); } - LatticePropagator point_source(UGrid); LatticePropagator z2wall_source(UGrid); LatticePropagator gfwall_source(UGrid); - Coordinate Origin({0,0,0,0}); - PointSource (Origin,point_source); - Z2WallSource (RNG4,0,z2wall_source); - GFWallSource (0,gfwall_source); - - std::vector<LatticePropagator> PointProps(nmass,UGrid); - std::vector<LatticePropagator> GaussProps(nmass,UGrid); + int tslice = 0; + ////////////////////////////////////////////////////////////////////// + // RNG seeded for Z2 wall + ////////////////////////////////////////////////////////////////////// + // You can manage seeds however you like. + // Recommend SeedUniqueString. + ////////////////////////////////////////////////////////////////////// + GridParallelRNG RNG4(UGrid); RNG4.SeedUniqueString("Study2-Source_Z2_p_0_0_0_t_0-880"); + Z2WallSource (RNG4,tslice,z2wall_source); + GFWallSource (tslice,gfwall_source); + std::vector<LatticePropagator> Z2Props (nmass,UGrid); std::vector<LatticePropagator> GFProps (nmass,UGrid); for(int m=0;m<nmass;m++) { + std::cout << GridLogMessage << " Mass " <<m << " z2wall source "<<norm2(z2wall_source)<<std::endl; Solve(*FermActs[m],z2wall_source ,Z2Props[m]); + std::cout << GridLogMessage << " Mass " <<m << " gfwall source "<<norm2(gfwall_source)<<std::endl; Solve(*FermActs[m],gfwall_source ,GFProps[m]); + + std::cout << GridLogMessage << " Mass " <<m << " z2wall source "<<norm2(z2wall_source)<< " " << norm2(gfwall_source)<<std::endl; } @@ -383,14 +411,15 @@ int main (int argc, char ** argv) std::stringstream wssg,wssz; /// Point sinks - ssg<<config<< "_m" << m1 << "_m"<< m2 << "p_gf_meson.xml"; - ssz<<config<< "_m" << m1 << "_m"<< m2 << "p_z2_meson.xml"; + ssg<<config<< "_m" << m1 << "_m"<< m2 << "_p_gf_meson.xml"; + ssz<<config<< "_m" << m1 << "_m"<< m2 << "_p_z2_meson.xml"; MesonTrace(ssz.str(),Z2Props[m1],Z2Props[m2],phase); + MesonTrace(ssg.str(),GFProps[m1],GFProps[m2],phase); /// Wall sinks - wssg<<config<< "_m" << m1 << "_m"<< m2 << "w_gf_meson.xml"; - wssz<<config<< "_m" << m1 << "_m"<< m2 << "w_z2_meson.xml"; + wssg<<config<< "_m" << m1 << "_m"<< m2 << "_w_gf_meson.xml"; + wssz<<config<< "_m" << m1 << "_m"<< m2 << "_w_z2_meson.xml"; WallSinkMesonTrace(wssg.str(),wsnk_gfProps[m1],wsnk_gfProps[m2]); WallSinkMesonTrace(wssz.str(),wsnk_z2Props[m1],wsnk_z2Props[m2]); diff --git a/systems/Tursa/sourceme.sh b/systems/Tursa/sourceme.sh index 38214052..dc560f9d 100644 --- a/systems/Tursa/sourceme.sh +++ b/systems/Tursa/sourceme.sh @@ -1,5 +1,6 @@ -module load cuda/11.4.1 openmpi/4.1.1 ucx/1.10.1 -module load cuda/11.4.1 openmpi/4.1.1 ucx/1.10.1 +module load cuda/11.4.1 openmpi/4.1.1-cuda11.4.1 ucx/1.12.0-cuda11.4.1 +#module load cuda/11.4.1 openmpi/4.1.1 ucx/1.10.1 export PREFIX=/home/tc002/tc002/shared/env/prefix/ export LD_LIBRARY_PATH=$PREFIX/lib/:$LD_LIBRARY_PATH unset SBATCH_EXPORT + From c144b32368fdd8a0338a2c0c5c0c70675e43c048 Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Tue, 26 Oct 2021 10:37:24 +0100 Subject: [PATCH 253/399] deflation timers --- Grid/algorithms/iterative/Deflation.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index 43fe3e35..4da8e037 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -70,12 +70,26 @@ public: } virtual void operator()(const Field &src,Field &guess) { + GridStopWatch w1; + GridStopWatch w2; + + w1.Start(); guess = Zero(); + w1.Stop(); + + LOG(Message) << "Zeroing the 'out' vector took: " << w1.Elapsed() << std::endl; + + w2.Start(); for (int i=0;i<N;i++) { const Field& tmp = evec[i]; axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess); } + w2.Stop(); + guess.Checkerboard() = src.Checkerboard(); + + LOG(Message) << "This projection took: " << w2.Elapsed() << std::endl; + } }; From 5398b7e7e3ba313e8eb866c1a3a8d791c1794045 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 26 Oct 2021 09:16:29 -0700 Subject: [PATCH 254/399] Max 128 size --- Grid/util/Coordinate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/util/Coordinate.h b/Grid/util/Coordinate.h index 004fbc72..05b83bf9 100644 --- a/Grid/util/Coordinate.h +++ b/Grid/util/Coordinate.h @@ -88,7 +88,7 @@ public: // Coordinate class, maxdims = 8 for now. //////////////////////////////////////////////////////////////// #define GRID_MAX_LATTICE_DIMENSION (8) -#define GRID_MAX_SIMD (16) +#define GRID_MAX_SIMD (32) static constexpr int MaxDims = GRID_MAX_LATTICE_DIMENSION; From a4ce6e42c7814853be1c5342ede2c1d979e60553 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 27 Oct 2021 00:27:03 +0100 Subject: [PATCH 255/399] Warning free compile on make all and make tests under nvcc --- Grid/algorithms/CoarsenedMatrix.h | 8 +++--- Grid/algorithms/LinearOperator.h | 4 ++- Grid/algorithms/Preconditioner.h | 10 +++++-- Grid/algorithms/iterative/BiCGSTABMixedPrec.h | 3 +- .../iterative/LocalCoherenceLanczos.h | 2 ++ .../PrecGeneralisedConjugateResidual.h | 2 +- ...GeneralisedConjugateResidualNonHermitian.h | 11 ++++---- Grid/tensors/Tensor_traits.h | 6 ++-- examples/Example_Laplacian_solver.cc | 2 +- tests/core/Test_contfrac_even_odd.cc | 1 - tests/core/Test_dwf_eofa_even_odd.cc | 1 - tests/core/Test_dwf_even_odd.cc | 2 -- tests/core/Test_gamma.cc | 4 +-- tests/core/Test_gpwilson_even_odd.cc | 1 - tests/core/Test_main.cc | 12 ++++---- tests/core/Test_mobius_eofa_even_odd.cc | 1 - tests/core/Test_mobius_even_odd.cc | 1 - tests/core/Test_staggered.cc | 2 +- tests/core/Test_staggered5D.cc | 1 - tests/core/Test_staggered_naive.cc | 2 -- tests/core/Test_wilson_even_odd.cc | 1 - .../core/Test_wilson_twisted_mass_even_odd.cc | 1 - tests/core/Test_zmobius_even_odd.cc | 1 - tests/forces/Test_rect_force.cc | 1 - ..._dwf_compressed_lanczos_reorg_synthetic.cc | 3 ++ tests/solver/Test_dwf_hdcr.cc | 3 ++ tests/solver/Test_dwf_hdcr_16_rb.cc | 9 ++++-- tests/solver/Test_dwf_hdcr_24_regression.cc | 4 +++ tests/solver/Test_dwf_hdcr_2level.cc | 8 ++++-- tests/solver/Test_dwf_hdcr_48_rb.cc | 5 +++- tests/solver/Test_dwf_hdcr_48_regression.cc | 3 ++ tests/solver/Test_dwf_multigrid.cc | 3 ++ tests/solver/Test_hw_multigrid_mixed_48.cc | 21 ++++++++------ tests/solver/Test_hw_multigrid_mixed_48_rb.cc | 28 +++++++++++-------- tests/solver/Test_multigrid_common.h | 3 ++ tests/solver/Test_wilson_qmr_unprec.cc | 1 - 36 files changed, 101 insertions(+), 70 deletions(-) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index 2fd187ff..a9a82f34 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -358,7 +358,7 @@ public: autoView( in_v , in, AcceleratorRead); autoView( out_v , out, AcceleratorWrite); autoView( Stencil_v , Stencil, AcceleratorRead); - auto& geom_v = geom; + int npoint = geom.npoint; typedef LatticeView<Cobj> Aview; Vector<Aview> AcceleratorViewContainer; @@ -380,7 +380,7 @@ public: int ptype; StencilEntry *SE; - for(int point=0;point<geom_v.npoint;point++){ + for(int point=0;point<npoint;point++){ SE=Stencil_v.GetEntry(ptype,point,ss); @@ -424,7 +424,7 @@ public: autoView( in_v , in, AcceleratorRead); autoView( out_v , out, AcceleratorWrite); autoView( Stencil_v , Stencil, AcceleratorRead); - auto& geom_v = geom; + int npoint = geom.npoint; typedef LatticeView<Cobj> Aview; Vector<Aview> AcceleratorViewContainer; @@ -454,7 +454,7 @@ public: int ptype; StencilEntry *SE; - for(int p=0;p<geom_v.npoint;p++){ + for(int p=0;p<npoint;p++){ int point = points_p[p]; SE=Stencil_v.GetEntry(ptype,point,ss); diff --git a/Grid/algorithms/LinearOperator.h b/Grid/algorithms/LinearOperator.h index e1077850..b1cf4d97 100644 --- a/Grid/algorithms/LinearOperator.h +++ b/Grid/algorithms/LinearOperator.h @@ -508,7 +508,7 @@ class SchurStaggeredOperator : public SchurOperatorBase<Field> { virtual void MpcDag (const Field &in, Field &out){ Mpc(in,out); } - virtual void MpcDagMpc(const Field &in, Field &out,RealD &ni,RealD &no) { + virtual void MpcDagMpc(const Field &in, Field &out) { assert(0);// Never need with staggered } }; @@ -586,6 +586,7 @@ class HermOpOperatorFunction : public OperatorFunction<Field> { template<typename Field> class PlainHermOp : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); LinearOperatorBase<Field> &_Linop; PlainHermOp(LinearOperatorBase<Field>& linop) : _Linop(linop) @@ -599,6 +600,7 @@ public: template<typename Field> class FunctionHermOp : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); OperatorFunction<Field> & _poly; LinearOperatorBase<Field> &_Linop; diff --git a/Grid/algorithms/Preconditioner.h b/Grid/algorithms/Preconditioner.h index 65ac3cf9..a95dad7c 100644 --- a/Grid/algorithms/Preconditioner.h +++ b/Grid/algorithms/Preconditioner.h @@ -30,13 +30,19 @@ Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk> NAMESPACE_BEGIN(Grid); -template<class Field> class Preconditioner : public LinearFunction<Field> { +template<class Field> using Preconditioner = LinearFunction<Field> ; + +/* +template<class Field> class Preconditioner : public LinearFunction<Field> { + using LinearFunction<Field>::operator(); virtual void operator()(const Field &src, Field & psi)=0; }; +*/ template<class Field> class TrivialPrecon : public Preconditioner<Field> { public: - void operator()(const Field &src, Field & psi){ + using Preconditioner<Field>::operator(); + virtual void operator()(const Field &src, Field & psi){ psi = src; } TrivialPrecon(void){}; diff --git a/Grid/algorithms/iterative/BiCGSTABMixedPrec.h b/Grid/algorithms/iterative/BiCGSTABMixedPrec.h index d9b2a9d7..c34035a8 100644 --- a/Grid/algorithms/iterative/BiCGSTABMixedPrec.h +++ b/Grid/algorithms/iterative/BiCGSTABMixedPrec.h @@ -36,7 +36,8 @@ NAMESPACE_BEGIN(Grid); template<class FieldD, class FieldF, typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0, typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0> class MixedPrecisionBiCGSTAB : public LinearFunction<FieldD> { - public: + public: + using LinearFunction<FieldD>::operator(); RealD Tolerance; RealD InnerTolerance; // Initial tolerance for inner CG. Defaults to Tolerance but can be changed Integer MaxInnerIterations; diff --git a/Grid/algorithms/iterative/LocalCoherenceLanczos.h b/Grid/algorithms/iterative/LocalCoherenceLanczos.h index 9c945565..dc82134a 100644 --- a/Grid/algorithms/iterative/LocalCoherenceLanczos.h +++ b/Grid/algorithms/iterative/LocalCoherenceLanczos.h @@ -67,6 +67,7 @@ public: template<class Fobj,class CComplex,int nbasis> class ProjectedHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > { public: + using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator(); typedef iVector<CComplex,nbasis > CoarseSiteVector; typedef Lattice<CoarseSiteVector> CoarseField; typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field @@ -97,6 +98,7 @@ public: template<class Fobj,class CComplex,int nbasis> class ProjectedFunctionHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > { public: + using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator(); typedef iVector<CComplex,nbasis > CoarseSiteVector; typedef Lattice<CoarseSiteVector> CoarseField; typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field diff --git a/Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h b/Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h index bf454ade..feceb21f 100644 --- a/Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h +++ b/Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h @@ -43,7 +43,7 @@ NAMESPACE_BEGIN(Grid); template<class Field> class PrecGeneralisedConjugateResidual : public LinearFunction<Field> { public: - + using LinearFunction<Field>::operator(); RealD Tolerance; Integer MaxIterations; int verbose; diff --git a/Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h b/Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h index 22b7725e..181df320 100644 --- a/Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h +++ b/Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h @@ -43,7 +43,7 @@ NAMESPACE_BEGIN(Grid); template<class Field> class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> { public: - + using LinearFunction<Field>::operator(); RealD Tolerance; Integer MaxIterations; int verbose; @@ -119,7 +119,8 @@ public: RealD GCRnStep(const Field &src, Field &psi,RealD rsq){ RealD cp; - ComplexD a, b, zAz; + ComplexD a, b; + // ComplexD zAz; RealD zAAz; ComplexD rq; @@ -146,7 +147,7 @@ public: ////////////////////////////////// MatTimer.Start(); Linop.Op(psi,Az); - zAz = innerProduct(Az,psi); + // zAz = innerProduct(Az,psi); zAAz= norm2(Az); MatTimer.Stop(); @@ -170,7 +171,7 @@ public: LinalgTimer.Start(); - zAz = innerProduct(Az,psi); + // zAz = innerProduct(Az,psi); zAAz= norm2(Az); //p[0],q[0],qq[0] @@ -212,7 +213,7 @@ public: MatTimer.Start(); Linop.Op(z,Az); MatTimer.Stop(); - zAz = innerProduct(Az,psi); + // zAz = innerProduct(Az,psi); zAAz= norm2(Az); LinalgTimer.Start(); diff --git a/Grid/tensors/Tensor_traits.h b/Grid/tensors/Tensor_traits.h index dfa3c043..89290214 100644 --- a/Grid/tensors/Tensor_traits.h +++ b/Grid/tensors/Tensor_traits.h @@ -47,20 +47,20 @@ NAMESPACE_BEGIN(Grid); class TypePair { public: T _internal[2]; - TypePair<T>& operator=(const Grid::Zero& o) { + accelerator TypePair<T>& operator=(const Grid::Zero& o) { _internal[0] = Zero(); _internal[1] = Zero(); return *this; } - TypePair<T> operator+(const TypePair<T>& o) const { + accelerator TypePair<T> operator+(const TypePair<T>& o) const { TypePair<T> r; r._internal[0] = _internal[0] + o._internal[0]; r._internal[1] = _internal[1] + o._internal[1]; return r; } - TypePair<T>& operator+=(const TypePair<T>& o) { + accelerator TypePair<T>& operator+=(const TypePair<T>& o) { _internal[0] += o._internal[0]; _internal[1] += o._internal[1]; return *this; diff --git a/examples/Example_Laplacian_solver.cc b/examples/Example_Laplacian_solver.cc index 4dc00280..9a584c0f 100644 --- a/examples/Example_Laplacian_solver.cc +++ b/examples/Example_Laplacian_solver.cc @@ -4,7 +4,7 @@ using namespace Grid; template<class Field> void SimpleConjugateGradient(LinearOperatorBase<Field> &HPDop,const Field &b, Field &x) { - RealD cp, c, alpha, d, beta, ssq, qq; + RealD cp, c, alpha, d, beta, ssq; RealD Tolerance=1.0e-10; int MaxIterations=10000; diff --git a/tests/core/Test_contfrac_even_odd.cc b/tests/core/Test_contfrac_even_odd.cc index 53d00010..42bfe361 100644 --- a/tests/core/Test_contfrac_even_odd.cc +++ b/tests/core/Test_contfrac_even_odd.cc @@ -235,7 +235,6 @@ void TestWhat(What & Ddwf, pickCheckerboard(Odd ,chi_o,chi); pickCheckerboard(Even,phi_e,phi); pickCheckerboard(Odd ,phi_o,phi); - RealD t1,t2; SchurDiagMooeeOperator<What,LatticeFermion> HermOpEO(Ddwf); HermOpEO.MpcDagMpc(chi_e,dchi_e); diff --git a/tests/core/Test_dwf_eofa_even_odd.cc b/tests/core/Test_dwf_eofa_even_odd.cc index 64701069..7812ebb8 100644 --- a/tests/core/Test_dwf_eofa_even_odd.cc +++ b/tests/core/Test_dwf_eofa_even_odd.cc @@ -215,7 +215,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd , chi_o, chi); pickCheckerboard(Even, phi_e, phi); pickCheckerboard(Odd , phi_o, phi); - RealD t1,t2; SchurDiagMooeeOperator<DomainWallEOFAFermionR,LatticeFermion> HermOpEO(Ddwf); HermOpEO.MpcDagMpc(chi_e, dchi_e); diff --git a/tests/core/Test_dwf_even_odd.cc b/tests/core/Test_dwf_even_odd.cc index 4918f02a..924eb3b7 100644 --- a/tests/core/Test_dwf_even_odd.cc +++ b/tests/core/Test_dwf_even_odd.cc @@ -212,8 +212,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd ,chi_o,chi); pickCheckerboard(Even,phi_e,phi); pickCheckerboard(Odd ,phi_o,phi); - RealD t1,t2; - SchurDiagMooeeOperator<DomainWallFermionR,LatticeFermion> HermOpEO(Ddwf); HermOpEO.MpcDagMpc(chi_e,dchi_e); diff --git a/tests/core/Test_gamma.cc b/tests/core/Test_gamma.cc index 2658f2c1..e52049fe 100644 --- a/tests/core/Test_gamma.cc +++ b/tests/core/Test_gamma.cc @@ -181,8 +181,8 @@ void checkAdj(const Gamma::Algebra a) void checkProject(GridSerialRNG &rng) { - SpinVector rv, recon, full; - HalfSpinVector hsp, hsm; + SpinVector rv, recon; + HalfSpinVector hsm; random(rng, rv); diff --git a/tests/core/Test_gpwilson_even_odd.cc b/tests/core/Test_gpwilson_even_odd.cc index 69ace859..d510657e 100644 --- a/tests/core/Test_gpwilson_even_odd.cc +++ b/tests/core/Test_gpwilson_even_odd.cc @@ -198,7 +198,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd ,chi_o,chi); pickCheckerboard(Even,phi_e,phi); pickCheckerboard(Odd ,phi_o,phi); - RealD t1,t2; SchurDiagMooeeOperator<GparityWilsonFermionR,FermionField> HermOpEO(Dw); HermOpEO.MpcDagMpc(chi_e,dchi_e); diff --git a/tests/core/Test_main.cc b/tests/core/Test_main.cc index 6e316aa6..25d840a6 100644 --- a/tests/core/Test_main.cc +++ b/tests/core/Test_main.cc @@ -364,14 +364,12 @@ int main(int argc, char **argv) { { // Peek-ology and Poke-ology, with a little app-ology Complex c; - ColourMatrix c_m; - SpinMatrix s_m; - SpinColourMatrix sc_m; + ColourMatrix c_m = Zero(); + SpinMatrix s_m = Zero(); + SpinColourMatrix sc_m = Zero(); - s_m = TensorIndexRecursion<ColourIndex>::traceIndex( - sc_m); // Map to traceColour - c_m = TensorIndexRecursion<SpinIndex>::traceIndex( - sc_m); // map to traceSpin + s_m = TensorIndexRecursion<ColourIndex>::traceIndex(sc_m); // Map to traceColour + c_m = TensorIndexRecursion<SpinIndex>::traceIndex(sc_m); // map to traceSpin c = TensorIndexRecursion<SpinIndex>::traceIndex(s_m); c = TensorIndexRecursion<ColourIndex>::traceIndex(c_m); diff --git a/tests/core/Test_mobius_eofa_even_odd.cc b/tests/core/Test_mobius_eofa_even_odd.cc index 7339f156..68ffe624 100644 --- a/tests/core/Test_mobius_eofa_even_odd.cc +++ b/tests/core/Test_mobius_eofa_even_odd.cc @@ -217,7 +217,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd , chi_o, chi); pickCheckerboard(Even, phi_e, phi); pickCheckerboard(Odd , phi_o, phi); - RealD t1,t2; SchurDiagMooeeOperator<MobiusEOFAFermionR,LatticeFermion> HermOpEO(Ddwf); HermOpEO.MpcDagMpc(chi_e, dchi_e); diff --git a/tests/core/Test_mobius_even_odd.cc b/tests/core/Test_mobius_even_odd.cc index 7f808cac..e210f236 100644 --- a/tests/core/Test_mobius_even_odd.cc +++ b/tests/core/Test_mobius_even_odd.cc @@ -262,7 +262,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd ,chi_o,chi); pickCheckerboard(Even,phi_e,phi); pickCheckerboard(Odd ,phi_o,phi); - RealD t1,t2; SchurDiagMooeeOperator<MobiusFermionR,LatticeFermion> HermOpEO(Ddwf); diff --git a/tests/core/Test_staggered.cc b/tests/core/Test_staggered.cc index 51f92993..ba615ad2 100644 --- a/tests/core/Test_staggered.cc +++ b/tests/core/Test_staggered.cc @@ -144,7 +144,7 @@ int main (int argc, char ** argv) Ds.Dhop(src,result,0); } double t1=usecond(); - double t2; + double flops=(16*(3*(6+8+8)) + 15*3*2)*volume*ncall; // == 66*16 + == 1146 std::cout<<GridLogMessage << "Called Ds"<<std::endl; diff --git a/tests/core/Test_staggered5D.cc b/tests/core/Test_staggered5D.cc index 6ab15873..b1b3be1d 100644 --- a/tests/core/Test_staggered5D.cc +++ b/tests/core/Test_staggered5D.cc @@ -162,7 +162,6 @@ int main (int argc, char ** argv) } double t1=usecond(); - double t2; double flops=(16*(3*(6+8+8)) + 15*3*2)*volume*ncall; // == 66*16 + == 1146 std::cout<<GridLogMessage << "Called Ds"<<std::endl; diff --git a/tests/core/Test_staggered_naive.cc b/tests/core/Test_staggered_naive.cc index f41d723d..d8ca9d5f 100644 --- a/tests/core/Test_staggered_naive.cc +++ b/tests/core/Test_staggered_naive.cc @@ -30,7 +30,6 @@ Author: paboyle <paboyle@ph.ed.ac.uk> using namespace std; using namespace Grid; - ; int main (int argc, char ** argv) { @@ -135,7 +134,6 @@ int main (int argc, char ** argv) Ds.Dhop(src,result,0); } double t1=usecond(); - double t2; double flops=(16*(3*(6+8+8)) + 15*3*2)*volume*ncall; // == 66*16 + == 1146 std::cout<<GridLogMessage << "Called Ds"<<std::endl; diff --git a/tests/core/Test_wilson_even_odd.cc b/tests/core/Test_wilson_even_odd.cc index e7733a79..4d240b80 100644 --- a/tests/core/Test_wilson_even_odd.cc +++ b/tests/core/Test_wilson_even_odd.cc @@ -204,7 +204,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd ,chi_o,chi); pickCheckerboard(Even,phi_e,phi); pickCheckerboard(Odd ,phi_o,phi); - RealD t1,t2; SchurDiagMooeeOperator<WilsonFermionR,LatticeFermion> HermOpEO(Dw); HermOpEO.MpcDagMpc(chi_e,dchi_e); diff --git a/tests/core/Test_wilson_twisted_mass_even_odd.cc b/tests/core/Test_wilson_twisted_mass_even_odd.cc index e0f73456..d9e798c3 100644 --- a/tests/core/Test_wilson_twisted_mass_even_odd.cc +++ b/tests/core/Test_wilson_twisted_mass_even_odd.cc @@ -205,7 +205,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd ,chi_o,chi); pickCheckerboard(Even,phi_e,phi); pickCheckerboard(Odd ,phi_o,phi); - RealD t1,t2; SchurDiagMooeeOperator<WilsonTMFermionR,LatticeFermion> HermOpEO(Dw); HermOpEO.MpcDagMpc(chi_e,dchi_e); diff --git a/tests/core/Test_zmobius_even_odd.cc b/tests/core/Test_zmobius_even_odd.cc index a52e9bc2..f6e18934 100644 --- a/tests/core/Test_zmobius_even_odd.cc +++ b/tests/core/Test_zmobius_even_odd.cc @@ -276,7 +276,6 @@ int main (int argc, char ** argv) pickCheckerboard(Odd ,chi_o,chi); pickCheckerboard(Even,phi_e,phi); pickCheckerboard(Odd ,phi_o,phi); - RealD t1,t2; SchurDiagMooeeOperator<ZMobiusFermionR,LatticeFermion> HermOpEO(Ddwf); diff --git a/tests/forces/Test_rect_force.cc b/tests/forces/Test_rect_force.cc index c9326f8d..e40cb5fd 100644 --- a/tests/forces/Test_rect_force.cc +++ b/tests/forces/Test_rect_force.cc @@ -57,7 +57,6 @@ int main (int argc, char ** argv) SU<Nc>::HotConfiguration(pRNG,U); double beta = 1.0; - double c1 = -0.331; IwasakiGaugeActionR Action(beta); // PlaqPlusRectangleActionR Action(beta,c1); diff --git a/tests/lanczos/Test_dwf_compressed_lanczos_reorg_synthetic.cc b/tests/lanczos/Test_dwf_compressed_lanczos_reorg_synthetic.cc index 3766e069..f3cb567c 100644 --- a/tests/lanczos/Test_dwf_compressed_lanczos_reorg_synthetic.cc +++ b/tests/lanczos/Test_dwf_compressed_lanczos_reorg_synthetic.cc @@ -40,6 +40,7 @@ using namespace Grid; template<class Fobj,class CComplex,int nbasis> class ProjectedHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > { public: + using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator(); typedef iVector<CComplex,nbasis > CoarseSiteVector; typedef Lattice<CoarseSiteVector> CoarseField; typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field @@ -67,6 +68,8 @@ public: template<class Fobj,class CComplex,int nbasis> class ProjectedFunctionHermOp : public LinearFunction<Lattice<iVector<CComplex,nbasis > > > { public: + using LinearFunction<Lattice<iVector<CComplex,nbasis > > >::operator (); + typedef iVector<CComplex,nbasis > CoarseSiteVector; typedef Lattice<CoarseSiteVector> CoarseField; typedef Lattice<CComplex> CoarseScalar; // used for inner products on fine field diff --git a/tests/solver/Test_dwf_hdcr.cc b/tests/solver/Test_dwf_hdcr.cc index f68e99ab..ba77dffa 100644 --- a/tests/solver/Test_dwf_hdcr.cc +++ b/tests/solver/Test_dwf_hdcr.cc @@ -55,6 +55,7 @@ RealD InverseApproximation(RealD x){ template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -78,6 +79,7 @@ public: template<class Field,class Matrix> class MirsSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & SmootherMatrix; FineOperator & SmootherOperator; @@ -108,6 +110,7 @@ public: template<class Fobj,class CComplex,int nbasis, class Matrix, class Guesser, class CoarseSolver> class MultiGridPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef CoarsenedMatrix<Fobj,CComplex,nbasis> CoarseOperator; diff --git a/tests/solver/Test_dwf_hdcr_16_rb.cc b/tests/solver/Test_dwf_hdcr_16_rb.cc index b7900b04..4682272d 100644 --- a/tests/solver/Test_dwf_hdcr_16_rb.cc +++ b/tests/solver/Test_dwf_hdcr_16_rb.cc @@ -56,9 +56,9 @@ template<class Field> class SolverWrapper : public LinearFunction<Field> { private: CheckerBoardedSparseMatrixBase<Field> & _Matrix; SchurRedBlackBase<Field> & _Solver; -public: - - ///////////////////////////////////////////////////// +public: + using LinearFunction<Field>::operator(); + ///////////////////////////////////////////////////// // Wrap the usual normal equations trick ///////////////////////////////////////////////////// SolverWrapper(CheckerBoardedSparseMatrixBase<Field> &Matrix, @@ -75,6 +75,7 @@ public: template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -98,6 +99,7 @@ public: template<class Field,class Matrix> class MirsSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & SmootherMatrix; FineOperator & SmootherOperator; @@ -128,6 +130,7 @@ public: template<class Fobj,class CComplex,int nbasis, class Matrix, class Guesser, class CoarseSolver> class MultiGridPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef CoarsenedMatrix<Fobj,CComplex,nbasis> CoarseOperator; diff --git a/tests/solver/Test_dwf_hdcr_24_regression.cc b/tests/solver/Test_dwf_hdcr_24_regression.cc index 180b2e00..e6363474 100644 --- a/tests/solver/Test_dwf_hdcr_24_regression.cc +++ b/tests/solver/Test_dwf_hdcr_24_regression.cc @@ -55,6 +55,7 @@ RealD InverseApproximation(RealD x){ template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -78,6 +79,7 @@ public: template<class Field,class Matrix> class MirsSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & SmootherMatrix; FineOperator & SmootherOperator; @@ -108,6 +110,8 @@ public: template<class Fobj,class CComplex,int nbasis, class Matrix, class Guesser, class CoarseSolver> class MultiGridPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef CoarsenedMatrix<Fobj,CComplex,nbasis> CoarseOperator; diff --git a/tests/solver/Test_dwf_hdcr_2level.cc b/tests/solver/Test_dwf_hdcr_2level.cc index 4fa1e302..819f3464 100644 --- a/tests/solver/Test_dwf_hdcr_2level.cc +++ b/tests/solver/Test_dwf_hdcr_2level.cc @@ -56,6 +56,7 @@ RealD InverseApproximation(RealD x){ template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -79,6 +80,7 @@ public: template<class Field,class Matrix> class MirsSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & SmootherMatrix; FineOperator & SmootherOperator; @@ -108,6 +110,7 @@ public: template<class Field,class Matrix> class RedBlackSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & SmootherMatrix; RealD tol; @@ -134,6 +137,7 @@ public: template<class Fobj,class CComplex,int nbasis, class Matrix, class Guesser, class CoarseSolver> class MultiGridPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef CoarsenedMatrix<Fobj,CComplex,nbasis> CoarseOperator; @@ -241,7 +245,7 @@ int main (int argc, char ** argv) Grid_init(&argc,&argv); const int Ls=16; - const int rLs=8; + // const int rLs=8; GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi()); GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); @@ -388,7 +392,7 @@ int main (int argc, char ** argv) // RedBlackSmoother<LatticeFermion,DomainWallFermionR> FineRBSmoother(0.00,0.001,100,Ddwf); // Wrap the 2nd level solver in a MultiGrid preconditioner acting on the fine space - ZeroGuesser<CoarseVector> CoarseZeroGuesser; + // ZeroGuesser<CoarseVector> CoarseZeroGuesser; TwoLevelMG TwoLevelPrecon(Aggregates, LDOp, HermIndefOp,Ddwf, FineSmoother, diff --git a/tests/solver/Test_dwf_hdcr_48_rb.cc b/tests/solver/Test_dwf_hdcr_48_rb.cc index a4d7bbb9..2b76681e 100644 --- a/tests/solver/Test_dwf_hdcr_48_rb.cc +++ b/tests/solver/Test_dwf_hdcr_48_rb.cc @@ -57,7 +57,7 @@ private: CheckerBoardedSparseMatrixBase<Field> & _Matrix; SchurRedBlackBase<Field> & _Solver; public: - + using LinearFunction<Field>::operator(); ///////////////////////////////////////////////////// // Wrap the usual normal equations trick ///////////////////////////////////////////////////// @@ -75,6 +75,7 @@ public: template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -98,6 +99,7 @@ public: template<class Field,class Matrix> class MirsSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & SmootherMatrix; FineOperator & SmootherOperator; @@ -128,6 +130,7 @@ public: template<class Fobj,class CComplex,int nbasis, class Matrix, class Guesser, class CoarseSolver> class MultiGridPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef CoarsenedMatrix<Fobj,CComplex,nbasis> CoarseOperator; diff --git a/tests/solver/Test_dwf_hdcr_48_regression.cc b/tests/solver/Test_dwf_hdcr_48_regression.cc index d07bd3a5..4dbb097b 100644 --- a/tests/solver/Test_dwf_hdcr_48_regression.cc +++ b/tests/solver/Test_dwf_hdcr_48_regression.cc @@ -55,6 +55,7 @@ RealD InverseApproximation(RealD x){ template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -78,6 +79,7 @@ public: template<class Field,class Matrix> class MirsSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & SmootherMatrix; FineOperator & SmootherOperator; @@ -108,6 +110,7 @@ public: template<class Fobj,class CComplex,int nbasis, class Matrix, class Guesser, class CoarseSolver> class MultiGridPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef CoarsenedMatrix<Fobj,CComplex,nbasis> CoarseOperator; diff --git a/tests/solver/Test_dwf_multigrid.cc b/tests/solver/Test_dwf_multigrid.cc index 351e10fd..e670b358 100644 --- a/tests/solver/Test_dwf_multigrid.cc +++ b/tests/solver/Test_dwf_multigrid.cc @@ -57,6 +57,7 @@ private: OperatorFunction<Field> & _Solver; LinearFunction<Field> & _Guess; public: + using LinearFunction<Field>::operator(); ///////////////////////////////////////////////////// // Wrap the usual normal equations trick @@ -118,6 +119,7 @@ RealD InverseApproximation(RealD x){ template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -174,6 +176,7 @@ public: template<class Fobj,class CComplex,int nbasis, class CoarseSolver> class HDCRPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef CoarsenedMatrix<Fobj,CComplex,nbasis> CoarseOperator; diff --git a/tests/solver/Test_hw_multigrid_mixed_48.cc b/tests/solver/Test_hw_multigrid_mixed_48.cc index 6b3b5f56..0e8d6a17 100644 --- a/tests/solver/Test_hw_multigrid_mixed_48.cc +++ b/tests/solver/Test_hw_multigrid_mixed_48.cc @@ -456,8 +456,8 @@ public: siteVector *CBp=Stencil.CommBuf(); - int ptype; - int nb2=nbasis/2; + // int ptype; + // int nb2=nbasis/2; autoView(in_v , in, AcceleratorRead); autoView(st, Stencil, AcceleratorRead); @@ -471,7 +471,7 @@ public: typedef decltype(coalescedRead(in_v[0])) calcVector; typedef decltype(coalescedRead(in_v[0](0))) calcComplex; int sU = sF/Ls; - int s = sF%Ls; + // int s = sF%Ls; calcComplex res = Zero(); calcVector nbr; @@ -517,14 +517,14 @@ public: autoView(st, Stencil, AcceleratorRead); siteVector *CBp=Stencil.CommBuf(); - int ptype; - int nb2=nbasis/2; + // int ptype; + // int nb2=nbasis/2; accelerator_for2d(sF, Coarse5D->oSites(), b, nbasis, Nsimd, { typedef decltype(coalescedRead(in_v[0])) calcVector; typedef decltype(coalescedRead(in_v[0](0))) calcComplex; int sU = sF/Ls; - int s = sF%Ls; + // int s = sF%Ls; calcComplex res = Zero(); @@ -650,7 +650,7 @@ private: OperatorFunction<Field> & _Solver; LinearFunction<Field> & _Guess; public: - + using LinearFunction<Field>::operator(); ///////////////////////////////////////////////////// // Wrap the usual normal equations trick ///////////////////////////////////////////////////// @@ -712,6 +712,7 @@ RealD InverseApproximation(RealD x){ template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -735,6 +736,7 @@ public: template<class Fobj,class CComplex,int nbasis, class CoarseSolver> class MGPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseVector CoarseVector; @@ -831,6 +833,7 @@ public: template<class Fobj,class CComplex,int nbasis, class CoarseSolver> class HDCRPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseVector CoarseVector; @@ -1174,18 +1177,18 @@ int main (int argc, char ** argv) PlainHermOp<CoarseCoarseVector> IRLOpL2 (IRLHermOpL2); ImplicitlyRestartedLanczos<CoarseCoarseVector> IRLL2(IRLOpChebyL2,IRLOpL2,cNstop,cNk,cNm,1.0e-3,20); - int cNconv; cNm=0; std::vector<RealD> eval2(cNm); std::vector<CoarseCoarseVector> evec2(cNm,CoarseCoarse5d); cc_src=1.0; + // int cNconv; // IRLL2.calc(eval2,evec2,cc_src,cNconv); ConjugateGradient<CoarseCoarseVector> CoarseCoarseCG(0.02,10000); DeflatedGuesser<CoarseCoarseVector> DeflCoarseCoarseGuesser(evec2,eval2); NormalEquations<CoarseCoarseVector> DeflCoarseCoarseCGNE(cc_Dwf,CoarseCoarseCG,DeflCoarseCoarseGuesser); - ZeroGuesser<CoarseVector> CoarseZeroGuesser; + // ZeroGuesser<CoarseVector> CoarseZeroGuesser; ZeroGuesser<CoarseCoarseVector> CoarseCoarseZeroGuesser; std::cout<<GridLogMessage << "**************************************************"<< std::endl; diff --git a/tests/solver/Test_hw_multigrid_mixed_48_rb.cc b/tests/solver/Test_hw_multigrid_mixed_48_rb.cc index 41257e94..e7ceb022 100644 --- a/tests/solver/Test_hw_multigrid_mixed_48_rb.cc +++ b/tests/solver/Test_hw_multigrid_mixed_48_rb.cc @@ -456,8 +456,8 @@ public: siteVector *CBp=Stencil.CommBuf(); - int ptype; - int nb2=nbasis/2; + //int ptype; + // int nb2=nbasis/2; autoView(in_v , in, AcceleratorRead); autoView(st, Stencil, AcceleratorRead); @@ -471,7 +471,7 @@ public: typedef decltype(coalescedRead(in_v[0])) calcVector; typedef decltype(coalescedRead(in_v[0](0))) calcComplex; int sU = sF/Ls; - int s = sF%Ls; + // int s = sF%Ls; calcComplex res = Zero(); calcVector nbr; @@ -517,14 +517,14 @@ public: autoView(st, Stencil, AcceleratorRead); siteVector *CBp=Stencil.CommBuf(); - int ptype; - int nb2=nbasis/2; + // int ptype; + // int nb2=nbasis/2; accelerator_for2d(sF, Coarse5D->oSites(), b, nbasis, Nsimd, { typedef decltype(coalescedRead(in_v[0])) calcVector; typedef decltype(coalescedRead(in_v[0](0))) calcComplex; int sU = sF/Ls; - int s = sF%Ls; + // int s = sF%Ls; calcComplex res = Zero(); @@ -648,7 +648,7 @@ private: CheckerBoardedSparseMatrixBase<Field> & _Matrix; SchurRedBlackBase<Field> & _Solver; public: - + using LinearFunction<Field>::operator(); ///////////////////////////////////////////////////// // Wrap the usual normal equations trick ///////////////////////////////////////////////////// @@ -669,6 +669,7 @@ private: OperatorFunction<Field> & _Solver; LinearFunction<Field> & _Guess; public: + using LinearFunction<Field>::operator(); ///////////////////////////////////////////////////// // Wrap the usual normal equations trick @@ -731,6 +732,7 @@ RealD InverseApproximation(RealD x){ template<class Field,class Matrix> class ChebyshevSmoother : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); typedef LinearOperatorBase<Field> FineOperator; Matrix & _SmootherMatrix; FineOperator & _SmootherOperator; @@ -754,6 +756,7 @@ public: template<class Fobj,class CComplex,int nbasis, class CoarseSolver> class MGPreconditioner : public LinearFunction< Lattice<Fobj> > { public: + using LinearFunction<Lattice<Fobj> >::operator(); typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseVector CoarseVector; @@ -850,7 +853,8 @@ public: template<class Fobj,class CComplex,int nbasis, class CoarseSolver> class HDCRPreconditioner : public LinearFunction< Lattice<Fobj> > { public: - + using LinearFunction<Lattice<Fobj> >::operator(); + typedef Aggregation<Fobj,CComplex,nbasis> Aggregates; typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseVector CoarseVector; typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseMatrix CoarseMatrix; @@ -1194,11 +1198,11 @@ int main (int argc, char ** argv) PlainHermOp<CoarseCoarseVector> IRLOpL2 (IRLHermOpL2); ImplicitlyRestartedLanczos<CoarseCoarseVector> IRLL2(IRLOpChebyL2,IRLOpL2,cNstop,cNk,cNm,1.0e-3,20); - int cNconv; cNm=0; std::vector<RealD> eval2(cNm); std::vector<CoarseCoarseVector> evec2(cNm,CoarseCoarse5d); cc_src=1.0; + // int cNconv; // IRLL2.calc(eval2,evec2,cc_src,cNconv); std::vector<RealD> tols ({0.005,0.001}); @@ -1218,10 +1222,10 @@ int main (int argc, char ** argv) for(auto c_hi : c_his ) { for(auto f_lo : f_los ) { for(auto f_hi : f_his ) { - ZeroGuesser<CoarseVector> CoarseZeroGuesser; - ZeroGuesser<CoarseCoarseVector> CoarseCoarseZeroGuesser; + // ZeroGuesser<CoarseVector> CoarseZeroGuesser; + // ZeroGuesser<CoarseCoarseVector> CoarseCoarseZeroGuesser; ConjugateGradient<CoarseCoarseVector> CoarseCoarseCG(tol,10000); - ZeroGuesser<CoarseCoarseVector> CoarseCoarseGuesser; + // ZeroGuesser<CoarseCoarseVector> CoarseCoarseGuesser; SchurRedBlackDiagMooeeSolve<CoarseCoarseVector> CoarseCoarseRBCG(CoarseCoarseCG); SchurSolverWrapper<CoarseCoarseVector> CoarseCoarseSolver(cc_Dwf,CoarseCoarseRBCG); diff --git a/tests/solver/Test_multigrid_common.h b/tests/solver/Test_multigrid_common.h index 22743b72..0cb63530 100644 --- a/tests/solver/Test_multigrid_common.h +++ b/tests/solver/Test_multigrid_common.h @@ -143,6 +143,7 @@ public: template<class Field> class MultiGridPreconditionerBase : public LinearFunction<Field> { public: + using LinearFunction<Field>::operator(); virtual ~MultiGridPreconditionerBase() = default; virtual void setup() = 0; virtual void operator()(Field const &in, Field &out) = 0; @@ -156,6 +157,7 @@ public: ///////////////////////////////////////////// // Type Definitions ///////////////////////////////////////////// + using MultiGridPreconditionerBase<Lattice<Fobj>>::operator(); // clang-format off typedef Aggregation<Fobj, CComplex, nBasis> Aggregates; @@ -568,6 +570,7 @@ public: ///////////////////////////////////////////// // Type Definitions ///////////////////////////////////////////// + using MultiGridPreconditionerBase<Lattice<Fobj>>::operator(); typedef Matrix FineDiracMatrix; typedef Lattice<Fobj> FineVector; diff --git a/tests/solver/Test_wilson_qmr_unprec.cc b/tests/solver/Test_wilson_qmr_unprec.cc index 45e241e6..c0b42a28 100644 --- a/tests/solver/Test_wilson_qmr_unprec.cc +++ b/tests/solver/Test_wilson_qmr_unprec.cc @@ -56,7 +56,6 @@ int main (int argc, char ** argv) QuasiMinimalResidual<LatticeFermion> QMR(1.0e-8,10000); RealD mass=0.0; - RealD M5=1.8; WilsonFermionR Dw(Umu,*Grid,*rbGrid,mass); NonHermitianLinearOperator<WilsonFermionR,LatticeFermion> NonHermOp(Dw); From 44204c7e06469eb13fb4c52c0a6c4f1f7508fef2 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Fri, 29 Oct 2021 02:02:56 +0100 Subject: [PATCH 256/399] Extra code --- examples/Example_wall_wall_3pt.cc | 539 ++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 examples/Example_wall_wall_3pt.cc diff --git a/examples/Example_wall_wall_3pt.cc b/examples/Example_wall_wall_3pt.cc new file mode 100644 index 00000000..9ea38260 --- /dev/null +++ b/examples/Example_wall_wall_3pt.cc @@ -0,0 +1,539 @@ +/* + * Warning: This code illustrative only: not well tested, and not meant for production use + * without regression / tests being applied + */ + +#include <Grid/Grid.h> + +using namespace std; +using namespace Grid; +typedef SpinColourMatrix Propagator; +typedef SpinColourVector Fermion; +typedef PeriodicGimplR GimplR; + +template<class Gimpl,class Field> class CovariantLaplacianCshift : public SparseMatrixBase<Field> +{ +public: + INHERIT_GIMPL_TYPES(Gimpl); + + GridBase *grid; + GaugeField U; + + CovariantLaplacianCshift(GaugeField &_U) : + grid(_U.Grid()), + U(_U) { }; + + virtual GridBase *Grid(void) { return grid; }; + + virtual void M (const Field &in, Field &out) + { + out=Zero(); + for(int mu=0;mu<Nd-1;mu++) { + GaugeLinkField Umu = PeekIndex<LorentzIndex>(U, mu); // NB: Inefficent + out = out - Gimpl::CovShiftForward(Umu,mu,in); + out = out - Gimpl::CovShiftBackward(Umu,mu,in); + out = out + 2.0*in; + } + }; + virtual void Mdag (const Field &in, Field &out) { M(in,out);}; // Laplacian is hermitian + virtual void Mdiag (const Field &in, Field &out) {assert(0);}; // Unimplemented need only for multigrid + virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; // Unimplemented need only for multigrid + virtual void MdirAll (const Field &in, std::vector<Field> &out) {assert(0);}; // Unimplemented need only for multigrid +}; + +void MakePhase(Coordinate mom,LatticeComplex &phase) +{ + GridBase *grid = phase.Grid(); + auto latt_size = grid->GlobalDimensions(); + ComplexD ci(0.0,1.0); + phase=Zero(); + + LatticeComplex coor(phase.Grid()); + for(int mu=0;mu<Nd;mu++){ + RealD TwoPiL = M_PI * 2.0/ latt_size[mu]; + LatticeCoordinate(coor,mu); + phase = phase + (TwoPiL * mom[mu]) * coor; + } + phase = exp(phase*ci); +} +void LinkSmear(int nstep, RealD rho,LatticeGaugeField &Uin,LatticeGaugeField &Usmr) +{ + Smear_Stout<GimplR> Stout(rho); + LatticeGaugeField Utmp(Uin.Grid()); + Utmp = Uin; + for(int i=0;i<nstep;i++){ + Stout.smear(Usmr,Utmp); + Utmp = Usmr; + } +} +void PointSource(Coordinate &coor,LatticePropagator &source) +{ + // Coordinate coor({0,0,0,0}); + source=Zero(); + SpinColourMatrix kronecker; kronecker=1.0; + pokeSite(kronecker,source,coor); +} +void GFWallSource(int tslice,LatticePropagator &source) +{ + GridBase *grid = source.Grid(); + LatticeComplex one(grid); one = ComplexD(1.0,0.0); + LatticeComplex zz(grid); zz=Zero(); + LatticeInteger t(grid); + LatticeCoordinate(t,Tdir); + one = where(t==Integer(tslice), one, zz); + source = 1.0; + source = source * one; +} + +void Z2WallSource(GridParallelRNG &RNG,int tslice,LatticePropagator &source) +{ + GridBase *grid = source.Grid(); + LatticeComplex noise(grid); + LatticeComplex zz(grid); zz=Zero(); + LatticeInteger t(grid); + + RealD nrm=1.0/sqrt(2); + bernoulli(RNG, noise); // 0,1 50:50 + + noise = (2.*noise - Complex(1,1))*nrm; + + LatticeCoordinate(t,Tdir); + noise = where(t==Integer(tslice), noise, zz); + + source = 1.0; + source = source*noise; + std::cout << " Z2 wall " << norm2(source) << std::endl; +} +void GaugeFix(LatticeGaugeField &U,LatticeGaugeField &Ufix) +{ + Real alpha=0.05; + + Real plaq=WilsonLoops<GimplR>::avgPlaquette(U); + + std::cout << " Initial plaquette "<<plaq << std::endl; + + LatticeColourMatrix xform(U.Grid()); + Ufix = U; + int orthog=Nd-1; + FourierAcceleratedGaugeFixer<GimplR>::SteepestDescentGaugeFix(Ufix,xform,alpha,100000,1.0e-14, 1.0e-14,true,orthog); + + plaq=WilsonLoops<GimplR>::avgPlaquette(Ufix); + + std::cout << " Final plaquette "<<plaq << std::endl; +} +template<class Field> +void GaussianSmear(LatticeGaugeField &U,Field &unsmeared,Field &smeared) +{ + typedef CovariantLaplacianCshift <GimplR,Field> Laplacian_t; + Laplacian_t Laplacian(U); + + Integer Iterations = 40; + Real width = 2.0; + Real coeff = (width*width) / Real(4*Iterations); + + Field tmp(U.Grid()); + smeared=unsmeared; + // chi = (1-p^2/2N)^N kronecker + for(int n = 0; n < Iterations; ++n) { + Laplacian.M(smeared,tmp); + smeared = smeared - coeff*tmp; + std::cout << " smear iter " << n<<" " <<norm2(smeared)<<std::endl; + } +} +void GaussianSource(Coordinate &site,LatticeGaugeField &U,LatticePropagator &source) +{ + LatticePropagator tmp(source.Grid()); + PointSource(site,source); + std::cout << " GaussianSource Kronecker "<< norm2(source)<<std::endl; + tmp = source; + GaussianSmear(U,tmp,source); + std::cout << " GaussianSource Smeared "<< norm2(source)<<std::endl; +} +void GaussianWallSource(GridParallelRNG &RNG,int tslice,LatticeGaugeField &U,LatticePropagator &source) +{ + Z2WallSource(RNG,tslice,source); + auto tmp = source; + GaussianSmear(U,tmp,source); +} +void SequentialSource(int tslice,Coordinate &mom,LatticePropagator &spectator,LatticePropagator &source) +{ + assert(mom.size()==Nd); + assert(mom[Tdir] == 0); + + GridBase * grid = spectator.Grid(); + + LatticeInteger ts(grid); + LatticeCoordinate(ts,Tdir); + source = Zero(); + source = where(ts==Integer(tslice),spectator,source); // Stick in a slice of the spectator, zero everywhere else + + LatticeComplex phase(grid); + MakePhase(mom,phase); + + source = source *phase; +} +template<class Action> +void Solve(Action &D,LatticePropagator &source,LatticePropagator &propagator) +{ + GridBase *UGrid = D.GaugeGrid(); + GridBase *FGrid = D.FermionGrid(); + + LatticeFermion src4 (UGrid); + LatticeFermion src5 (FGrid); + LatticeFermion result5(FGrid); + LatticeFermion result4(UGrid); + + ConjugateGradient<LatticeFermion> CG(1.0e-12,100000); + SchurRedBlackDiagTwoSolve<LatticeFermion> schur(CG); + ZeroGuesser<LatticeFermion> ZG; // Could be a DeflatedGuesser if have eigenvectors + for(int s=0;s<Nd;s++){ + for(int c=0;c<Nc;c++){ + PropToFerm<Action>(src4,source,s,c); + + D.ImportPhysicalFermionSource(src4,src5); + + result5=Zero(); + schur(D,src5,result5,ZG); + std::cout<<GridLogMessage + <<"spin "<<s<<" color "<<c + <<" norm2(src5d) " <<norm2(src5) + <<" norm2(result5d) "<<norm2(result5)<<std::endl; + + D.ExportPhysicalFermionSolution(result5,result4); + + FermToProp<Action>(propagator,result4,s,c); + } + } +} + +class MesonFile: Serializable { +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(MesonFile, std::vector<std::vector<Complex> >, data); +}; + +void MesonTrace(std::string file,LatticePropagator &q1,LatticePropagator &q2,LatticeComplex &phase) +{ + const int nchannel=4; + Gamma::Algebra Gammas[nchannel][2] = { + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::GammaTGamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaTGamma5} + }; + + Gamma G5(Gamma::Algebra::Gamma5); + + LatticeComplex meson_CF(q1.Grid()); + MesonFile MF; + + for(int ch=0;ch<nchannel;ch++){ + + Gamma Gsrc(Gammas[ch][0]); + Gamma Gsnk(Gammas[ch][1]); + + meson_CF = trace(G5*adj(q1)*G5*Gsnk*q2*adj(Gsrc)); + + std::vector<TComplex> meson_T; + sliceSum(meson_CF,meson_T, Tdir); + + int nt=meson_T.size(); + + std::vector<Complex> corr(nt); + for(int t=0;t<nt;t++){ + corr[t] = TensorRemove(meson_T[t]); // Yes this is ugly, not figured a work around + std::cout << " channel "<<ch<<" t "<<t<<" " <<corr[t]<<std::endl; + } + MF.data.push_back(corr); + } + + { + XmlWriter WR(file); + write(WR,"MesonFile",MF); + } +} + +void Meson3pt(std::string file,LatticePropagator &q1,LatticePropagator &q2,LatticeComplex &phase) +{ + const int nchannel=4; + Gamma::Algebra Gammas[nchannel][2] = { + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaX}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaY}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaZ}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaT} + }; + + Gamma G5(Gamma::Algebra::Gamma5); + + LatticeComplex meson_CF(q1.Grid()); + MesonFile MF; + + for(int ch=0;ch<nchannel;ch++){ + + Gamma Gsrc(Gammas[ch][0]); + Gamma Gsnk(Gammas[ch][1]); + + meson_CF = trace(G5*adj(q1)*G5*Gsnk*q2*adj(Gsrc)); + + std::vector<TComplex> meson_T; + sliceSum(meson_CF,meson_T, Tdir); + + int nt=meson_T.size(); + + std::vector<Complex> corr(nt); + for(int t=0;t<nt;t++){ + corr[t] = TensorRemove(meson_T[t]); // Yes this is ugly, not figured a work around + std::cout << " channel "<<ch<<" t "<<t<<" " <<corr[t]<<std::endl; + } + MF.data.push_back(corr); + } + + { + XmlWriter WR(file); + write(WR,"MesonFile",MF); + } +} + + +void WallSinkMesonTrace(std::string file,std::vector<Propagator> &q1,std::vector<Propagator> &q2) +{ + const int nchannel=4; + Gamma::Algebra Gammas[nchannel][2] = { + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::GammaTGamma5}, + {Gamma::Algebra::GammaTGamma5,Gamma::Algebra::Gamma5}, + {Gamma::Algebra::Gamma5 ,Gamma::Algebra::GammaTGamma5} + }; + + Gamma G5(Gamma::Algebra::Gamma5); + int nt=q1.size(); + std::vector<Complex> meson_CF(nt); + MesonFile MF; + + for(int ch=0;ch<nchannel;ch++){ + + Gamma Gsrc(Gammas[ch][0]); + Gamma Gsnk(Gammas[ch][1]); + + std::vector<Complex> corr(nt); + for(int t=0;t<nt;t++){ + meson_CF[t] = trace(G5*adj(q1[t])*G5*Gsnk*q2[t]*adj(Gsrc)); + corr[t] = TensorRemove(meson_CF[t]); // Yes this is ugly, not figured a work around + std::cout << " channel "<<ch<<" t "<<t<<" " <<corr[t]<<std::endl; + } + MF.data.push_back(corr); + } + + { + XmlWriter WR(file); + write(WR,"MesonFile",MF); + } +} +int make_idx(int p, int m,int nmom) +{ + if (m==0) return p; + assert(p==0); + return nmom + m - 1; +} + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + // Double precision grids + auto latt = GridDefaultLatt(); + GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), + GridDefaultSimd(Nd,vComplex::Nsimd()), + GridDefaultMpi()); + GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); + + + LatticeGaugeField Umu(UGrid); + LatticeGaugeField Utmp(UGrid); + LatticeGaugeField Usmr(UGrid); + std::string config; + if( argc > 1 && argv[1][0] != '-' ) + { + std::cout<<GridLogMessage <<"Loading configuration from "<<argv[1]<<std::endl; + FieldMetaData header; + NerscIO::readConfiguration(Umu, header, argv[1]); + config=argv[1]; + } + else + { + std::cout<<GridLogMessage <<"Using hot configuration"<<std::endl; + SU<Nc>::ColdConfiguration(Umu); + config="ColdConfig"; + } + // GaugeFix(Umu,Utmp); + // Umu=Utmp; + + int nsmr=3; + RealD rho=0.1; + LinkSmear(nsmr,rho,Umu,Usmr); + + + std::vector<int> smeared_link({ 0,0,1} ); + std::vector<RealD> masses({ 0.004,0.02477,0.447} ); // u/d, s, c ?? + std::vector<RealD> M5s ({ 1.8,1.8,1.0} ); + std::vector<RealD> bs ({ 1.0,1.0,1.5} ); // DDM + std::vector<RealD> cs ({ 0.0,0.0,0.5} ); // DDM + std::vector<int> Ls_s ({ 16,16,12} ); + std::vector<GridCartesian *> FGrids; + std::vector<GridRedBlackCartesian *> FrbGrids; + + std::vector<Coordinate> momenta; + momenta.push_back(Coordinate({0,0,0,0})); + momenta.push_back(Coordinate({1,0,0,0})); + momenta.push_back(Coordinate({2,0,0,0})); + + int nmass = masses.size(); + int nmom = momenta.size(); + + std::vector<MobiusFermionR *> FermActs; + + std::cout<<GridLogMessage <<"======================"<<std::endl; + std::cout<<GridLogMessage <<"MobiusFermion action as Scaled Shamir kernel"<<std::endl; + std::cout<<GridLogMessage <<"======================"<<std::endl; + + std::vector<Complex> boundary = {1,1,1,-1}; + typedef MobiusFermionR FermionAction; + FermionAction::ImplParams Params(boundary); + + for(int m=0;m<masses.size();m++) { + + RealD mass = masses[m]; + RealD M5 = M5s[m]; + RealD b = bs[m]; + RealD c = cs[m]; + int Ls = Ls_s[m]; + + if ( smeared_link[m] ) Utmp = Usmr; + else Utmp = Umu; + + FGrids.push_back(SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid)); + FrbGrids.push_back(SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid)); + + FermActs.push_back(new MobiusFermionR(Utmp,*FGrids[m],*FrbGrids[m],*UGrid,*UrbGrid,mass,M5,b,c,Params)); + } + + LatticePropagator z2wall_source(UGrid); + LatticePropagator gfwall_source(UGrid); + LatticePropagator phased_prop(UGrid); + + int tslice = 0; + int tseq=(tslice+16)%latt[Nd-1]; + ////////////////////////////////////////////////////////////////////// + // RNG seeded for Z2 wall + ////////////////////////////////////////////////////////////////////// + // You can manage seeds however you like. + // Recommend SeedUniqueString. + ////////////////////////////////////////////////////////////////////// + GridParallelRNG RNG4(UGrid); RNG4.SeedUniqueString("Study2-Source_Z2_p_0_0_0_t_0-880"); + Z2WallSource (RNG4,tslice,z2wall_source); + GFWallSource (tslice,gfwall_source); + + std::vector<LatticeComplex> phase(nmom,UGrid); + for(int m=0;m<nmom;m++){ + MakePhase(momenta[m],phase[m]); + } + + std::vector<LatticePropagator> Z2Props (nmom+nmass-1,UGrid); + std::vector<LatticePropagator> GFProps (nmom+nmass-1,UGrid); + for(int p=0;p<nmom;p++) { + int m=0; + int idx = make_idx(p,m,nmom); + phased_prop = z2wall_source * phase[p]; + Solve(*FermActs[m],phased_prop ,Z2Props[idx]); + + phased_prop = gfwall_source * phase[p]; + Solve(*FermActs[m],phased_prop ,GFProps[idx]); + } + for(int m=1;m<nmass;m++) { + int p=0; + int idx = make_idx(p,m,nmom); + phased_prop = z2wall_source; + Solve(*FermActs[m],phased_prop ,Z2Props[idx]); + + phased_prop = gfwall_source; + Solve(*FermActs[m],phased_prop ,GFProps[idx]); + } + + std::vector<std::vector<Propagator> > wsnk_z2Props(nmom+nmass-1); + std::vector<std::vector<Propagator> > wsnk_gfProps(nmom+nmass-1); + + // Non-zero kaon and point and D two point + // WW stick momentum on m1 (lighter) + // zero momentum on m2 + for(int m1=0;m1<nmass;m1++) { + for(int m2=m1;m2<nmass;m2++) { + int pmax = (m1==0)? nmom:1; + for(int p=0;p<pmax;p++){ + + std::stringstream ssg,ssz; + std::stringstream wssg,wssz; + + int idx1 = make_idx(p,m1,nmom); + int idx2 = make_idx(0,m2,nmom); + + /// Point sinks + ssg<<config<<"_p"<<p<< "_m" << m1 << "_m"<< m2 << "_p_gf_meson.xml"; + ssz<<config<<"_p"<<p<< "_m" << m1 << "_m"<< m2 << "_p_z2_meson.xml"; + MesonTrace(ssz.str(),Z2Props[idx1],Z2Props[idx2],phase[p]); // Q1 is conjugated + MesonTrace(ssg.str(),GFProps[idx1],GFProps[idx2],phase[p]); + + /// Wall sinks + wssg<<config<<"_p"<<p<< "_m" << m1 << "_m"<< m2 << "_w_gf_meson.xml"; + wssz<<config<<"_p"<<p<< "_m" << m1 << "_m"<< m2 << "_w_z2_meson.xml"; + + phased_prop = GFProps[m2] * phase[p]; + sliceSum(phased_prop,wsnk_gfProps[m1],Tdir); + sliceSum(GFProps[m1],wsnk_gfProps[m2],Tdir); + WallSinkMesonTrace(wssg.str(),wsnk_gfProps[m1],wsnk_gfProps[m2]); + + phased_prop = Z2Props[m2] * phase[p]; + sliceSum(phased_prop,wsnk_gfProps[m1],Tdir); + sliceSum(Z2Props[m1],wsnk_gfProps[m2],Tdir); + WallSinkMesonTrace(wssz.str(),wsnk_z2Props[m1],wsnk_z2Props[m2]); + } + }} + + + ///////////////////////////////////// + // Sequential solves + ///////////////////////////////////// + LatticePropagator seq_wsnk_z2src(UGrid); + LatticePropagator seq_wsnk_gfsrc(UGrid); + LatticePropagator seq_psnk_z2src(UGrid); + LatticePropagator seq_psnk_gfsrc(UGrid); + LatticePropagator source(UGrid); + for(int m=0;m<nmass-1;m++){ + int spect_idx = make_idx(0,m,nmom); + int charm=nmass-1; + + SequentialSource(tseq,momenta[0],GFProps[spect_idx],source); + Solve(*FermActs[charm],source,seq_psnk_gfsrc); + + SequentialSource(tseq,momenta[0],Z2Props[spect_idx],source); + Solve(*FermActs[charm],source,seq_psnk_z2src); + + // Todo need wall sequential solve + for(int p=0;p<nmom;p++){ + int active_idx = make_idx(p,0,nmom); + std::stringstream seq_3pt_p_z2; + std::stringstream seq_3pt_p_gf; + std::stringstream seq_3pt_w_z2; + std::stringstream seq_3pt_w_gf; + seq_3pt_p_z2 <<config<<"_3pt_p"<<p<< "_m" << m << "_p_z2_meson.xml"; + seq_3pt_p_gf <<config<<"_3pt_p"<<p<< "_m" << m << "_p_gf_meson.xml"; + seq_3pt_w_z2 <<config<<"_3pt_p"<<p<< "_m" << m << "_w_z2_meson.xml"; + seq_3pt_w_gf <<config<<"_3pt_p"<<p<< "_m" << m << "_w_gf_meson.xml"; + Meson3pt(seq_3pt_p_gf.str(),GFProps[active_idx],seq_psnk_gfsrc,phase[p]); + Meson3pt(seq_3pt_p_z2.str(),Z2Props[active_idx],seq_psnk_z2src,phase[p]); + } + } + + Grid_finalize(); +} + + + From 0b905a72ddfafcf02bcc4b6738ff31c74be79ed5 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Fri, 29 Oct 2021 02:22:22 +0100 Subject: [PATCH 257/399] Better reduction for GPUs --- Grid/lattice/Lattice_reduction_gpu.h | 48 ++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/Grid/lattice/Lattice_reduction_gpu.h b/Grid/lattice/Lattice_reduction_gpu.h index c2875052..823e497e 100644 --- a/Grid/lattice/Lattice_reduction_gpu.h +++ b/Grid/lattice/Lattice_reduction_gpu.h @@ -23,7 +23,7 @@ unsigned int nextPow2(Iterator x) { } template <class Iterator> -void getNumBlocksAndThreads(const Iterator n, const size_t sizeofsobj, Iterator &threads, Iterator &blocks) { +int getNumBlocksAndThreads(const Iterator n, const size_t sizeofsobj, Iterator &threads, Iterator &blocks) { int device; #ifdef GRID_CUDA @@ -37,13 +37,13 @@ void getNumBlocksAndThreads(const Iterator n, const size_t sizeofsobj, Iterator Iterator sharedMemPerBlock = gpu_props[device].sharedMemPerBlock; Iterator maxThreadsPerBlock = gpu_props[device].maxThreadsPerBlock; Iterator multiProcessorCount = gpu_props[device].multiProcessorCount; - + /* std::cout << GridLogDebug << "GPU has:" << std::endl; std::cout << GridLogDebug << "\twarpSize = " << warpSize << std::endl; std::cout << GridLogDebug << "\tsharedMemPerBlock = " << sharedMemPerBlock << std::endl; std::cout << GridLogDebug << "\tmaxThreadsPerBlock = " << maxThreadsPerBlock << std::endl; std::cout << GridLogDebug << "\tmultiProcessorCount = " << multiProcessorCount << std::endl; - + */ if (warpSize != WARP_SIZE) { std::cout << GridLogError << "The warp size of the GPU in use does not match the warp size set when compiling Grid." << std::endl; exit(EXIT_FAILURE); @@ -53,12 +53,12 @@ void getNumBlocksAndThreads(const Iterator n, const size_t sizeofsobj, Iterator threads = warpSize; if ( threads*sizeofsobj > sharedMemPerBlock ) { std::cout << GridLogError << "The object is too large for the shared memory." << std::endl; - exit(EXIT_FAILURE); + return 0; } while( 2*threads*sizeofsobj < sharedMemPerBlock && 2*threads <= maxThreadsPerBlock ) threads *= 2; // keep all the streaming multiprocessors busy blocks = nextPow2(multiProcessorCount); - + return 1; } template <class sobj, class Iterator> @@ -198,7 +198,7 @@ __global__ void reduceKernel(const vobj *lat, sobj *buffer, Iterator n) { // Possibly promote to double and sum ///////////////////////////////////////////////////////////////////////////////////////////////////////// template <class vobj> -inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) +inline typename vobj::scalar_objectD sumD_gpu_internal(const vobj *lat, Integer osites) { typedef typename vobj::scalar_objectD sobj; typedef decltype(lat) Iterator; @@ -208,6 +208,7 @@ inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) Integer numThreads, numBlocks; getNumBlocksAndThreads(size, sizeof(sobj), numThreads, numBlocks); + Integer smemSize = numThreads * sizeof(sobj); Vector<sobj> buffer(numBlocks); @@ -218,6 +219,41 @@ inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) auto result = buffer_v[0]; return result; } +template <class vobj> +inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) +{ + typedef typename vobj::vector_type vector; + typedef typename vobj::scalar_typeD scalarD; + typedef typename vobj::scalar_objectD sobj; + sobj ret; + scalarD *ret_p = (scalarD *)&ret; + + const int words = sizeof(vobj)/sizeof(vector); + + Integer nsimd= vobj::Nsimd(); + Integer size = osites*nsimd; + Integer numThreads, numBlocks; + int ok = getNumBlocksAndThreads(size, sizeof(sobj), numThreads, numBlocks); + + if ( ok ) { + ret = sumD_gpu_internal(lat,osites); + } else { + std::cout << GridLogWarning << " dropping to summing word by word for large object size "<<sizeof(vobj)<<std::endl; + Vector<vector> buffer(osites); + vector *dat = (vector *)lat; + vector *buf = &buffer[0]; + iScalar<vector> *tbuf =(iScalar<vector> *) &buffer[0]; + for(int w=0;w<words;w++) { + + accelerator_for(ss,osites,1,{ + buf[ss] = dat[ss*words+w]; + }); + + ret_p[w] = sumD_gpu_internal(tbuf,osites); + } + } + return ret; +} ///////////////////////////////////////////////////////////////////////////////////////////////////////// // Return as same precision as input performing reduction in double precision though ///////////////////////////////////////////////////////////////////////////////////////////////////////// From 42d56ea6b63778ba4013cd0da0d325b5619f23cb Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Fri, 29 Oct 2021 02:23:08 +0100 Subject: [PATCH 258/399] Verbosity --- Grid/lattice/Lattice_reduction_gpu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Grid/lattice/Lattice_reduction_gpu.h b/Grid/lattice/Lattice_reduction_gpu.h index 823e497e..73a704f5 100644 --- a/Grid/lattice/Lattice_reduction_gpu.h +++ b/Grid/lattice/Lattice_reduction_gpu.h @@ -238,7 +238,6 @@ inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) if ( ok ) { ret = sumD_gpu_internal(lat,osites); } else { - std::cout << GridLogWarning << " dropping to summing word by word for large object size "<<sizeof(vobj)<<std::endl; Vector<vector> buffer(osites); vector *dat = (vector *)lat; vector *buf = &buffer[0]; From b27b12828e34fafd8b44a80850d726cc0256892e Mon Sep 17 00:00:00 2001 From: Antonin Portelli <antonin.portelli@me.com> Date: Fri, 29 Oct 2021 13:01:31 +0100 Subject: [PATCH 259/399] reverse previous "fix", missing statement was probably intentional, added a comment to that effect --- Grid/parallelIO/IldgIO.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Grid/parallelIO/IldgIO.h b/Grid/parallelIO/IldgIO.h index 9ed773dd..576f08bd 100644 --- a/Grid/parallelIO/IldgIO.h +++ b/Grid/parallelIO/IldgIO.h @@ -576,7 +576,8 @@ class ScidacReader : public GridLimeReader { std::string rec_name(ILDG_BINARY_DATA); while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) { if ( !strncmp(limeReaderType(LimeR), rec_name.c_str(),strlen(rec_name.c_str()) ) ) { - skipPastObjectRecord(std::string(GRID_FIELD_NORM)); + // in principle should do the line below, but that breaks backard compatibility with old data + // skipPastObjectRecord(std::string(GRID_FIELD_NORM)); skipPastObjectRecord(std::string(SCIDAC_CHECKSUM)); return; } From 2ad181164266000e04fa109b656ca1922b5ac3a4 Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Tue, 9 Nov 2021 12:33:25 +0000 Subject: [PATCH 260/399] Added timing to deflation code. --- Grid/algorithms/iterative/Deflation.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index 814a0432..83ec1ab0 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -75,24 +75,19 @@ public: virtual void operator()(const Field &src,Field &guess) { GridStopWatch w1; - GridStopWatch w2; - w1.Start(); guess = Zero(); - w1.Stop(); - LOG(Message) << "Zeroing the 'out' vector took: " << w1.Elapsed() << std::endl; - - w2.Start(); + w1.Start(); for (int i=0;i<N;i++) { const Field& tmp = evec[i]; axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess); } - w2.Stop(); + w1.Stop(); guess.Checkerboard() = src.Checkerboard(); - LOG(Message) << "This projection took: " << w2.Elapsed() << std::endl; + std::cout << GridLogDebug << "This projection took: " << w1.Elapsed() << std::endl; } }; From d7bef70b5cb322dbf25ff2a41a7f7ab58eba2bbc Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Tue, 9 Nov 2021 12:57:09 +0000 Subject: [PATCH 261/399] Helper functions to allow probe of cache state of lattice objects. --- Grid/allocator/MemoryManager.h | 1 + Grid/allocator/MemoryManagerCache.cc | 29 +++++++++++++++++++++++++++ Grid/allocator/MemoryManagerShared.cc | 4 ++++ Grid/lattice/Lattice_base.h | 7 +++++++ 4 files changed, 41 insertions(+) diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index eafcd83f..740d8d92 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -170,6 +170,7 @@ private: public: static void Print(void); + static void PrintState( void* CpuPtr); static int isOpen (void* CpuPtr); static void ViewClose(void* CpuPtr,ViewMode mode); static void *ViewOpen (void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint); diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index 72111dbd..61dc1125 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -474,6 +474,35 @@ int MemoryManager::isOpen (void* _CpuPtr) } } +void MemoryManager::PrintState(void* _CpuPtr) +{ + uint64_t CpuPtr = (uint64_t)_CpuPtr; + + if ( EntryPresent(CpuPtr) ){ + auto AccCacheIterator = EntryLookup(CpuPtr); + auto & AccCache = AccCacheIterator->second; + std::string str; + if ( AccCache.state==Empty ) str = std::string("Empty"); + if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty"); + if ( AccCache.state==AccDirty ) str = std::string("AccDirty"); + if ( AccCache.state==Consistent)str = std::string("Consistent"); + if ( AccCache.state==EvictNext) str = std::string("EvictNext"); + + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; + std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; + std::cout << GridLogMessage << "0x"<<std::hex<<AccCache.CpuPtr<<std::dec + << "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str + << "\t" << AccCache.cpuLock + << "\t" << AccCache.accLock + << "\t" << AccCache.LRU_valid<<std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; + + } else { + std::cout << GridLogMessage << "No Entry in AccCache table." << std::endl; + } +} + NAMESPACE_END(Grid); #endif diff --git a/Grid/allocator/MemoryManagerShared.cc b/Grid/allocator/MemoryManagerShared.cc index 3f165007..c072873b 100644 --- a/Grid/allocator/MemoryManagerShared.cc +++ b/Grid/allocator/MemoryManagerShared.cc @@ -16,6 +16,10 @@ uint64_t MemoryManager::DeviceToHostXfer; void MemoryManager::ViewClose(void* AccPtr,ViewMode mode){}; void *MemoryManager::ViewOpen(void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint){ return CpuPtr; }; int MemoryManager::isOpen (void* CpuPtr) { return 0;} +void MemoryManager::PrintState(void* CpuPtr) +{ +std::cout << GridLogMessage << "Host<->Device memory movement not currently managed by Grid." << std::endl; +}; void MemoryManager::Print(void){}; void MemoryManager::NotifyDeletion(void *ptr){}; diff --git a/Grid/lattice/Lattice_base.h b/Grid/lattice/Lattice_base.h index 3ad9f913..9c3d723f 100644 --- a/Grid/lattice/Lattice_base.h +++ b/Grid/lattice/Lattice_base.h @@ -88,6 +88,13 @@ public: LatticeView<vobj> accessor(*( (LatticeAccelerator<vobj> *) this),mode); accessor.ViewClose(); } + + // Helper function to print the state of this object in the AccCache + void PrintCacheState(void) + { + MemoryManager::PrintState(this->_odata); + } + ///////////////////////////////////////////////////////////////////////////////// // Return a view object that may be dereferenced in site loops. // The view is trivially copy constructible and may be copied to an accelerator device From 829a32845171e444415827f3800d6d7d45b5c9ec Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Tue, 9 Nov 2021 20:46:57 +0000 Subject: [PATCH 262/399] remove deflation timing --- Grid/algorithms/iterative/Deflation.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index 83ec1ab0..9f85ac70 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -74,21 +74,11 @@ public: } virtual void operator()(const Field &src,Field &guess) { - GridStopWatch w1; - guess = Zero(); - - w1.Start(); for (int i=0;i<N;i++) { const Field& tmp = evec[i]; axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess); - } - w1.Stop(); - guess.Checkerboard() = src.Checkerboard(); - - std::cout << GridLogDebug << "This projection took: " << w1.Elapsed() << std::endl; - } }; From 12ef4130656001f9ebea862cda7d195b900a7e89 Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Tue, 9 Nov 2021 21:20:36 +0000 Subject: [PATCH 263/399] fix to deflation.h --- Grid/algorithms/iterative/Deflation.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index 9f85ac70..2eb28bf9 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -78,6 +78,7 @@ public: for (int i=0;i<N;i++) { const Field& tmp = evec[i]; axpy(guess,TensorRemove(innerProduct(tmp,src)) / eval[i],tmp,guess); + } guess.Checkerboard() = src.Checkerboard(); } }; From 41a575ff9b5d630d4b75e1e9dd0edf19e05f1c37 Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Tue, 9 Nov 2021 21:56:23 +0000 Subject: [PATCH 264/399] Format edit --- Grid/allocator/MemoryManagerCache.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index 61dc1125..04b3fe95 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -488,15 +488,12 @@ void MemoryManager::PrintState(void* _CpuPtr) if ( AccCache.state==Consistent)str = std::string("Consistent"); if ( AccCache.state==EvictNext) str = std::string("EvictNext"); - std::cout << GridLogMessage << "--------------------------------------------" << std::endl; std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl; - std::cout << GridLogMessage << "--------------------------------------------" << std::endl; std::cout << GridLogMessage << "0x"<<std::hex<<AccCache.CpuPtr<<std::dec << "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str << "\t" << AccCache.cpuLock << "\t" << AccCache.accLock << "\t" << AccCache.LRU_valid<<std::endl; - std::cout << GridLogMessage << "--------------------------------------------" << std::endl; } else { std::cout << GridLogMessage << "No Entry in AccCache table." << std::endl; From 76cde73705a33e450c69eb840b70a6d7cd916394 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 20:44:39 -0500 Subject: [PATCH 265/399] HIP improvements on messaging and intranode hipMemCopyAsynch --- Grid/threads/Accelerator.cc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 9f27f12b..240758b3 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -84,7 +84,8 @@ void acceleratorInit(void) // IBM Jsrun makes cuda Device numbering screwy and not match rank if ( world_rank == 0 ) { printf("AcceleratorCudaInit: using default device \n"); - printf("AcceleratorCudaInit: assume user either uses a) IBM jsrun, or \n"); + printf("AcceleratorCudaInit: assume user either uses\n"); + printf("AcceleratorCudaInit: a) IBM jsrun, or \n"); printf("AcceleratorCudaInit: b) invokes through a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding \n"); printf("AcceleratorCudaInit: Configure options --enable-setdevice=no \n"); } @@ -109,6 +110,7 @@ void acceleratorInit(void) #ifdef GRID_HIP hipDeviceProp_t *gpu_props; +hipStream_t copyStream; void acceleratorInit(void) { int nDevices = 1; @@ -166,16 +168,25 @@ void acceleratorInit(void) #ifdef GRID_DEFAULT_GPU if ( world_rank == 0 ) { printf("AcceleratorHipInit: using default device \n"); - printf("AcceleratorHipInit: assume user either uses a wrapping script to set CUDA_VISIBLE_DEVICES, UCX_NET_DEVICES, and numa binding \n"); - printf("AcceleratorHipInit: Configure options --enable-summit, --enable-select-gpu=no \n"); + printf("AcceleratorHipInit: assume user or srun sets ROCR_VISIBLE_DEVICES and numa binding \n"); + printf("AcceleratorHipInit: Configure options --enable-setdevice=no \n"); } + int device = 0; #else if ( world_rank == 0 ) { printf("AcceleratorHipInit: rank %d setting device to node rank %d\n",world_rank,rank); - printf("AcceleratorHipInit: Configure options --enable-select-gpu=yes \n"); + printf("AcceleratorHipInit: Configure options --enable-setdevice=yes \n"); } - hipSetDevice(rank); + int device = rank; #endif + hipSetDevice(device); + hipStreamCreate(&copyStream); + const int len=64; + char busid[len]; + if( rank == world_rank ) { + hipDeviceGetPCIBusId(busid, len, device); + printf("local rank %d device %d bus id: %s\n", rank, device, busid); + } if ( world_rank == 0 ) printf("AcceleratorHipInit: ================================================\n"); } #endif From 6ceb55668420e843b984ed244953cdbc5612dfea Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 20:45:12 -0500 Subject: [PATCH 266/399] Intranode asynch hipMemCopy --- Grid/threads/Accelerator.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index cec0600f..0dfe8c64 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -230,6 +230,7 @@ inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes cudaMemcpyAsync(to,from,bytes, cudaMemcpyDeviceToDevice,copyStream); } inline void acceleratorCopySynchronise(void) { cudaStreamSynchronize(copyStream); }; + inline int acceleratorIsCommunicable(void *ptr) { // int uvm=0; @@ -337,6 +338,7 @@ NAMESPACE_BEGIN(Grid); #define accelerator __host__ __device__ #define accelerator_inline __host__ __device__ inline +extern hipStream_t copyStream; /*These routines define mapping from thread grid to loop & vector lane indexing */ accelerator_inline int acceleratorSIMTlane(int Nsimd) { #ifdef GRID_SIMT @@ -411,10 +413,16 @@ inline void acceleratorFreeShared(void *ptr){ hipFree(ptr);}; inline void acceleratorFreeDevice(void *ptr){ hipFree(ptr);}; inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { hipMemcpy(to,from,bytes, hipMemcpyHostToDevice);} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ hipMemcpy(to,from,bytes, hipMemcpyDeviceToHost);} -inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { hipMemcpy(to,from,bytes, hipMemcpyDeviceToDevice);} -inline void acceleratorCopySynchronise(void) { } +//inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { hipMemcpy(to,from,bytes, hipMemcpyDeviceToDevice);} +//inline void acceleratorCopySynchronise(void) { } inline void acceleratorMemSet(void *base,int value,size_t bytes) { hipMemset(base,value,bytes);} +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) // Asynch +{ + hipMemcpyAsync(to,from,bytes, hipMemcpyDeviceToDevice,copyStream); +} +inline void acceleratorCopySynchronise(void) { hipStreamSynchronize(copyStream); }; + #endif ////////////////////////////////////////////// @@ -485,18 +493,12 @@ inline void acceleratorFreeCpu (void *ptr){free(ptr);}; /////////////////////////////////////////////////// // Synchronise across local threads for divergence resynch /////////////////////////////////////////////////// -accelerator_inline void acceleratorSynchronise(void) +accelerator_inline void acceleratorSynchronise(void) // Only Nvidia needs { #ifdef GRID_SIMT #ifdef GRID_CUDA __syncwarp(); #endif -#ifdef GRID_SYCL - //cl::sycl::detail::workGroupBarrier(); -#endif -#ifdef GRID_HIP - __syncthreads(); -#endif #endif return; } From 8079dc2a145226b455608e87646992e737638db3 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 20:45:44 -0500 Subject: [PATCH 267/399] Cray MPI not working right yet --- systems/Spock/comms.slurm | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 systems/Spock/comms.slurm diff --git a/systems/Spock/comms.slurm b/systems/Spock/comms.slurm new file mode 100644 index 00000000..0841fda0 --- /dev/null +++ b/systems/Spock/comms.slurm @@ -0,0 +1,26 @@ +#!/bin/bash +# Begin LSF Directives +#SBATCH -A LGT104 +#SBATCH -t 01:00:00 +##SBATCH -U openmpThu +#SBATCH -p ecp +#SBATCH -J comms +#SBATCH -o comms.%J +#SBATCH -e comms.%J +#SBATCH -N 1 +#SBATCH -n 2 + +DIR=. +module list +export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +export MPICH_GPU_SUPPORT_ENABLED=1 +#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +#export MPICH_SMP_SINGLE_COPY_MODE=CMA +export MPICH_SMP_SINGLE_COPY_MODE=NONE +export OMP_NUM_THREADS=8 + +AT=8 +echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE +PARAMS=" --accelerator-threads ${AT} --grid 64.64.32.32 --mpi 2.1.1.1 " +srun -n2 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_comms_host_device $PARAMS + From 2a4e739513dc41695b443a0464a6fe36a6aee231 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 20:46:09 -0500 Subject: [PATCH 268/399] Enable XGMI copy (need to rename nvlink to cover NVLINK/XGMI/XeLink) --- systems/Spock/config-command | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 systems/Spock/config-command diff --git a/systems/Spock/config-command b/systems/Spock/config-command new file mode 100644 index 00000000..70c97c37 --- /dev/null +++ b/systems/Spock/config-command @@ -0,0 +1,12 @@ +../../configure --enable-comms=mpi-auto \ +--enable-unified=no \ +--enable-shm=nvlink \ +--enable-accelerator=hip \ +--enable-gen-simd-width=64 \ +--enable-simd=GPU \ +--disable-fermion-reps \ +--disable-gparity \ +CXX=hipcc MPICXX=mpicxx \ +CXXFLAGS="-fPIC -I/opt/rocm-4.3.0/include/ -std=c++14 -I${MPICH_DIR}/include " \ +--prefix=/ccs/home/chulwoo/Grid \ + LDFLAGS=" -L${MPICH_DIR}/lib -lmpi -L${CRAY_MPICH_ROOTDIR}/gtl/lib -lmpi_gtl_hsa " From 14d82777e0da2c1dc8e6157f3f4344cab24461b5 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 20:47:16 -0500 Subject: [PATCH 269/399] Best modules for spock --- systems/Spock/sourceme.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 systems/Spock/sourceme.sh diff --git a/systems/Spock/sourceme.sh b/systems/Spock/sourceme.sh new file mode 100644 index 00000000..40d864b5 --- /dev/null +++ b/systems/Spock/sourceme.sh @@ -0,0 +1,5 @@ +module load PrgEnv-gnu +module load rocm/4.3.0 +module load gmp +module load cray-fftw +module load craype-accel-amd-gfx908 From 6d5277f2d78633913a67612f68b887e664bc7b6a Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 20:58:02 -0500 Subject: [PATCH 270/399] Update to Spock --- systems/Spock/dwf.slurm | 26 ++++++++++++++++++++++++++ systems/Spock/dwf4.slurm | 26 ++++++++++++++++++++++++++ systems/Spock/mpiwrapper.sh | 12 ++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 systems/Spock/dwf.slurm create mode 100644 systems/Spock/dwf4.slurm create mode 100755 systems/Spock/mpiwrapper.sh diff --git a/systems/Spock/dwf.slurm b/systems/Spock/dwf.slurm new file mode 100644 index 00000000..7144a270 --- /dev/null +++ b/systems/Spock/dwf.slurm @@ -0,0 +1,26 @@ +#!/bin/bash +# Begin LSF Directives +#SBATCH -A LGT104 +#SBATCH -t 01:00:00 +##SBATCH -U openmpThu +#SBATCH -p ecp +#SBATCH -J DWF +#SBATCH -o DWF.%J +#SBATCH -e DWF.%J +#SBATCH -N 1 +#SBATCH -n 1 + +DIR=. +module list +export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +export MPICH_GPU_SUPPORT_ENABLED=1 +#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +#export MPICH_SMP_SINGLE_COPY_MODE=NONE +export MPICH_SMP_SINGLE_COPY_MODE=CMA +export OMP_NUM_THREADS=8 + +AT=8 +echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE +PARAMS=" --accelerator-threads ${AT} --grid 32.32.32.32 --mpi 1.1.1.1 --comms-overlap" +srun -n1 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + diff --git a/systems/Spock/dwf4.slurm b/systems/Spock/dwf4.slurm new file mode 100644 index 00000000..02a7b888 --- /dev/null +++ b/systems/Spock/dwf4.slurm @@ -0,0 +1,26 @@ +#!/bin/bash +# Begin LSF Directives +#SBATCH -A LGT104 +#SBATCH -t 01:00:00 +##SBATCH -U openmpThu +#SBATCH -p ecp +#SBATCH -J DWF +#SBATCH -o DWF.%J +#SBATCH -e DWF.%J +#SBATCH -N 1 +#SBATCH -n 4 + +DIR=. +module list +export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +export MPICH_GPU_SUPPORT_ENABLED=1 +#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +#export MPICH_SMP_SINGLE_COPY_MODE=NONE +export MPICH_SMP_SINGLE_COPY_MODE=CMA +export OMP_NUM_THREADS=8 + +AT=8 +echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE +PARAMS=" --accelerator-threads ${AT} --grid 32.32.64.64 --mpi 1.1.2.2 --comms-overlap --shm 2048 --shm-mpi 0" +srun -n4 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + diff --git a/systems/Spock/mpiwrapper.sh b/systems/Spock/mpiwrapper.sh new file mode 100755 index 00000000..76c4e364 --- /dev/null +++ b/systems/Spock/mpiwrapper.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +lrank=$SLURM_LOCALID + +export ROCR_VISIBLE_DEVICES=$SLURM_LOCALID + +echo "`hostname` - $lrank device=$ROCR_VISIBLE_DEVICES binding=$BINDING" + +$* + + + From e32d5141b4f6d74105d33349173c93e4e382911f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 21:46:31 -0500 Subject: [PATCH 271/399] Updated to make MPI reliable still gives good perf, but MPI will be slow intranode --- systems/Spock/dwf4.slurm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systems/Spock/dwf4.slurm b/systems/Spock/dwf4.slurm index 02a7b888..261929ab 100644 --- a/systems/Spock/dwf4.slurm +++ b/systems/Spock/dwf4.slurm @@ -15,8 +15,8 @@ module list export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 export MPICH_GPU_SUPPORT_ENABLED=1 #export MPICH_SMP_SINGLE_COPY_MODE=XPMEM -#export MPICH_SMP_SINGLE_COPY_MODE=NONE -export MPICH_SMP_SINGLE_COPY_MODE=CMA +export MPICH_SMP_SINGLE_COPY_MODE=NONE +#export MPICH_SMP_SINGLE_COPY_MODE=CMA export OMP_NUM_THREADS=8 AT=8 From f34d34bd17307b37e89de51743fe26482bd161af Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 22 Nov 2021 22:27:16 -0500 Subject: [PATCH 272/399] 2 nodes --- systems/Spock/dwf8.slurm | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 systems/Spock/dwf8.slurm diff --git a/systems/Spock/dwf8.slurm b/systems/Spock/dwf8.slurm new file mode 100644 index 00000000..c4672db0 --- /dev/null +++ b/systems/Spock/dwf8.slurm @@ -0,0 +1,26 @@ +#!/bin/bash +# Begin LSF Directives +#SBATCH -A LGT104 +#SBATCH -t 01:00:00 +##SBATCH -U openmpThu +#SBATCH -p ecp +#SBATCH -J DWF +#SBATCH -o DWF.%J +#SBATCH -e DWF.%J +#SBATCH -N 2 +#SBATCH -n 8 + +DIR=. +module list +export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +export MPICH_GPU_SUPPORT_ENABLED=1 +#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +export MPICH_SMP_SINGLE_COPY_MODE=NONE +#export MPICH_SMP_SINGLE_COPY_MODE=CMA +export OMP_NUM_THREADS=8 + +AT=8 +echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE +PARAMS=" --accelerator-threads ${AT} --grid 32.64.64.64 --mpi 1.2.2.2 --comms-overlap --shm 2048 --shm-mpi 0" +srun -n8 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + From 0bd83cdbda5defdde82f48f0c06baef40b3dee29 Mon Sep 17 00:00:00 2001 From: RJHudspith <renwick.james.hudspith@gmail.com> Date: Sun, 28 Nov 2021 21:51:03 +0100 Subject: [PATCH 273/399] Fixes for Nc!=3 Nersc IO, Gauge and Gauge_NCxNC compatible with GLU. Trace normalisation changed in places removing explicit threes. Guards against non-su3 tests and tests failing when LIME is not compiled. --- Grid/Makefile.am | 2 +- Grid/parallelIO/IldgIO.h | 9 +- Grid/parallelIO/MetaData.h | 33 +- Grid/parallelIO/NerscIO.h | 46 ++- Grid/qcd/utils/WilsonLoops.h | 3 +- configure.ac | 2 +- tests/IO/Test_nersc_io.cc | 8 +- tests/core/Test_lie_generators.cc | 376 +++++++++--------- tests/core/Test_reunitarise.cc | 5 +- tests/hmc/Test_hmc_ScalarActionNxN.cc | 4 +- tests/hmc/Test_hmc_WG_Production.cc | 6 +- tests/lanczos/Test_compressed_lanczos.cc | 10 + .../Test_dwf_compressed_lanczos_reorg.cc | 11 +- tests/solver/Test_coarse_even_odd.cc | 12 +- tests/solver/Test_dwf_mrhs_cg.cc | 2 + 15 files changed, 282 insertions(+), 247 deletions(-) diff --git a/Grid/Makefile.am b/Grid/Makefile.am index 7c3c151b..aff5b9d0 100644 --- a/Grid/Makefile.am +++ b/Grid/Makefile.am @@ -70,7 +70,7 @@ endif lib_LIBRARIES = libGrid.a CCFILES += $(extra_sources) -HFILES += $(extra_headers) Config.h Version.h +HFILES += $(extra_headers) libGrid_a_SOURCES = $(CCFILES) libGrid_adir = $(includedir)/Grid diff --git a/Grid/parallelIO/IldgIO.h b/Grid/parallelIO/IldgIO.h index 576f08bd..80d135d2 100644 --- a/Grid/parallelIO/IldgIO.h +++ b/Grid/parallelIO/IldgIO.h @@ -31,6 +31,7 @@ directory #include <fstream> #include <iomanip> #include <iostream> +#include <string> #include <map> #include <pwd.h> @@ -654,7 +655,8 @@ class IldgWriter : public ScidacWriter { // Fill ILDG header data struct ////////////////////////////////////////////////////// ildgFormat ildgfmt ; - ildgfmt.field = std::string("su3gauge"); + const std::string stNC = std::to_string( Nc ) ; + ildgfmt.field = std::string("su"+stNC+"gauge"); if ( format == std::string("IEEE32BIG") ) { ildgfmt.precision = 32; @@ -871,7 +873,8 @@ class IldgReader : public GridLimeReader { } else { assert(found_ildgFormat); - assert ( ildgFormat_.field == std::string("su3gauge") ); + const std::string stNC = std::to_string( Nc ) ; + assert ( ildgFormat_.field == std::string("su"+stNC+"gauge") ); /////////////////////////////////////////////////////////////////////////////////////// // Populate our Grid metadata as best we can @@ -879,7 +882,7 @@ class IldgReader : public GridLimeReader { std::ostringstream vers; vers << ildgFormat_.version; FieldMetaData_.hdr_version = vers.str(); - FieldMetaData_.data_type = std::string("4D_SU3_GAUGE_3X3"); + FieldMetaData_.data_type = std::string("4D_SU"+stNC+"_GAUGE_"+stNC+"x"+stNC); FieldMetaData_.nd=4; FieldMetaData_.dimension.resize(4); diff --git a/Grid/parallelIO/MetaData.h b/Grid/parallelIO/MetaData.h index af8b3f76..6b9d8708 100644 --- a/Grid/parallelIO/MetaData.h +++ b/Grid/parallelIO/MetaData.h @@ -6,8 +6,8 @@ Copyright (C) 2015 - Author: Peter Boyle <paboyle@ph.ed.ac.uk> + Author: Jamie Hudspith <renwick.james.hudspth@gmail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -182,8 +182,8 @@ class GaugeStatistics public: void operator()(Lattice<vLorentzColourMatrixD> & data,FieldMetaData &header) { - header.link_trace=WilsonLoops<Impl>::linkTrace(data); - header.plaquette =WilsonLoops<Impl>::avgPlaquette(data); + header.link_trace = WilsonLoops<Impl>::linkTrace(data); + header.plaquette = WilsonLoops<Impl>::avgPlaquette(data); } }; typedef GaugeStatistics<PeriodicGimplD> PeriodicGaugeStatistics; @@ -203,20 +203,24 @@ template<> inline void PrepareMetaData<vLorentzColourMatrixD>(Lattice<vLorentzCo ////////////////////////////////////////////////////////////////////// inline void reconstruct3(LorentzColourMatrix & cm) { - const int x=0; - const int y=1; - const int z=2; + assert( Nc < 4 && Nc > 1 ) ; for(int mu=0;mu<Nd;mu++){ - cm(mu)()(2,x) = adj(cm(mu)()(0,y)*cm(mu)()(1,z)-cm(mu)()(0,z)*cm(mu)()(1,y)); //x= yz-zy - cm(mu)()(2,y) = adj(cm(mu)()(0,z)*cm(mu)()(1,x)-cm(mu)()(0,x)*cm(mu)()(1,z)); //y= zx-xz - cm(mu)()(2,z) = adj(cm(mu)()(0,x)*cm(mu)()(1,y)-cm(mu)()(0,y)*cm(mu)()(1,x)); //z= xy-yx + #if Nc == 2 + cm(mu)()(1,0) = -adj(cm(mu)()(0,y)) ; + cm(mu)()(1,1) = adj(cm(mu)()(0,x)) ; + #else + const int x=0 , y=1 , z=2 ; // a little disinenuous labelling + cm(mu)()(2,x) = adj(cm(mu)()(0,y)*cm(mu)()(1,z)-cm(mu)()(0,z)*cm(mu)()(1,y)); //x= yz-zy + cm(mu)()(2,y) = adj(cm(mu)()(0,z)*cm(mu)()(1,x)-cm(mu)()(0,x)*cm(mu)()(1,z)); //y= zx-xz + cm(mu)()(2,z) = adj(cm(mu)()(0,x)*cm(mu)()(1,y)-cm(mu)()(0,y)*cm(mu)()(1,x)); //z= xy-yx + #endif } } //////////////////////////////////////////////////////////////////////////////// // Some data types for intermediate storage //////////////////////////////////////////////////////////////////////////////// -template<typename vtype> using iLorentzColour2x3 = iVector<iVector<iVector<vtype, Nc>, 2>, Nd >; +template<typename vtype> using iLorentzColour2x3 = iVector<iVector<iVector<vtype, Nc>, Nc-1>, Nd >; typedef iLorentzColour2x3<Complex> LorentzColour2x3; typedef iLorentzColour2x3<ComplexF> LorentzColour2x3F; @@ -278,7 +282,6 @@ struct GaugeSimpleMunger{ template <class fobj, class sobj> struct GaugeSimpleUnmunger { - void operator()(sobj &in, fobj &out) { for (int mu = 0; mu < Nd; mu++) { for (int i = 0; i < Nc; i++) { @@ -317,8 +320,8 @@ template<class fobj,class sobj> struct Gauge3x2munger{ void operator() (fobj &in,sobj &out){ for(int mu=0;mu<Nd;mu++){ - for(int i=0;i<2;i++){ - for(int j=0;j<3;j++){ + for(int i=0;i<Nc-1;i++){ + for(int j=0;j<Nc;j++){ out(mu)()(i,j) = in(mu)(i)(j); }} } @@ -330,8 +333,8 @@ template<class fobj,class sobj> struct Gauge3x2unmunger{ void operator() (sobj &in,fobj &out){ for(int mu=0;mu<Nd;mu++){ - for(int i=0;i<2;i++){ - for(int j=0;j<3;j++){ + for(int i=0;i<Nc-1;i++){ + for(int j=0;j<Nc;j++){ out(mu)(i)(j) = in(mu)()(i,j); }} } diff --git a/Grid/parallelIO/NerscIO.h b/Grid/parallelIO/NerscIO.h index 99011e25..2dd30a15 100644 --- a/Grid/parallelIO/NerscIO.h +++ b/Grid/parallelIO/NerscIO.h @@ -9,6 +9,7 @@ Author: Matt Spraggs <matthew.spraggs@gmail.com> Author: Peter Boyle <paboyle@ph.ed.ac.uk> Author: paboyle <paboyle@ph.ed.ac.uk> + Author: Jamie Hudspith <renwick.james.hudspth@gmail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,6 +31,8 @@ #ifndef GRID_NERSC_IO_H #define GRID_NERSC_IO_H +#include <string> + NAMESPACE_BEGIN(Grid); using namespace Grid; @@ -145,15 +148,17 @@ public: std::string format(header.floating_point); - int ieee32big = (format == std::string("IEEE32BIG")); - int ieee32 = (format == std::string("IEEE32")); - int ieee64big = (format == std::string("IEEE64BIG")); - int ieee64 = (format == std::string("IEEE64") || format == std::string("IEEE64LITTLE")); + const int ieee32big = (format == std::string("IEEE32BIG")); + const int ieee32 = (format == std::string("IEEE32")); + const int ieee64big = (format == std::string("IEEE64BIG")); + const int ieee64 = (format == std::string("IEEE64") || \ + format == std::string("IEEE64LITTLE")); uint32_t nersc_csum,scidac_csuma,scidac_csumb; // depending on datatype, set up munger; // munger is a function of <floating point, Real, data_type> - if ( header.data_type == std::string("4D_SU3_GAUGE") ) { + const std::string stNC = std::to_string( Nc ) ; + if ( header.data_type == std::string("4D_SU"+stNC+"_GAUGE") ) { if ( ieee32 || ieee32big ) { BinaryIO::readLatticeObject<vLorentzColourMatrixD, LorentzColour2x3F> (Umu,file,Gauge3x2munger<LorentzColour2x3F,LorentzColourMatrix>(), offset,format, @@ -164,7 +169,7 @@ public: (Umu,file,Gauge3x2munger<LorentzColour2x3D,LorentzColourMatrix>(),offset,format, nersc_csum,scidac_csuma,scidac_csumb); } - } else if ( header.data_type == std::string("4D_SU3_GAUGE_3x3") ) { + } else if ( header.data_type == std::string("4D_SU"+stNC+"_GAUGE_"+stNC+"x"+stNC) ) { if ( ieee32 || ieee32big ) { BinaryIO::readLatticeObject<vLorentzColourMatrixD,LorentzColourMatrixF> (Umu,file,GaugeSimpleMunger<LorentzColourMatrixF,LorentzColourMatrix>(),offset,format, @@ -230,6 +235,7 @@ public: header.sequence_number = 1; header.ensemble_id = std::string("UKQCD"); header.ensemble_label = ens_label; + header.hdr_version = "1.0" ; typedef LorentzColourMatrixD fobj3D; typedef LorentzColour2x3D fobj2D; @@ -243,10 +249,14 @@ public: uint64_t offset; - // Sod it -- always write 3x3 double - header.floating_point = std::string("IEEE64BIG"); - header.data_type = std::string("4D_SU3_GAUGE_3x3"); - GaugeSimpleUnmunger<fobj3D,sobj> munge; + // Sod it -- always write NcxNc double + header.floating_point = std::string("IEEE64BIG"); + const std::string stNC = std::to_string( Nc ) ; + if( two_row ) { + header.data_type = std::string("4D_SU" + stNC + "_GAUGE" ); + } else { + header.data_type = std::string("4D_SU" + stNC + "_GAUGE_" + stNC + "x" + stNC ); + } if ( grid->IsBoss() ) { truncate(file); offset = writeHeader(header,file); @@ -254,8 +264,15 @@ public: grid->Broadcast(0,(void *)&offset,sizeof(offset)); uint32_t nersc_csum,scidac_csuma,scidac_csumb; - BinaryIO::writeLatticeObject<vobj,fobj3D>(Umu,file,munge,offset,header.floating_point, - nersc_csum,scidac_csuma,scidac_csumb); + if( two_row ) { + Gauge3x2unmunger<fobj2D,sobj> munge; + BinaryIO::writeLatticeObject<vobj,fobj2D>(Umu,file,munge,offset,header.floating_point, + nersc_csum,scidac_csuma,scidac_csumb); + } else { + GaugeSimpleUnmunger<fobj3D,sobj> munge; + BinaryIO::writeLatticeObject<vobj,fobj3D>(Umu,file,munge,offset,header.floating_point, + nersc_csum,scidac_csuma,scidac_csumb); + } header.checksum = nersc_csum; if ( grid->IsBoss() ) { writeHeader(header,file); @@ -287,8 +304,7 @@ public: header.plaquette=0.0; MachineCharacteristics(header); - uint64_t offset; - + uint64_t offset; #ifdef RNG_RANLUX header.floating_point = std::string("UINT64"); header.data_type = std::string("RANLUX48"); @@ -328,7 +344,7 @@ public: GridBase *grid = parallel.Grid(); - uint64_t offset = readHeader(file,grid,header); + uint64_t offset = readHeader(file,grid,header); FieldMetaData clone(header); diff --git a/Grid/qcd/utils/WilsonLoops.h b/Grid/qcd/utils/WilsonLoops.h index 0367c9fa..e002e3d5 100644 --- a/Grid/qcd/utils/WilsonLoops.h +++ b/Grid/qcd/utils/WilsonLoops.h @@ -125,7 +125,6 @@ public: return sumplaq / vol / faces / Nc; // Nd , Nc dependent... FIXME } - ////////////////////////////////////////////////// // average over all x,y,z the temporal loop ////////////////////////////////////////////////// @@ -165,7 +164,7 @@ public: double vol = Umu.Grid()->gSites(); - return p.real() / vol / 4.0 / 3.0; + return p.real() / vol / (4.0 * Nc ) ; }; ////////////////////////////////////////////////// diff --git a/configure.ac b/configure.ac index 406b0b74..9ab0595a 100644 --- a/configure.ac +++ b/configure.ac @@ -159,7 +159,7 @@ case ${ac_ZMOBIUS} in esac ############### Nc AC_ARG_ENABLE([Nc], - [AC_HELP_STRING([--enable-Nc=2|3|4], [enable number of colours])], + [AC_HELP_STRING([--enable-Nc=2|3|4|5], [enable number of colours])], [ac_Nc=${enable_Nc}], [ac_Nc=3]) case ${ac_Nc} in diff --git a/tests/IO/Test_nersc_io.cc b/tests/IO/Test_nersc_io.cc index c15c320e..ae5b9c0d 100644 --- a/tests/IO/Test_nersc_io.cc +++ b/tests/IO/Test_nersc_io.cc @@ -147,7 +147,7 @@ int main (int argc, char ** argv) Complex p = TensorRemove(Tp); std::cout<<GridLogMessage << "calculated plaquettes " <<p*PlaqScale<<std::endl; - Complex LinkTraceScale(1.0/vol/4.0/3.0); + Complex LinkTraceScale(1.0/vol/4.0/(Real)Nc); TComplex Tl = sum(LinkTrace); Complex l = TensorRemove(Tl); std::cout<<GridLogMessage << "calculated link trace " <<l*LinkTraceScale<<std::endl; @@ -157,8 +157,10 @@ int main (int argc, char ** argv) Complex ll= TensorRemove(TcP); std::cout<<GridLogMessage << "coarsened plaquettes sum to " <<ll*PlaqScale<<std::endl; - std::string clone2x3("./ckpoint_clone2x3.4000"); - std::string clone3x3("./ckpoint_clone3x3.4000"); + const string stNc = to_string( Nc ) ; + const string stNcM1 = to_string( Nc-1 ) ; + std::string clone2x3("./ckpoint_clone"+stNcM1+"x"+stNc+".4000"); + std::string clone3x3("./ckpoint_clone"+stNc+"x"+stNc+".4000"); NerscIO::writeConfiguration(Umu,clone3x3,0,precision32); NerscIO::writeConfiguration(Umu,clone2x3,1,precision32); diff --git a/tests/core/Test_lie_generators.cc b/tests/core/Test_lie_generators.cc index e044378c..8f18ed04 100644 --- a/tests/core/Test_lie_generators.cc +++ b/tests/core/Test_lie_generators.cc @@ -9,6 +9,7 @@ Copyright (C) 2015 Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk> Author: Peter Boyle <paboyle@ph.ed.ac.uk> Author: Guido Cossu <guido.cossu@ed.ac.uk> +Author: Jamie Hudspith <renwick.james.hudspth@gmail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,14 +43,14 @@ directory using namespace std; using namespace Grid; - ; +; int main(int argc, char** argv) { Grid_init(&argc, &argv); std::vector<int> latt({4, 4, 4, 8}); GridCartesian* grid = SpaceTimeGrid::makeFourDimGrid( - latt, GridDefaultSimd(Nd, vComplex::Nsimd()), GridDefaultMpi()); + latt, GridDefaultSimd(Nd, vComplex::Nsimd()), GridDefaultMpi()); GridRedBlackCartesian* rbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(grid); @@ -60,15 +61,19 @@ int main(int argc, char** argv) { << std::endl; SU2::printGenerators(); std::cout << "Dimension of adjoint representation: "<< SU2Adjoint::Dimension << std::endl; + + // guard as this code fails to compile for Nc != 3 +#if (Nc == 3) + SU2Adjoint::printGenerators(); SU2::testGenerators(); SU2Adjoint::testGenerators(); - + std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "* Generators for SU(Nc" << std::endl; std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; SU3::printGenerators(); std::cout << "Dimension of adjoint representation: "<< SU3Adjoint::Dimension << std::endl; SU3Adjoint::printGenerators(); @@ -111,12 +116,10 @@ int main(int argc, char** argv) { // AdjointRepresentation has the predefined number of colours Nc // Representations<FundamentalRepresentation, AdjointRepresentation, TwoIndexSymmetricRepresentation> RepresentationTypes(grid); - - LatticeGaugeField U(grid), V(grid); SU3::HotConfiguration<LatticeGaugeField>(gridRNG, U); SU3::HotConfiguration<LatticeGaugeField>(gridRNG, V); - + // Adjoint representation // Test group structure // (U_f * V_f)_r = U_r * V_r @@ -127,17 +130,17 @@ int main(int argc, char** argv) { SU3::LatticeMatrix Vmu = peekLorentz(V,mu); pokeLorentz(UV,Umu*Vmu, mu); } - + AdjRep.update_representation(UV); typename AdjointRep<Nc>::LatticeField UVr = AdjRep.U; // (U_f * V_f)_r - - + + AdjRep.update_representation(U); typename AdjointRep<Nc>::LatticeField Ur = AdjRep.U; // U_r - + AdjRep.update_representation(V); typename AdjointRep<Nc>::LatticeField Vr = AdjRep.U; // V_r - + typename AdjointRep<Nc>::LatticeField UrVr(grid); UrVr = Zero(); for (int mu = 0; mu < Nd; mu++) { @@ -145,10 +148,10 @@ int main(int argc, char** argv) { typename AdjointRep<Nc>::LatticeMatrix Vrmu = peekLorentz(Vr,mu); pokeLorentz(UrVr,Urmu*Vrmu, mu); } - + typename AdjointRep<Nc>::LatticeField Diff_check = UVr - UrVr; std::cout << GridLogMessage << "Group structure SU("<<Nc<<") check difference (Adjoint representation) : " << norm2(Diff_check) << std::endl; - + // Check correspondence of algebra and group transformations // Create a random vector SU3::LatticeAlgebraVector h_adj(grid); @@ -156,32 +159,31 @@ int main(int argc, char** argv) { random(gridRNG,h_adj); h_adj = real(h_adj); SU_Adjoint<Nc>::AdjointLieAlgebraMatrix(h_adj,Ar); - + // Re-extract h_adj SU3::LatticeAlgebraVector h_adj2(grid); SU_Adjoint<Nc>::projectOnAlgebra(h_adj2, Ar); SU3::LatticeAlgebraVector h_diff = h_adj - h_adj2; std::cout << GridLogMessage << "Projections structure check vector difference (Adjoint representation) : " << norm2(h_diff) << std::endl; - + // Exponentiate typename AdjointRep<Nc>::LatticeMatrix Uadj(grid); Uadj = expMat(Ar, 1.0, 16); - + typename AdjointRep<Nc>::LatticeMatrix uno(grid); uno = 1.0; // Check matrix Uadj, must be real orthogonal typename AdjointRep<Nc>::LatticeMatrix Ucheck = Uadj - conjugate(Uadj); std::cout << GridLogMessage << "Reality check: " << norm2(Ucheck) - << std::endl; - + << std::endl; + Ucheck = Uadj * adj(Uadj) - uno; std::cout << GridLogMessage << "orthogonality check 1: " << norm2(Ucheck) - << std::endl; + << std::endl; Ucheck = adj(Uadj) * Uadj - uno; std::cout << GridLogMessage << "orthogonality check 2: " << norm2(Ucheck) - << std::endl; - - + << std::endl; + // Construct the fundamental matrix in the group SU3::LatticeMatrix Af(grid); SU3::FundamentalLieAlgebraMatrix(h_adj,Af); @@ -193,72 +195,65 @@ int main(int argc, char** argv) { SU3::LatticeMatrix UnitCheck(grid); UnitCheck = Ufund * adj(Ufund) - uno_f; std::cout << GridLogMessage << "unitarity check 1: " << norm2(UnitCheck) - << std::endl; + << std::endl; UnitCheck = adj(Ufund) * Ufund - uno_f; std::cout << GridLogMessage << "unitarity check 2: " << norm2(UnitCheck) - << std::endl; - + << std::endl; // Tranform to the adjoint representation U = Zero(); // fill this with only one direction pokeLorentz(U,Ufund,0); // the representation transf acts on full gauge fields - + AdjRep.update_representation(U); Ur = AdjRep.U; // U_r typename AdjointRep<Nc>::LatticeMatrix Ur0 = peekLorentz(Ur,0); // this should be the same as Uadj - + typename AdjointRep<Nc>::LatticeMatrix Diff_check_mat = Ur0 - Uadj; std::cout << GridLogMessage << "Projections structure check group difference : " << norm2(Diff_check_mat) << std::endl; - - - // TwoIndexRep tests - std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "* eS^{ij} base for SU(2)" << std::endl; std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "Dimension of Two Index Symmetric representation: "<< SU2TwoIndexSymm::Dimension << std::endl; SU2TwoIndexSymm::printBase(); - std::cout << GridLogMessage << "*********************************************" - << std::endl; - std::cout << GridLogMessage << "Generators of Two Index Symmetric representation: "<< SU2TwoIndexSymm::Dimension << std::endl; + std::cout << GridLogMessage << "*********************************************" + << std::endl; + std::cout << GridLogMessage << "Generators of Two Index Symmetric representation: "<< SU2TwoIndexSymm::Dimension << std::endl; SU2TwoIndexSymm::printGenerators(); - std::cout << GridLogMessage << "Test of Two Index Symmetric Generators: "<< SU2TwoIndexSymm::Dimension << std::endl; + std::cout << GridLogMessage << "Test of Two Index Symmetric Generators: "<< SU2TwoIndexSymm::Dimension << std::endl; SU2TwoIndexSymm::testGenerators(); std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "* eAS^{ij} base for SU(2)" << std::endl; std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "Dimension of Two Index anti-Symmetric representation: "<< SU2TwoIndexAntiSymm::Dimension << std::endl; SU2TwoIndexAntiSymm::printBase(); - std::cout << GridLogMessage << "*********************************************" - << std::endl; - std::cout << GridLogMessage << "Dimension of Two Index anti-Symmetric representation: "<< SU2TwoIndexAntiSymm::Dimension << std::endl; + std::cout << GridLogMessage << "*********************************************" + << std::endl; + std::cout << GridLogMessage << "Dimension of Two Index anti-Symmetric representation: "<< SU2TwoIndexAntiSymm::Dimension << std::endl; SU2TwoIndexAntiSymm::printGenerators(); std::cout << GridLogMessage << "Test of Two Index anti-Symmetric Generators: "<< SU2TwoIndexAntiSymm::Dimension << std::endl; SU2TwoIndexAntiSymm::testGenerators(); - - std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "Test for the Two Index Symmetric projectors" - << std::endl; + << std::endl; // Projectors SU3TwoIndexSymm::LatticeTwoIndexMatrix Gauss2(grid); random(gridRNG,Gauss2); @@ -276,13 +271,13 @@ int main(int argc, char** argv) { SU3::LatticeAlgebraVector diff2 = ha - hb; std::cout << GridLogMessage << "Difference: " << norm2(diff) << std::endl; std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; - std::cout << GridLogMessage << "*********************************************" - << std::endl; + std::cout << GridLogMessage << "*********************************************" + << std::endl; std::cout << GridLogMessage << "Test for the Two index anti-Symmetric projectors" - << std::endl; + << std::endl; // Projectors SU3TwoIndexAntiSymm::LatticeTwoIndexMatrix Gauss2a(grid); random(gridRNG,Gauss2a); @@ -300,11 +295,11 @@ int main(int argc, char** argv) { SU3::LatticeAlgebraVector diff2a = ha - hb; std::cout << GridLogMessage << "Difference: " << norm2(diff2a) << std::endl; std::cout << GridLogMessage << "*********************************************" - << std::endl; + << std::endl; std::cout << GridLogMessage << "Two index Symmetric: Checking Group Structure" - << std::endl; + << std::endl; // Testing HMC representation classes TwoIndexRep< Nc, Symmetric > TIndexRep(grid); @@ -313,7 +308,7 @@ int main(int argc, char** argv) { LatticeGaugeField U2(grid), V2(grid); SU3::HotConfiguration<LatticeGaugeField>(gridRNG, U2); SU3::HotConfiguration<LatticeGaugeField>(gridRNG, V2); - + LatticeGaugeField UV2(grid); UV2 = Zero(); for (int mu = 0; mu < Nd; mu++) { @@ -321,16 +316,16 @@ int main(int argc, char** argv) { SU3::LatticeMatrix Vmu2 = peekLorentz(V2,mu); pokeLorentz(UV2,Umu2*Vmu2, mu); } - + TIndexRep.update_representation(UV2); typename TwoIndexRep< Nc, Symmetric >::LatticeField UVr2 = TIndexRep.U; // (U_f * V_f)_r - + TIndexRep.update_representation(U2); typename TwoIndexRep< Nc, Symmetric >::LatticeField Ur2 = TIndexRep.U; // U_r - + TIndexRep.update_representation(V2); typename TwoIndexRep< Nc, Symmetric >::LatticeField Vr2 = TIndexRep.U; // V_r - + typename TwoIndexRep< Nc, Symmetric >::LatticeField Ur2Vr2(grid); Ur2Vr2 = Zero(); for (int mu = 0; mu < Nd; mu++) { @@ -338,11 +333,11 @@ int main(int argc, char** argv) { typename TwoIndexRep< Nc, Symmetric >::LatticeMatrix Vrmu2 = peekLorentz(Vr2,mu); pokeLorentz(Ur2Vr2,Urmu2*Vrmu2, mu); } - + typename TwoIndexRep< Nc, Symmetric >::LatticeField Diff_check2 = UVr2 - Ur2Vr2; std::cout << GridLogMessage << "Group structure SU("<<Nc<<") check difference (Two Index Symmetric): " << norm2(Diff_check2) << std::endl; - - + + // Check correspondence of algebra and group transformations // Create a random vector SU3::LatticeAlgebraVector h_sym(grid); @@ -350,34 +345,31 @@ int main(int argc, char** argv) { random(gridRNG,h_sym); h_sym = real(h_sym); SU_TwoIndex<Nc,Symmetric>::TwoIndexLieAlgebraMatrix(h_sym,Ar_sym); - + // Re-extract h_sym SU3::LatticeAlgebraVector h_sym2(grid); SU_TwoIndex< Nc, Symmetric>::projectOnAlgebra(h_sym2, Ar_sym); SU3::LatticeAlgebraVector h_diff_sym = h_sym - h_sym2; std::cout << GridLogMessage << "Projections structure check vector difference (Two Index Symmetric): " << norm2(h_diff_sym) << std::endl; - - + // Exponentiate typename TwoIndexRep< Nc, Symmetric>::LatticeMatrix U2iS(grid); U2iS = expMat(Ar_sym, 1.0, 16); - + typename TwoIndexRep< Nc, Symmetric>::LatticeMatrix uno2iS(grid); uno2iS = 1.0; // Check matrix U2iS, must be real orthogonal typename TwoIndexRep< Nc, Symmetric>::LatticeMatrix Ucheck2iS = U2iS - conjugate(U2iS); std::cout << GridLogMessage << "Reality check: " << norm2(Ucheck2iS) - << std::endl; - + << std::endl; + Ucheck2iS = U2iS * adj(U2iS) - uno2iS; std::cout << GridLogMessage << "orthogonality check 1: " << norm2(Ucheck2iS) - << std::endl; + << std::endl; Ucheck2iS = adj(U2iS) * U2iS - uno2iS; std::cout << GridLogMessage << "orthogonality check 2: " << norm2(Ucheck2iS) - << std::endl; - - - + << std::endl; + // Construct the fundamental matrix in the group SU3::LatticeMatrix Af_sym(grid); SU3::FundamentalLieAlgebraMatrix(h_sym,Af_sym); @@ -386,147 +378,137 @@ int main(int argc, char** argv) { SU3::LatticeMatrix UnitCheck2(grid); UnitCheck2 = Ufund2 * adj(Ufund2) - uno_f; std::cout << GridLogMessage << "unitarity check 1: " << norm2(UnitCheck2) - << std::endl; + << std::endl; UnitCheck2 = adj(Ufund2) * Ufund2 - uno_f; std::cout << GridLogMessage << "unitarity check 2: " << norm2(UnitCheck2) - << std::endl; - - + << std::endl; + + // Tranform to the 2Index Sym representation U = Zero(); // fill this with only one direction pokeLorentz(U,Ufund2,0); // the representation transf acts on full gauge fields - + TIndexRep.update_representation(U); Ur2 = TIndexRep.U; // U_r typename TwoIndexRep< Nc, Symmetric>::LatticeMatrix Ur02 = peekLorentz(Ur2,0); // this should be the same as U2iS - + typename TwoIndexRep< Nc, Symmetric>::LatticeMatrix Diff_check_mat2 = Ur02 - U2iS; std::cout << GridLogMessage << "Projections structure check group difference (Two Index Symmetric): " << norm2(Diff_check_mat2) << std::endl; - - - - + if (TwoIndexRep<Nc, AntiSymmetric >::Dimension != 1){ - std::cout << GridLogMessage << "*********************************************" - << std::endl; + std::cout << GridLogMessage << "*********************************************" + << std::endl; - std::cout << GridLogMessage << "Two Index anti-Symmetric: Check Group Structure" - << std::endl; - // Testing HMC representation classes - TwoIndexRep< Nc, AntiSymmetric > TIndexRepA(grid); + std::cout << GridLogMessage << "Two Index anti-Symmetric: Check Group Structure" + << std::endl; + // Testing HMC representation classes + TwoIndexRep< Nc, AntiSymmetric > TIndexRepA(grid); - // Test group structure - // (U_f * V_f)_r = U_r * V_r - LatticeGaugeField U2A(grid), V2A(grid); - SU3::HotConfiguration<LatticeGaugeField>(gridRNG, U2A); - SU3::HotConfiguration<LatticeGaugeField>(gridRNG, V2A); + // Test group structure + // (U_f * V_f)_r = U_r * V_r + LatticeGaugeField U2A(grid), V2A(grid); + SU3::HotConfiguration<LatticeGaugeField>(gridRNG, U2A); + SU3::HotConfiguration<LatticeGaugeField>(gridRNG, V2A); - LatticeGaugeField UV2A(grid); - UV2A = Zero(); - for (int mu = 0; mu < Nd; mu++) { - SU3::LatticeMatrix Umu2A = peekLorentz(U2,mu); - SU3::LatticeMatrix Vmu2A = peekLorentz(V2,mu); - pokeLorentz(UV2A,Umu2A*Vmu2A, mu); + LatticeGaugeField UV2A(grid); + UV2A = Zero(); + for (int mu = 0; mu < Nd; mu++) { + SU3::LatticeMatrix Umu2A = peekLorentz(U2,mu); + SU3::LatticeMatrix Vmu2A = peekLorentz(V2,mu); + pokeLorentz(UV2A,Umu2A*Vmu2A, mu); + } + + TIndexRep.update_representation(UV2A); + typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField UVr2A = TIndexRepA.U; // (U_f * V_f)_r + + TIndexRep.update_representation(U2A); + typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Ur2A = TIndexRepA.U; // U_r + + TIndexRep.update_representation(V2A); + typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Vr2A = TIndexRepA.U; // V_r + + typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Ur2Vr2A(grid); + Ur2Vr2A = Zero(); + for (int mu = 0; mu < Nd; mu++) { + typename TwoIndexRep< Nc, AntiSymmetric >::LatticeMatrix Urmu2A = peekLorentz(Ur2A,mu); + typename TwoIndexRep< Nc, AntiSymmetric >::LatticeMatrix Vrmu2A = peekLorentz(Vr2A,mu); + pokeLorentz(Ur2Vr2A,Urmu2A*Vrmu2A, mu); + } + + typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Diff_check2A = UVr2A - Ur2Vr2A; + std::cout << GridLogMessage << "Group structure SU("<<Nc<<") check difference (Two Index anti-Symmetric): " << norm2(Diff_check2A) << std::endl; + + + // Check correspondence of algebra and group transformations + // Create a random vector + SU3::LatticeAlgebraVector h_Asym(grid); + typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Ar_Asym(grid); + random(gridRNG,h_Asym); + h_Asym = real(h_Asym); + SU_TwoIndex< Nc, AntiSymmetric>::TwoIndexLieAlgebraMatrix(h_Asym,Ar_Asym); + + // Re-extract h_sym + SU3::LatticeAlgebraVector h_Asym2(grid); + SU_TwoIndex< Nc, AntiSymmetric>::projectOnAlgebra(h_Asym2, Ar_Asym); + SU3::LatticeAlgebraVector h_diff_Asym = h_Asym - h_Asym2; + std::cout << GridLogMessage << "Projections structure check vector difference (Two Index anti-Symmetric): " << norm2(h_diff_Asym) << std::endl; + + + // Exponentiate + typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix U2iAS(grid); + U2iAS = expMat(Ar_Asym, 1.0, 16); + + typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix uno2iAS(grid); + uno2iAS = 1.0; + // Check matrix U2iS, must be real orthogonal + typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Ucheck2iAS = U2iAS - conjugate(U2iAS); + std::cout << GridLogMessage << "Reality check: " << norm2(Ucheck2iAS) + << std::endl; + + Ucheck2iAS = U2iAS * adj(U2iAS) - uno2iAS; + std::cout << GridLogMessage << "orthogonality check 1: " << norm2(Ucheck2iAS) + << std::endl; + Ucheck2iAS = adj(U2iAS) * U2iAS - uno2iAS; + std::cout << GridLogMessage << "orthogonality check 2: " << norm2(Ucheck2iAS) + << std::endl; + + + + // Construct the fundamental matrix in the group + SU3::LatticeMatrix Af_Asym(grid); + SU3::FundamentalLieAlgebraMatrix(h_Asym,Af_Asym); + SU3::LatticeMatrix Ufund2A(grid); + Ufund2A = expMat(Af_Asym, 1.0, 16); + SU3::LatticeMatrix UnitCheck2A(grid); + UnitCheck2A = Ufund2A * adj(Ufund2A) - uno_f; + std::cout << GridLogMessage << "unitarity check 1: " << norm2(UnitCheck2A) + << std::endl; + UnitCheck2A = adj(Ufund2A) * Ufund2A - uno_f; + std::cout << GridLogMessage << "unitarity check 2: " << norm2(UnitCheck2A) + << std::endl; + + + // Tranform to the 2Index Sym representation + U = Zero(); // fill this with only one direction + pokeLorentz(U,Ufund2A,0); // the representation transf acts on full gauge fields + + TIndexRepA.update_representation(U); + Ur2A = TIndexRepA.U; // U_r + typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Ur02A = peekLorentz(Ur2A,0); // this should be the same as U2iS + + typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Diff_check_mat2A = Ur02A - U2iAS; + std::cout << GridLogMessage << "Projections structure check group difference (Two Index anti-Symmetric): " << norm2(Diff_check_mat2A) << std::endl; + + } else { + std::cout << GridLogMessage << "Skipping Two Index anti-Symmetric tests " + "because representation is trivial (dim = 1)" + << std::endl; } - - TIndexRep.update_representation(UV2A); - typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField UVr2A = TIndexRepA.U; // (U_f * V_f)_r - - TIndexRep.update_representation(U2A); - typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Ur2A = TIndexRepA.U; // U_r - - TIndexRep.update_representation(V2A); - typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Vr2A = TIndexRepA.U; // V_r - - typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Ur2Vr2A(grid); - Ur2Vr2A = Zero(); - for (int mu = 0; mu < Nd; mu++) { - typename TwoIndexRep< Nc, AntiSymmetric >::LatticeMatrix Urmu2A = peekLorentz(Ur2A,mu); - typename TwoIndexRep< Nc, AntiSymmetric >::LatticeMatrix Vrmu2A = peekLorentz(Vr2A,mu); - pokeLorentz(Ur2Vr2A,Urmu2A*Vrmu2A, mu); - } - - typename TwoIndexRep< Nc, AntiSymmetric >::LatticeField Diff_check2A = UVr2A - Ur2Vr2A; - std::cout << GridLogMessage << "Group structure SU("<<Nc<<") check difference (Two Index anti-Symmetric): " << norm2(Diff_check2A) << std::endl; - - - // Check correspondence of algebra and group transformations - // Create a random vector - SU3::LatticeAlgebraVector h_Asym(grid); - typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Ar_Asym(grid); - random(gridRNG,h_Asym); - h_Asym = real(h_Asym); - SU_TwoIndex< Nc, AntiSymmetric>::TwoIndexLieAlgebraMatrix(h_Asym,Ar_Asym); - - // Re-extract h_sym - SU3::LatticeAlgebraVector h_Asym2(grid); - SU_TwoIndex< Nc, AntiSymmetric>::projectOnAlgebra(h_Asym2, Ar_Asym); - SU3::LatticeAlgebraVector h_diff_Asym = h_Asym - h_Asym2; - std::cout << GridLogMessage << "Projections structure check vector difference (Two Index anti-Symmetric): " << norm2(h_diff_Asym) << std::endl; - - - // Exponentiate - typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix U2iAS(grid); - U2iAS = expMat(Ar_Asym, 1.0, 16); - - typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix uno2iAS(grid); - uno2iAS = 1.0; - // Check matrix U2iS, must be real orthogonal - typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Ucheck2iAS = U2iAS - conjugate(U2iAS); - std::cout << GridLogMessage << "Reality check: " << norm2(Ucheck2iAS) - << std::endl; - - Ucheck2iAS = U2iAS * adj(U2iAS) - uno2iAS; - std::cout << GridLogMessage << "orthogonality check 1: " << norm2(Ucheck2iAS) - << std::endl; - Ucheck2iAS = adj(U2iAS) * U2iAS - uno2iAS; - std::cout << GridLogMessage << "orthogonality check 2: " << norm2(Ucheck2iAS) - << std::endl; - - - - // Construct the fundamental matrix in the group - SU3::LatticeMatrix Af_Asym(grid); - SU3::FundamentalLieAlgebraMatrix(h_Asym,Af_Asym); - SU3::LatticeMatrix Ufund2A(grid); - Ufund2A = expMat(Af_Asym, 1.0, 16); - SU3::LatticeMatrix UnitCheck2A(grid); - UnitCheck2A = Ufund2A * adj(Ufund2A) - uno_f; - std::cout << GridLogMessage << "unitarity check 1: " << norm2(UnitCheck2A) - << std::endl; - UnitCheck2A = adj(Ufund2A) * Ufund2A - uno_f; - std::cout << GridLogMessage << "unitarity check 2: " << norm2(UnitCheck2A) - << std::endl; - - - // Tranform to the 2Index Sym representation - U = Zero(); // fill this with only one direction - pokeLorentz(U,Ufund2A,0); // the representation transf acts on full gauge fields - - TIndexRepA.update_representation(U); - Ur2A = TIndexRepA.U; // U_r - typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Ur02A = peekLorentz(Ur2A,0); // this should be the same as U2iS - - typename TwoIndexRep< Nc, AntiSymmetric>::LatticeMatrix Diff_check_mat2A = Ur02A - U2iAS; - std::cout << GridLogMessage << "Projections structure check group difference (Two Index anti-Symmetric): " << norm2(Diff_check_mat2A) << std::endl; - -} else { - std::cout << GridLogMessage << "Skipping Two Index anti-Symmetric tests " - "because representation is trivial (dim = 1)" - << std::endl; -} - - - - - - - - +#endif Grid_finalize(); } diff --git a/tests/core/Test_reunitarise.cc b/tests/core/Test_reunitarise.cc index 6644be1a..10423159 100644 --- a/tests/core/Test_reunitarise.cc +++ b/tests/core/Test_reunitarise.cc @@ -122,14 +122,15 @@ int main (int argc, char ** argv) std::cout << "Determinant defect before projection " <<norm2(detU)<<std::endl; tmp = U*adj(U) - ident; std::cout << "Unitarity check before projection " << norm2(tmp)<<std::endl; - +#if (Nc == 3) ProjectSU3(U); detU= Determinant(U) ; detU= detU -1.0; std::cout << "Determinant ProjectSU3 defect " <<norm2(detU)<<std::endl; tmp = U*adj(U) - ident; std::cout << "Unitarity check after projection " << norm2(tmp)<<std::endl; - +#endif + ProjectSUn(UU); detUU= Determinant(UU); detUU= detUU -1.0; diff --git a/tests/hmc/Test_hmc_ScalarActionNxN.cc b/tests/hmc/Test_hmc_ScalarActionNxN.cc index 0c398306..726ecd4a 100644 --- a/tests/hmc/Test_hmc_ScalarActionNxN.cc +++ b/tests/hmc/Test_hmc_ScalarActionNxN.cc @@ -132,8 +132,8 @@ int main(int argc, char **argv) { // Checkpointer definition CheckpointerParameters CPparams(Reader); - //TheHMC.Resources.LoadBinaryCheckpointer(CPparams); - TheHMC.Resources.LoadScidacCheckpointer(CPparams, SPar); + TheHMC.Resources.LoadBinaryCheckpointer(CPparams); + //TheHMC.Resources.LoadScidacCheckpointer(CPparams, SPar); this breaks for compilation without lime RNGModuleParameters RNGpar(Reader); TheHMC.Resources.SetRNGSeeds(RNGpar); diff --git a/tests/hmc/Test_hmc_WG_Production.cc b/tests/hmc/Test_hmc_WG_Production.cc index b16c073b..8cd74791 100644 --- a/tests/hmc/Test_hmc_WG_Production.cc +++ b/tests/hmc/Test_hmc_WG_Production.cc @@ -74,10 +74,10 @@ int main(int argc, char **argv) { // Checkpointer definition CheckpointerParameters CPparams(Reader); - //TheHMC.Resources.LoadNerscCheckpointer(CPparams); + TheHMC.Resources.LoadNerscCheckpointer(CPparams); - // Store metadata in the Scidac checkpointer - TheHMC.Resources.LoadScidacCheckpointer(CPparams, WilsonPar); + // Store metadata in the Scidac checkpointer - obviously breaks without LIME + //TheHMC.Resources.LoadScidacCheckpointer(CPparams, WilsonPar); RNGModuleParameters RNGpar(Reader); TheHMC.Resources.SetRNGSeeds(RNGpar); diff --git a/tests/lanczos/Test_compressed_lanczos.cc b/tests/lanczos/Test_compressed_lanczos.cc index 3fa3c490..d7d0d52d 100644 --- a/tests/lanczos/Test_compressed_lanczos.cc +++ b/tests/lanczos/Test_compressed_lanczos.cc @@ -37,6 +37,8 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> using namespace std; using namespace Grid; +#ifdef HAVE_LIME + template<class Fobj,class CComplex,int nbasis> class LocalCoherenceLanczosScidac : public LocalCoherenceLanczos<Fobj,CComplex,nbasis> { @@ -249,3 +251,11 @@ int main (int argc, char ** argv) { Grid_finalize(); } +#else + +int main( void ) +{ + return 0 ; +} + +#endif // HAVE_LIME_H diff --git a/tests/lanczos/Test_dwf_compressed_lanczos_reorg.cc b/tests/lanczos/Test_dwf_compressed_lanczos_reorg.cc index ecd0e629..669a7b6d 100644 --- a/tests/lanczos/Test_dwf_compressed_lanczos_reorg.cc +++ b/tests/lanczos/Test_dwf_compressed_lanczos_reorg.cc @@ -36,7 +36,8 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> using namespace std; using namespace Grid; - ; + +#ifdef HAVE_LIME template<class Fobj,class CComplex,int nbasis> class LocalCoherenceLanczosScidac : public LocalCoherenceLanczos<Fobj,CComplex,nbasis> @@ -250,3 +251,11 @@ int main (int argc, char ** argv) { Grid_finalize(); } +#else + +int main( void ) +{ + return 0 ; +} + +#endif diff --git a/tests/solver/Test_coarse_even_odd.cc b/tests/solver/Test_coarse_even_odd.cc index dfbab747..c7127121 100644 --- a/tests/solver/Test_coarse_even_odd.cc +++ b/tests/solver/Test_coarse_even_odd.cc @@ -93,8 +93,16 @@ int main(int argc, char** argv) { // Setup of Dirac Matrix and Operator // ///////////////////////////////////////////////////////////////////////////// - LatticeGaugeField Umu(Grid_f); SU3::HotConfiguration(pRNG_f, Umu); - + LatticeGaugeField Umu(Grid_f); +#if (Nc==2) + SU2::HotConfiguration(pRNG_f, Umu); +#elif (defined Nc==3) + SU3::HotConfiguration(pRNG_f, Umu); +#elif (defined Nc==4) + SU4::HotConfiguration(pRNG_f, Umu); +#elif (defined Nc==5) + SU5::HotConfiguration(pRNG_f, Umu); +#endif RealD checkTolerance = (getPrecision<LatticeFermion>::value == 1) ? 1e-7 : 1e-15; RealD mass = -0.30; diff --git a/tests/solver/Test_dwf_mrhs_cg.cc b/tests/solver/Test_dwf_mrhs_cg.cc index b912ba4f..4242f64b 100644 --- a/tests/solver/Test_dwf_mrhs_cg.cc +++ b/tests/solver/Test_dwf_mrhs_cg.cc @@ -34,6 +34,7 @@ using namespace Grid; int main (int argc, char ** argv) { +#ifdef HAVE_LIME typedef typename DomainWallFermionR::FermionField FermionField; typedef typename DomainWallFermionR::ComplexField ComplexField; typename DomainWallFermionR::ImplParams params; @@ -237,4 +238,5 @@ int main (int argc, char ** argv) } Grid_finalize(); +#endif // HAVE_LIME } From 2bf3b4d5765b9db6d73fc4b3795971f851cb7d0f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 7 Dec 2021 09:02:02 -0800 Subject: [PATCH 274/399] Update to reduce memory footpring in benchmark test --- Grid/communicator/Communicator_mpi3.cc | 4 +- Grid/threads/Accelerator.h | 2 +- benchmarks/Benchmark_dwf_fp32.cc | 61 ++++++++++++++++++-------- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index 305a3a9b..162180bc 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -388,6 +388,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques // TODO : make a OMP loop on CPU, call threaded bcopy void *shm = (void *) this->ShmBufferTranslate(dest,recv); assert(shm!=NULL); + std::cout <<"acceleratorCopyDeviceToDeviceAsynch"<< std::endl; acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); } @@ -399,12 +400,13 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques } void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsRequest_t> &list,int dir) { + acceleratorCopySynchronise(); std::cout << "Copy Synchronised\n"<<std::endl; + int nreq=list.size(); if (nreq==0) return; std::vector<MPI_Status> status(nreq); - acceleratorCopySynchronise(); int ierr = MPI_Waitall(nreq,&list[0],&status[0]); assert(ierr==0); list.resize(0); diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index cec0600f..8be712ba 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -306,7 +306,7 @@ inline void acceleratorFreeDevice(void *ptr){free(ptr,*theGridAccelerator);}; inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { theGridAccelerator->memcpy(to,from,bytes); } -inline void acceleratorCopySynchronise(void) { theGridAccelerator->wait(); } +inline void acceleratorCopySynchronise(void) { theGridAccelerator->wait(); std::cout<<"acceleratorCopySynchronise() wait "<<std::endl; } inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { theGridAccelerator->memcpy(to,from,bytes); theGridAccelerator->wait();} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ theGridAccelerator->memcpy(to,from,bytes); theGridAccelerator->wait();} inline void acceleratorMemSet(void *base,int value,size_t bytes) { theGridAccelerator->memset(base,value,bytes); theGridAccelerator->wait();} diff --git a/benchmarks/Benchmark_dwf_fp32.cc b/benchmarks/Benchmark_dwf_fp32.cc index d48486c0..4edf7c16 100644 --- a/benchmarks/Benchmark_dwf_fp32.cc +++ b/benchmarks/Benchmark_dwf_fp32.cc @@ -126,19 +126,10 @@ int main (int argc, char ** argv) // Naive wilson implementation //////////////////////////////////// // replicate across fifth dimension - LatticeGaugeFieldF Umu5d(FGrid); - std::vector<LatticeColourMatrixF> U(4,FGrid); - { - autoView( Umu5d_v, Umu5d, CpuWrite); - autoView( Umu_v , Umu , CpuRead); - for(int ss=0;ss<Umu.Grid()->oSites();ss++){ - for(int s=0;s<Ls;s++){ - Umu5d_v[Ls*ss+s] = Umu_v[ss]; - } - } - } + // LatticeGaugeFieldF Umu5d(FGrid); + std::vector<LatticeColourMatrixF> U(4,UGrid); for(int mu=0;mu<Nd;mu++){ - U[mu] = PeekIndex<LorentzIndex>(Umu5d,mu); + U[mu] = PeekIndex<LorentzIndex>(Umu,mu); } std::cout << GridLogMessage << "Setting up Cshift based reference " << std::endl; @@ -147,10 +138,28 @@ int main (int argc, char ** argv) ref = Zero(); for(int mu=0;mu<Nd;mu++){ - tmp = U[mu]*Cshift(src,mu+1,1); + tmp = Cshift(src,mu+1,1); + { + autoView( tmp_v , tmp , CpuWrite); + autoView( U_v , U[mu] , CpuRead); + for(int ss=0;ss<U[mu].Grid()->oSites();ss++){ + for(int s=0;s<Ls;s++){ + tmp_v[Ls*ss+s] = U_v[ss]*tmp_v[Ls*ss+s]; + } + } + } ref=ref + tmp - Gamma(Gmu[mu])*tmp; - tmp =adj(U[mu])*src; + { + autoView( tmp_v , tmp , CpuWrite); + autoView( U_v , U[mu] , CpuRead); + autoView( src_v, src , CpuRead); + for(int ss=0;ss<U[mu].Grid()->oSites();ss++){ + for(int s=0;s<Ls;s++){ + tmp_v[Ls*ss+s] = adj(U_v[ss])*src_v[Ls*ss+s]; + } + } + } tmp =Cshift(tmp,mu+1,-1); ref=ref + tmp + Gamma(Gmu[mu])*tmp; } @@ -242,16 +251,30 @@ int main (int argc, char ** argv) for(int mu=0;mu<Nd;mu++){ // ref = src - Gamma(Gamma::Algebra::GammaX)* src ; // 1+gamma_x - tmp = U[mu]*Cshift(src,mu+1,1); + tmp = Cshift(src,mu+1,1); { autoView( ref_v, ref, CpuWrite); autoView( tmp_v, tmp, CpuRead); - for(int i=0;i<ref_v.size();i++){ - ref_v[i]+= tmp_v[i] + Gamma(Gmu[mu])*tmp_v[i]; ; + autoView( U_v , U[mu] , CpuRead); + for(int ss=0;ss<U[mu].Grid()->oSites();ss++){ + for(int s=0;s<Ls;s++){ + int i=s+Ls*ss; + ref_v[i]+= U_v[ss]*(tmp_v[i] + Gamma(Gmu[mu])*tmp_v[i]); ; + } } } - - tmp =adj(U[mu])*src; + + { + autoView( tmp_v , tmp , CpuWrite); + autoView( U_v , U[mu] , CpuRead); + autoView( src_v, src , CpuRead); + for(int ss=0;ss<U[mu].Grid()->oSites();ss++){ + for(int s=0;s<Ls;s++){ + tmp_v[Ls*ss+s] = adj(U_v[ss])*src_v[Ls*ss+s]; + } + } + } + // tmp =adj(U[mu])*src; tmp =Cshift(tmp,mu+1,-1); { autoView( ref_v, ref, CpuWrite); From 135808dcfa767edf988976ae31d2876bb6389f8b Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 7 Dec 2021 16:24:24 -0500 Subject: [PATCH 275/399] Less verbose --- Grid/communicator/Communicator_mpi3.cc | 5 +++-- benchmarks/Benchmark_dwf_fp32.cc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index 162180bc..7b3e8847 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -388,7 +388,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques // TODO : make a OMP loop on CPU, call threaded bcopy void *shm = (void *) this->ShmBufferTranslate(dest,recv); assert(shm!=NULL); - std::cout <<"acceleratorCopyDeviceToDeviceAsynch"<< std::endl; + // std::cout <<"acceleratorCopyDeviceToDeviceAsynch"<< std::endl; acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); } @@ -400,7 +400,8 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques } void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsRequest_t> &list,int dir) { - acceleratorCopySynchronise(); std::cout << "Copy Synchronised\n"<<std::endl; + // std::cout << "Copy Synchronised\n"<<std::endl; + acceleratorCopySynchronise(); int nreq=list.size(); diff --git a/benchmarks/Benchmark_dwf_fp32.cc b/benchmarks/Benchmark_dwf_fp32.cc index 4edf7c16..7fa37fb6 100644 --- a/benchmarks/Benchmark_dwf_fp32.cc +++ b/benchmarks/Benchmark_dwf_fp32.cc @@ -191,7 +191,7 @@ int main (int argc, char ** argv) std::cout << GridLogMessage<< "*****************************************************************" <<std::endl; DomainWallFermionF Dw(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5); - int ncall =3000; + int ncall =300; if (1) { FGrid->Barrier(); From b4f8e87982b1db0f964538eb69bd6cf9b6388125 Mon Sep 17 00:00:00 2001 From: Daniel Richtmann <daniel.richtmann@gmail.com> Date: Tue, 1 Feb 2022 23:08:09 +0100 Subject: [PATCH 276/399] Have Grid's cli interface understand floats --- Grid/util/Init.cc | 7 +++++++ Grid/util/Init.h | 1 + 2 files changed, 8 insertions(+) diff --git a/Grid/util/Init.cc b/Grid/util/Init.cc index 697f3ac1..6992129e 100644 --- a/Grid/util/Init.cc +++ b/Grid/util/Init.cc @@ -167,6 +167,13 @@ void GridCmdOptionInt(std::string &str,int & val) return; } +void GridCmdOptionFloat(std::string &str,float & val) +{ + std::stringstream ss(str); + ss>>val; + return; +} + void GridParseLayout(char **argv,int argc, Coordinate &latt_c, diff --git a/Grid/util/Init.h b/Grid/util/Init.h index 4eb8f06c..585660a1 100644 --- a/Grid/util/Init.h +++ b/Grid/util/Init.h @@ -57,6 +57,7 @@ void GridCmdOptionCSL(std::string str,std::vector<std::string> & vec); template<class VectorInt> void GridCmdOptionIntVector(const std::string &str,VectorInt & vec); void GridCmdOptionInt(std::string &str,int & val); +void GridCmdOptionFloat(std::string &str,float & val); void GridParseLayout(char **argv,int argc, From e83423fee65743deb55023986e9765fbda431e7f Mon Sep 17 00:00:00 2001 From: Daniel Richtmann <daniel.richtmann@gmail.com> Date: Tue, 1 Feb 2022 20:50:09 +0100 Subject: [PATCH 277/399] Refactor clover to align with other files and prepare for upcoming changes --- Grid/qcd/action/fermion/WilsonCloverFermion.h | 301 ++---------------- Grid/qcd/action/fermion/WilsonCloverHelpers.h | 191 +++++++++++ Grid/qcd/action/fermion/WilsonCloverTypes.h | 49 +++ .../WilsonCloverFermionImplementation.h | 163 +++++++++- 4 files changed, 410 insertions(+), 294 deletions(-) create mode 100644 Grid/qcd/action/fermion/WilsonCloverHelpers.h create mode 100644 Grid/qcd/action/fermion/WilsonCloverTypes.h diff --git a/Grid/qcd/action/fermion/WilsonCloverFermion.h b/Grid/qcd/action/fermion/WilsonCloverFermion.h index 92af7111..cd19fc10 100644 --- a/Grid/qcd/action/fermion/WilsonCloverFermion.h +++ b/Grid/qcd/action/fermion/WilsonCloverFermion.h @@ -4,10 +4,11 @@ Source file: ./lib/qcd/action/fermion/WilsonCloverFermion.h - Copyright (C) 2017 + Copyright (C) 2017 - 2022 Author: Guido Cossu <guido.cossu@ed.ac.uk> Author: David Preti <> + Author: Daniel Richtmann <daniel.richtmann@gmail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,7 +30,8 @@ #pragma once -#include <Grid/Grid.h> +#include <Grid/qcd/action/fermion/WilsonCloverTypes.h> +#include <Grid/qcd/action/fermion/WilsonCloverHelpers.h> NAMESPACE_BEGIN(Grid); @@ -50,18 +52,15 @@ NAMESPACE_BEGIN(Grid); ////////////////////////////////////////////////////////////////// template <class Impl> -class WilsonCloverFermion : public WilsonFermion<Impl> +class WilsonCloverFermion : public WilsonFermion<Impl>, + public WilsonCloverHelpers<Impl> { public: - // Types definitions INHERIT_IMPL_TYPES(Impl); - template <typename vtype> - using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; - typedef iImplClover<Simd> SiteCloverType; - typedef Lattice<SiteCloverType> CloverFieldType; + INHERIT_CLOVER_TYPES(Impl); -public: - typedef WilsonFermion<Impl> WilsonBase; + typedef WilsonFermion<Impl> WilsonBase; + typedef WilsonCloverHelpers<Impl> Helpers; virtual int ConstEE(void) { return 0; }; virtual void Instantiatable(void){}; @@ -72,42 +71,7 @@ public: const RealD _csw_r = 0.0, const RealD _csw_t = 0.0, const WilsonAnisotropyCoefficients &clover_anisotropy = WilsonAnisotropyCoefficients(), - const ImplParams &impl_p = ImplParams()) : WilsonFermion<Impl>(_Umu, - Fgrid, - Hgrid, - _mass, impl_p, clover_anisotropy), - CloverTerm(&Fgrid), - CloverTermInv(&Fgrid), - CloverTermEven(&Hgrid), - CloverTermOdd(&Hgrid), - CloverTermInvEven(&Hgrid), - CloverTermInvOdd(&Hgrid), - CloverTermDagEven(&Hgrid), - CloverTermDagOdd(&Hgrid), - CloverTermInvDagEven(&Hgrid), - CloverTermInvDagOdd(&Hgrid) - { - assert(Nd == 4); // require 4 dimensions - - if (clover_anisotropy.isAnisotropic) - { - csw_r = _csw_r * 0.5 / clover_anisotropy.xi_0; - diag_mass = _mass + 1.0 + (Nd - 1) * (clover_anisotropy.nu / clover_anisotropy.xi_0); - } - else - { - csw_r = _csw_r * 0.5; - diag_mass = 4.0 + _mass; - } - csw_t = _csw_t * 0.5; - - if (csw_r == 0) - std::cout << GridLogWarning << "Initializing WilsonCloverFermion with csw_r = 0" << std::endl; - if (csw_t == 0) - std::cout << GridLogWarning << "Initializing WilsonCloverFermion with csw_t = 0" << std::endl; - - ImportGauge(_Umu); - } + const ImplParams &impl_p = ImplParams()); virtual void M(const FermionField &in, FermionField &out); virtual void Mdag(const FermionField &in, FermionField &out); @@ -124,250 +88,21 @@ public: void ImportGauge(const GaugeField &_Umu); // Derivative parts unpreconditioned pseudofermions - void MDeriv(GaugeField &force, const FermionField &X, const FermionField &Y, int dag) - { - conformable(X.Grid(), Y.Grid()); - conformable(X.Grid(), force.Grid()); - GaugeLinkField force_mu(force.Grid()), lambda(force.Grid()); - GaugeField clover_force(force.Grid()); - PropagatorField Lambda(force.Grid()); + void MDeriv(GaugeField &force, const FermionField &X, const FermionField &Y, int dag); - // Guido: Here we are hitting some performance issues: - // need to extract the components of the DoubledGaugeField - // for each call - // Possible solution - // Create a vector object to store them? (cons: wasting space) - std::vector<GaugeLinkField> U(Nd, this->Umu.Grid()); - - Impl::extractLinkField(U, this->Umu); - - force = Zero(); - // Derivative of the Wilson hopping term - this->DhopDeriv(force, X, Y, dag); - - /////////////////////////////////////////////////////////// - // Clover term derivative - /////////////////////////////////////////////////////////// - Impl::outerProductImpl(Lambda, X, Y); - //std::cout << "Lambda:" << Lambda << std::endl; - - Gamma::Algebra sigma[] = { - Gamma::Algebra::SigmaXY, - Gamma::Algebra::SigmaXZ, - Gamma::Algebra::SigmaXT, - Gamma::Algebra::MinusSigmaXY, - Gamma::Algebra::SigmaYZ, - Gamma::Algebra::SigmaYT, - Gamma::Algebra::MinusSigmaXZ, - Gamma::Algebra::MinusSigmaYZ, - Gamma::Algebra::SigmaZT, - Gamma::Algebra::MinusSigmaXT, - Gamma::Algebra::MinusSigmaYT, - Gamma::Algebra::MinusSigmaZT}; - - /* - sigma_{\mu \nu}= - | 0 sigma[0] sigma[1] sigma[2] | - | sigma[3] 0 sigma[4] sigma[5] | - | sigma[6] sigma[7] 0 sigma[8] | - | sigma[9] sigma[10] sigma[11] 0 | - */ - - int count = 0; - clover_force = Zero(); - for (int mu = 0; mu < 4; mu++) - { - force_mu = Zero(); - for (int nu = 0; nu < 4; nu++) - { - if (mu == nu) - continue; - - RealD factor; - if (nu == 4 || mu == 4) - { - factor = 2.0 * csw_t; - } - else - { - factor = 2.0 * csw_r; - } - PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked - Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok - force_mu -= factor*Cmunu(U, lambda, mu, nu); // checked - count++; - } - - pokeLorentz(clover_force, U[mu] * force_mu, mu); - } - //clover_force *= csw; - force += clover_force; - } - - // Computing C_{\mu \nu}(x) as in Eq.(B.39) in Zbigniew Sroczynski's PhD thesis - GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) - { - conformable(lambda.Grid(), U[0].Grid()); - GaugeLinkField out(lambda.Grid()), tmp(lambda.Grid()); - // insertion in upper staple - // please check redundancy of shift operations - - // C1+ - tmp = lambda * U[nu]; - out = Impl::ShiftStaple(Impl::CovShiftForward(tmp, nu, Impl::CovShiftBackward(U[mu], mu, Impl::CovShiftIdentityBackward(U[nu], nu))), mu); - - // C2+ - tmp = U[mu] * Impl::ShiftStaple(adj(lambda), mu); - out += Impl::ShiftStaple(Impl::CovShiftForward(U[nu], nu, Impl::CovShiftBackward(tmp, mu, Impl::CovShiftIdentityBackward(U[nu], nu))), mu); - - // C3+ - tmp = U[nu] * Impl::ShiftStaple(adj(lambda), nu); - out += Impl::ShiftStaple(Impl::CovShiftForward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, Impl::CovShiftIdentityBackward(tmp, nu))), mu); - - // C4+ - out += Impl::ShiftStaple(Impl::CovShiftForward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, Impl::CovShiftIdentityBackward(U[nu], nu))), mu) * lambda; - - // insertion in lower staple - // C1- - out -= Impl::ShiftStaple(lambda, mu) * Impl::ShiftStaple(Impl::CovShiftBackward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, U[nu])), mu); - - // C2- - tmp = adj(lambda) * U[nu]; - out -= Impl::ShiftStaple(Impl::CovShiftBackward(tmp, nu, Impl::CovShiftBackward(U[mu], mu, U[nu])), mu); - - // C3- - tmp = lambda * U[nu]; - out -= Impl::ShiftStaple(Impl::CovShiftBackward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, tmp)), mu); - - // C4- - out -= Impl::ShiftStaple(Impl::CovShiftBackward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, U[nu])), mu) * lambda; - - return out; - } - -protected: +public: // here fixing the 4 dimensions, make it more general? RealD csw_r; // Clover coefficient - spatial RealD csw_t; // Clover coefficient - temporal RealD diag_mass; // Mass term - CloverFieldType CloverTerm, CloverTermInv; // Clover term - CloverFieldType CloverTermEven, CloverTermOdd; // Clover term EO - CloverFieldType CloverTermInvEven, CloverTermInvOdd; // Clover term Inv EO - CloverFieldType CloverTermDagEven, CloverTermDagOdd; // Clover term Dag EO - CloverFieldType CloverTermInvDagEven, CloverTermInvDagOdd; // Clover term Inv Dag EO - - public: - // eventually these can be compressed into 6x6 blocks instead of the 12x12 - // using the DeGrand-Rossi basis for the gamma matrices - CloverFieldType fillCloverYZ(const GaugeLinkField &F) - { - CloverFieldType T(F.Grid()); - T = Zero(); - autoView(T_v,T,AcceleratorWrite); - autoView(F_v,F,AcceleratorRead); - accelerator_for(i, CloverTerm.Grid()->oSites(),1, - { - T_v[i]()(0, 1) = timesMinusI(F_v[i]()()); - T_v[i]()(1, 0) = timesMinusI(F_v[i]()()); - T_v[i]()(2, 3) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 2) = timesMinusI(F_v[i]()()); - }); - - return T; - } - - CloverFieldType fillCloverXZ(const GaugeLinkField &F) - { - CloverFieldType T(F.Grid()); - T = Zero(); - - autoView(T_v, T,AcceleratorWrite); - autoView(F_v, F,AcceleratorRead); - accelerator_for(i, CloverTerm.Grid()->oSites(),1, - { - T_v[i]()(0, 1) = -F_v[i]()(); - T_v[i]()(1, 0) = F_v[i]()(); - T_v[i]()(2, 3) = -F_v[i]()(); - T_v[i]()(3, 2) = F_v[i]()(); - }); - - return T; - } - - CloverFieldType fillCloverXY(const GaugeLinkField &F) - { - CloverFieldType T(F.Grid()); - T = Zero(); - - autoView(T_v,T,AcceleratorWrite); - autoView(F_v,F,AcceleratorRead); - accelerator_for(i, CloverTerm.Grid()->oSites(),1, - { - T_v[i]()(0, 0) = timesMinusI(F_v[i]()()); - T_v[i]()(1, 1) = timesI(F_v[i]()()); - T_v[i]()(2, 2) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 3) = timesI(F_v[i]()()); - }); - - return T; - } - - CloverFieldType fillCloverXT(const GaugeLinkField &F) - { - CloverFieldType T(F.Grid()); - T = Zero(); - - autoView( T_v , T, AcceleratorWrite); - autoView( F_v , F, AcceleratorRead); - accelerator_for(i, CloverTerm.Grid()->oSites(),1, - { - T_v[i]()(0, 1) = timesI(F_v[i]()()); - T_v[i]()(1, 0) = timesI(F_v[i]()()); - T_v[i]()(2, 3) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 2) = timesMinusI(F_v[i]()()); - }); - - return T; - } - - CloverFieldType fillCloverYT(const GaugeLinkField &F) - { - CloverFieldType T(F.Grid()); - T = Zero(); - - autoView( T_v ,T,AcceleratorWrite); - autoView( F_v ,F,AcceleratorRead); - accelerator_for(i, CloverTerm.Grid()->oSites(),1, - { - T_v[i]()(0, 1) = -(F_v[i]()()); - T_v[i]()(1, 0) = (F_v[i]()()); - T_v[i]()(2, 3) = (F_v[i]()()); - T_v[i]()(3, 2) = -(F_v[i]()()); - }); - - return T; - } - - CloverFieldType fillCloverZT(const GaugeLinkField &F) - { - CloverFieldType T(F.Grid()); - - T = Zero(); - - autoView( T_v , T,AcceleratorWrite); - autoView( F_v , F,AcceleratorRead); - accelerator_for(i, CloverTerm.Grid()->oSites(),1, - { - T_v[i]()(0, 0) = timesI(F_v[i]()()); - T_v[i]()(1, 1) = timesMinusI(F_v[i]()()); - T_v[i]()(2, 2) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 3) = timesI(F_v[i]()()); - }); - - return T; - } + CloverField CloverTerm, CloverTermInv; // Clover term + CloverField CloverTermEven, CloverTermOdd; // Clover term EO + CloverField CloverTermInvEven, CloverTermInvOdd; // Clover term Inv EO + CloverField CloverTermDagEven, CloverTermDagOdd; // Clover term Dag EO + CloverField CloverTermInvDagEven, CloverTermInvDagOdd; // Clover term Inv Dag EO }; + NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/WilsonCloverHelpers.h b/Grid/qcd/action/fermion/WilsonCloverHelpers.h new file mode 100644 index 00000000..80f9032c --- /dev/null +++ b/Grid/qcd/action/fermion/WilsonCloverHelpers.h @@ -0,0 +1,191 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/qcd/action/fermion/WilsonCloverHelpers.h + + Copyright (C) 2021 - 2022 + + Author: Daniel Richtmann <daniel.richtmann@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ + +#pragma once + +// Helper routines that implement common clover functionality + +NAMESPACE_BEGIN(Grid); + +template<class Impl> class WilsonCloverHelpers { +public: + + INHERIT_IMPL_TYPES(Impl); + INHERIT_CLOVER_TYPES(Impl); + + // Computing C_{\mu \nu}(x) as in Eq.(B.39) in Zbigniew Sroczynski's PhD thesis + static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) + { + conformable(lambda.Grid(), U[0].Grid()); + GaugeLinkField out(lambda.Grid()), tmp(lambda.Grid()); + // insertion in upper staple + // please check redundancy of shift operations + + // C1+ + tmp = lambda * U[nu]; + out = Impl::ShiftStaple(Impl::CovShiftForward(tmp, nu, Impl::CovShiftBackward(U[mu], mu, Impl::CovShiftIdentityBackward(U[nu], nu))), mu); + + // C2+ + tmp = U[mu] * Impl::ShiftStaple(adj(lambda), mu); + out += Impl::ShiftStaple(Impl::CovShiftForward(U[nu], nu, Impl::CovShiftBackward(tmp, mu, Impl::CovShiftIdentityBackward(U[nu], nu))), mu); + + // C3+ + tmp = U[nu] * Impl::ShiftStaple(adj(lambda), nu); + out += Impl::ShiftStaple(Impl::CovShiftForward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, Impl::CovShiftIdentityBackward(tmp, nu))), mu); + + // C4+ + out += Impl::ShiftStaple(Impl::CovShiftForward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, Impl::CovShiftIdentityBackward(U[nu], nu))), mu) * lambda; + + // insertion in lower staple + // C1- + out -= Impl::ShiftStaple(lambda, mu) * Impl::ShiftStaple(Impl::CovShiftBackward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, U[nu])), mu); + + // C2- + tmp = adj(lambda) * U[nu]; + out -= Impl::ShiftStaple(Impl::CovShiftBackward(tmp, nu, Impl::CovShiftBackward(U[mu], mu, U[nu])), mu); + + // C3- + tmp = lambda * U[nu]; + out -= Impl::ShiftStaple(Impl::CovShiftBackward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, tmp)), mu); + + // C4- + out -= Impl::ShiftStaple(Impl::CovShiftBackward(U[nu], nu, Impl::CovShiftBackward(U[mu], mu, U[nu])), mu) * lambda; + + return out; + } + + static CloverField fillCloverYZ(const GaugeLinkField &F) + { + CloverField T(F.Grid()); + T = Zero(); + autoView(T_v,T,AcceleratorWrite); + autoView(F_v,F,AcceleratorRead); + accelerator_for(i, T.Grid()->oSites(),1, + { + T_v[i]()(0, 1) = timesMinusI(F_v[i]()()); + T_v[i]()(1, 0) = timesMinusI(F_v[i]()()); + T_v[i]()(2, 3) = timesMinusI(F_v[i]()()); + T_v[i]()(3, 2) = timesMinusI(F_v[i]()()); + }); + + return T; + } + + static CloverField fillCloverXZ(const GaugeLinkField &F) + { + CloverField T(F.Grid()); + T = Zero(); + + autoView(T_v, T,AcceleratorWrite); + autoView(F_v, F,AcceleratorRead); + accelerator_for(i, T.Grid()->oSites(),1, + { + T_v[i]()(0, 1) = -F_v[i]()(); + T_v[i]()(1, 0) = F_v[i]()(); + T_v[i]()(2, 3) = -F_v[i]()(); + T_v[i]()(3, 2) = F_v[i]()(); + }); + + return T; + } + + static CloverField fillCloverXY(const GaugeLinkField &F) + { + CloverField T(F.Grid()); + T = Zero(); + + autoView(T_v,T,AcceleratorWrite); + autoView(F_v,F,AcceleratorRead); + accelerator_for(i, T.Grid()->oSites(),1, + { + T_v[i]()(0, 0) = timesMinusI(F_v[i]()()); + T_v[i]()(1, 1) = timesI(F_v[i]()()); + T_v[i]()(2, 2) = timesMinusI(F_v[i]()()); + T_v[i]()(3, 3) = timesI(F_v[i]()()); + }); + + return T; + } + + static CloverField fillCloverXT(const GaugeLinkField &F) + { + CloverField T(F.Grid()); + T = Zero(); + + autoView( T_v , T, AcceleratorWrite); + autoView( F_v , F, AcceleratorRead); + accelerator_for(i, T.Grid()->oSites(),1, + { + T_v[i]()(0, 1) = timesI(F_v[i]()()); + T_v[i]()(1, 0) = timesI(F_v[i]()()); + T_v[i]()(2, 3) = timesMinusI(F_v[i]()()); + T_v[i]()(3, 2) = timesMinusI(F_v[i]()()); + }); + + return T; + } + + static CloverField fillCloverYT(const GaugeLinkField &F) + { + CloverField T(F.Grid()); + T = Zero(); + + autoView( T_v ,T,AcceleratorWrite); + autoView( F_v ,F,AcceleratorRead); + accelerator_for(i, T.Grid()->oSites(),1, + { + T_v[i]()(0, 1) = -(F_v[i]()()); + T_v[i]()(1, 0) = (F_v[i]()()); + T_v[i]()(2, 3) = (F_v[i]()()); + T_v[i]()(3, 2) = -(F_v[i]()()); + }); + + return T; + } + + static CloverField fillCloverZT(const GaugeLinkField &F) + { + CloverField T(F.Grid()); + + T = Zero(); + + autoView( T_v , T,AcceleratorWrite); + autoView( F_v , F,AcceleratorRead); + accelerator_for(i, T.Grid()->oSites(),1, + { + T_v[i]()(0, 0) = timesI(F_v[i]()()); + T_v[i]()(1, 1) = timesMinusI(F_v[i]()()); + T_v[i]()(2, 2) = timesMinusI(F_v[i]()()); + T_v[i]()(3, 3) = timesI(F_v[i]()()); + }); + + return T; + } +}; + +NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/WilsonCloverTypes.h b/Grid/qcd/action/fermion/WilsonCloverTypes.h new file mode 100644 index 00000000..4428259f --- /dev/null +++ b/Grid/qcd/action/fermion/WilsonCloverTypes.h @@ -0,0 +1,49 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/qcd/action/fermion/WilsonCloverTypes.h + + Copyright (C) 2021 - 2022 + + Author: Daniel Richtmann <daniel.richtmann@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ + +#pragma once + +NAMESPACE_BEGIN(Grid); + +template<class Impl> +class WilsonCloverTypes { +public: + INHERIT_IMPL_TYPES(Impl); + + template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; + + typedef iImplClover<Simd> SiteClover; + + typedef Lattice<SiteClover> CloverField; +}; + +#define INHERIT_CLOVER_TYPES(Impl) \ + typedef typename WilsonCloverTypes<Impl>::SiteClover SiteClover; \ + typedef typename WilsonCloverTypes<Impl>::CloverField CloverField; + +NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h index 3032a80c..fd81af11 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h @@ -2,12 +2,13 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: ./lib/qcd/action/fermion/WilsonCloverFermion.cc + Source file: ./lib/qcd/action/fermion/WilsonCloverFermionImplementation.h - Copyright (C) 2017 + Copyright (C) 2017 - 2022 Author: paboyle <paboyle@ph.ed.ac.uk> Author: Guido Cossu <guido.cossu@ed.ac.uk> + Author: Daniel Richtmann <daniel.richtmann@gmail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,6 +34,45 @@ NAMESPACE_BEGIN(Grid); +template<class Impl> +WilsonCloverFermion<Impl>::WilsonCloverFermion(GaugeField& _Umu, + GridCartesian& Fgrid, + GridRedBlackCartesian& Hgrid, + const RealD _mass, + const RealD _csw_r, + const RealD _csw_t, + const WilsonAnisotropyCoefficients& clover_anisotropy, + const ImplParams& impl_p) + : WilsonFermion<Impl>(_Umu, Fgrid, Hgrid, _mass, impl_p, clover_anisotropy) + , CloverTerm(&Fgrid) + , CloverTermInv(&Fgrid) + , CloverTermEven(&Hgrid) + , CloverTermOdd(&Hgrid) + , CloverTermInvEven(&Hgrid) + , CloverTermInvOdd(&Hgrid) + , CloverTermDagEven(&Hgrid) + , CloverTermDagOdd(&Hgrid) + , CloverTermInvDagEven(&Hgrid) + , CloverTermInvDagOdd(&Hgrid) { + assert(Nd == 4); // require 4 dimensions + + if(clover_anisotropy.isAnisotropic) { + csw_r = _csw_r * 0.5 / clover_anisotropy.xi_0; + diag_mass = _mass + 1.0 + (Nd - 1) * (clover_anisotropy.nu / clover_anisotropy.xi_0); + } else { + csw_r = _csw_r * 0.5; + diag_mass = 4.0 + _mass; + } + csw_t = _csw_t * 0.5; + + if(csw_r == 0) + std::cout << GridLogWarning << "Initializing WilsonCloverFermion with csw_r = 0" << std::endl; + if(csw_t == 0) + std::cout << GridLogWarning << "Initializing WilsonCloverFermion with csw_t = 0" << std::endl; + + ImportGauge(_Umu); +} + // *NOT* EO template <class Impl> void WilsonCloverFermion<Impl>::M(const FermionField &in, FermionField &out) @@ -67,10 +107,13 @@ void WilsonCloverFermion<Impl>::Mdag(const FermionField &in, FermionField &out) template <class Impl> void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) { + double t0 = usecond(); WilsonFermion<Impl>::ImportGauge(_Umu); + double t1 = usecond(); GridBase *grid = _Umu.Grid(); typename Impl::GaugeLinkField Bx(grid), By(grid), Bz(grid), Ex(grid), Ey(grid), Ez(grid); + double t2 = usecond(); // Compute the field strength terms mu>nu WilsonLoops<Impl>::FieldStrength(Bx, _Umu, Zdir, Ydir); WilsonLoops<Impl>::FieldStrength(By, _Umu, Zdir, Xdir); @@ -79,19 +122,22 @@ void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) WilsonLoops<Impl>::FieldStrength(Ey, _Umu, Tdir, Ydir); WilsonLoops<Impl>::FieldStrength(Ez, _Umu, Tdir, Zdir); + double t3 = usecond(); // Compute the Clover Operator acting on Colour and Spin // multiply here by the clover coefficients for the anisotropy - CloverTerm = fillCloverYZ(Bx) * csw_r; - CloverTerm += fillCloverXZ(By) * csw_r; - CloverTerm += fillCloverXY(Bz) * csw_r; - CloverTerm += fillCloverXT(Ex) * csw_t; - CloverTerm += fillCloverYT(Ey) * csw_t; - CloverTerm += fillCloverZT(Ez) * csw_t; + CloverTerm = Helpers::fillCloverYZ(Bx) * csw_r; + CloverTerm += Helpers::fillCloverXZ(By) * csw_r; + CloverTerm += Helpers::fillCloverXY(Bz) * csw_r; + CloverTerm += Helpers::fillCloverXT(Ex) * csw_t; + CloverTerm += Helpers::fillCloverYT(Ey) * csw_t; + CloverTerm += Helpers::fillCloverZT(Ez) * csw_t; CloverTerm += diag_mass; + double t4 = usecond(); int lvol = _Umu.Grid()->lSites(); int DimRep = Impl::Dimension; + double t5 = usecond(); { autoView(CTv,CloverTerm,CpuRead); autoView(CTIv,CloverTermInv,CpuWrite); @@ -100,7 +146,7 @@ void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) grid->LocalIndexToLocalCoor(site, lcoor); Eigen::MatrixXcd EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); - typename SiteCloverType::scalar_object Qx = Zero(), Qxinv = Zero(); + typename SiteClover::scalar_object Qx = Zero(), Qxinv = Zero(); peekLocalSite(Qx, CTv, lcoor); //if (csw!=0){ for (int j = 0; j < Ns; j++) @@ -125,6 +171,7 @@ void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) }); } + double t6 = usecond(); // Separate the even and odd parts pickCheckerboard(Even, CloverTermEven, CloverTerm); pickCheckerboard(Odd, CloverTermOdd, CloverTerm); @@ -137,6 +184,20 @@ void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) pickCheckerboard(Even, CloverTermInvDagEven, adj(CloverTermInv)); pickCheckerboard(Odd, CloverTermInvDagOdd, adj(CloverTermInv)); + double t7 = usecond(); + +#if 0 + std::cout << GridLogMessage << "WilsonCloverFermion::ImportGauge timings:" + << " WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 + << ", allocations = " << (t2 - t1) / 1e6 + << ", field strength = " << (t3 - t2) / 1e6 + << ", fill clover = " << (t4 - t3) / 1e6 + << ", misc = " << (t5 - t4) / 1e6 + << ", inversions = " << (t6 - t5) / 1e6 + << ", pick cbs = " << (t7 - t6) / 1e6 + << ", total = " << (t7 - t0) / 1e6 + << std::endl; +#endif } template <class Impl> @@ -167,7 +228,7 @@ template <class Impl> void WilsonCloverFermion<Impl>::MooeeInternal(const FermionField &in, FermionField &out, int dag, int inv) { out.Checkerboard() = in.Checkerboard(); - CloverFieldType *Clover; + CloverField *Clover; assert(in.Checkerboard() == Odd || in.Checkerboard() == Even); if (dag) @@ -214,9 +275,89 @@ void WilsonCloverFermion<Impl>::MooeeInternal(const FermionField &in, FermionFie out = *Clover * in; } } - } // MooeeInternal +// Derivative parts unpreconditioned pseudofermions +template <class Impl> +void WilsonCloverFermion<Impl>::MDeriv(GaugeField &force, const FermionField &X, const FermionField &Y, int dag) +{ + conformable(X.Grid(), Y.Grid()); + conformable(X.Grid(), force.Grid()); + GaugeLinkField force_mu(force.Grid()), lambda(force.Grid()); + GaugeField clover_force(force.Grid()); + PropagatorField Lambda(force.Grid()); + + // Guido: Here we are hitting some performance issues: + // need to extract the components of the DoubledGaugeField + // for each call + // Possible solution + // Create a vector object to store them? (cons: wasting space) + std::vector<GaugeLinkField> U(Nd, this->Umu.Grid()); + + Impl::extractLinkField(U, this->Umu); + + force = Zero(); + // Derivative of the Wilson hopping term + this->DhopDeriv(force, X, Y, dag); + + /////////////////////////////////////////////////////////// + // Clover term derivative + /////////////////////////////////////////////////////////// + Impl::outerProductImpl(Lambda, X, Y); + //std::cout << "Lambda:" << Lambda << std::endl; + + Gamma::Algebra sigma[] = { + Gamma::Algebra::SigmaXY, + Gamma::Algebra::SigmaXZ, + Gamma::Algebra::SigmaXT, + Gamma::Algebra::MinusSigmaXY, + Gamma::Algebra::SigmaYZ, + Gamma::Algebra::SigmaYT, + Gamma::Algebra::MinusSigmaXZ, + Gamma::Algebra::MinusSigmaYZ, + Gamma::Algebra::SigmaZT, + Gamma::Algebra::MinusSigmaXT, + Gamma::Algebra::MinusSigmaYT, + Gamma::Algebra::MinusSigmaZT}; + + /* + sigma_{\mu \nu}= + | 0 sigma[0] sigma[1] sigma[2] | + | sigma[3] 0 sigma[4] sigma[5] | + | sigma[6] sigma[7] 0 sigma[8] | + | sigma[9] sigma[10] sigma[11] 0 | + */ + + int count = 0; + clover_force = Zero(); + for (int mu = 0; mu < 4; mu++) + { + force_mu = Zero(); + for (int nu = 0; nu < 4; nu++) + { + if (mu == nu) + continue; + + RealD factor; + if (nu == 4 || mu == 4) + { + factor = 2.0 * csw_t; + } + else + { + factor = 2.0 * csw_r; + } + PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked + Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok + force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked + count++; + } + + pokeLorentz(clover_force, U[mu] * force_mu, mu); + } + //clover_force *= csw; + force += clover_force; +} // Derivative parts template <class Impl> From 0b6fd20c5472f25c272a51a8e023b39cb85d0faf Mon Sep 17 00:00:00 2001 From: Daniel Richtmann <daniel.richtmann@gmail.com> Date: Tue, 1 Feb 2022 21:19:50 +0100 Subject: [PATCH 278/399] Enable memory coalescing in clover term generation --- Grid/qcd/action/fermion/WilsonCloverHelpers.h | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonCloverHelpers.h b/Grid/qcd/action/fermion/WilsonCloverHelpers.h index 80f9032c..daf57f78 100644 --- a/Grid/qcd/action/fermion/WilsonCloverHelpers.h +++ b/Grid/qcd/action/fermion/WilsonCloverHelpers.h @@ -85,12 +85,12 @@ public: T = Zero(); autoView(T_v,T,AcceleratorWrite); autoView(F_v,F,AcceleratorRead); - accelerator_for(i, T.Grid()->oSites(),1, + accelerator_for(i, T.Grid()->oSites(),CloverField::vector_type::Nsimd(), { - T_v[i]()(0, 1) = timesMinusI(F_v[i]()()); - T_v[i]()(1, 0) = timesMinusI(F_v[i]()()); - T_v[i]()(2, 3) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 2) = timesMinusI(F_v[i]()()); + coalescedWrite(T_v[i]()(0, 1), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(1, 0), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(2, 3), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(3, 2), coalescedRead(timesMinusI(F_v[i]()()))); }); return T; @@ -103,12 +103,12 @@ public: autoView(T_v, T,AcceleratorWrite); autoView(F_v, F,AcceleratorRead); - accelerator_for(i, T.Grid()->oSites(),1, + accelerator_for(i, T.Grid()->oSites(),CloverField::vector_type::Nsimd(), { - T_v[i]()(0, 1) = -F_v[i]()(); - T_v[i]()(1, 0) = F_v[i]()(); - T_v[i]()(2, 3) = -F_v[i]()(); - T_v[i]()(3, 2) = F_v[i]()(); + coalescedWrite(T_v[i]()(0, 1), coalescedRead(-F_v[i]()())); + coalescedWrite(T_v[i]()(1, 0), coalescedRead(F_v[i]()())); + coalescedWrite(T_v[i]()(2, 3), coalescedRead(-F_v[i]()())); + coalescedWrite(T_v[i]()(3, 2), coalescedRead(F_v[i]()())); }); return T; @@ -121,12 +121,12 @@ public: autoView(T_v,T,AcceleratorWrite); autoView(F_v,F,AcceleratorRead); - accelerator_for(i, T.Grid()->oSites(),1, + accelerator_for(i, T.Grid()->oSites(),CloverField::vector_type::Nsimd(), { - T_v[i]()(0, 0) = timesMinusI(F_v[i]()()); - T_v[i]()(1, 1) = timesI(F_v[i]()()); - T_v[i]()(2, 2) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 3) = timesI(F_v[i]()()); + coalescedWrite(T_v[i]()(0, 0), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(1, 1), coalescedRead(timesI(F_v[i]()()))); + coalescedWrite(T_v[i]()(2, 2), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(3, 3), coalescedRead(timesI(F_v[i]()()))); }); return T; @@ -139,12 +139,12 @@ public: autoView( T_v , T, AcceleratorWrite); autoView( F_v , F, AcceleratorRead); - accelerator_for(i, T.Grid()->oSites(),1, + accelerator_for(i, T.Grid()->oSites(),CloverField::vector_type::Nsimd(), { - T_v[i]()(0, 1) = timesI(F_v[i]()()); - T_v[i]()(1, 0) = timesI(F_v[i]()()); - T_v[i]()(2, 3) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 2) = timesMinusI(F_v[i]()()); + coalescedWrite(T_v[i]()(0, 1), coalescedRead(timesI(F_v[i]()()))); + coalescedWrite(T_v[i]()(1, 0), coalescedRead(timesI(F_v[i]()()))); + coalescedWrite(T_v[i]()(2, 3), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(3, 2), coalescedRead(timesMinusI(F_v[i]()()))); }); return T; @@ -157,12 +157,12 @@ public: autoView( T_v ,T,AcceleratorWrite); autoView( F_v ,F,AcceleratorRead); - accelerator_for(i, T.Grid()->oSites(),1, + accelerator_for(i, T.Grid()->oSites(),CloverField::vector_type::Nsimd(), { - T_v[i]()(0, 1) = -(F_v[i]()()); - T_v[i]()(1, 0) = (F_v[i]()()); - T_v[i]()(2, 3) = (F_v[i]()()); - T_v[i]()(3, 2) = -(F_v[i]()()); + coalescedWrite(T_v[i]()(0, 1), coalescedRead(-(F_v[i]()()))); + coalescedWrite(T_v[i]()(1, 0), coalescedRead((F_v[i]()()))); + coalescedWrite(T_v[i]()(2, 3), coalescedRead((F_v[i]()()))); + coalescedWrite(T_v[i]()(3, 2), coalescedRead(-(F_v[i]()()))); }); return T; @@ -176,12 +176,12 @@ public: autoView( T_v , T,AcceleratorWrite); autoView( F_v , F,AcceleratorRead); - accelerator_for(i, T.Grid()->oSites(),1, + accelerator_for(i, T.Grid()->oSites(),CloverField::vector_type::Nsimd(), { - T_v[i]()(0, 0) = timesI(F_v[i]()()); - T_v[i]()(1, 1) = timesMinusI(F_v[i]()()); - T_v[i]()(2, 2) = timesMinusI(F_v[i]()()); - T_v[i]()(3, 3) = timesI(F_v[i]()()); + coalescedWrite(T_v[i]()(0, 0), coalescedRead(timesI(F_v[i]()()))); + coalescedWrite(T_v[i]()(1, 1), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(2, 2), coalescedRead(timesMinusI(F_v[i]()()))); + coalescedWrite(T_v[i]()(3, 3), coalescedRead(timesI(F_v[i]()()))); }); return T; From add86cd7f4070958ae993bd8dff0c6d91c9744e8 Mon Sep 17 00:00:00 2001 From: Daniel Richtmann <daniel.richtmann@gmail.com> Date: Tue, 1 Feb 2022 21:22:45 +0100 Subject: [PATCH 279/399] Abandon ET for clover application, use construct similar to multLink --- Grid/qcd/action/fermion/WilsonCloverHelpers.h | 20 +++++++++++++++++++ .../WilsonCloverFermionImplementation.h | 8 ++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonCloverHelpers.h b/Grid/qcd/action/fermion/WilsonCloverHelpers.h index daf57f78..337af647 100644 --- a/Grid/qcd/action/fermion/WilsonCloverHelpers.h +++ b/Grid/qcd/action/fermion/WilsonCloverHelpers.h @@ -186,6 +186,26 @@ public: return T; } + + template<class _Spinor> + static accelerator_inline void multClover(_Spinor& phi, const SiteClover& C, const _Spinor& chi) { + auto CC = coalescedRead(C); + mult(&phi, &CC, &chi); + } + + template<class _SpinorField> + inline void multCloverField(_SpinorField& out, const CloverField& C, const _SpinorField& phi) { + const int Nsimd = SiteSpinor::Nsimd(); + autoView(out_v, out, AcceleratorWrite); + autoView(phi_v, phi, AcceleratorRead); + autoView(C_v, C, AcceleratorRead); + typedef decltype(coalescedRead(out_v[0])) calcSpinor; + accelerator_for(sss,out.Grid()->oSites(),Nsimd,{ + calcSpinor tmp; + multClover(tmp,C_v[sss],phi_v(sss)); + coalescedWrite(out_v[sss],tmp); + }); + } }; NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h index fd81af11..e4d2e736 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h @@ -243,12 +243,12 @@ void WilsonCloverFermion<Impl>::MooeeInternal(const FermionField &in, FermionFie { Clover = (inv) ? &CloverTermInvDagEven : &CloverTermDagEven; } - out = *Clover * in; + Helpers::multCloverField(out, *Clover, in); } else { Clover = (inv) ? &CloverTermInv : &CloverTerm; - out = adj(*Clover) * in; + Helpers::multCloverField(out, *Clover, in); // don't bother with adj, hermitian anyway } } else @@ -266,13 +266,13 @@ void WilsonCloverFermion<Impl>::MooeeInternal(const FermionField &in, FermionFie // std::cout << "Calling clover term Even" << std::endl; Clover = (inv) ? &CloverTermInvEven : &CloverTermEven; } - out = *Clover * in; + Helpers::multCloverField(out, *Clover, in); // std::cout << GridLogMessage << "*Clover.Checkerboard() " << (*Clover).Checkerboard() << std::endl; } else { Clover = (inv) ? &CloverTermInv : &CloverTerm; - out = *Clover * in; + Helpers::multCloverField(out, *Clover, in); } } } // MooeeInternal From 3082ab82524dfc3ff0d83e0df34e3d6f930d3f1b Mon Sep 17 00:00:00 2001 From: Daniel Richtmann <daniel.richtmann@gmail.com> Date: Tue, 1 Feb 2022 21:39:52 +0100 Subject: [PATCH 280/399] Check in compact version of wilson clover fermions --- .../fermion/CompactWilsonCloverFermion.h | 494 ++++++++++++++++ Grid/qcd/action/fermion/WilsonCloverHelpers.h | 550 ++++++++++++++++++ Grid/qcd/action/fermion/WilsonCloverTypes.h | 43 ++ .../Test_compact_wilson_clover_speedup.cc | 226 +++++++ 4 files changed, 1313 insertions(+) create mode 100644 Grid/qcd/action/fermion/CompactWilsonCloverFermion.h create mode 100644 tests/core/Test_compact_wilson_clover_speedup.cc diff --git a/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h new file mode 100644 index 00000000..550bef85 --- /dev/null +++ b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h @@ -0,0 +1,494 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/qcd/action/fermion/CompactWilsonCloverFermion.h + + Copyright (C) 2020 - 2022 + + Author: Daniel Richtmann <daniel.richtmann@gmail.com> + Author: Nils Meyer <nils.meyer@ur.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + +#pragma once + +#include <Grid/qcd/action/fermion/WilsonCloverTypes.h> +#include <Grid/qcd/action/fermion/WilsonCloverHelpers.h> + +NAMESPACE_BEGIN(Grid); + +// see Grid/qcd/action/fermion/WilsonCloverFermion.h for description +// +// Modifications done here: +// +// Original: clover term = 12x12 matrix per site +// +// But: Only two diagonal 6x6 hermitian blocks are non-zero (also true for original, verified by running) +// Sufficient to store/transfer only the real parts of the diagonal and one triangular part +// 2 * (6 + 15 * 2) = 72 real or 36 complex words to be stored/transfered +// +// Here: Above but diagonal as complex numbers, i.e., need to store/transfer +// 2 * (6 * 2 + 15 * 2) = 84 real or 42 complex words +// +// Words per site and improvement compared to original (combined with the input and output spinors): +// +// - Original: 2*12 + 12*12 = 168 words -> 1.00 x less +// - Minimal: 2*12 + 36 = 60 words -> 2.80 x less +// - Here: 2*12 + 42 = 66 words -> 2.55 x less +// +// These improvements directly translate to wall-clock time +// +// Data layout: +// +// - diagonal and triangle part as separate lattice fields, +// this was faster than as 1 combined field on all tested machines +// - diagonal: as expected +// - triangle: store upper right triangle in row major order +// - graphical: +// 0 1 2 3 4 +// 5 6 7 8 +// 9 10 11 = upper right triangle indices +// 12 13 +// 14 +// 0 +// 1 +// 2 +// 3 = diagonal indices +// 4 +// 5 +// 0 +// 1 5 +// 2 6 9 = lower left triangle indices +// 3 7 10 12 +// 4 8 11 13 14 +// +// Impact on total memory consumption: +// - Original: (2 * 1 + 8 * 1/2) 12x12 matrices = 6 12x12 matrices = 864 complex words per site +// - Here: (2 * 1 + 4 * 1/2) diagonal parts = 4 diagonal parts = 24 complex words per site +// + (2 * 1 + 4 * 1/2) triangle parts = 4 triangle parts = 60 complex words per site +// = 84 complex words per site + +template<class Impl> +class CompactWilsonCloverFermion : public WilsonFermion<Impl>, + public WilsonCloverHelpers<Impl>, + public CompactWilsonCloverHelpers<Impl> { + ///////////////////////////////////////////// + // Sizes + ///////////////////////////////////////////// + +public: + + INHERIT_COMPACT_CLOVER_SIZES(Impl); + + ///////////////////////////////////////////// + // Type definitions + ///////////////////////////////////////////// + +public: + + INHERIT_IMPL_TYPES(Impl); + INHERIT_CLOVER_TYPES(Impl); + INHERIT_COMPACT_CLOVER_TYPES(Impl); + + typedef WilsonFermion<Impl> WilsonBase; + typedef WilsonCloverHelpers<Impl> Helpers; + typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; + + ///////////////////////////////////////////// + // Constructors + ///////////////////////////////////////////// + +public: + + CompactWilsonCloverFermion(GaugeField& _Umu, + GridCartesian& Fgrid, + GridRedBlackCartesian& Hgrid, + const RealD _mass, + const RealD _csw_r = 0.0, + const RealD _csw_t = 0.0, + const RealD _cF = 1.0, + const WilsonAnisotropyCoefficients& clover_anisotropy = WilsonAnisotropyCoefficients(), + const ImplParams& impl_p = ImplParams()) + : WilsonBase(_Umu, Fgrid, Hgrid, _mass, impl_p, clover_anisotropy) + , csw_r(_csw_r) + , csw_t(_csw_t) + , cF(_cF) + , open_boundaries(impl_p.boundary_phases[Nd-1] == 0.0) + , Diagonal(&Fgrid), Triangle(&Fgrid) + , DiagonalEven(&Hgrid), TriangleEven(&Hgrid) + , DiagonalOdd(&Hgrid), TriangleOdd(&Hgrid) + , DiagonalInv(&Fgrid), TriangleInv(&Fgrid) + , DiagonalInvEven(&Hgrid), TriangleInvEven(&Hgrid) + , DiagonalInvOdd(&Hgrid), TriangleInvOdd(&Hgrid) + , Tmp(&Fgrid) + , BoundaryMask(&Fgrid) + , BoundaryMaskEven(&Hgrid), BoundaryMaskOdd(&Hgrid) + { + csw_r *= 0.5; + csw_t *= 0.5; + if (clover_anisotropy.isAnisotropic) + csw_r /= clover_anisotropy.xi_0; + + ImportGauge(_Umu); + if (open_boundaries) + CompactHelpers::SetupMasks(this->BoundaryMask, this->BoundaryMaskEven, this->BoundaryMaskOdd); + } + + ///////////////////////////////////////////// + // Member functions (implementing interface) + ///////////////////////////////////////////// + +public: + + virtual void Instantiatable() {}; + int ConstEE() override { return 0; }; + int isTrivialEE() override { return 0; }; + + + void Dhop(const FermionField& in, FermionField& out, int dag) override { + WilsonBase::Dhop(in, out, dag); + if(open_boundaries) ApplyBoundaryMask(out); + } + + void DhopOE(const FermionField& in, FermionField& out, int dag) override { + WilsonBase::DhopOE(in, out, dag); + if(open_boundaries) ApplyBoundaryMask(out); + } + + void DhopEO(const FermionField& in, FermionField& out, int dag) override { + WilsonBase::DhopEO(in, out, dag); + if(open_boundaries) ApplyBoundaryMask(out); + } + + void DhopDir(const FermionField& in, FermionField& out, int dir, int disp) override { + WilsonBase::DhopDir(in, out, dir, disp); + if(this->open_boundaries) ApplyBoundaryMask(out); + } + + void DhopDirAll(const FermionField& in, std::vector<FermionField>& out) /* override */ { + WilsonBase::DhopDirAll(in, out); + if(this->open_boundaries) { + for(auto& o : out) ApplyBoundaryMask(o); + } + } + + void M(const FermionField& in, FermionField& out) override { + out.Checkerboard() = in.Checkerboard(); + WilsonBase::Dhop(in, out, DaggerNo); // call base to save applying bc + Mooee(in, Tmp); + axpy(out, 1.0, out, Tmp); + if(open_boundaries) ApplyBoundaryMask(out); + } + + void Mdag(const FermionField& in, FermionField& out) override { + out.Checkerboard() = in.Checkerboard(); + WilsonBase::Dhop(in, out, DaggerYes); // call base to save applying bc + MooeeDag(in, Tmp); + axpy(out, 1.0, out, Tmp); + if(open_boundaries) ApplyBoundaryMask(out); + } + + void Meooe(const FermionField& in, FermionField& out) override { + WilsonBase::Meooe(in, out); + if(open_boundaries) ApplyBoundaryMask(out); + } + + void MeooeDag(const FermionField& in, FermionField& out) override { + WilsonBase::MeooeDag(in, out); + if(open_boundaries) ApplyBoundaryMask(out); + } + + void Mooee(const FermionField& in, FermionField& out) override { + if(in.Grid()->_isCheckerBoarded) { + if(in.Checkerboard() == Odd) { + MooeeInternal(in, out, DiagonalOdd, TriangleOdd); + } else { + MooeeInternal(in, out, DiagonalEven, TriangleEven); + } + } else { + MooeeInternal(in, out, Diagonal, Triangle); + } + if(open_boundaries) ApplyBoundaryMask(out); + } + + void MooeeDag(const FermionField& in, FermionField& out) override { + Mooee(in, out); // blocks are hermitian + } + + void MooeeInv(const FermionField& in, FermionField& out) override { + if(in.Grid()->_isCheckerBoarded) { + if(in.Checkerboard() == Odd) { + MooeeInternal(in, out, DiagonalInvOdd, TriangleInvOdd); + } else { + MooeeInternal(in, out, DiagonalInvEven, TriangleInvEven); + } + } else { + MooeeInternal(in, out, DiagonalInv, TriangleInv); + } + if(open_boundaries) ApplyBoundaryMask(out); + } + + void MooeeInvDag(const FermionField& in, FermionField& out) override { + MooeeInv(in, out); // blocks are hermitian + } + + void Mdir(const FermionField& in, FermionField& out, int dir, int disp) override { + DhopDir(in, out, dir, disp); + } + + void MdirAll(const FermionField& in, std::vector<FermionField>& out) override { + DhopDirAll(in, out); + } + + void MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) override { + assert(!open_boundaries); // TODO check for changes required for open bc + + // NOTE: code copied from original clover term + conformable(X.Grid(), Y.Grid()); + conformable(X.Grid(), force.Grid()); + GaugeLinkField force_mu(force.Grid()), lambda(force.Grid()); + GaugeField clover_force(force.Grid()); + PropagatorField Lambda(force.Grid()); + + // Guido: Here we are hitting some performance issues: + // need to extract the components of the DoubledGaugeField + // for each call + // Possible solution + // Create a vector object to store them? (cons: wasting space) + std::vector<GaugeLinkField> U(Nd, this->Umu.Grid()); + + Impl::extractLinkField(U, this->Umu); + + force = Zero(); + // Derivative of the Wilson hopping term + this->DhopDeriv(force, X, Y, dag); + + /////////////////////////////////////////////////////////// + // Clover term derivative + /////////////////////////////////////////////////////////// + Impl::outerProductImpl(Lambda, X, Y); + //std::cout << "Lambda:" << Lambda << std::endl; + + Gamma::Algebra sigma[] = { + Gamma::Algebra::SigmaXY, + Gamma::Algebra::SigmaXZ, + Gamma::Algebra::SigmaXT, + Gamma::Algebra::MinusSigmaXY, + Gamma::Algebra::SigmaYZ, + Gamma::Algebra::SigmaYT, + Gamma::Algebra::MinusSigmaXZ, + Gamma::Algebra::MinusSigmaYZ, + Gamma::Algebra::SigmaZT, + Gamma::Algebra::MinusSigmaXT, + Gamma::Algebra::MinusSigmaYT, + Gamma::Algebra::MinusSigmaZT}; + + /* + sigma_{\mu \nu}= + | 0 sigma[0] sigma[1] sigma[2] | + | sigma[3] 0 sigma[4] sigma[5] | + | sigma[6] sigma[7] 0 sigma[8] | + | sigma[9] sigma[10] sigma[11] 0 | + */ + + int count = 0; + clover_force = Zero(); + for (int mu = 0; mu < 4; mu++) + { + force_mu = Zero(); + for (int nu = 0; nu < 4; nu++) + { + if (mu == nu) + continue; + + RealD factor; + if (nu == 4 || mu == 4) + { + factor = 2.0 * csw_t; + } + else + { + factor = 2.0 * csw_r; + } + PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked + Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok + force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked + count++; + } + + pokeLorentz(clover_force, U[mu] * force_mu, mu); + } + //clover_force *= csw; + force += clover_force; + } + + void MooDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override { + assert(0); + } + + void MeeDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override { + assert(0); + } + + ///////////////////////////////////////////// + // Member functions (internals) + ///////////////////////////////////////////// + + void MooeeInternal(const FermionField& in, + FermionField& out, + const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle) { + assert(in.Checkerboard() == Odd || in.Checkerboard() == Even); + out.Checkerboard() = in.Checkerboard(); + conformable(in, out); + conformable(in, diagonal); + conformable(in, triangle); + + CompactHelpers::MooeeKernel(diagonal.oSites(), 1, in, out, diagonal, triangle); + } + + ///////////////////////////////////////////// + // Helpers + ///////////////////////////////////////////// + + void ImportGauge(const GaugeField& _Umu) override { + // NOTE: parts copied from original implementation + + // Import gauge into base class + double t0 = usecond(); + WilsonBase::ImportGauge(_Umu); // NOTE: called here and in wilson constructor -> performed twice, but can't avoid that + + // Initialize temporary variables + double t1 = usecond(); + conformable(_Umu.Grid(), this->GaugeGrid()); + GridBase* grid = _Umu.Grid(); + typename Impl::GaugeLinkField Bx(grid), By(grid), Bz(grid), Ex(grid), Ey(grid), Ez(grid); + CloverField TmpOriginal(grid); + + // Compute the field strength terms mu>nu + double t2 = usecond(); + WilsonLoops<Impl>::FieldStrength(Bx, _Umu, Zdir, Ydir); + WilsonLoops<Impl>::FieldStrength(By, _Umu, Zdir, Xdir); + WilsonLoops<Impl>::FieldStrength(Bz, _Umu, Ydir, Xdir); + WilsonLoops<Impl>::FieldStrength(Ex, _Umu, Tdir, Xdir); + WilsonLoops<Impl>::FieldStrength(Ey, _Umu, Tdir, Ydir); + WilsonLoops<Impl>::FieldStrength(Ez, _Umu, Tdir, Zdir); + + // Compute the Clover Operator acting on Colour and Spin + // multiply here by the clover coefficients for the anisotropy + double t3 = usecond(); + TmpOriginal = Helpers::fillCloverYZ(Bx) * csw_r; + TmpOriginal += Helpers::fillCloverXZ(By) * csw_r; + TmpOriginal += Helpers::fillCloverXY(Bz) * csw_r; + TmpOriginal += Helpers::fillCloverXT(Ex) * csw_t; + TmpOriginal += Helpers::fillCloverYT(Ey) * csw_t; + TmpOriginal += Helpers::fillCloverZT(Ez) * csw_t; + TmpOriginal += this->diag_mass; + + // Convert the data layout of the clover term + double t4 = usecond(); + CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); + + // Possible modify the boundary values + double t5 = usecond(); + if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); + + // Invert the clover term in the improved layout + double t6 = usecond(); + CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); + + // Fill the remaining clover fields + double t7 = usecond(); + pickCheckerboard(Even, DiagonalEven, Diagonal); + pickCheckerboard(Even, TriangleEven, Triangle); + pickCheckerboard(Odd, DiagonalOdd, Diagonal); + pickCheckerboard(Odd, TriangleOdd, Triangle); + pickCheckerboard(Even, DiagonalInvEven, DiagonalInv); + pickCheckerboard(Even, TriangleInvEven, TriangleInv); + pickCheckerboard(Odd, DiagonalInvOdd, DiagonalInv); + pickCheckerboard(Odd, TriangleInvOdd, TriangleInv); + + // Report timings + double t8 = usecond(); +#if 0 + std::cout << GridLogMessage << "CompactWilsonCloverFermion::ImportGauge timings:" + << " WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 + << ", allocations = " << (t2 - t1) / 1e6 + << ", field strength = " << (t3 - t2) / 1e6 + << ", fill clover = " << (t4 - t3) / 1e6 + << ", convert = " << (t5 - t4) / 1e6 + << ", boundaries = " << (t6 - t5) / 1e6 + << ", inversions = " << (t7 - t6) / 1e6 + << ", pick cbs = " << (t8 - t7) / 1e6 + << ", total = " << (t8 - t0) / 1e6 + << std::endl; +#endif + } + + ///////////////////////////////////////////// + // Helpers + ///////////////////////////////////////////// + +private: + + template<class Field> + const MaskField* getCorrectMaskField(const Field &in) const { + if(in.Grid()->_isCheckerBoarded) { + if(in.Checkerboard() == Odd) { + return &this->BoundaryMaskOdd; + } else { + return &this->BoundaryMaskEven; + } + } else { + return &this->BoundaryMask; + } + } + + template<class Field> + void ApplyBoundaryMask(Field& f) { + const MaskField* m = getCorrectMaskField(f); assert(m != nullptr); + assert(m != nullptr); + CompactHelpers::ApplyBoundaryMask(f, *m); + } + + ///////////////////////////////////////////// + // Member Data + ///////////////////////////////////////////// + +public: + + RealD csw_r; + RealD csw_t; + RealD cF; + + bool open_boundaries; + + CloverDiagonalField Diagonal, DiagonalEven, DiagonalOdd; + CloverDiagonalField DiagonalInv, DiagonalInvEven, DiagonalInvOdd; + + CloverTriangleField Triangle, TriangleEven, TriangleOdd; + CloverTriangleField TriangleInv, TriangleInvEven, TriangleInvOdd; + + FermionField Tmp; + + MaskField BoundaryMask, BoundaryMaskEven, BoundaryMaskOdd; +}; + +NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/WilsonCloverHelpers.h b/Grid/qcd/action/fermion/WilsonCloverHelpers.h index 337af647..588525cc 100644 --- a/Grid/qcd/action/fermion/WilsonCloverHelpers.h +++ b/Grid/qcd/action/fermion/WilsonCloverHelpers.h @@ -208,4 +208,554 @@ public: } }; + +template<class Impl> class CompactWilsonCloverHelpers { +public: + + INHERIT_COMPACT_CLOVER_SIZES(Impl); + + INHERIT_IMPL_TYPES(Impl); + INHERIT_CLOVER_TYPES(Impl); + INHERIT_COMPACT_CLOVER_TYPES(Impl); + + #if 0 + static accelerator_inline typename SiteCloverTriangle::vector_type triangle_elem(const SiteCloverTriangle& triangle, int block, int i, int j) { + assert(i != j); + if(i < j) { + return triangle()(block)(triangle_index(i, j)); + } else { // i > j + return conjugate(triangle()(block)(triangle_index(i, j))); + } + } + #else + template<typename vobj> + static accelerator_inline vobj triangle_elem(const iImplCloverTriangle<vobj>& triangle, int block, int i, int j) { + assert(i != j); + if(i < j) { + return triangle()(block)(triangle_index(i, j)); + } else { // i > j + return conjugate(triangle()(block)(triangle_index(i, j))); + } + } + #endif + + static accelerator_inline int triangle_index(int i, int j) { + if(i == j) + return 0; + else if(i < j) + return Nred * (Nred - 1) / 2 - (Nred - i) * (Nred - i - 1) / 2 + j - i - 1; + else // i > j + return Nred * (Nred - 1) / 2 - (Nred - j) * (Nred - j - 1) / 2 + i - j - 1; + } + + static void MooeeKernel_gpu(int Nsite, + int Ls, + const FermionField& in, + FermionField& out, + const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle) { + autoView(diagonal_v, diagonal, AcceleratorRead); + autoView(triangle_v, triangle, AcceleratorRead); + autoView(in_v, in, AcceleratorRead); + autoView(out_v, out, AcceleratorWrite); + + typedef decltype(coalescedRead(out_v[0])) CalcSpinor; + + const uint64_t NN = Nsite * Ls; + + accelerator_for(ss, NN, Simd::Nsimd(), { + int sF = ss; + int sU = ss/Ls; + CalcSpinor res; + CalcSpinor in_t = in_v(sF); + auto diagonal_t = diagonal_v(sU); + auto triangle_t = triangle_v(sU); + for(int block=0; block<Nhs; block++) { + int s_start = block*Nhs; + for(int i=0; i<Nred; i++) { + int si = s_start + i/Nc, ci = i%Nc; + res()(si)(ci) = diagonal_t()(block)(i) * in_t()(si)(ci); + for(int j=0; j<Nred; j++) { + if (j == i) continue; + int sj = s_start + j/Nc, cj = j%Nc; + res()(si)(ci) = res()(si)(ci) + triangle_elem(triangle_t, block, i, j) * in_t()(sj)(cj); + }; + }; + }; + coalescedWrite(out_v[sF], res); + }); + } + + static void MooeeKernel_cpu(int Nsite, + int Ls, + const FermionField& in, + FermionField& out, + const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle) { + autoView(diagonal_v, diagonal, CpuRead); + autoView(triangle_v, triangle, CpuRead); + autoView(in_v, in, CpuRead); + autoView(out_v, out, CpuWrite); + + typedef SiteSpinor CalcSpinor; + +#if defined(A64FX) || defined(A64FXFIXEDSIZE) +#define PREFETCH_CLOVER(BASE) { \ + uint64_t base; \ + int pf_dist_L1 = 1; \ + int pf_dist_L2 = -5; /* -> penalty -> disable */ \ + \ + if ((pf_dist_L1 >= 0) && (sU + pf_dist_L1 < Nsite)) { \ + base = (uint64_t)&diag_t()(pf_dist_L1+BASE)(0); \ + svprfd(svptrue_b64(), (int64_t*)(base + 0), SV_PLDL1STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 256), SV_PLDL1STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 512), SV_PLDL1STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 768), SV_PLDL1STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 1024), SV_PLDL1STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 1280), SV_PLDL1STRM); \ + } \ + \ + if ((pf_dist_L2 >= 0) && (sU + pf_dist_L2 < Nsite)) { \ + base = (uint64_t)&diag_t()(pf_dist_L2+BASE)(0); \ + svprfd(svptrue_b64(), (int64_t*)(base + 0), SV_PLDL2STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 256), SV_PLDL2STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 512), SV_PLDL2STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 768), SV_PLDL2STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 1024), SV_PLDL2STRM); \ + svprfd(svptrue_b64(), (int64_t*)(base + 1280), SV_PLDL2STRM); \ + } \ + } +// TODO: Implement/generalize this for other architectures +// I played around a bit on KNL (see below) but didn't bring anything +// #elif defined(AVX512) +// #define PREFETCH_CLOVER(BASE) { \ +// uint64_t base; \ +// int pf_dist_L1 = 1; \ +// int pf_dist_L2 = +4; \ +// \ +// if ((pf_dist_L1 >= 0) && (sU + pf_dist_L1 < Nsite)) { \ +// base = (uint64_t)&diag_t()(pf_dist_L1+BASE)(0); \ +// _mm_prefetch((const char*)(base + 0), _MM_HINT_T0); \ +// _mm_prefetch((const char*)(base + 64), _MM_HINT_T0); \ +// _mm_prefetch((const char*)(base + 128), _MM_HINT_T0); \ +// _mm_prefetch((const char*)(base + 192), _MM_HINT_T0); \ +// _mm_prefetch((const char*)(base + 256), _MM_HINT_T0); \ +// _mm_prefetch((const char*)(base + 320), _MM_HINT_T0); \ +// } \ +// \ +// if ((pf_dist_L2 >= 0) && (sU + pf_dist_L2 < Nsite)) { \ +// base = (uint64_t)&diag_t()(pf_dist_L2+BASE)(0); \ +// _mm_prefetch((const char*)(base + 0), _MM_HINT_T1); \ +// _mm_prefetch((const char*)(base + 64), _MM_HINT_T1); \ +// _mm_prefetch((const char*)(base + 128), _MM_HINT_T1); \ +// _mm_prefetch((const char*)(base + 192), _MM_HINT_T1); \ +// _mm_prefetch((const char*)(base + 256), _MM_HINT_T1); \ +// _mm_prefetch((const char*)(base + 320), _MM_HINT_T1); \ +// } \ +// } +#else +#define PREFETCH_CLOVER(BASE) +#endif + + const uint64_t NN = Nsite * Ls; + + thread_for(ss, NN, { + int sF = ss; + int sU = ss/Ls; + CalcSpinor res; + CalcSpinor in_t = in_v[sF]; + auto diag_t = diagonal_v[sU]; // "diag" instead of "diagonal" here to make code below easier to read + auto triangle_t = triangle_v[sU]; + + // upper half + PREFETCH_CLOVER(0); + + auto in_cc_0_0 = conjugate(in_t()(0)(0)); // Nils: reduces number + auto in_cc_0_1 = conjugate(in_t()(0)(1)); // of conjugates from + auto in_cc_0_2 = conjugate(in_t()(0)(2)); // 30 to 20 + auto in_cc_1_0 = conjugate(in_t()(1)(0)); + auto in_cc_1_1 = conjugate(in_t()(1)(1)); + + res()(0)(0) = diag_t()(0)( 0) * in_t()(0)(0) + + triangle_t()(0)( 0) * in_t()(0)(1) + + triangle_t()(0)( 1) * in_t()(0)(2) + + triangle_t()(0)( 2) * in_t()(1)(0) + + triangle_t()(0)( 3) * in_t()(1)(1) + + triangle_t()(0)( 4) * in_t()(1)(2); + + res()(0)(1) = triangle_t()(0)( 0) * in_cc_0_0; + res()(0)(1) = diag_t()(0)( 1) * in_t()(0)(1) + + triangle_t()(0)( 5) * in_t()(0)(2) + + triangle_t()(0)( 6) * in_t()(1)(0) + + triangle_t()(0)( 7) * in_t()(1)(1) + + triangle_t()(0)( 8) * in_t()(1)(2) + + conjugate( res()(0)( 1)); + + res()(0)(2) = triangle_t()(0)( 1) * in_cc_0_0 + + triangle_t()(0)( 5) * in_cc_0_1; + res()(0)(2) = diag_t()(0)( 2) * in_t()(0)(2) + + triangle_t()(0)( 9) * in_t()(1)(0) + + triangle_t()(0)(10) * in_t()(1)(1) + + triangle_t()(0)(11) * in_t()(1)(2) + + conjugate( res()(0)( 2)); + + res()(1)(0) = triangle_t()(0)( 2) * in_cc_0_0 + + triangle_t()(0)( 6) * in_cc_0_1 + + triangle_t()(0)( 9) * in_cc_0_2; + res()(1)(0) = diag_t()(0)( 3) * in_t()(1)(0) + + triangle_t()(0)(12) * in_t()(1)(1) + + triangle_t()(0)(13) * in_t()(1)(2) + + conjugate( res()(1)( 0)); + + res()(1)(1) = triangle_t()(0)( 3) * in_cc_0_0 + + triangle_t()(0)( 7) * in_cc_0_1 + + triangle_t()(0)(10) * in_cc_0_2 + + triangle_t()(0)(12) * in_cc_1_0; + res()(1)(1) = diag_t()(0)( 4) * in_t()(1)(1) + + triangle_t()(0)(14) * in_t()(1)(2) + + conjugate( res()(1)( 1)); + + res()(1)(2) = triangle_t()(0)( 4) * in_cc_0_0 + + triangle_t()(0)( 8) * in_cc_0_1 + + triangle_t()(0)(11) * in_cc_0_2 + + triangle_t()(0)(13) * in_cc_1_0 + + triangle_t()(0)(14) * in_cc_1_1; + res()(1)(2) = diag_t()(0)( 5) * in_t()(1)(2) + + conjugate( res()(1)( 2)); + + vstream(out_v[sF]()(0)(0), res()(0)(0)); + vstream(out_v[sF]()(0)(1), res()(0)(1)); + vstream(out_v[sF]()(0)(2), res()(0)(2)); + vstream(out_v[sF]()(1)(0), res()(1)(0)); + vstream(out_v[sF]()(1)(1), res()(1)(1)); + vstream(out_v[sF]()(1)(2), res()(1)(2)); + + // lower half + PREFETCH_CLOVER(1); + + auto in_cc_2_0 = conjugate(in_t()(2)(0)); + auto in_cc_2_1 = conjugate(in_t()(2)(1)); + auto in_cc_2_2 = conjugate(in_t()(2)(2)); + auto in_cc_3_0 = conjugate(in_t()(3)(0)); + auto in_cc_3_1 = conjugate(in_t()(3)(1)); + + res()(2)(0) = diag_t()(1)( 0) * in_t()(2)(0) + + triangle_t()(1)( 0) * in_t()(2)(1) + + triangle_t()(1)( 1) * in_t()(2)(2) + + triangle_t()(1)( 2) * in_t()(3)(0) + + triangle_t()(1)( 3) * in_t()(3)(1) + + triangle_t()(1)( 4) * in_t()(3)(2); + + res()(2)(1) = triangle_t()(1)( 0) * in_cc_2_0; + res()(2)(1) = diag_t()(1)( 1) * in_t()(2)(1) + + triangle_t()(1)( 5) * in_t()(2)(2) + + triangle_t()(1)( 6) * in_t()(3)(0) + + triangle_t()(1)( 7) * in_t()(3)(1) + + triangle_t()(1)( 8) * in_t()(3)(2) + + conjugate( res()(2)( 1)); + + res()(2)(2) = triangle_t()(1)( 1) * in_cc_2_0 + + triangle_t()(1)( 5) * in_cc_2_1; + res()(2)(2) = diag_t()(1)( 2) * in_t()(2)(2) + + triangle_t()(1)( 9) * in_t()(3)(0) + + triangle_t()(1)(10) * in_t()(3)(1) + + triangle_t()(1)(11) * in_t()(3)(2) + + conjugate( res()(2)( 2)); + + res()(3)(0) = triangle_t()(1)( 2) * in_cc_2_0 + + triangle_t()(1)( 6) * in_cc_2_1 + + triangle_t()(1)( 9) * in_cc_2_2; + res()(3)(0) = diag_t()(1)( 3) * in_t()(3)(0) + + triangle_t()(1)(12) * in_t()(3)(1) + + triangle_t()(1)(13) * in_t()(3)(2) + + conjugate( res()(3)( 0)); + + res()(3)(1) = triangle_t()(1)( 3) * in_cc_2_0 + + triangle_t()(1)( 7) * in_cc_2_1 + + triangle_t()(1)(10) * in_cc_2_2 + + triangle_t()(1)(12) * in_cc_3_0; + res()(3)(1) = diag_t()(1)( 4) * in_t()(3)(1) + + triangle_t()(1)(14) * in_t()(3)(2) + + conjugate( res()(3)( 1)); + + res()(3)(2) = triangle_t()(1)( 4) * in_cc_2_0 + + triangle_t()(1)( 8) * in_cc_2_1 + + triangle_t()(1)(11) * in_cc_2_2 + + triangle_t()(1)(13) * in_cc_3_0 + + triangle_t()(1)(14) * in_cc_3_1; + res()(3)(2) = diag_t()(1)( 5) * in_t()(3)(2) + + conjugate( res()(3)( 2)); + + vstream(out_v[sF]()(2)(0), res()(2)(0)); + vstream(out_v[sF]()(2)(1), res()(2)(1)); + vstream(out_v[sF]()(2)(2), res()(2)(2)); + vstream(out_v[sF]()(3)(0), res()(3)(0)); + vstream(out_v[sF]()(3)(1), res()(3)(1)); + vstream(out_v[sF]()(3)(2), res()(3)(2)); + }); + } + + static void MooeeKernel(int Nsite, + int Ls, + const FermionField& in, + FermionField& out, + const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle) { +#if defined(GRID_CUDA) || defined(GRID_HIP) + MooeeKernel_gpu(Nsite, Ls, in, out, diagonal, triangle); +#else + MooeeKernel_cpu(Nsite, Ls, in, out, diagonal, triangle); +#endif + } + + static void Invert(const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle, + CloverDiagonalField& diagonalInv, + CloverTriangleField& triangleInv) { + conformable(diagonal, diagonalInv); + conformable(triangle, triangleInv); + conformable(diagonal, triangle); + + diagonalInv.Checkerboard() = diagonal.Checkerboard(); + triangleInv.Checkerboard() = triangle.Checkerboard(); + + GridBase* grid = diagonal.Grid(); + + long lsites = grid->lSites(); + + typedef typename SiteCloverDiagonal::scalar_object scalar_object_diagonal; + typedef typename SiteCloverTriangle::scalar_object scalar_object_triangle; + + autoView(diagonal_v, diagonal, CpuRead); + autoView(triangle_v, triangle, CpuRead); + autoView(diagonalInv_v, diagonalInv, CpuWrite); + autoView(triangleInv_v, triangleInv, CpuWrite); + + thread_for(site, lsites, { // NOTE: Not on GPU because of Eigen & (peek/poke)LocalSite + Eigen::MatrixXcd clover_inv_eigen = Eigen::MatrixXcd::Zero(Ns*Nc, Ns*Nc); + Eigen::MatrixXcd clover_eigen = Eigen::MatrixXcd::Zero(Ns*Nc, Ns*Nc); + + scalar_object_diagonal diagonal_tmp = Zero(); + scalar_object_diagonal diagonal_inv_tmp = Zero(); + scalar_object_triangle triangle_tmp = Zero(); + scalar_object_triangle triangle_inv_tmp = Zero(); + + Coordinate lcoor; + grid->LocalIndexToLocalCoor(site, lcoor); + + peekLocalSite(diagonal_tmp, diagonal_v, lcoor); + peekLocalSite(triangle_tmp, triangle_v, lcoor); + + // TODO: can we save time here by inverting the two 6x6 hermitian matrices separately? + for (long s_row=0;s_row<Ns;s_row++) { + for (long s_col=0;s_col<Ns;s_col++) { + if(abs(s_row - s_col) > 1 || s_row + s_col == 3) continue; + int block = s_row / Nhs; + int s_row_block = s_row % Nhs; + int s_col_block = s_col % Nhs; + for (long c_row=0;c_row<Nc;c_row++) { + for (long c_col=0;c_col<Nc;c_col++) { + int i = s_row_block * Nc + c_row; + int j = s_col_block * Nc + c_col; + if(i == j) + clover_eigen(s_row*Nc+c_row, s_col*Nc+c_col) = static_cast<ComplexD>(TensorRemove(diagonal_tmp()(block)(i))); + else + clover_eigen(s_row*Nc+c_row, s_col*Nc+c_col) = static_cast<ComplexD>(TensorRemove(triangle_elem(triangle_tmp, block, i, j))); + } + } + } + } + + clover_inv_eigen = clover_eigen.inverse(); + + for (long s_row=0;s_row<Ns;s_row++) { + for (long s_col=0;s_col<Ns;s_col++) { + if(abs(s_row - s_col) > 1 || s_row + s_col == 3) continue; + int block = s_row / Nhs; + int s_row_block = s_row % Nhs; + int s_col_block = s_col % Nhs; + for (long c_row=0;c_row<Nc;c_row++) { + for (long c_col=0;c_col<Nc;c_col++) { + int i = s_row_block * Nc + c_row; + int j = s_col_block * Nc + c_col; + if(i == j) + diagonal_inv_tmp()(block)(i) = clover_inv_eigen(s_row*Nc+c_row, s_col*Nc+c_col); + else if(i < j) + triangle_inv_tmp()(block)(triangle_index(i, j)) = clover_inv_eigen(s_row*Nc+c_row, s_col*Nc+c_col); + else + continue; + } + } + } + } + + pokeLocalSite(diagonal_inv_tmp, diagonalInv_v, lcoor); + pokeLocalSite(triangle_inv_tmp, triangleInv_v, lcoor); + }); + } + + static void ConvertLayout(const CloverField& full, + CloverDiagonalField& diagonal, + CloverTriangleField& triangle) { + conformable(full, diagonal); + conformable(full, triangle); + + diagonal.Checkerboard() = full.Checkerboard(); + triangle.Checkerboard() = full.Checkerboard(); + + autoView(full_v, full, AcceleratorRead); + autoView(diagonal_v, diagonal, AcceleratorWrite); + autoView(triangle_v, triangle, AcceleratorWrite); + + // NOTE: this function cannot be 'private' since nvcc forbids this for kernels + accelerator_for(ss, full.Grid()->oSites(), 1, { + for(int s_row = 0; s_row < Ns; s_row++) { + for(int s_col = 0; s_col < Ns; s_col++) { + if(abs(s_row - s_col) > 1 || s_row + s_col == 3) continue; + int block = s_row / Nhs; + int s_row_block = s_row % Nhs; + int s_col_block = s_col % Nhs; + for(int c_row = 0; c_row < Nc; c_row++) { + for(int c_col = 0; c_col < Nc; c_col++) { + int i = s_row_block * Nc + c_row; + int j = s_col_block * Nc + c_col; + if(i == j) + diagonal_v[ss]()(block)(i) = full_v[ss]()(s_row, s_col)(c_row, c_col); + else if(i < j) + triangle_v[ss]()(block)(triangle_index(i, j)) = full_v[ss]()(s_row, s_col)(c_row, c_col); + else + continue; + } + } + } + } + }); + } + + + static void ConvertLayout(const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle, + CloverField& full) { + conformable(full, diagonal); + conformable(full, triangle); + + full.Checkerboard() = diagonal.Checkerboard(); + + full = Zero(); + + autoView(diagonal_v, diagonal, AcceleratorRead); + autoView(triangle_v, triangle, AcceleratorRead); + autoView(full_v, full, AcceleratorWrite); + + // NOTE: this function cannot be 'private' since nvcc forbids this for kernels + accelerator_for(ss, full.Grid()->oSites(), 1, { + for(int s_row = 0; s_row < Ns; s_row++) { + for(int s_col = 0; s_col < Ns; s_col++) { + if(abs(s_row - s_col) > 1 || s_row + s_col == 3) continue; + int block = s_row / Nhs; + int s_row_block = s_row % Nhs; + int s_col_block = s_col % Nhs; + for(int c_row = 0; c_row < Nc; c_row++) { + for(int c_col = 0; c_col < Nc; c_col++) { + int i = s_row_block * Nc + c_row; + int j = s_col_block * Nc + c_col; + if(i == j) + full_v[ss]()(s_row, s_col)(c_row, c_col) = diagonal_v[ss]()(block)(i); + else + full_v[ss]()(s_row, s_col)(c_row, c_col) = triangle_elem(triangle_v[ss], block, i, j); + } + } + } + } + }); + } + + static void ModifyBoundaries(CloverDiagonalField& diagonal, CloverTriangleField& triangle, RealD csw_t, RealD cF, RealD diag_mass) { + // Checks/grid + double t0 = usecond(); + conformable(diagonal, triangle); + GridBase* grid = diagonal.Grid(); + + // Determine the boundary coordinates/sites + double t1 = usecond(); + int t_dir = Nd - 1; + Lattice<iScalar<vInteger>> t_coor(grid); + LatticeCoordinate(t_coor, t_dir); + int T = grid->GlobalDimensions()[t_dir]; + + // Set off-diagonal parts at boundary to zero -- OK + double t2 = usecond(); + CloverTriangleField zeroTriangle(grid); + zeroTriangle.Checkerboard() = triangle.Checkerboard(); + zeroTriangle = Zero(); + triangle = where(t_coor == 0, zeroTriangle, triangle); + triangle = where(t_coor == T-1, zeroTriangle, triangle); + + // Set diagonal to unity (scaled correctly) -- OK + double t3 = usecond(); + CloverDiagonalField tmp(grid); + tmp.Checkerboard() = diagonal.Checkerboard(); + tmp = -1.0 * csw_t + diag_mass; + diagonal = where(t_coor == 0, tmp, diagonal); + diagonal = where(t_coor == T-1, tmp, diagonal); + + // Correct values next to boundary + double t4 = usecond(); + if(cF != 1.0) { + tmp = cF - 1.0; + tmp += diagonal; + diagonal = where(t_coor == 1, tmp, diagonal); + diagonal = where(t_coor == T-2, tmp, diagonal); + } + + // Report timings + double t5 = usecond(); +#if 0 + std::cout << GridLogMessage << "CompactWilsonCloverHelpers::ModifyBoundaries timings:" + << " checks = " << (t1 - t0) / 1e6 + << ", coordinate = " << (t2 - t1) / 1e6 + << ", off-diag zero = " << (t3 - t2) / 1e6 + << ", diagonal unity = " << (t4 - t3) / 1e6 + << ", near-boundary = " << (t5 - t4) / 1e6 + << ", total = " << (t5 - t0) / 1e6 + << std::endl; +#endif + } + + template<class Field, class Mask> + static strong_inline void ApplyBoundaryMask(Field& f, const Mask& m) { + conformable(f, m); + auto grid = f.Grid(); + const int Nsite = grid->oSites(); + const int Nsimd = grid->Nsimd(); + autoView(f_v, f, AcceleratorWrite); + autoView(m_v, m, AcceleratorRead); + // NOTE: this function cannot be 'private' since nvcc forbids this for kernels + accelerator_for(ss, Nsite, Nsimd, { + coalescedWrite(f_v[ss], m_v(ss) * f_v(ss)); + }); + } + + template<class MaskField> + static void SetupMasks(MaskField& full, MaskField& even, MaskField& odd) { + assert(even.Grid()->_isCheckerBoarded && even.Checkerboard() == Even); + assert(odd.Grid()->_isCheckerBoarded && odd.Checkerboard() == Odd); + assert(!full.Grid()->_isCheckerBoarded); + + GridBase* grid = full.Grid(); + int t_dir = Nd-1; + Lattice<iScalar<vInteger>> t_coor(grid); + LatticeCoordinate(t_coor, t_dir); + int T = grid->GlobalDimensions()[t_dir]; + + MaskField zeroMask(grid); zeroMask = Zero(); + full = 1.0; + full = where(t_coor == 0, zeroMask, full); + full = where(t_coor == T-1, zeroMask, full); + + pickCheckerboard(Even, even, full); + pickCheckerboard(Odd, odd, full); + } +}; + NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/WilsonCloverTypes.h b/Grid/qcd/action/fermion/WilsonCloverTypes.h index 4428259f..a5a95d93 100644 --- a/Grid/qcd/action/fermion/WilsonCloverTypes.h +++ b/Grid/qcd/action/fermion/WilsonCloverTypes.h @@ -42,8 +42,51 @@ public: typedef Lattice<SiteClover> CloverField; }; +template<class Impl> +class CompactWilsonCloverTypes { +public: + INHERIT_IMPL_TYPES(Impl); + + static_assert(Nd == 4 && Nc == 3 && Ns == 4 && Impl::Dimension == 3, "Wrong dimensions"); + + static constexpr int Nred = Nc * Nhs; // 6 + static constexpr int Nblock = Nhs; // 2 + static constexpr int Ndiagonal = Nred; // 6 + static constexpr int Ntriangle = (Nred - 1) * Nc; // 15 + + template<typename vtype> using iImplCloverDiagonal = iScalar<iVector<iVector<vtype, Ndiagonal>, Nblock>>; + template<typename vtype> using iImplCloverTriangle = iScalar<iVector<iVector<vtype, Ntriangle>, Nblock>>; + + typedef iImplCloverDiagonal<Simd> SiteCloverDiagonal; + typedef iImplCloverTriangle<Simd> SiteCloverTriangle; + typedef iSinglet<Simd> SiteMask; + + typedef Lattice<SiteCloverDiagonal> CloverDiagonalField; + typedef Lattice<SiteCloverTriangle> CloverTriangleField; + typedef Lattice<SiteMask> MaskField; +}; + #define INHERIT_CLOVER_TYPES(Impl) \ typedef typename WilsonCloverTypes<Impl>::SiteClover SiteClover; \ typedef typename WilsonCloverTypes<Impl>::CloverField CloverField; +#define INHERIT_COMPACT_CLOVER_TYPES(Impl) \ + typedef typename CompactWilsonCloverTypes<Impl>::SiteCloverDiagonal SiteCloverDiagonal; \ + typedef typename CompactWilsonCloverTypes<Impl>::SiteCloverTriangle SiteCloverTriangle; \ + typedef typename CompactWilsonCloverTypes<Impl>::SiteMask SiteMask; \ + typedef typename CompactWilsonCloverTypes<Impl>::CloverDiagonalField CloverDiagonalField; \ + typedef typename CompactWilsonCloverTypes<Impl>::CloverTriangleField CloverTriangleField; \ + typedef typename CompactWilsonCloverTypes<Impl>::MaskField MaskField; \ + /* ugly duplication but needed inside functionality classes */ \ + template<typename vtype> using iImplCloverDiagonal = \ + iScalar<iVector<iVector<vtype, CompactWilsonCloverTypes<Impl>::Ndiagonal>, CompactWilsonCloverTypes<Impl>::Nblock>>; \ + template<typename vtype> using iImplCloverTriangle = \ + iScalar<iVector<iVector<vtype, CompactWilsonCloverTypes<Impl>::Ntriangle>, CompactWilsonCloverTypes<Impl>::Nblock>>; + +#define INHERIT_COMPACT_CLOVER_SIZES(Impl) \ + static constexpr int Nred = CompactWilsonCloverTypes<Impl>::Nred; \ + static constexpr int Nblock = CompactWilsonCloverTypes<Impl>::Nblock; \ + static constexpr int Ndiagonal = CompactWilsonCloverTypes<Impl>::Ndiagonal; \ + static constexpr int Ntriangle = CompactWilsonCloverTypes<Impl>::Ntriangle; + NAMESPACE_END(Grid); diff --git a/tests/core/Test_compact_wilson_clover_speedup.cc b/tests/core/Test_compact_wilson_clover_speedup.cc new file mode 100644 index 00000000..83e3da1f --- /dev/null +++ b/tests/core/Test_compact_wilson_clover_speedup.cc @@ -0,0 +1,226 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/core/Test_compact_wilson_clover_speedup.cc + + Copyright (C) 2020 - 2022 + + Author: Daniel Richtmann <daniel.richtmann@gmail.com> + Author: Nils Meyer <nils.meyer@ur.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ + +#include <Grid/Grid.h> + +using namespace Grid; + +NAMESPACE_BEGIN(CommandlineHelpers); + +static bool checkPresent(int* argc, char*** argv, const std::string& option) { + return GridCmdOptionExists(*argv, *argv + *argc, option); +} + +static std::string getContent(int* argc, char*** argv, const std::string& option) { + return GridCmdOptionPayload(*argv, *argv + *argc, option); +} + +static int readInt(int* argc, char*** argv, std::string&& option, int defaultValue) { + std::string arg; + int ret = defaultValue; + if(checkPresent(argc, argv, option)) { + arg = getContent(argc, argv, option); + GridCmdOptionInt(arg, ret); + } + return ret; +} + +static float readFloat(int* argc, char*** argv, std::string&& option, float defaultValue) { + std::string arg; + float ret = defaultValue; + if(checkPresent(argc, argv, option)) { + arg = getContent(argc, argv, option); + GridCmdOptionFloat(arg, ret); + } + return ret; +} + +NAMESPACE_END(CommandlineHelpers); + + +#define _grid_printf(LOGGER, ...) \ + { \ + if((LOGGER).isActive()) { /* this makes it safe to put, e.g., norm2 in the calling code w.r.t. performance */ \ + char _printf_buf[1024]; \ + std::sprintf(_printf_buf, __VA_ARGS__); \ + std::cout << (LOGGER) << _printf_buf; \ + fflush(stdout); \ + } \ + } +#define grid_printf_msg(...) _grid_printf(GridLogMessage, __VA_ARGS__) + + +template<typename Field> +bool resultsAgree(const Field& ref, const Field& res, const std::string& name) { + RealD checkTolerance = (getPrecision<Field>::value == 2) ? 1e-15 : 1e-7; + Field diff(ref.Grid()); + diff = ref - res; + auto absDev = norm2(diff); + auto relDev = absDev / norm2(ref); + std::cout << GridLogMessage + << "norm2(reference), norm2(" << name << "), abs. deviation, rel. deviation: " << norm2(ref) << " " + << norm2(res) << " " << absDev << " " << relDev << " -> check " + << ((relDev < checkTolerance) ? "passed" : "failed") << std::endl; + + return relDev <= checkTolerance; +} + + +template<typename vCoeff_t> +void runBenchmark(int* argc, char*** argv) { + // read from command line + const int nIter = CommandlineHelpers::readInt( argc, argv, "--niter", 1000); + const RealD mass = CommandlineHelpers::readFloat( argc, argv, "--mass", 0.5); + const RealD csw = CommandlineHelpers::readFloat( argc, argv, "--csw", 1.0); + const RealD cF = CommandlineHelpers::readFloat( argc, argv, "--cF", 1.0); + const bool antiPeriodic = CommandlineHelpers::checkPresent(argc, argv, "--antiperiodic"); + + // precision + static_assert(getPrecision<vCoeff_t>::value == 2 || getPrecision<vCoeff_t>::value == 1, "Incorrect precision"); // double or single + std::string precision = (getPrecision<vCoeff_t>::value == 2 ? "double" : "single"); + + // setup grids + GridCartesian* UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd, vCoeff_t::Nsimd()), GridDefaultMpi()); + GridRedBlackCartesian* UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); + // clang-format on + + // setup rng + std::vector<int> seeds({1, 2, 3, 4}); + GridParallelRNG pRNG(UGrid); + pRNG.SeedFixedIntegers(seeds); + + // type definitions + typedef WilsonImpl<vCoeff_t, FundamentalRepresentation, CoeffReal> WImpl; + typedef WilsonCloverFermion<WImpl> WilsonCloverOperator; + typedef CompactWilsonCloverFermion<WImpl> CompactWilsonCloverOperator; + typedef typename WilsonCloverOperator::FermionField Fermion; + typedef typename WilsonCloverOperator::GaugeField Gauge; + + // setup fields + Fermion src(UGrid); random(pRNG, src); + Fermion ref(UGrid); ref = Zero(); + Fermion res(UGrid); res = Zero(); + Fermion hop(UGrid); hop = Zero(); + Fermion diff(UGrid); diff = Zero(); + Gauge Umu(UGrid); SU3::HotConfiguration(pRNG, Umu); + + // setup boundary phases + typename WilsonCloverOperator::ImplParams implParams; + std::vector<Complex> boundary_phases(Nd, 1.); + if(antiPeriodic) boundary_phases[Nd-1] = -1.; + implParams.boundary_phases = boundary_phases; + WilsonAnisotropyCoefficients anisParams; + + // misc stuff needed for benchmarks + double volume=1.0; for(int mu=0; mu<Nd; mu++) volume*=UGrid->_fdimensions[mu]; + + // setup fermion operators + WilsonCloverOperator Dwc( Umu, *UGrid, *UrbGrid, mass, csw, csw, anisParams, implParams); + CompactWilsonCloverOperator Dwc_compact(Umu, *UGrid, *UrbGrid, mass, csw, csw, cF, anisParams, implParams); + + // now test the conversions + typename CompactWilsonCloverOperator::CloverField tmp_ref(UGrid); tmp_ref = Dwc.CloverTerm; + typename CompactWilsonCloverOperator::CloverField tmp_res(UGrid); tmp_res = Zero(); + typename CompactWilsonCloverOperator::CloverField tmp_diff(UGrid); tmp_diff = Zero(); + typename CompactWilsonCloverOperator::CloverDiagonalField diagonal(UGrid); diagonal = Zero(); + typename CompactWilsonCloverOperator::CloverTriangleField triangle(UGrid); diagonal = Zero(); + CompactWilsonCloverOperator::CompactHelpers::ConvertLayout(tmp_ref, diagonal, triangle); + CompactWilsonCloverOperator::CompactHelpers::ConvertLayout(diagonal, triangle, tmp_res); + tmp_diff = tmp_ref - tmp_res; + std::cout << GridLogMessage << "conversion: ref, res, diff, eps" + << " " << norm2(tmp_ref) + << " " << norm2(tmp_res) + << " " << norm2(tmp_diff) + << " " << norm2(tmp_diff) / norm2(tmp_ref) + << std::endl; + + // performance per site (use minimal values necessary) + double hop_flop_per_site = 1320; // Rich's Talk + what Peter uses + double hop_byte_per_site = (8 * 9 + 9 * 12) * 2 * getPrecision<vCoeff_t>::value * 4; + double clov_flop_per_site = 504; // Rich's Talk and 1412.2629 + double clov_byte_per_site = (2 * 18 + 12 + 12) * 2 * getPrecision<vCoeff_t>::value * 4; + double clov_flop_per_site_performed = 1128; + double clov_byte_per_site_performed = (12 * 12 + 12 + 12) * 2 * getPrecision<vCoeff_t>::value * 4; + + // total performance numbers + double hop_gflop_total = volume * nIter * hop_flop_per_site / 1e9; + double hop_gbyte_total = volume * nIter * hop_byte_per_site / 1e9; + double clov_gflop_total = volume * nIter * clov_flop_per_site / 1e9; + double clov_gbyte_total = volume * nIter * clov_byte_per_site / 1e9; + double clov_gflop_performed_total = volume * nIter * clov_flop_per_site_performed / 1e9; + double clov_gbyte_performed_total = volume * nIter * clov_byte_per_site_performed / 1e9; + + // warmup + measure dhop + for(auto n : {1, 2, 3, 4, 5}) Dwc.Dhop(src, hop, 0); + double t0 = usecond(); + for(int n = 0; n < nIter; n++) Dwc.Dhop(src, hop, 0); + double t1 = usecond(); + double secs_hop = (t1-t0)/1e6; + grid_printf_msg("Performance(%35s, %s): %2.4f s, %6.0f GFlop/s, %6.0f GByte/s, speedup vs ref = %.2f, fraction of hop = %.2f\n", + "hop", precision.c_str(), secs_hop, hop_gflop_total/secs_hop, hop_gbyte_total/secs_hop, 0.0, secs_hop/secs_hop); + +#define BENCH_CLOVER_KERNEL(KERNEL) { \ + /* warmup + measure reference clover */ \ + for(auto n : {1, 2, 3, 4, 5}) Dwc.KERNEL(src, ref); \ + double t2 = usecond(); \ + for(int n = 0; n < nIter; n++) Dwc.KERNEL(src, ref); \ + double t3 = usecond(); \ + double secs_ref = (t3-t2)/1e6; \ + grid_printf_msg("Performance(%35s, %s): %2.4f s, %6.0f GFlop/s, %6.0f GByte/s, speedup vs ref = %.2f, fraction of hop = %.2f\n", \ + "reference_"#KERNEL, precision.c_str(), secs_ref, clov_gflop_total/secs_ref, clov_gbyte_total/secs_ref, secs_ref/secs_ref, secs_ref/secs_hop); \ + grid_printf_msg("Performance(%35s, %s): %2.4f s, %6.0f GFlop/s, %6.0f GByte/s, speedup vs ref = %.2f, fraction of hop = %.2f\n", /* to see how well the ET performs */ \ + "reference_"#KERNEL"_performed", precision.c_str(), secs_ref, clov_gflop_performed_total/secs_ref, clov_gbyte_performed_total/secs_ref, secs_ref/secs_ref, secs_ref/secs_hop); \ +\ + /* warmup + measure compact clover */ \ + for(auto n : {1, 2, 3, 4, 5}) Dwc_compact.KERNEL(src, res); \ + double t4 = usecond(); \ + for(int n = 0; n < nIter; n++) Dwc_compact.KERNEL(src, res); \ + double t5 = usecond(); \ + double secs_res = (t5-t4)/1e6; \ + grid_printf_msg("Performance(%35s, %s): %2.4f s, %6.0f GFlop/s, %6.0f GByte/s, speedup vs ref = %.2f, fraction of hop = %.2f\n", \ + "compact_"#KERNEL, precision.c_str(), secs_res, clov_gflop_total/secs_res, clov_gbyte_total/secs_res, secs_ref/secs_res, secs_res/secs_hop); \ + assert(resultsAgree(ref, res, #KERNEL)); \ +} + + BENCH_CLOVER_KERNEL(Mooee); + BENCH_CLOVER_KERNEL(MooeeDag); + BENCH_CLOVER_KERNEL(MooeeInv); + BENCH_CLOVER_KERNEL(MooeeInvDag); + + grid_printf_msg("finalize %s\n", precision.c_str()); +} + +int main(int argc, char** argv) { + Grid_init(&argc, &argv); + + runBenchmark<vComplexD>(&argc, &argv); + runBenchmark<vComplexF>(&argc, &argv); + + Grid_finalize(); +} From 1b6b12589f7634ce1eecda5b3b5ce0a20897dfce Mon Sep 17 00:00:00 2001 From: Daniel Richtmann <daniel.richtmann@gmail.com> Date: Tue, 1 Feb 2022 22:41:01 +0100 Subject: [PATCH 281/399] Get splitting up into implementation and instantiation files correct --- .../fermion/CompactWilsonCloverFermion.h | 296 +------------- Grid/qcd/action/fermion/Fermion.h | 18 + ...CompactWilsonCloverFermionImplementation.h | 363 ++++++++++++++++++ ...WilsonCloverFermionInstantiation.cc.master | 41 ++ ...rFermionInstantiationGparityWilsonImplD.cc | 1 + ...rFermionInstantiationGparityWilsonImplF.cc | 1 + ...loverFermionInstantiationWilsonAdjImplD.cc | 1 + ...loverFermionInstantiationWilsonAdjImplF.cc | 1 + ...onCloverFermionInstantiationWilsonImplD.cc | 1 + ...onCloverFermionInstantiationWilsonImplF.cc | 1 + ...tiationWilsonTwoIndexAntiSymmetricImplD.cc | 1 + ...tiationWilsonTwoIndexAntiSymmetricImplF.cc | 1 + ...stantiationWilsonTwoIndexSymmetricImplD.cc | 1 + ...stantiationWilsonTwoIndexSymmetricImplF.cc | 1 + .../instantiation/generate_instantiations.sh | 2 +- 15 files changed, 454 insertions(+), 276 deletions(-) create mode 100644 Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h create mode 100644 Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master create mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc create mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplD/CompactWilsonCloverFermionInstantiationWilsonImplD.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonImplF/CompactWilsonCloverFermionInstantiationWilsonImplF.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc create mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc diff --git a/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h index 550bef85..3a166134 100644 --- a/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h +++ b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h @@ -125,31 +125,7 @@ public: const RealD _csw_t = 0.0, const RealD _cF = 1.0, const WilsonAnisotropyCoefficients& clover_anisotropy = WilsonAnisotropyCoefficients(), - const ImplParams& impl_p = ImplParams()) - : WilsonBase(_Umu, Fgrid, Hgrid, _mass, impl_p, clover_anisotropy) - , csw_r(_csw_r) - , csw_t(_csw_t) - , cF(_cF) - , open_boundaries(impl_p.boundary_phases[Nd-1] == 0.0) - , Diagonal(&Fgrid), Triangle(&Fgrid) - , DiagonalEven(&Hgrid), TriangleEven(&Hgrid) - , DiagonalOdd(&Hgrid), TriangleOdd(&Hgrid) - , DiagonalInv(&Fgrid), TriangleInv(&Fgrid) - , DiagonalInvEven(&Hgrid), TriangleInvEven(&Hgrid) - , DiagonalInvOdd(&Hgrid), TriangleInvOdd(&Hgrid) - , Tmp(&Fgrid) - , BoundaryMask(&Fgrid) - , BoundaryMaskEven(&Hgrid), BoundaryMaskOdd(&Hgrid) - { - csw_r *= 0.5; - csw_t *= 0.5; - if (clover_anisotropy.isAnisotropic) - csw_r /= clover_anisotropy.xi_0; - - ImportGauge(_Umu); - if (open_boundaries) - CompactHelpers::SetupMasks(this->BoundaryMask, this->BoundaryMaskEven, this->BoundaryMaskOdd); - } + const ImplParams& impl_p = ImplParams()); ///////////////////////////////////////////// // Member functions (implementing interface) @@ -161,191 +137,41 @@ public: int ConstEE() override { return 0; }; int isTrivialEE() override { return 0; }; + void Dhop(const FermionField& in, FermionField& out, int dag) override; - void Dhop(const FermionField& in, FermionField& out, int dag) override { - WilsonBase::Dhop(in, out, dag); - if(open_boundaries) ApplyBoundaryMask(out); - } + void DhopOE(const FermionField& in, FermionField& out, int dag) override; - void DhopOE(const FermionField& in, FermionField& out, int dag) override { - WilsonBase::DhopOE(in, out, dag); - if(open_boundaries) ApplyBoundaryMask(out); - } + void DhopEO(const FermionField& in, FermionField& out, int dag) override; - void DhopEO(const FermionField& in, FermionField& out, int dag) override { - WilsonBase::DhopEO(in, out, dag); - if(open_boundaries) ApplyBoundaryMask(out); - } + void DhopDir(const FermionField& in, FermionField& out, int dir, int disp) override; - void DhopDir(const FermionField& in, FermionField& out, int dir, int disp) override { - WilsonBase::DhopDir(in, out, dir, disp); - if(this->open_boundaries) ApplyBoundaryMask(out); - } + void DhopDirAll(const FermionField& in, std::vector<FermionField>& out) /* override */; - void DhopDirAll(const FermionField& in, std::vector<FermionField>& out) /* override */ { - WilsonBase::DhopDirAll(in, out); - if(this->open_boundaries) { - for(auto& o : out) ApplyBoundaryMask(o); - } - } + void M(const FermionField& in, FermionField& out) override; - void M(const FermionField& in, FermionField& out) override { - out.Checkerboard() = in.Checkerboard(); - WilsonBase::Dhop(in, out, DaggerNo); // call base to save applying bc - Mooee(in, Tmp); - axpy(out, 1.0, out, Tmp); - if(open_boundaries) ApplyBoundaryMask(out); - } + void Mdag(const FermionField& in, FermionField& out) override; - void Mdag(const FermionField& in, FermionField& out) override { - out.Checkerboard() = in.Checkerboard(); - WilsonBase::Dhop(in, out, DaggerYes); // call base to save applying bc - MooeeDag(in, Tmp); - axpy(out, 1.0, out, Tmp); - if(open_boundaries) ApplyBoundaryMask(out); - } + void Meooe(const FermionField& in, FermionField& out) override; - void Meooe(const FermionField& in, FermionField& out) override { - WilsonBase::Meooe(in, out); - if(open_boundaries) ApplyBoundaryMask(out); - } + void MeooeDag(const FermionField& in, FermionField& out) override; - void MeooeDag(const FermionField& in, FermionField& out) override { - WilsonBase::MeooeDag(in, out); - if(open_boundaries) ApplyBoundaryMask(out); - } + void Mooee(const FermionField& in, FermionField& out) override; - void Mooee(const FermionField& in, FermionField& out) override { - if(in.Grid()->_isCheckerBoarded) { - if(in.Checkerboard() == Odd) { - MooeeInternal(in, out, DiagonalOdd, TriangleOdd); - } else { - MooeeInternal(in, out, DiagonalEven, TriangleEven); - } - } else { - MooeeInternal(in, out, Diagonal, Triangle); - } - if(open_boundaries) ApplyBoundaryMask(out); - } + void MooeeDag(const FermionField& in, FermionField& out) override; - void MooeeDag(const FermionField& in, FermionField& out) override { - Mooee(in, out); // blocks are hermitian - } + void MooeeInv(const FermionField& in, FermionField& out) override; - void MooeeInv(const FermionField& in, FermionField& out) override { - if(in.Grid()->_isCheckerBoarded) { - if(in.Checkerboard() == Odd) { - MooeeInternal(in, out, DiagonalInvOdd, TriangleInvOdd); - } else { - MooeeInternal(in, out, DiagonalInvEven, TriangleInvEven); - } - } else { - MooeeInternal(in, out, DiagonalInv, TriangleInv); - } - if(open_boundaries) ApplyBoundaryMask(out); - } + void MooeeInvDag(const FermionField& in, FermionField& out) override; - void MooeeInvDag(const FermionField& in, FermionField& out) override { - MooeeInv(in, out); // blocks are hermitian - } + void Mdir(const FermionField& in, FermionField& out, int dir, int disp) override; - void Mdir(const FermionField& in, FermionField& out, int dir, int disp) override { - DhopDir(in, out, dir, disp); - } + void MdirAll(const FermionField& in, std::vector<FermionField>& out) override; - void MdirAll(const FermionField& in, std::vector<FermionField>& out) override { - DhopDirAll(in, out); - } + void MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) override; - void MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) override { - assert(!open_boundaries); // TODO check for changes required for open bc + void MooDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override; - // NOTE: code copied from original clover term - conformable(X.Grid(), Y.Grid()); - conformable(X.Grid(), force.Grid()); - GaugeLinkField force_mu(force.Grid()), lambda(force.Grid()); - GaugeField clover_force(force.Grid()); - PropagatorField Lambda(force.Grid()); - - // Guido: Here we are hitting some performance issues: - // need to extract the components of the DoubledGaugeField - // for each call - // Possible solution - // Create a vector object to store them? (cons: wasting space) - std::vector<GaugeLinkField> U(Nd, this->Umu.Grid()); - - Impl::extractLinkField(U, this->Umu); - - force = Zero(); - // Derivative of the Wilson hopping term - this->DhopDeriv(force, X, Y, dag); - - /////////////////////////////////////////////////////////// - // Clover term derivative - /////////////////////////////////////////////////////////// - Impl::outerProductImpl(Lambda, X, Y); - //std::cout << "Lambda:" << Lambda << std::endl; - - Gamma::Algebra sigma[] = { - Gamma::Algebra::SigmaXY, - Gamma::Algebra::SigmaXZ, - Gamma::Algebra::SigmaXT, - Gamma::Algebra::MinusSigmaXY, - Gamma::Algebra::SigmaYZ, - Gamma::Algebra::SigmaYT, - Gamma::Algebra::MinusSigmaXZ, - Gamma::Algebra::MinusSigmaYZ, - Gamma::Algebra::SigmaZT, - Gamma::Algebra::MinusSigmaXT, - Gamma::Algebra::MinusSigmaYT, - Gamma::Algebra::MinusSigmaZT}; - - /* - sigma_{\mu \nu}= - | 0 sigma[0] sigma[1] sigma[2] | - | sigma[3] 0 sigma[4] sigma[5] | - | sigma[6] sigma[7] 0 sigma[8] | - | sigma[9] sigma[10] sigma[11] 0 | - */ - - int count = 0; - clover_force = Zero(); - for (int mu = 0; mu < 4; mu++) - { - force_mu = Zero(); - for (int nu = 0; nu < 4; nu++) - { - if (mu == nu) - continue; - - RealD factor; - if (nu == 4 || mu == 4) - { - factor = 2.0 * csw_t; - } - else - { - factor = 2.0 * csw_r; - } - PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked - Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok - force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked - count++; - } - - pokeLorentz(clover_force, U[mu] * force_mu, mu); - } - //clover_force *= csw; - force += clover_force; - } - - void MooDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override { - assert(0); - } - - void MeeDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override { - assert(0); - } + void MeeDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override; ///////////////////////////////////////////// // Member functions (internals) @@ -354,93 +180,13 @@ public: void MooeeInternal(const FermionField& in, FermionField& out, const CloverDiagonalField& diagonal, - const CloverTriangleField& triangle) { - assert(in.Checkerboard() == Odd || in.Checkerboard() == Even); - out.Checkerboard() = in.Checkerboard(); - conformable(in, out); - conformable(in, diagonal); - conformable(in, triangle); - - CompactHelpers::MooeeKernel(diagonal.oSites(), 1, in, out, diagonal, triangle); - } + const CloverTriangleField& triangle); ///////////////////////////////////////////// // Helpers ///////////////////////////////////////////// - void ImportGauge(const GaugeField& _Umu) override { - // NOTE: parts copied from original implementation - - // Import gauge into base class - double t0 = usecond(); - WilsonBase::ImportGauge(_Umu); // NOTE: called here and in wilson constructor -> performed twice, but can't avoid that - - // Initialize temporary variables - double t1 = usecond(); - conformable(_Umu.Grid(), this->GaugeGrid()); - GridBase* grid = _Umu.Grid(); - typename Impl::GaugeLinkField Bx(grid), By(grid), Bz(grid), Ex(grid), Ey(grid), Ez(grid); - CloverField TmpOriginal(grid); - - // Compute the field strength terms mu>nu - double t2 = usecond(); - WilsonLoops<Impl>::FieldStrength(Bx, _Umu, Zdir, Ydir); - WilsonLoops<Impl>::FieldStrength(By, _Umu, Zdir, Xdir); - WilsonLoops<Impl>::FieldStrength(Bz, _Umu, Ydir, Xdir); - WilsonLoops<Impl>::FieldStrength(Ex, _Umu, Tdir, Xdir); - WilsonLoops<Impl>::FieldStrength(Ey, _Umu, Tdir, Ydir); - WilsonLoops<Impl>::FieldStrength(Ez, _Umu, Tdir, Zdir); - - // Compute the Clover Operator acting on Colour and Spin - // multiply here by the clover coefficients for the anisotropy - double t3 = usecond(); - TmpOriginal = Helpers::fillCloverYZ(Bx) * csw_r; - TmpOriginal += Helpers::fillCloverXZ(By) * csw_r; - TmpOriginal += Helpers::fillCloverXY(Bz) * csw_r; - TmpOriginal += Helpers::fillCloverXT(Ex) * csw_t; - TmpOriginal += Helpers::fillCloverYT(Ey) * csw_t; - TmpOriginal += Helpers::fillCloverZT(Ez) * csw_t; - TmpOriginal += this->diag_mass; - - // Convert the data layout of the clover term - double t4 = usecond(); - CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); - - // Possible modify the boundary values - double t5 = usecond(); - if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); - - // Invert the clover term in the improved layout - double t6 = usecond(); - CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); - - // Fill the remaining clover fields - double t7 = usecond(); - pickCheckerboard(Even, DiagonalEven, Diagonal); - pickCheckerboard(Even, TriangleEven, Triangle); - pickCheckerboard(Odd, DiagonalOdd, Diagonal); - pickCheckerboard(Odd, TriangleOdd, Triangle); - pickCheckerboard(Even, DiagonalInvEven, DiagonalInv); - pickCheckerboard(Even, TriangleInvEven, TriangleInv); - pickCheckerboard(Odd, DiagonalInvOdd, DiagonalInv); - pickCheckerboard(Odd, TriangleInvOdd, TriangleInv); - - // Report timings - double t8 = usecond(); -#if 0 - std::cout << GridLogMessage << "CompactWilsonCloverFermion::ImportGauge timings:" - << " WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 - << ", allocations = " << (t2 - t1) / 1e6 - << ", field strength = " << (t3 - t2) / 1e6 - << ", fill clover = " << (t4 - t3) / 1e6 - << ", convert = " << (t5 - t4) / 1e6 - << ", boundaries = " << (t6 - t5) / 1e6 - << ", inversions = " << (t7 - t6) / 1e6 - << ", pick cbs = " << (t8 - t7) / 1e6 - << ", total = " << (t8 - t0) / 1e6 - << std::endl; -#endif - } + void ImportGauge(const GaugeField& _Umu) override; ///////////////////////////////////////////// // Helpers diff --git a/Grid/qcd/action/fermion/Fermion.h b/Grid/qcd/action/fermion/Fermion.h index 59fc17d5..78cf7851 100644 --- a/Grid/qcd/action/fermion/Fermion.h +++ b/Grid/qcd/action/fermion/Fermion.h @@ -53,6 +53,7 @@ NAMESPACE_CHECK(Wilson); #include <Grid/qcd/action/fermion/WilsonTMFermion.h> // 4d wilson like NAMESPACE_CHECK(WilsonTM); #include <Grid/qcd/action/fermion/WilsonCloverFermion.h> // 4d wilson clover fermions +#include <Grid/qcd/action/fermion/CompactWilsonCloverFermion.h> // 4d compact wilson clover fermions NAMESPACE_CHECK(WilsonClover); #include <Grid/qcd/action/fermion/WilsonFermion5D.h> // 5d base used by all 5d overlap types NAMESPACE_CHECK(Wilson5D); @@ -153,6 +154,23 @@ typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR> WilsonCloverTwoInd typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF> WilsonCloverTwoIndexAntiSymmetricFermionF; typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD> WilsonCloverTwoIndexAntiSymmetricFermionD; +// Compact Clover fermions +typedef CompactWilsonCloverFermion<WilsonImplR> CompactWilsonCloverFermionR; +typedef CompactWilsonCloverFermion<WilsonImplF> CompactWilsonCloverFermionF; +typedef CompactWilsonCloverFermion<WilsonImplD> CompactWilsonCloverFermionD; + +typedef CompactWilsonCloverFermion<WilsonAdjImplR> CompactWilsonCloverAdjFermionR; +typedef CompactWilsonCloverFermion<WilsonAdjImplF> CompactWilsonCloverAdjFermionF; +typedef CompactWilsonCloverFermion<WilsonAdjImplD> CompactWilsonCloverAdjFermionD; + +typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplR> CompactWilsonCloverTwoIndexSymmetricFermionR; +typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplF> CompactWilsonCloverTwoIndexSymmetricFermionF; +typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplD> CompactWilsonCloverTwoIndexSymmetricFermionD; + +typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR> CompactWilsonCloverTwoIndexAntiSymmetricFermionR; +typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF> CompactWilsonCloverTwoIndexAntiSymmetricFermionF; +typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD> CompactWilsonCloverTwoIndexAntiSymmetricFermionD; + // Domain Wall fermions typedef DomainWallFermion<WilsonImplR> DomainWallFermionR; typedef DomainWallFermion<WilsonImplF> DomainWallFermionF; diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h new file mode 100644 index 00000000..3dfcb133 --- /dev/null +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -0,0 +1,363 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/qcd/action/fermion/CompactWilsonCloverFermionImplementation.h + + Copyright (C) 2017 - 2022 + + Author: paboyle <paboyle@ph.ed.ac.uk> + Author: Guido Cossu <guido.cossu@ed.ac.uk> + Author: Daniel Richtmann <daniel.richtmann@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + +#include <Grid/Grid.h> +#include <Grid/qcd/spin/Dirac.h> +#include <Grid/qcd/action/fermion/CompactWilsonCloverFermion.h> + +NAMESPACE_BEGIN(Grid); +template<class Impl> +CompactWilsonCloverFermion<Impl>::CompactWilsonCloverFermion(GaugeField& _Umu, + GridCartesian& Fgrid, + GridRedBlackCartesian& Hgrid, + const RealD _mass, + const RealD _csw_r, + const RealD _csw_t, + const RealD _cF, + const WilsonAnisotropyCoefficients& clover_anisotropy, + const ImplParams& impl_p) + : WilsonBase(_Umu, Fgrid, Hgrid, _mass, impl_p, clover_anisotropy) + , csw_r(_csw_r) + , csw_t(_csw_t) + , cF(_cF) + , open_boundaries(impl_p.boundary_phases[Nd-1] == 0.0) + , Diagonal(&Fgrid), Triangle(&Fgrid) + , DiagonalEven(&Hgrid), TriangleEven(&Hgrid) + , DiagonalOdd(&Hgrid), TriangleOdd(&Hgrid) + , DiagonalInv(&Fgrid), TriangleInv(&Fgrid) + , DiagonalInvEven(&Hgrid), TriangleInvEven(&Hgrid) + , DiagonalInvOdd(&Hgrid), TriangleInvOdd(&Hgrid) + , Tmp(&Fgrid) + , BoundaryMask(&Fgrid) + , BoundaryMaskEven(&Hgrid), BoundaryMaskOdd(&Hgrid) +{ + csw_r *= 0.5; + csw_t *= 0.5; + if (clover_anisotropy.isAnisotropic) + csw_r /= clover_anisotropy.xi_0; + + ImportGauge(_Umu); + if (open_boundaries) + CompactHelpers::SetupMasks(this->BoundaryMask, this->BoundaryMaskEven, this->BoundaryMaskOdd); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::Dhop(const FermionField& in, FermionField& out, int dag) { + WilsonBase::Dhop(in, out, dag); + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::DhopOE(const FermionField& in, FermionField& out, int dag) { + WilsonBase::DhopOE(in, out, dag); + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::DhopEO(const FermionField& in, FermionField& out, int dag) { + WilsonBase::DhopEO(in, out, dag); + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::DhopDir(const FermionField& in, FermionField& out, int dir, int disp) { + WilsonBase::DhopDir(in, out, dir, disp); + if(this->open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::DhopDirAll(const FermionField& in, std::vector<FermionField>& out) { + WilsonBase::DhopDirAll(in, out); + if(this->open_boundaries) { + for(auto& o : out) ApplyBoundaryMask(o); + } +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::M(const FermionField& in, FermionField& out) { + out.Checkerboard() = in.Checkerboard(); + WilsonBase::Dhop(in, out, DaggerNo); // call base to save applying bc + Mooee(in, Tmp); + axpy(out, 1.0, out, Tmp); + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::Mdag(const FermionField& in, FermionField& out) { + out.Checkerboard() = in.Checkerboard(); + WilsonBase::Dhop(in, out, DaggerYes); // call base to save applying bc + MooeeDag(in, Tmp); + axpy(out, 1.0, out, Tmp); + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::Meooe(const FermionField& in, FermionField& out) { + WilsonBase::Meooe(in, out); + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MeooeDag(const FermionField& in, FermionField& out) { + WilsonBase::MeooeDag(in, out); + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::Mooee(const FermionField& in, FermionField& out) { + if(in.Grid()->_isCheckerBoarded) { + if(in.Checkerboard() == Odd) { + MooeeInternal(in, out, DiagonalOdd, TriangleOdd); + } else { + MooeeInternal(in, out, DiagonalEven, TriangleEven); + } + } else { + MooeeInternal(in, out, Diagonal, Triangle); + } + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MooeeDag(const FermionField& in, FermionField& out) { + Mooee(in, out); // blocks are hermitian +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MooeeInv(const FermionField& in, FermionField& out) { + if(in.Grid()->_isCheckerBoarded) { + if(in.Checkerboard() == Odd) { + MooeeInternal(in, out, DiagonalInvOdd, TriangleInvOdd); + } else { + MooeeInternal(in, out, DiagonalInvEven, TriangleInvEven); + } + } else { + MooeeInternal(in, out, DiagonalInv, TriangleInv); + } + if(open_boundaries) ApplyBoundaryMask(out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MooeeInvDag(const FermionField& in, FermionField& out) { + MooeeInv(in, out); // blocks are hermitian +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::Mdir(const FermionField& in, FermionField& out, int dir, int disp) { + DhopDir(in, out, dir, disp); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MdirAll(const FermionField& in, std::vector<FermionField>& out) { + DhopDirAll(in, out); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) { + assert(!open_boundaries); // TODO check for changes required for open bc + + // NOTE: code copied from original clover term + conformable(X.Grid(), Y.Grid()); + conformable(X.Grid(), force.Grid()); + GaugeLinkField force_mu(force.Grid()), lambda(force.Grid()); + GaugeField clover_force(force.Grid()); + PropagatorField Lambda(force.Grid()); + + // Guido: Here we are hitting some performance issues: + // need to extract the components of the DoubledGaugeField + // for each call + // Possible solution + // Create a vector object to store them? (cons: wasting space) + std::vector<GaugeLinkField> U(Nd, this->Umu.Grid()); + + Impl::extractLinkField(U, this->Umu); + + force = Zero(); + // Derivative of the Wilson hopping term + this->DhopDeriv(force, X, Y, dag); + + /////////////////////////////////////////////////////////// + // Clover term derivative + /////////////////////////////////////////////////////////// + Impl::outerProductImpl(Lambda, X, Y); + //std::cout << "Lambda:" << Lambda << std::endl; + + Gamma::Algebra sigma[] = { + Gamma::Algebra::SigmaXY, + Gamma::Algebra::SigmaXZ, + Gamma::Algebra::SigmaXT, + Gamma::Algebra::MinusSigmaXY, + Gamma::Algebra::SigmaYZ, + Gamma::Algebra::SigmaYT, + Gamma::Algebra::MinusSigmaXZ, + Gamma::Algebra::MinusSigmaYZ, + Gamma::Algebra::SigmaZT, + Gamma::Algebra::MinusSigmaXT, + Gamma::Algebra::MinusSigmaYT, + Gamma::Algebra::MinusSigmaZT}; + + /* + sigma_{\mu \nu}= + | 0 sigma[0] sigma[1] sigma[2] | + | sigma[3] 0 sigma[4] sigma[5] | + | sigma[6] sigma[7] 0 sigma[8] | + | sigma[9] sigma[10] sigma[11] 0 | + */ + + int count = 0; + clover_force = Zero(); + for (int mu = 0; mu < 4; mu++) + { + force_mu = Zero(); + for (int nu = 0; nu < 4; nu++) + { + if (mu == nu) + continue; + + RealD factor; + if (nu == 4 || mu == 4) + { + factor = 2.0 * csw_t; + } + else + { + factor = 2.0 * csw_r; + } + PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked + Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok + force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked + count++; + } + + pokeLorentz(clover_force, U[mu] * force_mu, mu); + } + //clover_force *= csw; + force += clover_force; +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MooDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) { + assert(0); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MeeDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) { + assert(0); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::MooeeInternal(const FermionField& in, + FermionField& out, + const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle) { + assert(in.Checkerboard() == Odd || in.Checkerboard() == Even); + out.Checkerboard() = in.Checkerboard(); + conformable(in, out); + conformable(in, diagonal); + conformable(in, triangle); + + CompactHelpers::MooeeKernel(diagonal.oSites(), 1, in, out, diagonal, triangle); +} + +template<class Impl> +void CompactWilsonCloverFermion<Impl>::ImportGauge(const GaugeField& _Umu) { + // NOTE: parts copied from original implementation + + // Import gauge into base class + double t0 = usecond(); + WilsonBase::ImportGauge(_Umu); // NOTE: called here and in wilson constructor -> performed twice, but can't avoid that + + // Initialize temporary variables + double t1 = usecond(); + conformable(_Umu.Grid(), this->GaugeGrid()); + GridBase* grid = _Umu.Grid(); + typename Impl::GaugeLinkField Bx(grid), By(grid), Bz(grid), Ex(grid), Ey(grid), Ez(grid); + CloverField TmpOriginal(grid); + + // Compute the field strength terms mu>nu + double t2 = usecond(); + WilsonLoops<Impl>::FieldStrength(Bx, _Umu, Zdir, Ydir); + WilsonLoops<Impl>::FieldStrength(By, _Umu, Zdir, Xdir); + WilsonLoops<Impl>::FieldStrength(Bz, _Umu, Ydir, Xdir); + WilsonLoops<Impl>::FieldStrength(Ex, _Umu, Tdir, Xdir); + WilsonLoops<Impl>::FieldStrength(Ey, _Umu, Tdir, Ydir); + WilsonLoops<Impl>::FieldStrength(Ez, _Umu, Tdir, Zdir); + + // Compute the Clover Operator acting on Colour and Spin + // multiply here by the clover coefficients for the anisotropy + double t3 = usecond(); + TmpOriginal = Helpers::fillCloverYZ(Bx) * csw_r; + TmpOriginal += Helpers::fillCloverXZ(By) * csw_r; + TmpOriginal += Helpers::fillCloverXY(Bz) * csw_r; + TmpOriginal += Helpers::fillCloverXT(Ex) * csw_t; + TmpOriginal += Helpers::fillCloverYT(Ey) * csw_t; + TmpOriginal += Helpers::fillCloverZT(Ez) * csw_t; + TmpOriginal += this->diag_mass; + + // Convert the data layout of the clover term + double t4 = usecond(); + CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); + + // Possible modify the boundary values + double t5 = usecond(); + if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); + + // Invert the clover term in the improved layout + double t6 = usecond(); + CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); + + // Fill the remaining clover fields + double t7 = usecond(); + pickCheckerboard(Even, DiagonalEven, Diagonal); + pickCheckerboard(Even, TriangleEven, Triangle); + pickCheckerboard(Odd, DiagonalOdd, Diagonal); + pickCheckerboard(Odd, TriangleOdd, Triangle); + pickCheckerboard(Even, DiagonalInvEven, DiagonalInv); + pickCheckerboard(Even, TriangleInvEven, TriangleInv); + pickCheckerboard(Odd, DiagonalInvOdd, DiagonalInv); + pickCheckerboard(Odd, TriangleInvOdd, TriangleInv); + + // Report timings + double t8 = usecond(); +#if 0 + std::cout << GridLogMessage << "CompactWilsonCloverFermion::ImportGauge timings:" + << " WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 + << ", allocations = " << (t2 - t1) / 1e6 + << ", field strength = " << (t3 - t2) / 1e6 + << ", fill clover = " << (t4 - t3) / 1e6 + << ", convert = " << (t5 - t4) / 1e6 + << ", boundaries = " << (t6 - t5) / 1e6 + << ", inversions = " << (t7 - t6) / 1e6 + << ", pick cbs = " << (t8 - t7) / 1e6 + << ", total = " << (t8 - t0) / 1e6 + << std::endl; +#endif +} + +NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master b/Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master new file mode 100644 index 00000000..7c91c252 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master @@ -0,0 +1,41 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/ qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master + + Copyright (C) 2017 - 2022 + + Author: paboyle <paboyle@ph.ed.ac.uk> + Author: Guido Cossu <guido.cossu@ed.ac.uk> + Author: Daniel Richtmann <daniel.richtmann@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + +#include <Grid/Grid.h> +#include <Grid/qcd/spin/Dirac.h> +#include <Grid/qcd/action/fermion/CompactWilsonCloverFermion.h> +#include <Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h> + +NAMESPACE_BEGIN(Grid); + +#include "impl.h" +template class CompactWilsonCloverFermion<IMPLEMENTATION>; + +NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplD/CompactWilsonCloverFermionInstantiationWilsonImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplD/CompactWilsonCloverFermionInstantiationWilsonImplD.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonImplD/CompactWilsonCloverFermionInstantiationWilsonImplD.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplF/CompactWilsonCloverFermionInstantiationWilsonImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplF/CompactWilsonCloverFermionInstantiationWilsonImplF.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonImplF/CompactWilsonCloverFermionInstantiationWilsonImplF.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc new file mode 120000 index 00000000..a739bb99 --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc @@ -0,0 +1 @@ +../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh b/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh index d7553cdb..a09fd420 100755 --- a/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh +++ b/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh @@ -40,7 +40,7 @@ EOF done -CC_LIST="WilsonCloverFermionInstantiation WilsonFermionInstantiation WilsonKernelsInstantiation WilsonTMFermionInstantiation" +CC_LIST="WilsonCloverFermionInstantiation CompactWilsonCloverFermionInstantiation WilsonFermionInstantiation WilsonKernelsInstantiation WilsonTMFermionInstantiation" for impl in $WILSON_IMPL_LIST do From 86f4e179287d6b2fcf03cd19afcb213b8c959066 Mon Sep 17 00:00:00 2001 From: Julio Maia <jmaia@amd.com> Date: Mon, 7 Feb 2022 11:29:37 -0600 Subject: [PATCH 282/399] Changing thread block order and adding launch_bounds --- Grid/threads/Accelerator.h | 40 +++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 5991db26..b427b304 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -342,7 +342,7 @@ extern hipStream_t copyStream; /*These routines define mapping from thread grid to loop & vector lane indexing */ accelerator_inline int acceleratorSIMTlane(int Nsimd) { #ifdef GRID_SIMT - return hipThreadIdx_z; + return hipThreadIdx_x; #else return 0; #endif @@ -356,19 +356,41 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { { __VA_ARGS__;} \ }; \ int nt=acceleratorThreads(); \ - dim3 hip_threads(nt,1,nsimd); \ - dim3 hip_blocks ((num1+nt-1)/nt,num2,1); \ - hipLaunchKernelGGL(LambdaApply,hip_blocks,hip_threads, \ - 0,0, \ - num1,num2,nsimd,lambda); \ + dim3 hip_threads(nsimd, nt, 1); \ + dim3 hip_blocks ((num1+nt-1)/nt,num2,1); \ + if(hip_threads.x * hip_threads.y * hip_threads.z <= 64){ \ + hipLaunchKernelGGL(LambdaApply64,hip_blocks,hip_threads, \ + 0,0, \ + num1,num2,nsimd, lambda); \ + } else { \ + hipLaunchKernelGGL(LambdaApply,hip_blocks,hip_threads, \ + 0,0, \ + num1,num2,nsimd, lambda); \ + } \ } + template<typename lambda> __global__ +__launch_bounds__(64,1) +void LambdaApply64(uint64_t numx, uint64_t numy, uint64_t numz, lambda Lambda) +{ + // Following the same scheme as CUDA for now + uint64_t x = threadIdx.y + blockDim.y*blockIdx.x; + uint64_t y = threadIdx.z + blockDim.z*blockIdx.y; + uint64_t z = threadIdx.x; + if ( (x < numx) && (y<numy) && (z<numz) ) { + Lambda(x,y,z); + } +} + +template<typename lambda> __global__ +__launch_bounds__(1024,1) void LambdaApply(uint64_t numx, uint64_t numy, uint64_t numz, lambda Lambda) { - uint64_t x = hipThreadIdx_x + hipBlockDim_x*hipBlockIdx_x; - uint64_t y = hipThreadIdx_y + hipBlockDim_y*hipBlockIdx_y; - uint64_t z = hipThreadIdx_z ;//+ hipBlockDim_z*hipBlockIdx_z; + // Following the same scheme as CUDA for now + uint64_t x = threadIdx.y + blockDim.y*blockIdx.x; + uint64_t y = threadIdx.z + blockDim.z*blockIdx.y; + uint64_t z = threadIdx.x; if ( (x < numx) && (y<numy) && (z<numz) ) { Lambda(x,y,z); } From c32242058094b74b312bbebadb3f65cf481a9dda Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 14 Feb 2022 16:04:08 +0000 Subject: [PATCH 283/399] Dont instantiate an Nc=3 and non-GP hardwired code for other implementations --- .../CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc | 1 - .../CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc | 1 - .../CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc | 1 - .../CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc | 1 - ...CloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc | 1 - ...CloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc | 1 - ...lsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc | 1 - ...lsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc | 1 - 8 files changed, 8 deletions(-) delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc delete mode 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplD/CompactWilsonCloverFermionInstantiationGparityWilsonImplD.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc b/Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/GparityWilsonImplF/CompactWilsonCloverFermionInstantiationGparityWilsonImplF.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/CompactWilsonCloverFermionInstantiationWilsonAdjImplD.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/CompactWilsonCloverFermionInstantiationWilsonAdjImplF.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplD.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexAntiSymmetricImplF.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplD.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc deleted file mode 120000 index a739bb99..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/CompactWilsonCloverFermionInstantiationWilsonTwoIndexSymmetricImplF.cc +++ /dev/null @@ -1 +0,0 @@ -../CompactWilsonCloverFermionInstantiation.cc.master \ No newline at end of file From a3b022d469f087943be9c46c12169bcec0b5ce58 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 14 Feb 2022 15:09:08 -0500 Subject: [PATCH 284/399] Crusher compile --- systems/Crusher/config-command | 12 ++++++++++++ systems/Crusher/dwf.slurm | 26 ++++++++++++++++++++++++++ systems/Crusher/dwf4.slurm | 26 ++++++++++++++++++++++++++ systems/Crusher/dwf8.slurm | 27 +++++++++++++++++++++++++++ systems/Crusher/mpiwrapper.sh | 12 ++++++++++++ systems/Crusher/sourceme.sh | 5 +++++ 6 files changed, 108 insertions(+) create mode 100644 systems/Crusher/config-command create mode 100644 systems/Crusher/dwf.slurm create mode 100644 systems/Crusher/dwf4.slurm create mode 100644 systems/Crusher/dwf8.slurm create mode 100755 systems/Crusher/mpiwrapper.sh create mode 100644 systems/Crusher/sourceme.sh diff --git a/systems/Crusher/config-command b/systems/Crusher/config-command new file mode 100644 index 00000000..564dbc96 --- /dev/null +++ b/systems/Crusher/config-command @@ -0,0 +1,12 @@ +../../configure --enable-comms=mpi-auto \ +--enable-unified=no \ +--enable-shm=nvlink \ +--enable-accelerator=hip \ +--enable-gen-simd-width=64 \ +--enable-simd=GPU \ +--disable-fermion-reps \ +--disable-gparity \ +CXX=hipcc MPICXX=mpicxx \ +CXXFLAGS="-fPIC -I/opt/rocm-4.5.0/include/ -std=c++14 -I${MPICH_DIR}/include " \ +--prefix=/ccs/home/chulwoo/Grid \ + LDFLAGS=" -L${MPICH_DIR}/lib -lmpi -L${CRAY_MPICH_ROOTDIR}/gtl/lib -lmpi_gtl_hsa " diff --git a/systems/Crusher/dwf.slurm b/systems/Crusher/dwf.slurm new file mode 100644 index 00000000..7144a270 --- /dev/null +++ b/systems/Crusher/dwf.slurm @@ -0,0 +1,26 @@ +#!/bin/bash +# Begin LSF Directives +#SBATCH -A LGT104 +#SBATCH -t 01:00:00 +##SBATCH -U openmpThu +#SBATCH -p ecp +#SBATCH -J DWF +#SBATCH -o DWF.%J +#SBATCH -e DWF.%J +#SBATCH -N 1 +#SBATCH -n 1 + +DIR=. +module list +export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +export MPICH_GPU_SUPPORT_ENABLED=1 +#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +#export MPICH_SMP_SINGLE_COPY_MODE=NONE +export MPICH_SMP_SINGLE_COPY_MODE=CMA +export OMP_NUM_THREADS=8 + +AT=8 +echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE +PARAMS=" --accelerator-threads ${AT} --grid 32.32.32.32 --mpi 1.1.1.1 --comms-overlap" +srun -n1 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + diff --git a/systems/Crusher/dwf4.slurm b/systems/Crusher/dwf4.slurm new file mode 100644 index 00000000..9aebb480 --- /dev/null +++ b/systems/Crusher/dwf4.slurm @@ -0,0 +1,26 @@ +#!/bin/bash +# Begin LSF Directives +#SBATCH -A LGT104 +#SBATCH -t 01:00:00 +##SBATCH -U openmpThu +#SBATCH -p ecp +#SBATCH -J DWF +#SBATCH -o DWF.%J +#SBATCH -e DWF.%J +#SBATCH -N 1 +#SBATCH -n 4 + +DIR=. +module list +export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +export MPICH_GPU_SUPPORT_ENABLED=1 +#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +export MPICH_SMP_SINGLE_COPY_MODE=NONE +#export MPICH_SMP_SINGLE_COPY_MODE=CMA +export OMP_NUM_THREADS=4 + +AT=8 +echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE +PARAMS=" --accelerator-threads ${AT} --grid 32.32.64.64 --mpi 1.1.2.2 --comms-overlap --shm 2048 --shm-mpi 0" +srun -n4 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + diff --git a/systems/Crusher/dwf8.slurm b/systems/Crusher/dwf8.slurm new file mode 100644 index 00000000..4bf22d16 --- /dev/null +++ b/systems/Crusher/dwf8.slurm @@ -0,0 +1,27 @@ +#!/bin/bash +# Begin LSF Directives +#SBATCH -A LGT104 +#SBATCH -t 00:20:00 +#SBATCH -p ecp +#SBATCH -J DWF +#SBATCH -o DWF.%J +#SBATCH -e DWF.%J +#SBATCH -N 1 +#SBATCH -n 8 + + +DIR=. +module list +export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +export MPICH_GPU_SUPPORT_ENABLED=1 +#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +export MPICH_SMP_SINGLE_COPY_MODE=NONE +#export MPICH_SMP_SINGLE_COPY_MODE=CMA +export OMP_NUM_THREADS=1 + +AT=8 +echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE +PARAMS=" --accelerator-threads ${AT} --grid 32.64.64.64 --mpi 1.2.2.2 --comms-overlap --shm 2048 --shm-mpi 0" + +srun -n8 -c 1 --gpus-per-task=1 --gpu-bind=closest --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + diff --git a/systems/Crusher/mpiwrapper.sh b/systems/Crusher/mpiwrapper.sh new file mode 100755 index 00000000..76c4e364 --- /dev/null +++ b/systems/Crusher/mpiwrapper.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +lrank=$SLURM_LOCALID + +export ROCR_VISIBLE_DEVICES=$SLURM_LOCALID + +echo "`hostname` - $lrank device=$ROCR_VISIBLE_DEVICES binding=$BINDING" + +$* + + + diff --git a/systems/Crusher/sourceme.sh b/systems/Crusher/sourceme.sh new file mode 100644 index 00000000..72a2ff4e --- /dev/null +++ b/systems/Crusher/sourceme.sh @@ -0,0 +1,5 @@ +module load PrgEnv-gnu +module load rocm/4.5.0 +module load gmp +module load cray-fftw +module load craype-accel-amd-gfx908 From f49d5c2d22dabe503407a16d1d32e6d68079e3e3 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 14 Feb 2022 17:55:16 -0500 Subject: [PATCH 285/399] Updated scripts for crusher --- systems/Crusher/config-command | 2 +- systems/Crusher/dwf.slurm | 18 +++++++++++------- systems/Crusher/dwf4.slurm | 9 +++++---- systems/Crusher/dwf8.slurm | 12 ++++++------ systems/Crusher/sourceme.sh | 2 +- 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/systems/Crusher/config-command b/systems/Crusher/config-command index 564dbc96..90737808 100644 --- a/systems/Crusher/config-command +++ b/systems/Crusher/config-command @@ -8,5 +8,5 @@ --disable-gparity \ CXX=hipcc MPICXX=mpicxx \ CXXFLAGS="-fPIC -I/opt/rocm-4.5.0/include/ -std=c++14 -I${MPICH_DIR}/include " \ ---prefix=/ccs/home/chulwoo/Grid \ LDFLAGS=" -L${MPICH_DIR}/lib -lmpi -L${CRAY_MPICH_ROOTDIR}/gtl/lib -lmpi_gtl_hsa " +HIPFLAGS = --amdgpu-target=gfx90a diff --git a/systems/Crusher/dwf.slurm b/systems/Crusher/dwf.slurm index 7144a270..286615ef 100644 --- a/systems/Crusher/dwf.slurm +++ b/systems/Crusher/dwf.slurm @@ -3,24 +3,28 @@ #SBATCH -A LGT104 #SBATCH -t 01:00:00 ##SBATCH -U openmpThu -#SBATCH -p ecp +##SBATCH -p ecp #SBATCH -J DWF #SBATCH -o DWF.%J #SBATCH -e DWF.%J #SBATCH -N 1 #SBATCH -n 1 +#SBATCH --exclusive DIR=. module list -export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 +#export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 export MPICH_GPU_SUPPORT_ENABLED=1 -#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +export MPICH_SMP_SINGLE_COPY_MODE=XPMEM #export MPICH_SMP_SINGLE_COPY_MODE=NONE -export MPICH_SMP_SINGLE_COPY_MODE=CMA -export OMP_NUM_THREADS=8 +#export MPICH_SMP_SINGLE_COPY_MODE=CMA +export OMP_NUM_THREADS=1 AT=8 echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE -PARAMS=" --accelerator-threads ${AT} --grid 32.32.32.32 --mpi 1.1.1.1 --comms-overlap" -srun -n1 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + +PARAMS=" --accelerator-threads ${AT} --grid 24.24.24.24 --shm-mpi 0 --mpi 1.1.1.1" + +srun --gpus-per-task 1 -n1 ./benchmarks/Benchmark_dwf_fp32 $PARAMS + diff --git a/systems/Crusher/dwf4.slurm b/systems/Crusher/dwf4.slurm index 9aebb480..6bb953c4 100644 --- a/systems/Crusher/dwf4.slurm +++ b/systems/Crusher/dwf4.slurm @@ -3,12 +3,12 @@ #SBATCH -A LGT104 #SBATCH -t 01:00:00 ##SBATCH -U openmpThu -#SBATCH -p ecp #SBATCH -J DWF #SBATCH -o DWF.%J #SBATCH -e DWF.%J #SBATCH -N 1 #SBATCH -n 4 +#SBATCH --exclusive DIR=. module list @@ -19,8 +19,9 @@ export MPICH_SMP_SINGLE_COPY_MODE=NONE #export MPICH_SMP_SINGLE_COPY_MODE=CMA export OMP_NUM_THREADS=4 -AT=8 echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE -PARAMS=" --accelerator-threads ${AT} --grid 32.32.64.64 --mpi 1.1.2.2 --comms-overlap --shm 2048 --shm-mpi 0" -srun -n4 --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS +PARAMS=" --accelerator-threads 8 --grid 32.32.64.64 --mpi 1.1.2.2 --comms-overlap --shm 2048 --shm-mpi 0" + +srun --gpus-per-task 1 -n4 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS + diff --git a/systems/Crusher/dwf8.slurm b/systems/Crusher/dwf8.slurm index 4bf22d16..58686636 100644 --- a/systems/Crusher/dwf8.slurm +++ b/systems/Crusher/dwf8.slurm @@ -1,14 +1,14 @@ #!/bin/bash # Begin LSF Directives #SBATCH -A LGT104 -#SBATCH -t 00:20:00 -#SBATCH -p ecp +#SBATCH -t 01:00:00 +##SBATCH -U openmpThu #SBATCH -J DWF #SBATCH -o DWF.%J #SBATCH -e DWF.%J #SBATCH -N 1 #SBATCH -n 8 - +#SBATCH --exclusive DIR=. module list @@ -19,9 +19,9 @@ export MPICH_SMP_SINGLE_COPY_MODE=NONE #export MPICH_SMP_SINGLE_COPY_MODE=CMA export OMP_NUM_THREADS=1 -AT=8 echo MPICH_SMP_SINGLE_COPY_MODE $MPICH_SMP_SINGLE_COPY_MODE -PARAMS=" --accelerator-threads ${AT} --grid 32.64.64.64 --mpi 1.2.2.2 --comms-overlap --shm 2048 --shm-mpi 0" +PARAMS=" --accelerator-threads 8 --grid 32.64.64.64 --mpi 1.2.2.2 --comms-overlap --shm 2048 --shm-mpi 0" + +srun --gpus-per-task 1 -n8 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS -srun -n8 -c 1 --gpus-per-task=1 --gpu-bind=closest --label -c$OMP_NUM_THREADS --gpus-per-task=1 ./mpiwrapper.sh ./benchmarks/Benchmark_dwf_fp32 $PARAMS diff --git a/systems/Crusher/sourceme.sh b/systems/Crusher/sourceme.sh index 72a2ff4e..3f400ca4 100644 --- a/systems/Crusher/sourceme.sh +++ b/systems/Crusher/sourceme.sh @@ -2,4 +2,4 @@ module load PrgEnv-gnu module load rocm/4.5.0 module load gmp module load cray-fftw -module load craype-accel-amd-gfx908 +module load craype-accel-amd-gfx90a From 0c1618197f7fcc0c5828c1d3aa8a2dc4e915e62e Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 15 Feb 2022 08:52:07 -0500 Subject: [PATCH 286/399] Faster intranode MPI works now --- systems/Crusher/dwf8.slurm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systems/Crusher/dwf8.slurm b/systems/Crusher/dwf8.slurm index 58686636..30e83fff 100644 --- a/systems/Crusher/dwf8.slurm +++ b/systems/Crusher/dwf8.slurm @@ -14,8 +14,8 @@ DIR=. module list export MPIR_CVAR_GPU_EAGER_DEVICE_MEM=0 export MPICH_GPU_SUPPORT_ENABLED=1 -#export MPICH_SMP_SINGLE_COPY_MODE=XPMEM -export MPICH_SMP_SINGLE_COPY_MODE=NONE +export MPICH_SMP_SINGLE_COPY_MODE=XPMEM +#export MPICH_SMP_SINGLE_COPY_MODE=NONE #export MPICH_SMP_SINGLE_COPY_MODE=CMA export OMP_NUM_THREADS=1 From e8c187b3233aa6a5a02e4a1a31a3e83d629b5f2b Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 15 Feb 2022 11:24:38 -0500 Subject: [PATCH 287/399] SyCL happier? --- Grid/qcd/action/fermion/WilsonCloverHelpers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonCloverHelpers.h b/Grid/qcd/action/fermion/WilsonCloverHelpers.h index 588525cc..60f19317 100644 --- a/Grid/qcd/action/fermion/WilsonCloverHelpers.h +++ b/Grid/qcd/action/fermion/WilsonCloverHelpers.h @@ -726,8 +726,8 @@ public: static strong_inline void ApplyBoundaryMask(Field& f, const Mask& m) { conformable(f, m); auto grid = f.Grid(); - const int Nsite = grid->oSites(); - const int Nsimd = grid->Nsimd(); + const uint32_t Nsite = grid->oSites(); + const uint32_t Nsimd = grid->Nsimd(); autoView(f_v, f, AcceleratorWrite); autoView(m_v, m, AcceleratorRead); // NOTE: this function cannot be 'private' since nvcc forbids this for kernels From 63dbaeefaa533717379811f4695a149a82e2d6ec Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 16 Feb 2022 14:01:43 +0000 Subject: [PATCH 288/399] Extra barrier prior to finalize just in case it fixes an issue on Tursa --- Grid/util/Init.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/Grid/util/Init.cc b/Grid/util/Init.cc index 6992129e..36854d9c 100644 --- a/Grid/util/Init.cc +++ b/Grid/util/Init.cc @@ -534,6 +534,7 @@ void Grid_init(int *argc,char ***argv) void Grid_finalize(void) { #if defined (GRID_COMMS_MPI) || defined (GRID_COMMS_MPI3) || defined (GRID_COMMS_MPIT) + MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); Grid_unquiesce_nodes(); #endif From 2851870d70ae40ad540f790433f798d6160aefdc Mon Sep 17 00:00:00 2001 From: Mattia Bruno <38449948+mbruno46@users.noreply.github.com> Date: Tue, 22 Feb 2022 00:05:43 +0100 Subject: [PATCH 289/399] expClover support via helpers template class --- Grid/qcd/action/fermion/CloverHelpers.h | 371 ++++++++++++++++++ .../fermion/CompactWilsonCloverFermion.h | 3 +- Grid/qcd/action/fermion/Fermion.h | 72 ++-- Grid/qcd/action/fermion/WilsonCloverFermion.h | 3 +- Grid/qcd/action/fermion/WilsonCloverHelpers.h | 2 + ...CompactWilsonCloverFermionImplementation.h | 114 +++--- .../WilsonCloverFermionImplementation.h | 138 +++---- ...WilsonCloverFermionInstantiation.cc.master | 5 +- ...WilsonCloverFermionInstantiation.cc.master | 7 +- 9 files changed, 562 insertions(+), 153 deletions(-) create mode 100644 Grid/qcd/action/fermion/CloverHelpers.h diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h new file mode 100644 index 00000000..432bdf3c --- /dev/null +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -0,0 +1,371 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/qcd/action/fermion/WilsonCloverFermionImplementation.h + + Copyright (C) 2017 - 2022 + + Author: paboyle <paboyle@ph.ed.ac.uk> + Author: Daniel Richtmann <daniel.richtmann@gmail.com> + Author: Mattia Bruno <mattia.bruno@cern.ch> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + +#pragma once + +#include <Grid/Grid.h> +#include <Grid/qcd/spin/Dirac.h> +#include <Grid/qcd/action/fermion/WilsonCloverHelpers.h> + +//////////////////////////////////////////// +// Standard Clover +// (4+m0) + csw * clover_term +// Exp Clover +// (4+m0) * exp(csw/(4+m0) clover_term) +// = (4+m0) + csw * clover_term + ... +//////////////////////////////////////////// + +NAMESPACE_BEGIN(Grid); + + +////////////////////////////////// +// Generic Standard Clover +////////////////////////////////// + +template<class Impl> +class CloverHelpers: public WilsonCloverHelpers<Impl> { +public: + + INHERIT_IMPL_TYPES(Impl); + INHERIT_CLOVER_TYPES(Impl); + + typedef WilsonCloverHelpers<Impl> Helpers; + + static void Instantiate(CloverField& CloverTerm, CloverField& CloverTermInv, RealD csw_t, RealD diag_mass) { + GridBase *grid = CloverTerm.Grid(); + CloverTerm += diag_mass; + + int lvol = grid->lSites(); + int DimRep = Impl::Dimension; + { + autoView(CTv,CloverTerm,CpuRead); + autoView(CTIv,CloverTermInv,CpuWrite); + thread_for(site, lvol, { + Coordinate lcoor; + grid->LocalIndexToLocalCoor(site, lcoor); + Eigen::MatrixXcd EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); + Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); + typename SiteClover::scalar_object Qx = Zero(), Qxinv = Zero(); + peekLocalSite(Qx, CTv, lcoor); + //if (csw!=0){ + for (int j = 0; j < Ns; j++) + for (int k = 0; k < Ns; k++) + for (int a = 0; a < DimRep; a++) + for (int b = 0; b < DimRep; b++){ + auto zz = Qx()(j, k)(a, b); + EigenCloverOp(a + j * DimRep, b + k * DimRep) = std::complex<double>(zz); + } + // if (site==0) std::cout << "site =" << site << "\n" << EigenCloverOp << std::endl; + + EigenInvCloverOp = EigenCloverOp.inverse(); + //std::cout << EigenInvCloverOp << std::endl; + for (int j = 0; j < Ns; j++) + for (int k = 0; k < Ns; k++) + for (int a = 0; a < DimRep; a++) + for (int b = 0; b < DimRep; b++) + Qxinv()(j, k)(a, b) = EigenInvCloverOp(a + j * DimRep, b + k * DimRep); + // if (site==0) std::cout << "site =" << site << "\n" << EigenInvCloverOp << std::endl; + // } + pokeLocalSite(Qxinv, CTIv, lcoor); + }); + } + } + + + static void CloverTermDerivative(GaugeField& clover_force, + const FermionField& X, + const FermionField& Y, + const std::vector<GaugeLinkField>& U, + RealD csw_t, RealD csw_r) { + GaugeLinkField force_mu(clover_force.Grid()), lambda(clover_force.Grid()); + PropagatorField Lambda(clover_force.Grid()); + + /////////////////////////////////////////////////////////// + // Clover term derivative + /////////////////////////////////////////////////////////// + Impl::outerProductImpl(Lambda, X, Y); + //std::cout << "Lambda:" << Lambda << std::endl; + + Gamma::Algebra sigma[] = { + Gamma::Algebra::SigmaXY, + Gamma::Algebra::SigmaXZ, + Gamma::Algebra::SigmaXT, + Gamma::Algebra::MinusSigmaXY, + Gamma::Algebra::SigmaYZ, + Gamma::Algebra::SigmaYT, + Gamma::Algebra::MinusSigmaXZ, + Gamma::Algebra::MinusSigmaYZ, + Gamma::Algebra::SigmaZT, + Gamma::Algebra::MinusSigmaXT, + Gamma::Algebra::MinusSigmaYT, + Gamma::Algebra::MinusSigmaZT}; + + /* + sigma_{\mu \nu}= + | 0 sigma[0] sigma[1] sigma[2] | + | sigma[3] 0 sigma[4] sigma[5] | + | sigma[6] sigma[7] 0 sigma[8] | + | sigma[9] sigma[10] sigma[11] 0 | + */ + + int count = 0; + clover_force = Zero(); + for (int mu = 0; mu < 4; mu++) + { + force_mu = Zero(); + for (int nu = 0; nu < 4; nu++) + { + if (mu == nu) + continue; + + RealD factor; + if (nu == 4 || mu == 4) + { + factor = 2.0 * csw_t; + } + else + { + factor = 2.0 * csw_r; + } + PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked + Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok + force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked + count++; + } + + pokeLorentz(clover_force, U[mu] * force_mu, mu); + } + } +}; + + +////////////////////////////////// +// Generic Exp Clover +////////////////////////////////// + +template<class Impl> +class ExpCloverHelpers: public WilsonCloverHelpers<Impl> { +public: + + INHERIT_IMPL_TYPES(Impl); + INHERIT_CLOVER_TYPES(Impl); + + template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; + typedef WilsonCloverHelpers<Impl> Helpers; + + static void plusIdentity(const CloverField& in) { + int DimRep = Impl::Dimension; + + autoView(in_v, in, AcceleratorWrite); + + accelerator_for(ss, in.Grid()->oSites(), 1, { + for (int sa=0; sa<Ns; sa++) + for (int ca=0; ca<DimRep; ca++) + in_v[ss]()(sa,sa)(ca,ca) += 1.0; + }); + } + + static int getNMAX(RealD prec, RealD R) { + /* compute stop condition for exponential */ + int NMAX=1; + RealD cond=R*R/2.; + + while (cond*std::exp(R)>prec) { + NMAX++; + cond*=R/(double)(NMAX+1); + } + return NMAX; + } + + static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);} + static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);} + + static void Instantiate(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { + GridBase* grid = Clover.Grid(); + CloverField ExpClover(grid); + + int NMAX = getNMAX(Clover, 3.*csw_t/diag_mass); + + // csw/(diag_mass) * clover + Clover *= (1.0/diag_mass); + + ExpClover = Clover; + plusIdentity(ExpClover); // 1 + Clover + + // Taylor expansion, slow but generic + RealD pref = 1.0; + for (int i=2; i<=NMAX; i++) { + Clover = Clover * Clover; + pref /= (RealD)(i); + ExpClover += pref * Clover; + } + + // Convert the data layout of the clover terms + Clover = ExpClover * diag_mass; + CloverInv = adj(ExpClover * (1.0/diag_mass)); + } + + static void CloverTermDerivative(GaugeField& clover_force, const FermionField& X, const FermionField& Y, + const std::vector<GaugeLinkField>& U, RealD csw_t, RealD csw_r) { + assert(0); + } +}; + + +////////////////////////////////// +// Compact Standard Clover +////////////////////////////////// + + +template<class Impl> +class CompactCloverHelpers: public CompactWilsonCloverHelpers<Impl>, + public WilsonCloverHelpers<Impl> { +public: + + INHERIT_IMPL_TYPES(Impl); + INHERIT_CLOVER_TYPES(Impl); + INHERIT_COMPACT_CLOVER_TYPES(Impl); + + typedef WilsonCloverHelpers<Impl> Helpers; + typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; + + static void MassTerm(CloverField& Clover, RealD diag_mass) { + Clover += diag_mass; + } + + static void Instantiate(CloverDiagonalField& Diagonal, + CloverTriangleField& Triangle, + CloverDiagonalField& DiagonalInv, + CloverTriangleField& TriangleInv, + RealD csw_t, RealD diag_mass) { + // Invert the clover term in the improved layout + CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); + } + + // TODO: implement Cmunu for better performances with compact layout, but don't do it + // here, but rather in WilsonCloverHelpers.h -> CompactWilsonCloverHelpers + static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { + return Helpers::Cmunu(U, lambda, mu, nu); + } +}; + +////////////////////////////////// +// Compact Exp Clover +////////////////////////////////// + + +template<class Impl> +class CompactExpCloverHelpers: public CompactWilsonCloverHelpers<Impl> { +public: + + INHERIT_IMPL_TYPES(Impl); + INHERIT_CLOVER_TYPES(Impl); + INHERIT_COMPACT_CLOVER_TYPES(Impl); + + template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; + typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; + + static void plusIdentity(const CloverField& in) { + int DimRep = Impl::Dimension; + + autoView(in_v, in, AcceleratorWrite); + + accelerator_for(ss, in.Grid()->oSites(), 1, { + for (int sa=0; sa<Ns; sa++) + for (int ca=0; ca<DimRep; ca++) + in_v[ss]()(sa,sa)(ca,ca) += 1.0; + }); + } + + static int getNMAX(RealD prec, RealD R) { + /* compute stop condition for exponential */ + int NMAX=1; + RealD cond=R*R/2.; + + while (cond*std::exp(R)>prec) { + NMAX++; + cond*=R/(double)(NMAX+1); + } + return NMAX; + } + + static int getNMAX(Lattice<iImplCloverDiagonal<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);} + static int getNMAX(Lattice<iImplCloverDiagonal<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);} + + static void MassTerm(CloverField& Clover, RealD diag_mass) { + // csw/(diag_mass) * clover + Clover = Clover * (1.0/diag_mass); + } + + static void Instantiate(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, + CloverDiagonalField& DiagonalInv, CloverTriangleField& TriangleInv, + RealD csw_t, RealD diag_mass) { + GridBase* grid = Diagonal.Grid(); + int NMAX = getNMAX(Diagonal, 3.*csw_t/diag_mass); + // To be optimized: too much memory traffic; implement exp in improved layout + // Felix + Fabian: replace code below with + // + // ConvertLayout Clover -> Diagonal, Triangle + // ModifyBoundaries + // EvaluateExp + + // code to be replaced + CloverField Clover(grid), ExpClover(grid); + + CompactHelpers::ConvertLayout(Diagonal, Triangle, Clover); + + ExpClover = Clover; + plusIdentity(ExpClover); // 1 + Clover + + RealD pref = 1.0; + for (int i=2; i<=NMAX; i++) { + Clover = Clover * Clover; + pref /= (RealD)(i); + ExpClover += pref * Clover; + } + + // Convert the data layout of the clover terms + CompactHelpers::ConvertLayout(ExpClover, Diagonal, Triangle); + Diagonal = Diagonal * diag_mass; + Triangle = Triangle * diag_mass; + CompactHelpers::ConvertLayout(adj(ExpClover), DiagonalInv, TriangleInv); + DiagonalInv = DiagonalInv*(1.0/diag_mass); + TriangleInv = TriangleInv*(1.0/diag_mass); + } + + + static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { + assert(0); + } + +}; + + +NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h index 3a166134..249b20bd 100644 --- a/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h +++ b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h @@ -31,6 +31,7 @@ #include <Grid/qcd/action/fermion/WilsonCloverTypes.h> #include <Grid/qcd/action/fermion/WilsonCloverHelpers.h> +#include <Grid/qcd/action/fermion/CloverHelpers.h> NAMESPACE_BEGIN(Grid); @@ -85,7 +86,7 @@ NAMESPACE_BEGIN(Grid); // + (2 * 1 + 4 * 1/2) triangle parts = 4 triangle parts = 60 complex words per site // = 84 complex words per site -template<class Impl> +template<class Impl, class CloverHelpers> class CompactWilsonCloverFermion : public WilsonFermion<Impl>, public WilsonCloverHelpers<Impl>, public CompactWilsonCloverHelpers<Impl> { diff --git a/Grid/qcd/action/fermion/Fermion.h b/Grid/qcd/action/fermion/Fermion.h index 78cf7851..bfbc16b8 100644 --- a/Grid/qcd/action/fermion/Fermion.h +++ b/Grid/qcd/action/fermion/Fermion.h @@ -138,38 +138,62 @@ typedef WilsonTMFermion<WilsonImplF> WilsonTMFermionF; typedef WilsonTMFermion<WilsonImplD> WilsonTMFermionD; // Clover fermions -typedef WilsonCloverFermion<WilsonImplR> WilsonCloverFermionR; -typedef WilsonCloverFermion<WilsonImplF> WilsonCloverFermionF; -typedef WilsonCloverFermion<WilsonImplD> WilsonCloverFermionD; +typedef CloverHelpers<WilsonImplR> CloverR; +typedef CloverHelpers<WilsonImplF> CloverF; +typedef CloverHelpers<WilsonImplD> CloverD; -typedef WilsonCloverFermion<WilsonAdjImplR> WilsonCloverAdjFermionR; -typedef WilsonCloverFermion<WilsonAdjImplF> WilsonCloverAdjFermionF; -typedef WilsonCloverFermion<WilsonAdjImplD> WilsonCloverAdjFermionD; +typedef ExpCloverHelpers<WilsonImplR> ExpCloverR; +typedef ExpCloverHelpers<WilsonImplF> ExpCloverF; +typedef ExpCloverHelpers<WilsonImplD> ExpCloverD; -typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplR> WilsonCloverTwoIndexSymmetricFermionR; -typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplF> WilsonCloverTwoIndexSymmetricFermionF; -typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplD> WilsonCloverTwoIndexSymmetricFermionD; +typedef WilsonCloverFermion<WilsonImplR, CloverR> WilsonCloverFermionR; +typedef WilsonCloverFermion<WilsonImplF, CloverF> WilsonCloverFermionF; +typedef WilsonCloverFermion<WilsonImplD, CloverD> WilsonCloverFermionD; -typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR> WilsonCloverTwoIndexAntiSymmetricFermionR; -typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF> WilsonCloverTwoIndexAntiSymmetricFermionF; -typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD> WilsonCloverTwoIndexAntiSymmetricFermionD; +typedef WilsonCloverFermion<WilsonImplR, ExpCloverR> WilsonExpCloverFermionR; +typedef WilsonCloverFermion<WilsonImplF, ExpCloverF> WilsonExpCloverFermionF; +typedef WilsonCloverFermion<WilsonImplD, ExpCloverD> WilsonExpCloverFermionD; + +typedef WilsonCloverFermion<WilsonAdjImplR, CloverR> WilsonCloverAdjFermionR; +typedef WilsonCloverFermion<WilsonAdjImplF, CloverF> WilsonCloverAdjFermionF; +typedef WilsonCloverFermion<WilsonAdjImplD, CloverD> WilsonCloverAdjFermionD; + +typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplR, CloverR> WilsonCloverTwoIndexSymmetricFermionR; +typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplF, CloverF> WilsonCloverTwoIndexSymmetricFermionF; +typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplD, CloverD> WilsonCloverTwoIndexSymmetricFermionD; + +typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR, CloverR> WilsonCloverTwoIndexAntiSymmetricFermionR; +typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF, CloverF> WilsonCloverTwoIndexAntiSymmetricFermionF; +typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD, CloverD> WilsonCloverTwoIndexAntiSymmetricFermionD; // Compact Clover fermions -typedef CompactWilsonCloverFermion<WilsonImplR> CompactWilsonCloverFermionR; -typedef CompactWilsonCloverFermion<WilsonImplF> CompactWilsonCloverFermionF; -typedef CompactWilsonCloverFermion<WilsonImplD> CompactWilsonCloverFermionD; +typedef CompactCloverHelpers<WilsonImplR> CompactCloverR; +typedef CompactCloverHelpers<WilsonImplF> CompactCloverF; +typedef CompactCloverHelpers<WilsonImplD> CompactCloverD; -typedef CompactWilsonCloverFermion<WilsonAdjImplR> CompactWilsonCloverAdjFermionR; -typedef CompactWilsonCloverFermion<WilsonAdjImplF> CompactWilsonCloverAdjFermionF; -typedef CompactWilsonCloverFermion<WilsonAdjImplD> CompactWilsonCloverAdjFermionD; +typedef CompactExpCloverHelpers<WilsonImplR> CompactExpCloverR; +typedef CompactExpCloverHelpers<WilsonImplF> CompactExpCloverF; +typedef CompactExpCloverHelpers<WilsonImplD> CompactExpCloverD; -typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplR> CompactWilsonCloverTwoIndexSymmetricFermionR; -typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplF> CompactWilsonCloverTwoIndexSymmetricFermionF; -typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplD> CompactWilsonCloverTwoIndexSymmetricFermionD; +typedef CompactWilsonCloverFermion<WilsonImplR, CompactCloverR> CompactWilsonCloverFermionR; +typedef CompactWilsonCloverFermion<WilsonImplF, CompactCloverF> CompactWilsonCloverFermionF; +typedef CompactWilsonCloverFermion<WilsonImplD, CompactCloverD> CompactWilsonCloverFermionD; -typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR> CompactWilsonCloverTwoIndexAntiSymmetricFermionR; -typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF> CompactWilsonCloverTwoIndexAntiSymmetricFermionF; -typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD> CompactWilsonCloverTwoIndexAntiSymmetricFermionD; +typedef CompactWilsonCloverFermion<WilsonImplR, CompactExpCloverR> CompactWilsonExpCloverFermionR; +typedef CompactWilsonCloverFermion<WilsonImplF, CompactExpCloverF> CompactWilsonExpCloverFermionF; +typedef CompactWilsonCloverFermion<WilsonImplD, CompactExpCloverD> CompactWilsonExpCloverFermionD; + +typedef CompactWilsonCloverFermion<WilsonAdjImplR, CompactCloverR> CompactWilsonCloverAdjFermionR; +typedef CompactWilsonCloverFermion<WilsonAdjImplF, CompactCloverF> CompactWilsonCloverAdjFermionF; +typedef CompactWilsonCloverFermion<WilsonAdjImplD, CompactCloverD> CompactWilsonCloverAdjFermionD; + +typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplR, CompactCloverR> CompactWilsonCloverTwoIndexSymmetricFermionR; +typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplF, CompactCloverF> CompactWilsonCloverTwoIndexSymmetricFermionF; +typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplD, CompactCloverD> CompactWilsonCloverTwoIndexSymmetricFermionD; + +typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR, CompactCloverR> CompactWilsonCloverTwoIndexAntiSymmetricFermionR; +typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF, CompactCloverF> CompactWilsonCloverTwoIndexAntiSymmetricFermionF; +typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD, CompactCloverD> CompactWilsonCloverTwoIndexAntiSymmetricFermionD; // Domain Wall fermions typedef DomainWallFermion<WilsonImplR> DomainWallFermionR; diff --git a/Grid/qcd/action/fermion/WilsonCloverFermion.h b/Grid/qcd/action/fermion/WilsonCloverFermion.h index cd19fc10..562f08f4 100644 --- a/Grid/qcd/action/fermion/WilsonCloverFermion.h +++ b/Grid/qcd/action/fermion/WilsonCloverFermion.h @@ -32,6 +32,7 @@ #include <Grid/qcd/action/fermion/WilsonCloverTypes.h> #include <Grid/qcd/action/fermion/WilsonCloverHelpers.h> +#include <Grid/qcd/action/fermion/CloverHelpers.h> NAMESPACE_BEGIN(Grid); @@ -51,7 +52,7 @@ NAMESPACE_BEGIN(Grid); // csw_r = csw_t to recover the isotropic version ////////////////////////////////////////////////////////////////// -template <class Impl> +template<class Impl, class CloverHelpers> class WilsonCloverFermion : public WilsonFermion<Impl>, public WilsonCloverHelpers<Impl> { diff --git a/Grid/qcd/action/fermion/WilsonCloverHelpers.h b/Grid/qcd/action/fermion/WilsonCloverHelpers.h index 60f19317..c221d5f0 100644 --- a/Grid/qcd/action/fermion/WilsonCloverHelpers.h +++ b/Grid/qcd/action/fermion/WilsonCloverHelpers.h @@ -209,6 +209,8 @@ public: }; +//////////////////////////////////////////////////////// + template<class Impl> class CompactWilsonCloverHelpers { public: diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 3dfcb133..b42957ac 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -32,17 +32,18 @@ #include <Grid/qcd/spin/Dirac.h> #include <Grid/qcd/action/fermion/CompactWilsonCloverFermion.h> + NAMESPACE_BEGIN(Grid); -template<class Impl> -CompactWilsonCloverFermion<Impl>::CompactWilsonCloverFermion(GaugeField& _Umu, - GridCartesian& Fgrid, - GridRedBlackCartesian& Hgrid, - const RealD _mass, - const RealD _csw_r, - const RealD _csw_t, - const RealD _cF, - const WilsonAnisotropyCoefficients& clover_anisotropy, - const ImplParams& impl_p) +template<class Impl, class CloverHelpers> +CompactWilsonCloverFermion<Impl, CloverHelpers>::CompactWilsonCloverFermion(GaugeField& _Umu, + GridCartesian& Fgrid, + GridRedBlackCartesian& Hgrid, + const RealD _mass, + const RealD _csw_r, + const RealD _csw_t, + const RealD _cF, + const WilsonAnisotropyCoefficients& clover_anisotropy, + const ImplParams& impl_p) : WilsonBase(_Umu, Fgrid, Hgrid, _mass, impl_p, clover_anisotropy) , csw_r(_csw_r) , csw_t(_csw_t) @@ -68,40 +69,40 @@ CompactWilsonCloverFermion<Impl>::CompactWilsonCloverFermion(GaugeField& _Umu, CompactHelpers::SetupMasks(this->BoundaryMask, this->BoundaryMaskEven, this->BoundaryMaskOdd); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::Dhop(const FermionField& in, FermionField& out, int dag) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::Dhop(const FermionField& in, FermionField& out, int dag) { WilsonBase::Dhop(in, out, dag); if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::DhopOE(const FermionField& in, FermionField& out, int dag) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopOE(const FermionField& in, FermionField& out, int dag) { WilsonBase::DhopOE(in, out, dag); if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::DhopEO(const FermionField& in, FermionField& out, int dag) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopEO(const FermionField& in, FermionField& out, int dag) { WilsonBase::DhopEO(in, out, dag); if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::DhopDir(const FermionField& in, FermionField& out, int dir, int disp) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopDir(const FermionField& in, FermionField& out, int dir, int disp) { WilsonBase::DhopDir(in, out, dir, disp); if(this->open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::DhopDirAll(const FermionField& in, std::vector<FermionField>& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopDirAll(const FermionField& in, std::vector<FermionField>& out) { WilsonBase::DhopDirAll(in, out); if(this->open_boundaries) { for(auto& o : out) ApplyBoundaryMask(o); } } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::M(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::M(const FermionField& in, FermionField& out) { out.Checkerboard() = in.Checkerboard(); WilsonBase::Dhop(in, out, DaggerNo); // call base to save applying bc Mooee(in, Tmp); @@ -109,8 +110,8 @@ void CompactWilsonCloverFermion<Impl>::M(const FermionField& in, FermionField& o if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::Mdag(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::Mdag(const FermionField& in, FermionField& out) { out.Checkerboard() = in.Checkerboard(); WilsonBase::Dhop(in, out, DaggerYes); // call base to save applying bc MooeeDag(in, Tmp); @@ -118,20 +119,20 @@ void CompactWilsonCloverFermion<Impl>::Mdag(const FermionField& in, FermionField if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::Meooe(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::Meooe(const FermionField& in, FermionField& out) { WilsonBase::Meooe(in, out); if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MeooeDag(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MeooeDag(const FermionField& in, FermionField& out) { WilsonBase::MeooeDag(in, out); if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::Mooee(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::Mooee(const FermionField& in, FermionField& out) { if(in.Grid()->_isCheckerBoarded) { if(in.Checkerboard() == Odd) { MooeeInternal(in, out, DiagonalOdd, TriangleOdd); @@ -144,13 +145,13 @@ void CompactWilsonCloverFermion<Impl>::Mooee(const FermionField& in, FermionFiel if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MooeeDag(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MooeeDag(const FermionField& in, FermionField& out) { Mooee(in, out); // blocks are hermitian } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MooeeInv(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MooeeInv(const FermionField& in, FermionField& out) { if(in.Grid()->_isCheckerBoarded) { if(in.Checkerboard() == Odd) { MooeeInternal(in, out, DiagonalInvOdd, TriangleInvOdd); @@ -163,23 +164,23 @@ void CompactWilsonCloverFermion<Impl>::MooeeInv(const FermionField& in, FermionF if(open_boundaries) ApplyBoundaryMask(out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MooeeInvDag(const FermionField& in, FermionField& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MooeeInvDag(const FermionField& in, FermionField& out) { MooeeInv(in, out); // blocks are hermitian } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::Mdir(const FermionField& in, FermionField& out, int dir, int disp) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::Mdir(const FermionField& in, FermionField& out, int dir, int disp) { DhopDir(in, out, dir, disp); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MdirAll(const FermionField& in, std::vector<FermionField>& out) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MdirAll(const FermionField& in, std::vector<FermionField>& out) { DhopDirAll(in, out); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) { assert(!open_boundaries); // TODO check for changes required for open bc // NOTE: code copied from original clover term @@ -251,7 +252,7 @@ void CompactWilsonCloverFermion<Impl>::MDeriv(GaugeField& force, const FermionFi } PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok - force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked + force_mu -= factor*CloverHelpers::Cmunu(U, lambda, mu, nu); // checked count++; } @@ -261,18 +262,18 @@ void CompactWilsonCloverFermion<Impl>::MDeriv(GaugeField& force, const FermionFi force += clover_force; } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MooDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MooDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) { assert(0); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MeeDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MeeDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) { assert(0); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::MooeeInternal(const FermionField& in, +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::MooeeInternal(const FermionField& in, FermionField& out, const CloverDiagonalField& diagonal, const CloverTriangleField& triangle) { @@ -285,8 +286,8 @@ void CompactWilsonCloverFermion<Impl>::MooeeInternal(const FermionField& CompactHelpers::MooeeKernel(diagonal.oSites(), 1, in, out, diagonal, triangle); } -template<class Impl> -void CompactWilsonCloverFermion<Impl>::ImportGauge(const GaugeField& _Umu) { +template<class Impl, class CloverHelpers> +void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeField& _Umu) { // NOTE: parts copied from original implementation // Import gauge into base class @@ -318,8 +319,9 @@ void CompactWilsonCloverFermion<Impl>::ImportGauge(const GaugeField& _Umu) { TmpOriginal += Helpers::fillCloverXT(Ex) * csw_t; TmpOriginal += Helpers::fillCloverYT(Ey) * csw_t; TmpOriginal += Helpers::fillCloverZT(Ez) * csw_t; - TmpOriginal += this->diag_mass; - + // Handle mass term based on clover policy + CloverHelpers::MassTerm(TmpOriginal, this->diag_mass); + // Convert the data layout of the clover term double t4 = usecond(); CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); @@ -328,9 +330,9 @@ void CompactWilsonCloverFermion<Impl>::ImportGauge(const GaugeField& _Umu) { double t5 = usecond(); if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); - // Invert the clover term in the improved layout + // Instantiate based on clover policy double t6 = usecond(); - CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); + CloverHelpers::Instantiate(Diagonal, Triangle, DiagonalInv, TriangleInv, csw_t, this->diag_mass); // Fill the remaining clover fields double t7 = usecond(); @@ -353,7 +355,7 @@ void CompactWilsonCloverFermion<Impl>::ImportGauge(const GaugeField& _Umu) { << ", fill clover = " << (t4 - t3) / 1e6 << ", convert = " << (t5 - t4) / 1e6 << ", boundaries = " << (t6 - t5) / 1e6 - << ", inversions = " << (t7 - t6) / 1e6 + << ", instantiate = " << (t7 - t6) / 1e6 << ", pick cbs = " << (t8 - t7) / 1e6 << ", total = " << (t8 - t0) / 1e6 << std::endl; diff --git a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h index e4d2e736..2ebf45f8 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h @@ -34,8 +34,8 @@ NAMESPACE_BEGIN(Grid); -template<class Impl> -WilsonCloverFermion<Impl>::WilsonCloverFermion(GaugeField& _Umu, +template<class Impl, class CloverHelpers> +WilsonCloverFermion<Impl, CloverHelpers>::WilsonCloverFermion(GaugeField& _Umu, GridCartesian& Fgrid, GridRedBlackCartesian& Hgrid, const RealD _mass, @@ -74,8 +74,8 @@ WilsonCloverFermion<Impl>::WilsonCloverFermion(GaugeField& } // *NOT* EO -template <class Impl> -void WilsonCloverFermion<Impl>::M(const FermionField &in, FermionField &out) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::M(const FermionField &in, FermionField &out) { FermionField temp(out.Grid()); @@ -89,8 +89,8 @@ void WilsonCloverFermion<Impl>::M(const FermionField &in, FermionField &out) out += temp; } -template <class Impl> -void WilsonCloverFermion<Impl>::Mdag(const FermionField &in, FermionField &out) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::Mdag(const FermionField &in, FermionField &out) { FermionField temp(out.Grid()); @@ -104,8 +104,8 @@ void WilsonCloverFermion<Impl>::Mdag(const FermionField &in, FermionField &out) out += temp; } -template <class Impl> -void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeField &_Umu) { double t0 = usecond(); WilsonFermion<Impl>::ImportGauge(_Umu); @@ -131,47 +131,50 @@ void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) CloverTerm += Helpers::fillCloverXT(Ex) * csw_t; CloverTerm += Helpers::fillCloverYT(Ey) * csw_t; CloverTerm += Helpers::fillCloverZT(Ez) * csw_t; - CloverTerm += diag_mass; - + double t4 = usecond(); - int lvol = _Umu.Grid()->lSites(); - int DimRep = Impl::Dimension; + CloverHelpers::Instantiate(CloverTerm, CloverTermInv, csw_t, this->diag_mass); +// CloverTerm += diag_mass; +// +// double t4 = usecond(); +// int lvol = _Umu.Grid()->lSites(); +// int DimRep = Impl::Dimension; +// +// double t5 = usecond(); +// { +// autoView(CTv,CloverTerm,CpuRead); +// autoView(CTIv,CloverTermInv,CpuWrite); +// thread_for(site, lvol, { +// Coordinate lcoor; +// grid->LocalIndexToLocalCoor(site, lcoor); +// Eigen::MatrixXcd EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); +// Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); +// typename SiteClover::scalar_object Qx = Zero(), Qxinv = Zero(); +// peekLocalSite(Qx, CTv, lcoor); +// //if (csw!=0){ +// for (int j = 0; j < Ns; j++) +// for (int k = 0; k < Ns; k++) +// for (int a = 0; a < DimRep; a++) +// for (int b = 0; b < DimRep; b++){ +// auto zz = Qx()(j, k)(a, b); +// EigenCloverOp(a + j * DimRep, b + k * DimRep) = std::complex<double>(zz); +// } +// // if (site==0) std::cout << "site =" << site << "\n" << EigenCloverOp << std::endl; +// +// EigenInvCloverOp = EigenCloverOp.inverse(); +// //std::cout << EigenInvCloverOp << std::endl; +// for (int j = 0; j < Ns; j++) +// for (int k = 0; k < Ns; k++) +// for (int a = 0; a < DimRep; a++) +// for (int b = 0; b < DimRep; b++) +// Qxinv()(j, k)(a, b) = EigenInvCloverOp(a + j * DimRep, b + k * DimRep); +// // if (site==0) std::cout << "site =" << site << "\n" << EigenInvCloverOp << std::endl; +// // } +// pokeLocalSite(Qxinv, CTIv, lcoor); +// }); +// } double t5 = usecond(); - { - autoView(CTv,CloverTerm,CpuRead); - autoView(CTIv,CloverTermInv,CpuWrite); - thread_for(site, lvol, { - Coordinate lcoor; - grid->LocalIndexToLocalCoor(site, lcoor); - Eigen::MatrixXcd EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); - Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); - typename SiteClover::scalar_object Qx = Zero(), Qxinv = Zero(); - peekLocalSite(Qx, CTv, lcoor); - //if (csw!=0){ - for (int j = 0; j < Ns; j++) - for (int k = 0; k < Ns; k++) - for (int a = 0; a < DimRep; a++) - for (int b = 0; b < DimRep; b++){ - auto zz = Qx()(j, k)(a, b); - EigenCloverOp(a + j * DimRep, b + k * DimRep) = std::complex<double>(zz); - } - // if (site==0) std::cout << "site =" << site << "\n" << EigenCloverOp << std::endl; - - EigenInvCloverOp = EigenCloverOp.inverse(); - //std::cout << EigenInvCloverOp << std::endl; - for (int j = 0; j < Ns; j++) - for (int k = 0; k < Ns; k++) - for (int a = 0; a < DimRep; a++) - for (int b = 0; b < DimRep; b++) - Qxinv()(j, k)(a, b) = EigenInvCloverOp(a + j * DimRep, b + k * DimRep); - // if (site==0) std::cout << "site =" << site << "\n" << EigenInvCloverOp << std::endl; - // } - pokeLocalSite(Qxinv, CTIv, lcoor); - }); - } - - double t6 = usecond(); // Separate the even and odd parts pickCheckerboard(Even, CloverTermEven, CloverTerm); pickCheckerboard(Odd, CloverTermOdd, CloverTerm); @@ -184,7 +187,7 @@ void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) pickCheckerboard(Even, CloverTermInvDagEven, adj(CloverTermInv)); pickCheckerboard(Odd, CloverTermInvDagOdd, adj(CloverTermInv)); - double t7 = usecond(); + double t6 = usecond(); #if 0 std::cout << GridLogMessage << "WilsonCloverFermion::ImportGauge timings:" @@ -192,40 +195,39 @@ void WilsonCloverFermion<Impl>::ImportGauge(const GaugeField &_Umu) << ", allocations = " << (t2 - t1) / 1e6 << ", field strength = " << (t3 - t2) / 1e6 << ", fill clover = " << (t4 - t3) / 1e6 - << ", misc = " << (t5 - t4) / 1e6 - << ", inversions = " << (t6 - t5) / 1e6 - << ", pick cbs = " << (t7 - t6) / 1e6 - << ", total = " << (t7 - t0) / 1e6 + << ", instantiation = " << (t5 - t4) / 1e6 + << ", pick cbs = " << (t6 - t5) / 1e6 + << ", total = " << (t6 - t0) / 1e6 << std::endl; #endif } -template <class Impl> -void WilsonCloverFermion<Impl>::Mooee(const FermionField &in, FermionField &out) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::Mooee(const FermionField &in, FermionField &out) { this->MooeeInternal(in, out, DaggerNo, InverseNo); } -template <class Impl> -void WilsonCloverFermion<Impl>::MooeeDag(const FermionField &in, FermionField &out) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::MooeeDag(const FermionField &in, FermionField &out) { this->MooeeInternal(in, out, DaggerYes, InverseNo); } -template <class Impl> -void WilsonCloverFermion<Impl>::MooeeInv(const FermionField &in, FermionField &out) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::MooeeInv(const FermionField &in, FermionField &out) { this->MooeeInternal(in, out, DaggerNo, InverseYes); } -template <class Impl> -void WilsonCloverFermion<Impl>::MooeeInvDag(const FermionField &in, FermionField &out) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::MooeeInvDag(const FermionField &in, FermionField &out) { this->MooeeInternal(in, out, DaggerYes, InverseYes); } -template <class Impl> -void WilsonCloverFermion<Impl>::MooeeInternal(const FermionField &in, FermionField &out, int dag, int inv) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::MooeeInternal(const FermionField &in, FermionField &out, int dag, int inv) { out.Checkerboard() = in.Checkerboard(); CloverField *Clover; @@ -278,8 +280,8 @@ void WilsonCloverFermion<Impl>::MooeeInternal(const FermionField &in, FermionFie } // MooeeInternal // Derivative parts unpreconditioned pseudofermions -template <class Impl> -void WilsonCloverFermion<Impl>::MDeriv(GaugeField &force, const FermionField &X, const FermionField &Y, int dag) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::MDeriv(GaugeField &force, const FermionField &X, const FermionField &Y, int dag) { conformable(X.Grid(), Y.Grid()); conformable(X.Grid(), force.Grid()); @@ -349,7 +351,7 @@ void WilsonCloverFermion<Impl>::MDeriv(GaugeField &force, const FermionField &X, } PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok - force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked + force_mu -= factor*CloverHelpers::Cmunu(U, lambda, mu, nu); // checked count++; } @@ -360,15 +362,15 @@ void WilsonCloverFermion<Impl>::MDeriv(GaugeField &force, const FermionField &X, } // Derivative parts -template <class Impl> -void WilsonCloverFermion<Impl>::MooDeriv(GaugeField &mat, const FermionField &X, const FermionField &Y, int dag) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::MooDeriv(GaugeField &mat, const FermionField &X, const FermionField &Y, int dag) { assert(0); } // Derivative parts -template <class Impl> -void WilsonCloverFermion<Impl>::MeeDeriv(GaugeField &mat, const FermionField &U, const FermionField &V, int dag) +template<class Impl, class CloverHelpers> +void WilsonCloverFermion<Impl, CloverHelpers>::MeeDeriv(GaugeField &mat, const FermionField &U, const FermionField &V, int dag) { assert(0); // not implemented yet } diff --git a/Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master b/Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master index 7c91c252..1ea1549c 100644 --- a/Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master +++ b/Grid/qcd/action/fermion/instantiation/CompactWilsonCloverFermionInstantiation.cc.master @@ -9,6 +9,7 @@ Author: paboyle <paboyle@ph.ed.ac.uk> Author: Guido Cossu <guido.cossu@ed.ac.uk> Author: Daniel Richtmann <daniel.richtmann@gmail.com> + Author: Mattia Bruno <mattia.bruno@cern.ch> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,10 +33,12 @@ #include <Grid/qcd/spin/Dirac.h> #include <Grid/qcd/action/fermion/CompactWilsonCloverFermion.h> #include <Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h> +#include <Grid/qcd/action/fermion/CloverHelpers.h> NAMESPACE_BEGIN(Grid); #include "impl.h" -template class CompactWilsonCloverFermion<IMPLEMENTATION>; +template class CompactWilsonCloverFermion<IMPLEMENTATION, CompactCloverHelpers<IMPLEMENTATION>>; +template class CompactWilsonCloverFermion<IMPLEMENTATION, CompactExpCloverHelpers<IMPLEMENTATION>>; NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonCloverFermionInstantiation.cc.master b/Grid/qcd/action/fermion/instantiation/WilsonCloverFermionInstantiation.cc.master index af99dfb6..2cea3656 100644 --- a/Grid/qcd/action/fermion/instantiation/WilsonCloverFermionInstantiation.cc.master +++ b/Grid/qcd/action/fermion/instantiation/WilsonCloverFermionInstantiation.cc.master @@ -8,7 +8,8 @@ Author: paboyle <paboyle@ph.ed.ac.uk> Author: Guido Cossu <guido.cossu@ed.ac.uk> - + Author: Mattia Bruno <mattia.bruno@cern.ch> + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -31,10 +32,12 @@ #include <Grid/qcd/spin/Dirac.h> #include <Grid/qcd/action/fermion/WilsonCloverFermion.h> #include <Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h> +#include <Grid/qcd/action/fermion/CloverHelpers.h> NAMESPACE_BEGIN(Grid); #include "impl.h" -template class WilsonCloverFermion<IMPLEMENTATION>; +template class WilsonCloverFermion<IMPLEMENTATION, CloverHelpers<IMPLEMENTATION>>; +template class WilsonCloverFermion<IMPLEMENTATION, ExpCloverHelpers<IMPLEMENTATION>>; NAMESPACE_END(Grid); From 3d44aa9cb9ed629fc227822a252d5037d518a5ef Mon Sep 17 00:00:00 2001 From: Mattia Bruno <38449948+mbruno46@users.noreply.github.com> Date: Tue, 22 Feb 2022 01:10:19 +0100 Subject: [PATCH 290/399] cleaned up cloverhelpers; fixed test compact_clover which runs --- Grid/qcd/action/fermion/CloverHelpers.h | 85 +++---------------- .../WilsonCloverFermionImplementation.h | 39 --------- .../Test_compact_wilson_clover_speedup.cc | 4 +- 3 files changed, 12 insertions(+), 116 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index 432bdf3c..af712852 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -98,71 +98,10 @@ public: } } - - static void CloverTermDerivative(GaugeField& clover_force, - const FermionField& X, - const FermionField& Y, - const std::vector<GaugeLinkField>& U, - RealD csw_t, RealD csw_r) { - GaugeLinkField force_mu(clover_force.Grid()), lambda(clover_force.Grid()); - PropagatorField Lambda(clover_force.Grid()); - - /////////////////////////////////////////////////////////// - // Clover term derivative - /////////////////////////////////////////////////////////// - Impl::outerProductImpl(Lambda, X, Y); - //std::cout << "Lambda:" << Lambda << std::endl; - - Gamma::Algebra sigma[] = { - Gamma::Algebra::SigmaXY, - Gamma::Algebra::SigmaXZ, - Gamma::Algebra::SigmaXT, - Gamma::Algebra::MinusSigmaXY, - Gamma::Algebra::SigmaYZ, - Gamma::Algebra::SigmaYT, - Gamma::Algebra::MinusSigmaXZ, - Gamma::Algebra::MinusSigmaYZ, - Gamma::Algebra::SigmaZT, - Gamma::Algebra::MinusSigmaXT, - Gamma::Algebra::MinusSigmaYT, - Gamma::Algebra::MinusSigmaZT}; - - /* - sigma_{\mu \nu}= - | 0 sigma[0] sigma[1] sigma[2] | - | sigma[3] 0 sigma[4] sigma[5] | - | sigma[6] sigma[7] 0 sigma[8] | - | sigma[9] sigma[10] sigma[11] 0 | - */ - - int count = 0; - clover_force = Zero(); - for (int mu = 0; mu < 4; mu++) - { - force_mu = Zero(); - for (int nu = 0; nu < 4; nu++) - { - if (mu == nu) - continue; - - RealD factor; - if (nu == 4 || mu == 4) - { - factor = 2.0 * csw_t; - } - else - { - factor = 2.0 * csw_r; - } - PropagatorField Slambda = Gamma(sigma[count]) * Lambda; // sigma checked - Impl::TraceSpinImpl(lambda, Slambda); // traceSpin ok - force_mu -= factor*Helpers::Cmunu(U, lambda, mu, nu); // checked - count++; - } - - pokeLorentz(clover_force, U[mu] * force_mu, mu); - } + static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { + return Helpers::Cmunu(U, lambda, mu, nu); } + }; @@ -227,15 +166,14 @@ public: ExpClover += pref * Clover; } - // Convert the data layout of the clover terms Clover = ExpClover * diag_mass; CloverInv = adj(ExpClover * (1.0/diag_mass)); } - static void CloverTermDerivative(GaugeField& clover_force, const FermionField& X, const FermionField& Y, - const std::vector<GaugeLinkField>& U, RealD csw_t, RealD csw_r) { + static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { assert(0); } + }; @@ -329,14 +267,9 @@ public: RealD csw_t, RealD diag_mass) { GridBase* grid = Diagonal.Grid(); int NMAX = getNMAX(Diagonal, 3.*csw_t/diag_mass); - // To be optimized: too much memory traffic; implement exp in improved layout - // Felix + Fabian: replace code below with - // - // ConvertLayout Clover -> Diagonal, Triangle - // ModifyBoundaries - // EvaluateExp - // code to be replaced + // To be optimized: implement exp in improved layout + // START: code to be replaced CloverField Clover(grid), ExpClover(grid); CompactHelpers::ConvertLayout(Diagonal, Triangle, Clover); @@ -353,9 +286,11 @@ public: // Convert the data layout of the clover terms CompactHelpers::ConvertLayout(ExpClover, Diagonal, Triangle); + CompactHelpers::ConvertLayout(adj(ExpClover), DiagonalInv, TriangleInv); + // END: code to be replaced + Diagonal = Diagonal * diag_mass; Triangle = Triangle * diag_mass; - CompactHelpers::ConvertLayout(adj(ExpClover), DiagonalInv, TriangleInv); DiagonalInv = DiagonalInv*(1.0/diag_mass); TriangleInv = TriangleInv*(1.0/diag_mass); } diff --git a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h index 2ebf45f8..0991e274 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h @@ -134,45 +134,6 @@ void WilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeField &_Um double t4 = usecond(); CloverHelpers::Instantiate(CloverTerm, CloverTermInv, csw_t, this->diag_mass); -// CloverTerm += diag_mass; -// -// double t4 = usecond(); -// int lvol = _Umu.Grid()->lSites(); -// int DimRep = Impl::Dimension; -// -// double t5 = usecond(); -// { -// autoView(CTv,CloverTerm,CpuRead); -// autoView(CTIv,CloverTermInv,CpuWrite); -// thread_for(site, lvol, { -// Coordinate lcoor; -// grid->LocalIndexToLocalCoor(site, lcoor); -// Eigen::MatrixXcd EigenCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); -// Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); -// typename SiteClover::scalar_object Qx = Zero(), Qxinv = Zero(); -// peekLocalSite(Qx, CTv, lcoor); -// //if (csw!=0){ -// for (int j = 0; j < Ns; j++) -// for (int k = 0; k < Ns; k++) -// for (int a = 0; a < DimRep; a++) -// for (int b = 0; b < DimRep; b++){ -// auto zz = Qx()(j, k)(a, b); -// EigenCloverOp(a + j * DimRep, b + k * DimRep) = std::complex<double>(zz); -// } -// // if (site==0) std::cout << "site =" << site << "\n" << EigenCloverOp << std::endl; -// -// EigenInvCloverOp = EigenCloverOp.inverse(); -// //std::cout << EigenInvCloverOp << std::endl; -// for (int j = 0; j < Ns; j++) -// for (int k = 0; k < Ns; k++) -// for (int a = 0; a < DimRep; a++) -// for (int b = 0; b < DimRep; b++) -// Qxinv()(j, k)(a, b) = EigenInvCloverOp(a + j * DimRep, b + k * DimRep); -// // if (site==0) std::cout << "site =" << site << "\n" << EigenInvCloverOp << std::endl; -// // } -// pokeLocalSite(Qxinv, CTIv, lcoor); -// }); -// } double t5 = usecond(); // Separate the even and odd parts diff --git a/tests/core/Test_compact_wilson_clover_speedup.cc b/tests/core/Test_compact_wilson_clover_speedup.cc index 83e3da1f..7a74ab19 100644 --- a/tests/core/Test_compact_wilson_clover_speedup.cc +++ b/tests/core/Test_compact_wilson_clover_speedup.cc @@ -117,8 +117,8 @@ void runBenchmark(int* argc, char*** argv) { // type definitions typedef WilsonImpl<vCoeff_t, FundamentalRepresentation, CoeffReal> WImpl; - typedef WilsonCloverFermion<WImpl> WilsonCloverOperator; - typedef CompactWilsonCloverFermion<WImpl> CompactWilsonCloverOperator; + typedef WilsonCloverFermion<WImpl, CloverHelpers<WImpl>> WilsonCloverOperator; + typedef CompactWilsonCloverFermion<WImpl, CompactCloverHelpers<WImpl>> CompactWilsonCloverOperator; typedef typename WilsonCloverOperator::FermionField Fermion; typedef typename WilsonCloverOperator::GaugeField Gauge; From 11437930c56f86be3ee80d893a2d789d9c2f7c4f Mon Sep 17 00:00:00 2001 From: Mattia Bruno <38449948+mbruno46@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:45:16 +0100 Subject: [PATCH 291/399] cleaned up definitions of wilsonclover fermions --- Grid/qcd/action/fermion/Fermion.h | 78 ++++++++++++++----------------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/Grid/qcd/action/fermion/Fermion.h b/Grid/qcd/action/fermion/Fermion.h index bfbc16b8..223ff9dd 100644 --- a/Grid/qcd/action/fermion/Fermion.h +++ b/Grid/qcd/action/fermion/Fermion.h @@ -138,62 +138,52 @@ typedef WilsonTMFermion<WilsonImplF> WilsonTMFermionF; typedef WilsonTMFermion<WilsonImplD> WilsonTMFermionD; // Clover fermions -typedef CloverHelpers<WilsonImplR> CloverR; -typedef CloverHelpers<WilsonImplF> CloverF; -typedef CloverHelpers<WilsonImplD> CloverD; +template <typename WImpl> using WilsonClover = WilsonCloverFermion<WImpl, CloverHelpers<WImpl>>; +template <typename WImpl> using WilsonExpClover = WilsonCloverFermion<WImpl, ExpCloverHelpers<WImpl>>; -typedef ExpCloverHelpers<WilsonImplR> ExpCloverR; -typedef ExpCloverHelpers<WilsonImplF> ExpCloverF; -typedef ExpCloverHelpers<WilsonImplD> ExpCloverD; +typedef WilsonClover<WilsonImplR> WilsonCloverFermionR; +typedef WilsonClover<WilsonImplF> WilsonCloverFermionF; +typedef WilsonClover<WilsonImplD> WilsonCloverFermionD; -typedef WilsonCloverFermion<WilsonImplR, CloverR> WilsonCloverFermionR; -typedef WilsonCloverFermion<WilsonImplF, CloverF> WilsonCloverFermionF; -typedef WilsonCloverFermion<WilsonImplD, CloverD> WilsonCloverFermionD; +typedef WilsonExpClover<WilsonImplR> WilsonExpCloverFermionR; +typedef WilsonExpClover<WilsonImplF> WilsonExpCloverFermionF; +typedef WilsonExpClover<WilsonImplD> WilsonExpCloverFermionD; -typedef WilsonCloverFermion<WilsonImplR, ExpCloverR> WilsonExpCloverFermionR; -typedef WilsonCloverFermion<WilsonImplF, ExpCloverF> WilsonExpCloverFermionF; -typedef WilsonCloverFermion<WilsonImplD, ExpCloverD> WilsonExpCloverFermionD; +typedef WilsonClover<WilsonAdjImplR> WilsonCloverAdjFermionR; +typedef WilsonClover<WilsonAdjImplF> WilsonCloverAdjFermionF; +typedef WilsonClover<WilsonAdjImplD> WilsonCloverAdjFermionD; -typedef WilsonCloverFermion<WilsonAdjImplR, CloverR> WilsonCloverAdjFermionR; -typedef WilsonCloverFermion<WilsonAdjImplF, CloverF> WilsonCloverAdjFermionF; -typedef WilsonCloverFermion<WilsonAdjImplD, CloverD> WilsonCloverAdjFermionD; +typedef WilsonClover<WilsonTwoIndexSymmetricImplR> WilsonCloverTwoIndexSymmetricFermionR; +typedef WilsonClover<WilsonTwoIndexSymmetricImplF> WilsonCloverTwoIndexSymmetricFermionF; +typedef WilsonClover<WilsonTwoIndexSymmetricImplD> WilsonCloverTwoIndexSymmetricFermionD; -typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplR, CloverR> WilsonCloverTwoIndexSymmetricFermionR; -typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplF, CloverF> WilsonCloverTwoIndexSymmetricFermionF; -typedef WilsonCloverFermion<WilsonTwoIndexSymmetricImplD, CloverD> WilsonCloverTwoIndexSymmetricFermionD; - -typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR, CloverR> WilsonCloverTwoIndexAntiSymmetricFermionR; -typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF, CloverF> WilsonCloverTwoIndexAntiSymmetricFermionF; -typedef WilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD, CloverD> WilsonCloverTwoIndexAntiSymmetricFermionD; +typedef WilsonClover<WilsonTwoIndexAntiSymmetricImplR> WilsonCloverTwoIndexAntiSymmetricFermionR; +typedef WilsonClover<WilsonTwoIndexAntiSymmetricImplF> WilsonCloverTwoIndexAntiSymmetricFermionF; +typedef WilsonClover<WilsonTwoIndexAntiSymmetricImplD> WilsonCloverTwoIndexAntiSymmetricFermionD; // Compact Clover fermions -typedef CompactCloverHelpers<WilsonImplR> CompactCloverR; -typedef CompactCloverHelpers<WilsonImplF> CompactCloverF; -typedef CompactCloverHelpers<WilsonImplD> CompactCloverD; +template <typename WImpl> using CompactWilsonClover = CompactWilsonCloverFermion<WImpl, CompactCloverHelpers<WImpl>>; +template <typename WImpl> using CompactWilsonExpClover = CompactWilsonCloverFermion<WImpl, CompactExpCloverHelpers<WImpl>>; -typedef CompactExpCloverHelpers<WilsonImplR> CompactExpCloverR; -typedef CompactExpCloverHelpers<WilsonImplF> CompactExpCloverF; -typedef CompactExpCloverHelpers<WilsonImplD> CompactExpCloverD; +typedef CompactWilsonClover<WilsonImplR> CompactWilsonCloverFermionR; +typedef CompactWilsonClover<WilsonImplF> CompactWilsonCloverFermionF; +typedef CompactWilsonClover<WilsonImplD> CompactWilsonCloverFermionD; -typedef CompactWilsonCloverFermion<WilsonImplR, CompactCloverR> CompactWilsonCloverFermionR; -typedef CompactWilsonCloverFermion<WilsonImplF, CompactCloverF> CompactWilsonCloverFermionF; -typedef CompactWilsonCloverFermion<WilsonImplD, CompactCloverD> CompactWilsonCloverFermionD; +typedef CompactWilsonExpClover<WilsonImplR> CompactWilsonExpCloverFermionR; +typedef CompactWilsonExpClover<WilsonImplF> CompactWilsonExpCloverFermionF; +typedef CompactWilsonExpClover<WilsonImplD> CompactWilsonExpCloverFermionD; -typedef CompactWilsonCloverFermion<WilsonImplR, CompactExpCloverR> CompactWilsonExpCloverFermionR; -typedef CompactWilsonCloverFermion<WilsonImplF, CompactExpCloverF> CompactWilsonExpCloverFermionF; -typedef CompactWilsonCloverFermion<WilsonImplD, CompactExpCloverD> CompactWilsonExpCloverFermionD; +typedef CompactWilsonClover<WilsonAdjImplR> CompactWilsonCloverAdjFermionR; +typedef CompactWilsonClover<WilsonAdjImplF> CompactWilsonCloverAdjFermionF; +typedef CompactWilsonClover<WilsonAdjImplD> CompactWilsonCloverAdjFermionD; -typedef CompactWilsonCloverFermion<WilsonAdjImplR, CompactCloverR> CompactWilsonCloverAdjFermionR; -typedef CompactWilsonCloverFermion<WilsonAdjImplF, CompactCloverF> CompactWilsonCloverAdjFermionF; -typedef CompactWilsonCloverFermion<WilsonAdjImplD, CompactCloverD> CompactWilsonCloverAdjFermionD; +typedef CompactWilsonClover<WilsonTwoIndexSymmetricImplR> CompactWilsonCloverTwoIndexSymmetricFermionR; +typedef CompactWilsonClover<WilsonTwoIndexSymmetricImplF> CompactWilsonCloverTwoIndexSymmetricFermionF; +typedef CompactWilsonClover<WilsonTwoIndexSymmetricImplD> CompactWilsonCloverTwoIndexSymmetricFermionD; -typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplR, CompactCloverR> CompactWilsonCloverTwoIndexSymmetricFermionR; -typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplF, CompactCloverF> CompactWilsonCloverTwoIndexSymmetricFermionF; -typedef CompactWilsonCloverFermion<WilsonTwoIndexSymmetricImplD, CompactCloverD> CompactWilsonCloverTwoIndexSymmetricFermionD; - -typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplR, CompactCloverR> CompactWilsonCloverTwoIndexAntiSymmetricFermionR; -typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplF, CompactCloverF> CompactWilsonCloverTwoIndexAntiSymmetricFermionF; -typedef CompactWilsonCloverFermion<WilsonTwoIndexAntiSymmetricImplD, CompactCloverD> CompactWilsonCloverTwoIndexAntiSymmetricFermionD; +typedef CompactWilsonClover<WilsonTwoIndexAntiSymmetricImplR> CompactWilsonCloverTwoIndexAntiSymmetricFermionR; +typedef CompactWilsonClover<WilsonTwoIndexAntiSymmetricImplF> CompactWilsonCloverTwoIndexAntiSymmetricFermionF; +typedef CompactWilsonClover<WilsonTwoIndexAntiSymmetricImplD> CompactWilsonCloverTwoIndexAntiSymmetricFermionD; // Domain Wall fermions typedef DomainWallFermion<WilsonImplR> DomainWallFermionR; From 71034f828e76e066d51f67269ce7916c8f6c6cba Mon Sep 17 00:00:00 2001 From: Mattia Bruno <38449948+mbruno46@users.noreply.github.com> Date: Wed, 23 Feb 2022 01:02:27 +0100 Subject: [PATCH 292/399] attempt to fix broken WilsonExpClover; Compact version still broken will be replaced by F.Joswig --- Grid/qcd/action/fermion/CloverHelpers.h | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index af712852..7f7ad2b7 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -119,7 +119,8 @@ public: template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; typedef WilsonCloverHelpers<Impl> Helpers; - static void plusIdentity(const CloverField& in) { + // Can this be avoided? + static void IdentityTimesC(const CloverField& in, RealD c) { int DimRep = Impl::Dimension; autoView(in_v, in, AcceleratorWrite); @@ -127,7 +128,7 @@ public: accelerator_for(ss, in.Grid()->oSites(), 1, { for (int sa=0; sa<Ns; sa++) for (int ca=0; ca<DimRep; ca++) - in_v[ss]()(sa,sa)(ca,ca) += 1.0; + in_v[ss]()(sa,sa)(ca,ca) = c; }); } @@ -155,19 +156,22 @@ public: // csw/(diag_mass) * clover Clover *= (1.0/diag_mass); - ExpClover = Clover; - plusIdentity(ExpClover); // 1 + Clover - // Taylor expansion, slow but generic - RealD pref = 1.0; - for (int i=2; i<=NMAX; i++) { - Clover = Clover * Clover; - pref /= (RealD)(i); - ExpClover += pref * Clover; - } + // Horner scheme: a0 + a1 x + a2 x^2 + .. = a0 + x (a1 + x(...)) + // qN = cN + // qn = cn + qn+1 X + std::vector<RealD> cn(NMAX+1); + cn[0] = 1.0; + for (int i=1; i<=NMAX; i++) + cn[i] = cn[i-1] / RealD(i); + + ExpClover = Zero(); + IdentityTimesC(ExpClover, cn[NMAX]); + for (int i=NMAX-1; i>=0; i--) + ExpClover = ExpClover * Clover + cn[i]; Clover = ExpClover * diag_mass; - CloverInv = adj(ExpClover * (1.0/diag_mass)); + CloverInv = adj(ExpClover) * (1.0/diag_mass); } static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { From 8a3002c03be467740deff36ab43152b4481b876d Mon Sep 17 00:00:00 2001 From: Christoph Lehner <christoph@lhnr.de> Date: Thu, 24 Feb 2022 22:02:56 +0100 Subject: [PATCH 293/399] separate left and right masses for CayleyFermion5D --- Grid/qcd/action/fermion/CayleyFermion5D.h | 13 ++++-- .../CayleyFermion5DImplementation.h | 42 +++++++++++-------- benchmarks/Benchmark_mooee.cc | 4 +- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Grid/qcd/action/fermion/CayleyFermion5D.h b/Grid/qcd/action/fermion/CayleyFermion5D.h index c7d68d73..3e06aa26 100644 --- a/Grid/qcd/action/fermion/CayleyFermion5D.h +++ b/Grid/qcd/action/fermion/CayleyFermion5D.h @@ -68,9 +68,16 @@ public: /////////////////////////////////////////////////////////////// // Support for MADWF tricks /////////////////////////////////////////////////////////////// - RealD Mass(void) { return mass; }; + RealD Mass(void) { return (mass_plus + mass_minus) / 2.0; }; + RealD MassPlus(void) { return mass_plus; }; + RealD MassMinus(void) { return mass_minus; }; void SetMass(RealD _mass) { - mass=_mass; + mass_plus=mass_minus=_mass; + SetCoefficientsInternal(_zolo_hi,_gamma,_b,_c); // Reset coeffs + } ; + void SetMass(RealD _mass_plus, RealD _mass_minus) { + mass_plus=_mass_plus; + mass_minus=_mass_minus; SetCoefficientsInternal(_zolo_hi,_gamma,_b,_c); // Reset coeffs } ; void P(const FermionField &psi, FermionField &chi); @@ -108,7 +115,7 @@ public: void MeooeDag5D (const FermionField &in, FermionField &out); // protected: - RealD mass; + RealD mass_plus, mass_minus; // Save arguments to SetCoefficientsInternal Vector<Coeff_t> _gamma; diff --git a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h index 1ed66bda..ad3a925f 100644 --- a/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CayleyFermion5DImplementation.h @@ -47,7 +47,7 @@ CayleyFermion5D<Impl>::CayleyFermion5D(GaugeField &_Umu, FiveDimRedBlackGrid, FourDimGrid, FourDimRedBlackGrid,_M5,p), - mass(_mass) + mass_plus(_mass), mass_minus(_mass) { } @@ -209,8 +209,8 @@ void CayleyFermion5D<Impl>::M5D (const FermionField &psi, FermionField &chi) { int Ls=this->Ls; Vector<Coeff_t> diag (Ls,1.0); - Vector<Coeff_t> upper(Ls,-1.0); upper[Ls-1]=mass; - Vector<Coeff_t> lower(Ls,-1.0); lower[0] =mass; + Vector<Coeff_t> upper(Ls,-1.0); upper[Ls-1]=mass_minus; + Vector<Coeff_t> lower(Ls,-1.0); lower[0] =mass_plus; M5D(psi,chi,chi,lower,diag,upper); } template<class Impl> @@ -220,8 +220,8 @@ void CayleyFermion5D<Impl>::Meooe5D (const FermionField &psi, FermionField &D Vector<Coeff_t> diag = bs; Vector<Coeff_t> upper= cs; Vector<Coeff_t> lower= cs; - upper[Ls-1]=-mass*upper[Ls-1]; - lower[0] =-mass*lower[0]; + upper[Ls-1]=-mass_minus*upper[Ls-1]; + lower[0] =-mass_plus*lower[0]; M5D(psi,psi,Din,lower,diag,upper); } // FIXME Redunant with the above routine; check this and eliminate @@ -235,8 +235,8 @@ template<class Impl> void CayleyFermion5D<Impl>::Meo5D (const FermionField & upper[i]=-ceo[i]; lower[i]=-ceo[i]; } - upper[Ls-1]=-mass*upper[Ls-1]; - lower[0] =-mass*lower[0]; + upper[Ls-1]=-mass_minus*upper[Ls-1]; + lower[0] =-mass_plus*lower[0]; M5D(psi,psi,chi,lower,diag,upper); } template<class Impl> @@ -250,8 +250,8 @@ void CayleyFermion5D<Impl>::Mooee (const FermionField &psi, FermionField & upper[i]=-cee[i]; lower[i]=-cee[i]; } - upper[Ls-1]=-mass*upper[Ls-1]; - lower[0] =-mass*lower[0]; + upper[Ls-1]=-mass_minus*upper[Ls-1]; + lower[0] =-mass_plus*lower[0]; M5D(psi,psi,chi,lower,diag,upper); } template<class Impl> @@ -266,9 +266,9 @@ void CayleyFermion5D<Impl>::MooeeDag (const FermionField &psi, FermionField & // Assemble the 5d matrix if ( s==0 ) { upper[s] = -cee[s+1] ; - lower[s] = mass*cee[Ls-1]; + lower[s] = mass_minus*cee[Ls-1]; } else if ( s==(Ls-1)) { - upper[s] = mass*cee[0]; + upper[s] = mass_plus*cee[0]; lower[s] = -cee[s-1]; } else { upper[s]=-cee[s+1]; @@ -291,8 +291,8 @@ void CayleyFermion5D<Impl>::M5Ddag (const FermionField &psi, FermionField &chi) Vector<Coeff_t> diag(Ls,1.0); Vector<Coeff_t> upper(Ls,-1.0); Vector<Coeff_t> lower(Ls,-1.0); - upper[Ls-1]=-mass*upper[Ls-1]; - lower[0] =-mass*lower[0]; + upper[Ls-1]=-mass_plus*upper[Ls-1]; + lower[0] =-mass_minus*lower[0]; M5Ddag(psi,chi,chi,lower,diag,upper); } @@ -307,9 +307,9 @@ void CayleyFermion5D<Impl>::MeooeDag5D (const FermionField &psi, FermionField for (int s=0;s<Ls;s++){ if ( s== 0 ) { upper[s] = cs[s+1]; - lower[s] =-mass*cs[Ls-1]; + lower[s] =-mass_minus*cs[Ls-1]; } else if ( s==(Ls-1) ) { - upper[s] =-mass*cs[0]; + upper[s] =-mass_plus*cs[0]; lower[s] = cs[s-1]; } else { upper[s] = cs[s+1]; @@ -552,7 +552,7 @@ void CayleyFermion5D<Impl>::SetCoefficientsInternal(RealD zolo_hi,Vector<Coeff_t lee[i] =-cee[i+1]/bee[i]; // sub-diag entry on the ith column - leem[i]=mass*cee[Ls-1]/bee[0]; + leem[i]=mass_minus*cee[Ls-1]/bee[0]; for(int j=0;j<i;j++) { assert(bee[j+1]!=Coeff_t(0.0)); leem[i]*= aee[j]/bee[j+1]; @@ -560,7 +560,7 @@ void CayleyFermion5D<Impl>::SetCoefficientsInternal(RealD zolo_hi,Vector<Coeff_t uee[i] =-aee[i]/bee[i]; // up-diag entry on the ith row - ueem[i]=mass; + ueem[i]=mass_plus; for(int j=1;j<=i;j++) ueem[i]*= cee[j]/bee[j]; ueem[i]*= aee[0]/bee[0]; @@ -573,7 +573,7 @@ void CayleyFermion5D<Impl>::SetCoefficientsInternal(RealD zolo_hi,Vector<Coeff_t } { - Coeff_t delta_d=mass*cee[Ls-1]; + Coeff_t delta_d=mass_minus*cee[Ls-1]; for(int j=0;j<Ls-1;j++) { assert(bee[j] != Coeff_t(0.0)); delta_d *= cee[j]/bee[j]; @@ -642,6 +642,10 @@ void CayleyFermion5D<Impl>::ContractConservedCurrent( PropagatorField &q_in_1, Current curr_type, unsigned int mu) { + + assert(mass_plus == mass_minus); + RealD mass = mass_plus; + #if (!defined(GRID_HIP)) Gamma::Algebra Gmu [] = { Gamma::Algebra::GammaX, @@ -777,6 +781,8 @@ void CayleyFermion5D<Impl>::SeqConservedCurrent(PropagatorField &q_in, assert(mu>=0); assert(mu<Nd); + assert(mass_plus == mass_minus); + RealD mass = mass_plus; #if 0 int tshift = (mu == Nd-1) ? 1 : 0; diff --git a/benchmarks/Benchmark_mooee.cc b/benchmarks/Benchmark_mooee.cc index 0aaccecc..289af41d 100644 --- a/benchmarks/Benchmark_mooee.cc +++ b/benchmarks/Benchmark_mooee.cc @@ -81,8 +81,8 @@ int main (int argc, char ** argv) Vector<Coeff_t> diag = Dw.bs; Vector<Coeff_t> upper= Dw.cs; Vector<Coeff_t> lower= Dw.cs; - upper[Ls-1]=-Dw.mass*upper[Ls-1]; - lower[0] =-Dw.mass*lower[0]; + upper[Ls-1]=-Dw.mass_minus*upper[Ls-1]; + lower[0] =-Dw.mass_plus*lower[0]; LatticeFermion r_eo(FGrid); LatticeFermion src_e (FrbGrid); From 013dc2ef338bfb60d01947df65b6037e5d87412e Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Sun, 27 Feb 2022 18:13:47 +0000 Subject: [PATCH 294/399] tests: core tests for wilson clover and wilson exp clover including compact version extended/added --- tests/core/Test_wilson_clover.cc | 214 ++++++++++- tests/core/Test_wilson_exp_clover.cc | 530 +++++++++++++++++++++++++++ 2 files changed, 724 insertions(+), 20 deletions(-) create mode 100644 tests/core/Test_wilson_exp_clover.cc diff --git a/tests/core/Test_wilson_clover.cc b/tests/core/Test_wilson_clover.cc index 642c30a8..8f143070 100644 --- a/tests/core/Test_wilson_clover.cc +++ b/tests/core/Test_wilson_clover.cc @@ -2,11 +2,12 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: ./benchmarks/Benchmark_wilson.cc + Source file: ./tests/core/Test_wilson_clover.cc Copyright (C) 2015 Author: Guido Cossu <guido.cossu@ed.ac.uk> + Fabian Joswig <fabian.joswig@ed.ac.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,8 +68,6 @@ int main(int argc, char **argv) tmp = Zero(); FermionField err(&Grid); err = Zero(); - FermionField err2(&Grid); - err2 = Zero(); FermionField phi(&Grid); random(pRNG, phi); FermionField chi(&Grid); @@ -77,6 +76,8 @@ int main(int argc, char **argv) SU<Nc>::HotConfiguration(pRNG, Umu); std::vector<LatticeColourMatrix> U(4, &Grid); + double tolerance = 1e-4; + double volume = 1; for (int mu = 0; mu < Nd; mu++) { @@ -88,7 +89,7 @@ int main(int argc, char **argv) RealD csw_t = 1.0; WilsonCloverFermionR Dwc(Umu, Grid, RBGrid, mass, csw_r, csw_t, anis, params); - //Dwc.ImportGauge(Umu); // not necessary, included in the constructor + CompactWilsonCloverFermionR Dwc_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 1.0, anis, params); std::cout << GridLogMessage << "==========================================================" << std::endl; std::cout << GridLogMessage << "= Testing that Deo + Doe = Dunprec " << std::endl; @@ -112,7 +113,24 @@ int main(int argc, char **argv) setCheckerboard(r_eo, r_e); err = ref - r_eo; - std::cout << GridLogMessage << "EO norm diff " << norm2(err) << " " << norm2(ref) << " " << norm2(r_eo) << std::endl; + std::cout << GridLogMessage << "EO norm diff\t" << norm2(err) << " (" << norm2(ref) << " - " << norm2(r_eo) << ")" << std::endl; + assert(fabs(norm2(err)) < tolerance); + + + + Dwc_compact.Meooe(src_e, r_o); + std::cout << GridLogMessage << "Applied Meo" << std::endl; + Dwc_compact.Meooe(src_o, r_e); + std::cout << GridLogMessage << "Applied Moe" << std::endl; + Dwc_compact.Dhop(src, ref, DaggerNo); + + setCheckerboard(r_eo, r_o); + setCheckerboard(r_eo, r_e); + + err = ref - r_eo; + std::cout << GridLogMessage << "EO norm diff compact\t" << norm2(err) << " (" << norm2(ref) << " - " << norm2(r_eo) << ")" << std::endl; + assert(fabs(norm2(err)) < tolerance); + std::cout << GridLogMessage << "==============================================================" << std::endl; std::cout << GridLogMessage << "= Test Ddagger is the dagger of D by requiring " << std::endl; @@ -152,6 +170,22 @@ int main(int argc, char **argv) std::cout << GridLogMessage << "pDce - conj(cDpo) " << pDce - conj(cDpo) << std::endl; std::cout << GridLogMessage << "pDco - conj(cDpe) " << pDco - conj(cDpe) << std::endl; + Dwc_compact.Meooe(chi_e, dchi_o); + Dwc_compact.Meooe(chi_o, dchi_e); + Dwc_compact.MeooeDag(phi_e, dphi_o); + Dwc_compact.MeooeDag(phi_o, dphi_e); + + pDce = innerProduct(phi_e, dchi_e); + pDco = innerProduct(phi_o, dchi_o); + cDpe = innerProduct(chi_e, dphi_e); + cDpo = innerProduct(chi_o, dphi_o); + + std::cout << GridLogMessage << "e compact " << pDce << " " << cDpe << std::endl; + std::cout << GridLogMessage << "o compact " << pDco << " " << cDpo << std::endl; + + std::cout << GridLogMessage << "pDce - conj(cDpo) compact " << pDce - conj(cDpo) << std::endl; + std::cout << GridLogMessage << "pDco - conj(cDpe) compact " << pDco - conj(cDpe) << std::endl; + std::cout << GridLogMessage << "==============================================================" << std::endl; std::cout << GridLogMessage << "= Test MeeInv Mee = 1 (if csw!=0) " << std::endl; std::cout << GridLogMessage << "==============================================================" << std::endl; @@ -169,7 +203,21 @@ int main(int argc, char **argv) setCheckerboard(phi, phi_o); err = phi - chi; - std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + Dwc_compact.Mooee(chi_e, src_e); + Dwc_compact.MooeeInv(src_e, phi_e); + + Dwc_compact.Mooee(chi_o, src_o); + Dwc_compact.MooeeInv(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); std::cout << GridLogMessage << "==============================================================" << std::endl; std::cout << GridLogMessage << "= Test MeeDag MeeInvDag = 1 (if csw!=0) " << std::endl; @@ -188,7 +236,21 @@ int main(int argc, char **argv) setCheckerboard(phi, phi_o); err = phi - chi; - std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + Dwc_compact.MooeeDag(chi_e, src_e); + Dwc_compact.MooeeInvDag(src_e, phi_e); + + Dwc_compact.MooeeDag(chi_o, src_o); + Dwc_compact.MooeeInvDag(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); std::cout << GridLogMessage << "==============================================================" << std::endl; std::cout << GridLogMessage << "= Test MeeInv MeeDag = 1 (if csw!=0) " << std::endl; @@ -207,7 +269,21 @@ int main(int argc, char **argv) setCheckerboard(phi, phi_o); err = phi - chi; - std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + Dwc_compact.MooeeDag(chi_e, src_e); + Dwc_compact.MooeeInv(src_e, phi_e); + + Dwc_compact.MooeeDag(chi_o, src_o); + Dwc_compact.MooeeInv(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); std::cout << GridLogMessage << "================================================================" << std::endl; std::cout << GridLogMessage << "= Testing gauge covariance Clover term with EO preconditioning " << std::endl; @@ -249,7 +325,7 @@ int main(int argc, char **argv) ///////////////// WilsonCloverFermionR Dwc_prime(U_prime, Grid, RBGrid, mass, csw_r, csw_t, anis, params); - Dwc_prime.ImportGauge(U_prime); + CompactWilsonCloverFermionR Dwc_compact_prime(U_prime, Grid, RBGrid, mass, csw_r, csw_t, 1.0, anis, params); tmp = Omega * src; pickCheckerboard(Even, src_e, tmp); @@ -262,7 +338,37 @@ int main(int argc, char **argv) setCheckerboard(phi, phi_o); err = chi - adj(Omega) * phi; - std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + tmp = Zero(); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + + Dwc_compact.Mooee(src_e, chi_e); + Dwc_compact.Mooee(src_o, chi_o); + setCheckerboard(chi, chi_e); + setCheckerboard(chi, chi_o); + setCheckerboard(src, src_e); + setCheckerboard(src, src_o); + + tmp = Omega * src; + pickCheckerboard(Even, src_e, tmp); + pickCheckerboard(Odd, src_o, tmp); + + Dwc_compact_prime.Mooee(src_e, phi_e); + Dwc_compact_prime.Mooee(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = chi - adj(Omega) * phi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); std::cout << GridLogMessage << "=================================================================" << std::endl; std::cout << GridLogMessage << "= Testing gauge covariance Clover term w/o EO preconditioning " << std::endl; @@ -272,7 +378,6 @@ int main(int argc, char **argv) phi = Zero(); WilsonFermionR Dw(Umu, Grid, RBGrid, mass, params); - Dw.ImportGauge(Umu); Dw.M(src, result); Dwc.M(src, chi); @@ -280,13 +385,24 @@ int main(int argc, char **argv) Dwc_prime.M(Omega * src, phi); WilsonFermionR Dw_prime(U_prime, Grid, RBGrid, mass, params); - Dw_prime.ImportGauge(U_prime); Dw_prime.M(Omega * src, result2); + err = result - adj(Omega) * result2; + std::cout << GridLogMessage << "norm diff Wilson " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); err = chi - adj(Omega) * phi; - err2 = result - adj(Omega) * result2; - std::cout << GridLogMessage << "norm diff Wilson " << norm2(err) << std::endl; - std::cout << GridLogMessage << "norm diff WilsonClover " << norm2(err2) << std::endl; + std::cout << GridLogMessage << "norm diff WilsonClover " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + + Dwc_compact.M(src, chi); + Dwc_compact_prime.M(Omega * src, phi); + + err = chi - adj(Omega) * phi; + std::cout << GridLogMessage << "norm diff CompactWilsonClover " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); std::cout << GridLogMessage << "==========================================================" << std::endl; std::cout << GridLogMessage << "= Testing Mooee(csw=0) Clover to reproduce Mooee Wilson " << std::endl; @@ -296,7 +412,6 @@ int main(int argc, char **argv) phi = Zero(); err = Zero(); WilsonCloverFermionR Dwc_csw0(Umu, Grid, RBGrid, mass, 0.0, 0.0, anis, params); // <-- Notice: csw=0 - Dwc_csw0.ImportGauge(Umu); pickCheckerboard(Even, phi_e, phi); pickCheckerboard(Odd, phi_o, phi); @@ -316,7 +431,34 @@ int main(int argc, char **argv) setCheckerboard(src, src_o); err = chi - phi; - std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + err = Zero(); + CompactWilsonCloverFermionR Dwc_compact_csw0(Umu, Grid, RBGrid, mass, 0.0, 0.0, 1.0, anis, params); // <-- Notice: csw=0 + + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + Dw.Mooee(src_e, chi_e); + Dw.Mooee(src_o, chi_o); + Dwc_compact_csw0.Mooee(src_e, phi_e); + Dwc_compact_csw0.Mooee(src_o, phi_o); + + setCheckerboard(chi, chi_e); + setCheckerboard(chi, chi_o); + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + setCheckerboard(src, src_e); + setCheckerboard(src, src_o); + + err = chi - phi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); std::cout << GridLogMessage << "==========================================================" << std::endl; std::cout << GridLogMessage << "= Testing EO operator is equal to the unprec " << std::endl; @@ -348,9 +490,41 @@ int main(int argc, char **argv) setCheckerboard(phi, phi_o); err = ref - phi; - std::cout << GridLogMessage << "ref (unpreconditioned operator) diff :" << norm2(ref) << std::endl; - std::cout << GridLogMessage << "phi (EO decomposition) diff :" << norm2(phi) << std::endl; - std::cout << GridLogMessage << "norm diff :" << norm2(err) << std::endl; + std::cout << GridLogMessage << "ref (unpreconditioned operator) diff : " << norm2(ref) << std::endl; + std::cout << GridLogMessage << "phi (EO decomposition) diff : " << norm2(phi) << std::endl; + std::cout << GridLogMessage << "norm diff : " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + err = Zero(); + + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + // M phi = (Mooee src_e + Meooe src_o , Meooe src_e + Mooee src_o) + + Dwc_compact.M(src, ref); // Reference result from the unpreconditioned operator + + // EO matrix + Dwc_compact.Mooee(src_e, chi_e); + Dwc_compact.Mooee(src_o, chi_o); + Dwc_compact.Meooe(src_o, phi_e); + Dwc_compact.Meooe(src_e, phi_o); + + phi_o += chi_o; + phi_e += chi_e; + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = ref - phi; + std::cout << GridLogMessage << "ref (unpreconditioned operator) diff compact : " << norm2(ref) << std::endl; + std::cout << GridLogMessage << "phi (EO decomposition) diff compact : " << norm2(phi) << std::endl; + std::cout << GridLogMessage << "norm diff compact : " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); Grid_finalize(); } diff --git a/tests/core/Test_wilson_exp_clover.cc b/tests/core/Test_wilson_exp_clover.cc new file mode 100644 index 00000000..8516d0dc --- /dev/null +++ b/tests/core/Test_wilson_exp_clover.cc @@ -0,0 +1,530 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/core/Test_wilson_exp_clover.cc + + Copyright (C) 2022 + + Author: Guido Cossu <guido.cossu@ed.ac.uk> + Fabian Joswig <fabian.joswig@ed.ac.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ +#include <Grid/Grid.h> + +using namespace std; +using namespace Grid; + +int main(int argc, char **argv) +{ + Grid_init(&argc, &argv); + + auto latt_size = GridDefaultLatt(); + auto simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); + auto mpi_layout = GridDefaultMpi(); + GridCartesian Grid(latt_size, simd_layout, mpi_layout); + GridRedBlackCartesian RBGrid(&Grid); + + int threads = GridThread::GetThreads(); + std::cout << GridLogMessage << "Grid is setup to use " << threads << " threads" << std::endl; + std::cout << GridLogMessage << "Grid floating point word size is REALF" << sizeof(RealF) << std::endl; + std::cout << GridLogMessage << "Grid floating point word size is REALD" << sizeof(RealD) << std::endl; + std::cout << GridLogMessage << "Grid floating point word size is REAL" << sizeof(Real) << std::endl; + + std::vector<int> seeds({1, 2, 3, 4}); + GridParallelRNG pRNG(&Grid); + pRNG.SeedFixedIntegers(seeds); + // pRNG.SeedFixedIntegers(std::vector<int>({45,12,81,9}); + + typedef typename WilsonExpCloverFermionR::FermionField FermionField; + typename WilsonExpCloverFermionR::ImplParams params; + WilsonAnisotropyCoefficients anis; + + FermionField src(&Grid); + random(pRNG, src); + FermionField result(&Grid); + result = Zero(); + FermionField result2(&Grid); + result2 = Zero(); + FermionField ref(&Grid); + ref = Zero(); + FermionField tmp(&Grid); + tmp = Zero(); + FermionField err(&Grid); + err = Zero(); + FermionField phi(&Grid); + random(pRNG, phi); + FermionField chi(&Grid); + random(pRNG, chi); + LatticeGaugeField Umu(&Grid); + SU<Nc>::HotConfiguration(pRNG, Umu); + std::vector<LatticeColourMatrix> U(4, &Grid); + + double tolerance = 1e-4; + + double volume = 1; + for (int mu = 0; mu < Nd; mu++) + { + volume = volume * latt_size[mu]; + } + + RealD mass = 0.1; + RealD csw_r = 1.0; + RealD csw_t = 1.0; + + WilsonExpCloverFermionR Dwc(Umu, Grid, RBGrid, mass, csw_r, csw_t, anis, params); + CompactWilsonExpCloverFermionR Dwc_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 1.0, anis, params); + + std::cout << GridLogMessage << "==========================================================" << std::endl; + std::cout << GridLogMessage << "= Testing that Deo + Doe = Dunprec " << std::endl; + std::cout << GridLogMessage << "==========================================================" << std::endl; + + FermionField src_e(&RBGrid); + FermionField src_o(&RBGrid); + FermionField r_e(&RBGrid); + FermionField r_o(&RBGrid); + FermionField r_eo(&Grid); + pickCheckerboard(Even, src_e, src); + pickCheckerboard(Odd, src_o, src); + + Dwc.Meooe(src_e, r_o); + std::cout << GridLogMessage << "Applied Meo" << std::endl; + Dwc.Meooe(src_o, r_e); + std::cout << GridLogMessage << "Applied Moe" << std::endl; + Dwc.Dhop(src, ref, DaggerNo); + + setCheckerboard(r_eo, r_o); + setCheckerboard(r_eo, r_e); + + err = ref - r_eo; + std::cout << GridLogMessage << "EO norm diff\t" << norm2(err) << " (" << norm2(ref) << " - " << norm2(r_eo) << ")" << std::endl; + assert(fabs(norm2(err)) < tolerance); + + + + Dwc_compact.Meooe(src_e, r_o); + std::cout << GridLogMessage << "Applied Meo" << std::endl; + Dwc_compact.Meooe(src_o, r_e); + std::cout << GridLogMessage << "Applied Moe" << std::endl; + Dwc_compact.Dhop(src, ref, DaggerNo); + + setCheckerboard(r_eo, r_o); + setCheckerboard(r_eo, r_e); + + err = ref - r_eo; + std::cout << GridLogMessage << "EO norm diff compact\t" << norm2(err) << " (" << norm2(ref) << " - " << norm2(r_eo) << ")" << std::endl; + assert(fabs(norm2(err)) < tolerance); + + + std::cout << GridLogMessage << "==============================================================" << std::endl; + std::cout << GridLogMessage << "= Test Ddagger is the dagger of D by requiring " << std::endl; + std::cout << GridLogMessage << "= < phi | Deo | chi > * = < chi | Deo^dag| phi> " << std::endl; + std::cout << GridLogMessage << "==============================================================" << std::endl; + + FermionField chi_e(&RBGrid); + FermionField chi_o(&RBGrid); + + FermionField dchi_e(&RBGrid); + FermionField dchi_o(&RBGrid); + + FermionField phi_e(&RBGrid); + FermionField phi_o(&RBGrid); + + FermionField dphi_e(&RBGrid); + FermionField dphi_o(&RBGrid); + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + + Dwc.Meooe(chi_e, dchi_o); + Dwc.Meooe(chi_o, dchi_e); + Dwc.MeooeDag(phi_e, dphi_o); + Dwc.MeooeDag(phi_o, dphi_e); + + ComplexD pDce = innerProduct(phi_e, dchi_e); + ComplexD pDco = innerProduct(phi_o, dchi_o); + ComplexD cDpe = innerProduct(chi_e, dphi_e); + ComplexD cDpo = innerProduct(chi_o, dphi_o); + + std::cout << GridLogMessage << "e " << pDce << " " << cDpe << std::endl; + std::cout << GridLogMessage << "o " << pDco << " " << cDpo << std::endl; + + std::cout << GridLogMessage << "pDce - conj(cDpo) " << pDce - conj(cDpo) << std::endl; + std::cout << GridLogMessage << "pDco - conj(cDpe) " << pDco - conj(cDpe) << std::endl; + + Dwc_compact.Meooe(chi_e, dchi_o); + Dwc_compact.Meooe(chi_o, dchi_e); + Dwc_compact.MeooeDag(phi_e, dphi_o); + Dwc_compact.MeooeDag(phi_o, dphi_e); + + pDce = innerProduct(phi_e, dchi_e); + pDco = innerProduct(phi_o, dchi_o); + cDpe = innerProduct(chi_e, dphi_e); + cDpo = innerProduct(chi_o, dphi_o); + + std::cout << GridLogMessage << "e compact " << pDce << " " << cDpe << std::endl; + std::cout << GridLogMessage << "o compact " << pDco << " " << cDpo << std::endl; + + std::cout << GridLogMessage << "pDce - conj(cDpo) compact " << pDce - conj(cDpo) << std::endl; + std::cout << GridLogMessage << "pDco - conj(cDpe) compact " << pDco - conj(cDpe) << std::endl; + + std::cout << GridLogMessage << "==============================================================" << std::endl; + std::cout << GridLogMessage << "= Test MeeInv Mee = 1 (if csw!=0) " << std::endl; + std::cout << GridLogMessage << "==============================================================" << std::endl; + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + Dwc.Mooee(chi_e, src_e); + Dwc.MooeeInv(src_e, phi_e); + + Dwc.Mooee(chi_o, src_o); + Dwc.MooeeInv(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + Dwc_compact.Mooee(chi_e, src_e); + Dwc_compact.MooeeInv(src_e, phi_e); + + Dwc_compact.Mooee(chi_o, src_o); + Dwc_compact.MooeeInv(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + std::cout << GridLogMessage << "==============================================================" << std::endl; + std::cout << GridLogMessage << "= Test MeeDag MeeInvDag = 1 (if csw!=0) " << std::endl; + std::cout << GridLogMessage << "==============================================================" << std::endl; + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + Dwc.MooeeDag(chi_e, src_e); + Dwc.MooeeInvDag(src_e, phi_e); + + Dwc.MooeeDag(chi_o, src_o); + Dwc.MooeeInvDag(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + Dwc_compact.MooeeDag(chi_e, src_e); + Dwc_compact.MooeeInvDag(src_e, phi_e); + + Dwc_compact.MooeeDag(chi_o, src_o); + Dwc_compact.MooeeInvDag(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + std::cout << GridLogMessage << "==============================================================" << std::endl; + std::cout << GridLogMessage << "= Test MeeInv MeeDag = 1 (if csw!=0) " << std::endl; + std::cout << GridLogMessage << "==============================================================" << std::endl; + + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + Dwc.MooeeDag(chi_e, src_e); + Dwc.MooeeInv(src_e, phi_e); + + Dwc.MooeeDag(chi_o, src_o); + Dwc.MooeeInv(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + Dwc_compact.MooeeDag(chi_e, src_e); + Dwc_compact.MooeeInv(src_e, phi_e); + + Dwc_compact.MooeeDag(chi_o, src_o); + Dwc_compact.MooeeInv(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = phi - chi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + std::cout << GridLogMessage << "================================================================" << std::endl; + std::cout << GridLogMessage << "= Testing gauge covariance Clover term with EO preconditioning " << std::endl; + std::cout << GridLogMessage << "================================================================" << std::endl; + + chi = Zero(); + phi = Zero(); + tmp = Zero(); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + + Dwc.Mooee(src_e, chi_e); + Dwc.Mooee(src_o, chi_o); + setCheckerboard(chi, chi_e); + setCheckerboard(chi, chi_o); + setCheckerboard(src, src_e); + setCheckerboard(src, src_o); + + ////////////////////// Gauge Transformation + std::vector<int> seeds2({5, 6, 7, 8}); + GridParallelRNG pRNG2(&Grid); + pRNG2.SeedFixedIntegers(seeds2); + LatticeColourMatrix Omega(&Grid); + LatticeColourMatrix ShiftedOmega(&Grid); + LatticeGaugeField U_prime(&Grid); + U_prime = Zero(); + LatticeColourMatrix U_prime_mu(&Grid); + U_prime_mu = Zero(); + SU<Nc>::LieRandomize(pRNG2, Omega, 1.0); + for (int mu = 0; mu < Nd; mu++) + { + U[mu] = peekLorentz(Umu, mu); + ShiftedOmega = Cshift(Omega, mu, 1); + U_prime_mu = Omega * U[mu] * adj(ShiftedOmega); + pokeLorentz(U_prime, U_prime_mu, mu); + } + ///////////////// + + WilsonExpCloverFermionR Dwc_prime(U_prime, Grid, RBGrid, mass, csw_r, csw_t, anis, params); + CompactWilsonExpCloverFermionR Dwc_compact_prime(U_prime, Grid, RBGrid, mass, csw_r, csw_t, 1.0, anis, params); + + tmp = Omega * src; + pickCheckerboard(Even, src_e, tmp); + pickCheckerboard(Odd, src_o, tmp); + + Dwc_prime.Mooee(src_e, phi_e); + Dwc_prime.Mooee(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = chi - adj(Omega) * phi; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + tmp = Zero(); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + + Dwc_compact.Mooee(src_e, chi_e); + Dwc_compact.Mooee(src_o, chi_o); + setCheckerboard(chi, chi_e); + setCheckerboard(chi, chi_o); + setCheckerboard(src, src_e); + setCheckerboard(src, src_o); + + tmp = Omega * src; + pickCheckerboard(Even, src_e, tmp); + pickCheckerboard(Odd, src_o, tmp); + + Dwc_compact_prime.Mooee(src_e, phi_e); + Dwc_compact_prime.Mooee(src_o, phi_o); + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = chi - adj(Omega) * phi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + std::cout << GridLogMessage << "=================================================================" << std::endl; + std::cout << GridLogMessage << "= Testing gauge covariance Clover term w/o EO preconditioning " << std::endl; + std::cout << GridLogMessage << "================================================================" << std::endl; + + chi = Zero(); + phi = Zero(); + + WilsonFermionR Dw(Umu, Grid, RBGrid, mass, params); + + Dw.M(src, result); + Dwc.M(src, chi); + + Dwc_prime.M(Omega * src, phi); + + WilsonFermionR Dw_prime(U_prime, Grid, RBGrid, mass, params); + Dw_prime.M(Omega * src, result2); + + err = result - adj(Omega) * result2; + std::cout << GridLogMessage << "norm diff Wilson " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + err = chi - adj(Omega) * phi; + std::cout << GridLogMessage << "norm diff WilsonExpClover " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + + Dwc_compact.M(src, chi); + Dwc_compact_prime.M(Omega * src, phi); + + err = chi - adj(Omega) * phi; + std::cout << GridLogMessage << "norm diff CompactWilsonExpClover " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + std::cout << GridLogMessage << "==========================================================" << std::endl; + std::cout << GridLogMessage << "= Testing Mooee(csw=0) Clover to reproduce Mooee Wilson " << std::endl; + std::cout << GridLogMessage << "==========================================================" << std::endl; + + chi = Zero(); + phi = Zero(); + err = Zero(); + WilsonExpCloverFermionR Dwc_csw0(Umu, Grid, RBGrid, mass, 0.0, 0.0, anis, params); // <-- Notice: csw=0 + + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + Dw.Mooee(src_e, chi_e); + Dw.Mooee(src_o, chi_o); + Dwc_csw0.Mooee(src_e, phi_e); + Dwc_csw0.Mooee(src_o, phi_o); + + setCheckerboard(chi, chi_e); + setCheckerboard(chi, chi_o); + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + setCheckerboard(src, src_e); + setCheckerboard(src, src_o); + + err = chi - phi; + std::cout << GridLogMessage << "norm diff " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + err = Zero(); + CompactWilsonExpCloverFermionR Dwc_compact_csw0(Umu, Grid, RBGrid, mass, 0.0, 0.0, 1.0, anis, params); // <-- Notice: csw=0 + + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + Dw.Mooee(src_e, chi_e); + Dw.Mooee(src_o, chi_o); + Dwc_compact_csw0.Mooee(src_e, phi_e); + Dwc_compact_csw0.Mooee(src_o, phi_o); + + setCheckerboard(chi, chi_e); + setCheckerboard(chi, chi_o); + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + setCheckerboard(src, src_e); + setCheckerboard(src, src_o); + + err = chi - phi; + std::cout << GridLogMessage << "norm diff compact " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + std::cout << GridLogMessage << "==========================================================" << std::endl; + std::cout << GridLogMessage << "= Testing EO operator is equal to the unprec " << std::endl; + std::cout << GridLogMessage << "==========================================================" << std::endl; + + chi = Zero(); + phi = Zero(); + err = Zero(); + + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + // M phi = (Mooee src_e + Meooe src_o , Meooe src_e + Mooee src_o) + + Dwc.M(src, ref); // Reference result from the unpreconditioned operator + + // EO matrix + Dwc.Mooee(src_e, chi_e); + Dwc.Mooee(src_o, chi_o); + Dwc.Meooe(src_o, phi_e); + Dwc.Meooe(src_e, phi_o); + + phi_o += chi_o; + phi_e += chi_e; + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = ref - phi; + std::cout << GridLogMessage << "ref (unpreconditioned operator) diff : " << norm2(ref) << std::endl; + std::cout << GridLogMessage << "phi (EO decomposition) diff : " << norm2(phi) << std::endl; + std::cout << GridLogMessage << "norm diff : " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + chi = Zero(); + phi = Zero(); + err = Zero(); + + pickCheckerboard(Even, phi_e, phi); + pickCheckerboard(Odd, phi_o, phi); + pickCheckerboard(Even, chi_e, chi); + pickCheckerboard(Odd, chi_o, chi); + + // M phi = (Mooee src_e + Meooe src_o , Meooe src_e + Mooee src_o) + + Dwc_compact.M(src, ref); // Reference result from the unpreconditioned operator + + // EO matrix + Dwc_compact.Mooee(src_e, chi_e); + Dwc_compact.Mooee(src_o, chi_o); + Dwc_compact.Meooe(src_o, phi_e); + Dwc_compact.Meooe(src_e, phi_o); + + phi_o += chi_o; + phi_e += chi_e; + + setCheckerboard(phi, phi_e); + setCheckerboard(phi, phi_o); + + err = ref - phi; + std::cout << GridLogMessage << "ref (unpreconditioned operator) diff compact : " << norm2(ref) << std::endl; + std::cout << GridLogMessage << "phi (EO decomposition) diff compact : " << norm2(phi) << std::endl; + std::cout << GridLogMessage << "norm diff compact : " << norm2(err) << std::endl; + assert(fabs(norm2(err)) < tolerance); + + Grid_finalize(); +} From 239e2c1ee6a031be03c67c4b8de16e9538323982 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Sun, 27 Feb 2022 18:26:34 +0000 Subject: [PATCH 295/399] tests: wilson clover cg tests now include compact variant as well as exponential wilson clover operators --- tests/solver/Test_wilsonclover_cg_prec.cc | 27 +++++++++++++++-- tests/solver/Test_wilsonclover_cg_schur.cc | 32 ++++++++++++++++----- tests/solver/Test_wilsonclover_cg_unprec.cc | 27 +++++++++++++++-- 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/tests/solver/Test_wilsonclover_cg_prec.cc b/tests/solver/Test_wilsonclover_cg_prec.cc index 5ef2dc09..abf64a1f 100644 --- a/tests/solver/Test_wilsonclover_cg_prec.cc +++ b/tests/solver/Test_wilsonclover_cg_prec.cc @@ -71,7 +71,12 @@ int main (int argc, char ** argv) RealD mass = -0.1; RealD csw_r = 1.0; RealD csw_t = 1.0; + RealD cF = 1.0; WilsonCloverFermionR Dw(Umu, Grid, RBGrid, mass, csw_r, csw_t); + CompactWilsonCloverFermionR Dw_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 0.0); + WilsonExpCloverFermionR Dwe(Umu, Grid, RBGrid, mass, csw_r, csw_t); + CompactWilsonExpCloverFermionR Dwe_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 0.0); + // HermitianOperator<WilsonFermion,LatticeFermion> HermOp(Dw); // ConjugateGradient<LatticeFermion> CG(1.0e-8,10000); @@ -80,12 +85,28 @@ int main (int argc, char ** argv) LatticeFermion src_o(&RBGrid); LatticeFermion result_o(&RBGrid); pickCheckerboard(Odd,src_o,src); - result_o=Zero(); - SchurDiagMooeeOperator<WilsonCloverFermionR,LatticeFermion> HermOpEO(Dw); ConjugateGradient<LatticeFermion> CG(1.0e-8,10000); + + std::cout << GridLogMessage << "Testing Wilson Clover" << std::endl; + SchurDiagMooeeOperator<WilsonCloverFermionR,LatticeFermion> HermOpEO(Dw); + result_o=Zero(); CG(HermOpEO,src_o,result_o); - + + std::cout << GridLogMessage << "Testing Compact Wilson Clover" << std::endl; + SchurDiagMooeeOperator<CompactWilsonCloverFermionR,LatticeFermion> HermOpEO_compact(Dw_compact); + result_o=Zero(); + CG(HermOpEO_compact,src_o,result_o); + + std::cout << GridLogMessage << "Testing Wilson Exp Clover" << std::endl; + SchurDiagMooeeOperator<WilsonExpCloverFermionR,LatticeFermion> HermOpEO_exp(Dwe); + result_o=Zero(); + CG(HermOpEO_exp,src_o,result_o); + + std::cout << GridLogMessage << "Testing Compact Wilson Exp Clover" << std::endl; + SchurDiagMooeeOperator<CompactWilsonExpCloverFermionR,LatticeFermion> HermOpEO_exp_compact(Dwe_compact); + result_o=Zero(); + CG(HermOpEO_exp_compact,src_o,result_o); Grid_finalize(); } diff --git a/tests/solver/Test_wilsonclover_cg_schur.cc b/tests/solver/Test_wilsonclover_cg_schur.cc index 567a8283..50d06af7 100644 --- a/tests/solver/Test_wilsonclover_cg_schur.cc +++ b/tests/solver/Test_wilsonclover_cg_schur.cc @@ -60,18 +60,36 @@ int main (int argc, char ** argv) LatticeGaugeField Umu(&Grid); SU<Nc>::HotConfiguration(pRNG,Umu); LatticeFermion src(&Grid); random(pRNG,src); - LatticeFermion result(&Grid); result=Zero(); - LatticeFermion resid(&Grid); - - RealD mass = -0.1; - RealD csw_r = 1.0; - RealD csw_t = 1.0; - WilsonCloverFermionR Dw(Umu, Grid, RBGrid, mass, csw_r, csw_t); + LatticeFermion result(&Grid); + LatticeFermion resid(&Grid); ConjugateGradient<LatticeFermion> CG(1.0e-8,10000); SchurRedBlackDiagMooeeSolve<LatticeFermion> SchurSolver(CG); + RealD mass = -0.1; + RealD csw_r = 1.0; + RealD csw_t = 1.0; + RealD cF = 1.0; + + std::cout << GridLogMessage << "Testing Wilson Clover" << std::endl; + WilsonCloverFermionR Dw(Umu, Grid, RBGrid, mass, csw_r, csw_t); + result=Zero(); SchurSolver(Dw,src,result); + + std::cout << GridLogMessage << "Testing Compact Wilson Clover" << std::endl; + CompactWilsonCloverFermionR Dw_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 0.0); + result=Zero(); + SchurSolver(Dw_compact,src,result); + + std::cout << GridLogMessage << "Testing Wilson Exp Clover" << std::endl; + WilsonExpCloverFermionR Dwe(Umu, Grid, RBGrid, mass, csw_r, csw_t); + result=Zero(); + SchurSolver(Dwe,src,result); + + std::cout << GridLogMessage << "Testing Compact Wilson Exp Clover" << std::endl; + CompactWilsonExpCloverFermionR Dwe_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 0.0); + result=Zero(); + SchurSolver(Dwe_compact,src,result); Grid_finalize(); } diff --git a/tests/solver/Test_wilsonclover_cg_unprec.cc b/tests/solver/Test_wilsonclover_cg_unprec.cc index 755d80e1..2a859f11 100644 --- a/tests/solver/Test_wilsonclover_cg_unprec.cc +++ b/tests/solver/Test_wilsonclover_cg_unprec.cc @@ -59,7 +59,7 @@ int main (int argc, char ** argv) LatticeFermion src(&Grid); random(pRNG,src); RealD nrm = norm2(src); - LatticeFermion result(&Grid); result=Zero(); + LatticeFermion result(&Grid); LatticeGaugeField Umu(&Grid); SU<Nc>::HotConfiguration(pRNG,Umu); double volume=1; @@ -70,11 +70,34 @@ int main (int argc, char ** argv) RealD mass = -0.1; RealD csw_r = 1.0; RealD csw_t = 1.0; + RealD cF = 1.0; WilsonCloverFermionR Dw(Umu, Grid, RBGrid, mass, csw_r, csw_t); + CompactWilsonCloverFermionR Dw_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 0.0); + WilsonExpCloverFermionR Dwe(Umu, Grid, RBGrid, mass, csw_r, csw_t); + CompactWilsonExpCloverFermionR Dwe_compact(Umu, Grid, RBGrid, mass, csw_r, csw_t, 0.0); - MdagMLinearOperator<WilsonFermionR,LatticeFermion> HermOp(Dw); ConjugateGradient<LatticeFermion> CG(1.0e-8,10000); + + std::cout << GridLogMessage << "Testing Wilson Clover" << std::endl; + MdagMLinearOperator<WilsonCloverFermionR,LatticeFermion> HermOp(Dw); + result=Zero(); CG(HermOp,src,result); + std::cout << GridLogMessage << "Testing Compact Wilson Clover" << std::endl; + MdagMLinearOperator<CompactWilsonCloverFermionR,LatticeFermion> HermOp_compact(Dw_compact); + result=Zero(); + CG(HermOp_compact,src,result); + + + std::cout << GridLogMessage << "Testing Wilson Exp Clover" << std::endl; + MdagMLinearOperator<WilsonExpCloverFermionR,LatticeFermion> HermOp_exp(Dwe); + result=Zero(); + CG(HermOp_exp,src,result); + + std::cout << GridLogMessage << "Testing Compact Wilson Exp Clover" << std::endl; + MdagMLinearOperator<CompactWilsonExpCloverFermionR,LatticeFermion> HermOp_exp_compact(Dwe_compact); + result=Zero(); + CG(HermOp_exp_compact,src,result); + Grid_finalize(); } From 438caab25f66918dc5f29bd4a873f52baef2aeeb Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Sun, 27 Feb 2022 18:27:18 +0000 Subject: [PATCH 296/399] generate_instantiations.sh now correctly produces instantiations for CompactClover variant, redundant instantiations removed. --- ...ilsonKernelsInstantiationWilsonAdjImplD.cc | 52 +------------------ ...ilsonKernelsInstantiationWilsonAdjImplF.cc | 52 +------------------ .../WilsonKernelsInstantiationWilsonImplD.cc | 52 +------------------ .../WilsonKernelsInstantiationWilsonImplF.cc | 52 +------------------ ...tiationWilsonTwoIndexAntiSymmetricImplD.cc | 52 +------------------ ...tiationWilsonTwoIndexAntiSymmetricImplF.cc | 52 +------------------ ...stantiationWilsonTwoIndexSymmetricImplD.cc | 52 +------------------ ...stantiationWilsonTwoIndexSymmetricImplF.cc | 52 +------------------ .../WilsonKernelsInstantiationZWilsonImplD.cc | 52 +------------------ .../WilsonKernelsInstantiationZWilsonImplF.cc | 52 +------------------ .../instantiation/generate_instantiations.sh | 24 +++++++-- 11 files changed, 29 insertions(+), 515 deletions(-) mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/WilsonKernelsInstantiationWilsonAdjImplD.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/WilsonKernelsInstantiationWilsonAdjImplF.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonImplD/WilsonKernelsInstantiationWilsonImplD.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonImplF/WilsonKernelsInstantiationWilsonImplF.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplD.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplF.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplD.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplF.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplD/WilsonKernelsInstantiationZWilsonImplD.cc mode change 100644 => 120000 Grid/qcd/action/fermion/instantiation/ZWilsonImplF/WilsonKernelsInstantiationZWilsonImplF.cc diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/WilsonKernelsInstantiationWilsonAdjImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/WilsonKernelsInstantiationWilsonAdjImplD.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/WilsonKernelsInstantiationWilsonAdjImplD.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/WilsonKernelsInstantiationWilsonAdjImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/WilsonKernelsInstantiationWilsonAdjImplD.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplD/WilsonKernelsInstantiationWilsonAdjImplD.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/WilsonKernelsInstantiationWilsonAdjImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/WilsonKernelsInstantiationWilsonAdjImplF.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/WilsonKernelsInstantiationWilsonAdjImplF.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/WilsonKernelsInstantiationWilsonAdjImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/WilsonKernelsInstantiationWilsonAdjImplF.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonAdjImplF/WilsonKernelsInstantiationWilsonAdjImplF.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplD/WilsonKernelsInstantiationWilsonImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplD/WilsonKernelsInstantiationWilsonImplD.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplD/WilsonKernelsInstantiationWilsonImplD.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplD/WilsonKernelsInstantiationWilsonImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplD/WilsonKernelsInstantiationWilsonImplD.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonImplD/WilsonKernelsInstantiationWilsonImplD.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplF/WilsonKernelsInstantiationWilsonImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplF/WilsonKernelsInstantiationWilsonImplF.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonImplF/WilsonKernelsInstantiationWilsonImplF.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonImplF/WilsonKernelsInstantiationWilsonImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonImplF/WilsonKernelsInstantiationWilsonImplF.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonImplF/WilsonKernelsInstantiationWilsonImplF.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplD.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplD.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplD.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplD.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplF.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplF.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplF.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexAntiSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexAntiSymmetricImplF.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplD.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplD.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplD.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplD.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplD/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplD.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplF.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplF.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplF.cc b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplF.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/WilsonTwoIndexSymmetricImplF/WilsonKernelsInstantiationWilsonTwoIndexSymmetricImplF.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplD/WilsonKernelsInstantiationZWilsonImplD.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplD/WilsonKernelsInstantiationZWilsonImplD.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplD/WilsonKernelsInstantiationZWilsonImplD.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplD/WilsonKernelsInstantiationZWilsonImplD.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplD/WilsonKernelsInstantiationZWilsonImplD.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/ZWilsonImplD/WilsonKernelsInstantiationZWilsonImplD.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplF/WilsonKernelsInstantiationZWilsonImplF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplF/WilsonKernelsInstantiationZWilsonImplF.cc deleted file mode 100644 index f0b15e3b..00000000 --- a/Grid/qcd/action/fermion/instantiation/ZWilsonImplF/WilsonKernelsInstantiationZWilsonImplF.cc +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: ./lib/qcd/action/fermion/WilsonKernels.cc - -Copyright (C) 2015, 2020 - -Author: Peter Boyle <paboyle@ph.ed.ac.uk> -Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> -Author: paboyle <paboyle@ph.ed.ac.uk> -Author: Nils Meyer <nils.meyer@ur.de> Regensburg University - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -See the full license in the file "LICENSE" in the top level distribution -directory -*************************************************************************************/ -/* END LEGAL */ -#include <Grid/qcd/action/fermion/FermionCore.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h> -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsHandImplementation.h> - -#ifndef AVX512 -#ifndef QPX -#ifndef A64FX -#ifndef A64FXFIXEDSIZE -#include <Grid/qcd/action/fermion/implementation/WilsonKernelsAsmImplementation.h> -#endif -#endif -#endif -#endif - -NAMESPACE_BEGIN(Grid); - -#include "impl.h" -template class WilsonKernels<IMPLEMENTATION>; - -NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/ZWilsonImplF/WilsonKernelsInstantiationZWilsonImplF.cc b/Grid/qcd/action/fermion/instantiation/ZWilsonImplF/WilsonKernelsInstantiationZWilsonImplF.cc new file mode 120000 index 00000000..01c35e7b --- /dev/null +++ b/Grid/qcd/action/fermion/instantiation/ZWilsonImplF/WilsonKernelsInstantiationZWilsonImplF.cc @@ -0,0 +1 @@ +../WilsonKernelsInstantiation.cc.master \ No newline at end of file diff --git a/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh b/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh index a09fd420..d6845e75 100755 --- a/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh +++ b/Grid/qcd/action/fermion/instantiation/generate_instantiations.sh @@ -18,6 +18,10 @@ WILSON_IMPL_LIST=" \ GparityWilsonImplF \ GparityWilsonImplD " +COMPACT_WILSON_IMPL_LIST=" \ + WilsonImplF \ + WilsonImplD " + DWF_IMPL_LIST=" \ WilsonImplF \ WilsonImplD \ @@ -40,13 +44,23 @@ EOF done -CC_LIST="WilsonCloverFermionInstantiation CompactWilsonCloverFermionInstantiation WilsonFermionInstantiation WilsonKernelsInstantiation WilsonTMFermionInstantiation" +CC_LIST="WilsonCloverFermionInstantiation WilsonFermionInstantiation WilsonKernelsInstantiation WilsonTMFermionInstantiation" for impl in $WILSON_IMPL_LIST do for f in $CC_LIST do - ln -f -s ../$f.cc.master $impl/$f$impl.cc + ln -f -s ../$f.cc.master $impl/$f$impl.cc +done +done + +CC_LIST="CompactWilsonCloverFermionInstantiation" + +for impl in $COMPACT_WILSON_IMPL_LIST +do +for f in $CC_LIST +do + ln -f -s ../$f.cc.master $impl/$f$impl.cc done done @@ -63,14 +77,14 @@ for impl in $DWF_IMPL_LIST $GDWF_IMPL_LIST do for f in $CC_LIST do - ln -f -s ../$f.cc.master $impl/$f$impl.cc + ln -f -s ../$f.cc.master $impl/$f$impl.cc done done # overwrite the .cc file in Gparity directories for impl in $GDWF_IMPL_LIST do - ln -f -s ../WilsonKernelsInstantiationGparity.cc.master $impl/WilsonKernelsInstantiation$impl.cc + ln -f -s ../WilsonKernelsInstantiationGparity.cc.master $impl/WilsonKernelsInstantiation$impl.cc done @@ -84,7 +98,7 @@ for impl in $STAG_IMPL_LIST do for f in $CC_LIST do - ln -f -s ../$f.cc.master $impl/$f$impl.cc + ln -f -s ../$f.cc.master $impl/$f$impl.cc done done From 3e882f555dcb75a29e640c6c70fd7e7f23973b73 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 1 Mar 2022 08:54:45 -0500 Subject: [PATCH 297/399] Large / small sumD options --- Grid/lattice/Lattice_reduction.h | 9 +++++ Grid/lattice/Lattice_reduction_gpu.h | 53 ++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/Grid/lattice/Lattice_reduction.h b/Grid/lattice/Lattice_reduction.h index 326b9ea3..c3478ab4 100644 --- a/Grid/lattice/Lattice_reduction.h +++ b/Grid/lattice/Lattice_reduction.h @@ -142,6 +142,15 @@ inline typename vobj::scalar_objectD sumD(const vobj *arg, Integer osites) return sumD_cpu(arg,osites); #endif } +template<class vobj> +inline typename vobj::scalar_objectD sumD_large(const vobj *arg, Integer osites) +{ +#if defined(GRID_CUDA)||defined(GRID_HIP) + return sumD_gpu_large(arg,osites); +#else + return sumD_cpu(arg,osites); +#endif +} template<class vobj> inline typename vobj::scalar_object sum(const Lattice<vobj> &arg) diff --git a/Grid/lattice/Lattice_reduction_gpu.h b/Grid/lattice/Lattice_reduction_gpu.h index 73a704f5..c685a2c0 100644 --- a/Grid/lattice/Lattice_reduction_gpu.h +++ b/Grid/lattice/Lattice_reduction_gpu.h @@ -198,7 +198,7 @@ __global__ void reduceKernel(const vobj *lat, sobj *buffer, Iterator n) { // Possibly promote to double and sum ///////////////////////////////////////////////////////////////////////////////////////////////////////// template <class vobj> -inline typename vobj::scalar_objectD sumD_gpu_internal(const vobj *lat, Integer osites) +inline typename vobj::scalar_objectD sumD_gpu_small(const vobj *lat, Integer osites) { typedef typename vobj::scalar_objectD sobj; typedef decltype(lat) Iterator; @@ -207,7 +207,8 @@ inline typename vobj::scalar_objectD sumD_gpu_internal(const vobj *lat, Integer Integer size = osites*nsimd; Integer numThreads, numBlocks; - getNumBlocksAndThreads(size, sizeof(sobj), numThreads, numBlocks); + int ok = getNumBlocksAndThreads(size, sizeof(sobj), numThreads, numBlocks); + assert(ok); Integer smemSize = numThreads * sizeof(sobj); @@ -219,6 +220,37 @@ inline typename vobj::scalar_objectD sumD_gpu_internal(const vobj *lat, Integer auto result = buffer_v[0]; return result; } + +template <class vobj> +inline typename vobj::scalar_objectD sumD_gpu_large(const vobj *lat, Integer osites) +{ + typedef typename vobj::vector_type vector; + typedef typename vobj::scalar_typeD scalarD; + typedef typename vobj::scalar_objectD sobj; + sobj ret; + scalarD *ret_p = (scalarD *)&ret; + + const int words = sizeof(vobj)/sizeof(vector); + + Integer nsimd= vobj::Nsimd(); + Integer size = osites*nsimd; + Integer numThreads, numBlocks; + + Vector<vector> buffer(osites); + vector *dat = (vector *)lat; + vector *buf = &buffer[0]; + iScalar<vector> *tbuf =(iScalar<vector> *) &buffer[0]; + for(int w=0;w<words;w++) { + + accelerator_for(ss,osites,1,{ + buf[ss] = dat[ss*words+w]; + }); + + ret_p[w] = sumD_gpu_small(tbuf,osites); + } + return ret; +} + template <class vobj> inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) { @@ -236,23 +268,14 @@ inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) int ok = getNumBlocksAndThreads(size, sizeof(sobj), numThreads, numBlocks); if ( ok ) { - ret = sumD_gpu_internal(lat,osites); + ret = sumD_gpu_small(lat,osites); } else { - Vector<vector> buffer(osites); - vector *dat = (vector *)lat; - vector *buf = &buffer[0]; - iScalar<vector> *tbuf =(iScalar<vector> *) &buffer[0]; - for(int w=0;w<words;w++) { - - accelerator_for(ss,osites,1,{ - buf[ss] = dat[ss*words+w]; - }); - - ret_p[w] = sumD_gpu_internal(tbuf,osites); - } + ret = sumD_gpu_large(lat,osites); } return ret; } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// // Return as same precision as input performing reduction in double precision though ///////////////////////////////////////////////////////////////////////////////////////////////////////// From 694306f2022228b1b752fa467fe6a5583c68e7e4 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 1 Mar 2022 10:53:44 -0500 Subject: [PATCH 298/399] Configure for mac arm --- systems/mac-arm/config-command-mpi | 1 + 1 file changed, 1 insertion(+) create mode 100644 systems/mac-arm/config-command-mpi diff --git a/systems/mac-arm/config-command-mpi b/systems/mac-arm/config-command-mpi new file mode 100644 index 00000000..d1e75c39 --- /dev/null +++ b/systems/mac-arm/config-command-mpi @@ -0,0 +1 @@ +CXX=mpicxx-openmpi-mp CXXFLAGS=-I/opt/local/include/ LDFLAGS=-L/opt/local/lib/ ../../configure --enable-simd=GEN --enable-debug --enable-comms=mpi From e16fc5b2e4dd2d507c8c92a05f987a8aa1ec5c3e Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 1 Mar 2022 11:17:24 -0500 Subject: [PATCH 299/399] Threaded intranode comms transfer - ideally between NUMA domains --- Grid/threads/Accelerator.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index b427b304..12483185 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -481,9 +481,10 @@ inline void acceleratorCopySynchronise(void) { hipStreamSynchronize(copyStream); #define accelerator_for2d(iter1, num1, iter2, num2, nsimd, ... ) thread_for2d(iter1,num1,iter2,num2,{ __VA_ARGS__ }); accelerator_inline int acceleratorSIMTlane(int Nsimd) { return 0; } // CUDA specific -inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { memcpy(to,from,bytes);} -inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ memcpy(to,from,bytes);} -inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { memcpy(to,from,bytes);} + +inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { GridThread::bcopy(from,to,bytes);} +inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ GridThread::bcopy(from,to,bytes);} +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { GridThread::bcopy(from,to,bytes);} inline void acceleratorCopySynchronise(void) {}; inline int acceleratorIsCommunicable(void *ptr){ return 1; } From d4ae71b8806e535145a718a9e1e00a7315aaf5bc Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Wed, 2 Mar 2022 15:40:18 +0000 Subject: [PATCH 300/399] sum_gpu_large and sum_gpu templates added. --- Grid/lattice/Lattice_reduction.h | 16 ++++++++++++++++ Grid/lattice/Lattice_reduction_gpu.h | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/Grid/lattice/Lattice_reduction.h b/Grid/lattice/Lattice_reduction.h index c3478ab4..0ddac437 100644 --- a/Grid/lattice/Lattice_reduction.h +++ b/Grid/lattice/Lattice_reduction.h @@ -168,6 +168,22 @@ inline typename vobj::scalar_object sum(const Lattice<vobj> &arg) return ssum; } +template<class vobj> +inline typename vobj::scalar_object sum_large(const Lattice<vobj> &arg) +{ +#if defined(GRID_CUDA)||defined(GRID_HIP) + autoView( arg_v, arg, AcceleratorRead); + Integer osites = arg.Grid()->oSites(); + auto ssum= sum_gpu_large(&arg_v[0],osites); +#else + autoView(arg_v, arg, CpuRead); + Integer osites = arg.Grid()->oSites(); + auto ssum= sum_cpu(&arg_v[0],osites); +#endif + arg.Grid()->GlobalSum(ssum); + return ssum; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Deterministic Reduction operations //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Grid/lattice/Lattice_reduction_gpu.h b/Grid/lattice/Lattice_reduction_gpu.h index c685a2c0..c3422af3 100644 --- a/Grid/lattice/Lattice_reduction_gpu.h +++ b/Grid/lattice/Lattice_reduction_gpu.h @@ -288,6 +288,14 @@ inline typename vobj::scalar_object sum_gpu(const vobj *lat, Integer osites) return result; } +template <class vobj> +inline typename vobj::scalar_object sum_gpu_large(const vobj *lat, Integer osites) +{ + typedef typename vobj::scalar_object sobj; + sobj result; + result = sumD_gpu_large(lat,osites); + return result; +} NAMESPACE_END(Grid); From d1decee4cce0b9a9f02bb5521cb06840b20022ad Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Wed, 2 Mar 2022 16:54:23 +0000 Subject: [PATCH 301/399] Cleaned up unused variables in Lattice_reduction_gpu.h --- Grid/lattice/Lattice_reduction_gpu.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Grid/lattice/Lattice_reduction_gpu.h b/Grid/lattice/Lattice_reduction_gpu.h index c3422af3..bad86d2a 100644 --- a/Grid/lattice/Lattice_reduction_gpu.h +++ b/Grid/lattice/Lattice_reduction_gpu.h @@ -232,10 +232,6 @@ inline typename vobj::scalar_objectD sumD_gpu_large(const vobj *lat, Integer osi const int words = sizeof(vobj)/sizeof(vector); - Integer nsimd= vobj::Nsimd(); - Integer size = osites*nsimd; - Integer numThreads, numBlocks; - Vector<vector> buffer(osites); vector *dat = (vector *)lat; vector *buf = &buffer[0]; @@ -258,10 +254,7 @@ inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) typedef typename vobj::scalar_typeD scalarD; typedef typename vobj::scalar_objectD sobj; sobj ret; - scalarD *ret_p = (scalarD *)&ret; - const int words = sizeof(vobj)/sizeof(vector); - Integer nsimd= vobj::Nsimd(); Integer size = osites*nsimd; Integer numThreads, numBlocks; @@ -275,7 +268,6 @@ inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) return ret; } - ///////////////////////////////////////////////////////////////////////////////////////////////////////// // Return as same precision as input performing reduction in double precision though ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -297,5 +289,4 @@ inline typename vobj::scalar_object sum_gpu_large(const vobj *lat, Integer osite return result; } - NAMESPACE_END(Grid); From d5b2323a579ca3b8e95fb6686a0fdf7d151a4503 Mon Sep 17 00:00:00 2001 From: Felix Ziegler <felix.ziegler@ed.ac.uk> Date: Mon, 7 Mar 2022 14:44:24 +0000 Subject: [PATCH 302/399] included Cayley-Hamilton exponentiation for the compact Wilson exp clover, bug fix for inverse of exp clover --- Grid/qcd/action/fermion/CloverHelpers.h | 296 +++++++++++++++++++----- 1 file changed, 240 insertions(+), 56 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index 7f7ad2b7..134ee161 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -35,7 +35,7 @@ #include <Grid/qcd/action/fermion/WilsonCloverHelpers.h> //////////////////////////////////////////// -// Standard Clover +// Standard Clover // (4+m0) + csw * clover_term // Exp Clover // (4+m0) * exp(csw/(4+m0) clover_term) @@ -46,10 +46,10 @@ NAMESPACE_BEGIN(Grid); ////////////////////////////////// -// Generic Standard Clover +// Generic Standard Clover ////////////////////////////////// -template<class Impl> +template<class Impl> class CloverHelpers: public WilsonCloverHelpers<Impl> { public: @@ -61,7 +61,7 @@ public: static void Instantiate(CloverField& CloverTerm, CloverField& CloverTermInv, RealD csw_t, RealD diag_mass) { GridBase *grid = CloverTerm.Grid(); CloverTerm += diag_mass; - + int lvol = grid->lSites(); int DimRep = Impl::Dimension; { @@ -83,7 +83,7 @@ public: EigenCloverOp(a + j * DimRep, b + k * DimRep) = std::complex<double>(zz); } // if (site==0) std::cout << "site =" << site << "\n" << EigenCloverOp << std::endl; - + EigenInvCloverOp = EigenCloverOp.inverse(); //std::cout << EigenInvCloverOp << std::endl; for (int j = 0; j < Ns; j++) @@ -106,10 +106,10 @@ public: ////////////////////////////////// -// Generic Exp Clover +// Generic Exp Clover ////////////////////////////////// -template<class Impl> +template<class Impl> class ExpCloverHelpers: public WilsonCloverHelpers<Impl> { public: @@ -122,9 +122,9 @@ public: // Can this be avoided? static void IdentityTimesC(const CloverField& in, RealD c) { int DimRep = Impl::Dimension; - + autoView(in_v, in, AcceleratorWrite); - + accelerator_for(ss, in.Grid()->oSites(), 1, { for (int sa=0; sa<Ns; sa++) for (int ca=0; ca<DimRep; ca++) @@ -150,12 +150,12 @@ public: static void Instantiate(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { GridBase* grid = Clover.Grid(); CloverField ExpClover(grid); - + int NMAX = getNMAX(Clover, 3.*csw_t/diag_mass); - + // csw/(diag_mass) * clover Clover *= (1.0/diag_mass); - + // Taylor expansion, slow but generic // Horner scheme: a0 + a1 x + a2 x^2 + .. = a0 + x (a1 + x(...)) // qN = cN @@ -164,14 +164,24 @@ public: cn[0] = 1.0; for (int i=1; i<=NMAX; i++) cn[i] = cn[i-1] / RealD(i); - + ExpClover = Zero(); IdentityTimesC(ExpClover, cn[NMAX]); for (int i=NMAX-1; i>=0; i--) ExpClover = ExpClover * Clover + cn[i]; - + + // prepare inverse + CloverInv = (-1.0)*Clover; + Clover = ExpClover * diag_mass; - CloverInv = adj(ExpClover) * (1.0/diag_mass); + + ExpClover = Zero(); + IdentityTimesC(ExpClover, cn[NMAX]); + for (int i=NMAX-1; i>=0; i--) + ExpClover = ExpClover * CloverInv + cn[i]; + + CloverInv = ExpClover * (1.0/diag_mass); + } static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { @@ -182,11 +192,11 @@ public: ////////////////////////////////// -// Compact Standard Clover +// Compact Standard Clover ////////////////////////////////// -template<class Impl> +template<class Impl> class CompactCloverHelpers: public CompactWilsonCloverHelpers<Impl>, public WilsonCloverHelpers<Impl> { public: @@ -197,7 +207,7 @@ public: typedef WilsonCloverHelpers<Impl> Helpers; typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; - + static void MassTerm(CloverField& Clover, RealD diag_mass) { Clover += diag_mass; } @@ -219,10 +229,9 @@ public: }; ////////////////////////////////// -// Compact Exp Clover +// Compact Exp Clover ////////////////////////////////// - template<class Impl> class CompactExpCloverHelpers: public CompactWilsonCloverHelpers<Impl> { public: @@ -230,22 +239,28 @@ public: INHERIT_IMPL_TYPES(Impl); INHERIT_CLOVER_TYPES(Impl); INHERIT_COMPACT_CLOVER_TYPES(Impl); - - template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; + + template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; - + +/* static void plusIdentity(const CloverField& in) { int DimRep = Impl::Dimension; - + autoView(in_v, in, AcceleratorWrite); - + accelerator_for(ss, in.Grid()->oSites(), 1, { for (int sa=0; sa<Ns; sa++) for (int ca=0; ca<DimRep; ca++) in_v[ss]()(sa,sa)(ca,ca) += 1.0; }); } - +*/ + static void MassTerm(CloverField& Clover, RealD diag_mass) { + // do nothing! + // mass term is multiplied to exp(Clover) below + } + static int getNMAX(RealD prec, RealD R) { /* compute stop condition for exponential */ int NMAX=1; @@ -260,50 +275,219 @@ public: static int getNMAX(Lattice<iImplCloverDiagonal<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);} static int getNMAX(Lattice<iImplCloverDiagonal<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);} - - static void MassTerm(CloverField& Clover, RealD diag_mass) { - // csw/(diag_mass) * clover - Clover = Clover * (1.0/diag_mass); - } - - static void Instantiate(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, + + static void ExponentiateHermitean6by6(const iMatrix<ComplexD,6> &arg, const RealD& alpha, const std::vector<RealD>& cN, const int Niter, iMatrix<ComplexD,6>& dest){ + + typedef iMatrix<ComplexD,6> mat; + + RealD qn[6]; + RealD qnold[6]; + RealD p[5]; + RealD trA2, trA3, trA4; + + mat A2, A3, A4, A5; + A2 = alpha * alpha * arg * arg; + A3 = alpha * arg * A2; + A4 = A2 * A2; + A5 = A2 * A3; + + trA2 = toReal( trace(A2) ); + trA3 = toReal( trace(A3) ); + trA4 = toReal( trace(A4)); + + p[0] = toReal( trace(A3 * A3)) / 6.0 - 0.125 * trA4 * trA2 - trA3 * trA3 / 18.0 + trA2 * trA2 * trA2/ 48.0; + p[1] = toReal( trace(A5)) / 5.0 - trA3 * trA2 / 6.0; + p[2] = toReal( trace(A4)) / 4.0 - 0.125 * trA2 * trA2; + p[3] = trA3 / 3.0; + p[4] = 0.5 * trA2; + + qnold[0] = cN[Niter]; + qnold[1] = 0.0; + qnold[2] = 0.0; + qnold[3] = 0.0; + qnold[4] = 0.0; + qnold[5] = 0.0; + + for(int i = Niter-1; i >= 0; i--) + { + qn[0] = p[0] * qnold[5] + cN[i]; + qn[1] = p[1] * qnold[5] + qnold[0]; + qn[2] = p[2] * qnold[5] + qnold[1]; + qn[3] = p[3] * qnold[5] + qnold[2]; + qn[4] = p[4] * qnold[5] + qnold[3]; + qn[5] = qnold[4]; + + qnold[0] = qn[0]; + qnold[1] = qn[1]; + qnold[2] = qn[2]; + qnold[3] = qn[3]; + qnold[4] = qn[4]; + qnold[5] = qn[5]; + } + + mat unit(1.0); + + dest = (qn[0] * unit + qn[1] * alpha * arg + qn[2] * A2 + qn[3] * A3 + qn[4] * A4 + qn[5] * A5); + + } + + static void Instantiate(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, CloverDiagonalField& DiagonalInv, CloverTriangleField& TriangleInv, RealD csw_t, RealD diag_mass) { + GridBase* grid = Diagonal.Grid(); int NMAX = getNMAX(Diagonal, 3.*csw_t/diag_mass); - - // To be optimized: implement exp in improved layout - // START: code to be replaced - CloverField Clover(grid), ExpClover(grid); - - CompactHelpers::ConvertLayout(Diagonal, Triangle, Clover); - - ExpClover = Clover; - plusIdentity(ExpClover); // 1 + Clover - - RealD pref = 1.0; - for (int i=2; i<=NMAX; i++) { - Clover = Clover * Clover; - pref /= (RealD)(i); - ExpClover += pref * Clover; + + // + // Implementation completely in Daniel's layout + // + + // Taylor expansion with Cayley-Hamilton recursion + // underlying Horner scheme as above + std::vector<RealD> cn(NMAX+1); + cn[0] = 1.0; + for (int i=1; i<=NMAX; i++){ + cn[i] = cn[i-1] / RealD(i); } - - // Convert the data layout of the clover terms - CompactHelpers::ConvertLayout(ExpClover, Diagonal, Triangle); - CompactHelpers::ConvertLayout(adj(ExpClover), DiagonalInv, TriangleInv); - // END: code to be replaced - + + // Taken over from Daniel's implementation + conformable(Diagonal, Triangle); + + long lsites = grid->lSites(); + + typedef typename SiteCloverDiagonal::scalar_object scalar_object_diagonal; + typedef typename SiteCloverTriangle::scalar_object scalar_object_triangle; + typedef iMatrix<ComplexD,6> mat; + + autoView(diagonal_v, Diagonal, CpuRead); + autoView(triangle_v, Triangle, CpuRead); + autoView(diagonalExp_v, Diagonal, CpuWrite); + autoView(triangleExp_v, Triangle, CpuWrite); + autoView(diagonalExpInv_v, DiagonalInv, CpuWrite); + autoView(triangleExpInv_v, TriangleInv, CpuWrite); + + thread_for(site, lsites, { // NOTE: Not on GPU because of (peek/poke)LocalSite + + mat srcCloverOpUL(0.0); // upper left block + mat srcCloverOpLR(0.0); // lower right block + mat ExpCloverOp; + + scalar_object_diagonal diagonal_tmp = Zero(); + scalar_object_diagonal diagonal_exp_tmp = Zero(); + scalar_object_triangle triangle_tmp = Zero(); + scalar_object_triangle triangle_exp_tmp = Zero(); + + Coordinate lcoor; + grid->LocalIndexToLocalCoor(site, lcoor); + + peekLocalSite(diagonal_tmp, diagonal_v, lcoor); + peekLocalSite(triangle_tmp, triangle_v, lcoor); + + // block = 0 + int block; + block = 0; + for(int i = 0; i < 6; i++){ + for(int j = 0; j < 6; j++){ + if (i == j){ + srcCloverOpUL(i,j) = static_cast<ComplexD>(TensorRemove(diagonal_tmp()(block)(i))); + } + else{ + srcCloverOpUL(i,j) = static_cast<ComplexD>(TensorRemove(CompactHelpers::triangle_elem(triangle_tmp, block, i, j))); + } + } + } + // block = 1 + block = 1; + for(int i = 0; i < 6; i++){ + for(int j = 0; j < 6; j++){ + if (i == j){ + srcCloverOpLR(i,j) = static_cast<ComplexD>(TensorRemove(diagonal_tmp()(block)(i))); + } + else{ + srcCloverOpLR(i,j) = static_cast<ComplexD>(TensorRemove(CompactHelpers::triangle_elem(triangle_tmp, block, i, j))); + } + } + } + + // exp(Clover) + + ExponentiateHermitean6by6(srcCloverOpUL,1.0/diag_mass,cn,NMAX,ExpCloverOp); + + block = 0; + for(int i = 0; i < 6; i++){ + for(int j = 0; j < 6; j++){ + if (i == j){ + diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); + } + else if(i < j){ + triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); + } + } + } + + ExponentiateHermitean6by6(srcCloverOpLR,1.0/diag_mass,cn,NMAX,ExpCloverOp); + + block = 1; + for(int i = 0; i < 6; i++){ + for(int j = 0; j < 6; j++){ + if (i == j){ + diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); + } + else if(i < j){ + triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); + } + } + } + + pokeLocalSite(diagonal_exp_tmp, diagonalExp_v, lcoor); + pokeLocalSite(triangle_exp_tmp, triangleExp_v, lcoor); + + // inverse exp(-Clover) + + ExponentiateHermitean6by6(srcCloverOpUL,-1.0/diag_mass,cn,NMAX,ExpCloverOp); + + block = 0; + for(int i = 0; i < 6; i++){ + for(int j = 0; j < 6; j++){ + if (i == j){ + diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); + } + else if(i < j){ + triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); + } + } + } + + ExponentiateHermitean6by6(srcCloverOpLR,-1.0/diag_mass,cn,NMAX,ExpCloverOp); + + block = 1; + for(int i = 0; i < 6; i++){ + for(int j = 0; j < 6; j++){ + if (i == j){ + diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); + } + else if(i < j){ + triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); + } + } + } + + pokeLocalSite(diagonal_exp_tmp, diagonalExpInv_v, lcoor); + pokeLocalSite(triangle_exp_tmp, triangleExpInv_v, lcoor); + }); + Diagonal = Diagonal * diag_mass; Triangle = Triangle * diag_mass; + DiagonalInv = DiagonalInv*(1.0/diag_mass); TriangleInv = TriangleInv*(1.0/diag_mass); } - + static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { assert(0); } - + }; From 56c089d347aa7f40cb17b05f12dec7021a356066 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Mon, 7 Mar 2022 16:29:19 +0000 Subject: [PATCH 303/399] Removed leftover comments --- Grid/qcd/action/fermion/CloverHelpers.h | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index 134ee161..f7f81421 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -74,7 +74,7 @@ public: Eigen::MatrixXcd EigenInvCloverOp = Eigen::MatrixXcd::Zero(Ns * DimRep, Ns * DimRep); typename SiteClover::scalar_object Qx = Zero(), Qxinv = Zero(); peekLocalSite(Qx, CTv, lcoor); - //if (csw!=0){ + for (int j = 0; j < Ns; j++) for (int k = 0; k < Ns; k++) for (int a = 0; a < DimRep; a++) @@ -82,17 +82,13 @@ public: auto zz = Qx()(j, k)(a, b); EigenCloverOp(a + j * DimRep, b + k * DimRep) = std::complex<double>(zz); } - // if (site==0) std::cout << "site =" << site << "\n" << EigenCloverOp << std::endl; EigenInvCloverOp = EigenCloverOp.inverse(); - //std::cout << EigenInvCloverOp << std::endl; for (int j = 0; j < Ns; j++) for (int k = 0; k < Ns; k++) for (int a = 0; a < DimRep; a++) for (int b = 0; b < DimRep; b++) Qxinv()(j, k)(a, b) = EigenInvCloverOp(a + j * DimRep, b + k * DimRep); - // if (site==0) std::cout << "site =" << site << "\n" << EigenInvCloverOp << std::endl; - // } pokeLocalSite(Qxinv, CTIv, lcoor); }); } @@ -153,7 +149,6 @@ public: int NMAX = getNMAX(Clover, 3.*csw_t/diag_mass); - // csw/(diag_mass) * clover Clover *= (1.0/diag_mass); // Taylor expansion, slow but generic @@ -243,19 +238,6 @@ public: template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; -/* - static void plusIdentity(const CloverField& in) { - int DimRep = Impl::Dimension; - - autoView(in_v, in, AcceleratorWrite); - - accelerator_for(ss, in.Grid()->oSites(), 1, { - for (int sa=0; sa<Ns; sa++) - for (int ca=0; ca<DimRep; ca++) - in_v[ss]()(sa,sa)(ca,ca) += 1.0; - }); - } -*/ static void MassTerm(CloverField& Clover, RealD diag_mass) { // do nothing! // mass term is multiplied to exp(Clover) below @@ -383,7 +365,6 @@ public: peekLocalSite(diagonal_tmp, diagonal_v, lcoor); peekLocalSite(triangle_tmp, triangle_v, lcoor); - // block = 0 int block; block = 0; for(int i = 0; i < 6; i++){ @@ -396,7 +377,6 @@ public: } } } - // block = 1 block = 1; for(int i = 0; i < 6; i++){ for(int j = 0; j < 6; j++){ From 451e7972fdcbdf88ad8e6a7fc35795081284fcae Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Mon, 7 Mar 2022 17:43:33 +0000 Subject: [PATCH 304/399] Reintroduced explicit inversion of the Clover term in case of the CompactExpClover because of the open boundary O(a) improvement. Changed the timing output to GridLogDebug --- Grid/qcd/action/fermion/CloverHelpers.h | 46 ++----------------- ...CompactWilsonCloverFermionImplementation.h | 41 +++++++++-------- .../WilsonCloverFermionImplementation.h | 19 ++++---- 3 files changed, 34 insertions(+), 72 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index f7f81421..ad456bde 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -207,13 +207,13 @@ public: Clover += diag_mass; } - static void Instantiate(CloverDiagonalField& Diagonal, + static void Exponentiate_Clover(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, CloverDiagonalField& DiagonalInv, CloverTriangleField& TriangleInv, RealD csw_t, RealD diag_mass) { - // Invert the clover term in the improved layout - CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); + + // Do nothing } // TODO: implement Cmunu for better performances with compact layout, but don't do it @@ -313,7 +313,7 @@ public: } - static void Instantiate(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, + static void Exponentiate_Clover(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, CloverDiagonalField& DiagonalInv, CloverTriangleField& TriangleInv, RealD csw_t, RealD diag_mass) { @@ -345,8 +345,6 @@ public: autoView(triangle_v, Triangle, CpuRead); autoView(diagonalExp_v, Diagonal, CpuWrite); autoView(triangleExp_v, Triangle, CpuWrite); - autoView(diagonalExpInv_v, DiagonalInv, CpuWrite); - autoView(triangleExpInv_v, TriangleInv, CpuWrite); thread_for(site, lsites, { // NOTE: Not on GPU because of (peek/poke)LocalSite @@ -421,46 +419,10 @@ public: pokeLocalSite(diagonal_exp_tmp, diagonalExp_v, lcoor); pokeLocalSite(triangle_exp_tmp, triangleExp_v, lcoor); - - // inverse exp(-Clover) - - ExponentiateHermitean6by6(srcCloverOpUL,-1.0/diag_mass,cn,NMAX,ExpCloverOp); - - block = 0; - for(int i = 0; i < 6; i++){ - for(int j = 0; j < 6; j++){ - if (i == j){ - diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); - } - else if(i < j){ - triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); - } - } - } - - ExponentiateHermitean6by6(srcCloverOpLR,-1.0/diag_mass,cn,NMAX,ExpCloverOp); - - block = 1; - for(int i = 0; i < 6; i++){ - for(int j = 0; j < 6; j++){ - if (i == j){ - diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); - } - else if(i < j){ - triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); - } - } - } - - pokeLocalSite(diagonal_exp_tmp, diagonalExpInv_v, lcoor); - pokeLocalSite(triangle_exp_tmp, triangleExpInv_v, lcoor); }); Diagonal = Diagonal * diag_mass; Triangle = Triangle * diag_mass; - - DiagonalInv = DiagonalInv*(1.0/diag_mass); - TriangleInv = TriangleInv*(1.0/diag_mass); } diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index b42957ac..5120680b 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -326,16 +326,20 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie double t4 = usecond(); CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); - // Possible modify the boundary values + // Exponentiate the clover (nothing happens in case of the standard clover) double t5 = usecond(); + CloverHelpers::Exponentiate_Clover(Diagonal, Triangle, DiagonalInv, TriangleInv, csw_t, this->diag_mass); + + // Possible modify the boundary values + double t6 = usecond(); if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); - // Instantiate based on clover policy - double t6 = usecond(); - CloverHelpers::Instantiate(Diagonal, Triangle, DiagonalInv, TriangleInv, csw_t, this->diag_mass); + // Invert the Clover term (explicit inversion needed for the improvement in case of open boundary conditions) + double t7 = usecond(); + CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); // Fill the remaining clover fields - double t7 = usecond(); + double t8 = usecond(); pickCheckerboard(Even, DiagonalEven, Diagonal); pickCheckerboard(Even, TriangleEven, Triangle); pickCheckerboard(Odd, DiagonalOdd, Diagonal); @@ -346,20 +350,19 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie pickCheckerboard(Odd, TriangleInvOdd, TriangleInv); // Report timings - double t8 = usecond(); -#if 0 - std::cout << GridLogMessage << "CompactWilsonCloverFermion::ImportGauge timings:" - << " WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 - << ", allocations = " << (t2 - t1) / 1e6 - << ", field strength = " << (t3 - t2) / 1e6 - << ", fill clover = " << (t4 - t3) / 1e6 - << ", convert = " << (t5 - t4) / 1e6 - << ", boundaries = " << (t6 - t5) / 1e6 - << ", instantiate = " << (t7 - t6) / 1e6 - << ", pick cbs = " << (t8 - t7) / 1e6 - << ", total = " << (t8 - t0) / 1e6 - << std::endl; -#endif + double t9 = usecond(); + + std::cout << GridLogDebug << "CompactWilsonCloverFermion::ImportGauge timings:" << std::endl; + std::cout << GridLogDebug << "WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 << std::endl; + std::cout << GridLogDebug << "allocations = " << (t2 - t1) / 1e6 << std::endl; + std::cout << GridLogDebug << "field strength = " << (t3 - t2) / 1e6 << std::endl; + std::cout << GridLogDebug << "fill clover = " << (t4 - t3) / 1e6 << std::endl; + std::cout << GridLogDebug << "convert = " << (t5 - t4) / 1e6 << std::endl; + std::cout << GridLogDebug << "exponentiation = " << (t6 - t5) / 1e6 << std::endl; + std::cout << GridLogDebug << "boundaries = " << (t7 - t6) / 1e6 << std::endl; + std::cout << GridLogDebug << "inversions = " << (t8 - t7) / 1e6 << std::endl; + std::cout << GridLogDebug << "pick cbs = " << (t9 - t8) / 1e6 << std::endl; + std::cout << GridLogDebug << "total = " << (t9 - t0) / 1e6 << std::endl; } NAMESPACE_END(Grid); diff --git a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h index 0991e274..2a7e7535 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonCloverFermionImplementation.h @@ -150,17 +150,14 @@ void WilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeField &_Um pickCheckerboard(Odd, CloverTermInvDagOdd, adj(CloverTermInv)); double t6 = usecond(); -#if 0 - std::cout << GridLogMessage << "WilsonCloverFermion::ImportGauge timings:" - << " WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 - << ", allocations = " << (t2 - t1) / 1e6 - << ", field strength = " << (t3 - t2) / 1e6 - << ", fill clover = " << (t4 - t3) / 1e6 - << ", instantiation = " << (t5 - t4) / 1e6 - << ", pick cbs = " << (t6 - t5) / 1e6 - << ", total = " << (t6 - t0) / 1e6 - << std::endl; -#endif + std::cout << GridLogDebug << "WilsonCloverFermion::ImportGauge timings:" << std::endl; + std::cout << GridLogDebug << "WilsonFermion::Importgauge = " << (t1 - t0) / 1e6 << std::endl; + std::cout << GridLogDebug << "allocations = " << (t2 - t1) / 1e6 << std::endl; + std::cout << GridLogDebug << "field strength = " << (t3 - t2) / 1e6 << std::endl; + std::cout << GridLogDebug << "fill clover = " << (t4 - t3) / 1e6 << std::endl; + std::cout << GridLogDebug << "instantiation = " << (t5 - t4) / 1e6 << std::endl; + std::cout << GridLogDebug << "pick cbs = " << (t6 - t5) / 1e6 << std::endl; + std::cout << GridLogDebug << "total = " << (t6 - t0) / 1e6 << std::endl; } template<class Impl, class CloverHelpers> From 0c0c2b1e20d3290ca2d54fe3a8a9339aabc6d9dd Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 8 Mar 2022 09:44:51 +0000 Subject: [PATCH 305/399] Unnecessary arguments of CloverHelpers::Exponentiate_Clover removed. --- Grid/qcd/action/fermion/CloverHelpers.h | 6 +----- .../CompactWilsonCloverFermionImplementation.h | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index ad456bde..cd62f515 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -209,8 +209,6 @@ public: static void Exponentiate_Clover(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, - CloverDiagonalField& DiagonalInv, - CloverTriangleField& TriangleInv, RealD csw_t, RealD diag_mass) { // Do nothing @@ -313,9 +311,7 @@ public: } - static void Exponentiate_Clover(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, - CloverDiagonalField& DiagonalInv, CloverTriangleField& TriangleInv, - RealD csw_t, RealD diag_mass) { + static void Exponentiate_Clover(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, RealD csw_t, RealD diag_mass) { GridBase* grid = Diagonal.Grid(); int NMAX = getNMAX(Diagonal, 3.*csw_t/diag_mass); diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 5120680b..2a53a254 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -328,7 +328,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie // Exponentiate the clover (nothing happens in case of the standard clover) double t5 = usecond(); - CloverHelpers::Exponentiate_Clover(Diagonal, Triangle, DiagonalInv, TriangleInv, csw_t, this->diag_mass); + CloverHelpers::Exponentiate_Clover(Diagonal, Triangle, csw_t, this->diag_mass); // Possible modify the boundary values double t6 = usecond(); From 76c294a7bae149e9e52b980396a4265c77d3d2d1 Mon Sep 17 00:00:00 2001 From: Christoph Lehner <christoph@lhnr.de> Date: Tue, 8 Mar 2022 13:55:16 +0100 Subject: [PATCH 306/399] open bc fix --- .../CompactWilsonCloverFermionImplementation.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 3dfcb133..4e534f6e 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -64,8 +64,11 @@ CompactWilsonCloverFermion<Impl>::CompactWilsonCloverFermion(GaugeField& _Umu, csw_r /= clover_anisotropy.xi_0; ImportGauge(_Umu); - if (open_boundaries) + if (open_boundaries) { + this->BoundaryMaskEven.Checkerboard() = Even; + this->BoundaryMaskOdd.Checkerboard() = Odd; CompactHelpers::SetupMasks(this->BoundaryMask, this->BoundaryMaskEven, this->BoundaryMaskOdd); + } } template<class Impl> From 92a83a9eb33c54467b29ff557bad4aed90c4f65e Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 16 Mar 2022 17:14:36 +0000 Subject: [PATCH 307/399] Performance improve for Tesseract --- Grid/threads/Accelerator.h | 6 +++--- Grid/threads/Threads.h | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 12483185..389f2cc4 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -482,9 +482,9 @@ inline void acceleratorCopySynchronise(void) { hipStreamSynchronize(copyStream); accelerator_inline int acceleratorSIMTlane(int Nsimd) { return 0; } // CUDA specific -inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { GridThread::bcopy(from,to,bytes);} -inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ GridThread::bcopy(from,to,bytes);} -inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { GridThread::bcopy(from,to,bytes);} +inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { thread_bcopy(from,to,bytes); } +inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ thread_bcopy(from,to,bytes);} +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { thread_bcopy(from,to,bytes);} inline void acceleratorCopySynchronise(void) {}; inline int acceleratorIsCommunicable(void *ptr){ return 1; } diff --git a/Grid/threads/Threads.h b/Grid/threads/Threads.h index a9fa13ea..6887134d 100644 --- a/Grid/threads/Threads.h +++ b/Grid/threads/Threads.h @@ -72,3 +72,20 @@ Author: paboyle <paboyle@ph.ed.ac.uk> #define thread_region DO_PRAGMA(omp parallel) #define thread_critical DO_PRAGMA(omp critical) +#ifdef GRID_OMP +inline void thread_bcopy(void *from, void *to,size_t bytes) +{ + uint64_t *ufrom = (uint64_t *)from; + uint64_t *uto = (uint64_t *)to; + assert(bytes%8==0); + uint64_t words=bytes/8; + thread_for(w,words,{ + uto[w] = ufrom[w]; + }); +} +#else +inline void thread_bcopy(void *from, void *to,size_t bytes) +{ + bcopy(from,to,bytes); +} +#endif From 317bdcf158165d30ae1fd009026b253384357e08 Mon Sep 17 00:00:00 2001 From: Christoph Lehner <christoph@lhnr.de> Date: Thu, 24 Mar 2022 13:10:47 +0100 Subject: [PATCH 308/399] nerscio parametrization --- Grid/parallelIO/NerscIO.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Grid/parallelIO/NerscIO.h b/Grid/parallelIO/NerscIO.h index 99011e25..dabef9e5 100644 --- a/Grid/parallelIO/NerscIO.h +++ b/Grid/parallelIO/NerscIO.h @@ -209,26 +209,27 @@ public: template<class GaugeStats=PeriodicGaugeStatistics> static inline void writeConfiguration(Lattice<vLorentzColourMatrixD > &Umu, std::string file, - std::string ens_label = std::string("DWF")) + std::string ens_label = std::string("DWF"), + std::string ens_id = std::string("UKQCD"), + unsigned int sequence_number = 1) { - writeConfiguration(Umu,file,0,1,ens_label); + writeConfiguration(Umu,file,0,1,ens_label,ens_id,sequence_number); } template<class GaugeStats=PeriodicGaugeStatistics> static inline void writeConfiguration(Lattice<vLorentzColourMatrixD > &Umu, std::string file, int two_row, int bits32, - std::string ens_label = std::string("DWF")) + std::string ens_label = std::string("DWF"), + std::string ens_id = std::string("UKQCD"), + unsigned int sequence_number = 1) { typedef vLorentzColourMatrixD vobj; typedef typename vobj::scalar_object sobj; FieldMetaData header; - /////////////////////////////////////////// - // Following should become arguments - /////////////////////////////////////////// - header.sequence_number = 1; - header.ensemble_id = std::string("UKQCD"); + header.sequence_number = sequence_number; + header.ensemble_id = ens_id; header.ensemble_label = ens_label; typedef LorentzColourMatrixD fobj3D; From 0542eaf1da28daad4f6e346b78c2c8317e9566fe Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Thu, 31 Mar 2022 17:02:09 +0100 Subject: [PATCH 309/399] First version of conserved current contraction for Wilson type quarks --- .../WilsonFermionImplementation.h | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 84ac25c1..12e65d24 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -603,7 +603,37 @@ void WilsonFermion<Impl>::ContractConservedCurrent(PropagatorField &q_in_1, conformable(_grid, q_in_1.Grid()); conformable(_grid, q_in_2.Grid()); conformable(_grid, q_out.Grid()); - assert(0); + + PropagatorField tmp_shifted(UGrid); + PropagatorField g5Lg5(UGrid); + PropagatorField R(UGrid); + PropagatorField gmuR(UGrid); + + Gamma::Algebra Gmu [] = { + Gamma::Algebra::GammaX, + Gamma::Algebra::GammaY, + Gamma::Algebra::GammaZ, + Gamma::Algebra::GammaT, + }; + Gamma gmu=Gamma(Gmu[mu]); + + tmp_shifted=Cshift(q_in_1,mu,1); + g5Lg5=g5*tmp_shifted*g5; + R=q_in_2; + gmuR=gmu*R; + + qout=adj(g5Lg5)*R; + qout+=adj(g5Lg5)*gmuR; + + g5Lg5=g5*q_in_1*g5; + tmp_shifted=Cshift(q_in_2,mu,1); + Impl::multLinkField(R,this->Umu,tmp_shifted,mu); + gmuR=gmu*R; + + qout-=adj(g5Lg5)*R; + qout+=adj(g5Lg5)*gmuR; + + qout/=2; } From cdf31d52c15109102b31f88b135bbaff8f216d30 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Thu, 31 Mar 2022 17:04:35 +0100 Subject: [PATCH 310/399] GaugeGrid and typo fixed --- .../implementation/WilsonFermionImplementation.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 12e65d24..60ccdc2e 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -603,6 +603,7 @@ void WilsonFermion<Impl>::ContractConservedCurrent(PropagatorField &q_in_1, conformable(_grid, q_in_1.Grid()); conformable(_grid, q_in_2.Grid()); conformable(_grid, q_out.Grid()); + auto UGrid= this->GaugeGrid(); PropagatorField tmp_shifted(UGrid); PropagatorField g5Lg5(UGrid); @@ -622,18 +623,18 @@ void WilsonFermion<Impl>::ContractConservedCurrent(PropagatorField &q_in_1, R=q_in_2; gmuR=gmu*R; - qout=adj(g5Lg5)*R; - qout+=adj(g5Lg5)*gmuR; + q_out=adj(g5Lg5)*R; + q_out+=adj(g5Lg5)*gmuR; g5Lg5=g5*q_in_1*g5; tmp_shifted=Cshift(q_in_2,mu,1); Impl::multLinkField(R,this->Umu,tmp_shifted,mu); gmuR=gmu*R; - qout-=adj(g5Lg5)*R; - qout+=adj(g5Lg5)*gmuR; + q_out-=adj(g5Lg5)*R; + q_out+=adj(g5Lg5)*gmuR; - qout/=2; + q_out/=2; } From fe993c0836fe6c234e5e34112beecf0a5d98cadd Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Thu, 31 Mar 2022 17:08:17 +0100 Subject: [PATCH 311/399] /=2 replaced by *=0.5 --- .../action/fermion/implementation/WilsonFermionImplementation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 60ccdc2e..3e7bb291 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -634,7 +634,7 @@ void WilsonFermion<Impl>::ContractConservedCurrent(PropagatorField &q_in_1, q_out-=adj(g5Lg5)*R; q_out+=adj(g5Lg5)*gmuR; - q_out/=2; + q_out*=0.5; } From 603fd967477fd2b5b8ec06dfbcb948c13b46b151 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Fri, 1 Apr 2022 10:58:56 +0100 Subject: [PATCH 312/399] Missing link multiplication added. --- .../fermion/implementation/WilsonFermionImplementation.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 3e7bb291..c82dd172 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -619,7 +619,8 @@ void WilsonFermion<Impl>::ContractConservedCurrent(PropagatorField &q_in_1, Gamma gmu=Gamma(Gmu[mu]); tmp_shifted=Cshift(q_in_1,mu,1); - g5Lg5=g5*tmp_shifted*g5; + Impl::multLinkField(g5Lg5,this->Umu,tmp_shifted,mu); + g5Lg5=g5*g5Lg5*g5; R=q_in_2; gmuR=gmu*R; From 9e82c468ab8ad6d929e982b1759c0049da7f6254 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Fri, 1 Apr 2022 15:54:43 +0100 Subject: [PATCH 313/399] Multiplication of diagonal mass in exponentiate fixed for gpus --- Grid/qcd/action/fermion/CloverHelpers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index cd62f515..72727279 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -417,8 +417,8 @@ public: pokeLocalSite(triangle_exp_tmp, triangleExp_v, lcoor); }); - Diagonal = Diagonal * diag_mass; - Triangle = Triangle * diag_mass; + Diagonal *= diag_mass; + Triangle *= diag_mass; } From 427c8695feaba4de96e865a3754e07f669e6e121 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Fri, 1 Apr 2022 16:20:21 +0100 Subject: [PATCH 314/399] Change signs and prefactors for conserved current to mimic the 5d version. --- .../WilsonFermionImplementation.h | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index c82dd172..40e774b3 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -618,24 +618,22 @@ void WilsonFermion<Impl>::ContractConservedCurrent(PropagatorField &q_in_1, }; Gamma gmu=Gamma(Gmu[mu]); + g5Lg5=g5*q_in_1*g5; + tmp_shifted=Cshift(q_in_2,mu,1); + Impl::multLinkField(R,this->Umu,tmp_shifted,mu); + gmuR=gmu*R; + + q_out=adj(g5Lg5)*R; + q_out-=adj(g5Lg5)*gmuR; + tmp_shifted=Cshift(q_in_1,mu,1); Impl::multLinkField(g5Lg5,this->Umu,tmp_shifted,mu); g5Lg5=g5*g5Lg5*g5; R=q_in_2; gmuR=gmu*R; - q_out=adj(g5Lg5)*R; - q_out+=adj(g5Lg5)*gmuR; - - g5Lg5=g5*q_in_1*g5; - tmp_shifted=Cshift(q_in_2,mu,1); - Impl::multLinkField(R,this->Umu,tmp_shifted,mu); - gmuR=gmu*R; - q_out-=adj(g5Lg5)*R; - q_out+=adj(g5Lg5)*gmuR; - - q_out*=0.5; + q_out-=adj(g5Lg5)*gmuR; } From 6577a03d1641df1f94cc53f1accd0d5485e325d0 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Fri, 1 Apr 2022 18:39:12 +0100 Subject: [PATCH 315/399] Explcitly closed views in Exponentiate_Clover --- Grid/qcd/action/fermion/CloverHelpers.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index 72727279..67417fcd 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -417,6 +417,11 @@ public: pokeLocalSite(triangle_exp_tmp, triangleExp_v, lcoor); }); + diagonal_v.ViewClose(); + triangle_v.ViewClose(); + diagonalExp_v.ViewClose(); + triangleExp_v.ViewClose(); + Diagonal *= diag_mass; Triangle *= diag_mass; } From f23626a6b80a653c68d2a9d2860dc65c5be98f09 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Sat, 2 Apr 2022 11:32:15 +0100 Subject: [PATCH 316/399] End scope by additional block in CloverHelpers.h --- Grid/qcd/action/fermion/CloverHelpers.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index 67417fcd..c72a79e8 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -332,7 +332,7 @@ public: conformable(Diagonal, Triangle); long lsites = grid->lSites(); - + { typedef typename SiteCloverDiagonal::scalar_object scalar_object_diagonal; typedef typename SiteCloverTriangle::scalar_object scalar_object_triangle; typedef iMatrix<ComplexD,6> mat; @@ -416,11 +416,7 @@ public: pokeLocalSite(diagonal_exp_tmp, diagonalExp_v, lcoor); pokeLocalSite(triangle_exp_tmp, triangleExp_v, lcoor); }); - - diagonal_v.ViewClose(); - triangle_v.ViewClose(); - diagonalExp_v.ViewClose(); - triangleExp_v.ViewClose(); + } Diagonal *= diag_mass; Triangle *= diag_mass; From c8a824425bbb5b543d41340d91a78ecc9273011e Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 5 Apr 2022 10:58:22 +0100 Subject: [PATCH 317/399] Error message added if another conserved current than vector is requested for Wilson type fermions. --- .../WilsonFermionImplementation.h | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 40e774b3..69a7fb16 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -599,16 +599,22 @@ void WilsonFermion<Impl>::ContractConservedCurrent(PropagatorField &q_in_1, Current curr_type, unsigned int mu) { + if(curr_type != Current::Vector) + { + std::cout << GridLogError << "Only the conserved vector current is implemented so far." << std::endl; + exit(1); + } + Gamma g5(Gamma::Algebra::Gamma5); conformable(_grid, q_in_1.Grid()); conformable(_grid, q_in_2.Grid()); conformable(_grid, q_out.Grid()); auto UGrid= this->GaugeGrid(); - PropagatorField tmp_shifted(UGrid); - PropagatorField g5Lg5(UGrid); - PropagatorField R(UGrid); - PropagatorField gmuR(UGrid); + PropagatorField tmp_shifted(UGrid); + PropagatorField g5Lg5(UGrid); + PropagatorField R(UGrid); + PropagatorField gmuR(UGrid); Gamma::Algebra Gmu [] = { Gamma::Algebra::GammaX, @@ -647,6 +653,12 @@ void WilsonFermion<Impl>::SeqConservedCurrent(PropagatorField &q_in, unsigned int tmax, ComplexField &lattice_cmplx) { + if(curr_type != Current::Vector) + { + std::cout << GridLogError << "Only the conserved vector current is implemented so far." << std::endl; + exit(1); + } + conformable(_grid, q_in.Grid()); conformable(_grid, q_out.Grid()); assert(0); From d7191e5a026412c6c6c76a1f29961b4cb007506c Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 5 Apr 2022 11:48:56 +0100 Subject: [PATCH 318/399] SeqConservedCurrent implemented for Wilson fermions --- .../WilsonFermionImplementation.h | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 69a7fb16..6bfb9305 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -659,9 +659,45 @@ void WilsonFermion<Impl>::SeqConservedCurrent(PropagatorField &q_in, exit(1); } + int tshift = (mu == Nd-1) ? 1 : 0; + unsigned int LLt = GridDefaultLatt()[Tp]; conformable(_grid, q_in.Grid()); conformable(_grid, q_out.Grid()); - assert(0); + auto UGrid= this->GaugeGrid(); + + PropagatorField tmp(UGrid); + PropagatorField Utmp(UGrid); + PropagatorField L(UGrid); + PropagatorField zz (UGrid); + zz=Zero(); + LatticeInteger lcoor(UGrid); LatticeCoordinate(lcoor,Nd-1); + + Gamma::Algebra Gmu [] = { + Gamma::Algebra::GammaX, + Gamma::Algebra::GammaY, + Gamma::Algebra::GammaZ, + Gamma::Algebra::GammaT, + }; + Gamma gmu=Gamma(Gmu[mu]); + + tmp = Cshift(q_in,mu,1); + Impl::multLinkField(Utmp,this->Umu,tmp,mu); + tmp = ( Utmp*lattice_cmplx - gmu*Utmp*lattice_cmplx ); // Forward hop + tmp = where((lcoor>=tmin),tmp,zz); // Mask the time + q_out = where((lcoor<=tmax),tmp,zz); // Position of current complicated + + tmp = q_in *lattice_cmplx; + tmp = Cshift(tmp,mu,-1); + Impl::multLinkField(Utmp,this->Umu,tmp,mu+Nd); // Adjoint link + tmp = -( Utmp + gmu*Utmp ); + // Mask the time + if (tmax == LLt - 1 && tshift == 1){ // quick fix to include timeslice 0 if tmax + tshift is over the last timeslice + unsigned int t0 = 0; + tmp = where(((lcoor==t0) || (lcoor>=tmin+tshift)),tmp,zz); + } else { + tmp = where((lcoor>=tmin+tshift),tmp,zz); + } + q_out+= where((lcoor<=tmax+tshift),tmp,zz); // Position of current complicated } NAMESPACE_END(Grid); From 82aecbf4cfdb76a44c90855292e5c08628e48b62 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 5 Apr 2022 15:26:39 +0100 Subject: [PATCH 319/399] Test_wilson_conserved_current added --- tests/core/Test_wilson_conserved_current.cc | 290 ++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 tests/core/Test_wilson_conserved_current.cc diff --git a/tests/core/Test_wilson_conserved_current.cc b/tests/core/Test_wilson_conserved_current.cc new file mode 100644 index 00000000..3eba4fdc --- /dev/null +++ b/tests/core/Test_wilson_conserved_current.cc @@ -0,0 +1,290 @@ +/************************************************************************************* +Grid physics library, www.github.com/paboyle/Grid + +Source file: ./tests/Test_cayley_cg.cc + +Copyright (C) 2022 + +Author: Peter Boyle <paboyle@ph.ed.ac.uk> +Author: Fabian Joswig <fabian.joswig@ed.ac.uk> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ +#include <Grid/Grid.h> +#include <Grid/qcd/action/fermion/Reconstruct5Dprop.h> + +using namespace std; +using namespace Grid; + + +template<class What> +void TestConserved(What & Ddwf, + LatticeGaugeField &Umu, + GridCartesian * FGrid, GridRedBlackCartesian * FrbGrid, + GridCartesian * UGrid, GridRedBlackCartesian * UrbGrid, + RealD mass, RealD M5, + GridParallelRNG *RNG4, + GridParallelRNG *RNG5, + What *Ddwfrev=nullptr); + + Gamma::Algebra Gmu [] = { + Gamma::Algebra::GammaX, + Gamma::Algebra::GammaY, + Gamma::Algebra::GammaZ, + Gamma::Algebra::GammaT, + Gamma::Algebra::Gamma5 + }; + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + int threads = GridThread::GetThreads(); + std::cout<<GridLogMessage << "Grid is setup to use "<<threads<<" threads"<<std::endl; + + const int Ls=10; + std::vector < ComplexD > omegas; + std::vector < ComplexD > omegasrev(Ls); + + GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), + GridDefaultSimd(Nd,vComplex::Nsimd()), + GridDefaultMpi()); + GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); + GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); + GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); + + + GridCartesian * UGridF = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), + GridDefaultSimd(Nd,vComplexF::Nsimd()), + GridDefaultMpi()); + GridRedBlackCartesian * UrbGridF = SpaceTimeGrid::makeFourDimRedBlackGrid(UGridF); + GridCartesian * FGridF = SpaceTimeGrid::makeFiveDimGrid(Ls,UGridF); + GridRedBlackCartesian * FrbGridF = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGridF); + + + std::vector<int> seeds5({5,6,7,8}); + GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); + GridParallelRNG RNG4(UGrid); + std::vector<int> seeds4({1,2,3,4}); RNG4.SeedFixedIntegers(seeds4); + + LatticeGaugeField Umu(UGrid); + if( argc > 1 && argv[1][0] != '-' ) + { + std::cout<<GridLogMessage <<"Loading configuration from "<<argv[1]<<std::endl; + FieldMetaData header; + NerscIO::readConfiguration(Umu, header, argv[1]); + } + else + { + std::cout<<GridLogMessage <<"Using hot configuration"<<std::endl; + SU<Nc>::HotConfiguration(RNG4,Umu); + } + + RealD mass=0.3; + RealD M5 =1.0; + std::cout<<GridLogMessage <<"======================"<<std::endl; + std::cout<<GridLogMessage <<"WilsonFermion test"<<std::endl; + std::cout<<GridLogMessage <<"======================"<<std::endl; + DomainWallFermionR Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5); + TestConserved<DomainWallFermionR>(Ddwf,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + + std::cout<<GridLogMessage <<"======================"<<std::endl; + std::cout<<GridLogMessage <<"WilsonCloverFermion test"<<std::endl; + std::cout<<GridLogMessage <<"======================"<<std::endl; + MobiusFermionR Dmob(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,b,c); + TestConserved<MobiusFermionR>(Dmob,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + + Grid_finalize(); +} + + + +template<class Action> +void TestConserved(Action & Ddwf, + LatticeGaugeField &Umu, + GridCartesian * FGrid, GridRedBlackCartesian * FrbGrid, + GridCartesian * UGrid, GridRedBlackCartesian * UrbGrid, + RealD mass, RealD M5, + GridParallelRNG *RNG4, + GridParallelRNG *RNG5, + Action * Ddwfrev) +{ + LatticePropagator phys_src(UGrid); + LatticePropagator seqsrc(FGrid); + LatticePropagator prop5(FGrid); + LatticePropagator prop5rev(FGrid); + LatticePropagator prop4(UGrid); + LatticePropagator Axial_mu(UGrid); + LatticePropagator Vector_mu(UGrid); + LatticeComplex PA (UGrid); + LatticeComplex SV (UGrid); + LatticeComplex VV (UGrid); + LatticeComplex PJ5q(UGrid); + LatticeComplex PP (UGrid); + LatticePropagator seqprop(UGrid); + + SpinColourMatrix kronecker; kronecker=1.0; + Coordinate coor({0,0,0,0}); + phys_src=Zero(); + pokeSite(kronecker,phys_src,coor); + + ConjugateGradient<LatticeFermion> CG(1.0e-16,100000); + SchurRedBlackDiagTwoSolve<LatticeFermion> schur(CG); + ZeroGuesser<LatticeFermion> zpg; + for(int s=0;s<Nd;s++){ + for(int c=0;c<Nc;c++){ + LatticeFermion src4 (UGrid); + PropToFerm<Action>(src4,phys_src,s,c); + + LatticeFermion src5 (FGrid); + Ddwf.ImportPhysicalFermionSource(src4,src5); + + LatticeFermion result5(FGrid); result5=Zero(); + schur(Ddwf,src5,result5,zpg); + std::cout<<GridLogMessage<<"spin "<<s<<" color "<<c<<" norm2(sourc5d) "<<norm2(src5) + <<" norm2(result5d) "<<norm2(result5)<<std::endl; + FermToProp<Action>(prop5,result5,s,c); + + LatticeFermion result4(UGrid); + Ddwf.ExportPhysicalFermionSolution(result5,result4); + FermToProp<Action>(prop4,result4,s,c); + + if( Ddwfrev ) { + Ddwfrev->ImportPhysicalFermionSource(src4,src5); + result5 = Zero(); + schur(*Ddwfrev,src5,result5,zpg); + } + FermToProp<Action>(prop5rev,result5,s,c); + } + } + + auto curr = Current::Vector; + const int mu_J=0; + const int t_J=0; + + LatticeComplex ph (UGrid); ph=1.0; + + Ddwf.SeqConservedCurrent(prop5, + seqsrc, + phys_src, + curr, + mu_J, + t_J, + t_J,// whole lattice + ph); + + for(int s=0;s<Nd;s++){ + for(int c=0;c<Nc;c++){ + + LatticeFermion src5 (FGrid); + PropToFerm<Action>(src5,seqsrc,s,c); + + LatticeFermion result5(FGrid); result5=Zero(); + schur(Ddwf,src5,result5,zpg); + + LatticeFermion result4(UGrid); + Ddwf.ExportPhysicalFermionSolution(result5,result4); + FermToProp<Action>(seqprop,result4,s,c); + } + } + + Gamma g5(Gamma::Algebra::Gamma5); + Gamma gT(Gamma::Algebra::GammaT); + + std::vector<TComplex> sumPA; + std::vector<TComplex> sumSV; + std::vector<TComplex> sumVV; + std::vector<TComplex> sumPP; + std::vector<TComplex> sumPJ5q; + + Ddwf.ContractConservedCurrent(prop5rev,prop5,Axial_mu,phys_src,Current::Axial,Tdir); + Ddwf.ContractConservedCurrent(prop5rev,prop5,Vector_mu,phys_src,Current::Vector,Tdir); + Ddwf.ContractJ5q(prop5,PJ5q); + + PA = trace(g5*Axial_mu); // Pseudoscalar-Axial conserved current + SV = trace(Vector_mu); // Scalar-Vector conserved current + VV = trace(gT*Vector_mu); // (local) Vector-Vector conserved current + PP = trace(adj(prop4)*prop4); // Pseudoscalar density + + // Spatial sum + sliceSum(PA,sumPA,Tdir); + sliceSum(SV,sumSV,Tdir); + sliceSum(VV,sumVV,Tdir); + sliceSum(PP,sumPP,Tdir); + sliceSum(PJ5q,sumPJ5q,Tdir); + + const int Nt{static_cast<int>(sumPA.size())}; + std::cout<<GridLogMessage<<"Vector Ward identity by timeslice (~ 0)"<<std::endl; + for(int t=0;t<Nt;t++){ + std::cout<<GridLogMessage <<" t "<<t<<" SV "<<real(TensorRemove(sumSV[t]))<<" VV "<<real(TensorRemove(sumVV[t]))<<std::endl; + } + std::cout<<GridLogMessage<<"Axial Ward identity by timeslice (defect ~ 0)"<<std::endl; + for(int t=0;t<Nt;t++){ + const RealD DmuPAmu{real(TensorRemove(sumPA[t]-sumPA[(t-1+Nt)%Nt]))}; + std::cout<<GridLogMessage<<" t "<<t<<" DmuPAmu "<<DmuPAmu + <<" PP "<<real(TensorRemove(sumPP[t]))<<" PJ5q "<<real(TensorRemove(sumPJ5q[t])) + <<" Ward Identity defect " <<(DmuPAmu - 2.*real(TensorRemove(Ddwf.mass*sumPP[t] + sumPJ5q[t])))<<std::endl; + } + + /////////////////////////////// + // 3pt vs 2pt check + /////////////////////////////// + { + Gamma::Algebra gA = (curr == Current::Axial) ? Gamma::Algebra::Gamma5 : Gamma::Algebra::Identity; + Gamma g(gA); + + LatticePropagator cur(UGrid); + LatticePropagator tmp(UGrid); + LatticeComplex c(UGrid); + SpinColourMatrix qSite; + peekSite(qSite, seqprop, coor); + + Complex test_S, test_V, check_S, check_V; + + std::vector<TComplex> check_buf; + + test_S = trace(qSite*g); + test_V = trace(qSite*g*Gamma::gmu[mu_J]); + + Ddwf.ContractConservedCurrent(prop5rev,prop5,cur,phys_src,curr,mu_J); + + c = trace(cur*g); + sliceSum(c, check_buf, Tp); + check_S = TensorRemove(check_buf[t_J]); + + auto gmu=Gamma::gmu[mu_J]; + c = trace(cur*g*gmu); + sliceSum(c, check_buf, Tp); + check_V = TensorRemove(check_buf[t_J]); + + + std::cout<<GridLogMessage << std::setprecision(14)<<"Test S = " << abs(test_S) << std::endl; + std::cout<<GridLogMessage << "Test V = " << abs(test_V) << std::endl; + std::cout<<GridLogMessage << "Check S = " << abs(check_S) << std::endl; + std::cout<<GridLogMessage << "Check V = " << abs(check_V) << std::endl; + + // Check difference = 0 + check_S = check_S - test_S; + check_V = check_V - test_V; + + std::cout<<GridLogMessage << "Consistency check for sequential conserved " <<std::endl; + std::cout<<GridLogMessage << "Diff S = " << abs(check_S) << std::endl; + std::cout<<GridLogMessage << "Diff V = " << abs(check_V) << std::endl; + } + +} From b8bc560b5153a4adfe4f15e4b60656bbca23cfba Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 5 Apr 2022 17:33:45 +0100 Subject: [PATCH 320/399] Test_wilson_conserved_current implemented, all 5d references removed. --- .../WilsonFermionImplementation.h | 3 +- tests/core/Test_wilson_conserved_current.cc | 149 +++++++----------- 2 files changed, 58 insertions(+), 94 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h index 6bfb9305..c958019d 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonFermionImplementation.h @@ -4,12 +4,13 @@ Grid physics library, www.github.com/paboyle/Grid Source file: ./lib/qcd/action/fermion/WilsonFermion.cc -Copyright (C) 2015 +Copyright (C) 2022 Author: Peter Boyle <pabobyle@ph.ed.ac.uk> Author: Peter Boyle <paboyle@ph.ed.ac.uk> Author: Peter Boyle <peterboyle@Peters-MacBook-Pro-2.local> Author: paboyle <paboyle@ph.ed.ac.uk> +Author: Fabian Joswig <fabian.joswig@ed.ac.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/tests/core/Test_wilson_conserved_current.cc b/tests/core/Test_wilson_conserved_current.cc index 3eba4fdc..3ee1a271 100644 --- a/tests/core/Test_wilson_conserved_current.cc +++ b/tests/core/Test_wilson_conserved_current.cc @@ -26,21 +26,16 @@ See the full license in the file "LICENSE" in the top level distribution directo *************************************************************************************/ /* END LEGAL */ #include <Grid/Grid.h> -#include <Grid/qcd/action/fermion/Reconstruct5Dprop.h> using namespace std; using namespace Grid; template<class What> -void TestConserved(What & Ddwf, +void TestConserved(What & Dw, LatticeGaugeField &Umu, - GridCartesian * FGrid, GridRedBlackCartesian * FrbGrid, GridCartesian * UGrid, GridRedBlackCartesian * UrbGrid, - RealD mass, RealD M5, - GridParallelRNG *RNG4, - GridParallelRNG *RNG5, - What *Ddwfrev=nullptr); + GridParallelRNG *RNG4); Gamma::Algebra Gmu [] = { Gamma::Algebra::GammaX, @@ -57,28 +52,12 @@ int main (int argc, char ** argv) int threads = GridThread::GetThreads(); std::cout<<GridLogMessage << "Grid is setup to use "<<threads<<" threads"<<std::endl; - const int Ls=10; - std::vector < ComplexD > omegas; - std::vector < ComplexD > omegasrev(Ls); - GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()), GridDefaultMpi()); GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); - GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); - GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); - - - GridCartesian * UGridF = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), - GridDefaultSimd(Nd,vComplexF::Nsimd()), - GridDefaultMpi()); - GridRedBlackCartesian * UrbGridF = SpaceTimeGrid::makeFourDimRedBlackGrid(UGridF); - GridCartesian * FGridF = SpaceTimeGrid::makeFiveDimGrid(Ls,UGridF); - GridRedBlackCartesian * FrbGridF = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGridF); - std::vector<int> seeds5({5,6,7,8}); - GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5); GridParallelRNG RNG4(UGrid); std::vector<int> seeds4({1,2,3,4}); RNG4.SeedFixedIntegers(seeds4); @@ -95,19 +74,41 @@ int main (int argc, char ** argv) SU<Nc>::HotConfiguration(RNG4,Umu); } - RealD mass=0.3; - RealD M5 =1.0; - std::cout<<GridLogMessage <<"======================"<<std::endl; - std::cout<<GridLogMessage <<"WilsonFermion test"<<std::endl; - std::cout<<GridLogMessage <<"======================"<<std::endl; - DomainWallFermionR Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5); - TestConserved<DomainWallFermionR>(Ddwf,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + typename WilsonCloverFermionR::ImplParams params; + WilsonAnisotropyCoefficients anis; + RealD mass = 0.1; + RealD csw_r = 1.0; + RealD csw_t = 1.0; - std::cout<<GridLogMessage <<"======================"<<std::endl; + std::cout<<GridLogMessage <<"=================================="<<std::endl; + std::cout<<GridLogMessage <<"WilsonFermion test"<<std::endl; + std::cout<<GridLogMessage <<"=================================="<<std::endl; + WilsonFermionR Dw(Umu,*UGrid,*UrbGrid,mass,params); + TestConserved<WilsonFermionR>(Dw,Umu,UGrid,UrbGrid,&RNG4); + + std::cout<<GridLogMessage <<"=================================="<<std::endl; std::cout<<GridLogMessage <<"WilsonCloverFermion test"<<std::endl; - std::cout<<GridLogMessage <<"======================"<<std::endl; - MobiusFermionR Dmob(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,b,c); - TestConserved<MobiusFermionR>(Dmob,Umu,FGrid,FrbGrid,UGrid,UrbGrid,mass,M5,&RNG4,&RNG5); + std::cout<<GridLogMessage <<"=================================="<<std::endl; + WilsonCloverFermionR Dwc(Umu, *UGrid, *UrbGrid, mass, csw_r, csw_t, anis, params); + TestConserved<WilsonCloverFermionR>(Dwc,Umu,UGrid,UrbGrid,&RNG4); + + std::cout<<GridLogMessage <<"=================================="<<std::endl; + std::cout<<GridLogMessage <<"CompactWilsonCloverFermion test"<<std::endl; + std::cout<<GridLogMessage <<"=================================="<<std::endl; + CompactWilsonCloverFermionR Dwcc(Umu, *UGrid, *UrbGrid, mass, csw_r, csw_t, 1.0, anis, params); + TestConserved<CompactWilsonCloverFermionR>(Dwcc,Umu,UGrid,UrbGrid,&RNG4); + + std::cout<<GridLogMessage <<"=================================="<<std::endl; + std::cout<<GridLogMessage <<"WilsonExpCloverFermion test"<<std::endl; + std::cout<<GridLogMessage <<"=================================="<<std::endl; + WilsonExpCloverFermionR Dewc(Umu, *UGrid, *UrbGrid, mass, csw_r, csw_t, anis, params); + TestConserved<WilsonExpCloverFermionR>(Dewc,Umu,UGrid,UrbGrid,&RNG4); + + std::cout<<GridLogMessage <<"=================================="<<std::endl; + std::cout<<GridLogMessage <<"CompactWilsonExpCloverFermion test"<<std::endl; + std::cout<<GridLogMessage <<"=================================="<<std::endl; + CompactWilsonExpCloverFermionR Dewcc(Umu, *UGrid, *UrbGrid, mass, csw_r, csw_t, 1.0, anis, params); + TestConserved<CompactWilsonExpCloverFermionR>(Dewcc,Umu,UGrid,UrbGrid,&RNG4); Grid_finalize(); } @@ -115,27 +116,17 @@ int main (int argc, char ** argv) template<class Action> -void TestConserved(Action & Ddwf, +void TestConserved(Action & Dw, LatticeGaugeField &Umu, - GridCartesian * FGrid, GridRedBlackCartesian * FrbGrid, GridCartesian * UGrid, GridRedBlackCartesian * UrbGrid, - RealD mass, RealD M5, - GridParallelRNG *RNG4, - GridParallelRNG *RNG5, - Action * Ddwfrev) + GridParallelRNG *RNG4) { LatticePropagator phys_src(UGrid); - LatticePropagator seqsrc(FGrid); - LatticePropagator prop5(FGrid); - LatticePropagator prop5rev(FGrid); + LatticePropagator seqsrc(UGrid); LatticePropagator prop4(UGrid); - LatticePropagator Axial_mu(UGrid); LatticePropagator Vector_mu(UGrid); - LatticeComplex PA (UGrid); LatticeComplex SV (UGrid); LatticeComplex VV (UGrid); - LatticeComplex PJ5q(UGrid); - LatticeComplex PP (UGrid); LatticePropagator seqprop(UGrid); SpinColourMatrix kronecker; kronecker=1.0; @@ -151,25 +142,11 @@ void TestConserved(Action & Ddwf, LatticeFermion src4 (UGrid); PropToFerm<Action>(src4,phys_src,s,c); - LatticeFermion src5 (FGrid); - Ddwf.ImportPhysicalFermionSource(src4,src5); - - LatticeFermion result5(FGrid); result5=Zero(); - schur(Ddwf,src5,result5,zpg); - std::cout<<GridLogMessage<<"spin "<<s<<" color "<<c<<" norm2(sourc5d) "<<norm2(src5) - <<" norm2(result5d) "<<norm2(result5)<<std::endl; - FermToProp<Action>(prop5,result5,s,c); - - LatticeFermion result4(UGrid); - Ddwf.ExportPhysicalFermionSolution(result5,result4); + LatticeFermion result4(UGrid); result4=Zero(); + schur(Dw,src4,result4,zpg); + std::cout<<GridLogMessage<<"spin "<<s<<" color "<<c<<" norm2(sourc4d) "<<norm2(src4) + <<" norm2(result4d) "<<norm2(result4)<<std::endl; FermToProp<Action>(prop4,result4,s,c); - - if( Ddwfrev ) { - Ddwfrev->ImportPhysicalFermionSource(src4,src5); - result5 = Zero(); - schur(*Ddwfrev,src5,result5,zpg); - } - FermToProp<Action>(prop5rev,result5,s,c); } } @@ -179,7 +156,7 @@ void TestConserved(Action & Ddwf, LatticeComplex ph (UGrid); ph=1.0; - Ddwf.SeqConservedCurrent(prop5, + Dw.SeqConservedCurrent(prop4, seqsrc, phys_src, curr, @@ -191,14 +168,12 @@ void TestConserved(Action & Ddwf, for(int s=0;s<Nd;s++){ for(int c=0;c<Nc;c++){ - LatticeFermion src5 (FGrid); - PropToFerm<Action>(src5,seqsrc,s,c); + LatticeFermion src4 (UGrid); + PropToFerm<Action>(src4,seqsrc,s,c); - LatticeFermion result5(FGrid); result5=Zero(); - schur(Ddwf,src5,result5,zpg); + LatticeFermion result4(UGrid); result4=Zero(); + schur(Dw,src4,result4,zpg); - LatticeFermion result4(UGrid); - Ddwf.ExportPhysicalFermionSolution(result5,result4); FermToProp<Action>(seqprop,result4,s,c); } } @@ -206,46 +181,32 @@ void TestConserved(Action & Ddwf, Gamma g5(Gamma::Algebra::Gamma5); Gamma gT(Gamma::Algebra::GammaT); - std::vector<TComplex> sumPA; std::vector<TComplex> sumSV; std::vector<TComplex> sumVV; - std::vector<TComplex> sumPP; - std::vector<TComplex> sumPJ5q; - Ddwf.ContractConservedCurrent(prop5rev,prop5,Axial_mu,phys_src,Current::Axial,Tdir); - Ddwf.ContractConservedCurrent(prop5rev,prop5,Vector_mu,phys_src,Current::Vector,Tdir); - Ddwf.ContractJ5q(prop5,PJ5q); + Dw.ContractConservedCurrent(prop4,prop4,Vector_mu,phys_src,Current::Vector,Tdir); - PA = trace(g5*Axial_mu); // Pseudoscalar-Axial conserved current SV = trace(Vector_mu); // Scalar-Vector conserved current VV = trace(gT*Vector_mu); // (local) Vector-Vector conserved current - PP = trace(adj(prop4)*prop4); // Pseudoscalar density // Spatial sum - sliceSum(PA,sumPA,Tdir); sliceSum(SV,sumSV,Tdir); sliceSum(VV,sumVV,Tdir); - sliceSum(PP,sumPP,Tdir); - sliceSum(PJ5q,sumPJ5q,Tdir); - const int Nt{static_cast<int>(sumPA.size())}; + const int Nt{static_cast<int>(sumSV.size())}; + std::cout<<GridLogMessage<<"Vector Ward identity by timeslice (~ 0)"<<std::endl; for(int t=0;t<Nt;t++){ std::cout<<GridLogMessage <<" t "<<t<<" SV "<<real(TensorRemove(sumSV[t]))<<" VV "<<real(TensorRemove(sumVV[t]))<<std::endl; - } - std::cout<<GridLogMessage<<"Axial Ward identity by timeslice (defect ~ 0)"<<std::endl; - for(int t=0;t<Nt;t++){ - const RealD DmuPAmu{real(TensorRemove(sumPA[t]-sumPA[(t-1+Nt)%Nt]))}; - std::cout<<GridLogMessage<<" t "<<t<<" DmuPAmu "<<DmuPAmu - <<" PP "<<real(TensorRemove(sumPP[t]))<<" PJ5q "<<real(TensorRemove(sumPJ5q[t])) - <<" Ward Identity defect " <<(DmuPAmu - 2.*real(TensorRemove(Ddwf.mass*sumPP[t] + sumPJ5q[t])))<<std::endl; + assert(abs(real(TensorRemove(sumSV[t]))) < 1e-10); + assert(abs(real(TensorRemove(sumVV[t]))) < 1e-2); } /////////////////////////////// // 3pt vs 2pt check /////////////////////////////// { - Gamma::Algebra gA = (curr == Current::Axial) ? Gamma::Algebra::Gamma5 : Gamma::Algebra::Identity; + Gamma::Algebra gA = Gamma::Algebra::Identity; Gamma g(gA); LatticePropagator cur(UGrid); @@ -261,7 +222,7 @@ void TestConserved(Action & Ddwf, test_S = trace(qSite*g); test_V = trace(qSite*g*Gamma::gmu[mu_J]); - Ddwf.ContractConservedCurrent(prop5rev,prop5,cur,phys_src,curr,mu_J); + Dw.ContractConservedCurrent(prop4,prop4,cur,phys_src,curr,mu_J); c = trace(cur*g); sliceSum(c, check_buf, Tp); @@ -284,7 +245,9 @@ void TestConserved(Action & Ddwf, std::cout<<GridLogMessage << "Consistency check for sequential conserved " <<std::endl; std::cout<<GridLogMessage << "Diff S = " << abs(check_S) << std::endl; + assert(abs(check_S) < 1e-8); std::cout<<GridLogMessage << "Diff V = " << abs(check_V) << std::endl; + assert(abs(check_V) < 1e-8); } } From e61fed87db2b8d7dd465e05c1a2526eca79408fb Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Wed, 20 Apr 2022 15:41:55 +0100 Subject: [PATCH 321/399] SteepestDescentGaugeFix now exits when the algorithm does not converge. This behaviour can be altered by setting err_on_no_converge to false. --- Grid/qcd/utils/GaugeFix.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/utils/GaugeFix.h b/Grid/qcd/utils/GaugeFix.h index 2b3384da..29184a88 100644 --- a/Grid/qcd/utils/GaugeFix.h +++ b/Grid/qcd/utils/GaugeFix.h @@ -55,12 +55,12 @@ public: } } - static void SteepestDescentGaugeFix(GaugeLorentz &Umu,Real & alpha,int maxiter,Real Omega_tol, Real Phi_tol,bool Fourier=false,int orthog=-1) { + static void SteepestDescentGaugeFix(GaugeLorentz &Umu,Real & alpha,int maxiter,Real Omega_tol, Real Phi_tol,bool Fourier=false,int orthog=-1,bool err_on_no_converge=true) { GridBase *grid = Umu.Grid(); GaugeMat xform(grid); - SteepestDescentGaugeFix(Umu,xform,alpha,maxiter,Omega_tol,Phi_tol,Fourier,orthog); + SteepestDescentGaugeFix(Umu,xform,alpha,maxiter,Omega_tol,Phi_tol,Fourier,orthog,err_on_no_converge); } - static void SteepestDescentGaugeFix(GaugeLorentz &Umu,GaugeMat &xform,Real & alpha,int maxiter,Real Omega_tol, Real Phi_tol,bool Fourier=false,int orthog=-1) { + static void SteepestDescentGaugeFix(GaugeLorentz &Umu,GaugeMat &xform,Real & alpha,int maxiter,Real Omega_tol, Real Phi_tol,bool Fourier=false,int orthog=-1,bool err_on_no_converge=true) { GridBase *grid = Umu.Grid(); @@ -122,6 +122,8 @@ public: } } + std::cout << GridLogError << "Gauge fixing did not converge in " << maxiter << " iterations." << std::endl; + if (err_on_no_converge) assert(0); }; static Real SteepestDescentStep(std::vector<GaugeMat> &U,GaugeMat &xform,Real & alpha, GaugeMat & dmuAmu,int orthog) { GridBase *grid = U[0].Grid(); From 8b12a61097b25f5a3916f587c312336bf77f78ed Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Mon, 9 May 2022 11:53:22 +0100 Subject: [PATCH 322/399] fix: readded Config.h and Version.h to HFILEs in Grid/Makefile.am --- Grid/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/Makefile.am b/Grid/Makefile.am index aff5b9d0..7c3c151b 100644 --- a/Grid/Makefile.am +++ b/Grid/Makefile.am @@ -70,7 +70,7 @@ endif lib_LIBRARIES = libGrid.a CCFILES += $(extra_sources) -HFILES += $(extra_headers) +HFILES += $(extra_headers) Config.h Version.h libGrid_a_SOURCES = $(CCFILES) libGrid_adir = $(includedir)/Grid From 32facbd02a96595ff3fdb92aaa310bbcd0d356d3 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 10 May 2022 10:53:22 +0100 Subject: [PATCH 323/399] fix: assert for dimensions of compact Wilson clover moved to constructor. --- Grid/qcd/action/fermion/WilsonCloverTypes.h | 2 -- .../implementation/CompactWilsonCloverFermionImplementation.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonCloverTypes.h b/Grid/qcd/action/fermion/WilsonCloverTypes.h index a5a95d93..5ce3b91b 100644 --- a/Grid/qcd/action/fermion/WilsonCloverTypes.h +++ b/Grid/qcd/action/fermion/WilsonCloverTypes.h @@ -47,8 +47,6 @@ class CompactWilsonCloverTypes { public: INHERIT_IMPL_TYPES(Impl); - static_assert(Nd == 4 && Nc == 3 && Ns == 4 && Impl::Dimension == 3, "Wrong dimensions"); - static constexpr int Nred = Nc * Nhs; // 6 static constexpr int Nblock = Nhs; // 2 static constexpr int Ndiagonal = Nred; // 6 diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 2496f3f1..e4864730 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -59,6 +59,8 @@ CompactWilsonCloverFermion<Impl, CloverHelpers>::CompactWilsonCloverFermion(Gaug , BoundaryMask(&Fgrid) , BoundaryMaskEven(&Hgrid), BoundaryMaskOdd(&Hgrid) { + assert(Nd == 4 && Nc == 3 && Ns == 4 && Impl::Dimension == 3); + csw_r *= 0.5; csw_t *= 0.5; if (clover_anisotropy.isAnisotropic) From b051e00de044bee7a6756b9d51f943fe5189b32d Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Mon, 16 May 2022 00:25:13 +0100 Subject: [PATCH 324/399] Additional Local Coherance Deflation operator() --- Grid/algorithms/iterative/Deflation.h | 38 ++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index 2eb28bf9..a69d1549 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -113,7 +113,43 @@ public: blockPromote(guess_coarse,guess,subspace); guess.Checkerboard() = src.Checkerboard(); }; -}; + + void operator()(const std::vector<FineField> &src,std::vector<FineField> &guess) { + int Nevec = (int)evec_coarse.size(); + int Nsrc = (int)src.size(); + // make temp variables + std::vector<CoarseField> src_coarse(Nsrc,evec_coarse[0].Grid()); + std::vector<CoarseField> guess_coarse(Nsrc,evec_coarse[0].Grid()); + //Preporcessing + std::cout << GridLogMessage << "Start BlockProject for loop" << std::endl; + for (int j=0;j<Nsrc;j++) + { + guess_coarse[j] = Zero(); + std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl; + blockProject(src_coarse[j],src[j],subspace); + } + //deflation set up for eigen vector batchsize 1 and source batch size equal number of batches + std::cout << GridLogMessage << "Start ProjectAccum for loop" << std::endl; + for (int i=0;i<Nevec;i++) + { + std::cout << GridLogMessage << "ProjectAccum Nvec: " << i << std::endl; + const CoarseField & tmp = evec_coarse[i]; + for (int j=0;j<Nsrc;j++) + { + axpy(guess_coarse[j],TensorRemove(innerProduct(tmp,src_coarse[j])) / eval_coarse[i],tmp,guess_coarse[j]); + } + } + //postprocessing + std::cout << GridLogMessage << "Start BlockPromote for loop" << std::endl; + for (int j=0;j<Nsrc;j++) + { + std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl; + blockPromote(guess_coarse[j],guess[j],subspace); + guess[j].Checkerboard() = src[j].Checkerboard(); + } + }; + + }; From 8939d5dc73789840c369ef7b5002d9b03ad73e14 Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Mon, 16 May 2022 00:28:28 +0100 Subject: [PATCH 325/399] bugfix: eo operator called in correct location --- benchmarks/Benchmark_wilson_sweep.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/Benchmark_wilson_sweep.cc b/benchmarks/Benchmark_wilson_sweep.cc index 986a1831..d88e2d39 100644 --- a/benchmarks/Benchmark_wilson_sweep.cc +++ b/benchmarks/Benchmark_wilson_sweep.cc @@ -110,8 +110,8 @@ int main (int argc, char ** argv) bench_wilson(src,result,Dw,volume,DaggerYes); std::cout << "\t"; // EO - bench_wilson(src,result,Dw,volume,DaggerNo); - bench_wilson(src,result,Dw,volume,DaggerYes); + bench_wilson_eo(src,result,Dw,volume,DaggerNo); + bench_wilson_eo(src,result,Dw,volume,DaggerYes); std::cout << std::endl; } } From 4b1997e2f3a23abc0ee61a7b19b612b50548c5a5 Mon Sep 17 00:00:00 2001 From: James Richings <james.richings@ed.ac.uk> Date: Mon, 16 May 2022 15:58:33 +0100 Subject: [PATCH 326/399] wilson sweep test --- benchmarks/Benchmark_wilson_sweep.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/benchmarks/Benchmark_wilson_sweep.cc b/benchmarks/Benchmark_wilson_sweep.cc index d88e2d39..50ad1d03 100644 --- a/benchmarks/Benchmark_wilson_sweep.cc +++ b/benchmarks/Benchmark_wilson_sweep.cc @@ -44,6 +44,13 @@ void bench_wilson ( double const volume, int const dag ); +void bench_wilson_eo ( + LatticeFermion & src, + LatticeFermion & result, + WilsonFermionR & Dw, + double const volume, + int const dag ); + int main (int argc, char ** argv) { Grid_init(&argc,&argv); @@ -110,8 +117,8 @@ int main (int argc, char ** argv) bench_wilson(src,result,Dw,volume,DaggerYes); std::cout << "\t"; // EO - bench_wilson_eo(src,result,Dw,volume,DaggerNo); - bench_wilson_eo(src,result,Dw,volume,DaggerYes); + bench_wilson_eo(src_o,result_e,Dw,volume,DaggerNo); + bench_wilson_eo(src_o,result_e,Dw,volume,DaggerYes); std::cout << std::endl; } } From 79e34b3eb44b0c62a316736ffa5b0a7c0e2249f7 Mon Sep 17 00:00:00 2001 From: JPRichings <james.richings@ed.ac.uk> Date: Thu, 19 May 2022 14:53:17 +0100 Subject: [PATCH 327/399] Local Coherence batch deflation --- Grid/algorithms/iterative/Deflation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/algorithms/iterative/Deflation.h b/Grid/algorithms/iterative/Deflation.h index a69d1549..1a8f97c9 100644 --- a/Grid/algorithms/iterative/Deflation.h +++ b/Grid/algorithms/iterative/Deflation.h @@ -128,7 +128,7 @@ public: std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl; blockProject(src_coarse[j],src[j],subspace); } - //deflation set up for eigen vector batchsize 1 and source batch size equal number of batches + //deflation set up for eigen vector batchsize 1 and source batch size equal number of sources std::cout << GridLogMessage << "Start ProjectAccum for loop" << std::endl; for (int i=0;i<Nevec;i++) { From 617c5362c1a492a5f4c185ec6b1048abfd94d155 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 24 May 2022 11:37:33 +0100 Subject: [PATCH 328/399] fix: fixed warning: missing return statement at end of non-void function in CloverHelpers --- Grid/qcd/action/fermion/CloverHelpers.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index c72a79e8..57e71998 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -181,6 +181,7 @@ public: static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { assert(0); + return lambda; } }; @@ -425,6 +426,7 @@ public: static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { assert(0); + return lambda; } }; From c7205d2a73ea9033c3f4cc5b6f01ec3431fba348 Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 24 May 2022 14:30:26 +0100 Subject: [PATCH 329/399] Removed nvcc guards for json --- Grid/json/json.hpp | 4 +--- Grid/serialisation/JSON_IO.cc | 2 +- Grid/serialisation/Serialisation.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Grid/json/json.hpp b/Grid/json/json.hpp index 618aa7a1..5b5d2215 100644 --- a/Grid/json/json.hpp +++ b/Grid/json/json.hpp @@ -1,4 +1,3 @@ -#ifndef __NVCC__ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ @@ -18918,5 +18917,4 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef NLOHMANN_BASIC_JSON_TPL -#endif -#endif +#endif \ No newline at end of file diff --git a/Grid/serialisation/JSON_IO.cc b/Grid/serialisation/JSON_IO.cc index f2282099..e47a2969 100644 --- a/Grid/serialisation/JSON_IO.cc +++ b/Grid/serialisation/JSON_IO.cc @@ -26,7 +26,7 @@ *************************************************************************************/ /* END LEGAL */ #include <Grid/Grid.h> -#if (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) +#ifndef GRID_HIP NAMESPACE_BEGIN(Grid); diff --git a/Grid/serialisation/Serialisation.h b/Grid/serialisation/Serialisation.h index e14120af..4f395be2 100644 --- a/Grid/serialisation/Serialisation.h +++ b/Grid/serialisation/Serialisation.h @@ -36,7 +36,7 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> #include "BinaryIO.h" #include "TextIO.h" #include "XmlIO.h" -#if (!defined(GRID_CUDA)) && (!defined(GRID_HIP)) +#ifndef GRID_HIP #include "JSON_IO.h" #endif From 3ca0de1c409eb6c96ef95293d926915b8173346b Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 24 May 2022 14:37:33 +0100 Subject: [PATCH 330/399] Fix json write for vector<string> --- Grid/serialisation/JSON_IO.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/serialisation/JSON_IO.cc b/Grid/serialisation/JSON_IO.cc index e47a2969..c92203ba 100644 --- a/Grid/serialisation/JSON_IO.cc +++ b/Grid/serialisation/JSON_IO.cc @@ -82,7 +82,7 @@ void JSONWriter::writeDefault(const std::string &s, const std::string &x) if (s.size()) ss_ << "\""<< s << "\" : \"" << os.str() << "\" ," ; else - ss_ << os.str() << " ," ; + ss_ << "\""<< os.str() << "\" ," ; } // Reader implementation /////////////////////////////////////////////////////// From bab8aa8eb049749402f1fbb0b33f1e086ce44403 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 24 May 2022 15:27:40 +0100 Subject: [PATCH 331/399] fix: conditional pragmas according to new NVCC_DIAG_PRAGMA_SUPPORT standard in DisableWarnings.h --- Grid/DisableWarnings.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Grid/DisableWarnings.h b/Grid/DisableWarnings.h index 4bd1edd0..64e4faf4 100644 --- a/Grid/DisableWarnings.h +++ b/Grid/DisableWarnings.h @@ -44,14 +44,22 @@ directory #ifdef __NVCC__ //disables nvcc specific warning in json.hpp #pragma clang diagnostic ignored "-Wdeprecated-register" + +#if (__CUDACC_VER_MAJOR__ >= 11) && (__CUDACC_VER_MINOR__ >= 5) + //disables nvcc specific warning in json.hpp +#pragma nv_diag_suppress unsigned_compare_with_zero +#pragma nv_diag_suppress cast_to_qualified_type + //disables nvcc specific warning in many files +#pragma nv_diag_suppress esa_on_defaulted_function_ignored +#pragma nv_diag_suppress extra_semicolon +#else + //disables nvcc specific warning in json.hpp #pragma diag_suppress unsigned_compare_with_zero #pragma diag_suppress cast_to_qualified_type - //disables nvcc specific warning in many files #pragma diag_suppress esa_on_defaulted_function_ignored #pragma diag_suppress extra_semicolon - -//Eigen only +#endif #endif // Disable vectorisation in Eigen on the Power8/9 and PowerPC From e909aeedf082f0aaba98b31cccd8119b6c38d86f Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 24 May 2022 15:29:42 +0100 Subject: [PATCH 332/399] fix: conditional pragmas according to new NVCC_DIAG_PRAGMA_SUPPORT standard in Grid_Eigen_Dense.h --- Grid/Grid_Eigen_Dense.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Grid/Grid_Eigen_Dense.h b/Grid/Grid_Eigen_Dense.h index c62d9cdb..5aee81de 100644 --- a/Grid/Grid_Eigen_Dense.h +++ b/Grid/Grid_Eigen_Dense.h @@ -14,7 +14,11 @@ /* NVCC save and restore compile environment*/ #ifdef __NVCC__ #pragma push +#if (__CUDACC_VER_MAJOR__ >= 11) && (__CUDACC_VER_MINOR__ >= 5) +#pragma nv_diag_suppress code_is_unreachable +#else #pragma diag_suppress code_is_unreachable +#endif #pragma push_macro("__CUDA_ARCH__") #pragma push_macro("__NVCC__") #pragma push_macro("__CUDACC__") From 7937ac2bab3eaef32222e6b28c6d4dda9f5af6d9 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 24 May 2022 15:31:03 +0100 Subject: [PATCH 333/399] fix: conditional pragmas according to new NVCC_DIAG_PRAGMA_SUPPORT standard in pugixml/pugixml.cc --- Grid/pugixml/pugixml.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Grid/pugixml/pugixml.cc b/Grid/pugixml/pugixml.cc index 45e6496a..81973964 100644 --- a/Grid/pugixml/pugixml.cc +++ b/Grid/pugixml/pugixml.cc @@ -16,8 +16,12 @@ #ifdef __NVCC__ #pragma push +#if (__CUDACC_VER_MAJOR__ >= 11) && (__CUDACC_VER_MINOR__ >= 5) +#pragma nv_diag_suppress declared_but_not_referenced // suppress "function was declared but never referenced warning" +#else #pragma diag_suppress declared_but_not_referenced // suppress "function was declared but never referenced warning" #endif +#endif #include "pugixml.h" From e346154c5dfe8ba6ada0b11bcfc8cfba0cc57f6b Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 24 May 2022 15:47:01 +0100 Subject: [PATCH 334/399] Updated json CUDA compile guards --- Grid/serialisation/JSON_IO.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/serialisation/JSON_IO.h b/Grid/serialisation/JSON_IO.h index b5255de3..090a36e1 100644 --- a/Grid/serialisation/JSON_IO.h +++ b/Grid/serialisation/JSON_IO.h @@ -54,7 +54,7 @@ namespace Grid void pop(void); template <typename U> void writeDefault(const std::string &s, const U &x); -#ifdef __NVCC__ +#if defined(GRID_CUDA) || defined(GRID_HIP) void writeDefault(const std::string &s, const Grid::ComplexD &x) { std::complex<double> z(real(x),imag(x)); @@ -101,7 +101,7 @@ namespace Grid void readDefault(const std::string &s, std::vector<U> &output); template <typename U, typename P> void readDefault(const std::string &s, std::pair<U,P> &output); -#ifdef __NVCC__ +#if defined(GRID_CUDA) || defined(GRID_HIP) void readDefault(const std::string &s, ComplexD &output) { std::complex<double> z; From da4daea57ab0afd2865f9282f13870e661e97be9 Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 24 May 2022 16:16:06 +0100 Subject: [PATCH 335/399] Updated json to latest release 3.10.5 --- Grid/json/json.hpp | 24163 ++++++++++++++++++++++++------------------- 1 file changed, 13667 insertions(+), 10496 deletions(-) diff --git a/Grid/json/json.hpp b/Grid/json/json.hpp index 5b5d2215..cb27e058 100644 --- a/Grid/json/json.hpp +++ b/Grid/json/json.hpp @@ -1,12 +1,12 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.2.0 +| | |__ | | | | | | version 3.10.5 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>. SPDX-License-Identifier: MIT -Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>. +Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,588 +27,56 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef NLOHMANN_JSON_HPP -#define NLOHMANN_JSON_HPP +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file doc/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ #define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 2 -#define NLOHMANN_JSON_VERSION_PATCH 0 +#define NLOHMANN_JSON_VERSION_MINOR 10 +#define NLOHMANN_JSON_VERSION_PATCH 5 #include <algorithm> // all_of, find, for_each -#include <cassert> // assert -#include <ciso646> // and, not, or #include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <functional> // hash, less #include <initializer_list> // initializer_list -#include <iosfwd> // istream, ostream -#include <iterator> // iterator_traits, random_access_iterator_tag +#ifndef JSON_NO_IO + #include <iosfwd> // istream, ostream +#endif // JSON_NO_IO +#include <iterator> // random_access_iterator_tag +#include <memory> // unique_ptr #include <numeric> // accumulate #include <string> // string, stoi, to_string #include <utility> // declval, forward, move, pair, swap - -// #include <nlohmann/json_fwd.hpp> -#ifndef NLOHMANN_JSON_FWD_HPP -#define NLOHMANN_JSON_FWD_HPP - -#include <cstdint> // int64_t, uint64_t -#include <map> // map -#include <memory> // allocator -#include <string> // string #include <vector> // vector -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ -/*! -@brief default JSONSerializer template argument - -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template<typename T = void, typename SFINAE = void> -struct adl_serializer; - -template<template<typename U, typename V, typename... Args> class ObjectType = - std::map, - template<typename U, typename... Args> class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template<typename U> class AllocatorType = std::allocator, - template<typename T, typename SFINAE = void> class JSONSerializer = - adl_serializer> -class basic_json; - -/*! -@brief JSON Pointer - -A JSON pointer defines a string syntax for identifying a specific value -within a JSON document. It can be used with functions `at` and -`operator[]`. Furthermore, JSON pointers are the base for JSON patches. - -@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) - -@since version 2.0.0 -*/ -template<typename BasicJsonType> -class json_pointer; - -/*! -@brief default JSON class - -This type is the default specialization of the @ref basic_json class which -uses the standard template types. - -@since version 1.0.0 -*/ -using json = basic_json<>; -} - -#endif - -// #include <nlohmann/detail/macro_scope.hpp> - - -// This file contains all internal macro definitions -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) - #if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #endif -#endif - -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - -// disable documentation warnings on clang -#if defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdocumentation" -#endif - -// allow for portable deprecation warnings -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #define JSON_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) - #define JSON_DEPRECATED __declspec(deprecated) -#else - #define JSON_DEPRECATED -#endif - -// allow to disable exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) - #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) - #define JSON_INTERNAL_CATCH(exception) catch(exception) -#else - #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) - #define JSON_INTERNAL_CATCH(exception) if(false) -#endif - -// override exception macros -#if defined(JSON_THROW_USER) - #undef JSON_THROW - #define JSON_THROW JSON_THROW_USER -#endif -#if defined(JSON_TRY_USER) - #undef JSON_TRY - #define JSON_TRY JSON_TRY_USER -#endif -#if defined(JSON_CATCH_USER) - #undef JSON_CATCH - #define JSON_CATCH JSON_CATCH_USER - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_CATCH_USER -#endif -#if defined(JSON_INTERNAL_CATCH_USER) - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER -#endif - -// manual branch prediction -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) - #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else - #define JSON_LIKELY(x) x - #define JSON_UNLIKELY(x) x -#endif - -// C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 -#endif - -// Ugly macros to avoid uglier copy-paste when specializing basic_json. They -// may be removed in the future once the class is split. - -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template<template<typename, typename, typename...> class ObjectType, \ - template<typename, typename...> class ArrayType, \ - class StringType, class BooleanType, class NumberIntegerType, \ - class NumberUnsignedType, class NumberFloatType, \ - template<typename> class AllocatorType, \ - template<typename, typename = void> class JSONSerializer> - -#define NLOHMANN_BASIC_JSON_TPL \ - basic_json<ObjectType, ArrayType, StringType, BooleanType, \ - NumberIntegerType, NumberUnsignedType, NumberFloatType, \ - AllocatorType, JSONSerializer> - -// #include <nlohmann/detail/meta/cpp_future.hpp> - - -#include <ciso646> // not -#include <cstddef> // size_t -#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type - -namespace nlohmann -{ -namespace detail -{ -// alias templates to reduce boilerplate -template<bool B, typename T = void> -using enable_if_t = typename std::enable_if<B, T>::type; - -template<typename T> -using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; - -// implementation of C++14 index_sequence and affiliates -// source: https://stackoverflow.com/a/32223343 -template<std::size_t... Ints> -struct index_sequence -{ - using type = index_sequence; - using value_type = std::size_t; - static constexpr std::size_t size() noexcept - { - return sizeof...(Ints); - } -}; - -template<class Sequence1, class Sequence2> -struct merge_and_renumber; - -template<std::size_t... I1, std::size_t... I2> -struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>> - : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; - -template<std::size_t N> -struct make_index_sequence - : merge_and_renumber < typename make_index_sequence < N / 2 >::type, - typename make_index_sequence < N - N / 2 >::type > {}; - -template<> struct make_index_sequence<0> : index_sequence<> {}; -template<> struct make_index_sequence<1> : index_sequence<0> {}; - -template<typename... Ts> -using index_sequence_for = make_index_sequence<sizeof...(Ts)>; - -// dispatch utility (taken from ranges-v3) -template<unsigned N> struct priority_tag : priority_tag < N - 1 > {}; -template<> struct priority_tag<0> {}; - -// taken from ranges-v3 -template<typename T> -struct static_const -{ - static constexpr T value{}; -}; - -template<typename T> -constexpr T static_const<T>::value; -} -} - -// #include <nlohmann/detail/meta/type_traits.hpp> - - -#include <ciso646> // not -#include <limits> // numeric_limits -#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type -#include <utility> // declval - -// #include <nlohmann/json_fwd.hpp> - -// #include <nlohmann/detail/meta/cpp_future.hpp> - -// #include <nlohmann/detail/meta/detected.hpp> +// #include <nlohmann/adl_serializer.hpp> #include <type_traits> - -// #include <nlohmann/detail/meta/void_t.hpp> - - -namespace nlohmann -{ -namespace detail -{ -template <typename ...Ts> struct make_void -{ - using type = void; -}; -template <typename ...Ts> using void_t = typename make_void<Ts...>::type; -} -} - - -// http://en.cppreference.com/w/cpp/experimental/is_detected -namespace nlohmann -{ -namespace detail -{ -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - void operator=(nonesuch const&) = delete; -}; - -template <class Default, - class AlwaysVoid, - template <class...> class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; - -template <class Default, template <class...> class Op, class... Args> -struct detector<Default, void_t<Op<Args...>>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op<Args...>; -}; - -template <template <class...> class Op, class... Args> -using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t; - -template <template <class...> class Op, class... Args> -using detected_t = typename detector<nonesuch, void, Op, Args...>::type; - -template <class Default, template <class...> class Op, class... Args> -using detected_or = detector<Default, void, Op, Args...>; - -template <class Default, template <class...> class Op, class... Args> -using detected_or_t = typename detected_or<Default, Op, Args...>::type; - -template <class Expected, template <class...> class Op, class... Args> -using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>; - -template <class To, template <class...> class Op, class... Args> -using is_detected_convertible = - std::is_convertible<detected_t<Op, Args...>, To>; -} -} - -// #include <nlohmann/detail/macro_scope.hpp> - - -namespace nlohmann -{ -/*! -@brief detail namespace with internal helper functions - -This namespace collects functions that should not be exposed, -implementations of some @ref basic_json methods, and meta-programming helpers. - -@since version 2.1.0 -*/ -namespace detail -{ -///////////// -// helpers // -///////////// - -template<typename> struct is_basic_json : std::false_type {}; - -NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {}; - -////////////////////////// -// aliases for detected // -////////////////////////// - -template <typename T> -using mapped_type_t = typename T::mapped_type; - -template <typename T> -using key_type_t = typename T::key_type; - -template <typename T> -using value_type_t = typename T::value_type; - -template <typename T> -using difference_type_t = typename T::difference_type; - -template <typename T> -using pointer_t = typename T::pointer; - -template <typename T> -using reference_t = typename T::reference; - -template <typename T> -using iterator_category_t = typename T::iterator_category; - -template <typename T> -using iterator_t = typename T::iterator; - -template <typename T, typename... Args> -using to_json_function = decltype(T::to_json(std::declval<Args>()...)); - -template <typename T, typename... Args> -using from_json_function = decltype(T::from_json(std::declval<Args>()...)); - -template <typename T, typename U> -using get_template_function = decltype(std::declval<T>().template get<U>()); - -/////////////////// -// is_ functions // -/////////////////// - -template <typename T, typename = void> -struct is_iterator_traits : std::false_type {}; - -template <typename T> -struct is_iterator_traits<std::iterator_traits<T>> -{ - private: - using traits = std::iterator_traits<T>; - - public: - static constexpr auto value = - is_detected<value_type_t, traits>::value && - is_detected<difference_type_t, traits>::value && - is_detected<pointer_t, traits>::value && - is_detected<iterator_category_t, traits>::value && - is_detected<reference_t, traits>::value; -}; - -// source: https://stackoverflow.com/a/37193089/4116453 - -template <typename T, typename = void> -struct is_complete_type : std::false_type {}; - -template <typename T> -struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {}; - -template <typename BasicJsonType, typename CompatibleObjectType, - typename = void> -struct is_compatible_object_type_impl : std::false_type {}; - -template <typename BasicJsonType, typename CompatibleObjectType> -struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, - enable_if_t<is_detected<mapped_type_t, CompatibleObjectType>::value and - is_detected<key_type_t, CompatibleObjectType>::value >> -{ - - using object_t = typename BasicJsonType::object_t; - - // macOS's is_constructible does not play well with nonesuch... - static constexpr bool value = - std::is_constructible<typename object_t::key_type, - typename CompatibleObjectType::key_type>::value and - std::is_constructible<typename object_t::mapped_type, - typename CompatibleObjectType::mapped_type>::value; -}; - -template <typename BasicJsonType, typename CompatibleObjectType> -struct is_compatible_object_type - : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {}; - -template <typename BasicJsonType, typename CompatibleStringType, - typename = void> -struct is_compatible_string_type_impl : std::false_type {}; - -template <typename BasicJsonType, typename CompatibleStringType> -struct is_compatible_string_type_impl < - BasicJsonType, CompatibleStringType, - enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type, - value_type_t, CompatibleStringType>::value >> -{ - static constexpr auto value = - std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value; -}; - -template <typename BasicJsonType, typename CompatibleStringType> -struct is_compatible_string_type - : is_compatible_string_type_impl<BasicJsonType, CompatibleStringType> {}; - -template <typename BasicJsonType, typename CompatibleArrayType, typename = void> -struct is_compatible_array_type_impl : std::false_type {}; - -template <typename BasicJsonType, typename CompatibleArrayType> -struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and - is_detected<iterator_t, CompatibleArrayType>::value >> -{ - // This is needed because json_reverse_iterator has a ::iterator type... - // Therefore it is detected as a CompatibleArrayType. - // The real fix would be to have an Iterable concept. - static constexpr bool value = not is_iterator_traits<std::iterator_traits<CompatibleArrayType>>::value; -}; - -template <typename BasicJsonType, typename CompatibleArrayType> -struct is_compatible_array_type - : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {}; - -template <typename RealIntegerType, typename CompatibleNumberIntegerType, - typename = void> -struct is_compatible_integer_type_impl : std::false_type {}; - -template <typename RealIntegerType, typename CompatibleNumberIntegerType> -struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, - enable_if_t<std::is_integral<RealIntegerType>::value and - std::is_integral<CompatibleNumberIntegerType>::value and - not std::is_same<bool, CompatibleNumberIntegerType>::value >> -{ - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits<RealIntegerType>; - using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>; - - static constexpr auto value = - std::is_constructible<RealIntegerType, - CompatibleNumberIntegerType>::value and - CompatibleLimits::is_integer and - RealLimits::is_signed == CompatibleLimits::is_signed; -}; - -template <typename RealIntegerType, typename CompatibleNumberIntegerType> -struct is_compatible_integer_type - : is_compatible_integer_type_impl<RealIntegerType, - CompatibleNumberIntegerType> {}; - -// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists -template <typename BasicJsonType, typename T, typename = void> -struct has_from_json : std::false_type {}; - -template <typename BasicJsonType, typename T> -struct has_from_json<BasicJsonType, T, - enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<void, from_json_function, serializer, - const BasicJsonType&, T&>::value; -}; - -// This trait checks if JSONSerializer<T>::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template <typename BasicJsonType, typename T, typename = void> -struct has_non_default_from_json : std::false_type {}; - -template<typename BasicJsonType, typename T> -struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<T, from_json_function, serializer, - const BasicJsonType&>::value; -}; - -// This trait checks if BasicJsonType::json_serializer<T>::to_json exists -// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template <typename BasicJsonType, typename T, typename = void> -struct has_to_json : std::false_type {}; - -template <typename BasicJsonType, typename T> -struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<void, to_json_function, serializer, BasicJsonType&, - T>::value; -}; - -template <typename BasicJsonType, typename CompatibleType, typename = void> -struct is_compatible_type_impl: std::false_type {}; - -template <typename BasicJsonType, typename CompatibleType> -struct is_compatible_type_impl < - BasicJsonType, CompatibleType, - enable_if_t<is_complete_type<CompatibleType>::value >> -{ - static constexpr bool value = - has_to_json<BasicJsonType, CompatibleType>::value; -}; - -template <typename BasicJsonType, typename CompatibleType> -struct is_compatible_type - : is_compatible_type_impl<BasicJsonType, CompatibleType> {}; -} -} +#include <utility> + +// #include <nlohmann/detail/conversions/from_json.hpp> + + +#include <algorithm> // transform +#include <array> // array +#include <forward_list> // forward_list +#include <iterator> // inserter, front_inserter, end +#include <map> // map +#include <string> // string +#include <tuple> // tuple, make_tuple +#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include <unordered_map> // unordered_map +#include <utility> // pair, declval +#include <valarray> // valarray // #include <nlohmann/detail/exceptions.hpp> @@ -616,339 +84,15 @@ struct is_compatible_type #include <exception> // exception #include <stdexcept> // runtime_error #include <string> // to_string - -namespace nlohmann -{ -namespace detail -{ -//////////////// -// exceptions // -//////////////// - -/*! -@brief general exception of the @ref basic_json class - -This class is an extension of `std::exception` objects with a member @a id for -exception ids. It is used as the base class for all exceptions thrown by the -@ref basic_json class. This class can hence be used as "wildcard" to catch -exceptions. - -Subclasses: -- @ref parse_error for exceptions indicating a parse error -- @ref invalid_iterator for exceptions indicating errors with iterators -- @ref type_error for exceptions indicating executing a member function with - a wrong type -- @ref out_of_range for exceptions indicating access out of the defined range -- @ref other_error for exceptions indicating other library errors - -@internal -@note To have nothrow-copy-constructible exceptions, we internally use - `std::runtime_error` which can cope with arbitrary-length error messages. - Intermediate strings are built with static functions and then passed to - the actual constructor. -@endinternal - -@liveexample{The following code shows how arbitrary library exceptions can be -caught.,exception} - -@since version 3.0.0 -*/ -class exception : public std::exception -{ - public: - /// returns the explanatory string - const char* what() const noexcept override - { - return m.what(); - } - - /// the id of the exception - const int id; - - protected: - exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} - - static std::string name(const std::string& ename, int id_) - { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; - } - - private: - /// an exception object as storage for error messages - std::runtime_error m; -}; - -/*! -@brief exception indicating a parse error - -This exception is thrown by the library when a parse error occurs. Parse errors -can occur during the deserialization of JSON text, CBOR, MessagePack, as well -as when using JSON Patch. - -Member @a byte holds the byte index of the last read character in the input -file. - -Exceptions have ids 1xx. - -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. -json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. -json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. -json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. -json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. -json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. -json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. -json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. -json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. -json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. -json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. -json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. - -@note For an input with n bytes, 1 is the index of the first character and n+1 - is the index of the terminating null byte or the end of file. This also - holds true when reading a byte vector (CBOR or MessagePack). - -@liveexample{The following code shows how a `parse_error` exception can be -caught.,parse_error} - -@sa @ref exception for the base class of the library exceptions -@sa @ref invalid_iterator for exceptions indicating errors with iterators -@sa @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa @ref out_of_range for exceptions indicating access out of the defined range -@sa @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class parse_error : public exception -{ - public: - /*! - @brief create a parse error exception - @param[in] id_ the id of the exception - @param[in] byte_ the byte index where the error occurred (or 0 if the - position cannot be determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + - ": " + what_arg; - return parse_error(id_, byte_, w.c_str()); - } - - /*! - @brief byte index of the parse error - - The byte index of the last read character in the input file. - - @note For an input with n bytes, 1 is the index of the first character and - n+1 is the index of the terminating null byte or the end of file. - This also holds true when reading a byte vector (CBOR or MessagePack). - */ - const std::size_t byte; - - private: - parse_error(int id_, std::size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) {} -}; - -/*! -@brief exception indicating errors with iterators - -This exception is thrown if iterators passed to a library function do not match -the expected semantics. - -Exceptions have ids 2xx. - -name / id | example message | description ------------------------------------ | --------------- | ------------------------- -json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. -json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. -json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. -json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. -json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. -json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. -json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. -json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. -json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. -json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). - -@liveexample{The following code shows how an `invalid_iterator` exception can be -caught.,invalid_iterator} - -@sa @ref exception for the base class of the library exceptions -@sa @ref parse_error for exceptions indicating a parse error -@sa @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa @ref out_of_range for exceptions indicating access out of the defined range -@sa @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class invalid_iterator : public exception -{ - public: - static invalid_iterator create(int id_, const std::string& what_arg) - { - std::string w = exception::name("invalid_iterator", id_) + what_arg; - return invalid_iterator(id_, w.c_str()); - } - - private: - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating executing a member function with a wrong type - -This exception is thrown in case of a type error; that is, a library function is -executed on a JSON value whose type does not match the expected semantics. - -Exceptions have ids 3xx. - -name / id | example message | description ------------------------------ | --------------- | ------------------------- -json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. -json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. -json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. -json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. -json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. -json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. -json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. -json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. -json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. -json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. -json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. -json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. -json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. -json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. -json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. -json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | - -@liveexample{The following code shows how a `type_error` exception can be -caught.,type_error} - -@sa @ref exception for the base class of the library exceptions -@sa @ref parse_error for exceptions indicating a parse error -@sa @ref invalid_iterator for exceptions indicating errors with iterators -@sa @ref out_of_range for exceptions indicating access out of the defined range -@sa @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class type_error : public exception -{ - public: - static type_error create(int id_, const std::string& what_arg) - { - std::string w = exception::name("type_error", id_) + what_arg; - return type_error(id_, w.c_str()); - } - - private: - type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating access out of the defined range - -This exception is thrown in case a library function is called on an input -parameter that exceeds the expected range, for instance in case of array -indices or nonexisting object keys. - -Exceptions have ids 4xx. - -name / id | example message | description -------------------------------- | --------------- | ------------------------- -json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. -json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. -json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. -json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. -json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. -json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. -json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. | -json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | - -@liveexample{The following code shows how an `out_of_range` exception can be -caught.,out_of_range} - -@sa @ref exception for the base class of the library exceptions -@sa @ref parse_error for exceptions indicating a parse error -@sa @ref invalid_iterator for exceptions indicating errors with iterators -@sa @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class out_of_range : public exception -{ - public: - static out_of_range create(int id_, const std::string& what_arg) - { - std::string w = exception::name("out_of_range", id_) + what_arg; - return out_of_range(id_, w.c_str()); - } - - private: - out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating other library errors - -This exception is thrown in case of errors that cannot be classified with the -other exception types. - -Exceptions have ids 5xx. - -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. - -@sa @ref exception for the base class of the library exceptions -@sa @ref parse_error for exceptions indicating a parse error -@sa @ref invalid_iterator for exceptions indicating errors with iterators -@sa @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa @ref out_of_range for exceptions indicating access out of the defined range - -@liveexample{The following code shows how an `other_error` exception can be -caught.,other_error} - -@since version 3.0.0 -*/ -class other_error : public exception -{ - public: - static other_error create(int id_, const std::string& what_arg) - { - std::string w = exception::name("other_error", id_) + what_arg; - return other_error(id_, w.c_str()); - } - - private: - other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; -} -} +#include <vector> // vector // #include <nlohmann/detail/value_t.hpp> #include <array> // array -#include <ciso646> // and #include <cstddef> // size_t #include <cstdint> // uint8_t +#include <string> // string namespace nlohmann { @@ -977,7 +121,7 @@ number_float), because the library distinguishes these three types for numbers: @ref basic_json::number_float_t is used for floating-point numbers or to approximate integers which do not fit in the limits of their respective type. -@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON value with the default value for a given type @since version 1.0.0 @@ -992,61 +136,3696 @@ enum class value_t : std::uint8_t number_integer, ///< number value (signed integer) number_unsigned, ///< number value (unsigned integer) number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function }; /*! @brief comparison operator for JSON types Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string +- order: null < boolean < number < object < array < string < binary - furthermore, each type is not smaller than itself - discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. @since version 1.0.0 */ inline bool operator<(const value_t lhs, const value_t rhs) noexcept { - static constexpr std::array<std::uint8_t, 8> order = {{ + static constexpr std::array<std::uint8_t, 9> order = {{ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */ + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ } }; const auto l_index = static_cast<std::size_t>(lhs); const auto r_index = static_cast<std::size_t>(rhs); - return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index]; -} + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; } +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/string_escape.hpp> + + +#include <string> +// #include <nlohmann/detail/macro_scope.hpp> + + +#include <utility> // declval, pair +// #include <nlohmann/thirdparty/hedley/hedley.hpp> + + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson <evan@nemerson.com> + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see <http://creativecommons.org/publicdomain/zero/1.0/>. + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include <stdint.h> + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include <stdint.h> + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + +// #include <nlohmann/detail/meta/detected.hpp> + + +#include <type_traits> + +// #include <nlohmann/detail/meta/void_t.hpp> + + +namespace nlohmann +{ +namespace detail +{ +template<typename ...Ts> struct make_void +{ + using type = void; +}; +template<typename ...Ts> using void_t = typename make_void<Ts...>::type; +} // namespace detail +} // namespace nlohmann + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template<class Default, + class AlwaysVoid, + template<class...> class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template<class Default, template<class...> class Op, class... Args> +struct detector<Default, void_t<Op<Args...>>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op<Args...>; +}; + +template<template<class...> class Op, class... Args> +using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t; + +template<template<class...> class Op, class... Args> +struct is_detected_lazy : is_detected<Op, Args...> { }; + +template<template<class...> class Op, class... Args> +using detected_t = typename detector<nonesuch, void, Op, Args...>::type; + +template<class Default, template<class...> class Op, class... Args> +using detected_or = detector<Default, void, Op, Args...>; + +template<class Default, template<class...> class Op, class... Args> +using detected_or_t = typename detected_or<Default, Op, Args...>::type; + +template<class Expected, template<class...> class Op, class... Args> +using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>; + +template<class To, template<class...> class Op, class... Args> +using is_detected_convertible = + std::is_convertible<detected_t<Op, Args...>, To>; +} // namespace detail +} // namespace nlohmann + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include(<filesystem>) + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include(<experimental/filesystem>) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1940 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include <cstdlib> + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include <cassert> // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template<typename BasicJsonType> \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template<typename BasicJsonType> \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template<template<typename, typename, typename...> class ObjectType, \ + template<typename, typename...> class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template<typename> class AllocatorType, \ + template<typename, typename = void> class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json<ObjectType, ArrayType, StringType, BooleanType, \ + NumberIntegerType, NumberUnsignedType, NumberFloatType, \ + AllocatorType, JSONSerializer, BinaryType> + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template<typename... T> \ + using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template<typename... T> \ + std_name##_tag std_name(T&&...); \ + \ + template<typename... T> \ + using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \ + \ + template<typename... T> \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template<typename... T> \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...> \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} } -// #include <nlohmann/detail/conversions/from_json.hpp> +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/input/position_t.hpp> -#include <algorithm> // transform -#include <array> // array -#include <ciso646> // and, not -#include <forward_list> // forward_list -#include <iterator> // inserter, front_inserter, end -#include <map> // map -#include <string> // string -#include <tuple> // tuple, make_tuple -#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible -#include <unordered_map> // unordered_map -#include <utility> // pair, declval -#include <valarray> // valarray +#include <cstddef> // size_t -// #include <nlohmann/detail/exceptions.hpp> +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/macro_scope.hpp> + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/// @brief general exception of the @ref basic_json class +/// @sa https://json.nlohmann.me/api/basic_json/exception/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + template<typename BasicJsonType> + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector<std::string> tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (&current->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + static_cast<void>(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/// @brief exception indicating a parse error +/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template<typename BasicJsonType> + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + return {id_, pos.chars_read_total, w.c_str()}; + } + + template<typename BasicJsonType> + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + exception::diagnostics(context) + what_arg; + return {id_, byte_, w.c_str()}; + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/// @brief exception indicating errors with iterators +/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ +class invalid_iterator : public exception +{ + public: + template<typename BasicJsonType> + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/// @brief exception indicating executing a member function with a wrong type +/// @sa https://json.nlohmann.me/api/basic_json/type_error/ +class type_error : public exception +{ + public: + template<typename BasicJsonType> + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating access out of the defined range +/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ +class out_of_range : public exception +{ + public: + template<typename BasicJsonType> + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating other library errors +/// @sa https://json.nlohmann.me/api/basic_json/other_error/ +class other_error : public exception +{ + public: + template<typename BasicJsonType> + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +} // namespace detail +} // namespace nlohmann // #include <nlohmann/detail/macro_scope.hpp> // #include <nlohmann/detail/meta/cpp_future.hpp> + +#include <cstddef> // size_t +#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include <utility> // index_sequence, make_index_sequence, index_sequence_for + +// #include <nlohmann/detail/macro_scope.hpp> + + +namespace nlohmann +{ +namespace detail +{ + +template<typename T> +using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template<bool B, typename T = void> +using enable_if_t = typename std::enable_if<B, T>::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence<T, Ints...>); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence<int, 5>()); +// } +template <typename T, T... Ints> +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template <size_t... Ints> +using index_sequence = integer_sequence<size_t, Ints...>; + +namespace utility_internal +{ + +template <typename Seq, size_t SeqSize, size_t Rem> +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template <typename T, T... Ints, size_t SeqSize> +struct Extend<integer_sequence<T, Ints...>, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template <typename T, T... Ints, size_t SeqSize> +struct Extend<integer_sequence<T, Ints...>, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence<T, N>'. +// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'. +template <typename T, size_t N> +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template <typename T> +struct Gen<T, 0> +{ + using type = integer_sequence<T>; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template <typename T, T N> +using make_integer_sequence = typename utility_internal::Gen<T, N>::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template <size_t N> +using make_index_sequence = make_integer_sequence<size_t, N>; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template <typename... Ts> +using index_sequence_for = make_index_sequence<sizeof...(Ts)>; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template<unsigned N> struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template<typename T> +struct static_const +{ + static constexpr T value{}; +}; + +template<typename T> +constexpr T static_const<T>::value; // NOLINT(readability-redundant-declaration) + +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/meta/identity_tag.hpp> + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template <class T> struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + // #include <nlohmann/detail/meta/type_traits.hpp> + +#include <limits> // numeric_limits +#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type +#include <utility> // declval +#include <tuple> // tuple + +// #include <nlohmann/detail/macro_scope.hpp> + + +// #include <nlohmann/detail/iterators/iterator_traits.hpp> + + +#include <iterator> // random_access_iterator_tag + +// #include <nlohmann/detail/meta/void_t.hpp> + +// #include <nlohmann/detail/meta/cpp_future.hpp> + + +namespace nlohmann +{ +namespace detail +{ +template<typename It, typename = void> +struct iterator_types {}; + +template<typename It> +struct iterator_types < + It, + void_t<typename It::difference_type, typename It::value_type, typename It::pointer, + typename It::reference, typename It::iterator_category >> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template<typename T, typename = void> +struct iterator_traits +{ +}; + +template<typename T> +struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >> + : iterator_types<T> +{ +}; + +template<typename T> +struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/meta/call_std/begin.hpp> + + +// #include <nlohmann/detail/macro_scope.hpp> + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); +} // namespace nlohmann + +// #include <nlohmann/detail/meta/call_std/end.hpp> + + +// #include <nlohmann/detail/macro_scope.hpp> + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); +} // namespace nlohmann + +// #include <nlohmann/detail/meta/cpp_future.hpp> + +// #include <nlohmann/detail/meta/detected.hpp> + +// #include <nlohmann/json_fwd.hpp> +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include <cstdint> // int64_t, uint64_t +#include <map> // map +#include <memory> // allocator +#include <string> // string +#include <vector> // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template<typename T = void, typename SFINAE = void> +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template<template<typename U, typename V, typename... Args> class ObjectType = + std::map, + template<typename U, typename... Args> class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template<typename U> class AllocatorType = std::allocator, + template<typename T, typename SFINAE = void> class JSONSerializer = + adl_serializer, + class BinaryType = std::vector<std::uint8_t>> +class basic_json; + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template<typename BasicJsonType> +class json_pointer; + +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; + +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template<class Key, class T, class IgnoredLess, class Allocator> +struct ordered_map; + +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json<nlohmann::ordered_map>; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval<T>()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template<typename> struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template<typename> +class json_ref; + +template<typename> +struct is_json_ref : std::false_type {}; + +template<typename T> +struct is_json_ref<json_ref<T>> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template<typename T> +using mapped_type_t = typename T::mapped_type; + +template<typename T> +using key_type_t = typename T::key_type; + +template<typename T> +using value_type_t = typename T::value_type; + +template<typename T> +using difference_type_t = typename T::difference_type; + +template<typename T> +using pointer_t = typename T::pointer; + +template<typename T> +using reference_t = typename T::reference; + +template<typename T> +using iterator_category_t = typename T::iterator_category; + +template<typename T, typename... Args> +using to_json_function = decltype(T::to_json(std::declval<Args>()...)); + +template<typename T, typename... Args> +using from_json_function = decltype(T::from_json(std::declval<Args>()...)); + +template<typename T, typename U> +using get_template_function = decltype(std::declval<T>().template get<U>()); + +// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists +template<typename BasicJsonType, typename T, typename = void> +struct has_from_json : std::false_type {}; + +// trait checking if j.get<T> is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template <typename BasicJsonType, typename T> +struct is_getable +{ + static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value; +}; + +template<typename BasicJsonType, typename T> +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<void, from_json_function, serializer, + const BasicJsonType&, T&>::value; +}; + +// This trait checks if JSONSerializer<T>::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template<typename BasicJsonType, typename T, typename = void> +struct has_non_default_from_json : std::false_type {}; + +template<typename BasicJsonType, typename T> +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<T, from_json_function, serializer, + const BasicJsonType&>::value; +}; + +// This trait checks if BasicJsonType::json_serializer<T>::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template<typename BasicJsonType, typename T, typename = void> +struct has_to_json : std::false_type {}; + +template<typename BasicJsonType, typename T> +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<void, to_json_function, serializer, BasicJsonType&, + T>::value; +}; + + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template<class...> struct conjunction : std::true_type { }; +template<class B1> struct conjunction<B1> : B1 { }; +template<class B1, class... Bn> +struct conjunction<B1, Bn...> +: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template<class B> struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template <typename T> +struct is_default_constructible : std::is_default_constructible<T> {}; + +template <typename T1, typename T2> +struct is_default_constructible<std::pair<T1, T2>> + : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {}; + +template <typename T1, typename T2> +struct is_default_constructible<const std::pair<T1, T2>> + : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {}; + +template <typename... Ts> +struct is_default_constructible<std::tuple<Ts...>> + : conjunction<is_default_constructible<Ts>...> {}; + +template <typename... Ts> +struct is_default_constructible<const std::tuple<Ts...>> + : conjunction<is_default_constructible<Ts>...> {}; + + +template <typename T, typename... Args> +struct is_constructible : std::is_constructible<T, Args...> {}; + +template <typename T1, typename T2> +struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {}; + +template <typename T1, typename T2> +struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {}; + +template <typename... Ts> +struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {}; + +template <typename... Ts> +struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {}; + + +template<typename T, typename = void> +struct is_iterator_traits : std::false_type {}; + +template<typename T> +struct is_iterator_traits<iterator_traits<T>> +{ + private: + using traits = iterator_traits<T>; + + public: + static constexpr auto value = + is_detected<value_type_t, traits>::value && + is_detected<difference_type_t, traits>::value && + is_detected<pointer_t, traits>::value && + is_detected<iterator_category_t, traits>::value && + is_detected<reference_t, traits>::value; +}; + +template<typename T> +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference<T>::type; + + using iterator = detected_t<result_of_begin, t_ref>; + using sentinel = detected_t<result_of_end, t_ref>; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits<iterator_traits<iterator>>::value; + + public: + static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin; +}; + +template<typename R> +using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>; + +template<typename T> +using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template<typename T, typename = void> +struct is_complete_type : std::false_type {}; + +template<typename T> +struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {}; + +template<typename BasicJsonType, typename CompatibleObjectType, + typename = void> +struct is_compatible_object_type_impl : std::false_type {}; + +template<typename BasicJsonType, typename CompatibleObjectType> +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&& + is_detected<key_type_t, CompatibleObjectType>::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible<typename object_t::key_type, + typename CompatibleObjectType::key_type>::value && + is_constructible<typename object_t::mapped_type, + typename CompatibleObjectType::mapped_type>::value; +}; + +template<typename BasicJsonType, typename CompatibleObjectType> +struct is_compatible_object_type + : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {}; + +template<typename BasicJsonType, typename ConstructibleObjectType, + typename = void> +struct is_constructible_object_type_impl : std::false_type {}; + +template<typename BasicJsonType, typename ConstructibleObjectType> +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&& + is_detected<key_type_t, ConstructibleObjectType>::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible<ConstructibleObjectType>::value && + (std::is_move_assignable<ConstructibleObjectType>::value || + std::is_copy_assignable<ConstructibleObjectType>::value) && + (is_constructible<typename ConstructibleObjectType::key_type, + typename object_t::key_type>::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json<BasicJsonType, + typename ConstructibleObjectType::mapped_type>::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template<typename BasicJsonType, typename ConstructibleObjectType> +struct is_constructible_object_type + : is_constructible_object_type_impl<BasicJsonType, + ConstructibleObjectType> {}; + +template<typename BasicJsonType, typename CompatibleStringType> +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value; +}; + +template<typename BasicJsonType, typename ConstructibleStringType> +struct is_constructible_string_type +{ + static constexpr auto value = + is_constructible<ConstructibleStringType, + typename BasicJsonType::string_t>::value; +}; + +template<typename BasicJsonType, typename CompatibleArrayType, typename = void> +struct is_compatible_array_type_impl : std::false_type {}; + +template<typename BasicJsonType, typename CompatibleArrayType> +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected<iterator_t, CompatibleArrayType>::value&& + is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >> +{ + static constexpr bool value = + is_constructible<BasicJsonType, + range_value_t<CompatibleArrayType>>::value; +}; + +template<typename BasicJsonType, typename CompatibleArrayType> +struct is_compatible_array_type + : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {}; + +template<typename BasicJsonType, typename ConstructibleArrayType, typename = void> +struct is_constructible_array_type_impl : std::false_type {}; + +template<typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t<std::is_same<ConstructibleArrayType, + typename BasicJsonType::value_type>::value >> + : std::true_type {}; + +template<typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same<ConstructibleArrayType, + typename BasicJsonType::value_type>::value&& + !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&& + is_default_constructible<ConstructibleArrayType>::value&& +(std::is_move_assignable<ConstructibleArrayType>::value || + std::is_copy_assignable<ConstructibleArrayType>::value)&& +is_detected<iterator_t, ConstructibleArrayType>::value&& +is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&& +is_detected<range_value_t, ConstructibleArrayType>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&& + is_complete_type < + detected_t<range_value_t, ConstructibleArrayType >>::value >> +{ + using value_type = range_value_t<ConstructibleArrayType>; + + static constexpr bool value = + std::is_same<value_type, + typename BasicJsonType::array_t::value_type>::value || + has_from_json<BasicJsonType, + value_type>::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template<typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type + : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {}; + +template<typename RealIntegerType, typename CompatibleNumberIntegerType, + typename = void> +struct is_compatible_integer_type_impl : std::false_type {}; + +template<typename RealIntegerType, typename CompatibleNumberIntegerType> +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral<RealIntegerType>::value&& + std::is_integral<CompatibleNumberIntegerType>::value&& + !std::is_same<bool, CompatibleNumberIntegerType>::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits<RealIntegerType>; + using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>; + + static constexpr auto value = + is_constructible<RealIntegerType, + CompatibleNumberIntegerType>::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template<typename RealIntegerType, typename CompatibleNumberIntegerType> +struct is_compatible_integer_type + : is_compatible_integer_type_impl<RealIntegerType, + CompatibleNumberIntegerType> {}; + +template<typename BasicJsonType, typename CompatibleType, typename = void> +struct is_compatible_type_impl: std::false_type {}; + +template<typename BasicJsonType, typename CompatibleType> +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t<is_complete_type<CompatibleType>::value >> +{ + static constexpr bool value = + has_to_json<BasicJsonType, CompatibleType>::value; +}; + +template<typename BasicJsonType, typename CompatibleType> +struct is_compatible_type + : is_compatible_type_impl<BasicJsonType, CompatibleType> {}; + +template<typename T1, typename T2> +struct is_constructible_tuple : std::false_type {}; + +template<typename T1, typename... Args> +struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {}; + +// a naive helper to check if a type is an ordered_map (exploits the fact that +// ordered_map inherits capacity() from std::vector) +template <typename T> +struct is_ordered_map +{ + using one = char; + + struct two + { + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + }; + + template <typename C> static one test( decltype(&C::capacity) ) ; + template <typename C> static two test(...); + + enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) +}; + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 > +T conditional_static_cast(U value) +{ + return static_cast<T>(value); +} + +template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0> +T conditional_static_cast(U value) +{ + return value; +} + +} // namespace detail +} // namespace nlohmann + // #include <nlohmann/detail/value_t.hpp> +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include <experimental/filesystem> +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include <filesystem> +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif + namespace nlohmann { namespace detail @@ -1054,18 +3833,18 @@ namespace detail template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { - if (JSON_UNLIKELY(not j.is_null())) + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); } n = nullptr; } // overloads for basic_json template parameters -template<typename BasicJsonType, typename ArithmeticType, - enable_if_t<std::is_arithmetic<ArithmeticType>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, - int> = 0> +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic<ArithmeticType>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, + int > = 0 > void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast<value_t>(j)) @@ -1086,17 +3865,24 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) break; } + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::binary: + case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); } } template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { - if (JSON_UNLIKELY(not j.is_boolean())) + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); } b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); } @@ -1104,25 +3890,25 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) template<typename BasicJsonType> void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { - if (JSON_UNLIKELY(not j.is_string())) + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); } template < - typename BasicJsonType, typename CompatibleStringType, + typename BasicJsonType, typename ConstructibleStringType, enable_if_t < - is_compatible_string_type<BasicJsonType, CompatibleStringType>::value and - not std::is_same<typename BasicJsonType::string_t, - CompatibleStringType>::value, + is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&& + !std::is_same<typename BasicJsonType::string_t, + ConstructibleStringType>::value, int > = 0 > -void from_json(const BasicJsonType& j, CompatibleStringType& s) +void from_json(const BasicJsonType& j, ConstructibleStringType& s) { - if (JSON_UNLIKELY(not j.is_string())) + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); @@ -1157,13 +3943,14 @@ void from_json(const BasicJsonType& j, EnumType& e) // forward_list doesn't have an insert method template<typename BasicJsonType, typename T, typename Allocator, - enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> + enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) { - if (JSON_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } + l.clear(); std::transform(j.rbegin(), j.rend(), std::front_inserter(l), [](const BasicJsonType & i) { @@ -1173,15 +3960,29 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) // valarray doesn't have an insert method template<typename BasicJsonType, typename T, - enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> + enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0> void from_json(const BasicJsonType& j, std::valarray<T>& l) { - if (JSON_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } l.resize(j.size()); - std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l)); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get<T>(); + }); +} + +template<typename BasicJsonType, typename T, std::size_t N> +auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get<T>(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get<T>(); + } } template<typename BasicJsonType> @@ -1190,7 +3991,7 @@ void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_ arr = *j.template get_ptr<const typename BasicJsonType::array_t*>(); } -template <typename BasicJsonType, typename T, std::size_t N> +template<typename BasicJsonType, typename T, std::size_t N> auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/) -> decltype(j.template get<T>(), void()) @@ -1201,95 +4002,136 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, } } -template<typename BasicJsonType, typename CompatibleArrayType> -auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/) +template<typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t< + std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value, + int> = 0> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( - arr.reserve(std::declval<typename CompatibleArrayType::size_type>()), - j.template get<typename CompatibleArrayType::value_type>(), + arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()), + j.template get<typename ConstructibleArrayType::value_type>(), void()) { using std::end; - arr.reserve(j.size()); + ConstructibleArrayType ret; + ret.reserve(j.size()); std::transform(j.begin(), j.end(), - std::inserter(arr, end(arr)), [](const BasicJsonType & i) + std::inserter(ret, end(ret)), [](const BasicJsonType & i) { // get<BasicJsonType>() returns *this, this won't call a from_json // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); + return i.template get<typename ConstructibleArrayType::value_type>(); }); + arr = std::move(ret); } -template <typename BasicJsonType, typename CompatibleArrayType> -void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, +template<typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t< + std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value, + int> = 0> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<0> /*unused*/) { using std::end; + ConstructibleArrayType ret; std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), + j.begin(), j.end(), std::inserter(ret, end(ret)), [](const BasicJsonType & i) { // get<BasicJsonType>() returns *this, this won't call a from_json // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); + return i.template get<typename ConstructibleArrayType::value_type>(); }); + arr = std::move(ret); } -template <typename BasicJsonType, typename CompatibleArrayType, - enable_if_t < - is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and - not is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value and - not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and - not is_basic_json<CompatibleArrayType>::value, - int > = 0 > - -auto from_json(const BasicJsonType& j, CompatibleArrayType& arr) +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&& + !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&& + !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&& + !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&& + !is_basic_json<ConstructibleArrayType>::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), -j.template get<typename CompatibleArrayType::value_type>(), +j.template get<typename ConstructibleArrayType::value_type>(), void()) { - if (JSON_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + - std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } from_json_array_impl(j, arr, priority_tag<3> {}); } -template<typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0> -void from_json(const BasicJsonType& j, CompatibleObjectType& obj) +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/) { - if (JSON_UNLIKELY(not j.is_object())) + return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } }; +} + +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag) +-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } - auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>(); - using value_type = typename CompatibleObjectType::value_type; + return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}); +} + +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + } + + bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); +} + +template<typename BasicJsonType, typename ConstructibleObjectType, + enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + } + + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>(); + using value_type = typename ConstructibleObjectType::value_type; std::transform( inner_object->begin(), inner_object->end(), - std::inserter(obj, obj.begin()), + std::inserter(ret, ret.begin()), [](typename BasicJsonType::object_t::value_type const & p) { - return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>()); + return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>()); }); + obj = std::move(ret); } // overload for arithmetic types, not chosen for basic_json template arguments // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? -template<typename BasicJsonType, typename ArithmeticType, - enable_if_t < - std::is_arithmetic<ArithmeticType>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and - not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, - int> = 0> +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic<ArithmeticType>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&& + !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, + int > = 0 > void from_json(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast<value_t>(j)) @@ -1315,111 +4157,157 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) break; } + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::binary: + case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); } } +template<typename BasicJsonType, typename... Args, std::size_t... Idx> +std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/) +{ + return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward<BasicJsonType>(j).at(0).template get<A1>(), + std::forward<BasicJsonType>(j).at(1).template get<A2>()}; +} + template<typename BasicJsonType, typename A1, typename A2> -void from_json(const BasicJsonType& j, std::pair<A1, A2>& p) +void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/) { - p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()}; -} - -template<typename BasicJsonType, typename Tuple, std::size_t... Idx> -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>) -{ - t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...); + p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {}); } template<typename BasicJsonType, typename... Args> -void from_json(const BasicJsonType& j, std::tuple<Args...>& t) +std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/) { - from_json_tuple_impl(j, t, index_sequence_for<Args...> {}); + return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {}); } -template <typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, - typename = enable_if_t<not std::is_constructible< - typename BasicJsonType::string_t, Key>::value>> +template<typename BasicJsonType, typename... Args> +void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/) +{ + t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {}); +} + +template<typename BasicJsonType, typename TupleRelated> +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}); +} + +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m) { - if (JSON_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } + m.clear(); for (const auto& p : j) { - if (JSON_UNLIKELY(not p.is_array())) + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); } m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>()); } } -template <typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, - typename = enable_if_t<not std::is_constructible< - typename BasicJsonType::string_t, Key>::value>> +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m) { - if (JSON_UNLIKELY(not j.is_array())) + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } + m.clear(); for (const auto& p : j) { - if (JSON_UNLIKELY(not p.is_array())) + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); } m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>()); } } +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, std_fs::path& p) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + p = *j.template get_ptr<const typename BasicJsonType::string_t*>(); +} +#endif + struct from_json_fn { template<typename BasicJsonType, typename T> - auto operator()(const BasicJsonType& j, T& val) const - noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward<T>(val)))) + -> decltype(from_json(j, std::forward<T>(val))) { - return from_json(j, val); + return from_json(j, std::forward<T>(val)); } }; -} +} // namespace detail /// namespace to hold default `from_json` function /// to see why this is required: /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html -namespace +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) { -constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; -} -} +constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann // #include <nlohmann/detail/conversions/to_json.hpp> -#include <ciso646> // or, and, not +#include <algorithm> // copy #include <iterator> // begin, end +#include <string> // string #include <tuple> // tuple, get #include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type #include <utility> // move, forward, declval, pair #include <valarray> // valarray #include <vector> // vector -// #include <nlohmann/detail/meta/cpp_future.hpp> - -// #include <nlohmann/detail/meta/type_traits.hpp> - -// #include <nlohmann/detail/value_t.hpp> +// #include <nlohmann/detail/macro_scope.hpp> // #include <nlohmann/detail/iterators/iteration_proxy.hpp> #include <cstddef> // size_t -#include <string> // string, to_string #include <iterator> // input_iterator_tag +#include <string> // string, to_string +#include <tuple> // tuple_size, get, tuple_element +#include <utility> // move + +// #include <nlohmann/detail/meta/type_traits.hpp> // #include <nlohmann/detail/value_t.hpp> @@ -1428,100 +4316,114 @@ namespace nlohmann { namespace detail { +template<typename string_type> +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template<typename IteratorType> class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str{}; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept + : anchor(std::move(it)) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + /// proxy class for the items() function template<typename IteratorType> class iteration_proxy { private: - /// helper class for iteration - class iteration_proxy_internal - { - public: - using difference_type = std::ptrdiff_t; - using value_type = iteration_proxy_internal; - using pointer = iteration_proxy_internal*; - using reference = iteration_proxy_internal&; - using iterator_category = std::input_iterator_tag; - - private: - /// the iterator - IteratorType anchor; - /// an index for arrays (used to create key names) - std::size_t array_index = 0; - /// last stringified array index - mutable std::size_t array_index_last = 0; - /// a string representation of the array index - mutable std::string array_index_str = "0"; - /// an empty string (to return a reference for primitive values) - const std::string empty_str = ""; - - public: - explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {} - - iteration_proxy_internal(const iteration_proxy_internal&) = default; - iteration_proxy_internal& operator=(const iteration_proxy_internal&) = default; - - /// dereference operator (needed for range-based for) - iteration_proxy_internal& operator*() - { - return *this; - } - - /// increment operator (needed for range-based for) - iteration_proxy_internal& operator++() - { - ++anchor; - ++array_index; - - return *this; - } - - /// equality operator (needed for InputIterator) - bool operator==(const iteration_proxy_internal& o) const noexcept - { - return anchor == o.anchor; - } - - /// inequality operator (needed for range-based for) - bool operator!=(const iteration_proxy_internal& o) const noexcept - { - return anchor != o.anchor; - } - - /// return key of the iterator - const std::string& key() const - { - assert(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - if (array_index != array_index_last) - { - array_index_str = std::to_string(array_index); - array_index_last = array_index; - } - return array_index_str; - } - - // use key from the object - case value_t::object: - return anchor.key(); - - // use an empty key for all primitive types - default: - return empty_str; - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } - }; - /// the container to iterate typename IteratorType::reference container; @@ -1531,20 +4433,84 @@ template<typename IteratorType> class iteration_proxy : container(cont) {} /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() noexcept + iteration_proxy_value<IteratorType> begin() noexcept { - return iteration_proxy_internal(container.begin()); + return iteration_proxy_value<IteratorType>(container.begin()); } /// return iterator end (needed for range-based for) - iteration_proxy_internal end() noexcept + iteration_proxy_value<IteratorType> end() noexcept { - return iteration_proxy_internal(container.end()); + return iteration_proxy_value<IteratorType>(container.end()); } }; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0> +auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key()) +{ + return i.key(); } +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0> +auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value()) +{ + return i.value(); } +} // namespace detail +} // namespace nlohmann +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template<typename IteratorType> +class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> + : public std::integral_constant<std::size_t, 2> {}; + +template<std::size_t N, typename IteratorType> +class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> +{ + public: + using type = decltype( + get<N>(std::declval < + ::nlohmann::detail::iteration_proxy_value<IteratorType >> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std + +// #include <nlohmann/detail/meta/cpp_future.hpp> + +// #include <nlohmann/detail/meta/type_traits.hpp> + +// #include <nlohmann/detail/value_t.hpp> + + +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include <experimental/filesystem> +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include <filesystem> +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif namespace nlohmann { @@ -1554,6 +4520,13 @@ namespace detail // constructors // ////////////////// +/* + * Note all external_constructor<>::construct functions need to call + * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an + * allocated value (e.g., a string). See bug issue + * https://github.com/nlohmann/json/issues/2865 for more information. + */ + template<value_t> struct external_constructor; template<> @@ -1562,6 +4535,7 @@ struct external_constructor<value_t::boolean> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept { + j.m_value.destroy(j.m_type); j.m_type = value_t::boolean; j.m_value = b; j.assert_invariant(); @@ -1574,6 +4548,7 @@ struct external_constructor<value_t::string> template<typename BasicJsonType> static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) { + j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value = s; j.assert_invariant(); @@ -1582,28 +4557,53 @@ struct external_constructor<value_t::string> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) { + j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value = std::move(s); j.assert_invariant(); } - template<typename BasicJsonType, typename CompatibleStringType, - enable_if_t<not std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value, - int> = 0> + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value, + int > = 0 > static void construct(BasicJsonType& j, const CompatibleStringType& str) { + j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value.string = j.template create<typename BasicJsonType::string_t>(str); j.assert_invariant(); } }; +template<> +struct external_constructor<value_t::binary> +{ + template<typename BasicJsonType> + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(b); + j.assert_invariant(); + } + + template<typename BasicJsonType> + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(std::move(b)); + j.assert_invariant(); + } +}; + template<> struct external_constructor<value_t::number_float> { template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { + j.m_value.destroy(j.m_type); j.m_type = value_t::number_float; j.m_value = val; j.assert_invariant(); @@ -1616,6 +4616,7 @@ struct external_constructor<value_t::number_unsigned> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept { + j.m_value.destroy(j.m_type); j.m_type = value_t::number_unsigned; j.m_value = val; j.assert_invariant(); @@ -1628,6 +4629,7 @@ struct external_constructor<value_t::number_integer> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept { + j.m_value.destroy(j.m_type); j.m_type = value_t::number_integer; j.m_value = val; j.assert_invariant(); @@ -1640,40 +4642,49 @@ struct external_constructor<value_t::array> template<typename BasicJsonType> static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) { + j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = arr; + j.set_parents(); j.assert_invariant(); } template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { + j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = std::move(arr); + j.set_parents(); j.assert_invariant(); } - template<typename BasicJsonType, typename CompatibleArrayType, - enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value, - int> = 0> + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value, + int > = 0 > static void construct(BasicJsonType& j, const CompatibleArrayType& arr) { using std::begin; using std::end; + + j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)); + j.set_parents(); j.assert_invariant(); } template<typename BasicJsonType> static void construct(BasicJsonType& j, const std::vector<bool>& arr) { + j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->reserve(arr.size()); for (const bool x : arr) { j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); } j.assert_invariant(); } @@ -1682,10 +4693,15 @@ struct external_constructor<value_t::array> enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> static void construct(BasicJsonType& j, const std::valarray<T>& arr) { + j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->resize(arr.size()); - std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.set_parents(); j.assert_invariant(); } }; @@ -1696,28 +4712,34 @@ struct external_constructor<value_t::object> template<typename BasicJsonType> static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) { + j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value = obj; + j.set_parents(); j.assert_invariant(); } template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { + j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value = std::move(obj); + j.set_parents(); j.assert_invariant(); } - template<typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0> + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 > static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; using std::end; + j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj)); + j.set_parents(); j.assert_invariant(); } }; @@ -1781,19 +4803,25 @@ void to_json(BasicJsonType& j, const std::vector<bool>& e) external_constructor<value_t::array>::construct(j, e); } -template <typename BasicJsonType, typename CompatibleArrayType, - enable_if_t<is_compatible_array_type<BasicJsonType, - CompatibleArrayType>::value and - not is_compatible_object_type< - BasicJsonType, CompatibleArrayType>::value and - not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and - not is_basic_json<CompatibleArrayType>::value, - int> = 0> +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type<BasicJsonType, + CompatibleArrayType>::value&& + !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&& + !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&& + !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&& + !is_basic_json<CompatibleArrayType>::value, + int > = 0 > void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { external_constructor<value_t::array>::construct(j, arr); } +template<typename BasicJsonType> +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor<value_t::binary>::construct(j, bin); +} + template<typename BasicJsonType, typename T, enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray<T>& arr) @@ -1807,8 +4835,8 @@ void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) external_constructor<value_t::array>::construct(j, std::move(arr)); } -template<typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and not is_basic_json<CompatibleObjectType>::value, int> = 0> +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 > void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor<value_t::object>::construct(j, obj); @@ -1822,40 +4850,48 @@ void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) template < typename BasicJsonType, typename T, std::size_t N, - enable_if_t<not std::is_constructible<typename BasicJsonType::string_t, - const T (&)[N]>::value, - int> = 0 > -void to_json(BasicJsonType& j, const T (&arr)[N]) + enable_if_t < !std::is_constructible<typename BasicJsonType::string_t, + const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) { external_constructor<value_t::array>::construct(j, arr); } -template<typename BasicJsonType, typename... Args> -void to_json(BasicJsonType& j, const std::pair<Args...>& p) +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair<T1, T2>& p) { - j = {p.first, p.second}; + j = { p.first, p.second }; } // for https://github.com/nlohmann/json/pull/1134 template<typename BasicJsonType, typename T, - enable_if_t<std::is_same<T, typename iteration_proxy<typename BasicJsonType::iterator>::iteration_proxy_internal>::value, int> = 0> -void to_json(BasicJsonType& j, T b) noexcept + enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) { - j = {{b.key(), b.value()}}; + j = { {b.key(), b.value()} }; } template<typename BasicJsonType, typename Tuple, std::size_t... Idx> -void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...>) +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/) { - j = {std::get<Idx>(t)...}; + j = { std::get<Idx>(t)... }; } -template<typename BasicJsonType, typename... Args> -void to_json(BasicJsonType& j, const std::tuple<Args...>& t) +template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) { - to_json_tuple_impl(j, t, index_sequence_for<Args...> {}); + to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {}); } +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template<typename BasicJsonType> +void to_json(BasicJsonType& j, const std_fs::path& p) +{ + j = p.string(); +} +#endif + struct to_json_fn { template<typename BasicJsonType, typename T> @@ -1865,22 +4901,313 @@ struct to_json_fn return to_json(j, std::forward<T>(val)); } }; -} +} // namespace detail /// namespace to hold default `to_json` function -namespace +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) { -constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; +constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include <nlohmann/detail/meta/identity_tag.hpp> + +// #include <nlohmann/detail/meta/type_traits.hpp> + + +namespace nlohmann +{ + +/// @sa https://json.nlohmann.me/api/adl_serializer/ +template<typename ValueType, typename> +struct adl_serializer +{ + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template<typename BasicJsonType, typename TargetType = ValueType> + static auto from_json(BasicJsonType && j, TargetType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val))) + -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void()) + { + ::nlohmann::from_json(std::forward<BasicJsonType>(j), val); + } + + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template<typename BasicJsonType, typename TargetType = ValueType> + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))) + -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})) + { + return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}); + } + + /// @brief convert any value type to a JSON value + /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/ + template<typename BasicJsonType, typename TargetType = ValueType> + static auto to_json(BasicJsonType& j, TargetType && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val)))) + -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void()) + { + ::nlohmann::to_json(j, std::forward<TargetType>(val)); + } +}; +} // namespace nlohmann + +// #include <nlohmann/byte_container_with_subtype.hpp> + + +#include <cstdint> // uint8_t, uint64_t +#include <tuple> // tie +#include <utility> // move + +namespace nlohmann +{ + +/// @brief an internal type for a backed binary type +/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/ +template<typename BinaryType> +class byte_container_with_subtype : public BinaryType +{ + public: + using container_type = BinaryType; + using subtype_type = std::uint64_t; + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) == + std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /// @brief sets the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/ + void set_subtype(subtype_type subtype_) noexcept + { + m_subtype = subtype_; + m_has_subtype = true; + } + + /// @brief return the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/ + constexpr subtype_type subtype() const noexcept + { + return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1); + } + + /// @brief return whether the value has a subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /// @brief clears the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + subtype_type m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include <nlohmann/detail/conversions/from_json.hpp> + +// #include <nlohmann/detail/conversions/to_json.hpp> + +// #include <nlohmann/detail/exceptions.hpp> + +// #include <nlohmann/detail/hash.hpp> + + +#include <cstdint> // uint8_t +#include <cstddef> // size_t +#include <functional> // hash + +// #include <nlohmann/detail/macro_scope.hpp> + +// #include <nlohmann/detail/value_t.hpp> + + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; } + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template<typename BasicJsonType> +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast<std::size_t>(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash<string_t> {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash<bool> {}(j.template get<bool>()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_unsigned: + { + const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_float: + { + const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>()); + return combine(type, h); + } + + case BasicJsonType::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash<bool> {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype())); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash<std::uint8_t> {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return 0; // LCOV_EXCL_LINE + } } +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/input/binary_reader.hpp> + + +#include <algorithm> // generate_n +#include <array> // array +#include <cmath> // ldexp +#include <cstddef> // size_t +#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t +#include <cstdio> // snprintf +#include <cstring> // memcpy +#include <iterator> // back_inserter +#include <limits> // numeric_limits +#include <string> // char_traits, string +#include <utility> // make_pair, move +#include <vector> // vector + +// #include <nlohmann/detail/exceptions.hpp> + // #include <nlohmann/detail/input/input_adapters.hpp> -#include <cassert> // assert +#include <array> // array #include <cstddef> // size_t #include <cstring> // strlen -#include <istream> // istream #include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include <memory> // shared_ptr, make_shared, addressof #include <numeric> // accumulate @@ -1888,6 +5215,13 @@ constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; #include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include <utility> // pair, declval +#ifndef JSON_NO_IO + #include <cstdio> // FILE * + #include <istream> // istream +#endif // JSON_NO_IO + +// #include <nlohmann/detail/iterators/iterator_traits.hpp> + // #include <nlohmann/detail/macro_scope.hpp> @@ -1896,32 +5230,44 @@ namespace nlohmann namespace detail { /// the supported input formats -enum class input_format_t { json, cbor, msgpack, ubjson }; +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; //////////////////// // input adapters // //////////////////// +#ifndef JSON_NO_IO /*! -@brief abstract input adapter interface - -Produces a stream of std::char_traits<char>::int_type characters from a -std::istream, a buffer, or some other input type. Accepts the return of -exactly one non-EOF character for future input. The int_type characters -returned consist of all valid char values as positive values (typically -unsigned char), plus an EOF value outside that range, specified by the value -of the function std::char_traits<char>::eof(). This value is typically -1, but -could be any arbitrary value which is not a valid char value. +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. */ -struct input_adapter_protocol +class file_input_adapter { - /// get a character [0,255] or std::char_traits<char>::eof(). - virtual std::char_traits<char>::int_type get_character() = 0; - virtual ~input_adapter_protocol() = default; + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) noexcept = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + ~file_input_adapter() = default; + + std::char_traits<char>::int_type get_character() noexcept + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; }; -/// a type to simplify interfaces -using input_adapter_t = std::shared_ptr<input_adapter_protocol>; /*! Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at @@ -1932,103 +5278,111 @@ characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ -class input_stream_adapter : public input_adapter_protocol +class input_stream_adapter { public: - ~input_stream_adapter() override + using char_type = char; + + ~input_stream_adapter() { // clear stream flags; we use underlying streambuf I/O, do not - // maintain ifstream flags - is.clear(); + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } } explicit input_stream_adapter(std::istream& i) - : is(i), sb(*i.rdbuf()) + : is(&i), sb(i.rdbuf()) {} // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) noexcept + : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to // ensure that std::char_traits<char>::eof() and the character 0xFF do not - // end up as the same value, eg. 0xFFFFFFFF. - std::char_traits<char>::int_type get_character() override + // end up as the same value, e.g. 0xFFFFFFFF. + std::char_traits<char>::int_type get_character() { - return sb.sbumpc(); + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof())) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; } private: /// the associated input stream - std::istream& is; - std::streambuf& sb; + std::istream* is = nullptr; + std::streambuf* sb = nullptr; }; +#endif // JSON_NO_IO -/// input adapter for buffer input -class input_buffer_adapter : public input_adapter_protocol +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template<typename IteratorType> +class iterator_input_adapter { public: - input_buffer_adapter(const char* b, const std::size_t l) - : cursor(b), limit(b + l) + using char_type = typename std::iterator_traits<IteratorType>::value_type; + + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) {} - // delete because of pointer members - input_buffer_adapter(const input_buffer_adapter&) = delete; - input_buffer_adapter& operator=(input_buffer_adapter&) = delete; - - std::char_traits<char>::int_type get_character() noexcept override + typename std::char_traits<char_type>::int_type get_character() { - if (JSON_LIKELY(cursor < limit)) + if (JSON_HEDLEY_LIKELY(current != end)) { - return std::char_traits<char>::to_int_type(*(cursor++)); + auto result = std::char_traits<char_type>::to_int_type(*current); + std::advance(current, 1); + return result; } - return std::char_traits<char>::eof(); + return std::char_traits<char_type>::eof(); } private: - /// pointer to the current character - const char* cursor; - /// pointer past the last character - const char* const limit; + IteratorType current; + IteratorType end; + + template<typename BaseInputAdapter, size_t T> + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } }; -template<typename WideStringType> -class wide_string_input_adapter : public input_adapter_protocol + +template<typename BaseInputAdapter, size_t T> +struct wide_string_input_helper; + +template<typename BaseInputAdapter> +struct wide_string_input_helper<BaseInputAdapter, 4> { - public: - explicit wide_string_input_adapter(const WideStringType& w) : str(w) {} - - std::char_traits<char>::int_type get_character() noexcept override - { - // check if buffer needs to be filled - if (utf8_bytes_index == utf8_bytes_filled) - { - if (sizeof(typename WideStringType::value_type) == 2) - { - fill_buffer_utf16(); - } - else - { - fill_buffer_utf32(); - } - - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index == 0); - } - - // use buffer - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index < utf8_bytes_filled); - return utf8_bytes[utf8_bytes_index++]; - } - - private: - void fill_buffer_utf16() + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits<char>::eof(); utf8_bytes_filled = 1; @@ -2036,106 +5390,142 @@ class wide_string_input_adapter : public input_adapter_protocol else { // get the current character - const int wc = static_cast<int>(str[current_wchar++]); - - // UTF-16 to UTF-8 encoding - if (wc < 0x80) - { - utf8_bytes[0] = wc; - utf8_bytes_filled = 1; - } - else if (wc <= 0x7FF) - { - utf8_bytes[0] = 0xC0 | ((wc >> 6)); - utf8_bytes[1] = 0x80 | (wc & 0x3F); - utf8_bytes_filled = 2; - } - else if (0xD800 > wc or wc >= 0xE000) - { - utf8_bytes[0] = 0xE0 | ((wc >> 12)); - utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F); - utf8_bytes[2] = 0x80 | (wc & 0x3F); - utf8_bytes_filled = 3; - } - else - { - if (current_wchar < str.size()) - { - const int wc2 = static_cast<int>(str[current_wchar++]); - const int charcode = 0x10000 + (((wc & 0x3FF) << 10) | (wc2 & 0x3FF)); - utf8_bytes[0] = 0xf0 | (charcode >> 18); - utf8_bytes[1] = 0x80 | ((charcode >> 12) & 0x3F); - utf8_bytes[2] = 0x80 | ((charcode >> 6) & 0x3F); - utf8_bytes[3] = 0x80 | (charcode & 0x3F); - utf8_bytes_filled = 4; - } - else - { - // unknown character - ++current_wchar; - utf8_bytes[0] = wc; - utf8_bytes_filled = 1; - } - } - } - } - - void fill_buffer_utf32() - { - utf8_bytes_index = 0; - - if (current_wchar == str.size()) - { - utf8_bytes[0] = std::char_traits<char>::eof(); - utf8_bytes_filled = 1; - } - else - { - // get the current character - const int wc = static_cast<int>(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-32 to UTF-8 encoding if (wc < 0x80) { - utf8_bytes[0] = wc; + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc); utf8_bytes_filled = 1; } else if (wc <= 0x7FF) { - utf8_bytes[0] = 0xC0 | ((wc >> 6) & 0x1F); - utf8_bytes[1] = 0x80 | (wc & 0x3F); + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu)); utf8_bytes_filled = 2; } else if (wc <= 0xFFFF) { - utf8_bytes[0] = 0xE0 | ((wc >> 12) & 0x0F); - utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F); - utf8_bytes[2] = 0x80 | (wc & 0x3F); + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu)); utf8_bytes_filled = 3; } else if (wc <= 0x10FFFF) { - utf8_bytes[0] = 0xF0 | ((wc >> 18 ) & 0x07); - utf8_bytes[1] = 0x80 | ((wc >> 12) & 0x3F); - utf8_bytes[2] = 0x80 | ((wc >> 6) & 0x3F); - utf8_bytes[3] = 0x80 | (wc & 0x3F); + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu)); utf8_bytes_filled = 4; } else { // unknown character - utf8_bytes[0] = wc; + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc); utf8_bytes_filled = 1; } } } +}; + +template<typename BaseInputAdapter> +struct wide_string_input_helper<BaseInputAdapter, 2> +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits<char>::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u))); + utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u))); + utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast<unsigned int>(input.get_character()); + const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +// Wraps another input apdater to convert wide character types into individual bytes. +template<typename BaseInputAdapter, typename WideCharType> +class wide_string_input_adapter +{ + public: + using char_type = char; + + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits<char>::int_type get_character() noexcept + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer<sizeof(WideCharType)>(); + + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } + + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } private: - /// the wstring to process - const WideStringType& str; + BaseInputAdapter base_adapter; - /// index of the current wchar in str - std::size_t current_wchar = 0; + template<size_t T> + void fill_buffer() + { + wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } /// a buffer for UTF-8 bytes std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; @@ -2146,129 +5536,893 @@ class wide_string_input_adapter : public input_adapter_protocol std::size_t utf8_bytes_filled = 0; }; -class input_adapter + +template<typename IteratorType, typename Enable = void> +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits<iterator_type>::value_type; + using adapter_type = iterator_input_adapter<iterator_type>; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template<typename T> +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits<T>::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template<typename IteratorType> +struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits<iterator_type>::value_type; + using base_adapter_type = iterator_input_adapter<iterator_type>; + using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template<typename IteratorType> +typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory<IteratorType>; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope + +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template<typename ContainerType, typename Enable = void> +struct container_input_adapter_factory {}; + +template<typename ContainerType> +struct container_input_adapter_factory< ContainerType, + void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))); + + static adapter_type create(const ContainerType& container) +{ + return input_adapter(begin(container), end(container)); +} + }; + +} // namespace container_input_adapter_factory_impl + +template<typename ContainerType> +typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container); +} + +#ifndef JSON_NO_IO +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} +#endif // JSON_NO_IO + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer<CharT>::value&& + !std::is_array<CharT>::value&& + std::is_integral<typename std::remove_pointer<CharT>::type>::value&& + sizeof(typename std::remove_pointer<CharT>::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast<const char*>(b)); + const auto* ptr = reinterpret_cast<const char*>(b); + return input_adapter(ptr, ptr + length); +} + +template<typename T, std::size_t N> +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitly cast +// to the correct adapter. +class span_input_adapter { public: - // native support + template < typename CharT, + typename std::enable_if < + std::is_pointer<CharT>::value&& + std::is_integral<typename std::remove_pointer<CharT>::type>::value&& + sizeof(typename std::remove_pointer<CharT>::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {} - /// input adapter for input stream - input_adapter(std::istream& i) - : ia(std::make_shared<input_stream_adapter>(i)) {} - - /// input adapter for input stream - input_adapter(std::istream&& i) - : ia(std::make_shared<input_stream_adapter>(i)) {} - - input_adapter(const std::wstring& ws) - : ia(std::make_shared<wide_string_input_adapter<std::wstring>>(ws)) {} - - input_adapter(const std::u16string& ws) - : ia(std::make_shared<wide_string_input_adapter<std::u16string>>(ws)) {} - - input_adapter(const std::u32string& ws) - : ia(std::make_shared<wide_string_input_adapter<std::u32string>>(ws)) {} - - /// input adapter for buffer - template<typename CharT, - typename std::enable_if< - std::is_pointer<CharT>::value and - std::is_integral<typename std::remove_pointer<CharT>::type>::value and - sizeof(typename std::remove_pointer<CharT>::type) == 1, - int>::type = 0> - input_adapter(CharT b, std::size_t l) - : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {} - - // derived support - - /// input adapter for string literal - template<typename CharT, - typename std::enable_if< - std::is_pointer<CharT>::value and - std::is_integral<typename std::remove_pointer<CharT>::type>::value and - sizeof(typename std::remove_pointer<CharT>::type) == 1, - int>::type = 0> - input_adapter(CharT b) - : input_adapter(reinterpret_cast<const char*>(b), - std::strlen(reinterpret_cast<const char*>(b))) {} - - /// input adapter for iterator range with contiguous storage template<class IteratorType, typename std::enable_if< - std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value, + std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> - input_adapter(IteratorType first, IteratorType last) + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() { -#ifndef NDEBUG - // assertion to check that the iterator range is indeed contiguous, - // see http://stackoverflow.com/a/35008842/266378 for more discussion - const auto is_contiguous = std::accumulate( - first, last, std::pair<bool, int>(true, 0), - [&first](std::pair<bool, int> res, decltype(*first) val) - { - res.first &= (val == *(std::next(std::addressof(*first), res.second++))); - return res; - }).first; - assert(is_contiguous); -#endif - - // assertion to check that each element is 1 byte long - static_assert( - sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1, - "each element in the iterator range must have the size of 1 byte"); - - const auto len = static_cast<size_t>(std::distance(first, last)); - if (JSON_LIKELY(len > 0)) - { - // there is at least one element: use the address of first - ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len); - } - else - { - // the address of first cannot be used: use nullptr - ia = std::make_shared<input_buffer_adapter>(nullptr, len); - } - } - - /// input adapter for array - template<class T, std::size_t N> - input_adapter(T (&array)[N]) - : input_adapter(std::begin(array), std::end(array)) {} - - /// input adapter for contiguous container - template<class ContiguousContainer, typename - std::enable_if<not std::is_pointer<ContiguousContainer>::value and - std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value, - int>::type = 0> - input_adapter(const ContiguousContainer& c) - : input_adapter(std::begin(c), std::end(c)) {} - - operator input_adapter_t() - { - return ia; + return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) } private: - /// the actual adapter - input_adapter_t ia = nullptr; + contiguous_bytes_input_adapter ia; }; -} -} +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/input/json_sax.hpp> + + +#include <cstddef> +#include <string> // string +#include <utility> // move +#include <vector> // vector + +// #include <nlohmann/detail/exceptions.hpp> + +// #include <nlohmann/detail/macro_scope.hpp> + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template<typename BasicJsonType> +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief a floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string value was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string value. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief a binary value was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary value. + */ + virtual bool binary(binary_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + json_sax() = default; + json_sax(const json_sax&) = default; + json_sax(json_sax&&) noexcept = default; + json_sax& operator=(const json_sax&) = default; + json_sax& operator=(json_sax&&) noexcept = default; + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template<typename BasicJsonType> +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @param[in,out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + template<class Exception> + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast<void>(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template<typename Value> + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward<Value>(v)); + return &root; + } + + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward<Value>(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector<BasicJsonType*> ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template<typename BasicJsonType> +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back()) + { + if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parents(); + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (keep) + { + ref_stack.back()->set_parents(); + } + else + { + // discard array + *ref_stack.back() = discarded; + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + template<class Exception> + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast<void>(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template<typename Value> + std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward<Value>(v)); + + // check callback + const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) + { + return {false, nullptr}; + } + + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector<BasicJsonType*> ref_stack {}; + /// stack to manage which values to keep + std::vector<bool> keep_stack {}; + /// stack to manage which object keys to keep + std::vector<bool> key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template<typename BasicJsonType> +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann // #include <nlohmann/detail/input/lexer.hpp> +#include <array> // array #include <clocale> // localeconv #include <cstddef> // size_t -#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull #include <cstdio> // snprintf +#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull #include <initializer_list> // initializer_list #include <string> // char_traits, string +#include <utility> // move #include <vector> // vector -// #include <nlohmann/detail/macro_scope.hpp> - // #include <nlohmann/detail/input/input_adapters.hpp> +// #include <nlohmann/detail/input/position_t.hpp> + +// #include <nlohmann/detail/macro_scope.hpp> + namespace nlohmann { @@ -2278,19 +6432,9 @@ namespace detail // lexer // /////////// -/*! -@brief lexical analysis - -This class organizes the lexical analysis during JSON deserialization. -*/ template<typename BasicJsonType> -class lexer +class lexer_base { - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - public: /// token types for the parser enum class token_type @@ -2315,6 +6459,8 @@ class lexer }; /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST static const char* token_type_name(const token_type t) noexcept { switch (t) @@ -2329,9 +6475,9 @@ class lexer return "null literal"; case token_type::value_string: return "string literal"; - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; @@ -2357,13 +6503,37 @@ class lexer // LCOV_EXCL_STOP } } +}; +/*! +@brief lexical analysis - explicit lexer(detail::input_adapter_t&& adapter) - : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} +This class organizes the lexical analysis during JSON deserialization. +*/ +template<typename BasicJsonType, typename InputAdapterType> +class lexer : public lexer_base<BasicJsonType> +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits<char_type>::int_type; + + public: + using token_type = typename lexer_base<BasicJsonType>::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast<char_int_type>(get_decimal_point())) + {} // delete because of pointer members lexer(const lexer&) = delete; + lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~lexer() = default; private: ///////////////////// @@ -2371,10 +6541,11 @@ class lexer ///////////////////// /// return the locale-dependent decimal point + JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); - assert(loc != nullptr); + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -2400,25 +6571,25 @@ class lexer int get_codepoint() { // this function only makes sense after reading `\u` - assert(current == 'u'); + JSON_ASSERT(current == 'u'); int codepoint = 0; - const auto factors = { 12, 8, 4, 0 }; + const auto factors = { 12u, 8u, 4u, 0u }; for (const auto factor : factors) { get(); - if (current >= '0' and current <= '9') + if (current >= '0' && current <= '9') { - codepoint += ((current - 0x30) << factor); + codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor); } - else if (current >= 'A' and current <= 'F') + else if (current >= 'A' && current <= 'F') { - codepoint += ((current - 0x37) << factor); + codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor); } - else if (current >= 'a' and current <= 'f') + else if (current >= 'a' && current <= 'f') { - codepoint += ((current - 0x57) << factor); + codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor); } else { @@ -2426,7 +6597,7 @@ class lexer } } - assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); return codepoint; } @@ -2445,15 +6616,15 @@ class lexer @return true if and only if no range violation was detected */ - bool next_byte_in_range(std::initializer_list<int> ranges) + bool next_byte_in_range(std::initializer_list<char_int_type> ranges) { - assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); add(current); for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); - if (JSON_LIKELY(*range <= current and current <= *(++range))) + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) { add(current); } @@ -2470,7 +6641,7 @@ class lexer /*! @brief scan a string literal - This function scans a string according to Sect. 7 of RFC 7159. While + This function scans a string according to Sect. 7 of RFC 8259. While scanning, bytes are escaped and copied into buffer token_buffer. Then the function returns successfully, token_buffer is *not* null-terminated (as it may contain \0 bytes), and token_buffer.size() is the number of bytes in the @@ -2488,7 +6659,7 @@ class lexer reset(); // we entered the function by reading an open quote - assert(current == '\"'); + JSON_ASSERT(current == '\"'); while (true) { @@ -2496,7 +6667,7 @@ class lexer switch (get()) { // end of file while parsing string - case std::char_traits<char>::eof(): + case std::char_traits<char_type>::eof(): { error_message = "invalid string: missing closing quote"; return token_type::parse_error; @@ -2552,55 +6723,55 @@ class lexer const int codepoint1 = get_codepoint(); int codepoint = codepoint1; // start with codepoint1 - if (JSON_UNLIKELY(codepoint1 == -1)) + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if code point is a high surrogate - if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) { // expect next \uxxxx entry - if (JSON_LIKELY(get() == '\\' and get() == 'u')) + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) { const int codepoint2 = get_codepoint(); - if (JSON_UNLIKELY(codepoint2 == -1)) + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if codepoint2 is a low surrogate - if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) { // overwrite codepoint - codepoint = - // high surrogate occupies the most significant 22 bits - (codepoint1 << 10) - // low surrogate occupies the least significant 15 bits - + codepoint2 - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; + codepoint = static_cast<int>( + // high surrogate occupies the most significant 22 bits + (static_cast<unsigned int>(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast<unsigned int>(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result, so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); } else { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { - if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) { error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; return token_type::parse_error; @@ -2608,34 +6779,34 @@ class lexer } // result of the above calculation yields a proper codepoint - assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); // translate codepoint into bytes if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) - add(codepoint); + add(static_cast<char_int_type>(codepoint)); } else if (codepoint <= 0x7FF) { // 2-byte characters: 110xxxxx 10xxxxxx - add(0xC0 | (codepoint >> 6)); - add(0x80 | (codepoint & 0x3F)); + add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u))); + add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); } else if (codepoint <= 0xFFFF) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(0xE0 | (codepoint >> 12)); - add(0x80 | ((codepoint >> 6) & 0x3F)); - add(0x80 | (codepoint & 0x3F)); + add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u))); + add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu))); + add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); } else { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(0xF0 | (codepoint >> 18)); - add(0x80 | ((codepoint >> 12) & 0x3F)); - add(0x80 | ((codepoint >> 6) & 0x3F)); - add(0x80 | (codepoint & 0x3F)); + add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u))); + add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu))); + add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu))); + add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu))); } break; @@ -2652,39 +6823,194 @@ class lexer // invalid control characters case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + case 0x1F: { - error_message = "invalid string: control character must be escaped"; + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; return token_type::parse_error; } @@ -2820,7 +7146,7 @@ class lexer case 0xDE: case 0xDF: { - if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) { return token_type::parse_error; } @@ -2830,7 +7156,7 @@ class lexer // U+0800..U+0FFF: bytes E0 A0..BF 80..BF case 0xE0: { - if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -2854,7 +7180,7 @@ class lexer case 0xEE: case 0xEF: { - if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -2864,7 +7190,7 @@ class lexer // U+D000..U+D7FF: bytes ED 80..9F 80..BF case 0xED: { - if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -2874,7 +7200,7 @@ class lexer // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF case 0xF0: { - if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -2886,7 +7212,7 @@ class lexer case 0xF2: case 0xF3: { - if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -2896,7 +7222,7 @@ class lexer // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF case 0xF4: { - if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -2913,16 +7239,90 @@ class lexer } } + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits<char_type>::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits<char_type>::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { f = std::strtof(str, endptr); } + JSON_HEDLEY_NON_NULL(2) static void strtof(double& f, const char* str, char** endptr) noexcept { f = std::strtod(str, endptr); } + JSON_HEDLEY_NON_NULL(2) static void strtof(long double& f, const char* str, char** endptr) noexcept { f = std::strtold(str, endptr); @@ -2931,10 +7331,10 @@ class lexer /*! @brief scan a number literal - This function scans a string according to Sect. 6 of RFC 7159. + This function scans a string according to Sect. 6 of RFC 8259. The function is realized with a deterministic finite state machine derived - from the grammar described in RFC 7159. Starting in state "init", the + from the grammar described in RFC 8259. Starting in state "init", the input is read and used to determined the next state. Only state "done" accepts the number. State "error" is a trap state to model errors. In the table below, "anything" means any character but the ones listed before. @@ -2945,7 +7345,7 @@ class lexer minus | zero | any1 | [error] | [error] | [error] | [error] | [error] zero | done | done | exponent | done | done | decimal1 | done any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] decimal2 | decimal2 | decimal2 | exponent | done | done | done | done exponent | any2 | any2 | [error] | sign | sign | [error] | [error] sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] @@ -2968,7 +7368,7 @@ class lexer locale's decimal point is used instead of `.` to work with the locale-dependent converters. */ - token_type scan_number() + token_type scan_number() // lgtm [cpp/use-of-goto] { // reset token_buffer to store the number's bytes reset(); @@ -3006,13 +7406,9 @@ class lexer goto scan_number_any1; } - // LCOV_EXCL_START - default: - { - // all other characters are rejected outside scan_number() - assert(false); - } - // LCOV_EXCL_STOP + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } scan_number_minus: @@ -3250,7 +7646,7 @@ scan_number_done: // we are done scanning a number) unget(); - char* endptr = nullptr; + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) errno = 0; // try to parse integers first and fall back to floats @@ -3259,7 +7655,7 @@ scan_number_done: const auto x = std::strtoull(token_buffer.data(), &endptr, 10); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -3275,7 +7671,7 @@ scan_number_done: const auto x = std::strtoll(token_buffer.data(), &endptr, 10); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -3292,7 +7688,7 @@ scan_number_done: strtof(value_float, token_buffer.data(), &endptr); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); return token_type::value_float; } @@ -3302,13 +7698,14 @@ scan_number_done: @param[in] length the length of the passed literal text @param[in] return_type the token type to return on success */ - token_type scan_literal(const char* literal_text, const std::size_t length, + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, token_type return_type) { - assert(current == literal_text[0]); + JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { - if (JSON_UNLIKELY(get() != literal_text[i])) + if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i])) { error_message = "invalid literal"; return token_type::parse_error; @@ -3326,7 +7723,7 @@ scan_number_done: { token_buffer.clear(); token_string.clear(); - token_string.push_back(std::char_traits<char>::to_char_type(current)); + token_string.push_back(std::char_traits<char_type>::to_char_type(current)); } /* @@ -3339,9 +7736,11 @@ scan_number_done: @return character read from the input */ - std::char_traits<char>::int_type get() + char_int_type get() { - ++chars_read; + ++position.chars_read_total; + ++position.chars_read_current_line; + if (next_unget) { // just reset the next_unget variable and work with current @@ -3349,13 +7748,20 @@ scan_number_done: } else { - current = ia->get_character(); + current = ia.get_character(); } - if (JSON_LIKELY(current != std::char_traits<char>::eof())) + if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof())) { - token_string.push_back(std::char_traits<char>::to_char_type(current)); + token_string.push_back(std::char_traits<char_type>::to_char_type(current)); } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + return current; } @@ -3363,25 +7769,40 @@ scan_number_done: @brief unget current character (read it again on next get) We implement unget by setting variable next_unget to true. The input is not - changed - we just simulate ungetting by modifying chars_read and - token_string. The next call to get() will behave as if the unget character - is read again. + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. */ void unget() { next_unget = true; - --chars_read; - if (JSON_LIKELY(current != std::char_traits<char>::eof())) + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) { - assert(token_string.size() != 0); + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof())) + { + JSON_ASSERT(!token_string.empty()); token_string.pop_back(); } } /// add a character to token_buffer - void add(int c) + void add(char_int_type c) { - token_buffer.push_back(std::char_traits<char>::to_char_type(c)); + token_buffer.push_back(static_cast<typename string_t::value_type>(c)); } public: @@ -3418,9 +7839,9 @@ scan_number_done: ///////////////////// /// return position of last read token - constexpr std::size_t get_position() const noexcept + constexpr position_t get_position() const noexcept { - return chars_read; + return position; } /// return the last read token (for errors only). Will never contain EOF @@ -3432,17 +7853,17 @@ scan_number_done: std::string result; for (const auto c : token_string) { - if ('\x00' <= c and c <= '\x1F') + if (static_cast<unsigned char>(c) <= '\x1F') { // escape control characters - char cs[9]; - snprintf(cs, 9, "<U+%.4X>", static_cast<unsigned char>(c)); - result += cs; + std::array<char, 9> cs{{}}; + static_cast<void>((std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + result += cs.data(); } else { // add character as is - result.push_back(c); + result.push_back(static_cast<std::string::value_type>(c)); } } @@ -3450,6 +7871,7 @@ scan_number_done: } /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL constexpr const char* get_error_message() const noexcept { return error_message; @@ -3467,41 +7889,48 @@ scan_number_done: { if (get() == 0xEF) { - if (get() == 0xBB and get() == 0xBF) - { - // we completely parsed the BOM - return true; - } - else - { - // after reading 0xEF, an unexpected character followed - return false; - } + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; } - else + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + void skip_whitespace() + { + do { - // the first character is not the beginning of the BOM; unget it to - // process is later - unget(); - return true; + get(); } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); } token_type scan() { // initially, skip the BOM - if (chars_read == 0 and not skip_bom()) + if (position.chars_read_total == 0 && !skip_bom()) { error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; return token_type::parse_error; } // read next character and ignore whitespace - do + skip_whitespace(); + + // ignore comments + while (ignore_comments && current == '/') { - get(); + if (!scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); switch (current) { @@ -3521,11 +7950,20 @@ scan_number_done: // literals case 't': - return scan_literal("true", 4, token_type::literal_true); + { + std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } case 'f': - return scan_literal("false", 5, token_type::literal_false); + { + std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } case 'n': - return scan_literal("null", 4, token_type::literal_null); + { + std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } // string case '\"': @@ -3548,7 +7986,7 @@ scan_number_done: // end of input (the null byte is needed when parsing from // string literals) case '\0': - case std::char_traits<char>::eof(): + case std::char_traits<char_type>::eof(): return token_type::end_of_input; // error @@ -3560,19 +7998,22 @@ scan_number_done: private: /// input adapter - detail::input_adapter_t ia = nullptr; + InputAdapterType ia; + + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; /// the current character - std::char_traits<char>::int_type current = std::char_traits<char>::eof(); + char_int_type current = std::char_traits<char_type>::eof(); /// whether the next get() call should just return current bool next_unget = false; - /// the number of characters read - std::size_t chars_read = 0; + /// the start position of the current token + position_t position {}; /// raw input token string (for error messages) - std::vector<char> token_string {}; + std::vector<char_type> token_string {}; /// buffer for variable-length tokens (numbers, strings) string_t token_buffer {}; @@ -3586,22 +8027,10 @@ scan_number_done: number_float_t value_float = 0; /// the decimal point - const char decimal_point_char = '.'; + const char_int_type decimal_point_char = '.'; }; -} -} - -// #include <nlohmann/detail/input/parser.hpp> - - -#include <cassert> // assert -#include <cmath> // isfinite -#include <cstdint> // uint8_t -#include <functional> // function -#include <string> // string -#include <utility> // move - -// #include <nlohmann/detail/exceptions.hpp> +} // namespace detail +} // namespace nlohmann // #include <nlohmann/detail/macro_scope.hpp> @@ -3610,6 +8039,7 @@ scan_number_done: #include <cstdint> // size_t #include <utility> // declval +#include <string> // string // #include <nlohmann/detail/meta/detected.hpp> @@ -3620,53 +8050,57 @@ namespace nlohmann { namespace detail { -template <typename T> +template<typename T> using null_function_t = decltype(std::declval<T&>().null()); -template <typename T> +template<typename T> using boolean_function_t = decltype(std::declval<T&>().boolean(std::declval<bool>())); -template <typename T, typename Integer> +template<typename T, typename Integer> using number_integer_function_t = decltype(std::declval<T&>().number_integer(std::declval<Integer>())); -template <typename T, typename Unsigned> +template<typename T, typename Unsigned> using number_unsigned_function_t = decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>())); -template <typename T, typename Float, typename String> +template<typename T, typename Float, typename String> using number_float_function_t = decltype(std::declval<T&>().number_float( std::declval<Float>(), std::declval<const String&>())); -template <typename T, typename String> +template<typename T, typename String> using string_function_t = decltype(std::declval<T&>().string(std::declval<String&>())); -template <typename T> +template<typename T, typename Binary> +using binary_function_t = + decltype(std::declval<T&>().binary(std::declval<Binary&>())); + +template<typename T> using start_object_function_t = decltype(std::declval<T&>().start_object(std::declval<std::size_t>())); -template <typename T, typename String> +template<typename T, typename String> using key_function_t = decltype(std::declval<T&>().key(std::declval<String&>())); -template <typename T> +template<typename T> using end_object_function_t = decltype(std::declval<T&>().end_object()); -template <typename T> +template<typename T> using start_array_function_t = decltype(std::declval<T&>().start_array(std::declval<std::size_t>())); -template <typename T> +template<typename T> using end_array_function_t = decltype(std::declval<T&>().end_array()); -template <typename T, typename Exception> +template<typename T, typename Exception> using parse_error_function_t = decltype(std::declval<T&>().parse_error( std::declval<std::size_t>(), std::declval<const std::string&>(), std::declval<const Exception&>())); -template <typename SAX, typename BasicJsonType> +template<typename SAX, typename BasicJsonType> struct is_sax { private: @@ -3677,19 +8111,18 @@ struct is_sax using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; using exception_t = typename BasicJsonType::exception; public: static constexpr bool value = is_detected_exact<bool, null_function_t, SAX>::value && is_detected_exact<bool, boolean_function_t, SAX>::value && - is_detected_exact<bool, number_integer_function_t, SAX, - number_integer_t>::value && - is_detected_exact<bool, number_unsigned_function_t, SAX, - number_unsigned_t>::value && - is_detected_exact<bool, number_float_function_t, SAX, number_float_t, - string_t>::value && + is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value && + is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value && + is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value && is_detected_exact<bool, string_function_t, SAX, string_t>::value && + is_detected_exact<bool, binary_function_t, SAX, binary_t>::value && is_detected_exact<bool, start_object_function_t, SAX>::value && is_detected_exact<bool, key_function_t, SAX, string_t>::value && is_detected_exact<bool, end_object_function_t, SAX>::value && @@ -3698,7 +8131,7 @@ struct is_sax is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value; }; -template <typename SAX, typename BasicJsonType> +template<typename SAX, typename BasicJsonType> struct is_sax_static_asserts { private: @@ -3709,6 +8142,7 @@ struct is_sax_static_asserts using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; using exception_t = typename BasicJsonType::exception; public: @@ -3732,6 +8166,9 @@ struct is_sax_static_asserts static_assert( is_detected_exact<bool, string_function_t, SAX, string_t>::value, "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact<bool, binary_function_t, SAX, binary_t>::value, + "Missing/invalid function: bool binary(binary_t&)"); static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value, "Missing/invalid function: bool start_object(std::size_t)"); static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value, @@ -3747,718 +8184,10 @@ struct is_sax_static_asserts "Missing/invalid function: bool parse_error(std::size_t, const " "std::string&, const exception&)"); }; -} -} +} // namespace detail +} // namespace nlohmann -// #include <nlohmann/detail/input/input_adapters.hpp> - -// #include <nlohmann/detail/input/json_sax.hpp> - - -#include <cstddef> -#include <string> -#include <vector> - -// #include <nlohmann/detail/input/parser.hpp> - -// #include <nlohmann/detail/exceptions.hpp> - - -namespace nlohmann -{ - -/*! -@brief SAX interface - -This class describes the SAX interface used by @ref nlohmann::json::sax_parse. -Each function is called in different situations while the input is parsed. The -boolean return value informs the parser whether to continue processing the -input. -*/ -template<typename BasicJsonType> -struct json_sax -{ - /// type for (signed) integers - using number_integer_t = typename BasicJsonType::number_integer_t; - /// type for unsigned integers - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - /// type for floating-point numbers - using number_float_t = typename BasicJsonType::number_float_t; - /// type for strings - using string_t = typename BasicJsonType::string_t; - - /*! - @brief a null value was read - @return whether parsing should proceed - */ - virtual bool null() = 0; - - /*! - @brief a boolean value was read - @param[in] val boolean value - @return whether parsing should proceed - */ - virtual bool boolean(bool val) = 0; - - /*! - @brief an integer number was read - @param[in] val integer value - @return whether parsing should proceed - */ - virtual bool number_integer(number_integer_t val) = 0; - - /*! - @brief an unsigned integer number was read - @param[in] val unsigned integer value - @return whether parsing should proceed - */ - virtual bool number_unsigned(number_unsigned_t val) = 0; - - /*! - @brief an floating-point number was read - @param[in] val floating-point value - @param[in] s raw token value - @return whether parsing should proceed - */ - virtual bool number_float(number_float_t val, const string_t& s) = 0; - - /*! - @brief a string was read - @param[in] val string value - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool string(string_t& val) = 0; - - /*! - @brief the beginning of an object was read - @param[in] elements number of object elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_object(std::size_t elements) = 0; - - /*! - @brief an object key was read - @param[in] val object key - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool key(string_t& val) = 0; - - /*! - @brief the end of an object was read - @return whether parsing should proceed - */ - virtual bool end_object() = 0; - - /*! - @brief the beginning of an array was read - @param[in] elements number of array elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_array(std::size_t elements) = 0; - - /*! - @brief the end of an array was read - @return whether parsing should proceed - */ - virtual bool end_array() = 0; - - /*! - @brief a parse error occurred - @param[in] position the position in the input where the error occurs - @param[in] last_token the last read token - @param[in] error_msg a detailed error message - @return whether parsing should proceed (must return false) - */ - virtual bool parse_error(std::size_t position, - const std::string& last_token, - const detail::exception& ex) = 0; - - virtual ~json_sax() = default; -}; - - -namespace detail -{ -/*! -@brief SAX implementation to create a JSON value from SAX events - -This class implements the @ref json_sax interface and processes the SAX events -to create a JSON value which makes it basically a DOM parser. The structure or -hierarchy of the JSON value is managed by the stack `ref_stack` which contains -a pointer to the respective array or object for each recursion depth. - -After successful parsing, the value that is passed by reference to the -constructor contains the parsed value. - -@tparam BasicJsonType the JSON type -*/ -template<typename BasicJsonType> -class json_sax_dom_parser -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - - /*! - @param[in, out] r reference to a JSON value that is manipulated while - parsing - @param[in] allow_exceptions_ whether parse errors yield exceptions - */ - explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) - : root(r), allow_exceptions(allow_exceptions_) - {} - - bool null() - { - handle_value(nullptr); - return true; - } - - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } - - bool number_float(number_float_t val, const string_t&) - { - handle_value(val); - return true; - } - - bool string(string_t& val) - { - handle_value(val); - return true; - } - - bool start_object(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); - - if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, - "excessive object size: " + std::to_string(len))); - } - - return true; - } - - bool key(string_t& val) - { - // add null at given key and store the reference for later - object_element = &(ref_stack.back()->m_value.object->operator[](val)); - return true; - } - - bool end_object() - { - ref_stack.pop_back(); - return true; - } - - bool start_array(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); - - if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, - "excessive array size: " + std::to_string(len))); - } - - return true; - } - - bool end_array() - { - ref_stack.pop_back(); - return true; - } - - bool parse_error(std::size_t, const std::string&, - const detail::exception& ex) - { - errored = true; - if (allow_exceptions) - { - // determine the proper exception type from the id - switch ((ex.id / 100) % 100) - { - case 1: - JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex)); - case 4: - JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex)); - // LCOV_EXCL_START - case 2: - JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex)); - case 3: - JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex)); - case 5: - JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex)); - default: - assert(false); - // LCOV_EXCL_STOP - } - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - - private: - /*! - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - */ - template<typename Value> - BasicJsonType* handle_value(Value&& v) - { - if (ref_stack.empty()) - { - root = BasicJsonType(std::forward<Value>(v)); - return &root; - } - else - { - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v)); - return &(ref_stack.back()->m_value.array->back()); - } - else - { - assert(object_element); - *object_element = BasicJsonType(std::forward<Value>(v)); - return object_element; - } - } - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector<BasicJsonType*> ref_stack; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; -}; - -template<typename BasicJsonType> -class json_sax_dom_callback_parser -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using parser_callback_t = typename BasicJsonType::parser_callback_t; - using parse_event_t = typename BasicJsonType::parse_event_t; - - json_sax_dom_callback_parser(BasicJsonType& r, - const parser_callback_t cb, - const bool allow_exceptions_ = true) - : root(r), callback(cb), allow_exceptions(allow_exceptions_) - { - keep_stack.push_back(true); - } - - bool null() - { - handle_value(nullptr); - return true; - } - - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } - - bool number_float(number_float_t val, const string_t&) - { - handle_value(val); - return true; - } - - bool string(string_t& val) - { - handle_value(val); - return true; - } - - bool start_object(std::size_t len) - { - // check callback for object start - const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::object, true); - ref_stack.push_back(val.second); - - // check object limit - if (ref_stack.back()) - { - if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, - "excessive object size: " + std::to_string(len))); - } - } - - return true; - } - - bool key(string_t& val) - { - BasicJsonType k = BasicJsonType(val); - - // check callback for key - const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k); - key_keep_stack.push_back(keep); - - // add discarded value at given key and store the reference for later - if (keep and ref_stack.back()) - { - object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); - } - - return true; - } - - bool end_object() - { - if (ref_stack.back()) - { - if (not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) - { - // discard object - *ref_stack.back() = discarded; - } - } - - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - if (not ref_stack.empty() and ref_stack.back()) - { - // remove discarded value - if (ref_stack.back()->is_object()) - { - for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) - { - if (it->is_discarded()) - { - ref_stack.back()->erase(it); - break; - } - } - } - } - - return true; - } - - bool start_array(std::size_t len) - { - const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::array, true); - ref_stack.push_back(val.second); - - // check array limit - if (ref_stack.back()) - { - if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, - "excessive array size: " + std::to_string(len))); - } - } - - return true; - } - - bool end_array() - { - bool keep = true; - - if (ref_stack.back()) - { - keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (not keep) - { - // discard array - *ref_stack.back() = discarded; - } - } - - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - // remove discarded value - if (not keep and not ref_stack.empty()) - { - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->pop_back(); - } - } - - return true; - } - - bool parse_error(std::size_t, const std::string&, - const detail::exception& ex) - { - errored = true; - if (allow_exceptions) - { - // determine the proper exception type from the id - switch ((ex.id / 100) % 100) - { - case 1: - JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex)); - case 4: - JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex)); - // LCOV_EXCL_START - case 2: - JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex)); - case 3: - JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex)); - case 5: - JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex)); - default: - assert(false); - // LCOV_EXCL_STOP - } - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - - private: - /*! - @param[in] v value to add to the JSON value we build during parsing - @param[in] skip_callback whether we should skip calling the callback - function; this is required after start_array() and - start_object() SAX events, because otherwise we would call the - callback function with an empty array or object, respectively. - - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - - @return pair of boolean (whether value should be kept) and pointer (to the - passed value in the ref_stack hierarchy; nullptr if not kept) - */ - template<typename Value> - std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false) - { - assert(not keep_stack.empty()); - - // do not handle this value if we know it would be added to a discarded - // container - if (not keep_stack.back()) - { - return {false, nullptr}; - } - - // create value - auto value = BasicJsonType(std::forward<Value>(v)); - - // check callback - const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value); - - // do not handle this value if we just learnt it shall be discarded - if (not keep) - { - return {false, nullptr}; - } - - if (ref_stack.empty()) - { - root = std::move(value); - return {true, &root}; - } - else - { - // skip this value if we already decided to skip the parent - // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) - if (not ref_stack.back()) - { - return {false, nullptr}; - } - - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->push_back(std::move(value)); - return {true, &(ref_stack.back()->m_value.array->back())}; - } - else - { - // check if we should store an element for the current key - assert(not key_keep_stack.empty()); - const bool store_element = key_keep_stack.back(); - key_keep_stack.pop_back(); - - if (not store_element) - { - return {false, nullptr}; - } - - assert(object_element); - *object_element = std::move(value); - return {true, object_element}; - } - } - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector<BasicJsonType*> ref_stack; - /// stack to manage which values to keep - std::vector<bool> keep_stack; - /// stack to manage which object keys to keep - std::vector<bool> key_keep_stack; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// callback function - const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; - /// a discarded value for the callback - BasicJsonType discarded = BasicJsonType::value_t::discarded; -}; - -template<typename BasicJsonType> -class json_sax_acceptor -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - - bool null() - { - return true; - } - - bool boolean(bool) - { - return true; - } - - bool number_integer(number_integer_t) - { - return true; - } - - bool number_unsigned(number_unsigned_t) - { - return true; - } - - bool number_float(number_float_t, const string_t&) - { - return true; - } - - bool string(string_t&) - { - return true; - } - - bool start_object(std::size_t = std::size_t(-1)) - { - return true; - } - - bool key(string_t&) - { - return true; - } - - bool end_object() - { - return true; - } - - bool start_array(std::size_t = std::size_t(-1)) - { - return true; - } - - bool end_array() - { - return true; - } - - bool parse_error(std::size_t, const std::string&, const detail::exception&) - { - return false; - } -}; -} - -} - -// #include <nlohmann/detail/input/lexer.hpp> +// #include <nlohmann/detail/meta/type_traits.hpp> // #include <nlohmann/detail/value_t.hpp> @@ -4467,1517 +8196,28 @@ namespace nlohmann { namespace detail { -//////////// -// parser // -//////////// + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore, ///< ignore tags + store ///< store tags as binary type +}; /*! -@brief syntax analysis +@brief determine system byte order -This class implements a recursive decent parser. +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 */ -template<typename BasicJsonType> -class parser +static inline bool little_endianness(int num = 1) noexcept { - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using lexer_t = lexer<BasicJsonType>; - using token_type = typename lexer_t::token_type; - - public: - enum class parse_event_t : uint8_t - { - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value - }; - - using parser_callback_t = - std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>; - - /// a parser reading from an input adapter - explicit parser(detail::input_adapter_t&& adapter, - const parser_callback_t cb = nullptr, - const bool allow_exceptions_ = true) - : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) - { - // read first token - get_token(); - } - - /*! - @brief public parser interface - - @param[in] strict whether to expect the last token to be EOF - @param[in,out] result parsed JSON value - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ - void parse(const bool strict, BasicJsonType& result) - { - if (callback) - { - json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions); - sax_parse_internal(&sdp); - result.assert_invariant(); - - // in strict mode, input must be completely read - if (strict and (get_token() != token_type::end_of_input)) - { - sdp.parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input))); - } - - // in case of an error, return discarded value - if (sdp.is_errored()) - { - result = value_t::discarded; - return; - } - - // set top-level value to null if it was discarded by the callback - // function - if (result.is_discarded()) - { - result = nullptr; - } - } - else - { - json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions); - sax_parse_internal(&sdp); - result.assert_invariant(); - - // in strict mode, input must be completely read - if (strict and (get_token() != token_type::end_of_input)) - { - sdp.parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input))); - } - - // in case of an error, return discarded value - if (sdp.is_errored()) - { - result = value_t::discarded; - return; - } - } - } - - /*! - @brief public accept interface - - @param[in] strict whether to expect the last token to be EOF - @return whether the input is a proper JSON text - */ - bool accept(const bool strict = true) - { - json_sax_acceptor<BasicJsonType> sax_acceptor; - return sax_parse(&sax_acceptor, strict); - } - - template <typename SAX> - bool sax_parse(SAX* sax, const bool strict = true) - { - (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {}; - const bool result = sax_parse_internal(sax); - - // strict mode: next byte must be EOF - if (result and strict and (get_token() != token_type::end_of_input)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input))); - } - - return result; - } - - private: - template <typename SAX> - bool sax_parse_internal(SAX* sax) - { - // stack to remember the hieararchy of structured values we are parsing - // true = array; false = object - std::vector<bool> states; - // value to avoid a goto (see comment where set to true) - bool skip_to_state_evaluation = false; - - while (true) - { - if (not skip_to_state_evaluation) - { - // invariant: get_token() was called before each iteration - switch (last_token) - { - case token_type::begin_object: - { - if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1)))) - { - return false; - } - - // closing } -> we are done - if (get_token() == token_type::end_object) - { - if (JSON_UNLIKELY(not sax->end_object())) - { - return false; - } - break; - } - - // parse key - if (JSON_UNLIKELY(last_token != token_type::value_string)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string))); - } - else - { - if (JSON_UNLIKELY(not sax->key(m_lexer.get_string()))) - { - return false; - } - } - - // parse separator (:) - if (JSON_UNLIKELY(get_token() != token_type::name_separator)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator))); - } - - // remember we are now inside an object - states.push_back(false); - - // parse values - get_token(); - continue; - } - - case token_type::begin_array: - { - if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1)))) - { - return false; - } - - // closing ] -> we are done - if (get_token() == token_type::end_array) - { - if (JSON_UNLIKELY(not sax->end_array())) - { - return false; - } - break; - } - - // remember we are now inside an array - states.push_back(true); - - // parse values (no need to call get_token) - continue; - } - - case token_type::value_float: - { - const auto res = m_lexer.get_number_float(); - - if (JSON_UNLIKELY(not std::isfinite(res))) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); - } - else - { - if (JSON_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) - { - return false; - } - break; - } - } - - case token_type::literal_false: - { - if (JSON_UNLIKELY(not sax->boolean(false))) - { - return false; - } - break; - } - - case token_type::literal_null: - { - if (JSON_UNLIKELY(not sax->null())) - { - return false; - } - break; - } - - case token_type::literal_true: - { - if (JSON_UNLIKELY(not sax->boolean(true))) - { - return false; - } - break; - } - - case token_type::value_integer: - { - if (JSON_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) - { - return false; - } - break; - } - - case token_type::value_string: - { - if (JSON_UNLIKELY(not sax->string(m_lexer.get_string()))) - { - return false; - } - break; - } - - case token_type::value_unsigned: - { - if (JSON_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) - { - return false; - } - break; - } - - case token_type::parse_error: - { - // using "uninitialized" to avoid "expected" message - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized))); - } - - default: // the last token was unexpected - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value))); - } - } - } - else - { - skip_to_state_evaluation = false; - } - - // we reached this line after we successfully parsed a value - if (states.empty()) - { - // empty stack: we reached the end of the hieararchy: done - return true; - } - else - { - if (states.back()) // array - { - // comma -> next value - if (get_token() == token_type::value_separator) - { - // parse a new value - get_token(); - continue; - } - - // closing ] - if (JSON_LIKELY(last_token == token_type::end_array)) - { - if (JSON_UNLIKELY(not sax->end_array())) - { - return false; - } - - // We are done with this array. Before we can parse a - // new value, we need to evaluate the new state first. - // By setting skip_to_state_evaluation to false, we - // are effectively jumping to the beginning of this if. - assert(not states.empty()); - states.pop_back(); - skip_to_state_evaluation = true; - continue; - } - else - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array))); - } - } - else // object - { - // comma -> next value - if (get_token() == token_type::value_separator) - { - // parse key - if (JSON_UNLIKELY(get_token() != token_type::value_string)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string))); - } - else - { - if (JSON_UNLIKELY(not sax->key(m_lexer.get_string()))) - { - return false; - } - } - - // parse separator (:) - if (JSON_UNLIKELY(get_token() != token_type::name_separator)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator))); - } - - // parse values - get_token(); - continue; - } - - // closing } - if (JSON_LIKELY(last_token == token_type::end_object)) - { - if (JSON_UNLIKELY(not sax->end_object())) - { - return false; - } - - // We are done with this object. Before we can parse a - // new value, we need to evaluate the new state first. - // By setting skip_to_state_evaluation to false, we - // are effectively jumping to the beginning of this if. - assert(not states.empty()); - states.pop_back(); - skip_to_state_evaluation = true; - continue; - } - else - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object))); - } - } - } - } - } - - /// get next token from lexer - token_type get_token() - { - return (last_token = m_lexer.scan()); - } - - std::string exception_message(const token_type expected) - { - std::string error_msg = "syntax error - "; - if (last_token == token_type::parse_error) - { - error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + - m_lexer.get_token_string() + "'"; - } - else - { - error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); - } - - if (expected != token_type::uninitialized) - { - error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); - } - - return error_msg; - } - - private: - /// callback function - const parser_callback_t callback = nullptr; - /// the type of the last read token - token_type last_token = token_type::uninitialized; - /// the lexer - lexer_t m_lexer; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; -}; -} + return *reinterpret_cast<char*>(&num) == 1; } -// #include <nlohmann/detail/iterators/primitive_iterator.hpp> - -#include <cstddef> // ptrdiff_t -#include <limits> // numeric_limits - -namespace nlohmann -{ -namespace detail -{ -/* -@brief an iterator for primitive JSON types - -This class models an iterator for primitive JSON types (boolean, number, -string). It's only purpose is to allow the iterator/const_iterator classes -to "iterate" over primitive values. Internally, the iterator is modeled by -a `difference_type` variable. Value begin_value (`0`) models the begin, -end_value (`1`) models past the end. -*/ -class primitive_iterator_t -{ - private: - using difference_type = std::ptrdiff_t; - static constexpr difference_type begin_value = 0; - static constexpr difference_type end_value = begin_value + 1; - - /// iterator as signed integer type - difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)(); - - public: - constexpr difference_type get_value() const noexcept - { - return m_it; - } - - /// set iterator to a defined beginning - void set_begin() noexcept - { - m_it = begin_value; - } - - /// set iterator to a defined past the end - void set_end() noexcept - { - m_it = end_value; - } - - /// return whether the iterator can be dereferenced - constexpr bool is_begin() const noexcept - { - return m_it == begin_value; - } - - /// return whether the iterator is at end - constexpr bool is_end() const noexcept - { - return m_it == end_value; - } - - friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it == rhs.m_it; - } - - friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it < rhs.m_it; - } - - primitive_iterator_t operator+(difference_type n) noexcept - { - auto result = *this; - result += n; - return result; - } - - friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it - rhs.m_it; - } - - primitive_iterator_t& operator++() noexcept - { - ++m_it; - return *this; - } - - primitive_iterator_t const operator++(int) noexcept - { - auto result = *this; - ++m_it; - return result; - } - - primitive_iterator_t& operator--() noexcept - { - --m_it; - return *this; - } - - primitive_iterator_t const operator--(int) noexcept - { - auto result = *this; - --m_it; - return result; - } - - primitive_iterator_t& operator+=(difference_type n) noexcept - { - m_it += n; - return *this; - } - - primitive_iterator_t& operator-=(difference_type n) noexcept - { - m_it -= n; - return *this; - } -}; -} -} - -// #include <nlohmann/detail/iterators/internal_iterator.hpp> - - -// #include <nlohmann/detail/iterators/primitive_iterator.hpp> - - -namespace nlohmann -{ -namespace detail -{ -/*! -@brief an iterator value - -@note This structure could easily be a union, but MSVC currently does not allow -unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. -*/ -template<typename BasicJsonType> struct internal_iterator -{ - /// iterator for JSON objects - typename BasicJsonType::object_t::iterator object_iterator {}; - /// iterator for JSON arrays - typename BasicJsonType::array_t::iterator array_iterator {}; - /// generic iterator for all other types - primitive_iterator_t primitive_iterator {}; -}; -} -} - -// #include <nlohmann/detail/iterators/iter_impl.hpp> - - -#include <ciso646> // not -#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next -#include <type_traits> // conditional, is_const, remove_const - -// #include <nlohmann/detail/exceptions.hpp> - -// #include <nlohmann/detail/iterators/internal_iterator.hpp> - -// #include <nlohmann/detail/iterators/primitive_iterator.hpp> - -// #include <nlohmann/detail/macro_scope.hpp> - -// #include <nlohmann/detail/meta/cpp_future.hpp> - -// #include <nlohmann/detail/value_t.hpp> - - -namespace nlohmann -{ -namespace detail -{ -// forward declare, to be able to friend it later on -template<typename IteratorType> class iteration_proxy; - -/*! -@brief a template for a bidirectional iterator for the @ref basic_json class - -This class implements a both iterators (iterator and const_iterator) for the -@ref basic_json class. - -@note An iterator is called *initialized* when a pointer to a JSON value has - been set (e.g., by a constructor or a copy assignment). If the iterator is - default-constructed, it is *uninitialized* and most methods are undefined. - **The library uses assertions to detect calls on uninitialized iterators.** - -@requirement The class satisfies the following concept requirements: -- -[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): - The iterator that can be moved can be moved in both directions (i.e. - incremented and decremented). - -@since version 1.0.0, simplified in version 2.0.9, change to bidirectional - iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) -*/ -template<typename BasicJsonType> -class iter_impl -{ - /// allow basic_json to access private members - friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>; - friend BasicJsonType; - friend iteration_proxy<iter_impl>; - - using object_t = typename BasicJsonType::object_t; - using array_t = typename BasicJsonType::array_t; - // make sure BasicJsonType is basic_json or const basic_json - static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value, - "iter_impl only accepts (const) basic_json"); - - public: - - /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. - /// The C++ Standard has never required user-defined iterators to derive from std::iterator. - /// A user-defined iterator should provide publicly accessible typedefs named - /// iterator_category, value_type, difference_type, pointer, and reference. - /// Note that value_type is required to be non-const, even for constant iterators. - using iterator_category = std::bidirectional_iterator_tag; - - /// the type of the values when the iterator is dereferenced - using value_type = typename BasicJsonType::value_type; - /// a type to represent differences between iterators - using difference_type = typename BasicJsonType::difference_type; - /// defines a pointer to the type iterated over (value_type) - using pointer = typename std::conditional<std::is_const<BasicJsonType>::value, - typename BasicJsonType::const_pointer, - typename BasicJsonType::pointer>::type; - /// defines a reference to the type iterated over (value_type) - using reference = - typename std::conditional<std::is_const<BasicJsonType>::value, - typename BasicJsonType::const_reference, - typename BasicJsonType::reference>::type; - - /// default constructor - iter_impl() = default; - - /*! - @brief constructor for a given JSON instance - @param[in] object pointer to a JSON object for this iterator - @pre object != nullptr - @post The iterator is initialized; i.e. `m_object != nullptr`. - */ - explicit iter_impl(pointer object) noexcept : m_object(object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } - } - } - - /*! - @note The conventional copy constructor and copy assignment are implicitly - defined. Combined with the following converting constructor and - assignment, they support: (1) copy from iterator to iterator, (2) - copy from const iterator to const iterator, and (3) conversion from - iterator to const iterator. However conversion from const iterator - to iterator is not defined. - */ - - /*! - @brief converting constructor - @param[in] other non-const iterator to copy from - @note It is not checked whether @a other is initialized. - */ - iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept - : m_object(other.m_object), m_it(other.m_it) {} - - /*! - @brief converting assignment - @param[in,out] other non-const iterator to copy from - @return const/non-const iterator - @note It is not checked whether @a other is initialized. - */ - iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept - { - m_object = other.m_object; - m_it = other.m_it; - return *this; - } - - private: - /*! - @brief set the iterator to the first value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_begin() noexcept - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } - - case value_t::array: - { - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } - - case value_t::null: - { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); - break; - } - - default: - { - m_it.primitive_iterator.set_begin(); - break; - } - } - } - - /*! - @brief set the iterator past the last value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_end() noexcept - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - m_it.object_iterator = m_object->m_value.object->end(); - break; - } - - case value_t::array: - { - m_it.array_iterator = m_object->m_value.array->end(); - break; - } - - default: - { - m_it.primitive_iterator.set_end(); - break; - } - } - } - - public: - /*! - @brief return a reference to the value pointed to by the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator*() const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } - - case value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } - - case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - - default: - { - if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) - { - return *m_object; - } - - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } - } - } - - /*! - @brief dereference the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - pointer operator->() const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - assert(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } - - case value_t::array: - { - assert(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } - - default: - { - if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) - { - return m_object; - } - - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } - } - } - - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl const operator++(int) - { - auto result = *this; - ++(*this); - return result; - } - - /*! - @brief pre-increment (++it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator++() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - std::advance(m_it.object_iterator, 1); - break; - } - - case value_t::array: - { - std::advance(m_it.array_iterator, 1); - break; - } - - default: - { - ++m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /*! - @brief post-decrement (it--) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl const operator--(int) - { - auto result = *this; - --(*this); - return result; - } - - /*! - @brief pre-decrement (--it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator--() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } - - case value_t::array: - { - std::advance(m_it.array_iterator, -1); - break; - } - - default: - { - --m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /*! - @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator==(const iter_impl& other) const - { - // if objects are not the same, the comparison is undefined - if (JSON_UNLIKELY(m_object != other.m_object)) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - return (m_it.object_iterator == other.m_it.object_iterator); - - case value_t::array: - return (m_it.array_iterator == other.m_it.array_iterator); - - default: - return (m_it.primitive_iterator == other.m_it.primitive_iterator); - } - } - - /*! - @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator!=(const iter_impl& other) const - { - return not operator==(other); - } - - /*! - @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<(const iter_impl& other) const - { - // if objects are not the same, the comparison is undefined - if (JSON_UNLIKELY(m_object != other.m_object)) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); - - case value_t::array: - return (m_it.array_iterator < other.m_it.array_iterator); - - default: - return (m_it.primitive_iterator < other.m_it.primitive_iterator); - } - } - - /*! - @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<=(const iter_impl& other) const - { - return not other.operator < (*this); - } - - /*! - @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>(const iter_impl& other) const - { - return not operator<=(other); - } - - /*! - @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>=(const iter_impl& other) const - { - return not operator<(other); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator+=(difference_type i) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); - - case value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } - - default: - { - m_it.primitive_iterator += i; - break; - } - } - - return *this; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator-=(difference_type i) - { - return operator+=(-i); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } - - /*! - @brief addition of distance and iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - friend iter_impl operator+(difference_type i, const iter_impl& it) - { - auto result = it; - result += i; - return result; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } - - /*! - @brief return difference - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - difference_type operator-(const iter_impl& other) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); - - case value_t::array: - return m_it.array_iterator - other.m_it.array_iterator; - - default: - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } - } - - /*! - @brief access to successor - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator[](difference_type n) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); - - case value_t::array: - return *std::next(m_it.array_iterator, n); - - case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - - default: - { - if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n)) - { - return *m_object; - } - - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } - } - } - - /*! - @brief return the key of an object iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - const typename object_t::key_type& key() const - { - assert(m_object != nullptr); - - if (JSON_LIKELY(m_object->is_object())) - { - return m_it.object_iterator->first; - } - - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); - } - - /*! - @brief return the value of an iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference value() const - { - return operator*(); - } - - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it; -}; -} -} - -// #include <nlohmann/detail/iterators/iteration_proxy.hpp> - -// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp> - - -#include <cstddef> // ptrdiff_t -#include <iterator> // reverse_iterator -#include <utility> // declval - -namespace nlohmann -{ -namespace detail -{ -////////////////////// -// reverse_iterator // -////////////////////// - -/*! -@brief a template for a reverse iterator class - -@tparam Base the base iterator type to reverse. Valid types are @ref -iterator (to create @ref reverse_iterator) and @ref const_iterator (to -create @ref const_reverse_iterator). - -@requirement The class satisfies the following concept requirements: -- -[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): - The iterator that can be moved can be moved in both directions (i.e. - incremented and decremented). -- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): - It is possible to write to the pointed-to element (only if @a Base is - @ref iterator). - -@since version 1.0.0 -*/ -template<typename Base> -class json_reverse_iterator : public std::reverse_iterator<Base> -{ - public: - using difference_type = std::ptrdiff_t; - /// shortcut to the reverse iterator adapter - using base_iterator = std::reverse_iterator<Base>; - /// the reference type for the pointed-to element - using reference = typename Base::reference; - - /// create reverse iterator from iterator - explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept - : base_iterator(it) {} - - /// create reverse iterator from base class - explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} - - /// post-increment (it++) - json_reverse_iterator const operator++(int) - { - return static_cast<json_reverse_iterator>(base_iterator::operator++(1)); - } - - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - return static_cast<json_reverse_iterator&>(base_iterator::operator++()); - } - - /// post-decrement (it--) - json_reverse_iterator const operator--(int) - { - return static_cast<json_reverse_iterator>(base_iterator::operator--(1)); - } - - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - return static_cast<json_reverse_iterator&>(base_iterator::operator--()); - } - - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i)); - } - - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - return static_cast<json_reverse_iterator>(base_iterator::operator+(i)); - } - - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - return static_cast<json_reverse_iterator>(base_iterator::operator-(i)); - } - - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return base_iterator(*this) - base_iterator(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } - - /// return the key of an object iterator - auto key() const -> decltype(std::declval<Base>().key()) - { - auto it = --this->base(); - return it.key(); - } - - /// return the value of an iterator - reference value() const - { - auto it = --this->base(); - return it.operator * (); - } -}; -} -} - -// #include <nlohmann/detail/output/output_adapters.hpp> - - -#include <algorithm> // copy -#include <cstddef> // size_t -#include <ios> // streamsize -#include <iterator> // back_inserter -#include <memory> // shared_ptr, make_shared -#include <ostream> // basic_ostream -#include <string> // basic_string -#include <vector> // vector - -namespace nlohmann -{ -namespace detail -{ -/// abstract output adapter interface -template<typename CharType> struct output_adapter_protocol -{ - virtual void write_character(CharType c) = 0; - virtual void write_characters(const CharType* s, std::size_t length) = 0; - virtual ~output_adapter_protocol() = default; -}; - -/// a type to simplify interfaces -template<typename CharType> -using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>; - -/// output adapter for byte vectors -template<typename CharType> -class output_vector_adapter : public output_adapter_protocol<CharType> -{ - public: - explicit output_vector_adapter(std::vector<CharType>& vec) : v(vec) {} - - void write_character(CharType c) override - { - v.push_back(c); - } - - void write_characters(const CharType* s, std::size_t length) override - { - std::copy(s, s + length, std::back_inserter(v)); - } - - private: - std::vector<CharType>& v; -}; - -/// output adapter for output streams -template<typename CharType> -class output_stream_adapter : public output_adapter_protocol<CharType> -{ - public: - explicit output_stream_adapter(std::basic_ostream<CharType>& s) : stream(s) {} - - void write_character(CharType c) override - { - stream.put(c); - } - - void write_characters(const CharType* s, std::size_t length) override - { - stream.write(s, static_cast<std::streamsize>(length)); - } - - private: - std::basic_ostream<CharType>& stream; -}; - -/// output adapter for basic_string -template<typename CharType, typename StringType = std::basic_string<CharType>> -class output_string_adapter : public output_adapter_protocol<CharType> -{ - public: - explicit output_string_adapter(StringType& s) : str(s) {} - - void write_character(CharType c) override - { - str.push_back(c); - } - - void write_characters(const CharType* s, std::size_t length) override - { - str.append(s, length); - } - - private: - StringType& str; -}; - -template<typename CharType, typename StringType = std::basic_string<CharType>> -class output_adapter -{ - public: - output_adapter(std::vector<CharType>& vec) - : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {} - - output_adapter(std::basic_ostream<CharType>& s) - : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {} - - output_adapter(StringType& s) - : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {} - - operator output_adapter_t<CharType>() - { - return oa; - } - - private: - output_adapter_t<CharType> oa = nullptr; -}; -} -} - -// #include <nlohmann/detail/input/binary_reader.hpp> - - -#include <algorithm> // generate_n -#include <array> // array -#include <cassert> // assert -#include <cmath> // ldexp -#include <cstddef> // size_t -#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t -#include <cstdio> // snprintf -#include <cstring> // memcpy -#include <iterator> // back_inserter -#include <limits> // numeric_limits -#include <string> // char_traits, string -#include <utility> // make_pair, move - -// #include <nlohmann/detail/input/input_adapters.hpp> - -// #include <nlohmann/detail/input/json_sax.hpp> - -// #include <nlohmann/detail/exceptions.hpp> - -// #include <nlohmann/detail/macro_scope.hpp> - -// #include <nlohmann/detail/meta/is_sax.hpp> - -// #include <nlohmann/detail/value_t.hpp> - - -namespace nlohmann -{ -namespace detail -{ /////////////////// // binary reader // /////////////////// @@ -5985,14 +8225,17 @@ namespace detail /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ -template<typename BasicJsonType, typename SAX = json_sax_dom_parser<BasicJsonType>> +template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits<char_type>::int_type; public: /*! @@ -6000,30 +8243,43 @@ class binary_reader @param[in] adapter input adapter to read from */ - explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) { (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {}; - assert(ia); } + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~binary_reader() = default; + /*! @param[in] format the binary format to parse @param[in] sax_ a SAX event processor @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags - @return + @return whether parsing was successful */ + JSON_HEDLEY_NON_NULL(3) bool sax_parse(const input_format_t format, json_sax_t* sax_, - const bool strict = true) + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { sax = sax_; bool result = false; switch (format) { + case input_format_t::bson: + result = parse_bson_internal(); + break; + case input_format_t::cbor: - result = parse_cbor_internal(); + result = parse_cbor_internal(true, tag_handler); break; case input_format_t::msgpack: @@ -6034,14 +8290,13 @@ class binary_reader result = parse_ubjson_internal(); break; - // LCOV_EXCL_START - default: - assert(false); - // LCOV_EXCL_STOP + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } // strict mode: next byte must be EOF - if (result and strict) + if (result && strict) { if (format == input_format_t::ubjson) { @@ -6052,42 +8307,281 @@ class binary_reader get(); } - if (JSON_UNLIKELY(current != std::char_traits<char>::eof())) + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof())) { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, "expected end of input")); + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); } } return result; } + private: + ////////// + // BSON // + ////////// + /*! - @brief determine system byte order - - @return true if and only if system's byte order is little endian - - @note from http://stackoverflow.com/a/1001328/266378 + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser */ - static constexpr bool little_endianess(int num = 1) noexcept + bool parse_bson_internal() { - return (*reinterpret_cast<char*>(&num) == 1); + std::int32_t document_size{}; + get_number<std::int32_t, true>(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); } - private: + /*! + @brief Parses a C-style string from the BSON input. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast<typename string_t::value_type>(current); + } + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template<typename NumberType> + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + } + + return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in,out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template<typename NumberType> + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number<std::uint8_t>(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), ""); + } + + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array<char, 3> cr{{}}; + static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number<std::int32_t, true>(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + /*! @param[in] get_char whether a new character should be retrieved from the - input (true, default) or whether the last read - character should be considered instead + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated @return whether a valid CBOR value was passed to the SAX parser */ - bool parse_cbor_internal(const bool get_char = true) + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) { switch (get_char ? get() : current) { // EOF - case std::char_traits<char>::eof(): - return unexpect_eof(); + case std::char_traits<char_type>::eof(): + return unexpect_eof(input_format_t::cbor, "value"); // Integer 0x00..0x17 (0..23) case 0x00: @@ -6118,26 +8612,26 @@ class binary_reader case 0x18: // Unsigned integer (one-byte uint8_t follows) { - uint8_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } case 0x19: // Unsigned integer (two-byte uint16_t follows) { - uint16_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } case 0x1A: // Unsigned integer (four-byte uint32_t follows) { - uint32_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } case 0x1B: // Unsigned integer (eight-byte uint64_t follows) { - uint64_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); } // Negative integer -1-0x00..-1-0x17 (-1..-24) @@ -6165,33 +8659,68 @@ class binary_reader case 0x35: case 0x36: case 0x37: - return sax->number_integer(static_cast<int8_t>(0x20 - 1 - current)); + return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current)); case 0x38: // Negative integer (one-byte uint8_t follows) { - uint8_t number; - return get_number(number) and sax->number_integer(static_cast<number_integer_t>(-1) - number); + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number); } case 0x39: // Negative integer -1-n (two-byte uint16_t follows) { - uint16_t number; - return get_number(number) and sax->number_integer(static_cast<number_integer_t>(-1) - number); + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number); } case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) { - uint32_t number; - return get_number(number) and sax->number_integer(static_cast<number_integer_t>(-1) - number); + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number); } case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) { - uint64_t number; - return get_number(number) and sax->number_integer(static_cast<number_integer_t>(-1) + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number)); } + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + // UTF-8 string (0x00..0x17 bytes follow) case 0x60: case 0x61: @@ -6224,7 +8753,7 @@ class binary_reader case 0x7F: // UTF-8 string (indefinite length) { string_t s; - return get_cbor_string(s) and sax->string(s); + return get_cbor_string(s) && sax->string(s); } // array (0x00..0x17 data items follow) @@ -6252,34 +8781,34 @@ class binary_reader case 0x95: case 0x96: case 0x97: - return get_cbor_array(static_cast<std::size_t>(current & 0x1F)); + return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler); case 0x98: // array (one-byte uint8_t for n follows) { - uint8_t len; - return get_number(len) and get_cbor_array(static_cast<std::size_t>(len)); + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler); } case 0x99: // array (two-byte uint16_t for n follow) { - uint16_t len; - return get_number(len) and get_cbor_array(static_cast<std::size_t>(len)); + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler); } case 0x9A: // array (four-byte uint32_t for n follow) { - uint32_t len; - return get_number(len) and get_cbor_array(static_cast<std::size_t>(len)); + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler); } case 0x9B: // array (eight-byte uint64_t for n follow) { - uint64_t len; - return get_number(len) and get_cbor_array(static_cast<std::size_t>(len)); + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast<std::size_t>(len), tag_handler); } case 0x9F: // array (indefinite length) - return get_cbor_array(std::size_t(-1)); + return get_cbor_array(static_cast<std::size_t>(-1), tag_handler); // map (0x00..0x17 pairs of data items follow) case 0xA0: @@ -6306,34 +8835,144 @@ class binary_reader case 0xB5: case 0xB6: case 0xB7: - return get_cbor_object(static_cast<std::size_t>(current & 0x1F)); + return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler); case 0xB8: // map (one-byte uint8_t for n follows) { - uint8_t len; - return get_number(len) and get_cbor_object(static_cast<std::size_t>(len)); + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler); } case 0xB9: // map (two-byte uint16_t for n follow) { - uint16_t len; - return get_number(len) and get_cbor_object(static_cast<std::size_t>(len)); + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler); } case 0xBA: // map (four-byte uint32_t for n follow) { - uint32_t len; - return get_number(len) and get_cbor_object(static_cast<std::size_t>(len)); + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler); } case 0xBB: // map (eight-byte uint64_t for n follow) { - uint64_t len; - return get_number(len) and get_cbor_object(static_cast<std::size_t>(len)); + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast<std::size_t>(len), tag_handler); } case 0xBF: // map (indefinite length) - return get_cbor_object(std::size_t(-1)); + return get_cbor_object(static_cast<std::size_t>(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + + case cbor_tag_handler_t::ignore: + { + // ignore binary subtype + switch (current) + { + case 0xD8: + { + std::uint8_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xD9: + { + std::uint16_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDA: + { + std::uint32_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDB: + { + std::uint64_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + case cbor_tag_handler_t::store: + { + binary_t b; + // use binary subtype and store in binary container + switch (current) + { + case 0xD8: + { + std::uint8_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype)); + break; + } + case 0xD9: + { + std::uint16_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype)); + break; + } + case 0xDA: + { + std::uint32_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype)); + break; + } + case 0xDB: + { + std::uint64_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype)); + break; + } + default: + return parse_cbor_internal(true, tag_handler); + } + get(); + return get_cbor_binary(b) && sax->binary(b); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } case 0xF4: // false return sax->boolean(false); @@ -6346,17 +8985,20 @@ class binary_reader case 0xF9: // Half-Precision Float (two-byte IEEE 754) { - const int byte1 = get(); - if (JSON_UNLIKELY(not unexpect_eof())) + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) { return false; } - const int byte2 = get(); - if (JSON_UNLIKELY(not unexpect_eof())) + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) { return false; } + const auto byte1 = static_cast<unsigned char>(byte1_raw); + const auto byte2 = static_cast<unsigned char>(byte2_raw); + // code from RFC 7049, Appendix D, Figure 3: // As half-precision floating-point numbers were only added // to IEEE 754 in 2008, today's programming platforms often @@ -6365,13 +9007,13 @@ class binary_reader // without such support. An example of a small decoder for // half-precision floating-point numbers in the C language // is shown in Fig. 3. - const int half = (byte1 << 8) + byte2; + const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2); const double val = [&half] { - const int exp = (half >> 10) & 0x1F; - const int mant = half & 0x3FF; - assert(0 <= exp and exp <= 32); - assert(0 <= mant and mant <= 1024); + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); switch (exp) { case 0: @@ -6384,31 +9026,322 @@ class binary_reader return std::ldexp(mant + 1024, exp - 25); } }(); - return sax->number_float((half & 0x8000) != 0 + return sax->number_float((half & 0x8000u) != 0 ? static_cast<number_float_t>(-val) : static_cast<number_float_t>(val), ""); } case 0xFA: // Single-Precision Float (four-byte IEEE 754) { - float number; - return get_number(number) and sax->number_float(static_cast<number_float_t>(number), ""); + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), ""); } case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { - double number; - return get_number(number) and sax->number_float(static_cast<number_float_t>(number), ""); + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), ""); } default: // anything else (0xFF is handled inside the other types) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + } + } + } + + /*! + @param[in] len the length of the array or static_cast<std::size_t>(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != static_cast<std::size_t>(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or static_cast<std::size_t>(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + if (len != 0) + { + string_t key; + if (len != static_cast<std::size_t>(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + /*! @return whether a valid MessagePack value was passed to the SAX parser */ @@ -6417,8 +9350,8 @@ class binary_reader switch (get()) { // EOF - case std::char_traits<char>::eof(): - return unexpect_eof(); + case std::char_traits<char_type>::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); // positive fixint case 0x00: @@ -6568,7 +9501,7 @@ class binary_reader case 0x8D: case 0x8E: case 0x8F: - return get_msgpack_object(static_cast<std::size_t>(current & 0x0F)); + return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu)); // fixarray case 0x90: @@ -6587,7 +9520,7 @@ class binary_reader case 0x9D: case 0x9E: case 0x9F: - return get_msgpack_array(static_cast<std::size_t>(current & 0x0F)); + return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu)); // fixstr case 0xA0: @@ -6622,9 +9555,12 @@ class binary_reader case 0xBD: case 0xBE: case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 { string_t s; - return get_msgpack_string(s) and sax->string(s); + return get_msgpack_string(s) && sax->string(s); } case 0xC0: // nil @@ -6636,96 +9572,104 @@ class binary_reader case 0xC3: // true return sax->boolean(true); + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + case 0xCA: // float 32 { - float number; - return get_number(number) and sax->number_float(static_cast<number_float_t>(number), ""); + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), ""); } case 0xCB: // float 64 { - double number; - return get_number(number) and sax->number_float(static_cast<number_float_t>(number), ""); + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), ""); } case 0xCC: // uint 8 { - uint8_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xCD: // uint 16 { - uint16_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xCE: // uint 32 { - uint32_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xCF: // uint 64 { - uint64_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); } case 0xD0: // int 8 { - int8_t number; - return get_number(number) and sax->number_integer(number); + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xD1: // int 16 { - int16_t number; - return get_number(number) and sax->number_integer(number); + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xD2: // int 32 { - int32_t number; - return get_number(number) and sax->number_integer(number); + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xD3: // int 64 { - int64_t number; - return get_number(number) and sax->number_integer(number); - } - - case 0xD9: // str 8 - case 0xDA: // str 16 - case 0xDB: // str 32 - { - string_t s; - return get_msgpack_string(s) and sax->string(s); + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); } case 0xDC: // array 16 { - uint16_t len; - return get_number(len) and get_msgpack_array(static_cast<std::size_t>(len)); + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len)); } case 0xDD: // array 32 { - uint32_t len; - return get_number(len) and get_msgpack_array(static_cast<std::size_t>(len)); + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len)); } case 0xDE: // map 16 { - uint16_t len; - return get_number(len) and get_msgpack_object(static_cast<std::size_t>(len)); + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len)); } case 0xDF: // map 32 { - uint32_t len; - return get_number(len) and get_msgpack_object(static_cast<std::size_t>(len)); + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len)); } // negative fixint @@ -6761,306 +9705,16 @@ class binary_reader case 0xFD: case 0xFE: case 0xFF: - return sax->number_integer(static_cast<int8_t>(current)); + return sax->number_integer(static_cast<std::int8_t>(current)); default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, "error reading MessagePack; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } - /*! - @param[in] get_char whether a new character should be retrieved from the - input (true, default) or whether the last read - character should be considered instead - - @return whether a valid UBJSON value was passed to the SAX parser - */ - bool parse_ubjson_internal(const bool get_char = true) - { - return get_ubjson_value(get_char ? get_ignore_noop() : current); - } - - /*! - @brief get next character from the input - - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns a -'ve valued - `std::char_traits<char>::eof()` in that case. - - @return character read from the input - */ - int get() - { - ++chars_read; - return (current = ia->get_character()); - } - - /*! - @return character read from the input after ignoring all 'N' entries - */ - int get_ignore_noop() - { - do - { - get(); - } - while (current == 'N'); - - return current; - } - - /* - @brief read a number from the input - - @tparam NumberType the type of the number - @param[out] result number of type @a NumberType - - @return whether conversion completed - - @note This function needs to respect the system's endianess, because - bytes in CBOR, MessagePack, and UBJSON are stored in network order - (big endian) and therefore need reordering on little endian systems. - */ - template<typename NumberType> - bool get_number(NumberType& result) - { - // step 1: read input into array with system's byte order - std::array<uint8_t, sizeof(NumberType)> vec; - for (std::size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - if (JSON_UNLIKELY(not unexpect_eof())) - { - return false; - } - - // reverse byte order prior to conversion if necessary - if (is_little_endian) - { - vec[sizeof(NumberType) - i - 1] = static_cast<uint8_t>(current); - } - else - { - vec[i] = static_cast<uint8_t>(current); // LCOV_EXCL_LINE - } - } - - // step 2: convert array into number of type T and return - std::memcpy(&result, vec.data(), sizeof(NumberType)); - return true; - } - - /*! - @brief create a string by reading characters from the input - - @tparam NumberType the type of the number - @param[in] len number of characters to read - @param[out] string created by reading @a len bytes - - @return whether string creation completed - - @note We can not reserve @a len bytes for the result, because @a len - may be too large. Usually, @ref unexpect_eof() detects the end of - the input before we run out of string memory. - */ - template<typename NumberType> - bool get_string(const NumberType len, string_t& result) - { - bool success = true; - std::generate_n(std::back_inserter(result), len, [this, &success]() - { - get(); - if (JSON_UNLIKELY(not unexpect_eof())) - { - success = false; - } - return static_cast<char>(current); - }); - return success; - } - - /*! - @brief reads a CBOR string - - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - Additionally, CBOR's strings with indefinite lengths are supported. - - @param[out] result created string - - @return whether string creation completed - */ - bool get_cbor_string(string_t& result) - { - if (JSON_UNLIKELY(not unexpect_eof())) - { - return false; - } - - switch (current) - { - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - { - return get_string(current & 0x1F, result); - } - - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - { - uint8_t len; - return get_number(len) and get_string(len, result); - } - - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - { - uint16_t len; - return get_number(len) and get_string(len, result); - } - - case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) - { - uint32_t len; - return get_number(len) and get_string(len, result); - } - - case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) - { - uint64_t len; - return get_number(len) and get_string(len, result); - } - - case 0x7F: // UTF-8 string (indefinite length) - { - while (get() != 0xFF) - { - string_t chunk; - if (not get_cbor_string(chunk)) - { - return false; - } - result.append(chunk); - } - return true; - } - - default: - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + last_token)); - } - } - } - - /*! - @param[in] len the length of the array or std::size_t(-1) for an - array of indefinite size - @return whether array creation completed - */ - bool get_cbor_array(const std::size_t len) - { - if (JSON_UNLIKELY(not sax->start_array(len))) - { - return false; - } - - if (len != std::size_t(-1)) - for (std::size_t i = 0; i < len; ++i) - { - if (JSON_UNLIKELY(not parse_cbor_internal())) - { - return false; - } - } - else - { - while (get() != 0xFF) - { - if (JSON_UNLIKELY(not parse_cbor_internal(false))) - { - return false; - } - } - } - - return sax->end_array(); - } - - /*! - @param[in] len the length of the object or std::size_t(-1) for an - object of indefinite size - @return whether object creation completed - */ - bool get_cbor_object(const std::size_t len) - { - if (not JSON_UNLIKELY(sax->start_object(len))) - { - return false; - } - - string_t key; - if (len != std::size_t(-1)) - { - for (std::size_t i = 0; i < len; ++i) - { - get(); - if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) - { - return false; - } - - if (JSON_UNLIKELY(not parse_cbor_internal())) - { - return false; - } - key.clear(); - } - } - else - { - while (get() != 0xFF) - { - if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) - { - return false; - } - - if (JSON_UNLIKELY(not parse_cbor_internal())) - { - return false; - } - key.clear(); - } - } - - return sax->end_object(); - } - /*! @brief reads a MessagePack string @@ -7073,7 +9727,7 @@ class binary_reader */ bool get_msgpack_string(string_t& result) { - if (JSON_UNLIKELY(not unexpect_eof())) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) { return false; } @@ -7114,49 +9768,166 @@ class binary_reader case 0xBE: case 0xBF: { - return get_string(current & 0x1F, result); + return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result); } case 0xD9: // str 8 { - uint8_t len; - return get_number(len) and get_string(len, result); + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); } case 0xDA: // str 16 { - uint16_t len; - return get_number(len) and get_string(len, result); + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); } case 0xDB: // str 32 { - uint32_t len; - return get_number(len) and get_string(len, result); + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); } default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, "expected a MessagePack string; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); } } } + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast<std::uint8_t>(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + /*! @param[in] len the length of the array @return whether array creation completed */ bool get_msgpack_array(const std::size_t len) { - if (JSON_UNLIKELY(not sax->start_array(len))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) { return false; } for (std::size_t i = 0; i < len; ++i) { - if (JSON_UNLIKELY(not parse_msgpack_internal())) + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) { return false; } @@ -7171,7 +9942,7 @@ class binary_reader */ bool get_msgpack_object(const std::size_t len) { - if (JSON_UNLIKELY(not sax->start_object(len))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) { return false; } @@ -7180,12 +9951,12 @@ class binary_reader for (std::size_t i = 0; i < len; ++i) { get(); - if (JSON_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_msgpack_internal())) + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) { return false; } @@ -7195,6 +9966,22 @@ class binary_reader return sax->end_object(); } + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + /*! @brief reads a UBJSON string @@ -7213,10 +10000,10 @@ class binary_reader { if (get_char) { - get(); // TODO: may we ignore N here? + get(); // TODO(niels): may we ignore N here? } - if (JSON_UNLIKELY(not unexpect_eof())) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) { return false; } @@ -7225,37 +10012,37 @@ class binary_reader { case 'U': { - uint8_t len; - return get_number(len) and get_string(len, result); + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'i': { - int8_t len; - return get_number(len) and get_string(len, result); + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'I': { - int16_t len; - return get_number(len) and get_string(len, result); + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'l': { - int32_t len; - return get_number(len) and get_string(len, result); + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } case 'L': { - int64_t len; - return get_number(len) and get_string(len, result); + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); } default: auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, "expected a UBJSON string; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); } } @@ -7269,8 +10056,8 @@ class binary_reader { case 'U': { - uint8_t number; - if (JSON_UNLIKELY(not get_number(number))) + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } @@ -7280,19 +10067,19 @@ class binary_reader case 'i': { - int8_t number; - if (JSON_UNLIKELY(not get_number(number))) + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } - result = static_cast<std::size_t>(number); + result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char return true; } case 'I': { - int16_t number; - if (JSON_UNLIKELY(not get_number(number))) + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } @@ -7302,8 +10089,8 @@ class binary_reader case 'l': { - int32_t number; - if (JSON_UNLIKELY(not get_number(number))) + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } @@ -7313,8 +10100,8 @@ class binary_reader case 'L': { - int64_t number; - if (JSON_UNLIKELY(not get_number(number))) + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) { return false; } @@ -7325,7 +10112,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, "byte after '#' must denote a number type; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); } } } @@ -7340,7 +10127,7 @@ class binary_reader @return whether pair creation completed */ - bool get_ubjson_size_type(std::pair<std::size_t, int>& result) + bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result) { result.first = string_t::npos; // size result.second = 0; // type @@ -7350,28 +10137,30 @@ class binary_reader if (current == '$') { result.second = get(); // must not ignore 'N', because 'N' maybe the type - if (JSON_UNLIKELY(not unexpect_eof())) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) { return false; } get_ignore_noop(); - if (JSON_UNLIKELY(current != '#')) + if (JSON_HEDLEY_UNLIKELY(current != '#')) { - if (JSON_UNLIKELY(not unexpect_eof())) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) { return false; } auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, "expected '#' after UBJSON type information; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); } return get_ubjson_size_value(result.first); } - else if (current == '#') + + if (current == '#') { return get_ubjson_size_value(result.first); } + return true; } @@ -7379,12 +10168,12 @@ class binary_reader @param prefix the previously read or set type prefix @return whether value creation completed */ - bool get_ubjson_value(const int prefix) + bool get_ubjson_value(const char_int_type prefix) { switch (prefix) { - case std::char_traits<char>::eof(): // EOF - return unexpect_eof(); + case std::char_traits<char_type>::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); case 'T': // true return sax->boolean(true); @@ -7396,66 +10185,71 @@ class binary_reader case 'U': { - uint8_t number; - return get_number(number) and sax->number_unsigned(number); + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); } case 'i': { - int8_t number; - return get_number(number) and sax->number_integer(number); + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'I': { - int16_t number; - return get_number(number) and sax->number_integer(number); + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'l': { - int32_t number; - return get_number(number) and sax->number_integer(number); + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'L': { - int64_t number; - return get_number(number) and sax->number_integer(number); + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); } case 'd': { - float number; - return get_number(number) and sax->number_float(static_cast<number_float_t>(number), ""); + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), ""); } case 'D': { - double number; - return get_number(number) and sax->number_float(static_cast<number_float_t>(number), ""); + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); } case 'C': // char { get(); - if (JSON_UNLIKELY(not unexpect_eof())) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) { return false; } - if (JSON_UNLIKELY(current > 127)) + if (JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); } - string_t s(1, static_cast<char>(current)); + string_t s(1, static_cast<typename string_t::value_type>(current)); return sax->string(s); } case 'S': // string { string_t s; - return get_ubjson_string(s) and sax->string(s); + return get_ubjson_string(s) && sax->string(s); } case '[': // array @@ -7467,7 +10261,7 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, "error reading UBJSON; last byte: 0x" + last_token)); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); } } } @@ -7477,15 +10271,15 @@ class binary_reader */ bool get_ubjson_array() { - std::pair<std::size_t, int> size_and_type; - if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type))) + std::pair<std::size_t, char_int_type> size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) { return false; } if (size_and_type.first != string_t::npos) { - if (JSON_UNLIKELY(not sax->start_array(size_and_type.first))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) { return false; } @@ -7496,7 +10290,7 @@ class binary_reader { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second))) + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) { return false; } @@ -7507,7 +10301,7 @@ class binary_reader { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not parse_ubjson_internal())) + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) { return false; } @@ -7516,14 +10310,14 @@ class binary_reader } else { - if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1)))) { return false; } while (current != ']') { - if (JSON_UNLIKELY(not parse_ubjson_internal(false))) + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) { return false; } @@ -7539,8 +10333,8 @@ class binary_reader */ bool get_ubjson_object() { - std::pair<std::size_t, int> size_and_type; - if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type))) + std::pair<std::size_t, char_int_type> size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) { return false; } @@ -7548,7 +10342,7 @@ class binary_reader string_t key; if (size_and_type.first != string_t::npos) { - if (JSON_UNLIKELY(not sax->start_object(size_and_type.first))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) { return false; } @@ -7557,11 +10351,11 @@ class binary_reader { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) { return false; } - if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second))) + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) { return false; } @@ -7572,11 +10366,11 @@ class binary_reader { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_ubjson_internal())) + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) { return false; } @@ -7586,18 +10380,18 @@ class binary_reader } else { - if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1)))) + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1)))) { return false; } while (current != '}') { - if (JSON_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key))) + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_ubjson_internal())) + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) { return false; } @@ -7609,14 +10403,225 @@ class binary_reader return sax->end_object(); } + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector<char> number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast<char>(current)); + } + + // parse number string + using ia_type = decltype(detail::input_adapter(number_vector)); + auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base<BasicJsonType>::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + case token_type::uninitialized: + case token_type::literal_true: + case token_type::literal_false: + case token_type::literal_null: + case token_type::value_string: + case token_type::begin_array: + case token_type::begin_object: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::parse_error: + case token_type::end_of_input: + case token_type::literal_or_value: + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits<char_type>::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianness, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template<typename NumberType, bool InputIsLittleEndian = false> + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array<std::uint8_t, sizeof(NumberType)> vec{}; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current); + } + else + { + vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template<typename NumberType> + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast<typename string_t::value_type>(current)); + } + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template<typename NumberType> + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast<std::uint8_t>(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) @return whether the last read character is not EOF */ - bool unexpect_eof() const + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const { - if (JSON_UNLIKELY(current == std::char_traits<char>::eof())) + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof())) { - return sax->parse_error(chars_read, "<end of file>", parse_error::create(110, chars_read, "unexpected end of input")); + return sax->parse_error(chars_read, "<end of file>", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); } return true; } @@ -7626,44 +10631,2674 @@ class binary_reader */ std::string get_token_string() const { - char cr[3]; - snprintf(cr, 3, "%.2hhX", static_cast<unsigned char>(current)); - return std::string{cr}; + std::array<char, 3> cr{{}}; + static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; } private: /// input adapter - input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character - int current = std::char_traits<char>::eof(); + char_int_type current = std::char_traits<char_type>::eof(); /// the number of characters read std::size_t chars_read = 0; - /// whether we can assume little endianess - const bool is_little_endian = little_endianess(); + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); /// the SAX parser json_sax_t* sax = nullptr; }; -} -} +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/input/input_adapters.hpp> + +// #include <nlohmann/detail/input/lexer.hpp> + +// #include <nlohmann/detail/input/parser.hpp> + + +#include <cmath> // isfinite +#include <cstdint> // uint8_t +#include <functional> // function +#include <string> // string +#include <utility> // move +#include <vector> // vector + +// #include <nlohmann/detail/exceptions.hpp> + +// #include <nlohmann/detail/input/input_adapters.hpp> + +// #include <nlohmann/detail/input/json_sax.hpp> + +// #include <nlohmann/detail/input/lexer.hpp> + +// #include <nlohmann/detail/macro_scope.hpp> + +// #include <nlohmann/detail/meta/is_sax.hpp> + +// #include <nlohmann/detail/value_t.hpp> + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : std::uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template<typename BasicJsonType> +using parser_callback_t = + std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template<typename BasicJsonType, typename InputAdapterType> +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer<BasicJsonType, InputAdapterType>; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t<BasicJsonType> cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + + result.assert_invariant(); + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor<BasicJsonType> sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template<typename SAX> + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + return result; + } + + private: + template<typename SAX> + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector<bool> states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + } + + case token_type::uninitialized: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::end_of_input: + case token_type::literal_or_value: + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + } + + // states.back() is false -> object + + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t<BasicJsonType> callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/iterators/internal_iterator.hpp> + + +// #include <nlohmann/detail/iterators/primitive_iterator.hpp> + + +#include <cstddef> // ptrdiff_t +#include <limits> // numeric_limits + +// #include <nlohmann/detail/macro_scope.hpp> + + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + JSON_PRIVATE_UNLESS_TESTED: + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template<typename BasicJsonType> struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/iterators/iter_impl.hpp> + + +#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include <type_traits> // conditional, is_const, remove_const + +// #include <nlohmann/detail/exceptions.hpp> + +// #include <nlohmann/detail/iterators/internal_iterator.hpp> + +// #include <nlohmann/detail/iterators/primitive_iterator.hpp> + +// #include <nlohmann/detail/macro_scope.hpp> + +// #include <nlohmann/detail/meta/cpp_future.hpp> + +// #include <nlohmann/detail/meta/type_traits.hpp> + +// #include <nlohmann/detail/value_t.hpp> + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template<typename IteratorType> class iteration_proxy; +template<typename IteratorType> class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template<typename BasicJsonType> +class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>; + /// allow basic_json to access private members + friend other_iter_impl; + friend BasicJsonType; + friend iteration_proxy<iter_impl>; + friend iteration_proxy_value<iter_impl>; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional<std::is_const<BasicJsonType>::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional<std::is_const<BasicJsonType>::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + ~iter_impl() = default; + iter_impl(iter_impl&&) noexcept = default; + iter_impl& operator=(iter_impl&&) noexcept = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl<const BasicJsonType>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept + { + if (&other != this) + { + m_object = other.m_object; + m_it = other.m_it; + } + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp) + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + JSON_PRIVATE_UNLESS_TESTED: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/iterators/iteration_proxy.hpp> + +// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp> + + +#include <cstddef> // ptrdiff_t +#include <iterator> // reverse_iterator +#include <utility> // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template<typename Base> +class json_reverse_iterator : public std::reverse_iterator<Base> +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator<Base>; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + { + return static_cast<json_reverse_iterator>(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast<json_reverse_iterator&>(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + { + return static_cast<json_reverse_iterator>(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast<json_reverse_iterator&>(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast<json_reverse_iterator>(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast<json_reverse_iterator>(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval<Base>().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/iterators/primitive_iterator.hpp> + +// #include <nlohmann/detail/json_pointer.hpp> + + +#include <algorithm> // all_of +#include <cctype> // isdigit +#include <limits> // max +#include <numeric> // accumulate +#include <string> // string +#include <utility> // move +#include <vector> // vector + +// #include <nlohmann/detail/exceptions.hpp> + +// #include <nlohmann/detail/macro_scope.hpp> + +// #include <nlohmann/detail/string_escape.hpp> + +// #include <nlohmann/detail/value_t.hpp> + + +namespace nlohmann +{ + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template<typename BasicJsonType> +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /// @brief create JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }); + } + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ + operator std::string() const + { + return to_string(); + } + + /// @brief append another JSON pointer at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /// @brief append an unescaped reference token at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /// @brief append an array index at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param) + { + return json_pointer(lhs) /= std::move(token); + } + + /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) + { + return json_pointer(lhs) /= array_idx; + } + + /// @brief returns the parent of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /// @brief remove last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + reference_tokens.pop_back(); + } + + /// @brief return last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/back/ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + return reference_tokens.back(); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /// @brief return whether pointer points to the root document + /// @sa https://json.nlohmann.me/api/json_pointer/empty/ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + } + + std::size_t processed_chars = 0; + unsigned long long res = 0; // NOLINT(runtime/int) + JSON_TRY + { + res = std::stoull(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + } + + return static_cast<size_type>(res); + } + + JSON_PRIVATE_UNLESS_TESTED: + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + private: + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto* result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector<std::string> split(const std::string& reference_string) + { + std::vector<std::string> result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + } + } + + // finally, store the reference token + detail::unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + } + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector<std::string> reference_tokens; +}; +} // namespace nlohmann + +// #include <nlohmann/detail/json_ref.hpp> + + +#include <initializer_list> +#include <utility> + +// #include <nlohmann/detail/meta/type_traits.hpp> + + +namespace nlohmann +{ +namespace detail +{ +template<typename BasicJsonType> +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + {} + + json_ref(const value_type& value) + : value_ref(&value) + {} + + json_ref(std::initializer_list<json_ref> init) + : owned_value(init) + {} + + template < + class... Args, + enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward<Args>(args)...) + {} + + // class should be movable only + json_ref(json_ref&&) noexcept = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (value_ref == nullptr) + { + return std::move(owned_value); + } + return *value_ref; + } + + value_type const& operator*() const + { + return value_ref ? *value_ref : owned_value; + } + + value_type const* operator->() const + { + return &** this; + } + + private: + mutable value_type owned_value = nullptr; + value_type const* value_ref = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/macro_scope.hpp> + +// #include <nlohmann/detail/string_escape.hpp> + +// #include <nlohmann/detail/meta/cpp_future.hpp> + +// #include <nlohmann/detail/meta/type_traits.hpp> // #include <nlohmann/detail/output/binary_writer.hpp> #include <algorithm> // reverse #include <array> // array +#include <cmath> // isnan, isinf #include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t #include <cstring> // memcpy #include <limits> // numeric_limits +#include <string> // string +#include <utility> // move // #include <nlohmann/detail/input/binary_reader.hpp> +// #include <nlohmann/detail/macro_scope.hpp> + // #include <nlohmann/detail/output/output_adapters.hpp> +#include <algorithm> // copy +#include <cstddef> // size_t +#include <iterator> // back_inserter +#include <memory> // shared_ptr, make_shared +#include <string> // basic_string +#include <vector> // vector + +#ifndef JSON_NO_IO + #include <ios> // streamsize + #include <ostream> // basic_ostream +#endif // JSON_NO_IO + +// #include <nlohmann/detail/macro_scope.hpp> + + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template<typename CharType> struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; + + output_adapter_protocol() = default; + output_adapter_protocol(const output_adapter_protocol&) = default; + output_adapter_protocol(output_adapter_protocol&&) noexcept = default; + output_adapter_protocol& operator=(const output_adapter_protocol&) = default; + output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; +}; + +/// a type to simplify interfaces +template<typename CharType> +using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>; + +/// output adapter for byte vectors +template<typename CharType, typename AllocatorType = std::allocator<CharType>> +class output_vector_adapter : public output_adapter_protocol<CharType> +{ + public: + explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector<CharType, AllocatorType>& v; +}; + +#ifndef JSON_NO_IO +/// output adapter for output streams +template<typename CharType> +class output_stream_adapter : public output_adapter_protocol<CharType> +{ + public: + explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast<std::streamsize>(length)); + } + + private: + std::basic_ostream<CharType>& stream; +}; +#endif // JSON_NO_IO + +/// output adapter for basic_string +template<typename CharType, typename StringType = std::basic_string<CharType>> +class output_string_adapter : public output_adapter_protocol<CharType> +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template<typename CharType, typename StringType = std::basic_string<CharType>> +class output_adapter +{ + public: + template<typename AllocatorType = std::allocator<CharType>> + output_adapter(std::vector<CharType, AllocatorType>& vec) + : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {} + +#ifndef JSON_NO_IO + output_adapter(std::basic_ostream<CharType>& s) + : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {} +#endif // JSON_NO_IO + + output_adapter(StringType& s) + : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {} + + operator output_adapter_t<CharType>() + { + return oa; + } + + private: + output_adapter_t<CharType> oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + + namespace nlohmann { namespace detail @@ -7678,19 +13313,53 @@ namespace detail template<typename BasicJsonType, typename CharType> class binary_writer { + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + public: /*! @brief create a binary writer @param[in] adapter output adapter to write to */ - explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter) + explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter)) { - assert(oa); + JSON_ASSERT(oa); } /*! - @brief[in] j JSON value to serialize + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } + + case value_t::null: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + } + } + } + + /*! + @param[in] j JSON value to serialize */ void write_cbor(const BasicJsonType& j) { @@ -7698,15 +13367,15 @@ class binary_writer { case value_t::null: { - oa->write_character(static_cast<CharType>(0xF6)); + oa->write_character(to_char_type(0xF6)); break; } case value_t::boolean: { oa->write_character(j.m_value.boolean - ? static_cast<CharType>(0xF5) - : static_cast<CharType>(0xF4)); + ? to_char_type(0xF5) + : to_char_type(0xF4)); break; } @@ -7719,27 +13388,27 @@ class binary_writer // code from the value_t::number_unsigned case here. if (j.m_value.number_integer <= 0x17) { - write_number(static_cast<uint8_t>(j.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)()) + else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) { - oa->write_character(static_cast<CharType>(0x18)); - write_number(static_cast<uint8_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0x18)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)()) + else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()) { - oa->write_character(static_cast<CharType>(0x19)); - write_number(static_cast<uint16_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0x19)); + write_number(static_cast<std::uint16_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)()) + else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()) { - oa->write_character(static_cast<CharType>(0x1A)); - write_number(static_cast<uint32_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0x1A)); + write_number(static_cast<std::uint32_t>(j.m_value.number_integer)); } else { - oa->write_character(static_cast<CharType>(0x1B)); - write_number(static_cast<uint64_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0x1B)); + write_number(static_cast<std::uint64_t>(j.m_value.number_integer)); } } else @@ -7749,27 +13418,27 @@ class binary_writer const auto positive_number = -1 - j.m_value.number_integer; if (j.m_value.number_integer >= -24) { - write_number(static_cast<uint8_t>(0x20 + positive_number)); + write_number(static_cast<std::uint8_t>(0x20 + positive_number)); } - else if (positive_number <= (std::numeric_limits<uint8_t>::max)()) + else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)()) { - oa->write_character(static_cast<CharType>(0x38)); - write_number(static_cast<uint8_t>(positive_number)); + oa->write_character(to_char_type(0x38)); + write_number(static_cast<std::uint8_t>(positive_number)); } - else if (positive_number <= (std::numeric_limits<uint16_t>::max)()) + else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)()) { - oa->write_character(static_cast<CharType>(0x39)); - write_number(static_cast<uint16_t>(positive_number)); + oa->write_character(to_char_type(0x39)); + write_number(static_cast<std::uint16_t>(positive_number)); } - else if (positive_number <= (std::numeric_limits<uint32_t>::max)()) + else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)()) { - oa->write_character(static_cast<CharType>(0x3A)); - write_number(static_cast<uint32_t>(positive_number)); + oa->write_character(to_char_type(0x3A)); + write_number(static_cast<std::uint32_t>(positive_number)); } else { - oa->write_character(static_cast<CharType>(0x3B)); - write_number(static_cast<uint64_t>(positive_number)); + oa->write_character(to_char_type(0x3B)); + write_number(static_cast<std::uint64_t>(positive_number)); } } break; @@ -7779,35 +13448,51 @@ class binary_writer { if (j.m_value.number_unsigned <= 0x17) { - write_number(static_cast<uint8_t>(j.m_value.number_unsigned)); + write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) { - oa->write_character(static_cast<CharType>(0x18)); - write_number(static_cast<uint8_t>(j.m_value.number_unsigned)); + oa->write_character(to_char_type(0x18)); + write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) { - oa->write_character(static_cast<CharType>(0x19)); - write_number(static_cast<uint16_t>(j.m_value.number_unsigned)); + oa->write_character(to_char_type(0x19)); + write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) { - oa->write_character(static_cast<CharType>(0x1A)); - write_number(static_cast<uint32_t>(j.m_value.number_unsigned)); + oa->write_character(to_char_type(0x1A)); + write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned)); } else { - oa->write_character(static_cast<CharType>(0x1B)); - write_number(static_cast<uint64_t>(j.m_value.number_unsigned)); + oa->write_character(to_char_type(0x1B)); + write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned)); } break; } case value_t::number_float: { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); + } break; } @@ -7817,28 +13502,28 @@ class binary_writer const auto N = j.m_value.string->size(); if (N <= 0x17) { - write_number(static_cast<uint8_t>(0x60 + N)); + write_number(static_cast<std::uint8_t>(0x60 + N)); } - else if (N <= (std::numeric_limits<uint8_t>::max)()) + else if (N <= (std::numeric_limits<std::uint8_t>::max)()) { - oa->write_character(static_cast<CharType>(0x78)); - write_number(static_cast<uint8_t>(N)); + oa->write_character(to_char_type(0x78)); + write_number(static_cast<std::uint8_t>(N)); } - else if (N <= (std::numeric_limits<uint16_t>::max)()) + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { - oa->write_character(static_cast<CharType>(0x79)); - write_number(static_cast<uint16_t>(N)); + oa->write_character(to_char_type(0x79)); + write_number(static_cast<std::uint16_t>(N)); } - else if (N <= (std::numeric_limits<uint32_t>::max)()) + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { - oa->write_character(static_cast<CharType>(0x7A)); - write_number(static_cast<uint32_t>(N)); + oa->write_character(to_char_type(0x7A)); + write_number(static_cast<std::uint32_t>(N)); } // LCOV_EXCL_START - else if (N <= (std::numeric_limits<uint64_t>::max)()) + else if (N <= (std::numeric_limits<std::uint64_t>::max)()) { - oa->write_character(static_cast<CharType>(0x7B)); - write_number(static_cast<uint64_t>(N)); + oa->write_character(to_char_type(0x7B)); + write_number(static_cast<std::uint64_t>(N)); } // LCOV_EXCL_STOP @@ -7855,28 +13540,28 @@ class binary_writer const auto N = j.m_value.array->size(); if (N <= 0x17) { - write_number(static_cast<uint8_t>(0x80 + N)); + write_number(static_cast<std::uint8_t>(0x80 + N)); } - else if (N <= (std::numeric_limits<uint8_t>::max)()) + else if (N <= (std::numeric_limits<std::uint8_t>::max)()) { - oa->write_character(static_cast<CharType>(0x98)); - write_number(static_cast<uint8_t>(N)); + oa->write_character(to_char_type(0x98)); + write_number(static_cast<std::uint8_t>(N)); } - else if (N <= (std::numeric_limits<uint16_t>::max)()) + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { - oa->write_character(static_cast<CharType>(0x99)); - write_number(static_cast<uint16_t>(N)); + oa->write_character(to_char_type(0x99)); + write_number(static_cast<std::uint16_t>(N)); } - else if (N <= (std::numeric_limits<uint32_t>::max)()) + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { - oa->write_character(static_cast<CharType>(0x9A)); - write_number(static_cast<uint32_t>(N)); + oa->write_character(to_char_type(0x9A)); + write_number(static_cast<std::uint32_t>(N)); } // LCOV_EXCL_START - else if (N <= (std::numeric_limits<uint64_t>::max)()) + else if (N <= (std::numeric_limits<std::uint64_t>::max)()) { - oa->write_character(static_cast<CharType>(0x9B)); - write_number(static_cast<uint64_t>(N)); + oa->write_character(to_char_type(0x9B)); + write_number(static_cast<std::uint64_t>(N)); } // LCOV_EXCL_STOP @@ -7888,34 +13573,97 @@ class binary_writer break; } + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) + { + if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)()) + { + write_number(static_cast<std::uint8_t>(0xd8)); + write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)()) + { + write_number(static_cast<std::uint8_t>(0xd9)); + write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)()) + { + write_number(static_cast<std::uint8_t>(0xda)); + write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)()) + { + write_number(static_cast<std::uint8_t>(0xdb)); + write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype())); + } + } + + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast<std::uint8_t>(0x40 + N)); + } + else if (N <= (std::numeric_limits<std::uint8_t>::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast<std::uint8_t>(N)); + } + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast<std::uint16_t>(N)); + } + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast<std::uint32_t>(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits<std::uint64_t>::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast<std::uint64_t>(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast<const CharType*>(j.m_value.binary->data()), + N); + + break; + } + case value_t::object: { // step 1: write control byte and the object size const auto N = j.m_value.object->size(); if (N <= 0x17) { - write_number(static_cast<uint8_t>(0xA0 + N)); + write_number(static_cast<std::uint8_t>(0xA0 + N)); } - else if (N <= (std::numeric_limits<uint8_t>::max)()) + else if (N <= (std::numeric_limits<std::uint8_t>::max)()) { - oa->write_character(static_cast<CharType>(0xB8)); - write_number(static_cast<uint8_t>(N)); + oa->write_character(to_char_type(0xB8)); + write_number(static_cast<std::uint8_t>(N)); } - else if (N <= (std::numeric_limits<uint16_t>::max)()) + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { - oa->write_character(static_cast<CharType>(0xB9)); - write_number(static_cast<uint16_t>(N)); + oa->write_character(to_char_type(0xB9)); + write_number(static_cast<std::uint16_t>(N)); } - else if (N <= (std::numeric_limits<uint32_t>::max)()) + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { - oa->write_character(static_cast<CharType>(0xBA)); - write_number(static_cast<uint32_t>(N)); + oa->write_character(to_char_type(0xBA)); + write_number(static_cast<std::uint32_t>(N)); } // LCOV_EXCL_START - else if (N <= (std::numeric_limits<uint64_t>::max)()) + else if (N <= (std::numeric_limits<std::uint64_t>::max)()) { - oa->write_character(static_cast<CharType>(0xBB)); - write_number(static_cast<uint64_t>(N)); + oa->write_character(to_char_type(0xBB)); + write_number(static_cast<std::uint64_t>(N)); } // LCOV_EXCL_STOP @@ -7928,13 +13676,14 @@ class binary_writer break; } + case value_t::discarded: default: break; } } /*! - @brief[in] j JSON value to serialize + @param[in] j JSON value to serialize */ void write_msgpack(const BasicJsonType& j) { @@ -7942,15 +13691,15 @@ class binary_writer { case value_t::null: // nil { - oa->write_character(static_cast<CharType>(0xC0)); + oa->write_character(to_char_type(0xC0)); break; } case value_t::boolean: // true and false { oa->write_character(j.m_value.boolean - ? static_cast<CharType>(0xC3) - : static_cast<CharType>(0xC2)); + ? to_char_type(0xC3) + : to_char_type(0xC2)); break; } @@ -7964,31 +13713,31 @@ class binary_writer if (j.m_value.number_unsigned < 128) { // positive fixnum - write_number(static_cast<uint8_t>(j.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) { // uint 8 - oa->write_character(static_cast<CharType>(0xCC)); - write_number(static_cast<uint8_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCC)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) { // uint 16 - oa->write_character(static_cast<CharType>(0xCD)); - write_number(static_cast<uint16_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCD)); + write_number(static_cast<std::uint16_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) { // uint 32 - oa->write_character(static_cast<CharType>(0xCE)); - write_number(static_cast<uint32_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCE)); + write_number(static_cast<std::uint32_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) { // uint 64 - oa->write_character(static_cast<CharType>(0xCF)); - write_number(static_cast<uint64_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCF)); + write_number(static_cast<std::uint64_t>(j.m_value.number_integer)); } } else @@ -7996,35 +13745,35 @@ class binary_writer if (j.m_value.number_integer >= -32) { // negative fixnum - write_number(static_cast<int8_t>(j.m_value.number_integer)); + write_number(static_cast<std::int8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and - j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) { // int 8 - oa->write_character(static_cast<CharType>(0xD0)); - write_number(static_cast<int8_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xD0)); + write_number(static_cast<std::int8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and - j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) { // int 16 - oa->write_character(static_cast<CharType>(0xD1)); - write_number(static_cast<int16_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xD1)); + write_number(static_cast<std::int16_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and - j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) { // int 32 - oa->write_character(static_cast<CharType>(0xD2)); - write_number(static_cast<int32_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xD2)); + write_number(static_cast<std::int32_t>(j.m_value.number_integer)); } - else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and - j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) { // int 64 - oa->write_character(static_cast<CharType>(0xD3)); - write_number(static_cast<int64_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xD3)); + write_number(static_cast<std::int64_t>(j.m_value.number_integer)); } } break; @@ -8035,39 +13784,38 @@ class binary_writer if (j.m_value.number_unsigned < 128) { // positive fixnum - write_number(static_cast<uint8_t>(j.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) { // uint 8 - oa->write_character(static_cast<CharType>(0xCC)); - write_number(static_cast<uint8_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCC)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) { // uint 16 - oa->write_character(static_cast<CharType>(0xCD)); - write_number(static_cast<uint16_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCD)); + write_number(static_cast<std::uint16_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) { // uint 32 - oa->write_character(static_cast<CharType>(0xCE)); - write_number(static_cast<uint32_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCE)); + write_number(static_cast<std::uint32_t>(j.m_value.number_integer)); } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) { // uint 64 - oa->write_character(static_cast<CharType>(0xCF)); - write_number(static_cast<uint64_t>(j.m_value.number_integer)); + oa->write_character(to_char_type(0xCF)); + write_number(static_cast<std::uint64_t>(j.m_value.number_integer)); } break; } case value_t::number_float: { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -8078,25 +13826,25 @@ class binary_writer if (N <= 31) { // fixstr - write_number(static_cast<uint8_t>(0xA0 | N)); + write_number(static_cast<std::uint8_t>(0xA0 | N)); } - else if (N <= (std::numeric_limits<uint8_t>::max)()) + else if (N <= (std::numeric_limits<std::uint8_t>::max)()) { // str 8 - oa->write_character(static_cast<CharType>(0xD9)); - write_number(static_cast<uint8_t>(N)); + oa->write_character(to_char_type(0xD9)); + write_number(static_cast<std::uint8_t>(N)); } - else if (N <= (std::numeric_limits<uint16_t>::max)()) + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { // str 16 - oa->write_character(static_cast<CharType>(0xDA)); - write_number(static_cast<uint16_t>(N)); + oa->write_character(to_char_type(0xDA)); + write_number(static_cast<std::uint16_t>(N)); } - else if (N <= (std::numeric_limits<uint32_t>::max)()) + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { // str 32 - oa->write_character(static_cast<CharType>(0xDB)); - write_number(static_cast<uint32_t>(N)); + oa->write_character(to_char_type(0xDB)); + write_number(static_cast<std::uint32_t>(N)); } // step 2: write the string @@ -8113,19 +13861,19 @@ class binary_writer if (N <= 15) { // fixarray - write_number(static_cast<uint8_t>(0x90 | N)); + write_number(static_cast<std::uint8_t>(0x90 | N)); } - else if (N <= (std::numeric_limits<uint16_t>::max)()) + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { // array 16 - oa->write_character(static_cast<CharType>(0xDC)); - write_number(static_cast<uint16_t>(N)); + oa->write_character(to_char_type(0xDC)); + write_number(static_cast<std::uint16_t>(N)); } - else if (N <= (std::numeric_limits<uint32_t>::max)()) + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { // array 32 - oa->write_character(static_cast<CharType>(0xDD)); - write_number(static_cast<uint32_t>(N)); + oa->write_character(to_char_type(0xDD)); + write_number(static_cast<std::uint32_t>(N)); } // step 2: write each element @@ -8136,6 +13884,89 @@ class binary_writer break; } + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits<std::uint8_t>::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast<std::uint8_t>(N)); + } + } + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast<std::uint16_t>(N)); + } + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast<std::uint32_t>(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast<std::int8_t>(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast<const CharType*>(j.m_value.binary->data()), + N); + + break; + } + case value_t::object: { // step 1: write control byte and the object size @@ -8143,19 +13974,19 @@ class binary_writer if (N <= 15) { // fixmap - write_number(static_cast<uint8_t>(0x80 | (N & 0xF))); + write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF))); } - else if (N <= (std::numeric_limits<uint16_t>::max)()) + else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { // map 16 - oa->write_character(static_cast<CharType>(0xDE)); - write_number(static_cast<uint16_t>(N)); + oa->write_character(to_char_type(0xDE)); + write_number(static_cast<std::uint16_t>(N)); } - else if (N <= (std::numeric_limits<uint32_t>::max)()) + else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { // map 32 - oa->write_character(static_cast<CharType>(0xDF)); - write_number(static_cast<uint32_t>(N)); + oa->write_character(to_char_type(0xDF)); + write_number(static_cast<std::uint32_t>(N)); } // step 2: write each element @@ -8167,6 +13998,7 @@ class binary_writer break; } + case value_t::discarded: default: break; } @@ -8187,7 +14019,7 @@ class binary_writer { if (add_prefix) { - oa->write_character(static_cast<CharType>('Z')); + oa->write_character(to_char_type('Z')); } break; } @@ -8195,9 +14027,11 @@ class binary_writer case value_t::boolean: { if (add_prefix) + { oa->write_character(j.m_value.boolean - ? static_cast<CharType>('T') - : static_cast<CharType>('F')); + ? to_char_type('T') + : to_char_type('F')); + } break; } @@ -8223,7 +14057,7 @@ class binary_writer { if (add_prefix) { - oa->write_character(static_cast<CharType>('S')); + oa->write_character(to_char_type('S')); } write_number_with_ubjson_prefix(j.m_value.string->size(), true); oa->write_characters( @@ -8236,13 +14070,13 @@ class binary_writer { if (add_prefix) { - oa->write_character(static_cast<CharType>('[')); + oa->write_character(to_char_type('[')); } bool prefix_required = true; - if (use_type and not j.m_value.array->empty()) + if (use_type && !j.m_value.array->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) @@ -8253,14 +14087,14 @@ class binary_writer if (same_prefix) { prefix_required = false; - oa->write_character(static_cast<CharType>('$')); + oa->write_character(to_char_type('$')); oa->write_character(first_prefix); } } if (use_count) { - oa->write_character(static_cast<CharType>('#')); + oa->write_character(to_char_type('#')); write_number_with_ubjson_prefix(j.m_value.array->size(), true); } @@ -8269,9 +14103,52 @@ class binary_writer write_ubjson(el, use_count, use_type, prefix_required); } - if (not use_count) + if (!use_count) { - oa->write_character(static_cast<CharType>(']')); + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && !j.m_value.binary->empty()) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast<const CharType*>(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); } break; @@ -8281,13 +14158,13 @@ class binary_writer { if (add_prefix) { - oa->write_character(static_cast<CharType>('{')); + oa->write_character(to_char_type('{')); } bool prefix_required = true; - if (use_type and not j.m_value.object->empty()) + if (use_type && !j.m_value.object->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) @@ -8298,14 +14175,14 @@ class binary_writer if (same_prefix) { prefix_required = false; - oa->write_character(static_cast<CharType>('$')); + oa->write_character(to_char_type('$')); oa->write_character(first_prefix); } } if (use_count) { - oa->write_character(static_cast<CharType>('#')); + oa->write_character(to_char_type('#')); write_number_with_ubjson_prefix(j.m_value.object->size(), true); } @@ -8318,47 +14195,387 @@ class binary_writer write_ubjson(el.second, use_count, use_type, prefix_required); } - if (not use_count) + if (!use_count) { - oa->write_character(static_cast<CharType>('}')); + oa->write_character(to_char_type('}')); } break; } + case value_t::discarded: default: break; } } private: - /* - @brief write a number to output input + ////////// + // BSON // + ////////// - @param[in] n number of type @a NumberType - @tparam NumberType the type of the number - - @note This function needs to respect the system's endianess, because bytes - in CBOR, MessagePack, and UBJSON are stored in network order (big - endian) and therefore need reordering on little endian systems. + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). */ - template<typename NumberType> - void write_number(const NumberType n) + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) { - // step 1: write number to array of length NumberType - std::array<CharType, sizeof(NumberType)> vec; - std::memcpy(vec.data(), &n, sizeof(NumberType)); - - // step 2: write array to output (with possible reordering) - if (is_little_endian) + const auto it = name.find(static_cast<typename string_t::value_type>(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - // reverse byte order prior to conversion if necessary - std::reverse(vec.begin(), vec.end()); + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + static_cast<void>(j); } - oa->write_characters(vec.data(), sizeof(NumberType)); + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; } + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast<const CharType*>(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number<double, true>(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast<const CharType*>(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number<std::int32_t, true>(static_cast<std::int32_t>(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number<std::int64_t, true>(static_cast<std::int64_t>(value)); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const BasicJsonType& j) + { + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_unsigned)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value))); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size())); + write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00)); + + oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name, j); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + // UBJSON: write number (floating point) template<typename NumberType, typename std::enable_if< std::is_floating_point<NumberType>::value, int>::type = 0> @@ -8378,115 +14595,129 @@ class binary_writer void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { - if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)())) + if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)())) { if (add_prefix) { - oa->write_character(static_cast<CharType>('i')); // int8 + oa->write_character(to_char_type('i')); // int8 } - write_number(static_cast<uint8_t>(n)); + write_number(static_cast<std::uint8_t>(n)); } - else if (n <= (std::numeric_limits<uint8_t>::max)()) + else if (n <= (std::numeric_limits<std::uint8_t>::max)()) { if (add_prefix) { - oa->write_character(static_cast<CharType>('U')); // uint8 + oa->write_character(to_char_type('U')); // uint8 } - write_number(static_cast<uint8_t>(n)); + write_number(static_cast<std::uint8_t>(n)); } - else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)())) + else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)())) { if (add_prefix) { - oa->write_character(static_cast<CharType>('I')); // int16 + oa->write_character(to_char_type('I')); // int16 } - write_number(static_cast<int16_t>(n)); + write_number(static_cast<std::int16_t>(n)); } - else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)())) + else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) { if (add_prefix) { - oa->write_character(static_cast<CharType>('l')); // int32 + oa->write_character(to_char_type('l')); // int32 } - write_number(static_cast<int32_t>(n)); + write_number(static_cast<std::int32_t>(n)); } - else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)())) + else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) { if (add_prefix) { - oa->write_character(static_cast<CharType>('L')); // int64 + oa->write_character(to_char_type('L')); // int64 } - write_number(static_cast<int64_t>(n)); + write_number(static_cast<std::int64_t>(n)); } else { - JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i]))); + } } } // UBJSON: write number (signed integer) - template<typename NumberType, typename std::enable_if< - std::is_signed<NumberType>::value and - not std::is_floating_point<NumberType>::value, int>::type = 0> + template < typename NumberType, typename std::enable_if < + std::is_signed<NumberType>::value&& + !std::is_floating_point<NumberType>::value, int >::type = 0 > void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { - if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)()) + if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)()) { if (add_prefix) { - oa->write_character(static_cast<CharType>('i')); // int8 + oa->write_character(to_char_type('i')); // int8 } - write_number(static_cast<int8_t>(n)); + write_number(static_cast<std::int8_t>(n)); } - else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)())) + else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)())) { if (add_prefix) { - oa->write_character(static_cast<CharType>('U')); // uint8 + oa->write_character(to_char_type('U')); // uint8 } - write_number(static_cast<uint8_t>(n)); + write_number(static_cast<std::uint8_t>(n)); } - else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)()) + else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)()) { if (add_prefix) { - oa->write_character(static_cast<CharType>('I')); // int16 + oa->write_character(to_char_type('I')); // int16 } - write_number(static_cast<int16_t>(n)); + write_number(static_cast<std::int16_t>(n)); } - else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)()) + else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)()) { if (add_prefix) { - oa->write_character(static_cast<CharType>('l')); // int32 + oa->write_character(to_char_type('l')); // int32 } - write_number(static_cast<int32_t>(n)); + write_number(static_cast<std::int32_t>(n)); } - else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)()) + else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)()) { if (add_prefix) { - oa->write_character(static_cast<CharType>('L')); // int64 + oa->write_character(to_char_type('L')); // int64 } - write_number(static_cast<int64_t>(n)); + write_number(static_cast<std::int64_t>(n)); } // LCOV_EXCL_START else { - JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i]))); + } } // LCOV_EXCL_STOP } /*! @brief determine the type prefix of container values - - @note This function does not need to be 100% accurate when it comes to - integer limits. In case a number exceeds the limits of int64_t, - this will be detected by a later call to function - write_number_with_ubjson_prefix. Therefore, we return 'L' for any - value that does not fit the previous limits. */ CharType ubjson_prefix(const BasicJsonType& j) const noexcept { @@ -8500,50 +14731,54 @@ class binary_writer case value_t::number_integer: { - if ((std::numeric_limits<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)()) + if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) { return 'i'; } - else if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)()) + if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) { return 'U'; } - else if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)()) + if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) { return 'I'; } - else if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)()) + if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) { return 'l'; } - else // no check and assume int64_t (see note above) + if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) { return 'L'; } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE } case value_t::number_unsigned: { - if (j.m_value.number_unsigned <= (std::numeric_limits<int8_t>::max)()) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)())) { return 'i'; } - else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)()) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)())) { return 'U'; } - else if (j.m_value.number_unsigned <= (std::numeric_limits<int16_t>::max)()) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)())) { return 'I'; } - else if (j.m_value.number_unsigned <= (std::numeric_limits<int32_t>::max)()) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) { return 'l'; } - else // no check and assume int64_t (see note above) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) { return 'L'; } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE } case value_t::number_float: @@ -8552,83 +14787,170 @@ class binary_writer case value_t::string: return 'S'; - case value_t::array: + case value_t::array: // fallthrough + case value_t::binary: return '['; case value_t::object: return '{'; + case value_t::discarded: default: // discarded values return 'N'; } } - static constexpr CharType get_cbor_float_prefix(float) - { - return static_cast<CharType>(0xFA); // Single-Precision Float - } - - static constexpr CharType get_cbor_float_prefix(double) - { - return static_cast<CharType>(0xFB); // Double-Precision Float - } - - static constexpr CharType get_msgpack_float_prefix(float) - { - return static_cast<CharType>(0xCA); // float 32 - } - - static constexpr CharType get_msgpack_float_prefix(double) - { - return static_cast<CharType>(0xCB); // float 64 - } - - static constexpr CharType get_ubjson_float_prefix(float) + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) { return 'd'; // float 32 } - static constexpr CharType get_ubjson_float_prefix(double) + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) { return 'D'; // float 64 } + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianness, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template<typename NumberType, bool OutputIsLittleEndian = false> + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array<CharType, sizeof(NumberType)> vec{}; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) && + static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) && + static_cast<double>(static_cast<float>(n)) == static_cast<double>(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast<float>(n)) + : get_msgpack_float_prefix(static_cast<float>(n))); + write_number(static_cast<float>(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See <https://github.com/nlohmann/json/issues/1286> for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast<char*>(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial<CharType>::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template<typename C = CharType, + enable_if_t<std::is_unsigned<C>::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed<C>::value && + std::is_signed<char>::value && + std::is_same<char, typename std::remove_cv<InputCharType>::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + private: - /// whether we can assume little endianess - const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess(); + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); /// the output output_adapter_t<CharType> oa = nullptr; }; -} -} +} // namespace detail +} // namespace nlohmann + +// #include <nlohmann/detail/output/output_adapters.hpp> // #include <nlohmann/detail/output/serializer.hpp> #include <algorithm> // reverse, remove, fill, find, none_of #include <array> // array -#include <cassert> // assert -#include <ciso646> // and, or #include <clocale> // localeconv, lconv #include <cmath> // labs, isfinite, isnan, signbit #include <cstddef> // size_t, ptrdiff_t #include <cstdint> // uint8_t #include <cstdio> // snprintf #include <limits> // numeric_limits -#include <string> // string +#include <string> // string, char_traits +#include <iomanip> // setfill, setw +#include <sstream> // stringstream #include <type_traits> // is_same - -// #include <nlohmann/detail/exceptions.hpp> +#include <utility> // move // #include <nlohmann/detail/conversions/to_chars.hpp> -#include <cassert> // assert -#include <ciso646> // or, and, not +#include <array> // array #include <cmath> // signbit, isfinite #include <cstdint> // intN_t, uintN_t #include <cstring> // memcpy, memmove +#include <limits> // numeric_limits +#include <type_traits> // conditional + +// #include <nlohmann/detail/macro_scope.hpp> + namespace nlohmann { @@ -8657,7 +14979,7 @@ For a detailed description of the algorithm see: namespace dtoa_impl { -template <typename Target, typename Source> +template<typename Target, typename Source> Target reinterpret_bits(const Source source) { static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); @@ -8671,11 +14993,10 @@ struct diyfp // f * 2^e { static constexpr int kPrecision = 64; // = q - uint64_t f; - int e; + std::uint64_t f = 0; + int e = 0; - constexpr diyfp() noexcept : f(0), e(0) {} - constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} /*! @brief returns x - y @@ -8683,10 +15004,10 @@ struct diyfp // f * 2^e */ static diyfp sub(const diyfp& x, const diyfp& y) noexcept { - assert(x.e == y.e); - assert(x.f >= y.f); + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); - return diyfp(x.f - y.f, x.e); + return {x.f - y.f, x.e}; } /*! @@ -8720,23 +15041,23 @@ struct diyfp // f * 2^e // // = p_lo + 2^64 p_hi - const uint64_t u_lo = x.f & 0xFFFFFFFF; - const uint64_t u_hi = x.f >> 32; - const uint64_t v_lo = y.f & 0xFFFFFFFF; - const uint64_t v_hi = y.f >> 32; + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; - const uint64_t p0 = u_lo * v_lo; - const uint64_t p1 = u_lo * v_hi; - const uint64_t p2 = u_hi * v_lo; - const uint64_t p3 = u_hi * v_hi; + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; - const uint64_t p0_hi = p0 >> 32; - const uint64_t p1_lo = p1 & 0xFFFFFFFF; - const uint64_t p1_hi = p1 >> 32; - const uint64_t p2_lo = p2 & 0xFFFFFFFF; - const uint64_t p2_hi = p2 >> 32; + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; - uint64_t Q = p0_hi + p1_lo + p2_lo; + std::uint64_t Q = p0_hi + p1_lo + p2_lo; // The full product might now be computed as // @@ -8747,11 +15068,11 @@ struct diyfp // f * 2^e // Effectively we only need to add the highest bit in p_lo to p_hi (and // Q_hi + 1 does not overflow). - Q += uint64_t{1} << (64 - 32 - 1); // round, ties up + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up - const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32); + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); - return diyfp(h, x.e + y.e + 64); + return {h, x.e + y.e + 64}; } /*! @@ -8760,11 +15081,11 @@ struct diyfp // f * 2^e */ static diyfp normalize(diyfp x) noexcept { - assert(x.f != 0); + JSON_ASSERT(x.f != 0); - while ((x.f >> 63) == 0) + while ((x.f >> 63u) == 0) { - x.f <<= 1; + x.f <<= 1u; x.e--; } @@ -8779,10 +15100,10 @@ struct diyfp // f * 2^e { const int delta = x.e - target_exponent; - assert(delta >= 0); - assert(((x.f << delta) >> delta) == x.f); + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); - return diyfp(x.f << delta, target_exponent); + return {x.f << delta, target_exponent}; } }; @@ -8799,11 +15120,11 @@ boundaries. @pre value must be finite and positive */ -template <typename FloatType> +template<typename FloatType> boundaries compute_boundaries(FloatType value) { - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // Convert the IEEE representation into a diyfp. // @@ -8818,15 +15139,15 @@ boundaries compute_boundaries(FloatType value) constexpr int kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit) constexpr int kBias = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1); constexpr int kMinExp = 1 - kBias; - constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) - using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type; + using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type; - const uint64_t bits = reinterpret_bits<bits_type>(value); - const uint64_t E = bits >> (kPrecision - 1); - const uint64_t F = bits & (kHiddenBit - 1); + const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value)); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); - const bool is_denormal = (E == 0); + const bool is_denormal = E == 0; const diyfp v = is_denormal ? diyfp(F, kMinExp) : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias); @@ -8852,7 +15173,7 @@ boundaries compute_boundaries(FloatType value) // -----------------+------+------+-------------+-------------+--- (B) // v- m- v m+ v+ - const bool lower_boundary_is_closer = (F == 0 and E > 1); + const bool lower_boundary_is_closer = F == 0 && E > 1; const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); const diyfp m_minus = lower_boundary_is_closer ? diyfp(4 * v.f - 1, v.e - 2) // (B) @@ -8927,7 +15248,7 @@ constexpr int kGamma = -32; struct cached_power // c = f * 2^e ~= 10^k { - uint64_t f; + std::uint64_t f; int e; int k; }; @@ -8951,7 +15272,7 @@ inline cached_power get_cached_power_for_binary_exponent(int e) // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) // ==> 2^(alpha - e - 1) <= c // - // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as // // k = ceil( log_10( 2^(alpha - e - 1) ) ) // = ceil( (alpha - e - 1) * log_10(2) ) @@ -8991,110 +15312,110 @@ inline cached_power get_cached_power_for_binary_exponent(int e) // NB: // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. - constexpr int kCachedPowersSize = 79; constexpr int kCachedPowersMinDecExp = -300; constexpr int kCachedPowersDecStep = 8; - static constexpr cached_power kCachedPowers[] = + static constexpr std::array<cached_power, 79> kCachedPowers = { - { 0xAB70FE17C79AC6CA, -1060, -300 }, - { 0xFF77B1FCBEBCDC4F, -1034, -292 }, - { 0xBE5691EF416BD60C, -1007, -284 }, - { 0x8DD01FAD907FFC3C, -980, -276 }, - { 0xD3515C2831559A83, -954, -268 }, - { 0x9D71AC8FADA6C9B5, -927, -260 }, - { 0xEA9C227723EE8BCB, -901, -252 }, - { 0xAECC49914078536D, -874, -244 }, - { 0x823C12795DB6CE57, -847, -236 }, - { 0xC21094364DFB5637, -821, -228 }, - { 0x9096EA6F3848984F, -794, -220 }, - { 0xD77485CB25823AC7, -768, -212 }, - { 0xA086CFCD97BF97F4, -741, -204 }, - { 0xEF340A98172AACE5, -715, -196 }, - { 0xB23867FB2A35B28E, -688, -188 }, - { 0x84C8D4DFD2C63F3B, -661, -180 }, - { 0xC5DD44271AD3CDBA, -635, -172 }, - { 0x936B9FCEBB25C996, -608, -164 }, - { 0xDBAC6C247D62A584, -582, -156 }, - { 0xA3AB66580D5FDAF6, -555, -148 }, - { 0xF3E2F893DEC3F126, -529, -140 }, - { 0xB5B5ADA8AAFF80B8, -502, -132 }, - { 0x87625F056C7C4A8B, -475, -124 }, - { 0xC9BCFF6034C13053, -449, -116 }, - { 0x964E858C91BA2655, -422, -108 }, - { 0xDFF9772470297EBD, -396, -100 }, - { 0xA6DFBD9FB8E5B88F, -369, -92 }, - { 0xF8A95FCF88747D94, -343, -84 }, - { 0xB94470938FA89BCF, -316, -76 }, - { 0x8A08F0F8BF0F156B, -289, -68 }, - { 0xCDB02555653131B6, -263, -60 }, - { 0x993FE2C6D07B7FAC, -236, -52 }, - { 0xE45C10C42A2B3B06, -210, -44 }, - { 0xAA242499697392D3, -183, -36 }, - { 0xFD87B5F28300CA0E, -157, -28 }, - { 0xBCE5086492111AEB, -130, -20 }, - { 0x8CBCCC096F5088CC, -103, -12 }, - { 0xD1B71758E219652C, -77, -4 }, - { 0x9C40000000000000, -50, 4 }, - { 0xE8D4A51000000000, -24, 12 }, - { 0xAD78EBC5AC620000, 3, 20 }, - { 0x813F3978F8940984, 30, 28 }, - { 0xC097CE7BC90715B3, 56, 36 }, - { 0x8F7E32CE7BEA5C70, 83, 44 }, - { 0xD5D238A4ABE98068, 109, 52 }, - { 0x9F4F2726179A2245, 136, 60 }, - { 0xED63A231D4C4FB27, 162, 68 }, - { 0xB0DE65388CC8ADA8, 189, 76 }, - { 0x83C7088E1AAB65DB, 216, 84 }, - { 0xC45D1DF942711D9A, 242, 92 }, - { 0x924D692CA61BE758, 269, 100 }, - { 0xDA01EE641A708DEA, 295, 108 }, - { 0xA26DA3999AEF774A, 322, 116 }, - { 0xF209787BB47D6B85, 348, 124 }, - { 0xB454E4A179DD1877, 375, 132 }, - { 0x865B86925B9BC5C2, 402, 140 }, - { 0xC83553C5C8965D3D, 428, 148 }, - { 0x952AB45CFA97A0B3, 455, 156 }, - { 0xDE469FBD99A05FE3, 481, 164 }, - { 0xA59BC234DB398C25, 508, 172 }, - { 0xF6C69A72A3989F5C, 534, 180 }, - { 0xB7DCBF5354E9BECE, 561, 188 }, - { 0x88FCF317F22241E2, 588, 196 }, - { 0xCC20CE9BD35C78A5, 614, 204 }, - { 0x98165AF37B2153DF, 641, 212 }, - { 0xE2A0B5DC971F303A, 667, 220 }, - { 0xA8D9D1535CE3B396, 694, 228 }, - { 0xFB9B7CD9A4A7443C, 720, 236 }, - { 0xBB764C4CA7A44410, 747, 244 }, - { 0x8BAB8EEFB6409C1A, 774, 252 }, - { 0xD01FEF10A657842C, 800, 260 }, - { 0x9B10A4E5E9913129, 827, 268 }, - { 0xE7109BFBA19C0C9D, 853, 276 }, - { 0xAC2820D9623BF429, 880, 284 }, - { 0x80444B5E7AA7CF85, 907, 292 }, - { 0xBF21E44003ACDD2D, 933, 300 }, - { 0x8E679C2F5E44FF8F, 960, 308 }, - { 0xD433179D9C8CB841, 986, 316 }, - { 0x9E19DB92B4E31BA9, 1013, 324 }, + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } }; // This computation gives exactly the same results for k as // k = ceil((kAlpha - e - 1) * 0.30102999566398114) // for |e| <= 1500, but doesn't require floating-point operations. // NB: log_10(2) ~= 78913 / 2^18 - assert(e >= -1500); - assert(e <= 1500); + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); const int f = kAlpha - e - 1; - const int k = (f * 78913) / (1 << 18) + (f > 0); + const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; - assert(index >= 0); - assert(index < kCachedPowersSize); - static_cast<void>(kCachedPowersSize); // Fix warning. + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size()); - const cached_power cached = kCachedPowers[index]; - assert(kAlpha <= cached.e + e + 64); - assert(kGamma >= cached.e + e + 64); + const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); return cached; } @@ -9103,7 +15424,7 @@ inline cached_power get_cached_power_for_binary_exponent(int e) For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. For n == 0, returns 1 and sets pow10 := 1. */ -inline int find_largest_pow10(const uint32_t n, uint32_t& pow10) +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) { // LCOV_EXCL_START if (n >= 1000000000) @@ -9112,60 +15433,58 @@ inline int find_largest_pow10(const uint32_t n, uint32_t& pow10) return 10; } // LCOV_EXCL_STOP - else if (n >= 100000000) + if (n >= 100000000) { pow10 = 100000000; return 9; } - else if (n >= 10000000) + if (n >= 10000000) { pow10 = 10000000; return 8; } - else if (n >= 1000000) + if (n >= 1000000) { pow10 = 1000000; return 7; } - else if (n >= 100000) + if (n >= 100000) { pow10 = 100000; return 6; } - else if (n >= 10000) + if (n >= 10000) { pow10 = 10000; return 5; } - else if (n >= 1000) + if (n >= 1000) { pow10 = 1000; return 4; } - else if (n >= 100) + if (n >= 100) { pow10 = 100; return 3; } - else if (n >= 10) + if (n >= 10) { pow10 = 10; return 2; } - else - { - pow10 = 1; - return 1; - } + + pow10 = 1; + return 1; } -inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, - uint64_t rest, uint64_t ten_k) +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) { - assert(len >= 1); - assert(dist <= delta); - assert(rest <= delta); - assert(ten_k > 0); + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); // <--------------------------- delta ----> // <---- dist ---------> @@ -9187,10 +15506,10 @@ inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, // integer arithmetic. while (rest < dist - and delta - rest >= ten_k - and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) { - assert(buf[len - 1] != '0'); + JSON_ASSERT(buf[len - 1] != '0'); buf[len - 1]--; rest += ten_k; } @@ -9218,11 +15537,11 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // Grisu2 generates the digits of M+ from left to right and stops as soon as // V is in [M-,M+]. - assert(M_plus.e >= kAlpha); - assert(M_plus.e <= kGamma); + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); - uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) - uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): // @@ -9231,18 +15550,18 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = ((p1 ) * 2^-e + (p2 )) * 2^e // = p1 + p2 * 2^e - const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e); + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); - uint32_t p1 = static_cast<uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) - uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e // 1) // // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] - assert(p1 > 0); + JSON_ASSERT(p1 > 0); - uint32_t pow10; + std::uint32_t pow10{}; const int k = find_largest_pow10(p1, pow10); // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) @@ -9270,13 +15589,13 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) // pow10 = 10^(n-1) <= p1 < 10^n // - const uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) - const uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) // // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) // - assert(d <= 9); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) @@ -9296,7 +15615,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // Note: // Since rest and delta share the same exponent e, it suffices to // compare the significands. - const uint64_t rest = (uint64_t{p1} << -one.e) + p2; + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; if (rest <= delta) { // V = buffer * 10^n, with M- <= V <= M+. @@ -9312,7 +15631,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e // - const uint64_t ten_n = uint64_t{pow10} << -one.e; + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; grisu2_round(buffer, length, dist, delta, rest, ten_n); return; @@ -9363,7 +15682,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // and stop as soon as 10^-m * r * 2^e <= delta * 2^e - assert(p2 > delta); + JSON_ASSERT(p2 > delta); int m = 0; for (;;) @@ -9374,16 +15693,16 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e // - assert(p2 <= UINT64_MAX / 10); + JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10); p2 *= 10; - const uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e - const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e // // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e // - assert(d <= 9); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e @@ -9417,7 +15736,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e // - const uint64_t ten_m = one.f; + const std::uint64_t ten_m = one.f; grisu2_round(buffer, length, dist, delta, p2, ten_m); // By construction this algorithm generates the shortest possible decimal @@ -9440,11 +15759,12 @@ v = buf * 10^decimal_exponent len is the length of the buffer (number of decimal digits) The buffer must be large enough, i.e. >= max_digits10. */ +JSON_HEDLEY_NON_NULL(1) inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) { - assert(m_plus.e == m_minus.e); - assert(m_plus.e == v.e); + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); // --------(-----------------------+-----------------------)-------- (A) // m- v m+ @@ -9498,14 +15818,15 @@ v = buf * 10^decimal_exponent len is the length of the buffer (number of decimal digits) The buffer must be large enough, i.e. >= max_digits10. */ -template <typename FloatType> +template<typename FloatType> +JSON_HEDLEY_NON_NULL(1) void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) { static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3, "internal error: not enough precision"); - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // If the neighbors (and boundaries) of 'value' are always computed for double-precision // numbers, all float's can be recovered using strtod (and strtof). However, the resulting @@ -9513,7 +15834,7 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) // // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) // says "value is converted to a string as if by std::sprintf in the default ("C") locale" - // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars' // does. // On the other hand, the documentation for 'std::to_chars' requires that "parsing the // representation using the corresponding std::from_chars function recovers value exactly". That @@ -9537,10 +15858,12 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) @return a pointer to the element following the exponent. @pre -1000 < e < 1000 */ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL inline char* append_exponent(char* buf, int e) { - assert(e > -1000); - assert(e < 1000); + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); if (e < 0) { @@ -9552,7 +15875,7 @@ inline char* append_exponent(char* buf, int e) *buf++ = '+'; } - uint32_t k = static_cast<uint32_t>(e); + auto k = static_cast<std::uint32_t>(e); if (k < 10) { // Always print at least two digits in the exponent. @@ -9587,11 +15910,13 @@ notation. Otherwise it will be printed in exponential notation. @pre min_exp < 0 @pre max_exp > 0 */ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp) { - assert(min_exp < 0); - assert(max_exp > 0); + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); const int k = len; const int n = len + decimal_exponent; @@ -9600,40 +15925,40 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // k is the length of the buffer (number of decimal digits) // n is the position of the decimal point relative to the start of the buffer. - if (k <= n and n <= max_exp) + if (k <= n && n <= max_exp) { // digits[000] // len <= max_exp + 2 - std::memset(buf + k, '0', static_cast<size_t>(n - k)); + std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k)); // Make it look like a floating-point number (#362, #378) buf[n + 0] = '.'; buf[n + 1] = '0'; - return buf + (n + 2); + return buf + (static_cast<size_t>(n) + 2); } - if (0 < n and n <= max_exp) + if (0 < n && n <= max_exp) { // dig.its // len <= max_digits10 + 1 - assert(k > n); + JSON_ASSERT(k > n); - std::memmove(buf + (n + 1), buf + n, static_cast<size_t>(k - n)); + std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n)); buf[n] = '.'; - return buf + (k + 1); + return buf + (static_cast<size_t>(k) + 1U); } - if (min_exp < n and n <= 0) + if (min_exp < n && n <= 0) { // 0.[000]digits // len <= 2 + (-min_exp - 1) + max_digits10 - std::memmove(buf + (2 + -n), buf, static_cast<size_t>(k)); + std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k)); buf[0] = '0'; buf[1] = '.'; std::memset(buf + 2, '0', static_cast<size_t>(-n)); - return buf + (2 + (-n) + k); + return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k)); } if (k == 1) @@ -9648,9 +15973,9 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // d.igitsE+123 // len <= max_digits10 + 1 + 5 - std::memmove(buf + 2, buf + 1, static_cast<size_t>(k - 1)); + std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1); buf[1] = '.'; - buf += 1 + k; + buf += 1 + static_cast<size_t>(k); } *buf++ = 'e'; @@ -9669,11 +15994,13 @@ format. Returns an iterator pointing past-the-end of the decimal representation. @note The buffer must be large enough. @note The result is NOT null-terminated. */ -template <typename FloatType> -char* to_chars(char* first, char* last, FloatType value) +template<typename FloatType> +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) { static_cast<void>(last); // maybe unused - fix warning - assert(std::isfinite(value)); + JSON_ASSERT(std::isfinite(value)); // Use signbit(value) instead of (value < 0) since signbit works for -0. if (std::signbit(value)) @@ -9682,6 +16009,10 @@ char* to_chars(char* first, char* last, FloatType value) *first++ = '-'; } +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif if (value == 0) // +-0 { *first++ = '0'; @@ -9690,8 +16021,11 @@ char* to_chars(char* first, char* last, FloatType value) *first++ = '0'; return first; } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif - assert(last - first >= std::numeric_limits<FloatType>::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10); // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted @@ -9701,16 +16035,16 @@ char* to_chars(char* first, char* last, FloatType value) int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); - assert(len <= std::numeric_limits<FloatType>::max_digits10); + JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10); // Format the buffer like printf("%.*g", prec, value) constexpr int kMinExp = -4; // Use digits10 here to increase compatibility with version 2. constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10; - assert(last - first >= kMaxExp + 2); - assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10); - assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6); + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6); return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); } @@ -9718,10 +16052,14 @@ char* to_chars(char* first, char* last, FloatType value) } // namespace detail } // namespace nlohmann +// #include <nlohmann/detail/exceptions.hpp> + // #include <nlohmann/detail/macro_scope.hpp> // #include <nlohmann/detail/meta/cpp_future.hpp> +// #include <nlohmann/detail/output/binary_writer.hpp> + // #include <nlohmann/detail/output/output_adapters.hpp> // #include <nlohmann/detail/value_t.hpp> @@ -9735,6 +16073,14 @@ namespace detail // serialization // /////////////////// +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + template<typename BasicJsonType> class serializer { @@ -9742,24 +16088,33 @@ class serializer using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - static constexpr uint8_t UTF8_ACCEPT = 0; - static constexpr uint8_t UTF8_REJECT = 1; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; public: /*! @param[in] s output stream to serialize to @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors */ - serializer(output_adapter_t<char> s, const char ichar) - : o(std::move(s)), loc(std::localeconv()), - thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)), - decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)), - indent_char(ichar), indent_string(512, indent_char) + serializer(output_adapter_t<char> s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) {} // delete because of pointer members serializer(const serializer&) = delete; serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; /*! @brief internal implementation of the serialization function @@ -9772,13 +16127,19 @@ class serializer - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array - @param[in] val value to serialize - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) */ - void dump(const BasicJsonType& val, const bool pretty_print, + void dump(const BasicJsonType& val, + const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, const unsigned int current_indent = 0) @@ -9799,7 +16160,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -9817,8 +16178,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); @@ -9845,8 +16206,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); @@ -9872,7 +16233,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -9887,7 +16248,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(!val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); @@ -9908,7 +16269,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(!val.m_value.array->empty()); dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); @@ -9925,6 +16286,79 @@ class serializer return; } + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + case value_t::boolean: { if (val.m_value.boolean) @@ -9967,10 +16401,13 @@ class serializer o->write_characters("null", 4); return; } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } } - private: + JSON_PRIVATE_UNLESS_TESTED: /*! @brief dump escaped string @@ -9987,13 +16424,17 @@ class serializer */ void dump_escaped(const string_t& s, const bool ensure_ascii) { - uint32_t codepoint; - uint8_t state = UTF8_ACCEPT; + std::uint32_t codepoint{}; + std::uint8_t state = UTF8_ACCEPT; std::size_t bytes = 0; // number of bytes written to string_buffer + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + for (std::size_t i = 0; i < s.size(); ++i) { - const auto byte = static_cast<uint8_t>(s[i]); + const auto byte = static_cast<std::uint8_t>(s[i]); switch (decode(state, codepoint, byte)) { @@ -10054,19 +16495,21 @@ class serializer { // escape control characters (0x00..0x1F) or, if // ensure_ascii parameter is used, non-ASCII characters - if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F))) + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) { if (codepoint <= 0xFFFF) { - std::snprintf(string_buffer.data() + bytes, 7, "\\u%04x", - static_cast<uint16_t>(codepoint)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast<std::uint16_t>(codepoint))); bytes += 6; } else { - std::snprintf(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", - static_cast<uint16_t>(0xD7C0 + (codepoint >> 10)), - static_cast<uint16_t>(0xDC00 + (codepoint & 0x3FF))); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)), + static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)))); bytes += 12; } } @@ -10088,29 +16531,99 @@ class serializer o->write_characters(string_buffer.data(), bytes); bytes = 0; } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; break; } case UTF8_REJECT: // decode found invalid UTF-8 byte { - std::string sn(3, '\0'); - snprintf(&sn[0], sn.size(), "%.2X", byte); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn)); + switch (error_handler) + { + case error_handler_t::strict: + { + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType())); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + break; } default: // decode found yet incomplete multi-byte code point { - if (not ensure_ascii) + if (!ensure_ascii) { // code point will not be escaped - copy byte to buffer string_buffer[bytes++] = s[i]; } + ++undumped_chars; break; } } } - if (JSON_LIKELY(state == UTF8_ACCEPT)) + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) { // write buffer if (bytes > 0) @@ -10121,12 +16634,92 @@ class serializer else { // we finish reading, but do not accept: string was incomplete - std::string sn(3, '\0'); - snprintf(&sn[0], sn.size(), "%.2X", static_cast<uint8_t>(s.back())); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); + switch (error_handler) + { + case error_handler_t::strict: + { + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast<std::uint8_t>(s.back()) | 0); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType())); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } } } + private: + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + // templates to avoid warnings about useless casts + template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0> + bool is_negative_number(NumberType x) + { + return x < 0; + } + + template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 > + bool is_negative_number(NumberType /*unused*/) + { + return false; + } + /*! @brief dump an integer @@ -10136,12 +16729,30 @@ class serializer @param[in] x integer number (signed or unsigned) to dump @tparam NumberType either @a number_integer_t or @a number_unsigned_t */ - template<typename NumberType, detail::enable_if_t< - std::is_same<NumberType, number_unsigned_t>::value or - std::is_same<NumberType, number_integer_t>::value, - int> = 0> + template < typename NumberType, detail::enable_if_t < + std::is_integral<NumberType>::value || + std::is_same<NumberType, number_unsigned_t>::value || + std::is_same<NumberType, number_integer_t>::value || + std::is_same<NumberType, binary_char_t>::value, + int > = 0 > void dump_integer(NumberType x) { + static constexpr std::array<std::array<char, 2>, 100> digits_to_99 + { + { + {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, + {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, + {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, + {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, + {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, + {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, + {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, + {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, + {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, + {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, + } + }; + // special case for "0" if (x == 0) { @@ -10149,28 +16760,56 @@ class serializer return; } - const bool is_negative = not (x >= 0); // see issue #755 - std::size_t i = 0; + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) - while (x != 0) + number_unsigned_t abs_value; + + unsigned int n_chars{}; + + if (is_negative_number(x)) { - // spare 1 byte for '\0' - assert(i < number_buffer.size() - 1); + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast<number_integer_t>(x)); - const auto digit = std::labs(static_cast<long>(x % 10)); - number_buffer[i++] = static_cast<char>('0' + digit); - x /= 10; + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast<number_unsigned_t>(x); + n_chars = count_digits(abs_value); } - if (is_negative) + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward, + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) { - // make sure there is capacity for the '-' - assert(i < number_buffer.size() - 2); - number_buffer[i++] = '-'; + const auto digits_index = static_cast<unsigned>((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; } - std::reverse(number_buffer.begin(), number_buffer.begin() + i); - o->write_characters(number_buffer.data(), i); + if (abs_value >= 10) + { + const auto digits_index = static_cast<unsigned>(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast<char>('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); } /*! @@ -10184,7 +16823,7 @@ class serializer void dump_float(number_float_t x) { // NaN / inf - if (not std::isfinite(x)) + if (!std::isfinite(x)) { o->write_characters("null", 4); return; @@ -10196,16 +16835,16 @@ class serializer // // NB: The test below works if <long double> == <double>. static constexpr bool is_ieee_single_or_double - = (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or - (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024); + = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) || + (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024); dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>()); } void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) { - char* begin = number_buffer.data(); - char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + auto* begin = number_buffer.data(); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); o->write_characters(begin, static_cast<size_t>(end - begin)); } @@ -10216,26 +16855,28 @@ class serializer static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10; // the actual conversion - std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); // negative value indicates an error - assert(len > 0); + JSON_ASSERT(len > 0); // check if buffer was large enough - assert(static_cast<std::size_t>(len) < number_buffer.size()); + JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size()); // erase thousands separator if (thousands_sep != '\0') { - const auto end = std::remove(number_buffer.begin(), - number_buffer.begin() + len, thousands_sep); + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); + JSON_ASSERT((end - number_buffer.begin()) <= len); len = (end - number_buffer.begin()); } // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') + if (decimal_point != '\0' && decimal_point != '.') { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081 const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); if (dec_pos != number_buffer.end()) { @@ -10245,12 +16886,12 @@ class serializer o->write_characters(number_buffer.data(), static_cast<std::size_t>(len)); - // determine if need to append ".0" + // determine if we need to append ".0" const bool value_is_int_like = std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, [](char c) { - return (c == '.' or c == 'e'); + return c == '.' || c == 'e'; }); if (value_is_int_like) @@ -10280,9 +16921,9 @@ class serializer @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ */ - static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept { - static const std::array<uint8_t, 400> utf8d = + static const std::array<std::uint8_t, 400> utf8d = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F @@ -10302,16 +16943,45 @@ class serializer } }; - const uint8_t type = utf8d[byte]; + JSON_ASSERT(byte < utf8d.size()); + const std::uint8_t type = utf8d[byte]; codep = (state != UTF8_ACCEPT) - ? (byte & 0x3fu) | (codep << 6) - : static_cast<uint32_t>(0xff >> type) & (byte); + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); - state = utf8d[256u + state * 16u + type]; + std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; return state; } + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression) + return static_cast<number_unsigned_t>(-(x + 1)) + 1; + } + private: /// the output of the serializer output_adapter_t<char> o = nullptr; @@ -10333,829 +17003,253 @@ class serializer const char indent_char; /// the indentation string string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; }; -} -} +} // namespace detail +} // namespace nlohmann -// #include <nlohmann/detail/json_ref.hpp> +// #include <nlohmann/detail/value_t.hpp> + +// #include <nlohmann/json_fwd.hpp> + +// #include <nlohmann/ordered_map.hpp> -#include <initializer_list> -#include <utility> - -namespace nlohmann -{ -namespace detail -{ -template<typename BasicJsonType> -class json_ref -{ - public: - using value_type = BasicJsonType; - - json_ref(value_type&& value) - : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) - {} - - json_ref(const value_type& value) - : value_ref(const_cast<value_type*>(&value)), is_rvalue(false) - {} - - json_ref(std::initializer_list<json_ref> init) - : owned_value(init), value_ref(&owned_value), is_rvalue(true) - {} - - template<class... Args> - json_ref(Args&& ... args) - : owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true) - {} - - // class should be movable only - json_ref(json_ref&&) = default; - json_ref(const json_ref&) = delete; - json_ref& operator=(const json_ref&) = delete; - - value_type moved_or_copied() const - { - if (is_rvalue) - { - return std::move(*value_ref); - } - return *value_ref; - } - - value_type const& operator*() const - { - return *static_cast<value_type const*>(value_ref); - } - - value_type const* operator->() const - { - return static_cast<value_type const*>(value_ref); - } - - private: - mutable value_type owned_value = nullptr; - value_type* value_ref = nullptr; - const bool is_rvalue; -}; -} -} - -// #include <nlohmann/detail/json_pointer.hpp> - - -#include <cassert> // assert -#include <numeric> // accumulate -#include <string> // string +#include <functional> // less +#include <initializer_list> // initializer_list +#include <iterator> // input_iterator_tag, iterator_traits +#include <memory> // allocator +#include <stdexcept> // for out_of_range +#include <type_traits> // enable_if, is_convertible +#include <utility> // pair #include <vector> // vector // #include <nlohmann/detail/macro_scope.hpp> -// #include <nlohmann/detail/exceptions.hpp> - -// #include <nlohmann/detail/value_t.hpp> - namespace nlohmann { -template<typename BasicJsonType> -class json_pointer + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json<ordered_map> +template <class Key, class T, class IgnoredLess = std::less<Key>, + class Allocator = std::allocator<std::pair<const Key, T>>> + struct ordered_map : std::vector<std::pair<const Key, T>, Allocator> { - // allow basic_json to access private members - NLOHMANN_BASIC_JSON_TPL_DECLARATION - friend class basic_json; + using key_type = Key; + using mapped_type = T; + using Container = std::vector<std::pair<const Key, T>, Allocator>; + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using size_type = typename Container::size_type; + using value_type = typename Container::value_type; - public: - /*! - @brief create JSON pointer + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template <class It> + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} - Create a JSON pointer according to the syntax described in - [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - - @param[in] s string representing the JSON pointer; if omitted, the empty - string is assumed which references the whole JSON value - - @throw parse_error.107 if the given JSON pointer @a s is nonempty and does - not begin with a slash (`/`); see example below - - @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is - not followed by `0` (representing `~`) or `1` (representing `/`); see - example below - - @liveexample{The example shows the construction several valid JSON pointers - as well as the exceptional behavior.,json_pointer} - - @since version 2.0.0 - */ - explicit json_pointer(const std::string& s = "") - : reference_tokens(split(s)) - {} - - /*! - @brief return a string representation of the JSON pointer - - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode - - @return a string representation of the JSON pointer - - @liveexample{The example shows the result of `to_string`., - json_pointer__to_string} - - @since version 2.0.0 - */ - std::string to_string() const noexcept + std::pair<iterator, bool> emplace(const key_type& key, T&& t) { - return std::accumulate(reference_tokens.begin(), reference_tokens.end(), - std::string{}, - [](const std::string & a, const std::string & b) + for (auto it = this->begin(); it != this->end(); ++it) { - return a + "/" + escape(b); - }); - } - - /// @copydoc to_string() - operator std::string() const - { - return to_string(); - } - - /*! - @param[in] s reference token to be converted into an array index - - @return integer representation of @a s - - @throw out_of_range.404 if string @a s could not be converted to an integer - */ - static int array_index(const std::string& s) - { - std::size_t processed_chars = 0; - const int res = std::stoi(s, &processed_chars); - - // check if the string was completely read - if (JSON_UNLIKELY(processed_chars != s.size())) - { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); - } - - return res; - } - - private: - /*! - @brief remove and return last reference pointer - @throw out_of_range.405 if JSON pointer has no parent - */ - std::string pop_back() - { - if (JSON_UNLIKELY(is_root())) - { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); - } - - auto last = reference_tokens.back(); - reference_tokens.pop_back(); - return last; - } - - /// return whether pointer points to the root document - bool is_root() const - { - return reference_tokens.empty(); - } - - json_pointer top() const - { - if (JSON_UNLIKELY(is_root())) - { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); - } - - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; - } - - /*! - @brief create and return a reference to the pointed to value - - @complexity Linear in the number of reference tokens. - - @throw parse_error.109 if array index is not a number - @throw type_error.313 if value cannot be unflattened - */ - BasicJsonType& get_and_create(BasicJsonType& j) const - { - using size_type = typename BasicJsonType::size_type; - auto result = &j; - - // in case no reference tokens exist, return a reference to the JSON value - // j which will be overwritten by a primitive value - for (const auto& reference_token : reference_tokens) - { - switch (result->m_type) + if (it->first == key) { - case detail::value_t::null: - { - if (reference_token == "0") - { - // start a new array if reference token is 0 - result = &result->operator[](0); - } - else - { - // start a new object otherwise - result = &result->operator[](reference_token); - } - break; - } + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } - case detail::value_t::object: - { - // create an entry in the object - result = &result->operator[](reference_token); - break; - } + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } - case detail::value_t::array: - { - // create an entry in the array - JSON_TRY - { - result = &result->operator[](static_cast<size_type>(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } + const T& operator[](const Key& key) const + { + return at(key); + } - /* - The following code is only reached if there exists a reference - token _and_ the current value is primitive. In this case, we have - an error situation, because primitive values may only occur as - single value; that is, with an empty list of reference tokens. - */ - default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; } } - return *result; + JSON_THROW(std::out_of_range("key not found")); } - /*! - @brief return a reference to the pointed to value - - @note This version does not throw if a value is not present, but tries to - create nested values instead. For instance, calling this function - with pointer `"/this/that"` on a null value is equivalent to calling - `operator[]("this").operator[]("that")` on that value, effectively - changing the null value to an object. - - @param[in] ptr a JSON value - - @return reference to the JSON value pointed to by the JSON pointer - - @complexity Linear in the length of the JSON pointer. - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - BasicJsonType& get_unchecked(BasicJsonType* ptr) const + const T& at(const Key& key) const { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) + for (auto it = this->begin(); it != this->end(); ++it) { - // convert null values to arrays or objects before continuing - if (ptr->m_type == detail::value_t::null) + if (it->first == key) { - // check if reference token is a number - const bool nums = - std::all_of(reference_token.begin(), reference_token.end(), - [](const char x) - { - return (x >= '0' and x <= '9'); - }); - - // change value to array for numbers or "-" or to object otherwise - *ptr = (nums or reference_token == "-") - ? detail::value_t::array - : detail::value_t::object; - } - - switch (ptr->m_type) - { - case detail::value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case detail::value_t::array: - { - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - - if (reference_token == "-") - { - // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast<size_type>(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + return it->second; } } - return *ptr; + JSON_THROW(std::out_of_range("key not found")); } - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - BasicJsonType& get_checked(BasicJsonType* ptr) const + size_type erase(const Key& key) { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) + for (auto it = this->begin(); it != this->end(); ++it) { - switch (ptr->m_type) + if (it->first == key) { - case detail::value_t::object: + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; } - - case detail::value_t::array: - { - if (JSON_UNLIKELY(reference_token == "-")) - { - // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast<size_type>(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + Container::pop_back(); + return 1; } } - - return *ptr; + return 0; } - /*! - @brief return a const reference to the pointed to value - - @param[in] ptr a JSON value - - @return const reference to the JSON value pointed to by the JSON - pointer - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + iterator erase(iterator pos) { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) + return erase(pos, std::next(pos)); + } + + iterator erase(iterator first, iterator last) + { + const auto elements_affected = std::distance(first, last); + const auto offset = std::distance(Container::begin(), first); + + // This is the start situation. We need to delete elements_affected + // elements (3 in this example: e, f, g), and need to return an + // iterator past the last deleted element (h in this example). + // Note that offset is the distance from the start of the vector + // to first. We will need this later. + + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // first last + + // Since we cannot move const Keys, we re-construct them in place. + // We start at first and re-construct (viz. copy) the elements from + // the back of the vector. Example for first iteration: + + // ,--------. + // v | destroy e and re-construct with h + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // it it + elements_affected + + for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) { - switch (ptr->m_type) + it->~value_type(); // destroy but keep allocation + new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it + } + + // [ a, b, c, d, h, i, j, h, i, j ] + // ^ ^ + // first last + + // remove the unneeded elements at the end of the vector + Container::resize(this->size() - static_cast<size_type>(elements_affected)); + + // [ a, b, c, d, h, i, j ] + // ^ ^ + // first last + + // first is now pointing past the last deleted element, but we cannot + // use this iterator, because it may have been invalidated by the + // resize call. Instead, we can return begin() + offset. + return Container::begin() + offset; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) { - case detail::value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case detail::value_t::array: - { - if (JSON_UNLIKELY(reference_token == "-")) - { - // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - - // use unchecked array access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast<size_type>(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + return 1; } } - - return *ptr; + return 0; } - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - const BasicJsonType& get_checked(const BasicJsonType* ptr) const + iterator find(const Key& key) { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) + for (auto it = this->begin(); it != this->end(); ++it) { - switch (ptr->m_type) + if (it->first == key) { - case detail::value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case detail::value_t::array: - { - if (JSON_UNLIKELY(reference_token == "-")) - { - // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + reference_token + - "' must not begin with '0'")); - } - - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at(static_cast<size_type>(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + return it; } } - - return *ptr; + return Container::end(); } - /*! - @brief split the string input to reference tokens - - @note This function is only called by the json_pointer constructor. - All exceptions below are documented there. - - @throw parse_error.107 if the pointer is not empty or begins with '/' - @throw parse_error.108 if character '~' is not followed by '0' or '1' - */ - static std::vector<std::string> split(const std::string& reference_string) + const_iterator find(const Key& key) const { - std::vector<std::string> result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) + for (auto it = this->begin(); it != this->end(); ++it) { - return result; - } - - // check if nonempty reference string begins with slash - if (JSON_UNLIKELY(reference_string[0] != '/')) - { - JSON_THROW(detail::parse_error::create(107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); - } - - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - std::size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == string::npos+1 = 0 - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); - - // check reference tokens are properly escaped - for (std::size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) + if (it->first == key) { - assert(reference_token[pos] == '~'); - - // ~ must be followed by 0 or 1 - if (JSON_UNLIKELY(pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1'))) - { - JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); - } - } - - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); - } - - return result; - } - - /*! - @brief replace all occurrences of a substring by another string - - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f - - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** - - @since version 2.0.0 - */ - static void replace_substring(std::string& s, const std::string& f, - const std::string& t) - { - assert(not f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} - } - - /// escape "~"" to "~0" and "/" to "~1" - static std::string escape(std::string s) - { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape "~1" to tilde and "~0" to slash (order is important!) - static void unescape(std::string& s) - { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); - } - - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to - - @note Empty objects or arrays are flattened to `null`. - */ - static void flatten(const std::string& reference_string, - const BasicJsonType& value, - BasicJsonType& result) - { - switch (value.m_type) - { - case detail::value_t::array: - { - if (value.m_value.array->empty()) - { - // flatten empty array as null - result[reference_string] = nullptr; - } - else - { - // iterate array and use index as reference string - for (std::size_t i = 0; i < value.m_value.array->size(); ++i) - { - flatten(reference_string + "/" + std::to_string(i), - value.m_value.array->operator[](i), result); - } - } - break; - } - - case detail::value_t::object: - { - if (value.m_value.object->empty()) - { - // flatten empty object as null - result[reference_string] = nullptr; - } - else - { - // iterate object and use keys as reference string - for (const auto& element : *value.m_value.object) - { - flatten(reference_string + "/" + escape(element.first), element.second, result); - } - } - break; - } - - default: - { - // add primitive value with its reference string - result[reference_string] = value; - break; + return it; } } + return Container::end(); } - /*! - @param[in] value flattened JSON - - @return unflattened JSON - - @throw parse_error.109 if array index is not a number - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - @throw type_error.313 if value cannot be unflattened - */ - static BasicJsonType - unflatten(const BasicJsonType& value) + std::pair<iterator, bool> insert( value_type&& value ) { - if (JSON_UNLIKELY(not value.is_object())) - { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); - } + return emplace(value.first, std::move(value.second)); + } - BasicJsonType result; - - // iterate the JSON object values - for (const auto& element : *value.m_value.object) + std::pair<iterator, bool> insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) { - if (JSON_UNLIKELY(not element.second.is_primitive())) + if (it->first == value.first) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + return {it, false}; } - - // assign value to reference pointed to by JSON pointer; Note that if - // the JSON pointer is "" (i.e., points to the whole value), function - // get_and_create returns a reference to result itself. An assignment - // will then create a primitive value. - json_pointer(element.first).get_and_create(result) = element.second; } - - return result; + Container::push_back(value); + return {--this->end(), true}; } - friend bool operator==(json_pointer const& lhs, - json_pointer const& rhs) noexcept + template<typename InputIt> + using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category, + std::input_iterator_tag>::value>::type; + + template<typename InputIt, typename = require_input_iter<InputIt>> + void insert(InputIt first, InputIt last) { - return (lhs.reference_tokens == rhs.reference_tokens); - } - - friend bool operator!=(json_pointer const& lhs, - json_pointer const& rhs) noexcept - { - return not (lhs == rhs); - } - - /// the reference tokens - std::vector<std::string> reference_tokens; -}; -} - -// #include <nlohmann/adl_serializer.hpp> - - -#include <utility> - -// #include <nlohmann/detail/conversions/from_json.hpp> - -// #include <nlohmann/detail/conversions/to_json.hpp> - - -namespace nlohmann -{ -template<typename, typename> -struct adl_serializer -{ - /*! - @brief convert a JSON value to any value type - - This function is usually called by the `get()` function of the - @ref basic_json class (either explicit or via conversion operators). - - @param[in] j JSON value to read from - @param[in,out] val value to write to - */ - template<typename BasicJsonType, typename ValueType> - static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( - noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val))) -> decltype( - ::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void() - ) - { - ::nlohmann::from_json(std::forward<BasicJsonType>(j), val); - } - - /*! - @brief convert any value type to a JSON value - - This function is usually called by the constructors of the @ref basic_json - class. - - @param[in,out] j JSON value to write to - @param[in] val value to read from - */ - template <typename BasicJsonType, typename ValueType> - static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val)))) - -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), - void()) - { - ::nlohmann::to_json(j, std::forward<ValueType>(val)); + for (auto it = first; it != last; ++it) + { + insert(*it); + } } }; -} +} // namespace nlohmann + + +#if defined(JSON_HAS_CPP_17) + #include <string_view> +#endif /*! @brief namespace for Niels Lohmann @@ -11168,66 +17262,7 @@ namespace nlohmann /*! @brief a class to store JSON values -@tparam ObjectType type for JSON objects (`std::map` by default; will be used -in @ref object_t) -@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used -in @ref array_t) -@tparam StringType type for JSON strings and object keys (`std::string` by -default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (`bool` by default; will be used -in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by -default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c -`uint64_t` by default; will be used in @ref number_unsigned_t) -@tparam NumberFloatType type for JSON floating-point numbers (`double` by -default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (`std::allocator` by -default) -@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` -and `from_json()` (@ref adl_serializer by default) - -@requirement The class satisfies the following concept requirements: -- Basic - - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null - value. - - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): - A JSON value can be constructed from an rvalue argument. - - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): - A JSON value can be copy-constructed from an lvalue expression. - - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): - A JSON value van be assigned from an rvalue argument. - - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): - A JSON value can be copy-assigned from an lvalue expression. - - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): - JSON values can be destructed. -- Layout - - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): - JSON values have - [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the - class has no virtual functions or (virtual) base classes. -- Library-wide - - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): - JSON values can be compared with `==`, see @ref - operator==(const_reference,const_reference). - - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): - JSON values can be compared with `<`, see @ref - operator<(const_reference,const_reference). - - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): - Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of - other compatible types, using unqualified function call @ref swap(). - - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): - JSON values can be compared against `std::nullptr_t` objects which are used - to model the `null` value. -- Container - - [Container](https://en.cppreference.com/w/cpp/named_req/Container): - JSON values can be used like STL containers and provide iterator access. - - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); - JSON values can be used like STL containers and provide reverse iterator - access. - +@internal @invariant The member variables @a m_value and @a m_type have the following relationship: - If `m_type == value_t::object`, then `m_value.object != nullptr`. @@ -11235,43 +17270,55 @@ relationship: - If `m_type == value_t::string`, then `m_value.string != nullptr`. The invariants are checked by member function assert_invariant(). -@internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 +@note ObjectType trick from https://stackoverflow.com/a/9860911 @endinternal -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) - @since version 1.0.0 @nosubgrouping */ NLOHMANN_BASIC_JSON_TPL_DECLARATION -class basic_json +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) { private: template<detail::value_t> friend struct detail::external_constructor; friend ::nlohmann::json_pointer<basic_json>; - friend ::nlohmann::detail::parser<basic_json>; + + template<typename BasicJsonType, typename InputType> + friend class ::nlohmann::detail::parser; friend ::nlohmann::detail::serializer<basic_json>; template<typename BasicJsonType> friend class ::nlohmann::detail::iter_impl; template<typename BasicJsonType, typename CharType> friend class ::nlohmann::detail::binary_writer; - template<typename BasicJsonType, typename SAX> + template<typename BasicJsonType, typename InputType, typename SAX> friend class ::nlohmann::detail::binary_reader; template<typename BasicJsonType> friend class ::nlohmann::detail::json_sax_dom_parser; template<typename BasicJsonType> friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + JSON_PRIVATE_UNLESS_TESTED: // convenience aliases for types residing in namespace detail; - using lexer = ::nlohmann::detail::lexer<basic_json>; - using parser = ::nlohmann::detail::parser<basic_json>; + using lexer = ::nlohmann::detail::lexer_base<basic_json>; + template<typename InputAdapterType> + static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser( + InputAdapterType adapter, + detail::parser_callback_t<basic_json>cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + private: using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; template<typename BasicJsonType> using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>; @@ -11284,9 +17331,11 @@ class basic_json template<typename CharType> using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>; - using binary_reader = ::nlohmann::detail::binary_reader<basic_json>; + template<typename InputType> + using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>; template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>; + JSON_PRIVATE_UNLESS_TESTED: using serializer = ::nlohmann::detail::serializer<basic_json>; public: @@ -11295,6 +17344,10 @@ class basic_json using json_pointer = ::nlohmann::json_pointer<basic_json>; template<typename T, typename SFINAE> using json_serializer = JSONSerializer<T, SFINAE>; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>; @@ -11310,17 +17363,11 @@ class basic_json /// Classes to implement user-defined exceptions. /// @{ - /// @copydoc detail::exception using exception = detail::exception; - /// @copydoc detail::parse_error using parse_error = detail::parse_error; - /// @copydoc detail::invalid_iterator using invalid_iterator = detail::invalid_iterator; - /// @copydoc detail::type_error using type_error = detail::type_error; - /// @copydoc detail::out_of_range using out_of_range = detail::out_of_range; - /// @copydoc detail::other_error using other_error = detail::other_error; /// @} @@ -11368,45 +17415,21 @@ class basic_json /// @} - /*! - @brief returns the allocator associated with the container - */ + /// @brief returns the allocator associated with the container + /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ static allocator_type get_allocator() { return allocator_type(); } - /*! - @brief returns version information on the library - - This function returns a JSON object with information about the library, - including the version number and information on the platform and compiler. - - @return JSON object holding version information - key | description - ----------- | --------------- - `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). - `copyright` | The copyright line for the library as string. - `name` | The name of the library as string. - `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. - `url` | The URL of the project as string. - `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). - - @liveexample{The following code shows an example output of the `meta()` - function.,meta} - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @complexity Constant. - - @since 2.1.0 - */ + /// @brief returns version information on the library + /// @sa https://json.nlohmann.me/api/basic_json/meta/ + JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json meta() { basic_json result; - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["copyright"] = "(C) 2013-2022 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = @@ -11467,6 +17490,8 @@ class basic_json /// the template arguments passed to class @ref basic_json. /// @{ + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ #if defined(JSON_HAS_CPP_14) // Use transparent comparator if possible, combined with perfect forwarding // on find() and count() calls prevents unnecessary string construction. @@ -11475,456 +17500,69 @@ class basic_json using object_comparator_t = std::less<StringType>; #endif - /*! - @brief a type for an object - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: - > An object is an unordered collection of zero or more name/value pairs, - > where a name is a string and a value is a string, number, boolean, null, - > object, or array. - - To store objects in C++, a type is defined by the template parameters - described below. - - @tparam ObjectType the container to store objects (e.g., `std::map` or - `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). - The comparison function `std::less<StringType>` is used to order elements - inside the container. - @tparam AllocatorType the allocator to use for objects (e.g., - `std::allocator`) - - #### Default type - - With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default - value for @a object_t is: - - @code {.cpp} - std::map< - std::string, // key_type - basic_json, // value_type - std::less<std::string>, // key_compare - std::allocator<std::pair<const std::string, basic_json>> // allocator_type - > - @endcode - - #### Behavior - - The choice of @a object_t influences the behavior of the JSON class. With - the default type, objects have the following behavior: - - - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on - the name-value mappings. - - When the names within an object are not unique, it is unspecified which - one of the values for a given key will be chosen. For instance, - `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or - `{"key": 2}`. - - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. - For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored - and serialized as `{"a": 2, "b": 1}`. - - When comparing objects, the order of the name/value pairs is irrelevant. - This makes objects interoperable in the sense that they will not be - affected by these differences. For instance, `{"b": 1, "a": 2}` and - `{"a": 2, "b": 1}` will be treated as equal. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the object's limit of nesting is not explicitly constrained. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON object. - - #### Storage - - Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be - dereferenced. - - @sa @ref array_t -- type for an array value - - @since version 1.0.0 - - @note The order name/value pairs are added to the object is *not* - preserved by the library. Therefore, iterating an object may return - name/value pairs in a different order than they were originally stored. In - fact, keys will be traversed in alphabetical order as `std::map` with - `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the - specified "unordered" nature of JSON objects. - */ + /// @brief a type for an object + /// @sa https://json.nlohmann.me/api/basic_json/object_t/ using object_t = ObjectType<StringType, basic_json, object_comparator_t, AllocatorType<std::pair<const StringType, basic_json>>>; - /*! - @brief a type for an array - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: - > An array is an ordered sequence of zero or more values. - - To store objects in C++, a type is defined by the template parameters - explained below. - - @tparam ArrayType container type to store arrays (e.g., `std::vector` or - `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) - - #### Default type - - With the default values for @a ArrayType (`std::vector`) and @a - AllocatorType (`std::allocator`), the default value for @a array_t is: - - @code {.cpp} - std::vector< - basic_json, // value_type - std::allocator<basic_json> // allocator_type - > - @endcode - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the array's limit of nesting is not explicitly constrained. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON array. - - #### Storage - - Arrays are stored as pointers in a @ref basic_json type. That is, for any - access to array values, a pointer of type `array_t*` must be dereferenced. - - @sa @ref object_t -- type for an object value - - @since version 1.0.0 - */ + /// @brief a type for an array + /// @sa https://json.nlohmann.me/api/basic_json/array_t/ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>; - /*! - @brief a type for a string - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: - > A string is a sequence of zero or more Unicode characters. - - To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into - byte-sized characters during deserialization. - - @tparam StringType the container to store strings (e.g., `std::string`). - Note this container is used for keys/names in objects, see @ref object_t. - - #### Default type - - With the default values for @a StringType (`std::string`), the default - value for @a string_t is: - - @code {.cpp} - std::string - @endcode - - #### Encoding - - Strings are stored in UTF-8 encoding. Therefore, functions like - `std::string::size()` or `std::string::length()` return the number of - bytes in the string rather than the number of characters or glyphs. - - #### String comparison - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > Software implementations are typically required to test names of object - > members for equality. Implementations that transform the textual - > representation into sequences of Unicode code units and then perform the - > comparison numerically, code unit by code unit, are interoperable in the - > sense that implementations will agree in all cases on equality or - > inequality of two strings. For example, implementations that compare - > strings with escaped characters unconverted may incorrectly find that - > `"a\\b"` and `"a\u005Cb"` are not equal. - - This implementation is interoperable as it does compare strings code unit - by code unit. - - #### Storage - - String values are stored as pointers in a @ref basic_json type. That is, - for any access to string values, a pointer of type `string_t*` must be - dereferenced. - - @since version 1.0.0 - */ + /// @brief a type for a string + /// @sa https://json.nlohmann.me/api/basic_json/string_t/ using string_t = StringType; - /*! - @brief a type for a boolean - - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a - type which differentiates the two literals `true` and `false`. - - To store objects in C++, a type is defined by the template parameter @a - BooleanType which chooses the type to use. - - #### Default type - - With the default values for @a BooleanType (`bool`), the default value for - @a boolean_t is: - - @code {.cpp} - bool - @endcode - - #### Storage - - Boolean values are stored directly inside a @ref basic_json type. - - @since version 1.0.0 - */ + /// @brief a type for a boolean + /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/ using boolean_t = BooleanType; - /*! - @brief a type for a number (integer) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store integer numbers in C++, a type is defined by the template - parameter @a NumberIntegerType which chooses the type to use. - - #### Default type - - With the default values for @a NumberIntegerType (`int64_t`), the default - value for @a number_integer_t is: - - @code {.cpp} - int64_t - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. - - When the default type is used, the maximal integer number that can be - stored is `9223372036854775807` (INT64_MAX) and the minimal integer number - that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a - constructor. During deserialization, too large or small integer numbers - will be automatically be stored as @ref number_unsigned_t or @ref - number_float_t. - - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. - - As this range is a subrange of the exactly supported range [INT64_MIN, - INT64_MAX], this class's integer type is interoperable. - - #### Storage - - Integer number values are stored directly inside a @ref basic_json type. - - @sa @ref number_float_t -- type for number values (floating-point) - - @sa @ref number_unsigned_t -- type for number values (unsigned integer) - - @since version 1.0.0 - */ + /// @brief a type for a number (integer) + /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/ using number_integer_t = NumberIntegerType; - /*! - @brief a type for a number (unsigned) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store unsigned integer numbers in C++, a type is defined by the - template parameter @a NumberUnsignedType which chooses the type to use. - - #### Default type - - With the default values for @a NumberUnsignedType (`uint64_t`), the - default value for @a number_unsigned_t is: - - @code {.cpp} - uint64_t - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. - - When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer - number that can be stored is `0`. Integer numbers that are out of range - will yield over/underflow when used in a constructor. During - deserialization, too large or small integer numbers will be automatically - be stored as @ref number_integer_t or @ref number_float_t. - - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. - - As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], - this class's integer type is interoperable. - - #### Storage - - Integer number values are stored directly inside a @ref basic_json type. - - @sa @ref number_float_t -- type for number values (floating-point) - @sa @ref number_integer_t -- type for number values (integer) - - @since version 2.0.0 - */ + /// @brief a type for a number (unsigned) + /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/ using number_unsigned_t = NumberUnsignedType; - /*! - @brief a type for a number (floating-point) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store floating-point numbers in C++, a type is defined by the template - parameter @a NumberFloatType which chooses the type to use. - - #### Default type - - With the default values for @a NumberFloatType (`double`), the default - value for @a number_float_t is: - - @code {.cpp} - double - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, - the value will be stored as decimal number. For instance, the C++ - floating-point literal `01.2` will be serialized to `1.2`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > This specification allows implementations to set limits on the range and - > precision of numbers accepted. Since software that implements IEEE - > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations - > that expect no more precision or range than these provide, in the sense - > that implementations will approximate JSON numbers within the expected - > precision. - - This implementation does exactly follow this approach, as it uses double - precision floating-point numbers. Note values smaller than - `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` - will be stored as NaN internally and be serialized to `null`. - - #### Storage - - Floating-point number values are stored directly inside a @ref basic_json - type. - - @sa @ref number_integer_t -- type for number values (integer) - - @sa @ref number_unsigned_t -- type for number values (unsigned integer) - - @since version 1.0.0 - */ + /// @brief a type for a number (floating-point) + /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/ using number_float_t = NumberFloatType; + /// @brief a type for a packed binary type + /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ + using binary_t = nlohmann::byte_container_with_subtype<BinaryType>; + /// @} private: /// helper for exception-safe object creation template<typename T, typename... Args> + JSON_HEDLEY_RETURNS_NON_NULL static T* create(Args&& ... args) { AllocatorType<T> alloc; using AllocatorTraits = std::allocator_traits<AllocatorType<T>>; - auto deleter = [&](T * object) + auto deleter = [&](T * obj) { - AllocatorTraits::deallocate(alloc, object, 1); + AllocatorTraits::deallocate(alloc, obj, 1); }; - std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter); - AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...); - assert(object != nullptr); - return object.release(); + std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); } //////////////////////// // JSON value storage // //////////////////////// + JSON_PRIVATE_UNLESS_TESTED: /*! @brief a JSON value @@ -11941,6 +17579,7 @@ class basic_json number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as @@ -11957,6 +17596,8 @@ class basic_json array_t* array; /// string (stored with pointer to save storage) string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; /// boolean boolean_t boolean; /// number (integer) @@ -11999,27 +17640,33 @@ class basic_json break; } + case value_t::binary: + { + binary = create<binary_t>(); + break; + } + case value_t::boolean: { - boolean = boolean_t(false); + boolean = static_cast<boolean_t>(false); break; } case value_t::number_integer: { - number_integer = number_integer_t(0); + number_integer = static_cast<number_integer_t>(0); break; } case value_t::number_unsigned: { - number_unsigned = number_unsigned_t(0); + number_unsigned = static_cast<number_unsigned_t>(0); break; } case value_t::number_float: { - number_float = number_float_t(0.0); + number_float = static_cast<number_float_t>(0.0); break; } @@ -12029,12 +17676,13 @@ class basic_json break; } + case value_t::discarded: default: { object = nullptr; // silence warning, see #821 - if (JSON_UNLIKELY(t == value_t::null)) + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.2.0")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", basic_json())); // LCOV_EXCL_LINE } break; } @@ -12042,43 +17690,86 @@ class basic_json } /// constructor for strings - json_value(const string_t& value) - { - string = create<string_t>(value); - } + json_value(const string_t& value) : string(create<string_t>(value)) {} /// constructor for rvalue strings - json_value(string_t&& value) - { - string = create<string_t>(std::move(value)); - } + json_value(string_t&& value) : string(create<string_t>(std::move(value))) {} /// constructor for objects - json_value(const object_t& value) - { - object = create<object_t>(value); - } + json_value(const object_t& value) : object(create<object_t>(value)) {} /// constructor for rvalue objects - json_value(object_t&& value) - { - object = create<object_t>(std::move(value)); - } + json_value(object_t&& value) : object(create<object_t>(std::move(value))) {} /// constructor for arrays - json_value(const array_t& value) - { - array = create<array_t>(value); - } + json_value(const array_t& value) : array(create<array_t>(value)) {} /// constructor for rvalue arrays - json_value(array_t&& value) - { - array = create<array_t>(std::move(value)); - } + json_value(array_t&& value) : array(create<array_t>(std::move(value))) {} - void destroy(value_t t) noexcept + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {} + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {} + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) : binary(create<binary_t>(value)) {} + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {} + + void destroy(value_t t) { + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector<basic_json> stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + } + switch (t) { case value_t::object: @@ -12105,6 +17796,20 @@ class basic_json break; } + case value_t::binary: + { + AllocatorType<binary_t> alloc; + std::allocator_traits<decltype(alloc)>::destroy(alloc, binary); + std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: default: { break; @@ -12113,6 +17818,7 @@ class basic_json } }; + private: /*! @brief checks the class invariants @@ -12121,12 +17827,123 @@ class basic_json invariant. Furthermore, it has to be called each time the type of a JSON value is changed, because the invariant expresses a relationship between @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. */ - void assert_invariant() const noexcept + void assert_invariant(bool check_parents = true) const noexcept { - assert(m_type != value_t::object or m_value.object != nullptr); - assert(m_type != value_t::array or m_value.array != nullptr); - assert(m_type != value_t::string or m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast<void>(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count_set_parents) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count_set_parents; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast<void>(count_set_parents); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = static_cast<std::size_t>(-1)) + { +#if JSON_DIAGNOSTICS + if (old_capacity != static_cast<std::size_t>(-1)) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map<object_t>::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast<void>(j); + static_cast<void>(old_capacity); +#endif + return j; } public: @@ -12134,73 +17951,13 @@ class basic_json // JSON parser callback // ////////////////////////// - /*! - @brief parser event types + /// @brief parser event types + /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/ + using parse_event_t = detail::parse_event_t; - The parser callback distinguishes the following events: - - `object_start`: the parser read `{` and started to process a JSON object - - `key`: the parser read a key of a value in an object - - `object_end`: the parser read `}` and finished processing a JSON object - - `array_start`: the parser read `[` and started to process a JSON array - - `array_end`: the parser read `]` and finished processing a JSON array - - `value`: the parser finished reading a JSON value - - @image html callback_events.png "Example when certain parse events are triggered" - - @sa @ref parser_callback_t for more information and examples - */ - using parse_event_t = typename parser::parse_event_t; - - /*! - @brief per-element parser callback type - - With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse, it is called on certain events - (passed as @ref parse_event_t via parameter @a event) with a set recursion - depth @a depth and context JSON value @a parsed. The return value of the - callback function is a boolean indicating whether the element that emitted - the callback shall be kept or not. - - We distinguish six scenarios (determined by the event type) in which the - callback function can be called. The following table describes the values - of the parameters @a depth, @a event, and @a parsed. - - parameter @a event | description | parameter @a depth | parameter @a parsed - ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded - parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key - parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object - parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded - parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array - parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value - - @image html callback_events.png "Example when certain parse events are triggered" - - Discarding a value (i.e., returning `false`) has different effects - depending on the context in which function was called: - - - Discarded values in structured types are skipped. That is, the parser - will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced - with `null`. This case happens if the top-level element is skipped. - - @param[in] depth the depth of the recursion during parsing - - @param[in] event an event of type parse_event_t indicating the context in - the callback function has been called - - @param[in,out] parsed the current intermediate parse result; note that - writing to this value has no effect for parse_event_t::key events - - @return Whether the JSON value which called the function during parsing - should be kept (`true`) or not (`false`). In the latter case, it is either - skipped completely or replaced by an empty discarded object. - - @sa @ref parse for examples - - @since version 1.0.0 - */ - using parser_callback_t = typename parser::parser_callback_t; + /// @brief per-element parser callback type + /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/ + using parser_callback_t = detail::parser_callback_t<basic_json>; ////////////////// // constructors // @@ -12211,163 +17968,42 @@ class basic_json /// assignment, static functions creating objects, and the destructor. /// @{ - /*! - @brief create an empty value with a given type - - Create an empty JSON value with a given type. The value will be default - initialized with an empty value which depends on the type: - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @param[in] v the type of the value to create - - @complexity Constant. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows the constructor for different @ref - value_t values,basic_json__value_t} - - @sa @ref clear() -- restores the postcondition of this constructor - - @since version 1.0.0 - */ + /// @brief create an empty value with a given type + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(const value_t v) : m_type(v), m_value(v) { assert_invariant(); } - /*! - @brief create a null object - - Create a `null` JSON value. It either takes a null pointer as parameter - (explicitly creating `null`) or no parameter (implicitly creating `null`). - The passed null pointer itself is not read -- it is only used to choose - the right constructor. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @liveexample{The following code shows the constructor with and without a - null pointer parameter.,basic_json__nullptr_t} - - @since version 1.0.0 - */ + /// @brief create a null object + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); } - /*! - @brief create a JSON value - - This is a "catch all" constructor for all compatible JSON types; that is, - types for which a `to_json()` method exists. The constructor forwards the - parameter @a val to that method (to `json_serializer<U>::to_json` method - with `U = uncvref_t<CompatibleType>`, to be exact). - - Template type @a CompatibleType includes, but is not limited to, the - following types: - - **arrays**: @ref array_t and all kinds of compatible containers such as - `std::vector`, `std::deque`, `std::list`, `std::forward_list`, - `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, - `std::multiset`, and `std::unordered_multiset` with a `value_type` from - which a @ref basic_json value can be constructed. - - **objects**: @ref object_t and all kinds of compatible associative - containers such as `std::map`, `std::unordered_map`, `std::multimap`, - and `std::unordered_multimap` with a `key_type` compatible to - @ref string_t and a `value_type` from which a @ref basic_json value can - be constructed. - - **strings**: @ref string_t, string literals, and all compatible string - containers can be used. - - **numbers**: @ref number_integer_t, @ref number_unsigned_t, - @ref number_float_t, and all convertible number types such as `int`, - `size_t`, `int64_t`, `float` or `double` can be used. - - **boolean**: @ref boolean_t / `bool` can be used. - - See the examples below. - - @tparam CompatibleType a type such that: - - @a CompatibleType is not derived from `std::istream`, - - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move - constructors), - - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) - - @a CompatibleType is not a @ref basic_json nested type (e.g., - @ref json_pointer, @ref iterator, etc ...) - - @ref @ref json_serializer<U> has a - `to_json(basic_json_t&, CompatibleType&&)` method - - @tparam U = `uncvref_t<CompatibleType>` - - @param[in] val the value to be forwarded to the respective constructor - - @complexity Usually linear in the size of the passed @a val, also - depending on the implementation of the called `to_json()` - method. - - @exceptionsafety Depends on the called constructor. For types directly - supported by the library (i.e., all types for which no `to_json()` function - was provided), strong guarantee holds: if an exception is thrown, there are - no changes to any JSON value. - - @liveexample{The following code shows the constructor with several - compatible types.,basic_json__CompatibleType} - - @since version 2.1.0 - */ - template <typename CompatibleType, - typename U = detail::uncvref_t<CompatibleType>, - detail::enable_if_t< - not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0> - basic_json(CompatibleType && val) noexcept(noexcept( + /// @brief create a JSON value from compatible types + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename CompatibleType, + typename U = detail::uncvref_t<CompatibleType>, + detail::enable_if_t < + !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) JSONSerializer<U>::to_json(std::declval<basic_json_t&>(), std::forward<CompatibleType>(val)))) { JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val)); + set_parents(); assert_invariant(); } - /*! - @brief create a JSON value from an existing one - - This is a constructor for existing @ref basic_json types. - It does not hijack copy/move constructors, since the parameter has different - template arguments than the current ones. - - The constructor tries to convert the internal @ref m_value of the parameter. - - @tparam BasicJsonType a type such that: - - @a BasicJsonType is a @ref basic_json type. - - @a BasicJsonType has different template arguments than @ref basic_json_t. - - @param[in] val the @ref basic_json value to be converted. - - @complexity Usually linear in the size of the passed @a val, also - depending on the implementation of the called `to_json()` - method. - - @exceptionsafety Depends on the called constructor. For types directly - supported by the library (i.e., all types for which no `to_json()` function - was provided), strong guarantee holds: if an exception is thrown, there are - no changes to any JSON value. - - @since version 3.2.0 - */ - template <typename BasicJsonType, - detail::enable_if_t< - detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0> + /// @brief create a JSON value from an existing one + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 > basic_json(const BasicJsonType& val) { using other_boolean_t = typename BasicJsonType::boolean_t; @@ -12377,6 +18013,7 @@ class basic_json using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; switch (val.type()) { @@ -12401,90 +18038,24 @@ class basic_json case value_t::array: JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>()); break; + case value_t::binary: + JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>()); + break; case value_t::null: *this = nullptr; break; case value_t::discarded: m_type = value_t::discarded; break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } + set_parents(); assert_invariant(); } - /*! - @brief create a container (array or object) from an initializer list - - Creates a JSON value of type array or object from the passed initializer - list @a init. In case @a type_deduction is `true` (default), the type of - the JSON value to be created is deducted from the initializer list @a init - according to the following rules: - - 1. If the list is empty, an empty JSON object value `{}` is created. - 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are - treated as keys and the second elements are as values. - 3. In all other cases, an array is created. - - The rules aim to create the best fit between a C++ initializer list and - JSON values. The rationale is as follows: - - 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. - 2. C++ has no way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them - as an object. - 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. - - With the rules described above, the following JSON values cannot be - expressed by an initializer list: - - - the empty array (`[]`): use @ref array(initializer_list_t) - with an empty initializer list in this case - - arrays whose elements satisfy rule 2: use @ref - array(initializer_list_t) with the same initializer list - in this case - - @note When used without parentheses around an empty initializer list, @ref - basic_json() is called instead of this function, yielding the JSON null - value. - - @param[in] init initializer list with JSON values - - @param[in] type_deduction internal parameter; when set to `true`, the type - of the JSON value is deducted from the initializer list @a init; when set - to `false`, the type provided via @a manual_type is forced. This mode is - used by the functions @ref array(initializer_list_t) and - @ref object(initializer_list_t). - - @param[in] manual_type internal parameter; when @a type_deduction is set - to `false`, the created JSON value will use the provided type (only @ref - value_t::array and @ref value_t::object are valid); when @a type_deduction - is set to `true`, this parameter has no effect - - @throw type_error.301 if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string. In this case, the constructor could not - create an object. If @a type_deduction would have be `true`, an array - would have been created. See @ref object(initializer_list_t) - for an example. - - @complexity Linear in the size of the initializer list @a init. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The example below shows how JSON values are created from - initializer lists.,basic_json__list_init_t} - - @sa @ref array(initializer_list_t) -- create a JSON array - value from an initializer list - @sa @ref object(initializer_list_t) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ + /// @brief create a container (array or object) from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(initializer_list_t init, bool type_deduction = true, value_t manual_type = value_t::array) @@ -12494,11 +18065,11 @@ class basic_json bool is_an_object = std::all_of(init.begin(), init.end(), [](const detail::json_ref<basic_json>& element_ref) { - return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string()); + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); }); // adjust type if type deduction is not wanted - if (not type_deduction) + if (!type_deduction) { // if array is wanted, do not create an object though possible if (manual_type == value_t::array) @@ -12507,9 +18078,9 @@ class basic_json } // if object is wanted but impossible, throw an exception - if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object)) + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); } } @@ -12519,13 +18090,13 @@ class basic_json m_type = value_t::object; m_value = value_t::object; - std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref) + for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); - }); + } } else { @@ -12534,190 +18105,94 @@ class basic_json m_value.array = create<array_t>(init.begin(), init.end()); } + set_parents(); assert_invariant(); } - /*! - @brief explicitly create an array from an initializer list + /// @brief explicitly create a binary array (without subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } - Creates a JSON array value from a given initializer list. That is, given a - list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the - initializer list is empty, the empty array `[]` is created. + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } - @note This function is only needed to express two edge cases that cannot - be realized with the initializer list constructor (@ref - basic_json(initializer_list_t, bool, value_t)). These cases - are: - 1. creating an array whose elements are all pairs whose first element is a - string -- in this case, the initializer list constructor would create an - object, taking the first elements as keys - 2. creating an empty array -- passing the empty initializer list to the - initializer list constructor yields an empty object + /// @brief explicitly create a binary array + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } - @param[in] init initializer list with JSON values to create an array from - (optional) + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } - @return JSON array value - - @complexity Linear in the size of @a init. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows an example for the `array` - function.,array} - - @sa @ref basic_json(initializer_list_t, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref object(initializer_list_t) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ + /// @brief explicitly create an array from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/array/ + JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json array(initializer_list_t init = {}) { return basic_json(init, false, value_t::array); } - /*! - @brief explicitly create an object from an initializer list - - Creates a JSON object value from a given initializer list. The initializer - lists elements must be pairs, and their first elements must be strings. If - the initializer list is empty, the empty object `{}` is created. - - @note This function is only added for symmetry reasons. In contrast to the - related function @ref array(initializer_list_t), there are - no cases which can only be expressed by this function. That is, any - initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(initializer_list_t, bool, value_t). - - @param[in] init initializer list to create an object from (optional) - - @return JSON object value - - @throw type_error.301 if @a init is not a list of pairs whose first - elements are strings. In this case, no object can be created. When such a - value is passed to @ref basic_json(initializer_list_t, bool, value_t), - an array would have been created from the passed initializer list @a init. - See example below. - - @complexity Linear in the size of @a init. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows an example for the `object` - function.,object} - - @sa @ref basic_json(initializer_list_t, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref array(initializer_list_t) -- create a JSON array - value from an initializer list - - @since version 1.0.0 - */ + /// @brief explicitly create an object from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/object/ + JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json object(initializer_list_t init = {}) { return basic_json(init, false, value_t::object); } - /*! - @brief construct an array with count copies of given value - - Constructs a JSON array value by creating @a cnt copies of a passed value. - In case @a cnt is `0`, an empty array is created. - - @param[in] cnt the number of JSON copies of @a val to create - @param[in] val the JSON value to copy - - @post `std::distance(begin(),end()) == cnt` holds. - - @complexity Linear in @a cnt. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows examples for the @ref - basic_json(size_type\, const basic_json&) - constructor.,basic_json__size_type_basic_json} - - @since version 1.0.0 - */ + /// @brief construct an array with count copies of given value + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(size_type cnt, const basic_json& val) : m_type(value_t::array) { m_value.array = create<array_t>(cnt, val); + set_parents(); assert_invariant(); } - /*! - @brief construct a JSON container given an iterator range - - Constructs the JSON value with the contents of the range `[first, last)`. - The semantics depends on the different types a JSON value can have: - - In case of a null type, invalid_iterator.206 is thrown. - - In case of other primitive types (number, boolean, or string), @a first - must be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, invalid_iterator.204 is thrown. - - In case of structured types (array, object), the constructor behaves as - similar versions for `std::vector` or `std::map`; that is, a JSON array - or object is constructed from the values in the range. - - @tparam InputIT an input iterator type (@ref iterator or @ref - const_iterator) - - @param[in] first begin of the range to copy from (included) - @param[in] last end of the range to copy from (excluded) - - @pre Iterators @a first and @a last must be initialized. **This - precondition is enforced with an assertion (see warning).** If - assertions are switched off, a violation of this precondition yields - undefined behavior. - - @pre Range `[first, last)` is valid. Usually, this precondition cannot be - checked efficiently. Only certain edge cases are detected; see the - description of the exceptions below. A violation of this precondition - yields undefined behavior. - - @warning A precondition is enforced with a runtime assertion that will - result in calling `std::abort` if this precondition is not met. - Assertions can be disabled by defining `NDEBUG` at compile time. - See https://en.cppreference.com/w/cpp/error/assert for more - information. - - @throw invalid_iterator.201 if iterators @a first and @a last are not - compatible (i.e., do not belong to the same JSON value). In this case, - the range `[first, last)` is undefined. - @throw invalid_iterator.204 if iterators @a first and @a last belong to a - primitive type (number, boolean, or string), but @a first does not point - to the first element any more. In this case, the range `[first, last)` is - undefined. See example code below. - @throw invalid_iterator.206 if iterators @a first and @a last belong to a - null value. In this case, the range `[first, last)` is undefined. - - @complexity Linear in distance between @a first and @a last. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The example below shows several ways to create JSON values by - specifying a subrange with iterators.,basic_json__InputIt_InputIt} - - @since version 1.0.0 - */ - template<class InputIT, typename std::enable_if< - std::is_same<InputIT, typename basic_json_t::iterator>::value or - std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0> + /// @brief construct a JSON container given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < class InputIT, typename std::enable_if < + std::is_same<InputIT, typename basic_json_t::iterator>::value || + std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 > basic_json(InputIT first, InputIT last) { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); // make sure iterator fits the current value - if (JSON_UNLIKELY(first.m_object != last.m_object)) + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); } // copy type from first iterator @@ -12732,14 +18207,19 @@ class basic_json case value_t::number_unsigned: case value_t::string: { - if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin() - or not last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); } break; } + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: default: break; } @@ -12790,11 +18270,19 @@ class basic_json break; } + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: default: - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - std::string(first.m_object->type_name()))); + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); } + set_parents(); assert_invariant(); } @@ -12803,36 +18291,13 @@ class basic_json // other constructors and destructor // /////////////////////////////////////// - /// @private - basic_json(const detail::json_ref<basic_json>& ref) - : basic_json(ref.moved_or_copied()) - {} + template<typename JsonRef, + detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>, + std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} - /*! - @brief copy constructor - - Creates a copy of a given JSON value. - - @param[in] other the JSON value to copy - - @post `*this == other` - - @complexity Linear in the size of @a other. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is linear. - - As postcondition, it holds: `other == basic_json(other)`. - - @liveexample{The following code shows an example for the copy - constructor.,basic_json__basic_json} - - @since version 1.0.0 - */ + /// @brief copy constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(const basic_json& other) : m_type(other.m_type) { @@ -12883,80 +18348,45 @@ class basic_json break; } + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: default: break; } + set_parents(); assert_invariant(); } - /*! - @brief move constructor - - Move constructor. Constructs a JSON value with the contents of the given - value @a other using move semantics. It "steals" the resources from @a - other and leaves it as JSON null value. - - @param[in,out] other value to move to this object - - @post `*this` has the same value as @a other before the call. - @post @a other is a JSON null value. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) - requirements. - - @liveexample{The code below shows the move constructor explicitly called - via std::move.,basic_json__moveconstructor} - - @since version 1.0.0 - */ + /// @brief move constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { // check that passed value is valid - other.assert_invariant(); + other.assert_invariant(false); // invalidate payload other.m_type = value_t::null; other.m_value = {}; + set_parents(); assert_invariant(); } - /*! - @brief copy assignment - - Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, - and the `swap()` member function. - - @param[in] other value to copy from - - @complexity Linear. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is linear. - - @liveexample{The code below shows and example for the copy assignment. It - creates a copy of value `a` which is then swapped with `b`. Finally\, the - copy of `a` (which is the null value after the swap) is - destroyed.,basic_json__copyassignment} - - @since version 1.0.0 - */ - reference& operator=(basic_json other) noexcept ( - std::is_nothrow_move_constructible<value_t>::value and - std::is_nothrow_move_assignable<value_t>::value and - std::is_nothrow_move_constructible<json_value>::value and + /// @brief copy assignment + /// @sa https://json.nlohmann.me/api/basic_json/operator=/ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible<value_t>::value&& + std::is_nothrow_move_assignable<value_t>::value&& + std::is_nothrow_move_constructible<json_value>::value&& std::is_nothrow_move_assignable<json_value>::value ) { @@ -12967,28 +18397,16 @@ class basic_json swap(m_type, other.m_type); swap(m_value, other.m_value); + set_parents(); assert_invariant(); return *this; } - /*! - @brief destructor - - Destroys the JSON value and frees all allocated memory. - - @complexity Linear. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. - - @since version 1.0.0 - */ + /// @brief destructor + /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/ ~basic_json() noexcept { - assert_invariant(); + assert_invariant(false); m_value.destroy(m_type); } @@ -13003,47 +18421,15 @@ class basic_json /// Functions to inspect the type of a JSON value. /// @{ - /*! - @brief serialization - - Serialization function for JSON values. The function tries to mimic - Python's `json.dumps()` function, and currently supports its @a indent - and @a ensure_ascii parameters. - - @param[in] indent If indent is nonnegative, then array elements and object - members will be pretty-printed with that indent level. An indent level of - `0` will only insert newlines. `-1` (the default) selects the most compact - representation. - @param[in] indent_char The character to use for indentation if @a indent is - greater than `0`. The default is ` ` (space). - @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters - in the output are escaped with `\uXXXX` sequences, and the result consists - of ASCII characters only. - - @return string containing the serialization of the JSON value - - @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded - - @complexity Linear. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @liveexample{The following example shows the effect of different @a indent\, - @a indent_char\, and @a ensure_ascii parameters to the result of the - serialization.,dump} - - @see https://docs.python.org/2/library/json.html#json.dump - - @since version 1.0.0; indentation character @a indent_char, option - @a ensure_ascii and exceptions added in version 3.0.0 - */ - string_t dump(const int indent = -1, const char indent_char = ' ', - const bool ensure_ascii = false) const + /// @brief serialization + /// @sa https://json.nlohmann.me/api/basic_json/dump/ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const { string_t result; - serializer s(detail::output_adapter<char, string_t>(result), indent_char); + serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler); if (indent >= 0) { @@ -13057,373 +18443,106 @@ class basic_json return result; } - /*! - @brief return the type of the JSON value (explicit) - - Return the type of the JSON value as a value from the @ref value_t - enumeration. - - @return the type of the JSON value - Value type | return value - ------------------------- | ------------------------- - null | value_t::null - boolean | value_t::boolean - string | value_t::string - number (integer) | value_t::number_integer - number (unsigned integer) | value_t::number_unsigned - number (floating-point) | value_t::number_float - object | value_t::object - array | value_t::array - discarded | value_t::discarded - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `type()` for all JSON - types.,type} - - @sa @ref operator value_t() -- return the type of the JSON value (implicit) - @sa @ref type_name() -- return the type as string - - @since version 1.0.0 - */ + /// @brief return the type of the JSON value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/type/ constexpr value_t type() const noexcept { return m_type; } - /*! - @brief return whether type is primitive - - This function returns true if and only if the JSON type is primitive - (string, number, boolean, or null). - - @return `true` if type is primitive (string, number, boolean, or null), - `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_primitive()` for all JSON - types.,is_primitive} - - @sa @ref is_structured() -- returns whether JSON value is structured - @sa @ref is_null() -- returns whether JSON value is `null` - @sa @ref is_string() -- returns whether JSON value is a string - @sa @ref is_boolean() -- returns whether JSON value is a boolean - @sa @ref is_number() -- returns whether JSON value is a number - - @since version 1.0.0 - */ + /// @brief return whether type is primitive + /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ constexpr bool is_primitive() const noexcept { - return is_null() or is_string() or is_boolean() or is_number(); + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); } - /*! - @brief return whether type is structured - - This function returns true if and only if the JSON type is structured - (array or object). - - @return `true` if type is structured (array or object), `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_structured()` for all JSON - types.,is_structured} - - @sa @ref is_primitive() -- returns whether value is primitive - @sa @ref is_array() -- returns whether value is an array - @sa @ref is_object() -- returns whether value is an object - - @since version 1.0.0 - */ + /// @brief return whether type is structured + /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ constexpr bool is_structured() const noexcept { - return is_array() or is_object(); + return is_array() || is_object(); } - /*! - @brief return whether value is null - - This function returns true if and only if the JSON value is null. - - @return `true` if type is null, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_null()` for all JSON - types.,is_null} - - @since version 1.0.0 - */ + /// @brief return whether value is null + /// @sa https://json.nlohmann.me/api/basic_json/is_null/ constexpr bool is_null() const noexcept { - return (m_type == value_t::null); + return m_type == value_t::null; } - /*! - @brief return whether value is a boolean - - This function returns true if and only if the JSON value is a boolean. - - @return `true` if type is boolean, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_boolean()` for all JSON - types.,is_boolean} - - @since version 1.0.0 - */ + /// @brief return whether value is a boolean + /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ constexpr bool is_boolean() const noexcept { - return (m_type == value_t::boolean); + return m_type == value_t::boolean; } - /*! - @brief return whether value is a number - - This function returns true if and only if the JSON value is a number. This - includes both integer (signed and unsigned) and floating-point values. - - @return `true` if type is number (regardless whether integer, unsigned - integer or floating-type), `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number()` for all JSON - types.,is_number} - - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ + /// @brief return whether value is a number + /// @sa https://json.nlohmann.me/api/basic_json/is_number/ constexpr bool is_number() const noexcept { - return is_number_integer() or is_number_float(); + return is_number_integer() || is_number_float(); } - /*! - @brief return whether value is an integer number - - This function returns true if and only if the JSON value is a signed or - unsigned integer number. This excludes floating-point values. - - @return `true` if type is an integer or unsigned integer number, `false` - otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_integer()` for all - JSON types.,is_number_integer} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ + /// @brief return whether value is an integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ constexpr bool is_number_integer() const noexcept { - return (m_type == value_t::number_integer or m_type == value_t::number_unsigned); + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; } - /*! - @brief return whether value is an unsigned integer number - - This function returns true if and only if the JSON value is an unsigned - integer number. This excludes floating-point and signed integer values. - - @return `true` if type is an unsigned integer number, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_unsigned()` for all - JSON types.,is_number_unsigned} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 2.0.0 - */ + /// @brief return whether value is an unsigned integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ constexpr bool is_number_unsigned() const noexcept { - return (m_type == value_t::number_unsigned); + return m_type == value_t::number_unsigned; } - /*! - @brief return whether value is a floating-point number - - This function returns true if and only if the JSON value is a - floating-point number. This excludes signed and unsigned integer values. - - @return `true` if type is a floating-point number, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_float()` for all - JSON types.,is_number_float} - - @sa @ref is_number() -- check if value is number - @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - - @since version 1.0.0 - */ + /// @brief return whether value is a floating-point number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ constexpr bool is_number_float() const noexcept { - return (m_type == value_t::number_float); + return m_type == value_t::number_float; } - /*! - @brief return whether value is an object - - This function returns true if and only if the JSON value is an object. - - @return `true` if type is object, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_object()` for all JSON - types.,is_object} - - @since version 1.0.0 - */ + /// @brief return whether value is an object + /// @sa https://json.nlohmann.me/api/basic_json/is_object/ constexpr bool is_object() const noexcept { - return (m_type == value_t::object); + return m_type == value_t::object; } - /*! - @brief return whether value is an array - - This function returns true if and only if the JSON value is an array. - - @return `true` if type is array, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_array()` for all JSON - types.,is_array} - - @since version 1.0.0 - */ + /// @brief return whether value is an array + /// @sa https://json.nlohmann.me/api/basic_json/is_array/ constexpr bool is_array() const noexcept { - return (m_type == value_t::array); + return m_type == value_t::array; } - /*! - @brief return whether value is a string - - This function returns true if and only if the JSON value is a string. - - @return `true` if type is string, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_string()` for all JSON - types.,is_string} - - @since version 1.0.0 - */ + /// @brief return whether value is a string + /// @sa https://json.nlohmann.me/api/basic_json/is_string/ constexpr bool is_string() const noexcept { - return (m_type == value_t::string); + return m_type == value_t::string; } - /*! - @brief return whether value is discarded + /// @brief return whether value is a binary array + /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } - This function returns true if and only if the JSON value was discarded - during parsing with a callback function (see @ref parser_callback_t). - - @note This function will always be `false` for JSON values after parsing. - That is, discarded values can only occur during parsing, but will be - removed when inside a structured value or replaced by null in other cases. - - @return `true` if type is discarded, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_discarded()` for all JSON - types.,is_discarded} - - @since version 1.0.0 - */ + /// @brief return whether value is discarded + /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ constexpr bool is_discarded() const noexcept { - return (m_type == value_t::discarded); + return m_type == value_t::discarded; } - /*! - @brief return the type of the JSON value (implicit) - - Implicitly return the type of the JSON value as a value from the @ref - value_t enumeration. - - @return the type of the JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies the @ref value_t operator for - all JSON types.,operator__value_t} - - @sa @ref type() -- return the type of the JSON value (explicit) - @sa @ref type_name() -- return the type as string - - @since version 1.0.0 - */ + /// @brief return the type of the JSON value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/ constexpr operator value_t() const noexcept { return m_type; @@ -13439,12 +18558,12 @@ class basic_json /// get a boolean (explicit) boolean_t get_impl(boolean_t* /*unused*/) const { - if (JSON_LIKELY(is_boolean())) + if (JSON_HEDLEY_LIKELY(is_boolean())) { return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); } /// get a pointer to the value (object) @@ -13531,6 +18650,18 @@ class basic_json return is_number_float() ? &m_value.number_float : nullptr; } + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + /*! @brief helper function to implement get_ref() @@ -13546,14 +18677,14 @@ class basic_json static ReferenceType get_ref_impl(ThisType& obj) { // delegate the call to get_ptr<>() - auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>(); + auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>(); - if (JSON_LIKELY(ptr != nullptr)) + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) { return *ptr; } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); } public: @@ -13561,51 +18692,28 @@ class basic_json /// Direct access to the stored value of a JSON value. /// @{ - /*! - @brief get special-case overload - - This overloads avoids a lot of template boilerplate, it can be seen as the - identity method - - @tparam BasicJsonType == @ref basic_json - - @return a copy of *this - - @complexity Constant. - - @since version 2.1.0 - */ - template<typename BasicJsonType, detail::enable_if_t< - std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value, - int> = 0> - basic_json get() const + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>())) { - return *this; + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast<PointerType>(nullptr)); } - /*! - @brief get special-case overload - - This overloads converts the current @ref basic_json in a different - @ref basic_json type - - @tparam BasicJsonType == @ref basic_json - - @return a copy of *this, converted into @tparam BasicJsonType - - @complexity Depending on the implementation of the called `from_json()` - method. - - @since version 3.2.0 - */ - template<typename BasicJsonType, detail::enable_if_t< - not std::is_same<BasicJsonType, basic_json>::value and - detail::is_basic_json<BasicJsonType>::value, int> = 0> - BasicJsonType get() const + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template < typename PointerType, typename std::enable_if < + std::is_pointer<PointerType>::value&& + std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>())) { - return *this; + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast<PointerType>(nullptr)); } + private: /*! @brief get a value (explicit) @@ -13629,7 +18737,6 @@ class basic_json - @ref json_serializer<ValueType> does not have a `from_json()` method of the form `ValueType from_json(const basic_json&)` - @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @@ -13645,24 +18752,15 @@ class basic_json @since version 2.1.0 */ - template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, - detail::enable_if_t < - not detail::is_basic_json<ValueType>::value and - detail::has_from_json<basic_json_t, ValueType>::value and - not detail::has_non_default_from_json<basic_json_t, ValueType>::value, - int> = 0> - ValueType get() const noexcept(noexcept( - JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>()))) + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible<ValueType>::value&& + detail::has_from_json<basic_json_t, ValueType>::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>()))) { - // we cannot static_assert on ValueTypeCV being non-const, because - // there is support for get<const basic_json_t>(), which is why we - // still need the uncvref - static_assert(not std::is_reference<ValueTypeCV>::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - static_assert(std::is_default_constructible<ValueType>::value, - "types must be DefaultConstructible when used with get()"); - - ValueType ret; + auto ret = ValueType(); JSONSerializer<ValueType>::from_json(*this, ret); return ret; } @@ -13678,7 +18776,7 @@ class basic_json The function is equivalent to executing @code {.cpp} - return JSONSerializer<ValueTypeCV>::from_json(*this); + return JSONSerializer<ValueType>::from_json(*this); @endcode This overloads is chosen if: @@ -13689,7 +18787,6 @@ class basic_json @note If @ref json_serializer<ValueType> has both overloads of `from_json()`, this one is chosen. - @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @@ -13698,109 +18795,116 @@ class basic_json @since version 2.1.0 */ - template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, - detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and - detail::has_non_default_from_json<basic_json_t, ValueType>::value, - int> = 0> - ValueType get() const noexcept(noexcept( - JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>()))) + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json<basic_json_t, ValueType>::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>()))) { - static_assert(not std::is_reference<ValueTypeCV>::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - return JSONSerializer<ValueTypeCV>::from_json(*this); + return JSONSerializer<ValueType>::from_json(*this); } /*! - @brief get a value (explicit) + @brief get special-case overload - Explicit type conversion between the JSON value and a compatible value. - The value is filled into the input parameter by calling the @ref json_serializer<ValueType> - `from_json()` method. + This overloads converts the current @ref basic_json in a different + @ref basic_json type - The function is equivalent to executing - @code {.cpp} - ValueType v; - JSONSerializer<ValueType>::from_json(*this, v); - @endcode + @tparam BasicJsonType == @ref basic_json - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer<ValueType> has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and + @return a copy of *this, converted into @a BasicJsonType - @tparam ValueType the input parameter type. + @complexity Depending on the implementation of the called `from_json()` + method. - @return the input parameter, allowing chaining calls. - - @throw what @ref json_serializer<ValueType> `from_json()` method throws - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector<short>`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map<std::string\, - json>`.,get_to} - - @since version 3.3.0 + @since version 3.2.0 */ - template<typename ValueType, - detail::enable_if_t < - not detail::is_basic_json<ValueType>::value and - detail::has_from_json<basic_json_t, ValueType>::value, - int> = 0> - ValueType & get_to(ValueType& v) const noexcept(noexcept( - JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v))) + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json<BasicJsonType>::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const { - JSONSerializer<ValueType>::from_json(*this, v); - return v; + return *this; } - /*! - @brief get a pointer value (implicit) + @brief get special-case overload - Implicit pointer access to the internally stored JSON value. No copies are - made. + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method - @warning Writing data to the pointee of the result yields an undefined - state. + @tparam BasicJsonType == @ref basic_json - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + @return a copy of *this @complexity Constant. - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} - - @since version 1.0.0 + @since version 2.1.0 */ - template<typename PointerType, typename std::enable_if< - std::is_pointer<PointerType>::value, int>::type = 0> - auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>())) + template<typename BasicJsonType, + detail::enable_if_t< + std::is_same<BasicJsonType, basic_json_t>::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast<PointerType>(nullptr)); + return *this; } /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @brief get a pointer value (explicit) + @copydoc get() */ - template<typename PointerType, typename std::enable_if< - std::is_pointer<PointerType>::value and - std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0> - constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>())) + template<typename PointerType, + detail::enable_if_t< + std::is_pointer<PointerType>::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>()) { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast<PointerType>(nullptr)); + // delegate the call to get_ptr + return get_ptr<PointerType>(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))) + -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get<const basic_json_t>(), which is why we + // still need the uncvref + static_assert(!std::is_reference<ValueTypeCV>::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl<ValueType>(detail::priority_tag<4> {}); } /*! @@ -13826,7 +18930,7 @@ class basic_json `nullptr` is returned if the value and the requested pointer type does not match.,get__PointerType} - @sa @ref get_ptr() for explicit pointer-member access + @sa see @ref get_ptr() for explicit pointer-member access @since version 1.0.0 */ @@ -13838,44 +18942,47 @@ class basic_json return get_ptr<PointerType>(); } - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template<typename PointerType, typename std::enable_if< - std::is_pointer<PointerType>::value, int>::type = 0> - constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>()) + /// @brief get a value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_to/ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json<ValueType>::value&& + detail::has_from_json<basic_json_t, ValueType>::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v))) { - // delegate the call to get_ptr - return get_ptr<PointerType>(); + JSONSerializer<ValueType>::from_json(*this, v); + return v; } - /*! - @brief get a reference value (implicit) + // specialization to allow calling get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template<typename ValueType, + detail::enable_if_t < + detail::is_basic_json<ValueType>::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } - Implicit reference access to the internally stored JSON value. No copies - are made. + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json<basic_json_t, Array>::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer<Array>::from_json( + std::declval<const basic_json_t&>(), v))) + { + JSONSerializer<Array>::from_json(*this, v); + return v; + } - @warning Writing data to the referee of the result yields an undefined - state. - - @tparam ReferenceType reference type; must be a reference to @ref array_t, - @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. Enforced by static assertion. - - @return reference to the internally stored JSON value if the requested - reference type @a ReferenceType fits to the JSON value; throws - type_error.303 otherwise - - @throw type_error.303 in case passed type @a ReferenceType is incompatible - with the stored JSON value; see example below - - @complexity Constant. - - @liveexample{The example shows several calls to `get_ref()`.,get_ref} - - @since version 1.1.0 - */ + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ template<typename ReferenceType, typename std::enable_if< std::is_reference<ReferenceType>::value, int>::type = 0> ReferenceType get_ref() @@ -13884,13 +18991,11 @@ class basic_json return get_ref_impl<ReferenceType>(*this); } - /*! - @brief get a reference value (implicit) - @copydoc get_ref() - */ - template<typename ReferenceType, typename std::enable_if< - std::is_reference<ReferenceType>::value and - std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0> + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template < typename ReferenceType, typename std::enable_if < + std::is_reference<ReferenceType>::value&& + std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 > ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -13927,25 +19032,48 @@ class basic_json @since version 1.0.0 */ template < typename ValueType, typename std::enable_if < - not std::is_pointer<ValueType>::value and - not std::is_same<ValueType, detail::json_ref<basic_json>>::value and - not std::is_same<ValueType, typename string_t::value_type>::value and - not detail::is_basic_json<ValueType>::value + detail::conjunction < + detail::negation<std::is_pointer<ValueType>>, + detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>, + detail::negation<std::is_same<ValueType, typename string_t::value_type>>, + detail::negation<detail::is_basic_json<ValueType>>, + detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>, -#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 - and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value -#if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914 - and not std::is_same<ValueType, typename std::string_view>::value +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation<std::is_same<ValueType, std::string_view>>, #endif -#endif - and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value - , int >::type = 0 > - operator ValueType() const + detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType> + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const { // delegate the call to get<>() const return get<ValueType>(); } + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr<binary_t*>(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr<const binary_t*>(); + } + /// @} @@ -13957,83 +19085,35 @@ class basic_json /// Access to the JSON value. /// @{ - /*! - @brief access specified array element with bounds checking - - Returns a reference to the element at specified location @a idx, with - bounds checking. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 1.0.0 - - @liveexample{The example below shows how array elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__size_type} - */ + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ reference at(size_type idx) { // at only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { JSON_TRY { - return m_value.array->at(idx); + return set_parent(m_value.array->at(idx)); } JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } - /*! - @brief access specified array element with bounds checking - - Returns a const reference to the element at specified location @a idx, - with bounds checking. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 1.0.0 - - @liveexample{The example below shows how array elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__size_type_const} - */ + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ const_reference at(size_type idx) const { // at only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { JSON_TRY { @@ -14042,100 +19122,44 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } - /*! - @brief access specified object element with bounds checking - - Returns a reference to the element at with specified key @a key, with - bounds checking. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Logarithmic in the size of the container. - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__object_t_key_type} - */ + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ reference at(const typename object_t::key_type& key) { // at only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { JSON_TRY { - return m_value.object->at(key); + return set_parent(m_value.object->at(key)); } JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } - /*! - @brief access specified object element with bounds checking - - Returns a const reference to the element at with specified key @a key, - with bounds checking. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Logarithmic in the size of the container. - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__object_t_key_type_const} - */ + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ const_reference at(const typename object_t::key_type& key) const { // at only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { JSON_TRY { @@ -14144,40 +19168,17 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } } - /*! - @brief access specified array element - - Returns a reference to the element at specified location @a idx. - - @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), - then the array is silently filled up with `null` values to make `idx` a - valid reference to the last stored element. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw type_error.305 if the JSON value is not an array or null; in that - cases, using the [] operator with an index makes no sense. - - @complexity Constant if @a idx is in the range of the array. Otherwise - linear in `idx - size()`. - - @liveexample{The example below shows how array elements can be read and - written using `[]` operator. Note the addition of `null` - values.,operatorarray__size_type} - - @since version 1.0.0 - */ + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ reference operator[](size_type idx) { // implicitly convert null value to an empty array @@ -14189,79 +19190,54 @@ class basic_json } // operator[] only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { - m_value.array->insert(m_value.array->end(), - idx - m_value.array->size() + 1, - basic_json()); +#if JSON_DIAGNOSTICS + // remember array size & capacity before resizing + const auto old_size = m_value.array->size(); + const auto old_capacity = m_value.array->capacity(); +#endif + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + } + else + { + // set parent for values added above + set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size)); + } +#endif + assert_invariant(); } return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); } - /*! - @brief access specified array element - - Returns a const reference to the element at specified location @a idx. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw type_error.305 if the JSON value is not an array; in that case, - using the [] operator with an index makes no sense. - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read using - the `[]` operator.,operatorarray__size_type_const} - - @since version 1.0.0 - */ + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ const_reference operator[](size_type idx) const { // const operator[] only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); } - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ reference operator[](const typename object_t::key_type& key) { // implicitly convert null value to an empty object @@ -14273,84 +19249,32 @@ class basic_json } // operator[] only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { - return m_value.object->operator[](key); + return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** - - @throw type_error.305 if the JSON value is not an object; in that case, - using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ const_reference operator[](const typename object_t::key_type& key) const { // const operator[] only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ template<typename T> + JSON_HEDLEY_NON_NULL(2) reference operator[](T* key) { // implicitly convert null to object @@ -14362,186 +19286,75 @@ class basic_json } // at only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { - return m_value.object->operator[](key); + return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** - - @throw type_error.305 if the JSON value is not an object; in that case, - using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ template<typename T> + JSON_HEDLEY_NON_NULL(2) const_reference operator[](T* key) const { // at only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } - /*! - @brief access specified object element with default value - - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. - - The function is basically equivalent to executing - @code {.cpp} - try { - return at(key); - } catch(out_of_range) { - return default_value; - } - @endcode - - @note Unlike @ref at(const typename object_t::key_type&), this function - does not throw if the given key @a key was not found. - - @note Unlike @ref operator[](const typename object_t::key_type& key), this - function does not implicitly add an element to the position defined by @a - key. This function is furthermore also applicable to const objects. - - @param[in] key key of the element to access - @param[in] default_value the value to return if @a key is not found - - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. - - @return copy of the element at key @a key or @a default_value if @a key - is not found - - @throw type_error.306 if the JSON value is not an object; in that case, - using `value()` with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - - @since version 1.0.0 - */ - template<class ValueType, typename std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable<basic_json_t, ValueType>::value + && !std::is_same<value_t, ValueType>::value, int >::type = 0 > ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // at only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { // if key is found, return value and given default value otherwise const auto it = find(key); if (it != end()) { - return *it; + return it->template get<ValueType>(); } return default_value; } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); } - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const - */ + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* string_t value(const typename object_t::key_type& key, const char* default_value) const { return value(key, string_t(default_value)); } - /*! - @brief access specified object element via JSON Pointer with default value - - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. - - The function is basically equivalent to executing - @code {.cpp} - try { - return at(ptr); - } catch(out_of_range) { - return default_value; - } - @endcode - - @note Unlike @ref at(const json_pointer&), this function does not throw - if the given key @a key was not found. - - @param[in] ptr a JSON pointer to the element to access - @param[in] default_value the value to return if @a ptr found no value - - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. - - @return copy of the element at key @a key or @a default_value if @a key - is not found - - @throw type_error.306 if the JSON value is not an object; in that case, - using `value()` with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value_ptr} - - @sa @ref operator[](const json_pointer&) for unchecked access by reference - - @since version 2.0.2 - */ + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ template<class ValueType, typename std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> + detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0> ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // at only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { // if pointer resolves a value, return it or use default value JSON_TRY { - return ptr.get_checked(this); + return ptr.get_checked(this).template get<ValueType>(); } JSON_INTERNAL_CATCH (out_of_range&) { @@ -14549,87 +19362,34 @@ class basic_json } } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); } - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const json_pointer&, ValueType) const - */ + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* + JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { return value(ptr, string_t(default_value)); } - /*! - @brief access the first element - - Returns a reference to the first element in the container. For a JSON - container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - - @return In case of a structured type (array or object), a reference to the - first element is returned. In case of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. - - @throw invalid_iterator.214 when called on `null` value - - @liveexample{The following code shows an example for `front()`.,front} - - @sa @ref back() -- access the last element - - @since version 1.0.0 - */ + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ reference front() { return *begin(); } - /*! - @copydoc basic_json::front() - */ + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ const_reference front() const { return *cbegin(); } - /*! - @brief access the last element - - Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to - @code {.cpp} - auto tmp = c.end(); - --tmp; - return *tmp; - @endcode - - @return In case of a structured type (array or object), a reference to the - last element is returned. In case of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. - - @throw invalid_iterator.214 when called on a `null` value. See example - below. - - @liveexample{The following code shows an example for `back()`.,back} - - @sa @ref front() -- access the first element - - @since version 1.0.0 - */ + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ reference back() { auto tmp = end(); @@ -14637,9 +19397,8 @@ class basic_json return *tmp; } - /*! - @copydoc basic_json::back() - */ + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ const_reference back() const { auto tmp = cend(); @@ -14647,62 +19406,18 @@ class basic_json return *tmp; } - /*! - @brief remove element given an iterator - - Removes the element specified by iterator @a pos. The iterator @a pos must - be valid and dereferenceable. Thus the `end()` iterator (which is valid, - but is not dereferenceable) cannot be used as a value for @a pos. - - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. - - @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a - pos refers to the last element, the `end()` iterator is returned. - - @tparam IteratorType an @ref iterator or @ref const_iterator - - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. - - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.202 if called on an iterator which does not belong - to the current JSON value; example: `"iterator does not fit current - value"` - @throw invalid_iterator.205 if called on a primitive type with invalid - iterator (i.e., any iterator which is not `begin()`); example: `"iterator - out of range"` - - @complexity The complexity depends on the type: - - objects: amortized constant - - arrays: linear in distance between @a pos and the end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType} - - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - template<class IteratorType, typename std::enable_if< - std::is_same<IteratorType, typename basic_json_t::iterator>::value or - std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type - = 0> + /// @brief remove element given an iterator + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same<IteratorType, typename basic_json_t::iterator>::value || + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type + = 0 > IteratorType erase(IteratorType pos) { // make sure iterator fits the current value - if (JSON_UNLIKELY(this != pos.m_object)) + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } IteratorType result = end(); @@ -14714,10 +19429,11 @@ class basic_json case value_t::number_integer: case value_t::number_unsigned: case value_t::string: + case value_t::binary: { - if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); } if (is_string()) @@ -14727,6 +19443,13 @@ class basic_json std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } + else if (is_binary()) + { + AllocatorType<binary_t> alloc; + std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary); + std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } m_type = value_t::null; assert_invariant(); @@ -14745,69 +19468,27 @@ class basic_json break; } + case value_t::null: + case value_t::discarded: default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } return result; } - /*! - @brief remove elements given an iterator range - - Removes the element specified by the range `[first; last)`. The iterator - @a first does not need to be dereferenceable if `first == last`: erasing - an empty range is a no-op. - - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. - - @param[in] first iterator to the beginning of the range to remove - @param[in] last iterator past the end of the range to remove - @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the `end()` iterator is returned. - - @tparam IteratorType an @ref iterator or @ref const_iterator - - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. - - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.203 if called on iterators which does not belong - to the current JSON value; example: `"iterators do not fit current value"` - @throw invalid_iterator.204 if called on a primitive type with invalid - iterators (i.e., if `first != begin()` and `last != end()`); example: - `"iterators out of range"` - - @complexity The complexity depends on the type: - - objects: `log(size()) + std::distance(first, last)` - - arrays: linear in the distance between @a first and @a last, plus linear - in the distance between @a last and end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType_IteratorType} - - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - template<class IteratorType, typename std::enable_if< - std::is_same<IteratorType, typename basic_json_t::iterator>::value or - std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type - = 0> + /// @brief remove elements given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same<IteratorType, typename basic_json_t::iterator>::value || + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type + = 0 > IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value - if (JSON_UNLIKELY(this != first.m_object or this != last.m_object)) + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); } IteratorType result = end(); @@ -14819,11 +19500,12 @@ class basic_json case value_t::number_integer: case value_t::number_unsigned: case value_t::string: + case value_t::binary: { - if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin() - or not last.m_it.primitive_iterator.is_end())) + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); } if (is_string()) @@ -14833,6 +19515,13 @@ class basic_json std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } + else if (is_binary()) + { + AllocatorType<binary_t> alloc; + std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary); + std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } m_type = value_t::null; assert_invariant(); @@ -14853,92 +19542,45 @@ class basic_json break; } + case value_t::null: + case value_t::discarded: default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } return result; } - /*! - @brief remove element from a JSON object given a key - - Removes elements from a JSON object with the key value @a key. - - @param[in] key value of the elements to remove - - @return Number of elements removed. If @a ObjectType is the default - `std::map` type, the return value will always be `0` (@a key was not - found) or `1` (@a key was found). - - @post References and iterators to the erased elements are invalidated. - Other references and iterators are not affected. - - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` - - @complexity `log(size()) + count(key)` - - @liveexample{The example shows the effect of `erase()`.,erase__key_type} - - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ size_type erase(const typename object_t::key_type& key) { // this erase only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } - /*! - @brief remove element from a JSON array given an index - - Removes element from a JSON array at the index @a idx. - - @param[in] idx index of the element to remove - - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` - @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 - is out of range"` - - @complexity Linear in distance between @a idx and the end of the container. - - @liveexample{The example shows the effect of `erase()`.,erase__size_type} - - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - - @since version 1.0.0 - */ + /// @brief remove element from a JSON array given an index + /// @sa https://json.nlohmann.me/api/basic_json/erase/ void erase(const size_type idx) { // this erase only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { - if (JSON_UNLIKELY(idx >= size())) + if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); } m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx)); } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } } @@ -14952,28 +19594,8 @@ class basic_json /// @name lookup /// @{ - /*! - @brief find an element in a JSON object - - Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is - returned. - - @note This method always returns @ref end() when executed on a JSON type - that is not an object. - - @param[in] key key value of the element to search for. - - @return Iterator to an element with key equivalent to @a key. If no such - element is found or the JSON value is not an object, past-the-end (see - @ref end()) iterator is returned. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how `find()` is used.,find__key_type} - - @since version 1.0.0 - */ + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ template<typename KeyT> iterator find(KeyT&& key) { @@ -14987,10 +19609,8 @@ class basic_json return result; } - /*! - @brief find an element in a JSON object - @copydoc find(KeyT&&) - */ + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ template<typename KeyT> const_iterator find(KeyT&& key) const { @@ -15004,27 +19624,8 @@ class basic_json return result; } - /*! - @brief returns the number of occurrences of a key in a JSON object - - Returns the number of elements with key @a key. If ObjectType is the - default `std::map` type, the return value will always be `0` (@a key was - not found) or `1` (@a key was found). - - @note This method always returns `0` when executed on a JSON type that is - not an object. - - @param[in] key key value of the element to count - - @return Number of elements with key @a key. If the JSON value is not an - object, the return value will be `0`. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how `count()` is used.,count} - - @since version 1.0.0 - */ + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ template<typename KeyT> size_type count(KeyT&& key) const { @@ -15032,6 +19633,22 @@ class basic_json return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0; } + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + template < typename KeyT, typename std::enable_if < + !std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object given a JSON pointer + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + /// @} @@ -15042,30 +19659,8 @@ class basic_json /// @name iterators /// @{ - /*! - @brief returns an iterator to the first element - - Returns an iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator to the first element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for `begin()`.,begin} - - @sa @ref cbegin() -- returns a const iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end - - @since version 1.0.0 - */ + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ iterator begin() noexcept { iterator result(this); @@ -15073,39 +19668,15 @@ class basic_json return result; } - /*! - @copydoc basic_json::cbegin() - */ + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ const_iterator begin() const noexcept { return cbegin(); } - /*! - @brief returns a const iterator to the first element - - Returns a const iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator to the first element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast<const basic_json&>(*this).begin()`. - - @liveexample{The following code shows an example for `cbegin()`.,cbegin} - - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end - - @since version 1.0.0 - */ + /// @brief returns a const iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/cbegin/ const_iterator cbegin() const noexcept { const_iterator result(this); @@ -15113,30 +19684,8 @@ class basic_json return result; } - /*! - @brief returns an iterator to one past the last element - - Returns an iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator one past the last element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for `end()`.,end} - - @sa @ref cend() -- returns a const iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning - - @since version 1.0.0 - */ + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ iterator end() noexcept { iterator result(this); @@ -15144,39 +19693,15 @@ class basic_json return result; } - /*! - @copydoc basic_json::cend() - */ + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ const_iterator end() const noexcept { return cend(); } - /*! - @brief returns a const iterator to one past the last element - - Returns a const iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator one past the last element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast<const basic_json&>(*this).end()`. - - @liveexample{The following code shows an example for `cend()`.,cend} - - @sa @ref end() -- returns an iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning - - @since version 1.0.0 - */ + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/cend/ const_iterator cend() const noexcept { const_iterator result(this); @@ -15184,270 +19709,80 @@ class basic_json return result; } - /*! - @brief returns an iterator to the reverse-beginning - - Returns an iterator to the reverse-beginning; that is, the last element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(end())`. - - @liveexample{The following code shows an example for `rbegin()`.,rbegin} - - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end - - @since version 1.0.0 - */ + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - /*! - @copydoc basic_json::crbegin() - */ + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ const_reverse_iterator rbegin() const noexcept { return crbegin(); } - /*! - @brief returns an iterator to the reverse-end - - Returns an iterator to the reverse-end; that is, one before the first - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(begin())`. - - @liveexample{The following code shows an example for `rend()`.,rend} - - @sa @ref crend() -- returns a const reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 - */ + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - /*! - @copydoc basic_json::crend() - */ + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ const_reverse_iterator rend() const noexcept { return crend(); } - /*! - @brief returns a const reverse iterator to the last element - - Returns a const iterator to the reverse-beginning; that is, the last - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`. - - @liveexample{The following code shows an example for `crbegin()`.,crbegin} - - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end - - @since version 1.0.0 - */ + /// @brief returns a const reverse iterator to the last element + /// @sa https://json.nlohmann.me/api/basic_json/crbegin/ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } - /*! - @brief returns a const reverse iterator to one before the first - - Returns a const reverse iterator to the reverse-end; that is, one before - the first element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast<const basic_json&>(*this).rend()`. - - @liveexample{The following code shows an example for `crend()`.,crend} - - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 - */ + /// @brief returns a const reverse iterator to one before the first + /// @sa https://json.nlohmann.me/api/basic_json/crend/ const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } public: - /*! - @brief wrapper to access iterator member functions in range-based for - - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. - - For loop without iterator_wrapper: - - @code{cpp} - for (auto it = j_object.begin(); it != j_object.end(); ++it) - { - std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; - } - @endcode - - Range-based for loop without iterator proxy: - - @code{cpp} - for (auto it : j_object) - { - // "it" is of type json::reference and has no key() member - std::cout << "value: " << it << '\n'; - } - @endcode - - Range-based for loop with iterator proxy: - - @code{cpp} - for (auto it : json::iterator_wrapper(j_object)) - { - std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; - } - @endcode - - @note When iterating over an array, `key()` will return the index of the - element as string (see example). - - @param[in] ref reference to a JSON value - @return iteration proxy object wrapping @a ref with an interface to use in - range-based for loops - - @liveexample{The following code shows how the wrapper is used,iterator_wrapper} - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @note The name of this function is not yet final and may change in the - future. - - @deprecated This stream operator is deprecated and will be removed in - future 4.0.0 of the library. Please use @ref items() instead; - that is, replace `json::iterator_wrapper(j)` with `j.items()`. - */ - JSON_DEPRECATED + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept { return ref.items(); } - /*! - @copydoc iterator_wrapper(reference) - */ - JSON_DEPRECATED + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept { return ref.items(); } - /*! - @brief helper to access iterator member functions in range-based for - - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. - - For loop without `items()` function: - - @code{cpp} - for (auto it = j_object.begin(); it != j_object.end(); ++it) - { - std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; - } - @endcode - - Range-based for loop without `items()` function: - - @code{cpp} - for (auto it : j_object) - { - // "it" is of type json::reference and has no key() member - std::cout << "value: " << it << '\n'; - } - @endcode - - Range-based for loop with `items()` function: - - @code{cpp} - for (auto it : j_object.items()) - { - std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; - } - @endcode - - @note When iterating over an array, `key()` will return the index of the - element as string (see example). For primitive types (e.g., numbers), - `key()` returns an empty string. - - @return iteration proxy object wrapping @a ref with an interface to use in - range-based for loops - - @liveexample{The following code shows how the function is used.,items} - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 3.1.0. - */ + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ iteration_proxy<iterator> items() noexcept { return iteration_proxy<iterator>(*this); } - /*! - @copydoc items() - */ + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ iteration_proxy<const_iterator> items() const noexcept { return iteration_proxy<const_iterator>(*this); @@ -15463,47 +19798,8 @@ class basic_json /// @name capacity /// @{ - /*! - @brief checks whether the container is empty. - - Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `true` - boolean | `false` - string | `false` - number | `false` - object | result of function `object_t::empty()` - array | result of function `array_t::empty()` - - @liveexample{The following code uses `empty()` to check if a JSON - object contains any elements.,empty} - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `empty()` functions have constant - complexity. - - @iterators No changes. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @note This function does not return whether a string stored as JSON value - is empty - it returns whether the JSON container itself is empty which is - false in the case of a string. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `begin() == end()`. - - @sa @ref size() -- returns the number of elements - - @since version 1.0.0 - */ + /// @brief checks whether the container is empty. + /// @sa https://json.nlohmann.me/api/basic_json/empty/ bool empty() const noexcept { switch (m_type) @@ -15526,6 +19822,13 @@ class basic_json return m_value.object->empty(); } + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: default: { // all other types are nonempty @@ -15534,48 +19837,8 @@ class basic_json } } - /*! - @brief returns the number of elements - - Returns the number of elements in a JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` - boolean | `1` - string | `1` - number | `1` - object | result of function object_t::size() - array | result of function array_t::size() - - @liveexample{The following code calls `size()` on the different value - types.,size} - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their size() functions have constant - complexity. - - @iterators No changes. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @note This function does not return the length of a string stored as JSON - value - it returns the number of elements in the JSON value which is 1 in - the case of a string. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `std::distance(begin(), end())`. - - @sa @ref empty() -- checks whether the container is empty - @sa @ref max_size() -- returns the maximal number of elements - - @since version 1.0.0 - */ + /// @brief returns the number of elements + /// @sa https://json.nlohmann.me/api/basic_json/size/ size_type size() const noexcept { switch (m_type) @@ -15598,6 +19861,13 @@ class basic_json return m_value.object->size(); } + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: default: { // all other types have size 1 @@ -15606,46 +19876,8 @@ class basic_json } } - /*! - @brief returns the maximum possible number of elements - - Returns the maximum number of elements a JSON value is able to hold due to - system or library implementation limitations, i.e. `std::distance(begin(), - end())` for the JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` (same as `size()`) - boolean | `1` (same as `size()`) - string | `1` (same as `size()`) - number | `1` (same as `size()`) - object | result of function `object_t::max_size()` - array | result of function `array_t::max_size()` - - @liveexample{The following code calls `max_size()` on the different value - types. Note the output is implementation specific.,max_size} - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `max_size()` functions have constant - complexity. - - @iterators No changes. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of returning `b.size()` where `b` is the largest - possible JSON value. - - @sa @ref size() -- returns the number of elements - - @since version 1.0.0 - */ + /// @brief returns the maximum possible number of elements + /// @sa https://json.nlohmann.me/api/basic_json/max_size/ size_type max_size() const noexcept { switch (m_type) @@ -15662,6 +19894,14 @@ class basic_json return m_value.object->max_size(); } + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: default: { // all other types have max_size() == size() @@ -15680,42 +19920,8 @@ class basic_json /// @name modifiers /// @{ - /*! - @brief clears the contents - - Clears the content of a JSON value and resets it to the default value as - if @ref basic_json(value_t) would have been called with the current value - type from @ref type(): - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @post Has the same effect as calling - @code {.cpp} - *this = basic_json(type()); - @endcode - - @liveexample{The example below shows the effect of `clear()` to different - JSON types.,clear} - - @complexity Linear in the size of the JSON value. - - @iterators All iterators, pointers and references related to this container - are invalidated. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @sa @ref basic_json(value_t) -- constructor that creates an object with the - same value than calling `clear()` - - @since version 1.0.0 - */ + /// @brief clears the contents + /// @sa https://json.nlohmann.me/api/basic_json/clear/ void clear() noexcept { switch (m_type) @@ -15750,6 +19956,12 @@ class basic_json break; } + case value_t::binary: + { + m_value.binary->clear(); + break; + } + case value_t::array: { m_value.array->clear(); @@ -15762,37 +19974,21 @@ class basic_json break; } + case value_t::null: + case value_t::discarded: default: break; } } - /*! - @brief add an object to an array - - Appends the given element @a val to the end of the JSON value. If the - function is called on a JSON null value, an empty array is created before - appending @a val. - - @param[in] val the value to add to the JSON array - - @throw type_error.308 when called on a type other than JSON array or - null; example: `"cannot use push_back() with number"` - - @complexity Amortized constant. - - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON array. Note how the `null` value was silently - converted to a JSON array.,push_back} - - @since version 1.0.0 - */ + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(basic_json&& val) { // push_back only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_array()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an array @@ -15804,31 +20000,28 @@ class basic_json } // add element to array (move semantics) + const auto old_capacity = m_value.array->capacity(); m_value.array->push_back(std::move(val)); - // invalidate object - val.m_type = value_t::null; + set_parent(m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor } - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ reference operator+=(basic_json&& val) { push_back(std::move(val)); return *this; } - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(const basic_json& val) { // push_back only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_array()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an array @@ -15840,45 +20033,27 @@ class basic_json } // add element to array + const auto old_capacity = m_value.array->capacity(); m_value.array->push_back(val); + set_parent(m_value.array->back(), old_capacity); } - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ reference operator+=(const basic_json& val) { push_back(val); return *this; } - /*! - @brief add an object to an object - - Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting - @a val. - - @param[in] val the value to add to the JSON object - - @throw type_error.308 when called on a type other than JSON object or - null; example: `"cannot use push_back() with number"` - - @complexity Logarithmic in the size of the container, O(log(`size()`)). - - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON object. Note how the `null` value was silently - converted to a JSON object.,push_back__object_t__value} - - @since version 1.0.0 - */ + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(const typename object_t::value_type& val) { // push_back only works for null objects or objects - if (JSON_UNLIKELY(not(is_null() or is_object()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); } // transform null object into an object @@ -15889,48 +20064,24 @@ class basic_json assert_invariant(); } - // add element to array - m_value.object->insert(val); + // add element to object + auto res = m_value.object->insert(val); + set_parent(res.first->second); } - /*! - @brief add an object to an object - @copydoc push_back(const typename object_t::value_type&) - */ + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ reference operator+=(const typename object_t::value_type& val) { push_back(val); return *this; } - /*! - @brief add an object to an object - - This function allows to use `push_back` with an initializer list. In case - - 1. the current value is an object, - 2. the initializer list @a init contains only two elements, and - 3. the first element of @a init is a string, - - @a init is converted into an object element and added using - @ref push_back(const typename object_t::value_type&). Otherwise, @a init - is converted to a JSON value and added using @ref push_back(basic_json&&). - - @param[in] init an initializer list - - @complexity Linear in the size of the initializer list @a init. - - @note This function is required to resolve an ambiguous overload error, - because pairs like `{"key", "value"}` can be both interpreted as - `object_t::value_type` or `std::initializer_list<basic_json>`, see - https://github.com/nlohmann/json/issues/235 for more information. - - @liveexample{The example shows how initializer lists are treated as - objects when possible.,push_back__initializer_list} - */ + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ void push_back(initializer_list_t init) { - if (is_object() and init.size() == 2 and (*init.begin())->is_string()) + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) { basic_json&& key = init.begin()->moved_or_copied(); push_back(typename object_t::value_type( @@ -15942,44 +20093,23 @@ class basic_json } } - /*! - @brief add an object to an object - @copydoc push_back(initializer_list_t) - */ + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ reference operator+=(initializer_list_t init) { push_back(init); return *this; } - /*! - @brief add an object to an array - - Creates a JSON value from the passed parameters @a args to the end of the - JSON value. If the function is called on a JSON null value, an empty array - is created before appending the value created from @a args. - - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object - - @throw type_error.311 when called on a type other than JSON array or - null; example: `"cannot use emplace_back() with number"` - - @complexity Amortized constant. - - @liveexample{The example shows how `push_back()` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,emplace_back} - - @since version 2.0.8 - */ + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/ template<class... Args> - void emplace_back(Args&& ... args) + reference emplace_back(Args&& ... args) { // emplace_back only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_array()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); } // transform null object into an array @@ -15991,43 +20121,20 @@ class basic_json } // add element to array (perfect forwarding) + const auto old_capacity = m_value.array->capacity(); m_value.array->emplace_back(std::forward<Args>(args)...); + return set_parent(m_value.array->back(), old_capacity); } - /*! - @brief add an object to an object if key does not exist - - Inserts a new element into a JSON object constructed in-place with the - given @a args if there is no element with the key in the container. If the - function is called on a JSON null value, an empty object is created before - appending the value created from @a args. - - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object - - @return a pair consisting of an iterator to the inserted element, or the - already-existing element if no insertion happened, and a bool - denoting whether the insertion took place. - - @throw type_error.311 when called on a type other than JSON object or - null; example: `"cannot use emplace() with number"` - - @complexity Logarithmic in the size of the container, O(log(`size()`)). - - @liveexample{The example shows how `emplace()` can be used to add elements - to a JSON object. Note how the `null` value was silently converted to a - JSON object. Further note how no value is added if there was already one - value stored with the same key.,emplace} - - @since version 2.0.8 - */ + /// @brief add an object to an object if key does not exist + /// @sa https://json.nlohmann.me/api/basic_json/emplace/ template<class... Args> std::pair<iterator, bool> emplace(Args&& ... args) { // emplace only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_object()))) + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); } // transform null object into an object @@ -16040,6 +20147,8 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward<Args>(args)...); + set_parent(res.first->second); + // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -16055,7 +20164,7 @@ class basic_json iterator insert_iterator(const_iterator pos, Args&& ... args) { iterator result(this); - assert(m_value.array != nullptr); + JSON_ASSERT(m_value.array != nullptr); auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...); @@ -16065,318 +20174,143 @@ class basic_json // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + set_parents(); return result; } - /*! - @brief inserts element - - Inserts element @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. - - @throw type_error.309 if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - - @complexity Constant plus linear in the distance between @a pos and end of - the container. - - @liveexample{The example shows how `insert()` is used.,insert} - - @since version 1.0.0 - */ + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ iterator insert(const_iterator pos, const basic_json& val) { // insert only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator return insert_iterator(pos, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } - /*! - @brief inserts element - @copydoc insert(const_iterator, const basic_json&) - */ + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ iterator insert(const_iterator pos, basic_json&& val) { return insert(pos, val); } - /*! - @brief inserts elements - - Inserts @a cnt copies of @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] cnt number of copies of @a val to insert - @param[in] val element to insert - @return iterator pointing to the first element inserted, or @a pos if - `cnt==0` - - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - - @complexity Linear in @a cnt plus linear in the distance between @a pos - and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__count} - - @since version 1.0.0 - */ + /// @brief inserts copies of element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ iterator insert(const_iterator pos, size_type cnt, const basic_json& val) { // insert only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator return insert_iterator(pos, cnt, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } - /*! - @brief inserts elements - - Inserts elements from range `[first, last)` before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - @throw invalid_iterator.211 if @a first or @a last are iterators into - container for which insert is called; example: `"passed iterators may not - belong to container"` - - @return iterator pointing to the first element inserted, or @a pos if - `first==last` - - @complexity Linear in `std::distance(first, last)` plus linear in the - distance between @a pos and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__range} - - @since version 1.0.0 - */ + /// @brief inserts range of elements into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ iterator insert(const_iterator pos, const_iterator first, const_iterator last) { // insert only works for arrays - if (JSON_UNLIKELY(not is_array())) + if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // check if range iterators belong to the same JSON object - if (JSON_UNLIKELY(first.m_object != last.m_object)) + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } - if (JSON_UNLIKELY(first.m_object == this)) + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); } // insert to array and return iterator return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); } - /*! - @brief inserts elements - - Inserts elements from initializer list @a ilist before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] ilist initializer list to insert the values from - - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - - @return iterator pointing to the first element inserted, or @a pos if - `ilist` is empty - - @complexity Linear in `ilist.size()` plus linear in the distance between - @a pos and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__ilist} - - @since version 1.0.0 - */ + /// @brief inserts elements from initializer list into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ iterator insert(const_iterator pos, initializer_list_t ilist) { // insert only works for arrays - if (JSON_UNLIKELY(not is_array())) + if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); } // insert to array and return iterator return insert_iterator(pos, ilist.begin(), ilist.end()); } - /*! - @brief inserts elements - - Inserts elements from range `[first, last)`. - - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw type_error.309 if called on JSON values other than objects; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if iterator @a first or @a last does does not - point to an object; example: `"iterators first and last must point to - objects"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - - @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number - of elements to insert. - - @liveexample{The example shows how `insert()` is used.,insert__range_object} - - @since version 3.0.0 - */ + /// @brief inserts range of elements into object + /// @sa https://json.nlohmann.me/api/basic_json/insert/ void insert(const_iterator first, const_iterator last) { // insert only works for objects - if (JSON_UNLIKELY(not is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); } // check if range iterators belong to the same JSON object - if (JSON_UNLIKELY(first.m_object != last.m_object)) + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } // passed iterators must belong to objects - if (JSON_UNLIKELY(not first.m_object->is_object())) + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); } - /*! - @brief updates a JSON object from another object, overwriting existing keys - - Inserts all values from JSON object @a j and overwrites existing keys. - - @param[in] j JSON object to read values from - - @throw type_error.312 if called on JSON values other than objects; example: - `"cannot use update() with string"` - - @complexity O(N*log(size() + N)), where N is the number of elements to - insert. - - @liveexample{The example shows how `update()` is used.,update} - - @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update - - @since version 3.0.0 - */ - void update(const_reference j) + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_reference j, bool merge_objects = false) { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create<object_t>(); - assert_invariant(); - } - - if (JSON_UNLIKELY(not is_object())) - { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); - } - if (JSON_UNLIKELY(not j.is_object())) - { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); - } - - for (auto it = j.cbegin(); it != j.cend(); ++it) - { - m_value.object->operator[](it.key()) = it.value(); - } + update(j.begin(), j.end(), merge_objects); } - /*! - @brief updates a JSON object from another object, overwriting existing keys - - Inserts all values from from range `[first, last)` and overwrites existing - keys. - - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw type_error.312 if called on JSON values other than objects; example: - `"cannot use update() with string"` - @throw invalid_iterator.202 if iterator @a first or @a last does does not - point to an object; example: `"iterators first and last must point to - objects"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - - @complexity O(N*log(size() + N)), where N is the number of elements to - insert. - - @liveexample{The example shows how `update()` is used__range.,update} - - @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update - - @since version 3.0.0 - */ - void update(const_iterator first, const_iterator last) + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_iterator first, const_iterator last, bool merge_objects = false) { // implicitly convert null value to an empty object if (is_null()) @@ -16386,155 +20320,142 @@ class basic_json assert_invariant(); } - if (JSON_UNLIKELY(not is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); } // check if range iterators belong to the same JSON object - if (JSON_UNLIKELY(first.m_object != last.m_object)) + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); } // passed iterators must belong to objects - if (JSON_UNLIKELY(not first.m_object->is_object() - or not last.m_object->is_object())) + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object)); } for (auto it = first; it != last; ++it) { + if (merge_objects && it.value().is_object()) + { + auto it2 = m_value.object->find(it.key()); + if (it2 != m_value.object->end()) + { + it2->second.update(it.value(), true); + continue; + } + } m_value.object->operator[](it.key()) = it.value(); +#if JSON_DIAGNOSTICS + m_value.object->operator[](it.key()).m_parent = this; +#endif } } - /*! - @brief exchanges the values - - Exchanges the contents of the JSON value with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other JSON value to exchange the contents with - - @complexity Constant. - - @liveexample{The example below shows how JSON values can be swapped with - `swap()`.,swap__reference} - - @since version 1.0.0 - */ + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ void swap(reference other) noexcept ( - std::is_nothrow_move_constructible<value_t>::value and - std::is_nothrow_move_assignable<value_t>::value and - std::is_nothrow_move_constructible<json_value>::value and + std::is_nothrow_move_constructible<value_t>::value&& + std::is_nothrow_move_assignable<value_t>::value&& + std::is_nothrow_move_constructible<json_value>::value&& std::is_nothrow_move_assignable<json_value>::value ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + + set_parents(); + other.set_parents(); assert_invariant(); } - /*! - @brief exchanges the values + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible<value_t>::value&& + std::is_nothrow_move_assignable<value_t>::value&& + std::is_nothrow_move_constructible<json_value>::value&& + std::is_nothrow_move_assignable<json_value>::value + ) + { + left.swap(right); + } - Exchanges the contents of a JSON array with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other array to exchange the contents with - - @throw type_error.310 when JSON value is not an array; example: `"cannot - use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how arrays can be swapped with - `swap()`.,swap__array_t} - - @since version 1.0.0 - */ - void swap(array_t& other) + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(array_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for arrays - if (JSON_LIKELY(is_array())) + if (JSON_HEDLEY_LIKELY(is_array())) { std::swap(*(m_value.array), other); } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } - /*! - @brief exchanges the values - - Exchanges the contents of a JSON object with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other object to exchange the contents with - - @throw type_error.310 when JSON value is not an object; example: - `"cannot use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how objects can be swapped with - `swap()`.,swap__object_t} - - @since version 1.0.0 - */ - void swap(object_t& other) + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(object_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for objects - if (JSON_LIKELY(is_object())) + if (JSON_HEDLEY_LIKELY(is_object())) { std::swap(*(m_value.object), other); } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } - /*! - @brief exchanges the values - - Exchanges the contents of a JSON string with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other string to exchange the contents with - - @throw type_error.310 when JSON value is not a string; example: `"cannot - use swap() with boolean"` - - @complexity Constant. - - @liveexample{The example below shows how strings can be swapped with - `swap()`.,swap__string_t} - - @since version 1.0.0 - */ - void swap(string_t& other) + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(string_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for strings - if (JSON_LIKELY(is_string())) + if (JSON_HEDLEY_LIKELY(is_string())) { std::swap(*(m_value.string), other); } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); } } @@ -16548,47 +20469,14 @@ class basic_json /// @name lexicographical comparison operators /// @{ - /*! - @brief comparison: equal - - Compares two JSON values for equality according to the following rules: - - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same according to their respective - `operator==`. - - Integer and floating-point numbers are automatically converted before - comparison. Note than two NaN values are always treated as unequal. - - Two JSON null values are equal. - - @note Floating-point inside JSON values numbers are compared with - `json::number_float_t::operator==` which is `double::operator==` by - default. To compare floating-point while respecting an epsilon, an alternative - [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39) - could be used, for instance - @code {.cpp} - template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type> - inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept - { - return std::abs(a - b) <= epsilon; - } - @endcode - - @note NaN values never compare equal to themselves or to other NaN values. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are equal - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__equal} - - @since version 1.0.0 - */ + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ friend bool operator==(const_reference lhs, const_reference rhs) noexcept { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif const auto lhs_type = lhs.type(); const auto rhs_type = rhs.type(); @@ -16597,154 +20485,113 @@ class basic_json switch (lhs_type) { case value_t::array: - return (*lhs.m_value.array == *rhs.m_value.array); + return *lhs.m_value.array == *rhs.m_value.array; case value_t::object: - return (*lhs.m_value.object == *rhs.m_value.object); + return *lhs.m_value.object == *rhs.m_value.object; case value_t::null: return true; case value_t::string: - return (*lhs.m_value.string == *rhs.m_value.string); + return *lhs.m_value.string == *rhs.m_value.string; case value_t::boolean: - return (lhs.m_value.boolean == rhs.m_value.boolean); + return lhs.m_value.boolean == rhs.m_value.boolean; case value_t::number_integer: - return (lhs.m_value.number_integer == rhs.m_value.number_integer); + return lhs.m_value.number_integer == rhs.m_value.number_integer; case value_t::number_unsigned: - return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned); + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; case value_t::number_float: - return (lhs.m_value.number_float == rhs.m_value.number_float); + return lhs.m_value.number_float == rhs.m_value.number_float; + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + case value_t::discarded: default: return false; } } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) { - return (static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float); + return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) { - return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer)); + return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) { - return (static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float); + return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) { - return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned)); + return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) { - return (static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer); + return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) { - return (lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned)); + return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned); } return false; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif } - /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) - */ + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept { - return (lhs == basic_json(rhs)); + return lhs == basic_json(rhs); } - /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) - */ + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept { - return (basic_json(lhs) == rhs); + return basic_json(lhs) == rhs; } - /*! - @brief comparison: not equal - - Compares two JSON values for inequality by calculating `not (lhs == rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are not equal - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__notequal} - - @since version 1.0.0 - */ + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { - return not (lhs == rhs); + return !(lhs == rhs); } - /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) - */ + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept { - return (lhs != basic_json(rhs)); + return lhs != basic_json(rhs); } - /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) - */ + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept { - return (basic_json(lhs) != rhs); + return basic_json(lhs) != rhs; } - /*! - @brief comparison: less than - - Compares whether one JSON value @a lhs is less than another JSON value @a - rhs according to the following rules: - - If @a lhs and @a rhs have the same type, the values are compared using - the default `<` operator. - - Integer and floating-point numbers are automatically converted before - comparison - - In case @a lhs and @a rhs have different types, the values are ignored - and the order of the types is considered, see - @ref operator<(const value_t, const value_t). - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__less} - - @since version 1.0.0 - */ + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ friend bool operator<(const_reference lhs, const_reference rhs) noexcept { const auto lhs_type = lhs.type(); @@ -16755,54 +20602,60 @@ class basic_json switch (lhs_type) { case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 return (*lhs.m_value.array) < (*rhs.m_value.array); case value_t::object: - return *lhs.m_value.object < *rhs.m_value.object; + return (*lhs.m_value.object) < (*rhs.m_value.object); case value_t::null: return false; case value_t::string: - return *lhs.m_value.string < *rhs.m_value.string; + return (*lhs.m_value.string) < (*rhs.m_value.string); case value_t::boolean: - return lhs.m_value.boolean < rhs.m_value.boolean; + return (lhs.m_value.boolean) < (rhs.m_value.boolean); case value_t::number_integer: - return lhs.m_value.number_integer < rhs.m_value.number_integer; + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); case value_t::number_unsigned: - return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); case value_t::number_float: - return lhs.m_value.number_float < rhs.m_value.number_float; + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + case value_t::discarded: default: return false; } } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) { return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) { return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) { return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) { return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) { return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; } @@ -16813,164 +20666,97 @@ class basic_json return operator<(lhs_type, rhs_type); } - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept { - return (lhs < basic_json(rhs)); + return lhs < basic_json(rhs); } - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept { - return (basic_json(lhs) < rhs); + return basic_json(lhs) < rhs; } - /*! - @brief comparison: less than or equal - - Compares whether one JSON value @a lhs is less than or equal to another - JSON value by calculating `not (rhs < lhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than or equal to @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greater} - - @since version 1.0.0 - */ + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { - return not (rhs < lhs); + return !(rhs < lhs); } - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept { - return (lhs <= basic_json(rhs)); + return lhs <= basic_json(rhs); } - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept { - return (basic_json(lhs) <= rhs); + return basic_json(lhs) <= rhs; } - /*! - @brief comparison: greater than - - Compares whether one JSON value @a lhs is greater than another - JSON value by calculating `not (lhs <= rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than to @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__lessequal} - - @since version 1.0.0 - */ + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ friend bool operator>(const_reference lhs, const_reference rhs) noexcept { - return not (lhs <= rhs); + return !(lhs <= rhs); } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept { - return (lhs > basic_json(rhs)); + return lhs > basic_json(rhs); } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept { - return (basic_json(lhs) > rhs); + return basic_json(lhs) > rhs; } - /*! - @brief comparison: greater than or equal - - Compares whether one JSON value @a lhs is greater than or equal to another - JSON value by calculating `not (lhs < rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than or equal to @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greaterequal} - - @since version 1.0.0 - */ + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { - return not (lhs < rhs); + return !(lhs < rhs); } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept { - return (lhs >= basic_json(rhs)); + return lhs >= basic_json(rhs); } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template<typename ScalarType, typename std::enable_if< std::is_scalar<ScalarType>::value, int>::type = 0> - friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept { - return (basic_json(lhs) >= rhs); + return basic_json(lhs) >= rhs; } /// @} @@ -16981,43 +20767,14 @@ class basic_json /// @name serialization /// @{ - - /*! - @brief serialize to stream - - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. - - - The indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. - - - The indentation character can be controlled with the member variable - `fill` of the output stream @a o. For instance, the manipulator - `std::setfill('\\t')` sets indentation to use a tab character rather than - the default space character. - - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize - - @return the stream @a o - - @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded - - @complexity Linear. - - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} - - @since version 1.0.0; indentation character added in version 3.0.0 - */ +#ifndef JSON_NO_IO + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { // read width member and use it as indentation parameter if nonzero - const bool pretty_print = (o.width() > 0); - const auto indentation = (pretty_print ? o.width() : 0); + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; // reset width to 0 for subsequent calls to this stream o.width(0); @@ -17028,20 +20785,18 @@ class basic_json return o; } - /*! - @brief serialize to stream - @deprecated This stream operator is deprecated and will be removed in - future 4.0.0 of the library. Please use - @ref operator<<(std::ostream&, const basic_json&) - instead; that is, replace calls like `j >> o;` with `o << j;`. - @since version 1.0.0; deprecated since version 3.0.0 - */ - JSON_DEPRECATED + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + /// @deprecated This function is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator<<(std::ostream&, const basic_json&) instead; that is, + /// replace calls like `j >> o;` with `o << j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { return o << j; } - +#endif // JSON_NO_IO /// @} @@ -17052,337 +20807,181 @@ class basic_json /// @name deserialization /// @{ - /*! - @brief deserialize from a compatible input - - This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - container with contiguous storage of 1-byte values. Compatible container - types include `std::vector`, `std::string`, `std::array`, - `std::valarray`, and `std::initializer_list`. Furthermore, C-style - arrays can be used with `std::begin()`/`std::end()`. User-defined - containers can be used as long as they implement random-access iterators - and a contiguous storage. - - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @param[in] i input to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from an array.,parse__array__parser_callback_t} - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__istream__parser_callback_t} - - @liveexample{The example below demonstrates the `parse()` function reading - from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - - @since version 2.0.3 (contiguous containers) - */ - static basic_json parse(detail::input_adapter&& i, + /// @brief deserialize from a compatible input + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template<typename InputType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(i, cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } - static bool accept(detail::input_adapter&& i) + /// @brief deserialize from a pair of character iterators + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template<typename IteratorType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) { - return parser(i).accept(true); + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; } - /*! - @brief generate SAX events + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } - The SAX event lister must follow the interface of @ref json_sax. + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template<typename InputType> + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true); + } - This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - container with contiguous storage of 1-byte values. Compatible container - types include `std::vector`, `std::string`, `std::array`, - `std::valarray`, and `std::initializer_list`. Furthermore, C-style - arrays can be used with `std::begin()`/`std::end()`. User-defined - containers can be used as long as they implement random-access iterators - and a contiguous storage. + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template<typename IteratorType> + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @param[in] i input to read from - @param[in,out] sax SAX event listener - @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) - @param[in] strict whether the input has to be consumed completely - - @return return value of the last processed SAX event - - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the SAX consumer @a sax has - a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `sax_parse()` function - reading from string and processing the events with a user-defined SAX - event consumer.,sax_parse} - - @since version 3.2.0 - */ - template <typename SAX> - static bool sax_parse(detail::input_adapter&& i, SAX* sax, + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template <typename InputType, typename SAX> + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { - assert(sax); - switch (format) - { - case input_format_t::json: - return parser(std::move(i)).sax_parse(sax, strict); - default: - return detail::binary_reader<basic_json, SAX>(std::move(i)).sax_parse(format, sax, strict); - } + auto ia = detail::input_adapter(std::forward<InputType>(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); } - /*! - @brief deserialize from an iterator range with contiguous storage - - This function reads from an iterator range of a container with contiguous - storage of 1-byte values. Compatible container types include - `std::vector`, `std::string`, `std::array`, `std::valarray`, and - `std::initializer_list`. Furthermore, C-style arrays can be used with - `std::begin()`/`std::end()`. User-defined containers can be used as long - as they implement random-access iterators and a contiguous storage. - - @pre The iterator range is contiguous. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** - @pre Each element in the range has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with noncompliant iterators and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @tparam IteratorType iterator of container with contiguous storage - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return result of the deserialization - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from an iterator range.,parse__iteratortype__parser_callback_t} - - @since version 2.0.3 - */ - template<class IteratorType, typename std::enable_if< - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0> - static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template<class IteratorType, class SAX> + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) { - basic_json result; - parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result); - return result; + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); } - template<class IteratorType, typename std::enable_if< - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0> - static bool accept(IteratorType first, IteratorType last) + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + /// @deprecated This function is deprecated since 3.8.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// sax_parse(ptr, ptr + len) instead. + template <typename SAX> + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) { - return parser(detail::input_adapter(first, last)).accept(true); + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); } - - template<class IteratorType, class SAX, typename std::enable_if< - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0> - static bool sax_parse(IteratorType first, IteratorType last, SAX* sax) - { - return parser(detail::input_adapter(first, last)).sax_parse(sax); - } - - /*! - @brief deserialize from stream - @deprecated This stream operator is deprecated and will be removed in - version 4.0.0 of the library. Please use - @ref operator>>(std::istream&, basic_json&) - instead; that is, replace calls like `j << i;` with `i >> j;`. - @since version 1.0.0; deprecated since version 3.0.0 - */ - JSON_DEPRECATED +#ifndef JSON_NO_IO + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator>>(std::istream&, basic_json&) instead; that is, + /// replace calls like `j << i;` with `i >> j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) friend std::istream& operator<<(basic_json& j, std::istream& i) { return operator>>(i, j); } - /*! - @brief deserialize from stream - - Deserializes an input stream to a JSON value. - - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} - - @sa parse(std::istream&, const parser_callback_t) for a variant with a - parser callback function to filter values while parsing - - @since version 1.0.0 - */ + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ friend std::istream& operator>>(std::istream& i, basic_json& j) { parser(detail::input_adapter(i)).parse(false, j); return i; } - +#endif // JSON_NO_IO /// @} /////////////////////////// // convenience functions // /////////////////////////// - /*! - @brief return the type as string - - Returns the type name as string to be used in error messages - usually to - indicate that a function was called on a wrong JSON type. - - @return a string representation of a the @a m_type member: - Value type | return value - ----------- | ------------- - null | `"null"` - boolean | `"boolean"` - string | `"string"` - number | `"number"` (for all number types) - object | `"object"` - array | `"array"` - discarded | `"discarded"` - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @complexity Constant. - - @liveexample{The following code exemplifies `type_name()` for all JSON - types.,type_name} - - @sa @ref type() -- return the type of the JSON value - @sa @ref operator value_t() -- return the type of the JSON value (implicit) - - @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` - since 3.0.0 - */ + /// @brief return the type as string + /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_RETURNS_NON_NULL const char* type_name() const noexcept { + switch (m_type) { - switch (m_type) - { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; - } + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + default: + return "number"; } } - private: + JSON_PRIVATE_UNLESS_TESTED: ////////////////////// // member variables // ////////////////////// @@ -17393,6 +20992,11 @@ class basic_json /// the value of the current element json_value m_value = {}; +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// @@ -17401,616 +21005,322 @@ class basic_json /// @{ public: - /*! - @brief create a CBOR serialization of a given JSON value - - Serializes a given JSON value @a j to a byte vector using the CBOR (Concise - Binary Object Representation) serialization format. CBOR is a binary - serialization format which aims to be more compact than JSON itself, yet - more efficient to parse. - - The library uses the following mapping from JSON values types to - CBOR types according to the CBOR specification (RFC 7049): - - JSON value type | value/range | CBOR type | first byte - --------------- | ------------------------------------------ | ---------------------------------- | --------------- - null | `null` | Null | 0xF6 - boolean | `true` | True | 0xF5 - boolean | `false` | False | 0xF4 - number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B - number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A - number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 - number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 - number_integer | -24..-1 | Negative integer | 0x20..0x37 - number_integer | 0..23 | Integer | 0x00..0x17 - number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 - number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 - number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A - number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B - number_unsigned | 0..23 | Integer | 0x00..0x17 - number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 - number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 - number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A - number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B - number_float | *any value* | Double-Precision Float | 0xFB - string | *length*: 0..23 | UTF-8 string | 0x60..0x77 - string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 - string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 - string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A - string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B - array | *size*: 0..23 | array | 0x80..0x97 - array | *size*: 23..255 | array (1 byte follow) | 0x98 - array | *size*: 256..65535 | array (2 bytes follow) | 0x99 - array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A - array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B - object | *size*: 0..23 | map | 0xA0..0xB7 - object | *size*: 23..255 | map (1 byte follow) | 0xB8 - object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 - object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA - object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a CBOR value. - - @note If NaN or Infinity are stored inside a JSON number, they are - serialized properly. This behavior differs from the @ref dump() - function which serializes NaN or Infinity to `null`. - - @note The following CBOR types are not used in the conversion: - - byte strings (0x40..0x5F) - - UTF-8 strings terminated by "break" (0x7F) - - arrays terminated by "break" (0x9F) - - maps terminated by "break" (0xBF) - - date/time (0xC0..0xC1) - - bignum (0xC2..0xC3) - - decimal fraction (0xC4) - - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - - expected conversions (0xD5..0xD7) - - simple values (0xE0..0xF3, 0xF8) - - undefined (0xF7) - - half and single-precision floats (0xF9-0xFA) - - break (0xFF) - - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector - - @complexity Linear in the size of the JSON value @a j. - - @liveexample{The example shows the serialization of a JSON value to a byte - vector in CBOR format.,to_cbor} - - @sa http://cbor.io - @sa @ref from_cbor(detail::input_adapter, const bool strict) for the - analogous deserialization - @sa @ref to_msgpack(const basic_json&) for the related MessagePack format - @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the - related UBJSON format - - @since version 2.0.9 - */ - static std::vector<uint8_t> to_cbor(const basic_json& j) + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static std::vector<std::uint8_t> to_cbor(const basic_json& j) { - std::vector<uint8_t> result; + std::vector<std::uint8_t> result; to_cbor(j, result); return result; } - static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o) + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o) { - binary_writer<uint8_t>(o).write_cbor(j); + binary_writer<std::uint8_t>(o).write_cbor(j); } + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ static void to_cbor(const basic_json& j, detail::output_adapter<char> o) { binary_writer<char>(o).write_cbor(j); } - /*! - @brief create a MessagePack serialization of a given JSON value - - Serializes a given JSON value @a j to a byte vector using the MessagePack - serialization format. MessagePack is a binary serialization format which - aims to be more compact than JSON itself, yet more efficient to parse. - - The library uses the following mapping from JSON values types to - MessagePack types according to the MessagePack specification: - - JSON value type | value/range | MessagePack type | first byte - --------------- | --------------------------------- | ---------------- | ---------- - null | `null` | nil | 0xC0 - boolean | `true` | true | 0xC3 - boolean | `false` | false | 0xC2 - number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 - number_integer | -2147483648..-32769 | int32 | 0xD2 - number_integer | -32768..-129 | int16 | 0xD1 - number_integer | -128..-33 | int8 | 0xD0 - number_integer | -32..-1 | negative fixint | 0xE0..0xFF - number_integer | 0..127 | positive fixint | 0x00..0x7F - number_integer | 128..255 | uint 8 | 0xCC - number_integer | 256..65535 | uint 16 | 0xCD - number_integer | 65536..4294967295 | uint 32 | 0xCE - number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_unsigned | 0..127 | positive fixint | 0x00..0x7F - number_unsigned | 128..255 | uint 8 | 0xCC - number_unsigned | 256..65535 | uint 16 | 0xCD - number_unsigned | 65536..4294967295 | uint 32 | 0xCE - number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_float | *any value* | float 64 | 0xCB - string | *length*: 0..31 | fixstr | 0xA0..0xBF - string | *length*: 32..255 | str 8 | 0xD9 - string | *length*: 256..65535 | str 16 | 0xDA - string | *length*: 65536..4294967295 | str 32 | 0xDB - array | *size*: 0..15 | fixarray | 0x90..0x9F - array | *size*: 16..65535 | array 16 | 0xDC - array | *size*: 65536..4294967295 | array 32 | 0xDD - object | *size*: 0..15 | fix map | 0x80..0x8F - object | *size*: 16..65535 | map 16 | 0xDE - object | *size*: 65536..4294967295 | map 32 | 0xDF - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a MessagePack value. - - @note The following values can **not** be converted to a MessagePack value: - - strings with more than 4294967295 bytes - - arrays with more than 4294967295 elements - - objects with more than 4294967295 elements - - @note The following MessagePack types are not used in the conversion: - - bin 8 - bin 32 (0xC4..0xC6) - - ext 8 - ext 32 (0xC7..0xC9) - - float 32 (0xCA) - - fixext 1 - fixext 16 (0xD4..0xD8) - - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. - - @note If NaN or Infinity are stored inside a JSON number, they are - serialized properly. This behavior differs from the @ref dump() - function which serializes NaN or Infinity to `null`. - - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector - - @complexity Linear in the size of the JSON value @a j. - - @liveexample{The example shows the serialization of a JSON value to a byte - vector in MessagePack format.,to_msgpack} - - @sa http://msgpack.org - @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the - analogous deserialization - @sa @ref to_cbor(const basic_json& for the related CBOR format - @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the - related UBJSON format - - @since version 2.0.9 - */ - static std::vector<uint8_t> to_msgpack(const basic_json& j) + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static std::vector<std::uint8_t> to_msgpack(const basic_json& j) { - std::vector<uint8_t> result; + std::vector<std::uint8_t> result; to_msgpack(j, result); return result; } - static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o) + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o) { - binary_writer<uint8_t>(o).write_msgpack(j); + binary_writer<std::uint8_t>(o).write_msgpack(j); } + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ static void to_msgpack(const basic_json& j, detail::output_adapter<char> o) { binary_writer<char>(o).write_msgpack(j); } - /*! - @brief create a UBJSON serialization of a given JSON value - - Serializes a given JSON value @a j to a byte vector using the UBJSON - (Universal Binary JSON) serialization format. UBJSON aims to be more compact - than JSON itself, yet more efficient to parse. - - The library uses the following mapping from JSON values types to - UBJSON types according to the UBJSON specification: - - JSON value type | value/range | UBJSON type | marker - --------------- | --------------------------------- | ----------- | ------ - null | `null` | null | `Z` - boolean | `true` | true | `T` - boolean | `false` | false | `F` - number_integer | -9223372036854775808..-2147483649 | int64 | `L` - number_integer | -2147483648..-32769 | int32 | `l` - number_integer | -32768..-129 | int16 | `I` - number_integer | -128..127 | int8 | `i` - number_integer | 128..255 | uint8 | `U` - number_integer | 256..32767 | int16 | `I` - number_integer | 32768..2147483647 | int32 | `l` - number_integer | 2147483648..9223372036854775807 | int64 | `L` - number_unsigned | 0..127 | int8 | `i` - number_unsigned | 128..255 | uint8 | `U` - number_unsigned | 256..32767 | int16 | `I` - number_unsigned | 32768..2147483647 | int32 | `l` - number_unsigned | 2147483648..9223372036854775807 | int64 | `L` - number_float | *any value* | float64 | `D` - string | *with shortest length indicator* | string | `S` - array | *see notes on optimized format* | array | `[` - object | *see notes on optimized format* | map | `{` - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a UBJSON value. - - @note The following values can **not** be converted to a UBJSON value: - - strings with more than 9223372036854775807 bytes (theoretical) - - unsigned integer numbers above 9223372036854775807 - - @note The following markers are not used in the conversion: - - `Z`: no-op values are not created. - - `C`: single-byte strings are serialized with `S` markers. - - @note Any UBJSON output created @ref to_ubjson can be successfully parsed - by @ref from_ubjson. - - @note If NaN or Infinity are stored inside a JSON number, they are - serialized properly. This behavior differs from the @ref dump() - function which serializes NaN or Infinity to `null`. - - @note The optimized formats for containers are supported: Parameter - @a use_size adds size information to the beginning of a container and - removes the closing marker. Parameter @a use_type further checks - whether all elements of a container have the same type and adds the - type marker to the beginning of the container. The @a use_type - parameter must only be used together with @a use_size = true. Note - that @a use_size = true alone may result in larger representations - - the benefit of this parameter is that the receiving side is - immediately informed on the number of elements of the container. - - @param[in] j JSON value to serialize - @param[in] use_size whether to add size annotations to container types - @param[in] use_type whether to add type annotations to container types - (must be combined with @a use_size = true) - @return UBJSON serialization as byte vector - - @complexity Linear in the size of the JSON value @a j. - - @liveexample{The example shows the serialization of a JSON value to a byte - vector in UBJSON format.,to_ubjson} - - @sa http://ubjson.org - @sa @ref from_ubjson(detail::input_adapter, const bool strict) for the - analogous deserialization - @sa @ref to_cbor(const basic_json& for the related CBOR format - @sa @ref to_msgpack(const basic_json&) for the related MessagePack format - - @since version 3.1.0 - */ - static std::vector<uint8_t> to_ubjson(const basic_json& j, - const bool use_size = false, - const bool use_type = false) + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static std::vector<std::uint8_t> to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) { - std::vector<uint8_t> result; + std::vector<std::uint8_t> result; to_ubjson(j, result, use_size, use_type); return result; } - static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o, + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o, const bool use_size = false, const bool use_type = false) { - binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type); + binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type); } + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ static void to_ubjson(const basic_json& j, detail::output_adapter<char> o, const bool use_size = false, const bool use_type = false) { binary_writer<char>(o).write_ubjson(j, use_size, use_type); } - /*! - @brief create a JSON value from an input in CBOR format + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static std::vector<std::uint8_t> to_bson(const basic_json& j) + { + std::vector<std::uint8_t> result; + to_bson(j, result); + return result; + } - Deserializes a given input @a i to a JSON value using the CBOR (Concise - Binary Object Representation) serialization format. + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o) + { + binary_writer<std::uint8_t>(o).write_bson(j); + } - The library maps CBOR types to JSON value types as follows: + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter<char> o) + { + binary_writer<char>(o).write_bson(j); + } - CBOR type | JSON value type | first byte - ---------------------- | --------------- | ---------- - Integer | number_unsigned | 0x00..0x17 - Unsigned integer | number_unsigned | 0x18 - Unsigned integer | number_unsigned | 0x19 - Unsigned integer | number_unsigned | 0x1A - Unsigned integer | number_unsigned | 0x1B - Negative integer | number_integer | 0x20..0x37 - Negative integer | number_integer | 0x38 - Negative integer | number_integer | 0x39 - Negative integer | number_integer | 0x3A - Negative integer | number_integer | 0x3B - Negative integer | number_integer | 0x40..0x57 - UTF-8 string | string | 0x60..0x77 - UTF-8 string | string | 0x78 - UTF-8 string | string | 0x79 - UTF-8 string | string | 0x7A - UTF-8 string | string | 0x7B - UTF-8 string | string | 0x7F - array | array | 0x80..0x97 - array | array | 0x98 - array | array | 0x99 - array | array | 0x9A - array | array | 0x9B - array | array | 0x9F - map | object | 0xA0..0xB7 - map | object | 0xB8 - map | object | 0xB9 - map | object | 0xBA - map | object | 0xBB - map | object | 0xBF - False | `false` | 0xF4 - True | `true` | 0xF5 - Nill | `null` | 0xF6 - Half-Precision Float | number_float | 0xF9 - Single-Precision Float | number_float | 0xFA - Double-Precision Float | number_float | 0xFB - - @warning The mapping is **incomplete** in the sense that not all CBOR - types can be converted to a JSON value. The following CBOR types - are not supported and will yield parse errors (parse_error.112): - - byte strings (0x40..0x5F) - - date/time (0xC0..0xC1) - - bignum (0xC2..0xC3) - - decimal fraction (0xC4) - - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - - expected conversions (0xD5..0xD7) - - simple values (0xE0..0xF3, 0xF8) - - undefined (0xF7) - - @warning CBOR allows map keys of any type, whereas JSON only allows - strings as keys in object values. Therefore, CBOR maps with keys - other than UTF-8 strings are rejected (parse_error.113). - - @note Any CBOR output created @ref to_cbor can be successfully parsed by - @ref from_cbor. - - @param[in] i an input in CBOR format convertible to an input adapter - @param[in] strict whether to expect the input to be consumed until EOF - (true by default) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value - - @throw parse_error.110 if the given input ends prematurely or the end of - file was not reached when @a strict was set to true - @throw parse_error.112 if unsupported features from CBOR were - used in the given input @a v or if the input is not valid CBOR - @throw parse_error.113 if a string was expected as map key, but not found - - @complexity Linear in the size of the input @a i. - - @liveexample{The example shows the deserialization of a byte vector in CBOR - format to a JSON value.,from_cbor} - - @sa http://cbor.io - @sa @ref to_cbor(const basic_json&) for the analogous serialization - @sa @ref from_msgpack(detail::input_adapter, const bool, const bool) for the - related MessagePack format - @sa @ref from_ubjson(detail::input_adapter, const bool, const bool) for the - related UBJSON format - - @since version 2.0.9; parameter @a start_index since 2.1.1; changed to - consume input adapters, removed start_index parameter, and added - @a strict parameter since 3.0.0; added @allow_exceptions parameter - since 3.2.0 - */ - static basic_json from_cbor(detail::input_adapter&& i, + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template<typename InputType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, const bool strict = true, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = detail::input_adapter(std::forward<InputType>(i)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } - /*! - @copydoc from_cbor(detail::input_adapter, const bool, const bool) - */ - template<typename A1, typename A2, - detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0> - static basic_json from_cbor(A1 && a1, A2 && a2, + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template<typename IteratorType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, const bool strict = true, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } - /*! - @brief create a JSON value from an input in MessagePack format + template<typename T> + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } - Deserializes a given input @a i to a JSON value using the MessagePack - serialization format. - The library maps MessagePack types to JSON value types as follows: + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } - MessagePack type | JSON value type | first byte - ---------------- | --------------- | ---------- - positive fixint | number_unsigned | 0x00..0x7F - fixmap | object | 0x80..0x8F - fixarray | array | 0x90..0x9F - fixstr | string | 0xA0..0xBF - nil | `null` | 0xC0 - false | `false` | 0xC2 - true | `true` | 0xC3 - float 32 | number_float | 0xCA - float 64 | number_float | 0xCB - uint 8 | number_unsigned | 0xCC - uint 16 | number_unsigned | 0xCD - uint 32 | number_unsigned | 0xCE - uint 64 | number_unsigned | 0xCF - int 8 | number_integer | 0xD0 - int 16 | number_integer | 0xD1 - int 32 | number_integer | 0xD2 - int 64 | number_integer | 0xD3 - str 8 | string | 0xD9 - str 16 | string | 0xDA - str 32 | string | 0xDB - array 16 | array | 0xDC - array 32 | array | 0xDD - map 16 | object | 0xDE - map 32 | object | 0xDF - negative fixint | number_integer | 0xE0-0xFF - - @warning The mapping is **incomplete** in the sense that not all - MessagePack types can be converted to a JSON value. The following - MessagePack types are not supported and will yield parse errors: - - bin 8 - bin 32 (0xC4..0xC6) - - ext 8 - ext 32 (0xC7..0xC9) - - fixext 1 - fixext 16 (0xD4..0xD8) - - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. - - @param[in] i an input in MessagePack format convertible to an input - adapter - @param[in] strict whether to expect the input to be consumed until EOF - (true by default) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value - - @throw parse_error.110 if the given input ends prematurely or the end of - file was not reached when @a strict was set to true - @throw parse_error.112 if unsupported features from MessagePack were - used in the given input @a i or if the input is not valid MessagePack - @throw parse_error.113 if a string was expected as map key, but not found - - @complexity Linear in the size of the input @a i. - - @liveexample{The example shows the deserialization of a byte vector in - MessagePack format to a JSON value.,from_msgpack} - - @sa http://msgpack.org - @sa @ref to_msgpack(const basic_json&) for the analogous serialization - @sa @ref from_cbor(detail::input_adapter, const bool, const bool) for the - related CBOR format - @sa @ref from_ubjson(detail::input_adapter, const bool, const bool) for - the related UBJSON format - - @since version 2.0.9; parameter @a start_index since 2.1.1; changed to - consume input adapters, removed start_index parameter, and added - @a strict parameter since 3.0.0; added @allow_exceptions parameter - since 3.2.0 - */ - static basic_json from_msgpack(detail::input_adapter&& i, + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template<typename InputType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = detail::input_adapter(std::forward<InputType>(i)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } - /*! - @copydoc from_msgpack(detail::input_adapter, const bool, const bool) - */ - template<typename A1, typename A2, - detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0> - static basic_json from_msgpack(A1 && a1, A2 && a2, + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template<typename IteratorType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } - /*! - @brief create a JSON value from an input in UBJSON format + template<typename T> + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } - Deserializes a given input @a i to a JSON value using the UBJSON (Universal - Binary JSON) serialization format. + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } - The library maps UBJSON types to JSON value types as follows: - - UBJSON type | JSON value type | marker - ----------- | --------------------------------------- | ------ - no-op | *no value, next value is read* | `N` - null | `null` | `Z` - false | `false` | `F` - true | `true` | `T` - float32 | number_float | `d` - float64 | number_float | `D` - uint8 | number_unsigned | `U` - int8 | number_integer | `i` - int16 | number_integer | `I` - int32 | number_integer | `l` - int64 | number_integer | `L` - string | string | `S` - char | string | `C` - array | array (optimized values are supported) | `[` - object | object (optimized values are supported) | `{` - - @note The mapping is **complete** in the sense that any UBJSON value can - be converted to a JSON value. - - @param[in] i an input in UBJSON format convertible to an input adapter - @param[in] strict whether to expect the input to be consumed until EOF - (true by default) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value - - @throw parse_error.110 if the given input ends prematurely or the end of - file was not reached when @a strict was set to true - @throw parse_error.112 if a parse error occurs - @throw parse_error.113 if a string could not be parsed successfully - - @complexity Linear in the size of the input @a i. - - @liveexample{The example shows the deserialization of a byte vector in - UBJSON format to a JSON value.,from_ubjson} - - @sa http://ubjson.org - @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the - analogous serialization - @sa @ref from_cbor(detail::input_adapter, const bool, const bool) for the - related CBOR format - @sa @ref from_msgpack(detail::input_adapter, const bool, const bool) for - the related MessagePack format - - @since version 3.1.0; added @allow_exceptions parameter since 3.2.0 - */ - static basic_json from_ubjson(detail::input_adapter&& i, + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template<typename InputType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = detail::input_adapter(std::forward<InputType>(i)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } - /*! - @copydoc from_ubjson(detail::input_adapter, const bool, const bool) - */ - template<typename A1, typename A2, - detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0> - static basic_json from_ubjson(A1 && a1, A2 && a2, + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template<typename IteratorType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template<typename T> + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template<typename InputType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward<InputType>(i)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template<typename IteratorType> + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template<typename T> + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } /// @} ////////////////////////// @@ -18020,180 +21330,36 @@ class basic_json /// @name JSON Pointer functions /// @{ - /*! - @brief access specified element via JSON Pointer - - Uses a JSON pointer to retrieve a reference to the respective JSON value. - No bound checking is performed. Similar to @ref operator[](const typename - object_t::key_type&), `null` values are created in arrays and objects if - necessary. - - In particular: - - If the JSON pointer points to an object key that does not exist, it - is created an filled with a `null` value before a reference to it - is returned. - - If the JSON pointer points to an array index that does not exist, it - is created an filled with a `null` value before a reference to it - is returned. All indices between the current maximum and the given - index are also filled with `null`. - - The special value `-` is treated as a synonym for the index past the - end. - - @param[in] ptr a JSON pointer - - @return reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - - @liveexample{The behavior is shown in the example.,operatorjson_pointer} - - @since version 2.0.0 - */ + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ reference operator[](const json_pointer& ptr) { return ptr.get_unchecked(this); } - /*! - @brief access specified element via JSON Pointer - - Uses a JSON pointer to retrieve a reference to the respective JSON value. - No bound checking is performed. The function does not change the JSON - value; no `null` values are created. In particular, the the special value - `-` yields an exception. - - @param[in] ptr JSON pointer to the desired element - - @return const reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - - @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} - - @since version 2.0.0 - */ + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ const_reference operator[](const json_pointer& ptr) const { return ptr.get_unchecked(this); } - /*! - @brief access specified element via JSON Pointer - - Returns a reference to the element at with specified JSON pointer @a ptr, - with bounds checking. - - @param[in] ptr JSON pointer to the desired element - - @return reference to the element pointed to by @a ptr - - @throw parse_error.106 if an array index in the passed JSON pointer @a ptr - begins with '0'. See example below. - - @throw parse_error.109 if an array index in the passed JSON pointer @a ptr - is not a number. See example below. - - @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr - is out of range. See example below. - - @throw out_of_range.402 if the array index '-' is used in the passed JSON - pointer @a ptr. As `at` provides checked access (and no elements are - implicitly inserted), the index '-' is always invalid. See example below. - - @throw out_of_range.403 if the JSON pointer describes a key of an object - which cannot be found. See example below. - - @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. - See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 2.0.0 - - @liveexample{The behavior is shown in the example.,at_json_pointer} - */ + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ reference at(const json_pointer& ptr) { return ptr.get_checked(this); } - /*! - @brief access specified element via JSON Pointer - - Returns a const reference to the element at with specified JSON pointer @a - ptr, with bounds checking. - - @param[in] ptr JSON pointer to the desired element - - @return reference to the element pointed to by @a ptr - - @throw parse_error.106 if an array index in the passed JSON pointer @a ptr - begins with '0'. See example below. - - @throw parse_error.109 if an array index in the passed JSON pointer @a ptr - is not a number. See example below. - - @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr - is out of range. See example below. - - @throw out_of_range.402 if the array index '-' is used in the passed JSON - pointer @a ptr. As `at` provides checked access (and no elements are - implicitly inserted), the index '-' is always invalid. See example below. - - @throw out_of_range.403 if the JSON pointer describes a key of an object - which cannot be found. See example below. - - @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. - See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 2.0.0 - - @liveexample{The behavior is shown in the example.,at_json_pointer_const} - */ + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ const_reference at(const json_pointer& ptr) const { return ptr.get_checked(this); } - /*! - @brief return flattened JSON value - - The function creates a JSON object whose keys are JSON pointers (see [RFC - 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all - primitive. The original JSON value can be restored using the @ref - unflatten() function. - - @return an object that maps JSON pointers to primitive values - - @note Empty objects and arrays are flattened to `null` and will not be - reconstructed correctly by the @ref unflatten() function. - - @complexity Linear in the size the JSON value. - - @liveexample{The following code shows how a JSON object is flattened to an - object whose keys consist of JSON pointers.,flatten} - - @sa @ref unflatten() for the reverse function - - @since version 2.0.0 - */ + /// @brief return flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/flatten/ basic_json flatten() const { basic_json result(value_t::object); @@ -18201,36 +21367,8 @@ class basic_json return result; } - /*! - @brief unflatten a previously flattened JSON value - - The function restores the arbitrary nesting of a JSON value that has been - flattened before using the @ref flatten() function. The JSON value must - meet certain constraints: - 1. The value must be an object. - 2. The keys must be JSON pointers (see - [RFC 6901](https://tools.ietf.org/html/rfc6901)) - 3. The mapped values must be primitive JSON types. - - @return the original JSON from a flattened version - - @note Empty objects and arrays are flattened by @ref flatten() to `null` - values and can not unflattened to their original type. Apart from - this example, for a JSON value `j`, the following is always true: - `j == j.flatten().unflatten()`. - - @complexity Linear in the size the JSON value. - - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - - @liveexample{The following code shows how a flattened JSON object is - unflattened into the original nested JSON object.,unflatten} - - @sa @ref flatten() for the reverse function - - @since version 2.0.0 - */ + /// @brief unflatten a previously flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ basic_json unflatten() const { return json_pointer::unflatten(*this); @@ -18245,53 +21383,8 @@ class basic_json /// @name JSON Patch functions /// @{ - /*! - @brief applies a JSON patch - - [JSON Patch](http://jsonpatch.com) defines a JSON document structure for - expressing a sequence of operations to apply to a JSON) document. With - this function, a JSON Patch is applied to the current JSON value by - executing all operations from the patch. - - @param[in] json_patch JSON patch document - @return patched document - - @note The application of a patch is atomic: Either all operations succeed - and the patched document is returned or an exception is thrown. In - any case, the original value is not changed: the patch is applied - to a copy of the value. - - @throw parse_error.104 if the JSON patch does not consist of an array of - objects - - @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory - attributes are missing); example: `"operation add must have member path"` - - @throw out_of_range.401 if an array index is out of range. - - @throw out_of_range.403 if a JSON pointer inside the patch could not be - resolved successfully in the current JSON value; example: `"key baz not - found"` - - @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", - "move") - - @throw other_error.501 if "test" operation was unsuccessful - - @complexity Linear in the size of the JSON value and the length of the - JSON patch. As usually only a fraction of the JSON value is affected by - the patch, the complexity can usually be neglected. - - @liveexample{The following code shows how a JSON patch is applied to a - value.,patch} - - @sa @ref diff -- create a JSON patch by comparing two JSON values - - @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) - @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) - - @since version 2.0.0 - */ + /// @brief applies a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/patch/ basic_json patch(const basic_json& json_patch) const { // make a working copy to apply the patch to @@ -18334,73 +21427,75 @@ class basic_json const auto operation_add = [&result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it - if (ptr.is_root()) + if (ptr.empty()) { result = val; + return; } - else + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) { - // make sure the top element of the pointer exists - json_pointer top_pointer = ptr.top(); - if (top_pointer != ptr) + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: { - result.at(top_pointer); + // use operator[] to add value + parent[last_path] = val; + break; } - // get reference to parent of JSON pointer ptr - const auto last_path = ptr.pop_back(); - basic_json& parent = result[ptr]; - - switch (parent.m_type) + case value_t::array: { - case value_t::null: - case value_t::object: + if (last_path == "-") { - // use operator[] to add value - parent[last_path] = val; - break; + // special case: append to back + parent.push_back(val); } - - case value_t::array: + else { - if (last_path == "-") + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { - // special case: append to back - parent.push_back(val); + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); } - else - { - const auto idx = json_pointer::array_index(last_path); - if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size())) - { - // avoid undefined behavior - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - else - { - // default case: insert add offset - parent.insert(parent.begin() + static_cast<difference_type>(idx), val); - } - } - break; - } - // LCOV_EXCL_START - default: - { - // if there exists a parent it cannot be primitive - assert(false); + // default case: insert add offset + parent.insert(parent.begin() + static_cast<difference_type>(idx), val); } - // LCOV_EXCL_STOP + break; } + + // if there exists a parent it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } }; // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [&result](json_pointer & ptr) + const auto operation_remove = [this, &result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr - const auto last_path = ptr.pop_back(); + const auto last_path = ptr.back(); + ptr.pop_back(); basic_json& parent = result.at(ptr); // remove child @@ -18408,26 +21503,26 @@ class basic_json { // perform range check auto it = parent.find(last_path); - if (JSON_LIKELY(it != parent.end())) + if (JSON_HEDLEY_LIKELY(it != parent.end())) { parent.erase(it); } else { - JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); } } else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast<size_type>(json_pointer::array_index(last_path))); + parent.erase(json_pointer::array_index(last_path)); } }; // type check: top level value must be an array - if (JSON_UNLIKELY(not json_patch.is_array())) + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); } // iterate and apply the operations @@ -18445,15 +21540,17 @@ class basic_json const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; // check if desired value is present - if (JSON_UNLIKELY(it == val.m_value.object->end())) + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); } // check if result is of type string - if (JSON_UNLIKELY(string_type and not it->second.is_string())) + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); } // no error: return value @@ -18461,14 +21558,14 @@ class basic_json }; // type check: every element of the array must be an object - if (JSON_UNLIKELY(not val.is_object())) + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); } // collect mandatory members - const std::string op = get_value("op", "op", true); - const std::string path = get_value(op, "path", true); + const auto op = get_value("op", "op", true).template get<std::string>(); + const auto path = get_value(op, "path", true).template get<std::string>(); json_pointer ptr(path); switch (get_op(op)) @@ -18494,7 +21591,7 @@ class basic_json case patch_operations::move: { - const std::string from_path = get_value("move", "from", true); + const auto from_path = get_value("move", "from", true).template get<std::string>(); json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -18511,7 +21608,7 @@ class basic_json case patch_operations::copy: { - const std::string from_path = get_value("copy", "from", true); + const auto from_path = get_value("copy", "from", true).template get<std::string>(); const json_pointer from_ptr(from_path); // the "from" location must exist - use at() @@ -18539,19 +21636,20 @@ class basic_json } // throw an exception if test fails - if (JSON_UNLIKELY(not success)) + if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); } break; } case patch_operations::invalid: + default: { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); } } } @@ -18559,39 +21657,9 @@ class basic_json return result; } - /*! - @brief creates a diff as a JSON patch - - Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can - be changed into the value @a target by calling @ref patch function. - - @invariant For two JSON values @a source and @a target, the following code - yields always `true`: - @code {.cpp} - source.patch(diff(source, target)) == target; - @endcode - - @note Currently, only `remove`, `add`, and `replace` operations are - generated. - - @param[in] source JSON value to compare from - @param[in] target JSON value to compare against - @param[in] path helper value to create JSON pointers - - @return a JSON patch to convert the @a source to @a target - - @complexity Linear in the lengths of @a source and @a target. - - @liveexample{The following code shows how a JSON patch is created as a - diff for two JSON values.,diff} - - @sa @ref patch -- apply a JSON patch - @sa @ref merge_patch -- apply a JSON Merge Patch - - @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) - - @since version 2.0.0 - */ + /// @brief creates a diff as a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/diff/ + JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json diff(const basic_json& source, const basic_json& target, const std::string& path = "") { @@ -18611,106 +21679,113 @@ class basic_json { {"op", "replace"}, {"path", path}, {"value", target} }); + return result; } - else + + switch (source.type()) { - switch (source.type()) + case value_t::array: { - case value_t::array: + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) { - // first pass: traverse common elements - std::size_t i = 0; - while (i < source.size() and i < target.size()) - { - // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); - ++i; - } - - // i now reached the end of at least one array - // in a second pass, traverse the remaining elements - - // remove my remaining elements - const auto end_index = static_cast<difference_type>(result.size()); - while (i < source.size()) - { - // add operations in reverse order to avoid invalid - // indices - result.insert(result.begin() + end_index, object( - { - {"op", "remove"}, - {"path", path + "/" + std::to_string(i)} - })); - ++i; - } - - // add other remaining elements - while (i < target.size()) - { - result.push_back( - { - {"op", "add"}, - {"path", path + "/" + std::to_string(i)}, - {"value", target[i]} - }); - ++i; - } - - break; + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; } - case value_t::object: + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast<difference_type>(result.size()); + while (i < source.size()) { - // first pass: traverse this object's elements - for (auto it = source.cbegin(); it != source.cend(); ++it) + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( { - // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); - - if (target.find(it.key()) != target.end()) - { - // recursive call to compare object values at key it - auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); - } - else - { - // found a key that is not in o -> remove it - result.push_back(object( - { - {"op", "remove"}, {"path", path + "/" + key} - })); - } - } - - // second pass: traverse other object's elements - for (auto it = target.cbegin(); it != target.cend(); ++it) - { - if (source.find(it.key()) == source.end()) - { - // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); - result.push_back( - { - {"op", "add"}, {"path", path + "/" + key}, - {"value", it.value()} - }); - } - } - - break; + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; } - default: + // add other remaining elements + while (i < target.size()) { - // both primitive type: replace value result.push_back( { - {"op", "replace"}, {"path", path}, {"value", target} + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} }); - break; + ++i; } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = path + "/" + detail::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = path + "/" + detail::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; } } @@ -18726,57 +21801,17 @@ class basic_json /// @name JSON Merge Patch functions /// @{ - /*! - @brief applies a JSON Merge Patch - - The merge patch format is primarily intended for use with the HTTP PATCH - method as a means of describing a set of modifications to a target - resource's content. This function applies a merge patch to the current - JSON value. - - The function implements the following algorithm from Section 2 of - [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): - - ``` - define MergePatch(Target, Patch): - if Patch is an Object: - if Target is not an Object: - Target = {} // Ignore the contents and set it to an empty Object - for each Name/Value pair in Patch: - if Value is null: - if Name exists in Target: - remove the Name/Value pair from Target - else: - Target[Name] = MergePatch(Target[Name], Value) - return Target - else: - return Patch - ``` - - Thereby, `Target` is the current object; that is, the patch is applied to - the current value. - - @param[in] patch the patch to apply - - @complexity Linear in the lengths of @a patch. - - @liveexample{The following code shows how a JSON Merge Patch is applied to - a JSON document.,merge_patch} - - @sa @ref patch -- apply a JSON patch - @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) - - @since version 3.0.0 - */ - void merge_patch(const basic_json& patch) + /// @brief applies a JSON Merge Patch + /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/ + void merge_patch(const basic_json& apply_patch) { - if (patch.is_object()) + if (apply_patch.is_object()) { - if (not is_object()) + if (!is_object()) { *this = object(); } - for (auto it = patch.begin(); it != patch.end(); ++it) + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) { if (it.value().is_null()) { @@ -18790,44 +21825,44 @@ class basic_json } else { - *this = patch; + *this = apply_patch; } } /// @} }; + +/// @brief user-defined to_string function for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/to_string/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} + } // namespace nlohmann /////////////////////// // nonmember support // /////////////////////// -// specialization of std::swap, and std::hash -namespace std +namespace std // NOLINT(cert-dcl58-cpp) { -/// hash value for JSON objects -template<> -struct hash<nlohmann::json> +/// @brief hash value for JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_hash/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> { - /*! - @brief return a hash value for a JSON object - - @since version 1.0.0 - */ - std::size_t operator()(const nlohmann::json& j) const + std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const { - // a naive hashing via the string representation - const auto& h = hash<nlohmann::json::string_t>(); - return h(j.dump()); + return nlohmann::detail::hash(j); } }; -/// specialization for std::less<value_t> -/// @note: do not remove the space after '<', -/// see https://github.com/nlohmann/json/pull/679 +// specialization for std::less<value_t> template<> -struct less< ::nlohmann::detail::value_t> +struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679 { /*! @brief compare two value_t enum values @@ -18840,53 +21875,34 @@ struct less< ::nlohmann::detail::value_t> } }; -/*! -@brief exchanges the values of two JSON objects +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 -@since version 1.0.0 -*/ -template<> -inline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept( - is_nothrow_move_constructible<nlohmann::json>::value and - is_nothrow_move_assignable<nlohmann::json>::value -) +/// @brief exchanges the values of two JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_swap/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) + is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression) + is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value) { j1.swap(j2); } +#endif + } // namespace std -/*! -@brief user-defined string literal for JSON values - -This operator implements a user-defined string literal for JSON objects. It -can be used by adding `"_json"` to a string literal and returns a JSON object -if no parse error occurred. - -@param[in] s a string representation of a JSON object -@param[in] n the length of string @a s -@return a JSON object - -@since version 1.0.0 -*/ +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ +JSON_HEDLEY_NON_NULL(1) inline nlohmann::json operator "" _json(const char* s, std::size_t n) { return nlohmann::json::parse(s, s + n); } -/*! -@brief user-defined string literal for JSON pointer - -This operator implements a user-defined string literal for JSON Pointers. It -can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer -object if no parse error occurred. - -@param[in] s a string representation of a JSON Pointer -@param[in] n the length of string @a s -@return a JSON pointer object - -@since version 2.0.0 -*/ +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ +JSON_HEDLEY_NON_NULL(1) inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { return nlohmann::json::json_pointer(std::string(s, n)); @@ -18895,26 +21911,181 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std // #include <nlohmann/detail/macro_unscope.hpp> -// restore GCC/clang diagnostic settings -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic pop -#endif +// restore clang diagnostic settings #if defined(__clang__) - #pragma GCC diagnostic pop + #pragma clang diagnostic pop #endif // clean up +#undef JSON_ASSERT #undef JSON_INTERNAL_CATCH #undef JSON_CATCH #undef JSON_THROW #undef JSON_TRY -#undef JSON_LIKELY -#undef JSON_UNLIKELY -#undef JSON_DEPRECATED +#undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 #undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 +#undef JSON_HAS_FILESYSTEM +#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT +#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL + +// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp> -#endif \ No newline at end of file +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_CL_VERSION +#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MCST_LCC_VERSION +#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ From 315ea18be2dd355d9b599a3580a2713e975c9a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <giordano@users.noreply.github.com> Date: Tue, 14 Jun 2022 22:41:05 +0100 Subject: [PATCH 336/399] Update default value of gen-simd-width in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88b922a5..4af52d78 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ If you want to build all the tests at once just use `make tests`. - `--enable-mkl[=<path>]`: use Intel MKL for FFT (and LAPACK if enabled) routines. A UNIX prefix containing the library can be specified (optional). - `--enable-numa`: enable NUMA first touch optimisation - `--enable-simd=<code>`: setup Grid for the SIMD target `<code>` (default: `GEN`). A list of possible SIMD targets is detailed in a section below. -- `--enable-gen-simd-width=<size>`: select the size (in bytes) of the generic SIMD vector type (default: 32 bytes). +- `--enable-gen-simd-width=<size>`: select the size (in bytes) of the generic SIMD vector type (default: 64 bytes). - `--enable-comms=<comm>`: Use `<comm>` for message passing (default: `none`). A list of possible SIMD targets is detailed in a section below. - `--enable-rng={sitmo|ranlux48|mt19937}`: choose the RNG (default: `sitmo `). - `--disable-timers`: disable system dependent high-resolution timers. From 042ab1a052c9caadcfbeba427498b7719082c3db Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@users.noreply.github.com> Date: Mon, 27 Jun 2022 13:21:39 -0400 Subject: [PATCH 337/399] Update GridStd.h --- Grid/GridStd.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Grid/GridStd.h b/Grid/GridStd.h index 28f6bc46..d0a8124a 100644 --- a/Grid/GridStd.h +++ b/Grid/GridStd.h @@ -16,6 +16,7 @@ #include <functional> #include <stdio.h> #include <stdlib.h> +#include <strings.h> #include <stdio.h> #include <signal.h> #include <ctime> From f14e7e51e73abca5352ead21a1f25b8bdcd09be3 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 12 Jul 2022 10:56:22 -0700 Subject: [PATCH 338/399] Grid accelerator --- Grid/threads/Accelerator.cc | 2 ++ Grid/threads/Accelerator.h | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 240758b3..1e07887b 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -195,12 +195,14 @@ void acceleratorInit(void) #ifdef GRID_SYCL cl::sycl::queue *theGridAccelerator; +cl::sycl::queue *theCopyAccelerator; void acceleratorInit(void) { int nDevices = 1; cl::sycl::gpu_selector selector; cl::sycl::device selectedDevice { selector }; theGridAccelerator = new sycl::queue (selectedDevice); + theCopyAccelerator = new sycl::queue (selectedDevice); #ifdef GRID_SYCL_LEVEL_ZERO_IPC zeInit(0); diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 389f2cc4..132c28d2 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -262,6 +262,7 @@ NAMESPACE_END(Grid); NAMESPACE_BEGIN(Grid); extern cl::sycl::queue *theGridAccelerator; +extern cl::sycl::queue *theCopyAccelerator; #ifdef __SYCL_DEVICE_ONLY__ #define GRID_SIMT @@ -298,7 +299,7 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { }); \ }); -#define accelerator_barrier(dummy) theGridAccelerator->wait(); +#define accelerator_barrier(dummy) { printf(" theGridAccelerator::wait()\n"); ; theGridAccelerator->wait(); } inline void *acceleratorAllocShared(size_t bytes){ return malloc_shared(bytes,*theGridAccelerator);}; inline void *acceleratorAllocDevice(size_t bytes){ return malloc_device(bytes,*theGridAccelerator);}; @@ -307,10 +308,10 @@ inline void acceleratorFreeDevice(void *ptr){free(ptr,*theGridAccelerator);}; inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { theGridAccelerator->memcpy(to,from,bytes); } -inline void acceleratorCopySynchronise(void) { theGridAccelerator->wait(); std::cout<<"acceleratorCopySynchronise() wait "<<std::endl; } -inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { theGridAccelerator->memcpy(to,from,bytes); theGridAccelerator->wait();} -inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ theGridAccelerator->memcpy(to,from,bytes); theGridAccelerator->wait();} -inline void acceleratorMemSet(void *base,int value,size_t bytes) { theGridAccelerator->memset(base,value,bytes); theGridAccelerator->wait();} +inline void acceleratorCopySynchronise(void) { theCopyAccelerator->wait(); std::cout<<"acceleratorCopySynchronise() wait "<<std::endl; } +inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} +inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} +inline void acceleratorMemSet(void *base,int value,size_t bytes) { theCopyAccelerator->memset(base,value,bytes); theCopyAccelerator->wait();} inline int acceleratorIsCommunicable(void *ptr) { #if 0 From 5f8892bf03b3579b912e66e07917e4f936dddd7e Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 19 Jul 2022 09:31:51 -0700 Subject: [PATCH 339/399] Mistake pointed out by Camilo --- Grid/threads/Accelerator.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index 132c28d2..a7fd8db7 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -305,13 +305,13 @@ inline void *acceleratorAllocShared(size_t bytes){ return malloc_shared(bytes,*t inline void *acceleratorAllocDevice(size_t bytes){ return malloc_device(bytes,*theGridAccelerator);}; inline void acceleratorFreeShared(void *ptr){free(ptr,*theGridAccelerator);}; inline void acceleratorFreeDevice(void *ptr){free(ptr,*theGridAccelerator);}; -inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { - theGridAccelerator->memcpy(to,from,bytes); -} -inline void acceleratorCopySynchronise(void) { theCopyAccelerator->wait(); std::cout<<"acceleratorCopySynchronise() wait "<<std::endl; } + +inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { theCopyAccelerator->memcpy(to,from,bytes);} +inline void acceleratorCopySynchronise(void) { theCopyAccelerator->wait(); } inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} inline void acceleratorMemSet(void *base,int value,size_t bytes) { theCopyAccelerator->memset(base,value,bytes); theCopyAccelerator->wait();} + inline int acceleratorIsCommunicable(void *ptr) { #if 0 From 2ab1af5754fb3007930ae7cee20395cf1363d61b Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 19 Jul 2022 09:51:06 -0700 Subject: [PATCH 340/399] Ensure no synchronize and not optoin dependent --- Grid/communicator/Communicator_mpi3.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index 7b3e8847..b8ce7bca 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -392,9 +392,9 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques acceleratorCopyDeviceToDeviceAsynch(xmit,shm,bytes); } - if ( CommunicatorPolicy == CommunicatorPolicySequential ) { - this->StencilSendToRecvFromComplete(list,dir); - } + // if ( CommunicatorPolicy == CommunicatorPolicySequential ) { + // this->StencilSendToRecvFromComplete(list,dir); + // } return off_node_bytes; } From d32b923b6c9724f57e8cb0fa27e0b7aa2c753cf0 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 2 Aug 2022 07:58:04 -0700 Subject: [PATCH 341/399] Fencing on a stream in SYCL is needed. Didn't know that ... gulp --- .../implementation/WilsonKernelsImplementation.h | 3 +++ Grid/threads/Accelerator.cc | 3 ++- Grid/threads/Accelerator.h | 11 ++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h index 9228b84c..9f6960af 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h @@ -498,6 +498,7 @@ void WilsonKernels<Impl>::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField #ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDag); return;} #endif + acceleratorFenceComputeStream(); } else if( interior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteDagInt); return;} if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteDagInt); return;} @@ -505,11 +506,13 @@ void WilsonKernels<Impl>::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDagInt); return;} #endif } else if( exterior ) { + acceleratorFenceComputeStream(); if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteDagExt); return;} if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteDagExt); return;} #ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDagExt); return;} #endif + acceleratorFenceComputeStream(); } assert(0 && " Kernel optimisation case not covered "); } diff --git a/Grid/threads/Accelerator.cc b/Grid/threads/Accelerator.cc index 1e07887b..163e4ac4 100644 --- a/Grid/threads/Accelerator.cc +++ b/Grid/threads/Accelerator.cc @@ -202,7 +202,8 @@ void acceleratorInit(void) cl::sycl::gpu_selector selector; cl::sycl::device selectedDevice { selector }; theGridAccelerator = new sycl::queue (selectedDevice); - theCopyAccelerator = new sycl::queue (selectedDevice); + // theCopyAccelerator = new sycl::queue (selectedDevice); + theCopyAccelerator = theGridAccelerator; // Should proceed concurrenlty anyway. #ifdef GRID_SYCL_LEVEL_ZERO_IPC zeInit(0); diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index a7fd8db7..c7366a1f 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -247,7 +247,12 @@ inline int acceleratorIsCommunicable(void *ptr) ////////////////////////////////////////////// // SyCL acceleration ////////////////////////////////////////////// - +#ifdef GRID_SYCL +inline void acceleratorFenceComputeStream(void){ accelerator_barrier();}; +#else +// Ordering within a stream guaranteed on Nvidia & AMD +inline void acceleratorFenceComputeStream(void){ }; +#endif #ifdef GRID_SYCL NAMESPACE_END(Grid); #include <CL/sycl.hpp> @@ -299,15 +304,15 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { }); \ }); -#define accelerator_barrier(dummy) { printf(" theGridAccelerator::wait()\n"); ; theGridAccelerator->wait(); } +#define accelerator_barrier(dummy) { printf(" theGridAccelerator::wait()\n"); theGridAccelerator->wait(); } inline void *acceleratorAllocShared(size_t bytes){ return malloc_shared(bytes,*theGridAccelerator);}; inline void *acceleratorAllocDevice(size_t bytes){ return malloc_device(bytes,*theGridAccelerator);}; inline void acceleratorFreeShared(void *ptr){free(ptr,*theGridAccelerator);}; inline void acceleratorFreeDevice(void *ptr){free(ptr,*theGridAccelerator);}; +inline void acceleratorCopySynchronise(void) { printf(" theCopyAccelerator::wait()\n"); theCopyAccelerator->wait(); } inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { theCopyAccelerator->memcpy(to,from,bytes);} -inline void acceleratorCopySynchronise(void) { theCopyAccelerator->wait(); } inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} inline void acceleratorMemSet(void *base,int value,size_t bytes) { theCopyAccelerator->memset(base,value,bytes); theCopyAccelerator->wait();} From 84110166e420f2a5eddb4ee4bb38a386f087c299 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 2 Aug 2022 08:00:43 -0700 Subject: [PATCH 342/399] Fix the fence --- Grid/threads/Accelerator.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index c7366a1f..c97c91a3 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -248,12 +248,6 @@ inline int acceleratorIsCommunicable(void *ptr) // SyCL acceleration ////////////////////////////////////////////// #ifdef GRID_SYCL -inline void acceleratorFenceComputeStream(void){ accelerator_barrier();}; -#else -// Ordering within a stream guaranteed on Nvidia & AMD -inline void acceleratorFenceComputeStream(void){ }; -#endif -#ifdef GRID_SYCL NAMESPACE_END(Grid); #include <CL/sycl.hpp> #include <CL/sycl/usm.hpp> @@ -517,7 +511,16 @@ inline void *acceleratorAllocCpu(size_t bytes){return memalign(GRID_ALLOC_ALIGN, inline void acceleratorFreeCpu (void *ptr){free(ptr);}; #endif +////////////////////////////////////////////// +// Fencing needed ONLY for SYCL +////////////////////////////////////////////// +#ifdef GRID_SYCL +inline void acceleratorFenceComputeStream(void){ accelerator_barrier();}; +#else +// Ordering within a stream guaranteed on Nvidia & AMD +inline void acceleratorFenceComputeStream(void){ }; +#endif /////////////////////////////////////////////////// // Synchronise across local threads for divergence resynch From bb0a0da47a4035ffc0fdbfbb61411979aefeae5f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 2 Aug 2022 08:09:43 -0700 Subject: [PATCH 343/399] inon blocking caution due to SYCL --- Grid/algorithms/CoarsenedMatrix.h | 2 +- Grid/algorithms/approx/Chebyshev.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/algorithms/CoarsenedMatrix.h b/Grid/algorithms/CoarsenedMatrix.h index a9a82f34..ba4abecd 100644 --- a/Grid/algorithms/CoarsenedMatrix.h +++ b/Grid/algorithms/CoarsenedMatrix.h @@ -262,7 +262,7 @@ public: autoView( Tnp_v , (*Tnp), AcceleratorWrite); autoView( Tnm_v , (*Tnm), AcceleratorWrite); const int Nsimd = CComplex::Nsimd(); - accelerator_forNB(ss, FineGrid->oSites(), Nsimd, { + accelerator_for(ss, FineGrid->oSites(), Nsimd, { coalescedWrite(y_v[ss],xscale*y_v(ss)+mscale*Tn_v(ss)); coalescedWrite(Tnp_v[ss],2.0*y_v(ss)-Tnm_v(ss)); }); diff --git a/Grid/algorithms/approx/Chebyshev.h b/Grid/algorithms/approx/Chebyshev.h index 584ed1d5..7c93f0b8 100644 --- a/Grid/algorithms/approx/Chebyshev.h +++ b/Grid/algorithms/approx/Chebyshev.h @@ -264,7 +264,7 @@ public: auto Tnp_v = Tnp->View(); auto Tnm_v = Tnm->View(); constexpr int Nsimd = vector_type::Nsimd(); - accelerator_forNB(ss, in.Grid()->oSites(), Nsimd, { + accelerator_for(ss, in.Grid()->oSites(), Nsimd, { coalescedWrite(y_v[ss],xscale*y_v(ss)+mscale*Tn_v(ss)); coalescedWrite(Tnp_v[ss],2.0*y_v(ss)-Tnm_v(ss)); }); From 17d71771056264749b91be40d28fdb48a740f264 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 2 Aug 2022 08:33:39 -0700 Subject: [PATCH 344/399] Files for SYCL --- systems/PVC/benchmarks/run-1tile.sh | 62 +++++++++++++++++++++++++ systems/PVC/benchmarks/run-2tile-mpi.sh | 26 +++++++++++ systems/PVC/benchmarks/wrap.sh | 14 ++++++ systems/PVC/config-command | 15 ++++++ systems/PVC/setup.sh | 11 +++++ 5 files changed, 128 insertions(+) create mode 100644 systems/PVC/benchmarks/run-1tile.sh create mode 100755 systems/PVC/benchmarks/run-2tile-mpi.sh create mode 100755 systems/PVC/benchmarks/wrap.sh create mode 100644 systems/PVC/config-command create mode 100644 systems/PVC/setup.sh diff --git a/systems/PVC/benchmarks/run-1tile.sh b/systems/PVC/benchmarks/run-1tile.sh new file mode 100644 index 00000000..923afd84 --- /dev/null +++ b/systems/PVC/benchmarks/run-1tile.sh @@ -0,0 +1,62 @@ +#!/bin/sh +##SBATCH -p PVC-SPR-QZEH +##SBATCH -p PVC-ICX-QZNW +#SBATCH -p QZ1J-ICX-PVC +##SBATCH -p QZ1J-SPR-PVC-2C + +source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh + +export NT=16 + +export I_MPI_OFFLOAD=1 +export I_MPI_OFFLOAD_TOPOLIB=level_zero +export I_MPI_OFFLOAD_DOMAIN_SIZE=-1 + +# export IGC_EnableLSCFenceUGMBeforeEOT=0 +# export SYCL_PROGRAM_COMPILE_OPTIONS="-ze-opt-large-register-file=False" +export SYCL_DEVICE_FILTER=gpu,level_zero +#export IGC_ShaderDumpEnable=1 +#export IGC_DumpToCurrentDir=1 +export I_MPI_OFFLOAD_CELL=tile +export EnableImplicitScaling=0 +export EnableWalkerPartition=0 +export ZE_AFFINITY_MASK=0.0 +mpiexec -launcher ssh -n 1 -host localhost ./Benchmark_dwf_fp32 --mpi 1.1.1.1 --grid 32.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 --cacheblocking 8.8.8.8 + +export ZE_AFFINITY_MASK=0 +export I_MPI_OFFLOAD_CELL=device +export EnableImplicitScaling=1 +export EnableWalkerPartition=1 + + + + + + + + + + + + + + + + + + + + +#mpiexec -launcher ssh -n 2 -host localhost vtune -collect gpu-hotspots -knob gpu-sampling-interval=1 -data-limit=0 -r ./vtune_run4 -- ./wrap.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --comms-overlap --shm-mpi 1 + +#mpiexec -launcher ssh -n 1 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --comms-overlap --shm-mpi 1 + +#mpiexec -launcher ssh -n 2 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 + +#mpiexec -launcher ssh -n 2 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --comms-overlap --shm-mpi 1 + +#mpiexec -launcher ssh -n 2 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 0 + +#mpirun -np 2 ./wrap.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.2 --grid 16.32.32.64 --accelerator-threads $NT --comms-sequential --shm-mpi 0 +#mpirun -np 2 ./wrap.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --comms-sequential --shm-mpi 1 + diff --git a/systems/PVC/benchmarks/run-2tile-mpi.sh b/systems/PVC/benchmarks/run-2tile-mpi.sh new file mode 100755 index 00000000..9db0b66b --- /dev/null +++ b/systems/PVC/benchmarks/run-2tile-mpi.sh @@ -0,0 +1,26 @@ +#!/bin/bash +##SBATCH -p PVC-SPR-QZEH +##SBATCH -p PVC-ICX-QZNW +#SBATCH -p QZ1J-ICX-PVC + +source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh + +export NT=16 + + +# export IGC_EnableLSCFenceUGMBeforeEOT=0 +# export SYCL_PROGRAM_COMPILE_OPTIONS="-ze-opt-large-register-file=False" +#export IGC_ShaderDumpEnable=1 +#export IGC_DumpToCurrentDir=1 +export I_MPI_OFFLOAD=1 +export I_MPI_OFFLOAD_TOPOLIB=level_zero +export I_MPI_OFFLOAD_DOMAIN_SIZE=-1 +export SYCL_DEVICE_FILTER=gpu,level_zero +export I_MPI_OFFLOAD_CELL=tile +export EnableImplicitScaling=0 +export EnableWalkerPartition=0 + +mpiexec -launcher ssh -n 1 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.1 --grid 32.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 > 1tile.log + +mpiexec -launcher ssh -n 2 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 > 2tile.log + diff --git a/systems/PVC/benchmarks/wrap.sh b/systems/PVC/benchmarks/wrap.sh new file mode 100755 index 00000000..a352fff9 --- /dev/null +++ b/systems/PVC/benchmarks/wrap.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +export ZE_AFFINITY_MASK=0.$MPI_LOCALRANKID + +echo Ranke $MPI_LOCALRANKID ZE_AFFINITY_MASK is $ZE_AFFINITY_MASK + + +if [ $MPI_LOCALRANKID = "0" ] +then +# ~psteinbr/build_pti/ze_tracer -c $@ + onetrace --chrome-kernel-timeline $@ +else + $@ +fi diff --git a/systems/PVC/config-command b/systems/PVC/config-command new file mode 100644 index 00000000..3f5b5993 --- /dev/null +++ b/systems/PVC/config-command @@ -0,0 +1,15 @@ +INSTALL=/nfs/site/home/azusayax/install +../../configure \ + --enable-simd=GPU \ + --enable-gen-simd-width=64 \ + --enable-comms=mpi \ + --disable-accelerator-cshift \ + --disable-gparity \ + --disable-fermion-reps \ + --enable-shm=nvlink \ + --enable-accelerator=sycl \ + --enable-unified=yes \ + CXX=mpicxx \ + LDFLAGS="-fsycl-device-code-split=per_kernel -fsycl-device-lib=all -lze_loader -L$INSTALL/lib" \ + CXXFLAGS="-cxx=dpcpp -fsycl-unnamed-lambda -fsycl -no-fma -I$INSTALL/include -Wtautological-constant-compare" + diff --git a/systems/PVC/setup.sh b/systems/PVC/setup.sh new file mode 100644 index 00000000..2a6f920b --- /dev/null +++ b/systems/PVC/setup.sh @@ -0,0 +1,11 @@ +export https_proxy=http://proxy-chain.intel.com:911 +export LD_LIBRARY_PATH=/nfs/site/home/azusayax/install/lib:$LD_LIBRARY_PATH + +module load intel-release +source /opt/intel/oneapi/PVC_setup.sh +#source /opt/intel/oneapi/ATS_setup.sh +module load intel/mpich/pvc45.3 +export PATH=~/ATS/pti-gpu/tools/onetrace/:$PATH + +#clsh embargo-ci-neo-022845 +#source /opt/intel/vtune_amplifier/amplxe-vars.sh From 188d2c7a4dc77807b545f5f2813cdb589b9e44ca Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 2 Aug 2022 08:38:53 -0700 Subject: [PATCH 345/399] PVC default, ignore ATS --- Grid/threads/Accelerator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index c97c91a3..d2cf8a01 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -289,7 +289,7 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { cgh.parallel_for( \ cl::sycl::nd_range<3>(global,local), \ [=] (cl::sycl::nd_item<3> item) /*mutable*/ \ - [[intel::reqd_sub_group_size(8)]] \ + [[intel::reqd_sub_group_size(16)]] \ { \ auto iter1 = item.get_global_id(0); \ auto iter2 = item.get_global_id(1); \ From f922adf05e418312ddcb3e024c6bca179d7c8865 Mon Sep 17 00:00:00 2001 From: Gurtej Kanwar <gurtej@mit.edu> Date: Sun, 21 Aug 2022 16:16:18 +0200 Subject: [PATCH 346/399] Fix Photon ComplexField type --- Grid/qcd/action/gauge/Photon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/action/gauge/Photon.h b/Grid/qcd/action/gauge/Photon.h index 465aa8bd..3d4baccd 100644 --- a/Grid/qcd/action/gauge/Photon.h +++ b/Grid/qcd/action/gauge/Photon.h @@ -49,7 +49,7 @@ NAMESPACE_BEGIN(Grid); typedef Lattice<SiteLink> LinkField; typedef Lattice<SiteField> Field; - typedef Field ComplexField; + typedef LinkField ComplexField; }; typedef QedGImpl<vComplex> QedGImplR; From 554c2383593a85695f716eaebfcad539210ff689 Mon Sep 17 00:00:00 2001 From: Gurtej Kanwar <gurtej@mit.edu> Date: Sun, 21 Aug 2022 17:28:57 +0200 Subject: [PATCH 347/399] Update OpenSSL digest to use high-level methods This avoids deprecation warnings when compiling against OpenSSL 3.0 but should still be backwards compatible. It is the recommended way to use the digest API going forward. --- Grid/util/Sha.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Grid/util/Sha.h b/Grid/util/Sha.h index ee164c34..f3789ac4 100644 --- a/Grid/util/Sha.h +++ b/Grid/util/Sha.h @@ -27,6 +27,7 @@ /* END LEGAL */ extern "C" { #include <openssl/sha.h> +#include <openssl/evp.h> } #ifdef USE_IPP #include "ipp.h" @@ -70,10 +71,8 @@ public: static inline std::vector<unsigned char> sha256(const void *data,size_t bytes) { std::vector<unsigned char> hash(SHA256_DIGEST_LENGTH); - SHA256_CTX sha256; - SHA256_Init (&sha256); - SHA256_Update(&sha256, data,bytes); - SHA256_Final (&hash[0], &sha256); + auto digest = EVP_get_digestbyname("SHA256"); + EVP_Digest(data, bytes, &hash[0], NULL, digest, NULL); return hash; } static inline std::vector<int> sha256_seeds(const std::string &s) From 60dfb49afa48b75455fe076e25f67d09bef4a74e Mon Sep 17 00:00:00 2001 From: Gurtej Kanwar <gurtej@mit.edu> Date: Sun, 21 Aug 2022 17:29:55 +0200 Subject: [PATCH 348/399] Remove FP16 tests when FP16 is disabled --- tests/Test_simd.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Test_simd.cc b/tests/Test_simd.cc index 468bc982..16205ee1 100644 --- a/tests/Test_simd.cc +++ b/tests/Test_simd.cc @@ -793,6 +793,7 @@ int main (int argc, char ** argv) } std::cout <<" OK ! "<<std::endl; +#ifdef USE_FP16 // Double to Half std::cout << GridLogMessage<< "Double to half" ; precisionChange(&H[0],&D[0],Ndp); @@ -822,6 +823,7 @@ int main (int argc, char ** argv) assert( tmp < 1.0e-3 ); } std::cout <<" OK ! "<<std::endl; +#endif } Grid_finalize(); From 9296299b61508e8fb4a44fa32f656b7ab1857d9e Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 4 Oct 2022 11:10:34 -0700 Subject: [PATCH 349/399] Better commenting --- Grid/qcd/action/fermion/WilsonCompressor.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Grid/qcd/action/fermion/WilsonCompressor.h b/Grid/qcd/action/fermion/WilsonCompressor.h index e0e08c1c..5c3351a6 100644 --- a/Grid/qcd/action/fermion/WilsonCompressor.h +++ b/Grid/qcd/action/fermion/WilsonCompressor.h @@ -117,19 +117,19 @@ public: typedef decltype(coalescedRead(*in)) sobj; typedef decltype(coalescedRead(*out0)) hsobj; - unsigned int Nsimd = vobj::Nsimd(); + constexpr unsigned int Nsimd = vobj::Nsimd(); unsigned int mask = Nsimd >> (type + 1); int lane = acceleratorSIMTlane(Nsimd); int j0 = lane &(~mask); // inner coor zero int j1 = lane |(mask) ; // inner coor one - const vobj *vp0 = &in[k]; - const vobj *vp1 = &in[m]; - const vobj *vp = (lane&mask) ? vp1:vp0; - auto sa = coalescedRead(*vp,j0); - auto sb = coalescedRead(*vp,j1); + const vobj *vp0 = &in[k]; // out0[j] = merge low bit of type from in[k] and in[m] + const vobj *vp1 = &in[m]; // out1[j] = merge hi bit of type from in[k] and in[m] + const vobj *vp = (lane&mask) ? vp1:vp0;// if my lane has high bit take vp1, low bit take vp0 + auto sa = coalescedRead(*vp,j0); // lane to read for out 0, NB 50% read coalescing + auto sb = coalescedRead(*vp,j1); // lane to read for out 1 hsobj psa, psb; - projector::Proj(psa,sa,mu,dag); - projector::Proj(psb,sb,mu,dag); + projector::Proj(psa,sa,mu,dag); // spin project the result0 + projector::Proj(psb,sb,mu,dag); // spin project the result1 coalescedWrite(out0[j],psa); coalescedWrite(out1[j],psb); #else From e1e5c7502308e61b3649c87df56e1f3f31c7b90f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 4 Oct 2022 11:11:10 -0700 Subject: [PATCH 350/399] Stencil gather improvements - SVM was running slow and used for a pointer array that wasn't needed to be in SVM --- Grid/stencil/Stencil.h | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index c2bc8dab..65d878cb 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -80,11 +80,14 @@ void Gather_plane_simple_table (commVector<std::pair<int,int> >& table,const Lat /////////////////////////////////////////////////////////////////// template<class cobj,class vobj,class compressor> void Gather_plane_exchange_table(const Lattice<vobj> &rhs, - commVector<cobj *> pointers,int dimension,int plane,int cbmask,compressor &compress,int type) __attribute__((noinline)); + commVector<cobj *> pointers, + int dimension,int plane, + int cbmask,compressor &compress,int type) __attribute__((noinline)); template<class cobj,class vobj,class compressor> -void Gather_plane_exchange_table(commVector<std::pair<int,int> >& table,const Lattice<vobj> &rhs, - Vector<cobj *> pointers,int dimension,int plane,int cbmask, +void Gather_plane_exchange_table(commVector<std::pair<int,int> >& table, + const Lattice<vobj> &rhs, + std::vector<cobj *> &pointers,int dimension,int plane,int cbmask, compressor &compress,int type) { assert( (table.size()&0x1)==0); @@ -92,14 +95,15 @@ void Gather_plane_exchange_table(commVector<std::pair<int,int> >& table,const La int so = plane*rhs.Grid()->_ostride[dimension]; // base offset for start of plane auto rhs_v = rhs.View(AcceleratorRead); + auto rhs_p = &rhs_v[0]; auto p0=&pointers[0][0]; auto p1=&pointers[1][0]; auto tp=&table[0]; accelerator_forNB(j, num, vobj::Nsimd(), { - compress.CompressExchange(p0,p1, &rhs_v[0], j, - so+tp[2*j ].second, - so+tp[2*j+1].second, - type); + compress.CompressExchange(p0,p1, rhs_p, j, + so+tp[2*j ].second, + so+tp[2*j+1].second, + type); }); rhs_v.ViewClose(); } @@ -230,8 +234,8 @@ public: }; struct Merge { cobj * mpointer; - Vector<scalar_object *> rpointers; - Vector<cobj *> vpointers; + // std::vector<scalar_object *> rpointers; + std::vector<cobj *> vpointers; Integer buffer_size; Integer type; }; @@ -406,6 +410,7 @@ public: comms_bytes+=bytes; shm_bytes +=2*Packets[i].bytes-bytes; } + _grid->StencilBarrier();// Synch shared memory on a single nodes } void CommunicateComplete(std::vector<std::vector<CommsRequest_t> > &reqs) @@ -420,7 +425,7 @@ public: //////////////////////////////////////////////////////////////////////// void Communicate(void) { - if ( CartesianCommunicator::CommunicatorPolicy == CartesianCommunicator::CommunicatorPolicySequential ){ + if ( 0 ){ thread_region { // must be called in parallel region int mythread = thread_num(); @@ -569,7 +574,7 @@ public: d.buffer_size = buffer_size; dv.push_back(d); } - void AddMerge(cobj *merge_p,Vector<cobj *> &rpointers,Integer buffer_size,Integer type,std::vector<Merge> &mv) { + void AddMerge(cobj *merge_p,std::vector<cobj *> &rpointers,Integer buffer_size,Integer type,std::vector<Merge> &mv) { Merge m; m.type = type; m.mpointer = merge_p; @@ -582,6 +587,7 @@ public: } template<class decompressor> void CommsMergeSHM(decompressor decompress) { mpi3synctime-=usecond(); + accelerator_barrier(); _grid->StencilBarrier();// Synch shared memory on a single nodes mpi3synctime+=usecond(); shmmergetime-=usecond(); @@ -1114,8 +1120,8 @@ public: int bytes = (reduced_buffer_size*datum_bytes)/simd_layout; assert(bytes*simd_layout == reduced_buffer_size*datum_bytes); - Vector<cobj *> rpointers(maxl); - Vector<cobj *> spointers(maxl); + std::vector<cobj *> rpointers(maxl); + std::vector<cobj *> spointers(maxl); /////////////////////////////////////////// // Work out what to send where From 03508448f88c08cfa146e5b0082b930d5de503d7 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 4 Oct 2022 11:12:15 -0700 Subject: [PATCH 351/399] Remove verbose --- Grid/threads/Accelerator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index d2cf8a01..e17e85d1 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -298,14 +298,14 @@ accelerator_inline int acceleratorSIMTlane(int Nsimd) { }); \ }); -#define accelerator_barrier(dummy) { printf(" theGridAccelerator::wait()\n"); theGridAccelerator->wait(); } +#define accelerator_barrier(dummy) { theGridAccelerator->wait(); } inline void *acceleratorAllocShared(size_t bytes){ return malloc_shared(bytes,*theGridAccelerator);}; inline void *acceleratorAllocDevice(size_t bytes){ return malloc_device(bytes,*theGridAccelerator);}; inline void acceleratorFreeShared(void *ptr){free(ptr,*theGridAccelerator);}; inline void acceleratorFreeDevice(void *ptr){free(ptr,*theGridAccelerator);}; -inline void acceleratorCopySynchronise(void) { printf(" theCopyAccelerator::wait()\n"); theCopyAccelerator->wait(); } +inline void acceleratorCopySynchronise(void) { theCopyAccelerator->wait(); } inline void acceleratorCopyDeviceToDeviceAsynch(void *from,void *to,size_t bytes) { theCopyAccelerator->memcpy(to,from,bytes);} inline void acceleratorCopyToDevice(void *from,void *to,size_t bytes) { theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} inline void acceleratorCopyFromDevice(void *from,void *to,size_t bytes){ theCopyAccelerator->memcpy(to,from,bytes); theCopyAccelerator->wait();} From 413312f9a9a98634363f5c5206be19e97ec55fca Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 4 Oct 2022 11:12:59 -0700 Subject: [PATCH 352/399] Benchmark the halo construction. THe bye counts are out and should be doubled for SIMD directions --- benchmarks/Benchmark_halo.cc | 131 +++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 benchmarks/Benchmark_halo.cc diff --git a/benchmarks/Benchmark_halo.cc b/benchmarks/Benchmark_halo.cc new file mode 100644 index 00000000..43138e67 --- /dev/null +++ b/benchmarks/Benchmark_halo.cc @@ -0,0 +1,131 @@ + /************************************************************************************* + Grid physics library, www.github.com/paboyle/Grid + Source file: ./benchmarks/Benchmark_dwf.cc + Copyright (C) 2015 + + Author: Peter Boyle <paboyle@ph.ed.ac.uk> + Author: paboyle <paboyle@ph.ed.ac.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include <Grid/Grid.h> +#ifdef GRID_CUDA +#define CUDA_PROFILE +#endif + +#ifdef CUDA_PROFILE +#include <cuda_profiler_api.h> +#endif + +using namespace std; +using namespace Grid; + +template<class d> +struct scal { + d internal; +}; + + Gamma::Algebra Gmu [] = { + Gamma::Algebra::GammaX, + Gamma::Algebra::GammaY, + Gamma::Algebra::GammaZ, + Gamma::Algebra::GammaT + }; + + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + Coordinate latt4= GridDefaultLatt(); + Coordinate mpi = GridDefaultMpi(); + Coordinate simd = GridDefaultSimd(Nd,vComplexF::Nsimd()); + + GridLogLayout(); + + int Ls=16; + for(int i=0;i<argc;i++) + if(std::string(argv[i]) == "-Ls"){ + std::stringstream ss(argv[i+1]); ss >> Ls; + } + + + GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(latt4,simd ,mpi); + GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid); + GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid); + GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid); + + std::cout << GridLogMessage << "Making s innermost grids"<<std::endl; + GridCartesian * sUGrid = SpaceTimeGrid::makeFourDimDWFGrid(GridDefaultLatt(),GridDefaultMpi()); + GridRedBlackCartesian * sUrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(sUGrid); + GridCartesian * sFGrid = SpaceTimeGrid::makeFiveDimDWFGrid(Ls,UGrid); + GridRedBlackCartesian * sFrbGrid = SpaceTimeGrid::makeFiveDimDWFRedBlackGrid(Ls,UGrid); + + std::vector<int> seeds4({1,2,3,4}); + std::vector<int> seeds5({5,6,7,8}); + + std::cout << GridLogMessage << "Initialising 4d RNG" << std::endl; + GridParallelRNG RNG4(UGrid); RNG4.SeedUniqueString(std::string("The 4D RNG")); + std::cout << GridLogMessage << "Initialising 5d RNG" << std::endl; + GridParallelRNG RNG5(FGrid); RNG5.SeedUniqueString(std::string("The 5D RNG")); + std::cout << GridLogMessage << "Initialised RNGs" << std::endl; + + LatticeFermionF src (FGrid); random(RNG5,src); + RealD N2 = 1.0/::sqrt(norm2(src)); + src = src*N2; + + std::cout << GridLogMessage << "Drawing gauge field" << std::endl; + LatticeGaugeFieldF Umu(UGrid); + SU<Nc>::HotConfiguration(RNG4,Umu); + std::cout << GridLogMessage << "Random gauge initialised " << std::endl; + + RealD mass=0.1; + RealD M5 =1.8; + + RealD NP = UGrid->_Nprocessors; + RealD NN = UGrid->NodeCount(); + + DomainWallFermionF Dw(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5); + + const int ncall = 500; + std::cout << GridLogMessage<< "*********************************************************" <<std::endl; + std::cout << GridLogMessage<< "* Benchmarking DomainWallFermionF::HaloGatherOpt "<<std::endl; + std::cout << GridLogMessage<< "*********************************************************" <<std::endl; + { + typename DomainWallFermionF::Compressor compressor(0); + FGrid->Barrier(); + Dw.Stencil.HaloExchangeOptGather(src,compressor); + double t0=usecond(); + for(int i=0;i<ncall;i++){ + Dw.Stencil.HaloExchangeOptGather(src,compressor); + } + double t1=usecond(); + FGrid->Barrier(); + + double bytes=0.0; + if(mpi[0]) bytes+=latt4[1]*latt4[2]*latt4[3]; + if(mpi[1]) bytes+=latt4[0]*latt4[2]*latt4[3]; + if(mpi[2]) bytes+=latt4[0]*latt4[1]*latt4[3]; + if(mpi[3]) bytes+=latt4[0]*latt4[1]*latt4[2]; + bytes = bytes * Ls * 8.* (24.+12.)* 2.0; + + std::cout<<GridLogMessage << "Gather us /call = "<< (t1-t0)/ncall<<std::endl; + std::cout<<GridLogMessage << "Gather MBs /call = "<< bytes*ncall/(t1-t0)<<std::endl; + + } + + Grid_finalize(); + exit(0); +} From 0d5639f7075902182c59b9458a0e242f7ad3b7eb Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 4 Oct 2022 11:13:41 -0700 Subject: [PATCH 353/399] Run script update --- systems/PVC/benchmarks/run-2tile-mpi.sh | 11 +++++++++-- systems/PVC/benchmarks/wrap.sh | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/systems/PVC/benchmarks/run-2tile-mpi.sh b/systems/PVC/benchmarks/run-2tile-mpi.sh index 9db0b66b..34deac2c 100755 --- a/systems/PVC/benchmarks/run-2tile-mpi.sh +++ b/systems/PVC/benchmarks/run-2tile-mpi.sh @@ -1,6 +1,7 @@ #!/bin/bash ##SBATCH -p PVC-SPR-QZEH ##SBATCH -p PVC-ICX-QZNW + #SBATCH -p QZ1J-ICX-PVC source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh @@ -19,8 +20,14 @@ export SYCL_DEVICE_FILTER=gpu,level_zero export I_MPI_OFFLOAD_CELL=tile export EnableImplicitScaling=0 export EnableWalkerPartition=0 +#export SYCL_PI_LEVEL_ZERO_DEVICE_SCOPE_EVENTS=1 +export SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1 +#export SYCL_PI_LEVEL_ZERO_USE_COPY_ENGINE=0 -mpiexec -launcher ssh -n 1 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.1 --grid 32.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 > 1tile.log +mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 > dw.2tile.1x2.log +mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 > dw.2tile.2x1.log + +mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.1x2.log +mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.2x1.log -mpiexec -launcher ssh -n 2 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 > 2tile.log diff --git a/systems/PVC/benchmarks/wrap.sh b/systems/PVC/benchmarks/wrap.sh index a352fff9..bb7b517d 100755 --- a/systems/PVC/benchmarks/wrap.sh +++ b/systems/PVC/benchmarks/wrap.sh @@ -7,8 +7,8 @@ echo Ranke $MPI_LOCALRANKID ZE_AFFINITY_MASK is $ZE_AFFINITY_MASK if [ $MPI_LOCALRANKID = "0" ] then -# ~psteinbr/build_pti/ze_tracer -c $@ - onetrace --chrome-kernel-timeline $@ +# ~psteinbr/build_pti/ze_tracer -h $@ + onetrace --chrome-device-timeline $@ else $@ fi From 9e4835a3e3769e3b310941b26a58e7a239e4fb43 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 25 Oct 2022 15:19:43 +0100 Subject: [PATCH 354/399] feat: changed CompactWilsonExpClover exponentiation to Taylor expansion with Horner scheme. --- Grid/qcd/action/fermion/CloverHelpers.h | 203 ++++-------------- ...CompactWilsonCloverFermionImplementation.h | 11 +- 2 files changed, 50 insertions(+), 164 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index 57e71998..c1df81fb 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -242,6 +242,19 @@ public: // mass term is multiplied to exp(Clover) below } + // Can this be avoided? + static void IdentityTimesC(const CloverField& in, RealD c) { + int DimRep = Impl::Dimension; + + autoView(in_v, in, AcceleratorWrite); + + accelerator_for(ss, in.Grid()->oSites(), 1, { + for (int sa=0; sa<Ns; sa++) + for (int ca=0; ca<DimRep; ca++) + in_v[ss]()(sa,sa)(ca,ca) = c; + }); + } + static int getNMAX(RealD prec, RealD R) { /* compute stop condition for exponential */ int NMAX=1; @@ -254,176 +267,46 @@ public: return NMAX; } - static int getNMAX(Lattice<iImplCloverDiagonal<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);} - static int getNMAX(Lattice<iImplCloverDiagonal<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);} + static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);} + static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);} - static void ExponentiateHermitean6by6(const iMatrix<ComplexD,6> &arg, const RealD& alpha, const std::vector<RealD>& cN, const int Niter, iMatrix<ComplexD,6>& dest){ + static void Exponentiate_Clover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { - typedef iMatrix<ComplexD,6> mat; + GridBase* grid = Clover.Grid(); + CloverField ExpClover(grid); - RealD qn[6]; - RealD qnold[6]; - RealD p[5]; - RealD trA2, trA3, trA4; + int NMAX = getNMAX(Clover, 3.*csw_t/diag_mass); - mat A2, A3, A4, A5; - A2 = alpha * alpha * arg * arg; - A3 = alpha * arg * A2; - A4 = A2 * A2; - A5 = A2 * A3; + Clover *= (1.0/diag_mass); - trA2 = toReal( trace(A2) ); - trA3 = toReal( trace(A3) ); - trA4 = toReal( trace(A4)); - - p[0] = toReal( trace(A3 * A3)) / 6.0 - 0.125 * trA4 * trA2 - trA3 * trA3 / 18.0 + trA2 * trA2 * trA2/ 48.0; - p[1] = toReal( trace(A5)) / 5.0 - trA3 * trA2 / 6.0; - p[2] = toReal( trace(A4)) / 4.0 - 0.125 * trA2 * trA2; - p[3] = trA3 / 3.0; - p[4] = 0.5 * trA2; - - qnold[0] = cN[Niter]; - qnold[1] = 0.0; - qnold[2] = 0.0; - qnold[3] = 0.0; - qnold[4] = 0.0; - qnold[5] = 0.0; - - for(int i = Niter-1; i >= 0; i--) - { - qn[0] = p[0] * qnold[5] + cN[i]; - qn[1] = p[1] * qnold[5] + qnold[0]; - qn[2] = p[2] * qnold[5] + qnold[1]; - qn[3] = p[3] * qnold[5] + qnold[2]; - qn[4] = p[4] * qnold[5] + qnold[3]; - qn[5] = qnold[4]; - - qnold[0] = qn[0]; - qnold[1] = qn[1]; - qnold[2] = qn[2]; - qnold[3] = qn[3]; - qnold[4] = qn[4]; - qnold[5] = qn[5]; - } - - mat unit(1.0); - - dest = (qn[0] * unit + qn[1] * alpha * arg + qn[2] * A2 + qn[3] * A3 + qn[4] * A4 + qn[5] * A5); - - } - - static void Exponentiate_Clover(CloverDiagonalField& Diagonal, CloverTriangleField& Triangle, RealD csw_t, RealD diag_mass) { - - GridBase* grid = Diagonal.Grid(); - int NMAX = getNMAX(Diagonal, 3.*csw_t/diag_mass); - - // - // Implementation completely in Daniel's layout - // - - // Taylor expansion with Cayley-Hamilton recursion - // underlying Horner scheme as above + // Taylor expansion, slow but generic + // Horner scheme: a0 + a1 x + a2 x^2 + .. = a0 + x (a1 + x(...)) + // qN = cN + // qn = cn + qn+1 X std::vector<RealD> cn(NMAX+1); cn[0] = 1.0; - for (int i=1; i<=NMAX; i++){ + for (int i=1; i<=NMAX; i++) cn[i] = cn[i-1] / RealD(i); + + ExpClover = Zero(); + IdentityTimesC(ExpClover, cn[NMAX]); + for (int i=NMAX-1; i>=0; i--) + ExpClover = ExpClover * Clover + cn[i]; + + // prepare inverse + CloverInv = (-1.0)*Clover; + + Clover = ExpClover * diag_mass; + + ExpClover = Zero(); + IdentityTimesC(ExpClover, cn[NMAX]); + for (int i=NMAX-1; i>=0; i--) + ExpClover = ExpClover * CloverInv + cn[i]; + + CloverInv = ExpClover * (1.0/diag_mass); + } - // Taken over from Daniel's implementation - conformable(Diagonal, Triangle); - - long lsites = grid->lSites(); - { - typedef typename SiteCloverDiagonal::scalar_object scalar_object_diagonal; - typedef typename SiteCloverTriangle::scalar_object scalar_object_triangle; - typedef iMatrix<ComplexD,6> mat; - - autoView(diagonal_v, Diagonal, CpuRead); - autoView(triangle_v, Triangle, CpuRead); - autoView(diagonalExp_v, Diagonal, CpuWrite); - autoView(triangleExp_v, Triangle, CpuWrite); - - thread_for(site, lsites, { // NOTE: Not on GPU because of (peek/poke)LocalSite - - mat srcCloverOpUL(0.0); // upper left block - mat srcCloverOpLR(0.0); // lower right block - mat ExpCloverOp; - - scalar_object_diagonal diagonal_tmp = Zero(); - scalar_object_diagonal diagonal_exp_tmp = Zero(); - scalar_object_triangle triangle_tmp = Zero(); - scalar_object_triangle triangle_exp_tmp = Zero(); - - Coordinate lcoor; - grid->LocalIndexToLocalCoor(site, lcoor); - - peekLocalSite(diagonal_tmp, diagonal_v, lcoor); - peekLocalSite(triangle_tmp, triangle_v, lcoor); - - int block; - block = 0; - for(int i = 0; i < 6; i++){ - for(int j = 0; j < 6; j++){ - if (i == j){ - srcCloverOpUL(i,j) = static_cast<ComplexD>(TensorRemove(diagonal_tmp()(block)(i))); - } - else{ - srcCloverOpUL(i,j) = static_cast<ComplexD>(TensorRemove(CompactHelpers::triangle_elem(triangle_tmp, block, i, j))); - } - } - } - block = 1; - for(int i = 0; i < 6; i++){ - for(int j = 0; j < 6; j++){ - if (i == j){ - srcCloverOpLR(i,j) = static_cast<ComplexD>(TensorRemove(diagonal_tmp()(block)(i))); - } - else{ - srcCloverOpLR(i,j) = static_cast<ComplexD>(TensorRemove(CompactHelpers::triangle_elem(triangle_tmp, block, i, j))); - } - } - } - - // exp(Clover) - - ExponentiateHermitean6by6(srcCloverOpUL,1.0/diag_mass,cn,NMAX,ExpCloverOp); - - block = 0; - for(int i = 0; i < 6; i++){ - for(int j = 0; j < 6; j++){ - if (i == j){ - diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); - } - else if(i < j){ - triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); - } - } - } - - ExponentiateHermitean6by6(srcCloverOpLR,1.0/diag_mass,cn,NMAX,ExpCloverOp); - - block = 1; - for(int i = 0; i < 6; i++){ - for(int j = 0; j < 6; j++){ - if (i == j){ - diagonal_exp_tmp()(block)(i) = ExpCloverOp(i,j); - } - else if(i < j){ - triangle_exp_tmp()(block)(CompactHelpers::triangle_index(i, j)) = ExpCloverOp(i,j); - } - } - } - - pokeLocalSite(diagonal_exp_tmp, diagonalExp_v, lcoor); - pokeLocalSite(triangle_exp_tmp, triangleExp_v, lcoor); - }); - } - - Diagonal *= diag_mass; - Triangle *= diag_mass; - } - - static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { assert(0); return lambda; diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index e4864730..e042b985 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -305,6 +305,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie GridBase* grid = _Umu.Grid(); typename Impl::GaugeLinkField Bx(grid), By(grid), Bz(grid), Ex(grid), Ey(grid), Ez(grid); CloverField TmpOriginal(grid); + CloverField TmpInverse(grid); // Compute the field strength terms mu>nu double t2 = usecond(); @@ -326,14 +327,14 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie TmpOriginal += Helpers::fillCloverZT(Ez) * csw_t; // Handle mass term based on clover policy CloverHelpers::MassTerm(TmpOriginal, this->diag_mass); - + // Convert the data layout of the clover term double t4 = usecond(); - CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); + CloverHelpers::Exponentiate_Clover(TmpOriginal, TmpInverse, csw_t, this->diag_mass); // Exponentiate the clover (nothing happens in case of the standard clover) double t5 = usecond(); - CloverHelpers::Exponentiate_Clover(Diagonal, Triangle, csw_t, this->diag_mass); + CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); // Possible modify the boundary values double t6 = usecond(); @@ -341,7 +342,9 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie // Invert the Clover term (explicit inversion needed for the improvement in case of open boundary conditions) double t7 = usecond(); - CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); + //CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); + CompactHelpers::ConvertLayout(TmpInverse, DiagonalInv, TriangleInv); + //if(open_boundaries) handle differently! // Fill the remaining clover fields double t8 = usecond(); From 513d797ea63245fdffdafd5ea1ea66bb2e357168 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 25 Oct 2022 16:17:22 +0100 Subject: [PATCH 355/399] fix: signature of CompactWilsonCloverHelpers::Exponentiate fixed. --- Grid/qcd/action/fermion/CloverHelpers.h | 5 +---- .../CompactWilsonCloverFermionImplementation.h | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index c1df81fb..c2bb0d8f 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -208,10 +208,7 @@ public: Clover += diag_mass; } - static void Exponentiate_Clover(CloverDiagonalField& Diagonal, - CloverTriangleField& Triangle, - RealD csw_t, RealD diag_mass) { - + static void Exponentiate_Clover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { // Do nothing } diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index e042b985..e45a531f 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -365,8 +365,8 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie std::cout << GridLogDebug << "allocations = " << (t2 - t1) / 1e6 << std::endl; std::cout << GridLogDebug << "field strength = " << (t3 - t2) / 1e6 << std::endl; std::cout << GridLogDebug << "fill clover = " << (t4 - t3) / 1e6 << std::endl; - std::cout << GridLogDebug << "convert = " << (t5 - t4) / 1e6 << std::endl; - std::cout << GridLogDebug << "exponentiation = " << (t6 - t5) / 1e6 << std::endl; + std::cout << GridLogDebug << "exponentiation = " << (t5 - t4) / 1e6 << std::endl; + std::cout << GridLogDebug << "convert = " << (t6 - t5) / 1e6 << std::endl; std::cout << GridLogDebug << "boundaries = " << (t7 - t6) / 1e6 << std::endl; std::cout << GridLogDebug << "inversions = " << (t8 - t7) / 1e6 << std::endl; std::cout << GridLogDebug << "pick cbs = " << (t9 - t8) / 1e6 << std::endl; From b36442e263e117ad771b4416dac469dcb6aba2fa Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 25 Oct 2022 16:57:01 +0100 Subject: [PATCH 356/399] feat: CloverHelpers::InvertClover implemented which handles the inversion of the Clover term depending on clover type and the boundary conditions. --- Grid/qcd/action/fermion/CloverHelpers.h | 31 +++++++++++++++++-- ...CompactWilsonCloverFermionImplementation.h | 6 ++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index c2bb0d8f..e8c45888 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -208,10 +208,20 @@ public: Clover += diag_mass; } - static void Exponentiate_Clover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { + static void ExponentiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { // Do nothing } + static void InvertClover(CloverField& InvClover, + const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle, + CloverDiagonalField& diagonalInv, + CloverTriangleField& triangleInv, + bool open_boundaries) { + + CompactHelpers::Invert(diagonal, triangle, diagonalInv, triangleInv); + } + // TODO: implement Cmunu for better performances with compact layout, but don't do it // here, but rather in WilsonCloverHelpers.h -> CompactWilsonCloverHelpers static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { @@ -267,7 +277,7 @@ public: static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);} static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);} - static void Exponentiate_Clover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { + static void ExponentiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { GridBase* grid = Clover.Grid(); CloverField ExpClover(grid); @@ -302,7 +312,24 @@ public: CloverInv = ExpClover * (1.0/diag_mass); + } + + static void InvertClover(CloverField& InvClover, + const CloverDiagonalField& diagonal, + const CloverTriangleField& triangle, + CloverDiagonalField& diagonalInv, + CloverTriangleField& triangleInv, + bool open_boundaries) { + + if (open_boundaries) + { + CompactHelpers::Invert(diagonal, triangle, diagonalInv, triangleInv); } + else + { + CompactHelpers::ConvertLayout(InvClover, diagonalInv, triangleInv); + } + } static GaugeLinkField Cmunu(std::vector<GaugeLinkField> &U, GaugeLinkField &lambda, int mu, int nu) { assert(0); diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index e45a531f..6ee22113 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -330,7 +330,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie // Convert the data layout of the clover term double t4 = usecond(); - CloverHelpers::Exponentiate_Clover(TmpOriginal, TmpInverse, csw_t, this->diag_mass); + CloverHelpers::ExponentiateClover(TmpOriginal, TmpInverse, csw_t, this->diag_mass); // Exponentiate the clover (nothing happens in case of the standard clover) double t5 = usecond(); @@ -342,9 +342,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie // Invert the Clover term (explicit inversion needed for the improvement in case of open boundary conditions) double t7 = usecond(); - //CompactHelpers::Invert(Diagonal, Triangle, DiagonalInv, TriangleInv); - CompactHelpers::ConvertLayout(TmpInverse, DiagonalInv, TriangleInv); - //if(open_boundaries) handle differently! + CloverHelpers::InvertClover(TmpInverse, Diagonal, Triangle, DiagonalInv, TriangleInv, open_boundaries); // Fill the remaining clover fields double t8 = usecond(); From 86075fdd45a1f88108615a6dada2df2bee5af8ad Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 25 Oct 2022 17:05:34 +0100 Subject: [PATCH 357/399] feat: MassTerm and ExponentiateClover merged into InstantiateClover --- Grid/qcd/action/fermion/CloverHelpers.h | 13 ++----------- .../CompactWilsonCloverFermionImplementation.h | 12 ++++++------ 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index e8c45888..b8b4fa59 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -204,14 +204,10 @@ public: typedef WilsonCloverHelpers<Impl> Helpers; typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; - static void MassTerm(CloverField& Clover, RealD diag_mass) { + static void InstantiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { Clover += diag_mass; } - static void ExponentiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { - // Do nothing - } - static void InvertClover(CloverField& InvClover, const CloverDiagonalField& diagonal, const CloverTriangleField& triangle, @@ -244,11 +240,6 @@ public: template <typename vtype> using iImplClover = iScalar<iMatrix<iMatrix<vtype, Impl::Dimension>, Ns>>; typedef CompactWilsonCloverHelpers<Impl> CompactHelpers; - static void MassTerm(CloverField& Clover, RealD diag_mass) { - // do nothing! - // mass term is multiplied to exp(Clover) below - } - // Can this be avoided? static void IdentityTimesC(const CloverField& in, RealD c) { int DimRep = Impl::Dimension; @@ -277,7 +268,7 @@ public: static int getNMAX(Lattice<iImplClover<vComplexD>> &t, RealD R) {return getNMAX(1e-12,R);} static int getNMAX(Lattice<iImplClover<vComplexF>> &t, RealD R) {return getNMAX(1e-6,R);} - static void ExponentiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { + static void InstantiateClover(CloverField& Clover, CloverField& CloverInv, RealD csw_t, RealD diag_mass) { GridBase* grid = Clover.Grid(); CloverField ExpClover(grid); diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 6ee22113..0828f5fc 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -325,14 +325,14 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie TmpOriginal += Helpers::fillCloverXT(Ex) * csw_t; TmpOriginal += Helpers::fillCloverYT(Ey) * csw_t; TmpOriginal += Helpers::fillCloverZT(Ez) * csw_t; - // Handle mass term based on clover policy - CloverHelpers::MassTerm(TmpOriginal, this->diag_mass); + + // Instantiate the clover term + // - In case of the standard clover the mass term is added + // - In case of the exponential clover the clover term is exponentiated + double t4 = usecond(); + CloverHelpers::InstantiateClover(TmpOriginal, TmpInverse, csw_t, this->diag_mass); // Convert the data layout of the clover term - double t4 = usecond(); - CloverHelpers::ExponentiateClover(TmpOriginal, TmpInverse, csw_t, this->diag_mass); - - // Exponentiate the clover (nothing happens in case of the standard clover) double t5 = usecond(); CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); From 9317d893b201a54c7dfa411fd0aaf6a32aa6f61b Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 25 Oct 2022 17:10:06 +0100 Subject: [PATCH 358/399] docs: details about inversion of CompactClover term added. --- .../CompactWilsonCloverFermionImplementation.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 0828f5fc..40bdfb43 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -340,7 +340,10 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie double t6 = usecond(); if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); - // Invert the Clover term (explicit inversion needed for the improvement in case of open boundary conditions) + // Invert the Clover term + // In case of the exponential clover with (anti-)periodic boundary conditions exp(-Clover) saved + // in TmpInverse can be used. In all other cases the clover term has to be explictly inverted. + // TODO: For now this inversion is explictly done on the CPU double t7 = usecond(); CloverHelpers::InvertClover(TmpInverse, Diagonal, Triangle, DiagonalInv, TriangleInv, open_boundaries); From a2a879b668d008ad5f70f77e7160c8e855c444a0 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Tue, 25 Oct 2022 17:20:42 +0100 Subject: [PATCH 359/399] docs: CompactClover Debug Info improved. --- .../CompactWilsonCloverFermionImplementation.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 40bdfb43..16f89725 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -336,7 +336,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie double t5 = usecond(); CompactHelpers::ConvertLayout(TmpOriginal, Diagonal, Triangle); - // Possible modify the boundary values + // Modify the clover term at the temporal boundaries in case of open boundary conditions double t6 = usecond(); if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); @@ -366,10 +366,10 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie std::cout << GridLogDebug << "allocations = " << (t2 - t1) / 1e6 << std::endl; std::cout << GridLogDebug << "field strength = " << (t3 - t2) / 1e6 << std::endl; std::cout << GridLogDebug << "fill clover = " << (t4 - t3) / 1e6 << std::endl; - std::cout << GridLogDebug << "exponentiation = " << (t5 - t4) / 1e6 << std::endl; - std::cout << GridLogDebug << "convert = " << (t6 - t5) / 1e6 << std::endl; - std::cout << GridLogDebug << "boundaries = " << (t7 - t6) / 1e6 << std::endl; - std::cout << GridLogDebug << "inversions = " << (t8 - t7) / 1e6 << std::endl; + std::cout << GridLogDebug << "exponentiate clover = " << (t5 - t4) / 1e6 << std::endl; + std::cout << GridLogDebug << "convert layout = " << (t6 - t5) / 1e6 << std::endl; + std::cout << GridLogDebug << "modify boundaries = " << (t7 - t6) / 1e6 << std::endl; + std::cout << GridLogDebug << "invert clover = " << (t8 - t7) / 1e6 << std::endl; std::cout << GridLogDebug << "pick cbs = " << (t9 - t8) / 1e6 << std::endl; std::cout << GridLogDebug << "total = " << (t9 - t0) / 1e6 << std::endl; } From 5fa6a8b96d6d7d82dfb2ab2c0ebfb844871bcc6b Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Wed, 26 Oct 2022 12:40:28 +0100 Subject: [PATCH 360/399] docs: CompactClover debug info generalized. --- .../implementation/CompactWilsonCloverFermionImplementation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index 16f89725..c8d1ca30 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -366,7 +366,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie std::cout << GridLogDebug << "allocations = " << (t2 - t1) / 1e6 << std::endl; std::cout << GridLogDebug << "field strength = " << (t3 - t2) / 1e6 << std::endl; std::cout << GridLogDebug << "fill clover = " << (t4 - t3) / 1e6 << std::endl; - std::cout << GridLogDebug << "exponentiate clover = " << (t5 - t4) / 1e6 << std::endl; + std::cout << GridLogDebug << "instantiate clover = " << (t5 - t4) / 1e6 << std::endl; std::cout << GridLogDebug << "convert layout = " << (t6 - t5) / 1e6 << std::endl; std::cout << GridLogDebug << "modify boundaries = " << (t7 - t6) / 1e6 << std::endl; std::cout << GridLogDebug << "invert clover = " << (t8 - t7) / 1e6 << std::endl; From 184adeedb83c716b879d2026d1af902aa2457d02 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Wed, 26 Oct 2022 12:53:46 +0100 Subject: [PATCH 361/399] feat: renamed open_boundaries to fixedBoundaries --- Grid/qcd/action/fermion/CloverHelpers.h | 6 ++-- .../fermion/CompactWilsonCloverFermion.h | 2 +- ...CompactWilsonCloverFermionImplementation.h | 32 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Grid/qcd/action/fermion/CloverHelpers.h b/Grid/qcd/action/fermion/CloverHelpers.h index b8b4fa59..cd469ea7 100644 --- a/Grid/qcd/action/fermion/CloverHelpers.h +++ b/Grid/qcd/action/fermion/CloverHelpers.h @@ -213,7 +213,7 @@ public: const CloverTriangleField& triangle, CloverDiagonalField& diagonalInv, CloverTriangleField& triangleInv, - bool open_boundaries) { + bool fixedBoundaries) { CompactHelpers::Invert(diagonal, triangle, diagonalInv, triangleInv); } @@ -310,9 +310,9 @@ public: const CloverTriangleField& triangle, CloverDiagonalField& diagonalInv, CloverTriangleField& triangleInv, - bool open_boundaries) { + bool fixedBoundaries) { - if (open_boundaries) + if (fixedBoundaries) { CompactHelpers::Invert(diagonal, triangle, diagonalInv, triangleInv); } diff --git a/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h index 249b20bd..d79b34d4 100644 --- a/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h +++ b/Grid/qcd/action/fermion/CompactWilsonCloverFermion.h @@ -225,7 +225,7 @@ public: RealD csw_t; RealD cF; - bool open_boundaries; + bool fixedBoundaries; CloverDiagonalField Diagonal, DiagonalEven, DiagonalOdd; CloverDiagonalField DiagonalInv, DiagonalInvEven, DiagonalInvOdd; diff --git a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h index c8d1ca30..7e3b7f00 100644 --- a/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h +++ b/Grid/qcd/action/fermion/implementation/CompactWilsonCloverFermionImplementation.h @@ -48,7 +48,7 @@ CompactWilsonCloverFermion<Impl, CloverHelpers>::CompactWilsonCloverFermion(Gaug , csw_r(_csw_r) , csw_t(_csw_t) , cF(_cF) - , open_boundaries(impl_p.boundary_phases[Nd-1] == 0.0) + , fixedBoundaries(impl_p.boundary_phases[Nd-1] == 0.0) , Diagonal(&Fgrid), Triangle(&Fgrid) , DiagonalEven(&Hgrid), TriangleEven(&Hgrid) , DiagonalOdd(&Hgrid), TriangleOdd(&Hgrid) @@ -67,7 +67,7 @@ CompactWilsonCloverFermion<Impl, CloverHelpers>::CompactWilsonCloverFermion(Gaug csw_r /= clover_anisotropy.xi_0; ImportGauge(_Umu); - if (open_boundaries) { + if (fixedBoundaries) { this->BoundaryMaskEven.Checkerboard() = Even; this->BoundaryMaskOdd.Checkerboard() = Odd; CompactHelpers::SetupMasks(this->BoundaryMask, this->BoundaryMaskEven, this->BoundaryMaskOdd); @@ -77,31 +77,31 @@ CompactWilsonCloverFermion<Impl, CloverHelpers>::CompactWilsonCloverFermion(Gaug template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::Dhop(const FermionField& in, FermionField& out, int dag) { WilsonBase::Dhop(in, out, dag); - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopOE(const FermionField& in, FermionField& out, int dag) { WilsonBase::DhopOE(in, out, dag); - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopEO(const FermionField& in, FermionField& out, int dag) { WilsonBase::DhopEO(in, out, dag); - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopDir(const FermionField& in, FermionField& out, int dir, int disp) { WilsonBase::DhopDir(in, out, dir, disp); - if(this->open_boundaries) ApplyBoundaryMask(out); + if(this->fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::DhopDirAll(const FermionField& in, std::vector<FermionField>& out) { WilsonBase::DhopDirAll(in, out); - if(this->open_boundaries) { + if(this->fixedBoundaries) { for(auto& o : out) ApplyBoundaryMask(o); } } @@ -112,7 +112,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::M(const FermionField& in, WilsonBase::Dhop(in, out, DaggerNo); // call base to save applying bc Mooee(in, Tmp); axpy(out, 1.0, out, Tmp); - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> @@ -121,19 +121,19 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::Mdag(const FermionField& i WilsonBase::Dhop(in, out, DaggerYes); // call base to save applying bc MooeeDag(in, Tmp); axpy(out, 1.0, out, Tmp); - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::Meooe(const FermionField& in, FermionField& out) { WilsonBase::Meooe(in, out); - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::MeooeDag(const FermionField& in, FermionField& out) { WilsonBase::MeooeDag(in, out); - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> @@ -147,7 +147,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::Mooee(const FermionField& } else { MooeeInternal(in, out, Diagonal, Triangle); } - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> @@ -166,7 +166,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::MooeeInv(const FermionFiel } else { MooeeInternal(in, out, DiagonalInv, TriangleInv); } - if(open_boundaries) ApplyBoundaryMask(out); + if(fixedBoundaries) ApplyBoundaryMask(out); } template<class Impl, class CloverHelpers> @@ -186,7 +186,7 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::MdirAll(const FermionField template<class Impl, class CloverHelpers> void CompactWilsonCloverFermion<Impl, CloverHelpers>::MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) { - assert(!open_boundaries); // TODO check for changes required for open bc + assert(!fixedBoundaries); // TODO check for changes required for open bc // NOTE: code copied from original clover term conformable(X.Grid(), Y.Grid()); @@ -338,14 +338,14 @@ void CompactWilsonCloverFermion<Impl, CloverHelpers>::ImportGauge(const GaugeFie // Modify the clover term at the temporal boundaries in case of open boundary conditions double t6 = usecond(); - if(open_boundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); + if(fixedBoundaries) CompactHelpers::ModifyBoundaries(Diagonal, Triangle, csw_t, cF, this->diag_mass); // Invert the Clover term // In case of the exponential clover with (anti-)periodic boundary conditions exp(-Clover) saved // in TmpInverse can be used. In all other cases the clover term has to be explictly inverted. // TODO: For now this inversion is explictly done on the CPU double t7 = usecond(); - CloverHelpers::InvertClover(TmpInverse, Diagonal, Triangle, DiagonalInv, TriangleInv, open_boundaries); + CloverHelpers::InvertClover(TmpInverse, Diagonal, Triangle, DiagonalInv, TriangleInv, fixedBoundaries); // Fill the remaining clover fields double t8 = usecond(); From 82e959f66cac74c3863eaa0b42f3c8b42ae874d8 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 8 Nov 2022 12:45:25 -0800 Subject: [PATCH 362/399] SYCL reduction --- Grid/lattice/Lattice_reduction.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Grid/lattice/Lattice_reduction.h b/Grid/lattice/Lattice_reduction.h index 0ddac437..fb6a258c 100644 --- a/Grid/lattice/Lattice_reduction.h +++ b/Grid/lattice/Lattice_reduction.h @@ -28,6 +28,9 @@ Author: Christoph Lehner <christoph@lhnr.de> #if defined(GRID_CUDA)||defined(GRID_HIP) #include <Grid/lattice/Lattice_reduction_gpu.h> #endif +#if defined(GRID_SYCL) +#include <Grid/lattice/Lattice_reduction_sycl.h> +#endif NAMESPACE_BEGIN(Grid); @@ -127,7 +130,7 @@ inline Double max(const Double *arg, Integer osites) template<class vobj> inline typename vobj::scalar_object sum(const vobj *arg, Integer osites) { -#if defined(GRID_CUDA)||defined(GRID_HIP) +#if defined(GRID_CUDA)||defined(GRID_HIP)||defined(GRID_SYCL) return sum_gpu(arg,osites); #else return sum_cpu(arg,osites); @@ -136,7 +139,7 @@ inline typename vobj::scalar_object sum(const vobj *arg, Integer osites) template<class vobj> inline typename vobj::scalar_objectD sumD(const vobj *arg, Integer osites) { -#if defined(GRID_CUDA)||defined(GRID_HIP) +#if defined(GRID_CUDA)||defined(GRID_HIP)||defined(GRID_SYCL) return sumD_gpu(arg,osites); #else return sumD_cpu(arg,osites); @@ -145,7 +148,7 @@ inline typename vobj::scalar_objectD sumD(const vobj *arg, Integer osites) template<class vobj> inline typename vobj::scalar_objectD sumD_large(const vobj *arg, Integer osites) { -#if defined(GRID_CUDA)||defined(GRID_HIP) +#if defined(GRID_CUDA)||defined(GRID_HIP)||defined(GRID_SYCL) return sumD_gpu_large(arg,osites); #else return sumD_cpu(arg,osites); @@ -155,13 +158,13 @@ inline typename vobj::scalar_objectD sumD_large(const vobj *arg, Integer osites) template<class vobj> inline typename vobj::scalar_object sum(const Lattice<vobj> &arg) { -#if defined(GRID_CUDA)||defined(GRID_HIP) - autoView( arg_v, arg, AcceleratorRead); Integer osites = arg.Grid()->oSites(); - auto ssum= sum_gpu(&arg_v[0],osites); +#if defined(GRID_CUDA)||defined(GRID_HIP)||defined(GRID_SYCL) + typename vobj::scalar_object ssum; + autoView( arg_v, arg, AcceleratorRead); + ssum= sum_gpu(&arg_v[0],osites); #else autoView(arg_v, arg, CpuRead); - Integer osites = arg.Grid()->oSites(); auto ssum= sum_cpu(&arg_v[0],osites); #endif arg.Grid()->GlobalSum(ssum); @@ -171,7 +174,7 @@ inline typename vobj::scalar_object sum(const Lattice<vobj> &arg) template<class vobj> inline typename vobj::scalar_object sum_large(const Lattice<vobj> &arg) { -#if defined(GRID_CUDA)||defined(GRID_HIP) +#if defined(GRID_CUDA)||defined(GRID_HIP)||defined(GRID_SYCL) autoView( arg_v, arg, AcceleratorRead); Integer osites = arg.Grid()->oSites(); auto ssum= sum_gpu_large(&arg_v[0],osites); @@ -235,11 +238,10 @@ inline ComplexD rankInnerProduct(const Lattice<vobj> &left,const Lattice<vobj> & typedef decltype(innerProductD(vobj(),vobj())) inner_t; Vector<inner_t> inner_tmp(sites); auto inner_tmp_v = &inner_tmp[0]; - { autoView( left_v , left, AcceleratorRead); autoView( right_v,right, AcceleratorRead); - + // This code could read coalesce // GPU - SIMT lane compliance... accelerator_for( ss, sites, 1,{ auto x_l = left_v[ss]; From bd68861b28b24ce85adcbcbe5a7c50b383446b3f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 8 Nov 2022 12:49:26 -0800 Subject: [PATCH 363/399] SYCL sum --- Grid/lattice/Lattice_reduction_sycl.h | 125 ++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 Grid/lattice/Lattice_reduction_sycl.h diff --git a/Grid/lattice/Lattice_reduction_sycl.h b/Grid/lattice/Lattice_reduction_sycl.h new file mode 100644 index 00000000..90980c4c --- /dev/null +++ b/Grid/lattice/Lattice_reduction_sycl.h @@ -0,0 +1,125 @@ +NAMESPACE_BEGIN(Grid); + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// Possibly promote to double and sum +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +template <class vobj> +inline typename vobj::scalar_objectD sumD_gpu_tensor(const vobj *lat, Integer osites) +{ + typedef typename vobj::scalar_object sobj; + typedef typename vobj::scalar_objectD sobjD; + sobj *mysum =(sobj *) malloc_shared(sizeof(sobj),*theGridAccelerator); + sobj identity; zeroit(identity); + sobj ret ; + + Integer nsimd= vobj::Nsimd(); + + theGridAccelerator->submit([&](cl::sycl::handler &cgh) { + auto Reduction = cl::sycl::reduction(mysum,identity,std::plus<>()); + cgh.parallel_for(cl::sycl::range<1>{osites}, + Reduction, + [=] (cl::sycl::id<1> item, auto &sum) { + auto osite = item[0]; + sum +=Reduce(lat[osite]); + }); + }); + theGridAccelerator->wait(); + ret = mysum[0]; + free(mysum,*theGridAccelerator); + sobjD dret; convertType(dret,ret); + return dret; +} + +template <class vobj> +inline typename vobj::scalar_objectD sumD_gpu_large(const vobj *lat, Integer osites) +{ + return sumD_gpu_tensor(lat,osites); +} +template <class vobj> +inline typename vobj::scalar_objectD sumD_gpu_small(const vobj *lat, Integer osites) +{ + return sumD_gpu_large(lat,osites); +} + +template <class vobj> +inline typename vobj::scalar_objectD sumD_gpu(const vobj *lat, Integer osites) +{ + return sumD_gpu_large(lat,osites); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// Return as same precision as input performing reduction in double precision though +///////////////////////////////////////////////////////////////////////////////////////////////////////// +template <class vobj> +inline typename vobj::scalar_object sum_gpu(const vobj *lat, Integer osites) +{ + typedef typename vobj::scalar_object sobj; + sobj result; + result = sumD_gpu(lat,osites); + return result; +} + +template <class vobj> +inline typename vobj::scalar_object sum_gpu_large(const vobj *lat, Integer osites) +{ + typedef typename vobj::scalar_object sobj; + sobj result; + result = sumD_gpu_large(lat,osites); + return result; +} + +NAMESPACE_END(Grid); + +/* +template<class Double> Double svm_reduce(Double *vec,uint64_t L) +{ + Double sumResult; zeroit(sumResult); + Double *d_sum =(Double *)cl::sycl::malloc_shared(sizeof(Double),*theGridAccelerator); + Double identity; zeroit(identity); + theGridAccelerator->submit([&](cl::sycl::handler &cgh) { + auto Reduction = cl::sycl::reduction(d_sum,identity,std::plus<>()); + cgh.parallel_for(cl::sycl::range<1>{L}, + Reduction, + [=] (cl::sycl::id<1> index, auto &sum) { + sum +=vec[index]; + }); + }); + theGridAccelerator->wait(); + Double ret = d_sum[0]; + free(d_sum,*theGridAccelerator); + std::cout << " svm_reduce finished "<<L<<" sites sum = " << ret <<std::endl; + return ret; +} + +template <class vobj> +inline typename vobj::scalar_objectD sumD_gpu_repack(const vobj *lat, Integer osites) +{ + typedef typename vobj::vector_type vector; + typedef typename vobj::scalar_type scalar; + + typedef typename vobj::scalar_typeD scalarD; + typedef typename vobj::scalar_objectD sobjD; + + sobjD ret; + scalarD *ret_p = (scalarD *)&ret; + + const int nsimd = vobj::Nsimd(); + const int words = sizeof(vobj)/sizeof(vector); + + Vector<scalar> buffer(osites*nsimd); + scalar *buf = &buffer[0]; + vector *dat = (vector *)lat; + + for(int w=0;w<words;w++) { + + accelerator_for(ss,osites,nsimd,{ + int lane = acceleratorSIMTlane(nsimd); + buf[ss*nsimd+lane] = dat[ss*words+w].getlane(lane); + }); + //Precision change at this point is to late to gain precision + ret_p[w] = svm_reduce(buf,nsimd*osites); + } + return ret; +} +*/ From 63fd1dfa62b86a2fae01f31ae6619c3ac6713119 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 8 Nov 2022 13:22:09 -0800 Subject: [PATCH 364/399] Config on PVC --- systems/PVC/config-command | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/systems/PVC/config-command b/systems/PVC/config-command index 3f5b5993..858edda7 100644 --- a/systems/PVC/config-command +++ b/systems/PVC/config-command @@ -2,14 +2,15 @@ INSTALL=/nfs/site/home/azusayax/install ../../configure \ --enable-simd=GPU \ --enable-gen-simd-width=64 \ - --enable-comms=mpi \ + --enable-comms=mpi-auto \ --disable-accelerator-cshift \ --disable-gparity \ --disable-fermion-reps \ --enable-shm=nvlink \ --enable-accelerator=sycl \ - --enable-unified=yes \ - CXX=mpicxx \ + --enable-unified=no \ + MPICXX=mpicxx \ + CXX=dpcpp \ LDFLAGS="-fsycl-device-code-split=per_kernel -fsycl-device-lib=all -lze_loader -L$INSTALL/lib" \ - CXXFLAGS="-cxx=dpcpp -fsycl-unnamed-lambda -fsycl -no-fma -I$INSTALL/include -Wtautological-constant-compare" + CXXFLAGS="-fsycl-unnamed-lambda -fsycl -no-fma -I$INSTALL/include -Wtautological-constant-compare" From 187310136226348c0cd4cf507a79f76eab7fed0b Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 8 Nov 2022 13:22:45 -0800 Subject: [PATCH 365/399] PVC --- systems/PVC/benchmarks/run-1tile.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 systems/PVC/benchmarks/run-1tile.sh diff --git a/systems/PVC/benchmarks/run-1tile.sh b/systems/PVC/benchmarks/run-1tile.sh old mode 100644 new mode 100755 index 923afd84..0fe80247 --- a/systems/PVC/benchmarks/run-1tile.sh +++ b/systems/PVC/benchmarks/run-1tile.sh @@ -6,7 +6,7 @@ source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh -export NT=16 +export NT=8 export I_MPI_OFFLOAD=1 export I_MPI_OFFLOAD_TOPOLIB=level_zero @@ -21,7 +21,7 @@ export I_MPI_OFFLOAD_CELL=tile export EnableImplicitScaling=0 export EnableWalkerPartition=0 export ZE_AFFINITY_MASK=0.0 -mpiexec -launcher ssh -n 1 -host localhost ./Benchmark_dwf_fp32 --mpi 1.1.1.1 --grid 32.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 --cacheblocking 8.8.8.8 +mpiexec -launcher ssh -n 1 -host localhost ./Benchmark_dwf_fp32 --mpi 1.1.1.1 --grid 32.32.32.32 --accelerator-threads $NT --comms-sequential --shm-mpi 1 --device-mem 32768 export ZE_AFFINITY_MASK=0 export I_MPI_OFFLOAD_CELL=device From 5c75aa50089bc53bc8a2a18dd16e8d6aab5041c4 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 8 Nov 2022 13:22:57 -0800 Subject: [PATCH 366/399] Device mem --- systems/PVC/benchmarks/run-2tile-mpi.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/systems/PVC/benchmarks/run-2tile-mpi.sh b/systems/PVC/benchmarks/run-2tile-mpi.sh index 34deac2c..cefab776 100755 --- a/systems/PVC/benchmarks/run-2tile-mpi.sh +++ b/systems/PVC/benchmarks/run-2tile-mpi.sh @@ -8,7 +8,6 @@ source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh export NT=16 - # export IGC_EnableLSCFenceUGMBeforeEOT=0 # export SYCL_PROGRAM_COMPILE_OPTIONS="-ze-opt-large-register-file=False" #export IGC_ShaderDumpEnable=1 @@ -20,14 +19,16 @@ export SYCL_DEVICE_FILTER=gpu,level_zero export I_MPI_OFFLOAD_CELL=tile export EnableImplicitScaling=0 export EnableWalkerPartition=0 -#export SYCL_PI_LEVEL_ZERO_DEVICE_SCOPE_EVENTS=1 +export SYCL_PI_LEVEL_ZERO_DEVICE_SCOPE_EVENTS=1 export SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1 -#export SYCL_PI_LEVEL_ZERO_USE_COPY_ENGINE=0 +export SYCL_PI_LEVEL_ZERO_USE_COPY_ENGINE=0 -mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 > dw.2tile.1x2.log -mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 > dw.2tile.2x1.log - -mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.1x2.log -mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.2x1.log +for i in 0 +do +mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 --device-mem 32768 +mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 --device-mem 32768 +done +#mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.1x2.log +#mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.2x1.log From 0655dab46659e21dfb7eaa4a4c947296e315fa5e Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 8 Nov 2022 13:38:54 -0800 Subject: [PATCH 367/399] Open MP on host enabled --- systems/PVC/config-command | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systems/PVC/config-command b/systems/PVC/config-command index 858edda7..cd7bba1d 100644 --- a/systems/PVC/config-command +++ b/systems/PVC/config-command @@ -12,5 +12,5 @@ INSTALL=/nfs/site/home/azusayax/install MPICXX=mpicxx \ CXX=dpcpp \ LDFLAGS="-fsycl-device-code-split=per_kernel -fsycl-device-lib=all -lze_loader -L$INSTALL/lib" \ - CXXFLAGS="-fsycl-unnamed-lambda -fsycl -no-fma -I$INSTALL/include -Wtautological-constant-compare" + CXXFLAGS="-fsycl-unnamed-lambda -fsycl -no-fma -I$INSTALL/include -Wno-tautological-compare" From e13930c8b228380b6556541c334a0ae40a97afae Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 30 Nov 2022 15:11:29 -0500 Subject: [PATCH 368/399] Faster fermtoprop case --- Grid/qcd/QCD.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Grid/qcd/QCD.h b/Grid/qcd/QCD.h index 858aead7..b292e560 100644 --- a/Grid/qcd/QCD.h +++ b/Grid/qcd/QCD.h @@ -451,9 +451,20 @@ template<class vobj> void pokeLorentz(vobj &lhs,const decltype(peekIndex<Lorentz // Fermion <-> propagator assignements ////////////////////////////////////////////// //template <class Prop, class Ferm> +#define FAST_FERM_TO_PROP template <class Fimpl> void FermToProp(typename Fimpl::PropagatorField &p, const typename Fimpl::FermionField &f, const int s, const int c) { +#ifdef FAST_FERM_TO_PROP + autoView(p_v,p,AcceleratorWrite); + autoView(f_v,f,AcceleratorRead); + accelerator_for(idx,p_v.oSites(),1,{ + for(int ss = 0; ss < Ns; ++ss) { + for(int cc = 0; cc < Fimpl::Dimension; ++cc) { + p_v[idx]()(ss,s)(cc,c) = f_v[idx]()(ss)(cc); // Propagator sink index is LEFT, suitable for left mult by gauge link (e.g.) + }} + ); +#else for(int j = 0; j < Ns; ++j) { auto pjs = peekSpin(p, j, s); @@ -465,12 +476,23 @@ void FermToProp(typename Fimpl::PropagatorField &p, const typename Fimpl::Fermio } pokeSpin(p, pjs, j, s); } +#endif } //template <class Prop, class Ferm> template <class Fimpl> void PropToFerm(typename Fimpl::FermionField &f, const typename Fimpl::PropagatorField &p, const int s, const int c) { +#ifdef FAST_FERM_TO_PROP + autoView(p_v,p,AcceleratorWrite); + autoView(f_v,f,AcceleratorRead); + accelerator_for(idx,p_v.oSites(),1,{ + for(int ss = 0; ss < Ns; ++ss) { + for(int cc = 0; cc < Fimpl::Dimension; ++cc) { + f_v[idx]()(ss)(cc) = p_v[idx]()(ss,s)(cc,c); // LEFT index is copied across for s,c right index + }} + ); +#else for(int j = 0; j < Ns; ++j) { auto pjs = peekSpin(p, j, s); @@ -482,6 +504,7 @@ void PropToFerm(typename Fimpl::FermionField &f, const typename Fimpl::Propagato } pokeSpin(f, fj, j); } +#endif } ////////////////////////////////////////////// From 97a098636d816970631b120877b85e13691dd8ff Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 30 Nov 2022 15:36:35 -0500 Subject: [PATCH 369/399] FermToProp --- Grid/qcd/QCD.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/QCD.h b/Grid/qcd/QCD.h index b292e560..9c56c12c 100644 --- a/Grid/qcd/QCD.h +++ b/Grid/qcd/QCD.h @@ -463,7 +463,7 @@ void FermToProp(typename Fimpl::PropagatorField &p, const typename Fimpl::Fermio for(int cc = 0; cc < Fimpl::Dimension; ++cc) { p_v[idx]()(ss,s)(cc,c) = f_v[idx]()(ss)(cc); // Propagator sink index is LEFT, suitable for left mult by gauge link (e.g.) }} - ); + }); #else for(int j = 0; j < Ns; ++j) { @@ -491,7 +491,7 @@ void PropToFerm(typename Fimpl::FermionField &f, const typename Fimpl::Propagato for(int cc = 0; cc < Fimpl::Dimension; ++cc) { f_v[idx]()(ss)(cc) = p_v[idx]()(ss,s)(cc,c); // LEFT index is copied across for s,c right index }} - ); + }); #else for(int j = 0; j < Ns; ++j) { From d49694f38f1b07676c4200892922c9a89bca99ad Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 6 Dec 2022 15:48:54 +0000 Subject: [PATCH 370/399] PropToFerm fix --- Grid/qcd/QCD.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/QCD.h b/Grid/qcd/QCD.h index 9c56c12c..39f45ada 100644 --- a/Grid/qcd/QCD.h +++ b/Grid/qcd/QCD.h @@ -484,8 +484,8 @@ template <class Fimpl> void PropToFerm(typename Fimpl::FermionField &f, const typename Fimpl::PropagatorField &p, const int s, const int c) { #ifdef FAST_FERM_TO_PROP - autoView(p_v,p,AcceleratorWrite); - autoView(f_v,f,AcceleratorRead); + autoView(p_v,p,AcceleratorRead); + autoView(f_v,f,AcceleratorWrite); accelerator_for(idx,p_v.oSites(),1,{ for(int ss = 0; ss < Ns; ++ss) { for(int cc = 0; cc < Fimpl::Dimension; ++cc) { From 40234f531ff83172cebbb7401916641d0df7ddd2 Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 6 Dec 2022 17:34:51 +0000 Subject: [PATCH 371/399] FermToProp accelerator_for -> thread_for --- Grid/qcd/QCD.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/QCD.h b/Grid/qcd/QCD.h index 39f45ada..6dd6d5dc 100644 --- a/Grid/qcd/QCD.h +++ b/Grid/qcd/QCD.h @@ -456,9 +456,9 @@ template <class Fimpl> void FermToProp(typename Fimpl::PropagatorField &p, const typename Fimpl::FermionField &f, const int s, const int c) { #ifdef FAST_FERM_TO_PROP - autoView(p_v,p,AcceleratorWrite); - autoView(f_v,f,AcceleratorRead); - accelerator_for(idx,p_v.oSites(),1,{ + autoView(p_v,p,CpuWrite); + autoView(f_v,f,CpuRead); + thread_for(idx,p_v.oSites(),{ for(int ss = 0; ss < Ns; ++ss) { for(int cc = 0; cc < Fimpl::Dimension; ++cc) { p_v[idx]()(ss,s)(cc,c) = f_v[idx]()(ss)(cc); // Propagator sink index is LEFT, suitable for left mult by gauge link (e.g.) @@ -484,9 +484,9 @@ template <class Fimpl> void PropToFerm(typename Fimpl::FermionField &f, const typename Fimpl::PropagatorField &p, const int s, const int c) { #ifdef FAST_FERM_TO_PROP - autoView(p_v,p,AcceleratorRead); - autoView(f_v,f,AcceleratorWrite); - accelerator_for(idx,p_v.oSites(),1,{ + autoView(p_v,p,CpuRead); + autoView(f_v,f,CpuWrite); + thread_for(idx,p_v.oSites(),{ for(int ss = 0; ss < Ns; ++ss) { for(int cc = 0; cc < Fimpl::Dimension; ++cc) { f_v[idx]()(ss)(cc) = p_v[idx]()(ss,s)(cc,c); // LEFT index is copied across for s,c right index From 281f8101fe8c679bfd52d149d53a669fdbd92c0d Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sat, 17 Dec 2022 20:35:33 -0500 Subject: [PATCH 372/399] Matt FFT test --- tests/core/Test_fft_matt.cc | 160 ++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 tests/core/Test_fft_matt.cc diff --git a/tests/core/Test_fft_matt.cc b/tests/core/Test_fft_matt.cc new file mode 100644 index 00000000..d4455a7e --- /dev/null +++ b/tests/core/Test_fft_matt.cc @@ -0,0 +1,160 @@ + /************************************************************************************* + + grid` physics library, www.github.com/paboyle/Grid + + Source file: ./tests/Test_cshift.cc + + Copyright (C) 2015 + +Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk> +Author: Peter Boyle <paboyle@ph.ed.ac.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include <Grid/Grid.h> + +using namespace Grid; + ; + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + int threads = GridThread::GetThreads(); + std::cout<<GridLogMessage << "Grid is setup to use "<<threads<<" threads"<<std::endl; + + Coordinate latt_size = GridDefaultLatt(); + Coordinate simd_layout = GridDefaultSimd(Nd,vComplexD::Nsimd()); + Coordinate mpi_layout = GridDefaultMpi(); + + int vol = 1; + for(int d=0;d<latt_size.size();d++){ + vol = vol * latt_size[d]; + } + GridCartesian GRID(latt_size,simd_layout,mpi_layout); + GridRedBlackCartesian RBGRID(&GRID); + + LatticeComplexD one(&GRID); + LatticeComplexD zz(&GRID); + LatticeComplexD C(&GRID); + LatticeComplexD Ctilde(&GRID); + LatticeComplexD Cref (&GRID); + LatticeComplexD Csav (&GRID); + LatticeComplexD coor(&GRID); + + LatticeSpinMatrixD S(&GRID); + LatticeSpinMatrixD Stilde(&GRID); + + Coordinate p({1,3,2,3}); + + one = ComplexD(1.0,0.0); + zz = ComplexD(0.0,0.0); + + ComplexD ci(0.0,1.0); + + std::vector<int> seeds({1,2,3,4}); + GridSerialRNG sRNG; sRNG.SeedFixedIntegers(seeds); // naughty seeding + GridParallelRNG pRNG(&GRID); + pRNG.SeedFixedIntegers(seeds); + + LatticeGaugeFieldD Umu(&GRID); + + SU<Nc>::ColdConfiguration(pRNG,Umu); // Unit gauge + + //////////////////////////////////////////////////// + // Wilson test + //////////////////////////////////////////////////// + { + LatticeFermionD src(&GRID); gaussian(pRNG,src); + LatticeFermionD tmp(&GRID); + LatticeFermionD ref(&GRID); + + RealD mass=0.01; + WilsonFermionD Dw(Umu,GRID,RBGRID,mass); + + Dw.M(src,tmp); + + std::cout << "Dw src = " <<norm2(src)<<std::endl; + std::cout << "Dw tmp = " <<norm2(tmp)<<std::endl; + + Dw.FreePropagator(tmp,ref,mass); + + std::cout << "Dw ref = " <<norm2(ref)<<std::endl; + + ref = ref - src; + + std::cout << "Dw ref-src = " <<norm2(ref)<<std::endl; + } + + + //////////////////////////////////////////////////// + // Wilson prop + //////////////////////////////////////////////////// + { + std::cout<<"****************************************"<<std::endl; + std::cout << "Wilson Mom space 4d propagator \n"; + std::cout<<"****************************************"<<std::endl; + + LatticeFermionD src(&GRID); gaussian(pRNG,src); + LatticeFermionD tmp(&GRID); + LatticeFermionD ref(&GRID); + LatticeFermionD diff(&GRID); + + src=Zero(); + Coordinate point(4,0); // 0,0,0,0 + SpinColourVectorD ferm; + ferm=Zero(); + ferm()(0)(0) = ComplexD(1.0); + pokeSite(ferm,src,point); + + RealD mass=0.01; + WilsonFermionD Dw(Umu,GRID,RBGRID,mass); + + // Momentum space prop + std::cout << " Solving by FFT and Feynman rules" <<std::endl; + Dw.FreePropagator(src,ref,mass) ; + + Gamma G5(Gamma::Algebra::Gamma5); + + LatticeFermionD result(&GRID); + const int sdir=0; + + //////////////////////////////////////////////////////////////////////// + // Conjugate gradient on normal equations system + //////////////////////////////////////////////////////////////////////// + std::cout << " Solving by Conjugate Gradient (CGNE)" <<std::endl; + Dw.Mdag(src,tmp); + src=tmp; + MdagMLinearOperator<WilsonFermionD,LatticeFermionD> HermOp(Dw); + ConjugateGradient<LatticeFermionD> CG(1.0e-10,10000); + CG(HermOp,src,result); + + //////////////////////////////////////////////////////////////////////// + std::cout << " Taking difference" <<std::endl; + std::cout << "Dw result "<<norm2(result)<<std::endl; + std::cout << "Dw ref "<<norm2(ref)<<std::endl; + + diff = ref - result; + std::cout << "result - ref "<<norm2(diff)<<std::endl; + + DumpSliceNorm("Slice Norm Solution ",result,Nd-1); + } + + + Grid_finalize(); +} From d8c29f5fcf321e4d1a4f0579cbdf698df481b2ce Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Sun, 18 Dec 2022 12:05:00 -0500 Subject: [PATCH 373/399] Updated FFT test for PETSc --- Grid/lattice/Lattice.h | 1 + Grid/lattice/Lattice_crc.h | 55 +++++++++++++++++++++ tests/core/Test_fft_matt.cc | 95 ++++++++++++++++++++++++++++--------- 3 files changed, 129 insertions(+), 22 deletions(-) create mode 100644 Grid/lattice/Lattice_crc.h diff --git a/Grid/lattice/Lattice.h b/Grid/lattice/Lattice.h index 9f5f1da7..c4adf86a 100644 --- a/Grid/lattice/Lattice.h +++ b/Grid/lattice/Lattice.h @@ -46,3 +46,4 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> #include <Grid/lattice/Lattice_unary.h> #include <Grid/lattice/Lattice_transfer.h> #include <Grid/lattice/Lattice_basis.h> +#include <Grid/lattice/Lattice_crc.h> diff --git a/Grid/lattice/Lattice_crc.h b/Grid/lattice/Lattice_crc.h new file mode 100644 index 00000000..142e2349 --- /dev/null +++ b/Grid/lattice/Lattice_crc.h @@ -0,0 +1,55 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/lattice/Lattice_crc.h + + Copyright (C) 2021 + +Author: Peter Boyle <paboyle@ph.ed.ac.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ +#pragma once + +NAMESPACE_BEGIN(Grid); + +template<class vobj> void DumpSliceNorm(std::string s,Lattice<vobj> &f,int mu=-1) +{ + auto ff = localNorm2(f); + if ( mu==-1 ) mu = f.Grid()->Nd()-1; + typedef typename vobj::tensor_reduced normtype; + typedef typename normtype::scalar_object scalar; + std::vector<scalar> sff; + sliceSum(ff,sff,mu); + for(int t=0;t<sff.size();t++){ + std::cout << s<<" "<<t<<" "<<sff[t]<<std::endl; + } +} + +template<class vobj> uint32_t crc(Lattice<vobj> & buf) +{ + autoView( buf_v , buf, CpuRead); + return ::crc32(0L,(unsigned char *)&buf_v[0],(size_t)sizeof(vobj)*buf.oSites()); +} + +#define CRC(U) std::cout << "FingerPrint "<<__FILE__ <<" "<< __LINE__ <<" "<< #U <<" "<<crc(U)<<std::endl; + +NAMESPACE_END(Grid); + + diff --git a/tests/core/Test_fft_matt.cc b/tests/core/Test_fft_matt.cc index d4455a7e..d92fa94e 100644 --- a/tests/core/Test_fft_matt.cc +++ b/tests/core/Test_fft_matt.cc @@ -1,5 +1,4 @@ /************************************************************************************* - grid` physics library, www.github.com/paboyle/Grid Source file: ./tests/Test_cshift.cc @@ -29,7 +28,14 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> #include <Grid/Grid.h> using namespace Grid; - ; + +Gamma::Algebra Gmu [] = { + Gamma::Algebra::GammaX, + Gamma::Algebra::GammaY, + Gamma::Algebra::GammaZ, + Gamma::Algebra::GammaT, + Gamma::Algebra::Gamma5 +}; int main (int argc, char ** argv) { @@ -49,22 +55,7 @@ int main (int argc, char ** argv) GridCartesian GRID(latt_size,simd_layout,mpi_layout); GridRedBlackCartesian RBGRID(&GRID); - LatticeComplexD one(&GRID); - LatticeComplexD zz(&GRID); - LatticeComplexD C(&GRID); - LatticeComplexD Ctilde(&GRID); - LatticeComplexD Cref (&GRID); - LatticeComplexD Csav (&GRID); LatticeComplexD coor(&GRID); - - LatticeSpinMatrixD S(&GRID); - LatticeSpinMatrixD Stilde(&GRID); - - Coordinate p({1,3,2,3}); - - one = ComplexD(1.0,0.0); - zz = ComplexD(0.0,0.0); - ComplexD ci(0.0,1.0); std::vector<int> seeds({1,2,3,4}); @@ -73,7 +64,6 @@ int main (int argc, char ** argv) pRNG.SeedFixedIntegers(seeds); LatticeGaugeFieldD Umu(&GRID); - SU<Nc>::ColdConfiguration(pRNG,Umu); // Unit gauge //////////////////////////////////////////////////// @@ -81,17 +71,78 @@ int main (int argc, char ** argv) //////////////////////////////////////////////////// { LatticeFermionD src(&GRID); gaussian(pRNG,src); + LatticeFermionD src_p(&GRID); LatticeFermionD tmp(&GRID); LatticeFermionD ref(&GRID); + LatticeFermionD result(&GRID); - RealD mass=0.01; + RealD mass=0.1; WilsonFermionD Dw(Umu,GRID,RBGRID,mass); - Dw.M(src,tmp); + Dw.M(src,ref); + std::cout << "Norm src "<<norm2(src)<<std::endl; + std::cout << "Norm Dw x src "<<norm2(ref)<<std::endl; + { + FFT theFFT(&GRID); + //////////////// + // operator in Fourier space + //////////////// + tmp =ref; + theFFT.FFT_all_dim(result,tmp,FFT::forward); + std::cout<<"FFT[ Dw x src ] "<< norm2(result)<<std::endl; + + tmp = src; + theFFT.FFT_all_dim(src_p,tmp,FFT::forward); + std::cout<<"FFT[ src ] "<< norm2(src_p)<<std::endl; + + ///////////////////////////////////////////////////////////////// + // work out the predicted FT from Fourier + ///////////////////////////////////////////////////////////////// + auto FGrid = &GRID; + LatticeFermionD Kinetic(FGrid); Kinetic = Zero(); + LatticeComplexD kmu(FGrid); + LatticeInteger scoor(FGrid); + LatticeComplexD sk (FGrid); sk = Zero(); + LatticeComplexD sk2(FGrid); sk2= Zero(); + LatticeComplexD W(FGrid); W= Zero(); + LatticeComplexD one(FGrid); one =ComplexD(1.0,0.0); + ComplexD ci(0.0,1.0); + + for(int mu=0;mu<Nd;mu++) { + + RealD TwoPiL = M_PI * 2.0/ latt_size[mu]; + + LatticeCoordinate(kmu,mu); + + kmu = TwoPiL * kmu; + + sk2 = sk2 + 2.0*sin(kmu*0.5)*sin(kmu*0.5); + sk = sk + sin(kmu) *sin(kmu); + + // -1/2 Dw -> 1/2 gmu (eip - emip) = i sinp gmu + Kinetic = Kinetic + sin(kmu)*ci*(Gamma(Gmu[mu])*src_p); + + } + + W = mass + sk2; + Kinetic = Kinetic + W * src_p; + + std::cout<<"Momentum space src "<< norm2(src_p)<<std::endl; + std::cout<<"Momentum space Dw x src "<< norm2(Kinetic)<<std::endl; + std::cout<<"FT[Coordinate space Dw] "<< norm2(result)<<std::endl; + + result = result - Kinetic; + std::cout<<"diff "<< norm2(result)<<std::endl; + + } + + std::cout << " =======================================" <<std::endl; + std::cout << " Checking FourierFreePropagator x Dw = 1" <<std::endl; + std::cout << " =======================================" <<std::endl; std::cout << "Dw src = " <<norm2(src)<<std::endl; std::cout << "Dw tmp = " <<norm2(tmp)<<std::endl; - + Dw.M(src,tmp); Dw.FreePropagator(tmp,ref,mass); std::cout << "Dw ref = " <<norm2(ref)<<std::endl; @@ -122,7 +173,7 @@ int main (int argc, char ** argv) ferm()(0)(0) = ComplexD(1.0); pokeSite(ferm,src,point); - RealD mass=0.01; + RealD mass=0.1; WilsonFermionD Dw(Umu,GRID,RBGRID,mass); // Momentum space prop From 3791bc527b56c800c7e039b0e124691b76a1580c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 30 Nov 2022 15:55:17 -0500 Subject: [PATCH 374/399] Logging pulled in from dirichlet branch --- Grid/allocator/MemoryManager.cc | 2 +- Grid/allocator/MemoryManager.h | 6 +++ Grid/allocator/MemoryManagerCache.cc | 69 ++++++++++++++++++++------- Grid/allocator/MemoryManagerShared.cc | 1 + Grid/log/Log.cc | 17 +++++-- Grid/log/Log.h | 11 +++-- 6 files changed, 83 insertions(+), 23 deletions(-) diff --git a/Grid/allocator/MemoryManager.cc b/Grid/allocator/MemoryManager.cc index ef02f6aa..d055898f 100644 --- a/Grid/allocator/MemoryManager.cc +++ b/Grid/allocator/MemoryManager.cc @@ -40,7 +40,7 @@ void MemoryManager::PrintBytes(void) ////////////////////////////////////////////////////////////////////// MemoryManager::AllocationCacheEntry MemoryManager::Entries[MemoryManager::NallocType][MemoryManager::NallocCacheMax]; int MemoryManager::Victim[MemoryManager::NallocType]; -int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 8, 2, 8, 2, 8 }; +int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 8, 8, 16, 8, 16 }; uint64_t MemoryManager::CacheBytes[MemoryManager::NallocType]; ////////////////////////////////////////////////////////////////////// // Actual allocation and deallocation utils diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index 740d8d92..ad2d9ffc 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -36,6 +36,11 @@ NAMESPACE_BEGIN(Grid); #define GRID_ALLOC_SMALL_LIMIT (4096) +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define FILE_LINE __FILE__ ":" TOSTRING(__LINE__) +#define AUDIT(a) MemoryManager::Audit(FILE_LINE) + /*Pinning pages is costly*/ //////////////////////////////////////////////////////////////////////////// // Advise the LatticeAccelerator class @@ -94,6 +99,7 @@ private: static void PrintBytes(void); public: + static void Audit(std::string s); static void Init(void); static void InitMessage(void); static void *AcceleratorAllocate(size_t bytes); diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index 04b3fe95..f03ee79f 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -3,8 +3,13 @@ #warning "Using explicit device memory copies" NAMESPACE_BEGIN(Grid); -//#define dprintf(...) printf ( __VA_ARGS__ ); fflush(stdout); -#define dprintf(...) + +#define MAXLINE 512 +static char print_buffer [ MAXLINE ]; + +#define mprintf(...) snprintf (print_buffer,MAXLINE, __VA_ARGS__ ); std::cout << GridLogMemory << print_buffer; +#define dprintf(...) snprintf (print_buffer,MAXLINE, __VA_ARGS__ ); std::cout << GridLogMemory << print_buffer; +//#define dprintf(...) //////////////////////////////////////////////////////////// @@ -104,7 +109,7 @@ void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache) /////////////////////////////////////////////////////////// assert(AccCache.state!=Empty); - dprintf("MemoryManager: Discard(%llx) %llx\n",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); + mprintf("MemoryManager: Discard(%lx) %lx\n",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); assert(AccCache.accLock==0); assert(AccCache.cpuLock==0); assert(AccCache.CpuPtr!=(uint64_t)NULL); @@ -112,7 +117,7 @@ void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache) AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes); DeviceBytes -=AccCache.bytes; LRUremove(AccCache); - dprintf("MemoryManager: Free(%llx) LRU %lld Total %lld\n",(uint64_t)AccCache.AccPtr,DeviceLRUBytes,DeviceBytes); + dprintf("MemoryManager: Free(%lx) LRU %ld Total %ld\n",(uint64_t)AccCache.AccPtr,DeviceLRUBytes,DeviceBytes); } uint64_t CpuPtr = AccCache.CpuPtr; EntryErase(CpuPtr); @@ -126,9 +131,11 @@ void MemoryManager::Evict(AcceleratorViewEntry &AccCache) /////////////////////////////////////////////////////////////////////////// assert(AccCache.state!=Empty); - dprintf("MemoryManager: Evict(%llx) %llx\n",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); - assert(AccCache.accLock==0); - assert(AccCache.cpuLock==0); + mprintf("MemoryManager: Evict cpu %lx acc %lx cpuLock %ld accLock %ld\n", + (uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr, + (uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock); + if (AccCache.accLock!=0) return; + if (AccCache.cpuLock!=0) return; if(AccCache.state==AccDirty) { Flush(AccCache); } @@ -137,7 +144,7 @@ void MemoryManager::Evict(AcceleratorViewEntry &AccCache) AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes); DeviceBytes -=AccCache.bytes; LRUremove(AccCache); - dprintf("MemoryManager: Free(%llx) footprint now %lld \n",(uint64_t)AccCache.AccPtr,DeviceBytes); + dprintf("MemoryManager: Free(%lx) footprint now %ld \n",(uint64_t)AccCache.AccPtr,DeviceBytes); } uint64_t CpuPtr = AccCache.CpuPtr; EntryErase(CpuPtr); @@ -150,7 +157,7 @@ void MemoryManager::Flush(AcceleratorViewEntry &AccCache) assert(AccCache.AccPtr!=(uint64_t)NULL); assert(AccCache.CpuPtr!=(uint64_t)NULL); acceleratorCopyFromDevice((void *)AccCache.AccPtr,(void *)AccCache.CpuPtr,AccCache.bytes); - dprintf("MemoryManager: Flush %llx -> %llx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); + mprintf("MemoryManager: Flush %lx -> %lx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); DeviceToHostBytes+=AccCache.bytes; DeviceToHostXfer++; AccCache.state=Consistent; @@ -165,7 +172,7 @@ void MemoryManager::Clone(AcceleratorViewEntry &AccCache) AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes); DeviceBytes+=AccCache.bytes; } - dprintf("MemoryManager: Clone %llx <- %llx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); + mprintf("MemoryManager: Clone %lx <- %lx\n",(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); acceleratorCopyToDevice((void *)AccCache.CpuPtr,(void *)AccCache.AccPtr,AccCache.bytes); HostToDeviceBytes+=AccCache.bytes; HostToDeviceXfer++; @@ -191,6 +198,7 @@ void MemoryManager::CpuDiscard(AcceleratorViewEntry &AccCache) void MemoryManager::ViewClose(void* Ptr,ViewMode mode) { if( (mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard) ){ + dprintf("AcceleratorViewClose %lx\n",(uint64_t)Ptr); AcceleratorViewClose((uint64_t)Ptr); } else if( (mode==CpuRead)||(mode==CpuWrite)){ CpuViewClose((uint64_t)Ptr); @@ -202,6 +210,7 @@ void *MemoryManager::ViewOpen(void* _CpuPtr,size_t bytes,ViewMode mode,ViewAdvis { uint64_t CpuPtr = (uint64_t)_CpuPtr; if( (mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard) ){ + dprintf("AcceleratorViewOpen %lx\n",(uint64_t)CpuPtr); return (void *) AcceleratorViewOpen(CpuPtr,bytes,mode,hint); } else if( (mode==CpuRead)||(mode==CpuWrite)){ return (void *)CpuViewOpen(CpuPtr,bytes,mode,hint); @@ -241,11 +250,12 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod assert(AccCache.cpuLock==0); // Programming error if(AccCache.state!=Empty) { - dprintf("ViewOpen found entry %llx %llx : %lld %lld\n", + dprintf("ViewOpen found entry %lx %lx : %ld %ld accLock %ld\n", (uint64_t)AccCache.CpuPtr, (uint64_t)CpuPtr, (uint64_t)AccCache.bytes, - (uint64_t)bytes); + (uint64_t)bytes, + (uint64_t)AccCache.accLock); assert(AccCache.CpuPtr == CpuPtr); assert(AccCache.bytes ==bytes); } @@ -280,6 +290,7 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod AccCache.state = Consistent; // Empty + AccRead => Consistent } AccCache.accLock= 1; + dprintf("Copied Empty entry into device accLock= %d\n",AccCache.accLock); } else if(AccCache.state==CpuDirty ){ if(mode==AcceleratorWriteDiscard) { CpuDiscard(AccCache); @@ -292,21 +303,21 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod AccCache.state = Consistent; // CpuDirty + AccRead => Consistent } AccCache.accLock++; - dprintf("Copied CpuDirty entry into device accLock %d\n",AccCache.accLock); + dprintf("CpuDirty entry into device ++accLock= %d\n",AccCache.accLock); } else if(AccCache.state==Consistent) { if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard)) AccCache.state = AccDirty; // Consistent + AcceleratorWrite=> AccDirty else AccCache.state = Consistent; // Consistent + AccRead => Consistent AccCache.accLock++; - dprintf("Consistent entry into device accLock %d\n",AccCache.accLock); + dprintf("Consistent entry into device ++accLock= %d\n",AccCache.accLock); } else if(AccCache.state==AccDirty) { if((mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard)) AccCache.state = AccDirty; // AccDirty + AcceleratorWrite=> AccDirty else AccCache.state = AccDirty; // AccDirty + AccRead => AccDirty AccCache.accLock++; - dprintf("AccDirty entry into device accLock %d\n",AccCache.accLock); + dprintf("AccDirty entry ++accLock= %d\n",AccCache.accLock); } else { assert(0); } @@ -314,6 +325,7 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod // If view is opened on device remove from LRU if(AccCache.LRU_valid==1){ // must possibly remove from LRU as now locked on GPU + dprintf("AccCache entry removed from LRU \n"); LRUremove(AccCache); } @@ -334,10 +346,12 @@ void MemoryManager::AcceleratorViewClose(uint64_t CpuPtr) assert(AccCache.accLock>0); AccCache.accLock--; - // Move to LRU queue if not locked and close on device if(AccCache.accLock==0) { + dprintf("AccleratorViewClose %lx AccLock decremented to %ld move to LRU queue\n",(uint64_t)CpuPtr,(uint64_t)AccCache.accLock); LRUinsert(AccCache); + } else { + dprintf("AccleratorViewClose %lx AccLock decremented to %ld\n",(uint64_t)CpuPtr,(uint64_t)AccCache.accLock); } } void MemoryManager::CpuViewClose(uint64_t CpuPtr) @@ -473,6 +487,29 @@ int MemoryManager::isOpen (void* _CpuPtr) return 0; } } +void MemoryManager::Audit(std::string s) +{ + for(auto it=AccViewTable.begin();it!=AccViewTable.end();it++){ + auto &AccCache = it->second; + + std::string str; + if ( AccCache.state==Empty ) str = std::string("Empty"); + if ( AccCache.state==CpuDirty ) str = std::string("CpuDirty"); + if ( AccCache.state==AccDirty ) str = std::string("AccDirty"); + if ( AccCache.state==Consistent)str = std::string("Consistent"); + + if ( AccCache.cpuLock || AccCache.accLock ) { + std::cout << GridLogError << s<< "\n\t 0x"<<std::hex<<AccCache.CpuPtr<<std::dec + << "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str + << "\t cpuLock " << AccCache.cpuLock + << "\t accLock " << AccCache.accLock + << "\t LRUvalid " << AccCache.LRU_valid<<std::endl; + } + + assert( AccCache.cpuLock== 0 ) ; + assert( AccCache.accLock== 0 ) ; + } +} void MemoryManager::PrintState(void* _CpuPtr) { diff --git a/Grid/allocator/MemoryManagerShared.cc b/Grid/allocator/MemoryManagerShared.cc index c072873b..2434ad47 100644 --- a/Grid/allocator/MemoryManagerShared.cc +++ b/Grid/allocator/MemoryManagerShared.cc @@ -13,6 +13,7 @@ uint64_t MemoryManager::DeviceToHostBytes; uint64_t MemoryManager::HostToDeviceXfer; uint64_t MemoryManager::DeviceToHostXfer; +void MemoryManager::Audit(void){}; void MemoryManager::ViewClose(void* AccPtr,ViewMode mode){}; void *MemoryManager::ViewOpen(void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint){ return CpuPtr; }; int MemoryManager::isOpen (void* CpuPtr) { return 0;} diff --git a/Grid/log/Log.cc b/Grid/log/Log.cc index 9302b4cc..166aea0a 100644 --- a/Grid/log/Log.cc +++ b/Grid/log/Log.cc @@ -65,29 +65,40 @@ GridLogger GridLogSolver (1, "Solver", GridLogColours, "NORMAL"); GridLogger GridLogError (1, "Error" , GridLogColours, "RED"); GridLogger GridLogWarning(1, "Warning", GridLogColours, "YELLOW"); GridLogger GridLogMessage(1, "Message", GridLogColours, "NORMAL"); +GridLogger GridLogMemory (1, "Memory", GridLogColours, "NORMAL"); +GridLogger GridLogTracing(1, "Tracing", GridLogColours, "NORMAL"); GridLogger GridLogDebug (1, "Debug", GridLogColours, "PURPLE"); GridLogger GridLogPerformance(1, "Performance", GridLogColours, "GREEN"); +GridLogger GridLogDslash (1, "Dslash", GridLogColours, "BLUE"); GridLogger GridLogIterative (1, "Iterative", GridLogColours, "BLUE"); GridLogger GridLogIntegrator (1, "Integrator", GridLogColours, "BLUE"); +GridLogger GridLogHMC (1, "HMC", GridLogColours, "BLUE"); void GridLogConfigure(std::vector<std::string> &logstreams) { - GridLogError.Active(0); + GridLogError.Active(1); GridLogWarning.Active(0); GridLogMessage.Active(1); // at least the messages should be always on + GridLogMemory.Active(0); + GridLogTracing.Active(0); GridLogIterative.Active(0); GridLogDebug.Active(0); GridLogPerformance.Active(0); + GridLogDslash.Active(0); GridLogIntegrator.Active(1); GridLogColours.Active(0); + GridLogHMC.Active(1); for (int i = 0; i < logstreams.size(); i++) { - if (logstreams[i] == std::string("Error")) GridLogError.Active(1); + if (logstreams[i] == std::string("Tracing")) GridLogTracing.Active(1); + if (logstreams[i] == std::string("Memory")) GridLogMemory.Active(1); if (logstreams[i] == std::string("Warning")) GridLogWarning.Active(1); if (logstreams[i] == std::string("NoMessage")) GridLogMessage.Active(0); if (logstreams[i] == std::string("Iterative")) GridLogIterative.Active(1); if (logstreams[i] == std::string("Debug")) GridLogDebug.Active(1); if (logstreams[i] == std::string("Performance")) GridLogPerformance.Active(1); - if (logstreams[i] == std::string("Integrator")) GridLogIntegrator.Active(1); + if (logstreams[i] == std::string("Dslash")) GridLogDslash.Active(1); + if (logstreams[i] == std::string("NoIntegrator"))GridLogIntegrator.Active(0); + if (logstreams[i] == std::string("NoHMC")) GridLogHMC.Active(0); if (logstreams[i] == std::string("Colours")) GridLogColours.Active(1); } } diff --git a/Grid/log/Log.h b/Grid/log/Log.h index 68693647..2d663a3c 100644 --- a/Grid/log/Log.h +++ b/Grid/log/Log.h @@ -138,7 +138,8 @@ public: stream << std::setw(log.topWidth); } stream << log.topName << log.background()<< " : "; - stream << log.colour() << std::left; + // stream << log.colour() << std::left; + stream << std::left; if (log.chanWidth > 0) { stream << std::setw(log.chanWidth); @@ -153,9 +154,9 @@ public: stream << log.evidence() << now << log.background() << " : " ; } - stream << log.colour(); + // stream << log.colour(); + stream << std::right; stream.flags(f); - return stream; } else { return devnull; @@ -180,8 +181,12 @@ extern GridLogger GridLogWarning; extern GridLogger GridLogMessage; extern GridLogger GridLogDebug ; extern GridLogger GridLogPerformance; +extern GridLogger GridLogDslash; extern GridLogger GridLogIterative ; extern GridLogger GridLogIntegrator ; +extern GridLogger GridLogHMC; +extern GridLogger GridLogMemory; +extern GridLogger GridLogTracing; extern Colours GridLogColours; std::string demangle(const char* name) ; From b00a4142e5f919847285caeac13187fe3f6e10b2 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 1 Dec 2022 00:18:11 -0500 Subject: [PATCH 375/399] A=A fix --- Grid/lattice/Lattice_base.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Grid/lattice/Lattice_base.h b/Grid/lattice/Lattice_base.h index 9c3d723f..34f13fa6 100644 --- a/Grid/lattice/Lattice_base.h +++ b/Grid/lattice/Lattice_base.h @@ -129,7 +129,7 @@ public: auto exprCopy = expr; ExpressionViewOpen(exprCopy); - auto me = View(AcceleratorWriteDiscard); + auto me = View(AcceleratorWrite); accelerator_for(ss,me.size(),vobj::Nsimd(),{ auto tmp = eval(ss,exprCopy); coalescedWrite(me[ss],tmp); @@ -152,7 +152,7 @@ public: auto exprCopy = expr; ExpressionViewOpen(exprCopy); - auto me = View(AcceleratorWriteDiscard); + auto me = View(AcceleratorWrite); accelerator_for(ss,me.size(),vobj::Nsimd(),{ auto tmp = eval(ss,exprCopy); coalescedWrite(me[ss],tmp); @@ -174,7 +174,7 @@ public: this->checkerboard=cb; auto exprCopy = expr; ExpressionViewOpen(exprCopy); - auto me = View(AcceleratorWriteDiscard); + auto me = View(AcceleratorWrite); accelerator_for(ss,me.size(),vobj::Nsimd(),{ auto tmp = eval(ss,exprCopy); coalescedWrite(me[ss],tmp); @@ -245,7 +245,7 @@ public: /////////////////////////////////////////// // user defined constructor /////////////////////////////////////////// - Lattice(GridBase *grid,ViewMode mode=AcceleratorWriteDiscard) { + Lattice(GridBase *grid,ViewMode mode=AcceleratorWrite) { this->_grid = grid; resize(this->_grid->oSites()); assert((((uint64_t)&this->_odata[0])&0xF) ==0); @@ -288,7 +288,7 @@ public: typename std::enable_if<!std::is_same<robj,vobj>::value,int>::type i=0; conformable(*this,r); this->checkerboard = r.Checkerboard(); - auto me = View(AcceleratorWriteDiscard); + auto me = View(AcceleratorWrite); auto him= r.View(AcceleratorRead); accelerator_for(ss,me.size(),vobj::Nsimd(),{ coalescedWrite(me[ss],him(ss)); @@ -303,7 +303,7 @@ public: inline Lattice<vobj> & operator = (const Lattice<vobj> & r){ this->checkerboard = r.Checkerboard(); conformable(*this,r); - auto me = View(AcceleratorWriteDiscard); + auto me = View(AcceleratorWrite); auto him= r.View(AcceleratorRead); accelerator_for(ss,me.size(),vobj::Nsimd(),{ coalescedWrite(me[ss],him(ss)); From 43a45ec97bf0212844dc2bc8df9774f534bc29ba Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 1 Dec 2022 00:18:43 -0500 Subject: [PATCH 376/399] SSC_START --- Grid/perfmon/PerfCount.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Grid/perfmon/PerfCount.h b/Grid/perfmon/PerfCount.h index dd25b41e..62b2a740 100644 --- a/Grid/perfmon/PerfCount.h +++ b/Grid/perfmon/PerfCount.h @@ -30,6 +30,12 @@ Author: paboyle <paboyle@ph.ed.ac.uk> #ifndef GRID_PERFCOUNT_H #define GRID_PERFCOUNT_H + +#ifndef __SSC_START +#define __SSC_START +#define __SSC_STOP +#endif + #include <sys/time.h> #include <ctime> #include <chrono> @@ -72,17 +78,9 @@ static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, inline uint64_t cyclecount(void){ return 0; } -#define __SSC_MARK(mark) __asm__ __volatile__ ("movl %0, %%ebx; .byte 0x64, 0x67, 0x90 " ::"i"(mark):"%ebx") -#define __SSC_STOP __SSC_MARK(0x110) -#define __SSC_START __SSC_MARK(0x111) - #else -#define __SSC_MARK(mark) -#define __SSC_STOP -#define __SSC_START - /* * cycle counters arch dependent */ From 99b3697b031b201a2dbe11c1942e672bfc846f05 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 1 Dec 2022 00:19:33 -0500 Subject: [PATCH 377/399] More loggin --- Grid/allocator/MemoryManager.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index ad2d9ffc..c22a54f3 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -97,8 +97,8 @@ private: static void *Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim,uint64_t &cbytes) ; static void *Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t &cbytes) ; - static void PrintBytes(void); public: + static void PrintBytes(void); static void Audit(std::string s); static void Init(void); static void InitMessage(void); @@ -119,6 +119,8 @@ private: static uint64_t DeviceToHostBytes; static uint64_t HostToDeviceXfer; static uint64_t DeviceToHostXfer; + static uint64_t DeviceEvictions; + static uint64_t DeviceDestroy; private: #ifndef GRID_UVM @@ -176,6 +178,7 @@ private: public: static void Print(void); + static void PrintAll(void); static void PrintState( void* CpuPtr); static int isOpen (void* CpuPtr); static void ViewClose(void* CpuPtr,ViewMode mode); From 37ba32776f410af38b43ddceff009be1c0cfcc62 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 1 Dec 2022 00:19:42 -0500 Subject: [PATCH 378/399] More logging --- Grid/allocator/MemoryManagerShared.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Grid/allocator/MemoryManagerShared.cc b/Grid/allocator/MemoryManagerShared.cc index 2434ad47..ba95420e 100644 --- a/Grid/allocator/MemoryManagerShared.cc +++ b/Grid/allocator/MemoryManagerShared.cc @@ -12,8 +12,10 @@ uint64_t MemoryManager::HostToDeviceBytes; uint64_t MemoryManager::DeviceToHostBytes; uint64_t MemoryManager::HostToDeviceXfer; uint64_t MemoryManager::DeviceToHostXfer; +uint64_t MemoryManager::DeviceEvictions; +uint64_t MemoryManager::DeviceDestroy; -void MemoryManager::Audit(void){}; +void MemoryManager::Audit(std::string s){}; void MemoryManager::ViewClose(void* AccPtr,ViewMode mode){}; void *MemoryManager::ViewOpen(void* CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint){ return CpuPtr; }; int MemoryManager::isOpen (void* CpuPtr) { return 0;} @@ -22,6 +24,7 @@ void MemoryManager::PrintState(void* CpuPtr) std::cout << GridLogMessage << "Host<->Device memory movement not currently managed by Grid." << std::endl; }; void MemoryManager::Print(void){}; +void MemoryManager::PrintAll(void){}; void MemoryManager::NotifyDeletion(void *ptr){}; NAMESPACE_END(Grid); From 1822ced302f08435035921438cb414c364a98898 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 1 Dec 2022 00:24:08 -0500 Subject: [PATCH 379/399] Bug fix --- Grid/allocator/MemoryManagerCache.cc | 116 ++++++++++++++++++++------- 1 file changed, 86 insertions(+), 30 deletions(-) diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index f03ee79f..3420c9cc 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -28,6 +28,8 @@ uint64_t MemoryManager::HostToDeviceBytes; uint64_t MemoryManager::DeviceToHostBytes; uint64_t MemoryManager::HostToDeviceXfer; uint64_t MemoryManager::DeviceToHostXfer; +uint64_t MemoryManager::DeviceEvictions; +uint64_t MemoryManager::DeviceDestroy; //////////////////////////////////// // Priority ordering for unlocked entries @@ -115,8 +117,10 @@ void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache) assert(AccCache.CpuPtr!=(uint64_t)NULL); if(AccCache.AccPtr) { AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes); + DeviceDestroy++; DeviceBytes -=AccCache.bytes; LRUremove(AccCache); + AccCache.AccPtr=(uint64_t) NULL; dprintf("MemoryManager: Free(%lx) LRU %ld Total %ld\n",(uint64_t)AccCache.AccPtr,DeviceLRUBytes,DeviceBytes); } uint64_t CpuPtr = AccCache.CpuPtr; @@ -126,28 +130,36 @@ void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache) void MemoryManager::Evict(AcceleratorViewEntry &AccCache) { /////////////////////////////////////////////////////////////////////////// - // Make CPU consistent, remove from Accelerator, remove entry - // Cannot be locked. If allocated must be in LRU pool. + // Make CPU consistent, remove from Accelerator, remove from LRU, LEAVE CPU only entry + // Cannot be acclocked. If allocated must be in LRU pool. + // + // Nov 2022... Felix issue: Allocating two CpuPtrs, can have an entry in LRU-q with CPUlock. + // and require to evict the AccPtr copy. Eviction was a mistake in CpuViewOpen + // but there is a weakness where CpuLock entries are attempted for erase + // Take these OUT LRU queue when CPU locked? + // Cannot take out the table as cpuLock data is important. /////////////////////////////////////////////////////////////////////////// assert(AccCache.state!=Empty); mprintf("MemoryManager: Evict cpu %lx acc %lx cpuLock %ld accLock %ld\n", (uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr, (uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock); - if (AccCache.accLock!=0) return; - if (AccCache.cpuLock!=0) return; + assert(AccCache.accLock==0); // Cannot evict so logic bomb + assert(AccCache.CpuPtr!=(uint64_t)NULL); if(AccCache.state==AccDirty) { Flush(AccCache); } - assert(AccCache.CpuPtr!=(uint64_t)NULL); if(AccCache.AccPtr) { AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes); - DeviceBytes -=AccCache.bytes; LRUremove(AccCache); + AccCache.AccPtr=(uint64_t)NULL; + AccCache.state=CpuDirty; // CPU primary now + DeviceBytes -=AccCache.bytes; dprintf("MemoryManager: Free(%lx) footprint now %ld \n",(uint64_t)AccCache.AccPtr,DeviceBytes); } - uint64_t CpuPtr = AccCache.CpuPtr; - EntryErase(CpuPtr); + // uint64_t CpuPtr = AccCache.CpuPtr; + DeviceEvictions++; + // EntryErase(CpuPtr); } void MemoryManager::Flush(AcceleratorViewEntry &AccCache) { @@ -221,13 +233,16 @@ void *MemoryManager::ViewOpen(void* _CpuPtr,size_t bytes,ViewMode mode,ViewAdvis } void MemoryManager::EvictVictims(uint64_t bytes) { + assert(bytes<DeviceMaxBytes); while(bytes+DeviceLRUBytes > DeviceMaxBytes){ if ( DeviceLRUBytes > 0){ assert(LRU.size()>0); - uint64_t victim = LRU.back(); + uint64_t victim = LRU.back(); // From the LRU auto AccCacheIterator = EntryLookup(victim); auto & AccCache = AccCacheIterator->second; Evict(AccCache); + } else { + return; } } } @@ -322,7 +337,8 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod assert(0); } - // If view is opened on device remove from LRU + assert(AccCache.accLock>0); + // If view is opened on device must remove from LRU if(AccCache.LRU_valid==1){ // must possibly remove from LRU as now locked on GPU dprintf("AccCache entry removed from LRU \n"); @@ -389,7 +405,7 @@ uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,V auto & AccCache = AccCacheIterator->second; if (!AccCache.AccPtr) { - EvictVictims(bytes); + EvictVictims(bytes); } assert((mode==CpuRead)||(mode==CpuWrite)); @@ -444,20 +460,28 @@ void MemoryManager::NotifyDeletion(void *_ptr) void MemoryManager::Print(void) { PrintBytes(); - std::cout << GridLogDebug << "--------------------------------------------" << std::endl; - std::cout << GridLogDebug << "Memory Manager " << std::endl; - std::cout << GridLogDebug << "--------------------------------------------" << std::endl; - std::cout << GridLogDebug << DeviceBytes << " bytes allocated on device " << std::endl; - std::cout << GridLogDebug << DeviceLRUBytes<< " bytes evictable on device " << std::endl; - std::cout << GridLogDebug << DeviceMaxBytes<< " bytes max on device " << std::endl; - std::cout << GridLogDebug << HostToDeviceXfer << " transfers to device " << std::endl; - std::cout << GridLogDebug << DeviceToHostXfer << " transfers from device " << std::endl; - std::cout << GridLogDebug << HostToDeviceBytes<< " bytes transfered to device " << std::endl; - std::cout << GridLogDebug << DeviceToHostBytes<< " bytes transfered from device " << std::endl; - std::cout << GridLogDebug << AccViewTable.size()<< " vectors " << LRU.size()<<" evictable"<< std::endl; - std::cout << GridLogDebug << "--------------------------------------------" << std::endl; - std::cout << GridLogDebug << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl; - std::cout << GridLogDebug << "--------------------------------------------" << std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; + std::cout << GridLogMessage << "Memory Manager " << std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; + std::cout << GridLogMessage << DeviceBytes << " bytes allocated on device " << std::endl; + std::cout << GridLogMessage << DeviceLRUBytes<< " bytes evictable on device " << std::endl; + std::cout << GridLogMessage << DeviceMaxBytes<< " bytes max on device " << std::endl; + std::cout << GridLogMessage << HostToDeviceXfer << " transfers to device " << std::endl; + std::cout << GridLogMessage << DeviceToHostXfer << " transfers from device " << std::endl; + std::cout << GridLogMessage << HostToDeviceBytes<< " bytes transfered to device " << std::endl; + std::cout << GridLogMessage << DeviceToHostBytes<< " bytes transfered from device " << std::endl; + std::cout << GridLogMessage << DeviceEvictions << " Evictions from device " << std::endl; + std::cout << GridLogMessage << DeviceDestroy << " Destroyed vectors on device " << std::endl; + std::cout << GridLogMessage << AccViewTable.size()<< " vectors " << LRU.size()<<" evictable"<< std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; +} +void MemoryManager::PrintAll(void) +{ + Print(); + std::cout << GridLogMessage << std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; + std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; for(auto it=AccViewTable.begin();it!=AccViewTable.end();it++){ auto &AccCache = it->second; @@ -467,13 +491,13 @@ void MemoryManager::Print(void) if ( AccCache.state==AccDirty ) str = std::string("AccDirty"); if ( AccCache.state==Consistent)str = std::string("Consistent"); - std::cout << GridLogDebug << "0x"<<std::hex<<AccCache.CpuPtr<<std::dec + std::cout << GridLogMessage << "0x"<<std::hex<<AccCache.CpuPtr<<std::dec << "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str << "\t" << AccCache.cpuLock << "\t" << AccCache.accLock << "\t" << AccCache.LRU_valid<<std::endl; } - std::cout << GridLogDebug << "--------------------------------------------" << std::endl; + std::cout << GridLogMessage << "--------------------------------------------" << std::endl; }; int MemoryManager::isOpen (void* _CpuPtr) @@ -489,6 +513,24 @@ int MemoryManager::isOpen (void* _CpuPtr) } void MemoryManager::Audit(std::string s) { + uint64_t CpuBytes=0; + uint64_t AccBytes=0; + uint64_t LruBytes1=0; + uint64_t LruBytes2=0; + uint64_t LruCnt=0; + uint64_t LockedBytes=0; + + std::cout << " Memory Manager::Audit() from "<<s<<std::endl; + for(auto it=LRU.begin();it!=LRU.end();it++){ + uint64_t cpuPtr = *it; + assert(EntryPresent(cpuPtr)); + auto AccCacheIterator = EntryLookup(cpuPtr); + auto & AccCache = AccCacheIterator->second; + LruBytes2+=AccCache.bytes; + assert(AccCache.LRU_valid==1); + assert(AccCache.LRU_entry==it); + } + std::cout << " Memory Manager::Audit() LRU queue matches table entries "<<std::endl; for(auto it=AccViewTable.begin();it!=AccViewTable.end();it++){ auto &AccCache = it->second; @@ -498,7 +540,13 @@ void MemoryManager::Audit(std::string s) if ( AccCache.state==AccDirty ) str = std::string("AccDirty"); if ( AccCache.state==Consistent)str = std::string("Consistent"); - if ( AccCache.cpuLock || AccCache.accLock ) { + CpuBytes+=AccCache.bytes; + if( AccCache.AccPtr ) AccBytes+=AccCache.bytes; + if( AccCache.LRU_valid ) LruBytes1+=AccCache.bytes; + if( AccCache.LRU_valid ) LruCnt++; + + if ( AccCache.cpuLock || AccCache.accLock ) { + assert(AccCache.LRU_valid==0); std::cout << GridLogError << s<< "\n\t 0x"<<std::hex<<AccCache.CpuPtr<<std::dec << "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str << "\t cpuLock " << AccCache.cpuLock @@ -509,6 +557,14 @@ void MemoryManager::Audit(std::string s) assert( AccCache.cpuLock== 0 ) ; assert( AccCache.accLock== 0 ) ; } + std::cout << " Memory Manager::Audit() no locked table entries "<<std::endl; + assert(LruBytes1==LruBytes2); + assert(LruBytes1==DeviceLRUBytes); + std::cout << " Memory Manager::Audit() evictable bytes matches sum over table "<<std::endl; + assert(AccBytes==DeviceBytes); + std::cout << " Memory Manager::Audit() device bytes matches sum over table "<<std::endl; + assert(LruCnt == LRU.size()); + std::cout << " Memory Manager::Audit() LRU entry count matches "<<std::endl; } void MemoryManager::PrintState(void* _CpuPtr) @@ -526,8 +582,8 @@ void MemoryManager::PrintState(void* _CpuPtr) if ( AccCache.state==EvictNext) str = std::string("EvictNext"); std::cout << GridLogMessage << "CpuAddr\t\tAccAddr\t\tState\t\tcpuLock\taccLock\tLRU_valid "<<std::endl; - std::cout << GridLogMessage << "0x"<<std::hex<<AccCache.CpuPtr<<std::dec - << "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str + std::cout << GridLogMessage << "\tx"<<std::hex<<AccCache.CpuPtr<<std::dec + << "\tx"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str << "\t" << AccCache.cpuLock << "\t" << AccCache.accLock << "\t" << AccCache.LRU_valid<<std::endl; From ede02b688392c2716c121e7b18f6b7d67954db25 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 1 Dec 2022 00:25:04 -0500 Subject: [PATCH 380/399] Memory manager debug Felix case --- Grid/perfmon/PerfCount.cc | 5 +- Grid/perfmon/Timer.h | 22 +++--- Grid/perfmon/Tracing.h | 70 ++++++++++++++++++ systems/mac-arm/config-command-mpi | 2 +- tests/core/Test_memory_manager.cc | 110 +++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 13 deletions(-) create mode 100644 Grid/perfmon/Tracing.h create mode 100644 tests/core/Test_memory_manager.cc diff --git a/Grid/perfmon/PerfCount.cc b/Grid/perfmon/PerfCount.cc index 2062bb59..114c36a0 100644 --- a/Grid/perfmon/PerfCount.cc +++ b/Grid/perfmon/PerfCount.cc @@ -27,10 +27,13 @@ Author: paboyle <paboyle@ph.ed.ac.uk> /* END LEGAL */ #include <Grid/GridCore.h> -#include <Grid/perfmon/PerfCount.h> +#include <Grid/perfmon/Timer.h> +#include <Grid/perfmon/PerfCount.h> NAMESPACE_BEGIN(Grid); +GridTimePoint theProgramStart = GridClock::now(); + #define CacheControl(L,O,R) ((PERF_COUNT_HW_CACHE_##L)|(PERF_COUNT_HW_CACHE_OP_##O<<8)| (PERF_COUNT_HW_CACHE_RESULT_##R<<16)) #define RawConfig(A,B) (A<<8|B) const PerformanceCounter::PerformanceCounterConfig PerformanceCounter::PerformanceCounterConfigs [] = { diff --git a/Grid/perfmon/Timer.h b/Grid/perfmon/Timer.h index 2a44faee..ba5df85a 100644 --- a/Grid/perfmon/Timer.h +++ b/Grid/perfmon/Timer.h @@ -35,17 +35,8 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk> NAMESPACE_BEGIN(Grid) -// Dress the output; use std::chrono -// C++11 time facilities better? -inline double usecond(void) { - struct timeval tv; -#ifdef TIMERS_ON - gettimeofday(&tv,NULL); -#endif - return 1.0*tv.tv_usec + 1.0e6*tv.tv_sec; -} - -typedef std::chrono::system_clock GridClock; +//typedef std::chrono::system_clock GridClock; +typedef std::chrono::high_resolution_clock GridClock; typedef std::chrono::time_point<GridClock> GridTimePoint; typedef std::chrono::seconds GridSecs; @@ -53,6 +44,15 @@ typedef std::chrono::milliseconds GridMillisecs; typedef std::chrono::microseconds GridUsecs; typedef std::chrono::microseconds GridTime; +extern GridTimePoint theProgramStart; +// Dress the output; use std::chrono +// C++11 time facilities better? +inline double usecond(void) { + auto usecs = std::chrono::duration_cast<GridUsecs>(GridClock::now()-theProgramStart); + return 1.0*usecs.count(); +} + + inline std::ostream& operator<< (std::ostream & stream, const GridSecs & time) { stream << time.count()<<" s"; diff --git a/Grid/perfmon/Tracing.h b/Grid/perfmon/Tracing.h new file mode 100644 index 00000000..5000cef4 --- /dev/null +++ b/Grid/perfmon/Tracing.h @@ -0,0 +1,70 @@ +#pragma once + +NAMESPACE_BEGIN(Grid); + +#ifdef GRID_TRACING_NVTX +#include <nvToolsExt.h> +class GridTracer { +public: + GridTracer(const char* name) { + nvtxRangePushA(name); + } + ~GridTracer() { + nvtxRangePop(); + } +}; +inline void tracePush(const char *name) { nvtxRangePushA(name); } +inline void tracePop(const char *name) { nvtxRangePop(); } +inline int traceStart(const char *name) { } +inline void traceStop(int ID) { } +#endif + +#ifdef GRID_TRACING_ROCTX +#include <roctracer/roctx.h> +class GridTracer { + public: + GridTracer(const char* name) { + roctxRangePushA(name); + std::cout << "roctxRangePush "<<name<<std::endl; + } + ~GridTracer() { + roctxRangePop(); + std::cout << "roctxRangePop "<<std::endl; + } +}; +inline void tracePush(const char *name) { roctxRangePushA(name); } +inline void tracePop(const char *name) { roctxRangePop(); } +inline int traceStart(const char *name) { roctxRangeStart(name); } +inline void traceStop(int ID) { roctxRangeStop(ID); } +#endif + +#ifdef GRID_TRACING_TIMER +class GridTracer { + public: + const char *name; + double elapsed; + GridTracer(const char* _name) { + name = _name; + elapsed=-usecond(); + } + ~GridTracer() { + elapsed+=usecond(); + std::cout << GridLogTracing << name << " took " <<elapsed<< " us" <<std::endl; + } +}; +inline void tracePush(const char *name) { } +inline void tracePop(const char *name) { } +inline int traceStart(const char *name) { return 0; } +inline void traceStop(int ID) { } +#endif + +#ifdef GRID_TRACING_NONE +#define GRID_TRACE(name) +inline void tracePush(const char *name) { } +inline void tracePop(const char *name) { } +inline int traceStart(const char *name) { return 0; } +inline void traceStop(int ID) { } +#else +#define GRID_TRACE(name) GridTracer uniq_name_using_macros##__COUNTER__(name); +#endif +NAMESPACE_END(Grid); diff --git a/systems/mac-arm/config-command-mpi b/systems/mac-arm/config-command-mpi index d1e75c39..a900fc2d 100644 --- a/systems/mac-arm/config-command-mpi +++ b/systems/mac-arm/config-command-mpi @@ -1 +1 @@ -CXX=mpicxx-openmpi-mp CXXFLAGS=-I/opt/local/include/ LDFLAGS=-L/opt/local/lib/ ../../configure --enable-simd=GEN --enable-debug --enable-comms=mpi +CXX=mpicxx-openmpi-mp CXXFLAGS=-I/opt/local/include/ LDFLAGS=-L/opt/local/lib/ ../../configure --enable-simd=GEN --enable-debug --enable-comms=mpi --enable-unified=no diff --git a/tests/core/Test_memory_manager.cc b/tests/core/Test_memory_manager.cc new file mode 100644 index 00000000..fb01dfe6 --- /dev/null +++ b/tests/core/Test_memory_manager.cc @@ -0,0 +1,110 @@ + /************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/Test_memory_manager.cc + + Copyright (C) 2022 + +Author: Peter Boyle <pboyle@bnl.gov> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ + /* END LEGAL */ +#include <Grid/Grid.h> + +using namespace std; +using namespace Grid; + +void MemoryTest(GridCartesian * FGrid,int N); + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi()); + + int N=100; + for(int i=0;i<N;i++){ + std::cout << "============================"<<std::endl; + std::cout << "Epoch "<<i<<"/"<<N<<std::endl; + std::cout << "============================"<<std::endl; + MemoryTest(UGrid,256); + MemoryManager::Print(); + AUDIT(); + } + Grid_finalize(); +} + +void MemoryTest(GridCartesian * FGrid, int N) +{ + LatticeComplexD zero(FGrid); zero=Zero(); + std::vector<LatticeComplexD> A(N,zero);//FGrid); + + std::vector<ComplexD> B(N,ComplexD(0.0)); // Update sequentially on host + + for(int v=0;v<N;v++) A[v] = Zero(); + + uint64_t counter = 0; + for(int epoch = 0;epoch<10000;epoch++){ + + int v = random() %N; // Which vec + int w = random() %2; // Write or read + int e = random() %3; // expression or for loop + int dev= random() %2; // On device? + // int e=1; + ComplexD zc = counter++; + + if ( w ) { + B[v] = B[v] + zc; + if ( e == 0 ) { + A[v] = A[v] + zc - A[v] + A[v]; + } else { + if ( dev ) { + autoView(A_v,A[v],AcceleratorWrite); + accelerator_for(ss,FGrid->oSites(),1,{ + A_v[ss] = A_v[ss] + zc; + }); + } else { + autoView(A_v,A[v],CpuWrite); + thread_for(ss,FGrid->oSites(),{ + A_v[ss] = A_v[ss] + zc; + }); + } + } + } else { + if ( e == 0 ) { + A[v] = A[v] + A[v] - A[v]; + } else { + if ( dev ) { + autoView(A_v,A[v],AcceleratorRead); + accelerator_for(ss,FGrid->oSites(),1,{ + assert(B[v]==A_v[ss]()()().getlane(0)); + }); + // std::cout << "["<<v<<"] checked on GPU"<<B[v]<<std::endl; + } else { + autoView(A_v,A[v],CpuRead); + thread_for(ss,FGrid->oSites(),{ + assert(B[v]==A_v[ss]()()().getlane(0)); + }); + // std::cout << "["<<v<<"] checked on CPU"<<B[v]<<std::endl; + } + } + } + } + +} From 2ff868f7a5ce038e2c9614870edc48babe05793c Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 1 Dec 2022 00:35:05 -0500 Subject: [PATCH 381/399] CPU open doesn't need to free space --- Grid/allocator/MemoryManagerCache.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Grid/allocator/MemoryManagerCache.cc b/Grid/allocator/MemoryManagerCache.cc index 3420c9cc..bae184ec 100644 --- a/Grid/allocator/MemoryManagerCache.cc +++ b/Grid/allocator/MemoryManagerCache.cc @@ -404,9 +404,10 @@ uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,V auto AccCacheIterator = EntryLookup(CpuPtr); auto & AccCache = AccCacheIterator->second; - if (!AccCache.AccPtr) { - EvictVictims(bytes); - } + // CPU doesn't need to free space + // if (!AccCache.AccPtr) { + // EvictVictims(bytes); + // } assert((mode==CpuRead)||(mode==CpuWrite)); assert(AccCache.accLock==0); // Programming error From 4ca1bf7ccaaa471a51994564adb5e445bc62afd5 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 21 Dec 2022 07:23:16 -0500 Subject: [PATCH 382/399] Added gauge invariance test --- tests/core/Test_fft_matt.cc | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/core/Test_fft_matt.cc b/tests/core/Test_fft_matt.cc index d92fa94e..55234601 100644 --- a/tests/core/Test_fft_matt.cc +++ b/tests/core/Test_fft_matt.cc @@ -206,6 +206,65 @@ int main (int argc, char ** argv) DumpSliceNorm("Slice Norm Solution ",result,Nd-1); } + //////////////////////////////////////////////////// + //Gauge invariance test + //////////////////////////////////////////////////// + { + std::cout<<"****************************************"<<std::endl; + std::cout << "Gauge invariance test \n"; + std::cout<<"****************************************"<<std::endl; + LatticeGaugeField U_GT(&GRID); // Gauge transformed field + LatticeColourMatrix g(&GRID); // local Gauge xform matrix + U_GT = Umu; + // Make a random xform to teh gauge field + SU<Nc>::RandomGaugeTransform(pRNG,U_GT,g); // Unit gauge + + LatticeFermionD src(&GRID); + LatticeFermionD tmp(&GRID); + LatticeFermionD ref(&GRID); + LatticeFermionD diff(&GRID); + + // could loop over colors + src=Zero(); + Coordinate point(4,0); // 0,0,0,0 + SpinColourVectorD ferm; + ferm=Zero(); + ferm()(0)(0) = ComplexD(1.0); + pokeSite(ferm,src,point); + + RealD mass=0.1; + WilsonFermionD Dw(U_GT,GRID,RBGRID,mass); + + // Momentum space prop + std::cout << " Solving by FFT and Feynman rules" <<std::endl; + Dw.FreePropagator(src,ref,mass) ; + + Gamma G5(Gamma::Algebra::Gamma5); + + LatticeFermionD result(&GRID); + const int sdir=0; + + //////////////////////////////////////////////////////////////////////// + // Conjugate gradient on normal equations system + //////////////////////////////////////////////////////////////////////// + std::cout << " Solving by Conjugate Gradient (CGNE)" <<std::endl; + Dw.Mdag(src,tmp); + src=tmp; + MdagMLinearOperator<WilsonFermionD,LatticeFermionD> HermOp(Dw); + ConjugateGradient<LatticeFermionD> CG(1.0e-10,10000); + CG(HermOp,src,result); + + //////////////////////////////////////////////////////////////////////// + std::cout << " Taking difference" <<std::endl; + std::cout << "Dw result "<<norm2(result)<<std::endl; + std::cout << "Dw ref "<<norm2(ref)<<std::endl; + + diff = ref - result; + std::cout << "result - ref "<<norm2(diff)<<std::endl; + + DumpSliceNorm("Slice Norm Solution ",result,Nd-1); + } + Grid_finalize(); } From 88015b08588e040abcb888041bd3f63187307834 Mon Sep 17 00:00:00 2001 From: Christoph Lehner <christoph@lhnr.de> Date: Mon, 26 Dec 2022 10:01:32 +0100 Subject: [PATCH 383/399] Split sum in rankSum and GlobalSum --- Grid/lattice/Lattice_reduction.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Grid/lattice/Lattice_reduction.h b/Grid/lattice/Lattice_reduction.h index 326b9ea3..d9025de0 100644 --- a/Grid/lattice/Lattice_reduction.h +++ b/Grid/lattice/Lattice_reduction.h @@ -144,17 +144,23 @@ inline typename vobj::scalar_objectD sumD(const vobj *arg, Integer osites) } template<class vobj> -inline typename vobj::scalar_object sum(const Lattice<vobj> &arg) +inline typename vobj::scalar_object rankSum(const Lattice<vobj> &arg) { #if defined(GRID_CUDA)||defined(GRID_HIP) autoView( arg_v, arg, AcceleratorRead); Integer osites = arg.Grid()->oSites(); - auto ssum= sum_gpu(&arg_v[0],osites); + return sum_gpu(&arg_v[0],osites); #else autoView(arg_v, arg, CpuRead); Integer osites = arg.Grid()->oSites(); - auto ssum= sum_cpu(&arg_v[0],osites); + return sum_cpu(&arg_v[0],osites); #endif +} + +template<class vobj> +inline typename vobj::scalar_object sum(const Lattice<vobj> &arg) +{ + auto ssum = rankSum(arg); arg.Grid()->GlobalSum(ssum); return ssum; } From 7d62f1d6d20790f681f37fe1755e3712d2a4e2b0 Mon Sep 17 00:00:00 2001 From: Makis Kappas <makis.kappas@gmail.com> Date: Wed, 11 Jan 2023 21:26:25 +0000 Subject: [PATCH 384/399] Populate the Cshift_table in the GPU Cshift is allocated in Unified memory and used in the LambdaApply kernels but also populated from the host. This creates a lot of Unified HtoD and DtoH mem operations and has a negative effect in performance. With this commit we populate the Cshift table in the device with the populate_Cshift_table() kernel. --- Grid/cshift/Cshift_common.h | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Grid/cshift/Cshift_common.h b/Grid/cshift/Cshift_common.h index cf902b58..742c99da 100644 --- a/Grid/cshift/Cshift_common.h +++ b/Grid/cshift/Cshift_common.h @@ -297,6 +297,30 @@ template<class vobj> void Scatter_plane_merge(Lattice<vobj> &rhs,ExtractPointerA } } +#if (defined(GRID_CUDA) || defined(GRID_HIP)) && defined(ACCELERATOR_CSHIFT) + +template <typename T> +T iDivUp(T a, T b) // Round a / b to nearest higher integer value +{ return (a % b != 0) ? (a / b + 1) : (a / b); } + +template <typename T> +__global__ void populate_Cshift_table(T* vector, T lo, T ro, T e1, T e2, T stride) +{ + int idx = blockIdx.x*blockDim.x + threadIdx.x; + if (idx >= e1*e2) return; + + int n, b, o; + + n = idx / e2; + b = idx % e2; + o = n*stride + b; + + vector[2*idx + 0] = lo + o; + vector[2*idx + 1] = ro + o; +} + +#endif + ////////////////////////////////////////////////////// // local to node block strided copies ////////////////////////////////////////////////////// @@ -321,12 +345,20 @@ template<class vobj> void Copy_plane(Lattice<vobj>& lhs,const Lattice<vobj> &rhs int ent=0; if(cbmask == 0x3 ){ +#if (defined(GRID_CUDA) || defined(GRID_HIP)) && defined(ACCELERATOR_CSHIFT) + ent = e1*e2; + dim3 blockSize(acceleratorThreads()); + dim3 gridSize(iDivUp((unsigned int)ent, blockSize.x)); + populate_Cshift_table<<<gridSize, blockSize>>>(&Cshift_table[0].first, lo, ro, e1, e2, stride); + accelerator_barrier(); +#else for(int n=0;n<e1;n++){ for(int b=0;b<e2;b++){ int o =n*stride+b; Cshift_table[ent++] = std::pair<int,int>(lo+o,ro+o); } } +#endif } else { for(int n=0;n<e1;n++){ for(int b=0;b<e2;b++){ @@ -377,11 +409,19 @@ template<class vobj> void Copy_plane_permute(Lattice<vobj>& lhs,const Lattice<vo int ent=0; if ( cbmask == 0x3 ) { +#if (defined(GRID_CUDA) || defined(GRID_HIP)) && defined(ACCELERATOR_CSHIFT) + ent = e1*e2; + dim3 blockSize(acceleratorThreads()); + dim3 gridSize(iDivUp((unsigned int)ent, blockSize.x)); + populate_Cshift_table<<<gridSize, blockSize>>>(&Cshift_table[0].first, lo, ro, e1, e2, stride); + accelerator_barrier(); +#else for(int n=0;n<e1;n++){ for(int b=0;b<e2;b++){ int o =n*stride; Cshift_table[ent++] = std::pair<int,int>(lo+o+b,ro+o+b); }} +#endif } else { for(int n=0;n<e1;n++){ for(int b=0;b<e2;b++){ From ad0270ac8c6f55f756ad3d3e70047178f6f1ec55 Mon Sep 17 00:00:00 2001 From: Fabian Joswig <fabian.joswig@ed.ac.uk> Date: Thu, 12 Jan 2023 12:36:30 +0000 Subject: [PATCH 385/399] fix: diagnostic pragma warnings fixed for CUDA 12+ --- Grid/DisableWarnings.h | 2 +- Grid/Grid_Eigen_Dense.h | 2 +- Grid/pugixml/pugixml.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/DisableWarnings.h b/Grid/DisableWarnings.h index 64e4faf4..015e19d1 100644 --- a/Grid/DisableWarnings.h +++ b/Grid/DisableWarnings.h @@ -45,7 +45,7 @@ directory //disables nvcc specific warning in json.hpp #pragma clang diagnostic ignored "-Wdeprecated-register" -#if (__CUDACC_VER_MAJOR__ >= 11) && (__CUDACC_VER_MINOR__ >= 5) +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ //disables nvcc specific warning in json.hpp #pragma nv_diag_suppress unsigned_compare_with_zero #pragma nv_diag_suppress cast_to_qualified_type diff --git a/Grid/Grid_Eigen_Dense.h b/Grid/Grid_Eigen_Dense.h index 5aee81de..bdd39a65 100644 --- a/Grid/Grid_Eigen_Dense.h +++ b/Grid/Grid_Eigen_Dense.h @@ -14,7 +14,7 @@ /* NVCC save and restore compile environment*/ #ifdef __NVCC__ #pragma push -#if (__CUDACC_VER_MAJOR__ >= 11) && (__CUDACC_VER_MINOR__ >= 5) +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diag_suppress code_is_unreachable #else #pragma diag_suppress code_is_unreachable diff --git a/Grid/pugixml/pugixml.cc b/Grid/pugixml/pugixml.cc index 81973964..b2ba698b 100644 --- a/Grid/pugixml/pugixml.cc +++ b/Grid/pugixml/pugixml.cc @@ -16,7 +16,7 @@ #ifdef __NVCC__ #pragma push -#if (__CUDACC_VER_MAJOR__ >= 11) && (__CUDACC_VER_MINOR__ >= 5) +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diag_suppress declared_but_not_referenced // suppress "function was declared but never referenced warning" #else #pragma diag_suppress declared_but_not_referenced // suppress "function was declared but never referenced warning" From be528b6d27b900c4008b7e8c38915223e5845de1 Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 14 Feb 2023 14:37:10 +0000 Subject: [PATCH 386/399] Add batched block project/promote functions --- Grid/lattice/Lattice_transfer.h | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index ef489ea6..556785c0 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -288,7 +288,34 @@ inline void blockProject(Lattice<iVector<CComplex,nbasis > > &coarseData, blockZAXPY(fineDataRed,ip,Basis[v],fineDataRed); } } +template<class vobj,class CComplex,int nbasis,class VLattice> +inline void batchBlockProject(std::vector<Lattice<iVector<CComplex,nbasis>>> &coarseData, + const std::vector<Lattice<vobj>> &fineData, + const VLattice &Basis) +{ + int NBatch = fineData.size(); + GridBase * fine = fineData[0].Grid(); + GridBase * coarse= coarseData[0].Grid(); + Lattice<iScalar<CComplex>> ip(coarse); + std::vector<Lattice<vobj>> fineDataCopy = fineData; + + autoView(ip_, ip, AcceleratorWrite); + for(int v=0;v<nbasis;v++) { + for (int k=0; k<NBatch; k++) { + autoView( coarseData_ , coarseData[k], AcceleratorWrite); + blockInnerProductD(ip,Basis[v],fineDataCopy[k]); // ip = <basis|fine> + accelerator_for( sc, coarse->oSites(), vobj::Nsimd(), { + convertType(coarseData_[sc](v),ip_[sc]); + }); + + // improve numerical stability of projection + // |fine> = |fine> - <basis|fine> |basis> + ip=-ip; + blockZAXPY(fineDataCopy[k],ip,Basis[v],fineDataCopy[k]); + } + } +} template<class vobj,class vobj2,class CComplex> inline void blockZAXPY(Lattice<vobj> &fineZ, @@ -590,6 +617,24 @@ inline void blockPromote(const Lattice<iVector<CComplex,nbasis > > &coarseData, } #endif +template<class vobj,class CComplex,int nbasis,class VLattice> +inline void batchBlockPromote(const std::vector<Lattice<iVector<CComplex,nbasis>>> &coarseData, + std::vector<Lattice<vobj>> &fineData, + const VLattice &Basis) +{ + int NBatch = fineData.size(); + GridBase * fine = fineData[0].Grid(); + GridBase * coarse = coarseData[0].Grid(); + for (int k=0; k<NBatch; k++) + fineData[k]=Zero(); + for (int i=0;i<nbasis;i++) { + for (int k=0; k<NBatch; k++) { + Lattice<iScalar<CComplex>> ip = PeekIndex<0>(coarseData[k],i); + blockZAXPY(fineData[k],ip,Basis[i],fineData[k]); + } + } +} + // Useful for precision conversion, or indeed anything where an operator= does a conversion on scalars. // Simd layouts need not match since we use peek/poke Local template<class vobj,class vvobj> From 920a51438db5a5aaaa2f93b7308b567573cb52dc Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Tue, 14 Feb 2023 17:04:13 +0000 Subject: [PATCH 387/399] Added batched Mixed precision CG --- Grid/algorithms/Algorithms.h | 1 + .../ConjugateGradientMixedPrecBatched.h | 213 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h diff --git a/Grid/algorithms/Algorithms.h b/Grid/algorithms/Algorithms.h index 7f27784b..ff3da17d 100644 --- a/Grid/algorithms/Algorithms.h +++ b/Grid/algorithms/Algorithms.h @@ -54,6 +54,7 @@ NAMESPACE_CHECK(BiCGSTAB); #include <Grid/algorithms/iterative/SchurRedBlack.h> #include <Grid/algorithms/iterative/ConjugateGradientMultiShift.h> #include <Grid/algorithms/iterative/ConjugateGradientMixedPrec.h> +#include <Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h> #include <Grid/algorithms/iterative/BiCGSTABMixedPrec.h> #include <Grid/algorithms/iterative/BlockConjugateGradient.h> #include <Grid/algorithms/iterative/ConjugateGradientReliableUpdate.h> diff --git a/Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h b/Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h new file mode 100644 index 00000000..93f5c677 --- /dev/null +++ b/Grid/algorithms/iterative/ConjugateGradientMixedPrecBatched.h @@ -0,0 +1,213 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/algorithms/iterative/ConjugateGradientMixedPrecBatched.h + + Copyright (C) 2015 + + Author: Raoul Hodgson <raoul.hodgson@ed.ac.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory +*************************************************************************************/ +/* END LEGAL */ +#ifndef GRID_CONJUGATE_GRADIENT_MIXED_PREC_BATCHED_H +#define GRID_CONJUGATE_GRADIENT_MIXED_PREC_BATCHED_H + +NAMESPACE_BEGIN(Grid); + +//Mixed precision restarted defect correction CG +template<class FieldD,class FieldF, + typename std::enable_if< getPrecision<FieldD>::value == 2, int>::type = 0, + typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0> +class MixedPrecisionConjugateGradientBatched : public LinearFunction<FieldD> { +public: + using LinearFunction<FieldD>::operator(); + RealD Tolerance; + RealD InnerTolerance; //Initial tolerance for inner CG. Defaults to Tolerance but can be changed + Integer MaxInnerIterations; + Integer MaxOuterIterations; + Integer MaxPatchupIterations; + GridBase* SinglePrecGrid; //Grid for single-precision fields + RealD OuterLoopNormMult; //Stop the outer loop and move to a final double prec solve when the residual is OuterLoopNormMult * Tolerance + LinearOperatorBase<FieldF> &Linop_f; + LinearOperatorBase<FieldD> &Linop_d; + + //Option to speed up *inner single precision* solves using a LinearFunction that produces a guess + LinearFunction<FieldF> *guesser; + bool updateResidual; + + MixedPrecisionConjugateGradientBatched(RealD tol, + Integer maxinnerit, + Integer maxouterit, + Integer maxpatchit, + GridBase* _sp_grid, + LinearOperatorBase<FieldF> &_Linop_f, + LinearOperatorBase<FieldD> &_Linop_d, + bool _updateResidual=true) : + Linop_f(_Linop_f), Linop_d(_Linop_d), + Tolerance(tol), InnerTolerance(tol), MaxInnerIterations(maxinnerit), MaxOuterIterations(maxouterit), MaxPatchupIterations(maxpatchit), SinglePrecGrid(_sp_grid), + OuterLoopNormMult(100.), guesser(NULL), updateResidual(_updateResidual) { }; + + void useGuesser(LinearFunction<FieldF> &g){ + guesser = &g; + } + + void operator() (const FieldD &src_d_in, FieldD &sol_d){ + std::vector<FieldD> srcs_d_in{src_d_in}; + std::vector<FieldD> sols_d{sol_d}; + + (*this)(srcs_d_in,sols_d); + + sol_d = sols_d[0]; + } + + void operator() (const std::vector<FieldD> &src_d_in, std::vector<FieldD> &sol_d){ + assert(src_d_in.size() == sol_d.size()); + int NBatch = src_d_in.size(); + + std::cout << GridLogMessage << "NBatch = " << NBatch << std::endl; + + Integer TotalOuterIterations = 0; //Number of restarts + std::vector<Integer> TotalInnerIterations(NBatch,0); //Number of inner CG iterations + std::vector<Integer> TotalFinalStepIterations(NBatch,0); //Number of CG iterations in final patch-up step + + GridStopWatch TotalTimer; + TotalTimer.Start(); + + GridStopWatch InnerCGtimer; + GridStopWatch PrecChangeTimer; + + int cb = src_d_in[0].Checkerboard(); + + std::vector<RealD> src_norm; + std::vector<RealD> norm; + std::vector<RealD> stop; + + GridBase* DoublePrecGrid = src_d_in[0].Grid(); + FieldD tmp_d(DoublePrecGrid); + tmp_d.Checkerboard() = cb; + + FieldD tmp2_d(DoublePrecGrid); + tmp2_d.Checkerboard() = cb; + + std::vector<FieldD> src_d; + std::vector<FieldF> src_f; + std::vector<FieldF> sol_f; + + for (int i=0; i<NBatch; i++) { + sol_d[i].Checkerboard() = cb; + + src_norm.push_back(norm2(src_d_in[i])); + norm.push_back(0.); + stop.push_back(src_norm[i] * Tolerance*Tolerance); + + src_d.push_back(src_d_in[i]); //source for next inner iteration, computed from residual during operation + + src_f.push_back(SinglePrecGrid); + src_f[i].Checkerboard() = cb; + + sol_f.push_back(SinglePrecGrid); + sol_f[i].Checkerboard() = cb; + } + + RealD inner_tol = InnerTolerance; + + ConjugateGradient<FieldF> CG_f(inner_tol, MaxInnerIterations); + CG_f.ErrorOnNoConverge = false; + + Integer &outer_iter = TotalOuterIterations; //so it will be equal to the final iteration count + + for(outer_iter = 0; outer_iter < MaxOuterIterations; outer_iter++){ + std::cout << GridLogMessage << std::endl; + std::cout << GridLogMessage << "Outer iteration " << outer_iter << std::endl; + + bool allConverged = true; + + for (int i=0; i<NBatch; i++) { + //Compute double precision rsd and also new RHS vector. + Linop_d.HermOp(sol_d[i], tmp_d); + norm[i] = axpy_norm(src_d[i], -1., tmp_d, src_d_in[i]); //src_d is residual vector + + std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Outer iteration " << outer_iter <<" solve " << i << " residual "<< norm[i] << " target "<< stop[i] <<std::endl; + + PrecChangeTimer.Start(); + precisionChange(src_f[i], src_d[i]); + PrecChangeTimer.Stop(); + + sol_f[i] = Zero(); + + if(norm[i] > OuterLoopNormMult * stop[i]) { + allConverged = false; + } + } + if (allConverged) break; + + if (updateResidual) { + RealD normMax = *std::max_element(std::begin(norm), std::end(norm)); + RealD stopMax = *std::max_element(std::begin(stop), std::end(stop)); + while( normMax * inner_tol * inner_tol < stopMax) inner_tol *= 2; // inner_tol = sqrt(stop/norm) ?? + CG_f.Tolerance = inner_tol; + } + + //Optionally improve inner solver guess (eg using known eigenvectors) + if(guesser != NULL) { + (*guesser)(src_f, sol_f); + } + + for (int i=0; i<NBatch; i++) { + //Inner CG + InnerCGtimer.Start(); + CG_f(Linop_f, src_f[i], sol_f[i]); + InnerCGtimer.Stop(); + TotalInnerIterations[i] += CG_f.IterationsToComplete; + + //Convert sol back to double and add to double prec solution + PrecChangeTimer.Start(); + precisionChange(tmp_d, sol_f[i]); + PrecChangeTimer.Stop(); + + axpy(sol_d[i], 1.0, tmp_d, sol_d[i]); + } + + } + + //Final trial CG + std::cout << GridLogMessage << std::endl; + std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Starting final patch-up double-precision solve"<<std::endl; + + for (int i=0; i<NBatch; i++) { + ConjugateGradient<FieldD> CG_d(Tolerance, MaxPatchupIterations); + CG_d(Linop_d, src_d_in[i], sol_d[i]); + TotalFinalStepIterations[i] += CG_d.IterationsToComplete; + } + + TotalTimer.Stop(); + + std::cout << GridLogMessage << std::endl; + for (int i=0; i<NBatch; i++) { + std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: solve " << i << " Inner CG iterations " << TotalInnerIterations[i] << " Restarts " << TotalOuterIterations << " Final CG iterations " << TotalFinalStepIterations[i] << std::endl; + } + std::cout << GridLogMessage << std::endl; + std::cout<<GridLogMessage<<"MixedPrecisionConjugateGradientBatched: Total time " << TotalTimer.Elapsed() << " Precision change " << PrecChangeTimer.Elapsed() << " Inner CG total " << InnerCGtimer.Elapsed() << std::endl; + + } +}; + +NAMESPACE_END(Grid); + +#endif From ff97340324674b0e91be6afdccc39eb96e03c9d8 Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Sun, 26 Feb 2023 12:22:45 +0000 Subject: [PATCH 388/399] Expose cached bytes --- Grid/allocator/MemoryManager.cc | 6 +++++- Grid/allocator/MemoryManager.h | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Grid/allocator/MemoryManager.cc b/Grid/allocator/MemoryManager.cc index d055898f..955a1f90 100644 --- a/Grid/allocator/MemoryManager.cc +++ b/Grid/allocator/MemoryManager.cc @@ -35,6 +35,8 @@ void MemoryManager::PrintBytes(void) } +uint64_t MemoryManager::DeviceCacheBytes() { return CacheBytes[Acc] + CacheBytes[AccSmall]; } + ////////////////////////////////////////////////////////////////////// // Data tables for recently freed pooiniter caches ////////////////////////////////////////////////////////////////////// @@ -190,7 +192,9 @@ void MemoryManager::InitMessage(void) { std::cout << GridLogMessage<< "MemoryManager::Init() setting up"<<std::endl; #ifdef ALLOCATION_CACHE - std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent allocations: SMALL "<<Ncache[CpuSmall]<<" LARGE "<<Ncache[Cpu]<<std::endl; + std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent host allocations: SMALL "<<Ncache[CpuSmall]<<" LARGE "<<Ncache[Cpu]<<std::endl; + std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent device allocations: SMALL "<<Ncache[AccSmall]<<" LARGE "<<Ncache[Acc]<<std::endl; + std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent shared allocations: SMALL "<<Ncache[SharedSmall]<<" LARGE "<<Ncache[Shared]<<std::endl; #endif #ifdef GRID_UVM diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index c22a54f3..74390bc5 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -121,7 +121,9 @@ private: static uint64_t DeviceToHostXfer; static uint64_t DeviceEvictions; static uint64_t DeviceDestroy; - + + static uint64_t DeviceCacheBytes(); + private: #ifndef GRID_UVM ////////////////////////////////////////////////////////////////////// From 7731c7db8e782ba3737278013b3f292d6723641c Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Sun, 26 Feb 2023 14:15:28 +0000 Subject: [PATCH 389/399] Add huge cache type and allow Ncache==0 --- Grid/allocator/MemoryManager.cc | 51 ++++++++++++++++++++++----------- Grid/allocator/MemoryManager.h | 3 +- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/Grid/allocator/MemoryManager.cc b/Grid/allocator/MemoryManager.cc index 955a1f90..e9097c75 100644 --- a/Grid/allocator/MemoryManager.cc +++ b/Grid/allocator/MemoryManager.cc @@ -4,11 +4,14 @@ NAMESPACE_BEGIN(Grid); /*Allocation types, saying which pointer cache should be used*/ #define Cpu (0) -#define CpuSmall (1) -#define Acc (2) -#define AccSmall (3) -#define Shared (4) -#define SharedSmall (5) +#define CpuHuge (1) +#define CpuSmall (2) +#define Acc (3) +#define AccHuge (4) +#define AccSmall (5) +#define Shared (6) +#define SharedHuge (7) +#define SharedSmall (8) #undef GRID_MM_VERBOSE uint64_t total_shared; uint64_t total_device; @@ -35,14 +38,14 @@ void MemoryManager::PrintBytes(void) } -uint64_t MemoryManager::DeviceCacheBytes() { return CacheBytes[Acc] + CacheBytes[AccSmall]; } +uint64_t MemoryManager::DeviceCacheBytes() { return CacheBytes[Acc] + CacheBytes[AccHuge] + CacheBytes[AccSmall]; } ////////////////////////////////////////////////////////////////////// // Data tables for recently freed pooiniter caches ////////////////////////////////////////////////////////////////////// MemoryManager::AllocationCacheEntry MemoryManager::Entries[MemoryManager::NallocType][MemoryManager::NallocCacheMax]; int MemoryManager::Victim[MemoryManager::NallocType]; -int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 8, 8, 16, 8, 16 }; +int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 0, 8, 8, 0, 16, 8, 0, 16 }; uint64_t MemoryManager::CacheBytes[MemoryManager::NallocType]; ////////////////////////////////////////////////////////////////////// // Actual allocation and deallocation utils @@ -172,6 +175,16 @@ void MemoryManager::Init(void) } } + str= getenv("GRID_ALLOC_NCACHE_HUGE"); + if ( str ) { + Nc = atoi(str); + if ( (Nc>=0) && (Nc < NallocCacheMax)) { + Ncache[CpuHuge]=Nc; + Ncache[AccHuge]=Nc; + Ncache[SharedHuge]=Nc; + } + } + str= getenv("GRID_ALLOC_NCACHE_SMALL"); if ( str ) { Nc = atoi(str); @@ -192,9 +205,9 @@ void MemoryManager::InitMessage(void) { std::cout << GridLogMessage<< "MemoryManager::Init() setting up"<<std::endl; #ifdef ALLOCATION_CACHE - std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent host allocations: SMALL "<<Ncache[CpuSmall]<<" LARGE "<<Ncache[Cpu]<<std::endl; - std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent device allocations: SMALL "<<Ncache[AccSmall]<<" LARGE "<<Ncache[Acc]<<std::endl; - std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent shared allocations: SMALL "<<Ncache[SharedSmall]<<" LARGE "<<Ncache[Shared]<<std::endl; + std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent host allocations: SMALL "<<Ncache[CpuSmall]<<" LARGE "<<Ncache[Cpu]<<" HUGE "<<Ncache[CpuHuge]<<std::endl; + std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent device allocations: SMALL "<<Ncache[AccSmall]<<" LARGE "<<Ncache[Acc]<<" Huge "<<Ncache[AccHuge]<<std::endl; + std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent shared allocations: SMALL "<<Ncache[SharedSmall]<<" LARGE "<<Ncache[Shared]<<" Huge "<<Ncache[SharedHuge]<<std::endl; #endif #ifdef GRID_UVM @@ -226,8 +239,11 @@ void MemoryManager::InitMessage(void) { void *MemoryManager::Insert(void *ptr,size_t bytes,int type) { #ifdef ALLOCATION_CACHE - bool small = (bytes < GRID_ALLOC_SMALL_LIMIT); - int cache = type + small; + int cache; + if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2; + else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1; + else cache = type; + return Insert(ptr,bytes,Entries[cache],Ncache[cache],Victim[cache],CacheBytes[cache]); #else return ptr; @@ -236,11 +252,12 @@ void *MemoryManager::Insert(void *ptr,size_t bytes,int type) void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim, uint64_t &cacheBytes) { - assert(ncache>0); #ifdef GRID_OMP assert(omp_in_parallel()==0); #endif + if (ncache == 0) return ptr; + void * ret = NULL; int v = -1; @@ -275,8 +292,11 @@ void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries void *MemoryManager::Lookup(size_t bytes,int type) { #ifdef ALLOCATION_CACHE - bool small = (bytes < GRID_ALLOC_SMALL_LIMIT); - int cache = type+small; + int cache; + if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2; + else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1; + else cache = type; + return Lookup(bytes,Entries[cache],Ncache[cache],CacheBytes[cache]); #else return NULL; @@ -285,7 +305,6 @@ void *MemoryManager::Lookup(size_t bytes,int type) void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t & cacheBytes) { - assert(ncache>0); #ifdef GRID_OMP assert(omp_in_parallel()==0); #endif diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index 74390bc5..7a5f978c 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -35,6 +35,7 @@ NAMESPACE_BEGIN(Grid); // Move control to configure.ac and Config.h? #define GRID_ALLOC_SMALL_LIMIT (4096) +#define GRID_ALLOC_HUGE_LIMIT (2147483648) #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -83,7 +84,7 @@ private: } AllocationCacheEntry; static const int NallocCacheMax=128; - static const int NallocType=6; + static const int NallocType=9; static AllocationCacheEntry Entries[NallocType][NallocCacheMax]; static int Victim[NallocType]; static int Ncache[NallocType]; From a3e935c9028a77938603168aa0edd6b24f05d607 Mon Sep 17 00:00:00 2001 From: Raoul Hodgson <raoul.hodgson@ed.ac.uk> Date: Mon, 27 Feb 2023 11:38:16 +0000 Subject: [PATCH 390/399] Batched block project/promote size checks --- Grid/lattice/Lattice_transfer.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Grid/lattice/Lattice_transfer.h b/Grid/lattice/Lattice_transfer.h index 556785c0..4d1292a4 100644 --- a/Grid/lattice/Lattice_transfer.h +++ b/Grid/lattice/Lattice_transfer.h @@ -294,6 +294,8 @@ inline void batchBlockProject(std::vector<Lattice<iVector<CComplex,nbasis>>> &co const VLattice &Basis) { int NBatch = fineData.size(); + assert(coarseData.size() == NBatch); + GridBase * fine = fineData[0].Grid(); GridBase * coarse= coarseData[0].Grid(); @@ -622,7 +624,9 @@ inline void batchBlockPromote(const std::vector<Lattice<iVector<CComplex,nbasis> std::vector<Lattice<vobj>> &fineData, const VLattice &Basis) { - int NBatch = fineData.size(); + int NBatch = coarseData.size(); + assert(fineData.size() == NBatch); + GridBase * fine = fineData[0].Grid(); GridBase * coarse = coarseData[0].Grid(); for (int k=0; k<NBatch; k++) From 39c0815d9e6a338cf294af6fa6b759401888a5cc Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Tue, 21 Mar 2023 08:57:29 -0400 Subject: [PATCH 391/399] WriteDiscard --- Grid/lattice/Lattice_base.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Grid/lattice/Lattice_base.h b/Grid/lattice/Lattice_base.h index 34f13fa6..d6289de2 100644 --- a/Grid/lattice/Lattice_base.h +++ b/Grid/lattice/Lattice_base.h @@ -129,7 +129,7 @@ public: auto exprCopy = expr; ExpressionViewOpen(exprCopy); - auto me = View(AcceleratorWrite); + auto me = View(AcceleratorWriteDiscard); accelerator_for(ss,me.size(),vobj::Nsimd(),{ auto tmp = eval(ss,exprCopy); coalescedWrite(me[ss],tmp); @@ -152,7 +152,7 @@ public: auto exprCopy = expr; ExpressionViewOpen(exprCopy); - auto me = View(AcceleratorWrite); + auto me = View(AcceleratorWriteDiscard); accelerator_for(ss,me.size(),vobj::Nsimd(),{ auto tmp = eval(ss,exprCopy); coalescedWrite(me[ss],tmp); @@ -174,7 +174,7 @@ public: this->checkerboard=cb; auto exprCopy = expr; ExpressionViewOpen(exprCopy); - auto me = View(AcceleratorWrite); + auto me = View(AcceleratorWriteDiscard); accelerator_for(ss,me.size(),vobj::Nsimd(),{ auto tmp = eval(ss,exprCopy); coalescedWrite(me[ss],tmp); @@ -288,8 +288,8 @@ public: typename std::enable_if<!std::is_same<robj,vobj>::value,int>::type i=0; conformable(*this,r); this->checkerboard = r.Checkerboard(); - auto me = View(AcceleratorWrite); auto him= r.View(AcceleratorRead); + auto me = View(AcceleratorWriteDiscard); accelerator_for(ss,me.size(),vobj::Nsimd(),{ coalescedWrite(me[ss],him(ss)); }); @@ -303,8 +303,8 @@ public: inline Lattice<vobj> & operator = (const Lattice<vobj> & r){ this->checkerboard = r.Checkerboard(); conformable(*this,r); - auto me = View(AcceleratorWrite); auto him= r.View(AcceleratorRead); + auto me = View(AcceleratorWriteDiscard); accelerator_for(ss,me.size(),vobj::Nsimd(),{ coalescedWrite(me[ss],him(ss)); }); From 281488611a7b127a52e7d91930774bb7e7faa81f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 23 Mar 2023 10:28:50 -0400 Subject: [PATCH 392/399] WriteDiscard on construct --- Grid/lattice/Lattice_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/lattice/Lattice_base.h b/Grid/lattice/Lattice_base.h index d6289de2..838cdda5 100644 --- a/Grid/lattice/Lattice_base.h +++ b/Grid/lattice/Lattice_base.h @@ -245,7 +245,7 @@ public: /////////////////////////////////////////// // user defined constructor /////////////////////////////////////////// - Lattice(GridBase *grid,ViewMode mode=AcceleratorWrite) { + Lattice(GridBase *grid,ViewMode mode=AcceleratorWriteDiscard) { this->_grid = grid; resize(this->_grid->oSites()); assert((((uint64_t)&this->_odata[0])&0xF) ==0); From 481bbaf1fce5b7ef0162c6f9ecec73a80e263cc7 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Thu, 23 Mar 2023 12:55:31 -0400 Subject: [PATCH 393/399] Interface to query memory use --- Grid/allocator/MemoryManager.cc | 1 + Grid/allocator/MemoryManager.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/Grid/allocator/MemoryManager.cc b/Grid/allocator/MemoryManager.cc index e9097c75..a9e5c9b4 100644 --- a/Grid/allocator/MemoryManager.cc +++ b/Grid/allocator/MemoryManager.cc @@ -39,6 +39,7 @@ void MemoryManager::PrintBytes(void) } uint64_t MemoryManager::DeviceCacheBytes() { return CacheBytes[Acc] + CacheBytes[AccHuge] + CacheBytes[AccSmall]; } +uint64_t MemoryManager::HostCacheBytes() { return CacheBytes[Cpu] + CacheBytes[CpuHuge] + CacheBytes[CpuSmall]; } ////////////////////////////////////////////////////////////////////// // Data tables for recently freed pooiniter caches diff --git a/Grid/allocator/MemoryManager.h b/Grid/allocator/MemoryManager.h index 7a5f978c..0dc78f04 100644 --- a/Grid/allocator/MemoryManager.h +++ b/Grid/allocator/MemoryManager.h @@ -71,6 +71,21 @@ enum ViewMode { CpuWriteDiscard = 0x10 // same for now }; +struct MemoryStatus { + uint64_t DeviceBytes; + uint64_t DeviceLRUBytes; + uint64_t DeviceMaxBytes; + uint64_t HostToDeviceBytes; + uint64_t DeviceToHostBytes; + uint64_t HostToDeviceXfer; + uint64_t DeviceToHostXfer; + uint64_t DeviceEvictions; + uint64_t DeviceDestroy; + uint64_t DeviceAllocCacheBytes; + uint64_t HostAllocCacheBytes; +}; + + class MemoryManager { private: @@ -124,7 +139,24 @@ private: static uint64_t DeviceDestroy; static uint64_t DeviceCacheBytes(); + static uint64_t HostCacheBytes(); + static MemoryStatus GetFootprint(void) { + MemoryStatus stat; + stat.DeviceBytes = DeviceBytes; + stat.DeviceLRUBytes = DeviceLRUBytes; + stat.DeviceMaxBytes = DeviceMaxBytes; + stat.HostToDeviceBytes = HostToDeviceBytes; + stat.DeviceToHostBytes = DeviceToHostBytes; + stat.HostToDeviceXfer = HostToDeviceXfer; + stat.DeviceToHostXfer = DeviceToHostXfer; + stat.DeviceEvictions = DeviceEvictions; + stat.DeviceDestroy = DeviceDestroy; + stat.DeviceAllocCacheBytes = DeviceCacheBytes(); + stat.HostAllocCacheBytes = HostCacheBytes(); + return stat; + }; + private: #ifndef GRID_UVM ////////////////////////////////////////////////////////////////////// From 2fbcf13c46b1e7d0d0c6c7f9ead0f58d12c78cb5 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 27 Mar 2023 14:25:14 -0700 Subject: [PATCH 394/399] SYCL fix --- Grid/communicator/SharedMemoryMPI.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 795f3928..792f8405 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -36,9 +36,11 @@ Author: Christoph Lehner <christoph@lhnr.de> #ifdef GRID_HIP #include <hip/hip_runtime_api.h> #endif -#ifdef GRID_SYCl +#ifdef GRID_SYCL #endif +#define GRID_SYCL_LEVEL_ZERO_IPC + NAMESPACE_BEGIN(Grid); #define header "SharedMemoryMpi: " From dd3bbb8fa2ff3878045f3b7e01e68036baac9bcb Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 27 Mar 2023 17:27:45 -0700 Subject: [PATCH 395/399] MOve the synchronise out to the stencil so one call instead of one call per packet --- Grid/communicator/Communicator_mpi3.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/Grid/communicator/Communicator_mpi3.cc b/Grid/communicator/Communicator_mpi3.cc index b8ce7bca..280f5c4e 100644 --- a/Grid/communicator/Communicator_mpi3.cc +++ b/Grid/communicator/Communicator_mpi3.cc @@ -401,8 +401,6 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsRequest_t> &list,int dir) { // std::cout << "Copy Synchronised\n"<<std::endl; - acceleratorCopySynchronise(); - int nreq=list.size(); if (nreq==0) return; From 05e562e3d7496a101945742a6241a398a1c39351 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 27 Mar 2023 17:28:38 -0700 Subject: [PATCH 396/399] Move the copy synch out to stencil and do one per call instead of one per packet --- Grid/stencil/Stencil.h | 48 +++++++++++------------------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index 65d878cb..6296df4e 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -398,6 +398,8 @@ public: //////////////////////////////////////////////////////////////////////// void CommunicateBegin(std::vector<std::vector<CommsRequest_t> > &reqs) { + // Buffers are gathered AND synchronised + // Copies are MPI ISend OR asynch copy on copy stream reqs.resize(Packets.size()); commtime-=usecond(); for(int i=0;i<Packets.size();i++){ @@ -410,14 +412,18 @@ public: comms_bytes+=bytes; shm_bytes +=2*Packets[i].bytes-bytes; } - _grid->StencilBarrier();// Synch shared memory on a single nodes } void CommunicateComplete(std::vector<std::vector<CommsRequest_t> > &reqs) { + // complete intranode + acceleratorCopySynchronise(); + // complete MPI for(int i=0;i<Packets.size();i++){ _grid->StencilSendToRecvFromComplete(reqs[i],i); } + // Everyone agrees we are all done + _grid->StencilBarrier(); commtime+=usecond(); } //////////////////////////////////////////////////////////////////////// @@ -425,33 +431,9 @@ public: //////////////////////////////////////////////////////////////////////// void Communicate(void) { - if ( 0 ){ - thread_region { - // must be called in parallel region - int mythread = thread_num(); - int maxthreads= thread_max(); - int nthreads = CartesianCommunicator::nCommThreads; - assert(nthreads <= maxthreads); - if (nthreads == -1) nthreads = 1; - if (mythread < nthreads) { - for (int i = mythread; i < Packets.size(); i += nthreads) { - double start = usecond(); - uint64_t bytes= _grid->StencilSendToRecvFrom(Packets[i].send_buf, - Packets[i].to_rank, - Packets[i].recv_buf, - Packets[i].from_rank, - Packets[i].bytes,i); - comm_bytes_thr[mythread] += bytes; - shm_bytes_thr[mythread] += Packets[i].bytes - bytes; - comm_time_thr[mythread] += usecond() - start; - } - } - } - } else { // Concurrent and non-threaded asynch calls to MPI - std::vector<std::vector<CommsRequest_t> > reqs; - this->CommunicateBegin(reqs); - this->CommunicateComplete(reqs); - } + std::vector<std::vector<CommsRequest_t> > reqs; + this->CommunicateBegin(reqs); + this->CommunicateComplete(reqs); } template<class compressor> void HaloExchange(const Lattice<vobj> &source,compressor &compress) @@ -527,7 +509,6 @@ public: _grid->StencilBarrier();// Synch shared memory on a single nodes mpi3synctime_g+=usecond(); - // conformable(source.Grid(),_grid); assert(source.Grid()==_grid); halogtime-=usecond(); @@ -586,13 +567,8 @@ public: CommsMerge(decompress,Mergers,Decompressions); } template<class decompressor> void CommsMergeSHM(decompressor decompress) { - mpi3synctime-=usecond(); - accelerator_barrier(); - _grid->StencilBarrier();// Synch shared memory on a single nodes - mpi3synctime+=usecond(); - shmmergetime-=usecond(); - CommsMerge(decompress,MergersSHM,DecompressionsSHM); - shmmergetime+=usecond(); + assert(MergersSHM.size()==0); + assert(DecompressionsSHM.size()==0); } template<class decompressor> From 8feedb4f6f74498362785cc6cae8d43ad7b5988f Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 27 Mar 2023 17:29:21 -0700 Subject: [PATCH 397/399] Include files moved --- Grid/threads/Accelerator.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Grid/threads/Accelerator.h b/Grid/threads/Accelerator.h index e17e85d1..db998739 100644 --- a/Grid/threads/Accelerator.h +++ b/Grid/threads/Accelerator.h @@ -249,14 +249,16 @@ inline int acceleratorIsCommunicable(void *ptr) ////////////////////////////////////////////// #ifdef GRID_SYCL NAMESPACE_END(Grid); +#if 0 #include <CL/sycl.hpp> #include <CL/sycl/usm.hpp> - -#define GRID_SYCL_LEVEL_ZERO_IPC - -#ifdef GRID_SYCL_LEVEL_ZERO_IPC #include <level_zero/ze_api.h> #include <CL/sycl/backend/level_zero.hpp> +#else +#include <sycl/CL/sycl.hpp> +#include <sycl/usm.hpp> +#include <level_zero/ze_api.h> +#include <sycl/ext/oneapi/backend/level_zero.hpp> #endif NAMESPACE_BEGIN(Grid); From 0efa107cb6ccca45ea27d74ab2b928d206a9811a Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Mon, 27 Mar 2023 17:29:43 -0700 Subject: [PATCH 398/399] Script update --- systems/PVC/benchmarks/run-1tile.sh | 2 +- systems/PVC/benchmarks/run-2tile-mpi.sh | 14 ++++++-------- systems/PVC/benchmarks/wrap.sh | 10 +++++----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/systems/PVC/benchmarks/run-1tile.sh b/systems/PVC/benchmarks/run-1tile.sh index 0fe80247..9a29b773 100755 --- a/systems/PVC/benchmarks/run-1tile.sh +++ b/systems/PVC/benchmarks/run-1tile.sh @@ -4,7 +4,7 @@ #SBATCH -p QZ1J-ICX-PVC ##SBATCH -p QZ1J-SPR-PVC-2C -source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh +#source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh export NT=8 diff --git a/systems/PVC/benchmarks/run-2tile-mpi.sh b/systems/PVC/benchmarks/run-2tile-mpi.sh index cefab776..5a6a9b8f 100755 --- a/systems/PVC/benchmarks/run-2tile-mpi.sh +++ b/systems/PVC/benchmarks/run-2tile-mpi.sh @@ -4,7 +4,7 @@ #SBATCH -p QZ1J-ICX-PVC -source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh +#source /nfs/site/home/paboylex/ATS/GridNew/Grid/systems/PVC-nightly/setup.sh export NT=16 @@ -19,16 +19,14 @@ export SYCL_DEVICE_FILTER=gpu,level_zero export I_MPI_OFFLOAD_CELL=tile export EnableImplicitScaling=0 export EnableWalkerPartition=0 -export SYCL_PI_LEVEL_ZERO_DEVICE_SCOPE_EVENTS=1 -export SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1 +#export SYCL_PI_LEVEL_ZERO_DEVICE_SCOPE_EVENTS=1 +#export SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1 export SYCL_PI_LEVEL_ZERO_USE_COPY_ENGINE=0 -for i in 0 +for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 do -mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 --device-mem 32768 -mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 --device-mem 32768 +mpiexec -launcher ssh -n 2 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 0 --device-mem 32768 > 1.1.1.2.log$i +mpiexec -launcher ssh -n 2 -host localhost ./wrap.sh ./Benchmark_dwf_fp32 --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 0 --device-mem 32768 > 2.1.1.1.log$i done -#mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 1.1.1.2 --grid 32.32.32.64 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.1x2.log -#mpiexec -launcher ssh -n 2 -host localhost ./wrap4gpu.sh ./Benchmark_halo --mpi 2.1.1.1 --grid 64.32.32.32 --accelerator-threads $NT --shm-mpi 1 > halo.2tile.2x1.log diff --git a/systems/PVC/benchmarks/wrap.sh b/systems/PVC/benchmarks/wrap.sh index bb7b517d..06ed0ca1 100755 --- a/systems/PVC/benchmarks/wrap.sh +++ b/systems/PVC/benchmarks/wrap.sh @@ -5,10 +5,10 @@ export ZE_AFFINITY_MASK=0.$MPI_LOCALRANKID echo Ranke $MPI_LOCALRANKID ZE_AFFINITY_MASK is $ZE_AFFINITY_MASK -if [ $MPI_LOCALRANKID = "0" ] -then +#if [ $MPI_LOCALRANKID = "0" ] +#then # ~psteinbr/build_pti/ze_tracer -h $@ - onetrace --chrome-device-timeline $@ -else +# onetrace --chrome-device-timeline $@ +#else $@ -fi +#fi From a00ae981e0e6e83caa70f5bed346242ef1eb0bf9 Mon Sep 17 00:00:00 2001 From: Peter Boyle <paboyle@ph.ed.ac.uk> Date: Wed, 29 Mar 2023 15:00:40 -0400 Subject: [PATCH 399/399] Fence propagation from SYCL --- Grid/communicator/SharedMemoryMPI.cc | 3 +-- .../implementation/WilsonKernelsImplementation.h | 10 +++------- Grid/stencil/Stencil.h | 3 +++ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Grid/communicator/SharedMemoryMPI.cc b/Grid/communicator/SharedMemoryMPI.cc index 792f8405..a4f5731e 100644 --- a/Grid/communicator/SharedMemoryMPI.cc +++ b/Grid/communicator/SharedMemoryMPI.cc @@ -37,9 +37,8 @@ Author: Christoph Lehner <christoph@lhnr.de> #include <hip/hip_runtime_api.h> #endif #ifdef GRID_SYCL - -#endif #define GRID_SYCL_LEVEL_ZERO_IPC +#endif NAMESPACE_BEGIN(Grid); diff --git a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h index 9f6960af..bbf9937c 100644 --- a/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h +++ b/Grid/qcd/action/fermion/implementation/WilsonKernelsImplementation.h @@ -459,11 +459,7 @@ void WilsonKernels<Impl>::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField if( interior && exterior ) { if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSite); return;} -#ifdef SYCL_HACK - if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL_TMP(HandDhopSiteSycl); return; } -#else if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSite); return;} -#endif #ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSite); return;} #endif @@ -474,6 +470,7 @@ void WilsonKernels<Impl>::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteInt); return;} #endif } else if( exterior ) { + acceleratorFenceComputeStream(); if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteExt); return;} if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteExt); return;} #ifndef GRID_CUDA @@ -498,10 +495,9 @@ void WilsonKernels<Impl>::DhopKernel(int Opt,StencilImpl &st, DoubledGaugeField #ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDag); return;} #endif - acceleratorFenceComputeStream(); } else if( interior ) { - if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALL(GenericDhopSiteDagInt); return;} - if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALL(HandDhopSiteDagInt); return;} + if (Opt == WilsonKernelsStatic::OptGeneric ) { KERNEL_CALLNB(GenericDhopSiteDagInt); return;} + if (Opt == WilsonKernelsStatic::OptHandUnroll ) { KERNEL_CALLNB(HandDhopSiteDagInt); return;} #ifndef GRID_CUDA if (Opt == WilsonKernelsStatic::OptInlineAsm ) { ASM_CALL(AsmDhopSiteDagInt); return;} #endif diff --git a/Grid/stencil/Stencil.h b/Grid/stencil/Stencil.h index 6296df4e..0a1214a1 100644 --- a/Grid/stencil/Stencil.h +++ b/Grid/stencil/Stencil.h @@ -585,6 +585,7 @@ public: decompress.Exchange(mp,vp0,vp1,type,o); }); } + if ( mm.size() ) acceleratorFenceComputeStream(); mergetime+=usecond(); decompresstime-=usecond(); @@ -595,7 +596,9 @@ public: decompress.Decompress(kp,mp,o); }); } + if ( dd.size() ) acceleratorFenceComputeStream(); decompresstime+=usecond(); + } //////////////////////////////////////// // Set up routines